diff --git a/hotspot/.hgignore b/hotspot/.hgignore new file mode 100644 index 00000000000..8c4cff4cd1f --- /dev/null +++ b/hotspot/.hgignore @@ -0,0 +1,170 @@ +^build/solaris/solaris_sparc_compiler1/ +^build/solaris/solaris_sparc_compiler2/ +^build/solaris/solaris_sparc_core/ +^build/solaris/solaris_sparc_kernel/ +^build/solaris/solaris_sparc_docs/ +^build/solaris/jdk-solaris-sparc/ +^build/solaris/export-solaris-sparc/ +^build/solaris/solaris_sparcv9_compiler1/ +^build/solaris/solaris_sparcv9_compiler2/ +^build/solaris/solaris_sparcv9_core/ +^build/solaris/solaris_sparcv9_kernel/ +^build/solaris/solaris_sparcv9_docs/ +^build/solaris/jdk-solaris-sparcv9/ +^build/solaris/export-solaris-sparcv9/ +^build/solaris/solaris_sparc32_compiler1/ +^build/solaris/solaris_sparc32_compiler2/ +^build/solaris/solaris_sparc32_core/ +^build/solaris/solaris_sparc32_kernel/ +^build/solaris/solaris_sparc32_docs/ +^build/solaris/jdk-solaris-sparc32/ +^build/solaris/export-solaris-sparc32/ +^build/solaris/solaris_sparc64_compiler1/ +^build/solaris/solaris_sparc64_compiler2/ +^build/solaris/solaris_sparc64_core/ +^build/solaris/solaris_sparc64_kernel/ +^build/solaris/solaris_sparc64_docs/ +^build/solaris/jdk-solaris-sparc64/ +^build/solaris/export-solaris-sparc64/ +^build/solaris/solaris_i486_compiler1/ +^build/solaris/solaris_i486_compiler2/ +^build/solaris/solaris_i486_core/ +^build/solaris/solaris_i486_kernel/ +^build/solaris/solaris_i486_docs/ +^build/solaris/jdk-solaris-i486/ +^build/solaris/export-solaris-i486/ +^build/solaris/solaris_i386_compiler1/ +^build/solaris/solaris_i386_compiler2/ +^build/solaris/solaris_i386_core/ +^build/solaris/solaris_i386_kernel/ +^build/solaris/solaris_i386_docs/ +^build/solaris/jdk-solaris-i386/ +^build/solaris/export-solaris-i386/ +^build/solaris/solaris_amd64_compiler1/ +^build/solaris/solaris_amd64_compiler2/ +^build/solaris/solaris_amd64_core/ +^build/solaris/solaris_amd64_kernel/ +^build/solaris/solaris_amd64_docs/ +^build/solaris/jdk-solaris-amd64/ +^build/solaris/export-solaris-amd64/ +^build/solaris/solaris_x64_compiler1/ +^build/solaris/solaris_x64_compiler2/ +^build/solaris/solaris_x64_core/ +^build/solaris/solaris_x64_kernel/ +^build/solaris/solaris_x64_docs/ +^build/solaris/jdk-solaris-x64/ +^build/solaris/export-solaris-x64/ +^build/windows/windows_sparc_compiler1/ +^build/windows/windows_sparc_compiler2/ +^build/windows/windows_sparc_core/ +^build/windows/windows_sparc_kernel/ +^build/windows/windows_sparc_docs/ +^build/windows/jdk-windows-sparc/ +^build/windows/export-windows-sparc/ +^build/windows/windows_sparcv9_compiler1/ +^build/windows/windows_sparcv9_compiler2/ +^build/windows/windows_sparcv9_core/ +^build/windows/windows_sparcv9_kernel/ +^build/windows/windows_sparcv9_docs/ +^build/windows/jdk-windows-sparcv9/ +^build/windows/export-windows-sparcv9/ +^build/windows/windows_sparc32_compiler1/ +^build/windows/windows_sparc32_compiler2/ +^build/windows/windows_sparc32_core/ +^build/windows/windows_sparc32_kernel/ +^build/windows/windows_sparc32_docs/ +^build/windows/jdk-windows-sparc32/ +^build/windows/export-windows-sparc32/ +^build/windows/windows_sparc64_compiler1/ +^build/windows/windows_sparc64_compiler2/ +^build/windows/windows_sparc64_core/ +^build/windows/windows_sparc64_kernel/ +^build/windows/windows_sparc64_docs/ +^build/windows/jdk-windows-sparc64/ +^build/windows/export-windows-sparc64/ +^build/windows/windows_i486_compiler1/ +^build/windows/windows_i486_compiler2/ +^build/windows/windows_i486_core/ +^build/windows/windows_i486_kernel/ +^build/windows/windows_i486_docs/ +^build/windows/jdk-windows-i486/ +^build/windows/export-windows-i486/ +^build/windows/windows_i386_compiler1/ +^build/windows/windows_i386_compiler2/ +^build/windows/windows_i386_core/ +^build/windows/windows_i386_kernel/ +^build/windows/windows_i386_docs/ +^build/windows/jdk-windows-i386/ +^build/windows/export-windows-i386/ +^build/windows/windows_amd64_compiler1/ +^build/windows/windows_amd64_compiler2/ +^build/windows/windows_amd64_core/ +^build/windows/windows_amd64_kernel/ +^build/windows/windows_amd64_docs/ +^build/windows/jdk-windows-amd64/ +^build/windows/export-windows-amd64/ +^build/windows/windows_x64_compiler1/ +^build/windows/windows_x64_compiler2/ +^build/windows/windows_x64_core/ +^build/windows/windows_x64_kernel/ +^build/windows/windows_x64_docs/ +^build/windows/jdk-windows-x64/ +^build/windows/export-windows-x64/ +^build/linux/linux_sparc_compiler1/ +^build/linux/linux_sparc_compiler2/ +^build/linux/linux_sparc_core/ +^build/linux/linux_sparc_kernel/ +^build/linux/linux_sparc_docs/ +^build/linux/jdk-linux-sparc/ +^build/linux/export-linux-sparc/ +^build/linux/linux_sparcv9_compiler1/ +^build/linux/linux_sparcv9_compiler2/ +^build/linux/linux_sparcv9_core/ +^build/linux/linux_sparcv9_kernel/ +^build/linux/linux_sparcv9_docs/ +^build/linux/jdk-linux-sparcv9/ +^build/linux/export-linux-sparcv9/ +^build/linux/linux_sparc32_compiler1/ +^build/linux/linux_sparc32_compiler2/ +^build/linux/linux_sparc32_core/ +^build/linux/linux_sparc32_kernel/ +^build/linux/linux_sparc32_docs/ +^build/linux/jdk-linux-sparc32/ +^build/linux/export-linux-sparc32/ +^build/linux/linux_sparc64_compiler1/ +^build/linux/linux_sparc64_compiler2/ +^build/linux/linux_sparc64_core/ +^build/linux/linux_sparc64_kernel/ +^build/linux/linux_sparc64_docs/ +^build/linux/jdk-linux-sparc64/ +^build/linux/export-linux-sparc64/ +^build/linux/linux_i486_compiler1/ +^build/linux/linux_i486_compiler2/ +^build/linux/linux_i486_core/ +^build/linux/linux_i486_kernel/ +^build/linux/linux_i486_docs/ +^build/linux/jdk-linux-i486/ +^build/linux/export-linux-i486/ +^build/linux/linux_i386_compiler1/ +^build/linux/linux_i386_compiler2/ +^build/linux/linux_i386_core/ +^build/linux/linux_i386_kernel/ +^build/linux/linux_i386_docs/ +^build/linux/jdk-linux-i386/ +^build/linux/export-linux-i386/ +^build/linux/linux_amd64_compiler1/ +^build/linux/linux_amd64_compiler2/ +^build/linux/linux_amd64_core/ +^build/linux/linux_amd64_kernel/ +^build/linux/linux_amd64_docs/ +^build/linux/jdk-linux-amd64/ +^build/linux/export-linux-amd64/ +^build/linux/linux_x64_compiler1/ +^build/linux/linux_x64_compiler2/ +^build/linux/linux_x64_core/ +^build/linux/linux_x64_kernel/ +^build/linux/linux_x64_docs/ +^build/linux/jdk-linux-x64/ +^build/linux/export-linux-x64/ +^dist/ +^nbproject/private/ diff --git a/hotspot/ASSEMBLY_EXCEPTION b/hotspot/ASSEMBLY_EXCEPTION new file mode 100644 index 00000000000..8b7ac1d0813 --- /dev/null +++ b/hotspot/ASSEMBLY_EXCEPTION @@ -0,0 +1,27 @@ + +OPENJDK ASSEMBLY EXCEPTION + +The OpenJDK source code made available by Sun at openjdk.java.net and +openjdk.dev.java.net ("OpenJDK Code") is distributed under the terms of the +GNU General Public License version 2 +only ("GPL2"), with the following clarification and special exception. + + Linking this OpenJDK Code statically or dynamically with other code + is making a combined work based on this library. Thus, the terms + and conditions of GPL2 cover the whole combination. + + As a special exception, Sun gives you permission to link this + OpenJDK Code with certain code licensed by Sun as indicated at + http://openjdk.java.net/legal/exception-modules-2007-05-08.html + ("Designated Exception Modules") to produce an executable, + regardless of the license terms of the Designated Exception Modules, + and to copy and distribute the resulting executable under GPL2, + provided that the Designated Exception Modules continue to be + governed by the licenses under which they were offered by Sun. + +As such, it allows licensees and sublicensees of Sun's GPL2 OpenJDK Code to +build an executable that includes those portions of necessary code that Sun +could not provide under GPL2 (or that Sun has provided under GPL2 with the +Classpath exception). If you modify or add to the OpenJDK code, that new +GPL2 code may still be combined with Designated Exception Modules if the +new code is made subject to this exception by its copyright holder. diff --git a/hotspot/LICENSE b/hotspot/LICENSE new file mode 100644 index 00000000000..eeab58c21c9 --- /dev/null +++ b/hotspot/LICENSE @@ -0,0 +1,347 @@ +The GNU General Public License (GPL) + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to +most of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must +make sure that they, too, receive or can get the source code. And you must +show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program proprietary. +To prevent this, we have made it clear that any patent must be licensed for +everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms of +this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is included +without limitation in the term "modification".) Each licensee is addressed as +"you". + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is +not restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's 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 give any other recipients of the +Program a copy of this License along with the Program. + +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 Program or any portion of it, thus +forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or + in part contains or is derived from the Program or any part thereof, to be + licensed as a whole at no charge to all third parties under the terms of + this License. + + c) If the modified program normally reads commands interactively when run, + you must cause it, when started running for such interactive use in the + most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a copy + of this License. (Exception: if the Program itself is interactive but does + not normally print such an announcement, your work based on the Program is + not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, 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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 and +2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, 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 executable. However, as a special exception, the source code +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. + +If distribution of executable or 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 counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program 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. + +5. 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 Program +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Program (or +any work based on the Program), you indicate your acceptance of this License to +do so, and all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), +the recipient automatically receives a license from the original licensor to +copy, distribute or modify the Program 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 to this License. + +7. 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 Program at all. +For example, if a patent license would not permit royalty-free redistribution +of the Program 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 Program. + +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. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original +copyright holder who places the Program 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. + +9. The Free Software Foundation may publish revised and/or new versions of the +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 Program +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 Program does not specify a version number of this License, you may +choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, 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 + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE +PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. 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 +PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 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 Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. 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. + + One line to give the program's name and a brief idea of what it does. + + Copyright (C) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes + with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free + software, and you are welcome to redistribute it under certain conditions; + type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here +is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + 'Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. + + +"CLASSPATH" EXCEPTION TO THE GPL + +Certain source files distributed by Sun Microsystems, Inc. are subject to +the following clarification and special exception to the GPL, but only where +Sun has expressly included in the particular source file's header the words +"Sun designates this particular file as subject to the "Classpath" exception +as provided by Sun in the LICENSE file that accompanied this code." + + Linking this library statically or dynamically with other modules is making + a combined work based on this library. Thus, the terms and conditions of + the GNU General Public License cover the whole combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent modules, + and to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent module, + the terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. If + you modify this library, you may extend this exception to your version of + the library, but you are not obligated to do so. If you do not wish to do + so, delete this exception statement from your version. diff --git a/hotspot/README b/hotspot/README new file mode 100644 index 00000000000..19afb261fc3 --- /dev/null +++ b/hotspot/README @@ -0,0 +1,14 @@ +README: + This file should be located at the top of the hotspot Mercurial repository. + + See http://openjdk.java.net/ for more information about the OpenJDK. + + See ../README-builds.html for complete details on build machine requirements. + +Simple Build Instructions: + + cd make && gnumake + + The files that will be imported into the jdk build will be in the "build" + directory. + diff --git a/hotspot/THIRD_PARTY_README b/hotspot/THIRD_PARTY_README new file mode 100644 index 00000000000..9f4d7e5087a --- /dev/null +++ b/hotspot/THIRD_PARTY_README @@ -0,0 +1,1616 @@ +DO NOT TRANSLATE OR LOCALIZE. + +%% This notice is provided with respect to Thai dictionary for text breaking, which may be included with this software: + +--- begin of LICENSE file --- + +Copyright (C) 1982 The Royal Institute, Thai Royal Government. + +Copyright (C) 1998 National Electronics and Computer Technology Center, + National Science and Technology Development Agency, + Ministry of Science Technology and Environment, + Thai Royal Government. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without +limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to +whom the Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + +--- end of LICENSE file --- +%% This notice is provided with respect to ASM, which may be included with this software: +Copyright (c) 2000-2005 INRIA, France Telecom +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +%% This notice is provided with respect to zlib 1.1.3, which may be included with this software: + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. + +%% This notice is provided with respect to W3C (DTD for XML Signatures), which may be included with this software: +W3C® SOFTWARE NOTICE AND LICENSE +Copyright © 1994-2002 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/ +This W3C work (including software, documents, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions: +Permission to use, copy, modify, and distribute this software and its documentation, with or without modification,  for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make: +1.The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. +2.Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, a short notice of the following form (hypertext is preferred, text is permitted) should be used within the body of any redistributed or derivative code: "Copyright © [$date-of-software] World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/" +3.Notice of any changes or modifications to the W3C files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.) +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. +The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders. +____________________________________ +This formulation of W3C's notice and license became active on August 14 1998 so as to improve compatibility with GPL. This version ensures that W3C software licensing terms are no more restrictive than GPL and consequently W3C software may be distributed in GPL packages. See the older formulation for the policy prior to this date. Please see our Copyright FAQ for common questions about using materials from our site, including specific terms and conditions for packages like libwww, Amaya, and Jigsaw. Other questions about this notice can be directed to site-policy@w3.org. +  +%% This notice is provided with respect to jscheme.jar, which may be included with this software: +Software License Agreement +Copyright © 1998-2002 by Peter Norvig. +Permission is granted to anyone to use this software, in source or object code form, on any computer system, and to modify, compile, decompile, run, and redistribute it to anyone else, subject to the following restrictions: +1.The author makes no warranty of any kind, either expressed or implied, about the suitability of this software for any purpose. +2.The author accepts no liability of any kind for damages or other consequences of the use of this software, even if they arise from defects in the software. +3.The origin of this software must not be misrepresented, either by explicit claim or by omission. +4.Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. Altered versions may be distributed in packages under other licenses (such as the GNU license). +If you find this software useful, it would be nice if you let me (peter@norvig.com) know about it, and nicer still if you send me modifications that you are willing to share. However, you are not required to do so. + + +%% This notice is provided with respect to PC/SC Lite for Suse Linux v. 1.1.1, which may be included with this software: + +Copyright (c) 1999-2004 David Corcoran +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +Changes to this license can be made only by the copyright author with +explicit written consent. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +%% This notice is provided with respect to IAIK PKCS Wrapper, which may be included with this software: + +Copyright (c) 2002 Graz University of Technology. All rights reserved. +Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: + + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: + + "This product includes software developed by IAIK of Graz University of Technology." + + Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. + +4. The names "Graz University of Technology" and "IAIK of Graz University of Technology" must not be used to endorse or promote products derived from this software without prior written permission. + +5. Products derived from this software may not be called "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior written permission of Graz University of Technology. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +%% This notice is provided with respect to Document Object Model (DOM) v. Level 3, which may be included with this software: + +W3Cýý SOFTWARE NOTICE AND LICENSE + +http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 + +This work (and included software, documentation such as READMEs, or other related items) is being +provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you +(the licensee) agree that you have read, understood, and will comply with the following terms and conditions. + +Permission to copy, modify, and distribute this software and its documentation, with or without modification, for +any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies +of the software and documentation or portions thereof, including modifications: + 1.The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. + 2.Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the + W3C Software Short Notice should be included (hypertext is preferred, text is permitted) within the body + of any redistributed or derivative code. + 3.Notice of any changes or modifications to the files, including the date changes were made. (We + recommend you provide URIs to the location from which the code is derived.) +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKENO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THEUSE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS,COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL ORCONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. +The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the +software without specific, written prior permission. Title to copyright in this software and any associated +documentation will at all times remain with copyright holders. + +____________________________________ + +This formulation of W3C's notice and license became active on December 31 2002. This version removes the +copyright ownership notice such that this license can be used with materials other than those owned by the +W3C, reflects that ERCIM is now a host of the W3C, includes references to this specific dated version of the +license, and removes the ambiguous grant of "use". Otherwise, this version is the same as the previous +version and is written so as to preserve the Free Software Foundation's assessment of GPL compatibility and +OSI's certification under the Open Source Definition. Please see our Copyright FAQ for common questions +about using materials from our site, including specific terms and conditions for packages like libwww, Amaya, +and Jigsaw. Other questions about this notice can be directed to +site-policy@w3.org. + +%% This notice is provided with respect to Xalan, Xerces, which may be included with this software: + +/* + * The Apache Software License, Version 1.1 + * + * + * Copyright (c) 1999-2003 The Apache Software Foundation. All rights * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * + * 4. The names "Xerces" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation and was + * originally based on software copyright (c) 1999, International + * Business Machines, Inc., http://www.ibm.com. For more + * information on the Apache Software Foundation, please see + * + +%% This notice is provided with respect to JavaScript, which may be included with this software: + +AMENDMENTS +The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License.  Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1. +Additional Terms applicable to the Netscape Public License. +I. Effect. +These additional terms described in this Netscape Public License -- Amendments shall apply to the Mozilla Communicator client code and to all Covered Code under this License. +II. ''Netscape's Branded Code'' means Covered Code that Netscape distributes and/or permits others to distribute under one or more trademark(s) which are controlled by Netscape but which are not licensed for use under this License. +III. Netscape and logo. +This License does not grant any rights to use the trademarks "Netscape'', the "Netscape N and horizon'' logo or the "Netscape lighthouse" logo, "Netcenter", "Gecko", "Java" or "JavaScript", "Smart Browsing" even if such marks are included in the Original Code or Modifications. +IV. Inability to Comply Due to Contractual Obligation. +Prior to licensing the Original Code under this License, Netscape has licensed third party code for use in Netscape's Branded Code. To the extent that Netscape is limited contractually from making such third party code available under this License, Netscape may choose to reintegrate such code into Covered Code without being required to distribute such code in Source Code form, even if such code would otherwise be considered ''Modifications'' under this License. +V. Use of Modifications and Covered Code by Initial Developer. +V.1. In General. +The obligations of Section 3 apply to Netscape, except to the extent specified in this Amendment, Section V.2 and V.3. +V.2. Other Products. +Netscape may include Covered Code in products other than the Netscape's Branded Code which are released by Netscape during the two (2) years following the release date of the Original Code, without such additional products becoming subject to the terms of this License, and may license such additional products on different terms from those contained in this License. +V.3. Alternative Licensing. +Netscape may license the Source Code of Netscape's Branded Code, including Modifications incorporated therein, without such Netscape Branded Code becoming subject to the terms of this License, and may license such Netscape Branded Code on different terms from those contained in this License. +  +VI. Litigation. +Notwithstanding the limitations of Section 11 above, the provisions regarding litigation in Section 11(a), (b) and (c) of the License shall apply to all disputes relating to this License. + +EXHIBIT A-Netscape Public License. +  +''The contents of this file are subject to the Netscape Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/NPL/ +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. +The Original Code is Mozilla Communicator client code, released March 31, 1998. +The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1998-1999 Netscape Communications Corporation. All Rights Reserved. +Contributor(s): ______________________________________. +  +Alternatively, the contents of this file may be used under the terms of the _____ license (the  "[___] License"), in which case the provisions of [______] License are applicable  instead of those above.  If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting  the provisions above and replace  them with the notice and other provisions required by the [___] License.  If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License." + +MOZILLA PUBLIC LICENSE +Version 1.1 + +1. Definitions. +1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party. +1.1. ''Contributor'' means each entity that creates or contributes to the creation of Modifications. +1.2. ''Contributor Version'' means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. +1.3. ''Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. +1.4. ''Electronic Distribution Mechanism'' means a mechanism generally accepted in the software development community for the electronic transfer of data. +1.5. ''Executable'' means Covered Code in any form other than Source Code. +1.6. ''Initial Developer'' means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. +1.7. ''Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. +1.8. ''License'' means this document. +1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. +1.9. ''Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: +A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. +B. Any new file that contains any part of the Original Code or previous Modifications. +  +1.10. ''Original Code'' means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. +1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation,  method, process, and apparatus claims, in any patent Licensable by grantor. +1.11. ''Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. +1.12. "You'' (or "Your")  means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. +2. Source Code License. +2.1. The Initial Developer Grant. +The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: +(a)  under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and +(b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof). +  +(c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License. +(d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code;  or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices. +  +2.2. Contributor Grant. +Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license +  +(a)  under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and +(b) under Patent Claims infringed by the making, using, or selling of  Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of  Modifications made by that Contributor with its Contributor Version (or portions of such combination). +(c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code. +(d)    Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2)  separate from the Contributor Version;  3)  for infringements caused by: i) third party modifications of Contributor Version or ii)  the combination of Modifications made by that Contributor with other software  (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor. + +3. Distribution Obligations. +3.1. Application of License. +The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. +3.2. Availability of Source Code. +Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. +3.3. Description of Modifications. +You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. +3.4. Intellectual Property Matters +(a) Third Party Claims. +If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. +(b) Contributor APIs. +If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file. +  +          (c)    Representations. +Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License. + +3.5. Required Notices. +You must duplicate the notice in Exhibit A in each file of the Source Code.  If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice.  If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A.  You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code.  You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. +3.6. Distribution of Executable Versions. +You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. +3.7. Larger Works. +You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. +4. Inability to Comply Due to Statute or Regulation. +If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. +5. Application of this License. +This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code. +6. Versions of the License. +6.1. New Versions. +Netscape Communications Corporation (''Netscape'') may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. +6.2. Effect of New Versions. +Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License. +6.3. Derivative Works. +If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases ''Mozilla'', ''MOZILLAPL'', ''MOZPL'', ''Netscape'', "MPL", ''NPL'' or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.) +7. DISCLAIMER OF WARRANTY. +COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. +8. TERMINATION. +8.1.  This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. +8.2.  If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant")  alleging that: +(a)  such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i)  agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant.  If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above. +(b)  any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant. +8.3.  If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. +8.4.  In the event of termination under Sections 8.1 or 8.2 above,  all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination. +9. LIMITATION OF LIABILITY. +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. +10. U.S. GOVERNMENT END USERS. +The Covered Code is a ''commercial item,'' as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of ''commercial computer software'' and ''commercial computer software documentation,'' as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. +11. MISCELLANEOUS. +This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. +12. RESPONSIBILITY FOR CLAIMS. +As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. +13. MULTIPLE-LICENSED CODE. +Initial Developer may designate portions of the Covered Code as "Multiple-Licensed".  "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the NPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. +``The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +ANY KIND, either express or implied. See the License for the specific language governing rights and +limitations under the License. +The Original Code is ______________________________________. +The Initial Developer of the Original Code is ________________________. Portions created by + ______________________ are Copyright (C) ______ _______________________. All Rights +Reserved. +Contributor(s): ______________________________________. +Alternatively, the contents of this file may be used under the terms of the _____ license (the  "[___] License"), in which case the provisions of [______] License are applicable  instead of those above.  If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting  the provisions above and replace  them with the notice and other provisions required by the [___] License.  If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License." +[NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.] + +%% This notice is provided with respect to Mesa 3-D graphics library v. 5, which may be included with this software: + +Copyright (c) 2007 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +%% This notice is provided with respect to Byte Code Engineering Library (BCEL), which may be included with this software: + + Apache Software License + + /* +==================================================================== * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. Allrights + * reserved. + * + * Redistribution and use in source and binary forms, withor without + * modification, are permitted provided that the followingconditions + * are met: + * + * 1. Redistributions of source code must retain the abovecopyright + * notice, this list of conditions and the followingdisclaimer. + * + * 2. Redistributions in binary form must reproduce theabove copyright + * notice, this list of conditions and the followingdisclaimer in + * the documentation and/or other materials providedwith the + * distribution. + * + * 3. The end-user documentation included with theredistribution, + * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation +(http://www.apache.org/)." + * Alternately, this acknowledgment may appear in thesoftware itself, + * if and wherever such third-party acknowledgmentsnormally appear. + * + * 4. The names "Apache" and "Apache Software Foundation"and + * "Apache BCEL" must not be used to endorse or promoteproducts + * derived from this software without prior writtenpermission. For + * written permission, please contact apache@apache.org. * + * 5. Products derived from this software may not be called"Apache", + * "Apache BCEL", nor may "Apache" appear in their name,without + * prior written permission of the Apache SoftwareFoundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED ORIMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIEDWARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSEARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWAREFOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVERCAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICTLIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING INANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + * +==================================================================== * + * This software consists of voluntary contributions madeby many + * individuals on behalf of the Apache Software +Foundation. For more + * information on the Apache Software Foundation, pleasesee + * . + */ + +%% This notice is provided with respect to Regexp, Regular Expression Package, which may be included with this software: + +The Apache Software License, Version 1.1 +Copyright (c) 2001 The Apache Software Foundation. All rights +reserved. +Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the +distribution. + +3. The end-user documentation included with the redistribution, +if any, must include the following acknowledgment: +"This product includes software developed by the +Apache Software Foundation (http://www.apache.org/)." +Alternately, this acknowledgment may appear in the software itself, +if and wherever such third-party acknowledgments normally appear. + +4. The names "Apache" and "Apache Software Foundation" and +"Apache Turbine" must not be used to endorse or promote products +derived from this software without prior written permission. For +written permission, please contact apache@apache.org. + +5. Products derived from this software may not be called "Apache", +"Apache Turbine", nor may "Apache" appear in their name, without +prior written permission of the Apache Software Foundation. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR +ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +==================================================================== +This software consists of voluntary contributions made by many +individuals on behalf of the Apache Software Foundation. For more +information on the Apache Software Foundation, please see + +http://www.apache.org. + +%% This notice is provided with respect to CUP Parser Generator for Java, which may be included with this software: + +CUP Parser Generator Copyright Notice, License, and Disclaimer + +Copyright 1996-1999 by Scott Hudson, Frank Flannery, C. Scott Ananian +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided thatthe above copyright notice appear in all copies and that both the copyrightnotice and this permission notice and warranty disclaimer appear in +supporting documentation, and that the names of the authors or their employersnot be used in advertising or publicity pertaining to distribution of +the software without specific, written prior permission. + +The authors and their employers disclaim all warranties with regard to thissoftware, including all implied warranties of merchantability and +fitness. In no event shall the authors or their employers be liable for anyspecial, indirect or consequential damages or any damages whatsoever +resulting from loss of use, data or profits, whether in an action of contract,negligence or other tortious action, arising out of or in connection withthe use or performance of this software. + +%% This notice is provided with respect to SAX v. 2.0.1, which may be included with this software: + +Copyright Status + + SAX is free! + + In fact, it's not possible to own a license to SAX, since it's been placed in the public + domain. + + No Warranty + + Because SAX is released to the public domain, there is no warranty for the design or for + the software implementation, to the extent permitted by applicable law. Except when + otherwise stated in writing the copyright holders and/or other parties provide SAX "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 SAX is with you. Should SAX prove defective, you + assume the cost of all necessary servicing, repair or correction. + + 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 SAX, be liable to you for + damages, including any general, special, incidental or consequential damages arising out of + the use or inability to use SAX (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 SAX to + operate with any other programs), even if such holder or other party has been advised of + the possibility of such damages. + + Copyright Disclaimers + + This page includes statements to that effect by David Megginson, who would have been + able to claim copyright for the original work. + SAX 1.0 + + Version 1.0 of the Simple API for XML (SAX), created collectively by the membership of + the XML-DEV mailing list, is hereby released into the public domain. + + No one owns SAX: you may use it freely in both commercial and non-commercial + applications, bundle it with your software distribution, include it on a CD-ROM, list the + source code in a book, mirror the documentation at your own web site, or use it in any + other way you see fit. + + David Megginson, sax@megginson.com + 1998-05-11 + + SAX 2.0 + + I hereby abandon any property rights to SAX 2.0 (the Simple API for XML), and release + all of the SAX 2.0 source code, compiled code, and documentation contained in this + distribution into the Public Domain. SAX comes with NO WARRANTY or guarantee of + fitness for any purpose. + + David Megginson, david@megginson.com + 2000-05-05 + +%% This notice is provided with respect to Cryptix, which may be included with this software: + +Cryptix General License + +Copyright © 1995-2003 The Cryptix Foundation Limited. All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions aremet: + + 1.Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2.Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +THIS SOFTWARE IS PROVIDED BY THE CRYPTIX FOUNDATION LIMITED AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS ORIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FORA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CRYPTIX FOUNDATION LIMITED OR CONTRIBUTORS BELIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOTLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESSINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OFTHE POSSIBILITY OF SUCH DAMAGE. + +%% This notice is provided with respect to X Window System, which may be included with this software: + +Copyright The Open Group + +Permission to use, copy, modify, distribute, and sell this software and itsdocumentation for any purpose is hereby granted without fee, provided that theabove copyright notice appear in all copies and that both that copyright noticeand this permission notice appear in supporting documentation. + +The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESSFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUPBE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OFCONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THESOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. + +Portions also covered by other licenses as noted in the above URL. + +%% This notice is provided with respect to Retroweaver, which may be included with this software: + +Copyright (c) February 2004, Toby Reyelts +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of Toby Reyelts nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICTLIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +%% This notice is provided with respect to stripper, which may be included with this software: + +Stripper : debug information stripper + Copyright (c) 2003 Kohsuke Kawaguchi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +%% This notice is provided with respect to libpng official PNG reference library, which may be included with this software: + +This copy of the libpng notices is provided for your convenience. In case ofany discrepancy between this copy and the notices in the file png.h that isincluded in the libpng distribution, the latter shall prevail. + +COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: + +If you modify libpng you may insert additional notices immediately followingthis sentence. + +libpng version 1.2.6, December 3, 2004, is +Copyright (c) 2004 Glenn Randers-Pehrson, and is +distributed according to the same disclaimer and license as libpng-1.2.5with the following individual added to the list of Contributing Authors + Cosmin Truta + +libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, areCopyright (c) 2000-2002 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-1.0.6with the following individuals added to the list of Contributing Authors + Simon-Pierre Cadieux + Eric S. Raymond + Gilles Vollant + +and with the following additions to the disclaimer: + + There is no warranty against interference with your enjoyment of the library or against infringement. There is no warranty that our + efforts or the library will fulfill any of your particular purposes or needs. This library is provided with all faults, and the entire risk of satisfactory quality, performance, accuracy, and effort is with the user. + +libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, areCopyright (c) 1998, 1999 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-0.96,with the following individuals added to the list of Contributing Authors: + Tom Lane + Glenn Randers-Pehrson + Willem van Schaik + +libpng versions 0.89, June 1996, through 0.96, May 1997, are +Copyright (c) 1996, 1997 Andreas Dilger +Distributed according to the same disclaimer and license as libpng-0.88,with the following individuals added to the list of Contributing Authors: + John Bowler + Kevin Bracey + Sam Bushell + Magnus Holmgren + Greg Roelofs + Tom Tanner + +libpng versions 0.5, May 1995, through 0.88, January 1996, are +Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. + +For the purposes of this copyright and license, "Contributing Authors"is defined as the following set of individuals: + + Andreas Dilger + Dave Martindale + Guy Eric Schalnat + Paul Schmidt + Tim Wegner + +The PNG Reference Library is supplied "AS IS". The Contributing Authorsand Group 42, Inc. disclaim all warranties, expressed or implied, +including, without limitation, the warranties of merchantability and offitness for any purpose. The Contributing Authors and Group 42, Inc. +assume no liability for direct, indirect, incidental, special, exemplary,or consequential damages, which may result from the use of the PNG +Reference Library, even if advised of the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute thissource code, or portions hereof, for any purpose, without fee, subjectto the following restrictions: + +1. The origin of this source code must not be misrepresented. + +2. Altered versions must be plainly marked as such and must not + be misrepresented as being the original source. + +3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + +The Contributing Authors and Group 42, Inc. specifically permit, withoutfee, and encourage the use of this source code as a component to +supporting the PNG file format in commercial products. If you use thissource code in a product, acknowledgment is not required but would be +appreciated. + + +A "png_get_copyright" function is available, for convenient use in "about"boxes and the like: + + printf("%s",png_get_copyright(NULL)); + +Also, the PNG logo (in PNG format, of course) is supplied in the +files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + +Libpng is OSI Certified Open Source Software. OSI Certified Open Source is acertification mark of the Open Source Initiative. + +Glenn Randers-Pehrson +glennrp at users.sourceforge.net +December 3, 2004 + +%% This notice is provided with respect to Libungif - An uncompressed GIF library, which may be included with this software: + +The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond + +Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included inall copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS INTHE SOFTWARE. + +%% This notice is provided with respect to XML Resolver library, Xalan J2, and StAX API, which may be included with this software: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +%% Some Portions licensed from IBM are available at: +http://www.ibm.com/software/globalization/icu/ + +%% This notice is provided with respect to ICU4J, ICU 1.8.1 and later, which may be included with this software: + +ICU License - ICU 1.8.1 and later COPYRIGHT AND PERMISSION NOTICE Cop +yright (c) +1995-2003 International Business Machines Corporation and others All rightsreserved. Permission is hereby granted, free of charge, to any person obtaininga copy of this software and associated documentation files (the "Software"), todeal in the Software without restriction, including without limitation therights to use, copy, modify, merge, publish, distribute, and/or sell copies ofthe Software, and to permit persons to whom the Software is furnished to do so,provided that the above copyright notice(s) and this permission notice appear inall copies of the Software and that both the above copyright notice(s) and thispermission notice appear in supporting documentation. THE SOFTWARE IS PROVIDED"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOTLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSEAND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHTHOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY C + LAIM, OR ANYSPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTINGFROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCEOR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE ORPERFORMANCE OF THIS SOFTWARE. Except as contained in this notice, the name of acopyright holder shall not be used in advertising or otherwise to promote thesale, use or other dealings in this Software without prior written authorizationof the copyright holder. + +%% This notice is provided with respect to Jing, which may be included with this software: + +Jing Copying Conditions + +Copyright (c) 2001-2003 Thai Open Source Software Center Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice,this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice,this list of conditions and the following disclaimer in the documentation and/orother materials provided with the distribution. + * Neither the name of the Thai Open Source Software Center Ltd nor the namesof its contributors may be used to endorse or promote products derived from thissoftware without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ANDANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIEDWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AREDISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANYDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ONANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THISSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +%% This notice is provided with respect to RELAX NG Object Model/Parser, which may be included with this software: + + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy ofthis software and associated documentation files (the "Software"), to deal inthe Software without restriction, including without limitation the rights touse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies ofthe Software, and to permit persons to whom the Software is furnished to do so,subject to the following conditions: + +The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESSFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS ORCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHERIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR INCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +%% This notice is provided with respect to XFree86-VidMode Extension, which may be included with this software: + +Version 1.1 of XFree86 ProjectLicence. + + Copyright (C) 1994-2004 The XFree86 Project, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to deal inthe Software without restriction, including without limitation the rights touse, copy, modify, merge, publish, distribute, sublicence, and/or sell copies ofthe Software, and to permit persons to whom the Software is furnished to do so,subject to the following conditions: + + 1. Redistributions of source code must retain the above copyright notice,this list of conditions, and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyrightnotice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution, and in thesame place and form as other copyright, license and disclaimer information. 3. The end-user documentation included with the redistribution, if any,must include the following acknowledgment: "This product includes softwaredeveloped by The XFree86 Project, Inc (http://www.xfree86.org/) and itscontributors", in the same place and form as other third-party acknowledgments.Alternately, this acknowledgment may appear in the software itself, in the sameform and location as other such third-party acknowledgments. + 4. Except as contained in this notice, the name of The XFree86 Project,Inc shall not be used in advertising or otherwise to promote the sale, use orother dealings in this Software without prior written authorization from TheXFree86 Project, Inc. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY ANDFITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE XFREE86PROJECT, INC OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ORBUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER INCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISINGIN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITYOF SUCH DAMAGE. + +%% This notice is provided with respect to XML Security, which may be included with this software: + + The Apache Software License, + Version 1.1 + + + PDF + + + Copyright (C) 2002 The Apache SoftwareFoundation. + All rights reserved. Redistribution anduse in + source and binary forms, with or withoutmodifica- + tion, are permitted provided that thefollowing + conditions are met: 1. Redistributions ofsource + code must retain the above copyrightnotice, this + list of conditions and the followingdisclaimer. + 2. Redistributions in binary form mustreproduce + the above copyright notice, this list of conditions and the following disclaimerin the + documentation and/or other materialsprovided with + the distribution. 3. The end-userdocumentation + included with the redistribution, if any,must + include the following acknowledgment:"This + product includes software developed bythe Apache + Software Foundation +(http://www.apache.org/)." + Alternately, this acknowledgment mayappear in the + software itself, if and wherever suchthird-party + acknowledgments normally appear. 4. Thenames + "Apache Forrest" and "Apache SoftwareFoundation" + must not be used to endorse or promoteproducts + derived from this software without priorwritten + permission. For written permission,please contact + apache@apache.org. 5. Products derivedfrom this + software may not be called "Apache", normay + "Apache" appear in their name, withoutprior + written permission of the Apache Software Foundation. THIS SOFTWARE IS PROVIDED``AS IS'' + AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THEIMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESSFOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NOEVENT + SHALL THE APACHE SOFTWARE FOUNDATION ORITS + CONTRIBUTORS BE LIABLE FOR ANY DIRECT,INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, ORCONSEQUENTIAL + DAMAGES (INCLU- DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ORSERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANYTHEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICTLIABILITY, + OR TORT (INCLUDING NEGLIGENCE OROTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + SUCH DAMAGE. This software consists ofvoluntary + contributions made by many individuals onbehalf + of the Apache Software Foundation. Formore + information on the Apache SoftwareFoundation, + please see . + +%% This notice is provided with respect to Independent JPEG Group's software (libjpeg), which may be included with this software: + +In plain English: + +1. We don't promise that this software works. (But if you find any bugs, + please let us know!) +2. You can use this software for whatever you want. You don't have to pay us. +3. You may not pretend that you wrote this software. If you use it in a program, you must acknowledge somewhere in your documentation that you've used the IJG code. + +In legalese: + +The authors make NO WARRANTY or representation, either express or implied, with respect to this software, its quality, accuracy, merchantability, or fitness for a particular purpose. This software is provided "AS IS", and you, its user, assume the entire risk as to its quality and accuracy. + +This software is copyright (C) 1991-1998, Thomas G. Lane. +All Rights Reserved except as specified below. + +Permission is hereby granted to use, copy, modify, and distribute this software (or portions thereof) for any purpose, without fee, subject to these conditions: + +(1) If any part of the source code for this software is distributed, then this +README file must be included, with this copyright and no-warranty notice unaltered; and any additions, deletions, or changes to the original files must be clearly indicated in accompanying documentation. + +(2) If only executable code is distributed, then the accompanying documentation must state that "this software is based in part on the work of the Independent JPEG Group". + +(3) Permission for use of this software is granted only if the user accepts full responsibility for any undesirable consequences; the authors accept NO LIABILITY for damages of any kind. + +These conditions apply to any software derived from or based on the IJG code, not just to the unmodified library. If you use our work, you ought to acknowledge us. + +Permission is NOT granted for the use of any IJG author's name or company name in advertising or publicity relating to this software or products derived from it. This software may be referred to only as "the Independent JPEG Group's software". + +We specifically permit and encourage the use of this software as the basis of commercial products, provided that all warranty or liability claims are assumed by the product vendor. + +ansi2knr.c is included in this distribution by permission of L. Peter Deutsch, sole proprietor of its copyright holder, Aladdin Enterprises of Menlo Park, CA. ansi2knr.c is NOT covered by the above copyright and conditions, but instead by the usual distribution terms of the Free Software Foundation; principally, that you must include source code if you redistribute it. (See the file ansi2knr.c for full details.) However, since ansi2knr.c is not needed as part of any program generated from the IJG code, this does not limit you more than the foregoing paragraphs do. + +The Unix configuration script "configure" was produced with GNU Autoconf. It is copyright by the Free Software Foundation but is freely distributable. The same holds for its supporting scripts (config.guess, config.sub, ltconfig, ltmain.sh). Another support script, install-sh, is copyright by M.I.T. but is also freely distributable. + +It appears that the arithmetic coding option of the JPEG spec is covered by patents owned by IBM, AT&T, and Mitsubishi. Hence arithmetic coding cannot legally be used without obtaining one or more licenses. For this reason, support for arithmetic coding has been removed from the free JPEG software. (Since arithmetic coding provides only a marginal gain over the unpatented Huffman mode, it is unlikely that very many implementations will support it.) So far as we are aware, there are no patent restrictions on the remaining code. + +The IJG distribution formerly included code to read and write GIF files. To avoid entanglement with the Unisys LZW patent, GIF reading support has been removed altogether, and the GIF writer has been simplified to produce "uncompressed GIFs". This technique does not use the LZW algorithm; the resulting GIF files are larger than usual, but are readable by all standard GIF decoders. + +We are required to state that + "The Graphics Interchange Format(c) is the Copyright property of + CompuServe Incorporated. GIF(sm) is a Service Mark property of + CompuServe Incorporated." + +%% This notice is provided with respect to X Resize and Rotate (Xrandr) Extension, which may be included with this software: +2. XFree86 License + +XFree86 code without an explicit copyright is covered by the following +copyright/license: + +Copyright (C) 1994-2003 The XFree86 Project, Inc. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 +PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the XFree86 Project shall not be +used in advertising or otherwise to promote the sale, use or other dealings in +this Software without prior written authorization from the XFree86 Project. + +%% This notice is provided with respect to fontconfig, which may be included with this software: +Id: COPYING,v 1.3 2003/04/04 20:17:40 keithp Exp $ +Copyright 2001,2003 Keith Packard + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Keith Packard not be used in +advertising or publicity pertaining to distribution of the software without +specific, written prior permission. Keith Packard makes no +representations about the suitability of this software for any purpose. It +is provided "as is" without express or implied warranty. + +KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +%% This notice is provided with respect to XFree86, which may be included with this software: +Copyright (C) 1994-2002 The XFree86 Project, Inc. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated +documentation files (the "Software"), to deal in the Software without +restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the XFree86 Project shall not be +used in advertising or otherwise +to promote the sale, use or other dealings in this Software without prior +written authorization from the XFree86 +Project. +%% This notice is provided with respect to Fast Infoset, which may be included with this software: +* Fast Infoset ver. 0.1 software ("Software") +* +* Copyright, 2004-2005 Sun Microsystems, Inc. All Rights Reserved. +* +* Software is licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. You may +* obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations. +* +* Sun supports and benefits from the global community of open source +* developers, and thanks the community for its important contributions and +* open standards-based technology, which Sun has adopted into many of its +* products. +* +* Please note that portions of Software may be provided with notices and +* open source licenses from such communities and third parties that govern the +* use of those portions, and any licenses granted hereunder do not alter any +* rights and obligations you may have under such open source licenses, +* however, the disclaimer of warranty and limitation of liability provisions +* in this License will apply to all Software in this distribution. +* +* You acknowledge that the Software is not designed, licensed or intended +* for use in the design, construction, operation or maintenance of any nuclear +* facility. +* +* Apache License +* Version 2.0, January 2004 +* http://www.apache.org/licenses/ +* +*/ +/* +* ==================================================================== +* +* This code is subject to the freebxml License, Version 1.1 +* +* Copyright (c) 2001 - 2005 freebxml.org. All rights reserved. +* +* $Header: /cvs/fi/FastInfoset/src/com/sun/xml/internal/fastinfoset/AbstractResourceBundle.java,v 1.2 +*  ==================================================================== +*/ +%% This notice is provided with respect to Kerberos, which may be included with this software: + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government.  It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +%% This notice is provided with respect to Unicode's CLDR data repository, which may be included with this software: + + Unicode Copyright + + For the general privacy policy governing access to this site, see the +Unicode Privacy Policy. For trademark usage, see the the Unicode Consortium +Trademarks and Logo Policy. + Notice to End User: Terms of Use + Carefully read the following legal agreement ("Agreement"). Use or copying +of the software and/or codes provided with this agreement (The "Software") +constitutes your acceptance of these terms + + 1. Unicode Copyright. + 1. Copyright © 1991-2005 Unicode, Inc. All rights reserved. + 2. Certain documents and files on this website contain a legend +indicating that "Modification is permitted." Any person is hereby authorized, +without fee, to modify such documents and files to create derivative works +conforming to the Unicode® Standard, subject to Terms and Conditions herein. + 3. Any person is hereby authorized, without fee, to view, use, +reproduce, and distribute all documents and files solely for informational +purposes in the creation of products supporting the Unicode Standard, subject to +the Terms and Conditions herein. + 4. Further specifications of rights and restrictions pertaining to +the use of the particular set of data files known as the "Unicode Character +Database" can be found in Exhibit 1. + 5. Further specifications of rights and restrictions pertaining to +the use of the particular set of files that constitute the online edition of The +Unicode Standard, Version 4.0, may be found in V4.0 online edition. + 6. No license is granted to "mirror" the Unicode website where a +fee is charged for access to the "mirror" site. + 7. Modification is not permitted with respect to this document. All +copies of this document must be verbatim. + 2. Restricted Rights Legend. Any technical data or software which is +licensed to the United States of America, its agencies and/or instrumentalities +under this Agreement is commercial technical data or commercial computer +software developed exclusively at private expense as defined in FAR 2.101, or +DFARS 252.227-7014 (June 1995), as applicable. For technical data, use, +duplication, or disclosure by the Government is subject to restrictions as set +forth in DFARS 202.227-7015 Technical Data, Commercial and Items (Nov 1995) and +this Agreement. For Software, in accordance with FAR 12-212 or DFARS 227-7202, +as applicable, use, duplication or disclosure by the Government is subject to +the restrictions set forth in this Agreement. + 3. Warranties and Disclaimers. + 1. This publication and/or website may include technical or +typographical errors or other inaccuracies . Changes are periodically added to +the information herein; these changes will be incorporated in new editions of +the publication and/or website. Unicode may make improvements and/or changes in +the product(s) and/or program(s) described in this publication and/or website at +any time. + 2. If this file has been purchased on magnetic or optical media +from Unicode, Inc. the sole and exclusive remedy for any claim will be exchange +of the defective media within ninety (90) days of original purchase. + 3. EXCEPT AS PROVIDED IN SECTION C.2, THIS PUBLICATION AND/OR +SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND EITHER EXPRESS, +IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. UNICODE +AND ITS LICENSORS ASSUME NO RESPONSIBILITY FOR ERRORS OR OMISSIONS IN THIS +PUBLICATION AND/OR SOFTWARE OR OTHER DOCUMENTS WHICH ARE REFERENCED BY OR LINKED +TO THIS PUBLICATION OR THE UNICODE WEBSITE. + 4. Waiver of Damages. In no event shall Unicode or its licensors be +liable for any special, incidental, indirect or consequential damages of any +kind, or any damages whatsoever, whether or not Unicode was advised of the +possibility of the damage, including, without limitation, those resulting from +the following: loss of use, data or profits, in connection with the use, +modification or distribution of this information or its derivatives. + 5. Trademarks. + 1. Unicode and the Unicode logo are registered trademarks of +Unicode, Inc. + 2. This site contains product names and corporate names of other +companies. All product names and company names and logos mentioned herein are +the trademarks or registered trademarks of their respective owners. Other +products and corporate names mentioned herein which are trademarks of a third +party are used only for explanation and for the owners' benefit and with no +intent to infringe. + 3. Use of third party products or information referred to herein is +at the user's risk. + 6. Miscellaneous. + 1. Jurisdiction and Venue. This server is operated from a location +in the State of California, United States of America. Unicode makes no +representation that the materials are appropriate for use in other locations. If +you access this server from other locations, you are responsible for compliance +with local laws. This Agreement, all use of this site and any claims and damages +resulting from use of this site are governed solely by the laws of the State of +California without regard to any principles which would apply the laws of a +different jurisdiction. The user agrees that any disputes regarding this site +shall be resolved solely in the courts located in Santa Clara County, +California. The user agrees said courts have personal jurisdiction and agree to +waive any right to transfer the dispute to any other forum. + 2. Modification by Unicode Unicode shall have the right to modify +this Agreement at any time by posting it to this site. The user may not assign +any part of this Agreement without Unicode's prior written consent. + 3. Taxes. The user agrees to pay any taxes arising from access to +this website or use of the information herein, except for those based on +Unicode's net income. + 4. Severability. If any provision of this Agreement is declared +invalid or unenforceable, the remaining provisions of this Agreement shall +remain in effect. + 5. Entire Agreement. This Agreement constitutes the entire +agreement between the parties. + +EXHIBIT 1 +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + + Unicode Data Files include all data files under the directories +http://www.unicode.org/Public/ and http://www.unicode.org/reports/. Unicode +Software includes any source code under the directories +http://www.unicode.org/Public/ and http://www.unicode.org/reports/. + + NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES +("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND +AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU +DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES +OR SOFTWARE. + + COPYRIGHT AND PERMISSION NOTICE + + Copyright Ă?Â,Ă,© 1991-2004 Unicode, Inc. All rights reserved. Distributed under +the Terms of Use in http://www.unicode.org/copyright.html. + + Permission is hereby granted, free of charge, to any person obtaining a copy +of the Unicode data files and associated documentation (the "Data Files") or +Unicode software and associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files or +Software are furnished to do so, provided that (a) the above copyright notice(s) +and this permission notice appear with all copies of the Data Files or Software, +(b) both the above copyright notice(s) and this permission notice appear in +associated documentation, and (c) there is clear notice in each modified Data +File or in the Software as well as in the documentation associated with the Data +File(s) or Software that the data or software has been modified. + + THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD +PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. + + Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in these Data Files or Software without prior written authorization of the +copyright holder. + + Unicode and the Unicode logo are trademarks of Unicode, Inc., and may be +registered in some jurisdictions. All other trademarks and registered trademarks +mentioned herein are the property of their respective owners. +%% This notice is provided with respect to RSA PKCS#11 Header Files & Specification, which may be included with this software: + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government.  It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission.  FundsXpress makes no representations about the suitability of + * this software for any purpose.  It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +%% This notice is provided with respect to certain files/code which may included in the implementation of AWT within the software: + +****************************************************** +BEGIN  src/solaris/native/sun/awt/HPkeysym.h +Copyright 1987, 1998  The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts, + +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Hewlett Packard +or Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +HEWLETT-PACKARD MAKES NO WARRANTY OF ANY KIND WITH REGARD +TO THIS SOFWARE, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE.  Hewlett-Packard shall not be liable for errors +contained herein or direct, indirect, special, incidental or +consequential damages in connection with the furnishing, +performance, or use of this material. + +END  src/solaris/native/sun/awt/HPkeysym.h +****************************************************** +****************************************************** +BEGIN src/solaris/native/sun/awt/Xrandr.h +/* + * $XFree86: xc/lib/Xrandr/Xrandr.h,v 1.9 2002/09/29 23:39:44 keithp Exp $ + * + * Copyright © 2000 Compaq Computer Corporation, Inc. + * Copyright © 2002 Hewlett-Packard Company, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Compaq not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission.  HP makes no representations about the + * suitability of this software for any purpose.  It is provided "as is" + * without express or implied warranty. + * + * HP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL COMPAQ + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author:  Jim Gettys, HP Labs, HP. + */ + + +END src/solaris/native/sun/awt/Xrandr.h +****************************************************** +BEGIN src/solaris/native/sun/awt/extutil.h +/* + * $Xorg: extutil.h,v 1.3 2000/08/18 04:05:45 coskrey Exp $ + * +Copyright 1989, 1998  The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + * Author:  Jim Fulton, MIT The Open Group + * + *                     Xlib Extension-Writing Utilities + * + * This package contains utilities for writing the client API for various + * protocol extensions.  THESE INTERFACES ARE NOT PART OF THE X STANDARD AND + * ARE SUBJECT TO CHANGE! + */ +/* $XFree86: xc/include/extensions/extutil.h,v 1.5 2001/01/17 17:53:20 dawes Exp $ */ + +END src/solaris/native/sun/awt/extutil.h +****************************************************** +BEGIN   src/solaris/native/sun/awt/fontconfig.h +/* + * $RCSId: xc/lib/fontconfig/fontconfig/fontconfig.h,v 1.30 2002/09/26 00:17:27 +keithp Exp $ + * + * Copyright © 2001 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission.  Keith Packard makes no + * representations about the suitability of this software for any purpose.  It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + +END   src/solaris/native/sun/awt/fontconfig.h +****************************************************** +BEGIN src/solaris/native/sun/awt/list.c +AND  src/solaris/native/sun/awt/list.h +AND src/solaris/native/sun/awt/multiVis.c +AND  src/solaris/native/sun/awt/multiVis.h +AND  src/solaris/native/sun/awt/wsutils.h + +Copyright (c) 1994 Hewlett-Packard Co. +Copyright (c) 1996  X Consortium + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the X Consortium. + +END src/solaris/native/sun/awt/list.c +AND  src/solaris/native/sun/awt/list.h +AND src/solaris/native/sun/awt/multiVis.c +AND  src/solaris/native/sun/awt/multiVis.h +AND  src/solaris/native/sun/awt/wsutils.h + +***************************************************************** +BEGIN src/solaris/native/sun/awt/randr.h + + * + * Copyright © 2000, Compaq Computer Corporation, + * Copyright © 2002, Hewlett Packard, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Compaq or HP not be used in advertising + * or publicity pertaining to distribution of the software without specific, + * written prior permission.  HP makes no representations about the + * suitability of this software for any purpose.  It is provided "as is" + * without express or implied warranty. + * + * HP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL HP + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author:  Jim Gettys, HP Labs, Hewlett-Packard, Inc. + +END src/solaris/native/sun/awt/randr.h +***************************************************** + +BEGIN src/solaris/native/sun/java2d/opengl/J2D_GL/glx.h + * Mesa 3-D graphics library + * Version:  4.1 + * + * Copyright (C) 1999-2002  Brian Paul   All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +END src/solaris/native/sun/java2d/opengl/J2D_GL/glx.h diff --git a/hotspot/agent/doc/ReadMe-JavaScript.text b/hotspot/agent/doc/ReadMe-JavaScript.text new file mode 100644 index 00000000000..432aaa45aa0 --- /dev/null +++ b/hotspot/agent/doc/ReadMe-JavaScript.text @@ -0,0 +1,38 @@ +The HotSpot Serviceability Agent (SA) is a debugger for hotspot core +dumps and hung processes. There is a read-only JDI (Java Debugger +Interface) implementation on top of SA. This is part of JDK product and +the classes are in $JDK/tools/sa-jdi.jar. + +In addition, there are few serviceability tools in $JDK/bin, namely, +jstack (java stack trace tool), jmap (heap tool), jinfo (Java config +tool) and jsadebugd. The classes for these are also in sa-jdi.jar +file. sa-jdi.jar file is built along with hotspot (libjvm.so) on Solaris +and Linux platforms. On Windows platform, SA-JDI is not included and +serviceability tools do not use SA. + +Apart from these, HotSpot SA consists of a number of tools that are +*not* included in JDK product bits. + +The sources and makefile for all-of-SA (including non-productized stuff) +are under $HOTSPOT_WS/agent directory. The makefile $HOTSPOT/agent/make +directory and shell scripts (and batch files) are used to build and run +SA non-product tools. There is also documentation of SA under +$HOTSPOT/agent/doc directory. + +To build complete SA, you need to have Rhino Mozilla jar (js.jar) +version 1.5R5 under $HOTSPOT/agent/src/share/lib directory. Rhino is +JavaScript interpreter written in Java. Rhino is used to implement SA +features such as + +* SA command line debugger's JavaScript interface + - refer to $HOTSPOT/agent/doc/clhsdb.html + - refer to $HOTSPOT/agent/doc/jsdb.html +* SA simple object query language (SOQL) + - language to query Java heap. + +Rhino's "js.jar" is not included in hotspot source bundles. You need to +download it from http://www.mozilla.org/rhino/download.html. + +Without js.jar, $HOTSPOT/agent/make/Makefile will fail to build. But, +note that sa-jdi.jar containing the productized portions of SA will +still be built when you build hotspot JVM. diff --git a/hotspot/agent/doc/clhsdb.html b/hotspot/agent/doc/clhsdb.html new file mode 100644 index 00000000000..785be2f2568 --- /dev/null +++ b/hotspot/agent/doc/clhsdb.html @@ -0,0 +1,118 @@ + + + +Command line HSDB + + + + +

Command line HSDB

+

+When debugging remote core dumps it is easier to work with command line tools instead of +GUI tools. Command line HSDB (CLHSDB) tool is alternative to SA GUI tool HSDB. +

+ +

+There is also JavaScript based SA command line interface called jsdb. +But, CLHSDB supports Unix shell-like (or dbx/gdb-like) command line interface with +support for output redirection/appending (familiar >, >>), command history and so on. +Each CLHSDB command can have zero or more arguments and optionally end with output redirection +(or append) to a file. Commands may be stored in a file and run using source command. +help command prints usage message for all supported commands (or a specific command) +

+ +

Shell/batch scripts to run command line HSDB

+ +
    +
  • clhsdbproc.sh +
  • clhsdbproc64.sh +
  • clhsdbwindbg.bat +
  • clhsdbwindbg64.bat +
+ +

Annotated output of CLHSDB help command

+ +
+
+Available commands:
+  assert true | false turn on/off asserts in SA code
+  attach pid | exec core  attach SA to a process or core
+  class name find a Java class from debuggee and print oop
+  classes print all loaded Java classes with klassOop
+  detach detach SA from current target
+  dis address [ length ]  disassemble (sparc/x86) specified number of instructions from given address
+  dumpclass { address | name } [ directory ] dump .class file for given klassOop or class name
+  dumpheap [ file ] dump heap in hprof binary format
+  echo [ true | false ] turn on/off command echo mode
+  examine [ address/count ] | [ address,address] show contents of memory from given address
+  field [ type [ name fieldtype isStatic offset address ] ] print info about a field of HotSpot type
+  findpc address print info. about pointer location
+  flags [ flag ] show all -XX flag name value pairs. or just show given flag
+  help [ command ] print help message for all commands or just given command
+  history show command history. usual !command-number syntax works.
+  inspect expression inspect a given oop
+  jdis address show bytecode disassembly of a given methodOop
+  jhisto show Java heap histogram
+  jseval script evaluate a given string as JavaScript code
+  jsload file load and evaluate a JavaScript file
+  jstack [-v] show Java stack trace of all Java threads. -v is verbose mode
+  livenmethods show all live nmethods
+  mem address [ length ] show contents of memory -- also shows closest ELF/COFF symbol if found
+  pmap show Solaris pmap-like output
+  print expression print given klassOop, methodOop or arbitrary address
+  printas type expression print given address as given HotSpot type. eg. print JavaThread <address>
+  printstatics [ type ] print static fields of given HotSpot type (or all types if none specified)
+  pstack [-v] show mixed mode stack trace for all Java, non-Java threads. -v is verbose mode
+  quit quit CLHSDB tool
+  reattach detach and re-attach SA to current target
+  scanoops start end [ type ] scan a Oop from given start to end address
+  search [ heap | codecache | threads ] value search a value in heap or codecache or threads
+  source filename load and execute CLHSDB commands from given file
+  symbol name show address of a given ELF/COFF symbol
+  sysprops show all Java System properties
+  threads show all Java threads
+  tokenize ...
+  type [ type [ name super isOop isInteger isUnsigned size ] ] show info. on HotSpot type
+  universe print gc universe
+  verbose true | false turn on/off verbose mode
+  versioncheck [ true | false ] turn on/off debuggee VM version check
+  whatis address print info about any arbitrary address
+  where { -a | id } print Java stack trace of given Java thread or all Java threads (-a)
+
+
+ +

JavaScript integration

+ +

Few CLHSDB commands are already implemented in JavaScript. It is possible to extend CLHSDB command set +by implementing more commands in a JavaScript file and by loading it by jsload command. jseval +command may be used to evaluate arbitrary JavaScript expression from a string. Any JavaScript function +may be exposed as a CLHSDB command by registering it using JavaScript registerCommand +function. This function accepts command name, usage and name of the JavaScript implementation function +as arguments. +

+ +

Simple CLHSDB command implemented in JavaScript

+ +File: test.js +
+
+function helloImpl(name) {
+    println("hello, " + name);
+}
+
+// register the above JavaScript function as CLHSDB command
+registerCommand("hello", "hello name", "helloImpl");
+
+
+---------
+ +"test.js" can be loaded in CLHSDB prompt using jsload command using + +
+
+hsdb> jsload test.js
+
+
+ + + diff --git a/hotspot/agent/doc/hsdb.html b/hotspot/agent/doc/hsdb.html new file mode 100644 index 00000000000..c835d6b43a1 --- /dev/null +++ b/hotspot/agent/doc/hsdb.html @@ -0,0 +1,44 @@ + + + +SA HSDB GUI + + + +

+Once the HSDB debugger has been launched, the threads list is displayed +if launched with debuggee options (pid or core) in command line. If +HSDB was launched without debuggee, empty screen is shown. +

+

File menu sub-menu options to attach, detach debuggee and exit tool.

+

Tools menu sub-menus include:

+
    +
  • browsing of the annotated stack memory ("Stack Memory" button). It + is currently annotated with the following information: +
      +
    • method names of the Java frames and their extents (supporting + inlined compiled methods) +
    • locations and types of oops, found using the oop map information + from compiled methods (interpreter oop maps coming soon) +
    • if a Java frame was interrupted by a signal (e.g., because of a + crash), annotates the frame with the signal name and number +
    • interpreter codelet descriptions for interpreted frames +
    +
  • finding which thread or threads caused a crash (currently + identified by the presence of a signal handler frame - solaris-only) +
  • browsing of oops using the Oop Inspector. +
  • browsing of the java.lang.Thread object's oop. +
  • Object Histogram and inspection of objects and liveness analysis therein. +
  • Class Browser - view Java classes, bytecode disassembly, + or create .class files for selected classes +
  • native disassembly (sparc, x86 only) and nmethod disassembly with annotations for safepoint details. +
  • view -XX flags, System properties, VM version of debuggee +
+

Windows sub-menu options include:

+
    +
  • Windows sub-menu: Console window to run "CLHSDB" commands in GUI +
  • Windows sub-menu: Debugger console of underlying native debugger (MS Windbg or dbx (if used)) +
+ + + diff --git a/hotspot/agent/doc/index.html b/hotspot/agent/doc/index.html new file mode 100644 index 00000000000..987e5f9c659 --- /dev/null +++ b/hotspot/agent/doc/index.html @@ -0,0 +1,241 @@ + + + +Using HotSpot Serviceability Agent (SA) + + + +

Using HotSpot Serviceability Agent (SA)

+ +

HSDB GUI

+

+The top-level GUI program using the HotSpot Serviceability Agent APIs is +called HSDB, the "HotSpot Debugger". To run it, type "hsdbproc.sh" +or "hsdbwindbg.bat" or 64-bit variants (on Unix, Windows platforms +respectively). More info. on HSDB GUI are in hsdb.html. +

+ +

SA Modes

+

+There are three modes for the SA debugger: +

    +
  • attaching to a local process, +
  • opening a core file, and +
  • attaching to a remote "debug server". +
+

+The remote case requires two programs to be running on the remote machine: +the rmiregistry (see the script "start-rmiregistry.sh" in this directory; +run this in the background) and the debug server (see the script +"start-debug-server-proc.sh"), in that order. start-rmiregistry.sh takes no +arguments; start-debug-server-proc.sh (or -windbg.bat) takes as argument +the process ID or the executable and core file names to allow remote debugging +of. +

+ +

Command line HSDB

+

+There are also command line HSDB variants ("clhsdbproc.sh" or "clhsdbwindbg.bat" +or 64-bit variants). There is also a JavaScript based command line interface +called "jsdbproc.sh" [or "jsdbwindbg.bat" or 64-bit variants]. More details on +command line interfaces can be found in +

+

+ +

Other command line tools

+

+The following table lists all SA command line tools. <xxx>windbg.bat +files are for Windows. .sh files are for Solaris. <xxx>64.sh are for +64 bit debugees. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Tool + +Description +
+dumpflagsproc.sh, +dumpflagsproc64.sh, +dumpflagswindbg.bat +dumpflagswindbg64.bat + +dumps name and value of all -XX JVM command line arguments passed +to debuggee. +
+ +dumpsyspropsproc.sh, +dumpsyspropsproc64.sh, +dumpsyspropswindbg.bat +dumpsyspropswindbg64.bat + +This prints name and value of Java level System properties. +
+ +heapdumpproc.sh, +heapdumpproc64.sh, +heapdumpwindbg.bat +heapdumpwindbg64.bat + +Dumps heap in a file in hprof binary format. +
+ +heapsumproc.sh, +heapsumproc64.sh, +heapsumwindbg.bat +heapsumwindbg64.bat + +Prints summary information on Java heap. +
+jcoreproc.sh, +jcoreproc64.sh, +jcorewindbg.bat +jcorewindbg64.bat + +This can retrieve .class files from the debuggee. +set the environment variable JCORE_PACKAGES to comman separated list of +packages whose classes have to be retrieved from the core file. +
+jstackproc.sh, +jstackproc64.sh, +jstackwindbg.bat +jstackwindbg64.bat + +used to get java stack trace for all java threads. +
+jhistoproc.sh, +jhistoproc64.sh, +jhistowindbg.bat +jhistowindbg64.bat + +used to get object histogram of java heap. +
+permstatproc.sh, +permstatproc64.sh, +permstatwindbg.bat +permstatwindbg64.bat + +To gather statistics on perm. generation. +
+pstackproc.sh, +pstackproc64.sh, +pstackwindbg.bat +pstackwindbg64.bat + +This is cross platform mixed mode pstack utility. This works on any (non-java as well) process, core dump. For java process and core dumps, this prints both java and C/C++ frames. +
+pmapproc.sh, +pmapproc64.sh, +pmapwindbg.bat +pmapwindbg64.bat + +This is cross platform Solaris pmap-like utility. +
+soqlproc.sh, +soqlproc64.sh, +soqlwindbg.bat +soqlwindbg64.bat + +This is command line SOQL - Simple Object Query Language tool. +SOQL is SQL-like query language to query Java heap. +
+start-debug-server-proc.sh, +start-debug-server-proc64.sh, +start-debug-server-windbg.bat, +start-debug-server-windbg64.bat, +start-rmiregistry.bat, +start-rmiregistry64.bat, +start-rmiregistry.sh +start-rmiregistry64.sh + +These scripts are used to run SA remotely. +
+ +

Debugging transported core dumps

+

+When a core dump is moved from the machine where it was produced to a +difference machine, it may not always be possible for SA to debug the same. +More info. on debugging on transported core dumps is in +transported_core.html. +

+ +

SA Bugs

+

+Not all of the possible states of target VMs have been tested (or +supportable) with SA. For example, the SA will probably not work at all +if it freezes the target VM during certain phases of GC. When filing bugs +a pointer to a core file (see gcore(1)) which the SA can not handle well +is best. +

+ + + diff --git a/hotspot/agent/doc/jsdb.html b/hotspot/agent/doc/jsdb.html new file mode 100644 index 00000000000..e691bb9cb3c --- /dev/null +++ b/hotspot/agent/doc/jsdb.html @@ -0,0 +1,1369 @@ + + + +JavaScript interface to Hotspot Serviceability Agent + + + +

JavaScript interface to Hotspot Serviceability Agent

+ +

+Serviceability Agent (SA) provides Java API and tools to diagnose HotSpot Virtual Machine and +Java apps running on it. SA is a snapshot debugger -- can be used to observe state of a frozen java process or java core dump. +

+ +

Existing SA APIs

+

+There are two application programmer interfaces (APIs) for SA: +

+
1. Private java API +
+
+This tries to mimic hotspot VM's internal C++ classes and methods. Because VM data structures +are a moving target, this API can never be 'stable'! Besides, to use SA's private API knowledge of +HotSpot code base is essential. +
+
2. SA-JDI -- Java Debugger Interface read-only subset API +
+
+This is read-only subset of JDI (Java Debugger Interface) +This is a standardized interface to get java level state of a java process or java core dump. While this +interface is useful, this misses parts of java level state from target process or core such as +
    +
  • heap walking interface -- only objects traceable to static variables (of classes) and local +variables of stack frames can be accessed. +
  • re-constructing .class from debuggee are missing. +
  • re-constructing object mirrors for Java objects of the debuggee. +
+
+
+

+ +

SA Scripting interface

+ +

+Traditionally, platform debuggers such as dbx, gdb and Solaris mdb (Module Debugger), provide a scripting +language interface. Scripting language interface provides easy-to-use, dynamically typed +interface to access data structures from debuggee. dbx and mdb even allow user to write +C/C++ modules to extend the scripting language commands. +

+ +

+SA provides SOQL - Simple Object Query Language -- a SQL-like query language to access +Java heap as an object database. SA's main GUI (HSDB) also exposes scripting interface of underlying debugger such as dbx, windbg. +But to use this interface, user has to learn scripting interface of multiple debugger back-ends such as dbx, windbg. +And these scripting interfaces are 'raw' in the sense that no java state is exposed -- only C/C++ state of VM is exposed. +Higher level SA services are not available through scripting interface. +

+ +

+jsdb -- JavaScript Debugger attempts to provide JavaScript interface to SA. +jsdb provides + +

    +
  • high-level hotspot (and SA) independent scripting interface +
  • low-level SA-aware scripting interface. +
+

+ +

High level interface (Java State)

+ +jsdb is a command line JavaScript shell based on +Mozilla's Rhino JavaScript Engine. +This command line utility attaches to Java process or core file or remote debug server and waits for user input. +This shell supports the following global functions and objects in addition to the standard JavaScript functions and +objects: + +

jdsb globals

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function/VariableDescription
+address(jobject) + +function that returns the address of the Java object as a string +
+classof(jobject) + +function that returns the JavaScript object that represents class object of the Java object +
+dumpClass(jclass,[dir]) + +function that writes .class for the given Java Class. Optionally (second arg) accepts the directory where the +.class has to be written. +
+help() + +function that prints help message for global functions and objects +
+identityHash(jobject) + +function that returns the identity hashCode of the Java object +
+mirror(jobject) + +function that returns a local mirror of the Java object. +
+load([file1, file2,...]) + +function that loads zero or more JavaScript file(s). With no arguments, reads for +JavaScript code. +
+object(string) + +function that converts a string address into Java object +
+owner(jobject) + +function that returns the owner thread of this monitor or null +
+sizeof(jobject) + +function that returns the size of Java object in bytes +
+staticof(jclass, field) + +function that returns the value of given field of the given Java class +
+print(expr1, expr2,...) + +function that prints zero or more JavaScript expressions after converting those as strings +
+println(expr1, expr2..) + +function that same as print, but prints a newline at the end +
+read([prompt]) + +function that reads a single line from standard input +
+quit() + +function that quits the interactive load call as well as the shell +
+jvm + +variable -- a JavaScript object that represents the target jvm that is being debugged +
+ +

jvm object

+ +

+jvm object supports the following read-only properties. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Property name + +Description +
+threads + +array of Java threads from the debuggee +
+heap + +object representing the heap of the debuggee +
+type + +string value that is either "Server" or "Client" or "Core" -- the flavour of debuggee VM +
+bootClassPath + +string value of bootclasspath of the debuggee +
+cpu + +string value of cpu on which the debuggee runs/ran +
+sysProps + +name-value pairs (JavaScript associative array) of Java System properties of the debuggee +
+addressSize + +int value -- 32 for 32 bit debuggee, 64 for 64 bit debuggee +
+os + +string value of OS on which the debuggee runs/ran +
+buildInfo + +internal build info string from debuggee +
+flags + +name-value pairs (JavaScript associative array) of JVM command line flags of the debuggee +
+classPath + +string value of classpath of the debuggee +
+userDir + +string value of user.dir System property of the debuggee +
+ +

heap object

+ +

+heap object represents Java heap of the debuggee VM +

+ + + + + + + + + + + + + + + + + + + + + +
+Function or property name + +Description +
+capacity + +byte size of capacity of the heap +
+used + +byte size of used portion (of live objects) of the heap +
+forEachObject(func, [class], [include subtypes -- true|false]) + +This function accepts a callback function 'func' and optionally class name and boolean arguments. +This function calls the callback for each Java object in the debuggee's heap. The optional class +argument may be used to receive objects of given class only. The third arguments specifies whether +to include objects of subtype of given class [or interface] or not. The default value of class is "java.lang.Object" +and and that of the third argument is true. i.e., by default all objects are included. +
+forEachClass(func, [initiating loader -- true|false]) + +This function accepts a callback function 'func'. This function iterates through the classes of the debuggee and calls the +callback for each class. The second parameter tells whether to pass initiating loader to the iterator callback or not. +
+ +

Accessing Java objects and arrays in script

+ +

+From a given Java object, we can access all fields of the Java object by usual '.' operator. i.e., if you got a Java object +called 'o' of type java.lang.Thread from debuggee, you can access 'stackSize' field by o.stackSize syntax. Similarly, length of Java array +objects can be accessed by length property. And array indexing follows usual syntax. i.e., n'th element of array 'a' is +accessed by a[n]. +

+ +

jvm.threads array

+ +

+This is a JavaScript array of Java threads of the debuggee. As usual, 'length' property tells the number of threads and individual +threads may be accessed by index operator -- i.e, jvm.threads[0] returns the first thread. +

+ +

thread object

+ + +

+In addition to the fields of java.lang.Thread (or subclass) fields, thread objects have two additional properties. + +

    +
  • frames -- array of stack frame objects +
  • monitors -- array of monitor objects owned by the thread +
+ +

+ +

stack frame object

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Property name + +Description +
+thisObject + +Object representing 'this' of the current frame [will be null for static methods] +
+locals + +name-value pairs of local variables [JavaScript associative array] +
+line + +Java source line number at which the frame is executing +
+bci + +byte code index of the bytecode that the frame is executing +
+thread + +thread to which this frame belongs +
+method + +Java method that the frame is executing +
+ +

method object

+ +

+method object represents a Java method of debuggee +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Property name + +Description +
+isStatic + +boolean - true for static methods and false for non-static methods +
+isSynchronized + +boolean - true for synchronized methods and false for non-synchronized methods +
+isNative + +boolean - true for native methods and false for non-native methods +
+isProtected + +boolean - true for protected methods and false for non-protected methods +
+isPrivate + +boolean - true for private methods and false for non-private methods +
+isSynthetic + +boolean - true for Javac generated synthetic methods and false for non-synthetic methods +
+isPackagePrivate + +boolean - true for package-private methods and false for non-package-private methods +
+isPublic + +boolean - true for public methods and false for non-public methods +
+holder + +an object that represents Class that contains this method +
+signature + +string -- signature of this method +
+isObsolete + +boolean - true for obsolete (hotswapped) methods and false for non-obsolete methods +
+isStrict + +boolean - true for strictfp methods and false for non-strictfp methods +
+isFinal + +boolean - true for final methods and false for non-final methods +
+name + +string - name of this method +
+ +

class object

+ +

+A class object represents loaded Java class in debuggee VM. This represents java.lang.Class instance in the debuggee. +This is type of return value of classof global function. Also, method.holder property and field.holder are +of this type. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Property name + +Description +
+name + +name of this class +
+superClass + +class object representing super class of this class +
+isArrayClass + +boolean -- is the current class an array class? +
+isStatic + +boolean -- is the current class static or not +
+isInterface + +boolean -- is the current class an interface +
+isAbstract + +boolean -- is the current class abstract or not +
+isProtected + +boolean -- is the current class protected or not +
+isPrivate + +boolean -- is the current class private or not +
+isPackagePrivate + +boolean -- is the current class package private or not +
+isSynthetic + +boolean -- is the current class synthetic or not +
+classLoader + +object that represents ClassLoader object that loaded the current class +
+fields + +array of static and instance fields of the current class +
+protectionDomain + +protection domain to which current class belongs +
+isPublic + +boolean -- is the current class public or not +
+signers + +array of signers for current class +
+sourceFile + +string -- name of the source file for current class +
+interfaces + +array -- interfaces implemented by current class +
+isStrict + +boolean -- is the current class strictfp or not +
+methods + +array of methods (static and instance) of the current class +
+isFinal + +boolean -- is the current class final or not +
+statics + +name-value pairs (JavaScript associate array) of static fields of the current class +
+ +

field object

+

+field represents a static or instance field of some class in debuggee +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Property name + +Description +
+isStatic + +boolean -- is this field a static field? +
+holder + +class that owns this field +
+signature + +string signature of this field +
+isProtected + +boolean - is this field a protected field or not? +
+isPrivate + +boolean - is this field a private field or not? +
+isSynthetic + +boolean - is this javac generated synthetic field or not? +
+isPackagePrivate + +boolean - is this field a package private field or not? +
+isTransient + +boolean - is this field a transient field or not? +
+isFinal + +boolean - is this field final or not? +
+name + +string - name of this field +
+isPublic + +boolean - is this field public or not? +
+ +

Initialization Script

+

+jsdb engine looks for initialization script file named jsdb.js in user's home directory. If found, it loads just after attaching to debuggee but before printing prompt for user's input. User can assume that s/he can access debuggee VM +state during initialization script. +

+ +

Sample scripts

+ +Semantics and knowledge of application classes (for eg. AppServer's classes) would be needed to create app specific +scripts. The following script samples are app-independent and provide a flavour of kind of scripts that can be written. + +

Script to print system properties of JVM

+ +
+
+jvm.sysProps.toString()
+
+
+ +

Script to print JVM command line flags

+
+
+jvm.flags.toString()
+
+
+ + +

Script to print class-wise histogram of objects

+ +
+
+
+// associate array to hold histogram
+var histo;
+function func(obj) {
+    var classname = classof(obj).name;
+    if (histo[classname] == undefined) {
+       // first time we are visiting this class type
+       histo[classname] = 1;
+    } else {
+       histo[classname]++; 
+    }
+}
+
+// iterate through java heap calling 'func' for each object
+jvm.heap.forEachObject(func);
+
+// print the histogram
+for (i in histo) {
+   println('number of instances of ', i, ' = ', histo[i]);
+}
+
+
+
+ +

Script to print stack trace of all Java threads

+ +
+
+
+function printStackTrace(t) {
+    println(t.name);
+    println('');
+    for (i in t.frames) {
+       println(t.frames[i]);
+    }
+    println('');
+}
+
+// walk through the list of threads and call printStackTrace
+// for each thread
+for (o in jvm.threads) {
+    printStackTrace(jvm.threads[o]);
+}
+
+
+
+
+ + +

Script to re-construct .class files for all non-bootstrap classes

+ +
+
+
+function dump(cl) {
+   if (!cl.isArrayClass  && cl.classLoader != null) { 
+      // not an array class and a non-bootstrap class
+      // create .class files in e:\tmp dir
+      dumpClass(cl, "e:\\tmp); 
+   } else {
+      println("skipping bootstrap class ", cl.name);
+   }
+}
+
+// walk thru heap and call callback for each java.lang.Class instance
+jvm.heap.forEachObject(dump, "java.lang.Class");
+
+
+ +

Script to print paths of all java.io.File's currently accessed

+ +
+
+
+function printFile(f) {
+   // construct a mirror java.io.File here and
+   // print absolute path here
+   println(mirror(f).getAbsolutePath());
+}
+
+jvm.heap.forEachObject(printFile, "java.io.File");
+
+
+
+ +

Script to print static fields of java.lang.Thread class

+
+
+
+var threadClass = classof("java.lang.Thread");
+for (i in threadClass.statics) {
+  println(i, '=', threadClass.statics[i]);
+}
+
+
+
+ +

Low level interface (VM State)

+ +

+Low level jsdb interface works by JavaScript-to-Java (previously known as "LiveConnect") +interface provided by Rhino JavaScript engine. +

+ +

sapkg object

+

+This object provides short names for SA package names. For eg. instead of writing +Packages.sun.jvm.hotspot.memory, we can write sapkg.memory. +

+ +

sa object

+

+This object contains all SA singleton objects such as VM, Universe, SymbolTable, +SystemDictionary, ObjectHeap, CollectedHeap, Debugger, CDebugger (if available), +Interpreter, TypeDataBase and Threads. For eg. to access SymbolTable of Java debuggee, +we can use sa.symbolTable. User can execute the following code to get fields of this object. +

+
+
+for (i in sa) {
+  println(i);
+}
+
+
+ +

Heap Iterators

+
+
forEachOop(callback)
+
calls a callback function for each Oop in Java heap
+
forEachOopOfKlass(callback, klass, [includeSubtypes])
+
calls a callback function for each Oop of a give Klass type +Optinally, third argument can specify whether to include subtype Oops +or not. +
+
+ +

System Dictionary Access

+
+
forEachKlass(callback)
+
calls a callback function for each Klass in Java heap
+
forEachKlassAndLoader(callback)
+
+calls callback with Klass and initiating loader (Oop) for System dictionary +entry. +
+
forEachPrimArrayKlass(callback)
+
+calls callback with Klass and initiating loader (Oop) for each +primitive array Klass in the system. +
+
findInstanceKlass(name)
+
+finds the first instance klass with given name from System dictionary +
+
+ +

Thread, Frame Iterators

+
+
forEachJavaThread(callback)
+
calls callback for each Java Thread
+
forEachFrame(javaThread, callback)
+
calls callback for each Frame of a given JavaThread
+
forEachVFrame(javaThread, callback)
+
calls callback for each JavaVFrame of a given JavaThread
+
forEachThread(callback)
+
calls callback for each (native) ThreadProxy (obtained by CDebugger.getThreadList) +
+
forEachCFrame(threadProxy, callback)
+
+calls callback for each CFrame of a given ThreadProxy object +
+
+ +

Code blobs, Interpreter codelets

+
+
forEachCodeBlob(callback)
+
+calls callback with each code blob in code cache +
+
findCodeBlob(address)
+
+finds the code blob, if any, that contains the given address. +Returns null, on failure. +
+
findNMethod(address)
+
+finds the NMethod that contains given address. +
+
pcDescAt(addr)
+
+returns PCDesc at given address or null. +
+
forEachInterpCodelet(callbacl)
+
+calls callback with each Interpreter codelet +
+
+ +

VM structs, constants

+
+
forEachType(callback)
+
+calls callback for each Type in VM's type database +
+
forEachVMIntConst(callback)
+
+calls callback for each named integer constant. passes name +as argument. +
+
forEachVMLongConst(callback)
+
+calls callback for each named long constant. passes name +as argument. +
+
findVMType(name)
+
+finds a VM type by name. returns null if no known Type of given name +exists in type database. +
+
findVMIntConst(name)
+
+finds an integer constant in type data base by name. +
+
findVMLongConst(name)
+
+finds an long constant in type data base by name. +
+
vmTypeof(addr)
+
+returns VM type of object at 'addr' if any. Else, returns null. +
+
isOfVMType(addr, type)
+
+returns whether object at 'addr' is of VM type 'type' or not. +
+
printVMType(type, addr)
+
+prints 'addr' as VM object of type 'type' +
+
printXXX(addr)
+
+For each VM type, these functions are defined. For eg. there is printUniverse, +printSystemDictionary etc. are available. Without 'addr' being passed static fields are printed. With 'addr' param being passed, instance fields are printed. +
+
+ +

Low level debugger facilities

+
+
num2addr(number)
+
+converts a (long) number to SA Address instance +
+
str2addr(string)
+
+converts a given hex string to SA Address instance +
+
any2addr(any)
+
+Takes a number or a string or an Address and returns +an Address instance. For other types, returns 'undefined' +
+
addr2str(addr)
+
+converts a given Address instance to a hex string +
+
addr2num(addr)
+
+converts a given Address instance to a (long) number +
+
sym2addr(library, symbol)
+
+returns Address of a given symbol in a given library (shared object or DLL) +Example: sym2addr('jvm.dll', 'JNI_CreateJavaVM') +
addr2sym(addr)
+
+Returns nearest symbol to a given address (if any). If no such symbol is found, +returns the given address as a string. +
+
readBytesAt(addr, num)
+
+returns 'num' bytes at 'addr' as a Java byte[] +
+
readWordsAt(addr, num)
+
+returns 'num' words at 'addr' as a Java long[] +
+
readCStrAt(addr)
+
+returns 'C' String at given address +
+
readCStrLen(addr)
+
+returns the length of the 'C' String at given address +
+
readRegs(threadProxy)
+
+returns register set (of Thread Context) of a given thread specified +by threadProxy. return value is an associate array having name-value pairs +of registers. +
+
regs(threadProxy)
+
+prints register set of a given thread. +
+
mem(addr, [num])
+
+prints 'num' words (address size) at 'addr'. Prints nearest symbol for address, if found. +
+
dis(addr, [num])
+
prints native code disassembly of 'num' bytes at given address 'addr'. +Default value of 'num' is 4. This automatically detects whether the given address +inside a nmethod. If so, it prints safepoint info, entry points , method signature etc. +of the nmethod. +
+
jdis(method [or addr])
+
+prints Java bytecode disassembly for given method Oop or address of a method Oop. +
+
nmethoddis(nmethod)
+
+prints disassembly of given nmethod object. Note that you don't have to call this directly +instead use 'dis'. +
+
where
+
+prints Java stack trace for all Java threads +
+
+ +

Miscellaneous

+
+
addr2oop(addr)
+
+converts a given address to a Oop object +
+
oop2addr(oop)
+
+returns address of a given Oop object +
+
isOfVMType(addr, type)
+
+returns whether the given 'addr' points to a (C++) VM object of specified +type. type may be specified by SA Type object or string name of the type. +
+
newVMObject(addr)
+
+returns instance of SA object for a given address (similar to SA VirtualConstructor +interface). +
+
vmobj2addr(vmobject)
+
+returns Address represented by a given SA VMObject +
+
addr2vmobj(addr)
+
same as newVMObject(addr)
+
whatis(addr)
+
+returns string description of given address (using SA FindPointer and guess type API). +
isOop(addr)
+
+returns whether a given address is a valid Oop address or not +
+
+ +

Moving b/w jsdb low level and high level interfaces

+ +

+Java objects of debuggee are represented by different script wrappers in high level +interface. In the low-level interface these are instances of SA Oop class or its' +subclass. To move b/w low-level and high-level interfaces the following functions may +be used +

+
+
oop2obj(oop)
+
+converts a given Oop object to a high-level wrapper object +
+
obj2oop(obj)
+
+converts a jsdb high level wrapper to underlying Oop instance +
+
+ +

JavaScript tips

+ +
    +
  • to know properties, functions of any object, use the script +
    +
    +for(i in object) { println(i); }
    +
    +
    +
  • to view the source code of any function, just type the name of +function in jsdb prompt +
  • to view global functions, properties, run +
    +
    +for(i in this) { println(i); }
    +
    +
    +
+ + + diff --git a/hotspot/agent/doc/transported_core.html b/hotspot/agent/doc/transported_core.html new file mode 100644 index 00000000000..e1cdf0f2407 --- /dev/null +++ b/hotspot/agent/doc/transported_core.html @@ -0,0 +1,110 @@ + + + +Debugging transported core dumps + + + +

Debugging transported core dumps

+ +

+When a core dump is moved to a machine different from the one where it was +produced ("transported core dump"), debuggers (dbx, gdb, windbg or SA) do not +always successfully open the dump. This is due to kernel, library (shared +objects or DLLs) mismatch between core dump machine and debugger machine. +

+ +

+In most platforms, core dumps do not contain text (a.k.a) Code pages. +There pages are to be read from executable and shared objects (or DLLs). +Therefore it is important to have matching executable and shared object +files in debugger machine. +

+ +

Solaris transported core dumps

+ +

+Debuggers on Solaris (and Linux) use two addtional shared objects +rtld_db.so and libthread_db.so. rtld_db.so is used to +read information on shared objects from the core dump. libthread_db.so +is used to get information on threads from the core dump. rtld_db.so +evolves along with rtld.so (the runtime linker library) and libthread_db.so +evolves along with libthread.so (user land multithreading library). +Hence, debugger machine should have right version of rtld_db.so and +libthread_db.so to open the core dump successfully. More details on +these debugger libraries can be found in + +Solaris Linkers and Libraries Guide - 817-1984 +

+ +

Solaris SA against transported core dumps

+ +

+With transported core dumps, you may get "rtld_db failures" or +"libthread_db failures" or SA may just throw some other error +(hotspot symbol is missing) when opening the core dump. +Enviroment variable LIBSAPROC_DEBUG may be set to any value +to debug such scenarios. With this env. var set, SA prints many +messages in standard error which can be useful for further debugging. +SA on Solaris uses libproc.so library. This library also +prints debug messages with env. var LIBPROC_DEBUG. But, +setting LIBSAPROC_DEBUG results in setting LIBPROC_DEBUG as well. +

+

+The best possible way to debug a transported core dump is to match the +debugger machine to that of core dump machine. i.e., have same Kernel +and libthread patch level between the machines. mdb (Solaris modular +debugger) may be used to find the Kernel patch level of core dump +machine and debugger machine may be brought to the same level. +

+

+If the matching machine is "far off" in your network, then +

+

+ +

+But, it may not be feasible to find matching machine to debug. +If so, you can copy all application shared objects (and libthread_db.so, if needed) from the core dump +machine into your debugger machine's directory, say, /export/applibs. Now, set SA_ALTROOT +environment variable to point to /export/applibs directory. Note that /export/applibs should either +contain matching 'full path' of libraries. i.e., /usr/lib/libthread_db.so from core +machine should be under /export/applibs/use/lib directory and /use/java/jre/lib/sparc/client/libjvm.so +from core machine should be under /export/applibs/use/java/jre/lib/sparc/client so on or /export/applibs +should just contain libthread_db.so, libjvm.so etc. directly. +

+ +

+Support for transported core dumps is not built into the standard version of libproc.so. You need to +set LD_LIBRARY_PATH env var to point to the path of a specially built version of libproc.so. +Note that this version of libproc.so has a special symbol to support transported core dump debugging. +In future, we may get this feature built into standard libproc.so -- if that happens, this step (of +setting LD_LIBRARY_PATH) can be skipped. +

+ +

Ignoring libthread_db.so failures

+

+If you are okay with missing thread related information, you can set +SA_IGNORE_THREADDB environment variable to any value. With this +set, SA ignores libthread_db failure, but you won't be able to get any +thread related information. But, you would be able to use SA and get +other information. +

+ +

Linux SA against transported core dumps

+

+On Linux, SA parses core and shared library ELF files. SA does not use +libthread_db.so or rtld_db.so for core dump debugging (although +libthread_db.so is used for live process debugging). But, you +may still face problems with transported core dumps, because matching shared +objects may not be in the path(s) specified in core dump file. To +workaround this, you can define environment variable SA_ALTROOT +to be the directory where shared libraries are kept. The semantics of +this env. variable is same as that for Solaris (please refer above). +

+ + + + diff --git a/hotspot/agent/make/ClosureFinder.java b/hotspot/agent/make/ClosureFinder.java new file mode 100644 index 00000000000..171ba5ff901 --- /dev/null +++ b/hotspot/agent/make/ClosureFinder.java @@ -0,0 +1,254 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.io.*; +import java.util.*; + + +/** +

This class finds transitive closure of dependencies from a given +root set of classes. If your project has lots of .class files and you +want to ship only those .class files which are used (transitively) +from a root set of classes, then you can use this utility.

+How does it work?

+ +

We walk through all constant pool entries of a given class and +find all modified UTF-8 entries. Anything that looks like a class name is +considered as a class and we search for that class in the given +classpath. If we find a .class of that name, then we add that class to +list.

+ +

We could have used CONSTANT_ClassInfo type constants only. But +that will miss classes used through Class.forName or xyz.class +construct. But, if you refer to a class name in some other string we +would include it as dependency :(. But this is quite unlikely +anyway. To look for exact Class.forName argument(s) would involve +bytecode analysis. Also, we handle only simple reflection. If you +accept name of a class from externally (for eg properties file or +command line args for example, this utility will not be able to find +that dependency. In such cases, include those classes in the root set. +

+*/ + +public class ClosureFinder { + private Collection roots; // root class names Collection + private Map visitedClasses; // set of all dependencies as a Map + private String classPath; // classpath to look for .class files + private String[] pathComponents; // classpath components + private static final boolean isWindows = File.separatorChar != '/'; + + public ClosureFinder(Collection roots, String classPath) { + this.roots = roots; + this.classPath = classPath; + parseClassPath(); + } + + // parse classPath into pathComponents array + private void parseClassPath() { + List paths = new ArrayList(); + StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator); + while (st.hasMoreTokens()) + paths.add(st.nextToken()); + + Object[] arr = paths.toArray(); + pathComponents = new String[arr.length]; + System.arraycopy(arr, 0, pathComponents, 0, arr.length); + } + + // if output is aleady not computed, compute it now + // result is a map from class file name to base path where the .class was found + public Map find() { + if (visitedClasses == null) { + visitedClasses = new HashMap(); + computeClosure(); + } + return visitedClasses; + } + + // compute closure for all given root classes + private void computeClosure() { + for (Iterator rootsItr = roots.iterator(); rootsItr.hasNext();) { + String name = (String) rootsItr.next(); + name = name.substring(0, name.indexOf(".class")); + computeClosure(name); + } + } + + + // looks up for .class in pathComponents and returns + // base path if found, else returns null + private String lookupClassFile(String classNameAsPath) { + for (int i = 0; i < pathComponents.length; i++) { + File f = new File(pathComponents[i] + File.separator + + classNameAsPath + ".class"); + if (f.exists()) { + if (isWindows) { + String name = f.getName(); + // Windows reports special devices AUX,NUL,CON as files + // under any directory. It does not care about file extention :-( + if (name.compareToIgnoreCase("AUX.class") == 0 || + name.compareToIgnoreCase("NUL.class") == 0 || + name.compareToIgnoreCase("CON.class") == 0) { + return null; + } + } + return pathComponents[i]; + } + } + return null; + } + + + // from JVM spec. 2'nd edition section 4.4 + private static final int CONSTANT_Class = 7; + private static final int CONSTANT_FieldRef = 9; + private static final int CONSTANT_MethodRef = 10; + private static final int CONSTANT_InterfaceMethodRef = 11; + private static final int CONSTANT_String = 8; + private static final int CONSTANT_Integer = 3; + private static final int CONSTANT_Float = 4; + private static final int CONSTANT_Long = 5; + private static final int CONSTANT_Double = 6; + private static final int CONSTANT_NameAndType = 12; + private static final int CONSTANT_Utf8 = 1; + + // whether a given string may be a class name? + private boolean mayBeClassName(String internalClassName) { + int len = internalClassName.length(); + for (int s = 0; s < len; s++) { + char c = internalClassName.charAt(s); + if (!Character.isJavaIdentifierPart(c) && c != '/') + return false; + } + return true; + } + + // compute closure for a given class + private void computeClosure(String className) { + if (visitedClasses.get(className) != null) return; + String basePath = lookupClassFile(className); + if (basePath != null) { + visitedClasses.put(className, basePath); + try { + File classFile = new File(basePath + File.separator + className + ".class"); + FileInputStream fis = new FileInputStream(classFile); + DataInputStream dis = new DataInputStream(fis); + // look for .class signature + if (dis.readInt() != 0xcafebabe) { + System.err.println(classFile.getAbsolutePath() + " is not a valid .class file"); + return; + } + + // ignore major and minor version numbers + dis.readShort(); + dis.readShort(); + + // read number of constant pool constants + int numConsts = (int) dis.readShort(); + String[] strings = new String[numConsts]; + + // zero'th entry is unused + for (int cpIndex = 1; cpIndex < numConsts; cpIndex++) { + int constType = (int) dis.readByte(); + switch (constType) { + case CONSTANT_Class: + case CONSTANT_String: + dis.readShort(); // string name index; + break; + + case CONSTANT_FieldRef: + case CONSTANT_MethodRef: + case CONSTANT_InterfaceMethodRef: + case CONSTANT_NameAndType: + case CONSTANT_Integer: + case CONSTANT_Float: + // all these are 4 byte constants + dis.readInt(); + break; + + case CONSTANT_Long: + case CONSTANT_Double: + // 8 byte constants + dis.readLong(); + // occupies 2 cp entries + cpIndex++; + break; + + + case CONSTANT_Utf8: { + strings[cpIndex] = dis.readUTF(); + break; + } + + default: + System.err.println("invalid constant pool entry"); + return; + } + } + + // now walk thru the string constants and look for class names + for (int s = 0; s < numConsts; s++) { + if (strings[s] != null && mayBeClassName(strings[s])) + computeClosure(strings[s].replace('/', File.separatorChar)); + } + + } catch (IOException exp) { + // ignore for now + } + + } + } + + // a sample main that accepts roots classes in a file and classpath as args + public static void main(String[] args) { + if (args.length != 2) { + System.err.println("Usage: ClosureFinder "); + System.exit(1); + } + + List roots = new ArrayList(); + try { + FileInputStream fis = new FileInputStream(args[0]); + DataInputStream dis = new DataInputStream(fis); + String line = null; + while ((line = dis.readLine()) != null) { + if (isWindows) { + line = line.replace('/', File.separatorChar); + } + roots.add(line); + } + } catch (IOException exp) { + System.err.println(exp.getMessage()); + System.exit(2); + } + + ClosureFinder cf = new ClosureFinder(roots, args[1]); + Map out = cf.find(); + Iterator res = out.keySet().iterator(); + for(; res.hasNext(); ) { + String className = (String) res.next(); + System.out.println(className + ".class"); + } + } +} diff --git a/hotspot/agent/make/Makefile b/hotspot/agent/make/Makefile new file mode 100644 index 00000000000..0cde8d5754b --- /dev/null +++ b/hotspot/agent/make/Makefile @@ -0,0 +1,312 @@ +# +# Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This guards against adding broken .java files to the directory +# hierarchy, but may be a pain to keep in sync + +# Generated using the build-pkglist script +ifeq "x$(GAMMADIR)" "x" +include ../../make/defs.make +else +include $(GAMMADIR)/make/defs.make +endif + +PKGLIST = \ +sun.jvm.hotspot \ +sun.jvm.hotspot.asm \ +sun.jvm.hotspot.asm.amd64 \ +sun.jvm.hotspot.asm.ia64 \ +sun.jvm.hotspot.asm.sparc \ +sun.jvm.hotspot.asm.x86 \ +sun.jvm.hotspot.bugspot \ +sun.jvm.hotspot.bugspot.tree \ +sun.jvm.hotspot.c1 \ +sun.jvm.hotspot.code \ +sun.jvm.hotspot.compiler \ +sun.jvm.hotspot.debugger \ +sun.jvm.hotspot.debugger.amd64 \ +sun.jvm.hotspot.debugger.cdbg \ +sun.jvm.hotspot.debugger.cdbg.basic \ +sun.jvm.hotspot.debugger.cdbg.basic.amd64 \ +sun.jvm.hotspot.debugger.cdbg.basic.x86 \ +sun.jvm.hotspot.debugger.dbx \ +sun.jvm.hotspot.debugger.dbx.sparc \ +sun.jvm.hotspot.debugger.dbx.x86 \ +sun.jvm.hotspot.debugger.dummy \ +sun.jvm.hotspot.debugger.ia64 \ +sun.jvm.hotspot.debugger.linux \ +sun.jvm.hotspot.debugger.linux.amd64 \ +sun.jvm.hotspot.debugger.linux.ia64 \ +sun.jvm.hotspot.debugger.linux.x86 \ +sun.jvm.hotspot.debugger.posix \ +sun.jvm.hotspot.debugger.posix.elf \ +sun.jvm.hotspot.debugger.proc \ +sun.jvm.hotspot.debugger.proc.amd64 \ +sun.jvm.hotspot.debugger.proc.sparc \ +sun.jvm.hotspot.debugger.proc.x86 \ +sun.jvm.hotspot.debugger.remote \ +sun.jvm.hotspot.debugger.remote.amd64 \ +sun.jvm.hotspot.debugger.remote.sparc \ +sun.jvm.hotspot.debugger.remote.x86 \ +sun.jvm.hotspot.debugger.sparc \ +sun.jvm.hotspot.debugger.win32 \ +sun.jvm.hotspot.debugger.win32.coff \ +sun.jvm.hotspot.debugger.windbg \ +sun.jvm.hotspot.debugger.windbg.amd64 \ +sun.jvm.hotspot.debugger.windbg.ia64 \ +sun.jvm.hotspot.debugger.windbg.x86 \ +sun.jvm.hotspot.debugger.x86 \ +sun.jvm.hotspot.gc_implementation \ +sun.jvm.hotspot.gc_implementation.parallelScavenge \ +sun.jvm.hotspot.gc_implementation.shared \ +sun.jvm.hotspot.gc_interface \ +sun.jvm.hotspot.interpreter \ +sun.jvm.hotspot.jdi \ +sun.jvm.hotspot.livejvm \ +sun.jvm.hotspot.memory \ +sun.jvm.hotspot.oops \ +sun.jvm.hotspot.runtime \ +sun.jvm.hotspot.runtime.amd64 \ +sun.jvm.hotspot.runtime.ia64 \ +sun.jvm.hotspot.runtime.linux \ +sun.jvm.hotspot.runtime.linux_amd64 \ +sun.jvm.hotspot.runtime.linux_ia64 \ +sun.jvm.hotspot.runtime.linux_sparc \ +sun.jvm.hotspot.runtime.linux_x86 \ +sun.jvm.hotspot.runtime.posix \ +sun.jvm.hotspot.runtime.solaris_amd64 \ +sun.jvm.hotspot.runtime.solaris_sparc \ +sun.jvm.hotspot.runtime.solaris_x86 \ +sun.jvm.hotspot.runtime.sparc \ +sun.jvm.hotspot.runtime.win32_amd64 \ +sun.jvm.hotspot.runtime.win32_ia64 \ +sun.jvm.hotspot.runtime.win32_x86 \ +sun.jvm.hotspot.runtime.x86 \ +sun.jvm.hotspot.tools \ +sun.jvm.hotspot.tools.jcore \ +sun.jvm.hotspot.tools.soql \ +sun.jvm.hotspot.types \ +sun.jvm.hotspot.types.basic \ +sun.jvm.hotspot.ui \ +sun.jvm.hotspot.ui.action \ +sun.jvm.hotspot.ui.classbrowser \ +sun.jvm.hotspot.ui.resources \ +sun.jvm.hotspot.ui.table \ +sun.jvm.hotspot.ui.tree \ +sun.jvm.hotspot.ui.treetable \ +sun.jvm.hotspot.utilities \ +sun.jvm.hotspot.utilities.memo \ +sun.jvm.hotspot.utilities.soql +#END PKGLIST + +# Generated using the build-filelist script +FILELIST = \ +sun/jvm/hotspot/*.java \ +sun/jvm/hotspot/asm/*.java \ +sun/jvm/hotspot/asm/amd64/*.java \ +sun/jvm/hotspot/asm/ia64/*.java \ +sun/jvm/hotspot/asm/sparc/*.java \ +sun/jvm/hotspot/asm/x86/*.java \ +sun/jvm/hotspot/bugspot/*.java \ +sun/jvm/hotspot/bugspot/tree/*.java \ +sun/jvm/hotspot/c1/*.java \ +sun/jvm/hotspot/code/*.java \ +sun/jvm/hotspot/compiler/*.java \ +sun/jvm/hotspot/debugger/*.java \ +sun/jvm/hotspot/debugger/amd64/*.java \ +sun/jvm/hotspot/debugger/cdbg/*.java \ +sun/jvm/hotspot/debugger/cdbg/basic/*.java \ +sun/jvm/hotspot/debugger/cdbg/basic/amd64/*.java \ +sun/jvm/hotspot/debugger/cdbg/basic/x86/*.java \ +sun/jvm/hotspot/debugger/dbx/*.java \ +sun/jvm/hotspot/debugger/dbx/sparc/*.java \ +sun/jvm/hotspot/debugger/dbx/x86/*.java \ +sun/jvm/hotspot/debugger/dummy/*.java \ +sun/jvm/hotspot/debugger/ia64/*.java \ +sun/jvm/hotspot/debugger/linux/*.java \ +sun/jvm/hotspot/debugger/linux/x86/*.java \ +sun/jvm/hotspot/debugger/posix/*.java \ +sun/jvm/hotspot/debugger/posix/elf/*.java \ +sun/jvm/hotspot/debugger/proc/*.java \ +sun/jvm/hotspot/debugger/proc/amd64/*.java \ +sun/jvm/hotspot/debugger/proc/sparc/*.java \ +sun/jvm/hotspot/debugger/proc/x86/*.java \ +sun/jvm/hotspot/debugger/remote/*.java \ +sun/jvm/hotspot/debugger/remote/amd64/*.java \ +sun/jvm/hotspot/debugger/remote/sparc/*.java \ +sun/jvm/hotspot/debugger/remote/x86/*.java \ +sun/jvm/hotspot/debugger/sparc/*.java \ +sun/jvm/hotspot/debugger/win32/*.java \ +sun/jvm/hotspot/debugger/win32/coff/*.java \ +sun/jvm/hotspot/debugger/windbg/*.java \ +sun/jvm/hotspot/debugger/windbg/ia64/*.java \ +sun/jvm/hotspot/debugger/windbg/x86/*.java \ +sun/jvm/hotspot/debugger/x86/*.java \ +sun/jvm/hotspot/interpreter/*.java \ +sun/jvm/hotspot/jdi/*.java \ +sun/jvm/hotspot/livejvm/*.java \ +sun/jvm/hotspot/memory/*.java \ +sun/jvm/hotspot/oops/*.java \ +sun/jvm/hotspot/runtime/*.java \ +sun/jvm/hotspot/runtime/amd64/*.java \ +sun/jvm/hotspot/runtime/ia64/*.java \ +sun/jvm/hotspot/runtime/linux/*.java \ +sun/jvm/hotspot/runtime/linux_amd64/*.java \ +sun/jvm/hotspot/runtime/linux_ia64/*.java \ +sun/jvm/hotspot/runtime/linux_sparc/*.java \ +sun/jvm/hotspot/runtime/linux_x86/*.java \ +sun/jvm/hotspot/runtime/posix/*.java \ +sun/jvm/hotspot/runtime/solaris_amd64/*.java \ +sun/jvm/hotspot/runtime/solaris_sparc/*.java \ +sun/jvm/hotspot/runtime/solaris_x86/*.java \ +sun/jvm/hotspot/runtime/sparc/*.java \ +sun/jvm/hotspot/runtime/win32_amd64/*.java \ +sun/jvm/hotspot/runtime/win32_ia64/*.java \ +sun/jvm/hotspot/runtime/win32_x86/*.java \ +sun/jvm/hotspot/runtime/x86/*.java \ +sun/jvm/hotspot/tools/*.java \ +sun/jvm/hotspot/tools/jcore/*.java \ +sun/jvm/hotspot/tools/soql/*.java \ +sun/jvm/hotspot/types/*.java \ +sun/jvm/hotspot/types/basic/*.java \ +sun/jvm/hotspot/ui/*.java \ +sun/jvm/hotspot/ui/action/*.java \ +sun/jvm/hotspot/ui/classbrowser/*.java \ +sun/jvm/hotspot/ui/table/*.java \ +sun/jvm/hotspot/ui/tree/*.java \ +sun/jvm/hotspot/ui/treetable/*.java \ +sun/jvm/hotspot/utilities/*.java \ +sun/jvm/hotspot/utilities/memo/*.java \ +sun/jvm/hotspot/utilities/soql/*.java +#END FILELIST + +ifneq "x$(ALT_BOOTDIR)" "x" + BOOTDIR := $(ALT_BOOTDIR) +endif + +ifeq "x$(BOOTDIR)" "x" + JDK_HOME := $(shell dirname $(shell which java))/.. +else + JDK_HOME := $(BOOTDIR) +endif + +isUnix := $(shell test -r c:/; echo $$?) + +ifeq "$(isUnix)" "1" + CPS := : +else + CPS := ";" +endif + +SRC_DIR = ../src/share/classes +LIB_DIR = ../src/share/lib +CLOSED_LIB_DIR = ../closed/src/share/lib +BUILD_DIR = ../build +OUTPUT_DIR = $(BUILD_DIR)/classes +DOC_DIR = $(BUILD_DIR)/doc + +# gnumake 3.78.1 does not accept the *s, +# so use the shell to expand them +ALLFILES := $(patsubst %,$(SRC_DIR)/%,$(FILELIST)) +ALLFILES := $(shell /bin/ls $(ALLFILES)) + + +# tools.jar is needed by the JDI - SA binding +CLASSPATH = $(LIB_DIR)/maf-1_0.jar$(CPS)$(JDK_HOME)/lib/tools.jar +CLASSPATH := $(subst \,/,$(CLASSPATH)) + +# FIXME: autogenerate call to rmic + +SA_BUILD_VERSION_PROP = "sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VERSION)" + +SA_PROPERTIES = $(OUTPUT_DIR)/sa.properties + +# Tagging it on because there's no reason not to run it +all: filelist + @mkdir -p $(OUTPUT_DIR) + @echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) + @javac -source 1.4 -classpath $(CLASSPATH) -deprecation -sourcepath $(SRC_DIR) -g -d $(OUTPUT_DIR) @filelist + @rmic -classpath $(OUTPUT_DIR) -d $(OUTPUT_DIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer + rm -f $(OUTPUT_DIR)/sun/jvm/hotspot/utilities/soql/sa.js + cp $(SRC_DIR)/sun/jvm/hotspot/utilities/soql/sa.js $(OUTPUT_DIR)/sun/jvm/hotspot/utilities/soql + +allprof: filelist + @mkdir -p $(OUTPUT_DIR) + @echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) + @javac -source 1.4 -J-Xprof -classpath $(CLASSPATH) -deprecation -sourcepath $(SRC_DIR) -g -d $(OUTPUT_DIR) @filelist + @rmic -classpath $(OUTPUT_DIR) -d $(OUTPUT_DIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer + rm -f $(OUTPUT_DIR)/sun/jvm/hotspot/utilities/soql/sa.js + cp $(SRC_DIR)/sun/jvm/hotspot/utilities/soql/sa.js $(OUTPUT_DIR)/sun/jvm/hotspot/utilities/soql + +filelist: $(ALLFILES) + @if [ ! -f $(JDK_HOME)/lib/tools.jar ] ; then \ + echo "Missing $(JDK_HOME)/lib/tools.jar file. Use 1.6.0 or later version jdk to build SA."; \ + echo ""; \ + exit 1; \ + fi + @rm -f $@ + @echo $(ALLFILES) > $@ + +.PHONY: natives +natives: + cd ../src/os/`java -classpath $(OUTPUT_DIR) sun.jvm.hotspot.utilities.PlatformInfo`; $(MAKE) all + +.PHONY: sa-jdi.jar +sa-jdi.jar: + if [ ! -f $(JDK_HOME)/lib/tools.jar ] ; then \ + echo "Missing $(JDK_HOME)/lib/tools.jar file. Use 1.6.0 or later version jdk to build SA.";\ + exit 1; \ + fi + rm -f $(BUILD_DIR)/sa-jdi.jar + rm -f $(OUTPUT_DIR)/jdi_class_files + javac -source 1.4 ClosureFinder.java -d $(OUTPUT_DIR) + cd $(OUTPUT_DIR) ; find sun/jvm/hotspot/jdi -name "*.class" > jdi_class_files + cd $(OUTPUT_DIR) ; jar cvf ../sa-jdi.jar `java ClosureFinder jdi_class_files .` + cd $(BUILD_DIR) ; jar uvf sa-jdi.jar -C $(SRC_DIR) META-INF/services/com.sun.jdi.connect.Connector + cd $(BUILD_DIR) ; jar uvf sa-jdi.jar -C $(OUTPUT_DIR) sa.properties + rm -f $(OUTPUT_DIR)/ClosureFinder.class + rm -f $(OUTPUT_DIR)/jdi_class_files + +docs: + @javadoc -private -classpath $(CLASSPATH) -sourcepath $(SRC_DIR) -d $(DOC_DIR) $(PKGLIST) + +sizes: $(ALLFILES) + wc -l $(ALLFILES) + +cscope: $(ALLFILES) + echo $(ALLFILES) > java.files + cscope -b -i java.files -f java.out + +.PHONY: sa.jar +sa.jar: + rm -f $(BUILD_DIR)/sa.jar + mkdir -p $(OUTPUT_DIR)/sun/jvm/hotspot/ui/resources + rm -f $(OUTPUT_DIR)/sun/jvm/hotspot/ui/resources/* + cp $(SRC_DIR)/sun/jvm/hotspot/ui/resources/*.png $(OUTPUT_DIR)/sun/jvm/hotspot/ui/resources/ + cd $(OUTPUT_DIR) ; jar cvf ../sa.jar * + +clean:: + rm -rf filelist + cd ../src/os/`java -classpath $(OUTPUT_DIR) sun.jvm.hotspot.utilities.PlatformInfo`; $(MAKE) clean + rm -rf $(BUILD_DIR)/* diff --git a/hotspot/agent/make/README.txt b/hotspot/agent/make/README.txt new file mode 100644 index 00000000000..1ceb26b9339 --- /dev/null +++ b/hotspot/agent/make/README.txt @@ -0,0 +1,5 @@ +These are the Java-level sources for the Serviceability Agent (SA). + +To build, type "gnumake all". + +For usage documentation, please refer to ../doc/index.html. diff --git a/hotspot/agent/make/bugspot.bat b/hotspot/agent/make/bugspot.bat new file mode 100644 index 00000000000..da0880c4da8 --- /dev/null +++ b/hotspot/agent/make/bugspot.bat @@ -0,0 +1,25 @@ +REM +REM Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +java -showversion -cp ..\build\classes;..\src\share\lib\maf-1_0.jar;..\src\share\lib\jlfgr-1_0.jar;..\src\share\lib\js.jar;sa.jar;lib\maf-1_0.jar;lib\jlfgr-1_0.jar;lib\js.jar sun.jvm.hotspot.bugspot.Main diff --git a/hotspot/agent/make/build-filelist b/hotspot/agent/make/build-filelist new file mode 100644 index 00000000000..34bd51422c8 --- /dev/null +++ b/hotspot/agent/make/build-filelist @@ -0,0 +1,10 @@ +#!/bin/sh -f + +SH=`which sh` +MKS_HOME=`dirname $SH` + +CD=cd +FIND=$MKS_HOME/find +SORT=$MKS_HOME/sort + +$CD ../src/share/classes; $FIND sun \( -name SCCS -prune \) -o \( -name "*.java" \) -print | $SORT > ../../../make/filelist.txt diff --git a/hotspot/agent/make/build-pkglist b/hotspot/agent/make/build-pkglist new file mode 100644 index 00000000000..64d9a96cabb --- /dev/null +++ b/hotspot/agent/make/build-pkglist @@ -0,0 +1,11 @@ +#!/bin/sh -f + +SH=`which sh` +MKS_HOME=`dirname $SH` + +CD=cd +FIND=$MKS_HOME/find +SED=$MKS_HOME/sed +SORT=$MKS_HOME/sort + +$CD ../src/share/classes; $FIND sun/jvm/hotspot \( -name SCCS -prune \) -o -type d -print | $SED -e 's/\//./g' | $SORT > ../../../make/pkglist.txt diff --git a/hotspot/agent/make/build.xml b/hotspot/agent/make/build.xml new file mode 100644 index 00000000000..ebd5d39b785 --- /dev/null +++ b/hotspot/agent/make/build.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hotspot/agent/make/clhsdbproc.sh b/hotspot/agent/make/clhsdbproc.sh new file mode 100644 index 00000000000..033b7888ec9 --- /dev/null +++ b/hotspot/agent/make/clhsdbproc.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.CLHSDB $* diff --git a/hotspot/agent/make/clhsdbproc64.sh b/hotspot/agent/make/clhsdbproc64.sh new file mode 100644 index 00000000000..e452ab6acc8 --- /dev/null +++ b/hotspot/agent/make/clhsdbproc64.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.CLHSDB $* diff --git a/hotspot/agent/make/clhsdbwindbg.bat b/hotspot/agent/make/clhsdbwindbg.bat new file mode 100644 index 00000000000..0b9be545b27 --- /dev/null +++ b/hotspot/agent/make/clhsdbwindbg.bat @@ -0,0 +1,29 @@ +@echo off + +REM +REM Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.CLHSDB %1 %2 diff --git a/hotspot/agent/make/clhsdbwindbg64.bat b/hotspot/agent/make/clhsdbwindbg64.bat new file mode 100644 index 00000000000..07885f70bcc --- /dev/null +++ b/hotspot/agent/make/clhsdbwindbg64.bat @@ -0,0 +1,29 @@ +@echo off + +REM +REM Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.CLHSDB %1 %2 diff --git a/hotspot/agent/make/dumpflagsproc.sh b/hotspot/agent/make/dumpflagsproc.sh new file mode 100644 index 00000000000..f79e9038ffc --- /dev/null +++ b/hotspot/agent/make/dumpflagsproc.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.FlagDumper $* diff --git a/hotspot/agent/make/dumpflagsproc64.sh b/hotspot/agent/make/dumpflagsproc64.sh new file mode 100644 index 00000000000..80f56164b26 --- /dev/null +++ b/hotspot/agent/make/dumpflagsproc64.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.FlagDumper $* diff --git a/hotspot/agent/make/dumpflagswindbg.bat b/hotspot/agent/make/dumpflagswindbg.bat new file mode 100644 index 00000000000..1fc80034e54 --- /dev/null +++ b/hotspot/agent/make/dumpflagswindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.FlagDumper %1 %2 diff --git a/hotspot/agent/make/dumpflagswindbg64.bat b/hotspot/agent/make/dumpflagswindbg64.bat new file mode 100644 index 00000000000..c5d0907006f --- /dev/null +++ b/hotspot/agent/make/dumpflagswindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.FlagDumper %1 %2 diff --git a/hotspot/agent/make/dumpsyspropsproc.sh b/hotspot/agent/make/dumpsyspropsproc.sh new file mode 100644 index 00000000000..af2c1e31913 --- /dev/null +++ b/hotspot/agent/make/dumpsyspropsproc.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.SysPropsDumper $* + diff --git a/hotspot/agent/make/dumpsyspropsproc64.sh b/hotspot/agent/make/dumpsyspropsproc64.sh new file mode 100644 index 00000000000..fa9dd036f94 --- /dev/null +++ b/hotspot/agent/make/dumpsyspropsproc64.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.SysPropsDumper $* diff --git a/hotspot/agent/make/dumpsyspropswindbg.bat b/hotspot/agent/make/dumpsyspropswindbg.bat new file mode 100644 index 00000000000..43622cfd798 --- /dev/null +++ b/hotspot/agent/make/dumpsyspropswindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.SysPropsDumper %1 %2 diff --git a/hotspot/agent/make/dumpsyspropswindbg64.bat b/hotspot/agent/make/dumpsyspropswindbg64.bat new file mode 100644 index 00000000000..535d2c6f05b --- /dev/null +++ b/hotspot/agent/make/dumpsyspropswindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.SysPropsDumper %1 %2 diff --git a/hotspot/agent/make/finalizerinfoproc.sh b/hotspot/agent/make/finalizerinfoproc.sh new file mode 100644 index 00000000000..1296a209735 --- /dev/null +++ b/hotspot/agent/make/finalizerinfoproc.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.FinalizerInfo $* diff --git a/hotspot/agent/make/finalizerinfoproc64.sh b/hotspot/agent/make/finalizerinfoproc64.sh new file mode 100644 index 00000000000..afd850d85d2 --- /dev/null +++ b/hotspot/agent/make/finalizerinfoproc64.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.FinalizerInfo $* diff --git a/hotspot/agent/make/finalizerinfowindbg.bat b/hotspot/agent/make/finalizerinfowindbg.bat new file mode 100644 index 00000000000..0fa549fd546 --- /dev/null +++ b/hotspot/agent/make/finalizerinfowindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.FinalizerInfo %1 %2 diff --git a/hotspot/agent/make/finalizerinfowindbg64.bat b/hotspot/agent/make/finalizerinfowindbg64.bat new file mode 100644 index 00000000000..e8c52a17f50 --- /dev/null +++ b/hotspot/agent/make/finalizerinfowindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.FinalizerInfo %1 %2 diff --git a/hotspot/agent/make/grantAll.policy b/hotspot/agent/make/grantAll.policy new file mode 100644 index 00000000000..8ab626ec18a --- /dev/null +++ b/hotspot/agent/make/grantAll.policy @@ -0,0 +1,30 @@ +// +// Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// Do NOT use this policy file in a production system! + +grant { + // Allow everything for now + permission java.security.AllPermission; +}; diff --git a/hotspot/agent/make/heapdumpproc.sh b/hotspot/agent/make/heapdumpproc.sh new file mode 100644 index 00000000000..c58a206da60 --- /dev/null +++ b/hotspot/agent/make/heapdumpproc.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.HeapDumper $* diff --git a/hotspot/agent/make/heapdumpproc64.sh b/hotspot/agent/make/heapdumpproc64.sh new file mode 100644 index 00000000000..f1d2135f6c9 --- /dev/null +++ b/hotspot/agent/make/heapdumpproc64.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# +# Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.HeapDumper $* + diff --git a/hotspot/agent/make/heapdumpwindbg.bat b/hotspot/agent/make/heapdumpwindbg.bat new file mode 100644 index 00000000000..9711559ea49 --- /dev/null +++ b/hotspot/agent/make/heapdumpwindbg.bat @@ -0,0 +1,29 @@ +@echo off + +REM +REM Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.HeapDumper %1 %2 %3 %4 diff --git a/hotspot/agent/make/heapdumpwindbg64.bat b/hotspot/agent/make/heapdumpwindbg64.bat new file mode 100644 index 00000000000..7375d5fb051 --- /dev/null +++ b/hotspot/agent/make/heapdumpwindbg64.bat @@ -0,0 +1,29 @@ +@echo off + +REM +REM Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.HeapDumper %1 %2 %3 %4 diff --git a/hotspot/agent/make/heapsumproc.sh b/hotspot/agent/make/heapsumproc.sh new file mode 100644 index 00000000000..5fd95823069 --- /dev/null +++ b/hotspot/agent/make/heapsumproc.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.HeapSummary $* diff --git a/hotspot/agent/make/heapsumproc64.sh b/hotspot/agent/make/heapsumproc64.sh new file mode 100644 index 00000000000..7c0d0293a84 --- /dev/null +++ b/hotspot/agent/make/heapsumproc64.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.HeapSummary $* diff --git a/hotspot/agent/make/heapsumwindbg.bat b/hotspot/agent/make/heapsumwindbg.bat new file mode 100644 index 00000000000..4221de1064f --- /dev/null +++ b/hotspot/agent/make/heapsumwindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.HeapSummary %1 %2 diff --git a/hotspot/agent/make/heapsumwindbg64.bat b/hotspot/agent/make/heapsumwindbg64.bat new file mode 100644 index 00000000000..245e8edcb28 --- /dev/null +++ b/hotspot/agent/make/heapsumwindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.HeapSummary %1 %2 diff --git a/hotspot/agent/make/hsdb.bat b/hotspot/agent/make/hsdb.bat new file mode 100644 index 00000000000..6b12916d998 --- /dev/null +++ b/hotspot/agent/make/hsdb.bat @@ -0,0 +1,25 @@ +REM +REM Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +java -showversion -cp ..\build\classes;..\src\share\lib\maf-1_0.jar;..\src\share\lib\jlfgr-1_0.jar;..\src\share\lib\js.jar;sa.jar;lib\maf-1_0.jar;lib\jlfgr-1_0.jar;lib\js.jar sun.jvm.hotspot.HSDB %1 %2 diff --git a/hotspot/agent/make/hsdb.sh b/hotspot/agent/make/hsdb.sh new file mode 100644 index 00000000000..3e5cc2df040 --- /dev/null +++ b/hotspot/agent/make/hsdb.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +STARTDIR=`dirname $0` + +if [ "x$SA_JAVA" = "x" ]; then + SA_JAVA=java +fi + +$SA_JAVA -showversion -cp $STARTDIR/../build/classes:$STARTDIR/../src/share/lib/maf-1_0.jar:$STARTDIR/../src/share/lib/jlfgr-1_0.jar:$STARTDIR/../src/share/lib/js.jar:$STARTDIR/sa.jar:$STARTDIR/lib/maf-1_0.jar:$STARTDIR/lib/jlfgr-1_0.jar:$STARTDIR/lib/js.jar sun.jvm.hotspot.HSDB $* diff --git a/hotspot/agent/make/hsdbproc.sh b/hotspot/agent/make/hsdbproc.sh new file mode 100644 index 00000000000..d84dfa56202 --- /dev/null +++ b/hotspot/agent/make/hsdbproc.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.HSDB $* diff --git a/hotspot/agent/make/hsdbproc64.sh b/hotspot/agent/make/hsdbproc64.sh new file mode 100644 index 00000000000..4710d38ad47 --- /dev/null +++ b/hotspot/agent/make/hsdbproc64.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.HSDB $* diff --git a/hotspot/agent/make/hsdbwindbg.bat b/hotspot/agent/make/hsdbwindbg.bat new file mode 100644 index 00000000000..9cc82ec6cf5 --- /dev/null +++ b/hotspot/agent/make/hsdbwindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.HSDB %1 %2 diff --git a/hotspot/agent/make/hsdbwindbg64.bat b/hotspot/agent/make/hsdbwindbg64.bat new file mode 100644 index 00000000000..3f1bb170188 --- /dev/null +++ b/hotspot/agent/make/hsdbwindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.HSDB %1 %2 diff --git a/hotspot/agent/make/index.html b/hotspot/agent/make/index.html new file mode 100644 index 00000000000..436daaa2b8c --- /dev/null +++ b/hotspot/agent/make/index.html @@ -0,0 +1,262 @@ + + + + + +Using The HotSpot Serviceability Agent + + + + +

+Using The HotSpot Serviceability Agent +

+ +

+

+Contents +

+

+ + + +

+ +Introduction + +

+ +

+The HotSpot Serviceability Agent (SA) is a set of Java APIs which +mirror the internal APIs of the HotSpot VM and which can be used to +examine the state of a HotSpot VM. +

+ +

+The system understands the layout of certain VM data structures and is +able to traverse these structures in an examination-only fashion; that +is, it does not rely on being able to run code in the target VM. For +this reason it transparently works with either a running VM or a core +file. +

+ +

+The system can reconstruct information about Java frames on the stack +and objects in the heap. Many of the important data structures in the +VM like the CodeCache, Universe, StubQueue, Frames, and VFrames have +been exposed and have relatively complete (or at least useful) +implementations. +

+ +

+A small graphical debugger called HSDB (the "HotSpot Debugger") has +been written using these APIs. It provides stack memory dumps +annotated with method invocations, compiled-code inlining (if +present), interpreted vs. compiled code, interpreter codelets (if +interpreted), and live oops from oop-map information. It also provides +a tree-based oop inspector. More information will be added as +necessary; please send +email with suggestions on what would be useful. +

+ +

+The SA currently only works on Solaris. It uses dbx to connect to the +remote process or core file and communicates with a small piece of +code (an "import module") loaded into the debugger. +

+ +

+ +Organization of the sources + +

+ +

+The Java-side source code, which is the bulk of the SA, is in +src/share/vm/agent. The organization of the sun.jvm.hotspot package +hierarchy mirrors the organization in the VM. This should allow +engineers familiar with the HotSpot sources to quickly understand how +the SA works and to make modifications if necessary. To build these +sources, cd to src/share/vm/agent and type "make". +

+ +

+ +The SA on Solaris works by communicating with a small piece of code +(an "import module") loaded into dbx. The source code for this import +module is in src/os/solaris/agent. To build this library, cd to +src/os/solaris/agent and type "make 32bit" or "make 64bit". The +distinction is necessary because the SPARC version of dbx ships with +two versions of its executable, and depending on which architecture +(v8 or v9) the debugger is running on selects the appropriate +executable. The SA tries the v8 version first, but if you are running +on a v9 machine you must provide both versions to the SA. +

+ +

+ +The system is currently hardwired to look on jano for its dbx +executable and import module. The relevant directory structure looks +like this: + +

    +
  • .../hotspot/sa/ +
      +
    • solaris/ +
        +
      • sparc/ +
          +
        • bin/ +
            +
          • dbx: symlink to (v8) dbx 7.0 executable +
          +
        +
          +
        • lib/ +
            +
          • libsvc_agent_dbx.so: 32-bit version of import module +
          +
        +
      • sparcv9/ +
          +
        • lib/ +
            +
          • libsvc_agent_dbx.so: 32-bit version of import module +
          +
        +
      +
    +
+

+ +

+The code which builds up path names to these executables is contained +in sun.jvm.hotspot.HotSpotAgent.java. There are hardcoded paths in +this file to jano, but the rest of the system is isolated from this. +

+ +

+(It would be nice to have a panel in the debugger allowing +configuration of all of the known operating systems' options; right +now Solaris is the only supported OS, making that easier.) +

+ +

+ +Running HSDB + +

+ +

+An installation of HSDB has been placed on jano. To access it, add the +following directory to your PATH: +

+ +

+

+    /net/jano/export/disk05/hotspot/sa/bin/common
+
+

+ +

+To start the debugger, type "hsdb". +

+ +

+Alternatively, you can start a local build of the debugger by building +the sources in src/share/vm/agent, cd'ing to that directory, and +typing "java sun.jvm.hotspot.HSDB". +

+ +

+There are three modes for the debugger: attaching to a local process, +opening a core file, and attaching to a remote "debug server". The +remote case requires two programs to be running on the remote machine: +the rmiregistry (see the script "start-rmiregistry" in this directory; +run this in the background) and the debug server (see the script +"start-debug-server"), in that order. start-rmiregistry takes no +arguments; start-debug-server takes as argument the process ID or the +executable and core file names to allow remote debugging of. Make sure +you do NOT have a CLASSPATH environment variable set when you run +these scripts. (The classes put into the rmiregistry are in sun.*, and +there are permissions problems if they aren't placed on the boot +classpath.) +

+ +

+NOTE that the SA currently only works against VMs on Solaris/SPARC. +Remote debugging of Solaris/SPARC VMs on arbitrary platforms is +possible using the debug server; select "Connect to debug server..." +in HSDB. +

+ +

+Once the debugger has been launched, the threads list is displayed. +The current set of functionality allows: +

+ +
    +
  • Browsing of the annotated stack memory ("Stack Memory" button). It + is currently annotated with the following information: +
      +
    • Method names of the Java frames and their extents (supporting + inlined compiled methods) +
    • Locations and types of oops, found using the oop map information + from compiled methods (interpreter oop maps coming soon) +
    • If a Java frame was interrupted by a signal (e.g., because of a + crash), annotates the frame with the signal name and number +
    • Interpreter codelet descriptions for interpreted frames +
    +
  • Finding which thread or threads caused a crash (currently + identified by the presence of a signal handler frame) +
  • Browsing of oops using the Oop Inspector. +
  • Browsing of the java.lang.Thread object's oop. +
  • Object histogram and inspection of objects therein. +
+

+ +

+More functionality is planned. Please send email with suggestions on what +would be useful, with any questions or comments, or if the debugger +crashes. +

+ +

+ +Notes + +

+ +

+HSDB does not suspend the system at a safepoint, but at an arbitrary +point. This means that many of the invariants in the VM's code are not +followed. +

+ +

+As it happens, most of the places where the code ported over from the +VM has failed involve the topmost frame on the stack. Some +modifications have been made to allow the system to recognize +problematic situations. +

+ +

+Certainly, not all of the failure modes of the debugger have been +found. Please send email if +HSDB throws an exception. The best debugging aid in these situations +is a core file since it is a static view of the VM to which we can +then adapt the debugger code, as opposed to having to try to suspend +the VM over and over to reproduce the failure. gcore (1) is a useful +tool. (NOTE: do not try gcore with any application using the DGA X +server extension (example: Java2Demo); the kernel will panic. See bug +4343237.) +

+ + + diff --git a/hotspot/agent/make/jcoreproc.sh b/hotspot/agent/make/jcoreproc.sh new file mode 100644 index 00000000000..c38243f39e4 --- /dev/null +++ b/hotspot/agent/make/jcoreproc.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +# set the environment variable JCORE_PACKAGES to a comma separated list of +# packages whose classes have to be retrieved from the core file. + +$SA_JAVA_CMD -Dsun.jvm.hotspot.tools.jcore.filter=sun.jvm.hotspot.tools.jcore.PackageNameFilter -Dsun.jvm.hotspot.tools.jcore.PackageNameFilter.pkgList=$JCORE_PACKAGES sun.jvm.hotspot.tools.jcore.ClassDump $* diff --git a/hotspot/agent/make/jcoreproc64.sh b/hotspot/agent/make/jcoreproc64.sh new file mode 100644 index 00000000000..64530cf1300 --- /dev/null +++ b/hotspot/agent/make/jcoreproc64.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +# set the environment variable JCORE_PACKAGES to a comma separated list of +# packages whose classes have to be retrieved from the core file. + +$SA_JAVA_CMD -Dsun.jvm.hotspot.tools.jcore.filter=sun.jvm.hotspot.tools.jcore.PackageNameFilter -Dsun.jvm.hotspot.tools.jcore.PackageNameFilter.pkgList=$JCORE_PACKAGES sun.jvm.hotspot.tools.jcore.ClassDump $* diff --git a/hotspot/agent/make/jcorewindbg.bat b/hotspot/agent/make/jcorewindbg.bat new file mode 100644 index 00000000000..e368d03f97e --- /dev/null +++ b/hotspot/agent/make/jcorewindbg.bat @@ -0,0 +1,33 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +REM set the environment variable JCORE_PACKAGES to comman separated list of +REM packages whose classes have to be retrieved from the core file. + +%SA_JAVA_CMD% -Dsun.jvm.hotspot.tools.jcore.filter=sun.jvm.hotspot.tools.jcore.PackageNameFilter -Dsun.jvm.hotspot.tools.jcore.PackageNameFilter.pkgList=%JCORE_PACKAGES% sun.jvm.hotspot.tools.jcore.ClassDump %1 %2 + + diff --git a/hotspot/agent/make/jcorewindbg64.bat b/hotspot/agent/make/jcorewindbg64.bat new file mode 100644 index 00000000000..ecd162b4509 --- /dev/null +++ b/hotspot/agent/make/jcorewindbg64.bat @@ -0,0 +1,33 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +REM set the environment variable JCORE_PACKAGES to comman separated list of +REM packages whose classes have to be retrieved from the core file. + +%SA_JAVA_CMD% -Dsun.jvm.hotspot.tools.jcore.filter=sun.jvm.hotspot.tools.jcore.PackageNameFilter -Dsun.jvm.hotspot.tools.jcore.PackageNameFilter.pkgList=%JCORE_PACKAGES% sun.jvm.hotspot.tools.jcore.ClassDump %1 %2 + + diff --git a/hotspot/agent/make/jdbcore.sh b/hotspot/agent/make/jdbcore.sh new file mode 100644 index 00000000000..703767c891d --- /dev/null +++ b/hotspot/agent/make/jdbcore.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +usage() +{ + echo "usage: $0 " + exit 1 +} +# +if [ $# -lt 2 ]; then + usage +else + EXEC_FILE="${1}" + CORE_FILE="${2}" + echo "$0 attaching to core=${CORE_FILE}" +fi +# + +. `dirname $0`/saenv.sh + +$JAVA_HOME/bin/jdb -J-Xbootclasspath/a:$SA_CLASSPATH:$JAVA_HOME/lib/tools.jar \ + -J-Dsun.boot.library.path=$JAVA_HOME/jre/lib/$CPU:$SA_LIBPATH \ + -connect sun.jvm.hotspot.jdi.SACoreAttachingConnector:core=${CORE_FILE},javaExecutable=${EXEC_FILE} diff --git a/hotspot/agent/make/jdbcore64.sh b/hotspot/agent/make/jdbcore64.sh new file mode 100644 index 00000000000..8bb3dd4364a --- /dev/null +++ b/hotspot/agent/make/jdbcore64.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +usage() +{ + echo "usage: $0 " + exit 1 +} +# +if [ $# -lt 2 ]; then + usage +else + EXEC_FILE="${1}" + CORE_FILE="${2}" + echo "$0 attaching to core=${CORE_FILE}" +fi +# + +. `dirname $0`/saenv64.sh + +$JAVA_HOME/bin/jdb -J-d64 -J-Xbootclasspath/a:$SA_CLASSPATH:$JAVA_HOME/lib/tools.jar \ + -J-Dsun.boot.library.path=$JAVA_HOME/jre/lib/$CPU:$SA_LIBPATH \ + -connect sun.jvm.hotspot.jdi.SACoreAttachingConnector:core=${CORE_FILE},javaExecutable=${EXEC_FILE} diff --git a/hotspot/agent/make/jdbproc.sh b/hotspot/agent/make/jdbproc.sh new file mode 100644 index 00000000000..799ca440bfa --- /dev/null +++ b/hotspot/agent/make/jdbproc.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +usage() +{ + echo "usage: $0 " + exit 1 +} +# +if [ $# -lt 1 ]; then + usage +else + PID="${1}" + echo "$0 attaching to PID=${PID}" +fi +# + +. `dirname $0`/saenv.sh + +$JAVA_HOME/bin/jdb -J-Xbootclasspath/a:$SA_CLASSPATH:$JAVA_HOME/lib/tools.jar \ + -J-Dsun.boot.library.path=$JAVA_HOME/jre/lib/$CPU:$SA_LIBPATH \ + -connect sun.jvm.hotspot.jdi.SAPIDAttachingConnector:pid=${PID} diff --git a/hotspot/agent/make/jdbproc64.sh b/hotspot/agent/make/jdbproc64.sh new file mode 100644 index 00000000000..608d8330a1f --- /dev/null +++ b/hotspot/agent/make/jdbproc64.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +usage() +{ + echo "usage: $0 " + exit 1 +} +# +if [ $# -lt 1 ]; then + usage +else + PID="${1}" + echo "$0 attaching to PID=${PID}" +fi + +. `dirname $0`/saenv64.sh + +$JAVA_HOME/bin/jdb -J-d64 -J-Xbootclasspath/a:$SA_CLASSPATH:$JAVA_HOME/lib/tools.jar \ + -J-Dsun.boot.library.path=$JAVA_HOME/jre/lib/$CPU:$SA_LIBPATH \ + -connect sun.jvm.hotspot.jdi.SAPIDAttachingConnector:pid=${PID} + diff --git a/hotspot/agent/make/jhistoproc.sh b/hotspot/agent/make/jhistoproc.sh new file mode 100644 index 00000000000..d2e3b504abd --- /dev/null +++ b/hotspot/agent/make/jhistoproc.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.ObjectHistogram $* diff --git a/hotspot/agent/make/jhistoproc64.sh b/hotspot/agent/make/jhistoproc64.sh new file mode 100644 index 00000000000..8cda08be04b --- /dev/null +++ b/hotspot/agent/make/jhistoproc64.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.ObjectHistogram $* diff --git a/hotspot/agent/make/jhistowindbg.bat b/hotspot/agent/make/jhistowindbg.bat new file mode 100644 index 00000000000..28f17580bae --- /dev/null +++ b/hotspot/agent/make/jhistowindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.ObjectHistogram %1 %2 diff --git a/hotspot/agent/make/jhistowindbg64.bat b/hotspot/agent/make/jhistowindbg64.bat new file mode 100644 index 00000000000..b437f97aaff --- /dev/null +++ b/hotspot/agent/make/jhistowindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.ObjectHistogram %1 %2 diff --git a/hotspot/agent/make/jsdbproc.sh b/hotspot/agent/make/jsdbproc.sh new file mode 100644 index 00000000000..2d59d6e40cd --- /dev/null +++ b/hotspot/agent/make/jsdbproc.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.soql.JSDB $* diff --git a/hotspot/agent/make/jsdbproc64.sh b/hotspot/agent/make/jsdbproc64.sh new file mode 100644 index 00000000000..96ae452c8a9 --- /dev/null +++ b/hotspot/agent/make/jsdbproc64.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.JSDB $* diff --git a/hotspot/agent/make/jsdbwindbg.bat b/hotspot/agent/make/jsdbwindbg.bat new file mode 100644 index 00000000000..354425e1928 --- /dev/null +++ b/hotspot/agent/make/jsdbwindbg.bat @@ -0,0 +1,29 @@ +@echo off + +REM +REM Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.soql.JSDB %1 %2 diff --git a/hotspot/agent/make/jsdbwindbg64.bat b/hotspot/agent/make/jsdbwindbg64.bat new file mode 100644 index 00000000000..f6d761a82f4 --- /dev/null +++ b/hotspot/agent/make/jsdbwindbg64.bat @@ -0,0 +1,29 @@ +@echo off + +REM +REM Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.soql.JSDB %1 %2 diff --git a/hotspot/agent/make/jstackproc.sh b/hotspot/agent/make/jstackproc.sh new file mode 100644 index 00000000000..0e16dd9b009 --- /dev/null +++ b/hotspot/agent/make/jstackproc.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.StackTrace $* diff --git a/hotspot/agent/make/jstackproc64.sh b/hotspot/agent/make/jstackproc64.sh new file mode 100644 index 00000000000..aea5ef1f6bd --- /dev/null +++ b/hotspot/agent/make/jstackproc64.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.StackTrace $* diff --git a/hotspot/agent/make/jstackwindbg.bat b/hotspot/agent/make/jstackwindbg.bat new file mode 100644 index 00000000000..fdaed906901 --- /dev/null +++ b/hotspot/agent/make/jstackwindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.StackTrace %1 %2 diff --git a/hotspot/agent/make/jstackwindbg64.bat b/hotspot/agent/make/jstackwindbg64.bat new file mode 100644 index 00000000000..35562e7e783 --- /dev/null +++ b/hotspot/agent/make/jstackwindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.StackTrace %1 %2 diff --git a/hotspot/agent/make/marks_notes.html b/hotspot/agent/make/marks_notes.html new file mode 100644 index 00000000000..96b82356146 --- /dev/null +++ b/hotspot/agent/make/marks_notes.html @@ -0,0 +1,99 @@ + + + + Hotspot SA User Interface Notes + + + +

Hotspot SA User Interface Notes

+ +

Workspace and Building

+ +

+ All the source code for the Serviceability Agent is in + src/share/vm/agent in the HotSport workspace + /net/jano.sfbay/export/disk05/hotspot/ws/1.4/sa_baseline +

+ You can build the project by typing gnumake in the + src/share/vm/agent directory. +

+ You can also use the default build target using the Ant build file (build.xml). You can download Ant from + http://jakarta.apache.org/ant. Documentation for Ant can be + found at http://jakarta.apache.org/ant/manual/index.html + + +

Running the project

+ +
    +
  • java -cp classes sun.jvm.hotspot.HSDB +
  • java -cp classes sun.jvm.hotspot.bugspot.Main +
+ +

Feedback

+

+ Refactoring of package hierarchy. All user interface components should be in + the ui package. Perhaps: sun.jvm.hotspot.ui.hsdb.Main for the HSDB and + sun.jvm.hotspot.ui.bugspot.Main for BugSpot. +

+ The src\share\vm\agent area seems like a workspace so it should be organized like + one. In particular, I'd like to suggest the following directory layout:
+ +

    +
  • src: All sources that are curently under the sun directory. +
  • classes: compiled class files. +
  • lib: Resources like images, icons and jar files. +
  • docs: Documentation +
  • deploy: distribution bundles for Java Web Start. +
+ +

+ Seems like there is a lot of redundant functionality. Between the HSDB and BugSpot. Perhaps + this can be consolidated with a javax.swing.Actions architecture. + +

Tasklist

+ +

+ Stack memory pane: + It's one of the more useful JVM debugging tools in the SA. However, it + doesn't support any interaction with the text; the Memory Panel in BugSpot + was written afterward (with help from Shannon) and implements proper + selection, scrolling, and drag-and-drop, but no annotations. I'm not sure how + to integrate the annotations with the JTable that's being used for the memory + view; if you have suggestions here please let me know. +

+ Integrations with the NetBeans architecture (plug in). See the + Netbeans Open APIs homepage + + +

+ HSDB: Object Histogram. Column sizes should be sized according the the + contents. i.e, The size and count columns should be narrow enought to + handle the largest window. Since there is a lot of data, sorting + and searching should be implemented. +

+ +

Log

+ + Last modified: Tue Feb 05 19:15:12 Pacific Standard Time 2002 +

+ sun.jvm.hotspot.oops.ObjectHistogram should be the underlying data + structure for the TableModels. It shouldnt bother with sorting the data - + the table model should do that. It should implement these methods: + +

+      public int getSize()
+      public ObjectHistogramElement getElementAt(int row);
+    
+

+ ObjectHistogramElement should return the String that represents + the third column + + +


+
Mark Davidson
+ + +Last modified: Tue Feb 05 20:05:13 Pacific Standard Time 2002 + + + diff --git a/hotspot/agent/make/mkinstall b/hotspot/agent/make/mkinstall new file mode 100644 index 00000000000..a9940005317 --- /dev/null +++ b/hotspot/agent/make/mkinstall @@ -0,0 +1,146 @@ + +# make the directories + +SA_NAME=sa17 +SA_TEST=$SA_NAME/test + +mkdir $SA_NAME +mkdir $SA_NAME/solaris +mkdir $SA_NAME/solaris/amd64 +mkdir $SA_NAME/solaris/sparc +mkdir $SA_NAME/solaris/sparcv9 +mkdir $SA_NAME/solaris/i386 +mkdir $SA_NAME/linux +mkdir $SA_NAME/linux/i386 +mkdir $SA_NAME/linux/ia64 +mkdir $SA_NAME/linux/amd64 +mkdir $SA_NAME/win32 +mkdir $SA_NAME/win32/i386 +mkdir $SA_NAME/win32/ia64 +mkdir $SA_NAME/win32/amd64 +mkdir $SA_TEST + +# make sa.jar +jar -cvf $SA_NAME/sa.jar -C ../build/classes . + +# copy the native libraries + +cp ../src/os/solaris/proc/amd64/libsaproc.so $SA_NAME/solaris/amd64 +cp ../src/os/solaris/proc/sparc/libsaproc.so $SA_NAME/solaris/sparc +cp ../src/os/solaris/proc/sparcv9/libsaproc.so $SA_NAME/solaris/sparcv9 +cp ../src/os/solaris/proc/i386/libsaproc.so $SA_NAME/solaris/i386 +cp ../src/os/linux/i386/libsaproc.so $SA_NAME/linux/i386 +cp ../src/os/linux/ia64/libsaproc.so $SA_NAME/linux/ia64 +cp ../src/os/linux/amd64/libsaproc.so $SA_NAME/linux/amd64 +cp ../src/os/win32/windbg/i386/sawindbg.dll $SA_NAME/win32/i386 +cp ../src/os/win32/windbg/ia64/sawindbg.dll $SA_NAME/win32/ia64 +cp ../src/os/win32/windbg/amd64/sawindbg.dll $SA_NAME/win32/amd64 + +# copy Unix (Solaris and Linux) shell scripts +cp saenv.sh $SA_NAME ; chmod 755 $SA_NAME/saenv.sh +cp saenv64.sh $SA_NAME ; chmod 755 $SA_NAME/saenv64.sh +cp clhsdbproc.sh $SA_NAME ; chmod 755 $SA_NAME/clhsdbproc.sh +cp clhsdbproc64.sh $SA_NAME ; chmod 755 $SA_NAME/clhsdbproc64.sh +cp dumpflagsproc.sh $SA_NAME ; chmod 755 $SA_NAME/dumpflagsproc.sh +cp dumpflagsproc64.sh $SA_NAME ; chmod 755 $SA_NAME/dumpflagsproc64.sh +cp dumpsyspropsproc.sh $SA_NAME ; chmod 755 $SA_NAME/dumpsyspropsproc.sh +cp dumpsyspropsproc64.sh $SA_NAME ; chmod 755 $SA_NAME/dumpsyspropsproc64.sh +cp finalizerinfoproc.sh $SA_NAME ; chmod 755 $SA_NAME/finalizerinfoproc.sh +cp finalizerinfoproc64.sh $SA_NAME ; chmod 755 $SA_NAME/finalizerinfoproc64.sh +cp heapdumpproc.sh $SA_NAME ; chmod 755 $SA_NAME/heapdumpproc.sh +cp heapdumpproc64.sh $SA_NAME ; chmod 755 $SA_NAME/heapdumpproc64.sh +cp heapsumproc.sh $SA_NAME ; chmod 755 $SA_NAME/heapsumproc.sh +cp heapsumproc64.sh $SA_NAME ; chmod 755 $SA_NAME/heapsumproc64.sh +cp hsdbproc.sh $SA_NAME ; chmod 755 $SA_NAME/hsdbproc.sh +cp hsdbproc64.sh $SA_NAME ; chmod 755 $SA_NAME/hsdbproc64.sh +cp jcoreproc.sh $SA_NAME ; chmod 755 $SA_NAME/jcoreproc.sh +cp jcoreproc64.sh $SA_NAME ; chmod 755 $SA_NAME/jcoreproc64.sh +cp jdbcore.sh $SA_NAME ; chmod 755 $SA_NAME/jdbcore.sh +cp jdbcore64.sh $SA_NAME ; chmod 755 $SA_NAME/jdbcore64.sh +cp jdbproc.sh $SA_NAME ; chmod 755 $SA_NAME/jdbproc.sh +cp jdbproc64.sh $SA_NAME ; chmod 755 $SA_NAME/jdbproc64.sh +cp jhistoproc.sh $SA_NAME ; chmod 755 $SA_NAME/jhistoproc.sh +cp jhistoproc64.sh $SA_NAME ; chmod 755 $SA_NAME/jhistoproc64.sh +cp jsdbproc.sh $SA_NAME ; chmod 755 $SA_NAME/jsdbproc.sh +cp jsdbproc64.sh $SA_NAME ; chmod 755 $SA_NAME/jsdbproc64.sh +cp jstackproc.sh $SA_NAME ; chmod 755 $SA_NAME/jstackproc.sh +cp jstackproc64.sh $SA_NAME ; chmod 755 $SA_NAME/jstackproc64.sh +cp permstatproc.sh $SA_NAME ; chmod 755 $SA_NAME/permstatproc.sh +cp permstatproc64.sh $SA_NAME ; chmod 755 $SA_NAME/permstatproc64.sh +cp pmapproc.sh $SA_NAME ; chmod 755 $SA_NAME/pmapproc.sh +cp pmapproc64.sh $SA_NAME ; chmod 755 $SA_NAME/pmapproc64.sh +cp pstackproc.sh $SA_NAME ; chmod 755 $SA_NAME/pstackproc.sh +cp pstackproc64.sh $SA_NAME ; chmod 755 $SA_NAME/pstackproc64.sh +cp soqlproc.sh $SA_NAME ; chmod 755 $SA_NAME/soqlproc.sh +cp soqlproc64.sh $SA_NAME ; chmod 755 $SA_NAME/soqlproc64.sh +cp start-debug-server $SA_NAME ; chmod 755 $SA_NAME/start-debug-server +cp start-debug-server-proc.sh $SA_NAME ; chmod 755 $SA_NAME/start-debug-server-proc.sh +cp start-debug-server-proc64.sh $SA_NAME ; chmod 755 $SA_NAME/start-debug-server-proc64.sh +cp start-rmiregistry.sh $SA_NAME ; chmod 755 $SA_NAME/start-rmiregistry.sh + +# copy Windows batch files +cp saenv.bat $SA_NAME ; chmod 755 $SA_NAME/saenv.bat +cp saenv64.bat $SA_NAME ; chmod 755 $SA_NAME/saenv64.bat +cp clhsdbwindbg.bat $SA_NAME ; chmod 755 $SA_NAME/clhsdbwindbg.bat +cp clhsdbwindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/clhsdbwindbg64.bat +cp dumpflagswindbg.bat $SA_NAME ; chmod 755 $SA_NAME/dumpflagswindbg.bat +cp dumpflagswindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/dumpflagswindbg64.bat +cp dumpsyspropswindbg.bat $SA_NAME ; chmod 755 $SA_NAME/dumpsyspropswindbg.bat +cp dumpsyspropswindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/dumpsyspropswindbg64.bat +cp finalizerinfowindbg.bat $SA_NAME ; chmod 755 $SA_NAME/finalizerinfowindbg.bat +cp finalizerinfowindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/finalizerinfowindbg64.bat +cp heapdumpwindbg.bat $SA_NAME ; chmod 755 $SA_NAME/heapdumpwindbg.bat +cp heapdumpwindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/heapdumpwindbg64.bat +cp heapsumwindbg.bat $SA_NAME ; chmod 755 $SA_NAME/heapsumwindbg.bat +cp heapsumwindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/heapsumwindbg64.bat +cp hsdbwindbg.bat $SA_NAME ; chmod 755 $SA_NAME/hsdbwindbg.bat +cp hsdbwindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/hsdbwindbg64.bat +cp jcorewindbg.bat $SA_NAME ; chmod 755 $SA_NAME/jcorewindbg.bat +cp jcorewindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/jcorewindbg64.bat +cp jhistowindbg.bat $SA_NAME ; chmod 755 $SA_NAME/jhistowindbg.bat +cp jhistowindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/jhistowindbg64.bat +cp jsdbwindbg.bat $SA_NAME ; chmod 755 $SA_NAME/jsdbwindbg.bat +cp jsdbwindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/jsdbwindbg64.bat +cp jstackwindbg.bat $SA_NAME ; chmod 755 $SA_NAME/jstackwindbg.bat +cp jstackwindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/jstackwindbg64.bat +cp permstatwindbg.bat $SA_NAME ; chmod 755 $SA_NAME/permstatwindbg.bat +cp permstatwindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/permstatwindbg64.bat +cp pmapwindbg.bat $SA_NAME ; chmod 755 $SA_NAME/pmapwindbg.bat +cp pmapwindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/pmapwindbg64.bat +cp pstackwindbg.bat $SA_NAME ; chmod 755 $SA_NAME/pstackwindbg.bat +cp pstackwindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/pstackwindbg64.bat +cp soqlwindbg.bat $SA_NAME ; chmod 755 $SA_NAME/soqlwindbg.bat +cp soqlwindbg64.bat $SA_NAME ; chmod 755 $SA_NAME/soqlwindbg64.bat +cp start-debug-server-windbg.bat $SA_NAME ; chmod 755 $SA_NAME/start-debug-server-windbg.bat +cp start-debug-server-windbg64.bat $SA_NAME ; chmod 755 $SA_NAME/start-debug-server-windbg64.bat +cp start-rmiregistry.bat $SA_NAME ; chmod 755 $SA_NAME/start-rmiregistry.bat + + +# make the libproc test +cd ../test/libproc ; make; cd ../../make + +# copy libproc test suite + +cp ../test/libproc/README $SA_TEST/README-libproc +cp ../test/libproc/libproctest.sh $SA_TEST ; chmod 755 $SA_TEST/libproctest.sh +cp ../test/libproc/libproctest64.sh $SA_TEST ; chmod 755 $SA_TEST/libproctest64.sh +cp ../test/libproc/*.class $SA_TEST + +# copy RMI security policy file +cp grantAll.policy $SA_NAME + +# copy documentation +mkdir $SA_NAME/doc +cp ../doc/*.html $SA_NAME/doc +chmod 644 $SA_NAME/doc/*.html + +# make lib dir and copy other jar files +mkdir $SA_NAME/lib +cp ../src/share/lib/*.jar $SA_NAME/lib + +# tar and gzip +tar -cvf $SA_NAME.tar $SA_NAME +gzip $SA_NAME.tar + +# cleanup +\rm -rf $SA_NAME diff --git a/hotspot/agent/make/permstatproc.sh b/hotspot/agent/make/permstatproc.sh new file mode 100644 index 00000000000..2772d23c8ce --- /dev/null +++ b/hotspot/agent/make/permstatproc.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.PermStat $* diff --git a/hotspot/agent/make/permstatproc64.sh b/hotspot/agent/make/permstatproc64.sh new file mode 100644 index 00000000000..6abeb76d5cc --- /dev/null +++ b/hotspot/agent/make/permstatproc64.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.PermStat $* diff --git a/hotspot/agent/make/permstatwindbg.bat b/hotspot/agent/make/permstatwindbg.bat new file mode 100644 index 00000000000..7c35ed384a9 --- /dev/null +++ b/hotspot/agent/make/permstatwindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.PermStat %1 %2 diff --git a/hotspot/agent/make/permstatwindbg64.bat b/hotspot/agent/make/permstatwindbg64.bat new file mode 100644 index 00000000000..721c23e1865 --- /dev/null +++ b/hotspot/agent/make/permstatwindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.PermStat %1 %2 diff --git a/hotspot/agent/make/pmapproc.sh b/hotspot/agent/make/pmapproc.sh new file mode 100644 index 00000000000..91c27c55b62 --- /dev/null +++ b/hotspot/agent/make/pmapproc.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.PMap $* diff --git a/hotspot/agent/make/pmapproc64.sh b/hotspot/agent/make/pmapproc64.sh new file mode 100644 index 00000000000..bd33e5ce7a0 --- /dev/null +++ b/hotspot/agent/make/pmapproc64.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.PMap $* + diff --git a/hotspot/agent/make/pmapwindbg.bat b/hotspot/agent/make/pmapwindbg.bat new file mode 100644 index 00000000000..50e653c8b4d --- /dev/null +++ b/hotspot/agent/make/pmapwindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.PMap %1 %2 diff --git a/hotspot/agent/make/pmapwindbg64.bat b/hotspot/agent/make/pmapwindbg64.bat new file mode 100644 index 00000000000..414a75a62dd --- /dev/null +++ b/hotspot/agent/make/pmapwindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.PMap %1 %2 diff --git a/hotspot/agent/make/pstackproc.sh b/hotspot/agent/make/pstackproc.sh new file mode 100644 index 00000000000..1d22899020e --- /dev/null +++ b/hotspot/agent/make/pstackproc.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +type c++filt 1>/dev/null 2>/dev/null +if [ $? -eq 0 ]; then + $SA_JAVA_CMD sun.jvm.hotspot.tools.PStack $* | c++filt +else + $SA_JAVA_CMD sun.jvm.hotspot.tools.PStack $* +fi + diff --git a/hotspot/agent/make/pstackproc64.sh b/hotspot/agent/make/pstackproc64.sh new file mode 100644 index 00000000000..bb5ce693af8 --- /dev/null +++ b/hotspot/agent/make/pstackproc64.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +type c++filt 1>/dev/null 2>/dev/null +if [ $? -eq 0 ]; then + $SA_JAVA_CMD sun.jvm.hotspot.tools.PStack $* | c++filt +else + $SA_JAVA_CMD sun.jvm.hotspot.tools.PStack $* +fi + diff --git a/hotspot/agent/make/pstackwindbg.bat b/hotspot/agent/make/pstackwindbg.bat new file mode 100644 index 00000000000..1a7f33b099a --- /dev/null +++ b/hotspot/agent/make/pstackwindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.PStack %1 %2 diff --git a/hotspot/agent/make/pstackwindbg64.bat b/hotspot/agent/make/pstackwindbg64.bat new file mode 100644 index 00000000000..b1fd4438a36 --- /dev/null +++ b/hotspot/agent/make/pstackwindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.PStack %1 %2 diff --git a/hotspot/agent/make/saenv.bat b/hotspot/agent/make/saenv.bat new file mode 100644 index 00000000000..b5342027b98 --- /dev/null +++ b/hotspot/agent/make/saenv.bat @@ -0,0 +1,54 @@ +@echo off +REM +REM Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +REM This is common environment settings for all SA +REM windows batch scripts + +REM set jre\bin and jre\bin\client (or server) in PATH +REM WINDBG_HOME must point to the Windows Debugging Tools +REM installation directory + +if "%SA_JAVA%" == "" goto no_sa_java + +goto sa_java_set + +:no_sa_java +set SA_JAVA=java + +:sa_java_set + +set SA_CLASSPATH=..\build\classes;..\src\share\lib\maf-1_0.jar;..\src\share\lib\jlfgr-1_0.jar;..\src\share\lib\js.jar;sa.jar;lib\maf-1_0.jar;lib\jlfgr-1_0.jar;lib\js.jar + +set SA_LIBPATH=..\src\os\win32\windbg\i386;.\win32\i386 + +set OPTIONS=-Dsun.jvm.hotspot.debugger.useWindbgDebugger +set OPTIONS=-Dsun.jvm.hotspot.debugger.windbg.imagePath="%PATH%" %OPTIONS% +set OPTIONS=-Dsun.jvm.hotspot.debugger.windbg.sdkHome="%WINDBG_HOME%" %OPTIONS% + +if "%SA_DISABLE_VERS_CHK%" == "" goto vers_chk +set OPTIONS="-Dsun.jvm.hotspot.runtime.VM.disableVersionCheck %OPTIONS%" + +:vers_chk +set SA_JAVA_CMD=%SA_JAVA% -showversion -cp %SA_CLASSPATH% -Djava.library.path=%SA_LIBPATH% %OPTIONS% diff --git a/hotspot/agent/make/saenv.sh b/hotspot/agent/make/saenv.sh new file mode 100644 index 00000000000..e5e5c485dd4 --- /dev/null +++ b/hotspot/agent/make/saenv.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# +# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This file sets common environment variables for all SA scripts + +OS=`uname` +STARTDIR=`dirname $0` +ARCH=`uname -m` + +if [ "x$SA_JAVA" = "x" ]; then + SA_JAVA=java +fi + +if [ "$OS" = "Linux" ]; then + if [ "$ARCH" = "ia64" ] ; then + SA_LIBPATH=$STARTDIR/../src/os/linux/ia64:$STARTDIR/linux/ia64 + OPTIONS="-Dsa.library.path=$SA_LIBPATH" + CPU=ia64 + elif [ "$ARCH" = "x86_64" ] ; then + SA_LIBPATH=$STARTDIR/../src/os/linux/amd64:$STARTDIR/linux/amd64 + OPTIONS="-Dsa.library.path=$SA_LIBPATH" + CPU=amd64 + else + SA_LIBPATH=$STARTDIR/../src/os/linux/i386:$STARTDIR/linux/i386 + OPTIONS="-Dsa.library.path=$SA_LIBPATH" + CPU=i386 + fi +else + SA_LIBPATH=$STARTDIR/../src/os/solaris/proc/`uname -p`:$STARTDIR/solaris/`uname -p` + OPTIONS="-Dsa.library.path=$SA_LIBPATH -Dsun.jvm.hotspot.debugger.useProcDebugger" + CPU=sparc +fi + +if [ "x$SA_DISABLE_VERS_CHK" != "x" ]; then + OPTIONS="-Dsun.jvm.hotspot.runtime.VM.disableVersionCheck ${OPTIONS}" +fi + + +SA_CLASSPATH=$STARTDIR/../build/classes:$STARTDIR/../src/share/lib/maf-1_0.jar:$STARTDIR/../src/share/lib/jlfgr-1_0.jar:$STARTDIR/../src/share/lib/js.jar:$STARTDIR/sa.jar:$STARTDIR/lib/maf-1_0.jar:$STARTDIR/lib/jlfgr-1_0.jar:$STARTDIR/lib/js.jar + +OPTIONS="-Djava.system.class.loader=sun.jvm.hotspot.SALauncherLoader ${OPTIONS}" + +SA_JAVA_CMD="$SA_PREFIX_CMD $SA_JAVA -showversion ${OPTIONS} -cp $SA_CLASSPATH $SA_OPTIONS" diff --git a/hotspot/agent/make/saenv64.bat b/hotspot/agent/make/saenv64.bat new file mode 100644 index 00000000000..84e30c31bc2 --- /dev/null +++ b/hotspot/agent/make/saenv64.bat @@ -0,0 +1,60 @@ +@echo off +REM +REM Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +REM FIXME: How do I detect processor on Windows so that +REM AMD-64/IA-64 could be detected? Should I assume +REM MKS/Cygwin here? + +REM This is common environment settings for all SA +REM windows batch scripts + +REM set jre\bin and jre\bin\client (or server) in PATH +REM WINDBG_HOME must point to the Windows Debugging Tools +REM installation directory. + +if "%SA_JAVA%" == "" goto no_sa_java + +goto sa_java_set + +:no_sa_java +set SA_JAVA=java + +:sa_java_set + +set SA_CLASSPATH=..\build\classes;..\src\share\lib\maf-1_0.jar;..\src\share\lib\jlfgr-1_0.jar;..\src\share\lib\js.jar;sa.jar;lib\maf-1_0.jar;lib\jlfgr-1_0.jar;lib\js.jar + +REM For now, only AMD-64, IA-64 stack walking is not working anyway +set SA_LIBPATH=.\src\os\win32\windbg\amd64;.\win32\amd64 + +set OPTIONS=-Dsun.jvm.hotspot.debugger.useWindbgDebugger +set OPTIONS=-Dsun.jvm.hotspot.debugger.windbg.imagePath="%PATH%" %OPTIONS% +set OPTIONS=-Dsun.jvm.hotspot.debugger.windbg.sdkHome="%WINDBG_HOME%" %OPTIONS% + +if "%SA_DISABLE_VERS_CHK%" == "" goto vers_chk +set OPTIONS="-Dsun.jvm.hotspot.runtime.VM.disableVersionCheck %OPTIONS%" + +:vers_chk + +set SA_JAVA_CMD=%SA_JAVA% -showversion -cp %SA_CLASSPATH% -Djava.library.path=.%SA_LIBPATH% %OPTIONS% diff --git a/hotspot/agent/make/saenv64.sh b/hotspot/agent/make/saenv64.sh new file mode 100644 index 00000000000..ea22420496b --- /dev/null +++ b/hotspot/agent/make/saenv64.sh @@ -0,0 +1,62 @@ +#!/bin/sh +# +# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This file sets common environment variables for all 64-bit Solaris [sparcv9, +# amd64] SA scripts. Please note that for 64-bit Linux use saenv.sh. + +OS=`uname` +STARTDIR=`dirname $0` + +CPU=`isainfo | grep sparcv9` + +if [ "x$CPU" != "x" ]; then + CPU=sparcv9 +else + CPU=`isainfo | grep amd64` + if [ "x$CPU" != "x" ]; then + CPU=amd64 + else + echo "unknown CPU, only sparcv9, amd64 are supported!" + exit 1 + fi +fi + +SA_LIBPATH=$STARTDIR/../src/os/solaris/proc/$CPU:$STARTDIR/solaris/$CPU + +OPTIONS="-Dsa.library.path=$SA_LIBPATH -Dsun.jvm.hotspot.debugger.useProcDebugger" + +if [ "x$SA_JAVA" = "x" ]; then + SA_JAVA=java +fi + +if [ "x$SA_DISABLE_VERS_CHK" != "x" ]; then + OPTIONS="-Dsun.jvm.hotspot.runtime.VM.disableVersionCheck ${OPTIONS}" +fi + +SA_CLASSPATH=$STARTDIR/../build/classes:$STARTDIR/../src/share/lib/maf-1_0.jar:$STARTDIR/../src/share/lib/jlfgr-1_0.jar:$STARTDIR/../src/share/lib/js.jar:$STARTDIR/sa.jar:$STARTDIR/lib/maf-1_0.jar:$STARTDIR/lib/jlfgr-1_0.jar:$STARTDIR/lib/js.jar + +OPTIONS="-Djava.system.class.loader=sun.jvm.hotspot.SALauncherLoader ${OPTIONS}" + +SA_JAVA_CMD="$SA_PREFIX_CMD $SA_JAVA -d64 -showversion ${OPTIONS} -cp $SA_CLASSPATH $SA_OPTIONS" diff --git a/hotspot/agent/make/soqlproc.sh b/hotspot/agent/make/soqlproc.sh new file mode 100644 index 00000000000..9dba98a13e9 --- /dev/null +++ b/hotspot/agent/make/soqlproc.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.soql.SOQL $* diff --git a/hotspot/agent/make/soqlproc64.sh b/hotspot/agent/make/soqlproc64.sh new file mode 100644 index 00000000000..153354529c5 --- /dev/null +++ b/hotspot/agent/make/soqlproc64.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +$SA_JAVA_CMD sun.jvm.hotspot.tools.soql.SOQL $* diff --git a/hotspot/agent/make/soqlwindbg.bat b/hotspot/agent/make/soqlwindbg.bat new file mode 100644 index 00000000000..4d1e373f0ae --- /dev/null +++ b/hotspot/agent/make/soqlwindbg.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.soql.SOQL %1 %2 diff --git a/hotspot/agent/make/soqlwindbg64.bat b/hotspot/agent/make/soqlwindbg64.bat new file mode 100644 index 00000000000..4bbf72109f1 --- /dev/null +++ b/hotspot/agent/make/soqlwindbg64.bat @@ -0,0 +1,28 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +%SA_JAVA_CMD% sun.jvm.hotspot.tools.soql.SOQL %1 %2 diff --git a/hotspot/agent/make/start-debug-server b/hotspot/agent/make/start-debug-server new file mode 100644 index 00000000000..dfaf5d91515 --- /dev/null +++ b/hotspot/agent/make/start-debug-server @@ -0,0 +1,18 @@ +#!/bin/sh + +STARTDIR=`dirname $0` + +if [ "x$SA_JAVA" = "x" ]; then + SA_JAVA=java +fi + +if [ -f $STARTDIR/sa.jar ] ; then + CP=$STARTDIR/sa.jar +else + CP=$STARTDIR/../build/classes +fi + +# License file for development version of dbx +setenv LM_LICENSE_FILE 7588@extend.eng:/usr/dist/local/config/sparcworks/license.dat:7588@setlicense + +$SA_JAVA -Xbootclasspath/p:$CP -Djava.rmi.server.codebase=file:/$CP -Djava.security.policy=$STARTDIR\/grantAll.policy sun.jvm.hotspot.DebugServer $* diff --git a/hotspot/agent/make/start-debug-server-proc.sh b/hotspot/agent/make/start-debug-server-proc.sh new file mode 100644 index 00000000000..2f809818b9d --- /dev/null +++ b/hotspot/agent/make/start-debug-server-proc.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv.sh + +if [ -f $STARTDIR/sa.jar ] ; then + CP=$STARTDIR/sa.jar +else + CP=$STARTDIR/../build/classes +fi + +$SA_JAVA -classpath $CP ${OPTIONS} -Djava.rmi.server.codebase=file:/$CP -Djava.security.policy=$STARTDIR\/grantAll.policy sun.jvm.hotspot.DebugServer $* diff --git a/hotspot/agent/make/start-debug-server-proc64.sh b/hotspot/agent/make/start-debug-server-proc64.sh new file mode 100644 index 00000000000..10146777c30 --- /dev/null +++ b/hotspot/agent/make/start-debug-server-proc64.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +. `dirname $0`/saenv64.sh + +if [ -f $STARTDIR/sa.jar ] ; then + CP=$STARTDIR/sa.jar +else + CP=$STARTDIR/../build/classes +fi + +$SA_JAVA -d64 -classpath $CP ${OPTIONS} -Djava.rmi.server.codebase=file:/$CP -Djava.security.policy=$STARTDIR\/grantAll.policy sun.jvm.hotspot.DebugServer $* diff --git a/hotspot/agent/make/start-debug-server-windbg.bat b/hotspot/agent/make/start-debug-server-windbg.bat new file mode 100644 index 00000000000..9cac3839327 --- /dev/null +++ b/hotspot/agent/make/start-debug-server-windbg.bat @@ -0,0 +1,39 @@ +@echo off +REM +REM Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv.bat + +REM check for .\sa.jar, if it does not exist +REM assume that we are in build configuration. + +if not exist .\sa.jar goto IN_BUILD_CONF +set SA_CLASSPATH=.\sa.jar +goto EXEC_CMD + +:IN_BUILD_CONF +set SA_CLASSPATH=..\build\classes + +:EXEC_CMD +%SA_JAVA% -classpath %SA_CLASSPATH% -Djava.rmi.server.codebase=file:/%SA_CLASSPATH% -Djava.security.policy=grantAll.policy -Djava.library.path=%SA_LIBPATH% %OPTIONS% sun.jvm.hotspot.DebugServer %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/hotspot/agent/make/start-debug-server-windbg64.bat b/hotspot/agent/make/start-debug-server-windbg64.bat new file mode 100644 index 00000000000..bb7a4f99248 --- /dev/null +++ b/hotspot/agent/make/start-debug-server-windbg64.bat @@ -0,0 +1,39 @@ +@echo off +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +call saenv64.bat + +REM check for .\sa.jar, if it does not exist +REM assume that we are in build configuration. + +if not exist .\sa.jar goto IN_BUILD_CONF +set SA_CLASSPATH=.\sa.jar +goto EXEC_CMD + +:IN_BUILD_CONF +set SA_CLASSPATH=..\build\classes + +:EXEC_CMD +%SA_JAVA% -classpath %SA_CLASSPATH% -Djava.rmi.server.codebase=file:/%SA_CLASSPATH% -Djava.security.policy=grantAll.policy -Djava.library.path=%SA_LIBPATH% %OPTIONS% sun.jvm.hotspot.DebugServer %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/hotspot/agent/make/start-rmiregistry.bat b/hotspot/agent/make/start-rmiregistry.bat new file mode 100644 index 00000000000..d7f2a41d745 --- /dev/null +++ b/hotspot/agent/make/start-rmiregistry.bat @@ -0,0 +1,37 @@ +@echo off +REM +REM Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +REM check for .\sa.jar, if it does not exist +REM assume that we are in build configuration. + +if not exist .\sa.jar goto IN_BUILD_CONF +set CLASSPATH=.\sa.jar +goto EXEC_CMD + +:IN_BUILD_CONF +set CLASSPATH=..\build\classes + +:EXEC_CMD +start rmiregistry -J-Xbootclasspath/p:%CLASSPATH% diff --git a/hotspot/agent/make/start-rmiregistry.sh b/hotspot/agent/make/start-rmiregistry.sh new file mode 100644 index 00000000000..1a192603825 --- /dev/null +++ b/hotspot/agent/make/start-rmiregistry.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +STARTDIR=`dirname $0` + +if [ -f $STARTDIR/sa.jar ] ; then + CP=$STARTDIR/sa.jar +else + CP=$STARTDIR/../build/classes +fi + +rmiregistry -J-Xbootclasspath/p:$CP diff --git a/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c b/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c new file mode 100644 index 00000000000..c4ca7def0b1 --- /dev/null +++ b/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c @@ -0,0 +1,412 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include "libproc.h" + +#if defined(x86_64) && !defined(amd64) +#define amd64 1 +#endif + +#ifdef i386 +#include "sun_jvm_hotspot_debugger_x86_X86ThreadContext.h" +#endif + +#ifdef amd64 +#include "sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext.h" +#endif + +#if defined(sparc) || defined(sparcv9) +#include "sun_jvm_hotspot_debugger_sparc_SPARCThreadContext.h" +#endif + +static jfieldID p_ps_prochandle_ID = 0; +static jfieldID threadList_ID = 0; +static jfieldID loadObjectList_ID = 0; + +static jmethodID createClosestSymbol_ID = 0; +static jmethodID createLoadObject_ID = 0; +static jmethodID getThreadForThreadId_ID = 0; +static jmethodID listAdd_ID = 0; + +#define CHECK_EXCEPTION_(value) if ((*env)->ExceptionOccurred(env)) { return value; } +#define CHECK_EXCEPTION if ((*env)->ExceptionOccurred(env)) { return;} +#define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { throw_new_debugger_exception(env, str); return value; } +#define THROW_NEW_DEBUGGER_EXCEPTION(str) { throw_new_debugger_exception(env, str); return;} + +static void throw_new_debugger_exception(JNIEnv* env, const char* errMsg) { + (*env)->ThrowNew(env, (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"), errMsg); +} + +static struct ps_prochandle* get_proc_handle(JNIEnv* env, jobject this_obj) { + jlong ptr = (*env)->GetLongField(env, this_obj, p_ps_prochandle_ID); + return (struct ps_prochandle*)(intptr_t)ptr; +} + +/* + * Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal + * Method: init0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_init0 + (JNIEnv *env, jclass cls) { + jclass listClass; + + if (init_libproc(getenv("LIBSAPROC_DEBUG")) != true) { + THROW_NEW_DEBUGGER_EXCEPTION("can't initialize libproc"); + } + + // fields we use + p_ps_prochandle_ID = (*env)->GetFieldID(env, cls, "p_ps_prochandle", "J"); + CHECK_EXCEPTION; + threadList_ID = (*env)->GetFieldID(env, cls, "threadList", "Ljava/util/List;"); + CHECK_EXCEPTION; + loadObjectList_ID = (*env)->GetFieldID(env, cls, "loadObjectList", "Ljava/util/List;"); + CHECK_EXCEPTION; + + // methods we use + createClosestSymbol_ID = (*env)->GetMethodID(env, cls, "createClosestSymbol", + "(Ljava/lang/String;J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol;"); + CHECK_EXCEPTION; + createLoadObject_ID = (*env)->GetMethodID(env, cls, "createLoadObject", + "(Ljava/lang/String;JJ)Lsun/jvm/hotspot/debugger/cdbg/LoadObject;"); + CHECK_EXCEPTION; + getThreadForThreadId_ID = (*env)->GetMethodID(env, cls, "getThreadForThreadId", + "(J)Lsun/jvm/hotspot/debugger/ThreadProxy;"); + CHECK_EXCEPTION; + // java.util.List method we call + listClass = (*env)->FindClass(env, "java/util/List"); + CHECK_EXCEPTION; + listAdd_ID = (*env)->GetMethodID(env, listClass, "add", "(Ljava/lang/Object;)Z"); + CHECK_EXCEPTION; +} + +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_getAddressSize + (JNIEnv *env, jclass cls) +{ +#ifdef _LP64 + return 8; +#else + return 4; +#endif + +} + + +static void fillThreadsAndLoadObjects(JNIEnv* env, jobject this_obj, struct ps_prochandle* ph) { + int n = 0, i = 0; + + // add threads + n = get_num_threads(ph); + for (i = 0; i < n; i++) { + jobject thread; + jobject threadList; + lwpid_t lwpid; + + lwpid = get_lwp_id(ph, i); + thread = (*env)->CallObjectMethod(env, this_obj, getThreadForThreadId_ID, + (jlong)lwpid); + CHECK_EXCEPTION; + threadList = (*env)->GetObjectField(env, this_obj, threadList_ID); + CHECK_EXCEPTION; + (*env)->CallBooleanMethod(env, threadList, listAdd_ID, thread); + CHECK_EXCEPTION; + } + + // add load objects + n = get_num_libs(ph); + for (i = 0; i < n; i++) { + uintptr_t base; + const char* name; + jobject loadObject; + jobject loadObjectList; + + base = get_lib_base(ph, i); + name = get_lib_name(ph, i); + loadObject = (*env)->CallObjectMethod(env, this_obj, createLoadObject_ID, + (*env)->NewStringUTF(env, name), (jlong)0, (jlong)base); + CHECK_EXCEPTION; + loadObjectList = (*env)->GetObjectField(env, this_obj, loadObjectList_ID); + CHECK_EXCEPTION; + (*env)->CallBooleanMethod(env, loadObjectList, listAdd_ID, loadObject); + CHECK_EXCEPTION; + } +} + +/* + * Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal + * Method: attach0 + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__I + (JNIEnv *env, jobject this_obj, jint jpid) { + + struct ps_prochandle* ph; + if ( (ph = Pgrab(jpid)) == NULL) { + THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process"); + } + (*env)->SetLongField(env, this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph); + fillThreadsAndLoadObjects(env, this_obj, ph); +} + +/* + * Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal + * Method: attach0 + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2 + (JNIEnv *env, jobject this_obj, jstring execName, jstring coreName) { + const char *execName_cstr; + const char *coreName_cstr; + jboolean isCopy; + struct ps_prochandle* ph; + + execName_cstr = (*env)->GetStringUTFChars(env, execName, &isCopy); + CHECK_EXCEPTION; + coreName_cstr = (*env)->GetStringUTFChars(env, coreName, &isCopy); + CHECK_EXCEPTION; + + if ( (ph = Pgrab_core(execName_cstr, coreName_cstr)) == NULL) { + (*env)->ReleaseStringUTFChars(env, execName, execName_cstr); + (*env)->ReleaseStringUTFChars(env, coreName, coreName_cstr); + THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the core file"); + } + (*env)->SetLongField(env, this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph); + (*env)->ReleaseStringUTFChars(env, execName, execName_cstr); + (*env)->ReleaseStringUTFChars(env, coreName, coreName_cstr); + fillThreadsAndLoadObjects(env, this_obj, ph); +} + +/* + * Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal + * Method: detach0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_detach0 + (JNIEnv *env, jobject this_obj) { + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + if (ph != NULL) { + Prelease(ph); + } +} + +/* + * Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal + * Method: lookupByName0 + * Signature: (Ljava/lang/String;Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_lookupByName0 + (JNIEnv *env, jobject this_obj, jstring objectName, jstring symbolName) { + const char *objectName_cstr, *symbolName_cstr; + jlong addr; + jboolean isCopy; + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + + objectName_cstr = NULL; + if (objectName != NULL) { + objectName_cstr = (*env)->GetStringUTFChars(env, objectName, &isCopy); + CHECK_EXCEPTION_(0); + } + symbolName_cstr = (*env)->GetStringUTFChars(env, symbolName, &isCopy); + CHECK_EXCEPTION_(0); + + addr = (jlong) lookup_symbol(ph, objectName_cstr, symbolName_cstr); + + if (objectName_cstr != NULL) { + (*env)->ReleaseStringUTFChars(env, objectName, objectName_cstr); + } + (*env)->ReleaseStringUTFChars(env, symbolName, symbolName_cstr); + return addr; +} + +/* + * Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal + * Method: lookupByAddress0 + * Signature: (J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol; + */ +JNIEXPORT jobject JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_lookupByAddress0 + (JNIEnv *env, jobject this_obj, jlong addr) { + uintptr_t offset; + const char* sym = NULL; + + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + sym = symbol_for_pc(ph, (uintptr_t) addr, &offset); + if (sym == NULL) return 0; + return (*env)->CallObjectMethod(env, this_obj, createClosestSymbol_ID, + (*env)->NewStringUTF(env, sym), (jlong)offset); +} + +/* + * Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal + * Method: readBytesFromProcess0 + * Signature: (JJ)Lsun/jvm/hotspot/debugger/ReadResult; + */ +JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_readBytesFromProcess0 + (JNIEnv *env, jobject this_obj, jlong addr, jlong numBytes) { + + jboolean isCopy; + jbyteArray array; + jbyte *bufPtr; + ps_err_e err; + + array = (*env)->NewByteArray(env, numBytes); + CHECK_EXCEPTION_(0); + bufPtr = (*env)->GetByteArrayElements(env, array, &isCopy); + CHECK_EXCEPTION_(0); + + err = ps_pdread(get_proc_handle(env, this_obj), (psaddr_t) (uintptr_t)addr, bufPtr, numBytes); + (*env)->ReleaseByteArrayElements(env, array, bufPtr, 0); + return (err == PS_OK)? array : 0; +} + +JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_getThreadIntegerRegisterSet0 + (JNIEnv *env, jobject this_obj, jint lwp_id) { + + struct user_regs_struct gregs; + jboolean isCopy; + jlongArray array; + jlong *regs; + int i; + + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + if (get_lwp_regs(ph, lwp_id, &gregs) != true) { + THROW_NEW_DEBUGGER_EXCEPTION_("get_thread_regs failed for a lwp", 0); + } + +#undef NPRGREG +#ifdef i386 +#define NPRGREG sun_jvm_hotspot_debugger_x86_X86ThreadContext_NPRGREG +#endif +#ifdef ia64 +#define NPRGREG IA64_REG_COUNT +#endif +#ifdef amd64 +#define NPRGREG sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_NPRGREG +#endif +#if defined(sparc) || defined(sparcv9) +#define NPRGREG sun_jvm_hotspot_debugger_sparc_SPARCThreadContext_NPRGREG +#endif + + array = (*env)->NewLongArray(env, NPRGREG); + CHECK_EXCEPTION_(0); + regs = (*env)->GetLongArrayElements(env, array, &isCopy); + +#undef REG_INDEX + +#ifdef i386 +#define REG_INDEX(reg) sun_jvm_hotspot_debugger_x86_X86ThreadContext_##reg + + regs[REG_INDEX(GS)] = (uintptr_t) gregs.xgs; + regs[REG_INDEX(FS)] = (uintptr_t) gregs.xfs; + regs[REG_INDEX(ES)] = (uintptr_t) gregs.xes; + regs[REG_INDEX(DS)] = (uintptr_t) gregs.xds; + regs[REG_INDEX(EDI)] = (uintptr_t) gregs.edi; + regs[REG_INDEX(ESI)] = (uintptr_t) gregs.esi; + regs[REG_INDEX(FP)] = (uintptr_t) gregs.ebp; + regs[REG_INDEX(SP)] = (uintptr_t) gregs.esp; + regs[REG_INDEX(EBX)] = (uintptr_t) gregs.ebx; + regs[REG_INDEX(EDX)] = (uintptr_t) gregs.edx; + regs[REG_INDEX(ECX)] = (uintptr_t) gregs.ecx; + regs[REG_INDEX(EAX)] = (uintptr_t) gregs.eax; + regs[REG_INDEX(PC)] = (uintptr_t) gregs.eip; + regs[REG_INDEX(CS)] = (uintptr_t) gregs.xcs; + regs[REG_INDEX(SS)] = (uintptr_t) gregs.xss; + +#endif /* i386 */ + +#if ia64 + regs = (*env)->GetLongArrayElements(env, array, &isCopy); + for (i = 0; i < NPRGREG; i++ ) { + regs[i] = 0xDEADDEAD; + } +#endif /* ia64 */ + +#ifdef amd64 +#define REG_INDEX(reg) sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_##reg + + regs[REG_INDEX(R15)] = gregs.r15; + regs[REG_INDEX(R14)] = gregs.r14; + regs[REG_INDEX(R13)] = gregs.r13; + regs[REG_INDEX(R12)] = gregs.r12; + regs[REG_INDEX(RBP)] = gregs.rbp; + regs[REG_INDEX(RBX)] = gregs.rbx; + regs[REG_INDEX(R11)] = gregs.r11; + regs[REG_INDEX(R10)] = gregs.r10; + regs[REG_INDEX(R9)] = gregs.r9; + regs[REG_INDEX(R8)] = gregs.r8; + regs[REG_INDEX(RAX)] = gregs.rax; + regs[REG_INDEX(RCX)] = gregs.rcx; + regs[REG_INDEX(RDX)] = gregs.rdx; + regs[REG_INDEX(RSI)] = gregs.rsi; + regs[REG_INDEX(RDI)] = gregs.rdi; + regs[REG_INDEX(RIP)] = gregs.rip; + regs[REG_INDEX(CS)] = gregs.cs; + regs[REG_INDEX(RSP)] = gregs.rsp; + regs[REG_INDEX(SS)] = gregs.ss; + regs[REG_INDEX(FSBASE)] = gregs.fs_base; + regs[REG_INDEX(GSBASE)] = gregs.gs_base; + regs[REG_INDEX(DS)] = gregs.ds; + regs[REG_INDEX(ES)] = gregs.es; + regs[REG_INDEX(FS)] = gregs.fs; + regs[REG_INDEX(GS)] = gregs.gs; + +#endif /* amd64 */ + +#if defined(sparc) || defined(sparcv9) + +#define REG_INDEX(reg) sun_jvm_hotspot_debugger_sparc_SPARCThreadContext_##reg + +#ifdef _LP64 + regs[REG_INDEX(R_PSR)] = gregs.tstate; + regs[REG_INDEX(R_PC)] = gregs.tpc; + regs[REG_INDEX(R_nPC)] = gregs.tnpc; + regs[REG_INDEX(R_Y)] = gregs.y; +#else + regs[REG_INDEX(R_PSR)] = gregs.psr; + regs[REG_INDEX(R_PC)] = gregs.pc; + regs[REG_INDEX(R_nPC)] = gregs.npc; + regs[REG_INDEX(R_Y)] = gregs.y; +#endif + regs[REG_INDEX(R_G0)] = 0 ; + regs[REG_INDEX(R_G1)] = gregs.u_regs[0]; + regs[REG_INDEX(R_G2)] = gregs.u_regs[1]; + regs[REG_INDEX(R_G3)] = gregs.u_regs[2]; + regs[REG_INDEX(R_G4)] = gregs.u_regs[3]; + regs[REG_INDEX(R_G5)] = gregs.u_regs[4]; + regs[REG_INDEX(R_G6)] = gregs.u_regs[5]; + regs[REG_INDEX(R_G7)] = gregs.u_regs[6]; + regs[REG_INDEX(R_O0)] = gregs.u_regs[7]; + regs[REG_INDEX(R_O1)] = gregs.u_regs[8]; + regs[REG_INDEX(R_O2)] = gregs.u_regs[ 9]; + regs[REG_INDEX(R_O3)] = gregs.u_regs[10]; + regs[REG_INDEX(R_O4)] = gregs.u_regs[11]; + regs[REG_INDEX(R_O5)] = gregs.u_regs[12]; + regs[REG_INDEX(R_O6)] = gregs.u_regs[13]; + regs[REG_INDEX(R_O7)] = gregs.u_regs[14]; +#endif /* sparc */ + + + (*env)->ReleaseLongArrayElements(env, array, regs, JNI_COMMIT); + return array; +} diff --git a/hotspot/agent/src/os/linux/Makefile b/hotspot/agent/src/os/linux/Makefile new file mode 100644 index 00000000000..e243171bc2e --- /dev/null +++ b/hotspot/agent/src/os/linux/Makefile @@ -0,0 +1,77 @@ +# +# Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +ARCH := $(shell if ([ `uname -m` = "ia64" ]) ; then echo ia64 ; elif ([ `uname -m` = "x86_64" ]) ; then echo amd64; elif ([ `uname -m` = "sparc64" ]) ; then echo sparc; else echo i386 ; fi ) +GCC = gcc + +JAVAH = ${JAVA_HOME}/bin/javah + +SOURCES = salibelf.c \ + symtab.c \ + libproc_impl.c \ + ps_proc.c \ + ps_core.c \ + LinuxDebuggerLocal.c + +INCLUDES = -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux + +OBJS = $(SOURCES:.c=.o) + +LIBS = -lthread_db + +CFLAGS = -c -fPIC -g -D_GNU_SOURCE -D$(ARCH) $(INCLUDES) + +LIBSA = $(ARCH)/libsaproc.so + +all: $(LIBSA) + +LinuxDebuggerLocal.o: LinuxDebuggerLocal.c + $(JAVAH) -jni -classpath ../../../build/classes \ + sun.jvm.hotspot.debugger.x86.X86ThreadContext \ + sun.jvm.hotspot.debugger.sparc.SPARCThreadContext \ + sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext + $(GCC) $(CFLAGS) $< + +.c.obj: + $(GCC) $(CFLAGS) + +ifndef LDNOMAP + LFLAGS_LIBSA = -Xlinker --version-script=mapfile +endif + +$(LIBSA): $(OBJS) mapfile + if [ ! -d $(ARCH) ] ; then mkdir $(ARCH) ; fi + $(GCC) -shared $(LFLAGS_LIBSA) -o $(LIBSA) $(OBJS) $(LIBS) + +test.o: test.c + $(GCC) -c -o test.o -g -D_GNU_SOURCE -D$(ARCH) $(INCLUDES) test.c + +test: test.o + $(GCC) -o test test.o -L$(ARCH) -lsaproc $(LIBS) + +clean: + rm -rf $(LIBSA) + rm -rf $(OBJS) + rmdir $(ARCH) + diff --git a/hotspot/agent/src/os/linux/elfmacros.h b/hotspot/agent/src/os/linux/elfmacros.h new file mode 100644 index 00000000000..6f26819e916 --- /dev/null +++ b/hotspot/agent/src/os/linux/elfmacros.h @@ -0,0 +1,54 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _ELFMACROS_H_ +#define _ELFMACROS_H_ + +#if defined(_LP64) +#define ELF_EHDR Elf64_Ehdr +#define ELF_SHDR Elf64_Shdr +#define ELF_PHDR Elf64_Phdr +#define ELF_SYM Elf64_Sym +#define ELF_NHDR Elf64_Nhdr +#define ELF_DYN Elf64_Dyn +#define ELF_ADDR Elf64_Addr + +#define ELF_ST_TYPE ELF64_ST_TYPE + +#else + +#define ELF_EHDR Elf32_Ehdr +#define ELF_SHDR Elf32_Shdr +#define ELF_PHDR Elf32_Phdr +#define ELF_SYM Elf32_Sym +#define ELF_NHDR Elf32_Nhdr +#define ELF_DYN Elf32_Dyn +#define ELF_ADDR Elf32_Addr + +#define ELF_ST_TYPE ELF32_ST_TYPE + +#endif + + +#endif /* _ELFMACROS_H_ */ diff --git a/hotspot/agent/src/os/linux/libproc.h b/hotspot/agent/src/os/linux/libproc.h new file mode 100644 index 00000000000..cd0dbc2ce4b --- /dev/null +++ b/hotspot/agent/src/os/linux/libproc.h @@ -0,0 +1,142 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _LIBPROC_H_ +#define _LIBPROC_H_ + +#include +#include +#include "proc_service.h" + +#if defined(sparc) || defined(sparcv9) +/* + If _LP64 is defined ptrace.h should be taken from /usr/include/asm-sparc64 + otherwise it should be from /usr/include/asm-sparc + These two files define pt_regs structure differently +*/ +#ifdef _LP64 +#include "asm-sparc64/ptrace.h" +#else +#include "asm-sparc/ptrace.h" +#endif + +#endif //sparc or sparcv9 + +/************************************************************************************ + +0. This is very minimal subset of Solaris libproc just enough for current application. +Please note that the bulk of the functionality is from proc_service interface. This +adds Pgrab__ and some missing stuff. We hide the difference b/w live process and core +file by this interface. + +1. pthread_id unique in both NPTL & LinuxThreads. We store this in +OSThread::_pthread_id in JVM code. + +2. All threads see the same pid when they call getpid() under NPTL. +Threads receive different pid under LinuxThreads. We used to save the result of +::getpid() call in OSThread::_thread_id. This way uniqueness of OSThread::_thread_id +was lost under NPTL. Now, we store the result of ::gettid() call in +OSThread::_thread_id. Because gettid returns actual pid of thread (lwp id), this is +unique again. We therefore use OSThread::_thread_id as unique identifier. + +3. There is a unique LWP id under both thread libraries. libthread_db maps pthread_id +to its underlying lwp_id under both the thread libraries. thread_info.lwp_id stores +lwp_id of the thread. The lwp id is nothing but the actual pid of clone'd processes. But +unfortunately libthread_db does not work very well for core dumps. So, we get pthread_id +only for processes. For core dumps, we don't use libthread_db at all (like gdb). + +4. ptrace operates on this LWP id under both the thread libraries. When we say 'pid' for +ptrace call, we refer to lwp_id of the thread. + +5. for core file, we parse ELF files and read data from them. For processes we use +combination of ptrace and /proc calls. + +*************************************************************************************/ + +#ifdef ia64 +struct user_regs_struct { +/* copied from user.h which doesn't define this in a struct */ + +#define IA64_REG_COUNT (EF_SIZE/8+32) /* integer and fp regs */ +unsigned long regs[IA64_REG_COUNT]; /* integer and fp regs */ +}; +#endif + +#if defined(sparc) || defined(sparcv9) +#define user_regs_struct pt_regs +#endif + +// This C bool type must be int for compatibility with Linux calls and +// it would be a mistake to equivalence it to C++ bool on many platforms + +typedef int bool; +#define true 1 +#define false 0 + +struct ps_prochandle; + +// attach to a process +struct ps_prochandle* Pgrab(pid_t pid); + +// attach to a core dump +struct ps_prochandle* Pgrab_core(const char* execfile, const char* corefile); + +// release a process or core +void Prelease(struct ps_prochandle* ph); + +// functions not directly available in Solaris libproc + +// initialize libproc (call this only once per app) +// pass true to make library verbose +bool init_libproc(bool verbose); + +// get number of threads +int get_num_threads(struct ps_prochandle* ph); + +// get lwp_id of n'th thread +lwpid_t get_lwp_id(struct ps_prochandle* ph, int index); + +// get regs for a given lwp +bool get_lwp_regs(struct ps_prochandle* ph, lwpid_t lid, struct user_regs_struct* regs); + +// get number of shared objects +int get_num_libs(struct ps_prochandle* ph); + +// get name of n'th lib +const char* get_lib_name(struct ps_prochandle* ph, int index); + +// get base of lib +uintptr_t get_lib_base(struct ps_prochandle* ph, int index); + +// returns true if given library is found in lib list +bool find_lib(struct ps_prochandle* ph, const char *lib_name); + +// symbol lookup +uintptr_t lookup_symbol(struct ps_prochandle* ph, const char* object_name, + const char* sym_name); + +// address->nearest symbol lookup. return NULL for no symbol +const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* poffset); + +#endif //__LIBPROC_H_ diff --git a/hotspot/agent/src/os/linux/libproc_impl.c b/hotspot/agent/src/os/linux/libproc_impl.c new file mode 100644 index 00000000000..be00178f22f --- /dev/null +++ b/hotspot/agent/src/os/linux/libproc_impl.c @@ -0,0 +1,434 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +#include +#include +#include +#include +#include +#include +#include "libproc_impl.h" + +static const char* alt_root = NULL; +static int alt_root_len = -1; + +#define SA_ALTROOT "SA_ALTROOT" + +static void init_alt_root() { + if (alt_root_len == -1) { + alt_root = getenv(SA_ALTROOT); + if (alt_root) { + alt_root_len = strlen(alt_root); + } else { + alt_root_len = 0; + } + } +} + +int pathmap_open(const char* name) { + int fd; + char alt_path[PATH_MAX + 1]; + + init_alt_root(); + fd = open(name, O_RDONLY); + if (fd >= 0) { + return fd; + } + + if (alt_root_len > 0) { + strcpy(alt_path, alt_root); + strcat(alt_path, name); + fd = open(alt_path, O_RDONLY); + if (fd >= 0) { + print_debug("path %s substituted for %s\n", alt_path, name); + return fd; + } + + if (strrchr(name, '/')) { + strcpy(alt_path, alt_root); + strcat(alt_path, strrchr(name, '/')); + fd = open(alt_path, O_RDONLY); + if (fd >= 0) { + print_debug("path %s substituted for %s\n", alt_path, name); + return fd; + } + } + } + + return -1; +} + +static bool _libsaproc_debug; + +void print_debug(const char* format,...) { + if (_libsaproc_debug) { + va_list alist; + + va_start(alist, format); + fputs("libsaproc DEBUG: ", stderr); + vfprintf(stderr, format, alist); + va_end(alist); + } +} + +bool is_debug() { + return _libsaproc_debug; +} + +// initialize libproc +bool init_libproc(bool debug) { + // init debug mode + _libsaproc_debug = debug; + + // initialize the thread_db library + if (td_init() != TD_OK) { + print_debug("libthread_db's td_init failed\n"); + return false; + } + + return true; +} + +static void destroy_lib_info(struct ps_prochandle* ph) { + lib_info* lib = ph->libs; + while (lib) { + lib_info *next = lib->next; + if (lib->symtab) { + destroy_symtab(lib->symtab); + } + free(lib); + lib = next; + } +} + +static void destroy_thread_info(struct ps_prochandle* ph) { + thread_info* thr = ph->threads; + while (thr) { + thread_info *next = thr->next; + free(thr); + thr = next; + } +} + +// ps_prochandle cleanup + +// ps_prochandle cleanup +void Prelease(struct ps_prochandle* ph) { + // do the "derived class" clean-up first + ph->ops->release(ph); + destroy_lib_info(ph); + destroy_thread_info(ph); + free(ph); +} + +lib_info* add_lib_info(struct ps_prochandle* ph, const char* libname, uintptr_t base) { + return add_lib_info_fd(ph, libname, -1, base); +} + +lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, uintptr_t base) { + lib_info* newlib; + + if ( (newlib = (lib_info*) calloc(1, sizeof(struct lib_info))) == NULL) { + print_debug("can't allocate memory for lib_info\n"); + return NULL; + } + + strncpy(newlib->name, libname, sizeof(newlib->name)); + newlib->base = base; + + if (fd == -1) { + if ( (newlib->fd = pathmap_open(newlib->name)) < 0) { + print_debug("can't open shared object %s\n", newlib->name); + free(newlib); + return NULL; + } + } else { + newlib->fd = fd; + } + + // check whether we have got an ELF file. /proc//map + // gives out all file mappings and not just shared objects + if (is_elf_file(newlib->fd) == false) { + close(newlib->fd); + free(newlib); + return NULL; + } + + newlib->symtab = build_symtab(newlib->fd); + if (newlib->symtab == NULL) { + print_debug("symbol table build failed for %s\n", newlib->name); + } + + // even if symbol table building fails, we add the lib_info. + // This is because we may need to read from the ELF file for core file + // address read functionality. lookup_symbol checks for NULL symtab. + if (ph->libs) { + ph->lib_tail->next = newlib; + ph->lib_tail = newlib; + } else { + ph->libs = ph->lib_tail = newlib; + } + ph->num_libs++; + + return newlib; +} + +// lookup for a specific symbol +uintptr_t lookup_symbol(struct ps_prochandle* ph, const char* object_name, + const char* sym_name) { + // ignore object_name. search in all libraries + // FIXME: what should we do with object_name?? The library names are obtained + // by parsing /proc//maps, which may not be the same as object_name. + // What we need is a utility to map object_name to real file name, something + // dlopen() does by looking at LD_LIBRARY_PATH and /etc/ld.so.cache. For + // now, we just ignore object_name and do a global search for the symbol. + + lib_info* lib = ph->libs; + while (lib) { + if (lib->symtab) { + uintptr_t res = search_symbol(lib->symtab, lib->base, sym_name, NULL); + if (res) return res; + } + lib = lib->next; + } + + print_debug("lookup failed for symbol '%s' in obj '%s'\n", + sym_name, object_name); + return (uintptr_t) NULL; +} + + +const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* poffset) { + const char* res = NULL; + lib_info* lib = ph->libs; + while (lib) { + if (lib->symtab && addr >= lib->base) { + res = nearest_symbol(lib->symtab, addr - lib->base, poffset); + if (res) return res; + } + lib = lib->next; + } + return NULL; +} + +// add a thread to ps_prochandle +thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id) { + thread_info* newthr; + if ( (newthr = (thread_info*) calloc(1, sizeof(thread_info))) == NULL) { + print_debug("can't allocate memory for thread_info\n"); + return NULL; + } + + // initialize thread info + newthr->pthread_id = pthread_id; + newthr->lwp_id = lwp_id; + + // add new thread to the list + newthr->next = ph->threads; + ph->threads = newthr; + ph->num_threads++; + return newthr; +} + + +// struct used for client data from thread_db callback +struct thread_db_client_data { + struct ps_prochandle* ph; + thread_info_callback callback; +}; + +// callback function for libthread_db +static int thread_db_callback(const td_thrhandle_t *th_p, void *data) { + struct thread_db_client_data* ptr = (struct thread_db_client_data*) data; + td_thrinfo_t ti; + td_err_e err; + + memset(&ti, 0, sizeof(ti)); + err = td_thr_get_info(th_p, &ti); + if (err != TD_OK) { + print_debug("libthread_db : td_thr_get_info failed, can't get thread info\n"); + return err; + } + + print_debug("thread_db : pthread %d (lwp %d)\n", ti.ti_tid, ti.ti_lid); + + if (ptr->callback(ptr->ph, ti.ti_tid, ti.ti_lid) != true) + return TD_ERR; + + return TD_OK; +} + +// read thread_info using libthread_db +bool read_thread_info(struct ps_prochandle* ph, thread_info_callback cb) { + struct thread_db_client_data mydata; + td_thragent_t* thread_agent = NULL; + if (td_ta_new(ph, &thread_agent) != TD_OK) { + print_debug("can't create libthread_db agent\n"); + return false; + } + + mydata.ph = ph; + mydata.callback = cb; + + // we use libthread_db iterator to iterate thru list of threads. + if (td_ta_thr_iter(thread_agent, thread_db_callback, &mydata, + TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, + TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS) != TD_OK) { + td_ta_delete(thread_agent); + return false; + } + + // delete thread agent + td_ta_delete(thread_agent); + return true; +} + + +// get number of threads +int get_num_threads(struct ps_prochandle* ph) { + return ph->num_threads; +} + +// get lwp_id of n'th thread +lwpid_t get_lwp_id(struct ps_prochandle* ph, int index) { + int count = 0; + thread_info* thr = ph->threads; + while (thr) { + if (count == index) { + return thr->lwp_id; + } + count++; + thr = thr->next; + } + return -1; +} + +// get regs for a given lwp +bool get_lwp_regs(struct ps_prochandle* ph, lwpid_t lwp_id, struct user_regs_struct* regs) { + return ph->ops->get_lwp_regs(ph, lwp_id, regs); +} + +// get number of shared objects +int get_num_libs(struct ps_prochandle* ph) { + return ph->num_libs; +} + +// get name of n'th solib +const char* get_lib_name(struct ps_prochandle* ph, int index) { + int count = 0; + lib_info* lib = ph->libs; + while (lib) { + if (count == index) { + return lib->name; + } + count++; + lib = lib->next; + } + return NULL; +} + +// get base address of a lib +uintptr_t get_lib_base(struct ps_prochandle* ph, int index) { + int count = 0; + lib_info* lib = ph->libs; + while (lib) { + if (count == index) { + return lib->base; + } + count++; + lib = lib->next; + } + return (uintptr_t)NULL; +} + +bool find_lib(struct ps_prochandle* ph, const char *lib_name) { + lib_info *p = ph->libs; + while (p) { + if (strcmp(p->name, lib_name) == 0) { + return true; + } + p = p->next; + } + return false; +} + +//-------------------------------------------------------------------------- +// proc service functions + +// get process id +pid_t ps_getpid(struct ps_prochandle *ph) { + return ph->pid; +} + +// ps_pglobal_lookup() looks up the symbol sym_name in the symbol table +// of the load object object_name in the target process identified by ph. +// It returns the symbol's value as an address in the target process in +// *sym_addr. + +ps_err_e ps_pglobal_lookup(struct ps_prochandle *ph, const char *object_name, + const char *sym_name, psaddr_t *sym_addr) { + *sym_addr = (psaddr_t) lookup_symbol(ph, object_name, sym_name); + return (*sym_addr ? PS_OK : PS_NOSYM); +} + +// read "size" bytes info "buf" from address "addr" +ps_err_e ps_pdread(struct ps_prochandle *ph, psaddr_t addr, + void *buf, size_t size) { + return ph->ops->p_pread(ph, (uintptr_t) addr, buf, size)? PS_OK: PS_ERR; +} + +// write "size" bytes of data to debuggee at address "addr" +ps_err_e ps_pdwrite(struct ps_prochandle *ph, psaddr_t addr, + const void *buf, size_t size) { + return ph->ops->p_pwrite(ph, (uintptr_t)addr, buf, size)? PS_OK: PS_ERR; +} + +// ------------------------------------------------------------------------ +// Functions below this point are not yet implemented. They are here only +// to make the linker happy. + +ps_err_e ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lid, const prfpregset_t *fpregs) { + print_debug("ps_lsetfpregs not implemented\n"); + return PS_OK; +} + +ps_err_e ps_lsetregs(struct ps_prochandle *ph, lwpid_t lid, const prgregset_t gregset) { + print_debug("ps_lsetregs not implemented\n"); + return PS_OK; +} + +ps_err_e ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lid, prfpregset_t *fpregs) { + print_debug("ps_lgetfpregs not implemented\n"); + return PS_OK; +} + +ps_err_e ps_lgetregs(struct ps_prochandle *ph, lwpid_t lid, prgregset_t gregset) { + print_debug("ps_lgetfpregs not implemented\n"); + return PS_OK; +} + +// new libthread_db of NPTL seem to require this symbol +ps_err_e ps_get_thread_area() { + print_debug("ps_get_thread_area not implemented\n"); + return PS_OK; +} diff --git a/hotspot/agent/src/os/linux/libproc_impl.h b/hotspot/agent/src/os/linux/libproc_impl.h new file mode 100644 index 00000000000..43fbb59c296 --- /dev/null +++ b/hotspot/agent/src/os/linux/libproc_impl.h @@ -0,0 +1,128 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _LIBPROC_IMPL_H_ +#define _LIBPROC_IMPL_H_ + +#include +#include +#include "libproc.h" +#include "symtab.h" + +// data structures in this file mimic those of Solaris 8.0 - libproc's Pcontrol.h + +#define BUF_SIZE (PATH_MAX + NAME_MAX + 1) + +// list of shared objects +typedef struct lib_info { + char name[BUF_SIZE]; + uintptr_t base; + struct symtab* symtab; + int fd; // file descriptor for lib + struct lib_info* next; +} lib_info; + +// list of threads +typedef struct thread_info { + lwpid_t lwp_id; + pthread_t pthread_id; // not used cores, always -1 + struct user_regs_struct regs; // not for process, core uses for caching regset + struct thread_info* next; +} thread_info; + +// list of virtual memory maps +typedef struct map_info { + int fd; // file descriptor + off_t offset; // file offset of this mapping + uintptr_t vaddr; // starting virtual address + size_t memsz; // size of the mapping + struct map_info* next; +} map_info; + +// vtable for ps_prochandle +typedef struct ps_prochandle_ops { + // "derived class" clean-up + void (*release)(struct ps_prochandle* ph); + // read from debuggee + bool (*p_pread)(struct ps_prochandle *ph, + uintptr_t addr, char *buf, size_t size); + // write into debuggee + bool (*p_pwrite)(struct ps_prochandle *ph, + uintptr_t addr, const char *buf , size_t size); + // get integer regset of a thread + bool (*get_lwp_regs)(struct ps_prochandle* ph, lwpid_t lwp_id, struct user_regs_struct* regs); +} ps_prochandle_ops; + +// the ps_prochandle + +struct core_data { + int core_fd; // file descriptor of core file + int exec_fd; // file descriptor of exec file + int interp_fd; // file descriptor of interpreter (ld-linux.so.2) + // part of the class sharing workaround + int classes_jsa_fd; // file descriptor of class share archive + uintptr_t dynamic_addr; // address of dynamic section of a.out + uintptr_t ld_base_addr; // base address of ld.so + size_t num_maps; // number of maps. + map_info* maps; // maps in a linked list + // part of the class sharing workaround + map_info* class_share_maps;// class share maps in a linked list + map_info** map_array; // sorted (by vaddr) array of map_info pointers +}; + +struct ps_prochandle { + ps_prochandle_ops* ops; // vtable ptr + pid_t pid; + int num_libs; + lib_info* libs; // head of lib list + lib_info* lib_tail; // tail of lib list - to append at the end + int num_threads; + thread_info* threads; // head of thread list + struct core_data* core; // data only used for core dumps, NULL for process +}; + +int pathmap_open(const char* name); + +void print_debug(const char* format,...); +bool is_debug(); + +typedef bool (*thread_info_callback)(struct ps_prochandle* ph, pthread_t pid, lwpid_t lwpid); + +// reads thread info using libthread_db and calls above callback for each thread +bool read_thread_info(struct ps_prochandle* ph, thread_info_callback cb); + +// adds a new shared object to lib list, returns NULL on failure +lib_info* add_lib_info(struct ps_prochandle* ph, const char* libname, uintptr_t base); + +// adds a new shared object to lib list, supply open lib file descriptor as well +lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, + uintptr_t base); + +// adds a new thread to threads list, returns NULL on failure +thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id); + +// a test for ELF signature without using libelf +bool is_elf_file(int fd); + +#endif //_LIBPROC_IMPL_H_ diff --git a/hotspot/agent/src/os/linux/mapfile b/hotspot/agent/src/os/linux/mapfile new file mode 100644 index 00000000000..485e100bfc4 --- /dev/null +++ b/hotspot/agent/src/os/linux/mapfile @@ -0,0 +1,62 @@ +# + +# +# Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + + # native methods of LinuxDebuggerLocal class + Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_init0; + Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_getAddressSize; + Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__I; + Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2; + Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_detach0; + Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_lookupByName0; + Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_lookupByAddress0; + Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_readBytesFromProcess0; + Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_getThreadIntegerRegisterSet0; + + # proc_service.h functions - to be used by libthread_db + ps_getpid; + ps_pglobal_lookup; + ps_pdread; + ps_pdwrite; + ps_lsetfpregs; + ps_lsetregs; + ps_lgetfpregs; + ps_lgetregs; + ps_get_thread_area; + + # used by attach test program + init_libproc; + Pgrab; + Pgrab_core; + Prelease; + + local: + *; +}; diff --git a/hotspot/agent/src/os/linux/proc_service.h b/hotspot/agent/src/os/linux/proc_service.h new file mode 100644 index 00000000000..9cf1315a126 --- /dev/null +++ b/hotspot/agent/src/os/linux/proc_service.h @@ -0,0 +1,76 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _PROC_SERVICE_H_ +#define _PROC_SERVICE_H_ + +#include +#include + +// Linux does not have the proc service library, though it does provide the +// thread_db library which can be used to manipulate threads without having +// to know the details of LinuxThreads or NPTL + +// copied from Solaris "proc_service.h" +typedef enum { + PS_OK, /* generic "call succeeded" */ + PS_ERR, /* generic error */ + PS_BADPID, /* bad process handle */ + PS_BADLID, /* bad lwp identifier */ + PS_BADADDR, /* bad address */ + PS_NOSYM, /* p_lookup() could not find given symbol */ + PS_NOFREGS /* FPU register set not available for given lwp */ +} ps_err_e; + +// ps_getpid() is only defined on Linux to return a thread's process ID +pid_t ps_getpid(struct ps_prochandle *ph); + +// ps_pglobal_lookup() looks up the symbol sym_name in the symbol table +// of the load object object_name in the target process identified by ph. +// It returns the symbol's value as an address in the target process in +// *sym_addr. + +ps_err_e ps_pglobal_lookup(struct ps_prochandle *ph, const char *object_name, + const char *sym_name, psaddr_t *sym_addr); + +// read "size" bytes of data from debuggee at address "addr" +ps_err_e ps_pdread(struct ps_prochandle *ph, psaddr_t addr, + void *buf, size_t size); + +// write "size" bytes of data to debuggee at address "addr" +ps_err_e ps_pdwrite(struct ps_prochandle *ph, psaddr_t addr, + const void *buf, size_t size); + +ps_err_e ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lid, const prfpregset_t *fpregs); + +ps_err_e ps_lsetregs(struct ps_prochandle *ph, lwpid_t lid, const prgregset_t gregset); + +ps_err_e ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lid, prfpregset_t *fpregs); + +ps_err_e ps_lgetregs(struct ps_prochandle *ph, lwpid_t lid, prgregset_t gregset); + +// new libthread_db of NPTL seem to require this symbol +ps_err_e ps_get_thread_area(); + +#endif /* _PROC_SERVICE_H_ */ diff --git a/hotspot/agent/src/os/linux/ps_core.c b/hotspot/agent/src/os/linux/ps_core.c new file mode 100644 index 00000000000..b8890f15fc9 --- /dev/null +++ b/hotspot/agent/src/os/linux/ps_core.c @@ -0,0 +1,1011 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libproc_impl.h" +#include "salibelf.h" + +// This file has the libproc implementation to read core files. +// For live processes, refer to ps_proc.c. Portions of this is adapted +// /modelled after Solaris libproc.so (in particular Pcore.c) + +//---------------------------------------------------------------------- +// ps_prochandle cleanup helper functions + +// close all file descriptors +static void close_elf_files(struct ps_prochandle* ph) { + lib_info* lib = NULL; + + // close core file descriptor + if (ph->core->core_fd >= 0) + close(ph->core->core_fd); + + // close exec file descriptor + if (ph->core->exec_fd >= 0) + close(ph->core->exec_fd); + + // close interp file descriptor + if (ph->core->interp_fd >= 0) + close(ph->core->interp_fd); + + // close class share archive file + if (ph->core->classes_jsa_fd >= 0) + close(ph->core->classes_jsa_fd); + + // close all library file descriptors + lib = ph->libs; + while (lib) { + int fd = lib->fd; + if (fd >= 0 && fd != ph->core->exec_fd) close(fd); + lib = lib->next; + } +} + +// clean all map_info stuff +static void destroy_map_info(struct ps_prochandle* ph) { + map_info* map = ph->core->maps; + while (map) { + map_info* next = map->next; + free(map); + map = next; + } + + if (ph->core->map_array) { + free(ph->core->map_array); + } + + // Part of the class sharing workaround + map = ph->core->class_share_maps; + while (map) { + map_info* next = map->next; + free(map); + map = next; + } +} + +// ps_prochandle operations +static void core_release(struct ps_prochandle* ph) { + if (ph->core) { + close_elf_files(ph); + destroy_map_info(ph); + free(ph->core); + } +} + +static map_info* allocate_init_map(int fd, off_t offset, uintptr_t vaddr, size_t memsz) { + map_info* map; + if ( (map = (map_info*) calloc(1, sizeof(map_info))) == NULL) { + print_debug("can't allocate memory for map_info\n"); + return NULL; + } + + // initialize map + map->fd = fd; + map->offset = offset; + map->vaddr = vaddr; + map->memsz = memsz; + return map; +} + +// add map info with given fd, offset, vaddr and memsz +static map_info* add_map_info(struct ps_prochandle* ph, int fd, off_t offset, + uintptr_t vaddr, size_t memsz) { + map_info* map; + if ((map = allocate_init_map(fd, offset, vaddr, memsz)) == NULL) { + return NULL; + } + + // add this to map list + map->next = ph->core->maps; + ph->core->maps = map; + ph->core->num_maps++; + + return map; +} + +// Part of the class sharing workaround +static map_info* add_class_share_map_info(struct ps_prochandle* ph, off_t offset, + uintptr_t vaddr, size_t memsz) { + map_info* map; + if ((map = allocate_init_map(ph->core->classes_jsa_fd, + offset, vaddr, memsz)) == NULL) { + return NULL; + } + + map->next = ph->core->class_share_maps; + ph->core->class_share_maps = map; +} + +// Return the map_info for the given virtual address. We keep a sorted +// array of pointers in ph->map_array, so we can binary search. +static map_info* core_lookup(struct ps_prochandle *ph, uintptr_t addr) +{ + int mid, lo = 0, hi = ph->core->num_maps - 1; + map_info *mp; + + while (hi - lo > 1) { + mid = (lo + hi) / 2; + if (addr >= ph->core->map_array[mid]->vaddr) + lo = mid; + else + hi = mid; + } + + if (addr < ph->core->map_array[hi]->vaddr) + mp = ph->core->map_array[lo]; + else + mp = ph->core->map_array[hi]; + + if (addr >= mp->vaddr && addr < mp->vaddr + mp->memsz) + return (mp); + + + // Part of the class sharing workaround + // Unfortunately, we have no way of detecting -Xshare state. + // Check out the share maps atlast, if we don't find anywhere. + // This is done this way so to avoid reading share pages + // ahead of other normal maps. For eg. with -Xshare:off we don't + // want to prefer class sharing data to data from core. + mp = ph->core->class_share_maps; + if (mp) { + print_debug("can't locate map_info at 0x%lx, trying class share maps\n", + addr); + } + while (mp) { + if (addr >= mp->vaddr && addr < mp->vaddr + mp->memsz) { + print_debug("located map_info at 0x%lx from class share maps\n", + addr); + return (mp); + } + mp = mp->next; + } + + print_debug("can't locate map_info at 0x%lx\n", addr); + return (NULL); +} + +//--------------------------------------------------------------- +// Part of the class sharing workaround: +// +// With class sharing, pages are mapped from classes[_g].jsa file. +// The read-only class sharing pages are mapped as MAP_SHARED, +// PROT_READ pages. These pages are not dumped into core dump. +// With this workaround, these pages are read from classes[_g].jsa. + +// FIXME: !HACK ALERT! +// The format of sharing achive file header is needed to read shared heap +// file mappings. For now, I am hard coding portion of FileMapHeader here. +// Refer to filemap.hpp. + +// FileMapHeader describes the shared space data in the file to be +// mapped. This structure gets written to a file. It is not a class, +// so that the compilers don't add any compiler-private data to it. + +// Refer to CompactingPermGenGen::n_regions in compactingPermGenGen.hpp +#define NUM_SHARED_MAPS 4 + +// Refer to FileMapInfo::_current_version in filemap.hpp +#define CURRENT_ARCHIVE_VERSION 1 + +struct FileMapHeader { + int _magic; // identify file type. + int _version; // (from enum, above.) + size_t _alignment; // how shared archive should be aligned + + struct space_info { + int _file_offset; // sizeof(this) rounded to vm page size + char* _base; // copy-on-write base address + size_t _capacity; // for validity checking + size_t _used; // for setting space top on read + + // 4991491 NOTICE These are C++ bool's in filemap.hpp and must match up with + // the C type matching the C++ bool type on any given platform. For + // Hotspot on Linux we assume the corresponding C type is char but + // licensees on Linux versions may need to adjust the type of these fields. + char _read_only; // read only space? + char _allow_exec; // executable code in space? + + } _space[NUM_SHARED_MAPS]; // was _space[CompactingPermGenGen::n_regions]; + + // Ignore the rest of the FileMapHeader. We don't need those fields here. +}; + +static bool read_int(struct ps_prochandle* ph, uintptr_t addr, int* pvalue) { + int i; + if (ps_pdread(ph, (psaddr_t) addr, &i, sizeof(i)) == PS_OK) { + *pvalue = i; + return true; + } else { + return false; + } +} + +static bool read_pointer(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* pvalue) { + uintptr_t uip; + if (ps_pdread(ph, (psaddr_t) addr, &uip, sizeof(uip)) == PS_OK) { + *pvalue = uip; + return true; + } else { + return false; + } +} + +// used to read strings from debuggee +static bool read_string(struct ps_prochandle* ph, uintptr_t addr, char* buf, size_t size) { + size_t i = 0; + char c = ' '; + + while (c != '\0') { + if (ps_pdread(ph, (psaddr_t) addr, &c, sizeof(char)) != PS_OK) + return false; + if (i < size - 1) + buf[i] = c; + else // smaller buffer + return false; + i++; addr++; + } + + buf[i] = '\0'; + return true; +} + +#define USE_SHARED_SPACES_SYM "UseSharedSpaces" +// mangled name of Arguments::SharedArchivePath +#define SHARED_ARCHIVE_PATH_SYM "_ZN9Arguments17SharedArchivePathE" + +static bool init_classsharing_workaround(struct ps_prochandle* ph) { + lib_info* lib = ph->libs; + while (lib != NULL) { + // we are iterating over shared objects from the core dump. look for + // libjvm[_g].so. + const char *jvm_name = 0; + if ((jvm_name = strstr(lib->name, "/libjvm.so")) != 0 || + (jvm_name = strstr(lib->name, "/libjvm_g.so")) != 0) { + char classes_jsa[PATH_MAX]; + struct FileMapHeader header; + size_t n = 0; + int fd = -1, m = 0; + uintptr_t base = 0, useSharedSpacesAddr = 0; + uintptr_t sharedArchivePathAddrAddr = 0, sharedArchivePathAddr = 0; + int useSharedSpaces = 0; + map_info* mi = 0; + + memset(classes_jsa, 0, sizeof(classes_jsa)); + jvm_name = lib->name; + useSharedSpacesAddr = lookup_symbol(ph, jvm_name, USE_SHARED_SPACES_SYM); + if (useSharedSpacesAddr == 0) { + print_debug("can't lookup 'UseSharedSpaces' flag\n"); + return false; + } + + if (read_int(ph, useSharedSpacesAddr, &useSharedSpaces) != true) { + print_debug("can't read the value of 'UseSharedSpaces' flag\n"); + return false; + } + + if (useSharedSpaces == 0) { + print_debug("UseSharedSpaces is false, assuming -Xshare:off!\n"); + return true; + } + + sharedArchivePathAddrAddr = lookup_symbol(ph, jvm_name, SHARED_ARCHIVE_PATH_SYM); + if (sharedArchivePathAddrAddr == 0) { + print_debug("can't lookup shared archive path symbol\n"); + return false; + } + + if (read_pointer(ph, sharedArchivePathAddrAddr, &sharedArchivePathAddr) != true) { + print_debug("can't read shared archive path pointer\n"); + return false; + } + + if (read_string(ph, sharedArchivePathAddr, classes_jsa, sizeof(classes_jsa)) != true) { + print_debug("can't read shared archive path value\n"); + return false; + } + + print_debug("looking for %s\n", classes_jsa); + // open the class sharing archive file + fd = pathmap_open(classes_jsa); + if (fd < 0) { + print_debug("can't open %s!\n", classes_jsa); + ph->core->classes_jsa_fd = -1; + return false; + } else { + print_debug("opened %s\n", classes_jsa); + } + + // read FileMapHeader from the file + memset(&header, 0, sizeof(struct FileMapHeader)); + if ((n = read(fd, &header, sizeof(struct FileMapHeader))) + != sizeof(struct FileMapHeader)) { + print_debug("can't read shared archive file map header from %s\n", classes_jsa); + close(fd); + return false; + } + + // check file magic + if (header._magic != 0xf00baba2) { + print_debug("%s has bad shared archive file magic number 0x%x, expecing 0xf00baba2\n", + classes_jsa, header._magic); + close(fd); + return false; + } + + // check version + if (header._version != CURRENT_ARCHIVE_VERSION) { + print_debug("%s has wrong shared archive file version %d, expecting %d\n", + classes_jsa, header._version, CURRENT_ARCHIVE_VERSION); + close(fd); + return false; + } + + ph->core->classes_jsa_fd = fd; + // add read-only maps from classes[_g].jsa to the list of maps + for (m = 0; m < NUM_SHARED_MAPS; m++) { + if (header._space[m]._read_only) { + base = (uintptr_t) header._space[m]._base; + // no need to worry about the fractional pages at-the-end. + // possible fractional pages are handled by core_read_data. + add_class_share_map_info(ph, (off_t) header._space[m]._file_offset, + base, (size_t) header._space[m]._used); + print_debug("added a share archive map at 0x%lx\n", base); + } + } + return true; + } + lib = lib->next; + } + return true; +} + + +//--------------------------------------------------------------------------- +// functions to handle map_info + +// Order mappings based on virtual address. We use this function as the +// callback for sorting the array of map_info pointers. +static int core_cmp_mapping(const void *lhsp, const void *rhsp) +{ + const map_info *lhs = *((const map_info **)lhsp); + const map_info *rhs = *((const map_info **)rhsp); + + if (lhs->vaddr == rhs->vaddr) + return (0); + + return (lhs->vaddr < rhs->vaddr ? -1 : 1); +} + +// we sort map_info by starting virtual address so that we can do +// binary search to read from an address. +static bool sort_map_array(struct ps_prochandle* ph) { + size_t num_maps = ph->core->num_maps; + map_info* map = ph->core->maps; + int i = 0; + + // allocate map_array + map_info** array; + if ( (array = (map_info**) malloc(sizeof(map_info*) * num_maps)) == NULL) { + print_debug("can't allocate memory for map array\n"); + return false; + } + + // add maps to array + while (map) { + array[i] = map; + i++; + map = map->next; + } + + // sort is called twice. If this is second time, clear map array + if (ph->core->map_array) free(ph->core->map_array); + ph->core->map_array = array; + // sort the map_info array by base virtual address. + qsort(ph->core->map_array, ph->core->num_maps, sizeof (map_info*), + core_cmp_mapping); + + // print map + if (is_debug()) { + int j = 0; + print_debug("---- sorted virtual address map ----\n"); + for (j = 0; j < ph->core->num_maps; j++) { + print_debug("base = 0x%lx\tsize = %d\n", ph->core->map_array[j]->vaddr, + ph->core->map_array[j]->memsz); + } + } + + return true; +} + +#ifndef MIN +#define MIN(x, y) (((x) < (y))? (x): (y)) +#endif + +static bool core_read_data(struct ps_prochandle* ph, uintptr_t addr, char *buf, size_t size) { + ssize_t resid = size; + int page_size=sysconf(_SC_PAGE_SIZE); + while (resid != 0) { + map_info *mp = core_lookup(ph, addr); + uintptr_t mapoff; + ssize_t len, rem; + off_t off; + int fd; + + if (mp == NULL) + break; /* No mapping for this address */ + + fd = mp->fd; + mapoff = addr - mp->vaddr; + len = MIN(resid, mp->memsz - mapoff); + off = mp->offset + mapoff; + + if ((len = pread(fd, buf, len, off)) <= 0) + break; + + resid -= len; + addr += len; + buf = (char *)buf + len; + + // mappings always start at page boundary. But, may end in fractional + // page. fill zeros for possible fractional page at the end of a mapping. + rem = mp->memsz % page_size; + if (rem > 0) { + rem = page_size - rem; + len = MIN(resid, rem); + resid -= len; + addr += len; + // we are not assuming 'buf' to be zero initialized. + memset(buf, 0, len); + buf += len; + } + } + + if (resid) { + print_debug("core read failed for %d byte(s) @ 0x%lx (%d more bytes)\n", + size, addr, resid); + return false; + } else { + return true; + } +} + +// null implementation for write +static bool core_write_data(struct ps_prochandle* ph, + uintptr_t addr, const char *buf , size_t size) { + return false; +} + +static bool core_get_lwp_regs(struct ps_prochandle* ph, lwpid_t lwp_id, + struct user_regs_struct* regs) { + // for core we have cached the lwp regs from NOTE section + thread_info* thr = ph->threads; + while (thr) { + if (thr->lwp_id == lwp_id) { + memcpy(regs, &thr->regs, sizeof(struct user_regs_struct)); + return true; + } + thr = thr->next; + } + return false; +} + +static ps_prochandle_ops core_ops = { + release: core_release, + p_pread: core_read_data, + p_pwrite: core_write_data, + get_lwp_regs: core_get_lwp_regs +}; + +// read regs and create thread from NT_PRSTATUS entries from core file +static bool core_handle_prstatus(struct ps_prochandle* ph, const char* buf, size_t nbytes) { + // we have to read prstatus_t from buf + // assert(nbytes == sizeof(prstaus_t), "size mismatch on prstatus_t"); + prstatus_t* prstat = (prstatus_t*) buf; + thread_info* newthr; + print_debug("got integer regset for lwp %d\n", prstat->pr_pid); + // we set pthread_t to -1 for core dump + if((newthr = add_thread_info(ph, (pthread_t) -1, prstat->pr_pid)) == NULL) + return false; + + // copy regs + memcpy(&newthr->regs, prstat->pr_reg, sizeof(struct user_regs_struct)); + + if (is_debug()) { + print_debug("integer regset\n"); +#ifdef i386 + // print the regset + print_debug("\teax = 0x%x\n", newthr->regs.eax); + print_debug("\tebx = 0x%x\n", newthr->regs.ebx); + print_debug("\tecx = 0x%x\n", newthr->regs.ecx); + print_debug("\tedx = 0x%x\n", newthr->regs.edx); + print_debug("\tesp = 0x%x\n", newthr->regs.esp); + print_debug("\tebp = 0x%x\n", newthr->regs.ebp); + print_debug("\tesi = 0x%x\n", newthr->regs.esi); + print_debug("\tedi = 0x%x\n", newthr->regs.edi); + print_debug("\teip = 0x%x\n", newthr->regs.eip); +#endif + +#if defined(amd64) || defined(x86_64) + // print the regset + print_debug("\tr15 = 0x%lx\n", newthr->regs.r15); + print_debug("\tr14 = 0x%lx\n", newthr->regs.r14); + print_debug("\tr13 = 0x%lx\n", newthr->regs.r13); + print_debug("\tr12 = 0x%lx\n", newthr->regs.r12); + print_debug("\trbp = 0x%lx\n", newthr->regs.rbp); + print_debug("\trbx = 0x%lx\n", newthr->regs.rbx); + print_debug("\tr11 = 0x%lx\n", newthr->regs.r11); + print_debug("\tr10 = 0x%lx\n", newthr->regs.r10); + print_debug("\tr9 = 0x%lx\n", newthr->regs.r9); + print_debug("\tr8 = 0x%lx\n", newthr->regs.r8); + print_debug("\trax = 0x%lx\n", newthr->regs.rax); + print_debug("\trcx = 0x%lx\n", newthr->regs.rcx); + print_debug("\trdx = 0x%lx\n", newthr->regs.rdx); + print_debug("\trsi = 0x%lx\n", newthr->regs.rsi); + print_debug("\trdi = 0x%lx\n", newthr->regs.rdi); + print_debug("\torig_rax = 0x%lx\n", newthr->regs.orig_rax); + print_debug("\trip = 0x%lx\n", newthr->regs.rip); + print_debug("\tcs = 0x%lx\n", newthr->regs.cs); + print_debug("\teflags = 0x%lx\n", newthr->regs.eflags); + print_debug("\trsp = 0x%lx\n", newthr->regs.rsp); + print_debug("\tss = 0x%lx\n", newthr->regs.ss); + print_debug("\tfs_base = 0x%lx\n", newthr->regs.fs_base); + print_debug("\tgs_base = 0x%lx\n", newthr->regs.gs_base); + print_debug("\tds = 0x%lx\n", newthr->regs.ds); + print_debug("\tes = 0x%lx\n", newthr->regs.es); + print_debug("\tfs = 0x%lx\n", newthr->regs.fs); + print_debug("\tgs = 0x%lx\n", newthr->regs.gs); +#endif + } + + return true; +} + +#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) + +// read NT_PRSTATUS entries from core NOTE segment +static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) { + char* buf = NULL; + char* p = NULL; + size_t size = note_phdr->p_filesz; + + // we are interested in just prstatus entries. we will ignore the rest. + // Advance the seek pointer to the start of the PT_NOTE data + if (lseek(ph->core->core_fd, note_phdr->p_offset, SEEK_SET) == (off_t)-1) { + print_debug("failed to lseek to PT_NOTE data\n"); + return false; + } + + // Now process the PT_NOTE structures. Each one is preceded by + // an Elf{32/64}_Nhdr structure describing its type and size. + if ( (buf = (char*) malloc(size)) == NULL) { + print_debug("can't allocate memory for reading core notes\n"); + goto err; + } + + // read notes into buffer + if (read(ph->core->core_fd, buf, size) != size) { + print_debug("failed to read notes, core file must have been truncated\n"); + goto err; + } + + p = buf; + while (p < buf + size) { + ELF_NHDR* notep = (ELF_NHDR*) p; + char* descdata = p + sizeof(ELF_NHDR) + ROUNDUP(notep->n_namesz, 4); + print_debug("Note header with n_type = %d and n_descsz = %u\n", + notep->n_type, notep->n_descsz); + + if (notep->n_type == NT_PRSTATUS) { + if (core_handle_prstatus(ph, descdata, notep->n_descsz) != true) + return false; + } + p = descdata + ROUNDUP(notep->n_descsz, 4); + } + + free(buf); + return true; + +err: + if (buf) free(buf); + return false; +} + +// read all segments from core file +static bool read_core_segments(struct ps_prochandle* ph, ELF_EHDR* core_ehdr) { + int i = 0; + ELF_PHDR* phbuf = NULL; + ELF_PHDR* core_php = NULL; + + if ((phbuf = read_program_header_table(ph->core->core_fd, core_ehdr)) == NULL) + return false; + + /* + * Now iterate through the program headers in the core file. + * We're interested in two types of Phdrs: PT_NOTE (which + * contains a set of saved /proc structures), and PT_LOAD (which + * represents a memory mapping from the process's address space). + * + * Difference b/w Solaris PT_NOTE and Linux PT_NOTE: + * + * In Solaris there are two PT_NOTE segments the first PT_NOTE (if present) + * contains /proc structs in the pre-2.6 unstructured /proc format. the last + * PT_NOTE has data in new /proc format. + * + * In Solaris, there is only one pstatus (process status). pstatus contains + * integer register set among other stuff. For each LWP, we have one lwpstatus + * entry that has integer regset for that LWP. + * + * Linux threads are actually 'clone'd processes. To support core analysis + * of "multithreaded" process, Linux creates more than one pstatus (called + * "prstatus") entry in PT_NOTE. Each prstatus entry has integer regset for one + * "thread". Please refer to Linux kernel src file 'fs/binfmt_elf.c', in particular + * function "elf_core_dump". + */ + + for (core_php = phbuf, i = 0; i < core_ehdr->e_phnum; i++) { + switch (core_php->p_type) { + case PT_NOTE: + if (core_handle_note(ph, core_php) != true) goto err; + break; + + case PT_LOAD: { + if (core_php->p_filesz != 0) { + if (add_map_info(ph, ph->core->core_fd, core_php->p_offset, + core_php->p_vaddr, core_php->p_filesz) == NULL) goto err; + } + break; + } + } + + core_php++; + } + + free(phbuf); + return true; +err: + free(phbuf); + return false; +} + +// read segments of a shared object +static bool read_lib_segments(struct ps_prochandle* ph, int lib_fd, ELF_EHDR* lib_ehdr, uintptr_t lib_base) { + int i = 0; + ELF_PHDR* phbuf; + ELF_PHDR* lib_php = NULL; + + if ((phbuf = read_program_header_table(lib_fd, lib_ehdr)) == NULL) + return false; + + // we want to process only PT_LOAD segments that are not writable. + // i.e., text segments. The read/write/exec (data) segments would + // have been already added from core file segments. + for (lib_php = phbuf, i = 0; i < lib_ehdr->e_phnum; i++) { + if ((lib_php->p_type == PT_LOAD) && !(lib_php->p_flags & PF_W) && (lib_php->p_filesz != 0)) { + if (add_map_info(ph, lib_fd, lib_php->p_offset, lib_php->p_vaddr + lib_base, lib_php->p_filesz) == NULL) + goto err; + } + lib_php++; + } + + free(phbuf); + return true; +err: + free(phbuf); + return false; +} + +// process segments from interpreter (ld.so or ld-linux.so) +static bool read_interp_segments(struct ps_prochandle* ph) { + ELF_EHDR interp_ehdr; + + if (read_elf_header(ph->core->interp_fd, &interp_ehdr) != true) { + print_debug("interpreter is not a valid ELF file\n"); + return false; + } + + if (read_lib_segments(ph, ph->core->interp_fd, &interp_ehdr, ph->core->ld_base_addr) != true) { + print_debug("can't read segments of interpreter\n"); + return false; + } + + return true; +} + +// process segments of a a.out +static bool read_exec_segments(struct ps_prochandle* ph, ELF_EHDR* exec_ehdr) { + int i = 0; + ELF_PHDR* phbuf = NULL; + ELF_PHDR* exec_php = NULL; + + if ((phbuf = read_program_header_table(ph->core->exec_fd, exec_ehdr)) == NULL) + return false; + + for (exec_php = phbuf, i = 0; i < exec_ehdr->e_phnum; i++) { + switch (exec_php->p_type) { + + // add mappings for PT_LOAD segments + case PT_LOAD: { + // add only non-writable segments of non-zero filesz + if (!(exec_php->p_flags & PF_W) && exec_php->p_filesz != 0) { + if (add_map_info(ph, ph->core->exec_fd, exec_php->p_offset, exec_php->p_vaddr, exec_php->p_filesz) == NULL) goto err; + } + break; + } + + // read the interpreter and it's segments + case PT_INTERP: { + char interp_name[BUF_SIZE]; + + pread(ph->core->exec_fd, interp_name, MIN(exec_php->p_filesz, BUF_SIZE), exec_php->p_offset); + print_debug("ELF interpreter %s\n", interp_name); + // read interpreter segments as well + if ((ph->core->interp_fd = pathmap_open(interp_name)) < 0) { + print_debug("can't open runtime loader\n"); + goto err; + } + break; + } + + // from PT_DYNAMIC we want to read address of first link_map addr + case PT_DYNAMIC: { + ph->core->dynamic_addr = exec_php->p_vaddr; + print_debug("address of _DYNAMIC is 0x%lx\n", ph->core->dynamic_addr); + break; + } + + } // switch + exec_php++; + } // for + + free(phbuf); + return true; +err: + free(phbuf); + return false; +} + + +#define FIRST_LINK_MAP_OFFSET offsetof(struct r_debug, r_map) +#define LD_BASE_OFFSET offsetof(struct r_debug, r_ldbase) +#define LINK_MAP_ADDR_OFFSET offsetof(struct link_map, l_addr) +#define LINK_MAP_NAME_OFFSET offsetof(struct link_map, l_name) +#define LINK_MAP_NEXT_OFFSET offsetof(struct link_map, l_next) + +// read shared library info from runtime linker's data structures. +// This work is done by librtlb_db in Solaris +static bool read_shared_lib_info(struct ps_prochandle* ph) { + uintptr_t addr = ph->core->dynamic_addr; + uintptr_t debug_base; + uintptr_t first_link_map_addr; + uintptr_t ld_base_addr; + uintptr_t link_map_addr; + uintptr_t lib_base_diff; + uintptr_t lib_base; + uintptr_t lib_name_addr; + char lib_name[BUF_SIZE]; + ELF_DYN dyn; + ELF_EHDR elf_ehdr; + int lib_fd; + + // _DYNAMIC has information of the form + // [tag] [data] [tag] [data] ..... + // Both tag and data are pointer sized. + // We look for dynamic info with DT_DEBUG. This has shared object info. + // refer to struct r_debug in link.h + + dyn.d_tag = DT_NULL; + while (dyn.d_tag != DT_DEBUG) { + if (ps_pdread(ph, (psaddr_t) addr, &dyn, sizeof(ELF_DYN)) != PS_OK) { + print_debug("can't read debug info from _DYNAMIC\n"); + return false; + } + addr += sizeof(ELF_DYN); + } + + // we have got Dyn entry with DT_DEBUG + debug_base = dyn.d_un.d_ptr; + // at debug_base we have struct r_debug. This has first link map in r_map field + if (ps_pdread(ph, (psaddr_t) debug_base + FIRST_LINK_MAP_OFFSET, + &first_link_map_addr, sizeof(uintptr_t)) != PS_OK) { + print_debug("can't read first link map address\n"); + return false; + } + + // read ld_base address from struct r_debug + if (ps_pdread(ph, (psaddr_t) debug_base + LD_BASE_OFFSET, &ld_base_addr, + sizeof(uintptr_t)) != PS_OK) { + print_debug("can't read ld base address\n"); + return false; + } + ph->core->ld_base_addr = ld_base_addr; + + print_debug("interpreter base address is 0x%lx\n", ld_base_addr); + + // now read segments from interp (i.e ld.so or ld-linux.so) + if (read_interp_segments(ph) != true) + return false; + + // after adding interpreter (ld.so) mappings sort again + if (sort_map_array(ph) != true) + return false; + + print_debug("first link map is at 0x%lx\n", first_link_map_addr); + + link_map_addr = first_link_map_addr; + while (link_map_addr != 0) { + // read library base address of the .so. Note that even though calls + // link_map->l_addr as "base address", this is * not * really base virtual + // address of the shared object. This is actually the difference b/w the virtual + // address mentioned in shared object and the actual virtual base where runtime + // linker loaded it. We use "base diff" in read_lib_segments call below. + + if (ps_pdread(ph, (psaddr_t) link_map_addr + LINK_MAP_ADDR_OFFSET, + &lib_base_diff, sizeof(uintptr_t)) != PS_OK) { + print_debug("can't read shared object base address diff\n"); + return false; + } + + // read address of the name + if (ps_pdread(ph, (psaddr_t) link_map_addr + LINK_MAP_NAME_OFFSET, + &lib_name_addr, sizeof(uintptr_t)) != PS_OK) { + print_debug("can't read address of shared object name\n"); + return false; + } + + // read name of the shared object + if (read_string(ph, (uintptr_t) lib_name_addr, lib_name, sizeof(lib_name)) != true) { + print_debug("can't read shared object name\n"); + return false; + } + + if (lib_name[0] != '\0') { + // ignore empty lib names + lib_fd = pathmap_open(lib_name); + + if (lib_fd < 0) { + print_debug("can't open shared object %s\n", lib_name); + // continue with other libraries... + } else { + if (read_elf_header(lib_fd, &elf_ehdr)) { + lib_base = lib_base_diff + find_base_address(lib_fd, &elf_ehdr); + print_debug("reading library %s @ 0x%lx [ 0x%lx ]\n", + lib_name, lib_base, lib_base_diff); + // while adding library mappings we need to use "base difference". + if (! read_lib_segments(ph, lib_fd, &elf_ehdr, lib_base_diff)) { + print_debug("can't read shared object's segments\n"); + close(lib_fd); + return false; + } + add_lib_info_fd(ph, lib_name, lib_fd, lib_base); + // Map info is added for the library (lib_name) so + // we need to re-sort it before calling the p_pdread. + if (sort_map_array(ph) != true) + return false; + } else { + print_debug("can't read ELF header for shared object %s\n", lib_name); + close(lib_fd); + // continue with other libraries... + } + } + } + + // read next link_map address + if (ps_pdread(ph, (psaddr_t) link_map_addr + LINK_MAP_NEXT_OFFSET, + &link_map_addr, sizeof(uintptr_t)) != PS_OK) { + print_debug("can't read next link in link_map\n"); + return false; + } + } + + return true; +} + +// the one and only one exposed stuff from this file +struct ps_prochandle* Pgrab_core(const char* exec_file, const char* core_file) { + ELF_EHDR core_ehdr; + ELF_EHDR exec_ehdr; + ELF_EHDR lib_ehdr; + + struct ps_prochandle* ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle)); + if (ph == NULL) { + print_debug("can't allocate ps_prochandle\n"); + return NULL; + } + + if ((ph->core = (struct core_data*) calloc(1, sizeof(struct core_data))) == NULL) { + free(ph); + print_debug("can't allocate ps_prochandle\n"); + return NULL; + } + + // initialize ph + ph->ops = &core_ops; + ph->core->core_fd = -1; + ph->core->exec_fd = -1; + ph->core->interp_fd = -1; + + // open the core file + if ((ph->core->core_fd = open(core_file, O_RDONLY)) < 0) { + print_debug("can't open core file\n"); + goto err; + } + + // read core file ELF header + if (read_elf_header(ph->core->core_fd, &core_ehdr) != true || core_ehdr.e_type != ET_CORE) { + print_debug("core file is not a valid ELF ET_CORE file\n"); + goto err; + } + + if ((ph->core->exec_fd = open(exec_file, O_RDONLY)) < 0) { + print_debug("can't open executable file\n"); + goto err; + } + + if (read_elf_header(ph->core->exec_fd, &exec_ehdr) != true || exec_ehdr.e_type != ET_EXEC) { + print_debug("executable file is not a valid ELF ET_EXEC file\n"); + goto err; + } + + // process core file segments + if (read_core_segments(ph, &core_ehdr) != true) + goto err; + + // process exec file segments + if (read_exec_segments(ph, &exec_ehdr) != true) + goto err; + + // exec file is also treated like a shared object for symbol search + if (add_lib_info_fd(ph, exec_file, ph->core->exec_fd, + (uintptr_t)0 + find_base_address(ph->core->exec_fd, &exec_ehdr)) == NULL) + goto err; + + // allocate and sort maps into map_array, we need to do this + // here because read_shared_lib_info needs to read from debuggee + // address space + if (sort_map_array(ph) != true) + goto err; + + if (read_shared_lib_info(ph) != true) + goto err; + + // sort again because we have added more mappings from shared objects + if (sort_map_array(ph) != true) + goto err; + + if (init_classsharing_workaround(ph) != true) + goto err; + + return ph; + +err: + Prelease(ph); + return NULL; +} diff --git a/hotspot/agent/src/os/linux/ps_proc.c b/hotspot/agent/src/os/linux/ps_proc.c new file mode 100644 index 00000000000..81ea1fb7aa0 --- /dev/null +++ b/hotspot/agent/src/os/linux/ps_proc.c @@ -0,0 +1,341 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include +#include +#include +#include +#include "libproc_impl.h" + +#if defined(x86_64) && !defined(amd64) +#define amd64 1 +#endif + +#ifndef __WALL +#define __WALL 0x40000000 // Copied from /usr/include/linux/wait.h +#endif + +// This file has the libproc implementation specific to live process +// For core files, refer to ps_core.c + +static inline uintptr_t align(uintptr_t ptr, size_t size) { + return (ptr & ~(size - 1)); +} + +// --------------------------------------------- +// ptrace functions +// --------------------------------------------- + +// read "size" bytes of data from "addr" within the target process. +// unlike the standard ptrace() function, process_read_data() can handle +// unaligned address - alignment check, if required, should be done +// before calling process_read_data. + +static bool process_read_data(struct ps_prochandle* ph, uintptr_t addr, char *buf, size_t size) { + long rslt; + size_t i, words; + uintptr_t end_addr = addr + size; + uintptr_t aligned_addr = align(addr, sizeof(long)); + + if (aligned_addr != addr) { + char *ptr = (char *)&rslt; + errno = 0; + rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0); + if (errno) { + print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr); + return false; + } + for (; aligned_addr != addr; aligned_addr++, ptr++); + for (; ((intptr_t)aligned_addr % sizeof(long)) && aligned_addr < end_addr; + aligned_addr++) + *(buf++) = *(ptr++); + } + + words = (end_addr - aligned_addr) / sizeof(long); + + // assert((intptr_t)aligned_addr % sizeof(long) == 0); + for (i = 0; i < words; i++) { + errno = 0; + rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0); + if (errno) { + print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr); + return false; + } + *(long *)buf = rslt; + buf += sizeof(long); + aligned_addr += sizeof(long); + } + + if (aligned_addr != end_addr) { + char *ptr = (char *)&rslt; + errno = 0; + rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0); + if (errno) { + print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr); + return false; + } + for (; aligned_addr != end_addr; aligned_addr++) + *(buf++) = *(ptr++); + } + return true; +} + +// null implementation for write +static bool process_write_data(struct ps_prochandle* ph, + uintptr_t addr, const char *buf , size_t size) { + return false; +} + +// "user" should be a pointer to a user_regs_struct +static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct user_regs_struct *user) { + // we have already attached to all thread 'pid's, just use ptrace call + // to get regset now. Note that we don't cache regset upfront for processes. +// Linux on x86 and sparc are different. On x86 ptrace(PTRACE_GETREGS, ...) +// uses pointer from 4th argument and ignores 3rd argument. On sparc it uses +// pointer from 3rd argument and ignores 4th argument +#if defined(sparc) || defined(sparcv9) +#define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, addr, data) +#else +#define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, data, addr) +#endif + +#ifdef _LP64 +#ifdef PTRACE_GETREGS64 +#define PTRACE_GETREGS_REQ PTRACE_GETREGS64 +#endif +#else +#if defined(PTRACE_GETREGS) || defined(PT_GETREGS) +#define PTRACE_GETREGS_REQ PTRACE_GETREGS +#endif +#endif /* _LP64 */ + +#ifdef PTRACE_GETREGS_REQ + if (ptrace_getregs(PTRACE_GETREGS_REQ, pid, user, NULL) < 0) { + print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n", pid); + return false; + } + return true; +#else + print_debug("ptrace(PTRACE_GETREGS, ...) not supported\n"); + return false; +#endif + +} + +// attach to a process/thread specified by "pid" +static bool ptrace_attach(pid_t pid) { + if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) { + print_debug("ptrace(PTRACE_ATTACH, ..) failed for %d\n", pid); + return false; + } else { + int ret; + int status; + do { + // Wait for debuggee to stop. + ret = waitpid(pid, &status, 0); + if (ret == -1 && errno == ECHILD) { + // try cloned process. + ret = waitpid(pid, &status, __WALL); + } + if (ret >= 0) { + if (WIFSTOPPED(status)) { + // Debuggee stopped. + return true; + } else { + print_debug("waitpid(): Child process exited/terminated (status = 0x%x)\n", status); + return false; + } + } else { + switch (errno) { + case EINTR: + continue; + break; + case ECHILD: + print_debug("waitpid() failed. Child process pid (%d) does not exist \n", pid); + break; + case EINVAL: + print_debug("waitpid() failed. Invalid options argument.\n"); + break; + default: + print_debug("waitpid() failed. Unexpected error %d\n",errno); + } + return false; + } + } while(true); + } +} + +// ------------------------------------------------------- +// functions for obtaining library information +// ------------------------------------------------------- + +/* + * splits a string _str_ into substrings with delimiter _delim_ by replacing old * delimiters with _new_delim_ (ideally, '\0'). the address of each substring + * is stored in array _ptrs_ as the return value. the maximum capacity of _ptrs_ * array is specified by parameter _n_. + * RETURN VALUE: total number of substrings (always <= _n_) + * NOTE: string _str_ is modified if _delim_!=_new_delim_ + */ +static int split_n_str(char * str, int n, char ** ptrs, char delim, char new_delim) +{ + int i; + for(i = 0; i < n; i++) ptrs[i] = NULL; + if (str == NULL || n < 1 ) return 0; + + i = 0; + + // skipping leading blanks + while(*str&&*str==delim) str++; + + while(*str&&ipid); + fp = fopen(fname, "r"); + if (fp == NULL) { + print_debug("can't open /proc/%d/maps file\n", ph->pid); + return false; + } + + while(fgets_no_cr(buf, 256, fp)){ + char * word[6]; + int nwords = split_n_str(buf, 6, word, ' ', '\0'); + if (nwords > 5 && find_lib(ph, word[5]) == false) { + intptr_t base; + lib_info* lib; + sscanf(word[0], "%lx", &base); + if ((lib = add_lib_info(ph, word[5], (uintptr_t)base)) == NULL) + continue; // ignore, add_lib_info prints error + + // we don't need to keep the library open, symtab is already + // built. Only for core dump we need to keep the fd open. + close(lib->fd); + lib->fd = -1; + } + } + fclose(fp); + return true; +} + +// detach a given pid +static bool ptrace_detach(pid_t pid) { + if (pid && ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) { + print_debug("ptrace(PTRACE_DETACH, ..) failed for %d\n", pid); + return false; + } else { + return true; + } +} + +// detach all pids of a ps_prochandle +static void detach_all_pids(struct ps_prochandle* ph) { + thread_info* thr = ph->threads; + while (thr) { + ptrace_detach(thr->lwp_id); + thr = thr->next; + } +} + +static void process_cleanup(struct ps_prochandle* ph) { + detach_all_pids(ph); +} + +static ps_prochandle_ops process_ops = { + release: process_cleanup, + p_pread: process_read_data, + p_pwrite: process_write_data, + get_lwp_regs: process_get_lwp_regs +}; + +// attach to the process. One and only one exposed stuff +struct ps_prochandle* Pgrab(pid_t pid) { + struct ps_prochandle* ph = NULL; + thread_info* thr = NULL; + + if ( (ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle))) == NULL) { + print_debug("can't allocate memory for ps_prochandle\n"); + return NULL; + } + + if (ptrace_attach(pid) != true) { + free(ph); + return NULL; + } + + // initialize ps_prochandle + ph->pid = pid; + + // initialize vtable + ph->ops = &process_ops; + + // read library info and symbol tables, must do this before attaching threads, + // as the symbols in the pthread library will be used to figure out + // the list of threads within the same process. + read_lib_info(ph); + + // read thread info + read_thread_info(ph, add_new_thread); + + // attach to the threads + thr = ph->threads; + while (thr) { + // don't attach to the main thread again + if (ph->pid != thr->lwp_id && ptrace_attach(thr->lwp_id) != true) { + // even if one attach fails, we get return NULL + Prelease(ph); + return NULL; + } + thr = thr->next; + } + return ph; +} diff --git a/hotspot/agent/src/os/linux/salibelf.c b/hotspot/agent/src/os/linux/salibelf.c new file mode 100644 index 00000000000..bcc7092094e --- /dev/null +++ b/hotspot/agent/src/os/linux/salibelf.c @@ -0,0 +1,125 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "salibelf.h" +#include +#include + +extern void print_debug(const char*,...); + +// ELF file parsing helpers. Note that we do *not* use libelf here. +int read_elf_header(int fd, ELF_EHDR* ehdr) { + if (pread(fd, ehdr, sizeof (ELF_EHDR), 0) != sizeof (ELF_EHDR) || + memcmp(&ehdr->e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0 || + ehdr->e_version != EV_CURRENT) { + return 0; + } + return 1; +} + +bool is_elf_file(int fd) { + ELF_EHDR ehdr; + return read_elf_header(fd, &ehdr); +} + +// read program header table of an ELF file +ELF_PHDR* read_program_header_table(int fd, ELF_EHDR* hdr) { + ELF_PHDR* phbuf = 0; + // allocate memory for program header table + size_t nbytes = hdr->e_phnum * hdr->e_phentsize; + + if ((phbuf = (ELF_PHDR*) malloc(nbytes)) == NULL) { + print_debug("can't allocate memory for reading program header table\n"); + return NULL; + } + + if (pread(fd, phbuf, nbytes, hdr->e_phoff) != nbytes) { + print_debug("ELF file is truncated! can't read program header table\n"); + free(phbuf); + return NULL; + } + + return phbuf; +} + +// read section header table of an ELF file +ELF_SHDR* read_section_header_table(int fd, ELF_EHDR* hdr) { + ELF_SHDR* shbuf = 0; + // allocate memory for section header table + size_t nbytes = hdr->e_shnum * hdr->e_shentsize; + + if ((shbuf = (ELF_SHDR*) malloc(nbytes)) == NULL) { + print_debug("can't allocate memory for reading section header table\n"); + return NULL; + } + + if (pread(fd, shbuf, nbytes, hdr->e_shoff) != nbytes) { + print_debug("ELF file is truncated! can't read section header table\n"); + free(shbuf); + return NULL; + } + + return shbuf; +} + +// read a particular section's data +void* read_section_data(int fd, ELF_EHDR* ehdr, ELF_SHDR* shdr) { + void *buf = NULL; + if (shdr->sh_type == SHT_NOBITS || shdr->sh_size == 0) { + return buf; + } + if ((buf = calloc(shdr->sh_size, 1)) == NULL) { + print_debug("can't allocate memory for reading section data\n"); + return NULL; + } + if (pread(fd, buf, shdr->sh_size, shdr->sh_offset) != shdr->sh_size) { + free(buf); + print_debug("section data read failed\n"); + return NULL; + } + return buf; +} + +uintptr_t find_base_address(int fd, ELF_EHDR* ehdr) { + uintptr_t baseaddr = (uintptr_t)-1; + int cnt; + ELF_PHDR *phbuf, *phdr; + + // read program header table + if ((phbuf = read_program_header_table(fd, ehdr)) == NULL) { + goto quit; + } + + // the base address of a shared object is the lowest vaddr of + // its loadable segments (PT_LOAD) + for (phdr = phbuf, cnt = 0; cnt < ehdr->e_phnum; cnt++, phdr++) { + if (phdr->p_type == PT_LOAD && phdr->p_vaddr < baseaddr) { + baseaddr = phdr->p_vaddr; + } + } + +quit: + if (phbuf) free(phbuf); + return baseaddr; +} diff --git a/hotspot/agent/src/os/linux/salibelf.h b/hotspot/agent/src/os/linux/salibelf.h new file mode 100644 index 00000000000..4f688195912 --- /dev/null +++ b/hotspot/agent/src/os/linux/salibelf.h @@ -0,0 +1,52 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _SALIBELF_H_ +#define _SALIBELF_H_ + +#include +#include "elfmacros.h" +#include "libproc_impl.h" + +// read ELF file header. +int read_elf_header(int fd, ELF_EHDR* ehdr); + +// is given file descriptor corresponds to an ELF file? +bool is_elf_file(int fd); + +// read program header table of an ELF file. caller has to +// free the result pointer after use. NULL on failure. +ELF_PHDR* read_program_header_table(int fd, ELF_EHDR* hdr); + +// read section header table of an ELF file. caller has to +// free the result pointer after use. NULL on failure. +ELF_SHDR* read_section_header_table(int fd, ELF_EHDR* hdr); + +// read a particular section's data. caller has to free the +// result pointer after use. NULL on failure. +void* read_section_data(int fd, ELF_EHDR* ehdr, ELF_SHDR* shdr); + +// find the base address at which the library wants to load itself +uintptr_t find_base_address(int fd, ELF_EHDR* ehdr); +#endif /* _SALIBELF_H_ */ diff --git a/hotspot/agent/src/os/linux/symtab.c b/hotspot/agent/src/os/linux/symtab.c new file mode 100644 index 00000000000..ec3b99ff326 --- /dev/null +++ b/hotspot/agent/src/os/linux/symtab.c @@ -0,0 +1,228 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include +#include +#include +#include +#include "symtab.h" +#include "salibelf.h" + + +// ---------------------------------------------------- +// functions for symbol lookups +// ---------------------------------------------------- + +struct elf_section { + ELF_SHDR *c_shdr; + void *c_data; +}; + +struct elf_symbol { + char *name; + uintptr_t offset; + uintptr_t size; +}; + +typedef struct symtab { + char *strs; + size_t num_symbols; + struct elf_symbol *symbols; + struct hsearch_data *hash_table; +} symtab_t; + +// read symbol table from given fd. +struct symtab* build_symtab(int fd) { + ELF_EHDR ehdr; + char *names = NULL; + struct symtab* symtab = NULL; + + // Reading of elf header + struct elf_section *scn_cache = NULL; + int cnt = 0; + ELF_SHDR* shbuf = NULL; + ELF_SHDR* cursct = NULL; + ELF_PHDR* phbuf = NULL; + ELF_PHDR* phdr = NULL; + + uintptr_t baseaddr = (uintptr_t)-1; + + lseek(fd, (off_t)0L, SEEK_SET); + if (! read_elf_header(fd, &ehdr)) { + // not an elf + return NULL; + } + + // read ELF header + if ((shbuf = read_section_header_table(fd, &ehdr)) == NULL) { + goto quit; + } + + baseaddr = find_base_address(fd, &ehdr); + + scn_cache = (struct elf_section *) + calloc(ehdr.e_shnum * sizeof(struct elf_section), 1); + if (scn_cache == NULL) { + goto quit; + } + + for (cursct = shbuf, cnt = 0; cnt < ehdr.e_shnum; cnt++) { + scn_cache[cnt].c_shdr = cursct; + if (cursct->sh_type == SHT_SYMTAB || cursct->sh_type == SHT_STRTAB) { + if ( (scn_cache[cnt].c_data = read_section_data(fd, &ehdr, cursct)) == NULL) { + goto quit; + } + } + cursct++; + } + + for (cnt = 1; cnt < ehdr.e_shnum; cnt++) { + ELF_SHDR *shdr = scn_cache[cnt].c_shdr; + + if (shdr->sh_type == SHT_SYMTAB) { + ELF_SYM *syms; + int j, n, rslt; + size_t size; + + // FIXME: there could be multiple data buffers associated with the + // same ELF section. Here we can handle only one buffer. See man page + // for elf_getdata on Solaris. + + // guarantee(symtab == NULL, "multiple symtab"); + symtab = (struct symtab*)calloc(1, sizeof(struct symtab)); + if (symtab == NULL) { + goto quit; + } + // the symbol table + syms = (ELF_SYM *)scn_cache[cnt].c_data; + + // number of symbols + n = shdr->sh_size / shdr->sh_entsize; + + // create hash table, we use hcreate_r, hsearch_r and hdestroy_r to + // manipulate the hash table. + symtab->hash_table = (struct hsearch_data*) calloc(1, sizeof(struct hsearch_data)); + rslt = hcreate_r(n, symtab->hash_table); + // guarantee(rslt, "unexpected failure: hcreate_r"); + + // shdr->sh_link points to the section that contains the actual strings + // for symbol names. the st_name field in ELF_SYM is just the + // string table index. we make a copy of the string table so the + // strings will not be destroyed by elf_end. + size = scn_cache[shdr->sh_link].c_shdr->sh_size; + symtab->strs = (char *)malloc(size); + memcpy(symtab->strs, scn_cache[shdr->sh_link].c_data, size); + + // allocate memory for storing symbol offset and size; + symtab->num_symbols = n; + symtab->symbols = (struct elf_symbol *)calloc(n , sizeof(struct elf_symbol)); + + // copy symbols info our symtab and enter them info the hash table + for (j = 0; j < n; j++, syms++) { + ENTRY item, *ret; + char *sym_name = symtab->strs + syms->st_name; + + // skip non-object and non-function symbols + int st_type = ELF_ST_TYPE(syms->st_info); + if ( st_type != STT_FUNC && st_type != STT_OBJECT) + continue; + // skip empty strings and undefined symbols + if (*sym_name == '\0' || syms->st_shndx == SHN_UNDEF) continue; + + symtab->symbols[j].name = sym_name; + symtab->symbols[j].offset = syms->st_value - baseaddr; + symtab->symbols[j].size = syms->st_size; + + item.key = sym_name; + item.data = (void *)&(symtab->symbols[j]); + + hsearch_r(item, ENTER, &ret, symtab->hash_table); + } + } + } + +quit: + if (shbuf) free(shbuf); + if (phbuf) free(phbuf); + if (scn_cache) { + for (cnt = 0; cnt < ehdr.e_shnum; cnt++) { + if (scn_cache[cnt].c_data != NULL) { + free(scn_cache[cnt].c_data); + } + } + free(scn_cache); + } + return symtab; +} + +void destroy_symtab(struct symtab* symtab) { + if (!symtab) return; + if (symtab->strs) free(symtab->strs); + if (symtab->symbols) free(symtab->symbols); + if (symtab->hash_table) { + hdestroy_r(symtab->hash_table); + free(symtab->hash_table); + } + free(symtab); +} + +uintptr_t search_symbol(struct symtab* symtab, uintptr_t base, + const char *sym_name, int *sym_size) { + ENTRY item; + ENTRY* ret = NULL; + + // library does not have symbol table + if (!symtab || !symtab->hash_table) + return (uintptr_t)NULL; + + item.key = (char*) strdup(sym_name); + hsearch_r(item, FIND, &ret, symtab->hash_table); + if (ret) { + struct elf_symbol * sym = (struct elf_symbol *)(ret->data); + uintptr_t rslt = (uintptr_t) ((char*)base + sym->offset); + if (sym_size) *sym_size = sym->size; + free(item.key); + return rslt; + } + +quit: + free(item.key); + return (uintptr_t) NULL; +} + +const char* nearest_symbol(struct symtab* symtab, uintptr_t offset, + uintptr_t* poffset) { + int n = 0; + if (!symtab) return NULL; + for (; n < symtab->num_symbols; n++) { + struct elf_symbol* sym = &(symtab->symbols[n]); + if (sym->name != NULL && + offset >= sym->offset && offset < sym->offset + sym->size) { + if (poffset) *poffset = (offset - sym->offset); + return sym->name; + } + } + return NULL; +} diff --git a/hotspot/agent/src/os/linux/symtab.h b/hotspot/agent/src/os/linux/symtab.h new file mode 100644 index 00000000000..371fc7fb166 --- /dev/null +++ b/hotspot/agent/src/os/linux/symtab.h @@ -0,0 +1,50 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _SYMTAB_H_ +#define _SYMTAB_H_ + +#include + +// interface to manage ELF symbol tables + +struct symtab; + +// build symbol table for a given ELF file descriptor +struct symtab* build_symtab(int fd); + +// destroy the symbol table +void destroy_symtab(struct symtab* symtab); + +// search for symbol in the given symbol table. Adds offset +// to the base uintptr_t supplied. Returns NULL if not found. +uintptr_t search_symbol(struct symtab* symtab, uintptr_t base, + const char *sym_name, int *sym_size); + +// look for nearest symbol for a given offset (not address - base +// subtraction done by caller +const char* nearest_symbol(struct symtab* symtab, uintptr_t offset, + uintptr_t* poffset); + +#endif /*_SYMTAB_H_*/ diff --git a/hotspot/agent/src/os/linux/test.c b/hotspot/agent/src/os/linux/test.c new file mode 100644 index 00000000000..2327f2fc8ba --- /dev/null +++ b/hotspot/agent/src/os/linux/test.c @@ -0,0 +1,59 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include +#include "libproc.h" + +int main(int argc, char** argv) { + struct ps_prochandle* ph; + + init_libproc(true); + switch (argc) { + case 2: { + // process + ph = Pgrab(atoi(argv[1])); + break; + } + + case 3: { + // core + ph = Pgrab_core(argv[1], argv[2]); + break; + } + + default: { + printf("usage %s or %s \n"); + return 1; + } + } + + if (ph) { + Prelease(ph); + return 0; + } else { + printf("can't connect to debuggee\n"); + return 1; + } +} diff --git a/hotspot/agent/src/os/solaris/Makefile b/hotspot/agent/src/os/solaris/Makefile new file mode 100644 index 00000000000..e9ba9c1f093 --- /dev/null +++ b/hotspot/agent/src/os/solaris/Makefile @@ -0,0 +1,32 @@ +# +# Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + + +all: + cd dbx; $(MAKE) all + cd proc; $(MAKE) all + +clean: + cd dbx; $(MAKE) clean + cd proc; $(MAKE) clean diff --git a/hotspot/agent/src/os/solaris/dbx/Makefile b/hotspot/agent/src/os/solaris/dbx/Makefile new file mode 100644 index 00000000000..3ccda499cd1 --- /dev/null +++ b/hotspot/agent/src/os/solaris/dbx/Makefile @@ -0,0 +1,91 @@ +# +# Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + + +# Targets are: +# 32bit: Build the 32 bit version in ./32bit +# 64bit: Build the 64 bit version in ./64bit +# helloWorld: Build the helloWorld test program +# all: Build all of the above. This is the default. +# +# NOTE: This makefile uses IOBuf.cpp, IOBuf.hpp, Buffer.cpp, and +# Buffer.hpp from the src/os/win32/agent directory. + +.PHONY: 32bit 64bit + +ARCH_ORIG = $(shell uname -p) + +# C++ := /java/devtools/$(ARCH_ORIG)/SUNWspro/SC6.1/bin/CC + +C++ := CC +RM := /usr/bin/rm +MKDIRS := /usr/bin/mkdir -p + + +WIN32_DIR := ../../win32 +ARCH := $(subst i386,i486,$(ARCH_ORIG)) +# INCLUDES := -I/net/sparcworks.eng/export/set/sparcworks5/dbx_62_intg/dev/src/dbx -I$(WIN32_DIR) +INCLUDES := -I. -I$(WIN32_DIR) +CFLAGS_32bit := -xarch=v8 +CFLAGS_64bit := -xarch=v9 +CFLAGS := -PIC -xO3 $(INCLUDES) +LIBS := -lsocket -lnsl -lrtld_db +LDFLAGS := -G + +ifneq "$(ARCH)" "i486" + CFLAGS += $(CFLAGS_$(VERSION)) + LDFLAGS += $(CFLAGS_$(VERSION)) +endif + +# We use IOBuf.hpp, IOBuf.cpp, Buffer.hpp, and Buffer.cpp from the win32 dir. +vpath %.cpp .:$(WIN32_DIR) +vpath %.hpp .:$(WIN32_DIR) + +OBJS = $(VERSION)/svc_agent_dbx.o $(VERSION)/IOBuf.o $(VERSION)/Buffer.o + + + +# The default is to make both 32 bit and 64 bit versions. +all:: 32bit 64bit + +32bit 64bit:: + $(MKDIRS) $@ + $(MAKE) $@/libsvc_agent_dbx.so helloWorld VERSION=$@ + +$(VERSION)/IOBuf.o: IOBuf.hpp +$(VERSION)/Buffer.o: Buffer.hpp +$(VERSION)/svc_agent_dbx.o: svc_agent_dbx.hpp + +$(VERSION)/%.o: %.cpp + $(C++) $(CFLAGS) -c $< -o $@ + +$(VERSION)/libsvc_agent_dbx.so:: $(OBJS) + $(C++) $(LDFLAGS) -o $(VERSION)/libsvc_agent_dbx.so $(OBJS) $(LIBS) + +# Would be nice to move this into a shared directory +helloWorld:: helloWorld.cpp + $(C++) -g $< -o $@ + +clean:: + $(RM) -rf 32bit 64bit *.o helloWorld diff --git a/hotspot/agent/src/os/solaris/dbx/README b/hotspot/agent/src/os/solaris/dbx/README new file mode 100644 index 00000000000..e043f7e1642 --- /dev/null +++ b/hotspot/agent/src/os/solaris/dbx/README @@ -0,0 +1,9 @@ +shell_impl.h +proc_service_2.h + +The above files are captured from the dbx build environment. +Rather then use a -I that points to stuff in .eng domain that +may not be accessible in other domains these files are just +copied here so local builds in other domains will work. +These files rarely change so the fact that we might have to +strobe in new ones on rare occasions is no big deal. diff --git a/hotspot/agent/src/os/solaris/dbx/README-commands.txt b/hotspot/agent/src/os/solaris/dbx/README-commands.txt new file mode 100644 index 00000000000..4bc22d20d93 --- /dev/null +++ b/hotspot/agent/src/os/solaris/dbx/README-commands.txt @@ -0,0 +1,82 @@ +This import module uses a largely text-based protocol, except for +certain bulk data transfer operations. All text is in single-byte +US-ASCII. + +Commands understood: + +address_size ::= + + Returns 32 if attached to 32-bit process, 64 if 64-bit. + +peek_fail_fast ::= + + Indicates whether "peek" requests should "fail fast"; that is, if + any of the addresses in the requested range are unmapped, report + the entire range as unmapped. This is substantially faster than + the alternative, which is to read the entire range byte-by-byte. + However, it should only be used when it is guaranteed by the + client application that peeks come from at most one page. The + default is that peek_fast_fail is not enabled. + +peek
::= + B + [ []...]... + + NOTE that the binary portion of this message is prefixed by the + uppercase US-ASCII letter 'B', allowing easier synchronization by + clients. There is no data between the 'B' and the rest of the + message. + + May only be called once attached. Reads the address space of the + target process starting at the given address (see below for format + specifications) and extending the given number of bytes. Whether + the read succeeded is indicated by a single byte containing a 1 or + 0 (success or failure). If successful, the return result is given + in a sequence of ranges. _len_, the length of each range, is + indicated by a 32-bit unsigned integer transmitted with big-endian + byte ordering (i.e., most significant byte first). _isMapped_ + indicates whether the range is mapped or unmapped in the target + process's address space, and will contain the value 1 or 0 for + mapped or unmapped, respectively. If the range is mapped, + _isMapped_ is followed by _data_, containing the raw binary data + for the range. The sum of all ranges' lengths is guaranteed to be + equivalent to the number of bytes requested. + +poke
B[]... ::= + + NOTE that the binary portion of this message is prefixed by the + uppercase US-ASCII letter 'B', allowing easier synchronization by + clients. There is no data between the 'B' and the rest of the + message. + + Writes the given data to the target process starting at the given + address. Returns 1 on success, 0 on failure (i.e., one or more of + target addresses were unmapped). + +mapped
::= + + Returns 1 if entire address range [address...address + int arg) is + mapped in target process's address space, 0 if not + +lookup ::=
+ + First symbol is object name; second is symbol to be looked up. + Looks up symbol in target process's symbol table and returns + address. Returns NULL (0x0) if symbol is not found. + +thr_gregs ::= + + Fetch the "general" (integer) register set for the given thread. + Returned as a series of hexidecimal values. NOTE: the meaning of + the return value is architecture-dependent. In general it is the + contents of the prgregset_t. + +exit ::= + + Exits the serviceability agent dbx module, returning control to + the dbx prompt. + +// Data formats and example values: +
::= 0x12345678[9ABCDEF0] /* up to 64-bit hex value */ + ::= 5 /* up to 32-bit integer number; no leading sign */ + ::= 1 /* ASCII '0' or '1' */ diff --git a/hotspot/agent/src/os/solaris/dbx/helloWorld.cpp b/hotspot/agent/src/os/solaris/dbx/helloWorld.cpp new file mode 100644 index 00000000000..e332a7a1e1b --- /dev/null +++ b/hotspot/agent/src/os/solaris/dbx/helloWorld.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include + +extern "C" { + const char* helloWorldString = "Hello, world!"; + // Do not change these values without changing TestDebugger.java as well + // FIXME: should make these jbyte, jshort, etc... + volatile int8_t testByte = 132; + volatile int16_t testShort = 27890; + volatile int32_t testInt = 1020304050; + volatile int64_t testLong = 102030405060708090LL; + volatile float testFloat = 35.4F; + volatile double testDouble = 1.23456789; + + volatile int helloWorldTrigger = 0; +} + +int +main(int, char**) { + while (1) { + while (helloWorldTrigger == 0) { + } + + fprintf(stderr, "%s\n", helloWorldString); + fprintf(stderr, "testByte=%d\n", testByte); + fprintf(stderr, "testShort=%d\n", testShort); + fprintf(stderr, "testInt=%d\n", testInt); + fprintf(stderr, "testLong=%d\n", testLong); + fprintf(stderr, "testFloat=%d\n", testFloat); + fprintf(stderr, "testDouble=%d\n", testDouble); + + while (helloWorldTrigger != 0) { + } + } +} diff --git a/hotspot/agent/src/os/solaris/dbx/proc_service_2.h b/hotspot/agent/src/os/solaris/dbx/proc_service_2.h new file mode 100644 index 00000000000..8e370c006f8 --- /dev/null +++ b/hotspot/agent/src/os/solaris/dbx/proc_service_2.h @@ -0,0 +1,172 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _PROC_SERVICE_2_H +#define _PROC_SERVICE_2_H + +/* + * Types, function definitions for the provider of services beyond + * proc_service. This interface will be used by import modules like + * BAT/prex, NEO debugger etc. + */ + +/* + CCR info + + Version history: + + 1.0 - Initial CCR release + + 1.1 - Changes for GLUE/neo. + New entry points ps_svnt_generic() and ps_svc_generic() + - New entry point ps_getpid() + + Release information for automatic CCR updates: + BEGIN RELEASE NOTES: (signifies what gets put into CCR release notes) + 1.2 - Changes to support Solaris 2.7 + + END RELEASE NOTES: (signifies what gets put into CCR release notes) + + Following is used for CCR version number: + +#define CCR_PROC_SERVICE_2_VERSION 1.2 + +*/ + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ps_loadobj { + int objfd; /* fd of the load object or executable + * -1 implies its not available. + * This file decriptor is live only during the + * particular call to ps_iter_f(). If you + * need it beyond that you need to dup() it. + */ + psaddr_t + text_base; /* address where text of loadobj was mapped */ + psaddr_t + data_base; /* address where data of loadobj was mapped */ + const char *objname; /* loadobj name */ +}; + +typedef int ps_iter_f(const struct ps_prochandle *, const struct ps_loadobj *, + void *cd); + +/* + * Returns the ps_prochandle for the current process under focus. Returns + * NULL if there is none. + */ + +const struct ps_prochandle * +ps_get_prochandle(void); + +/* + * Returns the ps_prochandle for the current process(allows core files to + * be specified) under focus. Returns NULL if there is none. + */ +const struct ps_prochandle * +ps_get_prochandle2(int cores_too); + +/* + * Returns the pid of the process referred to by the ps_prochandle. + * + * 0 is returned in case the ps_prochandle is not valid or refers to dead + * process. + * + */ +pid_t +ps_getpid(const struct ps_prochandle *); + +/* + * Iteration function that iterates over all load objects *and the + * executable* + * + * If the callback routine returns: + * 0 - continue processing link objects + * non zero - stop calling the callback function + * + */ + +ps_err_e +ps_loadobj_iter(const struct ps_prochandle *, ps_iter_f *, void *clnt_data); + +/* + * Address => function name mapping + * + * Given an address, returns a pointer to the function's + * linker name (null terminated). + */ + +ps_err_e +ps_find_fun_name(const struct ps_prochandle *, psaddr_t addr, + const char **name); + +/* + * Interface to LD_PRELOAD. LD_PRELOAD given library across the + * program 'exec'. + * + */ + +/* + * Append/Prepend the 'lib' (has to be library name as understood by LD_PRELOAD) + * to the LD_PRELOAD variable setting to be used by the debugee + * Returns a cookie (in id). + */ +ps_err_e +ps_ld_preload_append(const char *lib, int *id); +ps_err_e +ps_ld_preload_prepend(const char *lib, int *id); + +/* + * Remove the library associated with 'id' from the LD_PRELOAD setting. + * + */ +ps_err_e +ps_ld_preload_remove(int id); + +#ifdef __cplusplus +} +#endif + +/* + * The following are C++ only interfaces + */ +#ifdef __cplusplus + +/* + * classes ServiceDbx and ServantDbx and defined in "gp_dbx_svc.h" which is + * accessed via CCR + */ +extern class ServantDbx *ps_svnt_generic(); +extern class ServiceDbx *ps_svc_generic(); + +#endif + +#endif /* _PROC_SERVICE_2_H */ diff --git a/hotspot/agent/src/os/solaris/dbx/shell_imp.h b/hotspot/agent/src/os/solaris/dbx/shell_imp.h new file mode 100644 index 00000000000..c6af9eaf1e4 --- /dev/null +++ b/hotspot/agent/src/os/solaris/dbx/shell_imp.h @@ -0,0 +1,164 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef SHELL_IMP_H +#define SHELL_IMP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + CCR info + + Vesrion history: + + 1.0 - Initial CCR release + + Release information for automatic CCR updates: + + BEGIN RELEASE NOTES: (signifies what gets put into CCR release notes) + 1.1 + - Entry points for va_list style msgs; new shell_imp_vmsg() + and shell_imp_verrmsg() + - shell_imp_env_checker() is now shell_imp_var_checker(). + Also the var_checker callback gets passed interp. + 1.2 - interposition framework (used by jdbx) + - access to input FILE pointer. + + END RELEASE NOTES: (signifies what gets put into CCR release notes) + +Following is used as a CCR version number: +#define CCR_SHELL_IMP_VERSION 1.1 +*/ + +#include + +#define SHELL_IMP_MAJOR 1 +#define SHELL_IMP_MINOR 2 +#define SHELL_IMP_FLAG_GLOB 0x1 +#define SHELL_IMP_FLAG_ARGQ 0x2 + +typedef void *shell_imp_interp_t; +typedef void *shell_imp_command_t; +typedef int shell_imp_fun_t(shell_imp_interp_t, int, char **, void *); + +int +shell_imp_init( + int, /* major version number */ + int, /* minor version number */ + shell_imp_interp_t, /* interpreter */ + int, /* argc */ + char *[] /* argv */ +); + +int +shell_imp_fini(shell_imp_interp_t); + +shell_imp_command_t +shell_imp_define_command(char *, /* command name e.g. "tnf" */ + shell_imp_fun_t *, /* callback function */ + int, /* SHELL_IMP_FLAG_* bit vector */ + void *, /* client_data Passed as last arg to + /* callback function */ + char * /* help message, e.g. */ + /* "enable the specified tnf probes" */ + ); + +int +shell_imp_undefine_command(shell_imp_command_t); + +int +shell_imp_var_checker(shell_imp_interp_t, + const char *, /* var name */ + int (*)(shell_imp_interp_t, const char*) /* env checker */ + ); + +int +shell_imp_execute(shell_imp_interp_t, const char *); + +const char * +shell_imp_get_var(shell_imp_interp_t, const char *); + +void +shell_imp_msg(shell_imp_interp_t, const char *, ...); + +void +shell_imp_errmsg(shell_imp_interp_t, const char *, ...); + +void +shell_imp_vmsg(shell_imp_interp_t, const char *, va_list); + +void +shell_imp_verrmsg(shell_imp_interp_t, const char *, va_list); + + + +/* + * Stuff added for 1.2 + */ + +struct shell_imp_interposition_info_t { + shell_imp_fun_t * + new_func; + void * new_client_data; + shell_imp_fun_t * + original_func; + void * original_client_data; + int original_flags; +}; + +typedef int shell_imp_dispatcher_t(shell_imp_interp_t, int, char **, + shell_imp_interposition_info_t *); + +shell_imp_command_t +shell_imp_interpose(char *name, + shell_imp_fun_t *new_func, + int flags, + void *client_data, + char * description, + shell_imp_dispatcher_t *); + +int shell_imp_uninterpose(shell_imp_command_t); + +int +shell_imp_dispatch_interposition(shell_imp_interp_t, + shell_imp_interposition_info_t *, + int argc, char *argv[]); + +int +shell_imp_dispatch_original(shell_imp_interp_t, + shell_imp_interposition_info_t *, + int argc, char *argv[]); + +FILE * +shell_imp_cur_input(shell_imp_interp_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.cpp b/hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.cpp new file mode 100644 index 00000000000..8f8efde5d24 --- /dev/null +++ b/hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.cpp @@ -0,0 +1,1068 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This is the implementation of a very simple dbx import module which +// handles requests from the VM which come in over a socket. The +// higher-level Java wrapper for dbx starts the debugger, attaches to +// the process, imports this command, and runs it. After that, the SA +// writes commands to this agent via its own private communications +// channel. The intent is to move away from the text-based front-end +// completely in the near future (no more calling "debug" by printing +// text to dbx's stdin). + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "proc_service_2.h" +#include "svc_agent_dbx.hpp" + +static ServiceabilityAgentDbxModule* module = NULL; +#define NEEDS_CLEANUP + +// Useful for debugging +#define VERBOSE_DEBUGGING + +#ifdef VERBOSE_DEBUGGING +# define debug_only(x) x +#else +# define debug_only(x) +#endif + +// For profiling +//#define PROFILING + +#ifdef PROFILING +#define PROFILE_COUNT 200 +static Timer scanTimer; +static Timer workTimer; +static Timer writeTimer; +static int numRequests = 0; +#endif /* PROFILING */ + +const char* ServiceabilityAgentDbxModule::CMD_ADDRESS_SIZE = "address_size"; +const char* ServiceabilityAgentDbxModule::CMD_PEEK_FAIL_FAST = "peek_fail_fast"; +const char* ServiceabilityAgentDbxModule::CMD_PEEK = "peek"; +const char* ServiceabilityAgentDbxModule::CMD_POKE = "poke"; +const char* ServiceabilityAgentDbxModule::CMD_MAPPED = "mapped"; +const char* ServiceabilityAgentDbxModule::CMD_LOOKUP = "lookup"; +const char* ServiceabilityAgentDbxModule::CMD_THR_GREGS = "thr_gregs"; +const char* ServiceabilityAgentDbxModule::CMD_EXIT = "exit"; + +// The initialization routines must not have C++ name mangling +extern "C" { + +/** This is the initialization routine called by dbx upon importing of + this module. Returns 0 upon successful initialization, -1 upon + failure. */ +int shell_imp_init(int major, int minor, + shell_imp_interp_t interp, int argc, char *argv[]) +{ + // Ensure shell interpreter data structure is laid out the way we + // expect + if (major != SHELL_IMP_MAJOR) { + debug_only(fprintf(stderr, "Serviceability agent: unexpected value for SHELL_IMP_MAJOR (got %d, expected %d)\n", major, SHELL_IMP_MAJOR);) + return -1; + } + if (minor < SHELL_IMP_MINOR) { + debug_only(fprintf(stderr, "Serviceability agent: unexpected value for SHELL_IMP_MINOR (got %d, expected >= %d)\n", minor, SHELL_IMP_MINOR);) + return -1; + } + + if (module != NULL) { + debug_only(fprintf(stderr, "Serviceability agent: module appears to already be initialized (should not happen)\n");) + // Already initialized. Should not happen. + return -1; + } + + module = new ServiceabilityAgentDbxModule(major, minor, interp, argc, argv); + if (!module->install()) { + debug_only(fprintf(stderr, "Serviceability agent: error installing import module\n");) + delete module; + module = NULL; + return -1; + } + + // Installation was successful. Next step will be for the user to + // enter the appropriate command on the command line, which will + // make the SA's dbx module wait for commands to come in over the + // socket. + return 0; +} + +/** This is the routine called by dbx upon unloading of this module. + Returns 0 upon success, -1 upon failure. */ +int +shell_imp_fini(shell_imp_interp_t) +{ + if (module == NULL) { + return -1; + } + + bool res = module->uninstall(); + delete module; + module = NULL; + if (!res) { + return -1; + } + return 0; +} + +} // extern "C" + +/** This is the routine which is called by the dbx shell when the user + requests the serviceability agent module to run. This delegates to + ServiceabilityAgentDbxModule::run. This routine's signature must + match that of shell_imp_fun_t. */ +extern "C" { +static int +svc_agent_run(shell_imp_interp_t, int, char **, void *) { + if (module == NULL) { + return -1; + } + + module->run(); + return 0; +} +} + +/* + * Implementation of ServiceabilityAgentDbxModule class + */ + +// NOTE: we need to forward declare the special "ps_get_prochandle2" +// function which allows examination of core files as well. It isn't +// currently in proc_service_2.h. Note also that it has name mangling +// because it isn't declared extern "C". +//const struct ps_prochandle *ps_get_prochandle2(int cores_too); + +ServiceabilityAgentDbxModule::ServiceabilityAgentDbxModule(int, int, shell_imp_interp_t interp, + int argc, char *argv[]) + :myComm(32768, 131072) +{ + _interp = interp; + _argc = argc; + _argv = argv; + _tdb_agent = NULL; + peek_fail_fast = false; + libThreadName = NULL; +} + +ServiceabilityAgentDbxModule::~ServiceabilityAgentDbxModule() { + if (_command != NULL) { + uninstall(); + } +} + +char* +readCStringFromProcess(psaddr_t addr) { + char c; + int num = 0; + ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); + + // Search for null terminator + do { + if (ps_pread(cur_proc, addr + num, &c, 1) != PS_OK) { + return NULL; + } + ++num; + } while (c != 0); + + // Allocate string + char* res = new char[num]; + if (ps_pread(cur_proc, addr, res, num) != PS_OK) { + delete[] res; + return NULL; + } + return res; +} + +int +findLibThreadCB(const rd_loadobj_t* lo, void* data) { + ServiceabilityAgentDbxModule* module = (ServiceabilityAgentDbxModule*) data; + char* name = readCStringFromProcess(lo->rl_nameaddr); + if (strstr(name, "libthread.so") != NULL) { + module->libThreadName = name; + return 0; + } else { + delete[] name; + return 1; + } +} + +bool +ServiceabilityAgentDbxModule::install() { + // NOTE interdependency between here and Java side wrapper + // FIXME: casts of string literal to char * to match prototype + _command = shell_imp_define_command((char *) "svc_agent_run", + &svc_agent_run, + 0, + NULL, + (char *) "Run the serviceability agent's dbx module.\n" + "This routine causes the module to listen on a socket for requests.\n" + "It does not return until the Java-side code tells it to exit, at\n" + "which point control is returned to the dbx shell."); + if (_command == NULL) { + debug_only(fprintf(stderr, "Serviceability agent: Failed to install svc_agent_run command\n")); + return false; + } + + // This is fairly painful. Since dbx doesn't currently load + // libthread_db with RTLD_GLOBAL, we can't just use RTLD_DEFAULT for + // the argument to dlsym. Instead, we have to use rtld_db to search + // through the loaded objects in the target process for libthread.so and + + // Try rtld_db + if (rd_init(RD_VERSION) != RD_OK) { + debug_only(fprintf(stderr, "Serviceability agent: Unable to init rtld_db\n")); + return false; + } + + rd_agent_t* rda = rd_new((struct ps_prochandle*) ps_get_prochandle2(1)); + if (rda == NULL) { + debug_only(fprintf(stderr, "Serviceability agent: Unable to allocate rtld_db agent\n")); + return false; + } + + if (rd_loadobj_iter(rda, (rl_iter_f*) findLibThreadCB, this) != RD_OK) { + debug_only(fprintf(stderr, "Serviceability agent: Loadobject iteration failed\n")); + return false; + } + + if (libThreadName == NULL) { + debug_only(fprintf(stderr, "Serviceability agent: Failed to find pathname to libthread.so in target process\n")); + return false; + } + + // Find and open libthread_db.so + char* slash = strrchr(libThreadName, '/'); + if (slash == NULL) { + debug_only(fprintf(stderr, "Serviceability agent: can't parse path to libthread.so \"%s\"\n")); + return false; + } + + int slashPos = slash - libThreadName; + char* buf = new char[slashPos + strlen("libthread_db.so") + 20]; // slop + if (buf == NULL) { + debug_only(fprintf(stderr, "Serviceability agent: error allocating libthread_db.so pathname\n")); + return false; + } + strncpy(buf, libThreadName, slashPos + 1); + + // Check dbx's data model; use sparcv9/ subdirectory if 64-bit and + // if target process is 32-bit + if ((sizeof(void*) == 8) && + (strstr(libThreadName, "sparcv9") == NULL)) { + strcpy(buf + slashPos + 1, "sparcv9/"); + slashPos += strlen("sparcv9/"); + } + + strcpy(buf + slashPos + 1, "libthread_db.so"); + + libThreadDB = dlopen(buf, RTLD_LAZY); + void* tmpDB = libThreadDB; + if (libThreadDB == NULL) { + debug_only(fprintf(stderr, "Serviceability agent: Warning: unable to find libthread_db.so at \"%s\"\n", buf)); + // Would like to handle this case as well. Maybe dbx has a better + // idea of where libthread_db.so lies. If the problem with dbx + // loading libthread_db without RTLD_GLOBAL specified ever gets + // fixed, we could run this code all the time. + tmpDB = RTLD_DEFAULT; + } + + delete[] buf; + + // Initialize access to libthread_db + td_init_fn = (td_init_fn_t*) dlsym(tmpDB, "td_init"); + td_ta_new_fn = (td_ta_new_fn_t*) dlsym(tmpDB, "td_ta_new"); + td_ta_delete_fn = (td_ta_delete_fn_t*) dlsym(tmpDB, "td_ta_delete"); + td_ta_map_id2thr_fn = (td_ta_map_id2thr_fn_t*) dlsym(tmpDB, "td_ta_map_id2thr"); + td_thr_getgregs_fn = (td_thr_getgregs_fn_t*) dlsym(tmpDB, "td_thr_getgregs"); + + if (td_init_fn == NULL || + td_ta_new_fn == NULL || + td_ta_delete_fn == NULL || + td_ta_map_id2thr_fn == NULL || + td_thr_getgregs_fn == NULL) { + debug_only(fprintf(stderr, "Serviceability agent: Failed to find one or more libthread_db symbols:\n")); + debug_only(if (td_init_fn == NULL) fprintf(stderr, " td_init\n")); + debug_only(if (td_ta_new_fn == NULL) fprintf(stderr, " td_ta_new\n")); + debug_only(if (td_ta_delete_fn == NULL) fprintf(stderr, " td_ta_delete\n")); + debug_only(if (td_ta_map_id2thr_fn == NULL) fprintf(stderr, " td_ta_map_id2thr\n")); + debug_only(if (td_thr_getgregs_fn == NULL) fprintf(stderr, " td_thr_getgregs\n")); + return false; + } + + if ((*td_init_fn)() != TD_OK) { + debug_only(fprintf(stderr, "Serviceability agent: Failed to initialize libthread_db\n")); + return false; + } + + return true; +} + +bool +ServiceabilityAgentDbxModule::uninstall() { + if (_command == NULL) { + return false; + } + + if (libThreadDB != NULL) { + dlclose(libThreadDB); + libThreadDB = NULL; + } + + int res = shell_imp_undefine_command(_command); + + if (res != 0) { + return false; + } + + return true; +} + +bool +ServiceabilityAgentDbxModule::run() { + // This is where most of the work gets done. + // The command processor loop looks like the following: + // - create listening socket + // - accept a connection (only one for now) + // - while that connection is open and the "exit" command has not + // been received: + // - read command + // - if it's the exit command, cleanup and return + // - otherwise, process command and write result + + int listening_socket = socket(AF_INET, SOCK_STREAM, 0); + if (listening_socket < 0) { + return false; + } + + // Set the SO_REUSEADDR property on the listening socket. This + // prevents problems with calls to bind() to the same port failing + // after this process exits. This seems to work on all platforms. + int reuse_address = 1; + if (setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, + (char *)&reuse_address, sizeof(reuse_address)) < 0) { + close(listening_socket); + return false; + } + + sockaddr_in server_address; + // Build the server address. We can bind the listening socket to the + // INADDR_ANY internet address. + memset((char*)&server_address, 0, sizeof(server_address)); + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = (unsigned long)htonl(INADDR_ANY); + server_address.sin_port = htons((short)PORT); + + // Bind socket to port + if (bind(listening_socket, (sockaddr*) &server_address, + sizeof(server_address)) < 0) { + close(listening_socket); + return false; + } + + // Arbitrarily chosen backlog of 5 (shouldn't matter since we expect + // at most one connection) + if (listen(listening_socket, 5) < 0) { + close(listening_socket); + return false; + } + + // OK, now ready to wait for a data connection. This call to + // accept() will block. + struct sockaddr_in client_address; + int address_len = sizeof(client_address); + int client_socket = accept(listening_socket, (sockaddr*) &client_address, + &address_len); + // Close listening socket regardless of whether accept() succeeded. + // (FIXME: this may be annoying, especially during debugging, but I + // really feel that robustness and multiple connections should be + // handled higher up, e.g., at the Java level -- multiple clients + // could conceivably connect to the SA via RMI, and that would be a + // more robust solution than implementing multiple connections at + // this level) + NEEDS_CLEANUP; + + // NOTE: the call to shutdown() usually fails, so don't panic if this happens + shutdown(listening_socket, 2); + + if (close(listening_socket) < 0) { + debug_only(fprintf(stderr, "Serviceability agent: Error closing listening socket\n")); + return false; + } + + if (client_socket < 0) { + debug_only(fprintf(stderr, "Serviceability agent: Failed to open client socket\n")); + // No more cleanup necessary + return false; + } + + // Attempt to disable TCP buffering on this socket. We send small + // amounts of data back and forth and don't want buffering. + int buffer_val = 1; + if (setsockopt(client_socket, IPPROTO_IP, TCP_NODELAY, (char *) &buffer_val, sizeof(buffer_val)) < 0) { + debug_only(fprintf(stderr, "Serviceability agent: Failed to set TCP_NODELAY option on client socket\n")); + cleanup(client_socket); + return false; + } + + // OK, we have the data socket through which we will communicate + // with the Java side. Wait for commands or until reading or writing + // caused an error. + + bool should_continue = true; + + myComm.setSocket(client_socket); + +#ifdef PROFILING + scanTimer.reset(); + workTimer.reset(); + writeTimer.reset(); +#endif + + // Allocate a new thread agent for libthread_db + if ((*td_ta_new_fn)((ps_prochandle*) ps_get_prochandle2(1), &_tdb_agent) != + TD_OK) { + debug_only(fprintf(stderr, "Serviceability agent: Failed to allocate thread agent\n")); + cleanup(client_socket); + return false; + } + + do { + // Decided to use text to communicate between these processes. + // Probably will make debugging easier -- could telnet in if + // necessary. Will make scanning harder, but probably doesn't + // matter. + + // Why not just do what workshop does and parse dbx's console? + // Probably could do that, but at least this way we are in control + // of the text format on both ends. + + // FIXME: should have some way of synchronizing these commands + // between the C and Java sources. + + NEEDS_CLEANUP; + + // Do a blocking read of a line from the socket. + char *input_buffer = myComm.readLine(); + if (input_buffer == NULL) { + debug_only(fprintf(stderr, "Serviceability agent: error during read: errno = %d\n", errno)); + debug_only(perror("Serviceability agent")); + // Error occurred during read. + // FIXME: should guard against SIGPIPE + cleanup(client_socket); + return false; + } + + // OK, now ready to scan. See README-commands.txt for syntax + // descriptions. + + bool res = false; + if (!strncmp(input_buffer, CMD_ADDRESS_SIZE, strlen(CMD_ADDRESS_SIZE))) { + res = handleAddressSize(input_buffer + strlen(CMD_ADDRESS_SIZE)); + } else if (!strncmp(input_buffer, CMD_PEEK_FAIL_FAST, strlen(CMD_PEEK_FAIL_FAST))) { + res = handlePeekFailFast(input_buffer + strlen(CMD_PEEK_FAIL_FAST)); + } else if (!strncmp(input_buffer, CMD_PEEK, strlen(CMD_PEEK))) { + res = handlePeek(input_buffer + strlen(CMD_PEEK)); + } else if (!strncmp(input_buffer, CMD_POKE, strlen(CMD_POKE))) { + res = handlePoke(input_buffer + strlen(CMD_POKE)); + } else if (!strncmp(input_buffer, CMD_MAPPED, strlen(CMD_MAPPED))) { + res = handleMapped(input_buffer + strlen(CMD_MAPPED)); + } else if (!strncmp(input_buffer, CMD_LOOKUP, strlen(CMD_LOOKUP))) { + res = handleLookup(input_buffer + strlen(CMD_LOOKUP)); + } else if (!strncmp(input_buffer, CMD_THR_GREGS, strlen(CMD_THR_GREGS))) { + res = handleThrGRegs(input_buffer + strlen(CMD_THR_GREGS)); + } else if (!strncmp(input_buffer, CMD_EXIT, strlen(CMD_EXIT))) { + should_continue = false; + } + + if (should_continue) { + if (!res) { + cleanup(client_socket); + return false; + } + } + +#ifdef PROFILING + if (++numRequests == PROFILE_COUNT) { + fprintf(stderr, "%d requests: %d ms scanning, %d ms work, %d ms writing\n", + PROFILE_COUNT, scanTimer.total(), workTimer.total(), writeTimer.total()); + fflush(stderr); + scanTimer.reset(); + workTimer.reset(); + writeTimer.reset(); + numRequests = 0; + } +#endif + + } while (should_continue); + + // Successful exit + cleanup(client_socket); + return true; +} + +void +ServiceabilityAgentDbxModule::cleanup(int client_socket) { + shutdown(client_socket, 2); + close(client_socket); + if (_tdb_agent != NULL) { + (*td_ta_delete_fn)(_tdb_agent); + } +} + +bool +ServiceabilityAgentDbxModule::handleAddressSize(char* data) { + int data_model; + ps_err_e result = ps_pdmodel((ps_prochandle*) ps_get_prochandle2(1), + &data_model); + if (result != PS_OK) { + myComm.writeString("0"); + myComm.flush(); + return false; + } + + int val; + switch (data_model) { + case PR_MODEL_ILP32: + val = 32; + break; + case PR_MODEL_LP64: + val = 64; + break; + default: + val = 0; + break; + } + + if (!myComm.writeInt(val)) { + return false; + } + if (!myComm.writeEOL()) { + return false; + } + return myComm.flush(); +} + +bool +ServiceabilityAgentDbxModule::handlePeekFailFast(char* data) { + unsigned int val; + if (!scanUnsignedInt(&data, &val)) { + return false; + } + peek_fail_fast = (val ? true : false); + return true; +} + +bool +ServiceabilityAgentDbxModule::handlePeek(char* data) { + // Scan hex address, return false if failed + psaddr_t addr; +#ifdef PROFILING + scanTimer.start(); +#endif /* PROFILING */ + if (!scanAddress(&data, &addr)) { + return false; + } + unsigned int num; + if (!scanUnsignedInt(&data, &num)) { + return false; + } + if (num == 0) { +#ifdef PROFILING + writeTimer.start(); +#endif /* PROFILING */ + myComm.writeBinChar('B'); + myComm.writeBinChar(1); + myComm.writeBinUnsignedInt(0); + myComm.writeBinChar(0); +#ifdef PROFILING + writeTimer.stop(); +#endif /* PROFILING */ + return true; + } +#ifdef PROFILING + scanTimer.stop(); + workTimer.start(); +#endif /* PROFILING */ + char* buf = new char[num]; + ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); + ps_err_e result = ps_pread(cur_proc, addr, buf, num); + if (result == PS_OK) { + // Fast case; entire read succeeded. +#ifdef PROFILING + workTimer.stop(); + writeTimer.start(); +#endif /* PROFILING */ + myComm.writeBinChar('B'); + myComm.writeBinChar(1); + myComm.writeBinUnsignedInt(num); + myComm.writeBinChar(1); + myComm.writeBinBuf(buf, num); +#ifdef PROFILING + writeTimer.stop(); +#endif /* PROFILING */ + } else { +#ifdef PROFILING + workTimer.stop(); +#endif /* PROFILING */ + + if (peek_fail_fast) { +#ifdef PROFILING + writeTimer.start(); +#endif /* PROFILING */ + // Fail fast + myComm.writeBinChar('B'); + myComm.writeBinChar(1); + myComm.writeBinUnsignedInt(num); + myComm.writeBinChar(0); +#ifdef PROFILING + writeTimer.stop(); +#endif /* PROFILING */ + } else { + // Slow case: try to read one byte at a time + // FIXME: need better way of handling this, a la VirtualQuery + + unsigned int strideLen = 0; + int bufIdx = 0; + bool lastByteMapped = (ps_pread(cur_proc, addr, buf, 1) == PS_OK ? true : false); + +#ifdef PROFILING + writeTimer.start(); +#endif /* PROFILING */ + myComm.writeBinChar('B'); + myComm.writeBinChar(1); +#ifdef PROFILING + writeTimer.stop(); +#endif /* PROFILING */ + + for (int i = 0; i < num; ++i, ++addr) { +#ifdef PROFILING + workTimer.start(); +#endif /* PROFILING */ + result = ps_pread(cur_proc, addr, &buf[bufIdx], 1); +#ifdef PROFILING + workTimer.stop(); +#endif /* PROFILING */ + bool tmpMapped = (result == PS_OK ? true : false); +#ifdef PROFILING + writeTimer.start(); +#endif /* PROFILING */ + if (tmpMapped != lastByteMapped) { + // State change. Write the length of the last stride. + myComm.writeBinUnsignedInt(strideLen); + if (lastByteMapped) { + // Stop gathering data. Write the data of the last stride. + myComm.writeBinChar(1); + myComm.writeBinBuf(buf, strideLen); + bufIdx = 0; + } else { + // Start gathering data to write. + myComm.writeBinChar(0); + } + strideLen = 0; + lastByteMapped = tmpMapped; + } +#ifdef PROFILING + writeTimer.stop(); +#endif /* PROFILING */ + if (lastByteMapped) { + ++bufIdx; + } + ++strideLen; + } + + // Write last stride (must be at least one byte long by definition) +#ifdef PROFILING + writeTimer.start(); +#endif /* PROFILING */ + myComm.writeBinUnsignedInt(strideLen); + if (lastByteMapped) { + myComm.writeBinChar(1); + myComm.writeBinBuf(buf, strideLen); + } else { + myComm.writeBinChar(0); + } +#ifdef PROFILING + writeTimer.stop(); +#endif /* PROFILING */ + } + } + delete[] buf; + myComm.flush(); + return true; +} + +bool +ServiceabilityAgentDbxModule::handlePoke(char* data) { + // FIXME: not yet implemented + NEEDS_CLEANUP; + bool res = myComm.writeBoolAsInt(false); + myComm.flush(); + return res; +} + +bool +ServiceabilityAgentDbxModule::handleMapped(char* data) { + // Scan address + psaddr_t addr; + if (!scanAddress(&data, &addr)) { + return false; + } + unsigned int num; + if (!scanUnsignedInt(&data, &num)) { + return false; + } + unsigned char val; + ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); + char* buf = new char[num]; + if (ps_pread(cur_proc, addr, buf, num) == PS_OK) { + myComm.writeBoolAsInt(true); + } else { + myComm.writeBoolAsInt(false); + } + delete[] buf; + myComm.writeEOL(); + myComm.flush(); + return true; +} + +extern "C" +int loadobj_iterator(const rd_loadobj_t* loadobj, void *) { + if (loadobj != NULL) { + fprintf(stderr, "loadobj_iterator: visited loadobj \"%p\"\n", (void*) loadobj->rl_nameaddr); + return 1; + } + + fprintf(stderr, "loadobj_iterator: NULL loadobj\n"); + return 0; +} + +bool +ServiceabilityAgentDbxModule::handleLookup(char* data) { + // Debugging: iterate over loadobjs + /* + rd_agent_t* rld_agent = rd_new((ps_prochandle*) ps_get_prochandle2(1)); + rd_loadobj_iter(rld_agent, &loadobj_iterator, NULL); + rd_delete(rld_agent); + */ + +#ifdef PROFILING + scanTimer.start(); +#endif /* PROFILING */ + + char* object_name = scanSymbol(&data); + if (object_name == NULL) { + return false; + } + char* symbol_name = scanSymbol(&data); + if (symbol_name == NULL) { + delete[] object_name; + return false; + } + +#ifdef PROFILING + scanTimer.stop(); + workTimer.start(); +#endif /* PROFILING */ + + ps_sym_t sym; + // FIXME: check return values from write routines + ps_prochandle* process = (ps_prochandle*) ps_get_prochandle2(1); + ps_err_e lookup_res = ps_pglobal_sym(process, + object_name, symbol_name, &sym); +#ifdef PROFILING + workTimer.stop(); + writeTimer.start(); +#endif /* PROFILING */ + + delete[] object_name; + delete[] symbol_name; + if (lookup_res != PS_OK) { + // This is too noisy + // debug_only(fprintf(stderr, "ServiceabilityAgentDbxModule::handleLookup: error %d\n", lookup_res)); + myComm.writeString("0x0"); + } else { + myComm.writeAddress((void *)sym.st_value); + } + myComm.writeEOL(); + myComm.flush(); + +#ifdef PROFILING + writeTimer.stop(); +#endif /* PROFILING */ + + return true; +} + +bool +ServiceabilityAgentDbxModule::handleThrGRegs(char* data) { +#ifdef PROFILING + scanTimer.start(); +#endif /* PROFILING */ + + unsigned int num; + // Get the thread ID + if (!scanUnsignedInt(&data, &num)) { + return false; + } + +#ifdef PROFILING + scanTimer.stop(); + workTimer.start(); +#endif /* PROFILING */ + + // Map tid to thread handle + td_thrhandle_t thread_handle; + if ((*td_ta_map_id2thr_fn)(_tdb_agent, num, &thread_handle) != TD_OK) { + // fprintf(stderr, "Error mapping thread ID %d to thread handle\n", num); + return false; + } + + // Fetch register set + prgregset_t reg_set; + memset(reg_set, 0, sizeof(reg_set)); + td_err_e result = (*td_thr_getgregs_fn)(&thread_handle, reg_set); + if ((result != TD_OK) && (result != TD_PARTIALREG)) { + // fprintf(stderr, "Error fetching registers for thread handle %d: error = %d\n", num, result); + return false; + } + +#ifdef PROFILING + workTimer.stop(); + writeTimer.start(); +#endif /* PROFILING */ + +#if (defined(__sparc) || defined(__i386)) + myComm.writeInt(NPRGREG); + myComm.writeSpace(); + for (int i = 0; i < NPRGREG; i++) { + myComm.writeAddress((void *)reg_set[i]); + if (i == NPRGREG - 1) { + myComm.writeEOL(); + } else { + myComm.writeSpace(); + } + } +#else +#error Please port ServiceabilityAgentDbxModule::handleThrGRegs to your current platform +#endif + + myComm.flush(); + +#ifdef PROFILING + writeTimer.stop(); +#endif /* PROFILING */ + + return true; +} + +// +// Input routines +// + +bool +ServiceabilityAgentDbxModule::scanAddress(char** data, psaddr_t* addr) { + *addr = 0; + + // Skip whitespace + while ((**data != 0) && (isspace(**data))) { + ++*data; + } + + if (**data == 0) { + return false; + } + + if (strncmp(*data, "0x", 2) != 0) { + return false; + } + + *data += 2; + + while ((**data != 0) && (!isspace(**data))) { + int val; + bool res = charToNibble(**data, &val); + if (!res) { + return false; + } + *addr <<= 4; + *addr |= val; + ++*data; + } + + return true; +} + +bool +ServiceabilityAgentDbxModule::scanUnsignedInt(char** data, unsigned int* num) { + *num = 0; + + // Skip whitespace + while ((**data != 0) && (isspace(**data))) { + ++*data; + } + + if (**data == 0) { + return false; + } + + while ((**data != 0) && (!isspace(**data))) { + char cur = **data; + if ((cur < '0') || (cur > '9')) { + return false; + } + *num *= 10; + *num += cur - '0'; + ++*data; + } + + return true; +} + +char* +ServiceabilityAgentDbxModule::scanSymbol(char** data) { + // Skip whitespace + while ((**data != 0) && (isspace(**data))) { + ++*data; + } + + if (**data == 0) { + return NULL; + } + + // First count length + int len = 1; // Null terminator + char* tmpData = *data; + while ((*tmpData != 0) && (!isspace(*tmpData))) { + ++tmpData; + ++len; + } + char* buf = new char[len]; + strncpy(buf, *data, len - 1); + buf[len - 1] = 0; + *data += len - 1; + return buf; +} + +bool +ServiceabilityAgentDbxModule::charToNibble(char ascii, int* value) { + if (ascii >= '0' && ascii <= '9') { + *value = ascii - '0'; + return true; + } else if (ascii >= 'A' && ascii <= 'F') { + *value = 10 + ascii - 'A'; + return true; + } else if (ascii >= 'a' && ascii <= 'f') { + *value = 10 + ascii - 'a'; + return true; + } + + return false; +} + + +char* +ServiceabilityAgentDbxModule::readCStringFromProcess(psaddr_t addr) { + char c; + int num = 0; + ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); + + // Search for null terminator + do { + if (ps_pread(cur_proc, addr + num, &c, 1) != PS_OK) { + return NULL; + } + ++num; + } while (c != 0); + + // Allocate string + char* res = new char[num]; + if (ps_pread(cur_proc, addr, res, num) != PS_OK) { + delete[] res; + return NULL; + } + return res; +} + + +//-------------------------------------------------------------------------------- +// Class Timer +// + +Timer::Timer() { + reset(); +} + +Timer::~Timer() { +} + +void +Timer::start() { + gettimeofday(&startTime, NULL); +} + +void +Timer::stop() { + struct timeval endTime; + gettimeofday(&endTime, NULL); + totalMicroseconds += timevalDiff(&startTime, &endTime); + ++counter; +} + +long +Timer::total() { + return (totalMicroseconds / 1000); +} + +long +Timer::average() { + return (long) ((double) total() / (double) counter); +} + +void +Timer::reset() { + totalMicroseconds = 0; + counter = 0; +} + +long long +Timer::timevalDiff(struct timeval* start, struct timeval* end) { + long long secs = end->tv_sec - start->tv_sec; + secs *= 1000000; + long long usecs = end->tv_usec - start->tv_usec; + return (secs + usecs); +} diff --git a/hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.hpp b/hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.hpp new file mode 100644 index 00000000000..1757d90878c --- /dev/null +++ b/hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.hpp @@ -0,0 +1,188 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "shell_imp.h" +#include "IOBuf.hpp" +#include +#include + +typedef td_err_e td_init_fn_t(); +typedef td_err_e td_ta_new_fn_t(struct ps_prochandle *, td_thragent_t **); +typedef td_err_e td_ta_delete_fn_t(td_thragent_t *); +typedef td_err_e td_ta_map_id2thr_fn_t(const td_thragent_t *, thread_t, td_thrhandle_t *); +typedef td_err_e td_thr_getgregs_fn_t(const td_thrhandle_t *, prgregset_t); + +class ServiceabilityAgentDbxModule { +public: + ServiceabilityAgentDbxModule(int major, int minor, + shell_imp_interp_t interp, int argc, char *argv[]); + ~ServiceabilityAgentDbxModule(); + + bool install(); + bool uninstall(); + + /* This is invoked through the dbx command interpreter. It listens + on a socket for commands and does not return until it receives an + "exit" command. At that point control is returned to dbx's main + loop, at which point if the user sends an exit command to dbx's + shell the dbx process will exit. Returns true if completed + successfully, false if an error occurred while running (for + example, unable to bind listening socket). */ + bool run(); + +private: + + // This must be shared between the Java and C layers + static const int PORT = 21928; + + // Command handlers + bool handleAddressSize(char* data); + bool handlePeekFailFast(char* data); + bool handlePeek(char* data); + bool handlePoke(char* data); + bool handleMapped(char* data); + bool handleLookup(char* data); + bool handleThrGRegs(char* data); + + // Input routines + + // May mutate addr argument even if result is false + bool scanAddress(char** data, psaddr_t* addr); + // May mutate num argument even if result is false + bool scanUnsignedInt(char** data, unsigned int* num); + // Returns NULL if error occurred while scanning. Otherwise, returns + // newly-allocated character array which must be freed with delete[]. + char* scanSymbol(char** data); + // Helper routine: converts ASCII to 4-bit integer. Returns true if + // character is in range, false otherwise. + bool charToNibble(char ascii, int* value); + + // Output routines + + // Writes an int with no leading or trailing spaces + bool writeInt(int val, int fd); + // Writes an address in hex format with no leading or trailing + // spaces + bool writeAddress(psaddr_t addr, int fd); + // Writes a register in hex format with no leading or trailing + // spaces (addresses and registers might be of different size) + bool writeRegister(prgreg_t reg, int fd); + // Writes a space to given file descriptor + bool writeSpace(int fd); + // Writes carriage return to given file descriptor + bool writeCR(int fd); + // Writes a bool as [0|1] + bool writeBoolAsInt(bool val, int fd); + // Helper routine: converts low 4 bits to ASCII [0..9][A..F] + char nibbleToChar(unsigned char nibble); + + // Base routine called by most of the above + bool writeString(const char* str, int fd); + + // Writes a binary character + bool writeBinChar(char val, int fd); + // Writes a binary unsigned int in network (big-endian) byte order + bool writeBinUnsignedInt(unsigned int val, int fd); + // Writes a binary buffer + bool writeBinBuf(char* buf, int size, int fd); + + // Routine to flush the socket + bool flush(int client_socket); + + void cleanup(int client_socket); + + // The shell interpreter on which we can invoke commands (?) + shell_imp_interp_t _interp; + + // The "command line" arguments passed to us by dbx (?) + int _argc; + char **_argv; + + // The installed command in the dbx shell + shell_imp_command_t _command; + + // Access to libthread_db (dlsym'ed to be able to pick up the + // version loaded by dbx) + td_init_fn_t* td_init_fn; + td_ta_new_fn_t* td_ta_new_fn; + td_ta_delete_fn_t* td_ta_delete_fn; + td_ta_map_id2thr_fn_t* td_ta_map_id2thr_fn; + td_thr_getgregs_fn_t* td_thr_getgregs_fn; + + // Our "thread agent" -- access to libthread_db + td_thragent_t* _tdb_agent; + + // Path to libthread.so in target process; free with delete[] + char* libThreadName; + + // Handle to dlopen'ed libthread_db.so + void* libThreadDB; + + // Helper callback for finding libthread_db.so + friend int findLibThreadCB(const rd_loadobj_t* lo, void* data); + + // Support for reading C strings out of the target process (so we + // can find the correct libthread_db). Returns newly-allocated char* + // which must be freed with delete[], or null if the read failed. + char* readCStringFromProcess(psaddr_t addr); + + IOBuf myComm; + + // Output buffer support (used by writeString, writeChar, flush) + char* output_buffer; + int output_buffer_size; + int output_buffer_pos; + + // "Fail fast" flag + bool peek_fail_fast; + + // Commands + static const char* CMD_ADDRESS_SIZE; + static const char* CMD_PEEK_FAIL_FAST; + static const char* CMD_PEEK; + static const char* CMD_POKE; + static const char* CMD_MAPPED; + static const char* CMD_LOOKUP; + static const char* CMD_THR_GREGS; + static const char* CMD_EXIT; +}; + +// For profiling. Times reported are in milliseconds. +class Timer { +public: + Timer(); + ~Timer(); + + void start(); + void stop(); + long total(); + long average(); + void reset(); + +private: + struct timeval startTime; + long long totalMicroseconds; // stored internally in microseconds + int counter; + long long timevalDiff(struct timeval* startTime, struct timeval* endTime); +}; diff --git a/hotspot/agent/src/os/solaris/proc/Makefile b/hotspot/agent/src/os/solaris/proc/Makefile new file mode 100644 index 00000000000..e7f80fba988 --- /dev/null +++ b/hotspot/agent/src/os/solaris/proc/Makefile @@ -0,0 +1,80 @@ +# +# Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Targets are: +# sparc: Build the 32 bit sparc version in ./sparc +# sparcv9: Build the 64 bit sparcv9 version in ./sparcv9 +# i386: Build the 32 bit i386 version in ./i386 + +.PHONY: sparc sparcv9 i386 + +ARCH_ORIG = $(shell uname -p) + +C++ := CC +RM := /usr/bin/rm +MKDIRS := /usr/bin/mkdir -p + +CLASSES_DIR = ../../../../build/classes + +ifeq "$(ARCH_ORIG)" "i386" + ALL_TARGET = i386 $(filter amd64,$(shell isalist)) +else + ALL_TARGET = sparc sparcv9 +endif + +all:: $(ALL_TARGET) + +javahomecheck:: + @if [ "x$(JAVA_HOME)" = "x" ] ; then \ + echo You must set the environment variable JAVA_HOME before executing this Makefile ; \ + exit 1 ; \ + fi + +i386:: javahomecheck + $(MKDIRS) $@ + @javah -classpath $(CLASSES_DIR) -jni sun.jvm.hotspot.debugger.proc.ProcDebuggerLocal + CC -G -KPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/solaris saproc.cpp \ + -M mapfile -o $@/libsaproc.so -ldemangle + +amd64:: javahomecheck + $(MKDIRS) $@ + @javah -classpath $(CLASSES_DIR) -jni sun.jvm.hotspot.debugger.proc.ProcDebuggerLocal + CC -G -KPIC -xarch=amd64 -I${JAVA_HOME}/include -I${JAVA_HOME}/include/solaris saproc.cpp \ + -M mapfile -o $@/libsaproc.so -ldemangle + +sparc:: javahomecheck + $(MKDIRS) $@ + @javah -classpath $(CLASSES_DIR) -jni sun.jvm.hotspot.debugger.proc.ProcDebuggerLocal + CC -G -KPIC -xarch=v8 -I${JAVA_HOME}/include -I${JAVA_HOME}/include/solaris saproc.cpp \ + -M mapfile -o $@/libsaproc.so -ldemangle + +sparcv9:: javahomecheck + $(MKDIRS) $@ + @javah -classpath $(CLASSES_DIR) -jni sun.jvm.hotspot.debugger.proc.ProcDebuggerLocal + CC -G -KPIC -xarch=v9 -I${JAVA_HOME}/include -I${JAVA_HOME}/include/solaris saproc.cpp \ + -M mapfile -o $@/libsaproc.so -ldemangle + +clean:: + $(RM) -rf sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal.h + $(RM) -rf sparc sparcv9 i386 diff --git a/hotspot/agent/src/os/solaris/proc/libproc.h b/hotspot/agent/src/os/solaris/proc/libproc.h new file mode 100644 index 00000000000..53091ef9339 --- /dev/null +++ b/hotspot/agent/src/os/solaris/proc/libproc.h @@ -0,0 +1,474 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Interfaces available from the process control library, libproc. + * + * libproc provides process control functions for the /proc tools + * (commands in /usr/proc/bin), /usr/bin/truss, and /usr/bin/gcore. + * libproc is a private support library for these commands only. + * It is _not_ a public interface, although it might become one + * in the fullness of time, when the interfaces settle down. + * + * In the meantime, be aware that any program linked with libproc in this + * release of Solaris is almost guaranteed to break in the next release. + * + * In short, do not use this header file or libproc for any purpose. + */ + +#ifndef _LIBPROC_H +#define _LIBPROC_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Opaque structure tag reference to a process control structure. + * Clients of libproc cannot look inside the process control structure. + * The implementation of struct ps_prochandle can change w/o affecting clients. + */ +struct ps_prochandle; + +extern int _libproc_debug; /* set non-zero to enable debugging fprintfs */ + +#if defined(sparc) || defined(__sparc) +#define R_RVAL1 R_O0 /* register holding a function return value */ +#define R_RVAL2 R_O1 /* 32 more bits for a 64-bit return value */ +#define SYSCALL32 0x91d02008 /* 32-bit syscall (ta 8) instruction */ +#define SYSCALL64 0x91d02040 /* 64-bit syscall (ta 64) instruction */ +typedef uint32_t syscall_t; /* holds a syscall instruction */ +#endif /* sparc */ + +#if defined(__i386) || defined(__ia64) +#define R_PC EIP +#define R_SP UESP +#define R_RVAL1 EAX /* register holding a function return value */ +#define R_RVAL2 EDX /* 32 more bits for a 64-bit return value */ +#define SYSCALL 0x9a /* syscall (lcall) instruction opcode */ +typedef uchar_t syscall_t[7]; /* holds a syscall instruction */ +#endif /* __i386 || __ia64 */ + +#define R_RVAL R_RVAL1 /* simple function return value register */ + +/* maximum sizes of things */ +#define PRMAXSIG (32 * sizeof (sigset_t) / sizeof (uint32_t)) +#define PRMAXFAULT (32 * sizeof (fltset_t) / sizeof (uint32_t)) +#define PRMAXSYS (32 * sizeof (sysset_t) / sizeof (uint32_t)) + +/* State values returned by Pstate() */ +#define PS_RUN 1 /* process is running */ +#define PS_STOP 2 /* process is stopped */ +#define PS_LOST 3 /* process is lost to control (EAGAIN) */ +#define PS_UNDEAD 4 /* process is terminated (zombie) */ +#define PS_DEAD 5 /* process is terminated (core file) */ + +/* Flags accepted by Pgrab() */ +#define PGRAB_RETAIN 0x01 /* Retain tracing flags, else clear flags */ +#define PGRAB_FORCE 0x02 /* Open the process w/o O_EXCL */ +#define PGRAB_RDONLY 0x04 /* Open the process or core w/ O_RDONLY */ +#define PGRAB_NOSTOP 0x08 /* Open the process but do not stop it */ + +/* Error codes from Pcreate() */ +#define C_STRANGE -1 /* Unanticipated error, errno is meaningful */ +#define C_FORK 1 /* Unable to fork */ +#define C_PERM 2 /* No permission (file set-id or unreadable) */ +#define C_NOEXEC 3 /* Cannot find executable file */ +#define C_INTR 4 /* Interrupt received while creating */ +#define C_LP64 5 /* Program is _LP64, self is _ILP32 */ + +/* Error codes from Pgrab(), Pfgrab_core(), and Pgrab_core() */ +#define G_STRANGE -1 /* Unanticipated error, errno is meaningful */ +#define G_NOPROC 1 /* No such process */ +#define G_NOCORE 2 /* No such core file */ +#define G_NOPROCORCORE 3 /* No such proc or core (for proc_arg_grab) */ +#define G_NOEXEC 4 /* Cannot locate executable file */ +#define G_ZOMB 5 /* Zombie process */ +#define G_PERM 6 /* No permission */ +#define G_BUSY 7 /* Another process has control */ +#define G_SYS 8 /* System process */ +#define G_SELF 9 /* Process is self */ +#define G_INTR 10 /* Interrupt received while grabbing */ +#define G_LP64 11 /* Process is _LP64, self is ILP32 */ +#define G_FORMAT 12 /* File is not an ELF format core file */ +#define G_ELF 13 /* Libelf error, elf_errno() is meaningful */ +#define G_NOTE 14 /* Required PT_NOTE Phdr not present in core */ + +/* Flags accepted by Prelease */ +#define PRELEASE_CLEAR 0x10 /* Clear all tracing flags */ +#define PRELEASE_RETAIN 0x20 /* Retain final tracing flags */ +#define PRELEASE_HANG 0x40 /* Leave the process stopped */ +#define PRELEASE_KILL 0x80 /* Terminate the process */ + +typedef struct { /* argument descriptor for system call (Psyscall) */ + long arg_value; /* value of argument given to system call */ + void *arg_object; /* pointer to object in controlling process */ + char arg_type; /* AT_BYVAL, AT_BYREF */ + char arg_inout; /* AI_INPUT, AI_OUTPUT, AI_INOUT */ + ushort_t arg_size; /* if AT_BYREF, size of object in bytes */ +} argdes_t; + +typedef struct { /* return values from system call (Psyscall) */ + int sys_errno; /* syscall error number */ + long sys_rval1; /* primary return value from system call */ + long sys_rval2; /* second return value from system call */ +} sysret_t; + +/* values for type */ +#define AT_BYVAL 1 +#define AT_BYREF 2 + +/* values for inout */ +#define AI_INPUT 1 +#define AI_OUTPUT 2 +#define AI_INOUT 3 + +/* maximum number of syscall arguments */ +#define MAXARGS 8 + +/* maximum size in bytes of a BYREF argument */ +#define MAXARGL (4*1024) + +/* Kludges to make things work on Solaris 2.6 */ +#if !defined(_LP64) && !defined(PR_MODEL_UNKNOWN) +#define PR_MODEL_UNKNOWN 0 +#define PR_MODEL_ILP32 0 /* process data model is ILP32 */ +#define PR_MODEL_LP64 2 /* process data model is LP64 */ +#define PR_MODEL_NATIVE PR_MODEL_ILP32 +#define pr_dmodel pr_filler[0] +#define STACK_BIAS 0 +#endif + +/* + * Function prototypes for routines in the process control package. + */ +extern struct ps_prochandle *Pcreate(const char *, char *const *, + int *, char *, size_t); + +extern const char *Pcreate_error(int); + +extern struct ps_prochandle *Pgrab(pid_t, int, int *); +extern struct ps_prochandle *Pgrab_core(const char *, const char *, int, int *); +extern struct ps_prochandle *Pfgrab_core(int, const char *, int *); + +extern const char *Pgrab_error(int); + +extern int Preopen(struct ps_prochandle *); +extern void Prelease(struct ps_prochandle *, int); +extern void Pfree(struct ps_prochandle *); + +extern int Pasfd(struct ps_prochandle *); +extern int Pctlfd(struct ps_prochandle *); +extern int Pcreate_agent(struct ps_prochandle *); +extern void Pdestroy_agent(struct ps_prochandle *); +extern int Pwait(struct ps_prochandle *, uint_t); +extern int Pstop(struct ps_prochandle *, uint_t); +extern int Pstate(struct ps_prochandle *); +extern const psinfo_t *Ppsinfo(struct ps_prochandle *); +extern const pstatus_t *Pstatus(struct ps_prochandle *); +extern int Pcred(struct ps_prochandle *, prcred_t *, int); +extern int Pgetareg(struct ps_prochandle *, int, prgreg_t *); +extern int Pputareg(struct ps_prochandle *, int, prgreg_t); +extern int Psetrun(struct ps_prochandle *, int, int); +extern ssize_t Pread(struct ps_prochandle *, void *, size_t, uintptr_t); +extern ssize_t Pread_string(struct ps_prochandle *, char *, size_t, uintptr_t); +extern ssize_t Pwrite(struct ps_prochandle *, const void *, size_t, uintptr_t); +extern int Pclearsig(struct ps_prochandle *); +extern int Pclearfault(struct ps_prochandle *); +extern int Psetbkpt(struct ps_prochandle *, uintptr_t, ulong_t *); +extern int Pdelbkpt(struct ps_prochandle *, uintptr_t, ulong_t); +extern int Pxecbkpt(struct ps_prochandle *, ulong_t); +extern int Psetflags(struct ps_prochandle *, long); +extern int Punsetflags(struct ps_prochandle *, long); +extern int Psignal(struct ps_prochandle *, int, int); +extern int Pfault(struct ps_prochandle *, int, int); +extern int Psysentry(struct ps_prochandle *, int, int); +extern int Psysexit(struct ps_prochandle *, int, int); +extern void Psetsignal(struct ps_prochandle *, const sigset_t *); +extern void Psetfault(struct ps_prochandle *, const fltset_t *); +extern void Psetsysentry(struct ps_prochandle *, const sysset_t *); +extern void Psetsysexit(struct ps_prochandle *, const sysset_t *); +extern void Psync(struct ps_prochandle *); +extern sysret_t Psyscall(struct ps_prochandle *, int, uint_t, argdes_t *); +extern int Pisprocdir(struct ps_prochandle *, const char *); + +/* + * Function prototypes for system calls forced on the victim process. + */ +extern int pr_open(struct ps_prochandle *, const char *, int, mode_t); +extern int pr_creat(struct ps_prochandle *, const char *, mode_t); +extern int pr_close(struct ps_prochandle *, int); +extern int pr_door_info(struct ps_prochandle *, int, struct door_info *); +extern void *pr_mmap(struct ps_prochandle *, + void *, size_t, int, int, int, off_t); +extern void *pr_zmap(struct ps_prochandle *, + void *, size_t, int, int); +extern int pr_munmap(struct ps_prochandle *, void *, size_t); +extern int pr_memcntl(struct ps_prochandle *, + caddr_t, size_t, int, caddr_t, int, int); +extern int pr_sigaction(struct ps_prochandle *, + int, const struct sigaction *, struct sigaction *); +extern int pr_getitimer(struct ps_prochandle *, + int, struct itimerval *); +extern int pr_setitimer(struct ps_prochandle *, + int, const struct itimerval *, struct itimerval *); +extern int pr_ioctl(struct ps_prochandle *, int, int, void *, size_t); +extern int pr_fcntl(struct ps_prochandle *, int, int, void *); +extern int pr_stat(struct ps_prochandle *, const char *, struct stat *); +extern int pr_lstat(struct ps_prochandle *, const char *, struct stat *); +extern int pr_fstat(struct ps_prochandle *, int, struct stat *); +extern int pr_statvfs(struct ps_prochandle *, const char *, statvfs_t *); +extern int pr_fstatvfs(struct ps_prochandle *, int, statvfs_t *); +extern int pr_getrlimit(struct ps_prochandle *, + int, struct rlimit *); +extern int pr_setrlimit(struct ps_prochandle *, + int, const struct rlimit *); +#if defined(_LARGEFILE64_SOURCE) +extern int pr_getrlimit64(struct ps_prochandle *, + int, struct rlimit64 *); +extern int pr_setrlimit64(struct ps_prochandle *, + int, const struct rlimit64 *); +#endif /* _LARGEFILE64_SOURCE */ +extern int pr_lwp_exit(struct ps_prochandle *); +extern int pr_exit(struct ps_prochandle *, int); +extern int pr_waitid(struct ps_prochandle *, + idtype_t, id_t, siginfo_t *, int); +extern off_t pr_lseek(struct ps_prochandle *, int, off_t, int); +extern offset_t pr_llseek(struct ps_prochandle *, int, offset_t, int); +extern int pr_rename(struct ps_prochandle *, const char *, const char *); +extern int pr_link(struct ps_prochandle *, const char *, const char *); +extern int pr_unlink(struct ps_prochandle *, const char *); +extern int pr_getpeername(struct ps_prochandle *, + int, struct sockaddr *, socklen_t *); +extern int pr_getsockname(struct ps_prochandle *, + int, struct sockaddr *, socklen_t *); + +/* + * Function prototypes for accessing per-LWP register information. + */ +extern int Plwp_getregs(struct ps_prochandle *, lwpid_t, prgregset_t); +extern int Plwp_setregs(struct ps_prochandle *, lwpid_t, const prgregset_t); + +extern int Plwp_getfpregs(struct ps_prochandle *, lwpid_t, prfpregset_t *); +extern int Plwp_setfpregs(struct ps_prochandle *, lwpid_t, + const prfpregset_t *); + +#if defined(sparc) || defined(__sparc) + +extern int Plwp_getxregs(struct ps_prochandle *, lwpid_t, prxregset_t *); +extern int Plwp_setxregs(struct ps_prochandle *, lwpid_t, const prxregset_t *); + +#if defined(__sparcv9) +extern int Plwp_getasrs(struct ps_prochandle *, lwpid_t, asrset_t); +extern int Plwp_setasrs(struct ps_prochandle *, lwpid_t, const asrset_t); +#endif /* __sparcv9 */ + +#endif /* __sparc */ + +extern int Plwp_getpsinfo(struct ps_prochandle *, lwpid_t, lwpsinfo_t *); + +/* + * LWP iteration interface. + */ +typedef int proc_lwp_f(void *, const lwpstatus_t *); +extern int Plwp_iter(struct ps_prochandle *, proc_lwp_f *, void *); + +/* + * Symbol table interfaces. + */ + +/* + * Pseudo-names passed to Plookup_by_name() for well-known load objects. + * NOTE: It is required that PR_OBJ_EXEC and PR_OBJ_LDSO exactly match + * the definitions of PS_OBJ_EXEC and PS_OBJ_LDSO from . + */ +#define PR_OBJ_EXEC ((const char *)0) /* search the executable file */ +#define PR_OBJ_LDSO ((const char *)1) /* search ld.so.1 */ +#define PR_OBJ_EVERY ((const char *)-1) /* search every load object */ + +/* + * 'object_name' is the name of a load object obtained from an + * iteration over the process's address space mappings (Pmapping_iter), + * or an iteration over the process's mapped objects (Pobject_iter), + * or else it is one of the special PR_OBJ_* values above. + */ +extern int Plookup_by_name(struct ps_prochandle *, + const char *, const char *, GElf_Sym *); + +extern int Plookup_by_addr(struct ps_prochandle *, + uintptr_t, char *, size_t, GElf_Sym *); + +typedef int proc_map_f(void *, const prmap_t *, const char *); + +extern int Pmapping_iter(struct ps_prochandle *, proc_map_f *, void *); +extern int Pobject_iter(struct ps_prochandle *, proc_map_f *, void *); + +extern const prmap_t *Paddr_to_map(struct ps_prochandle *, uintptr_t); +extern const prmap_t *Paddr_to_text_map(struct ps_prochandle *, uintptr_t); +extern const prmap_t *Pname_to_map(struct ps_prochandle *, const char *); + +extern char *Pplatform(struct ps_prochandle *, char *, size_t); +extern int Puname(struct ps_prochandle *, struct utsname *); + +extern char *Pexecname(struct ps_prochandle *, char *, size_t); +extern char *Pobjname(struct ps_prochandle *, uintptr_t, char *, size_t); + +extern char *Pgetenv(struct ps_prochandle *, const char *, char *, size_t); +extern long Pgetauxval(struct ps_prochandle *, int); + +/* + * Symbol table iteration interface. + */ +typedef int proc_sym_f(void *, const GElf_Sym *, const char *); + +extern int Psymbol_iter(struct ps_prochandle *, + const char *, int, int, proc_sym_f *, void *); + +/* + * 'which' selects which symbol table and can be one of the following. + */ +#define PR_SYMTAB 1 +#define PR_DYNSYM 2 +/* + * 'type' selects the symbols of interest by binding and type. It is a bit- + * mask of one or more of the following flags, whose order MUST match the + * order of STB and STT constants in . + */ +#define BIND_LOCAL 0x0001 +#define BIND_GLOBAL 0x0002 +#define BIND_WEAK 0x0004 +#define BIND_ANY (BIND_LOCAL|BIND_GLOBAL|BIND_WEAK) +#define TYPE_NOTYPE 0x0100 +#define TYPE_OBJECT 0x0200 +#define TYPE_FUNC 0x0400 +#define TYPE_SECTION 0x0800 +#define TYPE_FILE 0x1000 +#define TYPE_ANY (TYPE_NOTYPE|TYPE_OBJECT|TYPE_FUNC|TYPE_SECTION|TYPE_FILE) + +/* + * This returns the rtld_db agent handle for the process. + * The handle will become invalid at the next successful exec() and + * must not be used beyond that point (see Preset_maps(), below). + */ +extern rd_agent_t *Prd_agent(struct ps_prochandle *); + +/* + * This should be called when an RD_DLACTIVITY event with the + * RD_CONSISTENT state occurs via librtld_db's event mechanism. + * This makes libproc's address space mappings and symbol tables current. + */ +extern void Pupdate_maps(struct ps_prochandle *); + +/* + * This must be called after the victim process performs a successful + * exec() if any of the symbol table interface functions have been called + * prior to that point. This is essential because an exec() invalidates + * all previous symbol table and address space mapping information. + * It is always safe to call, but if it is called other than after an + * exec() by the victim process it just causes unnecessary overhead. + * + * The rtld_db agent handle obtained from a previous call to Prd_agent() is + * made invalid by Preset_maps() and Prd_agent() must be called again to get + * the new handle. + */ +extern void Preset_maps(struct ps_prochandle *); + +/* + * Given an address, Ppltdest() determines if this is part of a PLT, and if + * so returns the target address of this PLT entry and a flag indicating + * whether or not this PLT entry has been bound by the run-time linker. + */ +extern uintptr_t Ppltdest(struct ps_prochandle *, uintptr_t, int *); + +/* + * Stack frame iteration interface. + */ +typedef int proc_stack_f(void *, const prgregset_t, uint_t, const long *); + +extern int Pstack_iter(struct ps_prochandle *, + const prgregset_t, proc_stack_f *, void *); + +/* + * Compute the full pathname of a named directory without using chdir(). + * This is useful for dealing with /proc//cwd. + */ +extern char *proc_dirname(const char *, char *, size_t); + +/* + * Remove unprintable characters from psinfo.pr_psargs and replace with + * whitespace characters so it is safe for printing. + */ +extern void proc_unctrl_psinfo(psinfo_t *); + +/* + * Utility functions for processing arguments which should be /proc files, + * pids, and/or core files. The returned error code can be passed to + * Pgrab_error() in order to convert it to an error string. + */ +#define PR_ARG_PIDS 0x1 /* Allow pid and /proc file arguments */ +#define PR_ARG_CORES 0x2 /* Allow core file arguments */ + +#define PR_ARG_ANY (PR_ARG_PIDS | PR_ARG_CORES) + +extern struct ps_prochandle *proc_arg_grab(const char *, int, int, int *); +extern pid_t proc_arg_psinfo(const char *, int, psinfo_t *, int *); + +/* + * Utility functions for obtaining information via /proc without actually + * performing a Pcreate() or Pgrab(): + */ +extern int proc_get_auxv(pid_t, auxv_t *, int); +extern int proc_get_cred(pid_t, prcred_t *, int); +extern int proc_get_psinfo(pid_t, psinfo_t *); +extern int proc_get_status(pid_t, pstatus_t *); + +/* + * Utility functions for debugging tools to convert numeric fault, + * signal, and system call numbers to symbolic names: + */ +extern char *proc_fltname(int, char *, size_t); +extern char *proc_signame(int, char *, size_t); +extern char *proc_sysname(int, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBPROC_H */ diff --git a/hotspot/agent/src/os/solaris/proc/mapfile b/hotspot/agent/src/os/solaris/proc/mapfile new file mode 100644 index 00000000000..74701ed11d8 --- /dev/null +++ b/hotspot/agent/src/os/solaris/proc/mapfile @@ -0,0 +1,50 @@ +# + +# +# Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_attach0__Ljava_lang_String_2; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_demangle0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_detach0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_fillCFrameList0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_fillLoadObjectList0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_fillThreadList0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_getPageSize0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_getRemoteProcessAddressSize0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_getThreadIntegerRegisterSet0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_initIDs; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_lookupByAddress0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_lookupByName0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_readBytesFromProcess0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_resume0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_suspend0; + Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_writeBytesToProcess0; +local: + *; +}; diff --git a/hotspot/agent/src/os/solaris/proc/salibproc.h b/hotspot/agent/src/os/solaris/proc/salibproc.h new file mode 100644 index 00000000000..ba1262926f7 --- /dev/null +++ b/hotspot/agent/src/os/solaris/proc/salibproc.h @@ -0,0 +1,115 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +#ifndef _SALIBPROC_H_ +#define _SALIBPROC_H_ + +/* + * The following definitions, prototypes are from Solaris libproc.h. + * We used to use the copy of it from Solaris 8.0. But there are + * problems with that approach in building this library across Solaris + * versions. Solaris 10 has libproc.h in /usr/include. And libproc.h + * varies slightly across Solaris versions. On Solaris 9, we get + * 'sysret_t multiply defined' error. This is common minimum subset we + * really need from libproc.h. The libproc.h in the current dir has + * been left for reference and not used in build. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * 'object_name' is the name of a load object obtained from an + * iteration over the process's address space mappings (Pmapping_iter), + * or an iteration over the process's mapped objects (Pobject_iter), + * or else it is one of the special PR_OBJ_* values above. + */ + +extern int Plookup_by_addr(struct ps_prochandle *, + uintptr_t, char *, size_t, GElf_Sym *); + +typedef int proc_map_f(void *, const prmap_t *, const char *); +extern int Pobject_iter(struct ps_prochandle *, proc_map_f *, void *); + +/* + * Utility functions for processing arguments which should be /proc files, + * pids, and/or core files. The returned error code can be passed to + * Pgrab_error() in order to convert it to an error string. + */ +#define PR_ARG_PIDS 0x1 /* Allow pid and /proc file arguments */ +#define PR_ARG_CORES 0x2 /* Allow core file arguments */ +#define PR_ARG_ANY (PR_ARG_PIDS | PR_ARG_CORES) + +/* Flags accepted by Pgrab() (partial) */ +#define PGRAB_FORCE 0x02 /* Open the process w/o O_EXCL */ + +/* Error codes from Pgrab(), Pfgrab_core(), and Pgrab_core() */ +#define G_STRANGE -1 /* Unanticipated error, errno is meaningful */ +#define G_NOPROC 1 /* No such process */ +#define G_NOCORE 2 /* No such core file */ +#define G_NOPROCORCORE 3 /* No such proc or core (for proc_arg_grab) */ +#define G_NOEXEC 4 /* Cannot locate executable file */ +#define G_ZOMB 5 /* Zombie process */ +#define G_PERM 6 /* No permission */ +#define G_BUSY 7 /* Another process has control */ +#define G_SYS 8 /* System process */ +#define G_SELF 9 /* Process is self */ +#define G_INTR 10 /* Interrupt received while grabbing */ +#define G_LP64 11 /* Process is _LP64, self is ILP32 */ +#define G_FORMAT 12 /* File is not an ELF format core file */ +#define G_ELF 13 /* Libelf error, elf_errno() is meaningful */ +#define G_NOTE 14 /* Required PT_NOTE Phdr not present in core */ + +extern struct ps_prochandle *proc_arg_grab(const char *, int, int, int *); +extern const pstatus_t *Pstatus(struct ps_prochandle *); + +/* Flags accepted by Prelease (partial) */ +#define PRELEASE_CLEAR 0x10 /* Clear all tracing flags */ + +extern void Prelease(struct ps_prochandle *, int); +extern int Psetrun(struct ps_prochandle *, int, int); +extern int Pstop(struct ps_prochandle *, uint_t); + +/* + * Stack frame iteration interface. + */ +typedef int proc_stack_f(void *, const prgregset_t, uint_t, const long *); +extern int Pstack_iter(struct ps_prochandle *, + const prgregset_t, proc_stack_f *, void *); + +#define PR_OBJ_EVERY ((const char *)-1) /* search every load object */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _SALIBPROC_H_ */ diff --git a/hotspot/agent/src/os/solaris/proc/saproc.cpp b/hotspot/agent/src/os/solaris/proc/saproc.cpp new file mode 100644 index 00000000000..231773c5647 --- /dev/null +++ b/hotspot/agent/src/os/solaris/proc/saproc.cpp @@ -0,0 +1,1300 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "salibproc.h" +#include "sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal.h" +#include +#include +#include +#include +#include +#include +#include + +#define CHECK_EXCEPTION_(value) if(env->ExceptionOccurred()) { return value; } +#define CHECK_EXCEPTION if(env->ExceptionOccurred()) { return;} +#define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { throwNewDebuggerException(env, str); return value; } +#define THROW_NEW_DEBUGGER_EXCEPTION(str) { throwNewDebuggerException(env, str); return;} + +#define SYMBOL_BUF_SIZE 256 +#define ERR_MSG_SIZE (PATH_MAX + 256) + +// debug mode +static int _libsaproc_debug = 0; + +static void print_debug(const char* format,...) { + if (_libsaproc_debug) { + va_list alist; + + va_start(alist, format); + fputs("libsaproc DEBUG: ", stderr); + vfprintf(stderr, format, alist); + va_end(alist); + } +} + +struct Debugger { + JNIEnv* env; + jobject this_obj; +}; + +struct DebuggerWithObject : Debugger { + jobject obj; +}; + +struct DebuggerWith2Objects : DebuggerWithObject { + jobject obj2; +}; + +/* +* Portions of user thread level detail gathering code is from pstack source +* code. See pstack.c in Solaris 2.8 user commands source code. +*/ + +static void throwNewDebuggerException(JNIEnv* env, const char* errMsg) { + env->ThrowNew(env->FindClass("sun/jvm/hotspot/debugger/DebuggerException"), errMsg); +} + +// JNI ids for some fields, methods + +// libproc handler pointer +static jfieldID p_ps_prochandle_ID = 0; + +// libthread.so dlopen handle, thread agent ptr and function pointers +static jfieldID libthread_db_handle_ID = 0; +static jfieldID p_td_thragent_t_ID = 0; +static jfieldID p_td_init_ID = 0; +static jfieldID p_td_ta_new_ID = 0; +static jfieldID p_td_ta_delete_ID = 0; +static jfieldID p_td_ta_thr_iter_ID = 0; +static jfieldID p_td_thr_get_info_ID = 0; +static jfieldID p_td_ta_map_id2thr_ID = 0; +static jfieldID p_td_thr_getgregs_ID = 0; + +// reg index fields +static jfieldID pcRegIndex_ID = 0; +static jfieldID fpRegIndex_ID = 0; + +// part of the class sharing workaround +static jfieldID classes_jsa_fd_ID = 0; +static jfieldID p_file_map_header_ID = 0; + +// method ids + +static jmethodID getThreadForThreadId_ID = 0; +static jmethodID createSenderFrame_ID = 0; +static jmethodID createLoadObject_ID = 0; +static jmethodID createClosestSymbol_ID = 0; +static jmethodID listAdd_ID = 0; + +/* + * Functions we need from libthread_db + */ +typedef td_err_e + (*p_td_init_t)(void); +typedef td_err_e + (*p_td_ta_new_t)(void *, td_thragent_t **); +typedef td_err_e + (*p_td_ta_delete_t)(td_thragent_t *); +typedef td_err_e + (*p_td_ta_thr_iter_t)(const td_thragent_t *, td_thr_iter_f *, void *, + td_thr_state_e, int, sigset_t *, unsigned); +typedef td_err_e + (*p_td_thr_get_info_t)(const td_thrhandle_t *, td_thrinfo_t *); +typedef td_err_e + (*p_td_ta_map_id2thr_t)(const td_thragent_t *, thread_t, td_thrhandle_t *); +typedef td_err_e + (*p_td_thr_getgregs_t)(const td_thrhandle_t *, prgregset_t); + +static void +clear_libthread_db_ptrs(JNIEnv* env, jobject this_obj) { + // release libthread_db agent, if we had created + p_td_ta_delete_t p_td_ta_delete = 0; + p_td_ta_delete = (p_td_ta_delete_t) env->GetLongField(this_obj, p_td_ta_delete_ID); + + td_thragent_t *p_td_thragent_t = 0; + p_td_thragent_t = (td_thragent_t*) env->GetLongField(this_obj, p_td_thragent_t_ID); + if (p_td_thragent_t != 0 && p_td_ta_delete != 0) { + p_td_ta_delete(p_td_thragent_t); + } + + // dlclose libthread_db.so + void* libthread_db_handle = (void*) env->GetLongField(this_obj, libthread_db_handle_ID); + if (libthread_db_handle != 0) { + dlclose(libthread_db_handle); + } + + env->SetLongField(this_obj, libthread_db_handle_ID, (jlong)0); + env->SetLongField(this_obj, p_td_init_ID, (jlong)0); + env->SetLongField(this_obj, p_td_ta_new_ID, (jlong)0); + env->SetLongField(this_obj, p_td_ta_delete_ID, (jlong)0); + env->SetLongField(this_obj, p_td_ta_thr_iter_ID, (jlong)0); + env->SetLongField(this_obj, p_td_thr_get_info_ID, (jlong)0); + env->SetLongField(this_obj, p_td_ta_map_id2thr_ID, (jlong)0); + env->SetLongField(this_obj, p_td_thr_getgregs_ID, (jlong)0); +} + + +static void detach_internal(JNIEnv* env, jobject this_obj) { + // clear libthread_db stuff + clear_libthread_db_ptrs(env, this_obj); + + // release ptr to ps_prochandle + jlong p_ps_prochandle; + p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + if (p_ps_prochandle != 0L) { + Prelease((struct ps_prochandle*) p_ps_prochandle, PRELEASE_CLEAR); + } + + // part of the class sharing workaround + int classes_jsa_fd = env->GetIntField(this_obj, classes_jsa_fd_ID); + if (classes_jsa_fd != -1) { + close(classes_jsa_fd); + struct FileMapHeader* pheader = (struct FileMapHeader*) env->GetLongField(this_obj, p_file_map_header_ID); + if (pheader != NULL) { + free(pheader); + } + } +} + +// Is it okay to ignore libthread_db failure? Set env var to ignore +// libthread_db failure. You can still debug, but will miss threads +// related functionality. +static bool sa_ignore_threaddb = (getenv("SA_IGNORE_THREADDB") != 0); + +#define HANDLE_THREADDB_FAILURE(msg) \ + if (sa_ignore_threaddb) { \ + printf("libsaproc WARNING: %s\n", msg); \ + return; \ + } else { \ + THROW_NEW_DEBUGGER_EXCEPTION(msg); \ + } + +#define HANDLE_THREADDB_FAILURE_(msg, ret) \ + if (sa_ignore_threaddb) { \ + printf("libsaproc WARNING: %s\n", msg); \ + return ret; \ + } else { \ + THROW_NEW_DEBUGGER_EXCEPTION_(msg, ret); \ + } + +static const char * alt_root = NULL; +static int alt_root_len = -1; + +#define SA_ALTROOT "SA_ALTROOT" + +static void init_alt_root() { + if (alt_root_len == -1) { + alt_root = getenv(SA_ALTROOT); + if (alt_root) + alt_root_len = strlen(alt_root); + else + alt_root_len = 0; + } +} + +static int find_file_hook(const char * name, int elf_checksum) { + init_alt_root(); + + if (_libsaproc_debug) { + printf("libsaproc DEBUG: find_file_hook %s 0x%x\n", name, elf_checksum); + } + + if (alt_root_len > 0) { + int fd = -1; + char alt_path[PATH_MAX+1]; + + strcpy(alt_path, alt_root); + strcat(alt_path, name); + fd = open(alt_path, O_RDONLY); + if (fd >= 0) { + if (_libsaproc_debug) { + printf("libsaproc DEBUG: find_file_hook substituted %s\n", alt_path); + } + return fd; + } + + if (strrchr(name, '/')) { + strcpy(alt_path, alt_root); + strcat(alt_path, strrchr(name, '/')); + fd = open(alt_path, O_RDONLY); + if (fd >= 0) { + if (_libsaproc_debug) { + printf("libsaproc DEBUG: find_file_hook substituted %s\n", alt_path); + } + return fd; + } + } + } + return -1; +} + +static int pathmap_open(const char* name) { + int fd = open(name, O_RDONLY); + if (fd < 0) { + fd = find_file_hook(name, 0); + } + return fd; +} + +static void * pathmap_dlopen(const char * name, int mode) { + init_alt_root(); + + if (_libsaproc_debug) { + printf("libsaproc DEBUG: pathmap_dlopen %s\n", name); + } + + void * handle = NULL; + if (alt_root_len > 0) { + char alt_path[PATH_MAX+1]; + strcpy(alt_path, alt_root); + strcat(alt_path, name); + handle = dlopen(alt_path, mode); + if (_libsaproc_debug && handle) { + printf("libsaproc DEBUG: pathmap_dlopen substituted %s\n", alt_path); + } + + if (handle == NULL && strrchr(name, '/')) { + strcpy(alt_path, alt_root); + strcat(alt_path, strrchr(name, '/')); + handle = dlopen(alt_path, mode); + if (_libsaproc_debug && handle) { + printf("libsaproc DEBUG: pathmap_dlopen substituted %s\n", alt_path); + } + } + } + if (handle == NULL) { + handle = dlopen(name, mode); + } + if (_libsaproc_debug) { + printf("libsaproc DEBUG: pathmap_dlopen %s return 0x%x\n", name, handle); + } + return handle; +} + +// libproc and libthread_db callback functions + +extern "C" { + +static int +init_libthread_db_ptrs(void *cd, const prmap_t *pmp, const char *object_name) { + Debugger* dbg = (Debugger*) cd; + JNIEnv* env = dbg->env; + jobject this_obj = dbg->this_obj; + struct ps_prochandle* ph = (struct ps_prochandle*) env->GetLongField(this_obj, p_ps_prochandle_ID); + + char *s1 = 0, *s2 = 0; + char libthread_db[PATH_MAX]; + + if (strstr(object_name, "/libthread.so.") == NULL) + return (0); + + /* + * We found a libthread. + * dlopen() the matching libthread_db and get the thread agent handle. + */ + if (Pstatus(ph)->pr_dmodel == PR_MODEL_NATIVE) { + (void) strcpy(libthread_db, object_name); + s1 = (char*) strstr(object_name, ".so."); + s2 = (char*) strstr(libthread_db, ".so."); + (void) strcpy(s2, "_db"); + s2 += 3; + (void) strcpy(s2, s1); + } else { +#ifdef _LP64 + /* + * The victim process is 32-bit, we are 64-bit. + * We have to find the 64-bit version of libthread_db + * that matches the victim's 32-bit version of libthread. + */ + (void) strcpy(libthread_db, object_name); + s1 = (char*) strstr(object_name, "/libthread.so."); + s2 = (char*) strstr(libthread_db, "/libthread.so."); + (void) strcpy(s2, "/64"); + s2 += 3; + (void) strcpy(s2, s1); + s1 = (char*) strstr(s1, ".so."); + s2 = (char*) strstr(s2, ".so."); + (void) strcpy(s2, "_db"); + s2 += 3; + (void) strcpy(s2, s1); +#else + return (0); +#endif /* _LP64 */ + } + + void* libthread_db_handle = 0; + if ((libthread_db_handle = pathmap_dlopen(libthread_db, RTLD_LAZY|RTLD_LOCAL)) == NULL) { + char errMsg[PATH_MAX + 256]; + sprintf(errMsg, "Can't load %s!", libthread_db); + HANDLE_THREADDB_FAILURE_(errMsg, 0); + } + env->SetLongField(this_obj, libthread_db_handle_ID, (jlong)(uintptr_t)libthread_db_handle); + + void* tmpPtr = 0; + tmpPtr = dlsym(libthread_db_handle, "td_init"); + if (tmpPtr == 0) { + HANDLE_THREADDB_FAILURE_("dlsym failed on td_init!", 0); + } + env->SetLongField(this_obj, p_td_init_ID, (jlong)(uintptr_t) tmpPtr); + + tmpPtr =dlsym(libthread_db_handle, "td_ta_new"); + if (tmpPtr == 0) { + HANDLE_THREADDB_FAILURE_("dlsym failed on td_ta_new!", 0); + } + env->SetLongField(this_obj, p_td_ta_new_ID, (jlong)(uintptr_t) tmpPtr); + + tmpPtr = dlsym(libthread_db_handle, "td_ta_delete"); + if (tmpPtr == 0) { + HANDLE_THREADDB_FAILURE_("dlsym failed on td_ta_delete!", 0); + } + env->SetLongField(this_obj, p_td_ta_delete_ID, (jlong)(uintptr_t) tmpPtr); + + tmpPtr = dlsym(libthread_db_handle, "td_ta_thr_iter"); + if (tmpPtr == 0) { + HANDLE_THREADDB_FAILURE_("dlsym failed on td_ta_thr_iter!", 0); + } + env->SetLongField(this_obj, p_td_ta_thr_iter_ID, (jlong)(uintptr_t) tmpPtr); + + tmpPtr = dlsym(libthread_db_handle, "td_thr_get_info"); + if (tmpPtr == 0) { + HANDLE_THREADDB_FAILURE_("dlsym failed on td_thr_get_info!", 0); + } + env->SetLongField(this_obj, p_td_thr_get_info_ID, (jlong)(uintptr_t) tmpPtr); + + tmpPtr = dlsym(libthread_db_handle, "td_ta_map_id2thr"); + if (tmpPtr == 0) { + HANDLE_THREADDB_FAILURE_("dlsym failed on td_ta_map_id2thr!", 0); + } + env->SetLongField(this_obj, p_td_ta_map_id2thr_ID, (jlong)(uintptr_t) tmpPtr); + + tmpPtr = dlsym(libthread_db_handle, "td_thr_getgregs"); + if (tmpPtr == 0) { + HANDLE_THREADDB_FAILURE_("dlsym failed on td_thr_getgregs!", 0); + } + env->SetLongField(this_obj, p_td_thr_getgregs_ID, (jlong)(uintptr_t) tmpPtr); + + return 1; +} + +static int +fill_thread_list(const td_thrhandle_t *p_td_thragent_t, void* cd) { + DebuggerWithObject* dbgo = (DebuggerWithObject*) cd; + JNIEnv* env = dbgo->env; + jobject this_obj = dbgo->this_obj; + jobject list = dbgo->obj; + + td_thrinfo_t thrinfo; + p_td_thr_get_info_t p_td_thr_get_info = (p_td_thr_get_info_t) env->GetLongField(this_obj, p_td_thr_get_info_ID); + + if (p_td_thr_get_info(p_td_thragent_t, &thrinfo) != TD_OK) + return (0); + + jobject threadProxy = env->CallObjectMethod(this_obj, getThreadForThreadId_ID, (jlong)(uintptr_t) thrinfo.ti_tid); + CHECK_EXCEPTION_(1); + env->CallBooleanMethod(list, listAdd_ID, threadProxy); + CHECK_EXCEPTION_(1); + return 0; +} + +static int +fill_load_object_list(void *cd, const prmap_t* pmp, const char* obj_name) { + + if (obj_name) { + DebuggerWithObject* dbgo = (DebuggerWithObject*) cd; + JNIEnv* env = dbgo->env; + jobject this_obj = dbgo->this_obj; + jobject list = dbgo->obj; + + jstring objectName = env->NewStringUTF(obj_name); + CHECK_EXCEPTION_(1); + + jlong mapSize = (jlong) pmp->pr_size; + jobject sharedObject = env->CallObjectMethod(this_obj, createLoadObject_ID, + objectName, mapSize, (jlong)(uintptr_t)pmp->pr_vaddr); + CHECK_EXCEPTION_(1); + env->CallBooleanMethod(list, listAdd_ID, sharedObject); + CHECK_EXCEPTION_(1); + } + + return 0; +} + +static int +fill_cframe_list(void *cd, const prgregset_t regs, uint_t argc, const long *argv) { + DebuggerWith2Objects* dbgo2 = (DebuggerWith2Objects*) cd; + JNIEnv* env = dbgo2->env; + jobject this_obj = dbgo2->this_obj; + jobject curFrame = dbgo2->obj2; + + jint pcRegIndex = env->GetIntField(this_obj, pcRegIndex_ID); + jint fpRegIndex = env->GetIntField(this_obj, fpRegIndex_ID); + + jlong pc = (jlong) (uintptr_t) regs[pcRegIndex]; + jlong fp = (jlong) (uintptr_t) regs[fpRegIndex]; + + dbgo2->obj2 = env->CallObjectMethod(this_obj, createSenderFrame_ID, + curFrame, pc, fp); + CHECK_EXCEPTION_(1); + if (dbgo2->obj == 0) { + dbgo2->obj = dbgo2->obj2; + } + return 0; +} + +// part of the class sharing workaround + +// FIXME: !!HACK ALERT!! + +// The format of sharing achive file header is needed to read shared heap +// file mappings. For now, I am hard coding portion of FileMapHeader here. +// Refer to filemap.hpp. + +// FileMapHeader describes the shared space data in the file to be +// mapped. This structure gets written to a file. It is not a class, so +// that the compilers don't add any compiler-private data to it. + +// Refer to CompactingPermGenGen::n_regions in compactingPermGenGen.hpp +const int NUM_SHARED_MAPS = 4; + +// Refer to FileMapInfo::_current_version in filemap.hpp +const int CURRENT_ARCHIVE_VERSION = 1; + +struct FileMapHeader { + int _magic; // identify file type. + int _version; // (from enum, above.) + size_t _alignment; // how shared archive should be aligned + + + struct space_info { + int _file_offset; // sizeof(this) rounded to vm page size + char* _base; // copy-on-write base address + size_t _capacity; // for validity checking + size_t _used; // for setting space top on read + + bool _read_only; // read only space? + bool _allow_exec; // executable code in space? + + } _space[NUM_SHARED_MAPS]; // was _space[CompactingPermGenGen::n_regions]; + + // Ignore the rest of the FileMapHeader. We don't need those fields here. +}; + +static bool +read_int(struct ps_prochandle* ph, psaddr_t addr, int* pvalue) { + int i; + if (ps_pread(ph, addr, &i, sizeof(i)) == PS_OK) { + *pvalue = i; + return true; + } else { + return false; + } +} + +static bool +read_pointer(struct ps_prochandle* ph, psaddr_t addr, uintptr_t* pvalue) { + uintptr_t uip; + if (ps_pread(ph, addr, &uip, sizeof(uip)) == PS_OK) { + *pvalue = uip; + return true; + } else { + return false; + } +} + +static bool +read_string(struct ps_prochandle* ph, psaddr_t addr, char* buf, size_t size) { + char ch = ' '; + size_t i = 0; + + while (ch != '\0') { + if (ps_pread(ph, addr, &ch, sizeof(ch)) != PS_OK) + return false; + + if (i < size - 1) { + buf[i] = ch; + } else { // smaller buffer + return false; + } + + i++; addr++; + } + + buf[i] = '\0'; + return true; +} + +#define USE_SHARED_SPACES_SYM "UseSharedSpaces" +// mangled symbol name for Arguments::SharedArchivePath +#define SHARED_ARCHIVE_PATH_SYM "__1cJArgumentsRSharedArchivePath_" + +static int +init_classsharing_workaround(void *cd, const prmap_t* pmap, const char* obj_name) { + Debugger* dbg = (Debugger*) cd; + JNIEnv* env = dbg->env; + jobject this_obj = dbg->this_obj; + const char* jvm_name = 0; + if ((jvm_name = strstr(obj_name, "libjvm.so")) != NULL || + (jvm_name = strstr(obj_name, "libjvm_g.so")) != NULL) { + jvm_name = obj_name; + } else { + return 0; + } + + struct ps_prochandle* ph = (struct ps_prochandle*) env->GetLongField(this_obj, p_ps_prochandle_ID); + + // initialize classes[_g].jsa file descriptor field. + dbg->env->SetIntField(this_obj, classes_jsa_fd_ID, -1); + + // check whether class sharing is on by reading variable "UseSharedSpaces" + psaddr_t useSharedSpacesAddr = 0; + ps_pglobal_lookup(ph, jvm_name, USE_SHARED_SPACES_SYM, &useSharedSpacesAddr); + if (useSharedSpacesAddr == 0) { + THROW_NEW_DEBUGGER_EXCEPTION_("can't find 'UseSharedSpaces' flag\n", 1); + } + + // read the value of the flag "UseSharedSpaces" + int value = 0; + if (read_int(ph, useSharedSpacesAddr, &value) != true) { + THROW_NEW_DEBUGGER_EXCEPTION_("can't read 'UseSharedSpaces' flag", 1); + } else if (value == 0) { + print_debug("UseSharedSpaces is false, assuming -Xshare:off!\n"); + return 1; + } + + char classes_jsa[PATH_MAX]; + psaddr_t sharedArchivePathAddrAddr = 0; + ps_pglobal_lookup(ph, jvm_name, SHARED_ARCHIVE_PATH_SYM, &sharedArchivePathAddrAddr); + if (sharedArchivePathAddrAddr == 0) { + print_debug("can't find symbol 'Arguments::SharedArchivePath'\n"); + THROW_NEW_DEBUGGER_EXCEPTION_("can't get shared archive path from debuggee", 1); + } + + uintptr_t sharedArchivePathAddr = 0; + if (read_pointer(ph, sharedArchivePathAddrAddr, &sharedArchivePathAddr) != true) { + print_debug("can't find read pointer 'Arguments::SharedArchivePath'\n"); + THROW_NEW_DEBUGGER_EXCEPTION_("can't get shared archive path from debuggee", 1); + } + + if (read_string(ph, (psaddr_t)sharedArchivePathAddr, classes_jsa, sizeof(classes_jsa)) != true) { + print_debug("can't find read 'Arguments::SharedArchivePath' value\n"); + THROW_NEW_DEBUGGER_EXCEPTION_("can't get shared archive path from debuggee", 1); + } + + print_debug("looking for %s\n", classes_jsa); + + // open the classes[_g].jsa + int fd = pathmap_open(classes_jsa); + if (fd < 0) { + char errMsg[ERR_MSG_SIZE]; + sprintf(errMsg, "can't open shared archive file %s", classes_jsa); + THROW_NEW_DEBUGGER_EXCEPTION_(errMsg, 1); + } else { + print_debug("opened shared archive file %s\n", classes_jsa); + } + + // parse classes[_g].jsa + struct FileMapHeader* pheader = (struct FileMapHeader*) malloc(sizeof(struct FileMapHeader)); + if (pheader == NULL) { + close(fd); + THROW_NEW_DEBUGGER_EXCEPTION_("can't allocate memory for shared file map header", 1); + } + + memset(pheader, 0, sizeof(struct FileMapHeader)); + // read FileMapHeader + size_t n = read(fd, pheader, sizeof(struct FileMapHeader)); + if (n != sizeof(struct FileMapHeader)) { + free(pheader); + close(fd); + char errMsg[ERR_MSG_SIZE]; + sprintf(errMsg, "unable to read shared archive file map header from %s", classes_jsa); + THROW_NEW_DEBUGGER_EXCEPTION_(errMsg, 1); + } + + // check file magic + if (pheader->_magic != 0xf00baba2) { + free(pheader); + close(fd); + char errMsg[ERR_MSG_SIZE]; + sprintf(errMsg, "%s has bad shared archive magic 0x%x, expecting 0xf00baba2", + classes_jsa, pheader->_magic); + THROW_NEW_DEBUGGER_EXCEPTION_(errMsg, 1); + } + + // check version + if (pheader->_version != CURRENT_ARCHIVE_VERSION) { + free(pheader); + close(fd); + char errMsg[ERR_MSG_SIZE]; + sprintf(errMsg, "%s has wrong shared archive version %d, expecting %d", + classes_jsa, pheader->_version, CURRENT_ARCHIVE_VERSION); + THROW_NEW_DEBUGGER_EXCEPTION_(errMsg, 1); + } + + if (_libsaproc_debug) { + for (int m = 0; m < NUM_SHARED_MAPS; m++) { + print_debug("shared file offset %d mapped at 0x%lx, size = %ld, read only? = %d\n", + pheader->_space[m]._file_offset, pheader->_space[m]._base, + pheader->_space[m]._used, pheader->_space[m]._read_only); + } + } + + // FIXME: For now, omitting other checks such as VM version etc. + + // store class archive file fd and map header in debugger object fields + dbg->env->SetIntField(this_obj, classes_jsa_fd_ID, fd); + dbg->env->SetLongField(this_obj, p_file_map_header_ID, (jlong)(uintptr_t) pheader); + return 1; +} + +} // extern "C" + +// error messages for proc_arg_grab failure codes. The messages are +// modified versions of comments against corresponding #defines in +// libproc.h. +static const char* proc_arg_grab_errmsgs[] = { + "", + /* G_NOPROC */ "No such process", + /* G_NOCORE */ "No such core file", + /* G_NOPROCORCORE */ "No such process or core", + /* G_NOEXEC */ "Cannot locate executable file", + /* G_ZOMB */ "Zombie processs", + /* G_PERM */ "No permission to attach", + /* G_BUSY */ "Another process has already attached", + /* G_SYS */ "System process - can not attach", + /* G_SELF */ "Process is self - can't debug myself!", + /* G_INTR */ "Interrupt received while grabbing", + /* G_LP64 */ "debuggee is 64 bit, use java -d64 for debugger", + /* G_FORMAT */ "File is not an ELF format core file - corrupted core?", + /* G_ELF */ "Libelf error while parsing an ELF file", + /* G_NOTE */ "Required PT_NOTE Phdr not present - corrupted core?", +}; + +static void attach_internal(JNIEnv* env, jobject this_obj, jstring cmdLine, jboolean isProcess) { + jboolean isCopy; + int gcode; + const char* cmdLine_cstr = env->GetStringUTFChars(cmdLine, &isCopy); + CHECK_EXCEPTION; + + // some older versions of libproc.so crash when trying to attach 32 bit + // debugger to 64 bit core file. check and throw error. +#ifndef _LP64 + atoi(cmdLine_cstr); + if (errno) { + // core file + int core_fd; + if ((core_fd = open64(cmdLine_cstr, O_RDONLY)) >= 0) { + Elf32_Ehdr e32; + if (pread64(core_fd, &e32, sizeof (e32), 0) == sizeof (e32) && + memcmp(&e32.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0 && + e32.e_type == ET_CORE && e32.e_ident[EI_CLASS] == ELFCLASS64) { + close(core_fd); + THROW_NEW_DEBUGGER_EXCEPTION("debuggee is 64 bit, use java -d64 for debugger"); + } + close(core_fd); + } + // all other conditions are handled by libproc.so. + } +#endif + + // connect to process/core + struct ps_prochandle* ph = proc_arg_grab(cmdLine_cstr, (isProcess? PR_ARG_PIDS : PR_ARG_CORES), PGRAB_FORCE, &gcode); + env->ReleaseStringUTFChars(cmdLine, cmdLine_cstr); + if (! ph) { + if (gcode > 0 && gcode < sizeof(proc_arg_grab_errmsgs)/sizeof(const char*)) { + char errMsg[ERR_MSG_SIZE]; + sprintf(errMsg, "Attach failed : %s", proc_arg_grab_errmsgs[gcode]); + THROW_NEW_DEBUGGER_EXCEPTION(errMsg); + } else { + if (_libsaproc_debug && gcode == G_STRANGE) { + perror("libsaproc DEBUG: "); + } + if (isProcess) { + THROW_NEW_DEBUGGER_EXCEPTION("Not able to attach to process!"); + } else { + THROW_NEW_DEBUGGER_EXCEPTION("Not able to attach to core file!"); + } + } + } + + // even though libproc.so supports 64 bit debugger and 32 bit debuggee, we don't + // support such cross-bit-debugging. check for that combination and throw error. +#ifdef _LP64 + int data_model; + if (ps_pdmodel(ph, &data_model) != PS_OK) { + Prelease(ph, PRELEASE_CLEAR); + THROW_NEW_DEBUGGER_EXCEPTION("can't determine debuggee data model (ILP32? or LP64?)"); + } + if (data_model == PR_MODEL_ILP32) { + Prelease(ph, PRELEASE_CLEAR); + THROW_NEW_DEBUGGER_EXCEPTION("debuggee is 32 bit, use 32 bit java for debugger"); + } +#endif + + env->SetLongField(this_obj, p_ps_prochandle_ID, (jlong)(uintptr_t)ph); + + Debugger dbg; + dbg.env = env; + dbg.this_obj = this_obj; + jthrowable exception = 0; + if (! isProcess) { + /* + * With class sharing, shared perm. gen heap is allocated in with MAP_SHARED|PROT_READ. + * These pages are mapped from the file "classes[_g].jsa". MAP_SHARED pages are not dumped + * in Solaris core.To read shared heap pages, we have to read classes[_g].jsa file. + */ + Pobject_iter(ph, init_classsharing_workaround, &dbg); + exception = env->ExceptionOccurred(); + if (exception) { + env->ExceptionClear(); + detach_internal(env, this_obj); + env->Throw(exception); + return; + } + } + + /* + * Iterate over the process mappings looking + * for libthread and then dlopen the appropriate + * libthread_db and get function pointers. + */ + Pobject_iter(ph, init_libthread_db_ptrs, &dbg); + exception = env->ExceptionOccurred(); + if (exception) { + env->ExceptionClear(); + if (!sa_ignore_threaddb) { + detach_internal(env, this_obj); + env->Throw(exception); + } + return; + } + + // init libthread_db and create thread_db agent + p_td_init_t p_td_init = (p_td_init_t) env->GetLongField(this_obj, p_td_init_ID); + if (p_td_init == 0) { + if (!sa_ignore_threaddb) { + detach_internal(env, this_obj); + } + HANDLE_THREADDB_FAILURE("Did not find libthread in target process/core!"); + } + + if (p_td_init() != TD_OK) { + if (!sa_ignore_threaddb) { + detach_internal(env, this_obj); + } + HANDLE_THREADDB_FAILURE("Can't initialize thread_db!"); + } + + p_td_ta_new_t p_td_ta_new = (p_td_ta_new_t) env->GetLongField(this_obj, p_td_ta_new_ID); + + td_thragent_t *p_td_thragent_t = 0; + if (p_td_ta_new(ph, &p_td_thragent_t) != TD_OK) { + if (!sa_ignore_threaddb) { + detach_internal(env, this_obj); + } + HANDLE_THREADDB_FAILURE("Can't create thread_db agent!"); + } + env->SetLongField(this_obj, p_td_thragent_t_ID, (jlong)(uintptr_t) p_td_thragent_t); + +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: attach0 + * Signature: (Ljava/lang/String;)V + * Description: process detach + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_attach0__Ljava_lang_String_2 + (JNIEnv *env, jobject this_obj, jstring pid) { + attach_internal(env, this_obj, pid, JNI_TRUE); +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: attach0 + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + * Description: core file detach + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2 + (JNIEnv *env, jobject this_obj, jstring executable, jstring corefile) { + // ignore executable file name, libproc.so can detect a.out name anyway. + attach_internal(env, this_obj, corefile, JNI_FALSE); +} + + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: detach0 + * Signature: ()V + * Description: process/core file detach + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_detach0 + (JNIEnv *env, jobject this_obj) { + detach_internal(env, this_obj); +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: getRemoteProcessAddressSize0 + * Signature: ()I + * Description: get process/core address size + */ +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_getRemoteProcessAddressSize0 + (JNIEnv *env, jobject this_obj) { + jlong p_ps_prochandle; + p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + int data_model = PR_MODEL_ILP32; + ps_pdmodel((struct ps_prochandle*) p_ps_prochandle, &data_model); + print_debug("debuggee is %d bit\n", data_model == PR_MODEL_ILP32? 32 : 64); + return (jint) data_model == PR_MODEL_ILP32? 32 : 64; +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: getPageSize0 + * Signature: ()I + * Description: get process/core page size + */ +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_getPageSize0 + (JNIEnv *env, jobject this_obj) { + +/* + We are not yet attached to a java process or core file. getPageSize is called from + the constructor of ProcDebuggerLocal. The following won't work! + + jlong p_ps_prochandle; + p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + CHECK_EXCEPTION_(-1); + struct ps_prochandle* prochandle = (struct ps_prochandle*) p_ps_prochandle; + return (Pstate(prochandle) == PS_DEAD) ? Pgetauxval(prochandle, AT_PAGESZ) + : getpagesize(); + + So even though core may have been generated with a different page size settings, for now + call getpagesize. +*/ + + return getpagesize(); +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: getThreadIntegerRegisterSet0 + * Signature: (J)[J + * Description: get gregset for a given thread specified by thread id + */ +JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_getThreadIntegerRegisterSet0 + (JNIEnv *env, jobject this_obj, jlong tid) { + // map the thread id to thread handle + p_td_ta_map_id2thr_t p_td_ta_map_id2thr = (p_td_ta_map_id2thr_t) env->GetLongField(this_obj, p_td_ta_map_id2thr_ID); + + td_thragent_t* p_td_thragent_t = (td_thragent_t*) env->GetLongField(this_obj, p_td_thragent_t_ID); + if (p_td_thragent_t == 0) { + return 0; + } + + td_thrhandle_t thr_handle; + if (p_td_ta_map_id2thr(p_td_thragent_t, (thread_t) tid, &thr_handle) != TD_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("can't map thread id to thread handle!", 0); + } + + p_td_thr_getgregs_t p_td_thr_getgregs = (p_td_thr_getgregs_t) env->GetLongField(this_obj, p_td_thr_getgregs_ID); + prgregset_t gregs; + p_td_thr_getgregs(&thr_handle, gregs); + + jlongArray res = env->NewLongArray(NPRGREG); + CHECK_EXCEPTION_(0); + jboolean isCopy; + jlong* ptr = env->GetLongArrayElements(res, &isCopy); + for (int i = 0; i < NPRGREG; i++) { + ptr[i] = (jlong) (uintptr_t) gregs[i]; + } + env->ReleaseLongArrayElements(res, ptr, JNI_COMMIT); + return res; +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: fillThreadList0 + * Signature: (Ljava/util/List;)V + * Description: fills thread list of the debuggee process/core + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_fillThreadList0 + (JNIEnv *env, jobject this_obj, jobject list) { + + td_thragent_t* p_td_thragent_t = (td_thragent_t*) env->GetLongField(this_obj, p_td_thragent_t_ID); + if (p_td_thragent_t == 0) { + return; + } + + p_td_ta_thr_iter_t p_td_ta_thr_iter = (p_td_ta_thr_iter_t) env->GetLongField(this_obj, p_td_ta_thr_iter_ID); + + DebuggerWithObject dbgo; + dbgo.env = env; + dbgo.this_obj = this_obj; + dbgo.obj = list; + + p_td_ta_thr_iter(p_td_thragent_t, fill_thread_list, &dbgo, + TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: fillCFrameList0 + * Signature: ([J)Lsun/jvm/hotspot/debugger/proc/ProcCFrame; + * Description: fills CFrame list for a given thread + */ +JNIEXPORT jobject JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_fillCFrameList0 + (JNIEnv *env, jobject this_obj, jlongArray regsArray) { + jlong p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + + DebuggerWith2Objects dbgo2; + dbgo2.env = env; + dbgo2.this_obj = this_obj; + dbgo2.obj = NULL; + dbgo2.obj2 = NULL; + + jboolean isCopy; + jlong* ptr = env->GetLongArrayElements(regsArray, &isCopy); + CHECK_EXCEPTION_(0); + + prgregset_t gregs; + for (int i = 0; i < NPRGREG; i++) { + gregs[i] = (uintptr_t) ptr[i]; + } + + env->ReleaseLongArrayElements(regsArray, ptr, JNI_ABORT); + CHECK_EXCEPTION_(0); + Pstack_iter((struct ps_prochandle*) p_ps_prochandle, gregs, fill_cframe_list, &dbgo2); + return dbgo2.obj; +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: fillLoadObjectList0 + * Signature: (Ljava/util/List;)V + * Description: fills shared objects of the debuggee process/core + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_fillLoadObjectList0 + (JNIEnv *env, jobject this_obj, jobject list) { + DebuggerWithObject dbgo; + dbgo.env = env; + dbgo.this_obj = this_obj; + dbgo.obj = list; + + jlong p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + Pobject_iter((struct ps_prochandle*) p_ps_prochandle, fill_load_object_list, &dbgo); +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: readBytesFromProcess0 + * Signature: (JJ)[B + * Description: read bytes from debuggee process/core + */ +JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_readBytesFromProcess0 + (JNIEnv *env, jobject this_obj, jlong address, jlong numBytes) { + + jbyteArray array = env->NewByteArray(numBytes); + CHECK_EXCEPTION_(0); + jboolean isCopy; + jbyte* bufPtr = env->GetByteArrayElements(array, &isCopy); + CHECK_EXCEPTION_(0); + + jlong p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + ps_err_e ret = ps_pread((struct ps_prochandle*) p_ps_prochandle, + (psaddr_t)address, bufPtr, (size_t)numBytes); + + if (ret != PS_OK) { + // part of the class sharing workaround. try shared heap area + int classes_jsa_fd = env->GetIntField(this_obj, classes_jsa_fd_ID); + if (classes_jsa_fd != -1 && address != (jlong)0) { + print_debug("read failed at 0x%lx, attempting shared heap area\n", (long) address); + + struct FileMapHeader* pheader = (struct FileMapHeader*) env->GetLongField(this_obj, p_file_map_header_ID); + // walk through the shared mappings -- we just have 4 of them. + // so, linear walking is okay. + for (int m = 0; m < NUM_SHARED_MAPS; m++) { + + // We can skip the non-read-only maps. These are mapped as MAP_PRIVATE + // and hence will be read by libproc. Besides, the file copy may be + // stale because the process might have modified those pages. + if (pheader->_space[m]._read_only) { + jlong baseAddress = (jlong) (uintptr_t) pheader->_space[m]._base; + size_t usedSize = pheader->_space[m]._used; + if (address >= baseAddress && address < (baseAddress + usedSize)) { + // the given address falls in this shared heap area + print_debug("found shared map at 0x%lx\n", (long) baseAddress); + + + // If more data is asked than actually mapped from file, we need to zero fill + // till the end-of-page boundary. But, java array new does that for us. we just + // need to read as much as data available. + +#define MIN2(x, y) (((x) < (y))? (x) : (y)) + + jlong diff = address - baseAddress; + jlong bytesToRead = MIN2(numBytes, usedSize - diff); + off_t offset = pheader->_space[m]._file_offset + off_t(diff); + ssize_t bytesRead = pread(classes_jsa_fd, bufPtr, bytesToRead, offset); + if (bytesRead != bytesToRead) { + env->ReleaseByteArrayElements(array, bufPtr, JNI_ABORT); + print_debug("shared map read failed\n"); + return jbyteArray(0); + } else { + print_debug("shared map read succeeded\n"); + env->ReleaseByteArrayElements(array, bufPtr, 0); + return array; + } + } // is in current map + } // is read only map + } // for shared maps + } // classes_jsa_fd != -1 + env->ReleaseByteArrayElements(array, bufPtr, JNI_ABORT); + return jbyteArray(0); + } else { + env->ReleaseByteArrayElements(array, bufPtr, 0); + return array; + } +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: writeBytesToProcess0 + * Signature: (JJ[B)V + * Description: write bytes into debugger process + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_writeBytesToProcess0 + (JNIEnv *env, jobject this_obj, jlong address, jlong numBytes, jbyteArray data) { + jlong p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + jboolean isCopy; + jbyte* ptr = env->GetByteArrayElements(data, &isCopy); + CHECK_EXCEPTION; + + if (ps_pwrite((struct ps_prochandle*) p_ps_prochandle, address, ptr, numBytes) != PS_OK) { + env->ReleaseByteArrayElements(data, ptr, JNI_ABORT); + THROW_NEW_DEBUGGER_EXCEPTION("Process write failed!"); + } + + env->ReleaseByteArrayElements(data, ptr, JNI_ABORT); +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: suspend0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_suspend0 + (JNIEnv *env, jobject this_obj) { + jlong p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + // for now don't check return value. revisit this again. + Pstop((struct ps_prochandle*) p_ps_prochandle, 1000); +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: resume0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_resume0 + (JNIEnv *env, jobject this_obj) { + jlong p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + // for now don't check return value. revisit this again. + Psetrun((struct ps_prochandle*) p_ps_prochandle, 0, PRCFAULT|PRSTOP); +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: lookupByName0 + * Signature: (Ljava/lang/String;Ljava/lang/String;)J + * Description: symbol lookup by name +*/ +JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_lookupByName0 + (JNIEnv *env, jobject this_obj, jstring objectName, jstring symbolName) { + jlong p_ps_prochandle; + p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + + jboolean isCopy; + const char* objectName_cstr = NULL; + if (objectName != NULL) { + objectName_cstr = env->GetStringUTFChars(objectName, &isCopy); + CHECK_EXCEPTION_(0); + } else { + objectName_cstr = PR_OBJ_EVERY; + } + + const char* symbolName_cstr = env->GetStringUTFChars(symbolName, &isCopy); + CHECK_EXCEPTION_(0); + + psaddr_t symbol_addr = (psaddr_t) 0; + ps_pglobal_lookup((struct ps_prochandle*) p_ps_prochandle, objectName_cstr, + symbolName_cstr, &symbol_addr); + + if (symbol_addr == 0) { + print_debug("lookup for %s in %s failed\n", symbolName_cstr, objectName_cstr); + } + + if (objectName_cstr != PR_OBJ_EVERY) { + env->ReleaseStringUTFChars(objectName, objectName_cstr); + } + env->ReleaseStringUTFChars(symbolName, symbolName_cstr); + return (jlong) (uintptr_t) symbol_addr; +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: lookupByAddress0 + * Signature: (J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol; + * Description: lookup symbol name for a given address + */ +JNIEXPORT jobject JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_lookupByAddress0 + (JNIEnv *env, jobject this_obj, jlong address) { + jlong p_ps_prochandle; + p_ps_prochandle = env->GetLongField(this_obj, p_ps_prochandle_ID); + + char nameBuf[SYMBOL_BUF_SIZE + 1]; + GElf_Sym sym; + int res = Plookup_by_addr((struct ps_prochandle*) p_ps_prochandle, (uintptr_t) address, + nameBuf, sizeof(nameBuf), &sym); + if (res != 0) { // failed + return 0; + } + + jstring resSym = env->NewStringUTF(nameBuf); + CHECK_EXCEPTION_(0); + + return env->CallObjectMethod(this_obj, createClosestSymbol_ID, resSym, (address - sym.st_value)); +} + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: demangle0 + * Signature: (Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_demangle0 + (JNIEnv *env, jobject this_object, jstring name) { + jboolean isCopy; + const char* ptr = env->GetStringUTFChars(name, &isCopy); + char buf[2*SYMBOL_BUF_SIZE + 1]; + jstring res = 0; + if (cplus_demangle((char*) ptr, buf, sizeof(buf)) != DEMANGLE_ESPACE) { + res = env->NewStringUTF(buf); + } else { + res = name; + } + env->ReleaseStringUTFChars(name, ptr); + return res; +} + +typedef int (*find_file_hook_t)(const char *, int elf_checksum); + +/* + * Class: sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal + * Method: initIDs + * Signature: ()V + * Description: get JNI ids for fields and methods of ProcDebuggerLocal class + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_initIDs + (JNIEnv *env, jclass clazz) { + _libsaproc_debug = getenv("LIBSAPROC_DEBUG") != NULL; + if (_libsaproc_debug) { + // propagate debug mode to libproc.so + static const char* var = "LIBPROC_DEBUG=1"; + putenv((char*)var); + } + + void* libproc_handle = dlopen("libproc.so", RTLD_LAZY | RTLD_GLOBAL); + if (libproc_handle == 0) + THROW_NEW_DEBUGGER_EXCEPTION("can't load libproc.so, if you are using Solaris 5.7 or below, copy libproc.so from 5.8!"); + + // If possible, set shared object find file hook. + void (*set_hook)(find_file_hook_t) = (void(*)(find_file_hook_t))dlsym(libproc_handle, "Pset_find_file_hook"); + if (set_hook) { + // we found find file hook symbol, set up our hook function. + set_hook(find_file_hook); + } else if (getenv(SA_ALTROOT)) { + printf("libsaproc WARNING: %s set, but can't set file hook. " \ + "Did you use right version of libproc.so?\n", SA_ALTROOT); + } + + p_ps_prochandle_ID = env->GetFieldID(clazz, "p_ps_prochandle", "J"); + CHECK_EXCEPTION; + + libthread_db_handle_ID = env->GetFieldID(clazz, "libthread_db_handle", "J"); + CHECK_EXCEPTION; + + p_td_thragent_t_ID = env->GetFieldID(clazz, "p_td_thragent_t", "J"); + CHECK_EXCEPTION; + + p_td_init_ID = env->GetFieldID(clazz, "p_td_init", "J"); + CHECK_EXCEPTION; + + p_td_ta_new_ID = env->GetFieldID(clazz, "p_td_ta_new", "J"); + CHECK_EXCEPTION; + + p_td_ta_delete_ID = env->GetFieldID(clazz, "p_td_ta_delete", "J"); + CHECK_EXCEPTION; + + p_td_ta_thr_iter_ID = env->GetFieldID(clazz, "p_td_ta_thr_iter", "J"); + CHECK_EXCEPTION; + + p_td_thr_get_info_ID = env->GetFieldID(clazz, "p_td_thr_get_info", "J"); + CHECK_EXCEPTION; + + p_td_ta_map_id2thr_ID = env->GetFieldID(clazz, "p_td_ta_map_id2thr", "J"); + CHECK_EXCEPTION; + + p_td_thr_getgregs_ID = env->GetFieldID(clazz, "p_td_thr_getgregs", "J"); + CHECK_EXCEPTION; + + getThreadForThreadId_ID = env->GetMethodID(clazz, + "getThreadForThreadId", "(J)Lsun/jvm/hotspot/debugger/ThreadProxy;"); + CHECK_EXCEPTION; + + pcRegIndex_ID = env->GetFieldID(clazz, "pcRegIndex", "I"); + CHECK_EXCEPTION; + + fpRegIndex_ID = env->GetFieldID(clazz, "fpRegIndex", "I"); + CHECK_EXCEPTION; + + createSenderFrame_ID = env->GetMethodID(clazz, + "createSenderFrame", "(Lsun/jvm/hotspot/debugger/proc/ProcCFrame;JJ)Lsun/jvm/hotspot/debugger/proc/ProcCFrame;"); + CHECK_EXCEPTION; + + createLoadObject_ID = env->GetMethodID(clazz, + "createLoadObject", "(Ljava/lang/String;JJ)Lsun/jvm/hotspot/debugger/cdbg/LoadObject;"); + CHECK_EXCEPTION; + + createClosestSymbol_ID = env->GetMethodID(clazz, + "createClosestSymbol", "(Ljava/lang/String;J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol;"); + CHECK_EXCEPTION; + + listAdd_ID = env->GetMethodID(env->FindClass("java/util/List"), "add", "(Ljava/lang/Object;)Z"); + CHECK_EXCEPTION; + + // part of the class sharing workaround + classes_jsa_fd_ID = env->GetFieldID(clazz, "classes_jsa_fd", "I"); + CHECK_EXCEPTION; + p_file_map_header_ID = env->GetFieldID(clazz, "p_file_map_header", "J"); + CHECK_EXCEPTION; +} diff --git a/hotspot/agent/src/os/win32/BasicList.hpp b/hotspot/agent/src/os/win32/BasicList.hpp new file mode 100644 index 00000000000..eebf8ebed9a --- /dev/null +++ b/hotspot/agent/src/os/win32/BasicList.hpp @@ -0,0 +1,66 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _BASIC_LIST_ +#define _BASIC_LIST_ + +#include + +template +class BasicList { +protected: + typedef std::vector InternalListType; + InternalListType internalList; + +public: + BasicList() { + } + virtual ~BasicList() { + } + + void add(T arg) { + internalList.push_back(arg); + } + + bool remove(T arg) { + for (InternalListType::iterator iter = internalList.begin(); + iter != internalList.end(); iter++) { + if (*iter == arg) { + internalList.erase(iter); + return true; + } + } + return false; + } + + int size() { + return internalList.size(); + } + + T get(int index) { + return internalList[index]; + } +}; + +#endif // #defined _BASIC_LIST_ diff --git a/hotspot/agent/src/os/win32/Buffer.cpp b/hotspot/agent/src/os/win32/Buffer.cpp new file mode 100644 index 00000000000..24fb71077b1 --- /dev/null +++ b/hotspot/agent/src/os/win32/Buffer.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "Buffer.hpp" + +#include + +Buffer::Buffer(int bufSize) { + buf = new char[bufSize]; + sz = bufSize; + fill = 0; + drain = 0; +} + +Buffer::~Buffer() { + delete[] buf; +} + +char* +Buffer::fillPos() { + return buf + fill; +} + +int +Buffer::remaining() { + return sz - fill; +} + +int +Buffer::size() { + return sz; +} + +bool +Buffer::incrFillPos(int amt) { + if (fill + amt >= sz) { + return false; + } + fill += amt; + return true; +} + +int +Buffer::readByte() { + if (drain < fill) { + return buf[drain++] & 0xFF; + } else { + return -1; + } +} + +int +Buffer::readBytes(char* data, int len) { + int numRead = 0; + while (numRead < len) { + int c = readByte(); + if (c < 0) break; + data[numRead++] = (char) c; + } + return numRead; +} + +char* +Buffer::drainPos() { + return buf + drain; +} + +int +Buffer::drainRemaining() { + return fill - drain; +} + +bool +Buffer::incrDrainPos(int amt) { + if (drainRemaining() < amt) { + return false; + } + drain += amt; + return true; +} + +void +Buffer::compact() { + // Copy down data + memmove(buf, buf + drain, fill - drain); + // Adjust positions + fill -= drain; + drain = 0; +} diff --git a/hotspot/agent/src/os/win32/Buffer.hpp b/hotspot/agent/src/os/win32/Buffer.hpp new file mode 100644 index 00000000000..63a33c9609f --- /dev/null +++ b/hotspot/agent/src/os/win32/Buffer.hpp @@ -0,0 +1,68 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _BUFFER_ +#define _BUFFER_ + +// A Buffer is the backing store for the IOBuf abstraction and +// supports producer-consumer filling and draining. + +class Buffer { +public: + Buffer(int bufSize); + ~Buffer(); + + char* fillPos(); // Position of the place where buffer should be filled + int remaining(); // Number of bytes that can be placed starting at fillPos + int size(); // Size of the buffer + // Move up fill position by amount (decreases remaining()); returns + // false if not enough space + bool incrFillPos(int amt); + + // Read single byte (0..255); returns -1 if no data available. + int readByte(); + // Read multiple bytes, non-blocking (this buffer does not define a + // fill mechanism), into provided buffer. Returns number of bytes read. + int readBytes(char* buf, int len); + + // Access to drain position. Be very careful using this. + char* drainPos(); + int drainRemaining(); + bool incrDrainPos(int amt); + + // Compact buffer, removing already-consumed input. This must be + // called periodically to yield the illusion of an infinite buffer. + void compact(); + +private: + Buffer(const Buffer&); + Buffer& operator=(const Buffer&); + + char* buf; + int sz; + int fill; + int drain; +}; + +#endif // #defined _BUFFER_ diff --git a/hotspot/agent/src/os/win32/Dispatcher.cpp b/hotspot/agent/src/os/win32/Dispatcher.cpp new file mode 100644 index 00000000000..6f09ed2de6a --- /dev/null +++ b/hotspot/agent/src/os/win32/Dispatcher.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include +#include "dispatcher.hpp" + +const char* CMD_ASCII = "ascii"; +const char* CMD_UNICODE = "unicode"; +const char* CMD_PROCLIST = "proclist"; +const char* CMD_ATTACH = "attach"; +const char* CMD_DETACH = "detach"; +const char* CMD_LIBINFO = "libinfo"; +const char* CMD_PEEK = "peek"; +const char* CMD_POKE = "poke"; +const char* CMD_THREADLIST = "threadlist"; +const char* CMD_DUPHANDLE = "duphandle"; +const char* CMD_CLOSEHANDLE = "closehandle"; +const char* CMD_GETCONTEXT = "getcontext"; +const char* CMD_SETCONTEXT = "setcontext"; +const char* CMD_SELECTORENTRY = "selectorentry"; +const char* CMD_SUSPEND = "suspend"; +const char* CMD_RESUME = "resume"; +const char* CMD_POLLEVENT = "pollevent"; +const char* CMD_CONTINUEEVENT = "continueevent"; +const char* CMD_EXIT = "exit"; + +// Uncomment the #define below to get messages on stderr +// #define DEBUGGING + +void +Dispatcher::dispatch(char* cmd, Handler* handler) { + if (!strncmp(cmd, CMD_ASCII, strlen(CMD_ASCII))) { + handler->ascii(cmd + strlen(CMD_ASCII)); + + } else if (!strncmp(cmd, CMD_UNICODE, strlen(CMD_UNICODE))) { + handler->unicode(cmd + strlen(CMD_UNICODE)); + + } else if (!strncmp(cmd, CMD_PROCLIST, strlen(CMD_PROCLIST))) { + handler->procList(cmd + strlen(CMD_PROCLIST)); + + } else if (!strncmp(cmd, CMD_ATTACH, strlen(CMD_ATTACH))) { + handler->attach(cmd + strlen(CMD_ATTACH)); + + } else if (!strncmp(cmd, CMD_DETACH, strlen(CMD_DETACH))) { + handler->detach(cmd + strlen(CMD_DETACH)); + + } else if (!strncmp(cmd, CMD_LIBINFO, strlen(CMD_LIBINFO))) { + handler->libInfo(cmd + strlen(CMD_LIBINFO)); + + } else if (!strncmp(cmd, CMD_PEEK, strlen(CMD_PEEK))) { + handler->peek(cmd + strlen(CMD_PEEK)); + + } else if (!strncmp(cmd, CMD_POKE, strlen(CMD_POKE))) { + handler->poke(cmd + strlen(CMD_POKE)); + + } else if (!strncmp(cmd, CMD_THREADLIST, strlen(CMD_THREADLIST))) { + handler->threadList(cmd + strlen(CMD_THREADLIST)); + + } else if (!strncmp(cmd, CMD_DUPHANDLE, strlen(CMD_DUPHANDLE))) { + handler->dupHandle(cmd + strlen(CMD_DUPHANDLE)); + + } else if (!strncmp(cmd, CMD_CLOSEHANDLE, strlen(CMD_CLOSEHANDLE))) { + handler->closeHandle(cmd + strlen(CMD_CLOSEHANDLE)); + + } else if (!strncmp(cmd, CMD_GETCONTEXT, strlen(CMD_GETCONTEXT))) { + handler->getContext(cmd + strlen(CMD_GETCONTEXT)); + + } else if (!strncmp(cmd, CMD_SETCONTEXT, strlen(CMD_SETCONTEXT))) { + handler->setContext(cmd + strlen(CMD_SETCONTEXT)); + + } else if (!strncmp(cmd, CMD_SELECTORENTRY, strlen(CMD_SELECTORENTRY))) { + handler->selectorEntry(cmd + strlen(CMD_SELECTORENTRY)); + + } else if (!strncmp(cmd, CMD_SUSPEND, strlen(CMD_SUSPEND))) { + handler->suspend(cmd + strlen(CMD_SUSPEND)); + + } else if (!strncmp(cmd, CMD_RESUME, strlen(CMD_RESUME))) { + handler->resume(cmd + strlen(CMD_RESUME)); + + } else if (!strncmp(cmd, CMD_POLLEVENT, strlen(CMD_POLLEVENT))) { + handler->pollEvent(cmd + strlen(CMD_POLLEVENT)); + + } else if (!strncmp(cmd, CMD_CONTINUEEVENT, strlen(CMD_CONTINUEEVENT))) { + handler->continueEvent(cmd + strlen(CMD_CONTINUEEVENT)); + + } else if (!strcmp(cmd, CMD_EXIT)) { + handler->exit(cmd + strlen(CMD_EXIT)); + } + +#ifdef DEBUGGING + else fprintf(stderr, "Ignoring illegal command \"%s\"\n", cmd); +#endif +} diff --git a/hotspot/agent/src/os/win32/Dispatcher.hpp b/hotspot/agent/src/os/win32/Dispatcher.hpp new file mode 100644 index 00000000000..af07c0749f0 --- /dev/null +++ b/hotspot/agent/src/os/win32/Dispatcher.hpp @@ -0,0 +1,38 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _DISPATCHER_ +#define _DISPATCHER_ + +#include "Handler.hpp" + +/** This class understands the commands supported by the system and + calls the appropriate handler routines. */ + +class Dispatcher { +public: + static void dispatch(char* cmd, Handler* handler); +}; + +#endif // #defined _DISPATCHER_ diff --git a/hotspot/agent/src/os/win32/Handler.hpp b/hotspot/agent/src/os/win32/Handler.hpp new file mode 100644 index 00000000000..b9b05ef7349 --- /dev/null +++ b/hotspot/agent/src/os/win32/Handler.hpp @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _HANDLER_ +#define _HANDLER_ + +/** An abstract base class encapsulating the handlers for all commands + understood by the system. */ +class Handler { +public: + virtual void ascii(char* arg) = 0; + virtual void unicode(char* arg) = 0; + virtual void procList(char* arg) = 0; + virtual void attach(char* arg) = 0; + virtual void detach(char* arg) = 0; + virtual void libInfo(char* arg) = 0; + virtual void peek(char* arg) = 0; + virtual void poke(char* arg) = 0; + virtual void threadList(char* arg) = 0; + virtual void dupHandle(char* arg) = 0; + virtual void closeHandle(char* arg) = 0; + virtual void getContext(char* arg) = 0; + virtual void setContext(char* arg) = 0; + virtual void selectorEntry(char* arg) = 0; + virtual void suspend(char* arg) = 0; + virtual void resume(char* arg) = 0; + virtual void pollEvent(char* arg) = 0; + virtual void continueEvent(char* arg) = 0; + virtual void exit(char* arg) = 0; +}; + +#endif // #defined _HANDLER_ diff --git a/hotspot/agent/src/os/win32/IOBuf.cpp b/hotspot/agent/src/os/win32/IOBuf.cpp new file mode 100644 index 00000000000..41a3e2bcc2f --- /dev/null +++ b/hotspot/agent/src/os/win32/IOBuf.cpp @@ -0,0 +1,490 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include + +// This file is currently used for os/solaris/agent too. At some point in time +// the source will be reorganized to avoid these ifdefs. + +#ifdef __sun + #include + #include + #include +#endif + +#include "IOBuf.hpp" + +// Formats for printing pointers +#ifdef _LP64 +# define INTPTR_FORMAT "0x%016lx" +#else /* ! _LP64 */ +# define INTPTR_FORMAT "0x%08lx" +#endif /* _LP64 */ + +// Uncomment the #define below to get messages on stderr +// #define DEBUGGING + +IOBuf::IOBuf(int inLen, int outLen) { + inBuf = new Buffer(inLen); + outBuf = new Buffer(outLen); + fd = INVALID_SOCKET; + outHandle = NULL; + usingSocket = true; + reset(); +} + +IOBuf::~IOBuf() { + delete inBuf; + delete outBuf; +} + +void +IOBuf::setSocket(SOCKET sock) { + fd = sock; + usingSocket = true; +} + +// Reading/writing files is only needed and used on windows. +#ifdef WIN32 +void +IOBuf::setOutputFileHandle(HANDLE handle) { + outHandle = handle; + usingSocket = false; +} +#endif + +void +IOBuf::reset() { + gotDataLastTime = false; + state = TEXT_STATE; + binPos = 0; + binLength = 0; +} + +IOBuf::ReadLineResult +IOBuf::tryReadLine() { + return doReadLine(false); +} + +char* +IOBuf::readLine() { + ReadLineResult rr = doReadLine(true); + if (rr != RL_GOT_DATA) { + return NULL; + } + return getLine(); +} + +IOBuf::ReadLineResult +IOBuf::doReadLine(bool shouldWait) { + + if (!usingSocket) { + return IOBuf::RL_ERROR; + } + + if (gotDataLastTime) { + curLine.clear(); + } + + int c; + do { + c = readChar(shouldWait); + if (c >= 0) { + Action act = processChar((char) c); + if (act == GOT_LINE) { + curLine.push_back('\0'); + gotDataLastTime = true; + return IOBuf::RL_GOT_DATA; + } else if (act == SKIP_EOL_CHAR) { + // Do nothing + } else { + curLine.push_back((char) c); + } + } + } while (shouldWait || c >= 0); + + gotDataLastTime = false; + return IOBuf::RL_NO_DATA; +} + +bool +IOBuf::flushImpl(bool moreDataToCome) { + int numWritten = 0; + +#ifdef WIN32 + // When running on Windows and using IOBufs for inter-process + // communication, we need to write metadata into the stream + // indicating how many bytes are coming down. Five bytes are written + // per flush() call, four containing the integer number of bytes + // coming (not including the five-byte header) and one (a 0 or 1) + // indicating whether there is more data coming. + if (!usingSocket) { + int numToWrite = outBuf->drainRemaining(); + char moreToCome = (moreDataToCome ? 1 : 0); + DWORD numBytesWritten; + if (!WriteFile(outHandle, &numToWrite, sizeof(int), &numBytesWritten, NULL)) { + return false; + } + if (numBytesWritten != sizeof(int)) { + return false; + } + if (!WriteFile(outHandle, &moreToCome, 1, &numBytesWritten, NULL)) { + return false; + } + if (numBytesWritten != 1) { + return false; + } + } +#endif + + while (outBuf->drainRemaining() != 0) { +#ifdef DEBUGGING + fprintf(stderr, "Flushing %d bytes\n", outBuf->drainRemaining()); +#endif + if (usingSocket) { + numWritten = send(fd, outBuf->drainPos(), outBuf->drainRemaining(), 0); + } else { +#ifdef WIN32 + DWORD numBytesWritten; + if (!WriteFile(outHandle, outBuf->drainPos(), outBuf->drainRemaining(), &numBytesWritten, NULL)) { + numWritten = -1; + } else { + numWritten = numBytesWritten; + } +#endif + } + if (numWritten != -1) { +#ifdef DEBUGGING + fprintf(stderr, "Flushed %d bytes\n", numWritten); +#endif + outBuf->incrDrainPos(numWritten); + } else { + return false; + } + } + + outBuf->compact(); + + return true; +} + +int +IOBuf::readChar(bool block) { + do { + int c = inBuf->readByte(); + if (c >= 0) { + return c; + } + // See whether we need to compact the input buffer + if (inBuf->remaining() < inBuf->size() / 2) { + inBuf->compact(); + } + // See whether socket is ready + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (block || select(1 + fd, &fds, NULL, NULL, &timeout) > 0) { + if (block || FD_ISSET(fd, &fds)) { +#ifdef DEBUGGING + int b = (block ? 1 : 0); + fprintf(stderr, "calling recv: block = %d\n", b); +#endif + // Read data from socket + int numRead = recv(fd, inBuf->fillPos(), inBuf->remaining(), 0); + if (numRead < 0) { +#ifdef DEBUGGING + fprintf(stderr, "recv failed\n"); +#endif + return -1; + } + inBuf->incrFillPos(numRead); + } + } + } while (block); + + return inBuf->readByte(); +} + +char* +IOBuf::getLine() { +#ifdef DEBUGGING + fprintf(stderr, "Returning (first 10 chars) \"%.10s\"\n", curLine.begin()); +#endif + return curLine.begin(); +} + +bool +IOBuf::flush() { + return flushImpl(false); +} + +bool +IOBuf::writeString(const char* str) { + int len = strlen(str); + + if (len > outBuf->size()) { + return false; + } + + if (len > outBuf->remaining()) { + if (!flushImpl(true)) { + return false; + } + } + + // NOTE we do not copy the null terminator of the string. + + strncpy(outBuf->fillPos(), str, len); + outBuf->incrFillPos(len); + return true; +} + +bool +IOBuf::writeInt(int val) { + char buf[128]; + sprintf(buf, "%d", val); + return writeString(buf); +} + +bool +IOBuf::writeUnsignedInt(unsigned int val) { + char buf[128]; + sprintf(buf, "%u", val); + return writeString(buf); +} + +bool +IOBuf::writeBoolAsInt(bool val) { + if (val) { + return writeString("1"); + } else { + return writeString("0"); + } +} + +bool +IOBuf::writeAddress(void* val) { + char buf[128]; + sprintf(buf, INTPTR_FORMAT, val); + return writeString(buf); +} + +bool +IOBuf::writeSpace() { + return writeString(" "); +} + +bool +IOBuf::writeEOL() { + return writeString("\n\r"); +} + +bool +IOBuf::writeBinChar(char c) { + return writeBinBuf((char*) &c, sizeof(c)); +} + +bool +IOBuf::writeBinUnsignedShort(unsigned short i) { + i = htons(i); + return writeBinBuf((char*) &i, sizeof(i)); +} + +bool +IOBuf::writeBinUnsignedInt(unsigned int i) { + i = htonl(i); + return writeBinBuf((char*) &i, sizeof(i)); +} + +bool +IOBuf::writeBinBuf(char* buf, int size) { + while (size > 0) { + int spaceRemaining = outBuf->remaining(); + if (spaceRemaining == 0) { + if (!flushImpl(true)) { + return false; + } + spaceRemaining = outBuf->remaining(); + } + int toCopy = (size > spaceRemaining) ? spaceRemaining : size; + memcpy(outBuf->fillPos(), buf, toCopy); + outBuf->incrFillPos(toCopy); + buf += toCopy; + size -= toCopy; + if (size > 0) { + if (!flushImpl(true)) { + return false; + } + } + } + return true; +} + +#ifdef WIN32 +IOBuf::FillState +IOBuf::fillFromFileHandle(HANDLE fh, DWORD* numBytesRead) { + int totalToRead; + char moreToCome; + + outBuf->compact(); + + DWORD numRead; + if (!ReadFile(fh, &totalToRead, sizeof(int), &numRead, NULL)) { + return FAILED; + } + if (numRead != sizeof(int)) { + return FAILED; + } + if (!ReadFile(fh, &moreToCome, 1, &numRead, NULL)) { + return FAILED; + } + if (numRead != 1) { + return FAILED; + } + if (outBuf->remaining() < totalToRead) { + return FAILED; + } + + int tmp = totalToRead; + + while (totalToRead > 0) { + if (!ReadFile(fh, outBuf->fillPos(), totalToRead, &numRead, NULL)) { + return FAILED; + } + outBuf->incrFillPos((int) numRead); + totalToRead -= numRead; + } + + *numBytesRead = tmp; + return ((moreToCome == 0) ? DONE : MORE_DATA_PENDING); +} +#endif + +bool +IOBuf::isBinEscapeChar(char c) { + return (c == '|'); +} + +IOBuf::Action +IOBuf::processChar(char c) { + Action action = NO_ACTION; + switch (state) { + case TEXT_STATE: { + // Looking for text char, bin escape char, or EOL + if (isBinEscapeChar(c)) { +#ifdef DEBUGGING + fprintf(stderr, "[a: '%c'] ", inBuf[0]); +#endif + binPos = 0; +#ifdef DEBUGGING + fprintf(stderr, "[b: '%c'] ", inBuf[0]); +#endif + binLength = 0; +#ifdef DEBUGGING + fprintf(stderr, "[c: '%c'] ", inBuf[0]); +#endif + state = BIN_STATE; +#ifdef DEBUGGING + fprintf(stderr, "[d: '%c'] ", inBuf[0]); +#endif +#ifdef DEBUGGING + fprintf(stderr, "\nSwitching to BIN_STATE\n"); +#endif + } else if (isEOL(c)) { + state = EOL_STATE; + action = GOT_LINE; +#ifdef DEBUGGING + fprintf(stderr, "\nSwitching to EOL_STATE (GOT_LINE)\n"); +#endif + } +#ifdef DEBUGGING + else { + fprintf(stderr, "'%c' ", c); + fflush(stderr); + } +#endif + break; + } + + case BIN_STATE: { + // Seeking to finish read of input + if (binPos < 4) { + int cur = c & 0xFF; + binLength <<= 8; + binLength |= cur; + ++binPos; + } else { +#ifdef DEBUGGING + fprintf(stderr, "Reading binary byte %d of %d\n", + binPos - 4, binLength); +#endif + ++binPos; + if (binPos == 4 + binLength) { + state = TEXT_STATE; +#ifdef DEBUGGING + fprintf(stderr, "Switching to TEXT_STATE\n"); +#endif + } + } + break; + } + + case EOL_STATE: { + // More EOL characters just cause us to re-enter this state + if (isEOL(c)) { + action = SKIP_EOL_CHAR; + } else if (isBinEscapeChar(c)) { + binPos = 0; + binLength = 0; + state = BIN_STATE; + } else { + state = TEXT_STATE; +#ifdef DEBUGGING + fprintf(stderr, "'%c' ", c); + fflush(stderr); +#endif + } + break; + } + + } // switch + + return action; +} + + +bool +IOBuf::isEOL(char c) { +#ifdef WIN32 + return ((c == '\n') || (c == '\r')); +#elif defined(__sun) + return c == '\n'; +#else + #error Please port isEOL() to your platform + return false; +#endif +} diff --git a/hotspot/agent/src/os/win32/IOBuf.hpp b/hotspot/agent/src/os/win32/IOBuf.hpp new file mode 100644 index 00000000000..a6e84b884c6 --- /dev/null +++ b/hotspot/agent/src/os/win32/IOBuf.hpp @@ -0,0 +1,222 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _IO_BUF_ +#define _IO_BUF_ + +// This file is currently used for os/solaris/agent/ too. At some point in time +// the source will be reorganized to avoid these ifdefs. +// Note that this class can read/write from a file as well as a socket. This +// file capability is only implemented on win32. + +#ifdef WIN32 + #include +#else + #include + #include + // These are from win32 winsock2.h + typedef unsigned int SOCKET; + typedef void * HANDLE; + typedef unsigned long DWORD; + #define INVALID_SOCKET (SOCKET)(~0) +#endif + +#include +#include "Buffer.hpp" + +/** Manages an input/output buffer pair for a socket or file handle. */ +class IOBuf { +public: + IOBuf(int inBufLen, int outBufLen); + ~IOBuf(); + + enum ReadLineResult { + RL_GOT_DATA, + RL_NO_DATA, + RL_ERROR + }; + + /** Change the socket with which this buffer is associated */ + void setSocket(SOCKET sock); + + // Reading/writing files is only supported on windows. +#ifdef WIN32 + /** Change the output file handle with which this buffer is + associated. Currently IOBufs can not be used to read from a file + handle. */ + void setOutputFileHandle(HANDLE handle); +#endif + + /** Reset the input and output buffers, without flushing the output + data to the socket */ + void reset(); + + /** Try to read a line of data from the given socket without + blocking. If was able to read a complete line of data, returns a + character pointer to the beginning of the (null-terminated) + string. If not, returns NULL, but maintains enough state that + subsequent calls to tryReadLine() will not ignore the data + already read. NOTE: this skips end-of-line characters (typically + CR/LF) as defined by "isEOL()". When switching back and forth + between binary and text modes, to be sure no data is lost, pad + the beginning and end of the binary transmission with bytes + which can not be confused with these characters. */ + ReadLineResult tryReadLine(); + + /** Read a line of data from the given socket, blocking until a + line, including EOL, appears. Return the line, or NULL if + something goes wrong. */ + char *readLine(); + + /** Get the pointer to the beginning of the (null-terminated) line. + This should only be called if tryReadLine() has returned + RL_GOT_DATA. This sets the "parsing cursor" to the beginning of + the line. */ + char* getLine(); + + // NOTE: any further data-acquisition routines must ALWAYS call + // fixupData() at the beginning! + + //---------------------------------------------------------------------- + // Output routines + // + + /** Flush the output buffer to the socket. Returns true if + succeeded, false if write error occurred. */ + bool flush(); + + /** Write the given string to the output buffer. May flush if output + buffer becomes too full to store the data. Not guaranteed to + work if string is longer than the size of the output buffer. + Does not include the null terminator of the string. Returns true + if succeeded, false if write error occurred. */ + bool writeString(const char* str); + + /** Write the given int to the output buffer. May flush if output + buffer becomes too full to store the data. Returns true if + succeeded, false if write error occurred. */ + bool writeInt(int val); + + /** Write the given unsigned int to the output buffer. May flush if + output buffer becomes too full to store the data. Returns true + if succeeded, false if write error occurred. */ + bool writeUnsignedInt(unsigned int val); + + /** Write the given boolean to the output buffer. May flush if + output buffer becomes too full to store the data. Returns true + if succeeded, false if write error occurred. */ + bool writeBoolAsInt(bool val); + + /** Write the given address to the output buffer. May flush if + output buffer becomes too full to store the data. Returns true + if succeeded, false if write error occurred. */ + bool writeAddress(void* val); + + /** Writes a space to the output buffer. May flush if output buffer + becomes too full to store the data. Returns true if succeeded, + false if write error occurred. */ + bool writeSpace(); + + /** Writes an end-of-line sequence to the output buffer. May flush + if output buffer becomes too full to store the data. Returns + true if succeeded, false if write error occurred. */ + bool writeEOL(); + + /** Writes a binary character to the output buffer. */ + bool writeBinChar(char c); + + /** Writes a binary unsigned short in network (big-endian) byte + order to the output buffer. */ + bool writeBinUnsignedShort(unsigned short i); + + /** Writes a binary unsigned int in network (big-endian) byte order + to the output buffer. */ + bool writeBinUnsignedInt(unsigned int i); + + /** Writes a binary buffer to the output buffer. */ + bool writeBinBuf(char* buf, int size); + +#ifdef WIN32 + enum FillState { + DONE = 1, + MORE_DATA_PENDING = 2, + FAILED = 3 + }; + + /** Very specialized routine; fill the output buffer from the given + file handle. Caller is responsible for ensuring that there is + data to be read on the file handle. */ + FillState fillFromFileHandle(HANDLE fh, DWORD* numRead); +#endif + + /** Binary utility routine (for poke) */ + static bool isBinEscapeChar(char c); + +private: + IOBuf(const IOBuf&); + IOBuf& operator=(const IOBuf&); + + // Returns -1 if non-blocking and no data available + int readChar(bool block); + // Line-oriented reading + std::vector curLine; + bool gotDataLastTime; + + ReadLineResult doReadLine(bool); + + bool flushImpl(bool moreDataToCome); + + SOCKET fd; + HANDLE outHandle; + bool usingSocket; + + // Buffers + Buffer* inBuf; + Buffer* outBuf; + + // Simple finite-state machine to handle binary data + enum State { + TEXT_STATE, + BIN_STATE, + EOL_STATE + }; + enum Action { + NO_ACTION, + GOT_LINE, // TEXT_STATE -> EOL_STATE transition + SKIP_EOL_CHAR // EOL_STATE -> EOL_STATE transition + }; + + State state; + Action processChar(char c); + + // Handling incoming binary buffers (poke command) + int binPos; // Number of binary characters read so far; + // total number to read is binLength + 4 + int binLength; // Number of binary characters in message; + // not valid until binPos >= 4 + + bool isEOL(char c); +}; + +#endif // #defined _IO_BUF_ diff --git a/hotspot/agent/src/os/win32/LockableList.hpp b/hotspot/agent/src/os/win32/LockableList.hpp new file mode 100644 index 00000000000..a18e11eba83 --- /dev/null +++ b/hotspot/agent/src/os/win32/LockableList.hpp @@ -0,0 +1,54 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _LOCKABLE_LIST_ +#define _LOCKABLE_LIST_ + +#include +#include "BasicList.hpp" + +template +class LockableList : public BasicList { +private: + CRITICAL_SECTION crit; + +public: + LockableList() { + InitializeCriticalSection(&crit); + } + + ~LockableList() { + DeleteCriticalSection(&crit); + } + + void lock() { + EnterCriticalSection(&crit); + } + + void unlock() { + LeaveCriticalSection(&crit); + } +}; + +#endif // #defined _LOCKABLE_LIST_ diff --git a/hotspot/agent/src/os/win32/Makefile b/hotspot/agent/src/os/win32/Makefile new file mode 100644 index 00000000000..ba9e6560374 --- /dev/null +++ b/hotspot/agent/src/os/win32/Makefile @@ -0,0 +1,80 @@ +# +# Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +SERVER=SwDbgSrv.exe +SUBPROCESS=SwDbgSub.exe + +SERVER_SOURCES = \ + Buffer.cpp \ + Dispatcher.cpp \ + initWinsock.cpp \ + IOBuf.cpp \ + ioUtils.cpp \ + isNT4.cpp \ + nt4internals.cpp \ + procList.cpp \ + Reaper.cpp \ + SwDbgSrv.cpp \ + serverLists.cpp \ + toolHelp.cpp + +SUBPROCESS_SOURCES = \ + SwDbgSub.cpp \ + Buffer.cpp \ + IOBuf.cpp \ + isNT4.cpp \ + libInfo.cpp \ + Monitor.cpp \ + nt4internals.cpp \ + toolHelp.cpp + +SERVER_OBJS = $(SERVER_SOURCES:.cpp=.obj) +SUBPROCESS_OBJS = $(SUBPROCESS_SOURCES:.cpp=.obj) + +CPP=cl.exe +LINK32=link.exe + +# These do not need to be optimized (don't run a lot of code) and it +# will be useful to have the assertion checks in place + +CFLAGS=/nologo /MD /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c + +LIBS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib \ + ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib \ + winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib \ + odbccp32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 + +default: $(SERVER) $(SUBPROCESS) + +$(SERVER): $(SERVER_OBJS) + $(LINK32) /out:$@ $(SERVER_OBJS) $(LIBS) + +$(SUBPROCESS): $(SUBPROCESS_OBJS) + $(LINK32) /out:$@ $(SUBPROCESS_OBJS) $(LIBS) + +clean: + rm -f *.obj *.idb *.pch *.pdb *.ncb *.opt *.plg *.exe *.ilk + +.cpp.obj: + @ $(CPP) $(CFLAGS) /o $@ $< diff --git a/hotspot/agent/src/os/win32/Message.hpp b/hotspot/agent/src/os/win32/Message.hpp new file mode 100644 index 00000000000..9f79d3b6f49 --- /dev/null +++ b/hotspot/agent/src/os/win32/Message.hpp @@ -0,0 +1,123 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _MESSAGE_ +#define _MESSAGE_ + +// These are the commands sent from the server to the child processes +// over the child processes' stdin pipes. A subset of the commands +// understood by the overall system, these require responses from the +// child process. Having a data structure rather than sending text +// simplifies parsing on the child side. The child replies by sending +// back fully-formatted replies which are copied by the server process +// to the clients' sockets. + +struct PeekArg { + DWORD address; + DWORD numBytes; +}; + +// NOTE: when sending a PokeArg to the child process, we handle the +// buffer specially +struct PokeArg { + DWORD address; + DWORD numBytes; + void* data; +}; + +// Used for continueevent +struct BoolArg { + bool val; +}; + +// Used for duphandle, closehandle, and getcontext +struct HandleArg { + HANDLE handle; +}; + +// Used for setcontext +const int NUM_REGS_IN_CONTEXT = 22; +struct SetContextArg { + HANDLE handle; + DWORD Eax; + DWORD Ebx; + DWORD Ecx; + DWORD Edx; + DWORD Esi; + DWORD Edi; + DWORD Ebp; + DWORD Esp; + DWORD Eip; + DWORD Ds; + DWORD Es; + DWORD Fs; + DWORD Gs; + DWORD Cs; + DWORD Ss; + DWORD EFlags; + DWORD Dr0; + DWORD Dr1; + DWORD Dr2; + DWORD Dr3; + DWORD Dr6; + DWORD Dr7; +}; + +// Used for selectorentry +struct SelectorEntryArg { + HANDLE handle; + DWORD selector; +}; + +struct Message { + typedef enum { + ATTACH, + DETACH, + LIBINFO, + PEEK, + POKE, + THREADLIST, + DUPHANDLE, + CLOSEHANDLE, + GETCONTEXT, + SETCONTEXT, + SELECTORENTRY, + SUSPEND, + RESUME, + POLLEVENT, + CONTINUEEVENT + } Type; + + Type type; + union { + PeekArg peekArg; + PokeArg pokeArg; + BoolArg boolArg; + HandleArg handleArg; + SetContextArg setContextArg; + SelectorEntryArg selectorArg; + }; +}; + +#endif // #defined _MESSAGE_ diff --git a/hotspot/agent/src/os/win32/Monitor.cpp b/hotspot/agent/src/os/win32/Monitor.cpp new file mode 100644 index 00000000000..5825ab62b06 --- /dev/null +++ b/hotspot/agent/src/os/win32/Monitor.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include +#include "Monitor.hpp" + +Monitor::Monitor() { + _lock_count = -1; // No threads have entered the critical section + _owner = NULL; + _lock_event = CreateEvent(NULL, false, false, NULL); + _wait_event = CreateEvent(NULL, true, false, NULL); + _counter = 0; + _tickets = 0; + _waiters = 0; +} + +Monitor::~Monitor() { + assert(_owner == NULL); // Otherwise, owned monitor being deleted + assert(_lock_count == -1); // Otherwise, monitor being deleted with non -1 lock count + CloseHandle(_lock_event); + CloseHandle(_wait_event); +} + +void +Monitor::lock() { + if (InterlockedIncrement(&_lock_count) == 0) { + // Success, we now own the lock + } else { + DWORD dwRet = WaitForSingleObject((HANDLE)_lock_event, INFINITE); + assert(dwRet == WAIT_OBJECT_0); // Unexpected return value from WaitForSingleObject + } + assert(owner() == NULL); // Otherwise, lock count and owner are inconsistent + setOwner(GetCurrentThread()); +} + +void +Monitor::unlock() { + setOwner(NULL); + if (InterlockedDecrement(&_lock_count) >= 0) { + // Wake a waiting thread up + DWORD dwRet = SetEvent(_lock_event); + assert(dwRet != 0); // Unexpected return value from SetEvent + } +} + +bool +Monitor::wait(long timeout) { + assert(owner() != NULL); + assert(owner() == GetCurrentThread()); + + // 0 means forever. Convert to Windows specific code. + DWORD timeout_value = (timeout == 0) ? INFINITE : timeout; + DWORD which; + + long c = _counter; + bool retry = false; + + _waiters++; + // Loop until condition variable is signaled. The event object is + // set whenever the condition variable is signaled, and tickets will + // reflect the number of threads which have been notified. The counter + // field is used to make sure we don't respond to notifications that + // have occurred *before* we started waiting, and is incremented each + // time the condition variable is signaled. + + while (true) { + + // Leave critical region + unlock(); + + // If this is a retry, let other low-priority threads have a chance + // to run. Make sure that we sleep outside of the critical section. + if (retry) { + Sleep(1); + } else { + retry = true; + } + + which = WaitForSingleObject(_wait_event, timeout_value); + // Enter critical section + lock(); + + if (_tickets != 0 && _counter != c) break; + + if (which == WAIT_TIMEOUT) { + --_waiters; + return true; + } + } + _waiters--; + + // If this was the last thread to be notified, then we need to reset + // the event object. + if (--_tickets == 0) { + ResetEvent(_wait_event); + } + + return false; +} + +// Notify a single thread waiting on this monitor +bool +Monitor::notify() { + assert(ownedBySelf()); // Otherwise, notify on unknown thread + + if (_waiters > _tickets) { + if (!SetEvent(_wait_event)) { + return false; + } + _tickets++; + _counter++; + } + + return true; +} + +// Notify all threads waiting on this monitor +bool +Monitor::notifyAll() { + assert(ownedBySelf()); // Otherwise, notifyAll on unknown thread + + if (_waiters > 0) { + if (!SetEvent(_wait_event)) { + return false; + } + _tickets = _waiters; + _counter++; + } + + return true; +} + +HANDLE +Monitor::owner() { + return _owner; +} + +void +Monitor::setOwner(HANDLE owner) { + if (owner != NULL) { + assert(_owner == NULL); // Setting owner thread of already owned monitor + assert(owner == GetCurrentThread()); // Else should not be doing this + } else { + HANDLE oldOwner = _owner; + assert(oldOwner != NULL); // Removing the owner thread of an unowned mutex + assert(oldOwner == GetCurrentThread()); + } + _owner = owner; +} + +bool +Monitor::ownedBySelf() { + return (_owner == GetCurrentThread()); +} diff --git a/hotspot/agent/src/os/win32/Monitor.hpp b/hotspot/agent/src/os/win32/Monitor.hpp new file mode 100644 index 00000000000..2eeacd24ccd --- /dev/null +++ b/hotspot/agent/src/os/win32/Monitor.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _MONITOR_ +#define _MONITOR_ + +#include + +class Monitor { +public: + Monitor(); + ~Monitor(); + + void lock(); + void unlock(); + // Default time is forever (i.e, zero). Returns true if it times-out, otherwise + // false. + bool wait(long timeout = 0); + bool notify(); + bool notifyAll(); + +private: + HANDLE owner(); + void setOwner(HANDLE owner); + bool ownedBySelf(); + + HANDLE _owner; + long _lock_count; + HANDLE _lock_event; // Auto-reset event for blocking in lock() + HANDLE _wait_event; // Manual-reset event for notifications + long _counter; // Current number of notifications + long _waiters; // Number of threads waiting for notification + long _tickets; // Number of waiters to be notified +}; + + +#endif // #defined _MONITOR_ diff --git a/hotspot/agent/src/os/win32/README-commands.txt b/hotspot/agent/src/os/win32/README-commands.txt new file mode 100644 index 00000000000..0a1cb13a59f --- /dev/null +++ b/hotspot/agent/src/os/win32/README-commands.txt @@ -0,0 +1,246 @@ +This debug server uses a largely text-based protocol, except for +certain bulk data transfer operations. All text is in single-byte +US-ASCII except for the strings returned in "proclist". + +NOTE that the character '|' (vertical bar) is used as an escape +character to switch the incoming data stream to the debug server into +binary mode, so no text command may contain that character. + +Commands understood: + +ascii ::= + + Changes to ASCII mode. This affects all outgoing strings. At + startup the system is in unicode mode. + +unicode ::= + + Changes to UNICODE mode. This affects all outgoing strings. This + is the default mode upon startup. + +proclist ::= + [ []...]... + + Returns integer indicating number of processes to follow, followed + by (pid, name) pairs. Names are given by (charSize, numChars, + [char_t]...) tuples; charSize indicates the size of each character + in bytes, numChars the number of characters in the string, and + name the raw data for the string. Each individual character of the + string, if multi-byte, is transmitted in network byte order. + numChars and name are guaranteed to be separated by precisely one + US-ASCII space. If process list is not available because of + limitations of the underlying operating system, number of + processes returned is 0. + +attach ::= + + Attempts to attach to the specified process. Returns 1 if + successful, 0 if not. Will fail if already attached or if the + process ID does not exist. Attaching to a process causes the + process to be suspended. + +detach ::= + + Detaches from the given process. Attaching and detaching multiple + times during a debugging session is allowed. Detaching causes the + process to resume execution. + +libinfo ::= + [ []...
]... + + May only be called once attached and the target process must be + suspended; otherwise, returns 0. Returns list of the full path + names of all of the loaded modules (including the executable + image) in the target process, as well as the base address at which + each module was relocated. See proclist for format of strings, but + NOTE that charSize is ALWAYS 1 for this particular routine, + regardless of the setting of ASCII/UNICODE. + +peek
::= + B + [ []...]... + + NOTE that the binary portion of this message is prefixed by the + uppercase US-ASCII letter 'B', allowing easier synchronization by + clients. There is no data between the 'B' and the rest of the + message. + + May only be called once attached. Reads the address space of the + target process starting at the given address (see below for format + specifications) and extending the given number of bytes. Whether + the read succeeded is indicated by a single byte containing a 1 or + 0 (success or failure). If successful, the return result is given + in a sequence of ranges. _len_, the length of each range, is + indicated by a 32-bit unsigned integer transmitted with big-endian + byte ordering (i.e., most significant byte first). _isMapped_ + indicates whether the range is mapped or unmapped in the target + process's address space, and will contain the value 1 or 0 for + mapped or unmapped, respectively. If the range is mapped, + _isMapped_ is followed by _data_, containing the raw binary data + for the range. The sum of all ranges' lengths is guaranteed to be + equivalent to the number of bytes requested. + +poke
|[ []] ::= + + + NOTE that the binary portion of this message is prefixed by the + uppercase US-ASCII character '|' (vertical bar), allowing easier + synchronization by the server. There is no data between the '|' + and the rest of the message. ('B' is not used here because + addresses can contain that letter; no alphanumeric characters are + used because some of the parsing routines are used by the Solaris + SA port, and in that port any alphanumeric character can show up + as a part of a symbol being looked up.) + + May only be called once attached. Writes the address space of the + target process starting at the given address (see below for format + specifications), extending the given number of bytes, and + containing the given data. The number of bytes is a 32-bit + unsigned integer transmitted with big-endian byte ordering (i.e., + most significant byte first). This is followed by the raw binary + data to be placed at that address. The number of bytes of data + must match the number of bytes specified in the message. + + Returns true if the write succeeded; false if it failed, for + example because a portion of the region was not mapped in the + target address space. + +threadlist ::= [
...] + + May only be called once attached and the target process must be + suspended; otherwise, returns 0. If available, returns handles for + all of the threads in the target process. These handles may be + used as arguments to the getcontext and selectorentry + commands. They do not need to be (and should not be) duplicated + via the duphandle command and must not be closed via the + closehandle command. + +duphandle
::= + [
] + + Duplicates a HANDLE read from the target process's address space. + HANDLE is a Windows construct (typically typedef'd to void *). + The returned handle should ultimately be closed via the + closehandle command; failing to do so can cause resource leaks. + + The purpose of this command is to allow the debugger to read the + value of a thread handle from the target process and query its + register set and thread selector entries via the getcontext and + selectorentry commands, below; such use implies that the target + program has its own notion of the thread list, and further, that + the debugger has a way of locating that thread list. + +closehandle
::= + + Closes a handle retrieved via the duphandle command, above. + +getcontext
::= [] + + Returns the context for the given thread. The handle must either + be one of the handles returned from the threadlist command or the + result of duplicating a thread handle out of the target process + via the duphandle command. The target process must be suspended. + + The context is returned as a series of hex values which represent + the following x86 registers in the following order: + EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EIP, DS, ES, FS, GS, + CS, SS, EFLAGS, DR0, DR1, DR2, DR3, DR6, DR7 + + FIXME: needs to be generalized and/or specified for other + architectures. + +setcontext
::= + + Sets the context of the given thread. The target process must be + suspended. See the getcontext command for the ordering of the + registers in the context. + + Even if the setcontext command succeeds, some of the bits in some + of the registers (like the global enable bits in the debug + registers) may be overridden by the operating system. To ensure + the debugger's notion of the register set is up to date, it is + recommended to follow up a setcontext with a getcontext. + +selectorentry
::= + + [
+
+
] + + Retrieves a descriptor table entry for the given thread and + selector. This data structure allows conversion of a + segment-relative address to a linear virtual address. It is most + useful for locating the Thread Information Block for a given + thread handle to be able to find that thread's ID, to be able to + understand whether two different thread handles in fact refer to + the same underlying thread. + + This command will only work on the X86 architecture and will + return false for the success flag (with no additional information + sent) on other architectures. + +suspend ::= + + Suspends the target process. Must be attached to a target process. + A process is suspended when attached to via the attach command. If + the target process is already suspended then this command has no + effect. + +resume ::= + + Resumes the target process without detaching from it. Must be + attached to a target process. After resuming a target process, the + debugger client must be prepared to poll for events from the + target process fairly frequently in order for execution in the + target process to proceed normally. If the target process is + already resumed then this command has no effect. + +pollevent ::= + [
] + + Additional entries in result for given eventCode: + + LOAD/UNLOAD_DLL_DEBUG_EVENT:
+ EXCEPTION_DEBUG_EVENT:
+ + Additional entries for given exceptionCode: + + EXCEPTION_ACCESS_VIOLATION:
+ + + + Polls once to see whether a debug event has been generated by the + target process. If none is present, returns 0 immediately. + Otherwise, returns 1 along with a series of textual information + about the event. The event is not cleared, and the thread resumed, + until the continueevent command is sent, or the debugger client + detaches from the target process. + + Typically a debugger client will suspend the target process upon + reception of a debug event. Otherwise, it is not guaranteed that + all threads will be suspended upon reception of a debug event, and + any operations requiring that threads be suspended (including + fetching the context for the thread which generated the event) + will fail. + +continueevent ::= + + Indicates that the current debug event has been used by the + debugger client and that the target process should be resumed. The + passEventToClient flag indicates whether the event should be + propagated to the target process. Breakpoint and single-step + events should not be propagated to the target. Returns false if + there was no pending event, true otherwise. + +exit + + Exits this debugger session. + +Format specifications: + +// Data formats and example values: + ::= end of line (typically \n on Unix platforms, or \n\r on Windows) +
::= 0x12345678[9ABCDEF0] /* up to 64-bit hex value */ + ::= 5 /* up to 32-bit integer number; no leading sign */ + ::= 1 /* ASCII '0' or '1' */ + ::=
... diff --git a/hotspot/agent/src/os/win32/README.txt b/hotspot/agent/src/os/win32/README.txt new file mode 100644 index 00000000000..e470b471bef --- /dev/null +++ b/hotspot/agent/src/os/win32/README.txt @@ -0,0 +1,64 @@ +This is a "Simple Windows Debug Server" written for the purpose of +enabling the Serviceability Agent on Win32. It has backends both for +Windows NT 4.0 (using internal Windows APIs for a few routines) as +well as for 95/98/ME/2000 via the Tool Help APIs. + +The reason this debug server is necessary is that the Win32 debug APIs +by design tear down the target process when the debugger exits (see +knowledge base article Q164205 on msdn.microsoft.com). On Solaris, one +can attach to and detach from a process with no effect; this is key to +allowing dbx and gcore to work. + +The Simple Windows Debug Server effectively implements attach/detach +functionality for arbitrary debug clients. This allows the SA to +attach non-destructively to a process, and will enable gcore for Win32 +to be written shortly. While the debugger (the "client" in all of the +source code) is attached, the target process is suspended. (Note that +the debug server could be extended to support resumption of the target +process and transmission of debug events over to the debugger, but +this has been left for the future.) + +The makefile (type "nmake") builds two executables: SwDbgSrv.exe, +which is the server process, and SwDbgSub.exe, which is forked by the +server and should not be directly invoked by the user. + +The intent is that these two executables can be installed into +C:\WINNT\SYSTEM32 and SwDbgSrv installed to run as a service (on NT), +for example using ServiceInstaller (http://www.kcmultimedia.com/smaster/). +However, SwDbgSrv can also be run from the command line. It generates +no text output unless the source code is changed to enable debugging +printouts. As long as any processes which have been attached to by the +SA are alive, the SwDbgSrv and any forked SwDbgSub processes must be +left running. Terminating them will cause termination of the target +processes. + +The debug server opens port 27000 and accepts incoming connections +from localhost only. The security model assumes that if one can run a +process on the given machine then one basically has access to most or +all of the machine's facilities; this seems to be in line with the +standard Windows security model. The protocol used is text-based, so +one can debug the debug server using telnet. See README-commands.txt +for documentation on the supported commands. + +Testing indicates that the performance impact of attaching to a +process (and therefore permanently attaching a debugger) is minimal. +Some serious performance problems had been seen which ultimately +appeared to be a lack of physical memory on the machine running the +system. + +Bugs: + +This debug server is fundamentally incompatible with the Visual C++ +debugger. Once the debug server is used to attach to a process, the +Visual C++ IDE will not be able to attach to the same process (even if +the debug server is "detached" from that process). Note that this +system is designed to work with the same primitives that C and C++ +debuggers use (like "symbol lookup" and "read from process memory") +and exposes these primitives to Java, so in the long term we could +solve this problem by implementing platform-specific debug symbol +parsing and a platform-independent C++ debugger in Java. + +Note: + +The files IOBuf.cpp and IOBuf.hpp are also used in +building src/os/solaris/agent. diff --git a/hotspot/agent/src/os/win32/Reaper.cpp b/hotspot/agent/src/os/win32/Reaper.cpp new file mode 100644 index 00000000000..565aa9d675b --- /dev/null +++ b/hotspot/agent/src/os/win32/Reaper.cpp @@ -0,0 +1,159 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include "Reaper.hpp" + +using namespace std; + +Reaper::Reaper(ReaperCB* cb) { + InitializeCriticalSection(&crit); + event = CreateEvent(NULL, TRUE, FALSE, NULL); + this->cb = cb; + + active = false; + shouldShutDown = false; +} + +bool +Reaper::start() { + bool result = false; + + EnterCriticalSection(&crit); + + if (!active) { + DWORD id; + HANDLE reaper = CreateThread(NULL, 0, &Reaper::reaperThreadEntry, + this, 0, &id); + if (reaper != NULL) { + result = true; + } + } + + LeaveCriticalSection(&crit); + + return result; +} + +bool +Reaper::stop() { + bool result = false; + + EnterCriticalSection(&crit); + + if (active) { + shouldShutDown = true; + SetEvent(event); + while (active) { + Sleep(1); + } + shouldShutDown = false; + result = true; + } + + LeaveCriticalSection(&crit); + + return result; +} + +void +Reaper::registerProcess(HANDLE processHandle, void* userData) { + ProcessInfo info; + + info.handle = processHandle; + info.userData = userData; + + EnterCriticalSection(&crit); + + procInfo.push_back(info); + SetEvent(event); + + LeaveCriticalSection(&crit); +} + +void +Reaper::reaperThread() { + while (!shouldShutDown) { + // Take atomic snapshot of the current process list and user data + EnterCriticalSection(&crit); + + int num = procInfo.size(); + HANDLE* handleList = new HANDLE[1 + num]; + void** dataList = new void*[num]; + for (int i = 0; i < num; i++) { + handleList[i] = procInfo[i].handle; + dataList[i] = procInfo[i].userData; + } + + LeaveCriticalSection(&crit); + + // Topmost handle becomes the event object, so other threads can + // signal this one to notice differences in the above list (or + // shut down) + handleList[num] = event; + + // Wait for these objects + DWORD idx = WaitForMultipleObjects(1 + num, handleList, + FALSE, INFINITE); + if ((idx >= WAIT_OBJECT_0) && (idx <= WAIT_OBJECT_0 + num)) { + idx -= WAIT_OBJECT_0; + if (idx < num) { + // A process exited (i.e., it wasn't that we were woken up + // just because the event went off) + (*cb)(dataList[idx]); + // Remove this process from the list (NOTE: requires that + // ordering does not change, i.e., that all additions are to + // the back of the process list) + EnterCriticalSection(&crit); + + std::vector::iterator iter = procInfo.begin(); + iter += idx; + procInfo.erase(iter); + + LeaveCriticalSection(&crit); + } else { + // Notification from other thread + ResetEvent(event); + } + } else { + // Unexpected return value. For now, warn. + cerr << "Reaper::reaperThread(): unexpected return value " + << idx << " from WaitForMultipleObjects" << endl; + } + + // Clean up these lists + delete[] handleList; + delete[] dataList; + } + + // Time to shut down + active = false; +} + +DWORD WINAPI +Reaper::reaperThreadEntry(LPVOID data) { + Reaper* reaper = (Reaper*) data; + reaper->reaperThread(); + return 0; +} diff --git a/hotspot/agent/src/os/win32/Reaper.hpp b/hotspot/agent/src/os/win32/Reaper.hpp new file mode 100644 index 00000000000..047e3e112fa --- /dev/null +++ b/hotspot/agent/src/os/win32/Reaper.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _REAPER_ +#define _REAPER_ + +#include +#include + +typedef void ReaperCB(void* userData); + +/** A Reaper maintains a thread which waits for child processes to + terminate; upon termination it calls a user-specified ReaperCB to + clean up resources associated with those child processes. */ + +class Reaper { +private: + Reaper& operator=(const Reaper&); + Reaper(const Reaper&); + +public: + Reaper(ReaperCB*); + ~Reaper(); + + // Start the reaper thread. + bool start(); + + // Stop the reaper thread. This is called automatically in the + // reaper's destructor. It is not thread safe and should be called + // by at most one thread at a time. + bool stop(); + + // Register a given child process with the reaper. This should be + // called by the application's main thread. When that process + // terminates, the cleanup callback will be called with the + // specified userData in the context of the reaper thread. Callbacks + // are guaranteed to be called serially, so they can safely refer to + // static data as well as the given user data. + void registerProcess(HANDLE processHandle, void* userData); + +private: + // For thread safety of register() + CRITICAL_SECTION crit; + + ReaperCB* cb; + + // State variables + volatile bool active; + volatile bool shouldShutDown; + + struct ProcessInfo { + HANDLE handle; + void* userData; + }; + + // Bookkeeping + std::vector procInfo; + + // Synchronization between application thread and reaper thread + HANDLE event; + + // Entry point for reaper thread + void reaperThread(); + + // Static function which is actual thread entry point + static DWORD WINAPI reaperThreadEntry(LPVOID data); +}; + +#endif // #defined _REAPER_ diff --git a/hotspot/agent/src/os/win32/SwDbgSrv.cpp b/hotspot/agent/src/os/win32/SwDbgSrv.cpp new file mode 100644 index 00000000000..b17cc66a9a2 --- /dev/null +++ b/hotspot/agent/src/os/win32/SwDbgSrv.cpp @@ -0,0 +1,1266 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A Simple Windows Debug Server. +// +// This software provides a socket-based debug server which uses +// mostly ASCII protocols to communicate with its clients. Since the +// Windows security model is largely based around being able to run +// programs on the machine, this server only accepts connections +// coming from localhost. +// +// When run as a service (under Windows NT), this software provides +// clients the ability to attach to and detach from processes without +// killing those processes. Ordinarily this is forbidden by the +// Windows debugging APIs (although more recent debugging environments +// from Microsoft seem to have circumvented this restriction, perhaps +// in a different way). This is achieved by forking a persistent +// subprocess for each debugging session which remains alive as long +// as the target process is. +// +// At this point the client can read information out of the target +// process's address space. Future work includes exposing more +// functionality like writing to the remote address space and +// suspending and resuming threads. + +#include +#include +#include +// Must come before everything else +#include +#include +#include "Dispatcher.hpp" +#include "Handler.hpp" +#include "initWinsock.hpp" +#include "ioUtils.hpp" +#include "isNT4.hpp" +#include "Message.hpp" +#include "nt4internals.hpp" +#include "ports.h" +#include "procList.hpp" +#include "serverLists.hpp" +#include "Reaper.hpp" + +// Uncomment the #define below to get messages on stderr +// #define DEBUGGING + +using namespace std; + +static ChildList childList; +static ClientList clientList; +static Reaper* reaper = NULL; + +// Needed prototypes +void shutdownChild(ChildInfo* childInfo); +void detachClient(ClientInfo* clientInfo); +void shutdownClient(ClientInfo* clientInfo); + +char * +longToDotFormat(long addr) +{ + char *temp_s = new char[20]; + + sprintf(temp_s, "%d.%d.%d.%d", ((addr & 0xff000000) >> 24), + ((addr & 0x00ff0000) >> 16), ((addr & 0x0000ff00) >> 8), + (addr & 0x000000ff)); + + return temp_s; +} + +// NOTE that we do this query every time. It is a bad idea to cache IP +// addresses. For example, we might be hosted on a machine using DHCP +// and the connection addresses might change over time. (Yes, this +// actually happened.) +bool +isConnectionOkay(ULONG connAddr) { + if (connAddr == INADDR_LOOPBACK) { + return true; + } + + const int MAXNAME = 1024; + char myname[MAXNAME]; + gethostname(myname, MAXNAME); + struct hostent* myInfo = gethostbyname(myname); + if (myInfo == NULL) { +#ifdef DEBUGGING + cerr << "My host information was null" << endl; +#endif + } else { + // Run down the list of IP addresses for myself + assert(myInfo->h_length == sizeof(ULONG)); +#ifdef DEBUGGING + cerr << "My known IP addresses: " << endl; +#endif + for (char** pp = myInfo->h_addr_list; *pp != NULL; pp++) { + char* p = *pp; + ULONG altAddr = ntohl(*((ULONG*) p)); +#ifdef DEBUGGING + char* name = longToDotFormat(altAddr); + cerr << name << endl; + delete[] name; +#endif + if (altAddr == connAddr) { +#ifdef DEBUGGING + cerr << "FOUND" << endl; +#endif + return true; + } + } +#ifdef DEBUGGING + cerr << "Done." << endl; +#endif + } + + return false; +} + +SOCKET +setupListeningSocket(short port) { + SOCKET listening = socket(AF_INET, SOCK_STREAM, 0); + if (listening == INVALID_SOCKET) { + cerr << "Error creating listening socket" << endl; + exit(1); + } + + int reuseAddress = 1; + if (setsockopt(listening, SOL_SOCKET, SO_REUSEADDR, + (char *)&reuseAddress, sizeof(reuseAddress)) == -1) { + cerr << "Error reusing address" << endl; + exit(1); + } + + struct sockaddr_in serverInfo; + + memset((char *)&serverInfo, 0, sizeof(serverInfo)); + serverInfo.sin_addr.s_addr = INADDR_ANY; + serverInfo.sin_family = AF_INET; + serverInfo.sin_port = htons(port); + + if (bind(listening, (struct sockaddr *) &serverInfo, sizeof(serverInfo)) < 0) { + cerr << "Error binding socket" << endl; + exit(1); + } + + if (listen(listening, 5) < 0) { + cerr << "Error listening" << endl; + exit(1); + } + + return listening; +} + +/** Accepts a connection from the given listening socket, but only if + the connection came from localhost. Returns INVALID_SOCKET if the + connection came from any other IP address or if an error occurred + during the call to accept(). */ +SOCKET +acceptFromLocalhost(SOCKET listening) { + struct sockaddr_in peerAddr; + int peerAddrLen = sizeof(peerAddr); + SOCKET fd = accept(listening, (sockaddr*) &peerAddr, &peerAddrLen); + if (fd == INVALID_SOCKET) { + return fd; + } + + if (!isConnectionOkay(ntohl(peerAddr.sin_addr.s_addr))) { + // Reject connections from other machines for security purposes. + // The Windows security model seems to assume one user per + // machine, and that security is compromised if another user is + // able to run executables on the given host. (If these + // assumptions are not strict enough, we will have to change + // this.) + shutdown(fd, SD_BOTH); + closesocket(fd); + return INVALID_SOCKET; + } + + // Disable TCP buffering on all sockets. We send small amounts of + // data back and forth and don't want buffering. + int buffer_val = 1; + if (setsockopt(fd, IPPROTO_IP, TCP_NODELAY, + (char *) &buffer_val, sizeof(buffer_val)) < 0) { + shutdown(fd, SD_BOTH); + closesocket(fd); + } + + return fd; +} + +void +reapCB(void* arg) { + ChildInfo* info = (ChildInfo*) arg; + ListsLocker ll; + DWORD pid = info->getPid(); + shutdownChild(info); +#ifdef DEBUGGING + cerr << "Reaped child for process " << pid << endl; +#endif +} + +/** Starts a child process with stdin and stdout redirected to pipes, + handles to which are returned. auxHandle1 and auxHandle2 should be + closed as well when the child process exits. Returns false if + process creation failed. */ +bool +startChildProcess(DWORD pidToDebug, + DWORD childStdinBufSize, + DWORD childStdoutBufSize, + LPHANDLE childProcessHandle, + LPHANDLE writeToStdinHandle, + LPHANDLE readFromStdoutHandle, + LPHANDLE auxHandle1, + LPHANDLE auxHandle2) { + // Code adapted from Microsoft example + // "Creating a Child Process with Redirected Input and Output" + + SECURITY_ATTRIBUTES saAttr; + BOOL fSuccess; + + HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, + hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup, + hSaveStdin, hSaveStdout; + + // Set the bInheritHandle flag so pipe handles are inherited. + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + // The steps for redirecting child process's STDOUT: + // 1. Save current STDOUT, to be restored later. + // 2. Create anonymous pipe to be STDOUT for child process. + // 3. Set STDOUT of the parent process to be write handle to + // the pipe, so it is inherited by the child process. + // 4. Create a noninheritable duplicate of the read handle and + // close the inheritable read handle. + + // Save the handle to the current STDOUT. + hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); + // Create a pipe for the child process's STDOUT. + if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, childStdoutBufSize)) { + return false; + } + // Set a write handle to the pipe to be STDOUT. + if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) { + return false; + } + // Create noninheritable read handle and close the inheritable read + // handle. + fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, + GetCurrentProcess(), &hChildStdoutRdDup, + 0, FALSE, + DUPLICATE_SAME_ACCESS); + if( !fSuccess ) { + return false; + } + CloseHandle(hChildStdoutRd); + + // The steps for redirecting child process's STDIN: + // 1. Save current STDIN, to be restored later. + // 2. Create anonymous pipe to be STDIN for child process. + // 3. Set STDIN of the parent to be the read handle to the + // pipe, so it is inherited by the child process. + // 4. Create a noninheritable duplicate of the write handle, + // and close the inheritable write handle. + // Save the handle to the current STDIN. + hSaveStdin = GetStdHandle(STD_INPUT_HANDLE); + // Create a pipe for the child process's STDIN. + if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, childStdinBufSize)) { + return false; + } + // Set a read handle to the pipe to be STDIN. + if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) { + return false; + } + // Duplicate the write handle to the pipe so it is not inherited. + fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr, + GetCurrentProcess(), &hChildStdinWrDup, 0, + FALSE, // not inherited + DUPLICATE_SAME_ACCESS); + if (! fSuccess) { + return false; + } + CloseHandle(hChildStdinWr); + + // Create the child process + char cmdLine[256]; + sprintf(cmdLine, "SwDbgSub.exe %u", pidToDebug); + PROCESS_INFORMATION procInfo; + STARTUPINFO startInfo; + memset((char*) &startInfo, 0, sizeof(startInfo)); + startInfo.cb = sizeof(startInfo); + BOOL res = CreateProcess(NULL, + cmdLine, + NULL, + NULL, + TRUE, // inherit handles: important + 0, + NULL, + NULL, + &startInfo, + &procInfo); + if (!res) { + return false; + } + // After process creation, restore the saved STDIN and STDOUT. + if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) { + return false; + } + if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) { + return false; + } + + // hChildStdinWrDup can be used to write to the child's stdin + // hChildStdoutRdDup can be used to read from the child's stdout + + // NOTE: example code closes hChildStdoutWr before reading from + // hChildStdoutRdDup. "Close the write end of the pipe before + // reading from the read end of the pipe"??? Looks like this is + // example-specific. + + // Set up return arguments + // hChildStdoutRd and hChildStdinWr are already closed at this point + *childProcessHandle = procInfo.hProcess; + *writeToStdinHandle = hChildStdinWrDup; + *readFromStdoutHandle = hChildStdoutRdDup; + *auxHandle1 = hChildStdinRd; + *auxHandle2 = hChildStdoutWr; + return true; +} + +/** Clears the event and writes the message to the child process */ +bool +sendMessage(ChildInfo* child, Message* message) { + DWORD numBytesWritten; + if (!WriteFile(child->getWriteToStdinHandle(), + message, sizeof(Message), &numBytesWritten, NULL)) { + return false; + } + if (numBytesWritten != sizeof(Message)) { + return false; + } + // Follow up "poke" messages with the raw data + if (message->type == Message::POKE) { + if (!WriteFile(child->getWriteToStdinHandle(), + message->pokeArg.data, message->pokeArg.numBytes, &numBytesWritten, NULL)) { + return false; + } + if (numBytesWritten != message->pokeArg.numBytes) { + return false; + } + } + return true; +} + +/** Copies data from child's stdout to the client's IOBuf and sends it + along */ +bool +forwardReplyToClient(ChildInfo* child, ClientInfo* client) { + DWORD total = 0; + IOBuf::FillState ret; + + do { + DWORD temp; + ret = client->getIOBuf()->fillFromFileHandle(child->getReadFromStdoutHandle(), + &temp); + if (ret == IOBuf::DONE || ret == IOBuf::MORE_DATA_PENDING) { + if (!client->getIOBuf()->flush()) { +#ifdef DEBUGGING + cerr << "Forward failed because flush failed" << endl; +#endif + return false; + } + total += temp; + } + } while (ret == IOBuf::MORE_DATA_PENDING); + + return (ret == IOBuf::FAILED) ? false : true; +} + +//---------------------------------------------------------------------- +// Server Handler +// + +class ServerHandler : public Handler { +public: + ServerHandler(); + + // Starts up in Unicode mode by default + bool getASCII(); + + void setIOBuf(IOBuf* ioBuf); + + void procList(char* arg); + + // Must be called before calling one of the routines below + void setClientInfo(ClientInfo* info); + + // Indicates to outer loop that exit was called or that an error + // occurred and that the client exited. + bool exited(); + // Clears this state + void clearExited(); + + void ascii(char* arg); + void unicode(char* arg); + void attach(char* arg); + void detach(char* arg); + void libInfo(char* arg); + void peek(char* arg); + void poke(char* arg); + void threadList(char* arg); + void dupHandle(char* arg); + void closeHandle(char* arg); + void getContext(char* arg); + void setContext(char* arg); + void selectorEntry(char* arg); + void suspend(char* arg); + void resume(char* arg); + void pollEvent(char* arg); + void continueEvent(char* arg); + void exit(char* arg); + + // This is pretty gross. Needed to make the target process know + // about clients that have disconnected unexpectedly while attached. + friend void shutdownClient(ClientInfo*); +private: + // Writes: charSize numChars + // Handles both ASCII and UNICODE modes + void writeString(USHORT len, WCHAR* str); + + // Handles only ASCII mode + void writeString(USHORT len, char* str); + + ClientInfo* clientInfo; + IOBuf* ioBuf; + bool _exited; + bool _ascii; +}; + +static ServerHandler* handler; + +ServerHandler::ServerHandler() { + _exited = false; + _ascii = false; + ioBuf = NULL; +} + +bool +ServerHandler::getASCII() { + return _ascii; +} + +void +ServerHandler::setIOBuf(IOBuf* buf) { + ioBuf = buf; +} + +void +ServerHandler::setClientInfo(ClientInfo* info) { + clientInfo = info; +} + +bool +ServerHandler::exited() { + return _exited; +} + +void +ServerHandler::clearExited() { + _exited = false; +} + +void +ServerHandler::ascii(char* arg) { + _ascii = true; +} + +void +ServerHandler::unicode(char* arg) { + _ascii = false; +} + +void +ServerHandler::procList(char* arg) { +#ifdef DEBUGGING + cerr << "proclist" << endl; +#endif + + ProcEntryList processes; + ::procList(processes); + + ioBuf->writeInt(processes.size()); + + for (ProcEntryList::iterator iter = processes.begin(); + iter != processes.end(); iter++) { + ProcEntry& entry = *iter; + ioBuf->writeSpace(); + ioBuf->writeUnsignedInt(entry.getPid()); + ioBuf->writeSpace(); + writeString(entry.getNameLength(), entry.getName()); + } + + ioBuf->writeEOL(); + ioBuf->flush(); +} + +void +ServerHandler::attach(char* arg) { + // If the client is already attached to a process, fail. + if (clientInfo->getTarget() != NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->writeEOL(); + ioBuf->flush(); + return; + } + + // Try to get pid + DWORD pid; + if (!scanUnsignedLong(&arg, &pid)) { + ioBuf->writeBoolAsInt(false); + ioBuf->writeEOL(); + ioBuf->flush(); + return; + } + + // See whether this pid is already forked + ListsLocker ll; + ChildInfo* childInfo = childList.getChildByPid(pid); + if (childInfo != NULL) { + // If this child already has a client, return false + if (childInfo->getClient() != NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->writeEOL(); + ioBuf->flush(); + return; + } + + // Otherwise, can associate this client with this child process + childInfo->setClient(clientInfo); + clientInfo->setTarget(childInfo); + + // Tell the child we are attaching so it can suspend the target + // process + Message msg; + msg.type = Message::ATTACH; + sendMessage(childInfo, &msg); + + ioBuf->writeBoolAsInt(true); + ioBuf->writeEOL(); + ioBuf->flush(); + return; + } else { + // Have to fork a new child subprocess + HANDLE childProcessHandle; + HANDLE writeToStdinHandle; + HANDLE readFromStdoutHandle; + HANDLE auxHandle1; + HANDLE auxHandle2; + if (!startChildProcess(pid, + 32768, + 131072, + &childProcessHandle, + &writeToStdinHandle, + &readFromStdoutHandle, + &auxHandle1, + &auxHandle2)) { + ioBuf->writeBoolAsInt(false); + ioBuf->writeEOL(); + ioBuf->flush(); + return; + } + + // See whether the child succeeded in attaching to the process + char res; + DWORD numRead; + if (!ReadFile(readFromStdoutHandle, + &res, + sizeof(char), + &numRead, + NULL)) { + ioBuf->writeBoolAsInt(false); + ioBuf->writeEOL(); + ioBuf->flush(); + return; + } + + if (!res) { + ioBuf->writeBoolAsInt(false); + ioBuf->writeEOL(); + ioBuf->flush(); + return; + } + + // OK, success. + childInfo = new ChildInfo(pid, childProcessHandle, + writeToStdinHandle, readFromStdoutHandle, + auxHandle1, auxHandle2); + childList.addChild(childInfo); + reaper->registerProcess(childProcessHandle, childInfo); + // Associate this client with this child process + childInfo->setClient(clientInfo); + clientInfo->setTarget(childInfo); + + // Tell the child process to actually suspend the target process + Message msg; + msg.type = Message::ATTACH; + sendMessage(childInfo, &msg); + + // Write result to client + ioBuf->writeBoolAsInt(true); + ioBuf->writeEOL(); + ioBuf->flush(); + return; + } +} + +void +ServerHandler::detach(char* arg) { + // If the client is not attached, fail. + if (clientInfo->getTarget() == NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->writeEOL(); + ioBuf->flush(); + return; + } + + detachClient(clientInfo); + + ioBuf->writeBoolAsInt(true); + ioBuf->writeEOL(); + ioBuf->flush(); +} + +void +ServerHandler::libInfo(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + ioBuf->writeInt(0); + ioBuf->writeEOL(); + ioBuf->flush(); + return; + } + + // Send message to child + Message msg; + msg.type = Message::LIBINFO; + sendMessage(child, &msg); + + // Forward reply to client + forwardReplyToClient(child, clientInfo); +} + +void +ServerHandler::peek(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + ioBuf->writeString("B"); + ioBuf->writeBinChar(0); + ioBuf->flush(); + return; + } + + // Try to get address + DWORD address; + if (!scanAddress(&arg, &address)) { + ioBuf->writeString("B"); + ioBuf->writeBinChar(0); + ioBuf->flush(); + return; + } + + // Try to get number of bytes + DWORD numBytes; + if (!scanUnsignedLong(&arg, &numBytes)) { + ioBuf->writeString("B"); + ioBuf->writeBinChar(0); + ioBuf->flush(); + return; + } + + // Send message to child + Message msg; + msg.type = Message::PEEK; + msg.peekArg.address = address; + msg.peekArg.numBytes = numBytes; + sendMessage(child, &msg); + + // Forward reply to client + forwardReplyToClient(child, clientInfo); +} + +void +ServerHandler::poke(char* arg) { +#ifdef DEBUGGING + cerr << "ServerHandler::poke" << endl; +#endif + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Try to get address + DWORD address; + if (!scanAddress(&arg, &address)) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Try to get number of bytes + if (!scanAndSkipBinEscapeChar(&arg)) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + DWORD numBytes; + if (!scanBinUnsignedLong(&arg, &numBytes)) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Raw data is now in "arg" + // Send message to child + Message msg; + msg.type = Message::POKE; + msg.pokeArg.address = address; + msg.pokeArg.numBytes = numBytes; + msg.pokeArg.data = arg; + sendMessage(child, &msg); + + // Forward reply to client + forwardReplyToClient(child, clientInfo); +} + +void +ServerHandler::threadList(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Send message to child + Message msg; + msg.type = Message::THREADLIST; + sendMessage(child, &msg); + + // Forward reply to client + forwardReplyToClient(child, clientInfo); +} + +void +ServerHandler::dupHandle(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Try to get handle + DWORD address; + if (!scanAddress(&arg, &address)) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + } + + // Send message to child + Message msg; + msg.type = Message::DUPHANDLE; + msg.handleArg.handle = (HANDLE) address; + sendMessage(child, &msg); + + // Forward reply to client + forwardReplyToClient(child, clientInfo); +} + +void +ServerHandler::closeHandle(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + return; + } + + // Try to get handle + DWORD address; + if (!scanAddress(&arg, &address)) { + return; + } + + // Send message to child + Message msg; + msg.type = Message::CLOSEHANDLE; + msg.handleArg.handle = (HANDLE) address; + sendMessage(child, &msg); + + // No reply +} + +void +ServerHandler::getContext(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Try to get handle + DWORD address; + if (!scanAddress(&arg, &address)) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Send message to child + Message msg; + msg.type = Message::GETCONTEXT; + msg.handleArg.handle = (HANDLE) address; + sendMessage(child, &msg); + + // Forward reply to client + forwardReplyToClient(child, clientInfo); +} + +void +ServerHandler::setContext(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Try to get handle + DWORD address; + if (!scanAddress(&arg, &address)) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Try to get context + DWORD regs[NUM_REGS_IN_CONTEXT]; + for (int i = 0; i < NUM_REGS_IN_CONTEXT; i++) { + if (!scanAddress(&arg, ®s[i])) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + } + + // Send message to child + Message msg; + msg.type = Message::SETCONTEXT; + msg.setContextArg.handle = (HANDLE) address; + msg.setContextArg.Eax = regs[0]; + msg.setContextArg.Ebx = regs[1]; + msg.setContextArg.Ecx = regs[2]; + msg.setContextArg.Edx = regs[3]; + msg.setContextArg.Esi = regs[4]; + msg.setContextArg.Edi = regs[5]; + msg.setContextArg.Ebp = regs[6]; + msg.setContextArg.Esp = regs[7]; + msg.setContextArg.Eip = regs[8]; + msg.setContextArg.Ds = regs[9]; + msg.setContextArg.Es = regs[10]; + msg.setContextArg.Fs = regs[11]; + msg.setContextArg.Gs = regs[12]; + msg.setContextArg.Cs = regs[13]; + msg.setContextArg.Ss = regs[14]; + msg.setContextArg.EFlags = regs[15]; + msg.setContextArg.Dr0 = regs[16]; + msg.setContextArg.Dr1 = regs[17]; + msg.setContextArg.Dr2 = regs[18]; + msg.setContextArg.Dr3 = regs[19]; + msg.setContextArg.Dr6 = regs[20]; + msg.setContextArg.Dr7 = regs[21]; + sendMessage(child, &msg); + + // Forward reply to client + forwardReplyToClient(child, clientInfo); +} + +void +ServerHandler::selectorEntry(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Try to get thread handle + DWORD address; + if (!scanAddress(&arg, &address)) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Try to get selector + DWORD selector; + if (!scanUnsignedLong(&arg, &selector)) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Send message to child + Message msg; + msg.type = Message::SELECTORENTRY; + msg.selectorArg.handle = (HANDLE) address; + msg.selectorArg.selector = selector; + sendMessage(child, &msg); + + // Forward reply to client + forwardReplyToClient(child, clientInfo); +} + +void +ServerHandler::suspend(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + return; + } + + // Send message to child + Message msg; + msg.type = Message::SUSPEND; + sendMessage(child, &msg); + + // No reply +} + +void +ServerHandler::resume(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + return; + } + + // Send message to child + Message msg; + msg.type = Message::RESUME; + sendMessage(child, &msg); + + // No reply +} + +void +ServerHandler::pollEvent(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Send message to child + Message msg; + msg.type = Message::POLLEVENT; + sendMessage(child, &msg); + + // Forward reply to client + forwardReplyToClient(child, clientInfo); +} + +void +ServerHandler::continueEvent(char* arg) { + ListsLocker ll; + ChildInfo* child = clientInfo->getTarget(); + if (child == NULL) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Try to get bool arg + int passEventToClient; + if (!scanInt(&arg, &passEventToClient)) { + ioBuf->writeBoolAsInt(false); + ioBuf->flush(); + return; + } + + // Send message to child + Message msg; + msg.type = Message::CONTINUEEVENT; + msg.boolArg.val = ((passEventToClient != 0) ? true : false); + sendMessage(child, &msg); + + // Forward reply to client + forwardReplyToClient(child, clientInfo); +} + +void +ServerHandler::exit(char* arg) { + shutdownClient(clientInfo); + _exited = true; +} + +void +ServerHandler::writeString(USHORT len, WCHAR* str) { + if (_ascii) { + char* cStr = new char[len + 1]; + sprintf(cStr, "%.*ls", len, str); + writeString(len, cStr); + delete[] cStr; + } else { + ioBuf->writeInt(sizeof(unsigned short)); + ioBuf->writeSpace(); + ioBuf->writeInt(len); + ioBuf->writeSpace(); + for (int i = 0; i < len; i++) { + ioBuf->writeBinUnsignedShort(str[i]); + } + } +} + +void +ServerHandler::writeString(USHORT len, char* str) { + ioBuf->writeInt(1); + ioBuf->writeSpace(); + ioBuf->writeInt(len); + ioBuf->writeSpace(); + ioBuf->writeString(str); +} + +// +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// Shutdown routines +// + +void +shutdownChild(ChildInfo* childInfo) { + childList.removeChild(childInfo); + childInfo->closeAll(); + if (childInfo->getClient() != NULL) { + shutdownClient(childInfo->getClient()); + } + delete childInfo; +} + +void +detachClient(ClientInfo* info) { + ListsLocker ll; + // May have been dissociated while not under cover of lock + if (info->getTarget() == NULL) { + return; + } + + // Tell the child that we have detached to let the target process + // continue running + Message msg; + msg.type = Message::DETACH; + sendMessage(info->getTarget(), &msg); + + // Dissociate the client and the target + info->getTarget()->setClient(NULL); + info->setTarget(NULL); +} + +void +shutdownClient(ClientInfo* clientInfo) { +#ifdef DEBUGGING + cerr << "Shutting down client" << endl; +#endif + + // If we're connected, inform the target process that we're + // disconnecting + detachClient(clientInfo); + + // Remove this client from the list and delete it + clientList.removeClient(clientInfo); + if (clientInfo->getTarget() != NULL) { + clientInfo->getTarget()->setClient(NULL); + } + clientInfo->closeAll(); + delete clientInfo; +} + +// +//---------------------------------------------------------------------- + + +/** Main dispatcher for client commands. NOTE: do not refer to this + clientInfo data structure after calling this routine, as it may be + deleted internally. */ +void +readAndDispatch(ClientInfo* clientInfo) { + IOBuf::ReadLineResult res; + IOBuf* ioBuf = clientInfo->getIOBuf(); + unsigned long howMany; + ioctlsocket(clientInfo->getDataSocket(), FIONREAD, &howMany); + if (howMany == 0) { + // Client closed down. + shutdownClient(clientInfo); + return; + } + // Read and process as much data as possible + do { + res = ioBuf->tryReadLine(); + if (res == IOBuf::RL_ERROR) { +#ifdef DEBUGGING + cerr << "Error while reading line" << endl; +#endif + shutdownClient(clientInfo); + return; + } else if (res == IOBuf::RL_GOT_DATA) { +#ifdef DEBUGGING + cerr << "Got data: \"" << ioBuf->getLine() << "\"" << endl; +#endif + handler->setIOBuf(ioBuf); + handler->setClientInfo(clientInfo); + handler->clearExited(); + Dispatcher::dispatch(ioBuf->getLine(), handler); + } + } while (res == IOBuf::RL_GOT_DATA && (!handler->exited())); +#ifdef DEBUGGING + cerr << "Exiting readAndDispatch" << endl; +#endif +} + +int +main(int argc, char **argv) +{ + initWinsock(); + + if (isNT4()) { + loadPSAPIDLL(); // Will exit if not present + } + + SOCKET clientListeningSock = setupListeningSocket(CLIENT_PORT); + + handler = new ServerHandler(); + Lists::init(); + + reaper = new Reaper(&reapCB); + if (!reaper->start()) { + exit(1); + } + + while (true) { + // Select on all sockets: + // - client listening socket + // - sockets for all client connections + + // When one of the client connections closes, close its socket + // handles. + + fd_set set; + SOCKET maxSock = 0; + + // Set up fd_set + { + int i; + FD_ZERO(&set); + FD_SET(clientListeningSock, &set); + if (clientListeningSock > maxSock) { + maxSock = clientListeningSock; + } + for (i = 0; i < clientList.size(); i++) { + ClientInfo* info = clientList.get(i); + if (info->getDataSocket() > maxSock) { + maxSock = info->getDataSocket(); + } + FD_SET(info->getDataSocket(), &set); + } + } + struct timeval timeout; + timeout.tv_sec = 300; // 5 minutes + timeout.tv_usec = 0; + int res = select(maxSock, &set, NULL, NULL, &timeout); + if (res > 0) { + + //////////////// + // New client // + //////////////// + if (FD_ISSET(clientListeningSock, &set)) { + SOCKET fd = acceptFromLocalhost(clientListeningSock); + if (fd != INVALID_SOCKET) { + // Create new client information object + ClientInfo* info = new ClientInfo(fd); + // Add to list of clients + clientList.addClient(info); +#ifdef DEBUGGING + cerr << "New client" << endl; +#endif + } + } + + /////////////////////////// + // Commands from clients // + /////////////////////////// + ClientInfo* clientInfo; + if (clientList.isAnyDataSocketSet(&set, &clientInfo)) { + readAndDispatch(clientInfo); + } + } else if (res < 0) { + // Looks like one of the clients was killed. Try to figure out which one. + bool found = false; + fd_set set; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + for (int i = 0; i < clientList.size(); i++) { + ClientInfo* info = clientList.get(i); + FD_ZERO(&set); + FD_SET(info->getDataSocket(), &set); + if (select(1 + info->getDataSocket(), &set, NULL, NULL, &timeout) < 0) { + found = true; + clientList.removeClient(info); + info->closeAll(); + delete info; + break; + } + } + if (!found) { + // This indicates trouble -- one of our listening sockets died. + exit(1); + } + } + } + + return 0; +} diff --git a/hotspot/agent/src/os/win32/SwDbgSrv.dsp b/hotspot/agent/src/os/win32/SwDbgSrv.dsp new file mode 100644 index 00000000000..4257278704c --- /dev/null +++ b/hotspot/agent/src/os/win32/SwDbgSrv.dsp @@ -0,0 +1,146 @@ +# Microsoft Developer Studio Project File - Name="SwDbgSrv" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=SwDbgSrv - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "SwDbgSrv.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "SwDbgSrv.mak" CFG="SwDbgSrv - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "SwDbgSrv - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "SwDbgSrv - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "SwDbgSrv - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "SwDbgSrv - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "SwDbgSrv___Win32_Debug" +# PROP BASE Intermediate_Dir "SwDbgSrv___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "SwDbgSrv - Win32 Release" +# Name "SwDbgSrv - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\Buffer.cpp +# End Source File +# Begin Source File + +SOURCE=.\Dispatcher.cpp +# End Source File +# Begin Source File + +SOURCE=.\initWinsock.cpp +# End Source File +# Begin Source File + +SOURCE=.\IOBuf.cpp +# End Source File +# Begin Source File + +SOURCE=.\ioUtils.cpp +# End Source File +# Begin Source File + +SOURCE=.\isNT4.cpp +# End Source File +# Begin Source File + +SOURCE=.\nt4internals.cpp +# End Source File +# Begin Source File + +SOURCE=.\procList.cpp +# End Source File +# Begin Source File + +SOURCE=.\Reaper.cpp +# End Source File +# Begin Source File + +SOURCE=.\serverLists.cpp +# End Source File +# Begin Source File + +SOURCE=.\SwDbgSrv.cpp +# End Source File +# Begin Source File + +SOURCE=.\toolHelp.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/hotspot/agent/src/os/win32/SwDbgSrv.dsw b/hotspot/agent/src/os/win32/SwDbgSrv.dsw new file mode 100644 index 00000000000..a1570e459c3 --- /dev/null +++ b/hotspot/agent/src/os/win32/SwDbgSrv.dsw @@ -0,0 +1,41 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "SwDbgSrv"=.\SwDbgSrv.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "SwDbgSub"=.\SwDbgSub.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/hotspot/agent/src/os/win32/SwDbgSub.cpp b/hotspot/agent/src/os/win32/SwDbgSub.cpp new file mode 100644 index 00000000000..c8ef813e368 --- /dev/null +++ b/hotspot/agent/src/os/win32/SwDbgSub.cpp @@ -0,0 +1,883 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This is the source code for the subprocess forked by the Simple +// Windows Debug Server. It assumes most of the responsibility for the +// debug session, and processes all of the commands sent by clients. + +// Disable too-long symbol warnings +#pragma warning ( disable : 4786 ) + +#include +#include +#include +#include +// Must come before windows.h +#include +#include +#include "IOBuf.hpp" +#include "libInfo.hpp" +#include "LockableList.hpp" +#include "Message.hpp" +#include "Monitor.hpp" +#include "nt4internals.hpp" + +// Uncomment the #define below to get messages on stderr +// #define DEBUGGING + +using namespace std; + +DWORD pid; +HANDLE procHandle; +IOBuf* ioBuf; + +// State flags indicating whether the attach to the remote process +// definitively succeeded or failed +volatile bool attachFailed = false; +volatile bool attachSucceeded = false; + +// State flag indicating whether the target process is suspended. +// Modified by suspend()/resume(), viewed by debug thread, but only +// under cover of the threads lock. +volatile bool suspended = false; + +// State flags indicating whether we are considered to be attached to +// the target process and are therefore queuing up events to be sent +// back to the debug server. These flags are only accessed and +// modified under the cover of the eventLock. +Monitor* eventLock; +// The following is set to true when a client is attached to this process +volatile bool generateDebugEvents = false; +// Pointer to current debug event; non-NULL indicates a debug event is +// waiting to be sent to the client. Main thread sets this to NULL to +// indicate that the event has been consumed; also sets +// passEventToClient, below. +volatile DEBUG_EVENT* curDebugEvent = NULL; +// Set by main thread to indicate whether the most recently posted +// debug event should be passed on to the target process. +volatile bool passEventToClient = true; + +void conditionalPostDebugEvent(DEBUG_EVENT* ev, DWORD* continueOrNotHandledFlag) { + // FIXME: make it possible for the client to enable and disable + // certain types of events (have to do so in a platform-independent + // manner) + switch (ev->dwDebugEventCode) { + case EXCEPTION_DEBUG_EVENT: + switch (ev->u.Exception.ExceptionRecord.ExceptionCode) { + case EXCEPTION_BREAKPOINT: break; + case EXCEPTION_SINGLE_STEP: break; + case EXCEPTION_ACCESS_VIOLATION: break; + default: return; + } + } + eventLock->lock(); + if (generateDebugEvents) { + curDebugEvent = ev; + while (curDebugEvent != NULL) { + eventLock->wait(); + } + if (passEventToClient) { + *continueOrNotHandledFlag = DBG_EXCEPTION_NOT_HANDLED; + } else { + *continueOrNotHandledFlag = DBG_CONTINUE; + } + } + eventLock->unlock(); +} + + +//---------------------------------------------------------------------- +// Module list +// + +vector libs; + +//---------------------------------------------------------------------- +// Thread list +// + +struct ThreadInfo { + DWORD tid; + HANDLE thread; + + ThreadInfo(DWORD tid, HANDLE thread) { + this->tid = tid; + this->thread = thread; + } +}; + +class ThreadList : public LockableList { +public: + bool removeByThreadID(DWORD tid) { + for (InternalListType::iterator iter = internalList.begin(); + iter != internalList.end(); iter++) { + if ((*iter).tid == tid) { + internalList.erase(iter); + return true; + } + } + return false; + } + HANDLE threadIDToHandle(DWORD tid) { + for (InternalListType::iterator iter = internalList.begin(); + iter != internalList.end(); iter++) { + if ((*iter).tid == tid) { + return (*iter).thread; + } + } + return NULL; + } +}; + +ThreadList threads; + +//---------------------------------------------------------------------- +// INITIALIZATION AND TERMINATION +// + +void +printError(const char* prefix) { + DWORD detail = GetLastError(); + LPTSTR message; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + detail, + 0, + (LPTSTR) &message, + 1, + NULL); + // FIXME: This is signaling an error: "The handle is invalid." ? + // Do I have to do all of my WaitForDebugEvent calls from the same thread? + cerr << prefix << ": " << message << endl; + LocalFree(message); +} + +void +endProcess(bool waitForProcess = true) { + NT4::unloadNTDLL(); + if (waitForProcess) { + // Though we're exiting because of an error, do not tear down the + // target process. + WaitForSingleObject(procHandle, INFINITE); + } + CloseHandle(procHandle); + exit(0); +} + +DWORD WINAPI +debugThreadEntry(void*) { +#ifdef DEBUGGING + DWORD lastMsgId = 0; + int count = 0; +#endif + + if (!DebugActiveProcess(pid)) { + attachFailed = true; + return 0; + } + + // Wait for debug events. We keep the information from some of these + // on the side in anticipation of later queries by the client. NOTE + // that we leave the process running. The main thread is responsible + // for suspending and resuming all currently-active threads upon + // client attach and detach. + + while (true) { + DEBUG_EVENT ev; + if (!WaitForDebugEvent(&ev, INFINITE)) { +#ifdef DEBUGGING + if (++count < 10) { + // FIXME: This is signaling an error: "The handle is invalid." ? + // Do I have to do all of my WaitForDebugEvent calls from the same thread? + printError("WaitForDebugEvent failed"); + } +#endif + } else { + +#ifdef DEBUGGING + if (ev.dwDebugEventCode != lastMsgId) { + lastMsgId = ev.dwDebugEventCode; + count = 0; + cerr << "Debug thread received event " << ev.dwDebugEventCode << endl; + } else { + if (++count < 10) { + cerr << "Debug thread received event " << ev.dwDebugEventCode << endl; + } + } +#endif + + DWORD dbgContinueMode = DBG_CONTINUE; + + switch (ev.dwDebugEventCode) { + case LOAD_DLL_DEBUG_EVENT: + conditionalPostDebugEvent(&ev, &dbgContinueMode); + break; + + case UNLOAD_DLL_DEBUG_EVENT: + conditionalPostDebugEvent(&ev, &dbgContinueMode); + break; + + case CREATE_PROCESS_DEBUG_EVENT: + threads.lock(); + // FIXME: will this deal properly with child processes? If + // not, is it possible to make it do so? +#ifdef DEBUGGING + cerr << "CREATE_PROCESS_DEBUG_EVENT " << ev.dwThreadId + << " " << ev.u.CreateProcessInfo.hThread << endl; +#endif + if (ev.u.CreateProcessInfo.hThread != NULL) { + threads.add(ThreadInfo(ev.dwThreadId, ev.u.CreateProcessInfo.hThread)); + } + threads.unlock(); + break; + + case CREATE_THREAD_DEBUG_EVENT: + threads.lock(); +#ifdef DEBUGGING + cerr << "CREATE_THREAD_DEBUG_EVENT " << ev.dwThreadId + << " " << ev.u.CreateThread.hThread << endl; +#endif + if (suspended) { + // Suspend this thread before adding it to the thread list + SuspendThread(ev.u.CreateThread.hThread); + } + threads.add(ThreadInfo(ev.dwThreadId, ev.u.CreateThread.hThread)); + threads.unlock(); + break; + + case EXIT_THREAD_DEBUG_EVENT: + threads.lock(); +#ifdef DEBUGGING + cerr << "EXIT_THREAD_DEBUG_EVENT " << ev.dwThreadId << endl; +#endif + threads.removeByThreadID(ev.dwThreadId); + threads.unlock(); + break; + + case EXCEPTION_DEBUG_EVENT: + // cerr << "EXCEPTION_DEBUG_EVENT" << endl; + switch (ev.u.Exception.ExceptionRecord.ExceptionCode) { + case EXCEPTION_BREAKPOINT: + // cerr << "EXCEPTION_BREAKPOINT" << endl; + if (!attachSucceeded && !attachFailed) { + attachSucceeded = true; + } + break; + + default: + dbgContinueMode = DBG_EXCEPTION_NOT_HANDLED; + break; + } + conditionalPostDebugEvent(&ev, &dbgContinueMode); + break; + + case EXIT_PROCESS_DEBUG_EVENT: + endProcess(false); + // NOT REACHED + break; + + default: +#ifdef DEBUGGING + cerr << "Received debug event " << ev.dwDebugEventCode << endl; +#endif + break; + } + + ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, dbgContinueMode); + } + } +} + +bool +attachToProcess() { + // Create event lock + eventLock = new Monitor(); + + // Get a process handle for later + procHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (procHandle == NULL) { + return false; + } + + // Start up the debug thread + DWORD debugThreadId; + if (CreateThread(NULL, 0, &debugThreadEntry, NULL, 0, &debugThreadId) == NULL) { + // Failed to make background debug thread. Fail. + return false; + } + + while ((!attachSucceeded) && (!attachFailed)) { + Sleep(1); + } + + if (attachFailed) { + return false; + } + + assert(attachSucceeded); + + return true; +} + +bool +readMessage(Message* msg) { + DWORD numRead; + if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), + msg, + sizeof(Message), + &numRead, + NULL)) { + return false; + } + if (numRead != sizeof(Message)) { + return false; + } + // For "poke" messages, must follow up by reading raw data + if (msg->type == Message::POKE) { + char* dataBuf = new char[msg->pokeArg.numBytes]; + if (dataBuf == NULL) { + return false; + } + if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), + dataBuf, + msg->pokeArg.numBytes, + &numRead, + NULL)) { + delete[] dataBuf; + return false; + } + if (numRead != msg->pokeArg.numBytes) { + delete[] dataBuf; + return false; + } + msg->pokeArg.data = (void *) dataBuf; + } + return true; +} + +void +handlePeek(Message* msg) { +#ifdef DEBUGGING + cerr << "Entering handlePeek()" << endl; +#endif + + char* memBuf = new char[msg->peekArg.numBytes]; + if (memBuf == NULL) { + ioBuf->writeString("B"); + ioBuf->writeBinChar(0); + ioBuf->flush(); + delete[] memBuf; + return; + } + + // Try fast case first + DWORD numRead; + BOOL res = ReadProcessMemory(procHandle, + (LPCVOID) msg->peekArg.address, + memBuf, + msg->peekArg.numBytes, + &numRead); + if (res && (numRead == msg->peekArg.numBytes)) { + + // OK, complete success. Phew. +#ifdef DEBUGGING + cerr << "Peek success case" << endl; +#endif + ioBuf->writeString("B"); + ioBuf->writeBinChar(1); + ioBuf->writeBinUnsignedInt(numRead); + ioBuf->writeBinChar(1); + ioBuf->writeBinBuf(memBuf, numRead); + } else { +#ifdef DEBUGGING + cerr << "*** Peek slow case ***" << endl; +#endif + + ioBuf->writeString("B"); + ioBuf->writeBinChar(1); + + // Use VirtualQuery to speed things up a bit + DWORD numLeft = msg->peekArg.numBytes; + char* curAddr = (char*) msg->peekArg.address; + while (numLeft > 0) { + MEMORY_BASIC_INFORMATION memInfo; + VirtualQueryEx(procHandle, curAddr, &memInfo, sizeof(memInfo)); + DWORD numToRead = memInfo.RegionSize; + if (numToRead > numLeft) { + numToRead = numLeft; + } + DWORD numRead; + if (memInfo.State == MEM_COMMIT) { + // Read the process memory at this address for this length + // FIXME: should check the result of this read + ReadProcessMemory(procHandle, curAddr, memBuf, + numToRead, &numRead); + // Write this out +#ifdef DEBUGGING + cerr << "*** Writing " << numToRead << " bytes as mapped ***" << endl; +#endif + ioBuf->writeBinUnsignedInt(numToRead); + ioBuf->writeBinChar(1); + ioBuf->writeBinBuf(memBuf, numToRead); + } else { + // Indicate region is free +#ifdef DEBUGGING + cerr << "*** Writing " << numToRead << " bytes as unmapped ***" << endl; +#endif + ioBuf->writeBinUnsignedInt(numToRead); + ioBuf->writeBinChar(0); + } + curAddr += numToRead; + numLeft -= numToRead; + } + } + + ioBuf->flush(); + delete[] memBuf; +#ifdef DEBUGGING + cerr << "Exiting handlePeek()" << endl; +#endif +} + +void +handlePoke(Message* msg) { +#ifdef DEBUGGING + cerr << "Entering handlePoke()" << endl; +#endif + DWORD numWritten; + BOOL res = WriteProcessMemory(procHandle, + (LPVOID) msg->pokeArg.address, + msg->pokeArg.data, + msg->pokeArg.numBytes, + &numWritten); + if (res && (numWritten == msg->pokeArg.numBytes)) { + // Success + ioBuf->writeBoolAsInt(true); +#ifdef DEBUGGING + cerr << " (Succeeded)" << endl; +#endif + } else { + // Failure + ioBuf->writeBoolAsInt(false); +#ifdef DEBUGGING + cerr << " (Failed)" << endl; +#endif + } + ioBuf->writeEOL(); + ioBuf->flush(); + // We clean up the data + char* dataBuf = (char*) msg->pokeArg.data; + delete[] dataBuf; +#ifdef DEBUGGING + cerr << "Exiting handlePoke()" << endl; +#endif +} + +bool +suspend() { + if (suspended) { + return false; + } + // Before we suspend, we must take a snapshot of the loaded module + // names and base addresses, since acquiring this snapshot requires + // starting and exiting a thread in the remote process (at least on + // NT 4). + libs.clear(); +#ifdef DEBUGGING + cerr << "Starting suspension" << endl; +#endif + libInfo(pid, libs); +#ifdef DEBUGGING + cerr << " Got lib info" << endl; +#endif + threads.lock(); +#ifdef DEBUGGING + cerr << " Got thread lock" << endl; +#endif + suspended = true; + int j = 0; + for (int i = 0; i < threads.size(); i++) { + j++; + SuspendThread(threads.get(i).thread); + } +#ifdef DEBUGGING + cerr << "Suspended " << j << " threads" << endl; +#endif + threads.unlock(); + return true; +} + +bool +resume() { + if (!suspended) { + return false; + } + threads.lock(); + suspended = false; + for (int i = 0; i < threads.size(); i++) { + ResumeThread(threads.get(i).thread); + } + threads.unlock(); +#ifdef DEBUGGING + cerr << "Resumed process" << endl; +#endif + return true; +} + +int +main(int argc, char **argv) +{ + if (argc != 2) { + // Should only be used by performing CreateProcess within SwDbgSrv + exit(1); + } + + if (sscanf(argv[1], "%u", &pid) != 1) { + exit(1); + } + + // Try to attach to process + if (!attachToProcess()) { + // Attach failed. Notify parent by writing result to stdout file + // handle. + char res = 0; + DWORD numBytes; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &res, sizeof(res), + &numBytes, NULL); + exit(1); + } + + // Server is expecting success result back. + char res = 1; + DWORD numBytes; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &res, sizeof(res), + &numBytes, NULL); + + // Initialize our I/O buffer + ioBuf = new IOBuf(32768, 131072); + ioBuf->setOutputFileHandle(GetStdHandle(STD_OUTPUT_HANDLE)); + + // At this point we are attached. Enter our main loop which services + // requests from the server. Note that in order to handle attach/ + // detach properly (i.e., resumption of process upon "detach") we + // will need another thread which handles debug events. + while (true) { + // Read a message from the server + Message msg; + if (!readMessage(&msg)) { + endProcess(); + } + +#ifdef DEBUGGING + cerr << "Main thread read message: " << msg.type << endl; +#endif + + switch (msg.type) { + // ATTACH and DETACH messages MUST come in pairs + case Message::ATTACH: + suspend(); + eventLock->lock(); + generateDebugEvents = true; + eventLock->unlock(); + break; + + case Message::DETACH: + eventLock->lock(); + generateDebugEvents = false; + // Flush remaining event if any + if (curDebugEvent != NULL) { + curDebugEvent = NULL; + eventLock->notifyAll(); + } + eventLock->unlock(); + resume(); + break; + + case Message::LIBINFO: + { + if (!suspended) { + ioBuf->writeInt(0); + } else { + // Send back formatted text + ioBuf->writeInt(libs.size()); + for (int i = 0; i < libs.size(); i++) { + ioBuf->writeSpace(); + ioBuf->writeInt(1); + ioBuf->writeSpace(); + ioBuf->writeInt(libs[i].name.size()); + ioBuf->writeSpace(); + ioBuf->writeString(libs[i].name.c_str()); + ioBuf->writeSpace(); + ioBuf->writeAddress(libs[i].base); + } + } + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::PEEK: + handlePeek(&msg); + break; + + case Message::POKE: + handlePoke(&msg); + break; + + case Message::THREADLIST: + { + if (!suspended) { + ioBuf->writeInt(0); + } else { + threads.lock(); + ioBuf->writeInt(threads.size()); + for (int i = 0; i < threads.size(); i++) { + ioBuf->writeSpace(); + ioBuf->writeAddress((void*) threads.get(i).thread); + } + threads.unlock(); + } + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::DUPHANDLE: + { + HANDLE dup; + if (DuplicateHandle(procHandle, + msg.handleArg.handle, + GetCurrentProcess(), + &dup, + 0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + ioBuf->writeBoolAsInt(true); + ioBuf->writeSpace(); + ioBuf->writeAddress((void*) dup); + } else { + ioBuf->writeBoolAsInt(false); + } + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::CLOSEHANDLE: + { + CloseHandle(msg.handleArg.handle); + break; + } + + case Message::GETCONTEXT: + { + if (!suspended) { + ioBuf->writeBoolAsInt(false); + } else { + CONTEXT context; + context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; + if (GetThreadContext(msg.handleArg.handle, &context)) { + ioBuf->writeBoolAsInt(true); + // EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EIP, DS, ES, FS, GS, + // CS, SS, EFLAGS, DR0, DR1, DR2, DR3, DR6, DR7 + // See README-commands.txt + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Eax); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Ebx); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Ecx); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Edx); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Esi); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Edi); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Ebp); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Esp); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Eip); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegDs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegEs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegFs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegGs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegCs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegSs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.EFlags); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr0); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr1); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr2); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr3); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr6); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr7); + } else { + ioBuf->writeBoolAsInt(false); + } + } + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::SETCONTEXT: + { + if (!suspended) { + ioBuf->writeBoolAsInt(false); + } else { + CONTEXT context; + context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; + context.Eax = msg.setContextArg.Eax; + context.Ebx = msg.setContextArg.Ebx; + context.Ecx = msg.setContextArg.Ecx; + context.Edx = msg.setContextArg.Edx; + context.Esi = msg.setContextArg.Esi; + context.Edi = msg.setContextArg.Edi; + context.Ebp = msg.setContextArg.Ebp; + context.Esp = msg.setContextArg.Esp; + context.Eip = msg.setContextArg.Eip; + context.SegDs = msg.setContextArg.Ds; + context.SegEs = msg.setContextArg.Es; + context.SegFs = msg.setContextArg.Fs; + context.SegGs = msg.setContextArg.Gs; + context.SegCs = msg.setContextArg.Cs; + context.SegSs = msg.setContextArg.Ss; + context.EFlags = msg.setContextArg.EFlags; + context.Dr0 = msg.setContextArg.Dr0; + context.Dr1 = msg.setContextArg.Dr1; + context.Dr2 = msg.setContextArg.Dr2; + context.Dr3 = msg.setContextArg.Dr3; + context.Dr6 = msg.setContextArg.Dr6; + context.Dr7 = msg.setContextArg.Dr7; + if (SetThreadContext(msg.setContextArg.handle, &context)) { + ioBuf->writeBoolAsInt(true); + } else { + ioBuf->writeBoolAsInt(false); + } + } + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::SELECTORENTRY: + { + LDT_ENTRY entry; + + if (GetThreadSelectorEntry(msg.selectorArg.handle, + msg.selectorArg.selector, + &entry)) { + ioBuf->writeBoolAsInt(true); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.LimitLow); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.BaseLow); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.BaseMid); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.Flags1); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.Flags2); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.BaseHi); + } else { + ioBuf->writeBoolAsInt(false); + } + + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::SUSPEND: + suspend(); + break; + + case Message::RESUME: + resume(); + break; + + case Message::POLLEVENT: + eventLock->lock(); + if (curDebugEvent == NULL) { + ioBuf->writeBoolAsInt(false); + } else { + ioBuf->writeBoolAsInt(true); + ioBuf->writeSpace(); + threads.lock(); + ioBuf->writeAddress((void*) threads.threadIDToHandle(curDebugEvent->dwThreadId)); + threads.unlock(); + ioBuf->writeSpace(); + ioBuf->writeUnsignedInt(curDebugEvent->dwDebugEventCode); + // Figure out what else to write + switch (curDebugEvent->dwDebugEventCode) { + case LOAD_DLL_DEBUG_EVENT: + ioBuf->writeSpace(); + ioBuf->writeAddress(curDebugEvent->u.LoadDll.lpBaseOfDll); + break; + + case UNLOAD_DLL_DEBUG_EVENT: + ioBuf->writeSpace(); + ioBuf->writeAddress(curDebugEvent->u.UnloadDll.lpBaseOfDll); + break; + + case EXCEPTION_DEBUG_EVENT: + { + DWORD code = curDebugEvent->u.Exception.ExceptionRecord.ExceptionCode; + ioBuf->writeSpace(); + ioBuf->writeUnsignedInt(code); + ioBuf->writeSpace(); + ioBuf->writeAddress(curDebugEvent->u.Exception.ExceptionRecord.ExceptionAddress); + switch (curDebugEvent->u.Exception.ExceptionRecord.ExceptionCode) { + case EXCEPTION_ACCESS_VIOLATION: + ioBuf->writeSpace(); + ioBuf->writeBoolAsInt(curDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0] != 0); + ioBuf->writeSpace(); + ioBuf->writeAddress((void*) curDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[1]); + break; + + default: + break; + } + break; + } + + default: + break; + } + } + eventLock->unlock(); + ioBuf->writeEOL(); + ioBuf->flush(); + break; + + case Message::CONTINUEEVENT: + eventLock->lock(); + if (curDebugEvent == NULL) { + ioBuf->writeBoolAsInt(false); + } else { + curDebugEvent = NULL; + passEventToClient = msg.boolArg.val; + ioBuf->writeBoolAsInt(true); + eventLock->notify(); + } + eventLock->unlock(); + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + } + + endProcess(); + + // NOT REACHED + return 0; +} diff --git a/hotspot/agent/src/os/win32/SwDbgSub.dsp b/hotspot/agent/src/os/win32/SwDbgSub.dsp new file mode 100644 index 00000000000..a918a66845c --- /dev/null +++ b/hotspot/agent/src/os/win32/SwDbgSub.dsp @@ -0,0 +1,130 @@ +# Microsoft Developer Studio Project File - Name="SwDbgSub" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=SwDbgSub - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "SwDbgSub.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "SwDbgSub.mak" CFG="SwDbgSub - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "SwDbgSub - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "SwDbgSub - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "SwDbgSub - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "SwDbgSub - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "SwDbgSub___Win32_Debug" +# PROP BASE Intermediate_Dir "SwDbgSub___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "SwDbgSub - Win32 Release" +# Name "SwDbgSub - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\Buffer.cpp +# End Source File +# Begin Source File + +SOURCE=.\IOBuf.cpp +# End Source File +# Begin Source File + +SOURCE=.\isNT4.cpp +# End Source File +# Begin Source File + +SOURCE=.\libInfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\Monitor.cpp +# End Source File +# Begin Source File + +SOURCE=.\nt4internals.cpp +# End Source File +# Begin Source File + +SOURCE=.\SwDbgSub.cpp +# End Source File +# Begin Source File + +SOURCE=.\toolHelp.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/hotspot/agent/src/os/win32/initWinsock.cpp b/hotspot/agent/src/os/win32/initWinsock.cpp new file mode 100644 index 00000000000..e89855abddc --- /dev/null +++ b/hotspot/agent/src/os/win32/initWinsock.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include + +using namespace std; + +void +initWinsock() +{ + static int initted = 0; + WORD wVersionRequested; + WSADATA wsaData; + int err; + + if (!initted) { + wVersionRequested = MAKEWORD( 2, 0 ); + + err = WSAStartup( wVersionRequested, &wsaData ); + if ( err != 0 ) { + { + /* Tell the user that we couldn't find a usable */ + /* WinSock DLL. */ + cerr << "SocketBase::SocketBase: unable to find usable " + << "WinSock DLL" << endl; + exit(1); + } + } + + /* Confirm that the WinSock DLL supports 2.0.*/ + /* Note that if the DLL supports versions greater */ + /* than 2.0 in addition to 2.0, it will still return */ + /* 2.0 in wVersion since that is the version we */ + /* requested. */ + + if ( LOBYTE( wsaData.wVersion ) != 2 || + HIBYTE( wsaData.wVersion ) != 0 ) { + /* Tell the user that we couldn't find a usable */ + /* WinSock DLL. */ + { + cerr << "Unable to find suitable version of WinSock DLL" << endl; + WSACleanup( ); + exit(1); + } + } + + initted = 1; + } +} diff --git a/hotspot/agent/src/os/win32/initWinsock.hpp b/hotspot/agent/src/os/win32/initWinsock.hpp new file mode 100644 index 00000000000..c1e21c561d1 --- /dev/null +++ b/hotspot/agent/src/os/win32/initWinsock.hpp @@ -0,0 +1,30 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _INIT_WINSOCK_ +#define _INIT_WINSOCK_ + +void initWinsock(); + +#endif // #defined _INIT_WINSOCK_ diff --git a/hotspot/agent/src/os/win32/ioUtils.cpp b/hotspot/agent/src/os/win32/ioUtils.cpp new file mode 100644 index 00000000000..4fcf03e9406 --- /dev/null +++ b/hotspot/agent/src/os/win32/ioUtils.cpp @@ -0,0 +1,156 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include +#include "ioUtils.hpp" +#include "IOBuf.hpp" + +bool +scanInt(char** data, int* num) { + *num = 0; + + // Skip whitespace + while ((**data != 0) && (isspace(**data))) { + ++*data; + } + + if (**data == 0) { + return false; + } + + while ((**data != 0) && (!isspace(**data))) { + char cur = **data; + if ((cur < '0') || (cur > '9')) { + return false; + } + *num *= 10; + *num += cur - '0'; + ++*data; + } + + return true; +} + +bool +scanUnsignedLong(char** data, unsigned long* num) { + *num = 0; + + // Skip whitespace + while ((**data != 0) && (isspace(**data))) { + ++*data; + } + + if (**data == 0) { + return false; + } + + while ((**data != 0) && (!isspace(**data))) { + char cur = **data; + if ((cur < '0') || (cur > '9')) { + return false; + } + *num *= 10; + *num += cur - '0'; + ++*data; + } + + return true; +} + +bool +charToNibble(char ascii, int* value) { + if (ascii >= '0' && ascii <= '9') { + *value = ascii - '0'; + return true; + } else if (ascii >= 'A' && ascii <= 'F') { + *value = 10 + ascii - 'A'; + return true; + } else if (ascii >= 'a' && ascii <= 'f') { + *value = 10 + ascii - 'a'; + return true; + } + + return false; +} + +bool +scanAddress(char** data, unsigned long* addr) { + *addr = 0; + + // Skip whitespace + while ((**data != 0) && (isspace(**data))) { + ++*data; + } + + if (**data == 0) { + return false; + } + + if (strncmp(*data, "0x", 2) != 0) { + return false; + } + + *data += 2; + + while ((**data != 0) && (!isspace(**data))) { + int val; + bool res = charToNibble(**data, &val); + if (!res) { + return false; + } + *addr <<= 4; + *addr |= val; + ++*data; + } + + return true; +} + +bool +scanAndSkipBinEscapeChar(char** data) { + // Skip whitespace + while ((**data != 0) && (isspace(**data))) { + ++*data; + } + + if (!IOBuf::isBinEscapeChar(**data)) { + return false; + } + + ++*data; + + return true; +} + +bool +scanBinUnsignedLong(char** data, unsigned long* num) { + *num = 0; + for (int i = 0; i < 4; i++) { + unsigned char val = (unsigned char) **data; + *num = (*num << 8) | val; + ++*data; + } + return true; +} diff --git a/hotspot/agent/src/os/win32/ioUtils.hpp b/hotspot/agent/src/os/win32/ioUtils.hpp new file mode 100644 index 00000000000..7ccaf991dc5 --- /dev/null +++ b/hotspot/agent/src/os/win32/ioUtils.hpp @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _IO_UTILS_ +#define _IO_UTILS_ + +bool scanInt(char** data, int* num); +bool scanUnsignedLong(char** data, unsigned long* num); +bool scanAddress(char** data, unsigned long* addr); + +// Binary utils (for poke) +bool scanAndSkipBinEscapeChar(char** data); +bool scanBinUnsignedLong(char** data, unsigned long* num); + +#endif // #defined _IO_UTILS_ diff --git a/hotspot/agent/src/os/win32/isNT4.cpp b/hotspot/agent/src/os/win32/isNT4.cpp new file mode 100644 index 00000000000..2b7c55d8425 --- /dev/null +++ b/hotspot/agent/src/os/win32/isNT4.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "isNT4.hpp" +#include + +bool +isNT4() { + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + + if (!GetVersionEx(&info)) { + return false; + } + + return ((info.dwPlatformId == VER_PLATFORM_WIN32_NT) && + (info.dwMajorVersion == 4)); +} diff --git a/hotspot/agent/src/os/win32/isNT4.hpp b/hotspot/agent/src/os/win32/isNT4.hpp new file mode 100644 index 00000000000..fc0de6aa5c2 --- /dev/null +++ b/hotspot/agent/src/os/win32/isNT4.hpp @@ -0,0 +1,34 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _ISNT4_H_ +#define _ISNT4_H_ + +// We need to special-case the Windows NT 4.0 implementations of some +// of the debugging routines because the Tool Help API is not +// available on this platform. + +bool isNT4(); + +#endif // #defined _ISNT4_H_ diff --git a/hotspot/agent/src/os/win32/libInfo.cpp b/hotspot/agent/src/os/win32/libInfo.cpp new file mode 100644 index 00000000000..b89aa83c5a2 --- /dev/null +++ b/hotspot/agent/src/os/win32/libInfo.cpp @@ -0,0 +1,186 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Disable too-long symbol warnings +#pragma warning ( disable : 4786 ) + +#include "libInfo.hpp" +#include "nt4internals.hpp" +#include "isNT4.hpp" +#include "toolHelp.hpp" +#include + +using namespace std; + +typedef void LibInfoImplFunc(DWORD pid, vector& info); + +static void libInfoImplNT4(DWORD pid, vector& info); +static void libInfoImplToolHelp(DWORD pid, vector& info); + +void +libInfo(DWORD pid, vector& info) { + static LibInfoImplFunc* impl = NULL; + + if (impl == NULL) { + // See which operating system we're on + impl = (isNT4() ? &libInfoImplNT4 : &libInfoImplToolHelp); + } + + assert(impl != NULL); + + (*impl)(pid, info); +} + +static ULONG +ModuleCount(NT4::PDEBUG_BUFFER db) { + return db->ModuleInformation ? *PULONG(db->ModuleInformation) : 0; +} + +#define MAX2(a, b) (((a) < (b)) ? (b) : (a)) + +static void +libInfoImplNT4(DWORD pid, vector& info) { + static EnumProcessModulesFunc* enumFunc = NULL; + static GetModuleFileNameExFunc* fnFunc = NULL; + static GetModuleInformationFunc* infoFunc = NULL; + + if (enumFunc == NULL) { + HMODULE dll = loadPSAPIDLL(); + + enumFunc = (EnumProcessModulesFunc*) GetProcAddress(dll, "EnumProcessModules"); + fnFunc = (GetModuleFileNameExFunc*) GetProcAddress(dll, "GetModuleFileNameExA"); + infoFunc = (GetModuleInformationFunc*) GetProcAddress(dll, "GetModuleInformation"); + + assert(enumFunc != NULL); + assert(fnFunc != NULL); + assert(infoFunc != NULL); + } + + static HMODULE* mods = new HMODULE[256]; + static int numMods = 256; + + if (mods == NULL) { + mods = new HMODULE[numMods]; + if (mods == NULL) { + return; + } + } + + bool done = false; + + HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (proc == NULL) { + return; + } + + do { + DWORD bufSize = numMods * sizeof(HMODULE); + DWORD neededSize; + + if (!(*enumFunc)(proc, mods, bufSize, &neededSize)) { + // Enum failed + CloseHandle(proc); + return; + } + + int numFetched = neededSize / sizeof(HMODULE); + + if (numMods < numFetched) { + // Grow buffer + numMods = MAX2(numFetched, 2 * numMods); + delete[] mods; + mods = new HMODULE[numMods]; + if (mods == NULL) { + CloseHandle(proc); + return; + } + } else { + char filename[MAX_PATH]; + MODULEINFO modInfo; + + // Iterate through and fetch each one's info + for (int i = 0; i < numFetched; i++) { + if (!(*fnFunc)(proc, mods[i], filename, MAX_PATH)) { + CloseHandle(proc); + return; + } + + if (!(*infoFunc)(proc, mods[i], &modInfo, sizeof(MODULEINFO))) { + CloseHandle(proc); + return; + } + + info.push_back(LibInfo(string(filename), (void*) modInfo.lpBaseOfDll)); + } + + done = true; + } + } while (!done); + + CloseHandle(proc); + return; +} + +void +libInfoImplToolHelp(DWORD pid, vector& info) { + using namespace ToolHelp; + + static CreateToolhelp32SnapshotFunc* snapshotFunc = NULL; + static Module32FirstFunc* firstFunc = NULL; + static Module32NextFunc* nextFunc = NULL; + + if (snapshotFunc == NULL) { + HMODULE dll = loadDLL(); + + snapshotFunc = + (CreateToolhelp32SnapshotFunc*) GetProcAddress(dll, + "CreateToolhelp32Snapshot"); + + firstFunc = (Module32FirstFunc*) GetProcAddress(dll, + "Module32First"); + + nextFunc = (Module32NextFunc*) GetProcAddress(dll, + "Module32Next"); + + assert(snapshotFunc != NULL); + assert(firstFunc != NULL); + assert(nextFunc != NULL); + } + + HANDLE snapshot = (*snapshotFunc)(TH32CS_SNAPMODULE, pid); + if (snapshot == (HANDLE) -1) { + // Error occurred during snapshot + return; + } + + // Iterate + MODULEENTRY32 module; + if ((*firstFunc)(snapshot, &module)) { + do { + info.push_back(LibInfo(string(module.szExePath), (void*) module.modBaseAddr)); + } while ((*nextFunc)(snapshot, &module)); + } + + CloseHandle(snapshot); +} diff --git a/hotspot/agent/src/os/win32/libInfo.hpp b/hotspot/agent/src/os/win32/libInfo.hpp new file mode 100644 index 00000000000..60a8f0c9ae7 --- /dev/null +++ b/hotspot/agent/src/os/win32/libInfo.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _LIBINFO_ +#define _LIBINFO_ + +#include +#include +#include + +struct LibInfo { + std::string name; + void* base; + + LibInfo(const std::string& name, void* base) { + this->name = name; + this->base = base; + } +}; + +void libInfo(DWORD pid, std::vector& info); + +#endif // #defined _LIBINFO_ diff --git a/hotspot/agent/src/os/win32/nt4internals.cpp b/hotspot/agent/src/os/win32/nt4internals.cpp new file mode 100644 index 00000000000..0bd6066b6d3 --- /dev/null +++ b/hotspot/agent/src/os/win32/nt4internals.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "nt4internals.hpp" +#include +#include +#include + +namespace NT4 { + +static HMODULE ntDLL = NULL; + +HMODULE loadNTDLL() { + if (ntDLL == NULL) { + ntDLL = LoadLibrary("NTDLL.DLL"); + } + + assert(ntDLL != NULL); + return ntDLL; +} + +void unloadNTDLL() { + if (ntDLL != NULL) { + FreeLibrary(ntDLL); + ntDLL = NULL; + } +} + +} // namespace NT4 + +static HMODULE psapiDLL = NULL; + +HMODULE +loadPSAPIDLL() { + if (psapiDLL == NULL) { + psapiDLL = LoadLibrary("PSAPI.DLL"); + } + + if (psapiDLL == NULL) { + fprintf(stderr, "Simple Windows Debug Server requires PSAPI.DLL on Windows NT 4.0.\n"); + fprintf(stderr, "Please install this DLL from the SDK and restart the server.\n"); + exit(1); + } + + return psapiDLL; +} + +void +unloadPSAPIDLL() { + if (psapiDLL != NULL) { + FreeLibrary(psapiDLL); + psapiDLL = NULL; + } +} diff --git a/hotspot/agent/src/os/win32/nt4internals.hpp b/hotspot/agent/src/os/win32/nt4internals.hpp new file mode 100644 index 00000000000..b44956ddf7e --- /dev/null +++ b/hotspot/agent/src/os/win32/nt4internals.hpp @@ -0,0 +1,273 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _NT4INTERNALS_H_ +#define _NT4INTERNALS_H_ + +#include + +namespace NT4 { +extern "C" { + +// Data structures and constants required to be able to get necessary +// debugging-related information on Windows NT 4.0 through internal +// (i.e., non-public) APIs. These are adapted from those in the +// _Windows NT/2000 Native API Reference_ by Gary Nebbett, Macmillan +// Technical Publishing, 201 West 103rd Street, Indianapolis, IN +// 46290, 2000. + +typedef LONG NTSTATUS; +typedef LONG KPRIORITY; + +#if (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED) +#define NTAPI __stdcall +#else +#define _cdecl +#define NTAPI +#endif + +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemProcessesAndThreadsInformation = 5 +} SYSTEM_INFORMATION_CLASS; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; + +typedef struct _VM_COUNTERS { + ULONG PeakVirtualSize; + ULONG VirtualSize; + ULONG PageFaultCount; + ULONG PeakWorkingSetSize; + ULONG WorkingSetSize; + ULONG QuotaPeakPagedPoolUsage; + ULONG QuotaPagedPoolUsage; + ULONG QuotaPeakNonPagedPoolUsage; + ULONG QuotaNonPagedPoolUsage; + ULONG PagefileUsage; + ULONG PeakPagefileUsage; +} VM_COUNTERS, *PVM_COUNTERS; + +typedef struct _IO_COUNTERS { + LARGE_INTEGER ReadOperationCount; + LARGE_INTEGER WriteOperationCount; + LARGE_INTEGER OtherOperationCount; + LARGE_INTEGER ReadTransferCount; + LARGE_INTEGER WriteTransferCount; + LARGE_INTEGER OtherTransferCount; +} IO_COUNTERS, *PIO_COUNTERS; + +typedef struct _CLIENT_ID { + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +typedef enum { + StateInitialized, + StateReady, + StateRunning, + StateStandby, + StateTerminated, + StateWait, + StateTransition, + StateUnknown +} THREAD_STATE; + +typedef enum { + Executive, + FreePage, + PageIn, + PoolAllocation, + DelayExecution, + Suspended, + UserRequest, + WrExecutive, + WrFreePage, + WrPageIn, + WrPoolAllocation, + WrDelayExecution, + WrSuspended, + WrUserRequest, + WrEventPair, + WrQueue, + WrLpcReceive, + WrLpcReply, + WrVirtualMemory, + WrPageOut, + WrRendezvous, + Spare2, + Spare3, + Spare4, + Spare5, + Spare6, + WrKernel +} KWAIT_REASON; + +typedef struct _SYSTEM_THREADS { + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER CreateTime; + ULONG WaitTime; + PVOID StartAddress; + CLIENT_ID ClientId; + KPRIORITY Priority; + KPRIORITY BasePriority; + ULONG ContextSwitchCount; + THREAD_STATE State; + KWAIT_REASON WaitReason; +} SYSTEM_THREADS, *PSYSTEM_THREADS; + +typedef struct _SYSTEM_PROCESSES { // Information class 5 + ULONG NextEntryDelta; + ULONG ThreadCount; + ULONG Reserved1[6]; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ProcessName; + KPRIORITY BasePriority; + ULONG ProcessId; + ULONG InheritedFromProcessId; + ULONG HandleCount; + ULONG Reserved2[2]; + ULONG PrivatePageCount; + VM_COUNTERS VmCounters; + IO_COUNTERS IoCounters; // Windows 2000 only + SYSTEM_THREADS Threads[1]; +} SYSTEM_PROCESSES, *PSYSTEM_PROCESSES; + +typedef NTSTATUS NTAPI +ZwQuerySystemInformationFunc(IN SYSTEM_INFORMATION_CLASS SystemInformationClass, + IN OUT PVOID SystemInformation, + IN ULONG SystemInformationLength, + OUT PULONG ReturnLength OPTIONAL + ); + +typedef struct _DEBUG_BUFFER { + HANDLE SectionHandle; + PVOID SectionBase; + PVOID RemoteSectionBase; + ULONG SectionBaseDelta; + HANDLE EventPairHandle; + ULONG Unknown[2]; + HANDLE RemoteThreadHandle; + ULONG InfoClassMask; + ULONG SizeOfInfo; + ULONG AllocatedSize; + ULONG SectionSize; + PVOID ModuleInformation; + PVOID BackTraceInformation; + PVOID HeapInformation; + PVOID LockInformation; + PVOID Reserved[8]; +} DEBUG_BUFFER, *PDEBUG_BUFFER; + +typedef PDEBUG_BUFFER NTAPI +RtlCreateQueryDebugBufferFunc(IN ULONG Size, + IN BOOLEAN EventPair); + +#define PDI_MODULES 0x01 // The loaded modules of the process +#define PDI_BACKTRACE 0x02 // The heap stack back traces +#define PDI_HEAPS 0x04 // The heaps of the process +#define PDI_HEAP_TAGS 0x08 // The heap tags +#define PDI_HEAP_BLOCKS 0x10 // The heap blocks +#define PDI_LOCKS 0x20 // The locks created by the process + +typedef struct _DEBUG_MODULE_INFORMATION { // c.f. SYSTEM_MODULE_INFORMATION + ULONG Reserved[2]; + ULONG Base; + ULONG Size; + ULONG Flags; + USHORT Index; + USHORT Unknown; + USHORT LoadCount; + USHORT ModuleNameOffset; + CHAR ImageName[256]; +} DEBUG_MODULE_INFORMATION, *PDEBUG_MODULE_INFORMATION; + +// Flags +#define LDRP_STATIC_LINK 0x00000002 +#define LDRP_IMAGE_DLL 0x00000004 +#define LDRP_LOAD_IN_PROGRESS 0x00001000 +#define LDRP_UNLOAD_IN_PROGRESS 0x00002000 +#define LDRP_ENTRY_PROCESSED 0x00004000 +#define LDRP_ENTRY_INSERTED 0x00008000 +#define LDRP_CURRENT_LOAD 0x00010000 +#define LDRP_FAILED_BUILTIN_LOAD 0x00020000 +#define LDRP_DONT_CALL_FOR_THREADS 0x00040000 +#define LDRP_PROCESS_ATTACH_CALLED 0x00080000 +#define LDRP_DEBUG_SYMBOLS_LOADED 0x00100000 +#define LDRP_IMAGE_NOT_AT_BASE 0x00200000 +#define LDRP_WX86_IGNORE_MACHINETYPE 0x00400000 + +// NOTE that this will require creating a thread in the target +// process, implying that we can not call this while the process is +// suspended. May have to run this command in the child processes +// rather than the server. + +typedef NTSTATUS NTAPI +RtlQueryProcessDebugInformationFunc(IN ULONG ProcessId, + IN ULONG DebugInfoClassMask, + IN OUT PDEBUG_BUFFER DebugBuffer); + +typedef NTSTATUS NTAPI +RtlDestroyQueryDebugBufferFunc(IN PDEBUG_BUFFER DebugBuffer); + +// Routines to load and unload NTDLL.DLL. +HMODULE loadNTDLL(); +// Safe to call even if has not been loaded +void unloadNTDLL(); + +} // extern "C" +} // namespace NT4 + +//---------------------------------------------------------------------- + +// On NT 4 only, we now use PSAPI to enumerate the loaded modules in +// the target processes. RtlQueryProcessDebugInformation creates a +// thread in the target process, which causes problems when we are +// handling events like breakpoints in the debugger. The dependence on +// an external DLL which might not be present is unfortunate, but we +// can either redistribute this DLL (if allowed) or refuse to start on +// NT 4 if it is not present. + +typedef struct _MODULEINFO { + LPVOID lpBaseOfDll; + DWORD SizeOfImage; + LPVOID EntryPoint; +} MODULEINFO, *LPMODULEINFO; + +typedef BOOL (WINAPI EnumProcessModulesFunc)(HANDLE, HMODULE *, DWORD, LPDWORD); +typedef DWORD (WINAPI GetModuleFileNameExFunc)(HANDLE, HMODULE, LPTSTR, DWORD); +typedef BOOL (WINAPI GetModuleInformationFunc)(HANDLE, HMODULE, LPMODULEINFO, DWORD); +// Routines to load and unload PSAPI.DLL. +HMODULE loadPSAPIDLL(); +// Safe to call even if has not been loaded +void unloadPSAPIDLL(); + +#endif // #defined _NT4INTERNALS_H_ diff --git a/hotspot/agent/src/os/win32/ports.h b/hotspot/agent/src/os/win32/ports.h new file mode 100644 index 00000000000..f9e1fbab059 --- /dev/null +++ b/hotspot/agent/src/os/win32/ports.h @@ -0,0 +1,32 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _PORTS_H_ +#define _PORTS_H_ + +// This is the "public" port which end-user clients can connect to +// with an arbitrary application, including telnet. +const short CLIENT_PORT = 27000; + +#endif // #defined _PORTS_H_ diff --git a/hotspot/agent/src/os/win32/procList.cpp b/hotspot/agent/src/os/win32/procList.cpp new file mode 100644 index 00000000000..c5d66e659a4 --- /dev/null +++ b/hotspot/agent/src/os/win32/procList.cpp @@ -0,0 +1,190 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "procList.hpp" +#include "nt4internals.hpp" +#include "isNT4.hpp" +#include "toolHelp.hpp" +#include + +using namespace std; +using namespace NT4; + +typedef void ProcListImplFunc(ProcEntryList& processes); + +void procListImplNT4(ProcEntryList& processes); +void procListImplToolHelp(ProcEntryList& processes); + +ProcEntry::ProcEntry(ULONG pid, USHORT nameLength, WCHAR* name) { + this->pid = pid; + this->nameLength = nameLength; + this->name = new WCHAR[nameLength]; + memcpy(this->name, name, nameLength * sizeof(WCHAR)); +} + +ProcEntry::ProcEntry(ULONG pid, USHORT nameLength, char* name) { + this->pid = pid; + this->nameLength = nameLength; + this->name = new WCHAR[nameLength]; + int j = 0; + for (int i = 0; i < nameLength; i++) { + // FIXME: what is the proper promotion from ASCII to UNICODE? + this->name[i] = name[i] & 0xFF; + } +} + +ProcEntry::ProcEntry(const ProcEntry& arg) { + name = NULL; + copyFrom(arg); +} + +ProcEntry& +ProcEntry::operator=(const ProcEntry& arg) { + copyFrom(arg); + return *this; +} + +ProcEntry::~ProcEntry() { + delete[] name; +} + +void +ProcEntry::copyFrom(const ProcEntry& arg) { + if (name != NULL) { + delete[] name; + } + pid = arg.pid; + nameLength = arg.nameLength; + name = new WCHAR[nameLength]; + memcpy(name, arg.name, nameLength * sizeof(WCHAR)); +} + +ULONG +ProcEntry::getPid() { + return pid; +} + +USHORT +ProcEntry::getNameLength() { + return nameLength; +} + +WCHAR* +ProcEntry::getName() { + return name; +} + +void +procList(ProcEntryList& processes) { + static ProcListImplFunc* impl = NULL; + + if (impl == NULL) { + // See which operating system we're on + impl = (isNT4() ? &procListImplNT4 : &procListImplToolHelp); + } + + assert(impl != NULL); + + (*impl)(processes); +} + +void +procListImplNT4(ProcEntryList& processes) { + using namespace NT4; + + static ZwQuerySystemInformationFunc* query = NULL; + + if (query == NULL) { + HMODULE ntDLL = loadNTDLL(); + query = + (ZwQuerySystemInformationFunc*) GetProcAddress(ntDLL, + "ZwQuerySystemInformation"); + assert(query != NULL); + } + + ULONG n = 0x100; + PSYSTEM_PROCESSES sp = new SYSTEM_PROCESSES[n]; + while ((*query)(SystemProcessesAndThreadsInformation, + sp, n * sizeof(SYSTEM_PROCESSES), 0) == STATUS_INFO_LENGTH_MISMATCH) { + delete[] sp; + n *= 2; + sp = new SYSTEM_PROCESSES[n]; + } + + bool done = false; + for (PSYSTEM_PROCESSES p = sp; !done; + p = PSYSTEM_PROCESSES(PCHAR(p) + p->NextEntryDelta)) { + processes.push_back(ProcEntry(p->ProcessId, + p->ProcessName.Length / 2, + p->ProcessName.Buffer)); + done = p->NextEntryDelta == 0; + } +} + +void +procListImplToolHelp(ProcEntryList& processes) { + using namespace ToolHelp; + + static CreateToolhelp32SnapshotFunc* snapshotFunc = NULL; + static Process32FirstFunc* firstFunc = NULL; + static Process32NextFunc* nextFunc = NULL; + + if (snapshotFunc == NULL) { + HMODULE dll = loadDLL(); + + snapshotFunc = + (CreateToolhelp32SnapshotFunc*) GetProcAddress(dll, + "CreateToolhelp32Snapshot"); + + firstFunc = (Process32FirstFunc*) GetProcAddress(dll, + "Process32First"); + + nextFunc = (Process32NextFunc*) GetProcAddress(dll, + "Process32Next"); + + assert(snapshotFunc != NULL); + assert(firstFunc != NULL); + assert(nextFunc != NULL); + } + + HANDLE snapshot = (*snapshotFunc)(TH32CS_SNAPPROCESS, 0 /* ignored */); + if (snapshot == (HANDLE) -1) { + // Error occurred during snapshot + return; + } + + // Iterate + PROCESSENTRY32 proc; + if ((*firstFunc)(snapshot, &proc)) { + do { + // FIXME: could make this uniform to the NT version by cutting + // off the path name just before the executable name + processes.push_back(ProcEntry(proc.th32ProcessID, + strlen(proc.szExeFile), + proc.szExeFile)); + } while ((*nextFunc)(snapshot, &proc)); + } + + CloseHandle(snapshot); +} diff --git a/hotspot/agent/src/os/win32/procList.hpp b/hotspot/agent/src/os/win32/procList.hpp new file mode 100644 index 00000000000..24e40c268c2 --- /dev/null +++ b/hotspot/agent/src/os/win32/procList.hpp @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _PROCLIST_ +#define _PROCLIST_ + +#include +#include + +class ProcEntry { +public: + /** name may not be NULL */ + ProcEntry(ULONG pid, USHORT nameLength, wchar_t* name); + ProcEntry(ULONG pid, USHORT nameLength, char* name); + ~ProcEntry(); + ProcEntry(const ProcEntry& arg); + ProcEntry& operator=(const ProcEntry& arg); + + ULONG getPid(); + /** Returns number of WCHAR characters in getName() */ + USHORT getNameLength(); + WCHAR* getName(); + +private: + ULONG pid; + USHORT nameLength; + WCHAR* name; + void copyFrom(const ProcEntry& arg); +}; + +typedef std::vector ProcEntryList; +void procList(ProcEntryList& processes); + +#endif // #defined _PROCLIST_ diff --git a/hotspot/agent/src/os/win32/serverLists.cpp b/hotspot/agent/src/os/win32/serverLists.cpp new file mode 100644 index 00000000000..4c4f3090e2d --- /dev/null +++ b/hotspot/agent/src/os/win32/serverLists.cpp @@ -0,0 +1,270 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include "serverLists.hpp" + +//---------------------------------------------------------------------- +// Lists +// + +CRITICAL_SECTION Lists::crit; + +void +Lists::init() { + InitializeCriticalSection(&crit); +} + +void +Lists::lock() { + EnterCriticalSection(&crit); +} + +void +Lists::unlock() { + LeaveCriticalSection(&crit); +} + +//---------------------------------------------------------------------- +// ListsLocker +// + +ListsLocker::ListsLocker() { + Lists::lock(); +} + +ListsLocker::~ListsLocker() { + Lists::unlock(); +} + +//---------------------------------------------------------------------- +// ChildInfo +// + +ChildInfo::ChildInfo(DWORD pid, HANDLE childProcessHandle, + HANDLE writeToStdinHandle, HANDLE readFromStdoutHandle, + HANDLE auxHandle1, HANDLE auxHandle2) { + this->pid = pid; + this->childProcessHandle = childProcessHandle; + this->writeToStdinHandle = writeToStdinHandle; + this->readFromStdoutHandle = readFromStdoutHandle; + this->auxHandle1 = auxHandle1; + this->auxHandle2 = auxHandle2; + client = NULL; +} + +DWORD +ChildInfo::getPid() { + return pid; +} + +HANDLE +ChildInfo::getChildProcessHandle() { + return childProcessHandle; +} + +HANDLE +ChildInfo::getWriteToStdinHandle() { + return writeToStdinHandle; +} + +HANDLE +ChildInfo::getReadFromStdoutHandle() { + return readFromStdoutHandle; +} + +void +ChildInfo::setClient(ClientInfo* clientInfo) { + client = clientInfo; +} + +ClientInfo* +ChildInfo::getClient() { + return client; +} + +void +ChildInfo::closeAll() { + CloseHandle(childProcessHandle); + CloseHandle(writeToStdinHandle); + CloseHandle(readFromStdoutHandle); + CloseHandle(auxHandle1); + CloseHandle(auxHandle2); +} + +//---------------------------------------------------------------------- +// ChildList +// + +ChildList::ChildList() { +} + +ChildList::~ChildList() { +} + +void +ChildList::addChild(ChildInfo* info) { + // Could store these in binary sorted order by pid for efficiency + childList.push_back(info); +} + +ChildInfo* +ChildList::removeChild(HANDLE childProcessHandle) { + for (ChildInfoList::iterator iter = childList.begin(); iter != childList.end(); + iter++) { + ChildInfo* info = *iter; + if (info->getChildProcessHandle() == childProcessHandle) { + childList.erase(iter); + return info; + } + } + assert(false); + return NULL; +} + +void +ChildList::removeChild(ChildInfo* info) { + for (ChildInfoList::iterator iter = childList.begin(); iter != childList.end(); + iter++) { + if (*iter == info) { + childList.erase(iter); + return; + } + } + assert(false); +} + +ChildInfo* +ChildList::getChildByPid(DWORD pid) { + for (ChildInfoList::iterator iter = childList.begin(); iter != childList.end(); + iter++) { + ChildInfo* info = *iter; + if (info->getPid() == pid) { + return info; + } + } + return NULL; +} + +int +ChildList::size() { + return childList.size(); +} + +ChildInfo* +ChildList::getChildByIndex(int index) { + return childList[index]; +} + +//---------------------------------------------------------------------- +// ClientInfo +// + +ClientInfo::ClientInfo(SOCKET dataSocket) { + this->dataSocket = dataSocket; + buf = new IOBuf(32768, 131072); + buf->setSocket(dataSocket); + target = NULL; +} + +ClientInfo::~ClientInfo() { + delete buf; +} + +SOCKET +ClientInfo::getDataSocket() { + return dataSocket; +} + +IOBuf* +ClientInfo::getIOBuf() { + return buf; +} + +void +ClientInfo::setTarget(ChildInfo* childInfo) { + target = childInfo; +} + +ChildInfo* +ClientInfo::getTarget() { + return target; +} + +void +ClientInfo::closeAll() { + shutdown(dataSocket, SD_BOTH); + closesocket(dataSocket); + dataSocket = INVALID_SOCKET; +} + +//---------------------------------------------------------------------- +// ClientList +// + +ClientList::ClientList() { +} + +ClientList::~ClientList() { +} + +void +ClientList::addClient(ClientInfo* info) { + clientList.push_back(info); +} + +bool +ClientList::isAnyDataSocketSet(fd_set* fds, ClientInfo** out) { + for (ClientInfoList::iterator iter = clientList.begin(); iter != clientList.end(); + iter++) { + ClientInfo* info = *iter; + if (FD_ISSET(info->getDataSocket(), fds)) { + *out = info; + return true; + } + } + return false; +} + +void +ClientList::removeClient(ClientInfo* client) { + for (ClientInfoList::iterator iter = clientList.begin(); iter != clientList.end(); + iter++) { + if (*iter == client) { + clientList.erase(iter); + return; + } + } + assert(false); +} + +int +ClientList::size() { + return clientList.size(); +} + +ClientInfo* +ClientList::get(int num) { + return clientList[num]; +} diff --git a/hotspot/agent/src/os/win32/serverLists.hpp b/hotspot/agent/src/os/win32/serverLists.hpp new file mode 100644 index 00000000000..3f9bd90f329 --- /dev/null +++ b/hotspot/agent/src/os/win32/serverLists.hpp @@ -0,0 +1,204 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _SERVER_LISTS_ +#define _SERVER_LISTS_ + +#include +#include +#include "IOBuf.hpp" + +// +// NOTE: +// +// All of these lists are guarded by the global lock managed by the +// Lists class. Lists::init() must be called at the start of the +// program. +// + +class Lists { + friend class ListsLocker; +public: + static void init(); +private: + static void lock(); + static void unlock(); + static CRITICAL_SECTION crit; +}; + +// Should be allocated on stack. Ensures proper locking/unlocking +// pairing. +class ListsLocker { +public: + ListsLocker(); + ~ListsLocker(); +}; + +// We must keep track of all of the child processes we have forked to +// handle attaching to a target process. This is necessary because we +// allow clients to detach from processes, but the child processes we +// fork must necessarily stay alive for the duration of the target +// application. A subsequent attach operation to the target process +// results in the same child process being reused. For this reason, +// child processes are known to be in one of two states: attached and +// detached. + +class ClientInfo; + +class ChildInfo { +public: + /** The pid of the ChildInfo indicates the process ID of the target + process which the subprocess was created to debug, not the pid + of the subprocess itself. */ + ChildInfo(DWORD pid, HANDLE childProcessHandle, + HANDLE writeToStdinHandle, HANDLE readFromStdoutHandle, + HANDLE auxHandle1, HANDLE auxHandle2); + + DWORD getPid(); + HANDLE getChildProcessHandle(); + HANDLE getWriteToStdinHandle(); + HANDLE getReadFromStdoutHandle(); + + /** Set the client which is currently attached to the target process + via this child process. Set this to NULL to indicate that the + child process is ready to accept another attachment. */ + void setClient(ClientInfo* clientInfo); + + ClientInfo* getClient(); + + /** This is NOT automatically called in the destructor */ + void closeAll(); + +private: + DWORD pid; + HANDLE childProcessHandle; + HANDLE writeToStdinHandle; + HANDLE readFromStdoutHandle; + HANDLE auxHandle1; + HANDLE auxHandle2; + ClientInfo* client; +}; + +// We keep track of a list of child debugger processes, each of which +// is responsible for debugging a certain target process. These +// debugger processes can serve multiple clients during their +// lifetime. When a client detaches from a given process or tells the +// debugger to "exit", the debug server is notified that the child +// process is once again available to accept connections from clients. + +class ChildList { +private: + typedef std::vector ChildInfoList; + +public: + ChildList(); + ~ChildList(); + + void addChild(ChildInfo*); + + /** Removes and returns the ChildInfo* associated with the given + child process handle. */ + ChildInfo* removeChild(HANDLE childProcessHandle); + + /** Removes the given ChildInfo. */ + void removeChild(ChildInfo* info); + + /** Return the ChildInfo* associated with a given process ID without + removing it from the list. */ + ChildInfo* getChildByPid(DWORD pid); + + /** Iteration support */ + int size(); + + /** Iteration support */ + ChildInfo* getChildByIndex(int index); + +private: + ChildInfoList childList; +}; + +// We also keep a list of clients whose requests we are responsible +// for serving. Clients can attach and detach from child processes. + +class ClientInfo { +public: + ClientInfo(SOCKET dataSocket); + ~ClientInfo(); + + SOCKET getDataSocket(); + /** Gets an IOBuf configured for the data socket, which should be + used for all communication with the client. */ + IOBuf* getIOBuf(); + + /** Set the information for the process to which this client is + attached. Set this to NULL to indicate that the client is not + currently attached to any target process. */ + void setTarget(ChildInfo* childInfo); + + /** Get the information for the process to which this client is + currently attached, or NULL if none. */ + ChildInfo* getTarget(); + + /** Close down the socket connection to this client. This is NOT + automatically called by the destructor. */ + void closeAll(); + +private: + SOCKET dataSocket; + IOBuf* buf; + ChildInfo* target; +}; + +class ClientList { +private: + typedef std::vector ClientInfoList; + +public: + ClientList(); + ~ClientList(); + + /** Adds a client to the list. */ + void addClient(ClientInfo* info); + + /** Check to see whether the parent socket of any of the ClientInfo + objects is readable in the given fd_set. If so, returns TRUE and + sets the given ClientInfo* (a non-NULL pointer to which must be + given) appropriately. */ + bool isAnyDataSocketSet(fd_set* fds, ClientInfo** info); + + /** Removes a client from the list. User is responsible for deleting + the ClientInfo* using operator delete. */ + void removeClient(ClientInfo* client); + + /** Iteration support. */ + int size(); + + /** Iteration support. */ + ClientInfo* get(int num); + +private: + ClientInfoList clientList; +}; + +#endif // #defined _SERVER_LISTS_ diff --git a/hotspot/agent/src/os/win32/toolHelp.cpp b/hotspot/agent/src/os/win32/toolHelp.cpp new file mode 100644 index 00000000000..a6bb10f8c9c --- /dev/null +++ b/hotspot/agent/src/os/win32/toolHelp.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "toolHelp.hpp" +#include + +namespace ToolHelp { + +static HMODULE kernelDLL = NULL; + +HMODULE loadDLL() { + if (kernelDLL == NULL) { + kernelDLL = LoadLibrary("KERNEL32.DLL"); + } + + assert(kernelDLL != NULL); + return kernelDLL; +} + +void unloadDLL() { + if (kernelDLL != NULL) { + FreeLibrary(kernelDLL); + kernelDLL = NULL; + } +} + +} // namespace ToolHelp diff --git a/hotspot/agent/src/os/win32/toolHelp.hpp b/hotspot/agent/src/os/win32/toolHelp.hpp new file mode 100644 index 00000000000..223e1885f2e --- /dev/null +++ b/hotspot/agent/src/os/win32/toolHelp.hpp @@ -0,0 +1,75 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _TOOLHELP_H_ +#define _TOOLHELP_H_ + +#include +#include + +namespace ToolHelp { +extern "C" { + + /////////////// + // Snapshots // + /////////////// + typedef HANDLE WINAPI + CreateToolhelp32SnapshotFunc(DWORD dwFlags, DWORD th32ProcessID); + + ////////////////// + // Process List // + ////////////////// + typedef BOOL WINAPI Process32FirstFunc(HANDLE hSnapshot, + LPPROCESSENTRY32 lppe); + + typedef BOOL WINAPI Process32NextFunc(HANDLE hSnapshot, + LPPROCESSENTRY32 lppe); + + // NOTE: although these routines are defined in TLHELP32.H, they + // seem to always return false (maybe only under US locales) + typedef BOOL WINAPI Process32FirstWFunc(HANDLE hSnapshot, + LPPROCESSENTRY32W lppe); + + typedef BOOL WINAPI Process32NextWFunc(HANDLE hSnapshot, + LPPROCESSENTRY32W lppe); + + ///////////////// + // Module List // + ///////////////// + typedef BOOL WINAPI + Module32FirstFunc(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + + typedef BOOL WINAPI + Module32NextFunc (HANDLE hSnapshot, LPMODULEENTRY32 lpme); + + + // Routines to load and unload KERNEL32.DLL. + HMODULE loadDLL(); + // Safe to call even if has not been loaded + void unloadDLL(); + +} // extern "C" +} // namespace "ToolHelp" + +#endif // #defined _TOOLHELP_H_ diff --git a/hotspot/agent/src/os/win32/windbg/Makefile b/hotspot/agent/src/os/win32/windbg/Makefile new file mode 100644 index 00000000000..5fc9c68a334 --- /dev/null +++ b/hotspot/agent/src/os/win32/windbg/Makefile @@ -0,0 +1,83 @@ +# +# Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# set WINDBG_HOME and JAVA_HOME environment variables before this make. + +SAWINDBGDLL = sawindbg.dll +CPP32=cl.exe +CPP64=cl.exe +LINK32=link.exe +LINK64=link.exe +JAVAH=$(JAVA_HOME)/bin/javah +WINDBG_INCLUDE=$(WINDBG_HOME)/sdk/inc +WINDBG_LIB32=$(WINDBG_HOME)/sdk/lib/i386 +WINDBG_LIB_IA64=$(WINDBG_HOME)/sdk/lib/ia64 +WINDBG_LIB_AMD64=$(WINDBG_HOME)/sdk/lib/amd64 + +# These do not need to be optimized (don't run a lot of code) and it +# will be useful to have the assertion checks in place + +CFLAGS32=/nologo /MD /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_WINDOWS" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c + +CFLAGS64=/nologo /MD /W3 /GX /Od /D "WIN32" /D "WIN64" /D "_WINDOWS" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c + +LIBS32= $(WINDBG_LIB32)/dbgeng.lib \ + /nologo /subsystem:console /debug /machine:I386 + +LIBS_IA64= $(WINDBG_LIB_IA64)/dbgeng.lib \ + /nologo /subsystem:console /debug /machine:IA64 + +LIBS_AMD64= $(WINDBG_LIB_AMD64)/dbgeng.lib bufferoverflowU.lib \ + /nologo /subsystem:console /debug /machine:AMD64 + +default: i386/$(SAWINDBGDLL) + +ia64: ia64/$(SAWINDBGDLL) + +amd64: amd64/$(SAWINDBGDLL) + +i386/$(SAWINDBGDLL) : sawindbg.cpp + @ mkdir -p i386 + @ $(JAVAH) -jni -classpath ../../../../build/classes sun.jvm.hotspot.debugger.windbg.WindbgDebuggerLocal sun.jvm.hotspot.debugger.x86.X86ThreadContext + @ $(CPP32) /I$(JAVA_HOME)/include /I$(JAVA_HOME)/include/win32 /I$(WINDBG_INCLUDE) $(CFLAGS32) /Fp"i386/sawindbg.pch" /Fo"i386/" /Fd"i386/" /c sawindbg.cpp + $(LINK32) /out:$@ /DLL i386/sawindbg.obj $(LIBS32) + +ia64/$(SAWINDBGDLL) : sawindbg.cpp + @ mkdir -p ia64 + @ $(JAVAH) -jni -classpath ../../../../build/classes sun.jvm.hotspot.debugger.windbg.WindbgDebuggerLocal sun.jvm.hotspot.debugger.ia64.IA64ThreadContext + @ $(CPP64) /I$(JAVA_HOME)/include /I$(JAVA_HOME)/include/win32 /I$(WINDBG_INCLUDE) $(CFLAGS64) /Fp"ia64/sawindbg.pch" /Fo"ia64/" /Fd"ia64/" /c sawindbg.cpp + $(LINK64) /out:$@ /DLL ia64/sawindbg.obj $(LIBS_IA64) + +amd64/$(SAWINDBGDLL) : sawindbg.cpp + @ mkdir -p amd64 + @ $(JAVAH) -jni -classpath ../../../../build/classes sun.jvm.hotspot.debugger.windbg.WindbgDebuggerLocal sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext + @ $(CPP64) /I$(JAVA_HOME)/include /I$(JAVA_HOME)/include/win32 /I$(WINDBG_INCLUDE) $(CFLAGS64) /Fp"amd64/sawindbg.pch" /Fo"amd64/" /Fd"amd64/" /c sawindbg.cpp + $(LINK64) /out:$@ /DLL amd64/sawindbg.obj $(LIBS_AMD64) + +clean: + rm *.h + rm -rf i386 + rm -rf ia64 + rm -rf amd64 + diff --git a/hotspot/agent/src/os/win32/windbg/sawindbg.cpp b/hotspot/agent/src/os/win32/windbg/sawindbg.cpp new file mode 100644 index 00000000000..314317854e1 --- /dev/null +++ b/hotspot/agent/src/os/win32/windbg/sawindbg.cpp @@ -0,0 +1,967 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// this is source code windbg based SA debugger agent to debug +// Dr. Watson dump files and process snapshots. + +#include "sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal.h" + +#ifdef _M_IA64 + #include "sun_jvm_hotspot_debugger_ia64_IA64ThreadContext.h" + #define NPRGREG sun_jvm_hotspot_debugger_ia64_IA64ThreadContext_NPRGREG +#elif _M_IX86 + #include "sun_jvm_hotspot_debugger_x86_X86ThreadContext.h" + #define NPRGREG sun_jvm_hotspot_debugger_x86_X86ThreadContext_NPRGREG +#elif _M_AMD64 + #include "sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext.h" + #define NPRGREG sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_NPRGREG +#else + #error "SA windbg back-end is not supported for your cpu!" +#endif + +#include +#include + +#ifndef STDMETHODV +#define STDMETHODV(method) virtual HRESULT STDMETHODVCALLTYPE method +#endif + +#define DEBUG_NO_IMPLEMENTATION +#include +#include + +// simple template to manage array delete across early (error) returns + +template +class AutoArrayPtr { + T* m_ptr; + public: + AutoArrayPtr(T* ptr) : m_ptr(ptr) { + } + + ~AutoArrayPtr() { + delete [] m_ptr; + } + + T* asPtr() { + return m_ptr; + } +}; + +class AutoJavaString { + JNIEnv* m_env; + jstring m_str; + const char* m_buf; + + public: + AutoJavaString(JNIEnv* env, jstring str, const char* buf) + : m_env(env), m_str(str), m_buf(buf) { + } + + ~AutoJavaString() { + m_env->ReleaseStringUTFChars(m_str, m_buf); + } + + operator const char* () { + return m_buf; + } +}; + +// field and method IDs we want here + +static jfieldID imagePath_ID = 0; +static jfieldID symbolPath_ID = 0; +static jfieldID ptrIDebugClient_ID = 0; +static jfieldID ptrIDebugControl_ID = 0; +static jfieldID ptrIDebugDataSpaces_ID = 0; +static jfieldID ptrIDebugOutputCallbacks_ID = 0; +static jfieldID ptrIDebugAdvanced_ID = 0; +static jfieldID ptrIDebugSymbols_ID = 0; +static jfieldID ptrIDebugSystemObjects_ID = 0; + +static jmethodID addLoadObject_ID = 0; +static jmethodID addThread_ID = 0; +static jmethodID createClosestSymbol_ID = 0; +static jmethodID setThreadIntegerRegisterSet_ID = 0; + +#define CHECK_EXCEPTION_(value) if(env->ExceptionOccurred()) { return value; } +#define CHECK_EXCEPTION if(env->ExceptionOccurred()) { return;} + +#define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { \ + throwNewDebuggerException(env, str); return value; } + +#define THROW_NEW_DEBUGGER_EXCEPTION(str) { throwNewDebuggerException(env, str); \ + return;} + +static void throwNewDebuggerException(JNIEnv* env, const char* errMsg) { + env->ThrowNew(env->FindClass("sun/jvm/hotspot/debugger/DebuggerException"), errMsg); +} + +/* + * Class: sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal + * Method: initIDs + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal_initIDs + (JNIEnv *env, jclass clazz) { + imagePath_ID = env->GetStaticFieldID(clazz, "imagePath", "Ljava/lang/String;"); + CHECK_EXCEPTION; + + symbolPath_ID = env->GetStaticFieldID(clazz, "symbolPath", "Ljava/lang/String;"); + CHECK_EXCEPTION; + + ptrIDebugClient_ID = env->GetFieldID(clazz, "ptrIDebugClient", "J"); + CHECK_EXCEPTION; + + ptrIDebugControl_ID = env->GetFieldID(clazz, "ptrIDebugControl", "J"); + CHECK_EXCEPTION; + + ptrIDebugDataSpaces_ID = env->GetFieldID(clazz, "ptrIDebugDataSpaces", "J"); + CHECK_EXCEPTION; + + ptrIDebugOutputCallbacks_ID = env->GetFieldID(clazz, + "ptrIDebugOutputCallbacks", "J"); + CHECK_EXCEPTION; + + ptrIDebugAdvanced_ID = env->GetFieldID(clazz, "ptrIDebugAdvanced", "J"); + CHECK_EXCEPTION; + + ptrIDebugSymbols_ID = env->GetFieldID(clazz, + "ptrIDebugSymbols", "J"); + CHECK_EXCEPTION; + + ptrIDebugSystemObjects_ID = env->GetFieldID(clazz, + "ptrIDebugSystemObjects", "J"); + CHECK_EXCEPTION; + + addLoadObject_ID = env->GetMethodID(clazz, "addLoadObject", + "(Ljava/lang/String;JJ)V"); + CHECK_EXCEPTION; + + addThread_ID = env->GetMethodID(clazz, "addThread", "(J)V"); + CHECK_EXCEPTION; + + createClosestSymbol_ID = env->GetMethodID(clazz, "createClosestSymbol", + "(Ljava/lang/String;J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol;"); + CHECK_EXCEPTION; + + setThreadIntegerRegisterSet_ID = env->GetMethodID(clazz, + "setThreadIntegerRegisterSet", "(J[J)V"); + CHECK_EXCEPTION; + +} + +// class for IDebugOutputCallbacks + +class SAOutputCallbacks : public IDebugOutputCallbacks { + LONG m_refCount; + char* m_msgBuffer; + + public: + SAOutputCallbacks() : m_refCount(0), m_msgBuffer(0) { + } + + ~SAOutputCallbacks() { + clearBuffer(); + } + + const char* getBuffer() const { + return m_msgBuffer; + } + + void clearBuffer() { + if (m_msgBuffer) { + free(m_msgBuffer); + m_msgBuffer = 0; + } + } + + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* ppInterface); + STDMETHOD(Output)(THIS_ + IN ULONG mask, + IN PCSTR msg); +}; + +STDMETHODIMP_(ULONG) SAOutputCallbacks::AddRef(THIS) { + InterlockedIncrement(&m_refCount); + return m_refCount; +} + +STDMETHODIMP_(ULONG) SAOutputCallbacks::Release(THIS) { + LONG retVal; + InterlockedDecrement(&m_refCount); + retVal = m_refCount; + if (retVal == 0) { + delete this; + } + return retVal; +} + +STDMETHODIMP SAOutputCallbacks::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* ppInterface) { + *ppInterface = 0; + HRESULT res = E_NOINTERFACE; + if (TRUE == IsEqualIID(interfaceId, __uuidof(IUnknown)) || + TRUE == IsEqualIID(interfaceId, __uuidof(IDebugOutputCallbacks))) { + *ppInterface = (IDebugOutputCallbacks*) this; + AddRef(); + res = S_OK; + } + return res; +} + +STDMETHODIMP SAOutputCallbacks::Output(THIS_ + IN ULONG mask, + IN PCSTR msg) { + int len = (int) (strlen(msg) + 1); + if (m_msgBuffer == 0) { + m_msgBuffer = (char*) malloc(len); + if (m_msgBuffer == 0) { + fprintf(stderr, "out of memory debugger output!\n"); + return S_FALSE; + } + strcpy(m_msgBuffer, msg); + } else { + m_msgBuffer = (char*) realloc(m_msgBuffer, len + strlen(m_msgBuffer)); + if (m_msgBuffer == 0) { + fprintf(stderr, "out of memory debugger output!\n"); + return S_FALSE; + } + strcat(m_msgBuffer, msg); + } + return S_OK; +} + +static bool getWindbgInterfaces(JNIEnv* env, jobject obj) { + // get windbg interfaces .. + + IDebugClient* ptrIDebugClient = 0; + if (DebugCreate(__uuidof(IDebugClient), (PVOID*) &ptrIDebugClient) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: not able to create IDebugClient object!", false); + } + env->SetLongField(obj, ptrIDebugClient_ID, (jlong) ptrIDebugClient); + + IDebugControl* ptrIDebugControl = 0; + if (ptrIDebugClient->QueryInterface(__uuidof(IDebugControl), (PVOID*) &ptrIDebugControl) + != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: not able to get IDebugControl", false); + } + env->SetLongField(obj, ptrIDebugControl_ID, (jlong) ptrIDebugControl); + + IDebugDataSpaces* ptrIDebugDataSpaces = 0; + if (ptrIDebugClient->QueryInterface(__uuidof(IDebugDataSpaces), (PVOID*) &ptrIDebugDataSpaces) + != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: not able to get IDebugDataSpaces object!", false); + } + env->SetLongField(obj, ptrIDebugDataSpaces_ID, (jlong) ptrIDebugDataSpaces); + + SAOutputCallbacks* ptrIDebugOutputCallbacks = new SAOutputCallbacks(); + ptrIDebugOutputCallbacks->AddRef(); + env->SetLongField(obj, ptrIDebugOutputCallbacks_ID, (jlong) ptrIDebugOutputCallbacks); + CHECK_EXCEPTION_(false); + + IDebugAdvanced* ptrIDebugAdvanced = 0; + if (ptrIDebugClient->QueryInterface(__uuidof(IDebugAdvanced), (PVOID*) &ptrIDebugAdvanced) + != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: not able to get IDebugAdvanced object!", false); + } + env->SetLongField(obj, ptrIDebugAdvanced_ID, (jlong) ptrIDebugAdvanced); + + IDebugSymbols* ptrIDebugSymbols = 0; + if (ptrIDebugClient->QueryInterface(__uuidof(IDebugSymbols), (PVOID*) &ptrIDebugSymbols) + != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: not able to get IDebugSymbols object!", false); + } + env->SetLongField(obj, ptrIDebugSymbols_ID, (jlong) ptrIDebugSymbols); + + IDebugSystemObjects* ptrIDebugSystemObjects = 0; + if (ptrIDebugClient->QueryInterface(__uuidof(IDebugSystemObjects), (PVOID*) &ptrIDebugSystemObjects) + != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: not able to get IDebugSystemObjects object!", false); + } + env->SetLongField(obj, ptrIDebugSystemObjects_ID, (jlong) ptrIDebugSystemObjects); + + return true; +} + +static bool setImageAndSymbolPath(JNIEnv* env, jobject obj) { + jboolean isCopy; + jclass clazz = env->GetObjectClass(obj); + jstring path; + const char* buf; + + path = (jstring) env->GetStaticObjectField(clazz, imagePath_ID); + buf = env->GetStringUTFChars(path, &isCopy); + CHECK_EXCEPTION_(false); + AutoJavaString imagePath(env, path, buf); + + path = (jstring) env->GetStaticObjectField(clazz, symbolPath_ID); + buf = env->GetStringUTFChars(path, &isCopy); + CHECK_EXCEPTION_(false); + AutoJavaString symbolPath(env, path, buf); + + IDebugSymbols* ptrIDebugSymbols = (IDebugSymbols*) env->GetLongField(obj, + ptrIDebugSymbols_ID); + CHECK_EXCEPTION_(false); + + ptrIDebugSymbols->SetImagePath(imagePath); + ptrIDebugSymbols->SetSymbolPath(symbolPath); + return true; +} + +static bool openDumpFile(JNIEnv* env, jobject obj, jstring coreFileName) { + // open the dump file + jboolean isCopy; + const char* buf = env->GetStringUTFChars(coreFileName, &isCopy); + CHECK_EXCEPTION_(false); + AutoJavaString coreFile(env, coreFileName, buf); + if (setImageAndSymbolPath(env, obj) == false) { + return false; + } + + IDebugClient* ptrIDebugClient = (IDebugClient*) env->GetLongField(obj, + ptrIDebugClient_ID); + CHECK_EXCEPTION_(false); + if (ptrIDebugClient->OpenDumpFile(coreFile) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: OpenDumpFile failed!", false); + } + + IDebugControl* ptrIDebugControl = (IDebugControl*) env->GetLongField(obj, + ptrIDebugControl_ID); + CHECK_EXCEPTION_(false); + if (ptrIDebugControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: WaitForEvent failed!", false); + } + + return true; +} + + +static bool attachToProcess(JNIEnv* env, jobject obj, jint pid) { + if (setImageAndSymbolPath(env, obj) == false) { + return false; + } + IDebugClient* ptrIDebugClient = (IDebugClient*) env->GetLongField(obj, + ptrIDebugClient_ID); + CHECK_EXCEPTION_(false); + + /*********************************************************************************** + + We are attaching to a process in 'read-only' mode. i.e., we do not want to + put breakpoints, suspend/resume threads etc. For read-only JDI and HSDB kind of + usage this should suffice. We are not intending to use this for full-fledged + ProcessControl implementation to be used with BugSpotAgent. + + Please refer to DEBUG_ATTACH_NONINVASIVE mode source comments from dbgeng.h. + In this mode, debug engine does not call DebugActiveProrcess. i.e., we are not + actually debugging at all. We can safely 'detach' from the process anytime + we want and debuggee process is left as is on all Windows variants. + + This also makes JDI-on-SA installation/usage simpler because with this we would + not need a tool like ServiceInstaller from http://www.kcmultimedia.com/smaster. + + ***********************************************************************************/ + + + if (ptrIDebugClient->AttachProcess(0, pid, DEBUG_ATTACH_NONINVASIVE) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: AttachProcess failed!", false); + } + + IDebugControl* ptrIDebugControl = (IDebugControl*) env->GetLongField(obj, + ptrIDebugControl_ID); + CHECK_EXCEPTION_(false); + if (ptrIDebugControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: WaitForEvent failed!", false); + } + + return true; +} + + +static bool addLoadObjects(JNIEnv* env, jobject obj) { + IDebugSymbols* ptrIDebugSymbols = (IDebugSymbols*) env->GetLongField(obj, + ptrIDebugSymbols_ID); + CHECK_EXCEPTION_(false); + ULONG loaded = 0, unloaded = 0; + if (ptrIDebugSymbols->GetNumberModules(&loaded, &unloaded) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: GetNumberModules failed!", false); + } + + AutoArrayPtr params(new DEBUG_MODULE_PARAMETERS[loaded]); + + if (params.asPtr() == 0) { + THROW_NEW_DEBUGGER_EXCEPTION_("out of memory to allocate debug module params!", false); + } + + if (ptrIDebugSymbols->GetModuleParameters(loaded, 0, NULL, params.asPtr()) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: GetModuleParameters failed!", false); + } + + for (int u = 0; u < (int)loaded; u++) { + TCHAR imageName[MAX_PATH]; + if (ptrIDebugSymbols->GetModuleNames(DEBUG_ANY_ID, params.asPtr()[u].Base, + imageName, MAX_PATH, NULL, NULL, + 0, NULL, NULL, 0, NULL) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: GetModuleNames failed!", false); + } + + jstring strName = env->NewStringUTF(imageName); + CHECK_EXCEPTION_(false); + env->CallVoidMethod(obj, addLoadObject_ID, strName, (jlong) params.asPtr()[u].Size, + (jlong) params.asPtr()[u].Base); + CHECK_EXCEPTION_(false); + } + + return true; +} + +static bool addThreads(JNIEnv* env, jobject obj) { + IDebugSystemObjects* ptrIDebugSystemObjects = (IDebugSystemObjects*) env->GetLongField(obj, + ptrIDebugSystemObjects_ID); + CHECK_EXCEPTION_(false); + + ULONG numThreads = 0; + if (ptrIDebugSystemObjects->GetNumberThreads(&numThreads) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: GetNumberThreads failed!", false); + } + + AutoArrayPtr ptrSysThreadIds = new ULONG[numThreads]; + + if (ptrSysThreadIds.asPtr() == 0) { + THROW_NEW_DEBUGGER_EXCEPTION_("out of memory to allocate thread ids!", false); + } + + AutoArrayPtr ptrThreadIds = new ULONG[numThreads]; + + if (ptrThreadIds.asPtr() == 0) { + THROW_NEW_DEBUGGER_EXCEPTION_("out of memory to allocate thread ids!", false); + } + + if (ptrIDebugSystemObjects->GetThreadIdsByIndex(0, numThreads, + ptrThreadIds.asPtr(), ptrSysThreadIds.asPtr()) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: GetThreadIdsByIndex failed!", false); + } + + + IDebugAdvanced* ptrIDebugAdvanced = (IDebugAdvanced*) env->GetLongField(obj, + ptrIDebugAdvanced_ID); + CHECK_EXCEPTION_(false); + + // for each thread, get register context and save it. + for (ULONG t = 0; t < numThreads; t++) { + if (ptrIDebugSystemObjects->SetCurrentThreadId(ptrThreadIds.asPtr()[t]) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: SetCurrentThread failed!", false); + } + + jlongArray regs = env->NewLongArray(NPRGREG); + CHECK_EXCEPTION_(false); + + jboolean isCopy = JNI_FALSE; + jlong* ptrRegs = env->GetLongArrayElements(regs, &isCopy); + CHECK_EXCEPTION_(false); + + // copy register values from the CONTEXT struct + CONTEXT context; + memset(&context, 0, sizeof(CONTEXT)); + +#undef REG_INDEX +#ifdef _M_IA64 + #define REG_INDEX(x) sun_jvm_hotspot_debugger_ia64_IA64ThreadContext_##x + + context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG; + ptrIDebugAdvanced->GetThreadContext(&context, sizeof(CONTEXT)); + + ptrRegs[REG_INDEX(GR0)] = 0; // always 0 + ptrRegs[REG_INDEX(GR1)] = context.IntGp; // r1 + ptrRegs[REG_INDEX(GR2)] = context.IntT0; // r2-r3 + ptrRegs[REG_INDEX(GR3)] = context.IntT1; + ptrRegs[REG_INDEX(GR4)] = context.IntS0; // r4-r7 + ptrRegs[REG_INDEX(GR5)] = context.IntS1; + ptrRegs[REG_INDEX(GR6)] = context.IntS2; + ptrRegs[REG_INDEX(GR7)] = context.IntS3; + ptrRegs[REG_INDEX(GR8)] = context.IntV0; // r8 + ptrRegs[REG_INDEX(GR9)] = context.IntT2; // r9-r11 + ptrRegs[REG_INDEX(GR10)] = context.IntT3; + ptrRegs[REG_INDEX(GR11)] = context.IntT4; + ptrRegs[REG_INDEX(GR12)] = context.IntSp; // r12 stack pointer + ptrRegs[REG_INDEX(GR13)] = context.IntTeb; // r13 teb + ptrRegs[REG_INDEX(GR14)] = context.IntT5; // r14-r31 + ptrRegs[REG_INDEX(GR15)] = context.IntT6; + ptrRegs[REG_INDEX(GR16)] = context.IntT7; + ptrRegs[REG_INDEX(GR17)] = context.IntT8; + ptrRegs[REG_INDEX(GR18)] = context.IntT9; + ptrRegs[REG_INDEX(GR19)] = context.IntT10; + ptrRegs[REG_INDEX(GR20)] = context.IntT11; + ptrRegs[REG_INDEX(GR21)] = context.IntT12; + ptrRegs[REG_INDEX(GR22)] = context.IntT13; + ptrRegs[REG_INDEX(GR23)] = context.IntT14; + ptrRegs[REG_INDEX(GR24)] = context.IntT15; + ptrRegs[REG_INDEX(GR25)] = context.IntT16; + ptrRegs[REG_INDEX(GR26)] = context.IntT17; + ptrRegs[REG_INDEX(GR27)] = context.IntT18; + ptrRegs[REG_INDEX(GR28)] = context.IntT19; + ptrRegs[REG_INDEX(GR29)] = context.IntT20; + ptrRegs[REG_INDEX(GR30)] = context.IntT21; + ptrRegs[REG_INDEX(GR31)] = context.IntT22; + + ptrRegs[REG_INDEX(INT_NATS)] = context.IntNats; + ptrRegs[REG_INDEX(PREDS)] = context.Preds; + + ptrRegs[REG_INDEX(BR_RP)] = context.BrRp; + ptrRegs[REG_INDEX(BR1)] = context.BrS0; // b1-b5 + ptrRegs[REG_INDEX(BR2)] = context.BrS1; + ptrRegs[REG_INDEX(BR3)] = context.BrS2; + ptrRegs[REG_INDEX(BR4)] = context.BrS3; + ptrRegs[REG_INDEX(BR5)] = context.BrS4; + ptrRegs[REG_INDEX(BR6)] = context.BrT0; // b6-b7 + ptrRegs[REG_INDEX(BR7)] = context.BrT1; + + ptrRegs[REG_INDEX(AP_UNAT)] = context.ApUNAT; + ptrRegs[REG_INDEX(AP_LC)] = context.ApLC; + ptrRegs[REG_INDEX(AP_EC)] = context.ApEC; + ptrRegs[REG_INDEX(AP_CCV)] = context.ApCCV; + ptrRegs[REG_INDEX(AP_DCR)] = context.ApDCR; + + ptrRegs[REG_INDEX(RS_PFS)] = context.RsPFS; + ptrRegs[REG_INDEX(RS_BSP)] = context.RsBSP; + ptrRegs[REG_INDEX(RS_BSPSTORE)] = context.RsBSPSTORE; + ptrRegs[REG_INDEX(RS_RSC)] = context.RsRSC; + ptrRegs[REG_INDEX(RS_RNAT)] = context.RsRNAT; + + ptrRegs[REG_INDEX(ST_IPSR)] = context.StIPSR; + ptrRegs[REG_INDEX(ST_IIP)] = context.StIIP; + ptrRegs[REG_INDEX(ST_IFS)] = context.StIFS; + + ptrRegs[REG_INDEX(DB_I0)] = context.DbI0; + ptrRegs[REG_INDEX(DB_I1)] = context.DbI1; + ptrRegs[REG_INDEX(DB_I2)] = context.DbI2; + ptrRegs[REG_INDEX(DB_I3)] = context.DbI3; + ptrRegs[REG_INDEX(DB_I4)] = context.DbI4; + ptrRegs[REG_INDEX(DB_I5)] = context.DbI5; + ptrRegs[REG_INDEX(DB_I6)] = context.DbI6; + ptrRegs[REG_INDEX(DB_I7)] = context.DbI7; + + ptrRegs[REG_INDEX(DB_D0)] = context.DbD0; + ptrRegs[REG_INDEX(DB_D1)] = context.DbD1; + ptrRegs[REG_INDEX(DB_D2)] = context.DbD2; + ptrRegs[REG_INDEX(DB_D3)] = context.DbD3; + ptrRegs[REG_INDEX(DB_D4)] = context.DbD4; + ptrRegs[REG_INDEX(DB_D5)] = context.DbD5; + ptrRegs[REG_INDEX(DB_D6)] = context.DbD6; + ptrRegs[REG_INDEX(DB_D7)] = context.DbD7; + +#elif _M_IX86 + #define REG_INDEX(x) sun_jvm_hotspot_debugger_x86_X86ThreadContext_##x + + context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; + ptrIDebugAdvanced->GetThreadContext(&context, sizeof(CONTEXT)); + + ptrRegs[REG_INDEX(GS)] = context.SegGs; + ptrRegs[REG_INDEX(FS)] = context.SegFs; + ptrRegs[REG_INDEX(ES)] = context.SegEs; + ptrRegs[REG_INDEX(DS)] = context.SegDs; + + ptrRegs[REG_INDEX(EDI)] = context.Edi; + ptrRegs[REG_INDEX(ESI)] = context.Esi; + ptrRegs[REG_INDEX(EBX)] = context.Ebx; + ptrRegs[REG_INDEX(EDX)] = context.Edx; + ptrRegs[REG_INDEX(ECX)] = context.Ecx; + ptrRegs[REG_INDEX(EAX)] = context.Eax; + + ptrRegs[REG_INDEX(FP)] = context.Ebp; + ptrRegs[REG_INDEX(PC)] = context.Eip; + ptrRegs[REG_INDEX(CS)] = context.SegCs; + ptrRegs[REG_INDEX(EFL)] = context.EFlags; + ptrRegs[REG_INDEX(SP)] = context.Esp; + ptrRegs[REG_INDEX(SS)] = context.SegSs; + + ptrRegs[REG_INDEX(DR0)] = context.Dr0; + ptrRegs[REG_INDEX(DR1)] = context.Dr1; + ptrRegs[REG_INDEX(DR2)] = context.Dr2; + ptrRegs[REG_INDEX(DR3)] = context.Dr3; + ptrRegs[REG_INDEX(DR6)] = context.Dr6; + ptrRegs[REG_INDEX(DR7)] = context.Dr7; + +#elif _M_AMD64 + #define REG_INDEX(x) sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_##x + + context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; + ptrIDebugAdvanced->GetThreadContext(&context, sizeof(CONTEXT)); + + // Segment Registers and processor flags + ptrRegs[REG_INDEX(CS)] = context.SegCs; + ptrRegs[REG_INDEX(DS)] = context.SegDs; + ptrRegs[REG_INDEX(ES)] = context.SegEs; + ptrRegs[REG_INDEX(FS)] = context.SegFs; + ptrRegs[REG_INDEX(GS)] = context.SegGs; + ptrRegs[REG_INDEX(SS)] = context.SegSs; + ptrRegs[REG_INDEX(RFL)] = context.EFlags; + + // Integer registers + ptrRegs[REG_INDEX(RDI)] = context.Rdi; + ptrRegs[REG_INDEX(RSI)] = context.Rsi; + ptrRegs[REG_INDEX(RAX)] = context.Rax; + ptrRegs[REG_INDEX(RCX)] = context.Rcx; + ptrRegs[REG_INDEX(RDX)] = context.Rdx; + ptrRegs[REG_INDEX(RBX)] = context.Rbx; + ptrRegs[REG_INDEX(RBP)] = context.Rbp; + ptrRegs[REG_INDEX(RSP)] = context.Rsp; + + ptrRegs[REG_INDEX(R8)] = context.R8; + ptrRegs[REG_INDEX(R9)] = context.R9; + ptrRegs[REG_INDEX(R10)] = context.R10; + ptrRegs[REG_INDEX(R11)] = context.R11; + ptrRegs[REG_INDEX(R12)] = context.R12; + ptrRegs[REG_INDEX(R13)] = context.R13; + ptrRegs[REG_INDEX(R14)] = context.R14; + ptrRegs[REG_INDEX(R15)] = context.R15; + + // Program counter + ptrRegs[REG_INDEX(RIP)] = context.Rip; +#endif + + env->ReleaseLongArrayElements(regs, ptrRegs, JNI_COMMIT); + CHECK_EXCEPTION_(false); + + env->CallVoidMethod(obj, setThreadIntegerRegisterSet_ID, + (jlong) ptrThreadIds.asPtr()[t], regs); + CHECK_EXCEPTION_(false); + + ULONG sysId; + if (ptrIDebugSystemObjects->GetCurrentThreadSystemId(&sysId) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: GetCurrentThreadSystemId failed!", false); + } + + env->CallVoidMethod(obj, addThread_ID, (jlong) sysId); + CHECK_EXCEPTION_(false); + } + + return true; +} + +/* + * Class: sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal + * Method: attach0 + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2 + (JNIEnv *env, jobject obj, jstring execName, jstring coreFileName) { + + if (getWindbgInterfaces(env, obj) == false) { + return; + } + + if (openDumpFile(env, obj, coreFileName) == false) { + return; + } + + if (addLoadObjects(env, obj) == false) { + return; + } + + if (addThreads(env, obj) == false) { + return; + } +} + +/* + * Class: sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal + * Method: attach0 + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal_attach0__I + (JNIEnv *env, jobject obj, jint pid) { + + if (getWindbgInterfaces(env, obj) == false) { + return; + } + + if (attachToProcess(env, obj, pid) == false) { + return; + } + + if (addLoadObjects(env, obj) == false) { + return; + } + + if (addThreads(env, obj) == false) { + return; + } +} + + +static bool releaseWindbgInterfaces(JNIEnv* env, jobject obj) { + IDebugDataSpaces* ptrIDebugDataSpaces = (IDebugDataSpaces*) env->GetLongField(obj, + ptrIDebugDataSpaces_ID); + CHECK_EXCEPTION_(false); + if (ptrIDebugDataSpaces != 0) { + ptrIDebugDataSpaces->Release(); + } + + IDebugOutputCallbacks* ptrIDebugOutputCallbacks = (IDebugOutputCallbacks*) + env->GetLongField(obj, ptrIDebugOutputCallbacks_ID); + CHECK_EXCEPTION_(false); + if (ptrIDebugOutputCallbacks != 0) { + ptrIDebugOutputCallbacks->Release(); + } + + IDebugAdvanced* ptrIDebugAdvanced = (IDebugAdvanced*) env->GetLongField(obj, + ptrIDebugAdvanced_ID); + CHECK_EXCEPTION_(false); + + if (ptrIDebugAdvanced != 0) { + ptrIDebugAdvanced->Release(); + } + + IDebugSymbols* ptrIDebugSymbols = (IDebugSymbols*) env->GetLongField(obj, + ptrIDebugSymbols_ID); + CHECK_EXCEPTION_(false); + if (ptrIDebugSymbols != 0) { + ptrIDebugSymbols->Release(); + } + + IDebugSystemObjects* ptrIDebugSystemObjects = (IDebugSystemObjects*) env->GetLongField(obj, + ptrIDebugSystemObjects_ID); + CHECK_EXCEPTION_(false); + if (ptrIDebugSystemObjects != 0) { + ptrIDebugSystemObjects->Release(); + } + + IDebugControl* ptrIDebugControl = (IDebugControl*) env->GetLongField(obj, + ptrIDebugControl_ID); + CHECK_EXCEPTION_(false); + if (ptrIDebugControl != 0) { + ptrIDebugControl->Release(); + } + + IDebugClient* ptrIDebugClient = (IDebugClient*) env->GetLongField(obj, + ptrIDebugClient_ID); + CHECK_EXCEPTION_(false); + if (ptrIDebugClient != 0) { + ptrIDebugClient->Release(); + } + + return true; +} + +/* + * Class: sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal + * Method: detach0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal_detach0 + (JNIEnv *env, jobject obj) { + IDebugClient* ptrIDebugClient = (IDebugClient*) env->GetLongField(obj, + ptrIDebugClient_ID); + CHECK_EXCEPTION; + ptrIDebugClient->DetachProcesses(); + releaseWindbgInterfaces(env, obj); +} + + +/* + * Class: sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal + * Method: readBytesFromProcess0 + * Signature: (JJ)[B + */ +JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal_readBytesFromProcess0 + (JNIEnv *env, jobject obj, jlong address, jlong numBytes) { + jbyteArray byteArray = env->NewByteArray((long) numBytes); + CHECK_EXCEPTION_(0); + + jboolean isCopy = JNI_FALSE; + jbyte* bytePtr = env->GetByteArrayElements(byteArray, &isCopy); + CHECK_EXCEPTION_(0); + + IDebugDataSpaces* ptrIDebugDataSpaces = (IDebugDataSpaces*) env->GetLongField(obj, + ptrIDebugDataSpaces_ID); + CHECK_EXCEPTION_(0); + + ULONG bytesRead; + if (ptrIDebugDataSpaces->ReadVirtual((ULONG64) address, (PVOID) bytePtr, + (ULONG)numBytes, &bytesRead) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: ReadVirtual failed!", 0); + } + + if (bytesRead != numBytes) { + return 0; + } + + env->ReleaseByteArrayElements(byteArray, bytePtr, 0); + CHECK_EXCEPTION_(0); + + return byteArray; +} + +/* + * Class: sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal + * Method: getThreadIdFromSysId0 + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal_getThreadIdFromSysId0 + (JNIEnv *env, jobject obj, jlong sysId) { + IDebugSystemObjects* ptrIDebugSystemObjects = (IDebugSystemObjects*) env->GetLongField(obj, + ptrIDebugSystemObjects_ID); + CHECK_EXCEPTION_(0); + + ULONG id = 0; + if (ptrIDebugSystemObjects->GetThreadIdBySystemId((ULONG)sysId, &id) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: GetThreadIdBySystemId failed!", 0); + } + + return (jlong) id; +} + +// manage COM 'auto' pointers (to avoid multiple Release +// calls at every early (exception) returns). Similar to AutoArrayPtr. + +template +class AutoCOMPtr { + T* m_ptr; + + public: + AutoCOMPtr(T* ptr) : m_ptr(ptr) { + } + + ~AutoCOMPtr() { + if (m_ptr) { + m_ptr->Release(); + } + } + + T* operator->() { + return m_ptr; + } +}; + +/* + * Class: sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal + * Method: consoleExecuteCommand0 + * Signature: (Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal_consoleExecuteCommand0 + (JNIEnv *env, jobject obj, jstring cmd) { + jboolean isCopy = JNI_FALSE; + const char* buf = env->GetStringUTFChars(cmd, &isCopy); + CHECK_EXCEPTION_(0); + AutoJavaString command(env, cmd, buf); + + IDebugClient* ptrIDebugClient = (IDebugClient*) env->GetLongField(obj, ptrIDebugClient_ID); + CHECK_EXCEPTION_(0); + + IDebugClient* tmpClientPtr = 0; + if (ptrIDebugClient->CreateClient(&tmpClientPtr) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: CreateClient failed!", 0); + } + AutoCOMPtr tmpClient(tmpClientPtr); + + IDebugControl* tmpControlPtr = 0; + if (tmpClient->QueryInterface(__uuidof(IDebugControl), (PVOID*) &tmpControlPtr) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: QueryInterface (IDebugControl) failed", 0); + } + AutoCOMPtr tmpControl(tmpControlPtr); + + SAOutputCallbacks* saOutputCallbacks = (SAOutputCallbacks*) env->GetLongField(obj, + ptrIDebugOutputCallbacks_ID); + CHECK_EXCEPTION_(0); + + saOutputCallbacks->clearBuffer(); + + if (tmpClient->SetOutputCallbacks(saOutputCallbacks) != S_OK) { + THROW_NEW_DEBUGGER_EXCEPTION_("Windbg Error: SetOutputCallbacks failed!", 0); + } + + tmpControl->Execute(DEBUG_OUTPUT_VERBOSE, command, DEBUG_EXECUTE_DEFAULT); + + const char* output = saOutputCallbacks->getBuffer(); + if (output == 0) { + output = ""; + } + + jstring res = env->NewStringUTF(output); + saOutputCallbacks->clearBuffer(); + return res; +} + +/* + * Class: sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal + * Method: lookupByName0 + * Signature: (Ljava/lang/String;Ljava/lang/String;)J + */ + +JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal_lookupByName0 +(JNIEnv *env, jobject obj, jstring objName, jstring sym) { + IDebugSymbols* ptrIDebugSymbols = (IDebugSymbols*) env->GetLongField(obj, + ptrIDebugSymbols_ID); + CHECK_EXCEPTION_(0); + + jboolean isCopy; + const char* buf = env->GetStringUTFChars(sym, &isCopy); + CHECK_EXCEPTION_(0); + AutoJavaString name(env, sym, buf); + + ULONG64 offset = 0L; + if (strstr(name, "::") != 0) { + ptrIDebugSymbols->AddSymbolOptions(SYMOPT_UNDNAME); + } else { + ptrIDebugSymbols->RemoveSymbolOptions(SYMOPT_UNDNAME); + } + if (ptrIDebugSymbols->GetOffsetByName(name, &offset) != S_OK) { + return (jlong) 0; + } + return (jlong) offset; +} + +#define SYMBOL_BUFSIZE 512 +/* + * Class: sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal + * Method: lookupByAddress0 + * Signature: (J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol; + */ +JNIEXPORT jobject JNICALL Java_sun_jvm_hotspot_debugger_windbg_WindbgDebuggerLocal_lookupByAddress0 +(JNIEnv *env, jobject obj, jlong address) { + IDebugSymbols* ptrIDebugSymbols = (IDebugSymbols*) env->GetLongField(obj, + ptrIDebugSymbols_ID); + CHECK_EXCEPTION_(0); + + ULONG64 disp = 0L; + char buf[SYMBOL_BUFSIZE]; + memset(buf, 0, sizeof(buf)); + + if (ptrIDebugSymbols->GetNameByOffset(address, buf, sizeof(buf),0,&disp) + != S_OK) { + return 0; + } + + jstring sym = env->NewStringUTF(buf); + CHECK_EXCEPTION_(0); + jobject res = env->CallObjectMethod(obj, createClosestSymbol_ID, sym, disp); + CHECK_EXCEPTION_(0); + return res; +} diff --git a/hotspot/agent/src/scripts/README b/hotspot/agent/src/scripts/README new file mode 100644 index 00000000000..8092c612afd --- /dev/null +++ b/hotspot/agent/src/scripts/README @@ -0,0 +1,53 @@ +# +# Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +These scripts may be used to start SA debug server for SA/JDI +purpose. The SADebugServerAttachingConnector will connect to +SADebugServer. + +How to use? + +Before starting remote debug server, make sure that the environment +variable JAVA_HOME points to the pathname of a J2SE 1.5. + +step 1: Start the rmiregistry server using one of the following + commands as appropriate: + + start-rmiregistry.sh & + start-rmiregistry64.sh & + start-rmiregistry.bat + +step 2: For live process case, use one of the following commands + as appropriate: + + start-debug-server.sh + start-debug-server64.sh + start-debug-server.bat + + For core file case, use one of the following commands as + appropriate: + + start-debug-server.sh + start-debug-server64.sh + start-debug-server.bat diff --git a/hotspot/agent/src/scripts/start-debug-server.bat b/hotspot/agent/src/scripts/start-debug-server.bat new file mode 100644 index 00000000000..bce97b3367f --- /dev/null +++ b/hotspot/agent/src/scripts/start-debug-server.bat @@ -0,0 +1,47 @@ +@echo off + +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +if "%1" == "-help" goto usage + +:JAVA_HOME +if not exist %JAVA_HOME%\bin\java.exe goto BADJAVAHOME +if not exist %JAVA_HOME\lib\sa-jdi.jar goto BADJAVAHOME + +start %JAVA_HOME%\bin\java -classpath %JAVA_HOME%\lib\sa-jdi.jar sun.jvm.hotspot.jdi.SADebugServer %1 %2 +goto end + +:BADJAVAHOME +echo JAVA_HOME does not point to a working J2SE 1.5 installation. + +:usage +echo Usage: start-debug-server [pid] +echo $0 [Dr Watson dump file] +echo Start the JDI debug server on [pid] or [Dr Watson dump file] +echo so that it can be debugged from a remote machine. +echo JAVA_HOME must contain the pathname of a J2SE 1.5 +echo installation. + +:end diff --git a/hotspot/agent/src/scripts/start-debug-server.sh b/hotspot/agent/src/scripts/start-debug-server.sh new file mode 100644 index 00000000000..95b0209967f --- /dev/null +++ b/hotspot/agent/src/scripts/start-debug-server.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +if [ "$1" = "-help" ] ; then + echo "Usage: $0 " + echo " $0 " + echo " Start the JDI debug server on or " + echo " so that it can be debugged from a remote machine." + echo " JAVA_HOME must contain the pathname of a J2SE 1.5" + echo " installation." + exit 0 +fi + +if [ ! -x ${JAVA_HOME}/bin/java -o ! -r ${JAVA_HOME}/lib/sa-jdi.jar ] ; +then + echo '${JAVA_HOME} does not point to a working J2SE 1.5 installation.' + exit 1 +fi + +${JAVA_HOME}/bin/java -classpath ${JAVA_HOME}/lib/sa-jdi.jar sun.jvm.hotspot.jdi.SADebugServer $* diff --git a/hotspot/agent/src/scripts/start-debug-server64.sh b/hotspot/agent/src/scripts/start-debug-server64.sh new file mode 100644 index 00000000000..7c93ef120e5 --- /dev/null +++ b/hotspot/agent/src/scripts/start-debug-server64.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +if [ "$1" = "-help" ] ; then + echo "Usage: $0 " + echo " $0 " + echo " Start the JDI debug server on or " + echo " so that it can be debugged from a remote machine." + echo " JAVA_HOME must contain the pathname of a J2SE 1.5" + echo " installation." + exit 0 +fi + +if [ ! -x ${JAVA_HOME}/bin/java -o ! -r ${JAVA_HOME}/lib/sa-jdi.jar ] ; +then + echo '${JAVA_HOME} does not point to a working J2SE 1.5 installation.' + exit 1 +fi + +${JAVA_HOME}/bin/java -d64 -classpath ${JAVA_HOME}/lib/sa-jdi.jar sun.jvm.hotspot.jdi.SADebugServer $* diff --git a/hotspot/agent/src/scripts/start-rmiregistry.bat b/hotspot/agent/src/scripts/start-rmiregistry.bat new file mode 100644 index 00000000000..b78e102a4c2 --- /dev/null +++ b/hotspot/agent/src/scripts/start-rmiregistry.bat @@ -0,0 +1,48 @@ +@echo off + +REM +REM Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +@echo off + +if "%1" == "-help" goto usage + +:JAVA_HOME +if not exist %JAVA_HOME%\bin\rmiregistry goto BADJAVAHOME +if not exist %JAVA_HOME%\lib\sa-jdi.jar goto BADJAVAHOME + +start %JAVA_HOME%\bin\rmiregistry -J-Xbootclasspath/p:%JAVA_HOME%\lib\sa-jdi.jar +goto end + +:BADJAVAHOME +echo JAVA_HOME does not point to a working J2SE 1.5 installation. + +:usage +@echo usage: start-rmiregistry +@echo Start the rmi registry with with sa-jdi.jar on the bootclasspath +@echo for use by the debug server. +@echo JAVA_HOME must contain the pathname of a J2SE 1.5 installation. + +:end + diff --git a/hotspot/agent/src/scripts/start-rmiregistry.sh b/hotspot/agent/src/scripts/start-rmiregistry.sh new file mode 100644 index 00000000000..b131b274092 --- /dev/null +++ b/hotspot/agent/src/scripts/start-rmiregistry.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +if [ "$1" = "-help" ] ; then + echo "usage: $0&" + echo " Start the rmi registry with with sa-jdi.jar on the bootclasspath" + echo " for use by the debug server." + echo " JAVA_HOME must contain the pathname of a J2SE 1.5" + echo " installation." + exit 0 +fi + +if [ ! -x ${JAVA_HOME}/bin/rmiregistry -o ! -r ${JAVA_HOME}/lib/sa-jdi.jar ] ; +then + echo '${JAVA_HOME} does not point to a working J2SE installation.' + exit 1 +fi + +${JAVA_HOME}/bin/rmiregistry -J-Xbootclasspath/p:${JAVA_HOME}/lib/sa-jdi.jar diff --git a/hotspot/agent/src/scripts/start-rmiregistry64.sh b/hotspot/agent/src/scripts/start-rmiregistry64.sh new file mode 100644 index 00000000000..2561b253ae1 --- /dev/null +++ b/hotspot/agent/src/scripts/start-rmiregistry64.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +if [ "$1" = "-help" ] ; then + echo "usage: $0&" + echo " Start the rmi registry with with sa-jdi.jar on the bootclasspath" + echo " for use by the debug server." + echo " JAVA_HOME must contain the pathname of a J2SE 1.5" + echo " installation." + exit 0 +fi + +if [ ! -x ${JAVA_HOME}/bin/rmiregistry -o ! -r ${JAVA_HOME}/lib/sa-jdi.jar ] ; +then + echo '${JAVA_HOME} does not point to a working J2SE installation.' + exit 1 +fi + +${JAVA_HOME}/bin/rmiregistry -J-d64 -J-Xbootclasspath/p:${JAVA_HOME}/lib/sa-jdi.jar diff --git a/hotspot/agent/src/share/classes/META-INF/services/com.sun.jdi.connect.Connector b/hotspot/agent/src/share/classes/META-INF/services/com.sun.jdi.connect.Connector new file mode 100644 index 00000000000..cbe6b386f3a --- /dev/null +++ b/hotspot/agent/src/share/classes/META-INF/services/com.sun.jdi.connect.Connector @@ -0,0 +1,5 @@ +# SA JDI Connectors + +sun.jvm.hotspot.jdi.SACoreAttachingConnector +sun.jvm.hotspot.jdi.SADebugServerAttachingConnector +sun.jvm.hotspot.jdi.SAPIDAttachingConnector diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/CLHSDB.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/CLHSDB.java new file mode 100644 index 00000000000..acd9ada348f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/CLHSDB.java @@ -0,0 +1,251 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import sun.jvm.hotspot.*; +import sun.jvm.hotspot.debugger.*; + +import java.io.*; +import java.util.*; + +public class CLHSDB { + public static void main(String[] args) { + new CLHSDB(args).run(); + } + + private void run() { + // At this point, if pidText != null we are supposed to attach to it. + // Else, if execPath != null, it is the path of a jdk/bin/java + // and coreFilename is the pathname of a core file we are + // supposed to attach to. + + agent = new HotSpotAgent(); + + Runtime.getRuntime().addShutdownHook(new java.lang.Thread() { + public void run() { + detachDebugger(); + } + }); + + if (pidText != null) { + attachDebugger(pidText); + } else if (execPath != null) { + attachDebugger(execPath, coreFilename); + } + + + CommandProcessor.DebuggerInterface di = new CommandProcessor.DebuggerInterface() { + public HotSpotAgent getAgent() { + return agent; + } + public boolean isAttached() { + return attached; + } + public void attach(String pid) { + attachDebugger(pid); + } + public void attach(String java, String core) { + attachDebugger(java, core); + } + public void detach() { + detachDebugger(); + } + public void reattach() { + if (attached) { + detachDebugger(); + } + if (pidText != null) { + attach(pidText); + } else { + attach(execPath, coreFilename); + } + } + }; + + + BufferedReader in = + new BufferedReader(new InputStreamReader(System.in)); + CommandProcessor cp = new CommandProcessor(di, in, System.out, System.err); + cp.run(true); + + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + private HotSpotAgent agent; + private boolean attached; + // These had to be made data members because they are referenced in inner classes. + private String pidText; + private int pid; + private String execPath; + private String coreFilename; + + private void doUsage() { + System.out.println("Usage: java CLHSDB [[pid] | [path-to-java-executable [path-to-corefile]] | help ]"); + System.out.println(" pid: attach to the process whose id is 'pid'"); + System.out.println(" path-to-java-executable: Debug a core file produced by this program"); + System.out.println(" path-to-corefile: Debug this corefile. The default is 'core'"); + System.out.println(" If no arguments are specified, you can select what to do from the GUI.\n"); + HotSpotAgent.showUsage(); + } + + private CLHSDB(String[] args) { + switch (args.length) { + case (0): + break; + + case (1): + if (args[0].equals("help") || args[0].equals("-help")) { + doUsage(); + System.exit(0); + } + // If all numbers, it is a PID to attach to + // Else, it is a pathname to a .../bin/java for a core file. + try { + int unused = Integer.parseInt(args[0]); + // If we get here, we have a PID and not a core file name + pidText = args[0]; + } catch (NumberFormatException e) { + execPath = args[0]; + coreFilename = "core"; + } + break; + + case (2): + execPath = args[0]; + coreFilename = args[1]; + break; + + default: + System.out.println("HSDB Error: Too many options specified"); + doUsage(); + System.exit(1); + } + } + + /** NOTE we are in a different thread here than either the main + thread or the Swing/AWT event handler thread, so we must be very + careful when creating or removing widgets */ + private void attachDebugger(String pidText) { + try { + this.pidText = pidText; + pid = Integer.parseInt(pidText); + } + catch (NumberFormatException e) { + System.err.print("Unable to parse process ID \"" + pidText + "\".\nPlease enter a number."); + } + + try { + System.err.println("Attaching to process " + pid + ", please wait..."); + + // FIXME: display exec'd debugger's output messages during this + // lengthy call + agent.attach(pid); + attached = true; + } + catch (DebuggerException e) { + final String errMsg = formatMessage(e.getMessage(), 80); + System.err.println("Unable to connect to process ID " + pid + ":\n\n" + errMsg); + agent.detach(); + return; + } + } + + /** NOTE we are in a different thread here than either the main + thread or the Swing/AWT event handler thread, so we must be very + careful when creating or removing widgets */ + private void attachDebugger(final String executablePath, final String corePath) { + // Try to open this core file + try { + System.err.println("Opening core file, please wait..."); + + // FIXME: display exec'd debugger's output messages during this + // lengthy call + agent.attach(executablePath, corePath); + attached = true; + } + catch (DebuggerException e) { + final String errMsg = formatMessage(e.getMessage(), 80); + System.err.println("Unable to open core file\n" + corePath + ":\n\n" + errMsg); + agent.detach(); + return; + } + } + + /** NOTE we are in a different thread here than either the main + thread or the Swing/AWT event handler thread, so we must be very + careful when creating or removing widgets */ + private void connect(final String remoteMachineName) { + // Try to open this core file + try { + System.err.println("Connecting to debug server, please wait..."); + agent.attach(remoteMachineName); + attached = true; + } + catch (DebuggerException e) { + final String errMsg = formatMessage(e.getMessage(), 80); + System.err.println("Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg); + agent.detach(); + return; + } + } + + private void detachDebugger() { + if (!attached) { + return; + } + agent.detach(); + attached = false; + } + + private void detach() { + detachDebugger(); + } + + /** Punctuates the given string with \n's where necessary to not + exceed the given number of characters per line. Strips + extraneous whitespace. */ + private String formatMessage(String message, int charsPerLine) { + StringBuffer buf = new StringBuffer(message.length()); + StringTokenizer tokenizer = new StringTokenizer(message); + int curLineLength = 0; + while (tokenizer.hasMoreTokens()) { + String tok = tokenizer.nextToken(); + if (curLineLength + tok.length() > charsPerLine) { + buf.append('\n'); + curLineLength = 0; + } else { + if (curLineLength != 0) { + buf.append(' '); + ++curLineLength; + } + } + buf.append(tok); + curLineLength += tok.length(); + } + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java new file mode 100644 index 00000000000..458e97195db --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java @@ -0,0 +1,1269 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import java.io.*; +import java.math.*; +import java.util.*; +import java.util.regex.*; + +import sun.jvm.hotspot.types.Type; +import sun.jvm.hotspot.types.Field; +import sun.jvm.hotspot.HotSpotTypeDataBase; +import sun.jvm.hotspot.types.basic.BasicType; +import sun.jvm.hotspot.types.CIntegerType; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.compiler.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.utilities.soql.*; +import sun.jvm.hotspot.ui.classbrowser.*; +import sun.jvm.hotspot.ui.tree.*; +import sun.jvm.hotspot.tools.*; +import sun.jvm.hotspot.tools.ObjectHistogram; +import sun.jvm.hotspot.tools.StackTrace; + +public class CommandProcessor { + public abstract static class DebuggerInterface { + public abstract HotSpotAgent getAgent(); + public abstract boolean isAttached(); + public abstract void attach(String pid); + public abstract void attach(String java, String core); + public abstract void detach(); + public abstract void reattach(); + } + + static class Tokens { + final String input; + int i; + String[] tokens; + int length; + + String[] splitWhitespace(String cmd) { + String[] t = cmd.split("\\s"); + if (t.length == 1 && t[0].length() == 0) { + return new String[0]; + } + return t; + } + + void add(String s, ArrayList t) { + if (s.length() > 0) { + t.add(s); + } + } + + Tokens(String cmd) { + input = cmd; + + // check for quoting + int quote = cmd.indexOf('"'); + ArrayList t = new ArrayList(); + if (quote != -1) { + while (cmd.length() > 0) { + if (quote != -1) { + int endquote = cmd.indexOf('"', quote + 1); + if (endquote == -1) { + throw new RuntimeException("mismatched quotes: " + input); + } + + String before = cmd.substring(0, quote).trim(); + String quoted = cmd.substring(quote + 1, endquote); + cmd = cmd.substring(endquote + 1).trim(); + if (before.length() > 0) { + String[] w = splitWhitespace(before); + for (int i = 0; i < w.length; i++) { + add(w[i], t); + } + } + add(quoted, t); + quote = cmd.indexOf('"'); + } else { + String[] w = splitWhitespace(cmd); + for (int i = 0; i < w.length; i++) { + add(w[i], t); + } + cmd = ""; + + } + } + } else { + String[] w = splitWhitespace(cmd); + for (int i = 0; i < w.length; i++) { + add(w[i], t); + } + } + tokens = (String[])t.toArray(new String[0]); + i = 0; + length = tokens.length; + + //for (int i = 0; i < tokens.length; i++) { + // System.out.println("\"" + tokens[i] + "\""); + //} + } + + String nextToken() { + return tokens[i++]; + } + boolean hasMoreTokens() { + return i < length; + } + int countTokens() { + return length - i; + } + void trim(int n) { + if (length >= n) { + length -= n; + } else { + throw new IndexOutOfBoundsException(String.valueOf(n)); + } + } + String join(String sep) { + StringBuffer result = new StringBuffer(); + for (int w = i; w < length; w++) { + result.append(tokens[w]); + if (w + 1 < length) { + result.append(sep); + } + } + return result.toString(); + } + + String at(int i) { + if (i < 0 || i >= length) { + throw new IndexOutOfBoundsException(String.valueOf(i)); + } + return tokens[i]; + } + } + + + abstract class Command { + Command(String n, String u, boolean ok) { + name = n; + usage = u; + okIfDisconnected = ok; + } + + Command(String n, boolean ok) { + name = n; + usage = n; + okIfDisconnected = ok; + } + + final String name; + final String usage; + final boolean okIfDisconnected; + abstract void doit(Tokens t); + void usage() { + out.println("Usage: " + usage); + } + + void printOopValue(Oop oop) { + if (oop != null) { + Klass k = oop.getKlass(); + Symbol s = k.getName(); + if (s != null) { + out.print("Oop for " + s.asString() + " @ "); + } else { + out.print("Oop @ "); + } + Oop.printOopAddressOn(oop, out); + } else { + out.print("null"); + } + } + + void printNode(SimpleTreeNode node) { + int count = node.getChildCount(); + for (int i = 0; i < count; i++) { + try { + SimpleTreeNode field = node.getChild(i); + if (field instanceof OopTreeNodeAdapter) { + out.print(field); + out.print(" "); + printOopValue(((OopTreeNodeAdapter)field).getOop()); + out.println(); + } else { + out.println(field); + } + } catch (Exception e) { + out.println(); + out.println("Error: " + e); + if (verboseExceptions) { + e.printStackTrace(out); + } + } + } + } + } + + void quote(String s) { + if (s.indexOf(" ") == -1) { + out.print(s); + } else { + out.print("\""); + out.print(s); + out.print("\""); + } + } + + void dumpType(Type type) { + out.print("type "); + quote(type.getName()); + out.print(" "); + if (type.getSuperclass() != null) { + quote(type.getSuperclass().getName()); + out.print(" "); + } else { + out.print("null "); + } + out.print(type.isOopType()); + out.print(" "); + if (type.isCIntegerType()) { + out.print("true "); + out.print(((CIntegerType)type).isUnsigned()); + out.print(" "); + } else { + out.print("false false "); + } + out.print(type.getSize()); + out.println(); + } + + void dumpFields(Type type) { + Iterator i = type.getFields(); + while (i.hasNext()) { + Field f = (Field) i.next(); + out.print("field "); + quote(type.getName()); + out.print(" "); + out.print(f.getName()); + out.print(" "); + quote(f.getType().getName()); + out.print(" "); + out.print(f.isStatic()); + out.print(" "); + if (f.isStatic()) { + out.print("0 "); + out.print(f.getStaticFieldAddress()); + } else { + out.print(f.getOffset()); + out.print(" 0x0"); + } + out.println(); + } + } + + + Address lookup(String symbol) { + if (symbol.indexOf("::") != -1) { + String[] parts = symbol.split("::"); + StringBuffer mangled = new StringBuffer("__1c"); + for (int i = 0; i < parts.length; i++) { + int len = parts[i].length(); + if (len >= 26) { + mangled.append((char)('a' + (len / 26))); + len = len % 26; + } + mangled.append((char)('A' + len)); + mangled.append(parts[i]); + } + mangled.append("_"); + symbol = mangled.toString(); + } + return VM.getVM().getDebugger().lookup(null, symbol); + } + + Address parseAddress(String addr) { + return VM.getVM().getDebugger().parseAddress(addr); + } + + private final Command[] commandList = { + new Command("reattach", true) { + public void doit(Tokens t) { + int tokens = t.countTokens(); + if (tokens != 0) { + usage(); + return; + } + preAttach(); + debugger.reattach(); + postAttach(); + } + }, + new Command("attach", "attach pid | exec core", true) { + public void doit(Tokens t) { + int tokens = t.countTokens(); + if (tokens == 1) { + preAttach(); + debugger.attach(t.nextToken()); + postAttach(); + } else if (tokens == 2) { + preAttach(); + debugger.attach(t.nextToken(), t.nextToken()); + postAttach(); + } else { + usage(); + } + } + }, + new Command("detach", false) { + public void doit(Tokens t) { + if (t.countTokens() != 0) { + usage(); + } else { + debugger.detach(); + } + } + }, + new Command("examine", "examine [ address/count ] | [ address,address]", false) { + Pattern args1 = Pattern.compile("^(0x[0-9a-f]+)(/([0-9]*)([a-z]*))?$"); + Pattern args2 = Pattern.compile("^(0x[0-9a-f]+),(0x[0-9a-f]+)(/[a-z]*)?$"); + + String fill(Address a, int width) { + String s = "0x0"; + if (a != null) { + s = a.toString(); + } + if (s.length() != width) { + return s.substring(0, 2) + "000000000000000000000".substring(0, width - s.length()) + s.substring(2); + } + return s; + } + + public void doit(Tokens t) { + if (t.countTokens() != 1) { + usage(); + } else { + String arg = t.nextToken(); + Matcher m1 = args1.matcher(arg); + Matcher m2 = args2.matcher(arg); + Address start = null; + Address end = null; + String format = ""; + int formatSize = (int)VM.getVM().getAddressSize(); + + if (m1.matches()) { + start = VM.getVM().getDebugger().parseAddress(m1.group(1)); + int count = 1; + if (m1.group(2) != null) { + count = Integer.parseInt(m1.group(3)); + } + end = start.addOffsetTo(count * formatSize); + } else if (m2.matches()) { + start = VM.getVM().getDebugger().parseAddress(m2.group(1)); + end = VM.getVM().getDebugger().parseAddress(m2.group(2)); + } else { + usage(); + return; + } + int line = 80; + int formatWidth = formatSize * 8 / 4 + 2; + + out.print(fill(start, formatWidth)); + out.print(": "); + int width = line - formatWidth - 2; + + boolean needsPrintln = true; + while (start != null && start.lessThan(end)) { + Address val = start.getAddressAt(0); + out.print(fill(val, formatWidth)); + needsPrintln = true; + width -= formatWidth; + start = start.addOffsetTo(formatSize); + if (width <= formatWidth) { + out.println(); + needsPrintln = false; + if (start.lessThan(end)) { + out.print(fill(start, formatWidth)); + out.print(": "); + width = line - formatWidth - 2; + } + } else { + out.print(" "); + width -= 1; + } + } + if (needsPrintln) { + out.println(); + } + } + } + }, + new Command("findpc", "findpc address", false) { + public void doit(Tokens t) { + if (t.countTokens() != 1) { + usage(); + } else { + Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); + PointerLocation loc = PointerFinder.find(a); + loc.printOn(out); + } + } + }, + new Command("flags", "flags [ flag ]", false) { + public void doit(Tokens t) { + int tokens = t.countTokens(); + if (tokens != 0 && tokens != 1) { + usage(); + } else { + String name = tokens > 0 ? t.nextToken() : null; + + VM.Flag[] flags = VM.getVM().getCommandLineFlags(); + if (flags == null) { + out.println("Command Flag info not available (use 1.4.1_03 or later)!"); + } else { + boolean printed = false; + for (int f = 0; f < flags.length; f++) { + VM.Flag flag = flags[f]; + if (name == null || flag.getName().equals(name)) { + out.println(flag.getName() + " = " + flag.getValue()); + printed = true; + } + } + if (name != null && !printed) { + out.println("Couldn't find flag: " + name); + } + } + } + } + }, + new Command("help", "help [ command ]", true) { + public void doit(Tokens t) { + int tokens = t.countTokens(); + Command cmd = null; + if (tokens == 1) { + cmd = findCommand(t.nextToken()); + } + + if (cmd != null) { + cmd.usage(); + } else if (tokens == 0) { + out.println("Available commands:"); + Object[] keys = commands.keySet().toArray(); + Arrays.sort(keys, new Comparator() { + public int compare(Object o1, Object o2) { + return o1.toString().compareTo(o2.toString()); + } + }); + for (int i = 0; i < keys.length; i++) { + out.print(" "); + out.println(((Command)commands.get(keys[i])).usage); + } + } + } + }, + new Command("history", "history", true) { + public void doit(Tokens t) { + int tokens = t.countTokens(); + if (tokens != 0 && (tokens != 1 || !t.nextToken().equals("-h"))) { + usage(); + return; + } + boolean printIndex = tokens == 0; + for (int i = 0; i < history.size(); i++) { + if (printIndex) out.print(i + " "); + out.println(history.get(i)); + } + } + }, + new Command("inspect", "inspect expression", false) { + public void doit(Tokens t) { + if (t.countTokens() != 1) { + usage(); + } else { + Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); + SimpleTreeNode node = null; + if (VM.getVM().getUniverse().heap().isInReserved(a)) { + OopHandle handle = a.addOffsetToAsOopHandle(0); + Oop oop = VM.getVM().getObjectHeap().newOop(handle); + node = new OopTreeNodeAdapter(oop, null); + + out.println("instance of " + node.getValue() + " @ " + a + + " (size = " + oop.getObjectSize() + ")"); + } else if (VM.getVM().getCodeCache().contains(a)) { + CodeBlob blob = VM.getVM().getCodeCache().findBlobUnsafe(a); + a = blob.headerBegin(); + } + if (node == null) { + Type type = VM.getVM().getTypeDataBase().guessTypeForAddress(a); + if (type != null) { + out.println("Type is " + type.getName() + " (size of " + type.getSize() + ")"); + node = new CTypeTreeNodeAdapter(a, type, null); + } + } + if (node != null) { + printNode(node); + } + } + } + }, + new Command("jhisto", "jhisto", false) { + public void doit(Tokens t) { + ObjectHistogram histo = new ObjectHistogram(); + histo.run(out, err); + } + }, + new Command("jstack", "jstack [-v]", false) { + public void doit(Tokens t) { + boolean verbose = false; + if (t.countTokens() > 0 && t.nextToken().equals("-v")) { + verbose = true; + } + StackTrace jstack = new StackTrace(verbose, true); + jstack.run(out); + } + }, + new Command("print", "print expression", false) { + public void doit(Tokens t) { + if (t.countTokens() != 1) { + usage(); + } else { + Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); + HTMLGenerator gen = new HTMLGenerator(false); + out.println(gen.genHTML(a)); + } + } + }, + new Command("printas", "printas type expression", false) { + public void doit(Tokens t) { + if (t.countTokens() != 2) { + usage(); + } else { + Type type = agent.getTypeDataBase().lookupType(t.nextToken()); + Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); + CTypeTreeNodeAdapter node = new CTypeTreeNodeAdapter(a, type, null); + + out.println("pointer to " + type + " @ " + a + + " (size = " + type.getSize() + ")"); + printNode(node); + } + } + }, + new Command("symbol", "symbol name", false) { + public void doit(Tokens t) { + if (t.countTokens() != 1) { + usage(); + } else { + String symbol = t.nextToken(); + Address a = lookup(symbol); + out.println(symbol + " = " + a); + } + } + }, + new Command("printstatics", "printstatics [ type ]", false) { + public void doit(Tokens t) { + if (t.countTokens() > 1) { + usage(); + } else { + if (t.countTokens() == 0) { + out.println("All known static fields"); + printNode(new CTypeTreeNodeAdapter(agent.getTypeDataBase().getTypes())); + } else { + Type type = agent.getTypeDataBase().lookupType(t.nextToken()); + out.println("Static fields of " + type.getName()); + printNode(new CTypeTreeNodeAdapter(type)); + } + } + } + }, + new Command("pmap", "pmap", false) { + public void doit(Tokens t) { + PMap pmap = new PMap(); + pmap.run(out, debugger.getAgent().getDebugger()); + } + }, + new Command("pstack", "pstack [-v]", false) { + public void doit(Tokens t) { + boolean verbose = false; + if (t.countTokens() > 0 && t.nextToken().equals("-v")) { + verbose = true; + } + PStack pstack = new PStack(verbose, true); + pstack.run(out, debugger.getAgent().getDebugger()); + } + }, + new Command("quit", true) { + public void doit(Tokens t) { + if (t.countTokens() != 0) { + usage(); + } else { + debugger.detach(); + System.exit(0); + } + } + }, + new Command("echo", "echo [ true | false ]", true) { + public void doit(Tokens t) { + if (t.countTokens() == 0) { + out.println("echo is " + doEcho); + } else if (t.countTokens() == 1) { + doEcho = Boolean.valueOf(t.nextToken()).booleanValue(); + } else { + usage(); + } + } + }, + new Command("versioncheck", "versioncheck [ true | false ]", true) { + public void doit(Tokens t) { + if (t.countTokens() == 0) { + out.println("versioncheck is " + + (System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null)); + } else if (t.countTokens() == 1) { + if (Boolean.valueOf(t.nextToken()).booleanValue()) { + System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", null); + } else { + System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", "true"); + } + } else { + usage(); + } + } + }, + new Command("scanoops", "scanoops start end [ type ]", false) { + public void doit(Tokens t) { + if (t.countTokens() != 2 && t.countTokens() != 3) { + usage(); + } else { + long stride = VM.getVM().getAddressSize(); + Address base = VM.getVM().getDebugger().parseAddress(t.nextToken()); + Address end = VM.getVM().getDebugger().parseAddress(t.nextToken()); + Klass klass = null; + if (t.countTokens() == 1) { + klass = SystemDictionaryHelper.findInstanceKlass(t.nextToken()); + } + while (base != null && base.lessThan(end)) { + long step = stride; + OopHandle handle = base.addOffsetToAsOopHandle(0); + if (RobustOopDeterminator.oopLooksValid(handle)) { + try { + Oop oop = VM.getVM().getObjectHeap().newOop(handle); + if (klass == null || oop.getKlass().isSubtypeOf(klass)) + out.println(handle.toString() + " " + oop.getKlass().getName().asString()); + step = oop.getObjectSize(); + } catch (UnknownOopException ex) { + // ok + } catch (RuntimeException ex) { + ex.printStackTrace(); + } + } + base = base.addOffsetTo(step); + } + } + } + }, + new Command("field", "field [ type [ name fieldtype isStatic offset address ] ]", true) { + public void doit(Tokens t) { + if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) { + usage(); + return; + } + if (t.countTokens() == 1) { + Type type = agent.getTypeDataBase().lookupType(t.nextToken()); + dumpFields(type); + } else if (t.countTokens() == 0) { + Iterator i = agent.getTypeDataBase().getTypes(); + while (i.hasNext()) { + dumpFields((Type)i.next()); + } + } else { + BasicType containingType = (BasicType)agent.getTypeDataBase().lookupType(t.nextToken()); + + String fieldName = t.nextToken(); + + // The field's Type must already be in the database -- no exceptions + Type fieldType = agent.getTypeDataBase().lookupType(t.nextToken()); + + boolean isStatic = Boolean.valueOf(t.nextToken()).booleanValue(); + long offset = Long.parseLong(t.nextToken()); + Address staticAddress = parseAddress(t.nextToken()); + if (isStatic && staticAddress == null) { + staticAddress = lookup(containingType.getName() + "::" + fieldName); + } + + // check to see if the field already exists + Iterator i = containingType.getFields(); + while (i.hasNext()) { + Field f = (Field) i.next(); + if (f.getName().equals(fieldName)) { + if (f.isStatic() != isStatic) { + throw new RuntimeException("static/nonstatic mismatch: " + t.input); + } + if (!isStatic) { + if (f.getOffset() != offset) { + throw new RuntimeException("bad redefinition of field offset: " + t.input); + } + } else { + if (!f.getStaticFieldAddress().equals(staticAddress)) { + throw new RuntimeException("bad redefinition of field location: " + t.input); + } + } + if (f.getType() != fieldType) { + throw new RuntimeException("bad redefinition of field type: " + t.input); + } + return; + } + } + + // Create field by type + HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase(); + db.createField(containingType, + fieldName, fieldType, + isStatic, + offset, + staticAddress); + + } + } + + }, + new Command("tokenize", "tokenize ...", true) { + public void doit(Tokens t) { + while (t.hasMoreTokens()) { + out.println("\"" + t.nextToken() + "\""); + } + } + }, + new Command("type", "type [ type [ name super isOop isInteger isUnsigned size ] ]", true) { + public void doit(Tokens t) { + if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) { + usage(); + return; + } + if (t.countTokens() == 6) { + String typeName = t.nextToken(); + String superclassName = t.nextToken(); + if (superclassName.equals("null")) { + superclassName = null; + } + boolean isOop = Boolean.valueOf(t.nextToken()).booleanValue(); + boolean isInteger = Boolean.valueOf(t.nextToken()).booleanValue(); + boolean isUnsigned = Boolean.valueOf(t.nextToken()).booleanValue(); + long size = Long.parseLong(t.nextToken()); + + BasicType type = null; + try { + type = (BasicType)agent.getTypeDataBase().lookupType(typeName); + } catch (RuntimeException e) { + } + if (type != null) { + if (type.isOopType() != isOop) { + throw new RuntimeException("oop mismatch in type definition: " + t.input); + } + if (type.isCIntegerType() != isInteger) { + throw new RuntimeException("integer type mismatch in type definition: " + t.input); + } + if (type.isCIntegerType() && (((CIntegerType)type).isUnsigned()) != isUnsigned) { + throw new RuntimeException("unsigned mismatch in type definition: " + t.input); + } + if (type.getSuperclass() == null) { + if (superclassName != null) { + if (type.getSize() == -1) { + type.setSuperclass(agent.getTypeDataBase().lookupType(superclassName)); + } else { + throw new RuntimeException("unexpected superclass in type definition: " + t.input); + } + } + } else { + if (superclassName == null) { + throw new RuntimeException("missing superclass in type definition: " + t.input); + } + if (!type.getSuperclass().getName().equals(superclassName)) { + throw new RuntimeException("incorrect superclass in type definition: " + t.input); + } + } + if (type.getSize() != size) { + if (type.getSize() == -1) { + type.setSize(size); + } + throw new RuntimeException("size mismatch in type definition: " + t.input); + } + return; + } + + // Create type + HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase(); + db.createType(typeName, superclassName, isOop, isInteger, isUnsigned, size); + } else if (t.countTokens() == 1) { + Type type = agent.getTypeDataBase().lookupType(t.nextToken()); + dumpType(type); + } else { + Iterator i = agent.getTypeDataBase().getTypes(); + while (i.hasNext()) { + dumpType((Type)i.next()); + } + } + } + + }, + new Command("source", "source filename", true) { + public void doit(Tokens t) { + if (t.countTokens() != 1) { + usage(); + return; + } + String file = t.nextToken(); + BufferedReader savedInput = in; + try { + BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(file))); + in = input; + run(false); + } catch (Exception e) { + out.println("Error: " + e); + if (verboseExceptions) { + e.printStackTrace(out); + } + } finally { + in = savedInput; + } + + } + }, + new Command("search", "search [ heap | codecache | threads ] value", false) { + public void doit(Tokens t) { + if (t.countTokens() != 2) { + usage(); + } else { + String type = t.nextToken(); + final Address value = VM.getVM().getDebugger().parseAddress(t.nextToken()); + final long stride = VM.getVM().getAddressSize(); + if (type.equals("threads")) { + Threads threads = VM.getVM().getThreads(); + for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { + Address base = thread.getBaseOfStackPointer(); + Address end = thread.getLastJavaSP(); + if (end == null) continue; + if (end.lessThan(base)) { + Address tmp = base; + base = end; + end = tmp; + } + out.println("Searching " + base + " " + end); + while (base != null && base.lessThan(end)) { + Address val = base.getAddressAt(0); + if (AddressOps.equal(val, value)) { + out.println(base); + } + base = base.addOffsetTo(stride); + } + } + } else if (type.equals("heap")) { + RawHeapVisitor iterator = new RawHeapVisitor() { + public void prologue(long used) { + } + + public void visitAddress(Address addr) { + Address val = addr.getAddressAt(0); + if (AddressOps.equal(val, value)) { + out.println("found at " + addr); + } + } + + public void epilogue() { + } + }; + VM.getVM().getObjectHeap().iterateRaw(iterator); + } else if (type.equals("codecache")) { + CodeCacheVisitor v = new CodeCacheVisitor() { + public void prologue(Address start, Address end) { + } + public void visit(CodeBlob blob) { + boolean printed = false; + Address base = blob.getAddress(); + Address end = base.addOffsetTo(blob.getSize()); + while (base != null && base.lessThan(end)) { + Address val = base.getAddressAt(0); + if (AddressOps.equal(val, value)) { + if (!printed) { + printed = true; + blob.printOn(out); + } + out.println("found at " + base + "\n"); + } + base = base.addOffsetTo(stride); + } + } + public void epilogue() { + } + + + }; + VM.getVM().getCodeCache().iterate(v); + + } + } + } + }, + new Command("where", "where { -a | id }", false) { + public void doit(Tokens t) { + if (t.countTokens() != 1) { + usage(); + } else { + String name = t.nextToken(); + Threads threads = VM.getVM().getThreads(); + boolean all = name.equals("-a"); + for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { + StringWriter sw = new StringWriter(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + thread.printThreadIDOn(new PrintStream(bos)); + if (all || bos.toString().equals(name)) { + HTMLGenerator gen = new HTMLGenerator(false); + out.println(gen.genHTMLForJavaStackTrace(thread)); + if (!all) return; + } + } + if (!all) out.println("Couldn't find thread " + name); + } + } + }, + + new Command("threads", false) { + public void doit(Tokens t) { + if (t.countTokens() != 0) { + usage(); + } else { + Threads threads = VM.getVM().getThreads(); + for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { + thread.printThreadIDOn(out); + out.println(" " + thread.getThreadName()); + } + } + } + }, + + new Command("livenmethods", false) { + public void doit(Tokens t) { + if (t.countTokens() != 0) { + usage(); + } else { + ArrayList nmethods = new ArrayList(); + Threads threads = VM.getVM().getThreads(); + HTMLGenerator gen = new HTMLGenerator(false); + for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { + try { + for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) { + if (vf instanceof CompiledVFrame) { + NMethod c = ((CompiledVFrame)vf).getCode(); + if (!nmethods.contains(c)) { + nmethods.add(c); + out.println(gen.genHTML(c)); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + }, + new Command("universe", false) { + public void doit(Tokens t) { + if (t.countTokens() != 0) { + usage(); + } else { + Universe u = VM.getVM().getUniverse(); + out.println("Heap Parameters:"); + u.heap().printOn(out); + } + } + }, + new Command("verbose", "verbose true | false", true) { + public void doit(Tokens t) { + if (t.countTokens() != 1) { + usage(); + } else { + verboseExceptions = Boolean.valueOf(t.nextToken()).booleanValue(); + } + } + }, + new Command("assert", "assert true | false", true) { + public void doit(Tokens t) { + if (t.countTokens() != 1) { + usage(); + } else { + Assert.ASSERTS_ENABLED = Boolean.valueOf(t.nextToken()).booleanValue(); + } + } + }, + }; + + private boolean verboseExceptions = false; + private ArrayList history = new ArrayList(); + private HashMap commands = new HashMap(); + private boolean doEcho = false; + + private Command findCommand(String key) { + return (Command)commands.get(key); + } + + public void printPrompt() { + out.print("hsdb> "); + } + + private DebuggerInterface debugger; + private HotSpotAgent agent; + private JSJavaScriptEngine jsengine; + private BufferedReader in; + private PrintStream out; + private PrintStream err; + + // called before debuggee attach + private void preAttach() { + // nothing for now.. + } + + // called after debuggee attach + private void postAttach() { + // create JavaScript engine and start it + jsengine = new JSJavaScriptEngine() { + private ObjectReader reader = new ObjectReader(); + private JSJavaFactory factory = new JSJavaFactoryImpl(); + public ObjectReader getObjectReader() { + return reader; + } + public JSJavaFactory getJSJavaFactory() { + return factory; + } + protected void quit() { + debugger.detach(); + System.exit(0); + } + protected BufferedReader getInputReader() { + return in; + } + protected PrintStream getOutputStream() { + return out; + } + protected PrintStream getErrorStream() { + return err; + } + }; + try { + jsengine.defineFunction(this, + this.getClass().getMethod("registerCommand", + new Class[] { + String.class, String.class, String.class + })); + } catch (NoSuchMethodException exp) { + // should not happen, see below...!! + exp.printStackTrace(); + } + jsengine.start(); + } + + public void registerCommand(String cmd, String usage, final String func) { + commands.put(cmd, new Command(cmd, usage, false) { + public void doit(Tokens t) { + final int len = t.countTokens(); + Object[] args = new Object[len]; + for (int i = 0; i < len; i++) { + args[i] = t.nextToken(); + } + jsengine.call(func, args); + } + }); + } + + public void setOutput(PrintStream o) { + out = o; + } + + public void setErr(PrintStream e) { + err = e; + } + + public CommandProcessor(DebuggerInterface debugger, BufferedReader in, PrintStream out, PrintStream err) { + this.debugger = debugger; + this.agent = debugger.getAgent(); + this.in = in; + this.out = out; + this.err = err; + for (int i = 0; i < commandList.length; i++) { + Command c = commandList[i]; + commands.put(c.name, c); + } + if (debugger.isAttached()) { + postAttach(); + } + } + + + public void run(boolean prompt) { + // Process interactive commands. + while (true) { + if (prompt) printPrompt(); + String ln = null; + try { + ln = in.readLine(); + } catch (IOException e) { + } + if (ln == null) { + if (prompt) err.println("Input stream closed."); + return; + } + + executeCommand(ln); + } + } + + static Pattern historyPattern = Pattern.compile("((!\\*)|(!\\$)|(!!-?)|(!-?[0-9][0-9]*))"); + + public void executeCommand(String ln) { + if (ln.indexOf('!') != -1) { + int size = history.size(); + if (size == 0) { + ln = ""; + err.println("History is empty"); + } else { + StringBuffer result = new StringBuffer(); + Matcher m = historyPattern.matcher(ln); + int start = 0; + while (m.find()) { + if (m.start() > start) { + result.append(ln.substring(start, m.start() - start)); + } + start = m.end(); + + String cmd = m.group(); + if (cmd.equals("!!")) { + result.append((String)history.get(history.size() - 1)); + } else if (cmd.equals("!!-")) { + Tokens item = new Tokens((String)history.get(history.size() - 1)); + item.trim(1); + result.append(item.join(" ")); + } else if (cmd.equals("!*")) { + Tokens item = new Tokens((String)history.get(history.size() - 1)); + item.nextToken(); + result.append(item.join(" ")); + } else if (cmd.equals("!$")) { + Tokens item = new Tokens((String)history.get(history.size() - 1)); + result.append(item.at(item.countTokens() - 1)); + } else { + String tail = cmd.substring(1); + int index = Integer.parseInt(tail); + if (index < 0) { + index = history.size() + index; + } + if (index > size) { + err.println("No such history item"); + } else { + result.append((String)history.get(index)); + } + } + } + if (result.length() == 0) { + err.println("malformed history reference"); + ln = ""; + } else { + if (start < ln.length()) { + result.append(ln.substring(start)); + } + ln = result.toString(); + if (!doEcho) { + out.println(ln); + } + } + } + } + + if (doEcho) { + out.println("+ " + ln); + } + + PrintStream redirect = null; + Tokens t = new Tokens(ln); + if (t.hasMoreTokens()) { + boolean error = false; + history.add(ln); + int len = t.countTokens(); + if (len > 2) { + String r = t.at(len - 2); + if (r.equals(">") || r.equals(">>")) { + boolean append = r.length() == 2; + String file = t.at(len - 1); + try { + redirect = new PrintStream(new BufferedOutputStream(new FileOutputStream(file, append))); + t.trim(2); + } catch (Exception e) { + out.println("Error: " + e); + if (verboseExceptions) { + e.printStackTrace(out); + } + error = true; + } + } + } + if (!error) { + PrintStream savedout = out; + if (redirect != null) { + out = redirect; + } + try { + executeCommand(t); + } catch (Exception e) { + err.println("Error: " + e); + if (verboseExceptions) { + e.printStackTrace(err); + } + } finally { + if (redirect != null) { + out = savedout; + redirect.close(); + } + } + } + } + } + + void executeCommand(Tokens args) { + String cmd = args.nextToken(); + + Command doit = findCommand(cmd); + + /* + * Check for an unknown command + */ + if (doit == null) { + out.println("Unrecognized command. Try help..."); + } else if (!debugger.isAttached() && !doit.okIfDisconnected) { + out.println("Command not valid until the attached to a VM"); + } else { + try { + doit.doit(args); + } catch (Exception e) { + out.println("Error: " + e); + if (verboseExceptions) { + e.printStackTrace(out); + } + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/DebugServer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/DebugServer.java new file mode 100644 index 00000000000..be79f1d6e28 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/DebugServer.java @@ -0,0 +1,133 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.dbx.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.oops.*; + +public class DebugServer { + private void usage() { + System.out.println("usage: java " + getClass().getName() + " [server id]"); + System.out.println(" or: java " + getClass().getName() + " [server id]"); + System.out.println("\"pid\" must be the process ID of a HotSpot process."); + System.out.println("If reading a core file, \"executable\" must (currently) be the"); + System.out.println("full path name to the precise java executable which generated"); + System.out.println("the core file (not, on Solaris, the \"java\" wrapper script in"); + System.out.println("the \"bin\" subdirectory of the JDK.)"); + System.out.println("The \"server id\" is a unique name for a specific remote debuggee."); + System.exit(1); + } + + public static void main(String[] args) { + new DebugServer().run(args); + } + + private void run(String[] args) { + if ((args.length < 1) || (args.length > 3)) { + usage(); + } + + // Attempt to handle "-h" or "-help" + if (args[0].startsWith("-")) { + usage(); + } + + int pid = 0; + boolean usePid = false; + String coreFileName = null; + // FIXME: would be nice to pick this up from the core file + // somehow, but that doesn't look possible. Should at least figure + // it out from a path to the JDK. + String javaExecutableName = null; + String serverID = null; + + switch (args.length) { + case 1: + try { + pid = Integer.parseInt(args[0]); + usePid = true; + } catch (NumberFormatException e) { + usage(); + } + break; + + case 2: + // either we have pid and server id or exec file and core file + try { + pid = Integer.parseInt(args[0]); + usePid = true; + serverID = args[1]; + } catch (NumberFormatException e) { + pid = -1; + usePid = false; + javaExecutableName = args[0]; + coreFileName = args[1]; + } + break; + + case 3: + javaExecutableName = args[0]; + coreFileName = args[1]; + serverID = args[2]; + break; + + default: + // should not happend, taken care already. + break; + } + + final HotSpotAgent agent = new HotSpotAgent(); + try { + if (usePid) { + System.err.println("Attaching to process ID " + pid + " and starting RMI services, please wait..."); + agent.startServer(pid, serverID); + } else { + System.err.println("Attaching to core " + coreFileName + + " from executable " + javaExecutableName + " and starting RMI services, please wait..."); + agent.startServer(javaExecutableName, coreFileName, serverID); + } + } + catch (DebuggerException e) { + if (usePid) { + System.err.print("Error attaching to process or starting server: "); + } else { + System.err.print("Error attaching to core file or starting server: "); + } + e.printStackTrace(); + System.exit(1); + } + + // shutdown hook to clean-up the server in case of forced exit. + Runtime.getRuntime().addShutdownHook(new java.lang.Thread( + new Runnable() { + public void run() { + agent.shutdownServer(); + } + })); + System.err.println("Debugger attached and RMI services started."); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/HSDB.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HSDB.java new file mode 100644 index 00000000000..45c4866fc46 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HSDB.java @@ -0,0 +1,1779 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import java.io.*; +import java.awt.*; +import java.awt.event.*; +import java.math.*; +import javax.swing.*; +import javax.swing.tree.*; +import java.util.*; + +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.compiler.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_implementation.parallelScavenge.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.ui.*; +import sun.jvm.hotspot.ui.tree.*; +import sun.jvm.hotspot.ui.classbrowser.*; +import sun.jvm.hotspot.utilities.*; + +/** The top-level HotSpot Debugger. FIXME: make this an embeddable + component! (Among other things, figure out what to do with the + menu bar...) */ + +public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + public static void main(String[] args) { + new HSDB(args).run(); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + private HotSpotAgent agent; + private JDesktopPane desktop; + private boolean attached; + /** List */ + private java.util.List attachMenuItems; + /** List */ + private java.util.List detachMenuItems; + private JMenu toolsMenu; + private JMenuItem showDbgConsoleMenuItem; + private JMenuItem computeRevPtrsMenuItem; + private JInternalFrame attachWaitDialog; + private JInternalFrame threadsFrame; + private JInternalFrame consoleFrame; + private WorkerThread workerThread; + // These had to be made data members because they are referenced in inner classes. + private String pidText; + private int pid; + private String execPath; + private String coreFilename; + + private void doUsage() { + System.out.println("Usage: java HSDB [[pid] | [path-to-java-executable [path-to-corefile]] | help ]"); + System.out.println(" pid: attach to the process whose id is 'pid'"); + System.out.println(" path-to-java-executable: Debug a core file produced by this program"); + System.out.println(" path-to-corefile: Debug this corefile. The default is 'core'"); + System.out.println(" If no arguments are specified, you can select what to do from the GUI.\n"); + HotSpotAgent.showUsage(); + } + + private HSDB(String[] args) { + switch (args.length) { + case (0): + break; + + case (1): + if (args[0].equals("help") || args[0].equals("-help")) { + doUsage(); + System.exit(0); + } + // If all numbers, it is a PID to attach to + // Else, it is a pathname to a .../bin/java for a core file. + try { + int unused = Integer.parseInt(args[0]); + // If we get here, we have a PID and not a core file name + pidText = args[0]; + } catch (NumberFormatException e) { + execPath = args[0]; + coreFilename = "core"; + } + break; + + case (2): + execPath = args[0]; + coreFilename = args[1]; + break; + + default: + System.out.println("HSDB Error: Too many options specified"); + doUsage(); + System.exit(1); + } + } + + private void run() { + // At this point, if pidText != null we are supposed to attach to it. + // Else, if execPath != null, it is the path of a jdk/bin/java + // and coreFilename is the pathname of a core file we are + // supposed to attach to. + + agent = new HotSpotAgent(); + workerThread = new WorkerThread(); + attachMenuItems = new java.util.ArrayList(); + detachMenuItems = new java.util.ArrayList(); + + JFrame frame = new JFrame("HSDB - HotSpot Debugger"); + frame.setSize(800, 600); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + + JMenuBar menuBar = new JMenuBar(); + + // + // File menu + // + + JMenu menu = new JMenu("File"); + menu.setMnemonic(KeyEvent.VK_F); + JMenuItem item; + item = createMenuItem("Attach to HotSpot process...", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showAttachDialog(); + } + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.ALT_MASK)); + item.setMnemonic(KeyEvent.VK_A); + menu.add(item); + attachMenuItems.add(item); + + item = createMenuItem("Open HotSpot core file...", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showOpenCoreFileDialog(); + } + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.ALT_MASK)); + item.setMnemonic(KeyEvent.VK_O); + menu.add(item); + attachMenuItems.add(item); + + item = createMenuItem("Connect to debug server...", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showConnectDialog(); + } + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.ALT_MASK)); + item.setMnemonic(KeyEvent.VK_S); + menu.add(item); + attachMenuItems.add(item); + + item = createMenuItem("Detach", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + detach(); + } + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, ActionEvent.ALT_MASK)); + item.setMnemonic(KeyEvent.VK_S); + menu.add(item); + detachMenuItems.add(item); + + // Disable detach menu items at first + setMenuItemsEnabled(detachMenuItems, false); + + menu.addSeparator(); + + item = createMenuItem("Exit", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.exit(0); + } + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.ALT_MASK)); + item.setMnemonic(KeyEvent.VK_X); + menu.add(item); + menuBar.add(menu); + + // + // Tools menu + // + + toolsMenu = new JMenu("Tools"); + toolsMenu.setMnemonic(KeyEvent.VK_T); + + item = createMenuItem("Class Browser", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showClassBrowser(); + } + }); + item.setMnemonic(KeyEvent.VK_B); + + toolsMenu.add(item); + + item = createMenuItem("Code Viewer", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showCodeViewer(); + } + }); + item.setMnemonic(KeyEvent.VK_C); + + toolsMenu.add(item); + + + item = createMenuItem("Compute Reverse Ptrs", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + fireComputeReversePtrs(); + } + }); + computeRevPtrsMenuItem = item; + item.setMnemonic(KeyEvent.VK_M); + toolsMenu.add(item); + + item = createMenuItem("Deadlock Detection", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showDeadlockDetectionPanel(); + } + }); + item.setMnemonic(KeyEvent.VK_D); + toolsMenu.add(item); + + item = createMenuItem("Find Object by Query", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showFindByQueryPanel(); + } + }); + item.setMnemonic(KeyEvent.VK_Q); + toolsMenu.add(item); + + + item = createMenuItem("Find Pointer", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showFindPanel(); + } + }); + item.setMnemonic(KeyEvent.VK_P); + toolsMenu.add(item); + + item = createMenuItem("Find Value In Heap", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showFindInHeapPanel(); + } + }); + item.setMnemonic(KeyEvent.VK_V); + toolsMenu.add(item); + + item = createMenuItem("Find Value In Code Cache", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showFindInCodeCachePanel(); + } + }); + item.setMnemonic(KeyEvent.VK_A); + toolsMenu.add(item); + + item = createMenuItem("Heap Parameters", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showHeapParametersPanel(); + } + }); + item.setMnemonic(KeyEvent.VK_H); + toolsMenu.add(item); + + item = createMenuItem("Inspector", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showInspector(null); + } + }); + item.setMnemonic(KeyEvent.VK_R); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.ALT_MASK)); + toolsMenu.add(item); + + item = createMenuItem("Memory Viewer", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showMemoryViewer(); + } + }); + item.setMnemonic(KeyEvent.VK_M); + toolsMenu.add(item); + + item = createMenuItem("Monitor Cache Dump", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showMonitorCacheDumpPanel(); + } + }); + item.setMnemonic(KeyEvent.VK_D); + toolsMenu.add(item); + + item = createMenuItem("Object Histogram", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showObjectHistogram(); + } + }); + item.setMnemonic(KeyEvent.VK_O); + toolsMenu.add(item); + + item = createMenuItem("Show System Properties", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showSystemProperties(); + } + }); + item.setMnemonic(KeyEvent.VK_S); + toolsMenu.add(item); + + item = createMenuItem("Show VM Version", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showVMVersion(); + } + }); + item.setMnemonic(KeyEvent.VK_M); + toolsMenu.add(item); + + item = createMenuItem("Show -XX flags", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showCommandLineFlags(); + } + }); + item.setMnemonic(KeyEvent.VK_X); + toolsMenu.add(item); + + toolsMenu.setEnabled(false); + menuBar.add(toolsMenu); + + // + // Windows menu + // + + JMenu windowsMenu = new JMenu("Windows"); + windowsMenu.setMnemonic(KeyEvent.VK_W); + item = createMenuItem("Console", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showConsole(); + } + }); + item.setMnemonic(KeyEvent.VK_C); + windowsMenu.add(item); + showDbgConsoleMenuItem = createMenuItem("Debugger Console", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showDebuggerConsole(); + } + }); + showDbgConsoleMenuItem.setMnemonic(KeyEvent.VK_D); + windowsMenu.add(showDbgConsoleMenuItem); + showDbgConsoleMenuItem.setEnabled(false); + + menuBar.add(windowsMenu); + + + frame.setJMenuBar(menuBar); + + desktop = new JDesktopPane(); + frame.getContentPane().add(desktop); + GraphicsUtilities.reshapeToAspectRatio(frame, 4.0f/3.0f, 0.75f, Toolkit.getDefaultToolkit().getScreenSize()); + GraphicsUtilities.centerInContainer(frame, Toolkit.getDefaultToolkit().getScreenSize()); + frame.show(); + + Runtime.getRuntime().addShutdownHook(new java.lang.Thread() { + public void run() { + detachDebugger(); + } + }); + + if (pidText != null) { + attach(pidText); + } else if (execPath != null) { + attach(execPath, coreFilename); + } + } + + // FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog + private void showAttachDialog() { + // FIXME: create filtered text field which only accepts numbers + setMenuItemsEnabled(attachMenuItems, false); + final JInternalFrame attachDialog = new JInternalFrame("Attach to HotSpot process"); + attachDialog.getContentPane().setLayout(new BorderLayout()); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + attachDialog.setBackground(panel.getBackground()); + + panel.add(new JLabel("Enter process ID:")); + final JTextField pidTextField = new JTextField(10); + ActionListener attacher = new ActionListener() { + public void actionPerformed(ActionEvent e) { + attachDialog.setVisible(false); + desktop.remove(attachDialog); + workerThread.invokeLater(new Runnable() { + public void run() { + attach(pidTextField.getText()); + } + }); + } + }; + + pidTextField.addActionListener(attacher); + panel.add(pidTextField); + attachDialog.getContentPane().add(panel, BorderLayout.NORTH); + + Box vbox = Box.createVerticalBox(); + panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + JTextArea ta = new JTextArea( + "Enter the process ID of a currently-running HotSpot process. On " + + "Solaris and most Unix operating systems, this can be determined by " + + "typing \"ps -u | grep java\"; the process ID is the " + + "first number which appears on the resulting line. On Windows, the " + + "process ID is present in the Task Manager, which can be brought up " + + "while logged on to the desktop by pressing Ctrl-Alt-Delete."); + ta.setLineWrap(true); + ta.setWrapStyleWord(true); + ta.setEditable(false); + ta.setBackground(panel.getBackground()); + panel.add(ta); + vbox.add(panel); + + Box hbox = Box.createHorizontalBox(); + hbox.add(Box.createGlue()); + JButton button = new JButton("OK"); + button.addActionListener(attacher); + hbox.add(button); + hbox.add(Box.createHorizontalStrut(20)); + button = new JButton("Cancel"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + attachDialog.setVisible(false); + desktop.remove(attachDialog); + setMenuItemsEnabled(attachMenuItems, true); + } + }); + hbox.add(button); + hbox.add(Box.createGlue()); + panel = new JPanel(); + panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + panel.add(hbox); + vbox.add(panel); + + attachDialog.getContentPane().add(vbox, BorderLayout.SOUTH); + + desktop.add(attachDialog); + attachDialog.setSize(400, 300); + GraphicsUtilities.centerInContainer(attachDialog); + attachDialog.show(); + pidTextField.requestFocus(); + } + + // FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog + private void showOpenCoreFileDialog() { + setMenuItemsEnabled(attachMenuItems, false); + final JInternalFrame dialog = new JInternalFrame("Open Core File"); + dialog.getContentPane().setLayout(new BorderLayout()); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + dialog.setBackground(panel.getBackground()); + + Box hbox = Box.createHorizontalBox(); + Box vbox = Box.createVerticalBox(); + vbox.add(new JLabel("Path to core file:")); + vbox.add(new JLabel("Path to Java executable:")); + hbox.add(vbox); + + vbox = Box.createVerticalBox(); + final JTextField corePathField = new JTextField(40); + final JTextField execPathField = new JTextField(40); + vbox.add(corePathField); + vbox.add(execPathField); + hbox.add(vbox); + + final JButton browseCorePath = new JButton("Browse .."); + final JButton browseExecPath = new JButton("Browse .."); + browseCorePath.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JFileChooser fileChooser = new JFileChooser(new File(".")); + int retVal = fileChooser.showOpenDialog(dialog); + if (retVal == JFileChooser.APPROVE_OPTION) { + corePathField.setText(fileChooser.getSelectedFile().getPath()); + } + } + }); + browseExecPath.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JFileChooser fileChooser = new JFileChooser(new File(".")); + int retVal = fileChooser.showOpenDialog(dialog); + if (retVal == JFileChooser.APPROVE_OPTION) { + execPathField.setText(fileChooser.getSelectedFile().getPath()); + } + } + }); + vbox = Box.createVerticalBox(); + vbox.add(browseCorePath); + vbox.add(browseExecPath); + hbox.add(vbox); + + panel.add(hbox); + dialog.getContentPane().add(panel, BorderLayout.NORTH); + + ActionListener attacher = new ActionListener() { + public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); + desktop.remove(dialog); + workerThread.invokeLater(new Runnable() { + public void run() { + attach(execPathField.getText(), corePathField.getText()); + } + }); + } + }; + corePathField.addActionListener(attacher); + execPathField.addActionListener(attacher); + + vbox = Box.createVerticalBox(); + panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + JTextArea ta = new JTextArea( + "Enter the full path names to the core file from a HotSpot process " + + "and the Java executable from which it came. The latter is typically " + + "located in the JDK/JRE directory under the directory " + + "jre/bin//native_threads."); + ta.setLineWrap(true); + ta.setWrapStyleWord(true); + ta.setEditable(false); + ta.setBackground(panel.getBackground()); + panel.add(ta); + vbox.add(panel); + + hbox = Box.createHorizontalBox(); + hbox.add(Box.createGlue()); + JButton button = new JButton("OK"); + button.addActionListener(attacher); + hbox.add(button); + hbox.add(Box.createHorizontalStrut(20)); + button = new JButton("Cancel"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); + desktop.remove(dialog); + setMenuItemsEnabled(attachMenuItems, true); + } + }); + hbox.add(button); + hbox.add(Box.createGlue()); + panel = new JPanel(); + panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + panel.add(hbox); + vbox.add(panel); + + dialog.getContentPane().add(vbox, BorderLayout.SOUTH); + + desktop.add(dialog); + dialog.setSize(500, 300); + GraphicsUtilities.centerInContainer(dialog); + dialog.show(); + corePathField.requestFocus(); + } + + // FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog + private void showConnectDialog() { + // FIXME: create filtered text field which only accepts numbers + setMenuItemsEnabled(attachMenuItems, false); + final JInternalFrame dialog = new JInternalFrame("Connect to HotSpot Debug Server"); + dialog.getContentPane().setLayout(new BorderLayout()); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + dialog.setBackground(panel.getBackground()); + + panel.add(new JLabel("Enter machine name:")); + final JTextField pidTextField = new JTextField(40); + ActionListener attacher = new ActionListener() { + public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); + desktop.remove(dialog); + workerThread.invokeLater(new Runnable() { + public void run() { + connect(pidTextField.getText()); + } + }); + } + }; + + pidTextField.addActionListener(attacher); + panel.add(pidTextField); + dialog.getContentPane().add(panel, BorderLayout.NORTH); + + Box vbox = Box.createVerticalBox(); + panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + JTextArea ta = new JTextArea( + "Enter the name of a machine on which the HotSpot \"Debug Server\" is " + + "running and is attached to a process or core file."); + ta.setLineWrap(true); + ta.setWrapStyleWord(true); + ta.setEditable(false); + ta.setBackground(panel.getBackground()); + panel.add(ta); + vbox.add(panel); + + Box hbox = Box.createHorizontalBox(); + hbox.add(Box.createGlue()); + JButton button = new JButton("OK"); + button.addActionListener(attacher); + hbox.add(button); + hbox.add(Box.createHorizontalStrut(20)); + button = new JButton("Cancel"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); + desktop.remove(dialog); + setMenuItemsEnabled(attachMenuItems, true); + } + }); + hbox.add(button); + hbox.add(Box.createGlue()); + panel = new JPanel(); + panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + panel.add(hbox); + vbox.add(panel); + + dialog.getContentPane().add(vbox, BorderLayout.SOUTH); + + desktop.add(dialog); + dialog.setSize(400, 300); + GraphicsUtilities.centerInContainer(dialog); + dialog.show(); + pidTextField.requestFocus(); + } + + public void showThreadOopInspector(JavaThread thread) { + showInspector(new OopTreeNodeAdapter(thread.getThreadObj(), null)); + } + + public void showInspector(SimpleTreeNode adapter) { + showPanel("Inspector", new Inspector(adapter), 1.0f, 0.65f); + } + + public void showLiveness(Oop oop, LivenessPathList liveness) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PrintStream tty = new PrintStream(bos); + int numPaths = liveness.size(); + for (int i = 0; i < numPaths; i++) { + tty.println("Path " + (i + 1) + " of " + numPaths + ":"); + liveness.get(i).printOn(tty); + } + JTextArea ta = new JTextArea(bos.toString()); + ta.setLineWrap(true); + ta.setWrapStyleWord(true); + ta.setEditable(false); + + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + + JScrollPane scroller = new JScrollPane(); + scroller.getViewport().add(ta); + + panel.add(scroller, BorderLayout.CENTER); + + bos = new ByteArrayOutputStream(); + tty = new PrintStream(bos); + tty.print("Liveness result for "); + Oop.printOopValueOn(oop, tty); + + JInternalFrame frame = new JInternalFrame(bos.toString()); + frame.setResizable(true); + frame.setClosable(true); + frame.setIconifiable(true); + frame.getContentPane().setLayout(new BorderLayout()); + frame.getContentPane().add(panel, BorderLayout.CENTER); + frame.pack(); + desktop.add(frame); + GraphicsUtilities.reshapeToAspectRatio(frame, 0.5f / 0.2f, 0.5f, frame.getParent().getSize()); + frame.show(); + } + + private void fireComputeReversePtrs() { + // Possible this might have been computed elsewhere + if (VM.getVM().getRevPtrs() != null) { + computeRevPtrsMenuItem.setEnabled(false); + return; + } + + workerThread.invokeLater(new Runnable() { + public void run() { + HeapProgress progress = new HeapProgress("Reverse Pointers Analysis"); + try { + ReversePtrsAnalysis analysis = new ReversePtrsAnalysis(); + analysis.setHeapProgressThunk(progress); + analysis.run(); + computeRevPtrsMenuItem.setEnabled(false); + } catch (OutOfMemoryError e) { + final String errMsg = formatMessage(e.toString(), 80); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JOptionPane.showInternalMessageDialog(desktop, + "Error computing reverse pointers:" + errMsg, + "Error", + JOptionPane.WARNING_MESSAGE); + } + }); + } finally { + // make sure the progress bar goes away + progress.heapIterationComplete(); + } + } + }); + } + + // Simple struct containing signal information + class SignalInfo { + public int sigNum; + public String sigName; + } + + // Need to have mutable vframe as well as visible memory panel + abstract class StackWalker implements Runnable { + protected JavaVFrame vf; + protected AnnotatedMemoryPanel annoPanel; + + StackWalker(JavaVFrame vf, AnnotatedMemoryPanel annoPanel) { + this.vf = vf; + this.annoPanel = annoPanel; + } + } + + public void showThreadStackMemory(final JavaThread thread) { + // dumpStack(thread); + JavaVFrame vframe = getLastJavaVFrame(thread); + if (vframe == null) { + JOptionPane.showInternalMessageDialog(desktop, + "Thread \"" + thread.getThreadName() + + "\" has no Java frames on its stack", + "Show Stack Memory", + JOptionPane.INFORMATION_MESSAGE); + return; + } + + JInternalFrame stackFrame = new JInternalFrame("Stack Memory for " + thread.getThreadName()); + stackFrame.getContentPane().setLayout(new BorderLayout()); + stackFrame.setResizable(true); + stackFrame.setClosable(true); + stackFrame.setIconifiable(true); + final long addressSize = agent.getTypeDataBase().getAddressSize(); + boolean is64Bit = (addressSize == 8); + // This is somewhat of a hack to guess a thread's stack limits since the + // JavaThread doesn't support this functionality. However it is nice in that + // it locks us into the active region of the thread's stack and not its + // theoretical limits. + // + sun.jvm.hotspot.runtime.Frame tmpFrame = thread.getCurrentFrameGuess(); + Address sp = tmpFrame.getSP(); + Address starting = sp; + Address maxSP = starting; + Address minSP = starting; + RegisterMap tmpMap = thread.newRegisterMap(false); + while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) { + tmpFrame = tmpFrame.sender(tmpMap); + if (tmpFrame != null) { + sp = tmpFrame.getSP(); + if (sp != null) { + maxSP = AddressOps.max(maxSP, sp); + minSP = AddressOps.min(minSP, sp); + } + } + + } + // It is useful to be able to see say +/- 8K on the current stack range + AnnotatedMemoryPanel annoMemPanel = new AnnotatedMemoryPanel(agent.getDebugger(), is64Bit, starting, + minSP.addOffsetTo(-8192), + maxSP.addOffsetTo( 8192)); + + stackFrame.getContentPane().add(annoMemPanel, BorderLayout.CENTER); + desktop.add(stackFrame); + GraphicsUtilities.reshapeToAspectRatio(stackFrame, 4.0f / 3.0f, 0.85f, stackFrame.getParent().getSize()); + stackFrame.show(); + + // Stackmap computation for interpreted frames is expensive; do + // all stackwalking work in another thread for better GUI + // responsiveness + workerThread.invokeLater(new StackWalker(vframe, annoMemPanel) { + public void run() { + Address startAddr = null; + + // As this is a debugger, we want to provide potential crash + // information to the user, i.e., by marking signal handler frames + // on the stack. Since this system is currently targeted at + // annotating the Java frames (interpreted or compiled) on the + // stack and not, for example, "external" frames (note the current + // absence of a PC-to-symbol lookup mechanism at the Debugger + // level), we want to mark any Java frames which were interrupted + // by a signal. We do this by making two passes over the stack, + // one which finds signal handler frames and puts the parent + // frames in a table and one which finds Java frames and if they + // are in the table indicates that they were interrupted by a signal. + + Map interruptedFrameMap = new HashMap(); + { + sun.jvm.hotspot.runtime.Frame tmpFrame = thread.getCurrentFrameGuess(); + RegisterMap tmpMap = thread.newRegisterMap(false); + while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) { + if (tmpFrame.isSignalHandlerFrameDbg()) { + // Add some information to the map that we can extract later + sun.jvm.hotspot.runtime.Frame interruptedFrame = tmpFrame.sender(tmpMap); + SignalInfo info = new SignalInfo(); + info.sigNum = tmpFrame.getSignalNumberDbg(); + info.sigName = tmpFrame.getSignalNameDbg(); + interruptedFrameMap.put(interruptedFrame, info); + } + tmpFrame = tmpFrame.sender(tmpMap); + } + } + + while (vf != null) { + String anno = null; + JavaVFrame curVFrame = vf; + sun.jvm.hotspot.runtime.Frame curFrame = curVFrame.getFrame(); + Method interpreterFrameMethod = null; + + if (curVFrame.isInterpretedFrame()) { + anno = "Interpreted frame"; + } else { + anno = "Compiled frame"; + if (curVFrame.isDeoptimized()) { + anno += " (deoptimized)"; + } + } + if (curVFrame.mayBeImpreciseDbg()) { + anno += "; information may be imprecise"; + } + + if (curVFrame.isInterpretedFrame()) { + // Find the codelet + InterpreterCodelet codelet = VM.getVM().getInterpreter().getCodeletContaining(curFrame.getPC()); + String description = null; + if (codelet != null) { + description = codelet.getDescription(); + } + if (description == null) { + anno += "\n(Unknown interpreter codelet)"; + } else { + anno += "\nExecuting in codelet \"" + description + "\" at PC = " + curFrame.getPC(); + } + } else if (curVFrame.isCompiledFrame()) { + anno += "\nExecuting at PC = " + curFrame.getPC(); + } + + if (startAddr == null) { + startAddr = curFrame.getSP(); + } + + // FIXME: some compiled frames with empty oop map sets have been + // found (for example, Vector's inner Enumeration class, method + // "hasMoreElements"). Not sure yet why these cases are showing + // up -- should be possible (though unlikely) for safepoint code + // to patch the return instruction of these methods and then + // later attempt to get an oop map for that instruction. For + // now, we warn if we find such a method. + boolean shouldSkipOopMaps = false; + if (curVFrame.isCompiledFrame()) { + CodeBlob cb = VM.getVM().getCodeCache().findBlob(curFrame.getPC()); + OopMapSet maps = cb.getOopMaps(); + if ((maps == null) || (maps.getSize() == 0)) { + shouldSkipOopMaps = true; + } + } + + // Add signal information to annotation if necessary + SignalInfo sigInfo = (SignalInfo) interruptedFrameMap.get(curFrame); + if (sigInfo != null) { + // This frame took a signal and we need to report it. + anno = (anno + "\n*** INTERRUPTED BY SIGNAL " + Integer.toString(sigInfo.sigNum) + + " (" + sigInfo.sigName + ")"); + } + + JavaVFrame nextVFrame = curVFrame; + sun.jvm.hotspot.runtime.Frame nextFrame = curFrame; + do { + curVFrame = nextVFrame; + curFrame = nextFrame; + + try { + Method method = curVFrame.getMethod(); + if (interpreterFrameMethod == null && curVFrame.isInterpretedFrame()) { + interpreterFrameMethod = method; + } + int bci = curVFrame.getBCI(); + String lineNumberAnno = ""; + if (method.hasLineNumberTable()) { + if ((bci == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) || + (bci >= 0 && bci < method.getCodeSize())) { + lineNumberAnno = ", line " + method.getLineNumberFromBCI(bci); + } else { + lineNumberAnno = " (INVALID BCI)"; + } + } + anno += "\n" + method.getMethodHolder().getName().asString() + "." + + method.getName().asString() + method.getSignature().asString() + + "\n@bci " + bci + lineNumberAnno; + } catch (Exception e) { + anno += "\n(ERROR while iterating vframes for frame " + curFrame + ")"; + } + + nextVFrame = curVFrame.javaSender(); + if (nextVFrame != null) { + nextFrame = nextVFrame.getFrame(); + } + } while (nextVFrame != null && nextFrame.equals(curFrame)); + + if (shouldSkipOopMaps) { + anno = anno + "\nNOTE: null or empty OopMapSet found for this CodeBlob"; + } + + if (curFrame.getFP() != null) { + annoPanel.addAnnotation(new Annotation(curFrame.getSP(), + curFrame.getFP(), + anno)); + } else { + if (VM.getVM().getCPU().equals("x86") || VM.getVM().getCPU().equals("amd64")) { + // For C2, which has null frame pointers on x86/amd64 + CodeBlob cb = VM.getVM().getCodeCache().findBlob(curFrame.getPC()); + Address sp = curFrame.getSP(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb.getFrameSize() > 0, "CodeBlob must have non-zero frame size"); + } + annoPanel.addAnnotation(new Annotation(sp, + sp.addOffsetTo(cb.getFrameSize()), + anno)); + } else { + Assert.that(VM.getVM().getCPU().equals("ia64"), "only ia64 should reach here"); + } + } + + // Add interpreter frame annotations + if (curFrame.isInterpretedFrame()) { + annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameExpressionStack(), + curFrame.addressOfInterpreterFrameTOS(), + "Interpreter expression stack")); + if (interpreterFrameMethod != null) { + // The offset is just to get the right stack slots highlighted in the output + int offset = 1; + annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameLocal(offset), + curFrame.addressOfInterpreterFrameLocal((int) interpreterFrameMethod.getMaxLocals() + offset), + "Interpreter locals area for frame with SP = " + curFrame.getSP())); + } + String methodAnno = "Interpreter frame methodOop"; + if (interpreterFrameMethod == null) { + methodAnno += " (BAD OOP)"; + } + Address a = curFrame.addressOfInterpreterFrameMethod(); + annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), methodAnno)); + a = curFrame.addressOfInterpreterFrameCPCache(); + annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), "Interpreter constant pool cache")); + } + + RegisterMap rm = (RegisterMap) vf.getRegisterMap().clone(); + if (!shouldSkipOopMaps) { + try { + curFrame.oopsDo(new AddressVisitor() { + public void visitAddress(Address addr) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(addr.andWithMask(VM.getVM().getAddressSize() - 1) == null, + "Address " + addr + "should have been aligned"); + } + // Check contents + OopHandle handle = addr.getOopHandleAt(0); + String anno = "null oop"; + if (handle != null) { + // Find location + CollectedHeap collHeap = VM.getVM().getUniverse().heap(); + boolean bad = true; + anno = "BAD OOP"; + if (collHeap instanceof GenCollectedHeap) { + GenCollectedHeap heap = (GenCollectedHeap) collHeap; + for (int i = 0; i < heap.nGens(); i++) { + if (heap.getGen(i).isIn(handle)) { + if (i == 0) { + anno = "NewGen "; + } else if (i == 1) { + anno = "OldGen "; + } else { + anno = "Gen " + i + " "; + } + bad = false; + break; + } + } + + if (bad) { + // Try perm gen + if (heap.permGen().isIn(handle)) { + anno = "PermGen "; + bad = false; + } + } + } else if (collHeap instanceof ParallelScavengeHeap) { + ParallelScavengeHeap heap = (ParallelScavengeHeap) collHeap; + if (heap.youngGen().isIn(handle)) { + anno = "PSYoungGen "; + bad = false; + } else if (heap.oldGen().isIn(handle)) { + anno = "PSOldGen "; + bad = false; + } else if (heap.permGen().isIn(handle)) { + anno = "PSPermGen "; + bad = false; + } + } else { + // Optimistically assume the oop isn't bad + anno = "[Unknown generation] "; + bad = false; + } + + if (!bad) { + try { + Oop oop = VM.getVM().getObjectHeap().newOop(handle); + if (oop instanceof Instance) { + // Java-level objects always have workable names + anno = anno + oop.getKlass().getName().asString(); + } else { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + Oop.printOopValueOn(oop, new PrintStream(bos)); + anno = anno + bos.toString(); + } + } + catch (AddressException e) { + anno += "CORRUPT OOP"; + } + catch (NullPointerException e) { + anno += "CORRUPT OOP (null pointer)"; + } + } + } + + annoPanel.addAnnotation(new Annotation(addr, addr.addOffsetTo(addressSize), anno)); + } + }, rm); + } catch (Exception e) { + System.err.println("Error while performing oopsDo for frame " + curFrame); + e.printStackTrace(); + } + } + + vf = nextVFrame; + } + + // This used to paint as we walked the frames. This caused the display to be refreshed + // enough to be annoying on remote displays. It also would cause the annotations to + // be displayed in varying order which caused some annotations to overwrite others + // depending on the races between painting and adding annotations. This latter problem + // still exists to some degree but moving this code here definitely seems to reduce it + annoPanel.makeVisible(startAddr); + annoPanel.repaint(); + } + }); + } + + /** NOTE we are in a different thread here than either the main + thread or the Swing/AWT event handler thread, so we must be very + careful when creating or removing widgets */ + private void attach(String pidText) { + try { + this.pidText = pidText; + pid = Integer.parseInt(pidText); + } + catch (NumberFormatException e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + setMenuItemsEnabled(attachMenuItems, true); + JOptionPane.showInternalMessageDialog(desktop, + "Unable to parse process ID \"" + HSDB.this.pidText + "\".\nPlease enter a number.", + "Parse error", + JOptionPane.WARNING_MESSAGE); + } + }); + return; + } + + // Try to attach to this process + Runnable remover = new Runnable() { + public void run() { + attachWaitDialog.setVisible(false); + desktop.remove(attachWaitDialog); + attachWaitDialog = null; + } + }; + + try { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JOptionPane pane = new JOptionPane("Attaching to process " + pid + ", please wait...", JOptionPane.INFORMATION_MESSAGE); + pane.setOptions(new Object[] {}); + attachWaitDialog = pane.createInternalFrame(desktop, "Attaching to Process"); + attachWaitDialog.show(); + } + }); + + // FIXME: display exec'd debugger's output messages during this + // lengthy call + agent.attach(pid); + if (agent.getDebugger().hasConsole()) { + showDbgConsoleMenuItem.setEnabled(true); + } + attached = true; + SwingUtilities.invokeLater(remover); + } + catch (DebuggerException e) { + SwingUtilities.invokeLater(remover); + final String errMsg = formatMessage(e.getMessage(), 80); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + setMenuItemsEnabled(attachMenuItems, true); + JOptionPane.showInternalMessageDialog(desktop, + "Unable to connect to process ID " + pid + ":\n\n" + errMsg, + "Unable to Connect", + JOptionPane.WARNING_MESSAGE); + } + }); + agent.detach(); + return; + } + + // OK, the VM should be available. Create the Threads dialog. + showThreadsDialog(); + } + + /** NOTE we are in a different thread here than either the main + thread or the Swing/AWT event handler thread, so we must be very + careful when creating or removing widgets */ + private void attach(final String executablePath, final String corePath) { + // Try to open this core file + Runnable remover = new Runnable() { + public void run() { + attachWaitDialog.setVisible(false); + desktop.remove(attachWaitDialog); + attachWaitDialog = null; + } + }; + + try { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JOptionPane pane = new JOptionPane("Opening core file, please wait...", JOptionPane.INFORMATION_MESSAGE); + pane.setOptions(new Object[] {}); + attachWaitDialog = pane.createInternalFrame(desktop, "Opening Core File"); + attachWaitDialog.show(); + } + }); + + // FIXME: display exec'd debugger's output messages during this + // lengthy call + agent.attach(executablePath, corePath); + if (agent.getDebugger().hasConsole()) { + showDbgConsoleMenuItem.setEnabled(true); + } + attached = true; + SwingUtilities.invokeLater(remover); + } + catch (DebuggerException e) { + SwingUtilities.invokeLater(remover); + final String errMsg = formatMessage(e.getMessage(), 80); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + setMenuItemsEnabled(attachMenuItems, true); + JOptionPane.showInternalMessageDialog(desktop, + "Unable to open core file\n" + corePath + ":\n\n" + errMsg, + "Unable to Open Core File", + JOptionPane.WARNING_MESSAGE); + } + }); + agent.detach(); + return; + } + + // OK, the VM should be available. Create the Threads dialog. + showThreadsDialog(); + } + + /** NOTE we are in a different thread here than either the main + thread or the Swing/AWT event handler thread, so we must be very + careful when creating or removing widgets */ + private void connect(final String remoteMachineName) { + // Try to open this core file + Runnable remover = new Runnable() { + public void run() { + attachWaitDialog.setVisible(false); + desktop.remove(attachWaitDialog); + attachWaitDialog = null; + } + }; + + try { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JOptionPane pane = new JOptionPane("Connecting to debug server, please wait...", JOptionPane.INFORMATION_MESSAGE); + pane.setOptions(new Object[] {}); + attachWaitDialog = pane.createInternalFrame(desktop, "Connecting to Debug Server"); + attachWaitDialog.show(); + } + }); + + agent.attach(remoteMachineName); + if (agent.getDebugger().hasConsole()) { + showDbgConsoleMenuItem.setEnabled(true); + } + attached = true; + SwingUtilities.invokeLater(remover); + } + catch (DebuggerException e) { + SwingUtilities.invokeLater(remover); + final String errMsg = formatMessage(e.getMessage(), 80); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + setMenuItemsEnabled(attachMenuItems, true); + JOptionPane.showInternalMessageDialog(desktop, + "Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg, + "Unable to Connect", + JOptionPane.WARNING_MESSAGE); + } + }); + agent.detach(); + return; + } + + // OK, the VM should be available. Create the Threads dialog. + showThreadsDialog(); + } + + private void detachDebugger() { + if (!attached) { + return; + } + agent.detach(); + attached = false; + } + + private void detach() { + detachDebugger(); + attachWaitDialog = null; + threadsFrame = null; + consoleFrame = null; + setMenuItemsEnabled(attachMenuItems, true); + setMenuItemsEnabled(detachMenuItems, false); + toolsMenu.setEnabled(false); + showDbgConsoleMenuItem.setEnabled(false); + // FIXME: is this sufficient, or will I have to do anything else + // to the components to kill them off? What about WorkerThreads? + desktop.removeAll(); + desktop.invalidate(); + desktop.validate(); + desktop.repaint(); + } + + /** NOTE that this is called from another thread than the main or + Swing thread and we have to be careful about synchronization */ + private void showThreadsDialog() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + threadsFrame = new JInternalFrame("Java Threads"); + threadsFrame.setResizable(true); + threadsFrame.setIconifiable(true); + JavaThreadsPanel threadsPanel = new JavaThreadsPanel(); + threadsPanel.addPanelListener(HSDB.this); + threadsFrame.getContentPane().add(threadsPanel); + threadsFrame.setSize(500, 300); + threadsFrame.pack(); + desktop.add(threadsFrame); + GraphicsUtilities.moveToInContainer(threadsFrame, 0.75f, 0.25f, 0, 20); + threadsFrame.show(); + setMenuItemsEnabled(attachMenuItems, false); + setMenuItemsEnabled(detachMenuItems, true); + toolsMenu.setEnabled(true); + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + computeRevPtrsMenuItem.setEnabled(true); + } + }); + } + }); + } + + private void showObjectHistogram() { + sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram(); + ObjectHistogramCleanupThunk cleanup = + new ObjectHistogramCleanupThunk(histo); + doHeapIteration("Object Histogram", + "Generating histogram...", + histo, + cleanup); + } + + class ObjectHistogramCleanupThunk implements CleanupThunk { + sun.jvm.hotspot.oops.ObjectHistogram histo; + + ObjectHistogramCleanupThunk(sun.jvm.hotspot.oops.ObjectHistogram histo) { + this.histo = histo; + } + + public void heapIterationComplete() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JInternalFrame histoFrame = new JInternalFrame("Object Histogram"); + histoFrame.setResizable(true); + histoFrame.setClosable(true); + histoFrame.setIconifiable(true); + histoFrame.getContentPane().setLayout(new BorderLayout()); + ObjectHistogramPanel panel = new ObjectHistogramPanel(histo); + panel.addPanelListener(HSDB.this); + histoFrame.getContentPane().add(panel); + desktop.add(histoFrame); + GraphicsUtilities.reshapeToAspectRatio(histoFrame, 4.0f / 3.0f, 0.6f, + histoFrame.getParent().getSize()); + GraphicsUtilities.centerInContainer(histoFrame); + histoFrame.show(); + } + }); + } + } + + public void showObjectsOfType(Klass type) { + FindObjectByType finder = new FindObjectByType(type); + FindObjectByTypeCleanupThunk cleanup = + new FindObjectByTypeCleanupThunk(finder); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + type.printValueOn(new PrintStream(bos)); + String typeName = bos.toString(); + doHeapIteration("Show Objects Of Type", + "Finding instances of \"" + typeName + "\"", + finder, + cleanup); + } + + class FindObjectByTypeCleanupThunk implements CleanupThunk { + FindObjectByType finder; + + FindObjectByTypeCleanupThunk(FindObjectByType finder) { + this.finder = finder; + } + + public void heapIterationComplete() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JInternalFrame finderFrame = new JInternalFrame("Show Objects of Type"); + finderFrame.getContentPane().setLayout(new BorderLayout()); + finderFrame.setResizable(true); + finderFrame.setClosable(true); + finderFrame.setIconifiable(true); + ObjectListPanel panel = new ObjectListPanel(finder.getResults(), + new HeapProgress("Reverse Pointers Analysis")); + panel.addPanelListener(HSDB.this); + finderFrame.getContentPane().add(panel); + desktop.add(finderFrame); + GraphicsUtilities.reshapeToAspectRatio(finderFrame, 4.0f / 3.0f, 0.6f, + finderFrame.getParent().getSize()); + GraphicsUtilities.centerInContainer(finderFrame); + finderFrame.show(); + } + }); + } + } + + private void showDebuggerConsole() { + if (consoleFrame == null) { + consoleFrame = new JInternalFrame("Debugger Console"); + consoleFrame.setResizable(true); + consoleFrame.setClosable(true); + consoleFrame.setIconifiable(true); + consoleFrame.getContentPane().setLayout(new BorderLayout()); + consoleFrame.getContentPane().add(new DebuggerConsolePanel(agent.getDebugger()), BorderLayout.CENTER); + GraphicsUtilities.reshapeToAspectRatio(consoleFrame, 5.0f, 0.9f, desktop.getSize()); + } + if (consoleFrame.getParent() == null) { + desktop.add(consoleFrame); + } + consoleFrame.setVisible(true); + consoleFrame.show(); + consoleFrame.getContentPane().getComponent(0).requestFocus(); + } + + private void showConsole() { + CommandProcessor.DebuggerInterface di = new CommandProcessor.DebuggerInterface() { + public HotSpotAgent getAgent() { + return agent; + } + public boolean isAttached() { + return attached; + } + public void attach(String pid) { + attach(pid); + } + public void attach(String java, String core) { + } + public void detach() { + detachDebugger(); + } + public void reattach() { + if (attached) { + detachDebugger(); + } + if (pidText != null) { + attach(pidText); + } else { + attach(execPath, coreFilename); + } + } + }; + + showPanel("Command Line", new CommandProcessorPanel(new CommandProcessor(di, null, null, null))); + } + + private void showFindByQueryPanel() { + showPanel("Find Object by Query", new FindByQueryPanel()); + } + + private void showFindPanel() { + showPanel("Find Pointer", new FindPanel()); + } + + private void showFindInHeapPanel() { + showPanel("Find Address In Heap", new FindInHeapPanel()); + } + + private void showFindInCodeCachePanel() { + showPanel("Find Address In Code Cache", new FindInCodeCachePanel()); + } + + private void showHeapParametersPanel() { + showPanel("Heap Parameters", new HeapParametersPanel()); + } + + public void showThreadInfo(final JavaThread thread) { + showPanel("Info for " + thread.getThreadName(), new ThreadInfoPanel(thread)); + } + + public void showJavaStackTrace(final JavaThread thread) { + JavaStackTracePanel jstp = new JavaStackTracePanel(); + showPanel("Java stack trace for " + thread.getThreadName(), jstp); + jstp.setJavaThread(thread); + } + + private void showDeadlockDetectionPanel() { + showPanel("Deadlock Detection", new DeadlockDetectionPanel()); + } + + private void showMonitorCacheDumpPanel() { + showPanel("Monitor Cache Dump", new MonitorCacheDumpPanel()); + } + + public void showClassBrowser() { + final JInternalFrame progressFrame = new JInternalFrame("Class Browser"); + progressFrame.setResizable(true); + progressFrame.setClosable(true); + progressFrame.setIconifiable(true); + progressFrame.getContentPane().setLayout(new BorderLayout()); + final ProgressBarPanel bar = new ProgressBarPanel("Generating class list .."); + bar.setIndeterminate(true); + progressFrame.getContentPane().add(bar, BorderLayout.CENTER); + desktop.add(progressFrame); + progressFrame.pack(); + GraphicsUtilities.centerInContainer(progressFrame); + progressFrame.show(); + + workerThread.invokeLater(new Runnable() { + public void run() { + HTMLGenerator htmlGen = new HTMLGenerator(); + InstanceKlass[] klasses = SystemDictionaryHelper.getAllInstanceKlasses(); + final String htmlText = htmlGen.genHTMLForKlassNames(klasses); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JInternalFrame cbFrame = new JInternalFrame("Class Browser"); + cbFrame.getContentPane().setLayout(new BorderLayout()); + cbFrame.setResizable(true); + cbFrame.setClosable(true); + cbFrame.setIconifiable(true); + ClassBrowserPanel cbPanel = new ClassBrowserPanel(); + cbFrame.getContentPane().add(cbPanel, BorderLayout.CENTER); + desktop.remove(progressFrame); + desktop.repaint(); + desktop.add(cbFrame); + GraphicsUtilities.reshapeToAspectRatio(cbFrame, 1.25f, 0.85f, + cbFrame.getParent().getSize()); + cbFrame.show(); + cbPanel.setClassesText(htmlText); + } + }); + } + }); + } + + public void showCodeViewer() { + showPanel("Code Viewer", new CodeViewerPanel(), 1.25f, 0.85f); + } + + public void showCodeViewer(final Address address) { + final CodeViewerPanel panel = new CodeViewerPanel(); + showPanel("Code Viewer", panel, 1.25f, 0.85f); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + panel.viewAddress(address); + } + }); + + } + + public void showMemoryViewer() { + showPanel("Memory Viewer", new MemoryViewer(agent.getDebugger(), agent.getTypeDataBase().getAddressSize() == 8)); + } + + public void showCommandLineFlags() { + showPanel("Command Line Flags", new VMFlagsPanel()); + } + + public void showVMVersion() { + showPanel("VM Version Info", new VMVersionInfoPanel()); + } + + public void showSystemProperties() { + showPanel("System Properties", new SysPropsPanel()); + } + + private void showPanel(String name, JPanel panel) { + showPanel(name, panel, 5.0f / 3.0f, 0.4f); + } + + private void showPanel(String name, JPanel panel, float aspectRatio, float fillRatio) { + JInternalFrame frame = new JInternalFrame(name); + frame.getContentPane().setLayout(new BorderLayout()); + frame.setResizable(true); + frame.setClosable(true); + frame.setIconifiable(true); + frame.setMaximizable(true); + frame.getContentPane().add(panel, BorderLayout.CENTER); + desktop.add(frame); + GraphicsUtilities.reshapeToAspectRatio(frame, aspectRatio, fillRatio, frame.getParent().getSize()); + GraphicsUtilities.randomLocation(frame); + frame.show(); + if (panel instanceof SAPanel) { + ((SAPanel)panel).addPanelListener(this); + } + } + + //-------------------------------------------------------------------------------- + // Framework for heap iteration with progress bar + // + + interface CleanupThunk { + public void heapIterationComplete(); + } + + class HeapProgress implements HeapProgressThunk { + private JInternalFrame frame; + private ProgressBarPanel bar; + private String windowTitle; + private String progressBarTitle; + private CleanupThunk cleanup; + + HeapProgress(String windowTitle) { + this(windowTitle, "Percentage of heap visited", null); + } + + HeapProgress(String windowTitle, String progressBarTitle) { + this(windowTitle, progressBarTitle, null); + } + + HeapProgress(String windowTitle, String progressBarTitle, CleanupThunk cleanup) { + this.windowTitle = windowTitle; + this.progressBarTitle = progressBarTitle; + this.cleanup = cleanup; + } + + public void heapIterationFractionUpdate(final double fractionOfHeapVisited) { + if (frame == null) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + frame = new JInternalFrame(windowTitle); + frame.setResizable(true); + frame.setIconifiable(true); + frame.getContentPane().setLayout(new BorderLayout()); + bar = new ProgressBarPanel(progressBarTitle); + frame.getContentPane().add(bar, BorderLayout.CENTER); + desktop.add(frame); + frame.pack(); + GraphicsUtilities.constrainToSize(frame, frame.getParent().getSize()); + GraphicsUtilities.centerInContainer(frame); + frame.show(); + } + }); + } + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + bar.setValue(fractionOfHeapVisited); + } + }); + } + + public void heapIterationComplete() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + desktop.remove(frame); + desktop.repaint(); + if (VM.getVM().getRevPtrs() != null) { + // Ended up computing reverse pointers as a side-effect + computeRevPtrsMenuItem.setEnabled(false); + } + } + }); + + if (cleanup != null) { + cleanup.heapIterationComplete(); + } + } + } + + class VisitHeap implements Runnable { + HeapVisitor visitor; + + VisitHeap(HeapVisitor visitor) { + this.visitor = visitor; + } + + public void run() { + VM.getVM().getObjectHeap().iterate(visitor); + } + } + + private void doHeapIteration(String frameTitle, + String progressBarText, + HeapVisitor visitor, + CleanupThunk cleanup) { + sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram(); + HeapProgress progress = new HeapProgress(frameTitle, + progressBarText, + cleanup); + HeapVisitor progVisitor = new ProgressiveHeapVisitor(visitor, progress); + workerThread.invokeLater(new VisitHeap(progVisitor)); + } + + //-------------------------------------------------------------------------------- + // Stack trace helper + // + + private static JavaVFrame getLastJavaVFrame(JavaThread cur) { + RegisterMap regMap = cur.newRegisterMap(true); + sun.jvm.hotspot.runtime.Frame f = cur.getCurrentFrameGuess(); + if (f == null) return null; + boolean imprecise = true; + if (f.isInterpretedFrame() && !f.isInterpretedFrameValid()) { + System.err.println("Correcting for invalid interpreter frame"); + f = f.sender(regMap); + imprecise = false; + } + VFrame vf = VFrame.newVFrame(f, regMap, cur, true, imprecise); + if (vf == null) { + System.err.println(" (Unable to create vframe for topmost frame guess)"); + return null; + } + if (vf.isJavaFrame()) { + return (JavaVFrame) vf; + } + return (JavaVFrame) vf.javaSender(); + } + + // Internal routine for debugging + private static void dumpStack(JavaThread cur) { + RegisterMap regMap = cur.newRegisterMap(true); + sun.jvm.hotspot.runtime.Frame f = cur.getCurrentFrameGuess(); + PrintStream tty = System.err; + while (f != null) { + tty.print("Found "); + if (f.isInterpretedFrame()) { tty.print("interpreted"); } + else if (f.isCompiledFrame()) { tty.print("compiled"); } + else if (f.isEntryFrame()) { tty.print("entry"); } + else if (f.isNativeFrame()) { tty.print("native"); } + else if (f.isGlueFrame()) { tty.print("glue"); } + else { tty.print("external"); } + tty.print(" frame with PC = " + f.getPC() + ", SP = " + f.getSP() + ", FP = " + f.getFP()); + if (f.isSignalHandlerFrameDbg()) { + tty.print(" (SIGNAL HANDLER)"); + } + tty.println(); + + if (!f.isFirstFrame()) { + f = f.sender(regMap); + } else { + f = null; + } + } + } + + //-------------------------------------------------------------------------------- + // Component utilities + // + + private static JMenuItem createMenuItem(String name, ActionListener l) { + JMenuItem item = new JMenuItem(name); + item.addActionListener(l); + return item; + } + + /** Punctuates the given string with \n's where necessary to not + exceed the given number of characters per line. Strips + extraneous whitespace. */ + private String formatMessage(String message, int charsPerLine) { + StringBuffer buf = new StringBuffer(message.length()); + StringTokenizer tokenizer = new StringTokenizer(message); + int curLineLength = 0; + while (tokenizer.hasMoreTokens()) { + String tok = tokenizer.nextToken(); + if (curLineLength + tok.length() > charsPerLine) { + buf.append('\n'); + curLineLength = 0; + } else { + if (curLineLength != 0) { + buf.append(' '); + ++curLineLength; + } + } + buf.append(tok); + curLineLength += tok.length(); + } + return buf.toString(); + } + + private void setMenuItemsEnabled(java.util.List items, boolean enabled) { + for (Iterator iter = items.iterator(); iter.hasNext(); ) { + ((JMenuItem) iter.next()).setEnabled(enabled); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/HelloWorld.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HelloWorld.java new file mode 100644 index 00000000000..791f48a0d72 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HelloWorld.java @@ -0,0 +1,117 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import java.lang.reflect.*; + +public class HelloWorld { + private static String helloWorldString = "Hello, world!"; + private static volatile int helloWorldTrigger = 0; + private static final boolean useMethodInvoke = false; + private static Object lock = new Object(); + + public static void main(String[] args) { + int foo = a(); + + System.out.println("HelloWorld exiting. a() = " + foo); + } + + private static int a() { + return 1 + b(); + } + + private static int b() { + return 1 + c(); + } + + private static int c() { + return 1 + d("Hi"); + } + + private static int d(String x) { + System.out.println("HelloWorld.d() received \"" + x + "\" as argument"); + synchronized(lock) { + if (useMethodInvoke) { + try { + Method method = HelloWorld.class.getMethod("e", null); + Integer result = (Integer) method.invoke(null, new Object[0]); + return result.intValue(); + } + catch (Exception e) { + throw new RuntimeException(e.toString()); + } + } else { + + int i = fib(10); // 89 + long l = i; + float f = i; + double d = i; + char c = (char) i; + short s = (short) i; + byte b = (byte) i; + + int ret = e(); + + System.out.println("Tenth Fibonacci number in all formats: " + + i + ", " + + l + ", " + + f + ", " + + d + ", " + + c + ", " + + s + ", " + + b); + + return ret; + } + } + } + + public static int e() { + System.out.println("Going to sleep..."); + + int i = 0; + + while (helloWorldTrigger == 0) { + if (++i == 1000000) { + System.gc(); + } + } + + System.out.println(helloWorldString); + + while (helloWorldTrigger != 0) { + } + + return i; + } + + // Tree-recursive implementation for test + public static int fib(int n) { + if (n < 2) { + return 1; + } + return fib(n - 1) + fib(n - 2); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java new file mode 100644 index 00000000000..eef46d4e566 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java @@ -0,0 +1,653 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import java.io.PrintStream; +import java.net.*; +import java.rmi.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.dbx.*; +import sun.jvm.hotspot.debugger.proc.*; +import sun.jvm.hotspot.debugger.remote.*; +import sun.jvm.hotspot.debugger.win32.*; +import sun.jvm.hotspot.debugger.windbg.*; +import sun.jvm.hotspot.debugger.linux.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/**

This class wraps much of the basic functionality and is the + * highest-level factory for VM data structures. It makes it simple + * to start up the debugging system.

+ * + *

FIXME: need to add a way to configure the paths to dbx and the + * DSO from the outside. However, this should work for now for + * internal use.

+ * + *

FIXME: especially with the addition of remote debugging, this + * has turned into a mess; needs rethinking.

+ */ + +public class HotSpotAgent { + private JVMDebugger debugger; + private MachineDescription machDesc; + private TypeDataBase db; + + private String os; + private String cpu; + private String fileSep; + + // The system can work in several ways: + // - Attaching to local process + // - Attaching to local core file + // - Connecting to remote debug server + // - Starting debug server for process + // - Starting debug server for core file + + // These are options for the "client" side of things + private static final int PROCESS_MODE = 0; + private static final int CORE_FILE_MODE = 1; + private static final int REMOTE_MODE = 2; + private int startupMode; + + // This indicates whether we are really starting a server or not + private boolean isServer; + + // All possible required information for connecting + private int pid; + private String javaExecutableName; + private String coreFileName; + private String debugServerID; + + // All needed information for server side + private String serverID; + + private String[] jvmLibNames; + + // FIXME: make these configurable, i.e., via a dotfile; also + // consider searching within the JDK from which this Java executable + // comes to find them + private static final String defaultDbxPathPrefix = "/net/jano.sfbay/export/disk05/hotspot/sa"; + private static final String defaultDbxSvcAgentDSOPathPrefix = "/net/jano.sfbay/export/disk05/hotspot/sa"; + + static void showUsage() { + System.out.println(" You can also pass these -D options to java to specify where to find dbx and the \n" + + " Serviceability Agent plugin for dbx:"); + System.out.println(" -DdbxPathName=\n" + + " Default is derived from dbxPathPrefix"); + System.out.println(" or"); + System.out.println(" -DdbxPathPrefix=\n" + + " where xxx is the path name of a dir structure that contains:\n" + + " //bin/dbx\n" + + " The default is " + defaultDbxPathPrefix); + System.out.println(" and"); + System.out.println(" -DdbxSvcAgentDSOPathName=\n" + + " Default is determined from dbxSvcAgentDSOPathPrefix"); + System.out.println(" or"); + System.out.println(" -DdbxSvcAgentDSOPathPrefix=\n" + + " where xxx is the pathname of a dir structure that contains:\n" + + " //bin/lib/libsvc_agent_dbx.so\n" + + " The default is " + defaultDbxSvcAgentDSOPathPrefix); + } + + public HotSpotAgent() { + // for non-server add shutdown hook to clean-up debugger in case + // of forced exit. For remote server, shutdown hook is added by + // DebugServer. + Runtime.getRuntime().addShutdownHook(new java.lang.Thread( + new Runnable() { + public void run() { + synchronized (HotSpotAgent.this) { + if (!isServer) { + detach(); + } + } + } + })); + } + + //-------------------------------------------------------------------------------- + // Accessors (once the system is set up) + // + + public synchronized Debugger getDebugger() { + return debugger; + } + + public synchronized TypeDataBase getTypeDataBase() { + return db; + } + + //-------------------------------------------------------------------------------- + // Client-side operations + // + + /** This attaches to a process running on the local machine. */ + public synchronized void attach(int processID) + throws DebuggerException { + if (debugger != null) { + throw new DebuggerException("Already attached"); + } + pid = processID; + startupMode = PROCESS_MODE; + isServer = false; + go(); + } + + /** This opens a core file on the local machine */ + public synchronized void attach(String javaExecutableName, String coreFileName) + throws DebuggerException { + if (debugger != null) { + throw new DebuggerException("Already attached"); + } + if ((javaExecutableName == null) || (coreFileName == null)) { + throw new DebuggerException("Both the core file name and Java executable name must be specified"); + } + this.javaExecutableName = javaExecutableName; + this.coreFileName = coreFileName; + startupMode = CORE_FILE_MODE; + isServer = false; + go(); + } + + /** This attaches to a "debug server" on a remote machine; this + remote server has already attached to a process or opened a + core file and is waiting for RMI calls on the Debugger object to + come in. */ + public synchronized void attach(String remoteServerID) + throws DebuggerException { + if (debugger != null) { + throw new DebuggerException("Already attached to a process"); + } + if (remoteServerID == null) { + throw new DebuggerException("Debug server id must be specified"); + } + + debugServerID = remoteServerID; + startupMode = REMOTE_MODE; + isServer = false; + go(); + } + + /** This should only be called by the user on the client machine, + not the server machine */ + public synchronized boolean detach() throws DebuggerException { + if (isServer) { + throw new DebuggerException("Should not call detach() for server configuration"); + } + return detachInternal(); + } + + //-------------------------------------------------------------------------------- + // Server-side operations + // + + /** This attaches to a process running on the local machine and + starts a debug server, allowing remote machines to connect and + examine this process. Uses specified name to uniquely identify a + specific debuggee on the server */ + public synchronized void startServer(int processID, String uniqueID) { + if (debugger != null) { + throw new DebuggerException("Already attached"); + } + pid = processID; + startupMode = PROCESS_MODE; + isServer = true; + serverID = uniqueID; + go(); + } + + /** This attaches to a process running on the local machine and + starts a debug server, allowing remote machines to connect and + examine this process. */ + public synchronized void startServer(int processID) + throws DebuggerException { + startServer(processID, null); + } + + /** This opens a core file on the local machine and starts a debug + server, allowing remote machines to connect and examine this + core file. Uses supplied uniqueID to uniquely identify a specific + debugee */ + public synchronized void startServer(String javaExecutableName, + String coreFileName, + String uniqueID) { + if (debugger != null) { + throw new DebuggerException("Already attached"); + } + if ((javaExecutableName == null) || (coreFileName == null)) { + throw new DebuggerException("Both the core file name and Java executable name must be specified"); + } + this.javaExecutableName = javaExecutableName; + this.coreFileName = coreFileName; + startupMode = CORE_FILE_MODE; + isServer = true; + serverID = uniqueID; + go(); + } + + /** This opens a core file on the local machine and starts a debug + server, allowing remote machines to connect and examine this + core file. */ + public synchronized void startServer(String javaExecutableName, String coreFileName) + throws DebuggerException { + startServer(javaExecutableName, coreFileName, null); + } + + /** This may only be called on the server side after startServer() + has been called */ + public synchronized boolean shutdownServer() throws DebuggerException { + if (!isServer) { + throw new DebuggerException("Should not call shutdownServer() for client configuration"); + } + return detachInternal(); + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private boolean detachInternal() { + if (debugger == null) { + return false; + } + boolean retval = true; + if (!isServer) { + VM.shutdown(); + } + // We must not call detach() if we are a client and are connected + // to a remote debugger + Debugger dbg = null; + DebuggerException ex = null; + if (isServer) { + try { + RMIHelper.unbind(serverID); + } + catch (DebuggerException de) { + ex = de; + } + dbg = debugger; + } else { + if (startupMode != REMOTE_MODE) { + dbg = debugger; + } + } + if (dbg != null) { + retval = dbg.detach(); + } + + debugger = null; + machDesc = null; + db = null; + if (ex != null) { + throw(ex); + } + return retval; + } + + private void go() { + setupDebugger(); + setupVM(); + } + + private void setupDebugger() { + if (startupMode != REMOTE_MODE) { + // + // Local mode (client attaching to local process or setting up + // server, but not client attaching to server) + // + + try { + os = PlatformInfo.getOS(); + cpu = PlatformInfo.getCPU(); + } + catch (UnsupportedPlatformException e) { + throw new DebuggerException(e); + } + fileSep = System.getProperty("file.separator"); + + if (os.equals("solaris")) { + setupDebuggerSolaris(); + } else if (os.equals("win32")) { + setupDebuggerWin32(); + } else if (os.equals("linux")) { + setupDebuggerLinux(); + } else { + // Add support for more operating systems here + throw new DebuggerException("Operating system " + os + " not yet supported"); + } + + if (isServer) { + RemoteDebuggerServer remote = null; + try { + remote = new RemoteDebuggerServer(debugger); + } + catch (RemoteException rem) { + throw new DebuggerException(rem); + } + RMIHelper.rebind(serverID, remote); + } + } else { + // + // Remote mode (client attaching to server) + // + + // Create and install a security manager + + // FIXME: currently commented out because we were having + // security problems since we're "in the sun.* hierarchy" here. + // Perhaps a permissive policy file would work around this. In + // the long run, will probably have to move into com.sun.*. + + // if (System.getSecurityManager() == null) { + // System.setSecurityManager(new RMISecurityManager()); + // } + + connectRemoteDebugger(); + } + } + + private void setupVM() { + // We need to instantiate a HotSpotTypeDataBase on both the client + // and server machine. On the server it is only currently used to + // configure the Java primitive type sizes (which we should + // consider making constant). On the client it is used to + // configure the VM. + + try { + if (os.equals("solaris")) { + db = new HotSpotTypeDataBase(machDesc, + new HotSpotSolarisVtblAccess(debugger, jvmLibNames), + debugger, jvmLibNames); + } else if (os.equals("win32")) { + db = new HotSpotTypeDataBase(machDesc, + new Win32VtblAccess(debugger, jvmLibNames), + debugger, jvmLibNames); + } else if (os.equals("linux")) { + db = new HotSpotTypeDataBase(machDesc, + new LinuxVtblAccess(debugger, jvmLibNames), + debugger, jvmLibNames); + } else { + throw new DebuggerException("OS \"" + os + "\" not yet supported (no VtblAccess yet)"); + } + } + catch (NoSuchSymbolException e) { + throw new DebuggerException("Doesn't appear to be a HotSpot VM (could not find symbol \"" + + e.getSymbol() + "\" in remote process)"); + } + + if (startupMode != REMOTE_MODE) { + // Configure the debugger with the primitive type sizes just obtained from the VM + debugger.configureJavaPrimitiveTypeSizes(db.getJBooleanType().getSize(), + db.getJByteType().getSize(), + db.getJCharType().getSize(), + db.getJDoubleType().getSize(), + db.getJFloatType().getSize(), + db.getJIntType().getSize(), + db.getJLongType().getSize(), + db.getJShortType().getSize()); + } + + if (!isServer) { + // Do not initialize the VM on the server (unnecessary, since it's + // instantiated on the client) + try { + VM.initialize(db, debugger); + } catch (DebuggerException e) { + throw (e); + } catch (Exception e) { + throw new DebuggerException(e); + } + } + } + + //-------------------------------------------------------------------------------- + // OS-specific debugger setup/connect routines + // + + // + // Solaris + // + + private void setupDebuggerSolaris() { + setupJVMLibNamesSolaris(); + if(System.getProperty("sun.jvm.hotspot.debugger.useProcDebugger") != null) { + ProcDebuggerLocal dbg = new ProcDebuggerLocal(null, true); + debugger = dbg; + attachDebugger(); + + // Set up CPU-dependent stuff + if (cpu.equals("x86")) { + machDesc = new MachineDescriptionIntelX86(); + } else if (cpu.equals("sparc")) { + int addressSize = dbg.getRemoteProcessAddressSize(); + if (addressSize == -1) { + throw new DebuggerException("Error occurred while trying to determine the remote process's " + + "address size"); + } + + if (addressSize == 32) { + machDesc = new MachineDescriptionSPARC32Bit(); + } else if (addressSize == 64) { + machDesc = new MachineDescriptionSPARC64Bit(); + } else { + throw new DebuggerException("Address size " + addressSize + " is not supported on SPARC"); + } + } else if (cpu.equals("amd64")) { + machDesc = new MachineDescriptionAMD64(); + } else { + throw new DebuggerException("Solaris only supported on sparc/sparcv9/x86/amd64"); + } + + dbg.setMachineDescription(machDesc); + return; + + } else { + String dbxPathName; + String dbxPathPrefix; + String dbxSvcAgentDSOPathName; + String dbxSvcAgentDSOPathPrefix; + String[] dbxSvcAgentDSOPathNames = null; + + // use path names/prefixes specified on command + dbxPathName = System.getProperty("dbxPathName"); + if (dbxPathName == null) { + dbxPathPrefix = System.getProperty("dbxPathPrefix"); + if (dbxPathPrefix == null) { + dbxPathPrefix = defaultDbxPathPrefix; + } + dbxPathName = dbxPathPrefix + fileSep + os + fileSep + cpu + fileSep + "bin" + fileSep + "dbx"; + } + + dbxSvcAgentDSOPathName = System.getProperty("dbxSvcAgentDSOPathName"); + if (dbxSvcAgentDSOPathName != null) { + dbxSvcAgentDSOPathNames = new String[] { dbxSvcAgentDSOPathName } ; + } else { + dbxSvcAgentDSOPathPrefix = System.getProperty("dbxSvcAgentDSOPathPrefix"); + if (dbxSvcAgentDSOPathPrefix == null) { + dbxSvcAgentDSOPathPrefix = defaultDbxSvcAgentDSOPathPrefix; + } + if (cpu.equals("sparc")) { + dbxSvcAgentDSOPathNames = new String[] { + // FIXME: bad hack for SPARC v9. This is necessary because + // there are two dbx executables on SPARC, one for v8 and one + // for v9, and it isn't obvious how to tell the two apart + // using the dbx command line. See + // DbxDebuggerLocal.importDbxModule(). + dbxSvcAgentDSOPathPrefix + fileSep + os + fileSep + cpu + "v9" + fileSep + "lib" + + fileSep + "libsvc_agent_dbx.so", + dbxSvcAgentDSOPathPrefix + fileSep + os + fileSep + cpu + fileSep + "lib" + + fileSep + "libsvc_agent_dbx.so", + }; + } else { + dbxSvcAgentDSOPathNames = new String[] { + dbxSvcAgentDSOPathPrefix + fileSep + os + fileSep + cpu + fileSep + "lib" + + fileSep + "libsvc_agent_dbx.so" + }; + } + } + + // Note we do not use a cache for the local debugger in server + // mode; it's taken care of on the client side + DbxDebuggerLocal dbg = new DbxDebuggerLocal(null, dbxPathName, dbxSvcAgentDSOPathNames, !isServer); + debugger = dbg; + + attachDebugger(); + + // Set up CPU-dependent stuff + if (cpu.equals("x86")) { + machDesc = new MachineDescriptionIntelX86(); + } else if (cpu.equals("sparc")) { + int addressSize = dbg.getRemoteProcessAddressSize(); + if (addressSize == -1) { + throw new DebuggerException("Error occurred while trying to determine the remote process's " + + "address size. It's possible that the Serviceability Agent's dbx module failed to " + + "initialize. Examine the standard output and standard error streams from the dbx " + + "process for more information."); + } + + if (addressSize == 32) { + machDesc = new MachineDescriptionSPARC32Bit(); + } else if (addressSize == 64) { + machDesc = new MachineDescriptionSPARC64Bit(); + } else { + throw new DebuggerException("Address size " + addressSize + " is not supported on SPARC"); + } + } + + dbg.setMachineDescription(machDesc); + + } + } + + private void connectRemoteDebugger() throws DebuggerException { + RemoteDebugger remote = + (RemoteDebugger) RMIHelper.lookup(debugServerID); + debugger = new RemoteDebuggerClient(remote); + machDesc = ((RemoteDebuggerClient) debugger).getMachineDescription(); + os = debugger.getOS(); + if (os.equals("solaris")) { + setupJVMLibNamesSolaris(); + } else if (os.equals("win32")) { + setupJVMLibNamesWin32(); + } else if (os.equals("linux")) { + setupJVMLibNamesLinux(); + } else { + throw new RuntimeException("Unknown OS type"); + } + + cpu = debugger.getCPU(); + } + + private void setupJVMLibNamesSolaris() { + jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so", "gamma_g" }; + } + + // + // Win32 + // + + private void setupDebuggerWin32() { + setupJVMLibNamesWin32(); + + if (cpu.equals("x86")) { + machDesc = new MachineDescriptionIntelX86(); + } else if (cpu.equals("amd64")) { + machDesc = new MachineDescriptionAMD64(); + } else if (cpu.equals("ia64")) { + machDesc = new MachineDescriptionIA64(); + } else { + throw new DebuggerException("Win32 supported under x86, amd64 and ia64 only"); + } + + // Note we do not use a cache for the local debugger in server + // mode; it will be taken care of on the client side (once remote + // debugging is implemented). + + if (System.getProperty("sun.jvm.hotspot.debugger.useWindbgDebugger") != null) { + debugger = new WindbgDebuggerLocal(machDesc, !isServer); + } else { + debugger = new Win32DebuggerLocal(machDesc, !isServer); + } + + attachDebugger(); + + // FIXME: add support for server mode + } + + private void setupJVMLibNamesWin32() { + jvmLibNames = new String[] { "jvm.dll", "jvm_g.dll" }; + } + + // + // Linux + // + + private void setupDebuggerLinux() { + setupJVMLibNamesLinux(); + + if (cpu.equals("x86")) { + machDesc = new MachineDescriptionIntelX86(); + } else if (cpu.equals("ia64")) { + machDesc = new MachineDescriptionIA64(); + } else if (cpu.equals("amd64")) { + machDesc = new MachineDescriptionAMD64(); + } else if (cpu.equals("sparc")) { + if (LinuxDebuggerLocal.getAddressSize()==8) { + machDesc = new MachineDescriptionSPARC64Bit(); + } else { + machDesc = new MachineDescriptionSPARC32Bit(); + } + } else { + throw new DebuggerException("Linux only supported on x86/ia64/amd64/sparc/sparc64"); + } + + LinuxDebuggerLocal dbg = + new LinuxDebuggerLocal(machDesc, !isServer); + debugger = dbg; + + attachDebugger(); + } + + private void setupJVMLibNamesLinux() { + jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" }; + } + + /** Convenience routine which should be called by per-platform + debugger setup. Should not be called when startupMode is + REMOTE_MODE. */ + private void attachDebugger() { + if (startupMode == PROCESS_MODE) { + debugger.attach(pid); + } else if (startupMode == CORE_FILE_MODE) { + debugger.attach(javaExecutableName, coreFileName); + } else { + throw new DebuggerException("Should not call attach() for startupMode == " + startupMode); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotSolarisVtblAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotSolarisVtblAccess.java new file mode 100644 index 00000000000..2f462a850df --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotSolarisVtblAccess.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.types.basic.*; + +/** This class implements the compiler-specific access to the vtbl for + a given C++ type. */ +public class HotSpotSolarisVtblAccess extends BasicVtblAccess { + + public HotSpotSolarisVtblAccess(SymbolLookup symbolLookup, + String[] jvmLibNames) { + super(symbolLookup, jvmLibNames); + } + + protected String vtblSymbolForType(Type type) { + String demangledSymbol = type.getName() + "::__vtbl"; + return mangle(demangledSymbol); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private String mangle(String symbol) { + String[] parts = symbol.split("::"); + StringBuffer mangled = new StringBuffer("__1c"); + for (int i = 0; i < parts.length; i++) { + int len = parts[i].length(); + if (len >= 26) { + mangled.append((char)('a' + (len / 26))); + len = len % 26; + } + mangled.append((char)('A' + len)); + mangled.append(parts[i]); + } + mangled.append("_"); + return mangled.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotTypeDataBase.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotTypeDataBase.java new file mode 100644 index 00000000000..b8f263db638 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotTypeDataBase.java @@ -0,0 +1,616 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.types.basic.*; +import sun.jvm.hotspot.utilities.*; + +/**

This is the cross-platform TypeDataBase used by the Oop + hierarchy. The decision was made to make this cross-platform by + having the VM export the necessary symbols via a built-in table; + see src/share/vm/runtime/vmStructs.[ch]pp for more details.

+ +

WARNING: clients should refer to this class through the + TypeDataBase interface and not directly to the HotSpotTypeDataBase + type.

+ +

NOTE: since we are fetching the sizes of the Java primitive types + */ + +public class HotSpotTypeDataBase extends BasicTypeDataBase { + private Debugger symbolLookup; + private String[] jvmLibNames; + private static final int UNINITIALIZED_SIZE = -1; + private static final int C_INT8_SIZE = 1; + private static final int C_INT32_SIZE = 4; + private static final int C_INT64_SIZE = 8; + + private static final boolean DEBUG; + static { + DEBUG = System.getProperty("sun.jvm.hotspot.HotSpotTypeDataBase.DEBUG") + != null; + } + + /**

This requires a SymbolLookup mechanism as well as the + MachineDescription. Note that we do not need a NameMangler since + we use the vmStructs mechanism to avoid looking up C++ + symbols.

+ +

NOTE that it is guaranteed that this constructor will not + attempt to fetch any Java values from the remote process, only C + integers and addresses. This is required because we are fetching + the sizes of the Java primitive types from the remote process, + implying that attempting to fetch them before their sizes are + known is illegal.

+ +

Throws NoSuchSymbolException if a problem occurred while + looking up one of the bootstrapping symbols related to the + VMStructs table in the remote VM; this may indicate that the + remote process is not actually a HotSpot VM.

+ */ + public HotSpotTypeDataBase(MachineDescription machDesc, + VtblAccess vtblAccess, + Debugger symbolLookup, + String[] jvmLibNames) throws NoSuchSymbolException { + super(machDesc, vtblAccess); + this.symbolLookup = symbolLookup; + this.jvmLibNames = jvmLibNames; + + readVMTypes(); + initializePrimitiveTypes(); + readVMStructs(); + readVMIntConstants(); + readVMLongConstants(); + } + + private void readVMTypes() { + // Get the variables we need in order to traverse the VMTypeEntry[] + long typeEntryTypeNameOffset; + long typeEntrySuperclassNameOffset; + long typeEntryIsOopTypeOffset; + long typeEntryIsIntegerTypeOffset; + long typeEntryIsUnsignedOffset; + long typeEntrySizeOffset; + long typeEntryArrayStride; + + typeEntryTypeNameOffset = getLongValueFromProcess("gHotSpotVMTypeEntryTypeNameOffset"); + typeEntrySuperclassNameOffset = getLongValueFromProcess("gHotSpotVMTypeEntrySuperclassNameOffset"); + typeEntryIsOopTypeOffset = getLongValueFromProcess("gHotSpotVMTypeEntryIsOopTypeOffset"); + typeEntryIsIntegerTypeOffset = getLongValueFromProcess("gHotSpotVMTypeEntryIsIntegerTypeOffset"); + typeEntryIsUnsignedOffset = getLongValueFromProcess("gHotSpotVMTypeEntryIsUnsignedOffset"); + typeEntrySizeOffset = getLongValueFromProcess("gHotSpotVMTypeEntrySizeOffset"); + typeEntryArrayStride = getLongValueFromProcess("gHotSpotVMTypeEntryArrayStride"); + + // Fetch the address of the VMTypeEntry* + Address entryAddr = lookupInProcess("gHotSpotVMTypes"); + // System.err.println("gHotSpotVMTypes address = " + entryAddr); + // Dereference this once to get the pointer to the first VMTypeEntry + // dumpMemory(entryAddr, 80); + entryAddr = entryAddr.getAddressAt(0); + + if (entryAddr == null) { + throw new RuntimeException("gHotSpotVMTypes was not initialized properly in the remote process; can not continue"); + } + + // Start iterating down it until we find an entry with no name + Address typeNameAddr = null; + do { + // Fetch the type name first + typeNameAddr = entryAddr.getAddressAt(typeEntryTypeNameOffset); + if (typeNameAddr != null) { + String typeName = CStringUtilities.getString(typeNameAddr); + + String superclassName = null; + Address superclassNameAddr = entryAddr.getAddressAt(typeEntrySuperclassNameOffset); + if (superclassNameAddr != null) { + superclassName = CStringUtilities.getString(superclassNameAddr); + } + + boolean isOopType = (entryAddr.getCIntegerAt(typeEntryIsOopTypeOffset, C_INT32_SIZE, false) != 0); + boolean isIntegerType = (entryAddr.getCIntegerAt(typeEntryIsIntegerTypeOffset, C_INT32_SIZE, false) != 0); + boolean isUnsigned = (entryAddr.getCIntegerAt(typeEntryIsUnsignedOffset, C_INT32_SIZE, false) != 0); + long size = entryAddr.getCIntegerAt(typeEntrySizeOffset, C_INT64_SIZE, true); + + createType(typeName, superclassName, isOopType, isIntegerType, isUnsigned, size); + } + + entryAddr = entryAddr.addOffsetTo(typeEntryArrayStride); + } while (typeNameAddr != null); + } + + private void initializePrimitiveTypes() { + // Look up the needed primitive types by name...they had better be present + setJBooleanType(lookupPrimitiveType("jboolean")); + setJByteType (lookupPrimitiveType("jbyte")); + setJCharType (lookupPrimitiveType("jchar")); + setJDoubleType (lookupPrimitiveType("jdouble")); + setJFloatType (lookupPrimitiveType("jfloat")); + setJIntType (lookupPrimitiveType("jint")); + setJLongType (lookupPrimitiveType("jlong")); + setJShortType (lookupPrimitiveType("jshort")); + + // Indicate that these are the Java primitive types + ((BasicType) getJBooleanType()).setIsJavaPrimitiveType(true); + ((BasicType) getJByteType()).setIsJavaPrimitiveType(true); + ((BasicType) getJCharType()).setIsJavaPrimitiveType(true); + ((BasicType) getJDoubleType()).setIsJavaPrimitiveType(true); + ((BasicType) getJFloatType()).setIsJavaPrimitiveType(true); + ((BasicType) getJIntType()).setIsJavaPrimitiveType(true); + ((BasicType) getJLongType()).setIsJavaPrimitiveType(true); + ((BasicType) getJShortType()).setIsJavaPrimitiveType(true); + } + + private Type lookupPrimitiveType(String typeName) { + Type type = lookupType(typeName, false); + if (type == null) { + throw new RuntimeException("Error initializing the HotSpotDataBase: could not find the primitive type \"" + + typeName + "\" in the remote VM's VMStructs table. This type is required in " + + "order to determine the size of Java primitive types. Can not continue."); + } + return type; + } + + private void readVMStructs() { + // Get the variables we need in order to traverse the VMStructEntry[] + long structEntryTypeNameOffset; + long structEntryFieldNameOffset; + long structEntryTypeStringOffset; + long structEntryIsStaticOffset; + long structEntryOffsetOffset; + long structEntryAddressOffset; + long structEntryArrayStride; + + structEntryTypeNameOffset = getLongValueFromProcess("gHotSpotVMStructEntryTypeNameOffset"); + structEntryFieldNameOffset = getLongValueFromProcess("gHotSpotVMStructEntryFieldNameOffset"); + structEntryTypeStringOffset = getLongValueFromProcess("gHotSpotVMStructEntryTypeStringOffset"); + structEntryIsStaticOffset = getLongValueFromProcess("gHotSpotVMStructEntryIsStaticOffset"); + structEntryOffsetOffset = getLongValueFromProcess("gHotSpotVMStructEntryOffsetOffset"); + structEntryAddressOffset = getLongValueFromProcess("gHotSpotVMStructEntryAddressOffset"); + structEntryArrayStride = getLongValueFromProcess("gHotSpotVMStructEntryArrayStride"); + + // Fetch the address of the VMStructEntry* + Address entryAddr = lookupInProcess("gHotSpotVMStructs"); + // Dereference this once to get the pointer to the first VMStructEntry + entryAddr = entryAddr.getAddressAt(0); + if (entryAddr == null) { + throw new RuntimeException("gHotSpotVMStructs was not initialized properly in the remote process; can not continue"); + } + + // Start iterating down it until we find an entry with no name + Address fieldNameAddr = null; + String typeName = null; + String fieldName = null; + String typeString = null; + boolean isStatic = false; + long offset = 0; + Address staticFieldAddr = null; + long size = 0; + long index = 0; + String opaqueName = ""; + lookupOrCreateClass(opaqueName, false, false, false); + + do { + // Fetch the field name first + fieldNameAddr = entryAddr.getAddressAt(structEntryFieldNameOffset); + if (fieldNameAddr != null) { + fieldName = CStringUtilities.getString(fieldNameAddr); + + // Now the rest of the names. Keep in mind that the type name + // may be NULL, indicating that the type is opaque. + Address addr = entryAddr.getAddressAt(structEntryTypeNameOffset); + if (addr == null) { + throw new RuntimeException("gHotSpotVMStructs unexpectedly had a NULL type name at index " + index); + } + typeName = CStringUtilities.getString(addr); + + addr = entryAddr.getAddressAt(structEntryTypeStringOffset); + if (addr == null) { + typeString = opaqueName; + } else { + typeString = CStringUtilities.getString(addr); + } + + isStatic = !(entryAddr.getCIntegerAt(structEntryIsStaticOffset, C_INT32_SIZE, false) == 0); + if (isStatic) { + staticFieldAddr = entryAddr.getAddressAt(structEntryAddressOffset); + offset = 0; + } else { + offset = entryAddr.getCIntegerAt(structEntryOffsetOffset, C_INT64_SIZE, true); + staticFieldAddr = null; + } + + // The containing Type must already be in the database -- no exceptions + BasicType containingType = lookupOrFail(typeName); + + // The field's Type must already be in the database -- no exceptions + BasicType fieldType = lookupOrFail(typeString); + + // Create field by type + createField(containingType, fieldName, fieldType, + isStatic, offset, staticFieldAddr); + } + + ++index; + entryAddr = entryAddr.addOffsetTo(structEntryArrayStride); + } while (fieldNameAddr != null); + } + + private void readVMIntConstants() { + // Get the variables we need in order to traverse the VMIntConstantEntry[] + long intConstantEntryNameOffset; + long intConstantEntryValueOffset; + long intConstantEntryArrayStride; + + intConstantEntryNameOffset = getLongValueFromProcess("gHotSpotVMIntConstantEntryNameOffset"); + intConstantEntryValueOffset = getLongValueFromProcess("gHotSpotVMIntConstantEntryValueOffset"); + intConstantEntryArrayStride = getLongValueFromProcess("gHotSpotVMIntConstantEntryArrayStride"); + + // Fetch the address of the VMIntConstantEntry* + Address entryAddr = lookupInProcess("gHotSpotVMIntConstants"); + // Dereference this once to get the pointer to the first VMIntConstantEntry + entryAddr = entryAddr.getAddressAt(0); + if (entryAddr == null) { + throw new RuntimeException("gHotSpotVMIntConstants was not initialized properly in the remote process; can not continue"); + } + + // Start iterating down it until we find an entry with no name + Address nameAddr = null; + do { + // Fetch the type name first + nameAddr = entryAddr.getAddressAt(intConstantEntryNameOffset); + if (nameAddr != null) { + String name = CStringUtilities.getString(nameAddr); + int value = (int) entryAddr.getCIntegerAt(intConstantEntryValueOffset, C_INT32_SIZE, false); + + // Be a little resilient + Integer oldValue = lookupIntConstant(name, false); + if (oldValue == null) { + addIntConstant(name, value); + } else { + if (oldValue.intValue() != value) { + throw new RuntimeException("Error: the integer constant \"" + name + + "\" had its value redefined (old was " + oldValue + + ", new is " + value + ". Aborting."); + } else { + System.err.println("Warning: the int constant \"" + name + "\" (declared in the remote VM in VMStructs::localHotSpotVMIntConstants) " + + "had its value declared as " + value + " twice. Continuing."); + } + } + } + + entryAddr = entryAddr.addOffsetTo(intConstantEntryArrayStride); + } while (nameAddr != null); + } + + private void readVMLongConstants() { + // Get the variables we need in order to traverse the VMLongConstantEntry[] + long longConstantEntryNameOffset; + long longConstantEntryValueOffset; + long longConstantEntryArrayStride; + + longConstantEntryNameOffset = getLongValueFromProcess("gHotSpotVMLongConstantEntryNameOffset"); + longConstantEntryValueOffset = getLongValueFromProcess("gHotSpotVMLongConstantEntryValueOffset"); + longConstantEntryArrayStride = getLongValueFromProcess("gHotSpotVMLongConstantEntryArrayStride"); + + // Fetch the address of the VMLongConstantEntry* + Address entryAddr = lookupInProcess("gHotSpotVMLongConstants"); + // Dereference this once to get the pointer to the first VMLongConstantEntry + entryAddr = entryAddr.getAddressAt(0); + if (entryAddr == null) { + throw new RuntimeException("gHotSpotVMLongConstants was not initialized properly in the remote process; can not continue"); + } + + // Start iterating down it until we find an entry with no name + Address nameAddr = null; + do { + // Fetch the type name first + nameAddr = entryAddr.getAddressAt(longConstantEntryNameOffset); + if (nameAddr != null) { + String name = CStringUtilities.getString(nameAddr); + int value = (int) entryAddr.getCIntegerAt(longConstantEntryValueOffset, C_INT64_SIZE, true); + + // Be a little resilient + Long oldValue = lookupLongConstant(name, false); + if (oldValue == null) { + addLongConstant(name, value); + } else { + if (oldValue.longValue() != value) { + throw new RuntimeException("Error: the long constant \"" + name + + "\" had its value redefined (old was " + oldValue + + ", new is " + value + ". Aborting."); + } else { + System.err.println("Warning: the long constant \"" + name + "\" (declared in the remote VM in VMStructs::localHotSpotVMLongConstants) " + + "had its value declared as " + value + " twice. Continuing."); + } + } + } + + entryAddr = entryAddr.addOffsetTo(longConstantEntryArrayStride); + } while (nameAddr != null); + } + + private BasicType lookupOrFail(String typeName) { + BasicType type = (BasicType) lookupType(typeName, false); + if (type == null) { + throw new RuntimeException("Type \"" + typeName + "\", referenced in VMStructs::localHotSpotVMStructs in the remote VM, " + + "was not present in the remote VMStructs::localHotSpotVMTypes table (should have been caught " + + "in the debug build of that VM). Can not continue."); + } + return type; + } + + private long getLongValueFromProcess(String symbol) { + return lookupInProcess(symbol).getCIntegerAt(0, C_INT64_SIZE, true); + } + + private Address lookupInProcess(String symbol) throws NoSuchSymbolException { + // FIXME: abstract away the loadobject name + for (int i = 0; i < jvmLibNames.length; i++) { + Address addr = symbolLookup.lookup(jvmLibNames[i], symbol); + if (addr != null) { + return addr; + } + } + String errStr = "("; + for (int i = 0; i < jvmLibNames.length; i++) { + errStr += jvmLibNames[i]; + if (i < jvmLibNames.length - 1) { + errStr += ", "; + } + } + errStr += ")"; + throw new NoSuchSymbolException(symbol, + "Could not find symbol \"" + symbol + + "\" in any of the known library names " + + errStr); + } + + private BasicType lookupOrCreateClass(String typeName, boolean isOopType, + boolean isIntegerType, boolean isUnsigned) { + BasicType type = (BasicType) lookupType(typeName, false); + if (type == null) { + // Create a new type + type = createBasicType(typeName, isOopType, isIntegerType, isUnsigned); + } + return type; + } + + /** Creates a new BasicType, initializes its size to -1 so we can + test to ensure that all types' sizes are initialized by VMTypes, + and adds it to the database. Takes care of initializing integer + and oop types properly. */ + private BasicType createBasicType(String typeName, boolean isOopType, + boolean isIntegerType, boolean isUnsigned) { + + BasicType type = null; + + if (isIntegerType) { + type = new BasicCIntegerType(this, typeName, isUnsigned); + } else { + if (typeNameIsPointerType(typeName)) { + type = recursiveCreateBasicPointerType(typeName); + } else { + type = new BasicType(this, typeName); + } + + if (isOopType) { + // HACK: turn markOop into a C integer type. This allows + // proper handling of it in the Serviceability Agent. (FIXME + // -- consider doing something different here) + if (typeName.equals("markOop")) { + type = new BasicCIntegerType(this, typeName, true); + } else { + type.setIsOopType(true); + } + } + } + + type.setSize(UNINITIALIZED_SIZE); + addType(type); + return type; + } + + /** Recursively creates a PointerType from the string representation + of the type's name. Note that this currently needs some + workarounds due to incomplete information in the VMStructs + database. */ + private BasicPointerType recursiveCreateBasicPointerType(String typeName) { + String targetTypeName = typeName.substring(0, typeName.lastIndexOf('*')).trim(); + Type targetType = null; + if (typeNameIsPointerType(targetTypeName)) { + targetType = recursiveCreateBasicPointerType(targetTypeName); + } else { + targetType = lookupType(targetTypeName, false); + if (targetType == null) { + // Workaround for missing C integer types in database. + // Also looks like we can't throw an exception for other + // missing target types because there are some in old + // VMStructs tables that didn't have the target type declared. + // For this case, we create basic types that never get filled + // in. + + if (targetTypeName.equals("char") || + targetTypeName.equals("const char")) { + // We don't have a representation of const-ness of C types in the SA + BasicType basicTargetType = createBasicType(targetTypeName, false, true, false); + basicTargetType.setSize(1); + targetType = basicTargetType; + } else if (targetTypeName.equals("u_char")) { + BasicType basicTargetType = createBasicType(targetTypeName, false, true, true); + basicTargetType.setSize(1); + targetType = basicTargetType; + } else { + if (DEBUG) { + System.err.println("WARNING: missing target type \"" + targetTypeName + "\" for pointer type \"" + typeName + "\""); + } + targetType = createBasicType(targetTypeName, false, false, false); + } + } + } + return new BasicPointerType(this, typeName, targetType); + } + + private boolean typeNameIsPointerType(String typeName) { + int i = typeName.length() - 1; + while (i >= 0 && Character.isWhitespace(typeName.charAt(i))) { + --i; + } + if (i >= 0 && typeName.charAt(i) == '*') { + return true; + } + return false; + } + + public void createType(String typeName, String superclassName, + boolean isOopType, boolean isIntegerType, + boolean isUnsigned, long size) { + // See whether we have a superclass + BasicType superclass = null; + if (superclassName != null) { + // Fetch or create it (FIXME: would get oop types wrong if + // they had a hierarchy; consider using lookupOrFail) + superclass = lookupOrCreateClass(superclassName, false, false, false); + } + + // Lookup or create the current type + BasicType curType = lookupOrCreateClass(typeName, isOopType, isIntegerType, isUnsigned); + // Set superclass and/or ensure it's correct + if (superclass != null) { + if (curType.getSuperclass() == null) { + // Set the superclass in the current type + curType.setSuperclass(superclass); + } + + if (curType.getSuperclass() != superclass) { + throw new RuntimeException("Error: the type \"" + typeName + "\" (declared in the remote VM in VMStructs::localHotSpotVMTypes) " + + "had its superclass redefined (old was " + curType.getSuperclass().getName() + ", new is " + + superclass.getName() + ")."); + } + } + + // Classes are created with a size of UNINITIALIZED_SIZE. + // Set size if necessary. + if (curType.getSize() == UNINITIALIZED_SIZE) { + curType.setSize(size); + } else { + if (curType.getSize() != size) { + throw new RuntimeException("Error: the type \"" + typeName + "\" (declared in the remote VM in VMStructs::localHotSpotVMTypes) " + + "had its size redefined (old was " + curType.getSize() + ", new is " + size + ")."); + } + + System.err.println("Warning: the type \"" + typeName + "\" (declared in the remote VM in VMStructs::localHotSpotVMTypes) " + + "had its size declared as " + size + " twice. Continuing."); + } + + } + + /** "Virtual constructor" for fields based on type */ + public void createField(BasicType containingType, + String name, Type type, boolean isStatic, + long offset, Address staticFieldAddress) { + // Add field to containing type + containingType.addField(internalCreateField(containingType, name, type, isStatic, offset, staticFieldAddress)); + } + + Field internalCreateField(BasicType containingType, + String name, Type type, boolean isStatic, + long offset, Address staticFieldAddress) { + // "Virtual constructor" based on type + if (type.isOopType()) { + return new BasicOopField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type instanceof CIntegerType) { + return new BasicCIntegerField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJBooleanType())) { + return new BasicJBooleanField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJByteType())) { + return new BasicJByteField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJCharType())) { + return new BasicJCharField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJDoubleType())) { + return new BasicJDoubleField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJFloatType())) { + return new BasicJFloatField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJIntType())) { + return new BasicJIntField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJLongType())) { + return new BasicJLongField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJShortType())) { + return new BasicJShortField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + // Unknown ("opaque") type. Instantiate ordinary Field. + return new BasicField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + // For debugging + private void dumpMemory(Address addr, int len) { + int i = 0; + while (i < len) { + System.err.print(addr.addOffsetTo(i) + ":"); + for (int j = 0; j < 8 && i < len; i++, j++) { + String s = Long.toHexString(addr.getCIntegerAt(i, 1, true)); + System.err.print(" 0x"); + for (int k = 0; k < 2 - s.length(); k++) { + System.err.print("0"); + } + System.err.print(s); + } + System.err.println(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/LinuxVtblAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/LinuxVtblAccess.java new file mode 100644 index 00000000000..be8853cd706 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/LinuxVtblAccess.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.types.basic.*; + +public class LinuxVtblAccess extends BasicVtblAccess { + private String vt; + + public LinuxVtblAccess(SymbolLookup symbolLookup, + String[] dllNames) { + super(symbolLookup, dllNames); + + if (symbolLookup.lookup("libjvm.so", "__vt_10JavaThread") != null || + symbolLookup.lookup("libjvm_g.so", "__vt_10JavaThread") != null) { + // old C++ ABI + vt = "__vt_"; + } else { + // new C++ ABI + vt = "_ZTV"; + } + } + + protected String vtblSymbolForType(Type type) { + return vt + type.getName().length() + type; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ObjectHistogram.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ObjectHistogram.java new file mode 100644 index 00000000000..2d4bf98c691 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ObjectHistogram.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +/** A sample tool which uses the Serviceability Agent's APIs to obtain + an object histogram from a remote or crashed VM. */ +public class ObjectHistogram { + public static void main(String[] args) { + // moved to sun.jvm.hotspot.tools package.. + sun.jvm.hotspot.tools.ObjectHistogram.main(args); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/RMIHelper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/RMIHelper.java new file mode 100644 index 00000000000..474525f1b8e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/RMIHelper.java @@ -0,0 +1,131 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import java.io.*; +import java.net.*; +import java.rmi.*; +import java.rmi.registry.*; +import sun.jvm.hotspot.debugger.DebuggerException; + +public class RMIHelper { + private static final boolean startRegistry; + private static int port; + private static String serverNamePrefix; + + static { + String tmp = System.getProperty("sun.jvm.hotspot.rmi.startRegistry"); + if (tmp != null && tmp.equals("false")) { + startRegistry = false; + } else { + // by default, we attempt to start rmiregistry + startRegistry = true; + } + + port = Registry.REGISTRY_PORT; + tmp = System.getProperty("sun.jvm.hotspot.rmi.port"); + if (tmp != null) { + try { + port = Integer.parseInt(tmp); + } catch (NumberFormatException nfe) { + System.err.println("invalid port supplied, assuming default"); + } + } + + tmp = System.getProperty("sun.jvm.hotspot.rmi.serverNamePrefix"); + serverNamePrefix = (tmp != null)? serverNamePrefix : "SARemoteDebugger"; + } + + public static void rebind(String uniqueID, Remote object) throws DebuggerException { + String name = getName(uniqueID); + try { + Naming.rebind(name, object); + } catch (RemoteException re) { + if (startRegistry) { + // may be the user didn't start rmiregistry, try to start it + try { + LocateRegistry.createRegistry(port); + Naming.rebind(name, object); + } catch (Exception exp) { + throw new DebuggerException(exp); + } + } else { + throw new DebuggerException(re); + } + } catch (Exception exp) { + throw new DebuggerException(exp); + } + } + + public static void unbind(String uniqueID) throws DebuggerException { + String name = getName(uniqueID); + try { + Naming.unbind(name); + } catch (Exception exp) { + throw new DebuggerException(exp); + } + } + + public static Remote lookup(String debugServerID) throws DebuggerException { + // debugServerID follows the pattern [unique_id@]host[:port] + // we have to transform this as //host[:port]/['_'] + + int index = debugServerID.indexOf('@'); + StringBuffer nameBuf = new StringBuffer("//"); + String uniqueID = null; + if (index != -1) { + nameBuf.append(debugServerID.substring(index + 1)); + uniqueID = debugServerID.substring(0, index); + } else { + nameBuf.append(debugServerID); + } + + nameBuf.append('/'); + nameBuf.append(serverNamePrefix); + if (uniqueID != null) { + nameBuf.append('_'); + nameBuf.append(uniqueID); + } + + try { + return Naming.lookup(nameBuf.toString()); + } catch (Exception exp) { + throw new DebuggerException(exp); + } + } + + private static String getName(String uniqueID) { + String name = null; + if (uniqueID != null) { + name = serverNamePrefix + "_" + uniqueID; + } else { + name = serverNamePrefix; + } + if (port != Registry.REGISTRY_PORT) { + name = "//localhost:" + port + "/" + name; + } + return name; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/SALauncherLoader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/SALauncherLoader.java new file mode 100644 index 00000000000..9c8dce59600 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/SALauncherLoader.java @@ -0,0 +1,156 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.security.*; + +/** + * SA uses native debugger back-end library - libsaproc.so on Unix platforms. + * Starting from 5.0, in Solaris & Linux JDK "libsaproc.so" is shipped with JDK + * and is kept jre/lib/cpu directory (where all other JDK platform libraries + * are kept). This implies that always that jre copy of libsaproc.so will be + * used and the copy of libsaproc.so built from SA sources here will not + * be used at all. We can override libsaproc.so using this class loader + * as System class loader using "java.system.class.loader" property. This + * class loader loads classes paths specified paths using the System property + * "java.class.path". Because, this class loader loads SA debugger classes + * (among other classes), JVM calls findLibrary override here. In this + * findLibrary, we first check the library in the directories specified through + * "sa.library.path" System property. This way updated/latest SA native library + * can be loaded instead of the one from JDK's jre/lib directory. + */ +public class SALauncherLoader extends URLClassLoader { + + /** + * Checks native libraries under directories specified using + * the System property "sa.library.path". + */ + public String findLibrary(String name) { + name = System.mapLibraryName(name); + for (int i = 0; i < libpaths.length; i++) { + File file = new File(new File(libpaths[i]), name); + if (file.exists()) { + return file.getAbsolutePath(); + } + } + return null; + } + + public SALauncherLoader(ClassLoader parent) { + super(getClassPath(), parent); + String salibpath = System.getProperty("sa.library.path"); + if (salibpath != null) { + libpaths = salibpath.split(File.pathSeparator); + } else { + libpaths = new String[0]; + } + } + + /** + * Override loadClass so we can checkPackageAccess. + */ + public synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + int i = name.lastIndexOf('.'); + if (i != -1) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPackageAccess(name.substring(0, i)); + } + } + + Class clazz = findLoadedClass(name); + if (clazz != null) return clazz; + + /* + * NOTE: Unlike 'usual' class loaders, we do *not* delegate to + * the parent loader first. We attempt to load the class + * ourselves first and use parent loader only if we can't load. + * This is because the parent of this loader is 'default' + * System loader that can 'see' all SA classes in classpath and + * so will load those if delegated. And if parent loads SA classes, + * then JVM won't call findNative override in this class. + */ + try { + return findClass(name); + } catch (ClassNotFoundException cnfe) { + return (super.loadClass(name, resolve)); + } + } + + /** + * allow any classes loaded from classpath to exit the VM. + */ + protected PermissionCollection getPermissions(CodeSource codesource) { + PermissionCollection perms = super.getPermissions(codesource); + perms.add(new RuntimePermission("exitVM")); + return perms; + } + + //-- Internals only below this point + + private String[] libpaths; + + private static URL[] getClassPath() { + final String s = System.getProperty("java.class.path"); + final File[] path = (s == null) ? new File[0] : getClassPath(s); + + return pathToURLs(path); + } + + private static URL[] pathToURLs(File[] path) { + URL[] urls = new URL[path.length]; + for (int i = 0; i < path.length; i++) { + urls[i] = getFileURL(path[i]); + } + return urls; + } + + private static File[] getClassPath(String cp) { + String[] tmp = cp.split(File.pathSeparator); + File[] paths = new File[tmp.length]; + for (int i = 0; i < paths.length; i++) { + paths[i] = new File(tmp[i].equals("")? "." : tmp[i]); + } + return paths; + } + + private static URL getFileURL(File file) { + try { + file = file.getCanonicalFile(); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + return file.toURL(); + } catch (MalformedURLException mue) { + throw new InternalError(mue.getMessage()); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/StackTrace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/StackTrace.java new file mode 100644 index 00000000000..6b57e1c638b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/StackTrace.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +/** Traverses and prints the stack traces for all Java threads in the + remote VM */ +public class StackTrace { + public static void main(String[] args) { + // moved to sun.jvm.hotspot.tools package.. + sun.jvm.hotspot.tools.StackTrace.main(args); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/TestDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/TestDebugger.java new file mode 100644 index 00000000000..be7528aff2e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/TestDebugger.java @@ -0,0 +1,214 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.dbx.*; + +// A test of the debugger backend. This should be used to connect to +// the helloWorld.cpp program. + +public class TestDebugger { + // FIXME: make these configurable, i.e., via a dotfile + private static final String dbxPathName = "/export/home/kbr/ws/dbx_61/dev/Derived-sparcv9-S2./src/dbx/dbx"; + private static final String[] dbxSvcAgentDSOPathNames = + new String[] { + "/export/home/kbr/main/sa_baseline/src/os/solaris/agent/libsvc_agent_dbx.so" + }; + + private static void usage() { + System.out.println("usage: java TestDebugger [pid]"); + System.out.println("pid must be the process ID of the helloWorld process"); + System.exit(1); + } + + public static void main(String[] args) { + try { + if (args.length != 1) { + usage(); + } + + int pid = 0; + try { + pid = Integer.parseInt(args[0]); + } + catch (NumberFormatException e) { + usage(); + } + + JVMDebugger debugger = new DbxDebuggerLocal(new MachineDescriptionSPARC64Bit(), + dbxPathName, dbxSvcAgentDSOPathNames, true); + + try { + debugger.attach(pid); + } + catch (DebuggerException e) { + System.err.print("Error attaching to process ID " + pid + ": "); + if (e.getMessage() != null) { + System.err.print(e.getMessage()); + } + System.err.println(); + System.exit(1); + } + + // HACK: configure debugger with primitive type sizes to get + // Java types going + debugger.configureJavaPrimitiveTypeSizes(1, 1, 2, 8, 4, 4, 8, 2); + + // FIXME: figure out how to canonicalize and/or eliminate + // loadobject specification + String loadObjectName = "-"; + + // long strAddr = debugger.lookup("helloWorld", "helloWorldString"); + Address addr = debugger.lookup(loadObjectName, "helloWorldString"); + if (addr == null) { + System.err.println("Error looking up symbol \"helloWorldString\" in context \"" + + loadObjectName + "\""); + System.exit(1); + } + + // This is a pointer which points to the start of storage. + // Dereference it. + addr = addr.getAddressAt(0); + + // Read the number of bytes we know we need + int helloWorldLen = 13; + byte[] data = new byte[helloWorldLen]; + for (int i = 0; i < helloWorldLen; ++i) { + data[i] = (byte) addr.getCIntegerAt(i, 1, false); + } + + // Convert to characters + char[] chars = new char[data.length]; + for (int i = 0; i < data.length; ++i) { + chars[i] = (char) data[i]; + } + String helloWorldStr = new String(chars); + + System.out.println("Successfully read string \"" + helloWorldStr + "\" from target process\n"); + + // Test all Java data types (see helloWorld.cpp) + byte expectedByteValue = (byte) 132; + short expectedShortValue = (short) 27890; + int expectedIntValue = 1020304050; + long expectedLongValue = 102030405060708090L; + float expectedFloatValue = 35.4F; + double expectedDoubleValue = 1.23456789; + byte byteValue = 0; + short shortValue = 0; + int intValue = 0; + long longValue = 0; + float floatValue = 0; + double doubleValue = 0; + + addr = debugger.lookup(loadObjectName, "testByte"); + if (addr == null) { + System.err.println("Error looking up symbol \"testByte\" in context \"" + + loadObjectName + "\""); + System.exit(1); + } + byteValue = addr.getJByteAt(0); + if (byteValue != expectedByteValue) { + System.err.println("Error: unexpected byte value (got " + + byteValue + ", expected " + expectedByteValue + ")"); + System.exit(1); + } + + addr = debugger.lookup(loadObjectName, "testShort"); + if (addr == null) { + System.err.println("Error looking up symbol \"testShort\" in context \"" + + loadObjectName + "\""); + System.exit(1); + } + shortValue = addr.getJShortAt(0); + if (shortValue != expectedShortValue) { + System.err.println("Error: unexpected short value (got " + + shortValue + ", expected " + expectedShortValue + ")"); + System.exit(1); + } + + addr = debugger.lookup(loadObjectName, "testInt"); + if (addr == null) { + System.err.println("Error looking up symbol \"testInt\" in context \"" + + loadObjectName + "\""); + System.exit(1); + } + intValue = addr.getJIntAt(0); + if (intValue != expectedIntValue) { + System.err.println("Error: unexpected int value (got " + + intValue + ", expected " + expectedIntValue + ")"); + System.exit(1); + } + + addr = debugger.lookup(loadObjectName, "testLong"); + if (addr == null) { + System.err.println("Error looking up symbol \"testLong\" in context \"" + + loadObjectName + "\""); + System.exit(1); + } + longValue = addr.getJLongAt(0); + if (longValue != expectedLongValue) { + System.err.println("Error: unexpected long value (got " + + longValue + ", expected " + expectedLongValue + ")"); + System.exit(1); + } + + addr = debugger.lookup(loadObjectName, "testFloat"); + if (addr == null) { + System.err.println("Error looking up symbol \"testFloat\" in context \"" + + loadObjectName + "\""); + System.exit(1); + } + floatValue = addr.getJFloatAt(0); + if (floatValue != expectedFloatValue) { + System.err.println("Error: unexpected float value (got " + + floatValue + ", expected " + expectedFloatValue + ")"); + System.exit(1); + } + + addr = debugger.lookup(loadObjectName, "testDouble"); + if (addr == null) { + System.err.println("Error looking up symbol \"testDouble\" in context \"" + + loadObjectName + "\""); + System.exit(1); + } + doubleValue = addr.getJDoubleAt(0); + if (doubleValue != expectedDoubleValue) { + System.err.println("Error: unexpected double value (got " + + doubleValue + ", expected " + expectedDoubleValue + ")"); + System.exit(1); + } + + System.err.println("All tests passed successfully."); + + debugger.detach(); + } + catch (AddressException e) { + System.err.println("Error occurred during test:"); + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/Win32VtblAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/Win32VtblAccess.java new file mode 100644 index 00000000000..a0e1642df9c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/Win32VtblAccess.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.types.basic.*; + +/** This class implements the compiler-specific access to the vtbl for + a given C++ type. As it happens, on Win32 (at least for Visual C++ + 6.0) the name mangling for vtbls is very straightforward. We only + need to ensure that these symbols are exported from the HotSpot + DLL, which is done with a .DEF file. This class is named + "Win32VtblAccess" because it is not necessarily HotSpot-specific. */ + +public class Win32VtblAccess extends BasicVtblAccess { + public Win32VtblAccess(SymbolLookup symbolLookup, + String[] dllNames) { + super(symbolLookup, dllNames); + } + + protected String vtblSymbolForType(Type type) { + return "??_7" + type.getName() + "@@6B@"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/AbstractInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/AbstractInstruction.java new file mode 100644 index 00000000000..595c6535cda --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/AbstractInstruction.java @@ -0,0 +1,97 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public abstract class AbstractInstruction implements Instruction { + protected final String name; + + public AbstractInstruction(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + // some type testers + public boolean isIllegal() { + return false; + } + + public boolean isArithmetic() { + return false; + } + + public boolean isLogical() { + return false; + } + + public boolean isShift() { + return false; + } + + public boolean isMove() { + return false; + } + + public boolean isBranch() { + return false; + } + + public boolean isCall() { + return false; + } + + public boolean isReturn() { + return false; + } + + public boolean isLoad() { + return false; + } + + public boolean isStore() { + return false; + } + + public boolean isFloat() { + return false; + } + + public boolean isTrap() { + return false; + } + + public boolean isNoop() { + return false; + } + + // convert the instruction as String given currentPc + // and SymbolFinder + + public String asString(long currentPc, SymbolFinder symFinder) { + return name; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Address.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Address.java new file mode 100644 index 00000000000..8f19e08bc28 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Address.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public abstract class Address extends Operand { + public boolean isAddress() { + return true; + } + + public abstract String toString(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Arithmetic.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Arithmetic.java new file mode 100644 index 00000000000..6dcd53d1cdf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Arithmetic.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface Arithmetic extends Instruction, RTLOperations { + public Operand[] getArithmeticSources(); + public Operand getArithmeticDestination(); + public int getOperation(); // one of RTLOperations +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ArithmeticInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ArithmeticInstruction.java new file mode 100644 index 00000000000..7b8e9e1023b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ArithmeticInstruction.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface ArithmeticInstruction extends Instruction, RTLOperations { + public Operand[] getArithmeticSources(); + public Operand getArithmeticDestination(); + public int getOperation(); // one of RTLOperations +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/BaseIndexScaleDispAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/BaseIndexScaleDispAddress.java new file mode 100644 index 00000000000..6d9922d6c0e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/BaseIndexScaleDispAddress.java @@ -0,0 +1,88 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +// address is calculated as (base + (index * scale) + displacement) +// optionally index is auto incremented or decremented + +public abstract class BaseIndexScaleDispAddress extends IndirectAddress { + private final Register base, index; + private final int scale; + private final long disp; + private boolean isAutoIncr; + private boolean isAutoDecr; + + public BaseIndexScaleDispAddress(Register base, Register index, long disp, int scale) { + this.base = base; + this.index = index; + this.disp = disp; + this.scale = scale; + } + + public BaseIndexScaleDispAddress(Register base, Register index, long disp) { + this(base, index, disp, 1); + } + + public BaseIndexScaleDispAddress(Register base, Register index) { + this(base, index, 0L, 1); + } + + public BaseIndexScaleDispAddress(Register base, long disp) { + this(base, null, disp, 1); + } + + public Register getBase() { + return base; + } + + public Register getIndex() { + return index; + } + + public int getScale() { + return scale; + } + + public long getDisplacement() { + return disp; + } + + // is the index auto decremented or incremented? + public boolean isAutoIncrement() { + return isAutoIncr; + } + + public void setAutoIncrement(boolean value) { + isAutoIncr = value; + } + + public boolean isAutoDecrement() { + return isAutoDecr; + } + + public void setAutoDecrement(boolean value) { + isAutoDecr = value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/BranchInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/BranchInstruction.java new file mode 100644 index 00000000000..4d8d4e8c60a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/BranchInstruction.java @@ -0,0 +1,30 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface BranchInstruction extends Instruction { + public boolean isConditional(); + public Address getBranchDestination(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/CPUHelper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/CPUHelper.java new file mode 100644 index 00000000000..5af240781d7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/CPUHelper.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface CPUHelper { + public Disassembler createDisassembler(long startPc, byte[] code); + public Register getIntegerRegister(int num); + public Register getFloatRegister(int num); + public Register getStackPointer(); + public Register getFramePointer(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/CallInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/CallInstruction.java new file mode 100644 index 00000000000..5c34b9471b7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/CallInstruction.java @@ -0,0 +1,28 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface CallInstruction extends BranchInstruction { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/DirectAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/DirectAddress.java new file mode 100644 index 00000000000..13314ccc01d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/DirectAddress.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public class DirectAddress extends Address { + private long value; + public DirectAddress(long value) { + this.value = value; + } + + public long getValue() { + return value; + } + + public String toString() { + return Long.toHexString(value); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Disassembler.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Disassembler.java new file mode 100644 index 00000000000..44524616107 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Disassembler.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public abstract class Disassembler { + protected long startPc; + protected byte[] code; + + public Disassembler(long startPc, byte[] code) { + this.startPc = startPc; + this.code = code; + } + + public long getStartPC() { + return startPc; + } + + public byte[] getCode() { + return code; + } + + public abstract void decode(InstructionVisitor visitor); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/DummySymbolFinder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/DummySymbolFinder.java new file mode 100644 index 00000000000..d8864241c28 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/DummySymbolFinder.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public class DummySymbolFinder implements SymbolFinder { + public String getSymbolFor(long address) { + return "0x" + Long.toHexString(address); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Immediate.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Immediate.java new file mode 100644 index 00000000000..2e6ce6b78f1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Immediate.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +// Immediate is a Number operand + +public class Immediate extends ImmediateOrRegister { + private final Number value; + + public Immediate(Number value) { + this.value = value; + } + + public Number getNumber() { + return value; + } + + public boolean isImmediate() { + return true; + } + + public String toString() { + return value.toString(); + } + + public int hashCode() { + return value.hashCode(); + } + + public boolean equals(Object obj) { + if (obj == null) + return false; + + if (getClass() != obj.getClass()) + return false; + + Immediate other = (Immediate) obj; + return value.equals(other.value); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ImmediateOrRegister.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ImmediateOrRegister.java new file mode 100644 index 00000000000..fbcee76039f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ImmediateOrRegister.java @@ -0,0 +1,28 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public abstract class ImmediateOrRegister extends Operand { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/IndirectAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/IndirectAddress.java new file mode 100644 index 00000000000..d6368d09bab --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/IndirectAddress.java @@ -0,0 +1,28 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public abstract class IndirectAddress extends Address { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Instruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Instruction.java new file mode 100644 index 00000000000..dc04ac52059 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Instruction.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface Instruction { + public String getName(); + + // total size in bytes (operands + opcode). + // for eg. in sparc it is always 4 (= 32bits) + public int getSize(); + + // some type testers + public boolean isIllegal(); + public boolean isArithmetic(); + public boolean isLogical(); + public boolean isShift(); + public boolean isMove(); + public boolean isBranch(); + public boolean isCall(); + public boolean isReturn(); + public boolean isLoad(); + public boolean isStore(); + public boolean isFloat(); + public boolean isTrap(); + public boolean isNoop(); + + // convert the instruction as String given currentPc + // and SymbolFinder + + public String asString(long currentPc, SymbolFinder symFinder); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/InstructionVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/InstructionVisitor.java new file mode 100644 index 00000000000..f33953dad55 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/InstructionVisitor.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface InstructionVisitor { + public void prologue(); + public void visit(long currentPc, Instruction instr); + public void epilogue(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/LoadInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/LoadInstruction.java new file mode 100644 index 00000000000..0d6feb99b55 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/LoadInstruction.java @@ -0,0 +1,30 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface LoadInstruction extends MemoryInstruction { + public Address getLoadSource(); + public Register[] getLoadDestinations(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/LogicInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/LogicInstruction.java new file mode 100644 index 00000000000..f3065dedede --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/LogicInstruction.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface LogicInstruction extends Instruction, RTLOperations { + public Operand[] getLogicSources(); + public Operand getLogicDestination(); + public int getOperation(); // one of RTLOperations +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/MemoryInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/MemoryInstruction.java new file mode 100644 index 00000000000..28434de6bb5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/MemoryInstruction.java @@ -0,0 +1,30 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface MemoryInstruction extends RTLDataTypes { + public int getDataType(); // one of the RTLDataTypes. + public boolean isConditional(); // conditional store like swap or v9 like non-faulting loads +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/MoveInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/MoveInstruction.java new file mode 100644 index 00000000000..c013b597a3f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/MoveInstruction.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface MoveInstruction extends Instruction { + public ImmediateOrRegister getMoveSource(); + public Register getMoveDestination(); + // for condition moves + public boolean isConditional(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Operand.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Operand.java new file mode 100644 index 00000000000..df1fcf4232f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Operand.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +import sun.jvm.hotspot.utilities.Assert; + +/* + * Operand is used as RTL argument. An Operand is either + * a Number or a Register or an Address. +*/ + +public abstract class Operand { + // few type testers + public boolean isAddress() { + return false; + } + + public boolean isImmediate() { + return false; + } + + public boolean isRegister() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/PCRelativeAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/PCRelativeAddress.java new file mode 100644 index 00000000000..42d379a6fd3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/PCRelativeAddress.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +// address is specified as an offset from current PC + +public class PCRelativeAddress extends IndirectAddress { + private final long disp; + + public PCRelativeAddress(long disp) { + this.disp = disp; + } + + public String toString() { + return new Long(disp).toString(); + } + + public long getDisplacement() { + return disp; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/RTLDataTypes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/RTLDataTypes.java new file mode 100644 index 00000000000..448bda8fadc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/RTLDataTypes.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface RTLDataTypes { + + // HALF = 16 bits, WORD = 32 bits, DWORD = 64 bits and QWORD = 128 bits. + + public static final int RTLDT_SIGNED_BYTE = 0; + public static final int RTLDT_UNSIGNED_BYTE = 1; + public static final int RTLDT_SIGNED_HALF = 2; + public static final int RTLDT_UNSIGNED_HALF = 3; + public static final int RTLDT_SIGNED_WORD = 4; + public static final int RTLDT_UNSIGNED_WORD = 5; + public static final int RTLDT_SIGNED_DWORD = 6; + public static final int RTLDT_UNSIGNED_DWORD = 7; + public static final int RTLDT_SIGNED_QWORD = 8; + public static final int RTLDT_UNSIGNED_QWORD = 9; + + // float is 4 bytes, double is 8 bytes, extended double is 10 bytes + // and quad is 16 bytes. + + public static final int RTLDT_FL_SINGLE = 10; + public static final int RTLDT_FL_DOUBLE = 11; + public static final int RTLDT_FL_EXT_DOUBLE = 12; + public static final int RTLDT_FL_QUAD = 13; + + public static final int RTLDT_STRING = 14; + + public static final int RTLDT_UNKNOWN = Integer.MAX_VALUE; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/RTLOperations.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/RTLOperations.java new file mode 100644 index 00000000000..5e58ce922fd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/RTLOperations.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface RTLOperations { + + // arithmetic operations + public static final int RTLOP_ADD = 0; + // with carry + public static final int RTLOP_ADDC = 1; + public static final int RTLOP_SUB = 2; + // with carry + public static final int RTLOP_SUBC = 3; + public static final int RTLOP_SMUL = 4; + public static final int RTLOP_UMUL = 5; + public static final int RTLOP_SDIV = 6; + public static final int RTLOP_UDIV = 7; + + public static final int RTLOP_MAX_ARITHMETIC = RTLOP_UDIV; + + // logical operations + public static final int RTLOP_AND = 8; + public static final int RTLOP_OR = 9; + public static final int RTLOP_NOT = 10; + public static final int RTLOP_NAND = 11; + public static final int RTLOP_NOR = 12; + public static final int RTLOP_XOR = 13; + public static final int RTLOP_XNOR = 14; + + public static final int RTLOP_MAX_LOGICAL = RTLOP_XNOR; + + // shift operations + public static final int RTLOP_SRL = 15; + public static final int RTLOP_SRA = 16; + public static final int RTLOP_SLL = 17; + + public static final int RTLOP_MAX_SHIFT = RTLOP_SLL; + + public static final int RTLOP_UNKNOWN = Integer.MAX_VALUE; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Register.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Register.java new file mode 100644 index 00000000000..5e38334db5a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Register.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +/**

Register is an abstraction over machine registers.

+ +

FIXME: should align constants with underlying VM code

*/ + +public abstract class Register extends ImmediateOrRegister { + /** Corresponds to the machine register code. -1 stands for invalid + register (initial value). */ + protected int number; + + public Register() { + number = -1; + } + + public Register(int number) { + this.number = number; + } + + /** Must be overridden by subclass to indicate number of available + registers on this platform */ + public abstract int getNumberOfRegisters(); + + public boolean isValid() { + return ((0 <= number) && (number <= getNumberOfRegisters())); + } + + public int getNumber() { + return number; + } + + public boolean equals(Object x) { + if (x == null) { + return false; + } + + if (!getClass().equals(x.getClass())) { + return false; + } + + Register reg = (Register) x; + + return (reg.getNumber() == getNumber()); + } + + public int hashCode() { + return number; + } + + public boolean isRegister() { + return true; + } + + public abstract boolean isStackPointer(); + public abstract boolean isFramePointer(); + public abstract boolean isFloat(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ReturnInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ReturnInstruction.java new file mode 100644 index 00000000000..57be140135b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ReturnInstruction.java @@ -0,0 +1,28 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface ReturnInstruction extends BranchInstruction { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ShiftInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ShiftInstruction.java new file mode 100644 index 00000000000..1f0a15202a6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ShiftInstruction.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface ShiftInstruction extends Instruction, RTLOperations { + public Operand getShiftSource(); + public Operand getShiftLength(); // number of bits to shift + public Operand getShiftDestination(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/StoreInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/StoreInstruction.java new file mode 100644 index 00000000000..8bbc31ae6a5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/StoreInstruction.java @@ -0,0 +1,30 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +public interface StoreInstruction extends MemoryInstruction { + public Register[] getStoreSources(); + public Address getStoreDestination(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/SymbolFinder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/SymbolFinder.java new file mode 100644 index 00000000000..b43e3dfbbd3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/SymbolFinder.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm; + +// SymbolFinder gets Symbol for a given address. + +public interface SymbolFinder { + // return address value as hex string if no symbol associated with the given address. + public String getSymbolFor(long address); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64FloatRegister.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64FloatRegister.java new file mode 100644 index 00000000000..dc8def79c79 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64FloatRegister.java @@ -0,0 +1,64 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.amd64; + +import sun.jvm.hotspot.asm.Register; +import sun.jvm.hotspot.utilities.Assert; + +public class AMD64FloatRegister extends Register { + + public AMD64FloatRegister(int number) { + super(number); + } + + public int getNumber() { + return number; + } + + public int getNumberOfRegisters() { + return AMD64FloatRegisters.getNumRegisters(); + } + + public boolean isFloat() { + return true; + } + + public boolean isFramePointer() { + return false; + } + + public boolean isStackPointer() { + return false; + } + + public boolean isValid() { + return number >= 0 && number < AMD64FloatRegisters.getNumRegisters(); + } + + public String toString() { + return AMD64FloatRegisters.getRegisterName(number); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64FloatRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64FloatRegisters.java new file mode 100644 index 00000000000..4bd098df01c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64FloatRegisters.java @@ -0,0 +1,90 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.amd64; + +import sun.jvm.hotspot.utilities.Assert; + +public class AMD64FloatRegisters { + + public static int getNumRegisters() { + return NUM_REGIXMMERS; + } + + public static AMD64FloatRegister getRegister(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGIXMMERS, "invalid float register number!"); + } + return registers[regNum]; + } + + public static String getRegisterName(int i) { + return "XMM(" + i + ")"; + } + + public static final AMD64FloatRegister XMM0; + public static final AMD64FloatRegister XMM1; + public static final AMD64FloatRegister XMM2; + public static final AMD64FloatRegister XMM3; + public static final AMD64FloatRegister XMM4; + public static final AMD64FloatRegister XMM5; + public static final AMD64FloatRegister XMM6; + public static final AMD64FloatRegister XMM7; + public static final AMD64FloatRegister XMM8; + public static final AMD64FloatRegister XMM9; + public static final AMD64FloatRegister XMM10; + public static final AMD64FloatRegister XMM11; + public static final AMD64FloatRegister XMM12; + public static final AMD64FloatRegister XMM13; + public static final AMD64FloatRegister XMM14; + public static final AMD64FloatRegister XMM15; + + public static final int NUM_REGIXMMERS = 16; + + private static final AMD64FloatRegister[] registers; + + static { + XMM0 = new AMD64FloatRegister(0); + XMM1 = new AMD64FloatRegister(1); + XMM2 = new AMD64FloatRegister(2); + XMM3 = new AMD64FloatRegister(3); + XMM4 = new AMD64FloatRegister(4); + XMM5 = new AMD64FloatRegister(5); + XMM6 = new AMD64FloatRegister(6); + XMM7 = new AMD64FloatRegister(7); + XMM8 = new AMD64FloatRegister(8); + XMM9 = new AMD64FloatRegister(9); + XMM10 = new AMD64FloatRegister(10); + XMM11 = new AMD64FloatRegister(11); + XMM12 = new AMD64FloatRegister(12); + XMM13 = new AMD64FloatRegister(13); + XMM14 = new AMD64FloatRegister(14); + XMM15 = new AMD64FloatRegister(15); + + registers = new AMD64FloatRegister[] { + XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, + XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15 + }; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64Helper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64Helper.java new file mode 100644 index 00000000000..5aef9feb3d0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64Helper.java @@ -0,0 +1,51 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.amd64; + +import sun.jvm.hotspot.asm.*; + + +public class AMD64Helper implements CPUHelper { + public Disassembler createDisassembler(long startPc, byte[] code) { + // FIXME: no disassembler yet + return null; + } + + public Register getIntegerRegister(int num) { + return AMD64Registers.getRegister(num); + } + + public Register getFloatRegister(int num) { + return AMD64FloatRegisters.getRegister(num); + } + + public Register getStackPointer() { + return AMD64Registers.RSP; + } + + public Register getFramePointer() { + return AMD64Registers.RBP; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64Register.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64Register.java new file mode 100644 index 00000000000..b3564ceb12f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64Register.java @@ -0,0 +1,53 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.amd64; + +import sun.jvm.hotspot.asm.*; + +public class AMD64Register extends Register { + protected String name; + public AMD64Register(int num, String name) { + super(num); + this.name = name; + } + public int getNumberOfRegisters() { + return AMD64Registers.getNumberOfRegisters(); + } + public String toString() { + return name; + } + public boolean isFramePointer() { + return number == 5; //rbp + } + public boolean isStackPointer() { + return number == 4; //rsp + } + public boolean isFloat() { + return false; + } + public boolean isSegmentPointer() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64Registers.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64Registers.java new file mode 100644 index 00000000000..ad0f5898409 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/amd64/AMD64Registers.java @@ -0,0 +1,92 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.amd64; + +import sun.jvm.hotspot.utilities.*; + +public class AMD64Registers { + public static final int NUM_REGISTERS = 16; + + public static final AMD64Register RAX; + public static final AMD64Register RCX; + public static final AMD64Register RDX; + public static final AMD64Register RBX; + public static final AMD64Register RSP; + public static final AMD64Register RBP; + public static final AMD64Register RSI; + public static final AMD64Register RDI; + public static final AMD64Register R8; + public static final AMD64Register R9; + public static final AMD64Register R10; + public static final AMD64Register R11; + public static final AMD64Register R12; + public static final AMD64Register R13; + public static final AMD64Register R14; + public static final AMD64Register R15; + + private static final AMD64Register[] registers; + + static { + RAX = new AMD64Register(0, "rax"); + RCX = new AMD64Register(1, "rcx"); + RDX = new AMD64Register(2, "rdx"); + RBX = new AMD64Register(3, "rbx"); + RSP = new AMD64Register(4, "rsp"); + RBP = new AMD64Register(5, "rbp"); + RSI = new AMD64Register(6, "rsi"); + RDI = new AMD64Register(7, "rdi"); + R8 = new AMD64Register(8, "r8" ); + R9 = new AMD64Register(9, "r9" ); + R10 = new AMD64Register(10,"r10"); + R11 = new AMD64Register(11,"r11"); + R12 = new AMD64Register(12,"r12"); + R13 = new AMD64Register(13,"r13"); + R14 = new AMD64Register(14,"r14"); + R15 = new AMD64Register(15,"r15"); + registers = new AMD64Register[] { + RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, + R8, R9, R10, R11, R12, R13, R14, R15 + }; + } + + public static int getNumberOfRegisters() { + return NUM_REGISTERS; + } + + public static AMD64Register getRegister(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid integer register number!"); + } + return registers[regNum]; + } + + //Return the register name + public static String getRegisterName(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid integer register number!"); + } + return registers[regNum].toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64FloatRegister.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64FloatRegister.java new file mode 100644 index 00000000000..0b4f14b1fd4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64FloatRegister.java @@ -0,0 +1,73 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.ia64; + +import sun.jvm.hotspot.asm.Register; +import sun.jvm.hotspot.utilities.Assert; + +public class IA64FloatRegister extends IA64Register { + + public IA64FloatRegister(int number) { + super(number); + } + + public int getNumber() { + return number; + } + + public static final int SINGLE_PRECISION = 1; + public static final int DOUBLE_PRECISION = 2; + public static final int QUAD_PRECISION = 3; + + public int getNumber(int width) { + return number; + } + + private static final int nofRegisters = 128; + public int getNumberOfRegisters() { + return nofRegisters; + } + + public boolean isFloat() { + return true; + } + + public boolean isFramePointer() { + return false; + } + + public boolean isStackPointer() { + return false; + } + + public boolean isValid() { + return number >= 0 && number < nofRegisters; + } + + public String toString() { + return IA64FloatRegisters.getRegisterName(number); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64FloatRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64FloatRegisters.java new file mode 100644 index 00000000000..51ab617fba5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64FloatRegisters.java @@ -0,0 +1,320 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.ia64; + +import sun.jvm.hotspot.utilities.Assert; + +public class IA64FloatRegisters { + public static int getNumRegisters() { + return 128; + } + + public static IA64FloatRegister getRegister(int i) { + Assert.that(i >= 0 && i < 128, "float register number is invalid"); + return registers[i]; + } + + public static String getRegisterName(int i) { + return "%f" + i; + } + + public static final IA64FloatRegister F0; + public static final IA64FloatRegister F1; + public static final IA64FloatRegister F2; + public static final IA64FloatRegister F3; + public static final IA64FloatRegister F4; + public static final IA64FloatRegister F5; + public static final IA64FloatRegister F6; + public static final IA64FloatRegister F7; + public static final IA64FloatRegister F8; + public static final IA64FloatRegister F9; + public static final IA64FloatRegister F10; + public static final IA64FloatRegister F11; + public static final IA64FloatRegister F12; + public static final IA64FloatRegister F13; + public static final IA64FloatRegister F14; + public static final IA64FloatRegister F15; + public static final IA64FloatRegister F16; + public static final IA64FloatRegister F17; + public static final IA64FloatRegister F18; + public static final IA64FloatRegister F19; + public static final IA64FloatRegister F20; + public static final IA64FloatRegister F21; + public static final IA64FloatRegister F22; + public static final IA64FloatRegister F23; + public static final IA64FloatRegister F24; + public static final IA64FloatRegister F25; + public static final IA64FloatRegister F26; + public static final IA64FloatRegister F27; + public static final IA64FloatRegister F28; + public static final IA64FloatRegister F29; + public static final IA64FloatRegister F30; + public static final IA64FloatRegister F31; + public static final IA64FloatRegister F32; + public static final IA64FloatRegister F33; + public static final IA64FloatRegister F34; + public static final IA64FloatRegister F35; + public static final IA64FloatRegister F36; + public static final IA64FloatRegister F37; + public static final IA64FloatRegister F38; + public static final IA64FloatRegister F39; + public static final IA64FloatRegister F40; + public static final IA64FloatRegister F41; + public static final IA64FloatRegister F42; + public static final IA64FloatRegister F43; + public static final IA64FloatRegister F44; + public static final IA64FloatRegister F45; + public static final IA64FloatRegister F46; + public static final IA64FloatRegister F47; + public static final IA64FloatRegister F48; + public static final IA64FloatRegister F49; + public static final IA64FloatRegister F50; + public static final IA64FloatRegister F51; + public static final IA64FloatRegister F52; + public static final IA64FloatRegister F53; + public static final IA64FloatRegister F54; + public static final IA64FloatRegister F55; + public static final IA64FloatRegister F56; + public static final IA64FloatRegister F57; + public static final IA64FloatRegister F58; + public static final IA64FloatRegister F59; + public static final IA64FloatRegister F60; + public static final IA64FloatRegister F61; + public static final IA64FloatRegister F62; + public static final IA64FloatRegister F63; + public static final IA64FloatRegister F64; + public static final IA64FloatRegister F65; + public static final IA64FloatRegister F66; + public static final IA64FloatRegister F67; + public static final IA64FloatRegister F68; + public static final IA64FloatRegister F69; + public static final IA64FloatRegister F70; + public static final IA64FloatRegister F71; + public static final IA64FloatRegister F72; + public static final IA64FloatRegister F73; + public static final IA64FloatRegister F74; + public static final IA64FloatRegister F75; + public static final IA64FloatRegister F76; + public static final IA64FloatRegister F77; + public static final IA64FloatRegister F78; + public static final IA64FloatRegister F79; + public static final IA64FloatRegister F80; + public static final IA64FloatRegister F81; + public static final IA64FloatRegister F82; + public static final IA64FloatRegister F83; + public static final IA64FloatRegister F84; + public static final IA64FloatRegister F85; + public static final IA64FloatRegister F86; + public static final IA64FloatRegister F87; + public static final IA64FloatRegister F88; + public static final IA64FloatRegister F89; + public static final IA64FloatRegister F90; + public static final IA64FloatRegister F91; + public static final IA64FloatRegister F92; + public static final IA64FloatRegister F93; + public static final IA64FloatRegister F94; + public static final IA64FloatRegister F95; + public static final IA64FloatRegister F96; + public static final IA64FloatRegister F97; + public static final IA64FloatRegister F98; + public static final IA64FloatRegister F99; + public static final IA64FloatRegister F100; + public static final IA64FloatRegister F101; + public static final IA64FloatRegister F102; + public static final IA64FloatRegister F103; + public static final IA64FloatRegister F104; + public static final IA64FloatRegister F105; + public static final IA64FloatRegister F106; + public static final IA64FloatRegister F107; + public static final IA64FloatRegister F108; + public static final IA64FloatRegister F109; + public static final IA64FloatRegister F110; + public static final IA64FloatRegister F111; + public static final IA64FloatRegister F112; + public static final IA64FloatRegister F113; + public static final IA64FloatRegister F114; + public static final IA64FloatRegister F115; + public static final IA64FloatRegister F116; + public static final IA64FloatRegister F117; + public static final IA64FloatRegister F118; + public static final IA64FloatRegister F119; + public static final IA64FloatRegister F120; + public static final IA64FloatRegister F121; + public static final IA64FloatRegister F122; + public static final IA64FloatRegister F123; + public static final IA64FloatRegister F124; + public static final IA64FloatRegister F125; + public static final IA64FloatRegister F126; + public static final IA64FloatRegister F127; + public static final int NUM_REGISTERS = 128; + private static final IA64FloatRegister registers[]; + + static { + F0 = new IA64FloatRegister(0); + F1 = new IA64FloatRegister(1); + F2 = new IA64FloatRegister(2); + F3 = new IA64FloatRegister(3); + F4 = new IA64FloatRegister(4); + F5 = new IA64FloatRegister(5); + F6 = new IA64FloatRegister(6); + F7 = new IA64FloatRegister(7); + F8 = new IA64FloatRegister(8); + F9 = new IA64FloatRegister(9); + F10 = new IA64FloatRegister(10); + F11 = new IA64FloatRegister(11); + F12 = new IA64FloatRegister(12); + F13 = new IA64FloatRegister(13); + F14 = new IA64FloatRegister(14); + F15 = new IA64FloatRegister(15); + F16 = new IA64FloatRegister(16); + F17 = new IA64FloatRegister(17); + F18 = new IA64FloatRegister(18); + F19 = new IA64FloatRegister(19); + F20 = new IA64FloatRegister(20); + F21 = new IA64FloatRegister(21); + F22 = new IA64FloatRegister(22); + F23 = new IA64FloatRegister(23); + F24 = new IA64FloatRegister(24); + F25 = new IA64FloatRegister(25); + F26 = new IA64FloatRegister(26); + F27 = new IA64FloatRegister(27); + F28 = new IA64FloatRegister(28); + F29 = new IA64FloatRegister(29); + F30 = new IA64FloatRegister(30); + F31 = new IA64FloatRegister(31); + F32 = new IA64FloatRegister(32); + F33 = new IA64FloatRegister(33); + F34 = new IA64FloatRegister(34); + F35 = new IA64FloatRegister(35); + F36 = new IA64FloatRegister(36); + F37 = new IA64FloatRegister(37); + F38 = new IA64FloatRegister(38); + F39 = new IA64FloatRegister(39); + F40 = new IA64FloatRegister(40); + F41 = new IA64FloatRegister(41); + F42 = new IA64FloatRegister(42); + F43 = new IA64FloatRegister(43); + F44 = new IA64FloatRegister(44); + F45 = new IA64FloatRegister(45); + F46 = new IA64FloatRegister(46); + F47 = new IA64FloatRegister(47); + F48 = new IA64FloatRegister(48); + F49 = new IA64FloatRegister(49); + F50 = new IA64FloatRegister(50); + F51 = new IA64FloatRegister(51); + F52 = new IA64FloatRegister(52); + F53 = new IA64FloatRegister(53); + F54 = new IA64FloatRegister(54); + F55 = new IA64FloatRegister(55); + F56 = new IA64FloatRegister(56); + F57 = new IA64FloatRegister(57); + F58 = new IA64FloatRegister(58); + F59 = new IA64FloatRegister(59); + F60 = new IA64FloatRegister(60); + F61 = new IA64FloatRegister(61); + F62 = new IA64FloatRegister(62); + F63 = new IA64FloatRegister(63); + F64 = new IA64FloatRegister(64); + F65 = new IA64FloatRegister(65); + F66 = new IA64FloatRegister(66); + F67 = new IA64FloatRegister(67); + F68 = new IA64FloatRegister(68); + F69 = new IA64FloatRegister(69); + F70 = new IA64FloatRegister(70); + F71 = new IA64FloatRegister(71); + F72 = new IA64FloatRegister(72); + F73 = new IA64FloatRegister(73); + F74 = new IA64FloatRegister(74); + F75 = new IA64FloatRegister(75); + F76 = new IA64FloatRegister(76); + F77 = new IA64FloatRegister(77); + F78 = new IA64FloatRegister(78); + F79 = new IA64FloatRegister(79); + F80 = new IA64FloatRegister(80); + F81 = new IA64FloatRegister(81); + F82 = new IA64FloatRegister(82); + F83 = new IA64FloatRegister(83); + F84 = new IA64FloatRegister(84); + F85 = new IA64FloatRegister(85); + F86 = new IA64FloatRegister(86); + F87 = new IA64FloatRegister(87); + F88 = new IA64FloatRegister(88); + F89 = new IA64FloatRegister(89); + F90 = new IA64FloatRegister(90); + F91 = new IA64FloatRegister(91); + F92 = new IA64FloatRegister(92); + F93 = new IA64FloatRegister(93); + F94 = new IA64FloatRegister(94); + F95 = new IA64FloatRegister(95); + F96 = new IA64FloatRegister(96); + F97 = new IA64FloatRegister(97); + F98 = new IA64FloatRegister(98); + F99 = new IA64FloatRegister(99); + F100 = new IA64FloatRegister(100); + F101 = new IA64FloatRegister(101); + F102 = new IA64FloatRegister(102); + F103 = new IA64FloatRegister(103); + F104 = new IA64FloatRegister(104); + F105 = new IA64FloatRegister(105); + F106 = new IA64FloatRegister(106); + F107 = new IA64FloatRegister(107); + F108 = new IA64FloatRegister(108); + F109 = new IA64FloatRegister(109); + F110 = new IA64FloatRegister(110); + F111 = new IA64FloatRegister(111); + F112 = new IA64FloatRegister(112); + F113 = new IA64FloatRegister(113); + F114 = new IA64FloatRegister(114); + F115 = new IA64FloatRegister(115); + F116 = new IA64FloatRegister(116); + F117 = new IA64FloatRegister(117); + F118 = new IA64FloatRegister(118); + F119 = new IA64FloatRegister(119); + F120 = new IA64FloatRegister(120); + F121 = new IA64FloatRegister(121); + F122 = new IA64FloatRegister(122); + F123 = new IA64FloatRegister(123); + F124 = new IA64FloatRegister(124); + F125 = new IA64FloatRegister(125); + F126 = new IA64FloatRegister(126); + F127 = new IA64FloatRegister(127); + + registers = (new IA64FloatRegister[] { + F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, + F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, + F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, + F30, F31, F32, F33, F34, F35, F36, F37, F38, F39, + F40, F41, F42, F43, F44, F45, F46, F47, F48, F49, + F50, F51, F52, F53, F54, F55, F56, F57, F58, F59, + F60, F61, F62, F63, F64, F65, F66, F67, F68, F69, + F70, F71, F72, F73, F74, F75, F76, F77, F78, F79, + F80, F81, F82, F83, F84, F85, F86, F87, F88, F89, + F90, F91, F92, F93, F94, F95, F96, F97, F98, F99, + F100, F101, F102, F103, F104, F105, F106, F107, F108, F109, + F110, F111, F112, F113, F114, F115, F116, F117, F118, F119, + F120, F121, F122, F123, F124, F125, F126, F127 + }); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64Helper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64Helper.java new file mode 100644 index 00000000000..f539c6a85d2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64Helper.java @@ -0,0 +1,54 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.ia64; + +import sun.jvm.hotspot.asm.*; + +public class IA64Helper implements CPUHelper { + public Disassembler createDisassembler(long startPc, byte[] code) { + // FIXME: IA64 disassembler not implemented + return null; + } + + public Register getIntegerRegister(int num) { + // FIXME: IA64 disassembler not implemented + return null; + } + + public Register getFloatRegister(int num) { + // FIXME: IA64 disassembler not implemented + return null; + } + + public Register getStackPointer() { + // FIXME: IA64 disassembler not implemented + return null; + } + + public Register getFramePointer() { + // FIXME: IA64 disassembler not implemented + return null; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64Register.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64Register.java new file mode 100644 index 00000000000..aec9d8ba5b0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64Register.java @@ -0,0 +1,76 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.ia64; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class IA64Register extends Register { + + // + private static final int STACKED_BASE = 32; + private static final int STACKED_END = 127; + + // We put application registers here too rather than separate types + private static final int APPL_BASE = 128; + + private static final int nofRegisters = 129; // total number of registers + + /** Constructor for an explicitly numbered register */ + public IA64Register(int number) { + super(number); + } + + public int getNumberOfRegisters() { + return nofRegisters; + } + + public boolean isStacked() { + return (32 <= getNumber()); + } + + /** NOTE: this returns an offset in BYTES in this system! */ + public long spOffsetInSavedWindow() { + return 0; + } + + public String toString() { + return IA64Registers.getRegisterName(number); + } + + public boolean isFramePointer() { + return number == APPL_BASE; + } + + public boolean isStackPointer() { + return number == 12; + } + + public boolean isFloat() { + return false; + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64Registers.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64Registers.java new file mode 100644 index 00000000000..19279351204 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/ia64/IA64Registers.java @@ -0,0 +1,353 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.ia64; + +import sun.jvm.hotspot.utilities.*; + +public class IA64Registers { + + public static final IA64Register GR0; + public static final IA64Register GR1; + public static final IA64Register GR2; + public static final IA64Register GR3; + public static final IA64Register GR4; + public static final IA64Register GR5; + public static final IA64Register GR6; + public static final IA64Register GR7; + public static final IA64Register GR8; + public static final IA64Register GR9; + public static final IA64Register GR10; + public static final IA64Register GR11; + public static final IA64Register GR12; + public static final IA64Register GR13; + public static final IA64Register GR14; + public static final IA64Register GR15; + public static final IA64Register GR16; + public static final IA64Register GR17; + public static final IA64Register GR18; + public static final IA64Register GR19; + public static final IA64Register GR20; + public static final IA64Register GR21; + public static final IA64Register GR22; + public static final IA64Register GR23; + public static final IA64Register GR24; + public static final IA64Register GR25; + public static final IA64Register GR26; + public static final IA64Register GR27; + public static final IA64Register GR28; + public static final IA64Register GR29; + public static final IA64Register GR30; + public static final IA64Register GR31; + public static final IA64Register GR32; + public static final IA64Register GR33; + public static final IA64Register GR34; + public static final IA64Register GR35; + public static final IA64Register GR36; + public static final IA64Register GR37; + public static final IA64Register GR38; + public static final IA64Register GR39; + public static final IA64Register GR40; + public static final IA64Register GR41; + public static final IA64Register GR42; + public static final IA64Register GR43; + public static final IA64Register GR44; + public static final IA64Register GR45; + public static final IA64Register GR46; + public static final IA64Register GR47; + public static final IA64Register GR48; + public static final IA64Register GR49; + public static final IA64Register GR50; + public static final IA64Register GR51; + public static final IA64Register GR52; + public static final IA64Register GR53; + public static final IA64Register GR54; + public static final IA64Register GR55; + public static final IA64Register GR56; + public static final IA64Register GR57; + public static final IA64Register GR58; + public static final IA64Register GR59; + public static final IA64Register GR60; + public static final IA64Register GR61; + public static final IA64Register GR62; + public static final IA64Register GR63; + public static final IA64Register GR64; + public static final IA64Register GR65; + public static final IA64Register GR66; + public static final IA64Register GR67; + public static final IA64Register GR68; + public static final IA64Register GR69; + public static final IA64Register GR70; + public static final IA64Register GR71; + public static final IA64Register GR72; + public static final IA64Register GR73; + public static final IA64Register GR74; + public static final IA64Register GR75; + public static final IA64Register GR76; + public static final IA64Register GR77; + public static final IA64Register GR78; + public static final IA64Register GR79; + public static final IA64Register GR80; + public static final IA64Register GR81; + public static final IA64Register GR82; + public static final IA64Register GR83; + public static final IA64Register GR84; + public static final IA64Register GR85; + public static final IA64Register GR86; + public static final IA64Register GR87; + public static final IA64Register GR88; + public static final IA64Register GR89; + public static final IA64Register GR90; + public static final IA64Register GR91; + public static final IA64Register GR92; + public static final IA64Register GR93; + public static final IA64Register GR94; + public static final IA64Register GR95; + public static final IA64Register GR96; + public static final IA64Register GR97; + public static final IA64Register GR98; + public static final IA64Register GR99; + public static final IA64Register GR100; + public static final IA64Register GR101; + public static final IA64Register GR102; + public static final IA64Register GR103; + public static final IA64Register GR104; + public static final IA64Register GR105; + public static final IA64Register GR106; + public static final IA64Register GR107; + public static final IA64Register GR108; + public static final IA64Register GR109; + public static final IA64Register GR110; + public static final IA64Register GR111; + public static final IA64Register GR112; + public static final IA64Register GR113; + public static final IA64Register GR114; + public static final IA64Register GR115; + public static final IA64Register GR116; + public static final IA64Register GR117; + public static final IA64Register GR118; + public static final IA64Register GR119; + public static final IA64Register GR120; + public static final IA64Register GR121; + public static final IA64Register GR122; + public static final IA64Register GR123; + public static final IA64Register GR124; + public static final IA64Register GR125; + public static final IA64Register GR126; + public static final IA64Register GR127; + + public static final IA64Register AR_BSP; + + public static final int NUM_REGISTERS = 129; + private static final IA64Register registers[]; + + static { + GR0 = new IA64Register(0); + GR1 = new IA64Register(1); + GR2 = new IA64Register(2); + GR3 = new IA64Register(3); + GR4 = new IA64Register(4); + GR5 = new IA64Register(5); + GR6 = new IA64Register(6); + GR7 = new IA64Register(7); + GR8 = new IA64Register(8); + GR9 = new IA64Register(9); + GR10 = new IA64Register(10); + GR11 = new IA64Register(11); + GR12 = new IA64Register(12); + GR13 = new IA64Register(13); + GR14 = new IA64Register(14); + GR15 = new IA64Register(15); + GR16 = new IA64Register(16); + GR17 = new IA64Register(17); + GR18 = new IA64Register(18); + GR19 = new IA64Register(19); + GR20 = new IA64Register(20); + GR21 = new IA64Register(21); + GR22 = new IA64Register(22); + GR23 = new IA64Register(23); + GR24 = new IA64Register(24); + GR25 = new IA64Register(25); + GR26 = new IA64Register(26); + GR27 = new IA64Register(27); + GR28 = new IA64Register(28); + GR29 = new IA64Register(29); + GR30 = new IA64Register(30); + GR31 = new IA64Register(31); + GR32 = new IA64Register(32); + GR33 = new IA64Register(33); + GR34 = new IA64Register(34); + GR35 = new IA64Register(35); + GR36 = new IA64Register(36); + GR37 = new IA64Register(37); + GR38 = new IA64Register(38); + GR39 = new IA64Register(39); + GR40 = new IA64Register(40); + GR41 = new IA64Register(41); + GR42 = new IA64Register(42); + GR43 = new IA64Register(43); + GR44 = new IA64Register(44); + GR45 = new IA64Register(45); + GR46 = new IA64Register(46); + GR47 = new IA64Register(47); + GR48 = new IA64Register(48); + GR49 = new IA64Register(49); + GR50 = new IA64Register(50); + GR51 = new IA64Register(51); + GR52 = new IA64Register(52); + GR53 = new IA64Register(53); + GR54 = new IA64Register(54); + GR55 = new IA64Register(55); + GR56 = new IA64Register(56); + GR57 = new IA64Register(57); + GR58 = new IA64Register(58); + GR59 = new IA64Register(59); + GR60 = new IA64Register(60); + GR61 = new IA64Register(61); + GR62 = new IA64Register(62); + GR63 = new IA64Register(63); + GR64 = new IA64Register(64); + GR65 = new IA64Register(65); + GR66 = new IA64Register(66); + GR67 = new IA64Register(67); + GR68 = new IA64Register(68); + GR69 = new IA64Register(69); + GR70 = new IA64Register(70); + GR71 = new IA64Register(71); + GR72 = new IA64Register(72); + GR73 = new IA64Register(73); + GR74 = new IA64Register(74); + GR75 = new IA64Register(75); + GR76 = new IA64Register(76); + GR77 = new IA64Register(77); + GR78 = new IA64Register(78); + GR79 = new IA64Register(79); + GR80 = new IA64Register(80); + GR81 = new IA64Register(81); + GR82 = new IA64Register(82); + GR83 = new IA64Register(83); + GR84 = new IA64Register(84); + GR85 = new IA64Register(85); + GR86 = new IA64Register(86); + GR87 = new IA64Register(87); + GR88 = new IA64Register(88); + GR89 = new IA64Register(89); + GR90 = new IA64Register(90); + GR91 = new IA64Register(91); + GR92 = new IA64Register(92); + GR93 = new IA64Register(93); + GR94 = new IA64Register(94); + GR95 = new IA64Register(95); + GR96 = new IA64Register(96); + GR97 = new IA64Register(97); + GR98 = new IA64Register(98); + GR99 = new IA64Register(99); + GR100 = new IA64Register(100); + GR101 = new IA64Register(101); + GR102 = new IA64Register(102); + GR103 = new IA64Register(103); + GR104 = new IA64Register(104); + GR105 = new IA64Register(105); + GR106 = new IA64Register(106); + GR107 = new IA64Register(107); + GR108 = new IA64Register(108); + GR109 = new IA64Register(109); + GR110 = new IA64Register(110); + GR111 = new IA64Register(111); + GR112 = new IA64Register(112); + GR113 = new IA64Register(113); + GR114 = new IA64Register(114); + GR115 = new IA64Register(115); + GR116 = new IA64Register(116); + GR117 = new IA64Register(117); + GR118 = new IA64Register(118); + GR119 = new IA64Register(119); + GR120 = new IA64Register(120); + GR121 = new IA64Register(121); + GR122 = new IA64Register(122); + GR123 = new IA64Register(123); + GR124 = new IA64Register(124); + GR125 = new IA64Register(125); + GR126 = new IA64Register(126); + GR127 = new IA64Register(127); + + AR_BSP = new IA64Register(128); + + registers = (new IA64Register[] { + GR0, GR1, GR2, GR3, GR4, GR5, GR6, GR7, GR8, GR9, + GR10, GR11, GR12, GR13, GR14, GR15, GR16, GR17, GR18, GR19, + GR20, GR21, GR22, GR23, GR24, GR25, GR26, GR27, GR28, GR29, + GR30, GR31, GR32, GR33, GR34, GR35, GR36, GR37, GR38, GR39, + GR40, GR41, GR42, GR43, GR44, GR45, GR46, GR47, GR48, GR49, + GR50, GR51, GR52, GR53, GR54, GR55, GR56, GR57, GR58, GR59, + GR60, GR61, GR62, GR63, GR64, GR65, GR66, GR67, GR68, GR69, + GR70, GR71, GR72, GR73, GR74, GR75, GR76, GR77, GR78, GR79, + GR80, GR81, GR82, GR83, GR84, GR85, GR86, GR87, GR88, GR89, + GR90, GR91, GR92, GR93, GR94, GR95, GR96, GR97, GR98, GR99, + GR100, GR101, GR102, GR103, GR104, GR105, GR106, GR107, GR108, GR109, + GR110, GR111, GR112, GR113, GR114, GR115, GR116, GR117, GR118, GR119, + GR120, GR121, GR122, GR123, GR124, GR125, GR126, GR127, AR_BSP + }); + + } + + public static final IA64Register FP = AR_BSP; + public static final IA64Register SP = GR12; + + + /** Prefer to use this instead of the constant above */ + public static int getNumRegisters() { + return NUM_REGISTERS; + } + + + public static String getRegisterName(int regNum) { + if (regNum < 0 || regNum >= NUM_REGISTERS) { + return "[Illegal register " + regNum + "]"; + } + + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid integer register number!"); + } + + if (regNum == 128 ) { + return "BSP"; + } + + if (regNum == 12) { + return "SP"; + } + + return "R" + regNum; + + } + + public static IA64Register getRegister(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid register number!"); + } + + return registers[regNum]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceLdstubDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceLdstubDecoder.java new file mode 100644 index 00000000000..86118b3276c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceLdstubDecoder.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class AlternateSpaceLdstubDecoder extends LdstubDecoder { + AlternateSpaceLdstubDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, + SPARCInstructionFactory factory) { + setAddressSpace(instruction, addr); + return factory.newLdstubInstruction(name, addr, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceLoadDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceLoadDecoder.java new file mode 100644 index 00000000000..eb1198e8137 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceLoadDecoder.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class AlternateSpaceLoadDecoder extends LoadDecoder { + AlternateSpaceLoadDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, + SPARCInstructionFactory factory) { + setAddressSpace(instruction, addr); + return factory.newLoadInstruction(name, op3, addr, rd, dataType); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceStoreDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceStoreDecoder.java new file mode 100644 index 00000000000..6709bde183f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceStoreDecoder.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class AlternateSpaceStoreDecoder extends StoreDecoder { + AlternateSpaceStoreDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + protected Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, + SPARCInstructionFactory factory) { + setAddressSpace(instruction, addr); + return factory.newStoreInstruction(name, op3, addr, rd, dataType); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceSwapDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceSwapDecoder.java new file mode 100644 index 00000000000..a8d8a61f185 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/AlternateSpaceSwapDecoder.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class AlternateSpaceSwapDecoder extends SwapDecoder { + AlternateSpaceSwapDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, + SPARCInstructionFactory factory) { + setAddressSpace(instruction, addr); + return factory.newSwapInstruction(name, addr, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ArithmeticDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ArithmeticDecoder.java new file mode 100644 index 00000000000..5dddec9c45a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ArithmeticDecoder.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class ArithmeticDecoder extends Format3ADecoder { + ArithmeticDecoder(int op3, String name, int rtlOperation) { + super(op3, name, rtlOperation); + } + + Instruction decodeFormat3AInstruction(int instruction, + SPARCRegister rs1, + ImmediateOrRegister operand2, + SPARCRegister rd, + SPARCInstructionFactory factory) { + return factory.newArithmeticInstruction(name, op3, rtlOperation, rs1, operand2, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/BranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/BranchDecoder.java new file mode 100644 index 00000000000..b11eb52b152 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/BranchDecoder.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class BranchDecoder extends InstructionDecoder { + + // format 2 - condition code names. + // Appendix F - Opcodes and Condition Codes - Page 231 - Table F-7. + static final String integerConditionNames[] = { + "bn", "be", "ble", "bl", "bleu", "bcs", "bneg", "bvs", + "ba", "bne", "bg", "bge", "bgu", "bcc", "bpos", "bvc" + }; + + static final String integerAnnuledConditionNames[] = { + "bn,a", "be,a", "ble,a", "bl,a", "bleu,a", "bcs,a", "bneg,a", "bvs,a", + "ba,a", "bne,a", "bg,a", "bge,a", "bgu,a", "bcc,a", "bpos,a", "bvc,a" + }; + + // format 2 - condition code names. + // Appendix F - Opcodes and Condition Codes - Page 231 - Table F-7. + static final String floatConditionNames[] = { + "fbn", "fbne", "fblg", "fbul", "fbl", "fbug", "fbg", "fbu", + "fba", "fbe", "fbue", "fbge", "fbuge", "fble", "fbule", "fbo" + }; + + static final String floatAnnuledConditionNames[] = { + "fbn,a", "fbne,a", "fblg,a", "fbul,a", "fbl,a", "fbug,a", "fbg,a", "fbu,a", + "fba,a", "fbe,a", "fbue,a", "fbge,a", "fbuge,a", "fble,a", "fbule,a", "fbo,a" + }; + + static boolean getAnnuledBit(int instruction) { + return (instruction & ANNUL_MASK) != 0; + } + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + boolean isAnnuled = getAnnuledBit(instruction); + int conditionCode = getConditionCode(instruction); + String conditionName = getConditionName(conditionCode, isAnnuled); + int offset = extractSignedIntFromNBits(instruction, 22); + // word align the offset by right shifting 2 bits + offset <<= 2; + PCRelativeAddress addr = new PCRelativeAddress(offset); + return factory.newBranchInstruction(conditionName, addr, isAnnuled, conditionCode); + } + + abstract String getConditionName(int conditionCode, boolean isAnnuled); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/CallDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/CallDecoder.java new file mode 100644 index 00000000000..b9803ffefb8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/CallDecoder.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class CallDecoder extends InstructionDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + // sign extend, word align the offset + int offset = (instruction & DISP_30_MASK) << 2; + return factory.newCallInstruction(new PCRelativeAddress(offset)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/CoprocessorBranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/CoprocessorBranchDecoder.java new file mode 100644 index 00000000000..cee2989f535 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/CoprocessorBranchDecoder.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +// format 2 - condition code names. +// Appendix F - Opcodes and Condition Codes - Page 231 - Table F-7. + +class CoprocessorBranchDecoder extends BranchDecoder { + private static final String coprocessorConditionNames[] = { + "cbn", "cb123", "cb12", "cb13", "cb1", "cb23", "cb2", "cb3", + "cba", "cb0", "cb03", "cb02", "cb023", "cb01", "cb013", "cb012" + }; + + private static final String coprocessorAnnuledConditionNames[] = { + "cbn,a", "cb123,a", "cb12,a", "cb13,a", "cb1,a", "cb23,a", "cb2,a", "cb3,a", + "cba,a", "cb0,a", "cb03,a", "cb02,a", "cb023,a", "cb01,a", "cb013,a", "cb012,a" + }; + + String getConditionName(int conditionCode, boolean isAnnuled) { + return isAnnuled ? coprocessorAnnuledConditionNames[conditionCode] + : coprocessorConditionNames[conditionCode]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/CoprocessorDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/CoprocessorDecoder.java new file mode 100644 index 00000000000..ab44bed8d04 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/CoprocessorDecoder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class CoprocessorDecoder extends InstructionDecoder { + private int op3; + CoprocessorDecoder(int op3) { + this.op3 = op3; + } + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + int rs1Num = getSourceRegister1(instruction); + int rs2Num = getSourceRegister2(instruction); + int rdNum = getDestinationRegister(instruction); + + return factory.newCoprocessorInstruction(instruction, op3, + (instruction & OPC_MASK) >> OPF_START_BIT, + rs1Num, rs2Num, rdNum); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FP2RegisterDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FP2RegisterDecoder.java new file mode 100644 index 00000000000..693a1366188 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FP2RegisterDecoder.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.Assert; + +class FP2RegisterDecoder extends FloatDecoder { + + FP2RegisterDecoder(int opf, String name, int srcType, int resultType) { + super(opf, name, srcType, resultType); + } + + Instruction decodeFloatInstruction(int instruction, + SPARCRegister rs1, SPARCRegister rs2, + SPARCRegister rd, + SPARCInstructionFactory factory) { + if (Assert.ASSERTS_ENABLED) + Assert.that(rs2.isFloat() && rd.isFloat(), "rs2, rd have to be float registers"); + + return factory.newFP2RegisterInstruction(name, opf, (SPARCFloatRegister)rs2, (SPARCFloatRegister)rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FPArithmeticDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FPArithmeticDecoder.java new file mode 100644 index 00000000000..45a548ac230 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FPArithmeticDecoder.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.Assert; + +class FPArithmeticDecoder extends FloatDecoder { + private final int rtlOperation; + + FPArithmeticDecoder(int opf, String name, int rtlOperation, + int src1Type, int src2Type, int resultType) { + super(opf, name, src1Type, src2Type, resultType); + this.rtlOperation = rtlOperation; + } + + Instruction decodeFloatInstruction(int instruction, + SPARCRegister rs1, SPARCRegister rs2, + SPARCRegister rd, + SPARCInstructionFactory factory) { + if (Assert.ASSERTS_ENABLED) + Assert.that(rs1.isFloat() && rs2.isFloat() && rd.isFloat(), "rs1, rs2 and rd must be floats"); + return factory.newFPArithmeticInstruction(name, opf, rtlOperation, + (SPARCFloatRegister)rs1, + (SPARCFloatRegister)rs2, + (SPARCFloatRegister)rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FPMoveDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FPMoveDecoder.java new file mode 100644 index 00000000000..2bedbdc1a50 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FPMoveDecoder.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.Assert; + +class FPMoveDecoder extends FloatDecoder { + + FPMoveDecoder(int opf, String name, int srcType, int resultType) { + super(opf, name, srcType, resultType); + } + + Instruction decodeFloatInstruction(int instruction, + SPARCRegister rs1, SPARCRegister rs2, + SPARCRegister rd, + SPARCInstructionFactory factory) { + if (Assert.ASSERTS_ENABLED) + Assert.that(rs2.isFloat() && rd.isFloat(), "rs2, rd have to be float registers"); + + return factory.newFPMoveInstruction(name, opf, (SPARCFloatRegister)rs2, (SPARCFloatRegister)rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FPopDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FPopDecoder.java new file mode 100644 index 00000000000..33d66a40e8f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FPopDecoder.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class FPopDecoder extends InstructionDecoder { + abstract InstructionDecoder getOpfDecoder(int opf); + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + int opf = getOpf(instruction); + InstructionDecoder decoder = getOpfDecoder(opf); + return (decoder == null) ? factory.newIllegalInstruction(instruction) + : decoder.decode(instruction, factory); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FloatBranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FloatBranchDecoder.java new file mode 100644 index 00000000000..39f6dbc4a7c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FloatBranchDecoder.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +class FloatBranchDecoder extends BranchDecoder { + String getConditionName(int conditionCode, boolean isAnnuled) { + return isAnnuled ? floatAnnuledConditionNames[conditionCode] + : floatConditionNames[conditionCode]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FloatDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FloatDecoder.java new file mode 100644 index 00000000000..86d87553a71 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FloatDecoder.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class FloatDecoder extends InstructionDecoder { + final int opf; + final String name; + final int numSources; // 1 or 2; + final int src1Type; // RTLDT_FL_SINGLE, _DOUBLE, _QUAD + final int src2Type; // RTLDT_FL_SINGLE, _DOUBLE, _QUAD + final int resultType; // RTLDT_FL_SINGLE, _DOUBLE, _QUAD + + FloatDecoder(int opf, String name, int src1Type, int src2Type, int resultType) { + this.opf = opf; + this.name = name; + numSources = 2; + this.src1Type = src1Type; + this.src2Type = src2Type; + this.resultType = resultType; + } + + FloatDecoder(int opf, String name, int src2Type, int resultType) { + this.opf = opf; + this.name = name; + numSources = 1; + this.src1Type = RTLOP_UNKNOWN; + this.src2Type = src2Type; + this.resultType = resultType; + } + + abstract Instruction decodeFloatInstruction(int instruction, + SPARCRegister rs1, SPARCRegister rs2, SPARCRegister rd, + SPARCInstructionFactory factory); + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + int rs1Num = getSourceRegister1(instruction); + int rs2Num = getSourceRegister2(instruction); + int rdNum = getDestinationRegister(instruction); + + SPARCRegister rs1 = null; + if (numSources == 2) { + rs1 = RegisterDecoder.decode(src1Type, rs1Num); + if (rs1 == null) { + return factory.newIllegalInstruction(instruction); + } + } + + SPARCRegister rd = RegisterDecoder.decode(resultType, rdNum); + SPARCRegister rs2 = RegisterDecoder.decode(src2Type, rs2Num); + if (rd == null || rs2 == null) { + return factory.newIllegalInstruction(instruction); + } + + return decodeFloatInstruction(instruction, rs1, rs2, rd, factory); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FlushDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FlushDecoder.java new file mode 100644 index 00000000000..736d975d03a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/FlushDecoder.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class FlushDecoder extends MemoryInstructionDecoder { + FlushDecoder() { + super(FLUSH, "flush", RTLDT_UNKNOWN); + } + + Instruction decodeMemoryInstruction(int instruction, SPARCRegisterIndirectAddress addr, + SPARCRegister rd, SPARCInstructionFactory factory) { + return factory.newFlushInstruction(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/Format3ADecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/Format3ADecoder.java new file mode 100644 index 00000000000..39b04e68fcf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/Format3ADecoder.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class Format3ADecoder extends InstructionDecoder + implements /* imports */ RTLOperations { + final int op3; + final String name; + final int rtlOperation; + + Format3ADecoder(int op3, String name, int rtlOperation) { + this.op3 = op3; + this.name = name; + this.rtlOperation = rtlOperation; + } + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCRegister rs1 = SPARCRegisters.getRegister(getSourceRegister1(instruction)); + SPARCRegister rd = SPARCRegisters.getRegister(getDestinationRegister(instruction)); + ImmediateOrRegister operand2 = getOperand2(instruction); + return decodeFormat3AInstruction(instruction, rs1, operand2, rd, factory); + } + + abstract Instruction decodeFormat3AInstruction(int instruction, + SPARCRegister rs1, + ImmediateOrRegister operand2, + SPARCRegister rd, + SPARCInstructionFactory factory); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/IllegalInstructionDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/IllegalInstructionDecoder.java new file mode 100644 index 00000000000..73df4d63b7b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/IllegalInstructionDecoder.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class IllegalInstructionDecoder extends InstructionDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + return factory.newIllegalInstruction(instruction); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/InstructionDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/InstructionDecoder.java new file mode 100644 index 00000000000..0fa6beb1bf0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/InstructionDecoder.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +// basic instruction decoder class +abstract class InstructionDecoder implements /* imports */ SPARCOpcodes , RTLDataTypes, RTLOperations { + // some general utility functions - for format 2, 3 & 3A instructions + + static int extractSignedIntFromNBits(int value, int num_bits) { + return (value << (32 - num_bits)) >> (32 - num_bits); + } + + // "rs1" + static int getSourceRegister1(int instruction) { + return (instruction & RS1_MASK) >>> RS1_START_BIT; + } + + // "rs2" + static int getSourceRegister2(int instruction) { + return (instruction & RS2_MASK); + } + + // "rd" + static int getDestinationRegister(int instruction) { + return (instruction & RD_MASK) >>> RD_START_BIT; + } + + static int getConditionCode(int instruction) { + return (instruction & CONDITION_CODE_MASK) >>> CONDITION_CODE_START_BIT; + } + + // "i" bit - used to indicate whether second component in an indirect + // address is immediate value or a register. (format 3 & 3A). + + static boolean isIBitSet(int instruction) { + return (instruction & I_MASK) != 0; + } + + static ImmediateOrRegister getOperand2(int instruction) { + boolean iBit = isIBitSet(instruction); + ImmediateOrRegister operand2 = null; + if (iBit) { + operand2 = new Immediate(new Short((short)extractSignedIntFromNBits(instruction, 13))); + } else { + operand2 = SPARCRegisters.getRegister(getSourceRegister2(instruction)); + } + return operand2; + } + + // "opf" - floating point operation code. + static int getOpf(int instruction) { + return (instruction & OPF_MASK) >>> OPF_START_BIT; + } + + abstract Instruction decode(int instruction, SPARCInstructionFactory factory); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/IntegerBranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/IntegerBranchDecoder.java new file mode 100644 index 00000000000..96b6ebd3f57 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/IntegerBranchDecoder.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +class IntegerBranchDecoder extends BranchDecoder { + String getConditionName(int conditionCode, boolean isAnnuled) { + return isAnnuled ? integerAnnuledConditionNames[conditionCode] + : integerConditionNames[conditionCode]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/JmplDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/JmplDecoder.java new file mode 100644 index 00000000000..7a543a82526 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/JmplDecoder.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class JmplDecoder extends MemoryInstructionDecoder { + JmplDecoder() { + super(JMPL, "jmpl", RTLDT_UNSIGNED_WORD); + } + + Instruction decodeMemoryInstruction(int instruction, SPARCRegisterIndirectAddress addr, + SPARCRegister rd, SPARCInstructionFactory factory) { + // this may be most probably indirect call or ret or retl + Instruction instr = null; + if (rd == SPARCRegisters.O7) { + instr = factory.newIndirectCallInstruction(addr, rd); + } else if (rd == SPARCRegisters.G0) { + int disp = (int) addr.getDisplacement(); + Register base = addr.getBase(); + if (base == SPARCRegisters.I7 && disp == 8) { + instr = factory.newReturnInstruction(addr, rd, false /* not leaf */); + } else if (base == SPARCRegisters.O7 && disp == 8) { + instr = factory.newReturnInstruction(addr, rd, true /* leaf */); + } else { + instr = factory.newJmplInstruction(addr, rd); + } + } else { + instr = factory.newJmplInstruction(addr, rd); + } + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/LdstubDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/LdstubDecoder.java new file mode 100644 index 00000000000..1f142eee70e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/LdstubDecoder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class LdstubDecoder extends MemoryInstructionDecoder { + LdstubDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, + SPARCInstructionFactory factory) { + return factory.newLdstubInstruction(name, addr, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/LoadDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/LoadDecoder.java new file mode 100644 index 00000000000..6a01ccdadc1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/LoadDecoder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class LoadDecoder extends MemoryInstructionDecoder { + LoadDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, + SPARCInstructionFactory factory) { + return factory.newLoadInstruction(name, op3, addr, rd, dataType); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/LogicDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/LogicDecoder.java new file mode 100644 index 00000000000..03db640bca8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/LogicDecoder.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class LogicDecoder extends Format3ADecoder { + LogicDecoder(int op3, String name, int rtlOperation) { + super(op3, name, rtlOperation); + } + + Instruction decodeFormat3AInstruction(int instruction, + SPARCRegister rs1, + ImmediateOrRegister operand2, + SPARCRegister rd, + SPARCInstructionFactory factory) { + Instruction instr = null; + if (op3 == OR && rs1 == SPARCRegisters.G0 && rd != SPARCRegisters.G0) { + instr = factory.newMoveInstruction(name, op3, operand2, rd); + } else { + instr = factory.newLogicInstruction(name, op3, rtlOperation, rs1, operand2, rd); + } + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/MemoryInstructionDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/MemoryInstructionDecoder.java new file mode 100644 index 00000000000..c6a53363cc4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/MemoryInstructionDecoder.java @@ -0,0 +1,86 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class MemoryInstructionDecoder extends InstructionDecoder { + final int op3; + final String name; + final int dataType; + + SPARCRegisterIndirectAddress newRegisterIndirectAddress(SPARCRegister rs1, SPARCRegister rs2) { + return new SPARCRegisterIndirectAddress(rs1, rs2); + } + + SPARCRegisterIndirectAddress newRegisterIndirectAddress(SPARCRegister rs1, int offset) { + return new SPARCRegisterIndirectAddress(rs1, offset); + } + + static void setAddressSpace(int instruction, SPARCRegisterIndirectAddress addr) { + int asi = (instruction & ASI_MASK) >>> ASI_START_BIT; + addr.setAddressSpace(asi); + } + + SPARCRegisterIndirectAddress getRegisterIndirectAddress(int instruction) { + SPARCRegister rs1 = SPARCRegisters.getRegister(getSourceRegister1(instruction)); + boolean iBit = isIBitSet(instruction); + SPARCRegisterIndirectAddress addr = null; + if (iBit) { + int simm13 = extractSignedIntFromNBits(instruction, 13); + addr = newRegisterIndirectAddress(rs1,simm13); + } else { + SPARCRegister rs2 = SPARCRegisters.getRegister(getSourceRegister2(instruction)); + addr = newRegisterIndirectAddress(rs1,rs2); + } + return addr; + } + + MemoryInstructionDecoder(int op3, String name, int dataType) { + this.op3 = op3; + this.name = name; + this.dataType = dataType; + } + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCRegisterIndirectAddress addr = getRegisterIndirectAddress(instruction); + SPARCRegister rd = getDestination(instruction); + boolean isV9Okay = (factory instanceof SPARCV9InstructionFactory); + if ( (rd == null) || (! isV9Okay && rd.isV9Only()) ) + return factory.newIllegalInstruction(instruction); + + return decodeMemoryInstruction(instruction, addr, rd, factory); + } + + SPARCRegister getDestination(int instruction) { + int rdNum = getDestinationRegister(instruction); + SPARCRegister rd = RegisterDecoder.decode(dataType, rdNum); + return rd; + } + + abstract Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, SPARCInstructionFactory factory); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ReadDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ReadDecoder.java new file mode 100644 index 00000000000..ea49f9f40f8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ReadDecoder.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class ReadDecoder extends ReadWriteDecoder { + ReadDecoder(int specialRegNum) { + super(specialRegNum); + } + + Instruction decodeReadWrite(int instruction, SPARCInstructionFactory factory, + int rs1Num, int rdNum) { + Instruction instr = null; + int specialReg = specialRegNum; + if (rs1Num == 0) + specialReg = SPARCSpecialRegisters.Y; + if (rs1Num == 15 && rdNum == 0) { + instr = factory.newStbarInstruction(); + } else { + instr = factory.newReadInstruction(specialReg, rs1Num, + SPARCRegisters.getRegister(rdNum)); + } + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ReadWriteDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ReadWriteDecoder.java new file mode 100644 index 00000000000..5d6bb75c73c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ReadWriteDecoder.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class ReadWriteDecoder extends InstructionDecoder { + final int specialRegNum; + + abstract Instruction decodeReadWrite(int instruction, + SPARCInstructionFactory factory, + int rs1Num, int rdNum); + + ReadWriteDecoder(int specialRegNum) { + this.specialRegNum = specialRegNum; + } + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + Instruction instr = null; + int rs1Num = getSourceRegister1(instruction); + int rdNum = getDestinationRegister(instruction); + return decodeReadWrite(instruction, factory, rs1Num, rdNum); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/RegisterDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/RegisterDecoder.java new file mode 100644 index 00000000000..68d77c272ba --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/RegisterDecoder.java @@ -0,0 +1,85 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.RTLDataTypes; + +class RegisterDecoder implements /* imports */ RTLDataTypes { + // refer to page 40 - section 5.1.4.1 - Floating-point Register Number Encoding + private static SPARCFloatRegister decodeDouble(int num) { + // 6 bit double precision registers are encoded in 5 bits as + // b<4> b<3> b<2> b<1> b<5>. + + boolean lsb = (0x1 & num) != 0; + if (lsb) + num |= 0x20; // 10000b + + if ((num % 2) != 0) + return null; + + return SPARCFloatRegisters.getRegister(num); + } + + private static SPARCFloatRegister decodeQuad(int num) { + // 6 bit quad precision registers are encoded in 5 bits as + // b<4> b<3> b<2> 0 b<5> + + boolean lsb = (0x1 & num) != 0; + if (lsb) + num |= 0x20; // 10000b + + if ((num % 4) != 0) + return null; + + return SPARCFloatRegisters.getRegister(num); + } + + static SPARCRegister decode(int dataType, int regNum) { + regNum &= 0x1F; // mask out all but lsb 5 bits + SPARCRegister result = null; + switch (dataType) { + case RTLDT_FL_SINGLE: + result = SPARCFloatRegisters.getRegister(regNum); + break; + + case RTLDT_FL_DOUBLE: + result = decodeDouble(regNum); + break; + + case RTLDT_FL_QUAD: + result = decodeQuad(regNum); + break; + + case RTLDT_UNKNOWN: + result = null; + break; + + default: // some integer register + result = SPARCRegisters.getRegister(regNum); + break; + } + return result; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/RestoreDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/RestoreDecoder.java new file mode 100644 index 00000000000..06230a158da --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/RestoreDecoder.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class RestoreDecoder extends Format3ADecoder { + RestoreDecoder() { + super(RESTORE, "restore", RTLOP_UNKNOWN); + } + + Instruction decodeFormat3AInstruction(int instruction, + SPARCRegister rs1, + ImmediateOrRegister operand2, + SPARCRegister rd, + SPARCInstructionFactory factory) { + return factory.newRestoreInstruction(rs1, operand2, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/RettDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/RettDecoder.java new file mode 100644 index 00000000000..4dd7ab1961a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/RettDecoder.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class RettDecoder extends MemoryInstructionDecoder { + RettDecoder() { + super(RETT, "rett", RTLDT_UNKNOWN); + } + + Instruction decodeMemoryInstruction(int instruction, SPARCRegisterIndirectAddress addr, + SPARCRegister rd, SPARCInstructionFactory factory) { + return factory.newRettInstruction(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCArgument.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCArgument.java new file mode 100644 index 00000000000..08935aa804c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCArgument.java @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.*; + +/** SPARCArgument is an abstraction used to represent an outgoing + actual argument or an incoming formal parameter, whether it + resides in memory or in a register, in a manner consistent with + the SPARC Application Binary Interface, or ABI. This is often + referred to as the native or C calling convention. */ + +public class SPARCArgument { + private int number; + private boolean isIn; + + // FIXME: add 64-bit stuff here (support for FP registers) + + /** Only 6 registers may contain integer parameters */ + public static final int NUM_REGISTER_PARAMETERS = 6; + + public SPARCArgument(int number, boolean isIn) { + this.number = number; + this.isIn = isIn; + } + + int getNumber() { return number; } + boolean getIsIn() { return isIn; } + boolean getIsOut() { return !getIsIn(); } + + public SPARCArgument getSuccessor() { return new SPARCArgument(getNumber() + 1, getIsIn()); } + public SPARCArgument asIn() { return new SPARCArgument(getNumber(), true); } + public SPARCArgument asOut() { return new SPARCArgument(getNumber(), false); } + + /** Locating register-based arguments */ + public boolean isRegister() { return number < NUM_REGISTER_PARAMETERS; } + + public SPARCRegister asRegister() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isRegister(), "must be a register argument"); + } + return new SPARCRegister(getIsIn() ? SPARCRegisterType.IN : SPARCRegisterType.OUT, getNumber()); + } + + // locating memory-based arguments (FIXME: elided for now, will + // necessitate creating new SPARCAddress class) + // public SPARCAddress asAddress() { + // if (Assert.ASSERTS_ENABLED) { + // Assert.that(!isRegister(), "must be a memory argument"); + // } + // return addressInFrame(); + // } + // + // /** When applied to a register-based argument, give the corresponding address + // into the 6-word area "into which callee may store register arguments" + // (This is a different place than the corresponding register-save area location.) */ + // public SPARCAddress addressInFrame() const { + // return SPARCAddress( is_in() ? Address::extra_in_argument + // : Address::extra_out_argument, + // _number ); + // } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCArithmeticInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCArithmeticInstruction.java new file mode 100644 index 00000000000..e4642fdad4e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCArithmeticInstruction.java @@ -0,0 +1,103 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCArithmeticInstruction extends SPARCFormat3AInstruction + implements ArithmeticInstruction { + final private int operation; + + public SPARCArithmeticInstruction(String name, int opcode, int operation, SPARCRegister rs1, + ImmediateOrRegister operand2, SPARCRegister rd) { + super(name, opcode, rs1, operand2, rd); + this.operation = operation; + } + + protected String getDescription() { + if (rd == rs1 && operand2.isImmediate()) { + int value = ((Immediate)operand2).getNumber().intValue(); + StringBuffer buf = new StringBuffer(); + switch (opcode) { + case ADD: + buf.append("inc"); + break; + case ADDcc: + buf.append("inccc"); + break; + case SUB: + buf.append("dec"); + break; + case SUBcc: + buf.append("deccc"); + break; + default: + return super.getDescription(); + } + buf.append(spaces); + if (value != 1) { + buf.append(getOperand2String()); buf.append(comma); + } + buf.append(rd.toString()); + return buf.toString(); + } else if (rd == SPARCRegisters.G0 && opcode == SUBcc) { + StringBuffer buf = new StringBuffer(); + buf.append("cmp"); + buf.append(spaces); + buf.append(rs1.toString()); + buf.append(comma); + buf.append(getOperand2String()); + return buf.toString(); + } else if (rs1 == SPARCRegisters.G0 && opcode == SUB && operand2.isRegister()) { + StringBuffer buf = new StringBuffer(); + buf.append("neg"); + buf.append(spaces); + buf.append(operand2.toString()); + if (operand2 != rd) { + buf.append(comma); + buf.append(rd.toString()); + } + return buf.toString(); + } + + return super.getDescription(); + } + + public Operand getArithmeticDestination() { + return getDestinationRegister(); + } + + public Operand[] getArithmeticSources() { + return (new Operand[] { rs1, operand2 }); + } + + public int getOperation() { + return operation; + } + + public boolean isArithmetic() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCAtomicLoadStoreInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCAtomicLoadStoreInstruction.java new file mode 100644 index 00000000000..e0f6b208ddc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCAtomicLoadStoreInstruction.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public abstract class SPARCAtomicLoadStoreInstruction extends SPARCInstruction + implements LoadInstruction, StoreInstruction { + final protected SPARCRegisterIndirectAddress addr; + final protected SPARCRegister rd; + final protected Register[] regs = new Register[1]; + final protected String description; + + public SPARCAtomicLoadStoreInstruction(String name, SPARCRegisterIndirectAddress addr, SPARCRegister rd) { + super(name); + this.addr = addr; + this.rd = rd; + regs[0] = rd; + description = initDescription(); + } + + private String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(addr.toString()); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } + + public Address getLoadSource() { + return addr; + } + + public Address getStoreDestination() { + return addr; + } + + public Register[] getLoadDestinations() { + return regs; + } + + public Register[] getStoreSources() { + return regs; + } + + public boolean isLoad() { + return true; + } + + public boolean isStore() { + return true; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCBranchInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCBranchInstruction.java new file mode 100644 index 00000000000..e69b82642a7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCBranchInstruction.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCBranchInstruction extends SPARCInstruction + implements BranchInstruction { + final protected PCRelativeAddress addr; + final protected int conditionCode; + final protected boolean isAnnuled; + + public SPARCBranchInstruction(String name, PCRelativeAddress addr, boolean isAnnuled, int conditionCode) { + super(name); + this.addr = addr; + this.conditionCode = conditionCode; + this.isAnnuled = isAnnuled; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + long address = addr.getDisplacement() + currentPc; + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(symFinder.getSymbolFor(address)); + return buf.toString(); + } + + public Address getBranchDestination() { + return addr; + } + + public int getConditionCode() { + return conditionCode; + } + + public boolean isAnnuledBranch() { + return isAnnuled; + } + + public boolean isBranch() { + return true; + } + + public boolean isConditional() { + return conditionCode != CONDITION_BN && conditionCode != CONDITION_BA; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCCallInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCCallInstruction.java new file mode 100644 index 00000000000..9382cbb844b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCCallInstruction.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCCallInstruction extends SPARCInstruction + implements CallInstruction { + final private PCRelativeAddress addr; + + public SPARCCallInstruction(PCRelativeAddress addr) { + super("call"); + this.addr = addr; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + long address = addr.getDisplacement() + currentPc; + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(symFinder.getSymbolFor(address)); + return buf.toString(); + } + + public Address getBranchDestination() { + return addr; + } + + public boolean isCall() { + return true; + } + + public boolean isConditional() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCDisassembler.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCDisassembler.java new file mode 100644 index 00000000000..9e202c855d9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCDisassembler.java @@ -0,0 +1,144 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import java.io.*; +import java.util.*; + +public abstract class SPARCDisassembler extends Disassembler + implements /* imports */ SPARCOpcodes, RTLDataTypes, RTLOperations { + + // instruction cache - Map. + protected static Map instructionCache = new HashMap(); + protected final SPARCInstructionFactory factory; + + public SPARCDisassembler(long startPc, byte[] code, SPARCInstructionFactory factory) { + super(startPc, code); + this.factory = factory; + } + + protected static InstructionDecoder illegalDecoder = new IllegalInstructionDecoder(); + protected static InstructionDecoder callDecoder = new CallDecoder(); + + // direct call instruction + protected Instruction decodeFormat1Instruction(int instruction) { + return callDecoder.decode(instruction, factory); + } + + protected abstract InstructionDecoder getFormat2Decoder(int op2); + + protected Instruction decodeFormat2Instruction(int instruction) { + int op2 = (instruction & OP_2_MASK) >>> OP_2_START_BIT; + InstructionDecoder decoder = getFormat2Decoder(op2); + return decoder.decode(instruction, factory); + } + + // "op3" - used in format 3 & 3A instructions - 6 bits width + + protected static int getOp3(int instruction) { + return (instruction & OP_3_MASK) >>> OP_3_START_BIT; + } + + // op3 opcodes is broken up into column and row. MSB 2 bits form column. + // LSB 4 bits form row number. + + protected static int getOp3Row(int op3) { + return op3 & 0xF; + } + + protected static int getOp3Column(int op3) { + return (op3 >>> 4) & 0x3; + } + + protected abstract InstructionDecoder getFormat3Decoder(int row, int column); + + // memory instructions + protected Instruction decodeFormat3Instruction(int instruction) { + int op3 = getOp3(instruction); + int row = getOp3Row(op3); + int column = getOp3Column(op3); + return getFormat3Decoder(row, column).decode(instruction, factory); + } + + protected abstract InstructionDecoder getFormat3ADecoder(int row, int column); + + // arithmetic, logic, shift and the rest + protected Instruction decodeFormat3AInstruction(int instruction) { + int op3 = getOp3(instruction); + int row = getOp3Row(op3); + int column = getOp3Column(op3); + return getFormat3ADecoder(row, column).decode(instruction, factory); + } + + public void decode(InstructionVisitor visitor) { + visitor.prologue(); + try { + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(code)); + int instruction = -1; + int format = -1; + Instruction instr = null; + int len = 0; + + while (len < code.length) { + instr = null; + instruction = dis.readInt(); + // check whether we have this in cache. + instr = (Instruction) instructionCache.get(new Integer(instruction)); + if (instr == null) { + format = (instruction & FORMAT_MASK) >>> FORMAT_START_BIT; + + switch (format) { + case FORMAT_2: // 0 + instr = decodeFormat2Instruction(instruction); + break; + + case FORMAT_1: // 1 + instr = decodeFormat1Instruction(instruction); + break; + + case FORMAT_3A: // 2 + instr = decodeFormat3AInstruction(instruction); + break; + + case FORMAT_3: // 3 + instr = decodeFormat3Instruction(instruction); + break; + } + + // add the new instruction to cache. + instructionCache.put(new Integer(instruction), instr); + } + + visitor.visit(startPc + len, instr); + len += 4; + } + } catch (IOException ioExp) { + // ignore, can't happen + } finally { + visitor.epilogue(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFP2RegisterInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFP2RegisterInstruction.java new file mode 100644 index 00000000000..a5a0daabeaf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFP2RegisterInstruction.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCFP2RegisterInstruction extends SPARCInstruction { + final SPARCFloatRegister rs; + final SPARCFloatRegister rd; + final int opf; + + public SPARCFP2RegisterInstruction(String name, int opf, SPARCFloatRegister rs, SPARCFloatRegister rd) { + super(name); + this.rs = rs; + this.rd = rd; + this.opf = opf; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return getDescription(); + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(rs.toString()); + buf.append(comma); + buf.append(rd.toString()); + + return buf.toString(); + } + + public boolean isFloat() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFPArithmeticInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFPArithmeticInstruction.java new file mode 100644 index 00000000000..87cfaae17b9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFPArithmeticInstruction.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCFPArithmeticInstruction extends SPARCFormat3AInstruction + implements ArithmeticInstruction { + final private SPARCRegister rs2; + final private int rtlOperation; + + public SPARCFPArithmeticInstruction(String name, int opcode, int rtlOperation, + SPARCRegister rs1, SPARCRegister rs2, + SPARCRegister rd) { + super(name, opcode, rs1, rs2, rd); + this.rs2 = rs2; + this.rtlOperation = rtlOperation; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(rs1.toString()); + buf.append(comma); + buf.append(rs2.toString()); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } + + public int getOperation() { + return rtlOperation; + } + + public Operand[] getArithmeticSources() { + return new Operand[] { rs1, rs2 }; + } + + public Operand getArithmeticDestination() { + return rd; + } + + public boolean isFloat() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFPMoveInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFPMoveInstruction.java new file mode 100644 index 00000000000..c9ce8c2f042 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFPMoveInstruction.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCFPMoveInstruction extends SPARCFP2RegisterInstruction + implements MoveInstruction { + + public SPARCFPMoveInstruction(String name, int opf, SPARCFloatRegister rs, SPARCFloatRegister rd) { + super(name, opf, rs, rd); + } + + public Register getMoveDestination() { + return rd; + } + + public ImmediateOrRegister getMoveSource() { + return rs; + } + + public int getMoveOpcode() { + return opf; + } + + public boolean isConditional() { + return false; + } + + public boolean isMove() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFloatRegister.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFloatRegister.java new file mode 100644 index 00000000000..7ac075a86d8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFloatRegister.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.Register; +import sun.jvm.hotspot.utilities.Assert; + +public class SPARCFloatRegister extends SPARCRegister { + + public SPARCFloatRegister(int number) { + super(number); + } + + public int getNumber() { + return number; + } + + public static final int SINGLE_PRECISION = 1; + public static final int DOUBLE_PRECISION = 2; + public static final int QUAD_PRECISION = 3; + + public int getNumber(int width) { + switch (width) { + case SINGLE_PRECISION: + Assert.that(number < 32, "bad single-prec fp register"); + return number; + + case DOUBLE_PRECISION: + Assert.that(number < 64 && (number & 1) == 0, "bad double-prec fp register"); + return number & 0x1e | (number & 0x20) >> 5; + + case QUAD_PRECISION: + Assert.that(number < 64 && (number & 3) == 0, "bad quad-prec fp register"); + return number & 0x1c | (number & 0x20) >> 5; + } + throw new RuntimeException("Invalid floating point width supplied"); + } + + private static final int nofRegisters = 63; + public int getNumberOfRegisters() { + return nofRegisters; + } + + public boolean isFloat() { + return true; + } + + public boolean isFramePointer() { + return false; + } + + public boolean isStackPointer() { + return false; + } + + public boolean isV9Only() { + return number > 31; + } + + public boolean isValid() { + return number >= 0 && number < nofRegisters; + } + + public String toString() { + return SPARCFloatRegisters.getRegisterName(number); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFloatRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFloatRegisters.java new file mode 100644 index 00000000000..b5ce4646b74 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFloatRegisters.java @@ -0,0 +1,153 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.utilities.Assert; + +public class SPARCFloatRegisters { + public static int getNumRegisters() { + return 64; + } + + public static SPARCFloatRegister getRegister(int i) { + Assert.that(i >= 0 && i < 64, "float register number is invalid"); + return registers[i]; + } + + public static String getRegisterName(int i) { + return "%f" + i; + } + + public static final SPARCFloatRegister F0; + public static final SPARCFloatRegister F1; + public static final SPARCFloatRegister F2; + public static final SPARCFloatRegister F3; + public static final SPARCFloatRegister F4; + public static final SPARCFloatRegister F5; + public static final SPARCFloatRegister F6; + public static final SPARCFloatRegister F7; + public static final SPARCFloatRegister F8; + public static final SPARCFloatRegister F9; + public static final SPARCFloatRegister F10; + public static final SPARCFloatRegister F11; + public static final SPARCFloatRegister F12; + public static final SPARCFloatRegister F13; + public static final SPARCFloatRegister F14; + public static final SPARCFloatRegister F15; + public static final SPARCFloatRegister F16; + public static final SPARCFloatRegister F17; + public static final SPARCFloatRegister F18; + public static final SPARCFloatRegister F19; + public static final SPARCFloatRegister F20; + public static final SPARCFloatRegister F21; + public static final SPARCFloatRegister F22; + public static final SPARCFloatRegister F23; + public static final SPARCFloatRegister F24; + public static final SPARCFloatRegister F25; + public static final SPARCFloatRegister F26; + public static final SPARCFloatRegister F27; + public static final SPARCFloatRegister F28; + public static final SPARCFloatRegister F29; + public static final SPARCFloatRegister F30; + public static final SPARCFloatRegister F31; + public static final SPARCFloatRegister F32; + public static final SPARCFloatRegister F34; + public static final SPARCFloatRegister F36; + public static final SPARCFloatRegister F38; + public static final SPARCFloatRegister F40; + public static final SPARCFloatRegister F42; + public static final SPARCFloatRegister F44; + public static final SPARCFloatRegister F46; + public static final SPARCFloatRegister F48; + public static final SPARCFloatRegister F50; + public static final SPARCFloatRegister F52; + public static final SPARCFloatRegister F54; + public static final SPARCFloatRegister F56; + public static final SPARCFloatRegister F58; + public static final SPARCFloatRegister F60; + public static final SPARCFloatRegister F62; + public static final int NUM_REGISTERS = 64; + private static final SPARCFloatRegister registers[]; + + static { + F0 = new SPARCFloatRegister(0); + F1 = new SPARCFloatRegister(1); + F2 = new SPARCFloatRegister(2); + F3 = new SPARCFloatRegister(3); + F4 = new SPARCFloatRegister(4); + F5 = new SPARCFloatRegister(5); + F6 = new SPARCFloatRegister(6); + F7 = new SPARCFloatRegister(7); + F8 = new SPARCFloatRegister(8); + F9 = new SPARCFloatRegister(9); + F10 = new SPARCFloatRegister(10); + F11 = new SPARCFloatRegister(11); + F12 = new SPARCFloatRegister(12); + F13 = new SPARCFloatRegister(13); + F14 = new SPARCFloatRegister(14); + F15 = new SPARCFloatRegister(15); + F16 = new SPARCFloatRegister(16); + F17 = new SPARCFloatRegister(17); + F18 = new SPARCFloatRegister(18); + F19 = new SPARCFloatRegister(19); + F20 = new SPARCFloatRegister(20); + F21 = new SPARCFloatRegister(21); + F22 = new SPARCFloatRegister(22); + F23 = new SPARCFloatRegister(23); + F24 = new SPARCFloatRegister(24); + F25 = new SPARCFloatRegister(25); + F26 = new SPARCFloatRegister(26); + F27 = new SPARCFloatRegister(27); + F28 = new SPARCFloatRegister(28); + F29 = new SPARCFloatRegister(29); + F30 = new SPARCFloatRegister(30); + F31 = new SPARCFloatRegister(31); + F32 = new SPARCFloatRegister(32); + F34 = new SPARCFloatRegister(34); + F36 = new SPARCFloatRegister(36); + F38 = new SPARCFloatRegister(38); + F40 = new SPARCFloatRegister(40); + F42 = new SPARCFloatRegister(42); + F44 = new SPARCFloatRegister(44); + F46 = new SPARCFloatRegister(46); + F48 = new SPARCFloatRegister(48); + F50 = new SPARCFloatRegister(50); + F52 = new SPARCFloatRegister(52); + F54 = new SPARCFloatRegister(54); + F56 = new SPARCFloatRegister(56); + F58 = new SPARCFloatRegister(58); + F60 = new SPARCFloatRegister(60); + F62 = new SPARCFloatRegister(62); + registers = (new SPARCFloatRegister[] { + F0, F2, F3, F4, F5, F6, F7, F8, F9, F10, + F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, + F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, + F31, F32, null, F34, null, F36, null, F38, null, F40, + null, F42, null, F44, null, F46, null, F48, null, F50, + null, F52, null, F54, null, F56, null, F58, null, F60, + null, F62, null + }); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFlushInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFlushInstruction.java new file mode 100644 index 00000000000..64ed5fa5a58 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFlushInstruction.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCFlushInstruction extends SPARCInstruction { + final protected SPARCRegisterIndirectAddress addr; + final String description; + + public SPARCFlushInstruction(SPARCRegisterIndirectAddress addr) { + super("flush"); + this.addr = addr; + description = initDescription(); + } + + private String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(addr.toString()); + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFormat3AInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFormat3AInstruction.java new file mode 100644 index 00000000000..a408b69045c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCFormat3AInstruction.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public abstract class SPARCFormat3AInstruction extends SPARCInstruction { + final protected int opcode; + final protected SPARCRegister rs1; + final protected ImmediateOrRegister operand2; + final protected SPARCRegister rd; + + public SPARCFormat3AInstruction(String name, int opcode, SPARCRegister rs1, + ImmediateOrRegister operand2, SPARCRegister rd) { + super(name); + this.opcode = opcode; + this.rs1 = rs1; + this.operand2 = operand2; + this.rd = rd; + } + + protected String getOperand2String() { + StringBuffer buf = new StringBuffer(); + if (operand2.isRegister()) { + buf.append(operand2.toString()); + } else { + Number number = ((Immediate)operand2).getNumber(); + buf.append("0x"); + buf.append(Integer.toHexString(number.intValue())); + } + return buf.toString(); + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(rs1.toString()); + buf.append(comma); + buf.append(getOperand2String()); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return getDescription(); + } + + public int getOpcode() { + return opcode; + } + + public SPARCRegister getDestinationRegister() { + return rd; + } + + public ImmediateOrRegister getOperand2() { + return operand2; + } + + public SPARCRegister getSourceRegister1() { + return rs1; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCHelper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCHelper.java new file mode 100644 index 00000000000..efaf4b42033 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCHelper.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCHelper implements CPUHelper { + public Disassembler createDisassembler(long startPc, byte[] code) { + return new SPARCV9Disassembler(startPc, code); + } + + public Register getIntegerRegister(int num) { + return SPARCRegisters.getRegister(num); + } + + public Register getFloatRegister(int num) { + return SPARCFloatRegisters.getRegister(num); + } + + public Register getStackPointer() { + return SPARCRegisters.O7; + } + + public Register getFramePointer() { + return SPARCRegisters.I7; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCIllegalInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCIllegalInstruction.java new file mode 100644 index 00000000000..ad4762b33eb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCIllegalInstruction.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.SymbolFinder; + +public final class SPARCIllegalInstruction extends SPARCInstruction { + final private int instruction; + final private String description; + + public SPARCIllegalInstruction(int instruction) { + super("illegal"); + this.instruction = instruction; + description = "bad opcode - " + Integer.toHexString(instruction); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } + + public int getInstruction() { + return instruction; + } + + public boolean isIllegal() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCIndirectCallInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCIndirectCallInstruction.java new file mode 100644 index 00000000000..c22c26d5ed3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCIndirectCallInstruction.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCIndirectCallInstruction extends SPARCJmplInstruction + implements CallInstruction { + + public SPARCIndirectCallInstruction(SPARCRegisterIndirectAddress addr, SPARCRegister rd) { + super("call", addr, rd); + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + // remove '[' & ']' from jmp address + String addrStr = addr.toString(); + buf.append(addrStr.substring(1, addrStr.length() - 1)); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCInstruction.java new file mode 100644 index 00000000000..ede4c97e902 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCInstruction.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public abstract class SPARCInstruction + extends AbstractInstruction + implements /* imports */ SPARCOpcodes { + public SPARCInstruction(String name) { + super(name); + } + + public int getSize() { + return 4; + } + + protected static String comma = ", "; + protected static String spaces = "\t"; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCInstructionFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCInstructionFactory.java new file mode 100644 index 00000000000..fe8d55f1cd9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCInstructionFactory.java @@ -0,0 +1,110 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public interface SPARCInstructionFactory { + public SPARCInstruction newCallInstruction(PCRelativeAddress addr); + + public SPARCInstruction newNoopInstruction(); + + public SPARCInstruction newSethiInstruction(int imm22, SPARCRegister rd); + + public SPARCInstruction newUnimpInstruction(int const22); + + public SPARCInstruction newBranchInstruction(String name, PCRelativeAddress addr, boolean isAnnuled, int conditionCode); + + public SPARCInstruction newSpecialLoadInstruction(String name, int specialReg, int cregNum, + SPARCRegisterIndirectAddress addr); + + public SPARCInstruction newSpecialStoreInstruction(String name, int specialReg, int cregNum, + SPARCRegisterIndirectAddress addr); + + public SPARCInstruction newLoadInstruction(String name, int opcode, + SPARCRegisterIndirectAddress addr, SPARCRegister rd, + int dataType); + + public SPARCInstruction newStoreInstruction(String name, int opcode, + SPARCRegisterIndirectAddress addr, SPARCRegister rd, + int dataType); + + public SPARCInstruction newStbarInstruction(); + + public SPARCInstruction newReadInstruction(int specialReg, int asrRegNum, SPARCRegister rd); + + public SPARCInstruction newWriteInstruction(int specialReg, int asrRegNum, SPARCRegister rs1, + ImmediateOrRegister operand2); + + public SPARCInstruction newIllegalInstruction(int instruction); + + public SPARCInstruction newIndirectCallInstruction(SPARCRegisterIndirectAddress addr, + SPARCRegister rd); + + public SPARCInstruction newReturnInstruction(SPARCRegisterIndirectAddress addr, + SPARCRegister rd, boolean isLeaf); + + public SPARCInstruction newJmplInstruction(SPARCRegisterIndirectAddress addr, + SPARCRegister rd); + + public SPARCInstruction newFP2RegisterInstruction(String name, int opf, SPARCFloatRegister rs, SPARCFloatRegister rd); + + public SPARCInstruction newFPMoveInstruction(String name, int opf, SPARCFloatRegister rs, SPARCFloatRegister rd); + + public SPARCInstruction newFPArithmeticInstruction(String name, int opf, int rtlOperation, + SPARCFloatRegister rs1, SPARCFloatRegister rs2, + SPARCFloatRegister rd); + + public SPARCInstruction newFlushInstruction(SPARCRegisterIndirectAddress addr); + + public SPARCInstruction newSaveInstruction(SPARCRegister rs1, ImmediateOrRegister operand2, SPARCRegister rd); + + public SPARCInstruction newRestoreInstruction(SPARCRegister rs1, ImmediateOrRegister operand2, SPARCRegister rd); + + public SPARCInstruction newTrapInstruction(String name, int conditionCode); + + public SPARCInstruction newRettInstruction(SPARCRegisterIndirectAddress addr); + + public SPARCInstruction newArithmeticInstruction(String name, int opcode, int rtlOperation, + SPARCRegister rs1, ImmediateOrRegister operand2, + SPARCRegister rd); + + public SPARCInstruction newLogicInstruction(String name, int opcode, int rtlOperation, + SPARCRegister rs1, ImmediateOrRegister operand2, + SPARCRegister rd); + + public SPARCInstruction newMoveInstruction(String name, int opcode, + ImmediateOrRegister operand2, + SPARCRegister rd); + + public SPARCInstruction newShiftInstruction(String name, int opcode, int rtlOperation, + SPARCRegister rs1, ImmediateOrRegister operand2, + SPARCRegister rd); + + public SPARCInstruction newCoprocessorInstruction(int instruction, int cpopcode, int opc, + int rs1Num, int rs2Num, int rdNum); + public SPARCInstruction newSwapInstruction(String name, SPARCRegisterIndirectAddress addr, SPARCRegister rd); + public SPARCInstruction newLdstubInstruction(String name, SPARCRegisterIndirectAddress addr, SPARCRegister rd); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCInstructionFactoryImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCInstructionFactoryImpl.java new file mode 100644 index 00000000000..425cd3e78c1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCInstructionFactoryImpl.java @@ -0,0 +1,176 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCInstructionFactoryImpl implements SPARCInstructionFactory { + public SPARCInstruction newCallInstruction(PCRelativeAddress addr) { + return new SPARCCallInstruction(addr); + } + + public SPARCInstruction newNoopInstruction() { + return new SPARCNoopInstruction(); + } + + public SPARCInstruction newSethiInstruction(int imm22, SPARCRegister rd) { + return new SPARCSethiInstruction(imm22, rd); + } + + public SPARCInstruction newUnimpInstruction(int const22) { + return new SPARCUnimpInstruction(const22); + } + + public SPARCInstruction newBranchInstruction(String name, PCRelativeAddress addr, boolean isAnnuled, int conditionCode) { + return new SPARCBranchInstruction(name, addr, isAnnuled, conditionCode); + } + + public SPARCInstruction newSpecialLoadInstruction(String name, int specialReg, int cregNum, + SPARCRegisterIndirectAddress addr) { + return new SPARCSpecialLoadInstruction(name, specialReg, cregNum, addr); + } + + + public SPARCInstruction newSpecialStoreInstruction(String name, int specialReg, int cregNum, + SPARCRegisterIndirectAddress addr) { + return new SPARCSpecialStoreInstruction(name, specialReg, cregNum, addr); + } + + public SPARCInstruction newLoadInstruction(String name, int opcode, + SPARCRegisterIndirectAddress addr, SPARCRegister rd, + int dataType) { + return new SPARCLoadInstruction(name, opcode, addr, rd, dataType); + } + + public SPARCInstruction newStoreInstruction(String name, int opcode, + SPARCRegisterIndirectAddress addr, SPARCRegister rd, + int dataType) { + return new SPARCStoreInstruction(name, opcode, addr, rd, dataType); + } + + public SPARCInstruction newStbarInstruction() { + return new SPARCStbarInstruction(); + } + + public SPARCInstruction newReadInstruction(int specialReg, int asrRegNum, SPARCRegister rd) { + return new SPARCReadInstruction(specialReg, asrRegNum, rd); + } + + public SPARCInstruction newWriteInstruction(int specialReg, int asrRegNum, SPARCRegister rs1, + ImmediateOrRegister operand2) { + return new SPARCWriteInstruction(specialReg, asrRegNum, rs1,operand2); + } + + public SPARCInstruction newIllegalInstruction(int instruction) { + return new SPARCIllegalInstruction(instruction); + } + + + public SPARCInstruction newIndirectCallInstruction(SPARCRegisterIndirectAddress addr, + SPARCRegister rd) { + return new SPARCIndirectCallInstruction(addr, rd); + } + + public SPARCInstruction newReturnInstruction(SPARCRegisterIndirectAddress addr, + SPARCRegister rd, boolean isLeaf) { + return new SPARCReturnInstruction(addr, rd, isLeaf); + } + + public SPARCInstruction newJmplInstruction(SPARCRegisterIndirectAddress addr, + SPARCRegister rd) { + return new SPARCJmplInstruction(addr, rd); + } + + public SPARCInstruction newFPArithmeticInstruction(String name, int opf, int rtlOperation, + SPARCFloatRegister rs1, SPARCFloatRegister rs2, + SPARCFloatRegister rd) { + return new SPARCFPArithmeticInstruction(name, opf, rtlOperation, rs1, rs2, rd); + } + + public SPARCInstruction newFPMoveInstruction(String name, int opf, SPARCFloatRegister rs, SPARCFloatRegister rd) { + return new SPARCFPMoveInstruction(name, opf, rs, rd); + } + + public SPARCInstruction newFP2RegisterInstruction(String name, int opf, SPARCFloatRegister rs, SPARCFloatRegister rd) { + return new SPARCFP2RegisterInstruction(name, opf, rs, rd); + } + + public SPARCInstruction newFlushInstruction(SPARCRegisterIndirectAddress addr) { + return new SPARCFlushInstruction(addr); + } + + public SPARCInstruction newSaveInstruction(SPARCRegister rs1, ImmediateOrRegister operand2, SPARCRegister rd) { + return new SPARCSaveInstruction(rs1, operand2, rd); + } + + public SPARCInstruction newRestoreInstruction(SPARCRegister rs1, ImmediateOrRegister operand2, SPARCRegister rd) { + return new SPARCRestoreInstruction(rs1, operand2, rd); + } + + public SPARCInstruction newTrapInstruction(String name, int conditionCode) { + return new SPARCTrapInstruction(name, conditionCode); + } + + public SPARCInstruction newRettInstruction(SPARCRegisterIndirectAddress addr) { + return new SPARCRettInstruction(addr); + } + + public SPARCInstruction newArithmeticInstruction(String name, int opcode, int rtlOperation, + SPARCRegister rs1, ImmediateOrRegister operand2, + SPARCRegister rd) { + return new SPARCArithmeticInstruction(name, opcode, rtlOperation, rs1, operand2, rd); + } + + public SPARCInstruction newLogicInstruction(String name, int opcode, int rtlOperation, + SPARCRegister rs1, ImmediateOrRegister operand2, + SPARCRegister rd) { + return new SPARCLogicInstruction(name, opcode, rtlOperation, rs1, operand2, rd); + } + + public SPARCInstruction newMoveInstruction(String name, int opcode, + ImmediateOrRegister operand2, + SPARCRegister rd) { + return new SPARCMoveInstruction(name, opcode, operand2, rd); + } + + public SPARCInstruction newShiftInstruction(String name, int opcode, int rtlOperation, + SPARCRegister rs1, ImmediateOrRegister operand2, + SPARCRegister rd) { + return new SPARCShiftInstruction(name, opcode, rtlOperation, rs1, operand2, rd); + } + + public SPARCInstruction newCoprocessorInstruction(int instruction, int cpopcode, int opcode, + int rs1Num, int rs2Num, int rd) { + return new SPARCIllegalInstruction(instruction); + } + + public SPARCInstruction newSwapInstruction(String name, SPARCRegisterIndirectAddress addr, SPARCRegister rd) { + return new SPARCSwapInstruction(name, addr, rd); + } + + public SPARCInstruction newLdstubInstruction(String name, SPARCRegisterIndirectAddress addr, SPARCRegister rd) { + return new SPARCLdstubInstruction(name, addr, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCJmplInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCJmplInstruction.java new file mode 100644 index 00000000000..eba07cd20ec --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCJmplInstruction.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCJmplInstruction extends SPARCInstruction + implements BranchInstruction { + final protected SPARCRegisterIndirectAddress addr; + final protected SPARCRegister rd; + + protected SPARCJmplInstruction(String name, SPARCRegisterIndirectAddress addr, SPARCRegister rd) { + super(name); + this.addr = addr; + this.rd = rd; + } + + public SPARCJmplInstruction(SPARCRegisterIndirectAddress addr, SPARCRegister rd) { + this("jmpl", addr, rd); + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + String addrStr = addr.toString(); + // remove '[' & ']' from address + addrStr = addrStr.substring(1, addrStr.length() - 1); + if (rd == SPARCRegisters.G0) { + buf.append("jmp"); + buf.append(spaces); + buf.append(addrStr); + } else { + buf.append(getName()); + buf.append(spaces); + buf.append(addrStr); + buf.append(comma); + buf.append(rd.toString()); + } + + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return getDescription(); + } + + public Address getBranchDestination() { + return addr; + } + + public SPARCRegister getReturnAddressRegister() { + return rd; + } + + public boolean isAnnuledBranch() { + return false; + } + + public boolean isBranch() { + return true; + } + + public boolean isConditional() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCLdstubInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCLdstubInstruction.java new file mode 100644 index 00000000000..2f1948f1cf8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCLdstubInstruction.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCLdstubInstruction extends SPARCAtomicLoadStoreInstruction { + public SPARCLdstubInstruction(String name, SPARCRegisterIndirectAddress addr, SPARCRegister rd) { + super(name, addr, rd); + } + + public int getDataType() { + return RTLDT_UNSIGNED_BYTE; + } + + public boolean isConditional() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCLoadInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCLoadInstruction.java new file mode 100644 index 00000000000..ba197f46a24 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCLoadInstruction.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCLoadInstruction extends SPARCMemoryInstruction + implements LoadInstruction { + final protected SPARCRegister register2; // used for double word load instructions + final protected Register[] loadDestinations; + + public SPARCLoadInstruction(String name, int opcode, SPARCRegisterIndirectAddress address, SPARCRegister register, int dataType) { + super(name, opcode,address, register, dataType); + if (opcode == LDD || opcode == LDDA) { + int nextRegNum = (register.getNumber() + 1) % SPARCRegisters.NUM_REGISTERS; + register2 = SPARCRegisters.getRegister(nextRegNum); + loadDestinations = new Register[2]; + loadDestinations[0] = register; + loadDestinations[1] = register2; + } else { + register2 = null; + loadDestinations = new Register[1]; + loadDestinations[0] = register; + } + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(address.toString()); + buf.append(comma); + buf.append(register.toString()); + return buf.toString(); + } + + public Register[] getLoadDestinations() { + return loadDestinations; + } + + public Address getLoadSource() { + return address; + } + + public boolean isLoad() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCLogicInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCLogicInstruction.java new file mode 100644 index 00000000000..7c9bf45008e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCLogicInstruction.java @@ -0,0 +1,105 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCLogicInstruction extends SPARCFormat3AInstruction + implements LogicInstruction { + final private int operation; + + public SPARCLogicInstruction(String name, int opcode, int operation, SPARCRegister rs1, + ImmediateOrRegister operand2, SPARCRegister rd) { + super(name, opcode, rs1, operand2, rd); + this.operation = operation; + } + + protected String getDescription() { + SPARCRegister G0 = SPARCRegisters.G0; + if (opcode == ORcc && rd == G0 && rd == operand2) { + StringBuffer buf = new StringBuffer(); + buf.append("tst"); + buf.append(spaces); + buf.append(getOperand2String()); + return buf.toString(); + } else if (opcode == XNOR && G0 == operand2) { + StringBuffer buf = new StringBuffer(); + buf.append("not"); + buf.append(spaces); + buf.append(rs1.toString()); + if (rs1 != rd) { + buf.append(comma); + buf.append(rd.toString()); + } + return buf.toString(); + } else if (opcode == ANDcc && rd == G0) { + StringBuffer buf = new StringBuffer(); + buf.append("btst"); + buf.append(spaces); + buf.append(getOperand2String()); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } else if (rs1 == rd) { + StringBuffer buf = new StringBuffer(); + switch (opcode) { + case OR: + buf.append("bset"); + break; + case ANDN: + buf.append("bclr"); + break; + case XOR: + buf.append("btog"); + break; + default: + return super.getDescription(); + } + buf.append(spaces); + buf.append(getOperand2String()); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } else { + return super.getDescription(); + } + } + + public Operand getLogicDestination() { + return getDestinationRegister(); + } + + public Operand[] getLogicSources() { + return (new Operand[] { rs1, operand2 }); + } + + public int getOperation() { + return operation; + } + + public boolean isLogic() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCMemoryInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCMemoryInstruction.java new file mode 100644 index 00000000000..1db0040f562 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCMemoryInstruction.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.MemoryInstruction; +import sun.jvm.hotspot.asm.SymbolFinder; + +public abstract class SPARCMemoryInstruction extends SPARCInstruction + implements MemoryInstruction { + final protected SPARCRegisterIndirectAddress address; + final protected SPARCRegister register; + final protected int dataType; + final protected int opcode; + + public SPARCMemoryInstruction(String name, int opcode, SPARCRegisterIndirectAddress address, SPARCRegister register, int dataType) { + super(name); + this.address = address; + this.register = register; + this.dataType = dataType; + this.opcode = opcode; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(address.toString()); + buf.append(comma); + buf.append(register.toString()); + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return getDescription(); + } + + public int getDataType() { + return dataType; + } + + public boolean isConditional() { + return false; + } + + public int getOpcode() { + return opcode; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCMoveInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCMoveInstruction.java new file mode 100644 index 00000000000..3bc4c27295f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCMoveInstruction.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCMoveInstruction extends SPARCFormat3AInstruction + implements MoveInstruction, RTLOperations { + + public SPARCMoveInstruction(String name, int opcode, ImmediateOrRegister operand2, SPARCRegister rd) { + super(name, opcode, null, operand2, rd); + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + if (operand2 == SPARCRegisters.G0) { + buf.append("clr"); + buf.append(spaces); + buf.append(rd.toString()); + } else { + buf.append("mov"); + buf.append(spaces); + buf.append(getOperand2String()); + buf.append(comma); + buf.append(rd.toString()); + } + + return buf.toString(); + } + + public Register getMoveDestination() { + return getDestinationRegister(); + } + + public ImmediateOrRegister getMoveSource() { + return operand2; + } + + public boolean isConditional() { + return false; + } + + public boolean isMove() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCNoopInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCNoopInstruction.java new file mode 100644 index 00000000000..6cce6c74414 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCNoopInstruction.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCNoopInstruction extends SPARCInstruction { + public SPARCNoopInstruction() { + super("nop"); + } + + public boolean isNoop() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCOpcodes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCOpcodes.java new file mode 100644 index 00000000000..2b81b50f3dc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCOpcodes.java @@ -0,0 +1,354 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +// Please refer to "The SPARC Architecture Manual - Version 8" + +public interface SPARCOpcodes { + + // format type is coded in 2 bits - primary opcode - "op" + public static final int FORMAT_START_BIT = 30; + public static final int FORMAT_MASK = 3 << FORMAT_START_BIT; + + // sparc instruction formats + + // PC Relative CALL + public static final int FORMAT_1 = 1; + + // Bicc, FBfcc, CBccc, SETHI + public static final int FORMAT_2 = 0; + + // memory instructions + public static final int FORMAT_3 = 3; + + // arithmetic, logical, shift and remaining + public static final int FORMAT_3A = 2; + + // disp 30 - used in pc relative call + public static final int DISP_30_MASK = 0x3FFFFFFF; + + // secondary opcode "op2" used in FORMAT_2 instructions - 3 bits. + public static final int OP_2_START_BIT = 22; + public static final int OP_2_MASK = 7 << OP_2_START_BIT; + + // various "op2" masks + public static final int OP_2_UNIMP = 0; + public static final int OP_2_Bicc = 2; + public static final int OP_2_SETHI = 4; + public static final int OP_2_FBfcc = 6; + public static final int OP_2_CBccc = 7; + + // condition codes are encoded in 4 bits. + public static final int CONDITION_CODE_START_BIT = 25; + public static final int CONDITION_CODE_MASK = 0xF << CONDITION_CODE_START_BIT; + + // branch condition codes + public static final int CONDITION_BN = 0; + public static final int CONDITION_FBN = CONDITION_BN; + public static final int CONDITION_CBN = CONDITION_BN; + public static final int CONDITION_TN = CONDITION_BN; + + public static final int CONDITION_BE = 1; + public static final int CONDITION_FBNE = CONDITION_BE; + public static final int CONDITION_CB123= CONDITION_BE; + public static final int CONDITION_TE = CONDITION_BE; + + public static final int CONDITION_BLE = 2; + public static final int CONDITION_FBLG = CONDITION_BLE; + public static final int CONDITION_CB12 = CONDITION_BLE; + public static final int CONDITION_TLE = CONDITION_BLE; + + public static final int CONDITION_BL = 3; + public static final int CONDITION_FBUL = CONDITION_BL; + public static final int CONDITION_CB13 = CONDITION_BL; + public static final int CONDITION_TL = CONDITION_BL; + + public static final int CONDITION_BLEU = 4; + public static final int CONDITION_FBL = CONDITION_BLEU; + public static final int CONDITION_CB1 = CONDITION_BLEU; + public static final int CONDITION_TLEU = CONDITION_BLEU; + + public static final int CONDITION_BCS = 5; + public static final int CONDITION_FBUG = CONDITION_BCS; + public static final int CONDITION_CB23 = CONDITION_BCS; + public static final int CONDITION_TCS = CONDITION_BCS; + + public static final int CONDITION_BNEG = 6; + public static final int CONDITION_FBG = CONDITION_BNEG; + public static final int CONDITION_CB2 = CONDITION_BNEG; + public static final int CONDITION_TNEG = CONDITION_BNEG; + + public static final int CONDITION_BVS = 7; + public static final int CONDITION_FBU = CONDITION_BVS; + public static final int CONDITION_CB3 = CONDITION_BVS; + public static final int CONDITION_TVS = CONDITION_BVS; + + public static final int CONDITION_BA = 8; + public static final int CONDITION_FBA = CONDITION_BA; + public static final int CONDITION_CBA = CONDITION_BA; + public static final int CONDITION_TA = CONDITION_BA; + + public static final int CONDITION_BNE = 9; + public static final int CONDITION_FBE = CONDITION_BNE; + public static final int CONDITION_CB0 = CONDITION_BNE; + public static final int CONDITION_TNE = CONDITION_BNE; + + public static final int CONDITION_BG = 0xA; + public static final int CONDITION_FBUE = CONDITION_BG; + public static final int CONDITION_CB03 = CONDITION_BG; + public static final int CONDITION_TG = CONDITION_BG; + + public static final int CONDITION_BGE = 0xB; + public static final int CONDITION_FBGE = CONDITION_BGE; + public static final int CONDITION_CB02 = CONDITION_BGE; + public static final int CONDITION_TGE = CONDITION_BGE; + + public static final int CONDITION_BGU = 0xC; + public static final int CONDITION_FBUGE= CONDITION_BGU; + public static final int CONDITION_CB023= CONDITION_BGU; + public static final int CONDITION_TGU = CONDITION_BGU; + + public static final int CONDITION_BCC = 0xD; + public static final int CONDITION_FBLE = CONDITION_BCC; + public static final int CONDITION_CB01 = CONDITION_BCC; + public static final int CONDITION_TCC = CONDITION_BCC; + + public static final int CONDITION_BPOS = 0xE; + public static final int CONDITION_FBULE= CONDITION_BPOS; + public static final int CONDITION_CB013= CONDITION_BPOS; + public static final int CONDITION_TPOS = CONDITION_BPOS; + + public static final int CONDITION_BVC = 0xF; + public static final int CONDITION_FBO = CONDITION_BVC; + public static final int CONDITION_CB012= CONDITION_BVC; + public static final int CONDITION_TVC = CONDITION_BVC; + + // annul bit mask + public static final int ANNUL_MASK = 1 << 29; + + // 22 bit displacement or immediate value - used in FORMAT_2 instructions. + public static final int DISP_22_MASK = 0x3FFFFF; + public static final int IMM_22_MASK = DISP_22_MASK; + + // second operand mask, called "i" bit + public static final int I_START_BIT = 13; + public static final int I_MASK = 1 << I_START_BIT; + + // address space identifier - "asi" - 8 bits + public static final int ASI_START_BIT = 5; + public static final int ASI_MASK = 0xFF << ASI_START_BIT; + + // signed immediate value 13 bits - "simm13" + public static final int SIMM_13_MASK = 0x1FFF; + + // co-processor or floating point opcode field - "ocf/opf" - 9 bits + public static final int OPF_START_BIT = 5; + public static final int OPF_MASK = 0x1FF << OPF_START_BIT; + public static final int OPC_MASK = OPF_MASK; + + // opcode part 3 - used in FORMAT_3 and FORMAT_3A instructions + // "op3" - 6 bits + public static final int OP_3_START_BIT = 19; + public static final int OP_3_MASK = 0x3F << OP_3_START_BIT; + + // register masks + public static final int RD_START_BIT = 25; + public static final int RD_MASK = 0x1F << RD_START_BIT; // "rd" + public static final int RS1_START_BIT = 14; + public static final int RS1_MASK = 0x1F << RS1_START_BIT; // "rs1" + public static final int RS2_MASK = 0x1F; // "rs2" + + // load/store instructions - op3 values - used with op=3 (FORMAT_3) + public static final int LD = 0; + public static final int LDA = (1 << 4); + public static final int LDF = (2 << 4); + public static final int LDC = (3 << 4); + + public static final int LDUB = 1; + public static final int LDUBA = (1 << 4) | 1; + public static final int LDFSR = (2 << 4) | 1; + public static final int LDCSR = (3 << 4) | 1; + + public static final int LDUH = 2; + public static final int LDUHA = (1 << 4) | 2; + + public static final int LDD = 3; + public static final int LDDA = (1 << 4) | 3; + public static final int LDDF = (2 << 4) | 3; + public static final int LDDC = (3 << 4) | 3; + + public static final int ST = 4; + public static final int STA = (1 << 4) | 4; + public static final int STF = (2 << 4) | 4; + public static final int STC = (3 << 4) | 4; + + public static final int STB = 5; + public static final int STBA = (1 << 4) | 5; + public static final int STFSR = (2 << 4) | 5; + public static final int STCSR = (3 << 4) | 5; + + public static final int STH = 6; + public static final int STHA = (1 << 4) | 6; + public static final int STDFQ = (2 << 4) | 6; + public static final int STDCQ = (3 << 4) | 6; + + public static final int STD = 7; + public static final int STDA = (1 << 4) | 7; + public static final int STDF = (2 << 4) | 7; + public static final int STDC = (3 << 4) | 7; + + public static final int LDSB = 9; + public static final int LDSBA = (1 << 4) | 9; + + public static final int LDSH = 0xA; + public static final int LDSHA = (1 << 4) | 0xA; + + public static final int LDSTUB = 0xD; + public static final int LDSTUBA = (1 << 4) | 0xD; + + public static final int SWAP = 0xF; + public static final int SWAPA = (1 << 4) | 0xF; + + // arithmetic, logic remaining - op3 with op=2 (FORMAT_3A) + public static final int ADD = 0; + public static final int ADDcc = (1 << 4); + public static final int TADDcc = (2 << 4); + public static final int WRASR = (3 << 4); + public static final int WRY = WRASR; + + public static final int AND = 1; + public static final int ANDcc = (1 << 4) | 1; + public static final int TSUBcc = (2 << 4) | 1; + public static final int WRPSR = (3 << 4) | 1; + + public static final int OR = 2; + public static final int ORcc = (1 << 4) | 2; + public static final int TADDccTV = (2 << 4) | 2; + public static final int WRWIM = (3 << 4) | 2; + + public static final int XOR = 3; + public static final int XORcc = (1 << 4) | 3; + public static final int TSUBccTV = (2 << 4) | 3; + public static final int WRTBR = (3 << 4) | 3; + + public static final int SUB = 4; + public static final int SUBcc = (1 << 4) | 4; + public static final int MULScc = (2 << 4) | 4; + public static final int FPop1 = (3 << 4) | 4; + + public static final int ANDN = 5; + public static final int ANDNcc = (1 << 4) | 5; + public static final int SLL = (2 << 4) | 5; + public static final int FPop2 = (3 << 4) | 5; + + public static final int ORN = 6; + public static final int ORNcc = (1 << 4) | 6; + public static final int SRL = (2 << 4) | 6; + public static final int CPop1 = (3 << 4) | 6; + + public static final int XNOR = 7; + public static final int XNORcc = (1 << 4) | 7; + public static final int SRA = (2 << 4) | 7; + public static final int CPop2 = (3 << 4) | 7; + + public static final int ADDX = 8; + public static final int ADDXcc = (1 << 4) | 8; + public static final int RDASR = (2 << 4) | 8; + public static final int RDY = RDASR; + public static final int STBAR = RDASR; + public static final int JMPL = (3 << 4) | 8; + + public static final int RDPSR = (2 << 4) | 9; + public static final int RETT = (3 << 4) | 9; + + public static final int UMUL = 0xA; + public static final int UMULcc = (1 << 4) | 0xA; + public static final int RDWIM = (2 << 4) | 0xA; + public static final int Ticc = (3 << 4) | 0xA; + + public static final int SMUL = 0xB; + public static final int SMULcc = (1 << 4) | 0xB; + public static final int RDTBR = (2 << 4) | 0xB; + public static final int FLUSH = (3 << 4) | 0xB; + + public static final int SUBX = 0xC; + public static final int SUBXcc = (1 << 4) | 0xC; + public static final int SAVE = (3 << 4) | 0xC; + + public static final int RESTORE = (3 << 4) | 0xD; + + public static final int UDIV = 0xE; + public static final int UDIVcc = (1 << 4) | 0xE; + + public static final int SDIV = 0xF; + public static final int SDIVcc = (1 << 4) | 0xF; + + // opf - 9 bits (op=2, op3=0x34=FPop1) - floating point arithmetic + public static final int FMOVs = 0x01; + public static final int FNEGs = 0x05; + public static final int FABSs = 0x09; + public static final int FSQRTs = 0x29; + public static final int FSQRTd = 0x2A; + public static final int FSQRTq = 0x2B; + public static final int FADDs = 0x41; + public static final int FADDd = 0x42; + public static final int FADDq = 0x43; + public static final int FSUBs = 0x45; + public static final int FSUBd = 0x46; + public static final int FSUBq = 0x47; + public static final int FMULs = 0x49; + public static final int FMULd = 0x4A; + public static final int FMULq = 0x4B; + public static final int FDIVs = 0x4D; + public static final int FDIVd = 0x4E; + public static final int FDIVq = 0x4F; + public static final int FsMULd = 0x69; + public static final int FdMULq = 0x6E; + public static final int FiTOs = 0xC4; + public static final int FdTOs = 0xC6; + public static final int FqTOs = 0xC7; + public static final int FiTOd = 0xC8; + public static final int FsTOd = 0xC9; + public static final int FqTOd = 0xCB; + public static final int FiTOq = 0xCC; + public static final int FsTOq = 0xCD; + public static final int FdTOq = 0xCE; + public static final int FsTOi = 0xD1; + public static final int FdTOi = 0xD2; + public static final int FqTOi = 0xD3; + + // opf - 9 bits (op=2, op3=0x35=FPop2) - floating point comparisons + public static final int FCMPs = 0x51; + public static final int FCMPd = 0x52; + public static final int FCMPq = 0x53; + public static final int FCMPEs = 0x55; + public static final int FCMPEd = 0x56; + public static final int FCMPEq = 0x57; + + // 5 bit shift count mask + public static final int SHIFT_COUNT_5_MASK = 0x1F; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCReadInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCReadInstruction.java new file mode 100644 index 00000000000..020fe1d4fe1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCReadInstruction.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.SymbolFinder; +import sun.jvm.hotspot.utilities.Assert; + +public class SPARCReadInstruction extends SPARCSpecialRegisterInstruction { + final private int specialReg; + final private int asrRegNum; + final private SPARCRegister rd; + + public SPARCReadInstruction(int specialReg, int asrRegNum, SPARCRegister rd) { + super("rd"); + this.specialReg = specialReg; + this.asrRegNum = asrRegNum; + this.rd = rd; + } + + public int getSpecialRegister() { + return specialReg; + } + + public int getAncillaryRegister() { + if (Assert.ASSERTS_ENABLED) + Assert.that(specialReg == ASR, "not an ancillary register"); + return asrRegNum; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + if(specialReg == ASR) + buf.append("%asr" + asrRegNum); + else + buf.append(getSpecialRegisterName(specialReg)); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegister.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegister.java new file mode 100644 index 00000000000..fefa9755ab4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegister.java @@ -0,0 +1,128 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class SPARCRegister extends Register { + private static final int nofRegisters = 32; // total number of registers + + private static final int GLOBAL_BASE = 0; + private static final int OUT_BASE = 8; + private static final int LOCAL_BASE = 16; + private static final int IN_BASE = 24; + + private static final int LOCAL_SP_WORD_OFFSET = 0; + private static final int IN_SP_WORD_OFFSET = 8; + + /** Constructor for an explicitly numbered register */ + public SPARCRegister(int number) { + super(number); + } + + /** Constructor for an I, G, O, or L register */ + public SPARCRegister(SPARCRegisterType type, int number) { + if (type == SPARCRegisterType.GLOBAL) { + this.number = number + GLOBAL_BASE; + } else if (type == SPARCRegisterType.OUT) { + this.number = number + OUT_BASE; + } else if (type == SPARCRegisterType.LOCAL) { + this.number = number + LOCAL_BASE; + } else if (type == SPARCRegisterType.IN) { + this.number = number + IN_BASE; + } else { + throw new IllegalArgumentException("Invalid SPARC register type"); + } + } + + public int getNumberOfRegisters() { + return nofRegisters; + } + + public boolean isIn() { + return (IN_BASE <= getNumber()); + } + + public boolean isLocal() { + return (LOCAL_BASE <= getNumber() && getNumber() < IN_BASE); + } + + public boolean isOut() { + return (OUT_BASE <= getNumber() && getNumber() < LOCAL_BASE); + } + + public boolean isGlobal() { + return (GLOBAL_BASE <= getNumber() && getNumber() < OUT_BASE); + } + + public SPARCRegister afterSave() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isOut() || isGlobal(), "register not visible after save"); + } + return isOut() ? new SPARCRegister(getNumber() + (IN_BASE - OUT_BASE)) : this; + } + + public SPARCRegister afterRestore() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isIn() || isGlobal(), "register not visible after save"); + } + return isIn() ? new SPARCRegister(getNumber() + (OUT_BASE - IN_BASE)) : this; + } + + /** NOTE: this returns an offset in BYTES in this system! */ + public long spOffsetInSavedWindow() { + if (isIn()) { + return VM.getVM().getAddressSize() * (getNumber() - IN_BASE + IN_SP_WORD_OFFSET); + } else if (isLocal()) { + return VM.getVM().getAddressSize() * (getNumber() - LOCAL_BASE + LOCAL_SP_WORD_OFFSET); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(isIn() || isLocal(), "only ins and locals are saved in my frame"); + } + return 0; + } + + public String toString() { + return SPARCRegisters.getRegisterName(number); + } + + public boolean isFramePointer() { + return number == 30; // is I6? + } + + public boolean isStackPointer() { + return number == 14; // is O6? + } + + public boolean isFloat() { + return false; + } + + public boolean isV9Only() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegisterIndirectAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegisterIndirectAddress.java new file mode 100644 index 00000000000..5e0a198379c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegisterIndirectAddress.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.Address; +import sun.jvm.hotspot.asm.BaseIndexScaleDispAddress; + +public class SPARCRegisterIndirectAddress extends BaseIndexScaleDispAddress { + protected int addressSpace = -1; + + public SPARCRegisterIndirectAddress(SPARCRegister register, int offset) { + super(register, offset); + } + + public SPARCRegisterIndirectAddress(SPARCRegister base, SPARCRegister index) { + super(base, index); + } + + public int getAddressSpace() { + return addressSpace; + } + + public void setAddressSpace(int addressSpace) { + this.addressSpace = addressSpace; + } + + public String getAddressWithoutAsi() { + StringBuffer buf = new StringBuffer(); + buf.append('['); + buf.append(getBase().toString()); + sun.jvm.hotspot.asm.Register register = getIndex(); + if (register != null) { + buf.append(" + "); + buf.append(register.toString()); + } else { + long disp = getDisplacement(); + if (disp < 0) { + buf.append(" - 0x"); + disp = -disp; + } else { + buf.append(" + 0x"); + } + buf.append(Long.toHexString(disp)); + } + buf.append(']'); + return buf.toString(); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getAddressWithoutAsi()); + if(addressSpace != -1) + buf.append((new Integer(addressSpace)).toString()); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegisterType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegisterType.java new file mode 100644 index 00000000000..85abb4b8902 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegisterType.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +/** A class providing the enums describing SPARC register types */ + +public class SPARCRegisterType { + private SPARCRegisterType() { + } + + public static final SPARCRegisterType GLOBAL = new SPARCRegisterType(); + public static final SPARCRegisterType OUT = new SPARCRegisterType(); + public static final SPARCRegisterType IN = new SPARCRegisterType(); + public static final SPARCRegisterType LOCAL = new SPARCRegisterType(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegisters.java new file mode 100644 index 00000000000..45f8201157f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRegisters.java @@ -0,0 +1,189 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.utilities.*; + +public class SPARCRegisters { + + public static final SPARCRegister G0; + public static final SPARCRegister G1; + public static final SPARCRegister G2; + public static final SPARCRegister G3; + public static final SPARCRegister G4; + public static final SPARCRegister G5; + public static final SPARCRegister G6; + public static final SPARCRegister G7; + public static final SPARCRegister O0; + public static final SPARCRegister O1; + public static final SPARCRegister O2; + public static final SPARCRegister O3; + public static final SPARCRegister O4; + public static final SPARCRegister O5; + public static final SPARCRegister O6; + public static final SPARCRegister O7; + public static final SPARCRegister L0; + public static final SPARCRegister L1; + public static final SPARCRegister L2; + public static final SPARCRegister L3; + public static final SPARCRegister L4; + public static final SPARCRegister L5; + public static final SPARCRegister L6; + public static final SPARCRegister L7; + public static final SPARCRegister I0; + public static final SPARCRegister I1; + public static final SPARCRegister I2; + public static final SPARCRegister I3; + public static final SPARCRegister I4; + public static final SPARCRegister I5; + public static final SPARCRegister I6; + public static final SPARCRegister I7; + + private static String registerNames[]; + public static final int NUM_REGISTERS = 32; + private static SPARCRegister registers[]; + + static { + G0 = new SPARCRegister(0); + G1 = new SPARCRegister(1); + G2 = new SPARCRegister(2); + G3 = new SPARCRegister(3); + G4 = new SPARCRegister(4); + G5 = new SPARCRegister(5); + G6 = new SPARCRegister(6); + G7 = new SPARCRegister(7); + O0 = new SPARCRegister(8); + O1 = new SPARCRegister(9); + O2 = new SPARCRegister(10); + O3 = new SPARCRegister(11); + O4 = new SPARCRegister(12); + O5 = new SPARCRegister(13); + O6 = new SPARCRegister(14); + O7 = new SPARCRegister(15); + L0 = new SPARCRegister(16); + L1 = new SPARCRegister(17); + L2 = new SPARCRegister(18); + L3 = new SPARCRegister(19); + L4 = new SPARCRegister(20); + L5 = new SPARCRegister(21); + L6 = new SPARCRegister(22); + L7 = new SPARCRegister(23); + I0 = new SPARCRegister(24); + I1 = new SPARCRegister(25); + I2 = new SPARCRegister(26); + I3 = new SPARCRegister(27); + I4 = new SPARCRegister(28); + I5 = new SPARCRegister(29); + I6 = new SPARCRegister(30); + I7 = new SPARCRegister(31); + registerNames = new String[NUM_REGISTERS]; + registerNames[G0.getNumber()] = "%g0"; + registerNames[G1.getNumber()] = "%g1"; + registerNames[G2.getNumber()] = "%g2"; + registerNames[G3.getNumber()] = "%g3"; + registerNames[G4.getNumber()] = "%g4"; + registerNames[G5.getNumber()] = "%g5"; + registerNames[G6.getNumber()] = "%g6"; + registerNames[G7.getNumber()] = "%g7"; + registerNames[O0.getNumber()] = "%o0"; + registerNames[O1.getNumber()] = "%o1"; + registerNames[O2.getNumber()] = "%o2"; + registerNames[O3.getNumber()] = "%o3"; + registerNames[O4.getNumber()] = "%o4"; + registerNames[O5.getNumber()] = "%o5"; + registerNames[O6.getNumber()] = "%sp"; + registerNames[O7.getNumber()] = "%o7"; + registerNames[I0.getNumber()] = "%i0"; + registerNames[I1.getNumber()] = "%i1"; + registerNames[I2.getNumber()] = "%i2"; + registerNames[I3.getNumber()] = "%i3"; + registerNames[I4.getNumber()] = "%i4"; + registerNames[I5.getNumber()] = "%i5"; + registerNames[I6.getNumber()] = "%fp"; + registerNames[I7.getNumber()] = "%i7"; + registerNames[L0.getNumber()] = "%l0"; + registerNames[L1.getNumber()] = "%l1"; + registerNames[L2.getNumber()] = "%l2"; + registerNames[L3.getNumber()] = "%l3"; + registerNames[L4.getNumber()] = "%l4"; + registerNames[L5.getNumber()] = "%l5"; + registerNames[L6.getNumber()] = "%l6"; + registerNames[L7.getNumber()] = "%l7"; + registers = (new SPARCRegister[] { + G0, G1, G2, G3, G4, G5, G6, G7, O0, O1, + O2, O3, O4, O5, O6, O7, L0, L1, L2, L3, + L4, L5, L6, L7, I0, I1, I2, I3, I4, I5, + I6, I7 + }); + } + + public static final SPARCRegister FP = I6; + public static final SPARCRegister SP = O6; + + // Interpreter frames + + public static final SPARCRegister Lesp = L0; // expression stack pointer + public static final SPARCRegister Lbcp = L1; // pointer to next bytecode + public static final SPARCRegister Lmethod = L2; + public static final SPARCRegister Llocals = L3; + public static final SPARCRegister Lmonitors = L4; + public static final SPARCRegister Lbyte_code = L5; + public static final SPARCRegister Lscratch = L5; + public static final SPARCRegister Lscratch2 = L6; + public static final SPARCRegister LcpoolCache = L6; // constant pool cache + + public static final SPARCRegister OparamAddr = O0; // Callers Parameter area address + public static final SPARCRegister IsavedSP = I5; // Saved SP before bumping for locals + public static final SPARCRegister IsizeCalleeParms = I4; // Saved size of Callee parms used to pop arguments + public static final SPARCRegister IdispatchAddress = I3; // Register which saves the dispatch address for each bytecode + public static final SPARCRegister IdispatchTables = I2; // Base address of the bytecode dispatch tables + + + /** Prefer to use this instead of the constant above */ + public static int getNumRegisters() { + return NUM_REGISTERS; + } + + + public static String getRegisterName(int regNum) { + if (regNum < 0 || regNum >= NUM_REGISTERS) { + return "[Illegal register " + regNum + "]"; + } + + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid integer register number!"); + } + + return registerNames[regNum]; + } + + public static SPARCRegister getRegister(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid integer register number!"); + } + + return registers[regNum]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRestoreInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRestoreInstruction.java new file mode 100644 index 00000000000..9c336d4918a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRestoreInstruction.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCRestoreInstruction extends SPARCFormat3AInstruction { + final private boolean trivial; + public SPARCRestoreInstruction(SPARCRegister rs1, ImmediateOrRegister operand2, SPARCRegister rd) { + super("restore", RESTORE, rs1, operand2, rd); + SPARCRegister G0 = SPARCRegisters.G0; + trivial = (rs1 == G0 && operand2 == G0 && rd == G0); + } + + public boolean isTrivial() { + return trivial; + } + + protected String getDescription() { + return (trivial) ? getName() : super.getDescription(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRettInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRettInstruction.java new file mode 100644 index 00000000000..ad90314a312 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCRettInstruction.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCRettInstruction extends SPARCInstruction + implements BranchInstruction { + final protected SPARCRegisterIndirectAddress addr; + final protected String description; + + protected SPARCRettInstruction(String name, SPARCRegisterIndirectAddress addr) { + super(name); + this.addr = addr; + description = initDescription(); + } + + public SPARCRettInstruction(SPARCRegisterIndirectAddress addr) { + this("rett", addr); + } + + private String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(addr.toString()); + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } + + public Address getBranchDestination() { + return addr; + } + + public boolean isAnnuledBranch() { + return false; + } + + public boolean isBranch() { + return true; + } + + public boolean isConditional() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCReturnInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCReturnInstruction.java new file mode 100644 index 00000000000..1670b4718f6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCReturnInstruction.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCReturnInstruction extends SPARCJmplInstruction + implements ReturnInstruction { + + private final boolean leaf; + + public SPARCReturnInstruction(SPARCRegisterIndirectAddress addr, SPARCRegister rd, boolean leaf) { + super(leaf? "retl" : "ret", addr, rd); + this.leaf = leaf; + } + + public boolean isLeaf() { + return leaf; + } + + protected String getDescription() { + return getName(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSaveInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSaveInstruction.java new file mode 100644 index 00000000000..76f45682a79 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSaveInstruction.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCSaveInstruction extends SPARCFormat3AInstruction { + final private boolean trivial; + public SPARCSaveInstruction(SPARCRegister rs1, ImmediateOrRegister operand2, SPARCRegister rd) { + super("save", SAVE, rs1, operand2, rd); + SPARCRegister G0 = SPARCRegisters.G0; + trivial = (rs1 == G0 && operand2 == G0 && rd == G0); + } + + public boolean isTrivial() { + return trivial; + } + + protected String getOperand2String() { + StringBuffer buf = new StringBuffer(); + if (operand2.isRegister()) { + buf.append(operand2.toString()); + } else { + Number number = ((Immediate)operand2).getNumber(); + int value = number.intValue(); + if (value < 0) { + buf.append("-0x"); + value = -value; + } else { + buf.append("0x"); + } + + buf.append(Integer.toHexString(value)); + } + return buf.toString(); + } + + protected String getDescription() { + return (trivial) ? getName() : super.getDescription(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSethiInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSethiInstruction.java new file mode 100644 index 00000000000..97c7b84cb94 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSethiInstruction.java @@ -0,0 +1,74 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCSethiInstruction extends SPARCInstruction + implements MoveInstruction { + final private SPARCRegister register; + final private ImmediateOrRegister value; + final private String description; + + public SPARCSethiInstruction(int value, SPARCRegister register) { + super("sethi"); + this.register = register; + value <<= 10; + this.value = new Immediate(new Integer(value)); + description = initDescription(value); + } + + private String initDescription(int val) { + if (val == 0 && register == SPARCRegisters.G0) { + return "nop"; + } else { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append("%hi(0x"); + buf.append(Integer.toHexString(val)); + buf.append(')'); + buf.append(comma); + buf.append(register.toString()); + return buf.toString(); + } + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } + + public Register getMoveDestination() { + return register; + } + + public ImmediateOrRegister getMoveSource() { + return value; + } + + public boolean isConditional() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCShiftInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCShiftInstruction.java new file mode 100644 index 00000000000..7246efea778 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCShiftInstruction.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCShiftInstruction extends SPARCFormat3AInstruction + implements ShiftInstruction { + final private int operation; + + public SPARCShiftInstruction(String name, int opcode, int operation, SPARCRegister rs1, + ImmediateOrRegister operand2, SPARCRegister rd) { + super(name, opcode, rs1, operand2, rd); + this.operation = operation; + } + + public int getOperation() { + return operation; + } + + public Operand getShiftDestination() { + return getDestinationRegister(); + } + + public Operand getShiftLength() { + return operand2; + } + + public Operand getShiftSource() { + return rs1; + } + + public boolean isShift() { + return true; + } + + protected String getOperand2String() { + return operand2.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialLoadInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialLoadInstruction.java new file mode 100644 index 00000000000..4b0af1245cc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialLoadInstruction.java @@ -0,0 +1,76 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.Assert; + +public class SPARCSpecialLoadInstruction + extends SPARCSpecialRegisterInstruction + implements /* imports */ SPARCSpecialRegisters { + final private int specialReg; + final private int cregNum; + final private SPARCRegisterIndirectAddress addr; + + public SPARCSpecialLoadInstruction(String name, int specialReg, int cregNum, + SPARCRegisterIndirectAddress addr) { + super(name); + this.specialReg = specialReg; + this.cregNum = cregNum; + this.addr = addr; + } + + public SPARCSpecialLoadInstruction(String name, int specialReg, SPARCRegisterIndirectAddress addr) { + this(name, specialReg, -1, addr); + } + + public int getSpecialRegister() { + return specialReg; + } + + public int getCoprocessorRegister() { + if (Assert.ASSERTS_ENABLED) + Assert.that(specialReg == CREG, "not a coprocesssor register"); + return cregNum; + } + + public Address getSource() { + return addr; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(addr); + buf.append(comma); + if (specialReg == CREG) { + buf.append("creg" + cregNum); + } else { + buf.append(getSpecialRegisterName(specialReg)); + } + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialRegisterInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialRegisterInstruction.java new file mode 100644 index 00000000000..76b5a92a4ba --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialRegisterInstruction.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public abstract class SPARCSpecialRegisterInstruction + extends SPARCInstruction + implements /* import */ SPARCSpecialRegisters { + protected SPARCSpecialRegisterInstruction(String name) { + super(name); + } + + protected abstract String getDescription(); + + public String asString(long currentPc, SymbolFinder symFinder) { + return getDescription(); + } + + protected static String[] specialRegNames = new String[] { + "%y", + "%psr", + "%wim", + "%tbr", + "%asr", + "%fsr", + "%csr", + "%fq", + "%cq" + }; + + protected static String getSpecialRegisterName(int index) { + return specialRegNames[index]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialRegisters.java new file mode 100644 index 00000000000..bb8086290bf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialRegisters.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +public interface SPARCSpecialRegisters { + public static final int Y = 0; + public static final int PSR = 1; + public static final int WIM = 2; + public static final int TBR = 3; + public static final int ASR = 4; + public static final int FSR = 5; + public static final int CSR = 6; + public static final int FQ = 7; + public static final int CQ = 8; + public static final int CREG = 9; // co-processor reg +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialStoreInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialStoreInstruction.java new file mode 100644 index 00000000000..ad0df85e7e6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSpecialStoreInstruction.java @@ -0,0 +1,76 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.Assert; + +public class SPARCSpecialStoreInstruction + extends SPARCSpecialRegisterInstruction + implements /* imports */ SPARCSpecialRegisters { + final private int specialReg; + final private int cregNum; + final private SPARCRegisterIndirectAddress addr; + + public SPARCSpecialStoreInstruction(String name, int specialReg, int cregNum, + SPARCRegisterIndirectAddress addr) { + super(name); + this.specialReg = specialReg; + this.addr = addr; + this.cregNum = cregNum; + } + + public SPARCSpecialStoreInstruction(String name, int specialReg, + SPARCRegisterIndirectAddress addr) { + this(name, specialReg, -1, addr); + } + + public int getSpecialRegister() { + return specialReg; + } + + public int getCoprocessorRegister() { + if (Assert.ASSERTS_ENABLED) + Assert.that(specialReg == CREG, "not a special register"); + return cregNum; + } + + public Address getDestination() { + return addr; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(getName()); + buf.append(spaces); + if (specialReg == CREG) { + buf.append("creg" + cregNum); + } else { + buf.append(getSpecialRegisterName(specialReg)); + } + buf.append(comma); + buf.append(addr.toString()); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCStbarInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCStbarInstruction.java new file mode 100644 index 00000000000..f03c9b04a22 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCStbarInstruction.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.SymbolFinder; + +public class SPARCStbarInstruction extends SPARCInstruction { + public SPARCStbarInstruction() { + super("stbar"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCStoreInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCStoreInstruction.java new file mode 100644 index 00000000000..2a57c66f269 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCStoreInstruction.java @@ -0,0 +1,97 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCStoreInstruction extends SPARCMemoryInstruction + implements StoreInstruction { + final protected SPARCRegister register2; // used by double word store instructions + final protected Register[] storeSources; + + public SPARCStoreInstruction(String name, int opcode, SPARCRegisterIndirectAddress address, SPARCRegister register, int dataType) { + super(name, opcode, address, register, dataType); + if (opcode == STD || opcode == STDA) { + storeSources = new Register[2]; + storeSources[0] = register; + int nextRegNum = (register.getNumber() + 1) % SPARCRegisters.NUM_REGISTERS; + register2 = SPARCRegisters.getRegister(nextRegNum); + storeSources[1] = register2; + } else { + storeSources = new Register[1]; + storeSources[0] = register; + register2 = null; + } + } + + private String defaultInitDescription(StringBuffer buf) { + buf.append(getName()); + buf.append(spaces); + buf.append(register.toString()); + buf.append(comma); + buf.append(address.toString()); + return buf.toString(); + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + if (register == SPARCRegisters.G0) { + switch (opcode) { + case ST: + buf.append("clr"); + break; + case STH: + buf.append("clrh"); + break; + case STB: + buf.append("clrb"); + break; + default: + return defaultInitDescription(buf); + } + buf.append(spaces); + buf.append(address.toString()); + return buf.toString(); + } else { + return defaultInitDescription(buf); + } + } + + public int getDataType() { + return dataType; + } + + public Address getStoreDestination() { + return address; + } + + public Register[] getStoreSources() { + return storeSources; + } + + public boolean isStore() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSwapInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSwapInstruction.java new file mode 100644 index 00000000000..c46b0fae8fa --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCSwapInstruction.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCSwapInstruction extends SPARCAtomicLoadStoreInstruction { + public SPARCSwapInstruction(String name, SPARCRegisterIndirectAddress addr, SPARCRegister rd) { + super(name, addr, rd); + } + + public int getDataType() { + return RTLDT_UNSIGNED_WORD; + } + + public boolean isConditional() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCTrapInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCTrapInstruction.java new file mode 100644 index 00000000000..2443e82fcab --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCTrapInstruction.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCTrapInstruction extends SPARCInstruction + implements BranchInstruction { + final protected int conditionCode; + + public SPARCTrapInstruction(String name, int conditionCode) { + super(name); + this.conditionCode = conditionCode; + } + + public Address getBranchDestination() { + return null; + } + + public int getConditionCode() { + return conditionCode; + } + + public boolean isConditional() { + return conditionCode != CONDITION_TN && conditionCode != CONDITION_TA; + } + + public boolean isTrap() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCUnimpInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCUnimpInstruction.java new file mode 100644 index 00000000000..2dc9dccf9e4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCUnimpInstruction.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCUnimpInstruction extends SPARCInstruction { + final private String description; + final private int const22; + + protected SPARCUnimpInstruction(String name, int const22) { + super(name); + this.const22 = const22; + description = initDescription(); + } + + public SPARCUnimpInstruction(int const22) { + this("unimp", const22); + } + + public int getConst22() { + return const22; + } + + public boolean isIllegal() { + return true; + } + + private String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append("0x"); + buf.append(Integer.toHexString(const22)); + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV8Disassembler.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV8Disassembler.java new file mode 100644 index 00000000000..05842770ab9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV8Disassembler.java @@ -0,0 +1,205 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import java.io.*; +import java.util.*; + +// Please refer to "The SPARC Architecture Manual - Version 8" + +public class SPARCV8Disassembler extends SPARCDisassembler { + + public SPARCV8Disassembler(long startPc, byte[] code, SPARCInstructionFactory factory) { + super(startPc, code, factory); + } + + public SPARCV8Disassembler(long startPc, byte[] code) { + this(startPc, code, new SPARCInstructionFactoryImpl()); + } + + // decoders for format 2 instructions + private static InstructionDecoder format2Decoders[] = { + new UnimpDecoder(), + illegalDecoder, + new IntegerBranchDecoder(), + illegalDecoder, + new SethiDecoder(), + illegalDecoder, + new FloatBranchDecoder(), + new CoprocessorBranchDecoder() + }; + + protected InstructionDecoder getFormat2Decoder(int op2) { + return format2Decoders[op2]; + } + + // op3 decoder table for op=3 (FORMAT_3) instructions - (memory instructions) + // Appendix F - Opcodes and Condition Codes - Page 229 - Table F-4 + + private static final InstructionDecoder format3Decoders[][] = { + { + new LoadDecoder(LD, "ld", RTLDT_UNSIGNED_WORD), new AlternateSpaceLoadDecoder(LDA, "lda", RTLDT_UNSIGNED_WORD), + new LoadDecoder(LDF,"ld", RTLDT_FL_SINGLE), new SpecialLoadDecoder(LDC,"ld", SPARCSpecialRegisters.CREG) + }, + { + new LoadDecoder(LDUB, "ldub", RTLDT_UNSIGNED_BYTE), new AlternateSpaceLoadDecoder(LDUBA, "lduba", RTLDT_UNSIGNED_BYTE), + new SpecialLoadDecoder(LDFSR, "ld", SPARCSpecialRegisters.FSR), new SpecialLoadDecoder(LDCSR, "ld", SPARCSpecialRegisters.CSR) + }, + { + new LoadDecoder(LDUH, "lduh", RTLDT_UNSIGNED_HALF), new AlternateSpaceLoadDecoder(LDUHA, "lduha", RTLDT_UNSIGNED_HALF), + illegalDecoder, illegalDecoder + }, + { + new LoadDecoder(LDD, "ldd", RTLDT_UNSIGNED_DWORD), new AlternateSpaceLoadDecoder(LDDA, "ldda", RTLDT_UNSIGNED_DWORD), + new LoadDecoder(LDDF, "ldd", RTLDT_FL_DOUBLE), new SpecialLoadDecoder(LDDC, "ldd", SPARCSpecialRegisters.CREG) + }, + { + new StoreDecoder(ST, "st", RTLDT_UNSIGNED_WORD), new AlternateSpaceStoreDecoder(STA, "sta", RTLDT_UNSIGNED_WORD), + new StoreDecoder(STF, "st", RTLDT_FL_SINGLE), new SpecialStoreDecoder(STC, "st", SPARCSpecialRegisters.CREG) + }, + { + new StoreDecoder(STB, "stb", RTLDT_UNSIGNED_BYTE), new AlternateSpaceStoreDecoder(STBA, "stba", RTLDT_UNSIGNED_BYTE), + new SpecialStoreDecoder(STFSR, "st", SPARCSpecialRegisters.FSR), new SpecialStoreDecoder(STCSR, "st", SPARCSpecialRegisters.CSR), + }, + { + new StoreDecoder(STH, "sth", RTLDT_UNSIGNED_HALF), new AlternateSpaceStoreDecoder(STHA, "stha", RTLDT_UNSIGNED_HALF), + new SpecialStoreDecoder(STDFQ, "std", SPARCSpecialRegisters.FQ), new SpecialStoreDecoder(STDCQ, "std", SPARCSpecialRegisters.CQ), + }, + { + new StoreDecoder(STD, "std", RTLDT_UNSIGNED_DWORD), new AlternateSpaceStoreDecoder(STDA, "stda", RTLDT_UNSIGNED_DWORD), + new StoreDecoder(STDF, "std", RTLDT_FL_DOUBLE), new SpecialStoreDecoder(STDC, "std", SPARCSpecialRegisters.CREG) + }, + { + illegalDecoder, illegalDecoder, + illegalDecoder, illegalDecoder + }, + { + new LoadDecoder(LDSB, "ldsb", RTLDT_SIGNED_BYTE), new AlternateSpaceLoadDecoder(LDSBA, "ldsba", RTLDT_UNSIGNED_BYTE), + illegalDecoder, illegalDecoder + }, + { + new LoadDecoder(LDSH, "ldsh", RTLDT_SIGNED_HALF), new AlternateSpaceLoadDecoder(LDSHA, "ldsha", RTLDT_UNSIGNED_HALF), + illegalDecoder, illegalDecoder + }, + { + illegalDecoder, illegalDecoder, + illegalDecoder, illegalDecoder + }, + { + illegalDecoder, illegalDecoder, + illegalDecoder, illegalDecoder + }, + { + new LdstubDecoder(LDSTUB, "ldstub", RTLDT_UNSIGNED_BYTE), new AlternateSpaceLdstubDecoder(LDSTUBA, "ldstuba", RTLDT_UNSIGNED_BYTE), + illegalDecoder, illegalDecoder + }, + { + illegalDecoder, illegalDecoder, + illegalDecoder, illegalDecoder + }, + { + new SwapDecoder(SWAP, "swap", RTLDT_UNSIGNED_WORD), new AlternateSpaceSwapDecoder(SWAPA, "swapa", RTLDT_UNSIGNED_WORD), + illegalDecoder, illegalDecoder + }, + }; + + protected InstructionDecoder getFormat3Decoder(int row, int column) { + return format3Decoders[row][column]; + } + + // op3 decoder table for op=2 (FORMAT_3A) instructions + // Appendix F - Opcodes and Condition Codes - Page 228 - Table F-3 + protected static final InstructionDecoder format3ADecoders[][] = { + { + new ArithmeticDecoder(ADD, "add", RTLOP_ADD), new ArithmeticDecoder(ADDcc, "addcc", RTLOP_ADD), + new ArithmeticDecoder(TADDcc, "taddcc", RTLOP_ADD), new WriteDecoder(SPARCSpecialRegisters.ASR) + }, + { + new LogicDecoder(AND, "and", RTLOP_AND), new LogicDecoder(ANDcc, "andcc", RTLOP_AND), + new ArithmeticDecoder(TSUBcc, "tsubcc", RTLOP_ADD), new WriteDecoder(SPARCSpecialRegisters.PSR) + }, + { + new LogicDecoder(OR, "or", RTLOP_OR), new LogicDecoder(ORcc, "orcc", RTLOP_OR), + new ArithmeticDecoder(TADDccTV, "taddcctv", RTLOP_ADD), new WriteDecoder(SPARCSpecialRegisters.WIM) + }, + { + new LogicDecoder(XOR, "xor", RTLOP_XOR), new LogicDecoder(XORcc, "xorcc", RTLOP_XOR), + new ArithmeticDecoder(TSUBccTV, "tsubcctv", RTLOP_SUB), new WriteDecoder(SPARCSpecialRegisters.TBR) + }, + { + new ArithmeticDecoder(SUB, "sub", RTLOP_SUB), new ArithmeticDecoder(SUBcc, "subcc", RTLOP_SUB), + new ArithmeticDecoder(MULScc, "mulscc", RTLOP_SMUL), new V8FPop1Decoder() + }, + { + new LogicDecoder(ANDN, "andn", RTLOP_NAND), new LogicDecoder(ANDNcc, "andncc", RTLOP_NAND), + new ShiftDecoder(SLL, "sll", RTLOP_SLL), new V8FPop2Decoder() + }, + { + new LogicDecoder(ORN, "orn", RTLOP_NOR), new LogicDecoder(ORNcc, "orncc", RTLOP_NOR), + new ShiftDecoder(SRL, "srl", RTLOP_SRL), new CoprocessorDecoder(CPop1) + }, + { + new LogicDecoder(XNOR, "xnor", RTLOP_XNOR), new LogicDecoder(XNORcc, "xnorcc", RTLOP_XNOR), + new ShiftDecoder(SRA, "sra", RTLOP_SRA), new CoprocessorDecoder(CPop2) + }, + { + new ArithmeticDecoder(ADDX, "addx", RTLOP_ADDC), new ArithmeticDecoder(ADDXcc, "addxcc", RTLOP_ADDC), + new ReadDecoder(SPARCSpecialRegisters.ASR), new JmplDecoder() + }, + { + illegalDecoder, illegalDecoder, + new ReadDecoder(SPARCSpecialRegisters.PSR), new RettDecoder() + }, + { + new ArithmeticDecoder(UMUL, "umul", RTLOP_UMUL), new ArithmeticDecoder(UMULcc, "umulcc", RTLOP_UMUL), + new ReadDecoder(SPARCSpecialRegisters.WIM), new TrapDecoder() + }, + { + new ArithmeticDecoder(SMUL, "smul", RTLOP_SMUL), new ArithmeticDecoder(SMULcc, "smulcc", RTLOP_SMUL), + new ReadDecoder(SPARCSpecialRegisters.TBR), new FlushDecoder() + }, + { + new ArithmeticDecoder(SUBX, "subx", RTLOP_SUBC), new ArithmeticDecoder(SUBXcc, "subxcc", RTLOP_SUBC), + illegalDecoder, new SaveDecoder() + }, + { + illegalDecoder, illegalDecoder, + illegalDecoder, new RestoreDecoder() + }, + { + new ArithmeticDecoder(UDIV, "udiv", RTLOP_UDIV), new ArithmeticDecoder(UDIVcc, "udivcc", RTLOP_UDIV), + illegalDecoder, illegalDecoder + }, + { + new ArithmeticDecoder(SDIV, "sdiv", RTLOP_SDIV), new ArithmeticDecoder(SDIVcc, "sdivcc", RTLOP_SDIV), + illegalDecoder, illegalDecoder + } + }; + + protected InstructionDecoder getFormat3ADecoder(int row, int column) { + return format3ADecoders[row][column]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9BranchInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9BranchInstruction.java new file mode 100644 index 00000000000..8e8dcfdab26 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9BranchInstruction.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9BranchInstruction extends SPARCBranchInstruction + implements SPARCV9Instruction { + final private boolean predictTaken; + final private int conditionFlag; // icc, xcc or fccn - condition bits selected + + public SPARCV9BranchInstruction(String name, PCRelativeAddress addr, + boolean isAnnuled, int conditionCode, boolean predictTaken, int conditionFlag) { + super((name += (predictTaken)? ",pt" : ",pn"), addr, isAnnuled, conditionCode); + this.predictTaken = predictTaken; + this.conditionFlag = conditionFlag; + } + + public boolean getPredictTaken() { + return predictTaken; + } + + public String getConditionFlagName() { + return SPARCV9ConditionFlags.getFlagName(conditionFlag); + } + + public int getConditionFlag() { + return conditionFlag; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + long address = addr.getDisplacement() + currentPc; + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + + // add conditionFlag bit used. + buf.append(getConditionFlagName()); + buf.append(comma); + buf.append(symFinder.getSymbolFor(address)); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9CasInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9CasInstruction.java new file mode 100644 index 00000000000..a13d64f59ef --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9CasInstruction.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9CasInstruction extends SPARCAtomicLoadStoreInstruction + implements SPARCV9Instruction { + final private SPARCRegister rs2; + final private int dataType; + + public SPARCV9CasInstruction(String name, SPARCRegisterIndirectAddress addr, + SPARCRegister rs2, SPARCRegister rd, int dataType) { + super(name, addr, rd); + this.rs2 = rs2; + this.dataType = dataType; + } + + public int getDataType() { + return dataType; + } + + public boolean isConditional() { + return true; + } + + public SPARCRegister getComparisonRegister() { + return rs2; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ConditionFlags.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ConditionFlags.java new file mode 100644 index 00000000000..317ea157d21 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ConditionFlags.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +class SPARCV9ConditionFlags { + private static final String ccFlagNames[] = { + "%fcc0", "%fcc1", "%fcc2", "%fcc3", "%icc", null, "%xcc", null + }; + + public static String getFlagName(int index) { + return ccFlagNames[index]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9Disassembler.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9Disassembler.java new file mode 100644 index 00000000000..7cb3b15b187 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9Disassembler.java @@ -0,0 +1,203 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +// Please refer to "The SPARC Architecture Manual - Version 9" + +public class SPARCV9Disassembler extends SPARCDisassembler + implements /* imports */ SPARCV9Opcodes { + public SPARCV9Disassembler(long startPc, byte[] code, SPARCV9InstructionFactory factory) { + super(startPc, code, factory); + } + + public SPARCV9Disassembler(long startPc, byte[] code) { + this(startPc, code, new SPARCV9InstructionFactoryImpl()); + } + + // decoders for format 2 instructions + private static InstructionDecoder format2Decoders[] = { + new UnimpDecoder(), + new V9IntegerBranchDecoder(), + new IntegerBranchDecoder(), + new V9IntRegisterBranchDecoder(), + new SethiDecoder(), + new V9FloatBranchDecoder(), + new FloatBranchDecoder(), + illegalDecoder + }; + + protected InstructionDecoder getFormat2Decoder(int op2) { + return format2Decoders[op2]; + } + + // op3 opcode table for op=3 (FORMAT_3) instructions - (memory instructions) + // E.2 Tables - Page 275 - Table 33. + private static final InstructionDecoder format3Decoders[][] = { + { + new LoadDecoder(LDUW, "ld" /* lduw */, RTLDT_UNSIGNED_WORD), new V9AlternateSpaceLoadDecoder(LDUWA, "lduwa", RTLDT_UNSIGNED_WORD), + new LoadDecoder(LDF,"ld", RTLDT_FL_SINGLE), new V9AlternateSpaceLoadDecoder(LDFA, "lda", RTLDT_FL_SINGLE) + }, + { + new LoadDecoder(LDUB, "ldub", RTLDT_UNSIGNED_BYTE), new V9AlternateSpaceLoadDecoder(LDUBA, "lduba", RTLDT_UNSIGNED_BYTE), + new V9SpecialLoadDecoder(LDFSR), illegalDecoder + }, + { + new LoadDecoder(LDUH, "lduh", RTLDT_UNSIGNED_HALF), new V9AlternateSpaceLoadDecoder(LDUHA, "lduha", RTLDT_UNSIGNED_HALF), + new LoadDecoder(LDQF, "ldq", RTLDT_FL_QUAD), new V9AlternateSpaceLoadDecoder(LDQFA, "ldqa", RTLDT_FL_QUAD) + }, + { + new LoadDecoder(LDD, "ldd", RTLDT_UNSIGNED_DWORD), new V9AlternateSpaceLoadDecoder(LDDA, "ldda", RTLDT_UNSIGNED_DWORD), + new LoadDecoder(LDDF, "ldd", RTLDT_FL_DOUBLE), new LoadDecoder(LDDFA, "ldda", RTLDT_FL_DOUBLE) + }, + { + new StoreDecoder(STW, "st" /* stw, stuw, stsw */, RTLDT_UNSIGNED_WORD), new V9AlternateSpaceStoreDecoder(STWA, "stwa", RTLDT_UNSIGNED_WORD), + new StoreDecoder(STF, "st", RTLDT_FL_SINGLE), new StoreDecoder(STFA, "st", RTLDT_FL_SINGLE), + }, + { + new StoreDecoder(STB, "stb", RTLDT_UNSIGNED_BYTE), new V9AlternateSpaceStoreDecoder(STBA, "stba", RTLDT_UNSIGNED_BYTE), + new V9SpecialStoreDecoder(STFSR), illegalDecoder + }, + { + new StoreDecoder(STH, "sth", RTLDT_UNSIGNED_HALF), new V9AlternateSpaceStoreDecoder(STHA, "stha", RTLDT_UNSIGNED_HALF), + new StoreDecoder(STQF, "stq", RTLDT_FL_QUAD), new V9AlternateSpaceStoreDecoder(STQFA, "stqa", RTLDT_FL_QUAD), + }, + { + new StoreDecoder(STD, "std", RTLDT_UNSIGNED_DWORD), new V9AlternateSpaceStoreDecoder(STDA, "stda", RTLDT_UNSIGNED_DWORD), + new StoreDecoder(STDF, "std", RTLDT_FL_DOUBLE), new V9AlternateSpaceStoreDecoder(STDFA, "stda", RTLDT_FL_DOUBLE) + }, + { + new LoadDecoder(LDSW, "ldsw", RTLDT_SIGNED_WORD), new V9AlternateSpaceLoadDecoder(LDSWA, "ldswa", RTLDT_SIGNED_WORD), + illegalDecoder, illegalDecoder + }, + { + new LoadDecoder(LDSB, "ldsb", RTLDT_SIGNED_BYTE), new V9AlternateSpaceLoadDecoder(LDSBA, "ldsba", RTLDT_UNSIGNED_BYTE), + illegalDecoder, illegalDecoder + }, + { + new LoadDecoder(LDSH, "ldsh", RTLDT_SIGNED_HALF), new V9AlternateSpaceLoadDecoder(LDSHA, "ldsha", RTLDT_UNSIGNED_HALF), + illegalDecoder, illegalDecoder + }, + { + new LoadDecoder(LDX, "ldx", RTLDT_UNSIGNED_DWORD), new V9AlternateSpaceLoadDecoder(LDXA, "ldxa", RTLDT_UNSIGNED_DWORD), + illegalDecoder, illegalDecoder + }, + { + illegalDecoder, illegalDecoder, + illegalDecoder, new V9CasDecoder(CASA, "casa", RTLDT_UNSIGNED_WORD) + }, + { + new LdstubDecoder(LDSTUB, "ldstub", RTLDT_UNSIGNED_BYTE), new V9AlternateSpaceLdstubDecoder(LDSTUBA, "ldstuba", RTLDT_UNSIGNED_BYTE), + new V9PrefetchDecoder(), new V9AlternateSpacePrefetchDecoder() + }, + { + new StoreDecoder(STX, "stx", RTLDT_UNSIGNED_DWORD), new V9AlternateSpaceStoreDecoder(STXA, "stxa", RTLDT_UNSIGNED_DWORD), + illegalDecoder, new V9CasDecoder(CASXA, "casxa", RTLDT_UNSIGNED_DWORD) + }, + { + new SwapDecoder(SWAP, "swap", RTLDT_UNSIGNED_WORD), new V9AlternateSpaceSwapDecoder(SWAPA, "swapa", RTLDT_UNSIGNED_WORD), + illegalDecoder, illegalDecoder + }, + }; + + protected InstructionDecoder getFormat3Decoder(int row, int column) { + return format3Decoders[row][column]; + } + + // op3 decoder table for op=2 (FORMAT_3A) instructions + // E Opcode Maps - Page 274 - Table 32 + + protected static final InstructionDecoder format3ADecoders[][] = { + { + new ArithmeticDecoder(ADD, "add", RTLOP_ADD), new ArithmeticDecoder(ADDcc, "addcc", RTLOP_ADD), + new ArithmeticDecoder(TADDcc, "taddcc", RTLOP_ADD), new V9WriteDecoder() + }, + { + new LogicDecoder(AND, "and", RTLOP_AND), new LogicDecoder(ANDcc, "andcc", RTLOP_AND), + new ArithmeticDecoder(TSUBcc, "tsubcc", RTLOP_ADD), new V9SavedRestoredDecoder() + }, + { + new LogicDecoder(OR, "or", RTLOP_OR), new LogicDecoder(ORcc, "orcc", RTLOP_OR), + new ArithmeticDecoder(TADDccTV, "taddcctv", RTLOP_ADD), new V9WrprDecoder() + }, + { + new LogicDecoder(XOR, "xor", RTLOP_XOR), new LogicDecoder(XORcc, "xorcc", RTLOP_XOR), + new ArithmeticDecoder(TSUBccTV, "tsubcctv", RTLOP_SUB), illegalDecoder + }, + { + new ArithmeticDecoder(SUB, "sub", RTLOP_SUB), new ArithmeticDecoder(SUBcc, "subcc", RTLOP_SUB), + new ArithmeticDecoder(MULScc, "mulscc", RTLOP_SMUL), new V9FPop1Decoder() + }, + { + new LogicDecoder(ANDN, "andn", RTLOP_NAND), new LogicDecoder(ANDNcc, "andncc", RTLOP_NAND), + new V9ShiftDecoder(SLL, "sll", RTLOP_SLL), new V9FPop2Decoder() + }, + { + new LogicDecoder(ORN, "orn", RTLOP_NOR), new LogicDecoder(ORNcc, "orncc", RTLOP_NOR), + new V9ShiftDecoder(SRL, "srl", RTLOP_SRL), new CoprocessorDecoder(IMPDEP1) + }, + { + new LogicDecoder(XNOR, "xnor", RTLOP_XNOR), new LogicDecoder(XNORcc, "xnorcc", RTLOP_XNOR), + new V9ShiftDecoder(SRA, "sra", RTLOP_SRA), new CoprocessorDecoder(IMPDEP2) + }, + { + new ArithmeticDecoder(ADDC, "addc", RTLOP_ADDC), new ArithmeticDecoder(ADDCcc, "addccc", RTLOP_ADDC), + new V9ReadDecoder(), new JmplDecoder() + }, + { + new ArithmeticDecoder(MULX, "mulx", RTLOP_UMUL), illegalDecoder, + illegalDecoder, new RettDecoder() + }, + { + new ArithmeticDecoder(UMUL, "umul", RTLOP_UMUL), new ArithmeticDecoder(UMULcc, "umulcc", RTLOP_UMUL), + new V9RdprDecoder(), new TrapDecoder() + }, + { + new ArithmeticDecoder(SMUL, "smul", RTLOP_SMUL), new ArithmeticDecoder(SMULcc, "smulcc", RTLOP_SMUL), + new V9FlushwDecoder(), new FlushDecoder() + }, + { + new ArithmeticDecoder(SUBC, "subc", RTLOP_SUBC), new ArithmeticDecoder(SUBCcc, "subccc", RTLOP_SUBC), + new V9MOVccDecoder(), new SaveDecoder() + }, + { + new ArithmeticDecoder(UDIVX, "udivx", RTLOP_UDIV), illegalDecoder, + new ArithmeticDecoder(SDIVX, "sdivx", RTLOP_SDIV), new RestoreDecoder() + }, + { + new ArithmeticDecoder(UDIV, "udiv", RTLOP_UDIV), new ArithmeticDecoder(UDIVcc, "udivcc", RTLOP_UDIV), + new V9PopcDecoder(), new V9DoneRetryDecoder() + }, + { + new ArithmeticDecoder(SDIV, "sdiv", RTLOP_SDIV), new ArithmeticDecoder(SDIVcc, "sdivcc", RTLOP_SDIV), + new V9MOVrDecoder(), illegalDecoder + } + }; + + protected InstructionDecoder getFormat3ADecoder(int row, int column) { + return format3ADecoders[row][column]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9DoneInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9DoneInstruction.java new file mode 100644 index 00000000000..3914a4dc048 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9DoneInstruction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9DoneInstruction extends SPARCInstruction + implements SPARCV9Instruction { + public SPARCV9DoneInstruction() { + super("done"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9FMOVccInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9FMOVccInstruction.java new file mode 100644 index 00000000000..d9b935a1258 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9FMOVccInstruction.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9FMOVccInstruction extends SPARCFPMoveInstruction + implements MoveInstruction { + final int conditionCode; + final int conditionFlag; + + public SPARCV9FMOVccInstruction(String name, int opf, int conditionCode, + int conditionFlag, SPARCFloatRegister rs, + SPARCFloatRegister rd) { + super(name, opf, rs, rd); + this.conditionFlag = conditionFlag; + this.conditionCode = conditionCode; + } + + public int getConditionCode() { + return conditionCode; + } + + public int getConditionFlag() { + return conditionFlag; + } + + public boolean isConditional() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9FMOVrInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9FMOVrInstruction.java new file mode 100644 index 00000000000..fd5189010b4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9FMOVrInstruction.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9FMOVrInstruction extends SPARCFPMoveInstruction + implements SPARCV9Instruction { + final private int regConditionCode; + final private SPARCRegister rs1; + + public SPARCV9FMOVrInstruction(String name, int opf, SPARCRegister rs1, + SPARCFloatRegister rs2, SPARCFloatRegister rd, + int regConditionCode) { + super(name, opf, rs2, rd); + this.regConditionCode = regConditionCode; + this.rs1 = rs1; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(rs1.toString()); + buf.append(comma); + buf.append(rs.toString()); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } + + public int getRegisterConditionCode() { + return regConditionCode; + } + + public boolean isConditional() { + return true; + } + + public Register getConditionRegister() { + return rs1; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9FlushwInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9FlushwInstruction.java new file mode 100644 index 00000000000..c15e158b047 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9FlushwInstruction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9FlushwInstruction extends SPARCInstruction + implements SPARCV9Instruction { + public SPARCV9FlushwInstruction() { + super("flushw"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9IlltrapInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9IlltrapInstruction.java new file mode 100644 index 00000000000..533e8e5a196 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9IlltrapInstruction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9IlltrapInstruction extends SPARCUnimpInstruction + implements SPARCV9Instruction { + public SPARCV9IlltrapInstruction(int const22) { + super("illtrap", const22); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ImpdepInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ImpdepInstruction.java new file mode 100644 index 00000000000..b10bc4fcc7e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ImpdepInstruction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9ImpdepInstruction extends SPARCInstruction + implements SPARCV9Instruction { + public SPARCV9ImpdepInstruction(String name) { + super(name); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9Instruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9Instruction.java new file mode 100644 index 00000000000..b0fe4bdfcee --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9Instruction.java @@ -0,0 +1,28 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +public interface SPARCV9Instruction extends SPARCV9Opcodes { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9InstructionFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9InstructionFactory.java new file mode 100644 index 00000000000..bd701e2c6f7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9InstructionFactory.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public interface SPARCV9InstructionFactory extends SPARCInstructionFactory { + public SPARCInstruction newV9BranchInstruction(String name, PCRelativeAddress addr, + boolean isAnnuled, int conditionCode, boolean predictTaken, int conditionFlag); + public SPARCInstruction newV9RegisterBranchInstruction(String name, PCRelativeAddress addr, + boolean isAnnuled, int regConditionCode, SPARCRegister conditionRegister, + boolean predictTaken); + public SPARCInstruction newV9CasInstruction(String name, SPARCRegisterIndirectAddress addr, + SPARCRegister rs2, SPARCRegister rd, int dataType); + public SPARCInstruction newV9PrefetchInstruction(String name, SPARCRegisterIndirectAddress addr, + int prefetchFcn); + public SPARCInstruction newV9FlushwInstruction(); + public SPARCInstruction newV9MOVccInstruction(String name, int conditionCode, int conditionFlag, + ImmediateOrRegister source, SPARCRegister rd); + public SPARCInstruction newV9MOVrInstruction(String name, SPARCRegister rs1, + ImmediateOrRegister operand2, SPARCRegister rd, + int regConditionCode); + public SPARCInstruction newV9RdprInstruction(int regNum, SPARCRegister rd); + public SPARCInstruction newV9WrprInstruction(SPARCRegister rs1, ImmediateOrRegister operand2, int regNum); + public SPARCInstruction newV9PopcInstruction(ImmediateOrRegister source, SPARCRegister rd); + public SPARCInstruction newV9DoneInstruction(); + public SPARCInstruction newV9RetryInstruction(); + public SPARCInstruction newV9SavedInstruction(); + public SPARCInstruction newV9RestoredInstruction(); + public SPARCInstruction newV9ReadInstruction(int specialRegNum, int asrRegNum, SPARCRegister rd); + public SPARCInstruction newV9WriteInstruction(int specialRegNum, int asrRegNum, SPARCRegister rs1, + ImmediateOrRegister operand2); + public SPARCInstruction newV9MembarInstruction(int mmask, int cmask); + public SPARCInstruction newV9SirInstruction(); + public SPARCInstruction newV9FMOVccInstruction(String name, int opf, int conditionCode, + int conditionFlag, SPARCFloatRegister rs, + SPARCFloatRegister rd); + public SPARCInstruction newV9FMOVrInstruction(String name, int opf, + SPARCRegister rs1, SPARCFloatRegister rs2, + SPARCFloatRegister rd, int regConditionCode); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9InstructionFactoryImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9InstructionFactoryImpl.java new file mode 100644 index 00000000000..f3fbcd1994f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9InstructionFactoryImpl.java @@ -0,0 +1,139 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9InstructionFactoryImpl extends SPARCInstructionFactoryImpl + implements SPARCV9InstructionFactory { + + public SPARCInstruction newUnimpInstruction(int const22) { + return new SPARCV9IlltrapInstruction(const22); + } + + public SPARCInstruction newRettInstruction(SPARCRegisterIndirectAddress addr) { + return new SPARCV9ReturnInstruction(addr); + } + + public SPARCInstruction newCoprocessorInstruction(int instruction, int cpopcode, int opc, + int rs1Num, int rs2Num, int rdNum) { + return new SPARCV9ImpdepInstruction(cpopcode == SPARCOpcodes.CPop1? "impdep1" : "impdep2"); + } + + public SPARCInstruction newV9ReadInstruction(int specialRegNum, int asrRegNum, SPARCRegister rd) { + return new SPARCV9ReadInstruction(specialRegNum, asrRegNum, rd); + } + + public SPARCInstruction newV9WriteInstruction(int specialRegNum, int asrRegNum, SPARCRegister rs1, + ImmediateOrRegister operand2) { + return new SPARCV9WriteInstruction(specialRegNum, asrRegNum, rs1, operand2); + } + + public SPARCInstruction newV9BranchInstruction(String name, PCRelativeAddress addr, + boolean isAnnuled, int conditionCode, boolean predictTaken, int conditionFlag) { + return new SPARCV9BranchInstruction(name, addr, isAnnuled, conditionCode, + predictTaken, conditionFlag); + } + + public SPARCInstruction newV9RegisterBranchInstruction(String name, PCRelativeAddress addr, + boolean isAnnuled, int regConditionCode, SPARCRegister conditionRegister, + boolean predictTaken) { + return new SPARCV9RegisterBranchInstruction(name, addr, isAnnuled, regConditionCode, + conditionRegister, predictTaken); + } + + public SPARCInstruction newV9CasInstruction(String name, SPARCRegisterIndirectAddress addr, + SPARCRegister rs2, SPARCRegister rd, int dataType) { + return new SPARCV9CasInstruction(name, addr, rs2, rd, dataType); + } + + public SPARCInstruction newV9PrefetchInstruction(String name, SPARCRegisterIndirectAddress addr, + int prefetchFcn) { + return new SPARCV9PrefetchInstruction(name, addr, prefetchFcn); + } + + public SPARCInstruction newV9FlushwInstruction() { + return new SPARCV9FlushwInstruction(); + } + + public SPARCInstruction newV9MOVccInstruction(String name, int conditionCode, int conditionFlag, + ImmediateOrRegister source, SPARCRegister rd) { + return new SPARCV9MOVccInstruction(name, conditionCode, conditionFlag, source, rd); + } + + public SPARCInstruction newV9MOVrInstruction(String name, SPARCRegister rs1, + ImmediateOrRegister operand2, SPARCRegister rd, + int regConditionCode) { + return new SPARCV9MOVrInstruction(name, rs1, operand2, rd, regConditionCode); + } + + public SPARCInstruction newV9RdprInstruction(int regNum, SPARCRegister rd) { + return new SPARCV9RdprInstruction(regNum, rd); + } + + public SPARCInstruction newV9WrprInstruction(SPARCRegister rs1, ImmediateOrRegister operand2, int regNum) { + return new SPARCV9WrprInstruction(rs1, operand2, regNum); + } + + public SPARCInstruction newV9PopcInstruction(ImmediateOrRegister source, SPARCRegister rd) { + return new SPARCV9PopcInstruction(source, rd); + } + + public SPARCInstruction newV9DoneInstruction() { + return new SPARCV9DoneInstruction(); + } + + public SPARCInstruction newV9RetryInstruction() { + return new SPARCV9RetryInstruction(); + } + + public SPARCInstruction newV9SavedInstruction() { + return new SPARCV9SavedInstruction(); + } + + public SPARCInstruction newV9RestoredInstruction() { + return new SPARCV9RestoredInstruction(); + } + + public SPARCInstruction newV9MembarInstruction(int mmask, int cmask) { + return new SPARCV9MembarInstruction(mmask, cmask); + } + + public SPARCInstruction newV9SirInstruction() { + return new SPARCV9SirInstruction(); + } + + public SPARCInstruction newV9FMOVccInstruction(String name, int opf, + int conditionCode, int conditionFlag, + SPARCFloatRegister rs, SPARCFloatRegister rd) { + return new SPARCV9FMOVccInstruction(name, opf, conditionCode, conditionFlag, rs, rd); + } + + public SPARCInstruction newV9FMOVrInstruction(String name, int opf, + SPARCRegister rs1, SPARCFloatRegister rs2, + SPARCFloatRegister rd, int regConditionCode) { + return new SPARCV9FMOVrInstruction(name, opf, rs1, rs2, rd, regConditionCode); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9MOVccInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9MOVccInstruction.java new file mode 100644 index 00000000000..9fdabf3d861 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9MOVccInstruction.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9MOVccInstruction extends SPARCMoveInstruction + implements SPARCV9Instruction { + final private int conditionFlag; // condition flag used icc, xcc, fccn etc. + final private int conditionCode; + + public SPARCV9MOVccInstruction(String name, int conditionCode, int conditionFlag, + ImmediateOrRegister source, SPARCRegister rd) { + super(name, MOVcc, source, rd); + this.conditionCode = conditionCode; + this.conditionFlag = conditionFlag; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(SPARCV9ConditionFlags.getFlagName(conditionFlag)); + buf.append(comma); + buf.append(getOperand2String()); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } + + public int getConditionCode() { + return conditionCode; + } + + public int getConditionFlag() { + return conditionFlag; + } + + public String getConditionFlagName() { + return SPARCV9ConditionFlags.getFlagName(conditionFlag); + } + + public boolean isConditional() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9MOVrInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9MOVrInstruction.java new file mode 100644 index 00000000000..6e9aab74c54 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9MOVrInstruction.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9MOVrInstruction extends SPARCMoveInstruction + implements SPARCV9Instruction { + final private int regConditionCode; + final private SPARCRegister rs1; + + public SPARCV9MOVrInstruction(String name, SPARCRegister rs1, + ImmediateOrRegister operand2, SPARCRegister rd, + int regConditionCode) { + super(name, MOVr, operand2, rd); + this.regConditionCode = regConditionCode; + this.rs1 = rs1; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(rs1.toString()); + buf.append(comma); + buf.append(getOperand2String()); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } + + public int getRegisterConditionCode() { + return regConditionCode; + } + + public boolean isConditional() { + return true; + } + + public Register getConditionRegister() { + return getSourceRegister1(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9MembarInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9MembarInstruction.java new file mode 100644 index 00000000000..26b9a486e98 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9MembarInstruction.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.SymbolFinder; +import java.util.Vector; + +public class SPARCV9MembarInstruction extends SPARCInstruction + implements SPARCV9Instruction { + final private int mmask; + final private int cmask; + final private String description; + + public SPARCV9MembarInstruction(int mmask, int cmask) { + super("membar"); + this.mmask = mmask & 0xF; + this.cmask = cmask & 0x7; + description = initDescription(); + } + + private String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + + Vector masks = new Vector(); + if ((mmask & 0x1) != 0) + masks.add("#LoadLoad"); + if ((mmask & 0x2) != 0) + masks.add("#StoreLoad"); + if ((mmask & 0x4) != 0) + masks.add("#LoadStore"); + if ((mmask & 0x8) != 0) + masks.add("#StoreStore"); + + if ((cmask & 0x1) != 0) + masks.add("#Lookaside"); + if ((cmask & 0x2) != 0) + masks.add("#MemIssue"); + if ((cmask & 0x4) != 0) + masks.add("#Sync"); + + // add all masks + Object[] tempMasks = masks.toArray(); + for (int i=0; i < tempMasks.length - 1; i++) { + buf.append((String)tempMasks[i]); + buf.append("| "); + } + buf.append((String)tempMasks[tempMasks.length - 1]); + + return buf.toString(); + } + + public int getMMask() { + return mmask; + } + + public int getCMask() { + return cmask; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9Opcodes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9Opcodes.java new file mode 100644 index 00000000000..49fb70a39ed --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9Opcodes.java @@ -0,0 +1,365 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +// Please refer to "The SPARC Architecture Manual - Version 9" + +public interface SPARCV9Opcodes extends SPARCOpcodes { + // format 2, v9 specific "op2" values. + + // branch on integer condition codes with prediction + public static final int OP_2_BPcc = 1; + + // branch on integer register contents with prediction + public static final int OP_2_BPr = 3; + + // branch on float condition codes with prediction + public static final int OP_2_FBPfcc = 5; + + // "rcond" - branch on register condition + public static final int BRANCH_RCOND_START_BIT = 25; + + // rcond is 3 bits length + public static final int BRANCH_RCOND_MASK = 7 << BRANCH_RCOND_START_BIT; + + // "rcond" - as used in conditional moves + public static final int CMOVE_RCOND_START_BIT = 10; + public static final int CMOVE_RCOND_MASK = 7 << CMOVE_RCOND_START_BIT; + + public static final int IMPDEP1 = CPop1; + public static final int IMPDEP2 = CPop2; + + // various rcond values - used in BPr, MOVr and FMOVr + + // reserved register condition + public static final int BRANCH_RCOND_RESERVED1 = 0; // 000 + + public static final int BRZ = 1; + public static final int MOVRZ = BRZ; + public static final int FMOVZ = BRZ; + + public static final int BRLEZ = 2; + public static final int MOVRLEZ = BRLEZ; + public static final int FMOVLEZ = BRLEZ; + + public static final int BRLZ = 3; + public static final int MOVRLZ = BRLZ; + public static final int FMOVLZ = BRLZ; + + // reserved register condition + public static final int BRANCH_RCOND_RESERVED2 = 4; // 100 + + public static final int BRNZ = 5; + public static final int MOVRNZ = BRNZ; + public static final int FMOVNZ = BRNZ; + + public static final int BRGZ = 6; + public static final int MOVGZ = BRGZ; + public static final int FMOVGZ = BRGZ; + + public static final int BRGEZ = 7; + public static final int MOVRGEZ = BRGEZ; + public static final int FMOVGEZ = BRGEZ; + + // "p" - prediction bit - predict branch taken or not taken + public static final int PREDICTION_START_BIT = 19; + public static final int PREDICTION_MASK = 1 << PREDICTION_START_BIT; + + // branch pc relative displacement - hi 2 bits of disp16. + public static final int DISP_16_HI_START_BIT = 20; + + // disp 16 hi is 2 bits length + public static final int DISP_16_HI_MASK = 3 << DISP_16_HI_START_BIT; + + // disp 16 low 14 bits + public static final int DISP_16_LO_START_BIT = 0; // just for completion. + public static final int DISP_16_LO_MASK = 0x3FFF; + public static final int DISP_16_LO_NUMBITS = 14; + + // disp 19 - integer branch with prediction - displacement + public static final int DISP_19_MASK = 0x7FFFF; + + /* + * condition code selected for integer branches - cc1 & cc0. + * condition code selected for float branches - cc1 & cc0. + * opf_cc field - floating conditional moves - 3 bits. + * convert 2 bit codes as 3 bit codes always and use following codes + * uniformly. + */ + + // opf_cc - 3 bits + public static final int OPF_CC_START_BIT = 11; + public static final int OPF_CC_MASK = 7 << OPF_CC_START_BIT; + + public static final int fcc0 = 0; // 000 + public static final int fcc1 = 1; // 001 + public static final int fcc2 = 2; // 010 + public static final int fcc3 = 3; // 011 + public static final int icc = 4; // 100 + public static final int CFLAG_RESERVED1 = 5; // 101 + public static final int xcc = 6; // 110 + public static final int CFLAG_RESERVED2 = 7; // 111 + + // cc0, cc1 as in integer, float predicted branches + public static final int BPcc_CC_START_BIT = 20; + public static final int BPcc_CC_MASK = 3 << BPcc_CC_START_BIT; + public static final int FBPfcc_CC_START_BIT = BPcc_CC_START_BIT; + public static final int FBPfcc_CC_MASK = BPcc_CC_MASK; + + // condition codes for integer branches with prediction - BPcc + public static final int CONDITION_BPN = CONDITION_BN; + public static final int CONDITION_BPE = CONDITION_BE; + public static final int CONDITION_BPLE = CONDITION_BLE; + public static final int CONDITION_BPL = CONDITION_BL; + public static final int CONDITION_BPLEU = CONDITION_BLEU; + public static final int CONDITION_BPCS = CONDITION_BCS; + public static final int CONDITION_BPNEG = CONDITION_BNEG; + public static final int CONDITION_BPVS = CONDITION_BVS; + public static final int CONDITION_BPA = CONDITION_BA; + public static final int CONDITION_BPNE = CONDITION_BNE; + public static final int CONDITION_BPG = CONDITION_BG; + public static final int CONDITION_BPGE = CONDITION_BGE; + public static final int CONDITION_BPGU = CONDITION_BGU; + public static final int CONDITION_BPCC = CONDITION_BCC; + public static final int CONDITION_BPPOS = CONDITION_BPOS; + public static final int CONDITION_BPVC = CONDITION_BVC; + + // condition codes for float branches with prediction + public static final int CONDITION_FBPN = CONDITION_BN; + public static final int CONDITION_FBPNE = CONDITION_BE; + public static final int CONDITION_FBPLG = CONDITION_BLE; + public static final int CONDITION_FBPUL = CONDITION_BL; + public static final int CONDITION_FBPL = CONDITION_BLEU; + public static final int CONDITION_FBPUG = CONDITION_BCS; + public static final int CONDITION_FBPG = CONDITION_BNEG; + public static final int CONDITION_FBPU = CONDITION_BVS; + public static final int CONDITION_FBPA = CONDITION_BA; + public static final int CONDITION_FBPE = CONDITION_BNE; + public static final int CONDITION_FBPUE = CONDITION_BG; + public static final int CONDITION_FBPGE = CONDITION_BGE; + public static final int CONDITION_FBPUGE= CONDITION_BGU; + public static final int CONDITION_FBPLE = CONDITION_BCC; + public static final int CONDITION_FBPULE= CONDITION_BPOS; + public static final int CONDITION_FBPO = CONDITION_BVC; + + // "cmask" - 3 bit mask used in membar for completion constraints + public static final int CMASK_START_BIT = 4; + public static final int CMASK_MASK = 7 << CMASK_START_BIT; + + // "mmask" - 4 bit mask used in member for ordering instruction classes. + public static final int MMASK_START_BIT = 0; + public static final int MMASK_MASK = 0xF; // no need to shift + + // v9 specific load/store instruction opcodes + // load/store instructions - op3 values - used with op=3 (FORMAT_3) + + public static final int LDUW = LD; + public static final int LDUWA = LDA; + + public static final int LDXFSR = LDFSR; + + public static final int LDFA = LDC; + public static final int LDQF = (2 << 4) | 2; + public static final int LDQFA = (3 << 4) | 2; + public static final int LDDFA = LDDC; + + public static final int STW = ST; + public static final int STWA = STA; + public static final int STFA = STC; + + public static final int STXFSR = STFSR; + + public static final int STQF = STDFQ; + public static final int STQFA = STDCQ; + public static final int STDFA = STDC; + + public static final int LDSW = 8; + public static final int LDSWA = (1 << 4) | 8; + + public static final int LDX = 0xB; + public static final int LDXA = (1 << 4) | 0xB; + + public static final int PREFETCH = (2 << 4) | 0xD; + public static final int PREFETCHA = (3 << 4) | 0xD; + + public static final int CASA = (3 << 4) | 0xC; + + public static final int STX = 0xE; + public static final int STXA = (1 << 4) | 0xE; + public static final int CASXA = (3 << 4) | 0xE; + + // 6 bit immediate shift count mask + public static final int SHIFT_COUNT_6_MASK = 0x3F; + + // X bit mask - used to differentiate b/w 32 bit and 64 bit shifts + public static final int X_MASK = 1 << 12; + + // E Opcode maps - Page 274 - Table 32 - op3 (op=2) table + // v9 specific items + public static final int ADDC = ADDX; + public static final int ADDCcc = ADDXcc; + + public static final int SUBC = SUBX; + public static final int SUBCcc = SUBXcc; + + public static final int MULX = 9; + public static final int UDIVX = 0xD; + + public static final int SLLX = SLL; + public static final int SRLX = SRL; + public static final int SRAX = SRA; + + // special register reads + public static final int RDCCR = RDY; + public static final int RDASI = RDY; + public static final int RDTICK = RDY; + public static final int RDPC = RDY; + public static final int RDFPRS = RDY; + public static final int MEMBAR = RDY; + public static final int STMBAR = RDY; + + public static final int RDPR = (2 << 4) | 0xA; + + public static final int FLUSHW = (2 << 4) | 0xB; + + public static final int MOVcc = (2 << 4) | 0xC; + + public static final int SDIVX = (2 << 4) | 0xD; + + public static final int POPC = (2 << 4) | 0xE; + + public static final int MOVr = (2 << 4) | 0xF; + + // special regitser writes + public static final int WRCCR = WRY; + public static final int WRASI = WRY; + public static final int WRFPRS = WRY; + public static final int SIR = WRY; + + public static final int SAVED = (3 << 4) | 0x1; + public static final int RESTORED = SAVED; + + public static final int WRPR = (3 << 4) | 0x2; + + public static final int RETURN = RETT; + + public static final int DONE = (3 << 4) | 0xE; + public static final int RETRY = DONE; + + // various integer condition code move instructions + public static final int CONDITION_MOVN = CONDITION_BN; + public static final int CONDITION_MOVE = CONDITION_BE; + public static final int CONDITION_MOVLE = CONDITION_BLE; + public static final int CONDITION_MOVL = CONDITION_BL; + public static final int CONDITION_MOVLEU = CONDITION_BLEU; + public static final int CONDITION_MOVCS = CONDITION_BCS; + public static final int CONDITION_MOVNEG = CONDITION_BNEG; + public static final int CONDITION_MOVVS = CONDITION_BVS; + public static final int CONDITION_MOVA = CONDITION_BA; + public static final int CONDITION_MOVNE = CONDITION_BNE; + public static final int CONDITION_MOVG = CONDITION_BG; + public static final int CONDITION_MOVGE = CONDITION_BGE; + public static final int CONDITION_MOVGU = CONDITION_BGU; + public static final int CONDITION_MOVCC = CONDITION_BCC; + public static final int CONDITION_MOVPOS = CONDITION_BPOS; + public static final int CONDITION_MOVVC = CONDITION_BVC; + + // cc0, cc1 & cc2 in conditional moves + public static final int CMOVE_CC_START_BIT = 11; + public static final int CMOVE_CC0_CC1_MASK = 3 << CMOVE_CC_START_BIT; + public static final int CMOVE_CC2_START_BIT = 18; + public static final int CMOVE_CC2_MASK = 1 << CMOVE_CC2_START_BIT; + + public static final int CMOVE_COND_START_BIT = 14; + // condition code is 4 bits + public static final int CMOVE_COND_MASK = 0xF << CMOVE_COND_START_BIT; + + // opf[8:0] (op=2,op3=0x34=FPop1) - Table 34 - Page 276 - E Opcode Maps + // v9 specific opcodes only - remaining are in SPARCOpcodes. + + public static final int FMOVd = 0x2; + public static final int FMOVq = 0x3; + public static final int FNEGd = 0x6; + public static final int FNEGq = 0x7; + public static final int FABSd = 0xA; + public static final int FABSq = 0xB; + public static final int FsTOx = (0x8 << 4) | 0x1; + public static final int FdTOx = (0x8 << 4) | 0x2; + public static final int FqTOx = (0x8 << 4) | 0x3; + public static final int FxTOs = (0x8 << 4) | 0x4; + public static final int FxTOd = (0x8 << 4) | 0x8; + public static final int FxTOq = (0x8 << 4) | 0xC; + + // opf[8:0] (op=2, op3=0x35= FPop2) - Table 35 - Page 277 - E.2 Tables + // v9 specific opcodes only 0 remanining are in SPARCOpcodes. + + // fp condition moves + + public static final int FMOVs_fcc0 = 1; + public static final int FMOVs_fcc1 = 1 | (0x4 << 4); + public static final int FMOVs_fcc2 = 1 | (0x8 << 4); + public static final int FMOVs_fcc3 = 1 | (0xC << 4); + public static final int FMOVs_icc = 1 | (0x10 << 4); + public static final int FMOVs_xcc = 1 | (0x18 << 4); + + public static final int FMOVd_fcc0 = 2; + public static final int FMOVd_fcc1 = 2 | (0x4 << 4); + public static final int FMOVd_fcc2 = 2 | (0x8 << 4); + public static final int FMOVd_fcc3 = 2 | (0xC << 4); + public static final int FMOVd_icc = 2 | (0x10 << 4); + public static final int FMOVd_xcc = 2 | (0x18 << 4); + + public static final int FMOVq_fcc0 = 3; + public static final int FMOVq_fcc1 = 3 | (0x4 << 4); + public static final int FMOVq_fcc2 = 3 | (0x8 << 4); + public static final int FMOVq_fcc3 = 3 | (0xC << 4); + public static final int FMOVq_icc = 3 | (0x10 << 4); + public static final int FMOVq_xcc = 3 | (0x18 << 4); + + // fp register condition moves + + public static final int FMOVRsZ = 5 | (0x2 << 4); + public static final int FMOVRsLEZ = 5 | (0x4 << 4); + public static final int FMOVRsLZ = 5 | (0x6 << 4); + public static final int FMOVRsNZ = 5 | (0xA << 4); + public static final int FMOVRsGZ = 5 | (0xC << 4); + public static final int FMOVRsGEZ = 5 | (0xE << 4); + + public static final int FMOVRdZ = 6 | (0x2 << 4); + public static final int FMOVRdLEZ = 6 | (0x4 << 4); + public static final int FMOVRdLZ = 6 | (0x6 << 4); + public static final int FMOVRdNZ = 6 | (0xA << 4); + public static final int FMOVRdGZ = 6 | (0xC << 4); + public static final int FMOVRdGEZ = 6 | (0xE << 4); + + public static final int FMOVRqZ = 7 | (0x2 << 4); + public static final int FMOVRqLEZ = 7 | (0x4 << 4); + public static final int FMOVRqLZ = 7 | (0x6 << 4); + public static final int FMOVRqNZ = 7 | (0xA << 4); + public static final int FMOVRqGZ = 7 | (0xC << 4); + public static final int FMOVRqGEZ = 7 | (0xE << 4); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PopcInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PopcInstruction.java new file mode 100644 index 00000000000..86373929255 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PopcInstruction.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9PopcInstruction extends SPARCFormat3AInstruction + implements SPARCV9Instruction { + public SPARCV9PopcInstruction(ImmediateOrRegister source, SPARCRegister rd) { + super("popc", POPC, null, source, rd); + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(getOperand2String()); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } + + public ImmediateOrRegister getSource() { + return operand2; + } + + public SPARCRegister getDestination() { + return rd; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PrefetchInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PrefetchInstruction.java new file mode 100644 index 00000000000..165423beeaa --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PrefetchInstruction.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9PrefetchInstruction extends SPARCInstruction + implements SPARCV9Instruction { + final private SPARCRegisterIndirectAddress addr; + final private int prefetchFcn; + final private String description; + + public static final int PREFETCH_MANY_READS = 0; + public static final int PREFETCH_ONE_READ = 1; + public static final int PREFETCH_MANY_WRITES = 2; + public static final int PREFETCH_ONE_WRITE = 3; + public static final int PREFETCH_PAGE = 4; + + public SPARCV9PrefetchInstruction(String name, SPARCRegisterIndirectAddress addr, int prefetchFcn) { + super(name); + this.addr = addr; + this.prefetchFcn = prefetchFcn; + description = initDescription(); + } + + private String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(addr.toString()); + buf.append(comma); + buf.append(prefetchFcn); + return buf.toString(); + } + + public int getPrefetchFunction() { + return prefetchFcn; + } + + public SPARCRegisterIndirectAddress getPrefetchAddress() { + return addr; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PrivilegedRegisterInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PrivilegedRegisterInstruction.java new file mode 100644 index 00000000000..8f66a63e50d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PrivilegedRegisterInstruction.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public abstract class SPARCV9PrivilegedRegisterInstruction extends SPARCInstruction + implements SPARCV9Instruction, /* imports */ SPARCV9PrivilegedRegisters { + protected static final String regNames[] = { + "%tpc", "%tnpc", "%tstate", "%tt", "%tick", "%tba", "%pstate", "%tl", + "%pil", "%cwp", "%cansave", "%canrestore", "%cleanwin", "%otherwin", "%wstate", "%fq" + }; + + protected static String getPrivilegedRegisterName(int regNum) { + if ((regNum > 15 && regNum < 31) || regNum > 31) + return null; + return (regNum == 31)? "%ver" : regNames[regNum]; + } + + final protected int regNum; + + protected abstract String getDescription(); + + protected SPARCV9PrivilegedRegisterInstruction(String name, int regNum) { + super(name); + this.regNum = regNum; + } + + public int getPrivilegedRegisterNumber() { + return regNum; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return getDescription(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PrivilegedRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PrivilegedRegisters.java new file mode 100644 index 00000000000..efd18a462a8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9PrivilegedRegisters.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +public interface SPARCV9PrivilegedRegisters { + public static final int TPC = 0; + public static final int TNPC = 1; + public static final int TSTATE = 2; + public static final int TT = 3; + public static final int TICK = 4; + public static final int TBA = 5; + public static final int PSTATE = 6; + public static final int TL = 7; + public static final int PIL = 8; + public static final int CWP = 9; + public static final int CANSAVE = 10; + public static final int CANRESTORE = 11; + public static final int CLEANWIN = 12; + public static final int OTHERWIN = 13; + public static final int WSTATE = 14; + public static final int FQ = 15; + public static final int VER = 31; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RdprInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RdprInstruction.java new file mode 100644 index 00000000000..ca4af6c5ed7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RdprInstruction.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +public class SPARCV9RdprInstruction extends SPARCV9PrivilegedRegisterInstruction { + final private SPARCRegister rd; + + public SPARCV9RdprInstruction(int regNum, SPARCRegister rd) { + super("rdpr", regNum); + this.rd = rd; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(getPrivilegedRegisterName(regNum)); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } + + public SPARCRegister getDestination() { + return rd; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ReadInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ReadInstruction.java new file mode 100644 index 00000000000..fb84bbac997 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ReadInstruction.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.SymbolFinder; +import sun.jvm.hotspot.utilities.Assert; + +public class SPARCV9ReadInstruction extends SPARCV9SpecialRegisterInstruction { + final private int specialReg; + final private int asrRegNum; + final private SPARCRegister rd; + + public SPARCV9ReadInstruction(int specialReg, int asrRegNum, SPARCRegister rd) { + super("rd"); + this.specialReg = specialReg; + this.asrRegNum = asrRegNum; + this.rd = rd; + } + + public int getSpecialRegister() { + return specialReg; + } + + public int getAncillaryRegister() { + if (Assert.ASSERTS_ENABLED) + Assert.that(specialReg == ASR, "not an ancillary register"); + return asrRegNum; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + if(specialReg == ASR) + buf.append("%asr" + asrRegNum); + else + buf.append(getSpecialRegisterName(specialReg)); + buf.append(comma); + buf.append(rd.toString()); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RegisterBranchInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RegisterBranchInstruction.java new file mode 100644 index 00000000000..0002c273469 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RegisterBranchInstruction.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9RegisterBranchInstruction extends SPARCInstruction + implements SPARCV9Instruction, BranchInstruction { + final protected PCRelativeAddress addr; + final protected boolean isAnnuled; + final protected int regConditionCode; + final protected SPARCRegister conditionRegister; + final protected boolean predictTaken; + + public SPARCV9RegisterBranchInstruction(String name, PCRelativeAddress addr, + boolean isAnnuled, int regConditionCode, + SPARCRegister conditionRegister, boolean predictTaken) { + super(name); + this.addr = addr; + this.isAnnuled = isAnnuled; + this.regConditionCode = regConditionCode; + this.conditionRegister = conditionRegister; + this.predictTaken = predictTaken; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + long address = addr.getDisplacement() + currentPc; + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(symFinder.getSymbolFor(address)); + return buf.toString(); + } + + public boolean isBranch() { + return true; + } + + public Address getBranchDestination() { + return addr; + } + + public boolean isAnnuledBranch() { + return isAnnuled; + } + + public boolean isConditional() { + return true; + } + + public int getRegisterConditionCode() { + return regConditionCode; + } + + public SPARCRegister getConditionRegister() { + return conditionRegister; + } + + public boolean getPredictTaken() { + return predictTaken; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RegisterIndirectAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RegisterIndirectAddress.java new file mode 100644 index 00000000000..4da4ddca98b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RegisterIndirectAddress.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +public class SPARCV9RegisterIndirectAddress extends SPARCRegisterIndirectAddress { + protected boolean indirectAsi; + + public SPARCV9RegisterIndirectAddress(SPARCRegister register, int offset) { + super(register, offset); + } + + public SPARCV9RegisterIndirectAddress(SPARCRegister base, SPARCRegister index) { + super(base, index); + } + + public boolean getIndirectAsi() { + return indirectAsi; + } + + public void setIndirectAsi(boolean indirectAsi) { + this.indirectAsi = indirectAsi; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getAddressWithoutAsi()); + if (indirectAsi) { + buf.append("%asi"); + } else if (addressSpace != -1) { + buf.append(Integer.toString(addressSpace)); + } + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RestoredInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RestoredInstruction.java new file mode 100644 index 00000000000..134a3ec1bbf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RestoredInstruction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9RestoredInstruction extends SPARCInstruction + implements SPARCV9Instruction { + public SPARCV9RestoredInstruction() { + super("restored"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RetryInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RetryInstruction.java new file mode 100644 index 00000000000..6355e397f0d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9RetryInstruction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9RetryInstruction extends SPARCInstruction + implements SPARCV9Instruction { + public SPARCV9RetryInstruction() { + super("retry"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ReturnInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ReturnInstruction.java new file mode 100644 index 00000000000..1a6040f6e74 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9ReturnInstruction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9ReturnInstruction extends SPARCRettInstruction + implements SPARCV9Instruction { + public SPARCV9ReturnInstruction(SPARCRegisterIndirectAddress addr) { + super("return", addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SavedInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SavedInstruction.java new file mode 100644 index 00000000000..227b811088f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SavedInstruction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9SavedInstruction extends SPARCInstruction + implements SPARCV9Instruction { + public SPARCV9SavedInstruction() { + super("saved"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SirInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SirInstruction.java new file mode 100644 index 00000000000..747f51df682 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SirInstruction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.SymbolFinder; + +public class SPARCV9SirInstruction extends SPARCInstruction + implements SPARCV9Instruction { + public SPARCV9SirInstruction() { + super("sir"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SpecialRegisterInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SpecialRegisterInstruction.java new file mode 100644 index 00000000000..d6f59d128fd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SpecialRegisterInstruction.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public abstract class SPARCV9SpecialRegisterInstruction + extends SPARCInstruction + implements /* import */ SPARCV9SpecialRegisters, SPARCV9Instruction { + protected SPARCV9SpecialRegisterInstruction(String name) { + super(name); + } + + protected abstract String getDescription(); + + public String asString(long currentPc, SymbolFinder symFinder) { + return getDescription(); + } + + protected static String[] specialRegNames = new String[] { + "%y", + null, + "%ccr", + "%asi", + "%tick", + "%pc", + "%fprs", + "%asr", + }; + + protected static String getSpecialRegisterName(int index) { + return specialRegNames[index]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SpecialRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SpecialRegisters.java new file mode 100644 index 00000000000..b01d4b526c6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9SpecialRegisters.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +public interface SPARCV9SpecialRegisters { + public static final int Y = 0; + public static final int CCR = 2; + public static final int ASI = 3; + public static final int TICK = 4; + public static final int PC = 5; + public static final int FPRS = 6; + public static final int ASR = 7; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9WriteInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9WriteInstruction.java new file mode 100644 index 00000000000..69bf33b8c55 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9WriteInstruction.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.Assert; + +public class SPARCV9WriteInstruction extends SPARCV9SpecialRegisterInstruction { + final private int specialReg; + final private int asrRegNum; + final private SPARCRegister rs1; + final private ImmediateOrRegister operand2; + + public SPARCV9WriteInstruction(int specialReg, int asrRegNum, SPARCRegister rs1, ImmediateOrRegister operand2) { + super("wr"); + this.specialReg = specialReg; + this.asrRegNum = asrRegNum; + this.rs1 = rs1; + this.operand2 = operand2; + } + + public int getSpecialRegister() { + return specialReg; + } + + public int getAncillaryRegister() { + if (Assert.ASSERTS_ENABLED) + Assert.that(specialReg == ASR, "not an ancillary register"); + return asrRegNum; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(rs1.toString()); + buf.append(comma); + if (operand2.isRegister()) { + buf.append(operand2.toString()); + } else { + Number number = ((Immediate)operand2).getNumber(); + buf.append("0x"); + buf.append(Integer.toHexString(number.intValue())); + } + buf.append(comma); + + if(specialReg == ASR) + buf.append("%asr" + asrRegNum); + else + buf.append(getSpecialRegisterName(specialReg)); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9WrprInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9WrprInstruction.java new file mode 100644 index 00000000000..72a9124cec0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCV9WrprInstruction.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +public class SPARCV9WrprInstruction extends SPARCV9PrivilegedRegisterInstruction { + final private SPARCRegister rs1; + final private ImmediateOrRegister operand2; + + public SPARCV9WrprInstruction(SPARCRegister rs1, ImmediateOrRegister operand2, + int regNum) { + super("wrpr", regNum); + this.rs1 = rs1; + this.operand2 = operand2; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(rs1.toString()); + buf.append(comma); + if (operand2.isRegister()) { + buf.append(operand2.toString()); + } else { + int value = ((Immediate)operand2).getNumber().intValue(); + buf.append(Integer.toHexString(value)); + } + buf.append(comma); + buf.append(getPrivilegedRegisterName(regNum)); + return buf.toString(); + } + + public SPARCRegister getSourceRegister1() { + return rs1; + } + + public ImmediateOrRegister getOperand2() { + return operand2; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCWriteInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCWriteInstruction.java new file mode 100644 index 00000000000..8a848c9d0b1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SPARCWriteInstruction.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.Assert; + +public class SPARCWriteInstruction extends SPARCSpecialRegisterInstruction { + final private int specialReg; + final private int asrRegNum; + final private SPARCRegister rs1; + final private ImmediateOrRegister operand2; + + public SPARCWriteInstruction(int specialReg, int asrRegNum, SPARCRegister rs1, ImmediateOrRegister operand2) { + super("wr"); + this.specialReg = specialReg; + this.asrRegNum = asrRegNum; + this.rs1 = rs1; + this.operand2 = operand2; + } + + public int getSpecialRegister() { + return specialReg; + } + + public int getAncillaryRegister() { + if (Assert.ASSERTS_ENABLED) + Assert.that(specialReg == ASR, "not an ancillary register"); + return asrRegNum; + } + + protected String getDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getName()); + buf.append(spaces); + buf.append(rs1.toString()); + buf.append(comma); + if (operand2.isRegister()) { + buf.append(operand2.toString()); + } else { + Number number = ((Immediate)operand2).getNumber(); + buf.append("0x"); + buf.append(Integer.toHexString(number.intValue())); + } + buf.append(comma); + + if(specialReg == ASR) + buf.append("%asr" + asrRegNum); + else + buf.append(getSpecialRegisterName(specialReg)); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SaveDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SaveDecoder.java new file mode 100644 index 00000000000..428b06a2c26 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SaveDecoder.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class SaveDecoder extends Format3ADecoder { + SaveDecoder() { + super(SAVE, "save", RTLOP_UNKNOWN); + } + + Instruction decodeFormat3AInstruction(int instruction, + SPARCRegister rs1, + ImmediateOrRegister operand2, + SPARCRegister rd, + SPARCInstructionFactory factory) { + return factory.newSaveInstruction(rs1, operand2, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SethiDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SethiDecoder.java new file mode 100644 index 00000000000..e152a7b7031 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SethiDecoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class SethiDecoder extends InstructionDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + Instruction instr = null; + int rdNum = getDestinationRegister(instruction); + SPARCRegister rd = SPARCRegisters.getRegister(rdNum); + int imm22 = (instruction & DISP_22_MASK); + if (imm22 == 0 && rd == SPARCRegisters.G0) { + instr = factory.newNoopInstruction(); + } else { + instr = factory.newSethiInstruction(imm22, rd); + } + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ShiftDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ShiftDecoder.java new file mode 100644 index 00000000000..bc4d826ff2e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/ShiftDecoder.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class ShiftDecoder extends InstructionDecoder { + final int op3; + final String name; + final int rtlOperation; + + ShiftDecoder(int op3, String name, int rtlOperation) { + this.op3 = op3; + this.name = name; + this.rtlOperation = rtlOperation; + } + + private ImmediateOrRegister getShiftLength(int instruction) { + boolean iBit = isIBitSet(instruction); + ImmediateOrRegister operand2 = null; + if (iBit) { + int value = instruction & SHIFT_COUNT_5_MASK; + operand2 = new Immediate(new Short((short)value)); + } else { + operand2 = SPARCRegisters.getRegister(getSourceRegister2(instruction)); + } + return operand2; + } + + Instruction decode(int instruction, + SPARCInstructionFactory factory) { + SPARCRegister rs1 = SPARCRegisters.getRegister(getSourceRegister1(instruction)); + SPARCRegister rd = SPARCRegisters.getRegister(getDestinationRegister(instruction)); + ImmediateOrRegister operand2 = getShiftLength(instruction); + return factory.newShiftInstruction(name, op3, rtlOperation, rs1, operand2, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SpecialLoadDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SpecialLoadDecoder.java new file mode 100644 index 00000000000..e380b930065 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SpecialLoadDecoder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class SpecialLoadDecoder extends SpecialLoadStoreDecoder { + SpecialLoadDecoder(int op3, String name, int specialRegNum) { + super(op3, name, specialRegNum); + } + + Instruction decodeSpecialLoadStoreInstruction(int cregNum, + SPARCRegisterIndirectAddress addr, + SPARCInstructionFactory factory) { + return factory.newSpecialLoadInstruction(name, specialRegNum, cregNum, addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SpecialLoadStoreDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SpecialLoadStoreDecoder.java new file mode 100644 index 00000000000..08d2c5e7b3c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SpecialLoadStoreDecoder.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class SpecialLoadStoreDecoder extends MemoryInstructionDecoder { + final int specialRegNum; + + SpecialLoadStoreDecoder(int op3, String name, int specialRegNum) { + super(op3, name, RTLDT_UNKNOWN); + this.specialRegNum = specialRegNum; + } + + final Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, SPARCInstructionFactory factory) { + int cregNum = getSourceRegister1(instruction); + return decodeSpecialLoadStoreInstruction(cregNum, addr, factory); + } + + abstract Instruction decodeSpecialLoadStoreInstruction(int cregNum, + SPARCRegisterIndirectAddress addr, + SPARCInstructionFactory factory); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SpecialStoreDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SpecialStoreDecoder.java new file mode 100644 index 00000000000..856968faf4f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SpecialStoreDecoder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class SpecialStoreDecoder extends SpecialLoadStoreDecoder { + SpecialStoreDecoder(int op3, String name, int specialRegNum) { + super(op3, name, specialRegNum); + } + + Instruction decodeSpecialLoadStoreInstruction(int cregNum, + SPARCRegisterIndirectAddress addr, + SPARCInstructionFactory factory) { + return factory.newSpecialStoreInstruction(name, specialRegNum, cregNum, addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/StoreDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/StoreDecoder.java new file mode 100644 index 00000000000..f96f8e9dd8b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/StoreDecoder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class StoreDecoder extends MemoryInstructionDecoder { + StoreDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, + SPARCInstructionFactory factory) { + return factory.newStoreInstruction(name, op3, addr, rd, dataType); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SwapDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SwapDecoder.java new file mode 100644 index 00000000000..9192a1f5d57 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/SwapDecoder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class SwapDecoder extends MemoryInstructionDecoder { + SwapDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, + SPARCInstructionFactory factory) { + return factory.newSwapInstruction(name, addr, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/TrapDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/TrapDecoder.java new file mode 100644 index 00000000000..eb91a85fde3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/TrapDecoder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class TrapDecoder extends InstructionDecoder { + private static final String trapConditionNames[] = { + "tn", "te", "tle", "tl", "tleu", "tcs", "tneg", "tvs", + "ta", "tne", "tg", "tge", "tgu" , "tcc", "tpos", "tvc" + }; + + static String getTrapConditionName(int index) { + return trapConditionNames[index]; + } + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + int conditionCode = getConditionCode(instruction); + return factory.newTrapInstruction(getTrapConditionName(conditionCode), + conditionCode); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/UnimpDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/UnimpDecoder.java new file mode 100644 index 00000000000..d28d6150aa7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/UnimpDecoder.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class UnimpDecoder extends InstructionDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + return factory.newUnimpInstruction(instruction & DISP_22_MASK); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V8FPop1Decoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V8FPop1Decoder.java new file mode 100644 index 00000000000..30c6d94a49c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V8FPop1Decoder.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import java.util.*; + +class V8FPop1Decoder extends FPopDecoder { + static Map opfDecoders = new HashMap(); // Map + static void addOpfDecoder(int fpOpcode, InstructionDecoder decoder) { + opfDecoders.put(new Integer(fpOpcode), decoder); + } + + // opf (op=2, op3=0x34=FPop1) - Table F -5 - Page 230. + static { + addOpfDecoder(FMOVs, new FPMoveDecoder(FMOVs, "fmovs", RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FNEGs, new FP2RegisterDecoder(FNEGs, "fnegs", RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FABSs, new FP2RegisterDecoder(FABSs, "fabss", RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FSQRTs, new FP2RegisterDecoder(FSQRTs, "fsqrts", RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FSQRTd, new FP2RegisterDecoder(FSQRTd, "fsqrtd", RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE)); + addOpfDecoder(FSQRTq, new FP2RegisterDecoder(FSQRTq, "fsqrtq", RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + addOpfDecoder(FADDs, new FPArithmeticDecoder(FADDs, "fadds", RTLOP_ADD, RTLDT_FL_SINGLE, RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FADDd, new FPArithmeticDecoder(FADDd, "faddd", RTLOP_ADD, RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE)); + addOpfDecoder(FADDq, new FPArithmeticDecoder(FADDq, "faddq", RTLOP_ADD, RTLDT_FL_QUAD, RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + addOpfDecoder(FSUBs, new FPArithmeticDecoder(FSUBs, "fsubs", RTLOP_SUB, RTLDT_FL_SINGLE, RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FSUBd, new FPArithmeticDecoder(FSUBd, "fsubd", RTLOP_SUB, RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE)); + addOpfDecoder(FSUBq, new FPArithmeticDecoder(FSUBq, "fsubq", RTLOP_SUB, RTLDT_FL_QUAD, RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + addOpfDecoder(FMULs, new FPArithmeticDecoder(FMULs, "fmuls", RTLOP_SMUL, RTLDT_FL_SINGLE, RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FMULd, new FPArithmeticDecoder(FMULd, "fmuld", RTLOP_SMUL, RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE)); + addOpfDecoder(FMULq, new FPArithmeticDecoder(FMULq, "fmulq",RTLOP_SMUL, RTLDT_FL_QUAD, RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + addOpfDecoder(FsMULd, new FPArithmeticDecoder(FsMULd, "fsmuld", RTLOP_SMUL, RTLDT_FL_SINGLE, RTLDT_FL_SINGLE, RTLDT_FL_DOUBLE)); + addOpfDecoder(FdMULq, new FPArithmeticDecoder(FdMULq, "fdmulq",RTLOP_SMUL, RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE, RTLDT_FL_QUAD)); + addOpfDecoder(FDIVs, new FPArithmeticDecoder(FDIVs, "fdivs", RTLOP_SDIV, RTLDT_FL_SINGLE, RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FDIVd, new FPArithmeticDecoder(FDIVd, "fdivd", RTLOP_SDIV,RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE)); + addOpfDecoder(FDIVq, new FPArithmeticDecoder(FDIVq, "fdivq", RTLOP_SDIV,RTLDT_FL_QUAD, RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + addOpfDecoder(FiTOs, new FP2RegisterDecoder(FiTOs, "fitos", RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FiTOd, new FP2RegisterDecoder(FiTOd, "fitod", RTLDT_FL_SINGLE, RTLDT_FL_DOUBLE)); + addOpfDecoder(FiTOq, new FP2RegisterDecoder(FiTOq, "fitoq", RTLDT_FL_SINGLE, RTLDT_FL_QUAD)); + addOpfDecoder(FsTOi, new FP2RegisterDecoder(FsTOi, "fstoi", RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FdTOi, new FP2RegisterDecoder(FdTOi, "fdtoi", RTLDT_FL_DOUBLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FqTOi, new FP2RegisterDecoder(FqTOi, "fqtoi", RTLDT_FL_QUAD, RTLDT_FL_SINGLE)); + addOpfDecoder(FsTOd, new FP2RegisterDecoder(FsTOd, "fstod", RTLDT_FL_SINGLE, RTLDT_FL_DOUBLE)); + addOpfDecoder(FsTOq, new FP2RegisterDecoder(FsTOq, "fstoq", RTLDT_FL_SINGLE, RTLDT_FL_QUAD)); + addOpfDecoder(FdTOs, new FP2RegisterDecoder(FdTOs, "fdtos", RTLDT_FL_DOUBLE, RTLDT_FL_SINGLE)); + addOpfDecoder(FdTOq, new FP2RegisterDecoder(FdTOq, "fdtoq", RTLDT_FL_DOUBLE, RTLDT_FL_QUAD)); + addOpfDecoder(FqTOs, new FP2RegisterDecoder(FqTOs, "fqtos", RTLDT_FL_QUAD, RTLDT_FL_SINGLE)); + addOpfDecoder(FqTOd, new FP2RegisterDecoder(FqTOd, "fqtod", RTLDT_FL_QUAD, RTLDT_FL_DOUBLE)); + } + + InstructionDecoder getOpfDecoder(int opf) { + return (InstructionDecoder) opfDecoders.get(new Integer(opf)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V8FPop2Decoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V8FPop2Decoder.java new file mode 100644 index 00000000000..f25da88a5f6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V8FPop2Decoder.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import java.util.*; + +class V8FPop2Decoder extends FPopDecoder { + static Map fpop2Decoders = new HashMap(); // Map + static void addFPop2Decoder(int fpOpcode, InstructionDecoder decoder) { + fpop2Decoders.put(new Integer(fpOpcode), decoder); + } + + // opf (op=2, op3=0x35=FPop2 - Table F-6 page 231 + static { + addFPop2Decoder(FCMPs, new FP2RegisterDecoder(FCMPs, "fcmps", RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addFPop2Decoder(FCMPd, new FP2RegisterDecoder(FCMPd, "fcmpd", RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE)); + addFPop2Decoder(FCMPq, new FP2RegisterDecoder(FCMPq, "fcmpq", RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + addFPop2Decoder(FCMPEs, new FP2RegisterDecoder(FCMPEs, "fcmpes", RTLDT_FL_SINGLE, RTLDT_FL_SINGLE)); + addFPop2Decoder(FCMPEd, new FP2RegisterDecoder(FCMPEd, "fcmped", RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE)); + addFPop2Decoder(FCMPEq, new FP2RegisterDecoder(FCMPEq, "fcmpeq", RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + } + + InstructionDecoder getOpfDecoder(int opf) { + return (InstructionDecoder) fpop2Decoders.get(new Integer(opf)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceDecoder.java new file mode 100644 index 00000000000..97403b5e5bd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceDecoder.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class V9AlternateSpaceDecoder extends MemoryInstructionDecoder + implements V9InstructionDecoder { + V9AlternateSpaceDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + SPARCRegisterIndirectAddress newRegisterIndirectAddress(SPARCRegister rs1, SPARCRegister rs2) { + return new SPARCV9RegisterIndirectAddress(rs1, rs2); + } + + SPARCRegisterIndirectAddress newRegisterIndirectAddress(SPARCRegister rs1, int offset) { + return new SPARCV9RegisterIndirectAddress(rs1, offset); + } + + abstract Instruction decodeV9AsiLoadStore(int instruction, + SPARCV9RegisterIndirectAddress addr, + SPARCRegister rd, + SPARCV9InstructionFactory factory); + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, SPARCInstructionFactory factory) { + SPARCV9RegisterIndirectAddress v9addr = (SPARCV9RegisterIndirectAddress) addr; + if (isIBitSet(instruction)) { + // indirect asi + v9addr.setIndirectAsi(true); + } else { + // immediate asi + int asi = (instruction & ASI_MASK) >>> ASI_START_BIT; + v9addr.setAddressSpace(asi); + } + return decodeV9AsiLoadStore(instruction, v9addr, rd, + (SPARCV9InstructionFactory) factory); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceLdstubDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceLdstubDecoder.java new file mode 100644 index 00000000000..81a131159b4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceLdstubDecoder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9AlternateSpaceLdstubDecoder extends V9AlternateSpaceDecoder { + V9AlternateSpaceLdstubDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeV9AsiLoadStore(int instruction, + SPARCV9RegisterIndirectAddress addr, + SPARCRegister rd, + SPARCV9InstructionFactory factory) { + return factory.newLdstubInstruction(name, addr, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceLoadDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceLoadDecoder.java new file mode 100644 index 00000000000..e58ce6478b5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceLoadDecoder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9AlternateSpaceLoadDecoder extends V9AlternateSpaceDecoder { + V9AlternateSpaceLoadDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeV9AsiLoadStore(int instruction, + SPARCV9RegisterIndirectAddress addr, + SPARCRegister rd, + SPARCV9InstructionFactory factory) { + return factory.newLoadInstruction(name, op3, addr, rd, dataType); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpacePrefetchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpacePrefetchDecoder.java new file mode 100644 index 00000000000..fa5ed94428a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpacePrefetchDecoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9AlternateSpacePrefetchDecoder extends V9AlternateSpaceDecoder { + V9AlternateSpacePrefetchDecoder() { + // Fake the destination with an integer type so we can get fcn from rd + super(PREFETCHA, "prefetcha", RTLDT_SIGNED_WORD); + } + + Instruction decodeV9AsiLoadStore(int instruction, + SPARCV9RegisterIndirectAddress addr, + SPARCRegister rd, + SPARCV9InstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + return v9factory.newV9PrefetchInstruction(name, addr, rd.getNumber()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceStoreDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceStoreDecoder.java new file mode 100644 index 00000000000..f321eedaa20 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceStoreDecoder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9AlternateSpaceStoreDecoder extends V9AlternateSpaceDecoder { + V9AlternateSpaceStoreDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeV9AsiLoadStore(int instruction, + SPARCV9RegisterIndirectAddress addr, + SPARCRegister rd, + SPARCV9InstructionFactory factory) { + return factory.newStoreInstruction(name, op3, addr, rd, dataType); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceSwapDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceSwapDecoder.java new file mode 100644 index 00000000000..ce3983389c5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9AlternateSpaceSwapDecoder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9AlternateSpaceSwapDecoder extends V9AlternateSpaceDecoder { + V9AlternateSpaceSwapDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeV9AsiLoadStore(int instruction, + SPARCV9RegisterIndirectAddress addr, + SPARCRegister rd, + SPARCV9InstructionFactory factory) { + return factory.newSwapInstruction(name, addr, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9BranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9BranchDecoder.java new file mode 100644 index 00000000000..c3ec53a5ebd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9BranchDecoder.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class V9BranchDecoder extends BranchDecoder + implements /* imports */ V9InstructionDecoder { + static boolean getPredictTaken(int instruction) { + return (PREDICTION_MASK & instruction) != 0; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9CCBranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9CCBranchDecoder.java new file mode 100644 index 00000000000..238fa502d75 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9CCBranchDecoder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class V9CCBranchDecoder extends V9BranchDecoder { + abstract int getConditionFlag(int instruction); + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + int conditionFlag = getConditionFlag(instruction); + boolean predictTaken = getPredictTaken(instruction); + int conditionCode = getConditionCode(instruction); + boolean annuled = getAnnuledBit(instruction); + String name = getConditionName(conditionCode, annuled); + // signed word aligned 19 bit + PCRelativeAddress addr = new PCRelativeAddress(extractSignedIntFromNBits(instruction, 19) << 2); + return v9factory.newV9BranchInstruction(name, addr, annuled, conditionCode, + predictTaken, conditionFlag); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9CMoveDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9CMoveDecoder.java new file mode 100644 index 00000000000..3b296e848c8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9CMoveDecoder.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.Assert; + +abstract class V9CMoveDecoder extends InstructionDecoder + implements V9InstructionDecoder { + static private final String iccConditionNames[] = { + "n", "e", "le", "l", "leu", "cs", "neg", "vs", + "a", "ne", "g", "ge", "gu", "cc", "pos", "vc" + }; + + static private final String fccConditionNames[] = { + "fn", "fne", "flg", "ful", "fl", "fug", "fg", "fu", + "fa", "fe", "fue", "fge", "fuge", "fle", "fule", "fo" + }; + + static String getConditionName(int conditionCode, int conditionFlag) { + return (conditionFlag == icc || conditionFlag == xcc) ? + iccConditionNames[conditionCode] + : fccConditionNames[conditionCode]; + } + + static int getMoveConditionCode(int instruction) { + return (instruction & CMOVE_COND_MASK) >>> CMOVE_COND_START_BIT; + } + + static int getRegisterConditionCode(int instruction) { + return (instruction & CMOVE_RCOND_MASK) >>> CMOVE_RCOND_START_BIT; + } + + static ImmediateOrRegister getCMoveSource(int instruction, int numBits) { + ImmediateOrRegister source = null; + if (isIBitSet(instruction)) { + source = new Immediate(new Short((short) extractSignedIntFromNBits(instruction, numBits))); + } else { + source = SPARCRegisters.getRegister(getSourceRegister2(instruction)); + } + return source; + } + + static String getFloatTypeCode(int dataType) { + String result = null; + switch(dataType) { + case RTLDT_FL_SINGLE: + result = "s"; + break; + + case RTLDT_FL_DOUBLE: + result = "d"; + break; + + case RTLDT_FL_QUAD: + result = "q"; + break; + + default: + if (Assert.ASSERTS_ENABLED) + Assert.that(false, "should not reach here"); + } + return result; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9CasDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9CasDecoder.java new file mode 100644 index 00000000000..5c4bec496ac --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9CasDecoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9CasDecoder extends V9AlternateSpaceDecoder { + V9CasDecoder(int op3, String name, int dataType) { + super(op3, name, dataType); + } + + Instruction decodeV9AsiLoadStore(int instruction, + SPARCV9RegisterIndirectAddress addr, + SPARCRegister rd, + SPARCV9InstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + SPARCRegister rs2 = SPARCRegisters.getRegister(getSourceRegister2(instruction)); + return v9factory.newV9CasInstruction(name, addr, rs2, rd, dataType); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9DoneRetryDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9DoneRetryDecoder.java new file mode 100644 index 00000000000..b5a9f6a6165 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9DoneRetryDecoder.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9DoneRetryDecoder extends InstructionDecoder + implements V9InstructionDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + Instruction instr = null; + int rdNum = getDestinationRegister(instruction); + // "rd" field is "fcn". Only values 0 and 1 are defined. + // see page 157 - A.11 Done and Retry + switch (rdNum) { + case 0: + instr = v9factory.newV9DoneInstruction(); + break; + case 1: + instr = v9factory.newV9RetryInstruction(); + break; + default: + instr = v9factory.newIllegalInstruction(instruction); + break; + } + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FMOVccDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FMOVccDecoder.java new file mode 100644 index 00000000000..4b92f556f03 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FMOVccDecoder.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.Assert; + +class V9FMOVccDecoder extends V9CMoveDecoder implements /* imports */ RTLDataTypes { + private final int opf; + private final int dataType; + + V9FMOVccDecoder(int opf, int dataType) { + this.opf = opf; + this.dataType = dataType; + } + + private static String getFMoveCCName(int conditionCode, int conditionFlag, int dataType) { + StringBuffer buf = new StringBuffer("fmov"); + buf.append(getFloatTypeCode(dataType)); + buf.append(getConditionName(conditionCode, conditionFlag)); + return buf.toString(); + } + + private static int getFMoveConditionFlag(int instruction) { + return (instruction & OPF_CC_MASK) >>> OPF_CC_START_BIT; + } + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + Instruction instr = null; + int conditionFlag = getFMoveConditionFlag(instruction); + if (conditionFlag == CFLAG_RESERVED1 || conditionFlag == CFLAG_RESERVED2) { + instr = v9factory.newIllegalInstruction(instruction); + } else { + int rdNum = getDestinationRegister(instruction); + int rs1Num = getSourceRegister1(instruction); + SPARCRegister rd = RegisterDecoder.decode(dataType, rdNum); + int conditionCode = getMoveConditionCode(instruction); + SPARCRegister rs = RegisterDecoder.decode(dataType, rs1Num); + String name = getFMoveCCName(conditionCode, conditionFlag, dataType); + instr = v9factory.newV9FMOVccInstruction(name,opf, conditionCode, conditionFlag, + (SPARCFloatRegister)rs, (SPARCFloatRegister)rd); + } + + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FMOVrDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FMOVrDecoder.java new file mode 100644 index 00000000000..d66d844ed85 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FMOVrDecoder.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9FMOVrDecoder extends V9CMoveDecoder { + private final int opf; + private final String name; + private final int dataType; + + V9FMOVrDecoder(int opf, String name, int dataType) { + this.opf = opf; + this.name = name; + this.dataType = dataType; + } + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + int regConditionCode = getRegisterConditionCode(instruction); + int rdNum = getDestinationRegister(instruction); + int rs1Num = getSourceRegister1(instruction); + int rs2Num = getSourceRegister2(instruction); + SPARCRegister rd = RegisterDecoder.decode(dataType, rdNum); + SPARCRegister rs2 = RegisterDecoder.decode(dataType, rs2Num); + SPARCRegister rs1 = SPARCRegisters.getRegister(rs1Num); + return v9factory.newV9FMOVrInstruction(name, opf, rs1, (SPARCFloatRegister)rs2, + (SPARCFloatRegister)rd, regConditionCode); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FPop1Decoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FPop1Decoder.java new file mode 100644 index 00000000000..e0d8b774bc0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FPop1Decoder.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import java.util.*; + +class V9FPop1Decoder extends FPopDecoder + implements V9InstructionDecoder { + static Map v9opfDecoders = new HashMap(); // Map + static void addV9OpfDecoder(int fpOpcode, InstructionDecoder decoder) { + v9opfDecoders.put(new Integer(fpOpcode), decoder); + } + + static { + addV9OpfDecoder(FMOVd, new FPMoveDecoder(FMOVd, "fmovd", RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE)); + addV9OpfDecoder(FMOVq, new FPMoveDecoder(FMOVq, "fmovq", RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + addV9OpfDecoder(FNEGd, new FP2RegisterDecoder(FNEGd, "fnegd", RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE)); + addV9OpfDecoder(FNEGq, new FP2RegisterDecoder(FNEGq, "fnegq", RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + addV9OpfDecoder(FABSd, new FP2RegisterDecoder(FABSd, "fabsd", RTLDT_FL_DOUBLE, RTLDT_FL_DOUBLE)); + addV9OpfDecoder(FABSq, new FP2RegisterDecoder(FABSq, "fabsq", RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + addV9OpfDecoder(FsTOx, new FP2RegisterDecoder(FsTOx, "fstox", RTLDT_FL_SINGLE, RTLDT_FL_QUAD)); + addV9OpfDecoder(FdTOx, new FP2RegisterDecoder(FdTOx, "fdtox", RTLDT_FL_DOUBLE, RTLDT_FL_QUAD)); + addV9OpfDecoder(FqTOx, new FP2RegisterDecoder(FqTOx, "fqtox", RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + addV9OpfDecoder(FxTOs, new FP2RegisterDecoder(FxTOs, "fxtos", RTLDT_FL_QUAD, RTLDT_FL_SINGLE)); + addV9OpfDecoder(FxTOd, new FP2RegisterDecoder(FxTOd, "fxtod", RTLDT_FL_QUAD, RTLDT_FL_SINGLE)); + addV9OpfDecoder(FxTOq, new FP2RegisterDecoder(FxTOq, "fxtoq", RTLDT_FL_QUAD, RTLDT_FL_QUAD)); + } + + InstructionDecoder getOpfDecoder(int opf) { + InstructionDecoder decoder = (InstructionDecoder) V8FPop1Decoder.opfDecoders.get(new Integer(opf)); + return (decoder !=null)? decoder : (InstructionDecoder) v9opfDecoders.get(new Integer(opf)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FPop2Decoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FPop2Decoder.java new file mode 100644 index 00000000000..629b3bea794 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FPop2Decoder.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; +import java.util.*; + +class V9FPop2Decoder extends FPopDecoder + implements V9InstructionDecoder { + static Map v9fpop2Decoders = new HashMap(); // Map + static void addV9FPop2Decoder(int fpOpcode, InstructionDecoder decoder) { + v9fpop2Decoders.put(new Integer(fpOpcode), decoder); + } + + static { + // flag conditional moves + + addV9FPop2Decoder(FMOVs_fcc0, new V9FMOVccDecoder(FMOVs_fcc0, RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVs_fcc1, new V9FMOVccDecoder(FMOVs_fcc1, RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVs_fcc2, new V9FMOVccDecoder(FMOVs_fcc2, RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVs_fcc3, new V9FMOVccDecoder(FMOVs_fcc3, RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVs_icc, new V9FMOVccDecoder(FMOVs_icc, RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVs_xcc, new V9FMOVccDecoder(FMOVs_xcc, RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVd_fcc0, new V9FMOVccDecoder(FMOVd_fcc0, RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVd_fcc1, new V9FMOVccDecoder(FMOVd_fcc1, RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVd_fcc2, new V9FMOVccDecoder(FMOVd_fcc2, RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVd_fcc3, new V9FMOVccDecoder(FMOVd_fcc3, RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVd_icc, new V9FMOVccDecoder(FMOVd_icc, RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVd_xcc, new V9FMOVccDecoder(FMOVd_xcc, RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVq_fcc0, new V9FMOVccDecoder(FMOVq_fcc0, RTLDT_FL_QUAD)); + addV9FPop2Decoder(FMOVq_fcc1, new V9FMOVccDecoder(FMOVq_fcc1, RTLDT_FL_QUAD)); + addV9FPop2Decoder(FMOVq_fcc2, new V9FMOVccDecoder(FMOVq_fcc2, RTLDT_FL_QUAD)); + addV9FPop2Decoder(FMOVq_fcc3, new V9FMOVccDecoder(FMOVq_fcc3, RTLDT_FL_QUAD)); + addV9FPop2Decoder(FMOVq_icc, new V9FMOVccDecoder(FMOVq_icc, RTLDT_FL_QUAD)); + addV9FPop2Decoder(FMOVq_xcc, new V9FMOVccDecoder(FMOVq_xcc, RTLDT_FL_QUAD)); + + // register conditional moves + + addV9FPop2Decoder(FMOVRsZ, new V9FMOVrDecoder(FMOVRsZ, "fmovrsz", RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVRsLEZ, new V9FMOVrDecoder(FMOVRsLEZ, "fmovrslez", RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVRsLZ, new V9FMOVrDecoder(FMOVRsLZ, "fmovrslz", RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVRsNZ, new V9FMOVrDecoder(FMOVRsNZ, "fmovrsnz", RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVRsGZ, new V9FMOVrDecoder(FMOVRsGZ, "fmovrsgz", RTLDT_FL_SINGLE)); + addV9FPop2Decoder(FMOVRsGEZ, new V9FMOVrDecoder(FMOVRsGEZ, "fmovrsgez", RTLDT_FL_SINGLE)); + + addV9FPop2Decoder(FMOVRdZ, new V9FMOVrDecoder(FMOVRdZ, "fmovrdz", RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVRdLEZ, new V9FMOVrDecoder(FMOVRdLEZ, "fmovrdlez", RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVRdLZ, new V9FMOVrDecoder(FMOVRdLZ, "fmovrdlz", RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVRdNZ, new V9FMOVrDecoder(FMOVRdNZ, "fmovrdnz", RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVRdGZ, new V9FMOVrDecoder(FMOVRdGZ, "fmovrdgz", RTLDT_FL_DOUBLE)); + addV9FPop2Decoder(FMOVRdGEZ, new V9FMOVrDecoder(FMOVRdGEZ, "fmovrdgez", RTLDT_FL_DOUBLE)); + + addV9FPop2Decoder(FMOVRqZ, new V9FMOVrDecoder(FMOVRqZ, "fmovrqz", RTLDT_FL_QUAD)); + addV9FPop2Decoder(FMOVRqLEZ, new V9FMOVrDecoder(FMOVRqLEZ, "fmovrqlez", RTLDT_FL_QUAD)); + addV9FPop2Decoder(FMOVRqLZ, new V9FMOVrDecoder(FMOVRqLZ, "fmovrqlz", RTLDT_FL_QUAD)); + addV9FPop2Decoder(FMOVRqNZ, new V9FMOVrDecoder(FMOVRqNZ, "fmovrqnz", RTLDT_FL_QUAD)); + addV9FPop2Decoder(FMOVRqGZ, new V9FMOVrDecoder(FMOVRqGZ, "fmovrqgz", RTLDT_FL_QUAD)); + addV9FPop2Decoder(FMOVRqGEZ, new V9FMOVrDecoder(FMOVRqGEZ, "fmovrqgez", RTLDT_FL_QUAD)); + } + + InstructionDecoder getOpfDecoder(int opf) { + InstructionDecoder decoder = (InstructionDecoder) V8FPop2Decoder.fpop2Decoders.get(new Integer(opf)); + return (decoder != null) ? decoder : (InstructionDecoder) v9fpop2Decoders.get(new Integer(opf)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FloatBranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FloatBranchDecoder.java new file mode 100644 index 00000000000..fb7f9a69c61 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FloatBranchDecoder.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +class V9FloatBranchDecoder extends V9CCBranchDecoder { + String getConditionName(int conditionCode, boolean isAnnuled) { + return isAnnuled ? floatAnnuledConditionNames[conditionCode] + : floatConditionNames[conditionCode]; + } + + int getConditionFlag(int instruction) { + return (FBPfcc_CC_MASK & instruction) >>> FBPfcc_CC_START_BIT; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FlushwDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FlushwDecoder.java new file mode 100644 index 00000000000..4462f589fd8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9FlushwDecoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9FlushwDecoder extends InstructionDecoder + implements V9InstructionDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + Instruction instr = null; + // "i" bit has to be zero. see page 169 - A.21 Flush Register Windows. + if (isIBitSet(instruction)) { + instr = v9factory.newIllegalInstruction(instruction); + } else { + instr = v9factory.newV9FlushwInstruction(); + } + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9InstructionDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9InstructionDecoder.java new file mode 100644 index 00000000000..03dd716163f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9InstructionDecoder.java @@ -0,0 +1,28 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +interface V9InstructionDecoder extends /* imports */ SPARCV9Opcodes { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9IntRegisterBranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9IntRegisterBranchDecoder.java new file mode 100644 index 00000000000..3dc89139bdc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9IntRegisterBranchDecoder.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9IntRegisterBranchDecoder extends V9RegisterBranchDecoder { + static final String integerRegisterConditionNames[] = { + null, "brz", "brlez", "brlz", null, "brnz", "brgz", "brgez" + }; + + String getRegisterConditionName(int index) { + return integerRegisterConditionNames[index]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9IntegerBranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9IntegerBranchDecoder.java new file mode 100644 index 00000000000..e7edfda3b9b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9IntegerBranchDecoder.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +class V9IntegerBranchDecoder extends V9CCBranchDecoder { + String getConditionName(int conditionCode, boolean isAnnuled) { + return isAnnuled ? integerAnnuledConditionNames[conditionCode] + : integerConditionNames[conditionCode]; + } + + int getConditionFlag(int instruction) { + return ((BPcc_CC_MASK & instruction) >>> BPcc_CC_START_BIT) + icc; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9MOVccDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9MOVccDecoder.java new file mode 100644 index 00000000000..384b0de733a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9MOVccDecoder.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9MOVccDecoder extends V9CMoveDecoder { + private static String getMoveCCName(int conditionCode, int conditionFlag) { + return "mov" + getConditionName(conditionCode, conditionFlag); + } + + private static int getMoveConditionFlag(int instruction) { + boolean cc2Bit = (instruction & CMOVE_CC2_MASK) != 0; + int conditionFlag = (instruction & CMOVE_CC0_CC1_MASK) >>> CMOVE_CC_START_BIT; + if (cc2Bit) conditionFlag |= (0x4); // 100; + return conditionFlag; + } + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + Instruction instr = null; + int conditionFlag = getMoveConditionFlag(instruction); + if (conditionFlag == CFLAG_RESERVED1 || conditionFlag == CFLAG_RESERVED2) { + instr = v9factory.newIllegalInstruction(instruction); + } else { + int rdNum = getDestinationRegister(instruction); + SPARCRegister rd = SPARCRegisters.getRegister(rdNum); + int conditionCode = getMoveConditionCode(instruction); + ImmediateOrRegister source = getCMoveSource(instruction, 11); + String name = getMoveCCName(conditionCode, conditionFlag); + instr = v9factory.newV9MOVccInstruction(name, conditionCode, conditionFlag, source, rd); + } + + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9MOVrDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9MOVrDecoder.java new file mode 100644 index 00000000000..8cff284df72 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9MOVrDecoder.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9MOVrDecoder extends V9CMoveDecoder { + private static final String regConditionNames[] = { + null, "movrz", "movrlez", "movrlz", null, "movrnz", "movrgz", "movrgez" + }; + + private static String getMOVrName(int conditionCode) { + return regConditionNames[conditionCode]; + } + + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + Instruction instr = null; + int regConditionCode = getRegisterConditionCode(instruction); + String name = getMOVrName(regConditionCode); + if (name == null) { + instr = v9factory.newIllegalInstruction(instruction); + } else { + int rdNum = getDestinationRegister(instruction); + SPARCRegister rd = SPARCRegisters.getRegister(rdNum); + SPARCRegister rs1 = SPARCRegisters.getRegister(getSourceRegister1(instruction)); + ImmediateOrRegister operand2 = getCMoveSource(instruction, 10); + instr = v9factory.newV9MOVrInstruction(name, rs1, operand2, rd, regConditionCode); + } + + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9PopcDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9PopcDecoder.java new file mode 100644 index 00000000000..23703599209 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9PopcDecoder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9PopcDecoder extends InstructionDecoder + implements V9InstructionDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + int rs1Num = getSourceRegister1(instruction); + Instruction instr = null; + // in POPC, rs1 should be zero. see page 205 - A.41 Population Count + if (rs1Num != 0) { + instr = v9factory.newIllegalInstruction(instruction); + } else { + SPARCRegister rd = SPARCRegisters.getRegister(getDestinationRegister(instruction)); + instr = v9factory.newV9PopcInstruction(getOperand2(instruction), rd); + } + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9PrefetchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9PrefetchDecoder.java new file mode 100644 index 00000000000..e48c3d419e1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9PrefetchDecoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9PrefetchDecoder extends MemoryInstructionDecoder + implements V9InstructionDecoder { + V9PrefetchDecoder() { + // Fake the destination with an integer type so we can get fcn from rd + super(PREFETCH, "prefetch", RTLDT_SIGNED_WORD); + } + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + return v9factory.newV9PrefetchInstruction(name, addr, rd.getNumber()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9PrivilegedReadWriteDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9PrivilegedReadWriteDecoder.java new file mode 100644 index 00000000000..2be04466220 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9PrivilegedReadWriteDecoder.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class V9PrivilegedReadWriteDecoder extends InstructionDecoder + implements V9InstructionDecoder { + static boolean isLegalPrivilegedRegister(int reg) { + return (reg > -1 && reg < 16) || reg == 31; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9RdprDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9RdprDecoder.java new file mode 100644 index 00000000000..367d1b94372 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9RdprDecoder.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9RdprDecoder extends V9PrivilegedReadWriteDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + Instruction instr = null; + int prNum = getSourceRegister1(instruction); + if (isLegalPrivilegedRegister(prNum)) { + SPARCRegister rd = SPARCRegisters.getRegister(getDestinationRegister(instruction)); + instr = v9factory.newV9RdprInstruction(prNum, rd); + } else { + instr = v9factory.newIllegalInstruction(instruction); + } + + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9ReadDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9ReadDecoder.java new file mode 100644 index 00000000000..26dccc0a861 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9ReadDecoder.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9ReadDecoder extends InstructionDecoder + implements V9InstructionDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + Instruction instr = null; + int specialRegNum = getSourceRegister1(instruction); + + // rs1 values 1, 7-14 are reserved - see page 214, A.44 Read State Register. + if (specialRegNum == 1 || (specialRegNum > 6 && specialRegNum < 15)) { + instr = v9factory.newIllegalInstruction(instruction); + } else { + int rdNum = getDestinationRegister(instruction); + if (specialRegNum == 15) { + // may be stbar, member or illegal + if (rdNum == 0) { + boolean iBit = isIBitSet(instruction); + if (iBit) { + instr = v9factory.newV9MembarInstruction((instruction & MMASK_MASK) >>> MMASK_START_BIT, + (instruction & CMASK_MASK) >>> CMASK_START_BIT); + } else { + instr = v9factory.newStbarInstruction(); + } + } else { // rd != 0 && rs1 == 15 + instr = v9factory.newIllegalInstruction(instruction); + } + } else { + int asrRegNum = -1; + if (specialRegNum > 15){ + asrRegNum = specialRegNum; + specialRegNum = SPARCV9SpecialRegisters.ASR; + } + SPARCRegister rd = SPARCRegisters.getRegister(rdNum); + instr = v9factory.newV9ReadInstruction(specialRegNum, asrRegNum, rd); + } + } + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9RegisterBranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9RegisterBranchDecoder.java new file mode 100644 index 00000000000..fc161c55017 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9RegisterBranchDecoder.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +abstract class V9RegisterBranchDecoder extends V9BranchDecoder { + static int getDisp16(int instruction) { + int offset = (DISP_16_LO_MASK & instruction) | + ((DISP_16_HI_MASK & instruction) >>> (DISP_16_HI_START_BIT - DISP_16_LO_NUMBITS)); + + // sign extend and word align + offset = extractSignedIntFromNBits(offset, 16); + offset <<= 2; + + return offset; + } + + String getConditionName(int conditionCode, boolean isAnnuled) { + return null; + } + + abstract String getRegisterConditionName(int rcond); + + public Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + int rcond = (BRANCH_RCOND_MASK & instruction) >>> BRANCH_RCOND_START_BIT; + if (rcond == BRANCH_RCOND_RESERVED1 || rcond == BRANCH_RCOND_RESERVED2) + return factory.newIllegalInstruction(instruction); + + SPARCRegister rs1 = SPARCRegisters.getRegister(getSourceRegister1(instruction)); + boolean predictTaken = getPredictTaken(instruction); + boolean annuled = getAnnuledBit(instruction); + PCRelativeAddress addr = new PCRelativeAddress(getDisp16(instruction)); + String name = getRegisterConditionName(rcond); + return v9factory.newV9RegisterBranchInstruction(name, addr, annuled, rcond, rs1, predictTaken); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9SavedRestoredDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9SavedRestoredDecoder.java new file mode 100644 index 00000000000..6c49f890248 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9SavedRestoredDecoder.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9SavedRestoredDecoder extends InstructionDecoder + implements V9InstructionDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + Instruction instr = null; + int rdNum = getDestinationRegister(instruction); + // "rd" field is "fcn". Only values 0 and 1 are defined. + // see page 219 - A.47 Saved and Restored + switch (rdNum) { + case 0: + instr = v9factory.newV9SavedInstruction(); + break; + case 1: + instr = v9factory.newV9RestoredInstruction(); + break; + default: + instr = v9factory.newIllegalInstruction(instruction); + break; + } + + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9ShiftDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9ShiftDecoder.java new file mode 100644 index 00000000000..65477d6bbae --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9ShiftDecoder.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9ShiftDecoder extends InstructionDecoder + implements V9InstructionDecoder { + final int op3; + final String name; + final int rtlOperation; + + V9ShiftDecoder(int op3, String name, int rtlOperation) { + this.op3 = op3; + this.name = name; + this.rtlOperation = rtlOperation; + } + + static boolean isXBitSet(int instruction) { + return (instruction & X_MASK) != 0; + } + + Instruction decode(int instruction, + SPARCInstructionFactory factory) { + SPARCRegister rs1 = SPARCRegisters.getRegister(getSourceRegister1(instruction)); + SPARCRegister rd = SPARCRegisters.getRegister(getDestinationRegister(instruction)); + boolean xBit = isXBitSet(instruction); + ImmediateOrRegister operand2 = null; + + if (isIBitSet(instruction)) { + // look for 64 bits shift operations. + int value = instruction & ( xBit ? SHIFT_COUNT_6_MASK : SHIFT_COUNT_5_MASK); + operand2 = new Immediate(new Short((short) value)); + } else { + operand2 = SPARCRegisters.getRegister(getSourceRegister2(instruction)); + } + + return factory.newShiftInstruction(xBit? name + "x" : name, op3, rtlOperation, rs1, operand2, rd); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9SpecialLoadDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9SpecialLoadDecoder.java new file mode 100644 index 00000000000..31cf7992be9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9SpecialLoadDecoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9SpecialLoadDecoder extends MemoryInstructionDecoder + implements V9InstructionDecoder { + V9SpecialLoadDecoder(int op3) { + super(op3, "ld[x]fsr", RTLDT_UNKNOWN); + } + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, SPARCInstructionFactory factory) { + return factory.newSpecialLoadInstruction(rd == SPARCRegisters.G0? "ld" : "ldx", + SPARCSpecialRegisters.FSR, -1, + addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9SpecialStoreDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9SpecialStoreDecoder.java new file mode 100644 index 00000000000..968f7a22634 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9SpecialStoreDecoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9SpecialStoreDecoder extends MemoryInstructionDecoder + implements V9InstructionDecoder { + V9SpecialStoreDecoder(int op3) { + super(op3, "st[x]fsr", RTLDT_UNKNOWN); + } + + Instruction decodeMemoryInstruction(int instruction, + SPARCRegisterIndirectAddress addr, + SPARCRegister rd, SPARCInstructionFactory factory) { + return factory.newSpecialStoreInstruction(rd == SPARCRegisters.G0? "st" : "stx", + SPARCSpecialRegisters.FSR, -1, + addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9WriteDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9WriteDecoder.java new file mode 100644 index 00000000000..f25e092714d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9WriteDecoder.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9WriteDecoder extends InstructionDecoder + implements V9InstructionDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + Instruction instr = null; + int specialRegNum = getDestinationRegister(instruction); + + // rd values 1, 4,5 7-14 are reserved - see page 244, A.63 Write State Register. + if (specialRegNum == 1 || specialRegNum == 4 || specialRegNum == 5 + || (specialRegNum > 6 && specialRegNum < 15)) { + instr = v9factory.newIllegalInstruction(instruction); + } else { + int rs1Num = getSourceRegister1(instruction); + if (specialRegNum == 15) { + if (isIBitSet(instruction) && rs1Num == 0) { + instr = v9factory.newV9SirInstruction(); + } else { + instr = v9factory.newIllegalInstruction(instruction); + } + } else { + int asrRegNum = -1; + if (specialRegNum > 15) { + asrRegNum = specialRegNum; + specialRegNum = SPARCV9SpecialRegisters.ASR; + } + SPARCRegister rs1 = SPARCRegisters.getRegister(rs1Num); + instr = v9factory.newV9WriteInstruction(specialRegNum, asrRegNum, rs1, getOperand2(instruction)); + } + } + + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9WrprDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9WrprDecoder.java new file mode 100644 index 00000000000..3d06bc80372 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/V9WrprDecoder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class V9WrprDecoder extends V9PrivilegedReadWriteDecoder { + Instruction decode(int instruction, SPARCInstructionFactory factory) { + SPARCV9InstructionFactory v9factory = (SPARCV9InstructionFactory) factory; + Instruction instr = null; + int prNum = getDestinationRegister(instruction); + if (isLegalPrivilegedRegister(prNum)) { + SPARCRegister rs1 = SPARCRegisters.getRegister(getSourceRegister1(instruction)); + ImmediateOrRegister operand2 = getOperand2(instruction); + instr = v9factory.newV9WrprInstruction(rs1, operand2, prNum); + } else { + instr = v9factory.newIllegalInstruction(instruction); + } + + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/WriteDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/WriteDecoder.java new file mode 100644 index 00000000000..77ab3dc2663 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/sparc/WriteDecoder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.sparc; + +import sun.jvm.hotspot.asm.*; + +class WriteDecoder extends ReadWriteDecoder { + WriteDecoder(int specialRegNum) { + super(specialRegNum); + } + + Instruction decodeReadWrite(int instruction, SPARCInstructionFactory factory, + int rs1Num, int rdNum) { + Instruction instr = null; + int specialReg = specialRegNum; + if (rdNum == 0) + specialReg = SPARCSpecialRegisters.Y; + return factory.newWriteInstruction(specialReg, rdNum, + SPARCRegisters.getRegister(rs1Num), + getOperand2(instruction)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/ArithmeticDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/ArithmeticDecoder.java new file mode 100644 index 00000000000..3f0d878d894 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/ArithmeticDecoder.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; +import sun.jvm.hotspot.asm.*; + +public class ArithmeticDecoder extends InstructionDecoder { + private int rtlOperation; + + public ArithmeticDecoder(String name, int addrMode1, int operandType1, int rtlOperation) { + super(name, addrMode1, operandType1); + this.rtlOperation = rtlOperation; + } + public ArithmeticDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2, int rtlOperation) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + this.rtlOperation = rtlOperation; + } + public ArithmeticDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2, int addrMode3, int operandType3, int rtlOperation) { + super(name, addrMode1, operandType1, addrMode2, operandType2, addrMode3, operandType3); + this.rtlOperation = rtlOperation; + } + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + Operand op3 = getOperand3(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newArithmeticInstruction(name, rtlOperation, op1, op2, op3, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/BranchDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/BranchDecoder.java new file mode 100644 index 00000000000..8787b40e470 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/BranchDecoder.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.*; + +public class BranchDecoder extends InstructionDecoder { + + public BranchDecoder(String name) { + super(name); + } + public BranchDecoder(String name, int addrMode1, int operandType1) { + super(name, addrMode1, operandType1); + } + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand addr = getOperand1(bytesArray, operandSize, addrSize); + if (Assert.ASSERTS_ENABLED) { + Assert.that(addr == null || addr instanceof X86PCRelativeAddress, "Address should be PC Relative!"); + } + int size = byteIndex - instrStartIndex; + return factory.newBranchInstruction(name, (X86PCRelativeAddress)addr, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/CallDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/CallDecoder.java new file mode 100644 index 00000000000..7f3296a06a0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/CallDecoder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class CallDecoder extends InstructionDecoder { + public CallDecoder(String name, int addrMode1, int operandType1) { + super(name, addrMode1, operandType1); + } + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand operand = getOperand1(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + Address address; + if (operand instanceof X86Register) { + address = new X86RegisterDirectAddress((X86Register)operand); + } else { + address = (Address) operand; + } + return factory.newCallInstruction(name, address, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/ConditionalJmpDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/ConditionalJmpDecoder.java new file mode 100644 index 00000000000..d94c82c4ffe --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/ConditionalJmpDecoder.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.utilities.*; + +public class ConditionalJmpDecoder extends InstructionDecoder { + + public ConditionalJmpDecoder(String name, int addrMode1, int operandType1) { + super(name, addrMode1, operandType1); + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand addr = getOperand1(bytesArray, operandSize, addrSize); + if (Assert.ASSERTS_ENABLED) { + Assert.that(addr instanceof X86PCRelativeAddress, "Address should be PC Relative!"); + } + return factory.newCondJmpInstruction(name, (X86PCRelativeAddress)addr, byteIndex-instrStartIndex, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPArithmeticDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPArithmeticDecoder.java new file mode 100644 index 00000000000..9336984fdb6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPArithmeticDecoder.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class FPArithmeticDecoder extends FPInstructionDecoder { + private int rtlOperation; + + public FPArithmeticDecoder(String name, int addrMode1, int operandType1, int rtlOperation) { + super(name, addrMode1, operandType1); + this.rtlOperation = rtlOperation; + } + public FPArithmeticDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2, int rtlOperation) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + this.rtlOperation = rtlOperation; + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newFPArithmeticInstruction(name, rtlOperation, op1, op2, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPInstructionDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPInstructionDecoder.java new file mode 100644 index 00000000000..701196c7b92 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPInstructionDecoder.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +// basic float instruction decoder class +public class FPInstructionDecoder extends InstructionDecoder { + + public FPInstructionDecoder(String name) { + super(name); + } + public FPInstructionDecoder(String name, int addrMode1, int operandType1) { + super(name, addrMode1, operandType1); + } + public FPInstructionDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return new X86FPInstruction(name, op1, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPLoadDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPLoadDecoder.java new file mode 100644 index 00000000000..8502ff50bb9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPLoadDecoder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +class FPLoadDecoder extends FPInstructionDecoder { + FPLoadDecoder(String name, int addrMode1, int operandType1) { + super(name, addrMode1, operandType1); + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op = getOperand1(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newFPLoadInstruction(name, op, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPStoreDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPStoreDecoder.java new file mode 100644 index 00000000000..17667905c3f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FPStoreDecoder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +class FPStoreDecoder extends FPInstructionDecoder { + FPStoreDecoder(String name, int addrMode1, int operandType1) { + super(name, addrMode1, operandType1); + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op = getOperand1(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newFPStoreInstruction(name, op, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FloatDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FloatDecoder.java new file mode 100644 index 00000000000..0622de2e5c8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FloatDecoder.java @@ -0,0 +1,253 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class FloatDecoder extends FPInstructionDecoder { + + public FloatDecoder() { + super(null); + } + + //Please refer to IA-32 Intel Architecture Software Developer's Manual Volume 2 + //APPENDIX A - Escape opcodes + + /*When ModR/M byte is within 00h to BFh*/ + private static final FPInstructionDecoder floatMapOne[][] = { + /* d8 */ + { + new FPArithmeticDecoder("fadds", ADDR_E, v_mode, RTLOP_ADD), + new FPArithmeticDecoder("fmuls", ADDR_E, v_mode, RTLOP_SMUL), + new FPInstructionDecoder("fcoms", ADDR_E, v_mode), + new FPInstructionDecoder("fcomps", ADDR_E, v_mode), + new FPArithmeticDecoder("fsubs", ADDR_E, v_mode, RTLOP_SUB), + new FPArithmeticDecoder("fsubrs", ADDR_E, v_mode, RTLOP_SUB), + new FPArithmeticDecoder("fdivs", ADDR_E, v_mode, RTLOP_SDIV), + new FPArithmeticDecoder("fdivrs", ADDR_E, v_mode, RTLOP_SDIV) + }, + /* d9 */ + { + new FPLoadDecoder("flds", ADDR_E, v_mode), + null, + new FPStoreDecoder("fsts", ADDR_E, v_mode), + new FPStoreDecoder("fstps", ADDR_E, v_mode), + new FPStoreDecoder("fldenv", ADDR_E, v_mode), + new FPStoreDecoder("fldcw", ADDR_E, v_mode), + new FPStoreDecoder("fNstenv", ADDR_E, v_mode), + new FPStoreDecoder("fNstcw", ADDR_E, v_mode) + }, + /* da */ + { + new FPArithmeticDecoder("fiaddl", ADDR_E, v_mode, RTLOP_ADD), + new FPArithmeticDecoder("fimull", ADDR_E, v_mode, RTLOP_SMUL), + new FPInstructionDecoder("ficoml", ADDR_E, v_mode), + new FPInstructionDecoder("ficompl", ADDR_E, v_mode), + new FPArithmeticDecoder("fisubl", ADDR_E, v_mode, RTLOP_SUB), + new FPArithmeticDecoder("fisubrl", ADDR_E, v_mode, RTLOP_SUB), + new FPArithmeticDecoder("fidivl", ADDR_E, v_mode, RTLOP_SDIV), + new FPArithmeticDecoder("fidivrl", ADDR_E, v_mode, RTLOP_SDIV) + }, + /* db */ + { + new FPLoadDecoder("fildl", ADDR_E, v_mode), + null, + new FPStoreDecoder("fistl", ADDR_E, v_mode), + new FPStoreDecoder("fistpl", ADDR_E, v_mode), + null, + new FPLoadDecoder("fldt", ADDR_E, v_mode), + null, + new FPStoreDecoder("fstpt", ADDR_E, v_mode) + }, + /* dc */ + { + new FPArithmeticDecoder("faddl", ADDR_E, v_mode, RTLOP_ADD), + new FPArithmeticDecoder("fmull", ADDR_E, v_mode, RTLOP_SMUL), + new FPInstructionDecoder("fcoml", ADDR_E, v_mode), + new FPInstructionDecoder("fcompl", ADDR_E, v_mode), + new FPArithmeticDecoder("fsubl", ADDR_E, v_mode, RTLOP_SUB), + new FPArithmeticDecoder("fsubrl", ADDR_E, v_mode, RTLOP_SUB), + new FPArithmeticDecoder("fdivl", ADDR_E, v_mode, RTLOP_SDIV), + new FPArithmeticDecoder("fdivrl", ADDR_E, v_mode, RTLOP_SDIV) + }, + /* dd */ + { + new FPLoadDecoder("fldl", ADDR_E, v_mode), + null, + new FPStoreDecoder("fstl", ADDR_E, v_mode), + new FPStoreDecoder("fstpl", ADDR_E, v_mode), + new FPStoreDecoder("frstor", ADDR_E, v_mode), + null, + new FPStoreDecoder("fNsave", ADDR_E, v_mode), + new FPStoreDecoder("fNstsw", ADDR_E, v_mode) + }, + /* de */ + { + new FPArithmeticDecoder("fiadd", ADDR_E, v_mode, RTLOP_ADD), + new FPArithmeticDecoder("fimul", ADDR_E, v_mode, RTLOP_SMUL), + new FPInstructionDecoder("ficom", ADDR_E, v_mode), + new FPInstructionDecoder("ficomp", ADDR_E, v_mode), + new FPArithmeticDecoder("fisub", ADDR_E, v_mode, RTLOP_SUB), + new FPArithmeticDecoder("fisubr", ADDR_E, v_mode, RTLOP_SUB), + new FPArithmeticDecoder("fidiv", ADDR_E, v_mode, RTLOP_SDIV), + new FPArithmeticDecoder("fidivr", ADDR_E, v_mode, RTLOP_SDIV) + }, + /* df */ + { + new FPLoadDecoder("fild", ADDR_E, v_mode), + null, + new FPStoreDecoder("fist", ADDR_E, v_mode), + new FPStoreDecoder("fistp", ADDR_E, v_mode), + new FPLoadDecoder("fbld", ADDR_E, v_mode), + new FPLoadDecoder("fildll", ADDR_E, v_mode), + new FPStoreDecoder("fbstp", ADDR_E, v_mode), + new FPStoreDecoder("fistpll", ADDR_E, v_mode) + } + }; + + /*When ModR/M byte is outside 00h to BFh*/ + private static final FPInstructionDecoder floatMapTwo[][] = { + + /* d8 */ + /*parameter for ADDR_FPREG, 0 means ST(0), 1 means ST at rm value. */ + { + new FPArithmeticDecoder("fadd", ADDR_FPREG, 0, ADDR_FPREG, 1, RTLOP_ADD), + new FPArithmeticDecoder("fmul", ADDR_FPREG, 0, ADDR_FPREG, 1, RTLOP_SMUL), + new FPInstructionDecoder("fcom", ADDR_FPREG, 1), + new FPInstructionDecoder("fcomp", ADDR_FPREG, 1), + new FPArithmeticDecoder("fsub", ADDR_FPREG, 0, ADDR_FPREG, 1, RTLOP_SUB), + new FPArithmeticDecoder("fsubr", ADDR_FPREG, 0, ADDR_FPREG, 1, RTLOP_SUB), + new FPArithmeticDecoder("fdiv", ADDR_FPREG, 0, ADDR_FPREG, 1, RTLOP_SDIV), + new FPArithmeticDecoder("fdivr", ADDR_FPREG, 0, ADDR_FPREG, 1, RTLOP_SDIV) + }, + /* d9 */ + { + new FPLoadDecoder("fld", ADDR_FPREG, 1), + new FPInstructionDecoder("fxch", ADDR_FPREG, 1), + new FloatGRPDecoder(null, 0), + null, + new FloatGRPDecoder(null, 1), + new FloatGRPDecoder(null, 2), + new FloatGRPDecoder(null, 3), + new FloatGRPDecoder(null, 4) + }, + /* da */ + { + null, + null, + null, + null, + null, + new FloatGRPDecoder(null, 5), + null, + null + }, + /* db */ + { + null, + null, + null, + null, + new FloatGRPDecoder(null, 6), + null, + null, + null + }, + /* dc */ + { + new FPArithmeticDecoder("fadd", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_ADD), + new FPArithmeticDecoder("fmul", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_SMUL), + null, + null, + new FPArithmeticDecoder("fsub", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_SUB), + new FPArithmeticDecoder("fsubr",ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_SUB), + new FPArithmeticDecoder("fdiv", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_SDIV), + new FPArithmeticDecoder("fdivr", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_SDIV) + }, + /* dd */ + { + new FPInstructionDecoder("ffree", ADDR_FPREG, 1), + null, + new FPStoreDecoder("fst", ADDR_FPREG, 1), + new FPStoreDecoder("fstp", ADDR_FPREG, 1), + new FPInstructionDecoder("fucom", ADDR_FPREG, 1), + new FPInstructionDecoder("fucomp", ADDR_FPREG, 1), + null, + null + }, + /* de */ + { + new FPArithmeticDecoder("faddp", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_ADD), + new FPArithmeticDecoder("fmulp", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_SMUL), + null, + new FloatGRPDecoder(null, 7), + new FPArithmeticDecoder("fsubrp", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_SUB), + new FPArithmeticDecoder("fsubp", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_SUB), + new FPArithmeticDecoder("fdivrp", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_SDIV), + new FPArithmeticDecoder("fdivp", ADDR_FPREG, 1, ADDR_FPREG, 0, RTLOP_SDIV) + }, + /* df */ + { + null, + null, + null, + null, + new FloatGRPDecoder(null, 7), + null, + null, + null + } + }; + + public Instruction decode(byte[] bytesArray, int index, int instrStartIndex, int segmentOverride, int prefixes, X86InstructionFactory factory) { + this.byteIndex = index; + this.instrStartIndex = instrStartIndex; + this.prefixes = prefixes; + + int ModRM = readByte(bytesArray, byteIndex); + int reg = (ModRM >> 3) & 7; + int regOrOpcode = (ModRM >> 3) & 7; + int rm = ModRM & 7; + + int floatOpcode = InstructionDecoder.readByte(bytesArray, instrStartIndex); + FPInstructionDecoder instrDecoder = null; + + if(ModRM < 0xbf) { + instrDecoder = floatMapOne[floatOpcode - 0xd8][reg]; + } + else { + instrDecoder = floatMapTwo[floatOpcode - 0xd8][reg]; + } + + Instruction instr = null; + if(instrDecoder != null) { + instr = instrDecoder.decode(bytesArray, byteIndex, instrStartIndex, segmentOverride, prefixes, factory); + byteIndex = instrDecoder.getCurrentIndex(); + } else { + instr = factory.newIllegalInstruction(); + } + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FloatGRPDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FloatGRPDecoder.java new file mode 100644 index 00000000000..27af37e0534 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/FloatGRPDecoder.java @@ -0,0 +1,163 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class FloatGRPDecoder extends FPInstructionDecoder { + + final private int number; + + //Please refer to IA-32 Intel Architecture Software Developer's Manual Volume 2 + //APPENDIX A - Escape opcodes + + private static final FPInstructionDecoder floatGRPMap[][] = { + /* d9_2 */ + { + new FPInstructionDecoder("fnop"), + null, + null, + null, + null, + null, + null, + null + }, + /* d9_4 */ + { + new FPInstructionDecoder("fchs"), + new FPInstructionDecoder("fabs"), + null, + null, + new FPInstructionDecoder("ftst"), + new FPInstructionDecoder("fxam"), + null, + null + }, + /* d9_5 */ + { + new FPInstructionDecoder("fld1"), + new FPInstructionDecoder("fldl2t"), + new FPInstructionDecoder("fldl2e"), + new FPInstructionDecoder("fldpi"), + new FPInstructionDecoder("fldlg2"), + new FPInstructionDecoder("fldln2"), + new FPInstructionDecoder("fldz"), + null + }, + /* d9_6 */ + { + new FPInstructionDecoder("f2xm1"), + new FPInstructionDecoder("fyl2x"), + new FPInstructionDecoder("fptan"), + new FPInstructionDecoder("fpatan"), + new FPInstructionDecoder("fxtract"), + new FPInstructionDecoder("fprem1"), + new FPInstructionDecoder("fdecstp"), + new FPInstructionDecoder("fincstp") + }, + /* d9_7 */ + { + new FPInstructionDecoder("fprem"), + new FPInstructionDecoder("fyl2xp1"), + new FPInstructionDecoder("fsqrt"), + new FPInstructionDecoder("fsincos"), + new FPInstructionDecoder("frndint"), + new FPInstructionDecoder("fscale"), + new FPInstructionDecoder("fsin"), + new FPInstructionDecoder("fcos") + }, + /* da_5 */ + { + null, + new FPInstructionDecoder("fucompp"), + null, + null, + null, + null, + null, + null + }, + /* db_4 */ + { + new FPInstructionDecoder("feni(287 only)"), + new FPInstructionDecoder("fdisi(287 only)"), + new FPInstructionDecoder("fNclex"), + new FPInstructionDecoder("fNinit"), + new FPInstructionDecoder("fNsetpm(287 only)"), + null, + null, + null + }, + /* de_3 */ + { + null, + new FPInstructionDecoder("fcompp"), + null, + null, + null, + null, + null, + null + }, + /* df_4 */ + { + new FPInstructionDecoder("fNstsw"), + null, + null, + null, + null, + null, + null, + null + } + }; + + public FloatGRPDecoder(String name, int number) { + super(name); + this.number = number; + } + + public Instruction decode(byte[] bytesArray, int index, int instrStartIndex, int segmentOverride, int prefixes, X86InstructionFactory factory) { + this.byteIndex = index; + this.instrStartIndex = instrStartIndex; + this.prefixes = prefixes; + + int ModRM = readByte(bytesArray, byteIndex); + int rm = ModRM & 7; + + FPInstructionDecoder instrDecoder = null; + instrDecoder = floatGRPMap[number][rm]; + + Instruction instr = null; + if(instrDecoder != null) { + instr = instrDecoder.decode(bytesArray, byteIndex, instrStartIndex, segmentOverride, prefixes, factory); + byteIndex = instrDecoder.getCurrentIndex(); + } else { + instr = factory.newIllegalInstruction(); + } + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/GRPDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/GRPDecoder.java new file mode 100644 index 00000000000..14c464d24c3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/GRPDecoder.java @@ -0,0 +1,320 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class GRPDecoder extends InstructionDecoder { + + final private int number; + //Please refer to IA-32 Intel Architecture Software Developer's Manual Volume 2 + //APPENDIX A - Table A-4. Opcode Extensions for One and Two-byte Opcodes by Group Number. + private static final InstructionDecoder grpTable[][] = { + { + new ArithmeticDecoder("addb", ADDR_E, b_mode, ADDR_I, b_mode, RTLOP_ADD), + new LogicalDecoder("orb", ADDR_E, b_mode, ADDR_I, b_mode, RTLOP_OR), + new ArithmeticDecoder("adcb", ADDR_E, b_mode, ADDR_I, b_mode, RTLOP_ADDC), + new ArithmeticDecoder("sbbb", ADDR_E, b_mode, ADDR_I, b_mode, RTLOP_SUBC), + new LogicalDecoder("andb", ADDR_E, b_mode, ADDR_I, b_mode, RTLOP_AND), + new ArithmeticDecoder("subb", ADDR_E, b_mode, ADDR_I, b_mode, RTLOP_SUB), + new LogicalDecoder("xorb", ADDR_E, b_mode, ADDR_I, b_mode, RTLOP_XOR), + new InstructionDecoder("cmpb", ADDR_E, b_mode, ADDR_I, b_mode) + }, + { + new ArithmeticDecoder("addS", ADDR_E, v_mode, ADDR_I, v_mode, RTLOP_ADD), + new LogicalDecoder("orS", ADDR_E, v_mode, ADDR_I, v_mode, RTLOP_OR), + new ArithmeticDecoder("adcS", ADDR_E, v_mode, ADDR_I, v_mode, RTLOP_ADDC), + new ArithmeticDecoder("sbbS", ADDR_E, v_mode, ADDR_I, v_mode, RTLOP_SUBC), + new LogicalDecoder("andS", ADDR_E, v_mode, ADDR_I, v_mode, RTLOP_AND), + new ArithmeticDecoder("subS", ADDR_E, v_mode, ADDR_I, v_mode, RTLOP_SUB), + new LogicalDecoder("xorS", ADDR_E, v_mode, ADDR_I, v_mode, RTLOP_XOR), + new InstructionDecoder("cmpS", ADDR_E, v_mode, ADDR_I, v_mode) + }, + { + new ArithmeticDecoder("addS", ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_ADD), /*note: sIb here*/ + new LogicalDecoder("orS", ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_OR), + new ArithmeticDecoder("adcS", ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_ADDC), + new ArithmeticDecoder("sbbS", ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_SUBC), + new LogicalDecoder("andS", ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_AND), + new ArithmeticDecoder("subS", ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_SUB), + new LogicalDecoder("xorS", ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_XOR), + new InstructionDecoder("cmpS", ADDR_E, v_mode, ADDR_I, b_mode) + }, + { + new RotateDecoder("rolb", ADDR_E, b_mode, ADDR_I, b_mode), + new RotateDecoder("rorb", ADDR_E, b_mode, ADDR_I, b_mode), + new RotateDecoder("rclb", ADDR_E, b_mode, ADDR_I, b_mode), + new RotateDecoder("rcrb", ADDR_E, b_mode, ADDR_I, b_mode), + new ShiftDecoder("shlb", ADDR_E, b_mode, ADDR_I, b_mode, RTLOP_SLL), + new ShiftDecoder("shrb", ADDR_E, b_mode, ADDR_I, b_mode, RTLOP_SRL), + null, + new ShiftDecoder("sarb", ADDR_E, b_mode, ADDR_I, b_mode, RTLOP_SRA), + }, + { + new RotateDecoder("rolS", ADDR_E, v_mode, ADDR_I, b_mode), + new RotateDecoder("rorS", ADDR_E, v_mode, ADDR_I, b_mode), + new RotateDecoder("rclS", ADDR_E, v_mode, ADDR_I, b_mode), + new RotateDecoder("rcrS", ADDR_E, v_mode, ADDR_I, b_mode), + new ShiftDecoder("shlS", ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_SLL), + new ShiftDecoder("shrS", ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_SRL), + null, + new ShiftDecoder("sarS", ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_SRA) + }, + { + new RotateDecoder("rolb", ADDR_E, b_mode), + new RotateDecoder("rorb", ADDR_E, b_mode), + new RotateDecoder("rclb", ADDR_E, b_mode), + new RotateDecoder("rcrb", ADDR_E, b_mode), + new ShiftDecoder("shlb", ADDR_E, b_mode, RTLOP_SLL), + new ShiftDecoder("shrb", ADDR_E, b_mode, RTLOP_SRL), + null, + new ShiftDecoder("sarb", ADDR_E, b_mode, RTLOP_SRA) + }, + { + new RotateDecoder("rolS", ADDR_E, v_mode), + new RotateDecoder("rorS", ADDR_E, v_mode), + new RotateDecoder("rclS", ADDR_E, v_mode), + new RotateDecoder("rcrS", ADDR_E, v_mode), + new ShiftDecoder("shlS", ADDR_E, v_mode, RTLOP_SLL), + new ShiftDecoder("shrS", ADDR_E, v_mode, RTLOP_SRL), + null, + new ShiftDecoder("sarS", ADDR_E, v_mode, RTLOP_SRA) + }, + { + new RotateDecoder("rolb", ADDR_E, b_mode, ADDR_REG, CL), + new RotateDecoder("rorb", ADDR_E, b_mode, ADDR_REG, CL), + new RotateDecoder("rclb", ADDR_E, b_mode, ADDR_REG, CL), + new RotateDecoder("rcrb", ADDR_E, b_mode, ADDR_REG, CL), + new ShiftDecoder( "shlb", ADDR_E, b_mode, ADDR_REG, CL, RTLOP_SLL), + new ShiftDecoder("shrb", ADDR_E, b_mode, ADDR_REG, CL, RTLOP_SRL), + null, + new ShiftDecoder("sarb", ADDR_E, b_mode, ADDR_REG, CL, RTLOP_SRA) + }, + { + new RotateDecoder("rolS", ADDR_E, v_mode, ADDR_REG, CL), + new RotateDecoder("rorS", ADDR_E, v_mode, ADDR_REG, CL), + new RotateDecoder("rclS", ADDR_E, v_mode, ADDR_REG, CL), + new RotateDecoder("rcrS", ADDR_E, v_mode, ADDR_REG, CL), + new ShiftDecoder("shlS", ADDR_E, v_mode, ADDR_REG, CL, RTLOP_SLL), + new ShiftDecoder("shrS", ADDR_E, v_mode, ADDR_REG, CL, RTLOP_SRL), + null, + new ShiftDecoder("sarS", ADDR_E, v_mode, ADDR_REG, CL, RTLOP_SRA) + }, + { + new InstructionDecoder("testb", ADDR_E, b_mode, ADDR_I, b_mode), + null, /*new InstructionDecoder("(bad)", ADDR_E, b_mode)*/ + new LogicalDecoder("notb", ADDR_E, b_mode, RTLOP_NOT), + new InstructionDecoder("negb", ADDR_E, b_mode), + new ArithmeticDecoder("mulb", ADDR_REG, AL, ADDR_E, b_mode, RTLOP_UMUL), + new ArithmeticDecoder("imulb", ADDR_REG, AL, ADDR_E, b_mode, RTLOP_SMUL), + new ArithmeticDecoder("divb", ADDR_REG, AL, ADDR_E, b_mode, RTLOP_UDIV), + new ArithmeticDecoder("idivb", ADDR_REG, AL, ADDR_E, b_mode, RTLOP_SDIV) + }, + { + new InstructionDecoder("testS", ADDR_E, v_mode, ADDR_I, v_mode), + null, + new LogicalDecoder("notS", ADDR_E, v_mode, RTLOP_NOT), + new InstructionDecoder("negS", ADDR_E, v_mode), + new ArithmeticDecoder("mulS", ADDR_REG, EAX, ADDR_E, v_mode, RTLOP_UMUL), + new ArithmeticDecoder("imulS", ADDR_REG, EAX, ADDR_E, v_mode, RTLOP_SMUL), + new ArithmeticDecoder("divS", ADDR_REG, EAX, ADDR_E, v_mode, RTLOP_SDIV), + new ArithmeticDecoder("idivS", ADDR_REG, EAX, ADDR_E, v_mode, RTLOP_SDIV) + }, + { + new ArithmeticDecoder("incb", ADDR_E, b_mode, RTLOP_ADD), + new ArithmeticDecoder("decb", ADDR_E, b_mode, RTLOP_SUB), + null, + null, + null, + null, + null, + null + }, + { + new ArithmeticDecoder("incS", ADDR_E, v_mode, RTLOP_ADD), + new ArithmeticDecoder("decS", ADDR_E, v_mode, RTLOP_SUB), + new CallDecoder("call", ADDR_E, v_mode), + new CallDecoder("lcall", ADDR_E, p_mode), + new JmpDecoder("jmp", ADDR_E, v_mode), + new JmpDecoder("ljmp", ADDR_E, p_mode), + new InstructionDecoder("pushS", ADDR_E, v_mode), + null + }, + { + new InstructionDecoder("sldt", ADDR_E, w_mode), + new InstructionDecoder("str", ADDR_E, w_mode), + new InstructionDecoder("lldt", ADDR_E, w_mode), + new InstructionDecoder("ltr", ADDR_E, w_mode), + new InstructionDecoder("verr", ADDR_E, w_mode), + new InstructionDecoder("verw", ADDR_E, w_mode), + null, + null + }, + { + new InstructionDecoder("sgdt", ADDR_E, w_mode), + new InstructionDecoder("sidt", ADDR_E, w_mode), + new InstructionDecoder("lgdt", ADDR_E, w_mode), + new InstructionDecoder("lidt", ADDR_E, w_mode), + new InstructionDecoder("smsw", ADDR_E, w_mode), + null, + new InstructionDecoder("lmsw", ADDR_E, w_mode), + new InstructionDecoder("invlpg", ADDR_E, w_mode) + }, + { + null, + null, + null, + null, + new InstructionDecoder("btS", ADDR_E, v_mode, ADDR_I, b_mode), + new InstructionDecoder("btsS", ADDR_E, v_mode, ADDR_I, b_mode), + new InstructionDecoder("btrS", ADDR_E, v_mode, ADDR_I, b_mode), + new InstructionDecoder("btcS", ADDR_E, v_mode, ADDR_I, b_mode) + }, + /*16*/ + { + null, + new SSEInstructionDecoder("cmpxch8b", ADDR_W, q_mode), + null, + null, + null, + null, + null, + null + }, + /*17*/ + { + null, + null, + new SSEShiftDecoder("psrlw", ADDR_P, q_mode, ADDR_I, b_mode, RTLOP_SRL), + null, + new SSEShiftDecoder("psraw", ADDR_P, q_mode, ADDR_I, b_mode, RTLOP_SRA), + null, + new SSEShiftDecoder("psllw", ADDR_P, q_mode, ADDR_I, b_mode, RTLOP_SLL), + null + }, + /*18*/ + { + null, + null, + new SSEShiftDecoder("psrld", ADDR_P, q_mode, ADDR_I, b_mode, RTLOP_SRL), + null, + new SSEShiftDecoder("psrad", ADDR_P, q_mode, ADDR_I, b_mode, RTLOP_SRA), + null, + new SSEShiftDecoder("pslld", ADDR_P, q_mode, ADDR_I, b_mode, RTLOP_SLL), + null + }, + /*19*/ + { + null, + null, + new SSEShiftDecoder("psrlq", ADDR_P, q_mode, ADDR_I, b_mode, RTLOP_SRL), + null, + null, + null, + new SSEShiftDecoder("psllq", ADDR_P, q_mode, ADDR_I, b_mode, RTLOP_SLL), + null + }, + /*20 - Grp15*/ + { + new SSEInstructionDecoder("fxsave"), + new SSEInstructionDecoder("fxrstor"), + new SSEInstructionDecoder("ldmxcsr"), + new SSEInstructionDecoder("stmxcsr"), + null, + null, + null, + new SSEInstructionDecoder("clflush") + }, + /*21 - Grp16*/ + { + new SSEInstructionDecoder("prefetchnta"), + new SSEInstructionDecoder("prefetcht0"), + new SSEInstructionDecoder("prefetcht1"), + new SSEInstructionDecoder("prefetcht2"), + null, + null, + null, + null + }, + /*22 - Grp12:66*/ + { + null, + null, + new SSEShiftDecoder("psrlw", ADDR_P, dq_mode, ADDR_I, b_mode, RTLOP_SRL), + null, + new SSEShiftDecoder("psraw", ADDR_P, dq_mode, ADDR_I, b_mode, RTLOP_SRA), + null, + new SSEShiftDecoder("psllw", ADDR_P, dq_mode, ADDR_I, b_mode, RTLOP_SLL), + null + }, + /*23 - Grp13:66*/ + { + null, + null, + new SSEShiftDecoder("psrld", ADDR_W, dq_mode, ADDR_I, b_mode, RTLOP_SRL), + null, + new SSEShiftDecoder("psrad", ADDR_W, dq_mode, ADDR_I, b_mode, RTLOP_SRA), + null, + new SSEShiftDecoder("pslld", ADDR_W, dq_mode, ADDR_I, b_mode, RTLOP_SLL), + null + }, + /*24 - - Grp14:66*/ + { + null, + null, + new SSEShiftDecoder("psrlq", ADDR_W, dq_mode, ADDR_I, b_mode, RTLOP_SRL), + new SSEShiftDecoder("psrldq", ADDR_W, dq_mode, ADDR_I, b_mode, RTLOP_SRL), + null, + null, + new SSEShiftDecoder("psllq", ADDR_W, dq_mode, ADDR_I, b_mode, RTLOP_SLL), + new SSEShiftDecoder("psllq", ADDR_W, dq_mode, ADDR_I, b_mode, RTLOP_SLL) + } +}; + + public GRPDecoder(String name, int number) { + super(name); + this.number = number; + } + + public Instruction decode(byte[] bytesArray, int index, int instrStartIndex, int segmentOverride, int prefixes, X86InstructionFactory factory) { + this.byteIndex = index; + this.instrStartIndex = instrStartIndex; + this.prefixes = prefixes; + + int ModRM = readByte(bytesArray, byteIndex); + int reg = (ModRM >> 3) & 7; + + InstructionDecoder instrDecoder = grpTable[number][reg]; + Instruction instr = null; + if(instrDecoder != null) { + instr = instrDecoder.decode(bytesArray, byteIndex, instrStartIndex, segmentOverride, prefixes, factory); + byteIndex = instrDecoder.getCurrentIndex(); + } else { + instr = factory.newIllegalInstruction(); + } + + return instr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/InstructionDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/InstructionDecoder.java new file mode 100644 index 00000000000..179aec8b544 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/InstructionDecoder.java @@ -0,0 +1,554 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +// basic instruction decoder class +public class InstructionDecoder implements /* imports */ X86Opcodes , RTLDataTypes, RTLOperations { + + protected String name; + protected int addrMode1; + protected int operandType1; + protected int addrMode2; + protected int operandType2; + protected int addrMode3; + protected int operandType3; + + private int mod; + private int regOrOpcode; + private int rm; + protected int prefixes; + + protected int byteIndex; + protected int instrStartIndex; + + public InstructionDecoder(String name) { + this.name = name; + this.operandType1 = INVALID_OPERANDTYPE; + this.operandType2 = INVALID_OPERANDTYPE; + this.operandType3 = INVALID_OPERANDTYPE; + this.addrMode1 = INVALID_ADDRMODE; + this.addrMode2 = INVALID_ADDRMODE; + this.addrMode3 = INVALID_ADDRMODE; + } + public InstructionDecoder(String name, int addrMode1, int operandType1) { + this(name); + this.addrMode1 = addrMode1; + this.operandType1 = operandType1; + } + public InstructionDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2) { + this(name, addrMode1, operandType1); + this.addrMode2 = addrMode2; + this.operandType2 = operandType2; + } + public InstructionDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2, + int addrMode3, int operandType3) { + this(name, addrMode1, operandType1, addrMode2, operandType2); + this.addrMode3 = addrMode3; + this.operandType3 = operandType3; + } + // "operand1" + protected Operand getOperand1(byte[] bytesArray, boolean operandSize, boolean addrSize) { + if( (addrMode1 != INVALID_ADDRMODE) && (operandType1 != INVALID_OPERANDTYPE) ) + return getOperand(bytesArray, addrMode1, operandType1, operandSize, addrSize); + else + return null; + } + + // "operand2" + protected Operand getOperand2(byte[] bytesArray, boolean operandSize, boolean addrSize) { + if( (addrMode2 != INVALID_ADDRMODE) && (operandType2 != INVALID_OPERANDTYPE) ) + return getOperand(bytesArray, addrMode2, operandType2, operandSize, addrSize); + else + return null; + } + + // "operand3" + protected Operand getOperand3(byte[] bytesArray, boolean operandSize, boolean addrSize) { + if( (addrMode3 != INVALID_ADDRMODE) && (operandType3 != INVALID_OPERANDTYPE) ) + return getOperand(bytesArray, addrMode3, operandType3, operandSize, addrSize); + else + return null; + } + + static int readInt32(byte[] bytesArray, int index) { + int ret = 0; + ret = readByte(bytesArray, index); + ret |= readByte(bytesArray, index+1) << 8; + ret |= readByte(bytesArray, index+2) << 16; + ret |= readByte(bytesArray, index+3) << 24; + return ret; + } + static int readInt16(byte[] bytesArray, int index) { + int ret = 0; + ret = readByte(bytesArray, index); + ret |= readByte(bytesArray, index+1) << 8; + return ret; + } + static int readByte(byte[] bytesArray, int index) { + int ret = 0; + if (index < bytesArray.length) { + ret = (int)bytesArray[index]; + ret = ret & 0xff; + } + return ret; + } + private boolean isModRMPresent(int addrMode) { + if( (addrMode == ADDR_E) || (addrMode == ADDR_G) || (addrMode == ADDR_FPREG) || (addrMode == ADDR_Q) || (addrMode == ADDR_W) ) + return true; + else + return false; + } + public int getCurrentIndex() { + return byteIndex; + } + + public Instruction decode(byte[] bytesArray, int index, int instrStartIndex, int segmentOverride, int prefixes, X86InstructionFactory factory) { + this.byteIndex = index; + this.instrStartIndex = instrStartIndex; + this.prefixes = prefixes; + boolean operandSize; //operand-size prefix + boolean addrSize; //address-size prefix + if ( ( (prefixes & PREFIX_DATA) ^ segmentOverride ) == 1) + operandSize = true; + else + operandSize = false; + if ( ((prefixes & PREFIX_ADR) ^ segmentOverride) == 1) + addrSize = true; + else + addrSize = false; + this.name = getCorrectOpcodeName(name, prefixes, operandSize, addrSize); + + //Fetch the mod/reg/rm byte only if it is present. + if( isModRMPresent(addrMode1) || isModRMPresent(addrMode2) || isModRMPresent(addrMode3) ) { + + int ModRM = readByte(bytesArray, byteIndex); + byteIndex++; + mod = (ModRM >> 6) & 3; + regOrOpcode = (ModRM >> 3) & 7; + rm = ModRM & 7; + } + return decodeInstruction(bytesArray, operandSize, addrSize, factory); + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + Operand op3 = getOperand3(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newGeneralInstruction(name, op1, op2, op3, size, prefixes); + } + + // capital letters in template are macros + private String getCorrectOpcodeName(String oldName, int prefixes, boolean operandSize, boolean addrSize) { + StringBuffer newName = new StringBuffer(oldName); + int index = 0; + for(index=0; index> 6) & 3; + index = (sib >> 3) & 7; + base = sib & 7; + } + + switch (mod) { + case 0: + switch(rm) { + case 4: + if(base == 5) { + disp = readInt32(bytesArray, byteIndex); + byteIndex += 4; + if (index != 4) { + op = new X86RegisterIndirectAddress(segReg, null, X86Registers.getRegister32(index), disp, scale); + } else { + op = new X86RegisterIndirectAddress(segReg, null, null, disp, scale); + } + } + else { + if (index != 4) { + op = new X86RegisterIndirectAddress(segReg, X86Registers.getRegister32(base), X86Registers.getRegister32(index), 0, scale); + } else { + op = new X86RegisterIndirectAddress(segReg, X86Registers.getRegister32(base), null, 0, scale); + } + } + break; + case 5: + disp = readInt32(bytesArray, byteIndex); + byteIndex += 4; + //Create an Address object only with displacement + op = new X86RegisterIndirectAddress(segReg, null, null, disp); + break; + default: + base = rm; + //Create an Address object only with base + op = new X86RegisterIndirectAddress(segReg, X86Registers.getRegister32(base), null, 0); + break; + } + break; + case 1: + disp = (byte)readByte(bytesArray, byteIndex); + byteIndex++; + if (rm !=4) { + base = rm; + //Address with base and disp only + op = new X86RegisterIndirectAddress(segReg, X86Registers.getRegister32(base), null, disp); + } else { + if (index != 4) { + op = new X86RegisterIndirectAddress(segReg, X86Registers.getRegister32(base), X86Registers.getRegister32(index), disp, scale); + } else { + op = new X86RegisterIndirectAddress(segReg, X86Registers.getRegister32(base), null, disp, scale); + } + } + break; + case 2: + disp = readInt32(bytesArray, byteIndex); + byteIndex += 4; + if (rm !=4) { + base = rm; + //Address with base and disp + op = new X86RegisterIndirectAddress(segReg, X86Registers.getRegister32(base), null, disp); + } else if (index != 4) { + op = new X86RegisterIndirectAddress(segReg, X86Registers.getRegister32(base), X86Registers.getRegister32(index), disp, scale); + } else { + op = new X86RegisterIndirectAddress(segReg, X86Registers.getRegister32(base), null, disp, scale); + } + break; + } + } + break; + + case ADDR_I: + switch (operandType) { + case b_mode: + op = new Immediate(new Integer(readByte(bytesArray, byteIndex))); + byteIndex++; + break; + case w_mode: + op = new Immediate(new Integer(readInt16(bytesArray, byteIndex))); + byteIndex += 2; + break; + case v_mode: + if (operandSize == true) { //Operand size prefix is present + op = new Immediate(new Integer(readInt32(bytesArray, byteIndex))); + byteIndex += 4; + } else { + op = new Immediate(new Integer(readInt16(bytesArray, byteIndex))); + byteIndex += 2; + } + break; + default: + break; + } + break; + case ADDR_REG: //registers + switch(operandType) { + case EAX: + case ECX: + case EDX: + case EBX: + case ESP: + case EBP: + case ESI: + case EDI: + if(operandSize == true) { + op = X86Registers.getRegister32(operandType - EAX); + } + else { + op = X86Registers.getRegister16(operandType - EAX); + } + break; + case AX: + case CX: + case DX: + case BX: + case SP: + case BP: + case SI: + case DI: + op = X86Registers.getRegister16(operandType - AX); + break; + case AL: + case CL: + case DL: + case BL: + case AH: + case CH: + case DH: + case BH: + op = X86Registers.getRegister8(operandType - AL); + break; + case ES: //ES, CS, SS, DS, FS, GS + case CS: + case SS: + case DS: + case FS: + case GS: + op = X86SegmentRegisters.getSegmentRegister(operandType - ES); + break; + } + break; + case ADDR_DIR: //segment and offset + long segment = 0; + long offset = 0; + switch (operandType) { + case p_mode: + if (addrSize == true) { + offset = readInt32(bytesArray, byteIndex); + byteIndex += 4; + segment = readInt16(bytesArray, byteIndex); + byteIndex += 2; + } else { + offset = readInt16(bytesArray, byteIndex); + byteIndex += 2; + segment = readInt16(bytesArray, byteIndex); + byteIndex += 2; + } + op = new X86DirectAddress(segment, offset); //with offset + break; + case v_mode: + if (addrSize == true) { + offset = readInt32(bytesArray, byteIndex); + byteIndex += 4; + } else { + offset = readInt16(bytesArray, byteIndex); + byteIndex += 2; + } + op = new X86DirectAddress(offset); //with offset + break; + default: + break; + } + break; + case ADDR_G: + switch (operandType) { + case b_mode: + op = X86Registers.getRegister8(regOrOpcode); + break; + case w_mode: + op = X86Registers.getRegister16(regOrOpcode); + break; + case d_mode: + op = X86Registers.getRegister32(regOrOpcode); + break; + case v_mode: + if (operandSize == true) + op = X86Registers.getRegister32(regOrOpcode); + else + op = X86Registers.getRegister16(regOrOpcode); + break; + default: + break; + } + break; + case ADDR_SEG: + op = X86SegmentRegisters.getSegmentRegister(regOrOpcode); + break; + case ADDR_OFF: + int off = 0; + if (addrSize == true) { + off = readInt32(bytesArray, byteIndex); + byteIndex += 4; + } + else { + off = readInt16(bytesArray, byteIndex); + byteIndex += 2; + } + op = new X86DirectAddress((long)off); + break; + case ADDR_J: + long disp = 0; + //The effective address is Instruction pointer + relative offset + switch(operandType) { + case b_mode: + disp = (byte)readByte(bytesArray, byteIndex); + byteIndex++; + break; + case v_mode: + if (operandSize == true) { + disp = readInt32(bytesArray, byteIndex); + byteIndex += 4; + } + else { + disp = readInt16(bytesArray, byteIndex); + byteIndex += 2; + } + //disp = disp + (byteIndex-instrStartIndex); + break; + } + op = new X86PCRelativeAddress(disp); + break; + case ADDR_ESDI: + op = new X86SegmentRegisterAddress(X86SegmentRegisters.ES, X86Registers.DI); + break; + case ADDR_DSSI: + op = new X86SegmentRegisterAddress(X86SegmentRegisters.DS, X86Registers.SI); + break; + case ADDR_R: + switch (operandType) { + case b_mode: + op = X86Registers.getRegister8(mod); + break; + case w_mode: + op = X86Registers.getRegister16(mod); + break; + case d_mode: + op = X86Registers.getRegister32(mod); + break; + case v_mode: + if (operandSize == true) + op = X86Registers.getRegister32(mod); + else + op = X86Registers.getRegister16(mod); + break; + default: + break; + } + break; + case ADDR_FPREG: + switch (operandType) { + case 0: + op = X86FloatRegisters.getRegister(0); + break; + case 1: + op = X86FloatRegisters.getRegister(rm); + break; + } + break; + + //SSE: reg field of ModR/M byte selects a 128-bit XMM register + case ADDR_V: + op = X86XMMRegisters.getRegister(regOrOpcode); + break; + + //SSE: reg field of ModR/M byte selects a 64-bit MMX register + case ADDR_P: + op = X86MMXRegisters.getRegister(regOrOpcode); + break; + } + return op; + } + + private X86SegmentRegister getSegmentRegisterFromPrefix(int prefixes) { + X86SegmentRegister segRegister = null; + + if ( (prefixes & PREFIX_CS) != 0) + segRegister = X86SegmentRegisters.CS; + if ( (prefixes & PREFIX_DS) != 0) + segRegister = X86SegmentRegisters.DS; + if ( (prefixes & PREFIX_ES) != 0) + segRegister = X86SegmentRegisters.ES; + if ( (prefixes & PREFIX_FS) != 0) + segRegister = X86SegmentRegisters.FS; + if ( (prefixes & PREFIX_SS) != 0) + segRegister = X86SegmentRegisters.SS; + if ( (prefixes & PREFIX_GS) != 0) + segRegister = X86SegmentRegisters.GS; + + return segRegister; + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/JmpDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/JmpDecoder.java new file mode 100644 index 00000000000..c966645eb2a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/JmpDecoder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class JmpDecoder extends InstructionDecoder { + public JmpDecoder(String name, int addrMode1, int operandType1) { + super(name, addrMode1, operandType1); + } + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand operand = getOperand1(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + Address address; + if (operand instanceof X86Register) { + address = new X86RegisterDirectAddress((X86Register)operand); + } else { + address = (Address) operand; + } + return factory.newJmpInstruction(name, address, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/LogicalDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/LogicalDecoder.java new file mode 100644 index 00000000000..1757de0f7d0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/LogicalDecoder.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class LogicalDecoder extends InstructionDecoder { + private int rtlOperation; + + public LogicalDecoder(String name, int addrMode1, int operandType1, int rtlOperation) { + super(name, addrMode1, operandType1); + this.rtlOperation = rtlOperation; + } + public LogicalDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2, int rtlOperation) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + this.rtlOperation = rtlOperation; + } + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newLogicInstruction(name, rtlOperation, op1, op2, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/MoveDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/MoveDecoder.java new file mode 100644 index 00000000000..a41126af613 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/MoveDecoder.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class MoveDecoder extends InstructionDecoder { + public MoveDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + + if( (op1 instanceof X86Register) && (op2 instanceof ImmediateOrRegister) ) { + return factory.newMoveInstruction(name, (X86Register)op1, (ImmediateOrRegister)op2, size, prefixes); + } + else if( (op1 instanceof Address) && (op2 instanceof Immediate) ) { + return factory.newGeneralInstruction(name, op1, op2, size, prefixes); + } + else if( (op1 instanceof Address) && (op2 instanceof X86Register) ) { + return factory.newMoveStoreInstruction(name, (Address)op1, (X86Register)op2, 0, size, prefixes); + } + else if( (op1 instanceof X86Register) && (op2 instanceof Address) ) { + return factory.newMoveLoadInstruction(name, (X86Register)op1, (Address)op2, 0, size, prefixes); + } + + return null; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/RotateDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/RotateDecoder.java new file mode 100644 index 00000000000..e13f2e58881 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/RotateDecoder.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class RotateDecoder extends InstructionDecoder { + + public RotateDecoder(String name, int addrMode1, int operandType1) { + super(name, addrMode1, operandType1); + } + public RotateDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + } + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newRotateInstruction(name, op1, (ImmediateOrRegister)op2, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEArithmeticDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEArithmeticDecoder.java new file mode 100644 index 00000000000..49643e17a39 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEArithmeticDecoder.java @@ -0,0 +1,48 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class SSEArithmeticDecoder extends SSEInstructionDecoder { + private int rtlOperation; + + public SSEArithmeticDecoder(String name, int addrMode1, int operandType1, int rtlOperation) { + super(name, addrMode1, operandType1); + this.rtlOperation = rtlOperation; + } + + public SSEArithmeticDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2, int rtlOperation) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + this.rtlOperation = rtlOperation; + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newArithmeticInstruction(name, rtlOperation, op1, op2, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEInstructionDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEInstructionDecoder.java new file mode 100644 index 00000000000..da9a70ce25e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEInstructionDecoder.java @@ -0,0 +1,53 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +// SSE instructions decoder class +public class SSEInstructionDecoder extends InstructionDecoder { + + public SSEInstructionDecoder(String name) { + super(name); + } + public SSEInstructionDecoder(String name, int addrMode1, int operandType1) { + super(name, addrMode1, operandType1); + } + public SSEInstructionDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + } + + public SSEInstructionDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2, int addrMode3, int operandType3) { + super(name, addrMode1, operandType1, addrMode2, operandType2, addrMode3, operandType3); + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + Operand op3 = getOperand3(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newGeneralInstruction(name, op1, op2, op3, size, 0); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSELogicalDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSELogicalDecoder.java new file mode 100644 index 00000000000..d2a8c126e69 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSELogicalDecoder.java @@ -0,0 +1,45 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class SSELogicalDecoder extends SSEInstructionDecoder { + private int rtlOperation; + + public SSELogicalDecoder(String name, int addrMode1, int operandType1, int addrMode2, + int operandType2, int rtlOperation) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + this.rtlOperation = rtlOperation; + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, + boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newLogicInstruction(name, rtlOperation, op1, op2, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEMoveDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEMoveDecoder.java new file mode 100644 index 00000000000..16886cba61c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEMoveDecoder.java @@ -0,0 +1,55 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class SSEMoveDecoder extends SSEInstructionDecoder { + + public SSEMoveDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + + if( (op1 instanceof X86Register) && (op2 instanceof ImmediateOrRegister) ) { + return factory.newMoveInstruction(name, (X86Register)op1, (ImmediateOrRegister)op2, size, 0); + } + else if( (op1 instanceof Address) && (op2 instanceof Immediate) ) { + return factory.newGeneralInstruction(name, op1, op2, size, 0); + } + else if( (op1 instanceof Address) && (op2 instanceof X86Register) ) { + return factory.newMoveStoreInstruction(name, (Address)op1, (X86Register)op2, 0, size, 0); + } + else if( (op1 instanceof X86Register) && (op2 instanceof Address) ) { + return factory.newMoveLoadInstruction(name, (X86Register)op1, (Address)op2, 0, size, 0); + } + + return null; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEShiftDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEShiftDecoder.java new file mode 100644 index 00000000000..f573a930bf2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/SSEShiftDecoder.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class SSEShiftDecoder extends SSEInstructionDecoder { + private int rtlOperation; + + public SSEShiftDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2, int rtlOperation) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + this.rtlOperation = rtlOperation; + } + + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newShiftInstruction(name, rtlOperation, op1, (ImmediateOrRegister)op2, size, 0); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/ShiftDecoder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/ShiftDecoder.java new file mode 100644 index 00000000000..82a8885719f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/ShiftDecoder.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class ShiftDecoder extends InstructionDecoder { + private int rtlOperation; + + public ShiftDecoder(String name, int addrMode1, int operandType1, int rtlOperation) { + super(name, addrMode1, operandType1); + this.rtlOperation = rtlOperation; + } + public ShiftDecoder(String name, int addrMode1, int operandType1, int addrMode2, int operandType2, int rtlOperation) { + super(name, addrMode1, operandType1, addrMode2, operandType2); + this.rtlOperation = rtlOperation; + } + protected Instruction decodeInstruction(byte[] bytesArray, boolean operandSize, boolean addrSize, X86InstructionFactory factory) { + Operand op1 = getOperand1(bytesArray, operandSize, addrSize); + Operand op2 = getOperand2(bytesArray, operandSize, addrSize); + int size = byteIndex - instrStartIndex; + return factory.newShiftInstruction(name, rtlOperation, op1, (ImmediateOrRegister)op2, size, prefixes); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86ArithmeticInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86ArithmeticInstruction.java new file mode 100644 index 00000000000..b2bf1b34c1c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86ArithmeticInstruction.java @@ -0,0 +1,105 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86ArithmeticInstruction extends X86Instruction + implements ArithmeticInstruction { + final private int operation; //RTL operation + final private Operand operand1; + final private Operand operand2; + final private Operand operand3; + final private String description; + + public X86ArithmeticInstruction(String name, int operation, Operand op1, Operand op2, int size, int prefixes) { + super(name, size, prefixes); + this.operation = operation; + this.operand1 = op1; + this.operand2 = op2; + this.operand3 = null; + description = initDescription(); + } + + public X86ArithmeticInstruction(String name, int operation, Operand op1, Operand op2, Operand op3, int size, int prefixes) { + super(name, size, prefixes); + this.operation = operation; + this.operand1 = op1; + this.operand2 = op2; + this.operand3 = op3; + description = initDescription(); + } + + protected String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + if (operand1 != null) { + buf.append(getOperandAsString(operand1)); + } + if (operand2 != null) { + buf.append(comma); + buf.append(getOperandAsString(operand2)); + } + if(operand3 != null) { + buf.append(comma); + buf.append(getOperandAsString(operand3)); + } + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } + + public Operand getArithmeticDestination() { + return operand1; + } + + public Operand getOperand1() { + return operand1; + } + + public Operand getOperand2() { + return operand2; + } + + public Operand getOperand3() { + return operand3; + } + + public Operand[] getArithmeticSources() { + return (new Operand[] { operand1, operand2, operand3 }); + } + + public int getOperation() { + return operation; + } + + public boolean isArithmetic() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86BranchInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86BranchInstruction.java new file mode 100644 index 00000000000..2cbf8accca0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86BranchInstruction.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86BranchInstruction extends X86Instruction + implements BranchInstruction { + final private X86PCRelativeAddress addr; + + public X86BranchInstruction(String name, X86PCRelativeAddress addr, int size, int prefixes) { + super(name, size, prefixes); + this.addr = addr; + if(addr instanceof X86PCRelativeAddress) { + addr.setInstructionSize(getSize()); + } + } + + public String asString(long currentPc, SymbolFinder symFinder) { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + if(addr != null) { + buf.append(spaces); + if(addr instanceof X86PCRelativeAddress) { + long disp = ((X86PCRelativeAddress)addr).getDisplacement(); + long address = disp + currentPc; + buf.append(symFinder.getSymbolFor(address)); + } + } + return buf.toString(); + } + + public Address getBranchDestination() { + return addr; + } + + public boolean isBranch() { + return true; + } + + public boolean isConditional() { + return false; + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86CallInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86CallInstruction.java new file mode 100644 index 00000000000..4c64a420489 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86CallInstruction.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86CallInstruction extends X86Instruction + implements CallInstruction { + final private Address addr; + + public X86CallInstruction(String name, Address addr, int size, int prefixes) { + super(name, size, prefixes); + this.addr = addr; + if(addr instanceof X86PCRelativeAddress) { + ((X86PCRelativeAddress)addr).setInstructionSize(getSize()); + } + } + + public String asString(long currentPc, SymbolFinder symFinder) { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + long address; + if(addr instanceof X86PCRelativeAddress) { + long disp = ((X86PCRelativeAddress)addr).getDisplacement(); + address = disp + currentPc; + buf.append(symFinder.getSymbolFor(address)); + } + else { + buf.append(addr.toString()); + } + return buf.toString(); + } + + public Address getBranchDestination() { + return addr; + } + + public boolean isCall() { + return true; + } + + public boolean isConditional() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86CondJmpInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86CondJmpInstruction.java new file mode 100644 index 00000000000..ca5475a6713 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86CondJmpInstruction.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86CondJmpInstruction extends X86Instruction + implements BranchInstruction { + final private X86PCRelativeAddress addr; + + public X86CondJmpInstruction(String name, X86PCRelativeAddress addr, int size, int prefixes) { + super(name, size, prefixes); + this.addr = addr; + if(addr instanceof X86PCRelativeAddress) { + addr.setInstructionSize(getSize()); + } + } + + public String asString(long currentPc, SymbolFinder symFinder) { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + + if(addr instanceof X86PCRelativeAddress) { + long disp = ((X86PCRelativeAddress)addr).getDisplacement(); + long address = disp + currentPc; + buf.append(symFinder.getSymbolFor(address)); + } + return buf.toString(); + } + + public Address getBranchDestination() { + return addr; + } + + public boolean isBranch() { + return true; + } + + public boolean isConditional() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86DirectAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86DirectAddress.java new file mode 100644 index 00000000000..5fbdb296ac5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86DirectAddress.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.Address; +import sun.jvm.hotspot.asm.DirectAddress; +import sun.jvm.hotspot.asm.Register; + +public class X86DirectAddress extends DirectAddress { + private long segment; + public X86DirectAddress(long segment, long disp) { + super(disp); + this.segment = segment; + } + public X86DirectAddress(long disp) { + super(disp); + this.segment = 0; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + if (getSegment() != 0) { + buf.append("0x"); + buf.append(Long.toHexString(getSegment())); + buf.append(":"); + } + buf.append("["); + buf.append("0x"); + buf.append(Long.toHexString(getValue())); + buf.append("]"); + + return buf.toString(); + } + + long getSegment() { + return segment; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Disassembler.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Disassembler.java new file mode 100644 index 00000000000..eac53e1a073 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Disassembler.java @@ -0,0 +1,1617 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; +import java.io.*; + +public class X86Disassembler extends Disassembler + implements X86Opcodes { + private int byteIndex; + protected final X86InstructionFactory factory; + + public X86Disassembler(long startPc, byte[] code, X86InstructionFactory factory) { + super(startPc, code); + this.factory = factory; + } + + public X86Disassembler(long startPc, byte[] code) { + this(startPc, code, new X86InstructionFactoryImpl()); + } + + //Please refer to IA-32 Intel Architecture Software Developer's Manual Volume 2 + //APPENDIX A - Table A-2. One-byte Opcode Map + private static final InstructionDecoder oneByteTable[] = { + /* 00 */ + new ArithmeticDecoder("addb", ADDR_E, b_mode, ADDR_G, b_mode, RTLOP_ADD), + new ArithmeticDecoder("addS", ADDR_E, v_mode, ADDR_G, v_mode, RTLOP_ADD), + new ArithmeticDecoder("addb", ADDR_G, b_mode, ADDR_E, b_mode, RTLOP_ADD), + new ArithmeticDecoder("addS", ADDR_G, v_mode, ADDR_E, v_mode, RTLOP_ADD), + new ArithmeticDecoder("addb", ADDR_REG, AL, ADDR_I, b_mode, RTLOP_ADD), + new ArithmeticDecoder("addS", ADDR_REG, EAX, ADDR_I, v_mode, RTLOP_ADD), + new InstructionDecoder("pushl", ADDR_REG, ES), + new InstructionDecoder("popl", ADDR_REG, ES), + /* 08 */ + new LogicalDecoder("orb", ADDR_E, b_mode, ADDR_G, b_mode, RTLOP_OR), + new LogicalDecoder("orS", ADDR_E, v_mode, ADDR_G, v_mode, RTLOP_OR), + new LogicalDecoder("orb", ADDR_G, b_mode, ADDR_E, b_mode, RTLOP_OR), + new LogicalDecoder("orS", ADDR_G, v_mode, ADDR_E, v_mode, RTLOP_OR), + new LogicalDecoder("orb", ADDR_REG, AL, ADDR_I, b_mode, RTLOP_OR), + new LogicalDecoder("orS", ADDR_REG, EAX, ADDR_I, v_mode, RTLOP_OR), + new InstructionDecoder("pushl", ADDR_REG, CS), + null, /* 0x0f extended opcode escape */ + /* 10 */ + new ArithmeticDecoder("adcb", ADDR_E, b_mode, ADDR_G, b_mode, RTLOP_ADDC), + new ArithmeticDecoder("adcS", ADDR_E, v_mode, ADDR_G, v_mode, RTLOP_ADDC), + new ArithmeticDecoder("adcb", ADDR_G, b_mode, ADDR_E, b_mode, RTLOP_ADDC), + new ArithmeticDecoder("adcS", ADDR_G, v_mode, ADDR_E, v_mode, RTLOP_ADDC), + new ArithmeticDecoder("adcb", ADDR_REG, AL, ADDR_I, b_mode, RTLOP_ADDC), + new ArithmeticDecoder("adcS", ADDR_REG, EAX, ADDR_I, v_mode, RTLOP_ADDC), + new InstructionDecoder("pushl", ADDR_REG, SS), + new InstructionDecoder("popl", ADDR_REG, SS), + /* 18 */ + new ArithmeticDecoder("sbbb", ADDR_E, b_mode, ADDR_G, b_mode, RTLOP_SUBC), + new ArithmeticDecoder("sbbS", ADDR_E, v_mode, ADDR_G, v_mode, RTLOP_SUBC), + new ArithmeticDecoder("sbbb", ADDR_G, b_mode, ADDR_E, b_mode, RTLOP_SUBC), + new ArithmeticDecoder("sbbS", ADDR_G, v_mode, ADDR_E, v_mode, RTLOP_SUBC), + new ArithmeticDecoder("sbbb", ADDR_REG, AL, ADDR_I, b_mode, RTLOP_SUBC), + new ArithmeticDecoder("sbbS", ADDR_REG, EAX, ADDR_I, v_mode, RTLOP_SUBC), + new InstructionDecoder("pushl", ADDR_REG, DS), + new InstructionDecoder("popl", ADDR_REG, DS), + /* 20 */ + new LogicalDecoder("andb", ADDR_E, b_mode, ADDR_G, b_mode, RTLOP_AND), + new LogicalDecoder("andS", ADDR_E, v_mode, ADDR_G, v_mode, RTLOP_AND), + new LogicalDecoder("andb", ADDR_G, b_mode, ADDR_E, b_mode, RTLOP_AND), + new LogicalDecoder("andS", ADDR_G, v_mode, ADDR_E, v_mode, RTLOP_AND), + new LogicalDecoder("andb", ADDR_REG, AL, ADDR_I, b_mode, RTLOP_AND), + new LogicalDecoder("andS", ADDR_REG, EAX, ADDR_I, v_mode, RTLOP_AND), + null, /* SEG es prefix */ + new InstructionDecoder("daa"), + /* 28 */ + new ArithmeticDecoder("subb", ADDR_E, b_mode, ADDR_G, b_mode, RTLOP_SUB), + new ArithmeticDecoder("subS", ADDR_E, v_mode, ADDR_G, v_mode, RTLOP_SUB), + new ArithmeticDecoder("subb", ADDR_G, b_mode, ADDR_E, b_mode, RTLOP_SUB), + new ArithmeticDecoder("subS", ADDR_G, v_mode, ADDR_E, v_mode, RTLOP_SUB), + new ArithmeticDecoder("subb", ADDR_REG, AL, ADDR_I, b_mode, RTLOP_SUB), + new ArithmeticDecoder("subS", ADDR_REG, EAX, ADDR_I, v_mode, RTLOP_SUB), + null, /* SEG CS prefix */ + new InstructionDecoder("das"), + /* 30 */ + new LogicalDecoder("xorb", ADDR_E, b_mode, ADDR_G, b_mode, RTLOP_XOR), + new LogicalDecoder("xorS", ADDR_E, v_mode, ADDR_G, v_mode, RTLOP_XOR), + new LogicalDecoder("xorb", ADDR_G, b_mode, ADDR_E, b_mode, RTLOP_XOR), + new LogicalDecoder("xorS", ADDR_G, v_mode, ADDR_E, v_mode, RTLOP_XOR), + new LogicalDecoder("xorb", ADDR_REG, AL, ADDR_I, b_mode, RTLOP_XOR), + new LogicalDecoder("xorS", ADDR_REG, EAX, ADDR_I, v_mode, RTLOP_XOR), + null, /* SEG SS prefix */ + new InstructionDecoder("aaa"), + /* 38 */ + new InstructionDecoder("cmpb", ADDR_E, b_mode, ADDR_G, b_mode), + new InstructionDecoder("cmpS", ADDR_E, v_mode, ADDR_G, v_mode), + new InstructionDecoder("cmpb", ADDR_G, b_mode, ADDR_E, b_mode), + new InstructionDecoder("cmpS", ADDR_G, v_mode, ADDR_E, v_mode), + new InstructionDecoder("cmpb", ADDR_REG, AL, ADDR_I, b_mode), + new InstructionDecoder("cmpS", ADDR_REG, EAX, ADDR_I, v_mode), + null, /* SEG DS prefix */ + new InstructionDecoder("aas"), + /* 40 */ + new ArithmeticDecoder("incS", ADDR_REG, EAX, RTLOP_ADD), + new ArithmeticDecoder("incS", ADDR_REG, ECX, RTLOP_ADD), + new ArithmeticDecoder("incS", ADDR_REG, EDX, RTLOP_ADD), + new ArithmeticDecoder("incS", ADDR_REG, EBX, RTLOP_ADD), + new ArithmeticDecoder("incS", ADDR_REG, ESP, RTLOP_ADD), + new ArithmeticDecoder("incS", ADDR_REG, EBP, RTLOP_ADD), + new ArithmeticDecoder("incS", ADDR_REG, ESI, RTLOP_ADD), + new ArithmeticDecoder("incS", ADDR_REG, EDI, RTLOP_ADD), + /* 48 */ + new ArithmeticDecoder("decS", ADDR_REG, EAX, RTLOP_SUB), + new ArithmeticDecoder("decS", ADDR_REG, ECX, RTLOP_SUB), + new ArithmeticDecoder("decS", ADDR_REG, EDX, RTLOP_SUB), + new ArithmeticDecoder("decS", ADDR_REG, EBX, RTLOP_SUB), + new ArithmeticDecoder("decS", ADDR_REG, ESP, RTLOP_SUB), + new ArithmeticDecoder("decS", ADDR_REG, EBP, RTLOP_SUB), + new ArithmeticDecoder("decS", ADDR_REG, ESI, RTLOP_SUB), + new ArithmeticDecoder("decS", ADDR_REG, EDI, RTLOP_SUB), + /* 50 */ + new InstructionDecoder("pushS", ADDR_REG, EAX), + new InstructionDecoder("pushS", ADDR_REG, ECX), + new InstructionDecoder("pushS", ADDR_REG, EDX), + new InstructionDecoder("pushS", ADDR_REG, EBX), + new InstructionDecoder("pushS", ADDR_REG, ESP), + new InstructionDecoder("pushS", ADDR_REG, EBP), + new InstructionDecoder("pushS", ADDR_REG, ESI), + new InstructionDecoder("pushS", ADDR_REG, EDI), + /* 58 */ + new InstructionDecoder("popS", ADDR_REG, EAX), + new InstructionDecoder("popS", ADDR_REG, ECX), + new InstructionDecoder("popS", ADDR_REG, EDX), + new InstructionDecoder("popS", ADDR_REG, EBX), + new InstructionDecoder("popS", ADDR_REG, ESP), + new InstructionDecoder("popS", ADDR_REG, EBP), + new InstructionDecoder("popS", ADDR_REG, ESI), + new InstructionDecoder("popS", ADDR_REG, EDI), + /* 60 */ + new InstructionDecoder("pusha"), + new InstructionDecoder("popa"), + new InstructionDecoder("boundS", ADDR_G, v_mode, ADDR_E, v_mode), + new InstructionDecoder("arpl", ADDR_E, w_mode, ADDR_G, w_mode), + null, /* seg fs */ + null, /* seg gs */ + null, /* op size prefix */ + null, /* adr size prefix */ + /* 68 */ + new InstructionDecoder("pushS", ADDR_I, v_mode), /* 386 book wrong */ + new ArithmeticDecoder("imulS", ADDR_G, v_mode, ADDR_E, v_mode, ADDR_I, v_mode, RTLOP_SMUL), + new InstructionDecoder("pushl", ADDR_I, b_mode), /* push of byte really pushes 4 bytes */ + new ArithmeticDecoder("imulS", ADDR_G, v_mode, ADDR_E, v_mode, ADDR_I, b_mode, RTLOP_SMUL), + new InstructionDecoder("insb", ADDR_ESDI, b_mode, INDIR_REG, DX), + new InstructionDecoder("insS", ADDR_ESDI, v_mode, INDIR_REG, DX), + new InstructionDecoder("outsb", INDIR_REG, DX, ADDR_DSSI, b_mode), + new InstructionDecoder("outsS", INDIR_REG, DX, ADDR_DSSI, v_mode), + /* 70 */ + new ConditionalJmpDecoder("jo", ADDR_J, b_mode), + new ConditionalJmpDecoder("jno", ADDR_J, b_mode), + new ConditionalJmpDecoder("jb", ADDR_J, b_mode), + new ConditionalJmpDecoder("jae", ADDR_J, b_mode), + new ConditionalJmpDecoder("je", ADDR_J, b_mode), + new ConditionalJmpDecoder("jne", ADDR_J, b_mode), + new ConditionalJmpDecoder("jbe", ADDR_J, b_mode), + new ConditionalJmpDecoder("ja", ADDR_J, b_mode), + /* 78 */ + new ConditionalJmpDecoder("js", ADDR_J, b_mode), + new ConditionalJmpDecoder("jns", ADDR_J, b_mode), + new ConditionalJmpDecoder("jp", ADDR_J, b_mode), + new ConditionalJmpDecoder("jnp", ADDR_J, b_mode), + new ConditionalJmpDecoder("jl", ADDR_J, b_mode), + new ConditionalJmpDecoder("jnl", ADDR_J, b_mode), + new ConditionalJmpDecoder("jle", ADDR_J, b_mode), + new ConditionalJmpDecoder("jg", ADDR_J, b_mode), + /* 80 */ + new GRPDecoder(null, 0), + new GRPDecoder(null, 1), + null, + new GRPDecoder(null, 2), + new InstructionDecoder("testb", ADDR_E, b_mode, ADDR_G, b_mode), + new InstructionDecoder("testS", ADDR_E, v_mode, ADDR_G, v_mode), + new MoveDecoder("xchgb", ADDR_E, b_mode, ADDR_G, b_mode), + new MoveDecoder("xchgS", ADDR_E, v_mode, ADDR_G, v_mode), + /* 88 */ + new MoveDecoder("movb", ADDR_E, b_mode, ADDR_G, b_mode), + new MoveDecoder("movS", ADDR_E, v_mode, ADDR_G, v_mode), + new MoveDecoder("movb", ADDR_G, b_mode, ADDR_E, b_mode), + new MoveDecoder("movS", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("movw", ADDR_E, w_mode, ADDR_SEG, w_mode), + new InstructionDecoder("leaS", ADDR_G, v_mode, ADDR_E, 0), + new MoveDecoder("movw", ADDR_SEG, w_mode, ADDR_E, w_mode), + new InstructionDecoder("popS", ADDR_E, v_mode), + /* 90 */ + new InstructionDecoder("nop"), + new MoveDecoder("xchgS", ADDR_REG, ECX, ADDR_REG, EAX), + new MoveDecoder("xchgS", ADDR_REG, EDX, ADDR_REG, EAX), + new MoveDecoder("xchgS", ADDR_REG, EBX, ADDR_REG, EAX), + new MoveDecoder("xchgS", ADDR_REG, ESP, ADDR_REG, EAX), + new MoveDecoder("xchgS", ADDR_REG, EBP, ADDR_REG, EAX), + new MoveDecoder("xchgS", ADDR_REG, ESI, ADDR_REG, EAX), + new MoveDecoder("xchgS", ADDR_REG, EDI, ADDR_REG, EAX), + /* 98 */ + new InstructionDecoder("cwtl"), + new InstructionDecoder("cltd"), + new CallDecoder("lcall", ADDR_DIR, p_mode), + null, /* fwait */ + new InstructionDecoder("pushf"), + new InstructionDecoder("popf"), + new InstructionDecoder("sahf"), + new InstructionDecoder("lahf"), + /* a0 */ + new MoveDecoder("movb", ADDR_REG, AL, ADDR_OFF, b_mode), + new MoveDecoder("movS", ADDR_REG, EAX, ADDR_OFF, v_mode), + new MoveDecoder("movb", ADDR_OFF, b_mode, ADDR_REG, AL), + new MoveDecoder("movS", ADDR_OFF, v_mode, ADDR_REG, EAX), + new MoveDecoder("movsb", ADDR_ESDI, b_mode, ADDR_DSSI, b_mode), + new MoveDecoder("movsS", ADDR_ESDI, v_mode, ADDR_DSSI, v_mode), + new InstructionDecoder("cmpsb", ADDR_ESDI, b_mode, ADDR_DSSI, b_mode), + new InstructionDecoder("cmpsS", ADDR_ESDI, v_mode, ADDR_DSSI, v_mode), + /* a8 */ + new InstructionDecoder("testb", ADDR_REG, AL, ADDR_I, b_mode), + new InstructionDecoder("testS", ADDR_REG, EAX, ADDR_I, v_mode), + new InstructionDecoder("stosb", ADDR_ESDI, b_mode, ADDR_REG, AL), + new InstructionDecoder("stosS", ADDR_ESDI, v_mode, ADDR_REG, EAX), + new InstructionDecoder("lodsb", ADDR_REG, AL, ADDR_DSSI, b_mode), + new InstructionDecoder("lodsS", ADDR_REG, EAX, ADDR_DSSI, v_mode), + new InstructionDecoder("scasb", ADDR_REG, AL, ADDR_ESDI, b_mode), + new InstructionDecoder("scasS", ADDR_REG, EAX, ADDR_ESDI, v_mode), + /* b0 */ + new MoveDecoder("movb", ADDR_REG, AL, ADDR_I, b_mode), + new MoveDecoder("movb", ADDR_REG, CL, ADDR_I, b_mode), + new MoveDecoder("movb", ADDR_REG, DL, ADDR_I, b_mode), + new MoveDecoder("movb", ADDR_REG, BL, ADDR_I, b_mode), + new MoveDecoder("movb", ADDR_REG, AH, ADDR_I, b_mode), + new MoveDecoder("movb", ADDR_REG, CH, ADDR_I, b_mode), + new MoveDecoder("movb", ADDR_REG, DH, ADDR_I, b_mode), + new MoveDecoder("movb", ADDR_REG, BH, ADDR_I, b_mode), + /* b8 */ + new MoveDecoder("movS", ADDR_REG, EAX, ADDR_I, v_mode), + new MoveDecoder("movS", ADDR_REG, ECX, ADDR_I, v_mode), + new MoveDecoder("movS", ADDR_REG, EDX, ADDR_I, v_mode), + new MoveDecoder("movS", ADDR_REG, EBX, ADDR_I, v_mode), + new MoveDecoder("movS", ADDR_REG, ESP, ADDR_I, v_mode), + new MoveDecoder("movS", ADDR_REG, EBP, ADDR_I, v_mode), + new MoveDecoder("movS", ADDR_REG, ESI, ADDR_I, v_mode), + new MoveDecoder("movS", ADDR_REG, EDI, ADDR_I, v_mode), + /* c0 */ + new GRPDecoder(null, 3), + new GRPDecoder(null, 4), + new BranchDecoder("ret", ADDR_I, w_mode), + new BranchDecoder("ret"), + new InstructionDecoder("lesS", ADDR_G, v_mode, ADDR_E, 0), + new InstructionDecoder("ldsS", ADDR_G, v_mode, ADDR_E, 0), + new MoveDecoder("movb", ADDR_E, b_mode, ADDR_I, b_mode), + new MoveDecoder("movS", ADDR_E, v_mode, ADDR_I, v_mode), + /* c8 */ + new InstructionDecoder("enter", ADDR_I, w_mode, ADDR_I, b_mode), + new InstructionDecoder("leave"), + new InstructionDecoder("lret", ADDR_I, w_mode), + new InstructionDecoder("lret"), + new InstructionDecoder("int3"), + new InstructionDecoder("int", ADDR_I, b_mode), + new InstructionDecoder("into"), + new InstructionDecoder("iret"), + /* d0 */ + new GRPDecoder(null, 5), + new GRPDecoder(null, 6), + new GRPDecoder(null, 7), + new GRPDecoder(null, 8), + new InstructionDecoder("aam", ADDR_I, b_mode), + new InstructionDecoder("aad", ADDR_I, b_mode), + null, + new InstructionDecoder("xlat"), + /* d8 */ + new FloatDecoder(), + new FloatDecoder(), + new FloatDecoder(), + new FloatDecoder(), + new FloatDecoder(), + new FloatDecoder(), + new FloatDecoder(), + new FloatDecoder(), + /* e0 */ + new BranchDecoder("loopne", ADDR_J, b_mode), + new BranchDecoder("loope", ADDR_J, b_mode), + new BranchDecoder("loop", ADDR_J, b_mode), + new ConditionalJmpDecoder("jCcxz", ADDR_J, b_mode), + new InstructionDecoder("inb", ADDR_REG, AL, ADDR_I, b_mode), + new InstructionDecoder("inS", ADDR_REG, EAX, ADDR_I, b_mode), + new InstructionDecoder("outb", ADDR_I, b_mode, ADDR_REG, AL), + new InstructionDecoder("outS", ADDR_I, b_mode, ADDR_REG, EAX), + /* e8 */ + new CallDecoder("call", ADDR_J, v_mode), + new JmpDecoder("jmp", ADDR_J, v_mode), + new JmpDecoder("ljmp", ADDR_DIR, p_mode), + new JmpDecoder("jmp", ADDR_J, b_mode), + new InstructionDecoder("inb", ADDR_REG, AL, INDIR_REG, DX), + new InstructionDecoder("inS", ADDR_REG, EAX, INDIR_REG, DX), + new InstructionDecoder("outb", INDIR_REG, DX, ADDR_REG,AL), + new InstructionDecoder("outS", INDIR_REG, DX, ADDR_REG, EAX), + /* f0 */ + new InstructionDecoder("lock"), /* lock prefix */ + null, + new InstructionDecoder("repne"), /* repne */ + new InstructionDecoder("rep"), /* repz */ + new InstructionDecoder("hlt"), + new InstructionDecoder("cmc"), + new GRPDecoder(null, 9), + new GRPDecoder(null, 10), + /* f8 */ + new InstructionDecoder("clc"), + new InstructionDecoder("stc"), + new InstructionDecoder("cli"), + new InstructionDecoder("sti"), + new InstructionDecoder("cld"), + new InstructionDecoder("std"), + new GRPDecoder(null, 11), + new GRPDecoder(null, 12) + }; + + //APPENDIX A - Table A-3. Two-byte Opcode Map + private static final InstructionDecoder twoByteTable[] = { + /* 00 */ + new GRPDecoder(null, 13), + new GRPDecoder(null, 14), + new InstructionDecoder("larS", ADDR_G, v_mode, ADDR_E, w_mode), + new InstructionDecoder("lslS", ADDR_G, v_mode, ADDR_E, w_mode), + null, + null, + new InstructionDecoder("clts"), + null, + /* 08 */ + new InstructionDecoder("invd"), + new InstructionDecoder("wbinvd"), + null, + null, + null, + null, + null, + null, + /* 10 */ //SSE + new SSEMoveDecoder("movups", ADDR_V, ps_mode, ADDR_W, ps_mode), + new SSEMoveDecoder("movups", ADDR_W, ps_mode, ADDR_V, ps_mode), + new SSEMoveDecoder("movlps", ADDR_W, q_mode, ADDR_V, q_mode), + new SSEMoveDecoder("movlps", ADDR_V, q_mode, ADDR_W, q_mode), + new SSEInstructionDecoder("unpcklps", ADDR_V, ps_mode, ADDR_W, q_mode), + new SSEInstructionDecoder("unpckhps", ADDR_V, ps_mode, ADDR_W, q_mode), + new SSEMoveDecoder("movhps", ADDR_V, q_mode, ADDR_W, q_mode), + new SSEMoveDecoder("movhps", ADDR_W, q_mode, ADDR_V, q_mode), + /* 18 */ + new GRPDecoder(null, 21), + null, + null, + null, + null, + null, + null, + null, + /* 20 */ + /* these are all backward in appendix A of the intel book */ + new MoveDecoder("movl", ADDR_R, d_mode, ADDR_C, d_mode), + new MoveDecoder("movl", ADDR_R, d_mode, ADDR_D, d_mode), + new MoveDecoder("movl", ADDR_C, d_mode, ADDR_R, d_mode), + new MoveDecoder("movl", ADDR_D, d_mode, ADDR_R, d_mode), + new MoveDecoder("movl", ADDR_R, d_mode, ADDR_T, d_mode), + null, + new MoveDecoder("movl", ADDR_T, d_mode, ADDR_R, d_mode), + null, + /* 28 */ + new SSEMoveDecoder("movaps", ADDR_V, ps_mode, ADDR_W, ps_mode), + new SSEMoveDecoder("movaps", ADDR_W, ps_mode, ADDR_V, ps_mode), + new SSEInstructionDecoder("cvtpi2ps", ADDR_V, ps_mode, ADDR_Q, q_mode), + new SSEMoveDecoder("movntps", ADDR_W, ps_mode, ADDR_V, ps_mode), + new SSEInstructionDecoder("cvttps2pi", ADDR_Q, q_mode, ADDR_W, ps_mode), + new SSEInstructionDecoder("cvtps2pi", ADDR_Q, q_mode, ADDR_W, ps_mode), + new SSEInstructionDecoder("ucomiss", ADDR_V, ss_mode, ADDR_W, ss_mode), + new SSEInstructionDecoder("comiss", ADDR_V, ps_mode, ADDR_W, ps_mode), + /* 30 */ + new SSEInstructionDecoder("wrmsr"), + new SSEInstructionDecoder("rtdsc"), + new SSEInstructionDecoder("rdmsr"), + new SSEInstructionDecoder("rdpmc"), + new SSEInstructionDecoder("sysenter"), + new SSEInstructionDecoder("sysexit"), + null, + null, + /* 38 */ + null, + null, + null, + null, + new SSEMoveDecoder("movnti", ADDR_G, v_mode, ADDR_E, v_mode), + null, + null, + null, + /* 40 */ + new MoveDecoder("cmovo", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovno", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovb", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovae", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmove", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovne", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovbe", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmova", ADDR_G, v_mode, ADDR_E, v_mode), + /* 48 */ + new MoveDecoder("cmovs", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovns", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovp", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovnp", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovl", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovge", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovle", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("cmovg", ADDR_G, v_mode, ADDR_E, v_mode), + /* 50 */ + new SSEMoveDecoder("movmskps", ADDR_E, d_mode, ADDR_V, ps_mode), + new SSEInstructionDecoder("sqrtps", ADDR_V, ps_mode, ADDR_W, ps_mode), + new SSEInstructionDecoder("rsqrtps", ADDR_V, ps_mode, ADDR_W, ps_mode), + new SSEInstructionDecoder("rcpps", ADDR_V, ps_mode, ADDR_W, ps_mode), + new SSELogicalDecoder("andps", ADDR_V, ps_mode, ADDR_W, ps_mode, RTLOP_AND), + new SSELogicalDecoder("andnps", ADDR_V, ps_mode, ADDR_W, ps_mode, RTLOP_AND), + new SSELogicalDecoder("orps", ADDR_V, ps_mode, ADDR_W, ps_mode, RTLOP_OR), + new SSELogicalDecoder("xorps", ADDR_V, ps_mode, ADDR_W, ps_mode, RTLOP_XOR), + /* 58 */ + new SSEArithmeticDecoder("addps", ADDR_V, ps_mode, ADDR_W, ps_mode, RTLOP_ADD), + new SSEArithmeticDecoder("mulps", ADDR_V, ps_mode, ADDR_W, ps_mode, RTLOP_SMUL), + new SSEInstructionDecoder("cvtps2pd", ADDR_V, pd_mode, ADDR_W, ps_mode), + new SSEInstructionDecoder("cvtdq2ps", ADDR_V, ps_mode, ADDR_W, dq_mode), + new SSEArithmeticDecoder("subps", ADDR_V, ps_mode, ADDR_W, ps_mode, RTLOP_SUB), + new SSEInstructionDecoder("minps", ADDR_V, ps_mode, ADDR_W, ps_mode), + new SSEArithmeticDecoder("divps", ADDR_V, ps_mode, ADDR_W, ps_mode, RTLOP_SDIV), + new SSEInstructionDecoder("maxps", ADDR_V, ps_mode, ADDR_W, ps_mode), + /* 60 */ + new SSEInstructionDecoder("punpcklbw", ADDR_P, q_mode, ADDR_Q, d_mode), + new SSEInstructionDecoder("punpcklwd", ADDR_P, q_mode, ADDR_Q, d_mode), + new SSEInstructionDecoder("punpckldq", ADDR_P, q_mode, ADDR_Q, d_mode), + new SSEInstructionDecoder("packsswb", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEInstructionDecoder("pcmpgtb", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEInstructionDecoder("pcmpgtw", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEInstructionDecoder("pcmpgtd", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEInstructionDecoder("packuswb", ADDR_P, q_mode, ADDR_Q, q_mode), + /* 68 */ + new SSEInstructionDecoder("punpckhbw", ADDR_P, q_mode, ADDR_Q, d_mode), + new SSEInstructionDecoder("punpckhwd", ADDR_P, q_mode, ADDR_Q, d_mode), + new SSEInstructionDecoder("punpckhdq", ADDR_P, q_mode, ADDR_Q, d_mode), + new SSEInstructionDecoder("packssdw", ADDR_P, q_mode, ADDR_Q, d_mode), + null, + null, + new SSEMoveDecoder("movd", ADDR_P, d_mode, ADDR_E, d_mode), + new SSEMoveDecoder("movq", ADDR_P, q_mode, ADDR_E, q_mode), + /* 70 */ + new SSEInstructionDecoder("pshufw", ADDR_P, q_mode, ADDR_Q, q_mode, ADDR_I, b_mode), + new GRPDecoder(null, 17), + new GRPDecoder(null, 18), + new GRPDecoder(null, 19), + new SSEInstructionDecoder("pcmpeqb", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEInstructionDecoder("pcmpeqw", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEInstructionDecoder("pcmpeqd", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEInstructionDecoder("emms"), + /* 78 */ + null, + null, + null, + null, + null, + null, + new SSEMoveDecoder("movd", ADDR_E, d_mode, ADDR_P, d_mode), + new SSEMoveDecoder("movq", ADDR_Q, q_mode, ADDR_P, q_mode), + /* 80 */ + new ConditionalJmpDecoder("jo", ADDR_J, v_mode), + new ConditionalJmpDecoder("jno", ADDR_J, v_mode), + new ConditionalJmpDecoder("jb", ADDR_J, v_mode), + new ConditionalJmpDecoder("jae", ADDR_J, v_mode), + new ConditionalJmpDecoder("je", ADDR_J, v_mode), + new ConditionalJmpDecoder("jne", ADDR_J, v_mode), + new ConditionalJmpDecoder("jbe", ADDR_J, v_mode), + new ConditionalJmpDecoder("ja", ADDR_J, v_mode), + /* 88 */ + new ConditionalJmpDecoder("js", ADDR_J, v_mode), + new ConditionalJmpDecoder("jns", ADDR_J, v_mode), + new ConditionalJmpDecoder("jp", ADDR_J, v_mode), + new ConditionalJmpDecoder("jnp", ADDR_J, v_mode), + new ConditionalJmpDecoder("jl", ADDR_J, v_mode), + new ConditionalJmpDecoder("jge", ADDR_J, v_mode), + new ConditionalJmpDecoder("jle", ADDR_J, v_mode), + new ConditionalJmpDecoder("jg", ADDR_J, v_mode), + /* 90 */ + new InstructionDecoder("seto", ADDR_E, b_mode), + new InstructionDecoder("setno", ADDR_E, b_mode), + new InstructionDecoder("setb", ADDR_E, b_mode), + new InstructionDecoder("setae", ADDR_E, b_mode), + new InstructionDecoder("sete", ADDR_E, b_mode), + new InstructionDecoder("setne", ADDR_E, b_mode), + new InstructionDecoder("setbe", ADDR_E, b_mode), + new InstructionDecoder("seta", ADDR_E, b_mode), + /* 98 */ + new InstructionDecoder("sets", ADDR_E, b_mode), + new InstructionDecoder("setns", ADDR_E, b_mode), + new InstructionDecoder("setp", ADDR_E, b_mode), + new InstructionDecoder("setnp", ADDR_E, b_mode), + new InstructionDecoder("setl", ADDR_E, b_mode), + new InstructionDecoder("setge", ADDR_E, b_mode), + new InstructionDecoder("setle", ADDR_E, b_mode), + new InstructionDecoder("setg", ADDR_E, b_mode), + /* a0 */ + new InstructionDecoder("pushl", ADDR_REG, FS), + new InstructionDecoder("popl", ADDR_REG, FS), + null, + new InstructionDecoder("btS", ADDR_E, v_mode, ADDR_G, v_mode), + new InstructionDecoder("shldS", ADDR_E, v_mode, ADDR_G, v_mode, ADDR_I, b_mode), + new InstructionDecoder("shldS", ADDR_E, v_mode, ADDR_G, v_mode, ADDR_REG, CL), + null, + null, + /* a8 */ + new InstructionDecoder("pushl", ADDR_REG, GS), + new InstructionDecoder("popl", ADDR_REG, GS), + new SSEInstructionDecoder("rsm"), + new InstructionDecoder("btsS", ADDR_E, v_mode, ADDR_G, v_mode), + new InstructionDecoder("shrdS", ADDR_E, v_mode, ADDR_G, v_mode, ADDR_I, b_mode), + new InstructionDecoder("shrdS", ADDR_E, v_mode, ADDR_G, v_mode, ADDR_REG, CL), + new GRPDecoder(null, 20), + new ArithmeticDecoder("imulS", ADDR_G, v_mode, ADDR_E, v_mode, RTLOP_SMUL), + /* b0 */ + new InstructionDecoder("cmpxchgb", ADDR_E, b_mode, ADDR_G, b_mode), + new InstructionDecoder("cmpxchgS", ADDR_E, v_mode, ADDR_G, v_mode), + new InstructionDecoder("lssS", ADDR_G, v_mode, ADDR_M, p_mode), + new InstructionDecoder("btrS", ADDR_E, v_mode, ADDR_G, v_mode), + new InstructionDecoder("lfsS", ADDR_G, v_mode, ADDR_M, p_mode), + new InstructionDecoder("lgsS", ADDR_G, v_mode, ADDR_M, p_mode), + new MoveDecoder("movzbS", ADDR_G, v_mode, ADDR_E, b_mode), + new MoveDecoder("movzwS", ADDR_G, v_mode, ADDR_E, w_mode), + /* b8 */ + null, + null, + new GRPDecoder(null, 15), + new InstructionDecoder("btcS", ADDR_E, v_mode, ADDR_G, v_mode), + new InstructionDecoder("bsfS", ADDR_G, v_mode, ADDR_E, v_mode), + new InstructionDecoder("bsrS", ADDR_G, v_mode, ADDR_E, v_mode), + new MoveDecoder("movsbS", ADDR_G, v_mode, ADDR_E, b_mode), + new MoveDecoder("movswS", ADDR_G, v_mode, ADDR_E, w_mode), + /* c0 */ + new ArithmeticDecoder("xaddb", ADDR_E, b_mode, ADDR_G, b_mode, RTLOP_ADD), + new ArithmeticDecoder("xaddS", ADDR_E, v_mode, ADDR_G, v_mode, RTLOP_ADD), + new SSEInstructionDecoder("cmpps", ADDR_V, ps_mode, ADDR_W, ps_mode, ADDR_I, b_mode), + new SSEMoveDecoder("movnti", ADDR_E, d_mode, ADDR_G, d_mode), + new SSEInstructionDecoder("pinsrw", ADDR_P, q_mode, ADDR_E, d_mode, ADDR_I, b_mode), + new SSEInstructionDecoder("pextrw", ADDR_G, d_mode, ADDR_P, q_mode, ADDR_I, b_mode), + new SSEInstructionDecoder("shufps", ADDR_V, ps_mode, ADDR_W, ps_mode, ADDR_I, b_mode), + new GRPDecoder(null, 16), + /* c8 */ + new InstructionDecoder("bswap", ADDR_REG, EAX), + new InstructionDecoder("bswap", ADDR_REG, ECX), + new InstructionDecoder("bswap", ADDR_REG, EDX), + new InstructionDecoder("bswap", ADDR_REG, EBX), + new InstructionDecoder("bswap", ADDR_REG, ESP), + new InstructionDecoder("bswap", ADDR_REG, EBP), + new InstructionDecoder("bswap", ADDR_REG, ESI), + new InstructionDecoder("bswap", ADDR_REG, EDI), + /* d0 */ + null, + new SSEShiftDecoder("psrlw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SRL), + new SSEShiftDecoder("psrld", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SRL), + new SSEShiftDecoder("psrlq", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SRL), + new SSEArithmeticDecoder("paddq", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_ADD), + new SSEArithmeticDecoder("pmullw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SMUL), + null, + new SSEMoveDecoder("pmovmskb", ADDR_G, d_mode, ADDR_P, q_mode), + /* d8 */ + new SSEArithmeticDecoder("psubusb", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SUB), + new SSEArithmeticDecoder("psubusw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SUB), + new SSEInstructionDecoder("pminub", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSELogicalDecoder("pand", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_AND), + new SSEArithmeticDecoder("paddusb", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_ADD), + new SSEArithmeticDecoder("paddusw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_ADD), + new SSEInstructionDecoder("pmaxub", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSELogicalDecoder("pandn", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_AND), + /* e0 */ + new SSEInstructionDecoder("pavgb", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEInstructionDecoder("psraw", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEInstructionDecoder("psrad", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEInstructionDecoder("pavgw", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSEArithmeticDecoder("pmulhuw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_UMUL), + new SSEArithmeticDecoder("pmulhw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SMUL), + null, + new SSEMoveDecoder("movntq", ADDR_W, q_mode, ADDR_V, q_mode), + /* e8 */ + new SSEArithmeticDecoder("psubsb", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SUB), + new SSEArithmeticDecoder("psubsw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SUB), + new SSEInstructionDecoder("pminsw", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSELogicalDecoder("por", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_OR), + new SSEArithmeticDecoder("paddsb", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_ADD), + new SSEArithmeticDecoder("paddsw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_ADD), + new SSEInstructionDecoder("pmaxsw", ADDR_P, q_mode, ADDR_Q, q_mode), + new SSELogicalDecoder("pxor", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_XOR), + /* f0 */ + null, + new SSEShiftDecoder("psllw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SLL), + new SSEShiftDecoder("pslld", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SLL), + new SSEShiftDecoder("psllq", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SLL), + new SSEArithmeticDecoder("pmuludq", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_UMUL), + new SSEArithmeticDecoder("pmaddwd", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_ADD), + new SSEArithmeticDecoder("psadbw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_ADD), + new SSEMoveDecoder("maskmoveq", ADDR_P, pi_mode, ADDR_Q, pi_mode), + /* f8 */ + new SSEArithmeticDecoder("psubb", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SUB), + new SSEArithmeticDecoder("psubw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SUB), + new SSEArithmeticDecoder("psubd", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SUB), + new SSEArithmeticDecoder("psubq", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_SUB), + new SSEArithmeticDecoder("paddb", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_ADD), + new SSEArithmeticDecoder("paddw", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_ADD), + new SSEArithmeticDecoder("paddd", ADDR_P, q_mode, ADDR_Q, q_mode, RTLOP_ADD), + null + }; + + private static final InstructionDecoder twoBytePrefixF2Table[] = { + /* 00 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 08 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 10 */ + new SSEMoveDecoder("movsd", ADDR_V, sd_mode, ADDR_W, sd_mode), + new SSEMoveDecoder("movsd", ADDR_V, sd_mode, ADDR_W, sd_mode), + null, + null, + null, + null, + null, + null, + /* 18 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 20 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 28 */ + null, + null, + new SSEInstructionDecoder("cvtsi2sd", ADDR_V, sd_mode, ADDR_E, d_mode), + null, + new SSEInstructionDecoder("cvttsd2si", ADDR_G, d_mode, ADDR_W, sd_mode), + new SSEInstructionDecoder("cvtsd2si", ADDR_G, d_mode, ADDR_W, sd_mode), + null, + null, + /* 30 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 38 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 40 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 48 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 50 */ + null, + new SSEInstructionDecoder("sqrtsd", ADDR_V, sd_mode, ADDR_W, sd_mode), + null, + null, + null, + null, + null, + null, + /* 58 */ + new SSEArithmeticDecoder("addsd", ADDR_V, sd_mode, ADDR_W, sd_mode, RTLOP_ADD), + new SSEArithmeticDecoder("mulsd", ADDR_V, sd_mode, ADDR_W, sd_mode, RTLOP_SMUL), + new SSEInstructionDecoder("cvtsd2ss", ADDR_V, sd_mode, ADDR_W, sd_mode), + null, + new SSEArithmeticDecoder("subsd", ADDR_V, sd_mode, ADDR_W, sd_mode, RTLOP_SUB), + new SSEInstructionDecoder("minsd", ADDR_V, sd_mode, ADDR_W, sd_mode), + new SSEArithmeticDecoder("divsd", ADDR_V, sd_mode, ADDR_W, sd_mode, RTLOP_SDIV), + new SSEInstructionDecoder("maxsd", ADDR_V, sd_mode, ADDR_W, sd_mode), + /* 60 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 68 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 70 */ + new SSEInstructionDecoder("pshuflw", ADDR_V, dq_mode, ADDR_W, dq_mode, ADDR_I, b_mode), + null, + null, + null, + null, + null, + null, + null, + /* 78 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 80 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 88 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 90 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 98 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* a0 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* a8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* b0 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* b8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* c0 */ + null, + null, + new SSEInstructionDecoder("cmpsd", ADDR_V, sd_mode, ADDR_W, sd_mode, ADDR_I, b_mode), + null, + null, + null, + null, + null, + /* c8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* d0 */ + null, + null, + null, + null, + null, + null, + new SSEMoveDecoder("movdq2q", ADDR_P, q_mode, ADDR_W, q_mode), + null, + /* d8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* e0 */ + null, + null, + null, + null, + null, + null, + new SSEInstructionDecoder("cvtpd2dq", ADDR_V, dq_mode, ADDR_W, pd_mode), + null, + /* e8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* f0 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* f8 */ + null, + null, + null, + null, + null, + null, + null, + null + }; + + private static final InstructionDecoder twoBytePrefixF3Table[] = { + /* 00 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 08 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 10 */ + new SSEMoveDecoder("movss", ADDR_V, ss_mode, ADDR_W, ss_mode), + new SSEMoveDecoder("movss", ADDR_W, ss_mode, ADDR_V, ss_mode), + null, + null, + null, + null, + null, + null, + /* 18 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 20 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 28 */ + null, + null, + new SSEInstructionDecoder("cvtsi2ss", ADDR_V, ss_mode, ADDR_E, d_mode), + null, + new SSEInstructionDecoder("cvttss2si", ADDR_G, d_mode, ADDR_W, ss_mode), + new SSEInstructionDecoder("cvtss2si", ADDR_G, d_mode, ADDR_W, ss_mode), + null, + null, + /* 30 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 38 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 40 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 48 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 50 */ + null, + new SSEInstructionDecoder("sqrtss", ADDR_V, ss_mode, ADDR_W, ss_mode), + new SSEInstructionDecoder("rsqrtss", ADDR_V, ss_mode, ADDR_W, ss_mode), + new SSEInstructionDecoder("rcpss", ADDR_V, ss_mode, ADDR_W, ss_mode), + null, + null, + null, + null, + /* 58 */ + new SSEArithmeticDecoder("addss", ADDR_V, ss_mode, ADDR_W, ss_mode, RTLOP_ADD), + new SSEArithmeticDecoder("mulss", ADDR_V, ss_mode, ADDR_W, ss_mode, RTLOP_SMUL), + new SSEInstructionDecoder("cvtss2sd", ADDR_V, ss_mode, ADDR_W, ss_mode), + new SSEInstructionDecoder("cvttps2dq", ADDR_V, dq_mode, ADDR_W, ps_mode), + new SSEArithmeticDecoder("subss", ADDR_V, ss_mode, ADDR_W, ss_mode, RTLOP_SUB), + new SSEInstructionDecoder("minss", ADDR_V, ss_mode, ADDR_W, ss_mode), + new SSEArithmeticDecoder("divss", ADDR_V, ss_mode, ADDR_W, ss_mode, RTLOP_SDIV), + new SSEInstructionDecoder("maxss", ADDR_V, ss_mode, ADDR_W, ss_mode), + /* 60 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 68 */ + null, + null, + null, + null, + null, + null, + null, + new SSEMoveDecoder("movdqu", ADDR_V, dq_mode, ADDR_W, dq_mode), + /* 70 */ + new SSEInstructionDecoder("pshufhw", ADDR_V, dq_mode, ADDR_W, dq_mode, ADDR_I, b_mode), + null, + null, + null, + null, + null, + null, + null, + /* 78 */ + null, + null, + null, + null, + null, + null, + new SSEMoveDecoder("movq", ADDR_V, q_mode, ADDR_W, q_mode), + new SSEMoveDecoder("movdqu", ADDR_W, dq_mode, ADDR_V, dq_mode), + /* 80 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 88 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 90 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 98 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* a0 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* a8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* b0 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* b8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* c0 */ + null, + null, + new SSEInstructionDecoder("cmpss", ADDR_V, ss_mode, ADDR_W, ss_mode, ADDR_I, b_mode), + null, + null, + null, + null, + null, + /* c8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* d0 */ + null, + null, + null, + null, + null, + null, + new SSEMoveDecoder("movq2dq", ADDR_V, dq_mode, ADDR_Q, q_mode), + null, + /* d8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* e0 */ + null, + null, + null, + null, + null, + null, + new SSEInstructionDecoder("cvtdq2pd", ADDR_V, pd_mode, ADDR_W, dq_mode), + null, + /* e8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* f0 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* f8 */ + null, + null, + null, + null, + null, + null, + null, + null + }; + + private static final InstructionDecoder twoBytePrefix66Table[] = { + /* 00 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 08 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 10 */ + new SSEMoveDecoder("movupd", ADDR_V, pd_mode, ADDR_W, pd_mode), + new SSEMoveDecoder("movupd", ADDR_W, pd_mode, ADDR_V, pd_mode), + new SSEMoveDecoder("movlpd", ADDR_V, q_mode, ADDR_W, s_mode), + new SSEMoveDecoder("movlpd", ADDR_V, q_mode, ADDR_W, q_mode), + new SSEInstructionDecoder("unpcklpd", ADDR_V, pd_mode, ADDR_W, q_mode), + new SSEInstructionDecoder("unpckhpd", ADDR_V, pd_mode, ADDR_W, q_mode), + new SSEMoveDecoder("movhpd", ADDR_V, q_mode, ADDR_W, q_mode), + new SSEMoveDecoder("movhpd", ADDR_W, q_mode, ADDR_V, q_mode), + /* 18 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 20 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 28 */ + new SSEMoveDecoder("movapd", ADDR_V, pd_mode, ADDR_W, pd_mode), + new SSEMoveDecoder("movapd", ADDR_W, pd_mode, ADDR_V, pd_mode), + new SSEInstructionDecoder("cvtpi2pd", ADDR_V, pd_mode, ADDR_Q, dq_mode), + new SSEMoveDecoder("movntpd", ADDR_W, pd_mode, ADDR_V, pd_mode), + new SSEInstructionDecoder("cvttpd2pi", ADDR_Q, dq_mode, ADDR_W, pd_mode), + new SSEInstructionDecoder("cvtpd2pi", ADDR_Q, dq_mode, ADDR_W, pd_mode), + new SSEInstructionDecoder("ucomisd", ADDR_V, sd_mode, ADDR_W, sd_mode), + new SSEInstructionDecoder("comisd", ADDR_V, sd_mode, ADDR_W, sd_mode), + /* 30 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 38 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 40 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 48 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 50 */ + new SSEMoveDecoder("movmskpd", ADDR_E, d_mode, ADDR_V, pd_mode), + new SSEInstructionDecoder("sqrtpd", ADDR_V, pd_mode, ADDR_W, pd_mode), + null, + null, + new SSELogicalDecoder("andpd", ADDR_V, pd_mode, ADDR_W, pd_mode, RTLOP_AND), + new SSELogicalDecoder("andnpd", ADDR_V, pd_mode, ADDR_W, pd_mode, RTLOP_AND), + new SSELogicalDecoder("orpd", ADDR_V, pd_mode, ADDR_W, pd_mode, RTLOP_OR), + new SSELogicalDecoder("xorpd", ADDR_V, pd_mode, ADDR_W, pd_mode, RTLOP_XOR), + /* 58 */ + new SSEArithmeticDecoder("addpd", ADDR_V, pd_mode, ADDR_W, pd_mode, RTLOP_ADD), + new SSEArithmeticDecoder("mulpd", ADDR_V, pd_mode, ADDR_W, pd_mode, RTLOP_SMUL), + new SSEInstructionDecoder("cvtpd2ps", ADDR_V, ps_mode, ADDR_W, pd_mode), + new SSEInstructionDecoder("cvtps2dq", ADDR_V, dq_mode, ADDR_W, ps_mode), + new SSEArithmeticDecoder("subpd", ADDR_V, pd_mode, ADDR_W, pd_mode, RTLOP_SUB), + new SSEInstructionDecoder("minpd", ADDR_V, pd_mode, ADDR_W, pd_mode), + new SSEArithmeticDecoder("divpd", ADDR_V, pd_mode, ADDR_W, pd_mode, RTLOP_SDIV), + new SSEInstructionDecoder("maxpd", ADDR_V, pd_mode, ADDR_W, pd_mode), + /* 60 */ + new SSEInstructionDecoder("punpcklbw", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("punpcklwd", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("punpckldq", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("packsswb", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("pcmpgtb", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("pcmpgtw", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("pcmpgtd", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("packuswb", ADDR_V, dq_mode, ADDR_W, dq_mode), + /* 68 */ + new SSEInstructionDecoder("punpckhbw", ADDR_P, dq_mode, ADDR_Q, dq_mode), + new SSEInstructionDecoder("punpckhwd", ADDR_P, dq_mode, ADDR_Q, dq_mode), + new SSEInstructionDecoder("punpckhdq", ADDR_P, dq_mode, ADDR_Q, dq_mode), + new SSEInstructionDecoder("packssdw", ADDR_P, dq_mode, ADDR_Q, dq_mode), + new SSEInstructionDecoder("punpcklqdq", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("punpckhqdq", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEMoveDecoder("movd", ADDR_V, dq_mode, ADDR_E, d_mode), + new SSEMoveDecoder("movdqa", ADDR_V, dq_mode, ADDR_W, dq_mode), + /* 70 */ + new SSEInstructionDecoder("pshufd", ADDR_V, dq_mode, ADDR_W, dq_mode, ADDR_I, b_mode), + new GRPDecoder(null, 22), + new GRPDecoder(null, 23), + new GRPDecoder(null, 24), + new SSEInstructionDecoder("pcmpeqb", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("pcmpeqw", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("pcmpeqd", ADDR_V, dq_mode, ADDR_W, dq_mode), + null, + /* 78 */ + null, + null, + null, + null, + null, + null, + new SSEMoveDecoder("movd", ADDR_E, d_mode, ADDR_V, dq_mode), + new SSEMoveDecoder("movdqa", ADDR_W, dq_mode, ADDR_V, dq_mode), + /* 80 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 88 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 90 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* 98 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* a0 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* a8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* b0 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* b8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* c0 */ + null, + null, + new SSEInstructionDecoder("cmppd", ADDR_V, pd_mode, ADDR_W, pd_mode, ADDR_I, b_mode), + null, + new SSEInstructionDecoder("pinsrw", ADDR_V, dq_mode, ADDR_E, d_mode, ADDR_I, b_mode), + new SSEInstructionDecoder("pextrw", ADDR_G, d_mode, ADDR_V, dq_mode, ADDR_I, b_mode), + new SSEInstructionDecoder("shufpd", ADDR_V, pd_mode, ADDR_W, pd_mode, ADDR_I, b_mode), + null, + /* c8 */ + null, + null, + null, + null, + null, + null, + null, + null, + /* d0 */ + null, + new SSEShiftDecoder("psrlw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SRL), + new SSEShiftDecoder("psrld", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SRL), + new SSEShiftDecoder("psrlq", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SRL), + new SSEArithmeticDecoder("paddq", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_ADD), + new SSEArithmeticDecoder("pmullw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SMUL), + new SSEMoveDecoder("movq", ADDR_W, q_mode, ADDR_V, q_mode), + new SSEMoveDecoder("pmovmskb", ADDR_G, d_mode, ADDR_V, dq_mode), + /* d8 */ + new SSEArithmeticDecoder("psubusb", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SUB), + new SSEArithmeticDecoder("psubusw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SUB), + new SSEInstructionDecoder("pminub", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSELogicalDecoder("pand", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_AND), + new SSEArithmeticDecoder("paddusb", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_ADD), + new SSEArithmeticDecoder("paddusw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_ADD), + new SSEInstructionDecoder("pmaxub", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSELogicalDecoder("pandn", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_AND), + /* e0 */ + new SSEInstructionDecoder("pavgb", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("psraw", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("psrad", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEInstructionDecoder("pavgw", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSEArithmeticDecoder("pmulhuw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_UMUL), + new SSEArithmeticDecoder("pmulhw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SMUL), + new SSEInstructionDecoder("cvttpd2dq", ADDR_V, dq_mode, ADDR_W, pd_mode), + new SSEMoveDecoder("movntdq", ADDR_W, dq_mode, ADDR_V, dq_mode), + /* e8 */ + new SSEArithmeticDecoder("psubusb", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SUB), + new SSEArithmeticDecoder("psubusw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SUB), + new SSEInstructionDecoder("pminsw", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSELogicalDecoder("por", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_OR), + new SSEArithmeticDecoder("paddsb", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_ADD), + new SSEArithmeticDecoder("paddsw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_ADD), + new SSEInstructionDecoder("pmaxsw", ADDR_V, dq_mode, ADDR_W, dq_mode), + new SSELogicalDecoder("pxor", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_XOR), + /* f0 */ + null, + new SSEShiftDecoder("psllw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SLL), + new SSEShiftDecoder("pslld", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SLL), + new SSEShiftDecoder("psllq", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SLL), + new SSEArithmeticDecoder("pmuludq", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_UMUL), + new SSEArithmeticDecoder("pmaddwd", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_ADD), + new SSEArithmeticDecoder("psadbw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_ADD), + new SSEMoveDecoder("maskmovdqu", ADDR_V, dq_mode, ADDR_W, dq_mode), + /* f8 */ + new SSEArithmeticDecoder("psubb", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SUB), + new SSEArithmeticDecoder("psubw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SUB), + new SSEArithmeticDecoder("psubd", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SUB), + new SSEArithmeticDecoder("psubq", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_SUB), + new SSEArithmeticDecoder("paddb", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_ADD), + new SSEArithmeticDecoder("paddw", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_ADD), + new SSEArithmeticDecoder("paddd", ADDR_V, dq_mode, ADDR_W, dq_mode, RTLOP_ADD), + null + }; + + public void decode(InstructionVisitor visitor) { + int enter_instruction = 0; + Instruction instr = null; + visitor.prologue(); + InstructionDecoder instrDecoder = null; + try { + byteIndex = 0; + int len = 0; + int instrStartIndex = 0; + + while(len < code.length) { + int prefixes = 0; + instrStartIndex = byteIndex; + + //check if there is any prefix + prefixes = getPrefixes(); + int segmentOverride = 1; //get segment override prefix + + if (code[byteIndex] == 0xc8) + enter_instruction = 1; + else + enter_instruction = 0; + + //Read opcode + int opcode = InstructionDecoder.readByte(code, byteIndex); + byteIndex++; + + if (opcode == 0x0f) { + opcode = InstructionDecoder.readByte(code, byteIndex); + byteIndex++; + + //SSE: SSE instructions have reserved use of 0xF2, 0xF3, 0x66 prefixes + if ((prefixes & PREFIX_REPNZ) != 0) { + instrDecoder = twoBytePrefixF2Table[opcode]; + } else if ((prefixes & PREFIX_REPZ) != 0) { + instrDecoder = twoBytePrefixF3Table[opcode]; + } else if ((prefixes & PREFIX_DATA) != 0) { + instrDecoder = twoBytePrefix66Table[opcode]; + } else { + instrDecoder = twoByteTable[opcode]; + } + + } else { + instrDecoder = oneByteTable[opcode]; + } + if (instrDecoder != null) { + instr = instrDecoder.decode(code, byteIndex, instrStartIndex, segmentOverride, prefixes, factory); + visitor.visit(startPc + len, instr); + len = instrDecoder.getCurrentIndex(); + } + else { + len += 1; + } + byteIndex = len; + } + } catch (Exception exp) { + visitor.epilogue(); + } + } + + private int getPrefixes() { + int prefixByte = 0; + int prefixes = 0; + boolean isPrefix = true; + while (isPrefix) { + prefixByte = InstructionDecoder.readByte(code, byteIndex); + + switch (prefixByte) { + case 0xf3: + prefixes |= PREFIX_REPZ; + break; + case 0xf2: + prefixes |= PREFIX_REPNZ; + break; + case 0xf0: + prefixes |= PREFIX_LOCK; + break; + case 0x2e: + prefixes |= PREFIX_CS; + break; + case 0x36: + prefixes |= PREFIX_SS; + break; + case 0x3e: + prefixes |= PREFIX_DS; + break; + case 0x26: + prefixes |= PREFIX_ES; + break; + case 0x64: + prefixes |= PREFIX_FS; + break; + case 0x65: + prefixes |= PREFIX_GS; + break; + case 0x66: + prefixes |= PREFIX_DATA; + break; + case 0x67: + prefixes |= PREFIX_ADR; + break; + case 0x9b: + prefixes |= PREFIX_FWAIT; + break; + default: + isPrefix = false; + break; + } + if(isPrefix) + byteIndex++; + } + return prefixes; + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPArithmeticInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPArithmeticInstruction.java new file mode 100644 index 00000000000..b08415d22df --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPArithmeticInstruction.java @@ -0,0 +1,85 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86FPArithmeticInstruction extends X86FPInstruction + implements ArithmeticInstruction { + final private int operation; //RTL operation + final private Operand operand1; + final private Operand operand2; + final private String description; + + public X86FPArithmeticInstruction(String name, int operation, Operand op1, Operand op2, int size, int prefixes) { + super(name, size, prefixes); + this.operation = operation; + this.operand1 = op1; + this.operand2 = op2; + description = initDescription(); + } + + protected String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + if (operand1 != null) { + buf.append(getOperandAsString(operand1)); + } + if (operand2 != null) { + buf.append(comma); + buf.append(getOperandAsString(operand2)); + } + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } + + public Operand getArithmeticDestination() { + return operand1; + } + public Operand getOperand1() { + return operand1; + } + + public Operand getOperand2() { + return operand2; + } + + public Operand[] getArithmeticSources() { + return (new Operand[] { operand1, operand2}); + } + + public int getOperation() { + return operation; + } + + public boolean isArithmetic() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPInstruction.java new file mode 100644 index 00000000000..29d9ae5069a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPInstruction.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86FPInstruction extends X86Instruction { + + final private Operand operand1; + final private String description; + + public X86FPInstruction(String name, int size, int prefixes) { + super(name, size, prefixes); + this.operand1 = null; + description = initDescription(); + } + + public X86FPInstruction(String name, Operand op1, int size, int prefixes) { + super(name, size, prefixes); + this.operand1 = op1; + description = initDescription(); + } + + protected String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + if (operand1 != null) { + buf.append(getOperandAsString(operand1)); + } + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } + + public Operand getOperand1() { + return operand1; + } + + public boolean isFloat() { + return true; + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPLoadInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPLoadInstruction.java new file mode 100644 index 00000000000..6237c0093f7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPLoadInstruction.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86FPLoadInstruction extends X86FPInstruction { + + final private Operand source; + + public X86FPLoadInstruction(String name, Operand operand, int size, int prefixes) { + super(name, size, prefixes); + this.source = operand; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + buf.append(source.toString()); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPStoreInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPStoreInstruction.java new file mode 100644 index 00000000000..76b334e83e7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FPStoreInstruction.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86FPStoreInstruction extends X86FPInstruction { + + final private Operand dest; + + public X86FPStoreInstruction(String name, Operand op, int size, int prefixes) { + super(name, size, prefixes); + this.dest = op; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + buf.append(dest.toString()); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FloatRegister.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FloatRegister.java new file mode 100644 index 00000000000..c35e6d7c926 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FloatRegister.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.Register; +import sun.jvm.hotspot.utilities.Assert; + +public class X86FloatRegister extends Register { + + public X86FloatRegister(int number) { + super(number); + } + + public int getNumber() { + return number; + } + + public int getNumberOfRegisters() { + return X86FloatRegisters.getNumRegisters(); + } + + public boolean isFloat() { + return true; + } + + public boolean isFramePointer() { + return false; + } + + public boolean isStackPointer() { + return false; + } + + public boolean isValid() { + return number >= 0 && number < X86FloatRegisters.getNumRegisters(); + } + + public String toString() { + return X86FloatRegisters.getRegisterName(number); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FloatRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FloatRegisters.java new file mode 100644 index 00000000000..906dd72b7bd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86FloatRegisters.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.utilities.Assert; + +public class X86FloatRegisters { + + public static int getNumRegisters() { + return NUM_REGISTERS; + } + + public static X86FloatRegister getRegister(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid float register number!"); + } + return registers[regNum]; + } + + public static String getRegisterName(int i) { + return "ST(" + i + ")"; + } + + public static final X86FloatRegister ST0; + public static final X86FloatRegister ST1; + public static final X86FloatRegister ST2; + public static final X86FloatRegister ST3; + public static final X86FloatRegister ST4; + public static final X86FloatRegister ST5; + public static final X86FloatRegister ST6; + public static final X86FloatRegister ST7; + + public static final int NUM_REGISTERS = 8; + + private static final X86FloatRegister registers[]; + + static { + ST0 = new X86FloatRegister(0); + ST1 = new X86FloatRegister(1); + ST2 = new X86FloatRegister(2); + ST3 = new X86FloatRegister(3); + ST4 = new X86FloatRegister(4); + ST5 = new X86FloatRegister(5); + ST6 = new X86FloatRegister(6); + ST7 = new X86FloatRegister(7); + registers = (new X86FloatRegister[] { + ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7 + }); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86GeneralInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86GeneralInstruction.java new file mode 100644 index 00000000000..50792c5219b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86GeneralInstruction.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86GeneralInstruction extends X86Instruction { + final private Operand operand1; + final private Operand operand2; + final private Operand operand3; + final private String description; + + public X86GeneralInstruction(String name, Operand op1, Operand op2, Operand op3, int size, int prefixes) { + super(name, size, prefixes); + this.operand1 = op1; + this.operand2 = op2; + this.operand3 = op3; + description = initDescription(); + } + public X86GeneralInstruction(String name, Operand op1, Operand op2, int size, int prefixes) { + this(name, op1, op2, null, size, prefixes); + } + + public X86GeneralInstruction(String name, Operand op1, int size, int prefixes) { + this(name, op1, null, null, size, prefixes); + } + + protected String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + if (operand1 != null) { + buf.append(getOperandAsString(operand1)); + } + if (operand2 != null) { + buf.append(comma); + buf.append(getOperandAsString(operand2)); + } + if(operand3 != null) { + buf.append(comma); + buf.append(getOperandAsString(operand3)); + } + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } + + public Operand getOperand1() { + return operand1; + } + + public Operand getOperand2() { + return operand2; + } + + public Operand getOperand3() { + return operand3; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Helper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Helper.java new file mode 100644 index 00000000000..145400028f7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Helper.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + + +public class X86Helper implements CPUHelper { + public Disassembler createDisassembler(long startPc, byte[] code) { + return new X86Disassembler(startPc, code); + } + + public Register getIntegerRegister(int num) { + return X86Registers.getRegister32(num); + } + + public Register getFloatRegister(int num) { + return X86FloatRegisters.getRegister(num); + } + + public Register getStackPointer() { + return X86Registers.ESP; + } + + public Register getFramePointer() { + return X86Registers.EBP; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86IllegalInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86IllegalInstruction.java new file mode 100644 index 00000000000..672fc34bdd1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86IllegalInstruction.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.SymbolFinder; + +public final class X86IllegalInstruction extends X86Instruction { + final private String description; + + public X86IllegalInstruction() { + super("illegal", 1, 0); + description = "bad opcode"; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } + + public boolean isIllegal() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Instruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Instruction.java new file mode 100644 index 00000000000..8e317873a57 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Instruction.java @@ -0,0 +1,128 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public abstract class X86Instruction implements Instruction, X86Opcodes { + final private String name; + final private int size; + final private int prefixes; + + public X86Instruction(String name, int size, int prefixes) { + this.name = name; + this.size = size; + this.prefixes = prefixes; + } + + public abstract String asString(long currentPc, SymbolFinder symFinder); + + public String getName() { + return name; + } + + public String getPrefixString() { + StringBuffer buf = new StringBuffer(); + if ((prefixes & PREFIX_REPZ) != 0) + buf.append("repz "); + if ((prefixes & PREFIX_REPNZ) != 0) + buf.append("repnz "); + if ((prefixes & PREFIX_LOCK) != 0) + buf.append("lock "); + + return buf.toString(); + } + + protected String getOperandAsString(Operand op) { + StringBuffer buf = new StringBuffer(); + if ((op instanceof Register) || (op instanceof Address)) { + buf.append(op.toString()); + } else { + Number number = ((Immediate)op).getNumber(); + buf.append("0x"); + buf.append(Integer.toHexString(number.intValue())); + } + return buf.toString(); + } + + public int getSize() { + return size; + } + + public boolean isArithmetic() { + return false; + } + + public boolean isBranch() { + return false; + } + + public boolean isCall() { + return false; + } + + public boolean isFloat() { + return false; + } + + public boolean isIllegal() { + return false; + } + + public boolean isLoad() { + return false; + } + + public boolean isLogical() { + return false; + } + + public boolean isMove() { + return false; + } + + public boolean isReturn() { + return false; + } + + public boolean isShift() { + return false; + } + + public boolean isStore() { + return false; + } + + public boolean isTrap() { + return false; + } + + public boolean isNoop() { + return false; + } + + protected static String comma = ", "; + protected static String spaces = "\t"; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86InstructionFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86InstructionFactory.java new file mode 100644 index 00000000000..2cab80e7031 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86InstructionFactory.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public interface X86InstructionFactory { + public X86Instruction newCallInstruction(String name, Address addr, int size, int prefixes); + + public X86Instruction newJmpInstruction(String name, Address addr, int size, int prefixes); + + public X86Instruction newCondJmpInstruction(String name, X86PCRelativeAddress addr, int size, int prefixes); + + public X86Instruction newMoveInstruction(String name, X86Register rd, ImmediateOrRegister oSrc, int size, int prefixes); + + public X86Instruction newMoveLoadInstruction(String name, X86Register op1, Address op2, int dataType, int size, int prefixes); + + public X86Instruction newMoveStoreInstruction(String name, Address op1, X86Register op2, int dataType, int size, int prefixes); + + public X86Instruction newArithmeticInstruction(String name, int rtlOperation, Operand op1, Operand op2, Operand op3, int size, int prefixes); + + public X86Instruction newArithmeticInstruction(String name, int rtlOperation, Operand op1, Operand op2, int size, int prefixes); + + public X86Instruction newLogicInstruction(String name, int rtlOperation, Operand op1, Operand op2, int size, int prefixes); + + public X86Instruction newBranchInstruction(String name, X86PCRelativeAddress addr, int size, int prefixes); + + public X86Instruction newShiftInstruction(String name, int rtlOperation, Operand op1, ImmediateOrRegister op2, int size, int prefixes); + + public X86Instruction newRotateInstruction(String name, Operand op1, ImmediateOrRegister op2, int size, int prefixes); + + public X86Instruction newFPLoadInstruction(String name, Operand op, int size, int prefixes); + + public X86Instruction newFPStoreInstruction(String name, Operand op, int size, int prefixes); + + public X86Instruction newFPArithmeticInstruction(String name, int rtlOperation, Operand op1, Operand op2, int size, int prefixes); + + public X86Instruction newGeneralInstruction(String name, Operand op1, Operand op2, Operand op3, int size, int prefixes); + + public X86Instruction newGeneralInstruction(String name, Operand op1, Operand op2, int size, int prefixes); + + public X86Instruction newGeneralInstruction(String name, Operand op1, int size, int prefixes); + + public X86Instruction newIllegalInstruction(); + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86InstructionFactoryImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86InstructionFactoryImpl.java new file mode 100644 index 00000000000..0661ad60877 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86InstructionFactoryImpl.java @@ -0,0 +1,108 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86InstructionFactoryImpl implements X86InstructionFactory { + + public X86Instruction newCallInstruction(String name, Address addr, int size, int prefixes) { + return new X86CallInstruction(name, addr, size, prefixes); + } + + public X86Instruction newJmpInstruction(String name, Address addr, int size, int prefixes) { + return new X86JmpInstruction(name, addr, size, prefixes); + } + + public X86Instruction newCondJmpInstruction(String name, X86PCRelativeAddress addr, int size, int prefixes) { + return new X86CondJmpInstruction(name, addr, size, prefixes); + } + + public X86Instruction newMoveInstruction(String name, X86Register rd, ImmediateOrRegister oSrc, int size, int prefixes) { + return new X86MoveInstruction(name, rd, oSrc, size, prefixes); + } + + public X86Instruction newMoveLoadInstruction(String name, X86Register op1, Address op2, int dataType, int size, int prefixes) { + return new X86MoveLoadInstruction(name, op1, op2, dataType, size, prefixes); + } + + public X86Instruction newMoveStoreInstruction(String name, Address op1, X86Register op2, int dataType, int size, int prefixes) { + return new X86MoveStoreInstruction(name, op1, op2, dataType, size, prefixes); + } + + public X86Instruction newArithmeticInstruction(String name, int rtlOperation, Operand op1, Operand op2, Operand op3, int size, int prefixes) { + return new X86ArithmeticInstruction(name, rtlOperation, op1, op2, op3, size, prefixes); + } + + public X86Instruction newArithmeticInstruction(String name, int rtlOperation, Operand op1, Operand op2, int size, int prefixes) { + return new X86ArithmeticInstruction(name, rtlOperation, op1, op2, size, prefixes); + } + + + public X86Instruction newLogicInstruction(String name, int rtlOperation, Operand op1, Operand op2, int size, int prefixes) { + return new X86LogicInstruction(name, rtlOperation, op1, op2, size, prefixes); + } + + public X86Instruction newBranchInstruction(String name, X86PCRelativeAddress addr, int size, int prefixes) { + return new X86BranchInstruction(name, addr, size, prefixes); + } + + public X86Instruction newShiftInstruction(String name, int rtlOperation, Operand op1, ImmediateOrRegister op2, int size, int prefixes) { + return new X86ShiftInstruction(name, rtlOperation, op1, op2, size, prefixes); + } + + public X86Instruction newRotateInstruction(String name, Operand op1, ImmediateOrRegister op2, int size, int prefixes) { + return new X86RotateInstruction(name, op1, op2, size, prefixes); + } + + public X86Instruction newFPLoadInstruction(String name, Operand op, int size, int prefixes) { + return new X86FPLoadInstruction(name, op, size, prefixes); + } + + public X86Instruction newFPStoreInstruction(String name, Operand op, int size, int prefixes) { + return new X86FPStoreInstruction(name, op, size, prefixes); + } + + public X86Instruction newFPArithmeticInstruction(String name, int rtlOperation, Operand op1, Operand op2, int size, int prefixes) { + return new X86FPArithmeticInstruction(name, rtlOperation, op1, op2, size, prefixes); + } + + public X86Instruction newGeneralInstruction(String name, Operand op1, Operand op2, Operand op3, int size, int prefixes) { + return new X86GeneralInstruction(name, op1, op2, op3, size, prefixes); + } + + public X86Instruction newGeneralInstruction(String name, Operand op1, Operand op2, int size, int prefixes) { + return new X86GeneralInstruction(name, op1, op2, size, prefixes); + } + + public X86Instruction newGeneralInstruction(String name, Operand op1, int size, int prefixes) { + return new X86GeneralInstruction(name, op1, size, prefixes); + } + + public X86Instruction newIllegalInstruction() { + return new X86IllegalInstruction(); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86JmpInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86JmpInstruction.java new file mode 100644 index 00000000000..bf1c2f8e690 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86JmpInstruction.java @@ -0,0 +1,69 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86JmpInstruction extends X86Instruction + implements BranchInstruction { + final private Address addr; + + public X86JmpInstruction(String name, Address addr, int size, int prefixes) { + super(name, size, prefixes); + this.addr = addr; + if(addr instanceof X86PCRelativeAddress) { + ((X86PCRelativeAddress)addr).setInstructionSize(getSize()); + } + } + + public String asString(long currentPc, SymbolFinder symFinder) { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + long address; + if(addr instanceof X86PCRelativeAddress) { + long disp = ((X86PCRelativeAddress)addr).getDisplacement(); + address = disp + currentPc; + buf.append(symFinder.getSymbolFor(address)); + } + else { + buf.append(addr.toString()); + } + return buf.toString(); + } + + public Address getBranchDestination() { + return addr; + } + + public boolean isBranch() { + return true; + } + + public boolean isConditional() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86LogicInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86LogicInstruction.java new file mode 100644 index 00000000000..79c50f79806 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86LogicInstruction.java @@ -0,0 +1,70 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86LogicInstruction extends X86Instruction + implements LogicInstruction { + final private Operand operand1; + final private Operand operand2; + final private int operation; + + public X86LogicInstruction(String name, int operation, Operand op1, Operand op2, int size, int prefixes) { + super(name, size, prefixes); + this.operation = operation; + this.operand1 = op1; + this.operand2 = op2; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + buf.append(getOperandAsString(operand1)); + if(operand2 != null) { + buf.append(comma); + buf.append(getOperandAsString(operand2)); + } + return buf.toString(); + } + + public Operand getLogicDestination() { + return operand1; + } + + public Operand[] getLogicSources() { + return (new Operand[] { operand2 }); + } + + public int getOperation() { + return operation; + } + + public boolean isLogic() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MMXRegister.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MMXRegister.java new file mode 100644 index 00000000000..6ba1f087328 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MMXRegister.java @@ -0,0 +1,40 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86MMXRegister extends X86Register { + + public X86MMXRegister(int num, String name) { + super(num, name); + } + public int getNumberOfRegisters() { + return X86MMXRegisters.getNumberOfRegisters(); + } + public String toString() { + return name; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MMXRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MMXRegisters.java new file mode 100644 index 00000000000..085e92fe805 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MMXRegisters.java @@ -0,0 +1,80 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.utilities.*; + +/* 8 64-bit registers called MMX registers*/ + +public class X86MMXRegisters { + + public static final int NUM_MMX_REGISTERS = 8; + + public static final X86MMXRegister MM0; + public static final X86MMXRegister MM1; + public static final X86MMXRegister MM2; + public static final X86MMXRegister MM3; + public static final X86MMXRegister MM4; + public static final X86MMXRegister MM5; + public static final X86MMXRegister MM6; + public static final X86MMXRegister MM7; + + private static X86MMXRegister mmxRegisters[]; + + static { + //MMX registers + MM0 = new X86MMXRegister(0, "%mm0"); + MM1 = new X86MMXRegister(1, "%mm1"); + MM2 = new X86MMXRegister(2, "%mm2"); + MM3 = new X86MMXRegister(3, "%mm3"); + MM4 = new X86MMXRegister(4, "%mm4"); + MM5 = new X86MMXRegister(5, "%mm5"); + MM6 = new X86MMXRegister(6, "%mm6"); + MM7 = new X86MMXRegister(7, "%mm7"); + + mmxRegisters = (new X86MMXRegister[] { + MM0, MM1, MM2, MM3, MM4, MM5, MM6, MM7 + }); + } + + public static int getNumberOfRegisters() { + return NUM_MMX_REGISTERS; + } + + //Return the register name + public static String getRegisterName(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_MMX_REGISTERS, "invalid MMX register number!"); + } + return mmxRegisters[regNum].toString(); + } + + public static X86MMXRegister getRegister(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_MMX_REGISTERS, "invalid MMX register number!"); + } + return mmxRegisters[regNum]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MemoryIndirectAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MemoryIndirectAddress.java new file mode 100644 index 00000000000..afe496c7007 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MemoryIndirectAddress.java @@ -0,0 +1,46 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.asm.x86.*; + +public class X86MemoryIndirectAddress extends IndirectAddress { + + private long value; + + public X86MemoryIndirectAddress(long value) { + this.value = value; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("*"); + buf.append("["); + buf.append(Long.toHexString(value)); + buf.append(']'); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MemoryInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MemoryInstruction.java new file mode 100644 index 00000000000..b4ed561a31e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MemoryInstruction.java @@ -0,0 +1,66 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public abstract class X86MemoryInstruction extends X86Instruction + implements MemoryInstruction { + final protected Address address; + final protected X86Register register; + final protected int dataType; + final protected String description; + + public X86MemoryInstruction(String name, Address address, X86Register register, int dataType, int size, int prefixes) { + super(name, size, prefixes); + this.address = address; + this.register = register; + this.dataType = dataType; + description = initDescription(); + } + + protected String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + buf.append(register.toString()); + buf.append(comma); + buf.append(address.toString()); + return buf.toString(); + } + + public String asString(long currentPc, SymbolFinder symFinder) { + return description; + } + + public int getDataType() { + return dataType; + } + + public boolean isConditional() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MoveInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MoveInstruction.java new file mode 100644 index 00000000000..5ef18d975e7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MoveInstruction.java @@ -0,0 +1,78 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86MoveInstruction extends X86Instruction + implements MoveInstruction, RTLOperations { + private ImmediateOrRegister source; + private X86Register destination; + + public X86MoveInstruction(String name, X86Register rd, ImmediateOrRegister oSrc, int size, int prefixes) { + super(name, size, prefixes); + this.source = oSrc; + this.destination = rd; + } + public String asString(long currentPc, SymbolFinder symFinder) { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + buf.append(destination.toString()); + buf.append(comma); + buf.append(getSourceString()); + return buf.toString(); + } + + protected String getSourceString() { + StringBuffer buf = new StringBuffer(); + if ((source instanceof Register)) { + buf.append(source.toString()); + } else { + Number number = ((Immediate)source).getNumber(); + buf.append("0x"); + buf.append(Integer.toHexString(number.intValue())); + } + return buf.toString(); + } + + public ImmediateOrRegister getMoveSource() { + return source; + } + + public Register getMoveDestination() { + return destination; + } + + // for condition moves + public boolean isConditional() { + return false; + } + + public boolean isMove() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MoveLoadInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MoveLoadInstruction.java new file mode 100644 index 00000000000..acdcbce3f84 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MoveLoadInstruction.java @@ -0,0 +1,60 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86MoveLoadInstruction extends X86MemoryInstruction + implements LoadInstruction { + public X86MoveLoadInstruction(String name, X86Register register, Address address, int dataType, int size, int prefixes) { + super(name, address, register, dataType, size, prefixes); + } + + public boolean isLoad() { + return true; + } + + public Register[] getLoadDestinations() { + Register[] destinations = new Register[1]; + destinations[0] = register; + return destinations; + } + + protected String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + buf.append(register.toString()); + buf.append(comma); + buf.append(address.toString()); + return buf.toString(); + } + + public Address getLoadSource() { + return address; + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MoveStoreInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MoveStoreInstruction.java new file mode 100644 index 00000000000..b2315703ac6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86MoveStoreInstruction.java @@ -0,0 +1,65 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86MoveStoreInstruction extends X86MemoryInstruction + implements StoreInstruction { + final protected Register[] storeSources; + + public X86MoveStoreInstruction(String name, Address address, X86Register register, int dataType, int size, int prefixes) { + super(name, address, register, dataType, size, prefixes); + storeSources = new Register[1]; + storeSources[0] = register; + } + + protected String initDescription() { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + buf.append(address.toString()); + buf.append(comma); + buf.append(register.toString()); + return buf.toString(); + } + + public int getDataType() { + return dataType; + } + + public Address getStoreDestination() { + return address; + } + + public Register[] getStoreSources() { + return storeSources; + } + + public boolean isStore() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Opcodes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Opcodes.java new file mode 100644 index 00000000000..06cc4651769 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Opcodes.java @@ -0,0 +1,127 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +//Please refer to IA-32 Intel Architecture Software Developer's Manual Volume 2 +//APPENDIX A - A.1. + +public interface X86Opcodes + extends RTLDataTypes, RTLOperations { + public static final int b_mode = 1; + public static final int v_mode = 2; + public static final int w_mode = 3; + public static final int d_mode = 4; + public static final int p_mode = 5; + + public static final int dq_mode = 6; //SSE: double-quadword + public static final int pi_mode = 7; //SSE: quadword MMX register + public static final int ps_mode = 8; //SSE: 128bit single precision floating point data + public static final int pd_mode = 9; //SSE: 128bit double precision floating point data + public static final int sd_mode = 10; //SSE: 128bit scalar double precision floating point data + public static final int q_mode = 11; //SSE: quadword + public static final int ss_mode = 12; //SSE: scalar element of 128bit floating data + public static final int si_mode = 13; //SSE: doubleword integer register (e.g. eax) + public static final int s_mode = 14; //SSE: 6 byte pseudo descriptor + + public static final int INVALID_OPERANDTYPE = -1; + + public static final int EAX = 0; + public static final int ECX = 1; + public static final int EDX = 2; + public static final int EBX = 3; + public static final int ESP = 4; + public static final int EBP = 5; + public static final int ESI = 6; + public static final int EDI = 7; + + public static final int AX = 8; + public static final int CX = 9; + public static final int DX = 10; + public static final int BX = 11; + public static final int SP = 12; + public static final int BP = 13; + public static final int SI = 14; + public static final int DI = 15; + + public static final int AL = 16; + public static final int CL = 17; + public static final int DL = 18; + public static final int BL = 19; + public static final int AH = 20; + public static final int CH = 21; + public static final int DH = 22; + public static final int BH = 23; + + public static final int ES = 24; + public static final int CS = 25; + public static final int SS = 26; + public static final int DS = 27; + public static final int FS = 28; + public static final int GS = 29; + + //Addressing modes + public static final int ADDR_E = 1; + public static final int ADDR_I = 2; + public static final int ADDR_DIR = 3; + public static final int ADDR_J = 4; + public static final int ADDR_G = 5; + public static final int ADDR_REG = 6; + public static final int ADDR_ESDI = 7; + public static final int ADDR_DSSI = 8; + public static final int ADDR_SEG = 9; + public static final int ADDR_OFF = 10; + public static final int INDIR_REG = 11; + public static final int ADDR_INDIR_E = 12; + public static final int ADDR_R = 13; //mod field selects a register + public static final int ADDR_C = 14; //reg field selects a control register + public static final int ADDR_D = 15; //reg field selects debug register + public static final int ADDR_T = 16; //reg field selects test register + public static final int ADDR_M = 17; //modR/M refer only to memory + public static final int ADDR_FPREG = 18; + //SSE + public static final int ADDR_W = 19; //modR/M: either a 128 bit XMM register or memory + public static final int ADDR_Q = 20; //modR/M: either a 128 bit MMX register or memory + public static final int ADDR_V = 21; //reg field of modR/M selects a 128-bit XMM register + public static final int ADDR_P = 22; //reg field of modR/M selects a 64-bit MMX register + + public static final int INVALID_ADDRMODE = -1; + + //Refer to chapter 2 - Instruction Format + //Prefix codes + public static final int PREFIX_REPZ = 1; + public static final int PREFIX_REPNZ = 2; + public static final int PREFIX_LOCK = 4; + public static final int PREFIX_CS = 8; + public static final int PREFIX_SS = 0x10; + public static final int PREFIX_DS = 0x20; + public static final int PREFIX_ES = 0x40; + public static final int PREFIX_FS = 0x80; + public static final int PREFIX_GS = 0x100; + public static final int PREFIX_DATA = 0x200; + public static final int PREFIX_ADR = 0x400; + public static final int PREFIX_FWAIT = 0x800; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86PCRelativeAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86PCRelativeAddress.java new file mode 100644 index 00000000000..8691a752dda --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86PCRelativeAddress.java @@ -0,0 +1,54 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +// address is specified as an offset from current PC + +public class X86PCRelativeAddress extends PCRelativeAddress { + private int instrSize; + + public X86PCRelativeAddress(long disp) { + super(disp); + } + + + public void setInstructionSize(int size) { + instrSize = size; + } + + public String toString() { + long displacement = this.getDisplacement(); + return new Long(displacement).toString(); + } + + //In intel assembly the displacement is from start of next instruction. + //So we add the size of current instruction to get the correct disp. + public long getDisplacement() { + long displacement = super.getDisplacement() + (long)instrSize; + return displacement; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Register.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Register.java new file mode 100644 index 00000000000..b25acc0d0e3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Register.java @@ -0,0 +1,53 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86Register extends Register { + protected String name; + public X86Register(int num, String name) { + super(num); + this.name = name; + } + public int getNumberOfRegisters() { + return X86Registers.getNumberOfRegisters(); + } + public String toString() { + return name; + } + public boolean isFramePointer() { + return number == 5; //ebp + } + public boolean isStackPointer() { + return number == 4; //esp + } + public boolean isFloat() { + return false; + } + public boolean isSegmentPointer() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RegisterDirectAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RegisterDirectAddress.java new file mode 100644 index 00000000000..578f51646b9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RegisterDirectAddress.java @@ -0,0 +1,40 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.Address; +import sun.jvm.hotspot.asm.BaseIndexScaleDispAddress; + +public class X86RegisterDirectAddress extends Address { + final private X86Register base; + + public X86RegisterDirectAddress(X86Register base) { + this.base = base; + } + + public String toString() { + return base.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RegisterIndirectAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RegisterIndirectAddress.java new file mode 100644 index 00000000000..82be2f6c08e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RegisterIndirectAddress.java @@ -0,0 +1,86 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.Address; +import sun.jvm.hotspot.asm.BaseIndexScaleDispAddress; + +public class X86RegisterIndirectAddress extends BaseIndexScaleDispAddress { + + final private X86SegmentRegister segReg; + + public X86RegisterIndirectAddress(X86SegmentRegister segReg, X86Register base, X86Register index, long disp, int scale) { + super(base, index, disp, scale); + this.segReg = segReg; + } + + public X86RegisterIndirectAddress(X86SegmentRegister segReg, X86Register base, X86Register index, long disp) { + super(base, index, disp, -1); + this.segReg = segReg; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + if(segReg != null) { + buf.append(segReg.toString()); + buf.append(":"); + } + + long disp = getDisplacement(); + if(disp != 0) + buf.append(disp); + + sun.jvm.hotspot.asm.Register base = getBase(); + sun.jvm.hotspot.asm.Register index = getIndex(); + int scaleVal = getScale(); + scaleVal = 1 << scaleVal; + + if( (base != null) || (index != null) || (scaleVal > 1) ) + buf.append('['); + + if(base != null) { + buf.append(base.toString()); + if(index != null) { + buf.append("+"); + buf.append(index.toString()); + } + } + else { + if(index != null) { + buf.append(index.toString()); + } + } + + if (scaleVal > 1) { + buf.append(" * "); + buf.append(Integer.toString(scaleVal)); + } + + if( (base != null) || (index != null) || (scaleVal > 1) ) + buf.append(']'); + + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RegisterPart.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RegisterPart.java new file mode 100644 index 00000000000..4ea9c61ba3b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RegisterPart.java @@ -0,0 +1,47 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86RegisterPart extends X86Register { + private int startBit; + private int length; + public X86RegisterPart(int num, String name, int startBit, int length) { + super(num, name); + this.startBit = startBit; + this.length = length; + } + public boolean is32BitRegister() { + return ( length == 32); + } + public boolean is16BitRegister() { + return ( length == 16); + } + public boolean is8BitRegister() { + return ( length == 8); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Registers.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Registers.java new file mode 100644 index 00000000000..6630780cc62 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86Registers.java @@ -0,0 +1,134 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.utilities.*; + +public class X86Registers { + public static final int NUM_REGISTERS = 8; + + public static final X86Register EAX; + public static final X86Register ECX; + public static final X86Register EDX; + public static final X86Register EBX; + public static final X86Register ESP; + public static final X86Register EBP; + public static final X86Register ESI; + public static final X86Register EDI; + + public static final X86Register AX; + public static final X86Register CX; + public static final X86Register DX; + public static final X86Register BX; + public static final X86Register SP; + public static final X86Register BP; + public static final X86Register SI; + public static final X86Register DI; + + public static final X86Register AL; + public static final X86Register CL; + public static final X86Register DL; + public static final X86Register BL; + public static final X86Register AH; + public static final X86Register CH; + public static final X86Register DH; + public static final X86Register BH; + + private static X86Register registers8[]; + private static X86Register registers16[]; + private static X86Register registers32[]; + + static { + EAX = new X86RegisterPart(0, "%eax", 0, 32); + ECX = new X86RegisterPart(1, "%ecx", 0, 32); + EDX = new X86RegisterPart(2, "%edx", 0, 32); + EBX = new X86RegisterPart(3, "%ebx", 0, 32); + ESP = new X86RegisterPart(4, "%esp", 0, 32); + EBP = new X86RegisterPart(5, "%ebp", 0, 32); + ESI = new X86RegisterPart(6, "%esi", 0, 32); + EDI = new X86RegisterPart(7, "%edi", 0, 32); + + AX = new X86RegisterPart(0, "%ax", 0, 16); + CX = new X86RegisterPart(1, "%cx", 0, 16); + DX = new X86RegisterPart(2, "%dx", 0, 16); + BX = new X86RegisterPart(3, "%bx", 0, 16); + SP = new X86RegisterPart(4, "%sp", 0, 16); + BP = new X86RegisterPart(5, "%bp", 0, 16); + SI = new X86RegisterPart(6, "%si", 0, 16); + DI = new X86RegisterPart(7, "%di", 0, 16); + + AL = new X86RegisterPart(0, "%al", 0, 8); + CL = new X86RegisterPart(1, "%cl", 0, 8); + DL = new X86RegisterPart(2, "%dl", 0, 8); + BL = new X86RegisterPart(3, "%bl", 0, 8); + AH = new X86RegisterPart(0, "%ah", 8, 8); + CH = new X86RegisterPart(1, "%ch", 8, 8); + DH = new X86RegisterPart(2, "%dh", 8, 8); + BH = new X86RegisterPart(3, "%bh", 8, 8); + + registers32 = (new X86Register[] { + EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI + }); + registers16 = (new X86Register[] { + AX, CX, DX, BX, SP, BP, SI, DI + }); + registers8 = (new X86Register[] { + AL, CL, DL, BL, AH, CH, DH, BH + }); + } + + public static int getNumberOfRegisters() { + return NUM_REGISTERS; + } + + public static X86Register getRegister8(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid integer register number!"); + } + return registers8[regNum]; + } + + public static X86Register getRegister16(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid integer register number!"); + } + return registers16[regNum]; + } + + public static X86Register getRegister32(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid integer register number!"); + } + return registers32[regNum]; + } + + //Return the 32bit register name + public static String getRegisterName(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_REGISTERS, "invalid integer register number!"); + } + return registers32[regNum].toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RotateInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RotateInstruction.java new file mode 100644 index 00000000000..5e39f727414 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86RotateInstruction.java @@ -0,0 +1,66 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86RotateInstruction extends X86Instruction { + final private Operand operand1; + final private ImmediateOrRegister operand2; + + public X86RotateInstruction(String name, Operand operand1, ImmediateOrRegister operand2, int size, int prefixes) { + super(name, size, prefixes); + this.operand1 = operand1; + this.operand2 = operand2; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + if(operand2 != null) { + if ((operand2 instanceof Register)) { + buf.append(operand2.toString()); + } + else { + Number number = ((Immediate)operand2).getNumber(); + buf.append("0x"); + buf.append(Integer.toHexString(number.intValue())); + } + buf.append(comma); + } + buf.append(getOperandAsString(operand1)); + return buf.toString(); + } + + public Operand getRotateDestination() { + return operand1; + } + + public Operand getRotateSource() { + return operand1; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86SegmentRegister.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86SegmentRegister.java new file mode 100644 index 00000000000..482598181b3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86SegmentRegister.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86SegmentRegister extends X86Register { + + public X86SegmentRegister(int num, String name) { + super(num, name); + } + public int getNumberOfRegisters() { + return X86SegmentRegisters.getNumberOfRegisters(); + } + public String toString() { + return name; + } + public boolean isSegmentPointer() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86SegmentRegisterAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86SegmentRegisterAddress.java new file mode 100644 index 00000000000..2d035cd65cd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86SegmentRegisterAddress.java @@ -0,0 +1,53 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86SegmentRegisterAddress extends IndirectAddress { + private final X86SegmentRegister segment; + private final X86Register offset; + + public X86SegmentRegisterAddress(X86SegmentRegister segment, X86Register offset) { + this.segment = segment; + this.offset = offset; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getSegment().toString()); + buf.append(":"); + buf.append(getOffset().toString()); + return buf.toString(); + } + + public X86SegmentRegister getSegment() { + return segment; + } + + public X86Register getOffset() { + return offset; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86SegmentRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86SegmentRegisters.java new file mode 100644 index 00000000000..9bc1b9f9e8d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86SegmentRegisters.java @@ -0,0 +1,66 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.utilities.*; + +public class X86SegmentRegisters { + + public static final int NUM_SEGMENT_REGISTERS = 6; + + public static final X86SegmentRegister ES; + public static final X86SegmentRegister CS; + public static final X86SegmentRegister SS; + public static final X86SegmentRegister DS; + public static final X86SegmentRegister FS; + public static final X86SegmentRegister GS; + + private static X86SegmentRegister segmentRegisters[]; + + static { + //Segment registers + ES = new X86SegmentRegister(0, "%es"); + CS = new X86SegmentRegister(1, "%cs"); + SS = new X86SegmentRegister(2, "%ss"); + DS = new X86SegmentRegister(3, "%ds"); + FS = new X86SegmentRegister(4, "%fs"); + GS = new X86SegmentRegister(5, "%gs"); + + segmentRegisters = (new X86SegmentRegister[] { + ES, CS, SS, DS, FS, GS + }); + } + + public static int getNumberOfRegisters() { + return NUM_SEGMENT_REGISTERS; + } + + public static X86SegmentRegister getSegmentRegister(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_SEGMENT_REGISTERS, "invalid segment register number!"); + } + return segmentRegisters[regNum]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86ShiftInstruction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86ShiftInstruction.java new file mode 100644 index 00000000000..ecdbd380c35 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86ShiftInstruction.java @@ -0,0 +1,86 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86ShiftInstruction extends X86Instruction implements ShiftInstruction { + final private int operation; + final private Operand operand1; + final private ImmediateOrRegister operand2; + + public X86ShiftInstruction(String name, int operation, Operand operand1, ImmediateOrRegister operand2, int size, int prefixes) { + super(name, size, prefixes); + this.operand1 = operand1; + this.operand2 = operand2; + this.operation = operation; + } + + public String asString(long currentPc, SymbolFinder symFinder) { + StringBuffer buf = new StringBuffer(); + buf.append(getPrefixString()); + buf.append(getName()); + buf.append(spaces); + buf.append(getOperandAsString(operand1)); + + if(operand2 != null) { + buf.append(comma); + + if ((operand2 instanceof Register)) { + buf.append(operand2.toString()); + } + else { + Number number = ((Immediate)operand2).getNumber(); + buf.append("0x"); + buf.append(Integer.toHexString(number.intValue())); + } + } + return buf.toString(); + } + + public int getOperation() { + return operation; + } + + public Operand getShiftDestination() { + return operand1; + } + + public Operand getShiftLength() { + return operand2; + } + + public Operand getShiftSource() { + return operand1; + } + + public boolean isShift() { + return true; + } + + protected String getOperand2String() { + return operand2.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86XMMRegister.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86XMMRegister.java new file mode 100644 index 00000000000..1e29dd496ea --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86XMMRegister.java @@ -0,0 +1,40 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.asm.*; + +public class X86XMMRegister extends X86Register { + + public X86XMMRegister(int num, String name) { + super(num, name); + } + public int getNumberOfRegisters() { + return X86XMMRegisters.getNumberOfRegisters(); + } + public String toString() { + return name; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86XMMRegisters.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86XMMRegisters.java new file mode 100644 index 00000000000..eb29fd88af9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/x86/X86XMMRegisters.java @@ -0,0 +1,80 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.asm.x86; + +import sun.jvm.hotspot.utilities.*; + +/* There are 8 128-bit XMM registers*/ + +public class X86XMMRegisters { + + public static final int NUM_XMM_REGISTERS = 8; + + public static final X86XMMRegister XMM0; + public static final X86XMMRegister XMM1; + public static final X86XMMRegister XMM2; + public static final X86XMMRegister XMM3; + public static final X86XMMRegister XMM4; + public static final X86XMMRegister XMM5; + public static final X86XMMRegister XMM6; + public static final X86XMMRegister XMM7; + + private static X86XMMRegister xmmRegisters[]; + + static { + //XMM registers + XMM0 = new X86XMMRegister(0, "%xmm0"); + XMM1 = new X86XMMRegister(1, "%xmm1"); + XMM2 = new X86XMMRegister(2, "%xmm2"); + XMM3 = new X86XMMRegister(3, "%xmm3"); + XMM4 = new X86XMMRegister(4, "%xmm4"); + XMM5 = new X86XMMRegister(5, "%xmm5"); + XMM6 = new X86XMMRegister(6, "%xmm6"); + XMM7 = new X86XMMRegister(7, "%xmm7"); + + xmmRegisters = (new X86XMMRegister[] { + XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7 + }); + } + + public static int getNumberOfRegisters() { + return NUM_XMM_REGISTERS; + } + + //Return the register name + public static String getRegisterName(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_XMM_REGISTERS, "invalid XMM register number!"); + } + return xmmRegisters[regNum].toString(); + } + + public static X86XMMRegister getRegister(int regNum) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regNum > -1 && regNum < NUM_XMM_REGISTERS, "invalid XMM register number!"); + } + return xmmRegisters[regNum]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpot.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpot.java new file mode 100644 index 00000000000..66bb719e5ce --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpot.java @@ -0,0 +1,1536 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; +import java.util.*; +import javax.swing.*; +import javax.swing.filechooser.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.posix.*; +import sun.jvm.hotspot.debugger.win32.*; +import sun.jvm.hotspot.livejvm.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.ui.*; +import sun.jvm.hotspot.utilities.*; + +/** The BugSpot component. This is embeddable in an application by + virtue of its being a JComponent. It (currently) requires the use + of a menu bar which can be fetched via getMenuBar(). This is + intended ultimately to replace HSDB. */ + +public class BugSpot extends JPanel { + public BugSpot() { + super(); + Runtime.getRuntime().addShutdownHook(new java.lang.Thread() { + public void run() { + detachDebugger(); + } + }); + } + + /** Turn on or off MDI (Multiple Document Interface) mode. When MDI + is enabled, the BugSpot component contains a JDesktopPane and all + windows are JInternalFrames. When disabled, only the menu bar is + relevant. */ + public void setMDIMode(boolean onOrOff) { + mdiMode = onOrOff; + } + + /** Indicates whether MDI mode is enabled. */ + public boolean getMDIMode() { + return mdiMode; + } + + /** Build user interface widgets. This must be called before adding + the BugSpot component to its parent. */ + public void build() { + setLayout(new BorderLayout()); + + menuBar = new JMenuBar(); + + attachMenuItems = new java.util.ArrayList(); + detachMenuItems = new java.util.ArrayList(); + debugMenuItems = new java.util.ArrayList(); + suspendDebugMenuItems = new java.util.ArrayList(); + resumeDebugMenuItems = new java.util.ArrayList(); + + // + // File menu + // + + JMenu menu = createMenu("File", 'F', 0); + JMenuItem item; + item = createMenuItem("Open source file...", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + openSourceFile(); + } + }, + KeyEvent.VK_O, InputEvent.CTRL_MASK, + 'O', 0); + menu.add(item); + detachMenuItems.add(item); + + menu.addSeparator(); + + item = createMenuItem("Attach to process...", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showAttachDialog(); + } + }, + 'A', 0); + menu.add(item); + attachMenuItems.add(item); + + item = createMenuItem("Detach", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + detach(); + } + }, + 'D', 0); + menu.add(item); + detachMenuItems.add(item); + + // Disable detach menu items at first + setMenuItemsEnabled(detachMenuItems, false); + + menu.addSeparator(); + + menu.add(createMenuItem("Exit", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + detach(); + System.exit(0); + } + }, + 'x', 1)); + + menuBar.add(menu); + + // + // Debug menu + // + + debugMenu = createMenu("Debug", 'D', 0); + item = createMenuItem("Go", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (!attached) return; + if (!isSuspended()) return; + resume(); + } + }, + KeyEvent.VK_F5, 0, + 'G', 0); + debugMenu.add(item); + resumeDebugMenuItems.add(item); + + item = createMenuItem("Break", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (!attached) { + System.err.println("Not attached"); + return; + } + if (isSuspended()) { + System.err.println("Already suspended"); + return; + } + suspend(); + } + }, + 'B', 0); + debugMenu.add(item); + suspendDebugMenuItems.add(item); + + debugMenu.addSeparator(); + + item = createMenuItem("Threads...", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showThreadsDialog(); + } + }, + 'T', 0); + debugMenu.add(item); + debugMenuItems.add(item); + // FIXME: belongs under "View -> Debug Windows" + item = createMenuItem("Memory", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + showMemoryDialog(); + } + }, + 'M', 0); + debugMenu.add(item); + debugMenuItems.add(item); + + debugMenu.setEnabled(false); + menuBar.add(debugMenu); + + if (mdiMode) { + desktop = new JDesktopPane(); + add(desktop, BorderLayout.CENTER); + } + + fixedWidthFont = GraphicsUtilities.lookupFont("Courier"); + + debugEventTimer = new javax.swing.Timer(100, new ActionListener() { + public void actionPerformed(ActionEvent e) { + pollForDebugEvent(); + } + }); + } + + public JMenuBar getMenuBar() { + return menuBar; + } + + public void showAttachDialog() { + setMenuItemsEnabled(attachMenuItems, false); + final FrameWrapper attachDialog = newFrame("Attach to process"); + attachDialog.getContentPane().setLayout(new BorderLayout()); + attachDialog.setClosable(true); + attachDialog.setResizable(true); + + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + panel.setBorder(GraphicsUtilities.newBorder(5)); + attachDialog.setBackground(panel.getBackground()); + + JPanel listPanel = new JPanel(); + listPanel.setLayout(new BorderLayout()); + final ProcessListPanel plist = new ProcessListPanel(getLocalDebugger()); + panel.add(plist, BorderLayout.CENTER); + JCheckBox check = new JCheckBox("Update list continuously"); + check.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + plist.start(); + } else { + plist.stop(); + } + } + }); + listPanel.add(plist, BorderLayout.CENTER); + listPanel.add(check, BorderLayout.SOUTH); + panel.add(listPanel, BorderLayout.CENTER); + attachDialog.getContentPane().add(panel, BorderLayout.CENTER); + attachDialog.setClosingActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + plist.stop(); + setMenuItemsEnabled(attachMenuItems, true); + } + }); + + ActionListener attacher = new ActionListener() { + public void actionPerformed(ActionEvent e) { + plist.stop(); + attachDialog.setVisible(false); + removeFrame(attachDialog); + ProcessInfo info = plist.getSelectedProcess(); + if (info != null) { + attach(info.getPid()); + } + } + }; + + Box hbox = Box.createHorizontalBox(); + hbox.add(Box.createGlue()); + JButton button = new JButton("OK"); + button.addActionListener(attacher); + hbox.add(button); + hbox.add(Box.createHorizontalStrut(20)); + button = new JButton("Cancel"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + plist.stop(); + attachDialog.setVisible(false); + removeFrame(attachDialog); + setMenuItemsEnabled(attachMenuItems, true); + } + }); + hbox.add(button); + hbox.add(Box.createGlue()); + panel = new JPanel(); + panel.setBorder(GraphicsUtilities.newBorder(5)); + panel.add(hbox); + + attachDialog.getContentPane().add(panel, BorderLayout.SOUTH); + + addFrame(attachDialog); + attachDialog.pack(); + attachDialog.setSize(400, 300); + GraphicsUtilities.centerInContainer(attachDialog.getComponent(), + getParentDimension(attachDialog.getComponent())); + attachDialog.show(); + } + + public void showThreadsDialog() { + final FrameWrapper threadsDialog = newFrame("Threads"); + threadsDialog.getContentPane().setLayout(new BorderLayout()); + threadsDialog.setClosable(true); + threadsDialog.setResizable(true); + + ThreadListPanel threads = new ThreadListPanel(getCDebugger(), getAgent().isJavaMode()); + threads.addListener(new ThreadListPanel.Listener() { + public void setFocus(ThreadProxy thread, JavaThread jthread) { + setCurrentThread(thread); + // FIXME: print this to GUI, bring some windows to foreground + System.err.println("Focus changed to thread " + thread); + } + }); + threads.setBorder(GraphicsUtilities.newBorder(5)); + threadsDialog.getContentPane().add(threads); + addFrame(threadsDialog); + threadsDialog.pack(); + GraphicsUtilities.reshapeToAspectRatio(threadsDialog.getComponent(), + 3.0f, + 0.9f, + getParentDimension(threadsDialog.getComponent())); + GraphicsUtilities.centerInContainer(threadsDialog.getComponent(), + getParentDimension(threadsDialog.getComponent())); + threadsDialog.show(); + } + + public void showMemoryDialog() { + final FrameWrapper memoryDialog = newFrame("Memory"); + memoryDialog.getContentPane().setLayout(new BorderLayout()); + memoryDialog.setClosable(true); + memoryDialog.setResizable(true); + + memoryDialog.getContentPane().add(new MemoryViewer(getDebugger(), + (getDebugger().getMachineDescription().getAddressSize() == 8)), + BorderLayout.CENTER); + addFrame(memoryDialog); + memoryDialog.pack(); + GraphicsUtilities.reshapeToAspectRatio(memoryDialog.getComponent(), + 1.0f, + 0.7f, + getParentDimension(memoryDialog.getComponent())); + GraphicsUtilities.centerInContainer(memoryDialog.getComponent(), + getParentDimension(memoryDialog.getComponent())); + memoryDialog.show(); + } + + /** Changes the editor factory this debugger uses to display source + code. Specified factory may be null, in which case the default + factory is used. */ + public void setEditorFactory(EditorFactory fact) { + if (fact != null) { + editorFact = fact; + } else { + editorFact = new DefaultEditorFactory(); + } + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private WorkerThread workerThread; + private boolean mdiMode; + private JVMDebugger localDebugger; + private BugSpotAgent agent = new BugSpotAgent(); + private JMenuBar menuBar; + /** List */ + private java.util.List attachMenuItems; + private java.util.List detachMenuItems; + private java.util.List debugMenuItems; + private java.util.List suspendDebugMenuItems; + private java.util.List resumeDebugMenuItems; + private FrameWrapper stackFrame; + private VariablePanel localsPanel; + private StackTracePanel stackTracePanel; + private FrameWrapper registerFrame; + private RegisterPanel registerPanel; + // Used for mixed-language stack traces + private Map threadToJavaThreadMap; + + private JMenu debugMenu; + + // MDI mode only: desktop pane + private JDesktopPane desktop; + + // Attach/detach state + private boolean attached; + + // Suspension (combined Java/C++) state + private boolean suspended; + + // Fixed-width font + private Font fixedWidthFont; + + // Breakpoint setting + // Maps Strings to List/**/ + private Map sourceFileToLineNumberInfoMap; + // Maps Strings (file names) to Sets of Integers (line numbers) + private Map fileToBreakpointMap; + + // Debug events + private javax.swing.Timer debugEventTimer; + + // Java debug events + private boolean javaEventPending; + + static class BreakpointResult { + private boolean success; + private boolean set; + private int lineNo; + private String why; + + /** For positive results */ + BreakpointResult(boolean success, boolean set, int lineNo) { + this(success, set, lineNo, null); + } + + /** For negative results */ + BreakpointResult(boolean success, boolean set, int lineNo, String why) { + this.success = success; + this.set = set; + this.lineNo = lineNo; + this.why = why; + } + + public boolean succeeded() { + return success; + } + + public boolean set() { + return set; + } + + /** Line at which the breakpoint was actually set; only valid if + succeeded() returns true */ + public int getLine() { + return lineNo; + } + + public String getWhy() { + return why; + } + } + + + // Editors for source code. File name-to-Editor mapping. + private Map editors; + private EditorFactory editorFact = new DefaultEditorFactory(); + private EditorCommands editorComm = new EditorCommands() { + public void windowClosed(Editor editor) { + editors.remove(editor.getSourceFileName()); + } + + public void toggleBreakpointAtLine(Editor editor, int lineNumber) { + // FIXME: handle "lazy" breakpoints where the source file has + // been opened with some other mechanism (File -> Open) and we + // don't have debug information pointing to that file yet + // FIXME: NOT FINISHED + + BreakpointResult res = + handleBreakpointToggle(editor, lineNumber); + if (res.succeeded()) { + if (res.set()) { + editor.showBreakpointAtLine(res.getLine()); + } else { + editor.clearBreakpointAtLine(res.getLine()); + } + } else { + String why = res.getWhy(); + if (why == null) { + why = ""; + } else { + why = ": " + why; + } + showMessageDialog("Unable to toggle breakpoint" + why, + "Unable to toggle breakpoint", + JOptionPane.WARNING_MESSAGE); + } + } + }; + + private void attach(final int pid) { + try { + getAgent().attach(pid); + setMenuItemsEnabled(detachMenuItems, true); + setMenuItemsEnabled(suspendDebugMenuItems, false); + setMenuItemsEnabled(resumeDebugMenuItems, true); + debugMenu.setEnabled(true); + attached = true; + suspended = true; + + if (getAgent().isJavaMode()) { + System.err.println("Java HotSpot(TM) virtual machine detected."); + } else { + System.err.println("(No Java(TM) virtual machine detected)"); + } + + // Set up editor map + editors = new HashMap(); + + // Initialize breakpoints + fileToBreakpointMap = new HashMap(); + + // Create combined stack trace and local variable panel + JPanel framePanel = new JPanel(); + framePanel.setLayout(new BorderLayout()); + framePanel.setBorder(GraphicsUtilities.newBorder(5)); + localsPanel = new VariablePanel(); + JTabbedPane tab = new JTabbedPane(); + tab.addTab("Locals", localsPanel); + tab.setTabPlacement(JTabbedPane.BOTTOM); + framePanel.add(tab, BorderLayout.CENTER); + JPanel stackPanel = new JPanel(); + stackPanel.setLayout(new BoxLayout(stackPanel, BoxLayout.X_AXIS)); + stackPanel.add(new JLabel("Context:")); + stackPanel.add(Box.createHorizontalStrut(5)); + stackTracePanel = new StackTracePanel(); + stackTracePanel.addListener(new StackTracePanel.Listener() { + public void frameChanged(CFrame fr, JavaVFrame jfr) { + setCurrentFrame(fr, jfr); + } + }); + stackPanel.add(stackTracePanel); + framePanel.add(stackPanel, BorderLayout.NORTH); + stackFrame = newFrame("Stack"); + stackFrame.getContentPane().setLayout(new BorderLayout()); + stackFrame.getContentPane().add(framePanel, BorderLayout.CENTER); + stackFrame.setResizable(true); + stackFrame.setClosable(false); + addFrame(stackFrame); + stackFrame.setSize(400, 200); + GraphicsUtilities.moveToInContainer(stackFrame.getComponent(), 0.0f, 1.0f, 0, 20); + stackFrame.show(); + + // Create register panel + registerPanel = new RegisterPanel(); + registerPanel.setFont(fixedWidthFont); + registerFrame = newFrame("Registers"); + registerFrame.getContentPane().setLayout(new BorderLayout()); + registerFrame.getContentPane().add(registerPanel, BorderLayout.CENTER); + addFrame(registerFrame); + registerFrame.setResizable(true); + registerFrame.setClosable(false); + registerFrame.setSize(225, 200); + GraphicsUtilities.moveToInContainer(registerFrame.getComponent(), + 1.0f, 0.0f, 0, 0); + registerFrame.show(); + + resetCurrentThread(); + } catch (DebuggerException e) { + final String errMsg = formatMessage(e.getMessage(), 80); + setMenuItemsEnabled(attachMenuItems, true); + showMessageDialog("Unable to connect to process ID " + pid + ":\n\n" + errMsg, + "Unable to Connect", + JOptionPane.WARNING_MESSAGE); + getAgent().detach(); + } + } + + private synchronized void detachDebugger() { + if (!attached) { + return; + } + if (isSuspended()) { + resume(); // Necessary for JVMDI resumption + } + getAgent().detach(); + // FIXME: clear out breakpoints (both Java and C/C++) from target + // process + sourceFileToLineNumberInfoMap = null; + fileToBreakpointMap = null; + threadToJavaThreadMap = null; + editors = null; + attached = false; + } + + private synchronized void detach() { + detachDebugger(); + setMenuItemsEnabled(attachMenuItems, true); + setMenuItemsEnabled(detachMenuItems, false); + debugMenu.setEnabled(false); + if (mdiMode) { + // FIXME: is this sufficient, or will I have to do anything else + // to the components to kill them off? What about WorkerThreads? + desktop.removeAll(); + desktop.invalidate(); + desktop.validate(); + desktop.repaint(); + } + // FIXME: keep track of all windows and close them even in non-MDI + // mode + debugEventTimer.stop(); + } + + // Returns a Debugger for processes on the local machine. This is + // only used to fetch the process list. + private Debugger getLocalDebugger() { + if (localDebugger == null) { + String os = PlatformInfo.getOS(); + String cpu = PlatformInfo.getCPU(); + + if (os.equals("win32")) { + if (!cpu.equals("x86")) { + throw new DebuggerException("Unsupported CPU \"" + cpu + "\" for Windows"); + } + + localDebugger = new Win32DebuggerLocal(new MachineDescriptionIntelX86(), true); + } else if (os.equals("linux")) { + if (!cpu.equals("x86")) { + throw new DebuggerException("Unsupported CPU \"" + cpu + "\" for Linux"); + } + + // FIXME: figure out how to specify path to debugger module + throw new RuntimeException("FIXME: figure out how to specify path to debugger module"); + // localDebugger = new PosixDebuggerLocal(new MachineDescriptionIntelX86(), true); + } else { + // FIXME: port to Solaris + throw new DebuggerException("Unsupported OS \"" + os + "\""); + } + + // FIXME: we require that the primitive type sizes be configured + // in order to use basic functionality in class Address such as + // the fetching of floating-point values. There are a lot of + // assumptions in the current code that Java floats and doubles + // are of equivalent size to C values. The configurability of the + // primitive type sizes hasn't seemed necessary and in this kind + // of debugging scenario (namely, debugging arbitrary C++ + // processes) it appears difficult to support that kind of + // flexibility. + localDebugger.configureJavaPrimitiveTypeSizes(1, 1, 2, 8, 4, 4, 8, 2); + } + + return localDebugger; + } + + private BugSpotAgent getAgent() { + return agent; + } + + private Debugger getDebugger() { + return getAgent().getDebugger(); + } + + private CDebugger getCDebugger() { + return getAgent().getCDebugger(); + } + + private void resetCurrentThread() { + setCurrentThread((ThreadProxy) getCDebugger().getThreadList().get(0)); + } + + private void setCurrentThread(ThreadProxy t) { + // Create stack trace + // FIXME: add ability to intermix C/Java frames + java.util.List trace = new ArrayList(); + CFrame fr = getCDebugger().topFrameForThread(t); + while (fr != null) { + trace.add(new StackTraceEntry(fr, getCDebugger())); + try { + fr = fr.sender(); + } catch (AddressException e) { + e.printStackTrace(); + showMessageDialog("Error while walking stack; stack trace will be truncated\n(see console for details)", + "Error walking stack", + JOptionPane.WARNING_MESSAGE); + fr = null; + } + } + JavaThread jthread = javaThreadForProxy(t); + if (jthread != null) { + // Java mode, and we have a Java thread. + // Find all Java frames on the stack. We currently do this in a + // manner which involves minimal interaction between the Java + // and C/C++ debugging systems: any C frame which has a PC in an + // unknown location (i.e., not in any DSO) is assumed to be a + // Java frame. We merge stack segments of unknown frames with + // segments of Java frames beginning with native methods. + java.util.List javaTrace = new ArrayList(); + VFrame vf = jthread.getLastJavaVFrameDbg(); + while (vf != null) { + if (vf.isJavaFrame()) { + javaTrace.add(new StackTraceEntry((JavaVFrame) vf)); + vf = vf.sender(); + } + } + // Merge stack traces + java.util.List mergedTrace = new ArrayList(); + int c = 0; + int j = 0; + while (c < trace.size()) { + StackTraceEntry entry = (StackTraceEntry) trace.get(c); + if (entry.isUnknownCFrame()) { + boolean gotJavaFrame = false; + while (j < javaTrace.size()) { + StackTraceEntry javaEntry = (StackTraceEntry) javaTrace.get(j); + JavaVFrame jvf = javaEntry.getJavaFrame(); + Method m = jvf.getMethod(); + if (!m.isNative() || !gotJavaFrame) { + gotJavaFrame = true; + mergedTrace.add(javaEntry); + ++j; + } else { + break; // Reached native method; have intervening C frames + } + } + if (gotJavaFrame) { + // Skip this sequence of unknown frames, as we've + // successfully identified it as Java frames + while (c < trace.size() && entry.isUnknownCFrame()) { + ++c; + if (c < trace.size()) { + entry = (StackTraceEntry) trace.get(c); + } + } + continue; + } + } + // If we get here, we either have an unknown frame we didn't + // know how to categorize or we have a known C frame. Add it + // to the trace. + mergedTrace.add(entry); + ++c; + } + trace = mergedTrace; + } + stackTracePanel.setTrace(trace); + + registerPanel.update(t); + } + + private void setCurrentFrame(CFrame fr, JavaVFrame jfr) { + localsPanel.clear(); + + if (fr != null) { + localsPanel.update(fr); + + // FIXME: load source file if we can find it, otherwise display disassembly + LoadObject lo = getCDebugger().loadObjectContainingPC(fr.pc()); + if (lo != null) { + CDebugInfoDataBase db = lo.getDebugInfoDataBase(); + if (db != null) { + LineNumberInfo info = db.lineNumberForPC(fr.pc()); + if (info != null) { + System.err.println("PC " + fr.pc() + ": Source file \"" + + info.getSourceFileName() + + "\", line number " + + info.getLineNumber() + + ", PC range [" + + info.getStartPC() + + ", " + + info.getEndPC() + + ")"); + // OK, here we go... + showLineNumber(null, info.getSourceFileName(), info.getLineNumber()); + } else { + System.err.println("(No line number information for PC " + fr.pc() + ")"); + // Dump line number information for database + db.iterate(new LineNumberVisitor() { + public void doLineNumber(LineNumberInfo info) { + System.err.println(" Source file \"" + + info.getSourceFileName() + + "\", line number " + + info.getLineNumber() + + ", PC range [" + + info.getStartPC() + + ", " + + info.getEndPC() + + ")"); + } + }); + } + } + } + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(jfr != null, "Must have either C or Java frame"); + } + localsPanel.update(jfr); + // See whether we can locate source file and line number + // FIXME: infer pathmap entries from user's locating of this + // source file + // FIXME: figure out what to do for native methods. Possible to + // go to line number for the native method declaration? + Method m = jfr.getMethod(); + Symbol sfn = ((InstanceKlass) m.getMethodHolder()).getSourceFileName(); + if (sfn != null) { + int bci = jfr.getBCI(); + int lineNo = m.getLineNumberFromBCI(bci); + if (lineNo >= 0) { + // FIXME: show disassembly otherwise + showLineNumber(packageName(m.getMethodHolder().getName().asString()), + sfn.asString(), lineNo); + } + } + } + } + + private String packageName(String str) { + int idx = str.lastIndexOf('/'); + if (idx < 0) { + return ""; + } + return str.substring(0, idx).replace('/', '.'); + } + + private JavaThread javaThreadForProxy(ThreadProxy t) { + if (!getAgent().isJavaMode()) { + return null; + } + if (threadToJavaThreadMap == null) { + threadToJavaThreadMap = new HashMap(); + Threads threads = VM.getVM().getThreads(); + for (JavaThread thr = threads.first(); thr != null; thr = thr.next()) { + threadToJavaThreadMap.put(thr.getThreadProxy(), thr); + } + } + return (JavaThread) threadToJavaThreadMap.get(t); + } + + private static JMenu createMenu(String name, char mnemonic, int mnemonicPos) { + JMenu menu = new JMenu(name); + menu.setMnemonic(mnemonic); + menu.setDisplayedMnemonicIndex(mnemonicPos); + return menu; + } + + private static JMenuItem createMenuItem(String name, ActionListener l) { + JMenuItem item = new JMenuItem(name); + item.addActionListener(l); + return item; + } + + private static JMenuItem createMenuItemInternal(String name, ActionListener l, int accelerator, int modifiers) { + JMenuItem item = createMenuItem(name, l); + item.setAccelerator(KeyStroke.getKeyStroke(accelerator, modifiers)); + return item; + } + + private static JMenuItem createMenuItem(String name, ActionListener l, int accelerator) { + return createMenuItemInternal(name, l, accelerator, 0); + } + + private static JMenuItem createMenuItem(String name, ActionListener l, char mnemonic, int mnemonicPos) { + JMenuItem item = createMenuItem(name, l); + item.setMnemonic(mnemonic); + item.setDisplayedMnemonicIndex(mnemonicPos); + return item; + } + + private static JMenuItem createMenuItem(String name, + ActionListener l, + int accelerator, + int acceleratorMods, + char mnemonic, + int mnemonicPos) { + JMenuItem item = createMenuItemInternal(name, l, accelerator, acceleratorMods); + item.setMnemonic(mnemonic); + item.setDisplayedMnemonicIndex(mnemonicPos); + return item; + } + + /** Punctuates the given string with \n's where necessary to not + exceed the given number of characters per line. Strips + extraneous whitespace. */ + private static String formatMessage(String message, int charsPerLine) { + StringBuffer buf = new StringBuffer(message.length()); + StringTokenizer tokenizer = new StringTokenizer(message); + int curLineLength = 0; + while (tokenizer.hasMoreTokens()) { + String tok = tokenizer.nextToken(); + if (curLineLength + tok.length() > charsPerLine) { + buf.append('\n'); + curLineLength = 0; + } else { + if (curLineLength != 0) { + buf.append(' '); + ++curLineLength; + } + } + buf.append(tok); + curLineLength += tok.length(); + } + return buf.toString(); + } + + private void setMenuItemsEnabled(java.util.List items, boolean enabled) { + for (Iterator iter = items.iterator(); iter.hasNext(); ) { + ((JMenuItem) iter.next()).setEnabled(enabled); + } + } + + private void showMessageDialog(final String message, final String title, final int jOptionPaneKind) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (mdiMode) { + JOptionPane.showInternalMessageDialog(desktop, message, title, jOptionPaneKind); + } else { + JOptionPane.showMessageDialog(null, message, title, jOptionPaneKind); + } + } + }); + } + + private FrameWrapper newFrame(String title) { + if (mdiMode) { + return new JInternalFrameWrapper(new JInternalFrame(title)); + } else { + return new JFrameWrapper(new JFrame(title)); + } + } + + private void addFrame(FrameWrapper frame) { + if (mdiMode) { + desktop.add(frame.getComponent()); + } + } + + private void removeFrame(FrameWrapper frame) { + if (mdiMode) { + desktop.remove(frame.getComponent()); + desktop.invalidate(); + desktop.validate(); + desktop.repaint(); + } + // FIXME: do something when not in MDI mode + } + + private Dimension getParentDimension(Component c) { + if (mdiMode) { + return desktop.getSize(); + } else { + return Toolkit.getDefaultToolkit().getScreenSize(); + } + } + + // Default editor implementation + class DefaultEditor implements Editor { + private DefaultEditorFactory factory; + private FrameWrapper editorFrame; + private String filename; + private SourceCodePanel code; + private boolean shown; + private Object userData; + + public DefaultEditor(DefaultEditorFactory fact, String filename, final EditorCommands comm) { + this.filename = filename; + this.factory = fact; + editorFrame = newFrame(filename); + code = new SourceCodePanel(); + // FIXME: when font changes, change font in editors as well + code.setFont(fixedWidthFont); + editorFrame.getContentPane().add(code); + editorFrame.setClosable(true); + editorFrame.setResizable(true); + editorFrame.setClosingActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + comm.windowClosed(DefaultEditor.this); + removeFrame(editorFrame); + editorFrame.dispose(); + factory.editorClosed(DefaultEditor.this); + } + }); + editorFrame.setActivatedActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + factory.makeEditorCurrent(DefaultEditor.this); + code.requestFocus(); + } + }); + code.setEditorCommands(comm, this); + } + + public boolean openFile() { return code.openFile(filename); } + public String getSourceFileName() { return filename; } + public int getCurrentLineNumber() { return code.getCurrentLineNumber(); } + public void showLineNumber(int lineNo) { + if (!shown) { + addFrame(editorFrame); + GraphicsUtilities.reshapeToAspectRatio(editorFrame.getComponent(), + 1.0f, + 0.85f, + getParentDimension(editorFrame.getComponent())); + editorFrame.show(); + shown = true; + } + code.showLineNumber(lineNo); + editorFrame.toFront(); + } + public void highlightLineNumber(int lineNo) { code.highlightLineNumber(lineNo); } + public void showBreakpointAtLine(int lineNo) { code.showBreakpointAtLine(lineNo); } + public boolean hasBreakpointAtLine(int lineNo) { return code.hasBreakpointAtLine(lineNo); } + public void clearBreakpointAtLine(int lineNo) { code.clearBreakpointAtLine(lineNo); } + public void clearBreakpoints() { code.clearBreakpoints(); } + public void setUserData(Object o) { userData = o; } + public Object getUserData() { return userData; } + public void toFront() { editorFrame.toFront(); + factory.makeEditorCurrent(this); } + } + + class DefaultEditorFactory implements EditorFactory { + private LinkedList/**/ editors = new LinkedList(); + + public Editor openFile(String filename, EditorCommands commands) { + DefaultEditor editor = new DefaultEditor(this, filename, editorComm); + if (!editor.openFile()) { + return null; + } + return editor; + } + + public Editor getCurrentEditor() { + if (editors.isEmpty()) { + return null; + } + return (Editor) editors.getFirst(); + } + + void editorClosed(Editor editor) { + editors.remove(editor); + } + + void makeEditorCurrent(Editor editor) { + editors.remove(editor); + editors.addFirst(editor); + } + } + + // Helper class for loading .java files; show only those with + // correct file name which are also in the correct package + static class JavaFileFilter extends javax.swing.filechooser.FileFilter { + private String packageName; + private String fileName; + + JavaFileFilter(String packageName, String fileName) { + this.packageName = packageName; + this.fileName = fileName; + } + + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + // This rejects most files + if (!f.getName().equals(fileName)) { + return false; + } + // Ensure selected file is in the correct package + PackageScanner scanner = new PackageScanner(); + String pkg = scanner.scan(f); + if (!pkg.equals(packageName)) { + return false; + } + return true; + } + + public String getDescription() { return "Java source files"; } + } + + // Auxiliary information used only for Java source files + static class JavaUserData { + private String packageName; // External format + private String sourceFileName; + + /** Source file name is equivalent to that found in the .java + file; i.e., not a full path */ + JavaUserData(String packageName, String sourceFileName) { + this.packageName = packageName; + this.sourceFileName = sourceFileName; + } + + String packageName() { return packageName; } + String sourceFileName() { return sourceFileName; } + } + + // Opens a source file. This makes it available for the setting of + // lazy breakpoints. + private void openSourceFile() { + JFileChooser chooser = new JFileChooser(); + chooser.setDialogTitle("Open source code file"); + chooser.setMultiSelectionEnabled(false); + if (chooser.showOpenDialog(null) != JFileChooser.APPROVE_OPTION) { + return; + } + File chosen = chooser.getSelectedFile(); + if (chosen == null) { + return; + } + + // See whether we have a Java source file. If so, derive a package + // name for it. + String path = chosen.getPath(); + String name = null; + JavaUserData data = null; + if (path.endsWith(".java")) { + PackageScanner scanner = new PackageScanner(); + String pkg = scanner.scan(chosen); + // Now knowing both the package name and file name, we can put + // this in the editor map and use it for setting breakpoints + // later + String fileName = chosen.getName(); + name = pkg + "." + fileName; + data = new JavaUserData(pkg, fileName); + } else { + // FIXME: need pathmap mechanism + name = path; + } + Editor editor = (Editor) editors.get(name); + if (editor == null) { + editor = editorFact.openFile(path, editorComm); + if (editor == null) { + showMessageDialog("Unable to open file \"" + path + "\" -- unexpected error.", + "Unable to open file", + JOptionPane.WARNING_MESSAGE); + return; + } + editors.put(name, editor); + if (data != null) { + editor.setUserData(data); + } + } else { + editor.toFront(); + } + editor.showLineNumber(1); + // Show breakpoints as well if we have any for this file + Set set = (Set) fileToBreakpointMap.get(editor.getSourceFileName()); + if (set != null) { + for (Iterator iter = set.iterator(); iter.hasNext(); ) { + editor.showBreakpointAtLine(((Integer) iter.next()).intValue()); + } + } + } + + // Package name may be null, in which case the file is assumed to be + // a C source file. Otherwise it is assumed to be a Java source file + // and certain filtering rules will be applied. + private void showLineNumber(String packageName, String fileName, int lineNumber) { + String name; + if (packageName == null) { + name = fileName; + } else { + name = packageName + "." + fileName; + } + Editor editor = (Editor) editors.get(name); + if (editor == null) { + // See whether file exists + File file = new File(fileName); + String realFileName = fileName; + if (!file.exists()) { + // User must specify path to file + JFileChooser chooser = new JFileChooser(); + chooser.setDialogTitle("Please locate " + fileName); + chooser.setMultiSelectionEnabled(false); + if (packageName != null) { + chooser.setFileFilter(new JavaFileFilter(packageName, fileName)); + } + int res = chooser.showOpenDialog(null); + if (res != JFileChooser.APPROVE_OPTION) { + // FIXME: show disassembly instead + return; + } + // FIXME: would like to infer more from the selection; i.e., + // a pathmap leading up to this file + File chosen = chooser.getSelectedFile(); + if (chosen == null) { + return; + } + realFileName = chosen.getPath(); + } + // Now instruct editor factory to open file + editor = editorFact.openFile(realFileName, editorComm); + if (editor == null) { + showMessageDialog("Unable to open file \"" + realFileName + "\" -- unexpected error.", + "Unable to open file", + JOptionPane.WARNING_MESSAGE); + return; + } + // Got an editor; put it in map + editors.put(name, editor); + // If Java source file, add additional information for later + if (packageName != null) { + editor.setUserData(new JavaUserData(packageName, fileName)); + } + } + // Got editor; show line + editor.showLineNumber(lineNumber); + editor.highlightLineNumber(lineNumber); + // Show breakpoints as well if we have any for this file + Set set = (Set) fileToBreakpointMap.get(editor.getSourceFileName()); + if (set != null) { + for (Iterator iter = set.iterator(); iter.hasNext(); ) { + editor.showBreakpointAtLine(((Integer) iter.next()).intValue()); + } + } + } + + // + // Suspend/resume + // + + private boolean isSuspended() { + return suspended; + } + + private synchronized void suspend() { + setMenuItemsEnabled(resumeDebugMenuItems, true); + setMenuItemsEnabled(suspendDebugMenuItems, false); + BugSpotAgent agent = getAgent(); + if (agent.canInteractWithJava() && !agent.isJavaSuspended()) { + agent.suspendJava(); + } + agent.suspend(); + // FIXME: call VM.getVM().fireVMSuspended() + resetCurrentThread(); + debugEventTimer.stop(); + suspended = true; + } + + private synchronized void resume() { + // Note: we don't wipe out the cached state like the + // sourceFileToLineNumberInfoMap since it is too expensive to + // recompute. Instead we recompute it if any DLLs are loaded or + // unloaded. + threadToJavaThreadMap = null; + setMenuItemsEnabled(resumeDebugMenuItems, false); + setMenuItemsEnabled(suspendDebugMenuItems, true); + registerPanel.clear(); + // FIXME: call VM.getVM().fireVMResumed() + BugSpotAgent agent = getAgent(); + agent.resume(); + if (agent.canInteractWithJava()) { + if (agent.isJavaSuspended()) { + agent.resumeJava(); + } + if (javaEventPending) { + javaEventPending = false; + // Clear it out before resuming polling for events + agent.javaEventContinue(); + } + } + agent.enableJavaInteraction(); + suspended = false; + debugEventTimer.start(); + } + + // + // Breakpoints + // + + private synchronized BreakpointResult handleBreakpointToggle(Editor editor, int lineNumber) { + // Currently we only use user data in editors to indicate Java + // source files. If this changes then this code will need to + // change. + JavaUserData data = (JavaUserData) editor.getUserData(); + String filename = editor.getSourceFileName(); + if (data == null) { + // C/C++ code + // FIXME: as noted above in EditorCommands.toggleBreakpointAtLine, + // this needs more work to handle "lazy" breakpoints in files + // which we don't know about in the debug information yet + CDebugger dbg = getCDebugger(); + ProcessControl prctl = dbg.getProcessControl(); + if (prctl == null) { + return new BreakpointResult(false, false, 0, "Process control not enabled"); + } + boolean mustSuspendAndResume = (!prctl.isSuspended()); + try { + if (mustSuspendAndResume) { + prctl.suspend(); + } + // Search debug info for all DSOs + LineNumberInfo info = getLineNumberInfo(filename, lineNumber); + if (info != null) { + Set bpset = (Set) fileToBreakpointMap.get(filename); + if (bpset == null) { + bpset = new HashSet(); + fileToBreakpointMap.put(filename, bpset); + } + Integer key = new Integer(info.getLineNumber()); + if (bpset.contains(key)) { + // Clear breakpoint at this line's PC + prctl.clearBreakpoint(info.getStartPC()); + bpset.remove(key); + return new BreakpointResult(true, false, info.getLineNumber()); + } else { + // Set breakpoint at this line's PC + System.err.println("Setting breakpoint at PC " + info.getStartPC()); + prctl.setBreakpoint(info.getStartPC()); + bpset.add(key); + return new BreakpointResult(true, true, info.getLineNumber()); + } + } else { + return new BreakpointResult(false, false, 0, "No debug information for this source file and line"); + } + } finally { + if (mustSuspendAndResume) { + prctl.resume(); + } + } + } else { + BugSpotAgent agent = getAgent(); + if (!agent.canInteractWithJava()) { + String why; + if (agent.isJavaInteractionDisabled()) { + why = "Can not toggle Java breakpoints while stopped because\nof C/C++ debug events (breakpoints, single-stepping)"; + } else { + why = "Could not talk to SA's JVMDI module to enable Java\nprogramming language breakpoints (run with -Xdebug -Xrunsa)"; + } + return new BreakpointResult(false, false, 0, why); + } + Set bpset = (Set) fileToBreakpointMap.get(filename); + if (bpset == null) { + bpset = new HashSet(); + fileToBreakpointMap.put(filename, bpset); + } + boolean mustResumeAndSuspend = isSuspended(); + try { + if (mustResumeAndSuspend) { + agent.resume(); + } + ServiceabilityAgentJVMDIModule.BreakpointToggleResult res = + getAgent().toggleJavaBreakpoint(data.sourceFileName(), + data.packageName(), + lineNumber); + if (res.getSuccess()) { + Integer key = new Integer(res.getLineNumber()); + boolean addRemRes = false; + if (res.getWasSet()) { + addRemRes = bpset.add(key); + System.err.println("Setting breakpoint at " + res.getMethodName() + res.getMethodSignature() + + ", bci " + res.getBCI() + ", line " + res.getLineNumber()); + } else { + addRemRes = bpset.remove(key); + System.err.println("Clearing breakpoint at " + res.getMethodName() + res.getMethodSignature() + + ", bci " + res.getBCI() + ", line " + res.getLineNumber()); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(addRemRes, "Inconsistent Java breakpoint state with respect to target process"); + } + return new BreakpointResult(true, res.getWasSet(), res.getLineNumber()); + } else { + return new BreakpointResult(false, false, 0, res.getErrMsg()); + } + } finally { + if (mustResumeAndSuspend) { + agent.suspend(); + resetCurrentThread(); + } + } + } + } + + // Must call only when suspended + private LineNumberInfo getLineNumberInfo(String filename, int lineNumber) { + Map map = getSourceFileToLineNumberInfoMap(); + java.util.List infos = (java.util.List) map.get(filename); + if (infos == null) { + return null; + } + // Binary search for line number + return searchLineNumbers(infos, lineNumber, 0, infos.size()); + } + + // Must call only when suspended + private Map getSourceFileToLineNumberInfoMap() { + if (sourceFileToLineNumberInfoMap == null) { + // Build from debug info + java.util.List loadObjects = getCDebugger().getLoadObjectList(); + final Map map = new HashMap(); + for (Iterator iter = loadObjects.iterator(); iter.hasNext(); ) { + LoadObject lo = (LoadObject) iter.next(); + CDebugInfoDataBase db = lo.getDebugInfoDataBase(); + if (db != null) { + db.iterate(new LineNumberVisitor() { + public void doLineNumber(LineNumberInfo info) { + String name = info.getSourceFileName(); + if (name != null) { + java.util.List val = (java.util.List) map.get(name); + if (val == null) { + val = new ArrayList(); + map.put(name, val); + } + val.add(info); + } + } + }); + } + } + // Sort all lists + for (Iterator iter = map.values().iterator(); iter.hasNext(); ) { + java.util.List list = (java.util.List) iter.next(); + Collections.sort(list, new Comparator() { + public int compare(Object o1, Object o2) { + LineNumberInfo l1 = (LineNumberInfo) o1; + LineNumberInfo l2 = (LineNumberInfo) o2; + int n1 = l1.getLineNumber(); + int n2 = l2.getLineNumber(); + if (n1 < n2) return -1; + if (n1 == n2) return 0; + return 1; + } + }); + } + sourceFileToLineNumberInfoMap = map; + } + return sourceFileToLineNumberInfoMap; + } + + private LineNumberInfo searchLineNumbers(java.util.List infoList, int lineNo, int lowIdx, int highIdx) { + if (highIdx < lowIdx) return null; + if (lowIdx == highIdx) { + // Base case: see whether start PC is less than or equal to addr + if (checkLineNumber(infoList, lineNo, lowIdx)) { + return (LineNumberInfo) infoList.get(lowIdx); + } else { + return null; + } + } else if (lowIdx == highIdx - 1) { + if (checkLineNumber(infoList, lineNo, lowIdx)) { + return (LineNumberInfo) infoList.get(lowIdx); + } else if (checkLineNumber(infoList, lineNo, highIdx)) { + return (LineNumberInfo) infoList.get(highIdx); + } else { + return null; + } + } + int midIdx = (lowIdx + highIdx) >> 1; + LineNumberInfo info = (LineNumberInfo) infoList.get(midIdx); + if (lineNo < info.getLineNumber()) { + // Always move search down + return searchLineNumbers(infoList, lineNo, lowIdx, midIdx); + } else if (lineNo == info.getLineNumber()) { + return info; + } else { + // Move search up + return searchLineNumbers(infoList, lineNo, midIdx, highIdx); + } + } + + private boolean checkLineNumber(java.util.List infoList, int lineNo, int idx) { + LineNumberInfo info = (LineNumberInfo) infoList.get(idx); + return (info.getLineNumber() >= lineNo); + } + + // + // Debug events + // + + private synchronized void pollForDebugEvent() { + ProcessControl prctl = getCDebugger().getProcessControl(); + if (prctl == null) { + return; + } + DebugEvent ev = prctl.debugEventPoll(); + if (ev != null) { + DebugEvent.Type t = ev.getType(); + if (t == DebugEvent.Type.LOADOBJECT_LOAD || + t == DebugEvent.Type.LOADOBJECT_UNLOAD) { + // Conservatively clear cached debug info state + sourceFileToLineNumberInfoMap = null; + // FIXME: would be very useful to have "stop on load/unload" + // events + // FIXME: must do work at these events to implement lazy + // breakpoints + prctl.debugEventContinue(); + } else if (t == DebugEvent.Type.BREAKPOINT) { + // Note: Visual C++ only notifies on breakpoints it doesn't + // know about + + // FIXME: put back test + // if (!prctl.isBreakpointSet(ev.getPC())) { + showMessageDialog("Breakpoint reached at PC " + ev.getPC(), + "Breakpoint reached", + JOptionPane.INFORMATION_MESSAGE); + // } + agent.disableJavaInteraction(); + suspend(); + prctl.debugEventContinue(); + } else if (t == DebugEvent.Type.SINGLE_STEP) { + agent.disableJavaInteraction(); + suspend(); + prctl.debugEventContinue(); + } else if (t == DebugEvent.Type.ACCESS_VIOLATION) { + showMessageDialog("Access violation attempting to " + + (ev.getWasWrite() ? "write" : "read") + + " address " + ev.getAddress() + + " at PC " + ev.getPC(), + "Access Violation", + JOptionPane.WARNING_MESSAGE); + agent.disableJavaInteraction(); + suspend(); + prctl.debugEventContinue(); + } else { + String info = "Unknown debug event encountered"; + if (ev.getUnknownEventDetail() != null) { + info = info + ": " + ev.getUnknownEventDetail(); + } + showMessageDialog(info, "Unknown debug event", JOptionPane.INFORMATION_MESSAGE); + suspend(); + prctl.debugEventContinue(); + } + return; + } + + // No C++ debug event; poll for Java debug event + if (getAgent().canInteractWithJava()) { + if (!javaEventPending) { + if (getAgent().javaEventPending()) { + suspend(); + // This does a lot of work and we want to have the page + // cache available to us as it runs + sun.jvm.hotspot.livejvm.Event jev = getAgent().javaEventPoll(); + if (jev != null) { + javaEventPending = true; + if (jev.getType() == sun.jvm.hotspot.livejvm.Event.Type.BREAKPOINT) { + BreakpointEvent bpev = (BreakpointEvent) jev; + showMessageDialog("Breakpoint reached in method\n" + + bpev.methodID().method().externalNameAndSignature() + + ",\nbci " + bpev.location(), + "Breakpoint reached", + JOptionPane.INFORMATION_MESSAGE); + } else if (jev.getType() == sun.jvm.hotspot.livejvm.Event.Type.EXCEPTION) { + ExceptionEvent exev = (ExceptionEvent) jev; + showMessageDialog(exev.exception().getKlass().getName().asString() + + "\nthrown in method\n" + + exev.methodID().method().externalNameAndSignature() + + "\nat BCI " + exev.location(), + "Exception thrown", + JOptionPane.INFORMATION_MESSAGE); + } else { + Assert.that(false, "Should not reach here"); + } + } + } + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpotAgent.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpotAgent.java new file mode 100644 index 00000000000..27f5de84d7e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpotAgent.java @@ -0,0 +1,837 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import java.io.PrintStream; +import java.net.*; +import java.rmi.*; +import sun.jvm.hotspot.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.dbx.*; +import sun.jvm.hotspot.debugger.proc.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.win32.*; +import sun.jvm.hotspot.debugger.windbg.*; +import sun.jvm.hotspot.debugger.linux.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.remote.*; +import sun.jvm.hotspot.livejvm.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/**

This class wraps the basic functionality for connecting to the + * target process or debug server. It makes it simple to start up the + * debugging system.

+ * + *

This agent (as compared to the HotSpotAgent) can connect to + * and interact with arbitrary processes. If the target process + * happens to be a HotSpot JVM, the Java debugging features of the + * Serviceability Agent are enabled. Further, if the Serviceability + * Agent's JVMDI module is loaded into the target VM, interaction + * with the live Java program is possible, specifically the catching + * of exceptions and setting of breakpoints.

+ * + *

The BugSpot debugger requires that the underlying Debugger + * support C/C++ debugging via the CDebugger interface.

+ * + *

FIXME: need to add a way to configure the paths to dbx and the + * DSO from the outside. However, this should work for now for + * internal use.

+ * + *

FIXME: especially with the addition of remote debugging, this + * has turned into a mess; needs rethinking.

*/ + +public class BugSpotAgent { + + private JVMDebugger debugger; + private MachineDescription machDesc; + private TypeDataBase db; + + private String os; + private String cpu; + private String fileSep; + + // The system can work in several ways: + // - Attaching to local process + // - Attaching to local core file + // - Connecting to remote debug server + // - Starting debug server for process + // - Starting debug server for core file + + // These are options for the "client" side of things + private static final int PROCESS_MODE = 0; + private static final int CORE_FILE_MODE = 1; + private static final int REMOTE_MODE = 2; + private int startupMode; + + // This indicates whether we are really starting a server or not + private boolean isServer; + + // All possible required information for connecting + private int pid; + private String executableName; + private String coreFileName; + private String debugServerID; + + // All needed information for server side + private String serverID; + + // Indicates whether we are attached to a HotSpot JVM or not + private boolean javaMode; + + // Indicates whether we have process control over a live HotSpot JVM + // or not; non-null if so. + private ServiceabilityAgentJVMDIModule jvmdi; + // While handling C breakpoints interactivity with the Java program + // is forbidden. Too many invariants are broken while the target is + // stopped at a C breakpoint to risk making JVMDI calls. + private boolean javaInteractionDisabled; + + private String[] jvmLibNames; + private String[] saLibNames; + + // FIXME: make these configurable, i.e., via a dotfile; also + // consider searching within the JDK from which this Java executable + // comes to find them + private static final String defaultDbxPathPrefix = "/net/jano.eng/export/disk05/hotspot/sa"; + private static final String defaultDbxSvcAgentDSOPathPrefix = "/net/jano.eng/export/disk05/hotspot/sa"; + + private static final boolean DEBUG; + static { + DEBUG = System.getProperty("sun.jvm.hotspot.bugspot.BugSpotAgent.DEBUG") + != null; + } + + static void debugPrintln(String str) { + if (DEBUG) { + System.err.println(str); + } + } + + static void showUsage() { + System.out.println(" You can also pass these -D options to java to specify where to find dbx and the \n" + + " Serviceability Agent plugin for dbx:"); + System.out.println(" -DdbxPathName=\n" + + " Default is derived from dbxPathPrefix"); + System.out.println(" or"); + System.out.println(" -DdbxPathPrefix=\n" + + " where xxx is the path name of a dir structure that contains:\n" + + " //bin/dbx\n" + + " The default is " + defaultDbxPathPrefix); + System.out.println(" and"); + System.out.println(" -DdbxSvcAgentDSOPathName=\n" + + " Default is determined from dbxSvcAgentDSOPathPrefix"); + System.out.println(" or"); + System.out.println(" -DdbxSvcAgentDSOPathPrefix=\n" + + " where xxx is the pathname of a dir structure that contains:\n" + + " //bin/lib/libsvc_agent_dbx.so\n" + + " The default is " + defaultDbxSvcAgentDSOPathPrefix); + } + + public BugSpotAgent() { + // for non-server add shutdown hook to clean-up debugger in case + // of forced exit. For remote server, shutdown hook is added by + // DebugServer. + Runtime.getRuntime().addShutdownHook(new java.lang.Thread( + new Runnable() { + public void run() { + synchronized (BugSpotAgent.this) { + if (!isServer) { + detach(); + } + } + } + })); + } + + //-------------------------------------------------------------------------------- + // Accessors (once the system is set up) + // + + public synchronized Debugger getDebugger() { + return debugger; + } + + public synchronized CDebugger getCDebugger() { + return getDebugger().getCDebugger(); + } + + public synchronized ProcessControl getProcessControl() { + return getCDebugger().getProcessControl(); + } + + public synchronized TypeDataBase getTypeDataBase() { + return db; + } + + /** Indicates whether the target process is suspended + completely. Equivalent to getProcessControl().isSuspended(). */ + public synchronized boolean isSuspended() throws DebuggerException { + return getProcessControl().isSuspended(); + } + + /** Suspends the target process completely. Equivalent to + getProcessControl().suspend(). */ + public synchronized void suspend() throws DebuggerException { + getProcessControl().suspend(); + } + + /** Resumes the target process completely. Equivalent to + getProcessControl().suspend(). */ + public synchronized void resume() throws DebuggerException { + getProcessControl().resume(); + } + + /** Indicates whether we are attached to a Java HotSpot virtual + machine */ + public synchronized boolean isJavaMode() { + return javaMode; + } + + /** Temporarily disables interaction with the target process via + JVMDI. This is done while the target process is stopped at a C + breakpoint. Can be called even if the JVMDI agent has not been + initialized. */ + public synchronized void disableJavaInteraction() { + javaInteractionDisabled = true; + } + + /** Re-enables interaction with the target process via JVMDI. This + is done while the target process is continued past a C + braekpoint. Can be called even if the JVMDI agent has not been + initialized. */ + public synchronized void enableJavaInteraction() { + javaInteractionDisabled = false; + } + + /** Indicates whether Java interaction has been disabled */ + public synchronized boolean isJavaInteractionDisabled() { + return javaInteractionDisabled; + } + + /** Indicates whether we can talk to the Serviceability Agent's + JVMDI module to be able to set breakpoints */ + public synchronized boolean canInteractWithJava() { + return (jvmdi != null) && !javaInteractionDisabled; + } + + /** Suspends all Java threads in the target process. Can only be + called if we are attached to a HotSpot JVM and can connect to + the SA's JVMDI module. Must not be called when the target + process has been suspended with suspend(). */ + public synchronized void suspendJava() throws DebuggerException { + if (!canInteractWithJava()) { + throw new DebuggerException("Could not connect to SA's JVMDI module"); + } + if (jvmdi.isSuspended()) { + throw new DebuggerException("Target process already suspended via JVMDI"); + } + jvmdi.suspend(); + } + + /** Resumes all Java threads in the target process. Can only be + called if we are attached to a HotSpot JVM and can connect to + the SA's JVMDI module. Must not be called when the target + process has been suspended with suspend(). */ + public synchronized void resumeJava() throws DebuggerException { + if (!canInteractWithJava()) { + throw new DebuggerException("Could not connect to SA's JVMDI module"); + } + if (!jvmdi.isSuspended()) { + throw new DebuggerException("Target process already resumed via JVMDI"); + } + jvmdi.resume(); + } + + /** Indicates whether the target process has been suspended at the + Java language level via the SA's JVMDI module */ + public synchronized boolean isJavaSuspended() throws DebuggerException { + return jvmdi.isSuspended(); + } + + /** Toggle a Java breakpoint at the given location. */ + public synchronized ServiceabilityAgentJVMDIModule.BreakpointToggleResult + toggleJavaBreakpoint(String srcFileName, + String pkgName, + int lineNo) { + if (!canInteractWithJava()) { + throw new DebuggerException("Could not connect to SA's JVMDI module; can not toggle Java breakpoints"); + } + return jvmdi.toggleBreakpoint(srcFileName, pkgName, lineNo); + } + + /** Access to JVMDI module's eventPending */ + public synchronized boolean javaEventPending() throws DebuggerException { + if (!canInteractWithJava()) { + throw new DebuggerException("Could not connect to SA's JVMDI module; can not poll for Java debug events"); + } + return jvmdi.eventPending(); + } + + /** Access to JVMDI module's eventPoll */ + public synchronized Event javaEventPoll() throws DebuggerException { + if (!canInteractWithJava()) { + throw new DebuggerException("Could not connect to SA's JVMDI module; can not poll for Java debug events"); + } + return jvmdi.eventPoll(); + } + + /** Access to JVMDI module's eventContinue */ + public synchronized void javaEventContinue() throws DebuggerException { + if (!canInteractWithJava()) { + throw new DebuggerException("Could not connect to SA's JVMDI module; can not continue past Java debug events"); + } + jvmdi.eventContinue(); + } + + + // FIXME: add other accessors. For example, suspension and + // resumption should be done through this interface, as well as + // interaction with the live Java process such as breakpoint setting. + // Probably should not expose the ServiceabilityAgentJVMDIModule + // from this interface. + + //-------------------------------------------------------------------------------- + // Client-side operations + // + + /** This attaches to a process running on the local machine. */ + public synchronized void attach(int processID) + throws DebuggerException { + if (debugger != null) { + throw new DebuggerException("Already attached"); + } + pid = processID; + startupMode = PROCESS_MODE; + isServer = false; + go(); + } + + /** This opens a core file on the local machine */ + public synchronized void attach(String executableName, String coreFileName) + throws DebuggerException { + if (debugger != null) { + throw new DebuggerException("Already attached"); + } + if ((executableName == null) || (coreFileName == null)) { + throw new DebuggerException("Both the core file name and executable name must be specified"); + } + this.executableName = executableName; + this.coreFileName = coreFileName; + startupMode = CORE_FILE_MODE; + isServer = false; + go(); + } + + /** This attaches to a "debug server" on a remote machine; this + remote server has already attached to a process or opened a + core file and is waiting for RMI calls on the Debugger object to + come in. */ + public synchronized void attach(String remoteServerID) + throws DebuggerException { + if (debugger != null) { + throw new DebuggerException("Already attached to a process"); + } + if (remoteServerID == null) { + throw new DebuggerException("Debug server id must be specified"); + } + + debugServerID = remoteServerID; + startupMode = REMOTE_MODE; + isServer = false; + go(); + } + + /** This should only be called by the user on the client machine, + not the server machine */ + public synchronized boolean detach() throws DebuggerException { + if (isServer) { + throw new DebuggerException("Should not call detach() for server configuration"); + } + return detachInternal(); + } + + //-------------------------------------------------------------------------------- + // Server-side operations + // + + /** This attaches to a process running on the local machine and + starts a debug server, allowing remote machines to connect and + examine this process. uniqueID is used to uniquely identify the + debuggee */ + public synchronized void startServer(int processID, String uniqueID) + throws DebuggerException { + if (debugger != null) { + throw new DebuggerException("Already attached"); + } + pid = processID; + startupMode = PROCESS_MODE; + isServer = true; + serverID = uniqueID; + go(); + } + + /** This attaches to a process running on the local machine and + starts a debug server, allowing remote machines to connect and + examine this process. */ + public synchronized void startServer(int processID) + throws DebuggerException { + startServer(processID, null); + } + + /** This opens a core file on the local machine and starts a debug + server, allowing remote machines to connect and examine this + core file. uniqueID is used to uniquely identify the + debuggee */ + public synchronized void startServer(String executableName, String coreFileName, + String uniqueID) + throws DebuggerException { + if (debugger != null) { + throw new DebuggerException("Already attached"); + } + if ((executableName == null) || (coreFileName == null)) { + throw new DebuggerException("Both the core file name and Java executable name must be specified"); + } + this.executableName = executableName; + this.coreFileName = coreFileName; + startupMode = CORE_FILE_MODE; + isServer = true; + serverID = uniqueID; + go(); + } + + /** This opens a core file on the local machine and starts a debug + server, allowing remote machines to connect and examine this + core file.*/ + public synchronized void startServer(String executableName, String coreFileName) + throws DebuggerException { + startServer(executableName, coreFileName, null); + } + + /** This may only be called on the server side after startServer() + has been called */ + public synchronized boolean shutdownServer() throws DebuggerException { + if (!isServer) { + throw new DebuggerException("Should not call shutdownServer() for client configuration"); + } + return detachInternal(); + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private boolean detachInternal() { + if (debugger == null) { + return false; + } + if (canInteractWithJava()) { + jvmdi.detach(); + jvmdi = null; + } + boolean retval = true; + if (!isServer) { + VM.shutdown(); + } + // We must not call detach() if we are a client and are connected + // to a remote debugger + Debugger dbg = null; + DebuggerException ex = null; + if (isServer) { + try { + RMIHelper.unbind(serverID); + } + catch (DebuggerException de) { + ex = de; + } + dbg = debugger; + } else { + if (startupMode != REMOTE_MODE) { + dbg = debugger; + } + } + if (dbg != null) { + retval = dbg.detach(); + } + + debugger = null; + machDesc = null; + db = null; + if (ex != null) { + throw(ex); + } + return retval; + } + + private void go() { + setupDebugger(); + javaMode = setupVM(); + } + + private void setupDebugger() { + if (startupMode != REMOTE_MODE) { + // + // Local mode (client attaching to local process or setting up + // server, but not client attaching to server) + // + + try { + os = PlatformInfo.getOS(); + cpu = PlatformInfo.getCPU(); + } + catch (UnsupportedPlatformException e) { + throw new DebuggerException(e); + } + fileSep = System.getProperty("file.separator"); + + if (os.equals("solaris")) { + setupDebuggerSolaris(); + } else if (os.equals("win32")) { + setupDebuggerWin32(); + } else if (os.equals("linux")) { + setupDebuggerLinux(); + } else { + // Add support for more operating systems here + throw new DebuggerException("Operating system " + os + " not yet supported"); + } + if (isServer) { + RemoteDebuggerServer remote = null; + try { + remote = new RemoteDebuggerServer(debugger); + } + catch (RemoteException rem) { + throw new DebuggerException(rem); + } + RMIHelper.rebind(serverID, remote); + } + } else { + // + // Remote mode (client attaching to server) + // + + // Create and install a security manager + + // FIXME: currently commented out because we were having + // security problems since we're "in the sun.* hierarchy" here. + // Perhaps a permissive policy file would work around this. In + // the long run, will probably have to move into com.sun.*. + + // if (System.getSecurityManager() == null) { + // System.setSecurityManager(new RMISecurityManager()); + // } + + connectRemoteDebugger(); + } + } + + private boolean setupVM() { + // We need to instantiate a HotSpotTypeDataBase on both the client + // and server machine. On the server it is only currently used to + // configure the Java primitive type sizes (which we should + // consider making constant). On the client it is used to + // configure the VM. + + try { + if (os.equals("solaris")) { + db = new HotSpotTypeDataBase(machDesc, new HotSpotSolarisVtblAccess(debugger, jvmLibNames), + debugger, jvmLibNames); + } else if (os.equals("win32")) { + db = new HotSpotTypeDataBase(machDesc, new Win32VtblAccess(debugger, jvmLibNames), + debugger, jvmLibNames); + } else if (os.equals("linux")) { + db = new HotSpotTypeDataBase(machDesc, new LinuxVtblAccess(debugger, jvmLibNames), + debugger, jvmLibNames); + } else { + throw new DebuggerException("OS \"" + os + "\" not yet supported (no VtblAccess implemented yet)"); + } + } + catch (NoSuchSymbolException e) { + e.printStackTrace(); + return false; + } + + if (startupMode != REMOTE_MODE) { + // Configure the debugger with the primitive type sizes just obtained from the VM + debugger.configureJavaPrimitiveTypeSizes(db.getJBooleanType().getSize(), + db.getJByteType().getSize(), + db.getJCharType().getSize(), + db.getJDoubleType().getSize(), + db.getJFloatType().getSize(), + db.getJIntType().getSize(), + db.getJLongType().getSize(), + db.getJShortType().getSize()); + } + + if (!isServer) { + // Do not initialize the VM on the server (unnecessary, since it's + // instantiated on the client) + VM.initialize(db, debugger); + } + + try { + jvmdi = new ServiceabilityAgentJVMDIModule(debugger, saLibNames); + if (jvmdi.canAttach()) { + jvmdi.attach(); + jvmdi.setCommandTimeout(6000); + debugPrintln("Attached to Serviceability Agent's JVMDI module."); + // Jog VM to suspended point with JVMDI module + resume(); + suspendJava(); + suspend(); + debugPrintln("Suspended all Java threads."); + } else { + debugPrintln("Could not locate SA's JVMDI module; skipping attachment"); + jvmdi = null; + } + } catch (Exception e) { + e.printStackTrace(); + jvmdi = null; + } + + return true; + } + + //-------------------------------------------------------------------------------- + // OS-specific debugger setup/connect routines + // + + // + // Solaris + // + + private void setupDebuggerSolaris() { + setupJVMLibNamesSolaris(); + String prop = System.getProperty("sun.jvm.hotspot.debugger.useProcDebugger"); + if (prop != null && !prop.equals("false")) { + ProcDebuggerLocal dbg = new ProcDebuggerLocal(null, true); + debugger = dbg; + attachDebugger(); + + // Set up CPU-dependent stuff + if (cpu.equals("x86")) { + machDesc = new MachineDescriptionIntelX86(); + } else if (cpu.equals("sparc")) { + int addressSize = dbg.getRemoteProcessAddressSize(); + if (addressSize == -1) { + throw new DebuggerException("Error occurred while trying to determine the remote process's address size"); + } + + if (addressSize == 32) { + machDesc = new MachineDescriptionSPARC32Bit(); + } else if (addressSize == 64) { + machDesc = new MachineDescriptionSPARC64Bit(); + } else { + throw new DebuggerException("Address size " + addressSize + " is not supported on SPARC"); + } + } else if (cpu.equals("amd64")) { + machDesc = new MachineDescriptionAMD64(); + } else { + throw new DebuggerException("Solaris only supported on sparc/sparcv9/x86/amd64"); + } + + dbg.setMachineDescription(machDesc); + return; + } else { + String dbxPathName; + String dbxPathPrefix; + String dbxSvcAgentDSOPathName; + String dbxSvcAgentDSOPathPrefix; + String[] dbxSvcAgentDSOPathNames = null; + + // use path names/prefixes specified on command + dbxPathName = System.getProperty("dbxPathName"); + if (dbxPathName == null) { + dbxPathPrefix = System.getProperty("dbxPathPrefix"); + if (dbxPathPrefix == null) { + dbxPathPrefix = defaultDbxPathPrefix; + } + dbxPathName = dbxPathPrefix + fileSep + os + fileSep + cpu + fileSep + "bin" + fileSep + "dbx"; + } + + dbxSvcAgentDSOPathName = System.getProperty("dbxSvcAgentDSOPathName"); + if (dbxSvcAgentDSOPathName != null) { + dbxSvcAgentDSOPathNames = new String[] { dbxSvcAgentDSOPathName } ; + } else { + dbxSvcAgentDSOPathPrefix = System.getProperty("dbxSvcAgentDSOPathPrefix"); + if (dbxSvcAgentDSOPathPrefix == null) { + dbxSvcAgentDSOPathPrefix = defaultDbxSvcAgentDSOPathPrefix; + } + if (cpu.equals("sparc")) { + dbxSvcAgentDSOPathNames = new String[] { + // FIXME: bad hack for SPARC v9. This is necessary because + // there are two dbx executables on SPARC, one for v8 and one + // for v9, and it isn't obvious how to tell the two apart + // using the dbx command line. See + // DbxDebuggerLocal.importDbxModule(). + dbxSvcAgentDSOPathPrefix + fileSep + os + fileSep + cpu + "v9" + fileSep + "lib" + fileSep + "libsvc_agent_dbx.so", + dbxSvcAgentDSOPathPrefix + fileSep + os + fileSep + cpu + fileSep + "lib" + fileSep + "libsvc_agent_dbx.so", + }; + } else { + dbxSvcAgentDSOPathNames = new String[] { + dbxSvcAgentDSOPathPrefix + fileSep + os + fileSep + cpu + fileSep + "lib" + fileSep + "libsvc_agent_dbx.so" + }; + } + } + // Note we do not use a cache for the local debugger in server + // mode; it's taken care of on the client side + DbxDebuggerLocal dbg = new DbxDebuggerLocal(null, dbxPathName, dbxSvcAgentDSOPathNames, !isServer); + debugger = dbg; + + attachDebugger(); + + // Set up CPU-dependent stuff + if (cpu.equals("x86")) { + machDesc = new MachineDescriptionIntelX86(); + } else if (cpu.equals("sparc")) { + int addressSize = dbg.getRemoteProcessAddressSize(); + if (addressSize == -1) { + throw new DebuggerException("Error occurred while trying to determine the remote process's address size. It's possible that the Serviceability Agent's dbx module failed to initialize. Examine the standard output and standard error streams from the dbx process for more information."); + } + + if (addressSize == 32) { + machDesc = new MachineDescriptionSPARC32Bit(); + } else if (addressSize == 64) { + machDesc = new MachineDescriptionSPARC64Bit(); + } else { + throw new DebuggerException("Address size " + addressSize + " is not supported on SPARC"); + } + } + + dbg.setMachineDescription(machDesc); + } + } + + private void connectRemoteDebugger() throws DebuggerException { + RemoteDebugger remote = + (RemoteDebugger) RMIHelper.lookup(debugServerID); + debugger = new RemoteDebuggerClient(remote); + machDesc = ((RemoteDebuggerClient) debugger).getMachineDescription(); + os = debugger.getOS(); + if (os.equals("solaris")) { + setupJVMLibNamesSolaris(); + } else if (os.equals("win32")) { + setupJVMLibNamesWin32(); + } else if (os.equals("linux")) { + setupJVMLibNamesLinux(); + } else { + throw new RuntimeException("Unknown OS type"); + } + + cpu = debugger.getCPU(); + } + + private void setupJVMLibNamesSolaris() { + jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so", "gamma_g" }; + saLibNames = new String[] { "libsa.so", "libsa_g.so" }; + } + + // + // Win32 + // + + private void setupDebuggerWin32() { + setupJVMLibNamesWin32(); + + if (cpu.equals("x86")) { + machDesc = new MachineDescriptionIntelX86(); + } else if (cpu.equals("amd64")) { + machDesc = new MachineDescriptionAMD64(); + } else if (cpu.equals("ia64")) { + machDesc = new MachineDescriptionIA64(); + } else { + throw new DebuggerException("Win32 supported under x86, amd64 and ia64 only"); + } + + // Note we do not use a cache for the local debugger in server + // mode; it will be taken care of on the client side (once remote + // debugging is implemented). + + if (System.getProperty("sun.jvm.hotspot.debugger.useWindbgDebugger") != null) { + debugger = new WindbgDebuggerLocal(machDesc, !isServer); + } else { + debugger = new Win32DebuggerLocal(machDesc, !isServer); + } + + attachDebugger(); + } + + private void setupJVMLibNamesWin32() { + jvmLibNames = new String[] { "jvm.dll", "jvm_g.dll" }; + saLibNames = new String[] { "sa.dll", "sa_g.dll" }; + } + + // + // Linux + // + + private void setupDebuggerLinux() { + setupJVMLibNamesLinux(); + + if (cpu.equals("x86")) { + machDesc = new MachineDescriptionIntelX86(); + } else if (cpu.equals("ia64")) { + machDesc = new MachineDescriptionIA64(); + } else if (cpu.equals("amd64")) { + machDesc = new MachineDescriptionAMD64(); + } else if (cpu.equals("sparc")) { + if (LinuxDebuggerLocal.getAddressSize()==8) { + machDesc = new MachineDescriptionSPARC64Bit(); + } else { + machDesc = new MachineDescriptionSPARC32Bit(); + } + } else { + throw new DebuggerException("Linux only supported on x86/ia64/amd64/sparc/sparc64"); + } + + // Note we do not use a cache for the local debugger in server + // mode; it will be taken care of on the client side (once remote + // debugging is implemented). + + debugger = new LinuxDebuggerLocal(machDesc, !isServer); + attachDebugger(); + } + + private void setupJVMLibNamesLinux() { + // same as solaris + setupJVMLibNamesSolaris(); + } + + /** Convenience routine which should be called by per-platform + debugger setup. Should not be called when startupMode is + REMOTE_MODE. */ + private void attachDebugger() { + if (startupMode == PROCESS_MODE) { + debugger.attach(pid); + } else if (startupMode == CORE_FILE_MODE) { + debugger.attach(executableName, coreFileName); + } else { + throw new DebuggerException("Should not call attach() for startupMode == " + startupMode); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/JavaLineNumberInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/JavaLineNumberInfo.java new file mode 100644 index 00000000000..24443fe2b00 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/JavaLineNumberInfo.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import sun.jvm.hotspot.oops.*; + +/** Wrapper class which describes line number information for Java + class files. The line number table is converted into this + representation on demand. These objects are then sorted by line + number for fast lookup when setting breakpoints in a particular + source file. */ + +public class JavaLineNumberInfo { + private InstanceKlass klass; + private Method method; + private int startBCI; + private int lineNumber; + + public JavaLineNumberInfo(InstanceKlass klass, + Method method, + int startBCI, + int lineNumber) { + this.klass = klass; + this.method = method; + this.startBCI = startBCI; + this.lineNumber = lineNumber; + } + + public InstanceKlass getKlass() { return klass; } + public Method getMethod() { return method; } + public int getStartBCI() { return startBCI; } + public int getLineNumber() { return lineNumber; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/Main.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/Main.java new file mode 100644 index 00000000000..f86c8225f55 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/Main.java @@ -0,0 +1,52 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; + +import sun.jvm.hotspot.ui.*; + +/** The main class for the BugSpot debugger. */ + +public class Main { + public static void main(String[] args) { + JFrame frame = new JFrame("BugSpot"); + frame.setSize(800, 600); + BugSpot db = new BugSpot(); + db.setMDIMode(true); + db.build(); + frame.setJMenuBar(db.getMenuBar()); + frame.getContentPane().add(db); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + + GraphicsUtilities.reshapeToAspectRatio(frame, + 4.0f/3.0f, 0.85f, Toolkit.getDefaultToolkit().getScreenSize()); + GraphicsUtilities.centerInContainer(frame, + Toolkit.getDefaultToolkit().getScreenSize()); + frame.show(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/PCFinder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/PCFinder.java new file mode 100644 index 00000000000..789f3eba70a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/PCFinder.java @@ -0,0 +1,96 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +/** Helper class for locating a program counter. Indicates the + confidence of the find. */ + +public class PCFinder { + public static final int LOW_CONFIDENCE = 1; + public static final int HIGH_CONFIDENCE = 2; + + public static class Info { + private String name; + private long offset; + private int confidence; + + public Info(String name, long offset, int confidence) { + this.name = name; + this.offset = offset; + this.confidence = confidence; + } + + /** May be null */ + public String getName() { return name; } + + /** If this is -1, a symbol could not be found, and the offset + should not be shown */ + public long getOffset() { return offset; } + + /** PCFinder.LOW_CONFIDENCE or PCFinder.HIGH_CONFIDENCE */ + public int getConfidence() { return confidence; } + } + + /** Passed loadobject may be null in which case the returned Info + object has low confidence */ + public static Info findPC(Address pc, LoadObject lo, CDebugger dbg) { + if (lo == null) { + return new Info(null, -1, LOW_CONFIDENCE); + } + + // First try debug info + BlockSym sym = lo.debugInfoForPC(pc); + while (sym != null) { + if (sym.isFunction()) { + // Highest confidence + return new Info(sym.toString(), pc.minus(sym.getAddress()), HIGH_CONFIDENCE); + } + } + + // Now try looking up symbol in loadobject + + // FIXME: must add support for mapfiles on Win32 and try looking + // up there first if possible. Should we hide that behind + // LoadObject.closestSymbolToPC and have the ClosestSymbol return + // confidence? I think so. On Solaris there is no notion of a + // mapfile, and the confidence for closestSymbolToPC will be high + // instead of low. + + int confidence = HIGH_CONFIDENCE; + + ClosestSymbol cs = lo.closestSymbolToPC(pc); + if (cs != null) { + // FIXME: currently low confidence (only on Win32) + return new Info(cs.getName() + "()", cs.getOffset(), LOW_CONFIDENCE); + } + + // Unknown location + return new Info(dbg.getNameOfFile(lo.getName()).toUpperCase() + + "! " + pc + "()", -1, HIGH_CONFIDENCE); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/PackageScanner.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/PackageScanner.java new file mode 100644 index 00000000000..5723d85c462 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/PackageScanner.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import java.io.*; + +/** Scans a .java file for the package that it is in. */ + +public class PackageScanner { + + public PackageScanner() { + } + + public String scan(String filename) { + return scan(new File(filename)); + } + + /** Returns the String comprising the package name of the classes in + this .java file. Returns the (non-null) empty string if any + error occurs or if the classes are in the unnamed package. */ + public String scan(File file) { + BufferedReader buf = null; + String res = ""; + try { + buf = new BufferedReader(new FileReader(file)); + StreamTokenizer tok = new StreamTokenizer(buf); + tok.slashStarComments(true); + tok.slashSlashComments(true); + if (tok.nextToken() != StreamTokenizer.TT_WORD) { + return res; + } + if (!tok.sval.equals("package")) { + return res; + } + if (tok.nextToken() != StreamTokenizer.TT_WORD) { + return res; + } + res = tok.sval; + return res; + } catch (FileNotFoundException e) { + return res; + } catch (IOException e) { + return res; + } finally { + try { + if (buf != null) { + buf.close(); + } + } catch (IOException e) { + } + } + } + + public static void main(String[] args) { + if (args.length != 1) { + usage(); + } + + System.out.println(new PackageScanner().scan(args[0])); + } + + private static void usage() { + System.err.println("Usage: java PackageScanner <.java file name>"); + System.err.println("Prints package the .java file is in to stdout."); + System.exit(1); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/RegisterPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/RegisterPanel.java new file mode 100644 index 00000000000..f9e8fc711ae --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/RegisterPanel.java @@ -0,0 +1,173 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import java.awt.*; +import java.util.*; +import javax.swing.*; +import javax.swing.table.*; + +import sun.jvm.hotspot.debugger.*; + +/** Displays registers in a window. FIXME: this will need more work to + understand and handle register windows. */ + +public class RegisterPanel extends JPanel { + private java.util.List/**/ registers; + private AbstractTableModel dataModel; + private boolean valid; + private boolean editable; + private String nullAddressString; + private ThreadProxy curThread; + private JTable table; + + static class RegisterInfo { + private String name; + private Address value; + + RegisterInfo(String name, Address value) { + this.name = name; + this.value = value; + } + + String getName() { return name; } + Address getValue() { return value; } + } + + public RegisterPanel() { + super(); + + registers = new ArrayList(); + + dataModel = new AbstractTableModel() { + public int getColumnCount() { return 2; } + public int getRowCount() { return registers.size(); } + public String getColumnName(int col) { + switch (col) { + case 0: + return "Register Name"; + case 1: + return "Register Value"; + default: + throw new RuntimeException("Index " + col + " out of bounds"); + } + } + public Object getValueAt(int row, int col) { + RegisterInfo info = (RegisterInfo) registers.get(row); + + switch (col) { + case 0: + return info.getName(); + case 1: + if (valid) { + Address val = info.getValue(); + if (val != null) { + return val; + } else { + return nullAddressString; + } + } else { + return "-"; + } + default: + throw new RuntimeException("Index (" + col + ", " + row + ") out of bounds"); + } + } + public boolean isCellEditable(int row, int col) { + if (col == 0) return false; + if (!valid) return false; + if (curThread == null) return false; + if (!curThread.canSetContext()) return false; + + // FIXME: add listener to watch for register changes + // return true; + return false; + } + }; + + // Build user interface + setLayout(new BorderLayout()); + table = new JTable(dataModel); + table.setCellSelectionEnabled(true); + table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + table.setDragEnabled(true); + JTableHeader header = table.getTableHeader(); + header.setReorderingAllowed(false); + JScrollPane scrollPane = new JScrollPane(table); + add(scrollPane, BorderLayout.CENTER); + } + + + /** Updates the register panel with the register set from the + specified thread. Call this when the process has been suspended + and the current thread has been set. FIXME: this interface will + need to change to support register windows. */ + public void update(ThreadProxy curThread) { + this.curThread = curThread; + ThreadContext context = curThread.getContext(); + editable = curThread.canSetContext(); + registers.clear(); + for (int i = 0; i < context.getNumRegisters(); i++) { + String name = context.getRegisterName(i); + Address addr = context.getRegisterAsAddress(i); + if ((nullAddressString == null) && (addr != null)) { + String addrStr = addr.toString(); + StringBuffer buf = new StringBuffer(); + buf.append("0x"); + int len = addrStr.length() - 2; + for (int j = 0; j < len; j++) { + buf.append("0"); + } + nullAddressString = buf.toString(); + } + registers.add(new RegisterInfo(name, addr)); + } + valid = true; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + dataModel.fireTableDataChanged(); + } + }); + } + + /** Clears the registers' values. Call this when the processs has + been resumed. */ + public void clear() { + valid = false; + nullAddressString = null; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + dataModel.fireTableDataChanged(); + } + }); + } + + public void setFont(Font font) { + super.setFont(font); + if (table != null) { + table.setFont(font); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/StackTraceEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/StackTraceEntry.java new file mode 100644 index 00000000000..6654033d731 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/StackTraceEntry.java @@ -0,0 +1,87 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/** This class describes a frame in a stack trace. It abstracts over + C/C++ and Java frames. */ + +public class StackTraceEntry { + private CFrame cFrame; + private CDebugger dbg; + private JavaVFrame javaFrame; + private String value; // What is displayed in a stack trace + // For merging C and Java stack traces. + // For more precise stack traces, should probably have a way to + // convert a CFrame to a sun.jvm.hotspot.runtime.Frame. For now, + // doing similar algorithm to jdbx (which does not have intimate + // knowledge of the VM). + private boolean isUnknownCFrame; + + public StackTraceEntry(CFrame cFrame, CDebugger dbg) { + this.cFrame = cFrame; + this.dbg = dbg; + computeValue(); + } + + public StackTraceEntry(JavaVFrame javaFrame) { + this.javaFrame = javaFrame; + computeValue(); + } + + public boolean isCFrame() { return (cFrame != null); } + public boolean isJavaFrame() { return (javaFrame != null); } + public CFrame getCFrame() { return cFrame; } + public JavaVFrame getJavaFrame() { return javaFrame; } + public boolean isUnknownCFrame() { return isUnknownCFrame; } + public String toString() { + return value; + } + + private void computeValue() { + isUnknownCFrame = true; + value = ""; + if (cFrame != null) { + PCFinder.Info info = PCFinder.findPC(cFrame.pc(), cFrame.loadObjectForPC(), dbg); + if (info.getName() != null) { + value = "(C) " + info.getName(); + isUnknownCFrame = false; + if (info.getConfidence() == PCFinder.LOW_CONFIDENCE) { + value = value + " (?)"; + } + if (info.getOffset() >= 0) { + value = value + " + 0x" + Long.toHexString(info.getOffset()); + } + } + } else if (javaFrame != null) { + isUnknownCFrame = false; + Method m = javaFrame.getMethod(); + value = "(J) " + m.externalNameAndSignature(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/StackTracePanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/StackTracePanel.java new file mode 100644 index 00000000000..5c619c4007f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/StackTracePanel.java @@ -0,0 +1,115 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.ui.*; + +/** This panel contains a ListBox with all of the stack frames in a + given thread. When a given entry is selected, an event is + fired. */ + +public class StackTracePanel extends JPanel { + public interface Listener { + public void frameChanged(CFrame fr, JavaVFrame jfr); + } + + class Model extends AbstractListModel implements ComboBoxModel { + private Object selectedItem; + public Object getElementAt(int index) { + if (trace == null) return null; + return trace.get(index); + } + public int getSize() { + if (trace == null) return 0; + return trace.size(); + } + public Object getSelectedItem() { + return selectedItem; + } + public void setSelectedItem(Object item) { + selectedItem = item; + } + public void dataChanged() { + fireContentsChanged(this, 0, trace.size()); + } + } + + private java.util.List trace; + private Model model; + private JComboBox list; + private java.util.List listeners; + + public StackTracePanel() { + super(); + + model = new Model(); + + // Build user interface + setLayout(new BorderLayout()); + setBorder(GraphicsUtilities.newBorder(5)); + list = new JComboBox(model); + list.setPrototypeDisplayValue("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"); + add(list, BorderLayout.CENTER); + + // Add selection listener + list.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + fireFrameChanged(); + } + } + }); + } + + /** Takes a List of StackTraceEntry objects */ + public void setTrace(java.util.List trace) { + this.trace = trace; + model.dataChanged(); + list.setSelectedIndex(0); + fireFrameChanged(); + } + + public void addListener(Listener listener) { + if (listeners == null) { + listeners = new ArrayList(); + } + listeners.add(listener); + } + + protected void fireFrameChanged() { + if (listeners != null) { + StackTraceEntry entry = (StackTraceEntry) trace.get(list.getSelectedIndex()); + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + ((Listener) iter.next()).frameChanged(entry.getCFrame(), entry.getJavaFrame()); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/ThreadListPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/ThreadListPanel.java new file mode 100644 index 00000000000..eb76f9bf03e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/ThreadListPanel.java @@ -0,0 +1,237 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; +import javax.swing.table.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.ui.*; + +// NOTE: this class was not placed in sun.jvm.hotspot.ui to prevent +// mixing components designed for C and C++ debugging with the ones +// that work with the core serviceability agent functionality (which +// does not require that the CDebugger interface be implemented). + +/** The ThreadListPanel is used for C and C++ debugging and can + visualize all threads in the target process. The caller passes in + a CDebugger attached to the target process and can request that + JavaThreads' associations with these underlying threads be + displayed; this option is only valid when attached to a HotSpot + JVM and when the {@link sun.jvm.hotspot.runtime.VM} has been + initialized. */ + +public class ThreadListPanel extends JPanel { + /** Listener which can be added to receive "Set Focus" events */ + public static interface Listener { + /** ThreadProxy will always be provided; JavaThread will only be + present if displayJavaThreads was specified in the constructor + for the panel and the thread was a JavaThread. */ + public void setFocus(ThreadProxy thread, JavaThread jthread); + } + + static class ThreadInfo { + private ThreadProxy thread; + // Distinguish between PC == null and no top frame + private boolean gotPC; + private Address pc; + private String location; + private JavaThread javaThread; + private String javaThreadName; + + public ThreadInfo(ThreadProxy thread, CDebugger dbg, JavaThread jthread) { + this.thread = thread; + this.location = ""; + CFrame fr = dbg.topFrameForThread(thread); + if (fr != null) { + gotPC = true; + pc = fr.pc(); + PCFinder.Info info = PCFinder.findPC(pc, fr.loadObjectForPC(), dbg); + if (info.getName() != null) { + location = info.getName(); + if (info.getConfidence() == PCFinder.LOW_CONFIDENCE) { + location = location + " (?)"; + } + if (info.getOffset() < 0) { + location = location + " + 0x" + Long.toHexString(info.getOffset()); + } + } + } + if (jthread != null) { + javaThread = jthread; + javaThreadName = jthread.getThreadName(); + } + } + + public ThreadProxy getThread() { return thread; } + public boolean hasPC() { return gotPC; } + public Address getPC() { return pc; } + public String getLocation() { return location; } + public boolean isJavaThread() { return (javaThread != null); } + public JavaThread getJavaThread() { return javaThread; } + public String getJavaThreadName() { return javaThreadName; } + } + + // List + private java.util.List threadList; + private JTable table; + private AbstractTableModel dataModel; + // List + private java.util.List listeners; + + /** Takes a CDebugger from which the thread list is queried. + displayJavaThreads must only be set to true if the debugger is + attached to a HotSpot JVM and if the VM has already been + initialized. */ + public ThreadListPanel(CDebugger dbg, final boolean displayJavaThreads) { + super(); + + Map threadToJavaThreadMap = null; + if (displayJavaThreads) { + // Collect Java threads from virtual machine and insert them in + // table for later querying + threadToJavaThreadMap = new HashMap(); + Threads threads = VM.getVM().getThreads(); + for (JavaThread thr = threads.first(); thr != null; thr = thr.next()) { + threadToJavaThreadMap.put(thr.getThreadProxy(), thr); + } + } + + java.util.List/**/ threads = dbg.getThreadList(); + threadList = new ArrayList(threads.size()); + for (Iterator iter = threads.iterator(); iter.hasNext(); ) { + ThreadProxy thr = (ThreadProxy) iter.next(); + JavaThread jthr = null; + if (displayJavaThreads) { + jthr = (JavaThread) threadToJavaThreadMap.get(thr); + } + threadList.add(new ThreadInfo(thr, dbg, jthr)); + } + + // Thread ID, current PC, current symbol, Java Thread, [Java thread name] + dataModel = new AbstractTableModel() { + public int getColumnCount() { return (displayJavaThreads ? 5 : 3); } + public int getRowCount() { return threadList.size(); } + public String getColumnName(int col) { + switch (col) { + case 0: + return "Thread ID"; + case 1: + return "PC"; + case 2: + return "Location"; + case 3: + return "Java?"; + case 4: + return "Java Thread Name"; + default: + throw new RuntimeException("Index " + col + " out of bounds"); + } + } + public Object getValueAt(int row, int col) { + ThreadInfo info = (ThreadInfo) threadList.get(row); + + switch (col) { + case 0: + return info.getThread(); + case 1: + { + if (info.hasPC()) { + return info.getPC(); + } + return ""; + } + case 2: + return info.getLocation(); + case 3: + if (info.isJavaThread()) { + return "Yes"; + } else { + return ""; + } + case 4: + if (info.isJavaThread()) { + return info.getJavaThreadName(); + } else { + return ""; + } + default: + throw new RuntimeException("Index (" + col + ", " + row + ") out of bounds"); + } + } + }; + + // Build user interface + setLayout(new BorderLayout()); + table = new JTable(dataModel); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + JTableHeader header = table.getTableHeader(); + header.setReorderingAllowed(false); + table.setRowSelectionAllowed(true); + table.setColumnSelectionAllowed(false); + JScrollPane scrollPane = new JScrollPane(table); + add(scrollPane, BorderLayout.CENTER); + if (threadList.size() > 0) { + table.setRowSelectionInterval(0, 0); + } + + JButton button = new JButton("Set Focus"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int i = table.getSelectedRow(); + if (i < 0) { + return; + } + ThreadInfo info = (ThreadInfo) threadList.get(i); + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + ((Listener) iter.next()).setFocus(info.getThread(), info.getJavaThread()); + } + } + }); + JPanel focusPanel = new JPanel(); + focusPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); + focusPanel.setLayout(new BoxLayout(focusPanel, BoxLayout.Y_AXIS)); + focusPanel.add(Box.createGlue()); + focusPanel.add(button); + focusPanel.add(Box.createGlue()); + add(focusPanel, BorderLayout.EAST); + + // FIXME: make listener model for the debugger so if the user + // specifies a mapfile for or path to a given DSO later we can + // update our state + } + + public void addListener(Listener l) { + if (listeners == null) { + listeners = new ArrayList(); + } + listeners.add(l); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/VariablePanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/VariablePanel.java new file mode 100644 index 00000000000..07158486e89 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/VariablePanel.java @@ -0,0 +1,252 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot; + +import java.awt.*; +import javax.swing.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.bugspot.tree.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.ui.tree.*; +import sun.jvm.hotspot.ui.treetable.*; + +/** Manages display of a set of local variables in a frame, or the + contents of the "this" pointer */ + +public class VariablePanel extends JPanel { + private JTreeTable treeTable; + private SimpleTreeTableModel model; + private SimpleTreeGroupNode root; + + public VariablePanel() { + super(); + + model = new SimpleTreeTableModel(); + model.setValuesEditable(false); + root = new SimpleTreeGroupNode(); + model.setRoot(root); + treeTable = new JTreeTable(model); + treeTable.setRootVisible(false); + treeTable.setShowsRootHandles(true); + treeTable.setShowsIcons(false); + treeTable.setTreeEditable(false); + treeTable.getTableHeader().setReorderingAllowed(false); + treeTable.setCellSelectionEnabled(true); + treeTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + treeTable.setDragEnabled(true); + JScrollPane sp = new JScrollPane(treeTable); + sp.getViewport().setBackground(Color.white); + + setLayout(new BorderLayout()); + add(sp, BorderLayout.CENTER); + } + + /** Clear the contents of this VariablePanel */ + public void clear() { + root.removeAllChildren(); + model.fireTreeStructureChanged(); + } + + /** Update the contents of this VariablePanel from the given CFrame */ + public void update(CFrame fr) { + // Collect locals + CCollector coll = new CCollector(); + fr.iterateLocals(coll); + update(coll); + } + + /** Update the contents of this VariablePanel from the given JavaVFrame */ + public void update(JavaVFrame jfr) { + Method m = jfr.getMethod(); + if (!m.hasLocalVariableTable()) { + return; + } + int bci = jfr.getBCI(); + // Get local variable table + LocalVariableTableElement[] locals = m.getLocalVariableTable(); + // Get locals as StackValueCollection + StackValueCollection coll = jfr.getLocals(); + root.removeAllChildren(); + // See which locals are live + for (int i = 0; i < locals.length; i++) { + LocalVariableTableElement local = locals[i]; + if (local.getStartBCI() <= bci && bci < local.getStartBCI() + local.getLength()) { + // Valid; add it + SimpleTreeNode node = null; + Symbol name = null; + try { + name = m.getConstants().getSymbolAt(local.getNameCPIndex()); + if (name == null) { + System.err.println("Null name at slot " + + local.getNameCPIndex() + + " for local variable at slot " + + local.getSlot()); + continue; + } + } catch (Exception e) { + System.err.println("Unable to fetch name at slot " + + local.getNameCPIndex() + + " for local variable at slot " + + local.getSlot()); + e.printStackTrace(); + continue; + } + sun.jvm.hotspot.oops.NamedFieldIdentifier f = + new sun.jvm.hotspot.oops.NamedFieldIdentifier(name.asString()); + Symbol descriptor = null; + try { + descriptor = m.getConstants().getSymbolAt(local.getDescriptorCPIndex()); + } catch (Exception e) { + System.err.println("Unable to fetch descriptor at slot " + + local.getDescriptorCPIndex() + + " for local variable " + f.getName() + + " at slot " + local.getSlot()); + e.printStackTrace(); + continue; + } + + if (descriptor != null) { + switch (descriptor.getByteAt(0)) { + case 'F': { + node = new sun.jvm.hotspot.ui.tree.FloatTreeNodeAdapter(coll.floatAt(local.getSlot()), f, true); + break; + } + case 'D': { + node = new sun.jvm.hotspot.ui.tree.DoubleTreeNodeAdapter(coll.doubleAt(local.getSlot()), f, true); + break; + } + case 'C': { + node = new sun.jvm.hotspot.ui.tree.CharTreeNodeAdapter((char) coll.intAt(local.getSlot()), f, true); + break; + } + case 'B': + case 'S': + case 'I': { + node = new sun.jvm.hotspot.ui.tree.LongTreeNodeAdapter(coll.intAt(local.getSlot()), f, true); + break; + } + case 'Z': { + node = new sun.jvm.hotspot.ui.tree.BooleanTreeNodeAdapter( + ((coll.intAt(local.getSlot()) != 0) ? true : false), f, true + ); + break; + } + case 'J': { + node = new sun.jvm.hotspot.ui.tree.LongTreeNodeAdapter(coll.longAt(local.getSlot()), f, true); + break; + } + default: { + try { + node = new sun.jvm.hotspot.ui.tree.OopTreeNodeAdapter( + VM.getVM().getObjectHeap().newOop(coll.oopHandleAt(local.getSlot())), f, true + ); + } catch (AddressException e) { + node = new sun.jvm.hotspot.ui.tree.FieldTreeNodeAdapter(f, true) { + public int getChildCount() { return 0; } + public SimpleTreeNode getChild(int i) { return null; } + public boolean isLeaf() { return false; } + public int getIndexOfChild(SimpleTreeNode child) { return 0; } + public String getValue() { + return ""; + } + }; + } + break; + } + } + if (node != null) { + root.addChild(node); + } + } + } + } + + model.fireTreeStructureChanged(); + } + + /** Update the contents of this VariablePanel from the given "this" + pointer of the given type */ + public void update(Address thisAddr, Type type) { + // Collect fields + CCollector coll = new CCollector(); + type.iterateObject(thisAddr, coll); + update(coll); + } + + private void update(CCollector coll) { + root.removeAllChildren(); + for (int i = 0; i < coll.getNumChildren(); i++) { + root.addChild(coll.getChild(i)); + } + model.fireTreeStructureChanged(); + } + + static class CCollector extends DefaultObjectVisitor { + private java.util.List children; + + public CCollector() { + children = new ArrayList(); + } + + public int getNumChildren() { + return children.size(); + } + + public SimpleTreeNode getChild(int i) { + return (SimpleTreeNode) children.get(i); + } + + public void doBit(sun.jvm.hotspot.debugger.cdbg.FieldIdentifier f, long val) { + children.add(new sun.jvm.hotspot.bugspot.tree.LongTreeNodeAdapter(val, f, true)); + } + public void doInt(sun.jvm.hotspot.debugger.cdbg.FieldIdentifier f, long val) { + children.add(new sun.jvm.hotspot.bugspot.tree.LongTreeNodeAdapter(val, f, true)); + } + public void doEnum(sun.jvm.hotspot.debugger.cdbg.FieldIdentifier f, long val, String enumName) { + children.add(new sun.jvm.hotspot.bugspot.tree.EnumTreeNodeAdapter(enumName, val, f, true)); + } + public void doFloat(sun.jvm.hotspot.debugger.cdbg.FieldIdentifier f, float val) { + children.add(new sun.jvm.hotspot.bugspot.tree.FloatTreeNodeAdapter(val, f, true)); + } + public void doDouble(sun.jvm.hotspot.debugger.cdbg.FieldIdentifier f, double val) { + children.add(new sun.jvm.hotspot.bugspot.tree.DoubleTreeNodeAdapter(val, f, true)); + } + public void doPointer(sun.jvm.hotspot.debugger.cdbg.FieldIdentifier f, Address val) { + children.add(new sun.jvm.hotspot.bugspot.tree.AddressTreeNodeAdapter(val, f, true)); + } + public void doArray(sun.jvm.hotspot.debugger.cdbg.FieldIdentifier f, Address val) { + children.add(new sun.jvm.hotspot.bugspot.tree.AddressTreeNodeAdapter(val, f, true)); + } + public void doRef(sun.jvm.hotspot.debugger.cdbg.FieldIdentifier f, Address val) { + children.add(new sun.jvm.hotspot.bugspot.tree.AddressTreeNodeAdapter(val, f, true)); + } + public void doCompound(sun.jvm.hotspot.debugger.cdbg.FieldIdentifier f, Address val) { + children.add(new sun.jvm.hotspot.bugspot.tree.ObjectTreeNodeAdapter(val, f, true)); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/AddressTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/AddressTreeNodeAdapter.java new file mode 100644 index 00000000000..69ccd6e7104 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/AddressTreeNodeAdapter.java @@ -0,0 +1,67 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot.tree; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.ui.tree.SimpleTreeNode; + +/** Encapsulates a float value in a tree handled by SimpleTreeModel */ + +public class AddressTreeNodeAdapter extends FieldTreeNodeAdapter { + private Address val; + + public AddressTreeNodeAdapter(Address val, FieldIdentifier id) { + this(val, id, false); + } + + public AddressTreeNodeAdapter(Address val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + if (val != null) { + return val.toString(); + } + return "NULL"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/DoubleTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/DoubleTreeNodeAdapter.java new file mode 100644 index 00000000000..3b777f21cc6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/DoubleTreeNodeAdapter.java @@ -0,0 +1,63 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot.tree; + +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.ui.tree.SimpleTreeNode; + +/** Encapsulates a double value in a tree handled by SimpleTreeModel */ + +public class DoubleTreeNodeAdapter extends FieldTreeNodeAdapter { + private double val; + + public DoubleTreeNodeAdapter(double val, FieldIdentifier id) { + this(val, id, false); + } + + public DoubleTreeNodeAdapter(double val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + return Double.toString(val); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/EnumTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/EnumTreeNodeAdapter.java new file mode 100644 index 00000000000..4c30502a8f3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/EnumTreeNodeAdapter.java @@ -0,0 +1,69 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot.tree; + +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.ui.tree.SimpleTreeNode; + +/** Encapsulates an enumerated value in a tree handled by SimpleTreeModel */ + +public class EnumTreeNodeAdapter extends FieldTreeNodeAdapter { + private long val; + private String enumName; + + public EnumTreeNodeAdapter(String enumName, long val, FieldIdentifier id) { + this(enumName, val, id, false); + } + + public EnumTreeNodeAdapter(String enumName, long val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.enumName = enumName; + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + if (enumName != null) { + return enumName; + } else { + return Long.toString(val); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/FieldTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/FieldTreeNodeAdapter.java new file mode 100644 index 00000000000..6f307bfc498 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/FieldTreeNodeAdapter.java @@ -0,0 +1,73 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot.tree; + +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.ui.tree.SimpleTreeNode; + +/** Abstract base class for all adapters for fields of C/C++ objects */ + +public abstract class FieldTreeNodeAdapter implements SimpleTreeNode { + private FieldIdentifier id; + private boolean treeTableMode; + + /** The identifier may be null, i.e., for the root of the tree */ + public FieldTreeNodeAdapter(FieldIdentifier id, boolean treeTableMode) { + this.id = id; + this.treeTableMode = treeTableMode; + } + + public FieldIdentifier getID() { + return id; + } + + /** Defaults to false in subclasses */ + public boolean getTreeTableMode() { + return treeTableMode; + } + + public Type getType() { + return getID().getType(); + } + + public String getName() { + if (getID() != null) { + return getID().toString(); + } + return ""; + } + + public String toString() { + if (treeTableMode) { + return getName(); + } else { + if (getID() != null) { + return getName() + ": " + getValue(); + } else { + return getValue(); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/FloatTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/FloatTreeNodeAdapter.java new file mode 100644 index 00000000000..7dbb606da9f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/FloatTreeNodeAdapter.java @@ -0,0 +1,63 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot.tree; + +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.ui.tree.SimpleTreeNode; + +/** Encapsulates a float value in a tree handled by SimpleTreeModel */ + +public class FloatTreeNodeAdapter extends FieldTreeNodeAdapter { + private float val; + + public FloatTreeNodeAdapter(float val, FieldIdentifier id) { + this(val, id, false); + } + + public FloatTreeNodeAdapter(float val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + return Float.toString(val); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/LongTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/LongTreeNodeAdapter.java new file mode 100644 index 00000000000..7a009c3b79a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/LongTreeNodeAdapter.java @@ -0,0 +1,63 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot.tree; + +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.ui.tree.SimpleTreeNode; + +/** Encapsulates a long value in a tree handled by SimpleTreeModel */ + +public class LongTreeNodeAdapter extends FieldTreeNodeAdapter { + private long val; + + public LongTreeNodeAdapter(long val, FieldIdentifier id) { + this(val, id, false); + } + + public LongTreeNodeAdapter(long val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + return Long.toString(val); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/ObjectTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/ObjectTreeNodeAdapter.java new file mode 100644 index 00000000000..8ebc004efdf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/bugspot/tree/ObjectTreeNodeAdapter.java @@ -0,0 +1,216 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.bugspot.tree; + +import java.io.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.ui.tree.SimpleTreeNode; + +/** An adapter class which allows C/C++ objects to be displayed in a + tree via the SimpleTreeNode interface. */ + +public class ObjectTreeNodeAdapter extends FieldTreeNodeAdapter { + // Address of object + private Address addr; + + /** The address may be null (for object fields of objcets which are + null). The FieldIdentifier should not be null. treeTableMode + defaults to false. */ + public ObjectTreeNodeAdapter(Address addr, FieldIdentifier id) { + this(addr, id, false); + } + + /** The address may be null (for object fields of objcets which are + null). The FieldIdentifier should not be null. */ + public ObjectTreeNodeAdapter(Address addr, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.addr = addr; + } + + public int getChildCount() { + if (addr == null) { + return 0; + } + + Counter c = new Counter(); + getType().iterateObject(addr, c); + return c.getNumFields(); + } + + public SimpleTreeNode getChild(int index) { + if (addr == null) { + return null; + } + + Fetcher f = new Fetcher(index); + getType().iterateObject(addr, f); + return f.getChild(); + } + + public boolean isLeaf() { + return (addr == null); + } + + public int getIndexOfChild(SimpleTreeNode child) { + FieldIdentifier id = ((FieldTreeNodeAdapter) child).getID(); + Finder f = new Finder(id); + getType().iterateObject(addr, f); + return f.getIndex(); + } + + public String getValue() { + if (addr != null) { + return addr.toString(); + } + return "NULL"; + } + + /** Should be used only once, then have the number of fields + fetched. */ + static class Counter extends DefaultObjectVisitor { + private int numFields; + + public int getNumFields() { + return numFields; + } + + public void doBit(FieldIdentifier f, long val) { ++numFields; } + public void doInt(FieldIdentifier f, long val) { ++numFields; } + public void doEnum(FieldIdentifier f, long val, String enumName) { ++numFields; } + public void doFloat(FieldIdentifier f, float val) { ++numFields; } + public void doDouble(FieldIdentifier f, double val) { ++numFields; } + public void doPointer(FieldIdentifier f, Address val) { ++numFields; } + public void doArray(FieldIdentifier f, Address val) { ++numFields; } + public void doRef(FieldIdentifier f, Address val) { ++numFields; } + public void doCompound(FieldIdentifier f, Address addr) { ++numFields; } + } + + /** Creates a new SimpleTreeNode for the given field. */ + class Fetcher extends DefaultObjectVisitor { + private int index; + private int curField; + private SimpleTreeNode child; + + public Fetcher(int index) { + this.index = index; + } + + public SimpleTreeNode getChild() { + return child; + } + + public void doBit(FieldIdentifier f, long val) { + if (curField == index) { + child = new LongTreeNodeAdapter(val, f, getTreeTableMode()); + } + ++curField; + } + + public void doInt(FieldIdentifier f, long val) { + if (curField == index) { + child = new LongTreeNodeAdapter(val, f, getTreeTableMode()); + } + ++curField; + } + + public void doEnum(FieldIdentifier f, long val, String enumName) { + if (curField == index) { + child = new EnumTreeNodeAdapter(enumName, val, f, getTreeTableMode()); + } + ++curField; + } + + public void doFloat(FieldIdentifier f, float val) { + if (curField == index) { + child = new FloatTreeNodeAdapter(val, f, getTreeTableMode()); + } + ++curField; + } + + public void doDouble(FieldIdentifier f, double val) { + if (curField == index) { + child = new DoubleTreeNodeAdapter(val, f, getTreeTableMode()); + } + ++curField; + } + + public void doPointer(FieldIdentifier f, Address val) { + if (curField == index) { + child = new AddressTreeNodeAdapter(val, f, getTreeTableMode()); + } + ++curField; + } + + public void doArray(FieldIdentifier f, Address val) { + if (curField == index) { + child = new AddressTreeNodeAdapter(val, f, getTreeTableMode()); + } + ++curField; + } + + public void doRef(FieldIdentifier f, Address val) { + if (curField == index) { + child = new AddressTreeNodeAdapter(val, f, getTreeTableMode()); + } + ++curField; + } + + public void doCompound(FieldIdentifier f, Address val) { + if (curField == index) { + child = new ObjectTreeNodeAdapter(val, f, getTreeTableMode()); + } + ++curField; + } + } + + /** Finds the index of the given FieldIdentifier. */ + static class Finder extends DefaultObjectVisitor { + private FieldIdentifier id; + private int curField; + private int index = -1; + + public Finder(FieldIdentifier id) { + this.id = id; + } + + /** Returns -1 if not found */ + public int getIndex() { + return index; + } + + public void doBit(FieldIdentifier f, long val) { if (f.equals(id)) { index = curField; } ++curField; } + public void doInt(FieldIdentifier f, long val) { if (f.equals(id)) { index = curField; } ++curField; } + public void doEnum(FieldIdentifier f, long val, + String enumName) { if (f.equals(id)) { index = curField; } ++curField; } + public void doFloat(FieldIdentifier f, float val) { if (f.equals(id)) { index = curField; } ++curField; } + public void doDouble(FieldIdentifier f, double val) { if (f.equals(id)) { index = curField; } ++curField; } + public void doPointer(FieldIdentifier f, Address val) { if (f.equals(id)) { index = curField; } ++curField; } + public void doArray(FieldIdentifier f, Address val) { if (f.equals(id)) { index = curField; } ++curField; } + public void doRef(FieldIdentifier f, Address val) { if (f.equals(id)) { index = curField; } ++curField; } + public void doCompound(FieldIdentifier f, + Address val) { if (f.equals(id)) { index = curField; } ++curField; } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/c1/Runtime1.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/c1/Runtime1.java new file mode 100644 index 00000000000..3b3819f30c5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/c1/Runtime1.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.c1; + +import java.util.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** Currently a minimal port to get C1 frame traversal working */ + +public class Runtime1 { + private static Field blobsField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("Runtime1"); + + blobsField = type.getField("_blobs"); + } + + public Runtime1() { + } + + /** FIXME: consider making argument "type-safe" in Java port */ + public Address entryFor(int id) { + return blobFor(id).instructionsBegin(); + } + + /** FIXME: consider making argument "type-safe" in Java port */ + public CodeBlob blobFor(int id) { + Address blobAddr = blobsField.getStaticFieldAddress().getAddressAt(id * VM.getVM().getAddressSize()); + return VM.getVM().getCodeCache().createCodeBlobWrapper(blobAddr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/BufferBlob.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/BufferBlob.java new file mode 100644 index 00000000000..cedefcfa4b5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/BufferBlob.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class BufferBlob extends CodeBlob { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("BufferBlob"); + + // FIXME: add any needed fields + } + + public BufferBlob(Address addr) { + super(addr); + } + + public boolean isBufferBlob() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeBlob.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeBlob.java new file mode 100644 index 00000000000..525d3317f98 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeBlob.java @@ -0,0 +1,240 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.asm.x86.*; +import sun.jvm.hotspot.compiler.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class CodeBlob extends VMObject { + private static AddressField nameField; + private static CIntegerField sizeField; + private static CIntegerField headerSizeField; + private static CIntegerField relocationSizeField; + private static CIntegerField instructionsOffsetField; + private static CIntegerField frameCompleteOffsetField; + private static CIntegerField dataOffsetField; + private static CIntegerField oopsOffsetField; + private static CIntegerField oopsLengthField; + private static CIntegerField frameSizeField; + private static AddressField oopMapsField; + + // Only used by server compiler on x86; computed over in SA rather + // than relying on computation in target VM + private static final int NOT_YET_COMPUTED = -2; + private static final int UNDEFINED = -1; + private int linkOffset = NOT_YET_COMPUTED; + private static int matcherInterpreterFramePointerReg; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("CodeBlob"); + + nameField = type.getAddressField("_name"); + sizeField = type.getCIntegerField("_size"); + headerSizeField = type.getCIntegerField("_header_size"); + relocationSizeField = type.getCIntegerField("_relocation_size"); + frameCompleteOffsetField = type.getCIntegerField("_frame_complete_offset"); + instructionsOffsetField = type.getCIntegerField("_instructions_offset"); + dataOffsetField = type.getCIntegerField("_data_offset"); + oopsOffsetField = type.getCIntegerField("_oops_offset"); + oopsLengthField = type.getCIntegerField("_oops_length"); + frameSizeField = type.getCIntegerField("_frame_size"); + oopMapsField = type.getAddressField("_oop_maps"); + + if (VM.getVM().isServerCompiler()) { + matcherInterpreterFramePointerReg = + db.lookupIntConstant("Matcher::interpreter_frame_pointer_reg").intValue(); + } + } + + public CodeBlob(Address addr) { + super(addr); + } + + // Typing + public boolean isBufferBlob() { return false; } + public boolean isNMethod() { return false; } + public boolean isRuntimeStub() { return false; } + public boolean isDeoptimizationStub() { return false; } + public boolean isUncommonTrapStub() { return false; } + public boolean isExceptionStub() { return false; } + public boolean isSafepointStub() { return false; } + + // Fine grain nmethod support: isNmethod() == isJavaMethod() || isNativeMethod() || isOSRMethod() + public boolean isJavaMethod() { return false; } + public boolean isNativeMethod() { return false; } + /** On-Stack Replacement method */ + public boolean isOSRMethod() { return false; } + + // Boundaries + public Address headerBegin() { + return addr; + } + + public Address headerEnd() { + return addr.addOffsetTo(headerSizeField.getValue(addr)); + } + + // FIXME: add RelocInfo + // public RelocInfo relocationBegin(); + // public RelocInfo relocationEnd(); + + public Address instructionsBegin() { + return headerBegin().addOffsetTo(instructionsOffsetField.getValue(addr)); + } + + public Address instructionsEnd() { + return headerBegin().addOffsetTo(dataOffsetField.getValue(addr)); + } + + public Address dataBegin() { + return headerBegin().addOffsetTo(dataOffsetField.getValue(addr)); + } + + public Address dataEnd() { + return headerBegin().addOffsetTo(sizeField.getValue(addr)); + } + + public Address oopsBegin() { + return headerBegin().addOffsetTo(oopsOffsetField.getValue(addr)); + } + + public Address oopsEnd() { + return oopsBegin().addOffsetTo(getOopsLength()); + } + + // Offsets + public int getRelocationOffset() { return (int) headerSizeField.getValue(addr); } + public int getInstructionsOffset() { return (int) instructionsOffsetField.getValue(addr); } + public int getDataOffset() { return (int) dataOffsetField.getValue(addr); } + public int getOopsOffset() { return (int) oopsOffsetField.getValue(addr); } + + // Sizes + public int getSize() { return (int) sizeField.getValue(addr); } + public int getHeaderSize() { return (int) headerSizeField.getValue(addr); } + // FIXME: add getRelocationSize() + public int getInstructionsSize() { return (int) instructionsEnd().minus(instructionsBegin()); } + public int getDataSize() { return (int) dataEnd().minus(dataBegin()); } + + // Containment + public boolean blobContains(Address addr) { return headerBegin().lessThanOrEqual(addr) && dataEnd().greaterThan(addr); } + // FIXME: add relocationContains + public boolean instructionsContains(Address addr) { return instructionsBegin().lessThanOrEqual(addr) && instructionsEnd().greaterThan(addr); } + public boolean dataContains(Address addr) { return dataBegin().lessThanOrEqual(addr) && dataEnd().greaterThan(addr); } + public boolean oopsContains(Address addr) { return oopsBegin().lessThanOrEqual(addr) && oopsEnd().greaterThan(addr); } + public boolean contains(Address addr) { return instructionsContains(addr); } + public boolean isFrameCompleteAt(Address a) { return instructionsContains(a) && a.minus(instructionsBegin()) >= frameCompleteOffsetField.getValue(addr); } + + /** Support for oops in scopes and relocs. Note: index 0 is reserved for null. */ + public OopHandle getOopAt(int index) { + if (index == 0) return null; + if (Assert.ASSERTS_ENABLED) { + Assert.that(index > 0 && index <= getOopsLength(), "must be a valid non-zero index"); + } + return oopsBegin().getOopHandleAt((index - 1) * VM.getVM().getOopSize()); + } + + // Reclamation support (really only used by the nmethods, but in order to get asserts to work + // in the CodeCache they are defined virtual here) + public boolean isZombie() { return false; } + public boolean isLockedByVM() { return false; } + + /** OopMap for frame; can return null if none available */ + public OopMapSet getOopMaps() { + Address oopMapsAddr = oopMapsField.getValue(addr); + if (oopMapsAddr == null) { + return null; + } + return new OopMapSet(oopMapsAddr); + } + // FIXME: not yet implementable + // void set_oop_maps(OopMapSet* p); + + public OopMap getOopMapForReturnAddress(Address returnAddress, boolean debugging) { + Address pc = returnAddress; + if (Assert.ASSERTS_ENABLED) { + Assert.that(getOopMaps() != null, "nope"); + } + return getOopMaps().findMapAtOffset(pc.minus(instructionsBegin()), debugging); + } + + // virtual void preserve_callee_argument_oops(frame fr, const RegisterMap* reg_map, void f(oop*)) { ShouldNotReachHere(); } + // FIXME; + + /** NOTE: this returns a size in BYTES in this system! */ + public long getFrameSize() { + return VM.getVM().getAddressSize() * frameSizeField.getValue(addr); + } + + // Returns true, if the next frame is responsible for GC'ing oops passed as arguments + public boolean callerMustGCArguments(JavaThread thread) { return false; } + + public String getName() { + return CStringUtilities.getString(nameField.getValue(addr)); + } + + // FIXME: NOT FINISHED + + // FIXME: add more accessors + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + tty.print(getName()); + printComponentsOn(tty); + } + + protected void printComponentsOn(PrintStream tty) { + // FIXME: add relocation information + tty.println(" instructions: [" + instructionsBegin() + ", " + instructionsEnd() + "), " + + " data: [" + dataBegin() + ", " + dataEnd() + "), " + + " oops: [" + oopsBegin() + ", " + oopsEnd() + "), " + + " frame size: " + getFrameSize()); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private int getOopsLength() { + return (int) oopsLengthField.getValue(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java new file mode 100644 index 00000000000..570c5814e9b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java @@ -0,0 +1,197 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class CodeCache { + private static AddressField heapField; + private static VirtualConstructor virtualConstructor; + + private CodeHeap heap; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("CodeCache"); + + heapField = type.getAddressField("_heap"); + + virtualConstructor = new VirtualConstructor(db); + // Add mappings for all possible CodeBlob subclasses + virtualConstructor.addMapping("BufferBlob", BufferBlob.class); + virtualConstructor.addMapping("nmethod", NMethod.class); + virtualConstructor.addMapping("RuntimeStub", RuntimeStub.class); + virtualConstructor.addMapping("SafepointBlob", SafepointBlob.class); + virtualConstructor.addMapping("DeoptimizationBlob", DeoptimizationBlob.class); + if (VM.getVM().isServerCompiler()) { + virtualConstructor.addMapping("ExceptionBlob", ExceptionBlob.class); + virtualConstructor.addMapping("UncommonTrapBlob", UncommonTrapBlob.class); + } + } + + public CodeCache() { + heap = (CodeHeap) VMObjectFactory.newObject(CodeHeap.class, heapField.getValue()); + } + + public boolean contains(Address p) { + return getHeap().contains(p); + } + + /** When VM.getVM().isDebugging() returns true, this behaves like + findBlobUnsafe */ + public CodeBlob findBlob(Address start) { + CodeBlob result = findBlobUnsafe(start); + if (result == null) return null; + if (VM.getVM().isDebugging()) { + return result; + } + // We could potientially look up non_entrant methods + // NOTE: this is effectively a "guarantee", and is slightly different from the one in the VM + if (Assert.ASSERTS_ENABLED) { + Assert.that(!(result.isZombie() || result.isLockedByVM()), "unsafe access to zombie method"); + } + return result; + } + + public CodeBlob findBlobUnsafe(Address start) { + CodeBlob result = null; + + try { + result = (CodeBlob) virtualConstructor.instantiateWrapperFor(getHeap().findStart(start)); + } + catch (WrongTypeException wte) { + Address cbAddr = null; + try { + cbAddr = getHeap().findStart(start); + } + catch (Exception findEx) { + findEx.printStackTrace(); + } + + String message = "Couldn't deduce type of CodeBlob "; + if (cbAddr != null) { + message = message + "@" + cbAddr + " "; + } + message = message + "for PC=" + start; + + throw new RuntimeException(message, wte); + } + if (result == null) return null; + if (Assert.ASSERTS_ENABLED) { + // The HeapBlock that contains this blob is outside of the blob + // but it shouldn't be an error to find a blob based on the + // pointer to the HeapBlock. + Assert.that(result.blobContains(start) || result.blobContains(start.addOffsetTo(8)), + "found wrong CodeBlob"); + } + return result; + } + + public NMethod findNMethod(Address start) { + CodeBlob cb = findBlob(start); + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb == null || cb.isNMethod(), "did not find an nmethod"); + } + return (NMethod) cb; + } + + public NMethod findNMethodUnsafe(Address start) { + CodeBlob cb = findBlobUnsafe(start); + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb == null || cb.isNMethod(), "did not find an nmethod"); + } + return (NMethod) cb; + } + + /** Routine for instantiating appropriately-typed wrapper for a + CodeBlob. Used by CodeCache, Runtime1, etc. */ + public CodeBlob createCodeBlobWrapper(Address codeBlobAddr) { + try { + return (CodeBlob) virtualConstructor.instantiateWrapperFor(codeBlobAddr); + } + catch (Exception e) { + String message = "Unable to deduce type of CodeBlob from address " + codeBlobAddr + + " (expected type nmethod, RuntimeStub, "; + if (VM.getVM().isClientCompiler()) { + message = message + " or "; + } + message = message + "SafepointBlob"; + if (VM.getVM().isServerCompiler()) { + message = message + ", DeoptimizationBlob, or ExceptionBlob"; + } + message = message + ")"; + throw new RuntimeException(message); + } + } + + public void iterate(CodeCacheVisitor visitor) { + CodeHeap heap = getHeap(); + Address ptr = heap.begin(); + Address end = heap.end(); + + visitor.prologue(ptr, end); + CodeBlob lastBlob = null; + while (ptr != null && ptr.lessThan(end)) { + try { + CodeBlob blob = findBlobUnsafe(ptr); + if (blob != null) { + visitor.visit(blob); + if (blob == lastBlob) { + throw new InternalError("saw same blob twice"); + } + lastBlob = blob; + } + } catch (RuntimeException e) { + e.printStackTrace(); + } + Address next = heap.nextBlock(ptr); + if (next != null && next.lessThan(ptr)) { + throw new InternalError("pointer moved backwards"); + } + ptr = next; + } + visitor.epilogue(); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private CodeHeap getHeap() { + return heap; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeCacheVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeCacheVisitor.java new file mode 100644 index 00000000000..0fcf4efd523 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeCacheVisitor.java @@ -0,0 +1,42 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public interface CodeCacheVisitor { + + void prologue(Address start, Address end); + + void visit(CodeBlob blob); + + void epilogue(); + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CompressedReadStream.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CompressedReadStream.java new file mode 100644 index 00000000000..5fc013f38c9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CompressedReadStream.java @@ -0,0 +1,135 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import sun.jvm.hotspot.debugger.*; + +public class CompressedReadStream extends CompressedStream { + /** Equivalent to CompressedReadStream(buffer, 0) */ + public CompressedReadStream(Address buffer) { + this(buffer, 0); + } + + public CompressedReadStream(Address buffer, int position) { + super(buffer, position); + } + + public boolean readBoolean() { + return (read() != 0); + } + + public byte readByte() { + return (byte) read(); + } + + public char readChar() { + return (char) readInt(); + } + + public short readShort() { + return (short) readSignedInt(); + } + + public int readSignedInt() { + return decodeSign(readInt()); + } + + public int readInt() { + int b0 = read(); + if (b0 < L) { + return b0; + } else { + return readIntMb(b0); + } + } + + + public float readFloat() { + return Float.intBitsToFloat(reverseInt(readInt())); + } + + public double readDouble() { + int rh = readInt(); + int rl = readInt(); + int h = reverseInt(rh); + int l = reverseInt(rl); + return Double.longBitsToDouble((h << 32) | ((long)l & 0x00000000FFFFFFFFL)); + } + + public long readLong() { + long low = readSignedInt() & 0x00000000FFFFFFFFL; + long high = readSignedInt(); + return (high << 32) | low; + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + + // This encoding, called UNSIGNED5, is taken from J2SE Pack200. + // It assumes that most values have lots of leading zeroes. + // Very small values, in the range [0..191], code in one byte. + // Any 32-bit value (including negatives) can be coded, in + // up to five bytes. The grammar is: + // low_byte = [0..191] + // high_byte = [192..255] + // any_byte = low_byte | high_byte + // coding = low_byte + // | high_byte low_byte + // | high_byte high_byte low_byte + // | high_byte high_byte high_byte low_byte + // | high_byte high_byte high_byte high_byte any_byte + // Each high_byte contributes six bits of payload. + // The encoding is one-to-one (except for integer overflow) + // and easy to parse and unparse. + + private int readIntMb(int b0) { + int pos = position - 1; + int sum = b0; + // must collect more bytes: b[1]...b[4] + int lg_H_i = lg_H; + for (int i = 0; ;) { + int b_i = read(pos + (++i)); + sum += b_i << lg_H_i; // sum += b[i]*(64**i) + if (b_i < L || i == MAX_i) { + setPosition(pos+i+1); + return sum; + } + lg_H_i += lg_H; + } + } + + private short read(int index) { + return (short) buffer.getCIntegerAt(index, 1, true); + } + + /** Reads an unsigned byte, but returns it as a short */ + private short read() { + short retval = (short) buffer.getCIntegerAt(position, 1, true); + ++position; + return retval; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CompressedStream.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CompressedStream.java new file mode 100644 index 00000000000..4e0ce05819a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CompressedStream.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import sun.jvm.hotspot.debugger.*; + +/** NOTE that this class takes the address of a buffer. This means + that it can read previously-generated debug information directly + from the target VM. However, it also means that you can't create a + "wrapper" object for a CompressedStream down in the VM. It looks + like these are only kept persistently in OopMaps, and the code has + been special-cased in OopMap.java to handle this. */ + +public class CompressedStream { + protected Address buffer; + protected int position; + + /** Equivalent to CompressedStream(buffer, 0) */ + public CompressedStream(Address buffer) { + this(buffer, 0); + } + + public CompressedStream(Address buffer, int position) { + this.buffer = buffer; + this.position = position; + } + + public Address getBuffer() { + return buffer; + } + + public static final int LogBitsPerByte = 3; + public static final int BitsPerByte = 1 << 3; + + // Constants for UNSIGNED5 coding of Pack200 + public static final int lg_H = 6; + public static final int H = 1<> 31); + } + + public int decodeSign(int value) { + return (value >>> 1) ^ -(value & 1); + } + + // 32-bit self-inverse encoding of float bits + // converts trailing zeros (common in floats) to leading zeros + public int reverseInt(int i) { + // Hacker's Delight, Figure 7-1 + i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555; + i = (i & 0x33333333) << 3 | (i >>> 2) & 0x33333333; + i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f; + i = (i << 24) | ((i & 0xff00) << 8) | ((i >>> 8) & 0xff00) | (i >>> 24); + return i; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CompressedWriteStream.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CompressedWriteStream.java new file mode 100644 index 00000000000..12409a7d212 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CompressedWriteStream.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import sun.jvm.hotspot.debugger.*; + +/** Currently only used for oop map parsing (getBuffer() method) */ + +public class CompressedWriteStream extends CompressedStream { + /** Equivalent to CompressedWriteStream(buffer, 0) */ + public CompressedWriteStream(Address buffer) { + this(buffer, 0); + } + + /** In a real implementation there would need to be some way to + allocate the buffer */ + public CompressedWriteStream(Address buffer, int position) { + super(buffer, position); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantDoubleValue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantDoubleValue.java new file mode 100644 index 00000000000..abf57085748 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantDoubleValue.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; + +/** A ConstantDoubleValue describes a constant double; i.e., the + corresponding logical entity is either a source constant or its + computation has been constant-folded. */ + +public class ConstantDoubleValue extends ScopeValue { + private double value; + + public ConstantDoubleValue(double value) { + this.value = value; + } + + public boolean isConstantDouble() { + return true; + } + + public double getValue() { + return value; + } + + /** Serialization of debugging information */ + ConstantDoubleValue(DebugInfoReadStream stream) { + value = stream.readDouble(); + } + + // FIXME: not yet implementable + // void write_on(DebugInfoWriteStream* stream); + + // Printing + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + tty.print(value); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantIntValue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantIntValue.java new file mode 100644 index 00000000000..cf090ecead7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantIntValue.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; + +/** A ConstantIntValue describes a constant int; i.e., the + corresponding logical entity is either a source constant or its + computation has been constant-folded. */ + +public class ConstantIntValue extends ScopeValue { + private int value; + + public ConstantIntValue(int value) { + this.value = value; + } + + public boolean isConstantInt() { + return true; + } + + public int getValue() { + return value; + } + + /** Serialization of debugging information */ + ConstantIntValue(DebugInfoReadStream stream) { + value = stream.readSignedInt(); + } + + // FIXME: not yet implementable + // void write_on(DebugInfoWriteStream* stream); + + // Printing + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + tty.print(value); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantLongValue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantLongValue.java new file mode 100644 index 00000000000..7d06bc8fed3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantLongValue.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; + +/** A ConstantLongValue describes a constant long; i.e., the + corresponding logical entity is either a source constant or its + computation has been constant-folded. */ + +public class ConstantLongValue extends ScopeValue { + private long value; + + public ConstantLongValue(long value) { + this.value = value; + } + + public boolean isConstantLong() { + return true; + } + + public long getValue() { + return value; + } + + /** Serialization of debugging information */ + ConstantLongValue(DebugInfoReadStream stream) { + value = stream.readLong(); + } + + // FIXME: not yet implementable + // void write_on(DebugInfoWriteStream* stream); + + // Printing + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + tty.print(value); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantOopReadValue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantOopReadValue.java new file mode 100644 index 00000000000..103791f336e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ConstantOopReadValue.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; + +import sun.jvm.hotspot.debugger.*; + +/** A ConstantOopReadValue is created by the VM when reading debug + information */ + +public class ConstantOopReadValue extends ScopeValue { + private OopHandle value; + + /** Serialization of debugging information */ + public ConstantOopReadValue(DebugInfoReadStream stream) { + value = stream.readOopHandle(); + } + + public boolean isConstantOop() { + return true; + } + + public OopHandle getValue() { + return value; + } + + // FIXME: not yet implementable + // void write_on(DebugInfoWriteStream* stream); + + // Printing + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + tty.print(value); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/DebugInfoReadStream.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/DebugInfoReadStream.java new file mode 100644 index 00000000000..2fc3155fa91 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/DebugInfoReadStream.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.VM; + +public class DebugInfoReadStream extends CompressedReadStream { + private NMethod code; + private int InvocationEntryBCI; + + public DebugInfoReadStream(NMethod code, int offset) { + super(code.scopesDataBegin(), offset); + InvocationEntryBCI = VM.getVM().getInvocationEntryBCI(); + this.code = code; + } + + public OopHandle readOopHandle() { + return code.getOopAt(readInt()); + } + + public int readBCI() { + return readInt() + InvocationEntryBCI; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/DebugInformationRecorder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/DebugInformationRecorder.java new file mode 100644 index 00000000000..f31b5feb72c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/DebugInformationRecorder.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +/** Placeholder for now; simply need a couple of constants (FIXME: + should read these from the target VM) */ + +public class DebugInformationRecorder { + public static final int SERIALIZED_NULL = 0; + public static final int SYNCHRONIZATION_ENTRY_BCI = -1; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/DeoptimizationBlob.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/DeoptimizationBlob.java new file mode 100644 index 00000000000..49e7809d25e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/DeoptimizationBlob.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class DeoptimizationBlob extends SingletonBlob { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("DeoptimizationBlob"); + + // FIXME: add any needed fields + } + + public DeoptimizationBlob(Address addr) { + super(addr); + } + + public boolean isDeoptimizationStub() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ExceptionBlob.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ExceptionBlob.java new file mode 100644 index 00000000000..2110c0f733f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ExceptionBlob.java @@ -0,0 +1,57 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** ExceptionBlob: used for exception unwinding in compiled code + (currently only used by Compiler 2) */ + +public class ExceptionBlob extends SingletonBlob { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("ExceptionBlob"); + + // FIXME: add any needed fields + } + + public ExceptionBlob(Address addr) { + super(addr); + } + + public boolean isExceptionStub() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/Location.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/Location.java new file mode 100644 index 00000000000..160712104af --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/Location.java @@ -0,0 +1,339 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/**

A Location describes a concrete machine variable location + (such as integer or floating point register or a stack-held + variable). Used when generating debug-information for + nmethods.

+ +

Encoding:

+
+    bits:
+    Where:  [15]
+    Type:   [14..12]
+    Offset: [11..0]
+    
+*/ + +public class Location { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!VM.getVM().isCore(), "Debug info not used in core build"); + } + + OFFSET_MASK = db.lookupIntConstant("Location::OFFSET_MASK").intValue(); + OFFSET_SHIFT = db.lookupIntConstant("Location::OFFSET_SHIFT").intValue(); + TYPE_MASK = db.lookupIntConstant("Location::TYPE_MASK").intValue(); + TYPE_SHIFT = db.lookupIntConstant("Location::TYPE_SHIFT").intValue(); + WHERE_MASK = db.lookupIntConstant("Location::WHERE_MASK").intValue(); + WHERE_SHIFT = db.lookupIntConstant("Location::WHERE_SHIFT").intValue(); + + // Location::Type constants + TYPE_NORMAL = db.lookupIntConstant("Location::normal").intValue(); + TYPE_OOP = db.lookupIntConstant("Location::oop").intValue(); + TYPE_INT_IN_LONG = db.lookupIntConstant("Location::int_in_long").intValue(); + TYPE_LNG = db.lookupIntConstant("Location::lng").intValue(); + TYPE_FLOAT_IN_DBL = db.lookupIntConstant("Location::float_in_dbl").intValue(); + TYPE_DBL = db.lookupIntConstant("Location::dbl").intValue(); + TYPE_ADDR = db.lookupIntConstant("Location::addr").intValue(); + TYPE_INVALID = db.lookupIntConstant("Location::invalid").intValue(); + + // Location::Where constants + WHERE_ON_STACK = db.lookupIntConstant("Location::on_stack").intValue(); + WHERE_IN_REGISTER = db.lookupIntConstant("Location::in_register").intValue(); + } + + private int value; + + // type safe enum for "Where" + public static class Where { + public static final Where ON_STACK = new Where("on_stack"); + public static final Where IN_REGISTER = new Where("in_register"); + + private Where(String value) { + this.value = value; + } + + public String toString() { + return value; + } + + private String value; + + public int getValue() { + if (this == ON_STACK) { + return WHERE_ON_STACK; + } else if (this == IN_REGISTER) { + return WHERE_IN_REGISTER; + } else { + throw new RuntimeException("should not reach here"); + } + } + } + + // type safe enum for "Type" + public static class Type { + /** Ints, floats, double halves */ + public static final Type NORMAL = new Type("normal"); + /** Oop (please GC me!) */ + public static final Type OOP = new Type("oop"); + /** Long held in one register */ + public static final Type INT_IN_LONG = new Type("int_in_long"); + /** Long held in one register */ + public static final Type LNG = new Type("lng"); + /** Float held in double register */ + public static final Type FLOAT_IN_DBL = new Type("float_in_dbl"); + /** Double held in one register */ + public static final Type DBL = new Type("dbl"); + /** JSR return address */ + public static final Type ADDR = new Type("addr"); + /** Invalid location */ + public static final Type INVALID = new Type("invalid"); + + private Type(String value) { + this.value = value; + } + private String value; + + public String toString() { + return value; + } + + public int getValue() { + if (this == NORMAL) { + return TYPE_NORMAL; + } else if (this == OOP) { + return TYPE_OOP; + } else if (this == INT_IN_LONG) { + return TYPE_INT_IN_LONG; + } else if (this == LNG) { + return TYPE_LNG; + } else if (this == FLOAT_IN_DBL) { + return TYPE_FLOAT_IN_DBL; + } else if (this == DBL) { + return TYPE_DBL; + } else if (this == ADDR) { + return TYPE_ADDR; + } else if (this == INVALID) { + return TYPE_INVALID; + } else { + throw new RuntimeException("should not reach here"); + } + } + } + + private static int OFFSET_MASK; + private static int OFFSET_SHIFT; + private static int TYPE_MASK; + private static int TYPE_SHIFT; + private static int WHERE_MASK; + private static int WHERE_SHIFT; + + // constants in Type enum + private static int TYPE_NORMAL; + private static int TYPE_OOP; + private static int TYPE_INT_IN_LONG; + private static int TYPE_LNG; + private static int TYPE_FLOAT_IN_DBL; + private static int TYPE_DBL; + private static int TYPE_ADDR; + private static int TYPE_INVALID; + + // constants in Where enum + private static int WHERE_ON_STACK; + private static int WHERE_IN_REGISTER; + + /** Create a bit-packed Location */ + Location(Where where, Type type, int offset) { + setWhere(where); + setType(type); + setOffset(offset & 0x0000FFFF); + } + + public Where getWhere() { + int where = (value & WHERE_MASK) >> WHERE_SHIFT; + if (where == WHERE_ON_STACK) { + return Where.ON_STACK; + } else if (where == WHERE_IN_REGISTER) { + return Where.IN_REGISTER; + } else { + throw new RuntimeException("should not reach here"); + } + } + + public Type getType() { + int type = (value & TYPE_MASK) >> TYPE_SHIFT; + if (type == TYPE_NORMAL) { + return Type.NORMAL; + } else if (type == TYPE_OOP) { + return Type.OOP; + } else if (type == TYPE_INT_IN_LONG) { + return Type.INT_IN_LONG; + } else if (type == TYPE_LNG) { + return Type.LNG; + } else if (type == TYPE_FLOAT_IN_DBL) { + return Type.FLOAT_IN_DBL; + } else if (type == TYPE_DBL) { + return Type.DBL; + } else if (type == TYPE_ADDR) { + return Type.ADDR; + } else if (type == TYPE_INVALID) { + return Type.INVALID; + } else { + throw new RuntimeException("should not reach here"); + } + } + + public short getOffset() { + return (short) ((value & OFFSET_MASK) >> OFFSET_SHIFT); + } + + public boolean isRegister() { + return getWhere() == Where.IN_REGISTER; + } + + public boolean isStack() { + return getWhere() == Where.ON_STACK; + } + + public boolean holdsOop() { + return getType() == Type.OOP; + } + + public boolean holdsInt() { + return getType() == Type.INT_IN_LONG; + } + + public boolean holdsLong() { + return getType() == Type.LNG; + } + + public boolean holdsFloat() { + return getType() == Type.FLOAT_IN_DBL; + } + + public boolean holdsDouble() { + return getType() == Type.DBL; + } + + public boolean holdsAddr() { + return getType() == Type.ADDR; + } + + public boolean isIllegal() { + return getType() == Type.INVALID; + } + + public int getStackOffset() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(getWhere() == Where.ON_STACK, "wrong Where"); + } + return getOffset() << VM.getVM().getLogAddressSize(); + } + + public int getRegisterNumber() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(getWhere() == Where.IN_REGISTER, "wrong Where"); + } + return getOffset(); + } + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + tty.print("Value " + value + ", "); + if (isIllegal()) { + tty.print("Illegal"); + } else { + Where w = getWhere(); + if (w == Where.ON_STACK) { + tty.print("stack[" + getStackOffset() + "]"); + } else if (w == Where.IN_REGISTER) { + tty.print("reg " + getRegisterNumber()); + } + + Type type = getType(); + if (type == Type.NORMAL) { + } else if (type == Type.OOP) { + tty.print(",oop"); + } else if (type == Type.INT_IN_LONG) { + tty.print(",int"); + } else if (type == Type.LNG) { + tty.print(",long"); + } else if (type == Type.FLOAT_IN_DBL) { + tty.print(",float"); + } else if (type == Type.DBL) { + tty.print(",double"); + } else if (type == Type.ADDR) { + tty.print(",address"); + } else if (type == Type.INVALID) { + tty.print(",invalid"); + } + } + } + + /** Serialization of debugging information */ + public Location(DebugInfoReadStream stream) { + value = (0x0000FFFF & stream.readInt()); + } + + // FIXME: not yet implementable + // void write_on(DebugInfoWriteStream* stream); + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private void setWhere(Where where) { + value |= (where.getValue() << WHERE_SHIFT); + } + + private void setType(Type type) { + value |= (type.getValue() << TYPE_SHIFT); + } + + private void setOffset(int offset) { + value |= (offset << OFFSET_SHIFT); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/LocationValue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/LocationValue.java new file mode 100644 index 00000000000..f322bdc506a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/LocationValue.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; + +/** A Location value describes a value in a given location; i.e. the + corresponding logical entity (e.g., a method temporary) lives in + this location. */ + +public class LocationValue extends ScopeValue { + private Location location; + + public LocationValue(Location location) { + this.location = location; + } + + public boolean isLocation() { + return true; + } + + public Location getLocation() { + return location; + } + + /** Serialization of debugging information */ + LocationValue(DebugInfoReadStream stream) { + location = new Location(stream); + } + + // FIXME: not yet implementable + // void write_on(DebugInfoWriteStream* stream); + + // Printing + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + getLocation().printOn(tty); + } +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/MonitorValue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/MonitorValue.java new file mode 100644 index 00000000000..cf539d311c8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/MonitorValue.java @@ -0,0 +1,54 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; + +public class MonitorValue { + private ScopeValue owner; + private Location basicLock; + + // FIXME: not useful yet + // MonitorValue(ScopeValue* owner, Location basic_lock); + + public MonitorValue(DebugInfoReadStream stream) { + basicLock = new Location(stream); + owner = ScopeValue.readFrom(stream); + } + + public ScopeValue owner() { return owner; } + public Location basicLock() { return basicLock; } + + // FIXME: not yet implementable + // void write_on(DebugInfoWriteStream* stream); + + public void printOn(PrintStream tty) { + tty.print("monitor{"); + owner().printOn(tty); + tty.print(","); + basicLock().printOn(tty); + tty.print("}"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java new file mode 100644 index 00000000000..284c6813b93 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java @@ -0,0 +1,361 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class NMethod extends CodeBlob { + private static long pcDescSize; + private static CIntegerField zombieInstructionSizeField; + private static sun.jvm.hotspot.types.OopField methodField; + /** != InvocationEntryBci if this nmethod is an on-stack replacement method */ + private static CIntegerField entryBCIField; + /** To support simple linked-list chaining of nmethods */ + private static AddressField linkField; + /** Offsets for different nmethod parts */ + private static CIntegerField exceptionOffsetField; + private static CIntegerField deoptOffsetField; + private static CIntegerField origPCOffsetField; + private static CIntegerField stubOffsetField; + private static CIntegerField scopesDataOffsetField; + private static CIntegerField scopesPCsOffsetField; + private static CIntegerField dependenciesOffsetField; + private static CIntegerField handlerTableOffsetField; + private static CIntegerField nulChkTableOffsetField; + private static CIntegerField nmethodEndOffsetField; + + /** Offsets for entry points */ + /** Entry point with class check */ + private static AddressField entryPointField; + /** Entry point without class check */ + private static AddressField verifiedEntryPointField; + /** Entry point for on stack replacement */ + private static AddressField osrEntryPointField; + + // FIXME: add access to flags (how?) + + /** NMethod Flushing lock (if non-zero, then the nmethod is not removed) */ + private static JIntField lockCountField; + + /** not_entrant method removal. Each mark_sweep pass will update + this mark to current sweep invocation count if it is seen on the + stack. An not_entrant method can be removed when there is no + more activations, i.e., when the _stack_traversal_mark is less than + current sweep traversal index. */ + private static CIntegerField stackTraversalMarkField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("nmethod"); + + zombieInstructionSizeField = type.getCIntegerField("_zombie_instruction_size"); + methodField = type.getOopField("_method"); + entryBCIField = type.getCIntegerField("_entry_bci"); + linkField = type.getAddressField("_link"); + exceptionOffsetField = type.getCIntegerField("_exception_offset"); + deoptOffsetField = type.getCIntegerField("_deoptimize_offset"); + origPCOffsetField = type.getCIntegerField("_orig_pc_offset"); + stubOffsetField = type.getCIntegerField("_stub_offset"); + scopesDataOffsetField = type.getCIntegerField("_scopes_data_offset"); + scopesPCsOffsetField = type.getCIntegerField("_scopes_pcs_offset"); + dependenciesOffsetField = type.getCIntegerField("_dependencies_offset"); + handlerTableOffsetField = type.getCIntegerField("_handler_table_offset"); + nulChkTableOffsetField = type.getCIntegerField("_nul_chk_table_offset"); + nmethodEndOffsetField = type.getCIntegerField("_nmethod_end_offset"); + entryPointField = type.getAddressField("_entry_point"); + verifiedEntryPointField = type.getAddressField("_verified_entry_point"); + osrEntryPointField = type.getAddressField("_osr_entry_point"); + lockCountField = type.getJIntField("_lock_count"); + stackTraversalMarkField = type.getCIntegerField("_stack_traversal_mark"); + + pcDescSize = db.lookupType("PcDesc").getSize(); + } + + public NMethod(Address addr) { + super(addr); + } + + + // Accessors + public Address getAddress() { + return addr; + } + + public Method getMethod() { + return (Method) VM.getVM().getObjectHeap().newOop(methodField.getValue(addr)); + } + + // Type info + public boolean isNMethod() { return true; } + public boolean isJavaMethod() { return !getMethod().isNative(); } + public boolean isNativeMethod() { return getMethod().isNative(); } + public boolean isOSRMethod() { return getEntryBCI() != VM.getVM().getInvocationEntryBCI(); } + + /** Boundaries for different parts */ + public Address constantsBegin() { return instructionsBegin(); } + public Address constantsEnd() { return getEntryPoint(); } + public Address codeBegin() { return getEntryPoint(); } + public Address codeEnd() { return headerBegin().addOffsetTo(getStubOffset()); } + public Address exceptionBegin() { return headerBegin().addOffsetTo(getExceptionOffset()); } + public Address deoptBegin() { return headerBegin().addOffsetTo(getDeoptOffset()); } + public Address stubBegin() { return headerBegin().addOffsetTo(getStubOffset()); } + public Address stubEnd() { return headerBegin().addOffsetTo(getScopesDataOffset()); } + public Address scopesDataBegin() { return headerBegin().addOffsetTo(getScopesDataOffset()); } + public Address scopesDataEnd() { return headerBegin().addOffsetTo(getScopesPCsOffset()); } + public Address scopesPCsBegin() { return headerBegin().addOffsetTo(getScopesPCsOffset()); } + public Address scopesPCsEnd() { return headerBegin().addOffsetTo(getDependenciesOffset()); } + public Address dependenciesBegin() { return headerBegin().addOffsetTo(getDependenciesOffset()); } + public Address dependenciesEnd() { return headerBegin().addOffsetTo(getHandlerTableOffset()); } + public Address handlerTableBegin() { return headerBegin().addOffsetTo(getHandlerTableOffset()); } + public Address handlerTableEnd() { return headerBegin().addOffsetTo(getNulChkTableOffset()); } + public Address nulChkTableBegin() { return headerBegin().addOffsetTo(getNulChkTableOffset()); } + public Address nulChkTableEnd() { return headerBegin().addOffsetTo(getNMethodEndOffset()); } + + public int constantsSize() { return (int) constantsEnd() .minus(constantsBegin()); } + public int codeSize() { return (int) codeEnd() .minus(codeBegin()); } + public int stubSize() { return (int) stubEnd() .minus(stubBegin()); } + public int scopesDataSize() { return (int) scopesDataEnd() .minus(scopesDataBegin()); } + public int scopesPCsSize() { return (int) scopesPCsEnd() .minus(scopesPCsBegin()); } + public int dependenciesSize() { return (int) dependenciesEnd().minus(dependenciesBegin()); } + public int handlerTableSize() { return (int) handlerTableEnd().minus(handlerTableBegin()); } + public int nulChkTableSize() { return (int) nulChkTableEnd() .minus(nulChkTableBegin()); } + public int origPCOffset() { return (int) origPCOffsetField.getValue(addr); } + + public int totalSize() { + return + constantsSize() + + codeSize() + + stubSize() + + scopesDataSize() + + scopesPCsSize() + + dependenciesSize() + + handlerTableSize() + + nulChkTableSize(); + } + + public boolean constantsContains (Address addr) { return constantsBegin() .lessThanOrEqual(addr) && constantsEnd() .greaterThan(addr); } + public boolean codeContains (Address addr) { return codeBegin() .lessThanOrEqual(addr) && codeEnd() .greaterThan(addr); } + public boolean stubContains (Address addr) { return stubBegin() .lessThanOrEqual(addr) && stubEnd() .greaterThan(addr); } + public boolean scopesDataContains (Address addr) { return scopesDataBegin() .lessThanOrEqual(addr) && scopesDataEnd() .greaterThan(addr); } + public boolean scopesPCsContains (Address addr) { return scopesPCsBegin() .lessThanOrEqual(addr) && scopesPCsEnd() .greaterThan(addr); } + public boolean handlerTableContains(Address addr) { return handlerTableBegin().lessThanOrEqual(addr) && handlerTableEnd().greaterThan(addr); } + public boolean nulChkTableContains (Address addr) { return nulChkTableBegin() .lessThanOrEqual(addr) && nulChkTableEnd() .greaterThan(addr); } + + /** Entry points */ + public Address getEntryPoint() { return entryPointField.getValue(addr); } + public Address getVerifiedEntryPoint() { return verifiedEntryPointField.getValue(addr); } + + // FIXME: add interpreter_entry_point() + // FIXME: add lazy_interpreter_entry_point() for C2 + + // ********** + // * FIXME: * ADD ACCESS TO FLAGS!!!! + // ********** + // public boolean isInUse(); + // public boolean isAlive(); + // public boolean isNotEntrant(); + // public boolean isZombie(); + + // ******************************** + // * MAJOR FIXME: MAJOR HACK HERE * + // ******************************** + public boolean isZombie() { return false; } + + // public boolean isUnloaded(); + // public boolean isYoung(); + // public boolean isOld(); + // public int age(); + // public boolean isMarkedForDeoptimization(); + // public boolean isMarkedForUnloading(); + // public boolean isMarkedForReclamation(); + // public int level(); + // public int version(); + + // FIXME: add mutators for above + // FIXME: add exception cache access? + + /** On-stack replacement support */ + // FIXME: add mutators + public int getOSREntryBCI() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(getEntryBCI() != VM.getVM().getInvocationEntryBCI(), "wrong kind of nmethod"); + } + return getEntryBCI(); + } + + public NMethod getLink() { + return (NMethod) VMObjectFactory.newObject(NMethod.class, linkField.getValue(addr)); + } + + /** Tells whether frames described by this nmethod can be + deoptimized. Note: native wrappers cannot be deoptimized. */ + public boolean canBeDeoptimized() { return isJavaMethod(); } + + // FIXME: add inline cache support + // FIXME: add flush() + + public boolean isLockedByVM() { return lockCountField.getValue(addr) > 0; } + + // FIXME: add mark_as_seen_on_stack + // FIXME: add can_not_entrant_be_converted + + // FIXME: add GC support + // void follow_roots_or_mark_for_unloading(bool unloading_occurred, bool& marked_for_unloading); + // void follow_root_or_mark_for_unloading(oop* root, bool unloading_occurred, bool& marked_for_unloading); + // void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, void f(oop*)); + // void adjust_pointers(); + + /** Finds a PCDesc with real-pc equal to "pc" */ + public PCDesc getPCDescAt(Address pc) { + // FIXME: consider adding cache like the one down in the VM + for (Address p = scopesPCsBegin(); p.lessThan(scopesPCsEnd()); p = p.addOffsetTo(pcDescSize)) { + PCDesc pcDesc = new PCDesc(p); + if (pcDesc.getRealPC(this).equals(pc)) { + return pcDesc; + } + } + return null; + } + + /** ScopeDesc for an instruction */ + public ScopeDesc getScopeDescAt(Address pc) { + PCDesc pd = getPCDescAt(pc); + if (Assert.ASSERTS_ENABLED) { + Assert.that(pd != null, "scope must be present"); + } + return new ScopeDesc(this, pd.getScopeDecodeOffset()); + } + + /** This is only for use by the debugging system, and is only + intended for use in the topmost frame, where we are not + guaranteed to be at a PC for which we have a PCDesc. It finds + the PCDesc with realPC closest to the current PC. */ + public PCDesc getPCDescNearDbg(Address pc) { + PCDesc bestGuessPCDesc = null; + long bestDistance = 0; + for (Address p = scopesPCsBegin(); p.lessThan(scopesPCsEnd()); p = p.addOffsetTo(pcDescSize)) { + PCDesc pcDesc = new PCDesc(p); + // In case pc is null + long distance = -pcDesc.getRealPC(this).minus(pc); + if ((bestGuessPCDesc == null) || + ((distance >= 0) && (distance < bestDistance))) { + bestGuessPCDesc = pcDesc; + bestDistance = distance; + } + } + return bestGuessPCDesc; + } + + /** This is only for use by the debugging system, and is only + intended for use in the topmost frame, where we are not + guaranteed to be at a PC for which we have a PCDesc. It finds + the ScopeDesc closest to the current PC. NOTE that this may + return NULL for compiled methods which don't have any + ScopeDescs! */ + public ScopeDesc getScopeDescNearDbg(Address pc) { + PCDesc pd = getPCDescNearDbg(pc); + if (pd == null) return null; + return new ScopeDesc(this, pd.getScopeDecodeOffset()); + } + + public Map/**/ getSafepoints() { + Map safepoints = new HashMap(); // Map + sun.jvm.hotspot.debugger.Address p = null; + for (p = scopesPCsBegin(); p.lessThan(scopesPCsEnd()); + p = p.addOffsetTo(pcDescSize)) { + PCDesc pcDesc = new PCDesc(p); + sun.jvm.hotspot.debugger.Address pc = pcDesc.getRealPC(this); + safepoints.put(pc, pcDesc); + } + return safepoints; + } + + // FIXME: add getPCOffsetForBCI() + // FIXME: add embeddedOopAt() + // FIXME: add isDependentOn() + // FIXME: add isPatchableAt() + + /** Support for code generation. Only here for proof-of-concept. */ + public static int getEntryPointOffset() { return (int) entryPointField.getOffset(); } + public static int getVerifiedEntryPointOffset() { return (int) verifiedEntryPointField.getOffset(); } + public static int getOSREntryPointOffset() { return (int) osrEntryPointField.getOffset(); } + public static int getEntryBCIOffset() { return (int) entryBCIField.getOffset(); } + /** NOTE: renamed from "method_offset_in_bytes" */ + public static int getMethodOffset() { return (int) methodField.getOffset(); } + + public void print() { + printOn(System.out); + } + + public String toString() { + Method method = getMethod(); + return "NMethod for " + + method.getMethodHolder().getName().asString() + "." + + method.getName().asString() + method.getSignature().asString() + "==>n" + + super.toString(); + } + + public String flagsToString() { + // FIXME need access to flags... + return ""; + } + + public String getName() { + Method method = getMethod(); + return "NMethod for " + + method.getMethodHolder().getName().asString() + "." + + method.getName().asString() + + method.getSignature().asString(); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private int getEntryBCI() { return (int) entryBCIField .getValue(addr); } + private int getExceptionOffset() { return (int) exceptionOffsetField .getValue(addr); } + private int getDeoptOffset() { return (int) deoptOffsetField .getValue(addr); } + private int getStubOffset() { return (int) stubOffsetField .getValue(addr); } + private int getScopesDataOffset() { return (int) scopesDataOffsetField .getValue(addr); } + private int getScopesPCsOffset() { return (int) scopesPCsOffsetField .getValue(addr); } + private int getDependenciesOffset() { return (int) dependenciesOffsetField.getValue(addr); } + private int getHandlerTableOffset() { return (int) handlerTableOffsetField.getValue(addr); } + private int getNulChkTableOffset() { return (int) nulChkTableOffsetField .getValue(addr); } + private int getNMethodEndOffset() { return (int) nmethodEndOffsetField .getValue(addr); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/PCDesc.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/PCDesc.java new file mode 100644 index 00000000000..fe0388bb6fa --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/PCDesc.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** PcDescs map a physical PC (given as offset from start of nmethod) + to the corresponding source scope and byte code index. */ + +public class PCDesc extends VMObject { + private static CIntegerField pcOffsetField; + private static CIntegerField scopeDecodeOffsetField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("PcDesc"); + + pcOffsetField = type.getCIntegerField("_pc_offset"); + scopeDecodeOffsetField = type.getCIntegerField("_scope_decode_offset"); + } + + public PCDesc(Address addr) { + super(addr); + } + + // FIXME: add additional constructor probably needed for ScopeDesc::sender() + + public int getPCOffset() { + return (int) pcOffsetField.getValue(addr); + } + + public int getScopeDecodeOffset() { + return ((int) scopeDecodeOffsetField.getValue(addr)); + } + + public Address getRealPC(NMethod code) { + return code.instructionsBegin().addOffsetTo(getPCOffset()); + } + + public void print(NMethod code) { + printOn(System.out, code); + } + + public void printOn(PrintStream tty, NMethod code) { + tty.println("PCDesc(" + getRealPC(code) + "):"); + for (ScopeDesc sd = code.getScopeDescAt(getRealPC(code)); + sd != null; + sd = sd.sender()) { + tty.print(" "); + sd.getMethod().printValueOn(tty); + tty.print(" @" + sd.getBCI()); + tty.println(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/RuntimeStub.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/RuntimeStub.java new file mode 100644 index 00000000000..83ee3d83734 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/RuntimeStub.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class RuntimeStub extends CodeBlob { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("RuntimeStub"); + + // FIXME: add any needed fields + } + + public RuntimeStub(Address addr) { + super(addr); + } + + public boolean isRuntimeStub() { + return true; + } + + public String getName() { + return "RuntimeStub: " + super.getName(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/SafepointBlob.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/SafepointBlob.java new file mode 100644 index 00000000000..8b6af64698c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/SafepointBlob.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** SafepointBlob: handles illegal_instruction exceptions during a safepoint */ + +public class SafepointBlob extends SingletonBlob { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("SafepointBlob"); + + // FIXME: add any needed fields + } + + public SafepointBlob(Address addr) { + super(addr); + } + + public boolean isSafepointStub() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ScopeDesc.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ScopeDesc.java new file mode 100644 index 00000000000..b4d2d592d58 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ScopeDesc.java @@ -0,0 +1,164 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/** ScopeDescs contain the information that makes source-level + debugging of nmethods possible; each scopeDesc describes a method + activation */ + +public class ScopeDesc { + /** NMethod information */ + private NMethod code; + private Method method; + private int bci; + /** Decoding offsets */ + private int decodeOffset; + private int senderDecodeOffset; + private int localsDecodeOffset; + private int expressionsDecodeOffset; + private int monitorsDecodeOffset; + + public ScopeDesc(NMethod code, int decodeOffset) { + this.code = code; + this.decodeOffset = decodeOffset; + + // Decode header + DebugInfoReadStream stream = streamAt(decodeOffset); + + senderDecodeOffset = stream.readInt(); + method = (Method) VM.getVM().getObjectHeap().newOop(stream.readOopHandle()); + bci = stream.readBCI(); + // Decode offsets for body and sender + localsDecodeOffset = stream.readInt(); + expressionsDecodeOffset = stream.readInt(); + monitorsDecodeOffset = stream.readInt(); + } + + public NMethod getNMethod() { return code; } + public Method getMethod() { return method; } + public int getBCI() { return bci; } + + /** Returns a List<ScopeValue> */ + public List getLocals() { + return decodeScopeValues(localsDecodeOffset); + } + + /** Returns a List<ScopeValue> */ + public List getExpressions() { + return decodeScopeValues(expressionsDecodeOffset); + } + + /** Returns a List<MonitorValue> */ + public List getMonitors() { + return decodeMonitorValues(monitorsDecodeOffset); + } + + /** Stack walking. Returns null if this is the outermost scope. */ + public ScopeDesc sender() { + if (isTop()) { + return null; + } + + return new ScopeDesc(code, senderDecodeOffset); + } + + /** Returns where the scope was decoded */ + public int getDecodeOffset() { + return decodeOffset; + } + + /** Tells whether sender() returns null */ + public boolean isTop() { + return (senderDecodeOffset == DebugInformationRecorder.SERIALIZED_NULL); + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof ScopeDesc)) { + return false; + } + + ScopeDesc sd = (ScopeDesc) arg; + + return (sd.method.equals(method) && (sd.bci == bci)); + } + + public void printValue() { + printValueOn(System.out); + } + + public void printValueOn(PrintStream tty) { + tty.print("ScopeDesc for "); + method.printValueOn(tty); + tty.println(" @bci " + bci); + } + + // FIXME: add more accessors + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private DebugInfoReadStream streamAt(int decodeOffset) { + return new DebugInfoReadStream(code, decodeOffset); + } + + /** Returns a List<ScopeValue> or null if no values were present */ + private List decodeScopeValues(int decodeOffset) { + if (decodeOffset == DebugInformationRecorder.SERIALIZED_NULL) { + return null; + } + DebugInfoReadStream stream = streamAt(decodeOffset); + int length = stream.readInt(); + List res = new ArrayList(length); + for (int i = 0; i < length; i++) { + res.add(ScopeValue.readFrom(stream)); + } + return res; + } + + /** Returns a List<MonitorValue> or null if no values were present */ + private List decodeMonitorValues(int decodeOffset) { + if (decodeOffset == DebugInformationRecorder.SERIALIZED_NULL) { + return null; + } + DebugInfoReadStream stream = streamAt(decodeOffset); + int length = stream.readInt(); + List res = new ArrayList(length); + for (int i = 0; i < length; i++) { + res.add(new MonitorValue(stream)); + } + return res; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ScopeValue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ScopeValue.java new file mode 100644 index 00000000000..f9c99d3d4ce --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/ScopeValue.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; + +import sun.jvm.hotspot.utilities.*; + +/**

Classes used for serializing debugging information. These + abstractions are introducted to provide symmetric read and write + operations.

+ +

+

    +
  • ScopeValue: describes the value of a variable/expression in a scope +
      +
    • LocationValue: describes a value in a given location (in frame or register) +
    • ConstantValue: describes a constant +
    +
+

*/ + +public abstract class ScopeValue { + // Package private enumeration values (FIXME: read from target VM) + static final int LOCATION_CODE = 0; + static final int CONSTANT_INT_CODE = 1; + static final int CONSTANT_OOP_CODE = 2; + static final int CONSTANT_LONG_CODE = 3; + static final int CONSTANT_DOUBLE_CODE = 4; + + public boolean isLocation() { return false; } + public boolean isConstantInt() { return false; } + public boolean isConstantDouble() { return false; } + public boolean isConstantLong() { return false; } + public boolean isConstantOop() { return false; } + + public static ScopeValue readFrom(DebugInfoReadStream stream) { + switch (stream.readInt()) { + case LOCATION_CODE: + return new LocationValue(stream); + case CONSTANT_INT_CODE: + return new ConstantIntValue(stream); + case CONSTANT_OOP_CODE: + return new ConstantOopReadValue(stream); + case CONSTANT_LONG_CODE: + return new ConstantLongValue(stream); + case CONSTANT_DOUBLE_CODE: + return new ConstantDoubleValue(stream); + default: + Assert.that(false, "should not reach here"); + return null; + } + } + + public abstract void printOn(PrintStream tty); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/SingletonBlob.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/SingletonBlob.java new file mode 100644 index 00000000000..148531842a6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/SingletonBlob.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class SingletonBlob extends CodeBlob { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("SingletonBlob"); + + // FIXME: add any needed fields + } + + public SingletonBlob(Address addr) { + super(addr); + } + + public boolean isSingletonBlob() { return true; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/Stub.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/Stub.java new file mode 100644 index 00000000000..9095b26125a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/Stub.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.io.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +/** A port of the VM's Stub mechanism. Note that the separation of + Stub and StubInterface (done in the VM to save space) is not + currently necessary in these APIs and has been flattened so that + class Stub has virtual functions overridden by concrete + subclasses. */ + +public class Stub extends VMObject { + + public Stub(Address addr) { + super(addr); + } + + // NOTE (FIXME): initialize(int) / finalize() elided for now + + // + // General info/converters + // + + /** Must return the size provided by initialize */ + public long getSize() { Assert.that(false, "should not call this"); return 0; } + // NOTE (FIXME): code_size_to_size elided for now (would be a good reason for inserting the StubInterface abstraction) + /** Needed to add this for iteration */ + public Address getAddress() { return addr; } + + // + // Code info + // + + /** Points to the first byte of the code */ + public Address codeBegin() { Assert.that(false, "should not call this"); return null; } + /** Points to the first byte after the code */ + public Address codeEnd() { Assert.that(false, "should not call this"); return null; } + + // + // Debugging + // + + /** Verifies the Stub */ + public void verify() { Assert.that(false, "should not call this"); } + /** Prints some information about the stub */ + public void printOn(PrintStream tty) { Assert.that(false, "should not call this"); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/StubQueue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/StubQueue.java new file mode 100644 index 00000000000..373670a7754 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/StubQueue.java @@ -0,0 +1,174 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/**

A port of the VM's StubQueue. Note that the VM implicitly + knows the type of the objects contained in each StubQueue because + it passes in an instance of a StubInterface to the StubQueue's + constructor; the goal in the VM was to save space in the generated + code. In the SA APIs the pattern has been to use the + VirtualConstructor mechanism to instantiate wrapper objects of the + appropriate type for objects down in the VM; see, for example, the + CodeCache, which identifies NMethods, RuntimeStubs, etc.

+ +

In this port we eliminate the StubInterface in favor of + passing in the class corresponding to the type of Stub which this + StubQueue contains.

*/ + +public class StubQueue extends VMObject { + // FIXME: add the rest of the fields + private static AddressField stubBufferField; + private static CIntegerField bufferLimitField; + private static CIntegerField queueBeginField; + private static CIntegerField queueEndField; + private static CIntegerField numberOfStubsField; + + // The type of the contained stubs (i.e., InterpreterCodelet, + // ICStub). Must be a subclass of type Stub. + private Class stubType; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("StubQueue"); + + stubBufferField = type.getAddressField("_stub_buffer"); + bufferLimitField = type.getCIntegerField("_buffer_limit"); + queueBeginField = type.getCIntegerField("_queue_begin"); + queueEndField = type.getCIntegerField("_queue_end"); + numberOfStubsField = type.getCIntegerField("_number_of_stubs"); + } + + public StubQueue(Address addr, Class stubType) { + super(addr); + this.stubType = stubType; + } + + public boolean contains(Address pc) { + if (pc == null) return false; + long offset = pc.minus(getStubBuffer()); + return ((0 <= offset) && (offset < getBufferLimit())); + } + + public Stub getStubContaining(Address pc) { + if (contains(pc)) { + int i = 0; + for (Stub s = getFirst(); s != null; s = getNext(s)) { + if (stubContains(s, pc)) { + return s; + } + } + } + return null; + } + + public boolean stubContains(Stub s, Address pc) { + return (s.codeBegin().lessThanOrEqual(pc) && s.codeEnd().greaterThan(pc)); + } + + public int getNumberOfStubs() { + return (int) numberOfStubsField.getValue(addr); + } + + public Stub getFirst() { + return ((getNumberOfStubs() > 0) ? getStubAt(getQueueBegin()) : null); + } + + public Stub getNext(Stub s) { + long i = getIndexOf(s) + getStubSize(s); + if (i == getBufferLimit()) { + i = 0; + } + return ((i == getQueueEnd()) ? null : getStubAt(i)); + } + + public Stub getPrev(Stub s) { + if (getIndexOf(s) == getQueueBegin()) { + return null; + } + + Stub temp = getFirst(); + Stub prev = null; + while (temp != null && getIndexOf(temp) != getIndexOf(s)) { + prev = temp; + temp = getNext(temp); + } + + return prev; + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private long getQueueBegin() { + return queueBeginField.getValue(addr); + } + + private long getQueueEnd() { + return queueEndField.getValue(addr); + } + + private long getBufferLimit() { + return bufferLimitField.getValue(addr); + } + + private Address getStubBuffer() { + return stubBufferField.getValue(addr); + } + + private Stub getStubAt(long offset) { + checkIndex(offset); + return (Stub) VMObjectFactory.newObject(stubType, getStubBuffer().addOffsetTo(offset)); + } + + private long getIndexOf(Stub s) { + long i = s.getAddress().minus(getStubBuffer()); + checkIndex(i); + return i; + } + + private long getStubSize(Stub s) { + return s.getSize(); + } + + private void checkIndex(long i) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(0 <= i && i < getBufferLimit() && (i % VM.getVM().getAddressSize() == 0), "illegal index"); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/UncommonTrapBlob.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/UncommonTrapBlob.java new file mode 100644 index 00000000000..8598e5dffdc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/UncommonTrapBlob.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** UncommonTrapBlob (currently only used by Compiler 2) */ + +public class UncommonTrapBlob extends SingletonBlob { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("UncommonTrapBlob"); + + // FIXME: add any needed fields + } + + public UncommonTrapBlob(Address addr) { + super(addr); + } + + public boolean isUncommonTrapStub() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/VMRegImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/VMRegImpl.java new file mode 100644 index 00000000000..318e6cb7c89 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/VMRegImpl.java @@ -0,0 +1,70 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class VMRegImpl { + + private static VMReg stack0; + private static int stack0Val; + private static Address stack0Addr; + private static AddressField regNameField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("VMRegImpl"); + AddressField stack0Field = type.getAddressField("stack0"); + stack0Addr = stack0Field.getValue(); + stack0Val = (int) stack0Addr.hashCode(); + stack0 = new VMReg(stack0Val); + regNameField = type.getAddressField("regName[0]"); + } + + public static VMReg getStack0() { + return stack0; + } + + public static String getRegisterName(int index) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(index >= 0 && index < stack0Val, "invalid index : " + index); + } + Address regName = regNameField.getStaticFieldAddress(); + long addrSize = VM.getVM().getAddressSize(); + return CStringUtilities.getString(regName.getAddressAt(index * addrSize)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMap.java new file mode 100644 index 00000000000..043255221f3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMap.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.compiler; + +import java.util.*; + +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class OopMap extends VMObject { + private static CIntegerField pcOffsetField; + private static CIntegerField omvCountField; + private static CIntegerField omvDataSizeField; + private static AddressField omvDataField; + private static AddressField compressedWriteStreamField; + + // This is actually a field inside class CompressedStream + private static AddressField compressedStreamBufferField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("OopMap"); + + pcOffsetField = type.getCIntegerField("_pc_offset"); + omvCountField = type.getCIntegerField("_omv_count"); + omvDataSizeField = type.getCIntegerField("_omv_data_size"); + omvDataField = type.getAddressField("_omv_data"); + compressedWriteStreamField = type.getAddressField("_write_stream"); + + type = db.lookupType("CompressedStream"); + compressedStreamBufferField = type.getAddressField("_buffer"); + } + + public OopMap(Address addr) { + super(addr); + } + + public long getOffset() { + return pcOffsetField.getValue(addr); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + // Accessors -- package private for now + Address getOMVData() { + return omvDataField.getValue(addr); + } + + long getOMVDataSize() { + return omvDataSizeField.getValue(addr); + } + + long getOMVCount() { + return omvCountField.getValue(addr); + } + + CompressedWriteStream getWriteStream() { + Address wsAddr = compressedWriteStreamField.getValue(addr); + if (wsAddr == null) { + return null; + } + Address bufferAddr = compressedStreamBufferField.getValue(wsAddr); + if (bufferAddr == null) { + return null; + } + return new CompressedWriteStream(bufferAddr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapSet.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapSet.java new file mode 100644 index 00000000000..cbf86acdc7c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapSet.java @@ -0,0 +1,288 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.compiler; + +import java.util.*; + +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class OopMapSet extends VMObject { + private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.compiler.OopMapSet.DEBUG") != null; + + private static CIntegerField omCountField; + private static CIntegerField omSizeField; + private static AddressField omDataField; + private static int REG_COUNT; + private static int SAVED_ON_ENTRY_REG_COUNT; + private static int C_SAVED_ON_ENTRY_REG_COUNT; + private static class MyVisitor implements OopMapVisitor { + private AddressVisitor addressVisitor; + + public MyVisitor(AddressVisitor oopVisitor) { + setAddressVisitor(oopVisitor); + } + + public void setAddressVisitor(AddressVisitor addressVisitor) { + this.addressVisitor = addressVisitor; + } + + public void visitOopLocation(Address oopAddr) { + addressVisitor.visitAddress(oopAddr); + } + + public void visitDerivedOopLocation(Address baseOopAddr, Address derivedOopAddr) { + if (VM.getVM().isClientCompiler()) { + Assert.that(false, "should not reach here"); + } else if (VM.getVM().isServerCompiler() && + VM.getVM().useDerivedPointerTable()) { + Assert.that(false, "FIXME: add derived pointer table"); + } + } + + public void visitValueLocation(Address valueAddr) { + } + + public void visitDeadLocation(Address deadAddr) { + } + } + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("OopMapSet"); + + omCountField = type.getCIntegerField("_om_count"); + omSizeField = type.getCIntegerField("_om_size"); + omDataField = type.getAddressField("_om_data"); + + if (!VM.getVM().isCore()) { + REG_COUNT = db.lookupIntConstant("REG_COUNT").intValue(); + if (VM.getVM().isServerCompiler()) { + SAVED_ON_ENTRY_REG_COUNT = (int) db.lookupIntConstant("SAVED_ON_ENTRY_REG_COUNT").intValue(); + C_SAVED_ON_ENTRY_REG_COUNT = (int) db.lookupIntConstant("C_SAVED_ON_ENTRY_REG_COUNT").intValue(); + } + } + } + + public OopMapSet(Address addr) { + super(addr); + } + + /** Returns the number of OopMaps in this OopMapSet */ + public long getSize() { + return omCountField.getValue(addr); + } + + /** returns the OopMap at a given index */ + public OopMap getMapAt(int index) { + if (Assert.ASSERTS_ENABLED) { + Assert.that((index >= 0) && (index <= getSize()),"bad index"); + } + Address omDataAddr = omDataField.getValue(addr); + Address oopMapAddr = omDataAddr.getAddressAt(index * VM.getVM().getAddressSize()); + if (oopMapAddr == null) { + return null; + } + return new OopMap(oopMapAddr); + } + + public OopMap findMapAtOffset(long pcOffset, boolean debugging) { + int i; + int len = (int) getSize(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(len > 0, "must have pointer maps"); + } + + // Scan through oopmaps. Stop when current offset is either equal or greater + // than the one we are looking for. + for (i = 0; i < len; i++) { + if (getMapAt(i).getOffset() >= pcOffset) { + break; + } + } + + if (!debugging) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(i < len, "oopmap not found for pcOffset = " + pcOffset + "; len = " + len); + Assert.that(getMapAt(i).getOffset() == pcOffset, "oopmap not found"); + } + } else { + if (i == len) { + if (DEBUG) { + System.out.println("can't find oopmap at " + pcOffset); + System.out.print("Oopmap offsets are [ "); + for (i = 0; i < len; i++) { + System.out.print(getMapAt(i).getOffset()); + } + System.out.println("]"); + } + i = len - 1; + return getMapAt(i); + } + } + + OopMap m = getMapAt(i); + return m; + } + + /** Visitation -- iterates through the frame for a compiled method. + This is a very generic mechanism that requires the Address to be + dereferenced by the callee. Other, more specialized, visitation + mechanisms are given below. */ + public static void oopsDo(Frame fr, CodeBlob cb, RegisterMap regMap, AddressVisitor oopVisitor, boolean debugging) { + allDo(fr, cb, regMap, new MyVisitor(oopVisitor), debugging); + } + + /** Note that there are 4 required AddressVisitors: one for oops, + one for derived oops, one for values, and one for dead values */ + public static void allDo(Frame fr, CodeBlob cb, RegisterMap regMap, OopMapVisitor visitor, boolean debugging) { + if (Assert.ASSERTS_ENABLED) { + CodeBlob tmpCB = VM.getVM().getCodeCache().findBlob(fr.getPC()); + Assert.that(tmpCB != null && cb.equals(tmpCB), "wrong codeblob passed in"); + } + + OopMapSet maps = cb.getOopMaps(); + OopMap map = cb.getOopMapForReturnAddress(fr.getPC(), debugging); + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "no ptr map found"); + } + + // handle derived pointers first (otherwise base pointer may be + // changed before derived pointer offset has been collected) + OopMapValue omv; + { + for (OopMapStream oms = new OopMapStream(map, OopMapValue.OopTypes.DERIVED_OOP_VALUE); !oms.isDone(); oms.next()) { + if (VM.getVM().isClientCompiler()) { + Assert.that(false, "should not reach here"); + } + omv = oms.getCurrent(); + Address loc = fr.oopMapRegToLocation(omv.getReg(), regMap); + if (loc != null) { + Address baseLoc = fr.oopMapRegToLocation(omv.getContentReg(), regMap); + Address derivedLoc = loc; + visitor.visitDerivedOopLocation(baseLoc, derivedLoc); + } + } + } + + // We want dead, value and oop oop_types + OopMapValue.OopTypes[] values = new OopMapValue.OopTypes[] { + OopMapValue.OopTypes.OOP_VALUE, OopMapValue.OopTypes.VALUE_VALUE, OopMapValue.OopTypes.DEAD_VALUE + }; + + { + for (OopMapStream oms = new OopMapStream(map, values); !oms.isDone(); oms.next()) { + omv = oms.getCurrent(); + Address loc = fr.oopMapRegToLocation(omv.getReg(), regMap); + if (loc != null) { + if (omv.getType() == OopMapValue.OopTypes.OOP_VALUE) { + // This assert commented out because this will be useful + // to detect in the debugging system + // assert(Universe::is_heap_or_null(*loc), "found non oop pointer"); + visitor.visitOopLocation(loc); + } else if (omv.getType() == OopMapValue.OopTypes.VALUE_VALUE) { + visitor.visitValueLocation(loc); + } else if (omv.getType() == OopMapValue.OopTypes.DEAD_VALUE) { + visitor.visitDeadLocation(loc); + } + } + } + } + } + + /** Update callee-saved register info for the following frame. + Should only be called in non-core builds. */ + public static void updateRegisterMap(Frame fr, CodeBlob cb, RegisterMap regMap, boolean debugging) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!VM.getVM().isCore(), "non-core builds only"); + } + + if (!VM.getVM().isDebugging()) { + if (Assert.ASSERTS_ENABLED) { + OopMapSet maps = cb.getOopMaps(); + Assert.that((maps != null) && (maps.getSize() > 0), "found null or empty OopMapSet for CodeBlob"); + } + } else { + // Hack for some topmost frames that have been found with empty + // OopMapSets. (Actually have not seen the null case, but don't + // want to take any chances.) See HSDB.showThreadStackMemory(). + OopMapSet maps = cb.getOopMaps(); + if ((maps == null) || (maps.getSize() == 0)) { + return; + } + } + + // Check if caller must update oop argument + regMap.setIncludeArgumentOops(cb.callerMustGCArguments(regMap.getThread())); + + int nofCallee = 0; + Address[] locs = new Address[2 * REG_COUNT + 1]; + VMReg [] regs = new VMReg [2 * REG_COUNT + 1]; + // ("+1" because REG_COUNT might be zero) + + // Scan through oopmap and find location of all callee-saved registers + // (we do not do update in place, since info could be overwritten) + OopMap map = cb.getOopMapForReturnAddress(fr.getPC(), debugging); + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "no ptr map found"); + } + + OopMapValue omv = null; + for(OopMapStream oms = new OopMapStream(map, OopMapValue.OopTypes.CALLEE_SAVED_VALUE); !oms.isDone(); oms.next()) { + omv = oms.getCurrent(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(nofCallee < 2 * REG_COUNT, "overflow"); + } + regs[nofCallee] = omv.getContentReg(); + locs[nofCallee] = fr.oopMapRegToLocation(omv.getReg(), regMap); + nofCallee++; + } + + // Check that runtime stubs save all callee-saved registers + // After adapter frames were deleted C2 doesn't use callee save registers at present + if (Assert.ASSERTS_ENABLED) { + if (VM.getVM().isServerCompiler()) { + Assert.that(!cb.isRuntimeStub() || + (nofCallee >= SAVED_ON_ENTRY_REG_COUNT || nofCallee >= C_SAVED_ON_ENTRY_REG_COUNT), + "must save all"); + } + } + + // Copy found callee-saved register to reg_map + for (int i = 0; i < nofCallee; i++) { + regMap.setLocation(regs[i], locs[i]); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapStream.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapStream.java new file mode 100644 index 00000000000..aacc4c02420 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapStream.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.compiler; + +import sun.jvm.hotspot.code.*; + +public class OopMapStream { + private CompressedReadStream stream; + private OopMap oopMap; + private int mask; + private int size; + private int position; + private OopMapValue omv; + private boolean omvValid; + + public OopMapStream(OopMap oopMap) { + this(oopMap, (OopMapValue.OopTypes[]) null); + } + + public OopMapStream(OopMap oopMap, OopMapValue.OopTypes type) { + this(oopMap, (OopMapValue.OopTypes[]) null); + mask = type.getValue(); + } + + public OopMapStream(OopMap oopMap, OopMapValue.OopTypes[] types) { + if (oopMap.getOMVData() == null) { + stream = new CompressedReadStream(oopMap.getWriteStream().getBuffer()); + } else { + stream = new CompressedReadStream(oopMap.getOMVData()); + } + mask = computeMask(types); + size = (int) oopMap.getOMVCount(); + position = 0; + omv = new OopMapValue(); + omvValid = false; + } + + public boolean isDone() { + if (!omvValid) { + findNext(); + } + return !omvValid; + } + + public void next() { + findNext(); + } + + public OopMapValue getCurrent() { + return omv; + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private int computeMask(OopMapValue.OopTypes[] types) { + mask = 0; + if (types != null) { + for (int i = 0; i < types.length; i++) { + mask |= types[i].getValue(); + } + } + return mask; + } + + private void findNext() { + while (position++ < size) { + omv.readFrom(stream); + if ((omv.getType().getValue() & mask) > 0) { + omvValid = true; + return; + } + } + omvValid = false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapValue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapValue.java new file mode 100644 index 00000000000..b8edcc1fb08 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapValue.java @@ -0,0 +1,154 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.compiler; + +import java.util.*; + +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class OopMapValue { + private short value; + private short contentReg; + + /** Read from target VM; located in compiler/oopMap.hpp */ + // How bits are organized + static int TYPE_BITS; + static int REGISTER_BITS; + static int TYPE_SHIFT; + static int REGISTER_SHIFT; + static int TYPE_MASK; + static int TYPE_MASK_IN_PLACE; + static int REGISTER_MASK; + static int REGISTER_MASK_IN_PLACE; + + // Types of OopValues + static int UNUSED_VALUE; + static int OOP_VALUE; + static int VALUE_VALUE; + static int DEAD_VALUE; + static int CALLEE_SAVED_VALUE; + static int DERIVED_OOP_VALUE; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + TYPE_BITS = db.lookupIntConstant("OopMapValue::type_bits").intValue(); + REGISTER_BITS = db.lookupIntConstant("OopMapValue::register_bits").intValue(); + TYPE_SHIFT = db.lookupIntConstant("OopMapValue::type_shift").intValue(); + REGISTER_SHIFT = db.lookupIntConstant("OopMapValue::register_shift").intValue(); + TYPE_MASK = db.lookupIntConstant("OopMapValue::type_mask").intValue(); + TYPE_MASK_IN_PLACE = db.lookupIntConstant("OopMapValue::type_mask_in_place").intValue(); + REGISTER_MASK = db.lookupIntConstant("OopMapValue::register_mask").intValue(); + REGISTER_MASK_IN_PLACE = db.lookupIntConstant("OopMapValue::register_mask_in_place").intValue(); + UNUSED_VALUE = db.lookupIntConstant("OopMapValue::unused_value").intValue(); + OOP_VALUE = db.lookupIntConstant("OopMapValue::oop_value").intValue(); + VALUE_VALUE = db.lookupIntConstant("OopMapValue::value_value").intValue(); + DEAD_VALUE = db.lookupIntConstant("OopMapValue::dead_value").intValue(); + CALLEE_SAVED_VALUE = db.lookupIntConstant("OopMapValue::callee_saved_value").intValue(); + DERIVED_OOP_VALUE = db.lookupIntConstant("OopMapValue::derived_oop_value").intValue(); + } + + public static abstract class OopTypes { + public static final OopTypes UNUSED_VALUE = new OopTypes() { int getValue() { return OopMapValue.UNUSED_VALUE; }}; + public static final OopTypes OOP_VALUE = new OopTypes() { int getValue() { return OopMapValue.OOP_VALUE; }}; + public static final OopTypes VALUE_VALUE = new OopTypes() { int getValue() { return OopMapValue.VALUE_VALUE; }}; + public static final OopTypes DEAD_VALUE = new OopTypes() { int getValue() { return OopMapValue.DEAD_VALUE; }}; + public static final OopTypes CALLEE_SAVED_VALUE = new OopTypes() { int getValue() { return OopMapValue.CALLEE_SAVED_VALUE; }}; + public static final OopTypes DERIVED_OOP_VALUE = new OopTypes() { int getValue() { return OopMapValue.DERIVED_OOP_VALUE; }}; + + abstract int getValue(); + protected OopTypes() {} + } + + public OopMapValue() { setValue((short) 0); setContentReg(new VMReg(0)); } + public OopMapValue(VMReg reg, OopTypes t) { setReg(reg); setType(t); } + public OopMapValue(VMReg reg, OopTypes t, VMReg reg2) { setReg(reg); setType(t); setContentReg(reg2); } + public OopMapValue(CompressedReadStream stream) { readFrom(stream); } + + public void readFrom(CompressedReadStream stream) { + setValue((short) stream.readInt()); + if (isCalleeSaved() || isDerivedOop()) { + setContentReg(new VMReg(stream.readInt())); + } + } + + // Querying + public boolean isOop() { return (getValue() & TYPE_MASK_IN_PLACE) == OOP_VALUE; } + public boolean isValue() { return (getValue() & TYPE_MASK_IN_PLACE) == VALUE_VALUE; } + public boolean isDead() { return (getValue() & TYPE_MASK_IN_PLACE) == DEAD_VALUE; } + public boolean isCalleeSaved() { return (getValue() & TYPE_MASK_IN_PLACE) == CALLEE_SAVED_VALUE; } + public boolean isDerivedOop() { return (getValue() & TYPE_MASK_IN_PLACE) == DERIVED_OOP_VALUE; } + + public VMReg getReg() { return new VMReg((getValue() & REGISTER_MASK_IN_PLACE) >> REGISTER_SHIFT); } + public void setReg(VMReg r) { setValue((short) (r.getValue() << REGISTER_SHIFT | (getValue() & TYPE_MASK_IN_PLACE))); } + + public OopTypes getType() { + int which = (getValue() & TYPE_MASK_IN_PLACE); + if (which == UNUSED_VALUE) return OopTypes.UNUSED_VALUE; + else if (which == OOP_VALUE) return OopTypes.OOP_VALUE; + else if (which == VALUE_VALUE) return OopTypes.VALUE_VALUE; + else if (which == DEAD_VALUE) return OopTypes.DEAD_VALUE; + else if (which == CALLEE_SAVED_VALUE) return OopTypes.CALLEE_SAVED_VALUE; + else if (which == DERIVED_OOP_VALUE) return OopTypes.DERIVED_OOP_VALUE; + else throw new InternalError("unknown which " + which + " (TYPE_MASK_IN_PLACE = " + TYPE_MASK_IN_PLACE + ")"); + } + public void setType(OopTypes t) { setValue((short) ((getValue() & REGISTER_MASK_IN_PLACE) | t.getValue())); } + + public VMReg getContentReg() { return new VMReg(contentReg); } + public void setContentReg(VMReg r) { contentReg = (short) r.getValue(); } + + /** Physical location queries */ + public boolean isRegisterLoc() { return (getReg().lessThan(VM.getVM().getVMRegImplInfo().getStack0())); } + public boolean isStackLoc() { return (getReg().greaterThanOrEqual(VM.getVM().getVMRegImplInfo().getStack0())); } + + /** Returns offset from sp. */ + public int getStackOffset() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isStackLoc(), "must be stack location"); + } + return getReg().minus(VM.getVM().getVMRegImplInfo().getStack0()); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private void setValue(short value) { + this.value = value; + } + + private int getValue() { + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapVisitor.java new file mode 100644 index 00000000000..379927dda74 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/OopMapVisitor.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.compiler; + +import sun.jvm.hotspot.debugger.*; + +/** Adaptation of the oop visitation mechanism to Java. */ + +public interface OopMapVisitor { + public void visitOopLocation(Address oopAddr); + public void visitDerivedOopLocation(Address baseOopAddr, Address derivedOopAddr); + public void visitValueLocation(Address valueAddr); + public void visitDeadLocation(Address deadAddr); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/Address.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/Address.java new file mode 100644 index 00000000000..dd02c304b3f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/Address.java @@ -0,0 +1,207 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/**

This is the bottom-most interface which abstracts address + access for both debugging and introspection. In the situation of + debugging a target VM, these routines can throw the specified + RuntimeExceptions to indicate failure and allow recovery of the + debugging system. If these are used for introspecting the current + VM and implementing functionality in it, however, it is expected + that these kinds of failures will not occur and, in fact, a crash + will occur if the situation arises where they would have been + thrown.

+ +

Addresses are immutable. Further, it was decided not to expose + the representation of the Address (and provide a corresponding + factory method from, for example, long to Address). Unfortunately, + because of the existence of C and "reuse" of low bits of pointers, + it is occasionally necessary to perform logical operations like + masking off the low bits of an "address". While these operations + could be used to generate arbitrary Address objects, allowing this + is not the intent of providing these operations.

+ +

This interface is able to fetch all Java primitive types, + addresses, oops, and C integers of arbitrary size (see @see + sun.jvm.hotspot.types.CIntegerType for further discussion). Note + that the result of the latter is restricted to fitting into a + 64-bit value and the high-order bytes will be silently discarded + if too many bytes are requested.

+ +

Implementations may have restrictions, for example that the + Java-related routines may not be called until a certain point in + the bootstrapping process once the sizes of the Java primitive + types are known. (The current implementation has that property.) +

+ +

A note of warning: in C addresses, when represented as + integers, are usually represented with unsigned types. + Unfortunately, there are no unsigned primitive types in Java, so + care will have to be taken in the implementation of this interface + if using longs as the representation for 64-bit correctness. This + is not so simple for the comparison operators.

*/ + +public interface Address { + + /** This is stated explicitly here because it is important for + implementations to understand that equals() and hashCode() must + absolutely, positively work properly -- i.e., two Address + objects representing the same address are both equal (via + equals()) and have the same hash code. */ + public boolean equals(Object arg); + + /** This is stated explicitly here because it is important for + implementations to understand that equals() and hashCode() must + absolutely, positively work properly -- i.e., two Address + objects representing the same address are both equal (via + equals()) and have the same hash code. */ + public int hashCode(); + + // + // C/C++-related routines + // + + public long getCIntegerAt (long offset, long numBytes, boolean isUnsigned) + throws UnmappedAddressException, UnalignedAddressException; + /** This returns null if the address at the given offset is NULL. */ + public Address getAddressAt (long offset) throws UnmappedAddressException, UnalignedAddressException; + + // + // Java-related routines + // + + public boolean getJBooleanAt (long offset) throws UnmappedAddressException, UnalignedAddressException; + public byte getJByteAt (long offset) throws UnmappedAddressException, UnalignedAddressException; + public char getJCharAt (long offset) throws UnmappedAddressException, UnalignedAddressException; + public double getJDoubleAt (long offset) throws UnmappedAddressException, UnalignedAddressException; + public float getJFloatAt (long offset) throws UnmappedAddressException, UnalignedAddressException; + public int getJIntAt (long offset) throws UnmappedAddressException, UnalignedAddressException; + public long getJLongAt (long offset) throws UnmappedAddressException, UnalignedAddressException; + public short getJShortAt (long offset) throws UnmappedAddressException, UnalignedAddressException; + /** This returns null if the address at the given offset is NULL. */ + public OopHandle getOopHandleAt (long offset) + throws UnmappedAddressException, UnalignedAddressException, NotInHeapException; + + // + // C/C++-related mutators. These throw UnmappedAddressException if + // the target is read-only (for example, a core file rather than an + // active process), if the target address is unmapped, or if it is + // read-only. The implementation may supply extra detail messages. + // + + /** Sets a C integer numBytes in size at the specified offset. Note + that there is no "unsigned" flag for the accessor since the + value will not be sign-extended; the number of bytes are simply + copied from the value into the target address space. */ + public void setCIntegerAt(long offset, long numBytes, long value); + + /** Sets an Address at the specified location. */ + public void setAddressAt(long offset, Address value); + + // + // Java-related mutators -- see above. + // + + public void setJBooleanAt (long offset, boolean value) + throws UnmappedAddressException, UnalignedAddressException; + public void setJByteAt (long offset, byte value) + throws UnmappedAddressException, UnalignedAddressException; + public void setJCharAt (long offset, char value) + throws UnmappedAddressException, UnalignedAddressException; + public void setJDoubleAt (long offset, double value) + throws UnmappedAddressException, UnalignedAddressException; + public void setJFloatAt (long offset, float value) + throws UnmappedAddressException, UnalignedAddressException; + public void setJIntAt (long offset, int value) + throws UnmappedAddressException, UnalignedAddressException; + public void setJLongAt (long offset, long value) + throws UnmappedAddressException, UnalignedAddressException; + public void setJShortAt (long offset, short value) + throws UnmappedAddressException, UnalignedAddressException; + public void setOopHandleAt (long offset, OopHandle value) + throws UnmappedAddressException, UnalignedAddressException; + + // + // Arithmetic operations -- necessary evil. + // + + /** This throws an UnsupportedOperationException if this address happens + to actually be an OopHandle, because interior object pointers + are not allowed. Negative offsets are allowed and handle the + subtraction case. */ + public Address addOffsetTo (long offset) throws UnsupportedOperationException; + + /** This method had to be added in order to support heap iteration + in the debugging system, and is effectively the dangerous + operation of allowing interior object pointers. For this reason + it was kept as a separate API and its use is forbidden in the + non-debugging (i.e., reflective system) case. It is strongly + recommended that this not be called by clients: it is currently + wrapped up in the Space's iteration mechanism. */ + public OopHandle addOffsetToAsOopHandle(long offset) throws UnsupportedOperationException; + + /** Performs the subtraction "this - arg", returning the resulting + offset in bytes. Note that this must handle a null argument + properly, and can be used to convert an Address into a long for + further manipulation, but that the reverse conversion is not + possible. (FIXME: any signed/unsigned issues? Should this work + for OopHandles?) */ + public long minus(Address arg); + + /** Performs unsigned comparison "this < arg". + (FIXME: should this work for OopHandles?) */ + public boolean lessThan (Address arg); + /** Performs unsigned comparison "this <= arg". + (FIXME: should this work for OopHandles?) */ + public boolean lessThanOrEqual (Address arg); + /** Performs unsigned comparison "this > arg". + (FIXME: should this work for OopHandles?) */ + public boolean greaterThan (Address arg); + /** Performs unsigned comparison "this >= arg". + (FIXME: should this work for OopHandles?) */ + public boolean greaterThanOrEqual(Address arg); + + /** This throws an UnsupportedOperationException if this address happens + to actually be an OopHandle. Performs a logical "and" operation + of the bits of the address and the mask (least significant bits + of the Address and the mask are aligned) and returns the result + as an Address. Returns null if the result was zero. */ + public Address andWithMask(long mask) throws UnsupportedOperationException; + + /** This throws an UnsupportedOperationException if this address happens + to actually be an OopHandle. Performs a logical "or" operation + of the bits of the address and the mask (least significant bits + of the Address and the mask are aligned) and returns the result + as an Address. Returns null if the result was zero. */ + public Address orWithMask(long mask) throws UnsupportedOperationException; + + /** This throws an UnsupportedOperationException if this address happens + to actually be an OopHandle. Performs a logical "exclusive or" + operation of the bits of the address and the mask (least + significant bits of the Address and the mask are aligned) and + returns the result as an Address. Returns null if the result was + zero. */ + public Address xorWithMask(long mask) throws UnsupportedOperationException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/AddressException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/AddressException.java new file mode 100644 index 00000000000..bc44e5f489a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/AddressException.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class AddressException extends RuntimeException { + private long addr; + + public AddressException(long addr) { + this.addr = addr; + } + + public AddressException(String detail, long addr) { + super(detail); + this.addr = addr; + } + + public long getAddress() { + return addr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DataSource.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DataSource.java new file mode 100644 index 00000000000..dc0def9adf0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DataSource.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +import java.io.*; + +/** An abstraction which represents a seekable data source. + RandomAccessFile can be trivially mapped to this; in addition, we + can support an adapter for addresses, so we can parse DLLs + directly out of the remote process's address space. This class is + used by the Windows COFF and Posix ELF implementations. */ + +public interface DataSource { + public byte readByte() throws IOException; + public short readShort() throws IOException; + public int readInt() throws IOException; + public long readLong() throws IOException; + public int read(byte[] b) throws IOException; + public void seek(long pos) throws IOException; + public long getFilePointer() throws IOException; + public void close() throws IOException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/Debugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/Debugger.java new file mode 100644 index 00000000000..d1575e8f75d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/Debugger.java @@ -0,0 +1,127 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +import java.util.*; +import sun.jvm.hotspot.debugger.cdbg.CDebugger; + +public interface Debugger extends SymbolLookup, ThreadAccess { + /** Indicates whether this underlying debugger can provide a list of + currently-running processes. */ + public boolean hasProcessList() throws DebuggerException; + + /** Provide a snapshot of the list of currently-running processes in + the form of a List of ProcessInfo objects. Must only be called + if hasProcessList(), above, returns true. */ + public List getProcessList() throws DebuggerException; + + /** If an error occurs during attachment (i.e., "no such process"), + the thrown DebuggerException will contain a description of the + error in its message string. */ + public void attach(int processID) throws DebuggerException; + + /** This attaches the debugger to the given coreFileName, which is + assumed to have been generated from the specified + executableName. If an error occurs during loading of the core + file (i.e., "no such file"), the thrown DebuggerException will + contain a description of the error in its message string. */ + public void attach(String executableName, String coreFileName) + throws DebuggerException; + + /** Detach from the remote process. Returns false if not currently + attached. */ + public boolean detach() throws DebuggerException; + + /** Parse an address from a hex string in the format "0xFFFFFFFF". + The length of the address (i.e., 32 or 64 bits) is platform + dependent. This method should ONLY be used by routines which + need to interact with the user and parse a string entered by + hand; for example, a graphical user interface. This routine + should NOT be used to subvert the current safety mechanisms in + the system which prevent arbitrary conversion from Address to + long and back. */ + public Address parseAddress(String addressString) + throws NumberFormatException, DebuggerException; + + /** Returns the 64-bit value of an Address. This method should ONLY + be used when implementing a debugger which needs to interface to + C and which needs a unique identifier for certain objects. */ + public long getAddressValue(Address addr) throws DebuggerException; + + /** Support for remote debugging. Get the name of the operating + system on which this debugger is running (to be able to properly + configure the local system). Typical return values are + "solaris", "linux", "win32"; see utilities/PlatformInfo.java. */ + public String getOS() throws DebuggerException; + + /** Support for remote debugging. Get the name of the CPU type on + which this debugger is running (to be able to properly configure + the local system). Typical return values are "sparc", "x86"; see + utilities/PlatformInfo.java. */ + public String getCPU() throws DebuggerException; + + /** Retrieve the machine description for the underlying hardware for + the cases in which we need to do, for example, machine-dependent + byte swapping */ + public MachineDescription getMachineDescription() throws DebuggerException; + + /** Find out whether this debugger has a console available on which + commands can be executed; see executeCommandOnConsole, below. + This is an interim routine designed to allow access to the + underlying dbx process on Solaris until we have disassembly, + etc. in the SA. */ + public boolean hasConsole() throws DebuggerException; + + /** If the underlying debugger has a console (as dbx does), this + provides access to it. Takes in a platform-dependent String, + executes it on the debugger's console, and returns any output as + a String. */ + public String consoleExecuteCommand(String cmd) throws DebuggerException; + + /** If the underlying debugger has a console, this returns the + debugger-specific prompt which should be displayed. */ + public String getConsolePrompt() throws DebuggerException; + + /** If this platform supports C/C++ debugging via the CDebugger + interface, returns a CDebugger object; otherwise returns + null. */ + public CDebugger getCDebugger() throws DebuggerException; + + /** the following methods are intended only for RemoteDebuggerClient */ + public long getJBooleanSize(); + public long getJByteSize(); + public long getJCharSize(); + public long getJDoubleSize(); + public long getJFloatSize(); + public long getJIntSize(); + public long getJLongSize(); + public long getJShortSize(); + + public ReadResult readBytesFromProcess(long address, long numBytes) + throws DebuggerException; + + public void writeBytesToProcess(long address, long numBytes, byte[] data) + throws UnmappedAddressException, DebuggerException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DebuggerBase.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DebuggerBase.java new file mode 100644 index 00000000000..75d10ca2275 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DebuggerBase.java @@ -0,0 +1,521 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/**

DebuggerBase is a recommended base class for debugger + implementations. It can use a PageCache to cache data from the + target process. Note that this class would not be suitable if the + system were used to reflect upon itself; it would never be safe to + store the value in an OopHandle in anything but an OopHandle. + However, it provides a fair amount of code sharing to the current + dbx and win32 implementations.

+ +

NOTE that much of the code sharing is achieved by having this + class implement many of the methods in the Win32Debugger and + DbxDebugger interfaces.

*/ + +public abstract class DebuggerBase implements Debugger { + // May be set lazily, but must be set before calling any of the read + // routines below + protected MachineDescription machDesc; + protected DebuggerUtilities utils; + // Java primitive type sizes, set during bootstrapping. Do not call + // any of the Java read routines until these are set up. + protected long jbooleanSize; + protected long jbyteSize; + protected long jcharSize; + protected long jdoubleSize; + protected long jfloatSize; + protected long jintSize; + protected long jlongSize; + protected long jshortSize; + protected boolean javaPrimitiveTypesConfigured; + // Should be initialized if desired by calling initCache() + private PageCache cache; + + // State for faster accessors that don't allocate memory on each read + private boolean useFastAccessors; + private boolean bigEndian; + + // Page-fetching functionality for LRU cache + class Fetcher implements PageFetcher { + public Page fetchPage(long pageBaseAddress, long numBytes) { + // This assumes that if any byte is unmapped, that the entire + // page is. The common case, however, is that the page is + // mapped, so we always fetch the entire thing all at once to + // avoid two round-trip communications per page fetch, even + // though fetching of unmapped pages will be slow. + ReadResult res = readBytesFromProcess(pageBaseAddress, numBytes); + if (res.getData() == null) { + return new Page(pageBaseAddress, numBytes); + } + return new Page(pageBaseAddress, res.getData()); + } + } + + protected DebuggerBase() { + } + + /** From the JVMDebugger interface. This is the only public method + of this class. */ + public void configureJavaPrimitiveTypeSizes(long jbooleanSize, + long jbyteSize, + long jcharSize, + long jdoubleSize, + long jfloatSize, + long jintSize, + long jlongSize, + long jshortSize) { + this.jbooleanSize = jbooleanSize; + this.jbyteSize = jbyteSize; + this.jcharSize = jcharSize; + this.jdoubleSize = jdoubleSize; + this.jfloatSize = jfloatSize; + this.jintSize = jintSize; + this.jlongSize = jlongSize; + this.jshortSize = jshortSize; + + if (jbooleanSize < 1) { + throw new RuntimeException("jboolean size is too small"); + } + + if (jbyteSize < 1) { + throw new RuntimeException("jbyte size is too small"); + } + + if (jcharSize < 2) { + throw new RuntimeException("jchar size is too small"); + } + + if (jdoubleSize < 8) { + throw new RuntimeException("jdouble size is too small"); + } + + if (jfloatSize < 4) { + throw new RuntimeException("jfloat size is too small"); + } + + if (jintSize < 4) { + throw new RuntimeException("jint size is too small"); + } + + if (jlongSize < 8) { + throw new RuntimeException("jlong size is too small"); + } + + if (jshortSize < 2) { + throw new RuntimeException("jshort size is too small"); + } + + if (jintSize != jfloatSize) { + // If dataToJFloat were rewritten, this wouldn't be necessary + throw new RuntimeException("jint size and jfloat size must be equal"); + } + + if (jlongSize != jdoubleSize) { + // If dataToJDouble were rewritten, this wouldn't be necessary + throw new RuntimeException("jlong size and jdouble size must be equal"); + } + + useFastAccessors = + ((cache != null) && + (jbooleanSize == 1) && + (jbyteSize == 1) && + (jcharSize == 2) && + (jdoubleSize == 8) && + (jfloatSize == 4) && + (jintSize == 4) && + (jlongSize == 8) && + (jshortSize == 2)); + + javaPrimitiveTypesConfigured = true; + } + + /** May be called by subclasses if desired to initialize the page + cache but may not be overridden */ + protected final void initCache(long pageSize, long maxNumPages) { + cache = new PageCache(pageSize, maxNumPages, new Fetcher()); + if (machDesc != null) { + bigEndian = machDesc.isBigEndian(); + } + } + + /** May be called by subclasses if needed (if the machine + description is not available at the time of cache + initialization, as on Solaris) but may not be overridden */ + protected final void setBigEndian(boolean bigEndian) { + this.bigEndian = bigEndian; + } + + /** May be called by subclasses to clear out the cache but may not + be overridden. For convenience, this can be called even if the + cache has not been initialized. */ + protected final void clearCache() { + if (cache != null) { + cache.clear(); + } + } + + /** May be called by subclasses to disable the cache (for example, + when the target process has been resumed) but may not be + overridden. For convenience, this can be called even if the + cache has not been initialized. */ + protected final void disableCache() { + if (cache != null) { + cache.disable(); + } + } + + /** May be called by subclasses to re-enable the cache (for example, + when the target process has been suspended) but may not be + overridden. For convenience, this can be called even if the + cache has not been initialized. */ + protected final void enableCache() { + if (cache != null) { + cache.enable(); + } + } + + /** May be called by subclasses directly but may not be overridden */ + protected final byte[] readBytes(long address, long numBytes) + throws UnmappedAddressException, DebuggerException { + if (cache != null) { + return cache.getData(address, numBytes); + } else { + ReadResult res = readBytesFromProcess(address, numBytes); + if (res.getData() != null) { + return res.getData(); + } + throw new UnmappedAddressException(res.getFailureAddress()); + } + } + + /** May be called by subclasses directly but may not be overridden */ + protected final void writeBytes(long address, long numBytes, byte[] data) + throws UnmappedAddressException, DebuggerException { + if (cache != null) { + cache.clear(address, numBytes); + } + writeBytesToProcess(address, numBytes, data); + } + + public boolean readJBoolean(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jbooleanSize); + if (useFastAccessors) { + return (cache.getByte(address) != 0); + } else { + byte[] data = readBytes(address, jbooleanSize); + return utils.dataToJBoolean(data, jbooleanSize); + } + } + + public byte readJByte(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jbyteSize); + if (useFastAccessors) { + return cache.getByte(address); + } else { + byte[] data = readBytes(address, jbyteSize); + return utils.dataToJByte(data, jbyteSize); + } + } + + // NOTE: assumes value does not span pages (may be bad assumption on + // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) + public char readJChar(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jcharSize); + if (useFastAccessors) { + return cache.getChar(address, bigEndian); + } else { + byte[] data = readBytes(address, jcharSize); + return (char) utils.dataToJChar(data, jcharSize); + } + } + + // NOTE: assumes value does not span pages (may be bad assumption on + // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) + public double readJDouble(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jdoubleSize); + if (useFastAccessors) { + return cache.getDouble(address, bigEndian); + } else { + byte[] data = readBytes(address, jdoubleSize); + return utils.dataToJDouble(data, jdoubleSize); + } + } + + // NOTE: assumes value does not span pages (may be bad assumption on + // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) + public float readJFloat(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jfloatSize); + if (useFastAccessors) { + return cache.getFloat(address, bigEndian); + } else { + byte[] data = readBytes(address, jfloatSize); + return utils.dataToJFloat(data, jfloatSize); + } + } + + // NOTE: assumes value does not span pages (may be bad assumption on + // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) + public int readJInt(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jintSize); + if (useFastAccessors) { + return cache.getInt(address, bigEndian); + } else { + byte[] data = readBytes(address, jintSize); + return utils.dataToJInt(data, jintSize); + } + } + + // NOTE: assumes value does not span pages (may be bad assumption on + // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) + public long readJLong(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jlongSize); + if (useFastAccessors) { + return cache.getLong(address, bigEndian); + } else { + byte[] data = readBytes(address, jlongSize); + return utils.dataToJLong(data, jlongSize); + } + } + + // NOTE: assumes value does not span pages (may be bad assumption on + // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) + public short readJShort(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jshortSize); + if (useFastAccessors) { + return cache.getShort(address, bigEndian); + } else { + byte[] data = readBytes(address, jshortSize); + return utils.dataToJShort(data, jshortSize); + } + } + + // NOTE: assumes value does not span pages (may be bad assumption on + // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) + public long readCInteger(long address, long numBytes, boolean isUnsigned) + throws UnmappedAddressException, UnalignedAddressException { + checkConfigured(); + utils.checkAlignment(address, numBytes); + if (useFastAccessors) { + if (isUnsigned) { + switch((int) numBytes) { + case 1: return cache.getByte(address) & 0xFF; + case 2: return cache.getShort(address, bigEndian) & 0xFFFF; + case 4: return cache.getInt(address, bigEndian) & 0xFFFFFFFFL; + case 8: return cache.getLong(address, bigEndian); + default: { + byte[] data = readBytes(address, numBytes); + return utils.dataToCInteger(data, isUnsigned); + } + } + } else { + switch((int) numBytes) { + case 1: return cache.getByte(address); + case 2: return cache.getShort(address, bigEndian); + case 4: return cache.getInt(address, bigEndian); + case 8: return cache.getLong(address, bigEndian); + default: { + byte[] data = readBytes(address, numBytes); + return utils.dataToCInteger(data, isUnsigned); + } + } + } + } else { + byte[] data = readBytes(address, numBytes); + return utils.dataToCInteger(data, isUnsigned); + } + } + + public void writeJBoolean(long address, boolean value) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jbooleanSize); + byte[] data = utils.jbooleanToData(value); + writeBytes(address, jbooleanSize, data); + } + + public void writeJByte(long address, byte value) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jbyteSize); + byte[] data = utils.jbyteToData(value); + writeBytes(address, jbyteSize, data); + } + + public void writeJChar(long address, char value) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jcharSize); + byte[] data = utils.jcharToData(value); + writeBytes(address, jcharSize, data); + } + + public void writeJDouble(long address, double value) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jdoubleSize); + byte[] data = utils.jdoubleToData(value); + writeBytes(address, jdoubleSize, data); + } + + public void writeJFloat(long address, float value) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jfloatSize); + byte[] data = utils.jfloatToData(value); + writeBytes(address, jfloatSize, data); + } + + public void writeJInt(long address, int value) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jintSize); + byte[] data = utils.jintToData(value); + writeBytes(address, jintSize, data); + } + + public void writeJLong(long address, long value) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jlongSize); + byte[] data = utils.jlongToData(value); + writeBytes(address, jlongSize, data); + } + + public void writeJShort(long address, short value) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + utils.checkAlignment(address, jshortSize); + byte[] data = utils.jshortToData(value); + writeBytes(address, jshortSize, data); + } + + public void writeCInteger(long address, long numBytes, long value) + throws UnmappedAddressException, UnalignedAddressException { + checkConfigured(); + utils.checkAlignment(address, numBytes); + byte[] data = utils.cIntegerToData(numBytes, value); + writeBytes(address, numBytes, data); + } + + protected long readAddressValue(long address) + throws UnmappedAddressException, UnalignedAddressException { + return readCInteger(address, machDesc.getAddressSize(), true); + } + + protected void writeAddressValue(long address, long value) + throws UnmappedAddressException, UnalignedAddressException { + writeCInteger(address, machDesc.getAddressSize(), value); + } + + /** Can be called by subclasses but can not be overridden */ + protected final void checkConfigured() { + if (machDesc == null) { + throw new RuntimeException("MachineDescription must have been set by this point"); + } + if (utils == null) { + throw new RuntimeException("DebuggerUtilities must have been set by this point"); + } + } + + /** Can be called by subclasses but can not be overridden */ + protected final void checkJavaConfigured() { + checkConfigured(); + + if (!javaPrimitiveTypesConfigured) { + throw new RuntimeException("Java primitive type sizes have not yet been configured"); + } + } + + /** Possibly override page cache size with user-specified property */ + protected int parseCacheNumPagesProperty(int defaultNum) { + String cacheNumPagesString = System.getProperty("cacheNumPages"); + if (cacheNumPagesString != null) { + try { + return Integer.parseInt(cacheNumPagesString); + } catch (Exception e) { + System.err.println("Error parsing cacheNumPages property:"); + e.printStackTrace(); + } + } + return defaultNum; + } + + /** Interim solution for allowing subclasses to write bytes to + process until we make that functionality available in the basic + Address interface */ + protected void invalidatePageCache(long startAddress, long numBytes) { + cache.clear(startAddress, numBytes); + } + + public long getJBooleanSize() { + return jbooleanSize; + } + + public long getJByteSize() { + return jbyteSize; + } + + public long getJCharSize() { + return jcharSize; + } + + public long getJDoubleSize() { + return jdoubleSize; + } + + public long getJFloatSize() { + return jfloatSize; + } + + public long getJIntSize() { + return jintSize; + } + + public long getJLongSize() { + return jlongSize; + } + + public long getJShortSize() { + return jshortSize; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DebuggerException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DebuggerException.java new file mode 100644 index 00000000000..a8a362d423c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DebuggerException.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class DebuggerException extends RuntimeException { + public DebuggerException() { + super(); + } + + public DebuggerException(String message) { + super(message); + } + + public DebuggerException(String message, Throwable cause) { + super(message, cause); + } + + public DebuggerException(Throwable cause) { + super(cause); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DebuggerUtilities.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DebuggerUtilities.java new file mode 100644 index 00000000000..35a272cbbd5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/DebuggerUtilities.java @@ -0,0 +1,297 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/** Common routines for data conversion */ + +public class DebuggerUtilities { + protected long addressSize; + protected boolean isBigEndian; + + public DebuggerUtilities(long addressSize, boolean isBigEndian) { + this.addressSize = addressSize; + this.isBigEndian = isBigEndian; + } + + public String addressValueToString(long address) { + StringBuffer buf = new StringBuffer(); + buf.append("0x"); + String val; + // Make negative addresses have the correct size + if (addressSize == 8) { + val = Long.toHexString(address); + } else { + val = Integer.toHexString((int) address); + } + for (int i = 0; i < ((2 * addressSize) - val.length()); i++) { + buf.append('0'); + } + buf.append(val); + return buf.toString(); + } + + public void checkAlignment(long address, long alignment) { + if (address % alignment != 0) { + throw new UnalignedAddressException("Trying to read at address: " + + addressValueToString(address) + + " with alignment: " + alignment, + address); + } + } + + public long scanAddress(String addrStr) throws NumberFormatException { + String s = addrStr.trim(); + if (!s.startsWith("0x")) { + throw new NumberFormatException(addrStr); + } + long l = 0; + for (int i = 2; i < s.length(); ++i) { + int val = charToNibble(s.charAt(i)); + l <<= 4; + l |= val; + } + return l; + } + + public int charToNibble(char ascii) throws NumberFormatException { + if (ascii >= '0' && ascii <= '9') { + return ascii - '0'; + } else if (ascii >= 'A' && ascii <= 'F') { + return 10 + ascii - 'A'; + } else if (ascii >= 'a' && ascii <= 'f') { + return 10 + ascii - 'a'; + } + throw new NumberFormatException(new Character(ascii).toString()); + } + + public boolean dataToJBoolean(byte[] data, long jbooleanSize) { + checkDataContents(data, jbooleanSize); + + return (data[0] != 0); + } + + public byte dataToJByte(byte[] data, long jbyteSize) { + checkDataContents(data, jbyteSize); + + return data[0]; + } + + public char dataToJChar(byte[] data, long jcharSize) { + checkDataContents(data, jcharSize); + + if (!isBigEndian) { + byteSwap(data); + } + + return (char) (((data[0] & 0xFF) << 8) | (data[1] & 0xFF)); + } + + public double dataToJDouble(byte[] data, long jdoubleSize) { + long longBits = dataToJLong(data, jdoubleSize); + + return Double.longBitsToDouble(longBits); + } + + public float dataToJFloat(byte[] data, long jfloatSize) { + int intBits = dataToJInt(data, jfloatSize); + + return Float.intBitsToFloat(intBits); + } + + public int dataToJInt(byte[] data, long jintSize) { + checkDataContents(data, jintSize); + + if (!isBigEndian) { + byteSwap(data); + } + + return (((data[0] & 0xFF) << 24) | ((data[1] & 0xFF) << 16) | ((data[2] & 0xFF) << 8) | (data[3] & 0xFF)); + } + + public long dataToJLong(byte[] data, long jlongSize) { + checkDataContents(data, jlongSize); + + if (!isBigEndian) { + byteSwap(data); + } + + return rawDataToJLong(data); + } + + public short dataToJShort(byte[] data, long jshortSize) { + checkDataContents(data, jshortSize); + + if (!isBigEndian) { + byteSwap(data); + } + + return (short) (((data[0] & 0xFF) << 8) | (data[1] & 0xFF)); + } + + public long dataToCInteger(byte[] data, boolean isUnsigned) { + checkDataContents(data, data.length); + + if (!isBigEndian) { + byteSwap(data); + } + + // By default we'll be zero-extending. + // Therefore we need to check to see whether isUnsigned is false and + // also the high bit of the data is set. + if ((data.length < 8) && + (isUnsigned == false) && + ((data[0] & 0x80) != 0)) { + // Must sign-extend and right-align the data + byte[] newData = new byte[8]; + for (int i = 0; i < 8; ++i) { + if ((7 - i) < data.length) { + newData[i] = data[i + data.length - 8]; + } else { + newData[i] = (byte) 0xFF; + } + } + data = newData; + } + + // Now just do the usual loop + return rawDataToJLong(data); + } + + public long dataToAddressValue(byte[] data) { + checkDataContents(data, addressSize); + + if (!isBigEndian) { + byteSwap(data); + } + + return rawDataToJLong(data); + } + + public byte[] jbooleanToData(boolean value) { + byte[] res = new byte[1]; + res[0] = (byte) (value ? 1 : 0); + return res; + } + + public byte[] jbyteToData(byte value) { + byte[] res = new byte[1]; + res[0] = value; + return res; + } + + public byte[] jcharToData(char value) { + byte[] res = new byte[2]; + res[0] = (byte) ((value >> 8) & 0xFF); + res[1] = (byte) value; + if (!isBigEndian) { + byteSwap(res); + } + return res; + } + + public byte[] jdoubleToData(double value) { + return jlongToData(Double.doubleToLongBits(value)); + } + + public byte[] jfloatToData(float value) { + return jintToData(Float.floatToIntBits(value)); + } + + public byte[] jintToData(int value) { + byte[] res = new byte[4]; + for (int i = 0; i < 4; i++) { + res[3 - i] = (byte) (value & 0xFF); + value >>>= 8; + } + if (!isBigEndian) { + byteSwap(res); + } + return res; + } + + public byte[] jlongToData(long value) { + byte[] res = new byte[8]; + for (int i = 0; i < 8; i++) { + res[7 - i] = (byte) (value & 0xFF); + value >>>= 8; + } + if (!isBigEndian) { + byteSwap(res); + } + return res; + } + + public byte[] jshortToData(short value) { + byte[] res = new byte[2]; + res[0] = (byte) ((value >> 8) & 0xFF); + res[1] = (byte) value; + if (!isBigEndian) { + byteSwap(res); + } + return res; + } + + public byte[] cIntegerToData(long longNumBytes, long value) { + int numBytes = (int) longNumBytes; + byte[] res = new byte[numBytes]; + for (int i = 0; i < numBytes; i++) { + res[numBytes - i - 1] = (byte) (value & 0xFF); + value >>>= 8; + } + if (!isBigEndian) { + byteSwap(res); + } + return res; + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private void checkDataContents(byte[] data, long len) { + if (data.length != (int) len) { + throw new InternalError("Bug in Win32Debugger"); + } + } + + private void byteSwap(byte[] data) { + for (int i = 0; i < (data.length / 2); ++i) { + int altIndex = data.length - i - 1; + byte t = data[altIndex]; + data[altIndex] = data[i]; + data[i] = t; + } + } + + private long rawDataToJLong(byte[] data) { + long addr = 0; + for (int i = 0; i < data.length; ++i) { + addr <<= 8; + addr |= data[i] & 0xFF; + } + + return addr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/InputLexer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/InputLexer.java new file mode 100644 index 00000000000..c45cb1d4319 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/InputLexer.java @@ -0,0 +1,212 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +import java.io.*; + +/** InputLexer is the lexer through which the current set of debuggers + see the debug server. It provides the ability to read all of the + types the debuggers are interested in. All read operations are + blocking. */ + +public class InputLexer { + public InputLexer(BufferedInputStream in) throws IOException { + this.in = in; + pushedBack = false; + } + + public void close() throws IOException { + in.close(); + } + + /** Parses a boolean (really either a 0 or 1 integer in US-ASCII + encoding) on the input stream */ + public boolean parseBoolean() throws IOException { + int val = parseInt(); + return (val != 0); + } + + /** Parses an int in US-ASCII encoding on the input stream */ + public int parseInt() throws IOException { + long l = parseLong(); + long mask = 0xFFFFFFFF00000000L; + if ((l & mask) != 0) { + throw new IOException("Overflow error reading int from debug server (read " + l + ")"); + } + return (int) l; + } + + /** Parses a long in US-ASCII encoding on the input stream */ + public long parseLong() throws IOException { + skipWhitespace(); + byte b = readByte(); + if (!Character.isDigit((char) b)) { + error(); + } + long l = 0; + while (Character.isDigit((char) b)) { + l *= 10; + l += (b - '0'); + b = readByte(); + } + pushBack(b); + return l; + } + + /** Parses an address in the form 0x12345678 in US-ASCII encoding on + the input stream */ + public long parseAddress() throws IOException { + skipWhitespace(); + byte b; + if ((b = readByte()) != '0') { + error(); + } + b = readByte(); + if (b != 'x') { + error(); + } + long val = 0; + while (isHexDigit((char) (b = readByte()))) { + val *= 16; + val += Character.digit((char) b, 16); + } + pushBack(b); + return val; + } + + public void skipByte() throws IOException { + readByte(); + } + + /** Reads binary data; one byte */ + public byte readByte() throws IOException { + if (pushedBack) { + pushedBack = false; + return backBuf; + } + return readByteInternal(); + } + + /** Reads a block of binary data in BLOCKING fashion */ + public void readBytes(byte[] buf, int off, int len) throws IOException { + int startIdx = off; + int numRead = 0; + if (pushedBack) { + buf[startIdx] = backBuf; + pushedBack = false; + ++startIdx; + ++numRead; + } + while (numRead < len) { + numRead += in.read(buf, startIdx + numRead, len - numRead); + } + // if (numRead != len) { + // throw new IOException("Only read " + numRead + " out of " + + // len + " bytes requested"); + // } + } + + /** Reads binary data; one 16-bit character in big-endian format */ + public char readChar() throws IOException { + int hi = ((int) readByte()) & 0xFF; + int lo = ((int) readByte()) & 0xFF; + return (char) ((hi << 8) | lo); + } + + /** Reads binary data; one 32-bit unsigned int in big-endian format. + Returned as a long. */ + public long readUnsignedInt() throws IOException { + long b1 = ((long) readByte()) & 0xFF; + long b2 = ((long) readByte()) & 0xFF; + long b3 = ((long) readByte()) & 0xFF; + long b4 = ((long) readByte()) & 0xFF; + + return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4); + } + + /** Reads binary data; a US-ASCII string of the specified length */ + public String readByteString(int len) throws IOException { + byte[] b = new byte[len]; + for (int i = 0; i < len; i++) { + b[i] = readByte(); + } + try { + return new String(b, "US-ASCII"); + } + catch (UnsupportedEncodingException e) { + throw new IOException(e.toString()); + } + } + + /** Reads binary data; a Unicode string of the specified length */ + public String readCharString(int len) throws IOException { + char[] c = new char[len]; + for (int i = 0; i < len; i++) { + c[i] = readChar(); + } + return new String(c); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void skipWhitespace() throws IOException { + byte b; + while (Character.isWhitespace((char) (b = readByte()))) { + } + pushBack(b); + } + + private boolean isHexDigit(char c) { + return (('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F')); + } + + private void pushBack(byte b) { + if (pushedBack) { + throw new InternalError("Only one character pushback supported"); + } + backBuf = b; + pushedBack = true; + } + + private byte readByteInternal() throws IOException { + int i = in.read(); + if (i == -1) { + throw new IOException("End-of-file reached while reading from server"); + } + return (byte) i; + } + + private void error() throws IOException { + throw new IOException("Error parsing output of debug server"); + } + + private BufferedInputStream in; + private boolean pushedBack; + private byte backBuf; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/JVMDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/JVMDebugger.java new file mode 100644 index 00000000000..6308f7dd8ef --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/JVMDebugger.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/** An extension of the Debugger interface which can be configured + with Java type sizes to allow the sizes of primitive Java types to + be read from the remote JVM. */ + +public interface JVMDebugger extends Debugger { + /** This intent is that this can be called late in the bootstrapping + sequence, after which the debugger can handle reading of Java + primitive types, and thereby implement the Java functionality in + class Address. FIXME: consider adding oop size here as well and + removing it from the MachineDescription. */ + public void configureJavaPrimitiveTypeSizes(long jbooleanSize, + long jbyteSize, + long jcharSize, + long jdoubleSize, + long jfloatSize, + long jintSize, + long jlongSize, + long jshortSize); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/LongHashMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/LongHashMap.java new file mode 100644 index 00000000000..57317e3cec3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/LongHashMap.java @@ -0,0 +1,464 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +import java.util.*; + +/** + * This is a copy of java.util.HashMap which uses longs as keys + * instead of Objects. It turns out that using this in the PageCache + * implementation speeds up heap traversals by a factor of three. + * + * @author Josh Bloch + * @author Arthur van Hoff + */ + +public class LongHashMap +{ + static class Entry { + private int hash; + private long key; + private Object value; + private Entry next; + + Entry(int hash, long key, Object value, Entry next) { + this.hash = hash; + this.key = key; + this.value = value; + this.next = next; + } + + /** + * Returns the key corresponding to this entry. + * + * @return the key corresponding to this entry. + */ + long getKey() { return key; } + + /** + * Returns the value corresponding to this entry. If the mapping + * has been removed from the backing map (by the iterator's + * remove operation), the results of this call are undefined. + * + * @return the value corresponding to this entry. + */ + Object getValue() { return value; } + + /** + * Replaces the value corresponding to this entry with the specified + * value (optional operation). (Writes through to the map.) The + * behavior of this call is undefined if the mapping has already been + * removed from the map (by the iterator's remove operation). + * + * @param value new value to be stored in this entry. + * @return old value corresponding to the entry. + * + * @throws UnsupportedOperationException if the put operation + * is not supported by the backing map. + * @throws ClassCastException if the class of the specified value + * prevents it from being stored in the backing map. + * @throws IllegalArgumentException if some aspect of this value + * prevents it from being stored in the backing map. + * @throws NullPointerException the backing map does not permit + * null values, and the specified value is + * null. + */ + Object setValue(Object value) { + Object oldValue = this.value; + this.value = value; + return oldValue; + } + + /** + * Compares the specified object with this entry for equality. + * Returns true if the given object is also a map entry and + * the two entries represent the same mapping. More formally, two + * entries e1 and e2 represent the same mapping + * if
+         *     (e1.getKey()==null ?
+         *      e2.getKey()==null : e1.getKey().equals(e2.getKey()))  &&
+         *     (e1.getValue()==null ?
+         *      e2.getValue()==null : e1.getValue().equals(e2.getValue()))
+         * 
+ * This ensures that the equals method works properly across + * different implementations of the Map.Entry interface. + * + * @param o object to be compared for equality with this map entry. + * @return true if the specified object is equal to this map + * entry. + */ + public boolean equals(Object o) { + if (!(o instanceof Entry)) + return false; + Entry e = (Entry)o; + return (key == e.getKey()) && eq(value, e.getValue()); + } + + /** + * Returns the hash code value for this map entry. The hash code + * of a map entry e is defined to be:
+         *     (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
+         *     (e.getValue()==null ? 0 : e.getValue().hashCode())
+         * 
+ * This ensures that e1.equals(e2) implies that + * e1.hashCode()==e2.hashCode() for any two Entries + * e1 and e2, as required by the general + * contract of Object.hashCode. + * + * @return the hash code value for this map entry. + * @see Object#hashCode() + * @see Object#equals(Object) + * @see #equals(Object) + */ + public int hashCode() { + return hash ^ (value==null ? 0 : value.hashCode()); + } + } + + /** + * The hash table data. + */ + transient Entry table[]; + + /** + * The total number of mappings in the hash table. + */ + transient int size; + + /** + * The table is rehashed when its size exceeds this threshold. (The + * value of this field is (int)(capacity * loadFactor).) + * + * @serial + */ + int threshold; + + /** + * The load factor for the hash table. + * + * @serial + */ + final float loadFactor; + + /** + * The number of times this HashMap has been structurally modified + * Structural modifications are those that change the number of mappings in + * the HashMap or otherwise modify its internal structure (e.g., + * rehash). This field is used to make iterators on Collection-views of + * the HashMap fail-fast. (See ConcurrentModificationException). + */ + transient int modCount = 0; + + /** + * Constructs a new, empty map with the specified initial + * capacity and the specified load factor. + * + * @param initialCapacity the initial capacity of the HashMap. + * @param loadFactor the load factor of the HashMap + * @throws IllegalArgumentException if the initial capacity is less + * than zero, or if the load factor is nonpositive. + */ + public LongHashMap(int initialCapacity, float loadFactor) { + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal Initial Capacity: "+ + initialCapacity); + if (loadFactor <= 0 || Float.isNaN(loadFactor)) + throw new IllegalArgumentException("Illegal Load factor: "+ + loadFactor); + if (initialCapacity==0) + initialCapacity = 1; + this.loadFactor = loadFactor; + table = new Entry[initialCapacity]; + threshold = (int)(initialCapacity * loadFactor); + } + + /** + * Constructs a new, empty map with the specified initial capacity + * and default load factor, which is 0.75. + * + * @param initialCapacity the initial capacity of the HashMap. + * @throws IllegalArgumentException if the initial capacity is less + * than zero. + */ + public LongHashMap(int initialCapacity) { + this(initialCapacity, 0.75f); + } + + /** + * Constructs a new, empty map with a default capacity and load + * factor, which is 0.75. + */ + public LongHashMap() { + this(11, 0.75f); + } + + /** + * Returns the number of key-value mappings in this map. + * + * @return the number of key-value mappings in this map. + */ + public int size() { + return size; + } + + /** + * Returns true if this map contains no key-value mappings. + * + * @return true if this map contains no key-value mappings. + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Returns the value to which this map maps the specified key. Returns + * null if the map contains no mapping for this key. A return + * value of null does not necessarily indicate that the + * map contains no mapping for the key; it's also possible that the map + * explicitly maps the key to null. The containsKey + * operation may be used to distinguish these two cases. + * + * @return the value to which this map maps the specified key. + * @param key key whose associated value is to be returned. + */ + public Object get(long key) { + Entry e = getEntry(key); + return (e == null ? null : e.value); + } + + /** + * Returns true if this map contains a mapping for the specified + * key. + * + * @return true if this map contains a mapping for the specified + * key. + * @param key key whose presence in this Map is to be tested. + */ + public boolean containsKey(long key) { + return getEntry(key) != null; + } + + /** + * Returns the entry associated with the specified key in the + * HashMap. Returns null if the HashMap contains no mapping + * for this key. + */ + Entry getEntry(long key) { + Entry tab[] = table; + int hash = (int) key; + int index = (hash & 0x7FFFFFFF) % tab.length; + + for (Entry e = tab[index]; e != null; e = e.next) + if (e.hash == hash && e.key ==key) + return e; + + return null; + } + + /** + * Returns true if this map maps one or more keys to the + * specified value. + * + * @param value value whose presence in this map is to be tested. + * @return true if this map maps one or more keys to the + * specified value. + */ + public boolean containsValue(Object value) { + Entry tab[] = table; + + if (value==null) { + for (int i = tab.length ; i-- > 0 ;) + for (Entry e = tab[i] ; e != null ; e = e.next) + if (e.value==null) + return true; + } else { + for (int i = tab.length ; i-- > 0 ;) + for (Entry e = tab[i] ; e != null ; e = e.next) + if (value.equals(e.value)) + return true; + } + + return false; + } + + /** + * Associates the specified value with the specified key in this map. + * If the map previously contained a mapping for this key, the old + * value is replaced. + * + * @param key key with which the specified value is to be associated. + * @param value value to be associated with the specified key. + * @return previous value associated with specified key, or null + * if there was no mapping for key. A null return can + * also indicate that the HashMap previously associated + * null with the specified key. + */ + public Object put(long key, Object value) { + Entry tab[] = table; + int hash = (int) key; + int index = (hash & 0x7FFFFFFF) % tab.length; + + // Look for entry in hash table + for (Entry e = tab[index] ; e != null ; e = e.next) { + if (e.hash == hash && e.key == key) { + Object oldValue = e.value; + e.value = value; + return oldValue; + } + } + + // It's not there; grow the hash table if necessary... + modCount++; + if (size >= threshold) { + rehash(); + tab = table; + index = (hash & 0x7FFFFFFF) % tab.length; + } + + // ...and add the entry + size++; + tab[index] = newEntry(hash, key, value, tab[index]); + return null; + } + + /** + * Removes the mapping for this key from this map if present. + * + * @param key key whose mapping is to be removed from the map. + * @return previous value associated with specified key, or null + * if there was no mapping for key. A null return can + * also indicate that the map previously associated null + * with the specified key. + */ + public Object remove(long key) { + Entry e = removeEntryForKey(key); + return (e == null ? null : e.value); + } + + /** + * Removes and returns the entry associated with the specified key + * in the HashMap. Returns null if the HashMap contains no mapping + * for this key. + */ + Entry removeEntryForKey(long key) { + Entry tab[] = table; + int hash = (int) key; + int index = (hash & 0x7FFFFFFF) % tab.length; + + for (Entry e = tab[index], prev = null; e != null; + prev = e, e = e.next) { + if (e.hash == hash && e.key == key) { + modCount++; + if (prev != null) + prev.next = e.next; + else + tab[index] = e.next; + + size--; + return e; + } + } + + return null; + } + + /** + * Removes the specified entry from this HashMap (and increments modCount). + * + * @throws ConcurrentModificationException if the entry is not in the Map + */ + void removeEntry(Entry doomed) { + Entry[] tab = table; + int index = (doomed.hash & 0x7FFFFFFF) % tab.length; + + for (Entry e = tab[index], prev = null; e != null; + prev = e, e = e.next) { + if (e == doomed) { + modCount++; + if (prev == null) + tab[index] = e.next; + else + prev.next = e.next; + size--; + return; + } + } + throw new ConcurrentModificationException(); + } + + /** + * Removes all mappings from this map. + */ + public void clear() { + Entry tab[] = table; + modCount++; + for (int index = tab.length; --index >= 0; ) + tab[index] = null; + size = 0; + } + + /** + * Rehashes the contents of this map into a new HashMap instance + * with a larger capacity. This method is called automatically when the + * number of keys in this map exceeds its capacity and load factor. + */ + void rehash() { + Entry oldTable[] = table; + int oldCapacity = oldTable.length; + int newCapacity = oldCapacity * 2 + 1; + Entry newTable[] = new Entry[newCapacity]; + + modCount++; + threshold = (int)(newCapacity * loadFactor); + table = newTable; + + for (int i = oldCapacity ; i-- > 0 ;) { + for (Entry old = oldTable[i] ; old != null ; ) { + Entry e = old; + old = old.next; + + int index = (e.hash & 0x7FFFFFFF) % newCapacity; + e.next = newTable[index]; + newTable[index] = e; + } + } + } + + static boolean eq(Object o1, Object o2) { + return (o1==null ? o2==null : o1.equals(o2)); + } + + Entry newEntry(int hash, long key, Object value, Entry next) { + return new Entry(hash, key, value, next); + } + + int capacity() { + return table.length; + } + + float loadFactor() { + return loadFactor; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescription.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescription.java new file mode 100644 index 00000000000..bbe2f69f883 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescription.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +import java.io.Serializable; + +/** Encapsulates machine-specific information that currently must be + exported up to the Java level. Implementations must be + serializable. */ + +public interface MachineDescription extends Serializable { + /** Returns the size of an address in bytes. Currently needed to be + able to traverse arrays of pointers or oops. */ + public long getAddressSize(); + + /** Returns the size of an address in bytes. Currently needed to be + able to traverse arrays of pointers or oops. (FIXME: since we're + already reading the Java primitive types' sizes from the remote + VM, it would be nice to remove this routine, using a similar + mechanism to how the TypeDataBase deals with primitive types.) */ + public long getOopSize(); + + /** Returns the maximum value of the C integer type with the given + size in bytes and signedness. Throws IllegalArgumentException if + the size in bytes is not legal for a C type (or can not be + handled by this system). Note that the current implementation + does not currently handle unsigned 8-byte longs properly. */ + public long cIntegerTypeMaxValue(long sizeInBytes, boolean isUnsigned); + + /** Returns the minimum value of the C integer type with the given + size in bytes and signedness. Throws IllegalArgumentException if + the size in bytes is not legal for a C type (or can not be + handled by this system). */ + public long cIntegerTypeMinValue(long sizeInBytes, boolean isUnsigned); + + /** Indicates whether the CPU is big- or little-endian. This + information is typically only needed by the Debugger + implementation. */ + public boolean isBigEndian(); + + /** Indicates whether the underlying machine supports the LP64 data + model (currently only SPARC/64). */ + public boolean isLP64(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionAMD64.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionAMD64.java new file mode 100644 index 00000000000..5e5d34217ef --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionAMD64.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class MachineDescriptionAMD64 extends MachineDescriptionTwosComplement implements MachineDescription { + public long getAddressSize() { + return 8; + } + + public long getOopSize() { + return 8; + } + + public boolean isLP64() { + return true; + } + + public boolean isBigEndian() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIA64.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIA64.java new file mode 100644 index 00000000000..6cc6accd531 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIA64.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class MachineDescriptionIA64 extends MachineDescriptionTwosComplement implements MachineDescription { + public long getAddressSize() { + return 8; + } + + public long getOopSize() { + return 8; + } + + public boolean isLP64() { + return true; + } + + public boolean isBigEndian() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIntelX86.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIntelX86.java new file mode 100644 index 00000000000..fe486a6cf3e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIntelX86.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class MachineDescriptionIntelX86 extends MachineDescriptionTwosComplement implements MachineDescription { + public long getAddressSize() { + return 4; + } + + public long getOopSize() { + return 4; + } + + public boolean isBigEndian() { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionSPARC32Bit.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionSPARC32Bit.java new file mode 100644 index 00000000000..7c33d40e0ee --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionSPARC32Bit.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class MachineDescriptionSPARC32Bit extends MachineDescriptionTwosComplement implements MachineDescription { + public long getAddressSize() { + return 4; + } + + public long getOopSize() { + return 4; + } + + public boolean isBigEndian() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionSPARC64Bit.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionSPARC64Bit.java new file mode 100644 index 00000000000..22a88d87599 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionSPARC64Bit.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class MachineDescriptionSPARC64Bit extends MachineDescriptionTwosComplement implements MachineDescription { + public long getAddressSize() { + return 8; + } + + public long getOopSize() { + return 8; + } + + public boolean isBigEndian() { + return true; + } + + public boolean isLP64() { + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionTwosComplement.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionTwosComplement.java new file mode 100644 index 00000000000..705657d9d65 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionTwosComplement.java @@ -0,0 +1,96 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/** Base class for all twos-complement machine descriptions, which + handles the cIntegerType{Min,Max}Value methods. */ + +public abstract class MachineDescriptionTwosComplement { + + /** Handles 1, 2, 4, and 8-byte signed integers */ + private static final long[] signedMinValues = { + Byte.MIN_VALUE, + Short.MIN_VALUE, + Integer.MIN_VALUE, + Long.MIN_VALUE + }; + + /** Handles 1, 2, 4, and 8-byte signed integers */ + private static final long[] signedMaxValues = { + Byte.MAX_VALUE, + Short.MAX_VALUE, + Integer.MAX_VALUE, + Long.MAX_VALUE + }; + + /** Handles 1, 2, and 4-byte unsigned integers properly, with a bug + in the 8-byte unsigned integer's constant */ + private static final long[] unsignedMaxValues = { + 255L, + 65535L, + 4294967295L, + -1L + }; + + public long cIntegerTypeMaxValue(long sizeInBytes, boolean isUnsigned) { + if (isUnsigned) { + // Would be nice to signal to the caller that 8-byte unsigned + // integers are not supported properly, but it looks like doing + // so at this level will cause problems above + + return tableLookup(sizeInBytes, unsignedMaxValues); + } else { + return tableLookup(sizeInBytes, signedMaxValues); + } + }; + + public long cIntegerTypeMinValue(long sizeInBytes, boolean isUnsigned) { + if (isUnsigned) { + return 0; + } + + return tableLookup(sizeInBytes, signedMinValues); + } + + // Nearly all of the supported machines are not LP64 */ + public boolean isLP64() { + return false; + } + + private long tableLookup(long sizeInBytes, long[] table) { + switch ((int) sizeInBytes) { + case 1: + return table[0]; + case 2: + return table[1]; + case 4: + return table[2]; + case 8: + return table[3]; + default: + throw new IllegalArgumentException("C integer type of " + sizeInBytes + " not supported"); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MappedByteBufferDataSource.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MappedByteBufferDataSource.java new file mode 100644 index 00000000000..503ad8adaf3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MappedByteBufferDataSource.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +import java.io.*; +import java.nio.*; + +/** Implementation of DataSource using MappedByteBuffer. This works + around a performance problem in JDK 1.4 where ByteBuffer's + operations always become virtual calls rather than being inlined. + Need to fix this soon. */ + +public class MappedByteBufferDataSource implements DataSource { + private MappedByteBuffer buf; + + public MappedByteBufferDataSource(MappedByteBuffer buf) { + this.buf = buf; + } + + public byte readByte() throws IOException { return buf.get(); } + public short readShort() throws IOException { return buf.getShort(); } + public int readInt() throws IOException { return buf.getInt(); } + public long readLong() throws IOException { return buf.getLong(); } + public int read(byte[] b) throws IOException { buf.get(b); return b.length; } + public void seek(long pos) throws IOException { + try { + buf.position((int) pos); + } catch (IllegalArgumentException e) { + System.err.println("Error seeking to file position 0x" + Long.toHexString(pos)); + throw(e); + } + } + public long getFilePointer() throws IOException { return buf.position(); } + public void close() throws IOException { buf = null; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/NoSuchSymbolException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/NoSuchSymbolException.java new file mode 100644 index 00000000000..2ad7716a640 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/NoSuchSymbolException.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class NoSuchSymbolException extends RuntimeException { + private String symbol; + + public NoSuchSymbolException(String symbol) { + super(); + this.symbol = symbol; + } + + public NoSuchSymbolException(String symbol, Throwable cause) { + super(cause); + this.symbol = symbol; + } + + public NoSuchSymbolException(String symbol, String detail) { + super(detail); + this.symbol = symbol; + } + + public NoSuchSymbolException(String symbol, String detail, Throwable cause) { + super(detail, cause); + this.symbol = symbol; + } + + public String getSymbol() { + return symbol; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/NotInHeapException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/NotInHeapException.java new file mode 100644 index 00000000000..311069cde95 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/NotInHeapException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class NotInHeapException extends AddressException { + public NotInHeapException(long addr) { + super(addr); + } + + public NotInHeapException(String detail, long addr) { + super(detail, addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/OopHandle.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/OopHandle.java new file mode 100644 index 00000000000..cdfa5185e38 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/OopHandle.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/**

This is a tag interface (similar to Cloneable) which indicates + that the contained address is "special" and is updated under the + hood by the VM. The purpose is to support implementation of + reflection on the current VM with these interfaces; if the Java + code implementing parts of the VM requires proxies for objects in + the heap, it must be the case that those proxies are updated if GC + occurs. This is the level at which this updating is handled. The + VM (and specifically the GC code) must have intimate knowledge of + the VM-specific implementation of this interface.

+ +

Note that in the case of debugging a remote VM, it is not + workable to handle the automatic updating of these handles. + If the debugger allows the VM to resume running, it will have to + look up once again any object references via the path they were + found (i.e., the activation on the stack as the root, etc.)

+*/ + +public interface OopHandle extends Address { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/Page.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/Page.java new file mode 100644 index 00000000000..07e6306dc52 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/Page.java @@ -0,0 +1,201 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/** A class representing an arbitrary-sized page which can be linked + into a list. Used by the PageCache. */ + +public class Page { + private long baseAddress; + private byte[] data; + private Page prev; + private Page next; + private long unmappedPageLength; + + /** The length of the data[] array implicitly defines the size of the + page. */ + public Page(long baseAddress, byte[] data) { + this.baseAddress = baseAddress; + this.data = data; + } + + /** This constructor creates an "unmapped" page of the specified + length. Fetches from this page will cause -1 to be inserted into + the destination buffer. */ + public Page(long baseAddress, long unmappedPageLength) { + this.baseAddress = baseAddress; + this.unmappedPageLength = unmappedPageLength; + } + + public long getBaseAddress() { + return baseAddress; + } + + public long getSize() { + if (data != null) { + return data.length; + } else { + return unmappedPageLength; + } + } + + /** Indicates whether this page is mapped in the remote process's + address space */ + public boolean isMapped() { + return (data != null); + } + + public Page getPrev() { + return prev; + } + + public void setPrev(Page prev) { + this.prev = prev; + } + + public Page getNext() { + return next; + } + + public void setNext(Page next) { + this.next = next; + } + + /** Throws IndexOutOfBoundsException if the number of bytes + requested is greater than the page size, or if the start address + doesn't fall within the page. There are no guarantees on whether + any data was actually fetched if an IndexOutOfBoundsException is + thrown. If this page is unmapped, -1 is returned for all + addresses on this page. */ + public void getData(long startAddress, long numBytes, + int[] destBuf, long destBufOffset) + throws IndexOutOfBoundsException { + int startOffset = (int) (startAddress - baseAddress); + if ((data == null) && + ((startOffset < 0) || ((startOffset + numBytes) > (baseAddress + unmappedPageLength)))) { + throw new IndexOutOfBoundsException("startAddress = " + startAddress + + ", baseAddress = " + baseAddress + + ", unmappedPageLength = " + unmappedPageLength); + } + for (int i = 0; i < (int) numBytes; ++i) { + if (data != null) { + destBuf[i + (int) destBufOffset] = ((int) (data[i + startOffset]) & 0x000000FF); + } else { + destBuf[i + (int) destBufOffset] = -1; + } + } + } + + /** Throws IndexOutOfBoundsException if the number of bytes + requested is greater than the page size, or if the start address + doesn't fall within the page. There are no guarantees on whether + any data was actually fetched if an IndexOutOfBoundsException is + thrown. If this page is unmapped, throws a RuntimeException; + this should be watched for at higher levels. */ + public void getDataAsBytes(long startAddress, long numBytes, + byte[] destBuf, long destBufOffset) + throws IndexOutOfBoundsException { + long startOffset = startAddress - baseAddress; + if (data == null) { + throw new RuntimeException("Bug in PageCache; should not fetch from unmapped pages using getDataAsBytes"); + } + System.arraycopy(data, (int) startOffset, destBuf, (int) destBufOffset, (int) numBytes); + } + + public boolean getBoolean(long address) { + return (getByte(address) != 0); + } + + public byte getByte(long address) { + return data[(int) address - (int) baseAddress]; + } + + public short getShort(long address, boolean bigEndian) { + int start = (int) address - (int) baseAddress; + if (bigEndian) { + return (short) + (((data[start + 1] & 0xFF)) | + ((data[start] & 0xFF) << 8)); + } else { + return (short) + (((data[start + 1] & 0xFF) << 8) | + ((data[start] & 0xFF))); + } + } + + public char getChar(long address, boolean bigEndian) { + return (char) getShort(address, bigEndian); + } + + public int getInt(long address, boolean bigEndian) { + int start = (int) address - (int) baseAddress; + if (bigEndian) { + return + ((data[start + 3] & 0xFF)) | + ((data[start + 2] & 0xFF) << 8) | + ((data[start + 1] & 0xFF) << 16) | + ((data[start] & 0xFF) << 24); + } else { + return + ((data[start + 3] & 0xFF) << 24) | + ((data[start + 2] & 0xFF) << 16) | + ((data[start + 1] & 0xFF) << 8) | + ((data[start] & 0xFF)); + } + } + + public long getLong(long address, boolean bigEndian) { + int start = (int) address - (int) baseAddress; + if (bigEndian) { + return + ((data[start + 7] & 0xFFL)) | + ((data[start + 6] & 0xFFL) << 8) | + ((data[start + 5] & 0xFFL) << 16) | + ((data[start + 4] & 0xFFL) << 24) | + ((data[start + 3] & 0xFFL) << 32) | + ((data[start + 2] & 0xFFL) << 40) | + ((data[start + 1] & 0xFFL) << 48) | + ((data[start] & 0xFFL) << 56); + } else { + return + ((data[start + 7] & 0xFFL) << 56) | + ((data[start + 6] & 0xFFL) << 48) | + ((data[start + 5] & 0xFFL) << 40) | + ((data[start + 4] & 0xFFL) << 32) | + ((data[start + 3] & 0xFFL) << 24) | + ((data[start + 2] & 0xFFL) << 16) | + ((data[start + 1] & 0xFFL) << 8) | + ((data[start] & 0xFFL)); + } + } + + public float getFloat(long address, boolean bigEndian) { + return Float.intBitsToFloat(getInt(address, bigEndian)); + } + + public double getDouble(long address, boolean bigEndian) { + return Double.longBitsToDouble(getLong(address, bigEndian)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/PageCache.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/PageCache.java new file mode 100644 index 00000000000..3dedb9a6c8d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/PageCache.java @@ -0,0 +1,289 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/** This class implements an LRU page-level cache of configurable page + size and number of pages. It is configured with a PageFetcher + which enables it to transparently satisfy requests which span + multiple pages when one or more of those pages is not in the + cache. It is generic enough to be sharable among debugger + implementations. */ + +import sun.jvm.hotspot.utilities.*; + +public class PageCache { + /** The pageSize must be a power of two and implicitly specifies the + alignment of pages. numPages specifies how many pages should be + cached. */ + public PageCache(long pageSize, + long maxNumPages, + PageFetcher fetcher) { + checkPageInfo(pageSize, maxNumPages); + this.pageSize = pageSize; + this.maxNumPages = maxNumPages; + this.fetcher = fetcher; + addressToPageMap = new LongHashMap(); + enabled = true; + } + + /** This handles fetches which span multiple pages by virtue of the + presence of the PageFetcher. Throws UnmappedAddressException if + a page on which data was requested was unmapped. This can not + really handle numBytes > 32 bits. */ + public synchronized byte[] getData(long startAddress, long numBytes) + throws UnmappedAddressException { + byte[] data = new byte[(int) numBytes]; + long numRead = 0; + + while (numBytes > 0) { + long pageBaseAddress = startAddress & pageMask; + // Look up this page + Page page = checkPage(getPage(pageBaseAddress), startAddress); + // Figure out how many bytes to read from this page + long pageOffset = startAddress - pageBaseAddress; + long numBytesFromPage = Math.min(pageSize - pageOffset, numBytes); + // Read them starting at the appropriate offset in the + // destination buffer + page.getDataAsBytes(startAddress, numBytesFromPage, data, numRead); + // Increment offsets + numRead += numBytesFromPage; + numBytes -= numBytesFromPage; + startAddress += numBytesFromPage; + } + + return data; + } + + public synchronized boolean getBoolean(long address) { + return (getByte(address) != 0); + } + + public synchronized byte getByte(long address) { + return checkPage(getPage(address & pageMask), address).getByte(address); + } + + public synchronized short getShort(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getShort(address, bigEndian); + } + + public synchronized char getChar(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getChar(address, bigEndian); + } + + public synchronized int getInt(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getInt(address, bigEndian); + } + + public synchronized long getLong(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getLong(address, bigEndian); + } + + public synchronized float getFloat(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getFloat(address, bigEndian); + } + + public synchronized double getDouble(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getDouble(address, bigEndian); + } + + /** A mechanism for clearing cached data covering the given region */ + public synchronized void clear(long startAddress, long numBytes) { + long pageBaseAddress = startAddress & pageMask; + long endAddress = startAddress + numBytes; + while (pageBaseAddress < endAddress) { + flushPage(pageBaseAddress); + pageBaseAddress += pageSize; + } + } + + /** A mechanism for clearing out the cache is necessary to handle + detaching and reattaching */ + public synchronized void clear() { + // Should probably break next/prev links in list as well + addressToPageMap.clear(); + lruList = null; + numPages = 0; + } + + /** Disables the page cache; no further pages will be added to the + cache and all existing pages will be flushed. Call this when the + target process has been resumed. */ + public synchronized void disable() { + enabled = false; + clear(); + } + + /** Enables the page cache; fetched pages will be added to the + cache. Call this when the target process has been suspended. */ + public synchronized void enable() { + enabled = true; + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + // This is implemented with two data structures: a hash table for + // fast lookup by a page's base address and a circular doubly-linked + // list for implementing LRU behavior. + + private boolean enabled; + private long pageSize; + private long maxNumPages; + private long pageMask; + private long numPages; + private PageFetcher fetcher; + private LongHashMap addressToPageMap; // Map + private Page lruList; // Most recently fetched page, or null + + /** Page fetcher plus LRU functionality */ + private Page getPage(long pageBaseAddress) { + // Check head of LRU list first to avoid hash table lookup and + // extra list work if possible + if (lruList != null) { + if (lruList.getBaseAddress() == pageBaseAddress) { + // Hit. Return it. + return lruList; + } + } + // Long key = new Long(pageBaseAddress); + long key = pageBaseAddress; + Page page = (Page) addressToPageMap.get(key); + if (page == null) { + // System.err.println("** Cache miss at address 0x" + Long.toHexString(pageBaseAddress) + " **"); + // Fetch new page + page = fetcher.fetchPage(pageBaseAddress, pageSize); + if (enabled) { + // Add to cache, evicting last element if necessary + addressToPageMap.put(key, page); + if (Assert.ASSERTS_ENABLED) { + Assert.that(page == (Page) addressToPageMap.get(pageBaseAddress), + "must have found page in cache!"); + } + addPageToList(page); + // See whether eviction of oldest is necessary + if (numPages == maxNumPages) { + Page evictedPage = lruList.getPrev(); + // System.err.println("-> Evicting page at 0x" + Long.toHexString(evictedPage.getBaseAddress()) + + // "; " + countPages() + " pages left (expect " + numPages + ")"); + removePageFromList(evictedPage); + addressToPageMap.remove(evictedPage.getBaseAddress()); + } else { + ++numPages; + } + } + } else { + // Page already in cache, move to front of list + removePageFromList(page); + addPageToList(page); + } + return page; + } + + private Page checkPage(Page page, long startAddress) { + if (!page.isMapped()) { + throw new UnmappedAddressException(startAddress); + } + return page; + } + + private int countPages() { + Page page = lruList; + int num = 0; + if (page == null) { + return num; + } + do { + ++num; + page = page.getNext(); + } while (page != lruList); + return num; + } + + private void flushPage(long pageBaseAddress) { + long key = pageBaseAddress; + Page page = (Page) addressToPageMap.remove(key); + if (page != null) { + removePageFromList(page); + } + } + + // Adds given page to head of list + private void addPageToList(Page page) { + if (lruList == null) { + lruList = page; + page.setNext(page); + page.setPrev(page); + } else { + // Add to front of list + page.setNext(lruList); + page.setPrev(lruList.getPrev()); + lruList.getPrev().setNext(page); + lruList.setPrev(page); + lruList = page; + } + } + + // Removes given page from list + private void removePageFromList(Page page) { + if (page.getNext() == page) { + lruList = null; + } else { + if (lruList == page) { + lruList = page.getNext(); + } + page.getPrev().setNext(page.getNext()); + page.getNext().setPrev(page.getPrev()); + } + page.setPrev(null); + page.setNext(null); + } + + /** Ensure that page size fits within 32 bits and is a power of two, and that maxNumPages > 0 */ + private void checkPageInfo(long pageSize, long maxNumPages) { + if ((pageSize <= 0) || maxNumPages <= 0) { + throw new IllegalArgumentException("pageSize and maxNumPages must both be greater than zero"); + } + long tmpPageSize = pageSize >>> 32; + if (tmpPageSize != 0) { + throw new IllegalArgumentException("pageSize " + pageSize + " too big (must fit within 32 bits)"); + } + int numNonZeroBits = 0; + for (int i = 0; i < 32; ++i) { + if ((pageSize & 1L) != 0) { + ++numNonZeroBits; + if ((numNonZeroBits > 1) || (i == 0)) { + throw new IllegalArgumentException("pageSize " + pageSize + " must be a power of two"); + } + } + pageSize >>>= 1; + if (numNonZeroBits == 0) { + pageMask = (pageMask << 1) | 1L; + } + } + pageMask = ~pageMask; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/PageFetcher.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/PageFetcher.java new file mode 100644 index 00000000000..9ca1e53ae77 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/PageFetcher.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/** This interface specifies how a page is fetched by the PageCache. */ + +public interface PageFetcher { + public Page fetchPage(long pageBaseAddress, long numBytes); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ProcessInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ProcessInfo.java new file mode 100644 index 00000000000..9bf966f8aac --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ProcessInfo.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/** Simple wrapper class for name and process ID information. + Hopefully this will be generic enough to be portable. */ + +public class ProcessInfo { + public ProcessInfo(String name, int pid) { + this.name = name; + this.pid = pid; + } + + public String getName() { + return name; + } + + public int getPid() { + return pid; + } + + private String name; + private int pid; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/RandomAccessFileDataSource.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/RandomAccessFileDataSource.java new file mode 100644 index 00000000000..b1af93997f7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/RandomAccessFileDataSource.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +import java.io.*; + +/* This class is used by the Windows COFF and Posix ELF implementations. */ +public class RandomAccessFileDataSource implements DataSource { + public RandomAccessFileDataSource(RandomAccessFile file) { + this.file = file; + } + + public byte readByte() throws IOException { return file.readByte(); } + public short readShort() throws IOException { return file.readShort(); } + public int readInt() throws IOException { return file.readInt(); } + public long readLong() throws IOException { return file.readLong(); } + public int read(byte[] b) throws IOException { return file.read(b); } + public void seek(long pos) throws IOException { file.seek(pos); } + public long getFilePointer() throws IOException { return file.getFilePointer(); } + public void close() throws IOException { file.close(); } + + private RandomAccessFile file; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ReadResult.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ReadResult.java new file mode 100644 index 00000000000..20105878006 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ReadResult.java @@ -0,0 +1,54 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +import java.io.Serializable; + +/** The only reason for this is to not throw UnmappedAddressException + across readBytesFromProcess() calls; we can't decompose the + RemoteException precisely enough */ + +public class ReadResult implements Serializable { + private byte[] data; // NULL if read failed + private long failureAddress; + + /** Successful result */ + public ReadResult(byte[] data) { + this.data = data; + } + + /** Unsuccessful result */ + public ReadResult(long failureAddress) { + this.failureAddress = failureAddress; + } + + public byte[] getData() { + return data; + } + + public long getFailureAddress() { + return failureAddress; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/SymbolLookup.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/SymbolLookup.java new file mode 100644 index 00000000000..81fa4531b98 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/SymbolLookup.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public interface SymbolLookup { + /** Looks up the given symbol in the context of the given object. + +

+ + FIXME: we may want to hide the objectName so the user does not + have to specify it, but it isn't clear whether this will work + transparently with dbx. + +

+ + FIXME: what happens if the address is not found? Throw + exception? Currently returns null. */ + public Address lookup(String objectName, String symbol); + + /** Looks up the given symbol in the context of the given object, + assuming that symbol refers to a Java object. + + FIXME: still not sure whether this will be necessary. Seems that + lookup of static fields with type "oop" already works, since the + lookup routine returns the address of the oop (i.e., an + oopDesc**). + +

+ + FIXME: we may want to hide the objectName so the user does not + have to specify it, but it isn't clear whether this will work + transparently with dbx. + +

+ + FIXME: what happens if the address is not found? Throw + exception? Currently returns null. */ + public OopHandle lookupOop(String objectName, String symbol); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ThreadAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ThreadAccess.java new file mode 100644 index 00000000000..d01e31600b9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ThreadAccess.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/**

This interface abstracts over access to operating system-level + threads in the underlying process. It is designed to be minimal + and generic to allow cross-platform compatibility.

+ +

The basic operation this interface supports is creating a + sun.jvm.hotspot.debugger.ThreadProxy "token" for an existing + thread. As an example, the HotSpot VM contains a list of Java + threads, encapsulated in VM-specific JavaThread objects. Each of + these contains a platform-dependent field with the OS-level thread + identifier; on Solaris, this field's type is "thread_t", while on + Windows, it is HANDLE. It is necessary to be able to map from + these fields to a ThreadProxy object, in particular to be able to + get the thread's context. However, since the types of these fields + vary greatly from OS to OS (some use integers as thread IDs, some + use pointers as thread handles) it is not possible to define one + particular type (Address, long) in this interface as the lookup + "key" for a Thread.

+ +

For this reason this mapping mechanism takes the Address of + the memory location containing the thread identifier. On Solaris, + this is the address of a location containing a thread_t; on + Windows, this is the address of a location containing a HANDLE for + a thread. On Linux, this is the address of a location containing a + pthread_t.

+ +

The {@link sun.jvm.hotspot.debugger.cdbg.CDebugger} interface + provides access to the entire thread list of the target process, + but this is optional functionality not required to get the SA to + work.

*/ + +public interface ThreadAccess { + /** Gets an abstract ThreadProxy object for the thread identified by + the contents of the memory location pointed to by addr. The + contents at location addr are inherently platform-dependent; see + the documentation for this class for more information. FIXME: + what exception, if any, should this throw? */ + public ThreadProxy getThreadForIdentifierAddress(Address addr); + + /** Gets an abstract ThreadProxy object for the thread identified by + id or handle that is platform dependent */ + public ThreadProxy getThreadForThreadId(long id); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ThreadContext.java new file mode 100644 index 00000000000..862e45cd0e7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ThreadContext.java @@ -0,0 +1,57 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/** This is a placeholder interface for a thread's context, containing + only integer registers (no floating-point ones). What it contains + is platform-dependent. Not all registers are guaranteed to be + present in the context or read from the target process in all + situations. However, the routines in it are exposed to allow + platform-independent iteration. */ + +public interface ThreadContext { + /** Number of integer registers in the context */ + public int getNumRegisters(); + + /** Get the name of the specified register (0..getNumRegisters() - + 1) */ + public String getRegisterName(int i); + + /** Get the value of the specified register (0..getNumRegisters() - + 1) */ + public long getRegister(int index); + + /** Set the value of the specified register (0..getNumRegisters() - + 1) */ + public void setRegister(int index, long value); + + /** Get the value of the specified register (0..getNumRegisters() - + 1) as an Address */ + public Address getRegisterAsAddress(int index); + + /** Set the value of the specified register (0..getNumRegisters() - + 1) as an Address */ + public void setRegisterAsAddress(int index, Address value); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ThreadProxy.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ThreadProxy.java new file mode 100644 index 00000000000..6e5114ce963 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ThreadProxy.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/**

This interface abstracts raw access to operating system-level + threads. In a debugging environment these methods map to, for + example, thread_db calls on Solaris (see /usr/include/thread_db.h) + or the Win32 debugging API calls. In a runtime environment these + might map directly to pthread calls.

+ +

Implementors of this interface must provide equals() and + hashCode() methods which work properly regardless of how the + ThreadProxy is obtained, in particular either through {@link + sun.jvm.hotspot.debugger.ThreadAccess} or the thread list provided + by {@link sun.jvm.hotspot.debugger.cdbg.CDebugger}. This allows + matching up of the OS's notion of the thread list of the target + process with any user-level lists that may be present (i.e., the + JavaThread list in the HotSpot VM).

+ +

Implementors of this interface should also provide a + toString() which converts the ThreadProxy to a value easily + recognizable in the platform's debugger. (For example, on Solaris, + "t@<id>".)

+ +

FIXME: had to be renamed from "Thread" to avoid numerous + clashes with java.lang.Thread -- would be nice to pick a more + consistent name with the rest of the system.

*/ + +public interface ThreadProxy { + /** Retrieves the context for the given thread. It is only valid to + call this method if the thread is suspended (i.e., the process + has not been resumed via ProcessControl); throws an + IllegalThreadStateException if it is not. */ + public ThreadContext getContext() throws IllegalThreadStateException; + + /** Indicates whether calls to setContext() are valid. */ + public boolean canSetContext() throws DebuggerException; + + /** Sets the context for the given thread. The passed ThreadContext + must be a modified version of one returned from a previous call + to getContext(). It is only valid to call this method if the + thread is suspended (i.e., the process has not been resumed via + ProcessControl); throws an IllegalThreadStateException if it is + not. Throws a DebuggerException if the target process can not be + modified, for example because it is a core file. */ + public void setContext(ThreadContext context) + throws IllegalThreadStateException, DebuggerException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/UnalignedAddressException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/UnalignedAddressException.java new file mode 100644 index 00000000000..8083bd91ff1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/UnalignedAddressException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class UnalignedAddressException extends AddressException { + public UnalignedAddressException(long addr) { + super(addr); + } + + public UnalignedAddressException(String detail, long addr) { + super(detail, addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/UnmappedAddressException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/UnmappedAddressException.java new file mode 100644 index 00000000000..344a87c736e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/UnmappedAddressException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +public class UnmappedAddressException extends AddressException { + public UnmappedAddressException(long addr) { + super(addr); + } + + public UnmappedAddressException(String detail, long addr) { + super(detail, addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/amd64/AMD64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/amd64/AMD64ThreadContext.java new file mode 100644 index 00000000000..9832a2dc81d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/amd64/AMD64ThreadContext.java @@ -0,0 +1,108 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.amd64; + +import sun.jvm.hotspot.debugger.*; + +/** Specifies the thread context on amd64 platforms; only a sub-portion + * of the context is guaranteed to be present on all operating + * systems. */ + +public abstract class AMD64ThreadContext implements ThreadContext { + // Taken from /usr/include/sys/regset.h on Solaris/AMD64. + + // NOTE: the indices for the various registers must be maintained as + // listed across various operating systems. However, only a small + // subset of the registers' values are guaranteed to be present (and + // must be present for the SA's stack walking to work) + + public static final int R15 = 0; + public static final int R14 = 1; + public static final int R13 = 2; + public static final int R12 = 3; + public static final int R11 = 4; + public static final int R10 = 5; + public static final int R9 = 6; + public static final int R8 = 7; + public static final int RDI = 8; + public static final int RSI = 9; + public static final int RBP = 10; + public static final int RBX = 11; + public static final int RDX = 12; + public static final int RCX = 13; + public static final int RAX = 14; + public static final int TRAPNO = 15; + public static final int ERR = 16; + public static final int RIP = 17; + public static final int CS = 18; + public static final int RFL = 19; + public static final int RSP = 20; + public static final int SS = 21; + public static final int FS = 22; + public static final int GS = 23; + public static final int ES = 24; + public static final int DS = 25; + public static final int FSBASE = 26; + public static final int GSBASE = 27; + + public static final int NPRGREG = 28; + + private static final String[] regNames = { + "r15", "r14", "r13", "r12", "r11", "r10", "r9", "r8", + "rdi", "rsi", "rbp", "rbx", "rdx", "rcx", "rax", "trapno", + "err", "rip", "cs", "rfl", "rsp", "ss", "fs", "gs", + "es", "ds", "fsbase", "gsbase" + }; + + private long[] data; + + public AMD64ThreadContext() { + data = new long[NPRGREG]; + } + + public int getNumRegisters() { + return NPRGREG; + } + + public String getRegisterName(int index) { + return regNames[index]; + } + + public void setRegister(int index, long value) { + data[index] = value; + } + + public long getRegister(int index) { + return data[index]; + } + + /** This can't be implemented in this class since we would have to + * tie the implementation to, for example, the debugging system */ + public abstract void setRegisterAsAddress(int index, Address value); + + /** This can't be implemented in this class since we would have to + * tie the implementation to, for example, the debugging system */ + public abstract Address getRegisterAsAddress(int index); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/AccessControl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/AccessControl.java new file mode 100644 index 00000000000..e80d69a6a74 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/AccessControl.java @@ -0,0 +1,32 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface AccessControl { + public static final int NO_PROTECTION = 0; + public static final int PRIVATE = 1; + public static final int PROTECTED = 2; + public static final int PUBLIC = 3; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ArrayType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ArrayType.java new file mode 100644 index 00000000000..481e5433bb4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ArrayType.java @@ -0,0 +1,30 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface ArrayType extends Type { + public Type getElementType(); + public int getLength(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/BaseClass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/BaseClass.java new file mode 100644 index 00000000000..a9c98f5627e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/BaseClass.java @@ -0,0 +1,34 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +/** Models a base class; effectively just provides the "virtual" + keyword as well as public/private derivation information. */ +public interface BaseClass { + /** See {@link sun.jvm.hotspot.debugger.cdbg.AccessControl} */ + public int getAccessControl(); + public boolean isVirtual(); + public Type getType(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/BitType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/BitType.java new file mode 100644 index 00000000000..e342ec35143 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/BitType.java @@ -0,0 +1,34 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface BitType extends IntType { + /** Size in bits of this type */ + public int getSizeInBits(); + + /** Offset from the least-significant bit (LSB) of the LSB of this + type */ + public int getOffset(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/BlockSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/BlockSym.java new file mode 100644 index 00000000000..a076cec88fb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/BlockSym.java @@ -0,0 +1,50 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/** A BlockSym models a lexical scope in a block-structured + language. It is (currently) the bottommost scope type. */ + +public interface BlockSym extends Sym { + /** Get the lexically enclosing block, or null if none */ + public BlockSym getParent(); + + /** Length in bytes of the machine code in this block */ + public long getLength(); + + /** Address of the first machine instruction in this block */ + public Address getAddress(); + + /** Name of this block, or null if none */ + public String getName(); + + /** Number of local variable symbols associated with this block */ + public int getNumLocals(); + + /** Return ith local (0..getNumLocals() - 1) */ + public LocalSym getLocal(int i); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CDebugInfoDataBase.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CDebugInfoDataBase.java new file mode 100644 index 00000000000..f37af244285 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CDebugInfoDataBase.java @@ -0,0 +1,64 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/** Database for C and C++ debug information. This is being kept as + minimal as possible for now. It is not complete; for example, it + will have to be extended to support scoped information (module + scope, namespace scope). */ + +public interface CDebugInfoDataBase { + /** Name-to-type mapping */ + public Type lookupType(String name); + + /** Name-to-type mapping with const/volatile qualifications */ + public Type lookupType(String name, int cvAttributes); + + /** Iteration through all types */ + public void iterate(TypeVisitor t); + + /** Return debug info (closest lexically-enclosing block) for + current program counter. Returns null if no debug information + found or available. */ + public BlockSym debugInfoForPC(Address pc); + + /** Look up global or module-local symbol by name. FIXME: need some + way to identify modules -- has not been thought through yet + because it isn't clear exactly how these are represented in the + Visual C++ debug info. */ + public GlobalSym lookupSym(String name); + + /** Returns line number information for the given PC, including + source file name (not specified whether this is an absolute or + relative path) and start and end PCs for this line. Returns null + if no line number information is available. */ + public LineNumberInfo lineNumberForPC(Address pc) throws DebuggerException; + + /** Iteration through all line number information in this + database. */ + public void iterate(LineNumberVisitor v); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CDebugger.java new file mode 100644 index 00000000000..d772b33111b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CDebugger.java @@ -0,0 +1,79 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; + +/** Interface describing how to debug C and C++ programs. Being kept + very minimal and incomplete for now; can be extended later. */ + +public interface CDebugger { + /** Fetch the thread list for the target process as a List of + ThreadProxy objects. Do not mutate this list. Throws + DebuggerException if the target process is not suspended (via + ProcessControl) or if the fetch failed for some other reason. */ + public List/**/ getThreadList() throws DebuggerException; + + /** Return a list of LoadObjects in the target process. Do not + mutate this list. Throws DebuggerException if the target process + is not suspended (via ProcessControl) or if the fetch failed for + some other reason. */ + public List/**/ getLoadObjectList() throws DebuggerException; + + /** Fetch the loadobject containing the current program counter. + Returns null if the PC was outside the ranges of all loadobjects + in the target process. Throws DebuggerException if the target + process is not suspended (via ProcessControl) or if the fetch + failed for some other reason. */ + public LoadObject loadObjectContainingPC(Address pc) throws DebuggerException; + + /** Create a CFrame object for the top frame of the given thread, + specified as a ThreadProxy. Returns null if there are no frames + on the stack or the frame can not be created for some other + reason. Throws DebuggerException if the target process is not + suspended (via ProcessControl). */ + public CFrame topFrameForThread(ThreadProxy t) + throws DebuggerException, IllegalThreadStateException; + + /** Get the file name component for the given full path to a DLL. + (The path separator characters and behavior of File.getName() + are platform-specific.) */ + public String getNameOfFile(String fileName) throws DebuggerException; + + /** Fetch a ProcessControl object for the target process, enabling + suspension, resumption and event handling. This method may + return null for many reasons, including that the underlying + implementation does not support process interaction, or that the + target process is dead (i.e., a core file). */ + public ProcessControl getProcessControl() throws DebuggerException; + + /** is demangling of C++ symbols supported by this CDebugger? */ + public boolean canDemangle(); + + /** Demangle C++ symbols into readable strings, if possible. + otherwise returns the input symbol back. */ + public String demangle(String sym) throws UnsupportedOperationException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java new file mode 100644 index 00000000000..4a1bfda1c28 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java @@ -0,0 +1,68 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/** Models a "C" programming language frame on the stack -- really + just an arbitrary frame with hooks to access C and C++ debug + information if available. It is recommended that implementors of + this interface derive from BasicCFrame, which provides some of the + functionality. */ + +public interface CFrame { + /** Returns null when no more frames on stack */ + public CFrame sender(); + + /** Get the program counter of this frame */ + public Address pc(); + + /** Get the loadobject in which the PC lies. Returns null if the PC + is not contained in any of the loadobjects in the target + process. */ + public LoadObject loadObjectForPC(); + + /** If debug information is available, retrieves the block in which + the program counter lies. Returns null if there is no debug + information for the current program counter or if the PC could + not be located for other reasons. */ + public BlockSym blockForPC(); + + /** For the loadobject in which the PC lies, fetch the name of the + closest exported symbol and the distance of the PC to that + symbol. Returns null if the PC was not within any of the + loadobjects of the target process. FIXME: specify whether this + is mangled/demangled. */ + public ClosestSymbol closestSymbolToPC(); + + /** Gets the base pointer in this frame from which local variable + offsets in the debug info are based. Typically this is the + base-of-frame pointer (EBP on x86, FP/I6 on SPARC). */ + public Address localVariableBase(); + + /** Visit all local variables in this frame if debug information is + available. Automatically descends into compound types and arrays. */ + public void iterateLocals(ObjectVisitor v); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CVAttributes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CVAttributes.java new file mode 100644 index 00000000000..262838089fc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CVAttributes.java @@ -0,0 +1,30 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface CVAttributes { + public static final int CONST = 0x01; + public static final int VOLATILE = 0x02; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ClosestSymbol.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ClosestSymbol.java new file mode 100644 index 00000000000..7c2abe98fdc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ClosestSymbol.java @@ -0,0 +1,50 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +/** Models the closest symbol to a given program counter: name and + offset. */ + +public class ClosestSymbol { + private String name; + private long offset; + + public ClosestSymbol(String name, long offset) { + this.name = name; + this.offset = offset; + } + + public String getName() { + return name; + } + + public long getOffset() { + return offset; + } + + public String offsetAsHex() { + return "0x" + Long.toHexString(offset); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CompoundType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CompoundType.java new file mode 100644 index 00000000000..b84864f42a2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/CompoundType.java @@ -0,0 +1,46 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +/** Models all compound types, i.e., those containing fields: classes, + structs, and unions. The boolean type accessors indicate how the + type is really defined in the debug information. */ + +public interface CompoundType { + public int getNumBaseClasses(); + public BaseClass getBaseClass(int i); + + public int getNumFields(); + public Field getField(int i); + + /** Defined as a class in the debug information? */ + public boolean isClass(); + + /** Defined as a struct in the debug information? */ + public boolean isStruct(); + + /** Defined as a union in the debug information? */ + public boolean isUnion(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/DebugEvent.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/DebugEvent.java new file mode 100644 index 00000000000..821ac68861e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/DebugEvent.java @@ -0,0 +1,83 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/** Describes in an abstract sense the kind of debug events which may + be received from the target process. On UNIX platforms these are + typically signals intercepted via ptrace or some other mechanism, + while on Windows they are usually exceptions. Also describes + certain types of events like loading and unloading of DSOs/DLLs + ("LoadObjects"). */ + +public interface DebugEvent { + public static class Type { + private Type() {} + /** Indicates a DSO/DLL was loaded by the target process */ + public static final Type LOADOBJECT_LOAD = new Type(); + /** Indicates a DSO/DLL was unloaded by the target process */ + public static final Type LOADOBJECT_UNLOAD = new Type(); + /** Indicates a breakpoint was hit */ + public static final Type BREAKPOINT = new Type(); + /** Indicates a single machine instruction was stepped */ + public static final Type SINGLE_STEP = new Type(); + /** Indicates an unmapped memory address was read from or written + to by the target process */ + public static final Type ACCESS_VIOLATION = new Type(); + /** Indicates an event of an unknown type occurred in the target + process (catch-all for implementations; but add more event + types) */ + public static final Type UNKNOWN = new Type(); + } + + /** The type of this debug event; BREAKPOINT, SINGLE_STEP, etc. */ + public Type getType(); + + /** Retrieves the ThreadProxy for the thread on which the event + occurred. This is always present. */ + public ThreadProxy getThread(); + + /** For BREAKPOINT, SINGLE_STEP, and ACCESS_VIOLATION events, + returns the program counter at which the event occurred. For + other types of events returns an undefined value. */ + public Address getPC(); + + /** For ACCESS_VIOLATION events, indicates whether the fault + occurred on a write (vs. a read). For other types of events + returns an undefined value. */ + public boolean getWasWrite(); + + /** For ACCESS_VIOLATION events, returns the address at which the + fault occurred. For LOADOBJECT_LOAD and LOADOBJECT_UNLOAD + events, returns the base address of the loadobject in the target + process's address space. For other types of events returns an + undefined value. */ + public Address getAddress(); + + /** For UNKNOWN events, may return a detail message or may return + null. For other types of events returns an undefined value. */ + public String getUnknownEventDetail(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/DefaultObjectVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/DefaultObjectVisitor.java new file mode 100644 index 00000000000..5bc217f16c7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/DefaultObjectVisitor.java @@ -0,0 +1,44 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/** Implementation of the ObjectVisitor interface with all methods + empty */ + +public class DefaultObjectVisitor implements ObjectVisitor { + public void enterType(Type type, Address objectAddress) {} + public void exitType() {} + public void doBit(FieldIdentifier f, long val) {} + public void doInt(FieldIdentifier f, long val) {} + public void doEnum(FieldIdentifier f, long val, String enumName) {} + public void doFloat(FieldIdentifier f, float val) {} + public void doDouble(FieldIdentifier f, double val) {} + public void doPointer(FieldIdentifier f, Address val) {} + public void doArray(FieldIdentifier f, Address val) {} + public void doRef(FieldIdentifier f, Address val) {} + public void doCompound(FieldIdentifier f, Address addressOfEmbeddedCompoundObject) {} +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/DoubleType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/DoubleType.java new file mode 100644 index 00000000000..f4c8960b837 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/DoubleType.java @@ -0,0 +1,28 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface DoubleType extends Type { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/EnumType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/EnumType.java new file mode 100644 index 00000000000..4f7d4466246 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/EnumType.java @@ -0,0 +1,42 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +/** Describes enumerated types. Enumerations are like ints except that + they have a set of named values. */ + +public interface EnumType extends IntType { + /** Number of enumerates defined in this enum */ + public int getNumEnumerates(); + + /** Fetch ith (0..getNumEnumerants() - 1) name */ + public String getEnumName(int i); + + /** Fetch ith (0..getNumEnumerants() - 1) value */ + public long getEnumValue(int i); + + /** Return name for given enum value, or null if not found */ + public String enumNameForValue(long val); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/Field.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/Field.java new file mode 100644 index 00000000000..cb0b7e9fd0c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/Field.java @@ -0,0 +1,40 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +public interface Field { + /** See {@link sun.jvm.hotspot.debugger.cdbg.AccessControl} */ + public int getAccessControl(); + public String getName(); + public Type getType(); + /** Indicates whether this field is static */ + public boolean isStatic(); + /** Nonstatic fields only: offset of field in data structure, in bytes */ + public long getOffset(); + /** Static fields only: address of the field */ + public Address getAddress(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FieldIdentifier.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FieldIdentifier.java new file mode 100644 index 00000000000..ab211845295 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FieldIdentifier.java @@ -0,0 +1,34 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +/** Abstraction over named fields and indices of arrays. Call + toString() on a FieldIdentifier to get a printable name for the + field. */ + +public interface FieldIdentifier { + public Type getType(); + public String toString(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FloatType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FloatType.java new file mode 100644 index 00000000000..957e30d5a62 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FloatType.java @@ -0,0 +1,28 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface FloatType extends Type { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FunctionSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FunctionSym.java new file mode 100644 index 00000000000..301e224ea6a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FunctionSym.java @@ -0,0 +1,43 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +/** A FunctionSym is an extension of a BlockSym which contains + additional information such as the type (signature) of the + function. */ + +public interface FunctionSym extends BlockSym { + /** Type (a FunctionType, unless it could not be properly resolved) + of the procedure */ + public Type getType(); + + /** Indicates whether this function is global or module-local (i.e., + static) */ + public boolean isModuleLocal(); + + /** Should provide a reasonable visual representation of the + signature of this symbol. */ + public String toString(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FunctionType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FunctionType.java new file mode 100644 index 00000000000..98296832a4b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/FunctionType.java @@ -0,0 +1,31 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface FunctionType extends Type { + public Type getReturnType(); + public int getNumArguments(); + public Type getArgumentType(int i); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/GlobalSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/GlobalSym.java new file mode 100644 index 00000000000..6855d932592 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/GlobalSym.java @@ -0,0 +1,43 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/** Models either a global variable or a module-local variable. */ + +public interface GlobalSym extends Sym { + /** Name of the variable */ + public String getName(); + + /** Type of the variable */ + public Type getType(); + + /** Address of the variable */ + public Address getAddress(); + + /** Indicates whether this symbol is module-local (i.e., static) */ + public boolean isModuleLocal(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/IndexableFieldIdentifier.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/IndexableFieldIdentifier.java new file mode 100644 index 00000000000..0b9dddfdfa0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/IndexableFieldIdentifier.java @@ -0,0 +1,31 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +/** Identifier for indices of arrays */ + +public interface IndexableFieldIdentifier extends FieldIdentifier { + public int getIndex(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/IntType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/IntType.java new file mode 100644 index 00000000000..8b8f55b82b0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/IntType.java @@ -0,0 +1,33 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface IntType extends Type { + /** Returns size in bytes of this type */ + public int getIntSize(); + + /** Indicates whether this type is unsigned */ + public boolean isUnsigned(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LineNumberInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LineNumberInfo.java new file mode 100644 index 00000000000..cc0f1599926 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LineNumberInfo.java @@ -0,0 +1,41 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/** Describes line number information for a given range of program + counters. */ + +public interface LineNumberInfo { + /** Not specified whether this is an absolute or relative path. */ + public String getSourceFileName(); + public int getLineNumber(); + public Address getStartPC(); + /** FIXME: specify whether this is inclusive or exclusive (currently + when BasicLineNumberMapping.recomputeEndPCs() is called, this is + exclusive) */ + public Address getEndPC(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LineNumberVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LineNumberVisitor.java new file mode 100644 index 00000000000..61671bd46e4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LineNumberVisitor.java @@ -0,0 +1,29 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface LineNumberVisitor { + public void doLineNumber(LineNumberInfo info); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LoadObject.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LoadObject.java new file mode 100644 index 00000000000..890f10ab29a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LoadObject.java @@ -0,0 +1,65 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/** A LoadObject models a DSO/DLL/EXE; that is, an entity relocated by + the run-time linker. */ + +public interface LoadObject { + /** Base address at which this loadobject was relocated at run-time */ + public Address getBase(); + + /** Full path name of this loadobject */ + public String getName(); + + /** Size of the loadobject in bytes (determines the range of program + counters and data contained within this loadobject) */ + public long getSize(); + + /** Returns a debug info database for this loadobject if debug info + is present; otherwise, returns null. */ + public CDebugInfoDataBase getDebugInfoDataBase() throws DebuggerException; + + /** Get debug information for the given program counter. PC must be + contained within this loadobject or a DebuggerException is + thrown. Returns null if there is no debug information available + (i.e., because this is not a debug build). */ + public BlockSym debugInfoForPC(Address pc) throws DebuggerException; + + /** Fetch the name of the closest exported symbol and the distance + of the PC to that symbol. Returns null if the PC was not within + this loadobject or if a symbol could not be found before this + PC. FIXME: specify whether this is mangled/demangled. */ + public ClosestSymbol closestSymbolToPC(Address pc) throws DebuggerException; + + /** Returns line number information for the given PC, including + source file name (not specified whether this is an absolute or + relative path) and start and end PCs for this line. Returns null + if no line number information is available or if the given PC is + not in this loadobject. */ + public LineNumberInfo lineNumberForPC(Address pc) throws DebuggerException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LoadObjectComparator.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LoadObjectComparator.java new file mode 100644 index 00000000000..64cc61b3193 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LoadObjectComparator.java @@ -0,0 +1,48 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; + +// a comparator used to sort LoadObjects by base address + +public class LoadObjectComparator implements Comparator { + public int compare(Object o1, Object o2) { + LoadObject lo1 = (LoadObject) o1; + LoadObject lo2 = (LoadObject) o2; + Address base1 = lo1.getBase(); + Address base2 = lo2.getBase(); + long diff = base1.minus(base2); + return (diff == 0)? 0 : ((diff < 0)? -1 : +1); + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof LoadObjectComparator)) { + return false; + } + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LocalSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LocalSym.java new file mode 100644 index 00000000000..7811fa340d8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/LocalSym.java @@ -0,0 +1,40 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +/** Models a local variable in a scope. The meaning of the frame + offset is platform-dependent, and is typically added to the base + of frame pointer. */ + +public interface LocalSym extends Sym { + /** Name of the local variable */ + public String getName(); + + /** Type of the local variable */ + public Type getType(); + + /** Offset, in bytes, in the frame of the local variable */ + public long getFrameOffset(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/MemberFunctionType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/MemberFunctionType.java new file mode 100644 index 00000000000..8b6e3152049 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/MemberFunctionType.java @@ -0,0 +1,38 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface MemberFunctionType extends FunctionType { + /** Containing class of this member function */ + public Type getContainingClass(); + + /** Type of this pointer */ + public Type getThisType(); + + /** Logical this adjustor for the method. Whenever a class element + is referenced via the this pointer, thisadjust will be added to + the resultant offset before referencing the element. */ + public long getThisAdjust(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/NamedFieldIdentifier.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/NamedFieldIdentifier.java new file mode 100644 index 00000000000..a2c2466529d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/NamedFieldIdentifier.java @@ -0,0 +1,31 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +/** Named fields in compound types */ + +public interface NamedFieldIdentifier extends FieldIdentifier { + public String getName(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ObjectVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ObjectVisitor.java new file mode 100644 index 00000000000..3fe3fc915de --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ObjectVisitor.java @@ -0,0 +1,88 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/** Provides uniform visitation to primitive and compound objects. + Typically one will have an Address of an "object" (in the + idealistic C++ definition, including "primitive objects" like + ints) and a Type for that object. This visitor interface allows + one to either get the value of the object (if of primitive type) + or iterate through its fields, getting the value of each, in a + consistent fashion. Also supports iteration through arrays of + known length. */ + +public interface ObjectVisitor { + /** This is called before beginning iterating through either the + fields declared in this compound type (not its superclasses) or + the elements of this array */ + public void enterType(Type type, Address objectAddress); + + /** This is called after finishing iterating through this compound + type */ + public void exitType(); + + /** Primitive field or object of integer bitfield + type. FieldIdentifier is null if not a field of an enclosing + object. */ + public void doBit(FieldIdentifier f, long val); + + /** Primitive field or object of integer type. FieldIdentifier is + null if not a field of an enclosing object. */ + public void doInt(FieldIdentifier f, long val); + + /** Primitive field or object of enumerated type type. + FieldIdentifier is null if not a field of an enclosing + object. */ + public void doEnum(FieldIdentifier f, long val, String enumName); + + /** Primitive field or object of single-precision floating-point + type. FieldIdentifier is null if not a field of an enclosing + object. */ + public void doFloat(FieldIdentifier f, float val); + + /** Primitive field or object of double-precision floating-point + type. FieldIdentifier is null if not a field of an enclosing + object. */ + public void doDouble(FieldIdentifier f, double val); + + /** Primitive field or object of pointer type. FieldIdentifier is + null if not a field of an enclosing object. */ + public void doPointer(FieldIdentifier f, Address val); + + /** Primitive field or object of array type. FieldIdentifier is null + if not a field of an enclosing object. */ + public void doArray(FieldIdentifier f, Address val); + + /** Primitive field or object of (C++) reference + type. FieldIdentifier is null if not a field of an enclosing + object. */ + public void doRef(FieldIdentifier f, Address val); + + /** Identifies embedded objects in compound objects. FieldIdentifier + is null if not a field of an enclosing object. */ + public void doCompound(FieldIdentifier f, Address addressOfEmbeddedCompoundObject); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/PointerType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/PointerType.java new file mode 100644 index 00000000000..d3c0ff05b30 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/PointerType.java @@ -0,0 +1,29 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface PointerType extends Type { + public Type getTargetType(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ProcessControl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ProcessControl.java new file mode 100644 index 00000000000..9ecf9e2cb5d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/ProcessControl.java @@ -0,0 +1,115 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/**

A highly experimental interface for process control and debug + events. May not be sufficiently portable; for this reason it has + been factored out from the CDebugger interface and support for it + is optional.

+ +

The ProcessControl interface defines a process control and + event model for debugging. When a process is attached to by the + base Debugger, all threads in the target process are suspended. + The ProcessControl interface allows resumption and re-suspension + of the threads in the target process, setting of breakpoints, and + reception of debugging events (breakpoint hit, signal received, + etc.).

+ +

Debugging events are generated one at a time by the target + process. They must be queued up by the underlying debugging + mechanism so that an attempt to send a second debugging event + blocks until the first has been serviced with a call to + debugEventResume.

*/ + +public interface ProcessControl { + /** Suspends all threads in the target process. A process is already + suspended when attached to by {@link + sun.jvm.hotspot.debugger.Debugger.attach(int)}. The application + should check for the presence of a debug event via + debugEventPoll() upon re-suspending the target process (if one + is not yet known to be present.) + + @throw DebuggerException if the process is already suspended or + if the suspension failed for some other reason. */ + public void suspend() throws DebuggerException; + + /** Resumes all threads in the target process. + + @throw DebuggerException if the process is not suspended or if + the resumption failed for some other reason. */ + public void resume() throws DebuggerException; + + /** Indicates whether the target process is suspended. */ + public boolean isSuspended() throws DebuggerException; + + /** Sets a breakpoint at the given address. The target process must + be suspended in order to set a breakpoint. + + @throw DebuggerException if the breakpoint could not be set for + some reason, including that a breakpoint is already set at that + address or that the underlying debugging mechanism does not + support that many breakpoints. */ + public void setBreakpoint(Address addr) + throws UnmappedAddressException, DebuggerException; + + /** Clears a breakpoint at the given address. The target process + must be suspended in order to clear a breakpoint. + + @throw DebuggerException if the breakpoint could not be cleared + for some reason, including that there was no breakpoint at that + address. */ + public void clearBreakpoint(Address addr) throws DebuggerException; + + /** Indicates whether a breakpoint is set at the given address. */ + public boolean isBreakpointSet(Address addr) throws DebuggerException; + + /** Polls for the presence of a debug event. Does not wait for one + to be generated; returns null if none was pending. The target + process does not need to be suspended. Returns the same + DebugEvent object until the debug event is handled via + debugEventContinue. Typically the application will suspend the + target process upon reception of a debug event but before + handling it via a call to debugEventContinue. This ensures that + the state of the thread which generated the debug event is + precisely what it was when the event was generated. + + @return The pending debug event, or null if none pending. */ + public DebugEvent debugEventPoll() throws DebuggerException; + + /** Informs the target process to resume past this debug event. The + target process does not need to be suspended. Breakpoint debug + events must be handled transparently by the implementation to + re-execute the instruction and replace the breakpoint. (Ideally + they should be replaced in such a way that there is no race + condition between the re-execution and the re-insertion of the + breakpoint.) All other kinds of exceptions or signals are passed + on to the target process. + + @throw DebuggerException if no debug event is pending. */ + public void debugEventContinue() + throws DebuggerException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/RefType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/RefType.java new file mode 100644 index 00000000000..f1cfb5b50b4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/RefType.java @@ -0,0 +1,29 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface RefType extends Type { + public Type getTargetType(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/Sym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/Sym.java new file mode 100644 index 00000000000..d0549fdaf93 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/Sym.java @@ -0,0 +1,46 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +/** Provides a superinterface for all symbol types */ + +public interface Sym { + /** Name of this symbol */ + public String getName(); + + /** Returns getName() unless a subclass can return something more + appropriate */ + public String toString(); + + public BlockSym asBlock(); + public FunctionSym asFunction(); + public GlobalSym asGlobal(); + public LocalSym asLocal(); + + public boolean isBlock(); + public boolean isFunction(); + public boolean isGlobal(); + public boolean isLocal(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/TemplateType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/TemplateType.java new file mode 100644 index 00000000000..89bb90c7249 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/TemplateType.java @@ -0,0 +1,33 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import java.util.*; + +public interface TemplateType extends Type { + public int getNumTemplateArguments(); + public Type instantiate(Type[] arguments); + public Type instantiate(List/**/ arguments); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/Type.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/Type.java new file mode 100644 index 00000000000..4d49c1148fe --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/Type.java @@ -0,0 +1,104 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +import sun.jvm.hotspot.debugger.*; + +/** Models a C or C++ type. Symbols have an associated Type. */ + +public interface Type { + public String getName(); + /** Size of the type in bytes */ + public int getSize(); + + public BitType asBit(); + public IntType asInt(); + public EnumType asEnum(); + public FloatType asFloat(); + public DoubleType asDouble(); + public PointerType asPointer(); + public ArrayType asArray(); + public RefType asRef(); + public CompoundType asCompound(); + public FunctionType asFunction(); + public MemberFunctionType asMemberFunction(); + public VoidType asVoid(); + + public boolean isBit(); + public boolean isInt(); + public boolean isEnum(); + public boolean isFloat(); + public boolean isDouble(); + public boolean isPointer(); + public boolean isArray(); + public boolean isRef(); + public boolean isCompound(); + public boolean isFunction(); + public boolean isMemberFunction(); + public boolean isVoid(); + + public boolean isConst(); + public boolean isVolatile(); + + /** Visit an object of this type at the given address with the + specified visitor */ + public void iterateObject(Address a, ObjectVisitor v); + + /** Alternate visitor which allows end user to specify the + FieldIdentifier associated with this type (typically for + visiting locals in a frame) */ + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f); + + /** Returns getName() unless a subclass can return something more + appropriate */ + public String toString(); + + /* + // Kinds of types + + // Primitive types + private static final int BIT; // Specialized integer type with bit offset and size + private static final int INT; // Integer type of any size and signedness + private static final int FLOAT; // Single-precision floating-point + private static final int DOUBLE; // Double-precision floating-point + + // Pointer and related types + private static final int PTR; // Any pointer type + private static final int ARRAY; // Array type with known size + private static final int REF; // C++ references + + // Compound types + private static final int COMPOUND; + + // Function type + private static final int FUNC; + + // Template types + private static final int TEMPLATE_CLASS; + private static final int TEMPLATE_STRUCT; + private static final int TEMPLATE_UNION; + private static final int TEMPLATE_FUNCTION; + */ +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/TypeVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/TypeVisitor.java new file mode 100644 index 00000000000..9b58eb8ead3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/TypeVisitor.java @@ -0,0 +1,40 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface TypeVisitor { + public void doBitType(BitType t); + public void doIntType(IntType t); + public void doEnumType(EnumType t); + public void doFloatType(FloatType t); + public void doDoubleType(DoubleType t); + public void doPointerType(PointerType t); + public void doArrayType(ArrayType t); + public void doRefType(RefType t); + public void doCompoundType(CompoundType t); + public void doFunctionType(FunctionType t); + public void doMemberFunctionType(MemberFunctionType t); + public void doVoidType(VoidType t); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/VoidType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/VoidType.java new file mode 100644 index 00000000000..edb2024f2d8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/VoidType.java @@ -0,0 +1,28 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg; + +public interface VoidType extends Type { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicArrayType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicArrayType.java new file mode 100644 index 00000000000..edab1c86770 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicArrayType.java @@ -0,0 +1,94 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicArrayType extends BasicType implements ArrayType { + private Type elementType; + private int length; + + public BasicArrayType(String name, Type elementType, int sizeInBytes) { + this(name, elementType, sizeInBytes, 0, 0); + } + + private BasicArrayType(String name, Type elementType, int sizeInBytes, int length, int cvAttributes) { + super(name, sizeInBytes, cvAttributes); + this.elementType = elementType; + this.length = length; + } + + public ArrayType asArray() { return this; } + + public Type getElementType() { return elementType; } + public int getLength() { return length; } + + Type resolveTypes(BasicCDebugInfoDataBase db, ResolveListener listener) { + super.resolveTypes(db, listener); + elementType = db.resolveType(this, elementType, listener, "resolving array element type"); + // FIXME: need to figure out whether we have to align object sizes + // ourselves (see below) + if (!((BasicType) elementType).isLazy()) { + length = getSize() / elementType.getSize(); + } + return this; + } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + // What kind of iteration are we doing? If the end user requested + // iteration over a given array at a given address, the field + // identifier will be null, and we should descend and iterate + // through the array. Otherwise, we are already iterating through + // an object, and it is up to the end user whether to descend into + // the embedded object. + + if (f == null) { + v.enterType(this, a); + for (int i = 0; i < getLength(); i++) { + // FIXME: need to figure out whether we need to align object + // sizes ourselves (i.e., round up to doubleword size) or + // whether that reported in the debug info is already aligned + // (in Microsoft's compiler, I think the struct alignment is + // an option, so it's possible that there is nothing we are + // allowed to do here) + ((BasicType) getElementType()).iterateObject(a.addOffsetTo(i * getElementType().getSize()), + v, + new BasicIndexableFieldIdentifier(getElementType(), i)); + } + v.exitType(); + } else { + v.doArray(f, a); + } + } + + protected Type createCVVariant(int cvAttributes) { + return new BasicArrayType(getName(), getElementType(), getSize(), getLength(), cvAttributes); + } + + public void visit(TypeVisitor v) { + v.doArrayType(this); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicBaseClass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicBaseClass.java new file mode 100644 index 00000000000..2dea43ffc4f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicBaseClass.java @@ -0,0 +1,47 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicBaseClass implements BaseClass { + private int accessControl; + private boolean isVirtual; + private Type type; + + public BasicBaseClass(int accessControl, boolean isVirtual, Type type) { + this.accessControl = accessControl; + this.isVirtual = isVirtual; + this.type = type; + } + + public int getAccessControl() { return accessControl; } + public boolean isVirtual() { return isVirtual; } + public Type getType() { return type; } + + public void resolveTypes(Type containingType, BasicCDebugInfoDataBase db, ResolveListener listener) { + type = db.resolveType(containingType, type, listener, "resolving base class"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicBitType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicBitType.java new file mode 100644 index 00000000000..0199552a37c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicBitType.java @@ -0,0 +1,108 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.utilities.Assert; + +public class BasicBitType extends BasicIntType implements BitType { + // Integer type or lazy type + private Type underlyingType; + private int sizeInBits; + private int offset; + + /** Underlying type of enum must be an integer type (or as yet + unresolved) */ + + public BasicBitType(Type underlyingType, int sizeInBits, int lsbOffset) { + this(underlyingType, sizeInBits, lsbOffset, 0); + } + + private BasicBitType(Type underlyingType, int sizeInBits, int lsbOffset, int cvAttributes) { + super(null, 0, false, cvAttributes); + this.underlyingType = underlyingType; + this.sizeInBits = sizeInBits; + this.offset = lsbOffset; + } + + public BitType asBit() { return this; } + + public int getSize() { return underlyingType.getSize(); } + public boolean isUnsigned() { + if (underlyingType.isInt()) { + return ((IntType) underlyingType).isUnsigned(); + } + return false; + } + + public int getSizeInBits() { + return sizeInBits; + } + + public int getOffset() { + return offset; + } + + Type resolveTypes(BasicCDebugInfoDataBase db, ResolveListener listener) { + super.resolveTypes(db, listener); + underlyingType = db.resolveType(this, underlyingType, listener, "resolving bit type"); + setName(underlyingType.getName()); + if (Assert.ASSERTS_ENABLED) { + BasicType b = (BasicType) underlyingType; + Assert.that(b.isLazy() || b.isInt(), + "Underlying type of bitfield must be integer type (or unresolved due to error)"); + } + return this; + } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + long mask = maskFor(sizeInBits); + long val = ((a.getCIntegerAt(0, getSize(), isUnsigned())) >> getOffset()) & mask; + if (!isUnsigned()) { + if ((val & highBit(sizeInBits)) != 0) { + // Must sign extend + val = val | (~mask); + } + } + v.doBit(f, val); + } + + protected Type createCVVariant(int cvAttributes) { + return new BasicBitType(underlyingType, getSizeInBits(), getOffset(), cvAttributes); + } + + public void visit(TypeVisitor v) { + v.doBitType(this); + } + + private static long maskFor(int sizeInBits) { + return ((1 << sizeInBits) - 1); + } + + private static long highBit(int sizeInBits) { + return (1 << (sizeInBits - 1)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicBlockSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicBlockSym.java new file mode 100644 index 00000000000..3c6574294f2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicBlockSym.java @@ -0,0 +1,80 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicBlockSym extends BasicSym implements BlockSym { + private BlockSym parent; + private long length; + private Address addr; + + private List locals; + + /** Creates a new BlockSym. Parent can be null. */ + public BasicBlockSym(BlockSym parent, long length, Address addr, String name) { + super(name); + this.parent = parent; + this.length = length; + this.addr = addr; + } + + public BlockSym asBlock() { return this; } + + public BlockSym getParent() { return parent; } + public long getLength() { return length; } + public Address getAddress() { return addr; } + + public int getNumLocals() { + if (locals == null) { + return 0; + } + + return locals.size(); + } + + public LocalSym getLocal(int i) { + return (LocalSym) locals.get(i); + } + + public void addLocal(LocalSym local) { + if (locals == null) { + locals = new ArrayList(); + } + locals.add(local); + } + + public void resolve(BasicCDebugInfoDataBase db, ResolveListener listener) { + parent = (BlockSym) db.resolveSym(this, parent, listener, "resolving parent of block"); + if (locals != null) { + for (Iterator iter = locals.iterator(); iter.hasNext(); ) { + ((BasicLocalSym) iter.next()).resolve(db, listener); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicCDebugInfoDataBase.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicCDebugInfoDataBase.java new file mode 100644 index 00000000000..d3e66592cc8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicCDebugInfoDataBase.java @@ -0,0 +1,384 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.utilities.AddressOps; +import sun.jvm.hotspot.utilities.Assert; + +public class BasicCDebugInfoDataBase implements CDebugInfoDataBase { + private static final int INITIALIZED_STATE = 0; + private static final int CONSTRUCTION_STATE = 1; + private static final int RESOLVED_STATE = 2; + private static final int COMPLETE_STATE = 3; + + private int state = INITIALIZED_STATE; + + /////////// + // Types // + /////////// + + // Used only during construction + private Map lazyTypeMap; + + // Used during construction and at run time for iteration + private List types; + + // Used only during runtime + private Map nameToTypeMap; + + ///////////// + // Symbols // + ///////////// + + // Used only during construction + private Map lazySymMap; + + // List of blocks in increasing order by starting address. These can + // then be binary searched. + private List blocks; + + // Name-to-global symbol table + private Map nameToSymMap; + + ////////////////// + // Line numbers // + ////////////////// + + private BasicLineNumberMapping lineNumbers; + + /** Supports lazy instantiation and references between types and + symbols via insertion using arbitrary Object keys that are + wrapped by LazyTypes. Once the database has been fully + constructed and all types are present, one should call + resolveTypes(), which will resolve all LazyTypes down to + concrete types (and signal an error if some lazy types were + unresolved). */ + public void beginConstruction() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(state == INITIALIZED_STATE, "wrong state"); + } + state = CONSTRUCTION_STATE; + + // Types + lazyTypeMap = new HashMap(); + types = new ArrayList(); + + // Symbols + lazySymMap = new HashMap(); + blocks = new ArrayList(); + nameToSymMap = new HashMap(); + + // Line numbers + lineNumbers = new BasicLineNumberMapping(); + } + + /** Add a type which may later in construction be referred to via a + LazyType with this key. lazyKey may be null. */ + public void addType(Object lazyKey, Type type) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(state == CONSTRUCTION_STATE, "wrong state"); + } + if (lazyKey != null) { + if (lazyTypeMap.put(lazyKey, type) != null) { + throw new RuntimeException("Type redefined for lazy key " + lazyKey); + } + } else { + types.add(type); + } + } + + public void resolve(ResolveListener listener) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(state == CONSTRUCTION_STATE, "wrong state"); + } + // Go through all types in lazyTypeMap and types. + // Resolve all LazyTypes. + resolveLazyMap(listener); + for (ListIterator iter = types.listIterator(); iter.hasNext(); ) { + BasicType t = (BasicType) iter.next(); + BasicType t2 = (BasicType) t.resolveTypes(this, listener); + if (t != t2) { + iter.set(t2); + } + } + // Go through all symbols and resolve references to types and + // references to other symbols + for (Iterator iter = blocks.iterator(); iter.hasNext(); ) { + ((BasicSym) iter.next()).resolve(this, listener); + } + for (Iterator iter = nameToSymMap.values().iterator(); iter.hasNext(); ) { + ((BasicSym) iter.next()).resolve(this, listener); + } + + // Sort blocks in ascending order of starting address (but do not + // change ordering among blocks with the same starting address) + Collections.sort(blocks, new Comparator() { + public int compare(Object o1, Object o2) { + BlockSym b1 = (BlockSym) o1; + BlockSym b2 = (BlockSym) o2; + Address a1 = b1.getAddress(); + Address a2 = b2.getAddress(); + if (AddressOps.lt(a1, a2)) { return -1; } + if (AddressOps.gt(a1, a2)) { return 1; } + return 0; + } + }); + + state = RESOLVED_STATE; + } + + public void endConstruction() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(state == RESOLVED_STATE, "wrong state"); + } + // Move all types to type list + for (Iterator iter = lazyTypeMap.values().iterator(); iter.hasNext(); ) { + types.add(iter.next()); + } + // Build name-to-type map + nameToTypeMap = new HashMap(); + for (Iterator iter = types.iterator(); iter.hasNext(); ) { + Type t = (Type) iter.next(); + if (!t.isConst() && !t.isVolatile()) { + nameToTypeMap.put(t.getName(), t); + } + } + // Lose lazy maps + lazyTypeMap = null; + lazySymMap = null; + // Sort and finish line number information + lineNumbers.sort(); + // FIXME: on some platforms it might not be necessary to call + // recomputeEndPCs(). Will have to see what stabs information + // looks like. Should make configurable whether we make this call + // or not. + lineNumbers.recomputeEndPCs(); + + state = COMPLETE_STATE; + } + + public Type lookupType(String name) { + return lookupType(name, 0); + } + + public Type lookupType(String name, int cvAttributes) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(state == COMPLETE_STATE, "wrong state"); + } + BasicType t = (BasicType) nameToTypeMap.get(name); + if (t != null) { + if (cvAttributes != 0) { + t = (BasicType) t.getCVVariant(cvAttributes); + } + } + return t; + } + + public void iterate(TypeVisitor v) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(state == COMPLETE_STATE, "wrong state"); + } + for (Iterator iter = types.iterator(); iter.hasNext(); ) { + BasicType t = (BasicType) iter.next(); + t.visit(v); + } + } + + /** Add a BlockSym to the debug information database. The given + BlockSym may be referred to by a LazyBlockSym wrapping the given + Object key, which must be non-null. Any references to other + blocks (for example, the parent scope) should be made with + LazyBlockSyms. These references will be resolved after the + database is built. */ + public void addBlock(Object key, BlockSym block) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(key != null, "key must be non-null"); + } + lazySymMap.put(key, block); + blocks.add(block); + } + + public void addGlobalSym(GlobalSym sym) { + nameToSymMap.put(sym.getName(), sym); + } + + public BlockSym debugInfoForPC(Address pc) { + return searchBlocks(pc, 0, blocks.size() - 1); + } + + public GlobalSym lookupSym(String name) { + return (GlobalSym) nameToSymMap.get(name); + } + + public void addLineNumberInfo(BasicLineNumberInfo info) { + lineNumbers.addLineNumberInfo(info); + } + + public LineNumberInfo lineNumberForPC(Address pc) throws DebuggerException { + return lineNumbers.lineNumberForPC(pc); + } + + public void iterate(LineNumberVisitor v) { + lineNumbers.iterate(v); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + /** Intended only to be used by the BasicType implementation. */ + public Type resolveType(Type containingType, Type targetType, ResolveListener listener, String detail) { + BasicType basicTargetType = (BasicType) targetType; + if (Assert.ASSERTS_ENABLED) { + Assert.that(state == CONSTRUCTION_STATE, "wrong state"); + } + if (basicTargetType.isLazy()) { + BasicType resolved = (BasicType) lazyTypeMap.get(((LazyType) targetType).getKey()); + // FIXME: would like to have an assert here that the target is + // non-null, but apparently have bugs here with forward + // references of pointer types + if (resolved == null) { + listener.resolveFailed(containingType, (LazyType) targetType, detail + " because target type was not found"); + return targetType; + } + if (resolved.isLazy()) { + // Might happen for const/var variants for forward references + if (resolved.isConst() || resolved.isVolatile()) { + resolved = (BasicType) resolved.resolveTypes(this, listener); + } + if (resolved.isLazy()) { + listener.resolveFailed(containingType, (LazyType) targetType, + detail + " because target type (with key " + + ((Integer) ((LazyType) resolved).getKey()).intValue() + + (resolved.isConst() ? ", const" : ", not const") + + (resolved.isVolatile() ? ", volatile" : ", not volatile") + + ") was lazy"); + } + } + return resolved; + } + return targetType; + } + + /** Intended only to be usd by the BasicSym implementation. */ + public Type resolveType(Sym containingSymbol, Type targetType, ResolveListener listener, String detail) { + BasicType basicTargetType = (BasicType) targetType; + if (Assert.ASSERTS_ENABLED) { + Assert.that(state == CONSTRUCTION_STATE, "wrong state"); + } + if (basicTargetType.isLazy()) { + BasicType resolved = (BasicType) lazyTypeMap.get(((LazyType) targetType).getKey()); + // FIXME: would like to have an assert here that the target is + // non-null, but apparently have bugs here + if (resolved == null) { + listener.resolveFailed(containingSymbol, (LazyType) targetType, detail); + return targetType; + } + if (resolved.isLazy()) { + // Might happen for const/var variants for forward references + if (resolved.isConst() || resolved.isVolatile()) { + resolved = (BasicType) resolved.resolveTypes(this, listener); + } + if (resolved.isLazy()) { + listener.resolveFailed(containingSymbol, (LazyType) targetType, detail); + } + } + return resolved; + } + return targetType; + } + + /** Intended only to be usd by the BasicSym implementation. */ + public Sym resolveSym(Sym containingSymbol, Sym targetSym, ResolveListener listener, String detail) { + if (targetSym == null) return null; + BasicSym basicTargetSym = (BasicSym) targetSym; + if (Assert.ASSERTS_ENABLED) { + Assert.that(state == CONSTRUCTION_STATE, "wrong state"); + } + if (basicTargetSym.isLazy()) { + BasicSym resolved = (BasicSym) lazySymMap.get(((LazyBlockSym) targetSym).getKey()); + // FIXME: would like to have an assert here that the target is + // non-null, but apparently have bugs here + if (resolved == null) { + listener.resolveFailed(containingSymbol, (LazyBlockSym) targetSym, detail); + return targetSym; + } + if (resolved.isLazy()) { + listener.resolveFailed(containingSymbol, (LazyBlockSym) targetSym, detail); + } + return resolved; + } + return targetSym; + } + + private void resolveLazyMap(ResolveListener listener) { + for (Iterator iter = lazyTypeMap.entrySet().iterator(); iter.hasNext(); ) { + Map.Entry entry = (Map.Entry) iter.next(); + BasicType t = (BasicType) entry.getValue(); + BasicType t2 = (BasicType) t.resolveTypes(this, listener); + if (t2 != t) { + entry.setValue(t2); + } + } + } + + /** Find the block whose starting address is closest to but less + than the given address. */ + private BlockSym searchBlocks(Address addr, int lowIdx, int highIdx) { + if (highIdx < lowIdx) return null; + if ((lowIdx == highIdx) || (lowIdx == highIdx - 1)) { + // Base case: start with highIdx and walk backward. See whether + // addr is greater than any of the blocks' starting addresses, + // and if so, return that block. + Address lastAddr = null; + BlockSym ret = null; + for (int i = highIdx; i >= 0; --i) { + BlockSym block = (BlockSym) blocks.get(i); + if (AddressOps.lte(block.getAddress(), addr)) { + if ((lastAddr == null) || (AddressOps.equal(block.getAddress(), lastAddr))) { + lastAddr = block.getAddress(); + ret = block; + } else { + break; + } + } + } + return ret; + } + int midIdx = (lowIdx + highIdx) >> 1; + BlockSym block = (BlockSym) blocks.get(midIdx); + // See address relationship + if (AddressOps.lte(block.getAddress(), addr)) { + // Always move search up + return searchBlocks(addr, midIdx, highIdx); + } else { + // Always move search down + return searchBlocks(addr, lowIdx, midIdx); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicCFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicCFrame.java new file mode 100644 index 00000000000..981339f2e3b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicCFrame.java @@ -0,0 +1,94 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +/** Basic implementation of the CFrame interface providing some of the + functionality in a platform-independent manner. */ + +public abstract class BasicCFrame implements CFrame { + private CDebugger dbg; + + protected BasicCFrame(CDebugger dbg) { + this.dbg = dbg; + } + + protected CDebugger dbg() { + return dbg; + } + + public LoadObject loadObjectForPC() { + return dbg.loadObjectContainingPC(pc()); + } + + public BlockSym blockForPC() { + LoadObject lo = loadObjectForPC(); + if (lo == null) { + return null; + } + return lo.debugInfoForPC(pc()); + } + + public ClosestSymbol closestSymbolToPC() { + LoadObject lo = loadObjectForPC(); + if (lo == null) { + return null; + } + return lo.closestSymbolToPC(pc()); + } + + public void iterateLocals(ObjectVisitor v) { + BlockSym block = blockForPC(); + while (block != null) { + for (int i = 0; i < block.getNumLocals(); i++) { + final LocalSym local = block.getLocal(i); + Type t = local.getType(); + // t should not be null, but be robust in case of bugs in + // debug info + if (t != null) { + t.iterateObject(localVariableBase().addOffsetTo(local.getFrameOffset()), + v, + new NamedFieldIdentifier() { + public Type getType() { + return local.getType(); + } + + public String getName() { + return local.getName(); + } + + public String toString() { + return getName(); + } + }); + } + } + + block = block.getParent(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicCompoundType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicCompoundType.java new file mode 100644 index 00000000000..8a84841c2ee --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicCompoundType.java @@ -0,0 +1,143 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.utilities.Assert; + +public class BasicCompoundType extends BasicType implements CompoundType { + private CompoundTypeKind kind; + private List baseClasses; + private List fields; + + public BasicCompoundType(String name, int size, CompoundTypeKind kind) { + this(name, size, kind, 0); + } + + private BasicCompoundType(String name, int size, CompoundTypeKind kind, int cvAttributes) { + super(name, size, cvAttributes); + if (Assert.ASSERTS_ENABLED) { + Assert.that(kind != null, "null kind"); + } + this.kind = kind; + } + + public CompoundType asCompound() { return this; } + + public int getNumBaseClasses() { + return ((baseClasses == null) ? 0 : baseClasses.size()); + } + public BaseClass getBaseClass(int i) { + return (BaseClass) baseClasses.get(i); + } + + public void addBaseClass(BaseClass b) { + if (baseClasses == null) { + baseClasses = new ArrayList(); + } + baseClasses.add(b); + } + + public int getNumFields() { + return ((fields == null) ? 0 : fields.size()); + } + public Field getField(int i) { + return (Field) fields.get(i); + } + + public void addField(Field f) { + if (fields == null) { + fields = new ArrayList(); + } + fields.add(f); + } + + public boolean isClass() { return (kind == CompoundTypeKind.CLASS); } + public boolean isStruct() { return (kind == CompoundTypeKind.STRUCT); } + public boolean isUnion() { return (kind == CompoundTypeKind.UNION); } + + Type resolveTypes(BasicCDebugInfoDataBase db, ResolveListener listener) { + super.resolveTypes(db, listener); + if (baseClasses != null) { + for (Iterator iter = baseClasses.iterator(); iter.hasNext(); ) { + BasicBaseClass b = (BasicBaseClass) iter.next(); + b.resolveTypes(this, db, listener); + } + } + if (fields != null) { + for (Iterator iter = fields.iterator(); iter.hasNext(); ) { + BasicField b = (BasicField) iter.next(); + b.resolveTypes(this, db, listener); + } + } + return this; + } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + // What kind of iteration are we doing? If the end user requested + // iteration over a given object at a given address, the field + // identifier will be null, and we should descend and iterate over + // our fields and superclasses. Otherwise, we are already + // iterating through an object, and it is up to the end user + // whether to descend into the embedded object. + if (f == null) { + // FIXME: this is one of the key hard components of this + // implementation. Will need to properly handle multiple + // inheritance and possibly virtual base classes (i.e., not + // iterating twice for a virtual base class inherited indirectly + // more than once). For now, we do the simple thing, which + // assumes single inheritance. + for (int i = 0; i < getNumBaseClasses(); i++) { + BasicCompoundType b = (BasicCompoundType) getBaseClass(i).getType(); + b.iterateObject(a, v, f); + } + // Now we are in our scope + v.enterType(this, a); + // Iterate through our fields + for (int i = 0; i < getNumFields(); i++) { + Field field = getField(i); + BasicType fieldType = (BasicType) field.getType(); + fieldType.iterateObject(a.addOffsetTo(field.getOffset()), v, new BasicNamedFieldIdentifier(field)); + } + v.exitType(); + } else { + v.doCompound(f, a); + } + } + + protected Type createCVVariant(int cvAttributes) { + BasicCompoundType t = new BasicCompoundType(getName(), getSize(), kind, cvAttributes); + t.kind = kind; + t.baseClasses = baseClasses; + t.fields = fields; + return t; + } + + public void visit(TypeVisitor v) { + v.doCompoundType(this); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicDebugEvent.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicDebugEvent.java new file mode 100644 index 00000000000..a5189cfb086 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicDebugEvent.java @@ -0,0 +1,106 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicDebugEvent implements DebugEvent { + private DebugEvent.Type type; + private ThreadProxy thread; + private Address pc; + private Address address; + private boolean wasWrite; + private String detail; + + public BasicDebugEvent(DebugEvent.Type type, ThreadProxy thread) { + this.type = type; + this.thread = thread; + } + + public DebugEvent.Type getType() { return type; } + public ThreadProxy getThread() { return thread; } + public Address getPC() { return pc; } + public boolean getWasWrite() { return wasWrite; } + public Address getAddress() { return address; } + public String getUnknownEventDetail() { return detail; } + + /** Mutators for convenience */ + public void setType(DebugEvent.Type type) { this.type = type; } + public void setThread(ThreadProxy thread) { this.thread = thread; } + public void setPC(Address pc) { this.pc = pc; } + public void setWasWrite(boolean val) { wasWrite = val; } + public void setAddress(Address address) { this.address = address; } + public void setUnknownEventDetail(String msg) { detail = msg; } + + /** Factory methods for convenience */ + public static BasicDebugEvent newLoadObjectLoadEvent(ThreadProxy thread, Address base) { + return newAddressEvent(DebugEvent.Type.LOADOBJECT_LOAD, thread, base); + } + + public static BasicDebugEvent newLoadObjectUnloadEvent(ThreadProxy thread, Address base) { + return newAddressEvent(DebugEvent.Type.LOADOBJECT_UNLOAD, thread, base); + } + + public static BasicDebugEvent newBreakpointEvent(ThreadProxy thread, Address pc) { + return newPCEvent(DebugEvent.Type.BREAKPOINT, thread, pc); + } + + public static BasicDebugEvent newSingleStepEvent(ThreadProxy thread, Address pc) { + return newPCEvent(DebugEvent.Type.BREAKPOINT, thread, pc); + } + + public static BasicDebugEvent newAccessViolationEvent(ThreadProxy thread, + Address pc, + boolean wasWrite, + Address addr) { + BasicDebugEvent ev = newPCEvent(DebugEvent.Type.ACCESS_VIOLATION, thread, pc); + ev.setWasWrite(wasWrite); + ev.setAddress(addr); + return ev; + } + + public static BasicDebugEvent newUnknownEvent(ThreadProxy thread, String detail) { + BasicDebugEvent ev = new BasicDebugEvent(DebugEvent.Type.UNKNOWN, thread); + ev.setUnknownEventDetail(detail); + return ev; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private static BasicDebugEvent newAddressEvent(DebugEvent.Type type, ThreadProxy thread, Address addr) { + BasicDebugEvent ev = new BasicDebugEvent(type, thread); + ev.setAddress(addr); + return ev; + } + + private static BasicDebugEvent newPCEvent(DebugEvent.Type type, ThreadProxy thread, Address pc) { + BasicDebugEvent ev = new BasicDebugEvent(type, thread); + ev.setPC(pc); + return ev; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicDoubleType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicDoubleType.java new file mode 100644 index 00000000000..05e7f7c04a9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicDoubleType.java @@ -0,0 +1,52 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicDoubleType extends BasicType implements DoubleType { + public BasicDoubleType(String name, int size) { + this(name, size, 0); + } + + private BasicDoubleType(String name, int size, int cvAttributes) { + super(name, size, cvAttributes); + } + + public DoubleType asDouble() { return this; } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + v.doDouble(f, a.getJDoubleAt(0)); + } + + protected Type createCVVariant(int cvAttributes) { + return new BasicDoubleType(getName(), getSize(), cvAttributes); + } + + public void visit(TypeVisitor v) { + v.doDoubleType(this); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicEnumType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicEnumType.java new file mode 100644 index 00000000000..d2f1163fbb4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicEnumType.java @@ -0,0 +1,122 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.utilities.Assert; + +public class BasicEnumType extends BasicIntType implements EnumType { + // Integer type or lazy type + private Type underlyingType; + + private static class Enum { + String name; + long value; + Enum(String name, long value) { + this.name = name; + this.value = value; + } + + String getName() { return name; } + long getValue() { return value; } + } + private List/**/ enums; + + /** Underlying type of enum must be an integer type (or as yet + unresolved) */ + + public BasicEnumType(String name, Type underlyingType) { + this(name, underlyingType, 0); + } + + private BasicEnumType(String name, Type underlyingType, int cvAttributes) { + super(name, 0, false, cvAttributes); + this.underlyingType = underlyingType; + } + + public EnumType asEnum() { return this; } + + public int getSize() { return underlyingType.getSize(); } + public boolean isUnsigned() { + if (underlyingType.isInt()) { + return ((IntType) underlyingType).isUnsigned(); + } + return false; + } + + public void addEnum(String name, long val) { + if (enums == null) { + enums = new ArrayList(); + } + enums.add(new Enum(name, val)); + } + + public int getNumEnumerates() { return enums.size(); } + public String getEnumName(int i) { return ((Enum) enums.get(i)).getName(); } + public long getEnumValue(int i) { return ((Enum) enums.get(i)).getValue(); } + + public String enumNameForValue(long val) { + if (enums == null) { + return null; + } + + for (Iterator iter = enums.iterator(); iter.hasNext(); ) { + Enum e = (Enum) iter.next(); + if (e.getValue() == val) { + return e.getName(); + } + } + + return null; + } + + Type resolveTypes(BasicCDebugInfoDataBase db, ResolveListener listener) { + super.resolveTypes(db, listener); + underlyingType = db.resolveType(this, underlyingType, listener, "resolving enum type"); + if (Assert.ASSERTS_ENABLED) { + BasicType b = (BasicType) underlyingType; + Assert.that(b.isLazy() || b.isInt(), + "Underlying type of enum must be integer type (or unresolved due to error)"); + } + return this; + } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + long val = a.getCIntegerAt(0, getSize(), isUnsigned()); + v.doEnum(f, val, enumNameForValue(val)); + } + + protected Type createCVVariant(int cvAttributes) { + BasicEnumType t = new BasicEnumType(getName(), underlyingType, cvAttributes); + t.enums = enums; + return t; + } + + public void visit(TypeVisitor v) { + v.doEnumType(this); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicField.java new file mode 100644 index 00000000000..1edf9fad982 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicField.java @@ -0,0 +1,95 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicField implements Field { + private String name; + private Type type; + private int accessControl; + private boolean isStatic; + private long offset; + private Address address; + + /** See {@link sun.jvm.hotspot.debugger.cdbg.AccessControl} for + valid access control values */ + public BasicField(String name, Type type, int accessControl, boolean isStatic) { + this.name = name; + this.type = type; + this.accessControl = accessControl; + } + + public int getAccessControl() { return accessControl; } + + public String getName() { return name; } + + public Type getType() { return type; } + + public boolean isStatic() { return isStatic; } + + /** Nonstatic fields only: set offset of field */ + public void setOffset(long offset) { + if (isStatic) throw new RuntimeException("Nonstatic fields only"); + this.offset = offset; + } + + /** Nonstatic fields only: get offset of field */ + public long getOffset() { + if (isStatic) throw new RuntimeException("Nonstatic fields only"); + return offset; + } + + /** Static fields only: set address of field. The resolution + mechanism will automatically attempt to find the address of the + field based on a "class name"::"field name" global symbol lookup + in the database if the address has not been set. */ + public void setAddress(Address address) { + if (!isStatic) throw new RuntimeException("Static fields only"); + this.address = address; + } + + /** Static fields only: get address of field */ + public Address getAddress() { + if (!isStatic) throw new RuntimeException("Static fields only"); + return address; + } + + public void resolveTypes(Type containingType, BasicCDebugInfoDataBase db, ResolveListener listener) { + type = db.resolveType(containingType, type, listener, "resolving field type"); + if (isStatic) { + if (address == null) { + String fieldSymName = getType().getName() + "::" + getName(); + GlobalSym sym = db.lookupSym(fieldSymName); + if (sym == null) { + listener.resolveFailed(getType(), getName()); + } else { + address = sym.getAddress(); + } + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicFloatType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicFloatType.java new file mode 100644 index 00000000000..47b6fb8d8be --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicFloatType.java @@ -0,0 +1,52 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicFloatType extends BasicType implements FloatType { + public BasicFloatType(String name, int size) { + this(name, size, 0); + } + + private BasicFloatType(String name, int size, int cvAttributes) { + super(name, size, cvAttributes); + } + + public FloatType asFloat() { return this; } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + v.doFloat(f, a.getJFloatAt(0)); + } + + protected Type createCVVariant(int cvAttributes) { + return new BasicFloatType(getName(), getSize(), cvAttributes); + } + + public void visit(TypeVisitor v) { + v.doFloatType(this); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicFunctionSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicFunctionSym.java new file mode 100644 index 00000000000..93771e23b53 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicFunctionSym.java @@ -0,0 +1,72 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicFunctionSym extends BasicBlockSym implements FunctionSym { + private Type type; + private boolean isModuleLocal; + + public BasicFunctionSym(BlockSym parent, long length, Address addr, String name, + Type type, boolean isModuleLocal) { + super(parent, length, addr, name); + this.type = type; + this.isModuleLocal = isModuleLocal; + } + + public FunctionSym asFunction() { return this; } + + public Type getType() { return type; } + public boolean isModuleLocal() { return isModuleLocal; } + + public void resolve(BasicCDebugInfoDataBase db, ResolveListener listener) { + super.resolve(db, listener); + type = db.resolveType(this, type, listener, "resolving type of function symbol"); + } + + public String toString() { + if (getName() == null) { + return null; + } + + StringBuffer res = new StringBuffer(); + res.append(getName()); + res.append("("); + FunctionType type = (FunctionType) getType(); + if (type != null) { + int nargs = type.getNumArguments(); + for (int i = 0; i < nargs; i++) { + res.append(type.getArgumentType(i).toString()); + if (i != nargs - 1) { + res.append(", "); + } + } + } + res.append(")"); + return res.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicFunctionType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicFunctionType.java new file mode 100644 index 00000000000..949d54829f4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicFunctionType.java @@ -0,0 +1,85 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicFunctionType extends BasicType implements FunctionType { + private Type returnType; + private List argumentTypes; + + public BasicFunctionType(String name, int size, Type returnType) { + this(name, size, returnType, 0); + } + + protected BasicFunctionType(String name, int size, Type returnType, int cvAttributes) { + super(name, size, cvAttributes); + this.returnType = returnType; + } + + public FunctionType asFunction() { return this; } + + public Type getReturnType() { return returnType; } + + public int getNumArguments() { return ((argumentTypes == null) ? 0 : argumentTypes.size()); } + public Type getArgumentType(int i) { + return (Type) argumentTypes.get(i); + } + public void addArgumentType(Type t) { + if (argumentTypes == null) { + argumentTypes = new ArrayList(); + } + argumentTypes.add(t); + } + + Type resolveTypes(BasicCDebugInfoDataBase db, ResolveListener listener) { + super.resolveTypes(db, listener); + returnType = db.resolveType(this, returnType, listener, "resolving function return type"); + if (argumentTypes != null) { + for (ListIterator iter = argumentTypes.listIterator(); iter.hasNext(); ) { + iter.set(db.resolveType(this, (Type) iter.next(), listener, "resolving function argument types")); + } + } + return this; + } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + // FIXME: nothing to do here? Are we going to provide iteration + // mechanisms through member functions, and if so what are the + // types of those functions going to be? + } + + protected Type createCVVariant(int cvAttributes) { + BasicFunctionType t = new BasicFunctionType(getName(), getSize(), getReturnType(), cvAttributes); + t.argumentTypes = argumentTypes; + return t; + } + + public void visit(TypeVisitor v) { + v.doFunctionType(this); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicGlobalSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicGlobalSym.java new file mode 100644 index 00000000000..d217b88228e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicGlobalSym.java @@ -0,0 +1,51 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicGlobalSym extends BasicSym implements GlobalSym { + private Type type; + private Address addr; + private boolean isModuleLocal; + + public BasicGlobalSym(String name, Type type, Address addr, boolean isModuleLocal) { + super(name); + this.type = type; + this.addr = addr; + this.isModuleLocal = isModuleLocal; + } + + public GlobalSym asGlobal() { return this; } + + public Type getType() { return type; } + public Address getAddress() { return addr; } + public boolean isModuleLocal() { return isModuleLocal; } + + public void resolve(BasicCDebugInfoDataBase db, ResolveListener listener) { + type = db.resolveType(this, type, listener, "resolving type of global"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicIndexableFieldIdentifier.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicIndexableFieldIdentifier.java new file mode 100644 index 00000000000..c4ab5b47bba --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicIndexableFieldIdentifier.java @@ -0,0 +1,41 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicIndexableFieldIdentifier implements IndexableFieldIdentifier { + private Type type; + private int index; + + public BasicIndexableFieldIdentifier(Type type, int index) { + this.type = type; + this.index = index; + } + + public Type getType() { return type; } + public int getIndex() { return index; } + public String toString() { return Integer.toString(getIndex()); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicIntType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicIntType.java new file mode 100644 index 00000000000..f761809779c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicIntType.java @@ -0,0 +1,58 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicIntType extends BasicType implements IntType { + private boolean unsigned; + + public BasicIntType(String name, int size, boolean unsigned) { + this(name, size, unsigned, 0); + } + + protected BasicIntType(String name, int size, boolean unsigned, int cvAttributes) { + super(name, size, cvAttributes); + this.unsigned = unsigned; + } + + public IntType asInt() { return this; } + + public int getIntSize() { return getSize(); } + public boolean isUnsigned() { return unsigned; } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + v.doInt(f, a.getCIntegerAt(0, getSize(), isUnsigned())); + } + + protected Type createCVVariant(int cvAttributes) { + return new BasicIntType(getName(), getSize(), isUnsigned(), cvAttributes); + } + + public void visit(TypeVisitor v) { + v.doIntType(this); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicLineNumberInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicLineNumberInfo.java new file mode 100644 index 00000000000..4bb449da286 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicLineNumberInfo.java @@ -0,0 +1,60 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +/** Describes line number information for a given range of program + counters. */ + +public class BasicLineNumberInfo implements LineNumberInfo { + private String sourceFileName; + private int lineNo; + private Address startPC; + private Address endPC; + + public BasicLineNumberInfo(String sourceFileName, + int lineNo, + Address startPC, + Address endPC) { + this.sourceFileName = sourceFileName; + this.lineNo = lineNo; + this.startPC = startPC; + this.endPC = endPC; + } + + /** Not specified whether this is an absolute or relative path. */ + public String getSourceFileName() { return sourceFileName; } + public int getLineNumber() { return lineNo; } + public Address getStartPC() { return startPC; } + /** FIXME: specify whether this is inclusive or exclusive (currently + when BasicLineNumberMapping.recomputeEndPCs() is called, this is + exclusive) */ + public Address getEndPC() { return endPC; } + + /** For recomputing end PCs if they are not available in the debug info */ + public void setEndPC(Address pc) { endPC = pc; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicLineNumberMapping.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicLineNumberMapping.java new file mode 100644 index 00000000000..094864561f0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicLineNumberMapping.java @@ -0,0 +1,137 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.utilities.AddressOps; + +public class BasicLineNumberMapping { + private List infoList; + + public BasicLineNumberMapping() { + } + + /** Add line number information for the given PC. The end PC may be + a very loose approximation (i.e., the end of the given DLL) if + that information is not available in the debug information. + recomputeEndPCs() will recompute them if needed. */ + public void addLineNumberInfo(BasicLineNumberInfo info) { + if (infoList == null) { + infoList = new ArrayList(); + } + infoList.add(info); + } + + /** Sort the line number information by increasing starting program + counter. This must be done before any queries are made. */ + public void sort() { + if (infoList == null) return; + Collections.sort(infoList, new Comparator() { + public int compare(Object o1, Object o2) { + BasicLineNumberInfo l1 = (BasicLineNumberInfo) o1; + BasicLineNumberInfo l2 = (BasicLineNumberInfo) o2; + Address a1 = l1.getStartPC(); + Address a2 = l2.getStartPC(); + if (AddressOps.lt(a1, a2)) { return -1; } + if (AddressOps.gt(a1, a2)) { return 1; } + return 0; + } + }); + } + + /** Recomputes the ending PCs of each interval based on the starting + PC of the next one. If this needs to be called, must be called + after sort(). */ + public void recomputeEndPCs() { + if (infoList == null) return; + for (int i = 0; i < infoList.size() - 1; i++) { + BasicLineNumberInfo i1 = get(i); + BasicLineNumberInfo i2 = get(i + 1); + i1.setEndPC(i2.getStartPC()); + } + } + + public BasicLineNumberInfo lineNumberForPC(Address pc) throws DebuggerException { + if (infoList == null) return null; + return searchLineNumbers(pc, 0, infoList.size() - 1); + } + + public void iterate(LineNumberVisitor v) { + if (infoList == null) return; + for (int i = 0; i < infoList.size(); i++) { + v.doLineNumber(get(i)); + } + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private BasicLineNumberInfo get(int i) { + return (BasicLineNumberInfo) infoList.get(i); + } + + private BasicLineNumberInfo searchLineNumbers(Address addr, int lowIdx, int highIdx) { + if (highIdx < lowIdx) return null; + if (lowIdx == highIdx) { + // Base case: see whether start PC is less than or equal to addr + if (check(addr, lowIdx)) { + return get(lowIdx); + } else { + return null; + } + } else if (lowIdx == highIdx - 1) { + if (check(addr, lowIdx)) { + return get(lowIdx); + } else if (check(addr, highIdx)) { + return get(highIdx); + } else { + return null; + } + } + int midIdx = (lowIdx + highIdx) >> 1; + BasicLineNumberInfo info = get(midIdx); + if (AddressOps.lt(addr, info.getStartPC())) { + // Always move search down + return searchLineNumbers(addr, lowIdx, midIdx); + } else if (AddressOps.equal(addr, info.getStartPC())) { + return info; + } else { + // Move search up + return searchLineNumbers(addr, midIdx, highIdx); + } + } + + private boolean check(Address addr, int idx) { + BasicLineNumberInfo info = get(idx); + if (AddressOps.lte(info.getStartPC(), addr)) { + return true; + } else { + return false; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicLocalSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicLocalSym.java new file mode 100644 index 00000000000..63d8e3f499f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicLocalSym.java @@ -0,0 +1,47 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicLocalSym extends BasicSym implements LocalSym { + private Type type; + private long frameOffset; + + public BasicLocalSym(String name, Type type, long frameOffset) { + super(name); + this.type = type; + this.frameOffset = frameOffset; + } + + public LocalSym asLocal() { return this; } + + public Type getType() { return type; } + public long getFrameOffset() { return frameOffset; } + + public void resolve(BasicCDebugInfoDataBase db, ResolveListener listener) { + type = db.resolveType(this, type, listener, "resolving type of local"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicMemberFunctionType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicMemberFunctionType.java new file mode 100644 index 00000000000..c927d9221f8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicMemberFunctionType.java @@ -0,0 +1,90 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicMemberFunctionType extends BasicFunctionType implements MemberFunctionType { + private Type containingClass; + private Type thisType; + private long thisAdjust; + + public BasicMemberFunctionType(String name, + int size, + Type returnType, + Type containingClass, + Type thisType, + long thisAdjust) { + this(name, size, returnType, containingClass, thisType, thisAdjust, 0); + } + + private BasicMemberFunctionType(String name, + int size, + Type returnType, + Type containingClass, + Type thisType, + long thisAdjust, + int cvAttributes) { + super(name, size, returnType, cvAttributes); + this.containingClass = containingClass; + this.thisType = thisType; + this.thisAdjust = thisAdjust; + } + + public MemberFunctionType asMemberFunction() { return this; } + + public Type getContainingClass() { return containingClass; } + public Type getThisType() { return thisType; } + public long getThisAdjust() { return thisAdjust; } + + Type resolveTypes(BasicCDebugInfoDataBase db, ResolveListener listener) { + super.resolveTypes(db, listener); + containingClass = db.resolveType(this, containingClass, listener, "resolving member function class"); + thisType = db.resolveType(this, thisType, listener, "resolving member function \"this\" type"); + return this; + } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + // FIXME: nothing to do here? Are we going to provide iteration + // mechanisms through member functions, and if so what are the + // types of those functions going to be? + } + + protected Type createCVVariant(int cvAttributes) { + return new BasicMemberFunctionType(getName(), + getSize(), + getReturnType(), + getContainingClass(), + getThisType(), + getThisAdjust(), + cvAttributes); + } + + public void visit(TypeVisitor v) { + v.doMemberFunctionType(this); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicNamedFieldIdentifier.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicNamedFieldIdentifier.java new file mode 100644 index 00000000000..6765cbdf1e6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicNamedFieldIdentifier.java @@ -0,0 +1,39 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicNamedFieldIdentifier implements NamedFieldIdentifier { + private Field field; + + public BasicNamedFieldIdentifier(Field field) { + this.field = field; + } + + public String getName() { return field.getName(); } + public Type getType() { return field.getType(); } + public String toString() { return getName(); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicPointerType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicPointerType.java new file mode 100644 index 00000000000..8ab3bf03ff5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicPointerType.java @@ -0,0 +1,71 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicPointerType extends BasicType implements PointerType { + private Type targetType; + + public BasicPointerType(int size, Type targetType) { + this(null, size, targetType, 0); + } + + private BasicPointerType(String name, int size, Type targetType, int cvAttributes) { + super(name, size, cvAttributes); + this.targetType = targetType; + if (!((BasicType) targetType).isLazy()) { + computeName(); + } + } + + public PointerType asPointer() { return this; } + + public Type getTargetType() { return targetType; } + + Type resolveTypes(BasicCDebugInfoDataBase db, ResolveListener listener) { + super.resolveTypes(db, listener); + targetType = db.resolveType(this, targetType, listener, "resolving pointer type"); + computeName(); + return this; + } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + v.doPointer(f, a.getAddressAt(0)); + } + + protected Type createCVVariant(int cvAttributes) { + return new BasicPointerType(getName(), getSize(), getTargetType(), cvAttributes); + } + + public void visit(TypeVisitor v) { + v.doPointerType(this); + } + + private void computeName() { + setName(targetType.getName() + " *"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicRefType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicRefType.java new file mode 100644 index 00000000000..f8e2d99ebcd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicRefType.java @@ -0,0 +1,71 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicRefType extends BasicType implements RefType { + private Type targetType; + + public BasicRefType(String name, int size, Type targetType) { + this(name, size, targetType, 0); + } + + private BasicRefType(String name, int size, Type targetType, int cvAttributes) { + super(name, size, cvAttributes); + this.targetType = targetType; + if (!((BasicType) targetType).isLazy()) { + computeName(); + } + } + + public RefType asRef() { return this; } + + public Type getTargetType() { return targetType; } + + Type resolveTypes(BasicCDebugInfoDataBase db, ResolveListener listener) { + super.resolveTypes(db, listener); + targetType = db.resolveType(this, targetType, listener, "resolving ref type"); + computeName(); + return this; + } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) { + v.doRef(f, a.getAddressAt(0)); + } + + protected Type createCVVariant(int cvAttributes) { + return new BasicRefType(getName(), getSize(), getTargetType(), cvAttributes); + } + + public void visit(TypeVisitor v) { + v.doRefType(this); + } + + private void computeName() { + setName(targetType.getName() + " &"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicSym.java new file mode 100644 index 00000000000..4a1bd81f56c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicSym.java @@ -0,0 +1,53 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.cdbg.*; + +public abstract class BasicSym implements Sym { + private String name; + + protected BasicSym(String name) { + this.name = name; + } + + public String getName() { return name; } + public String toString() { return getName(); } + + public BlockSym asBlock() { return null; } + public FunctionSym asFunction() { return null; } + public GlobalSym asGlobal() { return null; } + public LocalSym asLocal() { return null; } + + public boolean isBlock() { return (asBlock() != null); } + public boolean isFunction() { return (asFunction() != null); } + public boolean isGlobal() { return (asGlobal() != null); } + public boolean isLocal() { return (asLocal() != null); } + + public boolean isLazy() { return false; } + + /** Resolve type and symbol references in this symbol */ + public abstract void resolve(BasicCDebugInfoDataBase db, ResolveListener listener); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicType.java new file mode 100644 index 00000000000..cd104fd7437 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicType.java @@ -0,0 +1,128 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public abstract class BasicType implements Type, CVAttributes { + private String name; + private int size; + private int cvAttributes; + // Types keep a list of const/volatile qualified variants of themselves + private List cvVariants; + + protected BasicType(String name, int size) { + this(name, size, 0); + } + + protected BasicType(String name, int size, int cvAttributes) { + this.name = name; + this.size = size; + this.cvAttributes = cvAttributes; + } + + public String getName() { return name; } + + /** For use during resolution only */ + protected void setName(String name) { this.name = name; } + + public int getSize() { return size; } + + public BitType asBit() { return null; } + public IntType asInt() { return null; } + public EnumType asEnum() { return null; } + public FloatType asFloat() { return null; } + public DoubleType asDouble() { return null; } + public PointerType asPointer() { return null; } + public ArrayType asArray() { return null; } + public RefType asRef() { return null; } + public CompoundType asCompound() { return null; } + public FunctionType asFunction() { return null; } + public MemberFunctionType asMemberFunction() { return null; } + public VoidType asVoid() { return null; } + + public boolean isBit() { return (asBit() != null); } + public boolean isInt() { return (asInt() != null); } + public boolean isEnum() { return (asEnum() != null); } + public boolean isFloat() { return (asFloat() != null); } + public boolean isDouble() { return (asDouble() != null); } + public boolean isPointer() { return (asPointer() != null); } + public boolean isArray() { return (asArray() != null); } + public boolean isRef() { return (asRef() != null); } + public boolean isCompound() { return (asCompound() != null); } + public boolean isFunction() { return (asFunction() != null); } + public boolean isMemberFunction() { return (asMemberFunction() != null); } + public boolean isVoid() { return (asVoid() != null); } + + public boolean isConst() { return ((cvAttributes & CONST) != 0); } + public boolean isVolatile() { return ((cvAttributes & VOLATILE) != 0); } + + Type resolveTypes(BasicCDebugInfoDataBase db, ResolveListener listener) { + if (cvVariants != null) { + for (ListIterator iter = cvVariants.listIterator(); iter.hasNext(); ) { + iter.set(db.resolveType(this, (BasicType) iter.next(), listener, "resolving const/var variants")); + } + } + return this; + } + public boolean isLazy() { return false; } + public void iterateObject(Address a, ObjectVisitor v) { + iterateObject(a, v, null); + } + public abstract void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f); + public Type getCVVariant(int cvAttributes) { + Type t = findCVVariant(cvAttributes); + if (t != null) return t; + t = createCVVariant(cvAttributes); + addCVVariant(t); + return t; + } + + public String toString() { + return getName(); + } + + private int getCVAttributes() { return cvAttributes; } + protected abstract Type createCVVariant(int cvAttributes); + protected Type findCVVariant(int cvAttributes) { + if (cvVariants != null) { + for (Iterator iter = cvVariants.iterator(); iter.hasNext(); ) { + BasicType t = (BasicType) iter.next(); + if (t.getCVAttributes() == cvAttributes) return t; + } + } + return null; + } + protected void addCVVariant(Type t) { + if (cvVariants == null) { + cvVariants = new ArrayList(); + } + cvVariants.add(t); + } + + public abstract void visit(TypeVisitor v); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicVoidType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicVoidType.java new file mode 100644 index 00000000000..bab95b6953b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/BasicVoidType.java @@ -0,0 +1,49 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class BasicVoidType extends BasicType implements VoidType { + public BasicVoidType() { + super("void", 0); + } + + public VoidType asVoid() { return this; } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) {} + + protected Type createCVVariant(int cvAttributes) { + // FIXME + System.err.println("WARNING: Should not attempt to create const/volatile variants for void type"); + return this; + // throw new RuntimeException("Should not attempt to create const/volatile variants for void type"); + } + + public void visit(TypeVisitor v) { + v.doVoidType(this); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/CompoundTypeKind.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/CompoundTypeKind.java new file mode 100644 index 00000000000..64da6fe4769 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/CompoundTypeKind.java @@ -0,0 +1,36 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +/** Type-safe enum for discriminating between classes, structs and + unions, which are all represented as compound types */ + +public class CompoundTypeKind { + public static final CompoundTypeKind CLASS = new CompoundTypeKind(); + public static final CompoundTypeKind STRUCT = new CompoundTypeKind(); + public static final CompoundTypeKind UNION = new CompoundTypeKind(); + + private CompoundTypeKind() {} +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/LazyBlockSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/LazyBlockSym.java new file mode 100644 index 00000000000..eb90a054253 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/LazyBlockSym.java @@ -0,0 +1,50 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +public class LazyBlockSym extends BasicSym implements BlockSym { + private Object key; + + public LazyBlockSym(Object key) { + super(null); + this.key = key; + } + + public BlockSym asBlock() { return this; } + public boolean isLazy() { return true; } + + public Object getKey() { return key; } + + public BlockSym getParent() { return null; } + public long getLength() { return 0; } + public Address getAddress() { return null; } + public int getNumLocals() { return 0; } + public LocalSym getLocal(int i) { throw new RuntimeException("Should not call this"); } + + public void resolve(BasicCDebugInfoDataBase db, ResolveListener listener) {} +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/LazyType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/LazyType.java new file mode 100644 index 00000000000..d3ae961374e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/LazyType.java @@ -0,0 +1,69 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.utilities.Assert; + +public class LazyType extends BasicType { + private Object key; + private int cvAttributes; + + public LazyType(Object key) { + this(key, 0); + } + + private LazyType(Object key, int cvAttributes) { + super(null, 0, cvAttributes); + if (Assert.ASSERTS_ENABLED) { + Assert.that(key != null, "key must not be null"); + } + this.key = key; + this.cvAttributes = cvAttributes; + } + + public boolean isLazy() { return true; } + public Object getKey() { return key; } + + Type resolveTypes(BasicCDebugInfoDataBase db, ResolveListener listener) { + BasicType t = (BasicType) db.resolveType(this, this, listener, "resolving lazy type"); + // Returned type might be lazy if there was an error + if (t.isLazy()) { + return this; + } + if (cvAttributes != 0) { + return t.getCVVariant(cvAttributes); + } + return t; + } + + public void iterateObject(Address a, ObjectVisitor v, FieldIdentifier f) {} + protected Type createCVVariant(int cvAttributes) { + return new LazyType(key, cvAttributes); + } + + public void visit(TypeVisitor v) {} +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/ResolveListener.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/ResolveListener.java new file mode 100644 index 00000000000..4fd94c314a4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/ResolveListener.java @@ -0,0 +1,45 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic; + +import sun.jvm.hotspot.debugger.cdbg.*; + +/** Provides notification about failed resolutions in the debug info + database without causing the entire resolve operation to fail */ +public interface ResolveListener { + /** Indicates failure to resolve a type within another type */ + public void resolveFailed(Type containingType, LazyType failedResolve, String detail); + + /** Indicates failure to resolve the address of a static field in a + type */ + public void resolveFailed(Type containingType, String staticFieldName); + + /** Indicates failure to resolve reference to a type from a symbol */ + public void resolveFailed(Sym containingSymbol, LazyType failedResolve, String detail); + + /** Indicates failure to resolve reference from one symbol to + another (currently occurs only from BlockSyms to other BlockSyms) */ + public void resolveFailed(Sym containingSymbol, LazyBlockSym failedResolve, String detail); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/amd64/AMD64CFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/amd64/AMD64CFrame.java new file mode 100644 index 00000000000..35cfec5ec74 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/amd64/AMD64CFrame.java @@ -0,0 +1,69 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.*; + +/** Basic AMD64 frame functionality providing sender() functionality. */ + +public class AMD64CFrame extends BasicCFrame { + private Address rbp; + private Address pc; + + private static final int ADDRESS_SIZE = 8; + + /** Constructor for topmost frame */ + public AMD64CFrame(CDebugger dbg, Address rbp, Address pc) { + super(dbg); + this.rbp = rbp; + this.pc = pc; + } + + public CFrame sender() { + if (rbp == null) { + return null; + } + + Address nextRBP = rbp.getAddressAt( 0 * ADDRESS_SIZE); + if (nextRBP == null) { + return null; + } + Address nextPC = rbp.getAddressAt( 1 * ADDRESS_SIZE); + if (nextPC == null) { + return null; + } + return new AMD64CFrame(dbg(), nextRBP, nextPC); + } + + public Address pc() { + return pc; + } + + public Address localVariableBase() { + return rbp; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/x86/X86CFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/x86/X86CFrame.java new file mode 100644 index 00000000000..f0370fb679c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/cdbg/basic/x86/X86CFrame.java @@ -0,0 +1,69 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.cdbg.basic.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.*; + +/** Basic X86 frame functionality providing sender() functionality. */ + +public class X86CFrame extends BasicCFrame { + private Address ebp; + private Address pc; + + private static final int ADDRESS_SIZE = 4; + + /** Constructor for topmost frame */ + public X86CFrame(CDebugger dbg, Address ebp, Address pc) { + super(dbg); + this.ebp = ebp; + this.pc = pc; + } + + public CFrame sender() { + if (ebp == null) { + return null; + } + + Address nextEBP = ebp.getAddressAt( 0 * ADDRESS_SIZE); + if (nextEBP == null) { + return null; + } + Address nextPC = ebp.getAddressAt( 1 * ADDRESS_SIZE); + if (nextPC == null) { + return null; + } + return new X86CFrame(dbg(), nextEBP, nextPC); + } + + public Address pc() { + return pc; + } + + public Address localVariableBase() { + return ebp; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxAddress.java new file mode 100644 index 00000000000..374dabf5794 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxAddress.java @@ -0,0 +1,387 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx; + +import sun.jvm.hotspot.debugger.*; + +class DbxAddress implements Address { + protected DbxDebugger debugger; + protected long addr; + + DbxAddress(DbxDebugger debugger, long addr) { + this.debugger = debugger; + this.addr = addr; + } + + // + // Basic Java routines + // + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof DbxAddress)) { + return false; + } + + return (addr == ((DbxAddress) arg).addr); + } + + public int hashCode() { + // FIXME: suggestions on a better hash code? + return (int) addr; + } + + public String toString() { + return debugger.addressValueToString(addr); + } + + // + // C/C++-related routines + // + + public long getCIntegerAt(long offset, long numBytes, boolean isUnsigned) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readCInteger(addr + offset, numBytes, isUnsigned); + } + + public Address getAddressAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readAddress(addr + offset); + } + + // + // Java-related routines + // + + public boolean getJBooleanAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJBoolean(addr + offset); + } + + public byte getJByteAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJByte(addr + offset); + } + + public char getJCharAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJChar(addr + offset); + } + + public double getJDoubleAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJDouble(addr + offset); + } + + public float getJFloatAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJFloat(addr + offset); + } + + public int getJIntAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJInt(addr + offset); + } + + public long getJLongAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJLong(addr + offset); + } + + public short getJShortAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJShort(addr + offset); + } + + public OopHandle getOopHandleAt(long offset) + throws UnalignedAddressException, UnmappedAddressException, NotInHeapException { + return debugger.readOopHandle(addr + offset); + } + + // Mutators -- not implemented for now (FIXME) + public void setCIntegerAt(long offset, long numBytes, long value) { + throw new DebuggerException("Unimplemented"); + } + public void setAddressAt(long offset, Address value) { + throw new DebuggerException("Unimplemented"); + } + public void setJBooleanAt (long offset, boolean value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJByteAt (long offset, byte value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJCharAt (long offset, char value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJDoubleAt (long offset, double value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJFloatAt (long offset, float value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJIntAt (long offset, int value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJLongAt (long offset, long value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJShortAt (long offset, short value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setOopHandleAt (long offset, OopHandle value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + + // + // Arithmetic operations -- necessary evil. + // + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new DbxAddress(debugger, value); + } + + public OopHandle addOffsetToAsOopHandle(long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new DbxOopHandle(debugger, value); + } + + /** (FIXME: any signed/unsigned issues? Should this work for + OopHandles?) */ + public long minus(Address arg) { + if (arg == null) { + return addr; + } + return addr - ((DbxAddress) arg).addr; + } + + // Two's complement representation. + // All negative numbers are larger than positive numbers. + // Numbers with the same sign can be compared normally. + // Test harness is below in main(). + + public boolean lessThan (Address arg) { + if (arg == null) { + return false; + } + DbxAddress dbxArg = (DbxAddress) arg; + if ((addr >= 0) && (dbxArg.addr < 0)) { + return true; + } + if ((addr < 0) && (dbxArg.addr >= 0)) { + return false; + } + return (addr < dbxArg.addr); + } + + public boolean lessThanOrEqual (Address arg) { + if (arg == null) { + return false; + } + DbxAddress dbxArg = (DbxAddress) arg; + if ((addr >= 0) && (dbxArg.addr < 0)) { + return true; + } + if ((addr < 0) && (dbxArg.addr >= 0)) { + return false; + } + return (addr <= dbxArg.addr); + } + + public boolean greaterThan (Address arg) { + if (arg == null) { + return true; + } + DbxAddress dbxArg = (DbxAddress) arg; + if ((addr >= 0) && (dbxArg.addr < 0)) { + return false; + } + if ((addr < 0) && (dbxArg.addr >= 0)) { + return true; + } + return (addr > dbxArg.addr); + } + + public boolean greaterThanOrEqual(Address arg) { + if (arg == null) { + return true; + } + DbxAddress dbxArg = (DbxAddress) arg; + if ((addr >= 0) && (dbxArg.addr < 0)) { + return false; + } + if ((addr < 0) && (dbxArg.addr >= 0)) { + return true; + } + return (addr >= dbxArg.addr); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + long value = addr & mask; + if (value == 0) { + return null; + } + return new DbxAddress(debugger, value); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + long value = addr | mask; + if (value == 0) { + return null; + } + return new DbxAddress(debugger, value); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + long value = addr ^ mask; + if (value == 0) { + return null; + } + return new DbxAddress(debugger, value); + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + long getValue() { + return addr; + } + + + private static void check(boolean arg, String failMessage) { + if (!arg) { + System.err.println(failMessage + ": FAILED"); + System.exit(1); + } + } + + // Test harness + public static void main(String[] args) { + // p/n indicates whether the interior address is really positive + // or negative. In unsigned terms, p1 < p2 < n1 < n2. + + DbxAddress p1 = new DbxAddress(null, 0x7FFFFFFFFFFFFFF0L); + DbxAddress p2 = (DbxAddress) p1.addOffsetTo(10); + DbxAddress n1 = (DbxAddress) p2.addOffsetTo(10); + DbxAddress n2 = (DbxAddress) n1.addOffsetTo(10); + + // lessThan positive tests + check(p1.lessThan(p2), "lessThan 1"); + check(p1.lessThan(n1), "lessThan 2"); + check(p1.lessThan(n2), "lessThan 3"); + check(p2.lessThan(n1), "lessThan 4"); + check(p2.lessThan(n2), "lessThan 5"); + check(n1.lessThan(n2), "lessThan 6"); + + // lessThan negative tests + check(!p1.lessThan(p1), "lessThan 7"); + check(!p2.lessThan(p2), "lessThan 8"); + check(!n1.lessThan(n1), "lessThan 9"); + check(!n2.lessThan(n2), "lessThan 10"); + + check(!p2.lessThan(p1), "lessThan 11"); + check(!n1.lessThan(p1), "lessThan 12"); + check(!n2.lessThan(p1), "lessThan 13"); + check(!n1.lessThan(p2), "lessThan 14"); + check(!n2.lessThan(p2), "lessThan 15"); + check(!n2.lessThan(n1), "lessThan 16"); + + // lessThanOrEqual positive tests + check(p1.lessThanOrEqual(p1), "lessThanOrEqual 1"); + check(p2.lessThanOrEqual(p2), "lessThanOrEqual 2"); + check(n1.lessThanOrEqual(n1), "lessThanOrEqual 3"); + check(n2.lessThanOrEqual(n2), "lessThanOrEqual 4"); + + check(p1.lessThanOrEqual(p2), "lessThanOrEqual 5"); + check(p1.lessThanOrEqual(n1), "lessThanOrEqual 6"); + check(p1.lessThanOrEqual(n2), "lessThanOrEqual 7"); + check(p2.lessThanOrEqual(n1), "lessThanOrEqual 8"); + check(p2.lessThanOrEqual(n2), "lessThanOrEqual 9"); + check(n1.lessThanOrEqual(n2), "lessThanOrEqual 10"); + + // lessThanOrEqual negative tests + check(!p2.lessThanOrEqual(p1), "lessThanOrEqual 11"); + check(!n1.lessThanOrEqual(p1), "lessThanOrEqual 12"); + check(!n2.lessThanOrEqual(p1), "lessThanOrEqual 13"); + check(!n1.lessThanOrEqual(p2), "lessThanOrEqual 14"); + check(!n2.lessThanOrEqual(p2), "lessThanOrEqual 15"); + check(!n2.lessThanOrEqual(n1), "lessThanOrEqual 16"); + + // greaterThan positive tests + check(n2.greaterThan(p1), "greaterThan 1"); + check(n2.greaterThan(p2), "greaterThan 2"); + check(n2.greaterThan(n1), "greaterThan 3"); + check(n1.greaterThan(p1), "greaterThan 4"); + check(n1.greaterThan(p2), "greaterThan 5"); + check(p2.greaterThan(p1), "greaterThan 6"); + + // greaterThan negative tests + check(!p1.greaterThan(p1), "greaterThan 7"); + check(!p2.greaterThan(p2), "greaterThan 8"); + check(!n1.greaterThan(n1), "greaterThan 9"); + check(!n2.greaterThan(n2), "greaterThan 10"); + + check(!p1.greaterThan(n2), "greaterThan 11"); + check(!p2.greaterThan(n2), "greaterThan 12"); + check(!n1.greaterThan(n2), "greaterThan 13"); + check(!p1.greaterThan(n1), "greaterThan 14"); + check(!p2.greaterThan(n1), "greaterThan 15"); + check(!p1.greaterThan(p2), "greaterThan 16"); + + // greaterThanOrEqual positive tests + check(p1.greaterThanOrEqual(p1), "greaterThanOrEqual 1"); + check(p2.greaterThanOrEqual(p2), "greaterThanOrEqual 2"); + check(n1.greaterThanOrEqual(n1), "greaterThanOrEqual 3"); + check(n2.greaterThanOrEqual(n2), "greaterThanOrEqual 4"); + + check(n2.greaterThanOrEqual(p1), "greaterThanOrEqual 5"); + check(n2.greaterThanOrEqual(p2), "greaterThanOrEqual 6"); + check(n2.greaterThanOrEqual(n1), "greaterThanOrEqual 7"); + check(n1.greaterThanOrEqual(p1), "greaterThanOrEqual 8"); + check(n1.greaterThanOrEqual(p2), "greaterThanOrEqual 9"); + check(p2.greaterThanOrEqual(p1), "greaterThanOrEqual 10"); + + // greaterThanOrEqual negative tests + check(!p1.greaterThanOrEqual(n2), "greaterThanOrEqual 11"); + check(!p2.greaterThanOrEqual(n2), "greaterThanOrEqual 12"); + check(!n1.greaterThanOrEqual(n2), "greaterThanOrEqual 13"); + check(!p1.greaterThanOrEqual(n1), "greaterThanOrEqual 14"); + check(!p2.greaterThanOrEqual(n1), "greaterThanOrEqual 15"); + check(!p1.greaterThanOrEqual(p2), "greaterThanOrEqual 16"); + + System.err.println("DbxAddress: all tests passed successfully."); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxDebugger.java new file mode 100644 index 00000000000..5e5c341c7a2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxDebugger.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx; + +import sun.jvm.hotspot.debugger.*; + +/** An extension of the JVMDebugger interface with a few additions to + support 32-bit vs. 64-bit debugging as well as features required + by the architecture-specific subpackages. */ + +public interface DbxDebugger extends JVMDebugger { + public String addressValueToString(long address) throws DebuggerException; + public boolean readJBoolean(long address) throws DebuggerException; + public byte readJByte(long address) throws DebuggerException; + public char readJChar(long address) throws DebuggerException; + public double readJDouble(long address) throws DebuggerException; + public float readJFloat(long address) throws DebuggerException; + public int readJInt(long address) throws DebuggerException; + public long readJLong(long address) throws DebuggerException; + public short readJShort(long address) throws DebuggerException; + public long readCInteger(long address, long numBytes, boolean isUnsigned) + throws DebuggerException; + public DbxAddress readAddress(long address) throws DebuggerException; + public DbxOopHandle readOopHandle(long address) throws DebuggerException; + public long[] getThreadIntegerRegisterSet(int tid) throws DebuggerException; + public Address newAddress(long value) throws DebuggerException; + + // NOTE: this interface implicitly contains the following methods: + // From the Debugger interface via JVMDebugger + // public void attach(int processID) throws DebuggerException; + // public void attach(String executableName, String coreFileName) throws DebuggerException; + // public boolean detach(); + // public Address parseAddress(String addressString) throws NumberFormatException; + // public long getAddressValue(Address addr) throws DebuggerException; + // public String getOS(); + // public String getCPU(); + // From the SymbolLookup interface via Debugger and JVMDebugger + // public Address lookup(String objectName, String symbol); + // public OopHandle lookupOop(String objectName, String symbol); + // From the JVMDebugger interface + // public void configureJavaPrimitiveTypeSizes(long jbooleanSize, + // long jbyteSize, + // long jcharSize, + // long jdoubleSize, + // long jfloatSize, + // long jintSize, + // long jlongSize, + // long jshortSize); + // From the ThreadAccess interface via Debugger and JVMDebugger + // public ThreadProxy getThreadForIdentifierAddress(Address addr); + // public ThreadProxy getThreadForThreadId(long id); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxDebuggerLocal.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxDebuggerLocal.java new file mode 100644 index 00000000000..ef99707d567 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxDebuggerLocal.java @@ -0,0 +1,733 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx; + +import java.io.*; +import java.net.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.dbx.sparc.*; +import sun.jvm.hotspot.debugger.dbx.x86.*; +import sun.jvm.hotspot.debugger.cdbg.CDebugger; +import sun.jvm.hotspot.utilities.*; + +/**

An implementation of the JVMDebugger interface which sits on + top of dbx and relies on the SA's dbx import module for + communication with the debugger.

+ +

NOTE that since we have the notion of fetching "Java + primitive types" from the remote process (which might have + different sizes than we expect) we have a bootstrapping + problem. We need to know the sizes of these types before we can + fetch them. The current implementation solves this problem by + requiring that it be configured with these type sizes before they + can be fetched. The readJ(Type) routines here will throw a + RuntimeException if they are called before the debugger is + configured with the Java primitive type sizes.

+*/ + +public class DbxDebuggerLocal extends DebuggerBase implements DbxDebugger { + // These may be set by DbxDebuggerRemote + protected boolean unalignedAccessesOkay; + protected DbxThreadFactory threadFactory; + + private String dbxPathName; + private String[] dbxSvcAgentDSOPathNames; + private Process dbxProcess; + private StreamMonitor dbxOutStreamMonitor; + private StreamMonitor dbxErrStreamMonitor; + private PrintWriter dbxOstr; + private PrintWriter out; + private InputLexer in; + private Socket importModuleSocket; + private static final int PORT = 21928; + private static final int LONG_TIMEOUT = 60000; + private static final int DBX_MODULE_NOT_FOUND = 101; + private static final int DBX_MODULE_LOADED = 102; + + //-------------------------------------------------------------------------------- + // Implementation of Debugger interface + // + + /**

machDesc may be null if it couldn't be determined yet; i.e., + if we're on SPARC, we need to ask the remote process whether + we're in 32- or 64-bit mode.

+ +

useCache should be set to true if debugging is being done + locally, and to false if the debugger is being created for the + purpose of supporting remote debugging.

*/ + public DbxDebuggerLocal(MachineDescription machDesc, + String dbxPathName, + String[] dbxSvcAgentDSOPathNames, + boolean useCache) { + this.machDesc = machDesc; + this.dbxPathName = dbxPathName; + this.dbxSvcAgentDSOPathNames = dbxSvcAgentDSOPathNames; + int cacheNumPages; + int cachePageSize; + if (PlatformInfo.getCPU().equals("sparc")) { + cacheNumPages = parseCacheNumPagesProperty(2048); + cachePageSize = 8192; + threadFactory = new DbxSPARCThreadFactory(this); + } else if (PlatformInfo.getCPU().equals("x86")) { + cacheNumPages = 4096; + cachePageSize = 4096; + threadFactory = new DbxX86ThreadFactory(this); + unalignedAccessesOkay = true; + } else { + throw new RuntimeException("Thread access for CPU architecture " + PlatformInfo.getCPU() + " not yet supported"); + } + if (useCache) { + // Cache portion of the remote process's address space. + // Fetching data over the socket connection to dbx is relatively + // slow. For now, this cache works best if it covers the entire + // heap of the remote process. FIXME: at least should make this + // tunable from the outside, i.e., via the UI. This is a 16 MB + // cache divided on SPARC into 2048 8K pages and on x86 into + // 4096 4K pages; the page size must be adjusted to be the OS's + // page size. (FIXME: should pick this up from the debugger.) + initCache(cachePageSize, cacheNumPages); + } + } + + /** Only called by DbxDebuggerRemote */ + protected DbxDebuggerLocal() { + } + + /** FIXME: implement this with a Runtime.exec() of ps followed by + parsing of its output */ + public boolean hasProcessList() throws DebuggerException { + return false; + } + + public List getProcessList() throws DebuggerException { + throw new DebuggerException("Not yet supported"); + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized void attach(int processID) throws DebuggerException { + try { + launchProcess(); + dbxErrStreamMonitor.addTrigger("dbx: no process", 1); + dbxErrStreamMonitor.addTrigger("dbx: Cannot open", 1); + dbxErrStreamMonitor.addTrigger("dbx: Cannot find", DBX_MODULE_NOT_FOUND); + dbxOstr = new PrintWriter(dbxProcess.getOutputStream(), true); + dbxOstr.println("debug - " + processID); + dbxOstr.println("kprint -u2 \\(ready\\)"); + boolean seen = dbxErrStreamMonitor.waitFor("(ready)", LONG_TIMEOUT); + if (!seen) { + detach(); + throw new DebuggerException("Timed out while connecting to process " + processID); + } + List retVals = dbxErrStreamMonitor.getTriggersSeen(); + if (retVals.contains(new Integer(1))) { + detach(); + throw new DebuggerException("No such process " + processID); + } + + // Throws DebuggerException upon failure + importDbxModule(); + + dbxOstr.println("svc_agent_run"); + + connectToImportModule(); + + // Set "fail fast" mode on process memory reads + printlnToOutput("peek_fail_fast 1"); + } + catch (IOException e) { + detach(); + throw new DebuggerException("Error while connecting to dbx process", e); + } + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized void attach(String executableName, String coreFileName) throws DebuggerException { + try { + launchProcess(); + // Missing executable + dbxErrStreamMonitor.addTrigger("dbx: Cannot open", 1); + // Missing core file + dbxErrStreamMonitor.addTrigger("dbx: can't read", 2); + // Corrupt executable + dbxErrStreamMonitor.addTrigger("dbx: File", 3); + // Corrupt core file + dbxErrStreamMonitor.addTrigger("dbx: Unable to read", 4); + // Mismatched core and executable + dbxErrStreamMonitor.addTrigger("dbx: core object name", 5); + // Missing loadobject + dbxErrStreamMonitor.addTrigger("dbx: can't stat", 6); + // Successful load of svc module + dbxOstr = new PrintWriter(dbxProcess.getOutputStream(), true); + dbxOstr.println("debug " + executableName + " " + coreFileName); + dbxOstr.println("kprint -u2 \\(ready\\)"); + boolean seen = dbxErrStreamMonitor.waitFor("(ready)", LONG_TIMEOUT); + if (!seen) { + detach(); + throw new DebuggerException("Timed out while attaching to core file"); + } + List retVals = dbxErrStreamMonitor.getTriggersSeen(); + if (retVals.size() > 0) { + detach(); + + if (retVals.contains(new Integer(1))) { + throw new DebuggerException("Can not find executable \"" + executableName + "\""); + } else if (retVals.contains(new Integer(2))) { + throw new DebuggerException("Can not find core file \"" + coreFileName + "\""); + } else if (retVals.contains(new Integer(3))) { + throw new DebuggerException("Corrupt executable \"" + executableName + "\""); + } else if (retVals.contains(new Integer(4))) { + throw new DebuggerException("Corrupt core file \"" + coreFileName + "\""); + } else if (retVals.contains(new Integer(5))) { + throw new DebuggerException("Mismatched core file/executable \"" + coreFileName + "\"/\"" + executableName + "\""); + } else { + throw new DebuggerException("Couldn't find all loaded libraries for executable \"" + executableName + "\""); + } + } + + // Throws DebuggerException upon failure + importDbxModule(); + + dbxOstr.println("svc_agent_run"); + + connectToImportModule(); + + // Set "fail fast" mode on process memory reads + printlnToOutput("peek_fail_fast 1"); + } + catch (IOException e) { + detach(); + throw new DebuggerException("Error while connecting to dbx process", e); + } + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized boolean detach() { + try { + if (dbxProcess == null) { + return false; + } + + if (out != null && dbxOstr != null) { + printlnToOutput("exit"); + dbxOstr.println("exit"); + + // Wait briefly for the process to exit (FIXME: should make this + // nicer) + try { + Thread.sleep(500); + } + catch (InterruptedException e) { + } + } + + shutdown(); + + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + /** From the Debugger interface via JVMDebugger */ + public Address parseAddress(String addressString) throws NumberFormatException { + long addr = utils.scanAddress(addressString); + if (addr == 0) { + return null; + } + return new DbxAddress(this, addr); + } + + /** From the Debugger interface via JVMDebugger */ + public String getOS() { + return PlatformInfo.getOS(); + } + + /** From the Debugger interface via JVMDebugger */ + public String getCPU() { + return PlatformInfo.getCPU(); + } + + public boolean hasConsole() throws DebuggerException { + return true; + } + + public synchronized String consoleExecuteCommand(String cmd) throws DebuggerException { + try { + // A little tricky. We need to cause the dbx import module to + // exit, then print our command on dbx's stdin along with a + // command which will allow our StreamMonitors to + // resynchronize. We need save the output from the StreamMonitors + // along the way. + printlnToOutput("exit"); + importModuleSocket.close(); + importModuleSocket = null; + out = null; + in = null; + dbxOstr.println("kprint \\(ready\\)"); + dbxOstr.flush(); + dbxOutStreamMonitor.waitFor("(ready)", LONG_TIMEOUT); + + dbxOutStreamMonitor.startCapture(); + dbxErrStreamMonitor.startCapture(); + dbxOstr.println(cmd); + dbxOstr.println("kprint \\(ready\\)"); + dbxOutStreamMonitor.waitFor("(ready)", LONG_TIMEOUT); + String result = dbxOutStreamMonitor.stopCapture(); + String result2 = dbxErrStreamMonitor.stopCapture(); + result = result + result2; + // Cut out the "(ready)" string + StringBuffer outBuf = new StringBuffer(result.length()); + BufferedReader reader = new BufferedReader(new StringReader(result)); + // FIXME: bug in BufferedReader? readLine returns null when + // ready() returns true. + String line = null; + do { + line = reader.readLine(); + if ((line != null) && (!line.equals("(ready)"))) { + outBuf.append(line); + outBuf.append("\n"); + } + } while (line != null); + dbxOstr.println("svc_agent_run"); + dbxOstr.flush(); + + connectToImportModule(); + + return outBuf.toString(); + } + catch (IOException e) { + detach(); + throw new DebuggerException("Error while executing command on dbx console", e); + } + } + + public String getConsolePrompt() throws DebuggerException { + return "(dbx) "; + } + + public CDebugger getCDebugger() throws DebuggerException { + return null; + } + + /** From the SymbolLookup interface via Debugger and JVMDebugger */ + public synchronized Address lookup(String objectName, String symbol) { + long addr = lookupInProcess(objectName, symbol); + if (addr == 0) { + return null; + } + return new DbxAddress(this, addr); + } + + /** From the SymbolLookup interface via Debugger and JVMDebugger */ + public synchronized OopHandle lookupOop(String objectName, String symbol) { + long addr = lookupInProcess(objectName, symbol); + if (addr == 0) { + return null; + } + return new DbxOopHandle(this, addr); + } + + /** From the Debugger interface */ + public MachineDescription getMachineDescription() { + return machDesc; + } + + /** Internal routine supporting lazy setting of MachineDescription, + since on SPARC we will need to query the remote process to ask + it what its data model is (32- or 64-bit). NOTE that this is NOT + present in the DbxDebugger interface because it should not be + called across the wire (until we support attaching to multiple + remote processes via RMI -- see the documentation for + DbxDebuggerRemoteIntf.) */ + public void setMachineDescription(MachineDescription machDesc) { + this.machDesc = machDesc; + setBigEndian(machDesc.isBigEndian()); + utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); + } + + /** Internal routine which queries the remote process about its data + model -- i.e., size of addresses. Returns -1 upon error. + Currently supported return values are 32 and 64. NOTE that this + is NOT present in the DbxDebugger interface because it should + not be called across the wire (until we support attaching to + multiple remote processes via RMI -- see the documentation for + DbxDebuggerRemoteIntf.) */ + public int getRemoteProcessAddressSize() { + if (dbxProcess == null) { + throw new RuntimeException("Not attached to remote process"); + } + + try { + printlnToOutput("address_size"); + int i = in.parseInt(); + return i; + } + catch (IOException e) { + return -1; + } + } + + //-------------------------------------------------------------------------------- + // Implementation of ThreadAccess interface + // + + /** From the ThreadAccess interface via Debugger and JVMDebugger */ + public ThreadProxy getThreadForIdentifierAddress(Address addr) { + return threadFactory.createThreadWrapper(addr); + } + + public ThreadProxy getThreadForThreadId(long id) { + return threadFactory.createThreadWrapper(id); + } + + //---------------------------------------------------------------------- + // Overridden from DebuggerBase because we need to relax alignment + // constraints on x86 + + public long readJLong(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + // FIXME: allow this to be configurable. Undesirable to add a + // dependency on the runtime package here, though, since this + // package should be strictly underneath it. + if (unalignedAccessesOkay) { + utils.checkAlignment(address, jintSize); + } else { + utils.checkAlignment(address, jlongSize); + } + byte[] data = readBytes(address, jlongSize); + return utils.dataToJLong(data, jlongSize); + } + + //-------------------------------------------------------------------------------- + // Internal routines (for implementation of DbxAddress). + // These must not be called until the MachineDescription has been set up. + // + + /** From the DbxDebugger interface */ + public String addressValueToString(long address) { + return utils.addressValueToString(address); + } + + /** Need to override this to relax alignment checks on Solaris/x86. */ + public long readCInteger(long address, long numBytes, boolean isUnsigned) + throws UnmappedAddressException, UnalignedAddressException { + checkConfigured(); + if (!unalignedAccessesOkay) { + utils.checkAlignment(address, numBytes); + } else { + // Only slightly relaxed semantics -- this is a hack, but is + // necessary on Solaris/x86 where it seems the compiler is + // putting some global 64-bit data on 32-bit boundaries + if (numBytes == 8) { + utils.checkAlignment(address, 4); + } else { + utils.checkAlignment(address, numBytes); + } + } + byte[] data = readBytes(address, numBytes); + return utils.dataToCInteger(data, isUnsigned); + } + + /** From the DbxDebugger interface */ + public DbxAddress readAddress(long address) + throws UnmappedAddressException, UnalignedAddressException { + long value = readAddressValue(address); + return (value == 0 ? null : new DbxAddress(this, value)); + } + + /** From the DbxDebugger interface */ + public DbxOopHandle readOopHandle(long address) + throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { + long value = readAddressValue(address); + return (value == 0 ? null : new DbxOopHandle(this, value)); + } + + //-------------------------------------------------------------------------------- + // Thread context access. Can not be package private, but should + // only be accessed by the architecture-specific subpackages. + + /** From the DbxDebugger interface. May have to redefine this later. */ + public synchronized long[] getThreadIntegerRegisterSet(int tid) { + try { + printlnToOutput("thr_gregs " + tid); + int num = in.parseInt(); + long[] res = new long[num]; + for (int i = 0; i < num; i++) { + res[i] = in.parseAddress(); + } + return res; + } + catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + //-------------------------------------------------------------------------------- + // Address access. Can not be package private, but should only be + // accessed by the architecture-specific subpackages. + + /** From the Debugger interface */ + public long getAddressValue(Address addr) { + if (addr == null) return 0; + return ((DbxAddress) addr).getValue(); + } + + /** From the DbxDebugger interface */ + public Address newAddress(long value) { + if (value == 0) return null; + return new DbxAddress(this, value); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private void launchProcess() throws IOException { + dbxProcess = Runtime.getRuntime().exec(dbxPathName); + // dbxOutStreamMonitor = new StreamMonitor(dbxProcess.getInputStream()); + // dbxErrStreamMonitor = new StreamMonitor(dbxProcess.getErrorStream()); + dbxOutStreamMonitor = new StreamMonitor(dbxProcess.getInputStream(), "dbx stdout", true); + dbxErrStreamMonitor = new StreamMonitor(dbxProcess.getErrorStream(), "dbx stderr", true); + } + + /** Requires that dbxErrStreamMonitor has a trigger on "dbx: Cannot + find" with number DBX_MODULE_NOT_FOUND as well as one on "dbx: + warning:" (plus the serviceability agent's dbx module path name, + to avoid conflation with inability to load individual object + files) with number DBX_MODULE_FAILED_TO_LOAD. The former + indicates an absence of libsvc_agent_dbx.so, while the latter + indicates that the module failed to load, specifically because + the architecture was mismatched. (I don't see a way to detect + from the dbx command prompt whether it's running the v8 or v9 + executbale, so we try to import both flavors of the import + module; the "v8" file name convention doesn't actually include + the v8 prefix, so this code should work for Intel as well.) */ + private void importDbxModule() throws DebuggerException { + // Trigger for a successful load + dbxOutStreamMonitor.addTrigger("Defining svc_agent_run", DBX_MODULE_LOADED); + for (int i = 0; i < dbxSvcAgentDSOPathNames.length; i++) { + dbxOstr.println("import " + dbxSvcAgentDSOPathNames[i]); + dbxOstr.println("kprint -u2 \\(Ready\\)"); + boolean seen = dbxErrStreamMonitor.waitFor("(Ready)", LONG_TIMEOUT); + if (!seen) { + detach(); + throw new DebuggerException("Timed out while importing dbx module from file\n" + dbxSvcAgentDSOPathNames[i]); + } + List retVals = dbxErrStreamMonitor.getTriggersSeen(); + if (retVals.contains(new Integer(DBX_MODULE_NOT_FOUND))) { + detach(); + throw new DebuggerException("Unable to find the Serviceability Agent's dbx import module at pathname \"" + + dbxSvcAgentDSOPathNames[i] + "\""); + } else { + retVals = dbxOutStreamMonitor.getTriggersSeen(); + if (retVals.contains(new Integer(DBX_MODULE_LOADED))) { + System.out.println("importDbxModule: imported " + dbxSvcAgentDSOPathNames[i]); + return; + } + } + } + + // Failed to load all flavors + detach(); + String errMsg = ("Unable to find a version of the Serviceability Agent's dbx import module\n" + + "matching the architecture of dbx at any of the following locations:"); + for (int i = 0; i < dbxSvcAgentDSOPathNames.length; i++) { + errMsg = errMsg + "\n" + dbxSvcAgentDSOPathNames[i]; + } + throw new DebuggerException(errMsg); + } + + /** Terminate the debugger forcibly */ + private void shutdown() { + + if (dbxProcess != null) { + // See whether the process has exited and, if not, terminate it + // forcibly + try { + dbxProcess.exitValue(); + } + catch (IllegalThreadStateException e) { + dbxProcess.destroy(); + } + } + + try { + if (importModuleSocket != null) { + importModuleSocket.close(); + } + } + catch (IOException e) { + } + + // Release references to all objects + clear(); + clearCache(); + } + + /** Looks up an address in the remote process's address space. + Returns 0 if symbol not found or upon error. Package private to + allow DbxDebuggerRemoteIntfImpl access. */ + synchronized long lookupInProcess(String objectName, String symbol) { + try { + printlnToOutput("lookup " + objectName + " " + symbol); + return in.parseAddress(); + } + catch (Exception e) { + return 0; + } + } + + /** This reads bytes from the remote process. */ + public synchronized ReadResult readBytesFromProcess(long address, long numBytes) + throws DebuggerException { + if (numBytes < 0) { + throw new DebuggerException("Can not read negative number (" + numBytes + ") of bytes from process"); + } + try { + String cmd = "peek " + utils.addressValueToString(address) + " " + numBytes; + printlnToOutput(cmd); + while (in.readByte() != 'B') { + } + byte res = in.readByte(); + if (res == 0) { + System.err.println("Failing command: " + cmd); + throw new DebuggerException("Read of remote process address space failed"); + } + // NOTE: must read ALL of the data regardless of whether we need + // to throw an UnmappedAddressException. Otherwise will corrupt + // the input stream each time we have a failure. Not good. Do + // not want to risk "flushing" the input stream in case a huge + // read has a hangup in the middle and we leave data on the + // stream. + byte[] buf = new byte[(int) numBytes]; + boolean bailOut = false; + long failureAddress = 0; + int numReads = 0; + while (numBytes > 0) { + long len = in.readUnsignedInt(); + boolean isMapped = ((in.readByte() == 0) ? false : true); + if (!isMapped) { + if (!bailOut) { + bailOut = true; + failureAddress = address; + } + } else { + // This won't work if we have unmapped regions, but if we do + // then we're going to throw an exception anyway + + // NOTE: there is a factor of 20 speed difference between + // these two ways of doing this read. + in.readBytes(buf, 0, (int) len); + } + + // Do NOT do this: + // for (int i = 0; i < (int) len; i++) { + // buf[i] = in.readByte(); + // } + + numBytes -= len; + address += len; + ++numReads; + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(numBytes == 0, "Bug in debug server's implementation of peek: numBytesLeft == " + + numBytes + ", should be 0 (did " + numReads + " reads)"); + } + if (bailOut) { + return new ReadResult(failureAddress); + } + return new ReadResult(buf); + } + catch (IOException e) { + throw new DebuggerException(e); + } + } + + public void writeBytesToProcess(long address, long numBytes, byte[] data) + throws UnmappedAddressException, DebuggerException { + // FIXME + throw new DebuggerException("Unimplemented"); + } + + /** This provides DbxDebuggerRemoteIntfImpl access to readBytesFromProcess */ + ReadResult readBytesFromProcessInternal(long address, long numBytes) + throws DebuggerException { + return readBytesFromProcess(address, numBytes); + } + + /** Convenience routine */ + private void printlnToOutput(String s) throws IOException { + out.println(s); + if (out.checkError()) { + throw new IOException("Error occurred while writing to debug server"); + } + } + + private void clear() { + dbxProcess = null; + dbxOstr = null; + out = null; + in = null; + importModuleSocket = null; + } + + /** Connects to the dbx import module, setting up out and in + streams. Factored out to allow access to the dbx console. */ + private void connectToImportModule() throws IOException { + // Try for 20 seconds to connect to dbx import module; time out + // with failure if didn't succeed + importModuleSocket = null; + long endTime = System.currentTimeMillis() + LONG_TIMEOUT; + + while ((importModuleSocket == null) && (System.currentTimeMillis() < endTime)) { + try { + importModuleSocket = new Socket(InetAddress.getLocalHost(), PORT); + importModuleSocket.setTcpNoDelay(true); + } + catch (IOException e) { + // Swallow IO exceptions while attempting connection + try { + // Don't swamp the CPU + Thread.sleep(1000); + } + catch (InterruptedException ex) { + } + } + } + + if (importModuleSocket == null) { + // Failed to connect because of timeout + detach(); + throw new DebuggerException("Timed out while attempting to connect to remote dbx process"); + } + + out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(importModuleSocket.getOutputStream(), "US-ASCII")), true); + in = new InputLexer(new BufferedInputStream(importModuleSocket.getInputStream())); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxOopHandle.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxOopHandle.java new file mode 100644 index 00000000000..785907e2100 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxOopHandle.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx; + +import sun.jvm.hotspot.debugger.*; + +class DbxOopHandle extends DbxAddress implements OopHandle { + DbxOopHandle(DbxDebugger debugger, long addr) { + super(debugger, addr); + } + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + throw new UnsupportedOperationException("addOffsetTo not applicable to OopHandles (interior object pointers not allowed)"); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("andWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("orWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("xorWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxThreadFactory.java new file mode 100644 index 00000000000..11a3241d90f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxThreadFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx; + +import sun.jvm.hotspot.debugger.*; + +/** An interface used only internally by the DbxDebugger to be able to + create platform-specific Thread objects */ + +public interface DbxThreadFactory { + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr); + public ThreadProxy createThreadWrapper(long id); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThread.java new file mode 100644 index 00000000000..74fc3f1454b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThread.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.dbx.*; +import sun.jvm.hotspot.utilities.*; + +public class DbxSPARCThread implements ThreadProxy { + private DbxDebugger debugger; + private int id; + + public DbxSPARCThread(DbxDebugger debugger, Address addr) { + this.debugger = debugger; + + // FIXME: the size here should be configurable. However, making it + // so would produce a dependency on the "types" package from the + // debugger package, which is not desired. + this.id = (int) addr.getCIntegerAt(0, 4, true); + } + + public DbxSPARCThread(DbxDebugger debugger, long id) { + this.debugger = debugger; + this.id = (int) id; + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof DbxSPARCThread)) { + return false; + } + + return (((DbxSPARCThread) obj).id == id); + } + + public int hashCode() { + return id; + } + + public ThreadContext getContext() throws IllegalThreadStateException { + DbxSPARCThreadContext context = new DbxSPARCThreadContext(debugger); + long[] regs = debugger.getThreadIntegerRegisterSet(id); + if (Assert.ASSERTS_ENABLED) { + Assert.that(regs.length == SPARCThreadContext.NPRGREG, "size of register set must match"); + } + for (int i = 0; i < regs.length; i++) { + context.setRegister(i, regs[i]); + } + return context; + } + + public boolean canSetContext() throws DebuggerException { + return false; + } + + public void setContext(ThreadContext context) + throws IllegalThreadStateException, DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + public String toString() { + return "t@" + id; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThreadContext.java new file mode 100644 index 00000000000..f06dab93970 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.dbx.*; + +public class DbxSPARCThreadContext extends SPARCThreadContext { + private DbxDebugger debugger; + + public DbxSPARCThreadContext(DbxDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThreadFactory.java new file mode 100644 index 00000000000..acbd49a2465 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.dbx.*; + +public class DbxSPARCThreadFactory implements DbxThreadFactory { + private DbxDebugger debugger; + + public DbxSPARCThreadFactory(DbxDebugger debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new DbxSPARCThread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new DbxSPARCThread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86Thread.java new file mode 100644 index 00000000000..10c0bb02aea --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86Thread.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.dbx.*; +import sun.jvm.hotspot.utilities.*; + +public class DbxX86Thread implements ThreadProxy { + private DbxDebugger debugger; + private int id; + + public DbxX86Thread(DbxDebugger debugger, Address addr) { + this.debugger = debugger; + + // FIXME: the size here should be configurable. However, making it + // so would produce a dependency on the "types" package from the + // debugger package, which is not desired. + this.id = (int) addr.getCIntegerAt(0, 4, true); + } + + public DbxX86Thread(DbxDebugger debugger, long id) { + this.debugger = debugger; + this.id = (int) id; + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof DbxX86Thread)) { + return false; + } + + return (((DbxX86Thread) obj).id == id); + } + + public int hashCode() { + return id; + } + + public ThreadContext getContext() throws IllegalThreadStateException { + DbxX86ThreadContext context = new DbxX86ThreadContext(debugger); + long[] regs = debugger.getThreadIntegerRegisterSet(id); + if (Assert.ASSERTS_ENABLED) { + Assert.that(regs.length == 19, "unknown size of register set -- adjust this code"); + } + for (int i = 0; i < regs.length; i++) { + context.setRegister(i, regs[i]); + } + return context; + } + + public boolean canSetContext() throws DebuggerException { + return false; + } + + public void setContext(ThreadContext context) + throws IllegalThreadStateException, DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + public String toString() { + return "t@" + id; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86ThreadContext.java new file mode 100644 index 00000000000..71794c3c9e4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86ThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.dbx.*; + +public class DbxX86ThreadContext extends X86ThreadContext { + private DbxDebugger debugger; + + public DbxX86ThreadContext(DbxDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86ThreadFactory.java new file mode 100644 index 00000000000..c6bcfd749cf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86ThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dbx.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.dbx.*; + +public class DbxX86ThreadFactory implements DbxThreadFactory { + private DbxDebugger debugger; + + public DbxX86ThreadFactory(DbxDebugger debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new DbxX86Thread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new DbxX86Thread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dummy/DummyAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dummy/DummyAddress.java new file mode 100644 index 00000000000..2c9bc12ff58 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dummy/DummyAddress.java @@ -0,0 +1,377 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dummy; + +import sun.jvm.hotspot.debugger.*; + +/** For testing purposes */ + +class DummyAddress implements Address { + protected DummyDebugger debugger; + protected long addr; + private static final long badLong = 0xBAADBABEL; + private static final double badDouble = 1.23456; + + DummyAddress(DummyDebugger debugger, long addr) { + this.debugger = debugger; + this.addr = addr; + } + + // + // Basic Java routines + // + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof DummyAddress)) { + return false; + } + + return (addr == ((DummyAddress) arg).addr); + } + + public int hashCode() { + // FIXME: suggestions on a better hash code? + return (int) addr; + } + + public String toString() { + return debugger.addressToString(this); + } + + // + // C/C++-related routines + // + + public long getCIntegerAt(long offset, long numBytes, boolean isUnsigned) throws UnalignedAddressException, UnmappedAddressException { + return badLong; + } + + public Address getAddressAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return new DummyAddress(debugger, badLong); + } + + // + // Java-related routines + // + + public boolean getJBooleanAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return false; + } + + public byte getJByteAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return (byte) badLong; + } + + public char getJCharAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return (char) badLong; + } + + public double getJDoubleAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return badDouble; + } + + public float getJFloatAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return (float) badDouble; + } + + public int getJIntAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return (int) badLong; + } + + public long getJLongAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return badLong; + } + + public short getJShortAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return (short) badLong; + } + + public OopHandle getOopHandleAt(long offset) + throws UnalignedAddressException, UnmappedAddressException, NotInHeapException { + return new DummyOopHandle(debugger, badLong); + } + + // Mutators -- not implemented + public void setCIntegerAt(long offset, long numBytes, long value) { + throw new DebuggerException("Unimplemented"); + } + public void setAddressAt(long offset, Address value) { + throw new DebuggerException("Unimplemented"); + } + public void setJBooleanAt (long offset, boolean value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJByteAt (long offset, byte value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJCharAt (long offset, char value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJDoubleAt (long offset, double value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJFloatAt (long offset, float value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJIntAt (long offset, int value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJLongAt (long offset, long value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJShortAt (long offset, short value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setOopHandleAt (long offset, OopHandle value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + + // + // Arithmetic operations -- necessary evil. + // + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new DummyAddress(debugger, value); + } + + public OopHandle addOffsetToAsOopHandle(long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new DummyOopHandle(debugger, value); + } + + /** (FIXME: any signed/unsigned issues? Should this work for + OopHandles?) */ + public long minus(Address arg) { + if (arg == null) { + return addr; + } + return addr - ((DummyAddress) arg).addr; + } + + // Two's complement representation. + // All negative numbers are larger than positive numbers. + // Numbers with the same sign can be compared normally. + // Test harness is below in main(). + + public boolean lessThan (Address arg) { + DummyAddress DummyArg = (DummyAddress) arg; + if ((addr >= 0) && (DummyArg.addr < 0)) { + return true; + } + if ((addr < 0) && (DummyArg.addr >= 0)) { + return false; + } + return (addr < DummyArg.addr); + } + + public boolean lessThanOrEqual (Address arg) { + DummyAddress DummyArg = (DummyAddress) arg; + if ((addr >= 0) && (DummyArg.addr < 0)) { + return true; + } + if ((addr < 0) && (DummyArg.addr >= 0)) { + return false; + } + return (addr <= DummyArg.addr); + } + + public boolean greaterThan (Address arg) { + DummyAddress DummyArg = (DummyAddress) arg; + if ((addr >= 0) && (DummyArg.addr < 0)) { + return false; + } + if ((addr < 0) && (DummyArg.addr >= 0)) { + return true; + } + return (addr > DummyArg.addr); + } + + public boolean greaterThanOrEqual(Address arg) { + DummyAddress DummyArg = (DummyAddress) arg; + if ((addr >= 0) && (DummyArg.addr < 0)) { + return false; + } + if ((addr < 0) && (DummyArg.addr >= 0)) { + return true; + } + return (addr >= DummyArg.addr); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + long value = addr & mask; + if (value == 0) { + return null; + } + return new DummyAddress(debugger, value); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + long value = addr | mask; + if (value == 0) { + return null; + } + return new DummyAddress(debugger, value); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + long value = addr ^ mask; + if (value == 0) { + return null; + } + return new DummyAddress(debugger, value); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + long getValue() { + return addr; + } + + private static void check(boolean arg, String failMessage) { + if (!arg) { + System.err.println(failMessage + ": FAILED"); + System.exit(1); + } + } + + // Test harness + public static void main(String[] args) { + // p/n indicates whether the interior address is really positive + // or negative. In unsigned terms, p1 < p2 < n1 < n2. + + DummyAddress p1 = new DummyAddress(null, 0x7FFFFFFFFFFFFFF0L); + DummyAddress p2 = (DummyAddress) p1.addOffsetTo(10); + DummyAddress n1 = (DummyAddress) p2.addOffsetTo(10); + DummyAddress n2 = (DummyAddress) n1.addOffsetTo(10); + + // lessThan positive tests + check(p1.lessThan(p2), "lessThan 1"); + check(p1.lessThan(n1), "lessThan 2"); + check(p1.lessThan(n2), "lessThan 3"); + check(p2.lessThan(n1), "lessThan 4"); + check(p2.lessThan(n2), "lessThan 5"); + check(n1.lessThan(n2), "lessThan 6"); + + // lessThan negative tests + check(!p1.lessThan(p1), "lessThan 7"); + check(!p2.lessThan(p2), "lessThan 8"); + check(!n1.lessThan(n1), "lessThan 9"); + check(!n2.lessThan(n2), "lessThan 10"); + + check(!p2.lessThan(p1), "lessThan 11"); + check(!n1.lessThan(p1), "lessThan 12"); + check(!n2.lessThan(p1), "lessThan 13"); + check(!n1.lessThan(p2), "lessThan 14"); + check(!n2.lessThan(p2), "lessThan 15"); + check(!n2.lessThan(n1), "lessThan 16"); + + // lessThanOrEqual positive tests + check(p1.lessThanOrEqual(p1), "lessThanOrEqual 1"); + check(p2.lessThanOrEqual(p2), "lessThanOrEqual 2"); + check(n1.lessThanOrEqual(n1), "lessThanOrEqual 3"); + check(n2.lessThanOrEqual(n2), "lessThanOrEqual 4"); + + check(p1.lessThanOrEqual(p2), "lessThanOrEqual 5"); + check(p1.lessThanOrEqual(n1), "lessThanOrEqual 6"); + check(p1.lessThanOrEqual(n2), "lessThanOrEqual 7"); + check(p2.lessThanOrEqual(n1), "lessThanOrEqual 8"); + check(p2.lessThanOrEqual(n2), "lessThanOrEqual 9"); + check(n1.lessThanOrEqual(n2), "lessThanOrEqual 10"); + + // lessThanOrEqual negative tests + check(!p2.lessThanOrEqual(p1), "lessThanOrEqual 11"); + check(!n1.lessThanOrEqual(p1), "lessThanOrEqual 12"); + check(!n2.lessThanOrEqual(p1), "lessThanOrEqual 13"); + check(!n1.lessThanOrEqual(p2), "lessThanOrEqual 14"); + check(!n2.lessThanOrEqual(p2), "lessThanOrEqual 15"); + check(!n2.lessThanOrEqual(n1), "lessThanOrEqual 16"); + + // greaterThan positive tests + check(n2.greaterThan(p1), "greaterThan 1"); + check(n2.greaterThan(p2), "greaterThan 2"); + check(n2.greaterThan(n1), "greaterThan 3"); + check(n1.greaterThan(p1), "greaterThan 4"); + check(n1.greaterThan(p2), "greaterThan 5"); + check(p2.greaterThan(p1), "greaterThan 6"); + + // greaterThan negative tests + check(!p1.greaterThan(p1), "greaterThan 7"); + check(!p2.greaterThan(p2), "greaterThan 8"); + check(!n1.greaterThan(n1), "greaterThan 9"); + check(!n2.greaterThan(n2), "greaterThan 10"); + + check(!p1.greaterThan(n2), "greaterThan 11"); + check(!p2.greaterThan(n2), "greaterThan 12"); + check(!n1.greaterThan(n2), "greaterThan 13"); + check(!p1.greaterThan(n1), "greaterThan 14"); + check(!p2.greaterThan(n1), "greaterThan 15"); + check(!p1.greaterThan(p2), "greaterThan 16"); + + // greaterThanOrEqual positive tests + check(p1.greaterThanOrEqual(p1), "greaterThanOrEqual 1"); + check(p2.greaterThanOrEqual(p2), "greaterThanOrEqual 2"); + check(n1.greaterThanOrEqual(n1), "greaterThanOrEqual 3"); + check(n2.greaterThanOrEqual(n2), "greaterThanOrEqual 4"); + + check(n2.greaterThanOrEqual(p1), "greaterThanOrEqual 5"); + check(n2.greaterThanOrEqual(p2), "greaterThanOrEqual 6"); + check(n2.greaterThanOrEqual(n1), "greaterThanOrEqual 7"); + check(n1.greaterThanOrEqual(p1), "greaterThanOrEqual 8"); + check(n1.greaterThanOrEqual(p2), "greaterThanOrEqual 9"); + check(p2.greaterThanOrEqual(p1), "greaterThanOrEqual 10"); + + // greaterThanOrEqual negative tests + check(!p1.greaterThanOrEqual(n2), "greaterThanOrEqual 11"); + check(!p2.greaterThanOrEqual(n2), "greaterThanOrEqual 12"); + check(!n1.greaterThanOrEqual(n2), "greaterThanOrEqual 13"); + check(!p1.greaterThanOrEqual(n1), "greaterThanOrEqual 14"); + check(!p2.greaterThanOrEqual(n1), "greaterThanOrEqual 15"); + check(!p1.greaterThanOrEqual(p2), "greaterThanOrEqual 16"); + + System.err.println("DummyAddress: all tests passed successfully."); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dummy/DummyDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dummy/DummyDebugger.java new file mode 100644 index 00000000000..ea03142f81c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dummy/DummyDebugger.java @@ -0,0 +1,168 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dummy; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.CDebugger; +import sun.jvm.hotspot.utilities.*; + +/** For testing purposes */ + +public class DummyDebugger extends DebuggerBase { + private MachineDescription machDesc; + + public DummyDebugger(MachineDescription machDesc) { + this.machDesc = machDesc; + } + + public boolean hasProcessList() throws DebuggerException { + return false; + } + + public List getProcessList() throws DebuggerException { + return null; + } + + public void attach(int processID) throws DebuggerException { + } + + public void attach(String executableName, String coreFileName) + throws DebuggerException { + } + + public boolean detach() { + return true; + } + + public Address parseAddress(String addrStr) { + String s = addrStr.trim(); + if (!s.startsWith("0x")) { + throw new NumberFormatException(addrStr); + } + long l = 0; + for (int i = 2; i < s.length(); ++i) { + int val = charToNibble(s.charAt(i)); + l <<= 4; + l |= val; + } + return new DummyAddress(this, l); + } + + public long getAddressValue(Address addr) { + if (addr == null) return 0; + return ((DummyAddress) addr).getValue(); + } + + public String getOS() { + return PlatformInfo.getOS(); + } + + public String getCPU() { + return PlatformInfo.getCPU(); + } + + public MachineDescription getMachineDescription() throws DebuggerException { + return machDesc; + } + + public boolean hasConsole() { + return false; + } + + public String consoleExecuteCommand(String cmd) + throws DebuggerException { + throw new DebuggerException("unimplemented"); + } + + public String getConsolePrompt() throws DebuggerException { + throw new DebuggerException("unimplemented"); + } + + public CDebugger getCDebugger() throws DebuggerException { + return null; + } + + public Address lookup(String objectName, String symbol) { + return null; + } + + public OopHandle lookupOop(String objectName, String symbol) { + return null; + } + + public ThreadProxy getThreadForIdentifierAddress(Address addr) { + return null; + } + + public ThreadProxy getThreadForThreadId(long id) { + return null; + } + + public ReadResult readBytesFromProcess(long address, long numBytes) + throws DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + public void writeBytesToProcess(long a, long b, byte[] buf) + throws DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + //---------------------------------------------------------------------- + // Package-internal routines + // + + String addressToString(DummyAddress addr) { + StringBuffer buf = new StringBuffer(); + buf.append("0x"); + String val; + if (addr == null) { + val = "0"; + } else { + val = Long.toHexString(addr.getValue()); + } + for (int i = 0; i < ((2 * machDesc.getAddressSize()) - val.length()); i++) { + buf.append('0'); + } + buf.append(val); + return buf.toString(); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private int charToNibble(char ascii) throws NumberFormatException { + if (ascii >= '0' && ascii <= '9') { + return ascii - '0'; + } else if (ascii >= 'A' && ascii <= 'F') { + return 10 + ascii - 'A'; + } else if (ascii >= 'a' && ascii <= 'f') { + return 10 + ascii - 'a'; + } + throw new NumberFormatException(new Character(ascii).toString()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dummy/DummyOopHandle.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dummy/DummyOopHandle.java new file mode 100644 index 00000000000..8b389285b00 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dummy/DummyOopHandle.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.dummy; + +import sun.jvm.hotspot.debugger.*; + +/** For testing purposes */ + +class DummyOopHandle extends DummyAddress implements OopHandle { + DummyOopHandle(DummyDebugger debugger, long addr) { + super(debugger, addr); + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof DummyOopHandle)) { + return false; + } + + return (addr == ((DummyAddress) arg).addr); + } + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + throw new UnsupportedOperationException("addOffsetTo not applicable to OopHandles (interior object pointers not allowed)"); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("andWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("orWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("xorWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ia64/IA64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ia64/IA64ThreadContext.java new file mode 100644 index 00000000000..69e0033377b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/ia64/IA64ThreadContext.java @@ -0,0 +1,182 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.ia64; + +import sun.jvm.hotspot.debugger.*; + +/** Specifies the thread context on ia64 platform; only a sub-portion + of the context is guaranteed to be present on all operating + systems. */ + +public abstract class IA64ThreadContext implements ThreadContext { + // Refer to winnt.h CONTEXT structure - Nov 2001 edition Platform SDK + // only a relevant subset of CONTEXT structure is used here. + // For eg. floating point registers are ignored. + + // NOTE: the indices for the various registers must be maintained as + // listed across various operating systems. However, only a + // subset of the registers' values are guaranteed to be present + + // global registers r0-r31 + public static final int GR0 = 0; + public static final int GR1 = 1; + public static final int GR2 = 2; + public static final int GR3 = 3; + public static final int GR4 = 4; + public static final int GR5 = 5; + public static final int GR6 = 6; + public static final int GR7 = 7; + public static final int GR8 = 8; + public static final int GR9 = 9; + public static final int GR10 = 10; + public static final int GR11 = 11; + public static final int GR12 = 12; + public static final int SP = GR12; + public static final int GR13 = 13; + public static final int GR14 = 14; + public static final int GR15 = 15; + public static final int GR16 = 16; + public static final int GR17 = 17; + public static final int GR18 = 18; + public static final int GR19 = 19; + public static final int GR20 = 20; + public static final int GR21 = 21; + public static final int GR22 = 22; + public static final int GR23 = 23; + public static final int GR24 = 24; + public static final int GR25 = 25; + public static final int GR26 = 26; + public static final int GR27 = 27; + public static final int GR28 = 28; + public static final int GR29 = 29; + public static final int GR30 = 30; + public static final int GR31 = 31; + + // Nat bits for r1-r31 + public static final int INT_NATS = 32; + + // predicates + public static final int PREDS = 33; + + // branch registers + public static final int BR0 = 34; + public static final int BR_RP = BR0; + public static final int BR1 = 35; + public static final int BR2 = 36; + public static final int BR3 = 37; + public static final int BR4 = 38; + public static final int BR5 = 39; + public static final int BR6 = 40; + public static final int BR7 = 41; + + // application registers + public static final int AP_UNAT = 42; // User Nat Collection register + public static final int AP_LC = 43; // Loop counter register + public static final int AP_EC = 43; // Epilog counter register + public static final int AP_CCV = 45; // CMPXCHG value register + public static final int AP_DCR = 46; // Default control register + + // register stack info + public static final int RS_PFS = 47; // Previous function state + public static final int AP_PFS = RS_PFS; + public static final int RS_BSP = 48; // Backing store pointer + public static final int AR_BSP = RS_BSP; + public static final int RS_BSPSTORE = 49; + public static final int AP_BSPSTORE = RS_BSPSTORE; + public static final int RS_RSC = 50; // RSE configuration + public static final int AP_RSC = RS_RSC; + public static final int RS_RNAT = 51; // RSE Nat collection register + public static final int AP_RNAT = RS_RNAT; + + // trap status register + public static final int ST_IPSR = 52; // Interuption Processor Status + public static final int ST_IIP = 53; // Interruption IP + public static final int ST_IFS = 54; // Interruption Function State + + // debug registers + public static final int DB_I0 = 55; + public static final int DB_I1 = 56; + public static final int DB_I2 = 57; + public static final int DB_I3 = 58; + public static final int DB_I4 = 59; + public static final int DB_I5 = 60; + public static final int DB_I6 = 61; + public static final int DB_I7 = 62; + + public static final int DB_D0 = 63; + public static final int DB_D1 = 64; + public static final int DB_D2 = 65; + public static final int DB_D3 = 66; + public static final int DB_D4 = 67; + public static final int DB_D5 = 68; + public static final int DB_D6 = 69; + public static final int DB_D7 = 70; + + public static final int NPRGREG = 71; + + private static final String[] regNames = { + "GR0", "GR1", "GR2", "GR3", "GR4", "GR5", "GR6", "GR7", "GR8", + "GR9", "GR10", "GR11", "GR12", "GR13", "GR14", "GR15", "GR16", + "GR17","GR18", "GR19", "GR20", "GR21", "GR22", "GR23", "GR24", + "GR25","GR26", "GR27", "GR28", "GR29", "GR30", "GR31", + "INT_NATS", "PREDS", + "BR0", "BR1", "BR2", "BR3", "BR4", "BR5", "BR6", "BR7", + "AP_UNAT", "AP_LC", "AP_EC", "AP_CCV", "AP_DCR", + "RS_FPS", "RS_BSP", "RS_BSPSTORE", "RS_RSC", "RS_RNAT", + "ST_IPSR", "ST_IIP", "ST_IFS", + "DB_I0", "DB_I1", "DB_I2", "DB_I3", "DB_I4", "DB_I5", "DB_I6", "DB_I7", + "DB_D0", "DB_D1", "DB_D2", "DB_D3", "DB_D4", "DB_D5", "DB_D6", "DB_D7" + }; + + private long[] data; + + public IA64ThreadContext() { + data = new long[NPRGREG]; + } + + public int getNumRegisters() { + return NPRGREG; + } + + public String getRegisterName(int index) { + return regNames[index]; + } + + public void setRegister(int index, long value) { + data[index] = value; + } + + public long getRegister(int index) { + return data[index]; + } + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public abstract void setRegisterAsAddress(int index, Address value); + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public abstract Address getRegisterAsAddress(int index); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxAddress.java new file mode 100644 index 00000000000..1112a7847b9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxAddress.java @@ -0,0 +1,389 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux; + +import sun.jvm.hotspot.debugger.*; + +class LinuxAddress implements Address { + protected LinuxDebugger debugger; + protected long addr; + + LinuxAddress(LinuxDebugger debugger, long addr) { + this.debugger = debugger; + this.addr = addr; + } + + // + // Basic Java routines + // + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof LinuxAddress)) { + return false; + } + + return (addr == ((LinuxAddress) arg).addr); + } + + public int hashCode() { + // FIXME: suggestions on a better hash code? + return (int) addr; + } + + public String toString() { + return debugger.addressValueToString(addr); + } + + // + // C/C++-related routines + // + + public long getCIntegerAt(long offset, long numBytes, boolean isUnsigned) + throws UnalignedAddressException, UnmappedAddressException { + return debugger.readCInteger(addr + offset, numBytes, isUnsigned); + } + + public Address getAddressAt(long offset) + throws UnalignedAddressException, UnmappedAddressException { + return debugger.readAddress(addr + offset); + } + + // + // Java-related routines + // + + public boolean getJBooleanAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJBoolean(addr + offset); + } + + public byte getJByteAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJByte(addr + offset); + } + + public char getJCharAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJChar(addr + offset); + } + + public double getJDoubleAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJDouble(addr + offset); + } + + public float getJFloatAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJFloat(addr + offset); + } + + public int getJIntAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJInt(addr + offset); + } + + public long getJLongAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJLong(addr + offset); + } + + public short getJShortAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJShort(addr + offset); + } + + public OopHandle getOopHandleAt(long offset) + throws UnalignedAddressException, UnmappedAddressException, NotInHeapException { + return debugger.readOopHandle(addr + offset); + } + + // Mutators -- not implemented for now (FIXME) + public void setCIntegerAt(long offset, long numBytes, long value) { + throw new DebuggerException("Unimplemented"); + } + public void setAddressAt(long offset, Address value) { + throw new DebuggerException("Unimplemented"); + } + public void setJBooleanAt (long offset, boolean value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJByteAt (long offset, byte value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJCharAt (long offset, char value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJDoubleAt (long offset, double value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJFloatAt (long offset, float value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJIntAt (long offset, int value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJLongAt (long offset, long value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJShortAt (long offset, short value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setOopHandleAt (long offset, OopHandle value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + + // + // Arithmetic operations -- necessary evil. + // + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new LinuxAddress(debugger, value); + } + + public OopHandle addOffsetToAsOopHandle(long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new LinuxOopHandle(debugger, value); + } + + /** (FIXME: any signed/unsigned issues? Should this work for + OopHandles?) */ + public long minus(Address arg) { + if (arg == null) { + return addr; + } + return addr - ((LinuxAddress) arg).addr; + } + + // Two's complement representation. + // All negative numbers are larger than positive numbers. + // Numbers with the same sign can be compared normally. + // Test harness is below in main(). + + public boolean lessThan (Address a) { + if (a == null) { + return false; + } + LinuxAddress arg = (LinuxAddress) a; + if ((addr >= 0) && (arg.addr < 0)) { + return true; + } + if ((addr < 0) && (arg.addr >= 0)) { + return false; + } + return (addr < arg.addr); + } + + public boolean lessThanOrEqual (Address a) { + if (a == null) { + return false; + } + LinuxAddress arg = (LinuxAddress) a; + if ((addr >= 0) && (arg.addr < 0)) { + return true; + } + if ((addr < 0) && (arg.addr >= 0)) { + return false; + } + return (addr <= arg.addr); + } + + public boolean greaterThan (Address a) { + if (a == null) { + return true; + } + LinuxAddress arg = (LinuxAddress) a; + if ((addr >= 0) && (arg.addr < 0)) { + return false; + } + if ((addr < 0) && (arg.addr >= 0)) { + return true; + } + return (addr > arg.addr); + } + + public boolean greaterThanOrEqual(Address a) { + if (a == null) { + return true; + } + LinuxAddress arg = (LinuxAddress) a; + if ((addr >= 0) && (arg.addr < 0)) { + return false; + } + if ((addr < 0) && (arg.addr >= 0)) { + return true; + } + return (addr >= arg.addr); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + long value = addr & mask; + if (value == 0) { + return null; + } + return new LinuxAddress(debugger, value); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + long value = addr | mask; + if (value == 0) { + return null; + } + return new LinuxAddress(debugger, value); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + long value = addr ^ mask; + if (value == 0) { + return null; + } + return new LinuxAddress(debugger, value); + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + long getValue() { + return addr; + } + + + private static void check(boolean arg, String failMessage) { + if (!arg) { + System.err.println(failMessage + ": FAILED"); + System.exit(1); + } + } + + // Test harness + public static void main(String[] args) { + // p/n indicates whether the interior address is really positive + // or negative. In unsigned terms, p1 < p2 < n1 < n2. + + LinuxAddress p1 = new LinuxAddress(null, 0x7FFFFFFFFFFFFFF0L); + LinuxAddress p2 = (LinuxAddress) p1.addOffsetTo(10); + LinuxAddress n1 = (LinuxAddress) p2.addOffsetTo(10); + LinuxAddress n2 = (LinuxAddress) n1.addOffsetTo(10); + + // lessThan positive tests + check(p1.lessThan(p2), "lessThan 1"); + check(p1.lessThan(n1), "lessThan 2"); + check(p1.lessThan(n2), "lessThan 3"); + check(p2.lessThan(n1), "lessThan 4"); + check(p2.lessThan(n2), "lessThan 5"); + check(n1.lessThan(n2), "lessThan 6"); + + // lessThan negative tests + check(!p1.lessThan(p1), "lessThan 7"); + check(!p2.lessThan(p2), "lessThan 8"); + check(!n1.lessThan(n1), "lessThan 9"); + check(!n2.lessThan(n2), "lessThan 10"); + + check(!p2.lessThan(p1), "lessThan 11"); + check(!n1.lessThan(p1), "lessThan 12"); + check(!n2.lessThan(p1), "lessThan 13"); + check(!n1.lessThan(p2), "lessThan 14"); + check(!n2.lessThan(p2), "lessThan 15"); + check(!n2.lessThan(n1), "lessThan 16"); + + // lessThanOrEqual positive tests + check(p1.lessThanOrEqual(p1), "lessThanOrEqual 1"); + check(p2.lessThanOrEqual(p2), "lessThanOrEqual 2"); + check(n1.lessThanOrEqual(n1), "lessThanOrEqual 3"); + check(n2.lessThanOrEqual(n2), "lessThanOrEqual 4"); + + check(p1.lessThanOrEqual(p2), "lessThanOrEqual 5"); + check(p1.lessThanOrEqual(n1), "lessThanOrEqual 6"); + check(p1.lessThanOrEqual(n2), "lessThanOrEqual 7"); + check(p2.lessThanOrEqual(n1), "lessThanOrEqual 8"); + check(p2.lessThanOrEqual(n2), "lessThanOrEqual 9"); + check(n1.lessThanOrEqual(n2), "lessThanOrEqual 10"); + + // lessThanOrEqual negative tests + check(!p2.lessThanOrEqual(p1), "lessThanOrEqual 11"); + check(!n1.lessThanOrEqual(p1), "lessThanOrEqual 12"); + check(!n2.lessThanOrEqual(p1), "lessThanOrEqual 13"); + check(!n1.lessThanOrEqual(p2), "lessThanOrEqual 14"); + check(!n2.lessThanOrEqual(p2), "lessThanOrEqual 15"); + check(!n2.lessThanOrEqual(n1), "lessThanOrEqual 16"); + + // greaterThan positive tests + check(n2.greaterThan(p1), "greaterThan 1"); + check(n2.greaterThan(p2), "greaterThan 2"); + check(n2.greaterThan(n1), "greaterThan 3"); + check(n1.greaterThan(p1), "greaterThan 4"); + check(n1.greaterThan(p2), "greaterThan 5"); + check(p2.greaterThan(p1), "greaterThan 6"); + + // greaterThan negative tests + check(!p1.greaterThan(p1), "greaterThan 7"); + check(!p2.greaterThan(p2), "greaterThan 8"); + check(!n1.greaterThan(n1), "greaterThan 9"); + check(!n2.greaterThan(n2), "greaterThan 10"); + + check(!p1.greaterThan(n2), "greaterThan 11"); + check(!p2.greaterThan(n2), "greaterThan 12"); + check(!n1.greaterThan(n2), "greaterThan 13"); + check(!p1.greaterThan(n1), "greaterThan 14"); + check(!p2.greaterThan(n1), "greaterThan 15"); + check(!p1.greaterThan(p2), "greaterThan 16"); + + // greaterThanOrEqual positive tests + check(p1.greaterThanOrEqual(p1), "greaterThanOrEqual 1"); + check(p2.greaterThanOrEqual(p2), "greaterThanOrEqual 2"); + check(n1.greaterThanOrEqual(n1), "greaterThanOrEqual 3"); + check(n2.greaterThanOrEqual(n2), "greaterThanOrEqual 4"); + + check(n2.greaterThanOrEqual(p1), "greaterThanOrEqual 5"); + check(n2.greaterThanOrEqual(p2), "greaterThanOrEqual 6"); + check(n2.greaterThanOrEqual(n1), "greaterThanOrEqual 7"); + check(n1.greaterThanOrEqual(p1), "greaterThanOrEqual 8"); + check(n1.greaterThanOrEqual(p2), "greaterThanOrEqual 9"); + check(p2.greaterThanOrEqual(p1), "greaterThanOrEqual 10"); + + // greaterThanOrEqual negative tests + check(!p1.greaterThanOrEqual(n2), "greaterThanOrEqual 11"); + check(!p2.greaterThanOrEqual(n2), "greaterThanOrEqual 12"); + check(!n1.greaterThanOrEqual(n2), "greaterThanOrEqual 13"); + check(!p1.greaterThanOrEqual(n1), "greaterThanOrEqual 14"); + check(!p2.greaterThanOrEqual(n1), "greaterThanOrEqual 15"); + check(!p1.greaterThanOrEqual(p2), "greaterThanOrEqual 16"); + + System.err.println("LinuxAddress: all tests passed successfully."); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java new file mode 100644 index 00000000000..4371456925a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java @@ -0,0 +1,130 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.linux.x86.*; +import sun.jvm.hotspot.debugger.linux.amd64.*; +import sun.jvm.hotspot.debugger.linux.sparc.*; +import sun.jvm.hotspot.utilities.*; + +class LinuxCDebugger implements CDebugger { + private LinuxDebugger dbg; + + LinuxCDebugger(LinuxDebugger dbg) { + this.dbg = dbg; + } + + public List getThreadList() throws DebuggerException { + return dbg.getThreadList(); + } + + public List/**/ getLoadObjectList() throws DebuggerException { + return dbg.getLoadObjectList(); + } + + public LoadObject loadObjectContainingPC(Address pc) throws DebuggerException { + if (pc == null) { + return null; + } + List objs = getLoadObjectList(); + Object[] arr = objs.toArray(); + // load objects are sorted by base address, do binary search + int mid = -1; + int low = 0; + int high = arr.length - 1; + + while (low <= high) { + mid = (low + high) >> 1; + LoadObject midVal = (LoadObject) arr[mid]; + long cmp = pc.minus(midVal.getBase()); + if (cmp < 0) { + high = mid - 1; + } else if (cmp > 0) { + long size = midVal.getSize(); + if (cmp >= size) { + low = mid + 1; + } else { + return (LoadObject) arr[mid]; + } + } else { // match found + return (LoadObject) arr[mid]; + } + } + // no match found. + return null; + } + + public CFrame topFrameForThread(ThreadProxy thread) throws DebuggerException { + String cpu = dbg.getCPU(); + if (cpu.equals("x86")) { + X86ThreadContext context = (X86ThreadContext) thread.getContext(); + Address ebp = context.getRegisterAsAddress(X86ThreadContext.EBP); + if (ebp == null) return null; + Address pc = context.getRegisterAsAddress(X86ThreadContext.EIP); + if (pc == null) return null; + return new LinuxX86CFrame(dbg, ebp, pc); + } else if (cpu.equals("amd64")) { + AMD64ThreadContext context = (AMD64ThreadContext) thread.getContext(); + Address rbp = context.getRegisterAsAddress(AMD64ThreadContext.RBP); + if (rbp == null) return null; + Address pc = context.getRegisterAsAddress(AMD64ThreadContext.RIP); + if (pc == null) return null; + return new LinuxAMD64CFrame(dbg, rbp, pc); + } else if (cpu.equals("sparc")) { + SPARCThreadContext context = (SPARCThreadContext) thread.getContext(); + Address sp = context.getRegisterAsAddress(SPARCThreadContext.R_SP); + if (sp == null) return null; + Address pc = context.getRegisterAsAddress(SPARCThreadContext.R_O7); + if (pc == null) return null; + return new LinuxSPARCCFrame(dbg, sp, pc, LinuxDebuggerLocal.getAddressSize()); + } else { + throw new DebuggerException(cpu + " is not yet supported"); + } + } + + public String getNameOfFile(String fileName) { + return new File(fileName).getName(); + } + + public ProcessControl getProcessControl() throws DebuggerException { + // FIXME: after stabs parser + return null; + } + + public boolean canDemangle() { + return false; + } + + public String demangle(String sym) { + throw new UnsupportedOperationException(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java new file mode 100644 index 00000000000..b6ae0f45262 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux; + +import java.util.List; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +/** An extension of the JVMDebugger interface with a few additions to + support 32-bit vs. 64-bit debugging as well as features required + by the architecture-specific subpackages. */ + +public interface LinuxDebugger extends JVMDebugger { + public String addressValueToString(long address) throws DebuggerException; + public boolean readJBoolean(long address) throws DebuggerException; + public byte readJByte(long address) throws DebuggerException; + public char readJChar(long address) throws DebuggerException; + public double readJDouble(long address) throws DebuggerException; + public float readJFloat(long address) throws DebuggerException; + public int readJInt(long address) throws DebuggerException; + public long readJLong(long address) throws DebuggerException; + public short readJShort(long address) throws DebuggerException; + public long readCInteger(long address, long numBytes, boolean isUnsigned) + throws DebuggerException; + public LinuxAddress readAddress(long address) throws DebuggerException; + public LinuxOopHandle readOopHandle(long address) throws DebuggerException; + public long[] getThreadIntegerRegisterSet(int lwp_id) throws DebuggerException; + public long getAddressValue(Address addr) throws DebuggerException; + public Address newAddress(long value) throws DebuggerException; + + // For LinuxCDebugger + public List getThreadList(); + public List getLoadObjectList(); + public ClosestSymbol lookup(long address); + + // NOTE: this interface implicitly contains the following methods: + // From the Debugger interface via JVMDebugger + // public void attach(int processID) throws DebuggerException; + // public void attach(String executableName, String coreFileName) throws DebuggerException; + // public boolean detach(); + // public Address parseAddress(String addressString) throws NumberFormatException; + // public String getOS(); + // public String getCPU(); + // From the SymbolLookup interface via Debugger and JVMDebugger + // public Address lookup(String objectName, String symbol); + // public OopHandle lookupOop(String objectName, String symbol); + // From the JVMDebugger interface + // public void configureJavaPrimitiveTypeSizes(long jbooleanSize, + // long jbyteSize, + // long jcharSize, + // long jdoubleSize, + // long jfloatSize, + // long jintSize, + // long jlongSize, + // long jshortSize); + // From the ThreadAccess interface via Debugger and JVMDebugger + // public ThreadProxy getThreadForIdentifierAddress(Address addr); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java new file mode 100644 index 00000000000..e92602efd52 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java @@ -0,0 +1,584 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux; + +import java.io.*; +import java.net.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.utilities.*; +import java.lang.reflect.*; + +/**

An implementation of the JVMDebugger interface. The basic debug + facilities are implemented through ptrace interface in the JNI code + (libsaproc.so). Library maps and symbol table management are done in + JNI.

+ +

NOTE that since we have the notion of fetching "Java + primitive types" from the remote process (which might have + different sizes than we expect) we have a bootstrapping + problem. We need to know the sizes of these types before we can + fetch them. The current implementation solves this problem by + requiring that it be configured with these type sizes before they + can be fetched. The readJ(Type) routines here will throw a + RuntimeException if they are called before the debugger is + configured with the Java primitive type sizes.

*/ + +public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger { + private boolean useGCC32ABI; + private boolean attached; + private long p_ps_prochandle; // native debugger handle + private boolean isCore; + + // CDebugger support + private LinuxCDebugger cdbg; + + // threadList and loadObjectList are filled by attach0 method + private List threadList; + private List loadObjectList; + + // called by native method lookupByAddress0 + private ClosestSymbol createClosestSymbol(String name, long offset) { + return new ClosestSymbol(name, offset); + } + + // called by native method attach0 + private LoadObject createLoadObject(String fileName, long textsize, + long base) { + File f = new File(fileName); + Address baseAddr = newAddress(base); + return new SharedObject(this, fileName, f.length(), baseAddr); + } + + // native methods + + private native static void init0() + throws DebuggerException; + private native void attach0(int pid) + throws DebuggerException; + private native void attach0(String execName, String coreName) + throws DebuggerException; + private native void detach0() + throws DebuggerException; + private native long lookupByName0(String objectName, String symbol) + throws DebuggerException; + private native ClosestSymbol lookupByAddress0(long address) + throws DebuggerException; + private native long[] getThreadIntegerRegisterSet0(int lwp_id) + throws DebuggerException; + private native byte[] readBytesFromProcess0(long address, long numBytes) + throws DebuggerException; + public native static int getAddressSize() ; + + // Note on Linux threads are really processes. When target process is + // attached by a serviceability agent thread, only that thread can do + // ptrace operations on the target. This is because from kernel's point + // view, other threads are just separate processes and they are not + // attached to the target. When they attempt to make ptrace calls, + // an ESRCH error will be returned as kernel believes target is not + // being traced by the caller. + // To work around the problem, we use a worker thread here to handle + // all JNI functions that are making ptrace calls. + + interface WorkerThreadTask { + public void doit(LinuxDebuggerLocal debugger) throws DebuggerException; + } + + class LinuxDebuggerLocalWorkerThread extends Thread { + LinuxDebuggerLocal debugger; + WorkerThreadTask task; + DebuggerException lastException; + + public LinuxDebuggerLocalWorkerThread(LinuxDebuggerLocal debugger) { + this.debugger = debugger; + setDaemon(true); + } + + public void run() { + synchronized (workerThread) { + for (;;) { + if (task != null) { + lastException = null; + try { + task.doit(debugger); + } catch (DebuggerException exp) { + lastException = exp; + } + task = null; + workerThread.notifyAll(); + } + + try { + workerThread.wait(); + } catch (InterruptedException x) {} + } + } + } + + public WorkerThreadTask execute(WorkerThreadTask task) throws DebuggerException { + synchronized (workerThread) { + this.task = task; + workerThread.notifyAll(); + while (this.task != null) { + try { + workerThread.wait(); + } catch (InterruptedException x) {} + } + if (lastException != null) { + throw new DebuggerException(lastException); + } else { + return task; + } + } + } + } + + private LinuxDebuggerLocalWorkerThread workerThread = null; + + //---------------------------------------------------------------------- + // Implementation of Debugger interface + // + + /**

machDesc may not be null.

+ +

useCache should be set to true if debugging is being done + locally, and to false if the debugger is being created for the + purpose of supporting remote debugging.

*/ + public LinuxDebuggerLocal(MachineDescription machDesc, + boolean useCache) throws DebuggerException { + this.machDesc = machDesc; + utils = new DebuggerUtilities(machDesc.getAddressSize(), + machDesc.isBigEndian()) { + public void checkAlignment(long address, long alignment) { + // Need to override default checkAlignment because we need to + // relax alignment constraints on Linux/x86 + if ( (address % alignment != 0) + &&(alignment != 8 || address % 4 != 0)) { + throw new UnalignedAddressException( + "Trying to read at address: " + + addressValueToString(address) + + " with alignment: " + alignment, + address); + } + } + }; + + if (useCache) { + // FIXME: re-test necessity of cache on Linux, where data + // fetching is faster + // Cache portion of the remote process's address space. + // Fetching data over the socket connection to dbx is slow. + // Might be faster if we were using a binary protocol to talk to + // dbx, but would have to test. For now, this cache works best + // if it covers the entire heap of the remote process. FIXME: at + // least should make this tunable from the outside, i.e., via + // the UI. This is a cache of 4096 4K pages, or 16 MB. The page + // size must be adjusted to be the hardware's page size. + // (FIXME: should pick this up from the debugger.) + if (getCPU().equals("ia64")) { + initCache(16384, parseCacheNumPagesProperty(1024)); + } else { + initCache(4096, parseCacheNumPagesProperty(4096)); + } + } + + workerThread = new LinuxDebuggerLocalWorkerThread(this); + workerThread.start(); + } + + /** From the Debugger interface via JVMDebugger */ + public boolean hasProcessList() throws DebuggerException { + return false; + } + + /** From the Debugger interface via JVMDebugger */ + public List getProcessList() throws DebuggerException { + throw new DebuggerException("getProcessList not implemented yet"); + } + + private void checkAttached() throws DebuggerException { + if (attached) { + if (isCore) { + throw new DebuggerException("attached to a core dump already"); + } else { + throw new DebuggerException("attached to a process already"); + } + } + } + + private void requireAttach() { + if (! attached) { + throw new RuntimeException("not attached to a process or a core!"); + } + } + + /* called from attach methods */ + private void findABIVersion() throws DebuggerException { + if (lookupByName0("libjvm.so", "__vt_10JavaThread") != 0 || + lookupByName0("libjvm_g.so", "__vt_10JavaThread") != 0) { + // old C++ ABI + useGCC32ABI = false; + } else { + // new C++ ABI + useGCC32ABI = true; + } + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized void attach(int processID) throws DebuggerException { + checkAttached(); + threadList = new ArrayList(); + loadObjectList = new ArrayList(); + class AttachTask implements WorkerThreadTask { + int pid; + public void doit(LinuxDebuggerLocal debugger) { + debugger.attach0(pid); + debugger.attached = true; + debugger.isCore = false; + findABIVersion(); + } + } + + AttachTask task = new AttachTask(); + task.pid = processID; + workerThread.execute(task); + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized void attach(String execName, String coreName) { + checkAttached(); + threadList = new ArrayList(); + loadObjectList = new ArrayList(); + attach0(execName, coreName); + attached = true; + isCore = true; + findABIVersion(); + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized boolean detach() { + if (!attached) { + return false; + } + + threadList = null; + loadObjectList = null; + + if (isCore) { + detach0(); + attached = false; + return true; + } else { + class DetachTask implements WorkerThreadTask { + boolean result = false; + + public void doit(LinuxDebuggerLocal debugger) { + debugger.detach0(); + debugger.attached = false; + result = true; + } + } + + DetachTask task = new DetachTask(); + workerThread.execute(task); + return task.result; + } + } + + /** From the Debugger interface via JVMDebugger */ + public Address parseAddress(String addressString) + throws NumberFormatException { + long addr = utils.scanAddress(addressString); + if (addr == 0) { + return null; + } + return new LinuxAddress(this, addr); + } + + /** From the Debugger interface via JVMDebugger */ + public String getOS() { + return PlatformInfo.getOS(); + } + + /** From the Debugger interface via JVMDebugger */ + public String getCPU() { + return PlatformInfo.getCPU(); + } + + public boolean hasConsole() throws DebuggerException { + return false; + } + + public String consoleExecuteCommand(String cmd) throws DebuggerException { + throw new DebuggerException("No debugger console available on Linux"); + } + + public String getConsolePrompt() throws DebuggerException { + return null; + } + + /* called from lookup */ + private long handleGCC32ABI(long addr, String symbol) throws DebuggerException { + if (useGCC32ABI && symbol.startsWith("_ZTV")) { + return addr + (2 * machDesc.getAddressSize()); + } else { + return addr; + } + } + + /** From the SymbolLookup interface via Debugger and JVMDebugger */ + public synchronized Address lookup(String objectName, String symbol) { + requireAttach(); + if (!attached) { + return null; + } + + if (isCore) { + long addr = lookupByName0(objectName, symbol); + return (addr == 0)? null : new LinuxAddress(this, handleGCC32ABI(addr, symbol)); + } else { + class LookupByNameTask implements WorkerThreadTask { + String objectName, symbol; + Address result; + + public void doit(LinuxDebuggerLocal debugger) { + long addr = debugger.lookupByName0(objectName, symbol); + result = (addr == 0 ? null : new LinuxAddress(debugger, handleGCC32ABI(addr, symbol))); + } + } + + LookupByNameTask task = new LookupByNameTask(); + task.objectName = objectName; + task.symbol = symbol; + workerThread.execute(task); + return task.result; + } + } + + /** From the SymbolLookup interface via Debugger and JVMDebugger */ + public synchronized OopHandle lookupOop(String objectName, String symbol) { + Address addr = lookup(objectName, symbol); + if (addr == null) { + return null; + } + return addr.addOffsetToAsOopHandle(0); + } + + /** From the Debugger interface */ + public MachineDescription getMachineDescription() { + return machDesc; + } + + //---------------------------------------------------------------------- + // Implementation of ThreadAccess interface + // + + /** From the ThreadAccess interface via Debugger and JVMDebugger */ + public ThreadProxy getThreadForIdentifierAddress(Address addr) { + return new LinuxThread(this, addr); + } + + /** From the ThreadAccess interface via Debugger and JVMDebugger */ + public ThreadProxy getThreadForThreadId(long id) { + return new LinuxThread(this, id); + } + + //---------------------------------------------------------------------- + // Internal routines (for implementation of LinuxAddress). + // These must not be called until the MachineDescription has been set up. + // + + /** From the LinuxDebugger interface */ + public String addressValueToString(long address) { + return utils.addressValueToString(address); + } + + /** From the LinuxDebugger interface */ + public LinuxAddress readAddress(long address) + throws UnmappedAddressException, UnalignedAddressException { + long value = readAddressValue(address); + return (value == 0 ? null : new LinuxAddress(this, value)); + } + + /** From the LinuxDebugger interface */ + public LinuxOopHandle readOopHandle(long address) + throws UnmappedAddressException, UnalignedAddressException, + NotInHeapException { + long value = readAddressValue(address); + return (value == 0 ? null : new LinuxOopHandle(this, value)); + } + + //---------------------------------------------------------------------- + // Thread context access + // + + public synchronized long[] getThreadIntegerRegisterSet(int lwp_id) + throws DebuggerException { + requireAttach(); + if (isCore) { + return getThreadIntegerRegisterSet0(lwp_id); + } else { + class GetThreadIntegerRegisterSetTask implements WorkerThreadTask { + int lwp_id; + long[] result; + public void doit(LinuxDebuggerLocal debugger) { + result = debugger.getThreadIntegerRegisterSet0(lwp_id); + } + } + + GetThreadIntegerRegisterSetTask task = new GetThreadIntegerRegisterSetTask(); + task.lwp_id = lwp_id; + workerThread.execute(task); + return task.result; + } + } + + /** Need to override this to relax alignment checks on x86. */ + public long readCInteger(long address, long numBytes, boolean isUnsigned) + throws UnmappedAddressException, UnalignedAddressException { + // Only slightly relaxed semantics -- this is a hack, but is + // necessary on x86 where it seems the compiler is + // putting some global 64-bit data on 32-bit boundaries + if (numBytes == 8) { + utils.checkAlignment(address, 4); + } else { + utils.checkAlignment(address, numBytes); + } + byte[] data = readBytes(address, numBytes); + return utils.dataToCInteger(data, isUnsigned); + } + + // Overridden from DebuggerBase because we need to relax alignment + // constraints on x86 + public long readJLong(long address) + throws UnmappedAddressException, UnalignedAddressException { + utils.checkAlignment(address, jintSize); + byte[] data = readBytes(address, jlongSize); + return utils.dataToJLong(data, jlongSize); + } + + //---------------------------------------------------------------------- + // Address access. Can not be package private, but should only be + // accessed by the architecture-specific subpackages. + + /** From the LinuxDebugger interface */ + public long getAddressValue(Address addr) { + if (addr == null) return 0; + return ((LinuxAddress) addr).getValue(); + } + + /** From the LinuxDebugger interface */ + public Address newAddress(long value) { + if (value == 0) return null; + return new LinuxAddress(this, value); + } + + /** From the LinuxCDebugger interface */ + public List/**/ getThreadList() { + requireAttach(); + return threadList; + } + + /** From the LinuxCDebugger interface */ + public List/**/ getLoadObjectList() { + requireAttach(); + return loadObjectList; + } + + /** From the LinuxCDebugger interface */ + public synchronized ClosestSymbol lookup(long addr) { + requireAttach(); + if (isCore) { + return lookupByAddress0(addr); + } else { + class LookupByAddressTask implements WorkerThreadTask { + long addr; + ClosestSymbol result; + + public void doit(LinuxDebuggerLocal debugger) { + result = debugger.lookupByAddress0(addr); + } + } + + LookupByAddressTask task = new LookupByAddressTask(); + task.addr = addr; + workerThread.execute(task); + return task.result; + } + } + + public CDebugger getCDebugger() { + if (cdbg == null) { + String cpu = getCPU(); + if (cpu.equals("ia64") ) { + // IA-64 is not supported because of stack-walking issues + return null; + } + cdbg = new LinuxCDebugger(this); + } + return cdbg; + } + + /** This reads bytes from the remote process. */ + public synchronized ReadResult readBytesFromProcess(long address, + long numBytes) throws UnmappedAddressException, DebuggerException { + requireAttach(); + if (isCore) { + byte[] res = readBytesFromProcess0(address, numBytes); + return (res != null)? new ReadResult(res) : new ReadResult(address); + } else { + class ReadBytesFromProcessTask implements WorkerThreadTask { + long address, numBytes; + ReadResult result; + public void doit(LinuxDebuggerLocal debugger) { + byte[] res = debugger.readBytesFromProcess0(address, numBytes); + if (res != null) + result = new ReadResult(res); + else + result = new ReadResult(address); + } + } + + ReadBytesFromProcessTask task = new ReadBytesFromProcessTask(); + task.address = address; + task.numBytes = numBytes; + workerThread.execute(task); + return task.result; + } + } + + public void writeBytesToProcess(long address, long numBytes, byte[] data) + throws UnmappedAddressException, DebuggerException { + // FIXME + throw new DebuggerException("Unimplemented"); + } + + static { + System.loadLibrary("saproc"); + init0(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxOopHandle.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxOopHandle.java new file mode 100644 index 00000000000..8fb9b00e96b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxOopHandle.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux; + +import sun.jvm.hotspot.debugger.*; + +class LinuxOopHandle extends LinuxAddress implements OopHandle { + LinuxOopHandle(LinuxDebugger debugger, long addr) { + super(debugger, addr); + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof LinuxOopHandle)) { + return false; + } + + return (addr == ((LinuxAddress) arg).addr); + } + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + throw new UnsupportedOperationException("addOffsetTo not applicable to OopHandles (interior object pointers not allowed)"); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("andWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("orWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("xorWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThread.java new file mode 100644 index 00000000000..687beefdc5e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThread.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux; + +import sun.jvm.hotspot.debugger.*; + +class LinuxThread implements ThreadProxy { + private LinuxDebugger debugger; + private int lwp_id; + + /** The address argument must be the address of the _thread_id in the + OSThread. It's value is result ::gettid() call. */ + LinuxThread(LinuxDebugger debugger, Address addr) { + this.debugger = debugger; + // FIXME: size of data fetched here should be configurable. + // However, making it so would produce a dependency on the "types" + // package from the debugger package, which is not desired. + this.lwp_id = (int) addr.getCIntegerAt(0, 4, true); + } + + LinuxThread(LinuxDebugger debugger, long id) { + this.debugger = debugger; + this.lwp_id = (int) id; + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof LinuxThread)) { + return false; + } + + return (((LinuxThread) obj).lwp_id == lwp_id); + } + + public int hashCode() { + return lwp_id; + } + + public String toString() { + return Integer.toString(lwp_id); + } + + public ThreadContext getContext() throws IllegalThreadStateException { + long[] data = debugger.getThreadIntegerRegisterSet(lwp_id); + ThreadContext context = LinuxThreadContextFactory.createThreadContext(debugger); + for (int i = 0; i < data.length; i++) { + context.setRegister(i, data[i]); + } + return context; + } + + public boolean canSetContext() throws DebuggerException { + return false; + } + + public void setContext(ThreadContext context) + throws IllegalThreadStateException, DebuggerException { + throw new DebuggerException("Unimplemented"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java new file mode 100644 index 00000000000..91da2eca876 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.linux.amd64.*; +import sun.jvm.hotspot.debugger.linux.ia64.*; +import sun.jvm.hotspot.debugger.linux.x86.*; +import sun.jvm.hotspot.debugger.linux.sparc.*; + +class LinuxThreadContextFactory { + static ThreadContext createThreadContext(LinuxDebugger dbg) { + String cpu = dbg.getCPU(); + if (cpu.equals("x86")) { + return new LinuxX86ThreadContext(dbg); + } else if (cpu.equals("amd64")) { + return new LinuxAMD64ThreadContext(dbg); + } else if (cpu.equals("ia64")) { + return new LinuxIA64ThreadContext(dbg); + } else if (cpu.equals("sparc")) { + return new LinuxSPARCThreadContext(dbg); + } else { + throw new RuntimeException("cpu " + cpu + " is not yet supported"); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/SharedObject.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/SharedObject.java new file mode 100644 index 00000000000..680ed25a387 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/SharedObject.java @@ -0,0 +1,48 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.posix.*; + +/** A Object can represent either a .so or an a.out file. */ + +class SharedObject extends DSO { + SharedObject(LinuxDebugger dbg, String filename, long size, Address relocation) { + super(filename, size, relocation); + this.dbg = dbg; + } + + protected Address newAddress(long address) { + return dbg.newAddress(address); + } + + protected long getAddressValue(Address addr) { + return dbg.getAddressValue(addr); + } + + private LinuxDebugger dbg; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java new file mode 100644 index 00000000000..f8577beca6d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java @@ -0,0 +1,75 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.linux.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.*; + +final public class LinuxAMD64CFrame extends BasicCFrame { + public LinuxAMD64CFrame(LinuxDebugger dbg, Address rbp, Address rip) { + super(dbg.getCDebugger()); + this.rbp = rbp; + this.rip = rip; + this.dbg = dbg; + } + + // override base class impl to avoid ELF parsing + public ClosestSymbol closestSymbolToPC() { + // try native lookup in debugger. + return dbg.lookup(dbg.getAddressValue(pc())); + } + + public Address pc() { + return rip; + } + + public Address localVariableBase() { + return rbp; + } + + public CFrame sender() { + if (rbp == null) { + return null; + } + + Address nextRBP = rbp.getAddressAt( 0 * ADDRESS_SIZE); + if (nextRBP == null) { + return null; + } + Address nextPC = rbp.getAddressAt( 1 * ADDRESS_SIZE); + if (nextPC == null) { + return null; + } + return new LinuxAMD64CFrame(dbg, nextRBP, nextPC); + } + + // package/class internals only + private static final int ADDRESS_SIZE = 8; + private Address rip; + private Address rbp; + private LinuxDebugger dbg; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64ThreadContext.java new file mode 100644 index 00000000000..7442d3dd03d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64ThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.linux.*; + +public class LinuxAMD64ThreadContext extends AMD64ThreadContext { + private LinuxDebugger debugger; + + public LinuxAMD64ThreadContext(LinuxDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/ia64/LinuxIA64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/ia64/LinuxIA64ThreadContext.java new file mode 100644 index 00000000000..d7a8ec7fc83 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/ia64/LinuxIA64ThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux.ia64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.ia64.*; +import sun.jvm.hotspot.debugger.linux.*; + +public class LinuxIA64ThreadContext extends IA64ThreadContext { + private LinuxDebugger debugger; + + public LinuxIA64ThreadContext(LinuxDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/sparc/LinuxSPARCCFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/sparc/LinuxSPARCCFrame.java new file mode 100644 index 00000000000..9b40cee0072 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/sparc/LinuxSPARCCFrame.java @@ -0,0 +1,81 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.linux.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.*; + +final public class LinuxSPARCCFrame extends BasicCFrame { + // package/class internals only + + public LinuxSPARCCFrame(LinuxDebugger dbg, Address sp, Address pc, int address_size) { + super(dbg.getCDebugger()); + this.sp = sp; + this.pc = pc; + this.dbg = dbg; + this.address_size=address_size; + if (address_size==8) SPARC_STACK_BIAS = 0x7ff; + else SPARC_STACK_BIAS = 0x0; + } + + // override base class impl to avoid ELF parsing + public ClosestSymbol closestSymbolToPC() { + // try native lookup in debugger. + return dbg.lookup(dbg.getAddressValue(pc())); + } + + public Address pc() { + return pc; + } + + public Address localVariableBase() { + return sp; + } + + public CFrame sender() { + if (sp == null) { + return null; + } + + Address nextSP = sp.getAddressAt( SPARCThreadContext.R_SP * address_size + SPARC_STACK_BIAS); + if (nextSP == null) { + return null; + } + Address nextPC = sp.getAddressAt(SPARCThreadContext.R_O7 * address_size + SPARC_STACK_BIAS); + if (nextPC == null) { + return null; + } + return new LinuxSPARCCFrame(dbg, nextSP, nextPC,address_size); + } + + public static int SPARC_STACK_BIAS; + private static int address_size; + private Address pc; + private Address sp; + private LinuxDebugger dbg; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/sparc/LinuxSPARCThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/sparc/LinuxSPARCThreadContext.java new file mode 100644 index 00000000000..a31506d7753 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/sparc/LinuxSPARCThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.linux.*; + +public class LinuxSPARCThreadContext extends SPARCThreadContext { + private LinuxDebugger debugger; + + public LinuxSPARCThreadContext(LinuxDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/x86/LinuxX86CFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/x86/LinuxX86CFrame.java new file mode 100644 index 00000000000..8f729a0fca5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/x86/LinuxX86CFrame.java @@ -0,0 +1,75 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.linux.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.*; + +final public class LinuxX86CFrame extends BasicCFrame { + // package/class internals only + public LinuxX86CFrame(LinuxDebugger dbg, Address ebp, Address pc) { + super(dbg.getCDebugger()); + this.ebp = ebp; + this.pc = pc; + this.dbg = dbg; + } + + // override base class impl to avoid ELF parsing + public ClosestSymbol closestSymbolToPC() { + // try native lookup in debugger. + return dbg.lookup(dbg.getAddressValue(pc())); + } + + public Address pc() { + return pc; + } + + public Address localVariableBase() { + return ebp; + } + + public CFrame sender() { + if (ebp == null) { + return null; + } + + Address nextEBP = ebp.getAddressAt( 0 * ADDRESS_SIZE); + if (nextEBP == null) { + return null; + } + Address nextPC = ebp.getAddressAt( 1 * ADDRESS_SIZE); + if (nextPC == null) { + return null; + } + return new LinuxX86CFrame(dbg, nextEBP, nextPC); + } + + private static final int ADDRESS_SIZE = 4; + private Address pc; + private Address ebp; + private LinuxDebugger dbg; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/x86/LinuxX86ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/x86/LinuxX86ThreadContext.java new file mode 100644 index 00000000000..79457ddf2cf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/x86/LinuxX86ThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.linux.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.linux.*; + +public class LinuxX86ThreadContext extends X86ThreadContext { + private LinuxDebugger debugger; + + public LinuxX86ThreadContext(LinuxDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/AddressDataSource.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/AddressDataSource.java new file mode 100644 index 00000000000..cf1abfb508a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/AddressDataSource.java @@ -0,0 +1,99 @@ +/* + * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +package sun.jvm.hotspot.debugger.posix; + +import java.io.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.posix.elf.*; + +class AddressDataSource implements DataSource { + AddressDataSource(Address addr) { + this.addr = addr; + offset = 0; + } + + public byte readByte() throws IOException { + try { + byte res = (byte) addr.getCIntegerAt(offset, 1, false); + ++offset; + return res; + } catch (UnmappedAddressException e) { + throw (IOException) new IOException("Unmapped address at 0x" + + Long.toHexString(e.getAddress())).initCause(e); + } catch (DebuggerException e) { + throw (IOException) new IOException().initCause(e); + } + } + + public short readShort() throws IOException { + // NOTE: byte swapping is taken care of at the ELFFileImpl level + int b1 = readByte() & 0xFF; + int b2 = readByte() & 0xFF; + return (short) ((b1 << 8) | b2); + } + + public int readInt() throws IOException { + // NOTE: byte swapping is taken care of at the ELFFileImpl level + int b1 = ((int) readByte()) & 0xFF; + int b2 = ((int) readByte()) & 0xFF; + int b3 = ((int) readByte()) & 0xFF; + int b4 = ((int) readByte()) & 0xFF; + return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4); + } + + public long readLong() throws IOException { + // NOTE: byte swapping is taken care of at the ELFFileImpl level + long b1 = ((long) readByte()) & 0xFFL; + long b2 = ((long) readByte()) & 0xFFL; + long b3 = ((long) readByte()) & 0xFFL; + long b4 = ((long) readByte()) & 0xFFL; + long b5 = ((long) readByte()) & 0xFFL; + long b6 = ((long) readByte()) & 0xFFL; + long b7 = ((long) readByte()) & 0xFFL; + long b8 = ((long) readByte()) & 0xFFL; + return (((((b1 << 24) | (b2 << 16) | (b3 << 8) | b4)) << 32) | + ((((b5 << 24) | (b6 << 16) | (b7 << 8) | b8)))); + } + + public int read(byte[] b) throws IOException { + for (int i = 0; i < b.length; i++) { + b[i] = readByte(); + } + return b.length; + } + + public void seek(long pos) throws IOException { + offset = pos; + } + + public long getFilePointer() throws IOException { + return offset; + } + + public void close() throws IOException { + } + + private Address addr; + private long offset; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/DSO.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/DSO.java new file mode 100644 index 00000000000..43c88f9f098 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/DSO.java @@ -0,0 +1,160 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +package sun.jvm.hotspot.debugger.posix; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.posix.elf.*; +import sun.jvm.hotspot.utilities.memo.*; + +/** Provides a simple wrapper around the ELF library which handles + relocation. */ +public abstract class DSO implements LoadObject { + + private MemoizedObject file; // contains ELFFile + private String filename; + private Address addr; + private long size; + private IsDSO dso = new IsDSO(); + + class IsDSO extends MemoizedBoolean { + protected boolean computeValue() { + return getFile().getHeader().getFileType() == ELFHeader.FT_DYN; + } + }; + + class ELFFileByName extends MemoizedObject { + protected Object computeValue() { + return ELFFileParser.getParser().parse(DSO.this.filename); + } + }; + + class ELFFileByAddress extends MemoizedObject { + protected Object computeValue() { + return ELFFileParser.getParser().parse(new AddressDataSource(DSO.this.addr)); + } + }; + + public DSO(String filename, long size, Address relocation) throws ELFException { + this.filename = filename; + this.size = size; + this.addr = relocation; + this.file = new ELFFileByName(); + } + + public DSO(long size, Address relocation) throws ELFException { + this.addr = relocation; + this.size = size; + this.file = new ELFFileByAddress(); + } + + public String getName() { + return filename; + } + + public Address getBase() { + return addr; + } + + /** if this .so is unloaded and re-loaded in the same process at a different + base, change the base by calling this to avoid re-parsing the ELF. */ + public void setBase(Address newBase) { + addr = newBase; + if (filename == null) { + // ELFFile was created by address. we have to re-parse it. + file = new ELFFileByAddress(); + dso = new IsDSO(); + } + } + + public long getSize() { + return size; + } + + public CDebugInfoDataBase getDebugInfoDataBase() throws DebuggerException { + // FIXME: after stabs parser + return null; + } + + public BlockSym debugInfoForPC(Address pc) throws DebuggerException { + // FIXME: after stabs parser + return null; + } + + public ClosestSymbol closestSymbolToPC(Address pcAsAddr) throws DebuggerException { + boolean dso = isDSO(); + long offset = dso? pcAsAddr.minus(addr) : getAddressValue(pcAsAddr); + ELFSymbol sym = getFile().getHeader().getELFSymbol(offset); + return (sym != null)? createClosestSymbol(sym.getName(), offset - sym.getValue()) : null; + } + + public LineNumberInfo lineNumberForPC(Address pc) throws DebuggerException { + // FIXME: after stabs parser + return null; + } + + /** return true if file is a .so */ + public boolean isDSO() { + return dso.getValue(); + } + + /** Look up a symbol; returns absolute address or null if symbol was + not found. */ + public Address lookupSymbol(String symbol) throws ELFException { + ELFSymbol sym = getFile().getHeader().getELFSymbol(symbol); + if (sym == null) { + return null; + } + + long value = sym.getValue(); + if (isDSO()) { + return addr.addOffsetTo(value); + } else { + return newAddress(value); + } + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof DSO)) { + return false; + } + DSO other = (DSO)o; + return getBase().equals(other.getBase()); + } + + public int hashCode() { + return getBase().hashCode(); + } + + protected ELFFile getFile() { + return (ELFFile) file.getValue(); + } + + protected abstract Address newAddress(long addr); + protected abstract long getAddressValue(Address addr); + + protected ClosestSymbol createClosestSymbol(String name, long diff) { + return new ClosestSymbol(name, diff); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFException.java new file mode 100644 index 00000000000..8636aeda54d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFException.java @@ -0,0 +1,48 @@ +/* + * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.posix.elf; + +/** Generic exception class for all exceptions which occur in this + package. Since there is no mechanism built into this library for + recovering from errors, the best clients can do is display the + error string. */ + +public class ELFException extends RuntimeException { + public ELFException() { + super(); + } + + public ELFException(String message) { + super(message); + } + + public ELFException(Throwable cause) { + super(cause); + } + + public ELFException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFFile.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFFile.java new file mode 100644 index 00000000000..2ffa9a474cd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFFile.java @@ -0,0 +1,71 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.posix.elf; + +public interface ELFFile { + /** ELF magic number. */ + public static final byte ELF_MAGIC_NUMBER[] = { 0x7f, 'E', 'L', 'F' }; + + public static final byte CLASS_INVALID = 0; + /** 32-bit objects. */ + public static final byte CLASS_32 = 1; + /** 64-bit objects. */ + public static final byte CLASS_64 = 2; + + /** No data encoding. */ + public static final byte DATA_INVALID = 0; + /** LSB data encoding. */ + public static final byte DATA_LSB = 1; + /** MSB data encoding. */ + public static final byte DATA_MSB = 2; + + /** No ELF header version. */ + public static final byte VERSION_INVALID = 0; + /** Current ELF header version. */ + public static final byte VERSION_CURRENT = 1; + + public static final byte NDX_MAGIC_0 = 0; + public static final byte NDX_MAGIC_1 = 1; + public static final byte NDX_MAGIC_2 = 2; + public static final byte NDX_MAGIC_3 = 3; + public static final byte NDX_OBJECT_SIZE = 4; + public static final byte NDX_ENCODING = 5; + public static final byte NDX_VERSION = 6; + + public ELFHeader getHeader(); + public void close(); + + /** Returns the 4 byte magic number for this file. This value should + * match the values in ELF_MAGIC_NUMBER. */ + public byte[] getMagicNumber(); + /** Returns a byte identifying the size of objects used for this ELF + * file. The byte will be either CLASS_INVALID, CLASS_32 or CLASS_64. */ + public byte getObjectSize(); + /** Returns a byte identifying the data encoding of the processor specific + * data. This byte will be either DATA_INVALID, DATA_LSB or DATA_MSB. */ + public byte getEncoding(); + /** Returns one of the version constants. This should be VERSION_CURRENT. */ + public byte getVersion(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFFileParser.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFFileParser.java new file mode 100644 index 00000000000..47417d39a02 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFFileParser.java @@ -0,0 +1,1097 @@ +/* + * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.posix.elf; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.utilities.memo.*; +import sun.jvm.hotspot.debugger.DataSource; +import sun.jvm.hotspot.debugger.RandomAccessFileDataSource; + +public class ELFFileParser { + private static ELFFileParser elfParser; + private static final String US_ASCII = "US-ASCII"; + + public static ELFFileParser getParser() { + if (elfParser == null) { + elfParser = new ELFFileParser(); + } + return elfParser; + } + + /** + * Parses the data in filename and returns the ELFFile representation. + */ + public ELFFile parse(String filename) throws ELFException { + try { + RandomAccessFile file = new RandomAccessFile(filename, "r"); + return parse(new RandomAccessFileDataSource(file)); + } catch (FileNotFoundException e) { + throw new ELFException(e); + } + } + + /** + * Parses the data source and returns the ELFFile representation. + */ + public ELFFile parse(DataSource source) throws ELFException { + return new ELFFileImpl(source); + } + + /** + * Implementation of the ELFFile interface. + */ + class ELFFileImpl implements ELFFile { + private DataSource file; + private ELFHeader header; + private byte ident[] = new byte[16]; + + ELFFileImpl(DataSource file) throws ELFException { + this.file = file; + int bytesRead = readBytes(ident); + if (bytesRead != ident.length) { + throw new ELFException("Error reading elf header (read " + + bytesRead + "bytes, expected to " + + "read " + ident.length + "bytes)."); + } + + // Check the magic number before we continue reading the file. + if (!Arrays.equals(getMagicNumber(), ELF_MAGIC_NUMBER)) { + throw new ELFException("Bad magic number for file."); + } + + header = new ELFHeaderImpl(); + } + + public ELFHeader getHeader() { return header; } + + public byte[] getMagicNumber() { + byte magicNumber[] = new byte[4]; + magicNumber[0] = ident[NDX_MAGIC_0]; + magicNumber[1] = ident[NDX_MAGIC_1]; + magicNumber[2] = ident[NDX_MAGIC_2]; + magicNumber[3] = ident[NDX_MAGIC_3]; + return magicNumber; + } + + public byte getObjectSize() { return ident[NDX_OBJECT_SIZE]; } + public byte getEncoding() { return ident[NDX_ENCODING]; } + public byte getVersion() { return ident[NDX_VERSION]; } + + + /** + * Implementation of the ELFHeader interface. + */ + class ELFHeaderImpl implements ELFHeader { + /** Marks the file as an object file and provide machine-independent + * data so the contents may be decoded and interpreted. */ + private byte ident[] = new byte[16]; // unsigned char + /** Identifies the object file type. */ + private short file_type; // Elf32_Half + /** The required architecture. */ + private short arch; // Elf32_Half + /** Version */ + private int version; // Elf32_Word + /** Virtual address to which the system first transfers control. + * If there is no entry point for the file the value is 0. */ + private int entry_point; // Elf32_Addr + /** Program header table offset in bytes. If there is no program + * header table the value is 0. */ + private int ph_offset; // Elf32_Off + /** Section header table offset in bytes. If there is no section + * header table the value is 0. */ + private int sh_offset; // Elf32_Off + /** Processor specific flags. */ + private int flags; // Elf32_Word + /** ELF header size in bytes. */ + private short eh_size; // Elf32_Half + /** Size of one entry in the file's program header table in bytes. + * All entries are the same size. */ + private short ph_entry_size; // Elf32_Half + /** Number of entries in the program header table, 0 if no + * entries. */ + private short num_ph; // Elf32_Half + /** Section header entry size in bytes. */ + private short sh_entry_size; // Elf32_Half + /** Number of entries in the section header table, 0 if no + * entries. */ + private short num_sh; // Elf32_Half + /** Index into the section header table associated with the section + * name string table. SH_UNDEF if there is no section name string + * table. */ + private short sh_string_ndx; // Elf32_Half + + /** MemoizedObject array of section headers associated with this + * ELF file. */ + private MemoizedObject[] sectionHeaders; + /** MemoizedObject array of program headers associated with this + * ELF file. */ + private MemoizedObject[] programHeaders; + + /** Used to cache symbol table lookup. */ + private ELFSectionHeader symbolTableSection; + /** Used to cache dynamic symbol table lookup. */ + private ELFSectionHeader dynamicSymbolTableSection; + /** Used to cache hash table lookup. */ + private ELFHashTable hashTable; + + /** + * Reads the ELF header and sets up the section and program headers + * in memoized arrays. + */ + ELFHeaderImpl() throws ELFException { + file_type = readShort(); + arch = readShort(); + version = readInt(); + entry_point = readInt(); + ph_offset = readInt(); + sh_offset = readInt(); + flags = readInt(); + eh_size = readShort(); + ph_entry_size = readShort(); + num_ph = readShort(); + sh_entry_size = readShort(); + num_sh = readShort(); + sh_string_ndx = readShort(); + + // Set up the section headers + sectionHeaders = new MemoizedObject[num_sh]; + for (int i = 0; i < num_sh; i++) { + final long sectionHeaderOffset = + (long)(sh_offset + (i * sh_entry_size)); + sectionHeaders[i] = new MemoizedObject() { + public Object computeValue() { + return new ELFSectionHeaderImpl(sectionHeaderOffset); + } + }; + } + +// // Set up the program headers +// programHeaders = new MemoizedObject[num_sh]; +// for (int i = 0; i < num_sh; i++) { +// final long programHeaderOffset = +// (long)(ph_offset + (i * ph_entry_size)); +// programHeaders[i] = new MemoizedObject() { +// public Object computeValue() { +// return new ProgramHeaderImpl(programHeaderOffset); +// } +// }; +// } + } + + public short getFileType() { return file_type; } + public short getArch() { return arch; } + public short getSectionHeaderSize() { return sh_entry_size; } + public short getNumberOfSectionHeaders() { return num_sh; } + +// public short getProgramHeaderSize() { return ph_entry_size; } +// public short getNumberOfProgramHeaders() { return num_ph; } + + + /** + * Returns the section header at the specified index. The section + * header at index 0 is defined as being a undefined section. */ + public ELFSectionHeader getSectionHeader(int index) { + return (ELFSectionHeader)sectionHeaders[index].getValue(); + } + + public ELFStringTable getSectionHeaderStringTable() { + return getSectionHeader(sh_string_ndx).getStringTable(); + } + + public ELFStringTable getStringTable() { + return findStringTableWithName(ELFSectionHeader.STRING_TABLE_NAME); + } + + public ELFStringTable getDynamicStringTable() { + return findStringTableWithName( + ELFSectionHeader.DYNAMIC_STRING_TABLE_NAME); + } + + private ELFStringTable findStringTableWithName(String tableName) { + // Loop through the section header and look for a section + // header with the name "tableName". We can ignore entry 0 + // since it is defined as being undefined. + ELFSectionHeader sh = null; + for (int i = 1; i < getNumberOfSectionHeaders(); i++) { + sh = getSectionHeader(i); + if (tableName.equals(sh.getName())) { + return sh.getStringTable(); + } + } + return null; + } + + /** + * The ELFHashTable does not currently work. This method will + * always return null. */ + public ELFHashTable getHashTable() { +// if (hashTable != null) { +// return hashTable; +// } +// +// ELFHashTable ht = null; +// for (int i = 1; i < getNumberOfSectionHeaders(); i++) { +// if ((ht = getSectionHeader(i).getHashTable()) != null) { +// hashTable = ht; +// return hashTable; +// } +// } + return null; + } + + public ELFSectionHeader getSymbolTableSection() { + if (symbolTableSection != null) { + return symbolTableSection; + } + + symbolTableSection = + getSymbolTableSection(ELFSectionHeader.TYPE_SYMTBL); + return symbolTableSection; + } + + public ELFSectionHeader getDynamicSymbolTableSection() { + if (dynamicSymbolTableSection != null) { + return dynamicSymbolTableSection; + } + + dynamicSymbolTableSection = + getSymbolTableSection(ELFSectionHeader.TYPE_DYNSYM); + return dynamicSymbolTableSection; + } + + private ELFSectionHeader getSymbolTableSection(int type) { + ELFSectionHeader sh = null; + for (int i = 1; i < getNumberOfSectionHeaders(); i++) { + sh = getSectionHeader(i); + if (sh.getType() == type) { + dynamicSymbolTableSection = sh; + return sh; + } + } + return null; + } + + public ELFSymbol getELFSymbol(String symbolName) { + if (symbolName == null) { + return null; + } + + // Check dynamic symbol table for symbol name. + ELFSymbol symbol = null; + int numSymbols = 0; + ELFSectionHeader sh = getDynamicSymbolTableSection(); + if (sh != null) { + numSymbols = sh.getNumberOfSymbols(); + for (int i = 0; i < Math.ceil(numSymbols / 2); i++) { + if (symbolName.equals( + (symbol = sh.getELFSymbol(i)).getName())) { + return symbol; + } else if (symbolName.equals( + (symbol = sh.getELFSymbol( + numSymbols - 1 - i)).getName())) { + return symbol; + } + } + } + + // Check symbol table for symbol name. + sh = getSymbolTableSection(); + if (sh != null) { + numSymbols = sh.getNumberOfSymbols(); + for (int i = 0; i < Math.ceil(numSymbols / 2); i++) { + if (symbolName.equals( + (symbol = sh.getELFSymbol(i)).getName())) { + return symbol; + } else if (symbolName.equals( + (symbol = sh.getELFSymbol( + numSymbols - 1 - i)).getName())) { + return symbol; + } + } + } + return null; + } + + public ELFSymbol getELFSymbol(long address) { + // Check dynamic symbol table for address. + ELFSymbol symbol = null; + int numSymbols = 0; + long value = 0L; + + ELFSectionHeader sh = getDynamicSymbolTableSection(); + if (sh != null) { + numSymbols = sh.getNumberOfSymbols(); + for (int i = 0; i < numSymbols; i++) { + symbol = sh.getELFSymbol(i); + value = symbol.getValue(); + if (address >= value && address < value + symbol.getSize()) { + return symbol; + } + } + } + + // Check symbol table for symbol name. + sh = getSymbolTableSection(); + if (sh != null) { + numSymbols = sh.getNumberOfSymbols(); + for (int i = 0; i < numSymbols; i++) { + symbol = sh.getELFSymbol(i); + value = symbol.getValue(); + if (address >= value && address < value + symbol.getSize()) { + return symbol; + } + } + } + return null; + } + +// public ProgramHeader getProgramHeader(int index) { +// return (ProgramHeader)programHeaders[index].getValue(); +// } + } + + + /** + * Implementation of the ELFSectionHeader interface. + */ + class ELFSectionHeaderImpl implements ELFSectionHeader { + /** Index into the section header string table which gives the + * name of the section. */ + private int name_ndx; // Elf32_Word + /** Section content and semantics. */ + private int type; // Elf32_Word + /** Flags. */ + private int flags; // Elf32_Word + /** If the section will be in the memory image of a process this + * will be the address at which the first byte of section will be + * loaded. Otherwise, this value is 0. */ + private int address; // Elf32_Addr + /** Offset from beginning of file to first byte of the section. */ + private int section_offset; // Elf32_Off + /** Size in bytes of the section. TYPE_NOBITS is a special case. */ + private int size; // Elf32_Word + /** Section header table index link. */ + private int link; // Elf32_Word + /** Extra information determined by the section type. */ + private int info; // Elf32_Word + /** Address alignment constraints for the section. */ + private int address_alignment; // Elf32_Word + /** Size of a fixed-size entry, 0 if none. */ + private int entry_size; // Elf32_Word + + /** Memoized symbol table. */ + private MemoizedObject[] symbols; + /** Memoized string table. */ + private MemoizedObject stringTable; + /** Memoized hash table. */ + private MemoizedObject hashTable; + + /** + * Reads the section header information located at offset. + */ + ELFSectionHeaderImpl(long offset) throws ELFException { + seek(offset); + name_ndx = readInt(); + type = readInt(); + flags = readInt(); + address = readInt(); + section_offset = readInt(); + size = readInt(); + link = readInt(); + info = readInt(); + address_alignment = readInt(); + entry_size = readInt(); + + switch (type) { + case ELFSectionHeader.TYPE_NULL: + break; + case ELFSectionHeader.TYPE_PROGBITS: + break; + case ELFSectionHeader.TYPE_SYMTBL: + case ELFSectionHeader.TYPE_DYNSYM: + // Setup the symbol table. + int num_entries = size / entry_size; + symbols = new MemoizedObject[num_entries]; + for (int i = 0; i < num_entries; i++) { + final int symbolOffset = section_offset + + (i * entry_size); + symbols[i] = new MemoizedObject() { + public Object computeValue() { + return new ELFSymbolImpl(symbolOffset,type); + } + }; + } + break; + case ELFSectionHeader.TYPE_STRTBL: + // Setup the string table. + final int strTableOffset = section_offset; + final int strTableSize = size; + stringTable = new MemoizedObject() { + public Object computeValue() { + return new ELFStringTableImpl(strTableOffset, + strTableSize); + } + }; + break; + case ELFSectionHeader.TYPE_RELO_EXPLICIT: + break; + case ELFSectionHeader.TYPE_HASH: + final int hashTableOffset = section_offset; + final int hashTableSize = size; + hashTable = new MemoizedObject() { + public Object computeValue() { + return new ELFHashTableImpl(hashTableOffset, + hashTableSize); + } + }; + break; + case ELFSectionHeader.TYPE_DYNAMIC: + break; + case ELFSectionHeader.TYPE_NOTE: + break; + case ELFSectionHeader.TYPE_NOBITS: + break; + case ELFSectionHeader.TYPE_RELO: + break; + case ELFSectionHeader.TYPE_SHLIB: + break; + default: + break; + } + } + + public int getType() { + return type; + } + + public int getNumberOfSymbols() { + if (symbols != null) { + return symbols.length; + } + return 0; + } + + /** + * Returns the ELFSymbol at the specified index. Index 0 is + * reserved for the undefined ELF symbol. */ + public ELFSymbol getELFSymbol(int index) { + return (ELFSymbol)symbols[index].getValue(); + } + + public ELFStringTable getStringTable() { + if (stringTable != null) { + return (ELFStringTable)stringTable.getValue(); + } + return null; + } + + /** + * The ELFHashTable does not currently work. This method will + * always return null. */ + public ELFHashTable getHashTable() { + if (hashTable != null) { + return (ELFHashTable)hashTable.getValue(); + } + return null; + } + + public String getName() { + if (name_ndx == 0) { + return null; + } + + ELFStringTable tbl = getHeader().getSectionHeaderStringTable(); + return tbl.get(name_ndx); + } + + public int getLink() { + return link; + } + + public int getOffset() { + return section_offset; + } + } + + +// class ProgramHeaderImpl implements ProgramHeader { +// /** Defines the kind of segment this element describes. */ +// private int type; // Elf32_Word +// /** Offset from the beginning of the file. */ +// private int offset; // Elf32_Off +// /** Virtual address at which the first byte of the segment +// * resides in memory. */ +// private int virtual_address; // Elf32_Addr +// /** Reserved for the physical address of the segment on systems +// * where physical addressinf is relevant. */ +// private int physical_address; // Elf32_addr +// /** File image size of segment in bytes, may be 0. */ +// private int file_size; // Elf32_Word +// /** Memory image size of segment in bytes, may be 0. */ +// private int mem_size; // Elf32_Word +// /** Flags relevant to this segment. Values for flags are defined +// * in ELFSectionHeader. */ +// private int flags; // Elf32_Word +// private int alignment; // Elf32_Word +// +// private MemoizedObject[] symbols; +// +// ProgramHeaderImpl(long offset) throws ELFException { +// seek(offset); +// type = readInt(); +// this.offset = readInt(); +// virtual_address = readInt(); +// physical_address = readInt(); +// file_size = readInt(); +// mem_size = readInt(); +// flags = readInt(); +// alignment = readInt(); +// +// switch (type) { +// case ELFSectionHeader.TYPE_NULL: +// break; +// case ELFSectionHeader.TYPE_PROGBITS: +// break; +// case ELFSectionHeader.TYPE_SYMTBL: +// case ELFSectionHeader.TYPE_DYNSYM: +// break; +// case ELFSectionHeader.TYPE_STRTBL: +// // Setup the string table. +// final int strTableOffset = section_offset; +// final int strTableSize = size; +// stringTable = new MemoizedObject() { +// public Object computeValue() { +// return new ELFStringTableImpl(strTableOffset, +// strTableSize); +// } +// }; +// new ELFStringTableImpl(offset, file_size); +// break; +// case ELFSectionHeader.TYPE_RELO_EXPLICIT: +// break; +// case ELFSectionHeader.TYPE_HASH: +// break; +// case ELFSectionHeader.TYPE_DYNAMIC: +// break; +// case ELFSectionHeader.TYPE_NOTE: +// break; +// case ELFSectionHeader.TYPE_NOBITS: +// break; +// case ELFSectionHeader.TYPE_RELO: +// break; +// case ELFSectionHeader.TYPE_SHLIB: +// break; +// default: +// break; +// } +// } +// +// public int getType() { +// return type; +// } +// } + + + /** + * Implementation of the ELFSymbol interface. + */ + class ELFSymbolImpl implements ELFSymbol { + /** Index into the symbol string table that holds the character + * representation of the symbols. 0 means the symbol has no + * character name. */ + private int name_ndx; // Elf32_Word + /** Value of the associated symbol. This may be an address or + * an absolute value. */ + private int value; // Elf32_Addr + /** Size of the symbol. 0 if the symbol has no size or the size + * is unknown. */ + private int size; // Elf32_Word + /** Specifies the symbol type and beinding attributes. */ + private byte info; // unsigned char + /** Currently holds the value of 0 and has no meaning. */ + private byte other; // unsigned char + /** Index to the associated section header. This value will need + * to be read as an unsigned short if we compare it to + * ELFSectionHeader.NDX_LORESERVE and ELFSectionHeader.NDX_HIRESERVE. */ + private short section_header_ndx; // Elf32_Half + + private int section_type; + + /** Offset from the beginning of the file to this symbol. */ + private long offset; + + ELFSymbolImpl(long offset, int section_type) throws ELFException { + seek(offset); + this.offset = offset; + name_ndx = readInt(); + value = readInt(); + size = readInt(); + info = readByte(); + other = readByte(); + section_header_ndx = readShort(); + + this.section_type = section_type; + + switch (getType()) { + case TYPE_NOOBJECT: + break; + case TYPE_OBJECT: + break; + case TYPE_FUNCTION: + break; + case TYPE_SECTION: + break; + case TYPE_FILE: + break; + case TYPE_LOPROC: + break; + case TYPE_HIPROC: + break; + default: + break; + } + } + + public int getBinding() { return info >> 4; } + public int getType() { return info & 0x0F; } + public long getOffset() { return offset; } + + public String getName() { + // Check to make sure this symbol has a name. + if (name_ndx == 0) { + return null; + } + + // Retrieve the name of the symbol from the correct string + // table. + String symbol_name = null; + if (section_type == ELFSectionHeader.TYPE_SYMTBL) { + symbol_name = getHeader().getStringTable().get(name_ndx); + } else if (section_type == ELFSectionHeader.TYPE_DYNSYM) { + symbol_name = + getHeader().getDynamicStringTable().get(name_ndx); + } + return symbol_name; + } + + public long getValue() { + return value; + } + + public int getSize() { + return size; + } + } + + /** + * Implementation of the ELFStringTable interface. + */ + class ELFStringTableImpl implements ELFStringTable { + /** The string table data. */ + private byte data[]; + private int numStrings; + + /** + * Reads all the strings from [offset, length]. + */ + ELFStringTableImpl(long offset, int length) throws ELFException { + seek(offset); + data = new byte[length]; + int bytesRead = readBytes(data); + if (bytesRead != length) { + throw new ELFException("Error reading string table (read " + + bytesRead + "bytes, expected to " + + "read " + data.length + "bytes)."); + } + + // Count the strings. + numStrings = 0; + for (int ptr = 0; ptr < data.length; ptr++) { + if (data[ptr] == '\0') { + numStrings++; + } + } + } + + public String get(int index) { + int startPtr = index; + int endPtr = index; + while (data[endPtr] != '\0') { + endPtr++; + } + return new String(data, startPtr, endPtr - startPtr); + } + + public int getNumStrings() { + return numStrings; + } + } + + + /** Implementation of the ELFHashTable. */ + class ELFHashTableImpl implements ELFHashTable { + private int num_buckets; + private int num_chains; + + // These could probably be memoized. + private int buckets[]; + private int chains[]; + + ELFHashTableImpl(long offset, int length) throws ELFException { + seek(offset); + num_buckets = readInt(); + num_chains = readInt(); + + buckets = new int[num_buckets]; + chains = new int[num_chains]; + // Read the bucket data. + for (int i = 0; i < num_buckets; i++) { + buckets[i] = readInt(); + } + + // Read the chain data. + for (int i = 0; i < num_chains; i++) { + chains[i] = readInt(); + } + + // Make sure that the amount of bytes we were supposed to read + // was what we actually read. + int actual = num_buckets * 4 + num_chains * 4 + 8; + if (length != actual) { + throw new ELFException("Error reading string table (read " + + actual + "bytes, expected to " + + "read " + length + "bytes)."); + } + } + + /** + * This method doesn't work every time and is unreliable. Use + * ELFSection.getELFSymbol(String) to retrieve symbols by name. + * NOTE: since this method is currently broken it will always + * return null. */ + public ELFSymbol getSymbol(String symbolName) { +// if (symbolName == null) { +// return null; +// } +// +// long hash = 0; +// long g = 0; +// +// for (int i = 0; i < symbolName.length(); i++) { +// hash = (hash << 4) + symbolName.charAt(i); +// if ((g = hash & 0xf0000000) != 0) { +// hash ^= g >>> 24; +// } +// hash &= ~g; +// } +// +// ELFSymbol symbol = null; +// ELFSectionHeader dyn_sh = +// getHeader().getDynamicSymbolTableSection(); +// int index = (int)hash % num_buckets; +// while(index != 0) { +// symbol = dyn_sh.getELFSymbol(index); +// if (symbolName.equals(symbol.getName())) { +// break; +// } +// symbol = null; +// index = chains[index]; +// } +// return symbol; + return null; + } + } + + + public void close() throws ELFException { + try { + file.close(); + } catch (IOException e) { + throw new ELFException(e); + } + } + + void seek(long offset) throws ELFException { + try { + file.seek(offset); + } catch (IOException e) { + throw new ELFException(e); + } + } + + long getFilePointer() throws ELFException { + try { + return file.getFilePointer(); + } catch (IOException e) { + throw new ELFException(e); + } + } + + byte readByte() throws ELFException { + try { + return file.readByte(); + } catch (IOException e) { + throw new ELFException(e); + } + } + + int readBytes(byte[] b) throws ELFException { + try { + return file.read(b); + } catch (IOException e) { + throw new ELFException(e); + } + } + + short readShort() throws ELFException { + try { + short val; + switch (ident[NDX_ENCODING]) { + case DATA_LSB: + val = byteSwap(file.readShort()); + break; + case DATA_MSB: + val = file.readShort(); + break; + default: + throw new ELFException("Invalid encoding."); + } + return val; + } catch (IOException e) { + throw new ELFException(e); + } + } + + int readInt() throws ELFException { + try { + int val; + switch (ident[NDX_ENCODING]) { + case DATA_LSB: + val = byteSwap(file.readInt()); + break; + case DATA_MSB: + val = file.readInt(); + break; + default: + throw new ELFException("Invalid encoding."); + } + return val; + } catch (IOException e) { + throw new ELFException(e); + } + } + + long readLong() throws ELFException { + try { + long val; + switch (ident[NDX_ENCODING]) { + case DATA_LSB: + val = byteSwap(file.readLong()); + break; + case DATA_MSB: + val = file.readLong(); + break; + default: + throw new ELFException("Invalid encoding."); + } + return val; + } catch (IOException e) { + throw new ELFException(e); + } + } + + /** Signed byte utility functions used for converting from big-endian + * (MSB) to little-endian (LSB). */ + short byteSwap(short arg) { + return (short) ((arg << 8) | ((arg >>> 8) & 0xFF)); + } + + int byteSwap(int arg) { + return (((int) byteSwap((short) arg)) << 16) | + (((int) (byteSwap((short) (arg >>> 16)))) & 0xFFFF); + } + + long byteSwap(long arg) { + return ((((long) byteSwap((int) arg)) << 32) | + (((long) byteSwap((int) (arg >>> 32))) & 0xFFFFFFFF)); + } + + + /* Unsigned byte utility functions. Since java does not have unsigned + * data types we must convert values manually and we must return + * unsigned values in a larger data type. Therefore we can only have + * unsigned values for byte, short, and int. */ + short readUnsignedByte() throws ELFException { + try { + return unsignedByte(file.readByte()); + } catch (IOException e) { + throw new ELFException(e); + } + } + + int readUnsignedShort() throws ELFException { + try { + int val; + switch (ident[NDX_ENCODING]) { + case DATA_LSB: + val = unsignedByteSwap(file.readShort()); + break; + case DATA_MSB: + val = unsignedByte(file.readShort()); + break; + default: + throw new ELFException("Invalid encoding."); + } + return val; + } catch (IOException e) { + throw new ELFException(e); + } + } + + long readUnsignedInt() throws ELFException { + try { + long val; + switch (ident[NDX_ENCODING]) { + case DATA_LSB: + val = unsignedByteSwap(file.readInt()); + break; + case DATA_MSB: + val = unsignedByte(file.readInt()); + break; + default: + throw new ELFException("Invalid encoding."); + } + return val; + } catch (IOException e) { + throw new ELFException(e); + } + } + + /** Returns the unsigned value of the byte. */ + short unsignedByte(byte arg) { + return (short)(arg & 0x00FF); + } + + /** Returns a big-endian unsigned representation of the short. */ + int unsignedByte(short arg) { + int val; + if (arg >= 0) { + val = arg; + } else { + val = (int)(((int)unsignedByte((byte)(arg >>> 8)) << 8) | + ((byte)arg)); + } + return val; + } + + /** Returns a big-endian unsigned representation of the int. */ + long unsignedByte(int arg) { + long val; + if (arg >= 0) { + val = arg; + } else { + val = (long)(((long)unsignedByte((short)(arg >>> 16)) << 16) | + ((short)arg)); + } + return val; + } + + /** Unsigned byte utility functions used for converting from big-endian + * (MSB) to little-endian (LSB). */ + int unsignedByteSwap(short arg) { + return (int)(((int)unsignedByte((byte)arg)) << 8) | + ((int)unsignedByte((byte)(arg >>> 8))); + } + + long unsignedByteSwap(int arg) { + return (long)(((long)unsignedByteSwap((short)arg)) << 16) | + ((long)unsignedByteSwap((short)(arg >>> 16))); + } + } + + public static void main(String args[]) { + if (args.length != 1) { + System.out.println("Usage: java ELFFileParser "); + System.exit(0); + } + + // Parse the file. + ELFFile elfFile = ELFFileParser.getParser().parse(args[0]); + + ELFHeader elfHeader = elfFile.getHeader(); + System.out.println("ELF File: " + args[0]); + + System.out.println("ELF object size: " + + ((elfFile.getObjectSize() == 0) ? "Invalid Object Size" : + (elfFile.getObjectSize() == 1) ? "32-bit" : "64-bit")); + System.out.println("ELF data encoding: " + + ((elfFile.getEncoding() == 0) ? "Invalid Data Encoding" : + (elfFile.getEncoding() == 1) ? "LSB" : "MSB")); + + int h = elfHeader.getNumberOfSectionHeaders(); + System.out.println("--> Start: reading " + h + " section headers."); + for (int i = 0; i < elfHeader.getNumberOfSectionHeaders(); i++) { + ELFSectionHeader sh = elfHeader.getSectionHeader(i); + String str = sh.getName(); + System.out.println("----> Start: Section (" + i + ") " + str); + + int num = 0; + if ((num = sh.getNumberOfSymbols()) != 0) { + System.out.println("------> Start: reading " + num + " symbols."); + for (int j = 0; j < num ; j++) { + ELFSymbol sym = sh.getELFSymbol(j); + //String name = sym.getName(); + //if (name != null) { + // System.out.println(name); + //} + } + System.out.println("<------ End: reading " + num + " symbols."); + } + ELFStringTable st; + if (sh.getType() == ELFSectionHeader.TYPE_STRTBL) { + System.out.println("------> Start: reading string table."); + st = sh.getStringTable(); + System.out.println("<------ End: reading string table."); + } + if (sh.getType() == ELFSectionHeader.TYPE_HASH) { + System.out.println("------> Start: reading hash table."); + sh.getHashTable(); + System.out.println("<------ End: reading hash table."); + } + System.out.println("<---- End: Section (" + i + ") " + str); + } + System.out.println("<-- End: reading " + h + " section headers."); +/* + h = elfHeader.getNumberOfProgramHeaders(); + System.out.println("--> Start: reading " + h + " program headers."); + for (int i = 0; i < elfHeader.getNumberOfProgramHeaders(); i++) { + elfHeader.getProgramHeader(i); + } + System.out.println("<-- End: reading " + h + " program headers."); +*/ + elfFile.close(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFHashTable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFHashTable.java new file mode 100644 index 00000000000..584075a21c4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFHashTable.java @@ -0,0 +1,34 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.posix.elf; + +public interface ELFHashTable { + /** + * Returns the ELFSymbol that has the specified name or null if no symbol + * with that name exists. NOTE: Currently this method does not work and + * willl always return null. + */ + public ELFSymbol getSymbol(String symbolName); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFHeader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFHeader.java new file mode 100644 index 00000000000..5d5087033ca --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFHeader.java @@ -0,0 +1,108 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.posix.elf; + +import java.io.FileInputStream; + +/** + * This is a Java class that represents a ELF file header. + * + * @author Joshua W. Outwater + */ +public interface ELFHeader { + /** No file type. */ + public static final int FT_NONE = 0; + /** Relocatable file type. */ + public static final int FT_REL = 1; + /** Executable file type. */ + public static final int FT_EXEC = 2; + /** Shared object file type. */ + public static final int FT_DYN = 3; + /** Core file file type. */ + public static final int FT_CORE = 4; + /** Processor specific. */ + public static final int FT_LOCPROC = 0xff00; + /** Processor specific. */ + public static final int FT_HICPROC = 0xffff; + + /** No architecture type. */ + public static final int ARCH_NONE = 0; + /** AT&T architecture type. */ + public static final int ARCH_ATT = 1; + /** SPARC architecture type. */ + public static final int ARCH_SPARC = 2; + /** Intel 386 architecture type. */ + public static final int ARCH_i386 = 3; + /** Motorolla 68000 architecture type. */ + public static final int ARCH_68k = 4; + /** Motorolla 88000 architecture type. */ + public static final int ARCH_88k = 5; + /** Intel 860 architecture type. */ + public static final int ARCH_i860 = 7; + /** MIPS architecture type. */ + public static final int ARCH_MIPS = 8; + + /** Returns a file type which is defined by the file type constants. */ + public short getFileType(); + /** Returns one of the architecture constants. */ + public short getArch(); + /** Returns the size of a section header. */ + public short getSectionHeaderSize(); + /** Returns the number of section headers. */ + public short getNumberOfSectionHeaders(); + /** Returns the section header at the specified index. The section header + * at index 0 is the undefined section header. */ + public ELFSectionHeader getSectionHeader(int index); + /** Returns the section header string table associated with this ELF + * file. */ + public ELFStringTable getSectionHeaderStringTable(); + /** Returns the string table associated with this ELF file. */ + public ELFStringTable getStringTable(); + /** Returns the dynamic string table associated with this ELF file, or null + * if one does not exist. */ + public ELFStringTable getDynamicStringTable(); + /** Returns the hash table associated with this ELF file, or null if one + * does not exist. NOTE: Currently the ELFHashTable does not work so this + * method will always return null. */ + public ELFHashTable getHashTable(); + /** Returns the symbol table associated with this ELF file, or null if one + * does not exist. */ + public ELFSectionHeader getSymbolTableSection(); + /** Returns the dynamic symbol table associated with this ELF file, or null + * if one does not exist. */ + public ELFSectionHeader getDynamicSymbolTableSection(); + /** Returns the elf symbol with the specified name or null if one is not + * found. */ + public ELFSymbol getELFSymbol(String name); + /** Returns the elf symbol with the specified address or null if one is not + * found. 'address' is relative to base of shared object for .so's. */ + public ELFSymbol getELFSymbol(long address); + /** Returns the size of a program header. */ + //public short getProgramHeaderSize(); + /** Returns the number of program headers. */ + //public short getNumberOfProgramHeaders(); + /** Returns the program header at the specified index. */ + //public ProgramHeader getProgramHeader(int index); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFProgramHeader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFProgramHeader.java new file mode 100644 index 00000000000..5498106fad4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFProgramHeader.java @@ -0,0 +1,47 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.posix.elf; + +/** + * This is the interface definintion for a ProgramHeader in an ELF file. + * Program headers contain system information necessary for preparing a program + * for execution. + */ +public interface ELFProgramHeader { + /** Type defining that the array element is unused. Other member values + * are undefined. */ + public static final int TYPE_NULL = 0; + /** Type defining that the array element specifies a loadable segment. */ + public static final int TYPE_LOAD = 1; + public static final int TYPE_DYNAMIC = 2; + public static final int TYPE_INTERP = 3; + public static final int TYPE_NOTE = 4; + public static final int TYPE_SHLIB = 5; + public static final int TYPE_PHDR = 6; + public static final int TYPE_LOPROC = 0x70000000; + public static final int TYPE_HIPROC = 0x7fffffff; + + public int getType(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFSectionHeader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFSectionHeader.java new file mode 100644 index 00000000000..e638ab0e3cc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFSectionHeader.java @@ -0,0 +1,121 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.posix.elf; + +public interface ELFSectionHeader { + /** Undefined section header index. */ + public static final int NDX_UNDEFINED = 0; + /** Lower bound section header index. */ + public static final int NDX_LORESERVE = 0xff00; + /** Lower bound section header index reserved for processor specific + * semantics. */ + public static final int NDX_LOPROC = 0xff00; + /** Upper bound section header index reserved for processor specific + * semantics. */ + public static final int NDX_HIPROC = 0xff1f; + /** Absolute values for the corresponding reference. Symbols defined + * relative to section number NDX_ABS have absolute values and are not + * affected by relocation. */ + public static final int NDX_ABS = 0xfff1; + /** Symbols defined relative to this section are common symbols, such + * as FORTRAN, COMMON or unallocated C external variables. */ + public static final int NDX_COMMON = 0xfff2; + /** Upper bound section header index. */ + public static final int NDX_HIRESERVE = 0xffff; + + /** Section is inactive. */ + public static final int TYPE_NULL = 0; + /** Section holds information defined by the program. */ + public static final int TYPE_PROGBITS = 1; + /** Section holds symbol table information for link editing. It may also + * be used to store symbols for dynamic linking. */ + public static final int TYPE_SYMTBL = 2; + /** Section holds string table information. */ + public static final int TYPE_STRTBL = 3; + /** Section holds relocation entries with explicit addends. */ + public static final int TYPE_RELO_EXPLICIT = 4; + /** Section holds symbol hash table. */ + public static final int TYPE_HASH = 5; + /** Section holds information for dynamic linking. */ + public static final int TYPE_DYNAMIC = 6; + /** Section holds information that marks the file. */ + public static final int TYPE_NOTE = 7; + /** Section occupies no space but resembles TYPE_PROGBITS. */ + public static final int TYPE_NOBITS = 8; + /** Section holds relocation entries without explicit addends. */ + public static final int TYPE_RELO = 9; + /** Section is reserved but has unspecified semantics. */ + public static final int TYPE_SHLIB = 10; + /** Section holds a minimum set of dynamic linking symbols. */ + public static final int TYPE_DYNSYM = 11; + /** Lower bound section type that contains processor specific semantics. */ + public static final int TYPE_LOPROC = 0x70000000; + /** Upper bound section type that contains processor specific semantics. */ + public static final int TYPE_HIPROC = 0x7fffffff; + /** Lower bound of the range of indexes reserved for application + * programs. */ + public static final int TYPE_LOUSER = 0x80000000; + /** Upper bound of the range of indexes reserved for application + * programs. */ + public static final int TYPE_HIUSER = 0xffffffff; + + /** Flag informing that this section contains data that should be writable + * during process execution. */ + public static final int FLAG_WRITE = 0x1; + /** Flag informing that section occupies memory during process + * execution. */ + public static final int FLAG_ALLOC = 0x2; + /** Flag informaing that section contains executable machine + * instructions. */ + public static final int FLAG_EXEC_INSTR = 0x4; + /** Flag informing that all the bits in the mask are reserved for processor + * specific semantics. */ + public static final int FLAG_MASK = 0xf0000000; + + /** Section header name identifying the section as a string table. */ + public static final String STRING_TABLE_NAME = ".strtab"; + /** Section header name identifying the section as a dynamic string + * table. */ + public static final String DYNAMIC_STRING_TABLE_NAME = ".dynstr"; + /** Returns the type of section header. */ + public int getType(); + /** Returns the number of symbols in this section or 0 if none. */ + public int getNumberOfSymbols(); + /** Returns the symbol at the specified index. The ELF symbol at index 0 + * is the undefined symbol. */ + public ELFSymbol getELFSymbol(int index); + /** Returns the string table for this section or null if one does not + * exist. */ + public ELFStringTable getStringTable(); + /** Returns the hash table for this section or null if one does not + * exist. NOTE: currently the ELFHashTable does not work and this method + * will always return null. */ + public ELFHashTable getHashTable(); + public int getLink(); + /** Returns the name of the section or null if the section has no name. */ + public String getName(); + /** Returns the offset in bytes to the beginning of the section. */ + public int getOffset(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFStringTable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFStringTable.java new file mode 100644 index 00000000000..9795cd9af69 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFStringTable.java @@ -0,0 +1,30 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.posix.elf; + +public interface ELFStringTable { + public String get(int index); + public int getNumStrings(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFSymbol.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFSymbol.java new file mode 100644 index 00000000000..12df0faf5a4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFSymbol.java @@ -0,0 +1,77 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.posix.elf; + +public interface ELFSymbol { + /** Binding specifying that local symbols are not visible outside the + * object file that contains its definition. */ + public static final int BINDING_LOCAL = 0; + /** Binding specifying that global symbols are visible to all object files + * being combined. */ + public static final int BINDING_GLOBAL = 1; + /** Binding secifying that the symbol resembles a global symbol, but has + * a lower precedence. */ + public static final int BINDING_WEAK = 2; + /** Lower bound binding values reserverd for processor specific + * semantics. */ + public static final int BINDING_LOPROC = 13; + /** Upper bound binding values reserverd for processor specific + * semantics. */ + public static final int BINDING_HIPROC = 15; + + /** Type specifying that the symbol is unspecified. */ + public static final byte TYPE_NOOBJECT = 0; + /** Type specifying that the symbol is associated with an object. */ + public static final byte TYPE_OBJECT = 1; + /** Type specifying that the symbol is associated with a function. */ + public static final byte TYPE_FUNCTION = 2; + /** Type specifying that the symbol is associated with a section. Symbol + * table entries of this type exist for relocation and normally have the + * binding BINDING_LOCAL. */ + public static final byte TYPE_SECTION = 3; + /** Type defining that the symbol is associated with a file. */ + public static final byte TYPE_FILE = 4; + /** Lower bound type reserved for processor specific semantics. */ + public static final byte TYPE_LOPROC = 13; + /** Upper bound type reserved for processor specific semantics. */ + public static final byte TYPE_HIPROC = 15; + + /** Returns the location from the beginning of the file to the symbol. */ + public long getOffset(); + /** Returns the name of the symbol or null if the symbol has no name. */ + public String getName(); + /** Returns the binding for this symbol. */ + public int getBinding(); + /** Returns the symbol type. */ + public int getType(); + + /** Value of the associated symbol. This may be a relativa address for .so + * or absolute address for other ELFs. */ + public long getValue(); + + /** Size of the symbol. 0 if the symbol has no size or the size + * is unknown. */ + public int getSize(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcAddress.java new file mode 100644 index 00000000000..9bde3f272ae --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcAddress.java @@ -0,0 +1,387 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc; + +import sun.jvm.hotspot.debugger.*; + +class ProcAddress implements Address { + protected ProcDebugger debugger; + protected long addr; + + ProcAddress(ProcDebugger debugger, long addr) { + this.debugger = debugger; + this.addr = addr; + } + + // + // Basic Java routines + // + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof ProcAddress)) { + return false; + } + + return (addr == ((ProcAddress) arg).addr); + } + + public int hashCode() { + // FIXME: suggestions on a better hash code? + return (int) addr; + } + + public String toString() { + return debugger.addressValueToString(addr); + } + + // + // C/C++-related routines + // + + public long getCIntegerAt(long offset, long numBytes, boolean isUnsigned) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readCInteger(addr + offset, numBytes, isUnsigned); + } + + public Address getAddressAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readAddress(addr + offset); + } + + // + // Java-related routines + // + + public boolean getJBooleanAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJBoolean(addr + offset); + } + + public byte getJByteAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJByte(addr + offset); + } + + public char getJCharAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJChar(addr + offset); + } + + public double getJDoubleAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJDouble(addr + offset); + } + + public float getJFloatAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJFloat(addr + offset); + } + + public int getJIntAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJInt(addr + offset); + } + + public long getJLongAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJLong(addr + offset); + } + + public short getJShortAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJShort(addr + offset); + } + + public OopHandle getOopHandleAt(long offset) + throws UnalignedAddressException, UnmappedAddressException, NotInHeapException { + return debugger.readOopHandle(addr + offset); + } + + // Mutators -- not implemented for now (FIXME) + public void setCIntegerAt(long offset, long numBytes, long value) { + throw new DebuggerException("Unimplemented"); + } + public void setAddressAt(long offset, Address value) { + throw new DebuggerException("Unimplemented"); + } + public void setJBooleanAt (long offset, boolean value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJByteAt (long offset, byte value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJCharAt (long offset, char value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJDoubleAt (long offset, double value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJFloatAt (long offset, float value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJIntAt (long offset, int value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJLongAt (long offset, long value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJShortAt (long offset, short value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setOopHandleAt (long offset, OopHandle value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + + // + // Arithmetic operations -- necessary evil. + // + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new ProcAddress(debugger, value); + } + + public OopHandle addOffsetToAsOopHandle(long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new ProcOopHandle(debugger, value); + } + + /** (FIXME: any signed/unsigned issues? Should this work for + OopHandles?) */ + public long minus(Address arg) { + if (arg == null) { + return addr; + } + return addr - ((ProcAddress) arg).addr; + } + + // Two's complement representation. + // All negative numbers are larger than positive numbers. + // Numbers with the same sign can be compared normally. + // Test harness is below in main(). + + public boolean lessThan (Address arg) { + if (arg == null) { + return false; + } + ProcAddress dbxArg = (ProcAddress) arg; + if ((addr >= 0) && (dbxArg.addr < 0)) { + return true; + } + if ((addr < 0) && (dbxArg.addr >= 0)) { + return false; + } + return (addr < dbxArg.addr); + } + + public boolean lessThanOrEqual (Address arg) { + if (arg == null) { + return false; + } + ProcAddress dbxArg = (ProcAddress) arg; + if ((addr >= 0) && (dbxArg.addr < 0)) { + return true; + } + if ((addr < 0) && (dbxArg.addr >= 0)) { + return false; + } + return (addr <= dbxArg.addr); + } + + public boolean greaterThan (Address arg) { + if (arg == null) { + return true; + } + ProcAddress dbxArg = (ProcAddress) arg; + if ((addr >= 0) && (dbxArg.addr < 0)) { + return false; + } + if ((addr < 0) && (dbxArg.addr >= 0)) { + return true; + } + return (addr > dbxArg.addr); + } + + public boolean greaterThanOrEqual(Address arg) { + if (arg == null) { + return true; + } + ProcAddress dbxArg = (ProcAddress) arg; + if ((addr >= 0) && (dbxArg.addr < 0)) { + return false; + } + if ((addr < 0) && (dbxArg.addr >= 0)) { + return true; + } + return (addr >= dbxArg.addr); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + long value = addr & mask; + if (value == 0) { + return null; + } + return new ProcAddress(debugger, value); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + long value = addr | mask; + if (value == 0) { + return null; + } + return new ProcAddress(debugger, value); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + long value = addr ^ mask; + if (value == 0) { + return null; + } + return new ProcAddress(debugger, value); + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + long getValue() { + return addr; + } + + + private static void check(boolean arg, String failMessage) { + if (!arg) { + System.err.println(failMessage + ": FAILED"); + System.exit(1); + } + } + + // Test harness + public static void main(String[] args) { + // p/n indicates whether the interior address is really positive + // or negative. In unsigned terms, p1 < p2 < n1 < n2. + + ProcAddress p1 = new ProcAddress(null, 0x7FFFFFFFFFFFFFF0L); + ProcAddress p2 = (ProcAddress) p1.addOffsetTo(10); + ProcAddress n1 = (ProcAddress) p2.addOffsetTo(10); + ProcAddress n2 = (ProcAddress) n1.addOffsetTo(10); + + // lessThan positive tests + check(p1.lessThan(p2), "lessThan 1"); + check(p1.lessThan(n1), "lessThan 2"); + check(p1.lessThan(n2), "lessThan 3"); + check(p2.lessThan(n1), "lessThan 4"); + check(p2.lessThan(n2), "lessThan 5"); + check(n1.lessThan(n2), "lessThan 6"); + + // lessThan negative tests + check(!p1.lessThan(p1), "lessThan 7"); + check(!p2.lessThan(p2), "lessThan 8"); + check(!n1.lessThan(n1), "lessThan 9"); + check(!n2.lessThan(n2), "lessThan 10"); + + check(!p2.lessThan(p1), "lessThan 11"); + check(!n1.lessThan(p1), "lessThan 12"); + check(!n2.lessThan(p1), "lessThan 13"); + check(!n1.lessThan(p2), "lessThan 14"); + check(!n2.lessThan(p2), "lessThan 15"); + check(!n2.lessThan(n1), "lessThan 16"); + + // lessThanOrEqual positive tests + check(p1.lessThanOrEqual(p1), "lessThanOrEqual 1"); + check(p2.lessThanOrEqual(p2), "lessThanOrEqual 2"); + check(n1.lessThanOrEqual(n1), "lessThanOrEqual 3"); + check(n2.lessThanOrEqual(n2), "lessThanOrEqual 4"); + + check(p1.lessThanOrEqual(p2), "lessThanOrEqual 5"); + check(p1.lessThanOrEqual(n1), "lessThanOrEqual 6"); + check(p1.lessThanOrEqual(n2), "lessThanOrEqual 7"); + check(p2.lessThanOrEqual(n1), "lessThanOrEqual 8"); + check(p2.lessThanOrEqual(n2), "lessThanOrEqual 9"); + check(n1.lessThanOrEqual(n2), "lessThanOrEqual 10"); + + // lessThanOrEqual negative tests + check(!p2.lessThanOrEqual(p1), "lessThanOrEqual 11"); + check(!n1.lessThanOrEqual(p1), "lessThanOrEqual 12"); + check(!n2.lessThanOrEqual(p1), "lessThanOrEqual 13"); + check(!n1.lessThanOrEqual(p2), "lessThanOrEqual 14"); + check(!n2.lessThanOrEqual(p2), "lessThanOrEqual 15"); + check(!n2.lessThanOrEqual(n1), "lessThanOrEqual 16"); + + // greaterThan positive tests + check(n2.greaterThan(p1), "greaterThan 1"); + check(n2.greaterThan(p2), "greaterThan 2"); + check(n2.greaterThan(n1), "greaterThan 3"); + check(n1.greaterThan(p1), "greaterThan 4"); + check(n1.greaterThan(p2), "greaterThan 5"); + check(p2.greaterThan(p1), "greaterThan 6"); + + // greaterThan negative tests + check(!p1.greaterThan(p1), "greaterThan 7"); + check(!p2.greaterThan(p2), "greaterThan 8"); + check(!n1.greaterThan(n1), "greaterThan 9"); + check(!n2.greaterThan(n2), "greaterThan 10"); + + check(!p1.greaterThan(n2), "greaterThan 11"); + check(!p2.greaterThan(n2), "greaterThan 12"); + check(!n1.greaterThan(n2), "greaterThan 13"); + check(!p1.greaterThan(n1), "greaterThan 14"); + check(!p2.greaterThan(n1), "greaterThan 15"); + check(!p1.greaterThan(p2), "greaterThan 16"); + + // greaterThanOrEqual positive tests + check(p1.greaterThanOrEqual(p1), "greaterThanOrEqual 1"); + check(p2.greaterThanOrEqual(p2), "greaterThanOrEqual 2"); + check(n1.greaterThanOrEqual(n1), "greaterThanOrEqual 3"); + check(n2.greaterThanOrEqual(n2), "greaterThanOrEqual 4"); + + check(n2.greaterThanOrEqual(p1), "greaterThanOrEqual 5"); + check(n2.greaterThanOrEqual(p2), "greaterThanOrEqual 6"); + check(n2.greaterThanOrEqual(n1), "greaterThanOrEqual 7"); + check(n1.greaterThanOrEqual(p1), "greaterThanOrEqual 8"); + check(n1.greaterThanOrEqual(p2), "greaterThanOrEqual 9"); + check(p2.greaterThanOrEqual(p1), "greaterThanOrEqual 10"); + + // greaterThanOrEqual negative tests + check(!p1.greaterThanOrEqual(n2), "greaterThanOrEqual 11"); + check(!p2.greaterThanOrEqual(n2), "greaterThanOrEqual 12"); + check(!n1.greaterThanOrEqual(n2), "greaterThanOrEqual 13"); + check(!p1.greaterThanOrEqual(n1), "greaterThanOrEqual 14"); + check(!p2.greaterThanOrEqual(n1), "greaterThanOrEqual 15"); + check(!p1.greaterThanOrEqual(p2), "greaterThanOrEqual 16"); + + System.err.println("ProcAddress: all tests passed successfully."); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcCDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcCDebugger.java new file mode 100644 index 00000000000..fff35b1bf8d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcCDebugger.java @@ -0,0 +1,101 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.utilities.*; + +class ProcCDebugger implements CDebugger { + private ProcDebugger dbg; + + ProcCDebugger(ProcDebugger dbg) { + this.dbg = dbg; + } + + public List getThreadList() throws DebuggerException { + return dbg.getThreadList(); + } + + public List/**/ getLoadObjectList() throws DebuggerException { + return dbg.getLoadObjectList(); + } + + public LoadObject loadObjectContainingPC(Address pc) throws DebuggerException { + if (pc == null) { + return null; + } + List objs = getLoadObjectList(); + Object[] arr = objs.toArray(); + // load objects are sorted by base address, do binary search + int mid = -1; + int low = 0; + int high = arr.length - 1; + + while (low <= high) { + mid = (low + high) >> 1; + LoadObject midVal = (LoadObject) arr[mid]; + long cmp = pc.minus(midVal.getBase()); + if (cmp < 0) { + high = mid - 1; + } else if (cmp > 0) { + long size = midVal.getSize(); + if (cmp >= size) { + low = mid + 1; + } else { + return (LoadObject) arr[mid]; + } + } else { // match found + return (LoadObject) arr[mid]; + } + } + // no match found. + return null; + } + + public CFrame topFrameForThread(ThreadProxy thread) throws DebuggerException { + return dbg.topFrameForThread(thread); + } + + public String getNameOfFile(String fileName) { + return new File(fileName).getName(); + } + + public ProcessControl getProcessControl() throws DebuggerException { + // FIXME: after stabs parser + return null; + } + + // C++ name demangling + public boolean canDemangle() { + return true; + } + + public String demangle(String sym) { + return dbg.demangle(sym); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcCFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcCFrame.java new file mode 100644 index 00000000000..31be7e6b073 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcCFrame.java @@ -0,0 +1,67 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.*; + +final class ProcCFrame extends BasicCFrame { + public Address pc() { + return pc; + } + + public Address localVariableBase() { + return fp; + } + + public CFrame sender() { + return sender; + } + + public ClosestSymbol closestSymbolToPC() { + // we don't use base class ELF parsing based + // symbol lookup for pc for performance reasons. + return procDbg.lookup(procDbg.getAddressValue(pc)); + } + + // package/class internals only + + ProcCFrame(ProcDebugger dbg, Address pc, Address fp) { + super(dbg.getCDebugger()); + this.pc = pc; + this.fp = fp; + this.procDbg = dbg; + } + + void setSender(ProcCFrame sender) { + this.sender = sender; + } + + private Address pc; + private Address fp; + private ProcCFrame sender; + private ProcDebugger procDbg; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcDebugger.java new file mode 100644 index 00000000000..d175f1580b8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcDebugger.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc; + +import java.util.List; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +/** An extension of the JVMDebugger interface with a few additions to + support 32-bit vs. 64-bit debugging as well as features required + by the architecture-specific subpackages. */ + +public interface ProcDebugger extends JVMDebugger { + public MachineDescription getMachineDescription() throws DebuggerException; + public String addressValueToString(long address) throws DebuggerException; + public boolean readJBoolean(long address) throws DebuggerException; + public byte readJByte(long address) throws DebuggerException; + public char readJChar(long address) throws DebuggerException; + public double readJDouble(long address) throws DebuggerException; + public float readJFloat(long address) throws DebuggerException; + public int readJInt(long address) throws DebuggerException; + public long readJLong(long address) throws DebuggerException; + public short readJShort(long address) throws DebuggerException; + public long readCInteger(long address, long numBytes, boolean isUnsigned) + throws DebuggerException; + public ProcAddress readAddress(long address) throws DebuggerException; + public ProcOopHandle readOopHandle(long address) throws DebuggerException; + public long[] getThreadIntegerRegisterSet(int tid) throws DebuggerException; + public long getAddressValue(Address addr) throws DebuggerException; + public Address newAddress(long value) throws DebuggerException; + + // for ProcCDebugger, ProcCFrame and SharedObject + public List getThreadList() throws DebuggerException; + public List getLoadObjectList() throws DebuggerException; + public CFrame topFrameForThread(ThreadProxy thread) throws DebuggerException; + public ClosestSymbol lookup(long address) throws DebuggerException; + public String demangle(String name); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcDebuggerLocal.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcDebuggerLocal.java new file mode 100644 index 00000000000..49edcae486a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcDebuggerLocal.java @@ -0,0 +1,687 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc; + +import java.io.*; +import java.net.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.proc.amd64.*; +import sun.jvm.hotspot.debugger.proc.sparc.*; +import sun.jvm.hotspot.debugger.proc.x86.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.utilities.*; + +/**

An implementation of the JVMDebugger interface which sits on + * top of proc and relies on the SA's proc import module for + * communication with the debugger.

+ * + *

NOTE that since we have the notion of fetching "Java + * primitive types" from the remote process (which might have + * different sizes than we expect) we have a bootstrapping + * problem. We need to know the sizes of these types before we can + * fetch them. The current implementation solves this problem by + * requiring that it be configured with these type sizes before they + * can be fetched. The readJ(Type) routines here will throw a + * RuntimeException if they are called before the debugger is + * configured with the Java primitive type sizes.

+ */ + +public class ProcDebuggerLocal extends DebuggerBase implements ProcDebugger { + + + protected static final int cacheSize = 16 * 1024 * 1024; // 16 MB + + //------------------------------------------------------------------------ + // Implementation of Debugger interface + // + + /**

machDesc may be null if it couldn't be determined yet; i.e., + * if we're on SPARC, we need to ask the remote process whether + * we're in 32- or 64-bit mode.

+ * + *

useCache should be set to true if debugging is being done + * locally, and to false if the debugger is being created for the + * purpose of supporting remote debugging.

*/ + public ProcDebuggerLocal(MachineDescription machDesc, boolean useCache) { + this.machDesc = machDesc; + int cacheNumPages; + int cachePageSize; + + final String cpu = PlatformInfo.getCPU(); + if (cpu.equals("sparc")) { + threadFactory = new ProcSPARCThreadFactory(this); + pcRegIndex = SPARCThreadContext.R_PC; + fpRegIndex = SPARCThreadContext.R_I6; + } else if (cpu.equals("x86")) { + threadFactory = new ProcX86ThreadFactory(this); + pcRegIndex = X86ThreadContext.EIP; + fpRegIndex = X86ThreadContext.EBP; + unalignedAccessesOkay = true; + } else if (cpu.equals("amd64")) { + threadFactory = new ProcAMD64ThreadFactory(this); + pcRegIndex = AMD64ThreadContext.RIP; + fpRegIndex = AMD64ThreadContext.RBP; + } else { + throw new RuntimeException("Thread access for CPU architecture " + PlatformInfo.getCPU() + " not yet supported"); + } + if (useCache) { + // Cache portion of the remote process's address space. + // For now, this cache works best if it covers the entire + // heap of the remote process. FIXME: at least should make this + // tunable from the outside, i.e., via the UI. This is a 16 MB + // cache divided on SPARC into 2048 8K pages and on x86 into + // 4096 4K pages; the page size must be adjusted to be the OS's + // page size. + + cachePageSize = getPageSize(); + cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); + initCache(cachePageSize, cacheNumPages); + } + + resetNativePointers(); + clearCacheFields(); + } + + /** FIXME: implement this with a Runtime.exec() of ps followed by + * parsing of its output */ + public boolean hasProcessList() throws DebuggerException { + return false; + } + + public List getProcessList() throws DebuggerException { + throw new DebuggerException("Not yet supported"); + } + + + /** From the Debugger interface via JVMDebugger */ + public synchronized void attach(int processID) throws DebuggerException { + checkAttached(); + isCore = false; + attach0(new Integer(processID).toString()); + attached = true; + suspended = true; + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized void attach + (String executableName, String coreFileName) throws DebuggerException { + checkAttached(); + isCore = true; + topFrameCache = new HashMap(); + attach0(executableName, coreFileName); + attached = true; + suspended = true; + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized boolean detach() { + if (! attached) { + return false; + } + + try { + if (p_ps_prochandle == 0L) { + return false; + } + detach0(); + clearCache(); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } finally { + resetNativePointers(); + clearCacheFields(); + suspended = false; + attached = false; + } + } + + public synchronized void suspend() throws DebuggerException { + requireAttach(); + if (suspended) { + throw new DebuggerException("Process already suspended"); + } + suspend0(); + suspended = true; + enableCache(); + reresolveLoadObjects(); + } + + public synchronized void resume() throws DebuggerException { + requireAttach(); + if (!suspended) { + throw new DebuggerException("Process not suspended"); + } + resume0(); + disableCache(); + suspended = false; + } + + public synchronized boolean isSuspended() throws DebuggerException { + requireAttach(); + return suspended; + } + + /** From the Debugger interface via JVMDebugger */ + public Address parseAddress(String addressString) throws NumberFormatException { + long addr = utils.scanAddress(addressString); + if (addr == 0) { + return null; + } + return new ProcAddress(this, addr); + } + + /** From the Debugger interface via JVMDebugger */ + public String getOS() { + return PlatformInfo.getOS(); + } + + /** From the Debugger interface via JVMDebugger */ + public String getCPU() { + return PlatformInfo.getCPU(); + } + + public boolean hasConsole() throws DebuggerException { + return false; + } + + public String consoleExecuteCommand(String cmd) throws DebuggerException { + throw new DebuggerException("Can't execute console commands"); + } + + public String getConsolePrompt() throws DebuggerException { + return ""; + } + + public CDebugger getCDebugger() throws DebuggerException { + if (cdbg == null) { + cdbg = new ProcCDebugger(this); + } + return cdbg; + } + + /** From the SymbolLookup interface via Debugger and JVMDebugger */ + public synchronized Address lookup(String objectName, String symbol) { + requireAttach(); + long addr = lookupByName0(objectName, symbol); + if (addr == 0) { + return null; + } + return new ProcAddress(this, addr); + } + + /** From the SymbolLookup interface via Debugger and JVMDebugger */ + public synchronized OopHandle lookupOop(String objectName, String symbol) { + Address addr = lookup(objectName, symbol); + if (addr == null) { + return null; + } + return addr.addOffsetToAsOopHandle(0); + } + + /** From the ProcDebugger interface */ + public MachineDescription getMachineDescription() { + return machDesc; + } + + /** Internal routine supporting lazy setting of MachineDescription, + * since on SPARC we will need to query the remote process to ask + * it what its data model is (32- or 64-bit). + */ + + public void setMachineDescription(MachineDescription machDesc) { + this.machDesc = machDesc; + setBigEndian(machDesc.isBigEndian()); + utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); + } + + public synchronized int getRemoteProcessAddressSize() + throws DebuggerException { + requireAttach(); + return getRemoteProcessAddressSize0(); + } + + //-------------------------------------------------------------------------------- + // Implementation of ThreadAccess interface + // + + /** From the ThreadAccess interface via Debugger and JVMDebugger */ + public ThreadProxy getThreadForIdentifierAddress(Address addr) { + return threadFactory.createThreadWrapper(addr); + } + + public ThreadProxy getThreadForThreadId(long id) { + return threadFactory.createThreadWrapper(id); + } + + //---------------------------------------------------------------------- + // Overridden from DebuggerBase because we need to relax alignment + // constraints on x86 + + public long readJLong(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + // FIXME: allow this to be configurable. Undesirable to add a + // dependency on the runtime package here, though, since this + // package should be strictly underneath it. + if (unalignedAccessesOkay) { + utils.checkAlignment(address, jintSize); + } else { + utils.checkAlignment(address, jlongSize); + } + byte[] data = readBytes(address, jlongSize); + return utils.dataToJLong(data, jlongSize); + } + + //-------------------------------------------------------------------------------- + // Internal routines (for implementation of ProcAddress). + // These must not be called until the MachineDescription has been set up. + // + + /** From the ProcDebugger interface */ + public String addressValueToString(long address) { + return utils.addressValueToString(address); + } + + /** Need to override this to relax alignment checks on Solaris/x86. */ + public long readCInteger(long address, long numBytes, boolean isUnsigned) + throws UnmappedAddressException, UnalignedAddressException { + checkConfigured(); + if (!unalignedAccessesOkay) { + utils.checkAlignment(address, numBytes); + } else { + // Only slightly relaxed semantics -- this is a hack, but is + // necessary on Solaris/x86 where it seems the compiler is + // putting some global 64-bit data on 32-bit boundaries + if (numBytes == 8) { + utils.checkAlignment(address, 4); + } else { + utils.checkAlignment(address, numBytes); + } + } + byte[] data = readBytes(address, numBytes); + return utils.dataToCInteger(data, isUnsigned); + } + + /** From the ProcDebugger interface */ + public ProcAddress readAddress(long address) + throws UnmappedAddressException, UnalignedAddressException { + long value = readAddressValue(address); + return (value == 0 ? null : new ProcAddress(this, value)); + } + + /** From the ProcDebugger interface */ + public ProcOopHandle readOopHandle(long address) + throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { + long value = readAddressValue(address); + return (value == 0 ? null : new ProcOopHandle(this, value)); + } + + public void writeBytesToProcess(long address, long numBytes, byte[] data) + throws UnmappedAddressException, DebuggerException { + if (isCore) { + throw new DebuggerException("Attached to a core file!"); + } + writeBytesToProcess0(address, numBytes, data); + } + + public synchronized ReadResult readBytesFromProcess(long address, long numBytes) + throws DebuggerException { + requireAttach(); + byte[] res = readBytesFromProcess0(address, numBytes); + if(res != null) + return new ReadResult(res); + else + return new ReadResult(address); + } + + protected int getPageSize() { + int pagesize = getPageSize0(); + if (pagesize == -1) { + // return the hard coded default value. + pagesize = (PlatformInfo.getCPU().equals("x86"))? 4096 : 8192; + } + return pagesize; + } + + //-------------------------------------------------------------------------------- + // Thread context access. Can not be package private, but should + // only be accessed by the architecture-specific subpackages. + + /** From the ProcDebugger interface. May have to redefine this later. */ + public synchronized long[] getThreadIntegerRegisterSet(int tid) { + requireAttach(); + return getThreadIntegerRegisterSet0(tid); + } + + //-------------------------------------------------------------------------------- + // Address access. Can not be package private, but should only be + // accessed by the architecture-specific subpackages. + + /** From the ProcDebugger interface */ + public long getAddressValue(Address addr) { + if (addr == null) return 0; + return ((ProcAddress) addr).getValue(); + } + + /** From the ProcDebugger interface */ + public Address newAddress(long value) { + if (value == 0) return null; + return new ProcAddress(this, value); + } + + /** From the ProcDebugger interface */ + public synchronized List getThreadList() throws DebuggerException { + requireAttach(); + List res = null; + if (isCore && (threadListCache != null)) { + res = threadListCache; + } else { + res = new ArrayList(); + fillThreadList0(res); + if (isCore) { + threadListCache = res; + } + } + return res; + } + + /** From the ProcDebugger interface */ + public synchronized List getLoadObjectList() throws DebuggerException { + requireAttach(); + if (!suspended) { + throw new DebuggerException("Process not suspended"); + } + + if (loadObjectCache == null) { + updateLoadObjectCache(); + } + return loadObjectCache; + } + + /** From the ProcDebugger interface */ + public synchronized CFrame topFrameForThread(ThreadProxy thread) + throws DebuggerException { + requireAttach(); + CFrame res = null; + if (isCore && ((res = (CFrame) topFrameCache.get(thread)) != null)) { + return res; + } else { + ThreadContext context = thread.getContext(); + int numRegs = context.getNumRegisters(); + long[] regs = new long[numRegs]; + for (int i = 0; i < numRegs; i++) { + regs[i] = context.getRegister(i); + } + res = fillCFrameList0(regs); + if (isCore) { + topFrameCache.put(thread, res); + } + return res; + } + } + + /** From the ProcDebugger interface */ + public synchronized ClosestSymbol lookup(long address) { + requireAttach(); + return lookupByAddress0(address); + } + + /** From the ProcDebugger interface */ + public String demangle(String name) { + return demangle0(name); + } + + //------------- Internals only below this point -------------------- + // + // + + private void updateLoadObjectCache() { + List res = new ArrayList(); + nameToDsoMap = new HashMap(); + fillLoadObjectList0(res); + loadObjectCache = sortLoadObjects(res); + } + + // sort load objects by base address + private static List sortLoadObjects(List in) { + // sort the list by base address + Object[] arr = in.toArray(); + Arrays.sort(arr, loadObjectComparator); + return Arrays.asList(arr); + } + + private long lookupByName(String objectName, String symbolName) + throws DebuggerException { + // NOTE: this assumes that process is suspended (which is probably + // necessary assumption given that DSOs can be loaded/unloaded as + // process runs). Should update documentation. + if (nameToDsoMap == null) { + getLoadObjectList(); + } + SharedObject dso = (SharedObject) nameToDsoMap.get(objectName); + // The DSO can be null because we use this to search through known + // DSOs in HotSpotTypeDataBase (for example) + if (dso != null) { + ProcAddress addr = (ProcAddress) dso.lookupSymbol(symbolName); + if (addr != null) { + return addr.getValue(); + } + } + return 0; + } + + private SharedObject findDSOByName(String fullPathName) { + if (loadObjectCache == null) + return null; + for (Iterator iter = loadObjectCache.iterator(); iter.hasNext(); ) { + SharedObject dso = (SharedObject) iter.next(); + if (dso.getName().equals(fullPathName)) { + return dso; + } + } + return null; + } + + private void reresolveLoadObjects() throws DebuggerException { + if (loadObjectCache == null) { + return; + } + updateLoadObjectCache(); + } + + + private void checkAttached() { + if (attached) { + if (isCore) { + throw new DebuggerException("already attached to a core file!"); + } else { + throw new DebuggerException("already attached to a process!"); + } + } + } + + private void requireAttach() { + if (! attached) { + throw new RuntimeException("not attached to a process or core file!"); + } + } + + private void clearCacheFields() { + loadObjectCache = null; + nameToDsoMap = null; + threadListCache = null; + topFrameCache = null; + } + + private void resetNativePointers() { + p_ps_prochandle = 0L; + + // reset thread_db pointers + libthread_db_handle = 0L; + p_td_thragent_t = 0L; + p_td_init = 0L; + p_td_ta_new = 0L; + p_td_ta_delete = 0L; + p_td_ta_thr_iter = 0L; + p_td_thr_get_info = 0L; + p_td_ta_map_id2thr = 0L; + p_td_thr_getgregs = 0L; + + // part of class sharing workaround + classes_jsa_fd = -1; + p_file_map_header = 0L; + } + + // native methods and native helpers + + // attach, detach + private native void attach0(String pid) throws DebuggerException; + private native void attach0(String executableFile, String coreFileName) throws DebuggerException; + private native void detach0() throws DebuggerException; + + // address size, page size + private native int getRemoteProcessAddressSize0() throws DebuggerException; + private native int getPageSize0() throws DebuggerException; + + // threads, stacks + private native long[] getThreadIntegerRegisterSet0(long tid) throws DebuggerException; + private native void fillThreadList0(List l) throws DebuggerException; + + // fills stack frame list given reg set of the top frame and top frame + private native ProcCFrame fillCFrameList0(long[] regs) throws DebuggerException; + + // helper called by fillCFrameList0 + private ProcCFrame createSenderFrame(ProcCFrame f, long pc, long fp) { + ProcCFrame sender = new ProcCFrame(this, newAddress(pc), newAddress(fp)); + if (f != null) { + f.setSender(sender); + } + return sender; + } + + // shared objects + private native void fillLoadObjectList0(List l) throws DebuggerException; + + // helper called by fillLoadObjectList0 + private LoadObject createLoadObject(String fileName, long textsize, long base) { + File f = new File(fileName); + Address baseAddr = newAddress(base); + SharedObject res = findDSOByName(fileName); + if (res != null) { + // already in cache. just change the base, if needed + Address oldBase = res.getBase(); + if (! baseAddr.equals(oldBase)) { + res.setBase(baseAddr); + } + } else { + // new shared object. + res = new SharedObject(this, fileName, f.length(), baseAddr); + } + nameToDsoMap.put(f.getName(), res); + return res; + } + + // symbol-to-pc + private native long lookupByName0(String objectName, String symbolName) throws DebuggerException; + private native ClosestSymbol lookupByAddress0(long address) throws DebuggerException; + + // helper called by lookupByAddress0 + private ClosestSymbol createClosestSymbol(String name, long offset) { + return new ClosestSymbol(name, offset); + } + + // process read/write + private native byte[] readBytesFromProcess0(long address, long numBytes) throws DebuggerException; + private native void writeBytesToProcess0(long address, long numBytes, byte[] data) throws DebuggerException; + + // process control + private native void suspend0() throws DebuggerException; + private native void resume0() throws DebuggerException; + + // demangle a C++ name + private native String demangle0(String name); + + // init JNI ids to fields, methods + private native static void initIDs() throws DebuggerException; + private static LoadObjectComparator loadObjectComparator; + + static { + System.loadLibrary("saproc"); + initIDs(); + loadObjectComparator = new LoadObjectComparator(); + } + + private boolean unalignedAccessesOkay; + private ProcThreadFactory threadFactory; + + // indices of PC and FP registers in gregset + private int pcRegIndex; + private int fpRegIndex; + + // Symbol lookup support + // This is a map of library names to DSOs + private Map nameToDsoMap; // Map + + // C/C++ debugging support + private List/**/ loadObjects; + private CDebugger cdbg; + + // ProcessControl support + private boolean suspended; + + // libproc handle + private long p_ps_prochandle; + + // libthread.so's dlopen handle, thread agent + // and function pointers + private long libthread_db_handle; + private long p_td_thragent_t; + private long p_td_init; + private long p_td_ta_new; + private long p_td_ta_delete; + private long p_td_ta_thr_iter; + private long p_td_thr_get_info; + private long p_td_ta_map_id2thr; + private long p_td_thr_getgregs; + + // part of class sharing workaround + private int classes_jsa_fd; + private long p_file_map_header; + + private boolean attached = false; + private boolean isCore; + + // for core files, we cache load object list, thread list, top frames etc. + // for processes we cache load object list and sync. it during suspend. + private List threadListCache; + private List loadObjectCache; + private Map topFrameCache; // Map +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcOopHandle.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcOopHandle.java new file mode 100644 index 00000000000..308222ba286 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcOopHandle.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc; + +import sun.jvm.hotspot.debugger.*; + +class ProcOopHandle extends ProcAddress implements OopHandle { + ProcOopHandle(ProcDebugger debugger, long addr) { + super(debugger, addr); + } + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + throw new UnsupportedOperationException("addOffsetTo not applicable to OopHandles (interior object pointers not allowed)"); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("andWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("orWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("xorWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcThreadFactory.java new file mode 100644 index 00000000000..d53f42dc304 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcThreadFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc; + +import sun.jvm.hotspot.debugger.*; + +/** An interface used only internally by the ProcDebugger to be able to + create platform-specific Thread objects */ + +public interface ProcThreadFactory { + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr); + public ThreadProxy createThreadWrapper(long id); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/SharedObject.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/SharedObject.java new file mode 100644 index 00000000000..646f73cbe1e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/SharedObject.java @@ -0,0 +1,48 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.posix.*; + +/** A Object can represent either a .so or an a.out file. */ + +class SharedObject extends DSO { + SharedObject(ProcDebugger dbg, String filename, long size, Address relocation) { + super(filename, size, relocation); + this.dbg = dbg; + } + + protected Address newAddress(long address) { + return dbg.newAddress(address); + } + + protected long getAddressValue(Address addr) { + return dbg.getAddressValue(addr); + } + + private ProcDebugger dbg; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/amd64/ProcAMD64Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/amd64/ProcAMD64Thread.java new file mode 100644 index 00000000000..f261232c3bf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/amd64/ProcAMD64Thread.java @@ -0,0 +1,86 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.proc.*; +import sun.jvm.hotspot.utilities.*; + +public class ProcAMD64Thread implements ThreadProxy { + private ProcDebugger debugger; + private int id; + + public ProcAMD64Thread(ProcDebugger debugger, Address addr) { + this.debugger = debugger; + + // FIXME: the size here should be configurable. However, making it + // so would produce a dependency on the "types" package from the + // debugger package, which is not desired. + this.id = (int) addr.getCIntegerAt(0, 4, true); + } + + public ProcAMD64Thread(ProcDebugger debugger, long id) { + this.debugger = debugger; + this.id = (int) id; + } + + public ThreadContext getContext() throws IllegalThreadStateException { + ProcAMD64ThreadContext context = new ProcAMD64ThreadContext(debugger); + long[] regs = debugger.getThreadIntegerRegisterSet(id); + if (Assert.ASSERTS_ENABLED) { + Assert.that(regs.length == AMD64ThreadContext.NPRGREG, "size mismatch"); + } + for (int i = 0; i < regs.length; i++) { + context.setRegister(i, regs[i]); + } + return context; + } + + public boolean canSetContext() throws DebuggerException { + return false; + } + + public void setContext(ThreadContext context) + throws IllegalThreadStateException, DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + public String toString() { + return "t@" + id; + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ProcAMD64Thread)) { + return false; + } + + return (((ProcAMD64Thread) obj).id == id); + } + + public int hashCode() { + return id; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/amd64/ProcAMD64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/amd64/ProcAMD64ThreadContext.java new file mode 100644 index 00000000000..bd69b6266a9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/amd64/ProcAMD64ThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.proc.*; + +public class ProcAMD64ThreadContext extends AMD64ThreadContext { + private ProcDebugger debugger; + + public ProcAMD64ThreadContext(ProcDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/amd64/ProcAMD64ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/amd64/ProcAMD64ThreadFactory.java new file mode 100644 index 00000000000..e35fbdb5722 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/amd64/ProcAMD64ThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.proc.*; + +public class ProcAMD64ThreadFactory implements ProcThreadFactory { + private ProcDebugger debugger; + + public ProcAMD64ThreadFactory(ProcDebugger debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new ProcAMD64Thread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new ProcAMD64Thread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/sparc/ProcSPARCThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/sparc/ProcSPARCThread.java new file mode 100644 index 00000000000..239dad66503 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/sparc/ProcSPARCThread.java @@ -0,0 +1,86 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.proc.*; +import sun.jvm.hotspot.utilities.*; + +public class ProcSPARCThread implements ThreadProxy { + private ProcDebugger debugger; + private int id; + + public ProcSPARCThread(ProcDebugger debugger, Address addr) { + this.debugger = debugger; + + // FIXME: the size here should be configurable. However, making it + // so would produce a dependency on the "types" package from the + // debugger package, which is not desired. + this.id = (int) addr.getCIntegerAt(0, 4, true); + } + + public ProcSPARCThread(ProcDebugger debugger, long id) { + this.debugger = debugger; + this.id = (int) id; + } + + public ThreadContext getContext() throws IllegalThreadStateException { + ProcSPARCThreadContext context = new ProcSPARCThreadContext(debugger); + long[] regs = debugger.getThreadIntegerRegisterSet(id); + if (Assert.ASSERTS_ENABLED) { + Assert.that(regs.length == SPARCThreadContext.NPRGREG, "size of register set must match"); + } + for (int i = 0; i < regs.length; i++) { + context.setRegister(i, regs[i]); + } + return context; + } + + public boolean canSetContext() throws DebuggerException { + return false; + } + + public void setContext(ThreadContext context) + throws IllegalThreadStateException, DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + public String toString() { + return "t@" + id; + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ProcSPARCThread)) { + return false; + } + + return (((ProcSPARCThread) obj).id == id); + } + + public int hashCode() { + return id; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/sparc/ProcSPARCThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/sparc/ProcSPARCThreadContext.java new file mode 100644 index 00000000000..d99366ad76d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/sparc/ProcSPARCThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.proc.*; + +public class ProcSPARCThreadContext extends SPARCThreadContext { + private ProcDebugger debugger; + + public ProcSPARCThreadContext(ProcDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/sparc/ProcSPARCThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/sparc/ProcSPARCThreadFactory.java new file mode 100644 index 00000000000..b4ff63b6e8d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/sparc/ProcSPARCThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.proc.*; + +public class ProcSPARCThreadFactory implements ProcThreadFactory { + private ProcDebugger debugger; + + public ProcSPARCThreadFactory(ProcDebugger debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new ProcSPARCThread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new ProcSPARCThread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/x86/ProcX86Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/x86/ProcX86Thread.java new file mode 100644 index 00000000000..7f474b70995 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/x86/ProcX86Thread.java @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.proc.*; +import sun.jvm.hotspot.utilities.*; + +public class ProcX86Thread implements ThreadProxy { + private ProcDebugger debugger; + private int id; + + public ProcX86Thread(ProcDebugger debugger, Address addr) { + this.debugger = debugger; + + // FIXME: the size here should be configurable. However, making it + // so would produce a dependency on the "types" package from the + // debugger package, which is not desired. + this.id = (int) addr.getCIntegerAt(0, 4, true); + } + + public ProcX86Thread(ProcDebugger debugger, long id) { + this.debugger = debugger; + this.id = (int) id; + } + + public ThreadContext getContext() throws IllegalThreadStateException { + ProcX86ThreadContext context = new ProcX86ThreadContext(debugger); + long[] regs = debugger.getThreadIntegerRegisterSet(id); + /* + _NGREG in reg.h is defined to be 19. Because we have included + debug registers X86ThreadContext.NPRGREG is 25. + */ + + if (Assert.ASSERTS_ENABLED) { + Assert.that(regs.length <= X86ThreadContext.NPRGREG, "size of register set is greater than " + X86ThreadContext.NPRGREG); + } + for (int i = 0; i < regs.length; i++) { + context.setRegister(i, regs[i]); + } + return context; + } + + public boolean canSetContext() throws DebuggerException { + return false; + } + + public void setContext(ThreadContext context) + throws IllegalThreadStateException, DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + public String toString() { + return "t@" + id; + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ProcX86Thread)) { + return false; + } + + return (((ProcX86Thread) obj).id == id); + } + + public int hashCode() { + return id; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/x86/ProcX86ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/x86/ProcX86ThreadContext.java new file mode 100644 index 00000000000..e2b0db5e78b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/x86/ProcX86ThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.proc.*; + +public class ProcX86ThreadContext extends X86ThreadContext { + private ProcDebugger debugger; + + public ProcX86ThreadContext(ProcDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/x86/ProcX86ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/x86/ProcX86ThreadFactory.java new file mode 100644 index 00000000000..42cc44dc3b2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/x86/ProcX86ThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.proc.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.proc.*; + +public class ProcX86ThreadFactory implements ProcThreadFactory { + private ProcDebugger debugger; + + public ProcX86ThreadFactory(ProcDebugger debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new ProcX86Thread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new ProcX86Thread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteAddress.java new file mode 100644 index 00000000000..8bd981c649c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteAddress.java @@ -0,0 +1,387 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote; + +import sun.jvm.hotspot.debugger.*; + +class RemoteAddress implements Address { + protected RemoteDebuggerClient debugger; + protected long addr; + + RemoteAddress(RemoteDebuggerClient debugger, long addr) { + this.debugger = debugger; + this.addr = addr; + } + + // + // Basic Java routines + // + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof RemoteAddress)) { + return false; + } + + return (addr == ((RemoteAddress) arg).addr); + } + + public int hashCode() { + // FIXME: suggestions on a better hash code? + return (int) addr; + } + + public String toString() { + return debugger.addressValueToString(addr); + } + + // + // C/C++-related routines + // + + public long getCIntegerAt(long offset, long numBytes, boolean isUnsigned) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readCInteger(addr + offset, numBytes, isUnsigned); + } + + public Address getAddressAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readAddress(addr + offset); + } + + // + // Java-related routines + // + + public boolean getJBooleanAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJBoolean(addr + offset); + } + + public byte getJByteAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJByte(addr + offset); + } + + public char getJCharAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJChar(addr + offset); + } + + public double getJDoubleAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJDouble(addr + offset); + } + + public float getJFloatAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJFloat(addr + offset); + } + + public int getJIntAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJInt(addr + offset); + } + + public long getJLongAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJLong(addr + offset); + } + + public short getJShortAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJShort(addr + offset); + } + + public OopHandle getOopHandleAt(long offset) + throws UnalignedAddressException, UnmappedAddressException, NotInHeapException { + return debugger.readOopHandle(addr + offset); + } + + // Mutators -- not implemented for now (FIXME) + public void setCIntegerAt(long offset, long numBytes, long value) { + throw new DebuggerException("Unimplemented"); + } + public void setAddressAt(long offset, Address value) { + throw new DebuggerException("Unimplemented"); + } + public void setJBooleanAt (long offset, boolean value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJByteAt (long offset, byte value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJCharAt (long offset, char value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJDoubleAt (long offset, double value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJFloatAt (long offset, float value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJIntAt (long offset, int value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJLongAt (long offset, long value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJShortAt (long offset, short value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setOopHandleAt (long offset, OopHandle value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + + // + // Arithmetic operations -- necessary evil. + // + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new RemoteAddress(debugger, value); + } + + public OopHandle addOffsetToAsOopHandle(long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new RemoteOopHandle(debugger, value); + } + + /** (FIXME: any signed/unsigned issues? Should this work for + OopHandles?) */ + public long minus(Address arg) { + if (arg == null) { + return addr; + } + return addr - ((RemoteAddress) arg).addr; + } + + // Two's complement representation. + // All negative numbers are larger than positive numbers. + // Numbers with the same sign can be compared normally. + // Test harness is below in main(). + + public boolean lessThan (Address arg) { + if (arg == null) { + return false; + } + RemoteAddress remoteArg = (RemoteAddress) arg; + if ((addr >= 0) && (remoteArg.addr < 0)) { + return true; + } + if ((addr < 0) && (remoteArg.addr >= 0)) { + return false; + } + return (addr < remoteArg.addr); + } + + public boolean lessThanOrEqual (Address arg) { + if (arg == null) { + return false; + } + RemoteAddress remoteArg = (RemoteAddress) arg; + if ((addr >= 0) && (remoteArg.addr < 0)) { + return true; + } + if ((addr < 0) && (remoteArg.addr >= 0)) { + return false; + } + return (addr <= remoteArg.addr); + } + + public boolean greaterThan (Address arg) { + if (arg == null) { + return true; + } + RemoteAddress remoteArg = (RemoteAddress) arg; + if ((addr >= 0) && (remoteArg.addr < 0)) { + return false; + } + if ((addr < 0) && (remoteArg.addr >= 0)) { + return true; + } + return (addr > remoteArg.addr); + } + + public boolean greaterThanOrEqual(Address arg) { + if (arg == null) { + return true; + } + RemoteAddress remoteArg = (RemoteAddress) arg; + if ((addr >= 0) && (remoteArg.addr < 0)) { + return false; + } + if ((addr < 0) && (remoteArg.addr >= 0)) { + return true; + } + return (addr >= remoteArg.addr); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + long value = addr & mask; + if (value == 0) { + return null; + } + return new RemoteAddress(debugger, value); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + long value = addr | mask; + if (value == 0) { + return null; + } + return new RemoteAddress(debugger, value); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + long value = addr ^ mask; + if (value == 0) { + return null; + } + return new RemoteAddress(debugger, value); + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + long getValue() { + return addr; + } + + + private static void check(boolean arg, String failMessage) { + if (!arg) { + System.err.println(failMessage + ": FAILED"); + System.exit(1); + } + } + + // Test harness + public static void main(String[] args) { + // p/n indicates whether the interior address is really positive + // or negative. In unsigned terms, p1 < p2 < n1 < n2. + + RemoteAddress p1 = new RemoteAddress(null, 0x7FFFFFFFFFFFFFF0L); + RemoteAddress p2 = (RemoteAddress) p1.addOffsetTo(10); + RemoteAddress n1 = (RemoteAddress) p2.addOffsetTo(10); + RemoteAddress n2 = (RemoteAddress) n1.addOffsetTo(10); + + // lessThan positive tests + check(p1.lessThan(p2), "lessThan 1"); + check(p1.lessThan(n1), "lessThan 2"); + check(p1.lessThan(n2), "lessThan 3"); + check(p2.lessThan(n1), "lessThan 4"); + check(p2.lessThan(n2), "lessThan 5"); + check(n1.lessThan(n2), "lessThan 6"); + + // lessThan negative tests + check(!p1.lessThan(p1), "lessThan 7"); + check(!p2.lessThan(p2), "lessThan 8"); + check(!n1.lessThan(n1), "lessThan 9"); + check(!n2.lessThan(n2), "lessThan 10"); + + check(!p2.lessThan(p1), "lessThan 11"); + check(!n1.lessThan(p1), "lessThan 12"); + check(!n2.lessThan(p1), "lessThan 13"); + check(!n1.lessThan(p2), "lessThan 14"); + check(!n2.lessThan(p2), "lessThan 15"); + check(!n2.lessThan(n1), "lessThan 16"); + + // lessThanOrEqual positive tests + check(p1.lessThanOrEqual(p1), "lessThanOrEqual 1"); + check(p2.lessThanOrEqual(p2), "lessThanOrEqual 2"); + check(n1.lessThanOrEqual(n1), "lessThanOrEqual 3"); + check(n2.lessThanOrEqual(n2), "lessThanOrEqual 4"); + + check(p1.lessThanOrEqual(p2), "lessThanOrEqual 5"); + check(p1.lessThanOrEqual(n1), "lessThanOrEqual 6"); + check(p1.lessThanOrEqual(n2), "lessThanOrEqual 7"); + check(p2.lessThanOrEqual(n1), "lessThanOrEqual 8"); + check(p2.lessThanOrEqual(n2), "lessThanOrEqual 9"); + check(n1.lessThanOrEqual(n2), "lessThanOrEqual 10"); + + // lessThanOrEqual negative tests + check(!p2.lessThanOrEqual(p1), "lessThanOrEqual 11"); + check(!n1.lessThanOrEqual(p1), "lessThanOrEqual 12"); + check(!n2.lessThanOrEqual(p1), "lessThanOrEqual 13"); + check(!n1.lessThanOrEqual(p2), "lessThanOrEqual 14"); + check(!n2.lessThanOrEqual(p2), "lessThanOrEqual 15"); + check(!n2.lessThanOrEqual(n1), "lessThanOrEqual 16"); + + // greaterThan positive tests + check(n2.greaterThan(p1), "greaterThan 1"); + check(n2.greaterThan(p2), "greaterThan 2"); + check(n2.greaterThan(n1), "greaterThan 3"); + check(n1.greaterThan(p1), "greaterThan 4"); + check(n1.greaterThan(p2), "greaterThan 5"); + check(p2.greaterThan(p1), "greaterThan 6"); + + // greaterThan negative tests + check(!p1.greaterThan(p1), "greaterThan 7"); + check(!p2.greaterThan(p2), "greaterThan 8"); + check(!n1.greaterThan(n1), "greaterThan 9"); + check(!n2.greaterThan(n2), "greaterThan 10"); + + check(!p1.greaterThan(n2), "greaterThan 11"); + check(!p2.greaterThan(n2), "greaterThan 12"); + check(!n1.greaterThan(n2), "greaterThan 13"); + check(!p1.greaterThan(n1), "greaterThan 14"); + check(!p2.greaterThan(n1), "greaterThan 15"); + check(!p1.greaterThan(p2), "greaterThan 16"); + + // greaterThanOrEqual positive tests + check(p1.greaterThanOrEqual(p1), "greaterThanOrEqual 1"); + check(p2.greaterThanOrEqual(p2), "greaterThanOrEqual 2"); + check(n1.greaterThanOrEqual(n1), "greaterThanOrEqual 3"); + check(n2.greaterThanOrEqual(n2), "greaterThanOrEqual 4"); + + check(n2.greaterThanOrEqual(p1), "greaterThanOrEqual 5"); + check(n2.greaterThanOrEqual(p2), "greaterThanOrEqual 6"); + check(n2.greaterThanOrEqual(n1), "greaterThanOrEqual 7"); + check(n1.greaterThanOrEqual(p1), "greaterThanOrEqual 8"); + check(n1.greaterThanOrEqual(p2), "greaterThanOrEqual 9"); + check(p2.greaterThanOrEqual(p1), "greaterThanOrEqual 10"); + + // greaterThanOrEqual negative tests + check(!p1.greaterThanOrEqual(n2), "greaterThanOrEqual 11"); + check(!p2.greaterThanOrEqual(n2), "greaterThanOrEqual 12"); + check(!n1.greaterThanOrEqual(n2), "greaterThanOrEqual 13"); + check(!p1.greaterThanOrEqual(n1), "greaterThanOrEqual 14"); + check(!p2.greaterThanOrEqual(n1), "greaterThanOrEqual 15"); + check(!p1.greaterThanOrEqual(p2), "greaterThanOrEqual 16"); + + System.err.println("RemoteAddress: all tests passed successfully."); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebugger.java new file mode 100644 index 00000000000..2b776a064eb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebugger.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote; + +import java.rmi.*; + +import sun.jvm.hotspot.debugger.*; + +/**

This interface describes the methods which are used in a + remote debugging scenario. It is only necessary because RMI + requires that all such methods throw RemoteException, which is a + checked (i.e., not a Runtime) exception. Since we already have a + suitable runtime exception (DebuggerException) present in the + signatures for all of the debugger-related methods, we would like + to repurpose this to wrap RemoteExceptions. This is done by + wrapping the actual remote debugger object +

+ +

NOTE that this interface currently assumes that the debugger + on the remote machine has already been attached to the target + process or has opened the desired core file. This implies that the + machine hosting the user interface can not effect + attaching/detaching. Currently this restriction has been enforced + to make the user interface less confusing, but there will also be + security concerns with allowing clients to attach to arbitrary + remote processes.

+*/ + +public interface RemoteDebugger extends Remote { + public String getOS() throws RemoteException; + public String getCPU() throws RemoteException; + public MachineDescription getMachineDescription() throws RemoteException; + public long lookupInProcess(String objectName, String symbol) throws RemoteException; + public ReadResult readBytesFromProcess(long address, long numBytes) throws RemoteException; + public boolean hasConsole() throws RemoteException; + public String getConsolePrompt() throws RemoteException; + public String consoleExecuteCommand(String cmd) throws RemoteException; + public long getJBooleanSize() throws RemoteException; + public long getJByteSize() throws RemoteException; + public long getJCharSize() throws RemoteException; + public long getJDoubleSize() throws RemoteException; + public long getJFloatSize() throws RemoteException; + public long getJIntSize() throws RemoteException; + public long getJLongSize() throws RemoteException; + public long getJShortSize() throws RemoteException; + public boolean areThreadsEqual(long addrOrId1, boolean isAddress1, + long addrOrId2, boolean isAddress2) throws RemoteException; + public int getThreadHashCode(long addrOrId, boolean isAddress) throws RemoteException; + public long[] getThreadIntegerRegisterSet(long addrOrId, boolean isAddress) throws RemoteException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebuggerClient.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebuggerClient.java new file mode 100644 index 00000000000..bcf5e512bda --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebuggerClient.java @@ -0,0 +1,381 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote; + +import java.rmi.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.remote.sparc.*; +import sun.jvm.hotspot.debugger.remote.x86.*; +import sun.jvm.hotspot.debugger.remote.amd64.*; + +/** An implementation of Debugger which wraps a + RemoteDebugger, providing remote debugging via RMI. + This implementation provides caching of the remote process's + address space on the local machine where the user interface is + running. */ + +public class RemoteDebuggerClient extends DebuggerBase implements JVMDebugger { + private RemoteDebugger remoteDebugger; + private RemoteThreadFactory threadFactory; + private boolean unalignedAccessesOkay = false; + private static final int cacheSize = 16 * 1024 * 1024; // 16 MB + + public RemoteDebuggerClient(RemoteDebugger remoteDebugger) throws DebuggerException { + super(); + try { + this.remoteDebugger = remoteDebugger; + machDesc = remoteDebugger.getMachineDescription(); + utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); + int cacheNumPages; + int cachePageSize; + String cpu = remoteDebugger.getCPU(); + // page size. (FIXME: should pick this up from the remoteDebugger.) + if (cpu.equals("sparc")) { + threadFactory = new RemoteSPARCThreadFactory(this); + cachePageSize = 8192; + cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); + } else if (cpu.equals("x86")) { + threadFactory = new RemoteX86ThreadFactory(this); + cachePageSize = 4096; + cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); + unalignedAccessesOkay = true; + } else if (cpu.equals("amd64")) { + threadFactory = new RemoteAMD64ThreadFactory(this); + cachePageSize = 4096; + cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); + unalignedAccessesOkay = true; + } else { + throw new DebuggerException("Thread access for CPU architecture " + cpu + " not yet supported"); + } + + // Cache portion of the remote process's address space. + initCache(cachePageSize, cacheNumPages); + + jbooleanSize = remoteDebugger.getJBooleanSize(); + jbyteSize = remoteDebugger.getJByteSize(); + jcharSize = remoteDebugger.getJCharSize(); + jdoubleSize = remoteDebugger.getJDoubleSize(); + jfloatSize = remoteDebugger.getJFloatSize(); + jintSize = remoteDebugger.getJIntSize(); + jlongSize = remoteDebugger.getJLongSize(); + jshortSize = remoteDebugger.getJShortSize(); + javaPrimitiveTypesConfigured = true; + } + catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + public long[] getThreadIntegerRegisterSet(Address addr) { + try { + return remoteDebugger.getThreadIntegerRegisterSet(getAddressValue(addr), true); + } + catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + public long[] getThreadIntegerRegisterSet(long id) { + try { + return remoteDebugger.getThreadIntegerRegisterSet(id, false); + } + catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + /** Unimplemented in this class (remote remoteDebugger should already be attached) */ + public boolean hasProcessList() throws DebuggerException { + throw new DebuggerException("Should not be called on RemoteDebuggerClient"); + } + + /** Unimplemented in this class (remote remoteDebugger should already be attached) */ + public List getProcessList() throws DebuggerException { + throw new DebuggerException("Should not be called on RemoteDebuggerClient"); + } + + /** Unimplemented in this class (remote remoteDebugger should already be attached) */ + public void attach(int processID) throws DebuggerException { + throw new DebuggerException("Should not be called on RemoteDebuggerClient"); + } + + /** Unimplemented in this class (remote remoteDebugger should already be attached) */ + public void attach(String executableName, String coreFileName) throws DebuggerException { + throw new DebuggerException("Should not be called on RemoteDebuggerClient"); + } + + /** Unimplemented in this class (remote remoteDebugger can not be detached) */ + public boolean detach() { + throw new DebuggerException("Should not be called on RemoteDebuggerClient"); + } + + public Address parseAddress(String addressString) throws NumberFormatException { + long addr = utils.scanAddress(addressString); + if (addr == 0) { + return null; + } + return new RemoteAddress(this, addr); + } + + public String getOS() throws DebuggerException { + try { + return remoteDebugger.getOS(); + } + catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + public String getCPU() { + try { + return remoteDebugger.getCPU(); + } + catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + public boolean hasConsole() throws DebuggerException { + try { + return remoteDebugger.hasConsole(); + } catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + public String consoleExecuteCommand(String cmd) throws DebuggerException { + try { + return remoteDebugger.consoleExecuteCommand(cmd); + } + catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + public String getConsolePrompt() throws DebuggerException { + try { + return remoteDebugger.getConsolePrompt(); + } catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + public CDebugger getCDebugger() throws DebuggerException { + return null; + } + + //-------------------------------------------------------------------------------- + // Implementation of SymbolLookup interface + + public Address lookup(String objectName, String symbol) { + try { + long addr = remoteDebugger.lookupInProcess(objectName, symbol); + if (addr == 0) { + return null; + } + return new RemoteAddress(this, addr); + } + catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + public OopHandle lookupOop(String objectName, String symbol) { + try { + long addr = remoteDebugger.lookupInProcess(objectName, symbol); + if (addr == 0) { + return null; + } + return new RemoteOopHandle(this, addr); + } + catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + /** Need to override this to relax alignment checks on x86. */ + public long readCInteger(long address, long numBytes, boolean isUnsigned) + throws UnmappedAddressException, UnalignedAddressException { + if (!unalignedAccessesOkay) { + utils.checkAlignment(address, numBytes); + } else { + // Only slightly relaxed semantics -- this is a hack, but is + // necessary on x86 where it seems the compiler is + // putting some global 64-bit data on 32-bit boundaries + if (numBytes == 8) { + utils.checkAlignment(address, 4); + } else { + utils.checkAlignment(address, numBytes); + } + } + byte[] data = readBytes(address, numBytes); + return utils.dataToCInteger(data, isUnsigned); + } + + // Overridden from DebuggerBase because we need to relax alignment + // constraints on x86 + public long readJLong(long address) + throws UnmappedAddressException, UnalignedAddressException { + // FIXME: allow this to be configurable. Undesirable to add a + // dependency on the runtime package here, though, since this + // package should be strictly underneath it. + if (unalignedAccessesOkay) { + utils.checkAlignment(address, jintSize); + } else { + utils.checkAlignment(address, jlongSize); + } + byte[] data = readBytes(address, jlongSize); + return utils.dataToJLong(data, jlongSize); + } + + + //-------------------------------------------------------------------------------- + // Implementation of JVMDebugger interface + // + + /** Unimplemented in this class (remote remoteDebugger should already be configured) */ + public void configureJavaPrimitiveTypeSizes(long jbooleanSize, + long jbyteSize, + long jcharSize, + long jdoubleSize, + long jfloatSize, + long jintSize, + long jlongSize, + long jshortSize) { + throw new DebuggerException("Should not be called on RemoteDebuggerClient"); + } + + public void setMachineDescription(MachineDescription machDesc) { + throw new DebuggerException("Should not be called on RemoteDebuggerClient"); + } + + public int getRemoteProcessAddressSize() { + throw new DebuggerException("Should not be called on RemoteDebuggerClient"); + } + + public String addressValueToString(long addr) { + return utils.addressValueToString(addr); + } + + public long getAddressValue(Address addr) throws DebuggerException { + if (addr == null) return 0; + return ((RemoteAddress) addr).getValue(); + } + + public Address newAddress(long value) { + if (value == 0) return null; + return new RemoteAddress(this, value); + } + + RemoteAddress readAddress(long address) + throws UnmappedAddressException, UnalignedAddressException { + long value = readAddressValue(address); + return (value == 0 ? null : new RemoteAddress(this, value)); + } + + RemoteOopHandle readOopHandle(long address) + throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { + long value = readAddressValue(address); + return (value == 0 ? null : new RemoteOopHandle(this, value)); + } + + boolean areThreadsEqual(Address addr1, Address addr2) { + try { + return remoteDebugger.areThreadsEqual(getAddressValue(addr1), true, + getAddressValue(addr2), true); + } catch (RemoteException e) { + } + return false; + } + + boolean areThreadsEqual(long id1, long id2) { + try { + return remoteDebugger.areThreadsEqual(id1, false, id2, false); + } catch (RemoteException e) { + } + return false; + } + + boolean areThreadsEqual(Address addr1, long id2) { + try { + return remoteDebugger.areThreadsEqual(getAddressValue(addr1), true, id2, false); + } catch (RemoteException e) { + } + return false; + } + + boolean areThreadsEqual(long id1, Address addr2) { + try { + return remoteDebugger.areThreadsEqual(id1, false, getAddressValue(addr2), true); + } catch (RemoteException e) { + } + return false; + } + + int getThreadHashCode(Address a) { + try { + return remoteDebugger.getThreadHashCode(getAddressValue(a), true); + } catch (RemoteException e) { + } + return a.hashCode(); + } + + int getThreadHashCode(long id) { + try { + return remoteDebugger.getThreadHashCode(id, false); + } catch (RemoteException e) { + } + return (int) id; + } + + public ThreadProxy getThreadForIdentifierAddress(Address addr) { + return threadFactory.createThreadWrapper(addr); + } + + public ThreadProxy getThreadForThreadId(long id) { + return threadFactory.createThreadWrapper(id); + } + + public MachineDescription getMachineDescription() throws DebuggerException { + return machDesc; + } + + /** This reads bytes from the remote process. */ + public ReadResult readBytesFromProcess(long address, long numBytes) { + try { + return remoteDebugger.readBytesFromProcess(address, numBytes); + } + catch (RemoteException e) { + throw new DebuggerException(e); + } + } + + public void writeBytesToProcess(long a, long b, byte[] c) { + throw new DebuggerException("Unimplemented!"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebuggerServer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebuggerServer.java new file mode 100644 index 00000000000..42056f45463 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebuggerServer.java @@ -0,0 +1,148 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote; + +import java.rmi.*; +import java.rmi.server.*; + +import sun.jvm.hotspot.debugger.*; + +/** The implementation of the RemoteDebugger interface. This + delegates to a local debugger */ + +public class RemoteDebuggerServer extends UnicastRemoteObject + implements RemoteDebugger { + + private transient Debugger debugger; + + /** This is the required no-arg constructor */ + public RemoteDebuggerServer() throws RemoteException { + super(); + } + + /** This is the constructor used on the machine where the debuggee + process lies */ + public RemoteDebuggerServer(Debugger debugger) throws RemoteException { + super(); + this.debugger = debugger; + } + + public String getOS() throws RemoteException { + return debugger.getOS(); + } + + public String getCPU() throws RemoteException { + return debugger.getCPU(); + } + + public MachineDescription getMachineDescription() throws RemoteException { + return debugger.getMachineDescription(); + } + + public long lookupInProcess(String objectName, String symbol) throws RemoteException { + Address addr = debugger.lookup(objectName, symbol); + return addr == null? 0L : debugger.getAddressValue(addr); + } + + public ReadResult readBytesFromProcess(long address, long numBytes) throws RemoteException { + return debugger.readBytesFromProcess(address, numBytes); + } + + public boolean hasConsole() throws RemoteException { + return debugger.hasConsole(); + } + + public String getConsolePrompt() throws RemoteException { + return debugger.getConsolePrompt(); + } + + public String consoleExecuteCommand(String cmd) throws RemoteException { + return debugger.consoleExecuteCommand(cmd); + } + + public long getJBooleanSize() throws RemoteException { + return debugger.getJBooleanSize(); + } + + public long getJByteSize() throws RemoteException { + return debugger.getJByteSize(); + } + + public long getJCharSize() throws RemoteException { + return debugger.getJCharSize(); + } + + public long getJDoubleSize() throws RemoteException { + return debugger.getJDoubleSize(); + } + + public long getJFloatSize() throws RemoteException { + return debugger.getJFloatSize(); + } + + public long getJIntSize() throws RemoteException { + return debugger.getJIntSize(); + } + + public long getJLongSize() throws RemoteException { + return debugger.getJLongSize(); + } + + public long getJShortSize() throws RemoteException { + return debugger.getJShortSize(); + } + + public boolean areThreadsEqual(long addrOrId1, boolean isAddress1, + long addrOrId2, boolean isAddress2) throws RemoteException { + ThreadProxy t1 = getThreadProxy(addrOrId1, isAddress1); + ThreadProxy t2 = getThreadProxy(addrOrId2, isAddress2); + return t1.equals(t2); + } + + + public int getThreadHashCode(long addrOrId, boolean isAddress) throws RemoteException { + ThreadProxy t = getThreadProxy(addrOrId, isAddress); + return t.hashCode(); + } + + public long[] getThreadIntegerRegisterSet(long addrOrId, boolean isAddress) throws RemoteException { + ThreadProxy t = getThreadProxy(addrOrId, isAddress); + ThreadContext tc = t.getContext(); + long[] regs = new long[tc.getNumRegisters()]; + for (int r = 0; r < regs.length; r++) { + regs[r] = tc.getRegister(r); + } + return regs; + } + + private ThreadProxy getThreadProxy(long addrOrId, boolean isAddress) throws DebuggerException { + if (isAddress) { + Address addr = debugger.parseAddress("0x" + Long.toHexString(addrOrId)); + return debugger.getThreadForIdentifierAddress(addr); + } else { + return debugger.getThreadForThreadId(addrOrId); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteOopHandle.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteOopHandle.java new file mode 100644 index 00000000000..452ce13f108 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteOopHandle.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote; + +import sun.jvm.hotspot.debugger.*; + +class RemoteOopHandle extends RemoteAddress implements OopHandle { + RemoteOopHandle(RemoteDebuggerClient debugger, long addr) { + super(debugger, addr); + } + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + throw new UnsupportedOperationException("addOffsetTo not applicable to OopHandles (interior object pointers not allowed)"); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("andWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("orWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("xorWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteThread.java new file mode 100644 index 00000000000..601e985800e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteThread.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote; + +import sun.jvm.hotspot.debugger.*; + +public abstract class RemoteThread implements ThreadProxy { + protected RemoteDebuggerClient debugger; + protected Address addr; + protected long id; + + public RemoteThread(RemoteDebuggerClient debugger, Address addr) { + this.debugger = debugger; + this.addr = addr; + this.id = -1L; // invalid, but don't depend on it. check null for addr + } + + public RemoteThread(RemoteDebuggerClient debugger, long id) { + this.debugger = debugger; + this.addr = null; + this.id = id; + } + + public boolean canSetContext() throws DebuggerException { + return false; + } + + public void setContext(ThreadContext context) + throws IllegalThreadStateException, DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + public boolean equals(Object o) { + if (o == null) { + return false; + } + + if (! (o instanceof RemoteThread)) { + return false; + } + RemoteThread other = (RemoteThread)o; + boolean isOtherAddress = (other.addr != null); + boolean isAddress = (addr != null); + + if (isAddress) { + return (isOtherAddress)? debugger.areThreadsEqual(addr, other.addr) : + debugger.areThreadsEqual(addr, other.id); + } else { + return (isOtherAddress)? debugger.areThreadsEqual(id, other.addr) : + debugger.areThreadsEqual(id, other.id); + } + } + + public int hashCode() { + return (addr != null)? debugger.getThreadHashCode(addr) : + debugger.getThreadHashCode(id); + } + + public String toString() { + return "t@ " + hashCode(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteThreadFactory.java new file mode 100644 index 00000000000..e8ae8d5cf08 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteThreadFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote; + +import sun.jvm.hotspot.debugger.*; + +/** An interface used only internally by the ProcDebugger to be able to + create platform-specific Thread objects */ + +public interface RemoteThreadFactory { + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr); + public ThreadProxy createThreadWrapper(long id); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/amd64/RemoteAMD64Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/amd64/RemoteAMD64Thread.java new file mode 100644 index 00000000000..2c36f2a46e6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/amd64/RemoteAMD64Thread.java @@ -0,0 +1,53 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.remote.*; +import sun.jvm.hotspot.utilities.*; + +public class RemoteAMD64Thread extends RemoteThread { + public RemoteAMD64Thread(RemoteDebuggerClient debugger, Address addr) { + super(debugger, addr); + } + + public RemoteAMD64Thread(RemoteDebuggerClient debugger, long id) { + super(debugger, id); + } + + public ThreadContext getContext() throws IllegalThreadStateException { + RemoteAMD64ThreadContext context = new RemoteAMD64ThreadContext(debugger); + long[] regs = (addr != null)? debugger.getThreadIntegerRegisterSet(addr) : + debugger.getThreadIntegerRegisterSet(id); + if (Assert.ASSERTS_ENABLED) { + Assert.that(regs.length == AMD64ThreadContext.NPRGREG, "size of register set must match"); + } + for (int i = 0; i < regs.length; i++) { + context.setRegister(i, regs[i]); + } + return context; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/amd64/RemoteAMD64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/amd64/RemoteAMD64ThreadContext.java new file mode 100644 index 00000000000..f196fa34556 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/amd64/RemoteAMD64ThreadContext.java @@ -0,0 +1,50 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.remote.*; + +public class RemoteAMD64ThreadContext extends AMD64ThreadContext { + private RemoteDebuggerClient debugger; + + public RemoteAMD64ThreadContext(RemoteDebuggerClient debugger) { + super(); + this.debugger = debugger; + } + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/amd64/RemoteAMD64ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/amd64/RemoteAMD64ThreadFactory.java new file mode 100644 index 00000000000..fcd5e424f7a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/amd64/RemoteAMD64ThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.remote.*; + +public class RemoteAMD64ThreadFactory implements RemoteThreadFactory { + private RemoteDebuggerClient debugger; + + public RemoteAMD64ThreadFactory(RemoteDebuggerClient debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new RemoteAMD64Thread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new RemoteAMD64Thread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/sparc/RemoteSPARCThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/sparc/RemoteSPARCThread.java new file mode 100644 index 00000000000..9b8b29bc992 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/sparc/RemoteSPARCThread.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.remote.*; +import sun.jvm.hotspot.utilities.*; + +public class RemoteSPARCThread extends RemoteThread { + public RemoteSPARCThread(RemoteDebuggerClient debugger, Address addr) { + super(debugger, addr); + } + + public RemoteSPARCThread(RemoteDebuggerClient debugger, long id) { + super(debugger, id); + } + + public ThreadContext getContext() throws IllegalThreadStateException { + RemoteSPARCThreadContext context = new RemoteSPARCThreadContext(debugger); + long[] regs = (addr != null)? debugger.getThreadIntegerRegisterSet(addr) : + debugger.getThreadIntegerRegisterSet(id); + + if (Assert.ASSERTS_ENABLED) { + Assert.that(regs.length == SPARCThreadContext.NPRGREG, "size of register set must match"); + } + for (int i = 0; i < regs.length; i++) { + context.setRegister(i, regs[i]); + } + return context; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/sparc/RemoteSPARCThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/sparc/RemoteSPARCThreadContext.java new file mode 100644 index 00000000000..d78326bed07 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/sparc/RemoteSPARCThreadContext.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.debugger.remote.*; + +public class RemoteSPARCThreadContext extends SPARCThreadContext { + private RemoteDebuggerClient debugger; + + public RemoteSPARCThreadContext(RemoteDebuggerClient debugger) { + super(); + this.debugger = debugger; + } + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/sparc/RemoteSPARCThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/sparc/RemoteSPARCThreadFactory.java new file mode 100644 index 00000000000..db0acc7ea86 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/sparc/RemoteSPARCThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote.sparc; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.remote.*; + +public class RemoteSPARCThreadFactory implements RemoteThreadFactory { + private RemoteDebuggerClient debugger; + + public RemoteSPARCThreadFactory(RemoteDebuggerClient debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new RemoteSPARCThread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new RemoteSPARCThread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/x86/RemoteX86Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/x86/RemoteX86Thread.java new file mode 100644 index 00000000000..327846471d7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/x86/RemoteX86Thread.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.remote.*; +import sun.jvm.hotspot.utilities.*; + +public class RemoteX86Thread extends RemoteThread { + public RemoteX86Thread(RemoteDebuggerClient debugger, Address addr) { + super(debugger, addr); + } + + public RemoteX86Thread(RemoteDebuggerClient debugger, long id) { + super(debugger, id); + } + + public ThreadContext getContext() throws IllegalThreadStateException { + RemoteX86ThreadContext context = new RemoteX86ThreadContext(debugger); + long[] regs = (addr != null)? debugger.getThreadIntegerRegisterSet(addr) : + debugger.getThreadIntegerRegisterSet(id); + if (Assert.ASSERTS_ENABLED) { + Assert.that(regs.length == X86ThreadContext.NPRGREG, "size of register set must match"); + } + for (int i = 0; i < regs.length; i++) { + context.setRegister(i, regs[i]); + } + return context; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/x86/RemoteX86ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/x86/RemoteX86ThreadContext.java new file mode 100644 index 00000000000..f48e1dcfbbc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/x86/RemoteX86ThreadContext.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.remote.*; + +public class RemoteX86ThreadContext extends X86ThreadContext { + private RemoteDebuggerClient debugger; + + public RemoteX86ThreadContext(RemoteDebuggerClient debugger) { + super(); + this.debugger = debugger; + } + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/x86/RemoteX86ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/x86/RemoteX86ThreadFactory.java new file mode 100644 index 00000000000..0069b01b0d2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/x86/RemoteX86ThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.remote.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.remote.*; + +public class RemoteX86ThreadFactory implements RemoteThreadFactory { + private RemoteDebuggerClient debugger; + + public RemoteX86ThreadFactory(RemoteDebuggerClient debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new RemoteX86Thread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new RemoteX86Thread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/sparc/SPARCThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/sparc/SPARCThreadContext.java new file mode 100644 index 00000000000..e5b374f1fec --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/sparc/SPARCThreadContext.java @@ -0,0 +1,134 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.sparc; + +import sun.jvm.hotspot.debugger.*; + +/** Currently provides just the minimal information necessary to get + stack traces working. FIXME: currently hardwired for v9 -- will + have to factor out v8/v9 specific code. FIXME: may want to try to + share code between this class and asm/sparc. */ + +public abstract class SPARCThreadContext implements ThreadContext { + // Taken from /usr/include/sys/procfs_isa.h + public static final int R_G0 = 0; + public static final int R_G1 = 1; + public static final int R_G2 = 2; + public static final int R_G3 = 3; + public static final int R_G4 = 4; + public static final int R_G5 = 5; + public static final int R_G6 = 6; + public static final int R_G7 = 7; + public static final int R_O0 = 8; + public static final int R_O1 = 9; + public static final int R_O2 = 10; + public static final int R_O3 = 11; + public static final int R_O4 = 12; + public static final int R_O5 = 13; + public static final int R_O6 = 14; + public static final int R_O7 = 15; + public static final int R_L0 = 16; + public static final int R_L1 = 17; + public static final int R_L2 = 18; + public static final int R_L3 = 19; + public static final int R_L4 = 20; + public static final int R_L5 = 21; + public static final int R_L6 = 22; + public static final int R_L7 = 23; + public static final int R_I0 = 24; + public static final int R_I1 = 25; + public static final int R_I2 = 26; + public static final int R_I3 = 27; + public static final int R_I4 = 28; + public static final int R_I5 = 29; + public static final int R_I6 = 30; + public static final int R_I7 = 31; + + // sparc-v9 + public static final int R_CCR = 32; + // sparc-v8 + public static final int R_PSR = 32; + + public static final int R_PC = 33; + public static final int R_nPC = 34; + + public static final int R_SP = R_O6; + public static final int R_FP = R_I6; + + public static final int R_Y = 35; + + // sparc-v9 + public static final int R_ASI = 36; + public static final int R_FPRS = 37; + + // sparc-v8 + public static final int R_WIM = 36; + public static final int R_TBR = 37; + + public static final int NPRGREG = 38; + + private static final String[] regNames = { + "G0", "G1", "G2", "G3", + "G4", "G5", "G6", "G7", + "O0", "O1", "O2", "O3", + "O4", "O5", "O6/SP", "O7", + "L0", "L1", "L2", "L3", + "L4", "L5", "L6", "L7", + "I0", "I1", "I2", "I3", + "I4", "I5", "I6/FP", "I7", + "CCR/PSR", "PC", "nPC", "Y", + "ASI/WIM", "FPRS/TBR" + }; + + private long[] data; + + public SPARCThreadContext() { + data = new long[NPRGREG]; + } + + public int getNumRegisters() { + return NPRGREG; + } + + public String getRegisterName(int index) { + return regNames[index]; + } + + public void setRegister(int index, long value) { + data[index] = value; + } + + public long getRegister(int index) { + return data[index]; + } + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public abstract void setRegisterAsAddress(int index, Address value); + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public abstract Address getRegisterAsAddress(int index); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/AddressDataSource.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/AddressDataSource.java new file mode 100644 index 00000000000..cb38f405fc0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/AddressDataSource.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import java.io.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.win32.coff.*; + +class AddressDataSource implements DataSource { + AddressDataSource(Address addr) { + this.addr = addr; + offset = 0; + } + + public byte readByte() throws IOException { + try { + byte res = (byte) addr.getCIntegerAt(offset, 1, false); + ++offset; + return res; + } catch (UnmappedAddressException e) { + throw (IOException) new IOException("Unmapped address at 0x" + Long.toHexString(e.getAddress())).initCause(e); + } catch (DebuggerException e) { + throw (IOException) new IOException(e.toString()).initCause(e); + } + } + + public short readShort() throws IOException { + // NOTE: byte swapping is taken care of at the COFFFileImpl level + int b1 = readByte() & 0xFF; + int b2 = readByte() & 0xFF; + return (short) ((b1 << 8) | b2); + } + + public int readInt() throws IOException { + // NOTE: byte swapping is taken care of at the COFFFileImpl level + int b1 = ((int) readByte()) & 0xFF; + int b2 = ((int) readByte()) & 0xFF; + int b3 = ((int) readByte()) & 0xFF; + int b4 = ((int) readByte()) & 0xFF; + return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4); + } + + public long readLong() throws IOException { + // NOTE: byte swapping is taken care of at the COFFFileImpl level + long b1 = ((long) readByte()) & 0xFFL; + long b2 = ((long) readByte()) & 0xFFL; + long b3 = ((long) readByte()) & 0xFFL; + long b4 = ((long) readByte()) & 0xFFL; + long b5 = ((long) readByte()) & 0xFFL; + long b6 = ((long) readByte()) & 0xFFL; + long b7 = ((long) readByte()) & 0xFFL; + long b8 = ((long) readByte()) & 0xFFL; + return (((((b1 << 24) | (b2 << 16) | (b3 << 8) | b4)) << 32) | + ((((b5 << 24) | (b6 << 16) | (b7 << 8) | b8)))); + } + + public int read(byte[] b) throws IOException { + for (int i = 0; i < b.length; i++) { + b[i] = readByte(); + } + return b.length; + } + + public void seek(long pos) throws IOException { + offset = pos; + } + + public long getFilePointer() throws IOException { + return offset; + } + + public void close() throws IOException { + } + + private Address addr; + private long offset; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/DLL.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/DLL.java new file mode 100644 index 00000000000..6c581689510 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/DLL.java @@ -0,0 +1,209 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.win32.coff.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.utilities.Assert; +import sun.jvm.hotspot.utilities.memo.*; + +/** Provides a simple wrapper around the COFF library which handles + relocation. A DLL can represent either a DLL or an EXE file. */ + +class DLL implements LoadObject { + + DLL(Win32Debugger dbg, String filename, long size, Address relocation) throws COFFException { + this.dbg = dbg; + fullPathName = filename; + this.size = size; + file = new MemoizedObject() { + public Object computeValue() { + return COFFFileParser.getParser().parse(fullPathName); + } + }; + addr = relocation; + } + + /** This constructor was originally used to fetch the DLL's name out + of the target process to match it up with the known DLL names, + before the fetching of the DLL names and bases was folded into + one command. It is no longer used. If it is used, getName() will + return null and getSize() will return 0. */ + DLL(Address base) throws COFFException { + this.addr = base; + file = new MemoizedObject() { + public Object computeValue() { + return COFFFileParser.getParser().parse(new AddressDataSource(addr)); + } + }; + } + + /** Indicates whether this is really a DLL or actually a .EXE + file. */ + boolean isDLL() { + return getFile().getHeader().hasCharacteristic(Characteristics.IMAGE_FILE_DLL); + } + + /** Look up a symbol; returns absolute address or null if symbol was + not found. */ + Address lookupSymbol(String symbol) throws COFFException { + if (!isDLL()) { + return null; + } + ExportDirectoryTable exports = getExportDirectoryTable(); + return lookupSymbol(symbol, exports, + 0, exports.getNumberOfNamePointers() - 1); + } + + public Address getBase() { + return addr; + } + + /** Returns the full path name of this DLL/EXE, or null if this DLL + object was created by parsing the target process's address + space. */ + public String getName() { + return fullPathName; + } + + public long getSize() { + return size; + } + + public CDebugInfoDataBase getDebugInfoDataBase() throws DebuggerException { + if (db != null) { + return db; + } + + // Try to parse + if (dbg == null) { + return null; // Need Win32Debugger + } + + if (Assert.ASSERTS_ENABLED) { + Assert.that(fullPathName != null, "Need full path name to build debug info database"); + } + + db = new Win32CDebugInfoBuilder(dbg).buildDataBase(fullPathName, addr); + return db; + } + + public BlockSym debugInfoForPC(Address pc) throws DebuggerException { + CDebugInfoDataBase db = getDebugInfoDataBase(); + if (db == null) { + return null; + } + return db.debugInfoForPC(pc); + } + + public ClosestSymbol closestSymbolToPC(Address pcAsAddr) throws DebuggerException { + ExportDirectoryTable exports = getExportDirectoryTable(); + if (exports == null) { + return null; + } + String name = null; + long pc = dbg.getAddressValue(pcAsAddr); + long diff = Long.MAX_VALUE; + long base = dbg.getAddressValue(addr); + for (int i = 0; i < exports.getNumberOfNamePointers(); i++) { + if (!exports.isExportAddressForwarder(exports.getExportOrdinal(i))) { + long tmp = base + (exports.getExportAddress(exports.getExportOrdinal(i)) & 0xFFFFFFFF); + if ((tmp <= pc) && ((pc - tmp) < diff)) { + diff = pc - tmp; + name = exports.getExportName(i); + } + } + } + if (name == null) { + return null; + } + return new ClosestSymbol(name, diff); + } + + public LineNumberInfo lineNumberForPC(Address pc) throws DebuggerException { + CDebugInfoDataBase db = getDebugInfoDataBase(); + if (db == null) { + return null; + } + return db.lineNumberForPC(pc); + } + + void close() { + getFile().close(); + file = null; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private COFFFile getFile() { + return (COFFFile) file.getValue(); + } + + private Address lookupSymbol(String symbol, ExportDirectoryTable exports, + int loIdx, int hiIdx) { + do { + int curIdx = ((loIdx + hiIdx) >> 1); + String cur = exports.getExportName(curIdx); + if (symbol.equals(cur)) { + return addr.addOffsetTo( + ((long) exports.getExportAddress(exports.getExportOrdinal(curIdx))) & 0xFFFFFFFFL + ); + } + if (symbol.compareTo(cur) < 0) { + if (hiIdx == curIdx) { + hiIdx = curIdx - 1; + } else { + hiIdx = curIdx; + } + } else { + if (loIdx == curIdx) { + loIdx = curIdx + 1; + } else { + loIdx = curIdx; + } + } + } while (loIdx <= hiIdx); + + return null; + } + + private ExportDirectoryTable getExportDirectoryTable() { + return + getFile().getHeader().getOptionalHeader().getDataDirectories().getExportDirectoryTable(); + } + + private Win32Debugger dbg; + private String fullPathName; + private long size; + // MemoizedObject contains a COFFFile + private MemoizedObject file; + // Base address of module in target process + private Address addr; + // Debug info database for this DLL + private CDebugInfoDataBase db; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/TestDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/TestDebugger.java new file mode 100644 index 00000000000..a5c06158354 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/TestDebugger.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; + +public class TestDebugger { + private static void usage() { + System.out.println("usage: java TestDebugger [pid]"); + System.exit(1); + } + + public static void main(String[] args) { + try { + if (args.length != 1) { + usage(); + } + + int pid = 0; + try { + pid = Integer.parseInt(args[0]); + } + catch (NumberFormatException e) { + usage(); + } + + JVMDebugger debugger = new Win32DebuggerLocal(new MachineDescriptionIntelX86(), true); + System.err.println("Process list: "); + List processes = debugger.getProcessList(); + for (Iterator iter = processes.iterator(); iter.hasNext(); ) { + ProcessInfo info = (ProcessInfo) iter.next(); + System.err.println(info.getPid() + " " + info.getName()); + } + System.err.println("Trying to attach..."); + debugger.attach(pid); + System.err.println("Attach succeeded."); + System.err.println("Trying to detach..."); + if (!debugger.detach()) { + System.err.println("ERROR: detach failed."); + System.exit(0); + } + System.err.println("Detach succeeded."); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/TestHelloWorld.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/TestHelloWorld.java new file mode 100644 index 00000000000..4843b732c83 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/TestHelloWorld.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; + +/** Tests to see whether we can find the "Hello, World" string in a + target process */ + +public class TestHelloWorld { + private static void usage() { + System.out.println("usage: java TestHelloWorld [pid]"); + System.out.println("pid must be the process ID of the HelloWorldDLL programs"); + System.exit(1); + } + + public static void main(String[] args) { + try { + if (args.length != 1) { + usage(); + } + + int pid = 0; + try { + pid = Integer.parseInt(args[0]); + } + catch (NumberFormatException e) { + usage(); + } + + JVMDebugger debugger = new Win32DebuggerLocal(new MachineDescriptionIntelX86(), true); + System.err.println("Trying to attach..."); + debugger.attach(pid); + System.err.println("Attach succeeded."); + Address addr = debugger.lookup("helloworld.dll", "helloWorldString"); + System.err.println("helloWorldString address = " + addr); + System.err.println("Trying to detach..."); + if (!debugger.detach()) { + System.err.println("ERROR: detach failed."); + System.exit(0); + } + System.err.println("Detach succeeded."); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Address.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Address.java new file mode 100644 index 00000000000..26524d25531 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Address.java @@ -0,0 +1,395 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import sun.jvm.hotspot.debugger.*; + +class Win32Address implements Address { + protected Win32Debugger debugger; + protected long addr; + + Win32Address(Win32Debugger debugger, long addr) { + this.debugger = debugger; + this.addr = addr; + } + + // + // Basic Java routines + // + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof Win32Address)) { + return false; + } + + return (addr == ((Win32Address) arg).addr); + } + + public int hashCode() { + // FIXME: suggestions on a better hash code? + return (int) addr; + } + + public String toString() { + return debugger.addressValueToString(addr); + } + + // + // C/C++-related routines + // + + public long getCIntegerAt(long offset, long numBytes, boolean isUnsigned) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readCInteger(addr + offset, numBytes, isUnsigned); + } + + public Address getAddressAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readAddress(addr + offset); + } + + // + // Java-related routines + // + + public boolean getJBooleanAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJBoolean(addr + offset); + } + + public byte getJByteAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJByte(addr + offset); + } + + public char getJCharAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJChar(addr + offset); + } + + public double getJDoubleAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJDouble(addr + offset); + } + + public float getJFloatAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJFloat(addr + offset); + } + + public int getJIntAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJInt(addr + offset); + } + + public long getJLongAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJLong(addr + offset); + } + + public short getJShortAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJShort(addr + offset); + } + + public OopHandle getOopHandleAt(long offset) + throws UnalignedAddressException, UnmappedAddressException, NotInHeapException { + return debugger.readOopHandle(addr + offset); + } + + // + // C/C++-related mutators + // + + public void setCIntegerAt(long offset, long numBytes, long value) { + debugger.writeCInteger(addr + offset, numBytes, value); + } + public void setAddressAt(long offset, Address value) { + debugger.writeAddress(addr + offset, (Win32Address) value); + } + + // + // Java-related mutators + // + + public void setJBooleanAt (long offset, boolean value) + throws UnmappedAddressException, UnalignedAddressException { + debugger.writeJBoolean(addr + offset, value); + } + public void setJByteAt (long offset, byte value) + throws UnmappedAddressException, UnalignedAddressException { + debugger.writeJByte(addr + offset, value); + } + public void setJCharAt (long offset, char value) + throws UnmappedAddressException, UnalignedAddressException { + debugger.writeJChar(addr + offset, value); + } + public void setJDoubleAt (long offset, double value) + throws UnmappedAddressException, UnalignedAddressException { + debugger.writeJDouble(addr + offset, value); + } + public void setJFloatAt (long offset, float value) + throws UnmappedAddressException, UnalignedAddressException { + debugger.writeJFloat(addr + offset, value); + } + public void setJIntAt (long offset, int value) + throws UnmappedAddressException, UnalignedAddressException { + debugger.writeJInt(addr + offset, value); + } + public void setJLongAt (long offset, long value) + throws UnmappedAddressException, UnalignedAddressException { + debugger.writeJLong(addr + offset, value); + } + public void setJShortAt (long offset, short value) + throws UnmappedAddressException, UnalignedAddressException { + debugger.writeJShort(addr + offset, value); + } + public void setOopHandleAt (long offset, OopHandle value) + throws UnmappedAddressException, UnalignedAddressException { + debugger.writeOopHandle(addr + offset, (Win32OopHandle) value); + } + + // + // Arithmetic operations -- necessary evil. + // + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new Win32Address(debugger, value); + } + + public OopHandle addOffsetToAsOopHandle(long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new Win32OopHandle(debugger, value); + } + + /** (FIXME: any signed/unsigned issues? Should this work for + OopHandles?) */ + public long minus(Address arg) { + if (arg == null) { + return addr; + } + return addr - ((Win32Address) arg).addr; + } + + // Two's complement representation. + // All negative numbers are larger than positive numbers. + // Numbers with the same sign can be compared normally. + // Test harness is below in main(). + + public boolean lessThan (Address a) { + if (a == null) { + return false; + } + Win32Address arg = (Win32Address) a; + if ((addr >= 0) && (arg.addr < 0)) { + return true; + } + if ((addr < 0) && (arg.addr >= 0)) { + return false; + } + return (addr < arg.addr); + } + + public boolean lessThanOrEqual (Address a) { + if (a == null) { + return false; + } + Win32Address arg = (Win32Address) a; + if ((addr >= 0) && (arg.addr < 0)) { + return true; + } + if ((addr < 0) && (arg.addr >= 0)) { + return false; + } + return (addr <= arg.addr); + } + + public boolean greaterThan (Address a) { + if (a == null) { + return true; + } + Win32Address arg = (Win32Address) a; + if ((addr >= 0) && (arg.addr < 0)) { + return false; + } + if ((addr < 0) && (arg.addr >= 0)) { + return true; + } + return (addr > arg.addr); + } + + public boolean greaterThanOrEqual(Address a) { + if (a == null) { + return true; + } + Win32Address arg = (Win32Address) a; + if ((addr >= 0) && (arg.addr < 0)) { + return false; + } + if ((addr < 0) && (arg.addr >= 0)) { + return true; + } + return (addr >= arg.addr); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + long value = addr & mask; + if (value == 0) { + return null; + } + return new Win32Address(debugger, value); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + long value = addr | mask; + if (value == 0) { + return null; + } + return new Win32Address(debugger, value); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + long value = addr ^ mask; + if (value == 0) { + return null; + } + return new Win32Address(debugger, value); + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + long getValue() { + return addr; + } + + + private static void check(boolean arg, String failMessage) { + if (!arg) { + System.err.println(failMessage + ": FAILED"); + System.exit(1); + } + } + + // Test harness + public static void main(String[] args) { + // p/n indicates whether the interior address is really positive + // or negative. In unsigned terms, p1 < p2 < n1 < n2. + + Win32Address p1 = new Win32Address(null, 0x7FFFFFFFFFFFFFF0L); + Win32Address p2 = (Win32Address) p1.addOffsetTo(10); + Win32Address n1 = (Win32Address) p2.addOffsetTo(10); + Win32Address n2 = (Win32Address) n1.addOffsetTo(10); + + // lessThan positive tests + check(p1.lessThan(p2), "lessThan 1"); + check(p1.lessThan(n1), "lessThan 2"); + check(p1.lessThan(n2), "lessThan 3"); + check(p2.lessThan(n1), "lessThan 4"); + check(p2.lessThan(n2), "lessThan 5"); + check(n1.lessThan(n2), "lessThan 6"); + + // lessThan negative tests + check(!p1.lessThan(p1), "lessThan 7"); + check(!p2.lessThan(p2), "lessThan 8"); + check(!n1.lessThan(n1), "lessThan 9"); + check(!n2.lessThan(n2), "lessThan 10"); + + check(!p2.lessThan(p1), "lessThan 11"); + check(!n1.lessThan(p1), "lessThan 12"); + check(!n2.lessThan(p1), "lessThan 13"); + check(!n1.lessThan(p2), "lessThan 14"); + check(!n2.lessThan(p2), "lessThan 15"); + check(!n2.lessThan(n1), "lessThan 16"); + + // lessThanOrEqual positive tests + check(p1.lessThanOrEqual(p1), "lessThanOrEqual 1"); + check(p2.lessThanOrEqual(p2), "lessThanOrEqual 2"); + check(n1.lessThanOrEqual(n1), "lessThanOrEqual 3"); + check(n2.lessThanOrEqual(n2), "lessThanOrEqual 4"); + + check(p1.lessThanOrEqual(p2), "lessThanOrEqual 5"); + check(p1.lessThanOrEqual(n1), "lessThanOrEqual 6"); + check(p1.lessThanOrEqual(n2), "lessThanOrEqual 7"); + check(p2.lessThanOrEqual(n1), "lessThanOrEqual 8"); + check(p2.lessThanOrEqual(n2), "lessThanOrEqual 9"); + check(n1.lessThanOrEqual(n2), "lessThanOrEqual 10"); + + // lessThanOrEqual negative tests + check(!p2.lessThanOrEqual(p1), "lessThanOrEqual 11"); + check(!n1.lessThanOrEqual(p1), "lessThanOrEqual 12"); + check(!n2.lessThanOrEqual(p1), "lessThanOrEqual 13"); + check(!n1.lessThanOrEqual(p2), "lessThanOrEqual 14"); + check(!n2.lessThanOrEqual(p2), "lessThanOrEqual 15"); + check(!n2.lessThanOrEqual(n1), "lessThanOrEqual 16"); + + // greaterThan positive tests + check(n2.greaterThan(p1), "greaterThan 1"); + check(n2.greaterThan(p2), "greaterThan 2"); + check(n2.greaterThan(n1), "greaterThan 3"); + check(n1.greaterThan(p1), "greaterThan 4"); + check(n1.greaterThan(p2), "greaterThan 5"); + check(p2.greaterThan(p1), "greaterThan 6"); + + // greaterThan negative tests + check(!p1.greaterThan(p1), "greaterThan 7"); + check(!p2.greaterThan(p2), "greaterThan 8"); + check(!n1.greaterThan(n1), "greaterThan 9"); + check(!n2.greaterThan(n2), "greaterThan 10"); + + check(!p1.greaterThan(n2), "greaterThan 11"); + check(!p2.greaterThan(n2), "greaterThan 12"); + check(!n1.greaterThan(n2), "greaterThan 13"); + check(!p1.greaterThan(n1), "greaterThan 14"); + check(!p2.greaterThan(n1), "greaterThan 15"); + check(!p1.greaterThan(p2), "greaterThan 16"); + + // greaterThanOrEqual positive tests + check(p1.greaterThanOrEqual(p1), "greaterThanOrEqual 1"); + check(p2.greaterThanOrEqual(p2), "greaterThanOrEqual 2"); + check(n1.greaterThanOrEqual(n1), "greaterThanOrEqual 3"); + check(n2.greaterThanOrEqual(n2), "greaterThanOrEqual 4"); + + check(n2.greaterThanOrEqual(p1), "greaterThanOrEqual 5"); + check(n2.greaterThanOrEqual(p2), "greaterThanOrEqual 6"); + check(n2.greaterThanOrEqual(n1), "greaterThanOrEqual 7"); + check(n1.greaterThanOrEqual(p1), "greaterThanOrEqual 8"); + check(n1.greaterThanOrEqual(p2), "greaterThanOrEqual 9"); + check(p2.greaterThanOrEqual(p1), "greaterThanOrEqual 10"); + + // greaterThanOrEqual negative tests + check(!p1.greaterThanOrEqual(n2), "greaterThanOrEqual 11"); + check(!p2.greaterThanOrEqual(n2), "greaterThanOrEqual 12"); + check(!n1.greaterThanOrEqual(n2), "greaterThanOrEqual 13"); + check(!p1.greaterThanOrEqual(n1), "greaterThanOrEqual 14"); + check(!p2.greaterThanOrEqual(n1), "greaterThanOrEqual 15"); + check(!p1.greaterThanOrEqual(p2), "greaterThanOrEqual 16"); + + System.err.println("Win32Address: all tests passed successfully."); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32CDebugInfoBuilder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32CDebugInfoBuilder.java new file mode 100644 index 00000000000..ee7424d0cda --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32CDebugInfoBuilder.java @@ -0,0 +1,824 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.win32.coff.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.*; +import sun.jvm.hotspot.utilities.Assert; + +class Win32CDebugInfoBuilder + implements DebugVC50SubsectionTypes, DebugVC50TypeLeafIndices, DebugVC50TypeEnums, DebugVC50SymbolTypes, DebugVC50MemberAttributes, CVAttributes, AccessControl { + private Win32Debugger dbg; + private Address base; + + private DebugVC50 vc50; + private BasicCDebugInfoDataBase db; + private DebugVC50TypeIterator iter; + + private DebugVC50SymbolIterator symIter; + + // Logical->physical segment mapping + private COFFFile file; + private DebugVC50SSSegMap segMap; + + // Canonicalization of primitive types + private Map primIndexToTypeMap; + + // Global unnamed enumeration + // (FIXME: must figure out how to handle nested type descriptions) + private BasicEnumType unnamedEnum; + + private Stack blockStack; + private int endsToSkip; + + private static final int POINTER_SIZE = 4; + + Win32CDebugInfoBuilder(Win32Debugger dbg) { + this.dbg = dbg; + } + + CDebugInfoDataBase buildDataBase(String dllName, Address base) { + this.base = base; + file = COFFFileParser.getParser().parse(dllName); + vc50 = getDebugVC50(file); + + if (vc50 == null) return null; + + segMap = getSegMap(); + + primIndexToTypeMap = new HashMap(); + blockStack = new Stack(); + endsToSkip = 0; + + db = new BasicCDebugInfoDataBase(); + db.beginConstruction(); + + // Get global types and add them to the database + DebugVC50SSGlobalTypes types = getGlobalTypes(); + for (iter = types.getTypeIterator(); !iter.done(); iter.next()) { + while (!iter.typeStringDone()) { + switch (iter.typeStringLeaf()) { + case LF_MODIFIER: { + int idx = iter.getModifierIndex(); + BasicType target = getTypeByIndex(idx); + short windowsMods = iter.getModifierAttribute(); + short mods = 0; + if ((windowsMods & MODIFIER_CONST_MASK) != 0) mods |= CONST; + if ((windowsMods & MODIFIER_VOLATILE_MASK) != 0) mods |= VOLATILE; + putType(target.getCVVariant(mods)); + break; + } + case LF_POINTER: { + int idx = iter.getPointerType(); + BasicType target = getTypeByIndex(idx); + short windowsMods = iter.getModifierAttribute(); + short mods = 0; + if ((windowsMods & POINTER_CONST_MASK) != 0) mods |= CONST; + if ((windowsMods & POINTER_VOLATILE_MASK) != 0) mods |= VOLATILE; + BasicPointerType ptrType = new BasicPointerType(POINTER_SIZE, target); + if (mods != 0) { + ptrType = (BasicPointerType) ptrType.getCVVariant(mods); + } + + putType(ptrType); + break; + } + case LF_ARRAY: { + BasicType elemType = getTypeByIndex(iter.getArrayElementType()); + putType(new BasicArrayType(iter.getArrayName(), elemType, iter.getArrayLength())); + break; + } + case LF_CLASS: + case LF_STRUCTURE: { + CompoundTypeKind kind = ((iter.typeStringLeaf() == LF_CLASS) ? CompoundTypeKind.CLASS + : CompoundTypeKind.STRUCT); + BasicCompoundType type = new BasicCompoundType(iter.getClassName(), + iter.getClassSize(), + kind); + // Skip parsing of forward references to types + // FIXME: do we have to resolve these later? + if ((iter.getClassProperty() & PROPERTY_FWDREF) == 0) { + DebugVC50TypeIterator fieldIter = iter.getClassFieldListIterator(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(fieldIter.typeStringLeaf() == LF_FIELDLIST, "Expected field list"); + } + boolean advance = false; + while (!fieldIter.typeStringDone()) { + advance = true; + switch (fieldIter.typeStringLeaf()) { + case LF_FIELDLIST: break; + case LF_BCLASS: { + int accessControl = memberAttributeToAccessControl(fieldIter.getBClassAttribute()); + Type baseType = getTypeByIndex(fieldIter.getBClassType()); + // FIXME: take offset into account + type.addBaseClass(new BasicBaseClass(accessControl, false, baseType)); + break; + } + case LF_VBCLASS: { + int accessControl = memberAttributeToAccessControl(fieldIter.getVBClassAttribute()); + Type baseType = getTypeByIndex(fieldIter.getVBClassBaseClassType()); + // FIXME: take offset and virtual base offset into account + type.addBaseClass(new BasicBaseClass(accessControl, true, baseType)); + break; + } + // I don't think we need to handle indirect virtual base + // classes since they should be handled indirectly through + // the modeling of the type hierarchy + case LF_IVBCLASS: break; + case LF_INDEX: { + fieldIter = fieldIter.getIndexIterator(); + advance = false; + break; + } + case LF_MEMBER: { + BasicField field = new BasicField(fieldIter.getMemberName(), + getTypeByIndex(fieldIter.getMemberType()), + memberAttributeToAccessControl(fieldIter.getMemberAttribute()), + false); + field.setOffset(fieldIter.getMemberOffset()); + type.addField(field); + break; + } + case LF_STMEMBER: { + BasicField field = new BasicField(fieldIter.getStaticName(), + getTypeByIndex(fieldIter.getStaticType()), + memberAttributeToAccessControl(fieldIter.getStaticAttribute()), + true); + // The field's address will be found during resolution + // of the debug info database + type.addField(field); + break; + } + // FIXME: handle methods + case LF_METHOD: break; + case LF_ONEMETHOD: break; + // FIXME: handle nested types + case LF_NESTTYPE: break; + case LF_NESTTYPEEX: break; + // NOTE: virtual functions not needed/handled yet for + // this debugging system (because we are not planning to + // handle calling methods in the target process at + // runtime) + case LF_VFUNCTAB: break; + case LF_FRIENDCLS: break; + case LF_VFUNCOFF: break; + case LF_MEMBERMODIFY: break; + case LF_PAD0: case LF_PAD1: case LF_PAD2: case LF_PAD3: + case LF_PAD4: case LF_PAD5: case LF_PAD6: case LF_PAD7: + case LF_PAD8: case LF_PAD9: case LF_PAD10: case LF_PAD11: + case LF_PAD12: case LF_PAD13: case LF_PAD14: case LF_PAD15: break; + default: System.err.println("WARNING: unexpected leaf index " + + fieldIter.typeStringLeaf() + + " in field list for type " + iter.getTypeIndex()); + } + if (advance) { + fieldIter.typeStringNext(); + } + } + } + putType(type); + break; + } + case LF_UNION: { + BasicCompoundType type = new BasicCompoundType(iter.getUnionName(), + iter.getUnionSize(), + CompoundTypeKind.UNION); + // Skip parsing of forward references to types + // FIXME: do we have to resolve these later? + if ((iter.getClassProperty() & PROPERTY_FWDREF) == 0) { + DebugVC50TypeIterator fieldIter = iter.getUnionFieldListIterator(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(fieldIter.typeStringLeaf() == LF_FIELDLIST, "Expected field list"); + } + boolean advance = false; + while (!fieldIter.typeStringDone()) { + advance = true; + switch (fieldIter.typeStringLeaf()) { + case LF_FIELDLIST: break; + case LF_BCLASS: break; + case LF_VBCLASS: break; + case LF_IVBCLASS: break; + case LF_INDEX: { + fieldIter = fieldIter.getIndexIterator(); + advance = false; + break; + } + case LF_MEMBER: { + BasicField field = new BasicField(fieldIter.getMemberName(), + getTypeByIndex(fieldIter.getMemberType()), + memberAttributeToAccessControl(fieldIter.getMemberAttribute()), + false); + field.setOffset(fieldIter.getMemberOffset()); + type.addField(field); + break; + } + case LF_STMEMBER: { + System.err.println("WARNING: I didn't think unions could contain static fields..."); + BasicField field = new BasicField(fieldIter.getStaticName(), + getTypeByIndex(fieldIter.getStaticType()), + memberAttributeToAccessControl(fieldIter.getStaticAttribute()), + true); + // The field's address will be found during resolution + // of the debug info database + type.addField(field); + break; + } + case LF_METHOD: break; + case LF_ONEMETHOD: break; + // FIXME: handle nested types + case LF_NESTTYPE: break; + case LF_NESTTYPEEX: break; + case LF_VFUNCTAB: break; + case LF_FRIENDCLS: break; + case LF_VFUNCOFF: break; + case LF_MEMBERMODIFY: break; + case LF_PAD0: case LF_PAD1: case LF_PAD2: case LF_PAD3: + case LF_PAD4: case LF_PAD5: case LF_PAD6: case LF_PAD7: + case LF_PAD8: case LF_PAD9: case LF_PAD10: case LF_PAD11: + case LF_PAD12: case LF_PAD13: case LF_PAD14: case LF_PAD15: break; + + default: System.err.println("WARNING: unexpected leaf index " + + fieldIter.typeStringLeaf() + + " in field list for union of type " + iter.getTypeIndex()); + } + if (advance) { + fieldIter.typeStringNext(); + } + } + } + putType(type); + break; + } + case LF_ENUM: { + String name = iter.getEnumName(); + BasicEnumType enumType = null; + if ((name == null) || (name.equals(""))) { + if (unnamedEnum == null) { + unnamedEnum = new BasicEnumType(null, getTypeByIndex(iter.getEnumType())); + } + enumType = unnamedEnum; + } else { + enumType = new BasicEnumType(name, getTypeByIndex(iter.getEnumType())); + } + DebugVC50TypeIterator fieldIter = iter.getEnumFieldListIterator(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(fieldIter.typeStringLeaf() == LF_FIELDLIST, "Expected field list"); + } + boolean advance = false; + while (!fieldIter.typeStringDone()) { + advance = true; + switch (fieldIter.typeStringLeaf()) { + case LF_FIELDLIST: break; + case LF_ENUMERATE: { + String enumName = fieldIter.getEnumerateName(); + long enumVal = fieldIter.getEnumerateValue(); + enumType.addEnum(enumName, enumVal); + break; + } + case LF_INDEX: { + fieldIter = fieldIter.getIndexIterator(); + advance = false; + break; + } + + case LF_PAD0: case LF_PAD1: case LF_PAD2: case LF_PAD3: + case LF_PAD4: case LF_PAD5: case LF_PAD6: case LF_PAD7: + case LF_PAD8: case LF_PAD9: case LF_PAD10: case LF_PAD11: + case LF_PAD12: case LF_PAD13: case LF_PAD14: case LF_PAD15: break; + + default: System.err.println("WARNING: unexpected leaf index " + + fieldIter.typeStringLeaf() + + " in field list for enum of type " + iter.getTypeIndex()); + } + + if (advance) { + fieldIter.typeStringNext(); + } + } + + putType(enumType); + break; + } + case LF_PROCEDURE: { + Type retType = getTypeByIndex(iter.getProcedureReturnType()); + BasicFunctionType func = new BasicFunctionType(null, POINTER_SIZE, retType); + DebugVC50TypeIterator argIter = iter.getProcedureArgumentListIterator(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(argIter.typeStringLeaf() == LF_ARGLIST, "Expected argument list"); + } + for (int i = 0; i < argIter.getArgListCount(); i++) { + func.addArgumentType(getTypeByIndex(argIter.getArgListType(i))); + } + putType(func); + break; + } + case LF_MFUNCTION: { + Type retType = getTypeByIndex(iter.getMFunctionReturnType()); + Type container = getTypeByIndex(iter.getMFunctionContainingClass()); + Type thisType = getTypeByIndex(iter.getMFunctionThis()); + long thisAdjust = iter.getMFunctionThisAdjust(); + BasicMemberFunctionType func = new BasicMemberFunctionType(null, + POINTER_SIZE, + retType, + container, + thisType, + thisAdjust); + DebugVC50TypeIterator argIter = iter.getMFunctionArgumentListIterator(); + for (int i = 0; i < argIter.getArgListCount(); i++) { + func.addArgumentType(getTypeByIndex(argIter.getArgListType(i))); + } + putType(func); + break; + } + // FIXME: handle virtual function table shape description + case LF_VTSHAPE: break; + case LF_BARRAY: System.err.println("FIXME: don't know what to do with LF_BARRAY leaves (convert to pointers?"); break; + case LF_LABEL: break; + case LF_NULL: break; // FIXME: do we need to handle this? With what? + case LF_DIMARRAY: System.err.println("FIXME: don't know what to do with LF_DIMARRAY leaves yet"); break; + case LF_VFTPATH: break; + case LF_PRECOMP: break; + case LF_ENDPRECOMP: break; + case LF_OEM: break; + case LF_TYPESERVER: break; + + // Type records referenced from other type records + + case LF_SKIP: break; + case LF_ARGLIST: skipTypeRecord(); break; + case LF_DEFARG: System.err.println("FIXME: handle default arguments (dereference the type)"); break; + case LF_FIELDLIST: skipTypeRecord(); break; + case LF_DERIVED: break; + case LF_BITFIELD: { + Type underlyingType = getTypeByIndex(iter.getBitfieldFieldType()); + BasicBitType bit = new BasicBitType(underlyingType, + (iter.getBitfieldLength() & 0xFF), + (iter.getBitfieldPosition() & 0xFF)); + putType(bit); + break; + } + case LF_METHODLIST: break; + case LF_DIMCONU: + case LF_DIMCONLU: + case LF_DIMVARU: + case LF_DIMVARLU: break; + case LF_REFSYM: break; + + case LF_PAD0: case LF_PAD1: case LF_PAD2: case LF_PAD3: + case LF_PAD4: case LF_PAD5: case LF_PAD6: case LF_PAD7: + case LF_PAD8: case LF_PAD9: case LF_PAD10: case LF_PAD11: + case LF_PAD12: case LF_PAD13: case LF_PAD14: case LF_PAD15: break; + + default: { + System.err.println("Unexpected leaf index " + + iter.typeStringLeaf() + " at offset 0x" + + Integer.toHexString(iter.typeStringOffset())); + break; + } + } + + + if (!iter.typeStringDone()) { + iter.typeStringNext(); + } + } + } + + // Add all symbol directories to debug info + // (FIXME: must figure out how to handle module-by-module + // arrangement of at least the static symbols to have proper + // lookup -- should probably also take advantage of the PROCREF + // and UDT references to understand how to build the global + // database vs. the module-by-module one) + DebugVC50SubsectionDirectory dir = vc50.getSubsectionDirectory(); + int moduleNumber = 0; // Debugging + for (int i = 0; i < dir.getNumEntries(); i++) { + DebugVC50Subsection ss = dir.getSubsection(i); + int ssType = ss.getSubsectionType(); + boolean process = false; + + if ((ssType == SST_GLOBAL_SYM) || + (ssType == SST_GLOBAL_PUB) || + (ssType == SST_STATIC_SYM)) { + DebugVC50SSSymbolBase syms = (DebugVC50SSSymbolBase) ss; + symIter = syms.getSymbolIterator(); + process = true; + } + + if (ssType == SST_ALIGN_SYM) { + DebugVC50SSAlignSym syms = (DebugVC50SSAlignSym) ss; + symIter = syms.getSymbolIterator(); + process = true; + } + + if (process) { + for (; !symIter.done(); symIter.next()) { + switch (symIter.getType()) { + case S_COMPILE: break; + case S_SSEARCH: break; // FIXME: may need this later + case S_END: { + try { + // FIXME: workaround for warnings until we figure out + // what to do with THUNK32 symbols + if (endsToSkip == 0) { + blockStack.pop(); + } else { + --endsToSkip; + } + } catch (EmptyStackException e) { + System.err.println("WARNING: mismatched block begins/ends in debug information"); + } + break; + } + case S_SKIP: break; + case S_CVRESERVE: break; + case S_OBJNAME: break; // FIXME: may need this later + case S_ENDARG: break; + case S_COBOLUDT: break; + case S_MANYREG: break; // FIXME: may need to add support for this + case S_RETURN: break; // NOTE: would need this if adding support for calling functions + case S_ENTRYTHIS: break; // FIXME: may need to add support for this + case S_REGISTER: break; // FIXME: may need to add support for this + case S_CONSTANT: break; // FIXME: will need to add support for this + case S_UDT: break; // FIXME: need to see how these are used; are + // they redundant, or are they used to describe + // global variables as opposed to types? + case S_COBOLUDT2: break; + case S_MANYREG2: break; + case S_BPREL32: { + LocalSym sym = new BasicLocalSym(symIter.getBPRelName(), + getTypeByIndex(symIter.getBPRelType()), + symIter.getBPRelOffset()); + addLocalToCurBlock(sym); + break; + } + case S_LDATA32: + case S_GDATA32: { + // FIXME: must handle these separately from global data (have + // module scoping and only add these at the module level) + boolean isModuleLocal = (symIter.getType() == S_LDATA32); + + GlobalSym sym = new BasicGlobalSym(symIter.getLGDataName(), + getTypeByIndex(symIter.getLGDataType()), + newAddress(symIter.getLGDataOffset(), symIter.getLGDataSegment()), + isModuleLocal); + // FIXME: must handle module-local symbols differently + addGlobalSym(sym); + break; + } + case S_PUB32: break; // FIXME: figure out how these differ from + // above and how they are used + case S_LPROC32: + case S_GPROC32: { + BasicFunctionSym sym = new BasicFunctionSym(newLazyBlockSym(symIter.getLGProcParentOffset()), + symIter.getLGProcLength(), + newAddress(symIter.getLGProcOffset(), symIter.getLGProcSegment()), + symIter.getLGProcName(), + getTypeByIndex(symIter.getLGProcType()), + (symIter.getType() == S_LPROC32)); + + // FIXME: have to handle local procedures differently (have + // notion of modules and only add those procedures to the + // module they are defined in) + addBlock(sym); + break; + } + case S_THUNK32: { + // FIXME: see whether we need to handle these + skipEnd(); + break; + } + case S_BLOCK32: { + BasicBlockSym sym = new BasicBlockSym(newLazyBlockSym(symIter.getBlockParentOffset()), + symIter.getBlockLength(), + newAddress(symIter.getBlockOffset(), symIter.getBlockSegment()), + symIter.getBlockName()); + addBlock(sym); + break; + } + case S_WITH32: break; + case S_LABEL32: break; + case S_CEXMODEL32: break; + case S_VFTTABLE32: break; // FIXME: may need to handle this + // (most likely for run-time type determination) + case S_REGREL32: break; // FIXME: may need to add support for this + case S_LTHREAD32: break; + case S_GTHREAD32: break; // FIXME: may need to add support for these + case S_PROCREF: break; + case S_DATAREF: break; + case S_ALIGN: break; + default: + // These two unknown symbol types show up very frequently. + // Symbol type 0 appears to always be a no-op symbol of + // length 2 (i.e., length just covers the symbol type.) + // Symbol type 4115 appears to be a copyright notice for + // the Microsoft linker. + if ((symIter.getType() != 0) && (symIter.getType() != 4115)) { + System.err.println(" NOTE: Unexpected symbol of type " + + symIter.getType() + " at offset 0x" + + Integer.toHexString(symIter.getOffset())); + } + break; + } + } + } + } + + // Add line number information for all modules + for (int i = 0; i < dir.getNumEntries(); i++) { + DebugVC50Subsection ss = dir.getSubsection(i); + if (ss.getSubsectionType() == SST_SRC_MODULE) { + DebugVC50SSSrcModule srcMod = (DebugVC50SSSrcModule) ss; + for (int sf = 0; sf < srcMod.getNumSourceFiles(); sf++) { + DebugVC50SrcModFileDesc desc = srcMod.getSourceFileDesc(sf); + // Uniquify these to save space + String name = desc.getSourceFileName().intern(); + for (int cs = 0; cs < desc.getNumCodeSegments(); cs++) { + DebugVC50SrcModLineNumberMap map = desc.getLineNumberMap(cs); + SectionHeader seg = file.getHeader().getSectionHeader(map.getSegment()); + for (int lp = 0; lp < map.getNumSourceLinePairs(); lp++) { + Address startPC = base.addOffsetTo(seg.getVirtualAddress() + map.getCodeOffset(lp)); + // Fake address for endPC -- will be filled in by BasicLineNumberMapping + Address endPC = base.addOffsetTo(seg.getSize()); + db.addLineNumberInfo(new BasicLineNumberInfo(name, map.getLineNumber(lp), startPC, endPC)); + } + } + } + } + } + + // Finish assembly of database + db.resolve(new ResolveListener() { + public void resolveFailed(Type containingType, LazyType failedResolve, String detail) { + System.err.println("WARNING: failed to resolve type of index " + + ((Integer) failedResolve.getKey()).intValue() + + " in type " + containingType.getName() + " (class " + + containingType.getClass().getName() + ") while " + detail); + } + + public void resolveFailed(Type containingType, String staticFieldName) { + System.err.println("WARNING: failed to resolve address of static field \"" + + staticFieldName + "\" in type " + containingType.getName()); + } + + public void resolveFailed(Sym containingSymbol, LazyType failedResolve, String detail) { + System.err.println("WARNING: failed to resolve type of index " + + ((Integer) failedResolve.getKey()).intValue() + + " in symbol of type " + containingSymbol.getClass().getName() + + " while " + detail); + } + + public void resolveFailed(Sym containingSymbol, LazyBlockSym failedResolve, String detail) { + System.err.println("WARNING: failed to resolve block at offset 0x" + + Integer.toHexString(((Integer) failedResolve.getKey()).intValue()) + + " in symbol of type " + containingSymbol.getClass().getName() + + " while " + detail); + } + }); + + db.endConstruction(); + + return db; + } + + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private static DebugVC50 getDebugVC50(COFFFile file) { + COFFHeader header = file.getHeader(); + OptionalHeader opt = header.getOptionalHeader(); + if (opt == null) { + // Optional header not found + return null; + } + OptionalHeaderDataDirectories dd = opt.getDataDirectories(); + if (dd == null) { + // Optional header data directories not found + return null; + } + DebugDirectory debug = dd.getDebugDirectory(); + if (debug == null) { + // Debug directory not found + return null; + } + for (int i = 0; i < debug.getNumEntries(); i++) { + DebugDirectoryEntry entry = debug.getEntry(i); + if (entry.getType() == DebugTypes.IMAGE_DEBUG_TYPE_CODEVIEW) { + return entry.getDebugVC50(); + } + } + + // CodeView information not found in debug directory + return null; + } + + private DebugVC50SSSegMap getSegMap() { + return (DebugVC50SSSegMap) findSubsection(SST_SEG_MAP); + } + + private DebugVC50SSGlobalTypes getGlobalTypes() { + return (DebugVC50SSGlobalTypes) findSubsection(SST_GLOBAL_TYPES); + } + + private DebugVC50SSGlobalSym getGlobalSymbols() { + return (DebugVC50SSGlobalSym) findSubsection(SST_GLOBAL_SYM); + } + + private DebugVC50Subsection findSubsection(short ssType) { + DebugVC50SubsectionDirectory dir = vc50.getSubsectionDirectory(); + for (int i = 0; i < dir.getNumEntries(); i++) { + DebugVC50Subsection ss = dir.getSubsection(i); + if (ss.getSubsectionType() == ssType) { + return ss; + } + } + throw new DebuggerException("Unable to find subsection of type " + ssType); + } + + private void putType(Type t) { + db.addType(new Integer(iter.getTypeIndex()), t); + } + + private Address newAddress(int offset, short segment) { + int seg = segment & 0xFFFF; + // NOTE: it isn't clear how to use the segMap to map from logical + // to physical segments. It seems it would make more sense if the + // SegDescs contained a physical segment number in addition to the + // offset within the physical segment of the logical one. + + // Get the section header corresponding to this segment + SectionHeader section = file.getHeader().getSectionHeader(seg); + + // Result is relative to image base + return base.addOffsetTo(section.getVirtualAddress() + offset); + } + + private BasicType getTypeByIndex(int intIndex) { + Integer index = new Integer(intIndex); + + // Handle primitive types here. + if (intIndex <= 0x0FFF) { + BasicType type = (BasicType) primIndexToTypeMap.get(index); + if (type != null) { + return type; + } + // Construct appropriate new primitive type + int primMode = intIndex & RESERVED_MODE_MASK; + if (primMode == RESERVED_MODE_DIRECT) { + int primType = intIndex & RESERVED_TYPE_MASK; + switch (primType) { + case RESERVED_TYPE_SIGNED_INT: + case RESERVED_TYPE_UNSIGNED_INT: { + boolean unsigned = (primType == RESERVED_TYPE_UNSIGNED_INT); + int size = 0; + String name = null; + switch (intIndex & RESERVED_SIZE_MASK) { + case RESERVED_SIZE_INT_1_BYTE: size = 1; name = "char"; break; + case RESERVED_SIZE_INT_2_BYTE: size = 2; name = "short"; break; + case RESERVED_SIZE_INT_4_BYTE: size = 4; name = "int"; break; + case RESERVED_SIZE_INT_8_BYTE: size = 8; name = "__int64"; break; + default: throw new DebuggerException("Illegal size of integer type " + intIndex); + } + type = new BasicIntType(name, size, unsigned); + break; + } + case RESERVED_TYPE_BOOLEAN: { + int size = 0; + switch (intIndex & RESERVED_SIZE_MASK) { + case RESERVED_SIZE_INT_1_BYTE: size = 1; break; + case RESERVED_SIZE_INT_2_BYTE: size = 2; break; + case RESERVED_SIZE_INT_4_BYTE: size = 4; break; + case RESERVED_SIZE_INT_8_BYTE: size = 8; break; + default: throw new DebuggerException("Illegal size of boolean type " + intIndex); + } + type = new BasicIntType("bool", size, false); + break; + } + case RESERVED_TYPE_REAL: { + switch (intIndex & RESERVED_SIZE_MASK) { + case RESERVED_SIZE_REAL_32_BIT: + type = new BasicFloatType("float", 4); + break; + case RESERVED_SIZE_REAL_64_BIT: + type = new BasicDoubleType("double", 8); + break; + default: + throw new DebuggerException("Unsupported floating-point size in type " + intIndex); + } + break; + } + case RESERVED_TYPE_REALLY_INT: { + switch (intIndex & RESERVED_SIZE_MASK) { + case RESERVED_SIZE_REALLY_INT_CHAR: type = new BasicIntType("char", 1, false); break; + case RESERVED_SIZE_REALLY_INT_WCHAR: type = new BasicIntType("wchar", 2, false); break; + case RESERVED_SIZE_REALLY_INT_2_BYTE: type = new BasicIntType("short", 2, false); break; + case RESERVED_SIZE_REALLY_INT_2_BYTE_U: type = new BasicIntType("short", 2, true); break; + case RESERVED_SIZE_REALLY_INT_4_BYTE: type = new BasicIntType("int", 4, false); break; + case RESERVED_SIZE_REALLY_INT_4_BYTE_U: type = new BasicIntType("int", 4, true); break; + case RESERVED_SIZE_REALLY_INT_8_BYTE: type = new BasicIntType("__int64", 8, false); break; + case RESERVED_SIZE_REALLY_INT_8_BYTE_U: type = new BasicIntType("__int64", 8, true); break; + default: throw new DebuggerException("Illegal REALLY_INT size in type " + intIndex); + } + break; + } + case RESERVED_TYPE_SPECIAL: { + switch (intIndex & RESERVED_SIZE_MASK) { + case RESERVED_SIZE_SPECIAL_NO_TYPE: + case RESERVED_SIZE_SPECIAL_VOID: type = new BasicVoidType(); break; + default: throw new DebuggerException("Don't know how to handle reserved special type " + intIndex); + } + break; + } + + default: + throw new DebuggerException("Don't know how to handle reserved type " + intIndex); + } + } else { + // Fold all pointer types together since we only support + // flat-mode addressing anyway + Type targetType = getTypeByIndex(intIndex & (~RESERVED_MODE_MASK)); + + type = new BasicPointerType(POINTER_SIZE, targetType); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(type != null, "Got null Type for primitive type " + intIndex); + } + primIndexToTypeMap.put(index, type); + return type; + } + + // Not primitive type. Construct lazy reference to target type. + // (Is it worth canonicalizing these as well to save space?) + return new LazyType(index); + } + + private void addBlock(BlockSym block) { + db.addBlock(new Integer(symIter.getOffset()), block); + blockStack.push(block); + } + + private void skipEnd() { + ++endsToSkip; + } + + private BlockSym newLazyBlockSym(int offset) { + if (offset == 0) { + return null; + } + + return new LazyBlockSym(new Integer(offset)); + } + + private int memberAttributeToAccessControl(short memberAttribute) { + int acc = memberAttribute & MEMATTR_ACCESS_MASK; + switch (acc) { + case MEMATTR_ACCESS_NO_PROTECTION: return NO_PROTECTION; + case MEMATTR_ACCESS_PRIVATE: return PRIVATE; + case MEMATTR_ACCESS_PROTECTED: return PROTECTED; + case MEMATTR_ACCESS_PUBLIC: return PUBLIC; + default: throw new RuntimeException("Should not reach here"); + } + } + + private void addLocalToCurBlock(LocalSym local) { + ((BasicBlockSym) blockStack.peek()).addLocal(local); + } + + private void addGlobalSym(GlobalSym sym) { + db.addGlobalSym(sym); + } + + private void skipTypeRecord() { + while (!iter.typeStringDone()) { + iter.typeStringNext(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32CDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32CDebugger.java new file mode 100644 index 00000000000..8f0d893e398 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32CDebugger.java @@ -0,0 +1,123 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.x86.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.utilities.AddressOps; + +class Win32CDebugger implements CDebugger, ProcessControl { + // FIXME: think about how to make this work in a remote debugging + // scenario; who should keep open DLLs? Need local copies of these + // DLLs on the debugging machine? + private Win32Debugger dbg; + + Win32CDebugger(Win32Debugger dbg) { + this.dbg = dbg; + } + + public List getThreadList() throws DebuggerException { + return dbg.getThreadList(); + } + + public List/**/ getLoadObjectList() throws DebuggerException{ + return dbg.getLoadObjectList(); + } + + public LoadObject loadObjectContainingPC(Address pc) throws DebuggerException { + // FIXME: could keep sorted list of these to be able to do binary + // searches, for better scalability + if (pc == null) { + return null; + } + List objs = getLoadObjectList(); + for (Iterator iter = objs.iterator(); iter.hasNext(); ) { + LoadObject obj = (LoadObject) iter.next(); + if (AddressOps.lte(obj.getBase(), pc) && (pc.minus(obj.getBase()) < obj.getSize())) { + return obj; + } + } + return null; + } + + public CFrame topFrameForThread(ThreadProxy thread) throws DebuggerException { + X86ThreadContext context = (X86ThreadContext) thread.getContext(); + Address ebp = context.getRegisterAsAddress(X86ThreadContext.EBP); + if (ebp == null) return null; + Address pc = context.getRegisterAsAddress(X86ThreadContext.EIP); + if (pc == null) return null; + return new X86CFrame(this, ebp, pc); + } + + public String getNameOfFile(String fileName) { + return new File(fileName).getName(); + } + + public ProcessControl getProcessControl() throws DebuggerException { + return this; + } + + // C++ name demangling + public boolean canDemangle() { + return false; + } + + public String demangle(String sym) { + throw new UnsupportedOperationException(); + } + + // + // Support for ProcessControl interface + // + + public void suspend() throws DebuggerException { + dbg.suspend(); + } + public void resume() throws DebuggerException { + dbg.resume(); + } + public boolean isSuspended() throws DebuggerException { + return dbg.isSuspended(); + } + public void setBreakpoint(Address addr) throws DebuggerException { + dbg.setBreakpoint(addr); + } + public void clearBreakpoint(Address addr) throws DebuggerException { + dbg.clearBreakpoint(addr); + } + public boolean isBreakpointSet(Address addr) throws DebuggerException { + return dbg.isBreakpointSet(addr); + } + public DebugEvent debugEventPoll() throws DebuggerException { + return dbg.debugEventPoll(); + } + public void debugEventContinue() throws DebuggerException { + dbg.debugEventContinue(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Debugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Debugger.java new file mode 100644 index 00000000000..283c0ae2e0d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Debugger.java @@ -0,0 +1,133 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import java.util.List; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +/** An extension of the JVMDebugger interface with a few additions to + support 32-bit vs. 64-bit debugging as well as features required + by the architecture-specific subpackages. */ + +public interface Win32Debugger extends JVMDebugger { + public String addressValueToString(long address) throws DebuggerException; + public boolean readJBoolean(long address) throws DebuggerException; + public byte readJByte(long address) throws DebuggerException; + public char readJChar(long address) throws DebuggerException; + public double readJDouble(long address) throws DebuggerException; + public float readJFloat(long address) throws DebuggerException; + public int readJInt(long address) throws DebuggerException; + public long readJLong(long address) throws DebuggerException; + public short readJShort(long address) throws DebuggerException; + public long readCInteger(long address, long numBytes, boolean isUnsigned) + throws DebuggerException; + public Win32Address readAddress(long address) throws DebuggerException; + public Win32OopHandle readOopHandle(long address) throws DebuggerException; + public void writeJBoolean(long address, boolean value) throws DebuggerException; + public void writeJByte(long address, byte value) throws DebuggerException; + public void writeJChar(long address, char value) throws DebuggerException; + public void writeJDouble(long address, double value) throws DebuggerException; + public void writeJFloat(long address, float value) throws DebuggerException; + public void writeJInt(long address, int value) throws DebuggerException; + public void writeJLong(long address, long value) throws DebuggerException; + public void writeJShort(long address, short value) throws DebuggerException; + public void writeCInteger(long address, long numBytes, long value) throws DebuggerException; + public void writeAddress(long address, Win32Address value) throws DebuggerException; + public void writeOopHandle(long address, Win32OopHandle value) throws DebuggerException; + + // On Windows the int is actually the value of a HANDLE which + // currently must be read from the target process; that is, the + // target process must maintain its own thread list, each element of + // which holds a HANDLE to its underlying OS thread. FIXME: should + // add access to the OS-level thread list, but there are too many + // limitations imposed by Windows to usefully do so; see + // src/os/win32/agent/README-commands.txt, command "duphandle". + // + // The returned array of register contents is guaranteed to be in + // the same order as in the DbxDebugger for Solaris/x86; that is, + // the indices match those in debugger/x86/X86ThreadContext.java. + public long[] getThreadIntegerRegisterSet(int threadHandleValue, + boolean mustDuplicateHandle) throws DebuggerException; + // Implmentation of setContext + public void setThreadIntegerRegisterSet(int threadHandleValue, + boolean mustDuplicateHandle, + long[] contents) throws DebuggerException; + + public Address newAddress(long value) throws DebuggerException; + + // Routine supporting the ThreadProxy implementation, in particular + // the ability to get a thread ID from a thread handle via + // examination of the Thread Information Block. Fetch the LDT entry + // for a given selector. + public Win32LDTEntry getThreadSelectorEntry(int threadHandleValue, + boolean mustDuplicateHandle, + int selector) throws DebuggerException; + + // Support for the CDebugger interface. Retrieves the thread list of + // the target process as a List of ThreadProxy objects. + public List/**/ getThreadList() throws DebuggerException; + + // Support for the CDebugger interface. Retrieves a List of the + // loadobjects in the target process. + public List/**/ getLoadObjectList() throws DebuggerException; + + // Support for the ProcessControl interface + public void writeBytesToProcess(long startAddress, long numBytes, byte[] data) throws UnmappedAddressException, DebuggerException; + public void suspend() throws DebuggerException; + public void resume() throws DebuggerException; + public boolean isSuspended() throws DebuggerException; + public void setBreakpoint(Address addr) throws DebuggerException; + public void clearBreakpoint(Address addr) throws DebuggerException; + public boolean isBreakpointSet(Address addr) throws DebuggerException; + // FIXME: do not want to expose complicated data structures (like + // the DebugEvent) in this interface due to serialization issues + public DebugEvent debugEventPoll() throws DebuggerException; + public void debugEventContinue() throws DebuggerException; + + // NOTE: this interface implicitly contains the following methods: + // From the Debugger interface via JVMDebugger + // public void attach(int processID) throws DebuggerException; + // public void attach(String executableName, String coreFileName) throws DebuggerException; + // public boolean detach(); + // public Address parseAddress(String addressString) throws NumberFormatException; + // public long getAddressValue(Address addr) throws DebuggerException; + // public String getOS(); + // public String getCPU(); + // From the SymbolLookup interface via Debugger and JVMDebugger + // public Address lookup(String objectName, String symbol); + // public OopHandle lookupOop(String objectName, String symbol); + // From the JVMDebugger interface + // public void configureJavaPrimitiveTypeSizes(long jbooleanSize, + // long jbyteSize, + // long jcharSize, + // long jdoubleSize, + // long jfloatSize, + // long jintSize, + // long jlongSize, + // long jshortSize); + // From the ThreadAccess interface via Debugger and JVMDebugger + // public ThreadProxy getThreadForIdentifierAddress(Address addr); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32DebuggerLocal.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32DebuggerLocal.java new file mode 100644 index 00000000000..0f4b11695aa --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32DebuggerLocal.java @@ -0,0 +1,1073 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import java.io.*; +import java.net.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.win32.coff.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.BasicDebugEvent; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.utilities.memo.*; + +/**

An implementation of the JVMDebugger interface which talks to + the Free Windows Debug Server (FwDbgSrv) over a socket to + implement attach/detach and read from process memory. All DLL and + symbol table management is done in Java.

+ +

NOTE that since we have the notion of fetching "Java + primitive types" from the remote process (which might have + different sizes than we expect) we have a bootstrapping + problem. We need to know the sizes of these types before we can + fetch them. The current implementation solves this problem by + requiring that it be configured with these type sizes before they + can be fetched. The readJ(Type) routines here will throw a + RuntimeException if they are called before the debugger is + configured with the Java primitive type sizes.

*/ + +public class Win32DebuggerLocal extends DebuggerBase implements Win32Debugger { + private Socket debuggerSocket; + private boolean attached; + // FIXME: update when core files supported + private long pid; + // Communication with debug server + private PrintWriter out; + private DataOutputStream rawOut; + private InputLexer in; + private static final int PORT = 27000; + private PageCache cache; + private static final long SHORT_TIMEOUT = 2000; + private static final long LONG_TIMEOUT = 20000; + + // Symbol lookup support + // This is a map of library names to DLLs + private Map nameToDllMap; + + // C/C++ debugging support + private List/**/ loadObjects; + private CDebugger cdbg; + + // ProcessControl support + private boolean suspended; + // Maps Long objects (addresses) to Byte objects (original instructions) + // (Longs used instead of Addresses to properly represent breakpoints at 0x0 if needed) + private Map breakpoints; + // Current debug event, if any + private DebugEvent curDebugEvent; + + //-------------------------------------------------------------------------------- + // Implementation of Debugger interface + // + + /**

machDesc may not be null.

+ +

useCache should be set to true if debugging is being done + locally, and to false if the debugger is being created for the + purpose of supporting remote debugging.

*/ + public Win32DebuggerLocal(MachineDescription machDesc, + boolean useCache) throws DebuggerException { + this.machDesc = machDesc; + utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); + if (useCache) { + // Cache portion of the remote process's address space. + // Fetching data over the socket connection to dbx is slow. + // Might be faster if we were using a binary protocol to talk to + // dbx, but would have to test. For now, this cache works best + // if it covers the entire heap of the remote process. FIXME: at + // least should make this tunable from the outside, i.e., via + // the UI. This is a cache of 4096 4K pages, or 16 MB. The page + // size must be adjusted to be the hardware's page size. + // (FIXME: should pick this up from the debugger.) + initCache(4096, parseCacheNumPagesProperty(4096)); + } + // FIXME: add instantiation of thread factory + + try { + connectToDebugServer(); + } catch (IOException e) { + throw new DebuggerException(e); + } + } + + /** From the Debugger interface via JVMDebugger */ + public boolean hasProcessList() throws DebuggerException { + return true; + } + + /** From the Debugger interface via JVMDebugger */ + public List getProcessList() throws DebuggerException { + List processes = new ArrayList(); + + try { + printlnToOutput("proclist"); + int num = in.parseInt(); + for (int i = 0; i < num; i++) { + int pid = in.parseInt(); + String name = parseString(); + // NOTE: Win32 hack + if (name.equals("")) { + name = "System Idle Process"; + } + processes.add(new ProcessInfo(name, pid)); + } + return processes; + } + catch (IOException e) { + throw new DebuggerException(e); + } + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized void attach(int processID) throws DebuggerException { + if (attached) { + // FIXME: update when core files supported + throw new DebuggerException("Already attached to process " + pid); + } + + try { + printlnToOutput("attach " + processID); + if (!in.parseBoolean()) { + throw new DebuggerException("Error attaching to process, or no such process"); + } + + attached = true; + pid = processID; + suspended = true; + breakpoints = new HashMap(); + curDebugEvent = null; + nameToDllMap = null; + loadObjects = null; + } + catch (IOException e) { + throw new DebuggerException(e); + } + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized void attach(String executableName, String coreFileName) throws DebuggerException { + throw new DebuggerException("Core files not yet supported on Win32"); + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized boolean detach() { + if (!attached) { + return false; + } + + attached = false; + suspended = false; + breakpoints = null; + + // Close all open DLLs + if (nameToDllMap != null) { + for (Iterator iter = nameToDllMap.values().iterator(); iter.hasNext(); ) { + DLL dll = (DLL) iter.next(); + dll.close(); + } + nameToDllMap = null; + loadObjects = null; + } + + cdbg = null; + clearCache(); + + try { + printlnToOutput("detach"); + return in.parseBoolean(); + } + catch (IOException e) { + throw new DebuggerException(e); + } + } + + /** From the Debugger interface via JVMDebugger */ + public Address parseAddress(String addressString) throws NumberFormatException { + return newAddress(utils.scanAddress(addressString)); + } + + /** From the Debugger interface via JVMDebugger */ + public String getOS() { + return PlatformInfo.getOS(); + } + + /** From the Debugger interface via JVMDebugger */ + public String getCPU() { + return PlatformInfo.getCPU(); + } + + public boolean hasConsole() throws DebuggerException { + return false; + } + + public String consoleExecuteCommand(String cmd) throws DebuggerException { + throw new DebuggerException("No debugger console available on Win32"); + } + + public String getConsolePrompt() throws DebuggerException { + return null; + } + + public CDebugger getCDebugger() throws DebuggerException { + if (cdbg == null) { + cdbg = new Win32CDebugger(this); + } + return cdbg; + } + + /** From the SymbolLookup interface via Debugger and JVMDebugger */ + public synchronized Address lookup(String objectName, String symbol) { + if (!attached) { + return null; + } + return newAddress(lookupInProcess(objectName, symbol)); + } + + /** From the SymbolLookup interface via Debugger and JVMDebugger */ + public synchronized OopHandle lookupOop(String objectName, String symbol) { + Address addr = lookup(objectName, symbol); + if (addr == null) { + return null; + } + return addr.addOffsetToAsOopHandle(0); + } + + /** From the Debugger interface */ + public MachineDescription getMachineDescription() { + return machDesc; + } + + //-------------------------------------------------------------------------------- + // Implementation of ThreadAccess interface + // + + /** From the ThreadAccess interface via Debugger and JVMDebugger */ + public ThreadProxy getThreadForIdentifierAddress(Address addr) { + return new Win32Thread(this, addr); + } + + public ThreadProxy getThreadForThreadId(long handle) { + return new Win32Thread(this, handle); + } + + //---------------------------------------------------------------------- + // Overridden from DebuggerBase because we need to relax alignment + // constraints on x86 + + public long readJLong(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + // FIXME: allow this to be configurable. Undesirable to add a + // dependency on the runtime package here, though, since this + // package should be strictly underneath it. + // utils.checkAlignment(address, jlongSize); + utils.checkAlignment(address, jintSize); + byte[] data = readBytes(address, jlongSize); + return utils.dataToJLong(data, jlongSize); + } + + //-------------------------------------------------------------------------------- + // Internal routines (for implementation of Win32Address). + // These must not be called until the MachineDescription has been set up. + // + + /** From the Win32Debugger interface */ + public String addressValueToString(long address) { + return utils.addressValueToString(address); + } + + /** From the Win32Debugger interface */ + public Win32Address readAddress(long address) + throws UnmappedAddressException, UnalignedAddressException { + return (Win32Address) newAddress(readAddressValue(address)); + } + + /** From the Win32Debugger interface */ + public Win32OopHandle readOopHandle(long address) + throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { + long value = readAddressValue(address); + return (value == 0 ? null : new Win32OopHandle(this, value)); + } + + /** From the Win32Debugger interface */ + public void writeAddress(long address, Win32Address value) { + writeAddressValue(address, getAddressValue(value)); + } + + /** From the Win32Debugger interface */ + public void writeOopHandle(long address, Win32OopHandle value) { + writeAddressValue(address, getAddressValue(value)); + } + + //-------------------------------------------------------------------------------- + // Thread context access + // + + public synchronized long[] getThreadIntegerRegisterSet(int threadHandleValue, + boolean mustDuplicateHandle) + throws DebuggerException { + if (!suspended) { + throw new DebuggerException("Process not suspended"); + } + + try { + int handle = threadHandleValue; + if (mustDuplicateHandle) { + printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); + if (!in.parseBoolean()) { + throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); + } + handle = (int) in.parseAddress(); // Must close to avoid leaks + } + printlnToOutput("getcontext 0x" + Integer.toHexString(handle)); + if (!in.parseBoolean()) { + if (mustDuplicateHandle) { + printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); + } + String failMessage = "GetThreadContext failed for thread handle 0x" + + Integer.toHexString(handle); + if (mustDuplicateHandle) { + failMessage = failMessage + ", duplicated from thread handle " + + Integer.toHexString(threadHandleValue); + } + throw new DebuggerException(failMessage); + } + // Otherwise, parse all registers. See + // src/os/win32/agent/README-commands.txt for the format. + // Note the array we have to return has to match that specified by + // X86ThreadContext.java. + int numRegs = 22; + long[] winRegs = new long[numRegs]; + for (int i = 0; i < numRegs; i++) { + winRegs[i] = in.parseAddress(); + } + if (mustDuplicateHandle) { + // Clean up after ourselves + printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); + } + // Now create the real return value + long[] retval = new long[X86ThreadContext.NPRGREG]; + retval[X86ThreadContext.EAX] = winRegs[0]; + retval[X86ThreadContext.EBX] = winRegs[1]; + retval[X86ThreadContext.ECX] = winRegs[2]; + retval[X86ThreadContext.EDX] = winRegs[3]; + retval[X86ThreadContext.ESI] = winRegs[4]; + retval[X86ThreadContext.EDI] = winRegs[5]; + retval[X86ThreadContext.EBP] = winRegs[6]; + retval[X86ThreadContext.ESP] = winRegs[7]; + retval[X86ThreadContext.EIP] = winRegs[8]; + retval[X86ThreadContext.DS] = winRegs[9]; + retval[X86ThreadContext.ES] = winRegs[10]; + retval[X86ThreadContext.FS] = winRegs[11]; + retval[X86ThreadContext.GS] = winRegs[12]; + retval[X86ThreadContext.CS] = winRegs[13]; + retval[X86ThreadContext.SS] = winRegs[14]; + retval[X86ThreadContext.EFL] = winRegs[15]; + retval[X86ThreadContext.DR0] = winRegs[16]; + retval[X86ThreadContext.DR1] = winRegs[17]; + retval[X86ThreadContext.DR2] = winRegs[18]; + retval[X86ThreadContext.DR3] = winRegs[19]; + retval[X86ThreadContext.DR6] = winRegs[20]; + retval[X86ThreadContext.DR7] = winRegs[21]; + return retval; + } catch (IOException e) { + throw new DebuggerException(e); + } + } + + public synchronized void setThreadIntegerRegisterSet(int threadHandleValue, + boolean mustDuplicateHandle, + long[] context) + throws DebuggerException { + if (!suspended) { + throw new DebuggerException("Process not suspended"); + } + + try { + int handle = threadHandleValue; + if (mustDuplicateHandle) { + printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); + if (!in.parseBoolean()) { + throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); + } + handle = (int) in.parseAddress(); // Must close to avoid leaks + } + // Change order of registers to match that of debug server + long[] winRegs = new long[context.length]; + winRegs[0] = context[X86ThreadContext.EAX]; + winRegs[1] = context[X86ThreadContext.EBX]; + winRegs[2] = context[X86ThreadContext.ECX]; + winRegs[3] = context[X86ThreadContext.EDX]; + winRegs[4] = context[X86ThreadContext.ESI]; + winRegs[5] = context[X86ThreadContext.EDI]; + winRegs[6] = context[X86ThreadContext.EBP]; + winRegs[7] = context[X86ThreadContext.ESP]; + winRegs[8] = context[X86ThreadContext.EIP]; + winRegs[9] = context[X86ThreadContext.DS]; + winRegs[10] = context[X86ThreadContext.ES]; + winRegs[11] = context[X86ThreadContext.FS]; + winRegs[12] = context[X86ThreadContext.GS]; + winRegs[13] = context[X86ThreadContext.CS]; + winRegs[14] = context[X86ThreadContext.SS]; + winRegs[15] = context[X86ThreadContext.EFL]; + winRegs[16] = context[X86ThreadContext.DR0]; + winRegs[17] = context[X86ThreadContext.DR1]; + winRegs[18] = context[X86ThreadContext.DR2]; + winRegs[19] = context[X86ThreadContext.DR3]; + winRegs[20] = context[X86ThreadContext.DR6]; + winRegs[21] = context[X86ThreadContext.DR7]; + StringBuffer cmd = new StringBuffer(); + cmd.append("setcontext 0x"); + cmd.append(Integer.toHexString(threadHandleValue)); + for (int i = 0; i < context.length; i++) { + cmd.append(" 0x"); + cmd.append(Long.toHexString(winRegs[i])); + } + printlnToOutput(cmd.toString()); + boolean res = in.parseBoolean(); + if (mustDuplicateHandle) { + printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); + } + if (!res) { + String failMessage = "SetThreadContext failed for thread handle 0x" + + Integer.toHexString(handle); + if (mustDuplicateHandle) { + failMessage = failMessage + ", duplicated from thread handle " + + Integer.toHexString(threadHandleValue); + } + throw new DebuggerException(failMessage); + } + } catch (IOException e) { + throw new DebuggerException(e); + } + } + + /** Fetches the Win32 LDT_ENTRY for the given thread and selector. + This data structure allows the conversion of a segment-relative + address to a linear virtual address. For example, it allows the + expression of operations like "mov eax, fs:[18h]", which fetches + the thread information block, allowing access to the thread + ID. */ + public synchronized Win32LDTEntry getThreadSelectorEntry(int threadHandleValue, + boolean mustDuplicateHandle, + int selector) + throws DebuggerException { + try { + int handle = threadHandleValue; + if (mustDuplicateHandle) { + printlnToOutput("duphandle 0x" + Integer.toHexString(threadHandleValue)); + if (!in.parseBoolean()) { + throw new DebuggerException("Error duplicating thread handle 0x" + threadHandleValue); + } + handle = (int) in.parseAddress(); // Must close to avoid leaks + } + printlnToOutput("selectorentry 0x" + Integer.toHexString(handle) + " " + selector); + if (!in.parseBoolean()) { + if (mustDuplicateHandle) { + printlnToOutput("closehandle 0x" + Integer.toHexString(handle)); + } + throw new DebuggerException("GetThreadContext failed for thread handle 0x" + handle + + ", duplicated from thread handle " + threadHandleValue); + } + // Parse result. See + // src/os/win32/agent/README-commands.txt for the format. + short limitLow = (short) in.parseAddress(); + short baseLow = (short) in.parseAddress(); + byte baseMid = (byte) in.parseAddress(); + byte flags1 = (byte) in.parseAddress(); + byte flags2 = (byte) in.parseAddress(); + byte baseHi = (byte) in.parseAddress(); + return new Win32LDTEntry(limitLow, baseLow, baseMid, flags1, flags2, baseHi); + } catch (IOException e) { + throw new DebuggerException(e); + } + } + + public synchronized List getThreadList() throws DebuggerException { + if (!suspended) { + throw new DebuggerException("Process not suspended"); + } + + try { + printlnToOutput("threadlist"); + List ret = new ArrayList(); + int numThreads = in.parseInt(); + for (int i = 0; i < numThreads; i++) { + int handle = (int) in.parseAddress(); + ret.add(new Win32Thread(this, handle)); + } + return ret; + } catch (IOException e) { + throw new DebuggerException(e); + } + } + + public synchronized List getLoadObjectList() throws DebuggerException { + if (!suspended) { + throw new DebuggerException("Process not suspended"); + } + + try { + if (loadObjects == null) { + loadObjects = new ArrayList(); + nameToDllMap = new HashMap(); + // Get list of library names and base addresses + printlnToOutput("libinfo"); + int numInfo = in.parseInt(); + + for (int i = 0; i < numInfo; i++) { + // NOTE: because Win32 is case insensitive, we standardize on + // lowercase file names. + String fullPathName = parseString().toLowerCase(); + Address base = newAddress(in.parseAddress()); + + File file = new File(fullPathName); + long size = file.length(); + DLL dll = new DLL(this, fullPathName, size, base); + String name = file.getName(); + nameToDllMap.put(name, dll); + loadObjects.add(dll); + } + } + } catch (IOException e) { + throw new DebuggerException(e); + } + + return loadObjects; + } + + //---------------------------------------------------------------------- + // Process control access + // + + public synchronized void writeBytesToProcess(long startAddress, long numBytes, byte[] data) + throws UnmappedAddressException, DebuggerException { + try { + printToOutput("poke 0x" + Long.toHexString(startAddress) + + " |"); + writeIntToOutput((int) numBytes); + writeToOutput(data, 0, (int) numBytes); + printlnToOutput(""); + if (!in.parseBoolean()) { + throw new UnmappedAddressException(startAddress); + } + } catch (IOException e) { + throw new DebuggerException(e); + } + } + + public synchronized void suspend() throws DebuggerException { + try { + if (suspended) { + throw new DebuggerException("Process already suspended"); + } + printlnToOutput("suspend"); + suspended = true; + enableCache(); + reresolveLoadObjects(); + } catch (IOException e) { + throw new DebuggerException(e); + } + } + + public synchronized void resume() throws DebuggerException { + try { + if (!suspended) { + throw new DebuggerException("Process not suspended"); + } + disableCache(); + printlnToOutput("resume"); + suspended = false; + } catch (IOException e) { + throw new DebuggerException(e); + } + } + + public synchronized boolean isSuspended() throws DebuggerException { + return suspended; + } + + public synchronized void setBreakpoint(Address addr) throws DebuggerException { + if (!suspended) { + throw new DebuggerException("Process not suspended"); + } + + long addrVal = getAddressValue(addr); + Long where = new Long(addrVal); + if (breakpoints.get(where) != null) { + throw new DebuggerException("Breakpoint already set at " + addr); + } + Byte what = new Byte(readBytes(addrVal, 1)[0]); + // Now put 0xCC (int 3) at the target address, fail if can not + writeBytesToProcess(addrVal, 1, new byte[] { (byte) 0xCC }); + // OK, the breakpoint is set. + breakpoints.put(where, what); + } + + public synchronized void clearBreakpoint(Address addr) throws DebuggerException { + if (!suspended) { + throw new DebuggerException("Process not suspended"); + } + + long addrVal = getAddressValue(addr); + Long where = new Long(addrVal); + Byte what = (Byte) breakpoints.get(where); + if (what == null) { + throw new DebuggerException("Breakpoint not set at " + addr); + } + // Put original data back at address + writeBytesToProcess(addrVal, 1, new byte[] { what.byteValue() }); + // OK, breakpoint is cleared + breakpoints.remove(where); + } + + public synchronized boolean isBreakpointSet(Address addr) throws DebuggerException { + return (breakpoints.get(new Long(getAddressValue(addr))) != null); + } + + // Following constants taken from winnt.h + private static final int EXCEPTION_DEBUG_EVENT = 1; + private static final int LOAD_DLL_DEBUG_EVENT = 6; + private static final int UNLOAD_DLL_DEBUG_EVENT = 7; + private static final int EXCEPTION_ACCESS_VIOLATION = 0xC0000005; + private static final int EXCEPTION_BREAKPOINT = 0x80000003; + private static final int EXCEPTION_SINGLE_STEP = 0x80000004; + + public synchronized DebugEvent debugEventPoll() throws DebuggerException { + if (curDebugEvent != null) { + return curDebugEvent; + } + + try { + printlnToOutput("pollevent"); + if (!in.parseBoolean()) { + return null; + } + // Otherwise, got a debug event. Need to figure out what kind it is. + int handle = (int) in.parseAddress(); + ThreadProxy thread = new Win32Thread(this, handle); + int code = in.parseInt(); + DebugEvent ev = null; + switch (code) { + case LOAD_DLL_DEBUG_EVENT: { + Address addr = newAddress(in.parseAddress()); + ev = BasicDebugEvent.newLoadObjectLoadEvent(thread, addr); + break; + } + + case UNLOAD_DLL_DEBUG_EVENT: { + Address addr = newAddress(in.parseAddress()); + ev = BasicDebugEvent.newLoadObjectUnloadEvent(thread, addr); + break; + } + + case EXCEPTION_DEBUG_EVENT: { + int exceptionCode = in.parseInt(); + Address pc = newAddress(in.parseAddress()); + switch (exceptionCode) { + case EXCEPTION_ACCESS_VIOLATION: + boolean wasWrite = in.parseBoolean(); + Address addr = newAddress(in.parseAddress()); + ev = BasicDebugEvent.newAccessViolationEvent(thread, pc, wasWrite, addr); + break; + + case EXCEPTION_BREAKPOINT: + ev = BasicDebugEvent.newBreakpointEvent(thread, pc); + break; + + case EXCEPTION_SINGLE_STEP: + ev = BasicDebugEvent.newSingleStepEvent(thread, pc); + break; + + default: + ev = BasicDebugEvent.newUnknownEvent(thread, + "Exception 0x" + Integer.toHexString(exceptionCode) + + " at PC " + pc); + break; + } + break; + } + + default: + ev = BasicDebugEvent.newUnknownEvent(thread, + "Debug event " + code + " occurred"); + break; + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(ev != null, "Must have created event"); + } + curDebugEvent = ev; + } catch (IOException e) { + throw new DebuggerException(e); + } + + return curDebugEvent; + } + + public synchronized void debugEventContinue() throws DebuggerException { + if (curDebugEvent == null) { + throw new DebuggerException("No debug event pending"); + } + + try { + /////////////////////////////////////////////////////////////////// + // // + // FIXME: this **must** be modified to handle breakpoint events + // properly. Must temporarily remove the breakpoint and enable + // single-stepping mode (hiding those single-step events from + // the user unless they have been requested; currently there is + // no way to request single-step events; and it isn't clear how + // to enable them or how the hardware and/or OS typically + // supports them, i.e., are they on a per-process or per-thread + // level?) until the process steps past the breakpoint, then put + // the breakpoint back. + // // + /////////////////////////////////////////////////////////////////// + + DebugEvent.Type t = curDebugEvent.getType(); + boolean shouldPassOn = true; + if (t == DebugEvent.Type.BREAKPOINT) { + // FIXME: correct algorithm appears to be as follows: + // + // 1. Check to see whether we know about this breakpoint. If + // not, it's requested by the user's program and we should + // ignore it (not pass it on to the program). + // + // 2. Replace the original opcode. + // + // 3. Set single-stepping mode in the debug registers. + // + // 4. Back up the PC. + // + // 5. In debugEventPoll(), watch for a single-step event on + // this thread. When we get it, put the breakpoint back. Only + // deliver that single-step event if the user has requested + // single-step events (FIXME: must figure out whether they are + // per-thread or per-process, and also expose a way to turn + // them on.) + + // To make breakpoints work for now, we will just back up the + // PC, which we have to do in order to not disrupt the program + // execution in case the user decides to disable the breakpoint. + + if (breakpoints.get(new Long(getAddressValue(curDebugEvent.getPC()))) != null) { + System.err.println("Backing up PC due to breakpoint"); + X86ThreadContext ctx = (X86ThreadContext) curDebugEvent.getThread().getContext(); + ctx.setRegister(X86ThreadContext.EIP, ctx.getRegister(X86ThreadContext.EIP) - 1); + curDebugEvent.getThread().setContext(ctx); + } else { + System.err.println("Skipping back up of PC since I didn't know about this breakpoint"); + System.err.println("Known breakpoints:"); + for (Iterator iter = breakpoints.keySet().iterator(); iter.hasNext(); ) { + System.err.println(" 0x" + Long.toHexString(((Long) iter.next()).longValue())); + } + } + shouldPassOn = false; + } else if (t == DebugEvent.Type.SINGLE_STEP) { + shouldPassOn = false; + } + // Other kinds of debug events are either ignored if passed on + // or probably should be passed on so the program exits + // FIXME: generate process exiting events (should be easy) + + int val = (shouldPassOn ? 1 : 0); + printlnToOutput("continueevent " + val); + if (!in.parseBoolean()) { + throw new DebuggerException("Unknown error while attempting to continue past debug event"); + } + curDebugEvent = null; + } catch (IOException e) { + throw new DebuggerException(e); + } + } + + //-------------------------------------------------------------------------------- + // Address access + // + + /** From the Debugger interface */ + public long getAddressValue(Address addr) { + if (addr == null) return 0; + return ((Win32Address) addr).getValue(); + } + + /** From the Win32Debugger interface */ + public Address newAddress(long value) { + if (value == 0) return null; + return new Win32Address(this, value); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private String parseString() throws IOException { + int charSize = in.parseInt(); + int numChars = in.parseInt(); + in.skipByte(); + String str; + if (charSize == 1) { + str = in.readByteString(numChars); + } else { + str = in.readCharString(numChars); + } + return str; + } + + /** Looks up an address in the remote process's address space. + Returns 0 if symbol not found or upon error. Package private to + allow Win32DebuggerRemoteIntfImpl access. NOTE that this returns + a long instead of an Address because we do not want to serialize + Addresses. */ + synchronized long lookupInProcess(String objectName, String symbol) { + // NOTE: this assumes that process is suspended (which is probably + // necessary assumption given that DLLs can be loaded/unloaded as + // process runs). Should update documentation. + if (nameToDllMap == null) { + getLoadObjectList(); + } + DLL dll = (DLL) nameToDllMap.get(objectName); + // The DLL can be null because we use this to search through known + // DLLs in HotSpotTypeDataBase (for example) + if (dll != null) { + Win32Address addr = (Win32Address) dll.lookupSymbol(symbol); + if (addr != null) { + return addr.getValue(); + } + } + return 0; + } + + /** This reads bytes from the remote process. */ + public synchronized ReadResult readBytesFromProcess(long address, long numBytes) + throws UnmappedAddressException, DebuggerException { + try { + String cmd = "peek " + utils.addressValueToString(address) + " " + numBytes; + printlnToOutput(cmd); + while (in.readByte() != 'B') { + } + byte res = in.readByte(); + if (res == 0) { + System.err.println("Failing command: " + cmd); + throw new DebuggerException("Read of remote process address space failed"); + } + // NOTE: must read ALL of the data regardless of whether we need + // to throw an UnmappedAddressException. Otherwise will corrupt + // the input stream each time we have a failure. Not good. Do + // not want to risk "flushing" the input stream in case a huge + // read has a hangup in the middle and we leave data on the + // stream. + byte[] buf = new byte[(int) numBytes]; + boolean bailOut = false; + long failureAddress = 0; + while (numBytes > 0) { + long len = in.readUnsignedInt(); + boolean isMapped = ((in.readByte() == 0) ? false : true); + if (!isMapped) { + if (!bailOut) { + bailOut = true; + failureAddress = address; + } + } else { + // This won't work if we have unmapped regions, but if we do + // then we're going to throw an exception anyway + + // NOTE: there is a factor of 20 speed difference between + // these two ways of doing this read. + in.readBytes(buf, 0, (int) len); + } + + // Do NOT do this: + // for (int i = 0; i < (int) len; i++) { + // buf[i] = in.readByte(); + // } + + numBytes -= len; + address += len; + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(numBytes == 0, "Bug in debug server's implementation of peek"); + } + if (bailOut) { + return new ReadResult(failureAddress); + } + return new ReadResult(buf); + } + catch (IOException e) { + throw new DebuggerException(e); + } + } + + /** Convenience routines */ + private void printlnToOutput(String s) throws IOException { + out.println(s); + if (out.checkError()) { + throw new IOException("Error occurred while writing to debug server"); + } + } + + private void printToOutput(String s) throws IOException { + out.print(s); + if (out.checkError()) { + throw new IOException("Error occurred while writing to debug server"); + } + } + + private void writeIntToOutput(int val) throws IOException { + rawOut.writeInt(val); + rawOut.flush(); + } + + private void writeToOutput(byte[] buf, int off, int len) throws IOException { + rawOut.write(buf, off, len); + rawOut.flush(); + } + + /** Connects to the debug server, setting up out and in streams. */ + private void connectToDebugServer() throws IOException { + // Try for a short period of time to connect to debug server; time out + // with failure if didn't succeed + debuggerSocket = null; + long endTime = System.currentTimeMillis() + SHORT_TIMEOUT; + + while ((debuggerSocket == null) && (System.currentTimeMillis() < endTime)) { + try { + // FIXME: this does not work if we are on a DHCP machine which + // did not get an IP address this session. It appears to use + // an old cached address and the connection does not actually + // succeed. Must file a bug. + // debuggerSocket = new Socket(InetAddress.getLocalHost(), PORT); + debuggerSocket = new Socket(InetAddress.getByName("127.0.0.1"), PORT); + debuggerSocket.setTcpNoDelay(true); + } + catch (IOException e) { + // Swallow IO exceptions while attempting connection + debuggerSocket = null; + try { + // Don't swamp the CPU + Thread.sleep(750); + } + catch (InterruptedException ex) { + } + } + } + + if (debuggerSocket == null) { + // Failed to connect because of timeout + throw new DebuggerException("Timed out while attempting to connect to debug server (please start SwDbgSrv.exe)"); + } + + out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(debuggerSocket.getOutputStream(), "US-ASCII")), true); + rawOut = new DataOutputStream(new BufferedOutputStream(debuggerSocket.getOutputStream())); + in = new InputLexer(new BufferedInputStream(debuggerSocket.getInputStream())); + } + + private DLL findDLLByName(String fullPathName) { + for (Iterator iter = loadObjects.iterator(); iter.hasNext(); ) { + DLL dll = (DLL) iter.next(); + if (dll.getName().equals(fullPathName)) { + return dll; + } + } + return null; + } + + private void reresolveLoadObjects() throws DebuggerException { + try { + // It is too expensive to throw away the loadobject list every + // time the process is suspended, largely because of debug + // information re-parsing. When we suspend the target process we + // instead fetch the list of loaded libraries in the target and + // see whether any loadobject needs to be thrown away (because it + // was unloaded) or invalidated (because it was unloaded and + // reloaded at a different target address). Note that we don't + // properly handle the case of a loaded DLL being unloaded, + // recompiled, and reloaded. We could handle this by keeping a + // time stamp. + + if (loadObjects == null) { + return; + } + + // Need to create new list since have to figure out which ones + // were unloaded + List newLoadObjects = new ArrayList(); + + // Get list of library names and base addresses + printlnToOutput("libinfo"); + int numInfo = in.parseInt(); + + for (int i = 0; i < numInfo; i++) { + // NOTE: because Win32 is case insensitive, we standardize on + // lowercase file names. + String fullPathName = parseString().toLowerCase(); + Address base = newAddress(in.parseAddress()); + + // Look for full path name in DLL list + DLL dll = findDLLByName(fullPathName); + boolean mustLoad = true; + if (dll != null) { + loadObjects.remove(dll); + + // See whether base addresses match; otherwise, need to reload + if (AddressOps.equal(base, dll.getBase())) { + mustLoad = false; + } + } + + if (mustLoad) { + // Create new DLL + File file = new File(fullPathName); + long size = file.length(); + String name = file.getName(); + dll = new DLL(this, fullPathName, size, base); + nameToDllMap.put(name, dll); + } + newLoadObjects.add(dll); + } + + // All remaining entries in loadObjects have to be removed from + // the nameToDllMap + for (Iterator dllIter = loadObjects.iterator(); dllIter.hasNext(); ) { + DLL dll = (DLL) dllIter.next(); + for (Iterator iter = nameToDllMap.keySet().iterator(); iter.hasNext(); ) { + String name = (String) iter.next(); + if (nameToDllMap.get(name) == dll) { + nameToDllMap.remove(name); + break; + } + } + } + + loadObjects = newLoadObjects; + } catch (IOException e) { + loadObjects = null; + nameToDllMap = null; + throw new DebuggerException(e); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32LDTEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32LDTEntry.java new file mode 100644 index 00000000000..f8f90c5e1cf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32LDTEntry.java @@ -0,0 +1,100 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import java.io.Serializable; + +/** Describes an LDT entry. (Some of the descriptions are taken + directly from Microsoft's documentation and are copyrighted by + Microsoft.) */ + +class Win32LDTEntry implements Serializable { + private short limitLow; + private short baseLow; + private byte baseMid; + private byte flags1; + private byte flags2; + private byte baseHi; + + private Win32LDTEntry() {} + + public Win32LDTEntry(short limitLow, + short baseLow, + byte baseMid, + byte flags1, + byte flags2, + byte baseHi) { + this.limitLow = limitLow; + this.baseLow = baseLow; + this.baseMid = baseMid; + this.flags1 = flags1; + this.flags2 = flags2; + this.baseHi = baseHi; + } + + /** Returns base address of segment */ + public long getBase() { return ( (baseLow & 0xFFFF) | + ((baseMid & 0xFF) << 16) | + ((baseHi & 0xFF) << 24)) & 0xFFFFFFFF; } + + public short getLimitLow() { return limitLow; } + public short getBaseLow() { return baseLow; } + public byte getBaseMid() { return baseMid; } + public byte getBaseHi() { return baseHi; } + + // FIXME: must verify mask and shift are correct + /** Describes type of segment. See TYPE_ portion of {@link + sun.jvm.hotspot.debugger.win32.Win32LDTEntryConstants}. */ + public int getType() { return (flags1 & 0x1F); } + + // FIXME: verify mask and shift are correct + /** Privilege level of descriptor: 0 = most privileged, 3 = least privileged */ + public int getPrivilegeLevel() { return ((flags1 & 0x60) >> 5); } + + // FIXME: verify mask is correct + /** Is segment present in physical memory? */ + public boolean isSegmentPhysical() { return ((flags1 & 0x70) != 0); } + + // FIXME: verify mask and shift are correct + /** High bits (16-19) of the address of the last byte of the segment */ + public int getLimitHi() { return (flags2 & 0x0F); } + + // FIXME: verify mask is correct + /**

Size of segment. If the segment is a data segment, this + member contains 1 if the segment is larger than 64 kilobytes (K) + or 0 if the segment is smaller than or equal to 64K.

+ +

If the segment is a code segment, this member contains 1 if + the segment is a code segment and runs with the default (native + mode) instruction set. This member contains 0 if the code + segment is an 80286 code segment and runs with 16-bit offsets + and the 80286-compatible instruction set.

*/ + public boolean isDefaultBig() { return ((flags2 & 0x40) != 0); } + + // FIXME: verify mask is correct + /** Returns true if segment is page granular, false if byte + granular. */ + public boolean isPageGranular() { return ((flags2 & 0x80) != 0); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32LDTEntryConstants.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32LDTEntryConstants.java new file mode 100644 index 00000000000..d09bf0e6299 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32LDTEntryConstants.java @@ -0,0 +1,39 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +/** Enumerates flags in Win32LDTEntry */ + +interface Win32LDTEntryConstants { + // Types of segments + public static final int TYPE_READ_ONLY_DATA = 0; + public static final int TYPE_READ_WRITE_DATA = 1; + public static final int TYPE_UNUSED = 2; + public static final int TYPE_READ_WRITE_EXPAND_DOWN_DATA = 3; + public static final int TYPE_EXECUTE_ONLY_CODE = 4; + public static final int TYPE_EXECUTABLE_READABLE_CODE = 5; + public static final int TYPE_EXECUTE_ONLY_CONFORMING_CODE = 6; + public static final int TYPE_EXECUTABLE_READABLE_CONFORMING_CODE = 7; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32OopHandle.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32OopHandle.java new file mode 100644 index 00000000000..f5eaab2d5e7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32OopHandle.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import sun.jvm.hotspot.debugger.*; + +class Win32OopHandle extends Win32Address implements OopHandle { + Win32OopHandle(Win32Debugger debugger, long addr) { + super(debugger, addr); + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof Win32OopHandle)) { + return false; + } + + return (addr == ((Win32Address) arg).addr); + } + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + throw new UnsupportedOperationException("addOffsetTo not applicable to OopHandles (interior object pointers not allowed)"); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("andWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("orWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("xorWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Thread.java new file mode 100644 index 00000000000..fc978cb5656 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Thread.java @@ -0,0 +1,129 @@ +/* + * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; + +class Win32Thread implements ThreadProxy { + private Win32Debugger debugger; + private int handle; + private boolean mustDuplicate; + private boolean gotID; + private int id; + + /** The address argument must be the address of the HANDLE of the + desired thread in the target process. */ + Win32Thread(Win32Debugger debugger, Address addr) { + this.debugger = debugger; + // FIXME: size of data fetched here should be configurable. + // However, making it so would produce a dependency on the "types" + // package from the debugger package, which is not desired. + this.handle = (int) addr.getCIntegerAt(0, 4, true); + // Thread handles in the target process must be duplicated before + // fetching their contexts + mustDuplicate = true; + gotID = false; + } + + /** The integer argument must be the value of a HANDLE received from + the "threadlist" operation. */ + Win32Thread(Win32Debugger debugger, long handle) { + this.debugger = debugger; + this.handle = (int) handle; + mustDuplicate = false; + gotID = false; + } + + public ThreadContext getContext() throws IllegalThreadStateException { + if (!debugger.isSuspended()) { + throw new IllegalThreadStateException("Target process must be suspended"); + } + long[] data = debugger.getThreadIntegerRegisterSet(handle, mustDuplicate); + Win32ThreadContext context = new Win32ThreadContext(debugger); + for (int i = 0; i < data.length; i++) { + context.setRegister(i, data[i]); + } + return context; + } + + public boolean canSetContext() throws DebuggerException { + return true; + } + + public void setContext(ThreadContext thrCtx) + throws IllegalThreadStateException, DebuggerException { + if (!debugger.isSuspended()) { + throw new IllegalThreadStateException("Target process must be suspended"); + } + X86ThreadContext context = (X86ThreadContext) thrCtx; + long[] data = new long[X86ThreadContext.NPRGREG]; + for (int i = 0; i < data.length; i++) { + data[i] = context.getRegister(i); + } + debugger.setThreadIntegerRegisterSet(handle, mustDuplicate, data); + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof Win32Thread)) { + return false; + } + + return (((Win32Thread) obj).getThreadID() == getThreadID()); + } + + public int hashCode() { + return getThreadID(); + } + + public String toString() { + return Integer.toString(getThreadID()); + } + + /** Retrieves the thread ID of this thread by examining the Thread + Information Block. */ + private int getThreadID() { + if (!gotID) { + try { + // Get thread context + X86ThreadContext context = (X86ThreadContext) getContext(); + // Get LDT entry for FS register + Win32LDTEntry ldt = + debugger.getThreadSelectorEntry(handle, + mustDuplicate, + (int) context.getRegister(X86ThreadContext.FS)); + // Get base address of segment = Thread Environment Block (TEB) + Address teb = debugger.newAddress(ldt.getBase()); + // Thread ID is at offset 0x24 + id = (int) teb.getCIntegerAt(0x24, 4, true); + gotID = true; + } catch (AddressException e) { + throw new DebuggerException(e); + } + } + + return id; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32ThreadContext.java new file mode 100644 index 00000000000..dbf5c4c5b3d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32ThreadContext.java @@ -0,0 +1,45 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; + +class Win32ThreadContext extends X86ThreadContext { + private Win32Debugger debugger; + + public Win32ThreadContext(Win32Debugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxBfEfRecord.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxBfEfRecord.java new file mode 100644 index 00000000000..38531a8be6f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxBfEfRecord.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes an Auxiliary .bf/.ef Record, which follows a .bf or .ef + symbol. (Some of the descriptions are taken directly from + Microsoft's documentation and are copyrighted by Microsoft.) */ + +public interface AuxBfEfRecord extends AuxSymbolRecord { + /** Actual ordinal line number (1, 2, 3, etc.) within source file, + corresponding to the .bf or .ef record. */ + public short getLineNumber(); + + /** Symbol-table index of the next .bf symbol record. If the + function is the last in the symbol table, this field is set to + zero. Not used for .ef records. */ + public int getPointerToNextFunction(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxFileRecord.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxFileRecord.java new file mode 100644 index 00000000000..36503a609dd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxFileRecord.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes an Auxiliary File record, which follows a symbol with + storage class FILE. The symbol name itself should be .file. + (Some of the descriptions are taken directly from Microsoft's + documentation and are copyrighted by Microsoft.) */ + +public interface AuxFileRecord extends AuxSymbolRecord { + public String getName(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxFunctionDefinitionRecord.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxFunctionDefinitionRecord.java new file mode 100644 index 00000000000..3f3aa010113 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxFunctionDefinitionRecord.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes an Auxiliary Function Definition record, which follows a + function definition symbol record. (Some of the descriptions are + taken directly from Microsoft's documentation and are copyrighted + by Microsoft.) */ + +public interface AuxFunctionDefinitionRecord extends AuxSymbolRecord { + /** Symbol-table index of the corresponding .bf (begin function) + symbol record. */ + public int getTagIndex(); + + /** Size of the executable code for the function itself. If the + function is in its own section, the Size of Raw Data in the + section header will be greater or equal to this field, depending + on alignment considerations. */ + public int getTotalSize(); + + /** Index of the first COFF line-number entry for the function in + the global array of line numbers (see {@link + sun.jvm.hotspot.debugger.win32.coff.SectionHeader#getCOFFLineNumber}), + or -1 if none exists. */ + public int getPointerToLineNumber(); + + /** Symbol-table index of the record for the next function. If the + function is the last in the symbol table, this field is set to + zero. */ + public int getPointerToNextFunction(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxSectionDefinitionsRecord.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxSectionDefinitionsRecord.java new file mode 100644 index 00000000000..9f11dbd76aa --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxSectionDefinitionsRecord.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes an Auxiliary Section Definitions record, which follows a + symbol that defines a section. Such a symbol has a name that is + the name of a section (such as .text or drectve and + has storage class STATIC. The auxiliary record provides + information on the section referred to. Thus it duplicates some of + the information in the section header. (Some of the descriptions + are taken directly from Microsoft's documentation and are + copyrighted by Microsoft.) */ + +public interface AuxSectionDefinitionsRecord extends AuxSymbolRecord { + /** Size of section data; same as Size of Raw Data in the section + header. */ + public int getLength(); + + /** Number of relocation entries for the section. */ + public short getNumberOfRelocations(); + + /** Number of line-number entries for the section. */ + public short getNumberOfLineNumbers(); + + /** Checksum for communal data. Applicable if the + IMAGE_SCN_LNK_COMDAT flag is set in the section header. */ + public int getCheckSum(); + + /** One-based index into the Section Table for the associated + section; used when the COMDAT Selection setting is 5. */ + public short getNumber(); + + /** COMDAT selection number. Applicable if the section is a COMDAT + section. See {@link + sun.jvm.hotspot.debugger.win32.coff.COMDATSelectionTypes}. */ + public byte getSelection(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxSymbolRecord.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxSymbolRecord.java new file mode 100644 index 00000000000..b9b2e9dda80 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxSymbolRecord.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes an Auxiliary Symbol Record. Such records may follow a + Symbol Table record. (Some of the descriptions are taken directly + from Microsoft's documentation and are copyrighted by Microsoft.) */ + +public interface AuxSymbolRecord { + public static final int FUNCTION_DEFINITION = 0; + public static final int BF_EF_RECORD = 1; + public static final int WEAK_EXTERNAL = 2; + public static final int FILE = 3; + public static final int SECTION_DEFINITION = 4; + + /** Returns {@link #FUNCTION_DEFINITION}, {@link #BF_EF_RECORD}, + {@link #WEAK_EXTERNAL}, {@link #FILE}, or {@link + #SECTION_DEFINITION}, indicating the concrete subtype of this + interface. */ + public int getType(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxWeakExternalRecord.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxWeakExternalRecord.java new file mode 100644 index 00000000000..233125e3b49 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/AuxWeakExternalRecord.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes an Auxiliary Weak External record, which follows a + weak-external symbol record. (Some of the descriptions are taken + directly from Microsoft's documentation and are copyrighted by + Microsoft.) */ + +public interface AuxWeakExternalRecord extends AuxSymbolRecord { + public static final int IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY = 1; + public static final int IMAGE_WEAK_EXTERN_SEARCH_LIBRARY = 2; + public static final int IMAGE_WEAK_EXTERN_SEARCH_ALIAS = 3; + + /** Symbol-table index of sym2, the symbol to be linked if sym1 is + not found. */ + public int getTagIndex(); + + /**

A value of {@link #IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY} + indicates that no library search for sym1 should be + performed.

+ +

A value of {@link #IMAGE_WEAK_EXTERN_SEARCH_LIBRARY} + indicates that a library search for sym1 should be + performed.

+ +

A value of {@link #IMAGE_WEAK_EXTERN_SEARCH_ALIAS} indicates + that sym1 is an alias for sym2.

*/ + public int getCharacteristics(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFException.java new file mode 100644 index 00000000000..ee5e8dce5a9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFException.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Generic exception class for all exceptions which occur in this + package. Since there is no mechanism built into this library for + recovering from errors, the best clients can do is display the + error string. */ + +public class COFFException extends RuntimeException { + public COFFException() { + super(); + } + + public COFFException(Throwable cause) { + super(cause); + } + + public COFFException(String message) { + super(message); + } + + public COFFException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFFile.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFFile.java new file mode 100644 index 00000000000..2599840898b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFFile.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Top-level interface modeling the information stored in either a + Portable Executable or object file. */ + +public interface COFFFile { + public COFFHeader getHeader(); + + /** Indicates whether the file is an image (.EXE or .DLL) or not + (.OBJ). */ + public boolean isImage(); + + /** Closes this COFF file. All subordinate objects are invalid after + it is closed. */ + public void close(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFFileParser.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFFileParser.java new file mode 100644 index 00000000000..a44715cdbed --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFFileParser.java @@ -0,0 +1,3933 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; + +import sun.jvm.hotspot.utilities.memo.*; +import sun.jvm.hotspot.utilities.Assert; +import sun.jvm.hotspot.debugger.DataSource; +import sun.jvm.hotspot.debugger.MappedByteBufferDataSource; + +/** Top-level factory which parses COFF files, including object files, + Portable Executables and DLLs. Returns {@link + sun.jvm.hotspot.debugger.win32.coff.COFFFile} objects. This class is a + singleton. */ + +public class COFFFileParser { + private static COFFFileParser soleInstance; + + // Constants from the file format documentation + private static final int COFF_HEADER_SIZE = 20; + private static final int SECTION_HEADER_SIZE = 40; + private static final int SYMBOL_SIZE = 18; + private static final int RELOCATION_SIZE = 10; + private static final int LINE_NUMBER_SIZE = 6; + + private static final String US_ASCII = "US-ASCII"; + + private COFFFileParser() {} + + /** This class is a singleton; returns the sole instance. */ + public static COFFFileParser getParser() { + if (soleInstance == null) { + soleInstance = new COFFFileParser(); + } + return soleInstance; + } + + public COFFFile parse(String filename) throws COFFException { + try { + File file = new File(filename); + FileInputStream stream = new FileInputStream(file); + MappedByteBuffer buf = stream.getChannel().map(FileChannel.MapMode.READ_ONLY, + 0, + file.length()); + + // This is pretty confusing. The file format is little-endian + // and so is the CPU. In order for the multi-byte accessors to + // work properly we must NOT change the endianness of the + // MappedByteBuffer. Need to think about this some more and file + // a bug if there is one. (FIXME) + // buf.order(ByteOrder.nativeOrder()); + return parse(new MappedByteBufferDataSource(buf)); + } catch (FileNotFoundException e) { + throw new COFFException(e); + } catch (IOException e) { + throw new COFFException(e); + } + } + + public COFFFile parse(DataSource source) throws COFFException { + return new COFFFileImpl(source); + } + + class COFFFileImpl implements COFFFile { + private DataSource file; + private long filePos; + private boolean isImage; + private long imageHeaderOffset; + private MemoizedObject header = new MemoizedObject() { + public Object computeValue() { + return new COFFHeaderImpl(); + } + }; + + COFFFileImpl(DataSource file) throws COFFException { + this.file = file; + initialize(); + } + + public boolean isImage() { + return isImage; + } + + public COFFHeader getHeader() { + return (COFFHeaderImpl) header.getValue(); + } + + class COFFHeaderImpl implements COFFHeader { + private short machine; + private short numberOfSections; + private int timeDateStamp; + private int pointerToSymbolTable; + private int numberOfSymbols; + private short sizeOfOptionalHeader; + private short characteristics; + private MemoizedObject[] sectionHeaders; + private MemoizedObject[] symbols; + + private MemoizedObject stringTable = new MemoizedObject() { + public Object computeValue() { + int ptr = getPointerToSymbolTable(); + if (ptr == 0) { + return new StringTable(0); + } else { + return new StringTable(ptr + SYMBOL_SIZE * getNumberOfSymbols()); + } + } + }; + + COFFHeaderImpl() { + seek(imageHeaderOffset); + machine = readShort(); + numberOfSections = readShort(); + timeDateStamp = readInt(); + pointerToSymbolTable = readInt(); + numberOfSymbols = readInt(); + sizeOfOptionalHeader = readShort(); + characteristics = readShort(); + + // Set up section headers + sectionHeaders = new MemoizedObject[numberOfSections]; + for (int i = 0; i < numberOfSections; i++) { + final int secHdrOffset = (int) + (imageHeaderOffset + COFF_HEADER_SIZE + sizeOfOptionalHeader + i * SECTION_HEADER_SIZE); + sectionHeaders[i] = new MemoizedObject() { + public Object computeValue() { + return new SectionHeaderImpl(secHdrOffset); + } + }; + } + + // Set up symbols + symbols = new MemoizedObject[numberOfSymbols]; + for (int i = 0; i < numberOfSymbols; i++) { + final int symbolOffset = pointerToSymbolTable + i * SYMBOL_SIZE; + symbols[i] = new MemoizedObject() { + public Object computeValue() { + return new COFFSymbolImpl(symbolOffset); + } + }; + } + } + + public short getMachineType() { return machine; } + public short getNumberOfSections() { return numberOfSections; } + public int getTimeDateStamp() { return timeDateStamp; } + public int getPointerToSymbolTable() { return pointerToSymbolTable; } + public int getNumberOfSymbols() { return numberOfSymbols; } + public short getSizeOfOptionalHeader() { return sizeOfOptionalHeader; } + public OptionalHeader getOptionalHeader() throws COFFException { + if (getSizeOfOptionalHeader() == 0) { + return null; + } + return new OptionalHeaderImpl((int) (imageHeaderOffset + COFF_HEADER_SIZE)); + } + public short getCharacteristics() { return characteristics; } + public boolean hasCharacteristic(short characteristic) { + return ((characteristics & characteristic) != 0); + } + public SectionHeader getSectionHeader(int index) { + // NOTE zero-basing of index + return (SectionHeader) sectionHeaders[index - 1].getValue(); + } + public COFFSymbol getCOFFSymbol(int index) { + return (COFFSymbol) symbols[index].getValue(); + } + public int getNumberOfStrings() { + return getStringTable().getNum(); + } + public String getString(int i) { + return getStringTable().get(i); + } + + StringTable getStringTable() { return (StringTable) stringTable.getValue(); } + + // NOTE: can destroy current seek() position! + int rvaToFileOffset(int rva) { + if (rva == 0) return 0; + // Search for section containing RVA + for (int i = 1; i <= getNumberOfSections(); i++) { + SectionHeader sec = getSectionHeader(i); + int va = sec.getVirtualAddress(); + int sz = sec.getSize(); + if ((va <= rva) && (rva < (va + sz))) { + return sec.getPointerToRawData() + (rva - va); + } + } + throw new COFFException("Unable to find RVA 0x" + + Integer.toHexString(rva) + + " in any section"); + } + + class OptionalHeaderImpl implements OptionalHeader { + private short magic; + private MemoizedObject standardFields; + private MemoizedObject windowsSpecificFields; + private MemoizedObject dataDirectories; + + private static final int STANDARD_FIELDS_OFFSET = 2; + private static final int PE32_WINDOWS_SPECIFIC_FIELDS_OFFSET = 28; + private static final int PE32_DATA_DIRECTORIES_OFFSET = 96; + private static final int PE32_PLUS_WINDOWS_SPECIFIC_FIELDS_OFFSET = 24; + private static final int PE32_PLUS_DATA_DIRECTORIES_OFFSET = 112; + + OptionalHeaderImpl(final int offset) { + seek(offset); + magic = readShort(); + + final boolean isPE32Plus = (magic == MAGIC_PE32_PLUS); + final int standardFieldsOffset = offset + STANDARD_FIELDS_OFFSET; + final int windowsSpecificFieldsOffset = offset + + (isPE32Plus + ? PE32_PLUS_WINDOWS_SPECIFIC_FIELDS_OFFSET + : PE32_WINDOWS_SPECIFIC_FIELDS_OFFSET); + final int dataDirectoriesOffset = offset + + (isPE32Plus + ? PE32_PLUS_DATA_DIRECTORIES_OFFSET + : PE32_DATA_DIRECTORIES_OFFSET); + + standardFields = new MemoizedObject() { + public Object computeValue() { + return new OptionalHeaderStandardFieldsImpl(standardFieldsOffset, + isPE32Plus); + } + }; + windowsSpecificFields = new MemoizedObject() { + public Object computeValue() { + return new OptionalHeaderWindowsSpecificFieldsImpl(windowsSpecificFieldsOffset, + isPE32Plus); + } + }; + dataDirectories = new MemoizedObject() { + public Object computeValue() { + return new OptionalHeaderDataDirectoriesImpl(dataDirectoriesOffset, + getWindowsSpecificFields().getNumberOfRvaAndSizes()); + } + }; + } + + public short getMagicNumber() { + return magic; + } + + public OptionalHeaderStandardFields getStandardFields() { + return (OptionalHeaderStandardFields) standardFields.getValue(); + } + + public OptionalHeaderWindowsSpecificFields getWindowsSpecificFields() { + return (OptionalHeaderWindowsSpecificFields) windowsSpecificFields.getValue(); + } + public OptionalHeaderDataDirectories getDataDirectories() { + return (OptionalHeaderDataDirectories) dataDirectories.getValue(); + } + } + + class OptionalHeaderStandardFieldsImpl implements OptionalHeaderStandardFields { + private boolean isPE32Plus; + private byte majorLinkerVersion; + private byte minorLinkerVersion; + private int sizeOfCode; + private int sizeOfInitializedData; + private int sizeOfUninitializedData; + private int addressOfEntryPoint; + private int baseOfCode; + private int baseOfData; + + OptionalHeaderStandardFieldsImpl(int offset, + boolean isPE32Plus) { + this.isPE32Plus = isPE32Plus; + seek(offset); + majorLinkerVersion = readByte(); + minorLinkerVersion = readByte(); + sizeOfCode = readInt(); + sizeOfInitializedData = readInt(); + sizeOfUninitializedData = readInt(); + addressOfEntryPoint = readInt(); + baseOfCode = readInt(); + if (isPE32Plus) { + baseOfData = readInt(); + } + } + + public byte getMajorLinkerVersion() { return majorLinkerVersion; } + public byte getMinorLinkerVersion() { return minorLinkerVersion; } + public int getSizeOfCode() { return sizeOfCode; } + public int getSizeOfInitializedData() { return sizeOfInitializedData; } + public int getSizeOfUninitializedData() { return sizeOfUninitializedData; } + public int getAddressOfEntryPoint() { return addressOfEntryPoint; } + public int getBaseOfCode() { return baseOfCode; } + public int getBaseOfData() throws COFFException { + if (isPE32Plus) { + throw new COFFException("Not present in PE32+ files"); + } + return baseOfData; + } + } + + class OptionalHeaderWindowsSpecificFieldsImpl implements OptionalHeaderWindowsSpecificFields { + private long imageBase; + private int sectionAlignment; + private int fileAlignment; + private short majorOperatingSystemVersion; + private short minorOperatingSystemVersion; + private short majorImageVersion; + private short minorImageVersion; + private short majorSubsystemVersion; + private short minorSubsystemVersion; + private int sizeOfImage; + private int sizeOfHeaders; + private int checkSum; + private short subsystem; + private short dllCharacteristics; + private long sizeOfStackReserve; + private long sizeOfStackCommit; + private long sizeOfHeapReserve; + private long sizeOfHeapCommit; + private int loaderFlags; + private int numberOfRvaAndSizes; + + OptionalHeaderWindowsSpecificFieldsImpl(int offset, boolean isPE32Plus) { + seek(offset); + + if (!isPE32Plus) { + imageBase = maskInt(readInt()); + } else { + imageBase = readLong(); + } + sectionAlignment = readInt(); + fileAlignment = readInt(); + majorOperatingSystemVersion = readShort(); + minorOperatingSystemVersion = readShort(); + majorImageVersion = readShort(); + minorImageVersion = readShort(); + majorSubsystemVersion = readShort(); + minorSubsystemVersion = readShort(); + readInt(); // Reserved + sizeOfImage = readInt(); + sizeOfHeaders = readInt(); + checkSum = readInt(); + subsystem = readShort(); + dllCharacteristics = readShort(); + if (!isPE32Plus) { + sizeOfStackReserve = maskInt(readInt()); + sizeOfStackCommit = maskInt(readInt()); + sizeOfHeapReserve = maskInt(readInt()); + sizeOfHeapCommit = maskInt(readInt()); + } else { + sizeOfStackReserve = readLong(); + sizeOfStackCommit = readLong(); + sizeOfHeapReserve = readLong(); + sizeOfHeapCommit = readLong(); + } + loaderFlags = readInt(); + numberOfRvaAndSizes = readInt(); + } + + public long getImageBase() { return imageBase; } + public int getSectionAlignment() { return sectionAlignment; } + public int getFileAlignment() { return fileAlignment; } + public short getMajorOperatingSystemVersion() { return majorOperatingSystemVersion; } + public short getMinorOperatingSystemVersion() { return minorOperatingSystemVersion; } + public short getMajorImageVersion() { return majorImageVersion; } + public short getMinorImageVersion() { return minorImageVersion; } + public short getMajorSubsystemVersion() { return majorSubsystemVersion; } + public short getMinorSubsystemVersion() { return minorSubsystemVersion; } + public int getSizeOfImage() { return sizeOfImage; } + public int getSizeOfHeaders() { return sizeOfHeaders; } + public int getCheckSum() { return checkSum; } + public short getSubsystem() { return subsystem; } + public short getDLLCharacteristics() { return dllCharacteristics; } + public long getSizeOfStackReserve() { return sizeOfStackReserve; } + public long getSizeOfStackCommit() { return sizeOfStackCommit; } + public long getSizeOfHeapReserve() { return sizeOfHeapReserve; } + public long getSizeOfHeapCommit() { return sizeOfHeapCommit; } + public int getLoaderFlags() { return loaderFlags; } + public int getNumberOfRvaAndSizes() { return numberOfRvaAndSizes; } + + private long maskInt(long arg) { + return (arg & 0x00000000FFFFFFFFL); + } + } + + class OptionalHeaderDataDirectoriesImpl implements OptionalHeaderDataDirectories { + private int numberOfRvaAndSizes; + private MemoizedObject[] dataDirectories; + private MemoizedObject exportDirectoryTable; + private MemoizedObject debugDirectory; + + private static final int DATA_DIRECTORY_SIZE = 8; + + OptionalHeaderDataDirectoriesImpl(int offset, + int numberOfRvaAndSizes) { + this.numberOfRvaAndSizes = numberOfRvaAndSizes; + dataDirectories = new MemoizedObject[numberOfRvaAndSizes]; + for (int i = 0; i < numberOfRvaAndSizes; i++) { + final int dirOffset = offset + (i * DATA_DIRECTORY_SIZE); + dataDirectories[i] = new MemoizedObject() { + public Object computeValue() { + return new DataDirectoryImpl(dirOffset); + } + }; + } + + exportDirectoryTable = new MemoizedObject() { + public Object computeValue() { + DataDirectory dir = getExportTable(); + if (dir.getRVA() == 0 || dir.getSize() == 0) { + return null; + } + return new ExportDirectoryTableImpl(rvaToFileOffset(dir.getRVA()), dir.getSize()); + } + }; + + debugDirectory = new MemoizedObject() { + public Object computeValue() { + DataDirectory dir = getDebug(); + if (dir.getRVA() == 0 || dir.getSize() == 0) { + return null; + } + return new DebugDirectoryImpl(rvaToFileOffset(dir.getRVA()), dir.getSize()); + } + }; + } + + public DataDirectory getExportTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(0)].getValue(); + } + public DataDirectory getImportTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(1)].getValue(); + } + public DataDirectory getResourceTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(2)].getValue(); + } + public DataDirectory getExceptionTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(3)].getValue(); + } + public DataDirectory getCertificateTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(4)].getValue(); + } + public DataDirectory getBaseRelocationTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(5)].getValue(); + } + public DataDirectory getDebug() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(6)].getValue(); + } + public DataDirectory getArchitecture() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(7)].getValue(); + } + public DataDirectory getGlobalPtr() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(8)].getValue(); + } + public DataDirectory getTLSTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(9)].getValue(); + } + public DataDirectory getLoadConfigTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(10)].getValue(); + } + public DataDirectory getBoundImportTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(11)].getValue(); + } + public DataDirectory getImportAddressTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(12)].getValue(); + } + public DataDirectory getDelayImportDescriptor() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(13)].getValue(); + } + public DataDirectory getCOMPlusRuntimeHeader() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(14)].getValue(); + } + + public ExportDirectoryTable getExportDirectoryTable() throws COFFException { + return (ExportDirectoryTable) exportDirectoryTable.getValue(); + } + + public DebugDirectory getDebugDirectory() throws COFFException { + return (DebugDirectory) debugDirectory.getValue(); + } + + private int checkIndex(int index) throws COFFException { + if ((index < 0) || (index >= dataDirectories.length)) { + throw new COFFException("Directory " + index + " unavailable (only " + + numberOfRvaAndSizes + " tables present)"); + } + return index; + } + } + + class DataDirectoryImpl implements DataDirectory { + int rva; + int size; + + DataDirectoryImpl(int offset) { + seek(offset); + rva = readInt(); + size = readInt(); + } + + public int getRVA() { return rva; } + public int getSize() { return size; } + } + + class ExportDirectoryTableImpl implements ExportDirectoryTable { + private int offset; + private int size; + + private int exportFlags; + private int timeDateStamp; + private short majorVersion; + private short minorVersion; + private int nameRVA; + private int ordinalBase; + private int addressTableEntries; + private int numberOfNamePointers; + private int exportAddressTableRVA; + private int namePointerTableRVA; + private int ordinalTableRVA; + + private MemoizedObject dllName; + + private MemoizedObject exportNameTable; + private MemoizedObject exportNamePointerTable; + private MemoizedObject exportOrdinalTable; + private MemoizedObject exportAddressTable; + + ExportDirectoryTableImpl(int offset, int size) { + this.offset = offset; + this.size = size; + seek(offset); + exportFlags = readInt(); + timeDateStamp = readInt(); + majorVersion = readShort(); + minorVersion = readShort(); + nameRVA = readInt(); + ordinalBase = readInt(); + addressTableEntries = readInt(); + numberOfNamePointers = readInt(); + exportAddressTableRVA = readInt(); + namePointerTableRVA = readInt(); + ordinalTableRVA = readInt(); + + dllName = new MemoizedObject() { + public Object computeValue() { + seek(rvaToFileOffset(getNameRVA())); + return readCString(); + } + }; + + exportNamePointerTable = new MemoizedObject() { + public Object computeValue() { + int[] pointers = new int[getNumberOfNamePointers()]; + seek(rvaToFileOffset(getNamePointerTableRVA())); + // Must make two passes to avoid rvaToFileOffset + // destroying seek() position + for (int i = 0; i < pointers.length; i++) { + pointers[i] = readInt(); + } + for (int i = 0; i < pointers.length; i++) { + pointers[i] = rvaToFileOffset(pointers[i]); + } + return pointers; + } + }; + + exportNameTable = new MemoizedObject() { + public Object computeValue() { + return new ExportNameTable(getExportNamePointerTable()); + } + }; + + exportOrdinalTable = new MemoizedObject() { + public Object computeValue() { + short[] ordinals = new short[getNumberOfNamePointers()]; + seek(rvaToFileOffset(getOrdinalTableRVA())); + for (int i = 0; i < ordinals.length; i++) { + ordinals[i] = readShort(); + } + return ordinals; + } + }; + + exportAddressTable = new MemoizedObject() { + public Object computeValue() { + int[] addresses = new int[getNumberOfAddressTableEntries()]; + seek(rvaToFileOffset(getExportAddressTableRVA())); + // Must make two passes to avoid rvaToFileOffset + // destroying seek() position + for (int i = 0; i < addresses.length; i++) { + addresses[i] = readInt(); + } + for (int i = 0; i < addresses.length; i++) { + addresses[i] = rvaToFileOffset(addresses[i]); + } + return addresses; + } + }; + } + + public int getExportFlags() { return exportFlags; } + public int getTimeDateStamp() { return timeDateStamp; } + public short getMajorVersion() { return majorVersion; } + public short getMinorVersion() { return minorVersion; } + public int getNameRVA() { return nameRVA; } + + public String getDLLName() { + return (String) dllName.getValue(); + } + + public int getOrdinalBase() { return ordinalBase; } + public int getNumberOfAddressTableEntries() { return addressTableEntries; } + public int getNumberOfNamePointers() { return numberOfNamePointers; } + public int getExportAddressTableRVA() { return exportAddressTableRVA; } + public int getNamePointerTableRVA() { return namePointerTableRVA; } + public int getOrdinalTableRVA() { return ordinalTableRVA; } + + public String getExportName(int i) { + return getExportNameTable().get(i); + } + + public short getExportOrdinal(int i) { + return getExportOrdinalTable()[i]; + } + + public boolean isExportAddressForwarder(short ordinal) { + int addr = getExportAddress(ordinal); + return ((offset <= addr) && (addr < (offset + size))); + } + + public String getExportAddressForwarder(short ordinal) { + seek(getExportAddress(ordinal)); + return readCString(); + } + + public int getExportAddress(short ordinal) { + + /////////////////////// + // FIXME: MAJOR HACK // + /////////////////////// + + // According to the documentation, the first line here is + // correct. However, it doesn't seem to work. The second + // one, however, does. + + // OK, it's probably due to using negative indices in the + // export address table in "real life"...need to rethink + // this when I'm more awake + + // return getExportAddressTable()[ordinal - ordinalBase]; + return getExportAddressTable()[ordinal]; + } + + private ExportNameTable getExportNameTable() { + return (ExportNameTable) exportNameTable.getValue(); + } + + private int[] getExportNamePointerTable() { + return (int[]) exportNamePointerTable.getValue(); + } + + private short[] getExportOrdinalTable() { + return (short[]) exportOrdinalTable.getValue(); + } + + private int[] getExportAddressTable() { + return (int[]) exportAddressTable.getValue(); + } + } + + class ExportNameTable { + private MemoizedObject[] names; + + ExportNameTable(final int[] exportNamePointerTable) { + names = new MemoizedObject[exportNamePointerTable.length]; + for (int i = 0; i < exportNamePointerTable.length; i++) { + final int idx = i; + names[idx] = new MemoizedObject() { + public Object computeValue() { + seek(exportNamePointerTable[idx]); + return readCString(); + } + }; + }; + } + + String get(int i) { + return (String) names[i].getValue(); + } + } + + class DebugDirectoryImpl implements DebugDirectory { + private int offset; + private int size; + private int numEntries; + + private static final int DEBUG_DIRECTORY_ENTRY_SIZE = 28; + + DebugDirectoryImpl(int offset, int size) { + this.offset = offset; + this.size = size; + + if ((size % DEBUG_DIRECTORY_ENTRY_SIZE) != 0) { + throw new COFFException("Corrupt DebugDirectory at offset 0x" + + Integer.toHexString(offset)); + } + + numEntries = size / DEBUG_DIRECTORY_ENTRY_SIZE; + } + + public int getNumEntries() { return numEntries; } + public DebugDirectoryEntry getEntry(int i) { + if ((i < 0) || (i >= getNumEntries())) throw new IndexOutOfBoundsException(); + return new DebugDirectoryEntryImpl(offset + i * DEBUG_DIRECTORY_ENTRY_SIZE); + } + } + + class DebugDirectoryEntryImpl implements DebugDirectoryEntry, DebugTypes { + private int characteristics; + private int timeDateStamp; + private short majorVersion; + private short minorVersion; + private int type; + private int sizeOfData; + private int addressOfRawData; + private int pointerToRawData; + + DebugDirectoryEntryImpl(int offset) { + seek(offset); + characteristics = readInt(); + timeDateStamp = readInt(); + majorVersion = readShort(); + minorVersion = readShort(); + type = readInt(); + sizeOfData = readInt(); + addressOfRawData = readInt(); + pointerToRawData = readInt(); + } + + public int getCharacteristics() { return characteristics; } + public int getTimeDateStamp() { return timeDateStamp; } + public short getMajorVersion() { return majorVersion; } + public short getMinorVersion() { return minorVersion; } + public int getType() { return type; } + public int getSizeOfData() { return sizeOfData; } + public int getAddressOfRawData() { return addressOfRawData; } + public int getPointerToRawData() { return pointerToRawData; } + + public DebugVC50 getDebugVC50() { + // See whether we can recognize VC++ 5.0 debug information. + try { + if (getType() != IMAGE_DEBUG_TYPE_CODEVIEW) return null; + + int offset = getPointerToRawData(); + seek(offset); + if (readByte() == 'N' && + readByte() == 'B' && + readByte() == '1' && + readByte() == '1') { + return new DebugVC50Impl(offset); + } + } catch (COFFException e) { + e.printStackTrace(); + } + return null; + } + + public byte getRawDataByte(int i) { + if (i < 0 || i >= getSizeOfData()) { + throw new IndexOutOfBoundsException(); + } + seek(getPointerToRawData() + i); + return readByte(); + } + } + + class DebugVC50Impl implements DebugVC50, DebugVC50TypeLeafIndices { + private int lfaBase; + + private int subsectionDirectoryOffset; + private MemoizedObject subsectionDirectory; + + DebugVC50Impl(int offset) { + lfaBase = offset; + seek(offset); + readInt(); // Discard NB11 + subsectionDirectoryOffset = globalOffset(readInt()); + + // Ensure information is complete + verify(); + + subsectionDirectory = new MemoizedObject() { + public Object computeValue() { + return new DebugVC50SubsectionDirectoryImpl(getSubsectionDirectoryOffset()); + } + }; + } + + public int getSubsectionDirectoryOffset() { + return subsectionDirectoryOffset; + } + + public DebugVC50SubsectionDirectory getSubsectionDirectory() { + return (DebugVC50SubsectionDirectory) subsectionDirectory.getValue(); + } + + private int globalOffset(int offset) { + return offset + lfaBase; + } + + private void verify() { + // Seek to subsection directory manually and look for + // signature following it. This finishes validating that we + // have VC++ 5.0 debug info. Throw COFFException if not + // found; will cause caller to return null. + seek(subsectionDirectoryOffset); + int headerLength = readShort(); + int entryLength = readShort(); + int numEntries = readInt(); + int endOffset = subsectionDirectoryOffset + headerLength + numEntries * entryLength; + seek(endOffset); + + if (readByte() == 'N' && + readByte() == 'B' && + readByte() == '1' && + readByte() == '1') { + return; + } + + throw new COFFException("Did not find NB11 signature at end of debug info"); + } + + class DebugVC50SubsectionDirectoryImpl + implements DebugVC50SubsectionDirectory, + DebugVC50SubsectionTypes { + private int offset; + private short dirHeaderLength; + private short dirEntryLength; + private int numEntries; + + DebugVC50SubsectionDirectoryImpl(int offset) { + this.offset = offset; + // Read header + seek(offset); + dirHeaderLength = readShort(); + dirEntryLength = readShort(); + numEntries = readInt(); + } + + public short getHeaderLength() { return dirHeaderLength; } + public short getEntryLength() { return dirEntryLength; } + public int getNumEntries() { return numEntries; } + + public DebugVC50Subsection getSubsection(int i) { + // Fetch the subsection type and instantiate the correct + // type of subsection based on it + seek(offset + dirHeaderLength + (i * dirEntryLength)); + short ssType = readShort(); + short iMod = readShort(); // Unneeded? + int lfo = globalOffset(readInt()); + int cb = readInt(); + switch (ssType) { + case SST_MODULE: + return new DebugVC50SSModuleImpl(ssType, iMod, cb, lfo); + case SST_TYPES: + return new DebugVC50SSTypesImpl(ssType, iMod, cb, lfo); + case SST_PUBLIC: + return new DebugVC50SSPublicImpl(ssType, iMod, cb, lfo); + case SST_PUBLIC_SYM: + return new DebugVC50SSPublicSymImpl(ssType, iMod, cb, lfo); + case SST_SYMBOLS: + return new DebugVC50SSSymbolsImpl(ssType, iMod, cb, lfo); + case SST_ALIGN_SYM: + return new DebugVC50SSAlignSymImpl(ssType, iMod, cb, lfo); + case SST_SRC_LN_SEG: + return new DebugVC50SSSrcLnSegImpl(ssType, iMod, cb, lfo); + case SST_SRC_MODULE: + return new DebugVC50SSSrcModuleImpl(ssType, iMod, cb, lfo); + case SST_LIBRARIES: + return new DebugVC50SSLibrariesImpl(ssType, iMod, cb, lfo); + case SST_GLOBAL_SYM: + return new DebugVC50SSGlobalSymImpl(ssType, iMod, cb, lfo); + case SST_GLOBAL_PUB: + return new DebugVC50SSGlobalPubImpl(ssType, iMod, cb, lfo); + case SST_GLOBAL_TYPES: + return new DebugVC50SSGlobalTypesImpl(ssType, iMod, cb, lfo); + case SST_MPC: + return new DebugVC50SSMPCImpl(ssType, iMod, cb, lfo); + case SST_SEG_MAP: + return new DebugVC50SSSegMapImpl(ssType, iMod, cb, lfo); + case SST_SEG_NAME: + return new DebugVC50SSSegNameImpl(ssType, iMod, cb, lfo); + case SST_PRE_COMP: + return new DebugVC50SSPreCompImpl(ssType, iMod, cb, lfo); + case SST_UNUSED: + return null; + case SST_OFFSET_MAP_16: + return new DebugVC50SSOffsetMap16Impl(ssType, iMod, cb, lfo); + case SST_OFFSET_MAP_32: + return new DebugVC50SSOffsetMap32Impl(ssType, iMod, cb, lfo); + case SST_FILE_INDEX: + return new DebugVC50SSFileIndexImpl(ssType, iMod, cb, lfo); + case SST_STATIC_SYM: + return new DebugVC50SSStaticSymImpl(ssType, iMod, cb, lfo); + default: + throw new COFFException("Unknown section type " + ssType); + } + } + } + + //////////////////////////////////// + // // + // Implementations of subsections // + // // + //////////////////////////////////// + + class DebugVC50SubsectionImpl implements DebugVC50Subsection { + private short ssType; + private short iMod; + private int ssSize; + + DebugVC50SubsectionImpl(short ssType, short iMod, int ssSize, int offset) { + this.ssType = ssType; + this.iMod = iMod; + this.ssSize = ssSize; + } + + public short getSubsectionType() { return ssType; } + public short getSubsectionModuleIndex() { return iMod; } + public int getSubsectionSize() { return ssSize; } + } + + class DebugVC50SSModuleImpl extends DebugVC50SubsectionImpl implements DebugVC50SSModule { + private int offset; + private short ovlNumber; + private short iLib; + private short cSeg; + private short style; + private MemoizedObject segInfo; + private MemoizedObject name; + + private static final int HEADER_SIZE = 8; + private static final int SEG_INFO_SIZE = 12; + + DebugVC50SSModuleImpl(short ssType, short iMod, int ssSize, final int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + seek(offset); + ovlNumber = readShort(); + iLib = readShort(); + cSeg = readShort(); + style = readShort(); + segInfo = new MemoizedObject() { + public Object computeValue() { + int base = offset + HEADER_SIZE; + DebugVC50SegInfo[] res = new DebugVC50SegInfo[cSeg]; + for (int i = 0; i < cSeg; i++) { + res[i] = new DebugVC50SegInfoImpl(base); + base += SEG_INFO_SIZE; + } + return res; + } + }; + name = new MemoizedObject() { + public Object computeValue() { + return readLengthPrefixedStringAt(offset + (HEADER_SIZE + cSeg * SEG_INFO_SIZE)); + } + }; + } + + public short getOverlayNumber() { return ovlNumber; } + public short getLibrariesIndex() { return iLib; } + public short getNumCodeSegments() { return cSeg; } + public short getDebuggingStyle() { return style; } + public DebugVC50SegInfo getSegInfo(int i) { return ((DebugVC50SegInfo[]) segInfo.getValue())[i]; } + public String getName() { return (String) name.getValue(); } + } + + class DebugVC50SegInfoImpl implements DebugVC50SegInfo { + private short seg; + private int offset; + private int cbSeg; + + DebugVC50SegInfoImpl(int offset) { + seek(offset); + seg = readShort(); + readShort(); // pad + offset = readInt(); + cbSeg = readInt(); + } + + public short getSegment() { return seg; } + public int getOffset() { return offset; } + public int getSegmentCodeSize() { return cbSeg; } + } + + class DebugVC50SSTypesImpl extends DebugVC50SubsectionImpl implements DebugVC50SSTypes { + DebugVC50SSTypesImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSPublicImpl extends DebugVC50SubsectionImpl implements DebugVC50SSPublic { + DebugVC50SSPublicImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSPublicSymImpl extends DebugVC50SubsectionImpl implements DebugVC50SSPublicSym { + DebugVC50SSPublicSymImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSSymbolsImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSymbols { + DebugVC50SSSymbolsImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSAlignSymImpl extends DebugVC50SubsectionImpl implements DebugVC50SSAlignSym { + private int offset; + + DebugVC50SSAlignSymImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + } + + public DebugVC50SymbolIterator getSymbolIterator() { + return new DebugVC50SymbolIteratorImpl(offset, getSubsectionSize()); + } + } + + class DebugVC50SSSrcLnSegImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSrcLnSeg { + DebugVC50SSSrcLnSegImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSSrcModuleImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSrcModule { + private int offset; + private short cFile; + private short cSeg; + private MemoizedObject baseSrcFiles; + private MemoizedObject segOffsets; + private MemoizedObject segs; + + DebugVC50SSSrcModuleImpl(short ssType, short iMod, int ssSize, final int offset) { + super(ssType, iMod, ssSize, offset); + + this.offset = offset; + seek(offset); + cFile = readShort(); + cSeg = readShort(); + + baseSrcFiles = new MemoizedObject() { + public Object computeValue() { + int[] offsets = new int[getNumSourceFiles()]; + seek(offset + 4); + for (int i = 0; i < getNumSourceFiles(); i++) { + offsets[i] = offset + readInt(); + } + DebugVC50SrcModFileDescImpl[] res = new DebugVC50SrcModFileDescImpl[offsets.length]; + for (int i = 0; i < res.length; i++) { + res[i] = new DebugVC50SrcModFileDescImpl(offsets[i], offset); + } + return res; + } + }; + + segOffsets = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4 * (getNumSourceFiles() + 1)); + int[] res = new int[2 * getNumCodeSegments()]; + for (int i = 0; i < 2 * getNumCodeSegments(); i++) { + res[i] = readInt(); + } + return res; + } + }; + + segs = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4 * (getNumSourceFiles() + 1) + 8 * getNumCodeSegments()); + short[] res = new short[getNumCodeSegments()]; + for (int i = 0; i < getNumCodeSegments(); i++) { + res[i] = readShort(); + } + return res; + } + }; + } + + public int getNumSourceFiles() { return cFile & 0xFFFF; } + public int getNumCodeSegments() { return cSeg & 0xFFFF; } + public DebugVC50SrcModFileDesc getSourceFileDesc(int i) { + return ((DebugVC50SrcModFileDescImpl[]) baseSrcFiles.getValue())[i]; + } + + public int getSegmentStartOffset(int i) { + return ((int[]) segOffsets.getValue())[2*i]; + } + + public int getSegmentEndOffset(int i) { + return ((int[]) segOffsets.getValue())[2*i+1]; + } + + public int getSegment(int i) { + return ((short[]) segs.getValue())[i] & 0xFFFF; + } + } + + class DebugVC50SrcModFileDescImpl implements DebugVC50SrcModFileDesc { + private short cSeg; + private MemoizedObject baseSrcLn; + private MemoizedObject segOffsets; + private MemoizedObject name; + + DebugVC50SrcModFileDescImpl(final int offset, final int baseOffset) { + seek(offset); + cSeg = readShort(); + + baseSrcLn = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4); + int[] offsets = new int[getNumCodeSegments()]; + for (int i = 0; i < getNumCodeSegments(); i++) { + offsets[i] = baseOffset + readInt(); + } + DebugVC50SrcModLineNumberMapImpl[] res = + new DebugVC50SrcModLineNumberMapImpl[getNumCodeSegments()]; + for (int i = 0; i < getNumCodeSegments(); i++) { + res[i] = new DebugVC50SrcModLineNumberMapImpl(offsets[i]); + } + return res; + } + }; + + segOffsets = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4 * (getNumCodeSegments() + 1)); + int[] res = new int[2 * getNumCodeSegments()]; + for (int i = 0; i < 2 * getNumCodeSegments(); i++) { + res[i] = readInt(); + } + return res; + } + }; + + name = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4 + 12 * getNumCodeSegments()); + // NOTE: spec says name length is two bytes, but it's really one + int cbName = readByte() & 0xFF; + byte[] res = new byte[cbName]; + readBytes(res); + try { + return new String(res, US_ASCII); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + } + }; + } + + public int getNumCodeSegments() { return cSeg & 0xFFFF; } + + public DebugVC50SrcModLineNumberMap getLineNumberMap(int i) { + return ((DebugVC50SrcModLineNumberMapImpl[]) baseSrcLn.getValue())[i]; + } + + public int getSegmentStartOffset(int i) { + return ((int[]) segOffsets.getValue())[2*i]; + } + + public int getSegmentEndOffset(int i) { + return ((int[]) segOffsets.getValue())[2*i+1]; + } + + public String getSourceFileName() { + return (String) name.getValue(); + } + } + + class DebugVC50SrcModLineNumberMapImpl implements DebugVC50SrcModLineNumberMap { + private short seg; + private short cPair; + private MemoizedObject offsets; + private MemoizedObject lineNumbers; + + DebugVC50SrcModLineNumberMapImpl(final int offset) { + seek(offset); + seg = readShort(); + cPair = readShort(); + offsets = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4); + int[] res = new int[getNumSourceLinePairs()]; + for (int i = 0; i < getNumSourceLinePairs(); i++) { + res[i] = readInt(); + } + return res; + } + }; + + lineNumbers = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4 * (getNumSourceLinePairs() + 1)); + short[] res = new short[getNumSourceLinePairs()]; + for (int i = 0; i < getNumSourceLinePairs(); i++) { + res[i] = readShort(); + } + return res; + } + }; + } + + public int getSegment() { return seg; } + public int getNumSourceLinePairs() { return cPair; } + public int getCodeOffset(int i) { + return ((int[]) offsets.getValue())[i]; + } + public int getLineNumber(int i) { + return ((short[]) lineNumbers.getValue())[i] & 0xFFFF; + } + } + + class DebugVC50SSLibrariesImpl extends DebugVC50SubsectionImpl implements DebugVC50SSLibraries { + DebugVC50SSLibrariesImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + + // FIXME: NOT FINISHED + } + + class DebugVC50SSSymbolBaseImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSymbolBase { + private int offset; + private short symHash; + private short addrHash; + private int cbSymbol; + private int cbSymHash; + private int cbAddrHash; + + private static final int HEADER_SIZE = 16; + + DebugVC50SSSymbolBaseImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + seek(offset); + symHash = readShort(); + addrHash = readShort(); + cbSymbol = readInt(); + cbSymHash = readInt(); + cbAddrHash = readInt(); + } + + public short getSymHashIndex() { return symHash; } + public short getAddrHashIndex() { return addrHash; } + public int getSymTabSize() { return cbSymbol; } + public int getSymHashSize() { return cbSymHash; } + public int getAddrHashSize() { return cbAddrHash; } + + public DebugVC50SymbolIterator getSymbolIterator() { + return new DebugVC50SymbolIteratorImpl(offset + HEADER_SIZE, cbSymbol); + } + } + + class DebugVC50SSGlobalSymImpl extends DebugVC50SSSymbolBaseImpl implements DebugVC50SSGlobalSym { + DebugVC50SSGlobalSymImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + class DebugVC50SSGlobalPubImpl extends DebugVC50SSSymbolBaseImpl implements DebugVC50SSGlobalPub { + DebugVC50SSGlobalPubImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSGlobalTypesImpl extends DebugVC50SubsectionImpl implements DebugVC50SSGlobalTypes { + private int offset; + private int cType; + + DebugVC50SSGlobalTypesImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + seek(offset); + readInt(); // Discard "flags" + cType = readInt(); + } + + public int getNumTypes() { return cType; } + // FIXME: should memoize these + public int getTypeOffset(int i) { + seek(offset + 4 * (i + 2)); + return readInt() + offsetOfFirstType(); + } + + public DebugVC50TypeIterator getTypeIterator() { + return new DebugVC50TypeIteratorImpl(this, + offsetOfFirstType(), + cType); + } + + private int offsetOfFirstType() { + return offset + 4 * (getNumTypes() + 2); + } + } + + class DebugVC50SSMPCImpl extends DebugVC50SubsectionImpl implements DebugVC50SSMPC { + DebugVC50SSMPCImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSSegMapImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSegMap { + private short cSeg; + private short cSegLog; + private MemoizedObject segDescs; + + DebugVC50SSSegMapImpl(short ssType, short iMod, int ssSize, final int offset) { + super(ssType, iMod, ssSize, offset); + seek(offset); + cSeg = readShort(); + cSegLog = readShort(); + segDescs = new MemoizedObject() { + public Object computeValue() { + DebugVC50SegDesc[] descs = new DebugVC50SegDesc[cSeg]; + for (int i = 0; i < cSeg; i++) { + descs[i] = new DebugVC50SegDescImpl(offset + 4 + (20 * i)); + } + return descs; + } + }; + } + + public short getNumSegDesc() { return cSeg; } + public short getNumLogicalSegDesc() { return cSegLog; } + public DebugVC50SegDesc getSegDesc(int i) { return ((DebugVC50SegDesc[]) segDescs.getValue())[i]; } + } + + class DebugVC50SegDescImpl implements DebugVC50SegDesc { + private short flags; + private short ovl; + private short group; + private short frame; + private short iSegName; + private short iClassName; + private int offset; + private int cbSeg; + + DebugVC50SegDescImpl(int offset) { + seek(offset); + flags = readShort(); + ovl = readShort(); + group = readShort(); + frame = readShort(); + iSegName = readShort(); + iClassName = readShort(); + offset = readInt(); + cbSeg = readInt(); + } + + public short getFlags() { return flags; } + public short getOverlayNum() { return ovl; } + public short getGroup() { return group; } + public short getFrame() { return frame; } + public short getName() { return iSegName; } + public short getClassName() { return iClassName; } + public int getOffset() { return offset; } + public int getSize() { return cbSeg; } + } + + + class DebugVC50SSSegNameImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSegName { + private int offset; + private int size; + private MemoizedObject names; + + DebugVC50SSSegNameImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + this.size = ssSize; + seek(offset); + names = new MemoizedObject() { + public Object computeValue() { + int i = 0; + List data = new ArrayList(); + while (i < size) { + String s = readCString(); + data.add(s); + i += s.length(); + } + String[] res = new String[data.size()]; + res = (String[]) data.toArray(res); + return res; + } + }; + } + + public String getSegName(int i) { + return ((String[]) names.getValue())[i]; + } + } + + class DebugVC50SSPreCompImpl extends DebugVC50SubsectionImpl implements DebugVC50SSPreComp { + DebugVC50SSPreCompImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSOffsetMap16Impl extends DebugVC50SubsectionImpl implements DebugVC50SSOffsetMap16 { + DebugVC50SSOffsetMap16Impl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSOffsetMap32Impl extends DebugVC50SubsectionImpl implements DebugVC50SSOffsetMap32 { + DebugVC50SSOffsetMap32Impl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSFileIndexImpl extends DebugVC50SubsectionImpl implements DebugVC50SSFileIndex { + private int offset; + private short cMod; // Number of modules in the executable + private short cRef; // Total number of file name references + private MemoizedObject modStart; + private MemoizedObject cRefCnt; + // FIXME: probably useless; needs fixup to be converted into + // indices rather than offsets + private MemoizedObject nameRef; + private MemoizedObject names; + + DebugVC50SSFileIndexImpl(short ssType, short iMod, int ssSize, final int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + seek(offset); + cMod = readShort(); + cRef = readShort(); + modStart = new MemoizedObject() { + public Object computeValue() { + short[] vals = new short[cMod]; + seek(4 + offset); + for (int i = 0; i < cMod; i++) { + vals[i] = readShort(); + } + return vals; + } + }; + cRefCnt = new MemoizedObject() { + public Object computeValue() { + short[] vals = new short[cMod]; + seek(4 + offset + (2 * cMod)); + for (int i = 0; i < cMod; i++) { + vals[i] = readShort(); + } + return vals; + } + }; + nameRef = new MemoizedObject() { + public Object computeValue() { + int[] vals = new int[cRef]; + seek(4 + offset + (4 * cMod)); + for (int i = 0; i < cMod; i++) { + vals[i] = readInt(); + } + return vals; + } + }; + names = new MemoizedObject() { + public Object computeValue() { + String[] vals = new String[cRef]; + for (int i = 0; i < cRef; i++) { + vals[i] = readCString(); + } + return vals; + } + }; + } + + public short getNumModules() { return cMod; } + public short getNumReferences() { return cRef; } + public short[] getModStart() { return (short[]) modStart.getValue(); } + public short[] getRefCount() { return (short[]) cRefCnt.getValue(); } + public int[] getNameRef() { return (int[]) nameRef.getValue(); } + public String[] getNames() { return (String[]) names.getValue(); } + } + + class DebugVC50SSStaticSymImpl extends DebugVC50SSSymbolBaseImpl implements DebugVC50SSStaticSym { + DebugVC50SSStaticSymImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + ////////////////////////////////////////////////// + // // + // Implementations of symbol and type iterators // + // // + ////////////////////////////////////////////////// + + class DebugVC50SymbolIteratorImpl implements DebugVC50SymbolIterator { + private int base; + private int size; + private int pos; + private int curSymSize; + private int curSymType; + + private static final int HEADER_SIZE = 4; + + DebugVC50SymbolIteratorImpl(int base, int size) { + this(base, size, base); + } + + private DebugVC50SymbolIteratorImpl(int base, int size, int pos) { + this.base = base; + this.size = size; + this.pos = pos; + seek(pos); + curSymSize = readShort() & 0xFFFF; + curSymType = readShort() & 0xFFFF; + } + + public boolean done() { + return (pos == (base + size)); + } + + public void next() throws NoSuchElementException { + if (done()) throw new NoSuchElementException("No more symbols"); + pos += curSymSize + 2; + seek(pos); + curSymSize = readShort() & 0xFFFF; + curSymType = readShort() & 0xFFFF; + } + + public short getLength() { + return (short) curSymSize; + } + + public int getType() { + return curSymType; + } + + public int getOffset() { + return pos + HEADER_SIZE; + } + + ///////////////////////// + // S_COMPILE accessors // + ///////////////////////// + + public byte getCompilerTargetProcessor() { + symSeek(0); + return readByte(); + } + + public int getCompilerFlags() { + symSeek(1); + int res = 0; + for (int i = 0; i < 3; i++) { + int b = readByte() & 0xFF; + res = (res << 8) | b; + } + return res; + } + + public String getComplierVersion() { + return readLengthPrefixedStringAt(4); + } + + ////////////////////////// + // S_REGISTER accessors // + ////////////////////////// + + public int getRegisterSymbolType() { + symSeek(0); + return readInt(); + } + + public short getRegisterEnum() { + symSeek(4); + return readShort(); + } + + public String getRegisterSymbolName() { + return readLengthPrefixedStringAt(6); + } + + ////////////////////////// + // S_CONSTANT accessors // + ////////////////////////// + + public int getConstantType() { + symSeek(0); + return readInt(); + } + + public int getConstantValueAsInt() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(4); + } + + public long getConstantValueAsLong() throws DebugVC50WrongNumericTypeException { + return readLongNumericLeafAt(4); + } + + public float getConstantValueAsFloat() throws DebugVC50WrongNumericTypeException { + return readFloatNumericLeafAt(4); + } + + public double getConstantValueAsDouble() throws DebugVC50WrongNumericTypeException { + return readDoubleNumericLeafAt(4); + } + + public String getConstantName() { + return readLengthPrefixedStringAt(4 + numericLeafLengthAt(4)); + } + + ///////////////////// + // S_UDT accessors // + ///////////////////// + + public int getUDTType() { + symSeek(0); + return readInt(); + } + + public String getUDTName() { + return readLengthPrefixedStringAt(4); + } + + ///////////////////////// + // S_SSEARCH accessors // + ///////////////////////// + + public int getSearchSymbolOffset() { + symSeek(0); + return readInt(); + } + + public short getSearchSegment() { + symSeek(4); + return readShort(); + } + + ///////////////////// + // S_END accessors // + ///////////////////// + + // (No accessors) + + ////////////////////// + // S_SKIP accessors // + ////////////////////// + + // (No accessors) + + /////////////////////////// + // S_CVRESERVE accessors // + /////////////////////////// + + // (No accessors) + + ///////////////////////// + // S_OBJNAME accessors // + ///////////////////////// + + public int getObjectCodeViewSignature() { + symSeek(0); + return readInt(); + } + + public String getObjectName() { + return readLengthPrefixedStringAt(4); + } + + //////////////////////// + // S_ENDARG accessors // + //////////////////////// + + // (No accessors) + + ////////////////////////// + // S_COBOLUDT accessors // + ////////////////////////// + + // (Elided as they are irrelevant) + + ///////////////////////// + // S_MANYREG accessors // + ///////////////////////// + + public int getManyRegType() { + symSeek(0); + return readInt(); + } + + public byte getManyRegCount() { + symSeek(4); + return readByte(); + } + + public byte getManyRegRegister(int i) { + symSeek(5 + i); + return readByte(); + } + + public String getManyRegName() { + return readLengthPrefixedStringAt(5 + getManyRegCount()); + } + + //////////////////////// + // S_RETURN accessors // + //////////////////////// + + public short getReturnFlags() { + symSeek(0); + return readShort(); + } + + public byte getReturnStyle() { + symSeek(2); + return readByte(); + } + + public byte getReturnRegisterCount() { + symSeek(3); + return readByte(); + } + + public byte getReturnRegister(int i) { + symSeek(4 + i); + return readByte(); + } + + /////////////////////////// + // S_ENTRYTHIS accessors // + /////////////////////////// + + public void advanceToEntryThisSymbol() { + seek(pos + 4); + int tmpSymSize = readShort(); + int tmpSymType = readShort(); + if (Assert.ASSERTS_ENABLED) { + // Make sure that ends of inner and outer symbols line + // up, otherwise need more work + Assert.that(pos + curSymSize + 2 == pos + 4 + tmpSymSize, + "advanceToEntryThisSymbol needs more work"); + } + pos += 4; + curSymSize = tmpSymSize; + curSymType = tmpSymType; + } + + /////////////////////////////////////////////////////////////////////// + // // + // // + // Symbols for (Intel) 16:32 Segmented and 32-bit Flat Architectures // + // // + // // + /////////////////////////////////////////////////////////////////////// + + ///////////////////////// + // S_BPREL32 accessors // + ///////////////////////// + + public int getBPRelOffset() { + symSeek(0); + return readInt(); + } + + public int getBPRelType() { + symSeek(4); + return readInt(); + } + + public String getBPRelName() { + return readLengthPrefixedStringAt(8); + } + + /////////////////////////////////////// + // S_LDATA32 and S_GDATA32 accessors // + /////////////////////////////////////// + + public int getLGDataType() { + symSeek(0); + return readInt(); + } + + public int getLGDataOffset() { + symSeek(4); + return readInt(); + } + + public short getLGDataSegment() { + symSeek(8); + return readShort(); + } + + public String getLGDataName() { + return readLengthPrefixedStringAt(10); + } + + /////////////////////// + // S_PUB32 accessors // + /////////////////////// + + // FIXME: has the same format as the above; consider updating + // documentation. No separate accessors provided. + + /////////////////////////////////////// + // S_LPROC32 and S_GPROC32 accessors // + /////////////////////////////////////// + + public DebugVC50SymbolIterator getLGProcParent() { + int offs = getLGProcParentOffset(); + if (offs == 0) return null; + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getLGProcParentOffset() { + symSeek(0); + int offs = readInt(); + if (offs == 0) return 0; + return base + offs; + } + + public DebugVC50SymbolIterator getLGProcEnd() { + int offs = getLGProcEndOffset(); + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getLGProcEndOffset() { + symSeek(4); + int offs = readInt(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(offs != 0, "should not have null end offset for procedure symbols"); + } + return base + offs; + } + + public DebugVC50SymbolIterator getLGProcNext() { + int offs = getLGProcNextOffset(); + if (offs == 0) return null; + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getLGProcNextOffset() { + symSeek(8); + int offs = readInt(); + if (offs == 0) return 0; + return base + offs; + } + + public int getLGProcLength() { + symSeek(12); + return readInt(); + } + + public int getLGProcDebugStart() { + symSeek(16); + return readInt(); + } + + public int getLGProcDebugEnd() { + symSeek(20); + return readInt(); + } + + public int getLGProcType() { + symSeek(24); + return readInt(); + } + + public int getLGProcOffset() { + symSeek(28); + return readInt(); + } + + public short getLGProcSegment() { + symSeek(32); + return readShort(); + } + + public byte getLGProcFlags() { + symSeek(34); + return readByte(); + } + + public String getLGProcName() { + return readLengthPrefixedStringAt(35); + } + + ///////////////////////// + // S_THUNK32 accessors // + ///////////////////////// + + public DebugVC50SymbolIterator getThunkParent() { + int offs = getThunkParentOffset(); + if (offs == 0) return null; + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getThunkParentOffset() { + symSeek(0); + int offs = readInt(); + if (offs == 0) return 0; + return base + offs; + } + + public DebugVC50SymbolIterator getThunkEnd() { + symSeek(4); + int offs = readInt(); + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getThunkEndOffset() { + symSeek(4); + int offs = readInt(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(offs != 0, "should not have null end offset for thunk symbols"); + } + return base + offs; + } + + public DebugVC50SymbolIterator getThunkNext() { + int offs = getThunkNextOffset(); + if (offs == 0) return null; + return new DebugVC50SymbolIteratorImpl(base, size, base + offs); + } + + public int getThunkNextOffset() { + symSeek(8); + int offs = readInt(); + if (offs == 0) return 0; + return base + offs; + } + + public int getThunkOffset() { + symSeek(12); + return readInt(); + } + + public short getThunkSegment() { + symSeek(16); + return readShort(); + } + + public short getThunkLength() { + symSeek(18); + return readShort(); + } + + public byte getThunkType() { + symSeek(20); + return readByte(); + } + + public String getThunkName() { + return readLengthPrefixedStringAt(21); + } + + public short getThunkAdjustorThisDelta() { + symSeek(21 + lengthPrefixedStringLengthAt(21)); + return readShort(); + } + + public String getThunkAdjustorTargetName() { + return readLengthPrefixedStringAt(23 + lengthPrefixedStringLengthAt(21)); + } + + public short getThunkVCallDisplacement() { + symSeek(21 + lengthPrefixedStringLengthAt(21)); + return readShort(); + } + + public int getThunkPCodeOffset() { + symSeek(21 + lengthPrefixedStringLengthAt(21)); + return readInt(); + } + + public short getThunkPCodeSegment() { + symSeek(25 + lengthPrefixedStringLengthAt(21)); + return readShort(); + } + + ///////////////////////// + // S_BLOCK32 accessors // + ///////////////////////// + + public DebugVC50SymbolIterator getBlockParent() { + int offs = getBlockParentOffset(); + if (offs == 0) return null; + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getBlockParentOffset() { + symSeek(0); + int offs = readInt(); + if (offs == 0) return 0; + return base + offs; + } + + public DebugVC50SymbolIterator getBlockEnd() { + symSeek(4); + int offs = readInt(); + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getBlockEndOffset() { + symSeek(4); + int offs = readInt(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(offs != 0, "should not have null end offset for block symbols"); + } + return base + offs; + } + + public int getBlockLength() { + symSeek(8); + return readInt(); + } + + public int getBlockOffset() { + symSeek(12); + return readInt(); + } + + public short getBlockSegment() { + symSeek(16); + return readShort(); + } + + public String getBlockName() { + return readLengthPrefixedStringAt(18); + } + + //////////////////////// + // S_WITH32 accessors // + //////////////////////// + + // FIXME: this is a Pascal construct; ignored for now + + ///////////////////////// + // S_LABEL32 accessors // + ///////////////////////// + + public int getLabelOffset() { + symSeek(0); + return readInt(); + } + + public short getLabelSegment() { + symSeek(4); + return readShort(); + } + + public byte getLabelFlags() { + symSeek(6); + return readByte(); + } + + public String getLabelName() { + return readLengthPrefixedStringAt(7); + } + + //////////////////////////// + // S_CEXMODEL32 accessors // + //////////////////////////// + + public int getChangeOffset() { + symSeek(0); + return readInt(); + } + + public short getChangeSegment() { + symSeek(4); + return readShort(); + } + + public short getChangeModel() { + symSeek(6); + return readShort(); + } + + //////////////////////////// + // S_VFTTABLE32 accessors // + //////////////////////////// + + public int getVTableRoot() { + symSeek(0); + return readInt(); + } + + public int getVTablePath() { + symSeek(4); + return readInt(); + } + + public int getVTableOffset() { + symSeek(8); + return readInt(); + } + + public short getVTableSegment() { + symSeek(12); + return readShort(); + } + + ////////////////////////// + // S_REGREL32 accessors // + ////////////////////////// + + public int getRegRelOffset() { + symSeek(0); + return readInt(); + } + + public int getRegRelType() { + symSeek(4); + return readInt(); + } + + public short getRegRelRegister() { + symSeek(8); + return readShort(); + } + + public String getRegRelName() { + return readLengthPrefixedStringAt(10); + } + + /////////////////////////////////////////// + // S_LTHREAD32 and S_GTHREAD32 accessors // + /////////////////////////////////////////// + + public int getLThreadType() { + symSeek(0); + return readInt(); + } + + public int getLThreadOffset() { + symSeek(4); + return readInt(); + } + + public short getLThreadSegment() { + symSeek(8); + return readShort(); + } + + public String getLThreadName() { + return readLengthPrefixedStringAt(10); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void symSeek(int offsetInSym) { + seek(pos + HEADER_SIZE + offsetInSym); + } + + private int numericLeafLengthAt(int offsetInSym) { + return DebugVC50Impl.this.numericLeafLengthAt(pos + HEADER_SIZE + offsetInSym); + } + + private int readIntNumericLeafAt(int offsetInSym) { + return DebugVC50Impl.this.readIntNumericLeafAt(pos + HEADER_SIZE + offsetInSym); + } + + private long readLongNumericLeafAt(int offsetInSym) { + return DebugVC50Impl.this.readLongNumericLeafAt(pos + HEADER_SIZE + offsetInSym); + } + + private float readFloatNumericLeafAt(int offsetInSym) { + return DebugVC50Impl.this.readFloatNumericLeafAt(pos + HEADER_SIZE + offsetInSym); + } + + private double readDoubleNumericLeafAt(int offsetInSym) { + return DebugVC50Impl.this.readDoubleNumericLeafAt(pos + HEADER_SIZE + offsetInSym); + } + + private int lengthPrefixedStringLengthAt(int offsetInSym) { + return DebugVC50Impl.this.lengthPrefixedStringLengthAt(pos + HEADER_SIZE + offsetInSym); + } + + private String readLengthPrefixedStringAt(int offsetInSym) { + return DebugVC50Impl.this.readLengthPrefixedStringAt(pos + HEADER_SIZE + offsetInSym); + } + } + + class DebugVC50TypeIteratorImpl implements DebugVC50TypeIterator, + DebugVC50TypeLeafIndices, DebugVC50MemberAttributes, DebugVC50TypeEnums { + private DebugVC50SSGlobalTypes parent; + private int base; + private int numTypes; + private int typeIndex; + private int typeRecordOffset; + private int typeStringOffset; + private int typeRecordSize; + private int typeStringLeaf; + + DebugVC50TypeIteratorImpl(DebugVC50SSGlobalTypes parent, int base, int numTypes) { + this(parent, base, numTypes, 0, base); + } + + private DebugVC50TypeIteratorImpl(DebugVC50SSGlobalTypes parent, int base, int numTypes, int curType, int offset) { + this.parent = parent; + this.base = base; + this.numTypes = numTypes; + this.typeIndex = curType; + if (!done()) { + typeRecordOffset = offset; + loadTypeRecord(); + } + } + + public boolean done() { + return (typeIndex == numTypes); + } + + public void next() throws NoSuchElementException { + if (done()) throw new NoSuchElementException(); + ++typeIndex; + if (!done()) { + typeRecordOffset = parent.getTypeOffset(typeIndex); + loadTypeRecord(); + } + } + + public short getLength() { + return (short) typeRecordSize; + } + + public int getTypeIndex() { + return biasTypeIndex(typeIndex); + } + + public int getNumTypes() { + return numTypes; + } + + public boolean typeStringDone() { + return (typeStringOffset - typeRecordOffset - 2) >= typeRecordSize; + } + + public void typeStringNext() throws NoSuchElementException { + if (typeStringDone()) throw new NoSuchElementException(); + typeStringOffset += typeStringLength(); + loadTypeString(); + } + + public int typeStringLeaf() { + return typeStringLeaf; + } + + public int typeStringOffset() { + return typeStringOffset; + } + + /////////////////////////// + // LF_MODIFIER accessors // + /////////////////////////// + + public int getModifierIndex() { + typeSeek(2); + return readInt(); + } + + public short getModifierAttribute() { + typeSeek(6); + return readShort(); + } + + ////////////////////////// + // LF_POINTER accessors // + ////////////////////////// + + public int getPointerType() { + typeSeek(2); + return readInt(); + } + + public int getPointerAttributes() { + typeSeek(6); + return readInt(); + } + + public int getPointerBasedOnTypeIndex() { + typeSeek(10); + return readInt(); + } + + public String getPointerBasedOnTypeName() { + return readLengthPrefixedStringAt(14); + } + + public int getPointerToMemberClass() { + typeSeek(10); + return readInt(); + } + + public short getPointerToMemberFormat() { + typeSeek(14); + return readShort(); + } + + //////////////////////// + // LF_ARRAY accessors // + //////////////////////// + + public int getArrayElementType() { + typeSeek(2); + return readInt(); + } + + public int getArrayIndexType() { + typeSeek(6); + return readInt(); + } + + public int getArrayLength() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(10); + } + + public String getArrayName() { + return readLengthPrefixedStringAt(10 + numericLeafLengthAt(10)); + } + + ///////////////////////////////////////// + // LF_CLASS and LF_STRUCTURE accessors // + ///////////////////////////////////////// + + public short getClassCount() { + typeSeek(2); + return readShort(); + } + + public short getClassProperty() { + typeSeek(4); + return readShort(); + } + + public int getClassFieldList() { + typeSeek(6); + return readInt(); + } + + public DebugVC50TypeIterator getClassFieldListIterator() { + int index = unbiasTypeIndex(getClassFieldList()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + public int getClassDerivationList() { + typeSeek(10); + return readInt(); + } + + public int getClassVShape() { + typeSeek(14); + return readInt(); + } + + public int getClassSize() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(18); + } + + public String getClassName() { + return readLengthPrefixedStringAt(18 + numericLeafLengthAt(18)); + } + + //////////////////////// + // LF_UNION accessors // + //////////////////////// + + public short getUnionCount() { + typeSeek(2); + return readShort(); + } + + public short getUnionProperty() { + typeSeek(4); + return readShort(); + } + + public int getUnionFieldList() { + typeSeek(6); + return readInt(); + } + + public DebugVC50TypeIterator getUnionFieldListIterator() { + int index = unbiasTypeIndex(getUnionFieldList()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + public int getUnionSize() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(10); + } + + public String getUnionName() { + return readLengthPrefixedStringAt(10 + numericLeafLengthAt(10)); + } + + /////////////////////// + // LF_ENUM accessors // + /////////////////////// + + public short getEnumCount() { + typeSeek(2); + return readShort(); + } + + public short getEnumProperty() { + typeSeek(4); + return readShort(); + } + + public int getEnumType() { + typeSeek(6); + return readInt(); + } + + public int getEnumFieldList() { + typeSeek(10); + return readInt(); + } + + public DebugVC50TypeIterator getEnumFieldListIterator() { + int index = unbiasTypeIndex(getEnumFieldList()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + public String getEnumName() { + return readLengthPrefixedStringAt(14); + } + + //////////////////////////// + // LF_PROCEDURE accessors // + //////////////////////////// + + public int getProcedureReturnType() { + typeSeek(2); + return readInt(); + } + + public byte getProcedureCallingConvention() { + typeSeek(6); + return readByte(); + } + + public short getProcedureNumberOfParameters() { + typeSeek(8); + return readShort(); + } + + public int getProcedureArgumentList() { + typeSeek(10); + return readInt(); + } + + public DebugVC50TypeIterator getProcedureArgumentListIterator() { + int index = unbiasTypeIndex(getProcedureArgumentList()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + //////////////////////////// + // LF_MFUNCTION accessors // + //////////////////////////// + + public int getMFunctionReturnType() { + typeSeek(2); + return readInt(); + } + + public int getMFunctionContainingClass() { + typeSeek(6); + return readInt(); + } + + public int getMFunctionThis() { + typeSeek(10); + return readInt(); + } + + public byte getMFunctionCallingConvention() { + typeSeek(14); + return readByte(); + } + + public short getMFunctionNumberOfParameters() { + typeSeek(16); + return readShort(); + } + + public int getMFunctionArgumentList() { + typeSeek(18); + return readInt(); + } + + public DebugVC50TypeIterator getMFunctionArgumentListIterator() { + int index = unbiasTypeIndex(getMFunctionArgumentList()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + public int getMFunctionThisAdjust() { + typeSeek(22); + return readInt(); + } + + ////////////////////////// + // LF_VTSHAPE accessors // + ////////////////////////// + + public short getVTShapeCount() { + typeSeek(2); + return readShort(); + } + + public int getVTShapeDescriptor(int i) { + typeSeek(4 + (i / 2)); + int val = readByte() & 0xFF; + if ((i % 2) != 0) { + val = val >> 4; + } + return val; + } + + ///////////////////////// + // LF_BARRAY accessors // + ///////////////////////// + + public int getBasicArrayType() { + typeSeek(2); + return readInt(); + } + + //////////////////////// + // LF_LABEL accessors // + //////////////////////// + + public short getLabelAddressMode() { + typeSeek(2); + return readShort(); + } + + /////////////////////////// + // LF_DIMARRAY accessors // + /////////////////////////// + + public int getDimArrayType() { + typeSeek(2); + return readInt(); + } + + public int getDimArrayDimInfo() { + typeSeek(6); + return readInt(); + } + + public String getDimArrayName() { + return readLengthPrefixedStringAt(10); + } + + ////////////////////////// + // LF_VFTPATH accessors // + ////////////////////////// + + public int getVFTPathCount() { + typeSeek(2); + return readInt(); + } + + public int getVFTPathBase(int i) { + typeSeek(6 + (4 * i)); + return readInt(); + } + + /////////////////////// + // LF_SKIP accessors // + /////////////////////// + + public int getSkipIndex() { + typeSeek(2); + return readInt(); + } + + ////////////////////////// + // LF_ARGLIST accessors // + ////////////////////////// + + public int getArgListCount() { + typeSeek(2); + return readInt(); + } + + public int getArgListType(int i) { + typeSeek(6 + (4 * i)); + return readInt(); + } + + ///////////////////////// + // LF_DEFARG accessors // + ///////////////////////// + + public int getDefaultArgType() { + typeSeek(2); + return readInt(); + } + + public String getDefaultArgExpression() { + return readLengthPrefixedStringAt(6); + } + + ////////////////////////// + // LF_DERIVED accessors // + ////////////////////////// + + public int getDerivedCount() { + typeSeek(2); + return readInt(); + } + + public int getDerivedType(int i) { + typeSeek(6); + return readInt(); + } + + /////////////////////////// + // LF_BITFIELD accessors // + /////////////////////////// + + public int getBitfieldFieldType() { + typeSeek(2); + return readInt(); + } + + public byte getBitfieldLength() { + typeSeek(6); + return readByte(); + } + + public byte getBitfieldPosition() { + typeSeek(7); + return readByte(); + } + + //////////////////////// + // LF_MLIST accessors // + //////////////////////// + + public short getMListAttribute() { + typeSeek(2); + return readShort(); + } + + public int getMListLength() { + return (getLength() - 6 - (isMListIntroducingVirtual() ? 4 : 0)) / 4; + } + + public int getMListType(int i) { + typeSeek(6 + 4 * i); + return readInt(); + } + + public boolean isMListIntroducingVirtual() { + return isIntroducingVirtual(getMListAttribute()); + } + + public int getMListVtabOffset() { + typeSeek(6 + 4 * getMListLength()); + return readInt(); + } + + ///////////////////////// + // LF_REFSYM accessors // + ///////////////////////// + + public DebugVC50SymbolIterator getRefSym() { + typeSeek(2); + int len = readShort() & 0xFFFF; + return new DebugVC50SymbolIteratorImpl(typeStringOffset + 2, len); + } + + ///////////////////////// + // LF_BCLASS accessors // + ///////////////////////// + + public short getBClassAttribute() { + typeSeek(2); + return readShort(); + } + + public int getBClassType() { + typeSeek(4); + return readInt(); + } + + public int getBClassOffset() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(8); + } + + ////////////////////////// + // LF_VBCLASS accessors // + ////////////////////////// + + public short getVBClassAttribute() { + typeSeek(2); + return readShort(); + } + + public int getVBClassBaseClassType() { + typeSeek(4); + return readInt(); + } + + public int getVBClassVirtualBaseClassType() { + typeSeek(8); + return readInt(); + } + + public int getVBClassVBPOff() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(12); + } + + public int getVBClassVBOff() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(12 + numericLeafLengthAt(12)); + } + + /////////////////////////// + // LF_IVBCLASS accessors // + /////////////////////////// + + public short getIVBClassAttribute() { + typeSeek(2); + return readShort(); + } + + public int getIVBClassBType() { + typeSeek(4); + return readInt(); + } + + public int getIVBClassVBPType() { + typeSeek(8); + return readInt(); + } + + public int getIVBClassVBPOff() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(12); + } + + public int getIVBClassVBOff() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(12 + numericLeafLengthAt(12)); + } + + //////////////////////////// + // LF_ENUMERATE accessors // + //////////////////////////// + + public short getEnumerateAttribute() { + typeSeek(2); + return readShort(); + } + + public long getEnumerateValue() { + return readIntNumericLeafAt(4); + } + + public String getEnumerateName() { + return readLengthPrefixedStringAt(4 + numericLeafLengthAt(4)); + } + + //////////////////////////// + // LF_FRIENDFCN accessors // + //////////////////////////// + + public int getFriendFcnType() { + typeSeek(4); + return readInt(); + } + + public String getFriendFcnName() { + return readLengthPrefixedStringAt(8); + } + + //////////////////////// + // LF_INDEX accessors // + //////////////////////// + + public int getIndexValue() { + typeSeek(4); + return readInt(); + } + + public DebugVC50TypeIterator getIndexIterator() { + int index = unbiasTypeIndex(getIndexValue()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + ///////////////////////// + // LF_MEMBER accessors // + ///////////////////////// + + public short getMemberAttribute() { + typeSeek(2); + return readShort(); + } + + public int getMemberType() { + typeSeek(4); + return readInt(); + } + + public int getMemberOffset() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(8); + } + + public String getMemberName() { + return readLengthPrefixedStringAt(8 + numericLeafLengthAt(8)); + } + + /////////////////////////// + // LF_STMEMBER accessors // + /////////////////////////// + + public short getStaticAttribute() { + typeSeek(2); + return readShort(); + } + + public int getStaticType() { + typeSeek(4); + return readInt(); + } + + public String getStaticName() { + return readLengthPrefixedStringAt(8); + } + + ///////////////////////// + // LF_METHOD accessors // + ///////////////////////// + + public short getMethodCount() { + typeSeek(2); + return readShort(); + } + + public int getMethodList() { + typeSeek(4); + return readInt(); + } + + public String getMethodName() { + return readLengthPrefixedStringAt(8); + } + + ///////////////////////////// + // LF_NESTEDTYPE accessors // + ///////////////////////////// + + public int getNestedType() { + typeSeek(4); + return readInt(); + } + + public String getNestedName() { + return readLengthPrefixedStringAt(8); + } + + /////////////////////////// + // LF_VFUNCTAB accessors // + /////////////////////////// + + public int getVFuncTabType() { + typeSeek(4); + return readInt(); + } + + //////////////////////////// + // LF_FRIENDCLS accessors // + //////////////////////////// + + public int getFriendClsType() { + typeSeek(4); + return readInt(); + } + + //////////////////////////// + // LF_ONEMETHOD accessors // + //////////////////////////// + + public short getOneMethodAttribute() { + typeSeek(2); + return readShort(); + } + + public int getOneMethodType() { + typeSeek(4); + return readInt(); + } + + public boolean isOneMethodIntroducingVirtual() { + return isIntroducingVirtual(getOneMethodAttribute()); + } + + public int getOneMethodVBaseOff() { + typeSeek(8); + return readInt(); + } + + public String getOneMethodName() { + int baseLen = 8 + (isOneMethodIntroducingVirtual() ? 4 : 0); + return readLengthPrefixedStringAt(baseLen); + } + + /////////////////////////// + // LF_VFUNCOFF accessors // + /////////////////////////// + + public int getVFuncOffType() { + typeSeek(4); + return readInt(); + } + + public int getVFuncOffOffset() { + typeSeek(8); + return readInt(); + } + + /////////////////////////////// + // LF_NESTEDTYPEEX accessors // + /////////////////////////////// + + public short getNestedExAttribute() { + typeSeek(2); + return readShort(); + } + + public int getNestedExType() { + typeSeek(4); + return readInt(); + } + + public String getNestedExName() { + return readLengthPrefixedStringAt(8); + } + + /////////////////////////////// + // LF_MEMBERMODIFY accessors // + /////////////////////////////// + + public short getMemberModifyAttribute() { + typeSeek(2); + return readShort(); + } + + public int getMemberModifyType() { + typeSeek(4); + return readInt(); + } + + public String getMemberModifyName() { + return readLengthPrefixedStringAt(8); + } + + //////////////////////////// + // Numeric Leaf accessors // + //////////////////////////// + + public short getNumericTypeAt(int byteOffset) { + typeSeek(byteOffset); + return readShort(); + } + + public int getNumericLengthAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + return numericLeafLengthAt(byteOffset); + } + + public int getNumericIntAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(byteOffset); + } + + public long getNumericLongAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + // FIXME + throw new RuntimeException("Unimplemented"); + } + + public float getNumericFloatAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + // FIXME + throw new RuntimeException("Unimplemented"); + } + + public double getNumericDoubleAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + // FIXME + throw new RuntimeException("Unimplemented"); + } + + public byte[] getNumericDataAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + // FIXME + throw new RuntimeException("Unimplemented"); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void loadTypeRecord() { + seek(typeRecordOffset); + typeRecordSize = readShort() & 0xFFFF; + typeStringOffset = typeRecordOffset + 2; + loadTypeString(); + } + + private void loadTypeString() { + seek(typeStringOffset); + int lo = readByte() & 0xFF; + // See if it is one of the single-byte leaves + if (lo >= LF_PAD0) { + typeStringLeaf = lo; + } else { + int hi = readByte() & 0xFF; + typeStringLeaf = (hi << 8) | lo; + } + } + + private void typeSeek(int offset) { + seek(typeStringOffset + offset); + } + + private int typeStringLength() { + // LF_PAD + if (typeStringLeaf >= 0xF0 && typeStringLeaf <= 0xFF) { + return (typeStringLeaf - 0xF0); + } + + switch (typeStringLeaf) { + + // Leaf indices for type records that can be referenced + // from symbols: + case LF_MODIFIER: return 8; + case LF_POINTER: { + int extraLen = 0; + int attr = (getPointerAttributes() & POINTER_PTRTYPE_MASK) >> POINTER_PTRTYPE_SHIFT; + int mode = (getPointerAttributes() & POINTER_PTRMODE_MASK) >> POINTER_PTRMODE_SHIFT; + if (attr == POINTER_PTRTYPE_BASED_ON_TYPE) { + extraLen = 4 + numericLeafLengthAt(typeStringOffset + 14); + } else if (mode == POINTER_PTRMODE_PTR_TO_DATA_MEMBER || + mode == POINTER_PTRMODE_PTR_TO_METHOD) { + extraLen = 6; + } + return 10 + extraLen; + } + case LF_ARRAY: { + int temp = 10 + numericLeafLengthAt(10); + return temp + lengthPrefixedStringLengthAt(temp); + } + case LF_CLASS: + case LF_STRUCTURE: { + int temp = 18 + numericLeafLengthAt(18); + return temp + lengthPrefixedStringLengthAt(temp); + } + case LF_UNION: { + int temp = 10 + numericLeafLengthAt(10); + return temp + lengthPrefixedStringLengthAt(temp); + } + case LF_ENUM: { + return 14 + lengthPrefixedStringLengthAt(14); + } + case LF_PROCEDURE: return 14; + case LF_MFUNCTION: return 26; + case LF_VTSHAPE: return 4 + ((getVTShapeCount() + 1) / 2); + case LF_COBOL0: + case LF_COBOL1: throw new COFFException("COBOL symbols unimplemented"); + case LF_BARRAY: return 6; + case LF_LABEL: return 4; + case LF_NULL: return 2; + case LF_NOTTRAN: return 2; + case LF_DIMARRAY: return 10 + lengthPrefixedStringLengthAt(10); + case LF_VFTPATH: return 6 + 4 * getVFTPathCount(); + case LF_PRECOMP: return 14 + lengthPrefixedStringLengthAt(14); + case LF_ENDPRECOMP: return 6; + case LF_OEM: throw new COFFException("OEM symbols unimplemented"); + case LF_TYPESERVER: return 10 + lengthPrefixedStringLengthAt(10); + + case LF_SKIP: return 6 + numericLeafLengthAt(6); + case LF_ARGLIST: return 6 + 4 * getArgListCount(); + case LF_DEFARG: return 6 + lengthPrefixedStringLengthAt(6); + // case LF_FIELDLIST: throw new COFFException("Should not see LF_FIELDLIST leaf"); + case LF_FIELDLIST: return 2; + case LF_DERIVED: return 6 + 4 * getDerivedCount(); + case LF_BITFIELD: return 8; + case LF_METHODLIST: { + return 6 + 4 * getMListLength() + (isMListIntroducingVirtual() ? 4 : 0); + } + case LF_DIMCONU: + case LF_DIMCONLU: + case LF_DIMVARU: + case LF_DIMVARLU: throw new COFFException("LF_DIMCONU, LF_DIMCONLU, LF_DIMVARU, and LF_DIMVARLU unsupported"); + case LF_REFSYM: { + seek(typeStringOffset + 2); + return 4 + readShort(); + } + + case LF_BCLASS: return 8 + numericLeafLengthAt(8); + case LF_VBCLASS: + case LF_IVBCLASS: { + int temp = 12 + numericLeafLengthAt(12); + return temp + numericLeafLengthAt(temp); + } + case LF_ENUMERATE: { + int temp = 4 + numericLeafLengthAt(4); + return temp + lengthPrefixedStringLengthAt(temp); + } + case LF_FRIENDFCN: return 8 + lengthPrefixedStringLengthAt(8); + case LF_INDEX: return 8; + case LF_MEMBER: { + int temp = 8 + numericLeafLengthAt(8); + return temp + lengthPrefixedStringLengthAt(temp); + } + case LF_STMEMBER: return 8 + lengthPrefixedStringLengthAt(8); + case LF_METHOD: return 8 + lengthPrefixedStringLengthAt(8); + case LF_NESTTYPE: return 8 + lengthPrefixedStringLengthAt(8); + case LF_VFUNCTAB: return 8; + case LF_FRIENDCLS: return 8; + case LF_ONEMETHOD: { + int baseLen = 8 + (isOneMethodIntroducingVirtual() ? 4 : 0); + return baseLen + lengthPrefixedStringLengthAt(baseLen); + } + case LF_VFUNCOFF: return 12; + case LF_NESTTYPEEX: return 8 + lengthPrefixedStringLengthAt(8); + case LF_MEMBERMODIFY: return 8 + lengthPrefixedStringLengthAt(8); + + // Should not encounter numeric leaves with this routine + case LF_CHAR: + case LF_SHORT: + case LF_USHORT: + case LF_LONG: + case LF_ULONG: + case LF_REAL32: + case LF_REAL64: + case LF_REAL80: + case LF_REAL128: + case LF_QUADWORD: + case LF_UQUADWORD: + case LF_REAL48: + case LF_COMPLEX32: + case LF_COMPLEX64: + case LF_COMPLEX80: + case LF_COMPLEX128: + case LF_VARSTRING: throw new RuntimeException("Unexpected numeric leaf " + typeStringLeaf + + "in type string"); + default: + throw new COFFException("Unrecognized leaf " + typeStringLeaf + " in type string at offset " + + typeStringOffset); + } + } + + private boolean isIntroducingVirtual(int mprop) { + int masked = mprop & MEMATTR_MPROP_MASK; + return ((masked == MEMATTR_MPROP_INTRODUCING_VIRTUAL) || + (masked == MEMATTR_MPROP_PURE_INTRODUCING_VIRTUAL)); + } + + private int numericLeafLengthAt(int offset) { + return DebugVC50Impl.this.numericLeafLengthAt(typeStringOffset + offset); + } + + private int readIntNumericLeafAt(int offset) { + return DebugVC50Impl.this.readIntNumericLeafAt(typeStringOffset + offset); + } + + private int lengthPrefixedStringLengthAt(int offset) { + return DebugVC50Impl.this.lengthPrefixedStringLengthAt(typeStringOffset + offset); + } + + private String readLengthPrefixedStringAt(int offset) { + return DebugVC50Impl.this.readLengthPrefixedStringAt(typeStringOffset + offset); + } + } + + private int numericLeafLengthAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException { + seek(absoluteOffset); + int leaf = readShort() & 0xFFFF; + if (leaf < 0x8000) return 2; + switch (leaf) { + case LF_CHAR: return 3; + case LF_SHORT: + case LF_USHORT: return 4; + case LF_LONG: + case LF_ULONG: return 6; + case LF_REAL32: return 6; + case LF_REAL64: return 10; + case LF_REAL80: return 12; + case LF_REAL128: return 18; + case LF_QUADWORD: + case LF_UQUADWORD: return 18; + case LF_REAL48: return 8; + case LF_COMPLEX32: return 10; + case LF_COMPLEX64: return 18; + case LF_COMPLEX80: return 26; + case LF_COMPLEX128: return 66; + // FIXME: figure out format of variable-length strings + case LF_VARSTRING: return 4 + readIntNumericLeafAt(absoluteOffset + 2); + + default: + throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf + + " at offset " + absoluteOffset); + } + } + + private int readIntNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException { + seek(absoluteOffset); + int leaf = readShort() & 0xFFFF; + if (leaf < 0x8000) return leaf; + switch (leaf) { + case LF_CHAR: return readByte() & 0xFF; + case LF_SHORT: + case LF_USHORT: return readShort() & 0xFFFF; + case LF_LONG: + case LF_ULONG: return readInt(); + + default: + throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf); + } + } + + private long readLongNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException { + seek(absoluteOffset); + int leaf = readShort() & 0xFFFF; + if (leaf < 0x8000) return leaf; + switch (leaf) { + case LF_CHAR: return readByte() & 0xFF; + case LF_SHORT: + case LF_USHORT: return readShort() & 0xFFFF; + case LF_LONG: + case LF_ULONG: return readInt() & 0xFFFFFFFF; + case LF_QUADWORD: + case LF_UQUADWORD: return readLong(); + + default: + throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf); + } + } + + private float readFloatNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException { + seek(absoluteOffset); + int leaf = readShort() & 0xFFFF; + if (leaf != LF_REAL32) { + throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf); + } + return readFloat(); + } + + private double readDoubleNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException { + seek(absoluteOffset); + int leaf = readShort() & 0xFFFF; + if (leaf != LF_REAL64) { + throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf); + } + return readDouble(); + } + + private int lengthPrefixedStringLengthAt(int absoluteOffset) { + // NOTE: the format of length-prefixed strings is not well + // specified. There is a LF_VARSTRING numeric leaf (the + // format of which is also not specified), but it seems that + // most length-prefixed strings are comprised of a single + // byte length followed by that many bytes of data. + seek(absoluteOffset); + int len = readByte() & 0xFF; + return 1 + len; + } + + private String readLengthPrefixedStringAt(int absoluteOffset) { + // NOTE: it isn't clear whether LF_VARSTRING numeric leaves + // ever show up, or in general what happens when the length + // of the string is > 255 (FIXME) + seek(absoluteOffset); + int len = readByte() & 0xFF; + byte[] res = new byte[len]; + int numRead = readBytes(res); + if (numRead != len) { + throw new COFFException("Error reading length prefixed string in symbol at offset " + + absoluteOffset); + } + try { + return new String(res, US_ASCII); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + } + + private int unbiasTypeIndex(int index) { + return index - 0x1000; + } + + private int biasTypeIndex(int index) { + return index + 0x1000; + } + } // Class DebugVC50Impl + + class SectionHeaderImpl implements SectionHeader { + private String name; + private int virtualSize; + private int virtualAddress; + private int sizeOfRawData; + private int pointerToRawData; + private int pointerToRelocations; + private int pointerToLineNumbers; + private short numberOfRelocations; + private short numberOfLineNumbers; + private int characteristics; + private MemoizedObject[] relocations; + private MemoizedObject[] lineNumbers; + + public SectionHeaderImpl(int offset) throws COFFException { + seek(offset); + + // FIXME: compute name lazily + + // Read name + byte[] tmpName = new byte[8]; + int numRead = readBytes(tmpName); + if (numRead != 8) { + throw new COFFException("Error reading name of section header at offset " + offset); + } + if (tmpName[0] == (byte) '/') { + // Long name; must find real value in string table + int index = 0; + try { + index = Integer.parseInt(new String(tmpName, 1, tmpName.length - 1, US_ASCII)); + } catch (NumberFormatException e) { + throw new COFFException("Error parsing string table index of name of section header " + + "at offset " + offset); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + // Look up in string table + name = getStringTable().get(index); + } else { + try { + name = new String(tmpName, US_ASCII); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + } + virtualSize = readInt(); + virtualAddress = readInt(); + sizeOfRawData = readInt(); + pointerToRawData = readInt(); + pointerToRelocations = readInt(); + pointerToLineNumbers = readInt(); + numberOfRelocations = readShort(); + numberOfLineNumbers = readShort(); + characteristics = readInt(); + + // Set up relocations + relocations = new MemoizedObject[numberOfRelocations]; + for (int i = 0; i < numberOfRelocations; i++) { + final int relocOffset = pointerToRelocations + i * RELOCATION_SIZE; + relocations[i] = new MemoizedObject() { + public Object computeValue() { + return new COFFRelocationImpl(relocOffset); + } + }; + } + + // Set up line numbers + lineNumbers = new MemoizedObject[numberOfLineNumbers]; + for (int i = 0; i < numberOfLineNumbers; i++) { + final int lineNoOffset = pointerToLineNumbers + i * LINE_NUMBER_SIZE; + lineNumbers[i] = new MemoizedObject() { + public Object computeValue() { + return new COFFLineNumberImpl(lineNoOffset); + } + }; + } + } + + public String getName() { return name; } + public int getSize() { return virtualSize; } + public int getVirtualAddress() { return virtualAddress; } + public int getSizeOfRawData() { return sizeOfRawData; } + public int getPointerToRawData() { return pointerToRawData; } + public int getPointerToRelocations() { return pointerToRelocations; } + public int getPointerToLineNumbers() { return pointerToLineNumbers; } + public short getNumberOfRelocations() { return numberOfRelocations; } + public short getNumberOfLineNumbers() { return numberOfLineNumbers; } + public int getSectionFlags() { return characteristics; } + public boolean hasSectionFlag(int flag ) { + return ((characteristics & flag) != 0); + } + public COFFRelocation getCOFFRelocation(int index) { + return (COFFRelocation) relocations[index].getValue(); + } + public COFFLineNumber getCOFFLineNumber(int index) { + return (COFFLineNumber) lineNumbers[index]; + } + } + + class COFFSymbolImpl implements COFFSymbol, COFFSymbolConstants { + private int offset; + private String name; + private int value; + private short sectionNumber; + private short type; + private byte storageClass; + private byte numberOfAuxSymbols; + private MemoizedObject auxFunctionDefinitionRecord = new MemoizedObject() { + public Object computeValue() { + return new AuxFunctionDefinitionRecordImpl(offset + SYMBOL_SIZE); + } + }; + private MemoizedObject auxBfEfRecord = new MemoizedObject() { + public Object computeValue() { + return new AuxBfEfRecordImpl(offset + SYMBOL_SIZE); + } + }; + private MemoizedObject auxWeakExternalRecord = new MemoizedObject() { + public Object computeValue() { + return new AuxWeakExternalRecordImpl(offset + SYMBOL_SIZE); + } + }; + private MemoizedObject auxFileRecord = new MemoizedObject() { + public Object computeValue() { + return new AuxFileRecordImpl(offset + SYMBOL_SIZE); + } + }; + private MemoizedObject auxSectionDefinitionsRecord = new MemoizedObject() { + public Object computeValue() { + return new AuxSectionDefinitionsRecordImpl(offset + SYMBOL_SIZE); + } + }; + + public COFFSymbolImpl(int offset) throws COFFException { + this.offset = offset; + seek(offset); + + // Parse name + byte[] tmpName = new byte[8]; + int numRead = readBytes(tmpName); + if (numRead != 8) { + throw new COFFException("Error reading name of symbol at offset " + offset); + } + if ((tmpName[0] == 0) && + (tmpName[1] == 0) && + (tmpName[2] == 0) && + (tmpName[3] == 0)) { + // It's an offset into the string table. + // FIXME: not sure about byte ordering... + int stringOffset = (tmpName[4] << 24 | + tmpName[5] << 16 | + tmpName[6] << 8 | + tmpName[7]); + name = getStringTable().getAtOffset(stringOffset); + } + + value = readInt(); + sectionNumber = readShort(); + type = readShort(); + storageClass = readByte(); + numberOfAuxSymbols = readByte(); + } + + public int getOffset() { return offset; } + public String getName() { return name; } + public int getValue() { return value; } + public short getSectionNumber() { return sectionNumber; } + public short getType() { return type; } + public byte getStorageClass() { return storageClass; } + public byte getNumberOfAuxSymbols() { return numberOfAuxSymbols; } + public boolean isFunctionDefinition() { + return ((getStorageClass() == IMAGE_SYM_CLASS_EXTERNAL) && + ((getType() >>> 8) == IMAGE_SYM_DTYPE_FUNCTION) && + (getSectionNumber() > 0)); + } + public AuxFunctionDefinitionRecord getAuxFunctionDefinitionRecord() { + return (AuxFunctionDefinitionRecord) auxFunctionDefinitionRecord.getValue(); + } + public boolean isBfOrEfSymbol() { + return ((getName().equals(".bf") || getName().equals(".ef")) && + (getStorageClass() == IMAGE_SYM_CLASS_FUNCTION)); + } + public AuxBfEfRecord getAuxBfEfRecord() { + return (AuxBfEfRecord) auxBfEfRecord.getValue(); + } + public boolean isWeakExternal() { + return ((getStorageClass() == IMAGE_SYM_CLASS_EXTERNAL) && + (getSectionNumber() == IMAGE_SYM_UNDEFINED) && + (getValue() == 0)); + } + public AuxWeakExternalRecord getAuxWeakExternalRecord() { + return (AuxWeakExternalRecord) auxWeakExternalRecord.getValue(); + } + public boolean isFile() { + return ((getName().equals(".file")) && + (getStorageClass() == IMAGE_SYM_CLASS_FILE)); + } + public AuxFileRecord getAuxFileRecord() { + return (AuxFileRecord) auxFileRecord.getValue(); + } + public boolean isSectionDefinition() { + // FIXME: not sure how to ensure that symbol name is the + // name of a section. + return ((getName().charAt(0) == '.') && + (getStorageClass() == IMAGE_SYM_CLASS_STATIC)); + } + public AuxSectionDefinitionsRecord getAuxSectionDefinitionsRecord() { + return (AuxSectionDefinitionsRecord) auxSectionDefinitionsRecord.getValue(); + } + } + + class AuxFunctionDefinitionRecordImpl implements AuxFunctionDefinitionRecord { + private int tagIndex; + private int totalSize; + private int pointerToLineNumber; + private int pointerToNextFunction; + + AuxFunctionDefinitionRecordImpl(int offset) { + seek(offset); + tagIndex = readInt(); + totalSize = readInt(); + // NOTE zero-basing of this index + pointerToLineNumber = readInt() - 1; + pointerToNextFunction = readInt(); + } + + public int getTagIndex() { return tagIndex; } + public int getTotalSize() { return totalSize; } + public int getPointerToLineNumber() { return pointerToLineNumber; } + public int getPointerToNextFunction() { return pointerToNextFunction; } + public int getType() { return FUNCTION_DEFINITION; } + } + + class AuxBfEfRecordImpl implements AuxBfEfRecord { + private short lineNumber; + private int pointerToNextFunction; + + AuxBfEfRecordImpl(int offset) { + seek(offset); + readInt(); + lineNumber = readShort(); + readInt(); + readShort(); + pointerToNextFunction = readInt(); + } + + public short getLineNumber() { return lineNumber; } + public int getPointerToNextFunction() { return pointerToNextFunction; } + public int getType() { return BF_EF_RECORD; } + } + + class AuxWeakExternalRecordImpl implements AuxWeakExternalRecord { + private int tagIndex; + private int characteristics; + + AuxWeakExternalRecordImpl(int offset) { + seek(offset); + tagIndex = readInt(); + characteristics = readInt(); + } + + public int getTagIndex() { return tagIndex; } + public int getCharacteristics() { return characteristics; } + public int getType() { return WEAK_EXTERNAL; } + } + + class AuxFileRecordImpl implements AuxFileRecord { + private String name; + + AuxFileRecordImpl(int offset) { + seek(offset); + byte[] tmpName = new byte[18]; + int numRead = readBytes(tmpName); + if (numRead != 18) { + throw new COFFException("Error reading auxiliary file record at offset " + offset); + } + try { + name = new String(tmpName, US_ASCII); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + } + + public String getName() { return name; } + public int getType() { return FILE; } + } + + class AuxSectionDefinitionsRecordImpl implements AuxSectionDefinitionsRecord { + private int length; + private short numberOfRelocations; + private short numberOfLineNumbers; + private int checkSum; + private short number; + private byte selection; + + AuxSectionDefinitionsRecordImpl(int offset) { + seek(offset); + length = readInt(); + numberOfRelocations = readShort(); + numberOfLineNumbers = readShort(); + checkSum = readInt(); + number = readShort(); + selection = readByte(); + } + + public int getLength() { return length; } + public short getNumberOfRelocations() { return numberOfRelocations; } + public short getNumberOfLineNumbers() { return numberOfLineNumbers; } + public int getCheckSum() { return checkSum; } + public short getNumber() { return number; } + public byte getSelection() { return selection; } + public int getType() { return SECTION_DEFINITION; } + } + + class COFFRelocationImpl implements COFFRelocation { + private int virtualAddress; + private int symbolTableIndex; + private short type; + + COFFRelocationImpl(int offset) { + seek(offset); + virtualAddress = readInt(); + symbolTableIndex = readInt(); + type = readShort(); + } + + public int getVirtualAddress() { return virtualAddress; } + public int getSymbolTableIndex() { return symbolTableIndex; } + public short getType() { return type; } + } + + class COFFLineNumberImpl implements COFFLineNumber { + private int type; + private short lineNumber; + + COFFLineNumberImpl(int offset) { + seek(offset); + type = readInt(); + lineNumber = readShort(); + } + + public int getType() { + return type; + } + + public short getLineNumber() { + return lineNumber; + } + } + + class StringTable { + class COFFString { + String str; + int offset; + + COFFString(String str, int offset) { + this.str = str; this.offset = offset; + } + } + + COFFString[] strings; + + StringTable(int offset) { + if (offset == 0) { + strings = new COFFString[0]; + return; + } + + seek(offset); + int length = readInt(); + byte[] data = new byte[length - 4]; + int numBytesRead = readBytes(data); + if (numBytesRead != data.length) { + throw new COFFException("Error reading string table (read " + + numBytesRead + " bytes, expected to read " + data.length + ")"); + } + int numStrings = 0; + int ptr = 0; + for (ptr = 0; ptr < data.length; ptr++) { + if (data[ptr] == 0) { + numStrings++; + } + } + strings = new COFFString[numStrings]; + int lastPtr = 0; + ptr = 0; + for (int i = 0; i < numStrings; i++) { + while (data[ptr] != 0) { + ptr++; + } + try { + strings[i] = new COFFString(new String(data, lastPtr, ptr - lastPtr, US_ASCII), + offset + ptr + 4); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + ptr++; + lastPtr = ptr; + } + } + + int getNum() { + return strings.length; + } + + String get(int i) { + return strings[i].str; + } + + /** This version takes an absolute offset in the file */ + String getAtOffset(int offset) { + int i = Arrays.binarySearch(strings, new COFFString(null, offset), + new Comparator() { + public int compare(Object o1, Object o2) { + COFFString s1 = (COFFString) o1; + COFFString s2 = (COFFString) o2; + if (s1.offset == s2.offset) { + return 0; + } else if (s1.offset < s2.offset) { + return -1; + } else { + return 1; + } + } + }); + if (i < 0) { + throw new COFFException("No string found at file offset " + offset); + } + return strings[i].str; + } + } + } + + void initialize() throws COFFException { + // Figure out whether this file is an object file or an image + // (either executable or DLL). + seek(0x3c); // Error here probably indicates file format error + try { + int peOffset = readInt(); + seek(peOffset); + if ((readByte() == (byte) 'P') && + (readByte() == (byte) 'E') && + (readByte() == (byte) 0) && + (readByte() == (byte) 0)) { + isImage = true; + imageHeaderOffset = getFilePointer(); + } + } + catch (COFFException e) { + // Expect failures here if not image file. + } + } + + byte readByteAt(long offset) throws COFFException { + seek(offset); + return readByte(); + } + + byte readByte() throws COFFException { + try { + return file.readByte(); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(filePos), e); + } + } + + int readBytesAt(long offset, byte[] b) throws COFFException { + seek(offset); + return readBytes(b); + } + + int readBytes(byte[] b) throws COFFException { + try { + return file.read(b); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(filePos), e); + } + } + + /** NOTE: reads little-endian short */ + short readShortAt(long offset) throws COFFException { + seek(offset); + return readShort(); + } + + /** NOTE: reads little-endian short */ + short readShort() throws COFFException { + try { + return byteSwap(file.readShort()); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(filePos), e); + } + } + + /** NOTE: reads little-endian int */ + int readIntAt(long offset) throws COFFException { + seek(offset); + return readInt(); + } + + /** NOTE: reads little-endian int */ + int readInt() throws COFFException { + try { + return byteSwap(file.readInt()); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(filePos), e); + } + } + + /** NOTE: reads little-endian long */ + long readLongAt(long offset) throws COFFException { + seek(offset); + return readLong(); + } + + /** NOTE: reads little-endian long */ + long readLong() throws COFFException { + try { + return byteSwap(file.readLong()); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(filePos), e); + } + } + + /** NOTE: reads little-endian float */ + float readFloat() throws COFFException { + int i = readInt(); + return Float.intBitsToFloat(i); + } + + /** NOTE: reads little-endian double */ + double readDouble() throws COFFException { + long l = readLong(); + return Double.longBitsToDouble(l); + } + + String readCString() throws COFFException { + List data = new ArrayList(); + byte b = 0; + while ((b = readByte()) != 0) { + data.add(new Byte(b)); + } + byte[] bytes = new byte[data.size()]; + for (int i = 0; i < data.size(); i++) { + bytes[i] = ((Byte) data.get(i)).byteValue(); + } + try { + return new String(bytes, US_ASCII); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + } + + void seek(long offset) throws COFFException { + try { + filePos = offset; + file.seek(offset); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(offset), e); + } + } + + long getFilePointer() throws COFFException { + try { + return file.getFilePointer(); + } catch (IOException e) { + throw new COFFException(e); + } + } + + short byteSwap(short arg) { + return (short) ((arg << 8) | ((arg >>> 8) & 0xFF)); + } + + int byteSwap(int arg) { + return (((int) byteSwap((short) arg)) << 16) | (((int) (byteSwap((short) (arg >>> 16)))) & 0xFFFF); + } + + long byteSwap(long arg) { + return ((((long) byteSwap((int) arg)) << 32) | (((long) byteSwap((int) (arg >>> 32))) & 0xFFFFFFFF)); + } + + public void close() throws COFFException { + try { + file.close(); + } catch (IOException e) { + throw new COFFException(e); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFHeader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFHeader.java new file mode 100644 index 00000000000..692ac37764b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFHeader.java @@ -0,0 +1,85 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the information stored in the COFF header of either a + Portable Executable or object file. */ + +public interface COFFHeader { + /** Returns one of the constants in {@link + sun.jvm.hotspot.debugger.win32.coff.MachineTypes}. */ + public short getMachineType(); + + /** Number of sections; indicates size of the Section Table, which + immediately follows the headers. */ + public short getNumberOfSections(); + + /** Time and date the file was created. */ + public int getTimeDateStamp(); + + /** File offset of the COFF symbol table or 0 if none is present. */ + public int getPointerToSymbolTable(); + + /** Number of entries in the symbol table. This data can be used in + locating the string table, which immediately follows the symbol + table. */ + public int getNumberOfSymbols(); + + /** Size of the optional header, which is required for executable + files but not for object files. An object file should have a + value of 0 here. */ + public short getSizeOfOptionalHeader(); + + /** Returns the optional header if one is present or null if not. */ + public OptionalHeader getOptionalHeader() throws COFFException; + + /** Gets the union of all characteristics set for this object or + image file. See {@link + sun.jvm.hotspot.debugger.win32.coff.Characteristics}. */ + public short getCharacteristics(); + + /** Indicates whether this file has the given characteristic. The + argument must be one of the constants specified in {@link + sun.jvm.hotspot.debugger.win32.coff.Characteristics}. */ + public boolean hasCharacteristic(short characteristic); + + /** Retrieves the section header at the given index, between + 1 and getNumberOfSections(). NOTE: This index is one-based, + so the first section is numbered one, not zero. */ + public SectionHeader getSectionHeader(int index); + + /** Retrieves the COFF symbol at the given index, between 0 and + getNumberOfSymbols() - 1. This is distinct from CodeView + information. */ + public COFFSymbol getCOFFSymbol(int index); + + /** Returns the number of strings in the String Table, which + immediately follows the COFF Symbol Table. */ + public int getNumberOfStrings(); + + /** Retrieves the ith string (0..{@link #getNumberOfStrings} - 1) + from the string table. */ + public String getString(int i); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFLineNumber.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFLineNumber.java new file mode 100644 index 00000000000..8c0012b1df8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFLineNumber.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes a COFF line number. (Some of the descriptions are taken + directly from Microsoft's documentation and are copyrighted by + Microsoft.) */ + +public interface COFFLineNumber { + /**

Union of two fields: Symbol Table Index and RVA. Whether + Symbol Table Index or RVA is used depends on the value of + getLineNumber().

+ +

SymbolTableIndex is used when getLineNumber() is 0: index to + symbol table entry for a function. This format is used to + indicate the function that a group of line-number records refer + to.

+ +

VirtualAddress is used when LineNumber is non-zero: relative + virtual address of the executable code that corresponds to the + source line indicated. In an object file, this contains the + virtual address within the section.

*/ + public int getType(); + + /** When nonzero, this field specifies a one-based line number. When + zero, the Type field is interpreted as a Symbol Table Index for + a function. */ + public short getLineNumber(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFRelocation.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFRelocation.java new file mode 100644 index 00000000000..d472846cc75 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFRelocation.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes a COFF relocation, only present in an object file. (Some + of the descriptions are taken directly from Microsoft's + documentation and are copyrighted by Microsoft.) */ + +public interface COFFRelocation { + /** Address of the item to which relocation is applied: this is the + offset from the beginning of the section, plus the value of the + section's RVA/Offset field (see {@link + sun.jvm.hotspot.debugger.win32.coff.SectionHeader}.) For example, if + the first byte of the section has an address of 0x10, the third + byte has an address of 0x12. */ + public int getVirtualAddress(); + + /** A zero-based index into the symbol table. This symbol gives the + address to be used for the relocation. If the specified symbol + has section storage class, then the symbol's address is the + address with the first section of the same name. */ + public int getSymbolTableIndex(); + + /** A value indicating what kind of relocation should be + performed. Valid relocation types depend on machine type. See + {@link sun.jvm.hotspot.debugger.win32.coff.TypeIndicators}. */ + public short getType(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFSymbol.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFSymbol.java new file mode 100644 index 00000000000..30bfa8dcdd7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFSymbol.java @@ -0,0 +1,139 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes a COFF symbol. (Some of the descriptions are taken + directly from Microsoft's documentation and are copyrighted by + Microsoft.) */ + +public interface COFFSymbol { + /** Offset within the file of this record. (FIXME: Now that we have + the auxiliary records exposed, it may not be necessary to expose + this.) */ + public int getOffset(); + + public String getName(); + + /** Value associated with the symbol. The interpretation of this + field depends on Section Number and Storage Class. A typical + meaning is the relocatable address. */ + public int getValue(); + + /** Signed integer identifying the section, using a one-based index + into the Section Table. Some values have special meaning defined + in {@link sun.jvm.hotspot.debugger.win32.coff.COFFSymbolConstants}. */ + public short getSectionNumber(); + + /**

The Type field of a symbol table entry contains two bytes, + each byte representing type information. The least-significant + byte represents simple (base) data type, and the + most-significant byte represents complex type, if any:

+ +

MSB: Complex type: none, pointer, function, array.

+ +

LSB: Base type: integer, floating-point, etc.

+ +

The possible base type values are listed in {@link + sun.jvm.hotspot.debugger.win32.coff.COFFSymbolConstants} under the + IMAGE_SYM_TYPE constants.

+ +

The most significant byte specifies whether the symbol is a + pointer to, function returning, or array of the base type + specified in the least significant byte. Microsoft tools use + this field only to indicate whether or not the symbol is a + function, so that the only two resulting values are 0x0 and 0x20 + for the Type field. However, other tools can use this field to + communicate more information.

+ +

It is very important to specify the function attribute + correctly. This information is required for incremental linking + to work correctly. For some architectures the information may be + required for other purposes.

+ +

The possible function types are listed in {@link + sun.jvm.hotspot.debugger.win32.coff.COFFSymbolConstants} under the + IMAGE_SYM_DTYPE constants.

*/ + public short getType(); + + /** Enumerated value representing storage class. See {@link + sun.jvm.hotspot.debugger.win32.coff.COFFSymbolConstants} under the + IMAGE_SYM_CLASS constants. */ + public byte getStorageClass(); + + /** Number of auxiliary symbol table entries that follow this + record. (FIXME: the APIs below which fetch such an auxiliary + symbol are only currently capable of fetching the first one.) */ + public byte getNumberOfAuxSymbols(); + + /** Indicates whether this symbol is a function definition: storage + class EXTERNAL (2), a Type value indicating it is a function + (0x20), and a section number greater than zero. This indicates + that the function is followed by an {@link + sun.jvm.hotspot.debugger.win32.coff.AuxFunctionDefinitionRecord}. + Note that a symbol table record that has a section number of + UNDEFINED (0) does not define the function and does not have an + auxiliary record. */ + public boolean isFunctionDefinition(); + + /** This should only be called if {@link #isFunctionDefinition} + returns true. */ + public AuxFunctionDefinitionRecord getAuxFunctionDefinitionRecord(); + + /** Indicates whether this symbol is a .bf or .ef symbol record and + is therefore followed by an {@link + sun.jvm.hotspot.debugger.win32.coff.AuxBfEfRecord}. */ + public boolean isBfOrEfSymbol(); + + /** This should only be called if {@link #isBfOrEfSymbol} returns + true. */ + public AuxBfEfRecord getAuxBfEfRecord(); + + /** Indicates whether this symbol is a weak external and is + therefore followed by an {@link + sun.jvm.hotspot.debugger.win32.coff.AuxWeakExternalRecord}. */ + public boolean isWeakExternal(); + + /** This should only be called if {@link #isWeakExternal} returns + true. */ + public AuxWeakExternalRecord getAuxWeakExternalRecord(); + + /** Indicates whether this symbol has storage class FILE and is + therefore followed by a {@link + sun.jvm.hotspot.debugger.win32.coff.AuxFileRecord}. */ + public boolean isFile(); + + /** This should only be called if {@link #isFile} returns + true. */ + public AuxFileRecord getAuxFileRecord(); + + /** Indicates whether this symbol defines a section and is therefore + followed by an {@link + sun.jvm.hotspot.debugger.win32.coff.AuxSectionDefinitionsRecord}. */ + public boolean isSectionDefinition(); + + /** This should only be called if {@link #isSectionDefinition} returns + true. */ + public AuxSectionDefinitionsRecord getAuxSectionDefinitionsRecord(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFSymbolConstants.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFSymbolConstants.java new file mode 100644 index 00000000000..711dc8a8bd5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFSymbolConstants.java @@ -0,0 +1,170 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Constants providing information about the section number, type + representation, and storage class of symbols. (Some of the + descriptions are taken directly from Microsoft's documentation and + are copyrighted by Microsoft.) */ + +public interface COFFSymbolConstants { + // + // Section Number Values + // + + /** Symbol record is not yet assigned a section. If the value is 0 + this indicates a references to an external symbol defined + elsewhere. If the value is non-zero this is a common symbol with + a size specified by the value. */ + public static final short IMAGE_SYM_UNDEFINED = (short) 0; + /** The symbol has an absolute (non-relocatable) value and is not an + address. */ + public static final short IMAGE_SYM_ABSOLUTE = (short) -1; + /** The symbol provides general type or debugging information but + does not correspond to a section. Microsoft tools use this + setting along with .file records (storage class FILE). */ + public static final short IMAGE_SYM_DEBUG = (short) -2; + + // + // Type Representation + // + + /** No type information or unknown base type. Microsoft tools use + this setting. */ + public static final short IMAGE_SYM_TYPE_NULL = (short) 0; + /** No valid type; used with void pointers and functions. */ + public static final short IMAGE_SYM_TYPE_VOID = (short) 1; + /** Character (signed byte). */ + public static final short IMAGE_SYM_TYPE_CHAR = (short) 2; + /** Two-byte signed integer. */ + public static final short IMAGE_SYM_TYPE_SHORT = (short) 3; + /** Natural integer type (normally four bytes in Windows NT). */ + public static final short IMAGE_SYM_TYPE_INT = (short) 4; + /** Four-byte signed integer. */ + public static final short IMAGE_SYM_TYPE_LONG = (short) 5; + /** Four-byte floating-point number. */ + public static final short IMAGE_SYM_TYPE_FLOAT = (short) 6; + /** Eight-byte floating-point number. */ + public static final short IMAGE_SYM_TYPE_DOUBLE = (short) 7; + /** Structure. */ + public static final short IMAGE_SYM_TYPE_STRUCT = (short) 8; + /** Union. */ + public static final short IMAGE_SYM_TYPE_UNION = (short) 9; + /** Enumerated type. */ + public static final short IMAGE_SYM_TYPE_ENUM = (short) 10; + /** Member of enumeration (a specific value). */ + public static final short IMAGE_SYM_TYPE_MOE = (short) 11; + /** Byte; unsigned one-byte integer. */ + public static final short IMAGE_SYM_TYPE_BYTE = (short) 12; + /** Word; unsigned two-byte integer. */ + public static final short IMAGE_SYM_TYPE_WORD = (short) 13; + /** Unsigned integer of natural size (normally, four bytes). */ + public static final short IMAGE_SYM_TYPE_UINT = (short) 14; + /** Unsigned four-byte integer. */ + public static final short IMAGE_SYM_TYPE_DWORD = (short) 15; + + /** No derived type; the symbol is a simple scalar variable. */ + public static final short IMAGE_SYM_DTYPE_NULL = (short) 0; + /** Pointer to base type. */ + public static final short IMAGE_SYM_DTYPE_POINTER = (short) 1; + /** Function returning base type. */ + public static final short IMAGE_SYM_DTYPE_FUNCTION = (short) 2; + /** Array of base type. */ + public static final short IMAGE_SYM_DTYPE_ARRAY = (short) 3; + + // + // Storage Class + // + + /** (0xFF) Special symbol representing end of function, for + debugging purposes. */ + public static final byte IMAGE_SYM_CLASS_END_OF_FUNCTION = (byte) -1; + /** No storage class assigned. */ + public static final byte IMAGE_SYM_CLASS_NULL = (byte) 0; + /** Automatic (stack) variable. The Value field specifies stack + frame offset. */ + public static final byte IMAGE_SYM_CLASS_AUTOMATIC = (byte) 1; + /** Used by Microsoft tools for external symbols. The Value field + indicates the size if the section number is IMAGE_SYM_UNDEFINED + (0). If the section number is not 0, then the Value field + specifies the offset within the section. */ + public static final byte IMAGE_SYM_CLASS_EXTERNAL = 2; + /** The Value field specifies the offset of the symbol within the + section. If the Value is 0, then the symbol represents a section + name. */ + public static final byte IMAGE_SYM_CLASS_STATIC = (byte) 3; + /** Register variable. The Value field specifies register number. */ + public static final byte IMAGE_SYM_CLASS_REGISTER = (byte) 4; + /** Symbol is defined externally. */ + public static final byte IMAGE_SYM_CLASS_EXTERNAL_DEF = (byte) 5; + /** Code label defined within the module. The Value field specifies + the offset of the symbol within the section. */ + public static final byte IMAGE_SYM_CLASS_LABEL = (byte) 6; + /** Reference to a code label not defined. */ + public static final byte IMAGE_SYM_CLASS_UNDEFINED_LABEL = (byte) 7; + /** Structure member. The Value field specifies nth member. */ + public static final byte IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = (byte) 8; + /** Formal argument (parameter) of a function. The Value field + specifies nth argument. */ + public static final byte IMAGE_SYM_CLASS_ARGUMENT = (byte) 9; + /** Structure tag-name entry. */ + public static final byte IMAGE_SYM_CLASS_STRUCT_TAG = (byte) 10; + /** Union member. The Value field specifies nth member. */ + public static final byte IMAGE_SYM_CLASS_MEMBER_OF_UNION = (byte) 11; + /** Union tag-name entry. */ + public static final byte IMAGE_SYM_CLASS_UNION_TAG = (byte) 12; + /** Typedef entry. */ + public static final byte IMAGE_SYM_CLASS_TYPE_DEFINITION = (byte) 13; + /** Static data declaration. */ + public static final byte IMAGE_SYM_CLASS_UNDEFINED_STATIC = (byte) 14; + /** Enumerated type tagname entry. */ + public static final byte IMAGE_SYM_CLASS_ENUM_TAG = (byte) 15; + /** Member of enumeration. Value specifies nth member. */ + public static final byte IMAGE_SYM_CLASS_MEMBER_OF_ENUM = (byte) 16; + /** Register parameter. */ + public static final byte IMAGE_SYM_CLASS_REGISTER_PARAM = (byte) 17; + /** Bit-field reference. Value specifies nth bit in the bit field. */ + public static final byte IMAGE_SYM_CLASS_BIT_FIELD = (byte) 18; + /** A .bb (beginning of block) or .eb (end of block) record. Value + is the relocatable address of the code location. */ + public static final byte IMAGE_SYM_CLASS_BLOCK = (byte) 100; + /** Used by Microsoft tools for symbol records that define the + extent of a function: begin function (named .bf), end function + (.ef), and lines in function (.lf). For .lf records, Value gives + the number of source lines in the function. For .ef records, + Value gives the size of function code. */ + public static final byte IMAGE_SYM_CLASS_FUNCTION = (byte) 101; + /** End of structure entry. */ + public static final byte IMAGE_SYM_CLASS_END_OF_STRUCT = (byte) 102; + /** Used by Microsoft tools, as well as traditional COFF format, for + the source-file symbol record. The symbol is followed by + auxiliary records that name the file. */ + public static final byte IMAGE_SYM_CLASS_FILE = (byte) 103; + /** Definition of a section (Microsoft tools use STATIC storage + class instead). */ + public static final byte IMAGE_SYM_CLASS_SECTION = (byte) 104; + /** Weak external. */ + public static final byte IMAGE_SYM_CLASS_WEAK_EXTERNAL = (byte) 105; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COMDATSelectionTypes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COMDATSelectionTypes.java new file mode 100644 index 00000000000..9063ebacf34 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COMDATSelectionTypes.java @@ -0,0 +1,57 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Enumerates the COMDAT selection types. One of these is returned + from {@link + sun.jvm.hotspot.debugger.win32.coff.AuxSectionDefinitionsRecord#getSelection()}. */ + +public interface COMDATSelectionTypes { + /** The linker issues a multiply defined symbol error if this symbol + is already defined. */ + public static final byte IMAGE_COMDAT_SELECT_NODUPLICATES = 1; + /** Any section defining the same COMDAT symbol may be linked; the + rest are removed. */ + public static final byte IMAGE_COMDAT_SELECT_ANY = 2; + /** The linker chooses an arbitrary section among the definitions + for this symbol. A multiply defined symbol error is issued if + all definitions don't have the same size. */ + public static final byte IMAGE_COMDAT_SELECT_SAME_SIZE = 3; + /** The linker chooses an arbitrary section among the definitions + for this symbol. A multiply defined symbol error is issued if + all definitions don't match exactly. */ + public static final byte IMAGE_COMDAT_SELECT_EXACT_MATCH = 4; + /** The section is linked if a certain other COMDAT section is + linked. This other section is indicated by the Number field of + the auxiliary symbol record for the section definition. Use of + this setting is useful for definitions that have components in + multiple sections (for example, code in one and data in + another), but where all must be linked or discarded as a set. */ + public static final byte IMAGE_COMDAT_SELECT_ASSOCIATIVE = 5; + /** The linker chooses the largest from the definitions for this + symbol. If multiple definitions have this size the choice + between them is arbitrary. */ + public static final byte IMAGE_COMDAT_SELECT_LARGEST = 6; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/Characteristics.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/Characteristics.java new file mode 100644 index 00000000000..da6827bf190 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/Characteristics.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Constants indicating attributes of the object or image file. (Some + of the descriptions are taken directly from Microsoft's + documentation and are copyrighted by Microsoft.) */ + +public interface Characteristics { + /** Image only, Windows CE, Windows NT and above. Indicates that the + file does not contain base relocations and must therefore be + loaded at its preferred base address. If the base address is not + available, the loader reports an error. Operating systems + running on top of MS-DOS (Win32s) are generally not able to use + the preferred base address and so cannot run these + images. However, beginning with version 4.0, Windows will use an + application's preferred base address. The default behavior of + the linker is to strip base relocations from EXEs. */ + public static final short IMAGE_FILE_RELOCS_STRIPPED = (short) 0x0001; + + /** Image only. Indicates that the image file is valid and can be + run. If this flag is not set, it generally indicates a linker + error. */ + public static final short IMAGE_FILE_EXECUTABLE_IMAGE = (short) 0x0002; + + /** COFF line numbers have been removed. */ + public static final short IMAGE_FILE_LINE_NUMS_STRIPPED = (short) 0x0004; + + /** COFF symbol table entries for local symbols have been removed. */ + public static final short IMAGE_FILE_LOCAL_SYMS_STRIPPED = (short) 0x0008; + + /** Aggressively trim working set. */ + public static final short IMAGE_FILE_AGGRESSIVE_WS_TRIM = (short) 0x0010; + + /** App can handle > 2gb addresses. */ + public static final short IMAGE_FILE_LARGE_ADDRESS_AWARE = (short) 0x0020; + + /** Use of this flag is reserved for future use. */ + public static final short IMAGE_FILE_16BIT_MACHINE = (short) 0x0040; + + /** Little endian: LSB precedes MSB in memory. */ + public static final short IMAGE_FILE_BYTES_REVERSED_LO = (short) 0x0080; + + /** Machine based on 32-bit-word architecture. */ + public static final short IMAGE_FILE_32BIT_MACHINE = (short) 0x0100; + + /** Debugging information removed from image file. */ + public static final short IMAGE_FILE_DEBUG_STRIPPED = (short) 0x0200; + + /** If image is on removable media, copy and run from swap file. */ + public static final short IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = (short) 0x0400; + + /** The image file is a system file, not a user program. */ + public static final short IMAGE_FILE_SYSTEM = (short) 0x1000; + + /** The image file is a dynamic-link library (DLL). Such files are + considered executable files for almost all purposes, although + they cannot be directly run. */ + public static final short IMAGE_FILE_DLL = (short) 0x2000; + + /** File should be run only on a UP machine. */ + public static final short IMAGE_FILE_UP_SYSTEM_ONLY = (short) 0x4000; + + /** Big endian: MSB precedes LSB in memory. */ + public static final short IMAGE_FILE_BYTES_REVERSED_HI = (short) 0x8000; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DLLCharacteristics.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DLLCharacteristics.java new file mode 100644 index 00000000000..91f0a824b95 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DLLCharacteristics.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Constants enumerating characteristics of a DLL; see {@link + sun.jvm.hotspot.debugger.win32.coff.OptionalHeaderWindowsSpecificFields#getDLLCharacteristics}. + (Some of the descriptions are taken directly from Microsoft's + documentation and are copyrighted by Microsoft.) */ + +public interface DLLCharacteristics { + /** Do not bind image */ + public short IMAGE_DLLCHARACTERISTICS_NO_BIND = (short) 0x0800; + /** Driver is a WDM Driver */ + public short IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = (short) 0x2000; + /** Image is Terminal Server aware */ + public short IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = (short) 0x8000; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DataDirectory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DataDirectory.java new file mode 100644 index 00000000000..10383cc6e60 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DataDirectory.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the information stored in one of the {@link + sun.jvm.hotspot.debugger.win32.coff.OptionalHeaderDataDirectories}. (Some + of the descriptions are taken directly from Microsoft's + documentation and are copyrighted by Microsoft.) */ + +public interface DataDirectory { + /** The relative virtual address of the table. The RVA is the + address of the table, when loaded, relative to the base address + of the image. */ + public int getRVA(); + + /** The size in bytes of this directory. */ + public int getSize(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugDirectory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugDirectory.java new file mode 100644 index 00000000000..3f0f4a9321c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugDirectory.java @@ -0,0 +1,39 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the Debug Directory, which is an entry in the image + optional header. This directory indicates where in the image the + CodeView debug information appears, if enabled using /Z7 + /PDB:NONE. (Some of the descriptions are taken directly from + Microsoft's documentation and are copyrighted by Microsoft.) */ + +public interface DebugDirectory { + /** Number of entries in the directory */ + public int getNumEntries(); + + /** Fetch the ith entry (0..getNumEntries() - 1) */ + public DebugDirectoryEntry getEntry(int i); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugDirectoryEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugDirectoryEntry.java new file mode 100644 index 00000000000..645fe10976d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugDirectoryEntry.java @@ -0,0 +1,69 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models an entry in the Debug Directory, which is an entry in the + image optional header. This directory indicates where in the image + the CodeView debug information appears, if enabled using /Z7 + /PDB:NONE. (Some of the descriptions are taken directly from + Microsoft's documentation and are copyrighted by Microsoft.) */ + +public interface DebugDirectoryEntry { + /** A reserved field intended to be used for flags, set to zero for + now. */ + public int getCharacteristics(); + + /** Time and date the debug data was created. */ + public int getTimeDateStamp(); + + /** Major version number of the debug data format. */ + public short getMajorVersion(); + + /** Minor version number of the debug data format. */ + public short getMinorVersion(); + + /** Format of debugging information: this field enables support of + multiple debuggers. See + @link{sun.jvm.hotspot.debugger.win32.coff.DebugTypes}. */ + public int getType(); + + /** Size of the debug data (not including the debug directory itself). */ + public int getSizeOfData(); + + /** Address of the debug data when loaded, relative to the image base. */ + public int getAddressOfRawData(); + + /** File pointer to the debug data. */ + public int getPointerToRawData(); + + /** If this debug directory entry is of type + IMAGE_DEBUG_TYPE_CODEVIEW (see + @link{sun.jvm.hotspot.debugger.win32.coff.DebugTypes}), returns + the contents as a DebugVC50 object; otherwise, returns null. */ + public DebugVC50 getDebugVC50(); + + /** Placeholder */ + public byte getRawDataByte(int i); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugTypes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugTypes.java new file mode 100644 index 00000000000..a0433d28929 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugTypes.java @@ -0,0 +1,57 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Constants indicating debug information types in the + DebugDirectory. (Some of the descriptions are taken directly from + Microsoft's documentation and are copyrighted by Microsoft.) */ + +public interface DebugTypes { + + /** Unknown value, ignored by all tools. */ + public static final int IMAGE_DEBUG_TYPE_UNKNOWN = 0; + + /** COFF debug information (line numbers, symbol table, and string + table). This type of debug information is also pointed to by + fields in the file headers. */ + public static final int IMAGE_DEBUG_TYPE_COFF = 1; + + /** CodeView debug information. The format of the data block is + described by the CV4 specification. */ + public static final int IMAGE_DEBUG_TYPE_CODEVIEW = 2; + + /** Frame Pointer Omission (FPO) information. This information tells + the debugger how to interpret non-standard stack frames, which + use the EBP register for a purpose other than as a frame + pointer. */ + public static final int IMAGE_DEBUG_TYPE_FPO = 3; + + public static final int IMAGE_DEBUG_TYPE_MISC = 4; + public static final int IMAGE_DEBUG_TYPE_EXCEPTION = 5; + public static final int IMAGE_DEBUG_TYPE_FIXUP = 6; + public static final int IMAGE_DEBUG_TYPE_OMAP_TO_SRC = 7; + public static final int IMAGE_DEBUG_TYPE_OMAP_FROM_SRC = 8; + public static final int IMAGE_DEBUG_TYPE_BORLAND = 9; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50.java new file mode 100644 index 00000000000..921edfb16da --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50.java @@ -0,0 +1,33 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models debug information in Visual C++ 5.0 format. */ + +public interface DebugVC50 { + public int getSubsectionDirectoryOffset(); + + public DebugVC50SubsectionDirectory getSubsectionDirectory(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50MemberAttributes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50MemberAttributes.java new file mode 100644 index 00000000000..4eb4a6d8bbc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50MemberAttributes.java @@ -0,0 +1,68 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Member attributes used to describe fields and methods, represented + as a 16-bit bit field. */ + +public interface DebugVC50MemberAttributes { + /** Access protection of the item */ + // FIXME: verify these are correct (properly aligned) + public static final short MEMATTR_ACCESS_MASK = (short) 0x0003; + public static final short MEMATTR_ACCESS_NO_PROTECTION = (short) 0; + public static final short MEMATTR_ACCESS_PRIVATE = (short) 1; + public static final short MEMATTR_ACCESS_PROTECTED = (short) 2; + public static final short MEMATTR_ACCESS_PUBLIC = (short) 3; + + /** Method attribute bit field for various type records */ + // FIXME: verify these are correct (properly aligned) + public static final short MEMATTR_MPROP_MASK = (short) 0x001C; + // 00000 + public static final short MEMATTR_MPROP_VANILLA = (short) 0x0000; + // 00100 + public static final short MEMATTR_MPROP_VIRTUAL = (short) 0x0004; + // 01000 + public static final short MEMATTR_MPROP_STATIC = (short) 0x0008; + // 01100 + public static final short MEMATTR_MPROP_FRIEND = (short) 0x000C; + // 10000 + public static final short MEMATTR_MPROP_INTRODUCING_VIRTUAL = (short) 0x0010; + // 10100 + public static final short MEMATTR_MPROP_PURE_VIRTUAL = (short) 0x0014; + // 11000 + public static final short MEMATTR_MPROP_PURE_INTRODUCING_VIRTUAL = (short) 0x0018; + + /** Set if the method is never instantiated by the compiler */ + public static final short MEMATTR_PSEUDO_MASK = (short) 0x0020; + + /** Set if the class cannot be inherited */ + public static final short MEMATTR_NOINHERIT_MASK = (short) 0x0040; + + /** Set if the class cannot be constructed */ + public static final short MEMATTR_NOCONSTRUCT_MASK = (short) 0x0080; + + /** Set if the method is instantiated by the compiler */ + public static final short MEMATTR_COMPGENX_MASK = (short) 0x0100; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50ReservedTypes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50ReservedTypes.java new file mode 100644 index 00000000000..b99bee37fbf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50ReservedTypes.java @@ -0,0 +1,860 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/**

Enumerates the reserved types referenced in the $$TYPES section + (see {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SSGlobalTypes}). (Some + of the descriptions are taken directly from Microsoft's + documentation and are copyrighted by Microsoft.)

+ +

These values are interpreted as bit fields with the following + meanings: + border="1" width = "50%" +
11 10 - 8 7 - 4 3 2 - 0 +
reserved mode type reserved size +
+

+ +

+ +
type Type +
0x00 Special +
0x01 Signed integral value +
0x02 Unsigned integral value +
0x03 Boolean +
0x04 Real +
0x05 Complex +
0x06 Special2 +
0x07 Really int value +
0x08 Reserved +
0x09 Reserved +
0x0a Reserved +
0x0b Reserved +
0x0c Reserved +
0x0d Reserved +
0x0e Reserved +
0x0f Reserved for CodeView expression evaluator use +
+ +

+ +
size Enumerated value for each of the types +
Type = special: +
0x00 No type +
0x01 Absolute symbol +
0x02 Segment +
0x03 Void +
0x04 Basic 8-byte currency value +
0x05 Near Basic string +
0x06 Far Basic string +
0x07 Untranslated type from CV 3.x format +
Type = signed/unsigned integral and Boolean values: +
0x00 1 byte +
0x01 2 byte +
0x02 4 byte +
0x03 8 byte +
0x04 Reserved +
0x05 Reserved +
0x06 Reserved +
0x07 Reserved +
Type = real and complex: +
0x00 32 bit +
0x01 64 bit +
0x02 80 bit +
0x03 128 bit +
0x04 48 bit +
0x05 Reserved +
0x06 Reserved +
0x07 Reserved +
Type = special2: +
0x00 Bit +
0x01 Pascal CHAR +
Type = Really int: +
0x00 Char +
0x01 Wide character +
0x02 2 byte signed integer +
0x03 2 byte unsigned integer +
0x04 4 byte signed integer +
0x05 4 byte unsigned integer +
0x06 8 byte signed integer +
0x07 8 byte unsigned integer +
+

+ +

+ +
mode Mode +
0x00 Direct; not a pointer +
0x01 Near pointer +
0x02 Far pointer +
0x03 Huge pointer +
0x04 32 bit near pointer +
0x05 32 bit far pointer +
0x06 64 bit near pointer +
0x07 Reserved +
+

+*/ + +public interface DebugVC50ReservedTypes { + + // + // Special types + // + + /** Uncharacterized type (no type) */ + public static final int T_NOTYPE = 0x0000; + + /** Absolute symbol */ + public static final int T_ABS = 0x0001; + + /** Segment type */ + public static final int T_SEGMENT = 0x0002; + + /** Void */ + public static final int T_VOID = 0x0003; + + /** Near pointer to void */ + public static final int T_PVOID = 0x0103; + + /** Far pointer to void */ + public static final int T_PFVOID = 0x0203; + + /** Huge pointer to void */ + public static final int T_PHVOID = 0x0303; + + /** 32 bit near pointer to void */ + public static final int T_32PVOID = 0x0403; + + /** 32 bit far pointer to void */ + public static final int T_32PFVOID = 0x0503; + + /** 64 bit pointer to void */ + public static final int T_64PVOID = 0x0603; + + /** Basic 8 byte currency value */ + public static final int T_CURRENCY = 0x0004; + + /** Near Basic string */ + public static final int T_NBASICSTR = 0x0005; + + /** Far Basic string */ + public static final int T_FBASICSTR = 0x0006; + + /** Untranslated type record from CV 3.x format */ + public static final int T_NOTTRANS = 0x0007; + + /** Bit */ + public static final int T_BIT = 0x0060; + + /** Pascal CHAR */ + public static final int T_PASCHAR = 0x0061; + + // + // Character types + // + + /** 8-bit signed */ + public static final int T_CHAR = 0x0010; + + /** 8-bit unsigned */ + public static final int T_UCHAR = 0x0020; + + /** Near pointer to 8-bit signed */ + public static final int T_PCHAR = 0x0110; + + /** Near pointer to 8-bit unsigned */ + public static final int T_PUCHAR = 0x0120; + + /** Far pointer to 8-bit signed */ + public static final int T_PFCHAR = 0x0210; + + /** Far pointer to 8-bit unsigned */ + public static final int T_PFUCHAR = 0x0220; + + /** Huge pointer to 8-bit signed */ + public static final int T_PHCHAR = 0x0310; + + /** Huge pointer to 8-bit unsigned */ + public static final int T_PHUCHAR = 0x0320; + + /** 16:32 near pointer to 8-bit signed */ + public static final int T_32PCHAR = 0x0410; + + /** 16:32 near pointer to 8-bit unsigned */ + public static final int T_32PUCHAR = 0x0420; + + /** 16:32 far pointer to 8-bit signed */ + public static final int T_32PFCHAR = 0x0510; + + /** 16:32 far pointer to 8-bit unsigned */ + public static final int T_32PFUCHAR = 0x0520; + + /** 64 bit pointer to 8 bit signed */ + public static final int T_64PCHAR = 0x0610; + + /** 64 bit pointer to 8 bit unsigned */ + public static final int T_64PUCHAR = 0x0620; + + // + // Really a character types + // + + /** real char */ + public static final int T_RCHAR = 0x0070; + + /** near pointer to a real char */ + public static final int T_PRCHAR = 0x0170; + + /** far pointer to a real char */ + public static final int T_PFRCHAR = 0x0270; + + /** huge pointer to a real char */ + public static final int T_PHRCHAR = 0x0370; + + /** 16:32 near pointer to a real char */ + public static final int T_32PRCHAR = 0x0470; + + /** 16:32 far pointer to a real char */ + public static final int T_32PFRCHAR = 0x0570; + + /** 64 bit pointer to a real char */ + public static final int T_64PRCHAR = 0x0670; + + // + // Wide character types + // + + /** wide char */ + public static final int T_WCHAR = 0x0071; + + /** near pointer to a wide char */ + public static final int T_PWCHAR = 0x0171; + + /** far pointer to a wide char */ + public static final int T_PFWCHAR = 0x0271; + + /** huge pointer to a wide char */ + public static final int T_PHWCHAR = 0x0371; + + /** 16:32 near pointer to a wide char */ + public static final int T_32PWCHAR = 0x0471; + + /** 16:32 far pointer to a wide char */ + public static final int T_32PFWCHAR = 0x0571; + + /** 64 bit pointer to a wide char */ + public static final int T_64PWCHAR = 0x0671; + + // + // Really 16 bit integer types + // + + /** really 16 bit signed int */ + public static final int T_INT2 = 0x0072; + + /** really 16 bit unsigned int */ + public static final int T_UINT2 = 0x0073; + + /** near pointer to 16 bit signed int */ + public static final int T_PINT2 = 0x0172; + + /** near pointer to 16 bit unsigned int */ + public static final int T_PUINT2 = 0x0173; + + /** far pointer to 16 bit signed int */ + public static final int T_PFINT2 = 0x0272; + + /** far pointer to 16 bit unsigned int */ + public static final int T_PFUINT2 = 0x0273; + + /** huge pointer to 16 bit signed int */ + public static final int T_PHINT2 = 0x0372; + + /** huge pointer to 16 bit unsigned int */ + public static final int T_PHUINT2 = 0x0373; + + /** 16:32 near pointer to 16 bit signed int */ + public static final int T_32PINT2 = 0x0472; + + /** 16:32 near pointer to 16 bit unsigned int */ + public static final int T_32PUINT2 = 0x0473; + + /** 16:32 far pointer to 16 bit signed int */ + public static final int T_32PFINT2 = 0x0572; + + /** 16:32 far pointer to 16 bit unsigned int */ + public static final int T_32PFUINT2 = 0x0573; + + /** 64 bit pointer to 16 bit signed int */ + public static final int T_64PINT2 = 0x0672; + + /** 64 bit pointer to 16 bit unsigned int */ + public static final int T_64PUINT2 = 0x0673; + + // + // 16-bit short types + // + + /** 16-bit signed */ + public static final int T_SHORT = 0x0011; + + /** 16-bit unsigned */ + public static final int T_USHORT = 0x0021; + + /** Near pointer to 16-bit signed */ + public static final int T_PSHORT = 0x0111; + + /** Near pointer to 16-bit unsigned */ + public static final int T_PUSHORT = 0x0121; + + /** Far pointer to 16-bit signed */ + public static final int T_PFSHORT = 0x0211; + + /** Far pointer to 16-bit unsigned */ + public static final int T_PFUSHORT = 0x0221; + + /** Huge pointer to 16-bit signed */ + public static final int T_PHSHORT = 0x0311; + + /** Huge pointer to 16-bit unsigned */ + public static final int T_PHUSHORT = 0x0321; + + /** 16:32 near pointer to 16 bit signed */ + public static final int T_32PSHORT = 0x0411; + + /** 16:32 near pointer to 16 bit unsigned */ + public static final int T_32PUSHORT = 0x0421; + + /** 16:32 far pointer to 16 bit signed */ + public static final int T_32PFSHORT = 0x0511; + + /** 16:32 far pointer to 16 bit unsigned */ + public static final int T_32PFUSHORT = 0x0521; + + /** 64 bit pointer to 16 bit signed */ + public static final int T_64PSHORT = 0x0611; + + /** 64 bit pointer to 16 bit unsigned */ + public static final int T_64PUSHORT = 0x0621; + + // + // Really 32 bit integer types + // + + /** really 32 bit signed int */ + public static final int T_INT4 = 0x0074; + + /** really 32 bit unsigned int */ + public static final int T_UINT4 = 0x0075; + + /** near pointer to 32 bit signed int */ + public static final int T_PINT4 = 0x0174; + + /** near pointer to 32 bit unsigned int */ + public static final int T_PUINT4 = 0x0175; + + /** far pointer to 32 bit signed int */ + public static final int T_PFINT4 = 0x0274; + + /** far pointer to 32 bit unsigned int */ + public static final int T_PFUINT4 = 0x0275; + + /** huge pointer to 32 bit signed int */ + public static final int T_PHINT4 = 0x0374; + + /** huge pointer to 32 bit unsigned int */ + public static final int T_PHUINT4 = 0x0375; + + /** 16:32 near pointer to 32 bit signed int */ + public static final int T_32PINT4 = 0x0474; + + /** 16:32 near pointer to 32 bit unsigned int */ + public static final int T_32PUINT4 = 0x0475; + + /** 16:32 far pointer to 32 bit signed int */ + public static final int T_32PFINT4 = 0x0574; + + /** 16:32 far pointer to 32 bit unsigned int */ + public static final int T_32PFUINT4 = 0x0575; + + /** 64 bit pointer to 32 bit signed int */ + public static final int T_64PINT4 = 0x0674; + + /** 64 bit pointer to 32 bit unsigned int */ + public static final int T_64PUINT4 = 0x0675; + + // + // 32-bit long types + // + + /** 32-bit signed */ + public static final int T_LONG = 0x0012; + + /** 32-bit unsigned */ + public static final int T_ULONG = 0x0022; + + /** Near pointer to 32-bit signed */ + public static final int T_PLONG = 0x0112; + + /** Near pointer to 32-bit unsigned */ + public static final int T_PULONG = 0x0122; + + /** Far pointer to 32-bit signed */ + public static final int T_PFLONG = 0x0212; + + /** Far pointer to 32-bit unsigned */ + public static final int T_PFULONG = 0x0222; + + /** Huge pointer to 32-bit signed */ + public static final int T_PHLONG = 0x0312; + + /** Huge pointer to 32-bit unsigned */ + public static final int T_PHULONG = 0x0322; + + /** 16:32 near pointer to 32 bit signed */ + public static final int T_32PLONG = 0x0412; + + /** 16:32 near pointer to 32 bit unsigned */ + public static final int T_32PULONG = 0x0422; + + /** 16:32 far pointer to 32 bit signed */ + public static final int T_32PFLONG = 0x0512; + + /** 16:32 far pointer to 32 bit unsigned */ + public static final int T_32PFULONG = 0x0522; + + /** 64 bit pointer to 32 bit signed */ + public static final int T_64PLONG = 0x0612; + + /** 64 bit pointer to 32 bit unsigned */ + public static final int T_64PULONG = 0x0622; + + // + // Really 64-bit integer types + // + + /** 64-bit signed int */ + public static final int T_INT8 = 0x0076; + + /** 64-bit unsigned int */ + public static final int T_UINT8 = 0x0077; + + /** Near pointer to 64-bit signed int */ + public static final int T_PINT8 = 0x0176; + + /** Near pointer to 64-bit unsigned int */ + public static final int T_PUINT8 = 0x0177; + + /** Far pointer to 64-bit signed int */ + public static final int T_PFINT8 = 0x0276; + + /** Far pointer to 64-bit unsigned int */ + public static final int T_PFUINT8 = 0x0277; + + /** Huge pointer to 64-bit signed int */ + public static final int T_PHINT8 = 0x0376; + + /** Huge pointer to 64-bit unsigned int */ + public static final int T_PHUINT8 = 0x0377; + + /** 16:32 near pointer to 64 bit signed int */ + public static final int T_32PINT8 = 0x0476; + + /** 16:32 near pointer to 64 bit unsigned int */ + public static final int T_32PUINT8 = 0x0477; + + /** 16:32 far pointer to 64 bit signed int */ + public static final int T_32PFINT8 = 0x0576; + + /** 16:32 far pointer to 64 bit unsigned int */ + public static final int T_32PFUINT8 = 0x0577; + + /** 64 bit pointer to 64 bit signed int */ + public static final int T_64PINT8 = 0x0676; + + /** 64 bit pointer to 64 bit unsigned int */ + public static final int T_64PUINT8 = 0x0677; + + // + // 64-bit integral types + // + + /** 64-bit signed */ + public static final int T_QUAD = 0x0013; + + /** 64-bit unsigned */ + public static final int T_UQUAD = 0x0023; + + /** Near pointer to 64-bit signed */ + public static final int T_PQUAD = 0x0113; + + /** Near pointer to 64-bit unsigned */ + public static final int T_PUQUAD = 0x0123; + + /** Far pointer to 64-bit signed */ + public static final int T_PFQUAD = 0x0213; + + /** Far pointer to 64-bit unsigned */ + public static final int T_PFUQUAD = 0x0223; + + /** Huge pointer to 64-bit signed */ + public static final int T_PHQUAD = 0x0313; + + /** Huge pointer to 64-bit unsigned */ + public static final int T_PHUQUAD = 0x0323; + + /** 16:32 near pointer to 64 bit signed */ + public static final int T_32PQUAD = 0x0413; + + /** 16:32 near pointer to 64 bit unsigned */ + public static final int T_32PUQUAD = 0x0423; + + /** 16:32 far pointer to 64 bit signed */ + public static final int T_32PFQUAD = 0x0513; + + /** 16:32 far pointer to 64 bit unsigned */ + public static final int T_32PFUQUAD = 0x0523; + + /** 64 bit pointer to 64 bit signed */ + public static final int T_64PQUAD = 0x0613; + + /** 64 bit pointer to 64 bit unsigned */ + public static final int T_64PUQUAD = 0x0623; + + // + // 32-bit real types + // + + /** 32-bit real */ + public static final int T_REAL32 = 0x0040; + + /** Near pointer to 32-bit real */ + public static final int T_PREAL32 = 0x0140; + + /** Far pointer to 32-bit real */ + public static final int T_PFREAL32 = 0x0240; + + /** Huge pointer to 32-bit real */ + public static final int T_PHREAL32 = 0x0340; + + /** 16:32 near pointer to 32 bit real */ + public static final int T_32PREAL32 = 0x0440; + + /** 16:32 far pointer to 32 bit real */ + public static final int T_32PFREAL32 = 0x0540; + + /** 64 pointer to 32 bit real */ + public static final int T_64PREAL32 = 0x0640; + + // + // 48-bit real types + // + + /** 48-bit real */ + public static final int T_REAL48 = 0x0044; + + /** Near pointer to 48-bit real */ + public static final int T_PREAL48 = 0x0144; + + /** Far pointer to 48-bit real */ + public static final int T_PFREAL48 = 0x0244; + + /** Huge pointer to 48-bit real */ + public static final int T_PHREAL48 = 0x0344; + + /** 16:32 near pointer to 48 bit real */ + public static final int T_32PREAL48 = 0x0444; + + /** 16:32 far pointer to 48 bit real */ + public static final int T_32PFREAL48 = 0x0544; + + /** 64 bit pointer to 48 bit real */ + public static final int T_64PREAL48 = 0x0644; + + // + // 64-bit real types + // + + /** 64-bit real */ + public static final int T_REAL64 = 0x0041; + + /** Near pointer to 64-bit real */ + public static final int T_PREAL64 = 0x0141; + + /** Far pointer to 64-bit real */ + public static final int T_PFREAL64 = 0x0241; + + /** Huge pointer to 64-bit real */ + public static final int T_PHREAL64 = 0x0341; + + /** 16:32 near pointer to 64 bit real */ + public static final int T_32PREAL64 = 0x0441; + + /** 16:32 far pointer to 64 bit real */ + public static final int T_32PFREAL64 = 0x0541; + + /** 64 bit pointer to 64 bit real */ + public static final int T_64PREAL64 = 0x0641; + + // + // 80-bit real types + // + + /** 80-bit real */ + public static final int T_REAL80 = 0x0042; + + /** Near pointer to 80-bit real */ + public static final int T_PREAL80 = 0x0142; + + /** Far pointer to 80-bit real */ + public static final int T_PFREAL80 = 0x0242; + + /** Huge pointer to 80-bit real */ + public static final int T_PHREAL80 = 0x0342; + + /** 16:32 near pointer to 80 bit real */ + public static final int T_32PREAL80 = 0x0442; + + /** 16:32 far pointer to 80 bit real */ + public static final int T_32PFREAL80 = 0x0542; + + /** 64 bit pointer to 80 bit real */ + public static final int T_64PREAL80 = 0x0642; + + // + // 128-bit real types + // + + /** 128-bit real */ + public static final int T_REAL128 = 0x0043; + + /** Near pointer to 128-bit real */ + public static final int T_PREAL128 = 0x0143; + + /** Far pointer to 128-bit real */ + public static final int T_PFREAL128 = 0x0243; + + /** Huge pointer to 128-bit real */ + public static final int T_PHREAL128 = 0x0343; + + /** 16:32 near pointer to 128 bit real */ + public static final int T_32PREAL128 = 0x0443; + + /** 16:32 far pointer to 128 bit real */ + public static final int T_32PFREAL128 = 0x0543; + + /** 64 bit pointer to 128 bit real */ + public static final int T_64PREAL128 = 0x0643; + + // + // 32-bit complex types + // + + /** 32-bit complex */ + public static final int T_CPLX32 = 0x0050; + + /** Near pointer to 32-bit complex */ + public static final int T_PCPLX32 = 0x0150; + + /** Far pointer to 32-bit complex */ + public static final int T_PFCPLX32 = 0x0250; + + /** Huge pointer to 32-bit complex */ + public static final int T_PHCPLX32 = 0x0350; + + /** 16:32 near pointer to 32 bit complex */ + public static final int T_32PCPLX32 = 0x0450; + + /** 16:32 far pointer to 32 bit complex */ + public static final int T_32PFCPLX32 = 0x0550; + + /** 64 bit pointer to 32 bit complex */ + public static final int T_64PCPLX32 = 0x0650; + + // + // 64-bit complex types + // + + /** 64-bit complex */ + public static final int T_CPLX64 = 0x0051; + + /** Near pointer to 64-bit complex */ + public static final int T_PCPLX64 = 0x0151; + + /** Far pointer to 64-bit complex */ + public static final int T_PFCPLX64 = 0x0251; + + /** Huge pointer to 64-bit complex */ + public static final int T_PHCPLX64 = 0x0351; + + /** 16:32 near pointer to 64 bit complex */ + public static final int T_32PCPLX64 = 0x0451; + + /** 16:32 far pointer to 64 bit complex */ + public static final int T_32PFCPLX64 = 0x0551; + + /** 64 bit pointer to 64 bit complex */ + public static final int T_64PCPLX64 = 0x0651; + + // + // 80-bit complex types + // + + /** 80-bit complex */ + public static final int T_CPLX80 = 0x0052; + + /** Near pointer to 80-bit complex */ + public static final int T_PCPLX80 = 0x0152; + + /** Far pointer to 80-bit complex */ + public static final int T_PFCPLX80 = 0x0252; + + /** Huge pointer to 80-bit complex */ + public static final int T_PHCPLX80 = 0x0352; + + /** 16:32 near pointer to 80 bit complex */ + public static final int T_32PCPLX80 = 0x0452; + + /** 16:32 far pointer to 80 bit complex */ + public static final int T_32PFCPLX80 = 0x0552; + + /** 64 bit pointer to 80 bit complex */ + public static final int T_64PCPLX80 = 0x0652; + + // + // 128-bit complex types + // + + /** 128-bit complex */ + public static final int T_CPLX128 = 0x0053; + + /** Near pointer to 128-bit complex */ + public static final int T_PCPLX128 = 0x0153; + + /** Far pointer to 128-bit complex */ + public static final int T_PFCPLX128 = 0x0253; + + /** Huge pointer to 128-bit real */ + public static final int T_PHCPLX128 = 0x0353; + + /** 16:32 near pointer to 128 bit complex */ + public static final int T_32PCPLX128 = 0x0453; + + /** 16:32 far pointer to 128 bit complex */ + public static final int T_32PFCPLX128 = 0x0553; + + /** 64 bit pointer to 128 bit complex */ + public static final int T_64PCPLX128 = 0x0653; + + // + // Boolean types + // + + /** 8-bit Boolean */ + public static final int T_BOOL08 = 0x0030; + + /** 16-bit Boolean */ + public static final int T_BOOL16 = 0x0031; + + /** 32-bit Boolean */ + public static final int T_BOOL32 = 0x0032; + + /** 64-bit Boolean */ + public static final int T_BOOL64 = 0x0033; + + /** Near pointer to 8-bit Boolean */ + public static final int T_PBOOL08 = 0x0130; + + /** Near pointer to 16-bit Boolean */ + public static final int T_PBOOL16 = 0x0131; + + /** Near pointer to 32-bit Boolean */ + public static final int T_PBOOL32 = 0x0132; + + /** Near pointer to 64-bit Boolean */ + public static final int T_PBOOL64 = 0x0133; + + /** Far pointer to 8-bit Boolean */ + public static final int T_PFBOOL08 = 0x0230; + + /** Far pointer to 16-bit Boolean */ + public static final int T_PFBOOL16 = 0x0231; + + /** Far pointer to 32-bit Boolean */ + public static final int T_PFBOOL32 = 0x0232; + + /** Far pointer to 64-bit Boolean */ + public static final int T_PFBOOL64 = 0x0233; + + /** Huge pointer to 8-bit Boolean */ + public static final int T_PHBOOL08 = 0x0330; + + /** Huge pointer to 16-bit Boolean */ + public static final int T_PHBOOL16 = 0x0331; + + /** Huge pointer to 32-bit Boolean */ + public static final int T_PHBOOL32 = 0x0332; + + /** Huge pointer to 64-bit Boolean */ + public static final int T_PHBOOL64 = 0x0333; + + /** 16:32 near pointer to 8 bit boolean */ + public static final int T_32PBOOL08 = 0x0430; + + /** 16:32 far pointer to 8 bit boolean */ + public static final int T_32PFBOOL08 = 0x0530; + + /** 16:32 near pointer to 16 bit boolean */ + public static final int T_32PBOOL16 = 0x0431; + + /** 16:32 far pointer to 16 bit boolean */ + public static final int T_32PFBOOL16 = 0x0531; + + /** 16:32 near pointer to 32 bit boolean */ + public static final int T_32PBOOL32 = 0x0432; + + /** 16:32 far pointer to 32 bit boolean */ + public static final int T_32PFBOOL32 = 0x0532; + + /** 16:32 near pointer to 64-bit Boolean */ + public static final int T_32PBOOL64 = 0x0433; + + /** 16:32 far pointer to 64-bit Boolean */ + public static final int T_32PFBOOL64 = 0x0533; + + /** 64 bit pointer to 8 bit boolean */ + public static final int T_64PBOOL08 = 0x0630; + + /** 64 bit pointer to 16 bit boolean */ + public static final int T_64PBOOL16 = 0x0631; + + /** 64 bit pointer to 32 bit boolean */ + public static final int T_64PBOOL32 = 0x0632; + + /** 64 bit pointer to 64-bit Boolean */ + public static final int T_64PBOOL64 = 0x0633; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSAlignSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSAlignSym.java new file mode 100644 index 00000000000..40c0ccd6581 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSAlignSym.java @@ -0,0 +1,35 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstAlignSym" subsection in Visual C++ 5.0 debug + information. This subsection apparently contains non-global + symbols left over from packing into the sstGlobalSym + subsection. Until we understand the contents of the sstGlobalSym + subsection, this subsection will contain no accessors. */ + +public interface DebugVC50SSAlignSym extends DebugVC50Subsection { + public DebugVC50SymbolIterator getSymbolIterator(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSFileIndex.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSFileIndex.java new file mode 100644 index 00000000000..7610596d81e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSFileIndex.java @@ -0,0 +1,57 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstFileIndex" subsection in Visual C++ 5.0 debug + information. This subsection contains a list of all of the source + files that contribute code to any module (compiland) in the + executable. File names are partially qualified relative to the + compilation directory. */ +public interface DebugVC50SSFileIndex extends DebugVC50Subsection { + /** Number of file name references per module. */ + public short getNumModules(); + + /** Count of the total number of file name references. */ + public short getNumReferences(); + + /** Array of indices into the NameOffset table for each + module. Each index is the start of the file name references for + each module. */ + public short[] getModStart(); + + /** Number of file name references per module. */ + public short[] getRefCount(); + + /** Array of offsets into the Names table. For each module, the + offset to first referenced file name is at NameRef[ModStart] and + continues for cRefCnt entries. FIXME: this probably is useless + and needs fixup to convert these offsets into indices into the + following array. */ + public int[] getNameRef(); + + /** List of zero terminated file names. Each file name is partially + qualified relative to the compilation directory. */ + public String[] getNames(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSGlobalPub.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSGlobalPub.java new file mode 100644 index 00000000000..fc9063bf21d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSGlobalPub.java @@ -0,0 +1,36 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstGlobalPub" subsection in Visual C++ 5.0 debug + information, which contains globally compacted symbols from the + sstPublics. This class provides access to the symbols via + iterators; it does not instantiate objects to represent symbols + because of the expected high volume of symbols. The caller is + expected to traverse this table and convert the platform-dependent + symbols into a platform-independent format at run time. */ + +public interface DebugVC50SSGlobalPub extends DebugVC50SSSymbolBase { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSGlobalSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSGlobalSym.java new file mode 100644 index 00000000000..e727d4f52b4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSGlobalSym.java @@ -0,0 +1,35 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstGlobalSym" subsection in Visual C++ 5.0 debug + information. This class provides access to the symbols via + iterators; it does not instantiate objects to represent symbols + because of the expected high volume of symbols. The caller is + expected to traverse this table and convert the platform-dependent + symbols into a platform-independent format at run time. */ + +public interface DebugVC50SSGlobalSym extends DebugVC50SSSymbolBase { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSGlobalTypes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSGlobalTypes.java new file mode 100644 index 00000000000..67d03bb70e1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSGlobalTypes.java @@ -0,0 +1,45 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstGlobalTypes" subsection in Visual C++ 5.0 debug + information. This class provides access to the types via + iterators; it does not instantiate objects to represent types + because of the expected high volume of types. The caller is + expected to traverse this table and convert the platform-dependent + types into a platform-independent format at run time. */ + +public interface DebugVC50SSGlobalTypes extends DebugVC50Subsection { + /** Number of types in the table. */ + public int getNumTypes(); + + /** Absolute file offset of the ith (0..getNumTypes() - 1) + type in the table. */ + public int getTypeOffset(int i); + + /** Create a new type iterator pointing to the first type in the + subsection. */ + public DebugVC50TypeIterator getTypeIterator(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSLibraries.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSLibraries.java new file mode 100644 index 00000000000..f7d5bf14eb8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSLibraries.java @@ -0,0 +1,34 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstLibraries" subsection in Visual C++ 5.0 debug + information. This subsection contains the library names used + during linking. (Some of the descriptions are taken directly from + Microsoft's documentation and are copyrighted by Microsoft.) */ + +public interface DebugVC50SSLibraries extends DebugVC50Subsection { + // FIXME: NOT FINISHED +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSMPC.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSMPC.java new file mode 100644 index 00000000000..948e06eea37 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSMPC.java @@ -0,0 +1,32 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstMPC" subsection in Visual C++ 5.0 debug + information. This subsection is only used for Pcode programs and + therefore the contents are not exposed. */ + +public interface DebugVC50SSMPC extends DebugVC50Subsection { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSModule.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSModule.java new file mode 100644 index 00000000000..e118edd5ee3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSModule.java @@ -0,0 +1,53 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "module" subsection in Visual C++ 5.0 debug + information. (Some of the descriptions are taken directly from + Microsoft's documentation and are copyrighted by Microsoft.) */ + +public interface DebugVC50SSModule extends DebugVC50Subsection { + public short getOverlayNumber(); + /** Index into sstLibraries subsection if this module was linked + from a library. */ + public short getLibrariesIndex(); + + /** Count of the number of code segments this module contributes to. + This is the length of the SegInfo array, below. */ + public short getNumCodeSegments(); + + /** Debugging style for this module. Currently only "CV" is defined. + A module can have only one debugging style. If a module contains + debugging information in an unrecognized style, the information + will be discarded. */ + public short getDebuggingStyle(); + + /** Fetch description of segment to which this module contributes + code (0..getNumCodeSegments - 1) */ + public DebugVC50SegInfo getSegInfo(int i); + + /** Name of the module */ + public String getName(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSOffsetMap16.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSOffsetMap16.java new file mode 100644 index 00000000000..fb87999e759 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSOffsetMap16.java @@ -0,0 +1,31 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstOffsetMap16" subsection in Visual C++ 5.0 debug + information. Currently has no accessors as 16-bit applications are + not supported. */ +public interface DebugVC50SSOffsetMap16 extends DebugVC50Subsection { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSOffsetMap32.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSOffsetMap32.java new file mode 100644 index 00000000000..d0136942cdf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSOffsetMap32.java @@ -0,0 +1,34 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstOffsetMap32" subsection in Visual C++ 5.0 debug + information. This table provides a mapping from logical to + physical offsets. This mapping is applied between the logical to + physical mapping described by the seg map table. FIXME: this table + is being elided for now unless it proves to be necessary to finish + the implementation. */ +public interface DebugVC50SSOffsetMap32 extends DebugVC50Subsection { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSPreComp.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSPreComp.java new file mode 100644 index 00000000000..efeffd8fe14 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSPreComp.java @@ -0,0 +1,31 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstPreComp" subsection in Visual C++ 5.0 debug + information. Currently has no accessors. */ + +public interface DebugVC50SSPreComp extends DebugVC50Subsection { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSPublic.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSPublic.java new file mode 100644 index 00000000000..1301a2d50f2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSPublic.java @@ -0,0 +1,32 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstPublic" subsection in Visual C++ 5.0 debug + information. This is an obsolete subsection, replaced by + sstPublicSym, and the implementation has been left empty. */ + +public interface DebugVC50SSPublic extends DebugVC50Subsection { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSPublicSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSPublicSym.java new file mode 100644 index 00000000000..3352d9fbfb7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSPublicSym.java @@ -0,0 +1,33 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstPublicSym" subsection in Visual C++ 5.0 debug + information. These subsections from various modules are compacted + into the sstGlobalSym table and for this reason this subsection + currently has no accessors. */ + +public interface DebugVC50SSPublicSym extends DebugVC50Subsection { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSegMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSegMap.java new file mode 100644 index 00000000000..e7527829e37 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSegMap.java @@ -0,0 +1,46 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstSegMap" subsection in Visual C++ 5.0 debug + information. This subsection contains the mapping between the + logical segment indices used in the symbol table and the physical + segments where the program was loaded. There is one sstSegMap per + executable or DLL. (Some of the descriptions are taken directly + from Microsoft's documentation and are copyrighted by Microsoft.) */ + +public interface DebugVC50SSSegMap extends DebugVC50Subsection { + /** Count of the number of segment descriptors in table. */ + public short getNumSegDesc(); + + /** The total number of logical segments. All group descriptors + follow the logical segment descriptors. The number of group + descriptors is given by cSeg - cSegLog. */ + public short getNumLogicalSegDesc(); + + /** Get the ith segment descriptor (0..getNumSegDesc() - + 1). */ + public DebugVC50SegDesc getSegDesc(int i); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSegName.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSegName.java new file mode 100644 index 00000000000..a8b5a11161b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSegName.java @@ -0,0 +1,39 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstSegName" subsection in Visual C++ 5.0 debug + information. This subsection contains all of the logical segment + and class names. The table is an array of zero-terminated strings. + Each string is indexed by its beginning from the start of the + table. See {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SSSegMap}. (Some of + the descriptions are taken directly from Microsoft's documentation + and are copyrighted by Microsoft.) */ + +public interface DebugVC50SSSegName extends DebugVC50Subsection { + /** Indexed by (0..DebugVC50SSSegMap.getNumSegDesc() - 1) */ + public String getSegName(int i); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSrcLnSeg.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSrcLnSeg.java new file mode 100644 index 00000000000..d9e3b936195 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSrcLnSeg.java @@ -0,0 +1,32 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstSrcLnSeg" subsection in Visual C++ 5.0 debug + information. This subsection has been replaced by the sstSrcModule + table and therefore currently has an empty implementation. */ + +public interface DebugVC50SSSrcLnSeg extends DebugVC50Subsection { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSrcModule.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSrcModule.java new file mode 100644 index 00000000000..c38733989d5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSrcModule.java @@ -0,0 +1,62 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstSrcModule" subsection in Visual C++ 5.0 debug + information. This subsection contains line number to address + mappings. (Some of the descriptions are taken directly from + Microsoft's documentation and are copyrighted by Microsoft.) */ + +public interface DebugVC50SSSrcModule extends DebugVC50Subsection { + /** The number of source files contributing code to segments. */ + public int getNumSourceFiles(); + + /** The number of code segments receiving code from this module. */ + public int getNumCodeSegments(); + + /** Get description of ith source file + (0..getNumSourceFiles() - 1) contributing code to this + module. */ + public DebugVC50SrcModFileDesc getSourceFileDesc(int i); + + /** Get ith (0..getNumCodeSegments() - 1) start offset, + within a segment, of code contributed to that segment. If this + and the end offset are both 0, the start/end offsets are not + known and the file and line tables must be examined to find + information about the program counter of interest. */ + public int getSegmentStartOffset(int i); + + /** Get ith (0..getNumCodeSegments() - 1) end offset, within + a segment, of code contributed to that segment. If this and the + start offset are both 0, the start/end offsets are not known and + the file and line tables must be examined to find information + about the program counter of interest. */ + public int getSegmentEndOffset(int i); + + /** Get ith (0..getNumCodeSegments() - 1) segment to which + this module contributes code. See {@link + sun.jvm.hotspot.debugger.win32.coff.COFFHeader.getSectionHeader}. */ + public int getSegment(int i); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSStaticSym.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSStaticSym.java new file mode 100644 index 00000000000..0fd04b90cf0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSStaticSym.java @@ -0,0 +1,35 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstStaticSym" subsection in Visual C++ 5.0 debug + information. This subsection is structured exactly like the + sstGlobalPub and sstGlobalSym subsections. It contains S_PROCREF + for all static functions as well as S_DATAREF for static module + level data and non-static data that could not be included (due to + type conflicts) in the sstGlobalSym subsection. */ + +public interface DebugVC50SSStaticSym extends DebugVC50SSSymbolBase { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSymbolBase.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSymbolBase.java new file mode 100644 index 00000000000..979d1b7725d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSymbolBase.java @@ -0,0 +1,53 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Base interface for subsections containing symbols: sstGlobalSym, + sstGlobalPub, sstStaticSym. */ + +public interface DebugVC50SSSymbolBase extends DebugVC50Subsection { + /** Index of the symbol hash function */ + public short getSymHashIndex(); + + /** Index of the address hash function */ + public short getAddrHashIndex(); + + /** Size in bytes of the symbol table */ + public int getSymTabSize(); + + /** Size in bytes of the symbol hash table */ + public int getSymHashSize(); + + /** Size in bytes of the address hash table */ + public int getAddrHashSize(); + + // Following this header is the symbol information, followed by the + // symbol hash tables, followed by the address hash tables. + + /** Retrieves an iterator over the symbols, which can be used to + parse these platform-dependent symbols into a platform- + independent format. Returns null if there are no symbols. */ + public DebugVC50SymbolIterator getSymbolIterator(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSymbols.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSymbols.java new file mode 100644 index 00000000000..bdbf5a734b2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSSymbols.java @@ -0,0 +1,34 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstSymbols" subsection in Visual C++ 5.0 debug + information. Public symbols from these subsections are moved to + the sstGlobalSym subsection during packing, and remaining symbols + are moved to the sstAlignSym subsection. For this reason this + subsection contains no accessors. */ + +public interface DebugVC50SSSymbols extends DebugVC50Subsection { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSTypes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSTypes.java new file mode 100644 index 00000000000..3170fcc6bb4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SSTypes.java @@ -0,0 +1,32 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the "sstTypes" subsection in Visual C++ 5.0 debug + information. This is a temporary subsection used during linking + and is a no-op in this package. */ + +public interface DebugVC50SSTypes extends DebugVC50Subsection { +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SegDesc.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SegDesc.java new file mode 100644 index 00000000000..2f76618b4c2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SegDesc.java @@ -0,0 +1,74 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models a segment descriptor in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SSSegMap}. */ + +public interface DebugVC50SegDesc { + /** Descriptor flags bit field. See {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SegDescEnums}. */ + public short getFlags(); + + /** The logical overlay number. */ + public short getOverlayNum(); + + /** The group index into the descriptor array. The group index must + either be 0 or cSegLog <= group < cSeg. */ + public short getGroup(); + + /**

This value has different meanings depending upon the values + of fAbs and fSel in the flags bit array and ovl:

+ +

+ +
fAbs fSel ovl Operation +
0 0 0 Frame is added to PSP + 0x10 if not a .com file +
0 0 0 Frame is added to PSP if it is a .com file +
0 0 != 0 Frame is added to current overlay base +
1 0 x Frame is absolute address +
0 1 x Frame contains a selector +
+

+ */ + public short getFrame(); + + /** The byte index of the segment or group name in the sstSegName + table. A value of 0xffff indicates there is no name. */ + public short getName(); + + /** The byte index of the class name in the sstSegName table. A + value of 0xffff indicates there is no name. */ + public short getClassName(); + + /** Byte offset of the logical segment within the specified physical + segment. If fGroup is set in flags, offset is the offset of the + group in the physical segment. Currently, all groups define + physical segments so offset will be zero for groups. */ + public int getOffset(); + + /** Byte count of the logical segment or group. */ + public int getSize(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SegDescEnums.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SegDescEnums.java new file mode 100644 index 00000000000..4e5b54eeeeb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SegDescEnums.java @@ -0,0 +1,55 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Bit field definitions used in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SegDesc}. */ + +public interface DebugVC50SegDescEnums { + // FIXME: verify these are correct + + /** If set, the descriptor represents a group. Since groups are not + assigned logical segment numbers, these entries are placed after + the logcial segment descriptors in the descriptor array. */ + public static final short SEGMAP_GROUP_MASK = (short) 0x1000; + + /** frame represents an absolute address. */ + public static final short SEGMAP_ABS_MASK = (short) 0x0200; + + /** frame represents a selector. */ + public static final short SEGMAP_SEL_MASK = (short) 0x0100; + + /** The descriptor describes a 32-bit linear address. */ + public static final short SEGMAP_32BIT_MASK = (short) 0x0008; + + /** The segment is executable. */ + public static final short SEGMAP_EXECUTABLE_MASK = (short) 0x0004; + + /** The segment is writable. */ + public static final short SEGMAP_WRITABLE_MASK = (short) 0x0002; + + /** The segment is readable. */ + public static final short SEGMAP_READABLE_MASK = (short) 0x0001; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SegInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SegInfo.java new file mode 100644 index 00000000000..c90a32eeac3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SegInfo.java @@ -0,0 +1,41 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the SegInfo data structure in the Module subsection in + Visual C++ 5.0 debug information. (Some of the descriptions are + taken directly from Microsoft's documentation and are copyrighted + by Microsoft.) */ + +public interface DebugVC50SegInfo { + /** Segment that this structure describes */ + public short getSegment(); + + /** Offset in segment where the code starts */ + public int getOffset(); + + /** Count of the number of bytes of code in the segment */ + public int getSegmentCodeSize(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SrcModFileDesc.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SrcModFileDesc.java new file mode 100644 index 00000000000..d0490fa20db --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SrcModFileDesc.java @@ -0,0 +1,56 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes code segments that receive code from a given source + file. See {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SSSrcModule}. */ + +public interface DebugVC50SrcModFileDesc { + /** The number of code segments receiving code from this module. */ + public int getNumCodeSegments(); + + /** Get the ith (0..getNumCodeSegments() - 1) line + number/address map for the given segment. */ + public DebugVC50SrcModLineNumberMap getLineNumberMap(int i); + + /** Get ith (0..getNumCodeSegments() - 1) start offset, + within a segment, of code contributed to that segment. If this + and the end offset are both 0, the start/end offsets are not + known and the file and line tables must be examined to find + information about the program counter of interest. */ + public int getSegmentStartOffset(int i); + + /** Get ith (0..getNumCodeSegments() - 1) end offset, within + a segment, of code contributed to that segment. If this and the + start offset are both 0, the start/end offsets are not known and + the file and line tables must be examined to find information + about the program counter of interest. */ + public int getSegmentEndOffset(int i); + + /** Source file name. This can be a fully or partially qualified + path name. */ + public String getSourceFileName(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SrcModLineNumberMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SrcModLineNumberMap.java new file mode 100644 index 00000000000..366de8dbbe8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SrcModLineNumberMap.java @@ -0,0 +1,44 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Supplies line number/address mapping for a given source file. */ + +public interface DebugVC50SrcModLineNumberMap { + /** Segment index for this table. */ + public int getSegment(); + + /** Number of source line pairs. */ + public int getNumSourceLinePairs(); + + /** Get the ith (i..getNumSourceLinePairs() - 1) offset + within the code segment of the start of the line in the parallel + line number array. */ + public int getCodeOffset(int i); + + /** Get the ith (i..getNumSourceLinePairs() - 1) line + number. */ + public int getLineNumber(int i); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50Subsection.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50Subsection.java new file mode 100644 index 00000000000..07275aaea36 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50Subsection.java @@ -0,0 +1,43 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Base class for subsections in Visual C++ 5.0 debug information. */ + +public interface DebugVC50Subsection { + /** Returns type of this subsection; see {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SubsectionTypes}. */ + public short getSubsectionType(); + + /** Returns module index associated with this subsection. This + number is 1 based and zero is never a valid index. The index + 0xffff is reserved for tables that are not associated with a + specific module. These tables include sstLibraries, + sstGlobalSym, sstGlobalPub and sstGlobalTypes. */ + public short getSubsectionModuleIndex(); + + /** Number of bytes in subsection. */ + public int getSubsectionSize(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SubsectionDirectory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SubsectionDirectory.java new file mode 100644 index 00000000000..dca77a4df63 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SubsectionDirectory.java @@ -0,0 +1,40 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the subsection directory portion of Visual C++ 5.0 debug + information. */ + +public interface DebugVC50SubsectionDirectory { + public short getHeaderLength(); + public short getEntryLength(); + public int getNumEntries(); + + // lfoNextDir and flags fields have been elided as they are unused + // according to the documentation + + /** Returns subsection at (zero-based) index i. */ + public DebugVC50Subsection getSubsection(int i); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SubsectionTypes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SubsectionTypes.java new file mode 100644 index 00000000000..9369d36f627 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SubsectionTypes.java @@ -0,0 +1,52 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Type enumeration for subsections in Visual C++ 5.0 debug + information. */ + +public interface DebugVC50SubsectionTypes { + public static final short SST_MODULE = (short) 0x120; + public static final short SST_TYPES = (short) 0x121; + public static final short SST_PUBLIC = (short) 0x122; + public static final short SST_PUBLIC_SYM = (short) 0x123; + public static final short SST_SYMBOLS = (short) 0x124; + public static final short SST_ALIGN_SYM = (short) 0x125; + public static final short SST_SRC_LN_SEG = (short) 0x126; + public static final short SST_SRC_MODULE = (short) 0x127; + public static final short SST_LIBRARIES = (short) 0x128; + public static final short SST_GLOBAL_SYM = (short) 0x129; + public static final short SST_GLOBAL_PUB = (short) 0x12a; + public static final short SST_GLOBAL_TYPES = (short) 0x12b; + public static final short SST_MPC = (short) 0x12c; + public static final short SST_SEG_MAP = (short) 0x12d; + public static final short SST_SEG_NAME = (short) 0x12e; + public static final short SST_PRE_COMP = (short) 0x12f; + public static final short SST_UNUSED = (short) 0x130; + public static final short SST_OFFSET_MAP_16 = (short) 0x131; + public static final short SST_OFFSET_MAP_32 = (short) 0x132; + public static final short SST_FILE_INDEX = (short) 0x133; + public static final short SST_STATIC_SYM = (short) 0x134; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SymbolEnums.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SymbolEnums.java new file mode 100644 index 00000000000..b7d7c009182 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SymbolEnums.java @@ -0,0 +1,127 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Miscellaneous integer enumerations for symbol parsing */ + +public interface DebugVC50SymbolEnums { + + /** Machine types (S_COMPILE) */ + public static final byte MACHTYPE_INTEL_8080 = (byte) 0x00; + public static final byte MACHTYPE_INTEL_8086 = (byte) 0x01; + public static final byte MACHTYPE_INTEL_80286 = (byte) 0x02; + public static final byte MACHTYPE_INTEL_80386 = (byte) 0x03; + public static final byte MACHTYPE_INTEL_80486 = (byte) 0x04; + public static final byte MACHTYPE_INTEL_PENTIUM = (byte) 0x05; + public static final byte MACHTYPE_INTEL_PENTIUM_PRO = (byte) 0x06; + public static final byte MACHTYPE_MIPS_R4000 = (byte) 0x10; + public static final byte MACHTYPE_MIPS_RESERVED = (byte) 0x11; + public static final byte MACHTYPE_MIPS_RESERVED2 = (byte) 0x12; + public static final byte MACHTYPE_MC68000 = (byte) 0x20; + public static final byte MACHTYPE_MC68010 = (byte) 0x21; + public static final byte MACHTYPE_MC68020 = (byte) 0x22; + public static final byte MACHTYPE_MC68030 = (byte) 0x23; + public static final byte MACHTYPE_MC68040 = (byte) 0x24; + public static final byte MACHTYPE_ALPHA = (byte) 0x30; + public static final byte MACHTYPE_PPC601 = (byte) 0x40; + public static final byte MACHTYPE_PPC603 = (byte) 0x41; + public static final byte MACHTYPE_PPC604 = (byte) 0x42; + public static final byte MACHTYPE_PPC620 = (byte) 0x43; + + /** Compile flags (S_COMPILE). All have masks and shifts because the + data is bit-packed into three bytes in the file. (FIXME: test + these masks and shifts to make sure they are correct.) */ + public static final int COMPFLAG_LANGUAGE_MASK = 0x00FF0000; + public static final int COMPFLAG_LANGUAGE_SHIFT = 16; + public static final int COMPFLAG_LANGUAGE_C = 0; + public static final int COMPFLAG_LANGUAGE_CPP = 1; // C++ + public static final int COMPFLAG_LANGUAGE_FORTRAN = 2; + public static final int COMPFLAG_LANGUAGE_MASM = 3; + public static final int COMPFLAG_LANGUAGE_PASCAL = 4; + public static final int COMPFLAG_LANGUAGE_BASIC = 5; + public static final int COMPFLAG_LANGUAGE_COBOL = 6; + + public static final int COMPFLAG_PCODE_PRESENT_MASK = 0x00008000; + + // Float precision enumeration + public static final int COMPFLAG_FLOAT_PRECISION_MASK = 0x00006000; + public static final int COMPFLAG_FLOAT_PRECISION_SHIFT = 13; + public static final int COMPFLAG_FLOAT_PRECISION_ANSI_C = 1; + + // Floating package enumeration + public static final int COMPFLAG_FLOAT_PACKAGE_MASK = 0x00001800; + public static final int COMPFLAG_FLOAT_PACKAGE_SHIFT = 11; + public static final int COMPFLAG_FLOAT_PACKAGE_HARDWARE = 0; + public static final int COMPFLAG_FLOAT_PACKAGE_EMULATOR = 1; + public static final int COMPFLAG_FLOAT_PACKAGE_ALTMATH = 2; + + // Ambient code/data memory model flags + public static final int COMPFLAG_AMBIENT_DATA_MASK = 0x00000700; + public static final int COMPFLAG_AMBIENT_DATA_SHIFT = 12; + public static final int COMPFLAG_AMBIENT_CODE_MASK = 0x000000E0; + public static final int COMPFLAG_AMBIENT_CODE_SHIFT = 8; + public static final int COMPFLAG_AMBIENT_MODEL_NEAR = 0; + public static final int COMPFLAG_AMBIENT_MODEL_FAR = 1; + public static final int COMPFLAG_AMBIENT_MODEL_HUGE = 2; + + // Indicates whether program is compiled for 32-bit addresses + public static final int COMPFLAG_MODE32_MASK = 0x00000010; + + /** Function return flags (S_RETURN) */ + // FIXME: verify these are correct + public static final short FUNCRET_VARARGS_LEFT_TO_RIGHT_MASK = (short) 0x0001; + public static final short FUNCRET_RETURNEE_STACK_CLEANUP_MASK = (short) 0x0002; + + // Return styles + public static final byte FUNCRET_VOID = (byte) 0x00; + public static final byte FUNCRET_IN_REGISTERS = (byte) 0x01; + public static final byte FUNCRET_INDIRECT_CALLER_NEAR = (byte) 0x02; + public static final byte FUNCRET_INDIRECT_CALLER_FAR = (byte) 0x03; + public static final byte FUNCRET_INDIRECT_RETURNEE_NEAR = (byte) 0x04; + public static final byte FUNCRET_INDIRECT_RETURNEE_FAR = (byte) 0x05; + + /** Procedure flags */ + // FIXME: verify these are correct + public static final byte PROCFLAGS_FRAME_POINTER_OMITTED = (byte) 0x01; + public static final byte PROCFLAGS_INTERRUPT_ROUTINE = (byte) 0x02; + public static final byte PROCFLAGS_FAR_RETURN = (byte) 0x04; + public static final byte PROCFLAGS_NEVER_RETURN = (byte) 0x08; + + /** Thunk types (S_THUNK32) */ + public static final byte THUNK_NO_TYPE = (byte) 0; + public static final byte THUNK_ADJUSTOR = (byte) 1; + public static final byte THUNK_VCALL = (byte) 2; + public static final byte THUNK_PCODE = (byte) 3; + + /** Execution models (S_CEXMODEL32) */ + public static final short EXMODEL_NOT_CODE = (short) 0x00; + public static final short EXMODEL_JUMP_TABLE = (short) 0x01; + public static final short EXMODEL_PADDING = (short) 0x02; + public static final short EXMODEL_NATIVE = (short) 0x20; + public static final short EXMODEL_MICROFOCUS_COBOL = (short) 0x21; + public static final short EXMODEL_PADDING_FOR_ALIGNMENT = (short) 0x22; + public static final short EXMODEL_CODE = (short) 0x23; + public static final short EXMODEL_PCODE = (short) 0x40; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SymbolIterator.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SymbolIterator.java new file mode 100644 index 00000000000..39500ce9350 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SymbolIterator.java @@ -0,0 +1,614 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +import java.util.*; + +/** Provides iteration-style access to the symbols in the sstGlobalSym + (and possibly other) subsections of the VC++ 5.0 debug + information. Clients should walk down these platform-dependent + symbols and transform them into the platform-independent + interfaces described in the package sun.jvm.hotspot.debugger.csym. */ + +public interface DebugVC50SymbolIterator + extends DebugVC50SymbolTypes, DebugVC50SymbolEnums { + + /** Indicates whether this iterator has processed all of the + available symbols. */ + public boolean done(); + + /** Go to the next symbol. NOTE that the iterator is pointing at the + first symbol initially, so one should use a while (!iter.done()) + { ... iter.next(); } construct. + + @throw NoSuchElementException if the iterator is already done + and next() is called. */ + public void next() throws NoSuchElementException; + + /** Length of record, in bytes, excluding the length field. */ + public short getLength(); + + /** The type enumeration is defined in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SymbolTypes} */ + public int getType(); + + /** For debugging: returns the file offset of the current symbol. */ + public int getOffset(); + + ///////////////////////// + // S_COMPILE accessors // + ///////////////////////// + + /** Machine enumeration specifying target processor; see + DebugVC50SymbolEnums. */ + public byte getCompilerTargetProcessor(); + + /** Compile flags; see DebugVC50SymbolEnums. */ + public int getCompilerFlags(); + + /** Length-prefixed string specifying language processor version. + Language processors can place additional data in version string + if desired. */ + public String getComplierVersion(); + + ////////////////////////// + // S_REGISTER accessors // + ////////////////////////// + + /** Type of the symbol which is in the register */ + public int getRegisterSymbolType(); + + /** Enumerate of the registers in which the symbol is stored. The + high and low bytes are treated independently for values split + across two registers (i.e., 64-bit values on a 32-bit machine.) */ + public short getRegisterEnum(); + + /** Length-prefixed name of the symbol stored in the register. */ + public String getRegisterSymbolName(); + + // Note: register tracking elided as it is not implemented in the + // Microsoft compilers. + + ////////////////////////// + // S_CONSTANT accessors // + ////////////////////////// + + /** Type of symbol or containing enum. This record is used to output + constants and C enumerations. If used to output an enumeration, + then the type index refers to the containing enum. */ + public int getConstantType(); + + /** Numeric leaf containing the value of the symbol as an int */ + public int getConstantValueAsInt() throws DebugVC50WrongNumericTypeException; + + /** Numeric leaf containing the value of the symbol as a long */ + public long getConstantValueAsLong() throws DebugVC50WrongNumericTypeException; + + /** Numeric leaf containing the value of the symbol as a float */ + public float getConstantValueAsFloat() throws DebugVC50WrongNumericTypeException; + + /** Numeric leaf containing the value of the symbol as a double */ + public double getConstantValueAsDouble() throws DebugVC50WrongNumericTypeException; + + /** Length-prefixed name of the symbol */ + public String getConstantName(); + + ///////////////////// + // S_UDT accessors // + ///////////////////// + + /** Type of symbol. This specifies a C typedef or user-defined type, + such as classes, structures, unions, or enums. */ + public int getUDTType(); + + /** Length-prefixed name of the user defined type. */ + public String getUDTName(); + + ///////////////////////// + // S_SSEARCH accessors // + ///////////////////////// + + // FIXME: Add more documentation and understand what this does + + /** $$SYMBOL offset of the procedure or thunk record for this module + that has the lowest offset for the specified segment. */ + public int getSearchSymbolOffset(); + + /** Segment (PE section) that this Start Search refers to. */ + public short getSearchSegment(); + + ///////////////////// + // S_END accessors // + ///////////////////// + + // (No accessors) + // Closes the scope of the nearest preceding Block Start, Global + // Procedure Start, Local Procedure Start, With Start, or Thunk + // Start definition. + + ////////////////////// + // S_SKIP accessors // + ////////////////////// + + // (No accessors) + // Use the length field, available in every symbol, to skip over + // these records. + + /////////////////////////// + // S_CVRESERVE accessors // + /////////////////////////// + + // (No accessors) + + ///////////////////////// + // S_OBJNAME accessors // + ///////////////////////// + + /** Signature used to determine whether changes in precompiled types + defined in this module require a recompilation of users of those + types. This does not have much meaning given that the algorithm + for computing the signature is unspecified. */ + public int getObjectCodeViewSignature(); + + /** Length prefixed name of the object file without any path + information prepended to the name. */ + public String getObjectName(); + + //////////////////////// + // S_ENDARG accessors // + //////////////////////// + + // (No accessors) + + ////////////////////////// + // S_COBOLUDT accessors // + ////////////////////////// + + // (Elided as they are irrelevant) + + ///////////////////////// + // S_MANYREG accessors // + ///////////////////////// + + /** Type index of the symbol. This record is used to specify that a + symbol is stored in a set of registers. */ + public int getManyRegType(); + + /** Count of the register enumerates that follow. */ + public byte getManyRegCount(); + + /** Get the ith register (0..getManyRegCount() - 1). The + registers are listed high order register first. */ + public byte getManyRegRegister(int i); + + /** Name of the symbol. */ + public String getManyRegName(); + + //////////////////////// + // S_RETURN accessors // + //////////////////////// + + /** Logical or of FUNCRET_VARARGS_LEFT_TO_RIGHT_MASK (push varargs + left to right if set) and FUNCRET_RETURNEE_STACK_CLEANUP_MASK + (returnee cleans up stack if true). */ + public short getReturnFlags(); + + /** Function return style; see constants in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SymbolEnums}. */ + public byte getReturnStyle(); + + /** Get count of registers containing return value; only valid for + FUNCRET_IN_REGISTERS return style. */ + public byte getReturnRegisterCount(); + + /** Get ith register (0..getReturnRegisterCount() - 1) + containing return value, high order first; only valid for + FUNCRET_IN_REGISTERS return style. */ + public byte getReturnRegister(int i); + + /////////////////////////// + // S_ENTRYTHIS accessors // + /////////////////////////// + + /** Advance this iterator to the symbol (which actually describes + the this pointer) contained within the S_ENTRYTHIS + symbol. */ + public void advanceToEntryThisSymbol(); + + /////////////////////////////////////////////////////////////////////// + // // + // // + // Symbols for (Intel) 16:32 Segmented and 32-bit Flat Architectures // + // // + // // + /////////////////////////////////////////////////////////////////////// + + ///////////////////////// + // S_BPREL32 accessors // + ///////////////////////// + + // This symbol specifies symbols that are allocated on the stack for + // a procedure. For C/C++, these include the actual parameters to a + // function and the local nonstatic variables of functions. + + /** Signed offset relative to BP. If 0, then the symbol was assigned + to a register or never instantiated by the optimizer and cannot + be evaluated because its location is unknown. */ + public int getBPRelOffset(); + + /** Type of the symbol. */ + public int getBPRelType(); + + /** Length-prefixed name of the symbol. */ + public String getBPRelName(); + + /////////////////////////////////////// + // S_LDATA32 and S_GDATA32 accessors // + /////////////////////////////////////// + + // FIXME: consider documenting this as covering S_PUB32 symbols as + // well + + // The formats of S_LDATA32 and S_GDATA32 symbols match; the only + // difference is the type tag. + // + // LDATA32 symbols are used for data that is not exported from a + // module. In C/C++, symbols that are declared static are emitted as + // Local Data symbols. Symbols that are emitted as Local Data cannot + // be moved by CVPACK into the global symbol table for the + // executable file. + // + // GDATA32 records have the same format as the Local Data 16:32 + // except that the record type is S_GDATA32. For C/C++, symbols that + // are not specifically declared static are emitted as Global Data + // Symbols and can be compacted by CVPACK into the global symbol + // table. + + /** Type index of the symbol. */ + public int getLGDataType(); + + /** Offset portion of the symbol address. */ + public int getLGDataOffset(); + + /** Segment portion of the symbol address. */ + public short getLGDataSegment(); + + /** Length-prefixed name of symbol. */ + public String getLGDataName(); + + /////////////////////// + // S_PUB32 accessors // + /////////////////////// + + // FIXME: has the same format as the above; consider updating + // documentation. No separate accessors provided. + + /////////////////////////////////////// + // S_LPROC32 and S_GPROC32 accessors // + /////////////////////////////////////// + + // LPROC32 and GPROC32 symbols have the same format, differing only + // in the type tag. + // + // The LPROC32 symbol record defines a local (file static) procedure + // definition. For C/C++, functions that are declared static to a + // module are emitted as Local Procedure symbols. Functions not + // specifically declared static are emitted as Global Procedures. + // + // GPROC32 records are used for procedures that are not specifically + // declared static to a module. The format is the same as the Local + // Procedure Start 16:32 symbol. + + /** Creates a new symbol iterator pointing to the symbol opening the + enclosing lexical scope of this function (if any); returns null + if there is no enclosing scope. */ + public DebugVC50SymbolIterator getLGProcParent(); + + /** Gets the absolute file offset of the parent symbol, or 0 if + none. This is useful for constructing and resolving types in a + lazy fashion. */ + public int getLGProcParentOffset(); + + /** Creates a new symbol iterator pointing to the block end symbol + terminating the lexical scope, or NULL if there is no containing + lexical scope. */ + public DebugVC50SymbolIterator getLGProcEnd(); + + /** Gets the absolute file offset of the end symbol. This is useful + for constructing and resolving types in a lazy fashion. */ + public int getLGProcEndOffset(); + + /** Creates a new symbol iterator pointing to the next outermost + scope symbol in the segment (if any); returns null if this is + the last outermost scope for the current segment. (See the + documentation for more information.) */ + public DebugVC50SymbolIterator getLGProcNext(); + + /** Gets the absolute file offset of the next symbol, or 0 if none. + This is useful for constructing and resolving types in a lazy + fashion. */ + public int getLGProcNextOffset(); + + /** Length in bytes of this procedure. */ + public int getLGProcLength(); + + /** Offset in bytes from the start of the procedure to the point + where the stack frame has been set up. Parameter and frame + variables can be viewed at this point. */ + public int getLGProcDebugStart(); + + /** Offset in bytes from the start of the procedure to the point + where the procedure is ready to return and has calculated its + return value, if any. Frame and register variables can still be + viewed. */ + public int getLGProcDebugEnd(); + + /** Type of the procedure type record. */ + public int getLGProcType(); + + /** Offset portion of the procedure address. */ + public int getLGProcOffset(); + + /** Segment portion of the procedure address. */ + public short getLGProcSegment(); + + /** Value defined by bitwise or of the the PROCFLAGS enumeration in + {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SymbolEnums}. */ + public byte getLGProcFlags(); + + /** Length-prefixed name of procedure. */ + public String getLGProcName(); + + ///////////////////////// + // S_THUNK32 accessors // + ///////////////////////// + + // This record is used to specify any piece of code that exists + // outside a procedure. It is followed by an End record. The thunk + // record is intended for small code fragments. and a two byte + // length field is sufficient for its intended purpose. + + /** Creates a new symbol iterator pointing to the symbol opening the + enclosing lexical scope of this thunk (if any); returns null if + there is no enclosing scope. */ + public DebugVC50SymbolIterator getThunkParent(); + + /** Gets the absolute file offset of the parent symbol, or 0 if + none. This is useful for constructing and resolving types in a + lazy fashion. */ + public int getThunkParentOffset(); + + /** Creates a new symbol iterator pointing to the block end symbol + terminating the lexical scope, or NULL if there is no containing + lexical scope. */ + public DebugVC50SymbolIterator getThunkEnd(); + + /** Gets the absolute file offset of the end symbol. This is useful + for constructing and resolving types in a lazy fashion. */ + public int getThunkEndOffset(); + + /** Creates a new symbol iterator pointing to the next outermost + scope symbol in the segment (if any); returns null if this is + the last outermost scope for the current segment. (See the + documentation for more information.) */ + public DebugVC50SymbolIterator getThunkNext(); + + /** Gets the absolute file offset of the next symbol, or 0 if none. + This is useful for constructing and resolving types in a lazy + fashion. */ + public int getThunkNextOffset(); + + /** Offset portion of the thunk address. */ + public int getThunkOffset(); + + /** Segment portion of the procedure address. */ + public short getThunkSegment(); + + /** Length in bytes of this thunk. */ + public short getThunkLength(); + + /** Ordinal specifying the type of thunk; see THUNK enumeration in + {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SymbolEnums}. */ + public byte getThunkType(); + + /** Length-prefixed name of thunk. */ + public String getThunkName(); + + /** Delta to be added to "this" pointer; only valid if thunk type is + "adjustor". */ + public short getThunkAdjustorThisDelta(); + + /** Length-prefixed name of target function; only valid if thunk type is + "adjustor". */ + public String getThunkAdjustorTargetName(); + + /** Displacement into the virtual table; only valid if thunk type is + "vcall". */ + public short getThunkVCallDisplacement(); + + /** Offset of p-code entry point; only valid if thunk type is + "pcode". */ + public int getThunkPCodeOffset(); + + /** Segment of p-code entry point; only valid if thunk type is + "pcode". */ + public short getThunkPCodeSegment(); + + ///////////////////////// + // S_BLOCK32 accessors // + ///////////////////////// + + // This symbol specifies the start of an inner block of lexically + // scoped symbols. The lexical scope is terminated by a matching + // S_END symbol. + + /** Creates a new symbol iterator pointing to the symbol opening the + enclosing lexical scope of this scope (if any); returns null if + there is no enclosing scope. */ + public DebugVC50SymbolIterator getBlockParent(); + + /** Gets the absolute file offset of the parent symbol, or 0 if + none. This is useful for constructing and resolving types in a + lazy fashion. */ + public int getBlockParentOffset(); + + /** Creates a new symbol iterator pointing to the block end symbol + terminating this scope. */ + public DebugVC50SymbolIterator getBlockEnd(); + + /** Gets the absolute file offset of the end symbol. This is useful + for constructing and resolving types in a lazy fashion. */ + public int getBlockEndOffset(); + + /** Length in bytes of the scope of this block. */ + public int getBlockLength(); + + /** Offset portion of the segmented procedure address. */ + public int getBlockOffset(); + + /** Segment portion of the segmented procedure address. */ + public short getBlockSegment(); + + /** Length-prefixed name of the block. */ + public String getBlockName(); + + //////////////////////// + // S_WITH32 accessors // + //////////////////////// + + // FIXME: this is a Pascal construct; ignored for now + + ///////////////////////// + // S_LABEL32 accessors // + ///////////////////////// + + /** Offset portion of the segmented address of the start of the + block. */ + public int getLabelOffset(); + + /** Segment portion of the segmented address of the start of the + block. */ + public short getLabelSegment(); + + /** Label flags. These are the same as the PROCFLAGS enumeration. */ + public byte getLabelFlags(); + + /** Length prefixed name of label. */ + public String getLabelName(); + + //////////////////////////// + // S_CEXMODEL32 accessors // + //////////////////////////// + + // This record is used to notify the debugger that, starting at the + // given code offset and until the address specified by the next + // Change Execution Model record, the execution model is of the + // specified type. The native execution model is assumed in the + // absence of Change Execution Model records. + + /** Offset portion of start of the block where the change occurs. */ + public int getChangeOffset(); + + /** Segment portion of start of the block where the change occurs. */ + public short getChangeSegment(); + + /** The execution model, enumerated in EXMODEL constants in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SymbolEnums}. */ + public short getChangeModel(); + + // FIXME: figure out how to deal with variant (or whether it is + // necessary) + + //////////////////////////// + // S_VFTTABLE32 accessors // + //////////////////////////// + + // This record is used to describe the base class path for the + // virtual function table descriptor. + + /** The type index of the class at the root of the path. */ + public int getVTableRoot(); + + /** Type index of the record describing the base class path from the + root to the leaf class for the virtual function table. */ + public int getVTablePath(); + + /** Offset portion of start of the virtual function table. */ + public int getVTableOffset(); + + /** Segment portion of the virtual function table. */ + public short getVTableSegment(); + + ////////////////////////// + // S_REGREL32 accessors // + ////////////////////////// + + // This symbol specifies symbols that are allocated relative to a + // register. + + /** Signed offset relative to register. */ + public int getRegRelOffset(); + + /** Type of the symbol. */ + public int getRegRelType(); + + /** Register enumerates on which the symbol is based. Note that the + register field can specify a pair of register such as ES:EBX. */ + public short getRegRelRegister(); + + /** Length-prefixed name of the symbol. */ + public String getRegRelName(); + + /////////////////////////////////////////// + // S_LTHREAD32 and S_GTHREAD32 accessors // + /////////////////////////////////////////// + + // These symbols are used for data declared with the __thread + // storage attribute that is not exported from a module. In C/C++, + // __thread symbols that are declared static are emitted as Local + // Thread Storage 16:32 symbols. Symbols that are emitted as Local + // Thread Storage 16:32 cannot be moved by CVPACK into the global + // symbol table for the executable file. __thread symbols that are + // not specifically declared static are emitted as Global Thread + // Storage 16:32 symbols and can be compacted by CVPACK into the + // global symbol table. + + /** Type index. */ + public int getLThreadType(); + + /** Offset into thread local storage. */ + public int getLThreadOffset(); + + /** Segment of thread local storage. */ + public short getLThreadSegment(); + + /** Length prefixed name. */ + public String getLThreadName(); + + // NOTE: accessors for all other kinds of symbols (i.e., MIPS) + // elided for now (FIXME) +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SymbolTypes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SymbolTypes.java new file mode 100644 index 00000000000..f1162bb800c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50SymbolTypes.java @@ -0,0 +1,145 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/**

Enumerates the types of symbols returned by the {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SymbolIterator}. (Some + of the descriptions are taken directly from Microsoft's + documentation and are copyrighted by Microsoft.)

+ +

NOTE that these types are specified as integers rather than + short integers; this is to make comparisons and switches simpler + because of Java's automatic sign extension.

+*/ + +public interface DebugVC50SymbolTypes { + /** Compile flags symbol */ + public static final int S_COMPILE = 0x0001; + + /** Start search */ + public static final int S_SSEARCH = 0x0005; + + /** End block, procedure, with, or thunk */ + public static final int S_END = 0x0006; + + /** Reserve symbol space */ + public static final int S_SKIP = 0x0007; + + /** Reserved for CodeView internal use */ + public static final int S_CVRESERVE = 0x0008; + + /** Specify name of object file */ + public static final int S_OBJNAME = 0x0009; + + /** Specify end of arguments in function symbols */ + public static final int S_ENDARG = 0x000a; + + /** Microfocus COBOL user-defined type */ + public static final int S_COBOLUDT = 0x000b; + + /** Many register symbol */ + public static final int S_MANYREG = 0x000c; + + /** Function return description */ + public static final int S_RETURN = 0x000d; + + /** Description of this pointer at entry */ + public static final int S_ENTRYTHIS = 0x000e; + + /** Register variable */ + public static final int S_REGISTER = 0x1001; + + /** Constant symbol */ + public static final int S_CONSTANT = 0x1002; + + /** User-defined type */ + public static final int S_UDT = 0x1003; + + /** Microfocus COBOL User-defined type (#2) */ + public static final int S_COBOLUDT2 = 0x1004; + + /** Many register symbol (#2) */ + public static final int S_MANYREG2 = 0x1005; + + /** BP relative 16:32 */ + public static final int S_BPREL32 = 0x1006; + + /** Local data 16:32 */ + public static final int S_LDATA32 = 0x1007; + + /** Global data 16:32 */ + public static final int S_GDATA32 = 0x1008; + + /** Public symbol 16:32 */ + public static final int S_PUB32 = 0x1009; + + /** Local procedure start 16:32 */ + public static final int S_LPROC32 = 0x100a; + + /** Global procedure start 16:32 */ + public static final int S_GPROC32 = 0x100b; + + /** Thunk start 16:32 */ + public static final int S_THUNK32 = 0x0206; + + /** Block start 16:32 */ + public static final int S_BLOCK32 = 0x0207; + + /** With start 16:32 */ + public static final int S_WITH32 = 0x0208; + + /** Label 16:32 */ + public static final int S_LABEL32 = 0x0209; + + /** Change execution model 16:32 */ + public static final int S_CEXMODEL32 = 0x020a; + + /** Virtual function table path descriptor 16:32 */ + public static final int S_VFTTABLE32 = 0x100c; + + /** 16:32 offset relative to arbitrary register */ + public static final int S_REGREL32 = 0x100d; + + /** Local Thread Storage data */ + public static final int S_LTHREAD32 = 0x100e; + + /** Global Thread Storage data */ + public static final int S_GTHREAD32 = 0x100f; + + /** Local procedure start MIPS */ + public static final int S_LPROCMIPS = 0x1010; + + /** Global procedure start MIPS */ + public static final int S_GPROCMIPS = 0x1011; + + /** Reference to a procedure */ + public static final int S_PROCREF = 0x0400; + + /** Reference to data */ + public static final int S_DATAREF = 0x0401; + + /** Page align symbols */ + public static final int S_ALIGN = 0x0402; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50TypeEnums.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50TypeEnums.java new file mode 100644 index 00000000000..08b8e0584a2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50TypeEnums.java @@ -0,0 +1,321 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Various enumerated values used in type leaves */ + +public interface DebugVC50TypeEnums { + /** LF_MODIFIER attributes */ + public static final int MODIFIER_CONST_MASK = 0x01; + public static final int MODIFIER_VOLATILE_MASK = 0x02; + public static final int MODIFIER_UNALIGNED_MASK = 0x04; + + /** LF_POINTER bitfields */ + // FIXME: verify these are correct + // ptrtype field + public static final int POINTER_PTRTYPE_MASK = 0x0000001F; + public static final int POINTER_PTRTYPE_SHIFT = 0; + public static final int POINTER_PTRTYPE_NEAR = 0; + public static final int POINTER_PTRTYPE_FAR = 1; + public static final int POINTER_PTRTYPE_HUGE = 2; + /** Obsolete */ + public static final int POINTER_PTRTYPE_BASED_ON_SEGMENT = 3; + public static final int POINTER_PTRTYPE_BASED_ON_VALUE = 4; + /** Obsolete */ + public static final int POINTER_PTRTYPE_BASED_ON_SEGMENT_OF_VALUE = 5; + /** Obsolete */ + public static final int POINTER_PTRTYPE_BASED_ON_ADDRESS_OF_SYMBOL = 6; + /** Obsolete */ + public static final int POINTER_PTRTYPE_BASED_ON_SEGMENT_OF_SYMBOL_ADDRESS = 7; + public static final int POINTER_PTRTYPE_BASED_ON_TYPE = 8; + /** Obsolete */ + public static final int POINTER_PTRTYPE_BASED_ON_SELF = 9; + public static final int POINTER_PTRTYPE_NEAR_32_BIT = 10; + public static final int POINTER_PTRTYPE_FAR_32_BIT = 11; + public static final int POINTER_PTRTYPE_64_BIT = 12; + + // ptrmode field + // FIXME: verify these are correct + public static final int POINTER_PTRMODE_MASK = 0x000000E0; + public static final int POINTER_PTRMODE_SHIFT = 5; + public static final int POINTER_PTRMODE_POINTER = 0; + public static final int POINTER_PTRMODE_REFERENCE = 1; + public static final int POINTER_PTRMODE_PTR_TO_DATA_MEMBER = 2; + public static final int POINTER_PTRMODE_PTR_TO_METHOD = 3; + + // FIXME: verify this is correct + public static final int POINTER_ISFLAT32_MASK = 0x00000100; + + // FIXME: verify this is correct + public static final int POINTER_VOLATILE_MASK = 0x00000200; + + // FIXME: verify this is correct + public static final int POINTER_CONST_MASK = 0x00000400; + + // FIXME: verify this is correct + public static final int POINTER_UNALIGNED_MASK = 0x00000800; + + // FIXME: verify this is correct + public static final int POINTER_RESTRICT_MASK = 0x00001000; + + /**

16:32 data for classes with or without virtual functions and + no virtual bases. Pointer layout is:

+ +

+ +
4 +
mdisp +
+

+ +

+ mdisp: displacement to data +

+ +

+ NULL value is 0x80000000. +

+ */ + public static final short PTR_FORMAT_DATA_NVF_NVB = (short) 3; + + /**

16:32 data for class with virtual bases. Pointer layout is:

+ +

+ +
4 4 4 +
mdisp pdisp> vdisp +
+

+ +

+ mdisp: displacement to data +

+ +

+ pdisp: this pointer displacement to virtual base table pointer +

+ +

+ vdisp: displacement within virtual base table +

+ +

+ NULL value is (*,*,0xffffffff). +

+ */ + public static final short PTR_FORMAT_DATA_VB = (short) 4; + + /**

16:32 method nonvirtual bases with single address point. + Pointer layout is:

+ +

+ +
4 +
off +
+

+ +

+ off: offset of function +

+ +

+ NULL value is 0L. +

+ */ + public static final short PTR_FORMAT_METHOD_NVB_SAP = (short) 11; + + /**

16:32 method nonvirtual bases with multiple address points. + Pointer layout is:

+ +

+ +
4 4 +
off disp +
+

+ +

+ off: offset of function +

+ +

+ disp: displacement of address point. +

+ +

+ NULL value is (0L : 0L). +

+ */ + public static final short PTR_FORMAT_METHOD_NVB_MAP = (short) 12; + + /**

16:32 method with virtual bases. Pointer layout is:

+ +

+ +
4 4 4 4 +
off mdisp pdisp vdisp +
+

+ +

+ off: offset of function +

+ +

+ mdisp: displacement to data +

+ +

+ pdisp: this pointer displacement to virtual base + table pointer +

+ +

+ vdisp: displacement within virtual base table +

+ + NULL value is (0L, *, *, *). + */ + public static final short PTR_FORMAT_METHOD_VB = (short) 13; + + /** Class, structure, union, and enum properties */ + // FIXME: verify these are correct + /** Structure is packed */ + public static final short PROPERTY_PACKED = (short) 0x001; + /** Class has constructors and/or destructors */ + public static final short PROPERTY_CTOR = (short) 0x002; + /** Class has overloaded operators */ + public static final short PROPERTY_OVEROPS = (short) 0x004; + /** Class is a nested class */ + public static final short PROPERTY_ISNESTED = (short) 0x008; + /** Class contains nested classes */ + public static final short PROPERTY_CNESTED = (short) 0x010; + /** Class has overloaded assignment */ + public static final short PROPERTY_OPASSIGN = (short) 0x020; + /** Class has casting methods */ + public static final short PROPERTY_OPCAST = (short) 0x040; + /** Class/structure is a forward (incomplete) reference */ + public static final short PROPERTY_FWDREF = (short) 0x080; + /** This is a scoped definition */ + public static final short PROPERTY_SCOPED = (short) 0x100; + + /** Calling conventions */ + /** Arguments pushed right to left, caller pops arguments. */ + public static final byte CALLCONV_NEAR_C = (byte) 0; + public static final byte CALLCONV_FAR_C = (byte) 1; + public static final byte CALLCONV_NEAR_PASCAL = (byte) 2; + /** Arguments pushed left to right, callee pops arguments. */ + public static final byte CALLCONV_FAR_PASCAL = (byte) 3; + public static final byte CALLCONV_NEAR_FASTCALL = (byte) 4; + public static final byte CALLCONV_FAR_FASTCALL = (byte) 5; + public static final byte CALLCONV_RESERVED = (byte) 6; + public static final byte CALLCONV_NEAR_STDCALL = (byte) 7; + public static final byte CALLCONV_FAR_STDCALL = (byte) 8; + public static final byte CALLCONV_NEAR_SYSCALL = (byte) 9; + public static final byte CALLCONV_FAR_SYSCALL = (byte) 10; + public static final byte CALLCONV_THIS_CALL = (byte) 11; + public static final byte CALLCONV_MIPS_CALL = (byte) 12; + public static final byte CALLCONV_GENERIC = (byte) 13; + + /** vtable entry descriptors */ + public static final int VTENTRY_NEAR = 0; + public static final int VTENTRY_FAR = 1; + public static final int VTENTRY_THIN = 2; + /** Address point displacement to outermost class. This is at + entry[-1] from table address. */ + public static final int VTENTRY_ADDRESS_PT_DISP = 3; + /** Far pointer to metaclass descriptor. This is at entry[-2] from + table address. */ + public static final int VTENTRY_FAR_PTR_TO_METACLASS = 4; + public static final int VTENTRY_NEAR_32 = 5; + public static final int VTENTRY_FAR_32 = 6; + + /** Label addressing modes */ + public static final short LABEL_ADDR_MODE_NEAR = (short) 0; + public static final short LABEL_ADDR_MODE_FAR = (short) 4; + + // + // Primitive/reserved type enumerations + // + + // FIXME: verify these are correct + // Type field + public static final int RESERVED_TYPE_MASK = 0x070; + public static final int RESERVED_TYPE_SPECIAL = 0x000; + public static final int RESERVED_TYPE_SIGNED_INT = 0x010; + public static final int RESERVED_TYPE_UNSIGNED_INT = 0x020; + public static final int RESERVED_TYPE_BOOLEAN = 0x030; + public static final int RESERVED_TYPE_REAL = 0x040; + public static final int RESERVED_TYPE_COMPLEX = 0x050; + public static final int RESERVED_TYPE_SPECIAL2 = 0x060; + public static final int RESERVED_TYPE_REALLY_INT = 0x070; + + // Mode field + public static final int RESERVED_MODE_MASK = 0x700; + public static final int RESERVED_MODE_DIRECT = 0x000; + public static final int RESERVED_MODE_NEAR_PTR = 0x100; + public static final int RESERVED_MODE_FAR_PTR = 0x200; + public static final int RESERVED_MODE_HUGE_PTR = 0x300; + public static final int RESERVED_MODE_NEAR_32_PTR = 0x400; + public static final int RESERVED_MODE_FAR_32_PTR = 0x500; + public static final int RESERVED_MODE_NEAR_64_PTR = 0x600; + + // Size field for each of the types above. + // Has different meanings based on type. + public static final int RESERVED_SIZE_MASK = 0x7; + // Special type + public static final int RESERVED_SIZE_SPECIAL_NO_TYPE = 0x0; + public static final int RESERVED_SIZE_SPECIAL_ABSOLUTE_SYMBOL = 0x1; + public static final int RESERVED_SIZE_SPECIAL_SEGMENT = 0x2; + public static final int RESERVED_SIZE_SPECIAL_VOID = 0x3; + public static final int RESERVED_SIZE_SPECIAL_BASIC_8_BYTE = 0x4; + public static final int RESERVED_SIZE_SPECIAL_NEAR_BASIC_STRING = 0x5; + public static final int RESERVED_SIZE_SPECIAL_FAR_BASIC_STRING = 0x6; + public static final int RESERVED_SIZE_SPECIAL_UNTRANSLATED = 0x7; + // Signed, unsigned and boolean types + public static final int RESERVED_SIZE_INT_1_BYTE = 0x0; + public static final int RESERVED_SIZE_INT_2_BYTE = 0x1; + public static final int RESERVED_SIZE_INT_4_BYTE = 0x2; + public static final int RESERVED_SIZE_INT_8_BYTE = 0x3; + // Real and complex types + public static final int RESERVED_SIZE_REAL_32_BIT = 0x0; + public static final int RESERVED_SIZE_REAL_64_BIT = 0x1; + public static final int RESERVED_SIZE_REAL_80_BIT = 0x2; + public static final int RESERVED_SIZE_REAL_128_BIT = 0x3; + public static final int RESERVED_SIZE_REAL_48_BIT = 0x4; + // Special2 type + public static final int RESERVED_SIZE_SPECIAL2_BIT = 0x0; + public static final int RESERVED_SIZE_SPECIAL2_PASCAL_CHAR = 0x1; + // Really int type + public static final int RESERVED_SIZE_REALLY_INT_CHAR = 0x0; + public static final int RESERVED_SIZE_REALLY_INT_WCHAR = 0x1; + public static final int RESERVED_SIZE_REALLY_INT_2_BYTE = 0x2; // 2 byte signed integer + public static final int RESERVED_SIZE_REALLY_INT_2_BYTE_U = 0x3; // 2 byte unsigned integer + public static final int RESERVED_SIZE_REALLY_INT_4_BYTE = 0x4; // 4 byte signed integer + public static final int RESERVED_SIZE_REALLY_INT_4_BYTE_U = 0x5; // 4 byte unsigned integer + public static final int RESERVED_SIZE_REALLY_INT_8_BYTE = 0x6; // 8 byte signed integer + public static final int RESERVED_SIZE_REALLY_INT_8_BYTE_U = 0x7; // 8 byte unsigned integer +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50TypeIterator.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50TypeIterator.java new file mode 100644 index 00000000000..f127f1f5086 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50TypeIterator.java @@ -0,0 +1,868 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +import java.util.NoSuchElementException; + +/**

Provides iteration-style access to the types in the + sstGlobalTypes subsection of the VC++ 5.0 debug + information. Clients should walk down these platform-dependent + types and transform them into the platform-independent interfaces + described in the package sun.jvm.hotspot.debugger.csym.

+ +

This iterator is a "two-dimensional" iterator; it iterates not + only over all of the types in the type table, but also iterates + over the leaf types in the current type string. This structure was + chosen to avoid constructing a new type iterator for each type in + the type table because of the expected large number of types.

+*/ + +public interface DebugVC50TypeIterator { + // + // Iteration through type table + // + + /** Indicates whether the iteration through the type table is + complete. */ + public boolean done(); + + /** Go to the next type in the type table. NOTE that the iterator is + pointing at the first type initially, so one should use a while + (!iter.done()) { ... iter.next(); } construct. + + @throw NoSuchElementException if the iterator is already done + and next() is called. */ + public void next() throws NoSuchElementException; + + /** Gets the length, in bytes, of the current type record. */ + public short getLength(); + + /** Gets the type index of the current type. This number is + compatible with type references in symbols and type records. */ + public int getTypeIndex(); + + /** Debugging support only */ + public int getNumTypes(); + + // + // Iteration through type strings + // + + /** Indicates whether iteration through the current type string is + complete. */ + public boolean typeStringDone(); + + /** Goes to the next element in the current type string. NOTE that + the iterator is pointing at the first type initially, so one + should use a while (!iter.typeStringDone()) { ... + iter.typeStringNext(); } construct. + + @throw NoSuchElementException if the iterator is already done + and typeStringNext() is called. */ + public void typeStringNext() throws NoSuchElementException; + + /** Return the leaf index (see {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeLeafIndices}) + for the current element of the current type string. */ + public int typeStringLeaf(); + + /** For debugging: returns the file offset of the current type + string leaf. */ + public int typeStringOffset(); + + // + // Leaf Indices Referenced from Symbols + // + + /////////////////////////// + // LF_MODIFIER accessors // + /////////////////////////// + + // This record is used to indicate the const,r volatile and + // unaligned properties for any particular type. + + /** Type index of the modified type. */ + public int getModifierIndex(); + + /** Attributes specified in MODIFIER_ enums in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeEnums}. */ + public short getModifierAttribute(); + + ////////////////////////// + // LF_POINTER accessors // + ////////////////////////// + + /** Type index of object pointed to. */ + public int getPointerType(); + + /** Pointer attributes. Consists of seven bit fields whose + enumerants are in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeEnums}: + PTRTYPE, PTRMODE, ISFLAT32, VOLATILE, CONST, UNALIGNED, and + RESTRICT. */ + public int getPointerAttributes(); + + /** Only valid if the pointer type is BASED_ON_TYPE; retrieves index + of type. */ + public int getPointerBasedOnTypeIndex(); + + /** Only valid if the pointer type is BASED_ON_TYPE; retrieves name + of type. */ + public String getPointerBasedOnTypeName(); + + /** Only valid if the pointer mode is either PTR_TO_DATA_MEMBER or + PTR_TO_METHOD; retrieves the type index of the containing + class. */ + public int getPointerToMemberClass(); + + /** Only valid if the pointer mode is either PTR_TO_DATA_MEMBER or + PTR_TO_METHOD; retrieves the data format of the pointer in + memory. See the PTR_FORMAT enum in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeEnums}. */ + public short getPointerToMemberFormat(); + + //////////////////////// + // LF_ARRAY accessors // + //////////////////////// + + /** Type index of each array element. */ + public int getArrayElementType(); + + /** Type index of indexing variable. */ + public int getArrayIndexType(); + + /** Length of the array in bytes. */ + public int getArrayLength() throws DebugVC50WrongNumericTypeException; + + /** Length-prefixed name of array. */ + public String getArrayName(); + + ///////////////////////////////////////// + // LF_CLASS and LF_STRUCTURE accessors // + ///////////////////////////////////////// + + /** Number of elements in the class or structure. This count + includes direct, virtual, and indirect virtual bases, and + methods including overloads, data members, static data members, + friends, and so on. */ + public short getClassCount(); + + /** Property bit field; see PROPERTY_ enumeration in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeEnums}. */ + public short getClassProperty(); + + /** Type index of the field list for this class. */ + public int getClassFieldList(); + + /** Get new iterator pointing at the field list of this class. */ + public DebugVC50TypeIterator getClassFieldListIterator(); + + /** Type index of the derivation list. This is output by the + compiler as 0x0000 and is filled in by the CVPACK utility to a + LF_DERIVED record containing the type indices of those classes + which immediately inherit the current class. A zero index + indicates that no derivation information is available. A LF_NULL + index indicates that the class is not inherited by other + classes. */ + public int getClassDerivationList(); + + /** Type index of the virtual function table shape descriptor. */ + public int getClassVShape(); + + /** Numeric leaf specifying size in bytes of the structure. */ + public int getClassSize() throws DebugVC50WrongNumericTypeException; + + /** Length-prefixed name of this type. */ + public String getClassName(); + + //////////////////////// + // LF_UNION accessors // + //////////////////////// + + /** Number of fields in the union. */ + public short getUnionCount(); + + /** Property bit field. */ + public short getUnionProperty(); + + /** Type index of field list. */ + public int getUnionFieldList(); + + /** Get new iterator pointing at the field list of this union. */ + public DebugVC50TypeIterator getUnionFieldListIterator(); + + /** Numeric leaf specifying size in bytes of the union. */ + public int getUnionSize() throws DebugVC50WrongNumericTypeException; + + /** Length-prefixed name of union. */ + public String getUnionName(); + + /////////////////////// + // LF_ENUM accessors // + /////////////////////// + + /** Number of enumerates. */ + public short getEnumCount(); + + /** Property bit field. */ + public short getEnumProperty(); + + /** Index of underlying type of enum. */ + public int getEnumType(); + + /** Type index of field list. */ + public int getEnumFieldList(); + + /** Get new iterator pointing at the field list of this enum. */ + public DebugVC50TypeIterator getEnumFieldListIterator(); + + /** Length-prefixed name of enum. */ + public String getEnumName(); + + //////////////////////////// + // LF_PROCEDURE accessors // + //////////////////////////// + + /** Type index of the value returned by the procedure. */ + public int getProcedureReturnType(); + + /** Calling convention of the procedure; see CALLCONV_ enumeration + in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeEnums}. */ + public byte getProcedureCallingConvention(); + + /** Number of parameters. */ + public short getProcedureNumberOfParameters(); + + /** Type index of argument list type record. */ + public int getProcedureArgumentList(); + + /** Get new iterator pointing at the argument list of this procedure. */ + public DebugVC50TypeIterator getProcedureArgumentListIterator(); + + //////////////////////////// + // LF_MFUNCTION accessors // + //////////////////////////// + + /** Type index of the value returned by the procedure. */ + public int getMFunctionReturnType(); + + /** Type index of the containing class of the function. */ + public int getMFunctionContainingClass(); + + /** Type index of the this parameter of the member function. + A type of void indicates that the member function is static and + has no this parameter. */ + public int getMFunctionThis(); + + /** Calling convention of the procedure; see CALLCONV_ enumeration + in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeEnums}. */ + public byte getMFunctionCallingConvention(); + + /** Number of parameters. This count does not include the + this parameter. */ + public short getMFunctionNumberOfParameters(); + + /** List of parameter specifiers. This list does not include the + this parameter. */ + public int getMFunctionArgumentList(); + + /** Get new iterator pointing at the argument list of this member function. */ + public DebugVC50TypeIterator getMFunctionArgumentListIterator(); + + /** Logical this adjustor for the method. Whenever a class + element is referenced via the this pointer, thisadjust + will be added to the resultant offset before referencing the + element. */ + public int getMFunctionThisAdjust(); + + ////////////////////////// + // LF_VTSHAPE accessors // + ////////////////////////// + + // This record describes the format of a virtual function table. + // This record is accessed via the vfunctabptr in the member list of + // the class which introduces the virtual function. The vfunctabptr + // is defined either by the LF_VFUNCTAB or LF_VFUNCOFF member + // record. If LF_VFUNCTAB record is used, then vfunctabptr is at the + // address point of the class. If LF_VFUNCOFF record is used, then + // vfunctabptr is at the specified offset from the class address + // point. The underlying type of the pointer is a VTShape type + // record. This record describes how to interpret the memory at the + // location pointed to by the virtual function table pointer. + + /** Number of descriptors. */ + public short getVTShapeCount(); + + /** Fetch the ith descriptor (0..getVTShapeCount() - 1). Each + descriptor is a 4-bit (half-byte) value described by the + VTENTRY_ enumeration in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeEnums}. */ + public int getVTShapeDescriptor(int i); + + // + // NOTE: LF_COBOL0, LF_COBOL1 accessors elided (FIXME) + // + + ///////////////////////// + // LF_BARRAY accessors // + ///////////////////////// + + /** Type of each element of the array. */ + public int getBasicArrayType(); + + //////////////////////// + // LF_LABEL accessors // + //////////////////////// + + /** Addressing mode of the label, described by LABEL_ADDR_MODE_ enum + in {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeEnums}. */ + public short getLabelAddressMode(); + + // + // LF_NULL, LF_NOTTRANS have no data + // + + /////////////////////////// + // LF_DIMARRAY accessors // + /////////////////////////// + + /** Underlying type of the array. */ + public int getDimArrayType(); + + /** Index of the type record containing the dimension information. */ + public int getDimArrayDimInfo(); + + /** Length-prefixed name of the array. */ + public String getDimArrayName(); + + ////////////////////////// + // LF_VFTPATH accessors // + ////////////////////////// + + /** Count of number of bases in the path to the virtual function + table. */ + public int getVFTPathCount(); + + /** Type indices of the base classes in the path + (0..getVFTPathCount() - 1). */ + public int getVFTPathBase(int i); + + // + // NOTE: LF_PRECOMP and LF_ENDPRECOMP accessors elided because the + // signature contained within is extremely compiler-specific and is + // left undefined in the specification, so is not useful. (FIXME) + // + + // + // NOTE: LF_OEM accessors elided because we will not need to parse + // vendor-specific debug information (yet). (FIXME) + // + + // + // NOTE: LF_TYPESERVER accessors elided because we will not be using + // this library in conjunction with a program database. (FIXME) + // + + // + // Type Records Referenced from Type Records + // + + /////////////////////// + // LF_SKIP accessors // + /////////////////////// + + /** In processing $$TYPES, the index counter is advanced to index + count, skipping all intermediate indices. This is the next valid + index. */ + public int getSkipIndex(); + + ////////////////////////// + // LF_ARGLIST accessors // + ////////////////////////// + + /** Count of number of indices in list. */ + public int getArgListCount(); + + /** List of type indices (0..getArgListCount() - 1) for describing + the formal parameters to a function or method. */ + public int getArgListType(int i); + + ///////////////////////// + // LF_DEFARG accessors // + ///////////////////////// + + /** Type index of resulting expression. */ + public int getDefaultArgType(); + + /** Length-prefixed string of supplied default expression. */ + public String getDefaultArgExpression(); + + // + // Field list accessors (LF_FIELDLIST) + // + // No explicit accessors for the field list. The field list is + // structured similarly to most type strings; it is a series of + // leaves. LF_INDEX leaves are used to split the field list if it + // gets long enough that it will cross a 48K boundary; LF_PAD leaves + // are used to enforce proper alignment. Both of these leaves, and + // their lengths, are understood by this iterator, and LF_INDEX + // leaves have an accessor for reaching the target type record. + // + + ////////////////////////// + // LF_DERIVED accessors // + ////////////////////////// + + // This type record specifies all of the classes that are directly + // derived from the class that references this type record. + + /** Number of types in the list. */ + public int getDerivedCount(); + + /** Fetch ith derived type (0..getDerivedCount() - 1). */ + public int getDerivedType(int i); + + /////////////////////////// + // LF_BITFIELD accessors // + /////////////////////////// + + // Bit fields are represented by an entry in the field list that + // indexes a bit field type definition. + + /** Type index of the field. */ + public int getBitfieldFieldType(); + + /** The length in bits of the object. */ + public byte getBitfieldLength(); + + /** Starting position (from bit 0) of the object in the word. */ + public byte getBitfieldPosition(); + + //////////////////////// + // LF_MLIST accessors // + //////////////////////// + + // This record is typically used to describe overloaded methods, + // though it can also be used (inefficiently) to describe a single + // method. It is referenced from the LF_METHOD record. The "count" + // is not really contained in this record; it is contained within + // the LF_METHOD record which points to this one. However, it seems + // it can be inferred from the length of this type string as the + // only repeated portion of the record is the type of each + // overloaded variant. + // + // Once a method has been found in this list, its symbol is found by + // qualifying the method name with its class (T::name) and then + // searching the symbol table for a symbol by that name with the + // correct type index. Note that the number of repeats is determined + // by the subleaf of the field list that references this LF_MLIST + // record. + + /** Attribute of the member function; see {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeEnums} and {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50MemberAttributes}. */ + public short getMListAttribute(); + + /** Number of types corresponding to this overloaded method. FIXME: + must verify this can be inferred solely from this record's + length. */ + public int getMListLength(); + + /** Type index of the procedure record for the ith occurrence + of the function (0..getMListLength() - 1). */ + public int getMListType(int i); + + /** Convenience routine indicating whether this member function is + introducing virtual. */ + public boolean isMListIntroducingVirtual(); + + /** Present only when property attribute is introducing virtual + (optional). Offset in vtable of the class which contains the + pointer to the function. (FIXME: is this on a per-method or + per-method list basis? If the latter, will have to provide an + iterator for this record.) */ + public int getMListVtabOffset(); + + // + // NOTE: LF_DIMCONU, LF_DIMCONLU, LF_DIMVARU, and LF_DIMVARLU + // accessors elided as these are very likely Fortran-specific + // (FIXME?) + // + + ///////////////////////// + // LF_REFSYM accessors // + ///////////////////////// + + // This record is used to describe a symbol that is referenced by a + // type record. The record is defined because type records cannot + // reference symbols or locations in the $$SYMBOLS table because + // global symbol compaction will move symbols. + + /** Create a new SymbolIterator pointing at the copy of the symbol + this record contains. */ + public DebugVC50SymbolIterator getRefSym(); + + // + // Subfields of complex lists + // + + ///////////////////////// + // LF_BCLASS accessors // + ///////////////////////// + + // This leaf specifies a real base class. If a class inherits real + // base classes, the corresponding REAL Base Class records will + // precede all other member records in the field list of that + // class. Base class records are emitted in left to right + // declaration order for real bases. + + /** Member attribute bit field. */ + public short getBClassAttribute(); + + /** Index to type record of the class. The class name can be + obtained from this record. */ + public int getBClassType(); + + /** Offset of subobject that represents the base class within the + structure. */ + public int getBClassOffset() throws DebugVC50WrongNumericTypeException; + + ////////////////////////// + // LF_VBCLASS accessors // + ////////////////////////// + + // This leaf specifies a directly inherited virtual base class. If a + // class directly inherits virtual base classes, the corresponding + // Direct Virtual BaseClass records will follow all Real Base Class + // member records and precede all other member records in the field + // list of that class. Direct Virtual Base class records are emitted + // in bottommost left-to-right inheritance order for directly + // inherited virtual bases. + + /** Member attribute bit field. */ + public short getVBClassAttribute(); + + /** Index to type record of the direct or indirect virtual base + class. The class name can be obtained from this record. */ + public int getVBClassBaseClassType(); + + /** Type index of the virtual base pointer for this base. */ + public int getVBClassVirtualBaseClassType(); + + /** Numeric leaf specifying the offset of the virtual base pointer + from the address point of the class for this virtual base. */ + public int getVBClassVBPOff() throws DebugVC50WrongNumericTypeException; + + /** Numeric leaf specifying the index into the virtual base + displacement table of the entry that contains the displacement + of the virtual base. The displacement is relative to the address + point of the class plus vbpoff. */ + public int getVBClassVBOff() throws DebugVC50WrongNumericTypeException; + + /////////////////////////// + // LF_IVBCLASS accessors // + /////////////////////////// + + // This leaf specifies indirectly inherited virtual base class. If a + // class indirectly inherits virtual base classes, the corresponding + // Indirect Virtual Base Class records will follow all Real Base + // Class and Direct Virtual Base Class member records and precede + // all other member records in the field list of that class. Direct + // Virtual Base class records are emitted in bottommost + // left-to-right inheritance order for virtual bases. + + /** Member attribute bit field. */ + public short getIVBClassAttribute(); + + /** Index to type record of the direct or indirect virtual base + class. The class name can be obtained from this record. */ + public int getIVBClassBType(); + + /** Type index of the virtual base pointer for this base. */ + public int getIVBClassVBPType(); + + /** Numeric leaf specifying the offset of the virtual base pointer + from the address point of the class for this virtual base. */ + public int getIVBClassVBPOff() throws DebugVC50WrongNumericTypeException; + + /** Numeric leaf specifying the index into the virtual base + displacement table of the entry that contains the displacement + of the virtual base. The displacement is relative to the address + point of the class plus vbpoff. */ + public int getIVBClassVBOff() throws DebugVC50WrongNumericTypeException; + + //////////////////////////// + // LF_ENUMERATE accessors // + //////////////////////////// + + /** Member attribute bit field. */ + public short getEnumerateAttribute(); + + /** Numeric leaf specifying the value of enumerate. */ + public long getEnumerateValue() throws DebugVC50WrongNumericTypeException; + + /** Length-prefixed name of the member field. */ + public String getEnumerateName(); + + //////////////////////////// + // LF_FRIENDFCN accessors // + //////////////////////////// + + /** Index to type record of the friend function. */ + public int getFriendFcnType(); + + /** Length prefixed name of friend function. */ + public String getFriendFcnName(); + + //////////////////////// + // LF_INDEX accessors // + //////////////////////// + + /** Type index. This field is emitted by the compiler when a complex + list needs to be split during writing. */ + public int getIndexValue(); + + /** Create a new type iterator starting at the above index. */ + public DebugVC50TypeIterator getIndexIterator(); + + ///////////////////////// + // LF_MEMBER accessors // + ///////////////////////// + + /** Member attribute bit field. */ + public short getMemberAttribute(); + + /** Index to type record for field. */ + public int getMemberType(); + + /** Numeric leaf specifying the offset of field in the structure. */ + public int getMemberOffset() throws DebugVC50WrongNumericTypeException; + + /** Length-prefixed name of the member field. */ + public String getMemberName(); + + /////////////////////////// + // LF_STMEMBER accessors // + /////////////////////////// + + // This leaf specifies a static data member of a class. Once a + // static data member has been found in this list, its symbol is + // found by qualifying the name with its class (T::name) and then + // searching the symbol table for a symbol by that name with the + // correct type index. + + /** Member attribute bit field. */ + public short getStaticAttribute(); + + /** Index to type record for field. */ + public int getStaticType(); + + /** Length-prefixed name of the member field. */ + public String getStaticName(); + + ///////////////////////// + // LF_METHOD accessors // + ///////////////////////// + + // This leaf specifies the overloaded member functions of a class. + // This type record can also be used to specify a non-overloaded + // method but is inefficient. The LF_ONEMETHOD record should be used + // for non-overloaded methods. + + /** Number of occurrences of function within the class. If the + function is overloaded then there will be multiple entries in + the method list. */ + public short getMethodCount(); + + /** Type index of method list. */ + public int getMethodList(); + + /** Length-prefixed name of method. */ + public String getMethodName(); + + ///////////////////////////// + // LF_NESTEDTYPE accessors // + ///////////////////////////// + + /** Type index of nested type. */ + public int getNestedType(); + + /** Length-prefixed name of type. */ + public String getNestedName(); + + /////////////////////////// + // LF_VFUNCTAB accessors // + /////////////////////////// + + // This leaf specifies virtual table pointers within the class. It + // is a requirement that this record be emitted in the field list + // before any virtual functions are emitted to the field list. + + /** Index to the pointer record describing the pointer. The pointer + will in turn have a LF_VTSHAPE type record as the underlying + type. Note that the offset of the virtual function table pointer + from the address point of the class is always zero. */ + public int getVFuncTabType(); + + //////////////////////////// + // LF_FRIENDCLS accessors // + //////////////////////////// + + /** Index to type record of the friend class. The name of the class + can be obtained from the referenced record. */ + public int getFriendClsType(); + + //////////////////////////// + // LF_ONEMETHOD accessors // + //////////////////////////// + + /** Method attribute; see {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50TypeEnums} and + {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50MemberAttributes}. */ + public short getOneMethodAttribute(); + + /** Type index of method. */ + public int getOneMethodType(); + + /** Convenience routine indicating whether this method is + introducing virtual. */ + public boolean isOneMethodIntroducingVirtual(); + + /** Offset in virtual function table if introducing virtual method. + If the method is not an introducing virtual, then this field is + not present. */ + public int getOneMethodVBaseOff(); + + /** Length prefixed name of method. */ + public String getOneMethodName(); + + /////////////////////////// + // LF_VFUNCOFF accessors // + /////////////////////////// + + // This record is used to specify a virtual function table pointer + // at a non-zero offset relative to the address point of a class. + + /** Type index of virtual function table pointer. */ + public int getVFuncOffType(); + + /** Offset of virtual function table pointer relative to address + point of class. */ + public int getVFuncOffOffset(); + + /////////////////////////////// + // LF_NESTEDTYPEEX accessors // + /////////////////////////////// + + // This leaf specifies nested type definition with classes, + // structures, unions, or enums and includes the protection + // attributes that are missing in LF_NESTEDTYPE. + + /** Nested type attribute (protection fields are valid). */ + public short getNestedExAttribute(); + + /** Type index of nested type. */ + public int getNestedExType(); + + /** Length-prefixed name of type. */ + public String getNestedExName(); + + /////////////////////////////// + // LF_MEMBERMODIFY accessors // + /////////////////////////////// + + /** New protection attributes. */ + public short getMemberModifyAttribute(); + + /** Type index of base class that introduced the member. */ + public int getMemberModifyType(); + + /** Length-prefixed name of member. */ + public String getMemberModifyName(); + + //////////////////////////// + // Numeric Leaf accessors // + //////////////////////////// + + /** Fetch the two-byte type (or data, for short integer numeric + leaves) of the numeric leaf at the given offset, in bytes, from + the start of the current leaf. */ + public short getNumericTypeAt(int byteOffset); + + /** The size in bytes of the numeric leaf at the given offset, in + bytes, from the start of the current leaf. + + @throw DebugVC50WrongNumericTypeException if there is no numeric + leaf at the specified byte offset. */ + public int getNumericLengthAt(int byteOffset) + throws DebugVC50WrongNumericTypeException; + + /** Fetch the value of the integer numeric leaf at the given offset, + in bytes, from the start of the current leaf. + + @throw DebugVC50WrongNumericTypeException if the specified + numeric leaf is not of integer type. */ + public int getNumericIntAt(int byteOffset) + throws DebugVC50WrongNumericTypeException; + + /** Fetch the value of the long or integer numeric leaf at the given + offset, in bytes, from the start of the current leaf. + + @throw DebugVC50WrongNumericTypeException if the specified + numeric leaf is not of long or integer type. */ + public long getNumericLongAt(int byteOffset) + throws DebugVC50WrongNumericTypeException; + + /** Fetch the value of the single-precision floating-point numeric + leaf at the given offset, in bytes, from the start of the + current leaf. + + @throw DebugVC50WrongNumericTypeException if the specified + numeric leaf is not of 32-bit float type. */ + public float getNumericFloatAt(int byteOffset) + throws DebugVC50WrongNumericTypeException; + + /** Fetch the value of the double-precision floating-point numeric + leaf at the given offset, in bytes, from the start of the + current leaf. + + @throw DebugVC50WrongNumericTypeException if the specified + numeric leaf is not of 64-bit float type. */ + public double getNumericDoubleAt(int byteOffset) + throws DebugVC50WrongNumericTypeException; + + /** Fetch the raw bytes, including LF_ prefix (if any), of the + numeric leaf at the given offset, in bytes, from the start of + the current leaf. + + @throw DebugVC50WrongNumericTypeException if there is no numeric + leaf at the specified byte offset. */ + public byte[] getNumericDataAt(int byteOffset) + throws DebugVC50WrongNumericTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50TypeLeafIndices.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50TypeLeafIndices.java new file mode 100644 index 00000000000..588c6be43d5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50TypeLeafIndices.java @@ -0,0 +1,143 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/**

Enumerates the leaf indices referenced in type strings + contained in the {@link + sun.jvm.hotspot.debugger.win32.coff.DebugVC50SSGlobalTypes} + subsection. (Some of the descriptions are taken directly from + Microsoft's documentation and are copyrighted by Microsoft.)

+ +

NOTE that these indices are specified as integers rather than + short integers; this is to make comparisons and switches simpler + because of Java's automatic sign extension.

*/ + +public interface DebugVC50TypeLeafIndices { + + // + // Leaf indices for type records that can be referenced from symbols: + // + + public static final int LF_MODIFIER = 0x1001; + public static final int LF_POINTER = 0x1002; + public static final int LF_ARRAY = 0x1003; + public static final int LF_CLASS = 0x1004; + public static final int LF_STRUCTURE = 0x1005; + public static final int LF_UNION = 0x1006; + public static final int LF_ENUM = 0x1007; + public static final int LF_PROCEDURE = 0x1008; + public static final int LF_MFUNCTION = 0x1009; + public static final int LF_VTSHAPE = 0x000a; + public static final int LF_COBOL0 = 0x100a; + public static final int LF_COBOL1 = 0x000c; + public static final int LF_BARRAY = 0x100b; + public static final int LF_LABEL = 0x000e; + public static final int LF_NULL = 0x000f; + public static final int LF_NOTTRAN = 0x0010; + public static final int LF_DIMARRAY = 0x100c; + public static final int LF_VFTPATH = 0x100d; + public static final int LF_PRECOMP = 0x100e; + public static final int LF_ENDPRECOMP = 0x0014; + public static final int LF_OEM = 0x100f; + public static final int LF_TYPESERVER = 0x0016; + + // + // Leaf indices for type records that can be referenced from other type records: + // + + public static final int LF_SKIP = 0x1200; + public static final int LF_ARGLIST = 0x1201; + public static final int LF_DEFARG = 0x1202; + public static final int LF_FIELDLIST = 0x1203; + public static final int LF_DERIVED = 0x1204; + public static final int LF_BITFIELD = 0x1205; + public static final int LF_METHODLIST = 0x1206; + public static final int LF_DIMCONU = 0x1207; + public static final int LF_DIMCONLU = 0x1208; + public static final int LF_DIMVARU = 0x1209; + public static final int LF_DIMVARLU = 0x120a; + public static final int LF_REFSYM = 0x020c; + + // + // Leaf indices for fields of complex lists: + // + + public static final int LF_BCLASS = 0x1400; + public static final int LF_VBCLASS = 0x1401; + public static final int LF_IVBCLASS = 0x1402; + public static final int LF_ENUMERATE = 0x0403; + public static final int LF_FRIENDFCN = 0x1403; + public static final int LF_INDEX = 0x1404; + public static final int LF_MEMBER = 0x1405; + public static final int LF_STMEMBER = 0x1406; + public static final int LF_METHOD = 0x1407; + public static final int LF_NESTTYPE = 0x1408; + public static final int LF_VFUNCTAB = 0x1409; + public static final int LF_FRIENDCLS = 0x140a; + public static final int LF_ONEMETHOD = 0x140b; + public static final int LF_VFUNCOFF = 0x140c; + public static final int LF_NESTTYPEEX = 0x140d; + public static final int LF_MEMBERMODIFY = 0x140e; + + // + // Leaf indices for numeric fields of symbols and type records: + // + + public static final int LF_NUMERIC = 0x8000; + public static final int LF_CHAR = 0x8000; + public static final int LF_SHORT = 0x8001; + public static final int LF_USHORT = 0x8002; + public static final int LF_LONG = 0x8003; + public static final int LF_ULONG = 0x8004; + public static final int LF_REAL32 = 0x8005; + public static final int LF_REAL64 = 0x8006; + public static final int LF_REAL80 = 0x8007; + public static final int LF_REAL128 = 0x8008; + public static final int LF_QUADWORD = 0x8009; + public static final int LF_UQUADWORD = 0x800a; + public static final int LF_REAL48 = 0x800b; + public static final int LF_COMPLEX32 = 0x800c; + public static final int LF_COMPLEX64 = 0x800d; + public static final int LF_COMPLEX80 = 0x800e; + public static final int LF_COMPLEX128 = 0x800f; + public static final int LF_VARSTRING = 0x8010; + + public static final int LF_PAD0 = 0xf0; + public static final int LF_PAD1 = 0xf1; + public static final int LF_PAD2 = 0xf2; + public static final int LF_PAD3 = 0xf3; + public static final int LF_PAD4 = 0xf4; + public static final int LF_PAD5 = 0xf5; + public static final int LF_PAD6 = 0xf6; + public static final int LF_PAD7 = 0xf7; + public static final int LF_PAD8 = 0xf8; + public static final int LF_PAD9 = 0xf9; + public static final int LF_PAD10 = 0xfa; + public static final int LF_PAD11 = 0xfb; + public static final int LF_PAD12 = 0xfc; + public static final int LF_PAD13 = 0xfd; + public static final int LF_PAD14 = 0xfe; + public static final int LF_PAD15 = 0xff; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50WrongNumericTypeException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50WrongNumericTypeException.java new file mode 100644 index 00000000000..da26c4d193b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50WrongNumericTypeException.java @@ -0,0 +1,39 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Thrown from accessors of numeric leaves if the type of the numeric + leaf does not match the return type of the specified accessor (for + example, a floating-point numeric leaf accessed as an int). */ + +public class DebugVC50WrongNumericTypeException extends RuntimeException { + public DebugVC50WrongNumericTypeException() { + super(); + } + + public DebugVC50WrongNumericTypeException(String message) { + super(message); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50X86RegisterEnums.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50X86RegisterEnums.java new file mode 100644 index 00000000000..e63a6b38790 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DebugVC50X86RegisterEnums.java @@ -0,0 +1,111 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +public interface DebugVC50X86RegisterEnums { + /** 8-bit registers */ + public static final int NONE = 0; + public static final int AL = 1; + public static final int CL = 2; + public static final int DL = 3; + public static final int BL = 4; + public static final int AH = 5; + public static final int CH = 6; + public static final int DH = 7; + public static final int BH = 8; + + /** 16-bit registers */ + public static final int AX = 9; + public static final int CX = 10; + public static final int DX = 11; + public static final int BX = 12; + public static final int SP = 13; + public static final int BP = 14; + public static final int SI = 15; + public static final int DI = 16; + + /** 32-bit registers */ + public static final int EAX = 17; + public static final int ECX = 18; + public static final int EDX = 19; + public static final int EBX = 20; + public static final int ESP = 21; + public static final int EBP = 22; + public static final int ESI = 23; + public static final int EDI = 24; + + /** Segment registers */ + public static final int ES = 25; + public static final int CS = 26; + public static final int SS = 27; + public static final int DS = 28; + public static final int FS = 29; + public static final int GS = 30; + + /** Special cases */ + public static final int IP = 31; + public static final int FLAGS = 32; + public static final int EIP = 33; + public static final int EFLAGS = 34; + + /** PCODE Registers */ + public static final int TEMP = 40; + public static final int TEMPH = 41; + public static final int QUOTE = 42; + + /** System Registers */ + public static final int CR0 = 80; + public static final int CR1 = 81; + public static final int CR2 = 82; + public static final int CR3 = 83; + public static final int DR0 = 90; + public static final int DR1 = 91; + public static final int DR2 = 92; + public static final int DR3 = 93; + public static final int DR4 = 94; + public static final int DR5 = 95; + public static final int DR6 = 96; + public static final int DR7 = 97; + + /** Register extensions for 80x87 */ + public static final int ST0 = 128; + public static final int ST1 = 129; + public static final int ST2 = 130; + public static final int ST3 = 131; + public static final int ST4 = 132; + public static final int ST5 = 133; + public static final int ST6 = 134; + public static final int ST7 = 135; + public static final int CONTROL = 136; + public static final int STATUS = 137; + public static final int TAG = 138; + public static final int FPIP = 139; + public static final int FPCS = 140; + public static final int FPDO = 141; + public static final int FPDS = 142; + public static final int ISEM = 143; + public static final int FPEIP = 144; + public static final int FPEDO = 145; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DumpExports.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DumpExports.java new file mode 100644 index 00000000000..48a55492a43 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/DumpExports.java @@ -0,0 +1,72 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +public class DumpExports { + private static void usage() { + System.err.println("usage: java DumpExports [.dll name]"); + System.exit(1); + } + + public static void main(String[] args) { + if (args.length != 1) { + usage(); + } + + String filename = args[0]; + COFFFile file = COFFFileParser.getParser().parse(filename); + ExportDirectoryTable exports = + file.getHeader(). + getOptionalHeader(). + getDataDirectories(). + getExportDirectoryTable(); + if (exports == null) { + System.out.println("No exports found."); + } else { + System.out.println(file.getHeader().getNumberOfSections() + " sections in file"); + for (int i = 0; i < file.getHeader().getNumberOfSections(); i++) { + System.out.println(" Section " + i + ": " + file.getHeader().getSectionHeader(1 + i).getName()); + } + + DataDirectory dir = file.getHeader().getOptionalHeader().getDataDirectories().getExportTable(); + System.out.println("Export table: RVA = 0x" + Integer.toHexString(dir.getRVA()) + + ", size = 0x" + Integer.toHexString(dir.getSize())); + + System.out.println("DLL name: " + exports.getDLLName()); + System.out.println("Time/date stamp 0x" + Integer.toHexString(exports.getTimeDateStamp())); + System.out.println("Major version 0x" + Integer.toHexString(exports.getMajorVersion() & 0xFFFF)); + System.out.println("Minor version 0x" + Integer.toHexString(exports.getMinorVersion() & 0xFFFF)); + System.out.println(exports.getNumberOfNamePointers() + " functions found"); + for (int i = 0; i < exports.getNumberOfNamePointers(); i++) { + System.out.println(" 0x" + + Integer.toHexString(exports.getExportAddress(exports.getExportOrdinal(i))) + + " " + + (exports.isExportAddressForwarder(exports.getExportOrdinal(i)) ? + ("Forwarded to " + exports.getExportAddressForwarder(exports.getExportOrdinal(i))) : + exports.getExportName(i))); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/ExportDirectoryTable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/ExportDirectoryTable.java new file mode 100644 index 00000000000..2bc7f9b2c13 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/ExportDirectoryTable.java @@ -0,0 +1,123 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the information stored in the export directory table. + Ostensibly this is supposed to lie in the .edata section. + However, experience has shown that this data does not appear to be + present there, instead (likely -- not yet tested) showing up in + the Export Table portion of the OptionalHeaderDataDirectories. + (Some of the descriptions are taken directly from Microsoft's + documentation and are copyrighted by Microsoft.) */ + +public interface ExportDirectoryTable { + /** A reserved field, set to zero for now. */ + public int getExportFlags(); + + /** Time and date the export data was created. */ + public int getTimeDateStamp(); + + /** Major version number. The major/minor version number can be set + by the user. */ + public short getMajorVersion(); + + /** Minor version number. */ + public short getMinorVersion(); + + /** Address of the ASCII string containing the name of the + DLL. Relative to image base. See {@link #getDLLName}. */ + public int getNameRVA(); + + /** Convenience routine which returns the name of the DLL containing + this export directory. */ + public String getDLLName(); + + /** Starting ordinal number for exports in this image. This field + specifies the starting ordinal number for the Export Address + Table. Usually set to 1. */ + public int getOrdinalBase(); + + /** Number of entries in the Export Address Table. */ + public int getNumberOfAddressTableEntries(); + + /** Number of entries in the Name Pointer Table (also the number of + entries in the Ordinal Table). */ + public int getNumberOfNamePointers(); + + /** Address of the Export Address Table, relative to the image + base. */ + public int getExportAddressTableRVA(); + + /** Address of the Export Name Pointer Table, relative to the image + base. The table size is given by Number of Name Pointers. */ + public int getNamePointerTableRVA(); + + /** Address of the Ordinal Table, relative to the image base. */ + public int getOrdinalTableRVA(); + + /** Returns the ith exported symbol (from 0..{@link + #getNumberOfNamePointers} - 1). These are arranged in sorted + order to allow binary searches. */ + public String getExportName(int i); + + /** Returns the ith entry (0..{@link + #getNumberOfNamePointers} in the Export Ordinal Table. This is + used for looking up a given symbol's address in the Export + Address Table; see {@link #getExportAddress}. */ + public short getExportOrdinal(int i); + + /** Indicates whether the specified export address is really a + forwarder, in which case the value is not an address but a + string. */ + public boolean isExportAddressForwarder(short ordinal); + + /** Get the forwarder name for the given ordinal. Must be called + only if isExportAddressForwarder() returns true. */ + public String getExportAddressForwarder(short ordinal); + + /**

Takes in an ordinal from the Export Ordinal Table (see + {@link #getExportOrdinal}). This ordinal is biased by {@link + #getOrdinalBase}; however, the subtraction described in the + documentation is done internally for convenience. Returns an + address that is in one of two formats. If the address specified + is not within the export section (as defined by the address and + length indicated in the Optional Header), the field is an Export + RVA: an actual address in code or data. Otherwise, the field is + a Forwarder RVA, which names a symbol in another DLL.

+ +

An Export RVA is the address of the exported symbol when + loaded into memory, relative to the image base. For example, the + address of an exported function.

+ +

A Forwarder RVA is a pointer to a null-terminated ASCII + string in the export section, giving the DLL name and the name + of the export (for example, "MYDLL.expfunc") or the DLL + name and an export (for example, "MYDLL.#27").

+ +

NOTE: the value returned has been transformed from an RVA to + a file pointer which can be added to the image base to find an + absolute address for the symbol.

*/ + public int getExportAddress(short ordinal); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/MachineTypes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/MachineTypes.java new file mode 100644 index 00000000000..9e2c57dff3c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/MachineTypes.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Constants defined by the MS COFF specification indicating machine + type of the given object file or image. (Some of the descriptions + are taken directly from Microsoft's documentation and are + copyrighted by Microsoft.) */ + +public interface MachineTypes { + /** Contents assumed to be applicable to any machine type. */ + public static final short IMAGE_FILE_MACHINE_UNKNOWN = (short) 0x0; + /** Alpha AXP. */ + public static final short IMAGE_FILE_MACHINE_ALPHA = (short) 0x184; + public static final short IMAGE_FILE_MACHINE_ARM = (short) 0x1c0; + /** Alpha AXP 64-bit. */ + public static final short IMAGE_FILE_MACHINE_ALPHA64 = (short) 0x284; + /** Intel 386 or later, and compatible processors. */ + public static final short IMAGE_FILE_MACHINE_I386 = (short) 0x14c; + /** Intel IA64 */ + public static final short IMAGE_FILE_MACHINE_IA64 = (short) 0x200; + /** Motorola 68000 series. */ + public static final short IMAGE_FILE_MACHINE_M68K = (short) 0x268; + public static final short IMAGE_FILE_MACHINE_MIPS16 = (short) 0x266; + /** MIPS with FPU */ + public static final short IMAGE_FILE_MACHINE_MIPSFPU = (short) 0x366; + /** MIPS16 with FPU */ + public static final short IMAGE_FILE_MACHINE_MIPSFPU16 = (short) 0x466; + /** Power PC, little endian. */ + public static final short IMAGE_FILE_MACHINE_POWERPC = (short) 0x1f0; + public static final short IMAGE_FILE_MACHINE_R3000 = (short) 0x162; + /** MIPS little endian. */ + public static final short IMAGE_FILE_MACHINE_R4000 = (short) 0x166; + public static final short IMAGE_FILE_MACHINE_R10000 = (short) 0x168; + /** Hitachi SH3 */ + public static final short IMAGE_FILE_MACHINE_SH3 = (short) 0x1a2; + /** Hitachi SH4 */ + public static final short IMAGE_FILE_MACHINE_SH4 = (short) 0x1a6; + public static final short IMAGE_FILE_MACHINE_THUMB = (short) 0x1c2; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeader.java new file mode 100644 index 00000000000..532fbd6670c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeader.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the information stored in the optional header of a Portable + Executable file. */ + +public interface OptionalHeader { + /** Magic number for a PE32 file */ + public static final short MAGIC_PE32 = (short) 0x10B; + + /** Magic number for a PE32+ file */ + public static final short MAGIC_PE32_PLUS = (short) 0x20B; + + /** Magic number for a "ROM image" */ + public static final short MAGIC_ROM_IMAGE = (short) 0x107; + + /** Returns the magic number of the Optional Header ({@link + #MAGIC_PE32}, {@link #MAGIC_PE32_PLUS}, or {@link + #MAGIC_ROM_IMAGE}) */ + public short getMagicNumber(); + + /** These are defined for all implementations of COFF, including + UNIX. */ + public OptionalHeaderStandardFields getStandardFields(); + + /** These include additional fields to support specific features of + Windows (for example, subsystem). */ + public OptionalHeaderWindowsSpecificFields getWindowsSpecificFields(); + + /** Gets the data directories portion of the optional header. */ + public OptionalHeaderDataDirectories getDataDirectories(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeaderDataDirectories.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeaderDataDirectories.java new file mode 100644 index 00000000000..efbe9c9792f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeaderDataDirectories.java @@ -0,0 +1,89 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +// FIXME: NOT FINISHED + +/** Models the information stored in the data directories portion of + the optional header of a Portable Executable file. FIXME: the + DataDirectory objects are less than useful; need to bring up more + of the data structures defined in the documentation. (Some of the + descriptions are taken directly from Microsoft's documentation and + are copyrighted by Microsoft.) */ + +public interface OptionalHeaderDataDirectories { + /** Export Table address and size. */ + public DataDirectory getExportTable() throws COFFException; + + /** Returns the Export Table, or null if none was present. */ + public ExportDirectoryTable getExportDirectoryTable() throws COFFException; + + /** Import Table address and size */ + public DataDirectory getImportTable() throws COFFException; + + /** Resource Table address and size. */ + public DataDirectory getResourceTable() throws COFFException; + + /** Exception Table address and size. */ + public DataDirectory getExceptionTable() throws COFFException; + + /** Attribute Certificate Table address and size. */ + public DataDirectory getCertificateTable() throws COFFException; + + /** Base Relocation Table address and size. */ + public DataDirectory getBaseRelocationTable() throws COFFException; + + /** Debug data starting address and size. */ + public DataDirectory getDebug() throws COFFException; + + /** Returns the Debug Directory, or null if none was present. */ + public DebugDirectory getDebugDirectory() throws COFFException; + + /** Architecture-specific data address and size. */ + public DataDirectory getArchitecture() throws COFFException; + + /** Relative virtual address of the value to be stored in the global + pointer register. Size member of this structure must be set to + 0. */ + public DataDirectory getGlobalPtr() throws COFFException; + + /** Thread Local Storage (TLS) Table address and size. */ + public DataDirectory getTLSTable() throws COFFException; + + /** Load Configuration Table address and size. */ + public DataDirectory getLoadConfigTable() throws COFFException; + + /** Bound Import Table address and size. */ + public DataDirectory getBoundImportTable() throws COFFException; + + /** Import Address Table address and size. */ + public DataDirectory getImportAddressTable() throws COFFException; + + /** Address and size of the Delay Import Descriptor. */ + public DataDirectory getDelayImportDescriptor() throws COFFException; + + /** COM+ Runtime Header address and size */ + public DataDirectory getCOMPlusRuntimeHeader() throws COFFException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeaderStandardFields.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeaderStandardFields.java new file mode 100644 index 00000000000..90a0e218dae --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeaderStandardFields.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the information stored in the standard fields portion of + the optional header of a Portable Executable file. (Some of the + descriptions are taken directly from Microsoft's documentation and + are copyrighted by Microsoft.) */ + +public interface OptionalHeaderStandardFields { + public byte getMajorLinkerVersion(); + public byte getMinorLinkerVersion(); + + /** Size of the code (text) section, or the sum of all code sections + if there are multiple sections. */ + public int getSizeOfCode(); + + /** Size of the initialized data section, or the sum of all such + sections if there are multiple data sections. */ + public int getSizeOfInitializedData(); + + /** Size of the uninitialized data section (BSS), or the sum of all + such sections if there are multiple BSS sections. */ + public int getSizeOfUninitializedData(); + + /** Address of entry point, relative to image base, when executable + file is loaded into memory. For program images, this is the + starting address. For device drivers, this is the address of the + initialization function. An entry point is optional for DLLs. + When none is present this field should be 0. */ + public int getAddressOfEntryPoint(); + + /** Address, relative to image base, of beginning of code section, + when loaded into memory. */ + public int getBaseOfCode(); + + /** Onle present in PE32 files; absent in PE32+ files. Address, + relative to image base, of beginning of data section, when + loaded into memory. */ + public int getBaseOfData() throws COFFException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeaderWindowsSpecificFields.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeaderWindowsSpecificFields.java new file mode 100644 index 00000000000..adc35da308a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/OptionalHeaderWindowsSpecificFields.java @@ -0,0 +1,115 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Models the information stored in the Windows-specific fields + portion of the optional header of a Portable Executable file. + (Some of the descriptions are taken directly from Microsoft's + documentation and are copyrighted by Microsoft.) */ + +public interface OptionalHeaderWindowsSpecificFields { + /** Preferred address of first byte of image when loaded into + memory; must be a multiple of 64K. The default for DLLs is + 0x10000000. The default for Windows CE EXEs is 0x00010000. The + default for Windows NT, Windows 95, and Windows 98 is + 0x00400000. */ + public long getImageBase(); + + /** Alignment (in bytes) of sections when loaded into memory. Must + be greater or equal to File Alignment. Default is the page size + for the architecture. */ + public int getSectionAlignment(); + + /** Alignment factor (in bytes) used to align the raw data of + sections in the image file. The value should be a power of 2 + between 512 and 64K inclusive. The default is 512. If the + SectionAlignment is less than the architecture's page size than + this must match the SectionAlignment. */ + public int getFileAlignment(); + + /** Major version number of required OS. */ + public short getMajorOperatingSystemVersion(); + + /** Minor version number of required OS. */ + public short getMinorOperatingSystemVersion(); + + /** Major version number of image. */ + public short getMajorImageVersion(); + + /** Minor version number of image. */ + public short getMinorImageVersion(); + + /** Major version number of subsystem. */ + public short getMajorSubsystemVersion(); + + /** Minor version number of subsystem. */ + public short getMinorSubsystemVersion(); + + /** Size, in bytes, of image, including all headers; must be a + multiple of Section Alignment. */ + public int getSizeOfImage(); + + /** Combined size of MS-DOS stub, PE Header, and section headers + rounded up to a multiple of FileAlignment. */ + public int getSizeOfHeaders(); + + /** Image file checksum. The algorithm for computing is incorporated + into IMAGHELP.DLL. The following are checked for validation at + load time: all drivers, any DLL loaded at boot time, and any DLL + that ends up in the server. */ + public int getCheckSum(); + + /** Subsystem required to run this image; returns one of the + constants defined in {@link + sun.jvm.hotspot.debugger.win32.coff.WindowsNTSubsystem}. */ + public short getSubsystem(); + + /** Indicates characteristics of a DLL; see {@link + sun.jvm.hotspot.debugger.win32.coff.DLLCharacteristics}. */ + public short getDLLCharacteristics(); + + /** Size of stack to reserve. Only the Stack Commit Size is + committed; the rest is made available one page at a time, until + reserve size is reached. */ + public long getSizeOfStackReserve(); + + /** Size of stack to commit. */ + public long getSizeOfStackCommit(); + + /** Size of local heap space to reserve. Only the Heap Commit Size + is committed; the rest is made available one page at a time, + until reserve size is reached. */ + public long getSizeOfHeapReserve(); + + /** Size of local heap space to commit. */ + public long getSizeOfHeapCommit(); + + /** Obsolete. */ + public int getLoaderFlags(); + + /** Number of data-dictionary entries in the remainder of the + Optional Header. Each describes a location and size. */ + public int getNumberOfRvaAndSizes(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/SectionFlags.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/SectionFlags.java new file mode 100644 index 00000000000..b50137190de --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/SectionFlags.java @@ -0,0 +1,174 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Constants indicating attributes of the section. (Some of the + descriptions are taken directly from Microsoft's documentation and + are copyrighted by Microsoft.) */ + +public interface SectionFlags { + /** Reserved for future use. */ + public static final int IMAGE_SCN_TYPE_REG = 0x00000000; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_TYPE_DSECT = 0x00000001; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_TYPE_NOLOAD = 0x00000002; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_TYPE_GROUP = 0x00000004; + + /** Section should not be padded to next boundary. This is obsolete + and replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid for object + files only. */ + public static final int IMAGE_SCN_TYPE_NO_PAD = 0x00000008; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_TYPE_COPY = 0x00000010; + + /** Section contains executable code. */ + public static final int IMAGE_SCN_CNT_CODE = 0x00000020; + + /** Section contains initialized data. */ + public static final int IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040; + + /** Section contains uninitialized data. */ + public static final int IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_LNK_OTHER = 0x00000100; + + /** Section contains comments or other information. The .drectve + section has this type. This is valid for object files only. */ + public static final int IMAGE_SCN_LNK_INFO = 0x00000200; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_TYPE_OVER = 0x00000400; + + /** Section will not become part of the image. This is valid for + object files only. */ + public static final int IMAGE_SCN_LNK_REMOVE = 0x00000800; + + /** Section contains COMDAT data; see {@link + sun.jvm.hotspot.debugger.win32.coff.COMDATSelectionTypes}. This is valid + for object files only. */ + public static final int IMAGE_SCN_LNK_COMDAT = 0x00001000; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_MEM_FARDATA = 0x00008000; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_MEM_PURGEABLE = 0x00020000; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_MEM_16BIT = 0x00020000; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_MEM_LOCKED = 0x00040000; + + /** Reserved for future use. */ + public static final int IMAGE_SCN_MEM_PRELOAD = 0x00080000; + + /** Align data on a 1-byte boundary. This is valid for object files + only. */ + public static final int IMAGE_SCN_ALIGN_1BYTES = 0x00100000; + + /** Align data on a 2-byte boundary. This is valid for object files + only. */ + public static final int IMAGE_SCN_ALIGN_2BYTES = 0x00200000; + + /** Align data on a 4-byte boundary. This is valid for object files + only. */ + public static final int IMAGE_SCN_ALIGN_4BYTES = 0x00300000; + + /** Align data on a 8-byte boundary. This is valid for object files + only. */ + public static final int IMAGE_SCN_ALIGN_8BYTES = 0x00400000; + + /** Align data on a 16-byte boundary. This is valid for object files + only. */ + public static final int IMAGE_SCN_ALIGN_16BYTES = 0x00500000; + + /** Align data on a 32-byte boundary. This is valid for object files + only. */ + public static final int IMAGE_SCN_ALIGN_32BYTES = 0x00600000; + + /** Align data on a 64-byte boundary. This is valid for object files + only. */ + public static final int IMAGE_SCN_ALIGN_64BYTES = 0x00700000; + + /** Align data on a 128-byte boundary. This is valid for object + files only. */ + public static final int IMAGE_SCN_ALIGN_128BYTES = 0x00800000; + + /** Align data on a 256-byte boundary. This is valid for object + files only. */ + public static final int IMAGE_SCN_ALIGN_256BYTES = 0x00900000; + + /** Align data on a 512-byte boundary. This is valid for object + files only. */ + public static final int IMAGE_SCN_ALIGN_512BYTES = 0x00A00000; + + /** Align data on a 1024-byte boundary. This is valid for object + files only. */ + public static final int IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000; + + /** Align data on a 2048-byte boundary. This is valid for object + files only. */ + public static final int IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000; + + /** Align data on a 4096-byte boundary. This is valid for object + files only. */ + public static final int IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000; + + /** Align data on a 8192-byte boundary. This is valid for object + files only. */ + public static final int IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000; + + /** Section contains extended relocations. */ + public static final int IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000; + + /** Section can be discarded as needed. */ + public static final int IMAGE_SCN_MEM_DISCARDABLE = 0x02000000; + + /** Section cannot be cached. */ + public static final int IMAGE_SCN_MEM_NOT_CACHED = 0x04000000; + + /** Section is not pageable. */ + public static final int IMAGE_SCN_MEM_NOT_PAGED = 0x08000000; + + /** Section can be shared in memory. */ + public static final int IMAGE_SCN_MEM_SHARED = 0x10000000; + + /** Section can be executed as code. */ + public static final int IMAGE_SCN_MEM_EXECUTE = 0x20000000; + + /** Section can be read. */ + public static final int IMAGE_SCN_MEM_READ = 0x40000000; + + /** Section can be written to. */ + public static final int IMAGE_SCN_MEM_WRITE = 0x80000000; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/SectionHeader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/SectionHeader.java new file mode 100644 index 00000000000..9b5f1374fcc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/SectionHeader.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Describes the header of a section in a COFF file. The section + headers are grouped together into the Section Table. (Some of the + descriptions are taken directly from Microsoft's documentation and + are copyrighted by Microsoft.) */ + +public interface SectionHeader { + public String getName(); + + /** Total size of the section when loaded into memory. If this value + is greater than Size of Raw Data, the section is zero-padded. + This field is valid only for executable images and should be set + to 0 for object files. */ + public int getSize(); + + /** For executable images this is the address of the first byte of + the section, when loaded into memory, relative to the image + base. For object files, this field is the address of the first + byte before relocation is applied; for simplicity, compilers + should set this to zero. Otherwise, it is an arbitrary value + that is subtracted from offsets during relocation. */ + public int getVirtualAddress(); + + /** Size of the section (object file) or size of the initialized + data on disk (image files). For executable image, this must be a + multiple of FileAlignment from the optional header. If this is + less than VirtualSize the remainder of the section is zero + filled. Because this field is rounded while the VirtualSize + field is not it is possible for this to be greater than + VirtualSize as well. When a section contains only uninitialized + data, this field should be 0. */ + public int getSizeOfRawData(); + + /** File pointer to section's first page within the COFF file. For + executable images, this must be a multiple of FileAlignment from + the optional header. For object files, the value should be + aligned on a four-byte boundary for best performance. When a + section contains only uninitialized data, this field should be + 0. */ + public int getPointerToRawData(); + + /** File pointer to beginning of relocation entries for the section. + Set to 0 for executable images or if there are no + relocations. */ + public int getPointerToRelocations(); + + /** File pointer to beginning of line-number entries for the + section. Set to 0 if there are no COFF line numbers. */ + public int getPointerToLineNumbers(); + + /** Number of relocation entries for the section. Set to 0 for + executable images. */ + public short getNumberOfRelocations(); + + /** Number of line-number entries for the section. */ + public short getNumberOfLineNumbers(); + + /** Flags describing section's characteristics; see {@link + sun.jvm.hotspot.debugger.win32.coff.SectionFlags}. */ + public int getSectionFlags(); + + /** Returns true if the appropriate flag (from {@link + sun.jvm.hotspot.debugger.win32.coff.SectionFlags}) is set. */ + public boolean hasSectionFlag(int flag); + + /** This is only present for object files. Retrieves the COFF + relocation at the given index; valid indices are numbered + 0...getNumberOfRelocations() - 1. */ + public COFFRelocation getCOFFRelocation(int index); + + /** Retrieves the COFF line number at the given index; valid indices + are numbered 0...getNumberOfLineNumbers() - 1. */ + public COFFLineNumber getCOFFLineNumber(int index); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/TestDebugInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/TestDebugInfo.java new file mode 100644 index 00000000000..0e5a6eb1f68 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/TestDebugInfo.java @@ -0,0 +1,268 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +public class TestDebugInfo implements DebugVC50SubsectionTypes, DebugVC50SymbolTypes, DebugVC50TypeLeafIndices { + public static void main(String[] args) { + if (args.length != 1) { + System.err.println("usage: java TestParser [file name]"); + System.err.println("File name may be an .exe, .dll or .obj"); + System.exit(1); + } + + try { + COFFFile file = COFFFileParser.getParser().parse(args[0]); + if (file.isImage()) { + System.out.println("PE Image detected."); + } else { + System.out.println("PE Image NOT detected, assuming object file."); + } + + DebugVC50 vc50 = getDebugVC50(file); + if (vc50 == null) { + System.out.println("No debug information found."); + System.exit(1); + } else { + System.out.println("Debug information found!"); + } + + DebugVC50SubsectionDirectory dir = vc50.getSubsectionDirectory(); + for (int i = 0; i < dir.getNumEntries(); i++) { + DebugVC50Subsection sec = dir.getSubsection(i); + switch (sec.getSubsectionType()) { + case SST_MODULE: System.out.println(" SST_MODULE"); break; + case SST_TYPES: System.out.println(" SST_TYPES"); break; + case SST_PUBLIC: System.out.println(" SST_PUBLIC"); break; + case SST_PUBLIC_SYM: System.out.println(" SST_PUBLIC_SYM"); break; + case SST_SYMBOLS: System.out.println(" SST_SYMBOLS"); break; + case SST_ALIGN_SYM: System.out.println(" SST_ALIGN_SYM"); printSymbolTable(((DebugVC50SSAlignSym) sec).getSymbolIterator()); break; + case SST_SRC_LN_SEG: System.out.println(" SST_SRC_LN_SEG"); break; + case SST_SRC_MODULE: System.out.println(" SST_SRC_MODULE"); break; + case SST_LIBRARIES: System.out.println(" SST_LIBRARIES"); break; + case SST_GLOBAL_SYM: System.out.println(" SST_GLOBAL_SYM"); printSymbolTable(sec); break; + case SST_GLOBAL_PUB: System.out.println(" SST_GLOBAL_PUB"); printSymbolTable(sec); break; + case SST_GLOBAL_TYPES: System.out.println(" SST_GLOBAL_TYPES"); printTypeTable(sec); break; + case SST_MPC: System.out.println(" SST_MPC"); break; + case SST_SEG_MAP: System.out.println(" SST_SEG_MAP"); break; + case SST_SEG_NAME: System.out.println(" SST_SEG_NAME"); break; + case SST_PRE_COMP: System.out.println(" SST_PRE_COMP"); break; + case SST_UNUSED: System.out.println(" SST_UNUSED"); break; + case SST_OFFSET_MAP_16: System.out.println(" SST_OFFSET_MAP_16"); break; + case SST_OFFSET_MAP_32: System.out.println(" SST_OFFSET_MAP_32"); break; + case SST_FILE_INDEX: System.out.println(" SST_FILE_INDEX"); break; + case SST_STATIC_SYM: System.out.println(" SST_STATIC_SYM"); printSymbolTable(sec); break; + default: System.out.println(" (Unknown subsection type " + sec.getSubsectionType() + ")"); break; + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static DebugVC50 getDebugVC50(COFFFile file) { + COFFHeader header = file.getHeader(); + OptionalHeader opt = header.getOptionalHeader(); + if (opt == null) { + System.out.println("Optional header not found."); + return null; + } + OptionalHeaderDataDirectories dd = opt.getDataDirectories(); + if (dd == null) { + System.out.println("Optional header data directories not found."); + return null; + } + DebugDirectory debug = dd.getDebugDirectory(); + if (debug == null) { + System.out.println("Debug directory not found."); + return null; + } + for (int i = 0; i < debug.getNumEntries(); i++) { + DebugDirectoryEntry entry = debug.getEntry(i); + if (entry.getType() == DebugTypes.IMAGE_DEBUG_TYPE_CODEVIEW) { + System.out.println("Debug Directory Entry " + i + " has debug type IMAGE_DEBUG_TYPE_CODEVIEW"); + return entry.getDebugVC50(); + } + } + + return null; + } + + private static void printSymbolTable(DebugVC50Subsection sec) { + DebugVC50SSSymbolBase sym = (DebugVC50SSSymbolBase) sec; + DebugVC50SymbolIterator iter = sym.getSymbolIterator(); + printSymbolTable(iter); + } + + private static void printSymbolTable(DebugVC50SymbolIterator iter) { + while (!iter.done()) { + int type = iter.getType() & 0xFFFF; + switch (type) { + case S_COMPILE: System.out.println(" S_COMPILE"); break; + case S_SSEARCH: System.out.println(" S_SSEARCH"); break; + case S_END: System.out.println(" S_END"); break; + case S_SKIP: System.out.println(" S_SKIP"); break; + case S_CVRESERVE: System.out.println(" S_CVRESERVE"); break; + case S_OBJNAME: System.out.println(" S_OBJNAME"); break; + case S_ENDARG: System.out.println(" S_ENDARG"); break; + case S_COBOLUDT: System.out.println(" S_COBOLUDT"); break; + case S_MANYREG: System.out.println(" S_MANYREG"); break; + case S_RETURN: System.out.println(" S_RETURN"); break; + case S_ENTRYTHIS: System.out.println(" S_ENTRYTHIS"); break; + case S_REGISTER: System.out.println(" S_REGISTER"); break; + case S_CONSTANT: System.out.println(" S_CONSTANT"); break; + case S_UDT: System.out.println(" S_UDT"); break; + case S_COBOLUDT2: System.out.println(" S_COBOLUDT2"); break; + case S_MANYREG2: System.out.println(" S_MANYREG2"); break; + case S_BPREL32: System.out.println(" S_BPREL32"); break; + case S_LDATA32: System.out.println(" S_LDATA32"); break; + case S_GDATA32: System.out.println(" S_GDATA32"); break; + case S_PUB32: System.out.println(" S_PUB32"); break; + case S_LPROC32: System.out.println(" S_LPROC32"); break; + case S_GPROC32: System.out.println(" S_GPROC32"); break; + case S_THUNK32: System.out.println(" S_THUNK32"); break; + case S_BLOCK32: System.out.println(" S_BLOCK32"); break; + case S_WITH32: System.out.println(" S_WITH32"); break; + case S_LABEL32: System.out.println(" S_LABEL32"); break; + case S_CEXMODEL32: System.out.println(" S_CEXMODEL32"); break; + case S_VFTTABLE32: System.out.println(" S_VFTTABLE32"); break; + case S_REGREL32: System.out.println(" S_REGREL32"); break; + case S_LTHREAD32: System.out.println(" S_LTHREAD32"); break; + case S_GTHREAD32: System.out.println(" S_GTHREAD32"); break; + case S_LPROCMIPS: System.out.println(" S_LPROCMIPS"); break; + case S_GPROCMIPS: System.out.println(" S_GPROCMIPS"); break; + case S_PROCREF: System.out.println(" S_PROCREF"); break; + case S_DATAREF: System.out.println(" S_DATAREF"); break; + case S_ALIGN: System.out.println(" S_ALIGN"); break; + default: System.out.println(" (Unknown symbol type " + type + ")"); break; + } + + iter.next(); + } + } + + private static void printTypeTable(DebugVC50Subsection sec) { + DebugVC50SSGlobalTypes types = (DebugVC50SSGlobalTypes) sec; + + DebugVC50TypeIterator iter = types.getTypeIterator(); + while (!iter.done()) { + System.out.print(" Type string: "); + while (!iter.typeStringDone()) { + int leaf = iter.typeStringLeaf() & 0xFFFF; + switch (leaf) { + case LF_MODIFIER: System.out.print("LF_MODIFIER "); break; + case LF_POINTER: System.out.print("LF_POINTER "); break; + case LF_ARRAY: System.out.print("LF_ARRAY "); break; + case LF_CLASS: System.out.print("LF_CLASS "); break; + case LF_STRUCTURE: System.out.print("LF_STRUCTURE "); break; + case LF_UNION: System.out.print("LF_UNION "); break; + case LF_ENUM: System.out.print("LF_ENUM "); break; + case LF_PROCEDURE: System.out.print("LF_PROCEDURE "); break; + case LF_MFUNCTION: System.out.print("LF_MFUNCTION "); break; + case LF_VTSHAPE: System.out.print("LF_VTSHAPE "); break; + case LF_COBOL0: System.out.print("LF_COBOL0 "); break; + case LF_COBOL1: System.out.print("LF_COBOL1 "); break; + case LF_BARRAY: System.out.print("LF_BARRAY "); break; + case LF_LABEL: System.out.print("LF_LABEL "); break; + case LF_NULL: System.out.print("LF_NULL "); break; + case LF_NOTTRAN: System.out.print("LF_NOTTRAN "); break; + case LF_DIMARRAY: System.out.print("LF_DIMARRAY "); break; + case LF_VFTPATH: System.out.print("LF_VFTPATH "); break; + case LF_PRECOMP: System.out.print("LF_PRECOMP "); break; + case LF_ENDPRECOMP: System.out.print("LF_ENDPRECOMP "); break; + case LF_OEM: System.out.print("LF_OEM "); break; + case LF_TYPESERVER: System.out.print("LF_TYPESERVER "); break; + case LF_SKIP: System.out.print("LF_SKIP "); break; + case LF_ARGLIST: System.out.print("LF_ARGLIST "); break; + case LF_DEFARG: System.out.print("LF_DEFARG "); break; + case LF_FIELDLIST: System.out.print("LF_FIELDLIST "); break; + case LF_DERIVED: System.out.print("LF_DERIVED "); break; + case LF_BITFIELD: System.out.print("LF_BITFIELD "); break; + case LF_METHODLIST: System.out.print("LF_METHODLIST "); break; + case LF_DIMCONU: System.out.print("LF_DIMCONU "); break; + case LF_DIMCONLU: System.out.print("LF_DIMCONLU "); break; + case LF_DIMVARU: System.out.print("LF_DIMVARU "); break; + case LF_DIMVARLU: System.out.print("LF_DIMVARLU "); break; + case LF_REFSYM: System.out.print("LF_REFSYM "); break; + case LF_BCLASS: System.out.print("LF_BCLASS "); break; + case LF_VBCLASS: System.out.print("LF_VBCLASS "); break; + case LF_IVBCLASS: System.out.print("LF_IVBCLASS "); break; + case LF_ENUMERATE: System.out.print("LF_ENUMERATE "); break; + case LF_FRIENDFCN: System.out.print("LF_FRIENDFCN "); break; + case LF_INDEX: System.out.print("LF_INDEX "); break; + case LF_MEMBER: System.out.print("LF_MEMBER "); System.out.print(iter.getMemberName() + " "); break; + case LF_STMEMBER: System.out.print("LF_STMEMBER "); break; + case LF_METHOD: System.out.print("LF_METHOD "); System.out.print(iter.getMethodName() + " "); break; + case LF_NESTTYPE: System.out.print("LF_NESTTYPE "); break; + case LF_VFUNCTAB: System.out.print("LF_VFUNCTAB "); break; + case LF_FRIENDCLS: System.out.print("LF_FRIENDCLS "); break; + case LF_ONEMETHOD: System.out.print("LF_ONEMETHOD "); System.out.print(iter.getOneMethodName() + " "); break; + case LF_VFUNCOFF: System.out.print("LF_VFUNCOFF "); break; + case LF_NESTTYPEEX: System.out.print("LF_NESTTYPEEX "); break; + case LF_MEMBERMODIFY: System.out.print("LF_MEMBERMODIFY "); break; + case LF_CHAR: System.out.print("LF_CHAR "); break; + case LF_SHORT: System.out.print("LF_SHORT "); break; + case LF_USHORT: System.out.print("LF_USHORT "); break; + case LF_LONG: System.out.print("LF_LONG "); break; + case LF_ULONG: System.out.print("LF_ULONG "); break; + case LF_REAL32: System.out.print("LF_REAL32 "); break; + case LF_REAL64: System.out.print("LF_REAL64 "); break; + case LF_REAL80: System.out.print("LF_REAL80 "); break; + case LF_REAL128: System.out.print("LF_REAL128 "); break; + case LF_QUADWORD: System.out.print("LF_QUADWORD "); break; + case LF_UQUADWORD: System.out.print("LF_UQUADWORD "); break; + case LF_REAL48: System.out.print("LF_REAL48 "); break; + case LF_COMPLEX32: System.out.print("LF_COMPLEX32 "); break; + case LF_COMPLEX64: System.out.print("LF_COMPLEX64 "); break; + case LF_COMPLEX80: System.out.print("LF_COMPLEX80 "); break; + case LF_COMPLEX128: System.out.print("LF_COMPLEX128 "); break; + case LF_VARSTRING: System.out.print("LF_VARSTRING "); break; + case LF_PAD0: System.out.print("LF_PAD0 "); break; + case LF_PAD1: System.out.print("LF_PAD1 "); break; + case LF_PAD2: System.out.print("LF_PAD2 "); break; + case LF_PAD3: System.out.print("LF_PAD3 "); break; + case LF_PAD4: System.out.print("LF_PAD4 "); break; + case LF_PAD5: System.out.print("LF_PAD5 "); break; + case LF_PAD6: System.out.print("LF_PAD6 "); break; + case LF_PAD7: System.out.print("LF_PAD7 "); break; + case LF_PAD8: System.out.print("LF_PAD8 "); break; + case LF_PAD9: System.out.print("LF_PAD9 "); break; + case LF_PAD10: System.out.print("LF_PAD10 "); break; + case LF_PAD11: System.out.print("LF_PAD11 "); break; + case LF_PAD12: System.out.print("LF_PAD12 "); break; + case LF_PAD13: System.out.print("LF_PAD13 "); break; + case LF_PAD14: System.out.print("LF_PAD14 "); break; + case LF_PAD15: System.out.print("LF_PAD15 "); break; + default: System.out.print("(Unknown leaf " + leaf + ")"); + } + + iter.typeStringNext(); + } + + System.out.println(""); + iter.next(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/TestParser.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/TestParser.java new file mode 100644 index 00000000000..52c2be21cc0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/TestParser.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +public class TestParser { + public static void main(String[] args) { + if (args.length != 1) { + System.err.println("usage: java TestParser [file name]"); + System.err.println("File name may be an .exe, .dll or .obj"); + System.exit(1); + } + + try { + COFFFile file = COFFFileParser.getParser().parse(args[0]); + if (file.isImage()) { + System.out.println("PE Image detected."); + } else { + System.out.println("PE Image NOT detected, assuming object file."); + } + COFFHeader header = file.getHeader(); + int numSections = header.getNumberOfSections(); + System.out.println(numSections + " sections detected."); + for (int i = 0; i < numSections; i++) { + SectionHeader secHeader = header.getSectionHeader(1 + i); + System.out.println(secHeader.getName()); + } + + // FIXME: the DLL exports are not contained in the COFF symbol + // table. Instead, they are in the Export Table, one of the + // Optional Header Data Directories available from the Optional + // Header. Must implement that next. + + /* + int numSymbols = header.getNumberOfSymbols(); + System.out.println(numSymbols + " symbols detected."); + System.out.println("Symbol dump:"); + for (int i = 0; i < header.getNumberOfSymbols(); i++) { + COFFSymbol sym = header.getCOFFSymbol(i); + System.out.println(" " + sym.getName()); + } + */ + + // OK, let's give the exported symbols a shot + OptionalHeader optHdr = header.getOptionalHeader(); + OptionalHeaderDataDirectories ddirs = optHdr.getDataDirectories(); + ExportDirectoryTable exports = ddirs.getExportDirectoryTable(); + System.out.println("Export flags (should be 0): " + exports.getExportFlags()); + System.out.println("DLL name (from export directory table): " + + exports.getDLLName()); + int numSymbols = exports.getNumberOfNamePointers(); + System.out.println(numSymbols + " exported symbols detected:"); + for (int i = 0; i < numSymbols; i++) { + System.out.println(" " + exports.getExportName(i)); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/TypeIndicators.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/TypeIndicators.java new file mode 100644 index 00000000000..c58dafc1acb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/TypeIndicators.java @@ -0,0 +1,348 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Enumerates the types of COFF object file relocations for all + currently-supported processors. (Some of the descriptions are + taken directly from Microsoft's documentation and are copyrighted + by Microsoft.) */ + +public interface TypeIndicators { + // + // I386 processors + // + + /** This relocation is ignored. */ + public static final short IMAGE_REL_I386_ABSOLUTE = 0x0000; + /** Not supported. */ + public static final short IMAGE_REL_I386_DIR16 = (short) 0x0001; + /** Not supported. */ + public static final short IMAGE_REL_I386_REL16 = (short) 0x0002; + /** The target?s 32-bit virtual address. */ + public static final short IMAGE_REL_I386_DIR32 = (short) 0x0006; + /** The target?s 32-bit relative virtual address. */ + public static final short IMAGE_REL_I386_DIR32NB = (short) 0x0007; + /** Not supported. */ + public static final short IMAGE_REL_I386_SEG12 = (short) 0x0009; + /** The 16-bit-section index of the section containing the + target. This is used to support debugging information. */ + public static final short IMAGE_REL_I386_SECTION = (short) 0x000A; + /** The 32-bit offset of the target from the beginning of its + section. This is used to support debugging information as well + as static thread local storage. */ + public static final short IMAGE_REL_I386_SECREL = (short) 0x000B; + /** The 32-bit relative displacement to the target. This supports + the x86 relative branch and call instructions. */ + public static final short IMAGE_REL_I386_REL32 = (short) 0x0014; + + // + // MIPS processors + // + + /** This relocation is ignored. */ + public static final short IMAGE_REL_MIPS_ABSOLUTE = (short) 0x0000; + /** The high 16 bits of the target's 32-bit virtual address. */ + public static final short IMAGE_REL_MIPS_REFHALF = (short) 0x0001; + /** The target's 32-bit virtual address. */ + public static final short IMAGE_REL_MIPS_REFWORD = (short) 0x0002; + /** The low 26 bits of the target's virtual address. This + supports the MIPS J and JAL instructions. */ + public static final short IMAGE_REL_MIPS_JMPADDR = (short) 0x0003; + /** The high 16 bits of the target's 32-bit virtual address. Used + for the first instruction in a two-instruction sequence that + loads a full address. This relocation must be immediately + followed by a PAIR relocations whose SymbolTableIndex contains a + signed 16-bit displacement which is added to the upper 16 bits + taken from the location being relocated. */ + public static final short IMAGE_REL_MIPS_REFHI = (short) 0x0004; + /** The low 16 bits of the target's virtual address. */ + public static final short IMAGE_REL_MIPS_REFLO = (short) 0x0005; + /** 16-bit signed displacement of the target relative to the Global + Pointer (GP) register. */ + public static final short IMAGE_REL_MIPS_GPREL = (short) 0x0006; + /** Same as IMAGE_REL_MIPS_GPREL. */ + public static final short IMAGE_REL_MIPS_LITERAL = (short) 0x0007; + /** The 16-bit section index of the section containing the target. + This is used to support debugging information. */ + public static final short IMAGE_REL_MIPS_SECTION = (short) 0x000A; + /** The 32-bit offset of the target from the beginning of its + section. This is used to support debugging information as well + as static thread local storage. */ + public static final short IMAGE_REL_MIPS_SECREL = (short) 0x000B; + /** The low 16 bits of the 32-bit offset of the target from the + beginning of its section. */ + public static final short IMAGE_REL_MIPS_SECRELLO = (short) 0x000C; + /** The high 16 bits of the 32-bit offset of the target from the + beginning of its section. A PAIR relocation must immediately + follow this on. The SymbolTableIndex of the PAIR relocation + contains a signed 16-bit displacement, which is added to the + upper 16 bits taken from the location being relocated. */ + public static final short IMAGE_REL_MIPS_SECRELHI = (short) 0x000D; + /** The low 26 bits of the target's virtual address. This supports + the MIPS16 JAL instruction. */ + public static final short IMAGE_REL_MIPS_JMPADDR16 = (short) 0x0010; + /** The target's 32-bit relative virtual address. */ + public static final short IMAGE_REL_MIPS_REFWORDNB = (short) 0x0022; + /** This relocation is only valid when it immediately follows a + REFHI or SECRELHI relocation. Its SymbolTableIndex contains a + displacement and not an index into the symbol table. */ + public static final short IMAGE_REL_MIPS_PAIR = (short) 0x0025; + + // + // Alpha processors + // + + /** This relocation is ignored. */ + public static final short IMAGE_REL_ALPHA_ABSOLUTE = (short) 0x0000; + /** The target's 32-bit virtual address. This fixup is illegal in a + PE32+ image unless the image has been sandboxed by clearing the + IMAGE_FILE_LARGE_ADDRESS_AWARE bit in the File Header. */ + public static final short IMAGE_REL_ALPHA_REFLONG = (short) 0x0001; + /** The target's 64-bit virtual address. */ + public static final short IMAGE_REL_ALPHA_REFQUAD = (short) 0x0002; + /** 32-bit signed displacement of the target relative to the Global + Pointer (GP) register. */ + public static final short IMAGE_REL_ALPHA_GPREL32 = (short) 0x0003; + /** 16-bit signed displacement of the target relative to the Global + Pointer (GP) register. */ + public static final short IMAGE_REL_ALPHA_LITERAL = (short) 0x0004; + /** Reserved for future use. */ + public static final short IMAGE_REL_ALPHA_LITUSE = (short) 0x0005; + /** Reserved for future use. */ + public static final short IMAGE_REL_ALPHA_GPDISP = (short) 0x0006; + /** The 21-bit relative displacement to the target. This supports + the Alpha relative branch instructions. */ + public static final short IMAGE_REL_ALPHA_BRADDR = (short) 0x0007; + /** 14-bit hints to the processor for the target of an Alpha jump + instruction. */ + public static final short IMAGE_REL_ALPHA_HINT = (short) 0x0008; + /** The target's 32-bit virtual address split into high and low + 16-bit parts. Either an ABSOLUTE or MATCH relocation must + immediately follow this relocation. The high 16 bits of the + target address are stored in the location identified by the + INLINE_REFLONG relocation. The low 16 bits are stored four bytes + later if the following relocation is of type ABSOLUTE or at a + signed displacement given in the SymbolTableIndex if the + following relocation is of type MATCH. */ + public static final short IMAGE_REL_ALPHA_INLINE_REFLONG = (short) 0x0009; + /** The high 16 bits of the target's 32-bit virtual address. Used + for the first instruction in a two-instruction sequence that + loads a full address. This relocation must be immediately + followed by a PAIR relocations whose SymbolTableIndex contains a + signed 16-bit displacement which is added to the upper 16 bits + taken from the location being relocated. */ + public static final short IMAGE_REL_ALPHA_REFHI = (short) 0x000A; + /** The low 16 bits of the target's virtual address. */ + public static final short IMAGE_REL_ALPHA_REFLO = (short) 0x000B; + /** This relocation is only valid when it immediately follows a + REFHI , REFQ3, REFQ2, or SECRELHI relocation. Its + SymbolTableIndex contains a displacement and not an index into + the symbol table. */ + public static final short IMAGE_REL_ALPHA_PAIR = (short) 0x000C; + /** This relocation is only valid when it immediately follows + INLINE_REFLONG relocation. Its SymbolTableIndex contains the + displacement in bytes of the location for the matching low + address and not an index into the symbol table. */ + public static final short IMAGE_REL_ALPHA_MATCH = (short) 0x000D; + /** The 16-bit section index of the section containing the target. + This is used to support debugging information. */ + public static final short IMAGE_REL_ALPHA_SECTION = (short) 0x000E; + /** The 32-bit offset of the target from the beginning of its + section. This is used to support debugging information as well + as static thread local storage. */ + public static final short IMAGE_REL_ALPHA_SECREL = (short) 0x000F; + /** The target's 32-bit relative virtual address. */ + public static final short IMAGE_REL_ALPHA_REFLONGNB = (short) 0x0010; + /** The low 16 bits of the 32-bit offset of the target from the + beginning of its section. */ + public static final short IMAGE_REL_ALPHA_SECRELLO = (short) 0x0011; + /** The high 16 bits of the 32-bit offset of the target from the + beginning of its section. A PAIR relocation must immediately + follow this on. The SymbolTableIndex of the PAIR relocation + contains a signed 16-bit displacement which is added to the + upper 16 bits taken from the location being relocated. */ + public static final short IMAGE_REL_ALPHA_SECRELHI = (short) 0x0012; + /** The low 16 bits of the high 32 bits of the target's 64-bit + virtual address. This relocation must be immediately followed by + a PAIR relocations whose SymbolTableIndex contains a signed + 32-bit displacement which is added to the 16 bits taken from the + location being relocated. The 16 bits in the relocated location + are shifted left by 32 before this addition. */ + public static final short IMAGE_REL_ALPHA_REFQ3 = (short) 0x0013; + /** The high 16 bits of the low 32 bits of the target's 64-bit + virtual address. This relocation must be immediately followed by + a PAIR relocations whose SymbolTableIndex contains a signed + 16-bit displacement which is added to the upper 16 bits taken + from the location being relocated. */ + public static final short IMAGE_REL_ALPHA_REFQ2 = (short) 0x0014; + /** The low 16 bits of the target's 64-bit virtual address. */ + public static final short IMAGE_REL_ALPHA_REFQ1 = (short) 0x0015; + /** The low 16 bits of the 32-bit signed displacement of the target + relative to the Global Pointer (GP) register. */ + public static final short IMAGE_REL_ALPHA_GPRELLO = (short) 0x0016; + /** The high 16 bits of the 32-bit signed displacement of the target + relative to the Global Pointer (GP) register. */ + public static final short IMAGE_REL_ALPHA_GPRELHI = (short) 0x0017; + + // + // PowerPC processors + // + + /** This relocation is ignored. */ + public static final short IMAGE_REL_PPC_ABSOLUTE = (short) 0x0000; + /** The target's 64-bit virtual address. */ + public static final short IMAGE_REL_PPC_ADDR64 = (short) 0x0001; + /** The target's 32-bit virtual address. */ + public static final short IMAGE_REL_PPC_ADDR32 = (short) 0x0002; + /** The low 24 bits of the target's virtual address. This is only + valid when the target symbol is absolute and can be sign + extended to its original value. */ + public static final short IMAGE_REL_PPC_ADDR24 = (short) 0x0003; + /** The low 16 bits of the target's virtual address. */ + public static final short IMAGE_REL_PPC_ADDR16 = (short) 0x0004; + /** The low 14 bits of the target's virtual address. This is only + valid when the target symbol is absolute and can be sign + extended to its original value. */ + public static final short IMAGE_REL_PPC_ADDR14 = (short) 0x0005; + /** A 24-bit PC-relative offset to the symbol's location. */ + public static final short IMAGE_REL_PPC_REL24 = (short) 0x0006; + /** A 14-bit PC-relative offset to the symbol's location. */ + public static final short IMAGE_REL_PPC_REL14 = (short) 0x0007; + /** The target's 32-bit relative virtual address. */ + public static final short IMAGE_REL_PPC_ADDR32NB = (short) 0x000A; + /** The 32-bit offset of the target from the beginning of its + section. This is used to support debugging information as well + as static thread local storage. */ + public static final short IMAGE_REL_PPC_SECREL = (short) 0x000B; + /** The 16-bit section index of the section containing the target. + This is used to support debugging information. */ + public static final short IMAGE_REL_PPC_SECTION = (short) 0x000C; + /** The 16-bit offset of the target from the beginning of its + section. This is used to support debugging information as well + as static thread local storage. */ + public static final short IMAGE_REL_PPC_SECREL16 = (short) 0x000F; + /** The high 16 bits of the target's 32-bit virtual address. Used + for the first instruction in a two-instruction sequence that + loads a full address. This relocation must be immediately + followed by a PAIR relocations whose SymbolTableIndex contains a + signed 16-bit displacement which is added to the upper 16 bits + taken from the location being relocated. */ + public static final short IMAGE_REL_PPC_REFHI = (short) 0x0010; + /** The low 16 bits of the target's virtual address. */ + public static final short IMAGE_REL_PPC_REFLO = (short) 0x0011; + /** This relocation is only valid when it immediately follows a + REFHI or SECRELHI relocation. Its SymbolTableIndex contains a + displacement and not an index into the symbol table. */ + public static final short IMAGE_REL_PPC_PAIR = (short) 0x0012; + /** The low 16 bits of the 32-bit offset of the target from the + beginning of its section. */ + public static final short IMAGE_REL_PPC_SECRELLO = (short) 0x0013; + /** The high 16 bits of the 32-bit offset of the target from the + beginning of its section. A PAIR relocation must immediately + follow this on. The SymbolTableIndex of the PAIR relocation + contains a signed 16-bit displacement which is added to the + upper 16 bits taken from the location being relocated. */ + public static final short IMAGE_REL_PPC_SECRELHI = (short) 0x0014; + /** 16-bit signed displacement of the target relative to the Global + Pointer (GP) register. */ + public static final short IMAGE_REL_PPC_GPREL = (short) 0x0015; + + // + // SH3 and SH4 processors + // + + /** This relocation is ignored. */ + public static final short IMAGE_REL_SH3_ABSOLUTE = (short) 0x0000; + /** Reference to the 16-bit location that contains the virtual + address of the target symbol. */ + public static final short IMAGE_REL_SH3_DIRECT16 = (short) 0x0001; + /** The target's 32-bit virtual address. */ + public static final short IMAGE_REL_SH3_DIRECT32 = (short) 0x0002; + /** Reference to the 8-bit location that contains the virtual + address of the target symbol. */ + public static final short IMAGE_REL_SH3_DIRECT8 = (short) 0x0003; + /** Reference to the 8-bit instruction that contains the effective + 16-bit virtual address of the target symbol. */ + public static final short IMAGE_REL_SH3_DIRECT8_WORD = (short) 0x0004; + /** Reference to the 8-bit instruction that contains the effective + 32-bit virtual address of the target symbol. */ + public static final short IMAGE_REL_SH3_DIRECT8_LONG = (short) 0x0005; + /** Reference to the 8-bit location whose low 4 bits contain the + virtual address of the target symbol. */ + public static final short IMAGE_REL_SH3_DIRECT4 = (short) 0x0006; + /** Reference to the 8-bit instruction whose low 4 bits contain the + effective 16-bit virtual address of the target symbol. */ + public static final short IMAGE_REL_SH3_DIRECT4_WORD = (short) 0x0007; + /** Reference to the 8-bit instruction whose low 4 bits contain the + effective 32-bit virtual address of the target symbol. */ + public static final short IMAGE_REL_SH3_DIRECT4_LONG = (short) 0x0008; + /** Reference to the 8-bit instruction which contains the effective + 16-bit relative offset of the target symbol. */ + public static final short IMAGE_REL_SH3_PCREL8_WORD = (short) 0x0009; + /** Reference to the 8-bit instruction which contains the effective + 32-bit relative offset of the target symbol. */ + public static final short IMAGE_REL_SH3_PCREL8_LONG = (short) 0x000A; + /** Reference to the 16-bit instruction whose low 12 bits contain + the effective 16-bit relative offset of the target symbol. */ + public static final short IMAGE_REL_SH3_PCREL12_WORD = (short) 0x000B; + /** Reference to a 32-bit location that is the virtual address of + the symbol's section. */ + public static final short IMAGE_REL_SH3_STARTOF_SECTION = (short) 0x000C; + /** Reference to the 32-bit location that is the size of the + symbol's section. */ + public static final short IMAGE_REL_SH3_SIZEOF_SECTION = (short) 0x000D; + /** The 16-bit section index of the section containing the target. + This is used to support debugging information. */ + public static final short IMAGE_REL_SH3_SECTION = (short) 0x000E; + /** The 32-bit offset of the target from the beginning of its + section. This is used to support debugging information as well + as static thread local storage. */ + public static final short IMAGE_REL_SH3_SECREL = (short) 0x000F; + /** The target's 32-bit relative virtual address. */ + public static final short IMAGE_REL_SH3_DIRECT32_NB = (short) 0x0010; + + // + // ARM processors + // + + /** This relocation is ignored. */ + public static final short IMAGE_REL_ARM_ABSOLUTE = (short) 0x0000; + /** The target's 32-bit virtual address. */ + public static final short IMAGE_REL_ARM_ADDR32 = (short) 0x0001; + /** The target's 32-bit relative virtual address. */ + public static final short IMAGE_REL_ARM_ADDR32NB = (short) 0x0002; + /** The 24-bit relative displacement to the target. */ + public static final short IMAGE_REL_ARM_BRANCH24 = (short) 0x0003; + /** Reference to a subroutine call, consisting of two 16-bit + instructions with 11-bit offsets. */ + public static final short IMAGE_REL_ARM_BRANCH11 = (short) 0x0004; + /** The 16-bit section index of the section containing the target. + This is used to support debugging information. */ + public static final short IMAGE_REL_ARM_SECTION = (short) 0x000E; + /** The 32-bit offset of the target from the beginning of its + section. This is used to support debugging information as well + as static thread local storage. */ + public static final short IMAGE_REL_ARM_SECREL = (short) 0x000F; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/WindowsNTSubsystem.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/WindowsNTSubsystem.java new file mode 100644 index 00000000000..71f2230c447 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/WindowsNTSubsystem.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +/** Constants enumerating which Windows NT subsystem a given Portable + Executable runs in; see {@link + sun.jvm.hotspot.debugger.win32.coff.OptionalHeaderWindowsSpecificFields#getSubsystem}. + (Some of the descriptions are taken directly from Microsoft's + documentation and are copyrighted by Microsoft.) */ + +public interface WindowsNTSubsystem { + /** Unknown subsystem. */ + public short IMAGE_SUBSYSTEM_UNKNOWN = (short) 0; + + /** Used for device drivers and native Windows NT processes. */ + public short IMAGE_SUBSYSTEM_NATIVE = (short) 1; + + /** Image runs in the Windows graphical user interface (GUI) subsystem. */ + public short IMAGE_SUBSYSTEM_WINDOWS_GUI = (short) 2; + + /** Image runs in the Windows character subsystem. */ + public short IMAGE_SUBSYSTEM_WINDOWS_CUI = (short) 3; + + /** Image runs in the Posix character subsystem. */ + public short IMAGE_SUBSYSTEM_POSIX_CUI = (short) 7; + + /** Image runs on Windows CE. */ + public short IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = (short) 9; + + /** Image is an EFI application. */ + public short IMAGE_SUBSYSTEM_EFI_APPLICATION = (short) 10; + + /** Image is an EFI driver that provides boot services. */ + public short IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = (short) 11; + + /** Image is an EFI driver that provides runtime services. */ + public short IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = (short) 12; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/AddressDataSource.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/AddressDataSource.java new file mode 100644 index 00000000000..d29c9eae9f3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/AddressDataSource.java @@ -0,0 +1,99 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg; + +import java.io.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.win32.coff.*; + +public class AddressDataSource implements DataSource { + public AddressDataSource(Address addr) { + this.addr = addr; + offset = 0; + } + + public byte readByte() throws IOException { + try { + byte res = (byte) addr.getCIntegerAt(offset, 1, false); + ++offset; + return res; + } catch (UnmappedAddressException e) { + throw (IOException) new IOException("Unmapped address at 0x" + Long.toHexString(e.getAddress())).initCause(e); + } catch (DebuggerException e) { + throw (IOException) new IOException().initCause(e); + } + } + + public short readShort() throws IOException { + // NOTE: byte swapping is taken care of at the COFFFileImpl level + int b1 = readByte() & 0xFF; + int b2 = readByte() & 0xFF; + return (short) ((b1 << 8) | b2); + } + + public int readInt() throws IOException { + // NOTE: byte swapping is taken care of at the COFFFileImpl level + int b1 = ((int) readByte()) & 0xFF; + int b2 = ((int) readByte()) & 0xFF; + int b3 = ((int) readByte()) & 0xFF; + int b4 = ((int) readByte()) & 0xFF; + return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4); + } + + public long readLong() throws IOException { + // NOTE: byte swapping is taken care of at the COFFFileImpl level + long b1 = ((long) readByte()) & 0xFFL; + long b2 = ((long) readByte()) & 0xFFL; + long b3 = ((long) readByte()) & 0xFFL; + long b4 = ((long) readByte()) & 0xFFL; + long b5 = ((long) readByte()) & 0xFFL; + long b6 = ((long) readByte()) & 0xFFL; + long b7 = ((long) readByte()) & 0xFFL; + long b8 = ((long) readByte()) & 0xFFL; + return (((((b1 << 24) | (b2 << 16) | (b3 << 8) | b4)) << 32) | + ((((b5 << 24) | (b6 << 16) | (b7 << 8) | b8)))); + } + + public int read(byte[] b) throws IOException { + for (int i = 0; i < b.length; i++) { + b[i] = readByte(); + } + return b.length; + } + + public void seek(long pos) throws IOException { + offset = pos; + } + + public long getFilePointer() throws IOException { + return offset; + } + + public void close() throws IOException { + } + + private Address addr; + private long offset; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/DLL.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/DLL.java new file mode 100644 index 00000000000..305f3d1f733 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/DLL.java @@ -0,0 +1,209 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.win32.coff.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.utilities.Assert; +import sun.jvm.hotspot.utilities.memo.*; + +/** Provides a simple wrapper around the COFF library which handles + relocation. A DLL can represent either a DLL or an EXE file. */ + +public class DLL implements LoadObject { + + public DLL(WindbgDebugger dbg, String filename, long size, Address relocation) throws COFFException { + this.dbg = dbg; + fullPathName = filename; + this.size = size; + file = new MemoizedObject() { + public Object computeValue() { + return COFFFileParser.getParser().parse(fullPathName); + } + }; + addr = relocation; + } + + /** This constructor was originally used to fetch the DLL's name out + of the target process to match it up with the known DLL names, + before the fetching of the DLL names and bases was folded into + one command. It is no longer used. If it is used, getName() will + return null and getSize() will return 0. */ + public DLL(Address base) throws COFFException { + this.addr = base; + file = new MemoizedObject() { + public Object computeValue() { + return COFFFileParser.getParser().parse(new AddressDataSource(addr)); + } + }; + } + + /** Indicates whether this is really a DLL or actually a .EXE + file. */ + public boolean isDLL() { + return getFile().getHeader().hasCharacteristic(Characteristics.IMAGE_FILE_DLL); + } + + /** Look up a symbol; returns absolute address or null if symbol was + not found. */ + public Address lookupSymbol(String symbol) throws COFFException { + if (!isDLL()) { + return null; + } + ExportDirectoryTable exports = getExportDirectoryTable(); + return lookupSymbol(symbol, exports, + 0, exports.getNumberOfNamePointers() - 1); + } + + public Address getBase() { + return addr; + } + + /** Returns the full path name of this DLL/EXE, or null if this DLL + object was created by parsing the target process's address + space. */ + public String getName() { + return fullPathName; + } + + public long getSize() { + return size; + } + + public CDebugInfoDataBase getDebugInfoDataBase() throws DebuggerException { + if (db != null) { + return db; + } + + // Try to parse + if (dbg == null) { + return null; // Need WindbgDebugger + } + + if (Assert.ASSERTS_ENABLED) { + Assert.that(fullPathName != null, "Need full path name to build debug info database"); + } + + db = new WindbgCDebugInfoBuilder(dbg).buildDataBase(fullPathName, addr); + return db; + } + + public BlockSym debugInfoForPC(Address pc) throws DebuggerException { + CDebugInfoDataBase db = getDebugInfoDataBase(); + if (db == null) { + return null; + } + return db.debugInfoForPC(pc); + } + + public ClosestSymbol closestSymbolToPC(Address pcAsAddr) throws DebuggerException { + ExportDirectoryTable exports = getExportDirectoryTable(); + if (exports == null) { + return null; + } + String name = null; + long pc = dbg.getAddressValue(pcAsAddr); + long diff = Long.MAX_VALUE; + long base = dbg.getAddressValue(addr); + for (int i = 0; i < exports.getNumberOfNamePointers(); i++) { + if (!exports.isExportAddressForwarder(exports.getExportOrdinal(i))) { + long tmp = base + (exports.getExportAddress(exports.getExportOrdinal(i)) & 0xFFFFFFFF); + if ((tmp <= pc) && ((pc - tmp) < diff)) { + diff = pc - tmp; + name = exports.getExportName(i); + } + } + } + if (name == null) { + return null; + } + return new ClosestSymbol(name, diff); + } + + public LineNumberInfo lineNumberForPC(Address pc) throws DebuggerException { + CDebugInfoDataBase db = getDebugInfoDataBase(); + if (db == null) { + return null; + } + return db.lineNumberForPC(pc); + } + + public void close() { + getFile().close(); + file = null; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private COFFFile getFile() { + return (COFFFile) file.getValue(); + } + + private Address lookupSymbol(String symbol, ExportDirectoryTable exports, + int loIdx, int hiIdx) { + do { + int curIdx = ((loIdx + hiIdx) >> 1); + String cur = exports.getExportName(curIdx); + if (symbol.equals(cur)) { + return addr.addOffsetTo( + ((long) exports.getExportAddress(exports.getExportOrdinal(curIdx))) & 0xFFFFFFFFL + ); + } + if (symbol.compareTo(cur) < 0) { + if (hiIdx == curIdx) { + hiIdx = curIdx - 1; + } else { + hiIdx = curIdx; + } + } else { + if (loIdx == curIdx) { + loIdx = curIdx + 1; + } else { + loIdx = curIdx; + } + } + } while (loIdx <= hiIdx); + + return null; + } + + private ExportDirectoryTable getExportDirectoryTable() { + return + getFile().getHeader().getOptionalHeader().getDataDirectories().getExportDirectoryTable(); + } + + private WindbgDebugger dbg; + private String fullPathName; + private long size; + // MemoizedObject contains a COFFFile + private MemoizedObject file; + // Base address of module in target process + private Address addr; + // Debug info database for this DLL + private CDebugInfoDataBase db; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgAddress.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgAddress.java new file mode 100644 index 00000000000..6033ee1b192 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgAddress.java @@ -0,0 +1,396 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg; + +import sun.jvm.hotspot.debugger.*; + +class WindbgAddress implements Address { + protected WindbgDebugger debugger; + protected long addr; + + WindbgAddress(WindbgDebugger debugger, long addr) { + this.debugger = debugger; + this.addr = addr; + } + + // + // Basic Java routines + // + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof WindbgAddress)) { + return false; + } + + return (addr == ((WindbgAddress) arg).addr); + } + + public int hashCode() { + // FIXME: suggestions on a better hash code? + return (int) addr; + } + + public String toString() { + return debugger.addressValueToString(addr); + } + + // + // C/C++-related routines + // + + public long getCIntegerAt(long offset, long numBytes, boolean isUnsigned) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readCInteger(addr + offset, numBytes, isUnsigned); + } + + public Address getAddressAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readAddress(addr + offset); + } + + // + // Java-related routines + // + + public boolean getJBooleanAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJBoolean(addr + offset); + } + + public byte getJByteAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJByte(addr + offset); + } + + public char getJCharAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJChar(addr + offset); + } + + public double getJDoubleAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJDouble(addr + offset); + } + + public float getJFloatAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJFloat(addr + offset); + } + + public int getJIntAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJInt(addr + offset); + } + + public long getJLongAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJLong(addr + offset); + } + + public short getJShortAt(long offset) throws UnalignedAddressException, UnmappedAddressException { + return debugger.readJShort(addr + offset); + } + + public OopHandle getOopHandleAt(long offset) + throws UnalignedAddressException, UnmappedAddressException, NotInHeapException { + return debugger.readOopHandle(addr + offset); + } + + // + // C/C++-related mutators + // + + public void setCIntegerAt(long offset, long numBytes, long value) { + throw new DebuggerException("Unimplemented"); + } + public void setAddressAt(long offset, Address value) { + throw new DebuggerException("Unimplemented"); + } + + // + // Java-related mutators + // + + // Mutators -- not implemented for now (FIXME) + public void setJBooleanAt (long offset, boolean value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJByteAt (long offset, byte value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJCharAt (long offset, char value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJDoubleAt (long offset, double value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJFloatAt (long offset, float value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJIntAt (long offset, int value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJLongAt (long offset, long value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setJShortAt (long offset, short value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + public void setOopHandleAt (long offset, OopHandle value) + throws UnmappedAddressException, UnalignedAddressException { + throw new DebuggerException("Unimplemented"); + } + + // + // Arithmetic operations -- necessary evil. + // + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new WindbgAddress(debugger, value); + } + + public OopHandle addOffsetToAsOopHandle(long offset) throws UnsupportedOperationException { + long value = addr + offset; + if (value == 0) { + return null; + } + return new WindbgOopHandle(debugger, value); + } + + /** (FIXME: any signed/unsigned issues? Should this work for + OopHandles?) */ + public long minus(Address arg) { + if (arg == null) { + return addr; + } + return addr - ((WindbgAddress) arg).addr; + } + + // Two's complement representation. + // All negative numbers are larger than positive numbers. + // Numbers with the same sign can be compared normally. + // Test harness is below in main(). + + public boolean lessThan (Address a) { + if (a == null) { + return false; + } + WindbgAddress arg = (WindbgAddress) a; + if ((addr >= 0) && (arg.addr < 0)) { + return true; + } + if ((addr < 0) && (arg.addr >= 0)) { + return false; + } + return (addr < arg.addr); + } + + public boolean lessThanOrEqual (Address a) { + if (a == null) { + return false; + } + WindbgAddress arg = (WindbgAddress) a; + if ((addr >= 0) && (arg.addr < 0)) { + return true; + } + if ((addr < 0) && (arg.addr >= 0)) { + return false; + } + return (addr <= arg.addr); + } + + public boolean greaterThan (Address a) { + if (a == null) { + return true; + } + WindbgAddress arg = (WindbgAddress) a; + if ((addr >= 0) && (arg.addr < 0)) { + return false; + } + if ((addr < 0) && (arg.addr >= 0)) { + return true; + } + return (addr > arg.addr); + } + + public boolean greaterThanOrEqual(Address a) { + if (a == null) { + return true; + } + WindbgAddress arg = (WindbgAddress) a; + if ((addr >= 0) && (arg.addr < 0)) { + return false; + } + if ((addr < 0) && (arg.addr >= 0)) { + return true; + } + return (addr >= arg.addr); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + long value = addr & mask; + if (value == 0) { + return null; + } + return new WindbgAddress(debugger, value); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + long value = addr | mask; + if (value == 0) { + return null; + } + return new WindbgAddress(debugger, value); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + long value = addr ^ mask; + if (value == 0) { + return null; + } + return new WindbgAddress(debugger, value); + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + long getValue() { + return addr; + } + + + private static void check(boolean arg, String failMessage) { + if (!arg) { + System.err.println(failMessage + ": FAILED"); + System.exit(1); + } + } + + // Test harness + public static void main(String[] args) { + // p/n indicates whether the interior address is really positive + // or negative. In unsigned terms, p1 < p2 < n1 < n2. + + WindbgAddress p1 = new WindbgAddress(null, 0x7FFFFFFFFFFFFFF0L); + WindbgAddress p2 = (WindbgAddress) p1.addOffsetTo(10); + WindbgAddress n1 = (WindbgAddress) p2.addOffsetTo(10); + WindbgAddress n2 = (WindbgAddress) n1.addOffsetTo(10); + + // lessThan positive tests + check(p1.lessThan(p2), "lessThan 1"); + check(p1.lessThan(n1), "lessThan 2"); + check(p1.lessThan(n2), "lessThan 3"); + check(p2.lessThan(n1), "lessThan 4"); + check(p2.lessThan(n2), "lessThan 5"); + check(n1.lessThan(n2), "lessThan 6"); + + // lessThan negative tests + check(!p1.lessThan(p1), "lessThan 7"); + check(!p2.lessThan(p2), "lessThan 8"); + check(!n1.lessThan(n1), "lessThan 9"); + check(!n2.lessThan(n2), "lessThan 10"); + + check(!p2.lessThan(p1), "lessThan 11"); + check(!n1.lessThan(p1), "lessThan 12"); + check(!n2.lessThan(p1), "lessThan 13"); + check(!n1.lessThan(p2), "lessThan 14"); + check(!n2.lessThan(p2), "lessThan 15"); + check(!n2.lessThan(n1), "lessThan 16"); + + // lessThanOrEqual positive tests + check(p1.lessThanOrEqual(p1), "lessThanOrEqual 1"); + check(p2.lessThanOrEqual(p2), "lessThanOrEqual 2"); + check(n1.lessThanOrEqual(n1), "lessThanOrEqual 3"); + check(n2.lessThanOrEqual(n2), "lessThanOrEqual 4"); + + check(p1.lessThanOrEqual(p2), "lessThanOrEqual 5"); + check(p1.lessThanOrEqual(n1), "lessThanOrEqual 6"); + check(p1.lessThanOrEqual(n2), "lessThanOrEqual 7"); + check(p2.lessThanOrEqual(n1), "lessThanOrEqual 8"); + check(p2.lessThanOrEqual(n2), "lessThanOrEqual 9"); + check(n1.lessThanOrEqual(n2), "lessThanOrEqual 10"); + + // lessThanOrEqual negative tests + check(!p2.lessThanOrEqual(p1), "lessThanOrEqual 11"); + check(!n1.lessThanOrEqual(p1), "lessThanOrEqual 12"); + check(!n2.lessThanOrEqual(p1), "lessThanOrEqual 13"); + check(!n1.lessThanOrEqual(p2), "lessThanOrEqual 14"); + check(!n2.lessThanOrEqual(p2), "lessThanOrEqual 15"); + check(!n2.lessThanOrEqual(n1), "lessThanOrEqual 16"); + + // greaterThan positive tests + check(n2.greaterThan(p1), "greaterThan 1"); + check(n2.greaterThan(p2), "greaterThan 2"); + check(n2.greaterThan(n1), "greaterThan 3"); + check(n1.greaterThan(p1), "greaterThan 4"); + check(n1.greaterThan(p2), "greaterThan 5"); + check(p2.greaterThan(p1), "greaterThan 6"); + + // greaterThan negative tests + check(!p1.greaterThan(p1), "greaterThan 7"); + check(!p2.greaterThan(p2), "greaterThan 8"); + check(!n1.greaterThan(n1), "greaterThan 9"); + check(!n2.greaterThan(n2), "greaterThan 10"); + + check(!p1.greaterThan(n2), "greaterThan 11"); + check(!p2.greaterThan(n2), "greaterThan 12"); + check(!n1.greaterThan(n2), "greaterThan 13"); + check(!p1.greaterThan(n1), "greaterThan 14"); + check(!p2.greaterThan(n1), "greaterThan 15"); + check(!p1.greaterThan(p2), "greaterThan 16"); + + // greaterThanOrEqual positive tests + check(p1.greaterThanOrEqual(p1), "greaterThanOrEqual 1"); + check(p2.greaterThanOrEqual(p2), "greaterThanOrEqual 2"); + check(n1.greaterThanOrEqual(n1), "greaterThanOrEqual 3"); + check(n2.greaterThanOrEqual(n2), "greaterThanOrEqual 4"); + + check(n2.greaterThanOrEqual(p1), "greaterThanOrEqual 5"); + check(n2.greaterThanOrEqual(p2), "greaterThanOrEqual 6"); + check(n2.greaterThanOrEqual(n1), "greaterThanOrEqual 7"); + check(n1.greaterThanOrEqual(p1), "greaterThanOrEqual 8"); + check(n1.greaterThanOrEqual(p2), "greaterThanOrEqual 9"); + check(p2.greaterThanOrEqual(p1), "greaterThanOrEqual 10"); + + // greaterThanOrEqual negative tests + check(!p1.greaterThanOrEqual(n2), "greaterThanOrEqual 11"); + check(!p2.greaterThanOrEqual(n2), "greaterThanOrEqual 12"); + check(!n1.greaterThanOrEqual(n2), "greaterThanOrEqual 13"); + check(!p1.greaterThanOrEqual(n1), "greaterThanOrEqual 14"); + check(!p2.greaterThanOrEqual(n1), "greaterThanOrEqual 15"); + check(!p1.greaterThanOrEqual(p2), "greaterThanOrEqual 16"); + + System.err.println("WindbgAddress: all tests passed successfully."); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgCDebugInfoBuilder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgCDebugInfoBuilder.java new file mode 100644 index 00000000000..d38cc33be19 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgCDebugInfoBuilder.java @@ -0,0 +1,824 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.win32.coff.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.*; +import sun.jvm.hotspot.utilities.Assert; + +class WindbgCDebugInfoBuilder + implements DebugVC50SubsectionTypes, DebugVC50TypeLeafIndices, DebugVC50TypeEnums, DebugVC50SymbolTypes, DebugVC50MemberAttributes, CVAttributes, AccessControl { + private WindbgDebugger dbg; + private Address base; + + private DebugVC50 vc50; + private BasicCDebugInfoDataBase db; + private DebugVC50TypeIterator iter; + + private DebugVC50SymbolIterator symIter; + + // Logical->physical segment mapping + private COFFFile file; + private DebugVC50SSSegMap segMap; + + // Canonicalization of primitive types + private Map primIndexToTypeMap; + + // Global unnamed enumeration + // (FIXME: must figure out how to handle nested type descriptions) + private BasicEnumType unnamedEnum; + + private Stack blockStack; + private int endsToSkip; + + private static final int POINTER_SIZE = 4; + + WindbgCDebugInfoBuilder(WindbgDebugger dbg) { + this.dbg = dbg; + } + + CDebugInfoDataBase buildDataBase(String dllName, Address base) { + this.base = base; + file = COFFFileParser.getParser().parse(dllName); + vc50 = getDebugVC50(file); + + if (vc50 == null) return null; + + segMap = getSegMap(); + + primIndexToTypeMap = new HashMap(); + blockStack = new Stack(); + endsToSkip = 0; + + db = new BasicCDebugInfoDataBase(); + db.beginConstruction(); + + // Get global types and add them to the database + DebugVC50SSGlobalTypes types = getGlobalTypes(); + for (iter = types.getTypeIterator(); !iter.done(); iter.next()) { + while (!iter.typeStringDone()) { + switch (iter.typeStringLeaf()) { + case LF_MODIFIER: { + int idx = iter.getModifierIndex(); + BasicType target = getTypeByIndex(idx); + short windowsMods = iter.getModifierAttribute(); + short mods = 0; + if ((windowsMods & MODIFIER_CONST_MASK) != 0) mods |= CONST; + if ((windowsMods & MODIFIER_VOLATILE_MASK) != 0) mods |= VOLATILE; + putType(target.getCVVariant(mods)); + break; + } + case LF_POINTER: { + int idx = iter.getPointerType(); + BasicType target = getTypeByIndex(idx); + short windowsMods = iter.getModifierAttribute(); + short mods = 0; + if ((windowsMods & POINTER_CONST_MASK) != 0) mods |= CONST; + if ((windowsMods & POINTER_VOLATILE_MASK) != 0) mods |= VOLATILE; + BasicPointerType ptrType = new BasicPointerType(POINTER_SIZE, target); + if (mods != 0) { + ptrType = (BasicPointerType) ptrType.getCVVariant(mods); + } + + putType(ptrType); + break; + } + case LF_ARRAY: { + BasicType elemType = getTypeByIndex(iter.getArrayElementType()); + putType(new BasicArrayType(iter.getArrayName(), elemType, iter.getArrayLength())); + break; + } + case LF_CLASS: + case LF_STRUCTURE: { + CompoundTypeKind kind = ((iter.typeStringLeaf() == LF_CLASS) ? CompoundTypeKind.CLASS + : CompoundTypeKind.STRUCT); + BasicCompoundType type = new BasicCompoundType(iter.getClassName(), + iter.getClassSize(), + kind); + // Skip parsing of forward references to types + // FIXME: do we have to resolve these later? + if ((iter.getClassProperty() & PROPERTY_FWDREF) == 0) { + DebugVC50TypeIterator fieldIter = iter.getClassFieldListIterator(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(fieldIter.typeStringLeaf() == LF_FIELDLIST, "Expected field list"); + } + boolean advance = false; + while (!fieldIter.typeStringDone()) { + advance = true; + switch (fieldIter.typeStringLeaf()) { + case LF_FIELDLIST: break; + case LF_BCLASS: { + int accessControl = memberAttributeToAccessControl(fieldIter.getBClassAttribute()); + Type baseType = getTypeByIndex(fieldIter.getBClassType()); + // FIXME: take offset into account + type.addBaseClass(new BasicBaseClass(accessControl, false, baseType)); + break; + } + case LF_VBCLASS: { + int accessControl = memberAttributeToAccessControl(fieldIter.getVBClassAttribute()); + Type baseType = getTypeByIndex(fieldIter.getVBClassBaseClassType()); + // FIXME: take offset and virtual base offset into account + type.addBaseClass(new BasicBaseClass(accessControl, true, baseType)); + break; + } + // I don't think we need to handle indirect virtual base + // classes since they should be handled indirectly through + // the modeling of the type hierarchy + case LF_IVBCLASS: break; + case LF_INDEX: { + fieldIter = fieldIter.getIndexIterator(); + advance = false; + break; + } + case LF_MEMBER: { + BasicField field = new BasicField(fieldIter.getMemberName(), + getTypeByIndex(fieldIter.getMemberType()), + memberAttributeToAccessControl(fieldIter.getMemberAttribute()), + false); + field.setOffset(fieldIter.getMemberOffset()); + type.addField(field); + break; + } + case LF_STMEMBER: { + BasicField field = new BasicField(fieldIter.getStaticName(), + getTypeByIndex(fieldIter.getStaticType()), + memberAttributeToAccessControl(fieldIter.getStaticAttribute()), + true); + // The field's address will be found during resolution + // of the debug info database + type.addField(field); + break; + } + // FIXME: handle methods + case LF_METHOD: break; + case LF_ONEMETHOD: break; + // FIXME: handle nested types + case LF_NESTTYPE: break; + case LF_NESTTYPEEX: break; + // NOTE: virtual functions not needed/handled yet for + // this debugging system (because we are not planning to + // handle calling methods in the target process at + // runtime) + case LF_VFUNCTAB: break; + case LF_FRIENDCLS: break; + case LF_VFUNCOFF: break; + case LF_MEMBERMODIFY: break; + case LF_PAD0: case LF_PAD1: case LF_PAD2: case LF_PAD3: + case LF_PAD4: case LF_PAD5: case LF_PAD6: case LF_PAD7: + case LF_PAD8: case LF_PAD9: case LF_PAD10: case LF_PAD11: + case LF_PAD12: case LF_PAD13: case LF_PAD14: case LF_PAD15: break; + default: System.err.println("WARNING: unexpected leaf index " + + fieldIter.typeStringLeaf() + + " in field list for type " + iter.getTypeIndex()); + } + if (advance) { + fieldIter.typeStringNext(); + } + } + } + putType(type); + break; + } + case LF_UNION: { + BasicCompoundType type = new BasicCompoundType(iter.getUnionName(), + iter.getUnionSize(), + CompoundTypeKind.UNION); + // Skip parsing of forward references to types + // FIXME: do we have to resolve these later? + if ((iter.getClassProperty() & PROPERTY_FWDREF) == 0) { + DebugVC50TypeIterator fieldIter = iter.getUnionFieldListIterator(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(fieldIter.typeStringLeaf() == LF_FIELDLIST, "Expected field list"); + } + boolean advance = false; + while (!fieldIter.typeStringDone()) { + advance = true; + switch (fieldIter.typeStringLeaf()) { + case LF_FIELDLIST: break; + case LF_BCLASS: break; + case LF_VBCLASS: break; + case LF_IVBCLASS: break; + case LF_INDEX: { + fieldIter = fieldIter.getIndexIterator(); + advance = false; + break; + } + case LF_MEMBER: { + BasicField field = new BasicField(fieldIter.getMemberName(), + getTypeByIndex(fieldIter.getMemberType()), + memberAttributeToAccessControl(fieldIter.getMemberAttribute()), + false); + field.setOffset(fieldIter.getMemberOffset()); + type.addField(field); + break; + } + case LF_STMEMBER: { + System.err.println("WARNING: I didn't think unions could contain static fields..."); + BasicField field = new BasicField(fieldIter.getStaticName(), + getTypeByIndex(fieldIter.getStaticType()), + memberAttributeToAccessControl(fieldIter.getStaticAttribute()), + true); + // The field's address will be found during resolution + // of the debug info database + type.addField(field); + break; + } + case LF_METHOD: break; + case LF_ONEMETHOD: break; + // FIXME: handle nested types + case LF_NESTTYPE: break; + case LF_NESTTYPEEX: break; + case LF_VFUNCTAB: break; + case LF_FRIENDCLS: break; + case LF_VFUNCOFF: break; + case LF_MEMBERMODIFY: break; + case LF_PAD0: case LF_PAD1: case LF_PAD2: case LF_PAD3: + case LF_PAD4: case LF_PAD5: case LF_PAD6: case LF_PAD7: + case LF_PAD8: case LF_PAD9: case LF_PAD10: case LF_PAD11: + case LF_PAD12: case LF_PAD13: case LF_PAD14: case LF_PAD15: break; + + default: System.err.println("WARNING: unexpected leaf index " + + fieldIter.typeStringLeaf() + + " in field list for union of type " + iter.getTypeIndex()); + } + if (advance) { + fieldIter.typeStringNext(); + } + } + } + putType(type); + break; + } + case LF_ENUM: { + String name = iter.getEnumName(); + BasicEnumType enumType = null; + if ((name == null) || (name.equals(""))) { + if (unnamedEnum == null) { + unnamedEnum = new BasicEnumType(null, getTypeByIndex(iter.getEnumType())); + } + enumType = unnamedEnum; + } else { + enumType = new BasicEnumType(name, getTypeByIndex(iter.getEnumType())); + } + DebugVC50TypeIterator fieldIter = iter.getEnumFieldListIterator(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(fieldIter.typeStringLeaf() == LF_FIELDLIST, "Expected field list"); + } + boolean advance = false; + while (!fieldIter.typeStringDone()) { + advance = true; + switch (fieldIter.typeStringLeaf()) { + case LF_FIELDLIST: break; + case LF_ENUMERATE: { + String enumName = fieldIter.getEnumerateName(); + long enumVal = fieldIter.getEnumerateValue(); + enumType.addEnum(enumName, enumVal); + break; + } + case LF_INDEX: { + fieldIter = fieldIter.getIndexIterator(); + advance = false; + break; + } + + case LF_PAD0: case LF_PAD1: case LF_PAD2: case LF_PAD3: + case LF_PAD4: case LF_PAD5: case LF_PAD6: case LF_PAD7: + case LF_PAD8: case LF_PAD9: case LF_PAD10: case LF_PAD11: + case LF_PAD12: case LF_PAD13: case LF_PAD14: case LF_PAD15: break; + + default: System.err.println("WARNING: unexpected leaf index " + + fieldIter.typeStringLeaf() + + " in field list for enum of type " + iter.getTypeIndex()); + } + + if (advance) { + fieldIter.typeStringNext(); + } + } + + putType(enumType); + break; + } + case LF_PROCEDURE: { + Type retType = getTypeByIndex(iter.getProcedureReturnType()); + BasicFunctionType func = new BasicFunctionType(null, POINTER_SIZE, retType); + DebugVC50TypeIterator argIter = iter.getProcedureArgumentListIterator(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(argIter.typeStringLeaf() == LF_ARGLIST, "Expected argument list"); + } + for (int i = 0; i < argIter.getArgListCount(); i++) { + func.addArgumentType(getTypeByIndex(argIter.getArgListType(i))); + } + putType(func); + break; + } + case LF_MFUNCTION: { + Type retType = getTypeByIndex(iter.getMFunctionReturnType()); + Type container = getTypeByIndex(iter.getMFunctionContainingClass()); + Type thisType = getTypeByIndex(iter.getMFunctionThis()); + long thisAdjust = iter.getMFunctionThisAdjust(); + BasicMemberFunctionType func = new BasicMemberFunctionType(null, + POINTER_SIZE, + retType, + container, + thisType, + thisAdjust); + DebugVC50TypeIterator argIter = iter.getMFunctionArgumentListIterator(); + for (int i = 0; i < argIter.getArgListCount(); i++) { + func.addArgumentType(getTypeByIndex(argIter.getArgListType(i))); + } + putType(func); + break; + } + // FIXME: handle virtual function table shape description + case LF_VTSHAPE: break; + case LF_BARRAY: System.err.println("FIXME: don't know what to do with LF_BARRAY leaves (convert to pointers?"); break; + case LF_LABEL: break; + case LF_NULL: break; // FIXME: do we need to handle this? With what? + case LF_DIMARRAY: System.err.println("FIXME: don't know what to do with LF_DIMARRAY leaves yet"); break; + case LF_VFTPATH: break; + case LF_PRECOMP: break; + case LF_ENDPRECOMP: break; + case LF_OEM: break; + case LF_TYPESERVER: break; + + // Type records referenced from other type records + + case LF_SKIP: break; + case LF_ARGLIST: skipTypeRecord(); break; + case LF_DEFARG: System.err.println("FIXME: handle default arguments (dereference the type)"); break; + case LF_FIELDLIST: skipTypeRecord(); break; + case LF_DERIVED: break; + case LF_BITFIELD: { + Type underlyingType = getTypeByIndex(iter.getBitfieldFieldType()); + BasicBitType bit = new BasicBitType(underlyingType, + (iter.getBitfieldLength() & 0xFF), + (iter.getBitfieldPosition() & 0xFF)); + putType(bit); + break; + } + case LF_METHODLIST: break; + case LF_DIMCONU: + case LF_DIMCONLU: + case LF_DIMVARU: + case LF_DIMVARLU: break; + case LF_REFSYM: break; + + case LF_PAD0: case LF_PAD1: case LF_PAD2: case LF_PAD3: + case LF_PAD4: case LF_PAD5: case LF_PAD6: case LF_PAD7: + case LF_PAD8: case LF_PAD9: case LF_PAD10: case LF_PAD11: + case LF_PAD12: case LF_PAD13: case LF_PAD14: case LF_PAD15: break; + + default: { + System.err.println("Unexpected leaf index " + + iter.typeStringLeaf() + " at offset 0x" + + Integer.toHexString(iter.typeStringOffset())); + break; + } + } + + + if (!iter.typeStringDone()) { + iter.typeStringNext(); + } + } + } + + // Add all symbol directories to debug info + // (FIXME: must figure out how to handle module-by-module + // arrangement of at least the static symbols to have proper + // lookup -- should probably also take advantage of the PROCREF + // and UDT references to understand how to build the global + // database vs. the module-by-module one) + DebugVC50SubsectionDirectory dir = vc50.getSubsectionDirectory(); + int moduleNumber = 0; // Debugging + for (int i = 0; i < dir.getNumEntries(); i++) { + DebugVC50Subsection ss = dir.getSubsection(i); + int ssType = ss.getSubsectionType(); + boolean process = false; + + if ((ssType == SST_GLOBAL_SYM) || + (ssType == SST_GLOBAL_PUB) || + (ssType == SST_STATIC_SYM)) { + DebugVC50SSSymbolBase syms = (DebugVC50SSSymbolBase) ss; + symIter = syms.getSymbolIterator(); + process = true; + } + + if (ssType == SST_ALIGN_SYM) { + DebugVC50SSAlignSym syms = (DebugVC50SSAlignSym) ss; + symIter = syms.getSymbolIterator(); + process = true; + } + + if (process) { + for (; !symIter.done(); symIter.next()) { + switch (symIter.getType()) { + case S_COMPILE: break; + case S_SSEARCH: break; // FIXME: may need this later + case S_END: { + try { + // FIXME: workaround for warnings until we figure out + // what to do with THUNK32 symbols + if (endsToSkip == 0) { + blockStack.pop(); + } else { + --endsToSkip; + } + } catch (EmptyStackException e) { + System.err.println("WARNING: mismatched block begins/ends in debug information"); + } + break; + } + case S_SKIP: break; + case S_CVRESERVE: break; + case S_OBJNAME: break; // FIXME: may need this later + case S_ENDARG: break; + case S_COBOLUDT: break; + case S_MANYREG: break; // FIXME: may need to add support for this + case S_RETURN: break; // NOTE: would need this if adding support for calling functions + case S_ENTRYTHIS: break; // FIXME: may need to add support for this + case S_REGISTER: break; // FIXME: may need to add support for this + case S_CONSTANT: break; // FIXME: will need to add support for this + case S_UDT: break; // FIXME: need to see how these are used; are + // they redundant, or are they used to describe + // global variables as opposed to types? + case S_COBOLUDT2: break; + case S_MANYREG2: break; + case S_BPREL32: { + LocalSym sym = new BasicLocalSym(symIter.getBPRelName(), + getTypeByIndex(symIter.getBPRelType()), + symIter.getBPRelOffset()); + addLocalToCurBlock(sym); + break; + } + case S_LDATA32: + case S_GDATA32: { + // FIXME: must handle these separately from global data (have + // module scoping and only add these at the module level) + boolean isModuleLocal = (symIter.getType() == S_LDATA32); + + GlobalSym sym = new BasicGlobalSym(symIter.getLGDataName(), + getTypeByIndex(symIter.getLGDataType()), + newAddress(symIter.getLGDataOffset(), symIter.getLGDataSegment()), + isModuleLocal); + // FIXME: must handle module-local symbols differently + addGlobalSym(sym); + break; + } + case S_PUB32: break; // FIXME: figure out how these differ from + // above and how they are used + case S_LPROC32: + case S_GPROC32: { + BasicFunctionSym sym = new BasicFunctionSym(newLazyBlockSym(symIter.getLGProcParentOffset()), + symIter.getLGProcLength(), + newAddress(symIter.getLGProcOffset(), symIter.getLGProcSegment()), + symIter.getLGProcName(), + getTypeByIndex(symIter.getLGProcType()), + (symIter.getType() == S_LPROC32)); + + // FIXME: have to handle local procedures differently (have + // notion of modules and only add those procedures to the + // module they are defined in) + addBlock(sym); + break; + } + case S_THUNK32: { + // FIXME: see whether we need to handle these + skipEnd(); + break; + } + case S_BLOCK32: { + BasicBlockSym sym = new BasicBlockSym(newLazyBlockSym(symIter.getBlockParentOffset()), + symIter.getBlockLength(), + newAddress(symIter.getBlockOffset(), symIter.getBlockSegment()), + symIter.getBlockName()); + addBlock(sym); + break; + } + case S_WITH32: break; + case S_LABEL32: break; + case S_CEXMODEL32: break; + case S_VFTTABLE32: break; // FIXME: may need to handle this + // (most likely for run-time type determination) + case S_REGREL32: break; // FIXME: may need to add support for this + case S_LTHREAD32: break; + case S_GTHREAD32: break; // FIXME: may need to add support for these + case S_PROCREF: break; + case S_DATAREF: break; + case S_ALIGN: break; + default: + // These two unknown symbol types show up very frequently. + // Symbol type 0 appears to always be a no-op symbol of + // length 2 (i.e., length just covers the symbol type.) + // Symbol type 4115 appears to be a copyright notice for + // the Microsoft linker. + if ((symIter.getType() != 0) && (symIter.getType() != 4115)) { + System.err.println(" NOTE: Unexpected symbol of type " + + symIter.getType() + " at offset 0x" + + Integer.toHexString(symIter.getOffset())); + } + break; + } + } + } + } + + // Add line number information for all modules + for (int i = 0; i < dir.getNumEntries(); i++) { + DebugVC50Subsection ss = dir.getSubsection(i); + if (ss.getSubsectionType() == SST_SRC_MODULE) { + DebugVC50SSSrcModule srcMod = (DebugVC50SSSrcModule) ss; + for (int sf = 0; sf < srcMod.getNumSourceFiles(); sf++) { + DebugVC50SrcModFileDesc desc = srcMod.getSourceFileDesc(sf); + // Uniquify these to save space + String name = desc.getSourceFileName().intern(); + for (int cs = 0; cs < desc.getNumCodeSegments(); cs++) { + DebugVC50SrcModLineNumberMap map = desc.getLineNumberMap(cs); + SectionHeader seg = file.getHeader().getSectionHeader(map.getSegment()); + for (int lp = 0; lp < map.getNumSourceLinePairs(); lp++) { + Address startPC = base.addOffsetTo(seg.getVirtualAddress() + map.getCodeOffset(lp)); + // Fake address for endPC -- will be filled in by BasicLineNumberMapping + Address endPC = base.addOffsetTo(seg.getSize()); + db.addLineNumberInfo(new BasicLineNumberInfo(name, map.getLineNumber(lp), startPC, endPC)); + } + } + } + } + } + + // Finish assembly of database + db.resolve(new ResolveListener() { + public void resolveFailed(Type containingType, LazyType failedResolve, String detail) { + System.err.println("WARNING: failed to resolve type of index " + + ((Integer) failedResolve.getKey()).intValue() + + " in type " + containingType.getName() + " (class " + + containingType.getClass().getName() + ") while " + detail); + } + + public void resolveFailed(Type containingType, String staticFieldName) { + System.err.println("WARNING: failed to resolve address of static field \"" + + staticFieldName + "\" in type " + containingType.getName()); + } + + public void resolveFailed(Sym containingSymbol, LazyType failedResolve, String detail) { + System.err.println("WARNING: failed to resolve type of index " + + ((Integer) failedResolve.getKey()).intValue() + + " in symbol of type " + containingSymbol.getClass().getName() + + " while " + detail); + } + + public void resolveFailed(Sym containingSymbol, LazyBlockSym failedResolve, String detail) { + System.err.println("WARNING: failed to resolve block at offset 0x" + + Integer.toHexString(((Integer) failedResolve.getKey()).intValue()) + + " in symbol of type " + containingSymbol.getClass().getName() + + " while " + detail); + } + }); + + db.endConstruction(); + + return db; + } + + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private static DebugVC50 getDebugVC50(COFFFile file) { + COFFHeader header = file.getHeader(); + OptionalHeader opt = header.getOptionalHeader(); + if (opt == null) { + // Optional header not found + return null; + } + OptionalHeaderDataDirectories dd = opt.getDataDirectories(); + if (dd == null) { + // Optional header data directories not found + return null; + } + DebugDirectory debug = dd.getDebugDirectory(); + if (debug == null) { + // Debug directory not found + return null; + } + for (int i = 0; i < debug.getNumEntries(); i++) { + DebugDirectoryEntry entry = debug.getEntry(i); + if (entry.getType() == DebugTypes.IMAGE_DEBUG_TYPE_CODEVIEW) { + return entry.getDebugVC50(); + } + } + + // CodeView information not found in debug directory + return null; + } + + private DebugVC50SSSegMap getSegMap() { + return (DebugVC50SSSegMap) findSubsection(SST_SEG_MAP); + } + + private DebugVC50SSGlobalTypes getGlobalTypes() { + return (DebugVC50SSGlobalTypes) findSubsection(SST_GLOBAL_TYPES); + } + + private DebugVC50SSGlobalSym getGlobalSymbols() { + return (DebugVC50SSGlobalSym) findSubsection(SST_GLOBAL_SYM); + } + + private DebugVC50Subsection findSubsection(short ssType) { + DebugVC50SubsectionDirectory dir = vc50.getSubsectionDirectory(); + for (int i = 0; i < dir.getNumEntries(); i++) { + DebugVC50Subsection ss = dir.getSubsection(i); + if (ss.getSubsectionType() == ssType) { + return ss; + } + } + throw new DebuggerException("Unable to find subsection of type " + ssType); + } + + private void putType(Type t) { + db.addType(new Integer(iter.getTypeIndex()), t); + } + + private Address newAddress(int offset, short segment) { + int seg = segment & 0xFFFF; + // NOTE: it isn't clear how to use the segMap to map from logical + // to physical segments. It seems it would make more sense if the + // SegDescs contained a physical segment number in addition to the + // offset within the physical segment of the logical one. + + // Get the section header corresponding to this segment + SectionHeader section = file.getHeader().getSectionHeader(seg); + + // Result is relative to image base + return base.addOffsetTo(section.getVirtualAddress() + offset); + } + + private BasicType getTypeByIndex(int intIndex) { + Integer index = new Integer(intIndex); + + // Handle primitive types here. + if (intIndex <= 0x0FFF) { + BasicType type = (BasicType) primIndexToTypeMap.get(index); + if (type != null) { + return type; + } + // Construct appropriate new primitive type + int primMode = intIndex & RESERVED_MODE_MASK; + if (primMode == RESERVED_MODE_DIRECT) { + int primType = intIndex & RESERVED_TYPE_MASK; + switch (primType) { + case RESERVED_TYPE_SIGNED_INT: + case RESERVED_TYPE_UNSIGNED_INT: { + boolean unsigned = (primType == RESERVED_TYPE_UNSIGNED_INT); + int size = 0; + String name = null; + switch (intIndex & RESERVED_SIZE_MASK) { + case RESERVED_SIZE_INT_1_BYTE: size = 1; name = "char"; break; + case RESERVED_SIZE_INT_2_BYTE: size = 2; name = "short"; break; + case RESERVED_SIZE_INT_4_BYTE: size = 4; name = "int"; break; + case RESERVED_SIZE_INT_8_BYTE: size = 8; name = "__int64"; break; + default: throw new DebuggerException("Illegal size of integer type " + intIndex); + } + type = new BasicIntType(name, size, unsigned); + break; + } + case RESERVED_TYPE_BOOLEAN: { + int size = 0; + switch (intIndex & RESERVED_SIZE_MASK) { + case RESERVED_SIZE_INT_1_BYTE: size = 1; break; + case RESERVED_SIZE_INT_2_BYTE: size = 2; break; + case RESERVED_SIZE_INT_4_BYTE: size = 4; break; + case RESERVED_SIZE_INT_8_BYTE: size = 8; break; + default: throw new DebuggerException("Illegal size of boolean type " + intIndex); + } + type = new BasicIntType("bool", size, false); + break; + } + case RESERVED_TYPE_REAL: { + switch (intIndex & RESERVED_SIZE_MASK) { + case RESERVED_SIZE_REAL_32_BIT: + type = new BasicFloatType("float", 4); + break; + case RESERVED_SIZE_REAL_64_BIT: + type = new BasicDoubleType("double", 8); + break; + default: + throw new DebuggerException("Unsupported floating-point size in type " + intIndex); + } + break; + } + case RESERVED_TYPE_REALLY_INT: { + switch (intIndex & RESERVED_SIZE_MASK) { + case RESERVED_SIZE_REALLY_INT_CHAR: type = new BasicIntType("char", 1, false); break; + case RESERVED_SIZE_REALLY_INT_WCHAR: type = new BasicIntType("wchar", 2, false); break; + case RESERVED_SIZE_REALLY_INT_2_BYTE: type = new BasicIntType("short", 2, false); break; + case RESERVED_SIZE_REALLY_INT_2_BYTE_U: type = new BasicIntType("short", 2, true); break; + case RESERVED_SIZE_REALLY_INT_4_BYTE: type = new BasicIntType("int", 4, false); break; + case RESERVED_SIZE_REALLY_INT_4_BYTE_U: type = new BasicIntType("int", 4, true); break; + case RESERVED_SIZE_REALLY_INT_8_BYTE: type = new BasicIntType("__int64", 8, false); break; + case RESERVED_SIZE_REALLY_INT_8_BYTE_U: type = new BasicIntType("__int64", 8, true); break; + default: throw new DebuggerException("Illegal REALLY_INT size in type " + intIndex); + } + break; + } + case RESERVED_TYPE_SPECIAL: { + switch (intIndex & RESERVED_SIZE_MASK) { + case RESERVED_SIZE_SPECIAL_NO_TYPE: + case RESERVED_SIZE_SPECIAL_VOID: type = new BasicVoidType(); break; + default: throw new DebuggerException("Don't know how to handle reserved special type " + intIndex); + } + break; + } + + default: + throw new DebuggerException("Don't know how to handle reserved type " + intIndex); + } + } else { + // Fold all pointer types together since we only support + // flat-mode addressing anyway + Type targetType = getTypeByIndex(intIndex & (~RESERVED_MODE_MASK)); + + type = new BasicPointerType(POINTER_SIZE, targetType); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(type != null, "Got null Type for primitive type " + intIndex); + } + primIndexToTypeMap.put(index, type); + return type; + } + + // Not primitive type. Construct lazy reference to target type. + // (Is it worth canonicalizing these as well to save space?) + return new LazyType(index); + } + + private void addBlock(BlockSym block) { + db.addBlock(new Integer(symIter.getOffset()), block); + blockStack.push(block); + } + + private void skipEnd() { + ++endsToSkip; + } + + private BlockSym newLazyBlockSym(int offset) { + if (offset == 0) { + return null; + } + + return new LazyBlockSym(new Integer(offset)); + } + + private int memberAttributeToAccessControl(short memberAttribute) { + int acc = memberAttribute & MEMATTR_ACCESS_MASK; + switch (acc) { + case MEMATTR_ACCESS_NO_PROTECTION: return NO_PROTECTION; + case MEMATTR_ACCESS_PRIVATE: return PRIVATE; + case MEMATTR_ACCESS_PROTECTED: return PROTECTED; + case MEMATTR_ACCESS_PUBLIC: return PUBLIC; + default: throw new RuntimeException("Should not reach here"); + } + } + + private void addLocalToCurBlock(LocalSym local) { + ((BasicBlockSym) blockStack.peek()).addLocal(local); + } + + private void addGlobalSym(GlobalSym sym) { + db.addGlobalSym(sym); + } + + private void skipTypeRecord() { + while (!iter.typeStringDone()) { + iter.typeStringNext(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgCDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgCDebugger.java new file mode 100644 index 00000000000..7ea89b93c6d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgCDebugger.java @@ -0,0 +1,108 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.x86.*; +import sun.jvm.hotspot.debugger.cdbg.basic.amd64.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.utilities.AddressOps; + +class WindbgCDebugger implements CDebugger { + // FIXME: think about how to make this work in a remote debugging + // scenario; who should keep open DLLs? Need local copies of these + // DLLs on the debugging machine? + private WindbgDebugger dbg; + + WindbgCDebugger(WindbgDebugger dbg) { + this.dbg = dbg; + } + + public List getThreadList() throws DebuggerException { + return dbg.getThreadList(); + } + + public List/**/ getLoadObjectList() throws DebuggerException{ + return dbg.getLoadObjectList(); + } + + public LoadObject loadObjectContainingPC(Address pc) throws DebuggerException { + // FIXME: could keep sorted list of these to be able to do binary + // searches, for better scalability + if (pc == null) { + return null; + } + List objs = getLoadObjectList(); + for (Iterator iter = objs.iterator(); iter.hasNext(); ) { + LoadObject obj = (LoadObject) iter.next(); + if (AddressOps.lte(obj.getBase(), pc) && (pc.minus(obj.getBase()) < obj.getSize())) { + return obj; + } + } + return null; + } + + public CFrame topFrameForThread(ThreadProxy thread) throws DebuggerException { + if (dbg.getCPU().equals("x86")) { + X86ThreadContext context = (X86ThreadContext) thread.getContext(); + Address ebp = context.getRegisterAsAddress(X86ThreadContext.EBP); + if (ebp == null) return null; + Address pc = context.getRegisterAsAddress(X86ThreadContext.EIP); + if (pc == null) return null; + return new X86CFrame(this, ebp, pc); + } else if (dbg.getCPU().equals("amd64")) { + AMD64ThreadContext context = (AMD64ThreadContext) thread.getContext(); + Address rbp = context.getRegisterAsAddress(AMD64ThreadContext.RBP); + if (rbp == null) return null; + Address pc = context.getRegisterAsAddress(AMD64ThreadContext.RIP); + if (pc == null) return null; + return new AMD64CFrame(this, rbp, pc); + } else { + // unsupported CPU! + return null; + } + } + + public String getNameOfFile(String fileName) { + return new File(fileName).getName(); + } + + public ProcessControl getProcessControl() throws DebuggerException { + return null; + } + + // C++ name demangling + public boolean canDemangle() { + return false; + } + + public String demangle(String sym) { + throw new UnsupportedOperationException(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebugger.java new file mode 100644 index 00000000000..b368058fe13 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebugger.java @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg; + +import java.util.List; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; + +/** An extension of the JVMDebugger interface with a few additions to + support 32-bit vs. 64-bit debugging as well as features required + by the architecture-specific subpackages. */ + +public interface WindbgDebugger extends JVMDebugger { + public String addressValueToString(long address) throws DebuggerException; + public boolean readJBoolean(long address) throws DebuggerException; + public byte readJByte(long address) throws DebuggerException; + public char readJChar(long address) throws DebuggerException; + public double readJDouble(long address) throws DebuggerException; + public float readJFloat(long address) throws DebuggerException; + public int readJInt(long address) throws DebuggerException; + public long readJLong(long address) throws DebuggerException; + public short readJShort(long address) throws DebuggerException; + public long readCInteger(long address, long numBytes, boolean isUnsigned) + throws DebuggerException; + public WindbgAddress readAddress(long address) throws DebuggerException; + public WindbgOopHandle readOopHandle(long address) throws DebuggerException; + + // The returned array of register contents is guaranteed to be in + // the same order as in the DbxDebugger for Solaris/x86 or amd64; that is, + // the indices match those in debugger/x86/X86ThreadContext.java or + // debugger/amd64/AMD64ThreadContext.java. + public long[] getThreadIntegerRegisterSet(long threadId) throws DebuggerException; + public Address newAddress(long value) throws DebuggerException; + + public long getThreadIdFromSysId(long sysId) throws DebuggerException; + // Support for the CDebugger interface. Retrieves the thread list of + // the target process as a List of ThreadProxy objects. + public List/**/ getThreadList() throws DebuggerException; + + // Support for the CDebugger interface. Retrieves a List of the + // loadobjects in the target process. + public List/**/ getLoadObjectList() throws DebuggerException; + + // NOTE: this interface implicitly contains the following methods: + // From the Debugger interface via JVMDebugger + // public void attach(int processID) throws DebuggerException; + // public void attach(String executableName, String coreFileName) throws DebuggerException; + // public boolean detach(); + // public Address parseAddress(String addressString) throws NumberFormatException; + // public long getAddressValue(Address addr) throws DebuggerException; + // public String getOS(); + // public String getCPU(); + // From the SymbolLookup interface via Debugger and JVMDebugger + // public Address lookup(String objectName, String symbol); + // public OopHandle lookupOop(String objectName, String symbol); + // From the JVMDebugger interface + // public void configureJavaPrimitiveTypeSizes(long jbooleanSize, + // long jbyteSize, + // long jcharSize, + // long jdoubleSize, + // long jfloatSize, + // long jintSize, + // long jlongSize, + // long jshortSize); + // From the ThreadAccess interface via Debugger and JVMDebugger + // public ThreadProxy getThreadForIdentifierAddress(Address addr); + + public int getAddressSize(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java new file mode 100644 index 00000000000..09a5464169b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java @@ -0,0 +1,624 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg; + +import java.io.*; +import java.net.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.ia64.*; +import sun.jvm.hotspot.debugger.windbg.amd64.*; +import sun.jvm.hotspot.debugger.windbg.x86.*; +import sun.jvm.hotspot.debugger.windbg.ia64.*; +import sun.jvm.hotspot.debugger.win32.coff.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.cdbg.basic.BasicDebugEvent; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.utilities.memo.*; + +/**

An implementation of the JVMDebugger interface which talks to + windbg and symbol table management is done in Java.

+ +

NOTE that since we have the notion of fetching "Java + primitive types" from the remote process (which might have + different sizes than we expect) we have a bootstrapping + problem. We need to know the sizes of these types before we can + fetch them. The current implementation solves this problem by + requiring that it be configured with these type sizes before they + can be fetched. The readJ(Type) routines here will throw a + RuntimeException if they are called before the debugger is + configured with the Java primitive type sizes.

*/ + +public class WindbgDebuggerLocal extends DebuggerBase implements WindbgDebugger { + private PageCache cache; + private boolean attached; + private boolean isCore; + + // Symbol lookup support + // This is a map of library names to DLLs + private Map nameToDllMap; + + // C/C++ debugging support + private List/**/ loadObjects; + private CDebugger cdbg; + + // thread access + private Map threadIntegerRegisterSet; + private List threadList; + + // windbg native interface pointers + + private long ptrIDebugClient; + private long ptrIDebugControl; + private long ptrIDebugDataSpaces; + private long ptrIDebugOutputCallbacks; + private long ptrIDebugAdvanced; + private long ptrIDebugSymbols; + private long ptrIDebugSystemObjects; + + private WindbgThreadFactory threadFactory; + + //-------------------------------------------------------------------------------- + // Implementation of Debugger interface + // + + /**

machDesc may not be null.

+ +

useCache should be set to true if debugging is being done + locally, and to false if the debugger is being created for the + purpose of supporting remote debugging.

*/ + public WindbgDebuggerLocal(MachineDescription machDesc, + boolean useCache) throws DebuggerException { + this.machDesc = machDesc; + utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()) { + public void checkAlignment(long address, long alignment) { + // Need to override default checkAlignment because we need to + // relax alignment constraints on Windows/x86 + if ( (address % alignment != 0) + &&(alignment != 8 || address % 4 != 0)) { + throw new UnalignedAddressException( + "Trying to read at address: " + + addressValueToString(address) + + " with alignment: " + alignment, + address); + } + } + }; + + String cpu = PlatformInfo.getCPU(); + if (cpu.equals("x86")) { + threadFactory = new WindbgX86ThreadFactory(this); + } else if (cpu.equals("amd64")) { + threadFactory = new WindbgAMD64ThreadFactory(this); + } else if (cpu.equals("ia64")) { + threadFactory = new WindbgIA64ThreadFactory(this); + } + + if (useCache) { + // Cache portion of the remote process's address space. + // Fetching data over the socket connection to dbx is slow. + // Might be faster if we were using a binary protocol to talk to + // dbx, but would have to test. For now, this cache works best + // if it covers the entire heap of the remote process. FIXME: at + // least should make this tunable from the outside, i.e., via + // the UI. This is a cache of 4096 4K pages, or 16 MB. The page + // size must be adjusted to be the hardware's page size. + // (FIXME: should pick this up from the debugger.) + initCache(4096, 4096); + } + // FIXME: add instantiation of thread factory + + } + + /** From the Debugger interface via JVMDebugger */ + public boolean hasProcessList() throws DebuggerException { + return false; + } + + /** From the Debugger interface via JVMDebugger */ + public List getProcessList() throws DebuggerException { + return null; + } + + + /** From the Debugger interface via JVMDebugger */ + public synchronized void attach(int processID) throws DebuggerException { + attachInit(); + attach0(processID); + attached = true; + isCore = false; + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized void attach(String executableName, String coreFileName) throws DebuggerException { + attachInit(); + attach0(executableName, coreFileName); + attached = true; + isCore = true; + } + + public List getLoadObjectList() { + requireAttach(); + return loadObjects; + } + + /** From the Debugger interface via JVMDebugger */ + public synchronized boolean detach() { + if ( ! attached) + return false; + + // Close all open DLLs + if (nameToDllMap != null) { + for (Iterator iter = nameToDllMap.values().iterator(); iter.hasNext(); ) { + DLL dll = (DLL) iter.next(); + dll.close(); + } + nameToDllMap = null; + loadObjects = null; + } + + cdbg = null; + clearCache(); + + threadIntegerRegisterSet = null; + threadList = null; + try { + detach0(); + } finally { + attached = false; + resetNativePointers(); + } + return true; + } + + + /** From the Debugger interface via JVMDebugger */ + public Address parseAddress(String addressString) throws NumberFormatException { + return newAddress(utils.scanAddress(addressString)); + } + + /** From the Debugger interface via JVMDebugger */ + public String getOS() { + return PlatformInfo.getOS(); + } + + /** From the Debugger interface via JVMDebugger */ + public String getCPU() { + return PlatformInfo.getCPU(); + } + + public boolean hasConsole() throws DebuggerException { + return true; + } + + public synchronized String consoleExecuteCommand(String cmd) throws DebuggerException { + requireAttach(); + if (! attached) { + throw new DebuggerException("debugger not yet attached to a Dr. Watson dump!"); + } + + return consoleExecuteCommand0(cmd); + } + + public String getConsolePrompt() throws DebuggerException { + return "(windbg)"; + } + + public CDebugger getCDebugger() throws DebuggerException { + if (cdbg == null) { + // FIXME: CDebugger is not yet supported for IA64 because + // of native stack walking issues. + if (! getCPU().equals("ia64")) { + cdbg = new WindbgCDebugger(this); + } + } + return cdbg; + } + + /** From the SymbolLookup interface via Debugger and JVMDebugger */ + public synchronized Address lookup(String objectName, String symbol) { + requireAttach(); + return newAddress(lookupByName(objectName, symbol)); + } + + /** From the SymbolLookup interface via Debugger and JVMDebugger */ + public synchronized OopHandle lookupOop(String objectName, String symbol) { + Address addr = lookup(objectName, symbol); + if (addr == null) { + return null; + } + return addr.addOffsetToAsOopHandle(0); + } + + public synchronized ClosestSymbol lookup(long address) { + return lookupByAddress0(address); + } + + /** From the Debugger interface */ + public MachineDescription getMachineDescription() { + return machDesc; + } + + //-------------------------------------------------------------------------------- + // Implementation of ThreadAccess interface + // + + + /** From the ThreadAccess interface via Debugger and JVMDebugger */ + public ThreadProxy getThreadForIdentifierAddress(Address addr) { + return threadFactory.createThreadWrapper(addr); + } + + public ThreadProxy getThreadForThreadId(long handle) { + // with windbg we can't make out using handle + throw new DebuggerException("Unimplemented!"); + } + + public long getThreadIdFromSysId(long sysId) throws DebuggerException { + requireAttach(); + return getThreadIdFromSysId0(sysId); + } + + //---------------------------------------------------------------------- + // Overridden from DebuggerBase because we need to relax alignment + // constraints on x86 + + public long readJLong(long address) + throws UnmappedAddressException, UnalignedAddressException { + checkJavaConfigured(); + // FIXME: allow this to be configurable. Undesirable to add a + // dependency on the runtime package here, though, since this + // package should be strictly underneath it. + // utils.checkAlignment(address, jlongSize); + utils.checkAlignment(address, jintSize); + byte[] data = readBytes(address, jlongSize); + return utils.dataToJLong(data, jlongSize); + } + + //-------------------------------------------------------------------------------- + // Internal routines (for implementation of WindbgAddress). + // These must not be called until the MachineDescription has been set up. + // + + /** From the WindbgDebugger interface */ + public String addressValueToString(long address) { + return utils.addressValueToString(address); + } + + /** From the WindbgDebugger interface */ + public WindbgAddress readAddress(long address) + throws UnmappedAddressException, UnalignedAddressException { + return (WindbgAddress) newAddress(readAddressValue(address)); + } + + /** From the WindbgDebugger interface */ + public WindbgOopHandle readOopHandle(long address) + throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { + long value = readAddressValue(address); + return (value == 0 ? null : new WindbgOopHandle(this, value)); + } + + /** From the WindbgDebugger interface */ + public int getAddressSize() { + return (int) machDesc.getAddressSize(); + } + + //-------------------------------------------------------------------------------- + // Thread context access + // + + private synchronized void setThreadIntegerRegisterSet(long threadId, + long[] regs) { + threadIntegerRegisterSet.put(new Long(threadId), regs); + } + + private synchronized void addThread(long sysId) { + threadList.add(threadFactory.createThreadWrapper(sysId)); + } + + public synchronized long[] getThreadIntegerRegisterSet(long threadId) + throws DebuggerException { + requireAttach(); + return (long[]) threadIntegerRegisterSet.get(new Long(threadId)); + } + + public synchronized List getThreadList() throws DebuggerException { + requireAttach(); + return threadList; + } + + private String findFullPath(String file) { + File f = new File(file); + if (f.exists()) { + return file; + } else { + // remove path part, if any. + file = f.getName(); + StringTokenizer st = new StringTokenizer(imagePath, File.pathSeparator); + while (st.hasMoreTokens()) { + f = new File(st.nextToken(), file); + if (f.exists()) { + return f.getPath(); + } + } + } + return null; + } + + private synchronized void addLoadObject(String file, long size, long base) { + String path = findFullPath(file); + if (path != null) { + DLL dll = null; + if (useNativeLookup) { + dll = new DLL(this, path, size,newAddress(base)) { + public ClosestSymbol closestSymbolToPC(Address pcAsAddr) { + long pc = getAddressValue(pcAsAddr); + ClosestSymbol sym = lookupByAddress0(pc); + if (sym == null) { + return super.closestSymbolToPC(pcAsAddr); + } else { + return sym; + } + } + }; + } else { + dll = new DLL(this, path, size, newAddress(base)); + } + loadObjects.add(dll); + nameToDllMap.put(new File(file).getName(), dll); + } + } + + //-------------------------------------------------------------------------------- + // Address access + // + + /** From the Debugger interface */ + public long getAddressValue(Address addr) { + if (addr == null) return 0; + return ((WindbgAddress) addr).getValue(); + } + + /** From the WindbgDebugger interface */ + public Address newAddress(long value) { + if (value == 0) return null; + return new WindbgAddress(this, value); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + // attach/detach helpers + private void checkAttached() { + if (attached) { + String msg = (isCore)? "already attached to a Dr. Watson dump!" : + "already attached to a process!"; + throw new DebuggerException(msg); + } + } + + private void requireAttach() { + if (!attached) { + throw new RuntimeException("not attached to a process or Dr Watson dump"); + } + } + + private void attachInit() { + checkAttached(); + loadObjects = new ArrayList(); + nameToDllMap = new HashMap(); + threadIntegerRegisterSet = new HashMap(); + threadList = new ArrayList(); + } + + private void resetNativePointers() { + ptrIDebugClient = 0L; + ptrIDebugControl = 0L; + ptrIDebugDataSpaces = 0L; + ptrIDebugOutputCallbacks = 0L; + ptrIDebugAdvanced = 0L; + ptrIDebugSymbols = 0L; + ptrIDebugSystemObjects = 0L; + } + + synchronized long lookupByName(String objectName, String symbol) { + long res = 0L; + if (useNativeLookup) { + res = lookupByName0(objectName, symbol); + if (res != 0L) { + return res; + } // else fallthru... + } + + DLL dll = (DLL) nameToDllMap.get(objectName); + // The DLL can be null because we use this to search through known + // DLLs in HotSpotTypeDataBase (for example) + if (dll != null) { + WindbgAddress addr = (WindbgAddress) dll.lookupSymbol(symbol); + if (addr != null) { + return addr.getValue(); + } + } + return 0L; + } + + /** This reads bytes from the remote process. */ + public synchronized ReadResult readBytesFromProcess(long address, long numBytes) + throws UnmappedAddressException, DebuggerException { + requireAttach(); + byte[] res = readBytesFromProcess0(address, numBytes); + if(res != null) + return new ReadResult(res); + else + return new ReadResult(address); + } + + + private DLL findDLLByName(String fullPathName) { + for (Iterator iter = loadObjects.iterator(); iter.hasNext(); ) { + DLL dll = (DLL) iter.next(); + if (dll.getName().equals(fullPathName)) { + return dll; + } + } + return null; + } + + public void writeBytesToProcess(long address, long numBytes, byte[] data) + throws UnmappedAddressException, DebuggerException { + // FIXME + throw new DebuggerException("Unimplemented"); + } + + private static String DTFWHome; + private static String imagePath; + private static String symbolPath; + private static boolean useNativeLookup; + + static { + + /* + * sawindbg.dll depends on dbgeng.dll which + * itself depends on dbghelp.dll. dbgeng.dll and dbghelp.dll. + * On systems newer than Windows 2000, these two .dlls are + * in the standard system directory so we will find them there. + * On Windows 2000 and earlier, these files do not exist. + * The user must download Debugging Tools For Windows (DTFW) + * and install it in order to use SA. + * + * We have to make sure we use the two files from the same directory + * in case there are more than one copy on the system because + * one version of dbgeng.dll might not be compatible with a + * different version of dbghelp.dll. + * We first look for them in the directory pointed at by + * env. var. DEBUGGINGTOOLSFORWINDOWS, next in the default + * installation dir for DTFW, and lastly in the standard + * system directory. We expect that that we will find + * them in the standard system directory on all systems + * newer than Windows 2000. + */ + String dirName = null; + DTFWHome = System.getenv("DEBUGGINGTOOLSFORWINDOWS"); + + if (DTFWHome == null) { + // See if we have the files in the default location. + String sysRoot = System.getenv("SYSTEMROOT"); + DTFWHome = sysRoot + File.separator + + ".." + File.separator + "Program Files" + + File.separator + "Debugging Tools For Windows"; + } + + { + String dbghelp = DTFWHome + File.separator + "dbghelp.dll"; + String dbgeng = DTFWHome + File.separator + "dbgeng.dll"; + File fhelp = new File(dbghelp); + File feng = new File(dbgeng); + if (fhelp.exists() && feng.exists()) { + // found both, we are happy. + // NOTE: The order of loads is important! If we load dbgeng.dll + // first, then the dependency - dbghelp.dll - will be loaded + // from usual DLL search thereby defeating the purpose! + System.load(dbghelp); + System.load(dbgeng); + } else if (! fhelp.exists() && ! feng.exists()) { + // neither exist. We will ignore this dir and assume + // they are in the system dir. + DTFWHome = null; + } else { + // one exists but not the other + //System.err.println("Error: Both files dbghelp.dll and dbgeng.dll " + // "must exist in directory " + DTFWHome); + throw new UnsatisfiedLinkError("Both files dbghelp.dll and " + + "dbgeng.dll must exist in " + + "directory " + DTFWHome); + } + } + if (DTFWHome == null) { + // The files better be in the system dir. + String sysDir = System.getenv("SYSTEMROOT") + + File.separator + "system32"; + + File feng = new File(sysDir + File.separator + "dbgeng.dll"); + if (!feng.exists()) { + throw new UnsatisfiedLinkError("File dbgeng.dll does not exist in " + + sysDir + ". Please search microsoft.com " + + "for Debugging Tools For Windows, and " + + "either download it to the default " + + "location, or download it to a custom " + + "location and set environment variable " + + " DEBUGGINGTOOLSFORWINDOWS " + + "to the pathname of that location."); + } + } + + // Now, load sawindbg.dll + System.loadLibrary("sawindbg"); + // where do I find '.exe', '.dll' files? + imagePath = System.getProperty("sun.jvm.hotspot.debugger.windbg.imagePath"); + if (imagePath == null) { + imagePath = System.getenv("PATH"); + } + + // where do I find '.pdb', '.dbg' files? + symbolPath = System.getProperty("sun.jvm.hotspot.debugger.windbg.symbolPath"); + + // mostly, debug files would be find where .dll's, .exe's are found. + if (symbolPath == null) { + symbolPath = imagePath; + } + + // should we parse DLL symbol table in Java code or use + // Windbg's native lookup facility? By default, we use + // native lookup so that we can take advantage of '.pdb' + // files, if available. + useNativeLookup = true; + String str = System.getProperty("sun.jvm.hotspot.debugger.windbg.disableNativeLookup"); + if (str != null) { + useNativeLookup = false; + } + + initIDs(); + } + + // native methods + private static native void initIDs(); + private native void attach0(String executableName, String coreFileName); + private native void attach0(int processID); + private native void detach0(); + private native byte[] readBytesFromProcess0(long address, long numBytes) + throws UnmappedAddressException, DebuggerException; + private native long getThreadIdFromSysId0(long sysId); + private native String consoleExecuteCommand0(String cmd); + private native long lookupByName0(String objName, String symName); + private native ClosestSymbol lookupByAddress0(long address); + + // helper called lookupByAddress0 + private ClosestSymbol createClosestSymbol(String symbol, long diff) { + return new ClosestSymbol(symbol, diff); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgOopHandle.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgOopHandle.java new file mode 100644 index 00000000000..d4a1cf811d8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgOopHandle.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg; + +import sun.jvm.hotspot.debugger.*; + +class WindbgOopHandle extends WindbgAddress implements OopHandle { + WindbgOopHandle(WindbgDebugger debugger, long addr) { + super(debugger, addr); + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof WindbgOopHandle)) { + return false; + } + + return (addr == ((WindbgAddress) arg).addr); + } + + public Address addOffsetTo (long offset) throws UnsupportedOperationException { + throw new UnsupportedOperationException("addOffsetTo not applicable to OopHandles (interior object pointers not allowed)"); + } + + public Address andWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("andWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address orWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("orWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } + + public Address xorWithMask(long mask) throws UnsupportedOperationException { + throw new UnsupportedOperationException("xorWithMask not applicable to OopHandles (i.e., anything but C addresses)"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgThreadFactory.java new file mode 100644 index 00000000000..821b84345d7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgThreadFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg; + +import sun.jvm.hotspot.debugger.*; + +/** An interface used only internally by the WindbgDebugger to be able to + create platform-specific Thread objects */ + +public interface WindbgThreadFactory { + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr); + public ThreadProxy createThreadWrapper(long id); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/amd64/WindbgAMD64Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/amd64/WindbgAMD64Thread.java new file mode 100644 index 00000000000..87ff3dcc8be --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/amd64/WindbgAMD64Thread.java @@ -0,0 +1,103 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.windbg.*; + +class WindbgAMD64Thread implements ThreadProxy { + private WindbgDebugger debugger; + private long sysId; + private boolean gotID; + private long id; + + /** The address argument must be the address of the HANDLE of the + desired thread in the target process. */ + WindbgAMD64Thread(WindbgDebugger debugger, Address addr) { + this.debugger = debugger; + // FIXME: size of data fetched here should be configurable. + // However, making it so would produce a dependency on the "types" + // package from the debugger package, which is not desired. + + // another hack here is that we use sys thread id instead of handle. + // windbg can't get details based on handles it seems. + // I assume that osThread_win32 thread struct has _thread_id (which + // sys thread id) just after handle field. + + this.sysId = (int) addr.addOffsetTo(debugger.getAddressSize()).getCIntegerAt(0, 4, true); + gotID = false; + } + + WindbgAMD64Thread(WindbgDebugger debugger, long sysId) { + this.debugger = debugger; + this.sysId = sysId; + gotID = false; + } + + public ThreadContext getContext() throws IllegalThreadStateException { + long[] data = debugger.getThreadIntegerRegisterSet(getThreadID()); + WindbgAMD64ThreadContext context = new WindbgAMD64ThreadContext(debugger); + for (int i = 0; i < data.length; i++) { + context.setRegister(i, data[i]); + } + return context; + } + + public boolean canSetContext() throws DebuggerException { + return false; + } + + public void setContext(ThreadContext thrCtx) + throws IllegalThreadStateException, DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof WindbgAMD64Thread)) { + return false; + } + + return (((WindbgAMD64Thread) obj).getThreadID() == getThreadID()); + } + + public int hashCode() { + return (int) getThreadID(); + } + + public String toString() { + return Long.toString(getThreadID()); + } + + /** Retrieves the thread ID of this thread by examining the Thread + Information Block. */ + private long getThreadID() { + if (!gotID) { + id = debugger.getThreadIdFromSysId(sysId); + } + + return id; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/amd64/WindbgAMD64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/amd64/WindbgAMD64ThreadContext.java new file mode 100644 index 00000000000..6a6b8b38d86 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/amd64/WindbgAMD64ThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.windbg.*; + +class WindbgAMD64ThreadContext extends AMD64ThreadContext { + private WindbgDebugger debugger; + + public WindbgAMD64ThreadContext(WindbgDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/amd64/WindbgAMD64ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/amd64/WindbgAMD64ThreadFactory.java new file mode 100644 index 00000000000..a29df652141 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/amd64/WindbgAMD64ThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.windbg.*; + +public class WindbgAMD64ThreadFactory implements WindbgThreadFactory { + private WindbgDebugger debugger; + + public WindbgAMD64ThreadFactory(WindbgDebugger debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new WindbgAMD64Thread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new WindbgAMD64Thread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64Thread.java new file mode 100644 index 00000000000..9fd14948907 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64Thread.java @@ -0,0 +1,103 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg.ia64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.ia64.*; +import sun.jvm.hotspot.debugger.windbg.*; + +class WindbgIA64Thread implements ThreadProxy { + private WindbgDebugger debugger; + private long sysId; + private boolean gotID; + private long id; + + /** The address argument must be the address of the HANDLE of the + desired thread in the target process. */ + WindbgIA64Thread(WindbgDebugger debugger, Address addr) { + this.debugger = debugger; + // FIXME: size of data fetched here should be configurable. + // However, making it so would produce a dependency on the "types" + // package from the debugger package, which is not desired. + + // another hack here is that we use sys thread id instead of handle. + // windbg can't get details based on handles it seems. + // I assume that osThread_win32 thread struct has _thread_id (which + // sys thread id) just after handle field. + + this.sysId = (int) addr.addOffsetTo(debugger.getAddressSize()).getCIntegerAt(0, 4, true); + gotID = false; + } + + WindbgIA64Thread(WindbgDebugger debugger, long sysId) { + this.debugger = debugger; + this.sysId = sysId; + gotID = false; + } + + public ThreadContext getContext() throws IllegalThreadStateException { + long[] data = debugger.getThreadIntegerRegisterSet(getThreadID()); + WindbgIA64ThreadContext context = new WindbgIA64ThreadContext(debugger); + for (int i = 0; i < data.length; i++) { + context.setRegister(i, data[i]); + } + return context; + } + + public boolean canSetContext() throws DebuggerException { + return false; + } + + public void setContext(ThreadContext thrCtx) + throws IllegalThreadStateException, DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof WindbgIA64Thread)) { + return false; + } + + return (((WindbgIA64Thread) obj).getThreadID() == getThreadID()); + } + + public int hashCode() { + return (int) getThreadID(); + } + + public String toString() { + return Long.toString(getThreadID()); + } + + /** Retrieves the thread ID of this thread by examining the Thread + Information Block. */ + private long getThreadID() { + if (!gotID) { + id = debugger.getThreadIdFromSysId(sysId); + } + + return id; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadContext.java new file mode 100644 index 00000000000..0870cc7c614 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg.ia64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.ia64.*; +import sun.jvm.hotspot.debugger.windbg.*; + +class WindbgIA64ThreadContext extends IA64ThreadContext { + private WindbgDebugger debugger; + + public WindbgIA64ThreadContext(WindbgDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadFactory.java new file mode 100644 index 00000000000..d6f9836204b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg.ia64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.windbg.*; + +public class WindbgIA64ThreadFactory implements WindbgThreadFactory { + private WindbgDebugger debugger; + + public WindbgIA64ThreadFactory(WindbgDebugger debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new WindbgIA64Thread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new WindbgIA64Thread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/x86/WindbgX86Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/x86/WindbgX86Thread.java new file mode 100644 index 00000000000..8625b3465ba --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/x86/WindbgX86Thread.java @@ -0,0 +1,103 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.windbg.*; + +class WindbgX86Thread implements ThreadProxy { + private WindbgDebugger debugger; + private long sysId; + private boolean gotID; + private long id; + + /** The address argument must be the address of the HANDLE of the + desired thread in the target process. */ + WindbgX86Thread(WindbgDebugger debugger, Address addr) { + this.debugger = debugger; + // FIXME: size of data fetched here should be configurable. + // However, making it so would produce a dependency on the "types" + // package from the debugger package, which is not desired. + + // another hack here is that we use sys thread id instead of handle. + // windbg can't get details based on handles it seems. + // I assume that osThread_win32 thread struct has _thread_id (which + // sys thread id) just after handle field. + + this.sysId = (int) addr.addOffsetTo(debugger.getAddressSize()).getCIntegerAt(0, 4, true); + gotID = false; + } + + WindbgX86Thread(WindbgDebugger debugger, long sysId) { + this.debugger = debugger; + this.sysId = sysId; + gotID = false; + } + + public ThreadContext getContext() throws IllegalThreadStateException { + long[] data = debugger.getThreadIntegerRegisterSet(getThreadID()); + WindbgX86ThreadContext context = new WindbgX86ThreadContext(debugger); + for (int i = 0; i < data.length; i++) { + context.setRegister(i, data[i]); + } + return context; + } + + public boolean canSetContext() throws DebuggerException { + return false; + } + + public void setContext(ThreadContext thrCtx) + throws IllegalThreadStateException, DebuggerException { + throw new DebuggerException("Unimplemented"); + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof WindbgX86Thread)) { + return false; + } + + return (((WindbgX86Thread) obj).getThreadID() == getThreadID()); + } + + public int hashCode() { + return (int) getThreadID(); + } + + public String toString() { + return Long.toString(getThreadID()); + } + + /** Retrieves the thread ID of this thread by examining the Thread + Information Block. */ + private long getThreadID() { + if (!gotID) { + id = debugger.getThreadIdFromSysId(sysId); + } + + return id; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/x86/WindbgX86ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/x86/WindbgX86ThreadContext.java new file mode 100644 index 00000000000..abc188e38f3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/x86/WindbgX86ThreadContext.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.debugger.windbg.*; + +class WindbgX86ThreadContext extends X86ThreadContext { + private WindbgDebugger debugger; + + public WindbgX86ThreadContext(WindbgDebugger debugger) { + super(); + this.debugger = debugger; + } + + public void setRegisterAsAddress(int index, Address value) { + setRegister(index, debugger.getAddressValue(value)); + } + + public Address getRegisterAsAddress(int index) { + return debugger.newAddress(getRegister(index)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/x86/WindbgX86ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/x86/WindbgX86ThreadFactory.java new file mode 100644 index 00000000000..cb4c1f8899c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/windbg/x86/WindbgX86ThreadFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.windbg.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.windbg.*; + +public class WindbgX86ThreadFactory implements WindbgThreadFactory { + private WindbgDebugger debugger; + + public WindbgX86ThreadFactory(WindbgDebugger debugger) { + this.debugger = debugger; + } + + public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { + return new WindbgX86Thread(debugger, threadIdentifierAddr); + } + + public ThreadProxy createThreadWrapper(long id) { + return new WindbgX86Thread(debugger, id); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/x86/X86ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/x86/X86ThreadContext.java new file mode 100644 index 00000000000..1776e631c90 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/x86/X86ThreadContext.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.x86; + +import sun.jvm.hotspot.debugger.*; + +/** Specifies the thread context on x86 platforms; only a sub-portion + of the context is guaranteed to be present on all operating + systems. */ + +public abstract class X86ThreadContext implements ThreadContext { + // Taken from /usr/include/ia32/sys/reg.h on Solaris/x86 + + // NOTE: the indices for the various registers must be maintained as + // listed across various operating systems. However, only a small + // subset of the registers' values are guaranteed to be present (and + // must be present for the SA's stack walking to work): EAX, EBX, + // ECX, EDX, ESI, EDI, EBP, ESP, and EIP. + + public static final int GS = 0; + public static final int FS = 1; + public static final int ES = 2; + public static final int DS = 3; + public static final int EDI = 4; + public static final int ESI = 5; + public static final int EBP = 6; + public static final int ESP = 7; + public static final int EBX = 8; + public static final int EDX = 9; + public static final int ECX = 10; + public static final int EAX = 11; + public static final int TRAPNO = 12; + public static final int ERR = 13; + public static final int EIP = 14; + public static final int CS = 15; + public static final int EFL = 16; + public static final int UESP = 17; + public static final int SS = 18; + // Additional state (not in reg.h) for debug registers + public static final int DR0 = 19; + public static final int DR1 = 20; + public static final int DR2 = 21; + public static final int DR3 = 22; + public static final int DR6 = 23; + public static final int DR7 = 24; + + + public static final int PC = EIP; + public static final int FP = EBP; + public static final int SP = UESP; + public static final int PS = EFL; + public static final int R0 = EAX; + public static final int R1 = EDX; + + public static final int NPRGREG = 25; + + private static final String[] regNames = { + "GS", "FS", "ES", "DS", + "EDI", "ESI", "EBP", "ESP", + "EBX", "EDX", "ECX", "EAX", + "TRAPNO", "ERR", "EIP", "CS", + "EFLAGS", "UESP", "SS", + "DR0", "DR1", "DR2", "DR3", + "DR6", "DR7" + }; + + // Ought to be int on x86 but we're stuck + private long[] data; + + public X86ThreadContext() { + data = new long[NPRGREG]; + } + + public int getNumRegisters() { + return NPRGREG; + } + + public String getRegisterName(int index) { + return regNames[index]; + } + + public void setRegister(int index, long value) { + data[index] = value; + } + + public long getRegister(int index) { + return data[index]; + } + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public abstract void setRegisterAsAddress(int index, Address value); + + /** This can't be implemented in this class since we would have to + tie the implementation to, for example, the debugging system */ + public abstract Address getRegisterAsAddress(int index); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/PSOldGen.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/PSOldGen.java new file mode 100644 index 00000000000..5b95e6e2765 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/PSOldGen.java @@ -0,0 +1,79 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.gc_implementation.parallelScavenge; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_implementation.shared.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class PSOldGen extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("PSOldGen"); + objectSpaceField = type.getAddressField("_object_space"); + } + + public PSOldGen(Address addr) { + super(addr); + } + + // Fields + private static AddressField objectSpaceField; + + // Accessors + public MutableSpace objectSpace() { + return (MutableSpace) VMObjectFactory.newObject(MutableSpace.class, objectSpaceField.getValue(addr)); + } + + public long capacity() { + return objectSpace().capacity(); + } + + public long used() { + return objectSpace().used(); + } + + public boolean isIn(Address a) { + return objectSpace().contains(a); + } + + public void printOn(PrintStream tty) { + tty.print("PSOldGen [ "); + objectSpace().printOn(tty); + tty.print(" ] "); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/PSPermGen.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/PSPermGen.java new file mode 100644 index 00000000000..06efe3f782e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/PSPermGen.java @@ -0,0 +1,61 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.gc_implementation.parallelScavenge; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_implementation.shared.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class PSPermGen extends PSOldGen { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + // just checking type existence + Type type = db.lookupType("PSPermGen"); + } + + public PSPermGen(Address addr) { + super(addr); + } + + public void printOn(PrintStream tty) { + tty.print("PSPermGen [ "); + objectSpace().printOn(tty); + tty.print(" ] "); + } + + // FIXME: no other stuff yet +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/PSYoungGen.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/PSYoungGen.java new file mode 100644 index 00000000000..99eb0e13c45 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/PSYoungGen.java @@ -0,0 +1,103 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.gc_implementation.parallelScavenge; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_implementation.shared.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class PSYoungGen extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("PSYoungGen"); + edenSpaceField = type.getAddressField("_eden_space"); + fromSpaceField = type.getAddressField("_from_space"); + toSpaceField = type.getAddressField("_to_space"); + } + + public PSYoungGen(Address addr) { + super(addr); + } + + // Fields + private static AddressField edenSpaceField; + private static AddressField fromSpaceField; + private static AddressField toSpaceField; + + // Accessors + public MutableSpace edenSpace() { + return (MutableSpace) VMObjectFactory.newObject(MutableSpace.class, edenSpaceField.getValue(addr)); + } + + public MutableSpace fromSpace() { + return (MutableSpace) VMObjectFactory.newObject(MutableSpace.class, fromSpaceField.getValue(addr)); + } + + public MutableSpace toSpace() { + return (MutableSpace) VMObjectFactory.newObject(MutableSpace.class, toSpaceField.getValue(addr)); + } + + public long capacity() { + return edenSpace().capacity() + fromSpace().capacity(); + } + + public long used() { + return edenSpace().used() + fromSpace().used(); + } + + public boolean isIn(Address a) { + if (edenSpace().contains(a)) { + return true; + } + + if (fromSpace().contains(a)) { + return true; + } + return false; + } + + public void printOn(PrintStream tty) { + tty.print("PSYoungGen [ "); + tty.print("eden = "); + edenSpace().printOn(tty); + tty.print(", from = "); + fromSpace().printOn(tty); + tty.print(", to = "); + toSpace().printOn(tty); + tty.print(" ] "); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/ParallelScavengeHeap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/ParallelScavengeHeap.java new file mode 100644 index 00000000000..e6044b18eeb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/parallelScavenge/ParallelScavengeHeap.java @@ -0,0 +1,108 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.gc_implementation.parallelScavenge; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class ParallelScavengeHeap extends CollectedHeap { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("ParallelScavengeHeap"); + youngGenField = type.getAddressField("_young_gen"); + oldGenField = type.getAddressField("_old_gen"); + permGenField = type.getAddressField("_perm_gen"); + } + + public ParallelScavengeHeap(Address addr) { + super(addr); + } + + // Fields + private static AddressField youngGenField; + private static AddressField oldGenField; + private static AddressField permGenField; + + // Accessors + public PSYoungGen youngGen() { + return (PSYoungGen) VMObjectFactory.newObject(PSYoungGen.class, youngGenField.getValue()); + } + + public PSOldGen oldGen() { + return (PSOldGen) VMObjectFactory.newObject(PSOldGen.class, oldGenField.getValue()); + } + + public PSPermGen permGen() { + return (PSPermGen) VMObjectFactory.newObject(PSPermGen.class, permGenField.getValue()); + } + + public long capacity() { + return youngGen().capacity() + oldGen().capacity(); + } + + public long used() { + return youngGen().used() + oldGen().used(); + } + + public boolean isIn(Address a) { + if (youngGen().isIn(a)) { + return true; + } + + if (oldGen().isIn(a)) { + return true; + } + + if (permGen().isIn(a)) { + return true; + } + + return false; + } + + public CollectedHeapName kind() { + return CollectedHeapName.PARALLEL_SCAVENGE_HEAP; + } + + public void printOn(PrintStream tty) { + tty.print("ParallelScavengeHeap [ "); + youngGen().printOn(tty); + oldGen().printOn(tty); + permGen().printOn(tty); + tty.print(" ] "); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/shared/ImmutableSpace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/shared/ImmutableSpace.java new file mode 100644 index 00000000000..102056ce33b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/shared/ImmutableSpace.java @@ -0,0 +1,90 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.gc_implementation.shared; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public abstract class ImmutableSpace extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("ImmutableSpace"); + bottomField = type.getAddressField("_bottom"); + endField = type.getAddressField("_end"); + } + + public ImmutableSpace(Address addr) { + super(addr); + } + + // Fields + private static AddressField bottomField; + private static AddressField endField; + + // Accessors + public Address bottom() { return bottomField.getValue(addr); } + public Address end() { return endField.getValue(addr); } + + /** Returns a subregion of the space containing all the objects in + the space. */ + public MemRegion usedRegion() { + return new MemRegion(bottom(), end()); + } + + /** Support for iteration over heap -- not sure how this will + interact with GC in reflective system, but necessary for the + debugging mechanism */ + public OopHandle bottomAsOopHandle() { + return bottomField.getOopHandle(addr); + } + + /** returns all MemRegions where live objects are */ + public abstract List/**/ getLiveRegions(); + + /** Returned value is in bytes */ + public long capacity() { return end().minus(bottom()); } + + public abstract long used(); + + /** Testers */ + public boolean contains(Address p) { + return (bottom().lessThanOrEqual(p) && end().greaterThan(p)); + } + + public void print() { printOn(System.out); } + public abstract void printOn(PrintStream tty); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/shared/MutableSpace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/shared/MutableSpace.java new file mode 100644 index 00000000000..6f6ace768a6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/shared/MutableSpace.java @@ -0,0 +1,75 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.gc_implementation.shared; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class MutableSpace extends ImmutableSpace { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("MutableSpace"); + topField = type.getAddressField("_top"); + } + + public MutableSpace(Address addr) { + super(addr); + } + + // Fields + private static AddressField topField; + + // Accessors + public Address top() { return topField.getValue(addr); } + + /** In bytes */ + public long used() { + return top().minus(bottom()); + } + + /** returns all MemRegions where live objects are */ + public List/**/ getLiveRegions() { + List res = new ArrayList(); + res.add(new MemRegion(bottom(), top())); + return res; + } + + public void printOn(PrintStream tty) { + tty.print(" [" + bottom() + "," + + top() + "," + end() + "] "); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_interface/CollectedHeap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_interface/CollectedHeap.java new file mode 100644 index 00000000000..900cd8e91f4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_interface/CollectedHeap.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.gc_interface; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class CollectedHeap extends VMObject { + private static long reservedFieldOffset; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("CollectedHeap"); + + reservedFieldOffset = type.getField("_reserved").getOffset(); + } + + public CollectedHeap(Address addr) { + super(addr); + } + + /** Returns the lowest address of the heap. */ + public Address start() { + return reservedRegion().start(); + } + + public long capacity() { return 0; } + public long used() { return 0; } + + public MemRegion reservedRegion() { + return new MemRegion(addr.addOffsetTo(reservedFieldOffset)); + } + + public boolean isIn(Address a) { + return isInReserved(a); + } + + public boolean isInReserved(Address a) { + return reservedRegion().contains(a); + } + + public CollectedHeapName kind() { + return CollectedHeapName.ABSTRACT; + } + + public void print() { printOn(System.out); } + public void printOn(PrintStream tty) { + MemRegion mr = reservedRegion(); + tty.println("unknown subtype of CollectedHeap @ " + getAddress() + " (" + + mr.start() + "," + mr.end() + ")"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_interface/CollectedHeapName.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_interface/CollectedHeapName.java new file mode 100644 index 00000000000..5b97c627118 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_interface/CollectedHeapName.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.gc_interface; + +/** Mimics the enums in the VM under CollectedHeap::Name */ + +public class CollectedHeapName { + private String name; + + private CollectedHeapName(String name) { this.name = name; } + + public static final CollectedHeapName ABSTRACT = new CollectedHeapName("abstract"); + public static final CollectedHeapName SHARED_HEAP = new CollectedHeapName("SharedHeap"); + public static final CollectedHeapName GEN_COLLECTED_HEAP = new CollectedHeapName("GenCollectedHeap"); + public static final CollectedHeapName PARALLEL_SCAVENGE_HEAP = new CollectedHeapName("ParallelScavengeHeap"); + + public String toString() { + return name; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/Bytecode.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/Bytecode.java new file mode 100644 index 00000000000..4c17483a5da --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/Bytecode.java @@ -0,0 +1,99 @@ +/* + * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class Bytecode { + Method method; + int bci; + static final int jintSize = 4; + static final String spaces = " "; + static final String comma = ", "; + + Bytecode(Method method, int bci) { + this.method = method; + this.bci = bci; + } + + // Address computation + // NOTE: assumes that the start of the method's bytecodes is 4-byte aligned + int alignedOffset(int offset) { + return Bits.roundTo(bci + offset, jintSize) - bci; + } + + int javaSignedWordAt(int offset) { + return method.getBytecodeIntArg(bci + offset); + } + + short javaShortAt(int offset) { + return method.getBytecodeShortArg(bci + offset); + } + + byte javaByteAt(int offset) { + return method.getBytecodeByteArg(bci + offset); + } + + public Method method() { return method; } + public int bci() { return bci; } + + // hotspot byte code + public int code() { + return Bytecodes.codeAt(method(), bci()); + } + + // jvm byte code + public int javaCode() { + return Bytecodes.javaCode(code()); + } + + public String getBytecodeName() { + return Bytecodes.name(code()); + } + + public String getJavaBytecodeName() { + return Bytecodes.name(javaCode()); + } + + public int getLength() { + return Bytecodes.lengthAt(method(), bci()); + } + + public int getJavaLength() { + return Bytecodes.javaLengthAt(method(), bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(getJavaBytecodeName()); + if (code() != javaCode()) { + buf.append(spaces); + buf.append('['); + buf.append(getBytecodeName()); + buf.append(']'); + } + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeANewArray.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeANewArray.java new file mode 100644 index 00000000000..fbd39025294 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeANewArray.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeANewArray extends BytecodeWithKlass { + BytecodeANewArray(Method method, int bci) { + super(method, bci); + } + + public Klass getKlass() { + return super.getKlass(); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check anewarray"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._anewarray; + } + + public static BytecodeANewArray at(Method method, int bci) { + BytecodeANewArray b = new BytecodeANewArray(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at anewarray */ + public static BytecodeANewArray atCheck(Method method, int bci) { + BytecodeANewArray b = new BytecodeANewArray(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeANewArray at(BytecodeStream bcs) { + return new BytecodeANewArray(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeBipush.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeBipush.java new file mode 100644 index 00000000000..3bea3079d6c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeBipush.java @@ -0,0 +1,74 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeBipush extends Bytecode { + BytecodeBipush(Method method, int bci) { + super(method, bci); + } + + public byte getValue() { + return javaByteAt(1); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check bipush"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._bipush; + } + + public static BytecodeBipush at(Method method, int bci) { + BytecodeBipush b = new BytecodeBipush(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at bipush */ + public static BytecodeBipush atCheck(Method method, int bci) { + BytecodeBipush b = new BytecodeBipush(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeBipush at(BytecodeStream bcs) { + return new BytecodeBipush(bcs.method(), bcs.bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("bipush"); + buf.append(spaces); + buf.append(Byte.toString(getValue())); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeCheckCast.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeCheckCast.java new file mode 100644 index 00000000000..88dab31b7ff --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeCheckCast.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeCheckCast extends BytecodeWithKlass { + BytecodeCheckCast(Method method, int bci) { + super(method, bci); + } + + public InstanceKlass getCheckCastKlass() { + return (InstanceKlass) getKlass(); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check checkcast"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._checkcast; + } + + public static BytecodeCheckCast at(Method method, int bci) { + BytecodeCheckCast b = new BytecodeCheckCast(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at checkcast */ + public static BytecodeCheckCast atCheck(Method method, int bci) { + BytecodeCheckCast b = new BytecodeCheckCast(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeCheckCast at(BytecodeStream bcs) { + return new BytecodeCheckCast(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeDisassembler.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeDisassembler.java new file mode 100644 index 00000000000..7ba662b1262 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeDisassembler.java @@ -0,0 +1,169 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import java.util.*; +import java.lang.reflect.Constructor; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeDisassembler { + private Method method; + + private static Map bytecode2Class = new HashMap(); // Map + + private static void addBytecodeClass(int bytecode, Class clazz) { + bytecode2Class.put(new Integer(bytecode), clazz); + } + + private static Class getBytecodeClass(int bytecode) { + return (Class) bytecode2Class.get(new Integer(bytecode)); + } + + static { + addBytecodeClass(Bytecodes._anewarray, BytecodeANewArray.class); + addBytecodeClass(Bytecodes._bipush, BytecodeBipush.class); + addBytecodeClass(Bytecodes._checkcast, BytecodeCheckCast.class); + addBytecodeClass(Bytecodes._getfield, BytecodeGetField.class); + addBytecodeClass(Bytecodes._getstatic, BytecodeGetStatic.class); + addBytecodeClass(Bytecodes._goto, BytecodeGoto.class); + addBytecodeClass(Bytecodes._goto_w, BytecodeGotoW.class); + addBytecodeClass(Bytecodes._ifeq, BytecodeIf.class); + addBytecodeClass(Bytecodes._ifne, BytecodeIf.class); + addBytecodeClass(Bytecodes._iflt, BytecodeIf.class); + addBytecodeClass(Bytecodes._ifge, BytecodeIf.class); + addBytecodeClass(Bytecodes._ifgt, BytecodeIf.class); + addBytecodeClass(Bytecodes._ifle, BytecodeIf.class); + addBytecodeClass(Bytecodes._if_icmpeq, BytecodeIf.class); + addBytecodeClass(Bytecodes._if_icmpne, BytecodeIf.class); + addBytecodeClass(Bytecodes._if_icmplt, BytecodeIf.class); + addBytecodeClass(Bytecodes._if_icmpge, BytecodeIf.class); + addBytecodeClass(Bytecodes._if_icmpgt, BytecodeIf.class); + addBytecodeClass(Bytecodes._if_icmple, BytecodeIf.class); + addBytecodeClass(Bytecodes._if_acmpeq, BytecodeIf.class); + addBytecodeClass(Bytecodes._if_acmpne, BytecodeIf.class); + addBytecodeClass(Bytecodes._ifnull, BytecodeIf.class); + addBytecodeClass(Bytecodes._ifnonnull, BytecodeIf.class); + addBytecodeClass(Bytecodes._iinc, BytecodeIinc.class); + addBytecodeClass(Bytecodes._instanceof, BytecodeInstanceOf.class); + addBytecodeClass(Bytecodes._invokevirtual, BytecodeInvoke.class); + addBytecodeClass(Bytecodes._invokestatic, BytecodeInvoke.class); + addBytecodeClass(Bytecodes._invokespecial, BytecodeInvoke.class); + addBytecodeClass(Bytecodes._invokeinterface, BytecodeInvoke.class); + addBytecodeClass(Bytecodes._jsr, BytecodeJsr.class); + addBytecodeClass(Bytecodes._jsr_w, BytecodeJsrW.class); + addBytecodeClass(Bytecodes._iload, BytecodeLoad.class); + addBytecodeClass(Bytecodes._lload, BytecodeLoad.class); + addBytecodeClass(Bytecodes._fload, BytecodeLoad.class); + addBytecodeClass(Bytecodes._dload, BytecodeLoad.class); + addBytecodeClass(Bytecodes._aload, BytecodeLoad.class); + addBytecodeClass(Bytecodes._ldc, BytecodeLoadConstant.class); + addBytecodeClass(Bytecodes._ldc_w, BytecodeLoadConstant.class); + addBytecodeClass(Bytecodes._ldc2_w, BytecodeLoadConstant.class); + addBytecodeClass(Bytecodes._lookupswitch, BytecodeLookupswitch.class); + addBytecodeClass(Bytecodes._multianewarray, BytecodeMultiANewArray.class); + addBytecodeClass(Bytecodes._new, BytecodeNew.class); + addBytecodeClass(Bytecodes._newarray, BytecodeNewArray.class); + addBytecodeClass(Bytecodes._putfield, BytecodePutField.class); + addBytecodeClass(Bytecodes._putstatic, BytecodePutStatic.class); + addBytecodeClass(Bytecodes._ret, BytecodeRet.class); + addBytecodeClass(Bytecodes._sipush, BytecodeSipush.class); + addBytecodeClass(Bytecodes._istore, BytecodeStore.class); + addBytecodeClass(Bytecodes._lstore, BytecodeStore.class); + addBytecodeClass(Bytecodes._fstore, BytecodeStore.class); + addBytecodeClass(Bytecodes._dstore, BytecodeStore.class); + addBytecodeClass(Bytecodes._astore, BytecodeStore.class); + addBytecodeClass(Bytecodes._tableswitch, BytecodeTableswitch.class); + + // only special fast_xxx cases. others are handled differently. + addBytecodeClass(Bytecodes._fast_iaccess_0, BytecodeFastAAccess0.class); + addBytecodeClass(Bytecodes._fast_aaccess_0, BytecodeFastIAccess0.class); + } + + public BytecodeDisassembler(Method method) { + this.method = method; + } + + public Method getMethod() { + return method; + } + + public void decode(BytecodeVisitor visitor) { + visitor.prologue(method); + + BytecodeStream stream = new BytecodeStream(method); + int javacode = Bytecodes._illegal; + while ( (javacode = stream.next()) != Bytecodes._illegal) { + // look for special Bytecode class + int bci = stream.bci(); + int hotspotcode = method.getBytecodeOrBPAt(bci); + Class clazz = getBytecodeClass(javacode); + if (clazz == null) { + // check for fast_(i|a)_access_0 + clazz = getBytecodeClass(hotspotcode); + if (clazz == null) { + // use generic bytecode class + clazz = Bytecode.class; + } + } + + // All bytecode classes must have a constructor with signature + // (Lsun/jvm/hotspot/oops/Method;I)V + + Constructor cstr = null; + try { + cstr = clazz.getDeclaredConstructor(new Class[] { Method.class, Integer.TYPE }); + } catch(NoSuchMethodException nomethod) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "Bytecode class without proper constructor!"); + } + } + + Object bytecodeObj = null; + try { + bytecodeObj = cstr.newInstance(new Object[] { method, new Integer(bci) }); + } catch (Exception exp) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "Bytecode instance of class " + + clazz.getName() + " can not be created!"); + } + } + + if (stream.isWide()) { + visitor.visit(new Bytecode(method, bci - 1)); + } + + try { + visitor.visit((Bytecode) bytecodeObj); + } catch(ClassCastException castfail) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, clazz.getName() + " is not derived from Bytecode!"); + } + } + } + + visitor.epilogue(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeFastAAccess0.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeFastAAccess0.java new file mode 100644 index 00000000000..11f3cd9baf2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeFastAAccess0.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeFastAAccess0 extends BytecodeGetPut { + BytecodeFastAAccess0(Method method, int bci) { + super(method, bci); + } + + public int index() { + return (int) (0xFF & javaShortAt(2)); + } + + public boolean isStatic() { + return false; + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check fast_aaccess_0"); + } + } + + public boolean isValid() { + return code() == Bytecodes._fast_aaccess_0; + } + + public static BytecodeFastAAccess0 at(Method method, int bci) { + BytecodeFastAAccess0 b = new BytecodeFastAAccess0(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at fast_aaccess_0 */ + public static BytecodeFastAAccess0 atCheck(Method method, int bci) { + BytecodeFastAAccess0 b = new BytecodeFastAAccess0(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeFastAAccess0 at(BytecodeStream bcs) { + return new BytecodeFastAAccess0(bcs.method(), bcs.bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("aload_0"); + buf.append(spaces); + buf.append(super.toString()); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeFastIAccess0.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeFastIAccess0.java new file mode 100644 index 00000000000..824ba6cc817 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeFastIAccess0.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeFastIAccess0 extends BytecodeGetPut { + BytecodeFastIAccess0(Method method, int bci) { + super(method, bci); + } + + public int index() { + return (int) (0xFF & javaShortAt(2)); + } + + public boolean isStatic() { + return false; + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check fast_iaccess_0"); + } + } + + public boolean isValid() { + return code() == Bytecodes._fast_iaccess_0; + } + + public static BytecodeFastIAccess0 at(Method method, int bci) { + BytecodeFastIAccess0 b = new BytecodeFastIAccess0(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at fast_iaccess_0 */ + public static BytecodeFastIAccess0 atCheck(Method method, int bci) { + BytecodeFastIAccess0 b = new BytecodeFastIAccess0(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeFastIAccess0 at(BytecodeStream bcs) { + return new BytecodeFastIAccess0(bcs.method(), bcs.bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("aload_0"); + buf.append(spaces); + buf.append(super.toString()); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGetField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGetField.java new file mode 100644 index 00000000000..618007d83db --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGetField.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeGetField extends BytecodeGetPut { + BytecodeGetField(Method method, int bci) { + super(method, bci); + } + + public boolean isStatic() { + return false; + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check getfield"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._getfield; + } + + public static BytecodeGetField at(Method method, int bci) { + BytecodeGetField b = new BytecodeGetField(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at getfield */ + public static BytecodeGetField atCheck(Method method, int bci) { + BytecodeGetField b = new BytecodeGetField(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeGetField at(BytecodeStream bcs) { + return new BytecodeGetField(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGetPut.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGetPut.java new file mode 100644 index 00000000000..a5ffd13bb59 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGetPut.java @@ -0,0 +1,76 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +// getfield, getstatic, putfield or putstatic + +public abstract class BytecodeGetPut extends BytecodeWithCPIndex { + BytecodeGetPut(Method method, int bci) { + super(method, bci); + } + + // returns the name of the accessed field + public Symbol name() { + ConstantPool cp = method().getConstants(); + return cp.getNameRefAt(index()); + } + + // returns the signature of the accessed field + public Symbol signature() { + ConstantPool cp = method().getConstants(); + return cp.getSignatureRefAt(index()); + } + + public Field getField() { + return method().getConstants().getFieldRefAt(index()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getJavaBytecodeName()); + buf.append(spaces); + buf.append('#'); + buf.append(Integer.toString(indexForFieldOrMethod())); + buf.append(" [Field "); + StringBuffer sigBuf = new StringBuffer(); + new SignatureConverter(signature(), sigBuf).dispatchField(); + buf.append(sigBuf.toString().replace('/', '.')); + buf.append(spaces); + buf.append(name().asString()); + buf.append("]"); + if (code() != javaCode()) { + buf.append(spaces); + buf.append('['); + buf.append(getBytecodeName()); + buf.append(']'); + } + return buf.toString(); + } + + public abstract boolean isStatic(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGetStatic.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGetStatic.java new file mode 100644 index 00000000000..98020a47f43 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGetStatic.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeGetStatic extends BytecodeGetPut { + BytecodeGetStatic(Method method, int bci) { + super(method, bci); + } + + public boolean isStatic() { + return true; + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check getstatic"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._getstatic; + } + + public static BytecodeGetStatic at(Method method, int bci) { + BytecodeGetStatic b = new BytecodeGetStatic(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at getstatic */ + public static BytecodeGetStatic atCheck(Method method, int bci) { + BytecodeGetStatic b = new BytecodeGetStatic(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeGetStatic at(BytecodeStream bcs) { + return new BytecodeGetStatic(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGoto.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGoto.java new file mode 100644 index 00000000000..b78ecda5295 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGoto.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeGoto extends BytecodeJmp { + BytecodeGoto(Method method, int bci) { + super(method, bci); + } + + public int getTargetBCI() { + return bci() + javaShortAt(1); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check goto"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._goto; + } + + public static BytecodeGoto at(Method method, int bci) { + BytecodeGoto b = new BytecodeGoto(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at goto */ + public static BytecodeGoto atCheck(Method method, int bci) { + BytecodeGoto b = new BytecodeGoto(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeGoto at(BytecodeStream bcs) { + return new BytecodeGoto(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGotoW.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGotoW.java new file mode 100644 index 00000000000..ad25bac0c51 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeGotoW.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeGotoW extends BytecodeJmp { + BytecodeGotoW(Method method, int bci) { + super(method, bci); + } + + public int getTargetBCI() { + return bci() + javaSignedWordAt(1); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check goto_w"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._goto_w; + } + + public static BytecodeGotoW at(Method method, int bci) { + BytecodeGotoW b = new BytecodeGotoW(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at goto_w */ + public static BytecodeGotoW atCheck(Method method, int bci) { + BytecodeGotoW b = new BytecodeGotoW(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeGotoW at(BytecodeStream bcs) { + return new BytecodeGotoW(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeIf.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeIf.java new file mode 100644 index 00000000000..eda108061d5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeIf.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeIf extends BytecodeJmp { + BytecodeIf(Method method, int bci) { + super(method, bci); + } + + public int getTargetBCI() { + return bci() + javaShortAt(1); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check if"); + } + } + + public boolean isValid() { + int jcode = javaCode(); + return (jcode >= Bytecodes._ifeq && jcode <= Bytecodes._if_acmpne) || + jcode == Bytecodes._ifnull || jcode == Bytecodes._ifnonnull; + } + + public static BytecodeIf at(Method method, int bci) { + BytecodeIf b = new BytecodeIf(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at some ifxxx bytecode */ + public static BytecodeIf atCheck(Method method, int bci) { + BytecodeIf b = new BytecodeIf(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeIf at(BytecodeStream bcs) { + return new BytecodeIf(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeIinc.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeIinc.java new file mode 100644 index 00000000000..7f5e5fcf482 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeIinc.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeIinc extends BytecodeWideable { + BytecodeIinc(Method method, int bci) { + super(method, bci); + } + + public int getIncrement() { + // increment is signed + return (isWide()) ? (int) javaShortAt(3) : (int) javaByteAt(2); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check iinc"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._iinc; + } + + public static BytecodeIinc at(Method method, int bci) { + BytecodeIinc b = new BytecodeIinc(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at iinc */ + public static BytecodeIinc atCheck(Method method, int bci) { + BytecodeIinc b = new BytecodeIinc(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeIinc at(BytecodeStream bcs) { + return new BytecodeIinc(bcs.method(), bcs.bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("iinc"); + buf.append(spaces); + buf.append('#'); + buf.append(Integer.toString(getLocalVarIndex())); + buf.append(" by "); + buf.append(Integer.toString(getIncrement())); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeInstanceOf.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeInstanceOf.java new file mode 100644 index 00000000000..4fe6e837741 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeInstanceOf.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeInstanceOf extends BytecodeWithKlass { + BytecodeInstanceOf(Method method, int bci) { + super(method, bci); + } + + public InstanceKlass getInstanceOfKlass() { + return (InstanceKlass) getKlass(); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check instanceof"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._instanceof; + } + + public static BytecodeInstanceOf at(Method method, int bci) { + BytecodeInstanceOf b = new BytecodeInstanceOf(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at instanceof */ + public static BytecodeInstanceOf atCheck(Method method, int bci) { + BytecodeInstanceOf b = new BytecodeInstanceOf(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeInstanceOf at(BytecodeStream bcs) { + return new BytecodeInstanceOf(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeInvoke.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeInvoke.java new file mode 100644 index 00000000000..af93181365d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeInvoke.java @@ -0,0 +1,127 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeInvoke extends BytecodeWithCPIndex { + BytecodeInvoke(Method method, int bci) { + super(method, bci); + } + + public static BytecodeInvoke at(Method method, int bci) { + BytecodeInvoke b = new BytecodeInvoke(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at an invoke */ + public static BytecodeInvoke atCheck(Method method, int bci) { + BytecodeInvoke b = new BytecodeInvoke(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeInvoke at(BytecodeStream bcs) { + return new BytecodeInvoke(bcs.method(), bcs.bci()); + } + + // returns the name of the invoked method + public Symbol name() { + ConstantPool cp = method().getConstants(); + return cp.getNameRefAt(index()); + } + + // returns the signature of the invoked method + public Symbol signature() { + ConstantPool cp = method().getConstants(); + return cp.getSignatureRefAt(index()); + } + + public Method getInvokedMethod() { + return method().getConstants().getMethodRefAt(index()); + } + + // returns the result type (see BasicType.java) of the invoke + public int resultType() { + ResultTypeFinder rts = new ResultTypeFinder(signature()); + rts.iterate(); + return rts.type(); + } + + public int adjustedInvokeCode() { + return javaCode(); + } + + // "specified" method (from constant pool) + // FIXME: elided for now + // public Method staticTarget(); + + // Testers + public boolean isInvokeinterface() { return adjustedInvokeCode() == Bytecodes._invokeinterface; } + public boolean isInvokevirtual() { return adjustedInvokeCode() == Bytecodes._invokevirtual; } + public boolean isInvokestatic() { return adjustedInvokeCode() == Bytecodes._invokestatic; } + public boolean isInvokespecial() { return adjustedInvokeCode() == Bytecodes._invokespecial; } + + public boolean isValid() { return isInvokeinterface() || + isInvokevirtual() || + isInvokestatic() || + isInvokespecial(); } + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check invoke"); + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getJavaBytecodeName()); + buf.append(spaces); + buf.append('#'); + buf.append(Integer.toString(indexForFieldOrMethod())); + buf.append(" [Method "); + StringBuffer sigBuf = new StringBuffer(); + new SignatureConverter(signature(), sigBuf).iterateReturntype(); + buf.append(sigBuf.toString().replace('/', '.')); + buf.append(spaces); + buf.append(name().asString()); + buf.append('('); + sigBuf = new StringBuffer(); + new SignatureConverter(signature(), sigBuf).iterateParameters(); + buf.append(sigBuf.toString().replace('/', '.')); + buf.append(')'); + buf.append(']'); + if (code() != javaCode()) { + buf.append(spaces); + buf.append('['); + buf.append(getBytecodeName()); + buf.append(']'); + } + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeJmp.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeJmp.java new file mode 100644 index 00000000000..a0e7219fa56 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeJmp.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public abstract class BytecodeJmp extends Bytecode { + BytecodeJmp(Method method, int bci) { + super(method, bci); + } + + public abstract int getTargetBCI(); + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getJavaBytecodeName()); + buf.append(spaces); + buf.append(Integer.toString(getTargetBCI())); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeJsr.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeJsr.java new file mode 100644 index 00000000000..1670d9b3b04 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeJsr.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeJsr extends BytecodeJmp { + BytecodeJsr(Method method, int bci) { + super(method, bci); + } + + public int getTargetBCI() { + return bci() + javaShortAt(1); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check jsr"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._jsr; + } + + public static BytecodeJsr at(Method method, int bci) { + BytecodeJsr b = new BytecodeJsr(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at jsr */ + public static BytecodeJsr atCheck(Method method, int bci) { + BytecodeJsr b = new BytecodeJsr(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeJsr at(BytecodeStream bcs) { + return new BytecodeJsr(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeJsrW.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeJsrW.java new file mode 100644 index 00000000000..adc39c74986 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeJsrW.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeJsrW extends BytecodeJmp { + BytecodeJsrW(Method method, int bci) { + super(method, bci); + } + + public int getTargetBCI() { + return bci() + javaSignedWordAt(1); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check jsr_w"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._jsr_w; + } + + public static BytecodeJsrW at(Method method, int bci) { + BytecodeJsrW b = new BytecodeJsrW(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at jsr_w */ + public static BytecodeJsrW atCheck(Method method, int bci) { + BytecodeJsrW b = new BytecodeJsrW(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeJsrW at(BytecodeStream bcs) { + return new BytecodeJsrW(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoad.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoad.java new file mode 100644 index 00000000000..dfbb1467585 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoad.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeLoad extends BytecodeLoadStore { + BytecodeLoad(Method method, int bci) { + super(method, bci); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check load"); + } + } + + public boolean isValid() { + int jcode = javaCode(); + switch (jcode) { + case Bytecodes._iload: + case Bytecodes._lload: + case Bytecodes._fload: + case Bytecodes._dload: + case Bytecodes._aload: + return true; + default: + return false; + } + } + + public static BytecodeLoad at(Method method, int bci) { + BytecodeLoad b = new BytecodeLoad(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at load */ + public static BytecodeLoad atCheck(Method method, int bci) { + BytecodeLoad b = new BytecodeLoad(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeLoad at(BytecodeStream bcs) { + return new BytecodeLoad(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadConstant.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadConstant.java new file mode 100644 index 00000000000..133a7038e32 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadConstant.java @@ -0,0 +1,176 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeLoadConstant extends BytecodeWithCPIndex { + BytecodeLoadConstant(Method method, int bci) { + super(method, bci); + } + + public int index() { + return javaCode() == Bytecodes._ldc ? + (int) (0xFF & javaByteAt(1)) + : (int) (0xFFFF & javaShortAt(1)); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check load constant"); + } + } + + public boolean isValid() { + int jcode = javaCode(); + boolean codeOk = jcode == Bytecodes._ldc || jcode == Bytecodes._ldc_w || + jcode == Bytecodes._ldc2_w; + if (! codeOk) return false; + + ConstantTag ctag = method().getConstants().getTagAt(index()); + if (jcode == Bytecodes._ldc2_w) { + // has to be double or long + return (ctag.isDouble() || ctag.isLong()) ? true: false; + } else { + // has to be int or float or String or Klass + return (ctag.isUnresolvedString() || ctag.isString() + || ctag.isUnresolvedKlass() || ctag.isKlass() + || ctag.isInt() || ctag.isFloat())? true: false; + } + } + + public boolean isKlassConstant() { + int jcode = javaCode(); + if (jcode == Bytecodes._ldc2_w) { + return false; + } + + ConstantTag ctag = method().getConstants().getTagAt(index()); + return ctag.isKlass() || ctag.isUnresolvedKlass(); + } + + // return Symbol (if unresolved) or Klass (if resolved) + public Oop getKlass() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isKlassConstant(), "not a klass literal"); + } + // tag change from 'unresolved' to 'klass' does not happen atomically. + // We just look at the object at the corresponding index and + // decide based on the oop type. + ConstantPool cpool = method().getConstants(); + int cpIndex = index(); + Oop oop = cpool.getObjAt(cpIndex); + if (oop.isKlass()) { + return (Klass) oop; + } else if (oop.isSymbol()) { + return (Symbol) oop; + } else { + throw new RuntimeException("should not reach here"); + } + } + + public static BytecodeLoadConstant at(Method method, int bci) { + BytecodeLoadConstant b = new BytecodeLoadConstant(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at ldc or ldc_w or ldc2_w */ + public static BytecodeLoadConstant atCheck(Method method, int bci) { + BytecodeLoadConstant b = new BytecodeLoadConstant(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeLoadConstant at(BytecodeStream bcs) { + return new BytecodeLoadConstant(bcs.method(), bcs.bci()); + } + + public String getConstantValue() { + ConstantPool cpool = method().getConstants(); + int cpIndex = index(); + ConstantTag ctag = cpool.getTagAt(cpIndex); + if (ctag.isInt()) { + return ""; + } else if (ctag.isLong()) { + return ""; + } else if (ctag.isFloat()) { + return ""; + } else if (ctag.isDouble()) { + return ""; + } else if (ctag.isString() || ctag.isUnresolvedString()) { + // tag change from 'unresolved' to 'string' does not happen atomically. + // We just look at the object at the corresponding index and + // decide based on the oop type. + Oop obj = cpool.getObjAt(cpIndex); + if (obj.isSymbol()) { + Symbol sym = (Symbol) obj; + return ""; + } else if (obj.isInstance()) { + return ""; + } else { + throw new RuntimeException("should not reach here"); + } + } else if (ctag.isKlass() || ctag.isUnresolvedKlass()) { + // tag change from 'unresolved' to 'klass' does not happen atomically. + // We just look at the object at the corresponding index and + // decide based on the oop type. + Oop obj = cpool.getObjAt(cpIndex); + if (obj.isKlass()) { + Klass k = (Klass) obj; + return ""; + } else if (obj.isSymbol()) { + Symbol sym = (Symbol) obj; + return ""; + } else { + throw new RuntimeException("should not reach here"); + } + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "invalid load constant type"); + } + return null; + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getJavaBytecodeName()); + buf.append(spaces); + buf.append('#'); + buf.append(Integer.toString(index())); + buf.append(spaces); + buf.append(getConstantValue()); + if (code() != javaCode()) { + buf.append(spaces); + buf.append('['); + buf.append(getBytecodeName()); + buf.append(']'); + } + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadStore.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadStore.java new file mode 100644 index 00000000000..ca015fc406d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadStore.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; + +public abstract class BytecodeLoadStore extends BytecodeWideable { + BytecodeLoadStore(Method method, int bci) { + super(method, bci); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getJavaBytecodeName()); + buf.append(spaces); + buf.append('#'); + buf.append(Integer.toString(getLocalVarIndex())); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLookupswitch.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLookupswitch.java new file mode 100644 index 00000000000..6c2333eff4e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLookupswitch.java @@ -0,0 +1,99 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeLookupswitch extends Bytecode { + BytecodeLookupswitch(Method method, int bci) { + super(method, bci); + } + + // Attributes + public int defaultOffset() { return javaSignedWordAt(alignedOffset(1 + 0*jintSize)); } + public int numberOfPairs() { return javaSignedWordAt(alignedOffset(1 + 1*jintSize)); } + public LookupswitchPair pairAt(int i) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(0 <= i && i < numberOfPairs(), "pair index out of bounds"); + } + return new LookupswitchPair(method, bci + alignedOffset(1 + (1 + i)*2*jintSize)); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check lookupswitch"); + } + } + + public boolean isValid() { + boolean result = javaCode() == Bytecodes._lookupswitch; + if (result == false) return false; + int i = numberOfPairs() - 1; + while (i-- > 0) { + if(pairAt(i).match() > pairAt(i+1).match()) + return false; // unsorted lookup table + } + return true; + } + + public static BytecodeLookupswitch at(Method method, int bci) { + BytecodeLookupswitch b = new BytecodeLookupswitch(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at lookupswitch */ + public static BytecodeLookupswitch atCheck(Method method, int bci) { + BytecodeLookupswitch b = new BytecodeLookupswitch(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeLookupswitch at(BytecodeStream bcs) { + return new BytecodeLookupswitch(bcs.method(), bcs.bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("lookupswitch"); + buf.append(spaces); + buf.append("default: "); + buf.append(Integer.toString(bci() + defaultOffset())); + buf.append(comma); + int i = numberOfPairs() - 1; + while (i-- > 0) { + LookupswitchPair pair = pairAt(i); + buf.append("case "); + buf.append(Integer.toString(pair.match())); + buf.append(':'); + buf.append(Integer.toString(bci() + pair.offset())); + buf.append(comma); + } + + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeMultiANewArray.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeMultiANewArray.java new file mode 100644 index 00000000000..0c17608da5c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeMultiANewArray.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeMultiANewArray extends BytecodeWithKlass { + BytecodeMultiANewArray(Method method, int bci) { + super(method, bci); + } + + public Klass getKlass() { + return super.getKlass(); + } + + public int getDimension() { + return 0xFF & javaByteAt(2); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check multianewarray"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._multianewarray; + } + + public static BytecodeMultiANewArray at(Method method, int bci) { + BytecodeMultiANewArray b = new BytecodeMultiANewArray(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at multianewarray */ + public static BytecodeMultiANewArray atCheck(Method method, int bci) { + BytecodeMultiANewArray b = new BytecodeMultiANewArray(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeMultiANewArray at(BytecodeStream bcs) { + return new BytecodeMultiANewArray(bcs.method(), bcs.bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(super.toString()); + buf.append(spaces); + buf.append(Integer.toString(getDimension())); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeNew.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeNew.java new file mode 100644 index 00000000000..a58d2cc7118 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeNew.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeNew extends BytecodeWithKlass { + BytecodeNew(Method method, int bci) { + super(method, bci); + } + + public InstanceKlass getNewKlass() { + return (InstanceKlass) getKlass(); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check new"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._new; + } + + public static BytecodeNew at(Method method, int bci) { + BytecodeNew b = new BytecodeNew(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at new */ + public static BytecodeNew atCheck(Method method, int bci) { + BytecodeNew b = new BytecodeNew(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeNew at(BytecodeStream bcs) { + return new BytecodeNew(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeNewArray.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeNewArray.java new file mode 100644 index 00000000000..50ab06b9ee5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeNewArray.java @@ -0,0 +1,133 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeNewArray extends Bytecode { + BytecodeNewArray(Method method, int bci) { + super(method, bci); + } + + public int getType() { + return (int) javaByteAt(1); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check newarray"); + } + } + + public boolean isValid() { + boolean result = javaCode() == Bytecodes._newarray; + if (result == false) return false; + switch (getType()) { + case TypeArrayKlass.T_BOOLEAN: + case TypeArrayKlass.T_CHAR: + case TypeArrayKlass.T_FLOAT: + case TypeArrayKlass.T_DOUBLE: + case TypeArrayKlass.T_BYTE: + case TypeArrayKlass.T_SHORT: + case TypeArrayKlass.T_INT: + case TypeArrayKlass.T_LONG: + break; + default: + return false; + } + + return true; + } + + public String getTypeName() { + String result; + switch (getType()) { + case TypeArrayKlass.T_BOOLEAN: + result = "boolean"; + break; + + case TypeArrayKlass.T_CHAR: + result = "char"; + break; + + case TypeArrayKlass.T_FLOAT: + result = "float"; + break; + + case TypeArrayKlass.T_DOUBLE: + result = "double"; + break; + + case TypeArrayKlass.T_BYTE: + result = "byte"; + break; + + case TypeArrayKlass.T_SHORT: + result = "short"; + break; + + case TypeArrayKlass.T_INT: + result = "int"; + break; + + case TypeArrayKlass.T_LONG: + result = "long"; + break; + + default: // should not happen + result = ""; + break; + } + + return result; + } + + public static BytecodeNewArray at(Method method, int bci) { + BytecodeNewArray b = new BytecodeNewArray(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at newarray */ + public static BytecodeNewArray atCheck(Method method, int bci) { + BytecodeNewArray b = new BytecodeNewArray(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeNewArray at(BytecodeStream bcs) { + return new BytecodeNewArray(bcs.method(), bcs.bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("newarray"); + buf.append(spaces); + buf.append(getTypeName()); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodePutField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodePutField.java new file mode 100644 index 00000000000..a4e092d37d0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodePutField.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodePutField extends BytecodeGetPut { + BytecodePutField(Method method, int bci) { + super(method, bci); + } + + public boolean isStatic() { + return false; + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check putfield"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._putfield; + } + + public static BytecodePutField at(Method method, int bci) { + BytecodePutField b = new BytecodePutField(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at putfield */ + public static BytecodePutField atCheck(Method method, int bci) { + BytecodePutField b = new BytecodePutField(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodePutField at(BytecodeStream bcs) { + return new BytecodePutField(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodePutStatic.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodePutStatic.java new file mode 100644 index 00000000000..c12620b6e80 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodePutStatic.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodePutStatic extends BytecodeGetPut { + BytecodePutStatic(Method method, int bci) { + super(method, bci); + } + + public boolean isStatic() { + return true; + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check putstatic"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._putstatic; + } + + public static BytecodePutStatic at(Method method, int bci) { + BytecodePutStatic b = new BytecodePutStatic(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at putstatic */ + public static BytecodePutStatic atCheck(Method method, int bci) { + BytecodePutStatic b = new BytecodePutStatic(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodePutStatic at(BytecodeStream bcs) { + return new BytecodePutStatic(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeRet.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeRet.java new file mode 100644 index 00000000000..e6f2096c783 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeRet.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeRet extends BytecodeWideable { + BytecodeRet(Method method, int bci) { + super(method, bci); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check ret"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._ret; + } + + public static BytecodeRet at(Method method, int bci) { + BytecodeRet b = new BytecodeRet(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at ret */ + public static BytecodeRet atCheck(Method method, int bci) { + BytecodeRet b = new BytecodeRet(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeRet at(BytecodeStream bcs) { + return new BytecodeRet(bcs.method(), bcs.bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("ret"); + buf.append(spaces); + buf.append('#'); + buf.append(Integer.toString(getLocalVarIndex())); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeSipush.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeSipush.java new file mode 100644 index 00000000000..2b2ceba7360 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeSipush.java @@ -0,0 +1,74 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeSipush extends Bytecode { + BytecodeSipush(Method method, int bci) { + super(method, bci); + } + + public short getValue() { + return javaShortAt(1); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check sipush"); + } + } + + public boolean isValid() { + return javaCode() == Bytecodes._sipush; + } + + public static BytecodeSipush at(Method method, int bci) { + BytecodeSipush b = new BytecodeSipush(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at sipush */ + public static BytecodeSipush atCheck(Method method, int bci) { + BytecodeSipush b = new BytecodeSipush(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeSipush at(BytecodeStream bcs) { + return new BytecodeSipush(bcs.method(), bcs.bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("sipush"); + buf.append(spaces); + buf.append(Short.toString(getValue())); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeStore.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeStore.java new file mode 100644 index 00000000000..cd396527a11 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeStore.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeStore extends BytecodeLoadStore { + BytecodeStore(Method method, int bci) { + super(method, bci); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check store"); + } + } + + public boolean isValid() { + int jcode = javaCode(); + switch (jcode) { + case Bytecodes._istore: + case Bytecodes._lstore: + case Bytecodes._fstore: + case Bytecodes._dstore: + case Bytecodes._astore: + return true; + default: + return false; + } + } + + public static BytecodeStore at(Method method, int bci) { + BytecodeStore b = new BytecodeStore(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at store */ + public static BytecodeStore atCheck(Method method, int bci) { + BytecodeStore b = new BytecodeStore(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeStore at(BytecodeStream bcs) { + return new BytecodeStore(bcs.method(), bcs.bci()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeStream.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeStream.java new file mode 100644 index 00000000000..187bb4adad0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeStream.java @@ -0,0 +1,139 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeStream { + private Method _method; + + // reading position + private int _bci; // bci if current bytecode + private int _next_bci; // bci of next bytecode + private int _end_bci; // bci after the current iteration interval + + // last bytecode read + private int _code; + private boolean _is_wide; + + // Construction + public BytecodeStream(Method method) { + _method = method; + setInterval(0, (int) method.getCodeSize()); + } + + // Iteration control + public void setInterval(int beg_bci, int end_bci) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(0 <= beg_bci && beg_bci <= _method.getCodeSize(), "illegal beg_bci"); + Assert.that(0 <= end_bci && end_bci <= _method.getCodeSize(), "illegal end_bci"); + } + // setup of iteration pointers + _bci = beg_bci; + _next_bci = beg_bci; + _end_bci = end_bci; + } + + public void setStart(int beg_bci) { + setInterval(beg_bci, (int) _method.getCodeSize()); + } + + // Iteration + public int next() { + int code; + // set reading position + _bci = _next_bci; + if (isLastBytecode()) { + // indicate end of bytecode stream + code = Bytecodes._illegal; + } else { + // get bytecode + int rawCode = Bytecodes.codeAt(_method, _bci); + code = 0; // Make javac happy + try { + code = Bytecodes.javaCode(rawCode); + } catch (AssertionFailure e) { + e.printStackTrace(); + Assert.that(false, "Failure occurred at bci " + _bci + " in method " + _method.externalNameAndSignature()); + } + + // set next bytecode position + // + int l = Bytecodes.lengthFor(code); + if (l == 0) l = Bytecodes.lengthAt(_method, _bci); + _next_bci += l; + if (Assert.ASSERTS_ENABLED) { + Assert.that(_bci < _next_bci, "length must be > 0"); + } + // set attributes + _is_wide = false; + // check for special (uncommon) cases + if (code == Bytecodes._wide) { + code = _method.getBytecodeOrBPAt(_bci + 1); + _is_wide = true; + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(Bytecodes.isJavaCode(code), "sanity check"); + } + } + _code = code; + return _code; + } + + // Stream attributes + public Method method() { return _method; } + public int bci() { return _bci; } + public int nextBCI() { return _next_bci; } + public int endBCI() { return _end_bci; } + public int code() { return _code; } + public boolean isWide() { return _is_wide; } + public boolean isActiveBreakpoint() { return Bytecodes.isActiveBreakpointAt(_method, _bci); } + public boolean isLastBytecode() { return _next_bci >= _end_bci; } + + // State changes + public void setNextBCI(int bci) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(0 <= bci && bci <= _method.getCodeSize(), "illegal bci"); + } + _next_bci = bci; + } + + // Bytecode-specific attributes + public int dest() { return bci() + _method.getBytecodeShortArg(bci() + 1); } + public int dest_w() { return bci() + _method.getBytecodeIntArg(bci() + 1); } + + // Unsigned indices, widening + public int getIndex() { return (isWide()) + ? (_method.getBytecodeShortArg(bci() + 2) & 0xFFFF) + : (_method.getBytecodeOrBPAt(bci() + 1) & 0xFF); } + public int getIndexBig() { return _method.getBytecodeShortArg(bci() + 1); } + + // Fetch at absolute BCI (for manual parsing of certain bytecodes) + public int codeAt(int bci) { + return _method.getBytecodeOrBPAt(bci); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeTableswitch.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeTableswitch.java new file mode 100644 index 00000000000..1917a9d676f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeTableswitch.java @@ -0,0 +1,105 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class BytecodeTableswitch extends Bytecode { + BytecodeTableswitch(Method method, int bci) { + super(method, bci); + } + + + // Attributes + public int defaultOffset() { return javaSignedWordAt(alignedOffset(1 + 0*jintSize)); } + public int lowKey() { return javaSignedWordAt(alignedOffset(1 + 1*jintSize)); } + public int highKey() { return javaSignedWordAt(alignedOffset(1 + 2*jintSize)); } + public int length() { return highKey()-lowKey()+1; } + public int destOffsetAt(int i) { + int x2 = alignedOffset(1 + (3 + i)*jintSize); + int val = javaSignedWordAt(x2); + return javaSignedWordAt(alignedOffset(1 + (3 + i)*jintSize)); + } + + public void verify() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isValid(), "check tableswitch"); + } + } + + public boolean isValid() { + boolean result = javaCode() == Bytecodes._tableswitch; + if (result == false) return false; + int lo = lowKey(); + int hi = highKey(); + if (hi < lo) // incorrect hi/lo values in tableswitch + return false; + + int i = hi - lo - 1 ; + while (i-- > 0) { + // no special check needed + } + return true; + } + + public static BytecodeTableswitch at(Method method, int bci) { + BytecodeTableswitch b = new BytecodeTableswitch(method, bci); + if (Assert.ASSERTS_ENABLED) { + b.verify(); + } + return b; + } + + /** Like at, but returns null if the BCI is not at tableswitch */ + public static BytecodeTableswitch atCheck(Method method, int bci) { + BytecodeTableswitch b = new BytecodeTableswitch(method, bci); + return (b.isValid() ? b : null); + } + + public static BytecodeTableswitch at(BytecodeStream bcs) { + return new BytecodeTableswitch(bcs.method(), bcs.bci()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("tableswitch"); + buf.append(spaces); + buf.append("default: "); + buf.append(Integer.toString(bci() + defaultOffset())); + buf.append(comma); + int lo = lowKey(); + int hi = highKey(); + int i = hi - lo - 1 ; + while (i-- > 0) { + buf.append("case "); + buf.append(Integer.toString(lo + i)); + buf.append(':'); + buf.append(Integer.toString(bci() + destOffsetAt(i))); + buf.append(comma); + } + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeVisitor.java new file mode 100644 index 00000000000..d2fd6e11414 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeVisitor.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.Method; + +public interface BytecodeVisitor { + public void prologue(Method method); + public void visit(Bytecode bytecode); + public void epilogue(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeWideable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeWideable.java new file mode 100644 index 00000000000..7459a6743e2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeWideable.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; + +public abstract class BytecodeWideable extends Bytecode { + BytecodeWideable(Method method, int bci) { + super(method, bci); + } + + public boolean isWide() { + int prevBci = bci() - 1; + return (prevBci > -1 && method.getBytecodeOrBPAt(prevBci) == Bytecodes._wide); + } + + // the local variable index + public int getLocalVarIndex() { + return (isWide()) ? (int) (0xFFFF & javaShortAt(1)) + : (int) (0xFF & javaByteAt(1)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeWithCPIndex.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeWithCPIndex.java new file mode 100644 index 00000000000..4c6fee2368b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeWithCPIndex.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +// any bytecode with constant pool index + +public abstract class BytecodeWithCPIndex extends Bytecode { + BytecodeWithCPIndex(Method method, int bci) { + super(method, bci); + } + + // the constant pool index for this bytecode + public int index() { return 0xFFFF & javaShortAt(1); } + + protected int indexForFieldOrMethod() { + ConstantPoolCache cpCache = method().getConstants().getCache(); + // get ConstantPool index from ConstantPoolCacheIndex at given bci + int cpCacheIndex = index(); + if (cpCache == null) { + return cpCacheIndex; + } else { + // change byte-ordering and go via cache + return cpCache.getEntryAt((int) (0xFFFF & VM.getVM().getBytes().swapShort((short) cpCacheIndex))).getConstantPoolIndex(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeWithKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeWithKlass.java new file mode 100644 index 00000000000..c094578aa9f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeWithKlass.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +public class BytecodeWithKlass extends BytecodeWithCPIndex { + BytecodeWithKlass(Method method, int bci) { + super(method, bci); + } + + protected Klass getKlass() { + return method().getConstants().getKlassRefAt(index()); + } + + public Symbol getClassName() { + Oop obj = method().getConstants().getObjAt(index()); + if (obj instanceof Symbol) { + return (Symbol)obj; + } else { + return ((Klass)obj).getName(); + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getJavaBytecodeName()); + buf.append(spaces); + buf.append('#'); + buf.append(Integer.toString(index())); + buf.append(spaces); + buf.append("[Class "); + buf.append(getClassName().asString().replace('/', '.')); + buf.append(']'); + if (code() != javaCode()) { + buf.append(spaces); + buf.append('['); + buf.append(getBytecodeName()); + buf.append(']'); + } + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java new file mode 100644 index 00000000000..1de1967cf09 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java @@ -0,0 +1,720 @@ +/* + * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +// Bytecodes specifies all bytecodes used in the VM and +// provides utility functions to get bytecode attributes. + +public class Bytecodes { + public static final int _illegal = -1; + + // Java bytecodes + public static final int _nop = 0; // 0x00 + public static final int _aconst_null = 1; // 0x01 + public static final int _iconst_m1 = 2; // 0x02 + public static final int _iconst_0 = 3; // 0x03 + public static final int _iconst_1 = 4; // 0x04 + public static final int _iconst_2 = 5; // 0x05 + public static final int _iconst_3 = 6; // 0x06 + public static final int _iconst_4 = 7; // 0x07 + public static final int _iconst_5 = 8; // 0x08 + public static final int _lconst_0 = 9; // 0x09 + public static final int _lconst_1 = 10; // 0x0a + public static final int _fconst_0 = 11; // 0x0b + public static final int _fconst_1 = 12; // 0x0c + public static final int _fconst_2 = 13; // 0x0d + public static final int _dconst_0 = 14; // 0x0e + public static final int _dconst_1 = 15; // 0x0f + public static final int _bipush = 16; // 0x10 + public static final int _sipush = 17; // 0x11 + public static final int _ldc = 18; // 0x12 + public static final int _ldc_w = 19; // 0x13 + public static final int _ldc2_w = 20; // 0x14 + public static final int _iload = 21; // 0x15 + public static final int _lload = 22; // 0x16 + public static final int _fload = 23; // 0x17 + public static final int _dload = 24; // 0x18 + public static final int _aload = 25; // 0x19 + public static final int _iload_0 = 26; // 0x1a + public static final int _iload_1 = 27; // 0x1b + public static final int _iload_2 = 28; // 0x1c + public static final int _iload_3 = 29; // 0x1d + public static final int _lload_0 = 30; // 0x1e + public static final int _lload_1 = 31; // 0x1f + public static final int _lload_2 = 32; // 0x20 + public static final int _lload_3 = 33; // 0x21 + public static final int _fload_0 = 34; // 0x22 + public static final int _fload_1 = 35; // 0x23 + public static final int _fload_2 = 36; // 0x24 + public static final int _fload_3 = 37; // 0x25 + public static final int _dload_0 = 38; // 0x26 + public static final int _dload_1 = 39; // 0x27 + public static final int _dload_2 = 40; // 0x28 + public static final int _dload_3 = 41; // 0x29 + public static final int _aload_0 = 42; // 0x2a + public static final int _aload_1 = 43; // 0x2b + public static final int _aload_2 = 44; // 0x2c + public static final int _aload_3 = 45; // 0x2d + public static final int _iaload = 46; // 0x2e + public static final int _laload = 47; // 0x2f + public static final int _faload = 48; // 0x30 + public static final int _daload = 49; // 0x31 + public static final int _aaload = 50; // 0x32 + public static final int _baload = 51; // 0x33 + public static final int _caload = 52; // 0x34 + public static final int _saload = 53; // 0x35 + public static final int _istore = 54; // 0x36 + public static final int _lstore = 55; // 0x37 + public static final int _fstore = 56; // 0x38 + public static final int _dstore = 57; // 0x39 + public static final int _astore = 58; // 0x3a + public static final int _istore_0 = 59; // 0x3b + public static final int _istore_1 = 60; // 0x3c + public static final int _istore_2 = 61; // 0x3d + public static final int _istore_3 = 62; // 0x3e + public static final int _lstore_0 = 63; // 0x3f + public static final int _lstore_1 = 64; // 0x40 + public static final int _lstore_2 = 65; // 0x41 + public static final int _lstore_3 = 66; // 0x42 + public static final int _fstore_0 = 67; // 0x43 + public static final int _fstore_1 = 68; // 0x44 + public static final int _fstore_2 = 69; // 0x45 + public static final int _fstore_3 = 70; // 0x46 + public static final int _dstore_0 = 71; // 0x47 + public static final int _dstore_1 = 72; // 0x48 + public static final int _dstore_2 = 73; // 0x49 + public static final int _dstore_3 = 74; // 0x4a + public static final int _astore_0 = 75; // 0x4b + public static final int _astore_1 = 76; // 0x4c + public static final int _astore_2 = 77; // 0x4d + public static final int _astore_3 = 78; // 0x4e + public static final int _iastore = 79; // 0x4f + public static final int _lastore = 80; // 0x50 + public static final int _fastore = 81; // 0x51 + public static final int _dastore = 82; // 0x52 + public static final int _aastore = 83; // 0x53 + public static final int _bastore = 84; // 0x54 + public static final int _castore = 85; // 0x55 + public static final int _sastore = 86; // 0x56 + public static final int _pop = 87; // 0x57 + public static final int _pop2 = 88; // 0x58 + public static final int _dup = 89; // 0x59 + public static final int _dup_x1 = 90; // 0x5a + public static final int _dup_x2 = 91; // 0x5b + public static final int _dup2 = 92; // 0x5c + public static final int _dup2_x1 = 93; // 0x5d + public static final int _dup2_x2 = 94; // 0x5e + public static final int _swap = 95; // 0x5f + public static final int _iadd = 96; // 0x60 + public static final int _ladd = 97; // 0x61 + public static final int _fadd = 98; // 0x62 + public static final int _dadd = 99; // 0x63 + public static final int _isub = 100; // 0x64 + public static final int _lsub = 101; // 0x65 + public static final int _fsub = 102; // 0x66 + public static final int _dsub = 103; // 0x67 + public static final int _imul = 104; // 0x68 + public static final int _lmul = 105; // 0x69 + public static final int _fmul = 106; // 0x6a + public static final int _dmul = 107; // 0x6b + public static final int _idiv = 108; // 0x6c + public static final int _ldiv = 109; // 0x6d + public static final int _fdiv = 110; // 0x6e + public static final int _ddiv = 111; // 0x6f + public static final int _irem = 112; // 0x70 + public static final int _lrem = 113; // 0x71 + public static final int _frem = 114; // 0x72 + public static final int _drem = 115; // 0x73 + public static final int _ineg = 116; // 0x74 + public static final int _lneg = 117; // 0x75 + public static final int _fneg = 118; // 0x76 + public static final int _dneg = 119; // 0x77 + public static final int _ishl = 120; // 0x78 + public static final int _lshl = 121; // 0x79 + public static final int _ishr = 122; // 0x7a + public static final int _lshr = 123; // 0x7b + public static final int _iushr = 124; // 0x7c + public static final int _lushr = 125; // 0x7d + public static final int _iand = 126; // 0x7e + public static final int _land = 127; // 0x7f + public static final int _ior = 128; // 0x80 + public static final int _lor = 129; // 0x81 + public static final int _ixor = 130; // 0x82 + public static final int _lxor = 131; // 0x83 + public static final int _iinc = 132; // 0x84 + public static final int _i2l = 133; // 0x85 + public static final int _i2f = 134; // 0x86 + public static final int _i2d = 135; // 0x87 + public static final int _l2i = 136; // 0x88 + public static final int _l2f = 137; // 0x89 + public static final int _l2d = 138; // 0x8a + public static final int _f2i = 139; // 0x8b + public static final int _f2l = 140; // 0x8c + public static final int _f2d = 141; // 0x8d + public static final int _d2i = 142; // 0x8e + public static final int _d2l = 143; // 0x8f + public static final int _d2f = 144; // 0x90 + public static final int _i2b = 145; // 0x91 + public static final int _i2c = 146; // 0x92 + public static final int _i2s = 147; // 0x93 + public static final int _lcmp = 148; // 0x94 + public static final int _fcmpl = 149; // 0x95 + public static final int _fcmpg = 150; // 0x96 + public static final int _dcmpl = 151; // 0x97 + public static final int _dcmpg = 152; // 0x98 + public static final int _ifeq = 153; // 0x99 + public static final int _ifne = 154; // 0x9a + public static final int _iflt = 155; // 0x9b + public static final int _ifge = 156; // 0x9c + public static final int _ifgt = 157; // 0x9d + public static final int _ifle = 158; // 0x9e + public static final int _if_icmpeq = 159; // 0x9f + public static final int _if_icmpne = 160; // 0xa0 + public static final int _if_icmplt = 161; // 0xa1 + public static final int _if_icmpge = 162; // 0xa2 + public static final int _if_icmpgt = 163; // 0xa3 + public static final int _if_icmple = 164; // 0xa4 + public static final int _if_acmpeq = 165; // 0xa5 + public static final int _if_acmpne = 166; // 0xa6 + public static final int _goto = 167; // 0xa7 + public static final int _jsr = 168; // 0xa8 + public static final int _ret = 169; // 0xa9 + public static final int _tableswitch = 170; // 0xaa + public static final int _lookupswitch = 171; // 0xab + public static final int _ireturn = 172; // 0xac + public static final int _lreturn = 173; // 0xad + public static final int _freturn = 174; // 0xae + public static final int _dreturn = 175; // 0xaf + public static final int _areturn = 176; // 0xb0 + public static final int _return = 177; // 0xb1 + public static final int _getstatic = 178; // 0xb2 + public static final int _putstatic = 179; // 0xb3 + public static final int _getfield = 180; // 0xb4 + public static final int _putfield = 181; // 0xb5 + public static final int _invokevirtual = 182; // 0xb6 + public static final int _invokespecial = 183; // 0xb7 + public static final int _invokestatic = 184; // 0xb8 + public static final int _invokeinterface = 185; // 0xb9 + public static final int _xxxunusedxxx = 186; // 0xba + public static final int _new = 187; // 0xbb + public static final int _newarray = 188; // 0xbc + public static final int _anewarray = 189; // 0xbd + public static final int _arraylength = 190; // 0xbe + public static final int _athrow = 191; // 0xbf + public static final int _checkcast = 192; // 0xc0 + public static final int _instanceof = 193; // 0xc1 + public static final int _monitorenter = 194; // 0xc2 + public static final int _monitorexit = 195; // 0xc3 + public static final int _wide = 196; // 0xc4 + public static final int _multianewarray = 197; // 0xc5 + public static final int _ifnull = 198; // 0xc6 + public static final int _ifnonnull = 199; // 0xc7 + public static final int _goto_w = 200; // 0xc8 + public static final int _jsr_w = 201; // 0xc9 + public static final int _breakpoint = 202; // 0xca + + public static final int number_of_java_codes = 203; + + // JVM bytecodes + public static final int _fast_agetfield = number_of_java_codes; + public static final int _fast_bgetfield = 204; + public static final int _fast_cgetfield = 205; + public static final int _fast_dgetfield = 206; + public static final int _fast_fgetfield = 207; + public static final int _fast_igetfield = 208; + public static final int _fast_lgetfield = 209; + public static final int _fast_sgetfield = 210; + public static final int _fast_aputfield = 211; + public static final int _fast_bputfield = 212; + public static final int _fast_cputfield = 213; + public static final int _fast_dputfield = 214; + public static final int _fast_fputfield = 215; + public static final int _fast_iputfield = 216; + public static final int _fast_lputfield = 217; + public static final int _fast_sputfield = 218; + public static final int _fast_aload_0 = 219; + public static final int _fast_iaccess_0 = 220; + public static final int _fast_aaccess_0 = 221; + public static final int _fast_faccess_0 = 222; + public static final int _fast_iload = 223; + public static final int _fast_iload2 = 224; + public static final int _fast_icaload = 225; + public static final int _fast_invokevfinal = 226; + public static final int _fast_linearswitch = 227; + public static final int _fast_binaryswitch = 228; + public static final int _shouldnotreachhere = 229; // For debugging + + public static final int number_of_codes = 230; + + public static int specialLengthAt(Method method, int bci) { + int code = codeAt(method, bci); + switch (code) { + case _wide: + return wideLengthFor(method.getBytecodeOrBPAt(bci + 1)); + case _tableswitch: + { + int alignedBCI = Bits.roundTo(bci + 1, jintSize); + int lo = method.getBytecodeIntArg(alignedBCI + 1*jintSize); + int hi = method.getBytecodeIntArg(alignedBCI + 2*jintSize); + return (alignedBCI - bci) + (3 + hi - lo + 1)*jintSize; + } + + case _lookupswitch: // fall through + case _fast_binaryswitch: // fall through + case _fast_linearswitch: + { + int alignedBCI = Bits.roundTo(bci + 1, jintSize); + int npairs = method.getBytecodeIntArg(alignedBCI + jintSize); + return (alignedBCI - bci) + (2 + 2*npairs)*jintSize; + } + + } + throw new RuntimeException("should not reach here"); + } + + // Conversion + public static void check(int code) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isDefined(code), "illegal code " + code); + } + } + public static void wideCheck(int code) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(wideIsDefined(code), "illegal code " + code); + } + } + + /** Fetches a bytecode, hiding breakpoints as necessary */ + public static int codeAt(Method method, int bci) { + int res = codeOrBPAt(method, bci); + if (res == _breakpoint) { + res = method.getOrigBytecodeAt(bci); + } + return res; + } + + /** Fetches a bytecode or a breakpoint */ + public static int codeOrBPAt(Method method, int bci) { + return method.getBytecodeOrBPAt(bci); + } + + public static boolean isActiveBreakpointAt(Method method, int bci) { + return (codeOrBPAt(method, bci) == _breakpoint); + } + + // find a bytecode, behind a breakpoint if necessary: + // FIXME: not yet implementable + // static Code non_breakpoint_code_at(address bcp, methodOop method = null); + + // Bytecode attributes + public static boolean isDefined (int code) { return 0 <= code && code < number_of_codes && _format[code] != null; } + public static boolean wideIsDefined(int code) { return isDefined(code) && _wide_format[code] != null; } + public static String name (int code) { check(code); return _name [code]; } + public static String format (int code) { check(code); return _format [code]; } + public static String wideFormat (int code) { wideCheck(code); return _wide_format [code]; } + public static int resultType (int code) { check(code); return _result_type [code]; } + public static int depth (int code) { check(code); return _depth [code]; } + public static int lengthFor (int code) { check(code); return _length [code]; } + public static boolean canTrap (int code) { check(code); return _can_trap [code]; } + public static int javaCode (int code) { check(code); return _java_code [code]; } + public static boolean canRewrite (int code) { check(code); return _can_rewrite [code]; } + public static int wideLengthFor(int code) { wideCheck(code); return wideFormat(code).length(); } + public static int lengthAt (Method method, int bci) { int l = lengthFor(codeAt(method, bci)); return l > 0 ? l : specialLengthAt(method, bci); } + public static int javaLengthAt (Method method, int bci) { int l = lengthFor(javaCode(codeAt(method, bci))); return l > 0 ? l : specialLengthAt(method, bci); } + public static boolean isJavaCode (int code) { return 0 <= code && code < number_of_java_codes; } + public static boolean isFastCode (int code) { return number_of_java_codes <= code && code < number_of_codes; } + + public static boolean isAload (int code) { return (code == _aload || code == _aload_0 || code == _aload_1 + || code == _aload_2 || code == _aload_3); } + public static boolean isAstore (int code) { return (code == _astore || code == _astore_0 || code == _astore_1 + || code == _astore_2 || code == _astore_3); } + + public static boolean isZeroConst (int code) { return (code == _aconst_null || code == _iconst_0 + || code == _fconst_0 || code == _dconst_0); } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private static String[] _name; + private static String[] _format; + private static String[] _wide_format; + private static int[] _result_type; + private static byte[] _depth; + private static byte[] _length; + private static boolean[] _can_trap; + private static int[] _java_code; + private static boolean[] _can_rewrite; + + static { + _name = new String [number_of_codes]; + _format = new String [number_of_codes]; + _wide_format = new String [number_of_codes]; + _result_type = new int [number_of_codes]; // See BasicType.java + _depth = new byte [number_of_codes]; + _length = new byte [number_of_codes]; + _can_trap = new boolean[number_of_codes]; + _java_code = new int [number_of_codes]; + _can_rewrite = new boolean[number_of_codes]; + + // In case we want to fetch this information from the VM in the + // future + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(); + } + }); + } + + private static final int jintSize = 4; + + // private static String[] _name = new String [number_of_codes]; + // private static String[] _format = new String [number_of_codes]; + // private static String[] _wide_format = new String [number_of_codes]; + // private static int[] _result_type = new int [number_of_codes]; // See BasicType.java + // private static byte[] _depth = new byte [number_of_codes]; + // private static byte[] _length = new byte [number_of_codes]; + // private static boolean[] _can_trap = new boolean[number_of_codes]; + // private static int[] _java_code = new int [number_of_codes]; + // private static boolean[] _can_rewrite = new boolean[number_of_codes]; + + // Initialization + private static void initialize() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(number_of_codes <= 256, "too many bytecodes"); + } + + // Format strings interpretation: + // + // b: bytecode + // c: signed constant, Java byte-ordering + // i: unsigned index , Java byte-ordering + // j: unsigned index , native byte-ordering + // o: branch offset , Java byte-ordering + // _: unused/ignored + // w: wide bytecode + // + // Note: Right now the format strings are used for 2 purposes: + // 1. to specify the length of the bytecode + // (= number of characters in format string) + // 2. to specify the bytecode attributes + // + // The bytecode attributes are currently used only for bytecode tracing + // (see BytecodeTracer); thus if more specific format information is + // used, one would also have to adjust the bytecode tracer. + // + // Note: For bytecodes with variable length, the format string is the empty string. + + // Note 1: null for the format string means the bytecode doesn't exist + // in that form. + // + // Note 2: The result type is T_ILLEGAL for bytecodes where the top of stack + // type after execution is not only determined by the bytecode itself. + + // Java bytecodes + // bytecode bytecode name format wide f. result tp stk traps + def(_nop , "nop" , "b" , null , BasicType.getTVoid() , 0, false); + def(_aconst_null , "aconst_null" , "b" , null , BasicType.getTObject() , 1, false); + def(_iconst_m1 , "iconst_m1" , "b" , null , BasicType.getTInt() , 1, false); + def(_iconst_0 , "iconst_0" , "b" , null , BasicType.getTInt() , 1, false); + def(_iconst_1 , "iconst_1" , "b" , null , BasicType.getTInt() , 1, false); + def(_iconst_2 , "iconst_2" , "b" , null , BasicType.getTInt() , 1, false); + def(_iconst_3 , "iconst_3" , "b" , null , BasicType.getTInt() , 1, false); + def(_iconst_4 , "iconst_4" , "b" , null , BasicType.getTInt() , 1, false); + def(_iconst_5 , "iconst_5" , "b" , null , BasicType.getTInt() , 1, false); + def(_lconst_0 , "lconst_0" , "b" , null , BasicType.getTLong() , 2, false); + def(_lconst_1 , "lconst_1" , "b" , null , BasicType.getTLong() , 2, false); + def(_fconst_0 , "fconst_0" , "b" , null , BasicType.getTFloat() , 1, false); + def(_fconst_1 , "fconst_1" , "b" , null , BasicType.getTFloat() , 1, false); + def(_fconst_2 , "fconst_2" , "b" , null , BasicType.getTFloat() , 1, false); + def(_dconst_0 , "dconst_0" , "b" , null , BasicType.getTDouble() , 2, false); + def(_dconst_1 , "dconst_1" , "b" , null , BasicType.getTDouble() , 2, false); + def(_bipush , "bipush" , "bc" , null , BasicType.getTInt() , 1, false); + def(_sipush , "sipush" , "bcc" , null , BasicType.getTInt() , 1, false); + def(_ldc , "ldc" , "bi" , null , BasicType.getTIllegal(), 1, true ); + def(_ldc_w , "ldc_w" , "bii" , null , BasicType.getTIllegal(), 1, true ); + def(_ldc2_w , "ldc2_w" , "bii" , null , BasicType.getTIllegal(), 2, true ); + def(_iload , "iload" , "bi" , "wbii" , BasicType.getTInt() , 1, false); + def(_lload , "lload" , "bi" , "wbii" , BasicType.getTLong() , 2, false); + def(_fload , "fload" , "bi" , "wbii" , BasicType.getTFloat() , 1, false); + def(_dload , "dload" , "bi" , "wbii" , BasicType.getTDouble() , 2, false); + def(_aload , "aload" , "bi" , "wbii" , BasicType.getTObject() , 1, false); + def(_iload_0 , "iload_0" , "b" , null , BasicType.getTInt() , 1, false); + def(_iload_1 , "iload_1" , "b" , null , BasicType.getTInt() , 1, false); + def(_iload_2 , "iload_2" , "b" , null , BasicType.getTInt() , 1, false); + def(_iload_3 , "iload_3" , "b" , null , BasicType.getTInt() , 1, false); + def(_lload_0 , "lload_0" , "b" , null , BasicType.getTLong() , 2, false); + def(_lload_1 , "lload_1" , "b" , null , BasicType.getTLong() , 2, false); + def(_lload_2 , "lload_2" , "b" , null , BasicType.getTLong() , 2, false); + def(_lload_3 , "lload_3" , "b" , null , BasicType.getTLong() , 2, false); + def(_fload_0 , "fload_0" , "b" , null , BasicType.getTFloat() , 1, false); + def(_fload_1 , "fload_1" , "b" , null , BasicType.getTFloat() , 1, false); + def(_fload_2 , "fload_2" , "b" , null , BasicType.getTFloat() , 1, false); + def(_fload_3 , "fload_3" , "b" , null , BasicType.getTFloat() , 1, false); + def(_dload_0 , "dload_0" , "b" , null , BasicType.getTDouble() , 2, false); + def(_dload_1 , "dload_1" , "b" , null , BasicType.getTDouble() , 2, false); + def(_dload_2 , "dload_2" , "b" , null , BasicType.getTDouble() , 2, false); + def(_dload_3 , "dload_3" , "b" , null , BasicType.getTDouble() , 2, false); + def(_aload_0 , "aload_0" , "b" , null , BasicType.getTObject() , 1, true ); // rewriting in interpreter + def(_aload_1 , "aload_1" , "b" , null , BasicType.getTObject() , 1, false); + def(_aload_2 , "aload_2" , "b" , null , BasicType.getTObject() , 1, false); + def(_aload_3 , "aload_3" , "b" , null , BasicType.getTObject() , 1, false); + def(_iaload , "iaload" , "b" , null , BasicType.getTInt() , -1, true ); + def(_laload , "laload" , "b" , null , BasicType.getTLong() , 0, true ); + def(_faload , "faload" , "b" , null , BasicType.getTFloat() , -1, true ); + def(_daload , "daload" , "b" , null , BasicType.getTDouble() , 0, true ); + def(_aaload , "aaload" , "b" , null , BasicType.getTObject() , -1, true ); + def(_baload , "baload" , "b" , null , BasicType.getTInt() , -1, true ); + def(_caload , "caload" , "b" , null , BasicType.getTInt() , -1, true ); + def(_saload , "saload" , "b" , null , BasicType.getTInt() , -1, true ); + def(_istore , "istore" , "bi" , "wbii" , BasicType.getTVoid() , -1, false); + def(_lstore , "lstore" , "bi" , "wbii" , BasicType.getTVoid() , -2, false); + def(_fstore , "fstore" , "bi" , "wbii" , BasicType.getTVoid() , -1, false); + def(_dstore , "dstore" , "bi" , "wbii" , BasicType.getTVoid() , -2, false); + def(_astore , "astore" , "bi" , "wbii" , BasicType.getTVoid() , -1, false); + def(_istore_0 , "istore_0" , "b" , null , BasicType.getTVoid() , -1, false); + def(_istore_1 , "istore_1" , "b" , null , BasicType.getTVoid() , -1, false); + def(_istore_2 , "istore_2" , "b" , null , BasicType.getTVoid() , -1, false); + def(_istore_3 , "istore_3" , "b" , null , BasicType.getTVoid() , -1, false); + def(_lstore_0 , "lstore_0" , "b" , null , BasicType.getTVoid() , -2, false); + def(_lstore_1 , "lstore_1" , "b" , null , BasicType.getTVoid() , -2, false); + def(_lstore_2 , "lstore_2" , "b" , null , BasicType.getTVoid() , -2, false); + def(_lstore_3 , "lstore_3" , "b" , null , BasicType.getTVoid() , -2, false); + def(_fstore_0 , "fstore_0" , "b" , null , BasicType.getTVoid() , -1, false); + def(_fstore_1 , "fstore_1" , "b" , null , BasicType.getTVoid() , -1, false); + def(_fstore_2 , "fstore_2" , "b" , null , BasicType.getTVoid() , -1, false); + def(_fstore_3 , "fstore_3" , "b" , null , BasicType.getTVoid() , -1, false); + def(_dstore_0 , "dstore_0" , "b" , null , BasicType.getTVoid() , -2, false); + def(_dstore_1 , "dstore_1" , "b" , null , BasicType.getTVoid() , -2, false); + def(_dstore_2 , "dstore_2" , "b" , null , BasicType.getTVoid() , -2, false); + def(_dstore_3 , "dstore_3" , "b" , null , BasicType.getTVoid() , -2, false); + def(_astore_0 , "astore_0" , "b" , null , BasicType.getTVoid() , -1, false); + def(_astore_1 , "astore_1" , "b" , null , BasicType.getTVoid() , -1, false); + def(_astore_2 , "astore_2" , "b" , null , BasicType.getTVoid() , -1, false); + def(_astore_3 , "astore_3" , "b" , null , BasicType.getTVoid() , -1, false); + def(_iastore , "iastore" , "b" , null , BasicType.getTVoid() , -3, true ); + def(_lastore , "lastore" , "b" , null , BasicType.getTVoid() , -4, true ); + def(_fastore , "fastore" , "b" , null , BasicType.getTVoid() , -3, true ); + def(_dastore , "dastore" , "b" , null , BasicType.getTVoid() , -4, true ); + def(_aastore , "aastore" , "b" , null , BasicType.getTVoid() , -3, true ); + def(_bastore , "bastore" , "b" , null , BasicType.getTVoid() , -3, true ); + def(_castore , "castore" , "b" , null , BasicType.getTVoid() , -3, true ); + def(_sastore , "sastore" , "b" , null , BasicType.getTVoid() , -3, true ); + def(_pop , "pop" , "b" , null , BasicType.getTVoid() , -1, false); + def(_pop2 , "pop2" , "b" , null , BasicType.getTVoid() , -2, false); + def(_dup , "dup" , "b" , null , BasicType.getTVoid() , 1, false); + def(_dup_x1 , "dup_x1" , "b" , null , BasicType.getTVoid() , 1, false); + def(_dup_x2 , "dup_x2" , "b" , null , BasicType.getTVoid() , 1, false); + def(_dup2 , "dup2" , "b" , null , BasicType.getTVoid() , 2, false); + def(_dup2_x1 , "dup2_x1" , "b" , null , BasicType.getTVoid() , 2, false); + def(_dup2_x2 , "dup2_x2" , "b" , null , BasicType.getTVoid() , 2, false); + def(_swap , "swap" , "b" , null , BasicType.getTVoid() , 0, false); + def(_iadd , "iadd" , "b" , null , BasicType.getTInt() , -1, false); + def(_ladd , "ladd" , "b" , null , BasicType.getTLong() , -2, false); + def(_fadd , "fadd" , "b" , null , BasicType.getTFloat() , -1, false); + def(_dadd , "dadd" , "b" , null , BasicType.getTDouble() , -2, false); + def(_isub , "isub" , "b" , null , BasicType.getTInt() , -1, false); + def(_lsub , "lsub" , "b" , null , BasicType.getTLong() , -2, false); + def(_fsub , "fsub" , "b" , null , BasicType.getTFloat() , -1, false); + def(_dsub , "dsub" , "b" , null , BasicType.getTDouble() , -2, false); + def(_imul , "imul" , "b" , null , BasicType.getTInt() , -1, false); + def(_lmul , "lmul" , "b" , null , BasicType.getTLong() , -2, false); + def(_fmul , "fmul" , "b" , null , BasicType.getTFloat() , -1, false); + def(_dmul , "dmul" , "b" , null , BasicType.getTDouble() , -2, false); + def(_idiv , "idiv" , "b" , null , BasicType.getTInt() , -1, true ); + def(_ldiv , "ldiv" , "b" , null , BasicType.getTLong() , -2, true ); + def(_fdiv , "fdiv" , "b" , null , BasicType.getTFloat() , -1, false); + def(_ddiv , "ddiv" , "b" , null , BasicType.getTDouble() , -2, false); + def(_irem , "irem" , "b" , null , BasicType.getTInt() , -1, true ); + def(_lrem , "lrem" , "b" , null , BasicType.getTLong() , -2, true ); + def(_frem , "frem" , "b" , null , BasicType.getTFloat() , -1, false); + def(_drem , "drem" , "b" , null , BasicType.getTDouble() , -2, false); + def(_ineg , "ineg" , "b" , null , BasicType.getTInt() , 0, false); + def(_lneg , "lneg" , "b" , null , BasicType.getTLong() , 0, false); + def(_fneg , "fneg" , "b" , null , BasicType.getTFloat() , 0, false); + def(_dneg , "dneg" , "b" , null , BasicType.getTDouble() , 0, false); + def(_ishl , "ishl" , "b" , null , BasicType.getTInt() , -1, false); + def(_lshl , "lshl" , "b" , null , BasicType.getTLong() , -1, false); + def(_ishr , "ishr" , "b" , null , BasicType.getTInt() , -1, false); + def(_lshr , "lshr" , "b" , null , BasicType.getTLong() , -1, false); + def(_iushr , "iushr" , "b" , null , BasicType.getTInt() , -1, false); + def(_lushr , "lushr" , "b" , null , BasicType.getTLong() , -1, false); + def(_iand , "iand" , "b" , null , BasicType.getTInt() , -1, false); + def(_land , "land" , "b" , null , BasicType.getTLong() , -2, false); + def(_ior , "ior" , "b" , null , BasicType.getTInt() , -1, false); + def(_lor , "lor" , "b" , null , BasicType.getTLong() , -2, false); + def(_ixor , "ixor" , "b" , null , BasicType.getTInt() , -1, false); + def(_lxor , "lxor" , "b" , null , BasicType.getTLong() , -2, false); + def(_iinc , "iinc" , "bic" , "wbiicc", BasicType.getTVoid() , 0, false); + def(_i2l , "i2l" , "b" , null , BasicType.getTLong() , 1, false); + def(_i2f , "i2f" , "b" , null , BasicType.getTFloat() , 0, false); + def(_i2d , "i2d" , "b" , null , BasicType.getTDouble() , 1, false); + def(_l2i , "l2i" , "b" , null , BasicType.getTInt() , -1, false); + def(_l2f , "l2f" , "b" , null , BasicType.getTFloat() , -1, false); + def(_l2d , "l2d" , "b" , null , BasicType.getTDouble() , 0, false); + def(_f2i , "f2i" , "b" , null , BasicType.getTInt() , 0, false); + def(_f2l , "f2l" , "b" , null , BasicType.getTLong() , 1, false); + def(_f2d , "f2d" , "b" , null , BasicType.getTDouble() , 1, false); + def(_d2i , "d2i" , "b" , null , BasicType.getTInt() , -1, false); + def(_d2l , "d2l" , "b" , null , BasicType.getTLong() , 0, false); + def(_d2f , "d2f" , "b" , null , BasicType.getTFloat() , -1, false); + def(_i2b , "i2b" , "b" , null , BasicType.getTByte() , 0, false); + def(_i2c , "i2c" , "b" , null , BasicType.getTChar() , 0, false); + def(_i2s , "i2s" , "b" , null , BasicType.getTShort() , 0, false); + def(_lcmp , "lcmp" , "b" , null , BasicType.getTVoid() , -3, false); + def(_fcmpl , "fcmpl" , "b" , null , BasicType.getTVoid() , -1, false); + def(_fcmpg , "fcmpg" , "b" , null , BasicType.getTVoid() , -1, false); + def(_dcmpl , "dcmpl" , "b" , null , BasicType.getTVoid() , -3, false); + def(_dcmpg , "dcmpg" , "b" , null , BasicType.getTVoid() , -3, false); + def(_ifeq , "ifeq" , "boo" , null , BasicType.getTVoid() , -1, false); + def(_ifne , "ifne" , "boo" , null , BasicType.getTVoid() , -1, false); + def(_iflt , "iflt" , "boo" , null , BasicType.getTVoid() , -1, false); + def(_ifge , "ifge" , "boo" , null , BasicType.getTVoid() , -1, false); + def(_ifgt , "ifgt" , "boo" , null , BasicType.getTVoid() , -1, false); + def(_ifle , "ifle" , "boo" , null , BasicType.getTVoid() , -1, false); + def(_if_icmpeq , "if_icmpeq" , "boo" , null , BasicType.getTVoid() , -2, false); + def(_if_icmpne , "if_icmpne" , "boo" , null , BasicType.getTVoid() , -2, false); + def(_if_icmplt , "if_icmplt" , "boo" , null , BasicType.getTVoid() , -2, false); + def(_if_icmpge , "if_icmpge" , "boo" , null , BasicType.getTVoid() , -2, false); + def(_if_icmpgt , "if_icmpgt" , "boo" , null , BasicType.getTVoid() , -2, false); + def(_if_icmple , "if_icmple" , "boo" , null , BasicType.getTVoid() , -2, false); + def(_if_acmpeq , "if_acmpeq" , "boo" , null , BasicType.getTVoid() , -2, false); + def(_if_acmpne , "if_acmpne" , "boo" , null , BasicType.getTVoid() , -2, false); + def(_goto , "goto" , "boo" , null , BasicType.getTVoid() , 0, false); + def(_jsr , "jsr" , "boo" , null , BasicType.getTInt() , 0, false); + def(_ret , "ret" , "bi" , "wbii" , BasicType.getTVoid() , 0, false); + def(_tableswitch , "tableswitch" , "" , null , BasicType.getTVoid() , -1, false); // may have backward branches + def(_lookupswitch , "lookupswitch" , "" , null , BasicType.getTVoid() , -1, false); // rewriting in interpreter + def(_ireturn , "ireturn" , "b" , null , BasicType.getTInt() , -1, true ); + def(_lreturn , "lreturn" , "b" , null , BasicType.getTLong() , -2, true ); + def(_freturn , "freturn" , "b" , null , BasicType.getTFloat() , -1, true ); + def(_dreturn , "dreturn" , "b" , null , BasicType.getTDouble() , -2, true ); + def(_areturn , "areturn" , "b" , null , BasicType.getTObject() , -1, true ); + def(_return , "return" , "b" , null , BasicType.getTVoid() , 0, true ); + def(_getstatic , "getstatic" , "bjj" , null , BasicType.getTIllegal(), 1, true ); + def(_putstatic , "putstatic" , "bjj" , null , BasicType.getTIllegal(), -1, true ); + def(_getfield , "getfield" , "bjj" , null , BasicType.getTIllegal(), 0, true ); + def(_putfield , "putfield" , "bjj" , null , BasicType.getTIllegal(), -2, true ); + def(_invokevirtual , "invokevirtual" , "bjj" , null , BasicType.getTIllegal(), -1, true ); + def(_invokespecial , "invokespecial" , "bjj" , null , BasicType.getTIllegal(), -1, true ); + def(_invokestatic , "invokestatic" , "bjj" , null , BasicType.getTIllegal(), 0, true ); + def(_invokeinterface , "invokeinterface" , "bjj__", null , BasicType.getTIllegal(), -1, true ); + def(_xxxunusedxxx , "xxxunusedxxx" , null , null , BasicType.getTVoid() , 0, false); + def(_new , "new" , "bii" , null , BasicType.getTObject() , 1, true ); + def(_newarray , "newarray" , "bc" , null , BasicType.getTObject() , 0, true ); + def(_anewarray , "anewarray" , "bii" , null , BasicType.getTObject() , 0, true ); + def(_arraylength , "arraylength" , "b" , null , BasicType.getTVoid() , 0, true ); + def(_athrow , "athrow" , "b" , null , BasicType.getTVoid() , -1, true ); + def(_checkcast , "checkcast" , "bii" , null , BasicType.getTObject() , 0, true ); + def(_instanceof , "instanceof" , "bii" , null , BasicType.getTInt() , 0, true ); + def(_monitorenter , "monitorenter" , "b" , null , BasicType.getTVoid() , -1, true ); + def(_monitorexit , "monitorexit" , "b" , null , BasicType.getTVoid() , -1, true ); + def(_wide , "wide" , "" , null , BasicType.getTVoid() , 0, false); + def(_multianewarray , "multianewarray" , "biic" , null , BasicType.getTObject() , 1, true ); + def(_ifnull , "ifnull" , "boo" , null , BasicType.getTVoid() , -1, false); + def(_ifnonnull , "ifnonnull" , "boo" , null , BasicType.getTVoid() , -1, false); + def(_goto_w , "goto_w" , "boooo", null , BasicType.getTVoid() , 0, false); + def(_jsr_w , "jsr_w" , "boooo", null , BasicType.getTInt() , 0, false); + def(_breakpoint , "breakpoint" , "" , null , BasicType.getTVoid() , 0, true ); + + // JVM bytecodes + // bytecode bytecode name format wide f. result tp stk traps std code + def(_fast_agetfield , "fast_agetfield" , "bjj" , null , BasicType.getTObject() , 0, true , _getfield ); + def(_fast_bgetfield , "fast_bgetfield" , "bjj" , null , BasicType.getTInt() , 0, true , _getfield ); + def(_fast_cgetfield , "fast_cgetfield" , "bjj" , null , BasicType.getTChar() , 0, true , _getfield ); + def(_fast_dgetfield , "fast_dgetfield" , "bjj" , null , BasicType.getTDouble() , 0, true , _getfield ); + def(_fast_fgetfield , "fast_fgetfield" , "bjj" , null , BasicType.getTFloat() , 0, true , _getfield ); + def(_fast_igetfield , "fast_igetfield" , "bjj" , null , BasicType.getTInt() , 0, true , _getfield ); + def(_fast_lgetfield , "fast_lgetfield" , "bjj" , null , BasicType.getTLong() , 0, true , _getfield ); + def(_fast_sgetfield , "fast_sgetfield" , "bjj" , null , BasicType.getTShort() , 0, true , _getfield ); + + def(_fast_aputfield , "fast_aputfield" , "bjj" , null , BasicType.getTObject() , 0, true , _putfield ); + def(_fast_bputfield , "fast_bputfield" , "bjj" , null , BasicType.getTInt() , 0, true , _putfield ); + def(_fast_cputfield , "fast_cputfield" , "bjj" , null , BasicType.getTChar() , 0, true , _putfield ); + def(_fast_dputfield , "fast_dputfield" , "bjj" , null , BasicType.getTDouble() , 0, true , _putfield ); + def(_fast_fputfield , "fast_fputfield" , "bjj" , null , BasicType.getTFloat() , 0, true , _putfield ); + def(_fast_iputfield , "fast_iputfield" , "bjj" , null , BasicType.getTInt() , 0, true , _putfield ); + def(_fast_lputfield , "fast_lputfield" , "bjj" , null , BasicType.getTLong() , 0, true , _putfield ); + def(_fast_sputfield , "fast_sputfield" , "bjj" , null , BasicType.getTShort() , 0, true , _putfield ); + + def(_fast_aload_0 , "fast_aload_0" , "b" , null , BasicType.getTObject() , 1, true , _aload_0 ); + def(_fast_iaccess_0 , "fast_iaccess_0" , "b_jj" , null , BasicType.getTInt() , 1, true , _aload_0 ); + def(_fast_aaccess_0 , "fast_aaccess_0" , "b_jj" , null , BasicType.getTObject() , 1, true , _aload_0 ); + def(_fast_faccess_0 , "fast_faccess_0" , "b_jj" , null , BasicType.getTObject() , 1, true , _aload_0 ); + + def(_fast_iload , "fast_iload" , "bi" , null , BasicType.getTInt() , 1, false, _iload); + def(_fast_iload2 , "fast_iload2" , "bi_i" , null , BasicType.getTInt() , 2, false, _iload); + def(_fast_icaload , "fast_icaload" , "bi_" , null , BasicType.getTInt() , 0, false, _iload); + + // Faster method invocation. + def(_fast_invokevfinal , "fast_invokevfinal" , "bjj" , null , BasicType.getTIllegal(), -1, true, _invokevirtual); + + def(_fast_linearswitch , "fast_linearswitch" , "" , null , BasicType.getTVoid() , -1, false, _lookupswitch ); + def(_fast_binaryswitch , "fast_binaryswitch" , "" , null , BasicType.getTVoid() , -1, false, _lookupswitch ); + def(_shouldnotreachhere , "_shouldnotreachhere" , "b" , null , BasicType.getTVoid() , 0, false); + + if (Assert.ASSERTS_ENABLED) { + // compare can_trap information for each bytecode with the + // can_trap information for the corresponding base bytecode + // (if a rewritten bytecode can trap, so must the base bytecode) + for (int i = 0; i < number_of_codes; i++) { + if (isDefined(i)) { + int j = javaCode(i); + if (canTrap(i) && !canTrap(j)) { + Assert.that(false, name(i) + " can trap => " + name(j) + " can trap, too"); + } + } + } + } + } + + private static void def(int code, String name, String format, String wide_format, int result_type, int depth, boolean can_trap) { + def(code, name, format, wide_format, result_type, depth, can_trap, code); + } + + private static void def(int code, String name, String format, String wide_format, int result_type, int depth, boolean can_trap, int java_code) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(wide_format == null || format != null, "short form must exist if there's a wide form"); + } + _name [code] = name; + _format [code] = format; + _wide_format [code] = wide_format; + _result_type [code] = result_type; + _depth [code] = (byte) depth; + _can_trap [code] = can_trap; + _length [code] = (byte) (format != null ? format.length() : 0); + _java_code [code] = java_code; + if (java_code != code) { + _can_rewrite[java_code] = true; + } else { + _can_rewrite[java_code] = false; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/Interpreter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/Interpreter.java new file mode 100644 index 00000000000..1d22899d6dd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/Interpreter.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import java.util.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class Interpreter { + private static AddressField codeField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("AbstractInterpreter"); + + codeField = type.getAddressField("_code"); + } + + public Interpreter() { + } + + public StubQueue getCode() { + Address code = codeField.getValue(); + if (code == null) return null; + return new StubQueue(code, InterpreterCodelet.class); + } + + public boolean contains(Address pc) { + return getCode().contains(pc); + } + + /** Debugging/printing */ + public InterpreterCodelet getCodeletContaining(Address pc) { + return (InterpreterCodelet) getCode().getStubContaining(pc); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/InterpreterCodelet.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/InterpreterCodelet.java new file mode 100644 index 00000000000..40cbaaca889 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/InterpreterCodelet.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** An InterpreterCodelet is a piece of interpreter code. All + interpreter code is generated into little codelets which contain + extra information for debugging and printing purposes. */ + +public class InterpreterCodelet extends Stub { + private static long instanceSize; + private static CIntegerField sizeField; // the size in bytes + private static AddressField descriptionField; // a description of the codelet, for debugging & printing + private static CIntegerField bytecodeField; // associated bytecode if any + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("InterpreterCodelet"); + + sizeField = type.getCIntegerField("_size"); + descriptionField = type.getAddressField("_description"); + bytecodeField = type.getCIntegerField("_bytecode"); + + instanceSize = type.getSize(); + } + + public InterpreterCodelet(Address addr) { + super(addr); + } + + public long getSize() { + return sizeField.getValue(addr); + } + + public Address codeBegin() { + return addr.addOffsetTo(instanceSize); + } + + public Address codeEnd() { + return addr.addOffsetTo(getSize()); + } + + public long codeSize() { + return codeEnd().minus(codeBegin()); + } + + public String getDescription() { + return CStringUtilities.getString(descriptionField.getValue(addr)); + } + + public void verify() { + } + + public void printOn(PrintStream tty) { + String desc = getDescription(); + if (desc != null) { + tty.print(desc); + } + // FIXME: add printing of bytecode + tty.println(" [" + codeBegin() + ", " + codeEnd() + ") " + + codeSize() + " bytes "); + // FIXME: add disassembly + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/LookupswitchPair.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/LookupswitchPair.java new file mode 100644 index 00000000000..a24ef3df864 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/LookupswitchPair.java @@ -0,0 +1,41 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; + +public class LookupswitchPair extends Bytecode { + LookupswitchPair(Method method, int bci) { + super(method, bci); + } + + public int match() { + return javaSignedWordAt(0 * jintSize); + } + + public int offset() { + return javaSignedWordAt(1 * jintSize); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/MaskFillerForNative.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/MaskFillerForNative.java new file mode 100644 index 00000000000..f5e73138c22 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/MaskFillerForNative.java @@ -0,0 +1,55 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +/** Helper class for computing oop maps for native methods */ + +class MaskFillerForNative extends NativeSignatureIterator { + MaskFillerForNative(Method method, BitMap mask, int maskSize) { + super(method); + this.mask = mask; + this.size = maskSize; + } + + public void passInt() { /* ignore */ } + public void passLong() { /* ignore */ } + public void passFloat() { /* ignore */ } + public void passDouble() { /* ignore */ } + public void passObject() { mask.atPut(offset(), true); } + + public void generate() { + super.iterate(); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + private BitMap mask; + private int size; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/OffsetClosure.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/OffsetClosure.java new file mode 100644 index 00000000000..1f8d6132a1e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/OffsetClosure.java @@ -0,0 +1,29 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +public interface OffsetClosure { + public void offsetDo(int offset); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/OopMapCacheEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/OopMapCacheEntry.java new file mode 100644 index 00000000000..2587648493b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/OopMapCacheEntry.java @@ -0,0 +1,156 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class OopMapCacheEntry { + // Iteration + public boolean isValue(int offset) { return !entryAt(offset); } + public boolean isOop (int offset) { return entryAt(offset); } + public void iterateOop(OffsetClosure oopClosure) { + int n = numberOfEntries(); + for (int i = 0; i < n; i++) { + if (entryAt(i)) { + oopClosure.offsetDo(i); + } + } + } + + // Initialization + public void fill(Method method, int bci) { + this.method = method; + this.bci = bci; + if (method.isNative()) { + // Native method activations have oops only among the parameters and one + // extra oop following the parameters (the mirror for static native methods). + fillForNative(); + } else { + OopMapForCacheEntry gen = new OopMapForCacheEntry(method, bci, this); + gen.computeMap(); + } + } + + public void setMask(CellTypeStateList vars, + CellTypeStateList stack, + int stackTop) { + // compute bit mask size + int maxLocals = (int) method.getMaxLocals(); + int nEntries = maxLocals + stackTop; + maskSize = nEntries; + allocateBitMask(); + + CellTypeStateList curList = vars; + int listIdx = 0; + + for (int entryIdx = 0; entryIdx < nEntries; entryIdx++, listIdx++) { + // switch to stack when done with locals + if (entryIdx == maxLocals) { + curList = stack; + listIdx = 0; + } + + CellTypeState cell = curList.get(listIdx); + // set oop bit + if ( cell.isReference()) { + mask.atPut(entryIdx, true); + } + } + + // verify bit mask + if (Assert.ASSERTS_ENABLED) { + Assert.that(verifyMask(vars, stack, maxLocals, stackTop), "mask could not be verified"); + } + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + private Method method; // the method for which the mask is valid + private int bci; // the bci for which the mask is valid + private int maskSize; // the required mask size in bits + private BitMap mask; // may be null if mask is empty + + Method method() { return method; } + int bci() { return bci; } + int numberOfEntries() { return maskSize; } + boolean entryAt(int offset) { + return mask.at(offset); + } + + void setEmptyMask() { mask = null; } + void allocateBitMask() { + if (maskSize > 0) { + mask = new BitMap(maskSize); + } + } + + // fills the bit mask for native calls + void fillForNative() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(method.isNative(), "method must be native method"); + } + maskSize = (int) method.getSizeOfParameters(); + allocateBitMask(); + // fill mask for parameters + MaskFillerForNative mf = new MaskFillerForNative(method, mask, maskSize); + mf.generate(); + } + + static class VerifyClosure implements OffsetClosure { + private OopMapCacheEntry entry; + private boolean failed; + + VerifyClosure(OopMapCacheEntry entry) { this.entry = entry; } + public void offsetDo(int offset) { if (!entry.isOop(offset)) failed = true; } + boolean failed() { return failed; } + } + + boolean verifyMask(CellTypeStateList vars, CellTypeStateList stack, int maxLocals, int stackTop) { + // Check mask includes map + VerifyClosure blk = new VerifyClosure(this); + iterateOop(blk); + if (blk.failed()) return false; + + // Check if map is generated correctly + for(int i = 0; i < maxLocals; i++) { + boolean v1 = isOop(i); + boolean v2 = vars.get(i).isReference(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(v1 == v2, "locals oop mask generation error"); + } + } + + for(int j = 0; j < stackTop; j++) { + boolean v1 = isOop(maxLocals + j); + boolean v2 = stack.get(j).isReference(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(v1 == v2, "stack oop mask generation error"); + } + } + return true; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/OopMapForCacheEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/OopMapForCacheEntry.java new file mode 100644 index 00000000000..51d9f6b62c4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/OopMapForCacheEntry.java @@ -0,0 +1,91 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.interpreter; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +class OopMapForCacheEntry extends GenerateOopMap { + private OopMapCacheEntry entry; + private int bci; + private int stackTop; + + OopMapForCacheEntry(Method method, int bci, OopMapCacheEntry entry) { + super(method); + this.entry = entry; + this.bci = bci; + this.stackTop = -1; + } + + public boolean reportResults() { return false; } + + public boolean possibleGCPoint(BytecodeStream bcs) { + return false; // We are not reporting any result. We call resultForBasicblock directly + } + + public void fillStackmapProlog(int nof_gc_points) { + // Do nothing + } + + public void fillStackmapEpilog() { + // Do nothing + } + + public void fillStackmapForOpcodes(BytecodeStream bcs, + CellTypeStateList vars, + CellTypeStateList stack, + int stackTop) { + // Only interested in one specific bci + if (bcs.bci() == bci) { + entry.setMask(vars, stack, stackTop); + this.stackTop = stackTop; + } + } + + public void fillInitVars(List/**/ initVars) { + // Do nothing + } + + public void computeMap() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!method().isNative(), "cannot compute oop map for native methods"); + } + // First check if it is a method where the stackmap is always empty + if (method().getCodeSize() == 0 || method().getMaxLocals() + method().getMaxStack() == 0) { + entry.setEmptyMask(); + } else { + super.computeMap(); + resultForBasicblock(bci); + } + } + + public int size() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(stackTop != -1, "computeMap must be called first"); + } + return (int) ((method().isStatic() ? 0 : 1) + method().getMaxLocals() + stackTop); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ArrayReferenceImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ArrayReferenceImpl.java new file mode 100644 index 00000000000..3c3d1f1dcf0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ArrayReferenceImpl.java @@ -0,0 +1,171 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +import sun.jvm.hotspot.oops.Instance; +import sun.jvm.hotspot.oops.Array; +import sun.jvm.hotspot.runtime.BasicType; +import sun.jvm.hotspot.utilities.Assert; + +public class ArrayReferenceImpl extends ObjectReferenceImpl + implements ArrayReference +{ + private int length; + ArrayReferenceImpl(VirtualMachine aVm, sun.jvm.hotspot.oops.Array aRef) { + super(aVm, aRef); + length = (int) aRef.getLength(); + } + + ArrayTypeImpl arrayType() { + return (ArrayTypeImpl)type(); + } + + /** + * Return array length. + */ + public int length() { + return length; + } + + public Value getValue(int index) { + List list = getValues(index, 1); + return (Value)list.get(0); + } + + public List getValues() { + return getValues(0, -1); + } + + /** + * Validate that the range to set/get is valid. + * length of -1 (meaning rest of array) has been converted + * before entry. + */ + private void validateArrayAccess(int index, int len) { + // because length can be computed from index, + // index must be tested first for correct error message + if ((index < 0) || (index > length())) { + throw new IndexOutOfBoundsException( + "Invalid array index: " + index); + } + if (len < 0) { + throw new IndexOutOfBoundsException( + "Invalid array range length: " + len); + } + if (index + len > length()) { + throw new IndexOutOfBoundsException( + "Invalid array range: " + + index + " to " + (index + len - 1)); + } + } + + public List getValues(int index, int len) { + if (len == -1) { // -1 means the rest of the array + len = length() - index; + } + validateArrayAccess(index, len); + List vals = new ArrayList(); + if (len == 0) { + return vals; + } + + sun.jvm.hotspot.oops.TypeArray typeArray = null; + sun.jvm.hotspot.oops.ObjArray objArray = null; + if (ref() instanceof sun.jvm.hotspot.oops.TypeArray) { + typeArray = (sun.jvm.hotspot.oops.TypeArray)ref(); + } else if (ref() instanceof sun.jvm.hotspot.oops.ObjArray) { + objArray = (sun.jvm.hotspot.oops.ObjArray)ref(); + } else { + throw new RuntimeException("should not reach here"); + } + + char c = arrayType().componentSignature().charAt(0); + BasicType variableType = BasicType.charToBasicType(c); + + final int limit = index + len; + for (int ii = index; ii < limit; ii++) { + ValueImpl valueImpl; + if (variableType == BasicType.T_BOOLEAN) { + valueImpl = (BooleanValueImpl) vm.mirrorOf(typeArray.getBooleanAt(ii)); + } else if (variableType == BasicType.T_CHAR) { + valueImpl = (CharValueImpl) vm.mirrorOf(typeArray.getCharAt(ii)); + } else if (variableType == BasicType.T_FLOAT) { + valueImpl = (FloatValueImpl) vm.mirrorOf(typeArray.getFloatAt(ii)); + } else if (variableType == BasicType.T_DOUBLE) { + valueImpl = (DoubleValueImpl) vm.mirrorOf(typeArray.getDoubleAt(ii)); + } else if (variableType == BasicType.T_BYTE) { + valueImpl = (ByteValueImpl) vm.mirrorOf(typeArray.getByteAt(ii)); + } else if (variableType == BasicType.T_SHORT) { + valueImpl = (ShortValueImpl) vm.mirrorOf(typeArray.getShortAt(ii)); + } else if (variableType == BasicType.T_INT) { + valueImpl = (IntegerValueImpl) vm.mirrorOf(typeArray.getIntAt(ii)); + } else if (variableType == BasicType.T_LONG) { + valueImpl = (LongValueImpl) vm.mirrorOf(typeArray.getLongAt(ii)); + } else if (variableType == BasicType.T_OBJECT) { + // we may have an [Ljava/lang/Object; - i.e., Object[] with the + // elements themselves may be arrays because every array is an Object. + valueImpl = (ObjectReferenceImpl) vm.objectMirror(objArray.getObjAt(ii)); + } else if (variableType == BasicType.T_ARRAY) { + valueImpl = (ArrayReferenceImpl) vm.arrayMirror((Array) objArray.getObjAt(ii)); + } else { + throw new RuntimeException("should not reach here"); + } + vals.add (valueImpl); + } + return vals; + } + + public void setValue(int index, Value value) + throws InvalidTypeException, + ClassNotLoadedException { + vm.throwNotReadOnlyException("ArrayReference.setValue(...)"); + } + + public void setValues(List values) + throws InvalidTypeException, + ClassNotLoadedException { + setValues(0, values, 0, -1); + } + + public void setValues(int index, List values, + int srcIndex, int length) + throws InvalidTypeException, + ClassNotLoadedException { + + vm.throwNotReadOnlyException("ArrayReference.setValue(...)"); + + } + + public String toString() { + return "instance of " + arrayType().componentTypeName() + + "[" + length() + "] (id=" + uniqueID() + ")"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ArrayTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ArrayTypeImpl.java new file mode 100644 index 00000000000..b4468eafcd5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ArrayTypeImpl.java @@ -0,0 +1,211 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +import sun.jvm.hotspot.oops.ArrayKlass; +import sun.jvm.hotspot.oops.InstanceKlass; +import sun.jvm.hotspot.oops.ObjArrayKlass; +import sun.jvm.hotspot.oops.TypeArrayKlass; +import sun.jvm.hotspot.oops.Klass; +import sun.jvm.hotspot.oops.Instance; +import sun.jvm.hotspot.oops.Symbol; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +public class ArrayTypeImpl extends ReferenceTypeImpl implements ArrayType { + protected ArrayTypeImpl(VirtualMachine aVm, ArrayKlass aRef) { + super(aVm, aRef); + } + + public ArrayReference newInstance(int length) { + vm.throwNotReadOnlyException("ArrayType.newInstance(int)"); + return null; + } + + public String componentSignature() { + return signature().substring(1); // Just skip the leading '[' + } + + public String componentTypeName() { + JNITypeParser parser = new JNITypeParser(componentSignature()); + return parser.typeName(); + } + + public ClassLoaderReference classLoader() { + if (ref() instanceof TypeArrayKlass) { + // primitive array klasses are loaded by bootstrap loader + return null; + } else { + Klass bottomKlass = ((ObjArrayKlass)ref()).getBottomKlass(); + if (bottomKlass instanceof TypeArrayKlass) { + // multidimensional primitive array klasses are loaded by bootstrap loader + return null; + } else { + // class loader of any other obj array klass is same as the loader + // that loaded the bottom InstanceKlass + Instance xx = (Instance)(((InstanceKlass) bottomKlass).getClassLoader()); + return vm.classLoaderMirror(xx); + } + } + } + + void addVisibleMethods(Map methodMap) { + // arrays don't have methods + } + + List getAllMethods() { + // arrays don't have methods + // JLS says arrays have methods of java.lang.Object. But + // JVMDI-JDI returns zero size list. We do the same here + // for consistency. + return new ArrayList(0); + } + + /* + * Find the type object, if any, of a component type of this array. + * The component type does not have to be immediate; e.g. this method + * can be used to find the component Foo of Foo[][]. + */ + public Type componentType() throws ClassNotLoadedException { + ArrayKlass k = (ArrayKlass) ref(); + if (k instanceof ObjArrayKlass) { + Klass elementKlass = ((ObjArrayKlass)k).getElementKlass(); + if (elementKlass == null) { + throw new ClassNotLoadedException(componentSignature()); + } else { + return vm.referenceType(elementKlass); + } + } else { + // It's a primitive type + return vm.primitiveTypeMirror(signature().charAt(1)); + } + } + + static boolean isComponentAssignable(Type destination, Type source) { + if (source instanceof PrimitiveType) { + // Assignment of primitive arrays requires identical + // component types. + return source.equals(destination); + } else { + if (destination instanceof PrimitiveType) { + return false; + } + + ReferenceTypeImpl refSource = (ReferenceTypeImpl)source; + ReferenceTypeImpl refDestination = (ReferenceTypeImpl)destination; + // Assignment of object arrays requires availability + // of widening conversion of component types + return refSource.isAssignableTo(refDestination); + } + } + + + /* + * Return true if an instance of the given reference type + * can be assigned to a variable of this type + */ + boolean isAssignableTo(ReferenceType destType) { + if (destType instanceof ArrayType) { + try { + Type destComponentType = ((ArrayType)destType).componentType(); + return isComponentAssignable(destComponentType, componentType()); + } catch (ClassNotLoadedException e) { + // One or both component types has not yet been + // loaded => can't assign + return false; + } + } else { + Symbol typeName = ((ReferenceTypeImpl)destType).typeNameAsSymbol(); + if (destType instanceof InterfaceType) { + // Every array type implements java.io.Serializable and + // java.lang.Cloneable. fixme in JVMDI-JDI, includes only + // Cloneable but not Serializable. + return typeName.equals(vm.javaLangCloneable()) || + typeName.equals(vm.javaIoSerializable()); + } else { + // Only valid ClassType assignee is Object + return typeName.equals(vm.javaLangObject()); + } + } + } + + List inheritedTypes() { + // arrays are derived from java.lang.Object and + // B[] is derived from A[] if B is derived from A. + // But JVMDI-JDI returns zero sized list and we do the + // same for consistency. + return new ArrayList(0); + } + + int getModifiers() { + /* + * For object arrays, the return values for Interface + * Accessible.isPrivate(), Accessible.isProtected(), + * etc... are the same as would be returned for the + * component type. Fetch the modifier bits from the + * component type and use those. + * + * For primitive arrays, the modifiers are always + * VMModifiers.FINAL | VMModifiers.PUBLIC + * + * Reference com.sun.jdi.Accessible.java. + */ + try { + Type t = componentType(); + if (t instanceof PrimitiveType) { + return VMModifiers.FINAL | VMModifiers.PUBLIC; + } else { + ReferenceType rt = (ReferenceType)t; + return rt.modifiers(); + } + } catch (ClassNotLoadedException cnle) { + cnle.printStackTrace(); + } + return -1; + } + + public String toString() { + return "array class " + name() + " (" + loaderString() + ")"; + } + + /* + * Save a pointless trip over the wire for these methods + * which have undefined results for arrays. + */ + public boolean isPrepared() { return true; } + public boolean isVerified() { return true; } + public boolean isInitialized() { return true; } + public boolean failedToInitialize() { return false; } + public boolean isAbstract() { return false; } + + /* + * Defined always to be true for arrays + */ + public boolean isFinal() { return true; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/BaseLineInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/BaseLineInfo.java new file mode 100644 index 00000000000..22565c2390d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/BaseLineInfo.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +class BaseLineInfo implements LineInfo { + private final int lineNumber; + private final ReferenceTypeImpl declaringType; + + BaseLineInfo(int lineNumber, + ReferenceTypeImpl declaringType) { + this.lineNumber = lineNumber; + this.declaringType = declaringType; + } + + public String liStratum() { + return SDE.BASE_STRATUM_NAME; + } + + public int liLineNumber() { + return lineNumber; + } + + public String liSourceName() + throws AbsentInformationException { + return declaringType.baseSourceName(); + } + + public String liSourcePath() + throws AbsentInformationException { + return declaringType.baseSourcePath(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/BooleanTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/BooleanTypeImpl.java new file mode 100644 index 00000000000..b805c33e8be --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/BooleanTypeImpl.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class BooleanTypeImpl extends PrimitiveTypeImpl implements BooleanType { + BooleanTypeImpl(VirtualMachine vm) { + super(vm); + } + + public String signature() { + return "Z"; + } + + PrimitiveValue convert(PrimitiveValue value) throws InvalidTypeException { + return vm.mirrorOf(((PrimitiveValueImpl)value).checkedBooleanValue()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/BooleanValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/BooleanValueImpl.java new file mode 100644 index 00000000000..89695bc2100 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/BooleanValueImpl.java @@ -0,0 +1,98 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class BooleanValueImpl extends PrimitiveValueImpl + implements BooleanValue { + private boolean value; + + BooleanValueImpl(VirtualMachine aVm,boolean aValue) { + super(aVm); + + value = aValue; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof BooleanValue)) { + return (value == ((BooleanValue)obj).value()) + && super.equals(obj); + } else { + return false; + } + } + + public int hashCode() { + /* + * TO DO: Better hash code + */ + return intValue(); + } + + public Type type() { + return vm.theBooleanType(); + } + + public boolean value() { + return value; + } + + public boolean booleanValue() { + return value; + } + + public byte byteValue() { + return(byte)((value)?1:0); + } + + public char charValue() { + return(char)((value)?1:0); + } + + public short shortValue() { + return(short)((value)?1:0); + } + + public int intValue() { + return(int)((value)?1:0); + } + + public long longValue() { + return(long)((value)?1:0); + } + + public float floatValue() { + return(float)((value)?1.0:0.0); + } + + public double doubleValue() { + return(double)((value)?1.0:0.0); + } + + public String toString() { + return "" + value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ByteTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ByteTypeImpl.java new file mode 100644 index 00000000000..c662976076d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ByteTypeImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class ByteTypeImpl extends PrimitiveTypeImpl implements ByteType { + ByteTypeImpl(VirtualMachine vm) { + super(vm); + } + + + public String signature() { + return "B"; + } + + PrimitiveValue convert(PrimitiveValue value) throws InvalidTypeException { + return vm.mirrorOf(((PrimitiveValueImpl)value).checkedByteValue()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ByteValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ByteValueImpl.java new file mode 100644 index 00000000000..db33a1bc3ac --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ByteValueImpl.java @@ -0,0 +1,112 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class ByteValueImpl extends PrimitiveValueImpl + implements ByteValue { + private byte value; + + ByteValueImpl(VirtualMachine aVm,byte aValue) { + super(aVm); + + value = aValue; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof ByteValue)) { + return (value == ((ByteValue)obj).value()) + && super.equals(obj); + } else { + return false; + } + } + + public int hashCode() { + /* + * TO DO: Better hash code + */ + return intValue(); + } + + public int compareTo(Object obj) { + byte other = ((ByteValue)obj).value(); + return value() - other; + } + + + public Type type() { + return vm.theByteType(); + } + + public byte value() { + return value; + } + + public boolean booleanValue() { + return(value == 0)?false:true; + } + + public byte byteValue() { + return value; + } + + public char charValue() { + return(char)value; + } + + public short shortValue() { + return(short)value; + } + + public int intValue() { + return(int)value; + } + + public long longValue() { + return(long)value; + } + + public float floatValue() { + return(float)value; + } + + public double doubleValue() { + return(double)value; + } + + char checkedCharValue() throws InvalidTypeException { + if ((value > Character.MAX_VALUE) || (value < Character.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to char"); + } else { + return super.checkedCharValue(); + } + } + + public String toString() { + return "" + value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/CharTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/CharTypeImpl.java new file mode 100644 index 00000000000..f5b57b0e27f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/CharTypeImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class CharTypeImpl extends PrimitiveTypeImpl implements CharType { + CharTypeImpl(VirtualMachine vm) { + super(vm); + } + + + public String signature() { + return "C"; + } + + PrimitiveValue convert(PrimitiveValue value) throws InvalidTypeException { + return vm.mirrorOf(((PrimitiveValueImpl)value).checkedCharValue()); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/CharValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/CharValueImpl.java new file mode 100644 index 00000000000..1df09bf8a96 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/CharValueImpl.java @@ -0,0 +1,121 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class CharValueImpl extends PrimitiveValueImpl + implements CharValue { + private char value; + + CharValueImpl(VirtualMachine aVm,char aValue) { + super(aVm); + + value = aValue; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof CharValue)) { + return (value == ((CharValue)obj).value()) && + super.equals(obj); + } else { + return false; + } + } + + public int hashCode() { + /* + * TO DO: Better hash code + */ + return intValue(); + } + + public int compareTo(Object obj) { + char other = ((CharValue)obj).value(); + return value() - other; + } + + public Type type() { + return vm.theCharType(); + } + + public char value() { + return value; + } + + public boolean booleanValue() { + return(value == 0)?false:true; + } + + public byte byteValue() { + return(byte)value; + } + + public char charValue() { + return(char)value; + } + + public short shortValue() { + return(short)value; + } + + public int intValue() { + return(int)value; + } + + public long longValue() { + return(long)value; + } + + public float floatValue() { + return(float)value; + } + + public double doubleValue() { + return(double)value; + } + + public String toString() { + return "" + value; + } + + byte checkedByteValue() throws InvalidTypeException { + // Note: since char is unsigned, don't check against MIN_VALUE + if (value > Byte.MAX_VALUE) { + throw new InvalidTypeException("Can't convert " + value + " to byte"); + } else { + return super.checkedByteValue(); + } + } + + short checkedShortValue() throws InvalidTypeException { + // Note: since char is unsigned, don't check against MIN_VALUE + if (value > Short.MAX_VALUE) { + throw new InvalidTypeException("Can't convert " + value + " to short"); + } else { + return super.checkedShortValue(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ClassLoaderReferenceImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ClassLoaderReferenceImpl.java new file mode 100644 index 00000000000..2ea15ed2ba4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ClassLoaderReferenceImpl.java @@ -0,0 +1,134 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.Instance; +import sun.jvm.hotspot.oops.Klass; +import sun.jvm.hotspot.memory.SystemDictionary; +import sun.jvm.hotspot.memory.Universe; +import sun.jvm.hotspot.runtime.VM; + +import com.sun.jdi.*; +import java.util.*; + +public class ClassLoaderReferenceImpl + extends ObjectReferenceImpl + implements ClassLoaderReference +{ + // because we work on process snapshot or core we can + // cache visibleClasses & definedClasses always (i.e., no suspension) + private List visibleClassesCache; + private List definedClassesCache; + + ClassLoaderReferenceImpl(VirtualMachine aVm, Instance oRef) { + super(aVm, oRef); + } + + protected String description() { + return "ClassLoaderReference " + uniqueID(); + } + + public List definedClasses() { + if (definedClassesCache == null) { + definedClassesCache = new ArrayList(); + Iterator iter = vm.allClasses().iterator(); + while (iter.hasNext()) { + ReferenceType type = (ReferenceType)iter.next(); + if (equals(type.classLoader())) { /* thanks OTI */ + definedClassesCache.add(type); + } + } + } + return definedClassesCache; + } + + private SystemDictionary getSystemDictionary() { + return vm.saSystemDictionary(); + } + + private Universe getUniverse() { + return vm.saUniverse(); + } + + public List visibleClasses() { + if (visibleClassesCache != null) + return visibleClassesCache; + + visibleClassesCache = new ArrayList(); + + // refer to getClassLoaderClasses in jvmtiGetLoadedClasses.cpp + // a. SystemDictionary::classes_do doesn't include arrays of primitive types (any dimensions) + SystemDictionary sysDict = getSystemDictionary(); + sysDict.classesDo( + new SystemDictionary.ClassAndLoaderVisitor() { + public void visit(Klass k, Oop loader) { + if (ref().equals(loader)) { + for (Klass l = k; l != null; l = l.arrayKlassOrNull()) { + visibleClassesCache.add(vm.referenceType(l)); + } + } + } + } + ); + + // b. multi dimensional arrays of primitive types + sysDict.primArrayClassesDo( + new SystemDictionary.ClassAndLoaderVisitor() { + public void visit(Klass k, Oop loader) { + if (ref().equals(loader)) { + visibleClassesCache.add(vm.referenceType(k)); + } + } + } + ); + + // c. single dimensional primitive array klasses from Universe + // these are not added to SystemDictionary + getUniverse().basicTypeClassesDo( + new SystemDictionary.ClassVisitor() { + public void visit(Klass k) { + visibleClassesCache.add(vm.referenceType(k)); + } + } + ); + + return visibleClassesCache; + } + + Type findType(String signature) throws ClassNotLoadedException { + List types = visibleClasses(); + Iterator iter = types.iterator(); + while (iter.hasNext()) { + ReferenceType type = (ReferenceType)iter.next(); + if (type.signature().equals(signature)) { + return type; + } + } + JNITypeParser parser = new JNITypeParser(signature); + throw new ClassNotLoadedException(parser.typeName(), + "Class " + parser.typeName() + " not loaded"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ClassObjectReferenceImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ClassObjectReferenceImpl.java new file mode 100644 index 00000000000..2a64c65fabf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ClassObjectReferenceImpl.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; +import sun.jvm.hotspot.oops.Instance; +import sun.jvm.hotspot.oops.Klass; +import sun.jvm.hotspot.oops.OopUtilities; + +public class ClassObjectReferenceImpl extends ObjectReferenceImpl + implements ClassObjectReference { + private ReferenceType reflectedType; + + ClassObjectReferenceImpl(VirtualMachine vm, Instance oRef) { + super(vm, oRef); + } + + public ReferenceType reflectedType() { + if (reflectedType == null) { + Klass k = OopUtilities.classOopToKlass(ref()); + reflectedType = vm.referenceType(k); + } + return reflectedType; + } + + public String toString() { + return "instance of " + referenceType().name() + + "(reflected class=" + reflectedType().name() + ", " + "id=" + + uniqueID() + ")"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ClassTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ClassTypeImpl.java new file mode 100644 index 00000000000..6feaa3eb884 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ClassTypeImpl.java @@ -0,0 +1,241 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; +import sun.jvm.hotspot.oops.Klass; +import sun.jvm.hotspot.oops.InstanceKlass; + +import java.util.*; +import java.lang.ref.SoftReference; + +public class ClassTypeImpl extends ReferenceTypeImpl + implements ClassType +{ + private SoftReference interfacesCache = null; + private SoftReference allInterfacesCache = null; + private SoftReference subclassesCache = null; + + protected ClassTypeImpl(VirtualMachine aVm, InstanceKlass aRef) { + super(aVm, aRef); + } + + public ClassType superclass() { + InstanceKlass kk = (InstanceKlass)ref().getSuper(); + if (kk == null) { + return null; + } + return (ClassType) vm.referenceType(kk); + } + + public List interfaces() { + List interfaces = (interfacesCache != null)? (List) interfacesCache.get() : null; + if (interfaces == null) { + checkPrepared(); + interfaces = Collections.unmodifiableList(getInterfaces()); + interfacesCache = new SoftReference(interfaces); + } + return interfaces; + } + + void addInterfaces(List list) { + List immediate = interfaces(); + + HashSet hashList = new HashSet(list); + hashList.addAll(immediate); + list.clear(); + list.addAll(hashList); + + Iterator iter = immediate.iterator(); + while (iter.hasNext()) { + InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next(); + interfaze.addSuperinterfaces(list); + } + + ClassTypeImpl superclass = (ClassTypeImpl)superclass(); + if (superclass != null) { + superclass.addInterfaces(list); + } + } + + public List allInterfaces() { + List allinterfaces = (allInterfacesCache != null)? (List) allInterfacesCache.get() : null; + if (allinterfaces == null) { + checkPrepared(); + allinterfaces = new ArrayList(); + addInterfaces(allinterfaces); + allinterfaces = Collections.unmodifiableList(allinterfaces); + allInterfacesCache = new SoftReference(allinterfaces); + } + return allinterfaces; + } + + public List subclasses() { + List subclasses = (subclassesCache != null)? (List) subclassesCache.get() : null; + if (subclasses == null) { + List all = vm.allClasses(); + subclasses = new ArrayList(0); + Iterator iter = all.iterator(); + while (iter.hasNext()) { + ReferenceType refType = (ReferenceType)iter.next(); + if (refType instanceof ClassType) { + ClassType clazz = (ClassType)refType; + ClassType superclass = clazz.superclass(); + if ((superclass != null) && superclass.equals(this)) { + subclasses.add(refType); + } + } + } + subclasses = Collections.unmodifiableList(subclasses); + subclassesCache = new SoftReference(subclasses); + } + return subclasses; + } + + public Method concreteMethodByName(String name, String signature) { + checkPrepared(); + List methods = visibleMethods(); + Method method = null; + Iterator iter = methods.iterator(); + while (iter.hasNext()) { + Method candidate = (Method)iter.next(); + if (candidate.name().equals(name) && + candidate.signature().equals(signature) && + !candidate.isAbstract()) { + + method = candidate; + break; + } + } + return method; + } + + List getAllMethods() { + ArrayList list = new ArrayList(methods()); + ClassType clazz = superclass(); + while (clazz != null) { + list.addAll(clazz.methods()); + clazz = clazz.superclass(); + } + /* + * Avoid duplicate checking on each method by iterating through + * duplicate-free allInterfaces() rather than recursing + */ + Iterator iter = allInterfaces().iterator(); + while (iter.hasNext()) { + InterfaceType interfaze = (InterfaceType)iter.next(); + list.addAll(interfaze.methods()); + } + return list; + } + + List inheritedTypes() { + List inherited = new ArrayList(interfaces()); + if (superclass() != null) { + inherited.add(0, superclass()); /* insert at front */ + } + return inherited; + } + + public boolean isEnum() { + ClassTypeImpl superclass = (ClassTypeImpl) superclass(); + if (superclass != null) { + return superclass.typeNameAsSymbol().equals(vm.javaLangEnum()); + } else { + return false; + } + } + + public void setValue(Field field, Value value) + throws InvalidTypeException, ClassNotLoadedException { + vm.throwNotReadOnlyException("ClassType.setValue(...)"); + } + + + public Value invokeMethod(ThreadReference threadIntf, Method methodIntf, + List arguments, int options) + throws InvalidTypeException, + ClassNotLoadedException, + IncompatibleThreadStateException, + InvocationException { + vm.throwNotReadOnlyException("ClassType.invokeMethod(...)"); + return null; + } + + public ObjectReference newInstance(ThreadReference threadIntf, + Method methodIntf, + List arguments, int options) + throws InvalidTypeException, + ClassNotLoadedException, + IncompatibleThreadStateException, + InvocationException { + vm.throwNotReadOnlyException("ClassType.newInstance(...)"); + return null; + } + + void addVisibleMethods(Map methodMap) { + /* + * Add methods from + * parent types first, so that the methods in this class will + * overwrite them in the hash table + */ + + Iterator iter = interfaces().iterator(); + while (iter.hasNext()) { + InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next(); + interfaze.addVisibleMethods(methodMap); + } + + ClassTypeImpl clazz = (ClassTypeImpl)superclass(); + if (clazz != null) { + clazz.addVisibleMethods(methodMap); + } + + addToMethodMap(methodMap, methods()); + } + + boolean isAssignableTo(ReferenceType type) { + ClassTypeImpl superclazz = (ClassTypeImpl)superclass(); + if (this.equals(type)) { + return true; + } else if ((superclazz != null) && superclazz.isAssignableTo(type)) { + return true; + } else { + List interfaces = interfaces(); + Iterator iter = interfaces.iterator(); + while (iter.hasNext()) { + InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next(); + if (interfaze.isAssignableTo(type)) { + return true; + } + } + return false; + } + } + + public String toString() { + return "class " + name() + "(" + loaderString() + ")"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ConcreteMethodImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ConcreteMethodImpl.java new file mode 100644 index 00000000000..d6dcb0d31ff --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ConcreteMethodImpl.java @@ -0,0 +1,467 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; +import sun.jvm.hotspot.oops.Symbol; +import sun.jvm.hotspot.oops.LocalVariableTableElement; +import sun.jvm.hotspot.oops.LineNumberTableElement; +import java.util.List; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Comparator; +import java.lang.ref.SoftReference; +import java.util.Collections; + +public class ConcreteMethodImpl extends MethodImpl { + + /* + * A subset of the line number info that is softly cached + */ + static private class SoftLocationXRefs { + final String stratumID; // The stratum of this information + final Map lineMapper; // Maps line number to location(s) + final List lineLocations; // List of locations ordered by code index + + /* + * Note: these do not necessarily correspond to + * the line numbers of the first and last elements + * in the lineLocations list. Use these only for bounds + * checking and with lineMapper. + */ + final int lowestLine; + final int highestLine; + + SoftLocationXRefs(String stratumID, Map lineMapper, List lineLocations, + int lowestLine, int highestLine) { + this.stratumID = stratumID; + this.lineMapper = Collections.unmodifiableMap(lineMapper); + this.lineLocations = + Collections.unmodifiableList(lineLocations); + this.lowestLine = lowestLine; + this.highestLine = highestLine; + } + } + + private SoftReference softBaseLocationXRefsRef; + private SoftReference softOtherLocationXRefsRef; + private SoftReference variablesRef = null; + private int firstIndex = -1; + private int lastIndex = -1; + private Location location; + private SoftReference bytecodesRef = null; + + ConcreteMethodImpl(VirtualMachine vm, ReferenceTypeImpl declaringType, + sun.jvm.hotspot.oops.Method saMethod ) { + super(vm, declaringType, saMethod); + } + + int argSlotCount() throws AbsentInformationException { + return (int) saMethod.getSizeOfParameters(); + } + + private SoftLocationXRefs getLocations(SDE.Stratum stratum) { + if (stratum.isJava()) { + return getBaseLocations(); + } + String stratumID = stratum.id(); + SoftLocationXRefs info = + (softOtherLocationXRefsRef == null) ? null : + (SoftLocationXRefs)softOtherLocationXRefsRef.get(); + if (info != null && info.stratumID.equals(stratumID)) { + return info; + } + + List lineLocations = new ArrayList(); + Map lineMapper = new HashMap(); + int lowestLine = -1; + int highestLine = -1; + SDE.LineStratum lastLineStratum = null; + SDE.Stratum baseStratum = + declaringType.stratum(SDE.BASE_STRATUM_NAME); + Iterator it = getBaseLocations().lineLocations.iterator(); + while(it.hasNext()) { + LocationImpl loc = (LocationImpl)it.next(); + int baseLineNumber = loc.lineNumber(baseStratum); + SDE.LineStratum lineStratum = + stratum.lineStratum(declaringType, + baseLineNumber); + + if (lineStratum == null) { + // location not mapped in this stratum + continue; + } + + int lineNumber = lineStratum.lineNumber(); + + // remove unmapped and dup lines + if ((lineNumber != -1) && + (!lineStratum.equals(lastLineStratum))) { + lastLineStratum = lineStratum; + // Remember the largest/smallest line number + if (lineNumber > highestLine) { + highestLine = lineNumber; + } + if ((lineNumber < lowestLine) || (lowestLine == -1)) { + lowestLine = lineNumber; + } + + loc.addStratumLineInfo( + new StratumLineInfo(stratumID, + lineNumber, + lineStratum.sourceName(), + lineStratum.sourcePath())); + + // Add to the location list + lineLocations.add(loc); + + // Add to the line -> locations map + Integer key = new Integer(lineNumber); + List mappedLocs = (List)lineMapper.get(key); + if (mappedLocs == null) { + mappedLocs = new ArrayList(1); + lineMapper.put(key, mappedLocs); + } + mappedLocs.add(loc); + } + } + + info = new SoftLocationXRefs(stratumID, + lineMapper, lineLocations, + lowestLine, highestLine); + softOtherLocationXRefsRef = new SoftReference(info); + return info; + } + + private SoftLocationXRefs getBaseLocations() { + SoftLocationXRefs info = (softBaseLocationXRefsRef == null) ? null : + (SoftLocationXRefs)softBaseLocationXRefsRef.get(); + if (info != null) { + return info; + } + + byte[] codeBuf = bytecodes(); + firstIndex = 0; + lastIndex = codeBuf.length - 1; + // This is odd; what is the Location of a Method? + // A StackFrame can have a location, but a Method? + // I guess it must be the Location for bci 0. + location = new LocationImpl(virtualMachine(), this, 0); + + boolean hasLineInfo = saMethod.hasLineNumberTable(); + LineNumberTableElement[] lntab = null; + int count; + + if (hasLineInfo) { + lntab = saMethod.getLineNumberTable(); + count = lntab.length; + } else { + count = 0; + } + + List lineLocations = new ArrayList(count); + Map lineMapper = new HashMap(); + int lowestLine = -1; + int highestLine = -1; + for (int i = 0; i < count; i++) { + long bci = lntab[i].getStartBCI(); + int lineNumber = lntab[i].getLineNumber(); + + /* + * Some compilers will point multiple consecutive + * lines at the same location. We need to choose + * one of them so that we can consistently map back + * and forth between line and location. So we choose + * to record only the last line entry at a particular + * location. + */ + if ((i + 1 == count) || (bci != lntab[i+1].getStartBCI())) { + // Remember the largest/smallest line number + if (lineNumber > highestLine) { + highestLine = lineNumber; + } + if ((lineNumber < lowestLine) || (lowestLine == -1)) { + lowestLine = lineNumber; + } + LocationImpl loc = + new LocationImpl(virtualMachine(), this, bci); + loc.addBaseLineInfo( + new BaseLineInfo(lineNumber, declaringType)); + + // Add to the location list + lineLocations.add(loc); + + // Add to the line -> locations map + Integer key = new Integer(lineNumber); + List mappedLocs = (List)lineMapper.get(key); + if (mappedLocs == null) { + mappedLocs = new ArrayList(1); + lineMapper.put(key, mappedLocs); + } + mappedLocs.add(loc); + } + } + + info = new SoftLocationXRefs(SDE.BASE_STRATUM_NAME, + lineMapper, lineLocations, + lowestLine, highestLine); + softBaseLocationXRefsRef = new SoftReference(info); + return info; + } + + List sourceNameFilter(List list, + SDE.Stratum stratum, + String sourceName) + throws AbsentInformationException { + if (sourceName == null) { + return list; + } else { + /* needs sourceName filteration */ + List locs = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) { + LocationImpl loc = (LocationImpl)it.next(); + if (loc.sourceName(stratum).equals(sourceName)) { + locs.add(loc); + } + } + return locs; + } + } + + public List allLineLocations(SDE.Stratum stratum, String sourceName) + throws AbsentInformationException { + List lineLocations = getLocations(stratum).lineLocations; + + if (lineLocations.size() == 0) { + throw new AbsentInformationException(); + } + + return Collections.unmodifiableList( + sourceNameFilter(lineLocations, stratum, sourceName)); + } + + public List locationsOfLine(SDE.Stratum stratum, String sourceName, + int lineNumber) throws AbsentInformationException { + SoftLocationXRefs info = getLocations(stratum); + + if (info.lineLocations.size() == 0) { + throw new AbsentInformationException(); + } + + /* + * Find the locations which match the line number + * passed in. + */ + List list = (List)info.lineMapper.get( + new Integer(lineNumber)); + + if (list == null) { + list = new ArrayList(0); + } + return Collections.unmodifiableList( + sourceNameFilter(list, stratum, sourceName)); + } + + LineInfo codeIndexToLineInfo(SDE.Stratum stratum, + long codeIndex) { + if (firstIndex == -1) { + getBaseLocations(); + } + + /* + * Check for invalid code index. + */ + if (codeIndex < firstIndex || codeIndex > lastIndex) { + throw new InternalError( + "Location with invalid code index"); + } + + List lineLocations = getLocations(stratum).lineLocations; + + /* + * Check for absent line numbers. + */ + if (lineLocations.size() == 0) { + return super.codeIndexToLineInfo(stratum, codeIndex); + } + + Iterator iter = lineLocations.iterator(); + /* + * Treat code before the beginning of the first line table + * entry as part of the first line. javac will generate + * code like this for some local classes. This "prolog" + * code contains assignments from locals in the enclosing + * scope to synthetic fields in the local class. Same for + * other language prolog code. + */ + LocationImpl bestMatch = (LocationImpl)iter.next(); + while (iter.hasNext()) { + LocationImpl current = (LocationImpl)iter.next(); + if (current.codeIndex() > codeIndex) { + break; + } + bestMatch = current; + } + return bestMatch.getLineInfo(stratum); + } + + public Location locationOfCodeIndex(long codeIndex) { + if (firstIndex == -1) { + getBaseLocations(); + } + + /* + * Check for invalid code index. + */ + if (codeIndex < firstIndex || codeIndex > lastIndex) { + return null; + } + + return new LocationImpl(virtualMachine(), this, codeIndex); + } + + public List variables() throws AbsentInformationException { + return getVariables(); + } + + public List variablesByName(String name) throws AbsentInformationException { + List variables = getVariables(); + + List retList = new ArrayList(2); + Iterator iter = variables.iterator(); + while(iter.hasNext()) { + LocalVariable variable = (LocalVariable)iter.next(); + if (variable.name().equals(name)) { + retList.add(variable); + } + } + return retList; + } + + public List arguments() throws AbsentInformationException { + if (argumentTypeNames().size() == 0) { + return new ArrayList(0); + } + List variables = getVariables(); + List retList = new ArrayList(variables.size()); + Iterator iter = variables.iterator(); + while(iter.hasNext()) { + LocalVariable variable = (LocalVariable)iter.next(); + if (variable.isArgument()) { + retList.add(variable); + } + } + return retList; + } + + public byte[] bytecodes() { + byte[] bytecodes = (bytecodesRef == null) ? null : + (byte[])bytecodesRef.get(); + if (bytecodes == null) { + bytecodes = saMethod.getByteCode(); + bytecodesRef = new SoftReference(bytecodes); + } + /* + * Arrays are always modifiable, so it is a little unsafe + * to return the cached bytecodes directly; instead, we + * make a clone at the cost of using more memory. + */ + return (byte[])bytecodes.clone(); + } + + public Location location() { + if (location == null) { + getBaseLocations(); + } + return location; + } + + private List getVariables() throws AbsentInformationException { + List variables = (variablesRef == null) ? null : + (List)variablesRef.get(); + if (variables != null) { + return variables; + } + + // if there are no locals, there won't be a LVT + if (saMethod.getMaxLocals() == 0) { + variables = Collections.unmodifiableList(new ArrayList(0)); + variablesRef = new SoftReference(variables); + return variables; + } + + if (! saMethod.hasLocalVariableTable()) { + throw new AbsentInformationException(); + } + //Build up the JDI view of local variable table. + LocalVariableTableElement[] locals = saMethod.getLocalVariableTable(); + int localCount = locals.length; + variables = new ArrayList(localCount); + for (int ii = 0; ii < localCount; ii++) { + String name = + saMethod.getConstants().getSymbolAt(locals[ii].getNameCPIndex()).asString(); + /* + * Skip "this$*", "this+*", "this" entries because they are never real + * variables from the JLS perspective. "this+*" is new with 1.5. + * Instead of using '+', we check for java letter or digit to avoid + * depending on javac's current choice of '+'. + */ + boolean isInternalName = name.startsWith("this") && + (name.length() == 4 || name.charAt(4)=='$' || !Character.isJavaIdentifierPart(name.charAt(4))); + if (! isInternalName) { + int slot = locals[ii].getSlot(); + long codeIndex = locals[ii].getStartBCI(); + int length = locals[ii].getLength(); + Location scopeStart = new LocationImpl(virtualMachine(), + this, codeIndex); + Location scopeEnd = + new LocationImpl(virtualMachine(), this, + codeIndex + length - 1); + String signature = + saMethod.getConstants().getSymbolAt(locals[ii].getDescriptorCPIndex()).asString(); + + int genericSigIndex = locals[ii].getSignatureCPIndex(); + String genericSignature = null; + if (genericSigIndex != 0) { + genericSignature = saMethod.getConstants().getSymbolAt(genericSigIndex).asString(); + } + + LocalVariable variable = + new LocalVariableImpl(virtualMachine(), this, + slot, scopeStart, scopeEnd, + name, signature, genericSignature); + // Add to the variable list + variables.add(variable); + } + } + + variables = Collections.unmodifiableList(variables); + variablesRef = new SoftReference(variables); + return variables; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ConnectorImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ConnectorImpl.java new file mode 100644 index 00000000000..364abb5cd38 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ConnectorImpl.java @@ -0,0 +1,631 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.connect.*; +import com.sun.jdi.InternalException; + +import java.io.*; +import java.lang.ref.*; +import java.lang.reflect.*; +import java.util.*; + +abstract class ConnectorImpl implements Connector { + Map defaultArguments = new LinkedHashMap(); + + // Used by BooleanArgument + static String trueString = null; + static String falseString; + + + /** This is not public in VirtualMachineManagerImpl + ThreadGroup mainGroupForJDI() { + return ((VirtualMachineManagerImpl)manager).mainGroupForJDI(); + } + ***/ + + // multiple debuggee support for SA/JDI + private static List freeVMClasses; // List> + private static ClassLoader myLoader; + // debug mode for SA/JDI connectors + static final protected boolean DEBUG; + static { + myLoader = ConnectorImpl.class.getClassLoader(); + freeVMClasses = new ArrayList(0); + DEBUG = System.getProperty("sun.jvm.hotspot.jdi.ConnectorImpl.DEBUG") != null; + } + + // add a new free VirtualMachineImpl class + private static synchronized void addFreeVMImplClass(Class clazz) { + if (DEBUG) { + System.out.println("adding free VirtualMachineImpl class"); + } + freeVMClasses.add(new SoftReference(clazz)); + } + + // returns null if we don't have anything free + private static synchronized Class getFreeVMImplClass() { + while (!freeVMClasses.isEmpty()) { + SoftReference ref = (SoftReference) freeVMClasses.remove(0); + Object o = ref.get(); + if (o != null) { + if (DEBUG) { + System.out.println("re-using loaded VirtualMachineImpl"); + } + return (Class) o; + } + } + return null; + } + + private static Class getVMImplClassFrom(ClassLoader cl) + throws ClassNotFoundException { + return Class.forName("sun.jvm.hotspot.jdi.VirtualMachineImpl", true, cl); + } + + /* SA has not been designed to support multiple debuggee VMs + * at-a-time. But, JDI supports multiple debuggee VMs. We + * support multiple debuggee VMs in SA/JDI, by creating a new + * class loader instance (refer to comment in SAJDIClassLoader + * for details). But, to avoid excessive class loading (and + * thereby resulting in larger footprint), we re-use 'dispose'd + * VirtualMachineImpl classes. + */ + protected static Class loadVirtualMachineImplClass() + throws ClassNotFoundException { + Class vmImplClass = getFreeVMImplClass(); + if (vmImplClass == null) { + ClassLoader cl = new SAJDIClassLoader(myLoader); + vmImplClass = getVMImplClassFrom(cl); + } + return vmImplClass; + } + + /* We look for System property sun.jvm.hotspot.jdi.. + * This property should have the value of JDK HOME directory for + * the given . + */ + private static String getSAClassPathForVM(String vmVersion) { + final String prefix = "sun.jvm.hotspot.jdi."; + // look for exact match of VM version + String jvmHome = System.getProperty(prefix + vmVersion); + if (DEBUG) { + System.out.println("looking for System property " + prefix + vmVersion); + } + + if (jvmHome == null) { + // omit chars after first '-' in VM version and try + // for example, in '1.5.0-b55' we take '1.5.0' + int index = vmVersion.indexOf('-'); + if (index != -1) { + vmVersion = vmVersion.substring(0, index); + if (DEBUG) { + System.out.println("looking for System property " + prefix + vmVersion); + } + jvmHome = System.getProperty(prefix + vmVersion); + } + + if (jvmHome == null) { + // System property is not set + if (DEBUG) { + System.out.println("can't locate JDK home for " + vmVersion); + } + return null; + } + } + + if (DEBUG) { + System.out.println("JDK home for " + vmVersion + " is " + jvmHome); + } + + // sa-jdi is in $JDK_HOME/lib directory + StringBuffer buf = new StringBuffer(); + buf.append(jvmHome); + buf.append(File.separatorChar); + buf.append("lib"); + buf.append(File.separatorChar); + buf.append("sa-jdi.jar"); + return buf.toString(); + } + + /* This method loads VirtualMachineImpl class by a ClassLoader + * configured with sa-jdi.jar path of given 'vmVersion'. This is + * used for cross VM version debugging. Refer to comments in + * SAJDIClassLoader as well. + */ + protected static Class loadVirtualMachineImplClass(String vmVersion) + throws ClassNotFoundException { + if (DEBUG) { + System.out.println("attemping to load sa-jdi.jar for version " + vmVersion); + } + String classPath = getSAClassPathForVM(vmVersion); + if (classPath != null) { + ClassLoader cl = new SAJDIClassLoader(myLoader, classPath); + return getVMImplClassFrom(cl); + } else { + return null; + } + } + + /* Is the given throwable an instanceof VMVersionMismatchException? + * Note that we can't do instanceof check because the exception + * class might have been loaded by a different class loader. + */ + private static boolean isVMVersionMismatch(Throwable throwable) { + String className = throwable.getClass().getName(); + return className.equals("sun.jvm.hotspot.runtime.VMVersionMismatchException"); + } + + /* gets target VM version from the given VMVersionMismatchException. + * Note that we need to reflectively call the method because of we may + * have got this from different classloader's namespace */ + private static String getVMVersion(Throwable throwable) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + // assert isVMVersionMismatch(throwable), "not a VMVersionMismatch" + Class expClass = throwable.getClass(); + Method targetVersionMethod = expClass.getMethod("getTargetVersion", new Class[0]); + return (String) targetVersionMethod.invoke(throwable, null); + } + + /** If the causal chain has a sun.jvm.hotspot.runtime.VMVersionMismatchException, + attempt to load VirtualMachineImpl class for target VM version. */ + protected static Class handleVMVersionMismatch(InvocationTargetException ite) { + Throwable cause = ite.getCause(); + if (DEBUG) { + System.out.println("checking for version mismatch..."); + } + while (cause != null) { + try { + if (isVMVersionMismatch(cause)) { + if (DEBUG) { + System.out.println("Triggering cross VM version support..."); + } + return loadVirtualMachineImplClass(getVMVersion(cause)); + } + } catch (Exception exp) { + if (DEBUG) { + System.out.println("failed to load VirtualMachineImpl class"); + exp.printStackTrace(); + } + return null; + } + cause = cause.getCause(); + } + return null; + } + + protected void checkNativeLink(SecurityManager sm, String os) { + if (os.equals("SunOS") || os.equals("Linux")) { + // link "saproc" - SA native library on SunOS and Linux? + sm.checkLink("saproc"); + } else if (os.startsWith("Windows")) { + // link "sawindbg" - SA native library on Windows. + sm.checkLink("sawindbg"); + } else { + throw new RuntimeException(os + " is not yet supported"); + } + } + + // we set an observer to detect VirtualMachineImpl.dispose call + // and on dispose we add corresponding VirtualMachineImpl.class to + // free VirtualMachimeImpl Class list. + protected static void setVMDisposeObserver(final Object vm) { + try { + Method setDisposeObserverMethod = vm.getClass().getDeclaredMethod("setDisposeObserver", + new Class[] { java.util.Observer.class }); + setDisposeObserverMethod.setAccessible(true); + setDisposeObserverMethod.invoke(vm, + new Object[] { + new Observer() { + public void update(Observable o, Object data) { + if (DEBUG) { + System.out.println("got VM.dispose notification"); + } + addFreeVMImplClass(vm.getClass()); + } + } + }); + } catch (Exception exp) { + if (DEBUG) { + System.out.println("setVMDisposeObserver() got an exception:"); + exp.printStackTrace(); + } + } + } + + public Map defaultArguments() { + Map defaults = new LinkedHashMap(); + Collection values = defaultArguments.values(); + + Iterator iter = values.iterator(); + while (iter.hasNext()) { + ArgumentImpl argument = (ArgumentImpl)iter.next(); + defaults.put(argument.name(), argument.clone()); + } + return defaults; + } + + void addStringArgument(String name, String label, String description, + String defaultValue, boolean mustSpecify) { + defaultArguments.put(name, + new StringArgumentImpl(name, label, + description, + defaultValue, + mustSpecify)); + } + + void addBooleanArgument(String name, String label, String description, + boolean defaultValue, boolean mustSpecify) { + defaultArguments.put(name, + new BooleanArgumentImpl(name, label, + description, + defaultValue, + mustSpecify)); + } + + void addIntegerArgument(String name, String label, String description, + String defaultValue, boolean mustSpecify, + int min, int max) { + defaultArguments.put(name, + new IntegerArgumentImpl(name, label, + description, + defaultValue, + mustSpecify, + min, max)); + } + + void addSelectedArgument(String name, String label, String description, + String defaultValue, boolean mustSpecify, + List list) { + defaultArguments.put(name, + new SelectedArgumentImpl(name, label, + description, + defaultValue, + mustSpecify, list)); + } + + ArgumentImpl argument(String name, Map arguments) + throws IllegalConnectorArgumentsException { + + ArgumentImpl argument = (ArgumentImpl)arguments.get(name); + if (argument == null) { + throw new IllegalConnectorArgumentsException( + "Argument missing", name); + } + String value = argument.value(); + if (value == null || value.length() == 0) { + if (argument.mustSpecify()) { + throw new IllegalConnectorArgumentsException( + "Argument unspecified", name); + } + } else if(!argument.isValid(value)) { + throw new IllegalConnectorArgumentsException( + "Argument invalid", name); + } + + return argument; + } + + String getString(String key) { + //fixme jjh; needs i18n + // this is not public return ((VirtualMachineManagerImpl)manager).getString(key); + return key; + } + + public String toString() { + String string = name() + " (defaults: "; + Iterator iter = defaultArguments().values().iterator(); + boolean first = true; + while (iter.hasNext()) { + ArgumentImpl argument = (ArgumentImpl)iter.next(); + if (!first) { + string += ", "; + } + string += argument.toString(); + first = false; + } + return string + ")"; + } + + abstract class ArgumentImpl implements Connector.Argument, Cloneable, Serializable { + private String name; + private String label; + private String description; + private String value; + private boolean mustSpecify; + + ArgumentImpl(String name, String label, String description, + String value, + boolean mustSpecify) { + this.name = name; + this.label = label; + this.description = description; + this.value = value; + this.mustSpecify = mustSpecify; + } + + public abstract boolean isValid(String value); + + public String name() { + return name; + } + + public String label() { + return label; + } + + public String description() { + return description; + } + + public String value() { + return value; + } + + public void setValue(String value) { + if (value == null) { + throw new NullPointerException("Can't set null value"); + } + this.value = value; + } + + public boolean mustSpecify() { + return mustSpecify; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof Connector.Argument)) { + Connector.Argument other = (Connector.Argument)obj; + return (name().equals(other.name())) && + (description().equals(other.description())) && + (mustSpecify() == other.mustSpecify()) && + (value().equals(other.value())); + } else { + return false; + } + } + + public int hashCode() { + return description().hashCode(); + } + + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + // Object should always support clone + throw (InternalException) new InternalException().initCause(e); + } + } + + public String toString() { + return name() + "=" + value(); + } + } + + class BooleanArgumentImpl extends ConnectorImpl.ArgumentImpl + implements Connector.BooleanArgument { + + BooleanArgumentImpl(String name, String label, String description, + boolean value, + boolean mustSpecify) { + super(name, label, description, null, mustSpecify); + if(trueString == null) { + trueString = getString("true"); + falseString = getString("false"); + } + setValue(value); + } + + /** + * Sets the value of the argument. + */ + public void setValue(boolean value) { + setValue(stringValueOf(value)); + } + + /** + * Performs basic sanity check of argument. + * @return true if value is a string + * representation of a boolean value. + * @see #stringValueOf(boolean) + */ + public boolean isValid(String value) { + return value.equals(trueString) || value.equals(falseString); + } + + /** + * Return the string representation of the value + * parameter. + * Does not set or examine the value or the argument. + * @return the localized String representation of the + * boolean value. + */ + public String stringValueOf(boolean value) { + return value? trueString : falseString; + } + + /** + * Return the value of the argument as a boolean. Since + * the argument may not have been set or may have an invalid + * value {@link #isValid(String)} should be called on + * {@link #value()} to check its validity. If it is invalid + * the boolean returned by this method is undefined. + * @return the value of the argument as a boolean. + */ + public boolean booleanValue() { + return value().equals(trueString); + } + } + + class IntegerArgumentImpl extends ConnectorImpl.ArgumentImpl + implements Connector.IntegerArgument { + + private final int min; + private final int max; + + IntegerArgumentImpl(String name, String label, String description, + String value, + boolean mustSpecify, int min, int max) { + super(name, label, description, value, mustSpecify); + this.min = min; + this.max = max; + } + + /** + * Sets the value of the argument. + * The value should be checked with {@link #isValid(int)} + * before setting it; invalid values will throw an exception + * when the connection is established - for example, + * on {@link LaunchingConnector#launch} + */ + public void setValue(int value) { + setValue(stringValueOf(value)); + } + + /** + * Performs basic sanity check of argument. + * @return true if value represents an int that is + * {@link #min()} <= value <= {@link #max()} + */ + public boolean isValid(String value) { + if (value == null) { + return false; + } + try { + return isValid(Integer.decode(value).intValue()); + } catch(NumberFormatException exc) { + return false; + } + } + + /** + * Performs basic sanity check of argument. + * @return true if + * {@link #min()} <= value <= {@link #max()} + */ + public boolean isValid(int value) { + return min <= value && value <= max; + } + + /** + * Return the string representation of the value + * parameter. + * Does not set or examine the value or the argument. + * @return the String representation of the + * int value. + */ + public String stringValueOf(int value) { + // *** Should this be internationalized???? + // *** Even Brian Beck was unsure if an Arabic programmer + // *** would expect port numbers in Arabic numerals, + // *** so punt for now. + return ""+value; + } + + /** + * Return the value of the argument as a int. Since + * the argument may not have been set or may have an invalid + * value {@link #isValid(String)} should be called on + * {@link #value()} to check its validity. If it is invalid + * the int returned by this method is undefined. + * @return the value of the argument as a int. + */ + public int intValue() { + if (value() == null) { + return 0; + } + try { + return Integer.decode(value()).intValue(); + } catch(NumberFormatException exc) { + return 0; + } + } + + /** + * The upper bound for the value. + * @return the maximum allowed value for this argument. + */ + public int max() { + return max; + } + + /** + * The lower bound for the value. + * @return the minimum allowed value for this argument. + */ + public int min() { + return min; + } + } + + class StringArgumentImpl extends ConnectorImpl.ArgumentImpl + implements Connector.StringArgument { + + StringArgumentImpl(String name, String label, String description, + String value, + boolean mustSpecify) { + super(name, label, description, value, mustSpecify); + } + + /** + * Performs basic sanity check of argument. + * @return true always + */ + public boolean isValid(String value) { + return true; + } + } + + class SelectedArgumentImpl extends ConnectorImpl.ArgumentImpl + implements Connector.SelectedArgument { + + private final List choices; + + SelectedArgumentImpl(String name, String label, String description, + String value, + boolean mustSpecify, List choices) { + super(name, label, description, value, mustSpecify); + this.choices = Collections.unmodifiableList( + new ArrayList(choices)); + } + + /** + * Return the possible values for the argument + * @return {@link List} of {@link String} + */ + public List choices() { + return choices; + } + + /** + * Performs basic sanity check of argument. + * @return true if value is one of {@link #choices()}. + */ + public boolean isValid(String value) { + return choices.contains(value); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/DoubleTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/DoubleTypeImpl.java new file mode 100644 index 00000000000..c3d923945fe --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/DoubleTypeImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class DoubleTypeImpl extends PrimitiveTypeImpl implements DoubleType { + DoubleTypeImpl(VirtualMachine vm) { + super(vm); + } + + + public String signature() { + return "D"; + } + + PrimitiveValue convert(PrimitiveValue value) throws InvalidTypeException { + return vm.mirrorOf(((PrimitiveValueImpl)value).checkedDoubleValue()); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/DoubleValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/DoubleValueImpl.java new file mode 100644 index 00000000000..025ea7aef7d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/DoubleValueImpl.java @@ -0,0 +1,159 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class DoubleValueImpl extends PrimitiveValueImpl + implements DoubleValue { + private double value; + + DoubleValueImpl(VirtualMachine aVm,double aValue) { + super(aVm); + + value = aValue; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof DoubleValue)) { + return (value == ((DoubleValue)obj).value()) && + super.equals(obj); + } else { + return false; + } + } + + public int compareTo(Object obj) { + double other = ((DoubleValue)obj).value(); + if (value() < other) { + return -1; + } else if (value() == other) { + return 0; + } else { + return 1; + } + } + + public int hashCode() { + /* + * TO DO: Better hash code + */ + return intValue(); + } + + public Type type() { + return vm.theDoubleType(); + } + + public double value() { + return value; + } + + public boolean booleanValue() { + return(value == 0.0)?false:true; + } + + public byte byteValue() { + return(byte)value; + } + + public char charValue() { + return(char)value; + } + + public short shortValue() { + return(short)value; + } + + public int intValue() { + return(int)value; + } + + public long longValue() { + return(long)value; + } + + public float floatValue() { + return(float)value; + } + + public double doubleValue() { + return(double)value; + } + + byte checkedByteValue() throws InvalidTypeException { + if ((value > Byte.MAX_VALUE) || (value < Byte.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to byte"); + } else { + return super.checkedByteValue(); + } + } + + char checkedCharValue() throws InvalidTypeException { + if ((value > Character.MAX_VALUE) || (value < Character.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to char"); + } else { + return super.checkedCharValue(); + } + } + + short checkedShortValue() throws InvalidTypeException { + if ((value > Short.MAX_VALUE) || (value < Short.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to short"); + } else { + return super.checkedShortValue(); + } + } + + int checkedIntValue() throws InvalidTypeException { + if ((value > Integer.MAX_VALUE) || (value < Integer.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to int"); + } else { + return super.checkedIntValue(); + } + } + + long checkedLongValue() throws InvalidTypeException { + long longValue = (long)value; + if (longValue != value) { + throw new InvalidTypeException("Can't convert " + value + " to long"); + } else { + return super.checkedLongValue(); + } + } + + float checkedFloatValue() throws InvalidTypeException { + float floatValue = (float)value; + if (floatValue != value) { + throw new InvalidTypeException("Can't convert " + value + " to float"); + } else { + return super.checkedFloatValue(); + } + } + + public String toString() { + return "" + value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/FieldImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/FieldImpl.java new file mode 100644 index 00000000000..5a271b9e748 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/FieldImpl.java @@ -0,0 +1,217 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.Instance; +import sun.jvm.hotspot.oops.Array; +import sun.jvm.hotspot.oops.InstanceKlass; +import sun.jvm.hotspot.oops.Symbol; +import sun.jvm.hotspot.oops.FieldIdentifier; + +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; +import java.util.Comparator; + +public class FieldImpl extends TypeComponentImpl implements Field { + private JNITypeParser signatureParser; + private sun.jvm.hotspot.oops.Field saField; + + FieldImpl( VirtualMachine vm, ReferenceTypeImpl declaringType, + sun.jvm.hotspot.oops.Field saField) { + super(vm, declaringType); + this.saField = saField; + getParser(); + } + + private void getParser() { + if (signatureParser == null) { + Symbol sig1 = saField.getSignature(); + signature = sig1.asString(); + signatureParser = new JNITypeParser(signature); + } + } + + sun.jvm.hotspot.oops.Field ref() { + return saField; + } + + // get the value of static field + ValueImpl getValue() { + return getValue(saField.getFieldHolder()); + } + + // get the value of this Field from a specific Oop + ValueImpl getValue(Oop target) { + ValueImpl valueImpl; + sun.jvm.hotspot.oops.Field saField = (sun.jvm.hotspot.oops.Field) ref(); + sun.jvm.hotspot.oops.FieldType ft = saField.getFieldType(); + if (ft.isArray()) { + sun.jvm.hotspot.oops.OopField of = (sun.jvm.hotspot.oops.OopField)saField; + valueImpl = (ArrayReferenceImpl) vm.arrayMirror((Array)of.getValue(target)); + } else if (ft.isObject()) { + sun.jvm.hotspot.oops.OopField of = (sun.jvm.hotspot.oops.OopField)saField; + valueImpl = (ObjectReferenceImpl) vm.objectMirror(of.getValue(target)); + } else if (ft.isByte()) { + sun.jvm.hotspot.oops.ByteField bf = (sun.jvm.hotspot.oops.ByteField)saField; + valueImpl = (ByteValueImpl) vm.mirrorOf(bf.getValue(target)); + } else if (ft.isChar()) { + sun.jvm.hotspot.oops.CharField cf = (sun.jvm.hotspot.oops.CharField)saField; + valueImpl = (CharValueImpl) vm.mirrorOf(cf.getValue(target)); + } else if (ft.isDouble()) { + sun.jvm.hotspot.oops.DoubleField df = (sun.jvm.hotspot.oops.DoubleField)saField; + valueImpl = (DoubleValueImpl) vm.mirrorOf(df.getValue(target)); + } else if (ft.isFloat()) { + sun.jvm.hotspot.oops.FloatField ff = (sun.jvm.hotspot.oops.FloatField)saField; + valueImpl = (FloatValueImpl) vm.mirrorOf(ff.getValue(target)); + } else if (ft.isInt()) { + sun.jvm.hotspot.oops.IntField iif = (sun.jvm.hotspot.oops.IntField)saField; + valueImpl = (IntegerValueImpl) vm.mirrorOf(iif.getValue(target)); + } else if (ft.isLong()) { + sun.jvm.hotspot.oops.LongField lf = (sun.jvm.hotspot.oops.LongField)saField; + valueImpl = (LongValueImpl) vm.mirrorOf(lf.getValue(target)); + } else if (ft.isShort()) { + sun.jvm.hotspot.oops.ShortField sf = (sun.jvm.hotspot.oops.ShortField)saField; + valueImpl = (ShortValueImpl) vm.mirrorOf(sf.getValue(target)); + } else if (ft.isBoolean()) { + sun.jvm.hotspot.oops.BooleanField bf = (sun.jvm.hotspot.oops.BooleanField)saField; + valueImpl = (BooleanValueImpl) vm.mirrorOf(bf.getValue(target)); + } else { + throw new RuntimeException("Should not reach here"); + } + return valueImpl; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof FieldImpl)) { + FieldImpl other = (FieldImpl)obj; + return (declaringType().equals(other.declaringType())) && + (ref().equals(other.ref())) && + super.equals(obj); + } else { + return false; + } + } + + public boolean isTransient() { + return saField.isTransient(); + } + + public boolean isVolatile() { + return saField.isVolatile(); + } + + public boolean isEnumConstant() { + return saField.isEnumConstant(); + } + + public Type type() throws ClassNotLoadedException { + // So, we do it just like JDI does by searching the enclosing type. + return findType(signature()); + } + + public String typeName() { //fixme jjh: jpda version creates redundant JNITypeParsers + getParser(); + return signatureParser.typeName(); + } + + public String genericSignature() { + Symbol genSig = saField.getGenericSignature(); + return (genSig != null)? genSig.asString() : null; + } + + // From interface Comparable + public int compareTo(Object object) { + Field field = (Field)object; + ReferenceTypeImpl declaringType = (ReferenceTypeImpl)declaringType(); + int rc = declaringType.compareTo(field.declaringType()); + if (rc == 0) { + rc = declaringType.indexOf(this) - + declaringType.indexOf(field); + } + return rc; + } + + // from interface Mirror + public String toString() { + StringBuffer buf = new StringBuffer(); + + buf.append(declaringType().name()); + buf.append('.'); + buf.append(name()); + return buf.toString(); + } + + public String name() { + FieldIdentifier myName = saField.getID(); + return myName.getName(); + } + + // From interface Accessible + public int modifiers() { + return saField.getAccessFlagsObj().getStandardFlags(); + } + + public boolean isPackagePrivate() { + return saField.isPackagePrivate(); + } + + public boolean isPrivate() { + return saField.isPrivate(); + } + + public boolean isProtected() { + return saField.isProtected(); + } + + public boolean isPublic() { + return saField.isPublic(); + } + + public boolean isStatic() { + return saField.isStatic(); + } + + public boolean isFinal() { + return saField.isFinal(); + } + + public boolean isSynthetic() { + return saField.isSynthetic(); + } + + public int hashCode() { + return saField.hashCode(); + } + + + private Type findType(String signature) throws ClassNotLoadedException { + ReferenceTypeImpl enclosing = (ReferenceTypeImpl)declaringType(); + return enclosing.findType(signature); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/FloatTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/FloatTypeImpl.java new file mode 100644 index 00000000000..0c638341012 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/FloatTypeImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class FloatTypeImpl extends PrimitiveTypeImpl implements FloatType { + FloatTypeImpl(VirtualMachine vm) { + super(vm); + } + + + public String signature() { + return "F"; + } + + PrimitiveValue convert(PrimitiveValue value) throws InvalidTypeException { + return vm.mirrorOf(((PrimitiveValueImpl)value).checkedFloatValue()); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/FloatValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/FloatValueImpl.java new file mode 100644 index 00000000000..5ecfe3a6c34 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/FloatValueImpl.java @@ -0,0 +1,151 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class FloatValueImpl extends PrimitiveValueImpl + implements FloatValue { + private float value; + + FloatValueImpl(VirtualMachine aVm,float aValue) { + super(aVm); + + value = aValue; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof FloatValue)) { + return (value == ((FloatValue)obj).value()) && + super.equals(obj); + } else { + return false; + } + } + + public int hashCode() { + /* + * TO DO: Better hash code + */ + return intValue(); + } + + public int compareTo(Object obj) { + float other = ((FloatValue)obj).value(); + if (value() < other) { + return -1; + } else if (value() == other) { + return 0; + } else { + return 1; + } + } + + public Type type() { + return vm.theFloatType(); + } + + public float value() { + return value; + } + + public boolean booleanValue() { + return(value == 0.0)?false:true; + } + + public byte byteValue() { + return(byte)value; + } + + public char charValue() { + return(char)value; + } + + public short shortValue() { + return(short)value; + } + + public int intValue() { + return(int)value; + } + + public long longValue() { + return(long)value; + } + + public float floatValue() { + return(float)value; + } + + public double doubleValue() { + return(double)value; + } + + byte checkedByteValue() throws InvalidTypeException { + if ((value > Byte.MAX_VALUE) || (value < Byte.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to byte"); + } else { + return super.checkedByteValue(); + } + } + + char checkedCharValue() throws InvalidTypeException { + if ((value > Character.MAX_VALUE) || (value < Character.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to char"); + } else { + return super.checkedCharValue(); + } + } + + short checkedShortValue() throws InvalidTypeException { + if ((value > Short.MAX_VALUE) || (value < Short.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to short"); + } else { + return super.checkedShortValue(); + } + } + + int checkedIntValue() throws InvalidTypeException { + int intValue = (int)value; + if (intValue != value) { + throw new InvalidTypeException("Can't convert " + value + " to int"); + } else { + return super.checkedIntValue(); + } + } + + long checkedLongValue() throws InvalidTypeException { + long longValue = (long)value; + if (longValue != value) { + throw new InvalidTypeException("Can't convert " + value + " to long"); + } else { + return super.checkedLongValue(); + } + } + + public String toString() { + return "" + value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/IntegerTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/IntegerTypeImpl.java new file mode 100644 index 00000000000..1f1d5e27982 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/IntegerTypeImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class IntegerTypeImpl extends PrimitiveTypeImpl implements IntegerType { + IntegerTypeImpl(VirtualMachine vm) { + super(vm); + } + + public String signature() { + return "I"; + } + + PrimitiveValue convert(PrimitiveValue value) throws InvalidTypeException { + return vm.mirrorOf(((PrimitiveValueImpl)value).checkedIntValue()); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/IntegerValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/IntegerValueImpl.java new file mode 100644 index 00000000000..c9ad6f22efc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/IntegerValueImpl.java @@ -0,0 +1,127 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class IntegerValueImpl extends PrimitiveValueImpl + implements IntegerValue { + private int value; + + IntegerValueImpl(VirtualMachine aVm,int aValue) { + super(aVm); + + value = aValue; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof IntegerValue)) { + return (value == ((IntegerValue)obj).value()) && + super.equals(obj); + } else { + return false; + } + } + + public int hashCode() { + /* + * TO DO: Better hash code + */ + return intValue(); + } + + public int compareTo(Object obj) { + int other = ((IntegerValue)obj).value(); + return value() - other; + } + + public Type type() { + return vm.theIntegerType(); + } + + public int value() { + return value; + } + + public boolean booleanValue() { + return(value == 0)?false:true; + } + + public byte byteValue() { + return(byte)value; + } + + public char charValue() { + return(char)value; + } + + public short shortValue() { + return(short)value; + } + + public int intValue() { + return(int)value; + } + + public long longValue() { + return(long)value; + } + + public float floatValue() { + return(float)value; + } + + public double doubleValue() { + return(double)value; + } + + byte checkedByteValue() throws InvalidTypeException { + if ((value > Byte.MAX_VALUE) || (value < Byte.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to byte"); + } else { + return super.checkedByteValue(); + } + } + + char checkedCharValue() throws InvalidTypeException { + if ((value > Character.MAX_VALUE) || (value < Character.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to char"); + } else { + return super.checkedCharValue(); + } + } + + short checkedShortValue() throws InvalidTypeException { + if ((value > Short.MAX_VALUE) || (value < Short.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to short"); + } else { + return super.checkedShortValue(); + } + } + + public String toString() { + return "" + value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/InterfaceTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/InterfaceTypeImpl.java new file mode 100644 index 00000000000..2344314eecf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/InterfaceTypeImpl.java @@ -0,0 +1,204 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; +import sun.jvm.hotspot.oops.InstanceKlass; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.Iterator; +import java.util.Collections; +import java.lang.ref.SoftReference; + +public class InterfaceTypeImpl extends ReferenceTypeImpl + implements InterfaceType { + private SoftReference superInterfacesCache = null; + private SoftReference subInterfacesCache = null; + private SoftReference implementorsCache = null; + + protected InterfaceTypeImpl(VirtualMachine aVm, InstanceKlass aRef) { + super(aVm, aRef); + } + + public List superinterfaces() throws ClassNotPreparedException { + List superinterfaces = (superInterfacesCache != null)? (List) superInterfacesCache.get() : null; + if (superinterfaces == null) { + checkPrepared(); + superinterfaces = Collections.unmodifiableList(getInterfaces()); + superInterfacesCache = new SoftReference(superinterfaces); + } + return superinterfaces; + } + + public List subinterfaces() { + List subinterfaces = (subInterfacesCache != null)? (List) subInterfacesCache.get() : null; + if (subinterfaces == null) { + List all = vm.allClasses(); + subinterfaces = new ArrayList(); + Iterator iter = all.iterator(); + while (iter.hasNext()) { + ReferenceType refType = (ReferenceType)iter.next(); + if (refType instanceof InterfaceType) { + InterfaceType interfaze = (InterfaceType)refType; + if (interfaze.isPrepared() && interfaze.superinterfaces().contains(this)) { + subinterfaces.add(interfaze); + } + } + } + subinterfaces = Collections.unmodifiableList(subinterfaces); + subInterfacesCache = new SoftReference(subinterfaces); + } + return subinterfaces; + } + + public List implementors() { + List implementors = (implementorsCache != null)? (List) implementorsCache.get() : null; + if (implementors == null) { + List all = vm.allClasses(); + implementors = new ArrayList(); + Iterator iter = all.iterator(); + while (iter.hasNext()) { + ReferenceType refType = (ReferenceType)iter.next(); + if (refType instanceof ClassType) { + ClassType clazz = (ClassType)refType; + if (clazz.isPrepared() && clazz.interfaces().contains(this)) { + implementors.add(clazz); + } + } + } + implementors = Collections.unmodifiableList(implementors); + implementorsCache = new SoftReference(implementors); + } + return implementors; + } + + void addVisibleMethods(Map methodMap) { + /* + * Add methods from + * parent types first, so that the methods in this class will + * overwrite them in the hash table + */ + Iterator iter = superinterfaces().iterator(); + while (iter.hasNext()) { + InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next(); + interfaze.addVisibleMethods(methodMap); + } + + addToMethodMap(methodMap, methods()); + } + + List getAllMethods() { + ArrayList list = new ArrayList(methods()); + /* + * It's more efficient if don't do this + * recursively. + */ + List interfaces = allSuperinterfaces(); + Iterator iter = interfaces.iterator(); + while (iter.hasNext()) { + InterfaceType interfaze = (InterfaceType)iter.next(); + list.addAll(interfaze.methods()); + } + + return list; + } + + List allSuperinterfaces() { + ArrayList list = new ArrayList(); + addSuperinterfaces(list); + return list; + } + + void addSuperinterfaces(List list) { + /* + * This code is a little strange because it + * builds the list with a more suitable order than the + * depth-first approach a normal recursive solution would + * take. Instead, all direct superinterfaces precede all + * indirect ones. + */ + + /* + * Get a list of direct superinterfaces that's not already in the + * list being built. + */ + List immediate = new ArrayList(superinterfaces()); + Iterator iter = immediate.iterator(); + while (iter.hasNext()) { + InterfaceType interfaze = (InterfaceType)iter.next(); + if (list.contains(interfaze)) { + iter.remove(); + } + } + + /* + * Add all new direct superinterfaces + */ + list.addAll(immediate); + + /* + * Recurse for all new direct superinterfaces. + */ + iter = immediate.iterator(); + while (iter.hasNext()) { + InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next(); + interfaze.addSuperinterfaces(list); + } + } + + boolean isAssignableTo(ReferenceType type) { + + // Exact match? + if (this.equals(type)) { + return true; + } else { + // Try superinterfaces. + List supers = superinterfaces(); + Iterator iter = supers.iterator(); + while (iter.hasNext()) { + InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next(); + if (interfaze.isAssignableTo(type)) { + return true; + } + } + + return false; + } + } + + List inheritedTypes() { + return superinterfaces(); + } + + public boolean isInitialized() { + return isPrepared(); + } + + public String toString() { + return "interface " + name() + " (" + loaderString() + ")"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/JNITypeParser.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/JNITypeParser.java new file mode 100644 index 00000000000..c1ff6d35fb8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/JNITypeParser.java @@ -0,0 +1,240 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import java.util.List; +import java.util.ArrayList; + +public class JNITypeParser { + + static final char SIGNATURE_ENDCLASS = ';'; + static final char SIGNATURE_FUNC = '('; + static final char SIGNATURE_ENDFUNC = ')'; + + private String signature; + private List typeNameList; + private List signatureList; + private int currentIndex; + + JNITypeParser(String signature) { + this.signature = signature; + } + + static String typeNameToSignature(String signature) { + StringBuffer buffer = new StringBuffer(); + int firstIndex = signature.indexOf('['); + int index = firstIndex; + while (index != -1) { + buffer.append('['); + index = signature.indexOf('[', index + 1); + } + + if (firstIndex != -1) { + signature = signature.substring(0, firstIndex); + } + + if (signature.equals("boolean")) { + buffer.append('Z'); + } else if (signature.equals("byte")) { + buffer.append('B'); + } else if (signature.equals("char")) { + buffer.append('C'); + } else if (signature.equals("short")) { + buffer.append('S'); + } else if (signature.equals("int")) { + buffer.append('I'); + } else if (signature.equals("long")) { + buffer.append('J'); + } else if (signature.equals("float")) { + buffer.append('F'); + } else if (signature.equals("double")) { + buffer.append('D'); + } else { + buffer.append('L'); + buffer.append(signature.replace('.', '/')); + buffer.append(';'); + } + + return buffer.toString(); + } + + String typeName() { + return (String)typeNameList().get(typeNameList().size()-1); + } + + List argumentTypeNames() { + return typeNameList().subList(0, typeNameList().size() - 1); + } + + String signature() { + return (String)signatureList().get(signatureList().size()-1); + } + + List argumentSignatures() { + return signatureList().subList(0, signatureList().size() - 1); + } + + int dimensionCount() { + int count = 0; + String signature = signature(); + while (signature.charAt(count) == '[') { + count++; + } + return count; + } + + String componentSignature(int level) { + return signature().substring(level); + } + + private synchronized List signatureList() { + if (signatureList == null) { + signatureList = new ArrayList(10); + String elem; + + currentIndex = 0; + + while(currentIndex < signature.length()) { + elem = nextSignature(); + signatureList.add(elem); + } + if (signatureList.size() == 0) { + throw new IllegalArgumentException("Invalid JNI signature '" + + signature + "'"); + } + } + return signatureList; + } + + private synchronized List typeNameList() { + if (typeNameList == null) { + typeNameList = new ArrayList(10); + String elem; + + currentIndex = 0; + + while(currentIndex < signature.length()) { + elem = nextTypeName(); + typeNameList.add(elem); + } + if (typeNameList.size() == 0) { + throw new IllegalArgumentException("Invalid JNI signature '" + + signature + "'"); + } + } + return typeNameList; + } + + private String nextSignature() { + char key = signature.charAt(currentIndex++); + + switch(key) { + case '[': + return key + nextSignature(); + + case 'L': + int endClass = signature.indexOf(SIGNATURE_ENDCLASS, + currentIndex); + String retVal = signature.substring(currentIndex - 1, + endClass + 1); + currentIndex = endClass + 1; + return retVal; + + case 'V': + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + case 'J': + case 'F': + case 'D': + return String.valueOf(key); + + case SIGNATURE_FUNC: + case SIGNATURE_ENDFUNC: + return nextSignature(); + + default: + throw new IllegalArgumentException( + "Invalid JNI signature character '" + key + "'"); + + } + } + + private String nextTypeName() { + char key = signature.charAt(currentIndex++); + + switch(key) { + case '[': + return nextTypeName() + "[]"; + + case 'B': + return "byte"; + + case 'C': + return "char"; + + case 'L': + int endClass = signature.indexOf(SIGNATURE_ENDCLASS, + currentIndex); + String retVal = signature.substring(currentIndex, + endClass); + retVal = retVal.replace('/','.'); + currentIndex = endClass + 1; + return retVal; + + case 'F': + return "float"; + + case 'D': + return "double"; + + case 'I': + return "int"; + + case 'J': + return "long"; + + case 'S': + return "short"; + + case 'V': + return "void"; + + case 'Z': + return "boolean"; + + case SIGNATURE_ENDFUNC: + case SIGNATURE_FUNC: + return nextTypeName(); + + default: + throw new IllegalArgumentException( + "Invalid JNI signature character '" + key + "'"); + + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/JVMTIThreadState.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/JVMTIThreadState.java new file mode 100644 index 00000000000..d98e10a8802 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/JVMTIThreadState.java @@ -0,0 +1,43 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +// from JVMTI specification - refer to jvmti.xml +public interface JVMTIThreadState { + public static final int JVMTI_THREAD_STATE_ALIVE = 0x0001; + public static final int JVMTI_THREAD_STATE_TERMINATED = 0x0002; + public static final int JVMTI_THREAD_STATE_RUNNABLE = 0x0004; + public static final int JVMTI_THREAD_STATE_WAITING = 0x0008; + public static final int JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010; + public static final int JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020; + public static final int JVMTI_THREAD_STATE_SLEEPING = 0x0040; + public static final int JVMTI_THREAD_STATE_WAITING_FOR_NOTIFICATION = 0x0080; + public static final int JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100; + public static final int JVMTI_THREAD_STATE_PARKED = 0x0200; + public static final int JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400; + public static final int JVMTI_THREAD_STATE_SUSPENDED = 0x100000; + public static final int JVMTI_THREAD_STATE_INTERRUPTED = 0x200000; + public static final int JVMTI_THREAD_STATE_IN_NATIVE = 0x400000; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LineInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LineInfo.java new file mode 100644 index 00000000000..b1f9c849a70 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LineInfo.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +interface LineInfo { + + String liStratum(); + + int liLineNumber(); + + String liSourceName() throws AbsentInformationException; + + String liSourcePath() throws AbsentInformationException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LocalVariableImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LocalVariableImpl.java new file mode 100644 index 00000000000..a1509aa564e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LocalVariableImpl.java @@ -0,0 +1,169 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; +import com.sun.jdi.*; + +public class LocalVariableImpl extends MirrorImpl + implements LocalVariable, ValueContainer +{ + private final Method method; + private final int slot; + private final Location scopeStart; + private final Location scopeEnd; + private final String name; + private final String signature; + private final String genericSignature; + + LocalVariableImpl(VirtualMachine vm, Method method, + int slot, Location scopeStart, Location scopeEnd, + String name, String signature, String genericSignature) { + super(vm); + this.method = method; + this.slot = slot; + this.scopeStart = scopeStart; + this.scopeEnd = scopeEnd; + this.name = name; + this.signature = signature; + this.genericSignature = genericSignature; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof LocalVariableImpl)) { + LocalVariableImpl other = (LocalVariableImpl)obj; + return (method.equals(other.method) && + slot() == other.slot() && + super.equals(obj)); + } else { + return false; + } + } + + public int hashCode() { + /* + * TO DO: Better hash code + */ + return (int)method.hashCode() + slot(); + } + + public int compareTo(Object object) { + LocalVariableImpl other = (LocalVariableImpl)object; + int rc = method.compareTo(other.method); + if (rc == 0) { + rc = slot() - other.slot(); + } + return rc; + } + + public String name() { + return name; + } + + /** + * @return a text representation of the declared type + * of this variable. + */ + public String typeName() { + JNITypeParser parser = new JNITypeParser(signature); + return parser.typeName(); + } + + public Type type() throws ClassNotLoadedException { + return findType(signature()); + } + + public Type findType(String signature) throws ClassNotLoadedException { + ReferenceTypeImpl enclosing = (ReferenceTypeImpl)method.declaringType(); + return enclosing.findType(signature); + } + + public String signature() { + return signature; + } + + public String genericSignature() { + return genericSignature; + } + + public boolean isVisible(StackFrame frame) { + //validateMirror(frame); + Method frameMethod = frame.location().method(); + + if (!frameMethod.equals(method)) { + throw new IllegalArgumentException( + "frame method different than variable's method"); + } + + // this is here to cover the possibility that we will + // allow LocalVariables for native methods. If we do + // so we will have to re-examinine this. + if (frameMethod.isNative()) { + return false; + } + + return ((scopeStart.compareTo(frame.location()) <= 0) + && (scopeEnd.compareTo(frame.location()) >= 0)); + } + + public boolean isArgument() { + try { + MethodImpl method = (MethodImpl)scopeStart.method(); + return (slot < method.argSlotCount()); + } catch (AbsentInformationException e) { + // If this variable object exists, there shouldn't be absent info + throw (InternalException) new InternalException().initCause(e); + } + } + + int slot() { + return slot; + } + + /* + * Compilers/VMs can have byte code ranges for variables of the + * same names that overlap. This is because the byte code ranges + * aren't necessarily scopes; they may have more to do with the + * lifetime of the variable's slot, depending on implementation. + * + * This method determines whether this variable hides an + * identically named variable; ie, their byte code ranges overlap + * this one starts after the given one. If it returns true this + * variable should be preferred when looking for a single variable + * with its name when both variables are visible. + */ + boolean hides(LocalVariable other) { + LocalVariableImpl otherImpl = (LocalVariableImpl)other; + if (!method.equals(otherImpl.method) || + !name.equals(otherImpl.name)) { + return false; + } else { + return (scopeStart.compareTo(otherImpl.scopeStart) > 0); + } + } + + public String toString() { + return name() + " in " + method.toString() + + "@" + scopeStart.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LocationImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LocationImpl.java new file mode 100644 index 00000000000..4f6ed567b34 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LocationImpl.java @@ -0,0 +1,225 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +import java.util.*; + +public class LocationImpl extends MirrorImpl implements Location { + private final ReferenceTypeImpl declaringType; + private Method method; + private sun.jvm.hotspot.oops.Method methodRef; + private long codeIndex; + private LineInfo baseLineInfo = null; + private LineInfo otherLineInfo = null; + + LocationImpl(VirtualMachine vm, + Method method, long codeIndex) { + super(vm); + + this.method = method; + this.codeIndex = method.isNative()? -1 : codeIndex; + this.declaringType = (ReferenceTypeImpl)method.declaringType(); + } + + /* + * This constructor allows lazy creation of the method mirror. This + * can be a performance savings if the method mirror does not yet + * exist. + */ + LocationImpl(VirtualMachine vm, ReferenceType declaringType, + sun.jvm.hotspot.oops.Method methodRef, long codeIndex) { + super(vm); + + this.method = null; + this.codeIndex = codeIndex; + this.declaringType = (ReferenceTypeImpl)declaringType; + this.methodRef = methodRef; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof Location)) { + Location other = (Location)obj; + return (method().equals(other.method())) && + (codeIndex() == other.codeIndex()) && + super.equals(obj); + } else { + return false; + } + } + + public int hashCode() { + /* + * TO DO: better hash code? + */ + return method().hashCode() + (int)codeIndex(); + } + + public int compareTo(Object object) { + LocationImpl other = (LocationImpl)object; + int rc = method().compareTo(other.method()); + if (rc == 0) { + long diff = codeIndex() - other.codeIndex(); + if (diff < 0) + return -1; + else if (diff > 0) + return 1; + else + return 0; + } + return rc; + } + + public ReferenceType declaringType() { + return declaringType; + } + + public Method method() { + if (method == null) { + method = declaringType.getMethodMirror(methodRef); + if (method.isNative()) { + codeIndex = -1; + } + } + return method; + } + + public long codeIndex() { + method(); // be sure information is up-to-date + return codeIndex; + } + + LineInfo getBaseLineInfo(SDE.Stratum stratum) { + LineInfo lineInfo; + + /* check if there is cached info to use */ + if (baseLineInfo != null) { + return baseLineInfo; + } + + /* compute the line info */ + MethodImpl methodImpl = (MethodImpl)method(); + lineInfo = methodImpl.codeIndexToLineInfo(stratum, + codeIndex()); + + /* cache it */ + addBaseLineInfo(lineInfo); + + return lineInfo; + } + + LineInfo getLineInfo(SDE.Stratum stratum) { + LineInfo lineInfo; + + /* base stratum is done slighly differently */ + if (stratum.isJava()) { + return getBaseLineInfo(stratum); + } + + /* check if there is cached info to use */ + lineInfo = otherLineInfo; // copy because of concurrency + if (lineInfo != null && + stratum.id().equals(lineInfo.liStratum())) { + return lineInfo; + } + int baseLineNumber = lineNumber(SDE.BASE_STRATUM_NAME); + SDE.LineStratum lineStratum = + stratum.lineStratum(declaringType, baseLineNumber); + + if (lineStratum != null && lineStratum.lineNumber() != -1) { + lineInfo = new StratumLineInfo(stratum.id(), + lineStratum.lineNumber(), + lineStratum.sourceName(), + lineStratum.sourcePath()); + } else { + /* find best match */ + MethodImpl methodImpl = (MethodImpl)method(); + lineInfo = methodImpl.codeIndexToLineInfo(stratum, + codeIndex()); + } + + /* cache it */ + addStratumLineInfo(lineInfo); + + return lineInfo; + } + + void addStratumLineInfo(LineInfo lineInfo) { + otherLineInfo = lineInfo; + } + + void addBaseLineInfo(LineInfo lineInfo) { + baseLineInfo = lineInfo; + } + + public String sourceName() throws AbsentInformationException { + return sourceName(vm.getDefaultStratum()); + } + + public String sourceName(String stratumID) + throws AbsentInformationException { + return sourceName(declaringType.stratum(stratumID)); + } + + String sourceName(SDE.Stratum stratum) + throws AbsentInformationException { + return getLineInfo(stratum).liSourceName(); + } + + public String sourcePath() throws AbsentInformationException { + return sourcePath(vm.getDefaultStratum()); + } + + public String sourcePath(String stratumID) + throws AbsentInformationException { + return sourcePath(declaringType.stratum(stratumID)); + } + + String sourcePath(SDE.Stratum stratum) + throws AbsentInformationException { + return getLineInfo(stratum).liSourcePath(); + } + + public int lineNumber() { + return lineNumber(vm.getDefaultStratum()); + } + + public int lineNumber(String stratumID) { + return lineNumber(declaringType.stratum(stratumID)); + } + + int lineNumber(SDE.Stratum stratum) { + return getLineInfo(stratum).liLineNumber(); + } + + public String toString() { + if (lineNumber() == -1) { + return method().toString() + "+" + codeIndex(); + } else { + return declaringType().name() + ":" + lineNumber(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LongTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LongTypeImpl.java new file mode 100644 index 00000000000..4ea24cded30 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LongTypeImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class LongTypeImpl extends PrimitiveTypeImpl implements LongType { + LongTypeImpl(VirtualMachine vm) { + super(vm); + } + + + public String signature() { + return "J"; + } + + PrimitiveValue convert(PrimitiveValue value) throws InvalidTypeException { + return vm.mirrorOf(((PrimitiveValueImpl)value).checkedLongValue()); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LongValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LongValueImpl.java new file mode 100644 index 00000000000..4349d64b47b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/LongValueImpl.java @@ -0,0 +1,141 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class LongValueImpl extends PrimitiveValueImpl + implements LongValue { + private long value; + + LongValueImpl(VirtualMachine aVm,long aValue) { + super(aVm); + + value = aValue; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof LongValue)) { + return (value == ((LongValue)obj).value()) && + super.equals(obj); + } else { + return false; + } + } + + public int hashCode() { + /* + * TO DO: Better hash code + */ + return intValue(); + } + + public int compareTo(Object obj) { + long other = ((LongValue)obj).value(); + if (value() < other) { + return -1; + } else if (value() == other) { + return 0; + } else { + return 1; + } + } + + public Type type() { + return vm.theLongType(); + } + + public long value() { + return value; + } + + public boolean booleanValue() { + return(value == 0)?false:true; + } + + public byte byteValue() { + return(byte)value; + } + + public char charValue() { + return(char)value; + } + + public short shortValue() { + return(short)value; + } + + public int intValue() { + return(int)value; + } + + public long longValue() { + return(long)value; + } + + public float floatValue() { + return(float)value; + } + + public double doubleValue() { + return(double)value; + } + + byte checkedByteValue() throws InvalidTypeException { + if ((value > Byte.MAX_VALUE) || (value < Byte.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to byte"); + } else { + return super.checkedByteValue(); + } + } + + char checkedCharValue() throws InvalidTypeException { + if ((value > Character.MAX_VALUE) || (value < Character.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to char"); + } else { + return super.checkedCharValue(); + } + } + + short checkedShortValue() throws InvalidTypeException { + if ((value > Short.MAX_VALUE) || (value < Short.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to short"); + } else { + return super.checkedShortValue(); + } + } + + int checkedIntValue() throws InvalidTypeException { + if ((value > Integer.MAX_VALUE) || (value < Integer.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to int"); + } else { + return super.checkedIntValue(); + } + } + + public String toString() { + return "" + value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/MethodImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/MethodImpl.java new file mode 100644 index 00000000000..ede0a0af0b8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/MethodImpl.java @@ -0,0 +1,273 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; +import sun.jvm.hotspot.oops.Symbol; +import sun.jvm.hotspot.oops.LocalVariableTableElement; +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; +import java.util.Comparator; +import java.lang.ref.SoftReference; +import java.util.Collections; + +public abstract class MethodImpl extends TypeComponentImpl implements Method { + private JNITypeParser signatureParser; + protected sun.jvm.hotspot.oops.Method saMethod; + + abstract int argSlotCount() throws AbsentInformationException; + abstract List allLineLocations(SDE.Stratum stratum, + String sourceName) + throws AbsentInformationException; + abstract List locationsOfLine(SDE.Stratum stratum, + String sourceName, + int lineNumber) + throws AbsentInformationException; + + static MethodImpl createMethodImpl(VirtualMachine vm, ReferenceTypeImpl declaringType, + sun.jvm.hotspot.oops.Method saMethod) { + // Someday might have to add concrete and non-concrete subclasses. + if (saMethod.isNative() || saMethod.isAbstract()) { + return new NonConcreteMethodImpl(vm, declaringType, saMethod); + } else { + return new ConcreteMethodImpl(vm, declaringType, saMethod); + } + } + + MethodImpl(VirtualMachine vm, ReferenceTypeImpl declaringType, + sun.jvm.hotspot.oops.Method saMethod ) { + super(vm, declaringType); + this.saMethod = saMethod; + getParser(); + } + + private JNITypeParser getParser() { + if (signatureParser == null) { + Symbol sig1 = saMethod.getSignature(); + signature = sig1.asString(); + signatureParser = new JNITypeParser(signature); + } + return signatureParser; + } + + // Object ref() { + sun.jvm.hotspot.oops.Method ref() { + return saMethod; + } + + public String genericSignature() { + Symbol genSig = saMethod.getGenericSignature(); + return (genSig != null)? genSig.asString() : null; + } + + public String returnTypeName() { + return getParser().typeName(); + } + + public Type returnType() throws ClassNotLoadedException { + return findType(getParser().signature()); + } + + private Type findType(String signature) throws ClassNotLoadedException { + ReferenceTypeImpl enclosing = (ReferenceTypeImpl)declaringType(); + return enclosing.findType(signature); + } + + public List argumentTypeNames() { + return getParser().argumentTypeNames(); + } + + List argumentSignatures() { + return getParser().argumentSignatures(); + } + + Type argumentType(int index) throws ClassNotLoadedException { + ReferenceTypeImpl enclosing = (ReferenceTypeImpl)declaringType(); + String signature = (String)argumentSignatures().get(index); + return enclosing.findType(signature); + } + + public List argumentTypes() throws ClassNotLoadedException { + int size = argumentSignatures().size(); + ArrayList types = new ArrayList(size); + for (int i = 0; i < size; i++) { + Type type = argumentType(i); + types.add(type); + } + return types; + } + + public boolean isAbstract() { + return saMethod.isAbstract(); + } + + public boolean isBridge() { + return saMethod.isBridge(); + } + + public boolean isSynchronized() { + return saMethod.isSynchronized(); + } + + public boolean isNative() { + return saMethod.isNative(); + } + + public boolean isVarArgs() { + return saMethod.isVarArgs(); + } + + public boolean isConstructor() { + return saMethod.isConstructor(); + } + + public boolean isStaticInitializer() { + return saMethod.isStaticInitializer(); + } + + public boolean isObsolete() { + return saMethod.isObsolete(); + } + + public final List allLineLocations() + throws AbsentInformationException { + return allLineLocations(vm.getDefaultStratum(), null); + } + + public List allLineLocations(String stratumID, + String sourceName) + throws AbsentInformationException { + return allLineLocations(declaringType.stratum(stratumID), + sourceName); + } + + public final List locationsOfLine(int lineNumber) + throws AbsentInformationException { + return locationsOfLine(vm.getDefaultStratum(), + null, lineNumber); + } + + public List locationsOfLine(String stratumID, + String sourceName, + int lineNumber) + throws AbsentInformationException { + return locationsOfLine(declaringType.stratum(stratumID), + sourceName, lineNumber); + } + + LineInfo codeIndexToLineInfo(SDE.Stratum stratum, + long codeIndex) { + if (stratum.isJava()) { + return new BaseLineInfo(-1, declaringType); + } else { + return new StratumLineInfo(stratum.id(), -1, + null, null); + } + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof MethodImpl)) { + MethodImpl other = (MethodImpl)obj; + return (declaringType().equals(other.declaringType())) && + (ref().equals(other.ref())) && + super.equals(obj); + } else { + return false; + } + } + + // From interface Comparable + public int compareTo(Object object) { + Method method = (Method)object; + ReferenceTypeImpl declaringType = (ReferenceTypeImpl)declaringType(); + int rc = declaringType.compareTo(method.declaringType()); + if (rc == 0) { + rc = declaringType.indexOf(this) - + declaringType.indexOf(method); + } + return rc; + } + + // from interface Mirror + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(declaringType().name()); + sb.append("."); + sb.append(name()); + sb.append("("); + boolean first = true; + for (Iterator it = argumentTypeNames().iterator(); it.hasNext();) { + if (!first) { + sb.append(", "); + } + sb.append((String)it.next()); + first = false; + } + sb.append(")"); + return sb.toString(); + } + + public String name() { + Symbol myName = saMethod.getName(); + return myName.asString(); + } + + public int modifiers() { + return saMethod.getAccessFlagsObj().getStandardFlags(); + } + + public boolean isPackagePrivate() { + return saMethod.isPackagePrivate(); + } + + public boolean isPrivate() { + return saMethod.isPrivate(); + } + + public boolean isProtected() { + return saMethod.isProtected(); + } + + public boolean isPublic() { + return saMethod.isPublic(); + } + + public boolean isStatic() { + return saMethod.isStatic(); + } + + public boolean isSynthetic() { + return saMethod.isSynthetic(); + } + + public boolean isFinal() { + return saMethod.isFinal(); + } + + public int hashCode() { + return saMethod.hashCode(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/MirrorImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/MirrorImpl.java new file mode 100644 index 00000000000..3f348b595b8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/MirrorImpl.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +abstract class MirrorImpl extends Object implements Mirror { + protected VirtualMachineImpl vm; + + MirrorImpl(VirtualMachine aVm) { + super(); + + // Yes, its a bit of a hack. But by doing it this + // way, this is the only place we have to change + // typing to substitute a new impl. + vm = (VirtualMachineImpl)aVm; + } + + public VirtualMachine virtualMachine() { + return vm; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof Mirror)) { + Mirror other = (Mirror)obj; + return vm.equals(other.virtualMachine()); + } else { + return false; + } + } + + public int hashCode() { + return vm.hashCode(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/MonitorInfoImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/MonitorInfoImpl.java new file mode 100644 index 00000000000..8930783a804 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/MonitorInfoImpl.java @@ -0,0 +1,71 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +// FIXME: This class should implement com.sun.jdi.MonitorInfo. +// So fix this when hotspot is started to build with +// jdk1.6. +public class MonitorInfoImpl extends MirrorImpl { + + /* Once false, monitorInfo should not be used. + * access synchronized on (vm.state()) + */ + private boolean isValid = true; + + ObjectReference monitor; + ThreadReference thread; + int stack_depth; + + MonitorInfoImpl(VirtualMachine vm, ObjectReference mon, + ThreadReference thread, int dpth) { + super(vm); + this.monitor = mon; + this.thread = thread; + this.stack_depth = dpth; + } + + private void validateMonitorInfo() { + if (!isValid) { + throw new InvalidStackFrameException("Thread has been resumed"); + } + } + + public ObjectReference monitor() { + validateMonitorInfo(); + return monitor; + } + + public int stackDepth() { + validateMonitorInfo(); + return stack_depth; + } + + public ThreadReference thread() { + validateMonitorInfo(); + return thread; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/NonConcreteMethodImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/NonConcreteMethodImpl.java new file mode 100644 index 00000000000..eda00b98663 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/NonConcreteMethodImpl.java @@ -0,0 +1,117 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +import java.util.List; +import java.util.Map; +import java.util.Iterator; +import java.util.ListIterator; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Collections; + +/** + * Represents non-concrete (that is, native or abstract) methods. + * Private to MethodImpl. + */ +public class NonConcreteMethodImpl extends MethodImpl { + + private Location location = null; + + NonConcreteMethodImpl(VirtualMachine vm, + ReferenceTypeImpl declaringType, + sun.jvm.hotspot.oops.Method saMethod) { + super(vm, declaringType, saMethod); + } + + public Location location() { + if (isAbstract()) { + return null; + } + if (location == null) { + location = new LocationImpl(vm, this, -1); + } + return location; + } + + public List allLineLocations(String stratumID, + String sourceName) { + return new ArrayList(0); + } + + public List allLineLocations(SDE.Stratum stratum, + String sourceName) { + return new ArrayList(0); + } + + public List locationsOfLine(String stratumID, + String sourceName, + int lineNumber) { + return new ArrayList(0); + } + + public List locationsOfLine(SDE.Stratum stratum, + String sourceName, + int lineNumber) { + return new ArrayList(0); + } + + public Location locationOfCodeIndex(long codeIndex) { + return null; + } + + LineInfo codeIndexToLineInfo(SDE.Stratum stratum, + long codeIndex) { + + if (stratum.isJava()) { + return new BaseLineInfo(-1, declaringType); + } else { + return new StratumLineInfo(stratum.id(), -1, + null, null); + } + } + + public List variables() throws AbsentInformationException { + throw new AbsentInformationException(); + } + + public List variablesByName(String name) throws AbsentInformationException { + throw new AbsentInformationException(); + } + + public List arguments() throws AbsentInformationException { + throw new AbsentInformationException(); + } + + public byte[] bytecodes() { + return new byte[0]; + } + + int argSlotCount() throws AbsentInformationException { + throw new InternalException("should not get here"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ObjectReferenceImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ObjectReferenceImpl.java new file mode 100644 index 00000000000..4911915c7f0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ObjectReferenceImpl.java @@ -0,0 +1,366 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import java.io.*; +import com.sun.jdi.*; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.debugger.OopHandle; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.Mark; +import sun.jvm.hotspot.oops.Instance; +import sun.jvm.hotspot.oops.Array; +import sun.jvm.hotspot.oops.OopUtilities; +import sun.jvm.hotspot.oops.Klass; +import sun.jvm.hotspot.oops.DefaultHeapVisitor; +import sun.jvm.hotspot.runtime.JavaThread; +import sun.jvm.hotspot.runtime.JavaVFrame; +import sun.jvm.hotspot.runtime.MonitorInfo; +import sun.jvm.hotspot.runtime.ObjectMonitor; +import sun.jvm.hotspot.runtime.Threads; +import sun.jvm.hotspot.utilities.Assert; + +import java.util.*; + +public class ObjectReferenceImpl extends ValueImpl implements ObjectReference { + private Oop saObject; + private long myID; + private boolean monitorInfoCached = false; + private ThreadReferenceImpl owningThread = null; + private List waitingThreads = null; // List + private int entryCount = 0; + + private static long nextID = 0L; + private static synchronized long nextID() { + return nextID++; + } + + ObjectReferenceImpl(VirtualMachine aVm, sun.jvm.hotspot.oops.Oop oRef) { + super(aVm); + saObject = oRef; + myID = nextID(); + } + + protected Oop ref() { + return saObject; + } + + public Type type() { + return referenceType(); + } + + public ReferenceType referenceType() { + Klass myKlass = ref().getKlass(); + return vm.referenceType(myKlass); + } + + public Value getValue(Field sig) { + List list = new ArrayList(1); + list.add(sig); + Map map = getValues(list); + return(Value)map.get(sig); + } + + public Map getValues(List theFields) { + //validateMirrors(theFields); + + List staticFields = new ArrayList(0); + int size = theFields.size(); + List instanceFields = new ArrayList(size); + + for (int i=0; i 0) { + map = referenceType().getValues(staticFields); + } else { + map = new HashMap(size); + } + + // Then get instance field(s) + size = instanceFields.size(); + for (int ii=0; ii 0 && refCount >= max) { + return true; + } + } catch (RuntimeException x) { + // Ignore RuntimeException thrown from vm.objectMirror(oop) + // for bad oop. It is possible to see some bad oop + // because heap might be iterating at no safepoint. + } + return false; + + } + }); + return objects; + } + + // refer to JvmtiEnvBase::count_locked_objects. + // Count the number of objects for a lightweight monitor. The obj + // parameter is object that owns the monitor so this routine will + // count the number of times the same object was locked by frames + // in JavaThread. i.e., we count total number of times the same + // object is (lightweight) locked by given thread. + private int countLockedObjects(JavaThread jt, Oop obj) { + int res = 0; + JavaVFrame frame = jt.getLastJavaVFrameDbg(); + while (frame != null) { + List monitors = frame.getMonitors(); + OopHandle givenHandle = obj.getHandle(); + for (Iterator itr = monitors.iterator(); itr.hasNext();) { + MonitorInfo mi = (MonitorInfo) itr.next(); + if (givenHandle.equals(mi.owner())) { + res++; + } + } + frame = (JavaVFrame) frame.javaSender(); + } + return res; + } + + // wrappers on same named method of Threads class + // returns List + private List getPendingThreads(ObjectMonitor mon) { + return vm.saVM().getThreads().getPendingThreads(mon); + } + + // returns List + private List getWaitingThreads(ObjectMonitor mon) { + return vm.saVM().getThreads().getWaitingThreads(mon); + } + + private JavaThread owningThreadFromMonitor(Address addr) { + return vm.saVM().getThreads().owningThreadFromMonitor(addr); + } + + // refer to JvmtiEnv::GetObjectMonitorUsage + private void computeMonitorInfo() { + monitorInfoCached = true; + Mark mark = saObject.getMark(); + ObjectMonitor mon = null; + Address owner = null; + // check for heavyweight monitor + if (! mark.hasMonitor()) { + // check for lightweight monitor + if (mark.hasLocker()) { + owner = mark.locker().getAddress(); // save the address of the Lock word + } + // implied else: no owner + } else { + // this object has a heavyweight monitor + mon = mark.monitor(); + + // The owner field of a heavyweight monitor may be NULL for no + // owner, a JavaThread * or it may still be the address of the + // Lock word in a JavaThread's stack. A monitor can be inflated + // by a non-owning JavaThread, but only the owning JavaThread + // can change the owner field from the Lock word to the + // JavaThread * and it may not have done that yet. + owner = mon.owner(); + } + + // find the owning thread + if (owner != null) { + owningThread = vm.threadMirror(owningThreadFromMonitor(owner)); + } + + // compute entryCount + if (owningThread != null) { + if (owningThread.getJavaThread().getAddress().equals(owner)) { + // the owner field is the JavaThread * + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "must have heavyweight monitor with JavaThread * owner"); + } + entryCount = (int) mark.monitor().recursions() + 1; + } else { + // The owner field is the Lock word on the JavaThread's stack + // so the recursions field is not valid. We have to count the + // number of recursive monitor entries the hard way. + entryCount = countLockedObjects(owningThread.getJavaThread(), saObject); + } + } + + // find the contenders & waiters + waitingThreads = new ArrayList(); + if (mon != null) { + // this object has a heavyweight monitor. threads could + // be contenders or waiters + // add all contenders + List pendingThreads = getPendingThreads(mon); + // convert the JavaThreads to ThreadReferenceImpls + for (Iterator itrPend = pendingThreads.iterator(); itrPend.hasNext();) { + waitingThreads.add(vm.threadMirror((JavaThread) itrPend.next())); + } + + // add all waiters (threads in Object.wait()) + // note that we don't do this JVMTI way. To do it JVMTI way, + // we would need to access ObjectWaiter list maintained in + // ObjectMonitor::_queue. But we don't have this struct exposed + // in vmStructs. We do waiters list in a way similar to getting + // pending threads list + List objWaitingThreads = getWaitingThreads(mon); + // convert the JavaThreads to ThreadReferenceImpls + for (Iterator itrWait = objWaitingThreads.iterator(); itrWait.hasNext();) { + waitingThreads.add(vm.threadMirror((JavaThread) itrWait.next())); + } + } + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof ObjectReferenceImpl)) { + ObjectReferenceImpl other = (ObjectReferenceImpl)obj; + return (ref().equals(other.ref())) && + super.equals(obj); + } else { + return false; + } + } + + public int hashCode() { + return saObject.hashCode(); + } + + public String toString() { + return "instance of " + referenceType().name() + "(id=" + uniqueID() + ")"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/PrimitiveTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/PrimitiveTypeImpl.java new file mode 100644 index 00000000000..7c32abbf139 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/PrimitiveTypeImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +abstract class PrimitiveTypeImpl extends TypeImpl implements PrimitiveType { + + PrimitiveTypeImpl(VirtualMachine vm) { + super(vm); + } + + /* + * Converts the given primitive value to a value of this type. + */ + abstract PrimitiveValue convert(PrimitiveValue value) throws InvalidTypeException; + + public String toString() { + return name(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/PrimitiveValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/PrimitiveValueImpl.java new file mode 100644 index 00000000000..64aa955c6de --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/PrimitiveValueImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public abstract class PrimitiveValueImpl extends ValueImpl + implements PrimitiveValue { + + PrimitiveValueImpl(VirtualMachine aVm) { + super(aVm); + } + + abstract public boolean booleanValue(); + abstract public byte byteValue(); + abstract public char charValue(); + abstract public short shortValue(); + abstract public int intValue(); + abstract public long longValue(); + abstract public float floatValue(); + abstract public double doubleValue(); + + /* + * The checked versions of the value accessors throw + * InvalidTypeException if the required conversion is + * narrowing and would result in the loss of information + * (either magnitude or precision). + * + * Default implementations here do no checking; subclasses + * override as necessary to do the proper checking. + */ + byte checkedByteValue() throws InvalidTypeException { + return byteValue(); + } + char checkedCharValue() throws InvalidTypeException { + return charValue(); + } + short checkedShortValue() throws InvalidTypeException { + return shortValue(); + } + int checkedIntValue() throws InvalidTypeException { + return intValue(); + } + long checkedLongValue() throws InvalidTypeException { + return longValue(); + } + float checkedFloatValue() throws InvalidTypeException { + return floatValue(); + } + + final boolean checkedBooleanValue() throws InvalidTypeException { + /* + * Always disallow a conversion to boolean from any other + * primitive + */ + if (this instanceof BooleanValue) { + return booleanValue(); + } else { + throw new InvalidTypeException("Can't convert non-boolean value to boolean"); + } + } + + final double checkedDoubleValue() throws InvalidTypeException { + /* + * Can't overflow by converting to double, so this method + * is never overridden + */ + return doubleValue(); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ReferenceTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ReferenceTypeImpl.java new file mode 100644 index 00000000000..68abf7c3d1e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ReferenceTypeImpl.java @@ -0,0 +1,963 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import java.io.*; + +import com.sun.jdi.*; + +import sun.jvm.hotspot.oops.Instance; +import sun.jvm.hotspot.oops.InstanceKlass; +import sun.jvm.hotspot.oops.ArrayKlass; +import sun.jvm.hotspot.oops.JVMDIClassStatus; +import sun.jvm.hotspot.oops.Klass; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.Symbol; +import sun.jvm.hotspot.oops.DefaultHeapVisitor; +import sun.jvm.hotspot.utilities.Assert; + +import java.util.*; +import java.lang.ref.SoftReference; + +public abstract class ReferenceTypeImpl extends TypeImpl +implements ReferenceType { + protected Klass saKlass; // This can be an InstanceKlass or an ArrayKlass + protected Symbol typeNameSymbol; // This is used in vm.classesByName to speedup search + private int modifiers = -1; + private String signature = null; + private SoftReference sdeRef = null; + private SoftReference fieldsCache; + private SoftReference allFieldsCache; + private SoftReference methodsCache; + private SoftReference allMethodsCache; + private SoftReference nestedTypesCache; + + /* to mark when no info available */ + static final SDE NO_SDE_INFO_MARK = new SDE(); + + protected ReferenceTypeImpl(VirtualMachine aVm, sun.jvm.hotspot.oops.Klass klass) { + super(aVm); + saKlass = klass; + typeNameSymbol = saKlass.getName(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(typeNameSymbol != null, "null type name for a Klass"); + } + } + + Symbol typeNameAsSymbol() { + return typeNameSymbol; + } + + Method getMethodMirror(sun.jvm.hotspot.oops.Method ref) { + // SA creates new Method objects when they are referenced which means + // that the incoming object might not be the same object as on our + // even though it is the same method. So do an address compare by + // calling equals rather than just reference compare. + Iterator it = methods().iterator(); + while (it.hasNext()) { + MethodImpl method = (MethodImpl)it.next(); + if (ref.equals(method.ref())) { + return method; + } + } + throw new IllegalArgumentException("Invalid method id: " + ref); + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof ReferenceTypeImpl)) { + ReferenceTypeImpl other = (ReferenceTypeImpl)obj; + return (ref().equals(other.ref())) && + (vm.equals(other.virtualMachine())); + } else { + return false; + } + } + + public int hashCode() { + return saKlass.hashCode(); + } + + public int compareTo(Object object) { + /* + * Note that it is critical that compareTo() == 0 + * implies that equals() == true. Otherwise, TreeSet + * will collapse classes. + * + * (Classes of the same name loaded by different class loaders + * or in different VMs must not return 0). + */ + ReferenceTypeImpl other = (ReferenceTypeImpl)object; + int comp = name().compareTo(other.name()); + if (comp == 0) { + Oop rf1 = ref(); + Oop rf2 = other.ref(); + // optimize for typical case: refs equal and VMs equal + if (rf1.equals(rf2)) { + // sequenceNumbers are always positive + comp = vm.sequenceNumber - + ((VirtualMachineImpl)(other.virtualMachine())).sequenceNumber; + } else { + comp = rf1.getHandle().minus(rf2.getHandle()) < 0? -1 : 1; + } + } + return comp; + } + + public String signature() { + if (signature == null) { + signature = saKlass.signature(); + } + return signature; + } + + // refer to JvmtiEnv::GetClassSignature. + // null is returned for array klasses. + public String genericSignature() { + if (saKlass instanceof ArrayKlass) { + return null; + } else { + Symbol genSig = ((InstanceKlass)saKlass).getGenericSignature(); + return (genSig != null)? genSig.asString() : null; + } + } + + public ClassLoaderReference classLoader() { + Instance xx = (Instance)(((InstanceKlass)saKlass).getClassLoader()); + return (ClassLoaderReferenceImpl)vm.classLoaderMirror(xx); + } + + public boolean isPublic() { + return((modifiers() & VMModifiers.PUBLIC) != 0); + } + + public boolean isProtected() { + return((modifiers() & VMModifiers.PROTECTED) != 0); + } + + public boolean isPrivate() { + return((modifiers() & VMModifiers.PRIVATE) != 0); + } + + public boolean isPackagePrivate() { + return !isPublic() && !isPrivate() && !isProtected(); + } + + public boolean isAbstract() { + return((modifiers() & VMModifiers.ABSTRACT) != 0); + } + + public boolean isFinal() { + return((modifiers() & VMModifiers.FINAL) != 0); + } + + public boolean isStatic() { + return((modifiers() & VMModifiers.STATIC) != 0); + } + + public boolean isPrepared() { + return (saKlass.getClassStatus() & JVMDIClassStatus.PREPARED) != 0; + } + + final void checkPrepared() throws ClassNotPreparedException { + if (! isPrepared()) { + throw new ClassNotPreparedException(); + } + } + + public boolean isVerified() { + return (saKlass.getClassStatus() & JVMDIClassStatus.VERIFIED) != 0; + } + + public boolean isInitialized() { + return (saKlass.getClassStatus() & JVMDIClassStatus.INITIALIZED) != 0; + } + + public boolean failedToInitialize() { + return (saKlass.getClassStatus() & JVMDIClassStatus.ERROR) != 0; + } + + private boolean isThrowableBacktraceField(sun.jvm.hotspot.oops.Field fld) { + // refer to JvmtiEnv::GetClassFields in jvmtiEnv.cpp. + // We want to filter out java.lang.Throwable.backtrace (see 4446677). + // It contains some methodOops that aren't quite real Objects. + if (fld.getFieldHolder().getName().equals(vm.javaLangThrowable()) && + fld.getID().getName().equals("backtrace")) { + return true; + } else { + return false; + } + } + + public final List fields() throws ClassNotPreparedException { + List fields = (fieldsCache != null)? (List) fieldsCache.get() : null; + if (fields == null) { + checkPrepared(); + if (saKlass instanceof ArrayKlass) { + fields = new ArrayList(0); + } else { + // Get a list of the sa Field types + List saFields = ((InstanceKlass)saKlass).getImmediateFields(); + + // Create a list of our Field types + int len = saFields.size(); + fields = new ArrayList(len); + for (int ii = 0; ii < len; ii++) { + sun.jvm.hotspot.oops.Field curField = (sun.jvm.hotspot.oops.Field)saFields.get(ii); + if (! isThrowableBacktraceField(curField)) { + fields.add(new FieldImpl(vm, this, curField)); + } + } + } + fields = Collections.unmodifiableList(fields); + fieldsCache = new SoftReference(fields); + } + return fields; + } + + public final List allFields() throws ClassNotPreparedException { + List allFields = (allFieldsCache != null)? (List) allFieldsCache.get() : null; + if (allFields == null) { + checkPrepared(); + if (saKlass instanceof ArrayKlass) { + // is 'length' a field of array klasses? To maintain + // consistency with JVMDI-JDI we return 0 size. + allFields = new ArrayList(0); + } else { + List saFields; + + // Get a list of the sa Field types + saFields = ((InstanceKlass)saKlass).getAllFields(); + + // Create a list of our Field types + int len = saFields.size(); + allFields = new ArrayList(len); + for (int ii = 0; ii < len; ii++) { + sun.jvm.hotspot.oops.Field curField = (sun.jvm.hotspot.oops.Field)saFields.get(ii); + if (! isThrowableBacktraceField(curField)) { + allFields.add(new FieldImpl(vm, vm.referenceType(curField.getFieldHolder()), curField)); + } + } + } + allFields = Collections.unmodifiableList(allFields); + allFieldsCache = new SoftReference(allFields); + } + return allFields; + } + + abstract List inheritedTypes(); + + void addVisibleFields(List visibleList, Map visibleTable, List ambiguousNames) { + List list = visibleFields(); + Iterator iter = list.iterator(); + while (iter.hasNext()) { + Field field = (Field)iter.next(); + String name = field.name(); + if (!ambiguousNames.contains(name)) { + Field duplicate = (Field)visibleTable.get(name); + if (duplicate == null) { + visibleList.add(field); + visibleTable.put(name, field); + } else if (!field.equals(duplicate)) { + ambiguousNames.add(name); + visibleTable.remove(name); + visibleList.remove(duplicate); + } else { + // identical field from two branches; do nothing + } + } + } + } + + public final List visibleFields() throws ClassNotPreparedException { + checkPrepared(); + /* + * Maintain two different collections of visible fields. The + * list maintains a reasonable order for return. The + * hash map provides an efficient way to lookup visible fields + * by name, important for finding hidden or ambiguous fields. + */ + List visibleList = new ArrayList(); + Map visibleTable = new HashMap(); + + /* Track fields removed from above collection due to ambiguity */ + List ambiguousNames = new ArrayList(); + + /* Add inherited, visible fields */ + List types = inheritedTypes(); + Iterator iter = types.iterator(); + while (iter.hasNext()) { + /* + * TO DO: Be defensive and check for cyclic interface inheritance + */ + ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next(); + type.addVisibleFields(visibleList, visibleTable, ambiguousNames); + } + + /* + * Insert fields from this type, removing any inherited fields they + * hide. + */ + List retList = new ArrayList(fields()); + iter = retList.iterator(); + while (iter.hasNext()) { + Field field = (Field)iter.next(); + Field hidden = (Field)visibleTable.get(field.name()); + if (hidden != null) { + visibleList.remove(hidden); + } + } + retList.addAll(visibleList); + return retList; + } + + public final Field fieldByName(String fieldName) throws ClassNotPreparedException { + java.util.List searchList; + Field f; + + // visibleFields calls checkPrepared + searchList = visibleFields(); + + for (int i=0; i 0) { + sb.append(typeName.substring(index, nextIndex)); + sb.append(java.io.File.separatorChar); + index = nextIndex + 1; + } + return sb.toString(); + } + + public String sourceDebugExtension() + throws AbsentInformationException { + if (!vm.canGetSourceDebugExtension()) { + throw new UnsupportedOperationException(); + } + SDE sde = sourceDebugExtensionInfo(); + if (sde == NO_SDE_INFO_MARK) { + throw new AbsentInformationException(); + } + return sde.sourceDebugExtension; + } + + private SDE sourceDebugExtensionInfo() { + if (!vm.canGetSourceDebugExtension()) { + return NO_SDE_INFO_MARK; + } + SDE sde = null; + sde = (sdeRef == null) ? null : (SDE)sdeRef.get(); + if (sde == null) { + String extension = null; + if (saKlass instanceof InstanceKlass) { + Symbol sdeSym = ((InstanceKlass)saKlass).getSourceDebugExtension(); + extension = (sdeSym != null)? sdeSym.asString() : null; + } + if (extension == null) { + sde = NO_SDE_INFO_MARK; + } else { + sde = new SDE(extension); + } + sdeRef = new SoftReference(sde); + } + return sde; + } + + public List availableStrata() { + SDE sde = sourceDebugExtensionInfo(); + if (sde.isValid()) { + return sde.availableStrata(); + } else { + List strata = new ArrayList(); + strata.add(SDE.BASE_STRATUM_NAME); + return strata; + } + } + + /** + * Always returns non-null stratumID + */ + public String defaultStratum() { + SDE sdei = sourceDebugExtensionInfo(); + if (sdei.isValid()) { + return sdei.defaultStratumId; + } else { + return SDE.BASE_STRATUM_NAME; + } + } + + public final int modifiers() { + if (modifiers == -1) { + modifiers = getModifiers(); + } + return modifiers; + } + + // new method since 1.6. + // Real body will be supplied later. + public List instances(long maxInstances) { + if (!vm.canGetInstanceInfo()) { + throw new UnsupportedOperationException( + "target does not support getting instances"); + } + + if (maxInstances < 0) { + throw new IllegalArgumentException("maxInstances is less than zero: " + + maxInstances); + } + + final List objects = new ArrayList(0); + if (isAbstract() || (this instanceof InterfaceType)) { + return objects; + } + + final Klass givenKls = this.ref(); + final long max = maxInstances; + vm.saObjectHeap().iterate(new DefaultHeapVisitor() { + private long instCount=0; + public boolean doObj(Oop oop) { + if (givenKls.equals(oop.getKlass())) { + objects.add(vm.objectMirror(oop)); + instCount++; + } + if (max > 0 && instCount >= max) { + return true; + } + return false; + } + }); + return objects; + } + + int getModifiers() { + return (int) saKlass.getClassModifiers(); + } + + public List allLineLocations() + throws AbsentInformationException { + return allLineLocations(vm.getDefaultStratum(), null); + } + + public List allLineLocations(String stratumID, String sourceName) + throws AbsentInformationException { + checkPrepared(); + boolean someAbsent = false; // A method that should have info, didn't + SDE.Stratum stratum = stratum(stratumID); + List list = new ArrayList(); // location list + + for (Iterator iter = methods().iterator(); iter.hasNext(); ) { + MethodImpl method = (MethodImpl)iter.next(); + try { + list.addAll( + method.allLineLocations(stratum.id(), sourceName)); + } catch(AbsentInformationException exc) { + someAbsent = true; + } + } + + // If we retrieved no line info, and at least one of the methods + // should have had some (as determined by an + // AbsentInformationException being thrown) then we rethrow + // the AbsentInformationException. + if (someAbsent && list.size() == 0) { + throw new AbsentInformationException(); + } + return list; + } + + public List locationsOfLine(int lineNumber) + throws AbsentInformationException { + return locationsOfLine(vm.getDefaultStratum(), + null, + lineNumber); + } + + public List locationsOfLine(String stratumID, + String sourceName, + int lineNumber) + throws AbsentInformationException { + checkPrepared(); + // A method that should have info, didn't + boolean someAbsent = false; + // A method that should have info, did + boolean somePresent = false; + List methods = methods(); + SDE.Stratum stratum = stratum(stratumID); + + List list = new ArrayList(); + + Iterator iter = methods.iterator(); + while(iter.hasNext()) { + MethodImpl method = (MethodImpl)iter.next(); + // eliminate native and abstract to eliminate + // false positives + if (!method.isAbstract() && + !method.isNative()) { + try { + list.addAll( + method.locationsOfLine(stratum.id(), + sourceName, + lineNumber)); + somePresent = true; + } catch(AbsentInformationException exc) { + someAbsent = true; + } + } + } + if (someAbsent && !somePresent) { + throw new AbsentInformationException(); + } + return list; + } + + Klass ref() { + return saKlass; + } + + + /* + * Return true if an instance of this type + * can be assigned to a variable of the given type + */ + abstract boolean isAssignableTo(ReferenceType type); + + boolean isAssignableFrom(ReferenceType type) { + return ((ReferenceTypeImpl)type).isAssignableTo(this); + } + + boolean isAssignableFrom(ObjectReference object) { + return object == null || + isAssignableFrom(object.referenceType()); + } + + int indexOf(Method method) { + // Make sure they're all here - the obsolete method + // won't be found and so will have index -1 + return methods().indexOf(method); + } + + int indexOf(Field field) { + // Make sure they're all here + return fields().indexOf(field); + } + + private static boolean isPrimitiveArray(String signature) { + int i = signature.lastIndexOf('['); + /* + * TO DO: Centralize JNI signature knowledge. + * + * Ref: + * jdk1.4/doc/guide/jpda/jdi/com/sun/jdi/doc-files/signature.html + */ + boolean isPA; + if (i < 0) { + isPA = false; + } else { + char c = signature.charAt(i + 1); + isPA = (c != 'L'); + } + return isPA; + } + + Type findType(String signature) throws ClassNotLoadedException { + Type type; + if (signature.length() == 1) { + /* OTI FIX: Must be a primitive type or the void type */ + char sig = signature.charAt(0); + if (sig == 'V') { + type = vm.theVoidType(); + } else { + type = vm.primitiveTypeMirror(sig); + } + } else { + // Must be a reference type. + ClassLoaderReferenceImpl loader = + (ClassLoaderReferenceImpl)classLoader(); + if ((loader == null) || + (isPrimitiveArray(signature)) //Work around 4450091 + ) { + // Caller wants type of boot class field + type = vm.findBootType(signature); + } else { + // Caller wants type of non-boot class field + type = loader.findType(signature); + } + } + return type; + } + + String loaderString() { + if (classLoader() != null) { + return "loaded by " + classLoader().toString(); + } else { + return "loaded by bootstrap loader"; + } + } + + long uniqueID() { + return vm.getAddressValue(ref()); + } + + // new method since 1.6 + public int majorVersion() { + if (!vm.canGetClassFileVersion()) { + throw new UnsupportedOperationException("Cannot get class file version"); + } + return (int)((InstanceKlass)saKlass).majorVersion(); + } + + // new method since 1.6 + public int minorVersion() { + if (!vm.canGetClassFileVersion()) { + throw new UnsupportedOperationException("Cannot get class file version"); + } + return (int)((InstanceKlass)saKlass).minorVersion(); + } + + // new method since 1.6 + public int constantPoolCount() { + if (!vm.canGetConstantPool()) { + throw new UnsupportedOperationException("Cannot get constant pool"); + } + if (saKlass instanceof ArrayKlass) { + return 0; + } else { + return (int)((InstanceKlass)saKlass).getConstants().getLength(); + } + } + + // new method since 1.6 + public byte[] constantPool() { + if (!vm.canGetConstantPool()) { + throw new UnsupportedOperationException("Cannot get constant pool"); + } + if (this instanceof ArrayType || this instanceof PrimitiveType) { + byte bytes[] = new byte[0]; + return bytes; + } else { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + try { + ((InstanceKlass)saKlass).getConstants().writeBytes(bs); + } catch (IOException ex) { + ex.printStackTrace(); + byte bytes[] = new byte[0]; + return bytes; + } + return bs.toByteArray(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SACoreAttachingConnector.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SACoreAttachingConnector.java new file mode 100644 index 00000000000..85ab2f9ccb2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SACoreAttachingConnector.java @@ -0,0 +1,156 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.connect.*; +import com.sun.jdi.Bootstrap; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.VirtualMachineManager; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; + +public class SACoreAttachingConnector extends ConnectorImpl implements AttachingConnector { + + static final String ARG_COREFILE = "core"; + static final String ARG_JAVA_EXECUTABLE = "javaExecutable"; + private Transport transport; + + public SACoreAttachingConnector(com.sun.tools.jdi.VirtualMachineManagerService ignored) { + this(); + } + + public SACoreAttachingConnector() { + super(); + //fixme jjh Must create resources for these strings + addStringArgument( + ARG_JAVA_EXECUTABLE, + "Java Executable", //getString("sa.javaExecutable.label"), + "Pathname of Java Executable", //getString("sa.javaExecutable.description"); + "", + true); + + addStringArgument( + ARG_COREFILE, + "Corefile", // getString("sa.CoreFile.label"), + "Pathname of a corefile from a Java Process", //getString("sa.CoreFile.description"), + "core", + false); + + transport = new Transport() { + public String name() { + return "filesystem"; + } + }; + } + + // security check to see whether the caller can perform attach + private void checkCoreAttach(String corefile) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + // whether the caller can link against SA native library? + checkNativeLink(sm, System.getProperty("os.name")); + // check whether the caller can read the core file? + sm.checkRead(corefile); + } catch (SecurityException se) { + throw new SecurityException("permission denied to attach to " + corefile); + } + } + } + + private VirtualMachine createVirtualMachine(Class vmImplClass, + String javaExec, String corefile) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + java.lang.reflect.Method connectByCoreMethod = vmImplClass.getMethod( + "createVirtualMachineForCorefile", + new Class[] { + VirtualMachineManager.class, + String.class, String.class, + Integer.TYPE + }); + return (VirtualMachine) connectByCoreMethod.invoke(null, + new Object[] { + Bootstrap.virtualMachineManager(), + javaExec, + corefile, + new Integer(0) + }); + } + + public VirtualMachine attach(Map arguments) throws IOException, + IllegalConnectorArgumentsException { + String javaExec = argument(ARG_JAVA_EXECUTABLE, arguments).value(); + if (javaExec == null || javaExec.equals("")) { + throw new IllegalConnectorArgumentsException("javaExec should be non-null and non-empty", + ARG_JAVA_EXECUTABLE); + } + String corefile = argument(ARG_COREFILE, arguments).value(); + if (corefile == null || corefile.equals("")) { + throw new IllegalConnectorArgumentsException("corefile should be non-null and non-empty", + ARG_COREFILE); + } + + checkCoreAttach(corefile); + + VirtualMachine myVM = null; + try { + try { + Class vmImplClass = loadVirtualMachineImplClass(); + myVM = createVirtualMachine(vmImplClass, javaExec, corefile); + } catch (InvocationTargetException ite) { + Class vmImplClass = handleVMVersionMismatch(ite); + if (vmImplClass != null) { + return createVirtualMachine(vmImplClass, javaExec, corefile); + } else { + throw ite; + } + } + } catch (Exception ee) { + if (DEBUG) { + System.out.println("VirtualMachineImpl() got an exception:"); + ee.printStackTrace(); + System.out.println("coreFile = " + corefile + ", javaExec = " + javaExec); + } + throw (IOException) new IOException().initCause(ee); + } + setVMDisposeObserver(myVM); + return myVM; + } + + public String name() { + return "sun.jvm.hotspot.jdi.SACoreAttachingConnector"; + } + + public String description() { + return getString("This connector allows you to attach to a core file using the Serviceability Agent"); + } + + public Transport transport() { + return transport; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SADebugServer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SADebugServer.java new file mode 100644 index 00000000000..0d39c98e0b3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SADebugServer.java @@ -0,0 +1,65 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +public final class SADebugServer { + // do not allow instance creation + private SADebugServer() {} + + private static void usage() { + java.io.PrintStream out = System.out; + out.println("Usage: jsadebugd [options] [server-id]"); + out.println("\t\t(to connect to a live java process)"); + out.println(" or jsadebugd [options] [server-id]"); + out.println("\t\t(to connect to a core file produced by )"); + out.println("\t\tserver-id is an optional unique id for this debug server, needed "); + out.println("\t\tif multiple debug servers are run on the same machine"); + out.println("where options include:"); + out.println(" -h | -help\tto print this help message"); + System.exit(1); + } + + public static void main(String[] args) { + if ((args.length < 1) || (args.length > 3)) { + usage(); + } + + // Attempt to handle "-h" or "-help" + if (args[0].startsWith("-")) { + usage(); + } + + // By default, SA agent classes prefer dbx debugger to proc debugger + // and Windows process debugger to windbg debugger. SA expects + // special properties to be set to choose other debuggers. For SA/JDI, + // we choose proc, windbg debuggers instead of the defaults. + + System.setProperty("sun.jvm.hotspot.debugger.useProcDebugger", "true"); + System.setProperty("sun.jvm.hotspot.debugger.useWindbgDebugger", "true"); + + // delegate to the actual SA debug server. + sun.jvm.hotspot.DebugServer.main(args); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SADebugServerAttachingConnector.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SADebugServerAttachingConnector.java new file mode 100644 index 00000000000..8a05272324d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SADebugServerAttachingConnector.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.connect.*; +import com.sun.jdi.Bootstrap; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.VirtualMachineManager; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; + +public class SADebugServerAttachingConnector extends ConnectorImpl implements AttachingConnector { + + static final String ARG_DEBUG_SERVER_NAME = "debugServerName"; + private Transport transport; + + public SADebugServerAttachingConnector(com.sun.tools.jdi.VirtualMachineManagerService ignored) { + this(); + } + + public SADebugServerAttachingConnector() { + // fixme jjh create resources for the these strings, + addStringArgument( + ARG_DEBUG_SERVER_NAME, + "Debug Server", //getString("sa.debugServer.label"), + "Name of a remote SA Debug Server", //getString("sa.debugServer.description"); + "", + true); + transport = new Transport() { + public String name() { + return "RMI"; + } + }; + } + + private VirtualMachine createVirtualMachine(Class vmImplClass, + String debugServerName) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + java.lang.reflect.Method connectByServerMethod = + vmImplClass.getMethod( + "createVirtualMachineForServer", + new Class[] { + VirtualMachineManager.class, + String.class, + Integer.TYPE + }); + return (VirtualMachine) connectByServerMethod.invoke(null, + new Object[] { + Bootstrap.virtualMachineManager(), + debugServerName, + new Integer(0) + }); + } + + public VirtualMachine attach(Map arguments) throws IOException, + IllegalConnectorArgumentsException { + String debugServerName = argument(ARG_DEBUG_SERVER_NAME, arguments).value(); + if (debugServerName == null || debugServerName.equals("")) { + throw new IllegalConnectorArgumentsException("debugServerName should be non-null and non-empty", + ARG_DEBUG_SERVER_NAME); + } + VirtualMachine myVM; + try { + try { + Class vmImplClass = loadVirtualMachineImplClass(); + myVM = createVirtualMachine(vmImplClass, debugServerName); + } catch (InvocationTargetException ite) { + Class vmImplClass = handleVMVersionMismatch(ite); + if (vmImplClass != null) { + return createVirtualMachine(vmImplClass, debugServerName); + } else { + throw ite; + } + } + } catch (Exception ee) { + if (DEBUG) { + System.out.println("VirtualMachineImpl() got an exception:"); + ee.printStackTrace(); + System.out.println("debug server name = " + debugServerName); + } + throw (IOException) new IOException().initCause(ee); + } + setVMDisposeObserver(myVM); + return myVM; + } + + public String name() { + return "sun.jvm.hotspot.jdi.SADebugServerAttachingConnector"; + } + + public String description() { + return getString("This connector allows you to attach to a Java Process via a debug server with the Serviceability Agent"); + } + + public Transport transport() { + return transport; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SAJDIClassLoader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SAJDIClassLoader.java new file mode 100644 index 00000000000..4483cf1b38b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SAJDIClassLoader.java @@ -0,0 +1,154 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import java.io.*; +import java.net.*; + +/* + * This class loader is used for two different reasons: + * + * 1) To support multiple simultaneous debuggees. + * + * SA's architecture does not allow us to use multiple simultaneous + * debuggees. This is because of lots of static fields caching + * vmStruct fields and singleton assumption in classes such as + * 'sun.jvm.hotspot.runtime.VM'. Hence, we use instances of this + * class loader to create a separate namespace for each debuggee VM. + * + * 2) To support cross VM version debugging. + * + * SA has very close dependency on VM data structures. Due to this, a + * version of SA can only support debuggees running a same dot-dot release and + * update releases only. For eg. this version of SA supports only 1.4.2 and + * 1.4.2_xx releases only. But, users may want to debug debuggees running + * a different version of VM. To support this, we use an instance of this + * class loader to load classes from corresponding sa-jdi.jar. + * + * Note that JDI classes would still be loaded from the debugger's tools.jar + * and not from debuggee's tools.jar. This means that if JDI interface evolved + * b/w debuggee and debugger VM versions, user may still get problems. This is + * the case when debugger runs on 1.5.0 and debuggee runs on 1.4.2. Because JDI + * evolved b/w these versions (generics, enum, varargs etc.), 1.4.2 sa-jdi.jar + * won't implement 1.5.0 JDI properly and user would get verifier errors. This + * class loader solution is suited for different dot-dot release where JDI will + * not evolve but VM data structures might change and SA implementation might + * have to change. For example, a debuggee running 1.5.1 VM can be debugged + * with debugger running on 1.5.0 VM. Here, JDI is same but VM data structures + * could still change. + */ + +class SAJDIClassLoader extends URLClassLoader { + private static final boolean DEBUG; + static { + DEBUG = System.getProperty("sun.jvm.hotspot.jdi.SAJDIClassLoader.DEBUG") != null; + } + + private ClassLoader parent; + private boolean classPathSet; + + SAJDIClassLoader(ClassLoader parent) { + super(new URL[0], parent); + this.parent = parent; + } + + SAJDIClassLoader(ClassLoader parent, String classPath) { + this(parent); + this.classPathSet = true; + try { + addURL(new File(classPath).toURL()); + } catch(MalformedURLException mue) { + throw new RuntimeException(mue); + } + } + + public synchronized Class loadClass(String name) + throws ClassNotFoundException { + // First, check if the class has already been loaded + Class c = findLoadedClass(name); + if (c == null) { + /* If we are loading any class in 'sun.jvm.hotspot.' or any of the + * sub-packages (except for 'debugger' sub-pkg. please refer below), + * we load it by 'this' loader. Or else, we forward the request to + * 'parent' loader, system loader etc. (rest of the code follows + * the patten in java.lang.ClassLoader.loadClass). + * + * 'sun.jvm.hotspot.debugger.' and sub-package classes are + * also loaded by parent loader. This is done for two reasons: + * + * 1. to avoid code bloat by too many classes. + * 2. to avoid loading same native library multiple times + * from multiple class loaders (which results in getting a + * UnsatisifiedLinkageError from System.loadLibrary). + */ + + if (name.startsWith("sun.jvm.hotspot.") && + !name.startsWith("sun.jvm.hotspot.debugger.")) { + return findClass(name); + } + if (parent != null) { + c = parent.loadClass(name); + } else { + c = findSystemClass(name); + } + } + return c; + } + + protected Class findClass(String name) throws ClassNotFoundException { + if (DEBUG) { + System.out.println("SA/JDI loader: about to load " + name); + } + if (classPathSet) { + return super.findClass(name); + } else { + byte[] b = null; + try { + InputStream in = getResourceAsStream(name.replace('.', '/') + ".class"); + // Read until end of stream is reached + b = new byte[1024]; + int total = 0; + int len = 0; + while ((len = in.read(b, total, b.length - total)) != -1) { + total += len; + if (total >= b.length) { + byte[] tmp = new byte[total * 2]; + System.arraycopy(b, 0, tmp, 0, total); + b = tmp; + } + } + // Trim array to correct size, if necessary + if (total != b.length) { + byte[] tmp = new byte[total]; + System.arraycopy(b, 0, tmp, 0, total); + b = tmp; + } + } catch (Exception exp) { + throw (ClassNotFoundException) new ClassNotFoundException().initCause(exp); + } + return defineClass(name, b, 0, b.length); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SAPIDAttachingConnector.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SAPIDAttachingConnector.java new file mode 100644 index 00000000000..32bcd31f428 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SAPIDAttachingConnector.java @@ -0,0 +1,143 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.connect.*; +import com.sun.jdi.Bootstrap; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.VirtualMachineManager; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; + +public class SAPIDAttachingConnector extends ConnectorImpl implements AttachingConnector { + static final String ARG_PID = "pid"; + private Transport transport; + + public SAPIDAttachingConnector(com.sun.tools.jdi.VirtualMachineManagerService ignored) { + this(); + } + + public SAPIDAttachingConnector() { + super(); + // fixme jjh: create resources for the these strings, + addStringArgument( + ARG_PID, + "PID", //getString("sa.pid.label"), + "PID of a Java process", //getString("sa.pid.description"); + "", + true); + transport = new Transport() { + public String name() { + return "local process"; + } + }; + } + + // security check to see whether the caller can perform attach + private void checkProcessAttach(int pid) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + String os = System.getProperty("os.name"); + try { + // Whether the caller can perform link against SA native library? + checkNativeLink(sm, os); + if (os.equals("SunOS") || os.equals("Linux")) { + // Whether the caller can read /proc/ file? + sm.checkRead("/proc/" + pid); + } + } catch (SecurityException se) { + throw new SecurityException("permission denied to attach to " + pid); + } + } + } + + private VirtualMachine createVirtualMachine(Class virtualMachineImplClass, int pid) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + java.lang.reflect.Method createByPIDMethod + = virtualMachineImplClass.getMethod("createVirtualMachineForPID", + new Class[] { + VirtualMachineManager.class, + Integer.TYPE, Integer.TYPE + }); + return (VirtualMachine) createByPIDMethod.invoke(null, + new Object[] { + Bootstrap.virtualMachineManager(), + new Integer(pid), + new Integer(0) + }); + } + + public VirtualMachine attach(Map arguments) throws IOException, + IllegalConnectorArgumentsException { + int pid = 0; + try { + pid = Integer.parseInt(argument(ARG_PID, arguments).value()); + } catch (NumberFormatException nfe) { + throw (IllegalConnectorArgumentsException) new IllegalConnectorArgumentsException + (nfe.getMessage(), ARG_PID).initCause(nfe); + } + + checkProcessAttach(pid); + + VirtualMachine myVM = null; + try { + try { + Class vmImplClass = loadVirtualMachineImplClass(); + myVM = createVirtualMachine(vmImplClass, pid); + } catch (InvocationTargetException ite) { + Class vmImplClass = handleVMVersionMismatch(ite); + if (vmImplClass != null) { + return createVirtualMachine(vmImplClass, pid); + } else { + throw ite; + } + } + } catch (Exception ee) { + if (DEBUG) { + System.out.println("VirtualMachineImpl() got an exception:"); + ee.printStackTrace(); + System.out.println("pid = " + pid); + } + throw (IOException) new IOException().initCause(ee); + } + setVMDisposeObserver(myVM); + return myVM; + } + + public String name() { + return "sun.jvm.hotspot.jdi.SAPIDAttachingConnector"; + } + + public String description() { + return getString("This connector allows you to attach to a Java process using the Serviceability Agent"); + } + + public Transport transport() { + return transport; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SDE.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SDE.java new file mode 100644 index 00000000000..f80842959b1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SDE.java @@ -0,0 +1,685 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +import java.util.*; +import java.io.File; + +class SDE { + private static final int INIT_SIZE_FILE = 3; + private static final int INIT_SIZE_LINE = 100; + private static final int INIT_SIZE_STRATUM = 3; + + static final String BASE_STRATUM_NAME = "Java"; + + /* for C capatibility */ + static final String NullString = null; + + private class FileTableRecord { + int fileId; + String sourceName; + String sourcePath; // do not read - use accessor + boolean isConverted = false; + + /** + * Return the sourcePath, computing it if not set. + * If set, convert '/' in the sourcePath to the + * local file separator. + */ + String getSourcePath(ReferenceTypeImpl refType) { + if (!isConverted) { + if (sourcePath == null) { + sourcePath = refType.baseSourceDir() + sourceName; + } else { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < sourcePath.length(); ++i) { + char ch = sourcePath.charAt(i); + if (ch == '/') { + buf.append(File.separatorChar); + } else { + buf.append(ch); + } + } + sourcePath = buf.toString(); + } + isConverted = true; + } + return sourcePath; + } + } + + private class LineTableRecord { + int jplsStart; + int jplsEnd; + int jplsLineInc; + int njplsStart; + int njplsEnd; + int fileId; + } + + private class StratumTableRecord { + String id; + int fileIndex; + int lineIndex; + } + + class Stratum { + private final int sti; /* stratum index */ + + private Stratum(int sti) { + this.sti = sti; + } + + String id() { + return stratumTable[sti].id; + } + + boolean isJava() { + return sti == baseStratumIndex; + } + + /** + * Return all the sourceNames for this stratum. + * Look from our starting fileIndex upto the starting + * fileIndex of next stratum - can do this since there + * is always a terminator stratum. + * Default sourceName (the first one) must be first. + */ + List sourceNames(ReferenceTypeImpl refType) { + int i; + int fileIndexStart = stratumTable[sti].fileIndex; + /* one past end */ + int fileIndexEnd = stratumTable[sti+1].fileIndex; + List result = new ArrayList(fileIndexEnd - fileIndexStart); + for (i = fileIndexStart; i < fileIndexEnd; ++i) { + result.add(fileTable[i].sourceName); + } + return result; + } + + /** + * Return all the sourcePaths for this stratum. + * Look from our starting fileIndex upto the starting + * fileIndex of next stratum - can do this since there + * is always a terminator stratum. + * Default sourcePath (the first one) must be first. + */ + List sourcePaths(ReferenceTypeImpl refType) { + int i; + int fileIndexStart = stratumTable[sti].fileIndex; + /* one past end */ + int fileIndexEnd = stratumTable[sti+1].fileIndex; + List result = new ArrayList(fileIndexEnd - fileIndexStart); + for (i = fileIndexStart; i < fileIndexEnd; ++i) { + result.add(fileTable[i].getSourcePath(refType)); + } + return result; + } + + LineStratum lineStratum(ReferenceTypeImpl refType, + int jplsLine) { + int lti = stiLineTableIndex(sti, jplsLine); + if (lti < 0) { + return null; + } else { + return new LineStratum(sti, lti, refType, + jplsLine); + } + } + } + + class LineStratum { + private final int sti; /* stratum index */ + private final int lti; /* line table index */ + private final ReferenceTypeImpl refType; + private final int jplsLine; + private String sourceName = null; + private String sourcePath = null; + + private LineStratum(int sti, int lti, + ReferenceTypeImpl refType, + int jplsLine) { + this.sti = sti; + this.lti = lti; + this.refType = refType; + this.jplsLine = jplsLine; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof LineStratum)) { + LineStratum other = (LineStratum)obj; + return (lti == other.lti) && + (sti == other.sti) && + (lineNumber() == other.lineNumber()) && + (refType.equals(other.refType)); + } else { + return false; + } + } + + int lineNumber() { + return stiLineNumber(sti, lti, jplsLine); + } + + /** + * Fetch the source name and source path for + * this line, converting or constructing + * the source path if needed. + */ + void getSourceInfo() { + if (sourceName != null) { + // already done + return; + } + int fti = stiFileTableIndex(sti, lti); + if (fti == -1) { + throw new InternalError( + "Bad SourceDebugExtension, no matching source id " + + lineTable[lti].fileId + " jplsLine: " + jplsLine); + } + FileTableRecord ftr = fileTable[fti]; + sourceName = ftr.sourceName; + sourcePath = ftr.getSourcePath(refType); + } + + String sourceName() { + getSourceInfo(); + return sourceName; + } + + String sourcePath() { + getSourceInfo(); + return sourcePath; + } + } + + private FileTableRecord[] fileTable = null; + private LineTableRecord[] lineTable = null; + private StratumTableRecord[] stratumTable = null; + + private int fileIndex = 0; + private int lineIndex = 0; + private int stratumIndex = 0; + private int currentFileId = 0; + + private int defaultStratumIndex = -1; + private int baseStratumIndex = -2; /* so as not to match -1 above */ + private int sdePos = 0; + + final String sourceDebugExtension; + String jplsFilename = null; + String defaultStratumId = null; + boolean isValid = false; + + SDE(String sourceDebugExtension) { + this.sourceDebugExtension = sourceDebugExtension; + decode(); + } + + SDE() { + this.sourceDebugExtension = null; + createProxyForAbsentSDE(); + } + + char sdePeek() { + if (sdePos >= sourceDebugExtension.length()) { + syntax(); + } + return sourceDebugExtension.charAt(sdePos); + } + + char sdeRead() { + if (sdePos >= sourceDebugExtension.length()) { + syntax(); + } + return sourceDebugExtension.charAt(sdePos++); + } + + void sdeAdvance() { + sdePos++; + } + + void syntax() { + throw new InternalError("bad SourceDebugExtension syntax - position " + + sdePos); + } + + void syntax(String msg) { + throw new InternalError("bad SourceDebugExtension syntax: " + msg); + } + + void assureLineTableSize() { + int len = lineTable == null? 0 : lineTable.length; + if (lineIndex >= len) { + int i; + int newLen = len == 0? INIT_SIZE_LINE : len * 2; + LineTableRecord[] newTable = new LineTableRecord[newLen]; + for (i = 0; i < len; ++i) { + newTable[i] = lineTable[i]; + } + for (; i < newLen; ++i) { + newTable[i] = new LineTableRecord(); + } + lineTable = newTable; + } + } + + void assureFileTableSize() { + int len = fileTable == null? 0 : fileTable.length; + if (fileIndex >= len) { + int i; + int newLen = len == 0? INIT_SIZE_FILE : len * 2; + FileTableRecord[] newTable = new FileTableRecord[newLen]; + for (i = 0; i < len; ++i) { + newTable[i] = fileTable[i]; + } + for (; i < newLen; ++i) { + newTable[i] = new FileTableRecord(); + } + fileTable = newTable; + } + } + + void assureStratumTableSize() { + int len = stratumTable == null? 0 : stratumTable.length; + if (stratumIndex >= len) { + int i; + int newLen = len == 0? INIT_SIZE_STRATUM : len * 2; + StratumTableRecord[] newTable = new StratumTableRecord[newLen]; + for (i = 0; i < len; ++i) { + newTable[i] = stratumTable[i]; + } + for (; i < newLen; ++i) { + newTable[i] = new StratumTableRecord(); + } + stratumTable = newTable; + } + } + + String readLine() { + StringBuffer sb = new StringBuffer(); + char ch; + + ignoreWhite(); + while (((ch = sdeRead()) != '\n') && (ch != '\r')) { + sb.append((char)ch); + } + // check for CR LF + if ((ch == '\r') && (sdePeek() == '\n')) { + sdeRead(); + } + ignoreWhite(); // leading white + return sb.toString(); + } + + private int defaultStratumTableIndex() { + if ((defaultStratumIndex == -1) && (defaultStratumId != null)) { + defaultStratumIndex = + stratumTableIndex(defaultStratumId); + } + return defaultStratumIndex; + } + + int stratumTableIndex(String stratumId) { + int i; + + if (stratumId == null) { + return defaultStratumTableIndex(); + } + for (i = 0; i < (stratumIndex-1); ++i) { + if (stratumTable[i].id.equals(stratumId)) { + return i; + } + } + return defaultStratumTableIndex(); + } + + Stratum stratum(String stratumID) { + int sti = stratumTableIndex(stratumID); + return new Stratum(sti); + } + + List availableStrata() { + List strata = new ArrayList(); + + for (int i = 0; i < (stratumIndex-1); ++i) { + StratumTableRecord rec = stratumTable[i]; + strata.add(rec.id); + } + return strata; + } + +/***************************** + * below functions/methods are written to compile under either Java or C + * + * Needed support functions: + * sdePeek() + * sdeRead() + * sdeAdvance() + * readLine() + * assureLineTableSize() + * assureFileTableSize() + * assureStratumTableSize() + * syntax() + * + * stratumTableIndex(String) + * + * Needed support variables: + * lineTable + * lineIndex + * fileTable + * fileIndex + * currentFileId + * + * Needed types: + * String + * + * Needed constants: + * NullString + */ + + void ignoreWhite() { + char ch; + + while (((ch = sdePeek()) == ' ') || (ch == '\t')) { + sdeAdvance(); + } + } + + void ignoreLine() { + char ch; + + while (((ch = sdeRead()) != '\n') && (ch != '\r')) { + } + /* check for CR LF */ + if ((ch == '\r') && (sdePeek() == '\n')) { + sdeAdvance(); + } + ignoreWhite(); /* leading white */ + } + + int readNumber() { + int value = 0; + char ch; + + ignoreWhite(); + while (((ch = sdePeek()) >= '0') && (ch <= '9')) { + sdeAdvance(); + value = (value * 10) + ch - '0'; + } + ignoreWhite(); + return value; + } + + void storeFile(int fileId, String sourceName, String sourcePath) { + assureFileTableSize(); + fileTable[fileIndex].fileId = fileId; + fileTable[fileIndex].sourceName = sourceName; + fileTable[fileIndex].sourcePath = sourcePath; + ++fileIndex; + } + + void fileLine() { + int hasAbsolute = 0; /* acts as boolean */ + int fileId; + String sourceName; + String sourcePath = null; + + /* is there an absolute filename? */ + if (sdePeek() == '+') { + sdeAdvance(); + hasAbsolute = 1; + } + fileId = readNumber(); + sourceName = readLine(); + if (hasAbsolute == 1) { + sourcePath = readLine(); + } + + storeFile(fileId, sourceName, sourcePath); + } + + void storeLine(int jplsStart, int jplsEnd, int jplsLineInc, + int njplsStart, int njplsEnd, int fileId) { + assureLineTableSize(); + lineTable[lineIndex].jplsStart = jplsStart; + lineTable[lineIndex].jplsEnd = jplsEnd; + lineTable[lineIndex].jplsLineInc = jplsLineInc; + lineTable[lineIndex].njplsStart = njplsStart; + lineTable[lineIndex].njplsEnd = njplsEnd; + lineTable[lineIndex].fileId = fileId; + ++lineIndex; + } + + /** + * Parse line translation info. Syntax is + * [ # ] [ , ] : + * [ , ] CR + */ + void lineLine() { + int lineCount = 1; + int lineIncrement = 1; + int njplsStart; + int jplsStart; + + njplsStart = readNumber(); + + /* is there a fileID? */ + if (sdePeek() == '#') { + sdeAdvance(); + currentFileId = readNumber(); + } + + /* is there a line count? */ + if (sdePeek() == ',') { + sdeAdvance(); + lineCount = readNumber(); + } + + if (sdeRead() != ':') { + syntax(); + } + jplsStart = readNumber(); + if (sdePeek() == ',') { + sdeAdvance(); + lineIncrement = readNumber(); + } + ignoreLine(); /* flush the rest */ + + storeLine(jplsStart, + jplsStart + (lineCount * lineIncrement) -1, + lineIncrement, + njplsStart, + njplsStart + lineCount -1, + currentFileId); + } + + /** + * Until the next stratum section, everything after this + * is in stratumId - so, store the current indicies. + */ + void storeStratum(String stratumId) { + /* remove redundant strata */ + if (stratumIndex > 0) { + if ((stratumTable[stratumIndex-1].fileIndex + == fileIndex) && + (stratumTable[stratumIndex-1].lineIndex + == lineIndex)) { + /* nothing changed overwrite it */ + --stratumIndex; + } + } + /* store the results */ + assureStratumTableSize(); + stratumTable[stratumIndex].id = stratumId; + stratumTable[stratumIndex].fileIndex = fileIndex; + stratumTable[stratumIndex].lineIndex = lineIndex; + ++stratumIndex; + currentFileId = 0; + } + + /** + * The beginning of a stratum's info + */ + void stratumSection() { + storeStratum(readLine()); + } + + void fileSection() { + ignoreLine(); + while (sdePeek() != '*') { + fileLine(); + } + } + + void lineSection() { + ignoreLine(); + while (sdePeek() != '*') { + lineLine(); + } + } + + /** + * Ignore a section we don't know about. + */ + void ignoreSection() { + ignoreLine(); + while (sdePeek() != '*') { + ignoreLine(); + } + } + + /** + * A base "Java" stratum is always available, though + * it is not in the SourceDebugExtension. + * Create the base stratum. + */ + void createJavaStratum() { + baseStratumIndex = stratumIndex; + storeStratum(BASE_STRATUM_NAME); + storeFile(1, jplsFilename, NullString); + /* JPL line numbers cannot exceed 65535 */ + storeLine(1, 65536, 1, 1, 65536, 1); + storeStratum("Aux"); /* in case they don't declare */ + } + + /** + * Decode a SourceDebugExtension which is in SourceMap format. + * This is the entry point into the recursive descent parser. + */ + void decode() { + /* check for "SMAP" - allow EOF if not ours */ + if ((sourceDebugExtension.length() < 4) || + (sdeRead() != 'S') || + (sdeRead() != 'M') || + (sdeRead() != 'A') || + (sdeRead() != 'P')) { + return; /* not our info */ + } + ignoreLine(); /* flush the rest */ + jplsFilename = readLine(); + defaultStratumId = readLine(); + createJavaStratum(); + while (true) { + if (sdeRead() != '*') { + syntax(); + } + switch (sdeRead()) { + case 'S': + stratumSection(); + break; + case 'F': + fileSection(); + break; + case 'L': + lineSection(); + break; + case 'E': + /* set end points */ + storeStratum("*terminator*"); + isValid = true; + return; + default: + ignoreSection(); + } + } + } + + void createProxyForAbsentSDE() { + jplsFilename = null; + defaultStratumId = BASE_STRATUM_NAME; + defaultStratumIndex = stratumIndex; + createJavaStratum(); + storeStratum("*terminator*"); + } + + /***************** query functions ***********************/ + + private int stiLineTableIndex(int sti, int jplsLine) { + int i; + int lineIndexStart; + int lineIndexEnd; + + lineIndexStart = stratumTable[sti].lineIndex; + /* one past end */ + lineIndexEnd = stratumTable[sti+1].lineIndex; + for (i = lineIndexStart; i < lineIndexEnd; ++i) { + if ((jplsLine >= lineTable[i].jplsStart) && + (jplsLine <= lineTable[i].jplsEnd)) { + return i; + } + } + return -1; + } + + private int stiLineNumber(int sti, int lti, int jplsLine) { + return lineTable[lti].njplsStart + + (((jplsLine - lineTable[lti].jplsStart) / + lineTable[lti].jplsLineInc)); + } + + private int fileTableIndex(int sti, int fileId) { + int i; + int fileIndexStart = stratumTable[sti].fileIndex; + /* one past end */ + int fileIndexEnd = stratumTable[sti+1].fileIndex; + for (i = fileIndexStart; i < fileIndexEnd; ++i) { + if (fileTable[i].fileId == fileId) { + return i; + } + } + return -1; + } + + private int stiFileTableIndex(int sti, int lti) { + return fileTableIndex(sti, lineTable[lti].fileId); + } + + boolean isValid() { + return isValid; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ShortTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ShortTypeImpl.java new file mode 100644 index 00000000000..519c2ae6843 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ShortTypeImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class ShortTypeImpl extends PrimitiveTypeImpl implements ShortType { + ShortTypeImpl(VirtualMachine vm) { + super(vm); + } + + + public String signature() { + return "S"; + } + + PrimitiveValue convert(PrimitiveValue value) throws InvalidTypeException { + return vm.mirrorOf(((PrimitiveValueImpl)value).checkedShortValue()); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ShortValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ShortValueImpl.java new file mode 100644 index 00000000000..12f6b3b5c03 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ShortValueImpl.java @@ -0,0 +1,119 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class ShortValueImpl extends PrimitiveValueImpl + implements ShortValue { + private short value; + + ShortValueImpl(VirtualMachine aVm,short aValue) { + super(aVm); + + value = aValue; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof ShortValue)) { + return (value == ((ShortValue)obj).value()) && + super.equals(obj); + } else { + return false; + } + } + + public int hashCode() { + /* + * TO DO: Better hash code + */ + return intValue(); + } + + public int compareTo(Object obj) { + short other = ((ShortValue)obj).value(); + return value() - other; + } + + public Type type() { + return vm.theShortType(); + } + + public short value() { + return value; + } + + public boolean booleanValue() { + return(value == 0)?false:true; + } + + public byte byteValue() { + return(byte)value; + } + + public char charValue() { + return(char)value; + } + + public short shortValue() { + return(short)value; + } + + public int intValue() { + return(int)value; + } + + public long longValue() { + return(long)value; + } + + public float floatValue() { + return(float)value; + } + + public double doubleValue() { + return(double)value; + } + + byte checkedByteValue() throws InvalidTypeException { + if ((value > Byte.MAX_VALUE) || (value < Byte.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to byte"); + } else { + return super.checkedByteValue(); + } + } + + char checkedCharValue() throws InvalidTypeException { + if ((value > Character.MAX_VALUE) || (value < Character.MIN_VALUE)) { + throw new InvalidTypeException("Can't convert " + value + " to char"); + } else { + return super.checkedCharValue(); + } + } + + public String toString() { + return "" + value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/StackFrameImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/StackFrameImpl.java new file mode 100644 index 00000000000..d768817e0e2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/StackFrameImpl.java @@ -0,0 +1,276 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; +import sun.jvm.hotspot.oops.ObjectHeap; +import sun.jvm.hotspot.debugger.OopHandle; +import sun.jvm.hotspot.oops.Array; +import sun.jvm.hotspot.oops.ObjArray; +import sun.jvm.hotspot.oops.TypeArray; +import sun.jvm.hotspot.oops.Instance; +import sun.jvm.hotspot.runtime.BasicType; +import sun.jvm.hotspot.runtime.JavaVFrame; +import sun.jvm.hotspot.runtime.StackValue; +import sun.jvm.hotspot.runtime.StackValueCollection; +import sun.jvm.hotspot.utilities.Assert; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Collections; + +public class StackFrameImpl extends MirrorImpl + implements StackFrame +{ + /* Once false, frame should not be used. + * access synchronized on (vm.state()) + */ + private boolean isValid = true; + + private final ThreadReferenceImpl thread; + private final JavaVFrame saFrame; + private final Location location; + private Map visibleVariables = null; + private ObjectReference thisObject = null; + + StackFrameImpl(VirtualMachine vm, ThreadReferenceImpl thread, + JavaVFrame jvf) { + super(vm); + this.thread = thread; + this.saFrame = jvf; + + sun.jvm.hotspot.oops.Method SAMethod = jvf.getMethod(); + + ReferenceType rt = ((VirtualMachineImpl)vm).referenceType(SAMethod.getMethodHolder()); + + this.location = new LocationImpl(vm, rt, SAMethod, (long)jvf.getBCI()); + } + + private void validateStackFrame() { + if (!isValid) { + throw new InvalidStackFrameException("Thread has been resumed"); + } + } + + JavaVFrame getJavaVFrame() { + return saFrame; + } + + /** + * Return the frame location. + * Need not be synchronized since it cannot be provably stale. + */ + public Location location() { + validateStackFrame(); + return location; + } + + /** + * Return the thread holding the frame. + * Need not be synchronized since it cannot be provably stale. + */ + public ThreadReference thread() { + validateStackFrame(); + return thread; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof StackFrameImpl)) { + StackFrameImpl other = (StackFrameImpl)obj; + return (saFrame.equals(other.saFrame)); + } else { + return false; + } + } + + public int hashCode() { + return saFrame.hashCode(); + } + + public ObjectReference thisObject() { + validateStackFrame(); + MethodImpl currentMethod = (MethodImpl)location.method(); + if (currentMethod.isStatic() || currentMethod.isNative()) { + return null; + } + if (thisObject == null) { + StackValueCollection values = saFrame.getLocals(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(values.size() > 0, "this is missing"); + } + // 'this' at index 0. + OopHandle handle = values.oopHandleAt(0); + ObjectHeap heap = vm.saObjectHeap(); + thisObject = vm.objectMirror(heap.newOop(handle)); + } + return thisObject; + } + + /** + * Build the visible variable map. + * Need not be synchronized since it cannot be provably stale. + */ + private void createVisibleVariables() throws AbsentInformationException { + if (visibleVariables == null) { + List allVariables = location.method().variables(); + Map map = new HashMap(allVariables.size()); + + Iterator iter = allVariables.iterator(); + while (iter.hasNext()) { + LocalVariableImpl variable = (LocalVariableImpl)iter.next(); + String name = variable.name(); + if (variable.isVisible(this)) { + LocalVariable existing = (LocalVariable)map.get(name); + if ((existing == null) || + variable.hides(existing)) { + map.put(name, variable); + } + } + } + visibleVariables = map; + } + } + + /** + * Return the list of visible variable in the frame. + * Need not be synchronized since it cannot be provably stale. + */ + public List visibleVariables() throws AbsentInformationException { + validateStackFrame(); + createVisibleVariables(); + List mapAsList = new ArrayList(visibleVariables.values()); + Collections.sort(mapAsList); + return mapAsList; + } + + /** + * Return a particular variable in the frame. + * Need not be synchronized since it cannot be provably stale. + */ + public LocalVariable visibleVariableByName(String name) throws AbsentInformationException { + validateStackFrame(); + createVisibleVariables(); + return (LocalVariable)visibleVariables.get(name); + } + + public Value getValue(LocalVariable variable) { + List list = new ArrayList(1); + list.add(variable); + Map map = getValues(list); + return (Value)map.get(variable); + } + + public Map getValues(List variables) { + validateStackFrame(); + StackValueCollection values = saFrame.getLocals(); + + int count = variables.size(); + Map map = new HashMap(count); + for (int ii=0; ii + private List ownedMonitorsInfo; // List + private ObjectReferenceImpl currentContendingMonitor; + + ThreadReferenceImpl(VirtualMachine aVm, sun.jvm.hotspot.runtime.JavaThread aRef) { + // We are given a JavaThread and save it in our myJavaThread field. + // But, our parent class is an ObjectReferenceImpl so we need an Oop + // for it. JavaThread is a wrapper around a Thread Oop so we get + // that Oop and give it to our super. + // We can get it back again by calling ref(). + super(aVm, (Instance)aRef.getThreadObj()); + myJavaThread = aRef; + } + + ThreadReferenceImpl(VirtualMachine vm, Instance oRef) { + // Instance must be of type java.lang.Thread + super(vm, oRef); + + // JavaThread retrieved from java.lang.Thread instance may be null. + // This is the case for threads not-started and for zombies. Wherever + // appropriate, check for null instead of resulting in NullPointerException. + myJavaThread = OopUtilities.threadOopGetJavaThread(oRef); + } + + // return value may be null. refer to the comment in constructor. + JavaThread getJavaThread() { + return myJavaThread; + } + + protected String description() { + return "ThreadReference " + uniqueID(); + } + + /** + * Note that we only cache the name string while suspended because + * it can change via Thread.setName arbitrarily + */ + public String name() { + return OopUtilities.threadOopGetName(ref()); + } + + public void suspend() { + vm.throwNotReadOnlyException("ThreadReference.suspend()"); + } + + public void resume() { + vm.throwNotReadOnlyException("ThreadReference.resume()"); + } + + public int suspendCount() { + // all threads are "suspended" when we attach to process or core. + // we interpret this as one suspend. + return 1; + } + + public void stop(ObjectReference throwable) throws InvalidTypeException { + vm.throwNotReadOnlyException("ThreadReference.stop()"); + } + + public void interrupt() { + vm.throwNotReadOnlyException("ThreadReference.interrupt()"); + } + + // refer to jvmtiEnv::GetThreadState + private int jvmtiGetThreadState() { + // get most state bits + int state = OopUtilities.threadOopGetThreadStatus(ref()); + // add more state bits + if (myJavaThread != null) { + JavaThreadState jts = myJavaThread.getThreadState(); + if (myJavaThread.isBeingExtSuspended()) { + state |= JVMTI_THREAD_STATE_SUSPENDED; + } + if (jts == JavaThreadState.IN_NATIVE) { + state |= JVMTI_THREAD_STATE_IN_NATIVE; + } + OSThread osThread = myJavaThread.getOSThread(); + if (osThread != null && osThread.interrupted()) { + state |= JVMTI_THREAD_STATE_INTERRUPTED; + } + } + return state; + } + + public int status() { + int state = jvmtiGetThreadState(); + int status = THREAD_STATUS_UNKNOWN; + // refer to map2jdwpThreadStatus in util.c (back-end) + if (! ((state & JVMTI_THREAD_STATE_ALIVE) != 0) ) { + if ((state & JVMTI_THREAD_STATE_TERMINATED) != 0) { + status = THREAD_STATUS_ZOMBIE; + } else { + status = THREAD_STATUS_NOT_STARTED; + } + } else { + if ((state & JVMTI_THREAD_STATE_SLEEPING) != 0) { + status = THREAD_STATUS_SLEEPING; + } else if ((state & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) != 0) { + status = THREAD_STATUS_MONITOR; + } else if ((state & JVMTI_THREAD_STATE_WAITING) != 0) { + status = THREAD_STATUS_WAIT; + } else if ((state & JVMTI_THREAD_STATE_RUNNABLE) != 0) { + status = THREAD_STATUS_RUNNING; + } + } + return status; + } + + public boolean isSuspended() { //fixme jjh + // If we want to support doing this for a VM which was being + // debugged, then we need to fix this. + // In the meantime, we will say all threads are suspended, + // otherwise, some things won't work, like the jdb 'up' cmd. + return true; + } + + public boolean isAtBreakpoint() { //fixme jjh + // If we want to support doing this for a VM which was being + // debugged, then we need to fix this. + return false; + } + + public ThreadGroupReference threadGroup() { + return (ThreadGroupReferenceImpl)vm.threadGroupMirror( + (Instance)OopUtilities.threadOopGetThreadGroup(ref())); + } + + public int frameCount() throws IncompatibleThreadStateException { //fixme jjh + privateFrames(0, -1); + return frames.size(); + } + + public List frames() throws IncompatibleThreadStateException { + return privateFrames(0, -1); + } + + public StackFrame frame(int index) throws IncompatibleThreadStateException { + List list = privateFrames(index, 1); + return (StackFrame)list.get(0); + } + + public List frames(int start, int length) + throws IncompatibleThreadStateException { + if (length < 0) { + throw new IndexOutOfBoundsException( + "length must be greater than or equal to zero"); + } + return privateFrames(start, length); + } + + /** + * Private version of frames() allows "-1" to specify all + * remaining frames. + */ + + private List privateFrames(int start, int length) + throws IncompatibleThreadStateException { + if (myJavaThread == null) { + // for zombies and yet-to-be-started threads we need to throw exception + throw new IncompatibleThreadStateException(); + } + if (frames == null) { + frames = new ArrayList(10); + JavaVFrame myvf = myJavaThread.getLastJavaVFrameDbg(); + while (myvf != null) { + StackFrame myFrame = new StackFrameImpl(vm, this, myvf); + //fixme jjh null should be a Location + frames.add(myFrame); + myvf = (JavaVFrame)myvf.javaSender(); + } + } + + List retVal; + if (frames.size() == 0) { + retVal = new ArrayList(0); + } else { + int toIndex = start + length; + if (length == -1) { + toIndex = frames.size(); + } + retVal = frames.subList(start, toIndex); + } + return Collections.unmodifiableList(retVal); + } + + // refer to JvmtiEnvBase::get_owned_monitors + public List ownedMonitors() throws IncompatibleThreadStateException { + if (vm.canGetOwnedMonitorInfo() == false) { + throw new UnsupportedOperationException(); + } + + if (myJavaThread == null) { + throw new IncompatibleThreadStateException(); + } + + if (ownedMonitors != null) { + return ownedMonitors; + } + + ownedMonitorsWithStackDepth(); + + for (Iterator omi = ownedMonitorsInfo.iterator(); omi.hasNext(); ) { + //FIXME : Change the MonitorInfoImpl cast to com.sun.jdi.MonitorInfo + // when hotspot start building with jdk1.6. + ownedMonitors.add(((MonitorInfoImpl)omi.next()).monitor()); + } + + return ownedMonitors; + } + + // new method since 1.6. + // Real body will be supplied later. + public List ownedMonitorsAndFrames() throws IncompatibleThreadStateException { + if (!vm.canGetMonitorFrameInfo()) { + throw new UnsupportedOperationException( + "target does not support getting Monitor Frame Info"); + } + + if (myJavaThread == null) { + throw new IncompatibleThreadStateException(); + } + + if (ownedMonitorsInfo != null) { + return ownedMonitorsInfo; + } + + ownedMonitorsWithStackDepth(); + return ownedMonitorsInfo; + } + + private void ownedMonitorsWithStackDepth() { + + ownedMonitorsInfo = new ArrayList(); + List lockedObjects = new ArrayList(); // List + List stackDepth = new ArrayList(); // List + ObjectMonitor waitingMonitor = myJavaThread.getCurrentWaitingMonitor(); + ObjectMonitor pendingMonitor = myJavaThread.getCurrentPendingMonitor(); + OopHandle waitingObj = null; + if (waitingMonitor != null) { + // save object of current wait() call (if any) for later comparison + waitingObj = waitingMonitor.object(); + } + OopHandle pendingObj = null; + if (pendingMonitor != null) { + // save object of current enter() call (if any) for later comparison + pendingObj = pendingMonitor.object(); + } + + JavaVFrame frame = myJavaThread.getLastJavaVFrameDbg(); + int depth=0; + while (frame != null) { + List frameMonitors = frame.getMonitors(); // List + for (Iterator miItr = frameMonitors.iterator(); miItr.hasNext(); ) { + sun.jvm.hotspot.runtime.MonitorInfo mi = (sun.jvm.hotspot.runtime.MonitorInfo) miItr.next(); + OopHandle obj = mi.owner(); + if (obj == null) { + // this monitor doesn't have an owning object so skip it + continue; + } + + if (obj.equals(waitingObj)) { + // the thread is waiting on this monitor so it isn't really owned + continue; + } + + if (obj.equals(pendingObj)) { + // the thread is pending on this monitor so it isn't really owned + continue; + } + + boolean found = false; + for (Iterator loItr = lockedObjects.iterator(); loItr.hasNext(); ) { + // check for recursive locks + if (obj.equals(loItr.next())) { + found = true; + break; + } + } + if (found) { + // already have this object so don't include it + continue; + } + // add the owning object to our list + lockedObjects.add(obj); + stackDepth.add(new Integer(depth)); + } + frame = (JavaVFrame) frame.javaSender(); + depth++; + } + + // now convert List to List + ObjectHeap heap = vm.saObjectHeap(); + Iterator stk = stackDepth.iterator(); + for (Iterator loItr = lockedObjects.iterator(); loItr.hasNext(); ) { + Oop obj = heap.newOop((OopHandle)loItr.next()); + ownedMonitorsInfo.add(new MonitorInfoImpl(vm, vm.objectMirror(obj), this, + ((Integer)stk.next()).intValue())); + } + } + + // refer to JvmtiEnvBase::get_current_contended_monitor + public ObjectReference currentContendedMonitor() + throws IncompatibleThreadStateException { + if (vm.canGetCurrentContendedMonitor() == false) { + throw new UnsupportedOperationException(); + } + + if (myJavaThread == null) { + throw new IncompatibleThreadStateException(); + } + ObjectMonitor mon = myJavaThread.getCurrentWaitingMonitor(); + if (mon == null) { + // thread is not doing an Object.wait() call + mon = myJavaThread.getCurrentPendingMonitor(); + if (mon != null) { + OopHandle handle = mon.object(); + // If obj == NULL, then ObjectMonitor is raw which doesn't count + // as contended for this API + return vm.objectMirror(vm.saObjectHeap().newOop(handle)); + } else { + // no contended ObjectMonitor + return null; + } + } else { + // thread is doing an Object.wait() call + OopHandle handle = mon.object(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(handle != null, "Object.wait() should have an object"); + } + Oop obj = vm.saObjectHeap().newOop(handle); + return vm.objectMirror(obj); + } + } + + + public void popFrames(StackFrame frame) throws IncompatibleThreadStateException { + vm.throwNotReadOnlyException("ThreadReference.popFrames()"); + } + + public void forceEarlyReturn(Value returnValue) throws IncompatibleThreadStateException { + vm.throwNotReadOnlyException("ThreadReference.forceEarlyReturn()"); + } + + public String toString() { + return "instance of " + referenceType().name() + + "(name='" + name() + "', " + "id=" + uniqueID() + ")"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/TypeComponentImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/TypeComponentImpl.java new file mode 100644 index 00000000000..80f6d506caf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/TypeComponentImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; +import sun.jvm.hotspot.oops.Symbol; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.InstanceKlass; + +import java.util.List; + +/** + * There is no SA class that corresponds to this. Therefore, + * all the methods in this class which involve the SA mirror class + * have to be implemented in the subclasses. + */ +abstract public class TypeComponentImpl extends MirrorImpl + implements TypeComponent { + + protected final ReferenceTypeImpl declaringType; + protected String signature; + + TypeComponentImpl(VirtualMachine vm, ReferenceTypeImpl declaringType) { + super(vm); + this.declaringType = declaringType; + } + + public ReferenceType declaringType() { + return declaringType; + } + + public String signature() { + return signature; + } + + abstract public String name(); + abstract public int modifiers(); + abstract public boolean isPackagePrivate(); + abstract public boolean isPrivate(); + abstract public boolean isProtected(); + abstract public boolean isPublic(); + abstract public boolean isStatic(); + abstract public boolean isFinal(); + abstract public int hashCode(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/TypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/TypeImpl.java new file mode 100644 index 00000000000..708a44351a0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/TypeImpl.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public abstract class TypeImpl extends MirrorImpl implements Type +{ + private String typeName; + + TypeImpl(VirtualMachine aVm) { + super(aVm); + } + + public abstract String signature(); + + public String name() { + if (typeName == null) { + JNITypeParser parser = new JNITypeParser(signature()); + typeName = parser.typeName(); + } + return typeName; + } + + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof Type)) { + Type other = (Type)obj; + return signature().equals(other.signature()) && + super.equals(obj); + } else { + return false; + } + } + + public int hashCode() { + return signature().hashCode(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VMModifiers.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VMModifiers.java new file mode 100644 index 00000000000..e21779c7824 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VMModifiers.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import sun.jvm.hotspot.runtime.ClassConstants; +import com.sun.jdi.*; + +public interface VMModifiers extends ClassConstants { + int PUBLIC = (int) JVM_ACC_PUBLIC; /* visible to everyone */ + int PRIVATE = (int) JVM_ACC_PRIVATE; /* visible only to the defining class */ + int PROTECTED = (int) JVM_ACC_PROTECTED; /* visible to subclasses */ + int STATIC = (int) JVM_ACC_STATIC; /* instance variable is static */ + int FINAL = (int) JVM_ACC_FINAL; /* no further subclassing, overriding */ + int SYNCHRONIZED = (int) JVM_ACC_SYNCHRONIZED; /* wrap method call in monitor lock */ + int VOLATILE = (int) JVM_ACC_VOLATILE; /* can cache in registers */ + int BRIDGE = (int) JVM_ACC_BRIDGE; /* bridge method generated by compiler */ + int TRANSIENT = (int) JVM_ACC_TRANSIENT; /* not persistant */ + int VARARGS = (int) JVM_ACC_VARARGS; /* method declared with variable number of args */ + int IS_ENUM_CONSTANT = (int) JVM_ACC_ENUM; /* field is declared as element of enum */ + int NATIVE = (int) JVM_ACC_NATIVE; /* implemented in C */ + int INTERFACE = (int) JVM_ACC_INTERFACE; /* class is an interface */ + int ABSTRACT = (int) JVM_ACC_ABSTRACT; /* no definition provided */ + int SYNTHETIC = (int) JVM_ACC_SYNTHETIC; /* not in source code */ +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ValueContainer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ValueContainer.java new file mode 100644 index 00000000000..5090debbc2e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ValueContainer.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +/* + * This interface allows us to pass fields, variables, and + * array components through the same interfaces. This currently allows + * more common code for type checking. In the future we could use it for + * more. + */ +interface ValueContainer { + Type type() throws ClassNotLoadedException; + Type findType(String signature) throws ClassNotLoadedException; + String typeName(); + String signature(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ValueImpl.java new file mode 100644 index 00000000000..6250ca2b860 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/ValueImpl.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +abstract class ValueImpl extends MirrorImpl implements Value { + ValueImpl(VirtualMachine aVm) { + super(aVm); + } + + // type() is in the subclasses +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VirtualMachineImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VirtualMachineImpl.java new file mode 100644 index 00000000000..56cb8188012 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VirtualMachineImpl.java @@ -0,0 +1,1232 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; +import com.sun.jdi.event.EventQueue; +import com.sun.jdi.request.EventRequestManager; + +import sun.jvm.hotspot.HotSpotAgent; +import sun.jvm.hotspot.types.TypeDataBase; +import sun.jvm.hotspot.oops.Klass; +import sun.jvm.hotspot.oops.InstanceKlass; +import sun.jvm.hotspot.oops.ArrayKlass; +import sun.jvm.hotspot.oops.ObjArrayKlass; +import sun.jvm.hotspot.oops.TypeArrayKlass; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.Instance; +import sun.jvm.hotspot.oops.Array; +import sun.jvm.hotspot.oops.ObjArray; +import sun.jvm.hotspot.oops.TypeArray; +import sun.jvm.hotspot.oops.Symbol; +import sun.jvm.hotspot.oops.ObjectHeap; +import sun.jvm.hotspot.oops.DefaultHeapVisitor; +import sun.jvm.hotspot.oops.JVMDIClassStatus; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.runtime.JavaThread; +import sun.jvm.hotspot.memory.SystemDictionary; +import sun.jvm.hotspot.memory.SymbolTable; +import sun.jvm.hotspot.memory.Universe; +import sun.jvm.hotspot.utilities.Assert; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.Iterator; +import java.util.Collections; +import java.util.HashMap; +import java.util.Observer; +import java.util.StringTokenizer; +import java.lang.ref.SoftReference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.Reference; + +public class VirtualMachineImpl extends MirrorImpl implements PathSearchingVirtualMachine { + + private HotSpotAgent saAgent = new HotSpotAgent(); + private VM saVM; + private Universe saUniverse; + private SystemDictionary saSystemDictionary; + private SymbolTable saSymbolTable; + private ObjectHeap saObjectHeap; + + VM saVM() { + return saVM; + } + + SystemDictionary saSystemDictionary() { + return saSystemDictionary; + } + + SymbolTable saSymbolTable() { + return saSymbolTable; + } + + Universe saUniverse() { + return saUniverse; + } + + ObjectHeap saObjectHeap() { + return saObjectHeap; + } + + com.sun.jdi.VirtualMachineManager vmmgr; + + private final ThreadGroup threadGroupForJDI; + + // Per-vm singletons for primitive types and for void. + // singleton-ness protected by "synchronized(this)". + private BooleanType theBooleanType; + private ByteType theByteType; + private CharType theCharType; + private ShortType theShortType; + private IntegerType theIntegerType; + private LongType theLongType; + private FloatType theFloatType; + private DoubleType theDoubleType; + + private VoidType theVoidType; + + private VoidValue voidVal; + private Map typesByID; // Map + private List typesBySignature; // List - used in signature search + private boolean retrievedAllTypes = false; + private List bootstrapClasses; // all bootstrap classes + private ArrayList allThreads; + private ArrayList topLevelGroups; + final int sequenceNumber; + + // ObjectReference cache + // "objectsByID" protected by "synchronized(this)". + private final Map objectsByID = new HashMap(); + private final ReferenceQueue referenceQueue = new ReferenceQueue(); + + // names of some well-known classes to jdi + private Symbol javaLangString; + private Symbol javaLangThread; + private Symbol javaLangThreadGroup; + private Symbol javaLangClass; + private Symbol javaLangClassLoader; + + // used in ReferenceTypeImpl.isThrowableBacktraceField + private Symbol javaLangThrowable; + + // names of classes used in array assignment check + // refer to ArrayTypeImpl.isAssignableTo + private Symbol javaLangObject; + private Symbol javaLangCloneable; + private Symbol javaIoSerializable; + + // symbol used in ClassTypeImpl.isEnum check + private Symbol javaLangEnum; + + Symbol javaLangObject() { + return javaLangObject; + } + + Symbol javaLangCloneable() { + return javaLangCloneable; + } + + Symbol javaIoSerializable() { + return javaIoSerializable; + } + + Symbol javaLangEnum() { + return javaLangEnum; + } + + Symbol javaLangThrowable() { + return javaLangThrowable; + } + + // name of the current default stratum + private String defaultStratum; + + // initialize known class name symbols + private void initClassNameSymbols() { + SymbolTable st = saSymbolTable(); + javaLangString = st.probe("java/lang/String"); + javaLangThread = st.probe("java/lang/Thread"); + javaLangThreadGroup = st.probe("java/lang/ThreadGroup"); + javaLangClass = st.probe("java/lang/Class"); + javaLangClassLoader = st.probe("java/lang/ClassLoader"); + javaLangThrowable = st.probe("java/lang/Throwable"); + javaLangObject = st.probe("java/lang/Object"); + javaLangCloneable = st.probe("java/lang/Cloneable"); + javaIoSerializable = st.probe("java/io/Serializable"); + javaLangEnum = st.probe("java/lang/Enum"); + } + + private void init() { + saVM = VM.getVM(); + saUniverse = saVM.getUniverse(); + saSystemDictionary = saVM.getSystemDictionary(); + saSymbolTable = saVM.getSymbolTable(); + saObjectHeap = saVM.getObjectHeap(); + initClassNameSymbols(); + } + + static public VirtualMachineImpl createVirtualMachineForCorefile(VirtualMachineManager mgr, + String javaExecutableName, + String coreFileName, + int sequenceNumber) + throws Exception { + if (Assert.ASSERTS_ENABLED) { + Assert.that(coreFileName != null, "SA VirtualMachineImpl: core filename = null is not yet implemented"); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(javaExecutableName != null, "SA VirtualMachineImpl: java executable = null is not yet implemented"); + } + + VirtualMachineImpl myvm = new VirtualMachineImpl(mgr, sequenceNumber); + try { + myvm.saAgent.attach(javaExecutableName, coreFileName); + myvm.init(); + } catch (Exception ee) { + myvm.saAgent.detach(); + throw ee; + } + return myvm; + } + + static public VirtualMachineImpl createVirtualMachineForPID(VirtualMachineManager mgr, + int pid, + int sequenceNumber) + throws Exception { + + VirtualMachineImpl myvm = new VirtualMachineImpl(mgr, sequenceNumber); + try { + myvm.saAgent.attach(pid); + myvm.init(); + } catch (Exception ee) { + myvm.saAgent.detach(); + throw ee; + } + return myvm; + } + + static public VirtualMachineImpl createVirtualMachineForServer(VirtualMachineManager mgr, + String server, + int sequenceNumber) + throws Exception { + if (Assert.ASSERTS_ENABLED) { + Assert.that(server != null, "SA VirtualMachineImpl: DebugServer = null is not yet implemented"); + } + + VirtualMachineImpl myvm = new VirtualMachineImpl(mgr, sequenceNumber); + try { + myvm.saAgent.attach(server); + myvm.init(); + } catch (Exception ee) { + myvm.saAgent.detach(); + throw ee; + } + return myvm; + } + + + VirtualMachineImpl(VirtualMachineManager mgr, int sequenceNumber) + throws Exception { + super(null); // Can't use super(this) + vm = this; + + this.sequenceNumber = sequenceNumber; + this.vmmgr = mgr; + + /* Create ThreadGroup to be used by all threads servicing + * this VM. + */ + threadGroupForJDI = new ThreadGroup("JDI [" + + this.hashCode() + "]"); + + ((com.sun.tools.jdi.VirtualMachineManagerImpl)mgr).addVirtualMachine(this); + + // By default SA agent classes prefer dbx debugger to proc debugger + // and Windows process debugger to windbg debugger. SA expects + // special properties to be set to choose other debuggers. We will set + // those here before attaching to SA agent. + + System.setProperty("sun.jvm.hotspot.debugger.useProcDebugger", "true"); + System.setProperty("sun.jvm.hotspot.debugger.useWindbgDebugger", "true"); + } + + // we reflectively use newly spec'ed class because our ALT_BOOTDIR + // is 1.4.2 and not 1.5. + private static Class vmCannotBeModifiedExceptionClass = null; + void throwNotReadOnlyException(String operation) { + RuntimeException re = null; + if (vmCannotBeModifiedExceptionClass == null) { + try { + vmCannotBeModifiedExceptionClass = Class.forName("com.sun.jdi.VMCannotBeModifiedException"); + } catch (ClassNotFoundException cnfe) { + vmCannotBeModifiedExceptionClass = UnsupportedOperationException.class; + } + } + try { + re = (RuntimeException) vmCannotBeModifiedExceptionClass.newInstance(); + } catch (Exception exp) { + re = new RuntimeException(exp.getMessage()); + } + throw re; + } + + public boolean equals(Object obj) { + // Oh boy; big recursion troubles if we don't have this! + // See MirrorImpl.equals + return this == obj; + } + + public int hashCode() { + // big recursion if we don't have this. See MirrorImpl.hashCode + return System.identityHashCode(this); + } + + public List classesByName(String className) { + String signature = JNITypeParser.typeNameToSignature(className); + List list; + if (!retrievedAllTypes) { + retrieveAllClasses(); + } + list = findReferenceTypes(signature); + return Collections.unmodifiableList(list); + } + + public List allClasses() { + if (!retrievedAllTypes) { + retrieveAllClasses(); + } + ArrayList a; + synchronized (this) { + a = new ArrayList(typesBySignature); + } + return Collections.unmodifiableList(a); + } + + // classes loaded by bootstrap loader + List bootstrapClasses() { + if (bootstrapClasses == null) { + bootstrapClasses = new ArrayList(); + List all = allClasses(); + for (Iterator itr = all.iterator(); itr.hasNext();) { + ReferenceType type = (ReferenceType) itr.next(); + if (type.classLoader() == null) { + bootstrapClasses.add(type); + } + } + } + return bootstrapClasses; + } + + private synchronized List findReferenceTypes(String signature) { + if (typesByID == null) { + return new ArrayList(0); + } + + // we haven't sorted types by signatures. But we can take + // advantage of comparing symbols instead of name. In the worst + // case, we will be comparing N addresses rather than N strings + // where N being total no. of classes in allClasses() list. + + // The signature could be Lx/y/z; or [.... + // If it is Lx/y/z; the internal type name is x/y/x + // for array klasses internal type name is same as + // signature + String typeName = null; + if (signature.charAt(0) == 'L') { + typeName = signature.substring(1, signature.length() - 1); + } else { + typeName = signature; + } + + Symbol typeNameSym = saSymbolTable().probe(typeName); + // if there is no symbol in VM, then we wouldn't have that type + if (typeNameSym == null) { + return new ArrayList(0); + } + + Iterator iter = typesBySignature.iterator(); + List list = new ArrayList(); + while (iter.hasNext()) { + // We have cached type name as symbol in reference type + ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next(); + if (typeNameSym.equals(type.typeNameAsSymbol())) { + list.add(type); + } + } + return list; + } + + private void retrieveAllClasses() { + final List saKlasses = new ArrayList(); + SystemDictionary.ClassVisitor visitor = new SystemDictionary.ClassVisitor() { + public void visit(Klass k) { + for (Klass l = k; l != null; l = l.arrayKlassOrNull()) { + // for non-array classes filter out un-prepared classes + // refer to 'allClasses' in share/back/VirtualMachineImpl.c + if (l instanceof ArrayKlass) { + saKlasses.add(l); + } else { + int status = l.getClassStatus(); + if ((status & JVMDIClassStatus.PREPARED) != 0) { + saKlasses.add(l); + } + } + } + } + }; + + // refer to jvmtiGetLoadedClasses.cpp - getLoadedClasses in VM code. + + // classes from SystemDictionary + saSystemDictionary.classesDo(visitor); + + // From SystemDictionary we do not get primitive single + // dimensional array classes. add primitive single dimensional array + // klasses from Universe. + saVM.getUniverse().basicTypeClassesDo(visitor); + + // Hold lock during processing to improve performance + // and to have safe check/set of retrievedAllTypes + synchronized (this) { + if (!retrievedAllTypes) { + // Number of classes + int count = saKlasses.size(); + for (int ii = 0; ii < count; ii++) { + Klass kk = (Klass)saKlasses.get(ii); + ReferenceTypeImpl type = referenceType(kk); + } + retrievedAllTypes = true; + } + } + } + + ReferenceTypeImpl referenceType(Klass kk) { + ReferenceTypeImpl retType = null; + synchronized (this) { + if (typesByID != null) { + retType = (ReferenceTypeImpl)typesByID.get(kk); + } + if (retType == null) { + retType = addReferenceType(kk); + } + } + return retType; + } + + private void initReferenceTypes() { + typesByID = new HashMap(); + typesBySignature = new ArrayList(); + } + + private synchronized ReferenceTypeImpl addReferenceType(Klass kk) { + if (typesByID == null) { + initReferenceTypes(); + } + ReferenceTypeImpl newRefType = null; + if (kk instanceof ObjArrayKlass || kk instanceof TypeArrayKlass) { + newRefType = new ArrayTypeImpl(this, (ArrayKlass)kk); + } else if (kk instanceof InstanceKlass) { + if (kk.isInterface()) { + newRefType = new InterfaceTypeImpl(this, (InstanceKlass)kk); + } else { + newRefType = new ClassTypeImpl(this, (InstanceKlass)kk); + } + } else { + throw new RuntimeException("should not reach here"); + } + + typesByID.put(kk, newRefType); + typesBySignature.add(newRefType); + return newRefType; + } + + ThreadGroup threadGroupForJDI() { + return threadGroupForJDI; + } + + public void redefineClasses(Map classToBytes) { + throwNotReadOnlyException("VirtualMachineImpl.redefineClasses()"); + } + + private List getAllThreads() { + if (allThreads == null) { + allThreads = new ArrayList(10); // Might be enough, might not be + for (sun.jvm.hotspot.runtime.JavaThread thread = + saVM.getThreads().first(); thread != null; + thread = thread.next()) { + // refer to JvmtiEnv::GetAllThreads in jvmtiEnv.cpp. + // filter out the hidden-from-external-view threads. + if (thread.isHiddenFromExternalView() == false) { + ThreadReferenceImpl myThread = threadMirror(thread); + allThreads.add(myThread); + } + } + } + return allThreads; + } + + public List allThreads() { //fixme jjh + return Collections.unmodifiableList(getAllThreads()); + } + + public void suspend() { + throwNotReadOnlyException("VirtualMachineImpl.suspend()"); + } + + public void resume() { + throwNotReadOnlyException("VirtualMachineImpl.resume()"); + } + + public List topLevelThreadGroups() { //fixme jjh + // The doc for ThreadGroup says that The top-level thread group + // is the only thread group whose parent is null. This means there is + // only one top level thread group. There will be a thread in this + // group so we will just find a thread whose threadgroup has no parent + // and that will be it. + + if (topLevelGroups == null) { + topLevelGroups = new ArrayList(1); + Iterator myIt = getAllThreads().iterator(); + while (myIt.hasNext()) { + ThreadReferenceImpl myThread = (ThreadReferenceImpl)myIt.next(); + ThreadGroupReference myGroup = myThread.threadGroup(); + ThreadGroupReference myParent = myGroup.parent(); + if (myGroup.parent() == null) { + topLevelGroups.add(myGroup); + break; + } + } + } + return Collections.unmodifiableList(topLevelGroups); + } + + public EventQueue eventQueue() { + throwNotReadOnlyException("VirtualMachine.eventQueue()"); + return null; + } + + public EventRequestManager eventRequestManager() { + throwNotReadOnlyException("VirtualMachineImpl.eventRequestManager()"); + return null; + } + + public BooleanValue mirrorOf(boolean value) { + return new BooleanValueImpl(this,value); + } + + public ByteValue mirrorOf(byte value) { + return new ByteValueImpl(this,value); + } + + public CharValue mirrorOf(char value) { + return new CharValueImpl(this,value); + } + + public ShortValue mirrorOf(short value) { + return new ShortValueImpl(this,value); + } + + public IntegerValue mirrorOf(int value) { + return new IntegerValueImpl(this,value); + } + + public LongValue mirrorOf(long value) { + return new LongValueImpl(this,value); + } + + public FloatValue mirrorOf(float value) { + return new FloatValueImpl(this,value); + } + + public DoubleValue mirrorOf(double value) { + return new DoubleValueImpl(this,value); + } + + public StringReference mirrorOf(String value) { + throwNotReadOnlyException("VirtualMachinestop.mirrorOf(String)"); + return null; + } + + public VoidValue mirrorOfVoid() { + if (voidVal == null) { + voidVal = new VoidValueImpl(this); + } + return voidVal; + } + + + public Process process() { + throwNotReadOnlyException("VirtualMachine.process"); + return null; + } + + // dispose observer for Class re-use. refer to ConnectorImpl. + private Observer disposeObserver; + + // ConnectorImpl loaded by a different class loader can not access it. + // i.e., runtime package of is not the same that of + // when L1 != L2. So, package private method + // can be called reflectively after using setAccessible(true). + + void setDisposeObserver(Observer observer) { + disposeObserver = observer; + } + + private void notifyDispose() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(disposeObserver != null, "null VM.dispose observer"); + } + disposeObserver.update(null, null); + } + + public void dispose() { + saAgent.detach(); + notifyDispose(); + } + + public void exit(int exitCode) { + throwNotReadOnlyException("VirtualMachine.exit(int)"); + } + + public boolean canBeModified() { + return false; + } + + public boolean canWatchFieldModification() { + return false; + } + + public boolean canWatchFieldAccess() { + return false; + } + + public boolean canGetBytecodes() { + return true; + } + + public boolean canGetSyntheticAttribute() { + return true; + } + + // FIXME: For now, all monitor capabilities are disabled + public boolean canGetOwnedMonitorInfo() { + return false; + } + + public boolean canGetCurrentContendedMonitor() { + return false; + } + + public boolean canGetMonitorInfo() { + return false; + } + + // because this SA works only with 1.5 and update releases + // this should always succeed unlike JVMDI/JDI. + public boolean canGet1_5LanguageFeatures() { + return true; + } + + public boolean canUseInstanceFilters() { + return false; + } + + public boolean canRedefineClasses() { + return false; + } + + public boolean canAddMethod() { + return false; + } + + public boolean canUnrestrictedlyRedefineClasses() { + return false; + } + + public boolean canPopFrames() { + return false; + } + + public boolean canGetSourceDebugExtension() { + // We can use InstanceKlass.getSourceDebugExtension only if + // ClassFileParser parsed the info. But, ClassFileParser parses + // SourceDebugExtension attribute only if corresponding JVMDI/TI + // capability is set to true. Currently, vmStructs does not expose + // JVMDI/TI capabilities and hence we conservatively assume false. + return false; + } + + public boolean canRequestVMDeathEvent() { + return false; + } + + // new method since 1.6 + public boolean canForceEarlyReturn() { + return false; + } + + // new method since 1.6 + public boolean canGetConstantPool() { + return true; + } + + // new method since 1.6 + public boolean canGetClassFileVersion() { + return true; + } + + // new method since 1.6. + public boolean canGetMethodReturnValues() { + return false; + } + + // new method since 1.6 + // Real body will be supplied later. + public boolean canGetInstanceInfo() { + return true; + } + + // new method since 1.6 + public boolean canUseSourceNameFilters() { + return false; + } + + // new method since 1.6. + public boolean canRequestMonitorEvents() { + return false; + } + + // new method since 1.6. + public boolean canGetMonitorFrameInfo() { + return true; + } + + // new method since 1.6 + // Real body will be supplied later. + public long[] instanceCounts(List classes) { + if (!canGetInstanceInfo()) { + throw new UnsupportedOperationException( + "target does not support getting instances"); + } + + final long[] retValue = new long[classes.size()] ; + + final Klass [] klassArray = new Klass[classes.size()]; + + boolean allAbstractClasses = true; + for (int i=0; i < classes.size(); i++) { + ReferenceTypeImpl rti = (ReferenceTypeImpl)classes.get(i); + klassArray[i] = rti.ref(); + retValue[i]=0; + if (!(rti.isAbstract() || ((ReferenceType)rti instanceof InterfaceType))) { + allAbstractClasses = false; + } + } + + if (allAbstractClasses) { + return retValue; + } + final int size = classes.size(); + saObjectHeap.iterate(new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + for (int i=0; i < size; i++) { + if (klassArray[i].equals(oop.getKlass())) { + retValue[i]++; + break; + } + } + return false; + } + }); + + return retValue; + } + + private List getPath (String pathName) { + String cp = saVM.getSystemProperty(pathName); + String pathSep = saVM.getSystemProperty("path.separator"); + ArrayList al = new ArrayList(); + StringTokenizer st = new StringTokenizer(cp, pathSep); + while (st.hasMoreTokens()) { + al.add(st.nextToken()); + } + al.trimToSize(); + return al; + } + + public List classPath() { + return getPath("java.class.path"); + } + + public List bootClassPath() { + return getPath("sun.boot.class.path"); + } + + public String baseDirectory() { + return saVM.getSystemProperty("user.dir"); + } + + public void setDefaultStratum(String stratum) { + defaultStratum = stratum; + } + + public String getDefaultStratum() { + return defaultStratum; + } + + public String description() { + String[] versionParts = {"" + vmmgr.majorInterfaceVersion(), + "" + vmmgr.minorInterfaceVersion(), + name()}; + return java.text.MessageFormat.format(java.util.ResourceBundle. + getBundle("com.sun.tools.jdi.resources.jdi").getString("version_format"), + versionParts); + } + + public String version() { + return saVM.getSystemProperty("java.version"); + } + + public String name() { + StringBuffer sb = new StringBuffer(); + sb.append("JVM version "); + sb.append(version()); + sb.append(" ("); + sb.append(saVM.getSystemProperty("java.vm.name")); + sb.append(", "); + sb.append(saVM.getSystemProperty("java.vm.info")); + sb.append(")"); + return sb.toString(); + } + + // from interface Mirror + public VirtualMachine virtualMachine() { + return this; + } + + public String toString() { + return name(); + } + + public void setDebugTraceMode(int traceFlags) { + // spec. says output is implementation dependent + // and trace mode may be ignored. we ignore it :-) + } + + // heap walking API + + // capability check + public boolean canWalkHeap() { + return true; + } + + // return a list of all objects in heap + public List/**/ allObjects() { + final List objects = new ArrayList(0); + saObjectHeap.iterate( + new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + objects.add(objectMirror(oop)); + return false; + } + }); + return objects; + } + + // equivalent to objectsByType(type, true) + public List/**/ objectsByType(ReferenceType type) { + return objectsByType(type, true); + } + + // returns objects of type exactly equal to given type + private List/**/ objectsByExactType(ReferenceType type) { + final List objects = new ArrayList(0); + final Klass givenKls = ((ReferenceTypeImpl)type).ref(); + saObjectHeap.iterate(new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + if (givenKls.equals(oop.getKlass())) { + objects.add(objectMirror(oop)); + } + return false; + } + }); + return objects; + } + + // returns objects of given type as well as it's subtypes + private List/**/ objectsBySubType(ReferenceType type) { + final List objects = new ArrayList(0); + final ReferenceType givenType = type; + saObjectHeap.iterate(new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + ReferenceTypeImpl curType = (ReferenceTypeImpl) referenceType(oop.getKlass()); + if (curType.isAssignableTo(givenType)) { + objects.add(objectMirror(oop)); + } + return false; + } + }); + return objects; + } + + // includeSubtypes - do you want to include subclass/subtype instances of given + // ReferenceType or do we want objects of exact type only? + public List/**/ objectsByType(ReferenceType type, boolean includeSubtypes) { + Klass kls = ((ReferenceTypeImpl)type).ref(); + if (kls instanceof InstanceKlass) { + InstanceKlass ik = (InstanceKlass) kls; + if (ik.isInterface()) { + if (ik.nofImplementors() == 0L) { + return new ArrayList(0); + } + } else { + // if the Klass is final or if there are no subklasses loaded yet + if (ik.getAccessFlagsObj().isFinal() || ik.getSubklassKlass() == null) { + includeSubtypes = false; + } + } + } else { + // no subtypes for primitive array types + ArrayTypeImpl arrayType = (ArrayTypeImpl) type; + try { + Type componentType = arrayType.componentType(); + if (componentType instanceof PrimitiveType) { + includeSubtypes = false; + } + } catch (ClassNotLoadedException cnle) { + // ignore. component type not yet loaded + } + } + + if (includeSubtypes) { + return objectsBySubType(type); + } else { + return objectsByExactType(type); + } + } + + Type findBootType(String signature) throws ClassNotLoadedException { + List types = allClasses(); + Iterator iter = types.iterator(); + while (iter.hasNext()) { + ReferenceType type = (ReferenceType)iter.next(); + if ((type.classLoader() == null) && + (type.signature().equals(signature))) { + return type; + } + } + JNITypeParser parser = new JNITypeParser(signature); + throw new ClassNotLoadedException(parser.typeName(), + "Type " + parser.typeName() + " not loaded"); + } + + BooleanType theBooleanType() { + if (theBooleanType == null) { + synchronized(this) { + if (theBooleanType == null) { + theBooleanType = new BooleanTypeImpl(this); + } + } + } + return theBooleanType; + } + + ByteType theByteType() { + if (theByteType == null) { + synchronized(this) { + if (theByteType == null) { + theByteType = new ByteTypeImpl(this); + } + } + } + return theByteType; + } + + CharType theCharType() { + if (theCharType == null) { + synchronized(this) { + if (theCharType == null) { + theCharType = new CharTypeImpl(this); + } + } + } + return theCharType; + } + + ShortType theShortType() { + if (theShortType == null) { + synchronized(this) { + if (theShortType == null) { + theShortType = new ShortTypeImpl(this); + } + } + } + return theShortType; + } + + IntegerType theIntegerType() { + if (theIntegerType == null) { + synchronized(this) { + if (theIntegerType == null) { + theIntegerType = new IntegerTypeImpl(this); + } + } + } + return theIntegerType; + } + + LongType theLongType() { + if (theLongType == null) { + synchronized(this) { + if (theLongType == null) { + theLongType = new LongTypeImpl(this); + } + } + } + return theLongType; + } + + FloatType theFloatType() { + if (theFloatType == null) { + synchronized(this) { + if (theFloatType == null) { + theFloatType = new FloatTypeImpl(this); + } + } + } + return theFloatType; + } + + DoubleType theDoubleType() { + if (theDoubleType == null) { + synchronized(this) { + if (theDoubleType == null) { + theDoubleType = new DoubleTypeImpl(this); + } + } + } + return theDoubleType; + } + + VoidType theVoidType() { + if (theVoidType == null) { + synchronized(this) { + if (theVoidType == null) { + theVoidType = new VoidTypeImpl(this); + } + } + } + return theVoidType; + } + + PrimitiveType primitiveTypeMirror(char tag) { + switch (tag) { + case 'Z': + return theBooleanType(); + case 'B': + return theByteType(); + case 'C': + return theCharType(); + case 'S': + return theShortType(); + case 'I': + return theIntegerType(); + case 'J': + return theLongType(); + case 'F': + return theFloatType(); + case 'D': + return theDoubleType(); + default: + throw new IllegalArgumentException("Unrecognized primitive tag " + tag); + } + } + + private void processQueue() { + Reference ref; + while ((ref = referenceQueue.poll()) != null) { + SoftObjectReference softRef = (SoftObjectReference)ref; + removeObjectMirror(softRef); + } + } + + // Address value is used as uniqueID by ObjectReferenceImpl + long getAddressValue(Oop obj) { + return vm.saVM.getDebugger().getAddressValue(obj.getHandle()); + } + + synchronized ObjectReferenceImpl objectMirror(Oop key) { + + // Handle any queue elements that are not strongly reachable + processQueue(); + + if (key == null) { + return null; + } + ObjectReferenceImpl object = null; + + /* + * Attempt to retrieve an existing object object reference + */ + SoftObjectReference ref = (SoftObjectReference)objectsByID.get(key); + if (ref != null) { + object = ref.object(); + } + + /* + * If the object wasn't in the table, or it's soft reference was + * cleared, create a new instance. + */ + if (object == null) { + if (key instanceof Instance) { + // look for well-known classes + Symbol className = key.getKlass().getName(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(className != null, "Null class name"); + } + Instance inst = (Instance) key; + if (className.equals(javaLangString)) { + object = new StringReferenceImpl(this, inst); + } else if (className.equals(javaLangThread)) { + object = new ThreadReferenceImpl(this, inst); + } else if (className.equals(javaLangThreadGroup)) { + object = new ThreadGroupReferenceImpl(this, inst); + } else if (className.equals(javaLangClass)) { + object = new ClassObjectReferenceImpl(this, inst); + } else if (className.equals(javaLangClassLoader)) { + object = new ClassLoaderReferenceImpl(this, inst); + } else { + // not a well-known class. But the base class may be + // one of the known classes. + Klass kls = key.getKlass().getSuper(); + while (kls != null) { + className = kls.getName(); + // java.lang.Class and java.lang.String are final classes + if (className.equals(javaLangThread)) { + object = new ThreadReferenceImpl(this, inst); + break; + } else if(className.equals(javaLangThreadGroup)) { + object = new ThreadGroupReferenceImpl(this, inst); + break; + } else if (className.equals(javaLangClassLoader)) { + object = new ClassLoaderReferenceImpl(this, inst); + break; + } + kls = kls.getSuper(); + } + + if (object == null) { + // create generic object reference + object = new ObjectReferenceImpl(this, inst); + } + } + } else if (key instanceof TypeArray) { + object = new ArrayReferenceImpl(this, (Array) key); + } else if (key instanceof ObjArray) { + object = new ArrayReferenceImpl(this, (Array) key); + } else { + throw new RuntimeException("unexpected object type " + key); + } + ref = new SoftObjectReference(key, object, referenceQueue); + + /* + * If there was no previous entry in the table, we add one here + * If the previous entry was cleared, we replace it here. + */ + objectsByID.put(key, ref); + } else { + ref.incrementCount(); + } + + return object; + } + + synchronized void removeObjectMirror(SoftObjectReference ref) { + /* + * This will remove the soft reference if it has not been + * replaced in the cache. + */ + objectsByID.remove(ref.key()); + } + + StringReferenceImpl stringMirror(Instance id) { + return (StringReferenceImpl) objectMirror(id); + } + + ArrayReferenceImpl arrayMirror(Array id) { + return (ArrayReferenceImpl) objectMirror(id); + } + + ThreadReferenceImpl threadMirror(Instance id) { + return (ThreadReferenceImpl) objectMirror(id); + } + + ThreadReferenceImpl threadMirror(JavaThread jt) { + return (ThreadReferenceImpl) objectMirror(jt.getThreadObj()); + } + + ThreadGroupReferenceImpl threadGroupMirror(Instance id) { + return (ThreadGroupReferenceImpl) objectMirror(id); + } + + ClassLoaderReferenceImpl classLoaderMirror(Instance id) { + return (ClassLoaderReferenceImpl) objectMirror(id); + } + + ClassObjectReferenceImpl classObjectMirror(Instance id) { + return (ClassObjectReferenceImpl) objectMirror(id); + } + + // Use of soft refs and caching stuff here has to be re-examined. + // It might not make sense for JDI - SA. + static private class SoftObjectReference extends SoftReference { + int count; + Object key; + + SoftObjectReference(Object key, ObjectReferenceImpl mirror, + ReferenceQueue queue) { + super(mirror, queue); + this.count = 1; + this.key = key; + } + + int count() { + return count; + } + + void incrementCount() { + count++; + } + + Object key() { + return key; + } + + ObjectReferenceImpl object() { + return (ObjectReferenceImpl)get(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VoidTypeImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VoidTypeImpl.java new file mode 100644 index 00000000000..cc007a09fd4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VoidTypeImpl.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class VoidTypeImpl extends TypeImpl implements VoidType { + VoidTypeImpl(VirtualMachine vm) { + super(vm); + } + + public String signature() { + return "V"; + } + + public String toString() { + return name(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VoidValueImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VoidValueImpl.java new file mode 100644 index 00000000000..93041d7297a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/VoidValueImpl.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.jdi; + +import com.sun.jdi.*; + +public class VoidValueImpl extends ValueImpl implements VoidValue { + + VoidValueImpl(VirtualMachine aVm) { + super(aVm); + } + + public boolean equals(Object obj) { + return (obj != null) && (obj instanceof VoidValue) && super.equals(obj); + } + + public int hashCode() { + return type().hashCode(); + } + + public Type type() { + return vm.theVoidType(); + } + + ValueImpl prepareForAssignmentTo(ValueContainer destination) + throws InvalidTypeException { + + throw new InvalidTypeException(); + } + + public String toString() { + return ""; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/BreakpointEvent.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/BreakpointEvent.java new file mode 100644 index 00000000000..436551a6216 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/BreakpointEvent.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.livejvm; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +public class BreakpointEvent extends Event { + private Oop thread; + private Oop clazz; + private JNIid method; + private int location; + + public BreakpointEvent(Oop thread, + Oop clazz, + JNIid method, + int location) { + super(Event.Type.BREAKPOINT); + this.thread = thread; + this.clazz = clazz; + this.method = method; + this.location = location; + } + + public Oop thread() { return thread; } + public Oop clazz() { return clazz; } + public JNIid methodID() { return method; } + public int location() { return location; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/CIntegerAccessor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/CIntegerAccessor.java new file mode 100644 index 00000000000..5b4c2fdaedb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/CIntegerAccessor.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.livejvm; + +import sun.jvm.hotspot.debugger.*; + +class CIntegerAccessor { + private Address addr; + private long numBytes; + private boolean isUnsigned; + + CIntegerAccessor(Address addr, long numBytes, boolean isUnsigned) { + this.addr = addr; + this.numBytes = numBytes; + this.isUnsigned = isUnsigned; + } + + long getValue() { + return addr.getCIntegerAt(0, numBytes, isUnsigned); + } + + void setValue(long value) { + addr.setCIntegerAt(0, numBytes, value); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/CStringAccessor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/CStringAccessor.java new file mode 100644 index 00000000000..c18ecae048c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/CStringAccessor.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.livejvm; + +import java.io.UnsupportedEncodingException; +import sun.jvm.hotspot.debugger.*; + +class CStringAccessor { + private Address addr; + private int bufLen; + + CStringAccessor(Address addr, int bufLen) { + this.addr = addr; + this.bufLen = bufLen; + } + + String getValue() throws DebuggerException { + int len = 0; + while ((addr.getCIntegerAt(len, 1, true) != 0) && (len < bufLen)) { + ++len; + } + byte[] res = new byte[len]; + for (int i = 0; i < len; i++) { + res[i] = (byte) addr.getCIntegerAt(i, 1, true); + } + try { + return new String(res, "US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new DebuggerException("Unable to use US-ASCII encoding"); + } + } + + void setValue(String value) throws DebuggerException { + try { + byte[] data = value.getBytes("US-ASCII"); + if (data.length >= bufLen) { + throw new DebuggerException("String too long"); + } + for (int i = 0; i < data.length; i++) { + addr.setCIntegerAt(i, 1, data[i]); + } + addr.setCIntegerAt(data.length, 1, 0); + } catch (UnsupportedEncodingException e) { + throw new DebuggerException("Unable to use US-ASCII encoding"); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/Event.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/Event.java new file mode 100644 index 00000000000..5aebce3317d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/Event.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.livejvm; + +public class Event { + public static class Type { + private Type() {} + public static final Type BREAKPOINT = new Type(); + public static final Type EXCEPTION = new Type(); + } + + private Type type; + + public Event(Type type) { + this.type = type; + } + + public Type getType() { return type; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/ExceptionEvent.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/ExceptionEvent.java new file mode 100644 index 00000000000..e651f2833c4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/ExceptionEvent.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.livejvm; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +public class ExceptionEvent extends Event { + private Oop thread; + private Oop clazz; + private JNIid method; + private int location; + private Oop exception; + private Oop catchClass; + private JNIid catchMethod; + private int catchLocation; + + public ExceptionEvent(Oop thread, + Oop clazz, + JNIid method, + int location, + Oop exception, + Oop catchClass, + JNIid catchMethod, + int catchLocation) { + super(Event.Type.EXCEPTION); + this.thread = thread; + this.clazz = clazz; + this.method = method; + this.location = location; + this.exception = exception; + this.catchClass = catchClass; + this.catchMethod = catchMethod; + this.catchLocation = catchLocation; + } + + public Oop thread() { return thread; } + public Oop clazz() { return clazz; } + public JNIid methodID() { return method; } + public int location() { return location; } + public Oop exception() { return exception; } + public Oop catchClass() { return catchClass; } + public JNIid catchMethodID() { return catchMethod; } + public int catchLocation() { return catchLocation; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/JNIHandleAccessor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/JNIHandleAccessor.java new file mode 100644 index 00000000000..ba7c181c7ce --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/JNIHandleAccessor.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.livejvm; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +class JNIHandleAccessor { + private Address addr; + private ObjectHeap heap; + + JNIHandleAccessor(Address addr, ObjectHeap heap) { + this.addr = addr; + this.heap = heap; + } + + Oop getValue() { + // Accessing the contents of the JNIHandle is a double dereference + Address handle = addr.getAddressAt(0); + if (handle == null) return null; + return heap.newOop(handle.getOopHandleAt(0)); + } + + void setValue(Oop value) { + Address handle = addr.getAddressAt(0); + if (Assert.ASSERTS_ENABLED) { + Assert.that(handle != null, "Must have valid global JNI handle for setting"); + } + handle.setOopHandleAt(0, value.getHandle()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/ServiceabilityAgentJVMDIModule.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/ServiceabilityAgentJVMDIModule.java new file mode 100644 index 00000000000..5fdc5841810 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/livejvm/ServiceabilityAgentJVMDIModule.java @@ -0,0 +1,415 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.livejvm; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/** Provides Java programming language-level interaction with a live + Java HotSpot VM via the use of the SA's JVMDI module. This is an + experimental mechanism. The BugSpot debugger should be converted + to use the JVMDI/JDWP-based JDI implementation for live process + interaction once the JDI binding for the SA is complete. */ + +public class ServiceabilityAgentJVMDIModule { + private Debugger dbg; + private String[] saLibNames; + private String saLibName; + private boolean attached; + + private boolean suspended; + + private static final int JVMDI_EVENT_BREAKPOINT = 2; + private static final int JVMDI_EVENT_EXCEPTION = 4; + + private static long timeoutMillis = 3000; + + // Values in target process + // Events sent from VM to SA + private CIntegerAccessor saAttached; + private CIntegerAccessor saEventPending; + private CIntegerAccessor saEventKind; + // Exception events + private JNIHandleAccessor saExceptionThread; + private JNIHandleAccessor saExceptionClass; + private JNIid saExceptionMethod; + private CIntegerAccessor saExceptionLocation; + private JNIHandleAccessor saExceptionException; + private JNIHandleAccessor saExceptionCatchClass; + private JNIid saExceptionCatchMethod; + private CIntegerAccessor saExceptionCatchLocation; + // Breakpoint events + private JNIHandleAccessor saBreakpointThread; + private JNIHandleAccessor saBreakpointClass; + private JNIid saBreakpointMethod; + private CIntegerAccessor saBreakpointLocation; + // Commands sent by the SA to the VM + private int SA_CMD_SUSPEND_ALL; + private int SA_CMD_RESUME_ALL; + private int SA_CMD_TOGGLE_BREAKPOINT; + private int SA_CMD_BUF_SIZE; + private CIntegerAccessor saCmdPending; + private CIntegerAccessor saCmdType; + private CIntegerAccessor saCmdResult; + private CStringAccessor saCmdResultErrMsg; + // Toggle breakpoint command arguments + private CStringAccessor saCmdBkptSrcFileName; + private CStringAccessor saCmdBkptPkgName; + private CIntegerAccessor saCmdBkptLineNumber; + private CIntegerAccessor saCmdBkptResWasError; + private CIntegerAccessor saCmdBkptResLineNumber; + private CIntegerAccessor saCmdBkptResBCI; + private CIntegerAccessor saCmdBkptResWasSet; + private CStringAccessor saCmdBkptResMethodName; + private CStringAccessor saCmdBkptResMethodSig; + + public ServiceabilityAgentJVMDIModule(Debugger dbg, String[] saLibNames) { + this.dbg = dbg; + this.saLibNames = saLibNames; + } + + /** Indicates whether a call to attach() should complete without an + exception. */ + public boolean canAttach() { + return setupLookup("SA_CMD_SUSPEND_ALL"); + } + + /** Attempt to initiate a connection with the JVMDI module in the + target VM. */ + public void attach() throws DebuggerException { + if (!canAttach()) { + throw new DebuggerException("Unable to initiate symbol lookup in SA's JVMDI module"); + } + + if (attached) { + throw new DebuggerException("Already attached"); + } + + // Attempt to look up well-known symbols in the target VM. + SA_CMD_SUSPEND_ALL = lookupConstInt("SA_CMD_SUSPEND_ALL"); + SA_CMD_RESUME_ALL = lookupConstInt("SA_CMD_RESUME_ALL"); + SA_CMD_TOGGLE_BREAKPOINT = lookupConstInt("SA_CMD_TOGGLE_BREAKPOINT"); + SA_CMD_BUF_SIZE = lookupConstInt("SA_CMD_BUF_SIZE"); + + saAttached = lookupCInt("saAttached"); + saEventPending = lookupCInt("saEventPending"); + saEventKind = lookupCInt("saEventKind"); + saCmdPending = lookupCInt("saCmdPending"); + saCmdType = lookupCInt("saCmdType"); + saCmdResult = lookupCInt("saCmdResult"); + saCmdResultErrMsg = lookupCString("saCmdResultErrMsg", SA_CMD_BUF_SIZE); + // Toggling of breakpoints + saCmdBkptSrcFileName = lookupCString("saCmdBkptSrcFileName", SA_CMD_BUF_SIZE); + saCmdBkptPkgName = lookupCString("saCmdBkptPkgName", SA_CMD_BUF_SIZE); + saCmdBkptLineNumber = lookupCInt("saCmdBkptLineNumber"); + saCmdBkptResWasError = lookupCInt("saCmdBkptResWasError"); + saCmdBkptResLineNumber = lookupCInt("saCmdBkptResLineNumber"); + saCmdBkptResBCI = lookupCInt("saCmdBkptResBCI"); + saCmdBkptResWasSet = lookupCInt("saCmdBkptResWasSet"); + saCmdBkptResMethodName = lookupCString("saCmdBkptResMethodName", SA_CMD_BUF_SIZE); + saCmdBkptResMethodSig = lookupCString("saCmdBkptResMethodSig", SA_CMD_BUF_SIZE); + + // Check for existence of symbols needed later + // FIXME: should probably cache these since we can't support the + // -Xrun module or the VM getting unloaded anyway + lookup("saExceptionThread"); + lookup("saExceptionClass"); + lookup("saExceptionMethod"); + lookup("saExceptionLocation"); + lookup("saExceptionException"); + lookup("saExceptionCatchClass"); + lookup("saExceptionCatchMethod"); + lookup("saExceptionCatchLocation"); + lookup("saBreakpointThread"); + lookup("saBreakpointClass"); + lookup("saBreakpointMethod"); + lookup("saBreakpointLocation"); + + saAttached.setValue(1); + attached = true; + } + + public void detach() { + saAttached.setValue(0); + attached = false; + saLibName = null; + } + + /** Set the timeout value (in milliseconds) for the VM to reply to + commands. Once this timeout has elapsed, the VM is assumed to + have disconnected. Defaults to 3000 milliseconds (3 seconds). */ + public void setCommandTimeout(long millis) { + timeoutMillis = millis; + } + + /** Get the timeout value (in milliseconds) for the VM to reply to + commands. Once this timeout has elapsed, the VM is assumed to + have disconnected. Defaults to 3000 milliseconds (3 seconds). */ + public long getCommandTimeout() { + return timeoutMillis; + } + + /** Indicates whether a Java debug event is pending */ + public boolean eventPending() { + return (saEventPending.getValue() != 0); + } + + /** Poll for event; returns null if none pending. */ + public Event eventPoll() { + if (saEventPending.getValue() == 0) { + return null; + } + + int kind = (int) saEventKind.getValue(); + switch (kind) { + case JVMDI_EVENT_EXCEPTION: { + JNIHandleAccessor thread = lookupJNIHandle("saExceptionThread"); + JNIHandleAccessor clazz = lookupJNIHandle("saExceptionClass"); + JNIid method = lookupJNIid("saExceptionMethod"); + CIntegerAccessor location = lookupCInt("saExceptionLocation"); + JNIHandleAccessor exception = lookupJNIHandle("saExceptionException"); + JNIHandleAccessor catchClass = lookupJNIHandle("saExceptionCatchClass"); + JNIid catchMethod = lookupJNIid("saExceptionCatchMethod"); + CIntegerAccessor catchLocation = lookupCInt("saExceptionCatchLocation"); + return new ExceptionEvent(thread.getValue(), clazz.getValue(), method, + (int) location.getValue(), exception.getValue(), + catchClass.getValue(), catchMethod, (int) catchLocation.getValue()); + } + + case JVMDI_EVENT_BREAKPOINT: { + JNIHandleAccessor thread = lookupJNIHandle("saBreakpointThread"); + JNIHandleAccessor clazz = lookupJNIHandle("saBreakpointClass"); + JNIid method = lookupJNIid("saBreakpointMethod"); + CIntegerAccessor location = lookupCInt("saBreakpointLocation"); + return new BreakpointEvent(thread.getValue(), clazz.getValue(), + method, (int) location.getValue()); + } + + default: + throw new DebuggerException("Unsupported event type " + kind); + } + } + + /** Continue past current event */ + public void eventContinue() { + saEventPending.setValue(0); + } + + /** Suspend all Java threads in the target VM. Throws + DebuggerException if the VM disconnected. */ + public void suspend() { + saCmdType.setValue(SA_CMD_SUSPEND_ALL); + saCmdPending.setValue(1); + waitForCommandCompletion(); + suspended = true; + } + + /** Resume all Java threads in the target VM. Throws + DebuggerException if the VM disconnected. */ + public void resume() { + saCmdType.setValue(SA_CMD_RESUME_ALL); + saCmdPending.setValue(1); + waitForCommandCompletion(); + suspended = false; + } + + /** Indicates whether all Java threads have been suspended via this + interface. */ + public boolean isSuspended() { + return suspended; + } + + /** Information about toggling of breakpoints */ + public static class BreakpointToggleResult { + private boolean success; + private String errMsg; + private int lineNumber; + private int bci; + private boolean wasSet; + private String methodName; + private String methodSig; + + /** Success constructor */ + public BreakpointToggleResult(int lineNumber, int bci, boolean wasSet, + String methodName, String methodSig) { + this.lineNumber = lineNumber; + this.bci = bci; + this.wasSet = wasSet; + this.methodName = methodName; + this.methodSig = methodSig; + success = true; + } + + /** Failure constructor */ + public BreakpointToggleResult(String errMsg) { + this.errMsg = errMsg; + success = false; + } + + /** Indicates whether this represents a successful return or not */ + public boolean getSuccess() { return success; } + + /** Valid only if getSuccess() returns false */ + public String getErrMsg() { return errMsg; } + + /** Line number at which breakpoint toggle occurred; valid only if + getSuccess() returns true. */ + public int getLineNumber() { return lineNumber; } + + /** BCI at which breakpoint toggle occurred; valid only if + getSuccess() returns true. */ + public int getBCI() { return bci; } + + /** Indicates whether the breakpoint toggle was the set of a + breakpoint or not; valid only if getSuccess() returns true. */ + public boolean getWasSet() { return wasSet; } + + /** Method name in which the breakpoint toggle occurred; valid + only if getSuccess() returns true. */ + public String getMethodName() { return methodName; } + + /** Method signature in which the breakpoint toggle occurred; + valid only if getSuccess() returns true. */ + public String getMethodSignature() { return methodSig; } + } + + /** Toggle a breakpoint. Throws DebuggerException if a real error + occurred; otherwise returns non-null BreakpointToggleResult. The + work of scanning the loaded classes is done in the target VM + because it turns out to be significantly faster than scanning + through the system dictionary from the SA, and interactivity + when setting breakpoints is important. */ + public BreakpointToggleResult toggleBreakpoint(String srcFileName, + String pkgName, + int lineNo) { + saCmdBkptSrcFileName.setValue(srcFileName); + saCmdBkptPkgName.setValue(pkgName); + saCmdBkptLineNumber.setValue(lineNo); + saCmdType.setValue(SA_CMD_TOGGLE_BREAKPOINT); + saCmdPending.setValue(1); + if (waitForCommandCompletion(true)) { + return new BreakpointToggleResult((int) saCmdBkptResLineNumber.getValue(), + (int) saCmdBkptResBCI.getValue(), + (saCmdBkptResWasSet.getValue() != 0), + saCmdBkptResMethodName.getValue(), + saCmdBkptResMethodSig.getValue()); + } else { + return new BreakpointToggleResult(saCmdResultErrMsg.getValue()); + } + } + + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private CIntegerAccessor lookupCInt(String symbolName) { + return new CIntegerAccessor(lookup(symbolName), 4, false); + } + + private CStringAccessor lookupCString(String symbolName, int bufLen) { + return new CStringAccessor(lookup(symbolName), bufLen); + } + + private JNIHandleAccessor lookupJNIHandle(String symbolName) { + return new JNIHandleAccessor(lookup(symbolName), VM.getVM().getObjectHeap()); + } + + private JNIid lookupJNIid(String symbolName) { + Address idAddr = lookup(symbolName).getAddressAt(0); + if (idAddr == null) { + return null; + } + return new JNIid(idAddr, VM.getVM().getObjectHeap()); + } + + private int lookupConstInt(String symbolName) { + Address addr = lookup(symbolName); + return (int) addr.getCIntegerAt(0, 4, false); + } + + private boolean setupLookup(String symbolName) { + if (saLibName == null) { + for (int i = 0; i < saLibNames.length; i++) { + Address addr = dbg.lookup(saLibNames[i], symbolName); + if (addr != null) { + saLibName = saLibNames[i]; + return true; + } + } + return false; + } + return true; + } + + private Address lookup(String symbolName) { + if (saLibName == null) { + for (int i = 0; i < saLibNames.length; i++) { + Address addr = dbg.lookup(saLibNames[i], symbolName); + if (addr != null) { + saLibName = saLibNames[i]; + return addr; + } + } + throw new DebuggerException("Unable to find symbol " + symbolName + " in any of the known names for the SA"); + } + + Address addr = dbg.lookup(saLibName, symbolName); + if (addr == null) { + throw new DebuggerException("Unable to find symbol " + symbolName + " in " + saLibName); + } + return addr; + } + + private void waitForCommandCompletion() { + waitForCommandCompletion(false); + } + + /** Returns true if command succeeded, false if not */ + private boolean waitForCommandCompletion(boolean forBreakpoint) { + long start = System.currentTimeMillis(); + long cur = start; + while ((saCmdPending.getValue() != 0) && + (cur - start < timeoutMillis)) { + try { + java.lang.Thread.currentThread().sleep(10); + } catch (InterruptedException e) { + } + cur = System.currentTimeMillis(); + } + if (saCmdPending.getValue() != 0) { + detach(); + throw new DebuggerException("VM appears to have died"); + } + boolean succeeded = saCmdResult.getValue() == 0; + if (!succeeded && + (!forBreakpoint || saCmdBkptResWasError.getValue() != 0)) { + String err = saCmdResultErrMsg.getValue(); + throw new DebuggerException("Error executing JVMDI command: " + err); + } + return succeeded; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSBitMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSBitMap.java new file mode 100644 index 00000000000..695a42c7183 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSBitMap.java @@ -0,0 +1,110 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class CMSBitMap extends VMObject { + private static AddressField bmStartWordField; + private static CIntegerField bmWordSizeField; + private static CIntegerField shifterField; + //private static AddressField bmField; + private static long virtualSpaceFieldOffset; + + public CMSBitMap(Address addr) { + super(addr); + } + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("CMSBitMap"); + bmStartWordField = type.getAddressField("_bmStartWord"); + bmWordSizeField = type.getCIntegerField("_bmWordSize"); + shifterField = type.getCIntegerField("_shifter"); + //bmField = type.getAddressField("_bm"); + virtualSpaceFieldOffset = type.getField("_virtual_space").getOffset(); + } + public void printAll() { + System.out.println("bmStartWord(): "+bmStartWord()); + System.out.println("bmWordSize(): "+bmWordSize()); + System.out.println("shifter(): "+shifter()); + } + + public Address bmStartWord() { + return bmStartWordField.getValue(addr); + } + public long bmWordSize() { + return bmWordSizeField.getValue(addr); + } + public long shifter() { + return shifterField.getValue(addr); + } + public VirtualSpace virtualSpace() { + return (VirtualSpace) VMObjectFactory.newObject(VirtualSpace.class, addr.addOffsetTo(virtualSpaceFieldOffset)); + } + + public BitMap bm() { + BitMap bitMap = new BitMap((int) (bmWordSize() >> (shifter() + 3) )); + VirtualSpace vs = virtualSpace(); + //bitMap.set_size((int)vs.committedSize()); + bitMap.set_map(vs.low()); + return bitMap; + } + + public Address getNextMarkedWordAddress(Address addr) { + Address endWord = bmStartWord().addOffsetTo(bmWordSize()); + int nextOffset = bm().getNextOneOffset(heapWordToOffset(addr), heapWordToOffset(endWord) ); + Address nextAddr = offsetToHeapWord(nextOffset); + return nextAddr; + } + + int heapWordToOffset(Address addr) { + int temp = (int)addr.minus(bmStartWord()) / (int) VM.getVM().getAddressSize(); + int ret_val = temp >> shifter(); + return ret_val; + } + + Address offsetToHeapWord(int offset) { + int temp = offset << shifter(); + return bmStartWord().addOffsetTo(temp*VM.getVM().getAddressSize()); + } + + boolean isMarked(Address addr) { + BitMap bm = bm(); + return bm.at(heapWordToOffset(addr)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSCollector.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSCollector.java new file mode 100644 index 00000000000..0e5aeefd7ad --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSCollector.java @@ -0,0 +1,76 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class CMSCollector extends VMObject { + private static long markBitMapFieldOffset; + + public CMSCollector(Address addr) { + super(addr); + } + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("CMSCollector"); + markBitMapFieldOffset = type.getField("_markBitMap").getOffset(); + } + + //Accessing mark bitmap + public CMSBitMap markBitMap() { + return (CMSBitMap) VMObjectFactory.newObject( + CMSBitMap.class, + addr.addOffsetTo(markBitMapFieldOffset)); + } + + public long blockSizeUsingPrintezisBits(Address addr) { + CMSBitMap markBitMap = markBitMap(); + long addressSize = VM.getVM().getAddressSize(); + if ( markBitMap.isMarked(addr) && markBitMap.isMarked(addr.addOffsetTo(1*addressSize)) ) { + System.err.println("Printezis bits are set..."); + Address nextOneAddr = markBitMap.getNextMarkedWordAddress(addr.addOffsetTo(2*addressSize)); + //return size in bytes + long size = (nextOneAddr.addOffsetTo(1*addressSize)).minus(addr); + return size; + } else { + //missing Printezis marks + System.err.println("Missing Printszis marks..."); + return -1; + } + + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSPermGen.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSPermGen.java new file mode 100644 index 00000000000..1d270858104 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSPermGen.java @@ -0,0 +1,56 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class CMSPermGen extends PermGen { + // The "generation" view. + private static AddressField genField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("CMSPermGen"); + genField = type.getAddressField("_gen"); + } + + public CMSPermGen(Address addr) { + super(addr); + } + + public Generation asGen() { + return GenerationFactory.newObject(genField.getValue(addr)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSPermGenGen.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSPermGenGen.java new file mode 100644 index 00000000000..f872060a6d7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CMSPermGenGen.java @@ -0,0 +1,37 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import sun.jvm.hotspot.debugger.*; + +public class CMSPermGenGen extends ConcurrentMarkSweepGeneration { + public CMSPermGenGen(Address addr) { + super(addr); + } + + public String name() { + return "concurrent-mark-sweep perm gen"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CardGeneration.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CardGeneration.java new file mode 100644 index 00000000000..24f0291ed9c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CardGeneration.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import sun.jvm.hotspot.debugger.*; + +/** Class CardGeneration is a generation that is covered by a card + table, and uses a card-size block-offset array to implement + block_start. */ + +public abstract class CardGeneration extends Generation { + public CardGeneration(Address addr) { + super(addr); + } + + // FIXME: not sure what I need to expose from here in order to have + // verification similar to that of the old RememberedSet +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CodeHeap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CodeHeap.java new file mode 100644 index 00000000000..64aec779eba --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CodeHeap.java @@ -0,0 +1,145 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class CodeHeap extends VMObject { + private static Field memoryField; + private static Field segmapField; + // private static CIntegerField numberOfCommittedSegmentsField; + // private static CIntegerField numberOfReservedSegmentsField; + // private static CIntegerField segmentSizeField; + private static CIntegerField log2SegmentSizeField; + // private static CIntegerField nextSegmentField; + // private static AddressField freelistField; + // private static CIntegerField freeSegmentsField; + + private VirtualSpace memory; + private VirtualSpace segmentMap; + private int log2SegmentSize; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("CodeHeap"); + + memoryField = type.getField("_memory"); + segmapField = type.getField("_segmap"); + log2SegmentSizeField = type.getCIntegerField("_log2_segment_size"); + + } + + public CodeHeap(Address addr) { + super(addr); + log2SegmentSize = (int) log2SegmentSizeField.getValue(addr); + segmentMap = new VirtualSpace(addr.addOffsetTo(segmapField.getOffset())); + memory = new VirtualSpace(addr.addOffsetTo(memoryField.getOffset())); + } + + public Address begin() { + return getMemory().low(); + } + + public Address end() { + return getMemory().high(); + } + + public boolean contains(Address p) { + return (begin().lessThanOrEqual(p) && end().greaterThan(p)); + } + + /** Returns the start of the block containing p or null */ + public Address findStart(Address p) { + if (!contains(p)) return null; + HeapBlock h = blockStart(p); + if (h == null || h.isFree()) { + return null; + } + return h.getAllocatedSpace(); + } + + public Address nextBlock(Address ptr) { + Address base = blockBase(ptr); + if (base == null) { + return null; + } + HeapBlock block = getBlockAt(base); + return base.addOffsetTo(block.getLength() * (1 << getLog2SegmentSize())); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private VirtualSpace getMemory() { + return memory; + } + + private VirtualSpace getSegmentMap() { + return segmentMap; + } + + private long segmentFor(Address p) { + return p.minus(getMemory().low()) >> getLog2SegmentSize(); + } + + private int getLog2SegmentSize() { + return log2SegmentSize; + } + + private HeapBlock getBlockAt(Address addr) { + return (HeapBlock) VMObjectFactory.newObject(HeapBlock.class, addr); + } + + + private HeapBlock blockStart(Address p) { + Address base = blockBase(p); + if (base == null) return null; + return getBlockAt(base); + } + + private Address blockBase(Address p) { + long i = segmentFor(p); + Address b = getSegmentMap().low(); + if (b.getCIntegerAt(i, 1, true) == 0xFF) { + return null; + } + while (b.getCIntegerAt(i, 1, true) > 0) { + i -= b.getCIntegerAt(i, 1, true); + } + return getMemory().low().addOffsetTo(i << getLog2SegmentSize()); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactibleFreeListSpace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactibleFreeListSpace.java new file mode 100644 index 00000000000..c1c701f223f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactibleFreeListSpace.java @@ -0,0 +1,161 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class CompactibleFreeListSpace extends CompactibleSpace { + private static AddressField collectorField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + long sizeofFreeChunk = db.lookupType("FreeChunk").getSize(); + VM vm = VM.getVM(); + MinChunkSizeInBytes = numQuanta(sizeofFreeChunk, vm.getMinObjAlignmentInBytes()) * + vm.getMinObjAlignmentInBytes(); + + Type type = db.lookupType("CompactibleFreeListSpace"); + collectorField = type.getAddressField("_collector"); + } + + public CompactibleFreeListSpace(Address addr) { + super(addr); + } + + // Accessing block offset table + public CMSCollector collector() { + return (CMSCollector) VMObjectFactory.newObject( + CMSCollector.class, + collectorField.getValue(addr)); + } + + public long used() { + List regions = getLiveRegions(); + long usedSize = 0L; + for (Iterator itr = regions.iterator(); itr.hasNext();) { + MemRegion mr = (MemRegion) itr.next(); + usedSize += mr.byteSize(); + } + return usedSize; + } + + public long free() { + return capacity() - used(); + } + + public void printOn(PrintStream tty) { + tty.print("free-list-space"); + } + + public Address skipBlockSizeUsingPrintezisBits(Address pos) { + CMSCollector collector = collector(); + long size = 0; + Address addr = null; + + if (collector != null) { + size = collector.blockSizeUsingPrintezisBits(pos); + if (size >= 3) { + addr = pos.addOffsetTo(adjustObjectSizeInBytes(size)); + } + } + return addr; + } + + public List/**/ getLiveRegions() { + List res = new ArrayList(); // List + VM vm = VM.getVM(); + Debugger dbg = vm.getDebugger(); + ObjectHeap heap = vm.getObjectHeap(); + Address cur = bottom(); + Address regionStart = cur; + Address limit = end(); + final long addressSize = vm.getAddressSize(); + + for (; cur.lessThan(limit);) { + Address klassOop = cur.getAddressAt(addressSize); + // FIXME: need to do a better job here. + // can I use bitMap here? + if (klassOop == null) { + //Find the object size using Printezis bits and skip over + System.err.println("Finding object size using Printezis bits and skipping over..."); + long size = collector().blockSizeUsingPrintezisBits(cur); + if (size == -1) { + System.err.println("Printezis bits not set..."); + break; + } + cur = cur.addOffsetTo(adjustObjectSizeInBytes(size)); + } + + if (FreeChunk.secondWordIndicatesFreeChunk(dbg.getAddressValue(klassOop))) { + if (! cur.equals(regionStart)) { + res.add(new MemRegion(regionStart, cur)); + } + FreeChunk fc = (FreeChunk) VMObjectFactory.newObject(FreeChunk.class, cur); + long chunkSize = fc.size(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(chunkSize > 0, "invalid FreeChunk size"); + } + // note that fc.size() gives chunk size in heap words + cur = cur.addOffsetTo(chunkSize * addressSize); + System.err.println("Free chunk in CMS heap, size="+chunkSize * addressSize); + regionStart = cur; + } else if (klassOop != null) { + Oop obj = heap.newOop(cur.addOffsetToAsOopHandle(0)); + long objectSize = obj.getObjectSize(); + cur = cur.addOffsetTo(adjustObjectSizeInBytes(objectSize)); + } + } + return res; + } + + //-- Internals only below this point + + // Unlike corresponding VM code, we operate on byte size rather than + // HeapWord size for convenience. + + private static long numQuanta(long x, long y) { + return ((x+y-1)/y); + } + + public static long adjustObjectSizeInBytes(long sizeInBytes) { + return Oop.alignObjectSize(Math.max(sizeInBytes, MinChunkSizeInBytes)); + } + + // FIXME: should I read this directly from VM? + private static long MinChunkSizeInBytes; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactibleSpace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactibleSpace.java new file mode 100644 index 00000000000..9c28f661007 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactibleSpace.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** A space that supports compaction operations. This is usually, but + not necessarily, a space that is normally contiguous. But, for + example, a free-list-based space whose normal collection is a + mark-sweep without compaction could still support compaction in + full GC's. */ + +public abstract class CompactibleSpace extends Space { + private static AddressField compactionTopField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("CompactibleSpace"); + + compactionTopField = type.getAddressField("_compaction_top"); + } + + public CompactibleSpace(Address addr) { + super(addr); + } + + /** May be used temporarily during a compaction phase. */ + public Address compactionTop() { + return compactionTopField.getValue(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactingPermGen.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactingPermGen.java new file mode 100644 index 00000000000..5f376372973 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactingPermGen.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** A PermGen implemented with a contiguous space. */ + +public class CompactingPermGen extends PermGen { + // The "generation" view. + private static AddressField genField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("CompactingPermGen"); + + genField = type.getAddressField("_gen"); + } + + public CompactingPermGen(Address addr) { + super(addr); + } + + public Generation asGen() { + return GenerationFactory.newObject(genField.getValue(addr)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactingPermGenGen.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactingPermGenGen.java new file mode 100644 index 00000000000..3f14f406223 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/CompactingPermGenGen.java @@ -0,0 +1,161 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** This is the "generation" view of a CompactingPermGen. */ +public class CompactingPermGenGen extends OneContigSpaceCardGeneration { + private static AddressField unsharedBottomField; + private static AddressField unsharedEndField; + private static AddressField sharedBottomField; + private static AddressField sharedEndField; + private static AddressField readOnlyBottomField; + private static AddressField readOnlyEndField; + private static AddressField readWriteBottomField; + private static AddressField readWriteEndField; + private static AddressField roSpaceField; + private static AddressField rwSpaceField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("CompactingPermGenGen"); + unsharedBottomField = type.getAddressField("unshared_bottom"); + unsharedEndField = type.getAddressField("unshared_end"); + sharedBottomField = type.getAddressField("shared_bottom"); + sharedEndField = type.getAddressField("shared_end"); + readOnlyBottomField = type.getAddressField("readonly_bottom"); + readOnlyEndField = type.getAddressField("readonly_end"); + readWriteBottomField = type.getAddressField("readwrite_bottom"); + readWriteEndField = type.getAddressField("readwrite_end"); + roSpaceField = type.getAddressField("_ro_space"); + rwSpaceField = type.getAddressField("_rw_space"); + } + + public boolean isSharingEnabled() { + return VM.getVM().isSharingEnabled(); + } + + // NEEDS_CLEANUP + public CompactingPermGenGen(Address addr) { + super(addr); + } + + public OffsetTableContigSpace roSpace() { + return newOffsetTableContigSpace(roSpaceField.getValue(addr)); + } + + public OffsetTableContigSpace rwSpace() { + return newOffsetTableContigSpace(rwSpaceField.getValue(addr)); + } + + public String name() { + return "compacting permanent generation"; + } + + public static Address unsharedBottom() { + return unsharedBottomField.getValue(); + } + + public static Address unsharedEnd() { + return unsharedEndField.getValue(); + } + + public static Address sharedBottom() { + return sharedBottomField.getValue(); + } + + public static Address sharedEnd() { + return sharedEndField.getValue(); + } + + public static Address readOnlyBottom() { + return readOnlyBottomField.getValue(); + } + + public static Address readOnlyEnd() { + return readOnlyEndField.getValue(); + } + + public static Address readWriteBottom() { + return readWriteBottomField.getValue(); + } + + public static Address readWriteEnd() { + return readWriteEndField.getValue(); + } + + public static boolean isShared(Address p) { + return sharedBottom().lessThanOrEqual(p) && sharedEnd().greaterThan(p); + } + + public static boolean isSharedReadOnly(Address p) { + return readOnlyBottom().lessThanOrEqual(p) && readOnlyEnd().greaterThan(p); + } + + public static boolean isSharedReadWrite(Address p) { + return readWriteBottom().lessThanOrEqual(p) && readWriteEnd().greaterThan(p); + } + + public boolean isIn(Address p) { + return unsharedBottom().lessThanOrEqual(p) && sharedEnd().greaterThan(p); + } + + public void spaceIterate(SpaceClosure blk, boolean usedOnly) { + super.spaceIterate(blk, usedOnly); + if (isSharingEnabled()) { + blk.doSpace(roSpace()); + blk.doSpace(rwSpace()); + } + } + + public void printOn(PrintStream tty) { + tty.print(" perm"); + theSpace().printOn(tty); + if (isSharingEnabled()) { + tty.print(" ro space: "); + roSpace().printOn(tty); + tty.print(", rw space: "); + rwSpace().printOn(tty); + } + } + + private OffsetTableContigSpace newOffsetTableContigSpace(Address addr) { + return (OffsetTableContigSpace) VMObjectFactory.newObject( + OffsetTableContigSpace.class, addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ConcurrentMarkSweepGeneration.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ConcurrentMarkSweepGeneration.java new file mode 100644 index 00000000000..af3e50b9624 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ConcurrentMarkSweepGeneration.java @@ -0,0 +1,81 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class ConcurrentMarkSweepGeneration extends CardGeneration { + private static AddressField cmsSpaceField; + + public ConcurrentMarkSweepGeneration(Address addr) { + super(addr); + } + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("ConcurrentMarkSweepGeneration"); + cmsSpaceField = type.getAddressField("_cmsSpace"); + } + + // Accessing space + public CompactibleFreeListSpace cmsSpace() { + return (CompactibleFreeListSpace) VMObjectFactory.newObject( + CompactibleFreeListSpace.class, + cmsSpaceField.getValue(addr)); + } + + public long capacity() { return cmsSpace().capacity(); } + public long used() { return cmsSpace().used(); } + public long free() { return cmsSpace().free(); } + public long contiguousAvailable() { throw new RuntimeException("not yet implemented"); } + public boolean contains(Address p) { return cmsSpace().contains(p); } + public void spaceIterate(SpaceClosure blk, boolean usedOnly) { + blk.doSpace(cmsSpace()); + } + + public Generation.Name kind() { + return Generation.Name.CONCURRENT_MARK_SWEEP; + } + + public String name() { + return "concurrent mark-sweep generation"; + } + + public void printOn(PrintStream tty) { + tty.println(name()); + cmsSpace().printOn(tty); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ContigPermSpace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ContigPermSpace.java new file mode 100644 index 00000000000..94ae79e7615 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ContigPermSpace.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import sun.jvm.hotspot.debugger.*; + +/** No additional functionality for now */ + +public class ContigPermSpace extends OffsetTableContigSpace { + public ContigPermSpace(Address addr) { + super(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ContiguousSpace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ContiguousSpace.java new file mode 100644 index 00000000000..e15a15612ed --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ContiguousSpace.java @@ -0,0 +1,97 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class ContiguousSpace extends CompactibleSpace { + private static AddressField topField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("ContiguousSpace"); + + topField = type.getAddressField("_top"); + } + + public ContiguousSpace(Address addr) { + super(addr); + } + + public Address top() { + return topField.getValue(addr); + } + + /** In bytes */ + public long capacity() { + return end().minus(bottom()); + } + + /** In bytes */ + public long used() { + return top().minus(bottom()); + } + + /** In bytes */ + public long free() { + return end().minus(top()); + } + + /** In a contiguous space we have a more obvious bound on what parts + contain objects. */ + public MemRegion usedRegion() { + return new MemRegion(bottom(), top()); + } + + /** Returns regions of Space where live objects live */ + public List/**/ getLiveRegions() { + List res = new ArrayList(); + res.add(new MemRegion(bottom(), top())); + return res; + } + + /** Testers */ + public boolean contains(Address p) { + return (bottom().lessThanOrEqual(p) && top().greaterThan(p)); + } + + public void printOn(PrintStream tty) { + tty.print(" [" + bottom() + "," + + top() + "," + end() + ")"); + super.printOn(tty); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/DefNewGeneration.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/DefNewGeneration.java new file mode 100644 index 00000000000..56f328ce21b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/DefNewGeneration.java @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** DefNewGeneration is a young generation containing eden, from- and + to-space. */ + +public class DefNewGeneration extends Generation { + protected static AddressField edenSpaceField; + protected static AddressField fromSpaceField; + protected static AddressField toSpaceField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("DefNewGeneration"); + + edenSpaceField = type.getAddressField("_eden_space"); + fromSpaceField = type.getAddressField("_from_space"); + toSpaceField = type.getAddressField("_to_space"); + } + + public DefNewGeneration(Address addr) { + super(addr); + } + + public Generation.Name kind() { + return Generation.Name.DEF_NEW; + } + + // Accessing spaces + public EdenSpace eden() { + return (EdenSpace) VMObjectFactory.newObject(EdenSpace.class, edenSpaceField.getValue(addr)); + } + + public ContiguousSpace from() { + return (ContiguousSpace) VMObjectFactory.newObject(ContiguousSpace.class, fromSpaceField.getValue(addr)); + } + + public ContiguousSpace to() { + return (ContiguousSpace) VMObjectFactory.newObject(ContiguousSpace.class, toSpaceField.getValue(addr)); + } + + public long capacity() { return eden().capacity() + from().capacity(); /* to() is only used during scavenge */ } + public long used() { return eden().used() + from().used(); /* to() is only used during scavenge */ } + public long free() { return eden().free() + from().free(); /* to() is only used during scavenge */ } + public long contiguousAvailable() { return eden().free(); } + + public String name() { + return "default new generation"; + } + + public void spaceIterate(SpaceClosure blk, boolean usedOnly) { + blk.doSpace(eden()); + blk.doSpace(from()); + if (!usedOnly) { + blk.doSpace(to()); + } + } + + public void printOn(PrintStream tty) { + tty.print(" eden"); + eden().printOn(tty); + tty.print(" from"); + from().printOn(tty); + tty.print(" to "); + to().printOn(tty); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Dictionary.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Dictionary.java new file mode 100644 index 00000000000..b4b7c5dc749 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Dictionary.java @@ -0,0 +1,108 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class Dictionary extends TwoOopHashtable { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + // just checking that the type exists + Type type = db.lookupType("Dictionary"); + } + + public Dictionary(Address addr) { + super(addr); + } + + // this is overriden here so that Hashtable.bucket will return + // object of DictionaryEntry.class + protected Class getHashtableEntryClass() { + return DictionaryEntry.class; + } + + /** Iterate over all klasses in dictionary; just the classes from + declaring class loaders */ + public void classesDo(SystemDictionary.ClassVisitor v) { + ObjectHeap heap = VM.getVM().getObjectHeap(); + int tblSize = tableSize(); + for (int index = 0; index < tblSize; index++) { + for (DictionaryEntry probe = (DictionaryEntry) bucket(index); probe != null; + probe = (DictionaryEntry) probe.next()) { + Oop k = probe.klass(); + if (k.isKlass() && + heap.equal(probe.loader(), ((InstanceKlass) k).getClassLoader())) { + v.visit((Klass) k); + } + } + } + } + + /** All classes, and their class loaders */ + public void classesDo(SystemDictionary.ClassAndLoaderVisitor v) { + int tblSize = tableSize(); + for (int index = 0; index < tblSize; index++) { + for (DictionaryEntry probe = (DictionaryEntry) bucket(index); probe != null; + probe = (DictionaryEntry) probe.next()) { + Oop k = probe.klass(); + if (k.isKlass()) { + v.visit((Klass) k, probe.loader()); + } + } + } + } + + public Klass find(int index, long hash, Symbol className, Oop classLoader, Oop protectionDomain) { + DictionaryEntry entry = getEntry(index, hash, className, classLoader); + if (entry != null && entry.isValidProtectionDomain(protectionDomain)) { + return entry.klass(); + } + return null; + } + + // - Internals only below this point + + private DictionaryEntry getEntry(int index, long hash, Symbol className, Oop classLoader) { + for (DictionaryEntry entry = (DictionaryEntry) bucket(index); entry != null; + entry = (DictionaryEntry) entry.next()) { + if (entry.hash() == hash && entry.equals(className, classLoader)) { + return entry; + } + } + return null; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/DictionaryEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/DictionaryEntry.java new file mode 100644 index 00000000000..23c5478c064 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/DictionaryEntry.java @@ -0,0 +1,112 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class DictionaryEntry extends sun.jvm.hotspot.utilities.HashtableEntry { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("DictionaryEntry"); + pdSetField = type.getAddressField("_pd_set"); + loaderField = type.getOopField("_loader"); + } + + // Fields + private static AddressField pdSetField; + private static sun.jvm.hotspot.types.OopField loaderField; + + // Accessors + + public ProtectionDomainEntry pdSet() { + Address tmp = pdSetField.getValue(addr); + return (ProtectionDomainEntry) VMObjectFactory.newObject( + ProtectionDomainEntry.class, tmp); + } + + public Oop loader() { + return VM.getVM().getObjectHeap().newOop(loaderField.getValue(addr)); + } + + public Klass klass() { + return (Klass) literal(); + } + + public DictionaryEntry(Address addr) { + super(addr); + } + + public boolean equals(Symbol className, Oop classLoader) { + InstanceKlass ik = (InstanceKlass) klass(); + Oop loader = loader(); + if (! ik.getName().equals(className)) { + return false; + } else { + return (loader == null)? (classLoader == null) : + (loader.equals(classLoader)); + } + } + + public boolean isValidProtectionDomain(Oop protectionDomain) { + if (protectionDomain == null) { + return true; + } else { + return containsProtectionDomain(protectionDomain); + } + } + + public boolean containsProtectionDomain(Oop protectionDomain) { + InstanceKlass ik = (InstanceKlass) klass(); + if (protectionDomain.equals(ik.getProtectionDomain())) { + return true; // Succeeds trivially + } + for (ProtectionDomainEntry current = pdSet(); current != null; + current = current.next()) { + if (protectionDomain.equals(current.protectionDomain())) { + return true; + } + } + return false; + } + + /* covariant return type :-( + public DictionaryEntry next() { + return (DictionaryEntry) super.next(); + } + For now, let the caller cast it .. + */ +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/EdenSpace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/EdenSpace.java new file mode 100644 index 00000000000..2b92c470ad2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/EdenSpace.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/**

Class EdenSpace describes eden-space in new + generation. (Currently it does not add any significant + functionality beyond ContiguousSpace.) */ + +public class EdenSpace extends ContiguousSpace { + public EdenSpace(Address addr) { + super(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/FreeChunk.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/FreeChunk.java new file mode 100644 index 00000000000..7802d41b650 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/FreeChunk.java @@ -0,0 +1,80 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +public class FreeChunk extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("FreeChunk"); + nextField = type.getAddressField("_next"); + prevField = type.getAddressField("_prev"); + sizeField = type.getCIntegerField("_size"); + } + + // Fields + private static AddressField nextField; + private static AddressField prevField; + private static CIntegerField sizeField; + + // Accessors + public FreeChunk next() { + return (FreeChunk) VMObjectFactory.newObject(FreeChunk.class, nextField.getValue(addr)); + } + + public FreeChunk prev() { + Address prev = prevField.getValue(addr).andWithMask(~0x3); + return (FreeChunk) VMObjectFactory.newObject(FreeChunk.class, prev); + } + + public long size() { + return sizeField.getValue(addr); + } + + public FreeChunk(Address addr) { + super(addr); + } + + public static boolean secondWordIndicatesFreeChunk(long word) { + return (word & 0x1L) == 0x1L; + } + + public boolean isFree() { + Debugger dbg = VM.getVM().getDebugger(); + Address prev = prevField.getValue(addr); + return secondWordIndicatesFreeChunk(dbg.getAddressValue(prev)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenCollectedHeap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenCollectedHeap.java new file mode 100644 index 00000000000..9d461a4db87 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenCollectedHeap.java @@ -0,0 +1,146 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class GenCollectedHeap extends SharedHeap { + private static CIntegerField nGensField; + private static long gensOffset; + private static AddressField genSpecsField; + + private static GenerationFactory genFactory; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("GenCollectedHeap"); + + nGensField = type.getCIntegerField("_n_gens"); + gensOffset = type.getField("_gens").getOffset(); + genSpecsField = type.getAddressField("_gen_specs"); + + genFactory = new GenerationFactory(); + } + + public GenCollectedHeap(Address addr) { + super(addr); + } + + public int nGens() { + return (int) nGensField.getValue(addr); + } + + public Generation getGen(int i) { + if (Assert.ASSERTS_ENABLED) { + Assert.that((i >= 0) && (i < nGens()), "Index " + i + + " out of range (should be between 0 and " + nGens() + ")"); + } + + if ((i < 0) || (i >= nGens())) { + return null; + } + + Address genAddr = addr.getAddressAt(gensOffset + + (i * VM.getVM().getAddressSize())); + return genFactory.newObject(addr.getAddressAt(gensOffset + + (i * VM.getVM().getAddressSize()))); + } + + public boolean isIn(Address a) { + for (int i = 0; i < nGens(); i++) { + Generation gen = getGen(i); + if (gen.isIn(a)) { + return true; + } + } + + return permGen().isIn(a); + } + + public long capacity() { + long capacity = 0; + for (int i = 0; i < nGens(); i++) { + capacity += getGen(i).capacity(); + } + return capacity; + } + + public long used() { + long used = 0; + for (int i = 0; i < nGens(); i++) { + used += getGen(i).used(); + } + return used; + } + + /** Package-private access to GenerationSpecs */ + GenerationSpec spec(int level) { + if (Assert.ASSERTS_ENABLED) { + Assert.that((level >= 0) && (level < nGens()), "Index " + level + + " out of range (should be between 0 and " + nGens() + ")"); + } + + if ((level < 0) || (level >= nGens())) { + return null; + } + + Address ptrList = genSpecsField.getValue(addr); + if (ptrList == null) { + return null; + } + return (GenerationSpec) + VMObjectFactory.newObject(GenerationSpec.class, + ptrList.getAddressAt(level * VM.getVM().getAddressSize())); + } + + public CollectedHeapName kind() { + return CollectedHeapName.GEN_COLLECTED_HEAP; + } + + public void printOn(PrintStream tty) { + for (int i = 0; i < nGens(); i++) { + tty.print("Gen " + i + ": "); + getGen(i).printOn(tty); + tty.println("Invocations: " + getGen(i).invocations()); + tty.println(); + } + permGen().printOn(tty); + tty.println("Invocations: " + permGen().invocations()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Generation.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Generation.java new file mode 100644 index 00000000000..7cd282aa491 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Generation.java @@ -0,0 +1,218 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +/**

The (supported) Generation hierarchy currently looks like this:

+ +
    +
  • Generation +
      +
    • CardGeneration +
        +
      • OneContigSpaceCardGeneration +
          +
        • CompactingPermGenGen +
        • TenuredGeneration +
        +
      +
    • DefNewGeneration +
    +
+*/ + + +public abstract class Generation extends VMObject { + private static long reservedFieldOffset; + private static long virtualSpaceFieldOffset; + private static CIntegerField levelField; + protected static final int K = 1024; + // Fields for class StatRecord + private static Field statRecordField; + private static CIntegerField invocationField; + + // constants from Name enum + private static int NAME_DEF_NEW; + private static int NAME_PAR_NEW; + private static int NAME_MARK_SWEEP_COMPACT; + private static int NAME_CONCURRENT_MARK_SWEEP; + private static int NAME_OTHER; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("Generation"); + + reservedFieldOffset = type.getField("_reserved").getOffset(); + virtualSpaceFieldOffset = type.getField("_virtual_space").getOffset(); + levelField = type.getCIntegerField("_level"); + // StatRecord + statRecordField = type.getField("_stat_record"); + type = db.lookupType("Generation::StatRecord"); + invocationField = type.getCIntegerField("invocations"); + + // constants from Generation::Name + NAME_DEF_NEW = db.lookupIntConstant("Generation::DefNew").intValue(); + NAME_PAR_NEW = db.lookupIntConstant("Generation::ParNew").intValue(); + NAME_MARK_SWEEP_COMPACT = db.lookupIntConstant("Generation::MarkSweepCompact").intValue(); + NAME_CONCURRENT_MARK_SWEEP = db.lookupIntConstant("Generation::ConcurrentMarkSweep").intValue(); + NAME_OTHER = db.lookupIntConstant("Generation::Other").intValue(); + } + + public Generation(Address addr) { + super(addr); + } + + public static class Name { + public static final Name DEF_NEW = new Name("DefNew"); + public static final Name PAR_NEW = new Name("ParNew"); + public static final Name MARK_SWEEP_COMPACT = new Name("MarkSweepCompact"); + public static final Name CONCURRENT_MARK_SWEEP = new Name("ConcurrentMarkSweep"); + public static final Name OTHER = new Name("Other"); + + private Name(String value) { + this.value = value; + } + + private String value; + public String toString() { + return value; + } + } + + public Generation.Name kind() { + return Generation.Name.OTHER; + } + + static Generation.Name nameForEnum(int value) { + if (value == NAME_DEF_NEW) { + return Name.DEF_NEW; + } else if (value == NAME_PAR_NEW) { + return Name.PAR_NEW; + } else if (value == NAME_MARK_SWEEP_COMPACT) { + return Name.MARK_SWEEP_COMPACT; + } else if (value == NAME_CONCURRENT_MARK_SWEEP) { + return Name.CONCURRENT_MARK_SWEEP; + } else if (value == NAME_OTHER) { + return Name.OTHER; + } else { + throw new RuntimeException("should not reach here"); + } + } + + public GenerationSpec spec() { + return ((GenCollectedHeap) VM.getVM().getUniverse().heap()).spec(level()); + } + + public int level() { + return (int) levelField.getValue(addr); + } + + public int invocations() { + return getStatRecord().getInvocations(); + } + + /** The maximum number of object bytes the generation can currently + hold. */ + public abstract long capacity(); + + /** The number of used bytes in the gen. */ + public abstract long used(); + + /** The number of free bytes in the gen. */ + public abstract long free(); + + /** The largest number of contiguous free words in the generation, + including expansion. (VM's version assumes it is called at a + safepoint.) */ + public abstract long contiguousAvailable(); + + public MemRegion reserved() { + return new MemRegion(addr.addOffsetTo(reservedFieldOffset)); + } + + /** Returns a region guaranteed to contain all the objects in the + generation. */ + public MemRegion usedRegion() { + return reserved(); + } + + /* Returns "TRUE" iff "p" points into an allocated object in the + generation. */ + public boolean isIn(Address p) { + GenerationIsInClosure blk = new GenerationIsInClosure(p); + spaceIterate(blk); + return (blk.space() != null); + } + + /** Returns "TRUE" iff "p" points into the reserved area of the + generation. */ + public boolean isInReserved(Address p) { + return reserved().contains(p); + } + + protected VirtualSpace virtualSpace() { + return (VirtualSpace) VMObjectFactory.newObject(VirtualSpace.class, addr.addOffsetTo(virtualSpaceFieldOffset)); + } + + public abstract String name(); + + /** Equivalent to spaceIterate(blk, false) */ + public void spaceIterate(SpaceClosure blk) { + spaceIterate(blk, false); + } + + /** Iteration - do not use for time critical operations */ + public abstract void spaceIterate(SpaceClosure blk, boolean usedOnly); + + public void print() { printOn(System.out); } + public abstract void printOn(PrintStream tty); + + public static class StatRecord extends VMObject { + public StatRecord(Address addr) { + super(addr); + } + + public int getInvocations() { + return (int) invocationField.getValue(addr); + } + + } + + private StatRecord getStatRecord() { + return (StatRecord) VMObjectFactory.newObject(Generation.StatRecord.class, addr.addOffsetTo(statRecordField.getOffset())); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenerationFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenerationFactory.java new file mode 100644 index 00000000000..eafd292d196 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenerationFactory.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** Factory containing a VirtualConstructor suitable for instantiating + wrapper objects for all types of generations */ + +public class GenerationFactory { + private static VirtualConstructor ctor; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + ctor = new VirtualConstructor(db); + + ctor.addMapping("CompactingPermGenGen", CompactingPermGenGen.class); + ctor.addMapping("CMSPermGenGen", CMSPermGenGen.class); + ctor.addMapping("DefNewGeneration", DefNewGeneration.class); + ctor.addMapping("ParNewGeneration", ParNewGeneration.class); + ctor.addMapping("TenuredGeneration", TenuredGeneration.class); + ctor.addMapping("ConcurrentMarkSweepGeneration", ConcurrentMarkSweepGeneration.class); + } + + public static Generation newObject(Address addr) { + try { + return (Generation) ctor.instantiateWrapperFor(addr); + } catch (WrongTypeException e) { + return new Generation(addr) { + public String name() { + return "unknown generation type"; + } + public void spaceIterate(SpaceClosure blk, boolean usedOnly) { + } + public void printOn(java.io.PrintStream tty) { + tty.println("unknown subtype of Generation @ " + getAddress() + " (" + + virtualSpace().low() + "," + virtualSpace().high() + ")"); + } + public long used() { + return 0; + } + public long free() { + return 0; + } + public long capacity() { + return 0; + } + public long contiguousAvailable() { + return 0; + } + + }; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenerationIsInClosure.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenerationIsInClosure.java new file mode 100644 index 00000000000..3e60ea0117b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenerationIsInClosure.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import sun.jvm.hotspot.debugger.*; + +/** Should only be used once */ + +class GenerationIsInClosure implements SpaceClosure { + private Address p; + private Space sp; + + GenerationIsInClosure(Address p) { + this.p = p; + } + + public void doSpace(Space s) { + if (s.contains(p)) { + sp = s; + } + } + + Space space() { + return sp; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenerationSpec.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenerationSpec.java new file mode 100644 index 00000000000..d58d5b65126 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/GenerationSpec.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class GenerationSpec extends VMObject { + private static CIntegerField nameField; + private static CIntegerField initSizeField; + private static CIntegerField maxSizeField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("GenerationSpec"); + + nameField = type.getCIntegerField("_name"); + initSizeField = type.getCIntegerField("_init_size"); + maxSizeField = type.getCIntegerField("_max_size"); + } + + public GenerationSpec(Address addr) { + super(addr); + } + + public Generation.Name name() { + return Generation.nameForEnum((int)nameField.getValue(addr)); + } + + public long initSize() { + return initSizeField.getValue(addr); + } + + public long maxSize() { + return maxSizeField.getValue(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/HeapBlock.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/HeapBlock.java new file mode 100644 index 00000000000..606a58da775 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/HeapBlock.java @@ -0,0 +1,96 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class HeapBlock extends VMObject { + private static long heapBlockSize; + private static Field headerField; + // Fields for class Header + private static CIntegerField headerLengthField; + private static CIntegerField headerUsedField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + // HeapBlock + Type type = db.lookupType("HeapBlock"); + heapBlockSize = type.getSize(); + headerField = type.getField("_header"); + + // Header + type = db.lookupType("HeapBlock::Header"); + headerLengthField = type.getCIntegerField("_length"); + headerUsedField = type.getCIntegerField("_used"); + } + + public HeapBlock(Address addr) { + super(addr); + } + + public long getLength() { + return getHeader().getLength(); + } + + public boolean isFree() { + return getHeader().isFree(); + } + + public Address getAllocatedSpace() { + return addr.addOffsetTo(heapBlockSize); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + public static class Header extends VMObject { + public Header(Address addr) { + super(addr); + } + + public long getLength() { + return headerLengthField.getValue(addr); + } + + public boolean isFree() { + return (headerUsedField.getValue(addr) == 0); + } + } + + private Header getHeader() { + return (Header) VMObjectFactory.newObject(HeapBlock.Header.class, addr.addOffsetTo(headerField.getOffset())); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/LoaderConstraintEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/LoaderConstraintEntry.java new file mode 100644 index 00000000000..dfc9f26598e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/LoaderConstraintEntry.java @@ -0,0 +1,90 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class LoaderConstraintEntry extends sun.jvm.hotspot.utilities.HashtableEntry { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("LoaderConstraintEntry"); + nameField = type.getOopField("_name"); + numLoadersField = type.getCIntegerField("_num_loaders"); + maxLoadersField = type.getCIntegerField("_max_loaders"); + loadersField = type.getAddressField("_loaders"); + } + + // Fields + private static sun.jvm.hotspot.types.OopField nameField; + private static CIntegerField numLoadersField; + private static CIntegerField maxLoadersField; + private static AddressField loadersField; + + // Accessors + + public Symbol name() { + return (Symbol) VM.getVM().getObjectHeap().newOop(nameField.getValue(addr)); + } + + public int numLoaders() { + return (int) numLoadersField.getValue(addr); + } + + public int maxLoaders() { + return (int) maxLoadersField.getValue(addr); + } + + public Oop initiatingLoader(int i) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(i >= 0 && i < numLoaders(), "invalid index"); + } + Address loaders = loadersField.getValue(addr); + OopHandle loader = loaders.addOffsetToAsOopHandle(i * VM.getVM().getOopSize()); + return VM.getVM().getObjectHeap().newOop(loader); + } + + public LoaderConstraintEntry(Address addr) { + super(addr); + } + + /* covariant return type :-( + public LoaderConstraintEntry next() { + return (LoaderConstraintEntry) super.next(); + } + For now, let the caller cast it .. + */ +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/LoaderConstraintTable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/LoaderConstraintTable.java new file mode 100644 index 00000000000..9d404bc1f57 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/LoaderConstraintTable.java @@ -0,0 +1,65 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class LoaderConstraintTable extends TwoOopHashtable { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("LoaderConstraintTable"); + nofBuckets = db.lookupIntConstant("LoaderConstraintTable::_nof_buckets").intValue(); + } + + // Fields + private static int nofBuckets; + + // Accessors + public static int getNumOfBuckets() { + return nofBuckets; + } + + public LoaderConstraintTable(Address addr) { + super(addr); + } + + // this is overriden here so that Hashtable.bucket will return + // object of LoaderConstraintEntry.class + protected Class getHashtableEntryClass() { + return LoaderConstraintEntry.class; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/MemRegion.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/MemRegion.java new file mode 100644 index 00000000000..582d730aa7d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/MemRegion.java @@ -0,0 +1,172 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** A very simple data structure representing a contigous region of + address space. */ + +public class MemRegion implements Cloneable { + private Address start; + private long byteSize; + + private static AddressField startField; + private static CIntegerField wordSizeField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("MemRegion"); + + startField = type.getAddressField("_start"); + wordSizeField = type.getCIntegerField("_word_size"); + } + + public MemRegion() { + } + + /** This constructor takes a "MemRegion*" in the target process */ + public MemRegion(Address memRegionAddr) { + this(startField.getValue(memRegionAddr), + wordSizeField.getValue(memRegionAddr)); + } + + public MemRegion(Address start, long wordSize) { + setStart(start); + setWordSize(wordSize); + } + + public MemRegion(Address start, Address limit) { + setStart(start); + byteSize = limit.minus(start); + } + + public Object clone() { + return new MemRegion(start, byteSize); + } + + public MemRegion copy() { + return (MemRegion) clone(); + } + + public MemRegion intersection(MemRegion mr2) { + MemRegion res = new MemRegion(); + if (AddressOps.gt(mr2.start(), start())) { + res.setStart(mr2.start()); + } else { + res.setStart(start()); + } + Address resEnd; + Address end = end(); + Address mr2End = mr2.end(); + if (AddressOps.lt(end, mr2End)) { + resEnd = end; + } else { + resEnd = mr2End; + } + if (AddressOps.lt(resEnd, res.start())) { + res.setStart(null); + res.setWordSize(0); + } else { + res.setEnd(resEnd); + } + return res; + } + + public MemRegion union(MemRegion mr2) { + MemRegion res = new MemRegion(); + if (AddressOps.lt(mr2.start(), start())) { + res.setStart(mr2.start()); + } else { + res.setStart(start()); + } + Address resEnd; + Address end = end(); + Address mr2End = mr2.end(); + if (AddressOps.gt(end, mr2End)) { + resEnd = end; + } else { + resEnd = mr2End; + } + res.setEnd(resEnd); + return res; + } + + public Address start() { + return start; + } + + public OopHandle startAsOopHandle() { + return start().addOffsetToAsOopHandle(0); + } + + public Address end() { + return start.addOffsetTo(byteSize); + } + + public OopHandle endAsOopHandle() { + return end().addOffsetToAsOopHandle(0); + } + + public void setStart(Address start) { + this.start = start; + } + + public void setEnd(Address end) { + byteSize = end.minus(start); + } + + public void setWordSize(long wordSize) { + byteSize = VM.getVM().getAddressSize() * wordSize; + } + + public boolean contains(MemRegion mr2) { + return AddressOps.lte(start, mr2.start) && AddressOps.gte(end(), mr2.end()); + } + + public boolean contains(Address addr) { + return AddressOps.gte(addr, start()) && AddressOps.lt(addr, end()); + } + + public long byteSize() { + return byteSize; + } + + public long wordSize() { + return byteSize / VM.getVM().getAddressSize(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/OffsetTableContigSpace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/OffsetTableContigSpace.java new file mode 100644 index 00000000000..c4c7352624f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/OffsetTableContigSpace.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import sun.jvm.hotspot.debugger.*; + +/** No additional functionality for now */ + +public class OffsetTableContigSpace extends ContiguousSpace { + public OffsetTableContigSpace(Address addr) { + super(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/OneContigSpaceCardGeneration.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/OneContigSpaceCardGeneration.java new file mode 100644 index 00000000000..cbe7d1620d8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/OneContigSpaceCardGeneration.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/**

OneSpaceOldGeneration models a heap of old objects contained + in a single contiguous space.

+ +

Garbage collection is performed using mark-compact.

*/ + +public abstract class OneContigSpaceCardGeneration extends CardGeneration { + private static AddressField theSpaceField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("OneContigSpaceCardGeneration"); + + theSpaceField = type.getAddressField("_the_space"); + } + + public OneContigSpaceCardGeneration(Address addr) { + super(addr); + } + + public ContiguousSpace theSpace() { + return (ContiguousSpace) VMObjectFactory.newObject(ContiguousSpace.class, theSpaceField.getValue(addr)); + } + + public boolean isIn(Address p) { + return theSpace().contains(p); + } + + /** Space queries */ + public long capacity() { return theSpace().capacity(); } + public long used() { return theSpace().used(); } + public long free() { return theSpace().free(); } + public long contiguousAvailable() { return theSpace().free() + virtualSpace().uncommittedSize(); } + + public void spaceIterate(SpaceClosure blk, boolean usedOnly) { + blk.doSpace(theSpace()); + } + + public void printOn(PrintStream tty) { + tty.print(" old "); + theSpace().printOn(tty); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ParNewGeneration.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ParNewGeneration.java new file mode 100644 index 00000000000..0e9ac6049e5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ParNewGeneration.java @@ -0,0 +1,37 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import sun.jvm.hotspot.debugger.*; + +public class ParNewGeneration extends DefNewGeneration { + public ParNewGeneration(Address addr) { + super(addr); + } + + public Generation.Name kind() { + return Generation.Name.PAR_NEW; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/PermGen.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/PermGen.java new file mode 100644 index 00000000000..a49b67ba0a0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/PermGen.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; + +/** All heaps contains a "permanent generation," containing permanent + (reflective) objects. This is like a regular generation in some + ways, but unlike one in others, and so is split apart. (FIXME: + this distinction is confusing and seems unnecessary.) */ + +public abstract class PermGen extends VMObject { + // NEEDS_CLEANUP + public PermGen(Address addr) { + super(addr); + } + + public abstract Generation asGen(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/PlaceholderEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/PlaceholderEntry.java new file mode 100644 index 00000000000..023ac60f7a8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/PlaceholderEntry.java @@ -0,0 +1,70 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class PlaceholderEntry extends sun.jvm.hotspot.utilities.HashtableEntry { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("PlaceholderEntry"); + loaderField = type.getOopField("_loader"); + } + + // Field + private static sun.jvm.hotspot.types.OopField loaderField; + + // Accessor + public Oop loader() { + return VM.getVM().getObjectHeap().newOop(loaderField.getValue(addr)); + } + + public PlaceholderEntry(Address addr) { + super(addr); + } + + public Symbol klass() { + return (Symbol) literal(); + } + + /* covariant return type :-( + public PlaceholderEntry next() { + return (PlaceholderEntry) super.next(); + } + For now, let the caller cast it .. + */ +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/PlaceholderTable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/PlaceholderTable.java new file mode 100644 index 00000000000..c71c1436f1b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/PlaceholderTable.java @@ -0,0 +1,64 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class PlaceholderTable extends TwoOopHashtable { + public PlaceholderTable(Address addr) { + super(addr); + } + + // this is overriden here so that Hashtable.bucket will return + // object of PlacholderEntry.class + protected Class getHashtableEntryClass() { + return PlaceholderEntry.class; + } + + /** All array classes of primitive type, and their class loaders */ + public void primArrayClassesDo(SystemDictionary.ClassAndLoaderVisitor v) { + ObjectHeap heap = VM.getVM().getObjectHeap(); + int tblSize = tableSize(); + for (int index = 0; index < tblSize; index++) { + for (PlaceholderEntry probe = (PlaceholderEntry) bucket(index); probe != null; + probe = (PlaceholderEntry) probe.next()) { + Symbol sym = probe.klass(); + // array of primitive arrays are stored in system dictionary as placeholders + FieldType ft = new FieldType(sym); + if (ft.isArray()) { + FieldType.ArrayInfo info = ft.getArrayInfo(); + if (info.elementBasicType() != BasicType.getTObject()) { + Klass arrayKlass = heap.typeArrayKlassObj(info.elementBasicType()); + arrayKlass = arrayKlass.arrayKlassOrNull(info.dimension()); + v.visit(arrayKlass, probe.loader()); + } + } + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ProtectionDomainEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ProtectionDomainEntry.java new file mode 100644 index 00000000000..b6f04f0f095 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/ProtectionDomainEntry.java @@ -0,0 +1,63 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class ProtectionDomainEntry extends VMObject { + private static AddressField nextField; + private static sun.jvm.hotspot.types.OopField protectionDomainField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("ProtectionDomainEntry"); + + nextField = type.getAddressField("_next"); + protectionDomainField = type.getOopField("_protection_domain"); + } + + public ProtectionDomainEntry(Address addr) { + super(addr); + } + + public ProtectionDomainEntry next() { + return (ProtectionDomainEntry) VMObjectFactory.newObject(ProtectionDomainEntry.class, addr); + } + + public Oop protectionDomain() { + return VM.getVM().getObjectHeap().newOop(protectionDomainField.getValue(addr)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SharedHeap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SharedHeap.java new file mode 100644 index 00000000000..1d980467d88 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SharedHeap.java @@ -0,0 +1,76 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public abstract class SharedHeap extends CollectedHeap { + private static AddressField permGenField; + private static VirtualConstructor ctor; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("SharedHeap"); + permGenField = type.getAddressField("_perm_gen"); + ctor = new VirtualConstructor(db); + ctor.addMapping("CompactingPermGen", CompactingPermGen.class); + ctor.addMapping("CMSPermGen", CMSPermGen.class); + + } + + public SharedHeap(Address addr) { + super(addr); + } + + /** These functions return the "permanent" generation, in which + reflective objects are allocated and stored. Two versions, the + second of which returns the view of the perm gen as a + generation. (FIXME: this distinction is strange and seems + unnecessary, and should be cleaned up.) */ + public PermGen perm() { + return (PermGen) ctor.instantiateWrapperFor(permGenField.getValue(addr)); + } + + public CollectedHeapName kind() { + return CollectedHeapName.SHARED_HEAP; + } + + public Generation permGen() { + return perm().asGen(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Space.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Space.java new file mode 100644 index 00000000000..8bd13b67add --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Space.java @@ -0,0 +1,114 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +/**

A Space describes a heap area. Class Space is an abstract base + class.

+ +

Space supports allocation, size computation and GC support is + provided.

+ +

Invariant: bottom() and end() are on page_size boundaries and:

+ +

bottom() <= top() <= end()

+ +

top() is inclusive and end() is exclusive.

*/ + +public abstract class Space extends VMObject { + private static AddressField bottomField; + private static AddressField endField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("Space"); + + bottomField = type.getAddressField("_bottom"); + endField = type.getAddressField("_end"); + } + + public Space(Address addr) { + super(addr); + } + + public Address bottom() { return bottomField.getValue(addr); } + public Address end() { return endField.getValue(addr); } + + /** Returns a subregion of the space containing all the objects in + the space. */ + public MemRegion usedRegion() { + return new MemRegion(bottom(), end()); + } + + /** Support for iteration over heap -- not sure how this will + interact with GC in reflective system, but necessary for the + debugging mechanism */ + public OopHandle bottomAsOopHandle() { + return bottomField.getOopHandle(addr); + } + + /** Support for iteration over heap -- not sure how this will + interact with GC in reflective system, but necessary for the + debugging mechanism */ + public OopHandle nextOopHandle(OopHandle handle, long size) { + return handle.addOffsetToAsOopHandle(size); + } + + /** returns all MemRegions where live objects are */ + public abstract List/**/ getLiveRegions(); + + /** Returned value is in bytes */ + public long capacity() { return end().minus(bottom()); } + /** Returned value is in bytes */ + public abstract long used(); + /** Returned value is in bytes */ + public abstract long free(); + + /** Testers */ + public boolean contains(Address p) { + return (bottom().lessThanOrEqual(p) && end().greaterThan(p)); + } + + public void print() { printOn(System.out); } + public void printOn(PrintStream tty) { + tty.print(" space capacity = "); + tty.print(capacity()); + tty.print(", "); + tty.print((double) used() * 100.0/ capacity()); + tty.print(" used"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SpaceClosure.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SpaceClosure.java new file mode 100644 index 00000000000..ddeffb5e3b3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SpaceClosure.java @@ -0,0 +1,29 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +public interface SpaceClosure { + public void doSpace(Space s); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/StringTable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/StringTable.java new file mode 100644 index 00000000000..ad604e03629 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/StringTable.java @@ -0,0 +1,81 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class StringTable extends sun.jvm.hotspot.utilities.Hashtable { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("StringTable"); + theTableField = type.getAddressField("_the_table"); + stringTableSize = db.lookupIntConstant("StringTable::string_table_size").intValue(); + } + + // Fields + private static AddressField theTableField; + private static int stringTableSize; + + // Accessors + public static StringTable getTheTable() { + Address tmp = theTableField.getValue(); + return (StringTable) VMObjectFactory.newObject(StringTable.class, tmp); + } + + public static int getStringTableSize() { + return stringTableSize; + } + + public StringTable(Address addr) { + super(addr); + } + + public interface StringVisitor { + public void visit(Instance string); + } + + public void stringsDo(StringVisitor visitor) { + int numBuckets = tableSize(); + for (int i = 0; i < numBuckets; i++) { + for (HashtableEntry e = (HashtableEntry) bucket(i); e != null; + e = (HashtableEntry) e.next()) { + visitor.visit((Instance) e.literal()); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SymbolTable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SymbolTable.java new file mode 100644 index 00000000000..f8fea8f8613 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SymbolTable.java @@ -0,0 +1,122 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class SymbolTable extends sun.jvm.hotspot.utilities.Hashtable { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("SymbolTable"); + theTableField = type.getAddressField("_the_table"); + symbolTableSize = db.lookupIntConstant("SymbolTable::symbol_table_size").intValue(); + } + + // Fields + private static AddressField theTableField; + private static int symbolTableSize; + + // Accessors + public static SymbolTable getTheTable() { + Address tmp = theTableField.getValue(); + return (SymbolTable) VMObjectFactory.newObject(SymbolTable.class, tmp); + } + + public static int getSymbolTableSize() { + return symbolTableSize; + } + + public SymbolTable(Address addr) { + super(addr); + } + + /** Clone of VM's "temporary" probe routine, as the SA currently + does not support mutation so lookup() would have no effect + anyway. Returns null if the given string is not in the symbol + table. */ + public Symbol probe(String name) { + try { + return probe(toModifiedUTF8Bytes(name)); + } catch (IOException e) { + return null; + } + } + + /** Clone of VM's "temporary" probe routine, as the SA currently + does not support mutation so lookup() would have no effect + anyway. Returns null if the given string is not in the symbol + table. */ + public Symbol probe(byte[] name) { + long hashValue = hashSymbol(name); + for (HashtableEntry e = (HashtableEntry) bucket(hashToIndex(hashValue)); e != null; e = (HashtableEntry) e.next()) { + if (e.hash() == hashValue) { + Symbol sym = (Symbol) e.literal(); + if (sym.equals(name)) { + return sym; + } + } + } + return null; + } + + public interface SymbolVisitor { + public void visit(Symbol sym); + } + + public void symbolsDo(SymbolVisitor visitor) { + int numBuckets = tableSize(); + for (int i = 0; i < numBuckets; i++) { + for (HashtableEntry e = (HashtableEntry) bucket(i); e != null; + e = (HashtableEntry) e.next()) { + visitor.visit((Symbol) e.literal()); + } + } + } + + private static byte[] toModifiedUTF8Bytes(String name) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeUTF(name); + dos.flush(); + byte[] buf = baos.toByteArray(); + byte[] res = new byte[buf.length - 2]; + // skip the length part + System.arraycopy(buf, 2, res, 0, res.length); + return res; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SystemDictionary.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SystemDictionary.java new file mode 100644 index 00000000000..ed173c6a2bd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/SystemDictionary.java @@ -0,0 +1,192 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class SystemDictionary { + private static AddressField dictionaryField; + private static AddressField sharedDictionaryField; + private static AddressField placeholdersField; + private static AddressField loaderConstraintTableField; + private static sun.jvm.hotspot.types.OopField javaSystemLoaderField; + private static int nofBuckets; + + private static sun.jvm.hotspot.types.OopField objectKlassField; + private static sun.jvm.hotspot.types.OopField classLoaderKlassField; + private static sun.jvm.hotspot.types.OopField stringKlassField; + private static sun.jvm.hotspot.types.OopField systemKlassField; + private static sun.jvm.hotspot.types.OopField threadKlassField; + private static sun.jvm.hotspot.types.OopField threadGroupKlassField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("SystemDictionary"); + + dictionaryField = type.getAddressField("_dictionary"); + sharedDictionaryField = type.getAddressField("_shared_dictionary"); + placeholdersField = type.getAddressField("_placeholders"); + loaderConstraintTableField = type.getAddressField("_loader_constraints"); + javaSystemLoaderField = type.getOopField("_java_system_loader"); + nofBuckets = db.lookupIntConstant("SystemDictionary::_nof_buckets").intValue(); + + objectKlassField = type.getOopField("_object_klass"); + classLoaderKlassField = type.getOopField("_classloader_klass"); + stringKlassField = type.getOopField("_string_klass"); + systemKlassField = type.getOopField("_system_klass"); + threadKlassField = type.getOopField("_thread_klass"); + threadGroupKlassField = type.getOopField("_threadGroup_klass"); + } + + public Dictionary dictionary() { + Address tmp = dictionaryField.getValue(); + return (Dictionary) VMObjectFactory.newObject(Dictionary.class, tmp); + } + + public Dictionary sharedDictionary() { + Address tmp = sharedDictionaryField.getValue(); + return (Dictionary) VMObjectFactory.newObject(Dictionary.class, tmp); + } + + public PlaceholderTable placeholders() { + Address tmp = placeholdersField.getValue(); + return (PlaceholderTable) VMObjectFactory.newObject(PlaceholderTable.class, tmp); + } + + public LoaderConstraintTable constraints() { + Address tmp = placeholdersField.getValue(); + return (LoaderConstraintTable) VMObjectFactory.newObject(LoaderConstraintTable.class, tmp); + } + + // few well known classes -- not all are added here. + // add more if needed. + public static InstanceKlass getThreadKlass() { + return (InstanceKlass) newOop(threadKlassField.getValue()); + } + + public static InstanceKlass getThreadGroupKlass() { + return (InstanceKlass) newOop(threadGroupKlassField.getValue()); + } + + public static InstanceKlass getObjectKlass() { + return (InstanceKlass) newOop(objectKlassField.getValue()); + } + + public static InstanceKlass getStringKlass() { + return (InstanceKlass) newOop(stringKlassField.getValue()); + } + + public static InstanceKlass getClassLoaderKlass() { + return (InstanceKlass) newOop(classLoaderKlassField.getValue()); + } + + public static InstanceKlass getSystemKlass() { + return (InstanceKlass) newOop(systemKlassField.getValue()); + } + + public InstanceKlass getAbstractOwnableSynchronizerKlass() { + return (InstanceKlass) find("java/util/concurrent/locks/AbstractOwnableSynchronizer", + null, null); + } + + public static Oop javaSystemLoader() { + return newOop(javaSystemLoaderField.getValue()); + } + + public static int getNumOfBuckets() { + return nofBuckets; + } + + private static Oop newOop(OopHandle handle) { + return VM.getVM().getObjectHeap().newOop(handle); + } + + /** Lookup an already loaded class. If not found null is returned. */ + public Klass find(String className, Oop classLoader, Oop protectionDomain) { + Symbol sym = VM.getVM().getSymbolTable().probe(className); + if (sym == null) return null; + return find(sym, classLoader, protectionDomain); + } + + /** Lookup an already loaded class. If not found null is returned. */ + public Klass find(Symbol className, Oop classLoader, Oop protectionDomain) { + Dictionary dict = dictionary(); + long hash = dict.computeHash(className, classLoader); + int index = dict.hashToIndex(hash); + return dict.find(index, hash, className, classLoader, protectionDomain); + } + + /** Interface for iterating through all classes in dictionary */ + public static interface ClassVisitor { + public void visit(Klass k); + } + + /** Interface for iterating through all classes and their class + loaders in dictionary */ + public static interface ClassAndLoaderVisitor { + public void visit(Klass k, Oop loader); + } + + /** Iterate over all klasses - including object, primitive + array klasses */ + public void allClassesDo(final ClassVisitor v) { + ClassVisitor visitor = new ClassVisitor() { + public void visit(Klass k) { + for (Klass l = k; l != null; l = l.arrayKlassOrNull()) { + v.visit(l); + } + } + }; + classesDo(visitor); + VM.getVM().getUniverse().basicTypeClassesDo(visitor); + } + + /** Iterate over all klasses in dictionary; just the classes from + declaring class loaders */ + public void classesDo(ClassVisitor v) { + dictionary().classesDo(v); + } + + /** All classes, and their class loaders */ + public void classesDo(ClassAndLoaderVisitor v) { + dictionary().classesDo(v); + } + + /** All array classes of primitive type, and their class loaders */ + public void primArrayClassesDo(ClassAndLoaderVisitor v) { + placeholders().primArrayClassesDo(v); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/TenuredGeneration.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/TenuredGeneration.java new file mode 100644 index 00000000000..7b6b1a3d053 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/TenuredGeneration.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import sun.jvm.hotspot.debugger.*; + +public class TenuredGeneration extends OneContigSpaceCardGeneration { + public TenuredGeneration(Address addr) { + super(addr); + } + + public Generation.Name kind() { + return Generation.Name.MARK_SWEEP_COMPACT; + } + + public String name() { + return "tenured generation"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/TenuredSpace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/TenuredSpace.java new file mode 100644 index 00000000000..cbe2f6469c6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/TenuredSpace.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import sun.jvm.hotspot.debugger.*; + +/** No additional functionality for now */ + +public class TenuredSpace extends OffsetTableContigSpace { + public TenuredSpace(Address addr) { + super(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Universe.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Universe.java new file mode 100644 index 00000000000..67fdf0c4915 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/memory/Universe.java @@ -0,0 +1,155 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.memory; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.gc_implementation.parallelScavenge.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + + +public class Universe { + private static AddressField collectedHeapField; + private static VirtualConstructor heapConstructor; + private static sun.jvm.hotspot.types.OopField mainThreadGroupField; + private static sun.jvm.hotspot.types.OopField systemThreadGroupField; + + // single dimensional primitive array klasses + private static sun.jvm.hotspot.types.OopField boolArrayKlassObjField; + private static sun.jvm.hotspot.types.OopField byteArrayKlassObjField; + private static sun.jvm.hotspot.types.OopField charArrayKlassObjField; + private static sun.jvm.hotspot.types.OopField intArrayKlassObjField; + private static sun.jvm.hotspot.types.OopField shortArrayKlassObjField; + private static sun.jvm.hotspot.types.OopField longArrayKlassObjField; + private static sun.jvm.hotspot.types.OopField singleArrayKlassObjField; + private static sun.jvm.hotspot.types.OopField doubleArrayKlassObjField; + + // system obj array klass object + private static sun.jvm.hotspot.types.OopField systemObjArrayKlassObjField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("Universe"); + + collectedHeapField = type.getAddressField("_collectedHeap"); + + heapConstructor = new VirtualConstructor(db); + heapConstructor.addMapping("GenCollectedHeap", GenCollectedHeap.class); + heapConstructor.addMapping("ParallelScavengeHeap", ParallelScavengeHeap.class); + + mainThreadGroupField = type.getOopField("_main_thread_group"); + systemThreadGroupField = type.getOopField("_system_thread_group"); + + boolArrayKlassObjField = type.getOopField("_boolArrayKlassObj"); + byteArrayKlassObjField = type.getOopField("_byteArrayKlassObj"); + charArrayKlassObjField = type.getOopField("_charArrayKlassObj"); + intArrayKlassObjField = type.getOopField("_intArrayKlassObj"); + shortArrayKlassObjField = type.getOopField("_shortArrayKlassObj"); + longArrayKlassObjField = type.getOopField("_longArrayKlassObj"); + singleArrayKlassObjField = type.getOopField("_singleArrayKlassObj"); + doubleArrayKlassObjField = type.getOopField("_doubleArrayKlassObj"); + + systemObjArrayKlassObjField = type.getOopField("_systemObjArrayKlassObj"); + } + + public Universe() { + } + + public CollectedHeap heap() { + try { + return (CollectedHeap) heapConstructor.instantiateWrapperFor(collectedHeapField.getValue()); + } catch (WrongTypeException e) { + return new CollectedHeap(collectedHeapField.getValue()); + } + } + + /** Returns "TRUE" iff "p" points into the allocated area of the heap. */ + public boolean isIn(Address p) { + return heap().isIn(p); + } + + /** Returns "TRUE" iff "p" points into the reserved area of the heap. */ + public boolean isInReserved(Address p) { + return heap().isInReserved(p); + } + + private Oop newOop(OopHandle handle) { + return VM.getVM().getObjectHeap().newOop(handle); + } + + public Oop mainThreadGroup() { + return newOop(mainThreadGroupField.getValue()); + } + + public Oop systemThreadGroup() { + return newOop(systemThreadGroupField.getValue()); + } + + public Oop systemObjArrayKlassObj() { + return newOop(systemObjArrayKlassObjField.getValue()); + } + + // iterate through the single dimensional primitive array klasses + // refer to basic_type_classes_do(void f(klassOop)) in universe.cpp + public void basicTypeClassesDo(SystemDictionary.ClassVisitor visitor) { + visitor.visit((Klass)newOop(boolArrayKlassObjField.getValue())); + visitor.visit((Klass)newOop(byteArrayKlassObjField.getValue())); + visitor.visit((Klass)newOop(charArrayKlassObjField.getValue())); + visitor.visit((Klass)newOop(intArrayKlassObjField.getValue())); + visitor.visit((Klass)newOop(shortArrayKlassObjField.getValue())); + visitor.visit((Klass)newOop(longArrayKlassObjField.getValue())); + visitor.visit((Klass)newOop(singleArrayKlassObjField.getValue())); + visitor.visit((Klass)newOop(doubleArrayKlassObjField.getValue())); + } + + public void print() { printOn(System.out); } + public void printOn(PrintStream tty) { + heap().printOn(tty); + } + + // Check whether an element of a typeArrayOop with the given type must be + // aligned 0 mod 8. The typeArrayOop itself must be aligned at least this + // strongly. + public static boolean elementTypeShouldBeAligned(BasicType type) { + return type == BasicType.T_DOUBLE || type == BasicType.T_LONG; + } + + // Check whether an object field (static/non-static) of the given type must be + // aligned 0 mod 8. + public static boolean fieldTypeShouldBeAligned(BasicType type) { + return type == BasicType.T_DOUBLE || type == BasicType.T_LONG; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/AccessFlags.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/AccessFlags.java new file mode 100644 index 00000000000..12cde846147 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/AccessFlags.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.runtime.ClassConstants; +import java.io.*; + +public class AccessFlags implements /* imports */ ClassConstants { + public AccessFlags(long flags) { + this.flags = flags; + } + + private long flags; + + // Java access flags + public boolean isPublic () { return (flags & JVM_ACC_PUBLIC ) != 0; } + public boolean isPrivate () { return (flags & JVM_ACC_PRIVATE ) != 0; } + public boolean isProtected () { return (flags & JVM_ACC_PROTECTED ) != 0; } + public boolean isStatic () { return (flags & JVM_ACC_STATIC ) != 0; } + public boolean isFinal () { return (flags & JVM_ACC_FINAL ) != 0; } + public boolean isSynchronized() { return (flags & JVM_ACC_SYNCHRONIZED) != 0; } + public boolean isSuper () { return (flags & JVM_ACC_SUPER ) != 0; } + public boolean isVolatile () { return (flags & JVM_ACC_VOLATILE ) != 0; } + public boolean isBridge () { return (flags & JVM_ACC_BRIDGE ) != 0; } + public boolean isTransient () { return (flags & JVM_ACC_TRANSIENT ) != 0; } + public boolean isVarArgs () { return (flags & JVM_ACC_VARARGS ) != 0; } + public boolean isNative () { return (flags & JVM_ACC_NATIVE ) != 0; } + public boolean isEnum () { return (flags & JVM_ACC_ENUM ) != 0; } + public boolean isAnnotation () { return (flags & JVM_ACC_ANNOTATION ) != 0; } + public boolean isInterface () { return (flags & JVM_ACC_INTERFACE ) != 0; } + public boolean isAbstract () { return (flags & JVM_ACC_ABSTRACT ) != 0; } + public boolean isStrict () { return (flags & JVM_ACC_STRICT ) != 0; } + public boolean isSynthetic () { return (flags & JVM_ACC_SYNTHETIC ) != 0; } + + public long getValue () { return flags; } + + // Hotspot internal flags + // methodOop flags + public boolean isMonitorMatching () { return (flags & JVM_ACC_MONITOR_MATCH ) != 0; } + public boolean hasMonitorBytecodes () { return (flags & JVM_ACC_HAS_MONITOR_BYTECODES ) != 0; } + public boolean hasLoops () { return (flags & JVM_ACC_HAS_LOOPS ) != 0; } + public boolean loopsFlagInit () { return (flags & JVM_ACC_LOOPS_FLAG_INIT ) != 0; } + public boolean queuedForCompilation() { return (flags & JVM_ACC_QUEUED ) != 0; } + public boolean isNotOsrCompilable () { return (flags & JVM_ACC_NOT_OSR_COMPILABLE ) != 0; } + public boolean hasLineNumberTable () { return (flags & JVM_ACC_HAS_LINE_NUMBER_TABLE ) != 0; } + public boolean hasCheckedExceptions() { return (flags & JVM_ACC_HAS_CHECKED_EXCEPTIONS ) != 0; } + public boolean hasJsrs () { return (flags & JVM_ACC_HAS_JSRS ) != 0; } + public boolean isObsolete () { return (flags & JVM_ACC_IS_OBSOLETE ) != 0; } + + // klassOop flags + public boolean hasMirandaMethods () { return (flags & JVM_ACC_HAS_MIRANDA_METHODS ) != 0; } + public boolean hasVanillaConstructor() { return (flags & JVM_ACC_HAS_VANILLA_CONSTRUCTOR) != 0; } + public boolean hasFinalizer () { return (flags & JVM_ACC_HAS_FINALIZER ) != 0; } + public boolean isCloneable () { return (flags & JVM_ACC_IS_CLONEABLE ) != 0; } + + // klassOop and methodOop flags + public boolean hasLocalVariableTable() { return (flags & JVM_ACC_HAS_LOCAL_VARIABLE_TABLE ) != 0; } + + // field flags + public boolean fieldAccessWatched () { return (flags & JVM_ACC_FIELD_ACCESS_WATCHED) != 0; } + public boolean fieldModificationWatched() { return (flags & JVM_ACC_FIELD_MODIFICATION_WATCHED) != 0; } + + public void printOn(PrintStream tty) { + // prints only .class flags and not the hotspot internal flags + if (isPublic ()) tty.print("public " ); + if (isPrivate ()) tty.print("private " ); + if (isProtected ()) tty.print("protected " ); + if (isStatic ()) tty.print("static " ); + if (isFinal ()) tty.print("final " ); + if (isSynchronized()) tty.print("synchronized "); + if (isVolatile ()) tty.print("volatile " ); + if (isBridge ()) tty.print("bridge " ); + if (isTransient ()) tty.print("transient " ); + if (isVarArgs ()) tty.print("varargs " ); + if (isNative ()) tty.print("native " ); + if (isEnum ()) tty.print("enum " ); + if (isInterface ()) tty.print("interface " ); + if (isAbstract ()) tty.print("abstract " ); + if (isStrict ()) tty.print("strict " ); + if (isSynthetic ()) tty.print("synthetic " ); + } + + // get flags written to .class files + public int getStandardFlags() { + return (int) (flags & JVM_ACC_WRITTEN_FLAGS); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Array.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Array.java new file mode 100644 index 00000000000..1f78aece062 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Array.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// Array is an abstract superclass for TypeArray and ObjArray + +public class Array extends Oop { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + Array(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("arrayOopDesc"); + length = new CIntField(type.getCIntegerField("_length"), 0); + headerSize = type.getSize(); + } + + // Size of the arrayOopDesc + private static long headerSize; + + // Fields + private static CIntField length; + + // Accessors for declared fields + public long getLength() { return length.getValue(this); } + + public long getObjectSize() { + ArrayKlass klass = (ArrayKlass) getKlass(); + // We have to fetch the length of the array, shift (multiply) it + // appropriately, up to wordSize, add the header, and align to + // object size. + long s = getLength() << klass.getLog2ElementSize(); + s += klass.getArrayHeaderInBytes(); + s = Oop.alignObjectSize(s); + return s; + } + + public static long baseOffsetInBytes(BasicType type) { + if (Universe.elementTypeShouldBeAligned(type)) { + return (VM.getVM().isLP64()) ? alignObjectSize(headerSize) + : VM.getVM().alignUp(headerSize, 8); + } else { + return headerSize; + } + } + + public boolean isArray() { return true; } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doCInt(length, true); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java new file mode 100644 index 00000000000..02f2297b5f8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java @@ -0,0 +1,159 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// ArrayKlass is the abstract class for all array classes + +public class ArrayKlass extends Klass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("arrayKlass"); + dimension = new CIntField(type.getCIntegerField("_dimension"), Oop.getHeaderSize()); + higherDimension = new OopField(type.getOopField("_higher_dimension"), Oop.getHeaderSize()); + lowerDimension = new OopField(type.getOopField("_lower_dimension"), Oop.getHeaderSize()); + vtableLen = new CIntField(type.getCIntegerField("_vtable_len"), Oop.getHeaderSize()); + allocSize = new CIntField(type.getCIntegerField("_alloc_size"), Oop.getHeaderSize()); + componentMirror = new OopField(type.getOopField("_component_mirror"), Oop.getHeaderSize()); + javaLangCloneableName = null; + javaLangObjectName = null; + javaIoSerializableName = null; + } + + ArrayKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static CIntField dimension; + private static OopField higherDimension; + private static OopField lowerDimension; + private static CIntField vtableLen; + private static CIntField allocSize; + private static OopField componentMirror; + + public Klass getJavaSuper() { + SystemDictionary sysDict = VM.getVM().getSystemDictionary(); + return sysDict.getObjectKlass(); + } + + public long getDimension() { return dimension.getValue(this); } + public Klass getHigherDimension() { return (Klass) higherDimension.getValue(this); } + public Klass getLowerDimension() { return (Klass) lowerDimension.getValue(this); } + public long getVtableLen() { return vtableLen.getValue(this); } + public long getAllocSize() { return allocSize.getValue(this); } + public Oop getComponentMirror() { return componentMirror.getValue(this); } + + // constant class names - javaLangCloneable, javaIoSerializable, javaLangObject + // Initialized lazily to avoid initialization ordering dependencies between ArrayKlass and SymbolTable + private static Symbol javaLangCloneableName; + private static Symbol javaLangObjectName; + private static Symbol javaIoSerializableName; + private static Symbol javaLangCloneableName() { + if (javaLangCloneableName == null) { + javaLangCloneableName = VM.getVM().getSymbolTable().probe("java/lang/Cloneable"); + } + return javaLangCloneableName; + } + + private static Symbol javaLangObjectName() { + if (javaLangObjectName == null) { + javaLangObjectName = VM.getVM().getSymbolTable().probe("java/lang/Object"); + } + return javaLangObjectName; + } + + private static Symbol javaIoSerializableName() { + if (javaIoSerializableName == null) { + javaIoSerializableName = VM.getVM().getSymbolTable().probe("java/io/Serializable"); + } + return javaIoSerializableName; + } + + public int getClassStatus() { + return JVMDIClassStatus.VERIFIED | JVMDIClassStatus.PREPARED | JVMDIClassStatus.INITIALIZED; + } + + public long computeModifierFlags() { + return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC; + } + + public long getArrayHeaderInBytes() { + return Bits.maskBits(getLayoutHelper() >> LH_HEADER_SIZE_SHIFT, 0xFF); + } + + public int getLog2ElementSize() { + return Bits.maskBits(getLayoutHelper() >> LH_LOG2_ELEMENT_SIZE_SHIFT, 0xFF); + } + + public int getElementType() { + return Bits.maskBits(getLayoutHelper() >> LH_ELEMENT_TYPE_SHIFT, 0xFF); + } + + boolean computeSubtypeOf(Klass k) { + // An array is a subtype of Serializable, Clonable, and Object + Symbol name = k.getName(); + if (name != null && (name.equals(javaIoSerializableName()) || + name.equals(javaLangCloneableName()) || + name.equals(javaLangObjectName()))) { + return true; + } else { + return false; + } + } + + public void printValueOn(PrintStream tty) { + tty.print("ArrayKlass"); + } + + public long getObjectSize() { + return alignObjectSize(InstanceKlass.getHeaderSize() + getVtableLen() * getHeap().getOopSize()); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doCInt(dimension, true); + visitor.doOop(higherDimension, true); + visitor.doOop(lowerDimension, true); + visitor.doCInt(vtableLen, true); + visitor.doCInt(allocSize, true); + visitor.doOop(componentMirror, true); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlassKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlassKlass.java new file mode 100644 index 00000000000..59b7390d336 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlassKlass.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class ArrayKlassKlass extends KlassKlass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("arrayKlassKlass"); + headerSize = type.getSize() + Oop.getHeaderSize(); + } + + ArrayKlassKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static long headerSize; + + public long getObjectSize() { return alignObjectSize(headerSize); } + + public void printValueOn(PrintStream tty) { + tty.print("ArrayKlassKlass"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/BooleanField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/BooleanField.java new file mode 100644 index 00000000000..074c1ee999a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/BooleanField.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.*; + +// The class for a boolean field simply provides access to the value. +public class BooleanField extends Field { + public BooleanField(FieldIdentifier id, long offset, boolean isVMField) { + super(id, offset, isVMField); + } + + public BooleanField(sun.jvm.hotspot.types.JBooleanField vmField, long startOffset) { + super(new NamedFieldIdentifier(vmField.getName()), vmField.getOffset() + startOffset, true); + } + + public BooleanField(InstanceKlass holder, int fieldArrayIndex) { + super(holder, fieldArrayIndex); + } + + public boolean getValue(Oop obj) { return obj.getHandle().getJBooleanAt(getOffset()); } + public void setValue(Oop obj, boolean value) throws MutationException { + // Fix this: setJBooleanAt is missing in Address + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/BreakpointInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/BreakpointInfo.java new file mode 100644 index 00000000000..1443314f4e6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/BreakpointInfo.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class BreakpointInfo extends VMObject { + private static CIntegerField origBytecodeField; + private static CIntegerField bciField; + private static CIntegerField nameIndexField; + private static CIntegerField signatureIndexField; + private static AddressField nextField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("BreakpointInfo"); + + origBytecodeField = type.getCIntegerField("_orig_bytecode"); + bciField = type.getCIntegerField("_bci"); + nameIndexField = type.getCIntegerField("_name_index"); + signatureIndexField = type.getCIntegerField("_signature_index"); + nextField = type.getAddressField ("_next"); + } + + public BreakpointInfo(Address addr) { + super(addr); + } + + public int getOrigBytecode() { return (int) origBytecodeField.getValue(addr); } + public int getBCI() { return (int) bciField.getValue(addr); } + public long getNameIndex() { return nameIndexField.getValue(addr); } + public long getSignatureIndex() { return signatureIndexField.getValue(addr); } + public BreakpointInfo getNext() { + return (BreakpointInfo) VMObjectFactory.newObject(BreakpointInfo.class, nextField.getValue(addr)); + } + + public boolean match(Method m, int bci) { + return (bci == getBCI() && match(m)); + } + + public boolean match(Method m) { + return (getNameIndex() == m.getNameIndex() && + getSignatureIndex() == m.getSignatureIndex()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ByteField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ByteField.java new file mode 100644 index 00000000000..7e52094a33a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ByteField.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.*; + +// The class for a byte field simply provides access to the value. +public class ByteField extends Field { + public ByteField(FieldIdentifier id, long offset, boolean isVMField) { + super(id, offset, isVMField); + } + + public ByteField(sun.jvm.hotspot.types.JByteField vmField, long startOffset) { + super(new NamedFieldIdentifier(vmField.getName()), vmField.getOffset() + startOffset, true); + } + + public ByteField(InstanceKlass holder, int fieldArrayIndex) { + super(holder, fieldArrayIndex); + } + + public byte getValue(Oop obj) { return obj.getHandle().getJByteAt(getOffset()); } + public void setValue(Oop obj, char value) throws MutationException { + // Fix this: setJCharAt is missing in Address + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CIntField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CIntField.java new file mode 100644 index 00000000000..088203846b1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CIntField.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.*; + +// The class for an C int field simply provides access to the value. +public class CIntField extends Field { + + public CIntField(sun.jvm.hotspot.types.CIntegerField vmField, long startOffset) { + super(new NamedFieldIdentifier(vmField.getName()), vmField.getOffset() + startOffset, true); + size = vmField.getSize(); + isUnsigned = ((sun.jvm.hotspot.types.CIntegerType) vmField.getType()).isUnsigned(); + } + + private long size; + private boolean isUnsigned; + + public long getValue(Oop obj) { + return obj.getHandle().getCIntegerAt(getOffset(), size, isUnsigned); + } + public void setValue(Oop obj, long value) throws MutationException { + // Fix this: set* missing in Address + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CellTypeState.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CellTypeState.java new file mode 100644 index 00000000000..315b1783d5f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CellTypeState.java @@ -0,0 +1,299 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import sun.jvm.hotspot.utilities.*; + +/** Auxiliary class for GenerateOopMap */ +public class CellTypeState { + private int _state; + + // Masks for separating the BITS and INFO portions of a + // CellTypeState + private static final int info_mask = Bits.rightNBits(28); + private static final int bits_mask = ~info_mask; + + // These constant are used for manipulating the BITS portion of a + // CellTypeState + private static final int uninit_bit = Bits.nthBit(31); + private static final int ref_bit = Bits.nthBit(30); + private static final int val_bit = Bits.nthBit(29); + private static final int addr_bit = Bits.nthBit(28); + private static final int live_bits_mask = bits_mask & ~uninit_bit; + + // These constants are used for manipulating the INFO portion of a + // CellTypeState + private static final int top_info_bit = Bits.nthBit(27); + private static final int not_bottom_info_bit = Bits.nthBit(26); + private static final int info_data_mask = Bits.rightNBits(26); + private static final int info_conflict = info_mask; + + // Within the INFO data, these values are used to distinguish + // different kinds of references. + // 0 if this reference is locked as a monitor + private static final int ref_not_lock_bit = Bits.nthBit(25); + // 1 if this reference is a "slot" reference + private static final int ref_slot_bit = Bits.nthBit(24); + // 0 if it is a "line" reference. + private static final int ref_data_mask = Bits.rightNBits(24); + + // These values are used to initialize commonly used CellTypeState + // constants. + private static final int bottom_value = 0; + private static final int uninit_value = uninit_bit | info_conflict; + private static final int ref_value = ref_bit; + private static final int ref_conflict = ref_bit | info_conflict; + private static final int val_value = val_bit | info_conflict; + private static final int addr_value = addr_bit; + private static final int addr_conflict = addr_bit | info_conflict; + + private CellTypeState() {} + + private CellTypeState(int state) { + _state = state; + } + + public CellTypeState copy() { + return new CellTypeState(_state); + } + + public static CellTypeState makeAny(int state) { + CellTypeState s = new CellTypeState(state); + if (Assert.ASSERTS_ENABLED) { + Assert.that(s.isValidState(), "check to see if CellTypeState is valid"); + } + return s; + } + + public static CellTypeState makeBottom() { + return makeAny(0); + } + + public static CellTypeState makeTop() { + return makeAny(Bits.AllBits); + } + + public static CellTypeState makeAddr(int bci) { + if (Assert.ASSERTS_ENABLED) { + Assert.that((bci >= 0) && (bci < info_data_mask), + "check to see if ret addr is valid"); + } + return makeAny(addr_bit | not_bottom_info_bit | (bci & info_data_mask)); + } + + public static CellTypeState makeSlotRef(int slot_num) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(slot_num >= 0 && slot_num < ref_data_mask, "slot out of range"); + } + return makeAny(ref_bit | not_bottom_info_bit | ref_not_lock_bit | ref_slot_bit | + (slot_num & ref_data_mask)); + } + + public static CellTypeState makeLineRef(int bci) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(bci >= 0 && bci < ref_data_mask, "line out of range"); + } + return makeAny(ref_bit | not_bottom_info_bit | ref_not_lock_bit | + (bci & ref_data_mask)); + } + + public static CellTypeState makeLockRef(int bci) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(bci >= 0 && bci < ref_data_mask, "line out of range"); + } + return makeAny(ref_bit | not_bottom_info_bit | (bci & ref_data_mask)); + } + + // Query methods: + public boolean isBottom() { return _state == 0; } + public boolean isLive() { return ((_state & live_bits_mask) != 0); } + public boolean isValidState() { + // Uninitialized and value cells must contain no data in their info field: + if ((canBeUninit() || canBeValue()) && !isInfoTop()) { + return false; + } + // The top bit is only set when all info bits are set: + if (isInfoTop() && ((_state & info_mask) != info_mask)) { + return false; + } + // The not_bottom_bit must be set when any other info bit is set: + if (isInfoBottom() && ((_state & info_mask) != 0)) { + return false; + } + return true; + } + + public boolean isAddress() { return ((_state & bits_mask) == addr_bit); } + public boolean isReference() { return ((_state & bits_mask) == ref_bit); } + public boolean isValue() { return ((_state & bits_mask) == val_bit); } + public boolean isUninit() { return ((_state & bits_mask) == uninit_bit); } + + public boolean canBeAddress() { return ((_state & addr_bit) != 0); } + public boolean canBeReference() { return ((_state & ref_bit) != 0); } + public boolean canBeValue() { return ((_state & val_bit) != 0); } + public boolean canBeUninit() { return ((_state & uninit_bit) != 0); } + + public boolean isInfoBottom() { return ((_state & not_bottom_info_bit) == 0); } + public boolean isInfoTop() { return ((_state & top_info_bit) != 0); } + public int getInfo() { + if (Assert.ASSERTS_ENABLED) { + Assert.that((!isInfoTop() && !isInfoBottom()), + "check to make sure top/bottom info is not used"); + } + return (_state & info_data_mask); + } + + public int getMonitorSource() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isLockReference(), "must be lock"); + } + return getInfo(); + } + + public boolean isGoodAddress() { return isAddress() && !isInfoTop(); } + public boolean isLockReference() { + return ((_state & (bits_mask | top_info_bit | ref_not_lock_bit)) == ref_bit); + } + public boolean isNonlockReference() { + return ((_state & (bits_mask | top_info_bit | ref_not_lock_bit)) == (ref_bit | ref_not_lock_bit)); + } + + public boolean equal(CellTypeState a) { return _state == a._state; } + public boolean equalKind(CellTypeState a) { + return (_state & bits_mask) == (a._state & bits_mask); + } + + public char toChar() { + if (canBeReference()) { + if (canBeValue() || canBeAddress()) + return '#'; // Conflict that needs to be rewritten + else + return 'r'; + } else if (canBeValue()) + return 'v'; + else if (canBeAddress()) + return 'p'; + else if (canBeUninit()) + return ' '; + else + return '@'; + } + + // Set + public void set(CellTypeState cts) { + _state = cts._state; + } + + // Merge + public CellTypeState merge (CellTypeState cts, int slot) { + CellTypeState result = new CellTypeState(); + + if (Assert.ASSERTS_ENABLED) { + Assert.that(!isBottom() && !cts.isBottom(), + "merge of bottom values is handled elsewhere"); + } + + result._state = _state | cts._state; + + // If the top bit is set, we don't need to do any more work. + if (!result.isInfoTop()) { + Assert.that((result.canBeAddress() || result.canBeReference()), + "only addresses and references have non-top info"); + + if (!equal(cts)) { + // The two values being merged are different. Raise to top. + if (result.isReference()) { + result = CellTypeState.makeSlotRef(slot); + } else { + result._state |= info_conflict; + } + } + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(result.isValidState(), "checking that CTS merge maintains legal state"); + } + + return result; + } + + // Debugging output + public void print(PrintStream tty) { + if (canBeAddress()) { + tty.print("(p"); + } else { + tty.print("( "); + } + if (canBeReference()) { + tty.print("r"); + } else { + tty.print(" "); + } + if (canBeValue()) { + tty.print("v"); + } else { + tty.print(" "); + } + if (canBeUninit()) { + tty.print("u|"); + } else { + tty.print(" |"); + } + if (isInfoTop()) { + tty.print("Top)"); + } else if (isInfoBottom()) { + tty.print("Bot)"); + } else { + if (isReference()) { + int info = getInfo(); + int data = info & ~(ref_not_lock_bit | ref_slot_bit); + if ((info & ref_not_lock_bit) != 0) { + // Not a monitor lock reference. + if ((info & ref_slot_bit) != 0) { + // slot + tty.print("slot" + data + ")"); + } else { + // line + tty.print("line" + data + ")"); + } + } else { + // lock + tty.print("lock" + data + ")"); + } + } else { + tty.print("" + getInfo() + ")"); + } + } + } + + // Default values of common values + public static CellTypeState bottom = CellTypeState.makeBottom(); + public static CellTypeState uninit = CellTypeState.makeAny(uninit_value); + public static CellTypeState ref = CellTypeState.makeAny(ref_conflict); + public static CellTypeState value = CellTypeState.makeAny(val_value); + public static CellTypeState refUninit = CellTypeState.makeAny(ref_conflict | uninit_value); + public static CellTypeState top = CellTypeState.makeTop(); + public static CellTypeState addr = CellTypeState.makeAny(addr_conflict); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CellTypeStateList.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CellTypeStateList.java new file mode 100644 index 00000000000..f3cf4629181 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CellTypeStateList.java @@ -0,0 +1,55 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.util.*; + +/** Auxiliary class for GenerateOopMap */ +public class CellTypeStateList { + public CellTypeStateList(int size) { + list = new ArrayList(size); + for (int i = 0; i < size; i++) { + list.add(i, CellTypeState.makeBottom()); + } + } + + public int size() { + return list.size(); + } + + public CellTypeState get(int i) { + return (CellTypeState) list.get(i); + } + + public CellTypeStateList subList(int fromIndex, int toIndex) { + return new CellTypeStateList(list.subList(fromIndex, toIndex)); + } + + //---------------------------------------------------------------------- + private List list; + private CellTypeStateList(List list) { + this.list = list; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CharField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CharField.java new file mode 100644 index 00000000000..caf340608f4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CharField.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.*; + +// The class for a char field simply provides access to the value. +public class CharField extends Field { + public CharField(FieldIdentifier id, long offset, boolean isVMField) { + super(id, offset, isVMField); + } + + public CharField(sun.jvm.hotspot.types.JCharField vmField, long startOffset) { + super(new NamedFieldIdentifier(vmField.getName()), vmField.getOffset() + startOffset, true); + } + + public CharField(InstanceKlass holder, int fieldArrayIndex) { + super(holder, fieldArrayIndex); + } + + public char getValue(Oop obj) { return obj.getHandle().getJCharAt(getOffset()); } + public void setValue(Oop obj, char value) throws MutationException { + // Fix this: setJCharAt is missing in Address + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CheckedExceptionElement.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CheckedExceptionElement.java new file mode 100644 index 00000000000..eab075fbec4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CheckedExceptionElement.java @@ -0,0 +1,62 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class CheckedExceptionElement { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("CheckedExceptionElement"); + offsetOfClassCPIndex = type.getCIntegerField("class_cp_index").getOffset(); + } + + private static long offsetOfClassCPIndex; + + private OopHandle handle; + private long offset; + + public CheckedExceptionElement(OopHandle handle, long offset) { + this.handle = handle; + this.offset = offset; + } + + public int getClassCPIndex() { + return (int) handle.getCIntegerAt(offset + offsetOfClassCPIndex, 2, true); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CompiledICHolder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CompiledICHolder.java new file mode 100644 index 00000000000..e99279a2f03 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CompiledICHolder.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class CompiledICHolder extends Oop { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("compiledICHolderOopDesc"); + holderMethod = new OopField(type.getOopField("_holder_method"), 0); + holderKlass = new OopField(type.getOopField("_holder_klass"), 0); + headerSize = type.getSize(); + } + + CompiledICHolder(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public boolean isCompiledICHolder() { return true; } + + private static long headerSize; + + // Fields + private static OopField holderMethod; + private static OopField holderKlass; + + // Accessors for declared fields + public Method getHolderMethod() { return (Method) holderMethod.getValue(this); } + public Klass getHolderKlass() { return (Klass) holderKlass.getValue(this); } + + public void printValueOn(PrintStream tty) { + tty.print("CompiledICHolder"); + } + + public long getObjectSize() { + return alignObjectSize(headerSize); + } + + void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doOop(holderMethod, true); + visitor.doOop(holderKlass, true); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CompiledICHolderKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CompiledICHolderKlass.java new file mode 100644 index 00000000000..b0e7b41d7de --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CompiledICHolderKlass.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class CompiledICHolderKlass extends Klass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("compiledICHolderKlass"); + headerSize = type.getSize() + Oop.getHeaderSize(); + } + + CompiledICHolderKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static long headerSize; + + public long getObjectSize() { return alignObjectSize(headerSize); } + + public void printValueOn(PrintStream tty) { + tty.print("CompilerICHolderKlass"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CompressedLineNumberReadStream.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CompressedLineNumberReadStream.java new file mode 100644 index 00000000000..0379b577410 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/CompressedLineNumberReadStream.java @@ -0,0 +1,66 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; + +public class CompressedLineNumberReadStream extends CompressedReadStream { + /** Equivalent to CompressedLineNumberReadStream(buffer, 0) */ + public CompressedLineNumberReadStream(Address buffer) { + this(buffer, 0); + } + + public CompressedLineNumberReadStream(Address buffer, int position) { + super(buffer, position); + } + + /** Read (bci, line number) pair from stream. Returns false at end-of-stream. */ + public boolean readPair() { + int next = readByte() & 0xFF; + // Check for terminator + if (next == 0) return false; + if (next == 0xFF) { + // Escape character, regular compression used + bci += readSignedInt(); + line += readSignedInt(); + } else { + // Single byte compression used + bci += next >> 3; + line += next & 0x7; + } + return true; + } + + public int bci() { return bci; } + public int line() { return line; } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private int bci; + private int line; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstMethod.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstMethod.java new file mode 100644 index 00000000000..926481cf618 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstMethod.java @@ -0,0 +1,397 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class ConstMethod extends Oop { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + // anon-enum constants for _flags. + private static int HAS_LINENUMBER_TABLE; + private static int HAS_CHECKED_EXCEPTIONS; + private static int HAS_LOCALVARIABLE_TABLE; + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("constMethodOopDesc"); + // Backpointer to non-const methodOop + method = new OopField(type.getOopField("_method"), 0); + // The exception handler table. 4-tuples of ints [start_pc, end_pc, + // handler_pc, catch_type index] For methods with no exceptions the + // table is pointing to Universe::the_empty_int_array + exceptionTable = new OopField(type.getOopField("_exception_table"), 0); + constMethodSize = new CIntField(type.getCIntegerField("_constMethod_size"), 0); + flags = new ByteField(type.getJByteField("_flags"), 0); + + // enum constants for flags + HAS_LINENUMBER_TABLE = db.lookupIntConstant("constMethodOopDesc::_has_linenumber_table").intValue(); + HAS_CHECKED_EXCEPTIONS = db.lookupIntConstant("constMethodOopDesc::_has_checked_exceptions").intValue(); + HAS_LOCALVARIABLE_TABLE = db.lookupIntConstant("constMethodOopDesc::_has_localvariable_table").intValue(); + + // Size of Java bytecodes allocated immediately after constMethodOop. + codeSize = new CIntField(type.getCIntegerField("_code_size"), 0); + nameIndex = new CIntField(type.getCIntegerField("_name_index"), 0); + signatureIndex = new CIntField(type.getCIntegerField("_signature_index"), 0); + genericSignatureIndex = new CIntField(type.getCIntegerField("_generic_signature_index"),0); + + // start of byte code + bytecodeOffset = type.getSize(); + + type = db.lookupType("CheckedExceptionElement"); + checkedExceptionElementSize = type.getSize(); + + type = db.lookupType("LocalVariableTableElement"); + localVariableTableElementSize = type.getSize(); + } + + ConstMethod(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + // Fields + private static OopField method; + private static OopField exceptionTable; + private static CIntField constMethodSize; + private static ByteField flags; + private static CIntField codeSize; + private static CIntField nameIndex; + private static CIntField signatureIndex; + private static CIntField genericSignatureIndex; + + // start of bytecode + private static long bytecodeOffset; + + private static long checkedExceptionElementSize; + private static long localVariableTableElementSize; + + // Accessors for declared fields + public Method getMethod() { + return (Method) method.getValue(this); + } + + public TypeArray getExceptionTable() { + return (TypeArray) exceptionTable.getValue(this); + } + + public long getConstMethodSize() { + return constMethodSize.getValue(this); + } + + public byte getFlags() { + return flags.getValue(this); + } + + public long getCodeSize() { + return codeSize.getValue(this); + } + + public long getNameIndex() { + return nameIndex.getValue(this); + } + + public long getSignatureIndex() { + return signatureIndex.getValue(this); + } + + public long getGenericSignatureIndex() { + return genericSignatureIndex.getValue(this); + } + + public Symbol getName() { + return getMethod().getName(); + } + + public Symbol getSignature() { + return getMethod().getSignature(); + } + + public Symbol getGenericSignature() { + return getMethod().getGenericSignature(); + } + + // bytecode accessors + + /** Get a bytecode or breakpoint at the given bci */ + public int getBytecodeOrBPAt(int bci) { + return getHandle().getJByteAt(bytecodeOffset + bci) & 0xFF; + } + + public byte getBytecodeByteArg(int bci) { + return (byte) getBytecodeOrBPAt(bci); + } + + /** Fetches a 16-bit big-endian ("Java ordered") value from the + bytecode stream */ + public short getBytecodeShortArg(int bci) { + int hi = getBytecodeOrBPAt(bci); + int lo = getBytecodeOrBPAt(bci + 1); + return (short) ((hi << 8) | lo); + } + + /** Fetches a 32-bit big-endian ("Java ordered") value from the + bytecode stream */ + public int getBytecodeIntArg(int bci) { + int b4 = getBytecodeOrBPAt(bci); + int b3 = getBytecodeOrBPAt(bci + 1); + int b2 = getBytecodeOrBPAt(bci + 2); + int b1 = getBytecodeOrBPAt(bci + 3); + + return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; + } + + public byte[] getByteCode() { + byte[] bc = new byte[ (int) getCodeSize() ]; + for( int i=0; i < bc.length; i++ ) + { + long offs = bytecodeOffset + i; + bc[i] = getHandle().getJByteAt( offs ); + } + return bc; + } + + public long getObjectSize() { + return getConstMethodSize() * getHeap().getOopSize(); + } + + public void printValueOn(PrintStream tty) { + tty.print("ConstMethod " + getName().asString() + getSignature().asString() + "@" + getHandle()); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doOop(method, true); + visitor.doOop(exceptionTable, true); + visitor.doCInt(constMethodSize, true); + visitor.doByte(flags, true); + visitor.doCInt(codeSize, true); + visitor.doCInt(nameIndex, true); + visitor.doCInt(signatureIndex, true); + visitor.doCInt(genericSignatureIndex, true); + visitor.doCInt(codeSize, true); + } + } + + // Accessors + + public boolean hasLineNumberTable() { + return (getFlags() & HAS_LINENUMBER_TABLE) != 0; + } + + public int getLineNumberFromBCI(int bci) { + if (!VM.getVM().isCore()) { + if (bci == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) bci = 0; + } + + if (isNative()) { + return -1; + } + + if (Assert.ASSERTS_ENABLED) { + Assert.that(bci == 0 || 0 <= bci && bci < getCodeSize(), "illegal bci"); + } + int bestBCI = 0; + int bestLine = -1; + if (hasLineNumberTable()) { + // The line numbers are a short array of 2-tuples [start_pc, line_number]. + // Not necessarily sorted and not necessarily one-to-one. + CompressedLineNumberReadStream stream = + new CompressedLineNumberReadStream(getHandle(), (int) offsetOfCompressedLineNumberTable()); + while (stream.readPair()) { + if (stream.bci() == bci) { + // perfect match + return stream.line(); + } else { + // update best_bci/line + if (stream.bci() < bci && stream.bci() >= bestBCI) { + bestBCI = stream.bci(); + bestLine = stream.line(); + } + } + } + } + return bestLine; + } + + public LineNumberTableElement[] getLineNumberTable() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(hasLineNumberTable(), + "should only be called if table is present"); + } + int len = getLineNumberTableLength(); + CompressedLineNumberReadStream stream = + new CompressedLineNumberReadStream(getHandle(), (int) offsetOfCompressedLineNumberTable()); + LineNumberTableElement[] ret = new LineNumberTableElement[len]; + + for (int idx = 0; idx < len; idx++) { + stream.readPair(); + ret[idx] = new LineNumberTableElement(stream.bci(), stream.line()); + } + return ret; + } + + public boolean hasLocalVariableTable() { + return (getFlags() & HAS_LOCALVARIABLE_TABLE) != 0; + } + + public Symbol getLocalVariableName(int bci, int slot) { + return getMethod().getLocalVariableName(bci, slot); + } + + /** Should only be called if table is present */ + public LocalVariableTableElement[] getLocalVariableTable() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(hasLocalVariableTable(), "should only be called if table is present"); + } + LocalVariableTableElement[] ret = new LocalVariableTableElement[getLocalVariableTableLength()]; + long offset = offsetOfLocalVariableTable(); + for (int i = 0; i < ret.length; i++) { + ret[i] = new LocalVariableTableElement(getHandle(), offset); + offset += localVariableTableElementSize; + } + return ret; + } + + public boolean hasCheckedExceptions() { + return (getFlags() & HAS_CHECKED_EXCEPTIONS) != 0; + } + + public CheckedExceptionElement[] getCheckedExceptions() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(hasCheckedExceptions(), "should only be called if table is present"); + } + CheckedExceptionElement[] ret = new CheckedExceptionElement[getCheckedExceptionsLength()]; + long offset = offsetOfCheckedExceptions(); + for (int i = 0; i < ret.length; i++) { + ret[i] = new CheckedExceptionElement(getHandle(), offset); + offset += checkedExceptionElementSize; + } + return ret; + } + + + //--------------------------------------------------------------------------- + // Internals only below this point + // + + private boolean isNative() { + return getMethod().isNative(); + } + + // Offset of end of code + private long offsetOfCodeEnd() { + return bytecodeOffset + getCodeSize(); + } + + // Offset of start of compressed line number table (see methodOop.hpp) + private long offsetOfCompressedLineNumberTable() { + return offsetOfCodeEnd() + (isNative() ? 2 * VM.getVM().getAddressSize() : 0); + } + + // Offset of last short in methodOop + private long offsetOfLastU2Element() { + return getObjectSize() - 2; + } + + private long offsetOfCheckedExceptionsLength() { + return offsetOfLastU2Element(); + } + + private int getCheckedExceptionsLength() { + if (hasCheckedExceptions()) { + return (int) getHandle().getCIntegerAt(offsetOfCheckedExceptionsLength(), 2, true); + } else { + return 0; + } + } + + // Offset of start of checked exceptions + private long offsetOfCheckedExceptions() { + long offset = offsetOfCheckedExceptionsLength(); + long length = getCheckedExceptionsLength(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(length > 0, "should only be called if table is present"); + } + offset -= length * checkedExceptionElementSize; + return offset; + } + + private int getLineNumberTableLength() { + int len = 0; + if (hasLineNumberTable()) { + CompressedLineNumberReadStream stream = + new CompressedLineNumberReadStream(getHandle(), (int) offsetOfCompressedLineNumberTable()); + while (stream.readPair()) { + len += 1; + } + } + return len; + } + + private int getLocalVariableTableLength() { + if (hasLocalVariableTable()) { + return (int) getHandle().getCIntegerAt(offsetOfLocalVariableTableLength(), 2, true); + } else { + return 0; + } + } + + // Offset of local variable table length + private long offsetOfLocalVariableTableLength() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(hasLocalVariableTable(), "should only be called if table is present"); + } + if (hasCheckedExceptions()) { + return offsetOfCheckedExceptions() - 2; + } else { + return offsetOfLastU2Element(); + } + } + + private long offsetOfLocalVariableTable() { + long offset = offsetOfLocalVariableTableLength(); + long length = getLocalVariableTableLength(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(length > 0, "should only be called if table is present"); + } + offset -= length * localVariableTableElementSize; + return offset; + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstMethodKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstMethodKlass.java new file mode 100644 index 00000000000..1b90b1371a9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstMethodKlass.java @@ -0,0 +1,60 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// The ConstMethodKlass is the klass of a ConstMethod + +public class ConstMethodKlass extends Klass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("constMethodKlass"); + headerSize = type.getSize() + Oop.getHeaderSize(); + } + + ConstMethodKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static long headerSize; + + public long getObjectSize() { return alignObjectSize(headerSize); } + + public void printValueOn(PrintStream tty) { + tty.print("ConstMethodKlass"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPool.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPool.java new file mode 100644 index 00000000000..ccd6f3e33a6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPool.java @@ -0,0 +1,481 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +// A ConstantPool is an array containing class constants +// as described in the class file + +public class ConstantPool extends Array implements ClassConstants { + // Used for debugging this code + private static final boolean DEBUG = false; + + protected void debugMessage(String message) { + System.out.println(message); + } + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("constantPoolOopDesc"); + tags = new OopField(type.getOopField("_tags"), 0); + cache = new OopField(type.getOopField("_cache"), 0); + poolHolder = new OopField(type.getOopField("_pool_holder"), 0); + headerSize = type.getSize(); + elementSize = db.getOopSize(); + } + + ConstantPool(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public boolean isConstantPool() { return true; } + + private static OopField tags; + private static OopField cache; + private static OopField poolHolder; + + + private static long headerSize; + private static long elementSize; + + public TypeArray getTags() { return (TypeArray) tags.getValue(this); } + public ConstantPoolCache getCache() { return (ConstantPoolCache) cache.getValue(this); } + public Klass getPoolHolder() { return (Klass) poolHolder.getValue(this); } + + private long indexOffset(long index) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(index > 0 && index < getLength(), "invalid cp index"); + } + return (index * elementSize) + headerSize; + } + + public ConstantTag getTagAt(long index) { + return new ConstantTag(getTags().getByteAt((int) index)); + } + + public Oop getObjAt(long index){ + return getHeap().newOop(getHandle().getOopHandleAt(indexOffset(index))); + } + + public Symbol getSymbolAt(long index) { + return (Symbol) getObjAt(index); + } + + public int getIntAt(long index){ + return getHandle().getJIntAt(indexOffset(index)); + } + + public float getFloatAt(long index){ + return getHandle().getJFloatAt(indexOffset(index)); + } + + public long getLongAt(long index) { + int oneHalf = getHandle().getJIntAt(indexOffset(index + 1)); + int otherHalf = getHandle().getJIntAt(indexOffset(index)); + // buildLongFromIntsPD accepts higher address value, lower address value + // in that order. + return VM.getVM().buildLongFromIntsPD(oneHalf, otherHalf); + } + + public double getDoubleAt(long index) { + return Double.longBitsToDouble(getLongAt(index)); + } + + public int getFieldOrMethodAt(int which) { + if (DEBUG) { + System.err.print("ConstantPool.getFieldOrMethodAt(" + which + "): new index = "); + } + int i = -1; + ConstantPoolCache cache = getCache(); + if (cache == null) { + i = which; + } else { + // change byte-ordering and go via cache + i = cache.getEntryAt(0xFFFF & VM.getVM().getBytes().swapShort((short) which)).getConstantPoolIndex(); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(getTagAt(i).isFieldOrMethod(), "Corrupted constant pool"); + } + if (DEBUG) { + System.err.println(i); + } + int res = getIntAt(i); + if (DEBUG) { + System.err.println("ConstantPool.getFieldOrMethodAt(" + i + "): result = " + res); + } + return res; + } + + public int getNameAndTypeAt(int which) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(getTagAt(which).isNameAndType(), "Corrupted constant pool"); + } + int i = getIntAt(which); + if (DEBUG) { + System.err.println("ConstantPool.getNameAndTypeAt(" + which + "): result = " + i); + } + return i; + } + + public Symbol getNameRefAt(int which) { + int refIndex = getNameAndTypeAt(getNameAndTypeRefIndexAt(which)); + int nameIndex = extractLowShortFromInt(refIndex); + return getSymbolAt(nameIndex); + } + + public Symbol getSignatureRefAt(int which) { + int refIndex = getNameAndTypeAt(getNameAndTypeRefIndexAt(which)); + int sigIndex = extractHighShortFromInt(refIndex); + return getSymbolAt(sigIndex); + } + + // returns null, if not resolved. + public Klass getKlassRefAt(int which) { + if( ! getTagAt(which).isKlass()) return null; + return (Klass) getObjAt(which); + } + + // returns null, if not resolved. + public InstanceKlass getFieldOrMethodKlassRefAt(int which) { + int refIndex = getFieldOrMethodAt(which); + int klassIndex = extractLowShortFromInt(refIndex); + return (InstanceKlass) getKlassRefAt(klassIndex); + } + + // returns null, if not resolved. + public Method getMethodRefAt(int which) { + InstanceKlass klass = getFieldOrMethodKlassRefAt(which); + if (klass == null) return null; + Symbol name = getNameRefAt(which); + Symbol sig = getSignatureRefAt(which); + return klass.findMethod(name, sig); + } + + // returns null, if not resolved. + public Field getFieldRefAt(int which) { + InstanceKlass klass = getFieldOrMethodKlassRefAt(which); + if (klass == null) return null; + Symbol name = getNameRefAt(which); + Symbol sig = getSignatureRefAt(which); + return klass.findField(name, sig); + } + + public int getNameAndTypeRefIndexAt(int index) { + int refIndex = getFieldOrMethodAt(index); + if (DEBUG) { + System.err.println("ConstantPool.getNameAndTypeRefIndexAt(" + index + "): refIndex = " + refIndex); + } + int i = extractHighShortFromInt(refIndex); + if (DEBUG) { + System.err.println("ConstantPool.getNameAndTypeRefIndexAt(" + index + "): result = " + i); + } + return i; + } + + /** Lookup for entries consisting of (name_index, signature_index) */ + public int getNameRefIndexAt(int index) { + int refIndex = getNameAndTypeAt(index); + if (DEBUG) { + System.err.println("ConstantPool.getNameRefIndexAt(" + index + "): refIndex = " + refIndex); + } + int i = extractLowShortFromInt(refIndex); + if (DEBUG) { + System.err.println("ConstantPool.getNameRefIndexAt(" + index + "): result = " + i); + } + return i; + } + + /** Lookup for entries consisting of (name_index, signature_index) */ + public int getSignatureRefIndexAt(int index) { + int refIndex = getNameAndTypeAt(index); + if (DEBUG) { + System.err.println("ConstantPool.getSignatureRefIndexAt(" + index + "): refIndex = " + refIndex); + } + int i = extractHighShortFromInt(refIndex); + if (DEBUG) { + System.err.println("ConstantPool.getSignatureRefIndexAt(" + index + "): result = " + i); + } + return i; + } + + final private static String[] nameForTag = new String[] { + }; + + private String nameForTag(int tag) { + switch (tag) { + case JVM_CONSTANT_Utf8: return "JVM_CONSTANT_Utf8"; + case JVM_CONSTANT_Unicode: return "JVM_CONSTANT_Unicode"; + case JVM_CONSTANT_Integer: return "JVM_CONSTANT_Integer"; + case JVM_CONSTANT_Float: return "JVM_CONSTANT_Float"; + case JVM_CONSTANT_Long: return "JVM_CONSTANT_Long"; + case JVM_CONSTANT_Double: return "JVM_CONSTANT_Double"; + case JVM_CONSTANT_Class: return "JVM_CONSTANT_Class"; + case JVM_CONSTANT_String: return "JVM_CONSTANT_String"; + case JVM_CONSTANT_Fieldref: return "JVM_CONSTANT_Fieldref"; + case JVM_CONSTANT_Methodref: return "JVM_CONSTANT_Methodref"; + case JVM_CONSTANT_InterfaceMethodref: return "JVM_CONSTANT_InterfaceMethodref"; + case JVM_CONSTANT_NameAndType: return "JVM_CONSTANT_NameAndType"; + case JVM_CONSTANT_Invalid: return "JVM_CONSTANT_Invalid"; + case JVM_CONSTANT_UnresolvedClass: return "JVM_CONSTANT_UnresolvedClass"; + case JVM_CONSTANT_ClassIndex: return "JVM_CONSTANT_ClassIndex"; + case JVM_CONSTANT_UnresolvedString: return "JVM_CONSTANT_UnresolvedString"; + case JVM_CONSTANT_StringIndex: return "JVM_CONSTANT_StringIndex"; + } + throw new InternalError("unknown tag"); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doOop(tags, true); + visitor.doOop(cache, true); + visitor.doOop(poolHolder, true); + + final int length = (int) getLength(); + // zero'th pool entry is always invalid. ignore it. + for (int index = 1; index < length; index++) { + int ctag = (int) getTags().getByteAt((int) index); + switch (ctag) { + case JVM_CONSTANT_ClassIndex: + case JVM_CONSTANT_StringIndex: + case JVM_CONSTANT_Integer: + visitor.doInt(new IntField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); + break; + + case JVM_CONSTANT_Float: + visitor.doFloat(new FloatField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); + break; + + case JVM_CONSTANT_Long: + visitor.doLong(new LongField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); + // long entries occupy two slots + index++; + break; + + case JVM_CONSTANT_Double: + visitor.doDouble(new DoubleField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); + // double entries occupy two slots + index++; + break; + + case JVM_CONSTANT_UnresolvedClass: + case JVM_CONSTANT_Class: + case JVM_CONSTANT_UnresolvedString: + case JVM_CONSTANT_Utf8: + visitor.doOop(new OopField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); + break; + + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: + case JVM_CONSTANT_NameAndType: + visitor.doInt(new IntField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); + break; + } + } + } + /* + int length = getLength(); + for (int index = 0; index < length; index++) { + long offset = baseOffset + (index + typeDataBase.getOopSize()); + visitor.doOop(new IndexableField(index, offset, false), getObjAt(index)); + } + */ + } + + public void writeBytes(OutputStream os) throws IOException { + // Map between any modified UTF-8 and it's constant pool index. + Map utf8ToIndex = new HashMap(); + DataOutputStream dos = new DataOutputStream(os); + TypeArray tags = getTags(); + int len = (int)getLength(); + int ci = 0; // constant pool index + + // collect all modified UTF-8 Strings from Constant Pool + + for (ci = 1; ci < len; ci++) { + byte cpConstType = tags.getByteAt(ci); + if(cpConstType == JVM_CONSTANT_Utf8) { + Symbol sym = getSymbolAt(ci); + utf8ToIndex.put(sym.asString(), new Short((short) ci)); + } + else if(cpConstType == JVM_CONSTANT_Long || + cpConstType == JVM_CONSTANT_Double) { + ci++; + } + } + + + for(ci = 1; ci < len; ci++) { + int cpConstType = (int)tags.getByteAt(ci); + // write cp_info + // write constant type + switch(cpConstType) { + case JVM_CONSTANT_Utf8: { + dos.writeByte(cpConstType); + Symbol sym = getSymbolAt(ci); + dos.writeShort((short)sym.getLength()); + dos.write(sym.asByteArray()); + if (DEBUG) debugMessage("CP[" + ci + "] = modified UTF-8 " + sym.asString()); + break; + } + + case JVM_CONSTANT_Unicode: + throw new IllegalArgumentException("Unicode constant!"); + + case JVM_CONSTANT_Integer: + dos.writeByte(cpConstType); + dos.writeInt(getIntAt(ci)); + if (DEBUG) debugMessage("CP[" + ci + "] = int " + getIntAt(ci)); + break; + + case JVM_CONSTANT_Float: + dos.writeByte(cpConstType); + dos.writeFloat(getFloatAt(ci)); + if (DEBUG) debugMessage("CP[" + ci + "] = float " + getFloatAt(ci)); + break; + + case JVM_CONSTANT_Long: { + dos.writeByte(cpConstType); + long l = getLongAt(ci); + // long entries occupy two pool entries + ci++; + dos.writeLong(l); + break; + } + + case JVM_CONSTANT_Double: + dos.writeByte(cpConstType); + dos.writeDouble(getDoubleAt(ci)); + // double entries occupy two pool entries + ci++; + break; + + case JVM_CONSTANT_Class: { + dos.writeByte(cpConstType); + // Klass already resolved. ConstantPool constains klassOop. + Klass refKls = (Klass) getObjAt(ci); + String klassName = refKls.getName().asString(); + Short s = (Short) utf8ToIndex.get(klassName); + dos.writeShort(s.shortValue()); + if (DEBUG) debugMessage("CP[" + ci + "] = class " + s); + break; + } + + // case JVM_CONSTANT_ClassIndex: + case JVM_CONSTANT_UnresolvedClass: { + dos.writeByte(JVM_CONSTANT_Class); + String klassName = getSymbolAt(ci).asString(); + Short s = (Short) utf8ToIndex.get(klassName); + dos.writeShort(s.shortValue()); + if (DEBUG) debugMessage("CP[" + ci + "] = class " + s); + break; + } + + case JVM_CONSTANT_String: { + dos.writeByte(cpConstType); + String str = OopUtilities.stringOopToString(getObjAt(ci)); + Short s = (Short) utf8ToIndex.get(str); + dos.writeShort(s.shortValue()); + if (DEBUG) debugMessage("CP[" + ci + "] = string " + s); + break; + } + + // case JVM_CONSTANT_StringIndex: + case JVM_CONSTANT_UnresolvedString: { + dos.writeByte(JVM_CONSTANT_String); + String val = getSymbolAt(ci).asString(); + + Short s = (Short) utf8ToIndex.get(val); + dos.writeShort(s.shortValue()); + if (DEBUG) debugMessage("CP[" + ci + "] = string " + s); + break; + } + + // all external, internal method/field references + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: { + dos.writeByte(cpConstType); + int value = getIntAt(ci); + short klassIndex = (short) extractLowShortFromInt(value); + short nameAndTypeIndex = (short) extractHighShortFromInt(value); + dos.writeShort(klassIndex); + dos.writeShort(nameAndTypeIndex); + if (DEBUG) debugMessage("CP[" + ci + "] = ref klass = " + + klassIndex + ", N&T = " + nameAndTypeIndex); + break; + } + + case JVM_CONSTANT_NameAndType: { + dos.writeByte(cpConstType); + int value = getIntAt(ci); + short nameIndex = (short) extractLowShortFromInt(value); + short signatureIndex = (short) extractHighShortFromInt(value); + dos.writeShort(nameIndex); + dos.writeShort(signatureIndex); + if (DEBUG) debugMessage("CP[" + ci + "] = N&T name = " + nameIndex + + ", type = " + signatureIndex); + break; + } + } // switch + } + dos.flush(); + return; + } + + public void printValueOn(PrintStream tty) { + tty.print("ConstantPool for " + getPoolHolder().getName().asString()); + } + + public long getObjectSize() { + return alignObjectSize(headerSize + (getLength() * elementSize)); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private static int extractHighShortFromInt(int val) { + return (val >> 16) & 0xFFFF; + } + + private static int extractLowShortFromInt(int val) { + return val & 0xFFFF; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java new file mode 100644 index 00000000000..9285f8e7898 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +// A ConstantPool is an array containing class constants +// as described in the class file + +public class ConstantPoolCache extends Array { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("constantPoolCacheOopDesc"); + constants = new OopField(type.getOopField("_constant_pool"), 0); + baseOffset = type.getSize(); + + Type elType = db.lookupType("ConstantPoolCacheEntry"); + elementSize = elType.getSize(); + } + + ConstantPoolCache(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public boolean isConstantPoolCache() { return true; } + + private static OopField constants; + + private static long baseOffset; + private static long elementSize; + + public ConstantPool getConstants() { return (ConstantPool) constants.getValue(this); } + + public long getObjectSize() { + return alignObjectSize(baseOffset + getLength() * elementSize); + } + + public ConstantPoolCacheEntry getEntryAt(int i) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(0 <= i && i < getLength(), "index out of bounds"); + } + return new ConstantPoolCacheEntry(this, i); + } + + public int getIntAt(int entry, int fld) { + //alignObjectSize ? + long offset = baseOffset + /*alignObjectSize*/entry * elementSize + fld* getHeap().getIntSize(); + return (int) getHandle().getCIntegerAt(offset, getHeap().getIntSize(), true ); + } + + + public void printValueOn(PrintStream tty) { + tty.print("ConstantPoolCache for " + getConstants().getPoolHolder().getName().asString()); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doOop(constants, true); + for (int i = 0; i < getLength(); i++) { + ConstantPoolCacheEntry entry = getEntryAt(i); + entry.iterateFields(visitor); + } + } + } +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCacheEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCacheEntry.java new file mode 100644 index 00000000000..429d41b5e6c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCacheEntry.java @@ -0,0 +1,98 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class ConstantPoolCacheEntry { + private static long size; + private static long baseOffset; + private static CIntegerField indices; + private static sun.jvm.hotspot.types.OopField f1; + private static CIntegerField f2; + private static CIntegerField flags; + + private ConstantPoolCache cp; + private long offset; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("ConstantPoolCacheEntry"); + size = type.getSize(); + + indices = type.getCIntegerField("_indices"); + f1 = type.getOopField ("_f1"); + f2 = type.getCIntegerField("_f2"); + flags = type.getCIntegerField("_flags"); + + type = db.lookupType("constantPoolCacheOopDesc"); + baseOffset = type.getSize(); + } + + ConstantPoolCacheEntry(ConstantPoolCache cp, int index) { + this.cp = cp; + offset = baseOffset + index * size; + } + + public int getConstantPoolIndex() { + return (int) (getIndices() & 0xFFFF); + } + + private long getIndices() { + return cp.getHandle().getCIntegerAt(indices.getOffset() + offset, indices.getSize(), indices.isUnsigned()); + } + + public Oop getF1() { + return cp.getHeap().newOop(cp.getHandle().getOopHandleAt(f1.getOffset() + offset)); + } + + public int getF2() { + return cp.getHandle().getJIntAt(f1.getOffset() + offset); + } + + public int getFlags() { + return cp.getHandle().getJIntAt(flags.getOffset() + offset); + } + + static NamedFieldIdentifier f1FieldName = new NamedFieldIdentifier("_f1"); + static NamedFieldIdentifier f2FieldName = new NamedFieldIdentifier("_f2"); + static NamedFieldIdentifier flagsFieldName = new NamedFieldIdentifier("_flags"); + + public void iterateFields(OopVisitor visitor) { + visitor.doOop(new OopField(f1FieldName, f1.getOffset() + offset, true), true); + visitor.doInt(new IntField(f2FieldName, f2.getOffset() + offset, true), true); + visitor.doInt(new IntField(flagsFieldName, flags.getOffset() + offset, true), true); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCacheKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCacheKlass.java new file mode 100644 index 00000000000..7d87e984128 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCacheKlass.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// A ConstantPoolCacheKlass is the klass of a ConstantPoolCache + +public class ConstantPoolCacheKlass extends ArrayKlass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("constantPoolCacheKlass"); + } + + ConstantPoolCacheKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public void printValueOn(PrintStream tty) { + tty.print("ConstantPoolCacheKlass"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolKlass.java new file mode 100644 index 00000000000..c40a87dab18 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolKlass.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// A ConstantPoolKlass is the klass of a ConstantPool + +public class ConstantPoolKlass extends ArrayKlass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("constantPoolKlass"); + } + + ConstantPoolKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public void printValueOn(PrintStream tty) { + tty.print("ConstantPoolKlass"); + } +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/DefaultHeapVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/DefaultHeapVisitor.java new file mode 100644 index 00000000000..1995ed2b2f7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/DefaultHeapVisitor.java @@ -0,0 +1,34 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +/** A DefaultHeapVisitor implements basic no-op HeapVisitor + functionality. */ + +public class DefaultHeapVisitor implements HeapVisitor { + public void prologue(long usedSize) {} + public boolean doObj(Oop obj) {return false;} + public void epilogue() {} +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/DefaultOopVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/DefaultOopVisitor.java new file mode 100644 index 00000000000..6340ec35f40 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/DefaultOopVisitor.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +/** A DefaultOopVisitor implements basic no-op OopVisitor + functionality except that the setObj() and getObj() methods are + implemented properly. */ + +public class DefaultOopVisitor implements OopVisitor { + private Oop obj; + + public void prologue() {} + + // Called after visiting an object + public void epilogue() {} + + public void setObj(Oop obj) { + this.obj = obj; + } + + public Oop getObj() { + return obj; + } + + // Callback methods for each field type in an object + public void doOop(OopField field, boolean isVMField) {} + public void doByte(ByteField field, boolean isVMField) {} + public void doChar(CharField field, boolean isVMField) {} + public void doBoolean(BooleanField field, boolean isVMField) {} + public void doShort(ShortField field, boolean isVMField) {} + public void doInt(IntField field, boolean isVMField) {} + public void doLong(LongField field, boolean isVMField) {} + public void doFloat(FloatField field, boolean isVMField) {} + public void doDouble(DoubleField field, boolean isVMField) {} + public void doCInt(CIntField field, boolean isVMField) {} +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/DoubleField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/DoubleField.java new file mode 100644 index 00000000000..10369945184 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/DoubleField.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.*; + +// The class for a double field simply provides access to the value. +public class DoubleField extends Field { + public DoubleField(FieldIdentifier id, long offset, boolean isVMField) { + super(id, offset, isVMField); + } + + public DoubleField(sun.jvm.hotspot.types.JDoubleField vmField, long startOffset) { + super(new NamedFieldIdentifier(vmField.getName()), vmField.getOffset() + startOffset, true); + } + + public DoubleField(InstanceKlass holder, int fieldArrayIndex) { + super(holder, fieldArrayIndex); + } + + public double getValue(Oop obj) { return obj.getHandle().getJDoubleAt(getOffset()); } + public void setValue(Oop obj, double value) throws MutationException { + // Fix this: setJDoubleAt is missing in Address + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java new file mode 100644 index 00000000000..918681f60aa --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java @@ -0,0 +1,153 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; + +import sun.jvm.hotspot.runtime.*; + +// Super class for all fields in an object +public class Field { + + Field(FieldIdentifier id, long offset, boolean isVMField) { + this.offset = offset; + this.id = id; + this.isVMField = isVMField; + } + + /** Constructor for fields that are named in an InstanceKlass's + fields array (i.e., named, non-VM fields) */ + Field(InstanceKlass holder, int fieldArrayIndex) { + this.holder = holder; + this.fieldArrayIndex = fieldArrayIndex; + + ConstantPool cp = holder.getConstants(); + TypeArray fields = holder.getFields(); + short access = fields.getShortAt(fieldArrayIndex + InstanceKlass.ACCESS_FLAGS_OFFSET); + short nameIndex = fields.getShortAt(fieldArrayIndex + InstanceKlass.NAME_INDEX_OFFSET); + short signatureIndex = fields.getShortAt(fieldArrayIndex + InstanceKlass.SIGNATURE_INDEX_OFFSET); + offset = VM.getVM().buildIntFromShorts(fields.getShortAt(fieldArrayIndex + InstanceKlass.LOW_OFFSET), + fields.getShortAt(fieldArrayIndex + InstanceKlass.HIGH_OFFSET)); + short genericSignatureIndex = fields.getShortAt(fieldArrayIndex + InstanceKlass.GENERIC_SIGNATURE_INDEX_OFFSET); + Symbol name = cp.getSymbolAt(nameIndex); + id = new NamedFieldIdentifier(name.asString()); + signature = cp.getSymbolAt(signatureIndex); + if (genericSignatureIndex != 0) { + genericSignature = cp.getSymbolAt(genericSignatureIndex); + } else { + genericSignature = null; + } + + fieldType = new FieldType(signature); + accessFlags = new AccessFlags(access); + } + + private long offset; + private FieldIdentifier id; + private boolean isVMField; + // Java fields only + private InstanceKlass holder; + private FieldType fieldType; + private Symbol signature; + private Symbol genericSignature; + private AccessFlags accessFlags; + private int fieldArrayIndex; + + /** Returns the byte offset of the field within the object or klass */ + public long getOffset() { return offset; } + + /** Returns the identifier of the field */ + public FieldIdentifier getID() { return id; } + + /** Indicates whether this is a VM field */ + public boolean isVMField() { return isVMField; } + + /** Indicates whether this is a named field */ + public boolean isNamedField() { return (id instanceof NamedFieldIdentifier); } + + public void printOn(PrintStream tty) { + getID().printOn(tty); + tty.print(" {" + getOffset() + "} :"); + } + + /** (Named, non-VM fields only) Returns the InstanceKlass containing + this (static or non-static) field. */ + public InstanceKlass getFieldHolder() { + return holder; + } + + /** (Named, non-VM fields only) Returns the index in the fields + TypeArray for this field. Equivalent to the "index" in the VM's + fieldDescriptors. */ + public int getFieldArrayIndex() { + return fieldArrayIndex; + } + + /** (Named, non-VM fields only) Retrieves the access flags. */ + public long getAccessFlags() { return accessFlags.getValue(); } + public AccessFlags getAccessFlagsObj() { return accessFlags; } + + /** (Named, non-VM fields only) Returns the type of this field. */ + public FieldType getFieldType() { return fieldType; } + + /** (Named, non-VM fields only) Returns the signature of this + field. */ + public Symbol getSignature() { return signature; } + public Symbol getGenericSignature() { return genericSignature; } + + // + // Following acccessors are for named, non-VM fields only + // + public boolean isPublic() { return accessFlags.isPublic(); } + public boolean isPrivate() { return accessFlags.isPrivate(); } + public boolean isProtected() { return accessFlags.isProtected(); } + public boolean isPackagePrivate() { return !isPublic() && !isPrivate() && !isProtected(); } + + public boolean isStatic() { return accessFlags.isStatic(); } + public boolean isFinal() { return accessFlags.isFinal(); } + public boolean isVolatile() { return accessFlags.isVolatile(); } + public boolean isTransient() { return accessFlags.isTransient(); } + + public boolean isSynthetic() { return accessFlags.isSynthetic(); } + public boolean isEnumConstant() { return accessFlags.isEnum(); } + + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (! (obj instanceof Field)) { + return false; + } + + Field other = (Field) obj; + return this.getFieldHolder().equals(other.getFieldHolder()) && + this.getID().equals(other.getID()); + } + + public int hashCode() { + return getFieldHolder().hashCode() ^ getID().hashCode(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/FieldIdentifier.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/FieldIdentifier.java new file mode 100644 index 00000000000..66fa0e01a1d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/FieldIdentifier.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; + +// A FieldIdentifier describes a field in an Oop with a name +public class FieldIdentifier { + + public String getName() { return ""; } + + public void printOn(PrintStream tty) { + tty.print(" - " + getName() + ":\t"); + } + +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/FieldType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/FieldType.java new file mode 100644 index 00000000000..f026ad5fc8f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/FieldType.java @@ -0,0 +1,107 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import sun.jvm.hotspot.runtime.BasicType; +import sun.jvm.hotspot.utilities.*; + +public class FieldType { + + private Symbol signature; + private char first; + + public FieldType(Symbol signature) { + this.signature = signature; + this.first = (char) signature.getByteAt(0); + if (Assert.ASSERTS_ENABLED) { + switch (this.first) { + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + case 'L': + case '[': + break; // Ok. signature char known + default: + Assert.that(false, "Unknown char in field signature \"" + signature.asString() + "\": " + this.first); + } + } + } + + public boolean isOop() { return isObject() || isArray(); } + public boolean isByte() { return first == 'B'; } + public boolean isChar() { return first == 'C'; } + public boolean isDouble() { return first == 'D'; } + public boolean isFloat() { return first == 'F'; } + public boolean isInt() { return first == 'I'; } + public boolean isLong() { return first == 'J'; } + public boolean isShort() { return first == 'S'; } + public boolean isBoolean() { return first == 'Z'; } + public boolean isObject() { return first == 'L'; } + public boolean isArray() { return first == '['; } + + public static class ArrayInfo { + private int dimension; + private int elementBasicType; // See BasicType.java + // FIXME: consider adding name of element class + + public ArrayInfo(int dimension, int elementBasicType) { + this.dimension = dimension; + this.elementBasicType = elementBasicType; + } + + public int dimension() { return dimension; } + /** See BasicType.java */ + public int elementBasicType() { return elementBasicType; } + } + + /** Only valid for T_ARRAY; throws unspecified exception otherwise */ + public ArrayInfo getArrayInfo() { + int index = 1; + int dim = 1; + index = skipOptionalSize(signature, index); + while (signature.getByteAt(index) == '[') { + index++; + dim++; + skipOptionalSize(signature, index); + } + int elementType = BasicType.charToType((char) signature.getByteAt(index)); + return new ArrayInfo(dim, elementType); + } + + private int skipOptionalSize(Symbol sig, int index) { + byte c = sig.getByteAt(index); + while (c >= '0' && c <= '9') { + ++index; + c = sig.getByteAt(index); + } + return index; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/FloatField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/FloatField.java new file mode 100644 index 00000000000..848fc6cfb7e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/FloatField.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.*; + +// The class for a float field simply provides access to the value. +public class FloatField extends Field { + public FloatField(FieldIdentifier id, long offset, boolean isVMField) { + super(id, offset, isVMField); + } + + public FloatField(sun.jvm.hotspot.types.JFloatField vmField, long startOffset) { + super(new NamedFieldIdentifier(vmField.getName()), vmField.getOffset() + startOffset, true); + } + + public FloatField(InstanceKlass holder, int fieldArrayIndex) { + super(holder, fieldArrayIndex); + } + + public float getValue(Oop obj) { return obj.getHandle().getJFloatAt(getOffset()); } + public void setValue(Oop obj, float value) throws MutationException { + // Fix this: setJFloatAt is missing in Address + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/GenerateOopMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/GenerateOopMap.java new file mode 100644 index 00000000000..d0b31dc2b32 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/GenerateOopMap.java @@ -0,0 +1,2323 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +/** Minimal port of the VM's oop map generator for interpreted frames */ + +public class GenerateOopMap { + interface JumpClosure { + public void process(GenerateOopMap c, int bcpDelta, int[] data); + } + + // Used for debugging this code + private static final boolean DEBUG = false; + + // These two should be removed. But requires som code to be cleaned up + private static final int MAXARGSIZE = 256; // This should be enough + private static final int MAX_LOCAL_VARS = 65536; // 16-bit entry + private static final boolean TraceMonitorMismatch = true; + private static final boolean TraceOopMapRewrites = true; + + // Commonly used constants + static CellTypeState[] epsilonCTS = { CellTypeState.bottom }; + static CellTypeState refCTS = CellTypeState.ref; + static CellTypeState valCTS = CellTypeState.value; + static CellTypeState[] vCTS = { CellTypeState.value, CellTypeState.bottom }; + static CellTypeState[] rCTS = { CellTypeState.ref, CellTypeState.bottom }; + static CellTypeState[] rrCTS = { CellTypeState.ref, CellTypeState.ref, CellTypeState.bottom }; + static CellTypeState[] vrCTS = { CellTypeState.value, CellTypeState.ref, CellTypeState.bottom }; + static CellTypeState[] vvCTS = { CellTypeState.value, CellTypeState.value, CellTypeState.bottom }; + static CellTypeState[] rvrCTS = { CellTypeState.ref, CellTypeState.value, CellTypeState.ref, CellTypeState.bottom }; + static CellTypeState[] vvrCTS = { CellTypeState.value, CellTypeState.value, CellTypeState.ref, CellTypeState.bottom }; + static CellTypeState[] vvvCTS = { CellTypeState.value, CellTypeState.value, CellTypeState.value, CellTypeState.bottom }; + static CellTypeState[] vvvrCTS = { CellTypeState.value, CellTypeState.value, CellTypeState.value, CellTypeState.ref, CellTypeState.bottom }; + static CellTypeState[] vvvvCTS = { CellTypeState.value, CellTypeState.value, CellTypeState.value, CellTypeState.value, CellTypeState.bottom }; + + /** Specialization of SignatureIterator - compute the effects of a call */ + static class ComputeCallStack extends SignatureIterator { + CellTypeStateList _effect; + int _idx; + + void set(CellTypeState state) { _effect.get(_idx++).set(state); } + int length() { return _idx; }; + + public void doBool () { set(CellTypeState.value); } + public void doChar () { set(CellTypeState.value); } + public void doFloat () { set(CellTypeState.value); } + public void doByte () { set(CellTypeState.value); } + public void doShort () { set(CellTypeState.value); } + public void doInt () { set(CellTypeState.value); } + public void doVoid () { set(CellTypeState.bottom);} + public void doObject(int begin, int end) { set(CellTypeState.ref); } + public void doArray (int begin, int end) { set(CellTypeState.ref); } + + public void doDouble() { set(CellTypeState.value); + set(CellTypeState.value); } + public void doLong () { set(CellTypeState.value); + set(CellTypeState.value); } + + ComputeCallStack(Symbol signature) { + super(signature); + } + + // Compute methods + int computeForParameters(boolean is_static, CellTypeStateList effect) { + _idx = 0; + _effect = effect; + + if (!is_static) { + effect.get(_idx++).set(CellTypeState.ref); + } + + iterateParameters(); + + return length(); + }; + + int computeForReturntype(CellTypeStateList effect) { + _idx = 0; + _effect = effect; + iterateReturntype(); + set(CellTypeState.bottom); // Always terminate with a bottom state, so ppush works + + return length(); + } + } + + /** Specialization of SignatureIterator - in order to set up first stack frame */ + static class ComputeEntryStack extends SignatureIterator { + CellTypeStateList _effect; + int _idx; + + void set(CellTypeState state) { _effect.get(_idx++).set(state); } + int length() { return _idx; }; + + public void doBool () { set(CellTypeState.value); } + public void doChar () { set(CellTypeState.value); } + public void doFloat () { set(CellTypeState.value); } + public void doByte () { set(CellTypeState.value); } + public void doShort () { set(CellTypeState.value); } + public void doInt () { set(CellTypeState.value); } + public void doVoid () { set(CellTypeState.bottom);} + public void doObject(int begin, int end) { set(CellTypeState.makeSlotRef(_idx)); } + public void doArray (int begin, int end) { set(CellTypeState.makeSlotRef(_idx)); } + + public void doDouble() { set(CellTypeState.value); + set(CellTypeState.value); } + public void doLong () { set(CellTypeState.value); + set(CellTypeState.value); } + + ComputeEntryStack(Symbol signature) { + super(signature); + } + + // Compute methods + int computeForParameters(boolean is_static, CellTypeStateList effect) { + _idx = 0; + _effect = effect; + + if (!is_static) { + effect.get(_idx++).set(CellTypeState.makeSlotRef(0)); + } + + iterateParameters(); + + return length(); + }; + + int computeForReturntype(CellTypeStateList effect) { + _idx = 0; + _effect = effect; + iterateReturntype(); + set(CellTypeState.bottom); // Always terminate with a bottom state, so ppush works + + return length(); + } + } + + /** Contains maping between jsr targets and there return addresses. + One-to-many mapping. */ + static class RetTableEntry { + private static int _init_nof_jsrs; // Default size of jsrs list + private int _target_bci; // Target PC address of jump (bytecode index) + private List/**/ _jsrs; // List of return addresses (bytecode index) + private RetTableEntry _next; // Link to next entry + + RetTableEntry(int target, RetTableEntry next) { + _target_bci = target; + _jsrs = new ArrayList(_init_nof_jsrs); + _next = next; + } + + // Query + int targetBci() { return _target_bci; } + int nofJsrs() { return _jsrs.size(); } + int jsrs(int i) { return ((Integer) _jsrs.get(i)).intValue(); } + + // Update entry + void addJsr (int return_bci) { _jsrs.add(new Integer(return_bci)); } + void addDelta(int bci, int delta) { + if (_target_bci > bci) { + _target_bci += delta; + } + + for (int k = 0; k < nofJsrs(); k++) { + int jsr = jsrs(k); + if (jsr > bci) { + _jsrs.set(k, new Integer(jsr+delta)); + } + } + } + RetTableEntry next() { return _next; } + } + + static class RetTable { + private RetTableEntry _first; + private static int _init_nof_entries; + + private void addJsr(int return_bci, int target_bci) { + RetTableEntry entry = _first; + + // Scan table for entry + for (;(entry != null) && (entry.targetBci() != target_bci); entry = entry.next()); + + if (entry == null) { + // Allocate new entry and put in list + entry = new RetTableEntry(target_bci, _first); + _first = entry; + } + + // Now "entry" is set. Make sure that the entry is initialized + // and has room for the new jsr. + entry.addJsr(return_bci); + } + + RetTable() {} + void computeRetTable(Method method) { + BytecodeStream i = new BytecodeStream(method); + int bytecode; + + while( (bytecode = i.next()) >= 0) { + switch (bytecode) { + case Bytecodes._jsr: + addJsr(i.nextBCI(), i.dest()); + break; + case Bytecodes._jsr_w: + addJsr(i.nextBCI(), i.dest_w()); + break; + } + } + } + void updateRetTable(int bci, int delta) { + RetTableEntry cur = _first; + while(cur != null) { + cur.addDelta(bci, delta); + cur = cur.next(); + } + } + RetTableEntry findJsrsForTarget(int targBci) { + RetTableEntry cur = _first; + + while(cur != null) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(cur.targetBci() != -1, "sanity check"); + } + if (cur.targetBci() == targBci) { + return cur; + } + cur = cur.next(); + } + throw new RuntimeException("Should not reach here"); + } + } + + static class BasicBlock { + private boolean _changed; // Reached a fixpoint or not + static final int _dead_basic_block = -2; + // Alive but not yet reached by analysis + static final int _unreached = -1; + // >=0: Alive and has a merged state + + int _bci; // Start of basic block + int _end_bci; // Bci of last instruction in basicblock + int _max_locals; // Determines split between vars and stack + int _max_stack; // Determines split between stack and monitors + CellTypeStateList _state; // State (vars, stack) at entry. + int _stack_top; // -1 indicates bottom stack value. + int _monitor_top; // -1 indicates bottom monitor stack value. + + CellTypeStateList vars() { return _state; } + CellTypeStateList stack() { return _state.subList(_max_locals, _state.size()); } + + boolean changed() { return _changed; } + void setChanged(boolean s) { _changed = s; } + + // Analysis has reached this basicblock + boolean isReachable() { return _stack_top >= 0; } + + // All basicblocks that are unreachable are going to have a _stack_top == _dead_basic_block. + // This info. is setup in a pre-parse before the real abstract interpretation starts. + boolean isDead() { return _stack_top == _dead_basic_block; } + boolean isAlive() { return _stack_top != _dead_basic_block; } + void markAsAlive() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isDead(), "must be dead"); + _stack_top = _unreached; + } + } + } + + //---------------------------------------------------------------------- + // Protected routines for GenerateOopMap + // + + // _monitor_top is set to this constant to indicate that a monitor matching + // problem was encountered prior to this point in control flow. + protected static final int bad_monitors = -1; + + // Main variables + Method _method; // The method we are examining + RetTable _rt; // Contains the return address mappings + int _max_locals; // Cached value of no. of locals + int _max_stack; // Cached value of max. stack depth + int _max_monitors; // Cached value of max. monitor stack depth + boolean _has_exceptions; // True, if exceptions exist for method + boolean _got_error; // True, if an error occured during interpretation. + String _error_msg; // Error message. Set if _got_error is true. + // bool _did_rewriting; // was bytecodes rewritten + // bool _did_relocation; // was relocation neccessary + boolean _monitor_safe; // The monitors in this method have been determined + // to be safe. + + // Working Cell type state + int _state_len; // Size of states + CellTypeStateList _state; // list of states + char[] _state_vec_buf; // Buffer used to print a readable version of a state + int _stack_top; + int _monitor_top; + + // Timing and statistics + // static elapsedTimer _total_oopmap_time; // Holds cumulative oopmap generation time + // static long _total_byte_count; // Holds cumulative number of bytes inspected + + // Monitor query logic + int _report_for_exit_bci; + int _matching_enter_bci; + + // Cell type methods + void initState() { + _state_len = _max_locals + _max_stack + _max_monitors; + _state = new CellTypeStateList(_state_len); + _state_vec_buf = new char[Math.max(_max_locals, Math.max(_max_stack, Math.max(_max_monitors, 1)))]; + } + void makeContextUninitialized () { + CellTypeStateList vs = vars(); + + for (int i = 0; i < _max_locals; i++) + vs.get(i).set(CellTypeState.uninit); + + _stack_top = 0; + _monitor_top = 0; + } + + int methodsigToEffect (Symbol signature, boolean isStatic, CellTypeStateList effect) { + ComputeEntryStack ces = new ComputeEntryStack(signature); + return ces.computeForParameters(isStatic, effect); + } + + boolean mergeStateVectors (CellTypeStateList cts, CellTypeStateList bbts) { + int i; + int len = _max_locals + _stack_top; + boolean change = false; + + for (i = len - 1; i >= 0; i--) { + CellTypeState v = cts.get(i).merge(bbts.get(i), i); + change = change || !v.equal(bbts.get(i)); + bbts.get(i).set(v); + } + + if (_max_monitors > 0 && _monitor_top != bad_monitors) { + // If there are no monitors in the program, or there has been + // a monitor matching error before this point in the program, + // then we do not merge in the monitor state. + + int base = _max_locals + _max_stack; + len = base + _monitor_top; + for (i = len - 1; i >= base; i--) { + CellTypeState v = cts.get(i).merge(bbts.get(i), i); + + // Can we prove that, when there has been a change, it will already + // have been detected at this point? That would make this equal + // check here unnecessary. + change = change || !v.equal(bbts.get(i)); + bbts.get(i).set(v); + } + } + + return change; + } + + void copyState (CellTypeStateList dst, CellTypeStateList src) { + int len = _max_locals + _stack_top; + for (int i = 0; i < len; i++) { + if (src.get(i).isNonlockReference()) { + dst.get(i).set(CellTypeState.makeSlotRef(i)); + } else { + dst.get(i).set(src.get(i)); + } + } + if (_max_monitors > 0 && _monitor_top != bad_monitors) { + int base = _max_locals + _max_stack; + len = base + _monitor_top; + for (int i = base; i < len; i++) { + dst.get(i).set(src.get(i)); + } + } + } + + void mergeStateIntoBB (BasicBlock bb) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(bb.isAlive(), "merging state into a dead basicblock"); + } + + if (_stack_top == bb._stack_top) { + if (_monitor_top == bb._monitor_top) { + if (mergeStateVectors(_state, bb._state)) { + bb.setChanged(true); + } + } else { + if (TraceMonitorMismatch) { + reportMonitorMismatch("monitor stack height merge conflict"); + } + // When the monitor stacks are not matched, we set _monitor_top to + // bad_monitors. This signals that, from here on, the monitor stack cannot + // be trusted. In particular, monitorexit bytecodes may throw + // exceptions. We mark this block as changed so that the change + // propagates properly. + bb._monitor_top = bad_monitors; + bb.setChanged(true); + _monitor_safe = false; + } + } else if (!bb.isReachable()) { + // First time we look at this BB + copyState(bb._state, _state); + bb._stack_top = _stack_top; + bb._monitor_top = _monitor_top; + bb.setChanged(true); + } else { + throw new RuntimeException("stack height conflict: " + + _stack_top + " vs. " + bb._stack_top); + } + } + + void mergeState (int bci, int[] data) { + mergeStateIntoBB(getBasicBlockAt(bci)); + } + + void setVar (int localNo, CellTypeState cts) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(cts.isReference() || cts.isValue() || cts.isAddress(), + "wrong celltypestate"); + } + if (localNo < 0 || localNo > _max_locals) { + throw new RuntimeException("variable write error: r" + localNo); + } + vars().get(localNo).set(cts); + } + + CellTypeState getVar (int localNo) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(localNo < _max_locals + _nof_refval_conflicts, "variable read error"); + } + if (localNo < 0 || localNo > _max_locals) { + throw new RuntimeException("variable read error: r" + localNo); + } + return vars().get(localNo).copy(); + } + + CellTypeState pop () { + if ( _stack_top <= 0) { + throw new RuntimeException("stack underflow"); + } + return stack().get(--_stack_top).copy(); + } + + void push (CellTypeState cts) { + if ( _stack_top >= _max_stack) { + if (DEBUG) { + System.err.println("Method: " + method().getName().asString() + method().getSignature().asString() + + " _stack_top: " + _stack_top + " _max_stack: " + _max_stack); + } + throw new RuntimeException("stack overflow"); + } + stack().get(_stack_top++).set(cts); + if (DEBUG) { + System.err.println("After push: _stack_top: " + _stack_top + + " _max_stack: " + _max_stack + + " just pushed: " + cts.toChar()); + } + } + + CellTypeState monitorPop () { + if (Assert.ASSERTS_ENABLED) { + Assert.that(_monitor_top != bad_monitors, "monitorPop called on error monitor stack"); + } + if (_monitor_top == 0) { + // We have detected a pop of an empty monitor stack. + _monitor_safe = false; + _monitor_top = bad_monitors; + + if (TraceMonitorMismatch) { + reportMonitorMismatch("monitor stack underflow"); + } + return CellTypeState.ref; // just to keep the analysis going. + } + return monitors().get(--_monitor_top).copy(); + } + + void monitorPush (CellTypeState cts) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(_monitor_top != bad_monitors, "monitorPush called on error monitor stack"); + } + if (_monitor_top >= _max_monitors) { + // Some monitorenter is being executed more than once. + // This means that the monitor stack cannot be simulated. + _monitor_safe = false; + _monitor_top = bad_monitors; + + if (TraceMonitorMismatch) { + reportMonitorMismatch("monitor stack overflow"); + } + return; + } + monitors().get(_monitor_top++).set(cts); + } + + CellTypeStateList vars () { return _state; } + CellTypeStateList stack () { return _state.subList(_max_locals, _state.size()); } + CellTypeStateList monitors() { return _state.subList(_max_locals+_max_stack, _state.size()); } + + void replaceAllCTSMatches (CellTypeState match, + CellTypeState replace) { + int i; + int len = _max_locals + _stack_top; + boolean change = false; + + for (i = len - 1; i >= 0; i--) { + if (match.equal(_state.get(i))) { + _state.get(i).set(replace); + } + } + + if (_monitor_top > 0) { + int base = _max_locals + _max_stack; + len = base + _monitor_top; + for (i = len - 1; i >= base; i--) { + if (match.equal(_state.get(i))) { + _state.get(i).set(replace); + } + } + } + } + + void printStates (PrintStream tty, CellTypeStateList vector, int num) { + for (int i = 0; i < num; i++) { + vector.get(i).print(tty); + } + } + + void printCurrentState (PrintStream tty, + BytecodeStream currentBC, + boolean detailed) { + if (detailed) { + tty.print(" " + currentBC.bci() + " vars = "); + printStates(tty, vars(), _max_locals); + tty.print(" " + Bytecodes.name(currentBC.code())); + switch(currentBC.code()) { + case Bytecodes._invokevirtual: + case Bytecodes._invokespecial: + case Bytecodes._invokestatic: + case Bytecodes._invokeinterface: + // FIXME: print signature of referenced method (need more + // accessors in ConstantPool and ConstantPoolCache) + int idx = currentBC.getIndexBig(); + tty.print(" idx " + idx); + /* + int idx = currentBC.getIndexBig(); + ConstantPool cp = method().getConstants(); + int nameAndTypeIdx = cp.name_and_type_ref_index_at(idx); + int signatureIdx = cp.signature_ref_index_at(nameAndTypeIdx); + symbolOop signature = cp.symbol_at(signatureIdx); + tty.print("%s", signature.as_C_string()); + */ + } + tty.println(); + tty.print(" stack = "); + printStates(tty, stack(), _stack_top); + tty.println(); + if (_monitor_top != bad_monitors) { + tty.print(" monitors = "); + printStates(tty, monitors(), _monitor_top); + } else { + tty.print(" [bad monitor stack]"); + } + tty.println(); + } else { + tty.print(" " + currentBC.bci() + " vars = '" + + stateVecToString(vars(), _max_locals) + "' "); + tty.print(" stack = '" + stateVecToString(stack(), _stack_top) + "' "); + if (_monitor_top != bad_monitors) { + tty.print(" monitors = '" + stateVecToString(monitors(), _monitor_top) + "' \t" + + Bytecodes.name(currentBC.code())); + } else { + tty.print(" [bad monitor stack]"); + } + switch(currentBC.code()) { + case Bytecodes._invokevirtual: + case Bytecodes._invokespecial: + case Bytecodes._invokestatic: + case Bytecodes._invokeinterface: + // FIXME: print signature of referenced method (need more + // accessors in ConstantPool and ConstantPoolCache) + int idx = currentBC.getIndexBig(); + tty.print(" idx " + idx); + /* + int idx = currentBC.getIndexBig(); + constantPoolOop cp = method().constants(); + int nameAndTypeIdx = cp.name_and_type_ref_index_at(idx); + int signatureIdx = cp.signature_ref_index_at(nameAndTypeIdx); + symbolOop signature = cp.symbol_at(signatureIdx); + tty.print("%s", signature.as_C_string()); + */ + } + tty.println(); + } + } + + void reportMonitorMismatch (String msg) { + if (Assert.ASSERTS_ENABLED) { + System.err.print(" Monitor mismatch in method "); + method().printValueOn(System.err); + System.err.println(": " + msg); + } + } + + // Basicblock info + BasicBlock[] _basic_blocks; // Array of basicblock info + int _gc_points; + int _bb_count; + BitMap _bb_hdr_bits; + + // Basicblocks methods + void initializeBB () { + _gc_points = 0; + _bb_count = 0; + _bb_hdr_bits = new BitMap((int) _method.getCodeSize()); + } + + void markBBHeadersAndCountGCPoints() { + initializeBB(); + + boolean fellThrough = false; // False to get first BB marked. + + // First mark all exception handlers as start of a basic-block + TypeArray excps = method().getExceptionTable(); + for(int i = 0; i < excps.getLength(); i += 4) { + int handler_pc_idx = i+2; + markBB(excps.getIntAt(handler_pc_idx), null); + } + + // Then iterate through the code + BytecodeStream bcs = new BytecodeStream(_method); + int bytecode; + + while( (bytecode = bcs.next()) >= 0) { + int bci = bcs.bci(); + + if (!fellThrough) + markBB(bci, null); + + fellThrough = jumpTargetsDo(bcs, + new JumpClosure() { + public void process(GenerateOopMap c, int bcpDelta, int[] data) { + c.markBB(bcpDelta, data); + } + }, + null); + + /* We will also mark successors of jsr's as basic block headers. */ + switch (bytecode) { + case Bytecodes._jsr: + if (Assert.ASSERTS_ENABLED) { + Assert.that(!fellThrough, "should not happen"); + } + markBB(bci + Bytecodes.lengthFor(bytecode), null); + break; + case Bytecodes._jsr_w: + if (Assert.ASSERTS_ENABLED) { + Assert.that(!fellThrough, "should not happen"); + } + markBB(bci + Bytecodes.lengthFor(bytecode), null); + break; + } + + if (possibleGCPoint(bcs)) + _gc_points++; + } + } + + boolean isBBHeader (int bci) { + return _bb_hdr_bits.at(bci); + } + + int gcPoints () { + return _gc_points; + } + + int bbCount () { + return _bb_count; + } + + void setBBMarkBit (int bci) { + _bb_hdr_bits.atPut(bci, true); + } + + void clear_bbmark_bit (int bci) { + _bb_hdr_bits.atPut(bci, false); + } + + BasicBlock getBasicBlockAt (int bci) { + BasicBlock bb = getBasicBlockContaining(bci); + if (Assert.ASSERTS_ENABLED) { + Assert.that(bb._bci == bci, "should have found BB"); + } + return bb; + } + + BasicBlock getBasicBlockContaining (int bci) { + BasicBlock[] bbs = _basic_blocks; + int lo = 0, hi = _bb_count - 1; + + while (lo <= hi) { + int m = (lo + hi) / 2; + int mbci = bbs[m]._bci; + int nbci; + + if ( m == _bb_count-1) { + if (Assert.ASSERTS_ENABLED) { + Assert.that( bci >= mbci && bci < method().getCodeSize(), "sanity check failed"); + } + return bbs[m]; + } else { + nbci = bbs[m+1]._bci; + } + + if ( mbci <= bci && bci < nbci) { + return bbs[m]; + } else if (mbci < bci) { + lo = m + 1; + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(mbci > bci, "sanity check"); + } + hi = m - 1; + } + } + + throw new RuntimeException("should have found BB"); + } + + void interpBB (BasicBlock bb) { + // We do not want to do anything in case the basic-block has not been initialized. This + // will happen in the case where there is dead-code hang around in a method. + if (Assert.ASSERTS_ENABLED) { + Assert.that(bb.isReachable(), "should be reachable or deadcode exist"); + } + restoreState(bb); + + BytecodeStream itr = new BytecodeStream(_method); + + // Set iterator interval to be the current basicblock + int lim_bci = nextBBStartPC(bb); + itr.setInterval(bb._bci, lim_bci); + + if (DEBUG) { + System.err.println("interpBB: method = " + method().getName().asString() + + method().getSignature().asString() + + ", BCI interval [" + bb._bci + ", " + lim_bci + ")"); + { + System.err.print("Bytecodes:"); + for (int i = bb._bci; i < lim_bci; i++) { + System.err.print(" 0x" + Long.toHexString(method().getBytecodeOrBPAt(i))); + } + System.err.println(); + } + } + + if (Assert.ASSERTS_ENABLED) { + Assert.that(lim_bci != bb._bci, "must be at least one instruction in a basicblock"); + } + itr.next(); // read first instruction + + // Iterates through all bytecodes except the last in a basic block. + // We handle the last one special, since there is controlflow change. + while(itr.nextBCI() < lim_bci && !_got_error) { + if (_has_exceptions || (_monitor_top != 0)) { + // We do not need to interpret the results of exceptional + // continuation from this instruction when the method has no + // exception handlers and the monitor stack is currently + // empty. + doExceptionEdge(itr); + } + interp1(itr); + itr.next(); + } + + // Handle last instruction. + if (!_got_error) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(itr.nextBCI() == lim_bci, "must point to end"); + } + if (_has_exceptions || (_monitor_top != 0)) { + doExceptionEdge(itr); + } + interp1(itr); + + boolean fall_through = jumpTargetsDo(itr, new JumpClosure() { + public void process(GenerateOopMap c, int bcpDelta, int[] data) { + c.mergeState(bcpDelta, data); + } + }, null); + if (_got_error) return; + + if (itr.code() == Bytecodes._ret) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!fall_through, "cannot be set if ret instruction"); + } + // Automatically handles 'wide' ret indicies + retJumpTargetsDo(itr, new JumpClosure() { + public void process(GenerateOopMap c, int bcpDelta, int[] data) { + c.mergeState(bcpDelta, data); + } + }, itr.getIndex(), null); + } else if (fall_through) { + // Hit end of BB, but the instr. was a fall-through instruction, + // so perform transition as if the BB ended in a "jump". + if (Assert.ASSERTS_ENABLED) { + Assert.that(lim_bci == _basic_blocks[bbIndex(bb) + 1]._bci, "there must be another bb"); + } + mergeStateIntoBB(_basic_blocks[bbIndex(bb) + 1]); + } + } + } + + void restoreState (BasicBlock bb) { + for (int i = 0; i < _state_len; i++) { + _state.get(i).set(bb._state.get(i)); + } + _stack_top = bb._stack_top; + _monitor_top = bb._monitor_top; + } + + int nextBBStartPC (BasicBlock bb) { + int bbNum = bbIndex(bb) + 1; + if (bbNum == _bb_count) + return (int) method().getCodeSize(); + + return _basic_blocks[bbNum]._bci; + } + + void updateBasicBlocks (int bci, int delta) { + BitMap bbBits = new BitMap((int) (_method.getCodeSize() + delta)); + for(int k = 0; k < _bb_count; k++) { + if (_basic_blocks[k]._bci > bci) { + _basic_blocks[k]._bci += delta; + _basic_blocks[k]._end_bci += delta; + } + bbBits.atPut(_basic_blocks[k]._bci, true); + } + _bb_hdr_bits = bbBits; + } + + void markBB(int bci, int[] data) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(bci>= 0 && bci < method().getCodeSize(), "index out of bounds"); + } + if (isBBHeader(bci)) + return; + + // FIXME: remove + // if (TraceNewOopMapGeneration) { + // tty.print_cr("Basicblock#%d begins at: %d", c._bb_count, bci); + // } + setBBMarkBit(bci); + _bb_count++; + } + + // Dead code detection + void markReachableCode() { + final int[] change = new int[1]; + change[0] = 1; + + // Mark entry basic block as alive and all exception handlers + _basic_blocks[0].markAsAlive(); + TypeArray excps = method().getExceptionTable(); + for(int i = 0; i < excps.getLength(); i += 4) { + int handler_pc_idx = i+2; + BasicBlock bb = getBasicBlockAt(excps.getIntAt(handler_pc_idx)); + // If block is not already alive (due to multiple exception handlers to same bb), then + // make it alive + if (bb.isDead()) + bb.markAsAlive(); + } + + BytecodeStream bcs = new BytecodeStream(_method); + + // Iterate through all basic blocks until we reach a fixpoint + while (change[0] != 0) { + change[0] = 0; + + for (int i = 0; i < _bb_count; i++) { + BasicBlock bb = _basic_blocks[i]; + if (bb.isAlive()) { + // Position bytecodestream at last bytecode in basicblock + bcs.setStart(bb._end_bci); + bcs.next(); + int bytecode = bcs.code(); + int bci = bcs.bci(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(bci == bb._end_bci, "wrong bci"); + } + + boolean fell_through = jumpTargetsDo(bcs, new JumpClosure() { + public void process(GenerateOopMap c, int bciDelta, int[] change) { + c.reachableBasicblock(bciDelta, change); + } + }, change); + + // We will also mark successors of jsr's as alive. + switch (bytecode) { + case Bytecodes._jsr: + case Bytecodes._jsr_w: + if (Assert.ASSERTS_ENABLED) { + Assert.that(!fell_through, "should not happen"); + } + reachableBasicblock(bci + Bytecodes.lengthFor(bytecode), change); + break; + } + if (fell_through) { + // Mark successor as alive + if (_basic_blocks[i+1].isDead()) { + _basic_blocks[i+1].markAsAlive(); + change[0] = 1; + } + } + } + } + } + } + + void reachableBasicblock (int bci, int[] data) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(bci>= 0 && bci < method().getCodeSize(), "index out of bounds"); + } + BasicBlock bb = getBasicBlockAt(bci); + if (bb.isDead()) { + bb.markAsAlive(); + data[0] = 1; // Mark basicblock as changed + } + } + + // Interpretation methods (primary) + void doInterpretation () { + // "i" is just for debugging, so we can detect cases where this loop is + // iterated more than once. + int i = 0; + do { + // FIXME: remove + // if (TraceNewOopMapGeneration) { + // tty.print("\n\nIteration #%d of do_interpretation loop, method:\n", i); + // method().print_name(tty); + // tty.print("\n\n"); + // } + _conflict = false; + _monitor_safe = true; + // init_state is now called from init_basic_blocks. The length of a + // state vector cannot be determined until we have made a pass through + // the bytecodes counting the possible monitor entries. + if (!_got_error) initBasicBlocks(); + if (!_got_error) setupMethodEntryState(); + if (!_got_error) interpAll(); + if (!_got_error) rewriteRefvalConflicts(); + i++; + } while (_conflict && !_got_error); + } + + void initBasicBlocks () { + // Note: Could consider reserving only the needed space for each BB's state + // (entry stack may not be of maximal height for every basic block). + // But cumbersome since we don't know the stack heights yet. (Nor the + // monitor stack heights...) + + _basic_blocks = new BasicBlock[_bb_count]; + for (int i = 0; i < _bb_count; i++) { + _basic_blocks[i] = new BasicBlock(); + } + + // Make a pass through the bytecodes. Count the number of monitorenters. + // This can be used an upper bound on the monitor stack depth in programs + // which obey stack discipline with their monitor usage. Initialize the + // known information about basic blocks. + BytecodeStream j = new BytecodeStream(_method); + int bytecode; + + int bbNo = 0; + int monitor_count = 0; + int prev_bci = -1; + while( (bytecode = j.next()) >= 0) { + if (j.code() == Bytecodes._monitorenter) { + monitor_count++; + } + + int bci = j.bci(); + if (isBBHeader(bci)) { + // Initialize the basicblock structure + BasicBlock bb = _basic_blocks[bbNo]; + bb._bci = bci; + bb._max_locals = _max_locals; + bb._max_stack = _max_stack; + bb.setChanged(false); + bb._stack_top = BasicBlock._dead_basic_block; // Initialize all basicblocks are dead. + bb._monitor_top = bad_monitors; + + if (bbNo > 0) { + _basic_blocks[bbNo - 1]._end_bci = prev_bci; + } + + bbNo++; + } + // Remember prevous bci. + prev_bci = bci; + } + // Set + _basic_blocks[bbNo-1]._end_bci = prev_bci; + + _max_monitors = monitor_count; + + // Now that we have a bound on the depth of the monitor stack, we can + // initialize the CellTypeState-related information. + initState(); + + // We allocate space for all state-vectors for all basicblocks in one huge chuck. + // Then in the next part of the code, we set a pointer in each _basic_block that + // points to each piece. + CellTypeStateList basicBlockState = new CellTypeStateList(bbNo * _state_len); + + // Make a pass over the basicblocks and assign their state vectors. + for (int blockNum=0; blockNum < bbNo; blockNum++) { + BasicBlock bb = _basic_blocks[blockNum]; + bb._state = basicBlockState.subList(blockNum * _state_len, (blockNum + 1) * _state_len); + + if (Assert.ASSERTS_ENABLED) { + if (blockNum + 1 < bbNo) { + int bc_len = Bytecodes.javaLengthAt(_method, bb._end_bci); + Assert.that(bb._end_bci + bc_len == _basic_blocks[blockNum + 1]._bci, + "unmatched bci info in basicblock"); + } + } + } + if (Assert.ASSERTS_ENABLED) { + BasicBlock bb = _basic_blocks[bbNo-1]; + int bc_len = Bytecodes.javaLengthAt(_method, bb._end_bci); + Assert.that(bb._end_bci + bc_len == _method.getCodeSize(), "wrong end bci"); + } + + // Check that the correct number of basicblocks was found + if (bbNo !=_bb_count) { + if (bbNo < _bb_count) { + throw new RuntimeException("jump into the middle of instruction?"); + } else { + throw new RuntimeException("extra basic blocks - should not happen?"); + } + } + + // Mark all alive blocks + markReachableCode(); + } + + void setupMethodEntryState () { + // Initialize all locals to 'uninit' and set stack-height to 0 + makeContextUninitialized(); + + // Initialize CellState type of arguments + methodsigToEffect(method().getSignature(), method().isStatic(), vars()); + + // If some references must be pre-assigned to null, then set that up + initializeVars(); + + // This is the start state + mergeStateIntoBB(_basic_blocks[0]); + + if (Assert.ASSERTS_ENABLED) { + Assert.that(_basic_blocks[0].changed(), "we are not getting off the ground"); + } + } + + void interpAll () { + boolean change = true; + + while (change && !_got_error) { + change = false; + for (int i = 0; i < _bb_count && !_got_error; i++) { + BasicBlock bb = _basic_blocks[i]; + if (bb.changed()) { + if (_got_error) return; + change = true; + bb.setChanged(false); + interpBB(bb); + } + } + } + } + + // + // Interpretation methods (secondary) + // + + /** Sets the current state to be the state after executing the + current instruction, starting in the current state. */ + void interp1 (BytecodeStream itr) { + if (DEBUG) { + System.err.println(" - bci " + itr.bci()); + } + + // if (TraceNewOopMapGeneration) { + // print_current_state(tty, itr, TraceNewOopMapGenerationDetailed); + // } + + // Should we report the results? Result is reported *before* the + // instruction at the current bci is executed. However, not for + // calls. For calls we do not want to include the arguments, so we + // postpone the reporting until they have been popped (in method + // ppl). + if (_report_result == true) { + switch(itr.code()) { + case Bytecodes._invokevirtual: + case Bytecodes._invokespecial: + case Bytecodes._invokestatic: + case Bytecodes._invokeinterface: + _itr_send = itr; + _report_result_for_send = true; + break; + default: + fillStackmapForOpcodes(itr, vars(), stack(), _stack_top); + break; + } + } + + // abstract interpretation of current opcode + switch(itr.code()) { + case Bytecodes._nop: break; + case Bytecodes._goto: break; + case Bytecodes._goto_w: break; + case Bytecodes._iinc: break; + case Bytecodes._return: doReturnMonitorCheck(); + break; + + case Bytecodes._aconst_null: + case Bytecodes._new: ppush1(CellTypeState.makeLineRef(itr.bci())); + break; + + case Bytecodes._iconst_m1: + case Bytecodes._iconst_0: + case Bytecodes._iconst_1: + case Bytecodes._iconst_2: + case Bytecodes._iconst_3: + case Bytecodes._iconst_4: + case Bytecodes._iconst_5: + case Bytecodes._fconst_0: + case Bytecodes._fconst_1: + case Bytecodes._fconst_2: + case Bytecodes._bipush: + case Bytecodes._sipush: ppush1(valCTS); break; + + case Bytecodes._lconst_0: + case Bytecodes._lconst_1: + case Bytecodes._dconst_0: + case Bytecodes._dconst_1: ppush(vvCTS); break; + + case Bytecodes._ldc2_w: ppush(vvCTS); break; + + case Bytecodes._ldc: doLdc(itr.getIndex(), itr.bci()); break; + case Bytecodes._ldc_w: doLdc(itr.getIndexBig(), itr.bci());break; + + case Bytecodes._iload: + case Bytecodes._fload: ppload(vCTS, itr.getIndex()); break; + + case Bytecodes._lload: + case Bytecodes._dload: ppload(vvCTS,itr.getIndex()); break; + + case Bytecodes._aload: ppload(rCTS, itr.getIndex()); break; + + case Bytecodes._iload_0: + case Bytecodes._fload_0: ppload(vCTS, 0); break; + case Bytecodes._iload_1: + case Bytecodes._fload_1: ppload(vCTS, 1); break; + case Bytecodes._iload_2: + case Bytecodes._fload_2: ppload(vCTS, 2); break; + case Bytecodes._iload_3: + case Bytecodes._fload_3: ppload(vCTS, 3); break; + + case Bytecodes._lload_0: + case Bytecodes._dload_0: ppload(vvCTS, 0); break; + case Bytecodes._lload_1: + case Bytecodes._dload_1: ppload(vvCTS, 1); break; + case Bytecodes._lload_2: + case Bytecodes._dload_2: ppload(vvCTS, 2); break; + case Bytecodes._lload_3: + case Bytecodes._dload_3: ppload(vvCTS, 3); break; + + case Bytecodes._aload_0: ppload(rCTS, 0); break; + case Bytecodes._aload_1: ppload(rCTS, 1); break; + case Bytecodes._aload_2: ppload(rCTS, 2); break; + case Bytecodes._aload_3: ppload(rCTS, 3); break; + + case Bytecodes._iaload: + case Bytecodes._faload: + case Bytecodes._baload: + case Bytecodes._caload: + case Bytecodes._saload: pp(vrCTS, vCTS); break; + + case Bytecodes._laload: pp(vrCTS, vvCTS); break; + case Bytecodes._daload: pp(vrCTS, vvCTS); break; + + case Bytecodes._aaload: ppNewRef(vrCTS, itr.bci()); break; + + case Bytecodes._istore: + case Bytecodes._fstore: ppstore(vCTS, itr.getIndex()); break; + + case Bytecodes._lstore: + case Bytecodes._dstore: ppstore(vvCTS, itr.getIndex()); break; + + case Bytecodes._astore: doAstore(itr.getIndex()); break; + + case Bytecodes._istore_0: + case Bytecodes._fstore_0: ppstore(vCTS, 0); break; + case Bytecodes._istore_1: + case Bytecodes._fstore_1: ppstore(vCTS, 1); break; + case Bytecodes._istore_2: + case Bytecodes._fstore_2: ppstore(vCTS, 2); break; + case Bytecodes._istore_3: + case Bytecodes._fstore_3: ppstore(vCTS, 3); break; + + case Bytecodes._lstore_0: + case Bytecodes._dstore_0: ppstore(vvCTS, 0); break; + case Bytecodes._lstore_1: + case Bytecodes._dstore_1: ppstore(vvCTS, 1); break; + case Bytecodes._lstore_2: + case Bytecodes._dstore_2: ppstore(vvCTS, 2); break; + case Bytecodes._lstore_3: + case Bytecodes._dstore_3: ppstore(vvCTS, 3); break; + + case Bytecodes._astore_0: doAstore(0); break; + case Bytecodes._astore_1: doAstore(1); break; + case Bytecodes._astore_2: doAstore(2); break; + case Bytecodes._astore_3: doAstore(3); break; + + case Bytecodes._iastore: + case Bytecodes._fastore: + case Bytecodes._bastore: + case Bytecodes._castore: + case Bytecodes._sastore: ppop(vvrCTS); break; + case Bytecodes._lastore: + case Bytecodes._dastore: ppop(vvvrCTS); break; + case Bytecodes._aastore: ppop(rvrCTS); break; + + case Bytecodes._pop: ppopAny(1); break; + case Bytecodes._pop2: ppopAny(2); break; + + case Bytecodes._dup: ppdupswap(1, "11"); break; + case Bytecodes._dup_x1: ppdupswap(2, "121"); break; + case Bytecodes._dup_x2: ppdupswap(3, "1321"); break; + case Bytecodes._dup2: ppdupswap(2, "2121"); break; + case Bytecodes._dup2_x1: ppdupswap(3, "21321"); break; + case Bytecodes._dup2_x2: ppdupswap(4, "214321"); break; + case Bytecodes._swap: ppdupswap(2, "12"); break; + + case Bytecodes._iadd: + case Bytecodes._fadd: + case Bytecodes._isub: + case Bytecodes._fsub: + case Bytecodes._imul: + case Bytecodes._fmul: + case Bytecodes._idiv: + case Bytecodes._fdiv: + case Bytecodes._irem: + case Bytecodes._frem: + case Bytecodes._ishl: + case Bytecodes._ishr: + case Bytecodes._iushr: + case Bytecodes._iand: + case Bytecodes._ior: + case Bytecodes._ixor: + case Bytecodes._l2f: + case Bytecodes._l2i: + case Bytecodes._d2f: + case Bytecodes._d2i: + case Bytecodes._fcmpl: + case Bytecodes._fcmpg: pp(vvCTS, vCTS); break; + + case Bytecodes._ladd: + case Bytecodes._dadd: + case Bytecodes._lsub: + case Bytecodes._dsub: + case Bytecodes._lmul: + case Bytecodes._dmul: + case Bytecodes._ldiv: + case Bytecodes._ddiv: + case Bytecodes._lrem: + case Bytecodes._drem: + case Bytecodes._land: + case Bytecodes._lor: + case Bytecodes._lxor: pp(vvvvCTS, vvCTS); break; + + case Bytecodes._ineg: + case Bytecodes._fneg: + case Bytecodes._i2f: + case Bytecodes._f2i: + case Bytecodes._i2c: + case Bytecodes._i2s: + case Bytecodes._i2b: pp(vCTS, vCTS); break; + + case Bytecodes._lneg: + case Bytecodes._dneg: + case Bytecodes._l2d: + case Bytecodes._d2l: pp(vvCTS, vvCTS); break; + + case Bytecodes._lshl: + case Bytecodes._lshr: + case Bytecodes._lushr: pp(vvvCTS, vvCTS); break; + + case Bytecodes._i2l: + case Bytecodes._i2d: + case Bytecodes._f2l: + case Bytecodes._f2d: pp(vCTS, vvCTS); break; + + case Bytecodes._lcmp: pp(vvvvCTS, vCTS); break; + case Bytecodes._dcmpl: + case Bytecodes._dcmpg: pp(vvvvCTS, vCTS); break; + + case Bytecodes._ifeq: + case Bytecodes._ifne: + case Bytecodes._iflt: + case Bytecodes._ifge: + case Bytecodes._ifgt: + case Bytecodes._ifle: + case Bytecodes._tableswitch: ppop1(valCTS); + break; + case Bytecodes._ireturn: + case Bytecodes._freturn: doReturnMonitorCheck(); + ppop1(valCTS); + break; + case Bytecodes._if_icmpeq: + case Bytecodes._if_icmpne: + case Bytecodes._if_icmplt: + case Bytecodes._if_icmpge: + case Bytecodes._if_icmpgt: + case Bytecodes._if_icmple: ppop(vvCTS); + break; + + case Bytecodes._lreturn: doReturnMonitorCheck(); + ppop(vvCTS); + break; + + case Bytecodes._dreturn: doReturnMonitorCheck(); + ppop(vvCTS); + break; + + case Bytecodes._if_acmpeq: + case Bytecodes._if_acmpne: ppop(rrCTS); break; + + case Bytecodes._jsr: doJsr(itr.dest()); break; + case Bytecodes._jsr_w: doJsr(itr.dest_w()); break; + + case Bytecodes._getstatic: doField(true, true, + itr.getIndexBig(), + itr.bci()); break; + case Bytecodes._putstatic: doField(false, true, itr.getIndexBig(), itr.bci()); break; + case Bytecodes._getfield: doField(true, false, itr.getIndexBig(), itr.bci()); break; + case Bytecodes._putfield: doField(false, false, itr.getIndexBig(), itr.bci()); break; + + case Bytecodes._invokevirtual: + case Bytecodes._invokespecial: doMethod(false, false, itr.getIndexBig(), itr.bci()); break; + case Bytecodes._invokestatic: doMethod(true, false, itr.getIndexBig(), itr.bci()); break; + case Bytecodes._invokeinterface: doMethod(false, true, itr.getIndexBig(), itr.bci()); break; + case Bytecodes._newarray: + case Bytecodes._anewarray: ppNewRef(vCTS, itr.bci()); break; + case Bytecodes._checkcast: doCheckcast(); break; + case Bytecodes._arraylength: + case Bytecodes._instanceof: pp(rCTS, vCTS); break; + case Bytecodes._monitorenter: doMonitorenter(itr.bci()); break; + case Bytecodes._monitorexit: doMonitorexit(itr.bci()); break; + + case Bytecodes._athrow: // handled by do_exception_edge() BUT ... + // vlh(apple): doExceptionEdge() does not get + // called if method has no exception handlers + if ((!_has_exceptions) && (_monitor_top > 0)) { + _monitor_safe = false; + } + break; + + case Bytecodes._areturn: doReturnMonitorCheck(); + ppop1(refCTS); + break; + case Bytecodes._ifnull: + case Bytecodes._ifnonnull: ppop1(refCTS); break; + case Bytecodes._multianewarray: doMultianewarray(itr.codeAt(itr.bci() + 3), itr.bci()); break; + + case Bytecodes._wide: throw new RuntimeException("Iterator should skip this bytecode"); + case Bytecodes._ret: break; + + // Java opcodes + case Bytecodes._fast_aaccess_0: ppNewRef(rCTS, itr.bci()); break; // Pair bytecode for (aload_0, _fast_agetfield) + + case Bytecodes._fast_iaccess_0: ppush1(valCTS); break; // Pair bytecode for (aload_0, _fast_igetfield) + + case Bytecodes._fast_igetfield: pp(rCTS, vCTS); break; + + case Bytecodes._fast_agetfield: ppNewRef(rCTS, itr.bci()); break; + + case Bytecodes._fast_aload_0: ppload(rCTS, 0); break; + + case Bytecodes._lookupswitch: + case Bytecodes._fast_linearswitch: + case Bytecodes._fast_binaryswitch: ppop1(valCTS); break; + + default: + throw new RuntimeException("unexpected opcode: " + itr.code()); + } + } + + void doExceptionEdge (BytecodeStream itr) { + // Only check exception edge, if bytecode can trap + if (!Bytecodes.canTrap(itr.code())) return; + switch (itr.code()) { + case Bytecodes._aload_0: + case Bytecodes._fast_aload_0: + // These bytecodes can trap for rewriting. We need to assume that + // they do not throw exceptions to make the monitor analysis work. + return; + + case Bytecodes._ireturn: + case Bytecodes._lreturn: + case Bytecodes._freturn: + case Bytecodes._dreturn: + case Bytecodes._areturn: + case Bytecodes._return: + // If the monitor stack height is not zero when we leave the method, + // then we are either exiting with a non-empty stack or we have + // found monitor trouble earlier in our analysis. In either case, + // assume an exception could be taken here. + if (_monitor_top == 0) { + return; + } + break; + + case Bytecodes._monitorexit: + // If the monitor stack height is bad_monitors, then we have detected a + // monitor matching problem earlier in the analysis. If the + // monitor stack height is 0, we are about to pop a monitor + // off of an empty stack. In either case, the bytecode + // could throw an exception. + if (_monitor_top != bad_monitors && _monitor_top != 0) { + return; + } + break; + } + + if (_has_exceptions) { + int bci = itr.bci(); + TypeArray exct = method().getExceptionTable(); + for(int i = 0; i< exct.getLength(); i+=4) { + int start_pc = exct.getIntAt(i); + int end_pc = exct.getIntAt(i+1); + int handler_pc = exct.getIntAt(i+2); + int catch_type = exct.getIntAt(i+3); + + if (start_pc <= bci && bci < end_pc) { + BasicBlock excBB = getBasicBlockAt(handler_pc); + CellTypeStateList excStk = excBB.stack(); + CellTypeStateList cOpStck = stack(); + CellTypeState cOpStck_0 = cOpStck.get(0).copy(); + int cOpStackTop = _stack_top; + + // Exception stacks are always the same. + if (Assert.ASSERTS_ENABLED) { + Assert.that(method().getMaxStack() > 0, "sanity check"); + } + + // We remembered the size and first element of "cOpStck" + // above; now we temporarily set them to the appropriate + // values for an exception handler. + cOpStck.get(0).set(CellTypeState.makeSlotRef(_max_locals)); + _stack_top = 1; + + mergeStateIntoBB(excBB); + + // Now undo the temporary change. + cOpStck.get(0).set(cOpStck_0); + _stack_top = cOpStackTop; + + // If this is a "catch all" handler, then we do not need to + // consider any additional handlers. + if (catch_type == 0) { + return; + } + } + } + } + + // It is possible that none of the exception handlers would have caught + // the exception. In this case, we will exit the method. We must + // ensure that the monitor stack is empty in this case. + if (_monitor_top == 0) { + return; + } + + // We pessimistically assume that this exception can escape the + // method. (It is possible that it will always be caught, but + // we don't care to analyse the types of the catch clauses.) + + // We don't set _monitor_top to bad_monitors because there are no successors + // to this exceptional exit. + + if (TraceMonitorMismatch && _monitor_safe) { + // We check _monitor_safe so that we only report the first mismatched + // exceptional exit. + reportMonitorMismatch("non-empty monitor stack at exceptional exit"); + } + _monitor_safe = false; + } + + void checkType (CellTypeState expected, CellTypeState actual) { + if (!expected.equalKind(actual)) { + throw new RuntimeException("wrong type on stack (found: " + + actual.toChar() + " expected: " + + expected.toChar() + ")"); + } + } + + void ppstore (CellTypeState[] in, int loc_no) { + for (int i = 0; i < in.length && !in[i].equal(CellTypeState.bottom); i++) { + CellTypeState expected = in[i]; + CellTypeState actual = pop(); + checkType(expected, actual); + if (Assert.ASSERTS_ENABLED) { + Assert.that(loc_no >= 0, "sanity check"); + } + setVar(loc_no++, actual); + } + } + + void ppload (CellTypeState[] out, int loc_no) { + for (int i = 0; i < out.length && !out[i].equal(CellTypeState.bottom); i++) { + CellTypeState out1 = out[i]; + CellTypeState vcts = getVar(loc_no); + if (Assert.ASSERTS_ENABLED) { + Assert.that(out1.canBeReference() || out1.canBeValue(), + "can only load refs. and values."); + } + if (out1.isReference()) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(loc_no>=0, "sanity check"); + } + if (!vcts.isReference()) { + // We were asked to push a reference, but the type of the + // variable can be something else + _conflict = true; + if (vcts.canBeUninit()) { + // It is a ref-uninit conflict (at least). If there are other + // problems, we'll get them in the next round + addToRefInitSet(loc_no); + vcts = out1; + } else { + // It wasn't a ref-uninit conflict. So must be a + // ref-val or ref-pc conflict. Split the variable. + recordRefvalConflict(loc_no); + vcts = out1; + } + push(out1); // recover... + } else { + push(vcts); // preserve reference. + } + // Otherwise it is a conflict, but one that verification would + // have caught if illegal. In particular, it can't be a topCTS + // resulting from mergeing two difference pcCTS's since the verifier + // would have rejected any use of such a merge. + } else { + push(out1); // handle val/init conflict + } + loc_no++; + } + } + + void ppush1 (CellTypeState in) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(in.isReference() | in.isValue(), "sanity check"); + } + if (DEBUG) { + System.err.println(" - pushing " + in.toChar()); + } + push(in); + } + + void ppush (CellTypeState[] in) { + for (int i = 0; i < in.length && !in[i].equal(CellTypeState.bottom); i++) { + ppush1(in[i]); + } + } + + void ppush (CellTypeStateList in) { + for (int i = 0; i < in.size() && !in.get(i).equal(CellTypeState.bottom); i++) { + ppush1(in.get(i)); + } + } + + void ppop1 (CellTypeState out) { + CellTypeState actual = pop(); + if (DEBUG) { + System.err.println(" - popping " + actual.toChar() + ", expecting " + out.toChar()); + } + checkType(out, actual); + } + + void ppop (CellTypeState[] out) { + for (int i = 0; i < out.length && !out[i].equal(CellTypeState.bottom); i++) { + ppop1(out[i]); + } + } + + void ppopAny (int poplen) { + if (_stack_top >= poplen) { + _stack_top -= poplen; + } else { + throw new RuntimeException("stack underflow"); + } + } + + void pp (CellTypeState[] in, CellTypeState[] out) { + ppop(in); + ppush(out); + } + + void ppNewRef (CellTypeState[] in, int bci) { + ppop(in); + ppush1(CellTypeState.makeLineRef(bci)); + } + + void ppdupswap (int poplen, String out) { + CellTypeState[] actual = new CellTypeState[5]; + Assert.that(poplen < 5, "this must be less than length of actual vector"); + + // pop all arguments + for(int i = 0; i < poplen; i++) actual[i] = pop(); + + // put them back + for (int i = 0; i < out.length(); i++) { + char push_ch = out.charAt(i); + int idx = push_ch - '1'; + if (Assert.ASSERTS_ENABLED) { + Assert.that(idx >= 0 && idx < poplen, "wrong arguments"); + } + push(actual[idx]); + } + } + + void doLdc (int idx, int bci) { + ConstantPool cp = method().getConstants(); + ConstantTag tag = cp.getTagAt(idx); + CellTypeState cts = (tag.isString() || tag.isUnresolvedString() || + tag.isKlass() || tag.isUnresolvedKlass()) + ? CellTypeState.makeLineRef(bci) + : valCTS; + ppush1(cts); + } + + void doAstore (int idx) { + CellTypeState r_or_p = pop(); + if (!r_or_p.isAddress() && !r_or_p.isReference()) { + // We actually expected ref or pc, but we only report that we expected a ref. It does not + // really matter (at least for now) + throw new RuntimeException("wrong type on stack (found: " + + r_or_p.toChar() + ", expected: {pr})"); + } + setVar(idx, r_or_p); + } + + void doJsr (int targBCI) { + push(CellTypeState.makeAddr(targBCI)); + } + + void doField (boolean is_get, boolean is_static, int idx, int bci) { + // Dig up signature for field in constant pool + ConstantPool cp = method().getConstants(); + int nameAndTypeIdx = cp.getNameAndTypeRefIndexAt(idx); + int signatureIdx = cp.getSignatureRefIndexAt(nameAndTypeIdx); + Symbol signature = cp.getSymbolAt(signatureIdx); + + if (DEBUG) { + System.err.println("doField: signature = " + signature.asString() + ", idx = " + idx + + ", nameAndTypeIdx = " + nameAndTypeIdx + ", signatureIdx = " + signatureIdx + ", bci = " + bci); + } + + // Parse signature (espcially simple for fields) + // The signature is UFT8 encoded, but the first char is always ASCII for signatures. + char sigch = (char) signature.getByteAt(0); + CellTypeState[] temp = new CellTypeState[4]; + CellTypeState[] eff = sigcharToEffect(sigch, bci, temp); + + CellTypeState[] in = new CellTypeState[4]; + CellTypeState[] out; + int i = 0; + + if (is_get) { + out = eff; + } else { + out = epsilonCTS; + i = copyCTS(in, eff); + } + if (!is_static) in[i++] = CellTypeState.ref; + in[i] = CellTypeState.bottom; + if (Assert.ASSERTS_ENABLED) { + Assert.that(i<=3, "sanity check"); + } + pp(in, out); + } + + void doMethod (boolean is_static, boolean is_interface, int idx, int bci) { + // Dig up signature for field in constant pool + ConstantPool cp = _method.getConstants(); + int nameAndTypeIdx = cp.getNameAndTypeRefIndexAt(idx); + int signatureIdx = cp.getSignatureRefIndexAt(nameAndTypeIdx); + Symbol signature = cp.getSymbolAt(signatureIdx); + + if (DEBUG) { + System.err.println("doMethod: signature = " + signature.asString() + ", idx = " + idx + + ", nameAndTypeIdx = " + nameAndTypeIdx + ", signatureIdx = " + signatureIdx + + ", bci = " + bci); + } + + // Parse method signature + CellTypeStateList out = new CellTypeStateList(4); + CellTypeStateList in = new CellTypeStateList(MAXARGSIZE+1); // Includes result + ComputeCallStack cse = new ComputeCallStack(signature); + + // Compute return type + int res_length = cse.computeForReturntype(out); + + // Temporary hack. + if (out.get(0).equal(CellTypeState.ref) && out.get(1).equal(CellTypeState.bottom)) { + out.get(0).set(CellTypeState.makeLineRef(bci)); + } + + if (Assert.ASSERTS_ENABLED) { + Assert.that(res_length<=4, "max value should be vv"); + } + + // Compute arguments + int arg_length = cse.computeForParameters(is_static, in); + if (Assert.ASSERTS_ENABLED) { + Assert.that(arg_length<=MAXARGSIZE, "too many locals"); + } + + // Pop arguments + for (int i = arg_length - 1; i >= 0; i--) ppop1(in.get(i));// Do args in reverse order. + + // Report results + if (_report_result_for_send == true) { + fillStackmapForOpcodes(_itr_send, vars(), stack(), _stack_top); + _report_result_for_send = false; + } + + // Push return address + ppush(out); + } + + void doMultianewarray (int dims, int bci) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(dims >= 1, "sanity check"); + } + for(int i = dims -1; i >=0; i--) { + ppop1(valCTS); + } + ppush1(CellTypeState.makeLineRef(bci)); + } + + void doMonitorenter (int bci) { + CellTypeState actual = pop(); + if (_monitor_top == bad_monitors) { + return; + } + + // Bail out when we get repeated locks on an identical monitor. This case + // isn't too hard to handle and can be made to work if supporting nested + // redundant synchronized statements becomes a priority. + // + // See also "Note" in do_monitorexit(), below. + if (actual.isLockReference()) { + _monitor_top = bad_monitors; + _monitor_safe = false; + + if (TraceMonitorMismatch) { + reportMonitorMismatch("nested redundant lock -- bailout..."); + } + return; + } + + CellTypeState lock = CellTypeState.makeLockRef(bci); + checkType(refCTS, actual); + if (!actual.isInfoTop()) { + replaceAllCTSMatches(actual, lock); + monitorPush(lock); + } + } + + void doMonitorexit (int bci) { + CellTypeState actual = pop(); + if (_monitor_top == bad_monitors) { + return; + } + checkType(refCTS, actual); + CellTypeState expected = monitorPop(); + if (!actual.isLockReference() || !expected.equal(actual)) { + // The monitor we are exiting is not verifiably the one + // on the top of our monitor stack. This causes a monitor + // mismatch. + _monitor_top = bad_monitors; + _monitor_safe = false; + + // We need to mark this basic block as changed so that + // this monitorexit will be visited again. We need to + // do this to ensure that we have accounted for the + // possibility that this bytecode will throw an + // exception. + BasicBlock bb = getBasicBlockContaining(bci); + bb.setChanged(true); + bb._monitor_top = bad_monitors; + + if (TraceMonitorMismatch) { + reportMonitorMismatch("improper monitor pair"); + } + } else { + // This code is a fix for the case where we have repeated + // locking of the same object in straightline code. We clear + // out the lock when it is popped from the monitor stack + // and replace it with an unobtrusive reference value that can + // be locked again. + // + // Note: when generateOopMap is fixed to properly handle repeated, + // nested, redundant locks on the same object, then this + // fix will need to be removed at that time. + replaceAllCTSMatches(actual, CellTypeState.makeLineRef(bci)); + } + + if (_report_for_exit_bci == bci) { + _matching_enter_bci = expected.getMonitorSource(); + } + } + + void doReturnMonitorCheck () { + if (_monitor_top > 0) { + // The monitor stack must be empty when we leave the method + // for the monitors to be properly matched. + _monitor_safe = false; + + // Since there are no successors to the *return bytecode, it + // isn't necessary to set _monitor_top to bad_monitors. + + if (TraceMonitorMismatch) { + reportMonitorMismatch("non-empty monitor stack at return"); + } + } + } + + void doCheckcast () { + CellTypeState actual = pop(); + checkType(refCTS, actual); + push(actual); + } + + CellTypeState[] sigcharToEffect (char sigch, int bci, CellTypeState[] out) { + // Object and array + if (sigch=='L' || sigch=='[') { + out[0] = CellTypeState.makeLineRef(bci); + out[1] = CellTypeState.bottom; + return out; + } + if (sigch == 'J' || sigch == 'D' ) return vvCTS; // Long and Double + if (sigch == 'V' ) return epsilonCTS; // Void + return vCTS; // Otherwise + } + + // Copies (optionally bottom/zero terminated) CTS string from "src" into "dst". + // Does NOT terminate with a bottom. Returns the number of cells copied. + int copyCTS (CellTypeState[] dst, CellTypeState[] src) { + int idx = 0; + for (; idx < src.length && !src[idx].isBottom(); idx++) { + dst[idx] = src[idx]; + } + return idx; + } + + // Create result set + boolean _report_result; + boolean _report_result_for_send; // Unfortunatly, stackmaps for sends are special, so we need some extra + BytecodeStream _itr_send; // variables to handle them properly. + + void reportResult () { + // if (TraceNewOopMapGeneration) tty.print_cr("Report result pass"); + + // We now want to report the result of the parse + _report_result = true; + + // Prolog code + fillStackmapProlog(_gc_points); + + // Mark everything changed, then do one interpretation pass. + for (int i = 0; i<_bb_count; i++) { + if (_basic_blocks[i].isReachable()) { + _basic_blocks[i].setChanged(true); + interpBB(_basic_blocks[i]); + } + } + + // Note: Since we are skipping dead-code when we are reporting results, then + // the no. of encountered gc-points might be fewer than the previously number + // we have counted. (dead-code is a pain - it should be removed before we get here) + fillStackmapEpilog(); + + // Report initvars + fillInitVars(_init_vars); + + _report_result = false; + } + + // Initvars + List/**/ _init_vars; + + void initializeVars () { + for (int k = 0; k < _init_vars.size(); k++) + _state.get(((Integer) _init_vars.get(k)).intValue()).set(CellTypeState.makeSlotRef(k)); + } + + void addToRefInitSet (int localNo) { + // if (TraceNewOopMapGeneration) + // tty.print_cr("Added init vars: %d", localNo); + + Integer local = new Integer(localNo); + + // Is it already in the set? + if (_init_vars.contains(local)) + return; + + _init_vars.add(local); + } + + // Conflicts rewrite logic + boolean _conflict; // True, if a conflict occured during interpretation + int _nof_refval_conflicts; // No. of conflicts that require rewrites + int[] _new_var_map; + + void recordRefvalConflict (int varNo) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(varNo>=0 && varNo< _max_locals, "index out of range"); + } + + if (TraceOopMapRewrites) { + System.err.println("### Conflict detected (local no: " + varNo + ")"); + } + + if (_new_var_map == null) { + _new_var_map = new int[_max_locals]; + for (int k = 0; k < _max_locals; k++) _new_var_map[k] = k; + } + + if ( _new_var_map[varNo] == varNo) { + // Check if max. number of locals has been reached + if (_max_locals + _nof_refval_conflicts >= MAX_LOCAL_VARS) { + throw new RuntimeException("Rewriting exceeded local variable limit"); + } + _new_var_map[varNo] = _max_locals + _nof_refval_conflicts; + _nof_refval_conflicts++; + } + } + + void rewriteRefvalConflicts () { + if (_nof_refval_conflicts > 0) { + if (VM.getVM().isDebugging()) { + throw new RuntimeException("Should not reach here (method rewriting should have been done by the VM already)"); + } else { + throw new RuntimeException("Method rewriting not yet implemented in Java"); + } + } + } + // Rewriting-related routines are not needed here + // void rewrite_refval_conflict (int from, int to); + // bool rewrite_refval_conflict_inst (BytecodeStream *i, int from, int to); + // bool rewrite_load_or_store (BytecodeStream *i, Bytecodes.Code bc, Bytecodes.Code bc0, unsigned int varNo); + + // bool expand_current_instr (int bci, int ilen, int newIlen, u_char inst_buffer[]); + // bool is_astore (BytecodeStream *itr, int *index); + // bool is_aload (BytecodeStream *itr, int *index); + + // List of bci's where a return address is on top of the stack + // GrowableArray *_ret_adr_tos; + + // bool stack_top_holds_ret_addr (int bci); + // void compute_ret_adr_at_TOS (); + // void update_ret_adr_at_TOS (int bci, int delta); + + String stateVecToString (CellTypeStateList vec, int len) { + for (int i = 0; i < len; i++) { + _state_vec_buf[i] = vec.get(i).toChar(); + } + return new String(_state_vec_buf, 0, len); + } + + // Helper method. Can be used in subclasses to fx. calculate gc_points. If the current instuction + // is a control transfer, then calls the jmpFct all possible destinations. + void retJumpTargetsDo (BytecodeStream bcs, JumpClosure closure, int varNo, int[] data) { + CellTypeState ra = vars().get(varNo); + if (!ra.isGoodAddress()) { + throw new RuntimeException("ret returns from two jsr subroutines?"); + } + int target = ra.getInfo(); + + RetTableEntry rtEnt = _rt.findJsrsForTarget(target); + int bci = bcs.bci(); + for (int i = 0; i < rtEnt.nofJsrs(); i++) { + int target_bci = rtEnt.jsrs(i); + // Make sure a jrtRet does not set the changed bit for dead basicblock. + BasicBlock jsr_bb = getBasicBlockContaining(target_bci - 1); + if (Assert.ASSERTS_ENABLED) { + BasicBlock target_bb = _basic_blocks[1 + bbIndex(jsr_bb)]; + Assert.that(target_bb == getBasicBlockAt(target_bci), "wrong calc. of successor basicblock"); + } + boolean alive = jsr_bb.isAlive(); + // if (TraceNewOopMapGeneration) { + // tty.print("pc = %d, ret . %d alive: %s\n", bci, target_bci, alive ? "true" : "false"); + // } + if (alive) { + closure.process(this, target_bci, data); + } + } + } + + /** If the current instruction in "c" has no effect on control flow, + returns "true". Otherwise, calls "closure.process()" one or + more times, with "c", an appropriate "pcDelta", and "data" as + arguments, then returns "false". There is one exception: if the + current instruction is a "ret", returns "false" without calling + "jmpFct". Arrangements for tracking the control flow of a "ret" + must be made externally. */ + boolean jumpTargetsDo (BytecodeStream bcs, JumpClosure closure, int[] data) { + int bci = bcs.bci(); + + switch (bcs.code()) { + case Bytecodes._ifeq: + case Bytecodes._ifne: + case Bytecodes._iflt: + case Bytecodes._ifge: + case Bytecodes._ifgt: + case Bytecodes._ifle: + case Bytecodes._if_icmpeq: + case Bytecodes._if_icmpne: + case Bytecodes._if_icmplt: + case Bytecodes._if_icmpge: + case Bytecodes._if_icmpgt: + case Bytecodes._if_icmple: + case Bytecodes._if_acmpeq: + case Bytecodes._if_acmpne: + case Bytecodes._ifnull: + case Bytecodes._ifnonnull: + closure.process(this, bcs.dest(), data); + closure.process(this, bci + 3, data); + break; + + case Bytecodes._goto: + closure.process(this, bcs.dest(), data); + break; + case Bytecodes._goto_w: + closure.process(this, bcs.dest_w(), data); + break; + case Bytecodes._tableswitch: + { + BytecodeTableswitch tableswitch = BytecodeTableswitch.at(bcs); + int len = tableswitch.length(); + + closure.process(this, bci + tableswitch.defaultOffset(), data); /* Default. jump address */ + while (--len >= 0) { + closure.process(this, bci + tableswitch.destOffsetAt(len), data); + } + break; + } + + case Bytecodes._fast_linearswitch: // Java opcodes + case Bytecodes._fast_binaryswitch: // get_int_table handles conversions + case Bytecodes._lookupswitch: + { + BytecodeLookupswitch lookupswitch = BytecodeLookupswitch.at(bcs); + int npairs = lookupswitch.numberOfPairs(); + closure.process(this, bci + lookupswitch.defaultOffset(), data); /* Default. */ + while(--npairs >= 0) { + LookupswitchPair pair = lookupswitch.pairAt(npairs); + closure.process(this, bci + pair.offset(), data); + } + break; + } + case Bytecodes._jsr: + Assert.that(bcs.isWide()==false, "sanity check"); + closure.process(this, bcs.dest(), data); + break; + case Bytecodes._jsr_w: + closure.process(this, bcs.dest_w(), data); + break; + case Bytecodes._wide: + throw new RuntimeException("Should not reach here"); + case Bytecodes._athrow: + case Bytecodes._ireturn: + case Bytecodes._lreturn: + case Bytecodes._freturn: + case Bytecodes._dreturn: + case Bytecodes._areturn: + case Bytecodes._return: + case Bytecodes._ret: + break; + default: + return true; + } + return false; + } + + // Monitor matching + // int fill_out_arrays (int *enter, int *exit, int max); + + // friend class RelocCallback; + + //---------------------------------------------------------------------- + // Public routines for GenerateOopMap + // + public GenerateOopMap(Method method) { + // We have to initialize all variables here, that can be queried direcly + _method = method; + _max_locals=0; + _init_vars = null; + _rt = new RetTable(); + } + + + // Compute the map. + public void computeMap() { + if (DEBUG) { + System.err.println("*** GenerateOopMap: computing for " + + method().getMethodHolder().getName().asString() + "." + + method().getName().asString() + + method().getSignature().asString()); + } + + // Initialize values + _got_error = false; + _conflict = false; + _max_locals = (int) method().getMaxLocals(); + _max_stack = (int) method().getMaxStack(); + _has_exceptions = (method().getExceptionTable().getLength() > 0); + _nof_refval_conflicts = 0; + _init_vars = new ArrayList(5); // There are seldom more than 5 init_vars + _report_result = false; + _report_result_for_send = false; + _report_for_exit_bci = -1; + _new_var_map = null; + // _ret_adr_tos = new GrowableArray(5); // 5 seems like a good number; + // _did_rewriting = false; + // _did_relocation = false; + + // FIXME: remove + /* + if (TraceNewOopMapGeneration) { + tty.print("Method name: %s\n", method().name().as_C_string()); + if (Verbose) { + _method.print_codes(); + tty.print_cr("Exception table:"); + typeArrayOop excps = method().exception_table(); + for(int i = 0; i < excps.length(); i += 4) { + tty.print_cr("[%d - %d] . %d", excps.int_at(i + 0), excps.int_at(i + 1), excps.int_at(i + 2)); + } + } + } + */ + + // if no code - do nothing + // compiler needs info + if (method().getCodeSize() == 0 || _max_locals + method().getMaxStack() == 0) { + fillStackmapProlog(0); + fillStackmapEpilog(); + return; + } + // Step 1: Compute all jump targets and their return value + if (!_got_error) + _rt.computeRetTable(_method); + + // Step 2: Find all basic blocks and count GC points + if (!_got_error) + markBBHeadersAndCountGCPoints(); + + // Step 3: Calculate stack maps + if (!_got_error) + doInterpretation(); + + // Step 4:Return results + if (!_got_error && reportResults()) + reportResult(); + + if (_got_error) { + // We could expand this code to throw somekind of exception (e.g., VerifyException). However, + // an exception thrown in this part of the code is likly to mean that we are executing some + // illegal bytecodes (that the verifier should have caught if turned on), so we will just exit + // with a fatal. + throw new RuntimeException("Illegal bytecode sequence encountered while generating interpreter pointer maps - method should be rejected by verifier."); + } + } + + // Do a callback on fill_stackmap_for_opcodes for basicblock containing bci + public void resultForBasicblock(int bci) { + // FIXME: remove + // if (TraceNewOopMapGeneration) tty.print_cr("Report result pass for basicblock"); + + // We now want to report the result of the parse + _report_result = true; + + // Find basicblock and report results + BasicBlock bb = getBasicBlockContaining(bci); + if (Assert.ASSERTS_ENABLED) { + Assert.that(bb.isReachable(), "getting result from unreachable basicblock"); + } + bb.setChanged(true); + interpBB(bb); + } + + // Query + public int maxLocals() { return _max_locals; } + public Method method() { return _method; } + + // bool did_rewriting() { return _did_rewriting; } + // bool did_relocation() { return _did_relocation; } + + // static void print_time(); + + // Monitor query + public boolean monitorSafe() { return _monitor_safe; } + // Takes as input the bci of a monitorexit bytecode. + // Returns the bci of the corresponding monitorenter. + // Can only be called safely after computeMap() is run. + public int getMonitorMatch(int bci) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(_monitor_safe, "Attempt to match monitor in broken code."); + } + + // if (TraceNewOopMapGeneration) + // tty.print_cr("Report monitor match for bci : %d", bci); + + // We want to report the line number of the monitorenter. + _report_for_exit_bci = bci; + _matching_enter_bci = -1; + + // Find basicblock and report results + BasicBlock bb = getBasicBlockContaining(bci); + if (bb.isReachable()) { + bb.setChanged(true); + interpBB(bb); + _report_for_exit_bci = -1; + if (Assert.ASSERTS_ENABLED) { + Assert.that(_matching_enter_bci != -1, "monitor matching invariant"); + } + } + return _matching_enter_bci; + } + + // Returns a Arena allocated object that contains pairing info. + // MonitorPairs* get_pairing(Arena *arena); + + // copies monitor pairing info into area; area_count specifies maximum + // possible number of monitor pairs + // int copy_pairing(int pair_count, MonitorPairs* pairs); + + private int bbIndex(BasicBlock bb) { + for (int i = 0; i < _basic_blocks.length; i++) { + if (_basic_blocks[i] == bb) { + return i; + } + } + throw new RuntimeException("Should have found block"); + } + + //---------------------------------------------------------------------- + // Specialization methods. Intended use: + // - possibleGCPoint must return true for every bci for which the + // stackmaps must be returned + // - fillStackmapProlog is called just before the result is + // reported. The arguments tells the estimated number of gc points + // - fillStackmapForOpcodes is called once for each bytecode index + // in order (0...code_length-1) + // - fillStackmapEpilog is called after all results has been + // reported. Note: Since the algorithm does not report stackmaps for + // deadcode, fewer gc_points might have been encounted than assumed + // during the epilog. It is the responsibility of the subclass to + // count the correct number. + // - fillInitVars are called once with the result of the init_vars + // computation + // + // All these methods are used during a call to computeMap. Note: + // None of the return results are valid after computeMap returns, + // since all values are allocated as resource objects. + // + // All virtual method must be implemented in subclasses + public boolean allowRewrites () { return false; } + public boolean reportResults () { return true; } + public boolean reportInitVars () { return true; } + public boolean possibleGCPoint (BytecodeStream bcs) { throw new RuntimeException("ShouldNotReachHere"); } + public void fillStackmapProlog (int nofGCPoints) { throw new RuntimeException("ShouldNotReachHere"); } + public void fillStackmapEpilog () { throw new RuntimeException("ShouldNotReachHere"); } + public void fillStackmapForOpcodes (BytecodeStream bcs, + CellTypeStateList vars, + CellTypeStateList stack, + int stackTop) { throw new RuntimeException("ShouldNotReachHere"); } + public void fillInitVars (List/**/ init_vars) { throw new RuntimeException("ShouldNotReachHere"); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/HeapPrinter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/HeapPrinter.java new file mode 100644 index 00000000000..cc9a18aaf99 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/HeapPrinter.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; + +public class HeapPrinter implements HeapVisitor { + + public HeapPrinter(PrintStream tty) { + oopPrinter = new OopPrinter(tty); + } + + private OopPrinter oopPrinter; + + public void prologue(long size) {} + + public boolean doObj(Oop obj) { + obj.iterate(oopPrinter, true); + return false; + } + + public void epilogue() {} +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/HeapVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/HeapVisitor.java new file mode 100644 index 00000000000..5d3cc0b45a1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/HeapVisitor.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +// A HeapVisitor is used for visiting all object in the heap + +public interface HeapVisitor { + // This is called at the beginning of the iteration to provide the + // HeapVisitor with information about the amount of memory which + // will be traversed (for example, for displaying a progress bar) + public void prologue(long usedSize); + + // Callback method for each object + // Return true if the iteration should be stopped. + public boolean doObj(Oop obj); + + // This is called after the traversal is complete + public void epilogue(); +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/IndexableFieldIdentifier.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/IndexableFieldIdentifier.java new file mode 100644 index 00000000000..d5924d279af --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/IndexableFieldIdentifier.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.*; +import sun.jvm.hotspot.types.*; +import java.io.*; + +// An IndexableFieldIdentifier describes a field in an Oop accessed by an index + +public class IndexableFieldIdentifier extends FieldIdentifier { + + public IndexableFieldIdentifier(int index) { + this.index = index; + } + + private int index; + + public int getIndex() { return index; } + + public String getName() { return Integer.toString(getIndex()); } + + public void printOn(PrintStream tty) { + tty.print(" - " + getIndex() + ":\t"); + } + + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (!(obj instanceof IndexableFieldIdentifier)) { + return false; + } + + return (((IndexableFieldIdentifier) obj).getIndex() == index); + } + + public int hashCode() { + return index; + } +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Instance.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Instance.java new file mode 100644 index 00000000000..f707915ba71 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Instance.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// An Instance is an instance of a Java Class + +public class Instance extends Oop { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("instanceOopDesc"); + } + + Instance(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public boolean isInstance() { return true; } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + ((InstanceKlass) getKlass()).iterateNonStaticFields(visitor); + } + + public void printValueOn(PrintStream tty) { + // Special-case strings. + // FIXME: would like to do this in more type-safe fashion (need + // SystemDictionary analogue) + if (getKlass().getName().asString().equals("java/lang/String")) { + tty.print("\"" + OopUtilities.stringOopToString(this) + "\""); + } else { + super.printValueOn(tty); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java new file mode 100644 index 00000000000..c98a35f3d64 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java @@ -0,0 +1,881 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +// An InstanceKlass is the VM level representation of a Java class. + +public class InstanceKlass extends Klass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + // field offset constants + public static int ACCESS_FLAGS_OFFSET; + public static int NAME_INDEX_OFFSET; + public static int SIGNATURE_INDEX_OFFSET; + public static int INITVAL_INDEX_OFFSET; + public static int LOW_OFFSET; + public static int HIGH_OFFSET; + public static int GENERIC_SIGNATURE_INDEX_OFFSET; + public static int NEXT_OFFSET; + public static int IMPLEMENTORS_LIMIT; + + // ClassState constants + private static int CLASS_STATE_UNPARSABLE_BY_GC; + private static int CLASS_STATE_ALLOCATED; + private static int CLASS_STATE_LOADED; + private static int CLASS_STATE_LINKED; + private static int CLASS_STATE_BEING_INITIALIZED; + private static int CLASS_STATE_FULLY_INITIALIZED; + private static int CLASS_STATE_INITIALIZATION_ERROR; + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("instanceKlass"); + arrayKlasses = new OopField(type.getOopField("_array_klasses"), Oop.getHeaderSize()); + methods = new OopField(type.getOopField("_methods"), Oop.getHeaderSize()); + methodOrdering = new OopField(type.getOopField("_method_ordering"), Oop.getHeaderSize()); + localInterfaces = new OopField(type.getOopField("_local_interfaces"), Oop.getHeaderSize()); + transitiveInterfaces = new OopField(type.getOopField("_transitive_interfaces"), Oop.getHeaderSize()); + nofImplementors = new CIntField(type.getCIntegerField("_nof_implementors"), Oop.getHeaderSize()); + IMPLEMENTORS_LIMIT = db.lookupIntConstant("instanceKlass::implementors_limit").intValue(); + implementors = new OopField[IMPLEMENTORS_LIMIT]; + for (int i = 0; i < IMPLEMENTORS_LIMIT; i++) { + long arrayOffset = Oop.getHeaderSize() + (i * db.getAddressSize()); + implementors[i] = new OopField(type.getOopField("_implementors[0]"), arrayOffset); + } + fields = new OopField(type.getOopField("_fields"), Oop.getHeaderSize()); + constants = new OopField(type.getOopField("_constants"), Oop.getHeaderSize()); + classLoader = new OopField(type.getOopField("_class_loader"), Oop.getHeaderSize()); + protectionDomain = new OopField(type.getOopField("_protection_domain"), Oop.getHeaderSize()); + signers = new OopField(type.getOopField("_signers"), Oop.getHeaderSize()); + sourceFileName = new OopField(type.getOopField("_source_file_name"), Oop.getHeaderSize()); + sourceDebugExtension = new OopField(type.getOopField("_source_debug_extension"), Oop.getHeaderSize()); + innerClasses = new OopField(type.getOopField("_inner_classes"), Oop.getHeaderSize()); + nonstaticFieldSize = new CIntField(type.getCIntegerField("_nonstatic_field_size"), Oop.getHeaderSize()); + staticFieldSize = new CIntField(type.getCIntegerField("_static_field_size"), Oop.getHeaderSize()); + staticOopFieldSize = new CIntField(type.getCIntegerField("_static_oop_field_size"), Oop.getHeaderSize()); + nonstaticOopMapSize = new CIntField(type.getCIntegerField("_nonstatic_oop_map_size"), Oop.getHeaderSize()); + isMarkedDependent = new CIntField(type.getCIntegerField("_is_marked_dependent"), Oop.getHeaderSize()); + initState = new CIntField(type.getCIntegerField("_init_state"), Oop.getHeaderSize()); + vtableLen = new CIntField(type.getCIntegerField("_vtable_len"), Oop.getHeaderSize()); + itableLen = new CIntField(type.getCIntegerField("_itable_len"), Oop.getHeaderSize()); + breakpoints = type.getAddressField("_breakpoints"); + genericSignature = new OopField(type.getOopField("_generic_signature"), Oop.getHeaderSize()); + majorVersion = new CIntField(type.getCIntegerField("_major_version"), Oop.getHeaderSize()); + minorVersion = new CIntField(type.getCIntegerField("_minor_version"), Oop.getHeaderSize()); + headerSize = alignObjectOffset(Oop.getHeaderSize() + type.getSize()); + + // read field offset constants + ACCESS_FLAGS_OFFSET = db.lookupIntConstant("instanceKlass::access_flags_offset").intValue(); + NAME_INDEX_OFFSET = db.lookupIntConstant("instanceKlass::name_index_offset").intValue(); + SIGNATURE_INDEX_OFFSET = db.lookupIntConstant("instanceKlass::signature_index_offset").intValue(); + INITVAL_INDEX_OFFSET = db.lookupIntConstant("instanceKlass::initval_index_offset").intValue(); + LOW_OFFSET = db.lookupIntConstant("instanceKlass::low_offset").intValue(); + HIGH_OFFSET = db.lookupIntConstant("instanceKlass::high_offset").intValue(); + GENERIC_SIGNATURE_INDEX_OFFSET = db.lookupIntConstant("instanceKlass::generic_signature_offset").intValue(); + NEXT_OFFSET = db.lookupIntConstant("instanceKlass::next_offset").intValue(); + // read ClassState constants + CLASS_STATE_UNPARSABLE_BY_GC = db.lookupIntConstant("instanceKlass::unparsable_by_gc").intValue(); + CLASS_STATE_ALLOCATED = db.lookupIntConstant("instanceKlass::allocated").intValue(); + CLASS_STATE_LOADED = db.lookupIntConstant("instanceKlass::loaded").intValue(); + CLASS_STATE_LINKED = db.lookupIntConstant("instanceKlass::linked").intValue(); + CLASS_STATE_BEING_INITIALIZED = db.lookupIntConstant("instanceKlass::being_initialized").intValue(); + CLASS_STATE_FULLY_INITIALIZED = db.lookupIntConstant("instanceKlass::fully_initialized").intValue(); + CLASS_STATE_INITIALIZATION_ERROR = db.lookupIntConstant("instanceKlass::initialization_error").intValue(); + + } + + InstanceKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static OopField arrayKlasses; + private static OopField methods; + private static OopField methodOrdering; + private static OopField localInterfaces; + private static OopField transitiveInterfaces; + private static CIntField nofImplementors; + private static OopField[] implementors; + private static OopField fields; + private static OopField constants; + private static OopField classLoader; + private static OopField protectionDomain; + private static OopField signers; + private static OopField sourceFileName; + private static OopField sourceDebugExtension; + private static OopField innerClasses; + private static CIntField nonstaticFieldSize; + private static CIntField staticFieldSize; + private static CIntField staticOopFieldSize; + private static CIntField nonstaticOopMapSize; + private static CIntField isMarkedDependent; + private static CIntField initState; + private static CIntField vtableLen; + private static CIntField itableLen; + private static AddressField breakpoints; + private static OopField genericSignature; + private static CIntField majorVersion; + private static CIntField minorVersion; + + // type safe enum for ClassState from instanceKlass.hpp + public static class ClassState { + public static final ClassState UNPARSABLE_BY_GC = new ClassState("unparsable_by_gc"); + public static final ClassState ALLOCATED = new ClassState("allocated"); + public static final ClassState LOADED = new ClassState("loaded"); + public static final ClassState LINKED = new ClassState("linked"); + public static final ClassState BEING_INITIALIZED = new ClassState("beingInitialized"); + public static final ClassState FULLY_INITIALIZED = new ClassState("fullyInitialized"); + public static final ClassState INITIALIZATION_ERROR = new ClassState("initializationError"); + + private ClassState(String value) { + this.value = value; + } + + public String toString() { + return value; + } + + private String value; + } + + private int getInitStateAsInt() { return (int) initState.getValue(this); } + public ClassState getInitState() { + int state = getInitStateAsInt(); + if (state == CLASS_STATE_UNPARSABLE_BY_GC) { + return ClassState.UNPARSABLE_BY_GC; + } else if (state == CLASS_STATE_ALLOCATED) { + return ClassState.ALLOCATED; + } else if (state == CLASS_STATE_LOADED) { + return ClassState.LOADED; + } else if (state == CLASS_STATE_LINKED) { + return ClassState.LINKED; + } else if (state == CLASS_STATE_BEING_INITIALIZED) { + return ClassState.BEING_INITIALIZED; + } else if (state == CLASS_STATE_FULLY_INITIALIZED) { + return ClassState.FULLY_INITIALIZED; + } else if (state == CLASS_STATE_INITIALIZATION_ERROR) { + return ClassState.INITIALIZATION_ERROR; + } else { + throw new RuntimeException("should not reach here"); + } + } + + // initialization state quaries + public boolean isLoaded() { + return getInitStateAsInt() >= CLASS_STATE_LOADED; + } + + public boolean isLinked() { + return getInitStateAsInt() >= CLASS_STATE_LINKED; + } + + public boolean isInitialized() { + return getInitStateAsInt() == CLASS_STATE_FULLY_INITIALIZED; + } + + public boolean isNotInitialized() { + return getInitStateAsInt() < CLASS_STATE_BEING_INITIALIZED; + } + + public boolean isBeingInitialized() { + return getInitStateAsInt() == CLASS_STATE_BEING_INITIALIZED; + } + + public boolean isInErrorState() { + return getInitStateAsInt() == CLASS_STATE_INITIALIZATION_ERROR; + } + + public int getClassStatus() { + int result = 0; + if (isLinked()) { + result |= JVMDIClassStatus.VERIFIED | JVMDIClassStatus.PREPARED; + } + + if (isInitialized()) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isLinked(), "Class status is not consistent"); + } + result |= JVMDIClassStatus.INITIALIZED; + } + + if (isInErrorState()) { + result |= JVMDIClassStatus.ERROR; + } + return result; + } + + // Byteside of the header + private static long headerSize; + + public static long getHeaderSize() { return headerSize; } + + // Accessors for declared fields + public Klass getArrayKlasses() { return (Klass) arrayKlasses.getValue(this); } + public ObjArray getMethods() { return (ObjArray) methods.getValue(this); } + public TypeArray getMethodOrdering() { return (TypeArray) methodOrdering.getValue(this); } + public ObjArray getLocalInterfaces() { return (ObjArray) localInterfaces.getValue(this); } + public ObjArray getTransitiveInterfaces() { return (ObjArray) transitiveInterfaces.getValue(this); } + public long nofImplementors() { return nofImplementors.getValue(this); } + public Klass getImplementor() { return (Klass) implementors[0].getValue(this); } + public Klass getImplementor(int i) { return (Klass) implementors[i].getValue(this); } + public TypeArray getFields() { return (TypeArray) fields.getValue(this); } + public ConstantPool getConstants() { return (ConstantPool) constants.getValue(this); } + public Oop getClassLoader() { return classLoader.getValue(this); } + public Oop getProtectionDomain() { return protectionDomain.getValue(this); } + public ObjArray getSigners() { return (ObjArray) signers.getValue(this); } + public Symbol getSourceFileName() { return (Symbol) sourceFileName.getValue(this); } + public Symbol getSourceDebugExtension(){ return (Symbol) sourceDebugExtension.getValue(this); } + public TypeArray getInnerClasses() { return (TypeArray) innerClasses.getValue(this); } + public long getNonstaticFieldSize() { return nonstaticFieldSize.getValue(this); } + public long getStaticFieldSize() { return staticFieldSize.getValue(this); } + public long getStaticOopFieldSize() { return staticOopFieldSize.getValue(this); } + public long getNonstaticOopMapSize() { return nonstaticOopMapSize.getValue(this); } + public boolean getIsMarkedDependent() { return isMarkedDependent.getValue(this) != 0; } + public long getVtableLen() { return vtableLen.getValue(this); } + public long getItableLen() { return itableLen.getValue(this); } + public Symbol getGenericSignature() { return (Symbol) genericSignature.getValue(this); } + public long majorVersion() { return majorVersion.getValue(this); } + public long minorVersion() { return minorVersion.getValue(this); } + + // "size helper" == instance size in words + public long getSizeHelper() { + int lh = getLayoutHelper(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(lh > 0, "layout helper initialized for instance class"); + } + return lh / VM.getVM().getAddressSize(); + } + + // same as enum InnerClassAttributeOffset in VM code. + public static interface InnerClassAttributeOffset { + // from JVM spec. "InnerClasses" attribute + public static final int innerClassInnerClassInfoOffset = 0; + public static final int innerClassOuterClassInfoOffset = 1; + public static final int innerClassInnerNameOffset = 2; + public static final int innerClassAccessFlagsOffset = 3; + public static final int innerClassNextOffset = 4; + }; + + // refer to compute_modifier_flags in VM code. + public long computeModifierFlags() { + long access = getAccessFlags(); + // But check if it happens to be member class. + TypeArray innerClassList = getInnerClasses(); + int length = ( innerClassList == null)? 0 : (int) innerClassList.getLength(); + if (length > 0) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(length % InnerClassAttributeOffset.innerClassNextOffset == 0, "just checking"); + } + for (int i = 0; i < length; i += InnerClassAttributeOffset.innerClassNextOffset) { + int ioff = innerClassList.getShortAt(i + + InnerClassAttributeOffset.innerClassInnerClassInfoOffset); + // 'ioff' can be zero. + // refer to JVM spec. section 4.7.5. + if (ioff != 0) { + // only look at classes that are already loaded + // since we are looking for the flags for our self. + Oop classInfo = getConstants().getObjAt(ioff); + Symbol name = null; + if (classInfo instanceof Klass) { + name = ((Klass) classInfo).getName(); + } else if (classInfo instanceof Symbol) { + name = (Symbol) classInfo; + } else { + throw new RuntimeException("should not reach here"); + } + + if (name.equals(getName())) { + // This is really a member class + access = innerClassList.getShortAt(i + + InnerClassAttributeOffset.innerClassAccessFlagsOffset); + break; + } + } + } // for inner classes + } + + // Remember to strip ACC_SUPER bit + return (access & (~JVM_ACC_SUPER)) & JVM_ACC_WRITTEN_FLAGS; + } + + + // whether given Symbol is name of an inner/nested Klass of this Klass? + // anonymous and local classes are excluded. + public boolean isInnerClassName(Symbol sym) { + return isInInnerClasses(sym, false); + } + + // whether given Symbol is name of an inner/nested Klass of this Klass? + // anonymous classes excluded, but local classes are included. + public boolean isInnerOrLocalClassName(Symbol sym) { + return isInInnerClasses(sym, true); + } + + private boolean isInInnerClasses(Symbol sym, boolean includeLocals) { + TypeArray innerClassList = getInnerClasses(); + int length = ( innerClassList == null)? 0 : (int) innerClassList.getLength(); + if (length > 0) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(length % InnerClassAttributeOffset.innerClassNextOffset == 0, "just checking"); + } + for (int i = 0; i < length; i += InnerClassAttributeOffset.innerClassNextOffset) { + int ioff = innerClassList.getShortAt(i + + InnerClassAttributeOffset.innerClassInnerClassInfoOffset); + // 'ioff' can be zero. + // refer to JVM spec. section 4.7.5. + if (ioff != 0) { + Oop iclassInfo = getConstants().getObjAt(ioff); + Symbol innerName = null; + if (iclassInfo instanceof Klass) { + innerName = ((Klass) iclassInfo).getName(); + } else if (iclassInfo instanceof Symbol) { + innerName = (Symbol) iclassInfo; + } else { + throw new RuntimeException("should not reach here"); + } + + Symbol myname = getName(); + int ooff = innerClassList.getShortAt(i + + InnerClassAttributeOffset.innerClassOuterClassInfoOffset); + // for anonymous classes inner_name_index of InnerClasses + // attribute is zero. + int innerNameIndex = innerClassList.getShortAt(i + + InnerClassAttributeOffset.innerClassInnerNameOffset); + // if this is not a member (anonymous, local etc.), 'ooff' will be zero + // refer to JVM spec. section 4.7.5. + if (ooff == 0) { + if (includeLocals) { + // does it looks like my local class? + if (innerName.equals(sym) && + innerName.asString().startsWith(myname.asString())) { + // exclude anonymous classes. + return (innerNameIndex != 0); + } + } + } else { + Oop oclassInfo = getConstants().getObjAt(ooff); + Symbol outerName = null; + if (oclassInfo instanceof Klass) { + outerName = ((Klass) oclassInfo).getName(); + } else if (oclassInfo instanceof Symbol) { + outerName = (Symbol) oclassInfo; + } else { + throw new RuntimeException("should not reach here"); + } + + // include only if current class is outer class. + if (outerName.equals(myname) && innerName.equals(sym)) { + return true; + } + } + } + } // for inner classes + return false; + } else { + return false; + } + } + + public boolean implementsInterface(Klass k) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(k.isInterface(), "should not reach here"); + } + ObjArray interfaces = getTransitiveInterfaces(); + final int len = (int) interfaces.getLength(); + for (int i = 0; i < len; i++) { + if (interfaces.getObjAt(i).equals(k)) return true; + } + return false; + } + + boolean computeSubtypeOf(Klass k) { + if (k.isInterface()) { + return implementsInterface(k); + } else { + return super.computeSubtypeOf(k); + } + } + + public void printValueOn(PrintStream tty) { + tty.print("InstanceKlass for " + getName().asString()); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doOop(arrayKlasses, true); + visitor.doOop(methods, true); + visitor.doOop(methodOrdering, true); + visitor.doOop(localInterfaces, true); + visitor.doOop(transitiveInterfaces, true); + visitor.doCInt(nofImplementors, true); + for (int i = 0; i < IMPLEMENTORS_LIMIT; i++) + visitor.doOop(implementors[i], true); + visitor.doOop(fields, true); + visitor.doOop(constants, true); + visitor.doOop(classLoader, true); + visitor.doOop(protectionDomain, true); + visitor.doOop(signers, true); + visitor.doOop(sourceFileName, true); + visitor.doOop(innerClasses, true); + visitor.doCInt(nonstaticFieldSize, true); + visitor.doCInt(staticFieldSize, true); + visitor.doCInt(staticOopFieldSize, true); + visitor.doCInt(nonstaticOopMapSize, true); + visitor.doCInt(isMarkedDependent, true); + visitor.doCInt(initState, true); + visitor.doCInt(vtableLen, true); + visitor.doCInt(itableLen, true); + } + + TypeArray fields = getFields(); + int length = (int) fields.getLength(); + for (int index = 0; index < length; index += NEXT_OFFSET) { + short accessFlags = fields.getShortAt(index + ACCESS_FLAGS_OFFSET); + short signatureIndex = fields.getShortAt(index + SIGNATURE_INDEX_OFFSET); + + FieldType type = new FieldType((Symbol) getConstants().getObjAt(signatureIndex)); + AccessFlags access = new AccessFlags(accessFlags); + if (access.isStatic()) { + visitField(visitor, type, index); + } + } + } + + public Klass getJavaSuper() { + return getSuper(); + } + + public void iterateNonStaticFields(OopVisitor visitor) { + if (getSuper() != null) { + ((InstanceKlass) getSuper()).iterateNonStaticFields(visitor); + } + TypeArray fields = getFields(); + + int length = (int) fields.getLength(); + for (int index = 0; index < length; index += NEXT_OFFSET) { + short accessFlags = fields.getShortAt(index + ACCESS_FLAGS_OFFSET); + short signatureIndex = fields.getShortAt(index + SIGNATURE_INDEX_OFFSET); + + FieldType type = new FieldType((Symbol) getConstants().getObjAt(signatureIndex)); + AccessFlags access = new AccessFlags(accessFlags); + if (!access.isStatic()) { + visitField(visitor, type, index); + } + } + } + + /** Field access by name. */ + public Field findLocalField(Symbol name, Symbol sig) { + TypeArray fields = getFields(); + int n = (int) fields.getLength(); + ConstantPool cp = getConstants(); + for (int i = 0; i < n; i += NEXT_OFFSET) { + int nameIndex = fields.getShortAt(i + NAME_INDEX_OFFSET); + int sigIndex = fields.getShortAt(i + SIGNATURE_INDEX_OFFSET); + Symbol f_name = cp.getSymbolAt(nameIndex); + Symbol f_sig = cp.getSymbolAt(sigIndex); + if (name.equals(f_name) && sig.equals(f_sig)) { + return newField(i); + } + } + + return null; + } + + /** Find field in direct superinterfaces. */ + public Field findInterfaceField(Symbol name, Symbol sig) { + ObjArray interfaces = getLocalInterfaces(); + int n = (int) interfaces.getLength(); + for (int i = 0; i < n; i++) { + InstanceKlass intf1 = (InstanceKlass) interfaces.getObjAt(i); + if (Assert.ASSERTS_ENABLED) { + Assert.that(intf1.isInterface(), "just checking type"); + } + // search for field in current interface + Field f = intf1.findLocalField(name, sig); + if (f != null) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(f.getAccessFlagsObj().isStatic(), "interface field must be static"); + } + return f; + } + // search for field in direct superinterfaces + f = intf1.findInterfaceField(name, sig); + if (f != null) return f; + } + // otherwise field lookup fails + return null; + } + + /** Find field according to JVM spec 5.4.3.2, returns the klass in + which the field is defined. */ + public Field findField(Symbol name, Symbol sig) { + // search order according to newest JVM spec (5.4.3.2, p.167). + // 1) search for field in current klass + Field f = findLocalField(name, sig); + if (f != null) return f; + + // 2) search for field recursively in direct superinterfaces + f = findInterfaceField(name, sig); + if (f != null) return f; + + // 3) apply field lookup recursively if superclass exists + InstanceKlass supr = (InstanceKlass) getSuper(); + if (supr != null) return supr.findField(name, sig); + + // 4) otherwise field lookup fails + return null; + } + + /** Find field according to JVM spec 5.4.3.2, returns the klass in + which the field is defined (convenience routine) */ + public Field findField(String name, String sig) { + SymbolTable symbols = VM.getVM().getSymbolTable(); + Symbol nameSym = symbols.probe(name); + Symbol sigSym = symbols.probe(sig); + if (nameSym == null || sigSym == null) { + return null; + } + return findField(nameSym, sigSym); + } + + /** Find field according to JVM spec 5.4.3.2, returns the klass in + which the field is defined (retained only for backward + compatibility with jdbx) */ + public Field findFieldDbg(String name, String sig) { + return findField(name, sig); + } + + /** Get field by its index in the fields array. Only designed for + use in a debugging system. */ + public Field getFieldByIndex(int fieldArrayIndex) { + return newField(fieldArrayIndex); + } + + + /** Return a List of SA Fields for the fields declared in this class. + Inherited fields are not included. + Return an empty list if there are no fields declared in this class. + Only designed for use in a debugging system. */ + public List getImmediateFields() { + // A list of Fields for each field declared in this class/interface, + // not including inherited fields. + TypeArray fields = getFields(); + + int length = (int) fields.getLength(); + List immediateFields = new ArrayList(length / NEXT_OFFSET); + for (int index = 0; index < length; index += NEXT_OFFSET) { + immediateFields.add(getFieldByIndex(index)); + } + + return immediateFields; + } + + /** Return a List of SA Fields for all the java fields in this class, + including all inherited fields. This includes hidden + fields. Thus the returned list can contain fields with + the same name. + Return an empty list if there are no fields. + Only designed for use in a debugging system. */ + public List getAllFields() { + // Contains a Field for each field in this class, including immediate + // fields and inherited fields. + List allFields = getImmediateFields(); + + // transitiveInterfaces contains all interfaces implemented + // by this class and its superclass chain with no duplicates. + + ObjArray interfaces = getTransitiveInterfaces(); + int n = (int) interfaces.getLength(); + for (int i = 0; i < n; i++) { + InstanceKlass intf1 = (InstanceKlass) interfaces.getObjAt(i); + if (Assert.ASSERTS_ENABLED) { + Assert.that(intf1.isInterface(), "just checking type"); + } + allFields.addAll(intf1.getImmediateFields()); + } + + // Get all fields in the superclass, recursively. But, don't + // include fields in interfaces implemented by superclasses; + // we already have all those. + if (!isInterface()) { + InstanceKlass supr; + if ( (supr = (InstanceKlass) getSuper()) != null) { + allFields.addAll(supr.getImmediateFields()); + } + } + + return allFields; + } + + + /** Return a List of SA Methods declared directly in this class/interface. + Return an empty list if there are none, or if this isn't a class/ + interface. + */ + public List getImmediateMethods() { + // Contains a Method for each method declared in this class/interface + // not including inherited methods. + + ObjArray methods = getMethods(); + int length = (int)methods.getLength(); + Object[] tmp = new Object[length]; + + TypeArray methodOrdering = getMethodOrdering(); + if (methodOrdering.getLength() != length) { + // no ordering info present + for (int index = 0; index < length; index++) { + tmp[index] = methods.getObjAt(index); + } + } else { + for (int index = 0; index < length; index++) { + int originalIndex = getMethodOrdering().getIntAt(index); + tmp[originalIndex] = methods.getObjAt(index); + } + } + + return Arrays.asList(tmp); + } + + /** Return a List containing an SA InstanceKlass for each + interface named in this class's 'implements' clause. + */ + public List getDirectImplementedInterfaces() { + // Contains an InstanceKlass for each interface in this classes + // 'implements' clause. + + ObjArray interfaces = getLocalInterfaces(); + int length = (int) interfaces.getLength(); + List directImplementedInterfaces = new ArrayList(length); + + for (int index = 0; index < length; index ++) { + directImplementedInterfaces.add(interfaces.getObjAt(index)); + } + + return directImplementedInterfaces; + } + + + public long getObjectSize() { + long bodySize = alignObjectOffset(getVtableLen() * getHeap().getOopSize()) + + alignObjectOffset(getItableLen() * getHeap().getOopSize()) + + (getStaticFieldSize() + getNonstaticOopMapSize()) * getHeap().getOopSize(); + return alignObjectSize(headerSize + bodySize); + } + + public Klass arrayKlassImpl(boolean orNull, int n) { + // FIXME: in reflective system this would need to change to + // actually allocate + if (getArrayKlasses() == null) { return null; } + ObjArrayKlass oak = (ObjArrayKlass) getArrayKlasses(); + if (orNull) { + return oak.arrayKlassOrNull(n); + } + return oak.arrayKlass(n); + } + + public Klass arrayKlassImpl(boolean orNull) { + return arrayKlassImpl(orNull, 1); + } + + public String signature() { + return "L" + super.signature() + ";"; + } + + /** Convenience routine taking Strings; lookup is done in + SymbolTable. */ + public Method findMethod(String name, String sig) { + SymbolTable syms = VM.getVM().getSymbolTable(); + Symbol nameSym = syms.probe(name); + Symbol sigSym = syms.probe(sig); + if (nameSym == null || sigSym == null) { + return null; + } + return findMethod(nameSym, sigSym); + } + + /** Find method in vtable. */ + public Method findMethod(Symbol name, Symbol sig) { + return findMethod(getMethods(), name, sig); + } + + /** Breakpoint support (see methods on methodOop for details) */ + public BreakpointInfo getBreakpoints() { + Address addr = getHandle().getAddressAt(Oop.getHeaderSize() + breakpoints.getOffset()); + return (BreakpointInfo) VMObjectFactory.newObject(BreakpointInfo.class, addr); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void visitField(OopVisitor visitor, FieldType type, int index) { + Field f = newField(index); + if (type.isOop()) { + visitor.doOop((OopField) f, false); + return; + } + if (type.isByte()) { + visitor.doByte((ByteField) f, false); + return; + } + if (type.isChar()) { + visitor.doChar((CharField) f, false); + return; + } + if (type.isDouble()) { + visitor.doDouble((DoubleField) f, false); + return; + } + if (type.isFloat()) { + visitor.doFloat((FloatField) f, false); + return; + } + if (type.isInt()) { + visitor.doInt((IntField) f, false); + return; + } + if (type.isLong()) { + visitor.doLong((LongField) f, false); + return; + } + if (type.isShort()) { + visitor.doShort((ShortField) f, false); + return; + } + if (type.isBoolean()) { + visitor.doBoolean((BooleanField) f, false); + return; + } + } + + // Creates new field from index in fields TypeArray + private Field newField(int index) { + TypeArray fields = getFields(); + short signatureIndex = fields.getShortAt(index + SIGNATURE_INDEX_OFFSET); + FieldType type = new FieldType((Symbol) getConstants().getObjAt(signatureIndex)); + if (type.isOop()) { + return new OopField(this, index); + } + if (type.isByte()) { + return new ByteField(this, index); + } + if (type.isChar()) { + return new CharField(this, index); + } + if (type.isDouble()) { + return new DoubleField(this, index); + } + if (type.isFloat()) { + return new FloatField(this, index); + } + if (type.isInt()) { + return new IntField(this, index); + } + if (type.isLong()) { + return new LongField(this, index); + } + if (type.isShort()) { + return new ShortField(this, index); + } + if (type.isBoolean()) { + return new BooleanField(this, index); + } + throw new RuntimeException("Illegal field type at index " + index); + } + + private static Method findMethod(ObjArray methods, Symbol name, Symbol signature) { + int len = (int) methods.getLength(); + // methods are sorted, so do binary search + int l = 0; + int h = len - 1; + while (l <= h) { + int mid = (l + h) >> 1; + Method m = (Method) methods.getObjAt(mid); + int res = m.getName().fastCompare(name); + if (res == 0) { + // found matching name; do linear search to find matching signature + // first, quick check for common case + if (m.getSignature().equals(signature)) return m; + // search downwards through overloaded methods + int i; + for (i = mid - 1; i >= l; i--) { + Method m1 = (Method) methods.getObjAt(i); + if (!m1.getName().equals(name)) break; + if (m1.getSignature().equals(signature)) return m1; + } + // search upwards + for (i = mid + 1; i <= h; i++) { + Method m1 = (Method) methods.getObjAt(i); + if (!m1.getName().equals(name)) break; + if (m1.getSignature().equals(signature)) return m1; + } + // not found + if (Assert.ASSERTS_ENABLED) { + int index = linearSearch(methods, name, signature); + if (index != -1) { + throw new DebuggerException("binary search bug: should have found entry " + index); + } + } + return null; + } else if (res < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (Assert.ASSERTS_ENABLED) { + int index = linearSearch(methods, name, signature); + if (index != -1) { + throw new DebuggerException("binary search bug: should have found entry " + index); + } + } + return null; + } + + private static int linearSearch(ObjArray methods, Symbol name, Symbol signature) { + int len = (int) methods.getLength(); + for (int index = 0; index < len; index++) { + Method m = (Method) methods.getObjAt(index); + if (m.getSignature().equals(signature) && m.getName().equals(name)) { + return index; + } + } + return -1; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlassKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlassKlass.java new file mode 100644 index 00000000000..c74c4762b31 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlassKlass.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// An InstanceKlassKlass is the klass of an InstanceKlass. +// There only exist one instance Universe::instanceKlassKlassObj() + +public class InstanceKlassKlass extends KlassKlass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("instanceKlassKlass"); + headerSize = type.getSize() + Oop.getHeaderSize(); + } + + InstanceKlassKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static long headerSize; + + public long getObjectSize() { return alignObjectSize(headerSize); } + + public void printValueOn(PrintStream tty) { + tty.print("InstanceKlassKlass"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/IntField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/IntField.java new file mode 100644 index 00000000000..37c7b3493a9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/IntField.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.*; + +// The class for an int field simply provides access to the value. +public class IntField extends Field { + public IntField(FieldIdentifier id, long offset, boolean isVMField) { + super(id, offset, isVMField); + } + + public IntField(sun.jvm.hotspot.types.JIntField vmField, long startOffset) { + super(new NamedFieldIdentifier(vmField.getName()), vmField.getOffset() + startOffset, true); + } + + public IntField(InstanceKlass holder, int fieldArrayIndex) { + super(holder, fieldArrayIndex); + } + + public int getValue(Oop obj) { return obj.getHandle().getJIntAt(getOffset()); } + public void setValue(Oop obj, int value) throws MutationException { + // Fix this: setJIntAt is missing in Address + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/JVMDIClassStatus.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/JVMDIClassStatus.java new file mode 100644 index 00000000000..2d1450b810b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/JVMDIClassStatus.java @@ -0,0 +1,35 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +// from #defines in jvmdi.h. These are also part of JVMDI spec. +// refer to GetClassStatus method of JVMDI spec. + +public interface JVMDIClassStatus { + public static int VERIFIED = 0x01; + public static int PREPARED = 0x02; + public static int INITIALIZED = 0x04; + public static int ERROR = 0x08; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Klass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Klass.java new file mode 100644 index 00000000000..256a6bd4ef1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Klass.java @@ -0,0 +1,210 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class Klass extends Oop implements ClassConstants { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + // anon-enum constants for _layout_helper. + public static int LH_INSTANCE_SLOW_PATH_BIT; + public static int LH_LOG2_ELEMENT_SIZE_SHIFT; + public static int LH_ELEMENT_TYPE_SHIFT; + public static int LH_HEADER_SIZE_SHIFT; + public static int LH_ARRAY_TAG_SHIFT; + public static int LH_ARRAY_TAG_TYPE_VALUE; + public static int LH_ARRAY_TAG_OBJ_VALUE; + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("Klass"); + javaMirror = new OopField(type.getOopField("_java_mirror"), Oop.getHeaderSize()); + superField = new OopField(type.getOopField("_super"), Oop.getHeaderSize()); + layoutHelper = new IntField(type.getJIntField("_layout_helper"), Oop.getHeaderSize()); + name = new OopField(type.getOopField("_name"), Oop.getHeaderSize()); + accessFlags = new CIntField(type.getCIntegerField("_access_flags"), Oop.getHeaderSize()); + subklass = new OopField(type.getOopField("_subklass"), Oop.getHeaderSize()); + nextSibling = new OopField(type.getOopField("_next_sibling"), Oop.getHeaderSize()); + allocCount = new CIntField(type.getCIntegerField("_alloc_count"), Oop.getHeaderSize()); + + LH_INSTANCE_SLOW_PATH_BIT = db.lookupIntConstant("Klass::_lh_instance_slow_path_bit").intValue(); + LH_LOG2_ELEMENT_SIZE_SHIFT = db.lookupIntConstant("Klass::_lh_log2_element_size_shift").intValue(); + LH_ELEMENT_TYPE_SHIFT = db.lookupIntConstant("Klass::_lh_element_type_shift").intValue(); + LH_HEADER_SIZE_SHIFT = db.lookupIntConstant("Klass::_lh_header_size_shift").intValue(); + LH_ARRAY_TAG_SHIFT = db.lookupIntConstant("Klass::_lh_array_tag_shift").intValue(); + LH_ARRAY_TAG_TYPE_VALUE = db.lookupIntConstant("Klass::_lh_array_tag_type_value").intValue(); + LH_ARRAY_TAG_OBJ_VALUE = db.lookupIntConstant("Klass::_lh_array_tag_obj_value").intValue(); + } + + Klass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + // jvmdi support - see also class_status in VM code + public int getClassStatus() { + return 0; // overridden in derived classes + } + + public boolean isKlass() { return true; } + + // Fields + private static OopField javaMirror; + private static OopField superField; + private static IntField layoutHelper; + private static OopField name; + private static CIntField accessFlags; + private static OopField subklass; + private static OopField nextSibling; + private static CIntField allocCount; + + // Accessors for declared fields + public Instance getJavaMirror() { return (Instance) javaMirror.getValue(this); } + public Klass getSuper() { return (Klass) superField.getValue(this); } + public Klass getJavaSuper() { return null; } + public int getLayoutHelper() { return (int) layoutHelper.getValue(this); } + public Symbol getName() { return (Symbol) name.getValue(this); } + public long getAccessFlags() { return accessFlags.getValue(this); } + // Convenience routine + public AccessFlags getAccessFlagsObj(){ return new AccessFlags(getAccessFlags()); } + public Klass getSubklassKlass() { return (Klass) subklass.getValue(this); } + public Klass getNextSiblingKlass() { return (Klass) nextSibling.getValue(this); } + public long getAllocCount() { return allocCount.getValue(this); } + + // computed access flags - takes care of inner classes etc. + // This is closer to actual source level than getAccessFlags() etc. + public long computeModifierFlags() { + return 0L; // Unless overridden, modifier_flags is 0. + } + + // same as JVM_GetClassModifiers + public final long getClassModifiers() { + // unlike the VM counterpart we never have to deal with primitive type, + // because we operator on Klass and not an instance of java.lang.Class. + long flags = computeModifierFlags(); + if (isSuper()) { + flags |= JVM_ACC_SUPER; + } + return flags; + } + + // subclass check + public boolean isSubclassOf(Klass k) { + if (k != null) { + Klass t = this; + // Run up the super chain and check + while (t != null) { + if (t.equals(k)) return true; + t = t.getSuper(); + } + } + return false; + } + + // subtype check + public boolean isSubtypeOf(Klass k) { + return computeSubtypeOf(k); + } + + boolean computeSubtypeOf(Klass k) { + return isSubclassOf(k); + } + + // Find LCA (Least Common Ancester) in class heirarchy + public Klass lca( Klass k2 ) { + Klass k1 = this; + while ( true ) { + if ( k1.isSubtypeOf(k2) ) return k2; + if ( k2.isSubtypeOf(k1) ) return k1; + k1 = k1.getSuper(); + k2 = k2.getSuper(); + } + } + + public void printValueOn(PrintStream tty) { + tty.print("Klass"); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doOop(javaMirror, true); + visitor.doOop(superField, true); + visitor.doInt(layoutHelper, true); + visitor.doOop(name, true); + visitor.doCInt(accessFlags, true); + visitor.doOop(subklass, true); + visitor.doOop(nextSibling, true); + visitor.doCInt(allocCount, true); + } + } + + public long getObjectSize() { + System.out.println("should not reach here"); + return 0; + } + + /** Array class with specific rank */ + public Klass arrayKlass(int rank) { return arrayKlassImpl(false, rank); } + /** Array class with this klass as element type */ + public Klass arrayKlass() { return arrayKlassImpl(false); } + /** These will return null instead of allocating on the heap */ + public Klass arrayKlassOrNull(int rank) { return arrayKlassImpl(true, rank); } + public Klass arrayKlassOrNull() { return arrayKlassImpl(true); } + + public Klass arrayKlassImpl(boolean orNull, int rank) { + throw new RuntimeException("array_klass should be dispatched to instanceKlass, objArrayKlass or typeArrayKlass"); + } + + public Klass arrayKlassImpl(boolean orNull) { + throw new RuntimeException("array_klass should be dispatched to instanceKlass, objArrayKlass or typeArrayKlass"); + } + + // This returns the name in the form java/lang/String which isn't really a signature + // The subclasses override this to produce the correct form, eg + // Ljava/lang/String; For ArrayKlasses getName itself is the signature. + public String signature() { return getName().asString(); } + + // Convenience routines + public boolean isPublic() { return getAccessFlagsObj().isPublic(); } + public boolean isFinal() { return getAccessFlagsObj().isFinal(); } + public boolean isInterface() { return getAccessFlagsObj().isInterface(); } + public boolean isAbstract() { return getAccessFlagsObj().isAbstract(); } + public boolean isSuper() { return getAccessFlagsObj().isSuper(); } + public boolean isSynthetic() { return getAccessFlagsObj().isSynthetic(); } + public boolean hasFinalizer() { return getAccessFlagsObj().hasFinalizer(); } + public boolean isCloneable() { return getAccessFlagsObj().isCloneable(); } + public boolean hasVanillaConstructor() { return getAccessFlagsObj().hasVanillaConstructor(); } + public boolean hasMirandaMethods () { return getAccessFlagsObj().hasMirandaMethods(); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/KlassKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/KlassKlass.java new file mode 100644 index 00000000000..1cf1b1b2424 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/KlassKlass.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// A KlassKlass serves as the fix point of the klass chain. +// The klass of KlassKlass is itself. + +public class KlassKlass extends Klass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("klassKlass"); + headerSize = type.getSize() + Oop.getHeaderSize(); + } + + KlassKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static long headerSize; + + public long getObjectSize() { return alignObjectSize(headerSize); } + + public void printValueOn(PrintStream tty) { + tty.print("KlassKlass"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/LineNumberTableElement.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/LineNumberTableElement.java new file mode 100644 index 00000000000..9b32042435a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/LineNumberTableElement.java @@ -0,0 +1,52 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class LineNumberTableElement { + private int start_bci; + private int line_number; + + public LineNumberTableElement(int start_bci, int line_number) { + this.start_bci = start_bci; + this.line_number = line_number; + } + + public int getStartBCI() { + return this.start_bci; + } + + public int getLineNumber() { + return this.line_number; + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/LocalVariableTableElement.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/LocalVariableTableElement.java new file mode 100644 index 00000000000..306a7fc0a58 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/LocalVariableTableElement.java @@ -0,0 +1,92 @@ +/* + * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class LocalVariableTableElement { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("LocalVariableTableElement"); + offsetOfStartBCI = type.getCIntegerField("start_bci").getOffset(); + offsetOfLength = type.getCIntegerField("length").getOffset(); + offsetOfNameCPIndex = type.getCIntegerField("name_cp_index").getOffset(); + offsetOfDescriptorCPIndex = type.getCIntegerField("descriptor_cp_index").getOffset(); + offsetOfSignatureCPIndex = type.getCIntegerField("signature_cp_index").getOffset(); + offsetOfSlot = type.getCIntegerField("slot").getOffset(); + } + + private static long offsetOfStartBCI; + private static long offsetOfLength; + private static long offsetOfNameCPIndex; + private static long offsetOfDescriptorCPIndex; + private static long offsetOfSignatureCPIndex; + private static long offsetOfSlot; + + private OopHandle handle; + private long offset; + + public LocalVariableTableElement(OopHandle handle, long offset) { + this.handle = handle; + this.offset = offset; + } + + public int getStartBCI() { + return (int) handle.getCIntegerAt(offset + offsetOfStartBCI, 2, true); + } + + public int getLength() { + return (int) handle.getCIntegerAt(offset + offsetOfLength, 2, true); + } + + public int getNameCPIndex() { + return (int) handle.getCIntegerAt(offset + offsetOfNameCPIndex, 2, true); + } + + public int getDescriptorCPIndex() { + return (int) handle.getCIntegerAt(offset + offsetOfDescriptorCPIndex, 2, true); + } + + public int getSignatureCPIndex() { + return (int) handle.getCIntegerAt(offset + offsetOfSignatureCPIndex, 2, true); + } + + public int getSlot() { + return (int) handle.getCIntegerAt(offset + offsetOfSlot, 2, true); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/LongField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/LongField.java new file mode 100644 index 00000000000..1810fb95dd0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/LongField.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.*; + +// The class for a long field simply provides access to the value. +public class LongField extends Field { + public LongField(FieldIdentifier id, long offset, boolean isVMField) { + super(id, offset, isVMField); + } + + public LongField(sun.jvm.hotspot.types.JLongField vmField, long startOffset) { + super(new NamedFieldIdentifier(vmField.getName()), vmField.getOffset() + startOffset, true); + } + + public LongField(InstanceKlass holder, int fieldArrayIndex) { + super(holder, fieldArrayIndex); + } + + public long getValue(Oop obj) { return obj.getHandle().getJLongAt(getOffset()); } + public void setValue(Oop obj, long value) { + // Fix this: setJLongAt is missing in Address + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Mark.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Mark.java new file mode 100644 index 00000000000..efaa6f4e939 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Mark.java @@ -0,0 +1,293 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** Mark is the analogue of the VM's markOop. In this system it does + not subclass Oop but VMObject. For a mark on the stack, the mark's + address will be an Address; for a mark in the header of an object, + it will be an OopHandle. It is assumed in a couple of places in + this code that the mark is the first word in an object. */ + +public class Mark extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("oopDesc"); + markField = type.getCIntegerField("_mark"); + + ageBits = db.lookupLongConstant("markOopDesc::age_bits").longValue(); + lockBits = db.lookupLongConstant("markOopDesc::lock_bits").longValue(); + biasedLockBits = db.lookupLongConstant("markOopDesc::biased_lock_bits").longValue(); + maxHashBits = db.lookupLongConstant("markOopDesc::max_hash_bits").longValue(); + hashBits = db.lookupLongConstant("markOopDesc::hash_bits").longValue(); + lockShift = db.lookupLongConstant("markOopDesc::lock_shift").longValue(); + biasedLockShift = db.lookupLongConstant("markOopDesc::biased_lock_shift").longValue(); + ageShift = db.lookupLongConstant("markOopDesc::age_shift").longValue(); + hashShift = db.lookupLongConstant("markOopDesc::hash_shift").longValue(); + lockMask = db.lookupLongConstant("markOopDesc::lock_mask").longValue(); + lockMaskInPlace = db.lookupLongConstant("markOopDesc::lock_mask_in_place").longValue(); + biasedLockMask = db.lookupLongConstant("markOopDesc::biased_lock_mask").longValue(); + biasedLockMaskInPlace = db.lookupLongConstant("markOopDesc::biased_lock_mask_in_place").longValue(); + biasedLockBitInPlace = db.lookupLongConstant("markOopDesc::biased_lock_bit_in_place").longValue(); + ageMask = db.lookupLongConstant("markOopDesc::age_mask").longValue(); + ageMaskInPlace = db.lookupLongConstant("markOopDesc::age_mask_in_place").longValue(); + hashMask = db.lookupLongConstant("markOopDesc::hash_mask").longValue(); + hashMaskInPlace = db.lookupLongConstant("markOopDesc::hash_mask_in_place").longValue(); + biasedLockAlignment = db.lookupLongConstant("markOopDesc::biased_lock_alignment").longValue(); + lockedValue = db.lookupLongConstant("markOopDesc::locked_value").longValue(); + unlockedValue = db.lookupLongConstant("markOopDesc::unlocked_value").longValue(); + monitorValue = db.lookupLongConstant("markOopDesc::monitor_value").longValue(); + markedValue = db.lookupLongConstant("markOopDesc::marked_value").longValue(); + biasedLockPattern = db.lookupLongConstant("markOopDesc::biased_lock_pattern").longValue(); + noHash = db.lookupLongConstant("markOopDesc::no_hash").longValue(); + noHashInPlace = db.lookupLongConstant("markOopDesc::no_hash_in_place").longValue(); + noLockInPlace = db.lookupLongConstant("markOopDesc::no_lock_in_place").longValue(); + maxAge = db.lookupLongConstant("markOopDesc::max_age").longValue(); + } + + // Field accessors + private static CIntegerField markField; + + // Constants -- read from VM + private static long ageBits; + private static long lockBits; + private static long biasedLockBits; + private static long maxHashBits; + private static long hashBits; + + private static long lockShift; + private static long biasedLockShift; + private static long ageShift; + private static long hashShift; + + private static long lockMask; + private static long lockMaskInPlace; + private static long biasedLockMask; + private static long biasedLockMaskInPlace; + private static long biasedLockBitInPlace; + private static long ageMask; + private static long ageMaskInPlace; + private static long hashMask; + private static long hashMaskInPlace; + private static long biasedLockAlignment; + + private static long lockedValue; + private static long unlockedValue; + private static long monitorValue; + private static long markedValue; + private static long biasedLockPattern; + + private static long noHash; + + private static long noHashInPlace; + private static long noLockInPlace; + + private static long maxAge; + + public Mark(Address addr) { + super(addr); + } + + public long value() { + return markField.getValue(addr); + } + + public Address valueAsAddress() { + return addr.getAddressAt(markField.getOffset()); + } + + // Biased locking accessors + // These must be checked by all code which calls into the + // ObjectSynchoronizer and other code. The biasing is not understood + // by the lower-level CAS-based locking code, although the runtime + // fixes up biased locks to be compatible with it when a bias is + // revoked. + public boolean hasBiasPattern() { + return (Bits.maskBitsLong(value(), biasedLockMaskInPlace) == biasedLockPattern); + } + + public JavaThread biasedLocker() { + Threads threads = VM.getVM().getThreads(); + Address addr = valueAsAddress().andWithMask(~(biasedLockMaskInPlace & ageMaskInPlace)); + return threads.createJavaThreadWrapper(addr); + } + + // Indicates that the mark gas the bias bit set but that it has not + // yet been biased toward a particular thread + public boolean isBiasedAnonymously() { + return hasBiasPattern() && (biasedLocker() == null); + } + + // lock accessors (note that these assume lock_shift == 0) + public boolean isLocked() { + return (Bits.maskBitsLong(value(), lockMaskInPlace) != unlockedValue); + } + public boolean isUnlocked() { + return (Bits.maskBitsLong(value(), biasedLockMaskInPlace) == unlockedValue); + } + public boolean isMarked() { + return (Bits.maskBitsLong(value(), lockMaskInPlace) == markedValue); + } + + // Special temporary state of the markOop while being inflated. + // Code that looks at mark outside a lock need to take this into account. + public boolean isBeingInflated() { + return (value() == 0); + } + + // Should this header be preserved during GC? + public boolean mustBePreserved() { + return (!isUnlocked() || !hasNoHash()); + } + + // WARNING: The following routines are used EXCLUSIVELY by + // synchronization functions. They are not really gc safe. + // They must get updated if markOop layout get changed. + + // FIXME + // markOop set_unlocked() const { + // return markOop(value() | unlocked_value); + // } + public boolean hasLocker() { + return ((value() & lockMaskInPlace) == lockedValue); + } + public BasicLock locker() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(hasLocker(), "check"); + } + return new BasicLock(valueAsAddress()); + } + public boolean hasMonitor() { + return ((value() & monitorValue) != 0); + } + public ObjectMonitor monitor() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(hasMonitor(), "check"); + } + // Use xor instead of &~ to provide one extra tag-bit check. + Address monAddr = valueAsAddress().xorWithMask(monitorValue); + return new ObjectMonitor(monAddr); + } + public boolean hasDisplacedMarkHelper() { + return ((value() & unlockedValue) == 0); + } + public Mark displacedMarkHelper() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(hasDisplacedMarkHelper(), "check"); + } + Address addr = valueAsAddress().andWithMask(~monitorValue); + return new Mark(addr.getAddressAt(0)); + } + // FIXME + // void set_displaced_mark_helper(markOop m) const { + // assert(has_displaced_mark_helper(), "check"); + // intptr_t ptr = (value() & ~monitor_value); + // *(markOop*)ptr = m; + // } + // markOop copy_set_hash(intptr_t hash) const { + // intptr_t tmp = value() & (~hash_mask_in_place); + // tmp |= ((hash & hash_mask) << hash_shift); + // return (markOop)tmp; + // } + // it is only used to be stored into BasicLock as the + // indicator that the lock is using heavyweight monitor + // static markOop unused_mark() { + // return (markOop) marked_value; + // } + // // the following two functions create the markOop to be + // // stored into object header, it encodes monitor info + // static markOop encode(BasicLock* lock) { + // return (markOop) lock; + // } + // static markOop encode(ObjectMonitor* monitor) { + // intptr_t tmp = (intptr_t) monitor; + // return (markOop) (tmp | monitor_value); + // } + // used for alignment-based marking to reuse the busy state to encode pointers + // (see markOop_alignment.hpp) + // markOop clear_lock_bits() { return markOop(value() & ~lock_mask_in_place); } + // + // // age operations + // markOop set_marked() { return markOop((value() & ~lock_mask_in_place) | marked_value); } + // + public int age() { return (int) Bits.maskBitsLong(value() >> ageShift, ageMask); } + // markOop set_age(int v) const { + // assert((v & ~age_mask) == 0, "shouldn't overflow age field"); + // return markOop((value() & ~age_mask_in_place) | (((intptr_t)v & age_mask) << age_shift)); + // } + // markOop incr_age() const { return age() == max_age ? markOop(this) : set_age(age() + 1); } + + // hash operations + public long hash() { + return Bits.maskBitsLong(value() >> hashShift, hashMask); + } + + public boolean hasNoHash() { + return hash() == noHash; + } + + // FIXME + // Prototype mark for initialization + // static markOop prototype() { + // return markOop( no_hash_in_place | no_lock_in_place ); + // } + + // Debugging + public void printOn(PrintStream tty) { + if (isLocked()) { + tty.print("locked(0x" + + Long.toHexString(value()) + ")->"); + displacedMarkHelper().printOn(tty); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isUnlocked(), "just checking"); + } + tty.print("mark("); + tty.print("hash " + Long.toHexString(hash()) + ","); + tty.print("age " + age() + ")"); + } + } + + // FIXME + // // Prepare address of oop for placement into mark + // inline static markOop encode_pointer_as_mark(void* p) { return markOop(p)->set_marked(); } + // + // // Recover address of oop from encoded form used in mark + // inline void* decode_pointer() { return clear_lock_bits(); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Method.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Method.java new file mode 100644 index 00000000000..789e38e474b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Method.java @@ -0,0 +1,324 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +// A Method represents a Java method + +public class Method extends Oop { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("methodOopDesc"); + constMethod = new OopField(type.getOopField("_constMethod"), 0); + constants = new OopField(type.getOopField("_constants"), 0); + methodSize = new CIntField(type.getCIntegerField("_method_size"), 0); + maxStack = new CIntField(type.getCIntegerField("_max_stack"), 0); + maxLocals = new CIntField(type.getCIntegerField("_max_locals"), 0); + sizeOfParameters = new CIntField(type.getCIntegerField("_size_of_parameters"), 0); + accessFlags = new CIntField(type.getCIntegerField("_access_flags"), 0); + code = type.getAddressField("_code"); + vtableIndex = new CIntField(type.getCIntegerField("_vtable_index"), 0); + if (!VM.getVM().isCore()) { + invocationCounter = new CIntField(type.getCIntegerField("_invocation_counter"), 0); + } + bytecodeOffset = type.getSize(); + + /* + interpreterEntry = type.getAddressField("_interpreter_entry"); + fromCompiledCodeEntryPoint = type.getAddressField("_from_compiled_code_entry_point"); + + */ + objectInitializerName = null; + classInitializerName = null; + } + + Method(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public boolean isMethod() { return true; } + + // Fields + private static OopField constMethod; + private static OopField constants; + private static CIntField methodSize; + private static CIntField maxStack; + private static CIntField maxLocals; + private static CIntField sizeOfParameters; + private static CIntField accessFlags; + private static CIntField vtableIndex; + private static CIntField invocationCounter; + private static long bytecodeOffset; + + private static AddressField code; + + // constant method names - , + // Initialized lazily to avoid initialization ordering dependencies between Method and SymbolTable + private static Symbol objectInitializerName; + private static Symbol classInitializerName; + private static Symbol objectInitializerName() { + if (objectInitializerName == null) { + objectInitializerName = VM.getVM().getSymbolTable().probe(""); + } + return objectInitializerName; + } + private static Symbol classInitializerName() { + if (classInitializerName == null) { + classInitializerName = VM.getVM().getSymbolTable().probe(""); + } + return classInitializerName; + } + + + /* + private static AddressCField interpreterEntry; + private static AddressCField fromCompiledCodeEntryPoint; + */ + + // Accessors for declared fields + public ConstMethod getConstMethod() { return (ConstMethod) constMethod.getValue(this); } + public ConstantPool getConstants() { return (ConstantPool) constants.getValue(this); } + public TypeArray getExceptionTable() { return getConstMethod().getExceptionTable(); } + /** WARNING: this is in words, not useful in this system; use getObjectSize() instead */ + public long getMethodSize() { return methodSize.getValue(this); } + public long getMaxStack() { return maxStack.getValue(this); } + public long getMaxLocals() { return maxLocals.getValue(this); } + public long getSizeOfParameters() { return sizeOfParameters.getValue(this); } + public long getNameIndex() { return getConstMethod().getNameIndex(); } + public long getSignatureIndex() { return getConstMethod().getSignatureIndex(); } + public long getGenericSignatureIndex() { return getConstMethod().getGenericSignatureIndex(); } + public long getAccessFlags() { return accessFlags.getValue(this); } + public long getCodeSize() { return getConstMethod().getCodeSize(); } + public long getVtableIndex() { return vtableIndex.getValue(this); } + public long getInvocationCounter() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!VM.getVM().isCore(), "must not be used in core build"); + } + return invocationCounter.getValue(this); + } + + // get associated compiled native method, if available, else return null. + public NMethod getNativeMethod() { + Address addr = code.getValue(getHandle()); + return (NMethod) VMObjectFactory.newObject(NMethod.class, addr); + } + + // Convenience routine + public AccessFlags getAccessFlagsObj() { + return new AccessFlags(getAccessFlags()); + } + + /** Get a bytecode or breakpoint at the given bci */ + public int getBytecodeOrBPAt(int bci) { + return getConstMethod().getBytecodeOrBPAt(bci); + } + + /** Fetch the original non-breakpoint bytecode at the specified + bci. It is required that there is currently a bytecode at this + bci. */ + public int getOrigBytecodeAt(int bci) { + BreakpointInfo bp = ((InstanceKlass) getMethodHolder()).getBreakpoints(); + for (; bp != null; bp = bp.getNext()) { + if (bp.match(this, bci)) { + return bp.getOrigBytecode(); + } + } + System.err.println("Requested bci " + bci); + for (; bp != null; bp = bp.getNext()) { + System.err.println("Breakpoint at bci " + bp.getBCI() + ", bytecode " + + bp.getOrigBytecode()); + } + Assert.that(false, "Should not reach here"); + return -1; // not reached + } + + public byte getBytecodeByteArg(int bci) { + return getConstMethod().getBytecodeByteArg(bci); + } + + /** Fetches a 16-bit big-endian ("Java ordered") value from the + bytecode stream */ + public short getBytecodeShortArg(int bci) { + return getConstMethod().getBytecodeShortArg(bci); + } + + /** Fetches a 32-bit big-endian ("Java ordered") value from the + bytecode stream */ + public int getBytecodeIntArg(int bci) { + return getConstMethod().getBytecodeIntArg(bci); + } + + public byte[] getByteCode() { + return getConstMethod().getByteCode(); + } + + /* + public Address getCode() { return codeField.getValue(this); } + public Address getInterpreterEntry() { return interpreterEntryField.getValue(this); } + public Address getFromCompiledCodeEntryPoint() { return fromCompiledCodeEntryPointField.getValue(this); } + */ + // Accessors + public Symbol getName() { return (Symbol) getConstants().getObjAt(getNameIndex()); } + public Symbol getSignature() { return (Symbol) getConstants().getObjAt(getSignatureIndex()); } + public Symbol getGenericSignature() { + long index = getGenericSignatureIndex(); + return (index != 0L) ? (Symbol) getConstants().getObjAt(index) : null; + } + + // Method holder (the Klass holding this method) + public Klass getMethodHolder() { return getConstants().getPoolHolder(); } + + // Access flags + public boolean isPublic() { return getAccessFlagsObj().isPublic(); } + public boolean isPrivate() { return getAccessFlagsObj().isPrivate(); } + public boolean isProtected() { return getAccessFlagsObj().isProtected(); } + public boolean isPackagePrivate() { AccessFlags af = getAccessFlagsObj(); + return (!af.isPublic() && !af.isPrivate() && !af.isProtected()); } + public boolean isStatic() { return getAccessFlagsObj().isStatic(); } + public boolean isFinal() { return getAccessFlagsObj().isFinal(); } + public boolean isSynchronized() { return getAccessFlagsObj().isSynchronized(); } + public boolean isBridge() { return getAccessFlagsObj().isBridge(); } + public boolean isVarArgs() { return getAccessFlagsObj().isVarArgs(); } + public boolean isNative() { return getAccessFlagsObj().isNative(); } + public boolean isAbstract() { return getAccessFlagsObj().isAbstract(); } + public boolean isStrict() { return getAccessFlagsObj().isStrict(); } + public boolean isSynthetic() { return getAccessFlagsObj().isSynthetic(); } + + public boolean isConstructor() { + return (!isStatic()) && getName().equals(objectInitializerName()); + } + + public boolean isStaticInitializer() { + return isStatic() && getName().equals(classInitializerName()); + } + + public boolean isObsolete() { + return getAccessFlagsObj().isObsolete(); + } + + public OopMapCacheEntry getMaskFor(int bci) { + OopMapCacheEntry entry = new OopMapCacheEntry(); + entry.fill(this, bci); + return entry; + } + + public long getObjectSize() { + return getMethodSize() * getHeap().getOopSize(); + } + + public void printValueOn(PrintStream tty) { + tty.print("Method " + getName().asString() + getSignature().asString() + "@" + getHandle()); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doOop(constMethod, true); + visitor.doOop(constants, true); + visitor.doCInt(methodSize, true); + visitor.doCInt(maxStack, true); + visitor.doCInt(maxLocals, true); + visitor.doCInt(sizeOfParameters, true); + visitor.doCInt(accessFlags, true); + } + } + + public boolean hasLineNumberTable() { + return getConstMethod().hasLineNumberTable(); + } + + public int getLineNumberFromBCI(int bci) { + return getConstMethod().getLineNumberFromBCI(bci); + } + + public LineNumberTableElement[] getLineNumberTable() { + return getConstMethod().getLineNumberTable(); + } + + public boolean hasLocalVariableTable() { + return getConstMethod().hasLocalVariableTable(); + } + + /** Should only be called if table is present */ + public LocalVariableTableElement[] getLocalVariableTable() { + return getConstMethod().getLocalVariableTable(); + } + + public Symbol getLocalVariableName(int bci, int slot) { + if (! hasLocalVariableTable()) { + return null; + } + + LocalVariableTableElement[] locals = getLocalVariableTable(); + for (int l = 0; l < locals.length; l++) { + LocalVariableTableElement local = locals[l]; + if ((bci >= local.getStartBCI()) && + (bci < (local.getStartBCI() + local.getLength())) && + slot == local.getSlot()) { + return getConstants().getSymbolAt(local.getNameCPIndex()); + } + } + + return null; + } + + public boolean hasCheckedExceptions() { + return getConstMethod().hasCheckedExceptions(); + } + + /** Should only be called if table is present */ + public CheckedExceptionElement[] getCheckedExceptions() { + return getConstMethod().getCheckedExceptions(); + } + + /** Returns name and signature in external form for debugging + purposes */ + public String externalNameAndSignature() { + final StringBuffer buf = new StringBuffer(); + buf.append(getMethodHolder().getName().asString()); + buf.append("."); + buf.append(getName().asString()); + buf.append("("); + new SignatureConverter(getSignature(), buf).iterateParameters(); + buf.append(")"); + return buf.toString().replace('/', '.'); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MethodData.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MethodData.java new file mode 100644 index 00000000000..561acbda56b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MethodData.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +// A MethodData provides interpreter profiling information + +public class MethodData extends Oop { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("methodDataOopDesc"); + baseOffset = type.getSize(); + + size = new CIntField(type.getCIntegerField("_size"), 0); + method = new OopField(type.getOopField("_method"), 0); + // FIXME: add more fields and accessors + } + + MethodData(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public boolean isMethodData() { return true; } + + private static long baseOffset; + private static CIntField size; + private static OopField method; + + public long getObjectSize() { + return alignObjectSize(size.getValue(this)); + } + + public Method getMethod() { + return (Method) method.getValue(this); + } + + public void printValueOn(PrintStream tty) { + Method m = getMethod(); + tty.print("MethodData for " + m.getName().asString() + m.getSignature().asString()); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doOop(method, true); + visitor.doCInt(size, true); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MethodDataKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MethodDataKlass.java new file mode 100644 index 00000000000..17934d2b52f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MethodDataKlass.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// The MethodDataKlass is the klass of a MethodData oop + +public class MethodDataKlass extends Klass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("methodDataKlass"); + headerSize = type.getSize() + Oop.getHeaderSize(); + } + + MethodDataKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static long headerSize; + + public long getObjectSize() { return alignObjectSize(headerSize); } + + public void printValueOn(PrintStream tty) { + tty.print("MethodDataKlass"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MethodKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MethodKlass.java new file mode 100644 index 00000000000..bfb02f6bf22 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MethodKlass.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// The MethodKlass is the klass of a Method + +public class MethodKlass extends Klass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("methodKlass"); + headerSize = type.getSize() + Oop.getHeaderSize(); + } + + MethodKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static long headerSize; + + public long getObjectSize() { return alignObjectSize(headerSize); } + + public void printValueOn(PrintStream tty) { + tty.print("MethodKlass"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MutationException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MutationException.java new file mode 100644 index 00000000000..f54b7208c4c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/MutationException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +public class MutationException extends RuntimeException { + public MutationException() { + super(); + } + + public MutationException(String detail) { + super(detail); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/NamedFieldIdentifier.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/NamedFieldIdentifier.java new file mode 100644 index 00000000000..316959df6ee --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/NamedFieldIdentifier.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; + +// A NamedFieldIdentifier describes a field in an Oop with a name +public class NamedFieldIdentifier extends FieldIdentifier { + + public NamedFieldIdentifier(String name) { + this.name = name; + } + + private String name; + + public String getName() { return name; } + + public void printOn(PrintStream tty) { + tty.print(" - " + getName() + ":\t"); + } + + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (!(obj instanceof NamedFieldIdentifier)) { + return false; + } + + return ((NamedFieldIdentifier) obj).getName().equals(name); + } + + public int hashCode() { + return name.hashCode(); + } +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjArray.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjArray.java new file mode 100644 index 00000000000..ff558489f14 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjArray.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// An ObjArray is an array containing oops + +public class ObjArray extends Array { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("objArrayOopDesc"); + elementSize = db.getOopSize(); + } + + ObjArray(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public boolean isObjArray() { return true; } + + private static long elementSize; + + public Oop getObjAt(long index) { + long offset = baseOffsetInBytes(BasicType.T_OBJECT) + (index * elementSize); + return getHeap().newOop(getHandle().getOopHandleAt(offset)); + } + + public void printValueOn(PrintStream tty) { + tty.print("ObjArray"); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + int length = (int) getLength(); + long baseOffset = baseOffsetInBytes(BasicType.T_OBJECT); + for (int index = 0; index < length; index++) { + long offset = baseOffset + (index * elementSize); + visitor.doOop(new OopField(new IndexableFieldIdentifier(index), offset, false), false); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjArrayKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjArrayKlass.java new file mode 100644 index 00000000000..294c2f4f96d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjArrayKlass.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +// ObjArrayKlass is the klass for ObjArrays + +public class ObjArrayKlass extends ArrayKlass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("objArrayKlass"); + elementKlass = new OopField(type.getOopField("_element_klass"), Oop.getHeaderSize()); + bottomKlass = new OopField(type.getOopField("_bottom_klass"), Oop.getHeaderSize()); + } + + ObjArrayKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static OopField elementKlass; + private static OopField bottomKlass; + + public Klass getElementKlass() { return (Klass) elementKlass.getValue(this); } + public Klass getBottomKlass() { return (Klass) bottomKlass.getValue(this); } + + public long computeModifierFlags() { + long elementFlags = getElementKlass().computeModifierFlags(); + long arrayFlags = 0L; + if ((elementFlags & (JVM_ACC_PUBLIC | JVM_ACC_PROTECTED)) != 0) { + // The array type is public if the component type is public or protected + arrayFlags = JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC; + } else { + // The array type is private if the component type is private + arrayFlags = JVM_ACC_ABSTRACT | JVM_ACC_FINAL; + } + return arrayFlags; + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doOop(elementKlass, true); + visitor.doOop(bottomKlass, true); + } + } + + public Klass arrayKlassImpl(boolean orNull, int n) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(getDimension() <= n, "check order of chain"); + } + int dimension = (int) getDimension(); + if (dimension == n) { + return this; + } + ObjArrayKlass ak = (ObjArrayKlass) getHigherDimension(); + if (ak == null) { + if (orNull) return null; + // FIXME: would need to change in reflective system to actually + // allocate klass + throw new RuntimeException("Can not allocate array klasses in debugging system"); + } + + if (orNull) { + return ak.arrayKlassOrNull(n); + } + return ak.arrayKlass(n); + } + + public Klass arrayKlassImpl(boolean orNull) { + return arrayKlassImpl(orNull, (int) (getDimension() + 1)); + } + + public void printValueOn(PrintStream tty) { + tty.print("ObjArrayKlass for "); + getElementKlass().printValueOn(tty); + } +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjArrayKlassKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjArrayKlassKlass.java new file mode 100644 index 00000000000..1c998c93e2f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjArrayKlassKlass.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// An ObjArrayKlassKlass is klass for ObjArrayKlass' +// We only have one + +public class ObjArrayKlassKlass extends ArrayKlassKlass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("objArrayKlassKlass"); + headerSize = type.getSize() + Oop.getHeaderSize(); + } + + ObjArrayKlassKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static long headerSize; + + public long getObjectSize() { return alignObjectSize(headerSize); } + + public void printValueOn(PrintStream tty) { + tty.print("ObjArrayKlassKlass"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java new file mode 100644 index 00000000000..fe849e527d8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java @@ -0,0 +1,635 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// The ObjectHeap is an abstraction over all generations in the VM +// It gives access to all present objects and classes. +// + +package sun.jvm.hotspot.oops; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.gc_implementation.parallelScavenge.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class ObjectHeap { + + private OopHandle symbolKlassHandle; + private OopHandle methodKlassHandle; + private OopHandle constMethodKlassHandle; + private OopHandle methodDataKlassHandle; + private OopHandle constantPoolKlassHandle; + private OopHandle constantPoolCacheKlassHandle; + private OopHandle klassKlassHandle; + private OopHandle instanceKlassKlassHandle; + private OopHandle typeArrayKlassKlassHandle; + private OopHandle objArrayKlassKlassHandle; + private OopHandle boolArrayKlassHandle; + private OopHandle byteArrayKlassHandle; + private OopHandle charArrayKlassHandle; + private OopHandle intArrayKlassHandle; + private OopHandle shortArrayKlassHandle; + private OopHandle longArrayKlassHandle; + private OopHandle singleArrayKlassHandle; + private OopHandle doubleArrayKlassHandle; + private OopHandle arrayKlassKlassHandle; + private OopHandle compiledICHolderKlassHandle; + + private SymbolKlass symbolKlassObj; + private MethodKlass methodKlassObj; + private ConstMethodKlass constMethodKlassObj; + private MethodDataKlass methodDataKlassObj; + private ConstantPoolKlass constantPoolKlassObj; + private ConstantPoolCacheKlass constantPoolCacheKlassObj; + private KlassKlass klassKlassObj; + private InstanceKlassKlass instanceKlassKlassObj; + private TypeArrayKlassKlass typeArrayKlassKlassObj; + private ObjArrayKlassKlass objArrayKlassKlassObj; + private TypeArrayKlass boolArrayKlassObj; + private TypeArrayKlass byteArrayKlassObj; + private TypeArrayKlass charArrayKlassObj; + private TypeArrayKlass intArrayKlassObj; + private TypeArrayKlass shortArrayKlassObj; + private TypeArrayKlass longArrayKlassObj; + private TypeArrayKlass singleArrayKlassObj; + private TypeArrayKlass doubleArrayKlassObj; + private ArrayKlassKlass arrayKlassKlassObj; + private CompiledICHolderKlass compiledICHolderKlassObj; + + public void initialize(TypeDataBase db) throws WrongTypeException { + // Lookup the roots in the object hierarchy. + Type universeType = db.lookupType("Universe"); + + symbolKlassHandle = universeType.getOopField("_symbolKlassObj").getValue(); + symbolKlassObj = new SymbolKlass(symbolKlassHandle, this); + + methodKlassHandle = universeType.getOopField("_methodKlassObj").getValue(); + methodKlassObj = new MethodKlass(methodKlassHandle, this); + + constMethodKlassHandle = universeType.getOopField("_constMethodKlassObj").getValue(); + constMethodKlassObj = new ConstMethodKlass(constMethodKlassHandle, this); + + constantPoolKlassHandle = universeType.getOopField("_constantPoolKlassObj").getValue(); + constantPoolKlassObj = new ConstantPoolKlass(constantPoolKlassHandle, this); + + constantPoolCacheKlassHandle = universeType.getOopField("_constantPoolCacheKlassObj").getValue(); + constantPoolCacheKlassObj = new ConstantPoolCacheKlass(constantPoolCacheKlassHandle, this); + + klassKlassHandle = universeType.getOopField("_klassKlassObj").getValue(); + klassKlassObj = new KlassKlass(klassKlassHandle, this); + + arrayKlassKlassHandle = universeType.getOopField("_arrayKlassKlassObj").getValue(); + arrayKlassKlassObj = new ArrayKlassKlass(arrayKlassKlassHandle, this); + + instanceKlassKlassHandle = universeType.getOopField("_instanceKlassKlassObj").getValue(); + instanceKlassKlassObj = new InstanceKlassKlass(instanceKlassKlassHandle, this); + + typeArrayKlassKlassHandle = universeType.getOopField("_typeArrayKlassKlassObj").getValue(); + typeArrayKlassKlassObj = new TypeArrayKlassKlass(typeArrayKlassKlassHandle, this); + + objArrayKlassKlassHandle = universeType.getOopField("_objArrayKlassKlassObj").getValue(); + objArrayKlassKlassObj = new ObjArrayKlassKlass(objArrayKlassKlassHandle, this); + + boolArrayKlassHandle = universeType.getOopField("_boolArrayKlassObj").getValue(); + boolArrayKlassObj = new TypeArrayKlass(boolArrayKlassHandle, this); + + byteArrayKlassHandle = universeType.getOopField("_byteArrayKlassObj").getValue(); + byteArrayKlassObj = new TypeArrayKlass(byteArrayKlassHandle, this); + + charArrayKlassHandle = universeType.getOopField("_charArrayKlassObj").getValue(); + charArrayKlassObj = new TypeArrayKlass(charArrayKlassHandle, this); + + intArrayKlassHandle = universeType.getOopField("_intArrayKlassObj").getValue(); + intArrayKlassObj = new TypeArrayKlass(intArrayKlassHandle, this); + + shortArrayKlassHandle = universeType.getOopField("_shortArrayKlassObj").getValue(); + shortArrayKlassObj = new TypeArrayKlass(shortArrayKlassHandle, this); + + longArrayKlassHandle = universeType.getOopField("_longArrayKlassObj").getValue(); + longArrayKlassObj = new TypeArrayKlass(longArrayKlassHandle, this); + + singleArrayKlassHandle = universeType.getOopField("_singleArrayKlassObj").getValue(); + singleArrayKlassObj = new TypeArrayKlass(singleArrayKlassHandle, this); + + doubleArrayKlassHandle = universeType.getOopField("_doubleArrayKlassObj").getValue(); + doubleArrayKlassObj = new TypeArrayKlass(doubleArrayKlassHandle, this); + + if (!VM.getVM().isCore()) { + methodDataKlassHandle = universeType.getOopField("_methodDataKlassObj").getValue(); + methodDataKlassObj = new MethodDataKlass(methodDataKlassHandle, this); + + compiledICHolderKlassHandle = universeType.getOopField("_compiledICHolderKlassObj").getValue(); + compiledICHolderKlassObj= new CompiledICHolderKlass(compiledICHolderKlassHandle ,this); + } + } + + public ObjectHeap(TypeDataBase db) throws WrongTypeException { + // Get commonly used sizes of basic types + oopSize = db.getOopSize(); + byteSize = db.getJByteType().getSize(); + charSize = db.getJCharType().getSize(); + booleanSize = db.getJBooleanType().getSize(); + intSize = db.getJIntType().getSize(); + shortSize = db.getJShortType().getSize(); + longSize = db.getJLongType().getSize(); + floatSize = db.getJFloatType().getSize(); + doubleSize = db.getJDoubleType().getSize(); + + initialize(db); + } + + /** Comparison operation for oops, either or both of which may be null */ + public boolean equal(Oop o1, Oop o2) { + if (o1 != null) return o1.equals(o2); + return (o2 == null); + } + + // Cached sizes of basic types + private long oopSize; + private long byteSize; + private long charSize; + private long booleanSize; + private long intSize; + private long shortSize; + private long longSize; + private long floatSize; + private long doubleSize; + + public long getOopSize() { return oopSize; } + public long getByteSize() { return byteSize; } + public long getCharSize() { return charSize; } + public long getBooleanSize() { return booleanSize; } + public long getIntSize() { return intSize; } + public long getShortSize() { return shortSize; } + public long getLongSize() { return longSize; } + public long getFloatSize() { return floatSize; } + public long getDoubleSize() { return doubleSize; } + + // Accessors for well-known system classes (from Universe) + public SymbolKlass getSymbolKlassObj() { return symbolKlassObj; } + public MethodKlass getMethodKlassObj() { return methodKlassObj; } + public ConstMethodKlass getConstMethodKlassObj() { return constMethodKlassObj; } + public MethodDataKlass getMethodDataKlassObj() { return methodDataKlassObj; } + public ConstantPoolKlass getConstantPoolKlassObj() { return constantPoolKlassObj; } + public ConstantPoolCacheKlass getConstantPoolCacheKlassObj() { return constantPoolCacheKlassObj; } + public KlassKlass getKlassKlassObj() { return klassKlassObj; } + public ArrayKlassKlass getArrayKlassKlassObj() { return arrayKlassKlassObj; } + public InstanceKlassKlass getInstanceKlassKlassObj() { return instanceKlassKlassObj; } + public ObjArrayKlassKlass getObjArrayKlassKlassObj() { return objArrayKlassKlassObj; } + public TypeArrayKlassKlass getTypeArrayKlassKlassObj() { return typeArrayKlassKlassObj; } + public TypeArrayKlass getBoolArrayKlassObj() { return boolArrayKlassObj; } + public TypeArrayKlass getByteArrayKlassObj() { return byteArrayKlassObj; } + public TypeArrayKlass getCharArrayKlassObj() { return charArrayKlassObj; } + public TypeArrayKlass getIntArrayKlassObj() { return intArrayKlassObj; } + public TypeArrayKlass getShortArrayKlassObj() { return shortArrayKlassObj; } + public TypeArrayKlass getLongArrayKlassObj() { return longArrayKlassObj; } + public TypeArrayKlass getSingleArrayKlassObj() { return singleArrayKlassObj; } + public TypeArrayKlass getDoubleArrayKlassObj() { return doubleArrayKlassObj; } + public CompiledICHolderKlass getCompiledICHolderKlassObj() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!VM.getVM().isCore(), "must not be called for core build"); + } + return compiledICHolderKlassObj; + } + + /** Takes a BasicType and returns the corresponding primitive array + klass */ + public Klass typeArrayKlassObj(int t) { + if (t == BasicType.getTBoolean()) return getBoolArrayKlassObj(); + if (t == BasicType.getTChar()) return getCharArrayKlassObj(); + if (t == BasicType.getTFloat()) return getSingleArrayKlassObj(); + if (t == BasicType.getTDouble()) return getDoubleArrayKlassObj(); + if (t == BasicType.getTByte()) return getByteArrayKlassObj(); + if (t == BasicType.getTShort()) return getShortArrayKlassObj(); + if (t == BasicType.getTInt()) return getIntArrayKlassObj(); + if (t == BasicType.getTLong()) return getLongArrayKlassObj(); + throw new RuntimeException("Illegal basic type " + t); + } + + /** an interface to filter objects while walking heap */ + public static interface ObjectFilter { + public boolean canInclude(Oop obj); + } + + /** The base heap iteration mechanism */ + public void iterate(HeapVisitor visitor) { + iterateLiveRegions(collectLiveRegions(), visitor, null); + } + + /** iterate objects satisfying a specified ObjectFilter */ + public void iterate(HeapVisitor visitor, ObjectFilter of) { + iterateLiveRegions(collectLiveRegions(), visitor, of); + } + + /** iterate objects of given Klass. param 'includeSubtypes' tells whether to + * include objects of subtypes or not */ + public void iterateObjectsOfKlass(HeapVisitor visitor, final Klass k, boolean includeSubtypes) { + if (includeSubtypes) { + if (k.isFinal()) { + // do the simpler "exact" klass loop + iterateExact(visitor, k); + } else { + iterateSubtypes(visitor, k); + } + } else { + // there can no object of abstract classes and interfaces + if (!k.isAbstract() && !k.isInterface()) { + iterateExact(visitor, k); + } + } + } + + /** iterate objects of given Klass (objects of subtypes included) */ + public void iterateObjectsOfKlass(HeapVisitor visitor, final Klass k) { + iterateObjectsOfKlass(visitor, k, true); + } + + /** This routine can be used to iterate through the heap at an + extremely low level (stepping word-by-word) to provide the + ability to do very low-level debugging */ + public void iterateRaw(RawHeapVisitor visitor) { + List liveRegions = collectLiveRegions(); + + // Summarize size + long totalSize = 0; + for (int i = 0; i < liveRegions.size(); i += 2) { + Address bottom = (Address) liveRegions.get(i); + Address top = (Address) liveRegions.get(i+1); + totalSize += top.minus(bottom); + } + visitor.prologue(totalSize); + + for (int i = 0; i < liveRegions.size(); i += 2) { + Address bottom = (Address) liveRegions.get(i); + Address top = (Address) liveRegions.get(i+1); + + // Traverses the space from bottom to top + while (bottom.lessThan(top)) { + visitor.visitAddress(bottom); + bottom = bottom.addOffsetTo(VM.getVM().getAddressSize()); + } + } + + visitor.epilogue(); + } + + // Iterates through only the perm generation for the purpose of + // finding static fields for liveness analysis + public void iteratePerm(HeapVisitor visitor) { + CollectedHeap heap = VM.getVM().getUniverse().heap(); + List liveRegions = new ArrayList(); + addPermGenLiveRegions(liveRegions, heap); + sortLiveRegions(liveRegions); + iterateLiveRegions(liveRegions, visitor, null); + } + + // Creates an instance from the Oop hierarchy based based on the handle + public Oop newOop(OopHandle handle) { + // The only known way to detect the right type of an oop is + // traversing the class chain until a well-known klass is recognized. + // A more direct solution would require the klasses to expose + // the C++ vtbl structure. + + // Handle the null reference + if (handle == null) return null; + + // First check if handle is one of the root objects + if (handle.equals(methodKlassHandle)) return getMethodKlassObj(); + if (handle.equals(constMethodKlassHandle)) return getConstMethodKlassObj(); + if (handle.equals(symbolKlassHandle)) return getSymbolKlassObj(); + if (handle.equals(constantPoolKlassHandle)) return getConstantPoolKlassObj(); + if (handle.equals(constantPoolCacheKlassHandle)) return getConstantPoolCacheKlassObj(); + if (handle.equals(instanceKlassKlassHandle)) return getInstanceKlassKlassObj(); + if (handle.equals(objArrayKlassKlassHandle)) return getObjArrayKlassKlassObj(); + if (handle.equals(klassKlassHandle)) return getKlassKlassObj(); + if (handle.equals(arrayKlassKlassHandle)) return getArrayKlassKlassObj(); + if (handle.equals(typeArrayKlassKlassHandle)) return getTypeArrayKlassKlassObj(); + if (handle.equals(boolArrayKlassHandle)) return getBoolArrayKlassObj(); + if (handle.equals(byteArrayKlassHandle)) return getByteArrayKlassObj(); + if (handle.equals(charArrayKlassHandle)) return getCharArrayKlassObj(); + if (handle.equals(intArrayKlassHandle)) return getIntArrayKlassObj(); + if (handle.equals(shortArrayKlassHandle)) return getShortArrayKlassObj(); + if (handle.equals(longArrayKlassHandle)) return getLongArrayKlassObj(); + if (handle.equals(singleArrayKlassHandle)) return getSingleArrayKlassObj(); + if (handle.equals(doubleArrayKlassHandle)) return getDoubleArrayKlassObj(); + if (!VM.getVM().isCore()) { + if (handle.equals(compiledICHolderKlassHandle)) return getCompiledICHolderKlassObj(); + if (handle.equals(methodDataKlassHandle)) return getMethodDataKlassObj(); + } + + // Then check if obj.klass() is one of the root objects + OopHandle klass = Oop.getKlassForOopHandle(handle); + if (klass != null) { + if (klass.equals(methodKlassHandle)) return new Method(handle, this); + if (klass.equals(constMethodKlassHandle)) return new ConstMethod(handle, this); + if (klass.equals(symbolKlassHandle)) return new Symbol(handle, this); + if (klass.equals(constantPoolKlassHandle)) return new ConstantPool(handle, this); + if (klass.equals(constantPoolCacheKlassHandle)) return new ConstantPoolCache(handle, this); + if (!VM.getVM().isCore()) { + if (klass.equals(compiledICHolderKlassHandle)) return new CompiledICHolder(handle, this); + if (klass.equals(methodDataKlassHandle)) return new MethodData(handle, this); + } + if (klass.equals(instanceKlassKlassHandle)) return new InstanceKlass(handle, this); + if (klass.equals(objArrayKlassKlassHandle)) return new ObjArrayKlass(handle, this); + if (klass.equals(typeArrayKlassKlassHandle)) return new TypeArrayKlass(handle, this); + + // Lastly check if obj.klass().klass() is on of the root objects + OopHandle klassKlass = Oop.getKlassForOopHandle(klass); + if (klassKlass != null) { + if (klassKlass.equals(instanceKlassKlassHandle)) return new Instance(handle, this); + if (klassKlass.equals(objArrayKlassKlassHandle)) return new ObjArray(handle, this); + if (klassKlass.equals(typeArrayKlassKlassHandle)) return new TypeArray(handle, this); + } + } + + System.err.println("Unknown oop at " + handle); + System.err.println("Oop's klass is " + klass); + + throw new UnknownOopException(); + } + + // Print all objects in the object heap + public void print() { + HeapPrinter printer = new HeapPrinter(System.out); + iterate(printer); + } + + //--------------------------------------------------------------------------- + // Internals only below this point + // + + private void iterateExact(HeapVisitor visitor, final Klass k) { + iterateLiveRegions(collectLiveRegions(), visitor, new ObjectFilter() { + public boolean canInclude(Oop obj) { + Klass tk = obj.getKlass(); + // null Klass is seen sometimes! + return (tk != null && tk.equals(k)); + } + }); + } + + private void iterateSubtypes(HeapVisitor visitor, final Klass k) { + iterateLiveRegions(collectLiveRegions(), visitor, new ObjectFilter() { + public boolean canInclude(Oop obj) { + Klass tk = obj.getKlass(); + // null Klass is seen sometimes! + return (tk != null && tk.isSubtypeOf(k)); + } + }); + } + + private void iterateLiveRegions(List liveRegions, HeapVisitor visitor, ObjectFilter of) { + // Summarize size + long totalSize = 0; + for (int i = 0; i < liveRegions.size(); i += 2) { + Address bottom = (Address) liveRegions.get(i); + Address top = (Address) liveRegions.get(i+1); + totalSize += top.minus(bottom); + } + visitor.prologue(totalSize); + + CompactibleFreeListSpace cmsSpaceOld = null; + CompactibleFreeListSpace cmsSpacePerm = null; + CollectedHeap heap = VM.getVM().getUniverse().heap(); + + if (heap instanceof GenCollectedHeap) { + GenCollectedHeap genHeap = (GenCollectedHeap) heap; + Generation genOld = genHeap.getGen(1); + Generation genPerm = genHeap.permGen(); + if (genOld instanceof ConcurrentMarkSweepGeneration) { + ConcurrentMarkSweepGeneration concGen = (ConcurrentMarkSweepGeneration)genOld; + cmsSpaceOld = concGen.cmsSpace(); + } + if (genPerm instanceof ConcurrentMarkSweepGeneration) { + ConcurrentMarkSweepGeneration concGen = (ConcurrentMarkSweepGeneration)genPerm; + cmsSpacePerm = concGen.cmsSpace(); + } + } + + for (int i = 0; i < liveRegions.size(); i += 2) { + Address bottom = (Address) liveRegions.get(i); + Address top = (Address) liveRegions.get(i+1); + + try { + // Traverses the space from bottom to top + OopHandle handle = bottom.addOffsetToAsOopHandle(0); + while (handle.lessThan(top)) { + Oop obj = null; + + try { + obj = newOop(handle); + } catch (UnknownOopException exp) { + } + if (obj == null) { + //Find the object size using Printezis bits and skip over + System.err.println("Finding object size using Printezis bits and skipping over..."); + long size = 0; + + if ( (cmsSpaceOld != null) && cmsSpaceOld.contains(handle) ){ + size = cmsSpaceOld.collector().blockSizeUsingPrintezisBits(handle); + } else if ((cmsSpacePerm != null) && cmsSpacePerm.contains(handle) ){ + size = cmsSpacePerm.collector().blockSizeUsingPrintezisBits(handle); + } + + if (size <= 0) { + //Either Printezis bits not set or handle is not in cms space. + throw new UnknownOopException(); + } + + handle = handle.addOffsetToAsOopHandle(CompactibleFreeListSpace.adjustObjectSizeInBytes(size)); + continue; + } + if (of == null || of.canInclude(obj)) { + if (visitor.doObj(obj)) { + // doObj() returns true to abort this loop. + break; + } + } + if ( (cmsSpaceOld != null) && cmsSpaceOld.contains(handle) || + (cmsSpacePerm != null) && cmsSpacePerm.contains(handle) ) { + handle = handle.addOffsetToAsOopHandle(CompactibleFreeListSpace.adjustObjectSizeInBytes(obj.getObjectSize()) ); + } else { + handle = handle.addOffsetToAsOopHandle(obj.getObjectSize()); + } + } + } + catch (AddressException e) { + // This is okay at the top of these regions + } + catch (UnknownOopException e) { + // This is okay at the top of these regions + } + } + + visitor.epilogue(); + } + + private void addPermGenLiveRegions(List output, CollectedHeap heap) { + LiveRegionsCollector lrc = new LiveRegionsCollector(output); + if (heap instanceof GenCollectedHeap) { + GenCollectedHeap genHeap = (GenCollectedHeap) heap; + Generation gen = genHeap.permGen(); + gen.spaceIterate(lrc, true); + } else if (heap instanceof ParallelScavengeHeap) { + ParallelScavengeHeap psh = (ParallelScavengeHeap) heap; + PSPermGen permGen = psh.permGen(); + addLiveRegions(permGen.objectSpace().getLiveRegions(), output); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "Expecting GenCollectedHeap or ParallelScavengeHeap, but got " + + heap.getClass().getName()); + } + } + } + + private void addLiveRegions(List input, List output) { + for (Iterator itr = input.iterator(); itr.hasNext();) { + MemRegion reg = (MemRegion) itr.next(); + Address top = reg.end(); + Address bottom = reg.start(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(top != null, "top address in a live region should not be null"); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(bottom != null, "bottom address in a live region should not be null"); + } + output.add(top); + output.add(bottom); + } + } + + private class LiveRegionsCollector implements SpaceClosure { + LiveRegionsCollector(List l) { + liveRegions = l; + } + + public void doSpace(Space s) { + addLiveRegions(s.getLiveRegions(), liveRegions); + } + private List liveRegions; + } + + // Returns a List
where the addresses come in pairs. These + // designate the live regions of the heap. + private List collectLiveRegions() { + // We want to iterate through all live portions of the heap, but + // do not want to abort the heap traversal prematurely if we find + // a problem (like an allocated but uninitialized object at the + // top of a generation). To do this we enumerate all generations' + // bottom and top regions, and factor in TLABs if necessary. + + // List
. Addresses come in pairs. + List liveRegions = new ArrayList(); + LiveRegionsCollector lrc = new LiveRegionsCollector(liveRegions); + + CollectedHeap heap = VM.getVM().getUniverse().heap(); + + if (heap instanceof GenCollectedHeap) { + GenCollectedHeap genHeap = (GenCollectedHeap) heap; + // Run through all generations, obtaining bottom-top pairs. + for (int i = 0; i < genHeap.nGens(); i++) { + Generation gen = genHeap.getGen(i); + gen.spaceIterate(lrc, true); + } + } else if (heap instanceof ParallelScavengeHeap) { + ParallelScavengeHeap psh = (ParallelScavengeHeap) heap; + PSYoungGen youngGen = psh.youngGen(); + // Add eden space + addLiveRegions(youngGen.edenSpace().getLiveRegions(), liveRegions); + // Add from-space but not to-space + addLiveRegions(youngGen.fromSpace().getLiveRegions(), liveRegions); + PSOldGen oldGen = psh.oldGen(); + addLiveRegions(oldGen.objectSpace().getLiveRegions(), liveRegions); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "Expecting GenCollectedHeap or ParallelScavengeHeap, but got " + + heap.getClass().getName()); + } + } + + // handle perm generation + addPermGenLiveRegions(liveRegions, heap); + + // If UseTLAB is enabled, snip out regions associated with TLABs' + // dead regions. Note that TLABs can be present in any generation. + + // FIXME: consider adding fewer boundaries to live region list. + // Theoretically only need to stop at TLAB's top and resume at its + // end. + + if (VM.getVM().getUseTLAB()) { + for (JavaThread thread = VM.getVM().getThreads().first(); thread != null; thread = thread.next()) { + if (thread.isJavaThread()) { + ThreadLocalAllocBuffer tlab = thread.tlab(); + if (tlab.start() != null) { + if ((tlab.top() == null) || (tlab.end() == null)) { + System.err.print("Warning: skipping invalid TLAB for thread "); + thread.printThreadIDOn(System.err); + System.err.println(); + } else { + // Go from: + // - below start() to start() + // - start() to top() + // - end() and above + liveRegions.add(tlab.start()); + liveRegions.add(tlab.start()); + liveRegions.add(tlab.top()); + liveRegions.add(tlab.end()); + } + } + } + } + } + + // Now sort live regions + sortLiveRegions(liveRegions); + + if (Assert.ASSERTS_ENABLED) { + Assert.that(liveRegions.size() % 2 == 0, "Must have even number of region boundaries"); + } + + return liveRegions; + } + + private void sortLiveRegions(List liveRegions) { + Collections.sort(liveRegions, new Comparator() { + public int compare(Object o1, Object o2) { + Address a1 = (Address) o1; + Address a2 = (Address) o2; + if (AddressOps.lt(a1, a2)) { + return -1; + } else if (AddressOps.gt(a1, a2)) { + return 1; + } + return 0; + } + }); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjectHistogram.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjectHistogram.java new file mode 100644 index 00000000000..7e30ccf20b5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjectHistogram.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; + +public class ObjectHistogram implements HeapVisitor { + + public ObjectHistogram() { map = new HashMap(); } + + private HashMap map; + + public void prologue(long size) {} + + public boolean doObj(Oop obj) { + Klass klass = obj.getKlass(); + if (!map.containsKey(klass)) map.put(klass, new ObjectHistogramElement(klass)); + ((ObjectHistogramElement) map.get(klass)).updateWith(obj); + return false; + } + + public void epilogue() {} + + /** Call this after the iteration is complete to obtain the + ObjectHistogramElements in descending order of total heap size + consumed in the form of a List. */ + public List getElements() { + List list = new ArrayList(); + list.addAll(map.values()); + Collections.sort(list, new Comparator() { + public int compare(Object o1, Object o2) { + return ((ObjectHistogramElement) o1).compare((ObjectHistogramElement) o2); + } + }); + return list; + } + + public void print() { printOn(System.out); } + + public void printOn(PrintStream tty) { + List list = getElements(); + ObjectHistogramElement.titleOn(tty); + Iterator iterator = list.listIterator(); + while (iterator.hasNext()) { + ((ObjectHistogramElement) iterator.next()).printOn(tty); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjectHistogramElement.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjectHistogramElement.java new file mode 100644 index 00000000000..9b3df5310d7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ObjectHistogramElement.java @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; + +public class ObjectHistogramElement { + + private Klass klass; + private long count; // Number of instances of klass + private long size; // Total size of all these instances + + public ObjectHistogramElement(Klass k) { + klass = k; + count = 0; + size = 0; + } + + public void updateWith(Oop obj) { + count = count + 1; + size = size + obj.getObjectSize(); + } + + public int compare(ObjectHistogramElement other) { + return (int) (other.size - size); + } + + /** Klass for this ObjectHistogramElement */ + public Klass getKlass() { + return klass; + } + + /** Number of instances of klass */ + public long getCount() { + return count; + } + + /** Total size of all these instances */ + public long getSize() { + return size; + } + + private String getInternalName(Klass k) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + getKlass().printValueOn(new PrintStream(bos)); + // '*' is used to denote VM internal klasses. + return "* " + bos.toString(); + } + + /** Human readable description **/ + public String getDescription() { + Klass k = getKlass(); + if (k instanceof InstanceKlass) { + return k.getName().asString().replace('/', '.'); + } else if (k instanceof ArrayKlass) { + ArrayKlass ak = (ArrayKlass) k; + if (k instanceof TypeArrayKlass) { + TypeArrayKlass tak = (TypeArrayKlass) ak; + return tak.getElementTypeName() + "[]"; + } else if (k instanceof ObjArrayKlass) { + ObjArrayKlass oak = (ObjArrayKlass) ak; + // See whether it's a "system objArray" + if (oak.equals(VM.getVM().getUniverse().systemObjArrayKlassObj())) { + return "* System ObjArray"; + } + Klass bottom = oak.getBottomKlass(); + int dim = (int) oak.getDimension(); + StringBuffer buf = new StringBuffer(); + if (bottom instanceof TypeArrayKlass) { + buf.append(((TypeArrayKlass) bottom).getElementTypeName()); + } else if (bottom instanceof InstanceKlass) { + buf.append(bottom.getName().asString().replace('/', '.')); + } else { + throw new RuntimeException("should not reach here"); + } + for (int i=0; i < dim; i++) { + buf.append("[]"); + } + return buf.toString(); + } + } + return getInternalName(k); + } + + public static void titleOn(PrintStream tty) { + tty.println("Object Histogram:"); + tty.println(); + tty.println("Size" + "\t" + "Count" + "\t" + "Class description"); + tty.println("-------------------------------------------------------"); + } + + public void printOn(PrintStream tty) { + tty.print(size + "\t" + count + "\t"); + tty.print(getDescription()); + tty.println(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Oop.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Oop.java new file mode 100644 index 00000000000..0ace3595754 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Oop.java @@ -0,0 +1,224 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.memory.CompactingPermGenGen; + +// Oop represents the superclass for all types of +// objects in the HotSpot object heap. + +public class Oop { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("oopDesc"); + mark = new CIntField(type.getCIntegerField("_mark"), 0); + klass = new OopField(type.getOopField("_klass"), 0); + headerSize = type.getSize(); + } + + private OopHandle handle; + private ObjectHeap heap; + + Oop(OopHandle handle, ObjectHeap heap) { + this.handle = handle; + this.heap = heap; + } + + ObjectHeap getHeap() { return heap; } + + /** Should not be used or needed by most clients outside this + package; is needed, however, by {@link + sun.jvm.hotspot.utilities.MarkBits}. */ + public OopHandle getHandle() { return handle; } + + private static long headerSize; + public static long getHeaderSize() { return headerSize; } + + private static CIntField mark; + private static OopField klass; + + public boolean isShared() { + return CompactingPermGenGen.isShared(handle); + } + + public boolean isSharedReadOnly() { + return CompactingPermGenGen.isSharedReadOnly(handle); + } + + public boolean isSharedReadWrite() { + return CompactingPermGenGen.isSharedReadWrite(handle); + } + + // Accessors for declared fields + public Mark getMark() { return new Mark(getHandle()); } + public Klass getKlass() { return (Klass) klass.getValue(this); } + + public boolean isA(Klass k) { + return getKlass().isSubtypeOf(k); + } + + // Returns the byte size of this object + public long getObjectSize() { + Klass k = getKlass(); + if (k instanceof InstanceKlass) { + return ((InstanceKlass)k).getSizeHelper() + * VM.getVM().getAddressSize(); + } + // If it is not an instance, this method should be replaced. + return getHeaderSize(); + } + + // Type test operations + public boolean isInstance() { return false; } + public boolean isInstanceRef() { return false; } + public boolean isArray() { return false; } + public boolean isObjArray() { return false; } + public boolean isTypeArray() { return false; } + public boolean isSymbol() { return false; } + public boolean isKlass() { return false; } + public boolean isThread() { return false; } + public boolean isMethod() { return false; } + public boolean isMethodData() { return false; } + public boolean isConstantPool() { return false; } + public boolean isConstantPoolCache() { return false; } + public boolean isCompiledICHolder() { return false; } + + // Align the object size. + public static long alignObjectSize(long size) { + return VM.getVM().alignUp(size, VM.getVM().getMinObjAlignmentInBytes()); + } + + // All vm's align longs, so pad out certain offsets. + public static long alignObjectOffset(long offset) { + return VM.getVM().alignUp(offset, VM.getVM().getBytesPerLong()); + } + + public boolean equals(Object obj) { + if (obj != null && (obj instanceof Oop)) { + return getHandle().equals(((Oop) obj).getHandle()); + } + return false; + } + + public int hashCode() { return getHandle().hashCode(); } + + /** Identity hash in the target VM */ + public long identityHash() { + Mark mark = getMark(); + if (mark.isUnlocked() && (!mark.hasNoHash())) { + return (int) mark.hash(); + } else if (mark.isMarked()) { + return (int) mark.hash(); + } else { + return slowIdentityHash(); + } + } + + public long slowIdentityHash() { + return VM.getVM().getObjectSynchronizer().identityHashValueFor(this); + } + + public void iterate(OopVisitor visitor, boolean doVMFields) { + visitor.setObj(this); + visitor.prologue(); + iterateFields(visitor, doVMFields); + visitor.epilogue(); + } + + void iterateFields(OopVisitor visitor, boolean doVMFields) { + if (doVMFields) { + visitor.doCInt(mark, true); + visitor.doOop(klass, true); + } + } + + public void print() { printOn(System.out); } + public void printValue() { printValueOn(System.out); } + public void printRaw() { printRawOn(System.out); } + + public static void printOopValueOn(Oop obj, PrintStream tty) { + if (obj == null) { + tty.print("null"); + } else { + obj.printValueOn(tty); + tty.print(" @ " + obj.getHandle()); + } + } + + public static void printOopAddressOn(Oop obj, PrintStream tty) { + if (obj == null) { + tty.print("null"); + } else { + tty.print(obj.getHandle().toString()); + } + } + + public void printOn(PrintStream tty) { + OopPrinter printer = new OopPrinter(tty); + iterate(printer, true); + } + + public void printValueOn(PrintStream tty) { + try { + tty.print("Oop for " + getKlass().getName().asString()); + } catch (java.lang.NullPointerException e) { + tty.print("Oop"); + } + } + + public void printRawOn(PrintStream tty) { + tty.print("Dumping raw memory for "); + printValueOn(tty); + tty.println(); + long size = getObjectSize() * 4; + for (long i = 0; i < size; i += 4) { + long memVal = getHandle().getCIntegerAt(i, 4, true); + tty.println(Long.toHexString(memVal)); + } + } + + public boolean verify() { return true;} + + // Package-private routine to speed up ObjectHeap.newOop + static OopHandle getKlassForOopHandle(OopHandle handle) { + if (handle == null) { + return null; + } + return handle.getOopHandleAt(klass.getOffset()); + } +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopField.java new file mode 100644 index 00000000000..0082908fbf5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopField.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.*; + +// The class for an oop field simply provides access to the value. +public class OopField extends Field { + public OopField(FieldIdentifier id, long offset, boolean isVMField) { + super(id, offset, isVMField); + } + + public OopField(sun.jvm.hotspot.types.OopField vmField, long startOffset) { + super(new NamedFieldIdentifier(vmField.getName()), vmField.getOffset() + startOffset, true); + } + + public OopField(InstanceKlass holder, int fieldArrayIndex) { + super(holder, fieldArrayIndex); + } + + public Oop getValue(Oop obj) { + return obj.getHeap().newOop(getValueAsOopHandle(obj)); + } + + /** Debugging support */ + public OopHandle getValueAsOopHandle(Oop obj) { + return obj.getHandle().getOopHandleAt(getOffset()); + } + + public void setValue(Oop obj) throws MutationException { + // Fix this: setOopAt is missing in Address + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopPrinter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopPrinter.java new file mode 100644 index 00000000000..ee6ffa7cc40 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopPrinter.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; + +public class OopPrinter implements OopVisitor { + public OopPrinter(PrintStream tty) { + this.tty = tty; + } + + PrintStream tty; + + public void prologue() { + Oop.printOopValueOn(getObj(), tty); + tty.println(" (object size = " + getObj().getObjectSize() + ")"); + } + + public void epilogue() { + tty.println(); + } + + + private Oop obj; + public void setObj(Oop obj) { this.obj = obj; } + public Oop getObj() { return obj; } + + + private void printField(Field field) { + field.printOn(tty); + } + + public void doOop(OopField field, boolean isVMField) { + printField(field); + Oop.printOopValueOn(field.getValue(getObj()), tty); + tty.println(); + } + public void doChar(CharField field, boolean isVMField) { + printField(field); + char c = field.getValue(getObj()); + // Fix me: not yet complete + if (Character.isLetterOrDigit(c)) tty.println(c); + else tty.println((int)c); + } + public void doByte(ByteField field, boolean isVMField) { + printField(field); + tty.println(field.getValue(getObj())); + } + public void doBoolean(BooleanField field, boolean isVMField) { + printField(field); + tty.println(field.getValue(getObj())); + } + public void doShort(ShortField field, boolean isVMField) { + printField(field); + tty.println(field.getValue(getObj())); + } + public void doInt(IntField field, boolean isVMField) { + printField(field); + tty.println(field.getValue(getObj())); + } + public void doLong(LongField field, boolean isVMField) { + printField(field); + tty.println(field.getValue(getObj())); + } + public void doFloat(FloatField field, boolean isVMField) { + printField(field); + tty.println(field.getValue(getObj())); + } + public void doDouble(DoubleField field, boolean isVMField) { + printField(field); + tty.println(field.getValue(getObj())); + } + public void doCInt(CIntField field, boolean isVMField) { + printField(field); + tty.println(field.getValue(getObj())); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopUtilities.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopUtilities.java new file mode 100644 index 00000000000..d3b7c8b6f91 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopUtilities.java @@ -0,0 +1,315 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.TypeDataBase; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.jdi.JVMTIThreadState; + +/** A utility class encapsulating useful oop operations */ + +public class OopUtilities implements /* imports */ JVMTIThreadState { + + // FIXME: access should be synchronized and cleared when VM is + // resumed + // String fields + private static IntField offsetField; + private static IntField countField; + private static OopField valueField; + // ThreadGroup fields + private static OopField threadGroupParentField; + private static OopField threadGroupNameField; + private static IntField threadGroupNThreadsField; + private static OopField threadGroupThreadsField; + private static IntField threadGroupNGroupsField; + private static OopField threadGroupGroupsField; + // Thread fields + private static OopField threadNameField; + private static OopField threadGroupField; + private static LongField threadEETopField; + // threadStatus field is new since 1.5 + private static IntField threadStatusField; + // parkBlocker field is new since 1.6 + private static OopField threadParkBlockerField; + + // possible values of java_lang_Thread::ThreadStatus + private static int THREAD_STATUS_NEW; + /* + Other enum constants are not needed as of now. Uncomment these as and when needed. + + private static int THREAD_STATUS_RUNNABLE; + private static int THREAD_STATUS_SLEEPING; + private static int THREAD_STATUS_IN_OBJECT_WAIT; + private static int THREAD_STATUS_IN_OBJECT_WAIT_TIMED; + private static int THREAD_STATUS_PARKED; + private static int THREAD_STATUS_PARKED_TIMED; + private static int THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER; + private static int THREAD_STATUS_TERMINATED; + */ + + // java.lang.Class fields + private static OopField hcKlassField; + + // java.util.concurrent.locks.AbstractOwnableSynchronizer fields + private static OopField absOwnSyncOwnerThreadField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + // FIXME: don't need this observer; however, do need a VM resumed + // and suspended observer to refetch fields + } + + public static String charArrayToString(TypeArray charArray) { + if (charArray == null) { + return null; + } + return charArrayToString(charArray, 0, (int) charArray.getLength()); + } + + public static String charArrayToString(TypeArray charArray, int offset, int length) { + if (charArray == null) { + return null; + } + final int limit = offset + length; + if (Assert.ASSERTS_ENABLED) { + Assert.that(offset >= 0 && limit <= charArray.getLength(), "out of bounds"); + } + StringBuffer buf = new StringBuffer(length); + for (int i = offset; i < limit; i++) { + buf.append(charArray.getCharAt(i)); + } + return buf.toString(); + } + + public static String stringOopToString(Oop stringOop) { + if (offsetField == null) { + InstanceKlass k = (InstanceKlass) stringOop.getKlass(); + offsetField = (IntField) k.findField("offset", "I"); + countField = (IntField) k.findField("count", "I"); + valueField = (OopField) k.findField("value", "[C"); + if (Assert.ASSERTS_ENABLED) { + Assert.that(offsetField != null && + countField != null && + valueField != null, "must find all java.lang.String fields"); + } + } + return charArrayToString((TypeArray) valueField.getValue(stringOop), + offsetField.getValue(stringOop), + countField.getValue(stringOop)); + } + + private static void initThreadGroupFields() { + if (threadGroupParentField == null) { + SystemDictionary sysDict = VM.getVM().getSystemDictionary(); + InstanceKlass k = sysDict.getThreadGroupKlass(); + threadGroupParentField = (OopField) k.findField("parent", "Ljava/lang/ThreadGroup;"); + threadGroupNameField = (OopField) k.findField("name", "Ljava/lang/String;"); + threadGroupNThreadsField = (IntField) k.findField("nthreads", "I"); + threadGroupThreadsField = (OopField) k.findField("threads", "[Ljava/lang/Thread;"); + threadGroupNGroupsField = (IntField) k.findField("ngroups", "I"); + threadGroupGroupsField = (OopField) k.findField("groups", "[Ljava/lang/ThreadGroup;"); + if (Assert.ASSERTS_ENABLED) { + Assert.that(threadGroupParentField != null && + threadGroupNameField != null && + threadGroupNThreadsField != null && + threadGroupThreadsField != null && + threadGroupNGroupsField != null && + threadGroupGroupsField != null, "must find all java.lang.ThreadGroup fields"); + } + } + } + + public static Oop threadGroupOopGetParent(Oop threadGroupOop) { + initThreadGroupFields(); + return threadGroupParentField.getValue(threadGroupOop); + } + + public static String threadGroupOopGetName(Oop threadGroupOop) { + initThreadGroupFields(); + return stringOopToString(threadGroupNameField.getValue(threadGroupOop)); + } + + public static Oop[] threadGroupOopGetThreads(Oop threadGroupOop) { + initThreadGroupFields(); + int nthreads = threadGroupNThreadsField.getValue(threadGroupOop); + Oop[] result = new Oop[nthreads]; + ObjArray threads = (ObjArray) threadGroupThreadsField.getValue(threadGroupOop); + for (int i = 0; i < nthreads; i++) { + result[i] = threads.getObjAt(i); + } + return result; + } + + public static Oop[] threadGroupOopGetGroups(Oop threadGroupOop) { + initThreadGroupFields(); + int ngroups = threadGroupNGroupsField.getValue(threadGroupOop); + Oop[] result = new Oop[ngroups]; + ObjArray groups = (ObjArray) threadGroupGroupsField.getValue(threadGroupOop); + for (int i = 0; i < ngroups; i++) { + result[i] = groups.getObjAt(i); + } + return result; + } + + private static void initThreadFields() { + if (threadNameField == null) { + SystemDictionary sysDict = VM.getVM().getSystemDictionary(); + InstanceKlass k = sysDict.getThreadKlass(); + threadNameField = (OopField) k.findField("name", "[C"); + threadGroupField = (OopField) k.findField("group", "Ljava/lang/ThreadGroup;"); + threadEETopField = (LongField) k.findField("eetop", "J"); + threadStatusField = (IntField) k.findField("threadStatus", "I"); + threadParkBlockerField = (OopField) k.findField("parkBlocker", + "Ljava/lang/Object;"); + TypeDataBase db = VM.getVM().getTypeDataBase(); + THREAD_STATUS_NEW = db.lookupIntConstant("java_lang_Thread::NEW").intValue(); + /* + Other enum constants are not needed as of now. Uncomment these as and when needed. + + THREAD_STATUS_RUNNABLE = db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue(); + THREAD_STATUS_SLEEPING = db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue(); + THREAD_STATUS_IN_OBJECT_WAIT = db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue(); + THREAD_STATUS_IN_OBJECT_WAIT_TIMED = db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue(); + THREAD_STATUS_PARKED = db.lookupIntConstant("java_lang_Thread::PARKED").intValue(); + THREAD_STATUS_PARKED_TIMED = db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue(); + THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue(); + THREAD_STATUS_TERMINATED = db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue(); + */ + + if (Assert.ASSERTS_ENABLED) { + // it is okay to miss threadStatusField, because this was + // introduced only in 1.5 JDK. + Assert.that(threadNameField != null && + threadGroupField != null && + threadEETopField != null, "must find all java.lang.Thread fields"); + } + } + } + + public static Oop threadOopGetThreadGroup(Oop threadOop) { + initThreadFields(); + return threadGroupField.getValue(threadOop); + } + + public static String threadOopGetName(Oop threadOop) { + initThreadFields(); + return charArrayToString((TypeArray) threadNameField.getValue(threadOop)); + } + + /** May return null if, e.g., thread was not started */ + public static JavaThread threadOopGetJavaThread(Oop threadOop) { + initThreadFields(); + Address addr = threadOop.getHandle().getAddressAt(threadEETopField.getOffset()); + if (addr == null) { + return null; + } + return VM.getVM().getThreads().createJavaThreadWrapper(addr); + } + + /** returns value of java.lang.Thread.threadStatus field */ + public static int threadOopGetThreadStatus(Oop threadOop) { + initThreadFields(); + // The threadStatus is only present starting in 1.5 + if (threadStatusField != null) { + return (int) threadStatusField.getValue(threadOop); + } else { + // All we can easily figure out is if it is alive, but that is + // enough info for a valid unknown status. + JavaThread thr = threadOopGetJavaThread(threadOop); + if (thr == null) { + // the thread hasn't run yet or is in the process of exiting + return THREAD_STATUS_NEW; + } else { + return JVMTI_THREAD_STATE_ALIVE; + } + } + } + + /** returns value of java.lang.Thread.parkBlocker field */ + public static Oop threadOopGetParkBlocker(Oop threadOop) { + initThreadFields(); + if (threadParkBlockerField != null) { + return threadParkBlockerField.getValue(threadOop); + } + return null; + } + + // initialize fields for java.lang.Class + private static void initClassFields() { + if (hcKlassField == null) { + // hc_klass is a HotSpot magic field and hence we can't + // find it from InstanceKlass for java.lang.Class. + TypeDataBase db = VM.getVM().getTypeDataBase(); + int hcKlassOffset = (int) Oop.getHeaderSize(); + try { + hcKlassOffset += (db.lookupIntConstant("java_lang_Class::hc_klass_offset").intValue() * + db.getAddressSize()); + } catch (RuntimeException re) { + // ignore, currently java_lang_Class::hc_klass_offset is zero + } + + hcKlassField = new OopField(new NamedFieldIdentifier("hc_klass"), hcKlassOffset, true); + } + } + + /** get klassOop field at offset hc_klass_offset from a java.lang.Class object */ + public static Klass classOopToKlass(Oop aClass) { + initClassFields(); + return (Klass) hcKlassField.getValue(aClass); + } + + // initialize fields for j.u.c.l AbstractOwnableSynchornizer class + private static void initAbsOwnSyncFields() { + if (absOwnSyncOwnerThreadField == null) { + SystemDictionary sysDict = VM.getVM().getSystemDictionary(); + InstanceKlass k = sysDict.getAbstractOwnableSynchronizerKlass(); + absOwnSyncOwnerThreadField = + (OopField) k.findField("exclusiveOwnerThread", + "Ljava/lang/Thread;"); + } + } + + // return exclusiveOwnerThread field of AbstractOwnableSynchronizer class + public static Oop abstractOwnableSynchronizerGetOwnerThread(Oop oop) { + initAbsOwnSyncFields(); + if (absOwnSyncOwnerThreadField == null) { + return null; // pre-1.6 VM? + } else { + return absOwnSyncOwnerThreadField.getValue(oop); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopVisitor.java new file mode 100644 index 00000000000..35a671d0a0f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopVisitor.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +// An OopVisitor can be used to inspect all fields within an object. +// Fields include vm fields, java fields, indexable fields. + +public interface OopVisitor { + // Called before visiting an object + public void prologue(); + + // Called after visiting an object + public void epilogue(); + + public void setObj(Oop obj); + + // Returns the object being visited + public Oop getObj(); + + // Callback methods for each field type in an object + public void doOop(OopField field, boolean isVMField); + public void doByte(ByteField field, boolean isVMField); + public void doChar(CharField field, boolean isVMField); + public void doBoolean(BooleanField field, boolean isVMField); + public void doShort(ShortField field, boolean isVMField); + public void doInt(IntField field, boolean isVMField); + public void doLong(LongField field, boolean isVMField); + public void doFloat(FloatField field, boolean isVMField); + public void doDouble(DoubleField field, boolean isVMField); + public void doCInt(CIntField field, boolean isVMField); +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/RawHeapVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/RawHeapVisitor.java new file mode 100644 index 00000000000..5016387f286 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/RawHeapVisitor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.runtime.AddressVisitor; + +public interface RawHeapVisitor extends AddressVisitor { + /** This is called at the beginning of the iteration to provide the + RawHeapVisitor with information about the amount of memory which + will be traversed (for example, for displaying a progress bar) */ + public void prologue(long usedSize); + + /** This is called after the traversal is complete */ + public void epilogue(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ShortField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ShortField.java new file mode 100644 index 00000000000..3eeef74966c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ShortField.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.*; + +// The class for a short field simply provides access to the value. +public class ShortField extends Field { + public ShortField(FieldIdentifier id, long offset, boolean isVMField) { + super(id, offset, isVMField); + } + + public ShortField(sun.jvm.hotspot.types.JShortField vmField, long startOffset) { + super(new NamedFieldIdentifier(vmField.getName()), vmField.getOffset(), true); + } + + public ShortField(InstanceKlass holder, int fieldArrayIndex) { + super(holder, fieldArrayIndex); + } + + public short getValue(Oop obj) { return obj.getHandle().getJShortAt(getOffset()); } + public void setValue(Oop obj, short value) throws MutationException { + // Fix this: setJFloatAt is missing in Address + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Symbol.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Symbol.java new file mode 100644 index 00000000000..c7ec576ebe7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Symbol.java @@ -0,0 +1,149 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +// A Symbol is a canonicalized string. +// All Symbols reside in global symbolTable. + +public class Symbol extends Oop { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("symbolOopDesc"); + length = new CIntField(type.getCIntegerField("_length"), 0); + baseOffset = type.getField("_body").getOffset(); + } + + // Format: + // [header] + // [klass ] + // [length] byte size of uft8 string + // ..body.. + + Symbol(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public boolean isSymbol() { return true; } + + private static long baseOffset; // tells where the array part starts + + // Fields + private static CIntField length; + + // Accessors for declared fields + public long getLength() { return length.getValue(this); } + + public byte getByteAt(long index) { + return getHandle().getJByteAt(baseOffset + index); + } + + public boolean equals(byte[] modUTF8Chars) { + int l = (int) getLength(); + if (l != modUTF8Chars.length) return false; + while (l-- > 0) { + if (modUTF8Chars[l] != getByteAt(l)) return false; + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(l == -1, "we should be at the beginning"); + } + return true; + } + + public byte[] asByteArray() { + int length = (int) getLength(); + byte [] result = new byte [length]; + for (int index = 0; index < length; index++) { + result[index] = getByteAt(index); + } + return result; + } + + public String asString() { + // Decode the byte array and return the string. + try { + return readModifiedUTF8(asByteArray()); + } catch(IOException e) { + return null; + } + } + + public boolean startsWith(String str) { + return asString().startsWith(str); + } + + public void printValueOn(PrintStream tty) { + tty.print("#" + asString()); + } + + public long getObjectSize() { + return alignObjectSize(baseOffset + getLength()); + } + + void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doCInt(length, true); + int length = (int) getLength(); + for (int index = 0; index < length; index++) { + visitor.doByte(new ByteField(new IndexableFieldIdentifier(index), baseOffset + index, false), true); + } + } + } + + /** Note: this comparison is used for vtable sorting only; it + doesn't matter what order it defines, as long as it is a total, + time-invariant order Since symbolOops are in permSpace, their + relative order in memory never changes, so use address + comparison for speed. */ + public int fastCompare(Symbol other) { + return (int) getHandle().minus(other.getHandle()); + } + + private static String readModifiedUTF8(byte[] buf) throws IOException { + final int len = buf.length; + byte[] tmp = new byte[len + 2]; + // write modified UTF-8 length as short in big endian + tmp[0] = (byte) ((len >>> 8) & 0xFF); + tmp[1] = (byte) ((len >>> 0) & 0xFF); + // copy the data + System.arraycopy(buf, 0, tmp, 2, len); + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(tmp)); + return dis.readUTF(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/SymbolKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/SymbolKlass.java new file mode 100644 index 00000000000..e067d50fef7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/SymbolKlass.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// A SymbolKlass is the klass for all Symbols + +public class SymbolKlass extends Klass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("symbolKlass"); + headerSize = type.getSize() + Oop.getHeaderSize(); + } + + SymbolKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static long headerSize; + + public long getObjectSize() { return alignObjectSize(headerSize); } + + public void printValueOn(PrintStream tty) { + tty.print("SymbolKlass"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/TypeArray.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/TypeArray.java new file mode 100644 index 00000000000..50c009b5dc4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/TypeArray.java @@ -0,0 +1,151 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +// A TypeArray is an array containing basic types (non oop elements). +// It is used for arrays of {characters, singles, doubles, bytes, shorts, integers, longs} + +public class TypeArray extends Array { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("typeArrayOopDesc"); + } + + TypeArray(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + public boolean isTypeArray() { return true; } + + public byte getByteAt(long index) { + long offset = baseOffsetInBytes(BasicType.T_BYTE) + index * getHeap().getByteSize(); + return getHandle().getJByteAt(offset); + } + + public boolean getBooleanAt(long index) { + long offset = baseOffsetInBytes(BasicType.T_BOOLEAN) + index * getHeap().getBooleanSize(); + return getHandle().getJBooleanAt(offset); + } + + public char getCharAt(long index) { + long offset = baseOffsetInBytes(BasicType.T_CHAR) + index * getHeap().getCharSize(); + return getHandle().getJCharAt(offset); + } + + public int getIntAt(long index) { + long offset = baseOffsetInBytes(BasicType.T_INT) + index * getHeap().getIntSize(); + return getHandle().getJIntAt(offset); + } + + public short getShortAt(long index) { + long offset = baseOffsetInBytes(BasicType.T_SHORT) + index * getHeap().getShortSize(); + return getHandle().getJShortAt(offset); + } + + public long getLongAt(long index) { + long offset = baseOffsetInBytes(BasicType.T_LONG) + index * getHeap().getLongSize(); + return getHandle().getJLongAt(offset); + } + + public float getFloatAt(long index) { + long offset = baseOffsetInBytes(BasicType.T_FLOAT) + index * getHeap().getFloatSize(); + return getHandle().getJFloatAt(offset); + } + + public double getDoubleAt(long index) { + long offset = baseOffsetInBytes(BasicType.T_DOUBLE) + index * getHeap().getDoubleSize(); + return getHandle().getJDoubleAt(offset); + } + + public void printValueOn(PrintStream tty) { + TypeArrayKlass klass = (TypeArrayKlass) getKlass(); + tty.print(klass.getTypeName()); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + TypeArrayKlass klass = (TypeArrayKlass) getKlass(); + int length = (int) getLength(); + int type = (int) klass.getElementType(); + for (int index = 0; index < length; index++) { + FieldIdentifier id = new IndexableFieldIdentifier(index); + switch (type) { + case TypeArrayKlass.T_BOOLEAN: { + long offset = baseOffsetInBytes(BasicType.T_BOOLEAN) + index * getHeap().getBooleanSize(); + visitor.doBoolean(new BooleanField(id, offset, false), false); + break; + } + case TypeArrayKlass.T_CHAR: { + long offset = baseOffsetInBytes(BasicType.T_CHAR) + index * getHeap().getCharSize(); + visitor.doChar(new CharField(id, offset, false), false); + break; + } + case TypeArrayKlass.T_FLOAT: { + long offset = baseOffsetInBytes(BasicType.T_FLOAT) + index * getHeap().getFloatSize(); + visitor.doFloat(new FloatField(id, offset, false), false); + break; + } + case TypeArrayKlass.T_DOUBLE: { + long offset = baseOffsetInBytes(BasicType.T_DOUBLE) + index * getHeap().getDoubleSize(); + visitor.doDouble(new DoubleField(id, offset, false), false); + break; + } + case TypeArrayKlass.T_BYTE: { + long offset = baseOffsetInBytes(BasicType.T_BYTE) + index * getHeap().getByteSize(); + visitor.doByte(new ByteField(id, offset, false), false); + break; + } + case TypeArrayKlass.T_SHORT: { + long offset = baseOffsetInBytes(BasicType.T_SHORT) + index * getHeap().getShortSize(); + visitor.doShort(new ShortField(id, offset, false), false); + break; + } + case TypeArrayKlass.T_INT: { + long offset = baseOffsetInBytes(BasicType.T_INT) + index * getHeap().getIntSize(); + visitor.doInt(new IntField(id, offset, false), false); + break; + } + case TypeArrayKlass.T_LONG: { + long offset = baseOffsetInBytes(BasicType.T_LONG) + index * getHeap().getLongSize(); + visitor.doLong(new LongField(id, offset, false), false); + break; + } + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/TypeArrayKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/TypeArrayKlass.java new file mode 100644 index 00000000000..b8a41cb21cf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/TypeArrayKlass.java @@ -0,0 +1,129 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +// TypeArrayKlass is a proxy for typeArrayKlass in the JVM + +public class TypeArrayKlass extends ArrayKlass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type t = db.lookupType("typeArrayKlass"); + maxLength = new CIntField(t.getCIntegerField("_max_length"), Oop.getHeaderSize()); + } + + TypeArrayKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static CIntField maxLength; + + public long getMaxLength() { return maxLength.getValue(this); } + + public static final int T_BOOLEAN = 4; + public static final int T_CHAR = 5; + public static final int T_FLOAT = 6; + public static final int T_DOUBLE = 7; + public static final int T_BYTE = 8; + public static final int T_SHORT = 9; + public static final int T_INT = 10; + public static final int T_LONG = 11; + + public String getTypeName() { + switch ((int) getElementType()) { + case T_BOOLEAN: return "[Z"; + case T_CHAR: return "[C"; + case T_FLOAT: return "[F"; + case T_DOUBLE: return "[D"; + case T_BYTE: return "[B"; + case T_SHORT: return "[S"; + case T_INT: return "[I"; + case T_LONG: return "[J"; + } + return "Unknown TypeArray"; + } + + public String getElementTypeName() { + switch ((int) getElementType()) { + case T_BOOLEAN: return "boolean"; + case T_CHAR: return "char"; + case T_FLOAT: return "float"; + case T_DOUBLE: return "double"; + case T_BYTE: return "byte"; + case T_SHORT: return "short"; + case T_INT: return "int"; + case T_LONG: return "long"; + } + throw new RuntimeException("should not reach here"); + } + + public void printValueOn(PrintStream tty) { + tty.print("TypeArrayKlass for " + getTypeName()); + } + + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + super.iterateFields(visitor, doVMFields); + if (doVMFields) { + visitor.doCInt(maxLength, true); + } + } + + public Klass arrayKlassImpl(boolean orNull, int n) { + int dimension = (int) getDimension(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(dimension <= n, "check order of chain"); + } + if (dimension == n) + return this; + ObjArrayKlass ak = (ObjArrayKlass) getHigherDimension(); + if (ak == null) { + if (orNull) return null; + // FIXME: would need to change in reflective system to actually + // allocate klass + throw new RuntimeException("Can not allocate array klasses in debugging system"); + } + if (orNull) { + return ak.arrayKlassOrNull(n); + } + return ak.arrayKlass(n); + } + + public Klass arrayKlassImpl(boolean orNull) { + return arrayKlassImpl(orNull, (int) (getDimension() + 1)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/TypeArrayKlassKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/TypeArrayKlassKlass.java new file mode 100644 index 00000000000..87b5d45f782 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/TypeArrayKlassKlass.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +public class TypeArrayKlassKlass extends ArrayKlassKlass { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("typeArrayKlassKlass"); + headerSize = type.getSize() + Oop.getHeaderSize(); + } + + public TypeArrayKlassKlass(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + private static long headerSize; + + public long getObjectSize() { return alignObjectSize(headerSize); } + + public void printValueOn(PrintStream tty) { + tty.print("TypeArrayKlassKlass"); + } +}; diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/UnknownOopException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/UnknownOopException.java new file mode 100644 index 00000000000..548e6133c42 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/UnknownOopException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.oops; + +public class UnknownOopException extends RuntimeException { + public UnknownOopException() { + super(); + } + + public UnknownOopException(String detail) { + super(detail); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/AddressVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/AddressVisitor.java new file mode 100644 index 00000000000..a2949af8357 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/AddressVisitor.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.debugger.*; + +/** A generic interface for visiting addresses. Used by the frame/oop + map iteration mechanisms. */ + +public interface AddressVisitor { + public void visitAddress(Address addr); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ArgumentSizeComputer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ArgumentSizeComputer.java new file mode 100644 index 00000000000..fd08880e505 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ArgumentSizeComputer.java @@ -0,0 +1,34 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.oops.*; + +/** Specialized SignatureIterator: Used to compute the argument size. */ + +public class ArgumentSizeComputer extends SignatureInfo { + protected void set(int size, int type) { if (!isReturnType()) this.size += size; } + public ArgumentSizeComputer(Symbol signature) { super(signature); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Arguments.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Arguments.java new file mode 100644 index 00000000000..d593dce512d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Arguments.java @@ -0,0 +1,96 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** This class represent VM's Arguments class -- command line args, flags etc.*/ +public class Arguments { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + public static String getJVMFlags() { + return buildString(jvmFlagsField, jvmFlagsCount); + } + + public static String getJVMArgs() { + return buildString(jvmArgsField, jvmArgsCount); + } + + public static String getJavaCommand() { + return getString(javaCommandField); + } + + // Internals only below this point + + // Fields + private static AddressField jvmFlagsField; + private static AddressField jvmArgsField; + private static AddressField javaCommandField; + private static long jvmFlagsCount; + private static long jvmArgsCount; + + private static synchronized void initialize(TypeDataBase db) { + Type argumentsType = db.lookupType("Arguments"); + jvmFlagsField = argumentsType.getAddressField("_jvm_flags_array"); + jvmArgsField = argumentsType.getAddressField("_jvm_args_array"); + javaCommandField = argumentsType.getAddressField("_java_command"); + + jvmArgsCount = argumentsType.getCIntegerField("_num_jvm_args").getValue(); + jvmFlagsCount = argumentsType.getCIntegerField("_num_jvm_flags").getValue(); + } + + private static String buildString(AddressField arrayField, long count) { + StringBuilder sb = new StringBuilder(); + if (count > 0) { + sb.append(getStringAt(arrayField, 0)); + for (long i = 1; i < count; i++) { + sb.append(" "); + sb.append(getStringAt(arrayField, i)); + } + } + return sb.toString(); + } + + private static String getString(AddressField field) { + Address addr = field.getAddress(); + return CStringUtilities.getString(addr); + } + + private static String getStringAt(AddressField field, long index) { + Address addr = field.getAddress(); + return CStringUtilities.getString(addr.getAddressAt(index * VM.getVM().getAddressSize())); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicLock.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicLock.java new file mode 100644 index 00000000000..ef284ee581e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicLock.java @@ -0,0 +1,56 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; + +public class BasicLock extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("BasicLock"); + displacedHeaderField = type.getCIntegerField("_displaced_header"); + } + + private static CIntegerField displacedHeaderField; + + public BasicLock(Address addr) { + super(addr); + } + + public Mark displacedHeader() { + return new Mark(addr.addOffsetTo(displacedHeaderField.getOffset())); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicObjectLock.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicObjectLock.java new file mode 100644 index 00000000000..ffedd787266 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicObjectLock.java @@ -0,0 +1,67 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +public class BasicObjectLock extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("BasicObjectLock"); + lockField = type.getField("_lock"); + objField = type.getOopField("_obj"); + size = (int) type.getSize(); + } + + private static sun.jvm.hotspot.types.Field lockField; + private static sun.jvm.hotspot.types.OopField objField; + private static int size; + + public BasicObjectLock(Address addr) { + super(addr); + } + + public OopHandle obj() { return objField.getValue(addr); } + public BasicLock lock() { return new BasicLock(addr.addOffsetTo(lockField.getOffset())); } + + /** Note: Use frame::interpreter_frame_monitor_size() for the size + of BasicObjectLocks in interpreter activation frames since it + includes machine-specific padding. This routine returns a size + in BYTES in this system! */ + public static int size() { return size; } + + /** Helper routine for Frames (also probably needed for iteration) */ + public Address address() { return addr; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicType.java new file mode 100644 index 00000000000..4f1d50ddb20 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicType.java @@ -0,0 +1,149 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +/** Encapsulates the BasicType enum in globalDefinitions.hpp in the + VM. */ + +public class BasicType { + public static final int tBoolean = 4; + public static final int tChar = 5; + public static final int tFloat = 6; + public static final int tDouble = 7; + public static final int tByte = 8; + public static final int tShort = 9; + public static final int tInt = 10; + public static final int tLong = 11; + public static final int tObject = 12; + public static final int tArray = 13; + public static final int tVoid = 14; + public static final int tAddress = 15; + public static final int tConflict = 16; + public static final int tIllegal = 99; + + public static final BasicType T_BOOLEAN = new BasicType(tBoolean); + public static final BasicType T_CHAR = new BasicType(tChar); + public static final BasicType T_FLOAT = new BasicType(tFloat); + public static final BasicType T_DOUBLE = new BasicType(tDouble); + public static final BasicType T_BYTE = new BasicType(tByte); + public static final BasicType T_SHORT = new BasicType(tShort); + public static final BasicType T_INT = new BasicType(tInt); + public static final BasicType T_LONG = new BasicType(tLong); + public static final BasicType T_OBJECT = new BasicType(tObject); + public static final BasicType T_ARRAY = new BasicType(tArray); + public static final BasicType T_VOID = new BasicType(tVoid); + public static final BasicType T_ADDRESS = new BasicType(tAddress); + public static final BasicType T_CONFLICT = new BasicType(tConflict); + public static final BasicType T_ILLEGAL = new BasicType(tIllegal); + + public static int getTBoolean() { + return tBoolean; + } + + public static int getTChar() { + return tChar; + } + + public static int getTFloat() { + return tFloat; + } + + public static int getTDouble() { + return tDouble; + } + + public static int getTByte() { + return tByte; + } + + public static int getTShort() { + return tShort; + } + + public static int getTInt() { + return tInt; + } + + public static int getTLong() { + return tLong; + } + + public static int getTObject() { + return tObject; + } + + public static int getTArray() { + return tArray; + } + + public static int getTVoid() { + return tVoid; + } + + public static int getTAddress() { + return tAddress; + } + + /** For stack value type with conflicting contents */ + public static int getTConflict() { + return tConflict; + } + + public static int getTIllegal() { + return tIllegal; + } + + public static BasicType charToBasicType(char c) { + switch( c ) { + case 'B': return T_BYTE; + case 'C': return T_CHAR; + case 'D': return T_DOUBLE; + case 'F': return T_FLOAT; + case 'I': return T_INT; + case 'J': return T_LONG; + case 'S': return T_SHORT; + case 'Z': return T_BOOLEAN; + case 'V': return T_VOID; + case 'L': return T_OBJECT; + case '[': return T_ARRAY; + } + return T_ILLEGAL; + } + + public static int charToType(char c) { + return charToBasicType(c).getType(); + } + + public int getType() { + return type; + } + + //-- Internals only below this point + private BasicType(int type) { + this.type = type; + } + + private int type; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java new file mode 100644 index 00000000000..a2a59126358 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java @@ -0,0 +1,87 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +/** Encapsulates the BasicTypeSize enum in globalDefinitions.hpp in + the VM. */ + +public class BasicTypeSize { + private static boolean initialized = false; + private static int tBooleanSize = 1; + private static int tCharSize = 1; + private static int tFloatSize = 1; + private static int tDoubleSize = 2; + private static int tByteSize = 1; + private static int tShortSize = 1; + private static int tIntSize = 1; + private static int tLongSize = 2; + private static int tObjectSize = 1; + private static int tArraySize = 1; + private static int tVoidSize = 0; + + public static int getTBooleanSize() { + return tBooleanSize; + } + + public static int getTCharSize() { + return tCharSize; + } + + public static int getTFloatSize() { + return tFloatSize; + } + + public static int getTDoubleSize() { + return tDoubleSize; + } + + public static int getTByteSize() { + return tByteSize; + } + + public static int getTShortSize() { + return tShortSize; + } + + public static int getTIntSize() { + return tIntSize; + } + + public static int getTLongSize() { + return tLongSize; + } + + public static int getTObjectSize() { + return tObjectSize; + } + + public static int getTArraySize() { + return tArraySize; + } + + public static int getTVoidSize() { + return tVoidSize; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Bytes.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Bytes.java new file mode 100644 index 00000000000..5c3d54c33e8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Bytes.java @@ -0,0 +1,64 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.debugger.*; + +/** Encapsulates some byte-swapping operations defined in the VM */ + +public class Bytes { + private boolean swap; + + public Bytes(MachineDescription machDesc) { + swap = !machDesc.isBigEndian(); + } + + /** Should only swap if the hardware's underlying byte order is + different from Java's */ + public short swapShort(short x) { + if (!swap) + return x; + + return (short) (((x >> 8) & 0xFF) | (x << 8)); + } + + /** Should only swap if the hardware's underlying byte order is + different from Java's */ + public int swapInt(int x) { + if (!swap) + return x; + + return (swapShort((short) x) << 16) | (swapShort((short) (x >> 16)) & 0xFFFF); + } + + /** Should only swap if the hardware's underlying byte order is + different from Java's */ + public long swapLong(long x) { + if (!swap) + return x; + + return (swapInt((int) x) << 32) | (swapInt((int) (x >> 32)) & 0xFFFFFFFF); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java new file mode 100644 index 00000000000..7468941d239 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java @@ -0,0 +1,175 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +public interface ClassConstants +{ + // constant pool constant types - from JVM spec. + + public static final int JVM_CONSTANT_Utf8 = 1; + public static final int JVM_CONSTANT_Unicode = 2; // unused + public static final int JVM_CONSTANT_Integer = 3; + public static final int JVM_CONSTANT_Float = 4; + public static final int JVM_CONSTANT_Long = 5; + public static final int JVM_CONSTANT_Double = 6; + public static final int JVM_CONSTANT_Class = 7; + public static final int JVM_CONSTANT_String = 8; + public static final int JVM_CONSTANT_Fieldref = 9; + public static final int JVM_CONSTANT_Methodref = 10; + public static final int JVM_CONSTANT_InterfaceMethodref = 11; + public static final int JVM_CONSTANT_NameAndType = 12; + + // HotSpot specific constant pool constant types. + + // For bad value initialization + public static final int JVM_CONSTANT_Invalid = 0; + + // Temporary tag until actual use + public static final int JVM_CONSTANT_UnresolvedClass = 100; + + // Temporary tag while constructing constant pool + public static final int JVM_CONSTANT_ClassIndex = 101; + + // Temporary tag until actual use + public static final int JVM_CONSTANT_UnresolvedString = 102; + + // Temporary tag while constructing constant pool + public static final int JVM_CONSTANT_StringIndex = 103; + + // 1.5 major/minor version numbers from JVM spec. 3rd edition + public static final short MAJOR_VERSION = 49; + public static final short MINOR_VERSION = 0; + + public static final short MAJOR_VERSION_OLD = 46; + public static final short MINOR_VERSION_OLD = 0; + + // From jvm.h + public static final long JVM_ACC_PUBLIC = 0x0001; /* visible to everyone */ + public static final long JVM_ACC_PRIVATE = 0x0002; /* visible only to the defining class */ + public static final long JVM_ACC_PROTECTED = 0x0004; /* visible to subclasses */ + public static final long JVM_ACC_STATIC = 0x0008; /* instance variable is static */ + public static final long JVM_ACC_FINAL = 0x0010; /* no further subclassing, overriding */ + public static final long JVM_ACC_SYNCHRONIZED = 0x0020; /* wrap method call in monitor lock */ + public static final long JVM_ACC_SUPER = 0x0020; /* funky handling of invokespecial */ + public static final long JVM_ACC_VOLATILE = 0x0040; /* can not cache in registers */ + public static final long JVM_ACC_BRIDGE = 0x0040; /* bridge method generated by compiler */ + public static final long JVM_ACC_TRANSIENT = 0x0080; /* not persistant */ + public static final long JVM_ACC_VARARGS = 0x0080; /* method declared with variable number of args */ + public static final long JVM_ACC_NATIVE = 0x0100; /* implemented in C */ + public static final long JVM_ACC_INTERFACE = 0x0200; /* class is an interface */ + public static final long JVM_ACC_ABSTRACT = 0x0400; /* no definition provided */ + public static final long JVM_ACC_STRICT = 0x0800; /* strict floating point */ + public static final long JVM_ACC_SYNTHETIC = 0x1000; /* compiler-generated class, method or field */ + public static final long JVM_ACC_ANNOTATION = 0x2000; /* annotation type */ + public static final long JVM_ACC_ENUM = 0x4000; /* field is declared as element of enum */ + + + // from accessFlags.hpp - hotspot internal flags + + // flags actually put in .class file + public static final long JVM_ACC_WRITTEN_FLAGS = 0x00007FFF; + + // methodOop flags + // monitorenter/monitorexit bytecodes match + public static final long JVM_ACC_MONITOR_MATCH = 0x10000000; + // Method contains monitorenter/monitorexit bytecodes + public static final long JVM_ACC_HAS_MONITOR_BYTECODES = 0x20000000; + // Method has loops + public static final long JVM_ACC_HAS_LOOPS = 0x40000000; + // The loop flag has been initialized + public static final long JVM_ACC_LOOPS_FLAG_INIT = (int)0x80000000; + // Queued for compilation + public static final long JVM_ACC_QUEUED = 0x01000000; + // TEMPORARY: currently on stack replacement compilation is not built into the + // invocation counter machinery. Until it is, we will keep track of methods which + // cannot be on stack replaced in the access flags. + public static final long JVM_ACC_NOT_OSR_COMPILABLE = 0x08000000; + public static final long JVM_ACC_HAS_LINE_NUMBER_TABLE = 0x00100000; + public static final long JVM_ACC_HAS_CHECKED_EXCEPTIONS = 0x00400000; + public static final long JVM_ACC_HAS_JSRS = 0x00800000; + // RedefineClasses() has made method obsolete + public static final long JVM_ACC_IS_OBSOLETE = 0x00010000; + + // klassOop flags + // True if this class has miranda methods in it's vtable + public static final long JVM_ACC_HAS_MIRANDA_METHODS = 0x10000000; + // True if klass has a vanilla default constructor + public static final long JVM_ACC_HAS_VANILLA_CONSTRUCTOR = 0x20000000; + // True if klass has a non-empty finalize() method + public static final long JVM_ACC_HAS_FINALIZER = 0x40000000; + // True if klass supports the Clonable interface + public static final long JVM_ACC_IS_CLONEABLE = 0x80000000; + + // klassOop and methodOop flags + public static final long JVM_ACC_HAS_LOCAL_VARIABLE_TABLE = 0x00200000; + // flags promoted from methods to the holding klass + public static final long JVM_ACC_PROMOTED_FLAGS = 0x00200000; + + // field flags + // Note: these flags must be defined in the low order 16 bits because + // instanceKlass only stores a ushort worth of information from the + // AccessFlags value. + // field access is watched by JVMTI + public static final long JVM_ACC_FIELD_ACCESS_WATCHED = 0x00002000; + // field modification is watched by JVMTI + public static final long JVM_ACC_FIELD_MODIFICATION_WATCHED = 0x00008000; + + // flags accepted by set_field_flags + public static final long JVM_ACC_FIELD_FLAGS = 0x00008000 | JVM_ACC_WRITTEN_FLAGS; + + // from jvm.h + + public static final long JVM_RECOGNIZED_CLASS_MODIFIERS = (JVM_ACC_PUBLIC | + JVM_ACC_FINAL | + JVM_ACC_SUPER | + JVM_ACC_INTERFACE | + JVM_ACC_ABSTRACT | + JVM_ACC_ANNOTATION | + JVM_ACC_SYNTHETIC); + + + public static final long JVM_RECOGNIZED_FIELD_MODIFIERS = (JVM_ACC_PUBLIC | + JVM_ACC_PRIVATE | + JVM_ACC_PROTECTED | + JVM_ACC_STATIC | + JVM_ACC_FINAL | + JVM_ACC_VOLATILE | + JVM_ACC_TRANSIENT | + JVM_ACC_ENUM | + JVM_ACC_SYNTHETIC); + + public static final long JVM_RECOGNIZED_METHOD_MODIFIERS = (JVM_ACC_PUBLIC | + JVM_ACC_PRIVATE | + JVM_ACC_PROTECTED | + JVM_ACC_STATIC | + JVM_ACC_FINAL | + JVM_ACC_SYNCHRONIZED | + JVM_ACC_BRIDGE | + JVM_ACC_VARARGS | + JVM_ACC_NATIVE | + JVM_ACC_ABSTRACT | + JVM_ACC_STRICT | + JVM_ACC_SYNTHETIC); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java new file mode 100644 index 00000000000..fc12a0abbd1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java @@ -0,0 +1,300 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +/** FIXME: missing many accessors; all we have right now is the method + and BCI. NOTE that this has been modified from the VM's version to + handle NULL ScopeDescs for the debugging case. This simplifies + using code a great deal. */ + +public class CompiledVFrame extends JavaVFrame { + private ScopeDesc scope; + private boolean mayBeImprecise; + + public CompiledVFrame(Frame fr, RegisterMap regMap, JavaThread thread, ScopeDesc scope, boolean mayBeImprecise) { + super(fr, regMap, thread); + this.scope = scope; + this.mayBeImprecise = mayBeImprecise; + if (!VM.getVM().isDebugging()) { + Assert.that(scope != null, "scope must be present"); + } + } + + public boolean isTop() { + if (VM.getVM().isDebugging()) { + return (getScope() == null || getScope().isTop()); + } else { + return getScope().isTop(); + } + } + + public boolean isCompiledFrame() { + return true; + } + + public boolean isDeoptimized() { + return fr.isDeoptimized(); + } + + public boolean mayBeImpreciseDbg() { + return mayBeImprecise; + } + + /** Returns the active method */ + public NMethod getCode() { + return VM.getVM().getCodeCache().findNMethod(fr.getPC()); + } + + /** Returns the active method. Does not perform a guarantee + regarding unloaded methods -- more suitable for debugging + system. */ + public NMethod getCodeUnsafe() { + return VM.getVM().getCodeCache().findNMethodUnsafe(fr.getPC()); + } + + /** Returns the ScopeDesc */ + public ScopeDesc getScope() { + return scope; + } + + public Method getMethod() { + if (VM.getVM().isDebugging() && getScope() == null) { + return getCodeUnsafe().getMethod(); + } + return getScope().getMethod(); + } + + public StackValueCollection getLocals() { + List scvList = getScope().getLocals(); + if (scvList == null) + return new StackValueCollection(); + + // scvList is the list of ScopeValues describing the JVM stack state. + // There is one scv_list entry for every JVM stack state in use. + int length = scvList.size(); + StackValueCollection result = new StackValueCollection(length); + for( int i = 0; i < length; i++ ) + result.add( createStackValue((ScopeValue) scvList.get(i)) ); + + return result; + } + + public StackValueCollection getExpressions() { + List scvList = getScope().getExpressions(); + if (scvList == null) + return new StackValueCollection(); + + // scvList is the list of ScopeValues describing the JVM stack state. + // There is one scv_list entry for every JVM stack state in use. + int length = scvList.size(); + StackValueCollection result = new StackValueCollection(length); + for( int i = 0; i < length; i++ ) + result.add( createStackValue((ScopeValue) scvList.get(i)) ); + + return result; + } + + /** Returns List */ + public List getMonitors() { + List monitors = getScope().getMonitors(); + if (monitors == null) { + return new ArrayList(); + } + List result = new ArrayList(monitors.size()); + for (int i = 0; i < monitors.size(); i++) { + MonitorValue mv = (MonitorValue) monitors.get(i); + StackValue ownerSV = createStackValue(mv.owner()); // it is an oop + result.add(new MonitorInfo(ownerSV.getObject(), resolveMonitorLock(mv.basicLock()))); + } + return result; + } + + public int getBCI() { + int raw = getRawBCI(); + return ((raw == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) ? 0 : raw); + } + + /** Returns SynchronizationEntryBCI or bci() (used for synchronization) */ + public int getRawBCI() { + if (VM.getVM().isDebugging() && getScope() == null) { + return 0; // No debugging information! + } + return getScope().getBCI(); + } + + /** Returns the sender vframe */ + public VFrame sender() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isTop(), "just checking"); + } + return sender(false); + } + + public VFrame sender(boolean mayBeImprecise) { + if (!VM.getVM().isDebugging()) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(scope != null, "When new stub generator is in place, then scope can never be NULL"); + } + } + Frame f = (Frame) getFrame().clone(); + return (isTop() + ? super.sender(false) + : new CompiledVFrame(f, getRegisterMap(), getThread(), getScope().sender(), mayBeImprecise)); + } + + private StackValue createStackValue(ScopeValue sv) { + // FIXME: this code appears to be out-of-date with respect to the VM especially in 64-bit mode + if (sv.isLocation()) { + // Stack or register value + Location loc = ((LocationValue) sv).getLocation(); + + if (loc.isIllegal()) return new StackValue(); + + // First find address of value + Address valueAddr = loc.isRegister() + // Value was in a callee-save register + ? getRegisterMap().getLocation(new VMReg(loc.getRegisterNumber())) + // Else value was directly saved on the stack. The frame's original stack pointer, + // before any extension by its callee (due to Compiler1 linkage on SPARC), must be used. + : ((Address)fr.getUnextendedSP()).addOffsetTo(loc.getStackOffset()); + + // Then package it right depending on type + if (loc.holdsFloat()) { // Holds a float in a double register? + // The callee has no clue whether the register holds a float, + // double or is unused. He always saves a double. Here we know + // a double was saved, but we only want a float back. Narrow the + // saved double to the float that the JVM wants. + if (Assert.ASSERTS_ENABLED) { + Assert.that( loc.isRegister(), "floats always saved to stack in 1 word" ); + } + float value = (float) valueAddr.getJDoubleAt(0); + return new StackValue(Float.floatToIntBits(value) & 0xFFFFFFFF); // 64-bit high half is stack junk + } else if (loc.holdsInt()) { // Holds an int in a long register? + // The callee has no clue whether the register holds an int, + // long or is unused. He always saves a long. Here we know + // a long was saved, but we only want an int back. Narrow the + // saved long to the int that the JVM wants. + if (Assert.ASSERTS_ENABLED) { + Assert.that( loc.isRegister(), "ints always saved to stack in 1 word" ); + } + return new StackValue(valueAddr.getJLongAt(0) & 0xFFFFFFFF); + } else if( loc.holdsOop() ) { // Holds an oop? + return new StackValue(valueAddr.getOopHandleAt(0)); + } else if( loc.holdsDouble() ) { + // Double value in a single stack slot + return new StackValue(valueAddr.getJIntAt(0) & 0xFFFFFFFF); + } else if(loc.holdsAddr()) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!VM.getVM().isServerCompiler(), "No address type for locations with C2 (jsr-s are inlined)"); + } + // FIXME: not yet implemented (no access to safepoint state yet) + return new StackValue(0); + // intptr_t return_addr_tmp = *(intptr_t *)value_addr; + // int bci = -1; + // // Get the bci of the jsr that generated this returnAddress value. + // // If the destination of a jsr is a block that ends with a return or throw, then + // // the GraphBuilder converts the jsr into a direct goto. This has the side effect that + // // the return address for the jsr gets emitted as a bci instead of as a real pc + // if (code()->contains((address)return_addr_tmp)) { + // ScopeDesc* scope = code()->scope_desc_at((address)(return_addr_tmp - jsr_call_offset), false); + // bci = scope->bci(); + // } else { + // bci = (int)return_addr_tmp; + // } + // // no need to lock method as this happens at Safepoint + // assert (SafepointSynchronize::is_at_safepoint(), "must be at safepoint, otherwise lock method()"); + // // make sure bci points to jsr + // Bytecode* bytecode = Bytecode_at(method()->bcp_from(bci)); + // Bytecodes::Code bc = bytecode->code(); + // assert (bc == Bytecodes::_jsr || bc == Bytecodes::_jsr_w, "must be jsr"); + // + // // the real returnAddress is the bytecode following the jsr + // return new StackValue((intptr_t)(bci + Bytecodes::length_for(bc))); + } else if (VM.getVM().isLP64() && loc.holdsLong()) { + if ( loc.isRegister() ) { + // Long value in two registers, high half in the first, low in the second + return new StackValue(((valueAddr.getJLongAt(0) & 0xFFFFFFFF) << 32) | + ((valueAddr.getJLongAt(8) & 0xFFFFFFFF))); + } else { + // Long value in a single stack slot + return new StackValue(valueAddr.getJLongAt(0)); + } + } else if( loc.isRegister() ) { + // At the moment, all non-oop values in registers are 4 bytes, + // including double and long halves (see Compile::FillLocArray() in + // opto/output.cpp). Haul them out as such and return a StackValue + // containing an image of the value as it appears in a stack slot. + // If this is a double or long half, the interpreter _must_ deal + // with doubles and longs as entities split across two stack slots. + // To change this so doubles and/or longs can live in one stack slot, + // a StackValue will have to understand that it can contain an + // undivided double or long, implying that a Location (and the debug + // info mechanism) and FillLocArray() will also have to understand it. + return new StackValue(valueAddr.getJIntAt(0) & 0xFFFFFFFF); + } else { + return new StackValue(valueAddr.getJIntAt(0) & 0xFFFFFFFF); + } + } else if (sv.isConstantInt()) { + // Constant int: treat same as register int. + return new StackValue(((ConstantIntValue) sv).getValue() & 0xFFFFFFFF); + } else if (sv.isConstantOop()) { + // constant oop + return new StackValue(((ConstantOopReadValue) sv).getValue()); + } else if (sv.isConstantDouble()) { + // Constant double in a single stack slot + double d = ((ConstantDoubleValue) sv).getValue(); + return new StackValue(Double.doubleToLongBits(d) & 0xFFFFFFFF); + } else if (VM.getVM().isLP64() && sv.isConstantLong()) { + // Constant long in a single stack slot + return new StackValue(((ConstantLongValue) sv).getValue() & 0xFFFFFFFF); + } + + // Unknown ScopeValue type + Assert.that(false, "Should not reach here"); + return new StackValue(0); // dummy + } + + private BasicLock resolveMonitorLock(Location location) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(location.isStack(), "for now we only look at the stack"); + } + int byteOffset = location.getStackOffset(); + // (stack picture) + // high: [ ] byte_offset + wordSize + // low [ ] byte_offset + // + // sp-> [ ] 0 + // the byte_offset is the distance from the stack pointer to the lowest address + // The frame's original stack pointer, before any extension by its callee + // (due to Compiler1 linkage on SPARC), must be used. + return new BasicLock(getFrame().getUnextendedSP().addOffsetTo(byteOffset)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/CompilerThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/CompilerThread.java new file mode 100644 index 00000000000..670f66d44ae --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/CompilerThread.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +public class CompilerThread extends JavaThread { + public CompilerThread(Address addr) { + super(addr); + } + + public boolean isJavaThread() { return false; } + public boolean isHiddenFromExternalView() { return true; } + public boolean isCompilerThread() { return true; } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ConcurrentLocksPrinter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ConcurrentLocksPrinter.java new file mode 100644 index 00000000000..50f90bc326f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ConcurrentLocksPrinter.java @@ -0,0 +1,87 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; + +public class ConcurrentLocksPrinter { + private Map locksMap = new HashMap(); // > + + public ConcurrentLocksPrinter() { + fillLocks(); + } + + public void print(JavaThread jthread, PrintStream tty) { + List locks = (List) locksMap.get(jthread); + tty.println("Locked ownable synchronizers:"); + if (locks == null || locks.isEmpty()) { + tty.println(" - None"); + } else { + for (Iterator itr = locks.iterator(); itr.hasNext();) { + Oop oop = (Oop) itr.next(); + tty.println(" - <" + oop.getHandle() + ">, (a " + + oop.getKlass().getName().asString() + ")"); + } + } + } + + //-- Internals only below this point + private JavaThread getOwnerThread(Oop oop) { + Oop threadOop = OopUtilities.abstractOwnableSynchronizerGetOwnerThread(oop); + if (threadOop == null) { + return null; + } else { + return OopUtilities.threadOopGetJavaThread(threadOop); + } + } + + private void fillLocks() { + VM vm = VM.getVM(); + SystemDictionary sysDict = vm.getSystemDictionary(); + Klass absOwnSyncKlass = sysDict.getAbstractOwnableSynchronizerKlass(); + ObjectHeap heap = vm.getObjectHeap(); + // may be not loaded at all + if (absOwnSyncKlass != null) { + heap.iterateObjectsOfKlass(new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + JavaThread thread = getOwnerThread(oop); + if (thread != null) { + List locks = (List) locksMap.get(thread); + if (locks == null) { + locks = new LinkedList(); + locksMap.put(thread, locks); + } + locks.add(oop); + } + return false; + } + + }, absOwnSyncKlass, true); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ConstructionException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ConstructionException.java new file mode 100644 index 00000000000..3f10cc3a6a0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ConstructionException.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +public class ConstructionException extends RuntimeException { + public ConstructionException() { + super(); + } + + public ConstructionException(String message) { + super(message); + } + + public ConstructionException(Throwable e) { + super(e); + } + + public ConstructionException(String message, Throwable e) { + super(message, e); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/DeadlockDetector.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/DeadlockDetector.java new file mode 100644 index 00000000000..9b671fe510c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/DeadlockDetector.java @@ -0,0 +1,207 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import java.io.*; +import java.util.*; +import java.util.Map.Entry; + +/** Prints information about Java-level deadlocks in supplied 'tty'. */ + +public class DeadlockDetector { + + public static void print(PrintStream tty) { + print(tty, true); + } + + /** prints zero or more deadlocks into 'tty' taking current + snapshot of Java threads and locks */ + public static void print(PrintStream tty, boolean concurrentLocks) { + tty.println("Deadlock Detection:"); + tty.println(); + + int globalDfn = 0, thisDfn; + int numberOfDeadlocks = 0; + JavaThread currentThread = null, previousThread = null; + ObjectMonitor waitingToLockMonitor = null; + Oop waitingToLockBlocker = null; + + threads = VM.getVM().getThreads(); + heap = VM.getVM().getObjectHeap(); + createThreadTable(); + + Iterator i = threadTable.entrySet().iterator(); + while (i.hasNext()) { + Entry e = (Entry)i.next(); + if (dfn(e) >= 0) { + // this thread was already visited + continue; + } + + thisDfn = globalDfn; + JavaThread thread = (JavaThread)e.getKey(); + previousThread = thread; + + // When there is a deadlock, all the monitors involved in the dependency + // cycle must be contended and heavyweight. So we only care about the + // heavyweight monitor a thread is waiting to lock. + try { + waitingToLockMonitor = thread.getCurrentPendingMonitor(); + } catch (RuntimeException re) { + tty.println("This version of HotSpot VM doesn't support deadlock detection."); + return; + } + + Klass abstractOwnableSyncKlass = null; + if (concurrentLocks) { + waitingToLockBlocker = thread.getCurrentParkBlocker(); + SystemDictionary sysDict = VM.getVM().getSystemDictionary(); + abstractOwnableSyncKlass = sysDict.getAbstractOwnableSynchronizerKlass(); + } + + while (waitingToLockMonitor != null || + waitingToLockBlocker != null) { + if (waitingToLockMonitor != null) { + currentThread = threads.owningThreadFromMonitor(waitingToLockMonitor); + } else { + if (concurrentLocks) { + if (waitingToLockBlocker.isA(abstractOwnableSyncKlass)) { + Oop threadOop = OopUtilities.abstractOwnableSynchronizerGetOwnerThread(waitingToLockBlocker); + if (threadOop != null) { + currentThread = OopUtilities.threadOopGetJavaThread(threadOop); + } + } + } + } + if (currentThread == null) { + // No dependency on another thread + break; + } + if (dfn(currentThread) < 0) { + // First visit to this thread + threadTable.put(currentThread, new Integer(globalDfn++)); + } else if (dfn(currentThread) < thisDfn) { + // Thread already visited, and not on a (new) cycle + break; + } else if (currentThread == previousThread) { + // Self-loop, ignore + break; + } else { + // We have a (new) cycle + numberOfDeadlocks ++; + printOneDeadlock(tty, currentThread, concurrentLocks); + break; + } + previousThread = currentThread; + waitingToLockMonitor = (ObjectMonitor)currentThread.getCurrentPendingMonitor(); + if (concurrentLocks) { + waitingToLockBlocker = currentThread.getCurrentParkBlocker(); + } + } + } + + switch (numberOfDeadlocks) { + case 0: + tty.println("No deadlocks found."); + break; + case 1: + tty.println("Found a total of 1 deadlock."); + break; + default: + tty.println("Found a total of " + numberOfDeadlocks + " deadlocks."); + break; + } + tty.println(); + } + + //-- Internals only below this point + private static Threads threads; + private static ObjectHeap heap; + private static HashMap threadTable; + + private static void createThreadTable() { + threadTable = new HashMap(); + for (JavaThread cur = threads.first(); cur != null; cur = cur.next()) { + // initialize dfn for each thread to -1 + threadTable.put(cur, new Integer(-1)); + } + } + + private static int dfn(JavaThread thread) { + Object obj = threadTable.get(thread); + if (obj != null) { + return ((Integer)obj).intValue(); + } + return -1; + } + + private static int dfn(Entry e) { + return ((Integer)e.getValue()).intValue(); + } + + private static void printOneDeadlock(PrintStream tty, JavaThread thread, + boolean concurrentLocks) { + tty.println("Found one Java-level deadlock:"); + tty.println("============================="); + ObjectMonitor waitingToLockMonitor = null; + Oop waitingToLockBlocker = null; + JavaThread currentThread = thread; + do { + tty.println(); + tty.println("\"" + currentThread.getThreadName() + "\":"); + waitingToLockMonitor = currentThread.getCurrentPendingMonitor(); + if (waitingToLockMonitor != null) { + tty.print(" waiting to lock Monitor@" + waitingToLockMonitor.getAddress()); + OopHandle obj = waitingToLockMonitor.object(); + Oop oop = heap.newOop(obj); + if (obj != null) { + tty.print(" (Object@"); + Oop.printOopAddressOn(oop, tty); + tty.print(", a " + oop.getKlass().getName().asString() + ")" ); + tty.print(",\n which is held by"); + } else { + // No Java object associated - a raw monitor + tty.print(" (raw monitor),\n which is held by"); + } + currentThread = threads.owningThreadFromMonitor(waitingToLockMonitor); + tty.print(" \"" + currentThread.getThreadName() + "\""); + } else if (concurrentLocks) { + waitingToLockBlocker = currentThread.getCurrentParkBlocker(); + tty.print(" waiting for ownable synchronizer "); + Oop.printOopAddressOn(waitingToLockBlocker, tty); + tty.print(", (a " + waitingToLockBlocker.getKlass().getName().asString() + ")" ); + Oop threadOop = OopUtilities.abstractOwnableSynchronizerGetOwnerThread(waitingToLockBlocker); + currentThread = OopUtilities.threadOopGetJavaThread(threadOop); + tty.print(",\n which is held by"); + tty.print(" \"" + currentThread.getThreadName() + "\""); + } + } while (!currentThread.equals(thread)); + tty.println(); + tty.println(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ExternalVFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ExternalVFrame.java new file mode 100644 index 00000000000..e8f50318616 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ExternalVFrame.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; + +public class ExternalVFrame extends VFrame { + private boolean mayBeImprecise; + + /** Package-internal constructor */ + ExternalVFrame(Frame fr, RegisterMap regMap, JavaThread thread, boolean mayBeImprecise) { + super(fr, regMap, thread); + + this.mayBeImprecise = mayBeImprecise; + } + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + getFrame().printValueOn(tty); + } + + public void printValue() { + printValueOn(System.out); + } + + public void printValueOn(PrintStream tty) { + super.printOn(tty); + } + + public boolean mayBeImpreciseDbg() { + return mayBeImprecise; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Frame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Frame.java new file mode 100644 index 00000000000..63e6ee2c6bb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Frame.java @@ -0,0 +1,735 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.compiler.*; +import sun.jvm.hotspot.c1.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/**

A frame represents a physical stack frame (an activation). + Frames can be C or Java frames, and the Java frames can be + interpreted or compiled. In contrast, vframes represent + source-level activations, so that one physical frame can + correspond to multiple source level frames because of inlining. +

+ +

NOTE that this is not a VMObject and does not wrap an Address + -- this is an actual port of the VM's Frame code to Java.

+ +

NOTE also that this is incomplete -- just trying to get + reading of interpreted frames working for now, so all non-core and + setter methods are removed for now. (FIXME)

*/ + +public abstract class Frame implements Cloneable { + /** A raw stack pointer. The accessor getSP() will return a real (usable) + stack pointer (e.g. from Thread::last_Java_sp) */ + protected Address raw_sp; + + /** Program counter (the next instruction after the call) */ + protected Address pc; + protected boolean deoptimized; + + public Frame() { + deoptimized = false; + } + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + /** Size of constMethodOopDesc for computing BCI from BCP (FIXME: hack) */ + private static long constMethodOopDescSize; + + private static synchronized void initialize(TypeDataBase db) { + Type constMethodOopType = db.lookupType("constMethodOopDesc"); + // FIXME: not sure whether alignment here is correct or how to + // force it (round up to address size?) + constMethodOopDescSize = constMethodOopType.getSize(); + } + + protected int bcpToBci(Address bcp, ConstMethod cm) { + // bcp will be null for interpreter native methods + // in addition depending on where we catch the system the value can + // be a bcp or a bci. + if (bcp == null) return 0; + long bci = bcp.minus(null); + if (bci >= 0 && bci < cm.getCodeSize()) return (int) bci; + return (int) (bcp.minus(cm.getHandle()) - constMethodOopDescSize); + } + + protected int bcpToBci(Address bcp, Method m) { + return bcpToBci(bcp, m.getConstMethod()); + } + + public abstract Object clone(); + + // Accessors + + /** pc: Returns the pc at which this frame will continue normally. + It must point at the beginning of the next instruction to + execute. */ + public Address getPC() { return pc; } + public void setPC(Address newpc) { pc = newpc; } + public boolean isDeoptimized() { return deoptimized; } + + public abstract Address getSP(); + public abstract Address getID(); + public abstract Address getFP(); + + /** testers -- platform dependent */ + public abstract boolean equals(Object arg); + + /** type testers */ + public boolean isInterpretedFrame() { return VM.getVM().getInterpreter().contains(getPC()); } + public boolean isJavaFrame() { + if (isInterpretedFrame()) return true; + if (!VM.getVM().isCore()) { + if (isCompiledFrame()) return true; + } + return false; + } + + /** Java frame called from C? */ + public boolean isEntryFrame() { return VM.getVM().getStubRoutines().returnsToCallStub(getPC()); } + public boolean isNativeFrame() { + if (!VM.getVM().isCore()) { + CodeBlob cb = VM.getVM().getCodeCache().findBlob(getPC()); + return (cb != null && cb.isNativeMethod()); + } else { + return false; + } + } + + public boolean isCompiledFrame() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!VM.getVM().isCore(), "noncore builds only"); + } + CodeBlob cb = VM.getVM().getCodeCache().findBlob(getPC()); + return (cb != null && cb.isJavaMethod()); + } + + public boolean isGlueFrame() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!VM.getVM().isCore(), "noncore builds only"); + } + CodeBlob cb = VM.getVM().getCodeCache().findBlob(getPC()); + if (cb == null) { + return false; + } + if (cb.isRuntimeStub()) return true; + else return false; + } + + /** oldest frame? (has no sender) FIXME: this is modified from the + C++ code to handle the debugging situation where we try to + traverse the stack for, for example, the signal thread, and + don't find any valid Java frames. Would really like to put the + second half of the conditional in some sort of debugging-only if + statement. */ + // *** FIXME: THE CALL TO isJavaFrame() IS WAY TOO EXPENSIVE!!!!! *** + public boolean isFirstFrame() { return ((isEntryFrame() && entryFrameIsFirst()) || + (!isJavaFrame() && !hasSenderPD())); } + /** same for Java frame */ + public boolean isFirstJavaFrame() { throw new RuntimeException("not yet implemented"); } + + /** This is an addition for debugging purposes on platforms which + have the notion of signals. */ + public abstract boolean isSignalHandlerFrameDbg(); + + /** If this is a signal handler frame (again, on a platform with a + notion of signals), get the signal number. */ + public abstract int getSignalNumberDbg(); + + /** If this is a signal handler frame (again, on a platform with a + notion of signals), get the name of the signal. */ + public abstract String getSignalNameDbg(); + + /** performs sanity checks on interpreted frames. */ + public abstract boolean isInterpretedFrameValid(); + + /** tells whether this frame is marked for deoptimization */ + public boolean shouldBeDeoptimized() { throw new RuntimeException("not yet implemented"); } + + /** tells whether this frame can be deoptimized */ + public boolean canBeDeoptimized() { throw new RuntimeException("not yet implemented"); } + + /** returns the sending frame */ + public abstract Frame sender(RegisterMap map, CodeBlob nm); + + /** equivalent to sender(map, null) */ + public Frame sender(RegisterMap map) { return sender(map, null); } + + /** returns the sender, but skips conversion frames */ + public Frame realSender(RegisterMap map) { + if (!VM.getVM().isCore()) { + Frame result = sender(map); + while (result.isGlueFrame()) { + result = result.sender(map); + } + return result; + } else { + return sender(map); + } + } + + /** Platform-dependent query indicating whether this frame has a + sender. Should return true if it is possible to call sender() at + all on this frame. (This is currently only needed for the + debugging system, if a stack trace is attempted for a Java + thread which has no Java frames, i.e., the signal thread; we + have to know to stop traversal at the bottom frame.) */ + protected abstract boolean hasSenderPD(); + + //-------------------------------------------------------------------------------- + // All frames: + // A low-level interface for vframes: + + /** Returns the address of the requested "slot" on the stack. Slots + are as wide as addresses, so are 32 bits wide on a 32-bit + machine and 64 bits wide on a 64-bit machine. */ + public Address addressOfStackSlot(int slot) { return getFP().addOffsetTo(slot * VM.getVM().getAddressSize()); } + + /** Fetches the OopHandle at the requested slot */ + public OopHandle getOopHandleAt(int slot) { return addressOfStackSlot(slot).getOopHandleAt(0); } + /** Fetches the OopHandle at the slot, adjusted for compiler frames */ + // FIXME: looks like this is only used for compiled frames + // public OopHandle getOopHandleAtAdjusted(MethodOop method, int slot) { return addressOfStackSlot(slot).getOopHandleAt(0); } + // FIXME: Not yet implementable + // public void setOopHandleAt(int slot, OopHandle value) { addressOfStackSlot(slot).setOopHandleAt(0, value); } + + /** Fetches the (Java) int at the requested slot */ + public int getIntAt(int slot) { return addressOfStackSlot(slot).getJIntAt(0); } + // FIXME: Not yet implementable + // public void setIntAt(int slot, int value) { addressOfStackSlot(slot).setJIntAt(0, value); } + + /** returns the frame size in stack slots */ + public abstract long frameSize(); + + /** Link (i.e., the pointer to the previous frame) */ + public abstract Address getLink(); + // public abstract void setLink(Address addr); + + /** Return address */ + public abstract Address getSenderPC(); + // FIXME: currently unimplementable + // public abstract void setSenderPC(Address addr); + + /** The frame's original SP, before any extension by an interpreted + callee; used for packing debug info into vframeArray objects and + vframeArray lookup. */ + public abstract Address getUnextendedSP(); + + /** Returns the stack pointer of the calling frame */ + public abstract Address getSenderSP(); + + //-------------------------------------------------------------------------------- + // Interpreter frames: + // + + public abstract Address addressOfInterpreterFrameLocals(); + + public Address addressOfInterpreterFrameLocal(int slot) { + return addressOfInterpreterFrameLocals().getAddressAt(0).addOffsetTo(-slot * VM.getVM().getAddressSize()); + } + + // FIXME: not yet implementable + // void interpreter_frame_set_locals(intptr_t* locs); + + // NOTE that the accessor "addressOfInterpreterFrameBCX" has + // necessarily been eliminated. The byte code pointer is inherently + // an interior pointer to a Method (the bytecodes follow the + // methodOopDesc data structure) and therefore acquisition of it in + // this system can not be allowed. All accesses to interpreter frame + // byte codes are via the byte code index (BCI). + + /** Byte code index. In the underlying frame, what is actually + stored is a byte code pointer (BCP), which is converted to a BCI + and back by the GC when methods are moved. In this system, + interior pointers are not allowed, so we must make the access to + the interpreter frame's BCI atomic with respect to GC. This may + mean implementation with an underlying call through native code + into the VM or a magic sequence in the compiler. (FIXME) */ + public abstract int getInterpreterFrameBCI(); + // FIXME: not yet implementable + // public abstract void setInterpreterFrameBCI(int bci); + + // FIXME: elided for now + // public abstract Address addressOfInterpreterCalleeReceiver(Symbol signature); + + /** Find receiver for an invoke when arguments are just pushed on + stack (i.e., callee stack-frame is not setup) */ + // FIXME: elided for now + // public OopHandle getInterpreterCalleeReceiver(SymbolOop signature) { return addressOfInterpreterCalleeReceiver(signature).getOopHandleAt(0); } + + //-------------------------------------------------------------------------------- + // Expression stack (may go up or down, direction == 1 or -1) + // + + public abstract Address addressOfInterpreterFrameExpressionStack(); + public abstract int getInterpreterFrameExpressionStackDirection(); + public Address addressOfInterpreterFrameExpressionStackSlot(int slot) { + return addressOfInterpreterFrameExpressionStack().addOffsetTo(-slot * VM.getVM().getAddressSize()); + } + + /** Top of expression stack */ + public abstract Address addressOfInterpreterFrameTOS(); + + /** Expression stack from top down */ + public abstract Address addressOfInterpreterFrameTOSAt(int slot); + + /** FIXME: is this portable? */ + public int getInterpreterFrameExpressionStackSize() { + return (int) (1 + (getInterpreterFrameExpressionStackDirection() * + (addressOfInterpreterFrameTOS().minus(addressOfInterpreterFrameExpressionStack())))); + } + + public abstract Address getInterpreterFrameSenderSP(); + // FIXME: not yet implementable + // public abstract void setInterpreterFrameSenderSP(Address senderSP); + + //-------------------------------------------------------------------------------- + // BasicObjectLocks: + // + + public abstract BasicObjectLock interpreterFrameMonitorBegin(); + public abstract BasicObjectLock interpreterFrameMonitorEnd(); + /** NOTE: this returns a size in BYTES in this system! */ + public abstract int interpreterFrameMonitorSize(); + public BasicObjectLock nextMonitorInInterpreterFrame(BasicObjectLock cur) { + return new BasicObjectLock(cur.address().addOffsetTo(interpreterFrameMonitorSize())); + } + public BasicObjectLock previousMonitorInInterpreterFrame(BasicObjectLock cur) { + return new BasicObjectLock(cur.address().addOffsetTo(-1 * interpreterFrameMonitorSize())); + } + + // interpreter_frame_monitor_begin is higher in memory than interpreter_frame_monitor_end + // Interpreter_frame_monitor_begin points to one element beyond the oldest one, + // interpreter_frame_monitor_end points to the youngest one, or if there are none, + // it points to one beyond where the first element will be. + // interpreter_frame_monitor_size reports the allocation size of a monitor in the interpreter stack. + // this value is >= BasicObjectLock::size(), and may be rounded up + + // FIXME: avoiding implementing this for now if possible + // public void interpreter_frame_set_monitor_end(BasicObjectLock* value); + // public void interpreter_frame_verify_monitor(BasicObjectLock* value) const; + // + // Tells whether the current interpreter_frame frame pointer + // corresponds to the old compiled/deoptimized fp + // The receiver used to be a top level frame + // public boolean interpreter_frame_equals_unpacked_fp(intptr_t* fp); + + //-------------------------------------------------------------------------------- + // Method and constant pool cache: + // + + /** Current method */ + public abstract Address addressOfInterpreterFrameMethod(); + + /** Current method */ + public Method getInterpreterFrameMethod() { + return (Method) VM.getVM().getObjectHeap().newOop(addressOfInterpreterFrameMethod().getOopHandleAt(0)); + } + + /** Current method */ + // FIXME: not yet implementable + // public void setInterpreterFrameMethod(Method method); + + /** Constant pool cache */ + public abstract Address addressOfInterpreterFrameCPCache(); + /** Constant pool cache */ + public ConstantPoolCache getInterpreterFrameCPCache() { + return (ConstantPoolCache) VM.getVM().getObjectHeap().newOop(addressOfInterpreterFrameCPCache().getOopHandleAt(0)); + } + + //-------------------------------------------------------------------------------- + // Entry frames: + // + + public abstract JavaCallWrapper getEntryFrameCallWrapper(); + + // FIXME: add + // inline intptr_t* entry_frame_argument_at(int offset) const; + + + /** Tells whether there is another chunk of Delta stack above */ + public boolean entryFrameIsFirst() { return (getEntryFrameCallWrapper().getLastJavaSP() == null); } + + //-------------------------------------------------------------------------------- + // Safepoints: + // + + protected abstract Address addressOfSavedOopResult(); + protected abstract Address addressOfSavedReceiver(); + + public OopHandle getSavedOopResult() { + return addressOfSavedOopResult().getOopHandleAt(0); + } + + // FIXME: not yet implementable + // public void setSavedOopResult(OopHandle obj); + + public OopHandle getSavedReceiver() { + return addressOfSavedReceiver().getOopHandleAt(0); + } + + // FIXME: not yet implementable + // public void setSavedReceiver(OopHandle obj); + + //-------------------------------------------------------------------------------- + // Oop traversals: + // + + public void oopsInterpretedArgumentsDo(Symbol signature, boolean isStatic, AddressVisitor f) { + ArgumentOopFinder finder = new ArgumentOopFinder(signature, isStatic, this, f); + finder.oopsDo(); + } + + /** Conversion from an VMReg::Name to physical stack location */ + public Address oopMapRegToLocation(VMReg reg, RegisterMap regMap) { + VMReg stack0 = VM.getVM().getVMRegImplInfo().getStack0(); + if (reg.lessThan(stack0)) { + // If it is passed in a register, it got spilled in the stub frame. + return regMap.getLocation(reg); + } else { + long spOffset = VM.getVM().getAddressSize() * reg.minus(stack0); + return getUnextendedSP().addOffsetTo(spOffset); + } + } + + public void oopsDo(AddressVisitor oopVisitor, RegisterMap map) { + if (isInterpretedFrame()) { + oopsInterpretedDo(oopVisitor, map); + } else if (isEntryFrame()) { + oopsEntryDo(oopVisitor, map); + } else if (VM.getVM().getCodeCache().contains(getPC())) { + oopsCodeBlobDo(oopVisitor, map); + } else { + Assert.that(false, "should not reach here"); + } + } + + //-------------------------------------------------------------------------------- + // Printing code + // + + public void printValue() { + printValueOn(System.out); + } + + public void printValueOn(PrintStream tty) { + // FIXME; + } + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + // FIXME; + } + + public void interpreterFramePrintOn(PrintStream tty) { + // FIXME; + } + + //-------------------------------------------------------------------------------- + // Get/set typed locals from a frame. + // Respects platform dependent word-ordering. + // + // FIXME: avoiding implementing this for now if possible + // + // Currently these work only for interpreted frames. + // Todo: make these work for compiled frames. + // + // oop get_local_object(jint slot) const; + // jint get_local_int (jint slot) const; + // jlong get_local_long (jint slot) const; + // jfloat get_local_float (jint slot) const; + // jdouble get_local_double(jint slot) const; + // + // void set_local_object(jint slot, oop obj); + // void set_local_int (jint slot, jint i); + // void set_local_long (jint slot, jlong l); + // void set_local_float (jint slot, jfloat f); + // void set_local_double(jint slot, jdouble d); + + // FIXME: add safepoint code, oops_do, etc. + // FIXME: NOT FINISHED + + + + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + // /** Helper method for better factored code in frame::sender */ + // private frame sender_for_entry_frame(RegisterMap* map) { throw new RuntimeException("not yet implemented"); } + // private frame sender_for_interpreter_frame(RegisterMap* map) { throw new RuntimeException("not yet implemented"); } + + // + // Oop iteration (FIXME: NOT FINISHED) + // + + + private static class InterpVisitor implements OopMapVisitor { + private AddressVisitor addressVisitor; + + public InterpVisitor(AddressVisitor oopVisitor) { + setAddressVisitor(oopVisitor); + } + + public void setAddressVisitor(AddressVisitor addressVisitor) { + this.addressVisitor = addressVisitor; + } + + public void visitOopLocation(Address oopAddr) { + addressVisitor.visitAddress(oopAddr); + } + + public void visitDerivedOopLocation(Address baseOopAddr, Address derivedOopAddr) { + if (VM.getVM().isClientCompiler()) { + Assert.that(false, "should not reach here"); + } else if (VM.getVM().isServerCompiler() && + VM.getVM().useDerivedPointerTable()) { + Assert.that(false, "FIXME: add derived pointer table"); + } + } + + public void visitValueLocation(Address valueAddr) { + } + + public void visitDeadLocation(Address deadAddr) { + } + } + + private void oopsInterpretedDo(AddressVisitor oopVisitor, RegisterMap map) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + Method m = getInterpreterFrameMethod(); + int bci = getInterpreterFrameBCI(); + + // FIXME: Seeing this sometimes + if (VM.getVM().isDebugging()) { + if (bci < 0 || bci >= m.getCodeSize()) return; + } + + if (Assert.ASSERTS_ENABLED) { + // Assert.that(VM.getVM().getUniverse().heap().isIn(m), "method must be valid oop"); + Assert.that((m.isNative() && (bci == 0)) || ((bci >= 0) && (bci < m.getCodeSize())), "invalid bci value"); + } + + // Handle the monitor elements in the activation + // FIXME: monitor information not yet exposed + // for ( + // BasicObjectLock* current = interpreter_frame_monitor_end(); + // current < interpreter_frame_monitor_begin(); + // current = next_monitor_in_interpreter_frame(current) + // ) { + //#ifdef ASSERT + // interpreter_frame_verify_monitor(current); + //#endif + // current->oops_do(f); + // } + + // process fixed part + oopVisitor.visitAddress(addressOfInterpreterFrameMethod()); + oopVisitor.visitAddress(addressOfInterpreterFrameCPCache()); + + // FIXME: expose interpreterFrameMirrorOffset + // if (m.isNative() && m.isStatic()) { + // oopVisitor.visitAddress(getFP().addOffsetTo(interpreterFrameMirrorOffset)); + // } + + int maxLocals = (int) (m.isNative() ? m.getSizeOfParameters() : m.getMaxLocals()); + InterpreterFrameClosure blk = new InterpreterFrameClosure(this, maxLocals, (int) m.getMaxStack(), oopVisitor); + + // process locals & expression stack + OopMapCacheEntry mask = m.getMaskFor(bci); + mask.iterateOop(blk); + + // process a callee's arguments if we are at a call site + // (i.e., if we are at an invoke bytecode) + if (map.getIncludeArgumentOops() && !m.isNative()) { + BytecodeInvoke call = BytecodeInvoke.atCheck(m, bci); + if (call != null && getInterpreterFrameExpressionStackSize() > 0) { + // we are at a call site & the expression stack is not empty + // => process callee's arguments + // + // Note: The expression stack can be empty if an exception + // occured during method resolution/execution. In all + // cases we empty the expression stack completely be- + // fore handling the exception (the exception handling + // code in the interpreter calls a blocking runtime + // routine which can cause this code to be executed). + // (was bug gri 7/27/98) + oopsInterpretedArgumentsDo(call.signature(), call.isInvokestatic(), oopVisitor); + } + } + } + + private void oopsEntryDo (AddressVisitor oopVisitor, RegisterMap regMap) {} + private void oopsCodeBlobDo (AddressVisitor oopVisitor, RegisterMap regMap) { + CodeBlob cb = VM.getVM().getCodeCache().findBlob(getPC()); + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb != null, "sanity check"); + } + if (cb.getOopMaps() != null) { + OopMapSet.oopsDo(this, cb, regMap, oopVisitor, VM.getVM().isDebugging()); + + // FIXME: add in traversal of argument oops (skipping this for + // now until we have the other stuff tested) + + } + + // FIXME: would add this in in non-debugging system + + // If we see an activation belonging to a non_entrant nmethod, we mark it. + // if (cb->is_nmethod() && ((nmethod *)cb)->is_not_entrant()) { + // ((nmethod*)cb)->mark_as_seen_on_stack(); + // } + } + + // FIXME: implement the above routines, plus add + // oops_interpreted_arguments_do and oops_compiled_arguments_do +} + +// +// Only used internally, to iterate through oop slots in interpreted +// frames +// +class InterpreterFrameClosure implements OffsetClosure { + // Used for debugging this code + private static final boolean DEBUG = false; + + private Frame fr; + private AddressVisitor f; + private int maxLocals; + private int maxStack; + + InterpreterFrameClosure(Frame fr, int maxLocals, int maxStack, AddressVisitor f) { + this.fr = fr; + this.maxLocals = maxLocals; + this.maxStack = maxStack; + this.f = f; + } + + public void offsetDo(int offset) { + if (DEBUG) { + System.err.println("Visiting offset " + offset + ", maxLocals = " + maxLocals + + " for frame " + fr + ", method " + + fr.getInterpreterFrameMethod().getMethodHolder().getName().asString() + + fr.getInterpreterFrameMethod().getName().asString()); + } + Address addr; + if (offset < maxLocals) { + addr = fr.addressOfInterpreterFrameLocal(offset); + if (Assert.ASSERTS_ENABLED) { + Assert.that(AddressOps.gte(addr, fr.getSP()), "must be inside the frame"); + } + if (DEBUG) { + System.err.println(" Visiting local at addr " + addr); + } + f.visitAddress(addr); + } else { + addr = fr.addressOfInterpreterFrameExpressionStackSlot(offset - maxLocals); + if (DEBUG) { + System.err.println(" Address of expression stack slot: " + addr + ", TOS = " + + fr.addressOfInterpreterFrameTOS()); + } + // In case of exceptions, the expression stack is invalid and the esp will be reset to express + // this condition. Therefore, we call f only if addr is 'inside' the stack (i.e., addr >= esp for Intel). + boolean inStack; + if (fr.getInterpreterFrameExpressionStackDirection() > 0) { + inStack = AddressOps.lte(addr, fr.addressOfInterpreterFrameTOS()); + } else { + inStack = AddressOps.gte(addr, fr.addressOfInterpreterFrameTOS()); + } + if (inStack) { + if (DEBUG) { + System.err.println(" In stack; visiting location."); + } + f.visitAddress(addr); + } else if (DEBUG) { + System.err.println(" *** WARNING: Address is out of bounds"); + } + } + } +} + +// Only used internally, to find arguments in interpreted frames +class ArgumentOopFinder extends SignatureInfo { + private AddressVisitor f; + private int offset; + private boolean isStatic; + private Frame fr; + + protected void set(int size, int type) { + offset -= size; + if (type == BasicType.getTObject() || type == BasicType.getTArray()) oopOffsetDo(); + } + + private void oopOffsetDo() { + f.visitAddress(fr.addressOfInterpreterFrameTOSAt(offset)); + } + + public ArgumentOopFinder(Symbol signature, boolean isStatic, Frame fr, AddressVisitor f) { + super(signature); + + // compute size of arguments + int argsSize = new ArgumentSizeComputer(signature).size() + (isStatic ? 0 : 1); + if (Assert.ASSERTS_ENABLED) { + Assert.that(!fr.isInterpretedFrame() || + argsSize <= fr.getInterpreterFrameExpressionStackSize(), "args cannot be on stack anymore"); + } + // initialize ArgumentOopFinder + this.f = f; + this.fr = fr; + this.offset = argsSize; + this.isStatic = isStatic; + } + + public void oopsDo() { + if (!isStatic) { + --offset; + oopOffsetDo(); + } + iterateParameters(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java new file mode 100644 index 00000000000..ff5ad46c355 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java @@ -0,0 +1,158 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class InterpretedVFrame extends JavaVFrame { + /** JVM state */ + public Method getMethod() { + return getFrame().getInterpreterFrameMethod(); + } + + public StackValueCollection getLocals() { + Method m = getMethod(); + + int length = (int) m.getMaxLocals(); + + if (m.isNative()) { + // If the method is native, getMaxLocals is not telling the truth. + // maxlocals then equals the size of parameters + length = (int) m.getSizeOfParameters(); + } + + StackValueCollection result = new StackValueCollection(length); + + // Get oopmap describing oops and int for current bci + OopMapCacheEntry oopMask = getMethod().getMaskFor(getBCI()); + + // handle locals + for(int i = 0; i < length; i++) { + // Find stack location + Address addr = addressOfLocalAt(i); + + // Depending on oop/int put it in the right package + StackValue sv; + if (oopMask.isOop(i)) { + // oop value + sv = new StackValue(addr.getOopHandleAt(0)); + } else { + // integer + // Fetch a signed integer the size of a stack slot + sv = new StackValue(addr.getCIntegerAt(0, VM.getVM().getAddressSize(), false)); + } + result.add(sv); + } + + return result; + } + + public StackValueCollection getExpressions() { + int length = getFrame().getInterpreterFrameExpressionStackSize(); + + if (getMethod().isNative()) { + // If the method is native, there is no expression stack + length = 0; + } + + int nofLocals = (int) getMethod().getMaxLocals(); + StackValueCollection result = new StackValueCollection(length); + + // Get oopmap describing oops and int for current bci + OopMapCacheEntry oopMask = getMethod().getMaskFor(getBCI()); + + for(int i = 0; i < length; i++) { + // Find stack location + Address addr = addressOfExpressionStackAt(i); + + // Depending on oop/int put it in the right package + StackValue sv; + if (oopMask.isOop(i + nofLocals)) { + // oop value + sv = new StackValue(addr.getOopHandleAt(0)); + } else { + // integer + // Fetch a signed integer the size of a stack slot + sv = new StackValue(addr.getCIntegerAt(0, VM.getVM().getAddressSize(), false)); + } + result.add(sv); + } + + return result; + } + + /** Returns List */ + public List getMonitors() { + List result = new ArrayList(5); + for (BasicObjectLock current = getFrame().interpreterFrameMonitorEnd(); + current.address().lessThan(getFrame().interpreterFrameMonitorBegin().address()); + current = getFrame().nextMonitorInInterpreterFrame(current)) { + result.add(new MonitorInfo(current.obj(), current.lock())); + } + return result; + } + + /** Test operation */ + public boolean isInterpretedFrame() { return true; } + + /** Package-internal constructor */ + InterpretedVFrame(Frame fr, RegisterMap regMap, JavaThread thread) { + super(fr, regMap, thread); + } + + /** Accessor for Byte Code Index (NOTE: access to BCP is not allowed + in this system; see Frame.java) */ + public int getBCI() { + return getFrame().getInterpreterFrameBCI(); + } + + /** Setter for Byte Code Index */ + // FIXME: not yet implementable + // public void setBCI(int bci) { + // getFrame().setInterpreterFrameBCI(bci); + // } + + public void verify() { + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private Address addressOfLocalAt(int index) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(getFrame().isInterpretedFrame(), "frame should be an interpreted frame"); + } + return fr.addressOfInterpreterFrameLocal(index); + } + + private Address addressOfExpressionStackAt(int index) { + return fr.addressOfInterpreterFrameExpressionStackSlot(index); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JNIHandleBlock.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JNIHandleBlock.java new file mode 100644 index 00000000000..3284b6a7e7d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JNIHandleBlock.java @@ -0,0 +1,154 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** */ +public class JNIHandleBlock extends VMObject { + private static Field handlesField; + private static CIntegerField topField; + private static AddressField nextField; + + private static int blockSizeInOops; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JNIHandleBlock"); + + handlesField = type.getField("_handles"); + topField = type.getCIntegerField("_top"); + nextField = type.getAddressField("_next"); + + blockSizeInOops = db.lookupIntConstant("JNIHandleBlock::block_size_in_oops").intValue(); + } + + public JNIHandleBlock(Address addr) { + super(addr); + } + + public JNIHandleBlock next() { + Address handleAddr = nextField.getValue(addr); + if (handleAddr == null) { + return null; + } + + /* the next handle block is valid only if the current block is full */ + if (top() < blockSizeInOops) { + return null; + } + return new JNIHandleBlock(handleAddr); + } + + public int top() { + return (int) topField.getValue(addr); + } + + public void oopsDo(AddressVisitor visitor) { + // Visit handles in this block + for (int i = 0; i < top(); i++) { + Address cur = getOopHandleAddress(i); + if (cur != null) { + visitor.visitAddress(cur); + } + } + + // Visit handles in subsequent blocks if necessary + JNIHandleBlock n = next(); + if (n != null) { + n.oopsDo(visitor); + } + } + + public OopHandle getOopHandle(int x) { + Address oopAddr = getOopHandleAddress(x); + if (oopAddr != null) { + return oopAddr.getOopHandleAt(0); + } + return null; + } + + /** Debugging routine only. Returns non-null JNIHandleBlock + containing the JNI handle or null if this handle block and its + successors did not contain it (or if the handle was deleted). */ + public JNIHandleBlock blockContainingHandle(Address jniHandle) { + JNIHandleBlock cur = this; + while (cur != null) { + if (indexOfHandle(jniHandle) >= 0) { + return cur; + } + cur = cur.next(); + } + return null; + } + + /** Debugging routine: returns the index (0..top() - 1) of the + handle in this block, or -1 if the handle was not contained in + this block. Does not search successor blocks. */ + public int indexOfHandle(Address jniHandle) { + for (int i = 0; i < top(); i++) { + Address addr = getOopHandleAddress(i); + if (addr != null) { + if (addr.equals(jniHandle)) { + return i; + } + } + } + return -1; + } + + public String toString() { + Address handleBase = addr.addOffsetTo(handlesField.getOffset()); + Address handleEnd = addr.addOffsetTo(handlesField.getOffset() + top() * VM.getVM().getOopSize()); + return "JNIHandleBlock [" + handleBase + ", " + handleEnd + ")"; + } + + /** Only returns addresses of valid OopHandles */ + private Address getOopHandleAddress(int x) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(x < top(), "out of bounds"); + } + + Address oopAddr = addr.addOffsetTo(handlesField.getOffset() + x * VM.getVM().getOopSize()); + OopHandle handle = oopAddr.getOopHandleAt(0); + if (VM.getVM().getUniverse().isInReserved(handle) && !VM.getVM().getJNIHandles().isDeletedHandle(handle)) { + /* the oop handle is valid only if it is not freed (i.e. reserved in heap) and is not a deleted oop */ + return oopAddr; + } else { + return null; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JNIHandles.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JNIHandles.java new file mode 100644 index 00000000000..e138343dd0f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JNIHandles.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +public class JNIHandles { + private static AddressField globalHandlesField; + private static AddressField weakGlobalHandlesField; + private static OopField deletedHandleField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JNIHandles"); + + globalHandlesField = type.getAddressField("_global_handles"); + weakGlobalHandlesField = type.getAddressField("_weak_global_handles"); + deletedHandleField = type.getOopField("_deleted_handle"); + + } + + public JNIHandles() { + } + + public JNIHandleBlock globalHandles() { + Address handleAddr = globalHandlesField.getValue(); + if (handleAddr == null) { + return null; + } + return new JNIHandleBlock(handleAddr); + } + + public JNIHandleBlock weakGlobalHandles() { + Address handleAddr = weakGlobalHandlesField.getValue(); + if (handleAddr == null) { + return null; + } + return new JNIHandleBlock(handleAddr); + } + + public OopHandle deletedHandle() { + return deletedHandleField.getValue(); + } + + public boolean isDeletedHandle(OopHandle handle) { + return (handle != null && handle.equals(deletedHandle())); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JNIid.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JNIid.java new file mode 100644 index 00000000000..fb88f34e078 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JNIid.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; + +public class JNIid extends VMObject { + private static sun.jvm.hotspot.types.OopField holder; + private static AddressField next; + private static CIntegerField offset; + private static sun.jvm.hotspot.types.OopField resolvedMethod; + private static sun.jvm.hotspot.types.OopField resolvedReceiver; + + private ObjectHeap heap; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + + // FIXME: JNIid has been removed as part of redesign of JNI method, + // field ID generation. Please refer to src/share/vm/prims/jniId.hpp/.cpp + // For now, commenting out the below code. + + /*********************************************************** + Type type = db.lookupType("JNIid"); + + holder = type.getOopField("_holder"); + next = type.getAddressField("_next"); + offset = type.getCIntegerField("_offset"); + resolvedMethod = type.getOopField("_resolved_method"); + resolvedReceiver = type.getOopField("_resolved_receiver"); + ***********************************************************/ + } + + public JNIid(Address addr, ObjectHeap heap) { + super(addr); + this.heap = heap; + } + + public JNIid next() { + Address nextAddr = next.getValue(addr); + if (nextAddr == null) { + return null; + } + return new JNIid(nextAddr, heap); + } + + public Klass holder() { return (Klass) heap.newOop(holder.getValue(addr)); } + public int offset() { return (int) offset.getValue(addr); } + public Method method() { + return (Method) ((InstanceKlass) holder()).getMethods().getObjAt(offset()); + } + public Method resolvedMethod() { return (Method) heap.newOop(resolvedMethod.getValue(addr)); } + public Klass resolvedReceiver() { return (Klass) heap.newOop(resolvedReceiver.getValue(addr)); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaCallWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaCallWrapper.java new file mode 100644 index 00000000000..b313435c2a5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaCallWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/**

A JavaCallWrapper is constructed before each JavaCall and + destroyed after the call. Its purpose is to allocate/deallocate a + new handle block and to save/restore the last Java fp/sp. A + pointer to the JavaCallWrapper is stored on the stack.

+ +

NOTE: this port is very minimal and is only designed to get + frame traversal working. FIXME: we will have to add platform- + specific stuff later and therefore a factory for these + objects.

*/ + +public class JavaCallWrapper extends VMObject { + protected static AddressField anchorField; + private static AddressField lastJavaSPField; + private static AddressField lastJavaPCField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaCallWrapper"); + Type anchorType = db.lookupType("JavaFrameAnchor"); + + anchorField = type.getAddressField("_anchor"); + lastJavaSPField = anchorType.getAddressField("_last_Java_sp"); + lastJavaPCField = anchorType.getAddressField("_last_Java_pc"); + } + + public JavaCallWrapper(Address addr) { + super(addr); + } + + public Address getLastJavaSP() { + return lastJavaSPField.getValue(addr.addOffsetTo(anchorField.getOffset())); + } + + public Address getLastJavaPC() { + return lastJavaPCField.getValue(addr.addOffsetTo(anchorField.getOffset())); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java new file mode 100644 index 00000000000..43ad81d44e5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java @@ -0,0 +1,430 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** This is an abstract class because there are certain OS- and + CPU-specific operations (like the setting and getting of the last + Java frame pointer) which need to be factored out. These + operations are implemented by, for example, + SolarisSPARCJavaThread, and the concrete subclasses are + instantiated by the JavaThreadFactory in the Threads class. */ + +public class JavaThread extends Thread { + private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.JavaThread.DEBUG") != null; + + private static AddressField nextField; + private static sun.jvm.hotspot.types.OopField threadObjField; + private static AddressField anchorField; + private static AddressField lastJavaSPField; + private static AddressField lastJavaPCField; + private static CIntegerField threadStateField; + private static AddressField osThreadField; + + private static JavaThreadPDAccess access; + + // JavaThreadStates read from underlying process + private static int UNINITIALIZED; + private static int NEW; + private static int NEW_TRANS; + private static int IN_NATIVE; + private static int IN_NATIVE_TRANS; + private static int IN_VM; + private static int IN_VM_TRANS; + private static int IN_JAVA; + private static int IN_JAVA_TRANS; + private static int BLOCKED; + private static int BLOCKED_TRANS; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + Type anchorType = db.lookupType("JavaFrameAnchor"); + + nextField = type.getAddressField("_next"); + threadObjField = type.getOopField("_threadObj"); + anchorField = type.getAddressField("_anchor"); + lastJavaSPField = anchorType.getAddressField("_last_Java_sp"); + lastJavaPCField = anchorType.getAddressField("_last_Java_pc"); + threadStateField = type.getCIntegerField("_thread_state"); + osThreadField = type.getAddressField("_osthread"); + + UNINITIALIZED = db.lookupIntConstant("_thread_uninitialized").intValue(); + NEW = db.lookupIntConstant("_thread_new").intValue(); + NEW_TRANS = db.lookupIntConstant("_thread_new_trans").intValue(); + IN_NATIVE = db.lookupIntConstant("_thread_in_native").intValue(); + IN_NATIVE_TRANS = db.lookupIntConstant("_thread_in_native_trans").intValue(); + IN_VM = db.lookupIntConstant("_thread_in_vm").intValue(); + IN_VM_TRANS = db.lookupIntConstant("_thread_in_vm_trans").intValue(); + IN_JAVA = db.lookupIntConstant("_thread_in_Java").intValue(); + IN_JAVA_TRANS = db.lookupIntConstant("_thread_in_Java_trans").intValue(); + BLOCKED = db.lookupIntConstant("_thread_blocked").intValue(); + BLOCKED_TRANS = db.lookupIntConstant("_thread_blocked_trans").intValue(); + } + + public JavaThread(Address addr) { + super(addr); + } + + void setThreadPDAccess(JavaThreadPDAccess access) { + this.access = access; + } + + public JavaThread next() { + Address threadAddr = nextField.getValue(addr); + if (threadAddr == null) { + return null; + } + + return VM.getVM().getThreads().createJavaThreadWrapper(threadAddr); + } + + /** NOTE: for convenience, this differs in definition from the + underlying VM. Only "pure" JavaThreads return true; + CompilerThreads and JVMDIDebuggerThreads return false. FIXME: + consider encapsulating platform-specific functionality in an + object instead of using inheritance (which is the primary reason + we can't traverse CompilerThreads, etc; didn't want to have, for + example, "SolarisSPARCCompilerThread".) */ + public boolean isJavaThread() { return true; } + + public static AddressField getAnchorField() { return anchorField; } + + /** Get the last Java stack pointer */ + public Address getLastJavaSP() { + Address sp = lastJavaSPField.getValue(addr.addOffsetTo(anchorField.getOffset())); + return sp; + } + + public Address getLastJavaPC() { + Address pc = lastJavaPCField.getValue(addr.addOffsetTo(anchorField.getOffset())); + return pc; + } + + /** Abstract accessor to last Java frame pointer, implemented by + OS/CPU-specific JavaThread implementation. May return null if + there is no frame pointer or if it is not necessary on this + platform. */ + public Address getLastJavaFP(){ + return access.getLastJavaFP(addr); + } + + /** Abstract accessor to last Java pc, implemented by + OS/CPU-specific JavaThread implementation. May return null if + there is no frame pointer or if it is not necessary on this + platform. */ + + /* + public Address getLastJavaPC(){ + return access.getLastJavaPC(addr); + } + */ + + // FIXME: not yet implementable + // public abstract void setLastJavaFP(Address fp); + + /** A stack pointer older than any java frame stack pointer. Only + needed on some platforms; for example, see + thread_solaris_sparc.hpp. */ + public Address getBaseOfStackPointer(){ + return access.getBaseOfStackPointer(addr); + } + // FIXME: not yet implementable + // public abstract void setBaseOfStackPointer(Address fp); + + /** Tells whether the last Java frame is set */ + public boolean hasLastJavaFrame() { + return (getLastJavaSP() != null); + } + + /** Accessing frames */ + public Frame getLastFrame() { + // FIXME: would need to implement runtime routine + // "cacheStatePD(boolean)" for reflective system to be able to + // flush register windows on SPARC + return cookLastFrame(getLastFramePD()); + } + + /** Internal routine implemented by platform-dependent subclasses */ + protected Frame getLastFramePD(){ + return access.getLastFramePD(this, addr); + } + + /** Accessing frames. Returns the last Java VFrame or null if none + was present. (NOTE that this is mostly unusable in a debugging + system; see getLastJavaVFrameDbg, below, which provides very + different functionality.) */ + public JavaVFrame getLastJavaVFrame(RegisterMap regMap) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regMap != null, "a map must be given"); + } + Frame f = getLastFrame(); + if (f == null) { + return null; + } + for (VFrame vf = VFrame.newVFrame(f, regMap, this); vf != null; vf = vf.sender()) { + if (vf.isJavaFrame()) { + return (JavaVFrame) vf; + } + } + return null; + } + + /** This should only be used by a debugger. Uses the current frame + guess to attempt to get the topmost JavaVFrame. + (getLastJavaVFrame, as a port of the VM's routine, assumes the + VM is at a safepoint.) */ + public JavaVFrame getLastJavaVFrameDbg() { + RegisterMap regMap = newRegisterMap(true); + sun.jvm.hotspot.runtime.Frame f = getCurrentFrameGuess(); + if (f == null) return null; + boolean imprecise = true; + if (f.isInterpretedFrame() && !f.isInterpretedFrameValid()) { + if (DEBUG) { + System.out.println("Correcting for invalid interpreter frame"); + } + f = f.sender(regMap); + imprecise = false; + } + VFrame vf = VFrame.newVFrame(f, regMap, this, true, imprecise); + if (vf == null) { + if (DEBUG) { + System.out.println(" (Unable to create vframe for topmost frame guess)"); + } + return null; + } + if (vf.isJavaFrame()) { + return (JavaVFrame) vf; + } + return (JavaVFrame) vf.javaSender(); + } + + /** In this system, a JavaThread is the top-level factory for a + RegisterMap, since the JavaThread implementation is already + platform-specific and RegisterMap is also necessarily + platform-specific. The updateMap argument indicates whether the + register map needs to be updated, for example during stack + traversal -- see frame.hpp. */ + public RegisterMap newRegisterMap(boolean updateMap){ + return access.newRegisterMap(this, updateMap); + } + + /** This is only designed to be used by the debugging system. + Returns a "best guess" of the topmost frame on the stack. This + guess should be as "raw" as possible. For example, if the + topmost frame is an interpreter frame (the return PC is in the + interpreter) but is not a valid frame (i.e., the BCI has not yet + been set up) this should still return the topmost frame and not + the sender. Validity checks are done at higher levels. */ + public Frame getCurrentFrameGuess(){ + return access.getCurrentFrameGuess(this, addr); + } + + /** Also only intended for use by the debugging system. Provides the + same effect of OSThread::print(); that is, prints a value which + allows the user to intuitively understand which native OS thread + maps to this Java thread. Does not print a newline or leading or + trailing spaces. */ + public void printThreadIDOn(PrintStream tty) { + access.printThreadIDOn(addr,tty); + } + + public void printThreadID() { + printThreadIDOn(System.out); + } + + public ThreadProxy getThreadProxy() { + return access.getThreadProxy(addr); + } + + // + // Safepoint support + // + + public JavaThreadState getThreadState() { + int val = (int) threadStateField.getValue(addr); + if (val == UNINITIALIZED) { + return JavaThreadState.UNINITIALIZED; + } else if (val == NEW) { + return JavaThreadState.NEW; + } else if (val == NEW_TRANS) { + return JavaThreadState.NEW_TRANS; + } else if (val == IN_NATIVE) { + return JavaThreadState.IN_NATIVE; + } else if (val == IN_NATIVE_TRANS) { + return JavaThreadState.IN_NATIVE_TRANS; + } else if (val == IN_VM) { + return JavaThreadState.IN_VM; + } else if (val == IN_VM_TRANS) { + return JavaThreadState.IN_VM_TRANS; + } else if (val == IN_JAVA) { + return JavaThreadState.IN_JAVA; + } else if (val == IN_JAVA_TRANS) { + return JavaThreadState.IN_JAVA_TRANS; + } else if (val == BLOCKED) { + return JavaThreadState.BLOCKED; + } else if (val == BLOCKED_TRANS) { + return JavaThreadState.BLOCKED_TRANS; + } else { + throw new RuntimeException("Illegal thread state " + val); + } + } + // FIXME: not yet implementable + // public void setThreadState(JavaThreadState s); + + // + // Miscellaneous operations + // + + public OSThread getOSThread() { + return (OSThread) VMObjectFactory.newObject(OSThread.class, osThreadField.getValue(addr)); + } + + /** Gets the Java-side thread object for this JavaThread */ + public Oop getThreadObj() { + return VM.getVM().getObjectHeap().newOop(threadObjField.getValue(addr)); + } + + /** Get the Java-side name of this thread */ + public String getThreadName() { + Oop threadObj = getThreadObj(); + if (threadObj == null) { + return ""; + } + return OopUtilities.threadOopGetName(threadObj); + } + + // + // Oop traversal + // + + public void oopsDo(AddressVisitor oopVisitor) { + super.oopsDo(oopVisitor); + + // FIXME: add in the rest of the routine from the VM + + // Traverse the execution stack + for(StackFrameStream fst = new StackFrameStream(this); !fst.isDone(); fst.next()) { + fst.getCurrent().oopsDo(oopVisitor, fst.getRegisterMap()); + } + } + + public boolean isInStack(Address a) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(VM.getVM().isDebugging(), "Not yet implemented for non-debugging system"); + } + Address highest = highestLock(); + Address sp = lastSPDbg(); + // Be robust + if ((highest == null) || (sp == null)) return false; + return (highest.greaterThanOrEqual(a) && sp.lessThanOrEqual(a)); + + // FIXME: should traverse MonitorArray/MonitorChunks as in VM + } + + public Oop getCurrentParkBlocker() { + Oop threadObj = getThreadObj(); + if (threadObj != null) { + return OopUtilities.threadOopGetParkBlocker(threadObj); + } + return null; + } + + public void printInfoOn(PrintStream tty) { + + tty.println("State: " + getThreadState().toString()); + // Attempt to figure out the addresses covered by Java frames. + // NOTE: we should make this a method and let the Stackwalk panel use the result too. + // + sun.jvm.hotspot.runtime.Frame tmpFrame = getCurrentFrameGuess(); + if (tmpFrame != null ) { + Address sp = tmpFrame.getSP(); + Address maxSP = sp; + Address minSP = sp; + RegisterMap tmpMap = newRegisterMap(false); + while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) { + tmpFrame = tmpFrame.sender(tmpMap); + if (tmpFrame != null) { + sp = tmpFrame.getSP(); + maxSP = AddressOps.max(maxSP, sp); + minSP = AddressOps.min(minSP, sp); + } + } + tty.println("Stack in use by Java: " + minSP + " .. " + maxSP); + } else { + tty.println("No Java frames present"); + } + tty.println("Base of Stack: " + getBaseOfStackPointer()); + tty.println("Last_Java_SP: " + getLastJavaSP()); + tty.println("Last_Java_FP: " + getLastJavaFP()); + tty.println("Last_Java_PC: " + getLastJavaPC()); + // More stuff like saved_execption_pc, safepoint_state, ... + access.printInfoOn(addr, tty); + + } + + /////////////////////////////// + // // + // FIXME: add more accessors // + // // + /////////////////////////////// + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private Frame cookLastFrame(Frame fr) { + if (fr == null) { + return null; + } + + Address pc = fr.getPC(); + + if (Assert.ASSERTS_ENABLED) { + if (pc == null) { + Assert.that(VM.getVM().isDebugging(), "must have PC"); + } + } + return fr; + } + + private Address lastSPDbg() { + return access.getLastSP(addr); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThreadFactory.java new file mode 100644 index 00000000000..3dd7ca754a3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThreadFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +/** This is only used internally to this package and its operating + system- and CPU-specific subpackages to be able to instantiate the + correct platform-specific implementation of JavaThread which + handles the last Java frame pointer and other constructs + properly. */ + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +public interface JavaThreadFactory { + public JavaThread createJavaThreadWrapper(Address realThreadObjAddr, TypeDataBase db); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThreadPDAccess.java new file mode 100644 index 00000000000..e74fba919f9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThreadPDAccess.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public interface JavaThreadPDAccess { + public Address getLastJavaFP(Address addr); + public Address getLastJavaPC(Address addr); + public Address getBaseOfStackPointer(Address addr); + public Frame getLastFramePD(JavaThread thread, Address addr); + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap); + public Frame getCurrentFrameGuess(JavaThread thread, Address addr); + public void printThreadIDOn(Address addr, PrintStream tty); + public void printInfoOn(Address threadAddr, PrintStream tty); + public Address getLastSP(Address addr); + public ThreadProxy getThreadProxy(Address addr); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThreadState.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThreadState.java new file mode 100644 index 00000000000..e2b51741d40 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThreadState.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +/** This is a type-safe enum mirroring the JavaThreadState enum in + globalDefinitions.hpp. The conversion between the underlying ints + and these values is done in JavaThread. */ + +public class JavaThreadState { + private String stringVal; + + /** Should never happen (missing initialization) */ + public static final JavaThreadState UNINITIALIZED = new JavaThreadState("UNINITIALIZED"); + /** Just starting up, i.e., in process of being initialized */ + public static final JavaThreadState NEW = new JavaThreadState("NEW"); + /** Corresponding transition state (not used, included for completness) */ + public static final JavaThreadState NEW_TRANS = new JavaThreadState("NEW_TRANS"); + /** Running in native code */ + public static final JavaThreadState IN_NATIVE = new JavaThreadState("IN_NATIVE"); + /** Corresponding transition state */ + public static final JavaThreadState IN_NATIVE_TRANS = new JavaThreadState("IN_NATIVE_TRANS"); + /** Running in VM */ + public static final JavaThreadState IN_VM = new JavaThreadState("IN_VM"); + /** Corresponding transition state */ + public static final JavaThreadState IN_VM_TRANS = new JavaThreadState("IN_VM_TRANS"); + /** Running in Java or in stub code */ + public static final JavaThreadState IN_JAVA = new JavaThreadState("IN_JAVA"); + /** Corresponding transition state (not used, included for completness) */ + public static final JavaThreadState IN_JAVA_TRANS = new JavaThreadState("IN_JAVA_TRANS"); + /** Blocked in vm */ + public static final JavaThreadState BLOCKED = new JavaThreadState("BLOCKED"); + /** Corresponding transition state */ + public static final JavaThreadState BLOCKED_TRANS = new JavaThreadState("BLOCKED_TRANS"); + /** Special state needed, since we cannot suspend a thread when it is in native_trans */ + + private JavaThreadState(String stringVal) { + this.stringVal = stringVal; + } + + public String toString() { + return stringVal; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java new file mode 100644 index 00000000000..43e9520d32c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java @@ -0,0 +1,199 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public abstract class JavaVFrame extends VFrame { + /** JVM state */ + public abstract Method getMethod(); + public abstract int getBCI(); + public abstract StackValueCollection getLocals(); + public abstract StackValueCollection getExpressions(); + public abstract List getMonitors(); // List + + /** Test operation */ + public boolean isJavaFrame() { return true; } + + /** Package-internal constructor */ + JavaVFrame(Frame fr, RegisterMap regMap, JavaThread thread) { + super(fr, regMap, thread); + } + + /** Get monitor (if any) that this JavaVFrame is trying to enter */ + // FIXME: not yet implemented + // public Address getPendingMonitor(int frameCount); + + /** Printing used during stack dumps */ + // FIXME: not yet implemented + // void print_lock_info(int frame_count); + + /** Printing operations */ + + // + // FIXME: implement visitor pattern for traversing vframe contents? + // + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + super.printOn(tty); + + tty.print("\t"); + getMethod().printValueOn(tty); + tty.println(); + tty.println("\tbci:\t" + getBCI()); + + printStackValuesOn(tty, "locals", getLocals()); + printStackValuesOn(tty, "expressions", getExpressions()); + + // List + // FIXME: not yet implemented + // List list = getMonitors(); + // if (list.isEmpty()) { + // return; + // } + // for (int index = 0; index < list.size(); index++) { + // MonitorInfo monitor = (MonitorInfo) list.get(index); + // tty.print("\t obj\t"); + // monitor.getOwner().printValueOn(tty); + // tty.println(); + // tty.print("\t "); + // monitor.lock().printOn(tty); + // tty.println(); + // } + } + + public void printActivation(int index) { + printActivationOn(System.out, index); + } + + public void printActivationOn(PrintStream tty, int index) { + // frame number and method + tty.print(index + " - "); + printValueOn(tty); + tty.println(); + + if (VM.getVM().wizardMode()) { + printOn(tty); + tty.println(); + } + } + + /** Verification operations */ + public void verify() { + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof JavaVFrame)) { + return false; + } + + JavaVFrame other = (JavaVFrame) o; + + // Check static part + if (!getMethod().equals(other.getMethod())) { + return false; + } + + if (getBCI() != other.getBCI()) { + return false; + } + + // dynamic part - we just compare the frame pointer + if (! getFrame().getFP().equals(other.getFrame().getFP())) { + return false; + } + return true; + } + + public int hashCode() { + return getMethod().hashCode() ^ getBCI() ^ getFrame().getFP().hashCode(); + } + + /** Structural compare */ + public boolean structuralCompare(JavaVFrame other) { + // Check static part + if (!getMethod().equals(other.getMethod())) { + return false; + } + + if (getBCI() != other.getBCI()) { + return false; + } + + // Check locals + StackValueCollection locs = getLocals(); + StackValueCollection otherLocs = other.getLocals(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(locs.size() == otherLocs.size(), "sanity check"); + } + for (int i = 0; i < locs.size(); i++) { + // it might happen the compiler reports a conflict and + // the interpreter reports a bogus int. + if ( isCompiledFrame() && (locs.get(i)).getType() == BasicType.getTConflict()) continue; + if (other.isCompiledFrame() && (otherLocs.get(i)).getType() == BasicType.getTConflict()) continue; + + if (!locs.get(i).equals(otherLocs.get(i))) { + return false; + } + } + + // Check expressions + StackValueCollection exprs = getExpressions(); + StackValueCollection otherExprs = other.getExpressions(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(exprs.size() == otherExprs.size(), "sanity check"); + } + for (int i = 0; i < exprs.size(); i++) { + if (!exprs.get(i).equals(otherExprs.get(i))) { + return false; + } + } + + return true; + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private void printStackValuesOn(PrintStream tty, String title, StackValueCollection values) { + if (values.isEmpty()) { + return; + } + tty.println("\t" + title + ":"); + for (int index = 0; index < values.size(); index++) { + tty.print("\t" + index + "\t"); + values.get(index).printOn(tty); + tty.println(); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JvmtiAgentThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JvmtiAgentThread.java new file mode 100644 index 00000000000..211fcc3e8d6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JvmtiAgentThread.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** FIXME: should be in ../prims dir if that directory existed; for + now keep it in runtime dir */ + +public class JvmtiAgentThread extends JavaThread { + public JvmtiAgentThread(Address addr) { + super(addr); + } + + public boolean isJavaThread() { return false; } + + public boolean isJvmtiAgentThread() { return true; } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/LowMemoryDetectorThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/LowMemoryDetectorThread.java new file mode 100644 index 00000000000..02ec5e700cb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/LowMemoryDetectorThread.java @@ -0,0 +1,41 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +public class LowMemoryDetectorThread extends JavaThread { + public LowMemoryDetectorThread(Address addr) { + super(addr); + } + + public boolean isJavaThread() { return false; } + public boolean isHiddenFromExternalView() { return true; } + public boolean isLowMemoryDetectorThread() { return true; } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/MonitorInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/MonitorInfo.java new file mode 100644 index 00000000000..d689d43597c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/MonitorInfo.java @@ -0,0 +1,40 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.debugger.*; + +public class MonitorInfo { + private OopHandle owner; + private BasicLock lock; + + public MonitorInfo(OopHandle owner, BasicLock lock) { + this.owner = owner; + this.lock = lock; + } + + public OopHandle owner() { return owner; } + public BasicLock lock() { return lock; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/NativeSignatureIterator.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/NativeSignatureIterator.java new file mode 100644 index 00000000000..479dec88827 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/NativeSignatureIterator.java @@ -0,0 +1,112 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.oops.*; + +/** Specialized SignatureIterator: Used for native call purposes */ + +public abstract class NativeSignatureIterator extends SignatureIterator { + private Method method; +// [RGV] We need seperate JNI and Java offset values because in 64 bit mode, the argument offsets +// are not in sync with the Java stack. For example a long takes up 1 "C" stack entry +// but 2 Java stack entries. + private int offset; // The java stack offset + private int prepended; // number of prepended JNI parameters (1 JNIEnv, plus 1 mirror if static) + private int jni_offset; // the current parameter offset, starting with 0 + + public void doBool () { passInt(); jni_offset++; offset++; } + public void doChar () { passInt(); jni_offset++; offset++; } + public void doFloat () { + if (VM.getVM().isLP64()) { + passFloat(); + } else { + passInt(); + } + jni_offset++; offset++; + } + + public void doDouble() { + if (VM.getVM().isLP64()) { + passDouble(); jni_offset++; offset += 2; + } else { + passDouble(); jni_offset += 2; offset += 2; + } + } + + public void doByte () { passInt(); jni_offset++; offset++; } + public void doShort () { passInt(); jni_offset++; offset++; } + public void doInt () { passInt(); jni_offset++; offset++; } + + public void doLong () { + if (VM.getVM().isLP64()) { + passLong(); jni_offset++; offset += 2; + } else { + passLong(); jni_offset += 2; offset += 2; + } + } + + public void doVoid () { throw new RuntimeException("should not reach here"); } + public void doObject(int begin, int end) { passObject(); jni_offset++; offset++; } + public void doArray (int begin, int end) { passObject(); jni_offset++; offset++; } + + public Method method() { return method; } + public int offset() { return offset; } + public int jniOffset() { return jni_offset + prepended; } + public boolean isStatic() { return method.isStatic(); } + + public abstract void passInt(); + public abstract void passLong(); + public abstract void passObject(); + public abstract void passFloat(); + public abstract void passDouble(); + + public NativeSignatureIterator(Method method) { + super(method.getSignature()); + this.method = method; + offset = 0; + jni_offset = 0; + + int JNIEnv_words = 1; + int mirror_words = 1; + prepended = !isStatic() ? JNIEnv_words : JNIEnv_words + mirror_words; + } + + // iterate() calles the 2 virtual methods according to the following invocation syntax: + // + // {pass_int | pass_long | pass_object} + // + // Arguments are handled from left to right (receiver first, if any). + // The offset() values refer to the Java stack offsets but are 0 based and increasing. + // The java_offset() values count down to 0, and refer to the Java TOS. + // The jni_offset() values increase from 1 or 2, and refer to C arguments. + public void iterate() { + if (!isStatic()) { + // handle receiver (not handled by iterate because not in signature) + passObject(); jni_offset++; offset++; + } + iterateParameters(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/OSThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/OSThread.java new file mode 100644 index 00000000000..aa4fafecc0e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/OSThread.java @@ -0,0 +1,55 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +// The OSThread class holds OS-specific thread information. It is equivalent +// to the sys_thread_t structure of the classic JVM implementation. +public class OSThread extends VMObject { + private static JIntField interruptedField; + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("OSThread"); + interruptedField = type.getJIntField("_interrupted"); + } + + public OSThread(Address addr) { + super(addr); + } + + public boolean interrupted() { + return ((int)interruptedField.getValue(addr)) != 0; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java new file mode 100644 index 00000000000..a353ab4cdfd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java @@ -0,0 +1,130 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; + +public class ObjectMonitor extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + heap = VM.getVM().getObjectHeap(); + Type type = db.lookupType("ObjectMonitor"); + sun.jvm.hotspot.types.Field f = type.getField("_header"); + headerFieldOffset = f.getOffset(); + f = type.getField("_object"); + objectFieldOffset = f.getOffset(); + f = type.getField("_owner"); + ownerFieldOffset = f.getOffset(); + f = type.getField("FreeNext"); + FreeNextFieldOffset = f.getOffset(); + countField = type.getCIntegerField("_count"); + waitersField = type.getCIntegerField("_waiters"); + recursionsField = type.getCIntegerField("_recursions"); + } + + public ObjectMonitor(Address addr) { + super(addr); + } + + public Mark header() { + return new Mark(addr.addOffsetTo(headerFieldOffset)); + } + + // FIXME + // void set_header(markOop hdr); + + // FIXME: must implement and delegate to platform-dependent implementation + // public boolean isBusy(); + public boolean isEntered(sun.jvm.hotspot.runtime.Thread current) { + Address o = owner(); + if (current.threadObjectAddress().equals(o) || + current.isLockOwned(o)) { + return true; + } + return false; + } + + public Address owner() { return addr.getAddressAt(ownerFieldOffset); } + // FIXME + // void set_owner(void* owner); + + public long waiters() { return waitersField.getValue(addr); } + + public Address freeNext() { return addr.getAddressAt(FreeNextFieldOffset); } + // FIXME + // void set_queue(void* owner); + + public long count() { return countField.getValue(addr); } + // FIXME + // void set_count(intptr_t count); + + public long recursions() { return recursionsField.getValue(addr); } + + public OopHandle object() { + return addr.getOopHandleAt(objectFieldOffset); + } + + public long contentions() { + // refer to objectMonitor_xxx.inline.hpp - contentions definition. + // for Solaris and Linux, contentions is same as count. for Windows + // it is different (objectMonitor_win32.inline.hpp) + long count = count(); + if (VM.getVM().getOS().equals("win32")) { + // don't count the owner of the monitor + return count > 0? count - 1 : 0; + } else { + // Solaris and Linux + return count; + } + } + + // FIXME + // void* object_addr(); + // void set_object(void* obj); + + // The following four either aren't expressed as typed fields in + // vmStructs.cpp because they aren't strongly typed in the VM, or + // would confuse the SA's type system. + private static ObjectHeap heap; + private static long headerFieldOffset; + private static long objectFieldOffset; + private static long ownerFieldOffset; + private static long FreeNextFieldOffset; + private static CIntegerField countField; + private static CIntegerField waitersField; + private static CIntegerField recursionsField; + // FIXME: expose platform-dependent stuff +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java new file mode 100644 index 00000000000..641ab638bab --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java @@ -0,0 +1,127 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +public class ObjectSynchronizer { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type; + try { + type = db.lookupType("ObjectSynchronizer"); + AddressField blockListField; + blockListField = type.getAddressField("gBlockList"); + gBlockListAddr = blockListField.getValue(); + blockSize = db.lookupIntConstant("ObjectSynchronizer::_BLOCKSIZE").intValue(); + } catch (RuntimeException e) { } + type = db.lookupType("ObjectMonitor"); + objectMonitorTypeSize = type.getSize(); + } + + public long identityHashValueFor(Oop obj) { + Mark mark = obj.getMark(); + if (mark.isUnlocked()) { + // FIXME: can not generate marks in debugging system + return mark.hash(); + } else if (mark.hasMonitor()) { + ObjectMonitor monitor = mark.monitor(); + Mark temp = monitor.header(); + return temp.hash(); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(VM.getVM().isDebugging(), "Can not access displaced header otherwise"); + } + if (mark.hasDisplacedMarkHelper()) { + Mark temp = mark.displacedMarkHelper(); + return temp.hash(); + } + // FIXME: can not do anything else here in debugging system + return 0; + } + } + + public static Iterator objectMonitorIterator() { + if (gBlockListAddr != null) { + return new ObjectMonitorIterator(); + } else { + return null; + } + } + + private static class ObjectMonitorIterator implements Iterator { + + // JVMTI raw monitors are not pointed by gBlockList + // and are not included by this Iterator. May add them later. + + ObjectMonitorIterator() { + blockAddr = gBlockListAddr; + index = blockSize - 1; + block = new ObjectMonitor(blockAddr); + } + + public boolean hasNext() { + return (index > 0 || block.freeNext() != null); + } + + public Object next() { + Address addr; + if (index > 0) { + addr = blockAddr.addOffsetTo(index*objectMonitorTypeSize); + } else { + blockAddr = block.freeNext(); + index = blockSize - 1; + addr = blockAddr.addOffsetTo(index*objectMonitorTypeSize); + } + index --; + return new ObjectMonitor(addr); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + private ObjectMonitor block; + private int index; + private Address blockAddr; + } + + private static Address gBlockListAddr; + private static int blockSize; + private static long objectMonitorTypeSize; + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java new file mode 100644 index 00000000000..42481ba8ab9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java @@ -0,0 +1,480 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class PerfDataEntry extends VMObject { + private static JIntField entryLengthField; + private static JIntField nameOffsetField; + private static JIntField vectorLengthField; + private static JByteField dataTypeField; + private static JByteField flagsField; + private static JByteField dataUnitsField; + private static JByteField dataVariabilityField; + private static JIntField dataOffsetField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("PerfDataEntry"); + entryLengthField = type.getJIntField("entry_length"); + nameOffsetField = type.getJIntField("name_offset"); + vectorLengthField = type.getJIntField("vector_length"); + dataTypeField = type.getJByteField("data_type"); + flagsField = type.getJByteField("flags"); + dataUnitsField = type.getJByteField("data_units"); + dataVariabilityField = type.getJByteField("data_variability"); + dataOffsetField = type.getJIntField("data_offset"); + } + + public PerfDataEntry(Address addr) { + super(addr); + } + + // Accessors + + public int entryLength() { + return (int) entryLengthField.getValue(addr); + } + + public int nameOffset() { + return (int) nameOffsetField.getValue(addr); + } + + public int vectorLength() { + return (int) vectorLengthField.getValue(addr); + } + + // returns one of the constants in BasicType class + public int dataType() { + char ch = (char) (byte) dataTypeField.getValue(addr); + return BasicType.charToType(ch); + } + + public byte flags() { + return (byte) flagsField.getValue(addr); + } + + public boolean supported() { + return (flags() & 0x1) != 0; + } + + // NOTE: Keep this in sync with PerfData::Units enum in VM code + public interface PerfDataUnits { + public static final int U_None = 1; + public static final int U_Bytes = 2; + public static final int U_Ticks = 3; + public static final int U_Events = 4; + public static final int U_String = 5; + public static final int U_Hertz = 6; + } + + // returns one of the constants in PerfDataUnits + public int dataUnits() { + return (int) dataUnitsField.getValue(addr); + } + + // NOTE: Keep this in sync with PerfData::Variability enum in VM code + public interface PerfDataVariability { + public static final int V_Constant = 1; + public static final int V_Monotonic = 2; + public static final int V_Variable = 3; + } + + // returns one of the constants in PerfDataVariability + public int dataVariability() { + return (int) dataVariabilityField.getValue(addr); + } + + public int dataOffset() { + return (int) dataOffsetField.getValue(addr); + } + + public String name() { + int off = nameOffset(); + return CStringUtilities.getString(addr.addOffsetTo(off)); + } + + public boolean booleanValue() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(vectorLength() == 0 && + dataType() == BasicType.tBoolean, "not a boolean"); + } + return addr.getJBooleanAt(dataOffset()); + } + + public char charValue() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(vectorLength() == 0 && + dataType() == BasicType.tChar, "not a char"); + } + return addr.getJCharAt(dataOffset()); + } + + public byte byteValue() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(vectorLength() == 0 && + dataType() == BasicType.tByte, "not a byte"); + } + return addr.getJByteAt(dataOffset()); + + } + + public short shortValue() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(vectorLength() == 0 && + dataType() == BasicType.tShort, "not a short"); + } + return addr.getJShortAt(dataOffset()); + } + + public int intValue() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(vectorLength() == 0 && + dataType() == BasicType.tInt, "not an int"); + } + return addr.getJIntAt(dataOffset()); + } + + public long longValue() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(vectorLength() == 0 && + dataType() == BasicType.tLong, "not a long"); + } + return addr.getJLongAt(dataOffset()); + } + + public float floatValue() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(vectorLength() == 0 && + dataType() == BasicType.tFloat, "not a float"); + } + return addr.getJFloatAt(dataOffset()); + } + + public double doubleValue() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(vectorLength() == 0 && + dataType() == BasicType.tDouble, "not a double"); + } + return addr.getJDoubleAt(dataOffset()); + } + + public boolean[] booleanArrayValue() { + int len = vectorLength(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(len > 0 && + dataType() == BasicType.tBoolean, "not a boolean vector"); + } + boolean[] res = new boolean[len]; + final int off = dataOffset(); + final long size = getHeap().getBooleanSize(); + for (int i = 0; i < len; i++) { + res[i] = addr.getJBooleanAt(off + i * size); + } + return res; + } + + public char[] charArrayValue() { + int len = vectorLength(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(len > 0 && + dataType() == BasicType.tChar, "not a char vector"); + } + char[] res = new char[len]; + final int off = dataOffset(); + final long size = getHeap().getCharSize(); + for (int i = 0; i < len; i++) { + res[i] = addr.getJCharAt(off + i * size); + } + return res; + } + + public byte[] byteArrayValue() { + int len = vectorLength(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(len > 0 && + dataType() == BasicType.tByte, "not a byte vector"); + } + byte[] res = new byte[len]; + final int off = dataOffset(); + final long size = getHeap().getByteSize(); + for (int i = 0; i < len; i++) { + res[i] = addr.getJByteAt(off + i * size); + } + return res; + } + + public short[] shortArrayValue() { + int len = vectorLength(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(len > 0 && + dataType() == BasicType.tShort, "not a short vector"); + } + short[] res = new short[len]; + final int off = dataOffset(); + final long size = getHeap().getShortSize(); + for (int i = 0; i < len; i++) { + res[i] = addr.getJShortAt(off + i * size); + } + return res; + } + + public int[] intArrayValue() { + int len = vectorLength(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(len > 0 && + dataType() == BasicType.tInt, "not an int vector"); + } + int[] res = new int[len]; + final int off = dataOffset(); + final long size = getHeap().getIntSize(); + for (int i = 0; i < len; i++) { + res[i] = addr.getJIntAt(off + i * size); + } + return res; + } + + public long[] longArrayValue() { + int len = vectorLength(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(len > 0 && + dataType() == BasicType.tLong, "not a long vector"); + } + long[] res = new long[len]; + final int off = dataOffset(); + final long size = getHeap().getLongSize(); + for (int i = 0; i < len; i++) { + res[i] = addr.getJLongAt(off + i * size); + } + return res; + } + + public float[] floatArrayValue() { + int len = vectorLength(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(len > 0 && + dataType() == BasicType.tFloat, "not a float vector"); + } + float[] res = new float[len]; + final int off = dataOffset(); + final long size = getHeap().getFloatSize(); + for (int i = 0; i < len; i++) { + res[i] = addr.getJFloatAt(off + i * size); + } + return res; + } + + public double[] doubleArrayValue() { + int len = vectorLength(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(len > 0 && + dataType() == BasicType.tDouble, "not a double vector"); + } + double[] res = new double[len]; + final int off = dataOffset(); + final long size = getHeap().getDoubleSize(); + for (int i = 0; i < len; i++) { + res[i] = addr.getJDoubleAt(off + i * size); + } + return res; + } + + // value as String + public String valueAsString() { + int dataType = dataType(); + int len = vectorLength(); + String str = null; + if (len == 0) { // scalar + switch (dataType) { + case BasicType.tBoolean: + str = Boolean.toString(booleanValue()); + break; + case BasicType.tChar: + str = "'" + Character.toString(charValue()) + "'"; + break; + case BasicType.tByte: + str = Byte.toString(byteValue()); + break; + case BasicType.tShort: + str = Short.toString(shortValue()); + break; + case BasicType.tInt: + str = Integer.toString(intValue()); + break; + case BasicType.tLong: + str = Long.toString(longValue()); + break; + case BasicType.tFloat: + str = Float.toString(floatValue()); + break; + case BasicType.tDouble: + str = Double.toString(doubleValue()); + break; + default: + str = ""; + break; + } + } else { // vector + switch (dataType) { + case BasicType.tBoolean: { + boolean[] res = booleanArrayValue(); + StringBuffer buf = new StringBuffer(); + buf.append('['); + for (int i = 0; i < res.length; i++) { + buf.append(Boolean.toString(res[i])); + buf.append(", "); + } + buf.append(']'); + str = buf.toString(); + break; + } + + case BasicType.tChar: { + // char[] is returned as a String + str = new String(charArrayValue()); + break; + } + + case BasicType.tByte: { + // byte[] is returned as a String + try { + str = new String(byteArrayValue(), "US-ASCII"); + } catch (java.io.UnsupportedEncodingException e) { + str = "can't decode string : " + e.getMessage(); + } + break; + } + + case BasicType.tShort: { + short[] res = shortArrayValue(); + StringBuffer buf = new StringBuffer(); + buf.append('['); + for (int i = 0; i < res.length; i++) { + buf.append(Short.toString(res[i])); + buf.append(", "); + } + buf.append(']'); + str = buf.toString(); + break; + } + + case BasicType.tInt: { + int[] res = intArrayValue(); + StringBuffer buf = new StringBuffer(); + buf.append('['); + for (int i = 0; i < res.length; i++) { + buf.append(Integer.toString(res[i])); + buf.append(", "); + } + buf.append(']'); + str = buf.toString(); + break; + } + + case BasicType.tLong: { + long[] res = longArrayValue(); + StringBuffer buf = new StringBuffer(); + buf.append('['); + for (int i = 0; i < res.length; i++) { + buf.append(Long.toString(res[i])); + buf.append(", "); + } + buf.append(']'); + str = buf.toString(); + break; + } + + case BasicType.tFloat: { + float[] res = floatArrayValue(); + StringBuffer buf = new StringBuffer(); + buf.append('['); + for (int i = 0; i < res.length; i++) { + buf.append(Float.toString(res[i])); + buf.append(", "); + } + buf.append(']'); + str = buf.toString(); + break; + } + + case BasicType.tDouble: { + double[] res = doubleArrayValue(); + StringBuffer buf = new StringBuffer(); + buf.append('['); + for (int i = 0; i < res.length; i++) { + buf.append(Double.toString(res[i])); + buf.append(", "); + } + buf.append(']'); + str = buf.toString(); + break; + } + + default: + str = ""; + break; + } + } + + // add units + switch (dataUnits()) { + case PerfDataUnits.U_None: + break; + case PerfDataUnits.U_Bytes: + str += " byte(s)"; + break; + case PerfDataUnits.U_Ticks: + str += " tick(s)"; + break; + case PerfDataUnits.U_Events: + str += " event(s)"; + break; + case PerfDataUnits.U_String: + break; + case PerfDataUnits.U_Hertz: + str += " Hz"; + break; + } + + return str; + } + + // -- Internals only below this point + private ObjectHeap getHeap() { + return VM.getVM().getObjectHeap(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/PerfDataPrologue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/PerfDataPrologue.java new file mode 100644 index 00000000000..c1a729c0747 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/PerfDataPrologue.java @@ -0,0 +1,106 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; + +public class PerfDataPrologue extends VMObject { + private static JIntField magicField; + private static JByteField byteOrderField; + private static JByteField majorVersionField; + private static JByteField minorVersionField; + private static JByteField accessibleField; + private static JIntField usedField; + private static JIntField overflowField; + private static JLongField modTimeStampField; + private static JIntField entryOffsetField; + private static JIntField numEntriesField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("PerfDataPrologue"); + magicField = type.getJIntField("magic"); + byteOrderField = type.getJByteField("byte_order"); + majorVersionField = type.getJByteField("major_version"); + minorVersionField = type.getJByteField("minor_version"); + accessibleField = type.getJByteField("accessible"); + usedField = type.getJIntField("used"); + overflowField = type.getJIntField("overflow"); + modTimeStampField = type.getJLongField("mod_time_stamp"); + entryOffsetField = type.getJIntField("entry_offset"); + numEntriesField = type.getJIntField("num_entries"); + } + + public PerfDataPrologue(Address addr) { + super(addr); + } + + // Accessors + public int magic() { + return (int) magicField.getValue(addr); + } + + public byte byteOrder() { + return (byte) byteOrderField.getValue(addr); + } + + public byte majorVersion() { + return (byte) majorVersionField.getValue(addr); + } + + public boolean accessible() { + return ((byte) accessibleField.getValue(addr)) != (byte)0; + } + + public int used() { + return (int) usedField.getValue(addr); + } + + public int overflow() { + return (int) overflowField.getValue(addr); + } + + public long modTimeStamp() { + return (long) modTimeStampField.getValue(addr); + } + + public int entryOffset() { + return (int) entryOffsetField.getValue(addr); + } + + public int numEntries() { + return (int) numEntriesField.getValue(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/PerfMemory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/PerfMemory.java new file mode 100644 index 00000000000..117829be8c8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/PerfMemory.java @@ -0,0 +1,109 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; + +public class PerfMemory { + private static AddressField startField; + private static AddressField endField; + private static AddressField topField; + private static CIntegerField capacityField; + private static AddressField prologueField; + private static JIntField initializedField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("PerfMemory"); + startField = type.getAddressField("_start"); + endField = type.getAddressField("_end"); + topField = type.getAddressField("_top"); + capacityField = type.getCIntegerField("_capacity"); + prologueField = type.getAddressField("_prologue"); + initializedField = type.getJIntField("_initialized"); + } + + // Accessors + public static Address start() { + return startField.getValue(); + } + + public static Address end() { + return endField.getValue(); + } + + public static Address top() { + return topField.getValue(); + } + + public static long capacity() { + return capacityField.getValue(); + } + + public static boolean initialized() { + return ((int) initializedField.getValue()) != 0; + } + + public static PerfDataPrologue prologue() { + return (PerfDataPrologue) VMObjectFactory.newObject( + PerfDataPrologue.class, prologueField.getValue()); + } + + public static boolean contains(Address addr) { + return start() != null && + addr.minus(start()) >= 0 && + end().minus(addr) > 0; + } + + // an interface supplied to iterate PerfDataEntries + public static interface PerfDataEntryVisitor { + // returns false to stop the iteration + public boolean visit(PerfDataEntry pde); + } + + public static void iterate(PerfDataEntryVisitor visitor) { + PerfDataPrologue header = prologue(); + int off = header.entryOffset(); + int num = header.numEntries(); + Address addr = header.getAddress(); + + for (int i = 0; i < num; i++) { + PerfDataEntry pde = (PerfDataEntry) VMObjectFactory.newObject( + PerfDataEntry.class, addr.addOffsetTo(off)); + off += pde.entryLength(); + if (visitor.visit(pde) == false) return; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/RegisterMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/RegisterMap.java new file mode 100644 index 00000000000..149aa0cc027 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/RegisterMap.java @@ -0,0 +1,210 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/**

A companion structure used for stack traversal. The + RegisterMap contains misc. information needed in order to do + correct stack traversal of stack frames. Hence, it must always be + passed in as an argument to Frame.sender(RegisterMap).

+ +

The use of RegisterMaps is slightly different in the + Serviceability Agent APIs than in the VM itself. In the VM, a + RegisterMap is created either for a particular thread or cloned + from another RegisterMap. In these APIs, a JavaThread is the + top-level factory for RegisterMaps, and RegisterMaps know how to + copy themselves (through either the clone() or copy() + methods).

*/ + +public abstract class RegisterMap implements Cloneable { + /** Location of registers */ + protected Address[] location; + // FIXME: don't know about LocationValidType + protected long[] locationValid; + /** Should include argument_oop marked locations for compiler */ + protected boolean includeArgumentOops; + /** Reference to current thread */ + protected JavaThread thread; + /** Tells if the register map needs to be updated when traversing the stack */ + protected boolean updateMap; + /** Location of a frame where the pc is not at a call (NULL if no frame exists) */ + protected static int regCount; + protected static int locationValidTypeSize; + protected static int locationValidSize; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + regCount = db.lookupIntConstant("ConcreteRegisterImpl::number_of_registers").intValue(); + // FIXME: don't know about LocationValidType. The LocationValidType is typedef'ed as julong + // so used julong to get the size of LocationValidType. + locationValidTypeSize = (int)db.lookupType("julong").getSize() * 8; + locationValidSize = (regCount + locationValidTypeSize - 1) / locationValidTypeSize; + } + + protected RegisterMap(JavaThread thread, boolean updateMap) { + this.thread = thread; + this.updateMap = updateMap; + location = new Address[regCount]; + locationValid = new long[locationValidSize]; + clear(); + } + + /** Makes a copy of map into this */ + protected RegisterMap(RegisterMap map) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "RegisterMap must be present"); + } + this.thread = map.getThread(); + this.updateMap = map.getUpdateMap(); + this.includeArgumentOops = map.getIncludeArgumentOops(); + location = new Address[map.location.length]; + locationValid = new long[map.locationValid.length]; + initializeFromPD(map); + if (updateMap) { + for (int i = 0; i < locationValidSize; i++) { + long bits = (!getUpdateMap()) ? 0 : map.locationValid[i]; + locationValid[i] = bits; + // for whichever bits are set, pull in the corresponding map->_location + int j = i*locationValidTypeSize; + while (bits != 0) { + if ((bits & 1) != 0) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(0 <= j && j < regCount, "range check"); + } + location[j] = map.location[j]; + } + bits >>>= 1; + j += 1; + } + } + } + } + + public abstract Object clone(); + + public RegisterMap copy() { + return (RegisterMap) clone(); + } + + public void clear() { + setIncludeArgumentOops(true); + if (!VM.getVM().isCore()) { + if (updateMap) { + for (int i = 0; i < locationValid.length; i++) { + locationValid[i] = 0; + } + clearPD(); + } else { + initializePD(); + } + } + } + + public Address getLocation(VMReg reg) { + int i = reg.getValue(); + int index = i / locationValidTypeSize; + if (Assert.ASSERTS_ENABLED) { + Assert.that(0 <= i && i < regCount, "sanity check"); + Assert.that(0 <= index && index < locationValidSize, "sanity check"); + } + if ((locationValid[index] & (1 << i % locationValidTypeSize)) != 0) { + return location[i]; + } else { + return getLocationPD(reg); + } + } + + public void setLocation(VMReg reg, Address loc) { + int i = reg.getValue(); + int index = i / locationValidTypeSize; + if (Assert.ASSERTS_ENABLED) { + Assert.that(0 <= i && i < regCount, "sanity check"); + Assert.that(0 <= index && index < locationValidSize, "sanity check"); + Assert.that(updateMap, "updating map that does not need updating"); + } + location[i] = loc; + locationValid[index] |= (1 << (i % locationValidTypeSize)); + } + + public boolean getIncludeArgumentOops() { + return includeArgumentOops; + } + + public void setIncludeArgumentOops(boolean f) { + includeArgumentOops = f; + } + + public JavaThread getThread() { + return thread; + } + + public boolean getUpdateMap() { + return updateMap; + } + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + tty.println("Register map"); + for (int i = 0; i < location.length; i++) { + Address src = getLocation(new VMReg(i)); + if (src != null) { + tty.print(" " + VMRegImpl.getRegisterName(i) + + " [" + src + "] = "); + if (src.andWithMask(VM.getVM().getAddressSize() - 1) != null) { + tty.print(""); + } else { + tty.print(src.getAddressAt(0)); + } + } + } + } + + /** Platform-dependent clear() functionality */ + protected abstract void clearPD(); + /** Platform-dependent initialize() functionality */ + protected abstract void initializePD(); + /** Platform-dependent initializeFrom() functionality */ + protected abstract void initializeFromPD(RegisterMap map); + /** Platform-dependent getLocation() functionality */ + protected abstract Address getLocationPD(VMReg reg); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ResultTypeFinder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ResultTypeFinder.java new file mode 100644 index 00000000000..fa7cd3e283b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ResultTypeFinder.java @@ -0,0 +1,34 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.oops.*; + +/** Specialized SignatureIterator: Used to compute the result type. */ + +public class ResultTypeFinder extends SignatureInfo { + protected void set(int size, int type) { if (isReturnType()) this.type = type; } + public ResultTypeFinder(Symbol signature) { super(signature); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/SignatureConverter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/SignatureConverter.java new file mode 100644 index 00000000000..6bd6df72458 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/SignatureConverter.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.oops.*; + +public class SignatureConverter extends SignatureIterator { + private StringBuffer buf; + private boolean first = true; + + public SignatureConverter(Symbol sig, StringBuffer buf) { + super(sig); + this.buf = buf; + } + + public void doBool () { appendComma(); buf.append("boolean"); } + public void doChar () { appendComma(); buf.append("char"); } + public void doFloat () { appendComma(); buf.append("float"); } + public void doDouble() { appendComma(); buf.append("double"); } + public void doByte () { appendComma(); buf.append("byte"); } + public void doShort () { appendComma(); buf.append("short"); } + public void doInt () { appendComma(); buf.append("int"); } + public void doLong () { appendComma(); buf.append("long"); } + public void doVoid () { + if(isReturnType()) { + appendComma(); buf.append("void"); + } else { + throw new RuntimeException("Should not reach here"); + } + } + + public void doObject(int begin, int end) { doObject(begin, end, true); } + public void doArray (int begin, int end) { + appendComma(); + int inner = arrayInnerBegin(begin); + switch (_signature.getByteAt(inner)) { + case 'B': buf.append("byte"); break; + case 'C': buf.append("char"); break; + case 'D': buf.append("double"); break; + case 'F': buf.append("float"); break; + case 'I': buf.append("int"); break; + case 'J': buf.append("long"); break; + case 'S': buf.append("short"); break; + case 'Z': buf.append("boolean"); break; + case 'L': doObject(inner + 1, end, false); break; + default: break; + } + for (int i = 0; i < inner - begin + 1; i++) { + buf.append("[]"); + } + } + + public void appendComma() { + if (!first) { + buf.append(", "); + } + first = false; + } + + private void doObject(int begin, int end, boolean comma) { + if (comma) { + appendComma(); + } + appendSubstring(begin, end - 1); + } + + private void appendSubstring(int begin, int end) { + for (int i = begin; i < end; i++) { + buf.append((char) (_signature.getByteAt(i) & 0xFF)); + } + } + + private int arrayInnerBegin(int begin) { + while (_signature.getByteAt(begin) == '[') { + ++begin; + } + return begin; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/SignatureInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/SignatureInfo.java new file mode 100644 index 00000000000..85b41a95604 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/SignatureInfo.java @@ -0,0 +1,63 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.oops.*; + +public abstract class SignatureInfo extends SignatureIterator { + protected boolean hasIterated; // need this because iterate cannot be called in constructor (set is virtual!) + protected int size; + protected int type; // BasicType + + protected void lazyIterate() { + if (!hasIterated) { + iterate(); + hasIterated = true; + } + } + + protected abstract void set(int size, int /*BasicType*/ type); + + public void doBool() { set(BasicTypeSize.getTBooleanSize(), BasicType.getTBoolean()); } + public void doChar() { set(BasicTypeSize.getTCharSize(), BasicType.getTChar()); } + public void doFloat() { set(BasicTypeSize.getTFloatSize(), BasicType.getTFloat()); } + public void doDouble() { set(BasicTypeSize.getTDoubleSize(), BasicType.getTDouble()); } + public void doByte() { set(BasicTypeSize.getTByteSize(), BasicType.getTByte()); } + public void doShort() { set(BasicTypeSize.getTShortSize(), BasicType.getTShort()); } + public void doInt() { set(BasicTypeSize.getTIntSize(), BasicType.getTInt()); } + public void doLong() { set(BasicTypeSize.getTLongSize(), BasicType.getTLong()); } + public void doVoid() { set(BasicTypeSize.getTVoidSize(), BasicType.getTVoid()); } + public void doObject(int begin, int end) { set(BasicTypeSize.getTObjectSize(), BasicType.getTObject()); } + public void doArray(int begin, int end) { set(BasicTypeSize.getTArraySize(), BasicType.getTArray()); } + + public SignatureInfo(Symbol signature) { + super(signature); + + type = BasicType.getTIllegal(); + } + + public int size() { lazyIterate(); return size; } + public int type() { lazyIterate(); return type; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/SignatureIterator.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/SignatureIterator.java new file mode 100644 index 00000000000..4707452e334 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/SignatureIterator.java @@ -0,0 +1,204 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.oops.*; + +/**

SignatureIterators iterate over a Java signature (or parts of it). + (Syntax according to: "The Java Virtual Machine Specification" by + Tim Lindholm & Frank Yellin; section 4.3 Descriptors; p. 89ff.)

+ +

Example: Iterating over +

+([Lfoo;D)I
+0123456789
+
+ + using

+ +
+iterateParameters() calls: do_array(2, 7); do_double();
+iterateReturntype() calls:                              do_int();
+iterate()           calls: do_array(2, 7); do_double(); do_int();
+
+is_returnType()        is: false         ; false      ; true
+
+*/ + +public abstract class SignatureIterator { + protected Symbol _signature; // the signature to iterate over + protected int _index; // the current character index (only valid during iteration) + protected int _parameter_index; // the current parameter index (0 outside iteration phase) + + protected void expect(char c) { + if (_signature.getByteAt(_index) != (byte) c) { + throw new RuntimeException("expecting '" + c + "'"); + } + _index++; + } + protected void skipOptionalSize() { + byte c = _signature.getByteAt(_index); + while ('0' <= c && c <= '9') { + c = _signature.getByteAt(++_index); + } + } + // returns the parameter size in words (0 for void) + protected int parseType() { + switch(_signature.getByteAt(_index)) { + case 'B': doByte (); _index++; return BasicTypeSize.getTByteSize(); + case 'C': doChar (); _index++; return BasicTypeSize.getTCharSize(); + case 'D': doDouble(); _index++; return BasicTypeSize.getTDoubleSize(); + case 'F': doFloat (); _index++; return BasicTypeSize.getTFloatSize(); + case 'I': doInt (); _index++; return BasicTypeSize.getTIntSize(); + case 'J': doLong (); _index++; return BasicTypeSize.getTLongSize(); + case 'S': doShort (); _index++; return BasicTypeSize.getTShortSize(); + case 'Z': doBool (); _index++; return BasicTypeSize.getTBooleanSize(); + case 'V': + { + if (!isReturnType()) { + throw new RuntimeException("illegal parameter type V (void)"); + } + + doVoid(); _index++; + return BasicTypeSize.getTVoidSize(); + } + case 'L': + { + int begin = ++_index; + while (_signature.getByteAt(_index++) != ';') ; + doObject(begin, _index); + return BasicTypeSize.getTObjectSize(); + } + case '[': + { + int begin = ++_index; + skipOptionalSize(); + while (_signature.getByteAt(_index) == '[') { + _index++; + skipOptionalSize(); + } + if (_signature.getByteAt(_index) == 'L') { + while (_signature.getByteAt(_index++) != ';') ; + } else { + _index++; + } + doArray(begin, _index); + return BasicTypeSize.getTArraySize(); + } + } + throw new RuntimeException("Should not reach here"); + } + protected void checkSignatureEnd() { + if (_index < _signature.getLength()) { + System.err.println("too many chars in signature"); + _signature.printValueOn(System.err); + System.err.println(" @ " + _index); + } + } + + public SignatureIterator(Symbol signature) { + _signature = signature; + _parameter_index = 0; + } + + // + // Iteration + // + + // dispatches once for field signatures + public void dispatchField() { + // no '(', just one (field) type + _index = 0; + _parameter_index = 0; + parseType(); + checkSignatureEnd(); + } + + // iterates over parameters only + public void iterateParameters() { + // Parse parameters + _index = 0; + _parameter_index = 0; + expect('('); + while (_signature.getByteAt(_index) != ')') { + _parameter_index += parseType(); + } + expect(')'); + _parameter_index = 0; // so isReturnType() is false outside iteration + } + + // iterates over returntype only + public void iterateReturntype() { + // Ignore parameters + _index = 0; + expect('('); + while (_signature.getByteAt(_index) != ')') { + _index++; + } + expect(')'); + // Parse return type + _parameter_index = -1; + parseType(); + checkSignatureEnd(); + _parameter_index = 0; // so isReturnType() is false outside iteration + } + + // iterates over whole signature + public void iterate() { + // Parse parameters + _index = 0; + _parameter_index = 0; + expect('('); + while (_signature.getByteAt(_index) != ')') { + _parameter_index += parseType(); + } + expect(')'); + // Parse return type + _parameter_index = -1; + parseType(); + checkSignatureEnd(); + _parameter_index = 0; // so isReturnType() is false outside iteration + } + + // Returns the word index of the current parameter; returns a negative value at the return type + public int parameterIndex() { return _parameter_index; } + public boolean isReturnType() { return (parameterIndex() < 0); } + + // Basic types + public abstract void doBool (); + public abstract void doChar (); + public abstract void doFloat (); + public abstract void doDouble(); + public abstract void doByte (); + public abstract void doShort (); + public abstract void doInt (); + public abstract void doLong (); + public abstract void doVoid (); + + // Object types (begin indexes the first character of the entry, end + // indexes the first character after the entry) + public abstract void doObject(int begin, int end); + public abstract void doArray (int begin, int end); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StackFrameStream.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StackFrameStream.java new file mode 100644 index 00000000000..152902bba67 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StackFrameStream.java @@ -0,0 +1,105 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.utilities.*; + +/**

StackFrameStream iterates through the frames of a thread + starting from top most frame. It automatically takes care of + updating the location of all (callee-saved) registers. Notice: If + a thread is stopped at a safepoint, all registers are saved, not + only the callee-saved ones.

+ +

Use:

+ +
+    for(StackFrameStream fst = new StackFrameStream(thread); !fst.isDone(); fst.next()) {
+      ...
+    }
+    
+*/ + +public class StackFrameStream { + private Frame fr; + private RegisterMap regMap; + private boolean isDone; + + /** Equivalent to StackFrameStream(thread, true) */ + public StackFrameStream(JavaThread thread) { + this(thread, true); + } + + public StackFrameStream(JavaThread thread, boolean update) { + if (!VM.getVM().isDebugging()) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(thread.hasLastJavaFrame(), "sanity check"); + } + fr = thread.getLastFrame(); + regMap = thread.newRegisterMap(update); + isDone = false; + } else { + // Special case code to find the topmost Java frame + // FIXME: should check to see whether we're at safepoint, and if + // so, skip the "current frame guess" call and unnecessary + // stackwalking work + fr = thread.getCurrentFrameGuess(); + regMap = thread.newRegisterMap(update); + while ((fr != null) && (!fr.isJavaFrame())) { + if (fr.isFirstFrame()) { + fr = null; + } else { + fr = fr.sender(regMap); + } + } + if (fr == null) { + isDone = true; + } + } + } + + /** Iteration */ + public boolean isDone() { + if (isDone) { + return true; + } else { + if (fr == null) { + isDone = true; + return true; + } + isDone = fr.isFirstFrame(); + return false; + } + } + + public void next() { + if (!isDone) { + fr = fr.sender(regMap); + } + } + + /** Query */ + public Frame getCurrent() { return fr; } + public RegisterMap getRegisterMap() { return regMap; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StackValue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StackValue.java new file mode 100644 index 00000000000..246b7098c77 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StackValue.java @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.utilities.*; + +public class StackValue { + private int type; + private OopHandle handleValue; + private long integerValue; + + public StackValue() { + type = BasicType.getTConflict(); + } + + public StackValue(OopHandle h) { + handleValue = h; + type = BasicType.getTObject(); + } + + public StackValue(long i) { + integerValue = i; + type = BasicType.getTInt(); + } + + /** This returns one of the "enum" values in BasicType.java */ + public int getType() { + return type; + } + + public OopHandle getObject() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(type == BasicType.getTObject(), "type check"); + } + return handleValue; + } + + public long getInteger() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(type == BasicType.getTInt(), "type check"); + } + return integerValue; + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!arg.getClass().equals(getClass())) { + return false; + } + + StackValue sv = (StackValue) arg; + if (type != sv.type) { + return false; + } + if (type == BasicType.getTObject()) { + return handleValue.equals(sv.handleValue); + } else if (type == BasicType.getTInt()) { + return (integerValue == sv.integerValue); + } else { + // Conflict type (not yet used) + return true; + } + } + + public int hashCode() { + if (type == BasicType.getTObject()) { + return handleValue.hashCode(); + } else { + // Returns 0 for conflict type + return (int) integerValue; + } + } + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + if (type == BasicType.getTInt()) { + tty.print(integerValue + " (long) " + (int) integerValue + " (int)"); + } else if (type == BasicType.getTObject()) { + tty.print("<" + handleValue + ">"); + } else if (type == BasicType.getTConflict()) { + tty.print("conflict"); + } else { + throw new RuntimeException("should not reach here"); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StackValueCollection.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StackValueCollection.java new file mode 100644 index 00000000000..8cb5706b173 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StackValueCollection.java @@ -0,0 +1,54 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; + +public class StackValueCollection { + private List list; + + public StackValueCollection() { list = new ArrayList(); } + public StackValueCollection(int length) { list = new ArrayList(length); } + + public void add(StackValue val) { list.add(val); } + public int size() { return list.size(); } + public boolean isEmpty() { return (size() == 0); } + public StackValue get(int i) { return (StackValue) list.get(i); } + + // Get typed locals/expressions + // FIXME: must figure out whether word swapping is necessary on x86 + public boolean booleanAt(int slot) { return (int)get(slot).getInteger() != 0; } + public byte byteAt(int slot) { return (byte) get(slot).getInteger(); } + public char charAt(int slot) { return (char) get(slot).getInteger(); } + public short shortAt(int slot) { return (short) get(slot).getInteger(); } + public int intAt(int slot) { return (int) get(slot).getInteger(); } + public long longAt(int slot) { return VM.getVM().buildLongFromIntsPD((int) get(slot).getInteger(), + (int) get(slot+1).getInteger()); } + public OopHandle oopHandleAt(int slot) { return get(slot).getObject(); } + public float floatAt(int slot) { return Float.intBitsToFloat(intAt(slot)); } + public double doubleAt(int slot) { return Double.longBitsToDouble(longAt(slot)); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java new file mode 100644 index 00000000000..be55ca524c7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** Very minimal port for now to get frames working */ + +public class StubRoutines { + private static AddressField callStubReturnAddressField; + private static AddressField callStubCompiledReturnAddressField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("StubRoutines"); + + callStubReturnAddressField = type.getAddressField("_call_stub_return_address"); + // Only some platforms have specif return from compiled to call_stub + try { + callStubCompiledReturnAddressField = type.getAddressField("_call_stub_compiled_return"); + } catch (RuntimeException re) { + callStubCompiledReturnAddressField = null; + } + } + + public StubRoutines() { + } + + public boolean returnsToCallStub(Address returnPC) { + Address addr = callStubReturnAddressField.getValue(); + boolean result = false; + if (addr == null) { + result = (addr == returnPC); + } else { + result = addr.equals(returnPC); + } + if (result || callStubCompiledReturnAddressField == null ) return result; + // Could be a return to compiled code return point + addr = callStubCompiledReturnAddressField.getValue(); + if (addr == null) { + return (addr == returnPC); + } else { + return (addr.equals(returnPC)); + } + + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Thread.java new file mode 100644 index 00000000000..2b28e9fb996 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Thread.java @@ -0,0 +1,160 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +public class Thread extends VMObject { + private static long tlabFieldOffset; + + private static CIntegerField suspendFlagsField; + // Thread::SuspendFlags enum constants + private static int EXTERNAL_SUSPEND; + private static int EXT_SUSPENDED; + private static int HAS_ASYNC_EXCEPTION; + + private static AddressField activeHandlesField; + private static AddressField highestLockField; + private static AddressField currentPendingMonitorField; + private static AddressField currentWaitingMonitorField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("Thread"); + + suspendFlagsField = type.getCIntegerField("_suspend_flags"); + EXTERNAL_SUSPEND = db.lookupIntConstant("Thread::_external_suspend").intValue(); + EXT_SUSPENDED = db.lookupIntConstant("Thread::_ext_suspended").intValue(); + HAS_ASYNC_EXCEPTION = db.lookupIntConstant("Thread::_has_async_exception").intValue(); + + tlabFieldOffset = type.getField("_tlab").getOffset(); + activeHandlesField = type.getAddressField("_active_handles"); + highestLockField = type.getAddressField("_highest_lock"); + currentPendingMonitorField = type.getAddressField("_current_pending_monitor"); + currentWaitingMonitorField = type.getAddressField("_current_waiting_monitor"); + } + + public Thread(Address addr) { + super(addr); + } + + public int suspendFlags() { + return (int) suspendFlagsField.getValue(addr); + } + + public boolean isExternalSuspend() { + return (suspendFlags() & EXTERNAL_SUSPEND) != 0; + } + + public boolean isExtSuspended() { + return (suspendFlags() & EXT_SUSPENDED) != 0; + } + + public boolean isBeingExtSuspended() { + return isExtSuspended() || isExternalSuspend(); + } + + // historical usage: checked for VM or external suspension + public boolean isAnySuspended() { + return isExtSuspended(); + } + + public boolean hasAsyncException() { + return (suspendFlags() & HAS_ASYNC_EXCEPTION) != 0; + } + + public ThreadLocalAllocBuffer tlab() { + return new ThreadLocalAllocBuffer(addr.addOffsetTo(tlabFieldOffset)); + } + + public JNIHandleBlock activeHandles() { + Address a = activeHandlesField.getAddress(addr); + if (a == null) { + return null; + } + return new JNIHandleBlock(a); + } + + public boolean isVMThread() { return false; } + public boolean isJavaThread() { return false; } + public boolean isCompilerThread() { return false; } + public boolean isHiddenFromExternalView() { return false; } + public boolean isJvmtiAgentThread() { return false; } + public boolean isWatcherThread() { return false; } + public boolean isConcurrentMarkSweepThread() { return false; } + public boolean isLowMemoryDetectorThread() { return false; } + + /** Memory operations */ + public void oopsDo(AddressVisitor oopVisitor) { + // FIXME: Empty for now; will later traverse JNI handles and + // pending exception + } + + public Address highestLock() { + return highestLockField.getValue(addr); + } + + public ObjectMonitor getCurrentPendingMonitor() { + Address monitorAddr = currentPendingMonitorField.getValue(addr); + if (monitorAddr == null) { + return null; + } + return new ObjectMonitor(monitorAddr); + } + + public ObjectMonitor getCurrentWaitingMonitor() { + Address monitorAddr = currentWaitingMonitorField.getValue(addr); + if (monitorAddr == null) { + return null; + } + return new ObjectMonitor(monitorAddr); + } + + public boolean isLockOwned(Address lock) { + if (isInStack(lock)) return true; + return false; + } + + public boolean isInStack(Address a) { + // In the Serviceability Agent we need access to the thread's + // stack pointer to be able to answer this question. Since it is + // only a debugging system at the moment we need access to the + // underlying thread, which is only present for Java threads; see + // JavaThread.java. + return false; + } + + /** Assistance for ObjectMonitor implementation */ + Address threadObjectAddress() { return addr; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java new file mode 100644 index 00000000000..e4dd4b9e445 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/**

ThreadLocalAllocBuffer: a descriptor for thread-local storage + used by the threads for allocation.

+ +

It is thread-private at any time, but maybe multiplexed over + time across multiple threads.

*/ + +public class ThreadLocalAllocBuffer extends VMObject { + private static AddressField startField; + private static AddressField topField; + private static AddressField endField; + private static CIntegerField desired_sizeField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("ThreadLocalAllocBuffer"); + + startField = type.getAddressField("_start"); + topField = type.getAddressField("_top"); + endField = type.getAddressField("_end"); + desired_sizeField = type.getCIntegerField("_desired_size"); + } + + public ThreadLocalAllocBuffer(Address addr) { + super(addr); + } + + public Address start() { return startField.getValue(addr); } + public Address end() { return endField.getValue(addr); } + public Address top() { return topField.getValue(addr); } + + /** Support for iteration over heap -- not sure how this will + interact with GC in reflective system, but necessary for the + debugging mechanism */ + public OopHandle startAsOopHandle() { + return startField.getOopHandle(addr); + } + + /** Support for iteration over heap -- not sure how this will + interact with GC in reflective system, but necessary for the + debugging mechanism */ + public OopHandle nextOopHandle(OopHandle handle, long size) { + return handle.addOffsetToAsOopHandle(size); + } + + // public int size(); // FIXME: must adjust this to be in bytes + // public int availableSize(); // FIXME: must adjust this to be in bytes + public void print() { + printOn(System.out); + } + + public boolean contains(Address p) { + if (top() == null) { + return false; + } + return (start().lessThanOrEqual(p) && top().greaterThan(p)); + } + + public void printOn(PrintStream tty) { + tty.println(" [" + start() + "," + + top() + "," + end() + ")"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java new file mode 100644 index 00000000000..1f7c9a1affb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java @@ -0,0 +1,216 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.solaris_sparc.SolarisSPARCJavaThreadPDAccess; +import sun.jvm.hotspot.runtime.solaris_x86.SolarisX86JavaThreadPDAccess; +import sun.jvm.hotspot.runtime.solaris_amd64.SolarisAMD64JavaThreadPDAccess; +import sun.jvm.hotspot.runtime.win32_amd64.Win32AMD64JavaThreadPDAccess; +import sun.jvm.hotspot.runtime.win32_x86.Win32X86JavaThreadPDAccess; +import sun.jvm.hotspot.runtime.win32_ia64.Win32IA64JavaThreadPDAccess; +import sun.jvm.hotspot.runtime.linux_x86.LinuxX86JavaThreadPDAccess; +import sun.jvm.hotspot.runtime.linux_ia64.LinuxIA64JavaThreadPDAccess; +import sun.jvm.hotspot.runtime.linux_amd64.LinuxAMD64JavaThreadPDAccess; +import sun.jvm.hotspot.runtime.linux_sparc.LinuxSPARCJavaThreadPDAccess; +import sun.jvm.hotspot.utilities.*; + +public class Threads { + private static JavaThreadFactory threadFactory; + private static AddressField threadListField; + private static VirtualConstructor virtualConstructor; + private static JavaThreadPDAccess access; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("Threads"); + + threadListField = type.getAddressField("_thread_list"); + + // Instantiate appropriate platform-specific JavaThreadFactory + String os = VM.getVM().getOS(); + String cpu = VM.getVM().getCPU(); + + access = null; + // FIXME: find the platform specific PD class by reflection? + if (os.equals("solaris")) { + if (cpu.equals("sparc")) { + access = new SolarisSPARCJavaThreadPDAccess(); + } else if (cpu.equals("x86")) { + access = new SolarisX86JavaThreadPDAccess(); + } else if (cpu.equals("amd64")) { + access = new SolarisAMD64JavaThreadPDAccess(); + } + } else if (os.equals("win32")) { + if (cpu.equals("x86")) { + access = new Win32X86JavaThreadPDAccess(); + } else if (cpu.equals("amd64")) { + access = new Win32AMD64JavaThreadPDAccess(); + } else if (cpu.equals("ia64")) { + access = new Win32IA64JavaThreadPDAccess(); + } + } else if (os.equals("linux")) { + if (cpu.equals("x86")) { + access = new LinuxX86JavaThreadPDAccess(); + } else if (cpu.equals("ia64")) { + access = new LinuxIA64JavaThreadPDAccess(); + } else if (cpu.equals("amd64")) { + access = new LinuxAMD64JavaThreadPDAccess(); + } else if (cpu.equals("sparc")) { + access = new LinuxSPARCJavaThreadPDAccess(); + } + + } + + if (access == null) { + throw new RuntimeException("OS/CPU combination " + os + "/" + cpu + + " not yet supported"); + } + + virtualConstructor = new VirtualConstructor(db); + // Add mappings for all known thread types + virtualConstructor.addMapping("JavaThread", JavaThread.class); + if (!VM.getVM().isCore()) { + virtualConstructor.addMapping("CompilerThread", CompilerThread.class); + } + // for now, use JavaThread itself. fix it later with appropriate class if needed + virtualConstructor.addMapping("SurrogateLockerThread", JavaThread.class); + virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class); + virtualConstructor.addMapping("LowMemoryDetectorThread", LowMemoryDetectorThread.class); + } + + public Threads() { + } + + /** NOTE: this returns objects of type JavaThread, CompilerThread, + JvmtiAgentThread, and LowMemoryDetectorThread. + The latter four are subclasses of the former. Most operations + (fetching the top frame, etc.) are only allowed to be performed on + a "pure" JavaThread. For this reason, {@link + sun.jvm.hotspot.runtime.JavaThread#isJavaThread} has been + changed from the definition in the VM (which returns true for + all of these thread types) to return true for JavaThreads and + false for the three subclasses. FIXME: should reconsider the + inheritance hierarchy; see {@link + sun.jvm.hotspot.runtime.JavaThread#isJavaThread}. */ + public JavaThread first() { + Address threadAddr = threadListField.getValue(); + if (threadAddr == null) { + return null; + } + + return createJavaThreadWrapper(threadAddr); + } + + /** Routine for instantiating appropriately-typed wrapper for a + JavaThread. Currently needs to be public for OopUtilities to + access it. */ + public JavaThread createJavaThreadWrapper(Address threadAddr) { + try { + JavaThread thread = (JavaThread)virtualConstructor.instantiateWrapperFor(threadAddr); + thread.setThreadPDAccess(access); + return thread; + } catch (Exception e) { + throw new RuntimeException("Unable to deduce type of thread from address " + threadAddr + + " (expected type JavaThread, CompilerThread, LowMemoryDetectorThread, JvmtiAgentThread, or SurrogateLockerThread)", e); + } + } + + /** Memory operations */ + public void oopsDo(AddressVisitor oopVisitor) { + // FIXME: add more of VM functionality + for (JavaThread thread = first(); thread != null; thread = thread.next()) { + thread.oopsDo(oopVisitor); + } + } + + // refer to Threads::owning_thread_from_monitor_owner + public JavaThread owningThreadFromMonitor(Address o) { + if (o == null) return null; + for (JavaThread thread = first(); thread != null; thread = thread.next()) { + if (o.equals(thread.threadObjectAddress())) { + return thread; + } + } + + long leastDiff = 0; + boolean leastDiffInitialized = false; + JavaThread theOwner = null; + for (JavaThread thread = first(); thread != null; thread = thread.next()) { + Address addr = thread.highestLock(); + if (addr == null || addr.lessThan(o)) continue; + long diff = addr.minus(o); + if (!leastDiffInitialized || diff < leastDiff) { + leastDiffInitialized = true; + leastDiff = diff; + theOwner = thread; + } + } + return theOwner; + } + + public JavaThread owningThreadFromMonitor(ObjectMonitor monitor) { + return owningThreadFromMonitor(monitor.owner()); + } + + // refer to Threads::get_pending_threads + // Get list of Java threads that are waiting to enter the specified monitor. + public List getPendingThreads(ObjectMonitor monitor) { + List pendingThreads = new ArrayList(); + for (JavaThread thread = first(); thread != null; thread = thread.next()) { + if (thread.isCompilerThread()) { + continue; + } + ObjectMonitor pending = thread.getCurrentPendingMonitor(); + if (monitor.equals(pending)) { + pendingThreads.add(thread); + } + } + return pendingThreads; + } + + // Get list of Java threads that have called Object.wait on the specified monitor. + public List getWaitingThreads(ObjectMonitor monitor) { + List pendingThreads = new ArrayList(); + for (JavaThread thread = first(); thread != null; thread = thread.next()) { + ObjectMonitor waiting = thread.getCurrentWaitingMonitor(); + if (monitor.equals(waiting)) { + pendingThreads.add(thread); + } + } + return pendingThreads; + } + + // FIXME: add other accessors +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VFrame.java new file mode 100644 index 00000000000..c1c427aadd2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VFrame.java @@ -0,0 +1,206 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.utilities.*; + +public class VFrame { + protected Frame fr; + protected RegisterMap regMap; + protected JavaThread thread; + + protected VFrame(Frame f, RegisterMap regMap, JavaThread thread) { + this.regMap = (RegisterMap) regMap.clone(); + + if (f != null) { + // the frame is null if we create a deoptimizedVFrame from a vframeArray + fr = (Frame) f.clone(); + } + + this.thread = thread; + } + + /** Factory method for creating vframes. The "unsafe" flag turns off + an assertion which the runtime system uses to ensure integrity, + but which must not be applied in the debugging situation. The + "mayBeImprecise" flag should be set to true for the case of the + top frame in the debugging system (obtained via + JavaThread.getCurrentFrameGuess()). */ + public static VFrame newVFrame(Frame f, RegisterMap regMap, JavaThread thread, boolean unsafe, boolean mayBeImprecise) { + if (f.isInterpretedFrame()) { + return new InterpretedVFrame(f, regMap, thread); + } + + if (!VM.getVM().isCore()) { + CodeBlob cb; + if (unsafe) { + cb = VM.getVM().getCodeCache().findBlobUnsafe(f.getPC()); + } else { + cb = VM.getVM().getCodeCache().findBlob(f.getPC()); + } + + if (cb != null) { + if (cb.isNMethod()) { + NMethod nm = (NMethod) cb; + // Compiled method (native stub or Java code) + ScopeDesc scope = null; + // FIXME: should revisit the check of isDebugging(); should not be necessary + if (mayBeImprecise || VM.getVM().isDebugging()) { + scope = nm.getScopeDescNearDbg(f.getPC()); + } else { + scope = nm.getScopeDescAt(f.getPC()); + } + return new CompiledVFrame(f, regMap, thread, scope, mayBeImprecise); + } + + if (f.isGlueFrame()) { + // This is a conversion frame. Skip this frame and try again. + RegisterMap tempMap = regMap.copy(); + Frame s = f.sender(tempMap); + return newVFrame(s, tempMap, thread, unsafe, false); + } + } + } + + // External frame + return new ExternalVFrame(f, regMap, thread, mayBeImprecise); + } + + /** Factory method for creating vframes. This is equivalent to + calling the above version with the "unsafe" and "imprecise" + flags set to false. */ + public static VFrame newVFrame(Frame f, RegisterMap regMap, JavaThread thread) { + return newVFrame(f, regMap, thread, false, false); + } + + /** Accessors */ + public Frame getFrame() { return fr; } + public RegisterMap getRegisterMap() { return regMap; } + public JavaThread getThread() { return thread; } + + /** Returns the sender vframe */ + public VFrame sender() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isTop(), "just checking"); + } + return sender(false); + } + + /** Returns the sender vframe; takes argument for debugging situation */ + public VFrame sender(boolean mayBeImprecise) { + RegisterMap tempMap = (RegisterMap) getRegisterMap().clone(); + if (fr.isFirstFrame()) { + return null; + } + Frame s = fr.realSender(tempMap); + // ia64 in 1.4.1 only has java frames and no entryFrame + // so "s" can be null here for the first frame. + if (s == null) { + Assert.that(VM.getVM().getCPU().equals("ia64"), "Only ia64 should have null here"); + return null; + } + if (s.isFirstFrame()) { + return null; + } + return VFrame.newVFrame(s, tempMap, getThread(), VM.getVM().isDebugging(), mayBeImprecise); + } + + /** Returns the next javaVFrame on the stack (skipping all other + kinds of frames). In the debugging situation, allows the + "imprecise" flag to propagate up the stack. We must not assert + that a ScopeDesc exists for the topmost compiled frame on the + stack. */ + public JavaVFrame javaSender() { + boolean imprecise = false; + + // Hack for debugging + if (VM.getVM().isDebugging()) { + if (!isJavaFrame()) { + imprecise = mayBeImpreciseDbg(); + } + } + VFrame f = sender(imprecise); + while (f != null) { + if (f.isJavaFrame()) { + return (JavaVFrame) f; + } + f = f.sender(imprecise); + } + return null; + } + + /** Answers if the this is the top vframe in the frame, i.e., if the + sender vframe is in the caller frame */ + public boolean isTop() { + return true; + } + + /** Returns top vframe within same frame (see isTop()) */ + public VFrame top() { + VFrame vf = this; + while (!vf.isTop()) { + vf = vf.sender(); + } + return vf; + } + + /** Type testing operations */ + public boolean isEntryFrame() { return false; } + public boolean isJavaFrame() { return false; } + public boolean isInterpretedFrame() { return false; } + public boolean isCompiledFrame() { return false; } + public boolean isDeoptimized() { return false; } + + /** An indication of whether this VFrame is "precise" or a best + guess. This is used in the debugging system to handle the top + frame on the stack, which, since the system will in general not + be at a safepoint, has to make some guesses about exactly where + in the execution it is. Any debugger should indicate to the user + that the information for this frame may not be 100% correct. + FIXME: may need to move this up into VFrame instead of keeping + it in CompiledVFrame. */ + public boolean mayBeImpreciseDbg() { return false; } + + /** Printing operations */ + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + if (VM.getVM().wizardMode()) { + fr.printValueOn(tty); + } + } + + public void printValue() { + printValueOn(System.out); + } + + public void printValueOn(PrintStream tty) { + printOn(tty); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java new file mode 100644 index 00000000000..9ce98f2de9c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java @@ -0,0 +1,795 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.regex.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.c1.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/**

This class encapsulates the global state of the VM; the + universe, object heap, interpreter, etc. It is a Singleton and + must be initialized with a call to initialize() before calling + getVM().

+ +

Many auxiliary classes (i.e., most of the VMObjects) keep + needed field offsets in the form of static Field objects. In a + debugging system, the VM might be shutdown and re-initialized (on + a differently-configured build, i.e., 32- vs. 64-bit), and all old + cached state (including fields and field offsets) must be + flushed.

+ +

An Observer pattern is used to implement the initialization of + such classes. Each such class, in its static initializer, + registers an Observer with the VM class via + VM.registerVMInitializedObserver(). This Observer is guaranteed to + be notified whenever the VM is initialized (or re-initialized). To + implement the first-time initialization, the observer is also + notified when it registers itself with the VM. (For bootstrapping + reasons, this implies that the constructor of VM can not + instantiate any such objects, since VM.soleInstance will not have + been set yet. This is a bootstrapping issue which may have to be + revisited later.)

+*/ + +public class VM { + private static VM soleInstance; + private static List vmInitializedObservers = new ArrayList(); + private List vmResumedObservers = new ArrayList(); + private List vmSuspendedObservers = new ArrayList(); + private TypeDataBase db; + private boolean isBigEndian; + /** This is only present if in a debugging system */ + private JVMDebugger debugger; + private long stackBias; + private long logAddressSize; + private Universe universe; + private ObjectHeap heap; + private SymbolTable symbols; + private StringTable strings; + private SystemDictionary dict; + private Threads threads; + private ObjectSynchronizer synchronizer; + private JNIHandles handles; + private Interpreter interpreter; + private StubRoutines stubRoutines; + private Bytes bytes; + /** Flags indicating whether we are attached to a core, C1, or C2 build */ + private boolean usingClientCompiler; + private boolean usingServerCompiler; + /** Flag indicating whether UseTLAB is turned on */ + private boolean useTLAB; + /** alignment constants */ + private boolean isLP64; + private int bytesPerLong; + private int minObjAlignmentInBytes; + /** This is only present in a non-core build */ + private CodeCache codeCache; + /** This is only present in a C1 build */ + private Runtime1 runtime1; + /** These constants come from globalDefinitions.hpp */ + private int invocationEntryBCI; + private int invalidOSREntryBCI; + private ReversePtrs revPtrs; + private VMRegImpl vmregImpl; + + // System.getProperties from debuggee VM + private Properties sysProps; + + // VM version strings come from Abstract_VM_Version class + private String vmRelease; + private String vmInternalInfo; + + private Flag[] commandLineFlags; + private Map flagsMap; + + private static Type intxType; + private static Type uintxType; + private static CIntegerType boolType; + private Boolean sharingEnabled; + + // command line flags supplied to VM - see struct Flag in globals.hpp + public static final class Flag { + private String type; + private String name; + private Address addr; + private String kind; + + private Flag(String type, String name, Address addr, String kind) { + this.type = type; + this.name = name; + this.addr = addr; + this.kind = kind; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public Address getAddress() { + return addr; + } + + public String getKind() { + return kind; + } + + public boolean isBool() { + return type.equals("bool"); + } + + public boolean getBool() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isBool(), "not a bool flag!"); + } + return addr.getCIntegerAt(0, boolType.getSize(), boolType.isUnsigned()) + != 0; + } + + public boolean isIntx() { + return type.equals("intx"); + } + + public long getIntx() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isIntx(), "not a intx flag!"); + } + return addr.getCIntegerAt(0, intxType.getSize(), false); + } + + public boolean isUIntx() { + return type.equals("uintx"); + } + + public long getUIntx() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isUIntx(), "not a uintx flag!"); + } + return addr.getCIntegerAt(0, uintxType.getSize(), true); + } + + public String getValue() { + if (isBool()) { + return new Boolean(getBool()).toString(); + } else if (isIntx()) { + return new Long(getIntx()).toString(); + } else if (isUIntx()) { + return new Long(getUIntx()).toString(); + } else { + return null; + } + } + }; + + private static void checkVMVersion(String vmRelease) { + if (System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null) { + // read sa build version. + String versionProp = "sun.jvm.hotspot.runtime.VM.saBuildVersion"; + String saVersion = saProps.getProperty(versionProp); + if (saVersion == null) + throw new RuntimeException("Missing property " + versionProp); + + // Strip nonproduct VM version substring (note: saVersion doesn't have it). + String vmVersion = vmRelease.replaceAll("(-fastdebug)|(-debug)|(-jvmg)|(-optimized)|(-profiled)",""); + + if (saVersion.equals(vmVersion)) { + // Exact match + return; + } + if (saVersion.indexOf('-') == saVersion.lastIndexOf('-') && + vmVersion.indexOf('-') == vmVersion.lastIndexOf('-')) { + // Throw exception if different release versions: + // .-b + throw new VMVersionMismatchException(saVersion, vmRelease); + } else { + // Otherwise print warning to allow mismatch not release versions + // during development. + System.err.println("WARNING: Hotspot VM version " + vmRelease + + " does not match with SA version " + saVersion + + "." + " You may see unexpected results. "); + } + } else { + System.err.println("WARNING: You have disabled SA and VM version check. You may be " + + "using incompatible version of SA and you may see unexpected " + + "results."); + } + } + + private static final boolean disableDerivedPrinterTableCheck; + private static final Properties saProps; + + static { + saProps = new Properties(); + URL url = null; + try { + url = VM.class.getClassLoader().getResource("sa.properties"); + saProps.load(new BufferedInputStream(url.openStream())); + } catch (Exception e) { + throw new RuntimeException("Unable to load properties " + + (url == null ? "null" : url.toString()) + + ": " + e.getMessage()); + } + + disableDerivedPrinterTableCheck = System.getProperty("sun.jvm.hotspot.runtime.VM.disableDerivedPointerTableCheck") != null; + } + + private VM(TypeDataBase db, JVMDebugger debugger, boolean isBigEndian) { + this.db = db; + this.debugger = debugger; + this.isBigEndian = isBigEndian; + + // Note that we don't construct universe, heap, threads, + // interpreter, or stubRoutines here (any more). The current + // initialization mechanisms require that the VM be completely set + // up (i.e., out of its constructor, with soleInstance assigned) + // before their static initializers are run. + + if (db.getAddressSize() == 4) { + logAddressSize = 2; + } else if (db.getAddressSize() == 8) { + logAddressSize = 3; + } else { + throw new RuntimeException("Address size " + db.getAddressSize() + " not yet supported"); + } + + // read VM version info + try { + Type vmVersion = db.lookupType("Abstract_VM_Version"); + Address releaseAddr = vmVersion.getAddressField("_s_vm_release").getValue(); + vmRelease = CStringUtilities.getString(releaseAddr); + Address vmInternalInfoAddr = vmVersion.getAddressField("_s_internal_vm_info_string").getValue(); + vmInternalInfo = CStringUtilities.getString(vmInternalInfoAddr); + } catch (Exception exp) { + throw new RuntimeException("can't determine target's VM version : " + exp.getMessage()); + } + + checkVMVersion(vmRelease); + + stackBias = db.lookupIntConstant("STACK_BIAS").intValue(); + invocationEntryBCI = db.lookupIntConstant("InvocationEntryBci").intValue(); + invalidOSREntryBCI = db.lookupIntConstant("InvalidOSREntryBci").intValue(); + + // We infer the presence of C1 or C2 from a couple of fields we + // already have present in the type database + { + Type type = db.lookupType("methodOopDesc"); + if (type.getField("_from_compiled_entry", false, false) == null) { + // Neither C1 nor C2 is present + usingClientCompiler = false; + usingServerCompiler = false; + } else { + // Determine whether C2 is present + if (type.getField("_interpreter_invocation_count", false, false) != null) { + usingServerCompiler = true; + } else { + usingClientCompiler = true; + } + } + } + + useTLAB = (db.lookupIntConstant("UseTLAB").intValue() != 0); + + if (debugger != null) { + isLP64 = debugger.getMachineDescription().isLP64(); + } + bytesPerLong = db.lookupIntConstant("BytesPerLong").intValue(); + minObjAlignmentInBytes = db.lookupIntConstant("MinObjAlignmentInBytes").intValue(); + + intxType = db.lookupType("intx"); + uintxType = db.lookupType("uintx"); + boolType = (CIntegerType) db.lookupType("bool"); + } + + /** This could be used by a reflective runtime system */ + public static void initialize(TypeDataBase db, boolean isBigEndian) { + if (soleInstance != null) { + throw new RuntimeException("Attempt to initialize VM twice"); + } + soleInstance = new VM(db, null, isBigEndian); + for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) { + ((Observer) iter.next()).update(null, null); + } + } + + /** This is used by the debugging system */ + public static void initialize(TypeDataBase db, JVMDebugger debugger) { + if (soleInstance != null) { + throw new RuntimeException("Attempt to initialize VM twice"); + } + soleInstance = new VM(db, debugger, debugger.getMachineDescription().isBigEndian()); + for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) { + ((Observer) iter.next()).update(null, null); + } + } + + /** This is used by the debugging system */ + public static void shutdown() { + soleInstance = null; + } + + /** This is used by both the debugger and any runtime system. It is + the basic mechanism by which classes which mimic underlying VM + functionality cause themselves to be initialized. The given + observer will be notified (with arguments (null, null)) when the + VM is re-initialized, as well as when it registers itself with + the VM. */ + public static void registerVMInitializedObserver(Observer o) { + vmInitializedObservers.add(o); + o.update(null, null); + } + + /** This is the primary accessor used by both the debugger and any + potential runtime system */ + public static VM getVM() { + if (soleInstance == null) { + throw new RuntimeException("VM.initialize() was not yet called"); + } + return soleInstance; + } + + /** This is only used by the debugging system. The given observer + will be notified if the underlying VM resumes execution. NOTE + that the given observer is not triggered if the VM is currently + running and therefore differs in behavior from {@link + #registerVMInitializedObserver} (because of the possibility of + race conditions if the observer is added while the VM is being + suspended or resumed). */ + public void registerVMResumedObserver(Observer o) { + vmResumedObservers.add(o); + } + + /** This is only used by the debugging system. The given observer + will be notified if the underlying VM suspends execution. NOTE + that the given observer is not triggered if the VM is currently + suspended and therefore differs in behavior from {@link + #registerVMInitializedObserver} (because of the possibility of + race conditions if the observer is added while the VM is being + suspended or resumed). */ + public void registerVMSuspendedObserver(Observer o) { + vmSuspendedObservers.add(o); + } + + /** This is only used by the debugging system. Informs all + registered resumption observers that the VM has been resumed. + The application is responsible for actually having performed the + resumption. No OopHandles must be used after this point, as they + may move in the target address space due to garbage + collection. */ + public void fireVMResumed() { + for (Iterator iter = vmResumedObservers.iterator(); iter.hasNext(); ) { + ((Observer) iter.next()).update(null, null); + } + } + + /** This is only used by the debugging system. Informs all + registered suspension observers that the VM has been suspended. + The application is responsible for actually having performed the + suspension. Garbage collection must be forbidden at this point; + for example, a JPDA-level suspension is not adequate since the + VM thread may still be running. */ + public void fireVMSuspended() { + for (Iterator iter = vmSuspendedObservers.iterator(); iter.hasNext(); ) { + ((Observer) iter.next()).update(null, null); + } + } + + /** Returns the OS this VM is running on. Notice that by delegating + to the debugger we can transparently support remote + debugging. */ + public String getOS() { + if (debugger != null) { + return debugger.getOS(); + } + return PlatformInfo.getOS(); + } + + /** Returns the CPU this VM is running on. Notice that by delegating + to the debugger we can transparently support remote + debugging. */ + public String getCPU() { + if (debugger != null) { + return debugger.getCPU(); + } + return PlatformInfo.getCPU(); + } + + public Type lookupType(String cTypeName) { + return db.lookupType(cTypeName); + } + + public Integer lookupIntConstant(String name) { + return db.lookupIntConstant(name); + } + + public long getAddressSize() { + return db.getAddressSize(); + } + + public long getOopSize() { + return db.getOopSize(); + } + + public long getLogAddressSize() { + return logAddressSize; + } + + /** NOTE: this offset is in BYTES in this system! */ + public long getStackBias() { + return stackBias; + } + + /** Indicates whether the underlying machine supports the LP64 data + model. This is needed for conditionalizing code in a few places */ + public boolean isLP64() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isDebugging(), "Debugging system only for now"); + } + return isLP64; + } + + /** Get bytes-per-long == long/double natural alignment. */ + public int getBytesPerLong() { + return bytesPerLong; + } + + /** Get minimum object alignment in bytes. */ + public int getMinObjAlignmentInBytes() { + return minObjAlignmentInBytes; + } + + /** Utility routine for getting data structure alignment correct */ + public long alignUp(long size, long alignment) { + return (size + alignment - 1) & ~(alignment - 1); + } + + /** Utility routine for getting data structure alignment correct */ + public long alignDown(long size, long alignment) { + return size & ~(alignment - 1); + } + + /** Utility routine for building an int from two "unsigned" 16-bit + shorts */ + public int buildIntFromShorts(short low, short high) { + return (((int) high) << 16) | (((int) low) & 0xFFFF); + } + + /** Utility routine for building a long from two "unsigned" 32-bit + ints in platform-dependent order */ + public long buildLongFromIntsPD(int oneHalf, int otherHalf) { + if (isBigEndian) { + return (((long) otherHalf) << 32) | (((long) oneHalf) & 0x00000000FFFFFFFFL); + } else{ + return (((long) oneHalf) << 32) | (((long) otherHalf) & 0x00000000FFFFFFFFL); + } + } + + /** Indicates whether Thread-Local Allocation Buffers are used */ + public boolean getUseTLAB() { + return useTLAB; + } + + public TypeDataBase getTypeDataBase() { + return db; + } + + public Universe getUniverse() { + if (universe == null) { + universe = new Universe(); + } + return universe; + } + + public ObjectHeap getObjectHeap() { + if (heap == null) { + heap = new ObjectHeap(db); + } + return heap; + } + + public SymbolTable getSymbolTable() { + if (symbols == null) { + symbols = SymbolTable.getTheTable(); + } + return symbols; + } + + public StringTable getStringTable() { + if (strings == null) { + strings = StringTable.getTheTable(); + } + return strings; + } + + public SystemDictionary getSystemDictionary() { + if (dict == null) { + dict = new SystemDictionary(); + } + return dict; + } + + public Threads getThreads() { + if (threads == null) { + threads = new Threads(); + } + return threads; + } + + public ObjectSynchronizer getObjectSynchronizer() { + if (synchronizer == null) { + synchronizer = new ObjectSynchronizer(); + } + return synchronizer; + } + + public JNIHandles getJNIHandles() { + if (handles == null) { + handles = new JNIHandles(); + } + return handles; + } + + public Interpreter getInterpreter() { + if (interpreter == null) { + interpreter = new Interpreter(); + } + return interpreter; + } + + public StubRoutines getStubRoutines() { + if (stubRoutines == null) { + stubRoutines = new StubRoutines(); + } + return stubRoutines; + } + + public VMRegImpl getVMRegImplInfo() { + if (vmregImpl == null) { + vmregImpl = new VMRegImpl(); + } + return vmregImpl; + } + + public Bytes getBytes() { + if (bytes == null) { + bytes = new Bytes(debugger.getMachineDescription()); + } + return bytes; + } + + /** Returns true if this is a "core" build, false if either C1 or C2 + is present */ + public boolean isCore() { + return (!(usingClientCompiler || usingServerCompiler)); + } + + /** Returns true if this is a C1 build, false otherwise */ + public boolean isClientCompiler() { + return usingClientCompiler; + } + + /** Returns true if this is a C2 build, false otherwise */ + public boolean isServerCompiler() { + return usingServerCompiler; + } + + /** Returns true if C2 derived pointer table should be used, false otherwise */ + public boolean useDerivedPointerTable() { + return !disableDerivedPrinterTableCheck; + } + + /** Returns the code cache; should not be used if is core build */ + public CodeCache getCodeCache() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!isCore(), "noncore builds only"); + } + if (codeCache == null) { + codeCache = new CodeCache(); + } + return codeCache; + } + + /** Should only be called for C1 builds */ + public Runtime1 getRuntime1() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isClientCompiler(), "C1 builds only"); + } + if (runtime1 == null) { + runtime1 = new Runtime1(); + } + return runtime1; + } + + /** Test to see whether we're in debugging mode (NOTE: this really + should not be tested by this code; currently only used in + StackFrameStream) */ + public boolean isDebugging() { + return (debugger != null); + } + + /** This is only used by the debugging (i.e., non-runtime) system */ + public JVMDebugger getDebugger() { + if (debugger == null) { + throw new RuntimeException("Attempt to use debugger in runtime system"); + } + return debugger; + } + + /** Indicates whether a given program counter is in Java code. This + includes but is not spanned by the interpreter and code cache. + Only used in the debugging system, for implementing + JavaThread.currentFrameGuess() on x86. */ + public boolean isJavaPCDbg(Address addr) { + // FIXME: this is not a complete enough set: must include areas + // like vtable stubs + return (getInterpreter().contains(addr) || + getCodeCache().contains(addr)); + } + + /** FIXME: figure out where to stick this */ + public int getInvocationEntryBCI() { + return invocationEntryBCI; + } + + /** FIXME: figure out where to stick this */ + public int getInvalidOSREntryBCI() { + return invalidOSREntryBCI; + } + + // FIXME: figure out where to stick this + public boolean wizardMode() { + return true; + } + + public ReversePtrs getRevPtrs() { + return revPtrs; + } + + public void setRevPtrs(ReversePtrs rp) { + revPtrs = rp; + } + + // returns null, if not available. + public String getVMRelease() { + return vmRelease; + } + + // returns null, if not available. + public String getVMInternalInfo() { + return vmInternalInfo; + } + + public boolean isSharingEnabled() { + if (sharingEnabled == null) { + Flag flag = getCommandLineFlag("UseSharedSpaces"); + sharingEnabled = (flag == null)? Boolean.FALSE : + (flag.getBool()? Boolean.TRUE: Boolean.FALSE); + } + return sharingEnabled.booleanValue(); + } + + + // returns null, if not available. + public Flag[] getCommandLineFlags() { + if (commandLineFlags == null) { + readCommandLineFlags(); + } + + return commandLineFlags; + } + + public Flag getCommandLineFlag(String name) { + if (flagsMap == null) { + flagsMap = new HashMap(); + Flag[] flags = getCommandLineFlags(); + for (int i = 0; i < flags.length; i++) { + flagsMap.put(flags[i].getName(), flags[i]); + } + } + return (Flag) flagsMap.get(name); + } + + private void readCommandLineFlags() { + // get command line flags + TypeDataBase db = getTypeDataBase(); + try { + Type flagType = db.lookupType("Flag"); + int numFlags = (int) flagType.getCIntegerField("numFlags").getValue(); + // NOTE: last flag contains null values. + commandLineFlags = new Flag[numFlags - 1]; + + Address flagAddr = flagType.getAddressField("flags").getValue(); + + AddressField typeFld = flagType.getAddressField("type"); + AddressField nameFld = flagType.getAddressField("name"); + AddressField addrFld = flagType.getAddressField("addr"); + AddressField kindFld = flagType.getAddressField("kind"); + + long flagSize = flagType.getSize(); // sizeof(Flag) + + // NOTE: last flag contains null values. + for (int f = 0; f < numFlags - 1; f++) { + String type = CStringUtilities.getString(typeFld.getValue(flagAddr)); + String name = CStringUtilities.getString(nameFld.getValue(flagAddr)); + Address addr = addrFld.getValue(flagAddr); + String kind = CStringUtilities.getString(kindFld.getValue(flagAddr)); + commandLineFlags[f] = new Flag(type, name, addr, kind); + flagAddr = flagAddr.addOffsetTo(flagSize); + } + + // sort flags by name + Arrays.sort(commandLineFlags, new Comparator() { + public int compare(Object o1, Object o2) { + Flag f1 = (Flag) o1; + Flag f2 = (Flag) o2; + return f1.getName().compareTo(f2.getName()); + } + }); + } catch (Exception exp) { + // ignore. may be older version. command line flags not available. + } + } + + public String getSystemProperty(String key) { + Properties props = getSystemProperties(); + return (props != null)? props.getProperty(key) : null; + } + + public Properties getSystemProperties() { + if (sysProps == null) { + readSystemProperties(); + } + return sysProps; + } + + private void readSystemProperties() { + InstanceKlass systemKls = getSystemDictionary().getSystemKlass(); + systemKls.iterate(new DefaultOopVisitor() { + ObjectReader objReader = new ObjectReader(); + public void doOop(sun.jvm.hotspot.oops.OopField field, boolean isVMField) { + if (field.getID().getName().equals("props")) { + try { + sysProps = (Properties) objReader.readObject(field.getValue(getObj())); + } catch (Exception e) { + if (Assert.ASSERTS_ENABLED) { + e.printStackTrace(); + } + } + } + } + }, false); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMObject.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMObject.java new file mode 100644 index 00000000000..d4b9c9b6f60 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMObject.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** This is a base class for all VM runtime objects which wrap + Addresses. The rationale is that without this in place, every + class would have to implement equals() and hashCode() with + boilerplate code, a practice which is inherently error-prone. */ + +public class VMObject { + protected Address addr; + + /** All of the objects have this as their constructor's signature + anyway */ + public VMObject(Address addr) { + this.addr = addr; + } + + public String toString() { + return getClass().getName() + "@" + addr; + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!getClass().equals(arg.getClass())) { + return false; + } + + VMObject obj = (VMObject) arg; + if (!addr.equals(obj.addr)) { + return false; + } + + return true; + } + + public int hashCode() { + return addr.hashCode(); + } + + public Address getAddress() { + return addr; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMObjectFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMObjectFactory.java new file mode 100644 index 00000000000..f647cd398f5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMObjectFactory.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.lang.reflect.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/**

This class implements a factory mechanism for the objects + created to wrap Addresses. It requires that the class passed in + implement a constructor taking with the following signature:

+ +

+ public <Type>(sun.jvm.hotspot.Address) + +

+ +

It is used to write shorter code when wrapping Addresses since + null checks are no longer necessary. In addition, it is a central + location where a canonicalizing map could be implemented if one + were desired (though the current system is designed to not require + one.)

+*/ + +public class VMObjectFactory { + public static Object newObject(Class clazz, Address addr) + throws ConstructionException { + try { + if (addr == null) { + return null; + } + + Constructor c = clazz.getConstructor(new Class[] { + Address.class + }); + return c.newInstance(new Object[] { addr }); + } + catch (Exception e) { + throw new ConstructionException(e); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMReg.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMReg.java new file mode 100644 index 00000000000..75e5832b006 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMReg.java @@ -0,0 +1,87 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + + +/** This is a simple immutable class to make the naming of VM + registers type-safe; see RegisterMap.java and frame.hpp. */ + +public class VMReg { + private int value; + + // C2 only + public static Address matcherRegEncodeAddr; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + if (VM.getVM().isServerCompiler()) { + Type type = db.lookupType("Matcher"); + Field f = type.getField("_regEncode"); + matcherRegEncodeAddr = f.getStaticFieldAddress(); + } + } + + public VMReg(int i) { + value = i; + } + + public int getValue() { + return value; + } + + public int regEncode() { + if (matcherRegEncodeAddr != null) { + return (int) matcherRegEncodeAddr.getCIntegerAt(value, 1, true); + } + return value; + } + + public boolean equals(Object arg) { + if ((arg != null) || (!(arg instanceof VMReg))) { + return false; + } + + return ((VMReg) arg).value == value; + } + + public boolean lessThan(VMReg arg) { return value < arg.value; } + public boolean lessThanOrEqual(VMReg arg) { return value <= arg.value; } + public boolean greaterThan(VMReg arg) { return value > arg.value; } + public boolean greaterThanOrEqual(VMReg arg) { return value >= arg.value; } + + public int minus(VMReg arg) { return value - arg.value; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMVersionMismatchException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMVersionMismatchException.java new file mode 100644 index 00000000000..9dc0ccdbec8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VMVersionMismatchException.java @@ -0,0 +1,55 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +/** An instance of this exception is thrown when debuggee VM version + is not supported current version of SA. */ +public class VMVersionMismatchException extends RuntimeException { + public VMVersionMismatchException(String supported, String target) { + super(); + supportedVersions = supported; + targetVersion = target; + } + + public String getMessage() { + StringBuffer msg = new StringBuffer(); + msg.append("Supported versions are "); + msg.append(supportedVersions); + msg.append(". Target VM is "); + msg.append(targetVersion); + return msg.toString(); + } + + public String getSupportedVersions() { + return supportedVersions; + } + + public String getTargetVersion() { + return targetVersion; + } + + private String supportedVersions; + private String targetVersion; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VirtualConstructor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VirtualConstructor.java new file mode 100644 index 00000000000..beb96328a47 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VirtualConstructor.java @@ -0,0 +1,97 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.types.*; + +/** This class provides generalized "virtual constructor" + functionality for VMObjects. In simple terms, it creates + correctly-typed Java wrapper objects for underlying Addresses, + using the "RTTI-like" functionality of TypeDataBase. For example, + if the given Address really is a DefNewGeneration*, the Java object + created for it will be of type + sun.jvm.hotspot.memory.DefNewGeneration, assuming the mapping from + type "DefNewGeneration" to class + sun.jvm.hotspot.memory.DefNewGeneration has been set up. */ + +public class VirtualConstructor { + private TypeDataBase db; + private Map map; // Map + + public VirtualConstructor(TypeDataBase db) { + this.db = db; + map = new HashMap(); + } + + /** Adds a mapping from the given C++ type name to the given Java + class. The latter must be a subclass of + sun.jvm.hotspot.runtime.VMObject. Returns false if there was + already a class for this type name in the map. */ + public boolean addMapping(String cTypeName, Class clazz) { + if (map.get(cTypeName) != null) { + return false; + } + + map.put(cTypeName, clazz); + return true; + } + + /** Instantiate the most-precisely typed wrapper object available + for the type of the given Address. If no type in the mapping + matched the type of the Address, throws a WrongTypeException. + Returns null for a null address (similar behavior to + VMObjectFactory). */ + public VMObject instantiateWrapperFor(Address addr) throws WrongTypeException { + if (addr == null) { + return null; + } + + for (Iterator iter = map.keySet().iterator(); iter.hasNext(); ) { + String typeName = (String) iter.next(); + if (db.addressTypeIsEqualToType(addr, db.lookupType(typeName))) { + return (VMObject) VMObjectFactory.newObject((Class) map.get(typeName), addr); + } + } + + String message = "No suitable match for type of address " + addr; + CDebugger cdbg = VM.getVM().getDebugger().getCDebugger(); + if (cdbg != null) { + // Most common case: V-table pointer is the first field + Address vtblPtr = addr.getAddressAt(0); + LoadObject lo = cdbg.loadObjectContainingPC(vtblPtr); + if (lo != null) { + ClosestSymbol symbol = lo.closestSymbolToPC(vtblPtr); + if (symbol != null) { + message += " (nearest symbol is " + symbol.getName() + ")"; + } + } + } + + throw new WrongTypeException(message); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VirtualSpace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VirtualSpace.java new file mode 100644 index 00000000000..763fd6c2611 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VirtualSpace.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +public class VirtualSpace extends VMObject { + private static AddressField lowField; + private static AddressField highField; + private static AddressField lowBoundaryField; + private static AddressField highBoundaryField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("VirtualSpace"); + + lowField = type.getAddressField("_low"); + highField = type.getAddressField("_high"); + lowBoundaryField = type.getAddressField("_low_boundary"); + highBoundaryField = type.getAddressField("_high_boundary"); + } + + public VirtualSpace(Address addr) { + super(addr); + } + + public Address low() { return lowField.getValue(addr); } + public Address high() { return highField.getValue(addr); } + public Address lowBoundary() { return lowBoundaryField.getValue(addr); } + public Address highBoundary() { return highBoundaryField.getValue(addr); } + + /** Testers (all sizes are byte sizes) */ + public long committedSize() { return high().minus(low()); } + public long reservedSize() { return highBoundary().minus(lowBoundary()); } + public long uncommittedSize() { return reservedSize() - committedSize(); } + public boolean contains(Address addr) { return (low().lessThanOrEqual(addr) && addr.lessThan(high())); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/WatcherThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/WatcherThread.java new file mode 100644 index 00000000000..755418898cf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/WatcherThread.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** These will never show up in the threads list (from Threads.first()) */ + +public class WatcherThread extends Thread { + public WatcherThread(Address addr) { + super(addr); + } + + public boolean isWatcherThread() { return true; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64CurrentFrameGuess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64CurrentFrameGuess.java new file mode 100644 index 00000000000..d881cac9700 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64CurrentFrameGuess.java @@ -0,0 +1,215 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.amd64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.runtime.*; + +/**

Should be able to be used on all amd64 platforms we support + (Linux/amd64) to implement JavaThread's + "currentFrameGuess()" functionality. Input is an AMD64ThreadContext; + output is SP, FP, and PC for an AMD64Frame. Instantiation of the + AMD64Frame is left to the caller, since we may need to subclass + AMD64Frame to support signal handler frames on Unix platforms.

+ +

Algorithm is to walk up the stack within a given range (say, + 512K at most) looking for a plausible PC and SP for a Java frame, + also considering those coming in from the context. If we find a PC + that belongs to the VM (i.e., in generated code like the + interpreter or CodeCache) then we try to find an associated EBP. + We repeat this until we either find a complete frame or run out of + stack to look at.

*/ + +public class AMD64CurrentFrameGuess { + private AMD64ThreadContext context; + private JavaThread thread; + private Address spFound; + private Address fpFound; + private Address pcFound; + + private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.amd64.AMD64Frame.DEBUG") + != null; + + public AMD64CurrentFrameGuess(AMD64ThreadContext context, + JavaThread thread) { + this.context = context; + this.thread = thread; + } + + /** Returns false if not able to find a frame within a reasonable range. */ + public boolean run(long regionInBytesToSearch) { + Address sp = context.getRegisterAsAddress(AMD64ThreadContext.RSP); + Address pc = context.getRegisterAsAddress(AMD64ThreadContext.RIP); + Address fp = context.getRegisterAsAddress(AMD64ThreadContext.RBP); + if (sp == null) { + // Bail out if no last java frame either + if (thread.getLastJavaSP() != null) { + setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); + return true; + } + return false; + } + Address end = sp.addOffsetTo(regionInBytesToSearch); + VM vm = VM.getVM(); + + setValues(null, null, null); // Assume we're not going to find anything + + if (vm.isJavaPCDbg(pc)) { + if (vm.isClientCompiler()) { + // If the topmost frame is a Java frame, we are (pretty much) + // guaranteed to have a viable EBP. We should be more robust + // than this (we have the potential for losing entire threads' + // stack traces) but need to see how much work we really have + // to do here. Searching the stack for an (SP, FP) pair is + // hard since it's easy to misinterpret inter-frame stack + // pointers as base-of-frame pointers; we also don't know the + // sizes of C1 frames (not registered in the nmethod) so can't + // derive them from ESP. + + setValues(sp, fp, pc); + return true; + } else { + if (vm.getInterpreter().contains(pc)) { + if (DEBUG) { + System.out.println("CurrentFrameGuess: choosing interpreter frame: sp = " + + sp + ", fp = " + fp + ", pc = " + pc); + } + setValues(sp, fp, pc); + return true; + } + + // For the server compiler, EBP is not guaranteed to be valid + // for compiled code. In addition, an earlier attempt at a + // non-searching algorithm (see below) failed because the + // stack pointer from the thread context was pointing + // (considerably) beyond the ostensible end of the stack, into + // garbage; walking from the topmost frame back caused a crash. + // + // This algorithm takes the current PC as a given and tries to + // find the correct corresponding SP by walking up the stack + // and repeatedly performing stackwalks (very inefficient). + // + // FIXME: there is something wrong with stackwalking across + // adapter frames...this is likely to be the root cause of the + // failure with the simpler algorithm below. + + for (long offset = 0; + offset < regionInBytesToSearch; + offset += vm.getAddressSize()) { + try { + Address curSP = sp.addOffsetTo(offset); + Frame frame = new AMD64Frame(curSP, null, pc); + RegisterMap map = thread.newRegisterMap(false); + while (frame != null) { + if (frame.isEntryFrame() && frame.entryFrameIsFirst()) { + // We were able to traverse all the way to the + // bottommost Java frame. + // This sp looks good. Keep it. + if (DEBUG) { + System.out.println("CurrentFrameGuess: Choosing sp = " + curSP + ", pc = " + pc); + } + setValues(curSP, null, pc); + return true; + } + frame = frame.sender(map); + } + } catch (Exception e) { + if (DEBUG) { + System.out.println("CurrentFrameGuess: Exception " + e + " at offset " + offset); + } + // Bad SP. Try another. + } + } + + // Were not able to find a plausible SP to go with this PC. + // Bail out. + return false; + + /* + // Original algorithm which does not work because SP was + // pointing beyond where it should have: + + // For the server compiler, EBP is not guaranteed to be valid + // for compiled code. We see whether the PC is in the + // interpreter and take care of that, otherwise we run code + // (unfortunately) duplicated from AMD64Frame.senderForCompiledFrame. + + CodeCache cc = vm.getCodeCache(); + if (cc.contains(pc)) { + CodeBlob cb = cc.findBlob(pc); + + // See if we can derive a frame pointer from SP and PC + // NOTE: This is the code duplicated from AMD64Frame + Address saved_fp = null; + int llink_offset = cb.getLinkOffset(); + if (llink_offset >= 0) { + // Restore base-pointer, since next frame might be an interpreter frame. + Address fp_addr = sp.addOffsetTo(VM.getVM().getAddressSize() * llink_offset); + saved_fp = fp_addr.getAddressAt(0); + } + + setValues(sp, saved_fp, pc); + return true; + } + */ + } + } else { + // If the current program counter was not known to us as a Java + // PC, we currently assume that we are in the run-time system + // and attempt to look to thread-local storage for saved ESP and + // EBP. Note that if these are null (because we were, in fact, + // in Java code, i.e., vtable stubs or similar, and the SA + // didn't have enough insight into the target VM to understand + // that) then we are going to lose the entire stack trace for + // the thread, which is sub-optimal. FIXME. + + if (DEBUG) { + System.out.println("CurrentFrameGuess: choosing last Java frame: sp = " + + thread.getLastJavaSP() + ", fp = " + thread.getLastJavaFP()); + } + if (thread.getLastJavaSP() == null) { + return false; // No known Java frames on stack + } + setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); + return true; + } + } + + public Address getSP() { return spFound; } + public Address getFP() { return fpFound; } + /** May be null if getting values from thread-local storage; take + care to call the correct AMD64Frame constructor to recover this if + necessary */ + public Address getPC() { return pcFound; } + + private void setValues(Address sp, Address fp, Address pc) { + spFound = sp; + fpFound = fp; + pcFound = pc; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java new file mode 100644 index 00000000000..fd241783cf6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java @@ -0,0 +1,528 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.amd64; + +import java.util.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.compiler.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** Specialization of and implementation of abstract methods of the + Frame class for the amd64 CPU. */ + +public class AMD64Frame extends Frame { + private static final boolean DEBUG; + static { + DEBUG = System.getProperty("sun.jvm.hotspot.runtime.amd64.AMD64Frame.DEBUG") != null; + } + + // refer to frame_amd64.hpp + private static final int PC_RETURN_OFFSET = 0; + // All frames + private static final int LINK_OFFSET = 0; + private static final int RETURN_ADDR_OFFSET = 1; + private static final int SENDER_SP_OFFSET = 2; + + // Interpreter frames + private static final int INTERPRETER_FRAME_MIRROR_OFFSET = 2; // for native calls only + private static final int INTERPRETER_FRAME_SENDER_SP_OFFSET = -1; + private static final int INTERPRETER_FRAME_LAST_SP_OFFSET = INTERPRETER_FRAME_SENDER_SP_OFFSET - 1; + private static final int INTERPRETER_FRAME_METHOD_OFFSET = INTERPRETER_FRAME_LAST_SP_OFFSET - 1; + private static int INTERPRETER_FRAME_MDX_OFFSET; // Non-core builds only + private static int INTERPRETER_FRAME_CACHE_OFFSET; + private static int INTERPRETER_FRAME_LOCALS_OFFSET; + private static int INTERPRETER_FRAME_BCX_OFFSET; + private static int INTERPRETER_FRAME_INITIAL_SP_OFFSET; + private static int INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET; + private static int INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET; + + // Entry frames + private static final int ENTRY_FRAME_CALL_WRAPPER_OFFSET = -6; + + // Native frames + private static final int NATIVE_FRAME_INITIAL_PARAM_OFFSET = 2; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + if (VM.getVM().isCore()) { + INTERPRETER_FRAME_CACHE_OFFSET = INTERPRETER_FRAME_METHOD_OFFSET - 1; + } else { + INTERPRETER_FRAME_MDX_OFFSET = INTERPRETER_FRAME_METHOD_OFFSET - 1; + INTERPRETER_FRAME_CACHE_OFFSET = INTERPRETER_FRAME_MDX_OFFSET - 1; + } + INTERPRETER_FRAME_LOCALS_OFFSET = INTERPRETER_FRAME_CACHE_OFFSET - 1; + INTERPRETER_FRAME_BCX_OFFSET = INTERPRETER_FRAME_LOCALS_OFFSET - 1; + INTERPRETER_FRAME_INITIAL_SP_OFFSET = INTERPRETER_FRAME_BCX_OFFSET - 1; + INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET; + INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET; + } + + // an additional field beyond sp and pc: + Address raw_fp; // frame pointer + private Address raw_unextendedSP; + + private AMD64Frame() { + } + + private void adjustForDeopt() { + if ( pc != null) { + // Look for a deopt pc and if it is deopted convert to original pc + CodeBlob cb = VM.getVM().getCodeCache().findBlob(pc); + if (cb != null && cb.isJavaMethod()) { + NMethod nm = (NMethod) cb; + if (pc.equals(nm.deoptBegin())) { + // adjust pc if frame is deoptimized. + if (Assert.ASSERTS_ENABLED) { + Assert.that(this.getUnextendedSP() != null, "null SP in Java frame"); + } + pc = this.getUnextendedSP().getAddressAt(nm.origPCOffset()); + deoptimized = true; + } + } + } + } + + public AMD64Frame(Address raw_sp, Address raw_fp, Address pc) { + this.raw_sp = raw_sp; + this.raw_unextendedSP = raw_sp; + this.raw_fp = raw_fp; + this.pc = pc; + + // Frame must be fully constructed before this call + adjustForDeopt(); + + if (DEBUG) { + System.out.println("AMD64Frame(sp, fp, pc): " + this); + dumpStack(); + } + } + + public AMD64Frame(Address raw_sp, Address raw_fp) { + this.raw_sp = raw_sp; + this.raw_unextendedSP = raw_sp; + this.raw_fp = raw_fp; + this.pc = raw_sp.getAddressAt(-1 * VM.getVM().getAddressSize()); + + // Frame must be fully constructed before this call + adjustForDeopt(); + + if (DEBUG) { + System.out.println("AMD64Frame(sp, fp): " + this); + dumpStack(); + } + } + + // This constructor should really take the unextended SP as an arg + // but then the constructor is ambiguous with constructor that takes + // a PC so take an int and convert it. + public AMD64Frame(Address raw_sp, Address raw_fp, long extension) { + this.raw_sp = raw_sp; + if ( raw_sp == null) { + this.raw_unextendedSP = null; + } else { + this.raw_unextendedSP = raw_sp.addOffsetTo(extension); + } + this.raw_fp = raw_fp; + this.pc = raw_sp.getAddressAt(-1 * VM.getVM().getAddressSize()); + + // Frame must be fully constructed before this call + adjustForDeopt(); + + if (DEBUG) { + System.out.println("AMD64Frame(sp, fp, extension): " + this); + dumpStack(); + } + + } + + public Object clone() { + AMD64Frame frame = new AMD64Frame(); + frame.raw_sp = raw_sp; + frame.raw_unextendedSP = raw_unextendedSP; + frame.raw_fp = raw_fp; + frame.pc = pc; + frame.deoptimized = deoptimized; + return frame; + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof AMD64Frame)) { + return false; + } + + AMD64Frame other = (AMD64Frame) arg; + + return (AddressOps.equal(getSP(), other.getSP()) && + AddressOps.equal(getFP(), other.getFP()) && + AddressOps.equal(getUnextendedSP(), other.getUnextendedSP()) && + AddressOps.equal(getPC(), other.getPC())); + } + + public int hashCode() { + if (raw_sp == null) { + return 0; + } + + return raw_sp.hashCode(); + } + + public String toString() { + return "sp: " + (getSP() == null? "null" : getSP().toString()) + + ", unextendedSP: " + (getUnextendedSP() == null? "null" : getUnextendedSP().toString()) + + ", fp: " + (getFP() == null? "null" : getFP().toString()) + + ", pc: " + (pc == null? "null" : pc.toString()); + } + + // accessors for the instance variables + public Address getFP() { return raw_fp; } + public Address getSP() { return raw_sp; } + public Address getID() { return raw_sp; } + + // FIXME: not implemented yet (should be done for Solaris/AMD64) + public boolean isSignalHandlerFrameDbg() { return false; } + public int getSignalNumberDbg() { return 0; } + public String getSignalNameDbg() { return null; } + + public boolean isInterpretedFrameValid() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isInterpretedFrame(), "Not an interpreted frame"); + } + + // These are reasonable sanity checks + if (getFP() == null || getFP().andWithMask(0x3) != null) { + return false; + } + + if (getSP() == null || getSP().andWithMask(0x3) != null) { + return false; + } + + if (getFP().addOffsetTo(INTERPRETER_FRAME_INITIAL_SP_OFFSET * VM.getVM().getAddressSize()).lessThan(getSP())) { + return false; + } + + // These are hacks to keep us out of trouble. + // The problem with these is that they mask other problems + if (getFP().lessThanOrEqual(getSP())) { + // this attempts to deal with unsigned comparison above + return false; + } + + if (getFP().minus(getSP()) > 4096 * VM.getVM().getAddressSize()) { + // stack frames shouldn't be large. + return false; + } + + return true; + } + + // FIXME: not applicable in current system + // void patch_pc(Thread* thread, address pc); + + public Frame sender(RegisterMap regMap, CodeBlob cb) { + AMD64RegisterMap map = (AMD64RegisterMap) regMap; + + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + // Default is we done have to follow them. The sender_for_xxx will + // update it accordingly + map.setIncludeArgumentOops(false); + + if (isEntryFrame()) return senderForEntryFrame(map); + if (isInterpretedFrame()) return senderForInterpreterFrame(map); + + + if (!VM.getVM().isCore()) { + if(cb == null) { + cb = VM.getVM().getCodeCache().findBlob(getPC()); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb.equals(VM.getVM().getCodeCache().findBlob(getPC())), "Must be the same"); + } + } + + if (cb != null) { + return senderForCompiledFrame(map, cb); + } + } + + // Must be native-compiled frame, i.e. the marshaling code for native + // methods that exists in the core system. + return new AMD64Frame(getSenderSP(), getLink(), getSenderPC()); + } + + private Frame senderForEntryFrame(AMD64RegisterMap map) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + // Java frame called from C; skip all C frames and return top C + // frame of that chunk as the sender + AMD64JavaCallWrapper jcw = (AMD64JavaCallWrapper) getEntryFrameCallWrapper(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(!entryFrameIsFirst(), "next Java fp must be non zero"); + Assert.that(jcw.getLastJavaSP().greaterThan(getSP()), "must be above this frame on stack"); + } + AMD64Frame fr; + if (jcw.getLastJavaPC() != null) { + fr = new AMD64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP(), jcw.getLastJavaPC()); + } else { + fr = new AMD64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP()); + } + map.clear(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(map.getIncludeArgumentOops(), "should be set by clear"); + } + return fr; + } + + private Frame senderForInterpreterFrame(AMD64RegisterMap map) { + Address unextendedSP = addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0); + Address sp = addressOfStackSlot(SENDER_SP_OFFSET); + // We do not need to update the callee-save register mapping because above + // us is either another interpreter frame or a converter-frame, but never + // directly a compiled frame. + // 11/24/04 SFG. This is no longer true after adapter were removed. However at the moment + // C2 no longer uses callee save register for java calls so there are no callee register + // to find. + return new AMD64Frame(sp, getLink(), unextendedSP.minus(sp)); + } + + private Frame senderForCompiledFrame(AMD64RegisterMap map, CodeBlob cb) { + // + // NOTE: some of this code is (unfortunately) duplicated in AMD64CurrentFrameGuess + // + + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + // frame owned by optimizing compiler + Address sender_sp = null; + + + if (VM.getVM().isClientCompiler()) { + sender_sp = addressOfStackSlot(SENDER_SP_OFFSET); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb.getFrameSize() >= 0, "Compiled by Compiler1: do not use"); + } + sender_sp = getUnextendedSP().addOffsetTo(cb.getFrameSize()); + } + + // On Intel the return_address is always the word on the stack + Address sender_pc = sender_sp.getAddressAt(-1 * VM.getVM().getAddressSize()); + + if (map.getUpdateMap() && cb.getOopMaps() != null) { + OopMapSet.updateRegisterMap(this, cb, map, true); + } + + if (VM.getVM().isClientCompiler()) { + // Move this here for C1 and collecting oops in arguments (According to Rene) + map.setIncludeArgumentOops(cb.callerMustGCArguments(map.getThread())); + } + + Address saved_fp = null; + if (VM.getVM().isClientCompiler()) { + saved_fp = getFP().getAddressAt(0); + } else if (VM.getVM().isServerCompiler() && + (VM.getVM().getInterpreter().contains(sender_pc) || + VM.getVM().getStubRoutines().returnsToCallStub(sender_pc))) { + // C2 prologue saves EBP in the usual place. + // however only use it if the sender had link infomration in it. + saved_fp = sender_sp.getAddressAt(-2 * VM.getVM().getAddressSize()); + } + + return new AMD64Frame(sender_sp, saved_fp, sender_pc); + } + + protected boolean hasSenderPD() { + // FIXME + // Check for null ebp? Need to do some tests. + return true; + } + + public long frameSize() { + return (getSenderSP().minus(getSP()) / VM.getVM().getAddressSize()); + } + + public Address getLink() { + return addressOfStackSlot(LINK_OFFSET).getAddressAt(0); + } + + // FIXME: not implementable yet + //inline void frame::set_link(intptr_t* addr) { *(intptr_t **)addr_at(link_offset) = addr; } + + public Address getUnextendedSP() { return raw_unextendedSP; } + + // Return address: + public Address getSenderPCAddr() { return addressOfStackSlot(RETURN_ADDR_OFFSET); } + public Address getSenderPC() { return getSenderPCAddr().getAddressAt(0); } + + // return address of param, zero origin index. + public Address getNativeParamAddr(int idx) { + return addressOfStackSlot(NATIVE_FRAME_INITIAL_PARAM_OFFSET + idx); + } + + public Address getSenderSP() { return addressOfStackSlot(SENDER_SP_OFFSET); } + + public Address compiledArgumentToLocationPD(VMReg reg, RegisterMap regMap, int argSize) { + if (VM.getVM().isCore() || VM.getVM().isClientCompiler()) { + throw new RuntimeException("Should not reach here"); + } + + return oopMapRegToLocation(reg, regMap); + } + + public Address addressOfInterpreterFrameLocals() { + return addressOfStackSlot(INTERPRETER_FRAME_LOCALS_OFFSET); + } + + private Address addressOfInterpreterFrameBCX() { + return addressOfStackSlot(INTERPRETER_FRAME_BCX_OFFSET); + } + + public int getInterpreterFrameBCI() { + // FIXME: this is not atomic with respect to GC and is unsuitable + // for use in a non-debugging, or reflective, system. Need to + // figure out how to express this. + Address bcp = addressOfInterpreterFrameBCX().getAddressAt(0); + OopHandle methodHandle = addressOfInterpreterFrameMethod().getOopHandleAt(0); + Method method = (Method) VM.getVM().getObjectHeap().newOop(methodHandle); + return (int) bcpToBci(bcp, method); + } + + public Address addressOfInterpreterFrameMDX() { + return addressOfStackSlot(INTERPRETER_FRAME_MDX_OFFSET); + } + + // FIXME + //inline int frame::interpreter_frame_monitor_size() { + // return BasicObjectLock::size(); + //} + + // expression stack + // (the max_stack arguments are used by the GC; see class FrameClosure) + + public Address addressOfInterpreterFrameExpressionStack() { + Address monitorEnd = interpreterFrameMonitorEnd().address(); + return monitorEnd.addOffsetTo(-1 * VM.getVM().getAddressSize()); + } + + public int getInterpreterFrameExpressionStackDirection() { return -1; } + + // top of expression stack + public Address addressOfInterpreterFrameTOS() { + return getSP(); + } + + /** Expression stack from top down */ + public Address addressOfInterpreterFrameTOSAt(int slot) { + return addressOfInterpreterFrameTOS().addOffsetTo(slot * VM.getVM().getAddressSize()); + } + + public Address getInterpreterFrameSenderSP() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isInterpretedFrame(), "interpreted frame expected"); + } + return addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0); + } + + // Monitors + public BasicObjectLock interpreterFrameMonitorBegin() { + return new BasicObjectLock(addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET)); + } + + public BasicObjectLock interpreterFrameMonitorEnd() { + Address result = addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET).getAddressAt(0); + if (Assert.ASSERTS_ENABLED) { + // make sure the pointer points inside the frame + Assert.that(AddressOps.gt(getFP(), result), "result must < than frame pointer"); + Assert.that(AddressOps.lte(getSP(), result), "result must >= than stack pointer"); + } + return new BasicObjectLock(result); + } + + public int interpreterFrameMonitorSize() { + return BasicObjectLock.size(); + } + + // Method + public Address addressOfInterpreterFrameMethod() { + return addressOfStackSlot(INTERPRETER_FRAME_METHOD_OFFSET); + } + + // Constant pool cache + public Address addressOfInterpreterFrameCPCache() { + return addressOfStackSlot(INTERPRETER_FRAME_CACHE_OFFSET); + } + + // Entry frames + public JavaCallWrapper getEntryFrameCallWrapper() { + return new AMD64JavaCallWrapper(addressOfStackSlot(ENTRY_FRAME_CALL_WRAPPER_OFFSET).getAddressAt(0)); + } + + protected Address addressOfSavedOopResult() { + // offset is 2 for compiler2 and 3 for compiler1 + return getSP().addOffsetTo((VM.getVM().isClientCompiler() ? 2 : 3) * + VM.getVM().getAddressSize()); + } + + protected Address addressOfSavedReceiver() { + return getSP().addOffsetTo(-4 * VM.getVM().getAddressSize()); + } + + private void dumpStack() { + if (getFP() != null) { + for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize()); + AddressOps.lte(addr, getFP().addOffsetTo(5 * VM.getVM().getAddressSize())); + addr = addr.addOffsetTo(VM.getVM().getAddressSize())) { + System.out.println(addr + ": " + addr.getAddressAt(0)); + } + } else { + for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize()); + AddressOps.lte(addr, getSP().addOffsetTo(20 * VM.getVM().getAddressSize())); + addr = addr.addOffsetTo(VM.getVM().getAddressSize())) { + System.out.println(addr + ": " + addr.getAddressAt(0)); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64JavaCallWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64JavaCallWrapper.java new file mode 100644 index 00000000000..ee26edb3e5d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64JavaCallWrapper.java @@ -0,0 +1,56 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.amd64; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +public class AMD64JavaCallWrapper extends JavaCallWrapper { + private static AddressField lastJavaFPField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaFrameAnchor"); + + lastJavaFPField = type.getAddressField("_last_Java_fp"); + } + + public AMD64JavaCallWrapper(Address addr) { + super(addr); + } + + public Address getLastJavaFP() { + return lastJavaFPField.getValue(addr.addOffsetTo(anchorField.getOffset())); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64RegisterMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64RegisterMap.java new file mode 100644 index 00000000000..79fd630c808 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64RegisterMap.java @@ -0,0 +1,52 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.amd64; + +import sun.jvm.hotspot.asm.amd64.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; + +public class AMD64RegisterMap extends RegisterMap { + + /** This is the only public constructor */ + public AMD64RegisterMap(JavaThread thread, boolean updateMap) { + super(thread, updateMap); + } + + protected AMD64RegisterMap(RegisterMap map) { + super(map); + } + + public Object clone() { + AMD64RegisterMap retval = new AMD64RegisterMap(this); + return retval; + } + + // no PD state to clear or copy: + protected void clearPD() {} + protected void initializePD() {} + protected void initializeFromPD(RegisterMap map) {} + protected Address getLocationPD(VMReg reg) { return null; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64CurrentFrameGuess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64CurrentFrameGuess.java new file mode 100644 index 00000000000..095cf225d18 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64CurrentFrameGuess.java @@ -0,0 +1,80 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.ia64; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.ia64.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.runtime.*; + +/**

Should be able to be used on all ia64 platforms we support + (Win32, Linux) to implement JavaThread's + "currentFrameGuess()" functionality. Input is an IA64ThreadContext; + output is SP, FP, and PC for an IA64Frame. Instantiation of the + IA64Frame is left to the caller, since we may need to subclass + IA64Frame to support signal handler frames on Unix platforms.

+ +

This is pretty much impossible on ia64. +

*/ + +public class IA64CurrentFrameGuess { + private IA64ThreadContext context; + private JavaThread thread; + private Address spFound; + private Address fpFound; + private Address pcFound; + + private static final boolean DEBUG = false; + + public IA64CurrentFrameGuess(IA64ThreadContext context, + JavaThread thread) { + this.context = context; + this.thread = thread; + } + + /** Returns false if not able to find a frame within a reasonable range. */ + public boolean run(long regionInBytesToSearch) { + /* + Without using the stack walking library this is not possible on ia64. + There is also the issue of walking dynamic code where there is no + stack walking info generated. + */ + return false; + } + + public Address getSP() { return null; } + public Address getFP() { return null; } + /** May be null if getting values from thread-local storage; take + care to call the correct IA64Frame constructor to recover this if + necessary */ + public Address getPC() { return null; } + + private void setValues(Address sp, Address fp, Address pc) { + spFound = sp; + fpFound = fp; + pcFound = pc; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64Frame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64Frame.java new file mode 100644 index 00000000000..ca228dce1c9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64Frame.java @@ -0,0 +1,481 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.ia64; + +import java.util.*; +// import sun.jvm.hotspot.asm.ia64.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.compiler.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** Specialization of and implementation of abstract methods of the + Frame class for the ia64 family of CPUs. */ + +public class IA64Frame extends Frame { + private static final boolean DEBUG = false; + + // All frames + + // Interpreter frames + + // Entry frames + + // Native frames + + // an additional field beyond sp and pc: + // Address raw_fp; // frame pointer only 1.4.2 + + Address iframe; + + private IA64Frame() { + } + + public IA64Frame(Address raw_sp, Address iframe, Address pc) { + this.raw_sp = raw_sp; + this.iframe = iframe; + this.pc = pc; + if (DEBUG) { + System.err.println("IA64Frame(sp, iframe, pc): " + this); + dumpStack(); + } + } + + public Object clone() { + IA64Frame frame = new IA64Frame(); + frame.raw_sp = raw_sp; + frame.iframe = iframe; + frame.pc = pc; + return frame; + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof IA64Frame)) { + return false; + } + + IA64Frame other = (IA64Frame) arg; + + return (AddressOps.equal(getSP(), other.getSP()) && + AddressOps.equal(getIFRAME(), other.getIFRAME()) && + AddressOps.equal(getPC(), other.getPC())); + } + + public int hashCode() { + if (iframe == null) { + return 0; + } + + return iframe.hashCode(); + } + + public String toString() { + return "sp: " + (getSP() == null? "null" : getSP().toString()) + + ", iframe: " + (getIFRAME() == null? "null" : getIFRAME().toString()) + + ", pc: " + (pc == null? "null" : pc.toString()); + } + + // accessors for the instance variables + public Address getFP() { return null; } + public Address getIFRAME() { return iframe; } + public Address getSP() { return raw_sp; } + public Address getID() { return getFP(); } + + // FIXME: not implemented yet + public boolean isSignalHandlerFrameDbg() { return false; } + public int getSignalNumberDbg() { return 0; } + public String getSignalNameDbg() { return null; } + + // FIXME: do sanity checks + public boolean isInterpretedFrameValid() { + return true; + } + + public boolean isInterpretedFrame() { return iframe != null; } + + + // FIXME: not applicable in current system + // void patch_pc(Thread* thread, address pc); + + public Frame sender(RegisterMap regMap, CodeBlob cb) { + + if (iframe == null) { + return null; + } + + cInterpreter fr = new cInterpreter(iframe); + + if (fr.prev() == null) { + Address wrapper = fr.wrapper(); + if ( wrapper == null) { + return null; + } + IA64JavaCallWrapper jcw = new IA64JavaCallWrapper(wrapper); + Address iprev = jcw.getPrevIFrame(); + if (iprev == null) { + return null; + } + return new IA64Frame(null, iprev, null); + } else { + return new IA64Frame(null, fr.prev(), null); + } + + /* + IA64RegisterMap map = (IA64RegisterMap) regMap; + + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + // Default is we done have to follow them. The sender_for_xxx will + // update it accordingly + map.setIncludeArgumentOops(false); + + if (isEntryFrame()) return senderForEntryFrame(map); + if (isInterpretedFrame()) return senderForInterpreterFrame(map); + + if (!VM.getVM().isCore()) { + if(cb == null) { + cb = VM.getVM().getCodeCache().findBlob(getPC()); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb.equals(VM.getVM().getCodeCache().findBlob(getPC())), "Must be the same"); + } + } + + if (cb != null) { + return senderForCompiledFrame(map, cb); + } + } + + // Must be native-compiled frame, i.e. the marshaling code for native + // methods that exists in the core system. + return new IA64Frame(getSenderSP(), getLink(), getSenderPC()); + + */ + } + + private Frame senderForEntryFrame(IA64RegisterMap map) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + /* + // Java frame called from C; skip all C frames and return top C + // frame of that chunk as the sender + IA64JavaCallWrapper jcw = (IA64JavaCallWrapper) getEntryFrameCallWrapper(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(!entryFrameIsFirst(), "next Java fp must be non zero"); + Assert.that(jcw.getLastJavaSP().greaterThan(getSP()), "must be above this frame on stack"); + } + IA64Frame fr = new IA64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP(), null); + map.clear(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(map.getIncludeArgumentOops(), "should be set by clear"); + } + return fr; + */ + throw new RuntimeException("senderForEntryFrame NYI"); + } + + private Frame senderForInterpreterFrame(IA64RegisterMap map) { + /* + Address sp = addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0); + // We do not need to update the callee-save register mapping because above + // us is either another interpreter frame or a converter-frame, but never + // directly a compiled frame. + return new IA64Frame(sp, getLink(), getSenderPC()); + */ + throw new RuntimeException("senderForInterpreterFrame NYI"); + } + + private Frame senderForDeoptimizedFrame(IA64RegisterMap map, CodeBlob cb) { + // FIXME + throw new RuntimeException("Deoptimized frames not handled yet"); + } + + private Frame senderForCompiledFrame(IA64RegisterMap map, CodeBlob cb) { + // + // NOTE: some of this code is (unfortunately) duplicated in IA64CurrentFrameGuess + // + + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + throw new RuntimeException("senderForCompiledFrame NYI"); + + /* + + // frame owned by optimizing compiler + Address sender_sp = null; + + if (VM.getVM().isClientCompiler()) { + sender_sp = addressOfStackSlot(SENDER_SP_OFFSET); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb.getFrameSize() >= 0, "Compiled by Compiler1: do not use"); + } + sender_sp = getSP().addOffsetTo(cb.getFrameSize()); + } + + // On Intel the return_address is always the word on the stack + Address sender_pc = sender_sp.getAddressAt(-1 * VM.getVM().getAddressSize()); + + if (map.getUpdateMap() && cb.getOopMaps() != null) { + OopMapSet.updateRegisterMap(this, cb, map, true); + } + + Address saved_fp = null; + if (VM.getVM().isClientCompiler()) { + saved_fp = getFP().getAddressAt(0); + } else { + int llink_offset = cb.getLinkOffset(); + if (llink_offset >= 0) { + // Restore base-pointer, since next frame might be an interpreter frame. + Address fp_addr = getSP().addOffsetTo(VM.getVM().getAddressSize() * llink_offset); + saved_fp = fp_addr.getAddressAt(0); + } + } + + sender_sp = null ; // sp_addr.getAddressAt(0); + + return new IA64Frame(sender_sp, saved_fp, sender_pc); + + */ + } + + protected boolean hasSenderPD() { + // FIXME + return true; + } + + public long frameSize() { + throw new RuntimeException("frameSize NYI"); + /* + return (getSenderSP().minus(getSP()) / VM.getVM().getAddressSize()); + */ + } + + public Address getLink() { + throw new RuntimeException("getLink NYI"); + /* + return addressOfStackSlot(LINK_OFFSET).getAddressAt(0); + */ + } + + // FIXME: not implementable yet + //inline void frame::set_link(intptr_t* addr) { *(intptr_t **)addr_at(link_offset) = addr; } + + public Address getUnextendedSP() { return getSP(); } + + // Return address: + /* + public Address getSenderPCAddr() { return addressOfStackSlot(RETURN_ADDR_OFFSET); } + */ + + public Address getSenderPC() { return null; } + + /* + // return address of param, zero origin index. + public Address getNativeParamAddr(int idx) { + return addressOfStackSlot(NATIVE_FRAME_INITIAL_PARAM_OFFSET + idx); + } + */ + + public Address getSenderSP() { return null; } + + /* + public Address compiledArgumentToLocationPD(VMReg reg, RegisterMap regMap, int argSize) { + if (VM.getVM().isCore() || VM.getVM().isClientCompiler()) { + throw new RuntimeException("Should not reach here"); + } + + return oopMapRegToLocation(reg, regMap); + } + + */ + + public Address addressOfInterpreterFrameLocals() { + if (iframe == null) { + throw new RuntimeException("Not an Interpreter frame"); + } + cInterpreter fr = new cInterpreter(iframe); + return fr.locals(); + } + + private Address addressOfInterpreterFrameBCX() { + if (iframe == null) { + throw new RuntimeException("Not an Interpreter frame"); + } + cInterpreter fr = new cInterpreter(iframe); + return fr.bcpAddr(); + } + + public int getInterpreterFrameBCI() { + // FIXME: this is not atomic with respect to GC and is unsuitable + // for use in a non-debugging, or reflective, system. Need to + // figure out how to express this. + Address bcp = addressOfInterpreterFrameBCX().getAddressAt(0); + OopHandle methodHandle = addressOfInterpreterFrameMethod().getOopHandleAt(0); + Method method = (Method) VM.getVM().getObjectHeap().newOop(methodHandle); + return bcpToBci(bcp, method); + } + + public Address addressOfInterpreterFrameMDX() { + return null; + } + + // FIXME + //inline int frame::interpreter_frame_monitor_size() { + // return BasicObjectLock::size(); + //} + + // expression stack + // (the max_stack arguments are used by the GC; see class FrameClosure) + + public Address addressOfInterpreterFrameExpressionStack() { + if (iframe == null) { + throw new RuntimeException("Not an Interpreter frame"); + } + cInterpreter fr = new cInterpreter(iframe); + return fr.stackBase(); + } + + public int getInterpreterFrameExpressionStackDirection() { return -1; } + + // top of expression stack + public Address addressOfInterpreterFrameTOS() { + if (iframe == null) { + throw new RuntimeException("Not an Interpreter frame"); + } + cInterpreter fr = new cInterpreter(iframe); + // tos always points to first free element in c++ interpreter not tos + return fr.stackBase().addOffsetTo(VM.getVM().getAddressSize()); + } + + /** Expression stack from top down */ + public Address addressOfInterpreterFrameTOSAt(int slot) { + return addressOfInterpreterFrameTOS().addOffsetTo(slot * VM.getVM().getAddressSize()); + } + + public Address getInterpreterFrameSenderSP() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isInterpretedFrame(), "interpreted frame expected"); + } + throw new RuntimeException("getInterpreterFrameSenderSP NYI"); + } + + // Monitors + public BasicObjectLock interpreterFrameMonitorBegin() { + if (iframe == null) { + throw new RuntimeException("Not an Interpreter frame"); + } + cInterpreter fr = new cInterpreter(iframe); + return new BasicObjectLock(fr.monitorBase()); + } + + public BasicObjectLock interpreterFrameMonitorEnd() { + if (iframe == null) { + throw new RuntimeException("Not an Interpreter frame"); + } + cInterpreter fr = new cInterpreter(iframe); + // Monitors end is just above stack base (2 slots per monitor) + Address result = fr.stackBase().addOffsetTo(2 * VM.getVM().getAddressSize()); + /* + if (Assert.ASSERTS_ENABLED) { + // make sure the pointer points inside the frame + Assert.that(AddressOps.gt(getFP(), result), "result must < than frame pointer"); + Assert.that(AddressOps.lte(getSP(), result), "result must >= than stack pointer"); + } + */ + return new BasicObjectLock(result); + } + + public int interpreterFrameMonitorSize() { + return BasicObjectLock.size(); + } + + // Method + public Address addressOfInterpreterFrameMethod() { + if (iframe == null) { + throw new RuntimeException("Not an Interpreter frame"); + } + cInterpreter fr = new cInterpreter(iframe); + return fr.methodAddr(); + } + + // Constant pool cache + public Address addressOfInterpreterFrameCPCache() { + if (iframe == null) { + throw new RuntimeException("Not an Interpreter frame"); + } + cInterpreter fr = new cInterpreter(iframe); + return fr.constantsAddr(); + } + + // Entry frames + public JavaCallWrapper getEntryFrameCallWrapper() { + throw new RuntimeException("getEntryFrameCallWrapper NYI"); + } + + protected Address addressOfSavedOopResult() { + throw new RuntimeException("public boolean isInterpretedFrame() NYI"); + /* + // offset is 2 for compiler2 and 3 for compiler1 + return getSP().addOffsetTo((VM.getVM().isClientCompiler() ? 2 : 3) * + VM.getVM().getAddressSize()); + */ + } + + protected Address addressOfSavedReceiver() { + throw new RuntimeException("getEntryFrameCallWrapper NYI"); + // return getSP().addOffsetTo(-4 * VM.getVM().getAddressSize()); + } + + private void dumpStack() { + /* + if (getFP() != null) { + for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize()); + AddressOps.lte(addr, getFP().addOffsetTo(5 * VM.getVM().getAddressSize())); + addr = addr.addOffsetTo(VM.getVM().getAddressSize())) { + System.out.println(addr + ": " + addr.getAddressAt(0)); + } + } else { + for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize()); + AddressOps.lte(addr, getSP().addOffsetTo(20 * VM.getVM().getAddressSize())); + addr = addr.addOffsetTo(VM.getVM().getAddressSize())) { + System.out.println(addr + ": " + addr.getAddressAt(0)); + } + } + */ + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64JavaCallWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64JavaCallWrapper.java new file mode 100644 index 00000000000..7d3377ec321 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64JavaCallWrapper.java @@ -0,0 +1,56 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.ia64; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +public class IA64JavaCallWrapper extends JavaCallWrapper { + private static AddressField lastJavaIFrameField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaCallWrapper"); + + lastJavaIFrameField = type.getAddressField("_last_Java_iframe"); + } + + public IA64JavaCallWrapper(Address addr) { + super(addr); + } + + public Address getPrevIFrame() { + return lastJavaIFrameField.getValue(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64RegisterMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64RegisterMap.java new file mode 100644 index 00000000000..ee178449e32 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/IA64RegisterMap.java @@ -0,0 +1,52 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.ia64; + +import sun.jvm.hotspot.asm.ia64.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; + +public class IA64RegisterMap extends RegisterMap { + + /** This is the only public constructor */ + public IA64RegisterMap(JavaThread thread, boolean updateMap) { + super(thread, updateMap); + } + + protected IA64RegisterMap(RegisterMap map) { + super(map); + } + + public Object clone() { + IA64RegisterMap retval = new IA64RegisterMap(this); + return retval; + } + + // no PD state to clear or copy: + protected void clearPD() {} + protected void initializePD() {} + protected void initializeFromPD(RegisterMap map) {} + protected Address getLocationPD(VMReg reg) { return null; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/cInterpreter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/cInterpreter.java new file mode 100644 index 00000000000..4699e87aea8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ia64/cInterpreter.java @@ -0,0 +1,199 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.ia64; + +import java.util.*; +// import sun.jvm.hotspot.asm.ia64.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.compiler.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** Specialization of and implementation of abstract methods of the + Frame class for the ia64 family of CPUs. */ + +public class cInterpreter extends VMObject { + private static final boolean DEBUG = true; + + private static AddressField bcpField; + private static AddressField localsField; + private static AddressField constantsField; + private static AddressField methodField; + private static AddressField stackField; // i.e. tos + private static AddressField stackBaseField; // ultimate bottom of stack + private static AddressField stackLimitField; // ultimate top of stack + private static AddressField monitorBaseField; + private static CIntegerField messageField; + private static AddressField prevFieldField; + private static AddressField wrapperField; + private static AddressField prevField; + + private static int NO_REQUEST; + private static int INITIALIZE; + private static int METHOD_ENTRY; + private static int METHOD_RESUME; + private static int GOT_MONITORS; + private static int RETHROW_EXCEPTION; + private static int CALL_METHOD; + private static int RETURN_FROM_METHOD; + private static int RETRY_METHOD; + private static int MORE_MONITORS; + private static int THROWING_EXCEPTION; + private static int POPPING_FRAME; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + + Type cInterpreterType = db.lookupType("cInterpreter"); + bcpField = cInterpreterType.getAddressField("_bcp"); + localsField = cInterpreterType.getAddressField("_locals"); + constantsField = cInterpreterType.getAddressField("_constants"); + methodField = cInterpreterType.getAddressField("_method"); + stackField = cInterpreterType.getAddressField("_stack"); + stackBaseField = cInterpreterType.getAddressField("_stack_base"); + stackLimitField = cInterpreterType.getAddressField("_stack_limit"); + monitorBaseField = cInterpreterType.getAddressField("_monitor_base"); + // messageField = cInterpreterType.getCIntegerField("_msg"); + messageField = null; + wrapperField = cInterpreterType.getAddressField("_wrapper"); + prevField = cInterpreterType.getAddressField("_prev_link"); + + /* + NO_REQUEST = db.lookupIntConstant("no_request").intValue(); + INITIALIZE = db.lookupIntConstant("initialize").intValue(); + METHOD_ENTRY = db.lookupIntConstant("method_entry").intValue(); + METHOD_RESUME = db.lookupIntConstant("method_resume").intValue(); + GOT_MONITORS = db.lookupIntConstant("got_monitors").intValue(); + RETHROW_EXCEPTION = db.lookupIntConstant("rethrow_exception").intValue(); + CALL_METHOD = db.lookupIntConstant("call_method").intValue(); + RETURN_FROM_METHOD = db.lookupIntConstant("return_from_method").intValue(); + RETRY_METHOD = db.lookupIntConstant("retry_method").intValue(); + MORE_MONITORS = db.lookupIntConstant("more_monitors").intValue(); + THROWING_EXCEPTION = db.lookupIntConstant("throwing_exception").intValue(); + POPPING_FRAME = db.lookupIntConstant("popping_frame").intValue(); + */ + } + + + public cInterpreter(Address addr) { + super(addr); + } + + public Address prev() { + return prevField.getValue(addr); + } + + public Address locals() { + + Address val = localsField.getValue(addr); + return val; + } + + public Address localsAddr() { + + Address localsAddr = localsField.getValue(addr); + return localsAddr; + } + + public Address bcp() { + + Address val = bcpField.getValue(addr); + return val; + } + + public Address bcpAddr() { + + Address bcpAddr = addr.addOffsetTo(bcpField.getOffset()); + return bcpAddr; + } + + public Address constants() { + + Address val = constantsField.getValue(addr); + return val; + } + + public Address constantsAddr() { + + Address constantsAddr = constantsField.getValue(addr); + return constantsAddr; + } + + public Address method() { + + Address val = methodField.getValue(addr); + return val; + } + public Address methodAddr() { + + Address methodAddr = addr.addOffsetTo(methodField.getOffset()); + return methodAddr; + } + + public Address stack() { + + Address val = stackField.getValue(addr); + return val; + } + + public Address stackBase() { + + Address val = stackBaseField.getValue(addr); + return val; + } + + public Address stackLimit() { + + Address val = stackLimitField.getValue(addr); + return val; + } + + public Address monitorBase() { + + Address val = monitorBaseField.getValue(addr); + return val; + } + + public Address wrapper() { + + return wrapperField.getValue(addr); + } + + public int message() { + int val = (int) messageField.getValue(addr); + return val; + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux/LinuxSignals.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux/LinuxSignals.java new file mode 100644 index 00000000000..21def8cba77 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux/LinuxSignals.java @@ -0,0 +1,71 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.linux; + +public class LinuxSignals { + private static String[] signalNames = { + "", /* No signal 0 */ + "SIGHUP", /* hangup */ + "SIGINT", /* interrupt (rubout) */ + "SIGQUIT", /* quit (ASCII FS) */ + "SIGILL", /* illegal instruction (not reset when caught) */ + "SIGTRAP", /* trace trap (not reset when caught) */ + "SIGABRT", /* used by abort, replace SIGIOT in the future */ + "SIGIOT", + "SIGBUS", + "SIGFPE", /* floating point exception */ + "SIGKILL", /* kill (cannot be caught or ignored) */ + "SIGUSR1", /* user defined signal 1 */ + "SIGSEGV", /* segmentation violation */ + "SIGUSR2", /* user defined signal 2 */ + "SIGPIPE", /* write on a pipe with no one to read it */ + "SIGALRM", /* alarm clock */ + "SIGTERM", /* software termination signal from kill */ + "SIGSTKFLT", + "SIGCHLD", /* child status change alias */ + "SIGCONT", /* stopped process has been continued */ + "SIGSTOP", /* stop (cannot be caught or ignored) */ + "SIGTSTP", /* user stop requested from tty */ + "SIGTTIN", /* background tty read attempted */ + "SIGTTOU", /* background tty write attempted */ + "SIGURG", /* urgent socket condition */ + "SIGXCPU", /* exceeded cpu limit */ + "SIGXFSZ", /* exceeded file size limit */ + "SIGVTALRM", /* virtual timer expired */ + "SIGPROF", /* profiling timer expired */ + "SIGWINCH", /* window size change */ + "SIGPOLL", /* pollable event occured */ + "SIGPWR", /* power-fail restart */ + "SIGSYS" + }; + + public static String getSignalName(int sigNum) { + if ((sigNum <= 0) || (sigNum >= signalNames.length)) { + // Probably best to fail in a non-destructive way + return ""; + } + return signalNames[sigNum]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_amd64/LinuxAMD64JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_amd64/LinuxAMD64JavaThreadPDAccess.java new file mode 100644 index 00000000000..f0c72876f2b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_amd64/LinuxAMD64JavaThreadPDAccess.java @@ -0,0 +1,131 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.linux_amd64; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.amd64.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class LinuxAMD64JavaThreadPDAccess implements JavaThreadPDAccess { + private static AddressField lastJavaFPField; + private static AddressField osThreadField; + + // Field from OSThread + private static CIntegerField osThreadThreadIDField; + + // This is currently unneeded but is being kept in case we change + // the currentFrameGuess algorithm + private static final long GUESS_SCAN_RANGE = 128 * 1024; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + osThreadField = type.getAddressField("_osthread"); + + Type anchorType = db.lookupType("JavaFrameAnchor"); + lastJavaFPField = anchorType.getAddressField("_last_Java_fp"); + + Type osThreadType = db.lookupType("OSThread"); + osThreadThreadIDField = osThreadType.getCIntegerField("_thread_id"); + } + + public Address getLastJavaFP(Address addr) { + return lastJavaFPField.getValue(addr.addOffsetTo(sun.jvm.hotspot.runtime.JavaThread.getAnchorField().getOffset())); + } + + public Address getLastJavaPC(Address addr) { + return null; + } + + public Address getBaseOfStackPointer(Address addr) { + return null; + } + + public Frame getLastFramePD(JavaThread thread, Address addr) { + Address fp = thread.getLastJavaFP(); + if (fp == null) { + return null; // no information + } + return new AMD64Frame(thread.getLastJavaSP(), fp); + } + + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { + return new AMD64RegisterMap(thread, updateMap); + } + + public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { + ThreadProxy t = getThreadProxy(addr); + AMD64ThreadContext context = (AMD64ThreadContext) t.getContext(); + AMD64CurrentFrameGuess guesser = new AMD64CurrentFrameGuess(context, thread); + if (!guesser.run(GUESS_SCAN_RANGE)) { + return null; + } + if (guesser.getPC() == null) { + return new AMD64Frame(guesser.getSP(), guesser.getFP()); + } else { + return new AMD64Frame(guesser.getSP(), guesser.getFP(), guesser.getPC()); + } + } + + public void printThreadIDOn(Address addr, PrintStream tty) { + tty.print(getThreadProxy(addr)); + } + + public void printInfoOn(Address threadAddr, PrintStream tty) { + tty.print("Thread id: "); + printThreadIDOn(threadAddr, tty); +// tty.println("\nPostJavaState: " + getPostJavaState(threadAddr)); + } + + public Address getLastSP(Address addr) { + ThreadProxy t = getThreadProxy(addr); + AMD64ThreadContext context = (AMD64ThreadContext) t.getContext(); + return context.getRegisterAsAddress(AMD64ThreadContext.RSP); + } + + public ThreadProxy getThreadProxy(Address addr) { + // Addr is the address of the JavaThread. + // Fetch the OSThread (for now and for simplicity, not making a + // separate "OSThread" class in this package) + Address osThreadAddr = osThreadField.getValue(addr); + // Get the address of the _thread_id from the OSThread + Address threadIdAddr = osThreadAddr.addOffsetTo(osThreadThreadIDField.getOffset()); + + JVMDebugger debugger = VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(threadIdAddr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_ia64/LinuxIA64JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_ia64/LinuxIA64JavaThreadPDAccess.java new file mode 100644 index 00000000000..d9dc0a22478 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_ia64/LinuxIA64JavaThreadPDAccess.java @@ -0,0 +1,137 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.linux_ia64; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.ia64.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.ia64.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class LinuxIA64JavaThreadPDAccess implements JavaThreadPDAccess { + // private static AddressField lastJavaPCField; + // private static AddressField lastJavaFPField; + private static AddressField lastJavaIFrameField; + private static AddressField osThreadField; + + // Field from OSThread + private static CIntegerField osThreadPThreadIDField; + + // This is currently unneeded but is being kept in case we change + // the currentFrameGuess algorithm + private static final long GUESS_SCAN_RANGE = 128 * 1024; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + + lastJavaIFrameField = type.getAddressField("_last_Java_iframe"); + osThreadField = type.getAddressField("_osthread"); + + type = db.lookupType("OSThread"); + osThreadPThreadIDField = type.getCIntegerField("_pthread_id"); + } + + public Address getLastJavaIFrame(Address addr) { + return lastJavaIFrameField.getValue(addr); + } + + + public Address getBaseOfStackPointer(Address addr) { + return null; + } + + public Address getLastJavaFP(Address addr) { + return null; // Not in 1.4.1 + } + + public Address getLastJavaPC(Address addr) { + return null; // Not in 1.4.1 + } + + public boolean isInterpretedFrame() { + + // In 1.4.1 there are only interpreted frames + // and there is no pc + return true; + } + + public Frame getLastFramePD(JavaThread thread, Address addr) { + // The thread is the JavaThread that contains "this" + // so we don't need any new accessor at the JavaThread level + Address iframe = getLastJavaIFrame(addr); + Address pc = thread.getLastJavaPC(); + if (iframe == null) { + return null; // no information + } + return new IA64Frame(thread.getLastJavaSP(), iframe, pc); + } + + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { + return new IA64RegisterMap(thread, updateMap); + } + + public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { + return getLastFramePD(thread, addr); + } + + public void printThreadIDOn(Address addr, PrintStream tty) { + tty.print(getThreadProxy(addr)); + } + + public void printInfoOn(Address threadAddr, PrintStream tty) { + tty.print("Thread id: "); + printThreadIDOn(threadAddr, tty); + tty.println("\nLastJavaIFrame: " + getLastJavaIFrame(threadAddr)); + } + + public Address getLastSP(Address addr) { + ThreadProxy t = getThreadProxy(addr); + IA64ThreadContext context = (IA64ThreadContext) t.getContext(); + return context.getRegisterAsAddress(IA64ThreadContext.SP); + } + + public ThreadProxy getThreadProxy(Address addr) { + // Addr is the address of the JavaThread. + // Fetch the OSThread (for now and for simplicity, not making a + // separate "OSThread" class in this package) + Address osThreadAddr = osThreadField.getValue(addr); + // Get the address of the _pthread_id from the OSThread + Address pthreadIdAddr = osThreadAddr.addOffsetTo(osThreadPThreadIDField.getOffset()); + + JVMDebugger debugger = VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(pthreadIdAddr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_sparc/LinuxSPARCJavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_sparc/LinuxSPARCJavaThreadPDAccess.java new file mode 100644 index 00000000000..bced19887a9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_sparc/LinuxSPARCJavaThreadPDAccess.java @@ -0,0 +1,165 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.linux_sparc; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.sparc.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class LinuxSPARCJavaThreadPDAccess implements JavaThreadPDAccess { + private static AddressField baseOfStackPointerField; + private static AddressField postJavaStateField; + private static AddressField osThreadField; + private static int isPC; + private static int hasFlushed; + + // Field from OSThread + private static CIntegerField osThreadThreadIDField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + Type anchorType = db.lookupType("JavaFrameAnchor"); + + osThreadField = type.getAddressField("_osthread"); + hasFlushed = db.lookupIntConstant("JavaFrameAnchor::flushed").intValue(); + + type = db.lookupType("OSThread"); + osThreadThreadIDField = type.getCIntegerField("_thread_id"); + } + + public Address getLastJavaFP(Address addr) { + return null; + + } + + public Address getLastJavaPC(Address addr) { + return null; + } + + public Address getBaseOfStackPointer(Address addr) { + return baseOfStackPointerField.getValue(addr); + } + + public Frame getLastFramePD(JavaThread thread, Address addr) { + + // This assert doesn't work in the debugging case for threads + // which are running Java code and which haven't re-entered the + // runtime (e.g., through a Method.invoke() or otherwise). They + // haven't yet "decached" their last Java stack pointer to the + // thread. + + // if (Assert.ASSERTS_ENABLED) { + // Assert.that(hasLastJavaFrame(), "must have last_Java_sp() when suspended"); + // // FIXME: add assertion about flushing register windows for runtime system + // // (not appropriate for debugging system, though, unless at safepoin t) + // } + + // FIXME: I don't think this is necessary, but might be useful + // while debugging + if (thread.getLastJavaSP() == null) { + return null; + } + + // sparc does a lazy window flush. The _flags field of the JavaFrameAnchor + // encodes whether the windows have flushed. Whenever the windows have flushed + // there will be a last_Java_pc. + // In a relective system we'd have to do something to force the thread to flush + // its windows and give us the pc (or the younger_sp so we can find it ourselves) + // In a debugger situation (process or core) the flush should have happened and + // so if we don't have the younger sp we can find it + // + if (thread.getLastJavaPC() != null) { + return new SPARCFrame(SPARCFrame.biasSP(thread.getLastJavaSP()), thread.getLastJavaPC()); + } else { + Frame top = getCurrentFrameGuess(thread, addr); + return new SPARCFrame(SPARCFrame.biasSP(thread.getLastJavaSP()), + SPARCFrame.biasSP(SPARCFrame.findYoungerSP(top.getSP(), thread.getLastJavaSP())), + false); + } + + + } + + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { + return new SPARCRegisterMap(thread, updateMap); + } + + public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { + ThreadProxy t = getThreadProxy(addr); + SPARCThreadContext context = (SPARCThreadContext) t.getContext(); + // For now, let's see what happens if we do a similar thing to + // what the runtime code does. I suspect this may cause us to lose + // the top frame from the stack. + Address sp = context.getRegisterAsAddress(SPARCThreadContext.R_SP); + Address pc = context.getRegisterAsAddress(SPARCThreadContext.R_PC); + + if ((sp == null) || (pc == null)) { + // Problems (have not hit this case so far, but would be bad to continue if we did) + return null; + } + + return new SPARCFrame(sp, pc); + } + + + public void printThreadIDOn(Address addr, PrintStream tty) { + tty.print(getThreadProxy(addr)); + } + + public Address getLastSP(Address addr) { + ThreadProxy t = getThreadProxy(addr); + SPARCThreadContext context = (SPARCThreadContext) t.getContext(); + return SPARCFrame.unBiasSP(context.getRegisterAsAddress(SPARCThreadContext.R_SP)); + } + + public void printInfoOn(Address threadAddr, PrintStream tty) { + } + + public ThreadProxy getThreadProxy(Address addr) { + // Fetch the OSThread (for now and for simplicity, not making a + // separate "OSThread" class in this package) + Address osThreadAddr = osThreadField.getValue(addr); + // Get the address of the thread ID from the OSThread + Address tidAddr = osThreadAddr.addOffsetTo(osThreadThreadIDField.getOffset()); + + JVMDebugger debugger = VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(tidAddr); + } + + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_x86/LinuxSignals.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_x86/LinuxSignals.java new file mode 100644 index 00000000000..72c0fcb8459 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_x86/LinuxSignals.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.linux_x86; + +public class LinuxSignals { + private static String[] signalNames = { + "", /* No signal 0 */ + "SIGHUP", /* hangup */ + "SIGINT", /* interrupt (rubout) */ + "SIGQUIT", /* quit (ASCII FS) */ + "SIGILL", /* illegal instruction (not reset when caught) */ + "SIGTRAP", /* trace trap (not reset when caught) */ + "SIGABRT", /* used by abort, replace SIGIOT in the future */ + "SIGIOT", + "SIGBUS", + "SIGFPE", /* floating point exception */ + "SIGKILL", /* kill (cannot be caught or ignored) */ + "SIGUSR1", /* user defined signal 1 */ + "SIGSEGV", /* segmentation violation */ + "SIGUSR2", /* user defined signal 2 */ + "SIGPIPE", /* write on a pipe with no one to read it */ + "SIGALRM", /* alarm clock */ + "SIGTERM", /* software termination signal from kill */ + "SIGSTKFLT", + "SIGCHLD", /* child status change alias */ + "SIGCONT", /* stopped process has been continued */ + "SIGSTOP", /* stop (cannot be caught or ignored) */ + "SIGTSTP", /* user stop requested from tty */ + "SIGTTIN", /* background tty read attempted */ + "SIGTTOU", /* background tty write attempted */ + "SIGURG", /* urgent socket condition */ + "SIGXCPU", /* exceeded cpu limit */ + "SIGXFSZ", /* exceeded file size limit */ + "SIGVTALRM", /* virtual timer expired */ + "SIGPROF", /* profiling timer expired */ + "SIGWINCH", /* window size change */ + "SIGPOLL", /* pollable event occured */ + "SIGPWR", /* power-fail restart */ + "SIGSYS" + }; + + public static String getSignalName(int sigNum) { + if ((sigNum <= 0) || (sigNum >= signalNames.length)) { + // Probably best to fail in a non-destructive way + return ""; + } + return signalNames[sigNum]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_x86/LinuxX86JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_x86/LinuxX86JavaThreadPDAccess.java new file mode 100644 index 00000000000..8e591af479c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_x86/LinuxX86JavaThreadPDAccess.java @@ -0,0 +1,131 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.linux_x86; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.x86.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class LinuxX86JavaThreadPDAccess implements JavaThreadPDAccess { + private static AddressField lastJavaFPField; + private static AddressField osThreadField; + + // Field from OSThread + private static CIntegerField osThreadThreadIDField; + + // This is currently unneeded but is being kept in case we change + // the currentFrameGuess algorithm + private static final long GUESS_SCAN_RANGE = 128 * 1024; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + osThreadField = type.getAddressField("_osthread"); + + Type anchorType = db.lookupType("JavaFrameAnchor"); + lastJavaFPField = anchorType.getAddressField("_last_Java_fp"); + + Type osThreadType = db.lookupType("OSThread"); + osThreadThreadIDField = osThreadType.getCIntegerField("_thread_id"); + } + + public Address getLastJavaFP(Address addr) { + return lastJavaFPField.getValue(addr.addOffsetTo(sun.jvm.hotspot.runtime.JavaThread.getAnchorField().getOffset())); + } + + public Address getLastJavaPC(Address addr) { + return null; + } + + public Address getBaseOfStackPointer(Address addr) { + return null; + } + + public Frame getLastFramePD(JavaThread thread, Address addr) { + Address fp = thread.getLastJavaFP(); + if (fp == null) { + return null; // no information + } + return new X86Frame(thread.getLastJavaSP(), fp); + } + + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { + return new X86RegisterMap(thread, updateMap); + } + + public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { + ThreadProxy t = getThreadProxy(addr); + X86ThreadContext context = (X86ThreadContext) t.getContext(); + X86CurrentFrameGuess guesser = new X86CurrentFrameGuess(context, thread); + if (!guesser.run(GUESS_SCAN_RANGE)) { + return null; + } + if (guesser.getPC() == null) { + return new X86Frame(guesser.getSP(), guesser.getFP()); + } else { + return new X86Frame(guesser.getSP(), guesser.getFP(), guesser.getPC()); + } + } + + public void printThreadIDOn(Address addr, PrintStream tty) { + tty.print(getThreadProxy(addr)); + } + + public void printInfoOn(Address threadAddr, PrintStream tty) { + tty.print("Thread id: "); + printThreadIDOn(threadAddr, tty); +// tty.println("\nPostJavaState: " + getPostJavaState(threadAddr)); + } + + public Address getLastSP(Address addr) { + ThreadProxy t = getThreadProxy(addr); + X86ThreadContext context = (X86ThreadContext) t.getContext(); + return context.getRegisterAsAddress(X86ThreadContext.ESP); + } + + public ThreadProxy getThreadProxy(Address addr) { + // Addr is the address of the JavaThread. + // Fetch the OSThread (for now and for simplicity, not making a + // separate "OSThread" class in this package) + Address osThreadAddr = osThreadField.getValue(addr); + // Get the address of the _thread_id from the OSThread + Address threadIdAddr = osThreadAddr.addOffsetTo(osThreadThreadIDField.getOffset()); + + JVMDebugger debugger = VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(threadIdAddr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/posix/POSIXSignals.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/posix/POSIXSignals.java new file mode 100644 index 00000000000..ee3eecd82a8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/posix/POSIXSignals.java @@ -0,0 +1,76 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.posix; + +public class POSIXSignals { + private static String[] signalNames = { + "", /* No signal 0 */ + "SIGHUP", /* hangup */ + "SIGINT", /* interrupt (rubout) */ + "SIGQUIT", /* quit (ASCII FS) */ + "SIGILL", /* illegal instruction (not reset when caught) */ + "SIGTRAP", /* trace trap (not reset when caught) */ + "SIGABRT", /* used by abort, replace SIGIOT in the future */ + "SIGEMT", /* EMT instruction */ + "SIGFPE", /* floating point exception */ + "SIGKILL", /* kill (cannot be caught or ignored) */ + "SIGBUS", /* bus error */ + "SIGSEGV", /* segmentation violation */ + "SIGSYS", /* bad argument to system call */ + "SIGPIPE", /* write on a pipe with no one to read it */ + "SIGALRM", /* alarm clock */ + "SIGTERM", /* software termination signal from kill */ + "SIGUSR1", /* user defined signal 1 */ + "SIGUSR2", /* user defined signal 2 */ + "SIGCHLD", /* child status change alias (POSIX) */ + "SIGPWR", /* power-fail restart */ + "SIGWINCH", /* window size change */ + "SIGURG", /* urgent socket condition */ + "SIGPOLL", /* pollable event occured */ + "SIGSTOP", /* stop (cannot be caught or ignored) */ + "SIGTSTP", /* user stop requested from tty */ + "SIGCONT", /* stopped process has been continued */ + "SIGTTIN", /* background tty read attempted */ + "SIGTTOU", /* background tty write attempted */ + "SIGVTALRM", /* virtual timer expired */ + "SIGPROF", /* profiling timer expired */ + "SIGXCPU", /* exceeded cpu limit */ + "SIGXFSZ", /* exceeded file size limit */ + "SIGWAITING", /* process's lwps are blocked */ + "SIGLWP", /* special signal used by thread library */ + "SIGFREEZE", /* special signal used by CPR */ + "SIGTHAW", /* special signal used by CPR */ + "SIGCANCEL", /* thread cancellation signal used by libthread */ + "SIGLOST", /* resource lost (eg, record-lock lost) */ + }; + + public static String getSignalName(int sigNum) { + if ((sigNum <= 0) || (sigNum >= signalNames.length)) { + // Probably best to fail in a non-destructive way + return ""; + } + return signalNames[sigNum]; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/solaris_amd64/SolarisAMD64JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/solaris_amd64/SolarisAMD64JavaThreadPDAccess.java new file mode 100644 index 00000000000..b9a87544219 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/solaris_amd64/SolarisAMD64JavaThreadPDAccess.java @@ -0,0 +1,137 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.solaris_amd64; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.amd64.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class SolarisAMD64JavaThreadPDAccess implements JavaThreadPDAccess { + private static AddressField lastJavaFPField; + private static AddressField osThreadField; + private static AddressField baseOfStackPointerField; + + // Field from OSThread + private static CIntegerField osThreadThreadIDField; + + // This is currently unneeded but is being kept in case we change + // the currentFrameGuess algorithm + private static final long GUESS_SCAN_RANGE = 128 * 1024; + + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + Type anchorType = db.lookupType("JavaFrameAnchor"); + + lastJavaFPField = anchorType.getAddressField("_last_Java_fp"); + osThreadField = type.getAddressField("_osthread"); + + type = db.lookupType("OSThread"); + osThreadThreadIDField = type.getCIntegerField("_thread_id"); + } + + public Address getLastJavaFP(Address addr) { + return lastJavaFPField.getValue(addr.addOffsetTo(sun.jvm.hotspot.runtime.JavaThread.getAnchorField().getOffset())); + } + + public Address getLastJavaPC(Address addr) { + return null; + } + + public Address getBaseOfStackPointer(Address addr) { + return null; + } + + public Frame getLastFramePD(JavaThread thread, Address addr) { + Address fp = thread.getLastJavaFP(); + if (fp == null) { + return null; // no information + } + Address pc = thread.getLastJavaPC(); + if ( pc != null ) { + return new AMD64Frame(thread.getLastJavaSP(), fp, pc); + } else { + return new AMD64Frame(thread.getLastJavaSP(), fp); + } + } + + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { + return new AMD64RegisterMap(thread, updateMap); + } + + public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { + ThreadProxy t = getThreadProxy(addr); + AMD64ThreadContext context = (AMD64ThreadContext) t.getContext(); + AMD64CurrentFrameGuess guesser = new AMD64CurrentFrameGuess(context, thread); + if (!guesser.run(GUESS_SCAN_RANGE)) { + return null; + } + if (guesser.getPC() == null) { + return new AMD64Frame(guesser.getSP(), guesser.getFP()); + } else { + return new AMD64Frame(guesser.getSP(), guesser.getFP(), guesser.getPC()); + } + } + + + public void printThreadIDOn(Address addr, PrintStream tty) { + tty.print(getThreadProxy(addr)); + } + + + public void printInfoOn(Address threadAddr, PrintStream tty) { + } + + public Address getLastSP(Address addr) { + ThreadProxy t = getThreadProxy(addr); + AMD64ThreadContext context = (AMD64ThreadContext) t.getContext(); + return context.getRegisterAsAddress(AMD64ThreadContext.RSP); + } + + public ThreadProxy getThreadProxy(Address addr) { + // Fetch the OSThread (for now and for simplicity, not making a + // separate "OSThread" class in this package) + Address osThreadAddr = osThreadField.getValue(addr); + // Get the address of the thread ID from the OSThread + Address tidAddr = osThreadAddr.addOffsetTo(osThreadThreadIDField.getOffset()); + + JVMDebugger debugger = VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(tidAddr); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/solaris_sparc/SolarisSPARCJavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/solaris_sparc/SolarisSPARCJavaThreadPDAccess.java new file mode 100644 index 00000000000..7d42c211d15 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/solaris_sparc/SolarisSPARCJavaThreadPDAccess.java @@ -0,0 +1,166 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.solaris_sparc; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.sparc.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.sparc.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class SolarisSPARCJavaThreadPDAccess implements JavaThreadPDAccess { + private static AddressField baseOfStackPointerField; + private static AddressField postJavaStateField; + private static AddressField osThreadField; + private static int isPC; + private static int hasFlushed; + + // Field from OSThread + private static CIntegerField osThreadThreadIDField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + Type anchorType = db.lookupType("JavaFrameAnchor"); + + baseOfStackPointerField = type.getAddressField("_base_of_stack_pointer"); + osThreadField = type.getAddressField("_osthread"); + hasFlushed = db.lookupIntConstant("JavaFrameAnchor::flushed").intValue(); + + type = db.lookupType("OSThread"); + osThreadThreadIDField = type.getCIntegerField("_thread_id"); + } + + public Address getLastJavaFP(Address addr) { + return null; + + } + + public Address getLastJavaPC(Address addr) { + return null; + } + + public Address getBaseOfStackPointer(Address addr) { + return baseOfStackPointerField.getValue(addr); + } + + public Frame getLastFramePD(JavaThread thread, Address addr) { + + // This assert doesn't work in the debugging case for threads + // which are running Java code and which haven't re-entered the + // runtime (e.g., through a Method.invoke() or otherwise). They + // haven't yet "decached" their last Java stack pointer to the + // thread. + + // if (Assert.ASSERTS_ENABLED) { + // Assert.that(hasLastJavaFrame(), "must have last_Java_sp() when suspended"); + // // FIXME: add assertion about flushing register windows for runtime system + // // (not appropriate for debugging system, though, unless at safepoin t) + // } + + // FIXME: I don't think this is necessary, but might be useful + // while debugging + if (thread.getLastJavaSP() == null) { + return null; + } + + // sparc does a lazy window flush. The _flags field of the JavaFrameAnchor + // encodes whether the windows have flushed. Whenever the windows have flushed + // there will be a last_Java_pc. + // In a relective system we'd have to do something to force the thread to flush + // its windows and give us the pc (or the younger_sp so we can find it ourselves) + // In a debugger situation (process or core) the flush should have happened and + // so if we don't have the younger sp we can find it + // + if (thread.getLastJavaPC() != null) { + return new SPARCFrame(SPARCFrame.biasSP(thread.getLastJavaSP()), thread.getLastJavaPC()); + } else { + Frame top = getCurrentFrameGuess(thread, addr); + return new SPARCFrame(SPARCFrame.biasSP(thread.getLastJavaSP()), + SPARCFrame.biasSP(SPARCFrame.findYoungerSP(top.getSP(), thread.getLastJavaSP())), + false); + } + + + } + + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { + return new SPARCRegisterMap(thread, updateMap); + } + + public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { + ThreadProxy t = getThreadProxy(addr); + SPARCThreadContext context = (SPARCThreadContext) t.getContext(); + // For now, let's see what happens if we do a similar thing to + // what the runtime code does. I suspect this may cause us to lose + // the top frame from the stack. + Address sp = context.getRegisterAsAddress(SPARCThreadContext.R_SP); + Address pc = context.getRegisterAsAddress(SPARCThreadContext.R_PC); + + if ((sp == null) || (pc == null)) { + // Problems (have not hit this case so far, but would be bad to continue if we did) + return null; + } + + return new SPARCFrame(sp, pc); + } + + + public void printThreadIDOn(Address addr, PrintStream tty) { + tty.print(getThreadProxy(addr)); + } + + public Address getLastSP(Address addr) { + ThreadProxy t = getThreadProxy(addr); + SPARCThreadContext context = (SPARCThreadContext) t.getContext(); + return SPARCFrame.unBiasSP(context.getRegisterAsAddress(SPARCThreadContext.R_SP)); + } + + public void printInfoOn(Address threadAddr, PrintStream tty) { + } + + public ThreadProxy getThreadProxy(Address addr) { + // Fetch the OSThread (for now and for simplicity, not making a + // separate "OSThread" class in this package) + Address osThreadAddr = osThreadField.getValue(addr); + // Get the address of the thread ID from the OSThread + Address tidAddr = osThreadAddr.addOffsetTo(osThreadThreadIDField.getOffset()); + + JVMDebugger debugger = VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(tidAddr); + } + + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/solaris_x86/SolarisX86JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/solaris_x86/SolarisX86JavaThreadPDAccess.java new file mode 100644 index 00000000000..34500caa919 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/solaris_x86/SolarisX86JavaThreadPDAccess.java @@ -0,0 +1,140 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.solaris_x86; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.x86.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** Placeholder for now to allow us to start the SA without support + for stack traces */ + +public class SolarisX86JavaThreadPDAccess implements JavaThreadPDAccess { + private static AddressField lastJavaFPField; + private static AddressField osThreadField; + private static AddressField baseOfStackPointerField; + + // Field from OSThread + private static CIntegerField osThreadThreadIDField; + + // This is currently unneeded but is being kept in case we change + // the currentFrameGuess algorithm + private static final long GUESS_SCAN_RANGE = 128 * 1024; + + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + Type anchorType = db.lookupType("JavaFrameAnchor"); + + lastJavaFPField = anchorType.getAddressField("_last_Java_fp"); + osThreadField = type.getAddressField("_osthread"); + + type = db.lookupType("OSThread"); + osThreadThreadIDField = type.getCIntegerField("_thread_id"); + } + + public Address getLastJavaFP(Address addr) { + return lastJavaFPField.getValue(addr.addOffsetTo(sun.jvm.hotspot.runtime.JavaThread.getAnchorField().getOffset())); + } + + public Address getLastJavaPC(Address addr) { + return null; + } + + public Address getBaseOfStackPointer(Address addr) { + return null; + } + + public Frame getLastFramePD(JavaThread thread, Address addr) { + Address fp = thread.getLastJavaFP(); + if (fp == null) { + return null; // no information + } + Address pc = thread.getLastJavaPC(); + if ( pc != null ) { + return new X86Frame(thread.getLastJavaSP(), fp, pc); + } else { + return new X86Frame(thread.getLastJavaSP(), fp); + } + } + + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { + return new X86RegisterMap(thread, updateMap); + } + + public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { + ThreadProxy t = getThreadProxy(addr); + X86ThreadContext context = (X86ThreadContext) t.getContext(); + X86CurrentFrameGuess guesser = new X86CurrentFrameGuess(context, thread); + if (!guesser.run(GUESS_SCAN_RANGE)) { + return null; + } + if (guesser.getPC() == null) { + return new X86Frame(guesser.getSP(), guesser.getFP()); + } else { + return new X86Frame(guesser.getSP(), guesser.getFP(), guesser.getPC()); + } + } + + + public void printThreadIDOn(Address addr, PrintStream tty) { + tty.print(getThreadProxy(addr)); + } + + + public void printInfoOn(Address threadAddr, PrintStream tty) { + } + + public Address getLastSP(Address addr) { + ThreadProxy t = getThreadProxy(addr); + X86ThreadContext context = (X86ThreadContext) t.getContext(); + return context.getRegisterAsAddress(X86ThreadContext.ESP); + } + + public ThreadProxy getThreadProxy(Address addr) { + // Fetch the OSThread (for now and for simplicity, not making a + // separate "OSThread" class in this package) + Address osThreadAddr = osThreadField.getValue(addr); + // Get the address of the thread ID from the OSThread + Address tidAddr = osThreadAddr.addOffsetTo(osThreadThreadIDField.getOffset()); + + JVMDebugger debugger = VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(tidAddr); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/sparc/SPARCFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/sparc/SPARCFrame.java new file mode 100644 index 00000000000..015329117dc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/sparc/SPARCFrame.java @@ -0,0 +1,1074 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.sparc; + +import java.util.*; + +import sun.jvm.hotspot.asm.sparc.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.compiler.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.posix.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** Specialization of and implementation of abstract methods of the + Frame class for the SPARC CPU. (FIXME: this is as quick a port as + possible to get things running; will have to do a better job right + away.) */ + +public class SPARCFrame extends Frame { + // The pc value is the raw return address, plus 8 (pcReturnOffset()). + // the value of sp and youngerSP that is stored in this object + // is always, always, always the value that would be found in the + // register (or window save area) while the target VM was executing. + // The caller of the constructor will alwasy know if has a biased or + // unbiased version of the stack pointer and can convert real (unbiased) + // value via a helper routine we supply. + // Whenever we return sp or youngerSP values we do not return the internal + // value but the real (unbiased) pointers since these are the true, usable + // memory addresses. The outlier case is that of the null pointer. The current + // mechanism makes null pointers always look null whether biased or not. + // This seems to cause no problems. In theory null real pointers could be biased + // just like other values however this has impact on things like addOffsetTo() + // to be able to take an Address that represents null and add an offset to it. + // This doesn't seem worth the bother and the impact on the rest of the code + // when the biasSP and unbiasSP can make this invisible. + // + // The general rule in this code is that when we have a variable like FP, youngerSP, SP + // that these are real (i.e. unbiased) addresses. The instance variables in a Frame are + // always raw values. The other rule is that it except for the frame constructors and + // the unBiasSP helper all methods accept parameters that are real addresses. + // + + /** Optional next-younger SP (used to locate O7, the PC) */ + private Address raw_youngerSP; + + /** Intepreter adjusts the stack pointer to make all locals contiguous */ + private long interpreterSPAdjustmentOffset; + + /** Number of stack entries for longs */ + private static final int WORDS_PER_LONG = 2; + + /** Normal SPARC return is 2 words past PC */ + public static final int PC_RETURN_OFFSET = 8; + + /** Size of each block, in order of increasing address */ + public static final int REGISTER_SAVE_WORDS = 16; + // FIXME: read these from the remote process + //#ifdef _LP64 + // callee_aggregate_return_pointer_words = 0, + //#else + // callee_aggregate_return_pointer_words = 1, + //#endif + public static final int CALLEE_AGGREGATE_RETURN_POINTER_WORDS = 1; + public static final int CALLEE_REGISTER_ARGUMENT_SAVE_AREA_WORDS = 6; + + // offset of each block, in order of increasing address: + public static final int REGISTER_SAVE_WORDS_SP_OFFSET = 0; + public static final int CALLEE_AGGREGATE_RETURN_POINTER_SP_OFFSET = REGISTER_SAVE_WORDS_SP_OFFSET + REGISTER_SAVE_WORDS; + public static final int CALLEE_REGISTER_ARGUMENT_SAVE_AREA_SP_OFFSET = (CALLEE_AGGREGATE_RETURN_POINTER_SP_OFFSET + + CALLEE_AGGREGATE_RETURN_POINTER_WORDS); + public static final int MEMORY_PARAMETER_WORD_SP_OFFSET = (CALLEE_REGISTER_ARGUMENT_SAVE_AREA_SP_OFFSET + + CALLEE_REGISTER_ARGUMENT_SAVE_AREA_WORDS); + public static final int VARARGS_OFFSET = MEMORY_PARAMETER_WORD_SP_OFFSET; + + private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.sparc.SPARCFrame.DEBUG") != null; + + public static Address unBiasSP(Address raw_sp) { + if (raw_sp != null) { + return raw_sp.addOffsetTo(VM.getVM().getStackBias()); + } else { + return null; + } + } + + public static Address biasSP(Address real_sp) { + if (real_sp != null) { + if (DEBUG) { + System.out.println("biasing realsp: " + real_sp + " biased: " + real_sp.addOffsetTo(-VM.getVM().getStackBias()) ); + } + return real_sp.addOffsetTo(-VM.getVM().getStackBias()); + } else { + if (DEBUG) { + System.out.println("biasing null realsp"); + } + return null; + } + } + // + // This is used to find the younger sp for a thread thatn has stopped but hasn't + // conveniently told us the information where we can find the pc or the frame + // containing the pc that corresponds to last_java_sp. This method will walk + // the frames trying to find the frame which we contains the data we need. + // + public static Address findYoungerSP(Address top, Address find) { + // top and find are unBiased sp values + // we return an unBiased value + Address findRaw = biasSP(find); + if (top == null || find == null || findRaw == null) { + throw new RuntimeException("bad values for findYoungerSP top: " + top + " find: " + find); + } + // It would be unusual to find more than 20 native frames before we find the java frame + // we are looking for. + final int maxFrames = 20; + int count = 0; + Address search = top; + Address next; + Address pc; + if (DEBUG) { + System.out.println("findYoungerSP top: " + top + " find: " + find + " findRaw: " + findRaw); + } + while ( count != maxFrames && search != null) { + next = search.getAddressAt(SPARCRegisters.I6.spOffsetInSavedWindow()); + pc = search.getAddressAt(SPARCRegisters.I7.spOffsetInSavedWindow()); + if (DEBUG) { + System.out.println("findYoungerSP next: " + next + " pc: " + pc); + } + if (next.equals(findRaw)) { + return search; + } + search = unBiasSP(next); + } + if (DEBUG) { + System.out.println("findYoungerSP: never found younger, top: " + top + " find: " + find); + } + return null; + } + + public Address getSP() { + if (DEBUG) { + System.out.println("getSP raw: " + raw_sp + " unbiased: " + unBiasSP(raw_sp)); + } + return unBiasSP(raw_sp); + } + + public Address getID() { + return getSP(); + } + + public Address getYoungerSP() { + if (DEBUG) { + System.out.println("getYoungerSP: " + raw_youngerSP + " unbiased: " + unBiasSP(raw_youngerSP)); + } + return unBiasSP(raw_youngerSP); + } + + /** This constructor relies on the fact that the creator of a frame + has flushed register windows which the frame will refer to, and + that those register windows will not be reloaded until the frame + is done reading and writing the stack. Moreover, if the + "younger_pc" argument points into the register save area of the + next younger frame (though it need not), the register window for + that next younger frame must also stay flushed. (The caller is + responsible for ensuring this.) */ + public SPARCFrame(Address raw_sp, Address raw_youngerSP, boolean youngerFrameIsInterpreted) { + super(); + if (DEBUG) { + System.out.println("Constructing frame(1) raw_sp: " + raw_sp + " raw_youngerSP: " + raw_youngerSP); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that((unBiasSP(raw_sp).andWithMask(VM.getVM().getAddressSize() - 1) == null), + "Expected raw sp likely got real sp, value was " + raw_sp); + if (raw_youngerSP != null) { + Assert.that((unBiasSP(raw_youngerSP).andWithMask(VM.getVM().getAddressSize() - 1) == null), + "Expected raw youngerSP likely got real youngerSP, value was " + raw_youngerSP); + } + } + this.raw_sp = raw_sp; + this.raw_youngerSP = raw_youngerSP; + if (raw_youngerSP == null) { + // make a deficient frame which doesn't know where its PC is + pc = null; + } else { + Address youngerSP = unBiasSP(raw_youngerSP); + pc = youngerSP.getAddressAt(SPARCRegisters.I7.spOffsetInSavedWindow()).addOffsetTo(PC_RETURN_OFFSET); + + if (Assert.ASSERTS_ENABLED) { + Assert.that(youngerSP.getAddressAt(SPARCRegisters.FP.spOffsetInSavedWindow()). + equals(raw_sp), + "youngerSP must be valid"); + } + } + + if (youngerFrameIsInterpreted) { + long IsavedSP = SPARCRegisters.IsavedSP.spOffsetInSavedWindow(); + // compute adjustment to this frame's SP made by its interpreted callee + interpreterSPAdjustmentOffset = 0; + Address savedSP = unBiasSP(getYoungerSP().getAddressAt(IsavedSP)); + if (savedSP == null) { + if ( DEBUG) { + System.out.println("WARNING: IsavedSP was null for frame " + this); + } + } else { + interpreterSPAdjustmentOffset = savedSP.minus(getSP()); + } + } else { + interpreterSPAdjustmentOffset = 0; + } + if ( pc != null) { + // Look for a deopt pc and if it is deopted convert to original pc + CodeBlob cb = VM.getVM().getCodeCache().findBlob(pc); + if (cb != null && cb.isJavaMethod()) { + NMethod nm = (NMethod) cb; + if (pc.equals(nm.deoptBegin())) { + // adjust pc if frame is deoptimized. + pc = this.getUnextendedSP().getAddressAt(nm.origPCOffset()); + deoptimized = true; + } + } + } + } + + /** Make a deficient frame which doesn't know where its PC is (note + no youngerSP argument) */ + public SPARCFrame(Address raw_sp, Address pc) { + super(); + if (DEBUG) { + System.out.println("Constructing frame(2) raw_sp: " + raw_sp ); + } + this.raw_sp = raw_sp; + if (Assert.ASSERTS_ENABLED) { + Assert.that((unBiasSP(raw_sp).andWithMask(VM.getVM().getAddressSize() - 1) == null), + "Expected raw sp likely got real sp, value was " + raw_sp); + } + raw_youngerSP = null; + this.pc = pc; + interpreterSPAdjustmentOffset = 0; + } + + /** Only used internally */ + private SPARCFrame() { + } + + public Object clone() { + SPARCFrame frame = new SPARCFrame(); + frame.raw_sp = raw_sp; + frame.pc = pc; + frame.raw_youngerSP = raw_youngerSP; + frame.interpreterSPAdjustmentOffset = interpreterSPAdjustmentOffset; + frame.deoptimized = deoptimized; + return frame; + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof SPARCFrame)) { + return false; + } + + SPARCFrame other = (SPARCFrame) arg; + + return (AddressOps.equal(getSP(), other.getSP()) && + AddressOps.equal(getFP(), other.getFP()) && + AddressOps.equal(getPC(), other.getPC())); + } + + public int hashCode() { + if (raw_sp == null) { + return 0; + } + + return raw_sp.hashCode(); + } + + public String toString() { + Address fp = getFP(); + Address sp = getSP(); + Address youngerSP = getYoungerSP(); + + return "sp: " + (sp == null? "null" : sp.toString()) + + ", younger_sp: " + (youngerSP == null? "null" : youngerSP.toString()) + + ", fp: " + (fp == null? "null" : fp.toString()) + + ", pc: " + (pc == null? "null" : pc.toString()); + } + + /**

Identifies a signal handler frame on the stack.

+ +

There are a few different algorithms for doing this, and + they vary from platform to platform. For example, based on a + conversation with Dave Dice, Solaris/x86 will be substantially + simpler to handle than Solaris/SPARC because the signal handler + frame can be identified because of a program counter == -1.

+ +

The dbx group provided code and advice on these topics; the + code below evolved from theirs, but is not correct/robust. + Without going into too many details, it seems that looking for + the incoming argument to the sigacthandler frame (which is what + this code identifies) is not guaranteed to be stable across + versions of Solaris, since that function is supplied by + libthread and is not guaranteed not to clobber I2 before it + calls __sighndlr later. From discussions, it sounds like a + robust algorithm which wouldn't require traversal of the + ucontext chain (used by dbx, but which Dave Dice thinks isn't + robust in the face of libthread -- need to follow up) would be + to be able to properly identify the __sighndlr frame, then get + I2 and treat that as a ucontext. To identify __sighndlr we would + need to look up that symbol in the remote process and look for a + program counter within a certain (small) distance.

+ +

If the underlying Debugger supports CDebugger interface, we + take the approach of __sighnldr symbol. This approach is more robust + compared to the original hueristic approach. Of course, if there + is no CDebugger support, we fallback to the hueristic approach.

+ +

The current implementation seems to work with Solaris 2.8. + A nice property of this system is that if we find a core file + this algorithm doesn't work on, we can change the code and try + again, so I'm putting this in as the current mechanism for + finding signal handler frames on Solaris/SPARC.

*/ + public boolean isSignalHandlerFrameDbg() { + CDebugger cdbg = VM.getVM().getDebugger().getCDebugger(); + if (cdbg != null) { + LoadObject dso = cdbg.loadObjectContainingPC(getPC()); + if (dso != null) { + ClosestSymbol cs = dso.closestSymbolToPC(getPC()); + if (cs != null && cs.getName().equals("__sighndlr")) { + return true; + } else { + return false; + } + } else { + return false; + } + } else { + if (getYoungerSP() == null) { + // System.err.println(" SPARCFrame.isSignalHandlerFrameDbg: youngerSP = " + getYoungerSP()); + return false; + } + Address i2 = getSP().getAddressAt(SPARCRegisters.I2.spOffsetInSavedWindow()); + if (i2 == null) { + return false; + } + Address fp = getFP(); + // My (mistaken) understanding of the dbx group's code was that + // the signal handler frame could be identified by testing the + // incoming argument to see whether it was a certain distance + // below the frame pointer; in fact, their code did substantially + // more than this (traversal of the ucontext chain, which this + // code can't do because the topmost ucontext is not currently + // available via the proc_service APIs in dbx). The current code + // appears to work, but is probably not robust. + int MAJOR_HACK_OFFSET = 8; // Difference between expected location of the ucontext and reality + // System.err.println(" SPARCFrame.isSignalHandlerFrameDbg: I2 = " + i2 + + // ", fp = " + fp + ", raw_youngerSP = " + getYoungerSP()); + boolean res = i2.equals(fp.addOffsetTo(VM.getVM().getAddressSize() * (REGISTER_SAVE_WORDS + MAJOR_HACK_OFFSET))); + if (res) { + // Qualify this with another test (FIXME: this is a gross heuristic found while testing) + Address sigInfoAddr = getSP().getAddressAt(SPARCRegisters.I5.spOffsetInSavedWindow()); + if (sigInfoAddr == null) { + System.err.println("Frame with fp = " + fp + " looked like a signal handler frame but wasn't"); + res = false; + } + } + return res; + } + } + + public int getSignalNumberDbg() { + // From looking at the stack trace in dbx, it looks like the + // siginfo* comes into sigacthandler in I5. It would be much more + // robust to look at the __sighndlr frame instead, but we can't + // currently identify that frame. + + Address sigInfoAddr = getSP().getAddressAt(SPARCRegisters.I5.spOffsetInSavedWindow()); + // Read si_signo out of siginfo* + return (int) sigInfoAddr.getCIntegerAt(0, 4, false); + } + + public String getSignalNameDbg() { + return POSIXSignals.getSignalName(getSignalNumberDbg()); + } + + public boolean isInterpretedFrameValid() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isInterpretedFrame(), "Not an interpreted frame"); + } + // These are reasonable sanity checks + if (getFP() == null || (getFP().andWithMask(2 * VM.getVM().getAddressSize() - 1)) != null) { + return false; + } + if (getSP() == null || (getSP().andWithMask(2 * VM.getVM().getAddressSize() - 1)) != null) { + return false; + } + if (getFP().addOffsetTo(INTERPRETER_FRAME_VM_LOCAL_WORDS * VM.getVM().getAddressSize()).lessThan(getSP())) { + return false; + } + // These are hacks to keep us out of trouble. + // The problem with these is that they mask other problems + if (getFP().lessThanOrEqual(getSP())) { // this attempts to deal with unsigned comparison above + return false; + } + if (getFP().minus(getSP()) > 4096 * VM.getVM().getAddressSize()) { // stack frames shouldn't be large. + return false; + } + // FIXME: this is not atomic with respect to GC and is unsuitable + // for use in a non-debugging, or reflective, system. Need to + // figure out how to express this. + if (addressOfInterpreterFrameBCX().getAddressAt(0) == null) { + return false; // BCP not yet set up + } + return true; + } + + //-------------------------------------------------------------------------------- + // Accessors: + // + + /** Accessors */ + + public long frameSize() { + return (getSenderSP().minus(getSP()) / VM.getVM().getAddressSize()); + } + + public Address getLink() { + return unBiasSP(getFP().getAddressAt(SPARCRegisters.FP.spOffsetInSavedWindow())); + } + + // FIXME: not implementable yet + // public void setLink(Address addr) { + // if (Assert.ASSERTS_ENABLED) { + // Assert.that(getLink().equals(addr), "frame nesting is controlled by hardware"); + // } + // } + + public Frame sender(RegisterMap regMap, CodeBlob cb) { + SPARCRegisterMap map = (SPARCRegisterMap) regMap; + + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + // Default is we don't have to follow them. The sender_for_xxx + // will update it accordingly + map.setIncludeArgumentOops(false); + + if (cb == null && isEntryFrame()) { + return senderForEntryFrame(map); + } + + Address youngerSP = getSP(); + Address sp = getSenderSP(); + boolean isInterpreted = false; + + // FIXME: this is a hack to get stackwalking to work in the face + // of a signal like a SEGV. For debugging purposes it's important + // that (a) we are able to traverse the stack if we take a signal + // and (b) that we get the correct program counter in this + // situation. If we are not using alternate signal stacks then (a) + // seems to work all the time (on SPARC), but (b) is violated for + // the frame just below the signal handler. + + // The mechanism for finding the ucontext is not robust. In + // addition, we may find that we need to be able to fetch more + // registers from the ucontext than just the program counter, + // since the register windows on the stack are "stale". This will + // require substantial restructuring of this frame code, so has + // been avoided for now. + + // It is difficult to find a clean solution for mixing debugging + // situations with VM frame traversal. One could consider + // implementing generic frame traversal in the dbx style and only + // using the VM's stack walking mechanism on a per-frame basis, + // for example to traverse Java-level activations in a compiled + // frame. However, this will probably not interact well with the + // mechanism for finding oops on the stack. + + if (VM.getVM().isDebugging()) { + // If we are a signal handler frame, use a trick: make the + // youngerSP of the caller frame point to the top of the + // ucontext's contained register set. This should allow fetching + // of the registers for the frame just below the signal handler + // frame in the usual fashion. + if (isSignalHandlerFrameDbg()) { + + if (DEBUG) { + System.out.println("SPARCFrame.sender: found signal handler frame"); + } + + // Try to give a valid SP and PC for a "deficient frame" since + // we don't have a real register save area; making this class + // work by reading its information from a ucontext as well as + // a register save area is a major undertaking and has been + // deferred for now. It is very important that the PC is + // correct, which is why we don't just fall through to the + // other code (which would read the PC from the stale register + // window and thereby fail to get the actual location of the + // fault). + + long offset = getMContextAreaOffsetInUContext(); + Address fp = sp; + // System.out.println(" FP: " + fp); + fp = fp.addOffsetTo(getUContextOffset() + getMContextAreaOffsetInUContext()); + // System.out.println(" start of mcontext: " + fp); + // FIXME: put these elsewhere. These are the register numbers + // in /usr/include/sys/regset.h. They might belong in + // SPARCReigsters.java, but we currently don't have that list + // of numbers in the SA code (because all of the registers are + // listed as instances of SPARCRegister) and it appears that + // our numbering of the registers and this one don't match up. + int PC_OFFSET_IN_GREGSET = 1; + int SP_OFFSET_IN_GREGSET = 17; + raw_sp = fp.getAddressAt(VM.getVM().getAddressSize() * SP_OFFSET_IN_GREGSET); + Address pc = fp.getAddressAt(VM.getVM().getAddressSize() * PC_OFFSET_IN_GREGSET); + // System.out.println(" next frame's SP: " + sp + " PC: " + pc); + return new SPARCFrame(raw_sp, pc); + } + } + + if (!VM.getVM().isCore()) { + // Note: The version of this operation on any platform with callee-save + // registers must update the register map (if not null). + // In order to do this correctly, the various subtypes of + // of frame (interpreted, compiled, glue, native), + // must be distinguished. There is no need on SPARC for + // such distinctions, because all callee-save registers are + // preserved for all frames via SPARC-specific mechanisms. + // + // *** HOWEVER, *** if and when we make any floating-point + // registers callee-saved, then we will have to copy over + // the RegisterMap update logic from the Intel code. + + + // The constructor of the sender must know whether this frame is interpreted so it can set the + // sender's _interpreter_sp_adjustment field. + if (VM.getVM().getInterpreter().contains(pc)) { + isInterpreted = true; + if (VM.getVM().isClientCompiler()) { + map.makeIntegerRegsUnsaved(); + map.shiftWindow(sp, youngerSP); + } + } else { + // Find a CodeBlob containing this frame's pc or elide the lookup and use the + // supplied blob which is already known to be associated with this frame. + cb = VM.getVM().getCodeCache().findBlob(pc); + if (cb != null) { + + if (cb.callerMustGCArguments(map.getThread())) { + map.setIncludeArgumentOops(true); + } + + // Update the location of all implicitly saved registers + // as the address of these registers in the register save + // area (for %o registers we use the address of the %i + // register in the next younger frame) + map.shiftWindow(sp, youngerSP); + if (map.getUpdateMap()) { + if (cb.getOopMaps() != null) { + OopMapSet.updateRegisterMap(this, cb, map, VM.getVM().isDebugging()); + } + } + } + } + } // #ifndef CORE + + return new SPARCFrame(biasSP(sp), biasSP(youngerSP), isInterpreted); + } + + protected boolean hasSenderPD() { + try { + // FIXME: should not happen!!! + if (getSP() == null) { + return false; + } + if ( unBiasSP(getSP().getAddressAt(SPARCRegisters.FP.spOffsetInSavedWindow())) == null ) { + return false; + } + return true; + } catch (RuntimeException e) { + if (DEBUG) { + System.out.println("Bad frame " + this); + } + throw e; + } + } + + //-------------------------------------------------------------------------------- + // Return address: + // + + public Address getSenderPC() { + return addressOfI7().getAddressAt(0).addOffsetTo(PC_RETURN_OFFSET); + } + + // FIXME: currently unimplementable + // inline void frame::set_sender_pc(address addr) { *I7_addr() = addr - pc_return_offset; } + + public Address getUnextendedSP() { + return getSP().addOffsetTo(interpreterSPAdjustmentOffset); + } + + public Address getSenderSP() { + return getFP(); + } + + /** Given the next-younger sp for a given frame's sp, compute the + frame. We need the next-younger sp, because its register save + area holds the flushed copy of its I7, which is the PC of the + frame we are interested in. */ + public SPARCFrame afterSave() { + return new SPARCFrame(biasSP(getYoungerSP()), null); + } + + /** Accessors for the instance variables */ + public Address getFP() { + Address sp = getSP(); + if (sp == null) { + System.out.println("SPARCFrame.getFP(): sp == null"); + } + Address fpAddr = sp.addOffsetTo(SPARCRegisters.FP.spOffsetInSavedWindow()); + try { + Address fp = unBiasSP(fpAddr.getAddressAt(0)); + if (fp == null) { + System.out.println("SPARCFrame.getFP(): fp == null (&fp == " + fpAddr + ")"); + } + return fp; + } catch (RuntimeException e) { + System.out.println("SPARCFrame.getFP(): is bad (&fp == " + fpAddr + " sp = " + sp + ")"); + return null; + } + } + + private Address addressOfFPSlot(int index) { + return getFP().addOffsetTo(index * VM.getVM().getAddressSize()); + } + + // FIXME: temporarily elided + // // All frames + // + // intptr_t* fp_addr_at(int index) const { return &fp()[index]; } + // intptr_t* sp_addr_at(int index) const { return &sp()[index]; } + // intptr_t fp_at( int index) const { return *fp_addr_at(index); } + // intptr_t sp_at( int index) const { return *sp_addr_at(index); } + // + // private: + // inline address* I7_addr() const; + // inline address* O7_addr() const; + // + // inline address* I0_addr() const; + // inline address* O0_addr() const; + // + // public: + // // access to SPARC arguments and argument registers + // + // intptr_t* register_addr(Register reg) const { + // return sp_addr_at(reg.sp_offset_in_saved_window()); + // } + // intptr_t* memory_param_addr(int param_ix, bool is_in) const { + // int offset = callee_register_argument_save_area_sp_offset + param_ix; + // if (is_in) + // return fp_addr_at(offset); + // else + // return sp_addr_at(offset); + // } + // intptr_t* param_addr(int param_ix, bool is_in) const { + // if (param_ix >= callee_register_argument_save_area_words) + // return memory_param_addr(param_ix, is_in); + // else if (is_in) + // return register_addr(Argument(param_ix, true).as_register()); + // else { + // // the registers are stored in the next younger frame + // // %%% is this really necessary? + // frame next_younger = after_save(); + // return next_younger.register_addr(Argument(param_ix, true).as_register()); + // } + // } + + //-------------------------------------------------------------------------------- + // Interpreter frames: + // + + /** 2 words, also used to save float regs across calls to C */ + public static final int INTERPRETER_FRAME_D_SCRATCH_FP_OFFSET = -2; + public static final int INTERPRETER_FRAME_L_SCRATCH_FP_OFFSET = -4; + /** For native calls only */ + public static final int INTERPRETER_FRAME_PADDING_OFFSET = -5; + /** For native calls only */ + public static final int INTERPRETER_FRAME_MIRROR_OFFSET = -6; + /** Should be same as above, and should be zero mod 8 */ + public static final int INTERPRETER_FRAME_VM_LOCALS_FP_OFFSET = -6; + public static final int INTERPRETER_FRAME_VM_LOCAL_WORDS = -INTERPRETER_FRAME_VM_LOCALS_FP_OFFSET; + + /** Interpreter frame set-up needs to save 2 extra words in outgoing + param area for class and jnienv arguments for native stubs (see + nativeStubGen_sparc.cpp) */ + public static final int INTERPRETER_FRAME_EXTRA_OUTGOING_ARGUMENT_WORDS = 2; + + // FIXME: elided for now + // + // // the compiler frame has many of the same fields as the interpreter frame + // // %%%%% factor out declarations of the shared fields + // enum compiler_frame_fixed_locals { + // compiler_frame_d_scratch_fp_offset = -2, + // compiler_frame_vm_locals_fp_offset = -2, // should be same as above + // + // compiler_frame_vm_local_words = -compiler_frame_vm_locals_fp_offset + // }; + // + // private: + // + // // where LcpoolCache is saved: + // constantPoolCacheOop* interpreter_frame_cpoolcache_addr() const { + // return (constantPoolCacheOop*)sp_addr_at( LcpoolCache.sp_offset_in_saved_window()); + // } + // + // // where Lmonitors is saved: + // BasicObjectLock** interpreter_frame_monitors_addr() const { + // return (BasicObjectLock**) sp_addr_at( Lmonitors.sp_offset_in_saved_window()); + // } + // intptr_t** interpreter_frame_esp_addr() const { + // return (intptr_t**)sp_addr_at( Lesp.sp_offset_in_saved_window()); + // } + // + // inline void interpreter_frame_set_tos_address(intptr_t* x); + // + // // next two fns read and write Lmonitors value, + // private: + // BasicObjectLock* interpreter_frame_monitors() const { return *interpreter_frame_monitors_addr(); } + // void interpreter_frame_set_monitors(BasicObjectLock* monitors) { *interpreter_frame_monitors_addr() = monitors; } + // + //#ifndef CORE + //inline oop *frame::pd_compiled_argument_to_location(VMReg::Name reg, RegisterMap reg_map, int arg_size) const { + // COMPILER1_ONLY(return (oop *) (arg_size - 1 - reg + sp() + memory_parameter_word_sp_offset); ) + // COMPILER2_ONLY(return oopmapreg_to_location(reg, ®_map); ) + //} + //#endif + + // FIXME: NOT FINISHED + public Address addressOfInterpreterFrameLocals() { + return getSP().addOffsetTo(SPARCRegisters.Llocals.spOffsetInSavedWindow()); + } + + // FIXME: this is not atomic with respect to GC and is unsuitable + // for use in a non-debugging, or reflective, system. + private Address addressOfInterpreterFrameBCX() { + // %%%%% reinterpreting Lbcp as a bcx + return getSP().addOffsetTo(SPARCRegisters.Lbcp.spOffsetInSavedWindow()); + } + + public int getInterpreterFrameBCI() { + // FIXME: this is not atomic with respect to GC and is unsuitable + // for use in a non-debugging, or reflective, system. Need to + // figure out how to express this. + Address bcp = addressOfInterpreterFrameBCX().getAddressAt(0); + OopHandle methodHandle = addressOfInterpreterFrameMethod().getOopHandleAt(0); + Method method = (Method) VM.getVM().getObjectHeap().newOop(methodHandle); + return bcpToBci(bcp, method); + } + + public Address addressOfInterpreterFrameExpressionStack() { + return addressOfInterpreterFrameMonitors().addOffsetTo(-1 * VM.getVM().getAddressSize()); + } + + public int getInterpreterFrameExpressionStackDirection() { + return -1; + } + + /** Top of expression stack */ + public Address addressOfInterpreterFrameTOS() { + return getSP().getAddressAt(SPARCRegisters.Lesp.spOffsetInSavedWindow()).addOffsetTo(VM.getVM().getAddressSize()); + } + + /** Expression stack from top down */ + public Address addressOfInterpreterFrameTOSAt(int slot) { + return addressOfInterpreterFrameTOS().addOffsetTo(slot * VM.getVM().getAddressSize()); + } + + public Address getInterpreterFrameSenderSP() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isInterpretedFrame(), "interpreted frame expected"); + } + return getFP(); + } + + // FIXME: elided for now + //inline void frame::interpreter_frame_set_tos_address( intptr_t* x ) { + // *interpreter_frame_esp_addr() = x - 1; + //} + + //-------------------------------------------------------------------------------- + // Monitors: + // + + private Address addressOfInterpreterFrameMonitors() { + return getSP().addOffsetTo(SPARCRegisters.Lmonitors.spOffsetInSavedWindow()).getAddressAt(0); + } + + // Monitors + public BasicObjectLock interpreterFrameMonitorBegin() { + int roundedVMLocalWords = Bits.roundTo(INTERPRETER_FRAME_VM_LOCAL_WORDS, WORDS_PER_LONG); + return new BasicObjectLock(addressOfFPSlot(-1 * roundedVMLocalWords)); + } + + public BasicObjectLock interpreterFrameMonitorEnd() { + return new BasicObjectLock(addressOfInterpreterFrameMonitors()); + } + + public int interpreterFrameMonitorSize() { + return Bits.roundTo(BasicObjectLock.size(), WORDS_PER_LONG * (int) VM.getVM().getAddressSize()); + } + + // FIXME: elided for now + // // monitor elements + // + // // in keeping with Intel side: end is lower in memory than begin; + // // and beginning element is oldest element + // // Also begin is one past last monitor. + // + // inline BasicObjectLock* frame::interpreter_frame_monitor_begin() const { + // int rounded_vm_local_words = round_to(frame::interpreter_frame_vm_local_words, WordsPerLong); + // return (BasicObjectLock *)fp_addr_at(-rounded_vm_local_words); + // } + // + // inline BasicObjectLock* frame::interpreter_frame_monitor_end() const { + // return interpreter_frame_monitors(); + // } + // + // + // inline void frame::interpreter_frame_set_monitor_end(BasicObjectLock* value) { + // interpreter_frame_set_monitors(value); + // } + // + // + // inline int frame::interpreter_frame_monitor_size() { + // return round_to(BasicObjectLock::size(), WordsPerLong); + // } + + public Address addressOfInterpreterFrameMethod() { + return getSP().addOffsetTo(SPARCRegisters.Lmethod.spOffsetInSavedWindow()); + } + + public Address addressOfInterpreterFrameCPCache() { + return getSP().addOffsetTo(SPARCRegisters.LcpoolCache.spOffsetInSavedWindow()); + } + + //-------------------------------------------------------------------------------- + // Entry frames: + // + + public JavaCallWrapper getEntryFrameCallWrapper() { + // Note: adjust this code if the link argument in StubGenerator::call_stub() changes! + SPARCArgument link = new SPARCArgument(0, false); + return (JavaCallWrapper) VMObjectFactory.newObject(JavaCallWrapper.class, + getSP().getAddressAt(link.asIn().asRegister().spOffsetInSavedWindow())); + } + + // + // + // inline JavaCallWrapper* frame::entry_frame_call_wrapper() const { + // // note: adjust this code if the link argument in StubGenerator::call_stub() changes! + // const Argument link = Argument(0, false); + // return (JavaCallWrapper*)sp()[link.as_in().as_register().sp_offset_in_saved_window()]; + // } + + //-------------------------------------------------------------------------------- + // Safepoints: + // + + protected Address addressOfSavedOopResult() { + return addressOfO0(); + } + + protected Address addressOfSavedReceiver() { + return addressOfO0(); + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private Address addressOfI7() { + return getSP().addOffsetTo(SPARCRegisters.I7.spOffsetInSavedWindow()); + } + + private Address addressOfO7() { + return afterSave().addressOfI7(); + } + + private Address addressOfI0() { + return getSP().addOffsetTo(SPARCRegisters.I0.spOffsetInSavedWindow()); + } + + private Address addressOfO0() { + return afterSave().addressOfI0(); + } + + private static boolean addressesEqual(Address a1, Address a2) { + if ((a1 == null) && (a2 == null)) { + return true; + } + + if ((a1 == null) || (a2 == null)) { + return false; + } + + return (a1.equals(a2)); + } + + + private Frame senderForEntryFrame(RegisterMap regMap) { + SPARCRegisterMap map = (SPARCRegisterMap) regMap; + + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + // Java frame called from C; skip all C frames and return top C + // frame of that chunk as the sender + JavaCallWrapper jcw = getEntryFrameCallWrapper(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(!entryFrameIsFirst(), "next Java fp must be non zero"); + Assert.that(jcw.getLastJavaSP().greaterThan(getSP()), "must be above this frame on stack"); + } + Address lastJavaSP = jcw.getLastJavaSP(); + Address lastJavaPC = jcw.getLastJavaPC(); + map.clear(); + + if (!VM.getVM().isCore()) { + map.makeIntegerRegsUnsaved(); + map.shiftWindow(lastJavaSP, null); + } + + if (Assert.ASSERTS_ENABLED) { + Assert.that(map.getIncludeArgumentOops(), "should be set by clear"); + } + + if (lastJavaPC != null) { + return new SPARCFrame(biasSP(lastJavaSP), lastJavaPC); + } else { + Address youngerSP = getNextYoungerSP(lastJavaSP, getSP()); + return new SPARCFrame(biasSP(lastJavaSP), biasSP(youngerSP), false); + } + } + + private static Address getNextYoungerSP(Address oldSP, Address youngSP) { + Address sp = getNextYoungerSPOrNull(oldSP, youngSP, null); + if (Assert.ASSERTS_ENABLED) { + Assert.that(sp != null, "missed the SP"); + } + return sp; + } + + private static Address getNextYoungerSPOrNull(Address oldSP, Address youngSP, Address sp) { + if (youngSP == null) { + // FIXME + throw new RuntimeException("can not handle null youngSP in debugging system (seems to require register window flush)"); + } + + if (sp == null) { + sp = youngSP; + } + + Address previousSP = null; + + /** Minimum frame size is 16 */ + int maxFrames = (int) (oldSP.minus(sp) / (16 * VM.getVM().getAddressSize())); + + while(!sp.equals(oldSP) && spIsValid(oldSP, youngSP, sp)) { + if (maxFrames-- <= 0) { + // too many frames have gone by; invalid parameters given to this function + break; + } + previousSP = sp; + sp = unBiasSP(sp.getAddressAt(SPARCRegisters.FP.spOffsetInSavedWindow())); + } + + return (sp.equals(oldSP) ? previousSP : null); + } + + private static boolean spIsValid(Address oldSP, Address youngSP, Address sp) { + long mask = VM.getVM().getAddressSize(); + mask = 2 * mask - 1; + return ((sp.andWithMask(mask) == null) && + (sp.lessThanOrEqual(oldSP)) && + (sp.greaterThanOrEqual(youngSP))); + } + + // FIXME: this is a hopefully temporary hack (not sure what is going on) + public long getUContextOffset() { + // FIXME: there is something I clearly don't understand about the + // way the signal handler frame is laid out, because I shouldn't need this extra offset + int MAJOR_HACK_OFFSET = 8; + // System.out.println(" SPARCFrame.isSignalHandlerFrameDbg: I2 = " + i2 + ", fp = " + fp + ", youngerSP = " + youngerSP); + return VM.getVM().getAddressSize() * (REGISTER_SAVE_WORDS + MAJOR_HACK_OFFSET); + } + + public long getMContextAreaOffsetInUContext() { + // From dbx-related sources: + // /* + // * struct sigframe is declaredf in the kernel sources in + // * .../uts/sun4c/os/machdep.c/sendsig() + // * unfortunately we only get a pointer to the 'uc' passed + // * to the sighandler so we need to do this stuff to get + // * to 'rwin'. + // * Have to do it like this to take account of alignment. + // */ + // static struct sigframe { + // struct rwindow rwin; + // ucontext_t uc; + // } sf_help; + + // From /usr/include/sys/ucontext.h: + // #if !defined(_XPG4_2) || defined(__EXTENSIONS__) + // struct ucontext { + // #else + // struct __ucontext { + // #endif + // uint_t uc_flags; + // ucontext_t *uc_link; + // sigset_t uc_sigmask; + // stack_t uc_stack; + // mcontext_t uc_mcontext; + // #ifdef __sparcv9 + // long uc_filler[4]; + // #else /* __sparcv9 */ + // long uc_filler[23]; + // #endif /* __sparcv9 */ + // }; + + // This walks to the start of the gregs in the mcontext_t + // (first entry in that data structure). Really should read + // this from header file. + + // Also not sure exactly how alignment works...maybe should read these offsets from the target VM + // (When you have a hammer, everything looks like a nail) + long offset = VM.getVM().alignUp(4, VM.getVM().getAddressSize()); // uc_flags + offset = VM.getVM().alignUp(offset + VM.getVM().getAddressSize(), 8); // uc_link plus + // doubleword alignment for structs? + offset += 16 + // uc_sigmask + 2 * VM.getVM().getAddressSize() + 4; // uc_stack + offset = VM.getVM().alignUp(offset + VM.getVM().getAddressSize(), 8); // doubleword alignment for structs? + + // System.out.println("SPARCFrame.getMContextAreaOffsetInUContext: offset = " + offset); + + return offset; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/sparc/SPARCRegisterMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/sparc/SPARCRegisterMap.java new file mode 100644 index 00000000000..f818ecb6cff --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/sparc/SPARCRegisterMap.java @@ -0,0 +1,240 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.sparc; + +import java.util.*; + +import sun.jvm.hotspot.asm.sparc.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class SPARCRegisterMap extends RegisterMap { + /** Register window save area (for L and I regs) */ + private Address window; + /** Previous save area (for O regs, if needed) */ + private Address youngerWindow; + + private static int registerImplNumberOfRegisters; + + // Unified register numbering scheme: each 32-bits counts as a register + // number, so all the V9 registers take 2 slots. + private static int[] R_L_nums = new int[] {0+040,2+040,4+040,6+040,8+040,10+040,12+040,14+040}; + private static int[] R_I_nums = new int[] {0+060,2+060,4+060,6+060,8+060,10+060,12+060,14+060}; + private static int[] R_O_nums = new int[] {0+020,2+020,4+020,6+020,8+020,10+020,12+020,14+020}; + private static int[] R_G_nums = new int[] {0+000,2+000,4+000,6+000,8+000,10+000,12+000,14+000}; + + private static long badMask; + private static long R_LIO_mask; + + private static int sizeofJint; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + badMask = 0; + R_LIO_mask = 0; + + sizeofJint = (int) db.lookupType("jint").getSize(); + registerImplNumberOfRegisters = db.lookupIntConstant("RegisterImpl::number_of_registers").intValue(); + + for (int i = 0; i < 8; i++) { + Assert.that(R_L_nums[i] < locationValidTypeSize, "in first chunk"); + Assert.that(R_I_nums[i] < locationValidTypeSize, "in first chunk"); + Assert.that(R_O_nums[i] < locationValidTypeSize, "in first chunk"); + Assert.that(R_G_nums[i] < locationValidTypeSize, "in first chunk"); + } + + badMask |= ((long) 1 << R_O_nums[6]); // SP + badMask |= ((long) 1 << R_O_nums[7]); // cPC + badMask |= ((long) 1 << R_I_nums[6]); // FP + badMask |= ((long) 1 << R_I_nums[7]); // rPC + badMask |= ((long) 1 << R_G_nums[2]); // TLS + badMask |= ((long) 1 << R_G_nums[7]); // reserved by libthread + + for (int i = 0; i < 8; i++) { + R_LIO_mask |= ((long) 1 << R_L_nums[i]); + R_LIO_mask |= ((long) 1 << R_I_nums[i]); + R_LIO_mask |= ((long) 1 << R_O_nums[i]); + } + } + + /** This is the only public constructor, and is only called by + SolarisSPARCJavaThread */ + public SPARCRegisterMap(JavaThread thread, boolean updateMap) { + super(thread, updateMap); + } + + protected SPARCRegisterMap(RegisterMap map) { + super(map); + } + + public Object clone() { + SPARCRegisterMap retval = new SPARCRegisterMap(this); + return retval; + } + + protected void clearPD() { + if (thread.hasLastJavaFrame()) { + Frame fr = thread.getLastFrame(); + window = fr.getSP(); + } else { + window = null; + if (VM.getVM().isDebugging()) { + Frame fr = thread.getCurrentFrameGuess(); + if (fr != null) { + window = fr.getSP(); + } + } + } + youngerWindow = null; + } + + protected Address getLocationPD(VMReg vmreg) { + VM vm = VM.getVM(); + int regname = vmreg.getValue(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(0 <= regname && regname < regCount, "sanity check"); + } + + // Only the GPRs get handled this way + if (regname >= (registerImplNumberOfRegisters << 1)) { + return null; + } + + // don't talk about bad registers + if ((badMask & ((long) 1 << regname)) != 0) { + return null; + } + + // Convert to a GPR + int secondWord = 0; + // 32-bit registers for in, out and local + if (!isEven(regname)) { + if (vm.isLP64()) { + secondWord = sizeofJint; + } else { + return null; + } + } + + SPARCRegister reg = new SPARCRegister(regname >> 1); + if (reg.isOut()) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(youngerWindow != null, "Younger window should be available"); + } + return youngerWindow.addOffsetTo(reg.afterSave().spOffsetInSavedWindow() + secondWord); + } + if (reg.isLocal() || reg.isIn()) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(window != null, "Window should be available"); + } + return window.addOffsetTo(reg.spOffsetInSavedWindow() + secondWord); + } + + // Only the window'd GPRs get handled this way; not the globals. + return null; + } + + protected void initializePD() { + window = null; + youngerWindow = null; + // avoid the shift_individual_registers game + makeIntegerRegsUnsaved(); + } + + protected void initializeFromPD(RegisterMap map) { + SPARCRegisterMap srm = (SPARCRegisterMap) map; + window = srm.window; + youngerWindow = srm.youngerWindow; + // avoid the shift_individual_registers game + makeIntegerRegsUnsaved(); + } + + public void shiftWindow(Address sp, Address youngerSP) { + window = sp; + youngerWindow = youngerSP; + // Throw away locations for %i, %o, and %l registers: + // But do not throw away %g register locs. + if (locationValid[0] != 0) { + shiftIndividualRegisters(); + } + } + + /** When popping out of compiled frames, we make all IRegs disappear. */ + public void makeIntegerRegsUnsaved() { + locationValid[0] = 0; + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private void shiftIndividualRegisters() { + if (!getUpdateMap()) { + return; + } + + checkLocationValid(); + + long lv = locationValid[0]; + long lv0 = lv; + + lv &= ~R_LIO_mask; // clear %l, %o, %i regs + + // if we cleared some non-%g locations, we may have to do some shifting + if (lv != lv0) { + // copy %i0-%i5 to %o0-%o5, if they have special locations + // This can happen in within stubs which spill argument registers + // around a dynamic link operation, such as resolve_opt_virtual_call. + for (int i = 0; i < 8; i++) { + if ((lv0 & ((long) 1 << R_I_nums[i])) != 0) { + location[R_O_nums[i]] = location[R_I_nums[i]]; + lv |= ((long) 1 << R_O_nums[i]); + } + } + } + + locationValid[0] = lv; + checkLocationValid(); + } + + private boolean isEven(int i) { + return (i & 1) == 0; + } + + private void checkLocationValid() { + if (Assert.ASSERTS_ENABLED) { + Assert.that((locationValid[0] & badMask) == 0, "cannot have special locations for SP,FP,TLS,etc."); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/win32_amd64/Win32AMD64JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/win32_amd64/Win32AMD64JavaThreadPDAccess.java new file mode 100644 index 00000000000..7cfe8f188f2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/win32_amd64/Win32AMD64JavaThreadPDAccess.java @@ -0,0 +1,137 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.win32_amd64; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.win32.*; +import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.amd64.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** This class is only public to allow using the VMObjectFactory to + instantiate these. +*/ + +public class Win32AMD64JavaThreadPDAccess implements JavaThreadPDAccess { + private static AddressField lastJavaFPField; + private static AddressField osThreadField; + + // Field from OSThread + private static Field osThreadThreadHandleField; + + // This is currently unneeded but is being kept in case we change + // the currentFrameGuess algorithm + private static final long GUESS_SCAN_RANGE = 128 * 1024; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + Type anchorType = db.lookupType("JavaFrameAnchor"); + lastJavaFPField = anchorType.getAddressField("_last_Java_fp"); + osThreadField = type.getAddressField("_osthread"); + + type = db.lookupType("OSThread"); + osThreadThreadHandleField = type.getField("_thread_handle"); + } + + public Address getLastJavaFP(Address addr) { + return lastJavaFPField.getValue(addr.addOffsetTo(sun.jvm.hotspot.runtime.JavaThread.getAnchorField().getOffset())); + } + + public Address getLastJavaPC(Address addr) { + return null; + } + + public Address getBaseOfStackPointer(Address addr) { + return null; + } + + public Frame getLastFramePD(JavaThread thread, Address addr) { + Address fp = thread.getLastJavaFP(); + if (fp == null) { + return null; // no information + } + Address pc = thread.getLastJavaPC(); + if ( pc != null ) { + return new AMD64Frame(thread.getLastJavaSP(), fp, pc); + } else { + return new AMD64Frame(thread.getLastJavaSP(), fp); + } + } + + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { + return new AMD64RegisterMap(thread, updateMap); + } + + public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { + ThreadProxy t = getThreadProxy(addr); + AMD64ThreadContext context = (AMD64ThreadContext) t.getContext(); + AMD64CurrentFrameGuess guesser = new AMD64CurrentFrameGuess(context, thread); + if (!guesser.run(GUESS_SCAN_RANGE)) { + return null; + } + if (guesser.getPC() == null) { + return new AMD64Frame(guesser.getSP(), guesser.getFP()); + } else { + return new AMD64Frame(guesser.getSP(), guesser.getFP(), guesser.getPC()); + } + } + + public void printThreadIDOn(Address addr, PrintStream tty) { + tty.print(getThreadProxy(addr)); + } + + public void printInfoOn(Address threadAddr, PrintStream tty) { + } + + public Address getLastSP(Address addr) { + ThreadProxy t = getThreadProxy(addr); + AMD64ThreadContext context = (AMD64ThreadContext) t.getContext(); + return context.getRegisterAsAddress(AMD64ThreadContext.RSP); + } + + public ThreadProxy getThreadProxy(Address addr) { + // Addr is the address of the JavaThread. + // Fetch the OSThread (for now and for simplicity, not making a + // separate "OSThread" class in this package) + Address osThreadAddr = osThreadField.getValue(addr); + // Get the address of the HANDLE within the OSThread + Address threadHandleAddr = + osThreadAddr.addOffsetTo(osThreadThreadHandleField.getOffset()); + JVMDebugger debugger = VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(threadHandleAddr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/win32_ia64/Win32IA64JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/win32_ia64/Win32IA64JavaThreadPDAccess.java new file mode 100644 index 00000000000..dcdfbd4afc5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/win32_ia64/Win32IA64JavaThreadPDAccess.java @@ -0,0 +1,137 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.win32_ia64; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.ia64.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.ia64.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class Win32IA64JavaThreadPDAccess implements JavaThreadPDAccess { + // private static AddressField lastJavaPCField; + // private static AddressField lastJavaFPField; + private static AddressField lastJavaIFrameField; + private static AddressField osThreadField; + + // Field from OSThread + private static CIntegerField osThreadPThreadIDField; + + // This is currently unneeded but is being kept in case we change + // the currentFrameGuess algorithm + private static final long GUESS_SCAN_RANGE = 128 * 1024; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + + lastJavaIFrameField = type.getAddressField("_last_Java_iframe"); + osThreadField = type.getAddressField("_osthread"); + + type = db.lookupType("OSThread"); + osThreadPThreadIDField = type.getCIntegerField("_pthread_id"); + } + + public Address getLastJavaIFrame(Address addr) { + return lastJavaIFrameField.getValue(addr); + } + + + public Address getBaseOfStackPointer(Address addr) { + return null; + } + + public Address getLastJavaFP(Address addr) { + return null; // Not in 1.4.1 + } + + public Address getLastJavaPC(Address addr) { + return null; // Not in 1.4.1 + } + + public boolean isInterpretedFrame() { + + // In 1.4.1 there are only interpreted frames + // and there is no pc + return true; + } + + public Frame getLastFramePD(JavaThread thread, Address addr) { + // The thread is the JavaThread that contains "this" + // so we don't need any new accessor at the JavaThread level + Address iframe = getLastJavaIFrame(addr); + Address pc = thread.getLastJavaPC(); + if (iframe == null) { + return null; // no information + } + return new IA64Frame(thread.getLastJavaSP(), iframe, pc); + } + + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { + return new IA64RegisterMap(thread, updateMap); + } + + public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { + return getLastFramePD(thread, addr); + } + + public void printThreadIDOn(Address addr, PrintStream tty) { + tty.print(getThreadProxy(addr)); + } + + public void printInfoOn(Address threadAddr, PrintStream tty) { + tty.print("Thread id: "); + printThreadIDOn(threadAddr, tty); + tty.println("\nLastJavaIFrame: " + getLastJavaIFrame(threadAddr)); + } + + public Address getLastSP(Address addr) { + ThreadProxy t = getThreadProxy(addr); + IA64ThreadContext context = (IA64ThreadContext) t.getContext(); + return context.getRegisterAsAddress(IA64ThreadContext.SP); + } + + public ThreadProxy getThreadProxy(Address addr) { + // Addr is the address of the JavaThread. + // Fetch the OSThread (for now and for simplicity, not making a + // separate "OSThread" class in this package) + Address osThreadAddr = osThreadField.getValue(addr); + // Get the address of the _pthread_id from the OSThread + Address pthreadIdAddr = osThreadAddr.addOffsetTo(osThreadPThreadIDField.getOffset()); + + JVMDebugger debugger = VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(pthreadIdAddr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/win32_x86/Win32X86JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/win32_x86/Win32X86JavaThreadPDAccess.java new file mode 100644 index 00000000000..52b2359ccbf --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/win32_x86/Win32X86JavaThreadPDAccess.java @@ -0,0 +1,137 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.win32_x86; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.win32.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.x86.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** This class is only public to allow using the VMObjectFactory to + instantiate these. +*/ + +public class Win32X86JavaThreadPDAccess implements JavaThreadPDAccess { + private static AddressField lastJavaFPField; + private static AddressField osThreadField; + + // Field from OSThread + private static Field osThreadThreadHandleField; + + // This is currently unneeded but is being kept in case we change + // the currentFrameGuess algorithm + private static final long GUESS_SCAN_RANGE = 128 * 1024; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + Type anchorType = db.lookupType("JavaFrameAnchor"); + lastJavaFPField = anchorType.getAddressField("_last_Java_fp"); + osThreadField = type.getAddressField("_osthread"); + + type = db.lookupType("OSThread"); + osThreadThreadHandleField = type.getField("_thread_handle"); + } + + public Address getLastJavaFP(Address addr) { + return lastJavaFPField.getValue(addr.addOffsetTo(sun.jvm.hotspot.runtime.JavaThread.getAnchorField().getOffset())); + } + + public Address getLastJavaPC(Address addr) { + return null; + } + + public Address getBaseOfStackPointer(Address addr) { + return null; + } + + public Frame getLastFramePD(JavaThread thread, Address addr) { + Address fp = thread.getLastJavaFP(); + if (fp == null) { + return null; // no information + } + Address pc = thread.getLastJavaPC(); + if ( pc != null ) { + return new X86Frame(thread.getLastJavaSP(), fp, pc); + } else { + return new X86Frame(thread.getLastJavaSP(), fp); + } + } + + public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { + return new X86RegisterMap(thread, updateMap); + } + + public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { + ThreadProxy t = getThreadProxy(addr); + X86ThreadContext context = (X86ThreadContext) t.getContext(); + X86CurrentFrameGuess guesser = new X86CurrentFrameGuess(context, thread); + if (!guesser.run(GUESS_SCAN_RANGE)) { + return null; + } + if (guesser.getPC() == null) { + return new X86Frame(guesser.getSP(), guesser.getFP()); + } else { + return new X86Frame(guesser.getSP(), guesser.getFP(), guesser.getPC()); + } + } + + public void printThreadIDOn(Address addr, PrintStream tty) { + tty.print(getThreadProxy(addr)); + } + + public void printInfoOn(Address threadAddr, PrintStream tty) { + } + + public Address getLastSP(Address addr) { + ThreadProxy t = getThreadProxy(addr); + X86ThreadContext context = (X86ThreadContext) t.getContext(); + return context.getRegisterAsAddress(X86ThreadContext.ESP); + } + + public ThreadProxy getThreadProxy(Address addr) { + // Addr is the address of the JavaThread. + // Fetch the OSThread (for now and for simplicity, not making a + // separate "OSThread" class in this package) + Address osThreadAddr = osThreadField.getValue(addr); + // Get the address of the HANDLE within the OSThread + Address threadHandleAddr = + osThreadAddr.addOffsetTo(osThreadThreadHandleField.getOffset()); + JVMDebugger debugger = VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(threadHandleAddr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86CurrentFrameGuess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86CurrentFrameGuess.java new file mode 100644 index 00000000000..f5e1edbc647 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86CurrentFrameGuess.java @@ -0,0 +1,216 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.x86; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.x86.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.runtime.*; + +/**

Should be able to be used on all x86 platforms we support + (Win32, Solaris/x86, and soon Linux) to implement JavaThread's + "currentFrameGuess()" functionality. Input is an X86ThreadContext; + output is SP, FP, and PC for an X86Frame. Instantiation of the + X86Frame is left to the caller, since we may need to subclass + X86Frame to support signal handler frames on Unix platforms.

+ +

Algorithm is to walk up the stack within a given range (say, + 512K at most) looking for a plausible PC and SP for a Java frame, + also considering those coming in from the context. If we find a PC + that belongs to the VM (i.e., in generated code like the + interpreter or CodeCache) then we try to find an associated EBP. + We repeat this until we either find a complete frame or run out of + stack to look at.

*/ + +public class X86CurrentFrameGuess { + private X86ThreadContext context; + private JavaThread thread; + private Address spFound; + private Address fpFound; + private Address pcFound; + + private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.x86.X86Frame.DEBUG") + != null; + + public X86CurrentFrameGuess(X86ThreadContext context, + JavaThread thread) { + this.context = context; + this.thread = thread; + } + + /** Returns false if not able to find a frame within a reasonable range. */ + public boolean run(long regionInBytesToSearch) { + Address sp = context.getRegisterAsAddress(X86ThreadContext.SP); + Address pc = context.getRegisterAsAddress(X86ThreadContext.PC); + Address fp = context.getRegisterAsAddress(X86ThreadContext.FP); + if (sp == null) { + // Bail out if no last java frame eithe + if (thread.getLastJavaSP() != null) { + setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); + return true; + } + // Bail out + return false; + } + Address end = sp.addOffsetTo(regionInBytesToSearch); + VM vm = VM.getVM(); + + setValues(null, null, null); // Assume we're not going to find anything + + if (vm.isJavaPCDbg(pc)) { + if (vm.isClientCompiler()) { + // If the topmost frame is a Java frame, we are (pretty much) + // guaranteed to have a viable EBP. We should be more robust + // than this (we have the potential for losing entire threads' + // stack traces) but need to see how much work we really have + // to do here. Searching the stack for an (SP, FP) pair is + // hard since it's easy to misinterpret inter-frame stack + // pointers as base-of-frame pointers; we also don't know the + // sizes of C1 frames (not registered in the nmethod) so can't + // derive them from ESP. + + setValues(sp, fp, pc); + return true; + } else { + if (vm.getInterpreter().contains(pc)) { + if (DEBUG) { + System.out.println("CurrentFrameGuess: choosing interpreter frame: sp = " + + sp + ", fp = " + fp + ", pc = " + pc); + } + setValues(sp, fp, pc); + return true; + } + + // For the server compiler, EBP is not guaranteed to be valid + // for compiled code. In addition, an earlier attempt at a + // non-searching algorithm (see below) failed because the + // stack pointer from the thread context was pointing + // (considerably) beyond the ostensible end of the stack, into + // garbage; walking from the topmost frame back caused a crash. + // + // This algorithm takes the current PC as a given and tries to + // find the correct corresponding SP by walking up the stack + // and repeatedly performing stackwalks (very inefficient). + // + // FIXME: there is something wrong with stackwalking across + // adapter frames...this is likely to be the root cause of the + // failure with the simpler algorithm below. + + for (long offset = 0; + offset < regionInBytesToSearch; + offset += vm.getAddressSize()) { + try { + Address curSP = sp.addOffsetTo(offset); + Frame frame = new X86Frame(curSP, null, pc); + RegisterMap map = thread.newRegisterMap(false); + while (frame != null) { + if (frame.isEntryFrame() && frame.entryFrameIsFirst()) { + // We were able to traverse all the way to the + // bottommost Java frame. + // This sp looks good. Keep it. + if (DEBUG) { + System.out.println("CurrentFrameGuess: Choosing sp = " + curSP + ", pc = " + pc); + } + setValues(curSP, null, pc); + return true; + } + frame = frame.sender(map); + } + } catch (Exception e) { + if (DEBUG) { + System.out.println("CurrentFrameGuess: Exception " + e + " at offset " + offset); + } + // Bad SP. Try another. + } + } + + // Were not able to find a plausible SP to go with this PC. + // Bail out. + return false; + + /* + // Original algorithm which does not work because SP was + // pointing beyond where it should have: + + // For the server compiler, EBP is not guaranteed to be valid + // for compiled code. We see whether the PC is in the + // interpreter and take care of that, otherwise we run code + // (unfortunately) duplicated from X86Frame.senderForCompiledFrame. + + CodeCache cc = vm.getCodeCache(); + if (cc.contains(pc)) { + CodeBlob cb = cc.findBlob(pc); + + // See if we can derive a frame pointer from SP and PC + // NOTE: This is the code duplicated from X86Frame + Address saved_fp = null; + int llink_offset = cb.getLinkOffset(); + if (llink_offset >= 0) { + // Restore base-pointer, since next frame might be an interpreter frame. + Address fp_addr = sp.addOffsetTo(VM.getVM().getAddressSize() * llink_offset); + saved_fp = fp_addr.getAddressAt(0); + } + + setValues(sp, saved_fp, pc); + return true; + } + */ + } + } else { + // If the current program counter was not known to us as a Java + // PC, we currently assume that we are in the run-time system + // and attempt to look to thread-local storage for saved ESP and + // EBP. Note that if these are null (because we were, in fact, + // in Java code, i.e., vtable stubs or similar, and the SA + // didn't have enough insight into the target VM to understand + // that) then we are going to lose the entire stack trace for + // the thread, which is sub-optimal. FIXME. + + if (DEBUG) { + System.out.println("CurrentFrameGuess: choosing last Java frame: sp = " + + thread.getLastJavaSP() + ", fp = " + thread.getLastJavaFP()); + } + if (thread.getLastJavaSP() == null) { + return false; // No known Java frames on stack + } + setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); + return true; + } + } + + public Address getSP() { return spFound; } + public Address getFP() { return fpFound; } + /** May be null if getting values from thread-local storage; take + care to call the correct X86Frame constructor to recover this if + necessary */ + public Address getPC() { return pcFound; } + + private void setValues(Address sp, Address fp, Address pc) { + spFound = sp; + fpFound = fp; + pcFound = pc; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java new file mode 100644 index 00000000000..77126e1b08b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java @@ -0,0 +1,527 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.x86; + +import java.util.*; +import sun.jvm.hotspot.asm.x86.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.compiler.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** Specialization of and implementation of abstract methods of the + Frame class for the x86 family of CPUs. */ + +public class X86Frame extends Frame { + private static final boolean DEBUG; + static { + DEBUG = System.getProperty("sun.jvm.hotspot.runtime.x86.X86Frame.DEBUG") != null; + } + + // All frames + private static final int LINK_OFFSET = 0; + private static final int RETURN_ADDR_OFFSET = 1; + private static final int SENDER_SP_OFFSET = 2; + + // Interpreter frames + private static final int INTERPRETER_FRAME_MIRROR_OFFSET = 2; // for native calls only + private static final int INTERPRETER_FRAME_SENDER_SP_OFFSET = -1; + private static final int INTERPRETER_FRAME_LAST_SP_OFFSET = INTERPRETER_FRAME_SENDER_SP_OFFSET - 1; + private static final int INTERPRETER_FRAME_METHOD_OFFSET = INTERPRETER_FRAME_LAST_SP_OFFSET - 1; + private static int INTERPRETER_FRAME_MDX_OFFSET; // Non-core builds only + private static int INTERPRETER_FRAME_CACHE_OFFSET; + private static int INTERPRETER_FRAME_LOCALS_OFFSET; + private static int INTERPRETER_FRAME_BCX_OFFSET; + private static int INTERPRETER_FRAME_INITIAL_SP_OFFSET; + private static int INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET; + private static int INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET; + + // Entry frames + private static final int ENTRY_FRAME_CALL_WRAPPER_OFFSET = 2; + + // Native frames + private static final int NATIVE_FRAME_INITIAL_PARAM_OFFSET = 2; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + if (VM.getVM().isCore()) { + INTERPRETER_FRAME_CACHE_OFFSET = INTERPRETER_FRAME_METHOD_OFFSET - 1; + } else { + INTERPRETER_FRAME_MDX_OFFSET = INTERPRETER_FRAME_METHOD_OFFSET - 1; + INTERPRETER_FRAME_CACHE_OFFSET = INTERPRETER_FRAME_MDX_OFFSET - 1; + } + INTERPRETER_FRAME_LOCALS_OFFSET = INTERPRETER_FRAME_CACHE_OFFSET - 1; + INTERPRETER_FRAME_BCX_OFFSET = INTERPRETER_FRAME_LOCALS_OFFSET - 1; + INTERPRETER_FRAME_INITIAL_SP_OFFSET = INTERPRETER_FRAME_BCX_OFFSET - 1; + INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET; + INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET; + } + + // an additional field beyond sp and pc: + Address raw_fp; // frame pointer + private Address raw_unextendedSP; + + private X86Frame() { + } + + private void adjustForDeopt() { + if ( pc != null) { + // Look for a deopt pc and if it is deopted convert to original pc + CodeBlob cb = VM.getVM().getCodeCache().findBlob(pc); + if (cb != null && cb.isJavaMethod()) { + NMethod nm = (NMethod) cb; + if (pc.equals(nm.deoptBegin())) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(this.getUnextendedSP() != null, "null SP in Java frame"); + } + // adjust pc if frame is deoptimized. + pc = this.getUnextendedSP().getAddressAt(nm.origPCOffset()); + deoptimized = true; + } + } + } + } + + public X86Frame(Address raw_sp, Address raw_fp, Address pc) { + this.raw_sp = raw_sp; + this.raw_unextendedSP = raw_sp; + this.raw_fp = raw_fp; + this.pc = pc; + + // Frame must be fully constructed before this call + adjustForDeopt(); + + if (DEBUG) { + System.out.println("X86Frame(sp, fp, pc): " + this); + dumpStack(); + } + } + + public X86Frame(Address raw_sp, Address raw_fp) { + this.raw_sp = raw_sp; + this.raw_unextendedSP = raw_sp; + this.raw_fp = raw_fp; + this.pc = raw_sp.getAddressAt(-1 * VM.getVM().getAddressSize()); + + // Frame must be fully constructed before this call + adjustForDeopt(); + + if (DEBUG) { + System.out.println("X86Frame(sp, fp): " + this); + dumpStack(); + } + } + + // This constructor should really take the unextended SP as an arg + // but then the constructor is ambiguous with constructor that takes + // a PC so take an int and convert it. + public X86Frame(Address raw_sp, Address raw_fp, long extension) { + this.raw_sp = raw_sp; + if (raw_sp == null) { + this.raw_unextendedSP = null; + } else { + this.raw_unextendedSP = raw_sp.addOffsetTo(extension); + } + this.raw_fp = raw_fp; + this.pc = raw_sp.getAddressAt(-1 * VM.getVM().getAddressSize()); + + // Frame must be fully constructed before this call + adjustForDeopt(); + + if (DEBUG) { + System.out.println("X86Frame(sp, fp): " + this); + dumpStack(); + } + + } + + public Object clone() { + X86Frame frame = new X86Frame(); + frame.raw_sp = raw_sp; + frame.raw_unextendedSP = raw_unextendedSP; + frame.raw_fp = raw_fp; + frame.raw_fp = raw_fp; + frame.pc = pc; + frame.deoptimized = deoptimized; + return frame; + } + + public boolean equals(Object arg) { + if (arg == null) { + return false; + } + + if (!(arg instanceof X86Frame)) { + return false; + } + + X86Frame other = (X86Frame) arg; + + return (AddressOps.equal(getSP(), other.getSP()) && + AddressOps.equal(getUnextendedSP(), other.getUnextendedSP()) && + AddressOps.equal(getFP(), other.getFP()) && + AddressOps.equal(getPC(), other.getPC())); + } + + public int hashCode() { + if (raw_sp == null) { + return 0; + } + + return raw_sp.hashCode(); + } + + public String toString() { + return "sp: " + (getSP() == null? "null" : getSP().toString()) + + ", unextendedSP: " + (getUnextendedSP() == null? "null" : getUnextendedSP().toString()) + + ", fp: " + (getFP() == null? "null" : getFP().toString()) + + ", pc: " + (pc == null? "null" : pc.toString()); + } + + // accessors for the instance variables + public Address getFP() { return raw_fp; } + public Address getSP() { return raw_sp; } + public Address getID() { return raw_sp; } + + // FIXME: not implemented yet (should be done for Solaris/X86) + public boolean isSignalHandlerFrameDbg() { return false; } + public int getSignalNumberDbg() { return 0; } + public String getSignalNameDbg() { return null; } + + public boolean isInterpretedFrameValid() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isInterpretedFrame(), "Not an interpreted frame"); + } + + // These are reasonable sanity checks + if (getFP() == null || getFP().andWithMask(0x3) != null) { + return false; + } + + if (getSP() == null || getSP().andWithMask(0x3) != null) { + return false; + } + + if (getFP().addOffsetTo(INTERPRETER_FRAME_INITIAL_SP_OFFSET * VM.getVM().getAddressSize()).lessThan(getSP())) { + return false; + } + + // These are hacks to keep us out of trouble. + // The problem with these is that they mask other problems + if (getFP().lessThanOrEqual(getSP())) { + // this attempts to deal with unsigned comparison above + return false; + } + + if (getFP().minus(getSP()) > 4096 * VM.getVM().getAddressSize()) { + // stack frames shouldn't be large. + return false; + } + + return true; + } + + // FIXME: not applicable in current system + // void patch_pc(Thread* thread, address pc); + + public Frame sender(RegisterMap regMap, CodeBlob cb) { + X86RegisterMap map = (X86RegisterMap) regMap; + + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + // Default is we done have to follow them. The sender_for_xxx will + // update it accordingly + map.setIncludeArgumentOops(false); + + if (isEntryFrame()) return senderForEntryFrame(map); + if (isInterpretedFrame()) return senderForInterpreterFrame(map); + + if (!VM.getVM().isCore()) { + if(cb == null) { + cb = VM.getVM().getCodeCache().findBlob(getPC()); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb.equals(VM.getVM().getCodeCache().findBlob(getPC())), "Must be the same"); + } + } + + if (cb != null) { + return senderForCompiledFrame(map, cb); + } + } + + // Must be native-compiled frame, i.e. the marshaling code for native + // methods that exists in the core system. + return new X86Frame(getSenderSP(), getLink(), getSenderPC()); + } + + private Frame senderForEntryFrame(X86RegisterMap map) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + // Java frame called from C; skip all C frames and return top C + // frame of that chunk as the sender + X86JavaCallWrapper jcw = (X86JavaCallWrapper) getEntryFrameCallWrapper(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(!entryFrameIsFirst(), "next Java fp must be non zero"); + Assert.that(jcw.getLastJavaSP().greaterThan(getSP()), "must be above this frame on stack"); + } + X86Frame fr; + if (jcw.getLastJavaPC() != null) { + fr = new X86Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP(), jcw.getLastJavaPC()); + } else { + fr = new X86Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP()); + } + map.clear(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(map.getIncludeArgumentOops(), "should be set by clear"); + } + return fr; + } + + private Frame senderForInterpreterFrame(X86RegisterMap map) { + Address unextendedSP = addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0); + Address sp = addressOfStackSlot(SENDER_SP_OFFSET); + // We do not need to update the callee-save register mapping because above + // us is either another interpreter frame or a converter-frame, but never + // directly a compiled frame. + // 11/24/04 SFG. With the removal of adapter frames this is no longer true. + // However c2 no longer uses callee save register for java calls so there + // are no callee register to find. + + return new X86Frame(sp, getLink(), unextendedSP.minus(sp)); + } + + private Frame senderForCompiledFrame(X86RegisterMap map, CodeBlob cb) { + // + // NOTE: some of this code is (unfortunately) duplicated in X86CurrentFrameGuess + // + + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + // frame owned by optimizing compiler + Address sender_sp = null; + + if (VM.getVM().isClientCompiler()) { + sender_sp = addressOfStackSlot(SENDER_SP_OFFSET); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb.getFrameSize() >= 0, "Compiled by Compiler1: do not use"); + } + sender_sp = getUnextendedSP().addOffsetTo(cb.getFrameSize()); + } + + // On Intel the return_address is always the word on the stack + Address sender_pc = sender_sp.getAddressAt(-1 * VM.getVM().getAddressSize()); + + if (map.getUpdateMap() && cb.getOopMaps() != null) { + OopMapSet.updateRegisterMap(this, cb, map, true); + } + + if (VM.getVM().isClientCompiler()) { + // Move this here for C1 and collecting oops in arguments (According to Rene) + map.setIncludeArgumentOops(cb.callerMustGCArguments(map.getThread())); + } + + Address saved_fp = null; + if (VM.getVM().isClientCompiler()) { + saved_fp = getFP().getAddressAt(0); + } else if (VM.getVM().isServerCompiler() && + (VM.getVM().getInterpreter().contains(sender_pc) || + VM.getVM().getStubRoutines().returnsToCallStub(sender_pc))) { + // C2 prologue saves EBP in the usual place. + // however only use it if the sender had link infomration in it. + saved_fp = sender_sp.getAddressAt(-2 * VM.getVM().getAddressSize()); + } + + return new X86Frame(sender_sp, saved_fp, sender_pc); + } + + protected boolean hasSenderPD() { + // FIXME + // Check for null ebp? Need to do some tests. + return true; + } + + public long frameSize() { + return (getSenderSP().minus(getSP()) / VM.getVM().getAddressSize()); + } + + public Address getLink() { + return addressOfStackSlot(LINK_OFFSET).getAddressAt(0); + } + + // FIXME: not implementable yet + //inline void frame::set_link(intptr_t* addr) { *(intptr_t **)addr_at(link_offset) = addr; } + + public Address getUnextendedSP() { return raw_unextendedSP; } + + // Return address: + public Address getSenderPCAddr() { return addressOfStackSlot(RETURN_ADDR_OFFSET); } + public Address getSenderPC() { return getSenderPCAddr().getAddressAt(0); } + + // return address of param, zero origin index. + public Address getNativeParamAddr(int idx) { + return addressOfStackSlot(NATIVE_FRAME_INITIAL_PARAM_OFFSET + idx); + } + + public Address getSenderSP() { return addressOfStackSlot(SENDER_SP_OFFSET); } + + public Address compiledArgumentToLocationPD(VMReg reg, RegisterMap regMap, int argSize) { + if (VM.getVM().isCore() || VM.getVM().isClientCompiler()) { + throw new RuntimeException("Should not reach here"); + } + + return oopMapRegToLocation(reg, regMap); + } + + public Address addressOfInterpreterFrameLocals() { + return addressOfStackSlot(INTERPRETER_FRAME_LOCALS_OFFSET); + } + + private Address addressOfInterpreterFrameBCX() { + return addressOfStackSlot(INTERPRETER_FRAME_BCX_OFFSET); + } + + public int getInterpreterFrameBCI() { + // FIXME: this is not atomic with respect to GC and is unsuitable + // for use in a non-debugging, or reflective, system. Need to + // figure out how to express this. + Address bcp = addressOfInterpreterFrameBCX().getAddressAt(0); + OopHandle methodHandle = addressOfInterpreterFrameMethod().getOopHandleAt(0); + Method method = (Method) VM.getVM().getObjectHeap().newOop(methodHandle); + return bcpToBci(bcp, method); + } + + public Address addressOfInterpreterFrameMDX() { + return addressOfStackSlot(INTERPRETER_FRAME_MDX_OFFSET); + } + + // FIXME + //inline int frame::interpreter_frame_monitor_size() { + // return BasicObjectLock::size(); + //} + + // expression stack + // (the max_stack arguments are used by the GC; see class FrameClosure) + + public Address addressOfInterpreterFrameExpressionStack() { + Address monitorEnd = interpreterFrameMonitorEnd().address(); + return monitorEnd.addOffsetTo(-1 * VM.getVM().getAddressSize()); + } + + public int getInterpreterFrameExpressionStackDirection() { return -1; } + + // top of expression stack + public Address addressOfInterpreterFrameTOS() { + return getSP(); + } + + /** Expression stack from top down */ + public Address addressOfInterpreterFrameTOSAt(int slot) { + return addressOfInterpreterFrameTOS().addOffsetTo(slot * VM.getVM().getAddressSize()); + } + + public Address getInterpreterFrameSenderSP() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isInterpretedFrame(), "interpreted frame expected"); + } + return addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0); + } + + // Monitors + public BasicObjectLock interpreterFrameMonitorBegin() { + return new BasicObjectLock(addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET)); + } + + public BasicObjectLock interpreterFrameMonitorEnd() { + Address result = addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET).getAddressAt(0); + if (Assert.ASSERTS_ENABLED) { + // make sure the pointer points inside the frame + Assert.that(AddressOps.gt(getFP(), result), "result must < than frame pointer"); + Assert.that(AddressOps.lte(getSP(), result), "result must >= than stack pointer"); + } + return new BasicObjectLock(result); + } + + public int interpreterFrameMonitorSize() { + return BasicObjectLock.size(); + } + + // Method + public Address addressOfInterpreterFrameMethod() { + return addressOfStackSlot(INTERPRETER_FRAME_METHOD_OFFSET); + } + + // Constant pool cache + public Address addressOfInterpreterFrameCPCache() { + return addressOfStackSlot(INTERPRETER_FRAME_CACHE_OFFSET); + } + + // Entry frames + public JavaCallWrapper getEntryFrameCallWrapper() { + return new X86JavaCallWrapper(addressOfStackSlot(ENTRY_FRAME_CALL_WRAPPER_OFFSET).getAddressAt(0)); + } + + protected Address addressOfSavedOopResult() { + // offset is 2 for compiler2 and 3 for compiler1 + return getSP().addOffsetTo((VM.getVM().isClientCompiler() ? 2 : 3) * + VM.getVM().getAddressSize()); + } + + protected Address addressOfSavedReceiver() { + return getSP().addOffsetTo(-4 * VM.getVM().getAddressSize()); + } + + private void dumpStack() { + if (getFP() != null) { + for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize()); + AddressOps.lte(addr, getFP().addOffsetTo(5 * VM.getVM().getAddressSize())); + addr = addr.addOffsetTo(VM.getVM().getAddressSize())) { + System.out.println(addr + ": " + addr.getAddressAt(0)); + } + } else { + for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize()); + AddressOps.lte(addr, getSP().addOffsetTo(20 * VM.getVM().getAddressSize())); + addr = addr.addOffsetTo(VM.getVM().getAddressSize())) { + System.out.println(addr + ": " + addr.getAddressAt(0)); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86JavaCallWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86JavaCallWrapper.java new file mode 100644 index 00000000000..1f10f808edc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86JavaCallWrapper.java @@ -0,0 +1,56 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.x86; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +public class X86JavaCallWrapper extends JavaCallWrapper { + private static AddressField lastJavaFPField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaFrameAnchor"); + + lastJavaFPField = type.getAddressField("_last_Java_fp"); + } + + public X86JavaCallWrapper(Address addr) { + super(addr); + } + + public Address getLastJavaFP() { + return lastJavaFPField.getValue(addr.addOffsetTo(anchorField.getOffset())); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86RegisterMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86RegisterMap.java new file mode 100644 index 00000000000..14b955f5e06 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86RegisterMap.java @@ -0,0 +1,52 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime.x86; + +import sun.jvm.hotspot.asm.x86.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; + +public class X86RegisterMap extends RegisterMap { + + /** This is the only public constructor */ + public X86RegisterMap(JavaThread thread, boolean updateMap) { + super(thread, updateMap); + } + + protected X86RegisterMap(RegisterMap map) { + super(map); + } + + public Object clone() { + X86RegisterMap retval = new X86RegisterMap(this); + return retval; + } + + // no PD state to clear or copy: + protected void clearPD() {} + protected void initializePD() {} + protected void initializeFromPD(RegisterMap map) {} + protected Address getLocationPD(VMReg reg) { return null; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/FinalizerInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/FinalizerInfo.java new file mode 100644 index 00000000000..e2c5f524fcb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/FinalizerInfo.java @@ -0,0 +1,138 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import sun.jvm.hotspot.tools.*; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.utilities.SystemDictionaryHelper; +import sun.jvm.hotspot.utilities.ObjectReader; +import sun.jvm.hotspot.utilities.MarkBits; + +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +/* + * Iterates over the queue of object pending finalization and prints a + * summary of these objects in the form of a histogram. + */ +public class FinalizerInfo extends Tool { + public static void main(String[] args) { + FinalizerInfo finfo = new FinalizerInfo(); + finfo.start(args); + finfo.stop(); + } + + public void run() { + /* + * The implementation here has a dependency on the implementation of + * java.lang.ref.Finalizer. If the Finalizer implementation changes it's + * possible this method will require changes too. We looked into using + * ObjectReader to deserialize the objects from the target VM but as + * there aren't any public methods to traverse the queue it means using + * reflection which will also tie us to the implementation. + * + * The assumption here is that Finalizer.queue is the ReferenceQueue + * with the objects awaiting finalization. The ReferenceQueue queueLength + * is the number of objects in the queue, and 'head' is the head of the + * queue. + */ + InstanceKlass ik = + SystemDictionaryHelper.findInstanceKlass("java.lang.ref.Finalizer"); + final OopField queueField[] = new OopField[1]; + ik.iterateFields(new DefaultOopVisitor() { + public void doOop(OopField field, boolean isVMField) { + String name = field.getID().getName(); + if (name.equals("queue")) { + queueField[0] = field; + } + } + }, false); + Oop queue = queueField[0].getValue(ik); + + InstanceKlass k = (InstanceKlass) queue.getKlass(); + + LongField queueLengthField = (LongField) k.findField("queueLength", "J"); + long queueLength = queueLengthField.getValue(queue); + + OopField headField = (OopField) k.findField("head", "Ljava/lang/ref/Reference;"); + Oop head = headField.getValue(queue); + + System.out.println("Number of objects pending for finalization: " + queueLength); + + /* + * If 'head' is non-NULL then it is the head of a list of References. + * We iterate over the list (end of list is when head.next == head) + */ + if (head != null) { + k = (InstanceKlass) head.getKlass(); + OopField referentField = + (OopField) k.findField("referent", "Ljava/lang/Object;"); + OopField nextField = + (OopField) k.findField("next", "Ljava/lang/ref/Reference;"); + + HashMap map = new HashMap(); + for (;;) { + Oop referent = referentField.getValue(head); + + Klass klass = referent.getKlass(); + if (!map.containsKey(klass)) { + map.put(klass, new ObjectHistogramElement(klass)); + } + ((ObjectHistogramElement)map.get(klass)).updateWith(referent); + + Oop next = nextField.getValue(head); + if (next == null || next.equals(head)) break; + head = next; + } + + /* + * Sort results - decending order by total size + */ + ArrayList list = new ArrayList(); + list.addAll(map.values()); + Collections.sort(list, new Comparator() { + public int compare(Object o1, Object o2) { + return ((ObjectHistogramElement)o1).compare((ObjectHistogramElement)o2); + } + }); + + /* + * Print summary of objects in queue + */ + System.out.println(""); + System.out.println("Count" + "\t" + "Class description"); + System.out.println("-------------------------------------------------------"); + for (int i=0; i\tto dump heap to " + + DEFAULT_DUMP_FILE); + System.out.println(" -f \tto dump heap to "); + super.printFlagsUsage(); + } + + // use HeapHprofBinWriter to write the heap dump + public void run() { + System.out.println("Dumping heap to " + dumpFile + " ..."); + try { + new HeapHprofBinWriter().write(dumpFile); + System.out.println("Heap dump file created"); + } catch (IOException ioe) { + System.err.println(ioe.getMessage()); + } + } + + // JDK jmap utility will always invoke this tool as: + // HeapDumper -f + public static void main(String args[]) { + String file = DEFAULT_DUMP_FILE; + if (args.length > 2) { + if (args[0].equals("-f")) { + file = args[1]; + String[] newargs = new String[args.length-2]; + System.arraycopy(args, 2, newargs, 0, args.length-2); + args = newargs; + } + } + + HeapDumper dumper = new HeapDumper(file); + dumper.start(args); + dumper.stop(); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java new file mode 100644 index 00000000000..039fc37e027 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java @@ -0,0 +1,216 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import java.util.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.gc_implementation.parallelScavenge.*; +import sun.jvm.hotspot.gc_implementation.shared.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.tools.*; + +public class HeapSummary extends Tool { + + public static void main(String[] args) { + HeapSummary hs = new HeapSummary(); + hs.start(args); + hs.stop(); + } + + public void run() { + CollectedHeap heap = VM.getVM().getUniverse().heap(); + VM.Flag[] flags = VM.getVM().getCommandLineFlags(); + Map flagMap = new HashMap(); + if (flags == null) { + System.out.println("WARNING: command line flags are not available"); + } else { + for (int f = 0; f < flags.length; f++) { + flagMap.put(flags[f].getName(), flags[f]); + } + } + + System.out.println(); + printGCAlgorithm(flagMap); + System.out.println(); + System.out.println("Heap Configuration:"); + printValue("MinHeapFreeRatio = ", getFlagValue("MinHeapFreeRatio", flagMap)); + printValue("MaxHeapFreeRatio = ", getFlagValue("MaxHeapFreeRatio", flagMap)); + printValMB("MaxHeapSize = ", getFlagValue("MaxHeapSize", flagMap)); + printValMB("NewSize = ", getFlagValue("NewSize", flagMap)); + printValMB("MaxNewSize = ", getFlagValue("MaxNewSize", flagMap)); + printValMB("OldSize = ", getFlagValue("OldSize", flagMap)); + printValue("NewRatio = ", getFlagValue("NewRatio", flagMap)); + printValue("SurvivorRatio = ", getFlagValue("SurvivorRatio", flagMap)); + printValMB("PermSize = ", getFlagValue("PermSize", flagMap)); + printValMB("MaxPermSize = ", getFlagValue("MaxPermSize", flagMap)); + + System.out.println(); + System.out.println("Heap Usage:"); + + if (heap instanceof GenCollectedHeap) { + GenCollectedHeap genHeap = (GenCollectedHeap) heap; + for (int n = 0; n < genHeap.nGens(); n++) { + Generation gen = genHeap.getGen(n); + if (gen instanceof sun.jvm.hotspot.memory.DefNewGeneration) { + System.out.println("New Generation (Eden + 1 Survivor Space):"); + printGen(gen); + + ContiguousSpace eden = ((DefNewGeneration)gen).eden(); + System.out.println("Eden Space:"); + printSpace(eden); + + ContiguousSpace from = ((DefNewGeneration)gen).from(); + System.out.println("From Space:"); + printSpace(from); + + ContiguousSpace to = ((DefNewGeneration)gen).to(); + System.out.println("To Space:"); + printSpace(to); + } else { + System.out.println(gen.name() + ":"); + printGen(gen); + } + } + // Perm generation + Generation permGen = genHeap.permGen(); + System.out.println("Perm Generation:"); + printGen(permGen); + } else if (heap instanceof ParallelScavengeHeap) { + ParallelScavengeHeap psh = (ParallelScavengeHeap) heap; + PSYoungGen youngGen = psh.youngGen(); + printPSYoungGen(youngGen); + + PSOldGen oldGen = psh.oldGen(); + long oldFree = oldGen.capacity() - oldGen.used(); + System.out.println("PS Old Generation"); + printValMB("capacity = ", oldGen.capacity()); + printValMB("used = ", oldGen.used()); + printValMB("free = ", oldFree); + System.out.println(alignment + (double)oldGen.used() * 100.0 / oldGen.capacity() + "% used"); + + PSPermGen permGen = psh.permGen(); + long permFree = permGen.capacity() - permGen.used(); + System.out.println("PS Perm Generation"); + printValMB("capacity = ", permGen.capacity()); + printValMB("used = ", permGen.used()); + printValMB("free = ", permFree); + System.out.println(alignment + (double)permGen.used() * 100.0 / permGen.capacity() + "% used"); + } else { + throw new RuntimeException("unknown heap type : " + heap.getClass()); + } + } + + // Helper methods + + private void printGCAlgorithm(Map flagMap) { + // print about new generation + long l = getFlagValue("UseParNewGC", flagMap); + if (l == 1L) { + System.out.println("using parallel threads in the new generation."); + } + + l = getFlagValue("UseTLAB", flagMap); + if (l == 1L) { + System.out.println("using thread-local object allocation."); + } + + l = getFlagValue("UseConcMarkSweepGC", flagMap); + if (l == 1L) { + System.out.println("Concurrent Mark-Sweep GC"); + return; + } + + l = getFlagValue("UseParallelGC", flagMap); + if (l == 1L) { + System.out.print("Parallel GC "); + l = getFlagValue("ParallelGCThreads", flagMap); + System.out.println("with " + l + " thread(s)"); + return; + } + + System.out.println("Mark Sweep Compact GC"); + } + + private void printPSYoungGen(PSYoungGen youngGen) { + System.out.println("PS Young Generation"); + MutableSpace eden = youngGen.edenSpace(); + System.out.println("Eden Space:"); + printMutableSpace(eden); + MutableSpace from = youngGen.fromSpace(); + System.out.println("From Space:"); + printMutableSpace(from); + MutableSpace to = youngGen.toSpace(); + System.out.println("To Space:"); + printMutableSpace(to); + } + + private void printMutableSpace(MutableSpace space) { + printValMB("capacity = ", space.capacity()); + printValMB("used = ", space.used()); + long free = space.capacity() - space.used(); + printValMB("free = ", free); + System.out.println(alignment + (double)space.used() * 100.0 / space.capacity() + "% used"); + } + + private static String alignment = " "; + + private void printGen(Generation gen) { + printValMB("capacity = ", gen.capacity()); + printValMB("used = ", gen.used()); + printValMB("free = ", gen.free()); + System.out.println(alignment + (double)gen.used() * 100.0 / gen.capacity() + "% used"); + } + + private void printSpace(ContiguousSpace space) { + printValMB("capacity = ", space.capacity()); + printValMB("used = ", space.used()); + printValMB("free = ", space.free()); + System.out.println(alignment + (double)space.used() * 100.0 / space.capacity() + "% used"); + } + + private static final double FACTOR = 1024*1024; + private void printValMB(String title, long value) { + double mb = value / FACTOR; + System.out.println(alignment + title + value + " (" + mb + "MB)"); + } + + private void printValue(String title, long value) { + System.out.println(alignment + title + value); + } + + private long getFlagValue(String name, Map flagMap) { + VM.Flag f = (VM.Flag) flagMap.get(name); + if (f != null) { + if (f.isBool()) { + return f.getBool()? 1L : 0L; + } else { + return Long.parseLong(f.getValue()); + } + } else { + return -1; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JInfo.java new file mode 100644 index 00000000000..61c739bc5b7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JInfo.java @@ -0,0 +1,144 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import sun.jvm.hotspot.runtime.*; + +public class JInfo extends Tool { + public JInfo(int m) { + mode = m; + } + + protected boolean needsJavaPrefix() { + return false; + } + + public String getName() { + return "jinfo"; + } + + protected void printFlagsUsage() { + System.out.println(" -flags\tto print VM flags"); + System.out.println(" -sysprops\tto print Java System properties"); + System.out.println(" \tto print both of the above"); + super.printFlagsUsage(); + } + + public static final int MODE_FLAGS = 0; + public static final int MODE_SYSPROPS = 1; + public static final int MODE_BOTH = 2; + + public void run() { + Tool tool = null; + switch (mode) { + case MODE_FLAGS: + printVMFlags(); + return; + case MODE_SYSPROPS: + tool = new SysPropsDumper(); + break; + case MODE_BOTH: { + tool = new Tool() { + public void run() { + Tool sysProps = new SysPropsDumper(); + sysProps.setAgent(getAgent()); + System.out.println("Java System Properties:"); + System.out.println(); + sysProps.run(); + System.out.println(); + System.out.println("VM Flags:"); + printVMFlags(); + System.out.println(); + } + }; + } + break; + + default: + usage(); + break; + } + tool.setAgent(getAgent()); + tool.run(); + } + + public static void main(String[] args) { + int mode = -1; + switch (args.length) { + case 1: + if (args[0].charAt(0) == '-') { + // -h or -help or some invalid flag + new JInfo(mode).usage(); + } else { + mode = MODE_BOTH; + } + break; + case 2: + case 3: { + String modeFlag = args[0]; + if (modeFlag.equals("-flags")) { + mode = MODE_FLAGS; + } else if (modeFlag.equals("-sysprops")) { + mode = MODE_SYSPROPS; + } else if (modeFlag.charAt(0) == '-') { + // -h or -help or some invalid flag + new JInfo(mode).usage(); + } else { + mode = MODE_BOTH; + } + + if (mode != MODE_BOTH) { + // we have to consume first flag argument. + String[] newArgs = new String[args.length - 1]; + for (int i = 0; i < newArgs.length; i++) { + newArgs[i] = args[i + 1]; + } + args = newArgs; + } + break; + } + + default: + new JInfo(mode).usage(); + } + + JInfo jinfo = new JInfo(mode); + jinfo.start(args); + jinfo.stop(); + } + + private void printVMFlags() { + String str = Arguments.getJVMFlags(); + if (str != null) { + System.out.println(str); + } + str = Arguments.getJVMArgs(); + if (str != null) { + System.out.println(str); + } + } + + private int mode; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JMap.java new file mode 100644 index 00000000000..85cf3758630 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JMap.java @@ -0,0 +1,188 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import java.io.*; +import sun.jvm.hotspot.utilities.*; + +public class JMap extends Tool { + public JMap(int m) { + mode = m; + } + + public JMap() { + this(MODE_PMAP); + } + + protected boolean needsJavaPrefix() { + return false; + } + + public String getName() { + return "jmap"; + } + + protected String getCommandFlags() { + return "-heap|-heap:format=b|-histo|-permstat|-finalizerinfo"; + } + + protected void printFlagsUsage() { + System.out.println(" \tto print same info as Solaris pmap"); + System.out.println(" -heap\tto print java heap summary"); + System.out.println(" -heap:format=b\tto dump java heap in hprof binary format"); + System.out.println(" -histo\tto print histogram of java object heap"); + System.out.println(" -permstat\tto print permanent generation statistics"); + System.out.println(" -finalizerinfo\tto print information on objects awaiting finalization"); + super.printFlagsUsage(); + } + + public static final int MODE_HEAP_SUMMARY = 0; + public static final int MODE_HISTOGRAM = 1; + public static final int MODE_PERMSTAT = 2; + public static final int MODE_PMAP = 3; + public static final int MODE_HEAP_GRAPH_HPROF_BIN = 4; + public static final int MODE_HEAP_GRAPH_GXL = 5; + public static final int MODE_FINALIZERINFO = 6; + + public void run() { + Tool tool = null; + switch (mode) { + + case MODE_HEAP_SUMMARY: + tool = new HeapSummary(); + break; + + case MODE_HISTOGRAM: + tool = new ObjectHistogram(); + break; + + case MODE_PERMSTAT: + tool = new PermStat(); + break; + + case MODE_PMAP: + tool = new PMap(); + break; + + case MODE_HEAP_GRAPH_HPROF_BIN: + writeHeapHprofBin(); + return; + + case MODE_HEAP_GRAPH_GXL: + writeHeapGXL(); + return; + + case MODE_FINALIZERINFO: + tool = new FinalizerInfo(); + break; + + default: + usage(); + break; + } + + tool.setAgent(getAgent()); + tool.setDebugeeType(getDebugeeType()); + tool.run(); + } + + public static void main(String[] args) { + int mode = MODE_PMAP; + if (args.length > 1 ) { + String modeFlag = args[0]; + boolean copyArgs = true; + if (modeFlag.equals("-heap")) { + mode = MODE_HEAP_SUMMARY; + } else if (modeFlag.equals("-histo")) { + mode = MODE_HISTOGRAM; + } else if (modeFlag.equals("-permstat")) { + mode = MODE_PERMSTAT; + } else if (modeFlag.equals("-finalizerinfo")) { + mode = MODE_FINALIZERINFO; + } else { + int index = modeFlag.indexOf("-heap:format="); + if (index != -1) { + String format = modeFlag.substring(1 + modeFlag.indexOf('=')); + if (format.equals("b")) { + mode = MODE_HEAP_GRAPH_HPROF_BIN; + } else if (format.equals("x")) { + mode = MODE_HEAP_GRAPH_GXL; + } else { + System.err.println("unknown heap format:" + format); + return; + } + } else { + copyArgs = false; + } + } + + if (copyArgs) { + String[] newArgs = new String[args.length - 1]; + for (int i = 0; i < newArgs.length; i++) { + newArgs[i] = args[i + 1]; + } + args = newArgs; + } + } + + JMap jmap = new JMap(mode); + jmap.start(args); + jmap.stop(); + } + + public boolean writeHeapHprofBin(String fileName) { + try { + HeapGraphWriter hgw = new HeapHprofBinWriter(); + hgw.write(fileName); + System.out.println("heap written to " + fileName); + return true; + } catch (IOException exp) { + System.err.println(exp.getMessage()); + return false; + } + } + + public boolean writeHeapHprofBin() { + return writeHeapHprofBin("heap.bin"); + } + + private boolean writeHeapGXL(String fileName) { + try { + HeapGraphWriter hgw = new HeapGXLWriter(); + hgw.write(fileName); + System.out.println("heap written to " + fileName); + return true; + } catch (IOException exp) { + System.err.println(exp.getMessage()); + return false; + } + } + + public boolean writeHeapGXL() { + return writeHeapGXL("heap.xml"); + } + + private int mode; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JSnap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JSnap.java new file mode 100644 index 00000000000..830aaf8b159 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JSnap.java @@ -0,0 +1,60 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import java.io.*; +import sun.jvm.hotspot.runtime.*; + +public class JSnap extends Tool { + public void run() { + final PrintStream out = System.out; + if (PerfMemory.initialized()) { + PerfDataPrologue prologue = PerfMemory.prologue(); + if (prologue.accessible()) { + PerfMemory.iterate(new PerfMemory.PerfDataEntryVisitor() { + public boolean visit(PerfDataEntry pde) { + if (pde.supported()) { + out.print(pde.name()); + out.print('='); + out.println(pde.valueAsString()); + } + // goto next entry + return true; + } + }); + } else { + out.println("PerfMemory is not accessible"); + } + } else { + out.println("PerfMemory is not initialized"); + } + } + + public static void main(String[] args) { + JSnap js = new JSnap(); + js.start(args); + js.stop(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JStack.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JStack.java new file mode 100644 index 00000000000..a6181eee41b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JStack.java @@ -0,0 +1,92 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +public class JStack extends Tool { + public JStack(boolean mixedMode, boolean concurrentLocks) { + this.mixedMode = mixedMode; + this.concurrentLocks = concurrentLocks; + } + + public JStack() { + this(true, true); + } + + protected boolean needsJavaPrefix() { + return false; + } + + public String getName() { + return "jstack"; + } + + protected void printFlagsUsage() { + System.out.println(" -l\tto print java.util.concurrent locks"); + System.out.println(" -m\tto print both java and native frames (mixed mode)"); + super.printFlagsUsage(); + } + + public void run() { + Tool tool = null; + if (mixedMode) { + tool = new PStack(false, concurrentLocks); + } else { + tool = new StackTrace(false, concurrentLocks); + } + tool.setAgent(getAgent()); + tool.setDebugeeType(getDebugeeType()); + tool.run(); + } + + public static void main(String[] args) { + boolean mixedMode = false; + boolean concurrentLocks = false; + int used = 0; + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-m")) { + mixedMode = true; + used++; + } else if (args[i].equals("-l")) { + concurrentLocks = true; + used++; + } + } + + if (used != 0) { + String[] newArgs = new String[args.length - used]; + for (int i = 0; i < newArgs.length; i++) { + newArgs[i] = args[i + used]; + } + args = newArgs; + } + + JStack jstack = new JStack(mixedMode, concurrentLocks); + jstack.start(args); + jstack.stop(); + } + + private boolean mixedMode; + private boolean concurrentLocks; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/ObjectHistogram.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/ObjectHistogram.java new file mode 100644 index 00000000000..241d1979531 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/ObjectHistogram.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import java.io.PrintStream; + +/** A sample tool which uses the Serviceability Agent's APIs to obtain + an object histogram from a remote or crashed VM. */ +public class ObjectHistogram extends Tool { + + public void run() { + run(System.out, System.err); + } + + public void run(PrintStream out, PrintStream err) { + // Ready to go with the database... + ObjectHeap heap = VM.getVM().getObjectHeap(); + sun.jvm.hotspot.oops.ObjectHistogram histogram = + new sun.jvm.hotspot.oops.ObjectHistogram(); + err.println("Iterating over heap. This may take a while..."); + long startTime = System.currentTimeMillis(); + heap.iterate(histogram); + long endTime = System.currentTimeMillis(); + histogram.printOn(out); + float secs = (float) (endTime - startTime) / 1000.0f; + err.println("Heap traversal took " + secs + " seconds."); + } + + public static void main(String[] args) { + ObjectHistogram oh = new ObjectHistogram(); + oh.start(args); + oh.stop(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/PMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/PMap.java new file mode 100644 index 00000000000..bd0ac2e0445 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/PMap.java @@ -0,0 +1,70 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.runtime.*; + +public class PMap extends Tool { + public void run() { + run(System.out); + } + + public void run(PrintStream out) { + run(out, getAgent().getDebugger()); + } + + public void run(PrintStream out, Debugger dbg) { + CDebugger cdbg = dbg.getCDebugger(); + if (cdbg != null) { + List l = cdbg.getLoadObjectList(); + for (Iterator itr = l.iterator() ; itr.hasNext();) { + LoadObject lo = (LoadObject) itr.next(); + out.print(lo.getBase() + "\t"); + out.print(lo.getSize()/1024 + "K\t"); + out.println(lo.getName()); + } + } else { + if (getDebugeeType() == DEBUGEE_REMOTE) { + out.println("remote configuration is not yet implemented"); + } else { + out.println("not yet implemented (debugger does not support CDebugger)!"); + } + } + } + + protected boolean requiresVM() { + return false; + } + + public static void main(String[] args) throws Exception { + PMap t = new PMap(); + t.start(args); + t.stop(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/PStack.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/PStack.java new file mode 100644 index 00000000000..9bab664f274 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/PStack.java @@ -0,0 +1,272 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +public class PStack extends Tool { + // in non-verbose mode, methodOops are not printed in java frames + public PStack(boolean v, boolean concurrentLocks) { + this.verbose = v; + this.concurrentLocks = concurrentLocks; + } + + public PStack() { + this(true, true); + } + + public void run() { + run(System.out); + } + + public void run(PrintStream out) { + Debugger dbg = getAgent().getDebugger(); + run(out, dbg, getAgent().isJavaMode()); + } + + public void run(PrintStream out, Debugger dbg) { + run(out, dbg, true); + } + + private void run(PrintStream out, Debugger dbg, final boolean isJava) { + CDebugger cdbg = dbg.getCDebugger(); + if (cdbg != null) { + ConcurrentLocksPrinter concLocksPrinter = null; + if (isJava) { + // compute and cache java Vframes. + initJFrameCache(); + if (concurrentLocks) { + concLocksPrinter = new ConcurrentLocksPrinter(); + } + // print Java level deadlocks + try { + DeadlockDetector.print(out); + } catch (Exception exp) { + out.println("can't print deadlock information: " + exp.getMessage()); + } + } + + List l = cdbg.getThreadList(); + final boolean cdbgCanDemangle = cdbg.canDemangle(); + for (Iterator itr = l.iterator() ; itr.hasNext();) { + ThreadProxy th = (ThreadProxy) itr.next(); + try { + CFrame f = cdbg.topFrameForThread(th); + out.print("----------------- "); + out.print(th); + out.println(" -----------------"); + while (f != null) { + ClosestSymbol sym = f.closestSymbolToPC(); + Address pc = f.pc(); + if (sym != null) { + String name = sym.getName(); + if (cdbgCanDemangle) { + name = cdbg.demangle(name); + } + out.print(pc + "\t" + name); + long diff = sym.getOffset(); + if (diff != 0L) { + out.print(" + 0x" + Long.toHexString(diff)); + } + out.println(); + } else { + if (isJava) { + // look for one or more java frames + String[] names = null; + // check interpreter frame + Interpreter interp = VM.getVM().getInterpreter(); + if (interp.contains(pc)) { + names = getJavaNames(th, f.localVariableBase()); + // print codelet name if we can't determine method + if (names == null || names.length == 0) { + out.print(" "); + InterpreterCodelet ic = interp.getCodeletContaining(pc); + if (ic != null) { + String desc = ic.getDescription(); + if (desc != null) out.print(desc); + } + out.println(); + } + } else { + // look for known code blobs + CodeCache c = VM.getVM().getCodeCache(); + if (c.contains(pc)) { + out.print(pc + "\t"); + CodeBlob cb = c.findBlobUnsafe(pc); + if (cb.isNMethod()) { + names = getJavaNames(th, f.localVariableBase()); + // just print compiled code, if can't determine method + if (names == null || names.length == 0) { + out.println(""); + } + } else if (cb.isBufferBlob()) { + out.println(""); + } else if (cb.isRuntimeStub()) { + out.println(""); + } else if (cb.isDeoptimizationStub()) { + out.println(""); + } else if (cb.isUncommonTrapStub()) { + out.println(""); + } else if (cb.isExceptionStub()) { + out.println(""); + } else if (cb.isSafepointStub()) { + out.println(""); + } else { + out.println(""); + } + } else { + printUnknown(out,pc); + } + } + // print java frames, if any + if (names != null && names.length != 0) { + // print java frame(s) + for (int i = 0; i < names.length; i++) { + out.println(pc + "\t" + names[i]); + } + } + } else { + printUnknown(out,pc); + } + } + f = f.sender(); + } + } catch (Exception exp) { + exp.printStackTrace(); + // continue, may be we can do a better job for other threads + } + if (isJava && concurrentLocks) { + JavaThread jthread = (JavaThread) proxyToThread.get(th); + if (jthread != null) { + concLocksPrinter.print(jthread, out); + } + } + } // for threads + } else { + if (getDebugeeType() == DEBUGEE_REMOTE) { + out.println("remote configuration is not yet implemented"); + } else { + out.println("not yet implemented (debugger does not support CDebugger)!"); + } + } + } + + protected boolean requiresVM() { + return false; + } + + public static void main(String[] args) throws Exception { + PStack t = new PStack(); + t.start(args); + t.stop(); + } + + // -- Internals only below this point + private Map jframeCache; // Map + private Map proxyToThread; // Map + private PrintStream out; + private boolean verbose; + private boolean concurrentLocks; + + private void initJFrameCache() { + // cache frames for subsequent reference + jframeCache = new HashMap(); + proxyToThread = new HashMap(); + Threads threads = VM.getVM().getThreads(); + for (JavaThread cur = threads.first(); cur != null; cur = cur.next()) { + List tmp = new ArrayList(10); + try { + for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) { + tmp.add(vf); + } + } catch (Exception exp) { + // may be we may get frames for other threads, continue + // after printing stack trace. + exp.printStackTrace(); + } + JavaVFrame[] jvframes = new JavaVFrame[tmp.size()]; + System.arraycopy(tmp.toArray(), 0, jvframes, 0, jvframes.length); + jframeCache.put(cur.getThreadProxy(), jvframes); + proxyToThread.put(cur.getThreadProxy(), cur); + } + } + + private void printUnknown(PrintStream out, Address pc) { + out.println(pc + "\t????????"); + } + + private String[] getJavaNames(ThreadProxy th, Address fp) { + if (fp == null) { + return null; + } + JavaVFrame[] jvframes = (JavaVFrame[]) jframeCache.get(th); + if (jvframes == null) return null; // not a java thread + List names = new ArrayList(10); + for (int fCount = 0; fCount < jvframes.length; fCount++) { + JavaVFrame vf = jvframes[fCount]; + Frame f = vf.getFrame(); + if (fp.equals(f.getFP())) { + StringBuffer sb = new StringBuffer(); + Method method = vf.getMethod(); + // a special char to identify java frames in output + sb.append("* "); + sb.append(method.externalNameAndSignature()); + sb.append(" bci:" + vf.getBCI()); + int lineNumber = method.getLineNumberFromBCI(vf.getBCI()); + if (lineNumber != -1) { + sb.append(" line:" + lineNumber); + } + + if (verbose) { + sb.append(" methodOop:" + method.getHandle()); + } + + if (vf.isCompiledFrame()) { + sb.append(" (Compiled frame"); + if (vf.isDeoptimized()) { + sb.append(" [deoptimized]"); + } + } else if (vf.isInterpretedFrame()) { + sb.append(" (Interpreted frame"); + } + if (vf.mayBeImpreciseDbg()) { + sb.append("; information may be imprecise"); + } + sb.append(")"); + names.add(sb.toString()); + } + } + String[] res = new String[names.size()]; + System.arraycopy(names.toArray(), 0, res, 0, res.length); + return res; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/PermStat.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/PermStat.java new file mode 100644 index 00000000000..1808babf2cc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/PermStat.java @@ -0,0 +1,311 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.tools.*; +import sun.jvm.hotspot.utilities.*; + +/** + A command line tool to print perm. generation statistics. +*/ + +public class PermStat extends Tool { + boolean verbose = true; + + public static void main(String[] args) { + PermStat ps = new PermStat(); + ps.start(args); + ps.stop(); + } + + private static class ClassData { + Klass klass; + long size; + + ClassData(Klass klass, long size) { + this.klass = klass; this.size = size; + } + } + + private static class LoaderData { + long numClasses; + long classSize; + List classDetail = new ArrayList(); // List + } + + public void run() { + printInternStringStatistics(); + printClassLoaderStatistics(); + } + + private void printInternStringStatistics() { + class StringStat implements StringTable.StringVisitor { + private int count; + private long size; + private OopField stringValueField; + + StringStat() { + VM vm = VM.getVM(); + SystemDictionary sysDict = vm.getSystemDictionary(); + InstanceKlass strKlass = sysDict.getStringKlass(); + // String has a field named 'value' of type 'char[]'. + stringValueField = (OopField) strKlass.findField("value", "[C"); + } + + private long stringSize(Instance instance) { + // We include String content in size calculation. + return instance.getObjectSize() + + stringValueField.getValue(instance).getObjectSize(); + } + + public void visit(Instance str) { + count++; + size += stringSize(str); + } + + public void print() { + System.out.println(count + + " intern Strings occupying " + size + " bytes."); + } + } + + StringStat stat = new StringStat(); + StringTable strTable = VM.getVM().getStringTable(); + strTable.stringsDo(stat); + stat.print(); + } + + private void printClassLoaderStatistics() { + final PrintStream out = System.out; + final PrintStream err = System.err; + final Map loaderMap = new HashMap(); + // loader data for bootstrap class loader + final LoaderData bootstrapLoaderData = new LoaderData(); + if (verbose) { + err.print("finding class loader instances .."); + } + + VM vm = VM.getVM(); + ObjectHeap heap = vm.getObjectHeap(); + Klass classLoaderKlass = vm.getSystemDictionary().getClassLoaderKlass(); + try { + heap.iterateObjectsOfKlass(new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + loaderMap.put(oop, new LoaderData()); + return false; + } + }, classLoaderKlass); + } catch (Exception se) { + se.printStackTrace(); + } + + if (verbose) { + err.println("done."); + err.print("computing per loader stat .."); + } + + SystemDictionary dict = VM.getVM().getSystemDictionary(); + dict.classesDo(new SystemDictionary.ClassAndLoaderVisitor() { + public void visit(Klass k, Oop loader) { + if (! (k instanceof InstanceKlass)) { + return; + } + LoaderData ld = (loader != null) ? (LoaderData)loaderMap.get(loader) + : bootstrapLoaderData; + if (ld != null) { + ld.numClasses++; + long size = computeSize((InstanceKlass)k); + ld.classDetail.add(new ClassData(k, size)); + ld.classSize += size; + } + } + }); + + if (verbose) { + err.println("done."); + err.print("please wait.. computing liveness"); + } + + // compute reverse pointer analysis (takes long time for larger app) + ReversePtrsAnalysis analysis = new ReversePtrsAnalysis(); + + if (verbose) { + analysis.setHeapProgressThunk(new HeapProgressThunk() { + public void heapIterationFractionUpdate(double fractionOfHeapVisited) { + err.print('.'); + } + // This will be called after the iteration is complete + public void heapIterationComplete() { + err.println("done."); + } + }); + } + + try { + analysis.run(); + } catch (Exception e) { + // e.printStackTrace(); + if (verbose) + err.println("liveness analysis may be inaccurate ..."); + } + ReversePtrs liveness = VM.getVM().getRevPtrs(); + + out.println("class_loader\tclasses\tbytes\tparent_loader\talive?\ttype"); + out.println(); + + long numClassLoaders = 1L; + long totalNumClasses = bootstrapLoaderData.numClasses; + long totalClassSize = bootstrapLoaderData.classSize; + long numAliveLoaders = 1L; + long numDeadLoaders = 0L; + + // print bootstrap loader details + out.print(""); + out.print('\t'); + out.print(bootstrapLoaderData.numClasses); + out.print('\t'); + out.print(bootstrapLoaderData.classSize); + out.print('\t'); + out.print(" null "); + out.print('\t'); + // bootstrap loader is always alive + out.print("live"); + out.print('\t'); + out.println(""); + + for (Iterator keyItr = loaderMap.keySet().iterator(); keyItr.hasNext();) { + Oop loader = (Oop) keyItr.next(); + LoaderData data = (LoaderData) loaderMap.get(loader); + numClassLoaders ++; + totalNumClasses += data.numClasses; + totalClassSize += data.classSize; + + out.print(loader.getHandle()); + out.print('\t'); + out.print(data.numClasses); + out.print('\t'); + out.print(data.classSize); + out.print('\t'); + + class ParentFinder extends DefaultOopVisitor { + public void doOop(OopField field, boolean isVMField) { + if (field.getID().getName().equals("parent")) { + parent = field.getValue(getObj()); + } + } + private Oop parent = null; + public Oop getParent() { return parent; } + } + + ParentFinder parentFinder = new ParentFinder(); + loader.iterate(parentFinder, false); + Oop parent = parentFinder.getParent(); + out.print((parent != null)? parent.getHandle().toString() : " null "); + out.print('\t'); + boolean alive = (liveness != null) ? (liveness.get(loader) != null) : true; + out.print(alive? "live" : "dead"); + if (alive) numAliveLoaders++; else numDeadLoaders++; + out.print('\t'); + Klass loaderKlass = loader.getKlass(); + if (loaderKlass != null) { + out.print(loaderKlass.getName().asString()); + out.print('@'); + out.print(loader.getKlass().getHandle()); + } else { + out.print(" null! "); + } + out.println(); + } + + out.println(); + // summary line + out.print("total = "); + out.print(numClassLoaders); + out.print('\t'); + out.print(totalNumClasses); + out.print('\t'); + out.print(totalClassSize); + out.print('\t'); + out.print(" N/A "); + out.print('\t'); + out.print("alive="); + out.print(numAliveLoaders); + out.print(", dead="); + out.print(numDeadLoaders); + out.print('\t'); + out.print(" N/A "); + out.println(); + } + + private long computeSize(InstanceKlass k) { + long size = 0L; + // InstanceKlass object size + size += k.getObjectSize(); + + // add ConstantPool size + size += k.getConstants().getObjectSize(); + + // add ConstantPoolCache, if any + ConstantPoolCache cpCache = k.getConstants().getCache(); + if (cpCache != null) { + size += cpCache.getObjectSize(); + } + + // add interfaces size + ObjArray interfaces = k.getLocalInterfaces(); + size += (interfaces.getLength() != 0L)? interfaces.getObjectSize() : 0L; + ObjArray transitiveInterfaces = k.getTransitiveInterfaces(); + size += (transitiveInterfaces.getLength() != 0L)? transitiveInterfaces.getObjectSize() : 0L; + + // add inner classes size + TypeArray innerClasses = k.getInnerClasses(); + size += innerClasses.getObjectSize(); + + // add fields size + size += k.getFields().getObjectSize(); + + // add methods size + ObjArray methods = k.getMethods(); + size += (methods.getLength() != 0L)? methods.getObjectSize() : 0L; + TypeArray methodOrdering = k.getMethodOrdering(); + size += (methodOrdering.getLength() != 0L)? methodOrdering.getObjectSize() : 0; + + // add each method's size + int numMethods = (int) methods.getLength(); + for (int i = 0; i < numMethods; i++) { + Method m = (Method) methods.getObjAt(i); + size += m.getObjectSize(); + } + + return size; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/StackTrace.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/StackTrace.java new file mode 100644 index 00000000000..6814d5378cc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/StackTrace.java @@ -0,0 +1,136 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.oops.*; + +/** Traverses and prints the stack traces for all Java threads in the + * remote VM */ +public class StackTrace extends Tool { + // in non-verbose mode pc, sp and methodOop are not printed + public StackTrace(boolean v, boolean concurrentLocks) { + this.verbose = v; + this.concurrentLocks = concurrentLocks; + } + + public StackTrace() { + this(true, true); + } + + public void run() { + run(System.out); + } + + public void run(java.io.PrintStream tty) { + // Ready to go with the database... + try { + // print deadlock information before stack trace + DeadlockDetector.print(tty); + } catch (Exception exp) { + exp.printStackTrace(); + tty.println("Can't print deadlocks:" + exp.getMessage()); + } + + try { + ConcurrentLocksPrinter concLocksPrinter = null; + if (concurrentLocks) { + concLocksPrinter = new ConcurrentLocksPrinter(); + } + Threads threads = VM.getVM().getThreads(); + int i = 1; + for (JavaThread cur = threads.first(); cur != null; cur = cur.next(), i++) { + if (cur.isJavaThread()) { + Address sp = cur.getLastJavaSP(); + tty.print("Thread "); + cur.printThreadIDOn(tty); + tty.print(": (state = " + cur.getThreadState()); + if (verbose) { + tty.println(", current Java SP = " + sp); + } + tty.println(')'); + try { + for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) { + Method method = vf.getMethod(); + tty.print(" - " + method.externalNameAndSignature() + + " @bci=" + vf.getBCI()); + + int lineNumber = method.getLineNumberFromBCI(vf.getBCI()); + if (lineNumber != -1) { + tty.print(", line=" + lineNumber); + } + + if (verbose) { + Address pc = vf.getFrame().getPC(); + if (pc != null) { + tty.print(", pc=" + pc); + } + + tty.print(", methodOop=" + method.getHandle()); + } + + if (vf.isCompiledFrame()) { + tty.print(" (Compiled frame"); + if (vf.isDeoptimized()) { + tty.print(" [deoptimized]"); + } + } + if (vf.isInterpretedFrame()) { + tty.print(" (Interpreted frame"); + } + if (vf.mayBeImpreciseDbg()) { + tty.print("; information may be imprecise"); + } + + tty.println(")"); + } + } catch (Exception e) { + tty.println("Error occurred during stack walking:"); + e.printStackTrace(); + } + tty.println(); + if (concurrentLocks) { + concLocksPrinter.print(cur, tty); + } + tty.println(); + } + } + } + catch (AddressException e) { + System.err.println("Error accessing address 0x" + Long.toHexString(e.getAddress())); + e.printStackTrace(); + } + } + + public static void main(String[] args) { + StackTrace st = new StackTrace(); + st.start(args); + st.stop(); + } + + private boolean verbose; + private boolean concurrentLocks; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/SysPropsDumper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/SysPropsDumper.java new file mode 100644 index 00000000000..f4aa8402fa4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/SysPropsDumper.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import java.io.PrintStream; +import java.util.*; + +import sun.jvm.hotspot.runtime.*; + +public class SysPropsDumper extends Tool { + + public void run() { + Properties sysProps = VM.getVM().getSystemProperties(); + PrintStream out = System.out; + if (sysProps != null) { + Enumeration keys = sysProps.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + out.print(key); + out.print(" = "); + out.println(sysProps.get(key)); + } + } else { + out.println("System Properties info not available!"); + } + } + + public static void main(String[] args) { + SysPropsDumper pd = new SysPropsDumper(); + pd.start(args); + pd.stop(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/Tool.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/Tool.java new file mode 100644 index 00000000000..b82beabd003 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/Tool.java @@ -0,0 +1,230 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools; + +import java.io.PrintStream; +import java.util.Hashtable; +import sun.jvm.hotspot.*; +import sun.jvm.hotspot.bugspot.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.debugger.*; + +// generic command line or GUI tool. +// override run & code main as shown below. + +public abstract class Tool implements Runnable { + private BugSpotAgent agent; + private int debugeeType; + + // debugeeType is one of constants below + protected static final int DEBUGEE_PID = 0; + protected static final int DEBUGEE_CORE = 1; + protected static final int DEBUGEE_REMOTE = 2; + + public String getName() { + return getClass().getName(); + } + + protected boolean needsJavaPrefix() { + return true; + } + + // whether this tool requires debuggee to be java process or core? + protected boolean requiresVM() { + return true; + } + + protected void setAgent(BugSpotAgent a) { + agent = a; + } + + protected void setDebugeeType(int dt) { + debugeeType = dt; + } + + protected BugSpotAgent getAgent() { + return agent; + } + + protected int getDebugeeType() { + return debugeeType; + } + + protected void printUsage() { + String name = null; + if (needsJavaPrefix()) { + name = "java " + getName(); + } else { + name = getName(); + } + System.out.println("Usage: " + name + " [option] "); + System.out.println("\t\t(to connect to a live java process)"); + System.out.println(" or " + name + " [option] "); + System.out.println("\t\t(to connect to a core file)"); + System.out.println(" or " + name + " [option] [server_id@]"); + System.out.println("\t\t(to connect to a remote debug server)"); + System.out.println(); + System.out.println("where option must be one of:"); + printFlagsUsage(); + } + + protected void printFlagsUsage() { + System.out.println(" -h | -help\tto print this help message"); + } + + protected void usage() { + printUsage(); + System.exit(1); + } + + /* + Derived class main should be of the following form: + + public static void main(String[] args) { + obj = new ; + obj.start(args); + } + + */ + + protected void stop() { + if (agent != null) { + agent.detach(); + System.exit(0); + } + } + + protected void start(String[] args) { + if ((args.length < 1) || (args.length > 2)) { + usage(); + } + + // Attempt to handle -h or -help or some invalid flag + if (args[0].startsWith("-")) { + usage(); + } + + PrintStream err = System.err; + + int pid = 0; + String coreFileName = null; + String executableName = null; + String remoteServer = null; + + switch (args.length) { + case 1: + try { + pid = Integer.parseInt(args[0]); + debugeeType = DEBUGEE_PID; + } catch (NumberFormatException e) { + // try remote server + remoteServer = args[0]; + debugeeType = DEBUGEE_REMOTE; + } + break; + + case 2: + executableName = args[0]; + coreFileName = args[1]; + debugeeType = DEBUGEE_CORE; + break; + + default: + usage(); + } + + agent = new BugSpotAgent(); + try { + switch (debugeeType) { + case DEBUGEE_PID: + err.println("Attaching to process ID " + pid + ", please wait..."); + agent.attach(pid); + break; + + case DEBUGEE_CORE: + err.println("Attaching to core " + coreFileName + + " from executable " + executableName + ", please wait..."); + agent.attach(executableName, coreFileName); + break; + + case DEBUGEE_REMOTE: + err.println("Attaching to remote server " + remoteServer + ", please wait..."); + agent.attach(remoteServer); + break; + } + } + catch (DebuggerException e) { + switch (debugeeType) { + case DEBUGEE_PID: + err.print("Error attaching to process: "); + break; + + case DEBUGEE_CORE: + err.print("Error attaching to core file: "); + break; + + case DEBUGEE_REMOTE: + err.print("Error attaching to remote server: "); + break; + } + if (e.getMessage() != null) { + err.print(e.getMessage()); + } + err.println(); + System.exit(1); + } + + err.println("Debugger attached successfully."); + + boolean isJava = agent.isJavaMode(); + if (isJava) { + VM vm = VM.getVM(); + if (vm.isCore()) { + err.println("Core build detected."); + } else if (vm.isClientCompiler()) { + err.println("Client compiler detected."); + } else if (vm.isServerCompiler()) { + err.println("Server compiler detected."); + } else { + throw new RuntimeException("Fatal error: " + + "should have been able to detect core/C1/C2 build"); + } + + String version = vm.getVMRelease(); + if (version != null) { + err.print("JVM version is "); + err.println(version); + } + + run(); + } else { // not a java process or core + if (requiresVM()) { + err.println(getName() + " requires a java VM process/core!"); + } else { + run(); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ByteCodeRewriter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ByteCodeRewriter.java new file mode 100644 index 00000000000..fd386a2ecb5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ByteCodeRewriter.java @@ -0,0 +1,167 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools.jcore; + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; + +public class ByteCodeRewriter +{ + private Method method; + private ConstantPool cpool; + private ConstantPoolCache cpCache; + private byte[] code; + private Bytes bytes; + + public static final boolean DEBUG = false; + private static final int jintSize = 4; + + protected void debugMessage(String message) { + System.out.println(message); + } + + public ByteCodeRewriter(Method method, ConstantPool cpool, byte[] code) { + this.method = method; + this.cpool = cpool; + this.cpCache = cpool.getCache(); + this.code = code; + this.bytes = VM.getVM().getBytes(); + + } + + protected short getConstantPoolIndex(int bci) { + // get ConstantPool index from ConstantPoolCacheIndex at given bci + short cpCacheIndex = method.getBytecodeShortArg(bci); + if (cpCache == null) { + return cpCacheIndex; + } else { + // change byte-ordering and go via cache + return (short) cpCache.getEntryAt((int) (0xFFFF & bytes.swapShort(cpCacheIndex))).getConstantPoolIndex(); + } + } + + static private void writeShort(byte[] buf, int index, short value) { + buf[index] = (byte) ((value >> 8) & 0x00FF); + buf[index + 1] = (byte) (value & 0x00FF); + } + + public void rewrite() { + int bytecode = Bytecodes._illegal; + int hotspotcode = Bytecodes._illegal; + int len = 0; + + for (int bci = 0; bci < code.length;) { + hotspotcode = Bytecodes.codeAt(method, bci); + bytecode = Bytecodes.javaCode(hotspotcode); + + if (Assert.ASSERTS_ENABLED) { + int code_from_buffer = 0xFF & code[bci]; + Assert.that(code_from_buffer == hotspotcode + || code_from_buffer == Bytecodes._breakpoint, + "Unexpected bytecode found in method bytecode buffer!"); + } + + // update the code buffer hotspot specific bytecode with the jvm bytecode + code[bci] = (byte) (0xFF & bytecode); + + // RewriteFrequentPairs + if(hotspotcode == Bytecodes._fast_iaccess_0 || + hotspotcode == Bytecodes._fast_aaccess_0 || + hotspotcode == Bytecodes._fast_faccess_0) { + // rewrite next bytecode as _getfield + bci++; + code[bci] = (byte) (0xFF & Bytecodes._getfield); + bytecode = Bytecodes._getfield; + hotspotcode = Bytecodes._getfield; + } else if (hotspotcode == Bytecodes._fast_iload2) { + // rewrite next bytecode as _iload + bci++; + code[bci] = (byte) (0xFF & Bytecodes._iload); + bytecode = Bytecodes._iload; + hotspotcode = Bytecodes._iload; + } else if (hotspotcode == Bytecodes._fast_icaload) { + // rewrite next bytecode as _caload + bci++; + code[bci] = (byte) (0xFF & Bytecodes._caload); + bytecode = Bytecodes._caload; + bytecode = Bytecodes._caload; + } + + short cpoolIndex = 0; + switch (bytecode) { + // bytecodes with ConstantPoolCache index + case Bytecodes._getstatic: + case Bytecodes._putstatic: + case Bytecodes._getfield: + case Bytecodes._putfield: + case Bytecodes._invokevirtual: + case Bytecodes._invokespecial: + case Bytecodes._invokestatic: + case Bytecodes._invokeinterface: { + cpoolIndex = getConstantPoolIndex(bci + 1); + writeShort(code, bci + 1, cpoolIndex); + break; + } + } + + len = Bytecodes.lengthFor(bytecode); + if (len <= 0) len = Bytecodes.lengthAt(method, bci); + + if (DEBUG) { + String operand = ""; + switch (len) { + case 2: + operand += code[bci + 1]; + break; + case 3: + operand += (cpoolIndex != 0)? cpoolIndex : + method.getBytecodeShortArg(bci + 1); + break; + case 5: + operand += method.getBytecodeIntArg(bci + 1); + break; + } + + // the operand following # is not quite like javap output. + // in particular, for goto & goto_w, the operand is PC relative + // offset for jump. Javap adds relative offset with current PC + // to give absolute bci to jump to. + + String message = "\t\t" + bci + " " + Bytecodes.name(bytecode); + if (hotspotcode != bytecode) + message += " [" + Bytecodes.name(hotspotcode) + "]"; + if (operand != "") + message += " #" + operand; + + if (DEBUG) debugMessage(message); + } + + bci += len; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassDump.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassDump.java new file mode 100644 index 00000000000..01770d9e2ee --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassDump.java @@ -0,0 +1,116 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools.jcore; + +import java.io.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.tools.*; + +public class ClassDump extends Tool { + private ClassFilter classFilter; + private String outputDirectory; + + public void run() { + // Ready to go with the database... + try { + + // load class filters + + String filterClassName = System.getProperty("sun.jvm.hotspot.tools.jcore.filter"); + if (filterClassName != null) { + try { + Class filterClass = Class.forName(filterClassName); + classFilter = (ClassFilter) filterClass.newInstance(); + } catch(Exception exp) { + System.err.println("Warning: Can not create class filter!"); + } + } + + outputDirectory = System.getProperty("sun.jvm.hotspot.tools.jcore.outputDir"); + if (outputDirectory == null) + outputDirectory = "."; + + // walk through the system dictionary + SystemDictionary dict = VM.getVM().getSystemDictionary(); + dict.classesDo(new SystemDictionary.ClassVisitor() { + public void visit(Klass k) { + if (k instanceof InstanceKlass) + dumpKlass((InstanceKlass) k); + } + }); + } + catch (AddressException e) { + System.err.println("Error accessing address 0x" + + Long.toHexString(e.getAddress())); + e.printStackTrace(); + } + } + + public String getName() { + return "jcore"; + } + + private void dumpKlass(InstanceKlass kls) { + if (classFilter != null && ! classFilter.canInclude(kls) ) { + return; + } + + String klassName = kls.getName().asString(); + klassName = klassName.replace('/', File.separatorChar); + int index = klassName.lastIndexOf(File.separatorChar); + File dir = null; + if (index != -1) { + String dirName = klassName.substring(0, index); + dir = new File(outputDirectory, dirName); + } else { + dir = new File(outputDirectory); + } + + dir.mkdirs(); + File f = new File(dir, klassName.substring(klassName.lastIndexOf(File.separatorChar) + 1) + + ".class"); + try { + f.createNewFile(); + OutputStream os = new BufferedOutputStream(new FileOutputStream(f)); + try { + ClassWriter cw = new ClassWriter(kls, os); + cw.write(); + } finally { + os.close(); + } + } catch(IOException exp) { + exp.printStackTrace(); + } + } + + public static void main(String[] args) { + ClassDump cd = new ClassDump(); + cd.start(args); + cd.stop(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassFilter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassFilter.java new file mode 100644 index 00000000000..6bfd1bce494 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassFilter.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools.jcore; + +import sun.jvm.hotspot.oops.InstanceKlass; + +public interface ClassFilter +{ + public boolean canInclude(InstanceKlass kls); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java new file mode 100644 index 00000000000..7750b2a0d58 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java @@ -0,0 +1,717 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools.jcore; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +public class ClassWriter implements /* imports */ ClassConstants +{ + public static final boolean DEBUG = false; + + protected void debugMessage(String message) { + System.out.println(message); + } + + protected InstanceKlass klass; + protected DataOutputStream dos; + protected ConstantPool cpool; + protected boolean is15Format; + + // Map between class name to index of type CONSTANT_Class + protected Map classToIndex = new HashMap(); + + // Map between any modified UTF-8 and it's constant pool index. + protected Map utf8ToIndex = new HashMap(); + + // constant pool index for attribute names. + + protected short _sourceFileIndex; + protected short _innerClassesIndex; + protected short _syntheticIndex; + protected short _deprecatedIndex; + protected short _constantValueIndex; + protected short _codeIndex; + protected short _exceptionsIndex; + protected short _lineNumberTableIndex; + protected short _localVariableTableIndex; + protected short _signatureIndex; + + protected static int extractHighShortFromInt(int val) { + return (val >> 16) & 0xFFFF; + } + + protected static int extractLowShortFromInt(int val) { + return val & 0xFFFF; + } + + public ClassWriter(InstanceKlass kls, OutputStream os) { + klass = kls; + dos = new DataOutputStream(os); + cpool = klass.getConstants(); + is15Format = is15ClassFile(); + } + + public void write() throws IOException { + if (DEBUG) debugMessage("class name = " + klass.getName().asString()); + + // write magic + dos.writeInt(0xCAFEBABE); + + writeVersion(is15Format); + writeConstantPool(); + writeClassAccessFlags(); + writeThisClass(); + writeSuperClass(); + writeInterfaces(); + writeFields(); + writeMethods(); + writeClassAttributes(); + + // flush output + dos.flush(); + } + + protected boolean is15ClassFile() { + // if klass has generic signature, then it is 1.5 class file. + if (klass.getGenericSignature() != null) { + return true; + } + + // if atleast one method has generic signature + // , then we have 1.5 class file. + ObjArray methods = klass.getMethods(); + final int numMethods = (int) methods.getLength(); + for (int m = 0; m < numMethods; m++) { + Method curMethod = (Method) methods.getObjAt(m); + if (curMethod.getGenericSignature() != null) { + return true; + } + } + + // if atleast one field has non-zero generic signature index, then we have + // 1.5 class file + TypeArray fields = klass.getFields(); + final int numFields = (int) fields.getLength(); + for (int f = 0; f < numFields; f += InstanceKlass.NEXT_OFFSET) { + short genSigIndex = fields.getShortAt(f + InstanceKlass.GENERIC_SIGNATURE_INDEX_OFFSET); + if (genSigIndex != (short)0) return true; + } + + return false; + } + + protected void writeVersion(boolean is15Format) throws IOException { + if (is15Format) { + dos.writeShort(MINOR_VERSION); + dos.writeShort(MAJOR_VERSION); + } else { + dos.writeShort(MINOR_VERSION_OLD); + dos.writeShort(MAJOR_VERSION_OLD); + } + } + + protected void writeConstantPool() throws IOException { + final TypeArray tags = cpool.getTags(); + final long len = tags.getLength(); + dos.writeShort((short) len); + + if (DEBUG) debugMessage("constant pool length = " + len); + + int ci = 0; // constant pool index + + // collect all modified UTF-8 Strings from Constant Pool + + for (ci = 1; ci < len; ci++) { + byte cpConstType = tags.getByteAt(ci); + if(cpConstType == JVM_CONSTANT_Utf8) { + Symbol sym = cpool.getSymbolAt(ci); + utf8ToIndex.put(sym.asString(), new Short((short) ci)); + } + else if(cpConstType == JVM_CONSTANT_Long || + cpConstType == JVM_CONSTANT_Double) { + ci++; + } + } + + // remember index of attribute name modified UTF-8 strings + + // class attributes + Short sourceFileIndex = (Short) utf8ToIndex.get("SourceFile"); + _sourceFileIndex = (sourceFileIndex != null)? sourceFileIndex.shortValue() : 0; + if (DEBUG) debugMessage("SourceFile index = " + _sourceFileIndex); + + Short innerClassesIndex = (Short) utf8ToIndex.get("InnerClasses"); + _innerClassesIndex = (innerClassesIndex != null)? innerClassesIndex.shortValue() : 0; + if (DEBUG) debugMessage("InnerClasses index = " + _innerClassesIndex); + + // field attributes + Short constantValueIndex = (Short) utf8ToIndex.get("ConstantValue"); + _constantValueIndex = (constantValueIndex != null)? + constantValueIndex.shortValue() : 0; + if (DEBUG) debugMessage("ConstantValue index = " + _constantValueIndex); + + Short syntheticIndex = (Short) utf8ToIndex.get("Synthetic"); + _syntheticIndex = (syntheticIndex != null)? syntheticIndex.shortValue() : 0; + if (DEBUG) debugMessage("Synthetic index = " + _syntheticIndex); + + Short deprecatedIndex = (Short) utf8ToIndex.get("Deprecated"); + _deprecatedIndex = (deprecatedIndex != null)? deprecatedIndex.shortValue() : 0; + if (DEBUG) debugMessage("Deprecated index = " + _deprecatedIndex); + + // method attributes + Short codeIndex = (Short) utf8ToIndex.get("Code"); + _codeIndex = (codeIndex != null)? codeIndex.shortValue() : 0; + if (DEBUG) debugMessage("Code index = " + _codeIndex); + + Short exceptionsIndex = (Short) utf8ToIndex.get("Exceptions"); + _exceptionsIndex = (exceptionsIndex != null)? exceptionsIndex.shortValue() : 0; + if (DEBUG) debugMessage("Exceptions index = " + _exceptionsIndex); + + // Short syntheticIndex = (Short) utf8ToIndex.get("Synthetic"); + // Short deprecatedIndex = (Short) utf8ToIndex.get("Deprecated"); + + // Code attributes + Short lineNumberTableIndex = (Short) utf8ToIndex.get("LineNumberTable"); + _lineNumberTableIndex = (lineNumberTableIndex != null)? + lineNumberTableIndex.shortValue() : 0; + if (DEBUG) debugMessage("LineNumberTable index = " + _lineNumberTableIndex); + + Short localVariableTableIndex = (Short) utf8ToIndex.get("LocalVariableTable"); + _localVariableTableIndex = (localVariableTableIndex != null)? + localVariableTableIndex.shortValue() : 0; + if (DEBUG) debugMessage("LocalVariableTable index = " + _localVariableTableIndex); + + Short signatureIdx = (Short) utf8ToIndex.get("Signature"); + _signatureIndex = (signatureIdx != null)? signatureIdx.shortValue() : 0; + if (DEBUG) debugMessage("Signature index = " + _signatureIndex); + + for(ci = 1; ci < len; ci++) { + // write cp_info + // write constant type + byte cpConstType = tags.getByteAt(ci); + switch(cpConstType) { + case JVM_CONSTANT_Utf8: { + dos.writeByte(cpConstType); + Symbol sym = cpool.getSymbolAt(ci); + dos.writeShort((short)sym.getLength()); + dos.write(sym.asByteArray()); + if (DEBUG) debugMessage("CP[" + ci + "] = modified UTF-8 " + sym.asString()); + break; + } + + case JVM_CONSTANT_Unicode: + throw new IllegalArgumentException("Unicode constant!"); + + case JVM_CONSTANT_Integer: + dos.writeByte(cpConstType); + dos.writeInt(cpool.getIntAt(ci)); + if (DEBUG) debugMessage("CP[" + ci + "] = int " + cpool.getIntAt(ci)); + break; + + case JVM_CONSTANT_Float: + dos.writeByte(cpConstType); + dos.writeFloat(cpool.getFloatAt(ci)); + if (DEBUG) debugMessage("CP[" + ci + "] = float " + cpool.getFloatAt(ci)); + break; + + case JVM_CONSTANT_Long: { + dos.writeByte(cpConstType); + long l = cpool.getLongAt(ci); + // long entries occupy two pool entries + ci++; + dos.writeLong(l); + break; + } + + case JVM_CONSTANT_Double: + dos.writeByte(cpConstType); + dos.writeDouble(cpool.getDoubleAt(ci)); + // double entries occupy two pool entries + ci++; + break; + + case JVM_CONSTANT_Class: { + dos.writeByte(cpConstType); + // Klass already resolved. ConstantPool constains klassOop. + Klass refKls = (Klass) cpool.getObjAt(ci); + String klassName = refKls.getName().asString(); + + Short s = (Short) utf8ToIndex.get(klassName); + classToIndex.put(klassName, new Short((short)ci)); + dos.writeShort(s.shortValue()); + if (DEBUG) debugMessage("CP[" + ci + "] = class " + s); + break; + } + + // case JVM_CONSTANT_ClassIndex: + case JVM_CONSTANT_UnresolvedClass: { + dos.writeByte(JVM_CONSTANT_Class); + String klassName = cpool.getSymbolAt(ci).asString(); + + Short s = (Short) utf8ToIndex.get(klassName); + classToIndex.put(klassName, new Short((short) ci)); + + dos.writeShort(s.shortValue()); + if (DEBUG) debugMessage("CP[" + ci + "] = class " + s); + break; + } + + case JVM_CONSTANT_String: { + dos.writeByte(cpConstType); + String str = OopUtilities.stringOopToString(cpool.getObjAt(ci)); + Short s = (Short) utf8ToIndex.get(str); + dos.writeShort(s.shortValue()); + if (DEBUG) debugMessage("CP[" + ci + "] = string " + s); + break; + } + + // case JVM_CONSTANT_StringIndex: + case JVM_CONSTANT_UnresolvedString: { + dos.writeByte(JVM_CONSTANT_String); + String val = cpool.getSymbolAt(ci).asString(); + + Short s = (Short) utf8ToIndex.get(val); + dos.writeShort(s.shortValue()); + if (DEBUG) debugMessage("CP[" + ci + "] = string " + s); + break; + } + + // all external, internal method/field references + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: { + dos.writeByte(cpConstType); + int value = cpool.getIntAt(ci); + short klassIndex = (short) extractLowShortFromInt(value); + short nameAndTypeIndex = (short) extractHighShortFromInt(value); + dos.writeShort(klassIndex); + dos.writeShort(nameAndTypeIndex); + if (DEBUG) debugMessage("CP[" + ci + "] = ref klass = " + + klassIndex + ", N&T = " + nameAndTypeIndex); + break; + } + + case JVM_CONSTANT_NameAndType: { + dos.writeByte(cpConstType); + int value = cpool.getIntAt(ci); + short nameIndex = (short) extractLowShortFromInt(value); + short signatureIndex = (short) extractHighShortFromInt(value); + dos.writeShort(nameIndex); + dos.writeShort(signatureIndex); + if (DEBUG) debugMessage("CP[" + ci + "] = N&T name = " + nameIndex + + ", type = " + signatureIndex); + break; + } + } // switch + } + } + + protected void writeClassAccessFlags() throws IOException { + int flags = (int)(klass.getAccessFlags() & JVM_RECOGNIZED_CLASS_MODIFIERS); + dos.writeShort((short)flags); + } + + protected void writeThisClass() throws IOException { + String klassName = klass.getName().asString(); + Short index = (Short) classToIndex.get(klassName); + dos.writeShort(index.shortValue()); + if (DEBUG) debugMessage("this class = " + index); + } + + protected void writeSuperClass() throws IOException { + Klass superKlass = klass.getSuper(); + if (superKlass != null) { // is not java.lang.Object + String superName = superKlass.getName().asString(); + Short index = (Short) classToIndex.get(superName); + if (DEBUG) debugMessage("super class = " + index); + dos.writeShort(index.shortValue()); + } else { + dos.writeShort(0); // no super class + } + } + protected void writeInterfaces() throws IOException { + ObjArray interfaces = klass.getLocalInterfaces(); + final int len = (int) interfaces.getLength(); + + if (DEBUG) debugMessage("number of interfaces = " + len); + + // write interfaces count + dos.writeShort((short) len); + for (int i = 0; i < len; i++) { + Klass k = (Klass) interfaces.getObjAt(i); + Short index = (Short) classToIndex.get(k.getName().asString()); + dos.writeShort(index.shortValue()); + if (DEBUG) debugMessage("\t" + index); + } + } + + protected void writeFields() throws IOException { + TypeArray fields = klass.getFields(); + final int length = (int) fields.getLength(); + + // write number of fields + dos.writeShort((short) (length / InstanceKlass.NEXT_OFFSET) ); + + if (DEBUG) debugMessage("number of fields = " + + length/InstanceKlass.NEXT_OFFSET); + + for (int index = 0; index < length; index += InstanceKlass.NEXT_OFFSET) { + short accessFlags = fields.getShortAt(index + InstanceKlass.ACCESS_FLAGS_OFFSET); + dos.writeShort(accessFlags & (short) JVM_RECOGNIZED_FIELD_MODIFIERS); + + short nameIndex = fields.getShortAt(index + InstanceKlass.NAME_INDEX_OFFSET); + dos.writeShort(nameIndex); + + short signatureIndex = fields.getShortAt(index + InstanceKlass.SIGNATURE_INDEX_OFFSET); + dos.writeShort(signatureIndex); + if (DEBUG) debugMessage("\tfield name = " + nameIndex + ", signature = " + signatureIndex); + + short fieldAttributeCount = 0; + boolean isSyn = isSynthetic(accessFlags); + if (isSyn) + fieldAttributeCount++; + + short initvalIndex = fields.getShortAt(index + InstanceKlass.INITVAL_INDEX_OFFSET); + if (initvalIndex != 0) + fieldAttributeCount++; + + short genSigIndex = fields.getShortAt(index + InstanceKlass.GENERIC_SIGNATURE_INDEX_OFFSET); + if (genSigIndex != 0) + fieldAttributeCount++; + + dos.writeShort(fieldAttributeCount); + + // write synthetic, if applicable + if (isSyn) + writeSynthetic(); + + if (initvalIndex != 0) { + dos.writeShort(_constantValueIndex); + dos.writeInt(2); + dos.writeShort(initvalIndex); + if (DEBUG) debugMessage("\tfield init value = " + initvalIndex); + } + + if (genSigIndex != 0) { + dos.writeShort(_signatureIndex); + dos.writeInt(2); + dos.writeShort(genSigIndex); + if (DEBUG) debugMessage("\tfield generic signature index " + genSigIndex); + } + } + } + + protected boolean isSynthetic(short accessFlags) { + return (accessFlags & (short) JVM_ACC_SYNTHETIC) != 0; + } + + protected void writeSynthetic() throws IOException { + dos.writeShort(_syntheticIndex); + dos.writeInt(0); + } + + protected void writeMethods() throws IOException { + ObjArray methods = klass.getMethods(); + final int len = (int) methods.getLength(); + // write number of methods + dos.writeShort((short) len); + if (DEBUG) debugMessage("number of methods = " + len); + for (int m = 0; m < len; m++) { + writeMethod((Method) methods.getObjAt(m)); + } + } + + protected void writeMethod(Method m) throws IOException { + long accessFlags = m.getAccessFlags(); + dos.writeShort((short) (accessFlags & JVM_RECOGNIZED_METHOD_MODIFIERS)); + dos.writeShort((short) m.getNameIndex()); + dos.writeShort((short) m.getSignatureIndex()); + if (DEBUG) debugMessage("\tmethod name = " + m.getNameIndex() + ", signature = " + + m.getSignatureIndex()); + + final boolean isNative = ((accessFlags & JVM_ACC_NATIVE) != 0); + final boolean isAbstract = ((accessFlags & JVM_ACC_ABSTRACT) != 0); + + short methodAttributeCount = 0; + + final boolean isSyn = isSynthetic((short)accessFlags); + if (isSyn) + methodAttributeCount++; + + final boolean hasCheckedExceptions = m.hasCheckedExceptions(); + if (hasCheckedExceptions) + methodAttributeCount++; + + final boolean isCodeAvailable = (!isNative) && (!isAbstract); + if (isCodeAvailable) + methodAttributeCount++; + + final boolean isGeneric = (m.getGenericSignature() != null); + if (isGeneric) + methodAttributeCount++; + + dos.writeShort(methodAttributeCount); + if (DEBUG) debugMessage("\tmethod attribute count = " + methodAttributeCount); + + if (isSyn) { + if (DEBUG) debugMessage("\tmethod is synthetic"); + writeSynthetic(); + } + + if (hasCheckedExceptions) { + CheckedExceptionElement[] exceptions = m.getCheckedExceptions(); + dos.writeShort(_exceptionsIndex); + + int attrSize = 2 /* number_of_exceptions */ + + exceptions.length * 2 /* exception_index */; + dos.writeInt(attrSize); + dos.writeShort(exceptions.length); + if (DEBUG) debugMessage("\tmethod has " + exceptions.length + + " checked exception(s)"); + for (int e = 0; e < exceptions.length; e++) { + short cpIndex = (short) exceptions[e].getClassCPIndex(); + dos.writeShort(cpIndex); + } + } + + if (isCodeAvailable) { + byte[] code = m.getByteCode(); + short codeAttrCount = 0; + int codeSize = 2 /* max_stack */ + + 2 /* max_locals */ + + 4 /* code_length */ + + code.length /* code */ + + 2 /* exp. table len. */ + + 2 /* code attr. count */; + + TypeArray exceptionTable = m.getExceptionTable(); + final int exceptionTableLen = (int) exceptionTable.getLength(); + if (exceptionTableLen != 0) { + if (DEBUG) debugMessage("\tmethod has exception table"); + codeSize += (exceptionTableLen / 4) /* exception table is 4-tuple array */ + * (2 /* start_pc */ + + 2 /* end_pc */ + + 2 /* handler_pc */ + + 2 /* catch_type */); + } + + boolean hasLineNumberTable = m.hasLineNumberTable(); + LineNumberTableElement[] lineNumberTable = null; + int lineNumberAttrLen = 0; + + if (hasLineNumberTable) { + if (DEBUG) debugMessage("\tmethod has line number table"); + lineNumberTable = m.getLineNumberTable(); + if (DEBUG) debugMessage("\t\tline table length = " + lineNumberTable.length); + + lineNumberAttrLen = 2 /* line number table length */ + + lineNumberTable.length * (2 /* start_pc */ + 2 /* line_number */); + + codeSize += 2 /* line number table attr index */ + + 4 /* line number table attr length */ + + lineNumberAttrLen; + + if (DEBUG) debugMessage("\t\tline number table attr size = " + + lineNumberAttrLen); + + codeAttrCount++; + } + + boolean hasLocalVariableTable = m.hasLocalVariableTable(); + LocalVariableTableElement[] localVariableTable = null; + int localVarAttrLen = 0; + + if (hasLocalVariableTable) { + if (DEBUG) debugMessage("\tmethod has local variable table"); + localVariableTable = m.getLocalVariableTable(); + if (DEBUG) debugMessage("\t\tlocal variable table length = " + + localVariableTable.length); + localVarAttrLen = + 2 /* local variable table length */ + + localVariableTable.length * ( 2 /* start_pc */ + + 2 /* length */ + + 2 /* name_index */ + + 2 /* signature_index */ + + 2 /* variable index */ ); + + if (DEBUG) debugMessage("\t\tlocal variable attr size = " + + localVarAttrLen); + + codeSize += 2 /* local variable table attr index */ + + 4 /* local variable table attr length */ + + localVarAttrLen; + + codeAttrCount++; + } + + // fix ConstantPoolCache indices to ConstantPool indices. + rewriteByteCode(m, code); + + // start writing Code + + dos.writeShort(_codeIndex); + + dos.writeInt(codeSize); + if (DEBUG) debugMessage("\tcode attribute length = " + codeSize); + + dos.writeShort((short) m.getMaxStack()); + if (DEBUG) debugMessage("\tmax stack = " + m.getMaxStack()); + + dos.writeShort((short) m.getMaxLocals()); + if (DEBUG) debugMessage("\tmax locals = " + m.getMaxLocals()); + + dos.writeInt(code.length); + if (DEBUG) debugMessage("\tcode size = " + code.length); + + dos.write(code); + + // write exception table size + dos.writeShort((short) (exceptionTableLen / 4)); + if (DEBUG) debugMessage("\texception table length = " + (exceptionTableLen / 4)); + + if (exceptionTableLen != 0) { + for (int e = 0; e < exceptionTableLen; e += 4) { + dos.writeShort((short) exceptionTable.getIntAt(e)); + dos.writeShort((short) exceptionTable.getIntAt(e + 1)); + dos.writeShort((short) exceptionTable.getIntAt(e + 2)); + dos.writeShort((short) exceptionTable.getIntAt(e + 3)); + } + } + + dos.writeShort((short)codeAttrCount); + if (DEBUG) debugMessage("\tcode attribute count = " + codeAttrCount); + + // write LineNumberTable, if available. + if (hasLineNumberTable) { + dos.writeShort(_lineNumberTableIndex); + dos.writeInt(lineNumberAttrLen); + dos.writeShort((short) lineNumberTable.length); + for (int l = 0; l < lineNumberTable.length; l++) { + dos.writeShort((short) lineNumberTable[l].getStartBCI()); + dos.writeShort((short) lineNumberTable[l].getLineNumber()); + } + } + + // write LocalVariableTable, if available. + if (hasLocalVariableTable) { + dos.writeShort((short) _localVariableTableIndex); + dos.writeInt(localVarAttrLen); + dos.writeShort((short) localVariableTable.length); + for (int l = 0; l < localVariableTable.length; l++) { + dos.writeShort((short) localVariableTable[l].getStartBCI()); + dos.writeShort((short) localVariableTable[l].getLength()); + dos.writeShort((short) localVariableTable[l].getNameCPIndex()); + dos.writeShort((short) localVariableTable[l].getDescriptorCPIndex()); + dos.writeShort((short) localVariableTable[l].getSlot()); + } + } + } + + if (isGeneric) { + writeGenericSignature(m.getGenericSignature().asString()); + } + } + + protected void rewriteByteCode(Method m, byte[] code) { + ByteCodeRewriter r = new ByteCodeRewriter(m, cpool, code); + r.rewrite(); + } + + protected void writeGenericSignature(String signature) throws IOException { + dos.writeShort(_signatureIndex); + if (DEBUG) debugMessage("signature attribute = " + _signatureIndex); + dos.writeInt(2); + Short index = (Short) utf8ToIndex.get(signature); + dos.writeShort(index.shortValue()); + if (DEBUG) debugMessage("generic signature = " + index); + } + + protected void writeClassAttributes() throws IOException { + final long flags = klass.getAccessFlags(); + final boolean isSyn = isSynthetic((short) flags); + + // check for source file + short classAttributeCount = 0; + + if (isSyn) + classAttributeCount++; + + Symbol sourceFileName = klass.getSourceFileName(); + if (sourceFileName != null) + classAttributeCount++; + + Symbol genericSignature = klass.getGenericSignature(); + if (genericSignature != null) + classAttributeCount++; + + TypeArray innerClasses = klass.getInnerClasses(); + final int numInnerClasses = (int) (innerClasses.getLength() / 4); + if (numInnerClasses != 0) + classAttributeCount++; + + dos.writeShort(classAttributeCount); + if (DEBUG) debugMessage("class attribute count = " + classAttributeCount); + + if (isSyn) + writeSynthetic(); + + // write SourceFile, if any + if (sourceFileName != null) { + dos.writeShort(_sourceFileIndex); + if (DEBUG) debugMessage("source file attribute = " + _sourceFileIndex); + dos.writeInt(2); + Short index = (Short) utf8ToIndex.get(sourceFileName.asString()); + dos.writeShort(index.shortValue()); + if (DEBUG) debugMessage("source file name = " + index); + } + + // write Signature, if any + if (genericSignature != null) { + writeGenericSignature(genericSignature.asString()); + } + + // write inner classes, if any + if (numInnerClasses != 0) { + dos.writeShort(_innerClassesIndex); + final int innerAttrLen = 2 /* number_of_inner_classes */ + + numInnerClasses * ( + 2 /* inner_class_info_index */ + + 2 /* outer_class_info_index */ + + 2 /* inner_class_name_index */ + + 2 /* inner_class_access_flags */); + dos.writeInt(innerAttrLen); + + dos.writeShort(numInnerClasses); + if (DEBUG) debugMessage("class has " + numInnerClasses + " inner class entries"); + + for (int index = 0; index < numInnerClasses * 4; index++) { + dos.writeShort(innerClasses.getShortAt(index)); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/NameFilter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/NameFilter.java new file mode 100644 index 00000000000..874ededae5a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/NameFilter.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools.jcore; + +import sun.jvm.hotspot.oops.*; +import java.util.regex.*; + +public class NameFilter implements ClassFilter +{ + public Pattern includePattern; + + public NameFilter() { + this(System.getProperty("sun.jvm.hotspot.tools.jcore.NameFilter.pattern")); + } + + public NameFilter(String pattern) { + if (pattern == null) pattern = "*"; + includePattern = Pattern.compile(pattern); + } + + public boolean canInclude(InstanceKlass kls) { + String klassName = kls.getName().asString().replace('/', '.'); + Matcher m = includePattern.matcher(klassName); + return m.matches(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java new file mode 100644 index 00000000000..6adc875baad --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools.jcore; + +import sun.jvm.hotspot.oops.*; +import java.util.*; + +public class PackageNameFilter implements ClassFilter +{ + public Object[] pkgList; + + public PackageNameFilter() { + // give comma separated list of package names to include + this(System.getProperty("sun.jvm.hotspot.tools.jcore.PackageNameFilter.pkgList")); + } + + public PackageNameFilter(String pattern) { + try { + StringTokenizer st = new StringTokenizer(pattern, ","); + List l = new LinkedList(); + while (st.hasMoreTokens()) { + l.add(st.nextToken()); + } + pkgList = l.toArray(); + } catch (Exception exp) { + exp.printStackTrace(); + } + } + + public boolean canInclude(InstanceKlass kls) { + String klassName = kls.getName().asString().replace('/', '.'); + final int len = pkgList.length; + if (len == 0) + return true; + for (int i=0; i < len; i++) + if (klassName.startsWith((String) pkgList[i] )) return true; + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/soql/JSDB.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/soql/JSDB.java new file mode 100644 index 00000000000..05652ad90b0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/soql/JSDB.java @@ -0,0 +1,54 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools.soql; + +import sun.jvm.hotspot.tools.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.utilities.soql.*; + +/** This is command line JavaScript debugger console */ +public class JSDB extends Tool { + public static void main(String[] args) { + JSDB jsdb = new JSDB(); + jsdb.start(args); + jsdb.stop(); + } + + public void run() { + JSJavaScriptEngine engine = new JSJavaScriptEngine() { + private ObjectReader objReader = new ObjectReader(); + private JSJavaFactory factory = new JSJavaFactoryImpl(); + + public ObjectReader getObjectReader() { + return objReader; + } + + public JSJavaFactory getJSJavaFactory() { + return factory; + } + }; + engine.startConsole(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/soql/SOQL.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/soql/SOQL.java new file mode 100644 index 00000000000..7f41327ef90 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/soql/SOQL.java @@ -0,0 +1,218 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.tools.soql; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.tools.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.utilities.soql.*; + +/** + This is command line SOQL (Simple Object Query Language) interpreter. +*/ + +public class SOQL extends Tool { + public static void main(String[] args) { + SOQL soql = new SOQL(); + soql.start(args); + soql.stop(); + } + + protected SOQLEngine soqlEngine; + protected BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + protected PrintStream out = System.out; + static protected String prompt = "soql> "; + static protected String secondPrompt = "> "; + + public void run() { + soqlEngine = SOQLEngine.getEngine(); + while (true) { + try { + out.print(prompt); + String line = in.readLine(); + if (line == null) { + return; + } + StringTokenizer st = new StringTokenizer(line); + if (st.hasMoreTokens()) { + String cmd = st.nextToken(); + if (cmd.equals("select")) { + handleSelect(line); + } else if (cmd.equals("classes")) { + handleClasses(line); + } else if (cmd.equals("class")) { + handleClass(line); + } else if (cmd.equals("object")) { + handleObject(line); + } else if (cmd.equals("quit")) { + out.println("Bye!"); + return; + } else if (cmd.equals("")) { + // do nothing ... + } else { + handleUnknown(line); + } + } + } catch (IOException e) { + e.printStackTrace(); + return; + } + } + } + + protected void handleSelect(String query) { + StringBuffer buf = new StringBuffer(query); + String tmp = null; + while (true) { + out.print(secondPrompt); + try { + tmp = in.readLine(); + } catch (IOException ioe) { + break; + } + if (tmp.equals("") || tmp.equals("go")) + break; + buf.append('\n'); + buf.append(tmp); + } + query = buf.toString(); + + try { + soqlEngine.executeQuery(query, + new ObjectVisitor() { + public void visit(Object o) { + if (o != null && o instanceof JSJavaObject) { + String oopAddr = ((JSJavaObject)o).getOop().getHandle().toString(); + out.println(oopAddr); + } else { + out.println((o == null)? "null" : o.toString()); + } + } + }); + } catch (SOQLException se) { + se.printStackTrace(); + } + } + + protected void handleClasses(String line) { + // just list all InstanceKlasses + InstanceKlass[] klasses = SystemDictionaryHelper.getAllInstanceKlasses(); + for (int i = 0; i < klasses.length; i++) { + out.print(klasses[i].getName().asString().replace('/', '.')); + out.print(" @"); + out.println(klasses[i].getHandle()); + } + } + + protected void handleClass(String line) { + StringTokenizer st = new StringTokenizer(line); + st.nextToken(); // ignore "class" + if (st.hasMoreTokens()) { + String className = st.nextToken(); + InstanceKlass klass = SystemDictionaryHelper.findInstanceKlass(className); + if (klass == null) { + out.println("class " + className + " not found"); + } else { + // klass.iterate(new OopPrinter(out), true); + + // base class + InstanceKlass base = (InstanceKlass) klass.getSuper(); + if (base != null) { + out.println("super"); + out.print("\t"); + out.println(base.getName().asString().replace('/', '.')); + } + + // list immediate fields only + TypeArray fields = klass.getFields(); + int numFields = (int) fields.getLength(); + ConstantPool cp = klass.getConstants(); + out.println("fields"); + if (numFields != 0) { + for (int f = 0; f < numFields; f += InstanceKlass.NEXT_OFFSET) { + int nameIndex = fields.getShortAt(f + InstanceKlass.NAME_INDEX_OFFSET); + int sigIndex = fields.getShortAt(f + InstanceKlass.SIGNATURE_INDEX_OFFSET); + Symbol f_name = cp.getSymbolAt(nameIndex); + Symbol f_sig = cp.getSymbolAt(sigIndex); + StringBuffer sigBuf = new StringBuffer(); + new SignatureConverter(f_sig, sigBuf).dispatchField(); + out.print('\t'); + out.print(sigBuf.toString().replace('/', '.')); + out.print(' '); + out.println(f_name.asString()); + } + } else { + out.println("\tno fields in this class"); + } + } + } else { + out.println("usage: class "); + } + } + + protected Oop getOopAtAddress(Address addr) { + OopHandle oopHandle = addr.addOffsetToAsOopHandle(0); + return VM.getVM().getObjectHeap().newOop(oopHandle); + } + + protected void handleObject(String line) { + StringTokenizer st = new StringTokenizer(line); + st.nextToken(); // ignore "object" + if (st.hasMoreTokens()) { + String addrStr = st.nextToken(); + Address addr = null; + Debugger dbg = VM.getVM().getDebugger(); + try { + addr = dbg.parseAddress(addrStr); + } catch (Exception e) { + out.println("invalid address : " + e.getMessage()); + return; + } + + Oop oop = null; + try { + oop = getOopAtAddress(addr); + } catch (Exception e) { + out.println("invalid object : " + e.getMessage()); + } + + if (oop != null) { + oop.iterate(new OopPrinter(out), true); + } else { + out.println("null object!"); + } + } else { + out.println("usage: object
"); + } + } + + protected void handleUnknown(String line) { + out.println("Unknown command!"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/AddressField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/AddressField.java new file mode 100644 index 00000000000..63d3abaa9b4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/AddressField.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which adds Address-typed accessor + methods. Since we currently do not understand pointer types (and + since coercion from integer to pointer types and back is often + done in C programs anyway) these accessors are not typechecked as, + for example, the Java primitive type accessors are. */ + +public interface AddressField extends Field { + /** This accessor requires that the field be nonstatic, or a WrongTypeException will be thrown. */ + public Address getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** This accessor requires that the field be static, or a WrongTypeException will be thrown. */ + public Address getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/CIntegerField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/CIntegerField.java new file mode 100644 index 00000000000..82dfe754531 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/CIntegerField.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which represents a field referring to a + C integer value. See CIntegerType for a discussion of C integer + types and why this class is not specialized for various int sizes + or signed/unsigned. */ + +public interface CIntegerField extends Field { + public boolean isUnsigned(); + + /** The field must be nonstatic and of integer type, or a + WrongTypeException will be thrown. */ + public long getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** The field must be static and of integer type, or a + WrongTypeException will be thrown. */ + public long getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/CIntegerType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/CIntegerType.java new file mode 100644 index 00000000000..2e96ebd51f8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/CIntegerType.java @@ -0,0 +1,57 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import java.util.*; + +/**

This subinterface of Type provides accessors to deal with all + C integer types. The observation is that, according to the C + specification, there is no guarantee that the integer types of + char, short, int, and long will not all be of the same size, + meaning that it is incorrect to use any Java primitive type that + is too small to hold all of these values when talking about C + integer types, signed or unsigned.

+ +

Therefore we use long, the largest Java primitive type, + universally to hold C integer field values (deciding that a + ubiquitous change to BigInteger is not currently advantageous). + Routines which read C integers from fields know the fields' sizes + and signedness and read the appropriate number of bytes and handle + sign- or zero- extension for signed and unsigned types, + respectively. Unfortunately, since long is a signed 64-bit + integer, there will be problems handling C's unsigned 64-bit + integers, but these problems must be dealt with by the user.

*/ +public interface CIntegerType extends Type { + /** Is this integer type unsigned? */ + public boolean isUnsigned(); + + /** What is the maximum value of this type? Note that this will not + work properly for unsigned long longs. */ + public long maxValue(); + + /** What is the minimum value of this type? Note that this will not + work properly for unsigned long longs. */ + public long minValue(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/Field.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/Field.java new file mode 100644 index 00000000000..4afe853848b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/Field.java @@ -0,0 +1,141 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/**

This is the basic interface which describes a field in a C/C++ + data structure or a Java object.

+ +

The accessors in this class are designed to allow manual + coercion of the data within the field, which is often necessary + when interfacing with C programs. Therefore, the accessors here do + not perform any type checking. Specializations of the Field + interface, such as JByteField, provide getValue() methods which + both perform type checking and return the appropriate specialized + type.

+ +

See @see CIntegerType for a description of why all C integer + types are bundled into the category "CIntegerType".

+ +

As an example, coercing a pointer field into an int can be + done in the following fashion (assuming the application has + registered an integer type in the type database called + "intptr_t"):

+ +
+    {
+      ...
+      Address myObject = ...;
+      CIntegerType intptr_tType = (CIntegerType) db.lookupType("intptr_t");
+      long addrVal = field.getCInteger(myObject, intptr_tType);
+      ...
+    }
+    
+ + FIXME: among other things, this interface is not sufficient to + describe fields which are themselves arrays (like symbolOop's + jbyte _body[1]). */ +public interface Field { + /** Get the name of this field */ + public String getName(); + + /** Get the type of this field */ + public Type getType(); + + /** Get the size, in bytes, of this field. Used for manual data + structure traversal where necessary. */ + public long getSize(); + + /** Is this a static field? */ + public boolean isStatic(); + + /** The offset of this field, in bytes, in its containing data + structure, if nonstatic. If this is a static field, throws a + WrongTypeException. */ + public long getOffset() throws WrongTypeException; + + /** The address of this field, if it is a static field. If this is a + nonstatic field, throws a WrongTypeException. */ + public Address getStaticFieldAddress() throws WrongTypeException; + + /**

These accessors require that the field be nonstatic; + otherwise, a WrongTypeException will be thrown. Note that type + checking is not performed by these accessors in order to allow + manual type coercion of field data. For better protection when + accessing primitive fields, use the get(Type)Field accessors in + Type.java.

+ +

NOTE that the Address passed in to these routines may, in + fact, be an OopHandle. Specifically, in a reflective system, + dereferencing operations applied to the OopHandle must be + performed atomically with respect to GC.

+ +

See @see CIntegerType for a description of why all C integer + types are bundled into the category "CIntegerType".

+ */ + public boolean getJBoolean (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public byte getJByte (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public char getJChar (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public short getJShort (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public int getJInt (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public long getJLong (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public float getJFloat (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public double getJDouble (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public long getCInteger (Address addr, CIntegerType type) + throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public Address getAddress (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public OopHandle getOopHandle(Address addr) + throws UnmappedAddressException, UnalignedAddressException, WrongTypeException, NotInHeapException; + + /**

These accessors require that the field be static; otherwise, + a WrongTypeException will be thrown. Note that type checking is + not performed by these accessors in order to allow manual type + coercion of field data. For better protection when accessing + primitive fields, use the get(Type)Field accessors in + Type.java.

+ +

NOTE that the Address passed in to these routines may, in + fact, be an OopHandle. Specifically, in a reflective system, + dereferencing operations applied to the OopHandle must be + performed atomically with respect to GC.

+ +

See @see CIntegerType for a description of why all C integer + types are bundled into the category "CIntegerType".

+ */ + public boolean getJBoolean () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public byte getJByte () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public char getJChar () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public float getJFloat () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public double getJDouble () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public int getJInt () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public long getJLong () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public short getJShort () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public long getCInteger (CIntegerType type) + throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + public Address getAddress () throws UnmappedAddressException, UnalignedAddressException; + public OopHandle getOopHandle() + throws UnmappedAddressException, UnalignedAddressException, NotInHeapException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JBooleanField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JBooleanField.java new file mode 100644 index 00000000000..568259f7c18 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JBooleanField.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which represents a field containing a + Java boolean value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + booleans. */ + +public interface JBooleanField extends Field { + /** The field must be nonstatic and the type of the field must be a + Java boolean, or a WrongTypeException will be thrown. */ + public boolean getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** The field must be static and the type of the field must be a + Java boolean, or a WrongTypeException will be thrown. */ + public boolean getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JByteField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JByteField.java new file mode 100644 index 00000000000..f806a247703 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JByteField.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which represents a field containing a + Java byte value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + bytes. */ + +public interface JByteField extends Field { + /** The field must be nonstatic and the type of the field must be a + Java byte, or a WrongTypeException will be thrown. */ + public byte getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** The field must be static and the type of the field must be a + Java byte, or a WrongTypeException will be thrown. */ + public byte getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JCharField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JCharField.java new file mode 100644 index 00000000000..526b94167d9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JCharField.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which represents a field containing a + Java char value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + chars. */ + +public interface JCharField extends Field { + /** The field must be nonstatic and the type of the field must be a + Java char, or a WrongTypeException will be thrown. */ + public char getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** The field must be static and the type of the field must be a + Java char, or a WrongTypeException will be thrown. */ + public char getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JDoubleField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JDoubleField.java new file mode 100644 index 00000000000..f0233bc0e99 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JDoubleField.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which represents a field containing a + Java double value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + doubles. */ + +public interface JDoubleField extends Field { + /** The field must be nonstatic and the type of the field must be a + Java double, or a WrongTypeException will be thrown. */ + public double getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** The field must be static and the type of the field must be a + Java double, or a WrongTypeException will be thrown. */ + public double getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JFloatField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JFloatField.java new file mode 100644 index 00000000000..30c6088dfad --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JFloatField.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which represents a field containing a + Java float value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + floats. */ + +public interface JFloatField extends Field { + /** The field must be nonstatic and the type of the field must be a + Java float, or a WrongTypeException will be thrown. */ + public float getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** The field must be static and the type of the field must be a + Java float, or a WrongTypeException will be thrown. */ + public float getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JIntField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JIntField.java new file mode 100644 index 00000000000..b13b9d4e09a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JIntField.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which represents a field containing a + Java int value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + ints. */ + +public interface JIntField extends Field { + /** The field must be nonstatic and the type of the field must be a + Java int, or a WrongTypeException will be thrown. */ + public int getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** The field must be static and the type of the field must be a + Java int, or a WrongTypeException will be thrown. */ + public int getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JLongField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JLongField.java new file mode 100644 index 00000000000..f4019de49bc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JLongField.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which represents a field containing a + Java long value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + longs. */ + +public interface JLongField extends Field { + /** The field must be nonstatic and the type of the field must be a + Java long, or a WrongTypeException will be thrown. */ + public long getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** The field must be static and the type of the field must be a + Java long, or a WrongTypeException will be thrown. */ + public long getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JShortField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JShortField.java new file mode 100644 index 00000000000..9253c0f807f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/JShortField.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which represents a field containing a + Java short value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + shorts. */ + +public interface JShortField extends Field { + /** The field must be nonstatic and the type of the field must be a + Java short, or a WrongTypeException will be thrown. */ + public short getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** The field must be static and the type of the field must be a + Java short, or a WrongTypeException will be thrown. */ + public short getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/OopField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/OopField.java new file mode 100644 index 00000000000..db067fca4ee --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/OopField.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import sun.jvm.hotspot.debugger.*; + +/** A specialization of Field which represents a field containing an + oop value and which adds typechecked getValue() routines returning + OopHandles. */ + +public interface OopField extends Field { + /** The field must be nonstatic and the type of the field must be an + oop type, or a WrongTypeException will be thrown. */ + public OopHandle getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; + + /** The field must be static and the type of the field must be an + oop type, or a WrongTypeException will be thrown. */ + public OopHandle getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/PointerType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/PointerType.java new file mode 100644 index 00000000000..26bc12350d7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/PointerType.java @@ -0,0 +1,35 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import java.util.*; + +/** This interface describes C pointer types, specifically to be able + to model pointer fields within types more accurately. */ + +public interface PointerType extends Type { + /** Returns the target type of this PointerType. */ + public Type getTargetType(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/Type.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/Type.java new file mode 100644 index 00000000000..08fb47f2e9d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/Type.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import java.util.*; + +/** This is the top-level interface which describes C++ classes and + primitive types as well as enough about Java primitive and object + types to allow the oop hierarchy to be constructed. */ + +public interface Type { + public String getName(); + + /* The supertype of this type. May be null for top-level classes and + primitive types. NOTE that multiple inheritance is currently not + handled. However, it could (probably) be by making this return a + list and adding appropriate routines to cast an address of one + type to another. */ + public Type getSuperclass(); + + /** The size in bytes, at the machine level, of this type. This + should be equivalent to the sizeof operator -- i.e., should + include any compiler-inserted fields like the vtbl for C++ + types. */ + public long getSize(); + + /** Indicates whether this type is a C integer type -- regardless of + size or signedness. */ + public boolean isCIntegerType(); + + /** Indicates whether this type is const char*. */ + public boolean isCStringType(); + + /** Indicates whether this type is one of the Java primitive types. */ + public boolean isJavaPrimitiveType(); + + /** Indicates whether this type is an oop type in the VM. */ + public boolean isOopType(); + + /** Indicates whether this type is a pointer type. */ + public boolean isPointerType(); + + /** This is permissive and returns the CField regardless of its type + as long as it is present. Returns null if the field was not + found, unless throwExceptionIfNotFound is true, in which case a + RuntimeException is thrown (for debugging purposes). */ + public Field getField(String fieldName, boolean searchSuperclassFields, + boolean throwExceptionIfNotFound); + + /** This is permissive and returns the CField regardless of its type + as long as it is present -- it is equivalent to + getField(fieldName, searchSuperclassFields, true). Throws a + RuntimeException if the field was not found. */ + public Field getField(String fieldName, boolean searchSuperclassFields); + + /** This is permissive and returns the CField regardless of its type + as long as it is present. This does not search superclasses' + fields; it is equivalent to getField(fieldName, false). Throws a + RuntimeException if the field was not found. */ + public Field getField(String fieldName); + + /** If there is a mismatch between the declared type and the type + which would otherwise be returned from this routine, it throws a + WrongTypeException. declaredType must not be null. Throws a + RuntimeException if field was otherwise not found. */ + public Field getField(String fieldName, Type declaredType, + boolean searchSuperclassFields) throws WrongTypeException; + + /** If there is a mismatch between the declared type and the type + which would otherwise be returned from this routine, it throws a + WrongTypeException. declaredType must not be null. This does not + search superclasses' fields; it is equivalent to + getField(fieldName, declaredType, false). Throws a + RuntimeException if field was otherwise not found. */ + public Field getField(String fieldName, Type declaredType) throws WrongTypeException; + + /** Iterate over all of the fields in this type but not in + any of its superclasses. The returned Iterator's "remove" method + must not be called. */ + public Iterator getFields(); + + /**

These accessors are designed to allow strong type checking + of certain well-known types of fields, specifically Java + primitive and oop types. Specialized fields for all primitive + types, as well as oop fields, are required to have strong type + checking and a WrongTypeException should be thrown if the given + field is not precisely of the given type. Address and Oop fields + are more permissive to reduce the complexity of the initial + implementation.

+ +

These accessors do not search the superclass's fields.

+ */ + public JBooleanField getJBooleanField (String fieldName) throws WrongTypeException; + public JByteField getJByteField (String fieldName) throws WrongTypeException; + public JCharField getJCharField (String fieldName) throws WrongTypeException; + public JDoubleField getJDoubleField (String fieldName) throws WrongTypeException; + public JFloatField getJFloatField (String fieldName) throws WrongTypeException; + public JIntField getJIntField (String fieldName) throws WrongTypeException; + public JLongField getJLongField (String fieldName) throws WrongTypeException; + public JShortField getJShortField (String fieldName) throws WrongTypeException; + public CIntegerField getCIntegerField (String fieldName) throws WrongTypeException; + public OopField getOopField (String fieldName) throws WrongTypeException; + public AddressField getAddressField (String fieldName); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/TypeDataBase.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/TypeDataBase.java new file mode 100644 index 00000000000..870454618ef --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/TypeDataBase.java @@ -0,0 +1,133 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +import java.util.Iterator; +import sun.jvm.hotspot.debugger.Address; + +public interface TypeDataBase { + /** Equivalent to lookupType(cTypeName, true) */ + public Type lookupType(String cTypeName); + + /** For simplicity of the initial implementation, this is not + guaranteed to work for primitive types. If throwException is + true, throws an (unspecified) run-time exception if the type is + not found. */ + public Type lookupType(String cTypeName, boolean throwException); + + /** Equivalent to lookupIntConstant(constantName, true) */ + public Integer lookupIntConstant(String constantName); + + /* For convenience, this interface also encapsulates the fetching of + integer constants, i.e., enums. If no constant of this name was + present, either throws an (unspecified) run-time exception or + returns null. */ + public Integer lookupIntConstant(String constantName, boolean throwException); + + /** Equivalent to lookupLongConstant(constantName, true) */ + public Long lookupLongConstant(String constantName); + + /* For convenience, this interface also encapsulates the fetching of + long constants (those requiring 64 bits on 64-bit platforms). If + no constant of this name was present, either throws an + (unspecified) run-time exception or returns null. */ + public Long lookupLongConstant(String constantName, boolean throwException); + + /** Accessors for types representing the Java primitive types; used + for both proper type checking and for walking down Java arrays. */ + public Type getJBooleanType(); + public Type getJByteType(); + public Type getJCharType(); + public Type getJDoubleType(); + public Type getJFloatType(); + public Type getJIntType(); + public Type getJLongType(); + public Type getJShortType(); + + /** Returns the size of a C address in bytes. This is currently + needed in order to properly traverse an array of pointers. + Traversing an array of structs, for example, is possible by + looking up the type of the struct and multiplying the index by + its size when indexing off of a base Address. (FIXME: what about + alignment?) */ + public long getAddressSize(); + + /** Returns the size of an oop in bytes. This is currently needed in + order to properly traverse an array of oops; it is distinguished + from the address size, above, because object pointers could + conceivably have a different representation than direct + pointers. Traversing an array of structs, for example, is + possible by looking up the type of the struct and multiplying + the index by its size when indexing off of a base Address. */ + public long getOopSize(); + + /**

This is an experimental interface emulating C++'s run-time + type information (RTTI) mechanism: determines whether the given + address is a pointer to the start of a C++ object of precisely + the given type -- it does not search superclasses of the type. + The convention is that this returns false for the null pointer. + It is needed to allow wrapper Java objects of the appropriate + type to be constructed for existing C++ objects of polymorphic + type. This method is only intended to work for C++ types + (implying that we should rename this package and the classes + contained within back to "ctypes"). Further, since the vptr + offset in an object is known at compile time but not necessarily + at runtime (unless debugging information is available), it is + reasonable for an implementation of this method to search nearby + memory for the (known) vtbl value for the given type. For this + reason, this method is not intended to support scans through + memory finding C++ objects, but is instead targeted towards + discovering the true type of a pointer assumed to be + intact.

+ +

The reason this method was placed in the type database is + that the latter is the first level at which it could be exposed, + and placing it here could avoid modifying the Type interface. It + is unclear what other, if any, vtbl access would be useful (or + implementable), so we are trying to avoid changing interfaces at + this point to support this kind of functionality.

+ +

This method necessarily does not support multiple + inheritance.

*/ + public boolean addressTypeIsEqualToType(Address addr, Type type); + + /** Helper routine for guessing the most derived type of a + polymorphic C++ object. Iterates the type database calling + addressTypeIsEqualToType for all known types. Returns a matching + Type for the given address if one was found, or null if none was + found. */ + public Type guessTypeForAddress(Address addr); + + /** Returns an Iterator over the Types in the database. */ + public Iterator getTypes(); + + /** Returns an Iterator over the String names of the integer + constants in the database. */ + public Iterator getIntConstants(); + + /** Returns an Iterator over the String names of the long constants + in the database. */ + public Iterator getLongConstants(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/WrongTypeException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/WrongTypeException.java new file mode 100644 index 00000000000..cdf44163de1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/WrongTypeException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types; + +public class WrongTypeException extends RuntimeException { + public WrongTypeException() { + super(); + } + + public WrongTypeException(String detail) { + super(detail); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicAddressFieldWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicAddressFieldWrapper.java new file mode 100644 index 00000000000..f93b1efaac1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicAddressFieldWrapper.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A wrapper for a Field which adds getValue() methods returning + Addresses. Must be a wrapper since the concrete type of the Field + implementation will not be, for example, a BasicAddressField + because, for simplicity, we don't currently understand pointer + types in the Type system. */ + +public class BasicAddressFieldWrapper extends BasicFieldWrapper implements AddressField { + public BasicAddressFieldWrapper(Field field) { + super(field); + } + + public Address getValue(Address addr) + throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getAddress(addr); + } + + public Address getValue() + throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getAddress(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicCIntegerField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicCIntegerField.java new file mode 100644 index 00000000000..1ffea6a4e3f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicCIntegerField.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A specialization of BasicField adding typechecked getValue() + routines returning ints. */ + +public class BasicCIntegerField extends BasicField implements CIntegerField { + private CIntegerType intType; + + public BasicCIntegerField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offset, Address staticFieldAddress) { + super(db, containingType, name, type, isStatic, offset, staticFieldAddress); + + if (!(type instanceof CIntegerType)) { + throw new WrongTypeException("Type of a BasicCIntegerField must be a CIntegerType"); + } + + intType = (CIntegerType) type; + } + + public boolean isUnsigned() { + return intType.isUnsigned(); + } + + /** The field must be nonstatic and of integer type, or a + WrongTypeException will be thrown. */ + public long getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getCInteger(addr, intType); + } + + /** The field must be static and of integer type, or a + WrongTypeException will be thrown. */ + public long getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getCInteger(intType); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicCIntegerType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicCIntegerType.java new file mode 100644 index 00000000000..41d1cf0d607 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicCIntegerType.java @@ -0,0 +1,95 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/**

This specialization of BasicType implements the CIntegerType + interface and describes all C integer types. */ + +public class BasicCIntegerType extends BasicType implements CIntegerType { + private boolean isUnsigned; + + public BasicCIntegerType(BasicTypeDataBase db, String name, boolean isUnsigned) { + super(db, name, null); + + this.isUnsigned = isUnsigned; + } + + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + + if (!(obj instanceof BasicCIntegerType)) { + return false; + } + + BasicCIntegerType arg = (BasicCIntegerType) obj; + + if (isUnsigned != arg.isUnsigned) { + return false; + } + + return true; + } + + public String toString() { + String prefix = null; + if (isUnsigned) { + prefix = "unsigned"; + } + + if (prefix != null) { + return prefix + " " + getName(); + } + + return getName(); + } + + public boolean isCIntegerType() { + return true; + } + + public boolean isUnsigned() { + return isUnsigned; + } + + /** This should be called at most once, and only by the builder of + the TypeDataBase */ + public void setIsUnsigned(boolean isUnsigned) { + this.isUnsigned = isUnsigned; + } + + public long maxValue() { + return db.cIntegerTypeMaxValue(getSize(), isUnsigned()); + } + + public long minValue() { + return db.cIntegerTypeMinValue(getSize(), isUnsigned()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicField.java new file mode 100644 index 00000000000..a2f1f540b35 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicField.java @@ -0,0 +1,237 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A basic implementation of Field which should be suitable for + cross-platform use. */ + +public class BasicField implements Field { + protected BasicTypeDataBase db; + protected Type type; + + private Type containingType; + private String name; + private long size; + private boolean isStatic; + /** Used for nonstatic fields only */ + private long offset; + /** Used for static fields only */ + private Address staticFieldAddress; + + /** offsetInBytes is ignored if the field is static; + staticFieldAddress is used only if the field is static. */ + public BasicField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offsetInBytes, Address staticFieldAddress) { + this.db = db; + this.containingType = containingType; + this.name = name; + this.type = type; + this.size = type.getSize(); + this.isStatic = isStatic; + this.offset = offsetInBytes; + this.staticFieldAddress = staticFieldAddress; + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public long getSize() { + return size; + } + + public boolean isStatic() { + return isStatic; + } + + public long getOffset() throws WrongTypeException { + if (isStatic) { + throw new WrongTypeException("field \"" + name + "\" in class " + + containingType.getName() + " is static"); + } + return offset; + } + + public Address getStaticFieldAddress() throws WrongTypeException { + if (!isStatic) { + throw new WrongTypeException("field \"" + name + "\" in class " + + containingType.getName() + " is not static"); + } + return staticFieldAddress; + } + + //-------------------------------------------------------------------------------- + // Dereferencing operations for non-static fields + // + + public boolean getJBoolean (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getJBooleanAt(offset); + } + public byte getJByte (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getJByteAt(offset); + } + public char getJChar (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getJCharAt(offset); + } + public double getJDouble (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getJDoubleAt(offset); + } + public float getJFloat (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getJFloatAt(offset); + } + public int getJInt (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getJIntAt(offset); + } + public long getJLong (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getJLongAt(offset); + } + public short getJShort (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getJShortAt(offset); + } + public long getCInteger (Address addr, CIntegerType type) + throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getCIntegerAt(offset, type.getSize(), type.isUnsigned()); + } + public Address getAddress (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getAddressAt(offset); + } + public OopHandle getOopHandle(Address addr) + throws UnmappedAddressException, UnalignedAddressException, WrongTypeException, NotInHeapException { + if (isStatic) { + throw new WrongTypeException(); + } + return addr.getOopHandleAt(offset); + } + + //-------------------------------------------------------------------------------- + // Dereferencing operations for static fields + // + + public boolean getJBoolean () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getJBooleanAt(0); + } + public byte getJByte () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getJByteAt(0); + } + public char getJChar () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getJCharAt(0); + } + public double getJDouble () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getJDoubleAt(0); + } + public float getJFloat () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getJFloatAt(0); + } + public int getJInt () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getJIntAt(0); + } + public long getJLong () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getJLongAt(0); + } + public short getJShort () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getJShortAt(0); + } + public long getCInteger (CIntegerType type) + throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getCIntegerAt(0, type.getSize(), type.isUnsigned()); + } + public Address getAddress () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getAddressAt(0); + } + public OopHandle getOopHandle() + throws UnmappedAddressException, UnalignedAddressException, WrongTypeException, NotInHeapException { + if (!isStatic) { + throw new WrongTypeException(); + } + return staticFieldAddress.getOopHandleAt(0); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicFieldWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicFieldWrapper.java new file mode 100644 index 00000000000..0692efd7a15 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicFieldWrapper.java @@ -0,0 +1,133 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** This is an adapter class which allows delegation of operation to + another BasicCField; see, for example, BasicCharCFieldWrapper. */ + +public class BasicFieldWrapper implements Field { + protected Field field; + + public BasicFieldWrapper(Field field) { + this.field = field; + } + + public String getName() { + return field.getName(); + } + + public Type getType() { + return field.getType(); + } + + public long getSize() { + return field.getSize(); + } + + public boolean isStatic() { + return field.isStatic(); + } + + public long getOffset() throws WrongTypeException { + return field.getOffset(); + } + + public Address getStaticFieldAddress() throws WrongTypeException { + return field.getStaticFieldAddress(); + } + + public boolean getJBoolean (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJBoolean(addr); + } + public byte getJByte (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJByte(addr); + } + public char getJChar (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJChar(addr); + } + public double getJDouble (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJDouble(addr); + } + public float getJFloat (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJFloat(addr); + } + public int getJInt (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJInt(addr); + } + public long getJLong (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJLong(addr); + } + public short getJShort (Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJShort(addr); + } + public long getCInteger (Address addr, CIntegerType type) throws UnmappedAddressException, UnalignedAddressException { + return field.getCInteger(addr, type); + } + public Address getAddress (Address addr) throws UnmappedAddressException, UnalignedAddressException { + return field.getAddress(addr); + } + public OopHandle getOopHandle(Address addr) + throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { + return field.getOopHandle(addr); + } + + public boolean getJBoolean () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJBoolean(); + } + public byte getJByte () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJByte(); + } + public char getJChar () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJChar(); + } + public double getJDouble () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJDouble(); + } + public float getJFloat () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJFloat(); + } + public int getJInt () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJInt(); + } + public long getJLong () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJLong(); + } + public short getJShort () throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return field.getJShort(); + } + public long getCInteger (CIntegerType type) throws UnmappedAddressException, UnalignedAddressException { + return field.getCInteger(type); + } + public Address getAddress () throws UnmappedAddressException, UnalignedAddressException { + return field.getAddress(); + } + public OopHandle getOopHandle() + throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { + return field.getOopHandle(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJBooleanField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJBooleanField.java new file mode 100644 index 00000000000..a2c35674789 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJBooleanField.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A specialization of Field which represents a field containing a + Java boolean value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + booleans. */ + +public class BasicJBooleanField extends BasicField implements JBooleanField { + public BasicJBooleanField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offset, Address staticFieldAddress) { + super(db, containingType, name, type, isStatic, offset, staticFieldAddress); + + if (!type.equals(db.getJBooleanType())) { + throw new WrongTypeException("Type of a BasicJBooleanField must be db.getJBooleanType()"); + } + } + + /** The field must be nonstatic and the type of the field must be a + Java boolean, or a WrongTypeException will be thrown. */ + public boolean getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJBoolean(addr); + } + + /** The field must be static and the type of the field must be a + Java boolean, or a WrongTypeException will be thrown. */ + public boolean getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJBoolean(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJByteField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJByteField.java new file mode 100644 index 00000000000..29ff42c20bb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJByteField.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A specialization of Field which represents a field containing a + Java byte value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + bytes. */ + +public class BasicJByteField extends BasicField implements JByteField { + public BasicJByteField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offset, Address staticFieldAddress) { + super(db, containingType, name, type, isStatic, offset, staticFieldAddress); + + if (!type.equals(db.getJByteType())) { + throw new WrongTypeException("Type of a BasicJByteField must be db.getJByteType()"); + } + } + + /** The field must be nonstatic and the type of the field must be a + Java byte, or a WrongTypeException will be thrown. */ + public byte getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJByte(addr); + } + + /** The field must be static and the type of the field must be a + Java byte, or a WrongTypeException will be thrown. */ + public byte getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJByte(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJCharField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJCharField.java new file mode 100644 index 00000000000..cadd6052461 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJCharField.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A specialization of Field which represents a field containing a + Java char value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + chars. */ + +public class BasicJCharField extends BasicField implements JCharField { + public BasicJCharField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offset, Address staticFieldAddress) { + super(db, containingType, name, type, isStatic, offset, staticFieldAddress); + + if (!type.equals(db.getJCharType())) { + throw new WrongTypeException("Type of a BasicJCharField must be db.getJCharType()"); + } + } + + /** The field must be nonstatic and the type of the field must be a + Java char, or a WrongTypeException will be thrown. */ + public char getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJChar(addr); + } + + /** The field must be static and the type of the field must be a + Java char, or a WrongTypeException will be thrown. */ + public char getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJChar(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJDoubleField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJDoubleField.java new file mode 100644 index 00000000000..5b091a41f16 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJDoubleField.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A specialization of Field which represents a field containing a + Java double value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + doubles. */ + +public class BasicJDoubleField extends BasicField implements JDoubleField { + public BasicJDoubleField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offset, Address staticFieldAddress) { + super(db, containingType, name, type, isStatic, offset, staticFieldAddress); + + if (!type.equals(db.getJDoubleType())) { + throw new WrongTypeException("Type of a BasicJDoubleField must be equal to TypeDataBase.getJDoubleType()"); + } + } + + /** The field must be nonstatic and the type of the field must be a + Java double, or a WrongTypeException will be thrown. */ + public double getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJDouble(addr); + } + + /** The field must be static and the type of the field must be a + Java double, or a WrongTypeException will be thrown. */ + public double getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJDouble(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJFloatField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJFloatField.java new file mode 100644 index 00000000000..e6b734ae30c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJFloatField.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A specialization of Field which represents a field containing a + Java float value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + floats. */ + +public class BasicJFloatField extends BasicField implements JFloatField { + public BasicJFloatField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offset, Address staticFieldAddress) { + super(db, containingType, name, type, isStatic, offset, staticFieldAddress); + + if (!type.equals(db.getJFloatType())) { + throw new WrongTypeException("Type of a BasicJFloatField must be equal to TypeDataBase.getJFloatType()"); + } + } + + /** The field must be nonstatic and the type of the field must be a + Java float, or a WrongTypeException will be thrown. */ + public float getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJFloat(addr); + } + + /** The field must be static and the type of the field must be a + Java float, or a WrongTypeException will be thrown. */ + public float getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJFloat(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJIntField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJIntField.java new file mode 100644 index 00000000000..2c165516f4d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJIntField.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A specialization of Field which represents a field containing a + Java int value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + ints. */ + +public class BasicJIntField extends BasicField implements JIntField { + public BasicJIntField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offset, Address staticFieldAddress) { + super(db, containingType, name, type, isStatic, offset, staticFieldAddress); + + if (!type.equals(db.getJIntType())) { + throw new WrongTypeException("Type of a BasicJIntField must be equal to TypeDataBase.getJIntType()"); + } + } + + /** The field must be nonstatic and the type of the field must be a + Java int, or a WrongTypeException will be thrown. */ + public int getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJInt(addr); + } + + /** The field must be static and the type of the field must be a + Java int, or a WrongTypeException will be thrown. */ + public int getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJInt(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJLongField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJLongField.java new file mode 100644 index 00000000000..42c7cf02b2d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJLongField.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A specialization of Field which represents a field containing a + Java long value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + longs. */ + +public class BasicJLongField extends BasicField implements JLongField { + public BasicJLongField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offset, Address staticFieldAddress) { + super(db, containingType, name, type, isStatic, offset, staticFieldAddress); + + if (!type.equals(db.getJLongType())) { + throw new WrongTypeException("Type of a BasicJLongField must be equal to TypeDataBase.getJLongType()"); + } + } + + /** The field must be nonstatic and the type of the field must be a + Java long, or a WrongTypeException will be thrown. */ + public long getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJLong(addr); + } + + /** The field must be static and the type of the field must be a + Java long, or a WrongTypeException will be thrown. */ + public long getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJLong(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJShortField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJShortField.java new file mode 100644 index 00000000000..b6fc691f2dc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicJShortField.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A specialization of Field which represents a field containing a + Java short value (in either a C/C++ data structure or a Java + object) and which adds typechecked getValue() routines returning + shorts. */ + +public class BasicJShortField extends BasicField implements JShortField { + public BasicJShortField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offset, Address staticFieldAddress) { + super(db, containingType, name, type, isStatic, offset, staticFieldAddress); + + if (!type.equals(db.getJShortType())) { + throw new WrongTypeException("Type of a BasicJShortField must be equal to TypeDataBase.getJShortType()"); + } + } + + /** The field must be nonstatic and the type of the field must be a + Java short, or a WrongTypeException will be thrown. */ + public short getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJShort(addr); + } + + /** The field must be static and the type of the field must be a + Java short, or a WrongTypeException will be thrown. */ + public short getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getJShort(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicOopField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicOopField.java new file mode 100644 index 00000000000..fe493802292 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicOopField.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** A specialization of BasicField which represents a field containing + an oop value and which adds typechecked getValue() routines + returning OopHandles. */ + +public class BasicOopField extends BasicField implements OopField { + public BasicOopField(BasicTypeDataBase db, Type containingType, String name, Type type, + boolean isStatic, long offset, Address staticFieldAddress) { + super(db, containingType, name, type, isStatic, offset, staticFieldAddress); + + if (!type.isOopType()) { + throw new WrongTypeException("Type of a BasicOopField must be an oop type"); + } + } + + /** The field must be nonstatic and the type of the field must be a + Java oop, or a WrongTypeException will be thrown. */ + public OopHandle getValue(Address addr) throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getOopHandle(addr); + } + + /** The field must be static and the type of the field must be a + Java oop, or a WrongTypeException will be thrown. */ + public OopHandle getValue() throws UnmappedAddressException, UnalignedAddressException, WrongTypeException { + return getOopHandle(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicPointerType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicPointerType.java new file mode 100644 index 00000000000..1d64364638c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicPointerType.java @@ -0,0 +1,50 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** This specialization of BasicType implements the PointerType + interface and describes all C pointer types. */ + +public class BasicPointerType extends BasicType implements PointerType { + private Type targetType; + + public BasicPointerType(BasicTypeDataBase db, String name, Type targetType) { + super(db, name, null); + + this.targetType = targetType; + } + + public boolean isPointerType() { + return true; + } + + public Type getTargetType() { + return targetType; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicType.java new file mode 100644 index 00000000000..ef1939a9028 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicType.java @@ -0,0 +1,306 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/**

This is a basic implementation of the Type interface which + should be complete enough to be portable across platforms. The + only issue will be the construction of these objects and their + components from the platform-specific debugging information; see + BasicTypeDataBase.

+ +

There are two types of clients of this class. The first is + that which builds the TypeDatabase. This kind of client uses the + additional public methods beyond those in the Type interface to + properly configure the BasicType objects. The second is the + consumer of these types; this kind of client should deal only with + the Type interfaces.

*/ + +public class BasicType implements Type { + protected BasicTypeDataBase db; + + private String name; + private long size; + private boolean isJavaPrimitiveType; + private boolean isOopType; + // These are only the fields defined in this class, not any of this + // class's superclasses. + private Map nameToFieldMap = new HashMap(); + private List fieldList = new LinkedList(); + // Superclass, or null if none. Primitive types do not have any + // inheritance relationship. + private Type superclass; + + /** superclass may be null */ + public BasicType(BasicTypeDataBase db, String name, Type superclass) { + if (name == null) { + throw new IllegalArgumentException("name may not be null"); + } + this.db = db; + this.name = name; + this.superclass = superclass; + } + + /** Equivalent to BasicType(db, name, null) */ + public BasicType(BasicTypeDataBase db, String name) { + this(db, name, null); + } + + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (!(obj instanceof BasicType)) { + return false; + } + + BasicType arg = (BasicType) obj; + + if (!name.equals(arg.name)) { + return false; + } + + return true; + } + + public int hashCode() { + return name.hashCode(); + } + + public String toString() { + return name; + } + + public String getName() { + return name; + } + + /** This should only be called at most once, and only by the builder + of the type database */ + public void setSuperclass(Type superclass) { + this.superclass = superclass; + } + + public Type getSuperclass() { + return superclass; + } + + /** This should only be called at most once, and only by the builder + of the type database */ + public void setSize(long sizeInBytes) { + this.size = sizeInBytes; + } + + public long getSize() { + return size; + } + + /** Overridden by BasicCIntegerType */ + public boolean isCIntegerType() { + return false; + } + + public boolean isCStringType() { + if (isPointerType()) { + Type target = ((PointerType)this).getTargetType(); + return target.isCIntegerType() && + target.getName().equals("const char"); + } else { + return false; + } + } + + public boolean isJavaPrimitiveType() { + return isJavaPrimitiveType; + } + + /** This should only be called at most once, and only by the builder + of the type database */ + public void setIsJavaPrimitiveType(boolean isJavaPrimitiveType) { + this.isJavaPrimitiveType = isJavaPrimitiveType; + } + + public boolean isOopType() { + return isOopType; + } + + /** Overridden by BasicPointerType */ + public boolean isPointerType() { + return false; + } + + /** This should only be called at most once, and only by the builder + of the type database */ + public void setIsOopType(boolean isOopType) { + this.isOopType = isOopType; + } + + public Field getField(String fieldName, boolean searchSuperclassFields, + boolean throwExceptionIfNotFound) { + Field field = null; + if (nameToFieldMap != null) { + field = (Field) nameToFieldMap.get(fieldName); + + if (field != null) { + return field; + } + } + + if (searchSuperclassFields) { + if (superclass != null) { + field = superclass.getField(fieldName, searchSuperclassFields, false); + } + } + + if (field == null && throwExceptionIfNotFound) { + throw new RuntimeException("field \"" + fieldName + "\" not found in type " + name); + } + + return field; + } + + public Field getField(String fieldName, boolean searchSuperclassFields) { + return getField(fieldName, searchSuperclassFields, true); + } + + public Field getField(String fieldName) { + return getField(fieldName, true); + } + + public Field getField(String fieldName, Type declaredType, + boolean searchSuperclassFields) throws WrongTypeException { + Field res = getField(fieldName, searchSuperclassFields); + if (res == null) { + return null; + } + if (!res.getType().equals(declaredType)) { + throw new WrongTypeException("field \"" + fieldName + "\" in type " + name + + " is not of type " + declaredType + + ", but instead of type " + res.getType()); + } + return res; + } + + public Field getField(String fieldName, Type declaredType) throws WrongTypeException { + return getField(fieldName, declaredType, false); + } + + /** The returned iterator's "remove" method must not be called */ + public Iterator getFields() { + return new ConstIterator(fieldList.iterator()); + } + + //-------------------------------------------------------------------------------- + // Specialized field type accessors + // + + public JBooleanField getJBooleanField(String fieldName) throws WrongTypeException { + return (JBooleanField) getField(fieldName, db.getJBooleanType()); + } + + public JByteField getJByteField(String fieldName) throws WrongTypeException { + return (JByteField) getField(fieldName, db.getJByteType()); + } + + public JCharField getJCharField(String fieldName) throws WrongTypeException { + return (JCharField) getField(fieldName, db.getJCharType()); + } + + public JDoubleField getJDoubleField(String fieldName) throws WrongTypeException { + return (JDoubleField) getField(fieldName, db.getJDoubleType()); + } + + public JFloatField getJFloatField(String fieldName) throws WrongTypeException { + return (JFloatField) getField(fieldName, db.getJFloatType()); + } + + public JIntField getJIntField(String fieldName) throws WrongTypeException { + return (JIntField) getField(fieldName, db.getJIntType()); + } + + public JLongField getJLongField(String fieldName) throws WrongTypeException { + return (JLongField) getField(fieldName, db.getJLongType()); + } + + public JShortField getJShortField(String fieldName) throws WrongTypeException { + return (JShortField) getField(fieldName, db.getJShortType()); + } + + public CIntegerField getCIntegerField(String fieldName) throws WrongTypeException { + Field field = getField(fieldName); + if (!(field.getType() instanceof CIntegerType)) { + throw new WrongTypeException("field \"" + fieldName + "\" in type " + name + + " is not of C integer type, but instead of type " + + field.getType()); + } + return (CIntegerField) field; + } + + public OopField getOopField(String fieldName) throws WrongTypeException { + Field field = getField(fieldName); + if (!field.getType().isOopType()) { + throw new WrongTypeException("field \"" + fieldName + "\" in type " + name + + " is not of oop type, but instead of type " + + field.getType()); + } + return (OopField) field; + } + + public AddressField getAddressField(String fieldName) { + // This type can not be inferred (for now), so provide a wrapper + Field field = getField(fieldName); + if (field == null) { + return null; + } + return new BasicAddressFieldWrapper(field); + } + + /** This method should only be used by the builder of the + TypeDataBase. Throws a RuntimeException if a field with this + name was already present in this class. */ + public void addField(Field field) { + if (nameToFieldMap.get(field.getName()) != null) { + throw new RuntimeException("field of name \"" + field.getName() + "\" already present"); + } + + nameToFieldMap.put(field.getName(), field); + fieldList.add(field); + } + + /** This method should only be used by the builder of the + TypeDataBase. Throws a RuntimeException if a field with this + name was not present in this class. */ + public void removeField(Field field) { + if (nameToFieldMap.remove(field.getName()) == null) { + throw new RuntimeException("field of name \"" + field.getName() + "\" was not present"); + } + fieldList.remove(field); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicTypeDataBase.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicTypeDataBase.java new file mode 100644 index 00000000000..80671cada43 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicTypeDataBase.java @@ -0,0 +1,407 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/**

This is a basic implementation of the TypeDataBase interface. + It allows an external type database builder to add types to be + consumed by a client through the Type interfaces. It has no + knowledge of symbol lookup; for example, the builder is + responsible for providing the addresses of static fields.

+ +

Among other things, the database builder is responsible for + providing the Types for the Java primitive types, as well as their + sizes.

+*/ + +public class BasicTypeDataBase implements TypeDataBase { + private MachineDescription machDesc; + private VtblAccess vtblAccess; + /** Maps strings to Type objects. This does not contain the primitive types. */ + private Map nameToTypeMap = new HashMap(); + /** Maps strings to Integers, used for enums, etc. */ + private Map nameToIntConstantMap = new HashMap(); + /** Maps strings to Longs, used for 32/64-bit constants, etc. */ + private Map nameToLongConstantMap = new HashMap(); + /** Primitive types. */ + private Type jbooleanType; + private Type jbyteType; + private Type jcharType; + private Type jdoubleType; + private Type jfloatType; + private Type jintType; + private Type jlongType; + private Type jshortType; + + /** For debugging */ + private static final boolean DEBUG; + static { + DEBUG = System.getProperty("sun.jvm.hotspot.types.basic.BasicTypeDataBase.DEBUG") != null; + } + + public BasicTypeDataBase(MachineDescription machDesc, VtblAccess vtblAccess) { + this.machDesc = machDesc; + this.vtblAccess = vtblAccess; + } + + public Type lookupType(String cTypeName) { + return lookupType(cTypeName, true); + } + + public Type lookupType(String cTypeName, boolean throwException) { + Type type = (Type) nameToTypeMap.get(cTypeName); + if (type == null && throwException) { + throw new RuntimeException("No type named \"" + cTypeName + "\" in database"); + } + return type; + } + + public Integer lookupIntConstant(String constantName) { + return lookupIntConstant(constantName, true); + } + + public Integer lookupIntConstant(String constantName, boolean throwException) { + Integer i = (Integer) nameToIntConstantMap.get(constantName); + if (i == null) { + if (throwException) { + throw new RuntimeException("No integer constant named \"" + constantName + "\" present in type database"); + } + } + return i; + } + + public Long lookupLongConstant(String constantName) { + return lookupLongConstant(constantName, true); + } + + public Long lookupLongConstant(String constantName, boolean throwException) { + Long i = (Long) nameToLongConstantMap.get(constantName); + if (i == null) { + if (throwException) { + throw new RuntimeException("No long constant named \"" + constantName + "\" present in type database"); + } + } + return i; + } + + public Type getJBooleanType() { + return jbooleanType; + } + + public Type getJByteType() { + return jbyteType; + } + + public Type getJCharType() { + return jcharType; + } + + public Type getJDoubleType() { + return jdoubleType; + } + + public Type getJFloatType() { + return jfloatType; + } + + public Type getJIntType() { + return jintType; + } + + public Type getJLongType() { + return jlongType; + } + + public Type getJShortType() { + return jshortType; + } + + public long getAddressSize() { + return machDesc.getAddressSize(); + } + + public long getOopSize() { + return machDesc.getOopSize(); + } + + public boolean addressTypeIsEqualToType(Address addr, Type type) { + if (addr == null) { + return false; + } + + // This implementation should be suitably platform-independent; we + // search nearby memory for the vtbl value of the given type. + + Address vtblAddr = vtblAccess.getVtblForType(type); + + if (vtblAddr == null) { + // Type was not polymorphic, or an error occurred during lookup + if (DEBUG) { + System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: vtblAddr == null"); + } + + return false; + } + + // The first implementation searched three locations for this vtbl + // value; scanning through the entire object was considered, but + // we thought we knew where we were looking, and looking only in + // these specific locations should reduce the probability of + // mistaking random bits as a pointer (although, realistically + // speaking, the likelihood of finding a match between the bit + // pattern of, for example, a double and the vtbl is vanishingly + // small.) + // 1. The first word of the object (should handle MSVC++ as + // well as the SparcWorks compilers with compatibility set to + // v5.0 or greater) + // 2. and 3. The last two Address-aligned words of the part of + // the object defined by its topmost polymorphic superclass. + // This should handle the SparcWorks compilers, v4.2 or + // earlier, as well as any other compilers which place the vptr + // at the end of the user-defined fields of the first base + // class with virtual functions. + // + // Unfortunately this algorithm did not work properly for the + // specific case of the ThreadShadow/Thread inheritance situation, + // because the Solaris compiler seems to cleverly eliminate the + // vtbl for ThreadShadow since the only virtual is empty. (We + // should get rid of the ThreadShadow and fix the include + // databases, but need to postpone this for the present.) The + // current solution performs the three-location check for this + // class and all of its known superclasses rather than just the + // topmost polymorphic one. + + Type curType = type; + + try { + while (curType != null) { + // Using the size information we have for this type, check the + // three locations described above. + + // (1) + if (vtblAddr.equals(addr.getAddressAt(0))) { + return true; + } + + // (2) + long offset = curType.getSize(); + // I don't think this should be misaligned under any + // circumstances, but I'm not sure (FIXME: also not sure which + // way to go here, up or down -- assuming down) + offset -= (offset % getAddressSize()); + if (offset <= 0) { + return false; + } + if (vtblAddr.equals(addr.getAddressAt(offset))) { + return true; + } + offset -= getAddressSize(); + if (offset <= 0) { + return false; + } + if (vtblAddr.equals(addr.getAddressAt(offset))) { + return true; + } + + curType = curType.getSuperclass(); + } + } + catch (Exception e) { + // Any UnmappedAddressExceptions, etc. are a good indication + // that the pointer is not of the specified type + if (DEBUG) { + System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: exception occurred during lookup:"); + e.printStackTrace(); + } + + return false; + } + + if (DEBUG) { + System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: all vptr tests failed for type " + + type.getName()); + } + + return false; + } + + public Type guessTypeForAddress(Address addr) { + for (Iterator iter = getTypes(); iter.hasNext(); ) { + Type t = (Type) iter.next(); + if (addressTypeIsEqualToType(addr, t)) { + return t; + } + } + return null; + } + + public long cIntegerTypeMaxValue(long sizeInBytes, boolean isUnsigned) { + return machDesc.cIntegerTypeMaxValue(sizeInBytes, isUnsigned); + } + + public long cIntegerTypeMinValue(long sizeInBytes, boolean isUnsigned) { + return machDesc.cIntegerTypeMinValue(sizeInBytes, isUnsigned); + } + + public Iterator getTypes() { + return nameToTypeMap.values().iterator(); + } + + public Iterator getIntConstants() { + return nameToIntConstantMap.keySet().iterator(); + } + + public Iterator getLongConstants() { + return nameToLongConstantMap.keySet().iterator(); + } + + //-------------------------------------------------------------------------------- + // Public routines only for use by the database builder + // + + /** This method should only be called by the builder of the + TypeDataBase and at most once */ + public void setJBooleanType(Type type) { + jbooleanType = type; + } + + /** This method should only be called by the builder of the + TypeDataBase and at most once */ + public void setJByteType(Type type) { + jbyteType = type; + } + + /** This method should only be called by the builder of the + TypeDataBase and at most once */ + public void setJCharType(Type type) { + jcharType = type; + } + + /** This method should only be called by the builder of the + TypeDataBase and at most once */ + public void setJDoubleType(Type type) { + jdoubleType = type; + } + + /** This method should only be called by the builder of the + TypeDataBase and at most once */ + public void setJFloatType(Type type) { + jfloatType = type; + } + + /** This method should only be called by the builder of the + TypeDataBase and at most once */ + public void setJIntType(Type type) { + jintType = type; + } + + /** This method should only be called by the builder of the + TypeDataBase and at most once */ + public void setJLongType(Type type) { + jlongType = type; + } + + /** This method should only be called by the builder of the + TypeDataBase and at most once */ + public void setJShortType(Type type) { + jshortType = type; + } + + /** This method should only be used by the builder of the + TypeDataBase. Throws a RuntimeException if a class with this + name was already present. */ + public void addType(Type type) { + if (nameToTypeMap.get(type.getName()) != null) { + throw new RuntimeException("type of name \"" + type.getName() + "\" already present"); + } + + nameToTypeMap.put(type.getName(), type); + } + + /** This method should only be used by the builder of the + TypeDataBase. Throws a RuntimeException if this class was not + present. */ + public void removeType(Type type) { + Type curType = (Type) nameToTypeMap.get(type.getName()); + if (curType == null) { + throw new RuntimeException("type of name \"" + type.getName() + "\" not present"); + } + + if (!curType.equals(type)) { + throw new RuntimeException("a different type of name \"" + type.getName() + "\" was present"); + } + + nameToTypeMap.remove(type.getName()); + } + + /** This method should only be used by the builder of the + TypeDataBase. Throws a RuntimeException if an integer constant + with this name was already present. */ + public void addIntConstant(String name, int value) { + if (nameToIntConstantMap.get(name) != null) { + throw new RuntimeException("int constant of name \"" + name + "\" already present"); + } + + nameToIntConstantMap.put(name, new Integer(value)); + } + + /** This method should only be used by the builder of the + TypeDataBase. Throws a RuntimeException if an integer constant + with this name was not present. */ + public void removeIntConstant(String name) { + Integer curConstant = (Integer) nameToIntConstantMap.get(name); + if (curConstant == null) { + throw new RuntimeException("int constant of name \"" + name + "\" not present"); + } + + nameToIntConstantMap.remove(name); + } + + /** This method should only be used by the builder of the + TypeDataBase. Throws a RuntimeException if a long constant with + this name was already present. */ + public void addLongConstant(String name, long value) { + if (nameToLongConstantMap.get(name) != null) { + throw new RuntimeException("long constant of name \"" + name + "\" already present"); + } + + nameToLongConstantMap.put(name, new Long(value)); + } + + /** This method should only be used by the builder of the + TypeDataBase. Throws a RuntimeException if a long constant with + this name was not present. */ + public void removeLongConstant(String name) { + Long curConstant = (Long) nameToLongConstantMap.get(name); + if (curConstant == null) { + throw new RuntimeException("long constant of name \"" + name + "\" not present"); + } + + nameToLongConstantMap.remove(name); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicVtblAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicVtblAccess.java new file mode 100644 index 00000000000..b408d3dce72 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicVtblAccess.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +public abstract class BasicVtblAccess implements VtblAccess { + protected SymbolLookup symbolLookup; + protected String[] dllNames; + + private Map typeToVtblMap = new HashMap(); + + public BasicVtblAccess(SymbolLookup symbolLookup, + String[] dllNames) { + this.symbolLookup = symbolLookup; + this.dllNames = dllNames; + } + + static Object nullAddress = new Object(); + + public Address getVtblForType(Type type) { + if (type == null) { + return null; + } + Object result = typeToVtblMap.get(type); + if (result == nullAddress) { + return null; + } + if (result != null) { + return (Address)result; + } + String vtblSymbol = vtblSymbolForType(type); + if (vtblSymbol == null) { + typeToVtblMap.put(type, nullAddress); + return null; + } + for (int i = 0; i < dllNames.length; i++) { + Address addr = symbolLookup.lookup(dllNames[i], vtblSymbol); + if (addr != null) { + typeToVtblMap.put(type, addr); + return addr; + } + } + typeToVtblMap.put(type, nullAddress); + return null; + } + + public void clearCaches() { + typeToVtblMap.clear(); + } + + protected abstract String vtblSymbolForType(Type type); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/VtblAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/VtblAccess.java new file mode 100644 index 00000000000..4267d00aaa3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/types/basic/VtblAccess.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.types.basic; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +/** This interface is designed to allow a platform-specific + implementation of the TypeDataBase.isOfType() method, while + leaving the rest of the basic.* package platform independent. */ + +public interface VtblAccess { + /** This is the necessarily platform-specific implementation. + Attempt to return the address of the vtbl for the given + polymorphic C++ type. This value will be used when searching + nearby memory to implement isOfType() in as platform-independent + a manner as possible. Returns null if this value was not + available for the given type, which might indicate that the type + was not polymorphic or that an error occurred while trying to + find the symbol. Note that this method does not support multiple + inheritance. */ + public Address getVtblForType(Type type); + + /** Clear any cached values from symbol lookups in the target + process. It is important that this mechanism be fast and for + that reason the default implementation memoizes type-to-vtbl + mappings. However, if the target process is resumed, these + mappings may become invalid. */ + public void clearCaches(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/AnnotatedMemoryPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/AnnotatedMemoryPanel.java new file mode 100644 index 00000000000..02bb9543842 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/AnnotatedMemoryPanel.java @@ -0,0 +1,653 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.math.*; +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.awt.event.*; +import java.io.*; +import javax.swing.*; +import javax.swing.event.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.debugger.dummy.*; +import sun.jvm.hotspot.utilities.*; + +/** A subclass of JPanel which displays a hex dump of memory along + with annotations describing the significance of various + pieces. This can be used to implement either a stack or heap + inspector. */ + +public class AnnotatedMemoryPanel extends JPanel { + private boolean is64Bit; + private Debugger debugger; + private long addressSize; + private HighPrecisionJScrollBar scrollBar; + private Font font; + private int bytesPerLine; + private int paintCount; + private String unmappedAddrString; + // Type of this is an IntervalTree indexed by Interval
and + // with user data of type Annotation + private IntervalTree annotations = + new IntervalTree(new Comparator() { + public int compare(Object o1, Object o2) { + Address a1 = (Address) o1; + Address a2 = (Address) o2; + + if ((a1 == null) && (a2 == null)) { + return 0; + } else if (a1 == null) { + return -1; + } else if (a2 == null) { + return 1; + } + + if (a1.equals(a2)) { + return 0; + } else if (a1.lessThan(a2)) { + return -1; + } + return 1; + } + }); + // Keep track of the last start address at which we painted, so we + // can scroll annotations + private Address lastStartAddr; + // This contains the list of currently-visible IntervalNodes, in + // sorted order by their low endpoint, in the form of a + // List. These annotations have already been laid out. + private java.util.List visibleAnnotations; + // Darker colors than defaults for better readability + private static Color[] colors = { + new Color(0.0f, 0.0f, 0.6f), // blue + new Color(0.6f, 0.0f, 0.6f), // magenta + new Color(0.0f, 0.8f, 0.0f), // green + new Color(0.8f, 0.3f, 0.0f), // orange + new Color(0.0f, 0.6f, 0.8f), // cyan + new Color(0.2f, 0.2f, 0.2f), // dark gray + }; + + /** Default is 32-bit mode */ + public AnnotatedMemoryPanel(Debugger debugger) { + this(debugger, false); + } + + public AnnotatedMemoryPanel(Debugger debugger, boolean is64Bit, Address addrValue, Address addrLow, Address addrHigh) { + super(); + init(debugger, is64Bit, addressToBigInt(addrValue), addressToBigInt(addrLow), addressToBigInt(addrHigh)); + } + + public AnnotatedMemoryPanel(Debugger debugger, boolean is64Bit ) { + super(); + init(debugger, is64Bit, defaultMemoryLocation(is64Bit), defaultMemoryLow(is64Bit), defaultMemoryHigh(is64Bit)); + } + + static class AnnoX { + int lineX; + Address highBound; + + public AnnoX(int lineX, Address highBound) { + this.lineX = lineX; + this.highBound = highBound; + } + } + + public synchronized void paintComponent(Graphics g) { + // System.err.println("AnnotatedMemoryPanel.paintComponent() " + ++paintCount); + super.paintComponent(g); + + // Clone the Graphics so we don't screw up its state for Swing + // drawing (as this code otherwise does) + g = g.create(); + + g.setFont(font); + g.setColor(Color.black); + Rectangle rect = new Rectangle(); + getBounds(rect); + String firstAddressString = null; + int lineHeight; + int addrWidth; + { + Rectangle2D bounds = GraphicsUtilities.getStringBounds(unmappedAddrString, g); + lineHeight = (int) bounds.getHeight(); + addrWidth = (int) bounds.getWidth(); + } + int addrX = (int) (0.25 * addrWidth); + int dataX = (int) (addrX + (1.5 * addrWidth)); + int lineStartX = dataX + addrWidth + 5; + int annoStartX = (int) (lineStartX + (0.75 * addrWidth)); + + int numLines = rect.height / lineHeight; + + BigInteger startVal = scrollBar.getValueHP(); + BigInteger perLine = new BigInteger(Integer.toString((int) addressSize)); + // lineCount and maxLines are both 1 less than expected + BigInteger lineCount = new BigInteger(Integer.toString((int) (numLines - 1))); + BigInteger maxLines = scrollBar.getMaximumHP().subtract(scrollBar.getMinimumHP()).divide(perLine); + if (lineCount.compareTo(maxLines) > 0) { + lineCount = maxLines; + } + BigInteger offsetVal = lineCount.multiply(perLine); + BigInteger endVal = startVal.add(offsetVal); + if (endVal.compareTo(scrollBar.getMaximumHP()) > 0) { + startVal = scrollBar.getMaximumHP().subtract(offsetVal); + endVal = scrollBar.getMaximumHP(); + // Sure seems like this call will cause us to immediately redraw... + scrollBar.setValueHP(startVal); + } + scrollBar.setVisibleAmountHP(offsetVal.add(perLine)); + scrollBar.setBlockIncrementHP(offsetVal); + + Address startAddr = bigIntToAddress(startVal); + Address endAddr = bigIntToAddress(endVal); + + // Scroll last-known annotations + int scrollOffset = 0; + if (lastStartAddr != null) { + scrollOffset = (int) lastStartAddr.minus(startAddr); + } else { + if (startAddr != null) { + scrollOffset = (int) (-1 * startAddr.minus(lastStartAddr)); + } + } + scrollOffset = scrollOffset * lineHeight / (int) addressSize; + scrollAnnotations(scrollOffset); + lastStartAddr = startAddr; + + int curY = lineHeight; + Address curAddr = startAddr; + for (int i = 0; i < numLines; i++) { + String s = bigIntToHexString(startVal); + g.drawString(s, addrX, curY); + try { + s = addressToString(startAddr.getAddressAt(i * addressSize)); + } + catch (UnmappedAddressException e) { + s = unmappedAddrString; + } + g.drawString(s, dataX, curY); + curY += lineHeight; + startVal = startVal.add(perLine); + } + + // Query for visible annotations (little slop to ensure we get the + // top and bottom) + // FIXME: it would be nice to have a more static layout; that is, + // if something scrolls off the bottom of the screen, other + // annotations still visible shouldn't change position + java.util.List va = + annotations.findAllNodesIntersecting(new Interval(startAddr.addOffsetTo(-addressSize), + endAddr.addOffsetTo(2 * addressSize))); + + // Render them + int curLineX = lineStartX; + int curTextX = annoStartX; + int curColorIdx = 0; + if (g instanceof Graphics2D) { + Stroke stroke = new BasicStroke(3.0f); + ((Graphics2D) g).setStroke(stroke); + } + + Stack drawStack = new Stack(); + + layoutAnnotations(va, g, curTextX, startAddr, lineHeight); + + for (Iterator iter = visibleAnnotations.iterator(); iter.hasNext(); ) { + Annotation anno = (Annotation) iter.next(); + Interval interval = anno.getInterval(); + + if (!drawStack.empty()) { + // See whether we can pop any items off the stack + boolean shouldContinue = true; + do { + AnnoX annoX = (AnnoX) drawStack.peek(); + if (annoX.highBound.lessThanOrEqual((Address) interval.getLowEndpoint())) { + curLineX = annoX.lineX; + drawStack.pop(); + shouldContinue = !drawStack.empty(); + } else { + shouldContinue = false; + } + } while (shouldContinue); + } + + // Draw a line covering the interval + Address lineStartAddr = (Address) interval.getLowEndpoint(); + // Give it a little slop + int lineStartY = (int) (lineStartAddr.minus(startAddr) * lineHeight / addressSize) + + (lineHeight / 3); + Address lineEndAddr = (Address) interval.getHighEndpoint(); + drawStack.push(new AnnoX(curLineX, lineEndAddr)); + int lineEndY = (int) (lineEndAddr.minus(startAddr) * lineHeight / addressSize); + g.setColor(anno.getColor()); + g.drawLine(curLineX, lineStartY, curLineX, lineEndY); + // Draw line to text + g.drawLine(curLineX, lineStartY, curTextX - 10, anno.getY() - (lineHeight / 2)); + curLineX += 8; + anno.draw(g); + ++curColorIdx; + } + } + + /** Add an annotation covering the address range [annotation.lowAddress, + annotation.highAddress); that is, it includes the low address and does not + include the high address. */ + public synchronized void addAnnotation(Annotation annotation) { + annotations.insert(annotation.getInterval(), annotation); + } + + /** Makes the given address visible somewhere in the window */ + public synchronized void makeVisible(Address addr) { + BigInteger bi = addressToBigInt(addr); + scrollBar.setValueHP(bi); + } + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + annotations.printOn(tty); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void init(Debugger debugger, boolean is64Bit, BigInteger addrValue, BigInteger addrLow, BigInteger addrHigh) { + this.is64Bit = is64Bit; + this.debugger = debugger; + if (is64Bit) { + addressSize = 8; + unmappedAddrString = "??????????????????"; + } else { + addressSize = 4; + unmappedAddrString = "??????????"; + } + setLayout(new BorderLayout()); + setupScrollBar(addrValue, addrLow, addrHigh); + add(scrollBar, BorderLayout.EAST); + visibleAnnotations = new ArrayList(); + setBackground(Color.white); + addHierarchyBoundsListener(new HierarchyBoundsListener() { + public void ancestorMoved(HierarchyEvent e) { + } + + public void ancestorResized(HierarchyEvent e) { + // FIXME: should perform incremental layout + // System.err.println("Ancestor resized"); + } + }); + + if (font == null) { + font = GraphicsUtilities.lookupFont("Courier"); + } + if (font == null) { + throw new RuntimeException("Error looking up monospace font Courier"); + } + getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), "PageDown"); + getActionMap().put("PageDown", new AbstractAction() { + public void actionPerformed(ActionEvent e) { + scrollBar.setValueHP(scrollBar.getValueHP().add(scrollBar.getBlockIncrementHP())); + } + }); + getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), "PageUp"); + getActionMap().put("PageUp", new AbstractAction() { + public void actionPerformed(ActionEvent e) { + scrollBar.setValueHP(scrollBar.getValueHP().subtract(scrollBar.getBlockIncrementHP())); + } + }); + getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "Down"); + getActionMap().put("Down", new AbstractAction() { + public void actionPerformed(ActionEvent e) { + scrollBar.setValueHP(scrollBar.getValueHP().add(scrollBar.getUnitIncrementHP())); + } + }); + getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "Up"); + getActionMap().put("Up", new AbstractAction() { + public void actionPerformed(ActionEvent e) { + scrollBar.setValueHP(scrollBar.getValueHP().subtract(scrollBar.getUnitIncrementHP())); + } + }); + setEnabled(true); + } + + private void setupScrollBar(BigInteger value, BigInteger min, BigInteger max) { + scrollBar = new HighPrecisionJScrollBar( Scrollbar.VERTICAL, value, min, max); + if (is64Bit) { + bytesPerLine = 8; + // 64-bit mode + scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08})); + scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x40})); + } else { + // 32-bit mode + bytesPerLine = 4; + scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04})); + scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x20})); + } + scrollBar.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + HighPrecisionJScrollBar h = (HighPrecisionJScrollBar) e.getSource(); + repaint(); + } + }); + } + + private static BigInteger defaultMemoryLocation(boolean is64Bit) { + if (is64Bit) { + return new BigInteger(1, new byte[] { + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } else { + return new BigInteger(1, new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + } + + private static BigInteger defaultMemoryLow(boolean is64Bit) { + if (is64Bit) { + return new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } else { + return new BigInteger(1, new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + } + + private static BigInteger defaultMemoryHigh(boolean is64Bit) { + if (is64Bit) { + return new BigInteger(1, new byte[] { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC}); + } else { + return new BigInteger(1, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC}); + } + } + + private void setupScrollBar() { + setupScrollBar(defaultMemoryLocation(is64Bit), + defaultMemoryLow(is64Bit), + defaultMemoryHigh(is64Bit)); + } + + private String bigIntToHexString(BigInteger bi) { + StringBuffer buf = new StringBuffer(); + buf.append("0x"); + String val = bi.toString(16); + for (int i = 0; i < ((2 * addressSize) - val.length()); i++) { + buf.append('0'); + } + buf.append(val); + return buf.toString(); + } + + private Address bigIntToAddress(BigInteger i) { + String s = bigIntToHexString(i); + return debugger.parseAddress(s); + } + + private BigInteger addressToBigInt(Address a) { + String s = addressToString(a); + if (!s.startsWith("0x")) { + throw new NumberFormatException(s); + } + return new BigInteger(s.substring(2), 16); + } + + private String addressToString(Address a) { + if (a == null) { + if (is64Bit) { + return "0x0000000000000000"; + } else { + return "0x00000000"; + } + } + return a.toString(); + } + + /** Scrolls the visible annotations by the given Y amount */ + private void scrollAnnotations(int y) { + for (Iterator iter = visibleAnnotations.iterator(); iter.hasNext(); ) { + Annotation anno = (Annotation) iter.next(); + anno.setY(anno.getY() + y); + } + } + + /** Takes the list of currently-visible annotations (in the form of + a List) and lays them out given the current + visible position and the already-visible annotations. Does not + perturb the layouts of the currently-visible annotations. */ + private void layoutAnnotations(java.util.List va, + Graphics g, + int x, + Address startAddr, + int lineHeight) { + // Handle degenerate case early: no visible annotations. + if (va.size() == 0) { + visibleAnnotations.clear(); + return; + } + + // We have two ranges of visible annotations: the one from the + // last repaint and the currently visible set. We want to preserve + // the layouts of the previously-visible annotations that are + // currently visible (to avoid color flashing, jumping, etc.) + // while making the coloring of the new annotations fit as well as + // possible. Note that annotations may appear and disappear from + // any point in the previously-visible list, but the ordering of + // the visible annotations is always the same. + + // This is really a constrained graph-coloring problem. This + // simple algorithm only takes into account half of the + // constraints (for example, the layout of the previous + // annotation, where it should be taking into account the layout + // of the previous and next annotations that were in the + // previously-visible list). There are situations where it can + // generate overlapping annotations and adjacent annotations with + // the same color; generally visible when scrolling line-by-line + // rather than page-by-page. In some of these situations, will + // have to move previously laid-out annotations. FIXME: revisit + // this. + + // Index of last annotation which we didn't know how to lay out + int deferredIndex = -1; + // We "lay out after" this one + Annotation constraintAnnotation = null; + // The first constraint annotation + Annotation firstConstraintAnnotation = null; + // The index from which we search forward in the + // visibleAnnotations list. This reduces the amount of work we do. + int searchIndex = 0; + // The new set of annotations + java.util.List newAnnos = new ArrayList(); + + for (Iterator iter = va.iterator(); iter.hasNext(); ) { + Annotation anno = (Annotation) ((IntervalNode) iter.next()).getData(); + + // Search forward for this one + boolean found = false; + for (int i = searchIndex; i < visibleAnnotations.size(); i++) { + Annotation el = (Annotation) visibleAnnotations.get(i); + // See whether we can abort the search unsuccessfully because + // we went forward too far + if (el.getLowAddress().greaterThan(anno.getLowAddress())) { + break; + } + if (el == anno) { + // Found successfully. + found = true; + searchIndex = i; + constraintAnnotation = anno; + if (firstConstraintAnnotation == null) { + firstConstraintAnnotation = constraintAnnotation; + } + break; + } + } + + if (!found) { + if (constraintAnnotation != null) { + layoutAfter(anno, constraintAnnotation, g, x, startAddr, lineHeight); + constraintAnnotation = anno; + } else { + // Defer layout of this annotation until later + ++deferredIndex; + } + } + + newAnnos.add(anno); + } + + if (firstConstraintAnnotation != null) { + // Go back and lay out deferred annotations + for (int i = deferredIndex; i >= 0; i--) { + Annotation anno = (Annotation) newAnnos.get(i); + layoutBefore(anno, firstConstraintAnnotation, g, x, startAddr, lineHeight); + firstConstraintAnnotation = anno; + } + } else { + // Didn't find any overlap between the old and new annotations. + // Lay out in a feed-forward fashion. + if (Assert.ASSERTS_ENABLED) { + Assert.that(constraintAnnotation == null, "logic error in layout code"); + } + for (Iterator iter = newAnnos.iterator(); iter.hasNext(); ) { + Annotation anno = (Annotation) iter.next(); + layoutAfter(anno, constraintAnnotation, g, x, startAddr, lineHeight); + constraintAnnotation = anno; + } + } + + visibleAnnotations = newAnnos; + } + + /** Lays out the given annotation before the optional constraint + annotation, obeying constraints imposed by that annotation if it + is specified. */ + private void layoutBefore(Annotation anno, Annotation constraintAnno, + Graphics g, int x, + Address startAddr, int lineHeight) { + anno.computeWidthAndHeight(g); + // Color + if (constraintAnno != null) { + anno.setColor(prevColor(constraintAnno.getColor())); + } else { + anno.setColor(colors[0]); + } + // X + anno.setX(x); + // Tentative Y + anno.setY((int) (((Address) anno.getInterval().getLowEndpoint()).minus(startAddr) * lineHeight / addressSize) + + (5 * lineHeight / 6)); + // See whether Y overlaps with last anno's Y; if so, move this one up + if ((constraintAnno != null) && (anno.getY() + anno.getHeight() > constraintAnno.getY())) { + anno.setY(constraintAnno.getY() - anno.getHeight()); + } + } + + /** Lays out the given annotation after the optional constraint + annotation, obeying constraints imposed by that annotation if it + is specified. */ + private void layoutAfter(Annotation anno, Annotation constraintAnno, + Graphics g, int x, + Address startAddr, int lineHeight) { + anno.computeWidthAndHeight(g); + // Color + if (constraintAnno != null) { + anno.setColor(nextColor(constraintAnno.getColor())); + } else { + anno.setColor(colors[0]); + } + // X + anno.setX(x); + // Tentative Y + anno.setY((int) (((Address) anno.getInterval().getLowEndpoint()).minus(startAddr) * lineHeight / addressSize) + + (5 * lineHeight / 6)); + // See whether Y overlaps with last anno's Y; if so, move this one down + if ((constraintAnno != null) && (anno.getY() < (constraintAnno.getY() + constraintAnno.getHeight()))) { + anno.setY(constraintAnno.getY() + constraintAnno.getHeight()); + } + } + + /** Returns previous color in our color palette */ + private Color prevColor(Color c) { + int i = findColorIndex(c); + if (i == 0) { + return colors[colors.length - 1]; + } else { + return colors[i - 1]; + } + } + + /** Returns next color in our color palette */ + private Color nextColor(Color c) { + return colors[(findColorIndex(c) + 1) % colors.length]; + } + + private int findColorIndex(Color c) { + for (int i = 0; i < colors.length; i++) { + if (colors[i] == c) { + return i; + } + } + throw new IllegalArgumentException(); + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + DummyDebugger debugger = new DummyDebugger(new MachineDescriptionIntelX86()); + AnnotatedMemoryPanel anno = new AnnotatedMemoryPanel(debugger); + frame.getContentPane().add(anno); + anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000000"), + debugger.parseAddress("0x80000040"), + "Stack Frame for \"foo\"")); + anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000010"), + debugger.parseAddress("0x80000020"), + "Locals for \"foo\"")); + anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000020"), + debugger.parseAddress("0x80000030"), + "Expression stack for \"foo\"")); + + frame.setSize(400, 300); + frame.addWindowListener(new WindowAdapter() { + public void windowClosed(WindowEvent e) { + System.exit(0); + } + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + frame.show(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/Annotation.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/Annotation.java new file mode 100644 index 00000000000..e6696cfa175 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/Annotation.java @@ -0,0 +1,161 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.utilities.*; + +/** This can be extended, along with AnnotatedMemoryPanel, to be an + arbitrarily complex mechanism, supporting user interaction, + etc. */ + +public class Annotation { + private Interval interval; + // List + private java.util.List strings; + // List + private java.util.List heights; + private Color baseColor; + private int width; + private int height; + private int x; + private int y; + + /** The Annotation can handle the sense of lowAddress and + highAddress being swapped. */ + public Annotation(Address lowAddress, + Address highAddress, + String s) { + strings = new ArrayList(); + heights = new ArrayList(); + for (StringTokenizer tok = new StringTokenizer(s, "\n"); tok.hasMoreTokens(); ) { + strings.add(tok.nextToken()); + } + if (AddressOps.lessThan(highAddress, lowAddress)) { + Address temp = lowAddress; + lowAddress = highAddress; + highAddress = temp; + } + interval = new Interval(lowAddress, highAddress); + } + + public Interval getInterval() { + return interval; + } + + public Address getLowAddress() { + return (Address) getInterval().getLowEndpoint(); + } + + public Address getHighAddress() { + return (Address) getInterval().getHighEndpoint(); + } + + /** Draw the annotation at its current (x, y) position with its + current color */ + public void draw(Graphics g) { + g.setColor(baseColor); + int tmpY = y; + for (int i = 0; i < strings.size(); i++) { + String s = (String) strings.get(i); + Integer h = (Integer) heights.get(i); + g.drawString(s, x, tmpY); + tmpY += h.intValue(); + } + } + + /** Sets the base color of this annotation. The annotation may + render sub-portions in a different color if desired. */ + public void setColor(Color c) { + this.baseColor = c; + } + + /** Returns the base color of this annotation. */ + public Color getColor() { + return baseColor; + } + + /** Computes width and height for this Annotation. Retrieve the + computed information using getWidth() and getHeight(). Separated + because the width and height only need to be recomputed if the + font changes. */ + public void computeWidthAndHeight(Graphics g) { + width = 0; + height = 0; + heights.clear(); + for (Iterator iter = strings.iterator(); iter.hasNext(); ) { + String s = (String) iter.next(); + Rectangle2D bounds = GraphicsUtilities.getStringBounds(s, g); + width = Math.max(width, (int) bounds.getWidth()); + height += (int) bounds.getHeight(); + heights.add(new Integer((int) bounds.getHeight())); + } + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + /** Set the x and y position of this annotation */ + public void setXAndY(int x, int y) { + this.x = x; this.y = y; + } + + public void setX(int x) { + this.x = x; + } + + public int getX() { + return x; + } + + public void setY(int y) { + this.y = y; + } + + public int getY() { + return y; + } + + public Rectangle getBounds() { + return new Rectangle(x, y, width, height); + } + public String toString() { + String result = "Annotation: lowAddr: " + getLowAddress() + " highAddr: " + getHighAddress() + " strings: " + strings.size(); + for (int i = 0; i < strings.size(); i++) { + result += "\n" + (String) strings.get(i); + } + return result; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/CommandProcessorPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/CommandProcessorPanel.java new file mode 100644 index 00000000000..e340a78a94f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/CommandProcessorPanel.java @@ -0,0 +1,226 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.io.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +import sun.jvm.hotspot.CommandProcessor; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.utilities.*; + +/** A JPanel subclass containing a scrollable text area displaying the + debugger's console, if it has one. This should not be created for + a debugger which does not have a console. */ + +public class CommandProcessorPanel extends JPanel { + private CommandProcessor commands; + private JTextArea editor; + private boolean updating; + private int mark; + private String curText; // handles multi-line input via '\' + + // Don't run the "main" method of this class unless this flag is set to true first + private static final boolean DEBUGGING = false; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(10240); + + + public CommandProcessorPanel(CommandProcessor cp) { + + commands = cp; + + setLayout(new BorderLayout()); + + editor = new JTextArea(); + editor.setDocument(new EditableAtEndDocument()); + editor.setFont(GraphicsUtilities.lookupFont("Courier")); + JScrollPane scroller = new JScrollPane(); + scroller.getViewport().add(editor); + add(scroller, BorderLayout.CENTER); + + // Set up out + PrintStream o = new PrintStream(baos, true); + cp.setOutput(o); + cp.setErr(o); + + editor.getDocument().addDocumentListener(new DocumentListener() { + public void changedUpdate(DocumentEvent e) { + } + + public void insertUpdate(DocumentEvent e) { + if (updating) return; + beginUpdate(); + editor.setCaretPosition(editor.getDocument().getLength()); + if (insertContains(e, '\n')) { + String cmd = getMarkedText(); + // Handle multi-line input + if ((cmd.length() == 0) || (cmd.charAt(cmd.length() - 1) != '\\')) { + // Trim "\\n" combinations + final String ln = trimContinuations(cmd); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + beginUpdate(); + try { + commands.executeCommand(ln); + commands.printPrompt(); + Document d = editor.getDocument(); + try { + d.insertString(d.getLength(), baos.toString(), null); + } + catch (BadLocationException ble) { + ble.printStackTrace(); + } + baos.reset(); + editor.setCaretPosition(editor.getDocument().getLength()); + setMark(); + } finally { + endUpdate(); + } + } + }); + } + } else { + endUpdate(); + } + } + + public void removeUpdate(DocumentEvent e) { + } + }); + + // This is a bit of a hack but is probably better than relying on + // the JEditorPane to update the caret's position precisely the + // size of the insertion + editor.addCaretListener(new CaretListener() { + public void caretUpdate(CaretEvent e) { + int len = editor.getDocument().getLength(); + if (e.getDot() > len) { + editor.setCaretPosition(len); + } + } + }); + + Box hbox = Box.createHorizontalBox(); + hbox.add(Box.createGlue()); + JButton button = new JButton("Clear Saved Text"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + clear(); + } + }); + hbox.add(button); + hbox.add(Box.createGlue()); + add(hbox, BorderLayout.SOUTH); + + clear(); + } + + public void requestFocus() { + editor.requestFocus(); + } + + public void clear() { + EditableAtEndDocument d = (EditableAtEndDocument) editor.getDocument(); + d.clear(); + commands.executeCommand(""); + setMark(); + editor.requestFocus(); + } + + public void setMark() { + ((EditableAtEndDocument) editor.getDocument()).setMark(); + } + + public String getMarkedText() { + try { + String s = ((EditableAtEndDocument) editor.getDocument()).getMarkedText(); + int i = s.length(); + while ((i > 0) && (s.charAt(i - 1) == '\n')) { + i--; + } + return s.substring(0, i); + } + catch (BadLocationException e) { + e.printStackTrace(); + return null; + } + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private void beginUpdate() { + updating = true; + } + + private void endUpdate() { + updating = false; + } + + private boolean insertContains(DocumentEvent e, char c) { + String s = null; + try { + s = editor.getText(e.getOffset(), e.getLength()); + for (int i = 0; i < e.getLength(); i++) { + if (s.charAt(i) == c) { + return true; + } + } + } + catch (BadLocationException ex) { + ex.printStackTrace(); + } + return false; + } + + private String trimContinuations(String text) { + int i; + while ((i = text.indexOf("\\\n")) >= 0) { + text = text.substring(0, i) + text.substring(i+2, text.length()); + } + return text; + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + frame.getContentPane().setLayout(new BorderLayout()); + CommandProcessorPanel panel = new CommandProcessorPanel(null); + frame.getContentPane().add(panel, BorderLayout.CENTER); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + frame.setSize(500, 500); + frame.show(); + panel.requestFocus(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/DeadlockDetectionPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/DeadlockDetectionPanel.java new file mode 100644 index 00000000000..f8a77c444a9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/DeadlockDetectionPanel.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.io.*; +import javax.swing.*; +import sun.jvm.hotspot.runtime.*; + +/** A JPanel to show information about Java-level deadlocks. */ + +public class DeadlockDetectionPanel extends JPanel { + public DeadlockDetectionPanel() { + super(); + + setLayout(new BorderLayout()); + + // Simple at first + JScrollPane scroller = new JScrollPane(); + JTextArea textArea = new JTextArea(); + textArea = new JTextArea(); + textArea.setEditable(false); + textArea.setLineWrap(true); + textArea.setWrapStyleWord(true); + scroller.getViewport().add(textArea); + add(scroller, BorderLayout.CENTER); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PrintStream tty = new PrintStream(bos); + printDeadlocks(tty); + textArea.setText(bos.toString()); + } + + private void printDeadlocks(PrintStream tty) { + DeadlockDetector.print(tty); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/DebuggerConsolePanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/DebuggerConsolePanel.java new file mode 100644 index 00000000000..7317fa0955c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/DebuggerConsolePanel.java @@ -0,0 +1,232 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.utilities.*; + +/** A JPanel subclass containing a scrollable text area displaying the + debugger's console, if it has one. This should not be created for + a debugger which does not have a console. */ + +public class DebuggerConsolePanel extends JPanel { + private Debugger debugger; + private JTextComponent editor; + private boolean updating; + private int mark; + private String curText; // handles multi-line input via '\' + // Don't run the "main" method of this class unless this flag is set to true first + private static final boolean DEBUGGING = false; + + public DebuggerConsolePanel(Debugger debugger) { + this.debugger = debugger; + if (!DEBUGGING) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(debugger.hasConsole(), "should not create a DebuggerConsolePanel for non-console debuggers"); + } + } + + setLayout(new BorderLayout()); + + editor = new JTextArea(); + editor.setDocument(new EditableAtEndDocument()); + editor.setFont(GraphicsUtilities.lookupFont("Courier")); + JScrollPane scroller = new JScrollPane(); + scroller.getViewport().add(editor); + add(scroller, BorderLayout.CENTER); + + editor.getDocument().addDocumentListener(new DocumentListener() { + public void changedUpdate(DocumentEvent e) { + } + + public void insertUpdate(DocumentEvent e) { + if (updating) return; + beginUpdate(); + editor.setCaretPosition(editor.getDocument().getLength()); + if (insertContains(e, '\n')) { + String cmd = getMarkedText(); + // Handle multi-line input + if ((cmd.length() == 0) || (cmd.charAt(cmd.length() - 1) != '\\')) { + // Trim "\\n" combinations + cmd = trimContinuations(cmd); + final String result; + if (DEBUGGING) { + System.err.println("Entered command: \"" + cmd + "\""); + result = ""; + } else { + result = DebuggerConsolePanel.this.debugger.consoleExecuteCommand(cmd); + } + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + print(result); + printPrompt(); + setMark(); + endUpdate(); + } + }); + } + } else { + endUpdate(); + } + } + + public void removeUpdate(DocumentEvent e) { + } + }); + + // This is a bit of a hack but is probably better than relying on + // the JEditorPane to update the caret's position precisely the + // size of the insertion + editor.addCaretListener(new CaretListener() { + public void caretUpdate(CaretEvent e) { + int len = editor.getDocument().getLength(); + if (e.getDot() > len) { + editor.setCaretPosition(len); + } + } + }); + + Box hbox = Box.createHorizontalBox(); + hbox.add(Box.createGlue()); + JButton button = new JButton("Clear Saved Text"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + clear(); + } + }); + hbox.add(button); + hbox.add(Box.createGlue()); + add(hbox, BorderLayout.SOUTH); + + clear(); + } + + public void requestFocus() { + editor.requestFocus(); + } + + public void clear() { + EditableAtEndDocument d = (EditableAtEndDocument) editor.getDocument(); + d.clear(); + printPrompt(); + setMark(); + editor.requestFocus(); + } + + public void setMark() { + ((EditableAtEndDocument) editor.getDocument()).setMark(); + } + + public String getMarkedText() { + try { + String s = ((EditableAtEndDocument) editor.getDocument()).getMarkedText(); + int i = s.length(); + while ((i > 0) && (s.charAt(i - 1) == '\n')) { + i--; + } + return s.substring(0, i); + } + catch (BadLocationException e) { + e.printStackTrace(); + return null; + } + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private void beginUpdate() { + updating = true; + } + + private void endUpdate() { + updating = false; + } + + private void print(String s) { + Document d = editor.getDocument(); + try { + d.insertString(d.getLength(), s, null); + } + catch (BadLocationException e) { + e.printStackTrace(); + } + } + + private void printPrompt() { + if (DEBUGGING) { + print("foo> "); + } else { + print(debugger.getConsolePrompt()); + } + } + + private boolean insertContains(DocumentEvent e, char c) { + String s = null; + try { + s = editor.getText(e.getOffset(), e.getLength()); + for (int i = 0; i < e.getLength(); i++) { + if (s.charAt(i) == c) { + return true; + } + } + } + catch (BadLocationException ex) { + ex.printStackTrace(); + } + return false; + } + + private String trimContinuations(String text) { + int i; + while ((i = text.indexOf("\\\n")) >= 0) { + text = text.substring(0, i) + text.substring(i+2, text.length()); + } + return text; + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + frame.getContentPane().setLayout(new BorderLayout()); + DebuggerConsolePanel panel = new DebuggerConsolePanel(null); + frame.getContentPane().add(panel, BorderLayout.CENTER); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + frame.setSize(500, 500); + frame.show(); + panel.requestFocus(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/EditableAtEndDocument.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/EditableAtEndDocument.java new file mode 100644 index 00000000000..771d08d5c54 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/EditableAtEndDocument.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import javax.swing.text.*; + +/** This class implements a special type of document in which edits + can only be performed at the end, from "mark" to the end of the + document. Thanks to Scott Violet for suggesting to subclass a + Document implementation for this purpose. (Can't do it with + DocumentEvents or UndoableEditEvents; however, in 1.4, there will + be a DocumentFilter which will allow this kind of functionality.) */ + +public class EditableAtEndDocument extends PlainDocument { + private int mark; + + public void insertString(int offset, String text, AttributeSet a) + throws BadLocationException { + int len = getLength(); + super.insertString(len, text, a); + } + + public void remove(int offs, int len) throws BadLocationException { + int start = offs; + int end = offs + len; + + int markStart = mark; + int markEnd = getLength(); + + if ((end < markStart) || (start > markEnd)) { + // no overlap + return; + } + + // Determine interval intersection + int cutStart = Math.max(start, markStart); + int cutEnd = Math.min(end, markEnd); + super.remove(cutStart, cutEnd - cutStart); + } + + public void setMark() { + mark = getLength(); + } + + public String getMarkedText() throws BadLocationException { + return getText(mark, getLength() - mark); + } + + /** Used to reset the contents of this document */ + public void clear() { + try { + super.remove(0, getLength()); + setMark(); + } + catch (BadLocationException e) { + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/Editor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/Editor.java new file mode 100644 index 00000000000..370d7549033 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/Editor.java @@ -0,0 +1,69 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +/** High-level interface describing what the debugger requires from an + editor component in order to display source code. The + EditorFactory is used to instantiate Editor objects. */ + +public interface Editor { + /** Get name of source file being displayed */ + public String getSourceFileName(); + + /** Get (one-based) line number on which cursor is currently placed */ + public int getCurrentLineNumber(); + + /** Make a particular line number visible on the screen. NOTE: line + numbers are numbered starting from 1. */ + public void showLineNumber(int lineNo); + + /** Highlight a particular line number. NOTE: line numbers are + numbered starting from 1. */ + public void highlightLineNumber(int lineNo); + + /** Show a breakpoint indicator on the current (one-based) line. */ + public void showBreakpointAtLine(int lineNo); + + /** Indicates whether a breakpoint indicator is visible on the + current (one-based) line. */ + public boolean hasBreakpointAtLine(int lineNo); + + /** Clear a breakpoint indicator on the current (one-based) line. */ + public void clearBreakpointAtLine(int lineNo); + + /** Clear all breakpoint indicators. */ + public void clearBreakpoints(); + + /** Set optional object of user data */ + public void setUserData(Object o); + + /** Get optional object of user data */ + public Object getUserData(); + + /** Bring the given Editor to the front of all other Editors. This + must also make this Editor the return value from + EditorFactory.getCurrentEditor(). */ + public void toFront(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/EditorCommands.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/EditorCommands.java new file mode 100644 index 00000000000..dfb6be648ae --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/EditorCommands.java @@ -0,0 +1,37 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +/** The debugger supplies an implementation of this interface to + Editors it requests be opened by the EditorFactory. Using this + object the Editor can communicate with the debugger. */ + +public interface EditorCommands { + /** Notifies the debugger that the editing window has been closed. */ + public void windowClosed(Editor editor); + + /** Toggles a breakpoint at the given (one-based) line */ + public void toggleBreakpointAtLine(Editor editor, int lineNumber); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/EditorFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/EditorFactory.java new file mode 100644 index 00000000000..1fa34b5a575 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/EditorFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import javax.swing.JComponent; + +/** An EditorFactory is the basis of pluggable editor components. One + can configure the debugger with a new EditorFactory, which + completely replaces how the debugger displays source code. */ + +public interface EditorFactory { + /** Opens the given file in a new window. The debugger has already + taken care of ensuring that the file can be found. The debugger + will typically not create two Editor objects for the same source + file, as it keeps track of open files. The EditorCommands object + provided to the Editor by the debugger allows the Editor to + notify the debugger of events such as a breakpoint being set or + a window being closed. */ + public Editor openFile(String filename, EditorCommands commands); + + /** Retrieves the current topmost file of all of the Editors this + EditorFactory has opened. This is used for the debugger user + interface to request that a breakpoint be set. (Editors can also + request that breakpoints be set via the EditorCommands, but this + is intended to support external editors with their own + keystrokes for performing this operation.) Returns null if there + is no file currently being edited. */ + public Editor getCurrentEditor(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindByQueryPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindByQueryPanel.java new file mode 100644 index 00000000000..3819e1cdfe9 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindByQueryPanel.java @@ -0,0 +1,111 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; +import javax.swing.event.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.ui.tree.*; +import sun.jvm.hotspot.utilities.soql.*; + +public class FindByQueryPanel extends SAPanel { + private JTextArea queryEditor; + private JEditorPane objectsEditor; + private SOQLEngine queryEngine; + + public FindByQueryPanel() { + queryEngine = SOQLEngine.getEngine(); + HyperlinkListener hyperListener = new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + VM vm = VM.getVM(); + OopHandle handle = vm.getDebugger().parseAddress(e.getDescription()).addOffsetToAsOopHandle(0); + showInspector(vm.getObjectHeap().newOop(handle)); + } + } + }; + + objectsEditor = new JEditorPane(); + objectsEditor.setContentType("text/html"); + objectsEditor.setEditable(false); + objectsEditor.addHyperlinkListener(hyperListener); + + queryEditor = new JTextArea(); + JButton queryButton = new JButton("Execute"); + queryButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + final StringBuffer buf = new StringBuffer(); + buf.append(""); + try { + queryEngine.executeQuery(queryEditor.getText(), + new ObjectVisitor() { + public void visit(Object o) { + if (o != null && o instanceof JSJavaObject) { + String oopAddr = ((JSJavaObject)o).getOop().getHandle().toString(); + buf.append(""); + buf.append(oopAddr); + buf.append(""); + } else { + buf.append((o == null)? "null" : o.toString()); + } + buf.append("
"); + } + }); + + } catch (Exception e) { + e.printStackTrace(); + buf.append(""); + buf.append(e.getMessage()); + buf.append(""); + } + buf.append(""); + objectsEditor.setText(buf.toString()); + } + }); + + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BorderLayout()); + topPanel.add(new JLabel("SOQL Query :"), BorderLayout.WEST); + topPanel.add(new JScrollPane(queryEditor), BorderLayout.CENTER); + topPanel.add(queryButton, BorderLayout.EAST); + + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new BorderLayout()); + bottomPanel.add(new JScrollPane(objectsEditor), BorderLayout.CENTER); + + JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topPanel, bottomPanel); + splitPane.setDividerLocation(0.3); + + setLayout(new BorderLayout()); + add(splitPane, BorderLayout.CENTER); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindInCodeCachePanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindInCodeCachePanel.java new file mode 100644 index 00000000000..b30d87862be --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindInCodeCachePanel.java @@ -0,0 +1,221 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.io.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.ui.classbrowser.*; + +/** Finds a given (Address) value in the code cache. Only intended for use + in a debugging system. */ + +public class FindInCodeCachePanel extends SAPanel { + private Visitor iterator; + private long usedSize; + private long iterated; + private Address value; + private ProgressBarPanel progressBar; + private HistoryComboBox addressField; + private JButton findButton; + private SAEditorPane contentEditor; + + class Visitor implements CodeCacheVisitor { + Address base; + StringBuffer result; + boolean searching; + + public void prologue(Address start, Address end) { + searching = true; + base = start; + usedSize = end.minus(start); + iterated = 0; + result = new StringBuffer(); + clearResultWindow(); + } + + public void visit(CodeBlob blob) { + Address begin = blob.headerBegin(); + Address end = begin.addOffsetTo(blob.getSize()); + long addressSize = VM.getVM().getAddressSize(); + + boolean found = false; + while (!found && begin.lessThan(end)) { + Address val = begin.getAddressAt(0); + if (AddressOps.equal(val, value)) { + reportResult(result, blob); + found = true; + } + begin = begin.addOffsetTo(addressSize); + } + iterated = end.minus(base);; + updateProgressBar(null); + } + + public void epilogue() { + } + + public void cleanup() { + iterated = 0; + updateProgressBar(result); + searching = false; + result = null; + } + + private void search() { + // Parse text + Address val = null; + try { + val = VM.getVM().getDebugger().parseAddress(addressField.getText()); + } catch (Exception ex) { + contentEditor.setText("Error parsing address"); + return; + } + + // make sure we don't start up multiple search threads in parallel + synchronized (iterator) { + if (searching && value.equals(val)) { + return; + } + + value = val; + contentEditor.setText(""); + findButton.setEnabled(false); + + System.out.println("Searching " + value); + java.lang.Thread t = new java.lang.Thread(new Runnable() { + public void run() { + synchronized (iterator) { + try { + VM.getVM().getCodeCache().iterate(iterator); + } finally { + iterator.cleanup(); + } + } + } + }); + t.start(); + } + } + } + + + public FindInCodeCachePanel() { + super(); + + setLayout(new BorderLayout()); + + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS)); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.add(new JLabel("Address to search for:")); + + addressField = new HistoryComboBox(); + panel.add(addressField); + + iterator = new Visitor(); + + findButton = new JButton("Find"); + ActionListener listener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + iterator.search(); + } + }; + panel.add(findButton); + findButton.addActionListener(listener); + addressField.addActionListener(listener); + topPanel.add(panel); + + progressBar = new ProgressBarPanel(ProgressBarPanel.HORIZONTAL, "Search progress:"); + topPanel.add(progressBar); + + add(topPanel, BorderLayout.NORTH); + + contentEditor = new SAEditorPane(); + + HyperlinkListener hyperListener = new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + String description = e.getDescription(); + int index = description.indexOf(':'); + if (index != -1) { + String item = description.substring(0, index); + if (item.equals("blob")) { + Address blob = VM.getVM().getDebugger().parseAddress(description.substring(index + 1)); + showCodeViewer(blob); + } + } + } + } + }; + + contentEditor.addHyperlinkListener(hyperListener); + + JScrollPane scroller = new JScrollPane(contentEditor); + add(scroller, BorderLayout.CENTER); + } + + private void reportResult(StringBuffer result, CodeBlob blob) { + result.append(""); + result.append(blob.getName()); + result.append("@"); + result.append(blob.instructionsBegin()); + result.append("
"); + } + + private void clearResultWindow() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + contentEditor.setText(""); + } + }); + } + + private void updateProgressBar(final StringBuffer result) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progressBar.setValue((double) iterated / (double) usedSize); + if (result != null) { + String s = " \n" + result + " "; + contentEditor.setText(s); + findButton.setEnabled(true); + } + } + }); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindInHeapPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindInHeapPanel.java new file mode 100644 index 00000000000..38f57682a2d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindInHeapPanel.java @@ -0,0 +1,217 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.util.*; +import java.io.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.text.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +/** Finds a given (Address) value in the heap. Only intended for use + in a debugging system. */ + +public class FindInHeapPanel extends JPanel { + private RawHeapVisitor iterator; + private long addressSize; + private long usedSize; + private long iterated; + private Address value; + private ProgressBarPanel progressBar; + private HistoryComboBox addressField; + private JButton findButton; + private JTextArea textArea; + private ArrayList updates; + private double lastFrac; + + static final double minUpdateFraction = 0.05; + + public FindInHeapPanel() { + super(); + + setLayout(new BorderLayout()); + + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS)); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.add(new JLabel("Address to search for:")); + + addressField = new HistoryComboBox(); + panel.add(addressField); + + addressSize = VM.getVM().getAddressSize(); + + iterator = new RawHeapVisitor() { + boolean error = false; + + public void prologue(long used) { + usedSize = used; + iterated = 0; + lastFrac = 0; + error = false; + updates = new ArrayList(); + } + + public void visitAddress(Address addr) { + if (error) return; + + Address val = addr.getAddressAt(0); + if (AddressOps.equal(val, value)) { + error = reportResult(addr); + } + iterated += addressSize; + updateProgressBar(); + } + + public void epilogue() { + iterated = 0; + updateProgressBar(); + findButton.setEnabled(true); + } + }; + + findButton = new JButton("Find"); + ActionListener listener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + clearResultWindow(); + // Parse text + try { + Address val = VM.getVM().getDebugger().parseAddress(addressField.getText()); + value = val; + + findButton.setEnabled(false); + + java.lang.Thread t = new java.lang.Thread(new Runnable() { + public void run() { + try { + VM.getVM().getObjectHeap().iterateRaw(iterator); + } finally { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + findButton.setEnabled(true); + } + }); + } + } + }); + t.start(); + } catch (Exception ex) { + textArea.setText("Error parsing address"); + } + } + }; + panel.add(findButton); + findButton.addActionListener(listener); + addressField.addActionListener(listener); + topPanel.add(panel); + + progressBar = new ProgressBarPanel(ProgressBarPanel.HORIZONTAL, "Search progress:"); + topPanel.add(progressBar); + + add(topPanel, BorderLayout.NORTH); + + textArea = new JTextArea(); + JScrollPane scroller = new JScrollPane(textArea); + add(scroller, BorderLayout.CENTER); + } + + private boolean pendingUpdate = false; + + private boolean reportResult(final Address addr) { + synchronized (this) { + try { + updates.add("found at " + addr + "\n"); + if (!pendingUpdate) { + pendingUpdate = true; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + updateResultWindow(); + } + }); + } + } catch (Throwable t) { + t.printStackTrace(); + return true; + } + } + + return false; + } + + private void clearResultWindow() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + + Document d = textArea.getDocument(); + try { + d.remove(0, d.getLength()); + } catch (BadLocationException e) { + } + } + }); + } + + private synchronized void updateResultWindow() { + if (updates.size() > 0) { + Iterator i = updates.iterator(); + while (i.hasNext()) { + textArea.append((String)i.next()); + } + updates = new ArrayList();; + } + pendingUpdate = false; + } + + private void invokeInDispatchThread(Runnable runnable) { + if (EventQueue.isDispatchThread()) { + runnable.run(); + } else { + SwingUtilities.invokeLater(runnable); + } + } + + private void updateProgressBar() { + final double frac = (double) iterated / (double) usedSize; + if (frac == 0.0 || (frac - lastFrac > minUpdateFraction)) { + lastFrac = frac; + if (iterated > usedSize) { + System.out.println("iterated " + iterated + " usedSize " + usedSize); + } + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progressBar.setValue(frac); + } + }); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindPanel.java new file mode 100644 index 00000000000..bb8cbb05dbd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FindPanel.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.io.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.text.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +/** Uses {@link sun.jvm.hotspot.utilities.PointerFinder} to provide a + graphical user interface to the VM's debugging utility "find". */ + +public class FindPanel extends JPanel { + // UI widgets we need permanent handles to + private JTextField addressField; + private JTextArea textArea; + private JLabel statusLabel; + + public FindPanel() { + super(); + + setLayout(new BorderLayout()); + Box hbox = Box.createHorizontalBox(); + hbox.add(new JLabel("Address: ")); + addressField = new JTextField(20); + hbox.add(addressField); + statusLabel = new JLabel(); + hbox.add(statusLabel); + add(hbox, BorderLayout.NORTH); + + JScrollPane scroller = new JScrollPane(); + textArea = new JTextArea(); + textArea.setEditable(false); + textArea.setLineWrap(true); + textArea.setWrapStyleWord(true); + scroller.getViewport().add(textArea); + add(scroller, BorderLayout.CENTER); + + addressField.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + Address a = VM.getVM().getDebugger().parseAddress(addressField.getText()); + PointerLocation loc = PointerFinder.find(a); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + loc.printOn(new PrintStream(bos)); + clear(); + textArea.append(bos.toString()); + statusLabel.setText(""); + } + catch (NumberFormatException ex) { + statusLabel.setText(""); + } + catch (AddressException ex) { + statusLabel.setText(""); + } + catch (Exception ex) { + ex.printStackTrace(); + statusLabel.setText(""); + } + } + }); + } + + private void clear() { + Document doc = textArea.getDocument(); + if (doc.getLength() > 0) { + try { + doc.remove(0, doc.getLength()); + } + catch (BadLocationException e) { + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FrameWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FrameWrapper.java new file mode 100644 index 00000000000..4fb9fecd750 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/FrameWrapper.java @@ -0,0 +1,65 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; + +/** Provides uniform interface for dealing with JInternalFrames and + JFrames. */ + +public interface FrameWrapper { + /** The JInternalFrame or JFrame this wraps */ + public Component getComponent(); + + public Container getContentPane(); + public void setVisible(boolean visible); + public void setSize(int x, int y); + public void pack(); + public void show(); + public void dispose(); + public void setBackground(Color color); + public void setResizable(boolean resizable); + + /** Largely for use with JInternalFrames but also affects, for + example, the default close operation for JFrames */ + public void setClosable(boolean closable); + + /** Set an ActionListener to be invoked when the underlying window + is closing ("windowClosing" event of a WindowListener). Note: + the ActionEvent passed to this listener may be null. */ + public void setClosingActionListener(ActionListener l); + + /** Set an ActionListener to be invoked when the underlying window + is activated ("windowActivated" event of a + WindowListener). Note: the ActionEvent passed to this listener + may be null. */ + public void setActivatedActionListener(ActionListener l); + + /** Move this frame to the front. Should change focus to the frame + if possible. */ + public void toFront(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/GraphicsUtilities.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/GraphicsUtilities.java new file mode 100644 index 00000000000..43282d276fe --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/GraphicsUtilities.java @@ -0,0 +1,156 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.util.Random; +import javax.swing.*; +import javax.swing.border.*; + +/** Useful utilities for drawing graphics */ + +public class GraphicsUtilities { + /** Returns a plain-styled 12-point version of the given font, or + null if the font could not be found */ + public static Font lookupFont(String fontName) { + Font[] allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + Font font = null; + for (int i = 0; i < allFonts.length; i++) { + if (allFonts[i].getFontName().indexOf(fontName) != -1) { + font = allFonts[i]; + break; + } + } + if (font == null) { + return null; + } + return font.deriveFont(Font.PLAIN, 12); + } + + /** Compute the width and height of given string given the current + font context in the Graphics object */ + public static Rectangle2D getStringBounds(String s, Graphics g) { + FontMetrics fm = g.getFontMetrics(); + return fm.getStringBounds(s, 0, s.length(), g); + } + + /** Compute just the width of the given string with the given + FontMetrics. This is less accurate then getStringBounds(), + above, since the graphics context is not taken into account. */ + public static int getStringWidth(String s, FontMetrics fm) { + return fm.stringWidth(s); + } + + public static void reshapeToAspectRatio(Component component, + float aspectRatio, + float fillRatio, + Dimension containerDimension) { + int x = containerDimension.width; + int y = containerDimension.height; + + int desiredX; + int desiredY; + + if (((float) x / (float) y) > aspectRatio) { + desiredY = (int) (fillRatio * y); + desiredX = (int) (desiredY * aspectRatio); + } else { + desiredX = (int) (fillRatio * x); + desiredY = (int) (desiredX / aspectRatio); + } + component.setSize(desiredX, desiredY); + } + + public static void constrainToSize(Component component, Dimension containerDimension) { + Dimension d = component.getSize(); + int x = d.width; + int y = d.height; + boolean changed = false; + + if (x > containerDimension.width) { + x = containerDimension.width; + changed = true; + } + if (y > containerDimension.height) { + y = containerDimension.height; + changed = true; + } + + if (changed) { + component.setSize(x, y); + } + } + + public static void centerInContainer(Component c) { + centerInContainer(c, c.getParent().getSize()); + } + + public static void centerInContainer(Component component, + Dimension containerDimension) { + Dimension sz = component.getSize(); + int x = ((containerDimension.width - sz.width) / 2); + int y = ((containerDimension.height - sz.height) / 2); + component.setLocation(x, y); + } + + public static void moveToInContainer(Component component, + float relativeX, + float relativeY, + int minX, + int minY) { + Dimension d = component.getParent().getSize(); + // Move the center of this component to the relative position in + // the parent. Don't clip this component, however. + Dimension sz = component.getSize(); + int xPos = Math.min(d.width - sz.width, + (int) ((d.width * relativeX) - (sz.width / 2))); + int yPos = Math.min(d.height - sz.height, + (int) ((d.height * relativeY) - (sz.height / 2))); + xPos = Math.max(xPos, minX); + yPos = Math.max(yPos, minY); + component.setLocation(xPos, yPos); + } + + static Random random = new Random(); + + public static void randomLocation(Component c) { + randomLocation(c, c.getParent().getSize()); + } + + public static void randomLocation(Component component, + Dimension containerDimension) { + Dimension sz = component.getSize(); + int x = (int)((containerDimension.width - sz.width) * random.nextFloat()); + int y = (int)((containerDimension.height - sz.height) * random.nextFloat()); + component.setLocation(x, y); + } + + + public static Border newBorder(int size) { + return BorderFactory.createEmptyBorder(size, size, size, size); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/HeapParametersPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/HeapParametersPanel.java new file mode 100644 index 00000000000..c8fc636ad77 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/HeapParametersPanel.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.io.*; +import javax.swing.*; + +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; + +/** Provides information about heap. FIXME: add back in information + about card marking table (was present before GC interface). */ + +public class HeapParametersPanel extends JPanel { + public HeapParametersPanel() { + super(); + + setLayout(new BorderLayout()); + + // Simple at first + JScrollPane scroller = new JScrollPane(); + JTextArea textArea = new JTextArea(); + textArea = new JTextArea(); + textArea.setEditable(false); + textArea.setLineWrap(true); + textArea.setWrapStyleWord(true); + scroller.getViewport().add(textArea); + add(scroller, BorderLayout.CENTER); + + Universe u = VM.getVM().getUniverse(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PrintStream tty = new PrintStream(bos); + tty.println("Heap Parameters:"); + u.heap().printOn(tty); + + textArea.setText(bos.toString()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/HighPrecisionJScrollBar.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/HighPrecisionJScrollBar.java new file mode 100644 index 00000000000..073afd6e6a7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/HighPrecisionJScrollBar.java @@ -0,0 +1,430 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import java.math.*; +import java.util.*; + +/** A JScrollBar which uses BigIntegers as the representation for the + minimum, maximum, unit increment, etc. Interaction with the + buttons and track is accurate to unit and block increments; + however, if the scale of the scrollbar (defined by + getMaximumHP().subtract(getMinimumHP())) is very large, each + interaction with the thumb will necessarily cause extremely large + motion of the value. */ + +public class HighPrecisionJScrollBar extends JScrollBar { + private BigInteger valueHP; + private BigInteger visibleHP; + private BigInteger minimumHP; + private BigInteger maximumHP; + private BigInteger unitIncrementHP; + private BigInteger blockIncrementHP; + private BigDecimal scaleFactor; + private BigInteger rangeHP; + // The underlying scrollbar runs a range from 0..BIG_RANGE-1 + private static final int BIG_RANGE = 10000; + // Do we need to scale HP values up/down to fit in 0..BIG_RANGE-1? + private boolean down; + private java.util.List changeListeners = new ArrayList(); + // Number of digits after decimal point to use when scaling between + // high and low precision + private static final int SCALE = 20; + + + // This is a hack to allow us to differentiate between clicks on the + // arrow and track since we can't get useful information from + // JScrollBars' AdjustmentListener (bug in design of BasicUI + // classes; FIXME: file RFE.) + private static final int UNIT_INCREMENT = 1; + private static final int BLOCK_INCREMENT = 2; + private static final int MINIMUM = 0; + private static final int MAXIMUM = 65536; + private boolean updating = false; + private int lastValueSeen = -1; + + public HighPrecisionJScrollBar() { + super(); + initialize(); + installListener(); + } + + public HighPrecisionJScrollBar(int orientation) { + super(orientation); + initialize(); + installListener(); + } + + /** value, minimum and maximum should be positive */ + public HighPrecisionJScrollBar(int orientation, BigInteger value, BigInteger minimum, BigInteger maximum) { + super(orientation); + initialize(value, minimum, maximum); + installListener(); + } + + public BigInteger getValueHP() { + return valueHP; + } + + + /** NOTE: the real value will always be set to be (value mod + unitIncrement) == 0, subtracting off the mod of the passed value + if necessary. */ + + public void setValueHP(BigInteger value) { + if (value.compareTo(getMaximumHP()) > 0) { + value = getMaximumHP(); + } else if (value.compareTo(getMinimumHP()) < 0) { + value = getMinimumHP(); + } + valueHP = value.subtract(value.mod(unitIncrementHP)); + int lpValue = toUnderlyingRange(this.valueHP); + if (getValueHP().add(getVisibleAmountHP()).compareTo(getMaximumHP()) >= 0 ) { + lpValue = BIG_RANGE - getVisibleAmount(); + } + lastValueSeen = lpValue; + setValue(lpValue); + fireStateChanged(); + } + public BigInteger getMinimumHP() { + return minimumHP; + } + + public void setMinimumHP(BigInteger minimum) { + setRange(minimum, maximumHP); + updateScrollBarValues(); + } + + public BigInteger getMaximumHP() { + return maximumHP; + } + + public void setMaximumHP(BigInteger maximum) { + setRange(minimumHP, maximum); + updateScrollBarValues(); + } + + public BigInteger getVisibleAmountHP() { + return visibleHP; + } + + public void setVisibleAmountHP(BigInteger visibleAmount) { + this.visibleHP = visibleAmount; + // int lpVisAmt = toUnderlyingRange(visibleAmount); + // Make certain that visibleAmount value that are full range come out looking like full range + int lpVisAmt; + if (visibleAmount.compareTo(rangeHP) < 0) { + lpVisAmt = scaleToUnderlying(visibleAmount); + if (lpVisAmt == 0) { + lpVisAmt = 1; + } + setVisible(true); + } else { + lpVisAmt = BIG_RANGE; + setVisible(false); + } + setVisibleAmount(lpVisAmt); + } + + public BigInteger getBlockIncrementHP() { + return blockIncrementHP; + } + + public void setBlockIncrementHP(BigInteger blockIncrement) { + this.blockIncrementHP = blockIncrement; + // NOTE we do not forward this to the underlying scrollBar because of + // the earlier mentioned hack. + } + + public BigInteger getUnitIncrementHP() { + return unitIncrementHP; + } + + public void setUnitIncrementHP(BigInteger unitIncrement) { + this.unitIncrementHP = unitIncrement; + // NOTE we do not forward this to the underlying scrollBar because of + // the earlier mentioned hack. + } + + + public void addChangeListener(ChangeListener l) { + changeListeners.add(l); + } + + public void removeChangeListener(ChangeListener l) { + changeListeners.remove(l); + } + + //---------------------------------------------------------------------- + // Programmatic access to scrollbar functionality + // (Causes change events to be sent) + + public void scrollUpOrLeft() { + if (updating) return; + beginUpdate(); + setValueHP(getValueHP().subtract(getUnitIncrementHP())); + endUpdate(); + } + + public void scrollDownOrRight() { + if (updating) return; + beginUpdate(); + setValueHP(getValueHP().add(getUnitIncrementHP())); + endUpdate(); + } + + public void pageUpOrLeft() { + if (updating) return; + beginUpdate(); + setValueHP(getValueHP().subtract(getBlockIncrementHP())); + endUpdate(); + } + + public void pageDownOrRight() { + if (updating) return; + beginUpdate(); + setValueHP(getValueHP().add(getBlockIncrementHP())); + endUpdate(); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void beginUpdate() { + updating = true; + } + + private void endUpdate() { + updating = false; + } + + private void initialize(BigInteger value, BigInteger minimum, BigInteger maximum) { + // Initialize the underlying scrollbar to the standard range values + // The increments are important and are how we differentiate arrow from track events + setMinimum(0); + setMaximum(BIG_RANGE - 1); + setValue(0); + setVisibleAmount(1); + setUnitIncrement(UNIT_INCREMENT); + setBlockIncrement(BLOCK_INCREMENT); + + setUnitIncrementHP(new BigInteger(Integer.toString(getUnitIncrement()))); + setBlockIncrementHP(new BigInteger(Integer.toString(getBlockIncrement()))); + + // Must set range and value first (it sets min/max) + setRange(minimum, maximum); + + setVisibleAmountHP(new BigInteger(Integer.toString(getVisibleAmount()))); + setValueHP(value); + } + + private void initialize() { + BigInteger min = new BigInteger(Integer.toString(getMinimum())); + BigInteger max = new BigInteger(Integer.toString(getMaximum())); + initialize(min, min, max); + } + + private void setRange(BigInteger minimum, BigInteger maximum) { + if (minimum.compareTo(maximum) > 0 ) { + throw new RuntimeException("Bad scrollbar range " + minimum + " > " + maximum); + } + minimumHP = minimum; + maximumHP = maximum; + rangeHP = maximum.subtract(minimum).add(BigInteger.ONE); + BigInteger range2 = new BigInteger(Integer.toString(BIG_RANGE)); + if (rangeHP.compareTo(range2) >= 0 ) { + down = true; + scaleFactor = new BigDecimal(rangeHP, SCALE).divide(new BigDecimal(range2, SCALE), BigDecimal.ROUND_DOWN).max(new BigDecimal(BigInteger.ONE)); + } else { + down = false; + scaleFactor = new BigDecimal(range2, SCALE).divide(new BigDecimal(rangeHP, SCALE), BigDecimal.ROUND_DOWN).max(new BigDecimal(BigInteger.ONE)); + } + // FIXME: should put in original scaling algorithm (shifting by + // number of bits) as alternative when scale between low and high + // precision is very large + } + + // A range update is complete. Rescale our computed values and + // inform the underlying scrollBar as needed. + private void updateScrollBarValues() { + setValueHP(getValueHP()); + setVisibleAmountHP(getVisibleAmountHP()); + setBlockIncrementHP(getBlockIncrementHP()); + setUnitIncrementHP(getUnitIncrementHP()); + } + + private BigDecimal getScaleFactor() { + return scaleFactor; + } + + + // Value scaling routines + private BigInteger scaleToHP(int i) { + BigDecimal ib = new BigDecimal(Integer.toString(i)); + if (down) return ib.multiply(getScaleFactor()).toBigInteger(); + else return ib.divide(getScaleFactor(), BigDecimal.ROUND_DOWN).toBigInteger(); + } + + private int scaleToUnderlying(BigInteger i) { + BigDecimal d = new BigDecimal(i); + if (down) return d.divide(getScaleFactor(), BigDecimal.ROUND_DOWN).intValue(); + else return d.multiply(getScaleFactor()).intValue(); + } + + // Range scaling routines + private BigInteger toHPRange(int i) { + return scaleToHP(i).add(minimumHP); + // return ib.shiftLeft(Math.max(2, maximumHP.bitLength() - 33)); + } + + private int toUnderlyingRange(BigInteger i) { + return scaleToUnderlying(i.subtract(minimumHP)); + // return i.shiftRight(Math.max(2, maximumHP.bitLength() - 33)).intValue(); + } + + private void installListener() { + super.addAdjustmentListener(new AdjustmentListener() { + public void adjustmentValueChanged(AdjustmentEvent e) { + if (updating) { + return; + } + beginUpdate(); + switch (e.getAdjustmentType()) { + case AdjustmentEvent.TRACK: + int val = e.getValue(); + int diff = val - lastValueSeen; + int absDiff = Math.abs(diff); + // System.err.println("diff: " + diff + " absDiff: " + absDiff); + if (absDiff == UNIT_INCREMENT) { + if (diff > 0) { + // System.err.println("case 1"); + setValueHP(getValueHP().add(getUnitIncrementHP())); + } else { + // System.err.println("case 2"); + setValueHP(getValueHP().subtract(getUnitIncrementHP())); + } + } else if (absDiff == BLOCK_INCREMENT) { + if (diff > 0) { + // System.err.println("case 3"); + setValueHP(getValueHP().add(getBlockIncrementHP())); + } else { + // System.err.println("case 4"); + setValueHP(getValueHP().subtract(getBlockIncrementHP())); + } + } else { + // System.err.println("case 5"); + // FIXME: seem to be getting spurious update events, + // with diff = 0, upon mouse down/up on the track + if (absDiff != 0) { + // Convert low-precision value to high precision + // (note we lose the low bits) + BigInteger i = null; + if (e.getValue() == getMinimum()) { + i = getMinimumHP(); + } else if (e.getValue() >= getMaximum() - 1) { + i = getMaximumHP(); + } else { + i = toHPRange(e.getValue()); + } + setValueHP(i); + } + } + break; + default: + // Should not reach here, but leaving it a no-op in case + // we later get the other events (should revisit code in + // that case) + break; + } + endUpdate(); + } + }); + } + + private void fireStateChanged() { + ChangeEvent e = null; + for (Iterator iter = changeListeners.iterator(); iter.hasNext(); ) { + ChangeListener l = (ChangeListener) iter.next(); + if (e == null) { + e = new ChangeEvent(this); + } + l.stateChanged(e); + } + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + frame.setSize(300, 300); + // 32-bit version + /* + HighPrecisionJScrollBar hpsb = + new HighPrecisionJScrollBar( + JScrollBar.VERTICAL, + new BigInteger(1, new byte[] { + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF})); + hpsb.setUnitIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01})); + hpsb.setBlockIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10})); + */ + + // 64-bit version + HighPrecisionJScrollBar hpsb = + new HighPrecisionJScrollBar( + JScrollBar.VERTICAL, + new BigInteger(1, new byte[] { + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF})); + hpsb.setUnitIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01})); + hpsb.setBlockIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10})); + hpsb.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + HighPrecisionJScrollBar h = (HighPrecisionJScrollBar) e.getSource(); + System.out.println("New value = 0x" + h.getValueHP().toString(16)); + } + }); + frame.getContentPane().add(hpsb); + frame.show(); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/HistoryComboBox.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/HistoryComboBox.java new file mode 100644 index 00000000000..8f2730f8668 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/HistoryComboBox.java @@ -0,0 +1,66 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + + +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; + +/** Provides an editable text field with history. */ + +public class HistoryComboBox extends JComboBox { + static final int HISTORY_LENGTH = 15; + + public HistoryComboBox() { + setEditable(true); + addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Object text = getSelectedItem(); + if (text != null) { + setText((String)text); + } + } + }); + } + + public String getText() { + Object text = getSelectedItem(); + if (text == null) { + return ""; + } + return (String)text; + } + + public void setText(String text) { + removeItem(text); + insertItemAt(text, 0); + setSelectedItem(text); + int length = getModel().getSize(); + while (length > HISTORY_LENGTH) { + removeItemAt(--length); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/Inspector.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/Inspector.java new file mode 100644 index 00000000000..703060d78a4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/Inspector.java @@ -0,0 +1,358 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; +import javax.swing.tree.TreePath; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.ui.tree.*; +import sun.jvm.hotspot.utilities.*; + +/** This class implements tree-browsing functionality of a particular + SimpleTreeNode, and is only designed to be used in a debugging + system. It uses a SimpleTreeModel internally. It can inspect both + oops as well as C++ objects described by the VMStructs database in + the target VM. */ + +public class Inspector extends SAPanel { + private JTree tree; + private SimpleTreeModel model; + + // UI widgets we need permanent handles to + private HistoryComboBox addressField; + private JLabel statusLabel; + + private JButton livenessButton; + private ActionListener livenessButtonListener; + private ActionListener showLivenessListener; + private static final String computeLivenessText = "Compute Liveness"; + private static final String showLivenessText = "Show Liveness"; + private JLabel liveStatus; + private LivenessPathList list = null; + private Oop currentOop = null; + + public Inspector() { + model = new SimpleTreeModel(); + tree = new JTree(model); + + setLayout(new BorderLayout()); + Box hbox = Box.createHorizontalBox(); + JButton button = new JButton("Previous Oop"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String text = addressField.getText(); + try { + VM vm = VM.getVM(); + Address a = vm.getDebugger().parseAddress(text); + OopHandle handle = a.addOffsetToAsOopHandle(-vm.getAddressSize()); + addressField.setText(handle.toString()); + } catch (Exception ex) { + } + } + }); + hbox.add(button); + hbox.add(new JLabel("Address / C++ Expression: ")); + addressField = new HistoryComboBox(); + hbox.add(addressField); + statusLabel = new JLabel(); + hbox.add(statusLabel); + + Box hboxDown = Box.createHorizontalBox(); + hboxDown.add(Box.createGlue()); + + livenessButton = new JButton(computeLivenessText); + livenessButtonListener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (currentOop != null) { + fireComputeLiveness(); + } + return; + } + }; + showLivenessListener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + fireShowLiveness(); + } + }; + livenessButton.addActionListener(livenessButtonListener); + hboxDown.add(livenessButton); + hboxDown.add(Box.createGlue()); + + liveStatus = new JLabel(); + hboxDown.add(liveStatus); + hboxDown.add(Box.createGlue()); + + add(hbox, BorderLayout.NORTH); + add(hboxDown, BorderLayout.SOUTH); + + addressField.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String text = addressField.getText(); + try { + Address a = VM.getVM().getDebugger().parseAddress(text); + int max_searches = 1000; + int searches = 0; + int offset = 0; + Oop oop = null; + if (a != null) { + OopHandle handle = a.addOffsetToAsOopHandle(0); + while (searches < max_searches) { + searches++; + if (RobustOopDeterminator.oopLooksValid(handle)) { + try { + oop = VM.getVM().getObjectHeap().newOop(handle); + addressField.setText(handle.toString()); + break; + } catch (UnknownOopException ex) { + // ok + } catch (RuntimeException ex) { + ex.printStackTrace(); + } + } + offset -= 4; + handle = a.addOffsetToAsOopHandle(offset); + } + } + if (oop != currentOop) { + currentOop = oop; + liveStatus.setText(""); + list = null; + if (livenessButton.getText().equals(showLivenessText)) { + livenessButton.setText(computeLivenessText); + livenessButton.removeActionListener(showLivenessListener); + livenessButton.addActionListener(livenessButtonListener); + } + } + + if (oop != null) { + statusLabel.setText(""); + setRoot(new OopTreeNodeAdapter(oop, null)); + return; + } + + // Try to treat this address as a C++ object and deduce its type + Type t = VM.getVM().getTypeDataBase().guessTypeForAddress(a); + if (t != null) { + statusLabel.setText(""); + setRoot(new CTypeTreeNodeAdapter(a, t, null)); + return; + } + + statusLabel.setText(""); + } + catch (NumberFormatException ex) { + currentOop = null; + liveStatus.setText(""); + list = null; + if (livenessButton.getText().equals(showLivenessText)) { + livenessButton.setText(computeLivenessText); + livenessButton.removeActionListener(showLivenessListener); + livenessButton.addActionListener(livenessButtonListener); + } + // Try to treat this as a C++ expression + CPPExpressions.CastExpr cast = CPPExpressions.parseCast(text); + if (cast != null) { + TypeDataBase db = VM.getVM().getTypeDataBase(); + Type t = db.lookupType(cast.getType()); + if (t == null) { + statusLabel.setText(""); + } else { + try { + Address a = VM.getVM().getDebugger().parseAddress(cast.getAddress()); + statusLabel.setText(""); + setRoot(new CTypeTreeNodeAdapter(a, t, null)); + } catch (NumberFormatException ex2) { + statusLabel.setText(""); + } + } + return; + } + CPPExpressions.StaticFieldExpr stat = CPPExpressions.parseStaticField(text); + if (stat != null) { + TypeDataBase db = VM.getVM().getTypeDataBase(); + Type t = db.lookupType(stat.getContainingType()); + if (t == null) { + statusLabel.setText(""); + } else { + sun.jvm.hotspot.types.Field f = t.getField(stat.getFieldName(), true, false); + if (f == null) { + statusLabel.setText(""); + } else if (!f.isStatic()) { + statusLabel.setText(""); + } else { + Type fieldType = f.getType(); + if (fieldType.isPointerType()) { + fieldType = ((PointerType) fieldType).getTargetType(); + + // Try to get a more derived type + Type typeGuess = db.guessTypeForAddress(f.getAddress()); + if (typeGuess != null) { + fieldType = typeGuess; + } + + statusLabel.setText(""); + setRoot(new CTypeTreeNodeAdapter(f.getAddress(), + fieldType, + new NamedFieldIdentifier(text))); + } else { + statusLabel.setText(""); + setRoot(new CTypeTreeNodeAdapter(f.getStaticFieldAddress(), + f.getType(), + new NamedFieldIdentifier(text))); + } + } + } + return; + } + + statusLabel.setText(""); + } + catch (AddressException ex) { + ex.printStackTrace(); + currentOop = null; + liveStatus.setText(""); + list = null; + if (livenessButton.getText().equals(showLivenessText)) { + livenessButton.setText(computeLivenessText); + livenessButton.removeActionListener(showLivenessListener); + livenessButton.addActionListener(livenessButtonListener); + } + statusLabel.setText(""); + } + catch (Exception ex) { + ex.printStackTrace(); + currentOop = null; + liveStatus.setText(""); + list = null; + if (livenessButton.getText().equals(showLivenessText)) { + livenessButton.setText(computeLivenessText); + livenessButton.removeActionListener(showLivenessListener); + livenessButton.addActionListener(livenessButtonListener); + } + statusLabel.setText(""); + } + } + }); + + MouseListener ml = new MouseAdapter() { + public void mousePressed(MouseEvent e) { + int selRow = tree.getRowForLocation(e.getX(), e.getY()); + TreePath selPath = tree.getPathForLocation(e.getX(), e.getY()); + if(selRow != -1) { + if (e.getClickCount() == 1 && (e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0) { + Object node = tree.getLastSelectedPathComponent(); + if (node != null && node instanceof SimpleTreeNode) { + showInspector((SimpleTreeNode)node); + } + } + } + } + }; + tree.addMouseListener(ml); + + JScrollPane scrollPane = new JScrollPane(tree); + + // Let's see what happens if we let the parent deal with resizing the panel + add(scrollPane, BorderLayout.CENTER); + } + + public Inspector(final SimpleTreeNode root) { + this(); + SwingUtilities.invokeLater( new Runnable() { + public void run() { + if (root instanceof OopTreeNodeAdapter) { + final Oop oop = ((OopTreeNodeAdapter)root).getOop(); + addressField.setText(oop.getHandle().toString()); + } + setRoot(root); + } + }); + } + + private void setRoot(SimpleTreeNode root) { + model.setRoot(root); + + // tree.invalidate(); + // tree.validate(); + // repaint(); + // FIXME: invalidate? How to get to redraw? Will I have to make + // tree listeners work? + } + + private void fireComputeLiveness() { + final Runnable cutoverButtonRunnable = new Runnable() { + public void run() { + list = LivenessAnalysis.computeAllLivenessPaths(currentOop); + if (list == null) { + liveStatus.setText("Oop is Dead"); + } else { + liveStatus.setText("Oop is Alive"); + livenessButton.removeActionListener(livenessButtonListener); + livenessButton.addActionListener(showLivenessListener); + + livenessButton.setEnabled(true); + livenessButton.setText(showLivenessText); + } + } + }; + + + if (VM.getVM().getRevPtrs() != null) { + cutoverButtonRunnable.run(); + } else { + final WorkerThread worker = new WorkerThread(); + worker.invokeLater(new Runnable() { + public void run() { + try { + ReversePtrsAnalysis rev = new ReversePtrsAnalysis(); + rev.run(); + cutoverButtonRunnable.run(); + } finally { + worker.shutdown(); + } + } + }); + } + } + + private void fireShowLiveness() { + if (list == null) { + return; // dead object + } + + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + SAListener listener = (SAListener) iter.next(); + listener.showLiveness(currentOop, list); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JFrameWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JFrameWrapper.java new file mode 100644 index 00000000000..70cece15b4f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JFrameWrapper.java @@ -0,0 +1,96 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; + +public class JFrameWrapper implements FrameWrapper { + private JFrame frame; + private boolean hasWindowListener; + private ActionListener closingActionListener; + private ActionListener activatedActionListener; + + public JFrameWrapper(JFrame frame) { + this.frame = frame; + } + + public Component getComponent() { return frame; } + public Container getContentPane() { return frame.getContentPane(); } + public void setVisible(boolean visible) { frame.setVisible(visible); } + public void setSize(int x, int y) { frame.setSize(x, y); } + public void pack() { frame.pack(); } + public void show() { frame.show(); } + public void dispose() { frame.dispose(); } + public void setBackground(Color color) { frame.setBackground(color); } + public void setResizable(boolean resizable) { frame.setResizable(resizable); } + + public void setClosable(boolean closable) { + if (closable) { + frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + } else { + frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + } + } + + public void setClosingActionListener(ActionListener l) { + closingActionListener = l; + maybeInstallWindowListener(); + } + + public void setActivatedActionListener(ActionListener l) { + activatedActionListener = l; + maybeInstallWindowListener(); + } + + public void toFront() { + frame.toFront(); + frame.requestFocus(); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void maybeInstallWindowListener() { + if (!hasWindowListener) { + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + if (closingActionListener != null) { + closingActionListener.actionPerformed(null); + } + } + + public void windowActivated(WindowEvent e) { + if (activatedActionListener != null) { + activatedActionListener.actionPerformed(null); + } + } + }); + hasWindowListener = true; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JInternalFrameWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JInternalFrameWrapper.java new file mode 100644 index 00000000000..83b1ad6b384 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JInternalFrameWrapper.java @@ -0,0 +1,93 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; + +public class JInternalFrameWrapper implements FrameWrapper { + private JInternalFrame frame; + private boolean hasWindowListener; + private ActionListener closingActionListener; + private ActionListener activatedActionListener; + + public JInternalFrameWrapper(JInternalFrame frame) { + this.frame = frame; + } + + public Component getComponent() { return frame; } + public Container getContentPane() { return frame.getContentPane(); } + public void setVisible(boolean visible) { frame.setVisible(visible); } + public void setSize(int x, int y) { frame.setSize(x, y); } + public void pack() { frame.pack(); } + public void show() { frame.show(); } + public void dispose() { frame.dispose(); } + public void setBackground(Color color) { frame.setBackground(color); } + public void setResizable(boolean resizable) { frame.setResizable(resizable); } + public void setClosable(boolean closable) { frame.setClosable(closable); } + + public void setClosingActionListener(ActionListener l) { + closingActionListener = l; + maybeInstallWindowListener(); + } + + public void setActivatedActionListener(ActionListener l) { + activatedActionListener = l; + maybeInstallWindowListener(); + } + + public void toFront() { + frame.toFront(); + try { + frame.setSelected(true); + } catch (java.beans.PropertyVetoException e) { + } + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void maybeInstallWindowListener() { + if (!hasWindowListener) { + frame.addInternalFrameListener(new InternalFrameAdapter() { + public void internalFrameClosing(InternalFrameEvent e) { + if (closingActionListener != null) { + closingActionListener.actionPerformed(null); + } + } + + public void internalFrameActivated(InternalFrameEvent e) { + if (activatedActionListener != null) { + activatedActionListener.actionPerformed(null); + } + } + }); + hasWindowListener = true; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JavaStackTracePanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JavaStackTracePanel.java new file mode 100644 index 00000000000..6e7ef12e0d2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JavaStackTracePanel.java @@ -0,0 +1,88 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import javax.swing.*; +import javax.swing.event.*; + +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.ui.classbrowser.*; + +/** Provides Java stack trace of a Java Thread */ + +public class JavaStackTracePanel extends JPanel { + private JSplitPane splitPane; + private SAEditorPane stackTraceEditor; + private SAEditorPane contentEditor; + private HTMLGenerator htmlGen = new HTMLGenerator(); + + public JavaStackTracePanel() { + initUI(); + } + + private void initUI() { + setLayout(new BorderLayout()); + HyperlinkListener hyperListener = new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + setContentText(htmlGen.genHTMLForHyperlink(e.getDescription())); + } + } + }; + + stackTraceEditor = new SAEditorPane(); + stackTraceEditor.addHyperlinkListener(hyperListener); + + contentEditor = new SAEditorPane(); + contentEditor.addHyperlinkListener(hyperListener); + + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BorderLayout()); + topPanel.add(new JScrollPane(stackTraceEditor), BorderLayout.CENTER); + + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new BorderLayout()); + bottomPanel.add(new JScrollPane(contentEditor), BorderLayout.CENTER); + + splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topPanel, bottomPanel); + splitPane.setDividerLocation(0.4); + + setLayout(new BorderLayout()); + add(splitPane, BorderLayout.CENTER); + } + + public void setJavaThread(final JavaThread thread) { + setStackTraceText(htmlGen.genHTMLForJavaStackTrace(thread)); + } + + private void setStackTraceText(String text) { + stackTraceEditor.setText(text); + } + + private void setContentText(String text) { + contentEditor.setText(text); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JavaThreadsPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JavaThreadsPanel.java new file mode 100644 index 00000000000..637fc5d9100 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/JavaThreadsPanel.java @@ -0,0 +1,470 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.BorderLayout; +import java.awt.Dimension; + +import java.awt.event.*; + +import java.io.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; + +import sun.jvm.hotspot.ui.action.*; + +import com.sun.java.swing.ui.*; +import com.sun.java.swing.action.*; + +/** + * This panel contains a JTable which displays the list of Java + * threads as their native thread identifiers combined with their + * Java names. It allows selection and examination of any of the + * threads. + */ +public class JavaThreadsPanel extends SAPanel implements ActionListener { + private JavaThreadsTableModel dataModel; + private StatusBar statusBar; + private JTable threadTable; + private java.util.List cachedThreads = new ArrayList(); + + + /** Constructor assumes the threads panel is created while the VM is + suspended. Subsequent resume and suspend operations of the VM + will cause the threads panel to clear and fill itself back in, + respectively. */ + public JavaThreadsPanel() { + VM.getVM().registerVMResumedObserver(new Observer() { + public void update(Observable o, Object data) { + decache(); + } + }); + + VM.getVM().registerVMSuspendedObserver(new Observer() { + public void update(Observable o, Object data) { + cache(); + } + }); + + cache(); + + setLayout(new BorderLayout()); + + dataModel = new JavaThreadsTableModel(cachedThreads); + statusBar = new StatusBar(); + + threadTable = new JTable(dataModel, new JavaThreadsColumnModel()); + threadTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + threadTable.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + if (evt.getClickCount() == 2) { + // double clicking will display the oop inspector. + fireShowThreadOopInspector(); + } + } + }); + + add(new JavaThreadsToolBar(statusBar), BorderLayout.NORTH); + add(new ThreadPanel(threadTable), BorderLayout.CENTER); + add(statusBar, BorderLayout.SOUTH); + + registerActions(); + } + + /** + * A splitpane panel which contains the thread table and the Thread Info. + * the thread info is toggleable + */ + private class ThreadPanel extends JPanel { + + private JSplitPane splitPane; + private JTable threadTable; + private ThreadInfoPanel threadInfo; + private int dividerSize; + private int dividerLocation = -1; + private boolean actionsEnabled = false; + + public ThreadPanel(JTable table) { + setLayout(new BorderLayout()); + this.threadInfo = new ThreadInfoPanel(); + this.threadTable = table; + + splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + splitPane.setOneTouchExpandable(true); + splitPane.setTopComponent(new JScrollPane(table)); + + // Set the size of the divider to 0 but save it so it can be restored + dividerSize = splitPane.getDividerSize(); + splitPane.setDividerSize(0); + + add(splitPane, BorderLayout.CENTER); + + // Register an ItemListener on the LogViewerAction which toggles + // the apearance of the ThreadInfoPanel + ActionManager manager = HSDBActionManager.getInstance(); + StateChangeAction action = manager.getStateChangeAction(ThreadInfoAction.VALUE_COMMAND); + if (action != null) { + action.setItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + if (evt.getStateChange() == ItemEvent.SELECTED) { + showOutputPane(); + } else { + hideOutputPane(); + } + } + }); + } + + // A listener is added to listen to changes in row selection + // and changes the contents of the ThreadInfoPanel. + ListSelectionModel selModel = table.getSelectionModel(); + selModel.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent evt) { + if (evt.getValueIsAdjusting() == false) { + setActionsEnabled(true); + if (isInfoVisible()) { + showCurrentThreadInfo(); + } + } + } + }); + } + + /** + * Returns a flag to indicate if the thread info is visible + */ + private boolean isInfoVisible() { + return (splitPane.getBottomComponent() != null); + } + + private void showOutputPane() { + if (splitPane.getBottomComponent() == null) { + splitPane.setBottomComponent(threadInfo); + + if (dividerLocation == -1) { + // Calculate the divider location from the pref size. + Dimension pSize = this.getSize(); + dividerLocation = pSize.height / 2; + } + + splitPane.setDividerSize(dividerSize); + splitPane.setDividerLocation(dividerLocation); + showCurrentThreadInfo(); + } + } + + private void hideOutputPane() { + dividerLocation = splitPane.getDividerLocation(); + splitPane.remove(threadInfo); + splitPane.setDividerSize(0); + } + + private void showCurrentThreadInfo() { + int row = threadTable.getSelectedRow(); + if (row >= 0) { + threadInfo.setJavaThread(dataModel.getJavaThread(row)); + } + } + + private void setActionsEnabled(boolean enabled) { + if (actionsEnabled != enabled) { + ActionManager manager = ActionManager.getInstance(); + manager.setActionEnabled(InspectAction.VALUE_COMMAND, enabled); + manager.setActionEnabled(MemoryAction.VALUE_COMMAND, enabled); + manager.setActionEnabled(JavaStackTraceAction.VALUE_COMMAND, enabled); + actionsEnabled = enabled; + } + } + + } // end ThreadPanel + + private class JavaThreadsToolBar extends CommonToolBar { + public JavaThreadsToolBar(StatusBar status) { + super(HSDBActionManager.getInstance(), status); + } + + protected void addComponents() { + addButton(manager.getAction(InspectAction.VALUE_COMMAND)); + addButton(manager.getAction(MemoryAction.VALUE_COMMAND)); + addButton(manager.getAction(JavaStackTraceAction.VALUE_COMMAND)); + + addToggleButton(manager.getStateChangeAction(ThreadInfoAction.VALUE_COMMAND)); + addButton(manager.getAction(FindCrashesAction.VALUE_COMMAND)); + } + } + + private class JavaThreadsColumnModel extends DefaultTableColumnModel { + private String[] columnNames = { "OS Thread ID", "Java Thread Name" }; + + public JavaThreadsColumnModel() { + // Should actually get the line metrics for + int PREF_WIDTH = 80; + int MAX_WIDTH = 100; + int HUGE_WIDTH = 140; + + TableColumn column; + + // Thread ID + column = new TableColumn(0, MAX_WIDTH); + column.setHeaderValue(columnNames[0]); + column.setMaxWidth(MAX_WIDTH); + column.setResizable(false); + addColumn(column); + + // Thread name + column = new TableColumn(1, HUGE_WIDTH); + column.setHeaderValue(columnNames[1]); + column.setResizable(false); + addColumn(column); + } + } // end class JavaThreadsColumnModel + + /** + * Encapsulates the set of threads in a table model + */ + private class JavaThreadsTableModel extends AbstractTableModel { + private String[] columnNames = { "OS Thread ID", "Java Thread Name" }; + + private java.util.List elements; + + public JavaThreadsTableModel(java.util.List threads) { + this.elements = threads; + } + + public int getColumnCount() { + return columnNames.length; + } + + public int getRowCount() { + return elements.size(); + } + + public String getColumnName(int col) { + return columnNames[col]; + } + + public Object getValueAt(int row, int col) { + CachedThread thread = getRow(row); + switch (col) { + case 0: + return thread.getThreadID(); + case 1: + return thread.getThreadName(); + default: + throw new RuntimeException("Index (" + col + ", " + row + ") out of bounds"); + } + } + + /** + * Returns the selected Java Thread indexed by the row or null. + */ + public JavaThread getJavaThread(int index) { + return getRow(index).getThread(); + } + + private CachedThread getRow(int row) { + return (CachedThread)elements.get(row); + } + + private String threadIDAt(int index) { + return ((CachedThread) cachedThreads.get(index)).getThreadID(); + } + + private String threadNameAt(int index) { + try { + return ((CachedThread) cachedThreads.get(index)).getThreadName(); + } catch (AddressException e) { + return ""; + } catch (NullPointerException e) { + return ""; + } + } + } // end class JavaThreadsTableModel + + public void actionPerformed(ActionEvent evt) { + String command = evt.getActionCommand(); + + if (command.equals(InspectAction.VALUE_COMMAND)) { + fireShowThreadOopInspector(); + } else if (command.equals(MemoryAction.VALUE_COMMAND)) { + fireShowThreadStackMemory(); + } else if (command.equals(ThreadInfoAction.VALUE_COMMAND)) { + fireShowThreadInfo(); + } else if (command.equals(FindCrashesAction.VALUE_COMMAND)) { + if (fireShowThreadCrashes()) { + statusBar.setMessage("Some thread crashes were encountered"); + } else { + statusBar.setMessage("No thread crashes encountered"); + } + } else if (command.equals(JavaStackTraceAction.VALUE_COMMAND)) { + fireShowJavaStackTrace(); + } + } + + // Cached data for a thread + private class CachedThread { + private JavaThread thread; + private String threadID; + private String threadName; + private boolean computed; + + public CachedThread(JavaThread thread) { + this.thread = thread; + } + + public JavaThread getThread() { + return thread; + } + + public String getThreadID() { + if (!computed) { + compute(); + } + + return threadID; + } + + public String getThreadName() { + if (!computed) { + compute(); + } + + return threadName; + } + + private void compute() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + thread.printThreadIDOn(new PrintStream(bos)); + threadID = bos.toString(); + threadName = thread.getThreadName(); + + computed = true; + } + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + protected void registerActions() { + registerAction(InspectAction.VALUE_COMMAND); + registerAction(MemoryAction.VALUE_COMMAND); + registerAction(FindCrashesAction.VALUE_COMMAND); + registerAction(JavaStackTraceAction.VALUE_COMMAND); + + // disable Inspector, Memory and Java Stack trace action until a thread is selected + ActionManager manager = ActionManager.getInstance(); + manager.setActionEnabled(InspectAction.VALUE_COMMAND, false); + manager.setActionEnabled(MemoryAction.VALUE_COMMAND, false); + manager.setActionEnabled(JavaStackTraceAction.VALUE_COMMAND, false); + } + + private void registerAction(String actionName) { + ActionManager manager = ActionManager.getInstance(); + DelegateAction action = manager.getDelegateAction(actionName); + action.addActionListener(this); + } + + + + private void fireShowThreadOopInspector() { + int i = threadTable.getSelectedRow(); + if (i < 0) { + return; + } + + JavaThread t = dataModel.getJavaThread(i); + showThreadOopInspector(t); + } + + private void fireShowThreadStackMemory() { + int i = threadTable.getSelectedRow(); + if (i < 0) { + return; + } + showThreadStackMemory(dataModel.getJavaThread(i)); + } + + private void fireShowJavaStackTrace() { + int i = threadTable.getSelectedRow(); + if (i < 0) { + return; + } + showJavaStackTrace(dataModel.getJavaThread(i)); + } + + private void fireShowThreadInfo() { + int i = threadTable.getSelectedRow(); + if (i < 0) { + return; + } + showThreadInfo(dataModel.getJavaThread(i)); + } + + /** + * Shows stack memory for threads which have crashed (defined as + * having taken a signal above a Java frame) + * + * @return a flag which indicates if crashes were encountered. + */ + private boolean fireShowThreadCrashes() { + boolean crash = false; + for (Iterator iter = cachedThreads.iterator(); iter.hasNext(); ) { + JavaThread t = (JavaThread) ((CachedThread) iter.next()).getThread(); + sun.jvm.hotspot.runtime.Frame tmpFrame = t.getCurrentFrameGuess(); + RegisterMap tmpMap = t.newRegisterMap(false); + while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) { + if (tmpFrame.isSignalHandlerFrameDbg()) { + showThreadStackMemory(t); + crash = true; + break; + } + tmpFrame = tmpFrame.sender(tmpMap); + } + } + return crash; + } + + private void cache() { + Threads threads = VM.getVM().getThreads(); + for (JavaThread t = threads.first(); t != null; t = t.next()) { + if (t.isJavaThread()) { + cachedThreads.add(new CachedThread(t)); + } + } + } + + private void decache() { + cachedThreads.clear(); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/MemoryPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/MemoryPanel.java new file mode 100644 index 00000000000..0043efc8df1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/MemoryPanel.java @@ -0,0 +1,678 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.datatransfer.*; +import java.awt.event.*; +import java.io.IOException; +import java.math.*; +import java.util.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.table.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.ui.*; + +public class MemoryPanel extends JPanel { + private boolean is64Bit; + private Debugger debugger; + private int addressSize; + private String unmappedAddrString; + private HighPrecisionJScrollBar scrollBar; + private AbstractTableModel model; + private JTable table; + private BigInteger startVal; + // Includes any partially-visible row at the bottom + private int numVisibleRows; + // Frequently-used subexpression + private int numUsableRows; + // Multi-row (and multi-column) selection. Have to duplicate state + // from UI so this can work as we scroll off the screen. + private boolean haveAnchor; + private int rowAnchorIndex; + private int colAnchorIndex; + private boolean haveLead; + private int rowLeadIndex; + private int colLeadIndex; + + abstract class ActionWrapper extends AbstractAction { + private Action parent; + ActionWrapper() { + } + + void setParent(Action parent) { + this.parent = parent; + } + + Action getParent() { + return parent; + } + + public void actionPerformed(ActionEvent e) { + if (getParent() != null) { + getParent().actionPerformed(e); + } + } + } + + public MemoryPanel(final Debugger debugger, boolean is64Bit) { + super(); + this.debugger = debugger; + this.is64Bit = is64Bit; + if (is64Bit) { + addressSize = 8; + unmappedAddrString = "??????????????????"; + } else { + addressSize = 4; + unmappedAddrString = "??????????"; + } + setLayout(new BorderLayout()); + setupScrollBar(); + add(scrollBar, BorderLayout.EAST); + + model = new AbstractTableModel() { + public int getRowCount() { + return numVisibleRows; + } + public int getColumnCount() { + return 2; + } + public Object getValueAt(int row, int column) { + switch (column) { + case 0: return bigIntToHexString(startVal.add(new BigInteger(Integer.toString((row * addressSize))))); + case 1: { + try { + Address addr = bigIntToAddress(startVal.add(new BigInteger(Integer.toString((row * addressSize))))); + if (addr != null) { + return addressToString(addr.getAddressAt(0)); + } + return unmappedAddrString; + } catch (UnmappedAddressException e) { + return unmappedAddrString; + } + } + default: throw new RuntimeException("Column " + column + " out of bounds"); + } + } + public boolean isCellEditable(int row, int col) { + return false; + } + }; + + // View with JTable with no header + table = new JTable(model); + table.setTableHeader(null); + table.setShowGrid(false); + table.setIntercellSpacing(new Dimension(0, 0)); + table.setCellSelectionEnabled(true); + table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + table.setDragEnabled(true); + Font font = GraphicsUtilities.lookupFont("Courier"); + if (font == null) { + throw new RuntimeException("Error looking up monospace font Courier"); + } + table.setFont(font); + + // Export proper data. + // We need to keep our own notion of the selection in order to + // properly export data, since the selection can go beyond the + // visible area on the screen (and since the table's model doesn't + // back all of those slots). + // Code thanks to Shannon.Hickey@sfbay + table.setTransferHandler(new TransferHandler() { + protected Transferable createTransferable(JComponent c) { + JTable table = (JTable)c; + if (haveSelection()) { + StringBuffer buf = new StringBuffer(); + int iDir = (getRowAnchor() < getRowLead() ? 1 : -1); + int jDir = (getColAnchor() < getColLead() ? 1 : -1); + + for (int i = getRowAnchor(); i != getRowLead() + iDir; i += iDir) { + for (int j = getColAnchor(); j != getColLead() + jDir; j += jDir) { + Object val = model.getValueAt(i, j); + buf.append(val == null ? "" : val.toString()); + if (j != getColLead()) { + buf.append("\t"); + } + } + if (i != getRowLead()) { + buf.append("\n"); + } + } + + return new StringTransferable(buf.toString()); + } + return null; + } + + public int getSourceActions(JComponent c) { + return COPY; + } + + public boolean importData(JComponent c, Transferable t) { + if (canImport(c, t.getTransferDataFlavors())) { + try { + String str = (String)t.getTransferData(DataFlavor.stringFlavor); + handleImport(c, str); + return true; + } catch (UnsupportedFlavorException ufe) { + } catch (IOException ioe) { + } + } + + return false; + } + + public boolean canImport(JComponent c, DataFlavor[] flavors) { + for (int i = 0; i < flavors.length; i++) { + if (DataFlavor.stringFlavor.equals(flavors[i])) { + return true; + } + } + return false; + } + + private void handleImport(JComponent c, String str) { + // do whatever you want with the string here + try { + makeVisible(debugger.parseAddress(str)); + clearSelection(); + table.clearSelection(); + } catch (NumberFormatException e) { + System.err.println("Unable to parse address \"" + str + "\""); + } + } + }); + + // Supporting keyboard scrolling + // See src/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java, + // search for Table.AncestorInputMap + + // Actions to override: + // selectPreviousRow, selectNextRow, + // scrollUpChangeSelection, scrollDownChangeSelection, + // selectPreviousRowExtendSelection, selectNextRowExtendSelection, + // scrollDownExtendSelection, scrollUpExtendSelection (Shift-PgDn/PgUp) + + ActionMap map = table.getActionMap(); + + // Up arrow + installActionWrapper(map, "selectPreviousRow", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + clearSelection(); + if (table.getSelectedRow() == 0) { + scrollBar.scrollUpOrLeft(); + table.setRowSelectionInterval(0, 0); + } else { + super.actionPerformed(e); + } + maybeGrabSelection(); + endUpdate(); + } + }); + // Down arrow + installActionWrapper(map, "selectNextRow", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + clearSelection(); + int row = table.getSelectedRow(); + if (row >= numUsableRows) { + scrollBar.scrollDownOrRight(); + table.setRowSelectionInterval(row, row); + } else { + super.actionPerformed(e); + } + maybeGrabSelection(); + endUpdate(); + } + }); + // Page up + installActionWrapper(map, "scrollUpChangeSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + clearSelection(); + int row = table.getSelectedRow(); + scrollBar.pageUpOrLeft(); + if (row >= 0) { + table.setRowSelectionInterval(row, row); + } + maybeGrabSelection(); + endUpdate(); + } + }); + // Page down + installActionWrapper(map, "scrollDownChangeSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + clearSelection(); + int row = table.getSelectedRow(); + scrollBar.pageDownOrRight(); + if (row >= 0) { + table.setRowSelectionInterval(row, row); + } + maybeGrabSelection(); + endUpdate(); + } + }); + // Shift + Up arrow + installActionWrapper(map, "selectPreviousRowExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + // setAnchor(table.getSelectedRow()); + // setLead(table.getSelectedRow()); + } + int newLead = getRowLead() - 1; + int newAnchor = getRowAnchor(); + if (newLead < 0) { + scrollBar.scrollUpOrLeft(); + ++newLead; + ++newAnchor; + } + setSelection(newAnchor, newLead, getColAnchor(), getColLead()); + // printSelection(); + endUpdate(); + } + }); + // Shift + Left arrow + installActionWrapper(map, "selectPreviousColumnExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + } + int newLead = Math.max(0, getColLead() - 1); + setSelection(getRowAnchor(), getRowLead(), getColAnchor(), newLead); + // printSelection(); + endUpdate(); + } + }); + // Shift + Down arrow + installActionWrapper(map, "selectNextRowExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + // setAnchor(table.getSelectedRow()); + // setLead(table.getSelectedRow()); + } + int newLead = getRowLead() + 1; + int newAnchor = getRowAnchor(); + if (newLead > numUsableRows) { + scrollBar.scrollDownOrRight(); + --newLead; + --newAnchor; + } + setSelection(newAnchor, newLead, getColAnchor(), getColLead()); + // printSelection(); + endUpdate(); + } + }); + // Shift + Right arrow + installActionWrapper(map, "selectNextColumnExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + } + int newLead = Math.min(model.getColumnCount() - 1, getColLead() + 1); + setSelection(getRowAnchor(), getRowLead(), getColAnchor(), newLead); + // printSelection(); + endUpdate(); + } + }); + // Shift + Page up + installActionWrapper(map, "scrollUpExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + // setAnchor(table.getSelectedRow()); + // setLead(table.getSelectedRow()); + } + int newLead = getRowLead() - numUsableRows; + int newAnchor = getRowAnchor(); + if (newLead < 0) { + scrollBar.pageUpOrLeft(); + newLead += numUsableRows; + newAnchor += numUsableRows; + } + setSelection(newAnchor, newLead, getColAnchor(), getColLead()); + // printSelection(); + endUpdate(); + } + }); + // Shift + Page down + installActionWrapper(map, "scrollDownExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + // setAnchor(table.getSelectedRow()); + // setLead(table.getSelectedRow()); + } + int newLead = getRowLead() + numUsableRows; + int newAnchor = getRowAnchor(); + if (newLead > numUsableRows) { + scrollBar.pageDownOrRight(); + newLead -= numUsableRows; + newAnchor -= numUsableRows; + } + setSelection(newAnchor, newLead, getColAnchor(), getColLead()); + // printSelection(); + endUpdate(); + } + }); + + // Clear our notion of selection upon mouse press + table.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if (shouldIgnore(e)) { + return; + } + // Make shift-clicking work properly + if (e.isShiftDown()) { + maybeGrabSelection(); + return; + } + // System.err.println(" Clearing selection on mouse press"); + clearSelection(); + } + }); + + // Watch for mouse going out of bounds + table.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseDragged(MouseEvent e) { + if (shouldIgnore(e)) { + // System.err.println(" (Ignoring consumed mouse event)"); + return; + } + + // Look for drag events outside table and scroll if necessary + Point p = e.getPoint(); + if (table.rowAtPoint(p) == -1) { + // See whether we are above or below the table + Rectangle rect = new Rectangle(); + getBounds(rect); + beginUpdate(); + if (p.y < rect.y) { + // System.err.println(" Scrolling up due to mouse event"); + // Scroll up + scrollBar.scrollUpOrLeft(); + setSelection(getRowAnchor(), 0, getColAnchor(), getColLead()); + } else { + // System.err.println(" Scrolling down due to mouse event"); + // Scroll down + scrollBar.scrollDownOrRight(); + setSelection(getRowAnchor(), numUsableRows, getColAnchor(), getColLead()); + } + // printSelection(); + endUpdate(); + } else { + maybeGrabSelection(); + } + } + }); + + + add(table, BorderLayout.CENTER); + + // Make sure we recompute number of visible rows + addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + recomputeNumVisibleRows(); + constrain(); + } + }); + addHierarchyListener(new HierarchyListener() { + public void hierarchyChanged(HierarchyEvent e) { + recomputeNumVisibleRows(); + constrain(); + } + }); + updateFromScrollBar(); + } + + /** Makes the given address visible somewhere in the window */ + public void makeVisible(Address addr) { + BigInteger bi = addressToBigInt(addr); + scrollBar.setValueHP(bi); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void setupScrollBar() { + if (is64Bit) { + // 64-bit mode + scrollBar = + new HighPrecisionJScrollBar( + Scrollbar.VERTICAL, + new BigInteger(1, new byte[] { + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC})); + scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08})); + scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x40})); + } else { + // 32-bit mode + scrollBar= + new HighPrecisionJScrollBar( + Scrollbar.VERTICAL, + new BigInteger(1, new byte[] { + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC})); + scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04})); + scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x20})); + } + scrollBar.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + updateFromScrollBar(); + } + }); + } + + private void updateFromScrollBar() { + beginUpdate(); + BigInteger oldStartVal = startVal; + startVal = scrollBar.getValueHP(); + constrain(); + model.fireTableDataChanged(); + if (oldStartVal != null) { + modifySelection(oldStartVal.subtract(startVal).intValue() / addressSize); + } + endUpdate(); + } + + private void constrain() { + BigInteger offset = new BigInteger(Integer.toString(addressSize * (numUsableRows))); + BigInteger endVal = startVal.add(offset); + if (endVal.compareTo(scrollBar.getMaximumHP()) > 0) { + startVal = scrollBar.getMaximumHP().subtract(offset); + endVal = scrollBar.getMaximumHP(); + scrollBar.setValueHP(startVal); + model.fireTableDataChanged(); + } + } + + private void recomputeNumVisibleRows() { + Rectangle rect = new Rectangle(); + getBounds(rect); + int h = table.getRowHeight(); + numVisibleRows = (rect.height + (h - 1)) / h; + numUsableRows = numVisibleRows - 2; + scrollBar.setBlockIncrementHP(new BigInteger(Integer.toString(addressSize * (numUsableRows)))); + model.fireTableDataChanged(); + // FIXME: refresh selection + } + + private String bigIntToHexString(BigInteger bi) { + StringBuffer buf = new StringBuffer(); + buf.append("0x"); + String val = bi.toString(16); + for (int i = 0; i < ((2 * addressSize) - val.length()); i++) { + buf.append('0'); + } + buf.append(val); + return buf.toString(); + } + + private Address bigIntToAddress(BigInteger i) { + String s = bigIntToHexString(i); + return debugger.parseAddress(s); + } + + private BigInteger addressToBigInt(Address a) { + String s = addressToString(a); + if (!s.startsWith("0x")) { + throw new NumberFormatException(s); + } + return new BigInteger(s.substring(2), 16); + } + + private String addressToString(Address a) { + if (a == null) { + if (is64Bit) { + return "0x0000000000000000"; + } else { + return "0x00000000"; + } + } + return a.toString(); + } + + private static void installActionWrapper(ActionMap map, + String actionName, + ActionWrapper wrapper) { + wrapper.setParent(map.get(actionName)); + map.put(actionName, wrapper); + } + + private boolean shouldIgnore(MouseEvent e) { + return e.isConsumed() || (!(SwingUtilities.isLeftMouseButton(e) && table.isEnabled())); + } + + private void clearSelection() { + haveAnchor = false; + haveLead = false; + } + + private int updateLevel; + private boolean updating() { return updateLevel > 0; } + private void beginUpdate() { ++updateLevel; } + private void endUpdate() { --updateLevel; } + + private boolean haveAnchor() { return haveAnchor; } + private boolean haveLead() { return haveLead; } + private boolean haveSelection() { return haveAnchor() && haveLead(); } + private int getRowAnchor() { return rowAnchorIndex; } + private int getColAnchor() { return colAnchorIndex; } + private int getRowLead() { return rowLeadIndex; } + private int getColLead() { return colLeadIndex; } + + private void setAnchorFromTable() { + setAnchor(table.getSelectionModel().getAnchorSelectionIndex(), + table.getColumnModel().getSelectionModel().getAnchorSelectionIndex()); + } + private void setLeadFromTable() { + setLead(table.getSelectionModel().getAnchorSelectionIndex(), + table.getColumnModel().getSelectionModel().getAnchorSelectionIndex()); + } + private void setAnchor(int row, int col) { + rowAnchorIndex = row; + colAnchorIndex = col; + haveAnchor = true; + } + private void setLead(int row, int col) { + rowLeadIndex = row; + colLeadIndex = col; + haveLead = true; + } + private int clamp(int val, int min, int max) { + return Math.max(Math.min(val, max), min); + } + private void maybeGrabSelection() { + if (table.getSelectedRow() != -1) { + // Grab selection + ListSelectionModel rowSel = table.getSelectionModel(); + ListSelectionModel colSel = table.getColumnModel().getSelectionModel(); + if (!haveAnchor()) { + // System.err.println("Updating from table's selection"); + setSelection(rowSel.getAnchorSelectionIndex(), rowSel.getLeadSelectionIndex(), + colSel.getAnchorSelectionIndex(), colSel.getLeadSelectionIndex()); + } else { + // System.err.println("Updating lead from table's selection"); + setSelection(getRowAnchor(), rowSel.getLeadSelectionIndex(), + getColAnchor(), colSel.getLeadSelectionIndex()); + } + // printSelection(); + } + } + private void setSelection(int rowAnchor, int rowLead, int colAnchor, int colLead) { + setAnchor(rowAnchor, colAnchor); + setLead(rowLead, colLead); + table.setRowSelectionInterval(clamp(rowAnchor, 0, numUsableRows), + clamp(rowLead, 0, numUsableRows)); + table.setColumnSelectionInterval(colAnchor, colLead); + } + private void modifySelection(int amount) { + if (haveSelection()) { + setSelection(getRowAnchor() + amount, getRowLead() + amount, + getColAnchor(), getColLead()); + } + } + private void printSelection() { + System.err.println("Selection updated to (" + + model.getValueAt(getRowAnchor(), getColAnchor()) + + ", " + + model.getValueAt(getRowLead(), getColLead()) + ") [(" + + getRowAnchor() + ", " + getColAnchor() + "), (" + + getRowLead() + ", " + getColLead() + ")]"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/MemoryViewer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/MemoryViewer.java new file mode 100644 index 00000000000..cdb96a7cc75 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/MemoryViewer.java @@ -0,0 +1,57 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import sun.jvm.hotspot.debugger.*; + +/** Wraps a MemoryPanel with a field allowing the user to type in an + address. */ + +public class MemoryViewer extends JPanel { + public MemoryViewer(final Debugger debugger, boolean is64Bit) { + super(); + final MemoryPanel memory = new MemoryPanel(debugger, is64Bit); + memory.setBorder(GraphicsUtilities.newBorder(5)); + JPanel addressPanel = new JPanel(); + addressPanel.setLayout(new BoxLayout(addressPanel, BoxLayout.X_AXIS)); + addressPanel.add(new JLabel("Address: ")); + final JTextField addressField = new JTextField(20); + addressPanel.add(addressField); + addressField.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + memory.makeVisible(debugger.parseAddress(addressField.getText())); + } catch (NumberFormatException ex) { + } + } + }); + setLayout(new BorderLayout()); + add(addressPanel, BorderLayout.NORTH); + add(memory, BorderLayout.CENTER); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/MonitorCacheDumpPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/MonitorCacheDumpPanel.java new file mode 100644 index 00000000000..fb3b28fe455 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/MonitorCacheDumpPanel.java @@ -0,0 +1,114 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.io.*; +import javax.swing.*; +import java.util.*; + +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; + +/** Provides information about monitor cache. */ + +public class MonitorCacheDumpPanel extends JPanel { + public MonitorCacheDumpPanel() { + super(); + + setLayout(new BorderLayout()); + + // Simple at first + JScrollPane scroller = new JScrollPane(); + JTextArea textArea = new JTextArea(); + textArea = new JTextArea(); + textArea.setEditable(false); + textArea.setLineWrap(true); + textArea.setWrapStyleWord(true); + scroller.getViewport().add(textArea); + add(scroller, BorderLayout.CENTER); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PrintStream tty = new PrintStream(bos); + tty.println("Monitor Cache Dump (not including JVMTI raw monitors):"); + tty.println(); + dumpOn(tty); + + textArea.setText(bos.toString()); + } + + private static void dumpMonitor(PrintStream tty, ObjectMonitor mon, boolean raw) { + tty.print("ObjectMonitor@" + mon.getAddress()); + if (raw) tty.print("(Raw Monitor)"); + tty.println(); + tty.println(" _header: 0x" + Long.toHexString(mon.header().value())); + OopHandle obj = mon.object(); + Oop oop = heap.newOop(obj); + tty.println(" _object: " + obj + ", a " + oop.getKlass().getName().asString()); + Address owner = mon.owner(); + tty.println(" _owner: " + owner); + if (!raw && owner != null) { + JavaThread thread = threads.owningThreadFromMonitor(mon); + if (thread != null) { + tty.println(" owning thread: " + thread.getThreadName()); + if (!thread.getAddress().equals(owner)) { + if (!thread.isLockOwned(owner)) { + tty.println(" WARNING! _owner doesn't fall in " + thread + + "'s stack space"); + } + } + } + } + tty.println(" _count: " + mon.count()); + tty.println(" _waiters: " + mon.waiters()); + tty.println(" _recursions: " + mon.recursions()); + } + + private void dumpOn(PrintStream tty) { + Iterator i = ObjectSynchronizer.objectMonitorIterator(); + if (i == null) { + tty.println("This version of HotSpot VM doesn't support monitor cache dump."); + tty.println("You need 1.4.0_04, 1.4.1_01 or later versions"); + return; + } + ObjectMonitor mon; + while (i.hasNext()) { + mon = (ObjectMonitor)i.next(); + if (mon.count() != 0 || mon.waiters() != 0 || mon.owner() != null) { + OopHandle object = mon.object(); + if (object == null) { + dumpMonitor(tty, mon, true); + } else { + dumpMonitor(tty, mon, false); + } + } + } + } + + private static Threads threads = VM.getVM().getThreads(); + private static ObjectHeap heap = VM.getVM().getObjectHeap(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ObjectHistogramPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ObjectHistogramPanel.java new file mode 100644 index 00000000000..1030a78253e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ObjectHistogramPanel.java @@ -0,0 +1,345 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.util.*; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; +import javax.swing.table.*; + +import sun.jvm.hotspot.oops.ObjectHistogram; +import sun.jvm.hotspot.oops.ObjectHistogramElement; +import sun.jvm.hotspot.oops.Klass; + +import sun.jvm.hotspot.ui.table.LongCellRenderer; +import sun.jvm.hotspot.ui.table.SortableTableModel; +import sun.jvm.hotspot.ui.table.SortHeaderCellRenderer; +import sun.jvm.hotspot.ui.table.SortHeaderMouseAdapter; +import sun.jvm.hotspot.ui.table.TableModelComparator; + +import sun.jvm.hotspot.ui.action.*; + +import com.sun.java.swing.ui.StatusBar; +import com.sun.java.swing.ui.CommonToolBar; + +import com.sun.java.swing.action.ActionManager; +import com.sun.java.swing.action.DelegateAction; + +/** + * Displays the results of an ObjectHistogram run in a JTable, with a + * button to display all objects of that type + */ +public class ObjectHistogramPanel extends JPanel implements ActionListener { + private ObjectHistogramTableModel dataModel; + private ObjectHistogramToolBar toolbar; + private StatusBar statusBar; + private JTable table; + private java.util.List listeners; + + public ObjectHistogramPanel(ObjectHistogram histo) { + dataModel = new ObjectHistogramTableModel(histo); + statusBar = new StatusBar(); + + table = new JTable(dataModel, new ObjectHistogramColummModel()); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + if (evt.getClickCount() == 2) { + fireShowObjectsOfType(); + } + } + }); + + JTableHeader header = table.getTableHeader(); + header.setDefaultRenderer(new SortHeaderCellRenderer(header, dataModel)); + header.addMouseListener(new SortHeaderMouseAdapter(table, dataModel)); + + setLayout(new BorderLayout()); + + toolbar = new ObjectHistogramToolBar(statusBar); + add(toolbar, BorderLayout.NORTH); + add(new JScrollPane(table), BorderLayout.CENTER); + add(statusBar, BorderLayout.SOUTH); + + registerActions(); + } + + private class ObjectHistogramToolBar extends CommonToolBar { + + private JTextField searchTF; + + public ObjectHistogramToolBar(StatusBar status) { + super(HSDBActionManager.getInstance(), status); + } + + protected void addComponents() { + searchTF = new JTextField(); + searchTF.setToolTipText("Find in Class Description"); + + // Pressing Enter on the text field will search + InputMap im = searchTF.getInputMap(); + im.put(KeyStroke.getKeyStroke("ENTER"), "enterPressed"); + ActionMap am = searchTF.getActionMap(); + am.put("enterPressed", manager.getAction(FindAction.VALUE_COMMAND)); + + add(searchTF); + addButton(manager.getAction(FindAction.VALUE_COMMAND)); + addButton(manager.getAction(ShowAction.VALUE_COMMAND)); + } + + public String getFindText() { + return searchTF.getText(); + } + } + + private class ObjectHistogramColummModel extends DefaultTableColumnModel { + private final String LABEL_SIZE = "Size"; + private final String LABEL_COUNT = "Count"; + private final String LABEL_DESC = "Class Description"; + + + public ObjectHistogramColummModel() { + // Should actually get the line metrics for + int PREF_WIDTH = 80; + int MAX_WIDTH = 100; + int HUGE_WIDTH = 140; + + LongCellRenderer lcRender = new LongCellRenderer(); + + TableColumn column; + + // Size + column = new TableColumn(0, PREF_WIDTH); + column.setHeaderValue(LABEL_SIZE); + column.setMaxWidth(MAX_WIDTH); + column.setResizable(false); + column.setCellRenderer(lcRender); + addColumn(column); + + // Count + column = new TableColumn(1, PREF_WIDTH); + column.setHeaderValue(LABEL_COUNT); + column.setMaxWidth(MAX_WIDTH); + column.setResizable(false); + column.setCellRenderer(lcRender); + addColumn(column); + + // Description + column = new TableColumn(2, HUGE_WIDTH); + column.setHeaderValue(LABEL_DESC); + addColumn(column); + } + } + + + /** + * A table model which encapsulates the ObjectHistogram + */ + private class ObjectHistogramTableModel extends SortableTableModel { + private String[] columnNames = { "Size", "Count", "Class Description" }; + private Class[] columnClasses = { Long.class, Long.class, String.class }; + + public ObjectHistogramTableModel(ObjectHistogram histo) { + // Set the rows + elements = histo.getElements(); + setComparator(new ObjectHistogramComparator(this)); + + } + + public int getColumnCount() { + return columnNames.length; + } + + public int getRowCount() { + return elements.size(); + } + + public String getColumnName(int col) { + return columnNames[col]; + } + + public Class getColumnClass(int col) { + return columnClasses[col]; + } + + public Object getValueAt(int row, int col) { + return getValueForColumn(getElement(row), col); + } + + public Object getValueForColumn(Object obj, int col) { + ObjectHistogramElement el = (ObjectHistogramElement)obj; + switch (col) { + case 0: + return new Long(el.getSize()); + case 1: + return new Long(el.getCount()); + case 2: + return el.getDescription(); + default: + throw new RuntimeException("Index (" + col + ") out of bounds"); + } + } + + public ObjectHistogramElement getElement(int index) { + return (ObjectHistogramElement) elements.get(index); + } + + private class ObjectHistogramComparator extends TableModelComparator { + + public ObjectHistogramComparator(ObjectHistogramTableModel model) { + super(model); + } + + /** + * Returns the value for the comparing object for the + * column. + * + * @param obj Object that was passed for Comparator + * @param column the column to retrieve + */ + public Object getValueForColumn(Object obj, int column) { + ObjectHistogramTableModel omodel = (ObjectHistogramTableModel)model; + return omodel.getValueForColumn(obj, column); + } + } + + } + + + // + // ActionListener implementation and actions support + // + + public void actionPerformed(ActionEvent evt) { + String command = evt.getActionCommand(); + + if (command.equals(ShowAction.VALUE_COMMAND)) { + fireShowObjectsOfType(); + } else if (command.equals(FindAction.VALUE_COMMAND)) { + findObject(); + } + + } + + protected void registerActions() { + registerAction(FindAction.VALUE_COMMAND); + registerAction(ShowAction.VALUE_COMMAND); + } + + private void registerAction(String actionName) { + ActionManager manager = ActionManager.getInstance(); + DelegateAction action = manager.getDelegateAction(actionName); + action.addActionListener(this); + } + + public interface Listener { + public void showObjectsOfType(Klass type); + } + + public void addPanelListener(Listener listener) { + if (listeners == null) { + listeners = new ArrayList(); + } + listeners.add(listener); + } + + public void removePanelListener(Listener listener) { + if (listeners != null) { + listeners.remove(listener); + } + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + /** + * Find and select the row that contains the text in the find field starting + * from the current selected row. + * + * Uses a linear search from the current row. Could be optimized withing the + * model and internal representation. + */ + private void findObject() { + String findText = toolbar.getFindText(); + + if (findText == null || findText.equals("")) { + return; + } + + TableModel model = table.getModel(); + + int row = table.getSelectedRow(); + if (row == model.getRowCount()) { + row = 0; + } else { + // Start at the row after the selected row. + row++; + } + + String value; + for (int i = row; i < model.getRowCount(); i++) { + value = (String)model.getValueAt(i, 2); + if (value != null && value.startsWith(findText)) { + table.setRowSelectionInterval(i, i); + Rectangle cellBounds = table.getCellRect(i, 0, true); + table.scrollRectToVisible(cellBounds); + return; + } + } + + // Wrap the table to search in the top rows. + for (int i = 0; i < row; i++) { + value = (String)model.getValueAt(i, 2); + if (value != null && value.startsWith(findText)) { + table.setRowSelectionInterval(i, i); + Rectangle cellBounds = table.getCellRect(i, 0, true); + table.scrollRectToVisible(cellBounds); + return; + } + } + } + + private void fireShowObjectsOfType() { + int i = table.getSelectedRow(); + if (i < 0) { + return; + } + + ObjectHistogramElement el = dataModel.getElement(i); + + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + Listener listener = (Listener) iter.next(); + listener.showObjectsOfType(el.getKlass()); + } + } + + public static void main(String[] args) { + + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ObjectListPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ObjectListPanel.java new file mode 100644 index 00000000000..6cc4bc6b9ba --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ObjectListPanel.java @@ -0,0 +1,343 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.io.*; +import java.util.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.table.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.ui.table.*; +import sun.jvm.hotspot.ui.tree.*; + +/** Lists objects along with their types */ + +public class ObjectListPanel extends SAPanel { + private ObjectListTableModel dataModel; + private JTable table; + private java.util.List elements; + private HeapProgressThunk thunk; + private boolean checkedForArrays; + private boolean hasArrays; + private int numColumns; + // For changing the text of the "Compute Liveness" button + private JButton livenessButton; + private ActionListener livenessButtonListener; + private static final String showLivenessText = "Show Liveness"; + + /** Takes a List in constructor, and an optional + HeapProgressThunk used if computing liveness */ + public ObjectListPanel(java.util.List els, + HeapProgressThunk thunk) { + super(); + + elements = els; + this.thunk = thunk; + computeNumColumns(); + + setLayout(new BorderLayout()); + + dataModel = new ObjectListTableModel(); + + table = new JTable(dataModel); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + JTableHeader header = table.getTableHeader(); + header.setDefaultRenderer(new SortHeaderCellRenderer(header, dataModel)); + header.addMouseListener(new SortHeaderMouseAdapter(table, dataModel)); + + JScrollPane scrollPane = new JScrollPane(table); + add(scrollPane, BorderLayout.CENTER); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + Box box = Box.createHorizontalBox(); + box.add(Box.createGlue()); + JButton button = new JButton("Inspect"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + fireShowInspector(); + } + }); + box.add(button); + + box.add(Box.createHorizontalStrut(20)); + + // Liveness button + button = new JButton(); + livenessButton = button; + if (VM.getVM().getRevPtrs() == null) { + button.setText("Compute Liveness"); + livenessButtonListener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + fireComputeLiveness(); + } + }; + } else { + button.setText("Show Liveness Path"); + livenessButtonListener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + fireShowLiveness(); + } + }; + } + button.addActionListener(livenessButtonListener); + box.add(button); + box.add(Box.createGlue()); + panel.add(box); + add(panel, BorderLayout.SOUTH); + } + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private static class AddressWrapper implements Comparable { + private Address address; + + private AddressWrapper(Address address) { + this.address = address; + } + + public String toString() { + return address.toString(); + } + + public int compareTo(Object o) { + AddressWrapper wrapper = (AddressWrapper) o; + Address addr = wrapper.address; + if (AddressOps.lessThan(address, addr)) return -1; + if (AddressOps.greaterThan(address, addr)) return 1; + return 0; + } + } + + private class ObjectListTableModel extends SortableTableModel { + public ObjectListTableModel() { + // Set the rows + this.elements = ObjectListPanel.this.elements; + setComparator(new ObjectListComparator(this)); + } + + public int getColumnCount() { return numColumns; } + public int getRowCount() { return elements.size(); } + public String getColumnName(int col) { + switch (col) { + case 0: + return "Address"; + case 1: + return "Oop"; + case 2: + if (hasArrays) { + return "Length"; + } else { + return "Class Description"; + } + case 3: + if (hasArrays) { + return "Class Description"; + } else if (VM.getVM().getRevPtrs() != null) { + return "Liveness"; + } + case 4: + if (hasArrays && (VM.getVM().getRevPtrs() != null)) { + return "Liveness"; + } + } + throw new RuntimeException("Index " + col + " out of bounds"); + } + + public Object getValueAt(int row, int col) { + Oop oop = (Oop) elements.get(row); + return getValueForColumn(oop, col); + } + + public Object getValueForColumn(Oop oop, int col) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + switch (col) { + case 0: + return new AddressWrapper(oop.getHandle()); + case 1: + oop.printValueOn(new PrintStream(bos)); + break; + case 2: + if (hasArrays) { + if (oop instanceof Array) { + return new Long(((Array) oop).getLength()); + } + return null; + } else { + oop.getKlass().printValueOn(new PrintStream(bos)); + break; + } + case 3: + if (hasArrays) { + oop.getKlass().printValueOn(new PrintStream(bos)); + break; + } else { + if (VM.getVM().getRevPtrs() != null) { + if (VM.getVM().getRevPtrs().get(oop) != null) { + return "Alive"; + } else { + return "Dead"; + } + } + } + case 4: + if (hasArrays) { + if (VM.getVM().getRevPtrs() != null) { + if (VM.getVM().getRevPtrs().get(oop) != null) { + return "Alive"; + } else { + return "Dead"; + } + } + } + default: + throw new RuntimeException("Column " + col + " out of bounds"); + } + + return bos.toString(); + } + + private class ObjectListComparator extends TableModelComparator { + public ObjectListComparator(ObjectListTableModel model) { + super(model); + } + + /** + * Returns the value for the comparing object for the + * column. + * + * @param obj Object that was passed for Comparator + * @param column the column to retrieve + */ + public Object getValueForColumn(Object obj, int column) { + ObjectListTableModel omodel = (ObjectListTableModel)model; + return omodel.getValueForColumn((Oop) obj, column); + } + } + } + + private void fireShowInspector() { + int i = table.getSelectedRow(); + if (i < 0) { + return; + } + + Oop oop = (Oop) elements.get(i); + + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + SAListener listener = (SAListener) iter.next(); + listener.showInspector(new OopTreeNodeAdapter(oop, null)); + } + } + + private void fireComputeLiveness() { + final Runnable cutoverButtonRunnable = new Runnable() { + public void run() { + livenessButton.removeActionListener(livenessButtonListener); + livenessButtonListener = null; + livenessButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + fireShowLiveness(); + } + }); + computeNumColumns(); + livenessButton.setEnabled(true); + livenessButton.setText(showLivenessText); + dataModel.fireTableStructureChanged(); + } + }; + + + if (VM.getVM().getRevPtrs() != null) { + cutoverButtonRunnable.run(); + } else { + final WorkerThread worker = new WorkerThread(); + worker.invokeLater(new Runnable() { + public void run() { + try { + ReversePtrsAnalysis rev = new ReversePtrsAnalysis(); + if (thunk != null) { + rev.setHeapProgressThunk(thunk); + } + rev.run(); + cutoverButtonRunnable.run(); + } finally { + worker.shutdown(); + } + } + }); + } + } + + private void fireShowLiveness() { + if (VM.getVM().getRevPtrs() == null) { + return; + } + + int i = table.getSelectedRow(); + if (i < 0) { + return; + } + + Oop oop = (Oop) elements.get(i); + LivenessPathList list = LivenessAnalysis.computeAllLivenessPaths(oop); + if (list == null) { + return; // dead object + } + + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + SAListener listener = (SAListener) iter.next(); + listener.showLiveness(oop, list); + } + } + + private void checkForArrays() { + if (checkedForArrays) return; + checkedForArrays = true; + for (Iterator iter = elements.iterator(); iter.hasNext(); ) { + if (iter.next() instanceof Array) { + hasArrays = true; + return; + } + } + } + + private void computeNumColumns() { + checkForArrays(); + numColumns = 3; + if (hasArrays) ++numColumns; + if (VM.getVM().getRevPtrs() != null) ++numColumns; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ProcessListPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ProcessListPanel.java new file mode 100644 index 00000000000..cfcf288297d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ProcessListPanel.java @@ -0,0 +1,232 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.table.*; + +import sun.jvm.hotspot.debugger.*; + +public class ProcessListPanel extends JPanel { + private Debugger dbg; + private AbstractTableModel dataModel; + private java.util.List els; + private boolean sortByName = true; + private boolean sortReversed = false; + private javax.swing.Timer timer; + private JTable table; + + /** Takes a Debugger in constructor. Updates the list once during + construction; list can be updated automatically by "starting" + the panel. */ + public ProcessListPanel(Debugger dbg) { + super(); + + this.dbg = dbg; + + update(); + + dataModel = new AbstractTableModel() { + public int getColumnCount() { return 2; } + public int getRowCount() { return els.size(); } + public String getColumnName(int col) { + switch (col) { + case 0: + return "Process Name"; + case 1: + return "Process ID"; + default: + throw new RuntimeException("Index " + col + " out of bounds"); + } + } + + public Object getValueAt(int row, int col) { + ProcessInfo info = (ProcessInfo) els.get(row); + + switch (col) { + case 0: + return info.getName(); + case 1: + return new Integer(info.getPid()); + default: + throw new RuntimeException("Index (" + col + ", " + row + ") out of bounds"); + } + } + }; + + // Create user interface + setLayout(new BorderLayout()); + table = new JTable(dataModel); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + JTableHeader header = table.getTableHeader(); + header.setReorderingAllowed(false); + table.setRowSelectionAllowed(true); + table.setColumnSelectionAllowed(false); + // Provide sorting in similar fashion to Task Manager + header.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + int viewColumn = table.getColumnModel().getColumnIndexAtX(e.getX()); + int column = table.convertColumnIndexToModel(viewColumn); + if (column != -1) { + boolean newSortByName = (column == 0); + if (newSortByName == sortByName) { + // Switch sense of "reversed" instead + sortReversed = !sortReversed; + } else { + sortByName = newSortByName; + sortReversed = false; + } + + // Keep the current selection if possible + int i = table.getSelectedRow(); + int pid = getPid(els, i); + sort(els); + i = findPid(els, pid); + dataModel.fireTableDataChanged(); + if ((i >= 0) || (els.size() > 0)) { + if (i >= 0) { + table.setRowSelectionInterval(i, i); + } else { + table.setRowSelectionInterval(0, 0); + } + } + } + } + }); + + JScrollPane scrollPane = new JScrollPane(table); + add(scrollPane, BorderLayout.CENTER); + + if (els.size() > 0) { + table.setRowSelectionInterval(0, 0); + } + } + + /** Set update interval for automatic updating of the process list */ + + public void setAutoUpdateInterval(int millis) { + getTimer().setDelay(millis); + } + + /** Start auto updating of the panel */ + public void start() { + getTimer().start(); + } + + /** Stop auto updating of the panel */ + public void stop() { + getTimer().stop(); + } + + /** Call this to update the panel's notion of the process list */ + public synchronized void update() { + if (!dbg.hasProcessList()) { + throw new RuntimeException("ProcessListPanel requires that debugger supports getProcessList()"); + } + java.util.List newEls = dbg.getProcessList(); + sort(newEls); + if (table != null) { + // Keep the current selection if possible + int i = table.getSelectedRow(); + int pid = getPid(els, i); + i = findPid(newEls, pid); + els = newEls; + dataModel.fireTableDataChanged(); + if ((i >= 0) || (els.size() > 0)) { + if (i >= 0) { + table.setRowSelectionInterval(i, i); + } else { + table.setRowSelectionInterval(0, 0); + } + } + } else { + els = newEls; + } + } + + /** Call this to get the selected ProcessInfo, or null if none selected */ + public synchronized ProcessInfo getSelectedProcess() { + int i = table.getSelectedRow(); + if (i < 0) { + return null; + } + return (ProcessInfo) els.get(i); + } + + private synchronized void sort(java.util.List els) { + Comparator c; + if (sortByName) { + c = new Comparator() { + public int compare(Object o1, Object o2) { + int scale = (sortReversed ? -1 : 1); + return scale * ((ProcessInfo) o1).getName().compareToIgnoreCase(((ProcessInfo) o2).getName()); + } + }; + } else { + c = new Comparator() { + public int compare(Object o1, Object o2) { + int scale = (sortReversed ? -1 : 1); + int pid1 = ((ProcessInfo) o1).getPid(); + int pid2 = ((ProcessInfo) o2).getPid(); + int ret; + if (pid1 < pid2) ret = -1; + else if (pid1 == pid2) ret = 0; + else ret = 1; + return ret * scale; + } + }; + } + Collections.sort(els, c); + } + + private javax.swing.Timer getTimer() { + if (timer == null) { + timer = new javax.swing.Timer(1000, new ActionListener() { + public void actionPerformed(ActionEvent e) { + update(); + } + }); + } + return timer; + } + + private synchronized int getPid(java.util.List els, int index) { + return ((ProcessInfo) els.get(index)).getPid(); + } + + private synchronized int findPid(java.util.List els, int pid) { + for (int i = 0; i < els.size(); i++) { + ProcessInfo info = (ProcessInfo) els.get(i); + if (info.getPid() == pid) { + return i; + } + } + return -1; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ProgressBarPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ProgressBarPanel.java new file mode 100644 index 00000000000..8a7142814a0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ProgressBarPanel.java @@ -0,0 +1,87 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import javax.swing.*; + +/** A panel containing a progress bar and a label */ + +public class ProgressBarPanel extends JPanel { + private JLabel text; + private JProgressBar bar; + private static final int MAX = 10000; + + public static final int VERTICAL = 0; + public static final int HORIZONTAL = 1; + + public ProgressBarPanel() { + this(VERTICAL); + } + + /** LayoutType is either VERTICAL or HORIZONTAL */ + public ProgressBarPanel(int layoutType) { + super(); + if (layoutType == VERTICAL) { + setLayout(new BorderLayout()); + text = new JLabel(); + add(text, BorderLayout.NORTH); + bar = new JProgressBar(JProgressBar.HORIZONTAL, 0, MAX); + JPanel panel = new JPanel(); + panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + panel.add(bar); + add(panel, BorderLayout.CENTER); + } else { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + text = new JLabel(); + add(text); + bar = new JProgressBar(JProgressBar.HORIZONTAL, 0, MAX); + add(bar); + } + } + + public ProgressBarPanel(String text) { + this(); + setText(text); + } + + public ProgressBarPanel(int layoutType, String text) { + this(layoutType); + setText(text); + } + + public void setText(String text) { + this.text.setText(text); + } + + public void setValue(double val) { + int realVal = (int) (val * MAX); + bar.setValue(realVal); + } + + public void setIndeterminate(boolean value) { + bar.setIndeterminate(value); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SAEditorPane.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SAEditorPane.java new file mode 100644 index 00000000000..89b3b19c2d1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SAEditorPane.java @@ -0,0 +1,93 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.io.*; +import java.awt.event.*; +import java.awt.im.InputContext; +import java.awt.datatransfer.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +/** A simple subclass of JEditorPane for displaying uneditable html + */ + +public class SAEditorPane extends JEditorPane { + + public SAEditorPane() { + setEditable(false); + setContentType("text/html"); + } + + /** + Override getSelectedText so that
elements produce \n when + text is copied out of the window. + */ + + public String getSelectedText() { + StringBuffer result = new StringBuffer(); + Document doc = getDocument(); + + int start = getSelectionStart(); + int end = getSelectionEnd(); + + try { + // Create an iterator using the root element + ElementIterator it = new ElementIterator(doc.getDefaultRootElement()); + + // Iterate all content elements (which are leaves) + Element e; + String separator = System.getProperty("line.separator"); + while ((e = it.next()) != null) { + if (e.isLeaf()) { + int rangeStart = e.getStartOffset(); + int rangeEnd = e.getEndOffset(); + + if (rangeEnd < start || rangeStart > end) continue; + if (end < rangeEnd) rangeEnd = end; + if (start > rangeStart) rangeStart = start; + try { + String line = getText(rangeStart, rangeEnd-rangeStart); + if (e.getName().equals("br")) + result.append(separator); + else + result.append(line); + } catch (BadLocationException ex) { + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return result.toString(); + } + + public void setText(String text) { + super.setText(text); + // put the cursor at the top instead of leaving it at the end. + setCaretPosition(0); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SAListener.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SAListener.java new file mode 100644 index 00000000000..f4518a4d728 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SAListener.java @@ -0,0 +1,60 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.BorderLayout; +import java.awt.Dimension; + +import java.awt.event.*; + +import java.io.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.utilities.LivenessPathList; + +import sun.jvm.hotspot.ui.action.*; + +import com.sun.java.swing.ui.*; +import com.sun.java.swing.action.*; +import sun.jvm.hotspot.ui.tree.SimpleTreeNode; + + +public interface SAListener { + public void showThreadOopInspector(JavaThread thread); + public void showInspector(SimpleTreeNode node); + public void showThreadStackMemory(JavaThread thread); + public void showThreadInfo(JavaThread thread); + public void showJavaStackTrace(JavaThread thread); + public void showCodeViewer(Address address); + public void showLiveness(Oop oop, LivenessPathList liveness); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SAPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SAPanel.java new file mode 100644 index 00000000000..f80e5364b0d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SAPanel.java @@ -0,0 +1,117 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.BorderLayout; +import java.awt.Dimension; + +import java.awt.event.*; + +import java.io.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.ui.tree.*; + +import sun.jvm.hotspot.ui.action.*; + +import com.sun.java.swing.ui.*; +import com.sun.java.swing.action.*; + +/** + * This base class encapsulates many of the events that are fired from + * the various panels in this directory so they can easily be plugged + * in to different containing frameworks (HSDB, BugSpot). + */ +public class SAPanel extends JPanel { + protected List listeners = new ArrayList(); + + + public SAPanel() { + } + + public void addPanelListener(SAListener listener) { + listeners.add(listener); + } + + public void removePanelListener(SAListener listener) { + listeners.remove(listener); + } + + public void showThreadOopInspector(JavaThread t) { + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + SAListener listener = (SAListener) iter.next(); + listener.showThreadOopInspector(t); + } + } + + public void showInspector(Oop oop) { + showInspector(new OopTreeNodeAdapter(oop, null)); + } + + public void showInspector(SimpleTreeNode node) { + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + SAListener listener = (SAListener) iter.next(); + listener.showInspector(node); + } + } + + public void showThreadStackMemory(JavaThread t) { + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + SAListener listener = (SAListener) iter.next(); + listener.showThreadStackMemory(t); + } + } + + public void showJavaStackTrace(JavaThread t) { + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + SAListener listener = (SAListener) iter.next(); + listener.showJavaStackTrace(t); + } + } + + public void showThreadInfo(JavaThread t) { + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + SAListener listener = (SAListener) iter.next(); + listener.showThreadInfo(t); + } + } + + public void showCodeViewer(Address address) { + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + SAListener listener = (SAListener) iter.next(); + listener.showCodeViewer(address); + } + } + + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SourceCodePanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SourceCodePanel.java new file mode 100644 index 00000000000..06d838d48a0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SourceCodePanel.java @@ -0,0 +1,303 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; +import java.util.*; +import javax.swing.*; +import javax.swing.text.BadLocationException; + +/** Panel supporting loading of and scrolling through source code. + Contains convenience routines for implementing the Editor + interface. */ + +public class SourceCodePanel extends JPanel { + private JTextArea source; + private RowHeader header; + private String filename; + // Amount of white space between edges, line numbers and icons + private static final int LINE_NO_SPACE = 4; + // Size of icons in resources directory + private static final int ICON_SIZE = 12; + // Icons used in panel drawing + private static Icon topFrameCurLine; + private static Icon lowerFrameCurLine; + private static Icon breakpoint; + // State + private int highlightedLine = -1; + private Set/**/ breakpoints = new HashSet(); // Zero-based lines internally + // Parent Editor container and EditorCommands object for setting breakpoints + private EditorCommands comm; + private Editor parent; + + /** Support for displaying icons and line numbers in row header of + scroll pane */ + class RowHeader extends JPanel { + private JViewport view; + private boolean showLineNumbers; + private int width; + private int rowHeight; + private boolean initted; + + public RowHeader() { + super(); + initted = true; + addHierarchyBoundsListener(new HierarchyBoundsAdapter() { + public void ancestorResized(HierarchyEvent e) { + recomputeSize(); + } + }); + } + + public void paint(Graphics g) { + super.paint(g); + if (getShowLineNumbers()) { + // Visible region of header panel, in coordinate system of the + // panel, is provided by clip bounds of Graphics object. This + // is used to figure out which line numbers to draw. + Rectangle clip = g.getClipBounds(); + // To avoid missing lines, round down starting line number and + // round up ending line number + int start = clip.y / rowHeight; + int end = start + (clip.height + (rowHeight - 1)) / rowHeight; + // Draw these line numbers, right justified to look better + FontMetrics fm = getFontMetrics(getFont()); + int ascent = fm.getMaxAscent(); // Causes proper alignment -- trial-and-error + for (int i = start; i <= end; i++) { + // Line numbers are 1-based + String str = Integer.toString(i + 1); + int strWidth = GraphicsUtilities.getStringWidth(str, fm); + g.drawString(str, width - strWidth - LINE_NO_SPACE, ascent + rowHeight * i); + + // Draw breakpoint if necessary + if (breakpoints.contains(new Integer(i))) { + breakpoint.paintIcon(this, g, LINE_NO_SPACE, rowHeight * i); + } + + // Draw current line icon if necessary + if (i == highlightedLine) { + // FIXME: use correct icon (not always topmost frame) + topFrameCurLine.paintIcon(this, g, LINE_NO_SPACE, rowHeight * i); + } + } + } + } + + public boolean getShowLineNumbers() { + return showLineNumbers; + } + + public void setShowLineNumbers(boolean val) { + if (val != showLineNumbers) { + showLineNumbers = val; + recomputeSize(); + // Force re-layout + invalidate(); + validate(); + } + } + + public void setFont(Font f) { + super.setFont(f); + rowHeight = getFontMetrics(f).getHeight(); + recomputeSize(); + } + + void setViewport(JViewport view) { + this.view = view; + } + + void recomputeSize() { + if (!initted) return; + if (view == null) return; + width = ICON_SIZE + 2 * LINE_NO_SPACE; + try { + int numLines = 1 + source.getLineOfOffset(source.getDocument().getEndPosition().getOffset() - 1); + String str = Integer.toString(numLines); + if (getShowLineNumbers()) { + // Compute width based on whether we are drawing line numbers + width += GraphicsUtilities.getStringWidth(str, getFontMetrics(getFont())) + LINE_NO_SPACE; + } + // FIXME: add on width for all icons (breakpoint, current line, + // current line in caller frame) + Dimension d = new Dimension(width, numLines * getFontMetrics(getFont()).getHeight()); + setSize(d); + setPreferredSize(d); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + } + + public SourceCodePanel() { + maybeLoadIcons(); + + // Build user interface + setLayout(new BorderLayout()); + source = new JTextArea(); + source.setEditable(false); + source.getCaret().setVisible(true); + header = new RowHeader(); + header.setShowLineNumbers(true); + JScrollPane scroller = new JScrollPane(source); + JViewport rowView = new JViewport(); + rowView.setView(header); + header.setViewport(rowView); + rowView.setScrollMode(JViewport.SIMPLE_SCROLL_MODE); + scroller.setRowHeader(rowView); + add(scroller, BorderLayout.CENTER); + // Reset font now that header and source are present + setFont(getFont()); + + source.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + source.getCaret().setVisible(true); + } + }); + + source.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_F9) { + int lineNo = getCurrentLineNumber(); + // Only the debugger can figure out whether we are setting + // or clearing a breakpoint, since it has the debug + // information available and knows whether we're on a + // valid line + comm.toggleBreakpointAtLine(parent, lineNo); + } + } + }); + + } + + public void setFont(Font f) { + super.setFont(f); + if (source != null) { + source.setFont(f); + } + if (header != null) { + header.setFont(f); + } + } + + public boolean getShowLineNumbers() { + return header.getShowLineNumbers(); + } + + public void setShowLineNumbers(boolean val) { + header.setShowLineNumbers(val); + } + + public boolean openFile(String filename) { + try { + this.filename = filename; + File file = new File(filename); + int len = (int) file.length(); + StringBuffer buf = new StringBuffer(len); // Approximation + char[] tmp = new char[4096]; + FileReader in = new FileReader(file); + int res = 0; + do { + res = in.read(tmp, 0, tmp.length); + if (res >= 0) { + buf.append(tmp, 0, res); + } + } while (res != -1); + in.close(); + String text = buf.toString(); + source.setText(text); + header.recomputeSize(); + return true; + } catch (IOException e) { + return false; + } + } + + public String getSourceFileName() { + return filename; + } + + /** Line number is one-based */ + public int getCurrentLineNumber() { + try { + return 1 + source.getLineOfOffset(source.getCaretPosition()); + } catch (BadLocationException e) { + return 0; + } + } + + /** Line number is one-based */ + public void showLineNumber(int lineNo) { + try { + int offset = source.getLineStartOffset(lineNo - 1); + Rectangle rect = source.modelToView(offset); + if (rect == null) { + return; + } + source.scrollRectToVisible(rect); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + + /** Line number is one-based */ + public void highlightLineNumber(int lineNo) { + highlightedLine = lineNo - 1; + } + + public void showBreakpointAtLine(int lineNo) { breakpoints.add(new Integer(lineNo - 1)); repaint(); } + public boolean hasBreakpointAtLine(int lineNo){ return breakpoints.contains(new Integer(lineNo - 1)); } + public void clearBreakpointAtLine(int lineNo) { breakpoints.remove(new Integer(lineNo - 1)); repaint(); } + public void clearBreakpoints() { breakpoints.clear(); repaint(); } + + public void setEditorCommands(EditorCommands comm, Editor parent) { + this.comm = comm; + this.parent = parent; + } + + public void requestFocus() { + source.requestFocus(); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void maybeLoadIcons() { + if (topFrameCurLine == null) { + topFrameCurLine = loadIcon("resources/arrow.png"); + lowerFrameCurLine = loadIcon("resources/triangle.png"); + breakpoint = loadIcon("resources/breakpoint.png"); + } + } + + private Icon loadIcon(String which) { + URL url = getClass().getResource(which); + return new ImageIcon(url); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/StringTransferable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/StringTransferable.java new file mode 100644 index 00000000000..8132ec158d8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/StringTransferable.java @@ -0,0 +1,60 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.datatransfer.*; +import java.io.IOException; + +/** String transferable for drag-and-drop. + + @author Shannon Hickey +*/ + +class StringTransferable implements Transferable { + + private static final DataFlavor[] supported = {DataFlavor.stringFlavor}; + + private String str; + + public StringTransferable(String str) { + this.str = str; + } + + public DataFlavor[] getTransferDataFlavors() { + return supported; + } + + public boolean isDataFlavorSupported(DataFlavor flavor) { + return DataFlavor.stringFlavor.equals(flavor); + } + + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + return str; + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SysPropsPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SysPropsPanel.java new file mode 100644 index 00000000000..03206cf375b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/SysPropsPanel.java @@ -0,0 +1,73 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.util.*; +import javax.swing.*; + +import sun.jvm.hotspot.runtime.*; + +/** Shows values of Java System properties. */ + +public class SysPropsPanel extends JPanel { + private SAEditorPane flagsPane; + + public SysPropsPanel() { + initUI(); + } + + private void initUI() { + setLayout(new BorderLayout()); + flagsPane = new SAEditorPane(); + flagsPane.setText(getFlags()); + + add(new JScrollPane(flagsPane), BorderLayout.CENTER); + } + + private String getFlags() { + final StringBuffer buf = new StringBuffer(); + buf.append("System Properties"); + buf.append(""); + + Properties sysProps = VM.getVM().getSystemProperties(); + if (sysProps != null) { + Enumeration keys = sysProps.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + buf.append(""); + } + } else { + buf.append(""); + } + buf.append("
"); + buf.append(key.toString()); + buf.append(""); + buf.append(sysProps.get(key).toString()); + buf.append("
System Properties info not available!
"); + buf.append(""); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ThreadInfoPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ThreadInfoPanel.java new file mode 100644 index 00000000000..46f757da366 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/ThreadInfoPanel.java @@ -0,0 +1,71 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.io.*; +import javax.swing.*; + +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; + +/** Provides implementation level info about a Java Thread */ + +public class ThreadInfoPanel extends JPanel { + + private JTextArea textArea; + + public ThreadInfoPanel() { + initUI(); + } + + private void initUI() { + setLayout(new BorderLayout()); + + // Simple at first + JScrollPane scroller = new JScrollPane(); + textArea = new JTextArea(); + textArea.setEditable(false); + textArea.setLineWrap(true); + textArea.setWrapStyleWord(true); + scroller.getViewport().add(textArea); + add(scroller, BorderLayout.CENTER); + } + + + public ThreadInfoPanel(final JavaThread thread) { + initUI(); + setJavaThread(thread); + } + + public void setJavaThread(final JavaThread thread) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PrintStream tty = new PrintStream(bos); + tty.println("Thread Info: " + thread.getThreadName()); + thread.printInfoOn(tty); + + textArea.setText(bos.toString()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/VMFlagsPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/VMFlagsPanel.java new file mode 100644 index 00000000000..b73b8eacf41 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/VMFlagsPanel.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import javax.swing.*; + +import sun.jvm.hotspot.runtime.*; + +/** Shows values of Java command line flags */ + +public class VMFlagsPanel extends JPanel { + private JEditorPane flagsPane; + + public VMFlagsPanel() { + initUI(); + } + + private void initUI() { + setLayout(new BorderLayout()); + flagsPane = new JEditorPane(); + flagsPane.setContentType("text/html"); + flagsPane.setEditable(false); + flagsPane.setText(getFlags()); + + add(new JScrollPane(flagsPane), BorderLayout.CENTER); + } + + private String getFlags() { + VM.Flag[] flags = VM.getVM().getCommandLineFlags(); + StringBuffer buf = new StringBuffer(); + buf.append("VM Command Line Flags"); + if (flags == null) { + buf.append("Command Flag info not available (use 1.4.1_03 or later)!"); + } else { + buf.append(""); + for (int f = 0; f < flags.length; f++) { + buf.append(""); + } + buf.append("
"); + buf.append(flags[f].getName()); + buf.append(""); + buf.append(flags[f].getValue()); + buf.append("
"); + } + + buf.append(""); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/VMVersionInfoPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/VMVersionInfoPanel.java new file mode 100644 index 00000000000..893cae7bf96 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/VMVersionInfoPanel.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import javax.swing.*; + +import sun.jvm.hotspot.runtime.*; + +/** Shows VM version information */ + +public class VMVersionInfoPanel extends JPanel { + private JEditorPane versionPane; + + public VMVersionInfoPanel() { + initUI(); + } + + private void initUI() { + setLayout(new BorderLayout()); + versionPane = new JEditorPane(); + versionPane.setContentType("text/html"); + versionPane.setEditable(false); + versionPane.setText(getVersionInfo()); + + add(versionPane, BorderLayout.CENTER); + } + + private String getVersionInfo() { + VM vm = VM.getVM(); + StringBuffer buf = new StringBuffer(); + buf.append("VM Version Info"); + buf.append(""); + + // VM type + buf.append(""); + buf.append(""); + + // VM release + String release = vm.getVMRelease(); + if (release != null) { + buf.append(""); + } + + // VM internal info + String internalInfo = vm.getVMInternalInfo(); + if (internalInfo != null) { + buf.append(""); + } + + buf.append("
VM Type"); + if (vm.isCore()) { + buf.append("core"); + } else if(vm.isClientCompiler()) { + buf.append("client"); + } else if(vm.isServerCompiler()) { + buf.append("server"); + } else { + buf.append("unknown"); + } + buf.append("
VM Release"); + buf.append(release); + buf.append("
VM Internal Info"); + buf.append(internalInfo); + buf.append("
"); + return buf.toString(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/FindAction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/FindAction.java new file mode 100644 index 00000000000..5725d62129d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/FindAction.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.action; + +import javax.swing.Action; + +import com.sun.java.swing.action.ActionManager; +import com.sun.java.swing.action.DelegateAction; + +/** + * Callback action for Finding the Object Type + */ +public class FindAction extends DelegateAction { + + // XXX - Constants should be static if not in inner class + public static final String VALUE_COMMAND = "find-command"; + public static final String VALUE_NAME = "Find Objects"; + public static final String VALUE_SMALL_ICON = "general/Find16.gif"; + public static final Integer VALUE_MNEMONIC = new Integer('F'); + public static final String VALUE_SHORT_DESCRIPTION = "Find Objects of this Type"; + public static final String VALUE_LONG_DESCRIPTION = VALUE_SHORT_DESCRIPTION; + + public FindAction() { + super(VALUE_NAME, ActionManager.getIcon(VALUE_SMALL_ICON)); + + putValue(Action.ACTION_COMMAND_KEY, VALUE_COMMAND); + putValue(Action.SHORT_DESCRIPTION, VALUE_SHORT_DESCRIPTION); + putValue(Action.LONG_DESCRIPTION, VALUE_LONG_DESCRIPTION); + putValue(Action.MNEMONIC_KEY, VALUE_MNEMONIC); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/FindClassesAction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/FindClassesAction.java new file mode 100644 index 00000000000..dfa14bf073e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/FindClassesAction.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.action; + +import javax.swing.Action; + +import com.sun.java.swing.action.ActionManager; +import com.sun.java.swing.action.DelegateAction; + +/** + * Callback action for Finding class by name. + */ +public class FindClassesAction extends DelegateAction { + + // XXX - Constants should be static if not in inner class + public static final String VALUE_COMMAND = "find-classes-command"; + public static final String VALUE_NAME = "Find classes"; + public static final String VALUE_SMALL_ICON = "general/Find16.gif"; + public static final Integer VALUE_MNEMONIC = new Integer('C'); + public static final String VALUE_SHORT_DESCRIPTION = "Find classes with given name part"; + public static final String VALUE_LONG_DESCRIPTION = VALUE_SHORT_DESCRIPTION; + + public FindClassesAction() { + super(VALUE_NAME, ActionManager.getIcon(VALUE_SMALL_ICON)); + + putValue(Action.ACTION_COMMAND_KEY, VALUE_COMMAND); + putValue(Action.SHORT_DESCRIPTION, VALUE_SHORT_DESCRIPTION); + putValue(Action.LONG_DESCRIPTION, VALUE_LONG_DESCRIPTION); + putValue(Action.MNEMONIC_KEY, VALUE_MNEMONIC); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/FindCrashesAction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/FindCrashesAction.java new file mode 100644 index 00000000000..e774f2dbb8a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/FindCrashesAction.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.action; + +import javax.swing.Action; + +import com.sun.java.swing.action.ActionManager; +import com.sun.java.swing.action.DelegateAction; + +/** + * Callback action for FindCrashesing the Object Type + */ +public class FindCrashesAction extends DelegateAction { + + public static final String VALUE_COMMAND = "find-crashes-command"; + public static final String VALUE_NAME = "Find Crashes..."; + // XXX - These icons are temporary. Should use a lightning bolt with a search arrow. + public static final String VALUE_SMALL_ICON = "general/Delete16.gif"; + public static final String VALUE_LARGE_ICON = "general/Delete24.gif"; + public static final Integer VALUE_MNEMONIC = new Integer('H'); + public static final String VALUE_SHORT_DESCRIPTION = "Find Crashes"; + public static final String VALUE_LONG_DESCRIPTION = "Searches the threads for potential crashes"; + + public FindCrashesAction() { + super(VALUE_NAME, ActionManager.getIcon(VALUE_SMALL_ICON)); + + putValue(Action.ACTION_COMMAND_KEY, VALUE_COMMAND); + putValue(Action.SHORT_DESCRIPTION, VALUE_SHORT_DESCRIPTION); + putValue(Action.LONG_DESCRIPTION, VALUE_LONG_DESCRIPTION); + putValue(Action.MNEMONIC_KEY, VALUE_MNEMONIC); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/HSDBActionManager.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/HSDBActionManager.java new file mode 100644 index 00000000000..16f8f4ed672 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/HSDBActionManager.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.action; + +import com.sun.java.swing.action.ActionManager; + +/** + * An application specific implementation of an ActionManager + */ +public class HSDBActionManager extends ActionManager { + + public static ActionManager getInstance() { + if (manager == null) { + manager = new HSDBActionManager(); + } + return manager; + } + + protected void addActions() { + // actions for ObjectHistogramPanel + addAction(FindAction.VALUE_COMMAND, new FindAction()); + addAction(ShowAction.VALUE_COMMAND, new ShowAction()); + + // Actions for Java Threads Panel + addAction(InspectAction.VALUE_COMMAND, new InspectAction()); + addAction(MemoryAction.VALUE_COMMAND, new MemoryAction()); + addAction(ThreadInfoAction.VALUE_COMMAND, new ThreadInfoAction()); + addAction(FindCrashesAction.VALUE_COMMAND, new FindCrashesAction()); + addAction(JavaStackTraceAction.VALUE_COMMAND, new JavaStackTraceAction()); + + // Action for ClassBrowserPanel + addAction(FindClassesAction.VALUE_COMMAND, new FindClassesAction()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/InspectAction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/InspectAction.java new file mode 100644 index 00000000000..c2c2c92bd36 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/InspectAction.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.action; + +import javax.swing.Action; + +import com.sun.java.swing.action.ActionManager; +import com.sun.java.swing.action.DelegateAction; + +/** + * Callback action for Inspecting the Object Type + */ +public class InspectAction extends DelegateAction { + + public static final String VALUE_COMMAND = "inspect-command"; + public static final String VALUE_NAME = "Inspect Thread..."; + public static final String VALUE_SMALL_ICON = "general/ZoomIn16.gif"; + public static final String VALUE_LARGE_ICON = "general/ZoomIn24.gif"; + public static final Integer VALUE_MNEMONIC = new Integer('T'); + public static final String VALUE_SHORT_DESCRIPTION = "Inspect selected thread"; + public static final String VALUE_LONG_DESCRIPTION = "Open an inspector on the selected thread"; + + public InspectAction() { + super(VALUE_NAME, ActionManager.getIcon(VALUE_SMALL_ICON)); + + putValue(Action.ACTION_COMMAND_KEY, VALUE_COMMAND); + putValue(Action.SHORT_DESCRIPTION, VALUE_SHORT_DESCRIPTION); + putValue(Action.LONG_DESCRIPTION, VALUE_LONG_DESCRIPTION); + putValue(Action.MNEMONIC_KEY, VALUE_MNEMONIC); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/JavaStackTraceAction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/JavaStackTraceAction.java new file mode 100644 index 00000000000..2ed5b7ecb8b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/JavaStackTraceAction.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.action; + +import javax.swing.Action; + +import com.sun.java.swing.action.ActionManager; +import com.sun.java.swing.action.DelegateAction; + +/** + * Callback action for viewing java stack trace + */ +public class JavaStackTraceAction extends DelegateAction { + public static final String VALUE_COMMAND = "jstack-command"; + public static final String VALUE_NAME = "Show Java stack trace"; + public static final String VALUE_SMALL_ICON = "general/History16.gif"; + public static final String VALUE_LARGE_ICON = "general/History24.gif"; + public static final Integer VALUE_MNEMONIC = new Integer('J'); + public static final String VALUE_SHORT_DESCRIPTION = "Show Java stack trace for selected thread"; + public static final String VALUE_LONG_DESCRIPTION = VALUE_SHORT_DESCRIPTION; + + public JavaStackTraceAction() { + super(VALUE_NAME, ActionManager.getIcon(VALUE_SMALL_ICON)); + + putValue(Action.ACTION_COMMAND_KEY, VALUE_COMMAND); + putValue(Action.SHORT_DESCRIPTION, VALUE_SHORT_DESCRIPTION); + putValue(Action.LONG_DESCRIPTION, VALUE_LONG_DESCRIPTION); + putValue(Action.MNEMONIC_KEY, VALUE_MNEMONIC); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/MemoryAction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/MemoryAction.java new file mode 100644 index 00000000000..18463dc97e4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/MemoryAction.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.action; + +import javax.swing.Action; + +import com.sun.java.swing.action.ActionManager; +import com.sun.java.swing.action.DelegateAction; + +/** + * Callback action for Memorying the Object Type + */ +public class MemoryAction extends DelegateAction { + + public static final String VALUE_COMMAND = "memory-command"; + public static final String VALUE_NAME = "Stack Memory..."; + // XXX - note: these icons are temporary. Should create custom icons. + public static final String VALUE_SMALL_ICON = "development/Server16.gif"; + public static final String VALUE_LARGE_ICON = "development/Server24.gif"; + public static final Integer VALUE_MNEMONIC = new Integer('M'); + public static final String VALUE_SHORT_DESCRIPTION = "Show Stack Memory"; + public static final String VALUE_LONG_DESCRIPTION = "Show the stack memory for the current thread"; + + public MemoryAction() { + super(VALUE_NAME, ActionManager.getIcon(VALUE_SMALL_ICON)); + + putValue(Action.ACTION_COMMAND_KEY, VALUE_COMMAND); + putValue(Action.SHORT_DESCRIPTION, VALUE_SHORT_DESCRIPTION); + putValue(Action.LONG_DESCRIPTION, VALUE_LONG_DESCRIPTION); + putValue(Action.MNEMONIC_KEY, VALUE_MNEMONIC); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/ShowAction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/ShowAction.java new file mode 100644 index 00000000000..023d90b4423 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/ShowAction.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.action; + +import javax.swing.Action; + +import com.sun.java.swing.action.ActionManager; +import com.sun.java.swing.action.DelegateAction; + +/** + * Callback action for Showing the Object Type + */ +public class ShowAction extends DelegateAction { + + public static final String VALUE_COMMAND = "show-command"; + public static final String VALUE_NAME = "Show Objects"; + public static final String VALUE_SMALL_ICON = "general/Zoom16.gif"; + public static final Integer VALUE_MNEMONIC = new Integer('S'); + public static final String VALUE_SHORT_DESCRIPTION = "Show Objects of this selected type"; + public static final String VALUE_LONG_DESCRIPTION = VALUE_SHORT_DESCRIPTION; + + public ShowAction() { + super(VALUE_NAME, ActionManager.getIcon(VALUE_SMALL_ICON)); + + putValue(Action.ACTION_COMMAND_KEY, VALUE_COMMAND); + putValue(Action.SHORT_DESCRIPTION, VALUE_SHORT_DESCRIPTION); + putValue(Action.LONG_DESCRIPTION, VALUE_LONG_DESCRIPTION); + putValue(Action.MNEMONIC_KEY, VALUE_MNEMONIC); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/ThreadInfoAction.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/ThreadInfoAction.java new file mode 100644 index 00000000000..f00b865efd6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/action/ThreadInfoAction.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.action; + +import javax.swing.Action; + +import com.sun.java.swing.action.ActionManager; +import com.sun.java.swing.action.StateChangeAction; + +/** + * Callback action for ThreadInfoing the Object Type + */ +public class ThreadInfoAction extends StateChangeAction { + + public static final String VALUE_COMMAND = "thread-info-command"; + public static final String VALUE_NAME = "Show Thread Information..."; + public static final String VALUE_SMALL_ICON = "general/Information16.gif"; + public static final String VALUE_LARGE_ICON = "general/Information24.gif"; + public static final Integer VALUE_MNEMONIC = new Integer('I'); + public static final String VALUE_SHORT_DESCRIPTION = "Show Thread Informaion"; + public static final String VALUE_LONG_DESCRIPTION = "Show information about the current thread"; + + public ThreadInfoAction() { + super(VALUE_NAME, ActionManager.getIcon(VALUE_SMALL_ICON)); + + putValue(Action.ACTION_COMMAND_KEY, VALUE_COMMAND); + putValue(Action.SHORT_DESCRIPTION, VALUE_SHORT_DESCRIPTION); + putValue(Action.LONG_DESCRIPTION, VALUE_LONG_DESCRIPTION); + putValue(Action.MNEMONIC_KEY, VALUE_MNEMONIC); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/ClassBrowserPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/ClassBrowserPanel.java new file mode 100644 index 00000000000..3dab494a67c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/ClassBrowserPanel.java @@ -0,0 +1,159 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.classbrowser; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.ui.*; +import sun.jvm.hotspot.ui.action.*; +import sun.jvm.hotspot.utilities.*; + +import com.sun.java.swing.ui.StatusBar; +import com.sun.java.swing.ui.CommonToolBar; + +import com.sun.java.swing.action.ActionManager; +import com.sun.java.swing.action.DelegateAction; + +public class ClassBrowserPanel extends JPanel implements ActionListener { + private StatusBar statusBar; + private ClassBrowserToolBar toolBar; + private JSplitPane splitPane; + private SAEditorPane classesEditor; + private SAEditorPane contentEditor; + private HTMLGenerator htmlGen; + + public ClassBrowserPanel() { + htmlGen = new HTMLGenerator(); + + HyperlinkListener hyperListener = new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + contentEditor.setText(htmlGen.genHTMLForHyperlink(e.getDescription())); + } + } + }; + + classesEditor = new SAEditorPane(); + classesEditor.addHyperlinkListener(hyperListener); + + contentEditor = new SAEditorPane(); + contentEditor.addHyperlinkListener(hyperListener); + + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BorderLayout()); + topPanel.add(new JScrollPane(classesEditor), BorderLayout.CENTER); + + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new BorderLayout()); + bottomPanel.add(new JScrollPane(contentEditor), BorderLayout.CENTER); + + splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topPanel, bottomPanel); + splitPane.setDividerLocation(0); + + setLayout(new BorderLayout()); + add(splitPane, BorderLayout.CENTER); + statusBar = new StatusBar(); + add(statusBar, BorderLayout.SOUTH); + toolBar = new ClassBrowserToolBar(statusBar); + add(toolBar, BorderLayout.NORTH); + registerActions(); + } + + public void setClassesText(String text) { + classesEditor.setText(text); + splitPane.setDividerLocation(0.5); + } + + public void setContentText(String text) { + contentEditor.setText(text); + splitPane.setDividerLocation(0.5); + } + + private class ClassBrowserToolBar extends CommonToolBar { + private JTextField searchTF; + + public ClassBrowserToolBar(StatusBar status) { + super(HSDBActionManager.getInstance(), status); + } + + protected void addComponents() { + searchTF = new JTextField(); + searchTF.setToolTipText("Find classes"); + + // Pressing Enter on the text field will search + InputMap im = searchTF.getInputMap(); + im.put(KeyStroke.getKeyStroke("ENTER"), "enterPressed"); + ActionMap am = searchTF.getActionMap(); + am.put("enterPressed", manager.getAction(FindClassesAction.VALUE_COMMAND)); + + add(searchTF); + addButton(manager.getAction(FindClassesAction.VALUE_COMMAND)); + } + + public String getFindText() { + return searchTF.getText(); + } + } + + // + // ActionListener implementation and actions support + // + + public void actionPerformed(ActionEvent evt) { + String command = evt.getActionCommand(); + + if (command.equals(FindClassesAction.VALUE_COMMAND)) { + findClasses(); + } + } + + protected void registerActions() { + registerAction(FindClassesAction.VALUE_COMMAND); + } + + private void registerAction(String actionName) { + ActionManager manager = ActionManager.getInstance(); + DelegateAction action = manager.getDelegateAction(actionName); + action.addActionListener(this); + } + + private void findClasses() { + String findText = toolBar.getFindText(); + if (findText == null || findText.equals("")) { + return; + } + + setContentText(htmlGen.genHTMLForWait("Finding classes ...")); + InstanceKlass[] klasses = SystemDictionaryHelper.findInstanceKlasses(findText); + if (klasses.length == 0) { + setContentText(htmlGen.genHTMLForMessage("No class found with name containing '" + findText + "'")); + } else { + setContentText(htmlGen.genHTMLForKlassNames(klasses)); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/CodeViewerPanel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/CodeViewerPanel.java new file mode 100644 index 00000000000..b4d2d583e1a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/CodeViewerPanel.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.classbrowser; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; +import sun.jvm.hotspot.ui.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +public class CodeViewerPanel extends JPanel { + protected SAEditorPane contentEditor; + protected HistoryComboBox address; + protected HTMLGenerator htmlGen; + protected JScrollPane scrollPane; + + public CodeViewerPanel() { + htmlGen = new HTMLGenerator(); + contentEditor = new SAEditorPane(); + + HyperlinkListener hyperListener = new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + String description = e.getDescription(); + int equalToIndex = description.indexOf('='); + if (equalToIndex != -1) { + String item = description.substring(0, equalToIndex); + if (item.equals("pc") || item.equals("klass") || item.equals("method")) { + address.setText(description.substring(equalToIndex + 1)); + } + } + contentEditor.setText(htmlGen.genHTMLForHyperlink(description)); + } + } + }; + + + setLayout(new BorderLayout()); + + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BorderLayout()); + topPanel.add(new JLabel("Enter PC or methodOop/klassOop Address: "), BorderLayout.WEST); + address = new HistoryComboBox(); + topPanel.add(address, BorderLayout.CENTER); + + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new GridLayout(1, 1)); + contentEditor = new SAEditorPane(); + contentEditor.addHyperlinkListener(hyperListener); + scrollPane = new JScrollPane(contentEditor); + bottomPanel.add(scrollPane); + + add(topPanel, BorderLayout.NORTH); + add(bottomPanel, BorderLayout.CENTER); + + address.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + viewAddress(); + } + }); + + } + + private void viewAddress() { + if (address.getText() != null && !address.getText().equals("")) { + contentEditor.setText(htmlGen.genHTMLForAddress(address.getText())); + } + } + + public void viewAddress(Address addr) { + address.setText(addr.toString()); + viewAddress(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java new file mode 100644 index 00000000000..9c5c35dffd6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java @@ -0,0 +1,2018 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.classbrowser; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.asm.*; +import sun.jvm.hotspot.asm.sparc.*; +import sun.jvm.hotspot.asm.x86.*; +import sun.jvm.hotspot.asm.ia64.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.compiler.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.tools.jcore.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +public class HTMLGenerator implements /* imports */ ClassConstants { + static class Formatter { + boolean html; + StringBuffer buf = new StringBuffer(); + + Formatter(boolean h) { + html = h; + } + + void append(String s) { + buf.append(s); + } + + void append(int s) { + buf.append(s); + } + + void append(char s) { + buf.append(s); + } + + void append(StringBuffer s) { + buf.append(s); + } + + void append(Formatter s) { + buf.append(s); + } + + StringBuffer getBuffer() { + return buf; + } + + public String toString() { + return buf.toString(); + } + + void wrap(String tag, String text) { + wrap(tag, tag, text); + } + void wrap(String before, String after, String text) { + beginTag(before); + append(text); + endTag(after); + } + + // header tags + void h1(String s) { nl(); wrap("h1", s); nl(); } + void h2(String s) { nl(); wrap("h2", s); nl(); } + void h3(String s) { nl(); wrap("h3", s); nl(); } + void h4(String s) { nl(); wrap("h4", s); nl(); } + + // list tags + void beginList() { beginTag("ul"); nl(); } + void li(String s) { wrap("li", s); nl(); } + void endList() { endTag("ul"); nl(); } + + // table tags + void beginTable(int border) { + beginTag("table border='" + border + "'"); + } + void cell(String s) { wrap("td", s); } + void headerCell(String s) { wrap("th", s); } + void endTable() { endTag("table"); } + + void link(String href, String text) { + wrap("a href='" + href + "'", "a", text); + } + void beginTag(String s) { + if (html) { append("<"); append(s); append(">"); } + } + void endTag(String s) { + if (html) { + append(""); + } else { + if (s.equals("table") || s.equals("tr")) { + nl(); + } + if (s.equals("td") || s.equals("th")) { + append(" "); + } + } + } + void bold(String s) { + wrap("b", s); + } + + void nl() { + if (!html) buf.append("\n"); + } + + void br() { + if (html) append("
"); + else append("\n"); + } + void genEmptyHTML() { + if (html) append(""); + } + + void genHTMLPrologue() { + if (html) append(""); + } + + void genHTMLPrologue(String title) { + if (html) { + append(""); + append(title); + append(""); + append(""); + } + h2(title); + } + void genHTMLEpilogue() { + if (html) append(""); + } + + } + + private static final String DUMP_KLASS_OUTPUT_DIR = "."; + private static final int NATIVE_CODE_SIZE = 200; + private final String spaces; + private final String tab; + + private boolean genHTML = true; + + public HTMLGenerator() { + this(true); + } + + public HTMLGenerator(boolean html) { + genHTML = html; + if (html) { + spaces = "  "; + tab = "    "; + } else { + spaces = " "; + tab = " "; + } + } + + private static CPUHelper cpuHelper; + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(); + } + }); + } + + private static synchronized void initialize() { + String cpu = VM.getVM().getCPU(); + if (cpu.equals("sparc")) { + cpuHelper = new SPARCHelper(); + } else if (cpu.equals("x86")) { + cpuHelper = new X86Helper(); + } else if (cpu.equals("ia64")) { + cpuHelper = new IA64Helper(); + } else { + throw new RuntimeException("cpu '" + cpu + "' is not yet supported!"); + } + } + + protected static synchronized CPUHelper getCPUHelper() { + return cpuHelper; + } + + protected String escapeHTMLSpecialChars(String value) { + if (!genHTML) return value; + + Formatter buf = new Formatter(genHTML); + int len = value.length(); + for (int i=0; i < len; i++) { + char c = value.charAt(i); + switch (c) { + case '<': + buf.append("<"); + break; + case '>': + buf.append(">"); + break; + case '&': + buf.append("&"); + break; + default: + buf.append(c); + break; + } + } + return buf.toString(); + } + + public String genHTMLForMessage(String message) { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(message); + buf.genHTMLEpilogue(); + return buf.toString(); + } + + public String genHTMLErrorMessage(Exception exp) { + exp.printStackTrace(); + return genHTMLForMessage(exp.getClass().getName() + " : " + exp.getMessage()); + } + + public String genHTMLForWait(String message) { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue("Please wait .."); + buf.h2(message); + return buf.toString(); + } + + protected String genKlassTitle(InstanceKlass klass) { + Formatter buf = new Formatter(genHTML); + AccessFlags acc = klass.getAccessFlagsObj(); + if (acc.isPublic()) { + buf.append("public "); + } else if (acc.isProtected()) { + buf.append("protected "); + } else if (acc.isPrivate()) { + buf.append("private "); + } + + if (acc.isStatic()) { + buf.append("static "); + } + + if (acc.isAbstract() ) { + buf.append("abstract "); + } else if (acc.isFinal()) { + buf.append("final "); + } + + if (acc.isStrict()) { + buf.append("strict "); + } + + // javac generated flags + if (acc.isEnum()) { + buf.append("[enum] "); + } + if (acc.isSynthetic()) { + buf.append("[synthetic] "); + } + + if (klass.isInterface()) { + buf.append("interface"); + } else { + buf.append("class"); + } + + buf.append(' '); + buf.append(klass.getName().asString().replace('/', '.')); + // is it generic? + Symbol genSig = klass.getGenericSignature(); + if (genSig != null) { + buf.append(" [signature "); + buf.append(escapeHTMLSpecialChars(genSig.asString())); + buf.append("] "); + } else { + buf.append(' '); + } + buf.append('@'); + buf.append(klass.getHandle().toString()); + return buf.toString(); + } + + protected String genBaseHref() { + return ""; + } + + protected String genKlassHref(InstanceKlass klass) { + return genBaseHref() + "klass=" + klass.getHandle(); + } + + protected String genKlassLink(InstanceKlass klass) { + Formatter buf = new Formatter(genHTML); + buf.link(genKlassHref(klass), genKlassTitle(klass)); + return buf.toString(); + } + + protected String genMethodModifierString(AccessFlags acc) { + Formatter buf = new Formatter(genHTML); + if (acc.isPrivate()) { + buf.append("private "); + } else if (acc.isProtected()) { + buf.append("protected "); + } else if (acc.isPublic()) { + buf.append("public "); + } + + if (acc.isStatic()) { + buf.append("static "); + } else if (acc.isAbstract() ) { + buf.append("abstract "); + } else if (acc.isFinal()) { + buf.append("final "); + } + + if (acc.isNative()) { + buf.append("native "); + } + + if (acc.isStrict()) { + buf.append("strict "); + } + + if (acc.isSynchronized()) { + buf.append("synchronized "); + } + + // javac generated flags + if (acc.isBridge()) { + buf.append("[bridge] "); + } + + if (acc.isSynthetic()) { + buf.append("[synthetic] "); + } + + if (acc.isVarArgs()) { + buf.append("[varargs] "); + } + + return buf.toString(); + } + + protected String genMethodNameAndSignature(Method method) { + Formatter buf = new Formatter(genHTML); + buf.append(genMethodModifierString(method.getAccessFlagsObj())); + Symbol sig = method.getSignature(); + new SignatureConverter(sig, buf.getBuffer()).iterateReturntype(); + buf.append(" "); + String methodName = method.getName().asString(); + buf.append(escapeHTMLSpecialChars(methodName)); + buf.append('('); + new SignatureConverter(sig, buf.getBuffer()).iterateParameters(); + buf.append(')'); + // is it generic? + Symbol genSig = method.getGenericSignature(); + if (genSig != null) { + buf.append(" [signature "); + buf.append(escapeHTMLSpecialChars(genSig.asString())); + buf.append("] "); + } + return buf.toString().replace('/', '.'); + } + + protected String genMethodTitle(Method method) { + Formatter buf = new Formatter(genHTML); + buf.append(genMethodNameAndSignature(method)); + buf.append(' '); + buf.append('@'); + buf.append(method.getHandle().toString()); + return buf.toString(); + } + + protected String genMethodHref(Method m) { + return genBaseHref() + "method=" + m.getHandle(); + } + + protected String genMethodLink(Method m) { + Formatter buf = new Formatter(genHTML); + buf.link(genMethodHref(m), genMethodTitle(m)); + return buf.toString(); + } + + protected String genMethodAndKlassLink(Method m) { + Formatter buf = new Formatter(genHTML); + buf.append(genMethodLink(m)); + buf.append(" of "); + buf.append(genKlassLink((InstanceKlass) m.getMethodHolder())); + return buf.toString(); + } + + protected String genNMethodHref(NMethod nm) { + return genBaseHref() + "nmethod=" + nm.getAddress(); + } + + public String genNMethodTitle(NMethod nmethod) { + Formatter buf = new Formatter(genHTML); + Method m = nmethod.getMethod(); + + buf.append("Disassembly for compiled method ["); + buf.append(genMethodTitle(m)); + buf.append(" ] "); + buf.append('@'); + buf.append(nmethod.getAddress().toString()); + return buf.toString(); + } + + protected String genNMethodLink(NMethod nm) { + Formatter buf = new Formatter(genHTML); + buf.link(genNMethodHref(nm), genNMethodTitle(nm)); + return buf.toString(); + } + + public String genCodeBlobTitle(CodeBlob blob) { + Formatter buf = new Formatter(genHTML); + buf.append("Disassembly for code blob " + blob.getName() + " ["); + buf.append(blob.getClass().getName()); + buf.append(" ] @"); + buf.append(blob.getAddress().toString()); + return buf.toString(); + } + + protected BytecodeDisassembler createBytecodeDisassembler(Method m) { + return new BytecodeDisassembler(m); + } + + private String genLowHighShort(int val) { + Formatter buf = new Formatter(genHTML); + buf.append('#'); + buf.append(Integer.toString(val & 0xFFFF)); + buf.append(" #"); + buf.append(Integer.toString((val >> 16) & 0xFFFF)); + return buf.toString(); + } + + protected String genHTMLTableForConstantPool(ConstantPool cpool) { + Formatter buf = new Formatter(genHTML); + buf.beginTable(1); + + buf.beginTag("tr"); + buf.headerCell("Index"); + buf.headerCell("Constant Type"); + buf.headerCell("Constant Value"); + buf.endTag("tr"); + + final int length = (int) cpool.getLength(); + // zero'th pool entry is always invalid. ignore it. + for (int index = 1; index < length; index++) { + buf.beginTag("tr"); + buf.cell(Integer.toString(index)); + + int ctag = (int) cpool.getTags().getByteAt((int) index); + switch (ctag) { + case JVM_CONSTANT_Integer: + buf.cell("JVM_CONSTANT_Integer"); + buf.cell(Integer.toString(cpool.getIntAt(index))); + break; + + case JVM_CONSTANT_Float: + buf.cell("JVM_CONSTANT_Float"); + buf.cell(Float.toString(cpool.getFloatAt(index))); + break; + + case JVM_CONSTANT_Long: + buf.cell("JVM_CONSTANT_Long"); + buf.cell(Long.toString(cpool.getLongAt(index))); + // long entries occupy two slots + index++; + break; + + case JVM_CONSTANT_Double: + buf.cell("JVM_CONSTANT_Double"); + buf.cell(Double.toString(cpool.getDoubleAt(index))); + // double entries occupy two slots + index++; + break; + + case JVM_CONSTANT_UnresolvedClass: + buf.cell("JVM_CONSTANT_UnresolvedClass"); + buf.cell(cpool.getSymbolAt(index).asString()); + break; + + case JVM_CONSTANT_Class: + buf.cell("JVM_CONSTANT_Class"); + Klass klass = (Klass) cpool.getObjAt(index); + if (klass instanceof InstanceKlass) { + buf.cell(genKlassLink((InstanceKlass) klass)); + } else { + buf.cell(klass.getName().asString().replace('/', '.')); + } + break; + + case JVM_CONSTANT_UnresolvedString: + buf.cell("JVM_CONSTANT_UnresolvedString"); + buf.cell("\"" + + escapeHTMLSpecialChars(cpool.getSymbolAt(index).asString()) + + "\""); + break; + + case JVM_CONSTANT_Utf8: + buf.cell("JVM_CONSTANT_Utf8"); + buf.cell("\"" + + escapeHTMLSpecialChars(cpool.getSymbolAt(index).asString()) + + "\""); + break; + + case JVM_CONSTANT_String: + buf.cell("JVM_CONSTANT_String"); + buf.cell("\"" + + escapeHTMLSpecialChars(OopUtilities.stringOopToString(cpool.getObjAt(index))) + "\""); + break; + + case JVM_CONSTANT_Fieldref: + buf.cell("JVM_CONSTANT_Fieldref"); + buf.cell(genLowHighShort(cpool.getIntAt(index))); + break; + + case JVM_CONSTANT_Methodref: + buf.cell("JVM_CONSTANT_Methodref"); + buf.cell(genLowHighShort(cpool.getIntAt(index))); + break; + + case JVM_CONSTANT_InterfaceMethodref: + buf.cell("JVM_CONSTANT_InterfaceMethodref"); + buf.cell(genLowHighShort(cpool.getIntAt(index))); + break; + + case JVM_CONSTANT_NameAndType: + buf.cell("JVM_CONSTANT_NameAndType"); + buf.cell(genLowHighShort(cpool.getIntAt(index))); + break; + + case JVM_CONSTANT_ClassIndex: + buf.cell("JVM_CONSTANT_ClassIndex"); + buf.cell(Integer.toString(cpool.getIntAt(index))); + break; + + case JVM_CONSTANT_StringIndex: + buf.cell("JVM_CONSTANT_StringIndex"); + buf.cell(Integer.toString(cpool.getIntAt(index))); + break; + } + + buf.endTag("tr"); + } + + buf.endTable(); + return buf.toString(); + } + + public String genHTML(ConstantPool cpool) { + try { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(genConstantPoolTitle(cpool)); + buf.h3("Holder Class"); + buf.append(genKlassLink((InstanceKlass) cpool.getPoolHolder())); + buf.h3("Constants"); + buf.append(genHTMLTableForConstantPool(cpool)); + buf.genHTMLEpilogue(); + return buf.toString(); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + protected String genConstantPoolHref(ConstantPool cpool) { + return genBaseHref() + "cpool=" + cpool.getHandle(); + } + + protected String genConstantPoolTitle(ConstantPool cpool) { + Formatter buf = new Formatter(genHTML); + buf.append("Constant Pool of ["); + buf.append(genKlassTitle((InstanceKlass) cpool.getPoolHolder())); + buf.append("] @"); + buf.append(cpool.getHandle().toString()); + return buf.toString(); + } + + protected String genConstantPoolLink(ConstantPool cpool) { + Formatter buf = new Formatter(genHTML); + buf.link(genConstantPoolHref(cpool), genConstantPoolTitle(cpool)); + return buf.toString(); + } + + public String genHTML(Method method) { + try { + final Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(genMethodTitle(method)); + + buf.h3("Holder Class"); + buf.append(genKlassLink((InstanceKlass) method.getMethodHolder())); + + NMethod nmethod = method.getNativeMethod(); + if (nmethod != null) { + buf.h3("Compiled Code"); + buf.append(genNMethodLink(nmethod)); + } + + boolean hasThrows = method.hasCheckedExceptions(); + ConstantPool cpool = ((InstanceKlass) method.getMethodHolder()).getConstants(); + if (hasThrows) { + buf.h3("Checked Exception(s)"); + CheckedExceptionElement[] exceptions = method.getCheckedExceptions(); + buf.beginTag("ul"); + for (int exp = 0; exp < exceptions.length; exp++) { + short cpIndex = (short) exceptions[exp].getClassCPIndex(); + Oop obj = cpool.getObjAt(cpIndex); + if (obj instanceof Symbol) { + buf.li(((Symbol)obj).asString().replace('/', '.')); + } else { + buf.li(genKlassLink((InstanceKlass)obj)); + } + } + buf.endTag("ul"); + } + + if (method.isNative() || method.isAbstract()) { + buf.genHTMLEpilogue(); + return buf.toString(); + } + + buf.h3("Bytecode"); + BytecodeDisassembler disasm = createBytecodeDisassembler(method); + final boolean hasLineNumbers = method.hasLineNumberTable(); + disasm.decode(new BytecodeVisitor() { + private Method method; + public void prologue(Method m) { + method = m; + buf.beginTable(0); + buf.beginTag("tr"); + if (hasLineNumbers) { + buf.headerCell("line"); + } + buf.headerCell("bci" + spaces); + buf.headerCell("bytecode"); + buf.endTag("tr"); + } + + public void visit(Bytecode instr) { + int curBci = instr.bci(); + buf.beginTag("tr"); + if (hasLineNumbers) { + int lineNumber = method.getLineNumberFromBCI(curBci); + buf.cell(Integer.toString(lineNumber) + spaces); + } + buf.cell(Integer.toString(curBci) + spaces); + + buf.beginTag("td"); + String instrStr = escapeHTMLSpecialChars(instr.toString()); + + if (instr instanceof BytecodeNew) { + BytecodeNew newBytecode = (BytecodeNew) instr; + InstanceKlass klass = newBytecode.getNewKlass(); + if (klass != null) { + buf.link(genKlassHref(klass), instrStr); + } else { + buf.append(instrStr); + } + } else if(instr instanceof BytecodeInvoke) { + BytecodeInvoke invokeBytecode = (BytecodeInvoke) instr; + Method m = invokeBytecode.getInvokedMethod(); + if (m != null) { + buf.link(genMethodHref(m), instrStr); + buf.append(" of "); + InstanceKlass klass = (InstanceKlass) m.getMethodHolder(); + buf.link(genKlassHref(klass), genKlassTitle(klass)); + } else { + buf.append(instrStr); + } + } else if (instr instanceof BytecodeGetPut) { + BytecodeGetPut getPut = (BytecodeGetPut) instr; + sun.jvm.hotspot.oops.Field f = getPut.getField(); + buf.append(instrStr); + if (f != null) { + InstanceKlass klass = f.getFieldHolder(); + buf.append(" of "); + buf.link(genKlassHref(klass), genKlassTitle(klass)); + } + } else if (instr instanceof BytecodeLoadConstant) { + BytecodeLoadConstant ldc = (BytecodeLoadConstant) instr; + if (ldc.isKlassConstant()) { + Oop oop = ldc.getKlass(); + if (oop instanceof Klass) { + buf.append(""); + buf.append(instrStr); + buf.append(""); + } else { + // unresolved klass literal + buf.append(instrStr); + } + } else { + // not a klass literal + buf.append(instrStr); + } + } else { + buf.append(instrStr); + } + buf.endTag("td"); + buf.endTag("tr"); + } + + public void epilogue() { + buf.endTable(); + } + }); + + // display exception table for this method + TypeArray exceptionTable = method.getExceptionTable(); + // exception table is 4 tuple array of shorts + int numEntries = (int)exceptionTable.getLength() / 4; + if (numEntries != 0) { + buf.h4("Exception Table"); + buf.beginTable(1); + buf.beginTag("tr"); + buf.headerCell("start bci"); + buf.headerCell("end bci"); + buf.headerCell("handler bci"); + buf.headerCell("catch type"); + buf.endTag("tr"); + + for (int e = 0; e < numEntries; e += 4) { + buf.beginTag("tr"); + buf.cell(Integer.toString(exceptionTable.getIntAt(e))); + buf.cell(Integer.toString(exceptionTable.getIntAt(e + 1))); + buf.cell(Integer.toString(exceptionTable.getIntAt(e + 2))); + short cpIndex = (short) exceptionTable.getIntAt(e + 3); + Oop obj = cpIndex == 0? null : cpool.getObjAt(cpIndex); + if (obj == null) { + buf.cell("Any"); + } else if (obj instanceof Symbol) { + buf.cell(((Symbol)obj).asString().replace('/', '.')); + } else { + buf.cell(genKlassLink((InstanceKlass)obj)); + } + buf.endTag("tr"); + } + + buf.endTable(); + } + + // display constant pool hyperlink + buf.h3("Constant Pool"); + buf.append(genConstantPoolLink(cpool)); + buf.genHTMLEpilogue(); + return buf.toString(); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + protected Disassembler createDisassembler(long startPc, byte[] code) { + return getCPUHelper().createDisassembler(startPc, code); + } + + protected SymbolFinder createSymbolFinder() { + return new DummySymbolFinder(); + } + + // genHTML for a given address. Address may be a PC or + // methodOop or klassOop. + + public String genHTMLForAddress(String addrStr) { + return genHTML(parseAddress(addrStr)); + } + + public String genHTML(sun.jvm.hotspot.debugger.Address pc) { + CodeBlob blob = null; + + try { + blob = (CodeBlob)VM.getVM().getCodeCache().findBlobUnsafe(pc); + } catch (Exception exp) { + // ignore + } + + if (blob != null) { + if (blob instanceof NMethod) { + return genHTML((NMethod)blob); + } else { + // may be interpreter code. + Interpreter interp = VM.getVM().getInterpreter(); + if (interp.contains(pc)) { + InterpreterCodelet codelet = interp.getCodeletContaining(pc); + return genHTML(codelet); + } + return genHTML(blob); + } + } else if (VM.getVM().getCodeCache().contains(pc)) { + return "Unknown location in the CodeCache: " + pc; + } + + // did not find nmethod. + // try methodOop, klassOop and constantPoolOop. + try { + Oop obj = getOopAtAddress(pc); + if (obj != null) { + if (obj instanceof Method) { + return genHTML((Method) obj); + } else if (obj instanceof InstanceKlass) { + return genHTML((InstanceKlass) obj); + } else if (obj instanceof ConstantPool) { + return genHTML((ConstantPool) obj); + } + } + } catch (Exception exp) { + // ignore + } + + // didn't find any. do raw disassembly. + return genHTMLForRawDisassembly(pc, null); + } + + protected byte[] readBuffer(sun.jvm.hotspot.debugger.Address addr, int size) { + byte[] buf = new byte[size]; + for (int b = 0; b < size; b++) { + buf[b] = (byte) addr.getJByteAt(b); + } + return buf; + } + + public String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address startPc, int size) { + try { + return genHTMLForRawDisassembly(startPc, null, readBuffer(startPc, size)); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + protected String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address startPc, + String prevPCs) { + try { + return genHTMLForRawDisassembly(startPc, prevPCs, readBuffer(startPc, NATIVE_CODE_SIZE)); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + protected String genPCHref(long targetPc) { + return genBaseHref() + "pc=0x" + Long.toHexString(targetPc); + } + + protected String genMultPCHref(String pcs) { + StringBuffer buf = new StringBuffer(genBaseHref()); + buf.append("pc_multiple="); + buf.append(pcs); + return buf.toString(); + } + + protected String genPCHref(long currentPc, sun.jvm.hotspot.asm.Address addr) { + String href = null; + if (addr instanceof PCRelativeAddress) { + PCRelativeAddress pcRelAddr = (PCRelativeAddress) addr; + href = genPCHref(currentPc + pcRelAddr.getDisplacement()); + } else if(addr instanceof DirectAddress) { + href = genPCHref(((DirectAddress) addr).getValue()); + } + + return href; + } + + class RawCodeVisitor implements InstructionVisitor { + private int instrSize = 0; + private Formatter buf; + private SymbolFinder symFinder = createSymbolFinder(); + + RawCodeVisitor(Formatter buf) { + this.buf = buf; + } + + public int getInstructionSize() { + return instrSize; + } + + public void prologue() { + } + + public void visit(long currentPc, Instruction instr) { + String href = null; + if (instr.isCall()) { + CallInstruction call = (CallInstruction) instr; + sun.jvm.hotspot.asm.Address addr = call.getBranchDestination(); + href = genPCHref(currentPc, addr); + } + + instrSize += instr.getSize(); + buf.append("0x"); + buf.append(Long.toHexString(currentPc)); + buf.append(':'); + buf.append(tab); + + if (href != null) { + buf.link(href, instr.asString(currentPc, symFinder)); + } else { + buf.append(instr.asString(currentPc, symFinder)); + } + buf.br(); + } + + public void epilogue() { + } + }; + + protected String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address addr, + String prevPCs, + byte[] code) { + try { + long startPc = addressToLong(addr); + Disassembler disasm = createDisassembler(startPc, code); + final Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue("Disassembly @0x" + Long.toHexString(startPc)); + + if (prevPCs != null && genHTML) { + buf.beginTag("p"); + buf.link(genMultPCHref(prevPCs), "show previous code .."); + buf.endTag("p"); + } + + + buf.h3("Code"); + RawCodeVisitor visitor = new RawCodeVisitor(buf); + disasm.decode(visitor); + + if (genHTML) buf.beginTag("p"); + Formatter tmpBuf = new Formatter(genHTML); + tmpBuf.append("0x"); + tmpBuf.append(Long.toHexString(startPc + visitor.getInstructionSize()).toString()); + tmpBuf.append(",0x"); + tmpBuf.append(Long.toHexString(startPc)); + if (prevPCs != null) { + tmpBuf.append(','); + tmpBuf.append(prevPCs); + } + if (genHTML) { + buf.link(genMultPCHref(tmpBuf.toString()), "show more code .."); + buf.endTag("p"); + } + + buf.genHTMLEpilogue(); + return buf.toString(); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + protected String genSafepointInfo(NMethod nm, PCDesc pcDesc) { + ScopeDesc sd = nm.getScopeDescAt(pcDesc.getRealPC(nm)); + Formatter buf = new Formatter(genHTML); + Formatter tabs = new Formatter(genHTML); + + buf.beginTag("pre"); + genScope(buf, tabs, sd); + buf.endTag("pre"); + buf.append(genOopMapInfo(nm, pcDesc)); + + return buf.toString(); + } + + protected void genScope(Formatter buf, Formatter tabs, ScopeDesc sd) { + if (sd == null) { + return; + } + + genScope(buf, tabs, sd.sender()); + + buf.append(tabs); + Method m = sd.getMethod(); + buf.append(genMethodAndKlassLink(m)); + int bci = sd.getBCI(); + buf.append(" @ bci = "); + buf.append(Integer.toString(bci)); + + int line = m.getLineNumberFromBCI(bci); + if (line != -1) { + buf.append(", line = "); + buf.append(Integer.toString(line)); + } + + List locals = sd.getLocals(); + if (locals != null) { + buf.br(); + buf.append(tabs); + buf.append(genHTMLForLocals(sd, locals)); + } + + List expressions = sd.getExpressions(); + if (expressions != null) { + buf.br(); + buf.append(tabs); + buf.append(genHTMLForExpressions(sd, expressions)); + } + + List monitors = sd.getMonitors(); + if (monitors != null) { + buf.br(); + buf.append(tabs); + buf.append(genHTMLForMonitors(sd, monitors)); + } + + tabs.append(tab); + buf.br(); + } + + protected String genHTMLForOopMap(OopMap map) { + final int stack0 = VMRegImpl.getStack0().getValue(); + Formatter buf = new Formatter(genHTML); + + final class OopMapValueIterator { + final Formatter iterate(OopMapStream oms, String type, boolean printContentReg) { + Formatter tmpBuf = new Formatter(genHTML); + boolean found = false; + tmpBuf.beginTag("tr"); + tmpBuf.beginTag("td"); + tmpBuf.append(type); + tmpBuf.endTag("td"); + tmpBuf.endTag("tr"); + for (; ! oms.isDone(); oms.next()) { + OopMapValue omv = oms.getCurrent(); + if (omv == null) { + continue; + } + found = true; + VMReg vmReg = omv.getReg(); + int reg = vmReg.getValue(); + if (reg < stack0) { + tmpBuf.append(VMRegImpl.getRegisterName(vmReg.getValue())); + } else { + tmpBuf.append('['); + tmpBuf.append(Integer.toString((reg - stack0) * 4)); + tmpBuf.append(']'); + } + if (printContentReg) { + tmpBuf.append(" = "); + VMReg vmContentReg = omv.getContentReg(); + int contentReg = vmContentReg.getValue(); + tmpBuf.append(VMRegImpl.getRegisterName(vmContentReg.getValue())); + } + tmpBuf.append(spaces); + } + tmpBuf.endTag("td"); + tmpBuf.endTag("tr"); + return found ? tmpBuf : new Formatter(genHTML); + } + } + + buf.beginTable(0); + + OopMapValueIterator omvIterator = new OopMapValueIterator(); + OopMapStream oms = new OopMapStream(map, OopMapValue.OopTypes.OOP_VALUE); + buf.append(omvIterator.iterate(oms, "Oop:", false)); + + oms = new OopMapStream(map, OopMapValue.OopTypes.VALUE_VALUE); + buf.append(omvIterator.iterate(oms, "Value:", false)); + + oms = new OopMapStream(map, OopMapValue.OopTypes.DEAD_VALUE); + buf.append(omvIterator.iterate(oms, "Dead:", false)); + + oms = new OopMapStream(map, OopMapValue.OopTypes.CALLEE_SAVED_VALUE); + buf.append(omvIterator.iterate(oms, "Callee saved:", true)); + + oms = new OopMapStream(map, OopMapValue.OopTypes.DERIVED_OOP_VALUE); + buf.append(omvIterator.iterate(oms, "Derived oop:", true)); + + buf.endTag("table"); + return buf.toString(); + } + + + protected String genOopMapInfo(NMethod nmethod, PCDesc pcDesc) { + OopMapSet mapSet = nmethod.getOopMaps(); + int pcOffset = pcDesc.getPCOffset(); + OopMap map = mapSet.findMapAtOffset(pcOffset, VM.getVM().isDebugging()); + if (map == null) { + throw new IllegalArgumentException("no oopmap at safepoint!"); + } + + return genOopMapInfo(map); + } + + protected String genOopMapInfo(OopMap map) { + Formatter buf = new Formatter(genHTML); + buf.beginTag("pre"); + buf.append("OopMap: "); + buf.append(genHTMLForOopMap(map)); + buf.endTag("pre"); + + return buf.toString(); + } + + protected String locationAsString(Location loc) { + Formatter buf = new Formatter(genHTML); + if (loc.isIllegal()) { + buf.append("illegal"); + } else { + Location.Where w = loc.getWhere(); + Location.Type type = loc.getType(); + + if (w == Location.Where.ON_STACK) { + buf.append("stack[" + loc.getStackOffset() + "]"); + } else if (w == Location.Where.IN_REGISTER) { + boolean isFloat = (type == Location.Type.FLOAT_IN_DBL || + type == Location.Type.DBL); + int regNum = loc.getRegisterNumber(); + VMReg vmReg = new VMReg(regNum); + buf.append(VMRegImpl.getRegisterName(vmReg.getValue())); + } + + buf.append(", "); + if (type == Location.Type.NORMAL) { + buf.append("normal"); + } else if (type == Location.Type.OOP) { + buf.append("oop"); + } else if (type == Location.Type.INT_IN_LONG) { + buf.append("int"); + } else if (type == Location.Type.LNG) { + buf.append("long"); + } else if (type == Location.Type.FLOAT_IN_DBL) { + buf.append("float"); + } else if (type == Location.Type.DBL) { + buf.append("double"); + } else if (type == Location.Type.ADDR) { + buf.append("address"); + } else if (type == Location.Type.INVALID) { + buf.append("invalid"); + } + } + return buf.toString(); + } + + private String scopeValueAsString(ScopeValue sv) { + Formatter buf = new Formatter(genHTML); + if (sv.isConstantInt()) { + buf.append("int "); + ConstantIntValue intValue = (ConstantIntValue) sv; + buf.append(Integer.toString(intValue.getValue())); + } else if (sv.isConstantLong()) { + buf.append("long "); + ConstantLongValue longValue = (ConstantLongValue) sv; + buf.append(Long.toString(longValue.getValue())); + buf.append("L"); + } else if (sv.isConstantDouble()) { + buf.append("double "); + ConstantDoubleValue dblValue = (ConstantDoubleValue) sv; + buf.append(Double.toString(dblValue.getValue())); + buf.append("D"); + } else if (sv.isConstantOop()) { + buf.append("oop "); + ConstantOopReadValue oopValue = (ConstantOopReadValue) sv; + OopHandle oopHandle = oopValue.getValue(); + if (oopHandle != null) { + buf.append(oopHandle.toString()); + } else { + buf.append("null"); + } + } else if (sv.isLocation()) { + LocationValue lvalue = (LocationValue) sv; + Location loc = lvalue.getLocation(); + if (loc != null) { + buf.append(locationAsString(loc)); + } else { + buf.append("null"); + } + } + return buf.toString(); + } + + protected String genHTMLForScopeValues(ScopeDesc sd, boolean locals, List values) { + int length = values.size(); + Formatter buf = new Formatter(genHTML); + buf.append(locals? "locals " : "expressions "); + for (int i = 0; i < length; i++) { + ScopeValue sv = (ScopeValue) values.get(i); + if (sv == null) { + continue; + } + buf.append('('); + if (locals) { + Symbol name = sd.getMethod().getLocalVariableName(sd.getBCI(), i); + if (name != null) { + buf.append("'"); + buf.append(name.asString()); + buf.append('\''); + } else { + buf.append("["); + buf.append(Integer.toString(i)); + buf.append(']'); + } + } else { + buf.append("["); + buf.append(Integer.toString(i)); + buf.append(']'); + } + + buf.append(", "); + buf.append(scopeValueAsString(sv)); + buf.append(") "); + } + + return buf.toString(); + } + + protected String genHTMLForLocals(ScopeDesc sd, List locals) { + return genHTMLForScopeValues(sd, true, locals); + } + + protected String genHTMLForExpressions(ScopeDesc sd, List expressions) { + return genHTMLForScopeValues(sd, false, expressions); + } + + protected String genHTMLForMonitors(ScopeDesc sd, List monitors) { + int length = monitors.size(); + Formatter buf = new Formatter(genHTML); + buf.append("monitors "); + for (int i = 0; i < length; i++) { + MonitorValue mv = (MonitorValue) monitors.get(i); + if (mv == null) { + continue; + } + buf.append("(owner = "); + ScopeValue owner = mv.owner(); + if (owner != null) { + buf.append(scopeValueAsString(owner)); + } else { + buf.append("null"); + } + buf.append(", lock = "); + + Location loc = mv.basicLock(); + if (loc != null) { + buf.append(locationAsString(loc)); + } else { + buf.append("null"); + } + buf.append(") "); + } + return buf.toString(); + } + + public String genHTML(final NMethod nmethod) { + try { + final Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(genNMethodTitle(nmethod)); + buf.h3("Method"); + buf.append(genMethodAndKlassLink(nmethod.getMethod())); + + buf.h3("Compiled Code"); + sun.jvm.hotspot.debugger.Address codeBegin = nmethod.codeBegin(); + sun.jvm.hotspot.debugger.Address codeEnd = nmethod.codeEnd(); + final int codeSize = (int)codeEnd.minus(codeBegin); + final long startPc = addressToLong(codeBegin); + final byte[] code = new byte[codeSize]; + for (int i=0; i < code.length; i++) + code[i] = codeBegin.getJByteAt(i); + + final long verifiedEntryPoint = addressToLong(nmethod.getVerifiedEntryPoint()); + final long entryPoint = addressToLong(nmethod.getEntryPoint()); + final Map safepoints = nmethod.getSafepoints(); + + final SymbolFinder symFinder = createSymbolFinder(); + final Disassembler disasm = createDisassembler(startPc, code); + class NMethodVisitor implements InstructionVisitor { + boolean prevWasCall; + public void prologue() { + prevWasCall = false; + } + + public void visit(long currentPc, Instruction instr) { + String href = null; + if (instr.isCall()) { + CallInstruction call = (CallInstruction) instr; + sun.jvm.hotspot.asm.Address addr = call.getBranchDestination(); + href = genPCHref(currentPc, addr); + } + + if (currentPc == verifiedEntryPoint) { + buf.bold("Verified Entry Point"); buf.br(); + } + if (currentPc == entryPoint) { + buf.bold(">Entry Point"); buf.br(); + } + + PCDesc pcDesc = (PCDesc) safepoints.get(longToAddress(currentPc)); + + boolean isSafepoint = (pcDesc != null); + if (isSafepoint && prevWasCall) { + buf.append(genSafepointInfo(nmethod, pcDesc)); + } + + buf.append("0x"); + buf.append(Long.toHexString(currentPc)); + buf.append(':'); + buf.append(tab); + + if (href != null) { + buf.link(href, instr.asString(currentPc, symFinder)); + } else { + buf.append(instr.asString(currentPc, symFinder)); + } + + if (isSafepoint && !prevWasCall) { + buf.append(genSafepointInfo(nmethod, pcDesc)); + } + + buf.br(); + prevWasCall = instr.isCall(); + } + + public void epilogue() { + } + }; + + disasm.decode(new NMethodVisitor()); + + sun.jvm.hotspot.debugger.Address stubBegin = nmethod.stubBegin(); + if (stubBegin != null) { + sun.jvm.hotspot.debugger.Address stubEnd = nmethod.stubEnd(); + buf.h3("Stub"); + long stubStartPc = addressToLong(stubBegin); + long stubEndPc = addressToLong(stubEnd); + int range = (int) (stubEndPc - stubStartPc); + byte[] stubCode = readBuffer(stubBegin, range); + Disassembler disasm2 = createDisassembler(stubStartPc, stubCode); + disasm2.decode(new NMethodVisitor()); + } + buf.genHTMLEpilogue(); + return buf.toString(); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + public String genHTML(final CodeBlob blob) { + try { + final Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(genCodeBlobTitle(blob)); + buf.h3("CodeBlob"); + + buf.h3("Compiled Code"); + final sun.jvm.hotspot.debugger.Address codeBegin = blob.instructionsBegin(); + final int codeSize = blob.getInstructionsSize(); + final long startPc = addressToLong(codeBegin); + final byte[] code = new byte[codeSize]; + for (int i=0; i < code.length; i++) + code[i] = codeBegin.getJByteAt(i); + + final SymbolFinder symFinder = createSymbolFinder(); + final Disassembler disasm = createDisassembler(startPc, code); + class CodeBlobVisitor implements InstructionVisitor { + OopMapSet maps; + OopMap curMap; + int curMapIndex; + long curMapOffset; + public void prologue() { + maps = blob.getOopMaps(); + if (maps != null && (maps.getSize() > 0)) { + curMap = maps.getMapAt(0); + if (curMap != null) { + curMapOffset = curMap.getOffset(); + } + } + } + + public void visit(long currentPc, Instruction instr) { + String href = null; + if (instr.isCall()) { + CallInstruction call = (CallInstruction) instr; + sun.jvm.hotspot.asm.Address addr = call.getBranchDestination(); + href = genPCHref(currentPc, addr); + } + + buf.append("0x"); + buf.append(Long.toHexString(currentPc)); + buf.append(':'); + buf.append(tab); + + if (href != null) { + buf.link(href, instr.asString(currentPc, symFinder)); + } else { + buf.append(instr.asString(currentPc, symFinder)); + } + buf.br(); + + // See whether we have an oop map at this PC + if (curMap != null) { + long curOffset = currentPc - startPc; + if (curOffset == curMapOffset) { + buf.append(genOopMapInfo(curMap)); + if (++curMapIndex >= maps.getSize()) { + curMap = null; + } else { + curMap = maps.getMapAt(curMapIndex); + if (curMap != null) { + curMapOffset = curMap.getOffset(); + } + } + } + } + } + + public void epilogue() { + } + }; + + disasm.decode(new CodeBlobVisitor()); + + buf.genHTMLEpilogue(); + return buf.toString(); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + protected String genInterpreterCodeletTitle(InterpreterCodelet codelet) { + Formatter buf = new Formatter(genHTML); + buf.append("Interpreter codelet ["); + buf.append(codelet.codeBegin().toString()); + buf.append(','); + buf.append(codelet.codeEnd().toString()); + buf.append(") - "); + buf.append(codelet.getDescription()); + return buf.toString(); + } + + protected String genInterpreterCodeletLinkPageHref(StubQueue stubq) { + return genBaseHref() + "interp_codelets"; + } + + public String genInterpreterCodeletLinksPage() { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue("Interpreter Codelets"); + buf.beginTag("ul"); + + Interpreter interp = VM.getVM().getInterpreter(); + StubQueue code = interp.getCode(); + InterpreterCodelet stub = (InterpreterCodelet) code.getFirst(); + while (stub != null) { + buf.beginTag("li"); + sun.jvm.hotspot.debugger.Address addr = stub.codeBegin(); + buf.link(genPCHref(addressToLong(addr)), stub.getDescription() + " @" + addr); + buf.endTag("li"); + stub = (InterpreterCodelet) code.getNext(stub); + } + + buf.endTag("ul"); + buf.genHTMLEpilogue(); + return buf.toString(); + } + + public String genHTML(InterpreterCodelet codelet) { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(genInterpreterCodeletTitle(codelet)); + Interpreter interp = VM.getVM().getInterpreter(); + StubQueue stubq = interp.getCode(); + + if (genHTML) { + buf.beginTag("h3"); + buf.link(genInterpreterCodeletLinkPageHref(stubq), "View links for all codelets"); + buf.endTag("h3"); + buf.br(); + } + + Stub prev = stubq.getPrev(codelet); + if (prev != null) { + if (genHTML) { + buf.beginTag("h3"); + buf.link(genPCHref(addressToLong(prev.codeBegin())), "View Previous Codelet"); + buf.endTag("h3"); + buf.br(); + } else { + buf.h3("Previous Codelet = 0x" + Long.toHexString(addressToLong(prev.codeBegin()))); + } + } + + buf.h3("Code"); + long stubStartPc = addressToLong(codelet.codeBegin()); + long stubEndPc = addressToLong(codelet.codeEnd()); + int range = (int) (stubEndPc - stubStartPc); + byte[] stubCode = readBuffer(codelet.codeBegin(), range); + Disassembler disasm = createDisassembler(stubStartPc, stubCode); + disasm.decode(new RawCodeVisitor(buf)); + + + Stub next = stubq.getNext(codelet); + if (next != null) { + if (genHTML) { + buf.beginTag("h3"); + buf.link(genPCHref(addressToLong(next.codeBegin())), "View Next Codelet"); + buf.endTag("h3"); + } else { + buf.h3("Next Codelet = 0x" + Long.toHexString(addressToLong(next.codeBegin()))); + } + } + + buf.genHTMLEpilogue(); + return buf.toString(); + } + + protected String genDumpKlassesTitle(InstanceKlass[] klasses) { + return (klasses.length == 1) ? "Create .class for this class" + : "Create .class for all classes"; + } + + protected String genDumpKlassesHref(InstanceKlass[] klasses) { + StringBuffer buf = new StringBuffer(genBaseHref()); + buf.append("jcore_multiple="); + for (int k = 0; k < klasses.length; k++) { + buf.append(klasses[k].getHandle().toString()); + buf.append(','); + } + return buf.toString(); + } + + protected String genDumpKlassesLink(InstanceKlass[] klasses) { + if (!genHTML) return ""; + + Formatter buf = new Formatter(genHTML); + buf.link(genDumpKlassesHref(klasses), genDumpKlassesTitle(klasses)); + return buf.toString(); + } + + public String genHTMLForKlassNames(InstanceKlass[] klasses) { + try { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(); + buf.h3(genDumpKlassesLink(klasses)); + + buf.append(genHTMLListForKlassNames(klasses)); + buf.genHTMLEpilogue(); + return buf.toString(); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + protected String genHTMLListForKlassNames(InstanceKlass[] klasses) { + final Formatter buf = new Formatter(genHTML); + buf.beginTable(0); + for (int i = 0; i < klasses.length; i++) { + InstanceKlass ik = klasses[i]; + buf.beginTag("tr"); + buf.cell(genKlassLink(ik)); + buf.endTag("tr"); + } + + buf.endTable(); + return buf.toString(); + } + + public String genHTMLForMethodNames(InstanceKlass klass) { + try { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(); + buf.append(genHTMLListForMethods(klass)); + buf.genHTMLEpilogue(); + return buf.toString(); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + protected String genHTMLListForMethods(InstanceKlass klass) { + Formatter buf = new Formatter(genHTML); + ObjArray methods = klass.getMethods(); + int numMethods = (int) methods.getLength(); + if (numMethods != 0) { + buf.h3("Methods"); + buf.beginTag("ul"); + for (int m = 0; m < numMethods; m++) { + Method mtd = (Method) methods.getObjAt(m); + buf.li(genMethodLink(mtd) + ";"); + } + buf.endTag("ul"); + } + return buf.toString(); + } + + protected String genHTMLListForInterfaces(InstanceKlass klass) { + try { + Formatter buf = new Formatter(genHTML); + ObjArray interfaces = klass.getLocalInterfaces(); + int numInterfaces = (int) interfaces.getLength(); + if (numInterfaces != 0) { + buf.h3("Interfaces"); + buf.beginTag("ul"); + for (int i = 0; i < numInterfaces; i++) { + InstanceKlass inf = (InstanceKlass) interfaces.getObjAt(i); + buf.li(genKlassLink(inf)); + } + buf.endTag("ul"); + } + return buf.toString(); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + protected String genFieldModifierString(AccessFlags acc) { + Formatter buf = new Formatter(genHTML); + if (acc.isPrivate()) { + buf.append("private "); + } else if (acc.isProtected()) { + buf.append("protected "); + } else if (acc.isPublic()) { + buf.append("public "); + } + + if (acc.isStatic()) { + buf.append("static "); + } + + if (acc.isFinal()) { + buf.append("final "); + } + if (acc.isVolatile()) { + buf.append("volatile "); + } + if (acc.isTransient()) { + buf.append("transient "); + } + + // javac generated flags + if (acc.isSynthetic()) { + buf.append("[synthetic] "); + } + return buf.toString(); + } + + public String genHTMLForFieldNames(InstanceKlass klass) { + try { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(); + buf.append(genHTMLListForFields(klass)); + buf.genHTMLEpilogue(); + return buf.toString(); + } catch (Exception exp) { + return genHTMLErrorMessage(exp); + } + } + + protected String genHTMLListForFields(InstanceKlass klass) { + Formatter buf = new Formatter(genHTML); + TypeArray fields = klass.getFields(); + int numFields = (int) fields.getLength(); + ConstantPool cp = klass.getConstants(); + if (numFields != 0) { + buf.h3("Fields"); + buf.beginList(); + for (int f = 0; f < numFields; f += InstanceKlass.NEXT_OFFSET) { + int nameIndex = fields.getShortAt(f + InstanceKlass.NAME_INDEX_OFFSET); + int sigIndex = fields.getShortAt(f + InstanceKlass.SIGNATURE_INDEX_OFFSET); + int genSigIndex = fields.getShortAt(f + InstanceKlass.GENERIC_SIGNATURE_INDEX_OFFSET); + Symbol f_name = cp.getSymbolAt(nameIndex); + Symbol f_sig = cp.getSymbolAt(sigIndex); + Symbol f_genSig = (genSigIndex != 0)? cp.getSymbolAt(genSigIndex) : null; + AccessFlags acc = new AccessFlags(fields.getShortAt(f + InstanceKlass.ACCESS_FLAGS_OFFSET)); + + buf.beginTag("li"); + buf.append(genFieldModifierString(acc)); + buf.append(' '); + Formatter sigBuf = new Formatter(genHTML); + new SignatureConverter(f_sig, sigBuf.getBuffer()).dispatchField(); + buf.append(sigBuf.toString().replace('/', '.')); + buf.append(' '); + buf.append(f_name.asString()); + buf.append(';'); + // is it generic? + if (f_genSig != null) { + buf.append(" [signature "); + buf.append(escapeHTMLSpecialChars(f_genSig.asString())); + buf.append("] "); + } + buf.endTag("li"); + } + buf.endList(); + } + return buf.toString(); + } + + protected String genKlassHierarchyHref(InstanceKlass klass) { + return genBaseHref() + "hierarchy=" + klass.getHandle(); + } + + protected String genKlassHierarchyTitle(InstanceKlass klass) { + Formatter buf = new Formatter(genHTML); + buf.append("Class Hierarchy of "); + buf.append(genKlassTitle(klass)); + return buf.toString(); + } + + protected String genKlassHierarchyLink(InstanceKlass klass) { + Formatter buf = new Formatter(genHTML); + buf.link(genKlassHierarchyHref(klass), genKlassHierarchyTitle(klass)); + return buf.toString(); + } + + protected String genHTMLListForSubKlasses(InstanceKlass klass) { + Formatter buf = new Formatter(genHTML); + Klass subklass = klass.getSubklassKlass(); + if (subklass != null) { + buf.beginList(); + while (subklass != null) { + if (subklass instanceof InstanceKlass) { + buf.li(genKlassLink((InstanceKlass)subklass)); + } + subklass = subklass.getNextSiblingKlass(); + } + buf.endList(); + } + return buf.toString(); + } + + public String genHTMLForKlassHierarchy(InstanceKlass klass) { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(genKlassHierarchyTitle(klass)); + + + buf.beginTag("pre"); + buf.append(genKlassLink(klass)); + buf.br(); + StringBuffer tabs = new StringBuffer(tab); + InstanceKlass superKlass = klass; + while ( (superKlass = (InstanceKlass) superKlass.getSuper()) != null ) { + buf.append(tabs); + buf.append(genKlassLink(superKlass)); + tabs.append(tab); + buf.br(); + } + buf.endTag("pre"); + + // generate subklass list + Klass subklass = klass.getSubklassKlass(); + if (subklass != null) { + buf.h3("Direct Subclasses"); + buf.append(genHTMLListForSubKlasses(klass)); + } + + buf.genHTMLEpilogue(); + return buf.toString(); + } + + protected String genDumpKlassHref(InstanceKlass klass) { + return genBaseHref() + "jcore=" + klass.getHandle(); + } + + protected String genDumpKlassLink(InstanceKlass klass) { + if (!genHTML) return ""; + + Formatter buf = new Formatter(genHTML); + buf.link(genDumpKlassHref(klass), "Create .class File"); + return buf.toString(); + } + + public String genHTML(InstanceKlass klass) { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(genKlassTitle(klass)); + InstanceKlass superKlass = (InstanceKlass) klass.getSuper(); + + if (genHTML) { + // super class tree and subclass list + buf.beginTag("h3"); + buf.link(genKlassHierarchyHref(klass), "View Class Hierarchy"); + buf.endTag("h3"); + } + + // jcore - create .class link + buf.h3(genDumpKlassLink(klass)); + + // super class + if (superKlass != null) { + buf.h3("Super Class"); + buf.append(genKlassLink(superKlass)); + } + + // interfaces + buf.append(genHTMLListForInterfaces(klass)); + + // fields + buf.append(genHTMLListForFields(klass)); + + // methods + buf.append(genHTMLListForMethods(klass)); + + // constant pool link + buf.h3("Constant Pool"); + buf.append(genConstantPoolLink(klass.getConstants())); + + buf.genHTMLEpilogue(); + return buf.toString(); + } + + protected sun.jvm.hotspot.debugger.Address parseAddress(String address) { + VM vm = VM.getVM(); + sun.jvm.hotspot.debugger.Address addr = vm.getDebugger().parseAddress(address); + return addr; + } + + protected long addressToLong(sun.jvm.hotspot.debugger.Address addr) { + return VM.getVM().getDebugger().getAddressValue(addr); + } + + protected sun.jvm.hotspot.debugger.Address longToAddress(long addr) { + return parseAddress("0x" + Long.toHexString(addr)); + } + + protected Oop getOopAtAddress(sun.jvm.hotspot.debugger.Address addr) { + OopHandle oopHandle = addr.addOffsetToAsOopHandle(0); + return VM.getVM().getObjectHeap().newOop(oopHandle); + } + + protected Oop getOopAtAddress(String address) { + sun.jvm.hotspot.debugger.Address addr = parseAddress(address); + return getOopAtAddress(addr); + } + + private void dumpKlass(InstanceKlass kls) throws IOException { + String klassName = kls.getName().asString(); + klassName = klassName.replace('/', File.separatorChar); + int index = klassName.lastIndexOf(File.separatorChar); + File dir = null; + if (index != -1) { + String dirName = klassName.substring(0, index); + dir = new File(DUMP_KLASS_OUTPUT_DIR, dirName); + } else { + dir = new File(DUMP_KLASS_OUTPUT_DIR); + } + + dir.mkdirs(); + File f = new File(dir, klassName.substring(klassName.lastIndexOf(File.separatorChar) + 1) + + ".class"); + f.createNewFile(); + FileOutputStream fis = new FileOutputStream(f); + ClassWriter cw = new ClassWriter(kls, fis); + cw.write(); + } + + public String genDumpKlass(InstanceKlass kls) { + try { + dumpKlass(kls); + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(genKlassTitle(kls)); + buf.append(".class created for "); + buf.append(genKlassLink(kls)); + buf.genHTMLEpilogue(); + return buf.toString(); + } catch(IOException exp) { + return genHTMLErrorMessage(exp); + } + } + + protected String genJavaStackTraceTitle(JavaThread thread) { + Formatter buf = new Formatter(genHTML); + buf.append("Java Stack Trace for "); + buf.append(thread.getThreadName()); + return buf.toString(); + } + + public String genHTMLForJavaStackTrace(JavaThread thread) { + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(genJavaStackTraceTitle(thread)); + + buf.append("Thread state = "); + buf.append(thread.getThreadState().toString()); + buf.br(); + buf.beginTag("pre"); + for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) { + Method method = vf.getMethod(); + buf.append(" - "); + buf.append(genMethodLink(method)); + buf.append(" @bci = " + vf.getBCI()); + + int lineNumber = method.getLineNumberFromBCI(vf.getBCI()); + if (lineNumber != -1) { + buf.append(", line = "); + buf.append(lineNumber); + } + + sun.jvm.hotspot.debugger.Address pc = vf.getFrame().getPC(); + if (pc != null) { + buf.append(", pc = "); + buf.link(genPCHref(addressToLong(pc)), pc.toString()); + } + + if (vf.isCompiledFrame()) { + buf.append(" (Compiled"); + } + else if (vf.isInterpretedFrame()) { + buf.append(" (Interpreted"); + } + + if (vf.mayBeImpreciseDbg()) { + buf.append("; information may be imprecise"); + } + buf.append(")"); + buf.br(); + } + + buf.endTag("pre"); + buf.genHTMLEpilogue(); + return buf.toString(); + } + + public String genHTMLForHyperlink(String href) { + if (href.startsWith("klass=")) { + href = href.substring(href.indexOf('=') + 1); + Oop obj = getOopAtAddress(href); + if (Assert.ASSERTS_ENABLED) { + Assert.that(obj instanceof InstanceKlass, "class= href with improper InstanceKlass!"); + } + return genHTML((InstanceKlass) obj); + } else if (href.startsWith("method=")) { + href = href.substring(href.indexOf('=') + 1); + Oop obj = getOopAtAddress(href); + if (Assert.ASSERTS_ENABLED) { + Assert.that(obj instanceof Method, "method= href with improper Method!"); + } + return genHTML((Method) obj); + } else if (href.startsWith("nmethod=")) { + String addr = href.substring(href.indexOf('=') + 1); + Object obj = VMObjectFactory.newObject(NMethod.class, parseAddress(addr)); + if (Assert.ASSERTS_ENABLED) { + Assert.that(obj instanceof NMethod, "nmethod= href with improper NMethod!"); + } + return genHTML((NMethod) obj); + } else if (href.startsWith("pc=")) { + String address = href.substring(href.indexOf('=') + 1); + return genHTML(parseAddress(address)); + } else if (href.startsWith("pc_multiple=")) { + int indexOfComma = href.indexOf(','); + if (indexOfComma == -1) { + String firstPC = href.substring(href.indexOf('=') + 1); + return genHTMLForRawDisassembly(parseAddress(firstPC), null); + } else { + String firstPC = href.substring(href.indexOf('=') + 1, indexOfComma); + return genHTMLForRawDisassembly(parseAddress(firstPC), href.substring(indexOfComma + 1)); + } + } else if (href.startsWith("interp_codelets")) { + return genInterpreterCodeletLinksPage(); + } else if (href.startsWith("hierarchy=")) { + href = href.substring(href.indexOf('=') + 1); + Oop obj = getOopAtAddress(href); + if (Assert.ASSERTS_ENABLED) { + Assert.that(obj instanceof InstanceKlass, "class= href with improper InstanceKlass!"); + } + return genHTMLForKlassHierarchy((InstanceKlass) obj); + } else if (href.startsWith("cpool=")) { + href = href.substring(href.indexOf('=') + 1); + Oop obj = getOopAtAddress(href); + if (Assert.ASSERTS_ENABLED) { + Assert.that(obj instanceof ConstantPool, "cpool= href with improper ConstantPool!"); + } + return genHTML((ConstantPool) obj); + } else if (href.startsWith("jcore=")) { + href = href.substring(href.indexOf('=') + 1); + Oop obj = getOopAtAddress(href); + if (Assert.ASSERTS_ENABLED) { + Assert.that(obj instanceof InstanceKlass, "jcore= href with improper InstanceKlass!"); + } + return genDumpKlass((InstanceKlass) obj); + } else if (href.startsWith("jcore_multiple=")) { + href = href.substring(href.indexOf('=') + 1); + Formatter buf = new Formatter(genHTML); + buf.genHTMLPrologue(); + StringTokenizer st = new StringTokenizer(href, ","); + while (st.hasMoreTokens()) { + Oop obj = getOopAtAddress(st.nextToken()); + if (Assert.ASSERTS_ENABLED) { + Assert.that(obj instanceof InstanceKlass, "jcore_multiple= href with improper InstanceKlass!"); + } + + InstanceKlass kls = (InstanceKlass) obj; + try { + dumpKlass(kls); + buf.append(".class created for "); + buf.append(genKlassLink(kls)); + } catch(Exception exp) { + buf.bold("can't .class for " + + genKlassTitle(kls) + + " : " + + exp.getMessage()); + } + buf.br(); + } + + buf.genHTMLEpilogue(); + return buf.toString(); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "unknown href link!"); + } + return null; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/resources/arrow.png b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/resources/arrow.png new file mode 100644 index 00000000000..8b74ca57810 Binary files /dev/null and b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/resources/arrow.png differ diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/resources/breakpoint.png b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/resources/breakpoint.png new file mode 100644 index 00000000000..52ddabdbee2 Binary files /dev/null and b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/resources/breakpoint.png differ diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/resources/triangle.png b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/resources/triangle.png new file mode 100644 index 00000000000..d97d2226c20 Binary files /dev/null and b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/resources/triangle.png differ diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/LongCellRenderer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/LongCellRenderer.java new file mode 100644 index 00000000000..924a9ee97a1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/LongCellRenderer.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.table; + +import java.awt.Component; + +import javax.swing.*; +import javax.swing.table.*; + +/** + * A renderer for long values. + */ +public class LongCellRenderer extends DefaultTableCellRenderer { + + private JFormattedTextField textField; + + // Subclassed to set the background value + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, + int row, int column) { + if (textField == null) { + textField = new JFormattedTextField(); + textField.setFont(table.getFont()); + textField.setHorizontalAlignment(JTextField.RIGHT); + } + + textField.setForeground(isSelected ? table.getSelectionForeground() : + table.getForeground()); + textField.setBackground(isSelected ? table.getSelectionBackground() : + table.getBackground()); + if (hasFocus) { + textField.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); + } else { + textField.setBorder(noFocusBorder); + } + + textField.setValue((Long)value); + + return textField; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/SortHeaderCellRenderer.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/SortHeaderCellRenderer.java new file mode 100644 index 00000000000..5cbdf959068 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/SortHeaderCellRenderer.java @@ -0,0 +1,88 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.table; + +import java.awt.Component; + +import javax.swing.*; +import javax.swing.table.*; + +/** + * A cell renderer for the JTableHeader which understands the sorted + * column state and renders arrow buttons to indicated the sorted column + * and order + */ +public class SortHeaderCellRenderer extends DefaultTableCellRenderer { + + private Icon descendingIcon; + private Icon ascendingIcon; + + private SortableTableModel model; + + public SortHeaderCellRenderer(JTableHeader header, SortableTableModel model) { + this.model = model; + + descendingIcon = getIcon("navigation/Down16.gif"); + ascendingIcon = getIcon("navigation/Up16.gif"); + + setForeground(header.getForeground()); + setBackground(header.getBackground()); + setFont(header.getFont()); + + setBorder(UIManager.getBorder("TableHeader.cellBorder")); + setHorizontalAlignment(JLabel.CENTER); + + } + + /** + * Retrieves an Image Icon from the JLF graphics repository. + */ + public ImageIcon getIcon(String name) { + String imagePath = "/toolbarButtonGraphics/" + name; + java.net.URL url = this.getClass().getResource(imagePath); + if (url != null) { + return new ImageIcon(url); + } + return null; + } + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, + int row, int column) { + setText((value == null) ? "" : value.toString()); + + Icon icon = null; + if (column == model.getColumn()) { + if (model.isAscending()) { + icon = ascendingIcon; + } else { + icon = descendingIcon; + } + } + setIcon(icon); + + return this; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/SortHeaderMouseAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/SortHeaderMouseAdapter.java new file mode 100644 index 00000000000..96765d757c8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/SortHeaderMouseAdapter.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.table; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.TableColumnModel; + +import com.sun.java.swing.ui.CommonUI; + +/** + * A mouse adapater which is attached to the header of a JTable. It listens + * for mouse clicks on a column and sorts that column. + */ +public class SortHeaderMouseAdapter extends MouseAdapter { + + private SortableTableModel model; + private JTable table; + + public SortHeaderMouseAdapter(JTable table, SortableTableModel model) { + this.model = model; + this.table = table; + } + + public void mouseClicked(MouseEvent evt) { + // XXX Benchmark sort performance + //long start = System.currentTimeMillis(); + CommonUI.setWaitCursor(SwingUtilities.getRoot(table)); + + TableColumnModel columnModel = table.getColumnModel(); + int viewColumn = columnModel.getColumnIndexAtX(evt.getX()); + int column = table.convertColumnIndexToModel(viewColumn); + if (evt.getClickCount() == 1 && column != -1) { + // Reverse the sorting direction. + model.sortByColumn(column, !model.isAscending()); + } + + // XXX Benchmark performance + // System.out.println("Sort time: " + + // (System.currentTimeMillis() - start)); + CommonUI.setDefaultCursor(SwingUtilities.getRoot(table)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/SortableTableModel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/SortableTableModel.java new file mode 100644 index 00000000000..b8299093b24 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/SortableTableModel.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.table; + +import java.util.Collections; +import java.util.List; + +import javax.swing.event.TableModelEvent; +import javax.swing.table.AbstractTableModel; + +/** + * A table model which stores its rows as a list. The elements + * of the list may be sortable by column. The TableModelComparator + * must be set for sorting to be enabled. + */ +public abstract class SortableTableModel extends AbstractTableModel { + + private TableModelComparator comparator; + + /** + * All the rows are stored as a List. + */ + protected java.util.List elements; + + /** + * This comparator must be set. + */ + public void setComparator(TableModelComparator comparator) { + this.comparator = comparator; + } + + public void sortByColumn(int column, boolean ascending) { + comparator.addColumn(column); + comparator.setAscending(ascending); + + Collections.sort(elements, comparator); + + fireTableChanged(new TableModelEvent(this)); + } + + public boolean isAscending() { + return comparator.isAscending(); + } + + public int getColumn() { + return comparator.getColumn(); + } + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/TableModelComparator.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/TableModelComparator.java new file mode 100644 index 00000000000..ae69a450b79 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/table/TableModelComparator.java @@ -0,0 +1,126 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.table; + +import java.util.*; + +import javax.swing.table.TableModel; +import javax.swing.event.TableModelEvent; + +/** + * A comparator which compares rows in a table model + */ +public abstract class TableModelComparator implements Comparator { + + private boolean ascending; + protected TableModel model; + + private int[] columns; + + public TableModelComparator(TableModel model) { + this.model = model; + + // XXX - Should actually listen for column changes and resize + columns = new int[model.getColumnCount()]; + columns[0] = -1; + } + + /** + * Add the column to the sort criteria + */ + public void addColumn(int column) { + // Shift columns in the array + int[] tempArray = new int[model.getColumnCount()]; + System.arraycopy(columns, 1, tempArray, 0, columns.length - 1); + + columns = tempArray; + columns[0] = column; + } + + /** + * Get the last column that was sorted + */ + public int getColumn() { + return columns[0]; + } + + public void setAscending(boolean ascending) { + this.ascending = ascending; + } + + public boolean isAscending() { + return ascending; + } + + /** + * Implementation of the comparator method. A comparison is + * made for rows. + */ + public int compare(Object row1, Object row2) { + for (int i = 0; i < columns.length; i++) { + + Object o1 = getValueForColumn(row1, columns[i]); + Object o2 = getValueForColumn(row2, columns[i]); + + // If both values are null, return 0. + if (o1 == null && o2 == null) { + return 0; + } else if (o1 == null) { // Define null less than everything. + return -1; + } else if (o2 == null) { + return 1; + } + + int result = 0; + + if (o1 instanceof Comparable) { + Comparable c1 = (Comparable)o1; + Comparable c2 = (Comparable)o2; + + result = c1.compareTo(c2); + } + + // XXX Should have some sort of provision for determininte + // if there is another way of comparing the objects. + // Perhaps we should add the requirement that all table + // values be Compabable. + + if (result != 0) { + return ascending ? result : -result; + } + } + return 0; + } + + /** + * Returns the value for the comparing object for the + * column. + * + * @param obj Row object that was passed into Comparator. + * @param column the column to retrieve + */ + public abstract Object getValueForColumn(Object obj, int column); + +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/BadAddressTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/BadAddressTreeNodeAdapter.java new file mode 100644 index 00000000000..4ec21eecbd7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/BadAddressTreeNodeAdapter.java @@ -0,0 +1,92 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import java.io.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; + +/** Simple wrapper for displaying bad addresses in the Inspector */ + +public class BadAddressTreeNodeAdapter extends FieldTreeNodeAdapter { + private boolean usingAddress; + private Address addr; + private long addrValue; + + public BadAddressTreeNodeAdapter(Address addr, FieldIdentifier id) { + this(addr, id, false); + } + + /** The address may be null (for address fields of structures which + are null); the FieldIdentifier may also be null (for the root + node). */ + public BadAddressTreeNodeAdapter(Address addr, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.addr = addr; + usingAddress = true; + } + + public BadAddressTreeNodeAdapter(long addr, FieldIdentifier id) { + this(addr, id, false); + } + + /** He FieldIdentifier may be null (for the root node). */ + public BadAddressTreeNodeAdapter(long addrValue, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.addrValue = addrValue; + usingAddress = false; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + throw new RuntimeException("Should not call this"); + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + throw new RuntimeException("Should not call this"); + } + + public String getValue() { + // FIXME: should have this better factored to not have to replicate this code + String addrString = null; + if (usingAddress) { + if (addr == null) { + addrString = "0x0"; + } else { + addrString = addr.toString(); + } + } else { + addrString = "0x" + Long.toHexString(addrValue); + } + return "** BAD ADDRESS " + addrString + " **"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/BadOopTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/BadOopTreeNodeAdapter.java new file mode 100644 index 00000000000..a275e8da6da --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/BadOopTreeNodeAdapter.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import java.io.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; + +/** Simple wrapper for displaying bad oops in the Inspector */ + +public class BadOopTreeNodeAdapter extends FieldTreeNodeAdapter { + private OopHandle oop; + + public BadOopTreeNodeAdapter(OopHandle oop, FieldIdentifier id) { + this(oop, id, false); + } + + /** The oop may be null (for oop fields of oops which are null); the + FieldIdentifier may also be null (for the root node). */ + public BadOopTreeNodeAdapter(OopHandle oop, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.oop = oop; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + throw new RuntimeException("Should not call this"); + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + throw new RuntimeException("Should not call this"); + } + + public String getValue() { + return "** BAD OOP " + oop + " **"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/BooleanTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/BooleanTreeNodeAdapter.java new file mode 100644 index 00000000000..19af8e2d790 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/BooleanTreeNodeAdapter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import sun.jvm.hotspot.oops.*; + +/** Encapsulates a boolean value in a tree handled by SimpleTreeModel */ + +public class BooleanTreeNodeAdapter extends FieldTreeNodeAdapter { + private boolean val; + + public BooleanTreeNodeAdapter(boolean val, FieldIdentifier id) { + this(val, id, false); + } + + public BooleanTreeNodeAdapter(boolean val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + return (val ? "true" : "false"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/CStringTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/CStringTreeNodeAdapter.java new file mode 100644 index 00000000000..91b531ed7d0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/CStringTreeNodeAdapter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import sun.jvm.hotspot.oops.*; + +/** Encapsulates a char value in a tree handled by SimpleTreeModel */ + +public class CStringTreeNodeAdapter extends FieldTreeNodeAdapter { + private String val; + + public CStringTreeNodeAdapter(String val, FieldIdentifier id) { + this(val, id, false); + } + + public CStringTreeNodeAdapter(String val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + return "\"" + val + "\""; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/CTypeTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/CTypeTreeNodeAdapter.java new file mode 100644 index 00000000000..989a05030f2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/CTypeTreeNodeAdapter.java @@ -0,0 +1,232 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import java.util.*; +import sun.jvm.hotspot.oops.FieldIdentifier; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.UnknownOopException; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.CStringUtilities; + +/** Encapsulates an arbitrary type value in a tree handled by SimpleTreeModel */ + +public class CTypeTreeNodeAdapter extends FieldTreeNodeAdapter { + final private Address addr; + final private Type type; + private CTypeFieldIdentifier[] fields = null; + + private void collectFields(Type type, ArrayList list, boolean statics, boolean recurse) { + Type supertype = type.getSuperclass(); + if (supertype != null && recurse) { + collectFields(supertype, list, statics, recurse); + } + Iterator i = type.getFields(); + while (i.hasNext()) { + Field f = (Field) i.next(); + if (f.isStatic() == statics) { + list.add(new CTypeFieldIdentifier(type, f)); + } + } + } + + + private CTypeFieldIdentifier[] getFields() { + if (fields == null) { + ArrayList f = new ArrayList(); + collectFields(type, f, false, true); + fields = (CTypeFieldIdentifier[]) f.toArray(new CTypeFieldIdentifier[0]); + } + return fields; + } + + static class CTypeFieldIdentifier extends FieldIdentifier { + final private Field field; + final private Type holder; + + CTypeFieldIdentifier(Type t, Field f) { + holder = t; + field = f; + } + + public Field getField() { + return field; + } + + public String getName() { + return field.getType().getName() + " " + holder.getName() + "::" + field.getName(); + } + } + + + public CTypeTreeNodeAdapter(Address a, Type t, FieldIdentifier id) { + this(a, t, id, false); + } + + public CTypeTreeNodeAdapter(Address a, Type t, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + type = t; + addr = a; + } + + public CTypeTreeNodeAdapter(Type t) { + super(null, false); + type = t; + addr = null; + ArrayList statics = new ArrayList(); + collectFields(type, statics, true, false); + fields = (CTypeFieldIdentifier[])statics.toArray(new CTypeFieldIdentifier[0]); + } + + public CTypeTreeNodeAdapter(Iterator types) { + super(null, false); + addr = null; + type = null; + ArrayList statics = new ArrayList(); + while (types.hasNext()) { + collectFields((Type)types.next(), statics, true, false); + } + fields = (CTypeFieldIdentifier[])statics.toArray(new CTypeFieldIdentifier[0]); + } + + public int getChildCount() { + return getFields().length; + } + + public SimpleTreeNode getChild(int index) { + CTypeFieldIdentifier cf = getFields()[index]; + Field f = cf.getField(); + Type t = f.getType(); + try { + if (t.isOopType()) { + OopHandle handle; + if (f.isStatic()) { + handle = f.getOopHandle(); + } else { + handle = f.getOopHandle(addr); + } + try { + Oop oop = VM.getVM().getObjectHeap().newOop(handle); + return new OopTreeNodeAdapter(oop, cf, getTreeTableMode()); + } catch (AddressException e) { + return new BadOopTreeNodeAdapter(handle, + new CTypeFieldIdentifier(type, f), + getTreeTableMode()); + } catch (UnknownOopException e) { + return new BadOopTreeNodeAdapter(handle, + new CTypeFieldIdentifier(type, f), + getTreeTableMode()); + } + } else if (t.isCIntegerType()) { + long value = 0; + if (f.isStatic()) { + value = f.getCInteger((CIntegerType)t); + } else { + value = f.getCInteger(addr, (CIntegerType)t); + } + return new LongTreeNodeAdapter(value, cf, getTreeTableMode()); + } else if (t.isJavaPrimitiveType()) { + boolean isStatic = f.isStatic(); + if (f instanceof JByteField) { + long value = isStatic? f.getJByte() : f.getJByte(addr); + return new LongTreeNodeAdapter(value, cf, getTreeTableMode()); + } else if (f instanceof JShortField) { + long value = isStatic? f.getJShort() : f.getJShort(addr); + return new LongTreeNodeAdapter(value, cf, getTreeTableMode()); + } else if (f instanceof JIntField) { + long value = isStatic? f.getJInt() : f.getJInt(addr); + return new LongTreeNodeAdapter(value, cf, getTreeTableMode()); + } else if (f instanceof JLongField) { + long value = isStatic? f.getJLong() : f.getJLong(addr); + return new LongTreeNodeAdapter(value, cf, getTreeTableMode()); + } else if (f instanceof JCharField) { + char value = isStatic? f.getJChar() : f.getJChar(addr); + return new CharTreeNodeAdapter(value, cf, getTreeTableMode()); + } else if (f instanceof JBooleanField) { + boolean value = isStatic? f.getJBoolean() : f.getJBoolean(addr); + return new BooleanTreeNodeAdapter(value, cf, getTreeTableMode()); + } else if (f instanceof JFloatField) { + float value = isStatic? f.getJFloat() : f.getJFloat(addr); + return new DoubleTreeNodeAdapter(value, cf, getTreeTableMode()); + } else if (f instanceof JDoubleField) { + double value = isStatic? f.getJDouble() : f.getJDouble(addr); + return new DoubleTreeNodeAdapter(value, cf, getTreeTableMode()); + } else { + throw new RuntimeException("unhandled type: " + t.getName()); + } + } else if (t.isPointerType()) { + Address ptr; + if (f.isStatic()) { + ptr = f.getAddress(); + } else { + ptr = f.getAddress(addr); + } + + if (t.isCStringType()) { + return new CStringTreeNodeAdapter(CStringUtilities.getString(ptr), cf); + } + + return new CTypeTreeNodeAdapter(ptr, ((PointerType) t).getTargetType(), cf, getTreeTableMode()); + } else { + if (f.isStatic()) { + return new CTypeTreeNodeAdapter(f.getStaticFieldAddress(), f.getType(), + cf, getTreeTableMode()); + } else { + return new CTypeTreeNodeAdapter(addr.addOffsetTo(f.getOffset()), f.getType(), + cf, getTreeTableMode()); + } + } + } catch (AddressException e) { + return new BadAddressTreeNodeAdapter(e.getAddress(), + new CTypeFieldIdentifier(type, f), + getTreeTableMode()); + } + } + + public boolean isLeaf() { + return getFields().length == 0; + } + + public int getIndexOfChild(SimpleTreeNode child) { + CTypeFieldIdentifier id = (CTypeFieldIdentifier)((FieldTreeNodeAdapter) child).getID(); + CTypeFieldIdentifier[] f = getFields(); + for (int i = 0; i < f.length; i++) { + if (id == f[i]) { + return i; + } + } + return -1; + } + + public String getValue() { + if (type != null) { + return type.getName() + " @ " + addr; + } else { + return ""; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/CharTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/CharTreeNodeAdapter.java new file mode 100644 index 00000000000..362d33a2b61 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/CharTreeNodeAdapter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import sun.jvm.hotspot.oops.*; + +/** Encapsulates a char value in a tree handled by SimpleTreeModel */ + +public class CharTreeNodeAdapter extends FieldTreeNodeAdapter { + private char val; + + public CharTreeNodeAdapter(char val, FieldIdentifier id) { + this(val, id, false); + } + + public CharTreeNodeAdapter(char val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + return "'" + val + "'"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/DoubleTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/DoubleTreeNodeAdapter.java new file mode 100644 index 00000000000..c117e369842 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/DoubleTreeNodeAdapter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import sun.jvm.hotspot.oops.*; + +/** Encapsulates a double value in a tree handled by SimpleTreeModel */ + +public class DoubleTreeNodeAdapter extends FieldTreeNodeAdapter { + private double val; + + public DoubleTreeNodeAdapter(double val, FieldIdentifier id) { + this(val, id, false); + } + + public DoubleTreeNodeAdapter(double val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + return Double.toString(val); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/FieldTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/FieldTreeNodeAdapter.java new file mode 100644 index 00000000000..3dcbd845eb5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/FieldTreeNodeAdapter.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import sun.jvm.hotspot.oops.*; + +/** Abstract base class for all adapters for fields of oops */ + +public abstract class FieldTreeNodeAdapter implements SimpleTreeNode { + private FieldIdentifier id; + private boolean treeTableMode; + + /** The identifier may be null, i.e., for the root of the tree */ + public FieldTreeNodeAdapter(FieldIdentifier id, boolean treeTableMode) { + this.id = id; + this.treeTableMode = treeTableMode; + } + + public FieldIdentifier getID() { + return id; + } + + /** Defaults to false in subclasses */ + public boolean getTreeTableMode() { + return treeTableMode; + } + + public String getName() { + if (getID() != null) { + return getID().getName(); + } + return ""; + } + + public String toString() { + if (treeTableMode) { + return getName(); + } else { + if (getID() != null) { + return getName() + ": " + getValue(); + } else { + return getValue(); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/FloatTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/FloatTreeNodeAdapter.java new file mode 100644 index 00000000000..c162882bd8e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/FloatTreeNodeAdapter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import sun.jvm.hotspot.oops.*; + +/** Encapsulates a float value in a tree handled by SimpleTreeModel */ + +public class FloatTreeNodeAdapter extends FieldTreeNodeAdapter { + private float val; + + public FloatTreeNodeAdapter(float val, FieldIdentifier id) { + this(val, id, false); + } + + public FloatTreeNodeAdapter(float val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + return Float.toString(val); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/LongTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/LongTreeNodeAdapter.java new file mode 100644 index 00000000000..f94d6e2d326 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/LongTreeNodeAdapter.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import sun.jvm.hotspot.oops.*; + +/** Encapsulates a long value in a tree handled by SimpleTreeModel */ + +public class LongTreeNodeAdapter extends FieldTreeNodeAdapter { + private long val; + private boolean hexFormat = false; + + public LongTreeNodeAdapter(long val, FieldIdentifier id) { + this(val, id, false); + } + + public LongTreeNodeAdapter(long val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public void setHexFormat(boolean hexFormat) { + this.hexFormat = hexFormat; + } + + public boolean getHexFormat() { + return hexFormat; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + if (hexFormat) { + return "0x" + Long.toHexString(val); + } else { + return Long.toString(val); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/OopTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/OopTreeNodeAdapter.java new file mode 100644 index 00000000000..4edcd787ad5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/OopTreeNodeAdapter.java @@ -0,0 +1,264 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import java.io.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; + +/** An adapter class which allows oops to be displayed in a tree via + the SimpleTreeNode interface. FIXME: must attach this to some sort + of policy object which determines how to display names and whether + VM fields should be shown. (Must also fix oop visitation mechanism + in oops package.) */ + +public class OopTreeNodeAdapter extends FieldTreeNodeAdapter { + private Oop oop; + + /** The oop may be null (for oop fields of oops which are null); the + FieldIdentifier may also be null (for the root node). + treeTableMode defaults to false. */ + public OopTreeNodeAdapter(Oop oop, FieldIdentifier id) { + this(oop, id, false); + } + + /** The oop may be null (for oop fields of oops which are null); the + FieldIdentifier may also be null (for the root node). */ + public OopTreeNodeAdapter(Oop oop, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.oop = oop; + } + + public Oop getOop() { + return oop; + } + + public int getChildCount() { + if (oop == null) { + return 0; + } + + Counter c = new Counter(); + oop.iterate(c, true); + return c.getNumFields() + (VM.getVM().getRevPtrs() == null ? 0 : 1); + } + + public SimpleTreeNode getChild(int index) { + if (oop == null) { + return null; + } + if (VM.getVM().getRevPtrs() != null) { + if (index == 0) { + return new RevPtrsTreeNodeAdapter(oop, getTreeTableMode()); + } else { + index -= 1; + } + } + + Fetcher f = new Fetcher(index); + oop.iterate(f, true); + return f.getChild(); + } + + public boolean isLeaf() { + return (oop == null); + } + + public int getIndexOfChild(SimpleTreeNode child) { + if (child instanceof RevPtrsTreeNodeAdapter) { + // assert(VM.getVM().getRevPtrs() != null, "Only created from revptrs"); + return 0; + } + FieldIdentifier id = ((FieldTreeNodeAdapter) child).getID(); + Finder f = new Finder(id); + oop.iterate(f, true); + return f.getIndex() + (VM.getVM().getRevPtrs() == null ? 0 : 1); + } + + public String getValue() { + if (oop != null) { + // FIXME: choose style of printing depending on whether we're + // displaying VM fields? Want to make Java objects look like + // Java objects. + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + Oop.printOopValueOn(oop, new PrintStream(bos)); + return bos.toString(); + } + return "null"; + } + + /** Should be applied to one oop at a time, then have the number of + fields fetched. FIXME: want this to distinguish between VM and + non-VM fields. */ + static class Counter extends DefaultOopVisitor { + private int numFields; + + public int getNumFields() { + return numFields; + } + + public void prologue() { + numFields = 0; + } + + public void doOop(OopField field, boolean isVMField) { ++numFields; } + public void doByte(ByteField field, boolean isVMField) { ++numFields; } + public void doChar(CharField field, boolean isVMField) { ++numFields; } + public void doBoolean(BooleanField field, boolean isVMField) { ++numFields; } + public void doShort(ShortField field, boolean isVMField) { ++numFields; } + public void doInt(IntField field, boolean isVMField) { ++numFields; } + public void doLong(LongField field, boolean isVMField) { ++numFields; } + public void doFloat(FloatField field, boolean isVMField) { ++numFields; } + public void doDouble(DoubleField field, boolean isVMField) { ++numFields; } + public void doCInt(CIntField field, boolean isVMField) { ++numFields; } + } + + /** Creates a new SimpleTreeNode for the given field. FIXME: want + this to distinguish between VM and non-VM fields. */ + class Fetcher extends DefaultOopVisitor { + private int index; + private int curField; + private SimpleTreeNode child; + + public Fetcher(int index) { + this.index = index; + } + + public SimpleTreeNode getChild() { + return child; + } + + public void prologue() { + curField = 0; + } + + public void doOop(OopField field, boolean isVMField) { + if (curField == index) { + try { + child = new OopTreeNodeAdapter(field.getValue(getObj()), field.getID(), getTreeTableMode()); + } catch (AddressException e) { + child = new BadOopTreeNodeAdapter(field.getValueAsOopHandle(getObj()), field.getID(), getTreeTableMode()); + } + } + ++curField; + } + + public void doByte(ByteField field, boolean isVMField) { + if (curField == index) { + child = new LongTreeNodeAdapter(field.getValue(getObj()) & 0xFF, field.getID(), getTreeTableMode()); + } + ++curField; + } + + public void doChar(CharField field, boolean isVMField) { + if (curField == index) { + child = new CharTreeNodeAdapter(field.getValue(getObj()), field.getID(), getTreeTableMode()); + } + ++curField; + } + + public void doBoolean(BooleanField field, boolean isVMField) { + if (curField == index) { + child = new BooleanTreeNodeAdapter(field.getValue(getObj()), field.getID(), getTreeTableMode()); + } + ++curField; + } + + public void doShort(ShortField field, boolean isVMField) { + if (curField == index) { + child = new LongTreeNodeAdapter(field.getValue(getObj()) & 0xFFFF, field.getID(), getTreeTableMode()); + } + ++curField; + } + + public void doInt(IntField field, boolean isVMField) { + if (curField == index) { + child = new LongTreeNodeAdapter(field.getValue(getObj()) & 0xFFFFFFFF, field.getID(), getTreeTableMode()); + } + ++curField; + } + + public void doLong(LongField field, boolean isVMField) { + if (curField == index) { + child = new LongTreeNodeAdapter(field.getValue(getObj()), field.getID(), getTreeTableMode()); + } + ++curField; + } + + public void doFloat(FloatField field, boolean isVMField) { + if (curField == index) { + child = new FloatTreeNodeAdapter(field.getValue(getObj()), field.getID(), getTreeTableMode()); + } + ++curField; + } + + public void doDouble(DoubleField field, boolean isVMField) { + if (curField == index) { + child = new DoubleTreeNodeAdapter(field.getValue(getObj()), field.getID(), getTreeTableMode()); + } + ++curField; + } + + public void doCInt(CIntField field, boolean isVMField) { + if (curField == index) { + child = new LongTreeNodeAdapter(field.getValue(getObj()), field.getID(), getTreeTableMode()); + } + ++curField; + } + } + + /** Finds the index of the given FieldIdentifier. */ + static class Finder extends DefaultOopVisitor { + private FieldIdentifier id; + private int curField; + private int index; + + public Finder(FieldIdentifier id) { + this.id = id; + } + + /** Returns -1 if not found */ + public int getIndex() { + return index; + } + + public void prologue() { + curField = 0; + index = -1; + } + + public void doOop(OopField field, boolean isVMField) { if (field.getID().equals(id)) { index = curField; } ++curField; } + public void doByte(ByteField field, boolean isVMField) { if (field.getID().equals(id)) { index = curField; } ++curField; } + public void doChar(CharField field, boolean isVMField) { if (field.getID().equals(id)) { index = curField; } ++curField; } + public void doBoolean(BooleanField field, boolean isVMField) { if (field.getID().equals(id)) { index = curField; } ++curField; } + public void doShort(ShortField field, boolean isVMField) { if (field.getID().equals(id)) { index = curField; } ++curField; } + public void doInt(IntField field, boolean isVMField) { if (field.getID().equals(id)) { index = curField; } ++curField; } + public void doLong(LongField field, boolean isVMField) { if (field.getID().equals(id)) { index = curField; } ++curField; } + public void doFloat(FloatField field, boolean isVMField) { if (field.getID().equals(id)) { index = curField; } ++curField; } + public void doDouble(DoubleField field, boolean isVMField) { if (field.getID().equals(id)) { index = curField; } ++curField; } + public void doCInt(CIntField field, boolean isVMField) { if (field.getID().equals(id)) { index = curField; } ++curField; } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/RevPtrsTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/RevPtrsTreeNodeAdapter.java new file mode 100644 index 00000000000..90adf71c571 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/RevPtrsTreeNodeAdapter.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import java.util.*; +import java.io.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +/** Who directly points to this node. */ + +public class RevPtrsTreeNodeAdapter extends FieldTreeNodeAdapter { + private static FieldIdentifier fid = new NamedFieldIdentifier("_revPtrs"); + + private List children; + + public RevPtrsTreeNodeAdapter(Oop oop) { + this(oop, false); + } + + public RevPtrsTreeNodeAdapter(Oop oop, boolean treeTableMode) { + super(fid, treeTableMode); + children = VM.getVM().getRevPtrs().get(oop); + } + + public int getChildCount() { + return children != null ? children.size() : 0; + } + + public SimpleTreeNode getChild(int index) { + LivenessPathElement lpe = (LivenessPathElement)children.get(index); + IndexableFieldIdentifier ifid = new IndexableFieldIdentifier(index); + Oop oop = lpe.getObj(); + if (oop != null) { + return new OopTreeNodeAdapter(oop, ifid, getTreeTableMode()); + } else { + NamedFieldIdentifier nfi = (NamedFieldIdentifier)lpe.getField(); + return new RootTreeNodeAdapter(nfi.getName(), ifid, getTreeTableMode()); + } + } + + public boolean isLeaf() { + return false; + } + + public int getIndexOfChild(SimpleTreeNode child) { + FieldIdentifier id = ((FieldTreeNodeAdapter) child).getID(); + IndexableFieldIdentifier ifid = (IndexableFieldIdentifier)id; + return ifid.getIndex(); + } + + public String getName() { return "<>"; } + public String getValue() { return ""; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/RootTreeNodeAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/RootTreeNodeAdapter.java new file mode 100644 index 00000000000..cf295a9dabb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/RootTreeNodeAdapter.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import sun.jvm.hotspot.oops.*; + +/** Root oop pointer */ + +public class RootTreeNodeAdapter extends FieldTreeNodeAdapter { + private String val; + + public RootTreeNodeAdapter(String val, FieldIdentifier id) { + this(val, id, false); + } + + /** The identifier may be null, i.e., for the root of the tree */ + public RootTreeNodeAdapter(String val, FieldIdentifier id, boolean treeTableMode) { + super(id, treeTableMode); + this.val = val; + } + + public int getChildCount() { + return 0; + } + + public SimpleTreeNode getChild(int index) { + return null; + } + + public boolean isLeaf() { + return true; + } + + public int getIndexOfChild(SimpleTreeNode child) { + return 0; + } + + public String getValue() { + return val; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/SimpleTreeGroupNode.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/SimpleTreeGroupNode.java new file mode 100644 index 00000000000..215b4d045ed --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/SimpleTreeGroupNode.java @@ -0,0 +1,61 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import java.util.*; + +/** Node to which others can be added; suitable for use as root node + of graph */ + +public class SimpleTreeGroupNode implements SimpleTreeNode { + private List children; + + public SimpleTreeGroupNode() { + children = new ArrayList(); + } + + public int getChildCount() { return children.size(); } + public SimpleTreeNode getChild(int index) { + return (SimpleTreeNode) children.get(index); + } + public void addChild(SimpleTreeNode child) { + children.add(child); + } + public SimpleTreeNode removeChild(int index) { + return (SimpleTreeNode) children.remove(index); + } + public void removeAllChildren() { + children.clear(); + } + public boolean isLeaf() { + return false; + } + public int getIndexOfChild(SimpleTreeNode child) { + return children.indexOf(child); + } + + public String getName() { return null; } + public String getValue() { return null; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/SimpleTreeModel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/SimpleTreeModel.java new file mode 100644 index 00000000000..40124cb9b5d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/SimpleTreeModel.java @@ -0,0 +1,103 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import java.util.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.tree.*; + +/** A very simple tree model which allows the root to be set, so we + can reuse the same model for various types of data; the + specialization is contained within the nodes. This tree model + operates on SimpleTreeNodes. */ + +public class SimpleTreeModel implements TreeModel { + private static final SimpleTreeNode singletonNullRoot = new SimpleTreeNode() { + public int getChildCount() { return 0; } + public SimpleTreeNode getChild(int index) { return null; } + public boolean isLeaf() { return true; } + public int getIndexOfChild(SimpleTreeNode child) { return 0; } + public String toString() { return ""; } + public String getName() { return toString(); } + public String getValue() { return toString(); } + }; + private SimpleTreeNode root = singletonNullRoot; + /** List */ + private List listeners = new ArrayList(); + + public void setRoot(SimpleTreeNode node) { + if (node != null) { + root = node; + } else { + root = singletonNullRoot; + } + fireTreeStructureChanged(); + } + + public Object getRoot() { + return root; + } + + public Object getChild(Object parent, int index) { + return ((SimpleTreeNode) parent).getChild(index); + } + + public int getChildCount(Object parent) { + return ((SimpleTreeNode) parent).getChildCount(); + } + + public boolean isLeaf(Object node) { + if (node == null) { + return true; + } + return ((SimpleTreeNode) node).isLeaf(); + } + + /** Unsupported operation */ + public void valueForPathChanged(TreePath path, Object newValue) { + throw new UnsupportedOperationException(); + } + + public int getIndexOfChild(Object parent, Object child) { + return ((SimpleTreeNode) parent).getIndexOfChild((SimpleTreeNode) child); + } + + public void addTreeModelListener(TreeModelListener l) { + listeners.add(l); + } + + public void removeTreeModelListener(TreeModelListener l) { + listeners.remove(l); + } + + public void fireTreeStructureChanged() { + TreeModelEvent e = new TreeModelEvent(getRoot(), new Object[] { getRoot() }, null, null); + for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { + TreeModelListener l = (TreeModelListener) iter.next(); + l.treeStructureChanged(e); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/SimpleTreeNode.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/SimpleTreeNode.java new file mode 100644 index 00000000000..df2093e3551 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/tree/SimpleTreeNode.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.tree; + +import javax.swing.*; +import javax.swing.tree.*; + +/** A very simple interface for nodes in the SimpleTreeModel. */ + +public interface SimpleTreeNode { + public int getChildCount(); + public SimpleTreeNode getChild(int index); + public boolean isLeaf(); + public int getIndexOfChild(SimpleTreeNode child); + + /** TreeTable support: get name of the node */ + public String getName(); + + /** TreeTable support: get value of the node */ + public String getValue(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/AbstractTreeTableModel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/AbstractTreeTableModel.java new file mode 100644 index 00000000000..7fa423f4989 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/AbstractTreeTableModel.java @@ -0,0 +1,206 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.treetable; + +import javax.swing.tree.*; +import javax.swing.event.*; + +/** + * An abstract implementation of the TreeTableModel interface, handling the list + * of listeners. + * @author Philip Milne + */ + +public abstract class AbstractTreeTableModel implements TreeTableModel { + protected Object root; + protected EventListenerList listenerList = new EventListenerList(); + + public AbstractTreeTableModel(Object root) { + this.root = root; + } + + // + // Default implmentations for methods in the TreeModel interface. + // + + public Object getRoot() { + return root; + } + + public boolean isLeaf(Object node) { + return getChildCount(node) == 0; + } + + public void valueForPathChanged(TreePath path, Object newValue) {} + + // This is not called in the JTree's default mode: use a naive implementation. + public int getIndexOfChild(Object parent, Object child) { + for (int i = 0; i < getChildCount(parent); i++) { + if (getChild(parent, i).equals(child)) { + return i; + } + } + return -1; + } + + public void addTreeModelListener(TreeModelListener l) { + listenerList.add(TreeModelListener.class, l); + } + + public void removeTreeModelListener(TreeModelListener l) { + listenerList.remove(TreeModelListener.class, l); + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeNodesChanged(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeNodesChanged(e); + } + } + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeNodesInserted(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeNodesInserted(e); + } + } + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeNodesRemoved(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e); + } + } + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeStructureChanged(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeStructureChanged(e); + } + } + } + + // + // Default impelmentations for methods in the TreeTableModel interface. + // + + public Class getColumnClass(int column) { return Object.class; } + + /** By default, make the column with the Tree in it the only editable one. + * Making this column editable causes the JTable to forward mouse + * and keyboard events in the Tree column to the underlying JTree. + */ + public boolean isCellEditable(Object node, int column) { + return getColumnClass(column) == TreeTableModel.class; + } + + public void setValueAt(Object aValue, Object node, int column) {} + + + // Left to be implemented in the subclass: + + /* + * public Object getChild(Object parent, int index) + * public int getChildCount(Object parent) + * public int getColumnCount() + * public String getColumnName(Object node, int column) + * public Object getValueAt(Object node, int column) + */ +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/JTreeTable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/JTreeTable.java new file mode 100644 index 00000000000..42b0c94da9e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/JTreeTable.java @@ -0,0 +1,585 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.treetable; + +import java.awt.*; + +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.*; +import javax.swing.tree.*; +import javax.swing.table.*; + +import java.awt.event.*; + +import java.util.EventObject; + +/** + * This example shows how to create a simple JTreeTable component, + * by using a JTree as a renderer (and editor) for the cells in a + * particular column in the JTable. + * + * + * @author Philip Milne + * @author Scott Violet + */ +public class JTreeTable extends JTable { + /** A subclass of JTree. */ + protected TreeTableCellRenderer tree; + + ////////////////////////// + // Convenience routines // + ////////////////////////// + + private boolean treeEditable = true; + private boolean showsIcons = true; + + public boolean getTreeEditable() { + return treeEditable; + } + + public void setTreeEditable(boolean editable) { + treeEditable = editable; + } + + public boolean getShowsIcons() { + return showsIcons; + } + + public void setShowsIcons(boolean show) { + showsIcons = show; + } + + public void setRootVisible(boolean visible) { + tree.setRootVisible(visible); + } + + public boolean getShowsRootHandles() { + return tree.getShowsRootHandles(); + } + + public void setShowsRootHandles(boolean newValue) { + tree.setShowsRootHandles(newValue); + } + + public JTreeTable(TreeTableModel treeTableModel) { + super(); + + // Create the tree. It will be used as a renderer and editor. + tree = new TreeTableCellRenderer(treeTableModel); + + // Install a tableModel representing the visible rows in the tree. + super.setModel(new TreeTableModelAdapter(treeTableModel, tree)); + + // Force the JTable and JTree to share their row selection models. + ListToTreeSelectionModelWrapper selectionWrapper = new + ListToTreeSelectionModelWrapper(); + tree.setSelectionModel(selectionWrapper); + setSelectionModel(selectionWrapper.getListSelectionModel()); + + // Install the tree editor renderer and editor. + setDefaultRenderer(TreeTableModel.class, tree); + setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor()); + + // No grid. + setShowGrid(false); + + // No intercell spacing + setIntercellSpacing(new Dimension(0, 0)); + + // And update the height of the trees row to match that of + // the table. + if (tree.getRowHeight() < 1) { + // Metal looks better like this. + setRowHeight(20); + } + } + + /** + * Overridden to message super and forward the method to the tree. + * Since the tree is not actually in the component hieachy it will + * never receive this unless we forward it in this manner. + */ + public void updateUI() { + super.updateUI(); + if(tree != null) { + tree.updateUI(); + // Do this so that the editor is referencing the current renderer + // from the tree. The renderer can potentially change each time + // laf changes. + setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor()); + } + // Use the tree's default foreground and background colors in the + // table. + LookAndFeel.installColorsAndFont(this, "Tree.background", + "Tree.foreground", "Tree.font"); + } + + /** + * Workaround for BasicTableUI anomaly. Make sure the UI never tries to + * resize the editor. The UI currently uses different techniques to + * paint the renderers and editors and overriding setBounds() below + * is not the right thing to do for an editor. Returning -1 for the + * editing row in this case, ensures the editor is never painted. + */ + public int getEditingRow() { + return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1 : + editingRow; + } + + /** + * Returns the actual row that is editing as getEditingRow + * will always return -1. + */ + private int realEditingRow() { + return editingRow; + } + + /** + * This is overriden to invoke supers implementation, and then, + * if the receiver is editing a Tree column, the editors bounds is + * reset. The reason we have to do this is because JTable doesn't + * think the table is being edited, as getEditingRow returns + * -1, and therefore doesn't automaticly resize the editor for us. + */ + public void sizeColumnsToFit(int resizingColumn) { + super.sizeColumnsToFit(resizingColumn); + if (getEditingColumn() != -1 && getColumnClass(editingColumn) == + TreeTableModel.class) { + Rectangle cellRect = getCellRect(realEditingRow(), + getEditingColumn(), false); + Component component = getEditorComponent(); + component.setBounds(cellRect); + component.validate(); + } + } + + /** + * Overridden to pass the new rowHeight to the tree. + */ + public void setRowHeight(int rowHeight) { + super.setRowHeight(rowHeight); + if (tree != null && tree.getRowHeight() != rowHeight) { + tree.setRowHeight(getRowHeight()); + } + } + + /** + * Returns the tree that is being shared between the model. + */ + public JTree getTree() { + return tree; + } + + /** + * Overriden to invoke repaint for the particular location if + * the column contains the tree. This is done as the tree editor does + * not fill the bounds of the cell, we need the renderer to paint + * the tree in the background, and then draw the editor over it. + */ + public boolean editCellAt(int row, int column, EventObject e){ + boolean retValue = super.editCellAt(row, column, e); + if (retValue && getColumnClass(column) == TreeTableModel.class) { + repaint(getCellRect(row, column, false)); + } + return retValue; + } + + /** A DefaultTreeCellRenderer which can optionally skip drawing + all icons. */ + class JTreeTableCellRenderer extends DefaultTreeCellRenderer { + public Icon getClosedIcon() { return (showsIcons ? super.getClosedIcon() : null); } + public Icon getDefaultClosedIcon() { return (showsIcons ? super.getDefaultClosedIcon() : null); } + public Icon getDefaultLeafIcon() { return (showsIcons ? super.getDefaultLeafIcon() : null); } + public Icon getDefaultOpenIcon() { return (showsIcons ? super.getDefaultOpenIcon() : null); } + public Icon getLeafIcon() { return (showsIcons ? super.getLeafIcon() : null); } + public Icon getOpenIcon() { return (showsIcons ? super.getOpenIcon() : null); } + } + + /** + * A TreeCellRenderer that displays a JTree. + */ + public class TreeTableCellRenderer extends JTree implements + TableCellRenderer { + /** Last table/tree row asked to renderer. */ + protected int visibleRow; + /** Border to draw around the tree, if this is non-null, it will + * be painted. */ + protected Border highlightBorder; + + public TreeTableCellRenderer(TreeModel model) { + super(model); + setCellRenderer(new JTreeTableCellRenderer()); + } + + /** + * updateUI is overridden to set the colors of the Tree's renderer + * to match that of the table. + */ + public void updateUI() { + super.updateUI(); + // Make the tree's cell renderer use the table's cell selection + // colors. + TreeCellRenderer tcr = getCellRenderer(); + if (tcr instanceof DefaultTreeCellRenderer) { + DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr); + // For 1.1 uncomment this, 1.2 has a bug that will cause an + // exception to be thrown if the border selection color is + // null. + // dtcr.setBorderSelectionColor(null); + dtcr.setTextSelectionColor(UIManager.getColor + ("Table.selectionForeground")); + dtcr.setBackgroundSelectionColor(UIManager.getColor + ("Table.selectionBackground")); + } + } + + /** + * Sets the row height of the tree, and forwards the row height to + * the table. + */ + public void setRowHeight(int rowHeight) { + if (rowHeight > 0) { + super.setRowHeight(rowHeight); + if (JTreeTable.this != null && + JTreeTable.this.getRowHeight() != rowHeight) { + JTreeTable.this.setRowHeight(getRowHeight()); + } + } + } + + /** + * This is overridden to set the height to match that of the JTable. + */ + public void setBounds(int x, int y, int w, int h) { + super.setBounds(x, 0, w, JTreeTable.this.getHeight()); + } + + /** + * Sublcassed to translate the graphics such that the last visible + * row will be drawn at 0,0. + */ + public void paint(Graphics g) { + g.translate(0, -visibleRow * getRowHeight()); + super.paint(g); + // Draw the Table border if we have focus. + if (highlightBorder != null) { + highlightBorder.paintBorder(this, g, 0, visibleRow * + getRowHeight(), getWidth(), + getRowHeight()); + } + } + + /** + * TreeCellRenderer method. Overridden to update the visible row. + */ + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, int column) { + Color background; + Color foreground; + + if(isSelected) { + background = table.getSelectionBackground(); + foreground = table.getSelectionForeground(); + } + else { + background = table.getBackground(); + foreground = table.getForeground(); + } + highlightBorder = null; + if (realEditingRow() == row && getEditingColumn() == column) { + background = UIManager.getColor("Table.focusCellBackground"); + foreground = UIManager.getColor("Table.focusCellForeground"); + } + else if (hasFocus) { + highlightBorder = UIManager.getBorder + ("Table.focusCellHighlightBorder"); + if (isCellEditable(row, column)) { + background = UIManager.getColor + ("Table.focusCellBackground"); + foreground = UIManager.getColor + ("Table.focusCellForeground"); + } + } + + visibleRow = row; + setBackground(background); + + TreeCellRenderer tcr = getCellRenderer(); + if (tcr instanceof DefaultTreeCellRenderer) { + DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr); + if (isSelected) { + dtcr.setTextSelectionColor(foreground); + dtcr.setBackgroundSelectionColor(background); + } + else { + dtcr.setTextNonSelectionColor(foreground); + dtcr.setBackgroundNonSelectionColor(background); + } + } + return this; + } + } + + + /** + * An editor that can be used to edit the tree column. This extends + * DefaultCellEditor and uses a JTextField (actually, TreeTableTextField) + * to perform the actual editing. + *

To support editing of the tree column we can not make the tree + * editable. The reason this doesn't work is that you can not use + * the same component for editing and renderering. The table may have + * the need to paint cells, while a cell is being edited. If the same + * component were used for the rendering and editing the component would + * be moved around, and the contents would change. When editing, this + * is undesirable, the contents of the text field must stay the same, + * including the caret blinking, and selections persisting. For this + * reason the editing is done via a TableCellEditor. + *

Another interesting thing to be aware of is how tree positions + * its render and editor. The render/editor is responsible for drawing the + * icon indicating the type of node (leaf, branch...). The tree is + * responsible for drawing any other indicators, perhaps an additional + * +/- sign, or lines connecting the various nodes. So, the renderer + * is positioned based on depth. On the other hand, table always makes + * its editor fill the contents of the cell. To get the allusion + * that the table cell editor is part of the tree, we don't want the + * table cell editor to fill the cell bounds. We want it to be placed + * in the same manner as tree places it editor, and have table message + * the tree to paint any decorations the tree wants. Then, we would + * only have to worry about the editing part. The approach taken + * here is to determine where tree would place the editor, and to override + * the reshape method in the JTextField component to + * nudge the textfield to the location tree would place it. Since + * JTreeTable will paint the tree behind the editor everything should + * just work. So, that is what we are doing here. Determining of + * the icon position will only work if the TreeCellRenderer is + * an instance of DefaultTreeCellRenderer. If you need custom + * TreeCellRenderers, that don't descend from DefaultTreeCellRenderer, + * and you want to support editing in JTreeTable, you will have + * to do something similiar. + */ + public class TreeTableCellEditor extends DefaultCellEditor { + public TreeTableCellEditor() { + super(new TreeTableTextField()); + } + + /** + * Overriden to determine an offset that tree would place the + * editor at. The offset is determined from the + * getRowBounds JTree method, and additionaly + * from the icon DefaultTreeCellRenderer will use. + *

The offset is then set on the TreeTableTextField component + * created in the constructor, and returned. + */ + public Component getTableCellEditorComponent(JTable table, + Object value, + boolean isSelected, + int r, int c) { + Component component = super.getTableCellEditorComponent + (table, value, isSelected, r, c); + JTree t = getTree(); + boolean rv = t.isRootVisible(); + int offsetRow = rv ? r : r - 1; + Rectangle bounds = t.getRowBounds(offsetRow); + int offset = bounds.x; + TreeCellRenderer tcr = t.getCellRenderer(); + if (tcr instanceof DefaultTreeCellRenderer) { + Object node = t.getPathForRow(offsetRow). + getLastPathComponent(); + Icon icon; + if (t.getModel().isLeaf(node)) + icon = ((DefaultTreeCellRenderer)tcr).getLeafIcon(); + else if (tree.isExpanded(offsetRow)) + icon = ((DefaultTreeCellRenderer)tcr).getOpenIcon(); + else + icon = ((DefaultTreeCellRenderer)tcr).getClosedIcon(); + if (icon != null) { + offset += ((DefaultTreeCellRenderer)tcr).getIconTextGap() + + icon.getIconWidth(); + } + } + ((TreeTableTextField)getComponent()).offset = offset; + return component; + } + + /** + * This is overriden to forward the event to the tree. This will + * return true if the click count >= 3, or the event is null. + */ + public boolean isCellEditable(EventObject e) { + if (e instanceof MouseEvent) { + MouseEvent me = (MouseEvent)e; + // If the modifiers are not 0 (or the left mouse button), + // tree may try and toggle the selection, and table + // will then try and toggle, resulting in the + // selection remaining the same. To avoid this, we + // only dispatch when the modifiers are 0 (or the left mouse + // button). + if (me.getModifiers() == 0 || + me.getModifiers() == InputEvent.BUTTON1_MASK) { + for (int counter = getColumnCount() - 1; counter >= 0; + counter--) { + if (getColumnClass(counter) == TreeTableModel.class) { + MouseEvent newME = new MouseEvent + (JTreeTable.this.tree, me.getID(), + me.getWhen(), me.getModifiers(), + me.getX() - getCellRect(0, counter, true).x, + me.getY(), me.getClickCount(), + me.isPopupTrigger()); + JTreeTable.this.tree.dispatchEvent(newME); + break; + } + } + } + if (me.getClickCount() >= 3) { + return treeEditable; + } + return false; + } + if (e == null) { + return treeEditable; + } + return false; + } + } + + + /** + * Component used by TreeTableCellEditor. The only thing this does + * is to override the reshape method, and to ALWAYS + * make the x location be offset. + */ + static class TreeTableTextField extends JTextField { + public int offset; + + public void reshape(int x, int y, int w, int h) { + int newX = Math.max(x, offset); + super.reshape(newX, y, w - (newX - x), h); + } + } + + + /** + * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel + * to listen for changes in the ListSelectionModel it maintains. Once + * a change in the ListSelectionModel happens, the paths are updated + * in the DefaultTreeSelectionModel. + */ + class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel { + /** Set to true when we are updating the ListSelectionModel. */ + protected boolean updatingListSelectionModel; + + public ListToTreeSelectionModelWrapper() { + super(); + getListSelectionModel().addListSelectionListener + (createListSelectionListener()); + } + + /** + * Returns the list selection model. ListToTreeSelectionModelWrapper + * listens for changes to this model and updates the selected paths + * accordingly. + */ + ListSelectionModel getListSelectionModel() { + return listSelectionModel; + } + + /** + * This is overridden to set updatingListSelectionModel + * and message super. This is the only place DefaultTreeSelectionModel + * alters the ListSelectionModel. + */ + public void resetRowSelection() { + if(!updatingListSelectionModel) { + updatingListSelectionModel = true; + try { + super.resetRowSelection(); + } + finally { + updatingListSelectionModel = false; + } + } + // Notice how we don't message super if + // updatingListSelectionModel is true. If + // updatingListSelectionModel is true, it implies the + // ListSelectionModel has already been updated and the + // paths are the only thing that needs to be updated. + } + + /** + * Creates and returns an instance of ListSelectionHandler. + */ + protected ListSelectionListener createListSelectionListener() { + return new ListSelectionHandler(); + } + + /** + * If updatingListSelectionModel is false, this will + * reset the selected paths from the selected rows in the list + * selection model. + */ + protected void updateSelectedPathsFromSelectedRows() { + if(!updatingListSelectionModel) { + updatingListSelectionModel = true; + try { + // This is way expensive, ListSelectionModel needs an + // enumerator for iterating. + int min = listSelectionModel.getMinSelectionIndex(); + int max = listSelectionModel.getMaxSelectionIndex(); + + clearSelection(); + if(min != -1 && max != -1) { + for(int counter = min; counter <= max; counter++) { + if(listSelectionModel.isSelectedIndex(counter)) { + TreePath selPath = tree.getPathForRow + (counter); + + if(selPath != null) { + addSelectionPath(selPath); + } + } + } + } + } + finally { + updatingListSelectionModel = false; + } + } + } + + /** + * Class responsible for calling updateSelectedPathsFromSelectedRows + * when the selection of the list changse. + */ + class ListSelectionHandler implements ListSelectionListener { + public void valueChanged(ListSelectionEvent e) { + updateSelectedPathsFromSelectedRows(); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/SimpleTreeTableModel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/SimpleTreeTableModel.java new file mode 100644 index 00000000000..8c62e07b68b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/SimpleTreeTableModel.java @@ -0,0 +1,90 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.treetable; + +import javax.swing.tree.*; +import sun.jvm.hotspot.ui.tree.*; + +/** An extension of SimpleTreeModel which implements the + TreeTableModel interface. It supports a two-column "Name, Value" + interface and disabling of editing of the "Value" column. Because + of a bug in the implementation of JTreeTable, it always returns + "true" for isCellEditable of cells in the "Name" column; + otherwise, mouse clicks to open handles are not dispatched + properly. Users are responsible for calling setTreeEditable(false) + on the JTreeTable to make the names uneditable. */ + +public class SimpleTreeTableModel extends SimpleTreeModel implements TreeTableModel { + private boolean valuesEditable = true; + + public int getColumnCount() { + return 2; + } + public String getColumnName(int column) { + switch (column) { + case 0: return "Name"; + case 1: return "Value"; + default: throw new RuntimeException("Index " + column + " out of bounds"); + } + } + public Class getColumnClass(int column) { + switch (column) { + case 0: return TreeTableModel.class; + case 1: return String.class; + default: throw new RuntimeException("Index " + column + " out of bounds"); + } + } + public Object getValueAt(Object node, int column) { + SimpleTreeNode realNode = (SimpleTreeNode) node; + switch (column) { + case 0: return realNode.getName(); + case 1: return realNode.getValue(); + default: throw new RuntimeException("Index " + column + " out of bounds"); + } + } + public boolean isCellEditable(Object node, int column) { + switch (column) { + // This must return true in order for the JTreeTable's handles to + // work properly + case 0: return true; + case 1: return valuesEditable; + default: throw new RuntimeException("Index " + column + " out of bounds"); + } + } + public void setValueAt(Object aValue, Object node, int column) { + // FIXME: figure out how to handle this + throw new RuntimeException("FIXME: figure out how to handle editing of SimpleTreeNodes"); + } + + /** Defaults to true */ + public boolean getValuesEditable() { + return valuesEditable; + } + + /** Defaults to true */ + public void setValuesEditable(boolean val) { + valuesEditable = val; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/TreeTableModel.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/TreeTableModel.java new file mode 100644 index 00000000000..5b5c440626b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/TreeTableModel.java @@ -0,0 +1,74 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.treetable; + +import javax.swing.tree.TreeModel; + +/** + * TreeTableModel is the model used by a JTreeTable. It extends TreeModel + * to add methods for getting inforamtion about the set of columns each + * node in the TreeTableModel may have. Each column, like a column in + * a TableModel, has a name and a type associated with it. Each node in + * the TreeTableModel can return a value for each of the columns and + * set that value if isCellEditable() returns true. + * + * @author Philip Milne + * @author Scott Violet + */ +public interface TreeTableModel extends TreeModel +{ + /** + * Returns the number ofs availible column. + */ + public int getColumnCount(); + + /** + * Returns the name for column number column. + */ + public String getColumnName(int column); + + /** + * Returns the type for column number column. + */ + public Class getColumnClass(int column); + + /** + * Returns the value to be displayed for node node, + * at column number column. + */ + public Object getValueAt(Object node, int column); + + /** + * Indicates whether the the value for node node, + * at column number column is editable. + */ + public boolean isCellEditable(Object node, int column); + + /** + * Sets the value for node node, + * at column number column. + */ + public void setValueAt(Object aValue, Object node, int column); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/TreeTableModelAdapter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/TreeTableModelAdapter.java new file mode 100644 index 00000000000..5f669ec66f2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/treetable/TreeTableModelAdapter.java @@ -0,0 +1,135 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui.treetable; + +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.table.AbstractTableModel; +import javax.swing.tree.TreePath; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; + +/** + * This is a wrapper class takes a TreeTableModel and implements + * the table model interface. The implementation is trivial, with + * all of the event dispatching support provided by the superclass: + * the AbstractTableModel. + * + * + * @author Philip Milne + * @author Scott Violet + */ +public class TreeTableModelAdapter extends AbstractTableModel +{ + JTree tree; + TreeTableModel treeTableModel; + + public TreeTableModelAdapter(TreeTableModel treeTableModel, JTree tree) { + this.tree = tree; + this.treeTableModel = treeTableModel; + + tree.addTreeExpansionListener(new TreeExpansionListener() { + // Don't use fireTableRowsInserted() here; the selection model + // would get updated twice. + public void treeExpanded(TreeExpansionEvent event) { + fireTableDataChanged(); + } + public void treeCollapsed(TreeExpansionEvent event) { + fireTableDataChanged(); + } + }); + + // Install a TreeModelListener that can update the table when + // tree changes. We use delayedFireTableDataChanged as we can + // not be guaranteed the tree will have finished processing + // the event before us. + treeTableModel.addTreeModelListener(new TreeModelListener() { + public void treeNodesChanged(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + + public void treeNodesInserted(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + + public void treeNodesRemoved(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + + public void treeStructureChanged(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + }); + } + + // Wrappers, implementing TableModel interface. + + public int getColumnCount() { + return treeTableModel.getColumnCount(); + } + + public String getColumnName(int column) { + return treeTableModel.getColumnName(column); + } + + public Class getColumnClass(int column) { + return treeTableModel.getColumnClass(column); + } + + public int getRowCount() { + return tree.getRowCount(); + } + + protected Object nodeForRow(int row) { + TreePath treePath = tree.getPathForRow(row); + return treePath.getLastPathComponent(); + } + + public Object getValueAt(int row, int column) { + return treeTableModel.getValueAt(nodeForRow(row), column); + } + + public boolean isCellEditable(int row, int column) { + return treeTableModel.isCellEditable(nodeForRow(row), column); + } + + public void setValueAt(Object value, int row, int column) { + treeTableModel.setValueAt(value, nodeForRow(row), column); + } + + /** + * Invokes fireTableDataChanged after all the pending events have been + * processed. SwingUtilities.invokeLater is used to handle this. + */ + protected void delayedFireTableDataChanged() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + fireTableDataChanged(); + } + }); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AbstractHeapGraphWriter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AbstractHeapGraphWriter.java new file mode 100644 index 00000000000..28c9f5e7b16 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AbstractHeapGraphWriter.java @@ -0,0 +1,380 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/** + * This is abstract base class for heap graph writers. This class does + * not assume any file format for the heap graph. It hides heap + * iteration, object (fields) iteration mechanism from derived + * classes. This class does not even accept OutputStream etc. so that + * derived class can construct specific writer/filter from input + * stream. + */ + +public abstract class AbstractHeapGraphWriter implements HeapGraphWriter { + // the function iterates heap and calls Oop type specific writers + protected void write() throws IOException { + SymbolTable symTbl = VM.getVM().getSymbolTable(); + javaLangClass = symTbl.probe("java/lang/Class"); + javaLangString = symTbl.probe("java/lang/String"); + javaLangThread = symTbl.probe("java/lang/Thread"); + ObjectHeap heap = VM.getVM().getObjectHeap(); + try { + heap.iterate(new DefaultHeapVisitor() { + public void prologue(long usedSize) { + try { + writeHeapHeader(); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + + public boolean doObj(Oop oop) { + try { + if (oop instanceof TypeArray) { + writePrimitiveArray((TypeArray)oop); + } else if (oop instanceof ObjArray) { + Klass klass = oop.getKlass(); + ObjArrayKlass oak = (ObjArrayKlass) klass; + Klass bottomType = oak.getBottomKlass(); + if (bottomType instanceof InstanceKlass || + bottomType instanceof TypeArrayKlass) { + writeObjectArray((ObjArray)oop); + } else { + writeInternalObject(oop); + } + } else if (oop instanceof Instance) { + Instance instance = (Instance) oop; + Klass klass = instance.getKlass(); + Symbol name = klass.getName(); + if (name.equals(javaLangString)) { + writeString(instance); + } else if (name.equals(javaLangClass)) { + writeClass(instance); + } else if (name.equals(javaLangThread)) { + writeThread(instance); + } else { + klass = klass.getSuper(); + while (klass != null) { + name = klass.getName(); + if (name.equals(javaLangThread)) { + writeThread(instance); + return false; + } + klass = klass.getSuper(); + } + writeInstance(instance); + } + } else { + // not-a-Java-visible oop + writeInternalObject(oop); + } + } catch (IOException exp) { + throw new RuntimeException(exp); + } + return false; + } + + public void epilogue() { + try { + writeHeapFooter(); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + }); + + // write JavaThreads + writeJavaThreads(); + + // write JNI global handles + writeGlobalJNIHandles(); + + } catch (RuntimeException re) { + handleRuntimeException(re); + } + } + + protected void writeJavaThreads() throws IOException { + Threads threads = VM.getVM().getThreads(); + JavaThread jt = threads.first(); + int index = 1; + while (jt != null) { + if (jt.getThreadObj() != null) { + // Note that the thread serial number range is 1-to-N + writeJavaThread(jt, index); + index++; + } + jt = jt.next(); + } + } + + protected void writeJavaThread(JavaThread jt, int index) + throws IOException { + } + + protected void writeGlobalJNIHandles() throws IOException { + JNIHandles handles = VM.getVM().getJNIHandles(); + JNIHandleBlock blk = handles.globalHandles(); + if (blk != null) { + try { + blk.oopsDo(new AddressVisitor() { + public void visitAddress(Address handleAddr) { + try { + if (handleAddr != null) { + writeGlobalJNIHandle(handleAddr); + } + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + }); + } catch (RuntimeException re) { + handleRuntimeException(re); + } + } + } + + protected void writeGlobalJNIHandle(Address handleAddr) throws IOException { + } + + protected void writeHeapHeader() throws IOException { + } + + // write non-Java-visible (hotspot internal) object + protected void writeInternalObject(Oop oop) throws IOException { + } + + // write Java primitive array + protected void writePrimitiveArray(TypeArray array) throws IOException { + writeObject(array); + } + + // write Java object array + protected void writeObjectArray(ObjArray array) throws IOException { + writeObject(array); + } + + protected void writeInstance(Instance instance) throws IOException { + writeObject(instance); + } + + protected void writeString(Instance instance) throws IOException { + writeInstance(instance); + } + + protected void writeClass(Instance instance) throws IOException { + writeInstance(instance); + } + + protected void writeThread(Instance instance) throws IOException { + writeInstance(instance); + } + + protected void writeObject(Oop oop) throws IOException { + writeObjectHeader(oop); + writeObjectFields(oop); + writeObjectFooter(oop); + } + + protected void writeObjectHeader(Oop oop) throws IOException { + } + + // write instance fields of given object + protected void writeObjectFields(final Oop oop) throws IOException { + try { + oop.iterate(new DefaultOopVisitor() { + public void doOop(OopField field, boolean isVMField) { + try { + Oop ref = field.getValue(oop); + if (ref instanceof TypeArray || + ref instanceof ObjArray || + ref instanceof Instance) { + writeReferenceField(oop, field); + } else { + writeInternalReferenceField(oop, field); + } + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + + public void doByte(ByteField field, boolean isVMField) { + try { + writeByteField(oop, field); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + + public void doChar(CharField field, boolean isVMField) { + try { + writeCharField(oop, field); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + + public void doBoolean(BooleanField field, boolean vField) { + try { + writeBooleanField(oop, field); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + + public void doShort(ShortField field, boolean isVMField) { + try { + writeShortField(oop, field); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + + public void doInt(IntField field, boolean isVMField) { + try { + writeIntField(oop, field); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + + public void doLong(LongField field, boolean isVMField) { + try { + writeLongField(oop, field); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + + public void doFloat(FloatField field, boolean isVMField) { + try { + writeFloatField(oop, field); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + + public void doDouble(DoubleField field, boolean vField) { + try { + writeDoubleField(oop, field); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + }, false); + } catch (RuntimeException re) { + handleRuntimeException(re); + } + } + + // object field writers + protected void writeInternalReferenceField(Oop oop, OopField field) + throws IOException { + } + + protected void writeReferenceField(Oop oop, OopField field) + throws IOException { + } + + protected void writeByteField(Oop oop, ByteField field) + throws IOException { + } + + protected void writeCharField(Oop oop, CharField field) + throws IOException { + } + + protected void writeBooleanField(Oop oop, BooleanField field) + throws IOException { + } + + protected void writeShortField(Oop oop, ShortField field) + throws IOException { + } + + protected void writeIntField(Oop oop, IntField field) + throws IOException { + } + + protected void writeLongField(Oop oop, LongField field) + throws IOException { + } + + protected void writeFloatField(Oop oop, FloatField field) + throws IOException { + } + + protected void writeDoubleField(Oop oop, DoubleField field) + throws IOException { + } + + protected void writeObjectFooter(Oop oop) throws IOException { + } + + protected void writeHeapFooter() throws IOException { + } + + // HeapVisitor, OopVisitor methods can't throw any non-runtime + // exception. But, derived class write methods (which are called + // from visitor callbacks) may throw IOException. Hence, we throw + // RuntimeException with origianal IOException as cause from the + // visitor methods. This method gets back the original IOException + // (if any) and re-throws the same. + protected void handleRuntimeException(RuntimeException re) + throws IOException { + Throwable cause = re.getCause(); + if (cause != null && cause instanceof IOException) { + throw (IOException) cause; + } else { + // some other RuntimeException, just re-throw + throw re; + } + } + + // whether a given oop is Java visible or hotspot internal? + protected boolean isJavaVisible(Oop oop) { + if (oop instanceof Instance || oop instanceof TypeArray) { + return true; + } else if (oop instanceof ObjArray) { + ObjArrayKlass oak = (ObjArrayKlass) oop.getKlass(); + Klass bottomKlass = oak.getBottomKlass(); + return bottomKlass instanceof InstanceKlass || + bottomKlass instanceof TypeArrayKlass; + } else { + return false; + } + } + + protected Symbol javaLangClass; + protected Symbol javaLangString; + protected Symbol javaLangThread; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AddressOps.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AddressOps.java new file mode 100644 index 00000000000..e57dd37f067 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AddressOps.java @@ -0,0 +1,120 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import sun.jvm.hotspot.debugger.*; + +/** Helper class with operations on addresses. Solves the problem of + one or both of the arguments being null and needing to call + methods like greaterThan(), lessThan(), etc. on them. */ + +public class AddressOps { + /** Returns true if a1 is less than a2. Either or both may be null. */ + public static boolean lessThan(Address a1, Address a2) { + if (a2 == null) { + return false; + } else if (a1 == null) { + return true; + } else { + return a1.lessThan(a2); + } + } + + /** Returns true if a1 is less than or equal to a2. Either or both may be null. */ + public static boolean lessThanOrEqual(Address a1, Address a2) { + if (a2 == null) { + return (a1 == null); + } else if (a1 == null) { + return true; + } else { + return a1.lessThanOrEqual(a2); + } + } + + /** Returns true if a1 is greater than a2. Either or both may be null. */ + public static boolean greaterThan(Address a1, Address a2) { + if (a1 == null) { + return false; + } else if (a2 == null) { + return true; + } else { + return a1.greaterThan(a2); + } + } + + /** Returns true if a1 is greater than or equal to a2. Either or both may be null. */ + public static boolean greaterThanOrEqual(Address a1, Address a2) { + if (a1 == null) { + return (a2 == null); + } else if (a2 == null) { + return true; + } else { + return a1.greaterThanOrEqual(a2); + } + } + + /** Returns true if a1 is equal to a2. Either or both may be null. */ + public static boolean equal(Address a1, Address a2) { + if ((a1 == null) && (a2 == null)) { + return true; + } + + if ((a1 == null) || (a2 == null)) { + return false; + } + + return (a1.equals(a2)); + } + + /** Shorthand for {@link #lessThan} */ + public static boolean lt(Address a1, Address a2) { + return lessThan(a1, a2); + } + + /** Shorthand for {@link #lessThanOrEqual} */ + public static boolean lte(Address a1, Address a2) { + return lessThanOrEqual(a1, a2); + } + + /** Shorthand for {@link #greaterThan} */ + public static boolean gt(Address a1, Address a2) { + return greaterThan(a1, a2); + } + + /** Shorthand for {@link #greaterThanOrEqual} */ + public static boolean gte(Address a1, Address a2) { + return greaterThanOrEqual(a1, a2); + } + + /** Returns maximum of the two addresses */ + public static Address max(Address a1, Address a2) { + return (gt(a1, a2) ? a1 : a2); + } + + /** Returns minimum of the two addresses */ + public static Address min(Address a1, Address a2) { + return (lt(a1, a2) ? a1 : a2); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Assert.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Assert.java new file mode 100644 index 00000000000..62ae9b3e7aa --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Assert.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +public class Assert { + public static boolean ASSERTS_ENABLED = true; + + public static void that(boolean test, String message) { + if (!test) { + throw new AssertionFailure(message); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AssertionFailure.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AssertionFailure.java new file mode 100644 index 00000000000..72b2bf7f1cd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AssertionFailure.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +public class AssertionFailure extends RuntimeException { + public AssertionFailure() { + super(); + } + + public AssertionFailure(String message) { + super(message); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BasicHashtable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BasicHashtable.java new file mode 100644 index 00000000000..82ae165ca77 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BasicHashtable.java @@ -0,0 +1,79 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +// Superclass for symbol and string tables. + +public class BasicHashtable extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("BasicHashtable"); + tableSizeField = type.getCIntegerField("_table_size"); + bucketsField = type.getAddressField("_buckets"); + bucketSize = db.lookupType("HashtableBucket").getSize(); + } + + // Fields + private static CIntegerField tableSizeField; + private static AddressField bucketsField; + private static long bucketSize; + + // Accessors + protected int tableSize() { + return (int) tableSizeField.getValue(addr); + } + + protected BasicHashtableEntry bucket(int i) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(i >= 0 && i < tableSize(), "Invalid bucket id"); + } + Address tmp = bucketsField.getValue(addr); + tmp = tmp.addOffsetTo(i * bucketSize); + HashtableBucket bucket = (HashtableBucket) VMObjectFactory.newObject( + HashtableBucket.class, tmp); + return bucket.getEntry(getHashtableEntryClass()); + } + + // derived class may return Class + protected Class getHashtableEntryClass() { + return BasicHashtableEntry.class; + } + + public BasicHashtable(Address addr) { + super(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BasicHashtableEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BasicHashtableEntry.java new file mode 100644 index 00000000000..888d8976d8c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BasicHashtableEntry.java @@ -0,0 +1,80 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.ObjectHeap; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +public class BasicHashtableEntry extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("BasicHashtableEntry"); + hashField = type.getCIntegerField("_hash"); + nextField = type.getAddressField("_next"); + } + + // Fields + private static CIntegerField hashField; + private static AddressField nextField; + + // Accessors + public long hash() { + return hashField.getValue(addr) & 0xFFFFFFFFL; + } + + private long nextAddressValue() { + Debugger dbg = VM.getVM().getDebugger(); + Address nextValue = nextField.getValue(addr); + return (nextValue != null) ? dbg.getAddressValue(nextValue) : 0L; + } + + public boolean isShared() { + return (nextAddressValue() & 1L) != 0; + } + + public BasicHashtableEntry next() { + Address nextValue = nextField.getValue(addr); + Address next = (nextValue != null)? nextValue.andWithMask(-2L) : null; + // using this.getClass so that return type will be as expected in + // subclass context. But, because we can't use covariant return type + // caller has to use this next and cast the result to correct type. + return (BasicHashtableEntry) VMObjectFactory.newObject(this.getClass(), next); + } + + public BasicHashtableEntry(Address addr) { + super(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BitMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BitMap.java new file mode 100644 index 00000000000..779939eb108 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BitMap.java @@ -0,0 +1,215 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import sun.jvm.hotspot.debugger.*; + +/** Manages a bitmap of the specified bit size */ +public class BitMap { + public BitMap(int sizeInBits) { + this.size = sizeInBits; + int nofWords = sizeInWords(); + data = new int[nofWords]; + } + + public int size() { + return size; + } + + // Accessors + public boolean at(int offset) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(offset>=0 && offset < size(), "BitMap index out of bounds"); + } + return Bits.isSetNthBit(wordFor(offset), offset % bitsPerWord); + } + + public void atPut(int offset, boolean value) { + int index = indexFor(offset); + int pos = offset % bitsPerWord; + if (value) { + data[index] = Bits.setNthBit(data[index], pos); + } else { + data[index] = Bits.clearNthBit(data[index], pos); + } + } + + public void set_size(int value) { + size = value; + } + + public void set_map(Address addr) { + for (int i=0; i>> 1; + } + } + } + + /** Sets this bitmap to the logical union of it and the + argument. Both bitmaps must be the same size. Returns true if a + change was caused in this bitmap. */ + public boolean setUnion(BitMap other) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(size() == other.size(), "must have same size"); + } + boolean changed = false; + for (int index = 0; index < sizeInWords(); index++) { + int temp = data[index] | other.data[index]; + changed = changed || (temp != data[index]); + data[index] = temp; + } + return changed; + } + + /** Sets this bitmap to the logical intersection of it and the + argument. Both bitmaps must be the same size. */ + public void setIntersection(BitMap other) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(size() == other.size(), "must have same size"); + } + for (int index = 0; index < sizeInWords(); index++) { + data[index] = data[index] & (other.data[index]); + } + } + + /** Sets this bitmap to the contents of the argument. Both bitmaps + must be the same size. */ + public void setFrom(BitMap other) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(size() == other.size(), "must have same size"); + } + for (int index = 0; index < sizeInWords(); index++) { + data[index] = other.data[index]; + } + } + + /** Sets this bitmap to the logical difference between it and the + argument; that is, any bits that are set in the argument are + cleared in this bitmap. Both bitmaps must be the same size. + Returns true if a change was caused in this bitmap. */ + public boolean setDifference(BitMap other) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(size() == other.size(), "must have same size"); + } + boolean changed = false; + for (int index = 0; index < sizeInWords(); index++) { + int temp = data[index] & ~(other.data[index]); + changed = changed || (temp != data[index]); + data[index] = temp; + } + return changed; + } + + /** Both bitmaps must be the same size. */ + public boolean isSame(BitMap other) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(size() == other.size(), "must have same size"); + } + for (int index = 0; index < sizeInWords(); index++) { + if (data[index] != (other.data[index])) return false; + } + return true; + } + + public int getNextOneOffset(int l_offset, int r_offset) { + if (l_offset == r_offset) { + return l_offset; + } + + int index = indexFor(l_offset); + int r_index = indexFor(r_offset); + int res_offset = l_offset; + + int pos = bitInWord(res_offset); + int res = data[index] >> pos; + + if (res != 0) { + // find the position of the 1-bit + for (; (res & 1) == 0; res_offset++) { + res = res >> 1; + } + return res_offset; + } + // skip over all word length 0-bit runs + for (index++; index < r_index; index++) { + res = data[index]; + if (res != 0) { + // found a 1, return the offset + for (res_offset = index * bitsPerWord; (res & 1) == 0; res_offset++) { + res = res >> 1; + } + return res_offset; + } + } + return r_offset; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + private int size; // in bits + private int[] data; + private static final int bitsPerWord = 32; + private static final int bytesPerWord = 4; + + private int sizeInWords() { + return (size() + bitsPerWord - 1) / bitsPerWord; + } + + private int indexFor(int offset) { + return offset / bitsPerWord; + } + + private int wordFor(int offset) { + return data[offset / bitsPerWord]; + } + + private int bitInWord(int offset) { + return offset & (bitsPerWord - 1); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BitMapClosure.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BitMapClosure.java new file mode 100644 index 00000000000..3581459312f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/BitMapClosure.java @@ -0,0 +1,30 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +public interface BitMapClosure { + /** Called when specified bit in map is set */ + public void doBit(int offset); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Bits.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Bits.java new file mode 100644 index 00000000000..64f33d94dc6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Bits.java @@ -0,0 +1,83 @@ +/* + * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** Bit manipulation routines */ + +public class Bits { + public static final int AllBits = ~0; + public static final int NoBits = 0; + public static final int OneBit = 1; + + public static final int BitsPerByte = 8; + public static final int BitsPerInt = 32; + + public static final int LogBytesPerInt = 2; + public static final int LogBytesPerLong = 3; + + + public static int setBits(int x, int m) { + return x | m; + } + + public static int clearBits(int x, int m) { + return x & ~m; + } + + public static int nthBit(int n) { + return (n > 32) ? 0 : (OneBit << n); + } + + public static int setNthBit(int x, int n) { + return setBits(x, nthBit(n)); + } + + public static int clearNthBit(int x, int n) { + return clearBits(x, nthBit(n)); + } + + public static boolean isSetNthBit(int word, int n) { + return maskBits(word, nthBit(n)) != NoBits; + } + + public static int rightNBits(int n) { + return (nthBit(n) - 1); + } + + public static int maskBits(int x, int m) { + return x & m; + } + + public static long maskBitsLong(long x, long m) { + return x & m; + } + + /** Returns integer round-up to the nearest multiple of s (s must be + a power of two) */ + public static int roundTo(int x, int s) { + int m = s - 1; + return maskBits(x + m, ~m); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/CPPExpressions.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/CPPExpressions.java new file mode 100644 index 00000000000..d259e657cff --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/CPPExpressions.java @@ -0,0 +1,103 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.regex.*; + +/** Provides helper routines for parsing simple C++ expressions such + as "(JavaThread *) 0xf00" and "Universe::_collectedHeap". */ + +public class CPPExpressions { + private static Pattern castPattern; + + /** Represents a cast expression such as "(JavaThread *) + 0xf00". Returns both the type to which we are casting as well as + the address. */ + public static class CastExpr { + private String type; + private String address; + + private CastExpr(String type, String address) { + this.type = type; + this.address = address; + } + + public String getType() { + return type; + } + + public String getAddress() { + return address; + } + } + + /** Represents a static field expression such as "Universe::_collectedHeap". */ + public static class StaticFieldExpr { + private String containingType; + private String fieldName; + + private StaticFieldExpr(String containingType, String fieldName) { + this.containingType = containingType; + this.fieldName = fieldName; + } + + public String getContainingType() { + return containingType; + } + + public String getFieldName() { + return fieldName; + } + } + + /** Attempts to parse the given string into a CastExpr. Returns null + if the string did not match the supported pattern. */ + public static CastExpr parseCast(String expr) { + if (castPattern == null) { + castPattern = Pattern.compile("\\s*\\(\\s*([0-9A-Za-z:_]*)\\s*\\*\\s*\\)\\s*([0-9a-zA-Z]*)\\s*"); + } + Matcher matcher = castPattern.matcher(expr); + if (matcher.matches()) { + String type = matcher.group(1); + String addr = matcher.group(2); + return new CastExpr(type, addr); + } + return null; + } + + /** Attempts to parse the given string into a + StaticFieldExpr. Returns null if the string did not match the + supported pattern. */ + public static StaticFieldExpr parseStaticField(String expr) { + String sep = "::"; + int idx = expr.lastIndexOf(sep); + if (idx < 0) { + return null; + } + String containingType = expr.substring(0, idx); + String fieldName = expr.substring(idx + sep.length(), expr.length()); + return new StaticFieldExpr(containingType, fieldName); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/CStringUtilities.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/CStringUtilities.java new file mode 100644 index 00000000000..ba1fc5f600c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/CStringUtilities.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; +import java.util.*; + +import sun.jvm.hotspot.debugger.*; + +/** A utility class encapsulating useful operations on C strings + represented as Addresses */ + +public class CStringUtilities { + /** Return the length of a null-terminated ASCII string in the + remote process */ + public static int getStringLength(Address addr) { + int i = 0; + while (addr.getCIntegerAt(i, 1, false) != 0) { + i++; + } + return i; + } + + private static String encoding = System.getProperty("file.encoding", "US-ASCII"); + + /** Fetch a null-terminated ASCII string from the remote process. + Returns null if the argument is null, otherwise returns a + non-null string (for example, returns an empty string if the + first character fetched is the null terminator). */ + public static String getString(Address addr) { + if (addr == null) { + return null; + } + + List data = new ArrayList(); + byte val = 0; + long i = 0; + do { + val = (byte) addr.getCIntegerAt(i, 1, false); + if (val != 0) { + data.add(new Byte(val)); + } + ++i; + } while (val != 0); + + // Convert to byte[] and from there to String + byte[] bytes = new byte[data.size()]; + for (i = 0; i < data.size(); ++i) { + bytes[(int) i] = ((Byte) data.get((int) i)).byteValue(); + } + // FIXME: When we switch to use JDK 6 to build SA, + // we can change the following to just return: + // return new String(bytes, Charset.defaultCharset()); + try { + return new String(bytes, encoding); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Error converting bytes to String using " + encoding + " encoding", e); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstIterator.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstIterator.java new file mode 100644 index 00000000000..d57214a5d3d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstIterator.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.Iterator; + +/** An Iterator which wraps another and prevents the "remove" method + from being called */ + +public class ConstIterator implements Iterator { + private Iterator iter; + + public ConstIterator(Iterator iter) { + this.iter = iter; + } + + public boolean hasNext() { + return iter.hasNext(); + } + + public Object next() { + return iter.next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java new file mode 100644 index 00000000000..56ee4d44e48 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java @@ -0,0 +1,79 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +public class ConstantTag { + // These replicated from the VM to save space + private static int JVM_CONSTANT_Utf8 = 1; + private static int JVM_CONSTANT_Unicode = 2; // unused + private static int JVM_CONSTANT_Integer = 3; + private static int JVM_CONSTANT_Float = 4; + private static int JVM_CONSTANT_Long = 5; + private static int JVM_CONSTANT_Double = 6; + private static int JVM_CONSTANT_Class = 7; + private static int JVM_CONSTANT_String = 8; + private static int JVM_CONSTANT_Fieldref = 9; + private static int JVM_CONSTANT_Methodref = 10; + private static int JVM_CONSTANT_InterfaceMethodref = 11; + private static int JVM_CONSTANT_NameAndType = 12; + private static int JVM_CONSTANT_Invalid = 0; // For bad value initialization + private static int JVM_CONSTANT_UnresolvedClass = 100; // Temporary tag until actual use + private static int JVM_CONSTANT_ClassIndex = 101; // Temporary tag while constructing constant pool + private static int JVM_CONSTANT_UnresolvedString = 102; // Temporary tag until actual use + private static int JVM_CONSTANT_StringIndex = 103; // Temporary tag while constructing constant pool + private static int JVM_CONSTANT_UnresolvedClassInError = 104; // Resolution failed + + private byte tag; + + public ConstantTag(byte tag) { + this.tag = tag; + } + + public boolean isKlass() { return tag == JVM_CONSTANT_Class; } + public boolean isField () { return tag == JVM_CONSTANT_Fieldref; } + public boolean isMethod() { return tag == JVM_CONSTANT_Methodref; } + public boolean isInterfaceMethod() { return tag == JVM_CONSTANT_InterfaceMethodref; } + public boolean isString() { return tag == JVM_CONSTANT_String; } + public boolean isInt() { return tag == JVM_CONSTANT_Integer; } + public boolean isFloat() { return tag == JVM_CONSTANT_Float; } + public boolean isLong() { return tag == JVM_CONSTANT_Long; } + public boolean isDouble() { return tag == JVM_CONSTANT_Double; } + public boolean isNameAndType() { return tag == JVM_CONSTANT_NameAndType; } + public boolean isUtf8() { return tag == JVM_CONSTANT_Utf8; } + + public boolean isInvalid() { return tag == JVM_CONSTANT_Invalid; } + + public boolean isUnresolvedKlass() { + return tag == JVM_CONSTANT_UnresolvedClass || tag == JVM_CONSTANT_UnresolvedClassInError; + } + public boolean isUnresolveKlassInError() { return tag == JVM_CONSTANT_UnresolvedClassInError; } + public boolean isKlassIndex() { return tag == JVM_CONSTANT_ClassIndex; } + public boolean isUnresolvedString() { return tag == JVM_CONSTANT_UnresolvedString; } + public boolean isStringIndex() { return tag == JVM_CONSTANT_StringIndex; } + + public boolean isKlassReference() { return isKlassIndex() || isUnresolvedKlass(); } + public boolean isFieldOrMethod() { return isField() || isMethod() || isInterfaceMethod(); } + public boolean isSymbol() { return isUtf8(); } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/FindObjectByType.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/FindObjectByType.java new file mode 100644 index 00000000000..27e8933da96 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/FindObjectByType.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.*; + +import sun.jvm.hotspot.oops.*; + +public class FindObjectByType implements HeapVisitor { + private Klass type; + private List results = new ArrayList(); + + public FindObjectByType(Klass type) { + this.type = type; + } + + /** Returns a List of Oops */ + public List getResults() { + return results; + } + + public void prologue(long size) {} + public void epilogue() {} + + public boolean doObj(Oop obj) { + if (obj.getKlass().equals(type)) { + results.add(obj); + } + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Hashtable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Hashtable.java new file mode 100644 index 00000000000..1fb4ed4f181 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Hashtable.java @@ -0,0 +1,70 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +public class Hashtable extends BasicHashtable { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + // just to confirm that type exists + Type type = db.lookupType("Hashtable"); + } + + // derived class may return Class + protected Class getHashtableEntryClass() { + return HashtableEntry.class; + } + + public int hashToIndex(long fullHash) { + return (int) (fullHash % tableSize()); + } + + public Hashtable(Address addr) { + super(addr); + } + + // VM's Hashtable::hash_symbol + protected static long hashSymbol(byte[] buf) { + long h = 0; + int s = 0; + int len = buf.length; + while (len-- > 0) { + h = 31*h + (0xFFL & buf[s]); + s++; + } + return h & 0xFFFFFFFFL; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HashtableBucket.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HashtableBucket.java new file mode 100644 index 00000000000..4c167e684f5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HashtableBucket.java @@ -0,0 +1,62 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +public class HashtableBucket extends VMObject { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("HashtableBucket"); + entryField = type.getAddressField("_entry"); + } + + // Field + private static AddressField entryField; + + // Accessor - accepts Class + public BasicHashtableEntry getEntry(Class clazz) { + Address tmp = entryField.getValue(addr); + return (BasicHashtableEntry) VMObjectFactory.newObject(clazz, tmp); + } + + public BasicHashtableEntry entry() { + return getEntry(HashtableEntry.class); + } + + public HashtableBucket(Address addr) { + super(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HashtableEntry.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HashtableEntry.java new file mode 100644 index 00000000000..4a9910a00cb --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HashtableEntry.java @@ -0,0 +1,59 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.ObjectHeap; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.runtime.*; + +public class HashtableEntry extends BasicHashtableEntry { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("HashtableEntry"); + literalField = type.getOopField("_literal"); + } + + // Fields + private static OopField literalField; + + // Accessors + public Oop literal() { + return VM.getVM().getObjectHeap().newOop(literalField.getValue(addr)); + } + + public HashtableEntry(Address addr) { + super(addr); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapGXLWriter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapGXLWriter.java new file mode 100644 index 00000000000..cfda833c12b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapGXLWriter.java @@ -0,0 +1,410 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/** + *

This class writes Java heap in Graph eXchange Language (GXL) + * format. GXL is an open standard for serializing arbitrary graphs in + * XML syntax.

+ * + *

A GXL document contains one or more graphs. A graph contains + * nodes and edges. Both nodes and edges can have attributes. graphs, + * nodes, edges and attributes are represented by XML elements graph, + * node, edge and attr respectively. Attributes can be typed. GXL + * supports locator, bool, int, float, bool, string, enum as well as + * set, seq, bag, tup types. Nodes must have a XML attribute 'id' that + * is unique id of the node in the GXL document. Edges must have + * 'from' and 'to' XML attributes that are ids of from and to nodes.

+ * + *

Java heap to GXL document mapping:

+ *
    + *
  • Java object - GXL node. + *
  • Java primitive field - GXL attribute (type mapping below). + *
  • Java reference field - GXL edge from referee to referent node. + *
  • Java primitive array - GXL node with seq type attribute. + *
  • Java char array - GXL node with one attribute of string type. + *
  • Java object array - GXL node and 'length' edges. + *
+ * + *

Java primitive to GXL type mapping:

+ *
    + *
  • Java byte, int, short, long - GXL int attribute + *
  • Java float, double - GXL float attribute + *
  • Java boolean - GXL bool atttribute + *
  • Java char - GXL string attribute + *
+ * + * Exact Java primitive type code is written in 'kind' attribute of + * 'attr' element. Type code is specified in JVM spec. second edition + * section 4.3.2 (Field Descriptor). + * + * @see GXL + * @see GXL DTD + */ + +public class HeapGXLWriter extends AbstractHeapGraphWriter { + public void write(String fileName) throws IOException { + out = new PrintWriter(new BufferedWriter(new FileWriter(fileName))); + super.write(); + if (out.checkError()) { + throw new IOException(); + } + out.flush(); + } + + protected void writeHeapHeader() throws IOException { + // XML processing instruction + out.print(""); + + out.println(""); + out.println(""); + + // document properties + writeAttribute("creation-date", "string", new Date().toString()); + + // write VM info + writeVMInfo(); + + // emit a node for null + out.print(""); + } + + protected void writeObjectHeader(Oop oop) throws IOException { + refFields = new ArrayList(); + isArray = oop.isArray(); + + // generate an edge for instanceof relation + // between object node and it's class node. + writeEdge(oop, oop.getKlass().getJavaMirror(), "instanceof"); + + out.print(""); + } + + protected void writeObjectFooter(Oop oop) throws IOException { + out.println(""); + + // write the reference fields as edges + for (Iterator itr = refFields.iterator(); itr.hasNext();) { + OopField field = (OopField) itr.next(); + Oop ref = field.getValue(oop); + + String name = field.getID().getName(); + if (isArray) { + // for arrays elements we use element pattern + name = "element" + name; + } else { + name = identifierToXMLName(name); + } + writeEdge(oop, ref, name); + } + refFields = null; + } + + protected void writeObjectArray(ObjArray array) throws IOException { + writeObjectHeader(array); + writeArrayLength(array); + writeObjectFields(array); + writeObjectFooter(array); + } + + protected void writePrimitiveArray(TypeArray array) + throws IOException { + writeObjectHeader(array); + // write array length + writeArrayLength(array); + // write array elements + out.println("\t"); + TypeArrayKlass klass = (TypeArrayKlass) array.getKlass(); + if (klass.getElementType() == TypeArrayKlass.T_CHAR) { + // char[] special treatment -- write it as string + out.print("\t"); + out.print(escapeXMLChars(OopUtilities.charArrayToString(array))); + out.println(""); + } else { + out.println("\t"); + writeObjectFields(array); + out.println("\t"); + } + out.println("\t"); + writeObjectFooter(array); + } + + protected void writeClass(Instance instance) throws IOException { + writeObjectHeader(instance); + Klass reflectedType = OopUtilities.classOopToKlass(instance); + boolean isInstanceKlass = (reflectedType instanceof InstanceKlass); + // reflectedType is null for primitive types (int.class etc). + if (reflectedType != null) { + Symbol name = reflectedType.getName(); + if (name != null) { + // write class name as an attribute + writeAttribute("class-name", "string", name.asString()); + } + if (isInstanceKlass) { + // write object-size as an attribute + long sizeInBytes = reflectedType.getLayoutHelper(); + writeAttribute("object-size", "int", + Long.toString(sizeInBytes)); + // write static fields of this class. + writeObjectFields(reflectedType); + } + } + out.println(""); + + // write edges for super class and direct interfaces + if (reflectedType != null) { + Klass superType = reflectedType.getSuper(); + Oop superMirror = (superType == null)? + null : superType.getJavaMirror(); + writeEdge(instance, superMirror, "extends"); + if (isInstanceKlass) { + // write edges for directly implemented interfaces + InstanceKlass ik = (InstanceKlass) reflectedType; + ObjArray interfaces = ik.getLocalInterfaces(); + final int len = (int) interfaces.getLength(); + for (int i = 0; i < len; i++) { + Klass k = (Klass) interfaces.getObjAt(i); + writeEdge(instance, k.getJavaMirror(), "implements"); + } + + // write loader + Oop loader = ik.getClassLoader(); + writeEdge(instance, loader, "loaded-by"); + + // write signers + Oop signers = ik.getSigners(); + writeEdge(instance, signers, "signed-by"); + + // write protection domain + Oop protectionDomain = ik.getProtectionDomain(); + writeEdge(instance, protectionDomain, "protection-domain"); + + // write edges for static reference fields from this class + for (Iterator itr = refFields.iterator(); itr.hasNext();) { + OopField field = (OopField) itr.next(); + Oop ref = field.getValue(reflectedType); + String name = field.getID().getName(); + writeEdge(instance, ref, identifierToXMLName(name)); + } + } + } + refFields = null; + } + + protected void writeReferenceField(Oop oop, OopField field) + throws IOException { + refFields.add(field); + } + + protected void writeByteField(Oop oop, ByteField field) + throws IOException { + writeField(field, "int", "B", Byte.toString(field.getValue(oop))); + } + + protected void writeCharField(Oop oop, CharField field) + throws IOException { + writeField(field, "string", "C", + escapeXMLChars(Character.toString(field.getValue(oop)))); + } + + protected void writeBooleanField(Oop oop, BooleanField field) + throws IOException { + writeField(field, "bool", "Z", Boolean.toString(field.getValue(oop))); + } + + protected void writeShortField(Oop oop, ShortField field) + throws IOException { + writeField(field, "int", "S", Short.toString(field.getValue(oop))); + } + + protected void writeIntField(Oop oop, IntField field) + throws IOException { + writeField(field, "int", "I", Integer.toString(field.getValue(oop))); + } + + protected void writeLongField(Oop oop, LongField field) + throws IOException { + writeField(field, "int", "J", Long.toString(field.getValue(oop))); + } + + protected void writeFloatField(Oop oop, FloatField field) + throws IOException { + writeField(field, "float", "F", Float.toString(field.getValue(oop))); + } + + protected void writeDoubleField(Oop oop, DoubleField field) + throws IOException { + writeField(field, "float", "D", Double.toString(field.getValue(oop))); + } + + protected void writeHeapFooter() throws IOException { + out.println(""); + out.println(""); + } + + //-- Internals only below this point + + // Java identifier to XML NMTOKEN type string + private static String identifierToXMLName(String name) { + // for now, just replace '$' with '_' + return name.replace('$', '_'); + } + + // escapes XML meta-characters and illegal characters + private static String escapeXMLChars(String s) { + // FIXME: is there a better way or API? + StringBuffer result = null; + for(int i = 0, max = s.length(), delta = 0; i < max; i++) { + char c = s.charAt(i); + String replacement = null; + if (c == '&') { + replacement = "&"; + } else if (c == '<') { + replacement = "<"; + } else if (c == '>') { + replacement = ">"; + } else if (c == '"') { + replacement = """; + } else if (c == '\'') { + replacement = "'"; + } else if (c < '\u0020' || (c > '\ud7ff' && c < '\ue000') || + c == '\ufffe' || c == '\uffff') { + // These are illegal in XML -- put these in a CDATA section. + // Refer to section 2.2 Characters in XML specification at + // http://www.w3.org/TR/2004/REC-xml-20040204/ + replacement = ""; + } + + if (replacement != null) { + if (result == null) { + result = new StringBuffer(s); + } + result.replace(i + delta, i + delta + 1, replacement); + delta += (replacement.length() - 1); + } + } + if (result == null) { + return s; + } + return result.toString(); + } + + private static String getID(Oop oop) { + // address as unique id for node -- prefixed by "ID_". + if (oop == null) { + return "ID_NULL"; + } else { + return "ID_" + oop.getHandle().toString(); + } + } + + private void writeArrayLength(Array array) throws IOException { + writeAttribute("length", "int", + Integer.toString((int) array.getLength())); + } + + private void writeAttribute(String name, String type, String value) { + out.print("\t<"); + out.print(type); + out.print('>'); + out.print(value); + out.print(""); + } + + private void writeEdge(Oop from, Oop to, String name) throws IOException { + out.print(""); + writeAttribute("name", "string", name); + out.println(""); + } + + private void writeField(Field field, String type, String kind, + String value) throws IOException { + // 'type' is GXL type of the attribute + // 'kind' is Java type code ("B", "C", "Z", "S", "I", "J", "F", "D") + if (isArray) { + out.print('\t'); + } else { + out.print("\t"); + } + out.print('<'); + out.print(type); + out.print('>'); + out.print(value); + out.print("'); + if (isArray) { + out.println(); + } else { + out.println(""); + } + } + + private void writeVMInfo() throws IOException { + VM vm = VM.getVM(); + writeAttribute("vm-version", "string", vm.getVMRelease()); + writeAttribute("vm-type", "string", + (vm.isClientCompiler())? "client" : + ((vm.isServerCompiler())? "server" : "core")); + writeAttribute("os", "string", vm.getOS()); + writeAttribute("cpu", "string", vm.getCPU()); + writeAttribute("pointer-size", "string", + Integer.toString((int)vm.getOopSize() * 8)); + } + + // XML encoding that we'll use + private static final String ENCODING = "UTF-8"; + + // reference fields of currently visited object + private List/**/ refFields; + // are we writing an array now? + private boolean isArray; + private PrintWriter out; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapGraphWriter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapGraphWriter.java new file mode 100644 index 00000000000..868918b0084 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapGraphWriter.java @@ -0,0 +1,36 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; + +/** + * Interface implemented by all heap graph writers. + */ + +public interface HeapGraphWriter { + /** writes the heap graph in the given file */ + public void write(String fileName) throws IOException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java new file mode 100644 index 00000000000..c1eb5aa467a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java @@ -0,0 +1,1003 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; +import java.nio.channels.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/* + * This class writes Java heap in hprof binary format. This format is + * used by Heap Analysis Tool (HAT). The class is heavily influenced + * by 'hprof_io.c' of 1.5 new hprof implementation. + */ + +/* hprof binary format: (result either written to a file or sent over + * the network). + * + * WARNING: This format is still under development, and is subject to + * change without notice. + * + * header "JAVA PROFILE 1.0.1" (0-terminated) + * u4 size of identifiers. Identifiers are used to represent + * UTF8 strings, objects, stack traces, etc. They usually + * have the same size as host pointers. For example, on + * Solaris and Win32, the size is 4. + * u4 high word + * u4 low word number of milliseconds since 0:00 GMT, 1/1/70 + * [record]* a sequence of records. + * + */ + +/* + * + * Record format: + * + * u1 a TAG denoting the type of the record + * u4 number of *microseconds* since the time stamp in the + * header. (wraps around in a little more than an hour) + * u4 number of bytes *remaining* in the record. Note that + * this number excludes the tag and the length field itself. + * [u1]* BODY of the record (a sequence of bytes) + */ + +/* + * The following TAGs are supported: + * + * TAG BODY notes + *---------------------------------------------------------- + * HPROF_UTF8 a UTF8-encoded name + * + * id name ID + * [u1]* UTF8 characters (no trailing zero) + * + * HPROF_LOAD_CLASS a newly loaded class + * + * u4 class serial number (> 0) + * id class object ID + * u4 stack trace serial number + * id class name ID + * + * HPROF_UNLOAD_CLASS an unloading class + * + * u4 class serial_number + * + * HPROF_FRAME a Java stack frame + * + * id stack frame ID + * id method name ID + * id method signature ID + * id source file name ID + * u4 class serial number + * i4 line number. >0: normal + * -1: unknown + * -2: compiled method + * -3: native method + * + * HPROF_TRACE a Java stack trace + * + * u4 stack trace serial number + * u4 thread serial number + * u4 number of frames + * [id]* stack frame IDs + * + * + * HPROF_ALLOC_SITES a set of heap allocation sites, obtained after GC + * + * u2 flags 0x0001: incremental vs. complete + * 0x0002: sorted by allocation vs. live + * 0x0004: whether to force a GC + * u4 cutoff ratio + * u4 total live bytes + * u4 total live instances + * u8 total bytes allocated + * u8 total instances allocated + * u4 number of sites that follow + * [u1 is_array: 0: normal object + * 2: object array + * 4: boolean array + * 5: char array + * 6: float array + * 7: double array + * 8: byte array + * 9: short array + * 10: int array + * 11: long array + * u4 class serial number (may be zero during startup) + * u4 stack trace serial number + * u4 number of bytes alive + * u4 number of instances alive + * u4 number of bytes allocated + * u4]* number of instance allocated + * + * HPROF_START_THREAD a newly started thread. + * + * u4 thread serial number (> 0) + * id thread object ID + * u4 stack trace serial number + * id thread name ID + * id thread group name ID + * id thread group parent name ID + * + * HPROF_END_THREAD a terminating thread. + * + * u4 thread serial number + * + * HPROF_HEAP_SUMMARY heap summary + * + * u4 total live bytes + * u4 total live instances + * u8 total bytes allocated + * u8 total instances allocated + * + * HPROF_HEAP_DUMP denote a heap dump + * + * [heap dump sub-records]* + * + * There are four kinds of heap dump sub-records: + * + * u1 sub-record type + * + * HPROF_GC_ROOT_UNKNOWN unknown root + * + * id object ID + * + * HPROF_GC_ROOT_THREAD_OBJ thread object + * + * id thread object ID (may be 0 for a + * thread newly attached through JNI) + * u4 thread sequence number + * u4 stack trace sequence number + * + * HPROF_GC_ROOT_JNI_GLOBAL JNI global ref root + * + * id object ID + * id JNI global ref ID + * + * HPROF_GC_ROOT_JNI_LOCAL JNI local ref + * + * id object ID + * u4 thread serial number + * u4 frame # in stack trace (-1 for empty) + * + * HPROF_GC_ROOT_JAVA_FRAME Java stack frame + * + * id object ID + * u4 thread serial number + * u4 frame # in stack trace (-1 for empty) + * + * HPROF_GC_ROOT_NATIVE_STACK Native stack + * + * id object ID + * u4 thread serial number + * + * HPROF_GC_ROOT_STICKY_CLASS System class + * + * id object ID + * + * HPROF_GC_ROOT_THREAD_BLOCK Reference from thread block + * + * id object ID + * u4 thread serial number + * + * HPROF_GC_ROOT_MONITOR_USED Busy monitor + * + * id object ID + * + * HPROF_GC_CLASS_DUMP dump of a class object + * + * id class object ID + * u4 stack trace serial number + * id super class object ID + * id class loader object ID + * id signers object ID + * id protection domain object ID + * id reserved + * id reserved + * + * u4 instance size (in bytes) + * + * u2 size of constant pool + * [u2, constant pool index, + * ty, type + * 2: object + * 4: boolean + * 5: char + * 6: float + * 7: double + * 8: byte + * 9: short + * 10: int + * 11: long + * vl]* and value + * + * u2 number of static fields + * [id, static field name, + * ty, type, + * vl]* and value + * + * u2 number of inst. fields (not inc. super) + * [id, instance field name, + * ty]* type + * + * HPROF_GC_INSTANCE_DUMP dump of a normal object + * + * id object ID + * u4 stack trace serial number + * id class object ID + * u4 number of bytes that follow + * [vl]* instance field values (class, followed + * by super, super's super ...) + * + * HPROF_GC_OBJ_ARRAY_DUMP dump of an object array + * + * id array object ID + * u4 stack trace serial number + * u4 number of elements + * id array class ID + * [id]* elements + * + * HPROF_GC_PRIM_ARRAY_DUMP dump of a primitive array + * + * id array object ID + * u4 stack trace serial number + * u4 number of elements + * u1 element type + * 4: boolean array + * 5: char array + * 6: float array + * 7: double array + * 8: byte array + * 9: short array + * 10: int array + * 11: long array + * [u1]* elements + * + * HPROF_CPU_SAMPLES a set of sample traces of running threads + * + * u4 total number of samples + * u4 # of traces + * [u4 # of samples + * u4]* stack trace serial number + * + * HPROF_CONTROL_SETTINGS the settings of on/off switches + * + * u4 0x00000001: alloc traces on/off + * 0x00000002: cpu sampling on/off + * u2 stack trace depth + * + */ + +public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + // hprof binary file header + private static final String HPROF_HEADER = "JAVA PROFILE 1.0.1"; + + // constants in enum HprofTag + private static final int HPROF_UTF8 = 0x01; + private static final int HPROF_LOAD_CLASS = 0x02; + private static final int HPROF_UNLOAD_CLASS = 0x03; + private static final int HPROF_FRAME = 0x04; + private static final int HPROF_TRACE = 0x05; + private static final int HPROF_ALLOC_SITES = 0x06; + private static final int HPROF_HEAP_SUMMARY = 0x07; + private static final int HPROF_START_THREAD = 0x0A; + private static final int HPROF_END_THREAD = 0x0B; + private static final int HPROF_HEAP_DUMP = 0x0C; + private static final int HPROF_CPU_SAMPLES = 0x0D; + private static final int HPROF_CONTROL_SETTINGS = 0x0E; + + // Heap dump constants + // constants in enum HprofGcTag + private static final int HPROF_GC_ROOT_UNKNOWN = 0xFF; + private static final int HPROF_GC_ROOT_JNI_GLOBAL = 0x01; + private static final int HPROF_GC_ROOT_JNI_LOCAL = 0x02; + private static final int HPROF_GC_ROOT_JAVA_FRAME = 0x03; + private static final int HPROF_GC_ROOT_NATIVE_STACK = 0x04; + private static final int HPROF_GC_ROOT_STICKY_CLASS = 0x05; + private static final int HPROF_GC_ROOT_THREAD_BLOCK = 0x06; + private static final int HPROF_GC_ROOT_MONITOR_USED = 0x07; + private static final int HPROF_GC_ROOT_THREAD_OBJ = 0x08; + private static final int HPROF_GC_CLASS_DUMP = 0x20; + private static final int HPROF_GC_INSTANCE_DUMP = 0x21; + private static final int HPROF_GC_OBJ_ARRAY_DUMP = 0x22; + private static final int HPROF_GC_PRIM_ARRAY_DUMP = 0x23; + + // constants in enum HprofType + private static final int HPROF_ARRAY_OBJECT = 1; + private static final int HPROF_NORMAL_OBJECT = 2; + private static final int HPROF_BOOLEAN = 4; + private static final int HPROF_CHAR = 5; + private static final int HPROF_FLOAT = 6; + private static final int HPROF_DOUBLE = 7; + private static final int HPROF_BYTE = 8; + private static final int HPROF_SHORT = 9; + private static final int HPROF_INT = 10; + private static final int HPROF_LONG = 11; + + // Java type codes + private static final int JVM_SIGNATURE_BOOLEAN = 'Z'; + private static final int JVM_SIGNATURE_CHAR = 'C'; + private static final int JVM_SIGNATURE_BYTE = 'B'; + private static final int JVM_SIGNATURE_SHORT = 'S'; + private static final int JVM_SIGNATURE_INT = 'I'; + private static final int JVM_SIGNATURE_LONG = 'J'; + private static final int JVM_SIGNATURE_FLOAT = 'F'; + private static final int JVM_SIGNATURE_DOUBLE = 'D'; + private static final int JVM_SIGNATURE_ARRAY = '['; + private static final int JVM_SIGNATURE_CLASS = 'L'; + + + public synchronized void write(String fileName) throws IOException { + // open file stream and create buffered data output stream + FileOutputStream fos = new FileOutputStream(fileName); + FileChannel chn = fos.getChannel(); + out = new DataOutputStream(new BufferedOutputStream(fos)); + + VM vm = VM.getVM(); + dbg = vm.getDebugger(); + objectHeap = vm.getObjectHeap(); + symTbl = vm.getSymbolTable(); + + OBJ_ID_SIZE = (int) vm.getOopSize(); + + BOOLEAN_BASE_OFFSET = TypeArray.baseOffsetInBytes(BasicType.T_BOOLEAN); + BYTE_BASE_OFFSET = TypeArray.baseOffsetInBytes(BasicType.T_BYTE); + CHAR_BASE_OFFSET = TypeArray.baseOffsetInBytes(BasicType.T_CHAR); + SHORT_BASE_OFFSET = TypeArray.baseOffsetInBytes(BasicType.T_SHORT); + INT_BASE_OFFSET = TypeArray.baseOffsetInBytes(BasicType.T_INT); + LONG_BASE_OFFSET = TypeArray.baseOffsetInBytes(BasicType.T_LONG); + FLOAT_BASE_OFFSET = TypeArray.baseOffsetInBytes(BasicType.T_FLOAT); + DOUBLE_BASE_OFFSET = TypeArray.baseOffsetInBytes(BasicType.T_DOUBLE); + OBJECT_BASE_OFFSET = TypeArray.baseOffsetInBytes(BasicType.T_OBJECT); + + BOOLEAN_SIZE = objectHeap.getBooleanSize(); + BYTE_SIZE = objectHeap.getByteSize(); + CHAR_SIZE = objectHeap.getCharSize(); + SHORT_SIZE = objectHeap.getShortSize(); + INT_SIZE = objectHeap.getIntSize(); + LONG_SIZE = objectHeap.getLongSize(); + FLOAT_SIZE = objectHeap.getFloatSize(); + DOUBLE_SIZE = objectHeap.getDoubleSize(); + + // hprof bin format header + writeFileHeader(); + + // dummy stack trace without any frames so that + // HAT can be run without -stack false option + writeDummyTrace(); + + // hprof UTF-8 symbols section + writeSymbols(); + // HPROF_LOAD_CLASS records for all classes + writeClasses(); + + // write heap data now + out.writeByte((byte)HPROF_HEAP_DUMP); + out.writeInt(0); // relative timestamp + + // remember position of dump length, we will fixup + // length later - hprof format requires length. + out.flush(); + long dumpStart = chn.position(); + + // write dummy length of 0 and we'll fix it later. + out.writeInt(0); + + // write CLASS_DUMP records + writeClassDumpRecords(); + + // this will write heap data into the buffer stream + super.write(); + + // flush buffer stream and throw it. + out.flush(); + out = null; + + // now get current position to calculate length + long dumpEnd = chn.position(); + // calculate length of heap data + int dumpLen = (int) (dumpEnd - dumpStart - 4); + + // seek the position to write length + chn.position(dumpStart); + + // write length as integer + fos.write((dumpLen >>> 24) & 0xFF); + fos.write((dumpLen >>> 16) & 0xFF); + fos.write((dumpLen >>> 8) & 0xFF); + fos.write((dumpLen >>> 0) & 0xFF); + + // close the file stream + fos.close(); + } + + private void writeClassDumpRecords() throws IOException { + SystemDictionary sysDict = VM.getVM().getSystemDictionary(); + try { + sysDict.allClassesDo(new SystemDictionary.ClassVisitor() { + public void visit(Klass k) { + try { + writeClassDumpRecord(k); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + } catch (RuntimeException re) { + handleRuntimeException(re); + } + } + + protected void writeClass(Instance instance) throws IOException { + Klass reflectedKlass = OopUtilities.classOopToKlass(instance); + // dump instance record only for primitive type Class objects. + // all other Class objects are covered by writeClassDumpRecords. + if (reflectedKlass == null) { + writeInstance(instance); + } + } + + private void writeClassDumpRecord(Klass k) throws IOException { + out.writeByte((byte)HPROF_GC_CLASS_DUMP); + writeObjectID(k.getJavaMirror()); + out.writeInt(DUMMY_STACK_TRACE_ID); + Klass superKlass = k.getJavaSuper(); + if (superKlass != null) { + writeObjectID(superKlass.getJavaMirror()); + } else { + writeObjectID(null); + } + + if (k instanceof InstanceKlass) { + InstanceKlass ik = (InstanceKlass) k; + writeObjectID(ik.getClassLoader()); + writeObjectID(ik.getSigners()); + writeObjectID(ik.getProtectionDomain()); + // two reserved id fields + writeObjectID(null); + writeObjectID(null); + List fields = getInstanceFields(ik); + int instSize = getSizeForFields(fields); + classDataCache.put(ik, new ClassData(instSize, fields)); + out.writeInt(instSize); + + // For now, ignore constant pool - HAT ignores too! + // output number of cp entries as zero. + out.writeShort((short) 0); + + List declaredFields = ik.getImmediateFields(); + List staticFields = new ArrayList(); + List instanceFields = new ArrayList(); + Iterator itr = null; + for (itr = declaredFields.iterator(); itr.hasNext();) { + Field field = (Field) itr.next(); + if (field.isStatic()) { + staticFields.add(field); + } else { + instanceFields.add(field); + } + } + + // dump static field descriptors + writeFieldDescriptors(staticFields, ik); + + // dump instance field descriptors + writeFieldDescriptors(instanceFields, null); + } else { + if (k instanceof ObjArrayKlass) { + ObjArrayKlass oak = (ObjArrayKlass) k; + Klass bottomKlass = oak.getBottomKlass(); + if (bottomKlass instanceof InstanceKlass) { + InstanceKlass ik = (InstanceKlass) bottomKlass; + writeObjectID(ik.getClassLoader()); + writeObjectID(ik.getSigners()); + writeObjectID(ik.getProtectionDomain()); + } else { + writeObjectID(null); + writeObjectID(null); + writeObjectID(null); + } + } else { + writeObjectID(null); + writeObjectID(null); + writeObjectID(null); + } + // two reserved id fields + writeObjectID(null); + writeObjectID(null); + // write zero instance size -- as instance size + // is variable for arrays. + out.writeInt(0); + // no constant pool for array klasses + out.writeShort((short) 0); + // no static fields for array klasses + out.writeShort((short) 0); + // no instance fields for array klasses + out.writeShort((short) 0); + } + } + + protected void writeJavaThread(JavaThread jt, int index) throws IOException { + out.writeByte((byte) HPROF_GC_ROOT_THREAD_OBJ); + writeObjectID(jt.getThreadObj()); + out.writeInt(index); + out.writeInt(DUMMY_STACK_TRACE_ID); + writeLocalJNIHandles(jt, index); + } + + protected void writeLocalJNIHandles(JavaThread jt, int index) throws IOException { + final int threadIndex = index; + JNIHandleBlock blk = jt.activeHandles(); + if (blk != null) { + try { + blk.oopsDo(new AddressVisitor() { + public void visitAddress(Address handleAddr) { + try { + if (handleAddr != null) { + OopHandle oopHandle = handleAddr.getOopHandleAt(0); + Oop oop = objectHeap.newOop(oopHandle); + // exclude JNI handles hotspot internal objects + if (oop != null && isJavaVisible(oop)) { + out.writeByte((byte) HPROF_GC_ROOT_JNI_LOCAL); + writeObjectID(oop); + out.writeInt(threadIndex); + out.writeInt(EMPTY_FRAME_DEPTH); + } + } + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + }); + } catch (RuntimeException re) { + handleRuntimeException(re); + } + } + } + + protected void writeGlobalJNIHandle(Address handleAddr) throws IOException { + OopHandle oopHandle = handleAddr.getOopHandleAt(0); + Oop oop = objectHeap.newOop(oopHandle); + // exclude JNI handles of hotspot internal objects + if (oop != null && isJavaVisible(oop)) { + out.writeByte((byte) HPROF_GC_ROOT_JNI_GLOBAL); + writeObjectID(oop); + // use JNIHandle address as ID + writeObjectID(getAddressValue(handleAddr)); + } + } + + protected void writeObjectArray(ObjArray array) throws IOException { + out.writeByte((byte) HPROF_GC_OBJ_ARRAY_DUMP); + writeObjectID(array); + out.writeInt(DUMMY_STACK_TRACE_ID); + out.writeInt((int) array.getLength()); + writeObjectID(array.getKlass().getJavaMirror()); + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { + long offset = OBJECT_BASE_OFFSET + index * OBJ_ID_SIZE; + OopHandle handle = array.getHandle().getOopHandleAt(offset); + writeObjectID(getAddressValue(handle)); + } + } + + protected void writePrimitiveArray(TypeArray array) throws IOException { + out.writeByte((byte) HPROF_GC_PRIM_ARRAY_DUMP); + writeObjectID(array); + out.writeInt(DUMMY_STACK_TRACE_ID); + out.writeInt((int) array.getLength()); + TypeArrayKlass tak = (TypeArrayKlass) array.getKlass(); + final int type = (int) tak.getElementType(); + out.writeByte((byte) type); + switch (type) { + case TypeArrayKlass.T_BOOLEAN: + writeBooleanArray(array); + break; + case TypeArrayKlass.T_CHAR: + writeCharArray(array); + break; + case TypeArrayKlass.T_FLOAT: + writeFloatArray(array); + break; + case TypeArrayKlass.T_DOUBLE: + writeDoubleArray(array); + break; + case TypeArrayKlass.T_BYTE: + writeByteArray(array); + break; + case TypeArrayKlass.T_SHORT: + writeShortArray(array); + break; + case TypeArrayKlass.T_INT: + writeIntArray(array); + break; + case TypeArrayKlass.T_LONG: + writeLongArray(array); + break; + default: + throw new RuntimeException("should not reach here"); + } + } + + private void writeBooleanArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { + long offset = BOOLEAN_BASE_OFFSET + index * BOOLEAN_SIZE; + out.writeBoolean(array.getHandle().getJBooleanAt(offset)); + } + } + + private void writeByteArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { + long offset = BYTE_BASE_OFFSET + index * BYTE_SIZE; + out.writeByte(array.getHandle().getJByteAt(offset)); + } + } + + private void writeShortArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { + long offset = SHORT_BASE_OFFSET + index * SHORT_SIZE; + out.writeShort(array.getHandle().getJShortAt(offset)); + } + } + + private void writeIntArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { + long offset = INT_BASE_OFFSET + index * INT_SIZE; + out.writeInt(array.getHandle().getJIntAt(offset)); + } + } + + private void writeLongArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { + long offset = LONG_BASE_OFFSET + index * LONG_SIZE; + out.writeLong(array.getHandle().getJLongAt(offset)); + } + } + + private void writeCharArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { + long offset = CHAR_BASE_OFFSET + index * CHAR_SIZE; + out.writeChar(array.getHandle().getJCharAt(offset)); + } + } + + private void writeFloatArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { + long offset = FLOAT_BASE_OFFSET + index * FLOAT_SIZE; + out.writeFloat(array.getHandle().getJFloatAt(offset)); + } + } + + private void writeDoubleArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { + long offset = DOUBLE_BASE_OFFSET + index * DOUBLE_SIZE; + out.writeDouble(array.getHandle().getJDoubleAt(offset)); + } + } + + protected void writeInstance(Instance instance) throws IOException { + out.writeByte((byte) HPROF_GC_INSTANCE_DUMP); + writeObjectID(instance); + out.writeInt(DUMMY_STACK_TRACE_ID); + Klass klass = instance.getKlass(); + writeObjectID(klass.getJavaMirror()); + + ClassData cd = (ClassData) classDataCache.get(klass); + if (Assert.ASSERTS_ENABLED) { + Assert.that(cd != null, "can not get class data for " + klass.getName().asString() + klass.getHandle()); + } + List fields = cd.fields; + int size = cd.instSize; + out.writeInt(size); + for (Iterator itr = fields.iterator(); itr.hasNext();) { + writeField((Field) itr.next(), instance); + } + } + + //-- Internals only below this point + + private void writeFieldDescriptors(List fields, InstanceKlass ik) + throws IOException { + // ik == null for instance fields. + out.writeShort((short) fields.size()); + for (Iterator itr = fields.iterator(); itr.hasNext();) { + Field field = (Field) itr.next(); + Symbol name = symTbl.probe(field.getID().getName()); + writeObjectID(name); + char typeCode = (char) field.getSignature().getByteAt(0); + int kind = signatureToHprofKind(typeCode); + out.writeByte((byte)kind); + if (ik != null) { + // static field + writeField(field, ik); + } + } + } + + public static int signatureToHprofKind(char ch) { + switch (ch) { + case JVM_SIGNATURE_CLASS: + case JVM_SIGNATURE_ARRAY: + return HPROF_NORMAL_OBJECT; + case JVM_SIGNATURE_BOOLEAN: + return HPROF_BOOLEAN; + case JVM_SIGNATURE_CHAR: + return HPROF_CHAR; + case JVM_SIGNATURE_FLOAT: + return HPROF_FLOAT; + case JVM_SIGNATURE_DOUBLE: + return HPROF_DOUBLE; + case JVM_SIGNATURE_BYTE: + return HPROF_BYTE; + case JVM_SIGNATURE_SHORT: + return HPROF_SHORT; + case JVM_SIGNATURE_INT: + return HPROF_INT; + case JVM_SIGNATURE_LONG: + return HPROF_LONG; + default: + throw new RuntimeException("should not reach here"); + } + } + + private void writeField(Field field, Oop oop) throws IOException { + char typeCode = (char) field.getSignature().getByteAt(0); + switch (typeCode) { + case JVM_SIGNATURE_BOOLEAN: + out.writeBoolean(((BooleanField)field).getValue(oop)); + break; + case JVM_SIGNATURE_CHAR: + out.writeChar(((CharField)field).getValue(oop)); + break; + case JVM_SIGNATURE_BYTE: + out.writeByte(((ByteField)field).getValue(oop)); + break; + case JVM_SIGNATURE_SHORT: + out.writeShort(((ShortField)field).getValue(oop)); + break; + case JVM_SIGNATURE_INT: + out.writeInt(((IntField)field).getValue(oop)); + break; + case JVM_SIGNATURE_LONG: + out.writeLong(((LongField)field).getValue(oop)); + break; + case JVM_SIGNATURE_FLOAT: + out.writeFloat(((FloatField)field).getValue(oop)); + break; + case JVM_SIGNATURE_DOUBLE: + out.writeDouble(((DoubleField)field).getValue(oop)); + break; + case JVM_SIGNATURE_CLASS: + case JVM_SIGNATURE_ARRAY: { + OopHandle handle = ((OopField)field).getValueAsOopHandle(oop); + writeObjectID(getAddressValue(handle)); + break; + } + default: + throw new RuntimeException("should not reach here"); + } + } + + private void writeHeader(int tag, int len) throws IOException { + out.writeByte((byte)tag); + out.writeInt(0); // current ticks + out.writeInt(len); + } + + private void writeDummyTrace() throws IOException { + writeHeader(HPROF_TRACE, 3 * 4); + out.writeInt(DUMMY_STACK_TRACE_ID); + out.writeInt(0); + out.writeInt(0); + } + + private void writeSymbols() throws IOException { + try { + symTbl.symbolsDo(new SymbolTable.SymbolVisitor() { + public void visit(Symbol sym) { + try { + writeSymbol(sym); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + }); + } catch (RuntimeException re) { + handleRuntimeException(re); + } + } + + private void writeSymbol(Symbol sym) throws IOException { + byte[] buf = sym.asString().getBytes("UTF-8"); + writeHeader(HPROF_UTF8, buf.length + OBJ_ID_SIZE); + writeObjectID(sym); + out.write(buf); + } + + private void writeClasses() throws IOException { + // write class list (id, name) association + SystemDictionary sysDict = VM.getVM().getSystemDictionary(); + try { + sysDict.allClassesDo(new SystemDictionary.ClassVisitor() { + private int serialNum = 1; + public void visit(Klass k) { + try { + Instance clazz = k.getJavaMirror(); + writeHeader(HPROF_LOAD_CLASS, 2 * (OBJ_ID_SIZE + 4)); + out.writeInt(serialNum); + writeObjectID(clazz); + out.writeInt(DUMMY_STACK_TRACE_ID); + writeObjectID(k.getName()); + serialNum++; + } catch (IOException exp) { + throw new RuntimeException(exp); + } + } + }); + } catch (RuntimeException re) { + handleRuntimeException(re); + } + } + + // writes hprof binary file header + private void writeFileHeader() throws IOException { + // version string + out.writeBytes(HPROF_HEADER); + out.writeByte((byte)'\0'); + + // write identifier size. we use pointers as identifiers. + out.writeInt(OBJ_ID_SIZE); + + // timestamp -- file creation time. + out.writeLong(System.currentTimeMillis()); + } + + // writes unique ID for an object + private void writeObjectID(Oop oop) throws IOException { + OopHandle handle = (oop != null)? oop.getHandle() : null; + long address = getAddressValue(handle); + writeObjectID(address); + } + + private void writeObjectID(long address) throws IOException { + if (OBJ_ID_SIZE == 4) { + out.writeInt((int) address); + } else { + out.writeLong(address); + } + } + + private long getAddressValue(Address addr) { + return (addr == null)? 0L : dbg.getAddressValue(addr); + } + + // get all declared as well as inherited (directly/indirectly) fields + private static List/**/ getInstanceFields(InstanceKlass ik) { + InstanceKlass klass = ik; + List res = new ArrayList(); + while (klass != null) { + List curFields = klass.getImmediateFields(); + for (Iterator itr = curFields.iterator(); itr.hasNext();) { + Field f = (Field) itr.next(); + if (! f.isStatic()) { + res.add(f); + } + } + klass = (InstanceKlass) klass.getSuper(); + } + return res; + } + + // get size in bytes (in stream) required for given fields. Note + // that this is not the same as object size in heap. The size in + // heap will include size of padding/alignment bytes as well. + private int getSizeForFields(List fields) { + int size = 0; + for (Iterator itr = fields.iterator(); itr.hasNext();) { + Field field = (Field) itr.next(); + char typeCode = (char) field.getSignature().getByteAt(0); + switch (typeCode) { + case JVM_SIGNATURE_BOOLEAN: + case JVM_SIGNATURE_BYTE: + size++; + break; + case JVM_SIGNATURE_CHAR: + case JVM_SIGNATURE_SHORT: + size += 2; + break; + case JVM_SIGNATURE_INT: + case JVM_SIGNATURE_FLOAT: + size += 4; + break; + case JVM_SIGNATURE_CLASS: + case JVM_SIGNATURE_ARRAY: + size += OBJ_ID_SIZE; + break; + case JVM_SIGNATURE_LONG: + case JVM_SIGNATURE_DOUBLE: + size += 8; + break; + default: + throw new RuntimeException("should not reach here"); + } + } + return size; + } + + // We don't have allocation site info. We write a dummy + // stack trace with this id. + private static final int DUMMY_STACK_TRACE_ID = 1; + private static final int EMPTY_FRAME_DEPTH = -1; + + private DataOutputStream out; + private Debugger dbg; + private ObjectHeap objectHeap; + private SymbolTable symTbl; + + // oopSize of the debuggee + private int OBJ_ID_SIZE; + + private long BOOLEAN_BASE_OFFSET; + private long BYTE_BASE_OFFSET; + private long CHAR_BASE_OFFSET; + private long SHORT_BASE_OFFSET; + private long INT_BASE_OFFSET; + private long LONG_BASE_OFFSET; + private long FLOAT_BASE_OFFSET; + private long DOUBLE_BASE_OFFSET; + private long OBJECT_BASE_OFFSET; + + private long BOOLEAN_SIZE; + private long BYTE_SIZE; + private long CHAR_SIZE; + private long SHORT_SIZE; + private long INT_SIZE; + private long LONG_SIZE; + private long FLOAT_SIZE; + private long DOUBLE_SIZE; + + private static class ClassData { + int instSize; + List fields; + ClassData(int instSize, List fields) { + this.instSize = instSize; + this.fields = fields; + } + } + + private Map classDataCache = new HashMap(); // +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapProgressThunk.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapProgressThunk.java new file mode 100644 index 00000000000..e336995e376 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapProgressThunk.java @@ -0,0 +1,36 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** Indicates progress in iterating the heap. Used by + ProgressiveHeapVisitor and implemented by various GUIs. */ + +public interface HeapProgressThunk { + /** Will be called periodically with a number between 0 and 1. */ + public void heapIterationFractionUpdate(double fractionOfHeapVisited); + + /** Will be called after the iteration is complete. */ + public void heapIterationComplete(); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/IntegerEnum.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/IntegerEnum.java new file mode 100644 index 00000000000..be991225fd7 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/IntegerEnum.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** A class enabling simple construction of type-safe integer + enumerations */ + +public class IntegerEnum { + private int value; + + /** Constructor is protected because it will only be used by the + subclass */ + protected IntegerEnum(int value) { + this.value = value; + } + + public int getValue() { + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Interval.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Interval.java new file mode 100644 index 00000000000..c44f162c406 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Interval.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** An interval is an immutable data structure defined by its two + endpoints. */ + +import java.util.Comparator; + +public class Interval { + private Object lowEndpoint; + private Object highEndpoint; + + /** It is required that the low endpoint be less than or equal to + the high endpoint according to the Comparator which will be + passed into the overlaps() routines. */ + public Interval(Object lowEndpoint, Object highEndpoint) { + this.lowEndpoint = lowEndpoint; + this.highEndpoint = highEndpoint; + } + + public Object getLowEndpoint() { + return lowEndpoint; + } + + public Object getHighEndpoint() { + return highEndpoint; + } + + /** This takes the Interval to compare against as well as a + Comparator which will be applied to the low and high endpoints + of the given intervals. */ + public boolean overlaps(Interval arg, Comparator endpointComparator) { + return overlaps(arg.getLowEndpoint(), arg.getHighEndpoint(), endpointComparator); + } + + /** Routine which can be used instead of the one taking an interval, + for the situation where the endpoints are being retrieved from + different data structures */ + public boolean overlaps(Object otherLowEndpoint, + Object otherHighEndpoint, + Comparator endpointComparator) { + if (endpointComparator.compare(highEndpoint, otherLowEndpoint) <= 0) { + return false; + } + if (endpointComparator.compare(lowEndpoint, otherHighEndpoint) >= 0) { + return false; + } + return true; + } + + public String toString() { + return "[ " + getLowEndpoint().toString() + ", " + getHighEndpoint().toString() + ")"; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/IntervalNode.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/IntervalNode.java new file mode 100644 index 00000000000..00050d7f1ed --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/IntervalNode.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** Derived from the example in Section 15.3 of CLR. */ + +import java.util.Comparator; + +public class IntervalNode extends RBNode { + private Interval interval; + private Comparator endpointComparator; + private Object minEndpoint; + private Object maxEndpoint; + + public IntervalNode(Interval interval, Comparator endpointComparator, Object data) { + super(data); + this.interval = interval; + this.endpointComparator = endpointComparator; + } + + public void copyFrom(RBNode arg) { + IntervalNode argNode = (IntervalNode) arg; + this.interval = argNode.interval; + } + + public Interval getInterval() { + return interval; + } + + public Object getMinEndpoint() { + return minEndpoint; + } + + public Object getMaxEndpoint() { + return maxEndpoint; + } + + public boolean update() { + Object newMaxEndpoint = computeMaxEndpoint(); + Object newMinEndpoint = computeMinEndpoint(); + + if ((maxEndpoint != newMaxEndpoint) || (minEndpoint != newMinEndpoint)) { + maxEndpoint = newMaxEndpoint; + minEndpoint = newMinEndpoint; + return true; + } + + return false; + } + + // Computes maximum endpoint without setting it in this node + public Object computeMinEndpoint() { + IntervalNode left = (IntervalNode) getLeft(); + if (left != null) { + return left.getMinEndpoint(); + } + return interval.getLowEndpoint(); + } + + // Computes maximum endpoint without setting it in this node + public Object computeMaxEndpoint() { + Object curMax = interval.getHighEndpoint(); + if (getLeft() != null) { + IntervalNode left = (IntervalNode) getLeft(); + if (endpointComparator.compare(left.getMaxEndpoint(), curMax) > 0) { + curMax = left.getMaxEndpoint(); + } + } + + if (getRight() != null) { + IntervalNode right = (IntervalNode) getRight(); + if (endpointComparator.compare(right.getMaxEndpoint(), curMax) > 0) { + curMax = right.getMaxEndpoint(); + } + } + return curMax; + } + + public String toString() { + String res = interval.toString(); + Object d = getData(); + if (d != null) { + res += " " + d; + } + return res; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/IntervalTree.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/IntervalTree.java new file mode 100644 index 00000000000..b2f79735e53 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/IntervalTree.java @@ -0,0 +1,175 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** Derived from the example in Section 15.3 of CLR. */ + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class IntervalTree extends RBTree { + private Comparator endpointComparator; + + /** This constructor takes only one comparator: one which operates + upon the endpoints of the Intervals this tree will store. It + constructs an internal "interval comparator" out of this one. */ + public IntervalTree(Comparator endpointComparator) { + super(new IntervalComparator(endpointComparator)); + this.endpointComparator = endpointComparator; + } + + public void insert(Interval interval, Object data) { + IntervalNode node = new IntervalNode(interval, endpointComparator, data); + insertNode(node); + } + + /** Returns a List<IntervalNode> indicating which nodes' + intervals were intersected by the given query interval. It is + guaranteed that these nodes will be returned sorted by + increasing low endpoint. */ + public List findAllNodesIntersecting(Interval interval) { + List retList = new ArrayList(); + searchForIntersectingNodesFrom((IntervalNode) getRoot(), interval, retList); + return retList; + } + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + printFromNode(getRoot(), tty, 0); + } + + //---------------------------------------------------------------------- + // Overridden internal functionality + + protected Object getNodeValue(RBNode node) { + return ((IntervalNode) node).getInterval(); + } + + protected void verify() { + super.verify(); + verifyFromNode(getRoot()); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void verifyFromNode(RBNode node) { + if (node == null) { + return; + } + + // We already know that the red/black structure has been verified. + // What we need to find out is whether this node has been updated + // properly -- i.e., whether its notion of the maximum endpoint is + // correct. + IntervalNode intNode = (IntervalNode) node; + if (!intNode.getMaxEndpoint().equals(intNode.computeMaxEndpoint())) { + if (DEBUGGING && VERBOSE) { + print(); + } + throw new RuntimeException("Node's max endpoint was not updated properly"); + } + if (!intNode.getMinEndpoint().equals(intNode.computeMinEndpoint())) { + if (DEBUGGING && VERBOSE) { + print(); + } + throw new RuntimeException("Node's min endpoint was not updated properly"); + } + + verifyFromNode(node.getLeft()); + verifyFromNode(node.getRight()); + } + + static class IntervalComparator implements Comparator { + private Comparator endpointComparator; + + public IntervalComparator(Comparator endpointComparator) { + this.endpointComparator = endpointComparator; + } + + public int compare(Object o1, Object o2) { + Interval i1 = (Interval) o1; + Interval i2 = (Interval) o2; + return endpointComparator.compare(i1.getLowEndpoint(), i2.getLowEndpoint()); + } + } + + private void searchForIntersectingNodesFrom(IntervalNode node, + Interval interval, + List resultList) { + if (node == null) { + return; + } + + // Inorder traversal (very important to guarantee sorted order) + + // Check to see whether we have to traverse the left subtree + IntervalNode left = (IntervalNode) node.getLeft(); + if ((left != null) && + (endpointComparator.compare(left.getMaxEndpoint(), + interval.getLowEndpoint()) > 0)) { + searchForIntersectingNodesFrom(left, interval, resultList); + } + + // Check for intersection with current node + if (node.getInterval().overlaps(interval, endpointComparator)) { + resultList.add(node); + } + + // Check to see whether we have to traverse the left subtree + IntervalNode right = (IntervalNode) node.getRight(); + if ((right != null) && + (endpointComparator.compare(interval.getHighEndpoint(), + right.getMinEndpoint()) > 0)) { + searchForIntersectingNodesFrom(right, interval, resultList); + } + } + + /** Debugging */ + private void printFromNode(RBNode node, PrintStream tty, int indentDepth) { + for (int i = 0; i < indentDepth; i++) { + tty.print(" "); + } + + tty.print("-"); + if (node == null) { + tty.println(); + return; + } + + tty.println(" " + node + + " (min " + ((IntervalNode) node).getMinEndpoint() + + ", max " + ((IntervalNode) node).getMaxEndpoint() + ")" + + ((node.getColor() == RBColor.RED) ? " (red)" : " (black)")); + if (node.getLeft() != null) printFromNode(node.getLeft(), tty, indentDepth + 2); + if (node.getRight() != null) printFromNode(node.getRight(), tty, indentDepth + 2); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessAnalysis.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessAnalysis.java new file mode 100644 index 00000000000..449861b7af6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessAnalysis.java @@ -0,0 +1,196 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/** Finds all paths from roots to the specified set of objects. NOTE: + currently only a subset of the roots known to the VM is exposed to + the SA: objects on the stack, static fields in classes, and JNI + handles. These should be most of the user-level roots keeping + objects alive. */ + +public class LivenessAnalysis { + // Used for debugging this code + private static final boolean DEBUG = false; + + private LivenessAnalysis() {} + + public static LivenessPathList computeAllLivenessPaths(Oop target) { + LivenessPathList list = computeAllLivenessPaths(target, true); + if ((list == null) || (list.size() == 0)) { + // Dead object + return null; + } + return list; + } + + //--------------------------------------------------------------------------- + // Internals only below this point + // + + // Returns true if a new path was completed, otherwise false + // indicating there were no more paths to complete. + // + // The trimPathsThroughPopularObjects flag alters the behavior of + // the returned results. If true, then if multiple paths to + // different roots all go through a particular popular object, those + // paths will be truncated and only one (arbitrary one) will be be + // returned. On the other hand, if the target object itself is + // popular and there are multiple distinct paths to it (indicating + // that there are multiple objects pointing directly to it) then all + // of those paths will be reported. + private static LivenessPathList computeAllLivenessPaths(Oop target, boolean trimPathsThroughPopularObjects) { + ReversePtrs rev = VM.getVM().getRevPtrs(); + if (rev == null) { + throw new RuntimeException("LivenessAnalysis requires ReversePtrs to have been computed"); + } + + // Currently the reverse pointer analysis returns non-null results + // only for live objects + if (rev.get(target) == null) { + // Object is dead + return null; + } + + // HashSet of Oops acting as a bit mask indicating which ones have + // already been traversed + Set/**/ visitedOops = new HashSet/**/(); + + // IdentityHashMap of LivenessElements acting as a bit mask + // indicating which roots have already been traversed + Map/**/ visitedRoots = + new IdentityHashMap/**/(); + + visitedOops.add(target); + + // Construct the initial LivenessPath + LivenessPathList list = new LivenessPathList(); + { + LivenessPath path = new LivenessPath(); + path.push(new LivenessPathElement(target, null)); + list.add(path); + } + + // Iterate until done + while (true) { + // See if there are any incomplete paths left + LivenessPath path = null; + + for (int i = list.size() - 1; i >= 0; i--) { + LivenessPath tmp = list.get(i); + if (!tmp.isComplete()) { + path = tmp; + break; + } + } + + // If no incomplete paths left, return + if (path == null) { + return list; + } + + // Try to complete this path + + // Remove the path from the list of reported ones in + // anticipation of completing it + list.remove(path); + + try { + // Fetch next set of reverse pointers for the last object on + // the list + ArrayList/**/ nextPtrs = + rev.get(path.peek().getObj()); + + // Depending on exactly what the reverse pointers analysis + // yields, these results may be null, although currently they + // won't be + if (nextPtrs != null) { + // Iterate these + for (Iterator iter = nextPtrs.iterator(); iter.hasNext(); ) { + LivenessPathElement nextElement = (LivenessPathElement) iter.next(); + // See whether we've visited this element yet + if ((nextElement.isRoot() && (visitedRoots.get(nextElement) == null)) || + (!nextElement.isRoot() && !visitedOops.contains(nextElement.getObj()))) { + // Show we've visited this one + if (nextElement.isRoot()) { + visitedRoots.put(nextElement, nextElement); + } else { + visitedOops.add(nextElement.getObj()); + } + + // Create a new LivenessPath for each one + LivenessPath nextPath = path.copy(); + nextPath.push(nextElement); + + // Regardless of whether we found a root, put the + // original path back on the list because we're going to + // do depth-first searching rather than breadth-first + list.add(path); + list.add(nextPath); + + // See whether this path is complete (i.e., it + // terminates in a root) + if (trimPathsThroughPopularObjects && nextElement.isRoot()) { + // Go back through the path list and remove all + // incomplete paths ending in any of the intermediate + // (non-root and non-terminal) nodes in this path. + // This has the effect of removing multiple paths + // going through popular objects. + for (int i = 1; i < nextPath.size() - 1; i++) { + LivenessPathElement el = nextPath.get(i); + int j = 0; + while (j < list.size()) { + LivenessPath curPath = list.get(j); + // We can use an object identity since these + // intermediate nodes are canonicalized via the + // ReversePtrsAnalysis + if (curPath.peek() == el) { + list.remove(curPath); + } else { + j++; + } + } + } + } + + // Do depth-first searching, not breadth-first + break; + } + } + } + } catch (Exception e) { + System.err.println("LivenessAnalysis: WARNING: " + e + + " during traversal"); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessPath.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessPath.java new file mode 100644 index 00000000000..2887db545cc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessPath.java @@ -0,0 +1,108 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.oops.*; + +/** Describes a path from an object back to the root which is keeping + it alive. Elements of the path are (object, field) pairs, where + the object is expressed as a @link{sun.jvm.hotspot.oops.Oop}, and + where the field is expressed as a + @link{sun.jvm.hotspot.oops.FieldIdentifier}. If the element + reflects a root, the Oop will be null. If the element is the end + of the path, the FieldIdentifier will be null. */ + +public class LivenessPath { + LivenessPath() { + stack = new Stack(); + } + + /** Number of elements in the path */ + public int size() { + return stack.size(); + } + + /** Fetch the element at the given index; 0-based */ + public LivenessPathElement get(int index) throws ArrayIndexOutOfBoundsException { + return (LivenessPathElement) stack.get(index); + } + + public void printOn(PrintStream tty) { + for (int j = 0; j < size(); j++) { + LivenessPathElement el = get(j); + tty.print(" - "); + if (el.getObj() != null) { + Oop.printOopValueOn(el.getObj(), tty); + } + if (el.getField() != null) { + if (el.getObj() != null) { + tty.print(", field "); + } + tty.print(el.getField().getName()); + } + tty.println(); + } + } + + /** Indicates whether this path is "complete", i.e., whether the + last element is a root. Convenience routine for LivenessAnalysis. */ + boolean isComplete() { + if (size() == 0) + return false; + return peek().isRoot(); + } + + // Convenience routine for LivenessAnalysis + LivenessPathElement peek() { + return (LivenessPathElement) stack.peek(); + } + + // Convenience routine for LivenessAnalysis + void push(LivenessPathElement el) { + stack.push(el); + } + + // Convenience routine for LivenessAnalysis + void pop() { + stack.pop(); + } + + // Make a copy of the contents of the path -- the + // LivenessPathElements are not duplicated, only the containing path + LivenessPath copy() { + LivenessPath dup = new LivenessPath(); + for (int i = 0; i < stack.size(); i++) { + dup.stack.push(stack.get(i)); + } + return dup; + } + + //--------------------------------------------------------------------------- + // Internals only below this point + // + private Stack stack; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessPathElement.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessPathElement.java new file mode 100644 index 00000000000..e75ef7bcacd --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessPathElement.java @@ -0,0 +1,62 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import sun.jvm.hotspot.oops.*; + +/** Describes an element in a {@link + sun.jvm.hotspot.utilities.LivenessPath}. It indicates that the + field in the given object leads to the next element in the + path. Root elements do not have an object; the terminal element in + the path does not have a field identifier. */ + +public class LivenessPathElement { + LivenessPathElement(Oop obj, FieldIdentifier id) { + this.obj = obj; + this.id = id; + } + + public boolean isRoot() { + return (obj == null); + } + + public boolean isTerminal() { + return (id == null); + } + + public Oop getObj() { + return obj; + } + + public FieldIdentifier getField() { + return id; + } + + //--------------------------------------------------------------------------- + // Internals only below this point + // + private Oop obj; + private FieldIdentifier id; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessPathList.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessPathList.java new file mode 100644 index 00000000000..316a0e25d85 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/LivenessPathList.java @@ -0,0 +1,53 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.*; + +/** Simple container class for LivenessPaths */ + +public class LivenessPathList { + public LivenessPathList() { + list = new ArrayList(); + } + + public int size() { + return list.size(); + } + + public LivenessPath get(int i) { + return (LivenessPath) list.get(i); + } + + void add(LivenessPath path) { + list.add(path); + } + + void remove(LivenessPath path) { + list.remove(path); + } + + private ArrayList list; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/MarkBits.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/MarkBits.java new file mode 100644 index 00000000000..e1d6fa7ec5a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/MarkBits.java @@ -0,0 +1,93 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/** Helper class which covers the reserved area of the heap with an + (object-external) set of mark bits, used for GC-like scans through + the heap like liveness analysis. */ + +public class MarkBits { + public MarkBits(CollectedHeap heap) { + MemRegion reserved = heap.reservedRegion(); + // Must cover "reserved" with one bit for each OopHandle + start = reserved.start(); + end = reserved.end(); + long numOopHandles = end.minus(start) / VM.getVM().getOopSize(); + // FIXME: will have trouble with larger heap sizes + bits = new BitMap((int) numOopHandles); + } + + public void clear() { + bits.clear(); + } + + /** Returns true if a mark was newly placed for the given Oop, or + false if the Oop was already marked. If the Oop happens to lie + outside the heap (should not happen), prints a warning and + returns false. */ + public boolean mark(Oop obj) { + if (obj == null) { + System.err.println("MarkBits: WARNING: null object, ignoring"); + return false; + } + + OopHandle handle = obj.getHandle(); + // FIXME: will have trouble with larger heap sizes + long idx = handle.minus(start) / VM.getVM().getOopSize(); + if ((idx < 0) || (idx >= bits.size())) { + System.err.println("MarkBits: WARNING: object " + handle + " outside of heap, ignoring"); + return false; + } + int intIdx = (int) idx; + if (bits.at(intIdx)) { + return false; // already marked + } + bits.atPut(intIdx, true); + return true; + } + + /** Forces clearing of a given mark bit. */ + public void clear(Oop obj) { + OopHandle handle = obj.getHandle(); + // FIXME: will have trouble with larger heap sizes + long idx = handle.minus(start) / VM.getVM().getOopSize(); + if ((idx < 0) || (idx >= bits.size())) { + System.err.println("MarkBits: WARNING: object " + handle + " outside of heap, ignoring"); + return; + } + int intIdx = (int) idx; + bits.atPut(intIdx, false); + } + + private BitMap bits; + private Address start; + private Address end; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/MessageQueue.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/MessageQueue.java new file mode 100644 index 00000000000..5401b7204a3 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/MessageQueue.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/**

A two-way unbounded-length message queue useful for + communication between threads. Messages written on one side become + readable on the other in first-in, first-out order. This is an + interface to one of two "sides" of an underlying backend, for + example, the MessageQueueBackend.

*/ + +public interface MessageQueue { + /** This blocks until a message is available. Even if the thread is + interrupted while it is waiting, this will not return until a + message is written by the entity on the other side of the + queue. */ + public Object readMessage(); + + /** This blocks for up to millis milliseconds until a + message is available. If no message becomes available within + this time period, or the thread is interrupted during the wait, + returns null. (This implies that passing the value null back and + forth is not distinguishable with this method.) Passing a value + of 0 for the millis argument causes this method to + return without blocking. The millis argument must be greater + than or equal to zero. */ + public Object readMessageWithTimeout(long millis); + + /** Write a message to the queue */ + public void writeMessage(Object obj); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/MessageQueueBackend.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/MessageQueueBackend.java new file mode 100644 index 00000000000..0809016785f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/MessageQueueBackend.java @@ -0,0 +1,104 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** The backend for the message queue abstraction. This class is + instantiated first and queried to provide the two "sides" of the + message queue. */ + +import java.util.LinkedList; + +public class MessageQueueBackend { + // The two queues + private MessageQueueImpl leftRightQueue; + private MessageQueueImpl rightLeftQueue; + + public MessageQueueBackend() { + LinkedList leftRightPipe = new LinkedList(); + LinkedList rightLeftPipe = new LinkedList(); + leftRightQueue = new MessageQueueImpl(rightLeftPipe, leftRightPipe); + rightLeftQueue = new MessageQueueImpl(leftRightPipe, rightLeftPipe); + } + + /** Get one of the two symmetric sides of this message queue. */ + public MessageQueue getFirstQueue() { + return leftRightQueue; + } + + /** Get second of the two symmetric sides of this message queue. */ + public MessageQueue getSecondQueue() { + return rightLeftQueue; + } + + private class MessageQueueImpl implements MessageQueue { + private LinkedList readList; + private LinkedList writeList; + + public MessageQueueImpl(LinkedList listToReadFrom, LinkedList listToWriteTo) { + readList = listToReadFrom; + writeList = listToWriteTo; + } + + public Object readMessage() { + synchronized(readList) { + while (readList.isEmpty()) { + try { + readList.wait(); + } + catch (InterruptedException e) { + } + } + return readList.removeFirst(); + } + } + + public Object readMessageWithTimeout(long millis) { + synchronized(readList) { + if (readList.isEmpty()) { + if (millis == 0) { + return null; + } + try { + readList.wait(millis); + } + catch (InterruptedException e) { + } + } + // If list is still empty after wait, return null + if (readList.isEmpty()) { + return null; + } + return readList.removeFirst(); + } + } + + public void writeMessage(Object obj) { + synchronized(writeList) { + writeList.addLast(obj); + writeList.notify(); + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ObjectReader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ObjectReader.java new file mode 100644 index 00000000000..84461920d69 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ObjectReader.java @@ -0,0 +1,651 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.lang.reflect.Modifier; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +/** + * ObjectReader can "deserialize" objects from debuggee. + * + * Class Loading: + * + * ObjectReader loads classes using the given class loader. If no + * class loader is supplied, it uses a ProcImageClassLoader, which + * loads classes from debuggee core or process. + + * Object creation: + * + * This class uses no-arg constructor to construct objects. But if + * there is no no-arg constructor in a given class, then it tries to + * use other constructors with 'default' values - null for object + * types, 0, 0.0, false etc. for primitives. If this process fails to + * construct an instance (because of null checking by constructor or 0 + * being invalid for an int arg etc.), then null is returned. While + * constructing complete object graph 'null' is inserted silently on + * failure and the deserialization continues to construct best-effort + * object graph. + * + * Debug messages: + * + * The flag sun.jvm.hotspot.utilities.ObjectReader.DEBUG may be set to + * non-null to get debug error messages and stack traces. + * + * JDK version: + * + * JDK classes are loaded by bootstrap class loader and not by the + * supplied class loader or ProcImageClassLoader. This may create + * problems if a JDK class evolves. i.e., if SA runs a JDK version + * different from that of the debuggee, there is a possibility of + * schema change. It is recommended that the matching JDK version be + * used to run SA for proper object deserialization. + * + */ + +public class ObjectReader { + + private static final boolean DEBUG; + static { + DEBUG = System.getProperty("sun.jvm.hotspot.utilities.ObjectReader.DEBUG") != null; + } + + public ObjectReader(ClassLoader cl) { + this.cl = cl; + this.oopToObjMap = new HashMap(); + this.fieldMap = new HashMap(); + } + + public ObjectReader() { + this(new ProcImageClassLoader()); + } + + public Object readObject(Oop oop) throws ClassNotFoundException { + if (oop instanceof Instance) { + return readInstance((Instance) oop); + } else if (oop instanceof TypeArray){ + return readPrimitiveArray((TypeArray)oop); + } else if (oop instanceof ObjArray){ + return readObjectArray((ObjArray)oop); + } else { + return null; + } + } + + protected final Object getDefaultPrimitiveValue(Class clz) { + if (clz == Boolean.TYPE) { + return Boolean.FALSE; + } else if (clz == Character.TYPE) { + return new Character(' '); + } else if (clz == Byte.TYPE) { + return new Byte((byte) 0); + } else if (clz == Short.TYPE) { + return new Short((short) 0); + } else if (clz == Integer.TYPE) { + return new Integer(0); + } else if (clz == Long.TYPE) { + return new Long(0L); + } else if (clz == Float.TYPE) { + return new Float(0.0f); + } else if (clz == Double.TYPE) { + return new Double(0.0); + } else { + throw new RuntimeException("should not reach here!"); + } + } + + protected Symbol javaLangString; + protected Symbol javaLangString() { + if (javaLangString == null) { + javaLangString = VM.getVM().getSymbolTable().probe("java/lang/String"); + } + return javaLangString; + } + + public Object readInstance(Instance oop) throws ClassNotFoundException { + Object result = getFromObjTable(oop); + if (result == null) { + InstanceKlass kls = (InstanceKlass) oop.getKlass(); + // Handle java.lang.String instances differently. As part of JSR-133, fields of immutable + // classes have been made final. The algorithm below will not be able to read Strings from + // debuggee (can't use reflection to set final fields). But, need to read Strings is very + // important. FIXME: need a framework to handle many other special cases. + if (kls.getName().equals(javaLangString())) { + return OopUtilities.stringOopToString(oop); + } + + Class clz = readClass(kls); + try { + result = clz.newInstance(); + } catch (Exception ex) { + // no-arg constructor failed to create object. Let us try + // to call constructors one-by-one with default arguments + // (null for objects, 0/0.0 etc. for primitives) till we + // succeed or fail on all constructors. + + java.lang.reflect.Constructor[] ctrs = clz.getDeclaredConstructors(); + for (int n = 0; n < ctrs.length; n++) { + java.lang.reflect.Constructor c = ctrs[n]; + Class[] paramTypes = c.getParameterTypes(); + Object[] params = new Object[paramTypes.length]; + for (int i = 0; i < params.length; i++) { + if (paramTypes[i].isPrimitive()) { + params[i] = getDefaultPrimitiveValue(paramTypes[i]); + } + } + try { + c.setAccessible(true); + result = c.newInstance(params); + break; + } catch (Exception exp) { + if (DEBUG) { + System.err.println("Can't create object using " + c); + exp.printStackTrace(); + } + } + } + } + + if (result != null) { + putIntoObjTable(oop, result); + oop.iterate(new FieldSetter(result), false); + } + } + return result; + } + + public Object readPrimitiveArray(final TypeArray array) { + + Object result = getFromObjTable(array); + if (result == null) { + int length = (int) array.getLength(); + TypeArrayKlass klass = (TypeArrayKlass) array.getKlass(); + int type = (int) klass.getElementType(); + switch (type) { + case TypeArrayKlass.T_BOOLEAN: { + final boolean[] arrayObj = new boolean[length]; + array.iterate(new DefaultOopVisitor() { + public void doBoolean(BooleanField field, boolean isVMField) { + IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID(); + arrayObj[ifd.getIndex()] = field.getValue(array); + } + }, false); + result = arrayObj; + } + break; + + case TypeArrayKlass.T_CHAR: { + final char[] arrayObj = new char[length]; + array.iterate(new DefaultOopVisitor() { + public void doChar(CharField field, boolean isVMField) { + IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID(); + arrayObj[ifd.getIndex()] = field.getValue(array); + } + }, false); + result = arrayObj; + } + break; + + case TypeArrayKlass.T_FLOAT: { + final float[] arrayObj = new float[length]; + array.iterate(new DefaultOopVisitor() { + public void doFloat(FloatField field, boolean isVMField) { + IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID(); + arrayObj[ifd.getIndex()] = field.getValue(array); + } + }, false); + result = arrayObj; + } + break; + + case TypeArrayKlass.T_DOUBLE: { + final double[] arrayObj = new double[length]; + array.iterate(new DefaultOopVisitor() { + public void doDouble(DoubleField field, boolean isVMField) { + IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID(); + arrayObj[ifd.getIndex()] = field.getValue(array); + } + }, false); + result = arrayObj; + } + break; + + case TypeArrayKlass.T_BYTE: { + final byte[] arrayObj = new byte[length]; + array.iterate(new DefaultOopVisitor() { + public void doByte(ByteField field, boolean isVMField) { + IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID(); + arrayObj[ifd.getIndex()] = field.getValue(array); + } + }, false); + result = arrayObj; + } + break; + + case TypeArrayKlass.T_SHORT: { + final short[] arrayObj = new short[length]; + array.iterate(new DefaultOopVisitor() { + public void doShort(ShortField field, boolean isVMField) { + IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID(); + arrayObj[ifd.getIndex()] = field.getValue(array); + } + }, false); + result = arrayObj; + } + break; + + case TypeArrayKlass.T_INT: { + final int[] arrayObj = new int[length]; + array.iterate(new DefaultOopVisitor() { + public void doInt(IntField field, boolean isVMField) { + IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID(); + arrayObj[ifd.getIndex()] = field.getValue(array); + } + }, false); + result = arrayObj; + } + break; + + case TypeArrayKlass.T_LONG: { + final long[] arrayObj = new long[length]; + array.iterate(new DefaultOopVisitor() { + public void doLong(LongField field, boolean isVMField) { + IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID(); + arrayObj[ifd.getIndex()] = field.getValue(array); + } + }, false); + result = arrayObj; + } + break; + + default: + throw new RuntimeException("should not reach here!"); + } + + putIntoObjTable(array, result); + } + return result; + } + + protected final boolean isRobust(OopHandle handle) { + return RobustOopDeterminator.oopLooksValid(handle); + } + + public Object readObjectArray(final ObjArray array) throws ClassNotFoundException { + Object result = getFromObjTable(array); + if (result == null) { + int length = (int) array.getLength(); + ObjArrayKlass klass = (ObjArrayKlass) array.getKlass(); + Klass bottomKls = klass.getBottomKlass(); + Class bottomCls = null; + final int dimension = (int) klass.getDimension(); + int[] dimArray = null; + if (bottomKls instanceof InstanceKlass) { + bottomCls = readClass((InstanceKlass) bottomKls); + dimArray = new int[dimension]; + } else { // instanceof TypeArrayKlass + TypeArrayKlass botKls = (TypeArrayKlass) bottomKls; + dimArray = new int[dimension -1]; + } + // initialize the length + dimArray[0] = length; + final Object[] arrayObj = (Object[]) java.lang.reflect.Array.newInstance(bottomCls, dimArray); + putIntoObjTable(array, arrayObj); + result = arrayObj; + array.iterate(new DefaultOopVisitor() { + public void doOop(OopField field, boolean isVMField) { + OopHandle handle = field.getValueAsOopHandle(getObj()); + if (! isRobust(handle)) { + return; + } + + IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID(); + try { + arrayObj[ifd.getIndex()] = readObject(field.getValue(getObj())); + } catch (Exception e) { + if (DEBUG) { + System.err.println("Array element set failed for " + ifd); + e.printStackTrace(); + } + } + } + }, false); + } + return result; + } + + protected class FieldSetter extends DefaultOopVisitor { + protected Object obj; + + public FieldSetter(Object obj) { + this.obj = obj; + } + + private void printFieldSetError(java.lang.reflect.Field f, Exception ex) { + if (DEBUG) { + if (f != null) System.err.println("Field set failed for " + f); + ex.printStackTrace(); + } + } + + // Callback methods for each field type in an object + public void doOop(OopField field, boolean isVMField) { + OopHandle handle = field.getValueAsOopHandle(getObj()); + if (! isRobust(handle) ) { + return; + } + + java.lang.reflect.Field f = null; + try { + f = readField(field); + if (Modifier.isFinal(f.getModifiers())) return; + f.setAccessible(true); + f.set(obj, readObject(field.getValue(getObj()))); + } catch (Exception ex) { + printFieldSetError(f, ex); + } + } + + public void doByte(ByteField field, boolean isVMField) { + java.lang.reflect.Field f = null; + try { + f = readField(field); + if (Modifier.isFinal(f.getModifiers())) return; + f.setAccessible(true); + f.setByte(obj, field.getValue(getObj())); + } catch (Exception ex) { + printFieldSetError(f, ex); + } + } + + public void doChar(CharField field, boolean isVMField) { + java.lang.reflect.Field f = null; + try { + f = readField(field); + if (Modifier.isFinal(f.getModifiers())) return; + f.setAccessible(true); + f.setChar(obj, field.getValue(getObj())); + } catch (Exception ex) { + printFieldSetError(f, ex); + } + } + + public void doBoolean(BooleanField field, boolean isVMField) { + java.lang.reflect.Field f = null; + try { + f = readField(field); + if (Modifier.isFinal(f.getModifiers())) return; + f.setAccessible(true); + f.setBoolean(obj, field.getValue(getObj())); + } catch (Exception ex) { + printFieldSetError(f, ex); + } + } + + public void doShort(ShortField field, boolean isVMField) { + java.lang.reflect.Field f = null; + try { + f = readField(field); + if (Modifier.isFinal(f.getModifiers())) return; + f.setAccessible(true); + f.setShort(obj, field.getValue(getObj())); + } catch (Exception ex) { + printFieldSetError(f, ex); + } + } + + public void doInt(IntField field, boolean isVMField) { + java.lang.reflect.Field f = null; + try { + f = readField(field); + if (Modifier.isFinal(f.getModifiers())) return; + f.setAccessible(true); + f.setInt(obj, field.getValue(getObj())); + } catch (Exception ex) { + printFieldSetError(f, ex); + } + } + + public void doLong(LongField field, boolean isVMField) { + java.lang.reflect.Field f = null; + try { + f = readField(field); + if (Modifier.isFinal(f.getModifiers())) return; + f.setAccessible(true); + f.setLong(obj, field.getValue(getObj())); + } catch (Exception ex) { + printFieldSetError(f, ex); + } + } + + public void doFloat(FloatField field, boolean isVMField) { + java.lang.reflect.Field f = null; + try { + f = readField(field); + if (Modifier.isFinal(f.getModifiers())) return; + f.setAccessible(true); + f.setFloat(obj, field.getValue(getObj())); + } catch (Exception ex) { + printFieldSetError(f, ex); + } + } + + public void doDouble(DoubleField field, boolean isVMField) { + java.lang.reflect.Field f = null; + try { + f = readField(field); + if (Modifier.isFinal(f.getModifiers())) return; + f.setAccessible(true); + f.setDouble(obj, field.getValue(getObj())); + } catch (Exception ex) { + printFieldSetError(f, ex); + } + } + + public void doCInt(CIntField field, boolean isVMField) { + throw new RuntimeException("should not reach here!"); + } + } + + public Class readClass(InstanceKlass kls) throws ClassNotFoundException { + Class cls = (Class) getFromObjTable(kls); + if (cls == null) { + cls = Class.forName(kls.getName().asString().replace('/', '.'), true, cl); + putIntoObjTable(kls, cls); + } + return cls; + } + + public Object readMethodOrConstructor(sun.jvm.hotspot.oops.Method m) + throws NoSuchMethodException, ClassNotFoundException { + String name = m.getName().asString(); + if (name.equals("")) { + return readConstructor(m); + } else { + return readMethod(m); + } + } + + public java.lang.reflect.Method readMethod(sun.jvm.hotspot.oops.Method m) + throws NoSuchMethodException, ClassNotFoundException { + java.lang.reflect.Method result = (java.lang.reflect.Method) getFromObjTable(m); + if (result == null) { + Class clz = readClass((InstanceKlass)m.getMethodHolder()); + String name = m.getName().asString(); + Class[] paramTypes = getParamTypes(m.getSignature()); + result = clz.getMethod(name, paramTypes); + putIntoObjTable(m, result); + } + return result; + } + + public java.lang.reflect.Constructor readConstructor(sun.jvm.hotspot.oops.Method m) + throws NoSuchMethodException, ClassNotFoundException { + java.lang.reflect.Constructor result = (java.lang.reflect.Constructor) getFromObjTable(m); + if (result == null) { + Class clz = readClass((InstanceKlass)m.getMethodHolder()); + String name = m.getName().asString(); + Class[] paramTypes = getParamTypes(m.getSignature()); + result = clz.getDeclaredConstructor(paramTypes); + putIntoObjTable(m, result); + } + return result; + } + + public java.lang.reflect.Field readField(sun.jvm.hotspot.oops.Field f) + throws NoSuchFieldException, ClassNotFoundException { + java.lang.reflect.Field result = (java.lang.reflect.Field) fieldMap.get(f); + if (result == null) { + FieldIdentifier fieldId = f.getID(); + Class clz = readClass((InstanceKlass) f.getFieldHolder()); + String name = fieldId.getName(); + try { + result = clz.getField(name); + } catch (NoSuchFieldException nsfe) { + result = clz.getDeclaredField(name); + } + fieldMap.put(f, result); + } + return result; + } + + protected final ClassLoader cl; + protected Map oopToObjMap; // Map + protected Map fieldMap; // Map + + protected void putIntoObjTable(Oop oop, Object obj) { + oopToObjMap.put(oop, obj); + } + + protected Object getFromObjTable(Oop oop) { + return oopToObjMap.get(oop); + } + + protected class SignatureParser extends SignatureIterator { + protected Vector tmp = new Vector(); // Vector + + public SignatureParser(Symbol s) { + super(s); + } + + public void doBool () { tmp.add(Boolean.TYPE); } + public void doChar () { tmp.add(Character.TYPE); } + public void doFloat () { tmp.add(Float.TYPE); } + public void doDouble() { tmp.add(Double.TYPE); } + public void doByte () { tmp.add(Byte.TYPE); } + public void doShort () { tmp.add(Short.TYPE); } + public void doInt () { tmp.add(Integer.TYPE); } + public void doLong () { tmp.add(Long.TYPE); } + public void doVoid () { + if(isReturnType()) { + tmp.add(Void.TYPE); + } else { + throw new RuntimeException("should not reach here"); + } + } + + public void doObject(int begin, int end) { + tmp.add(getClass(begin, end)); + } + + public void doArray (int begin, int end) { + int inner = arrayInnerBegin(begin); + Class elemCls = null; + switch (_signature.getByteAt(inner)) { + case 'B': elemCls = Boolean.TYPE; break; + case 'C': elemCls = Character.TYPE; break; + case 'D': elemCls = Double.TYPE; break; + case 'F': elemCls = Float.TYPE; break; + case 'I': elemCls = Integer.TYPE; break; + case 'J': elemCls = Long.TYPE; break; + case 'S': elemCls = Short.TYPE; break; + case 'Z': elemCls = Boolean.TYPE; break; + case 'L': elemCls = getClass(inner + 1, end); break; + default: break; + } + + int dimension = inner - begin; + // create 0 x 0 ... array and get class from that + int[] dimArray = new int[dimension]; + tmp.add(java.lang.reflect.Array.newInstance(elemCls, dimArray).getClass()); + } + + protected Class getClass(int begin, int end) { + String className = getClassName(begin, end); + try { + return Class.forName(className, true, cl); + } catch (Exception e) { + if (DEBUG) { + System.err.println("Can't load class " + className); + } + throw new RuntimeException(e); + } + } + + protected String getClassName(int begin, int end) { + StringBuffer buf = new StringBuffer(); + for (int i = begin; i < end; i++) { + char c = (char) (_signature.getByteAt(i) & 0xFF); + if (c == '/') { + buf.append('.'); + } else { + buf.append(c); + } + } + return buf.toString(); + } + + protected int arrayInnerBegin(int begin) { + while (_signature.getByteAt(begin) == '[') { + ++begin; + } + return begin; + } + + public int getNumParams() { + return tmp.size(); + } + + public Enumeration getParamTypes() { + return tmp.elements(); + } + } + + protected Class[] getParamTypes(Symbol signature) { + SignatureParser sp = new SignatureParser(signature); + sp.iterateParameters(); + Class result[] = new Class[sp.getNumParams()]; + Enumeration e = sp.getParamTypes(); + int i = 0; + while (e.hasMoreElements()) { + result[i] = (Class) e.nextElement(); + i++; + } + return result; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PlatformInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PlatformInfo.java new file mode 100644 index 00000000000..d4ba3db933a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PlatformInfo.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** Provides canonicalized OS and CPU information for the rest of the + system. */ + +public class PlatformInfo { + /* Returns "solaris" if on Solaris; "win32" if Windows; "linux" if + Linux. Used to determine location of dbx and import module, or + possible debugger agent on win32. */ + public static String getOS() throws UnsupportedPlatformException { + String os = System.getProperty("os.name"); + if (os.equals("SunOS")) { + return "solaris"; + } else if (os.equals("Linux")) { + return "linux"; + } else if (os.startsWith("Windows")) { + return "win32"; + } else { + throw new UnsupportedPlatformException("Operating system " + os + " not yet supported"); + } + } + + /* Returns "sparc" if on SPARC, "x86" if on x86. */ + public static String getCPU() throws UnsupportedPlatformException { + String cpu = System.getProperty("os.arch"); + if (cpu.equals("i386")) { + return "x86"; + } else if (cpu.equals("sparc") || cpu.equals("x86") || cpu.equals("ia64")) { + return cpu; + } else if (cpu.equals("sparcv9")) { + return "sparc"; + } else if (cpu.equals("x86_64") || cpu.equals("amd64")) { + return "amd64"; + } else { + throw new UnsupportedPlatformException("CPU type " + cpu + " not yet supported"); + } + } + + // this main is invoked from Makefile to make platform specific agent Makefile(s). + public static void main(String[] args) { + System.out.println(getOS()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PointerFinder.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PointerFinder.java new file mode 100644 index 00000000000..0e26a50f20f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PointerFinder.java @@ -0,0 +1,149 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.memory.*; + +/** This class, only intended for use in the debugging system, + provides the functionality of find() in the VM. */ + +public class PointerFinder { + public static PointerLocation find(Address a) { + PointerLocation loc = new PointerLocation(a); + + CollectedHeap heap = VM.getVM().getUniverse().heap(); + if (heap instanceof GenCollectedHeap) { + GenCollectedHeap genheap = (GenCollectedHeap) heap; + if (genheap.isIn(a)) { + for (int i = 0; i < genheap.nGens(); i++) { + Generation g = genheap.getGen(i); + if (g.isIn(a)) { + loc.gen = g; + break; + } + } + + if (loc.gen == null) { + // Should be in perm gen + Generation permGen = genheap.permGen(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(permGen.isIn(a), "should have been in ordinary or perm gens if it's in the heap"); + } + loc.permGen = permGen; + } + + if (VM.getVM().getUseTLAB()) { + // Try to find thread containing it + for (JavaThread t = VM.getVM().getThreads().first(); t != null; t = t.next()) { + ThreadLocalAllocBuffer tlab = t.tlab(); + if (tlab.contains(a)) { + loc.inTLAB = true; + loc.tlabThread = t; + loc.tlab = tlab; + break; + } + } + } + + return loc; + } + } else { + if (heap.isIn(a)) { + loc.heap = heap; + return loc; + } + } + + Interpreter interp = VM.getVM().getInterpreter(); + if (interp.contains(a)) { + loc.inInterpreter = true; + loc.interpreterCodelet = interp.getCodeletContaining(a); + return loc; + } + + if (!VM.getVM().isCore()) { + CodeCache c = VM.getVM().getCodeCache(); + if (c.contains(a)) { + loc.inCodeCache = true; + loc.blob = c.findBlobUnsafe(a); + if (Assert.ASSERTS_ENABLED) { + Assert.that(loc.blob != null, "Should have found CodeBlob"); + } + loc.inBlobInstructions = loc.blob.instructionsContains(a); + loc.inBlobData = loc.blob.dataContains(a); + loc.inBlobOops = loc.blob.oopsContains(a); + loc.inBlobUnknownLocation = (!(loc.inBlobInstructions || + loc.inBlobData || + loc.inBlobOops)); + return loc; + } + } + + // Check JNIHandles; both local and global + JNIHandles handles = VM.getVM().getJNIHandles(); + JNIHandleBlock handleBlock = handles.globalHandles(); + if (handleBlock != null) { + handleBlock = handleBlock.blockContainingHandle(a); + } + if (handleBlock != null) { + loc.inStrongGlobalJNIHandleBlock = true; + loc.handleBlock = handleBlock; + return loc; + } else { + handleBlock = handles.weakGlobalHandles(); + if (handleBlock != null) { + handleBlock = handleBlock.blockContainingHandle(a); + if (handleBlock != null) { + loc.inWeakGlobalJNIHandleBlock = true; + loc.handleBlock = handleBlock; + return loc; + } else { + // Look in thread-local handles + for (JavaThread t = VM.getVM().getThreads().first(); t != null; t = t.next()) { + handleBlock = t.activeHandles(); + if (handleBlock != null) { + handleBlock = handleBlock.blockContainingHandle(a); + if (handleBlock != null) { + loc.inLocalJNIHandleBlock = true; + loc.handleBlock = handleBlock; + loc.handleThread = t; + return loc; + } + } + } + } + } + } + + + // Fall through; have to return it anyway. + return loc; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PointerLocation.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PointerLocation.java new file mode 100644 index 00000000000..22d4ca418db --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PointerLocation.java @@ -0,0 +1,280 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.memory.*; + +/** This class attempts to describe possible locations of pointers in + the VM. */ + +public class PointerLocation { + ////////////////////////////////////////////////////////////////// + // // + // These are package private to simplify the implementation and // + // interaction with PointerFinder // + // // + ////////////////////////////////////////////////////////////////// + + Address addr; + + CollectedHeap heap; + Generation gen; + Generation permGen; + + // If UseTLAB was enabled and the pointer was found in a + // currently-active TLAB, these will be set + boolean inTLAB; + JavaThread tlabThread; + ThreadLocalAllocBuffer tlab; + + // Generated code locations + boolean inInterpreter; + boolean inCodeCache; + + // FIXME: add other locations like VTableStubs, StubRoutines, maybe + // even "on thread x's stack" + + InterpreterCodelet interpreterCodelet; + CodeBlob blob; + // FIXME: add more detail about CodeBlob + boolean inBlobInstructions; + boolean inBlobData; + boolean inBlobOops; + boolean inBlobUnknownLocation; + + boolean inStrongGlobalJNIHandleBlock; + boolean inWeakGlobalJNIHandleBlock; + boolean inLocalJNIHandleBlock; + JNIHandleBlock handleBlock; + sun.jvm.hotspot.runtime.Thread handleThread; + + public PointerLocation(Address addr) { + this.addr = addr; + } + + public boolean isInHeap() { + return (heap != null || (gen != null) || (permGen != null)); + } + + public boolean isInNewGen() { + return ((gen != null) && (gen.level() == 0)); + } + + public boolean isInOldGen() { + return ((gen != null) && (gen.level() == 1)); + } + + public boolean isInPermGen() { + return (permGen != null); + } + + public boolean inOtherGen() { + return (!isInNewGen() && !isInOldGen() && !isInPermGen()); + } + + /** Only valid if isInHeap() */ + public Generation getGeneration() { + if (gen != null) { + return gen; + } else { + return permGen; + } + } + + /** This may be true if isInNewGen is also true */ + public boolean isInTLAB() { + return inTLAB; + } + + /** Only valid if isInTLAB() returns true */ + public JavaThread getTLABThread() { + return tlabThread; + } + + /** Only valid if isInTLAB() returns true */ + public ThreadLocalAllocBuffer getTLAB() { + return tlab; + } + + public boolean isInInterpreter() { + return inInterpreter; + } + + /** For now, only valid if isInInterpreter is true */ + public InterpreterCodelet getInterpreterCodelet() { + return interpreterCodelet; + } + + public boolean isInCodeCache() { + return inCodeCache; + } + + /** For now, only valid if isInCodeCache is true */ + public CodeBlob getCodeBlob() { + return blob; + } + + public boolean isInBlobInstructions() { + return inBlobInstructions; + } + + public boolean isInBlobData() { + return inBlobData; + } + + public boolean isInBlobOops() { + return inBlobOops; + } + + public boolean isInBlobUnknownLocation() { + return inBlobUnknownLocation; + } + + public boolean isInStrongGlobalJNIHandleBlock() { + return inStrongGlobalJNIHandleBlock; + } + + public boolean isInWeakGlobalJNIHandleBlock() { + return inWeakGlobalJNIHandleBlock; + } + + public boolean isInLocalJNIHandleBlock() { + return inLocalJNIHandleBlock; + } + + /** Only valid if isInStrongGlobalJNIHandleBlock, + isInWeakGlobalJNIHandleBlock, or isInLocalJNIHandleBlock is true */ + public JNIHandleBlock getJNIHandleBlock() { + return handleBlock; + } + + /** Only valid if isInLocalJNIHandleBlock is true */ + public sun.jvm.hotspot.runtime.Thread getJNIHandleThread() { + return handleThread; + } + + public boolean isUnknown() { + return (!(isInHeap() || isInInterpreter() || isInCodeCache() || + isInStrongGlobalJNIHandleBlock() || isInWeakGlobalJNIHandleBlock() || isInLocalJNIHandleBlock())); + } + + public String toString() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + printOn(new PrintStream(bos)); + return bos.toString(); + } + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + tty.print("Address "); + if (addr == null) { + tty.print("0x0"); + } else { + tty.print(addr.toString()); + } + tty.print(": "); + if (isInHeap()) { + if (isInTLAB()) { + tty.print("In thread-local allocation buffer for thread \"" + + getTLABThread().getThreadName() + "\" ("); + getTLABThread().printThreadIDOn(tty); + tty.print(") "); + getTLAB().printOn(tty); + } else { + if (isInNewGen()) { + tty.print("In new generation "); + } else if (isInOldGen()) { + tty.print("In old generation "); + } else if (isInPermGen()) { + tty.print("In perm generation "); + } else if (gen != null) { + tty.print("In Generation " + getGeneration().level()); + } else { + tty.print("In unknown section of Java heap"); + } + if (getGeneration() != null) { + getGeneration().printOn(tty); + } + } + } else if (isInInterpreter()) { + tty.println("In interpreter codelet \"" + interpreterCodelet.getDescription() + "\""); + interpreterCodelet.printOn(tty); + } else if (isInCodeCache()) { + CodeBlob b = getCodeBlob(); + tty.print("In "); + if (isInBlobInstructions()) { + tty.print("instructions"); + } else if (isInBlobData()) { + tty.print("data"); + } else if (isInBlobOops()) { + tty.print("oops"); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isInBlobUnknownLocation(), "Should have known location in CodeBlob"); + } + tty.print("unknown location"); + } + tty.print(" in "); + b.printOn(tty); + + // FIXME: add more detail + } else if (isInStrongGlobalJNIHandleBlock() || + isInWeakGlobalJNIHandleBlock() || + isInLocalJNIHandleBlock()) { + tty.print("In "); + if (isInStrongGlobalJNIHandleBlock()) { + tty.print("strong global"); + } else if (isInWeakGlobalJNIHandleBlock()) { + tty.print("weak global"); + } else { + tty.print("thread-local"); + } + tty.print(" JNI handle block (" + handleBlock.top() + " handle slots present)"); + if (isInLocalJNIHandleBlock()) { + if (handleThread.isJavaThread()) { + tty.print(" for JavaThread "); + ((JavaThread) handleThread).printThreadIDOn(tty); + } else { + tty.print("for a non-Java Thread"); + } + } + } else { + // This must be last + if (Assert.ASSERTS_ENABLED) { + Assert.that(isUnknown(), "Should have unknown location"); + } + tty.print("In unknown location"); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ProcImageClassLoader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ProcImageClassLoader.java new file mode 100644 index 00000000000..0425bc2cf05 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ProcImageClassLoader.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.tools.jcore.*; + +/** + Class loader that loads classes from the process/core image + of the debuggee. +*/ + +public class ProcImageClassLoader extends ClassLoader { + public ProcImageClassLoader(ClassLoader parent) { + super(parent); + } + + public ProcImageClassLoader() { + this(Thread.currentThread().getContextClassLoader()); + } + + protected Class findClass(String className) throws ClassNotFoundException { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + InstanceKlass klass = SystemDictionaryHelper.findInstanceKlass(className); + ClassWriter cw = new ClassWriter(klass, bos); + cw.write(); + byte[] buf = bos.toByteArray(); + return defineClass(className, buf, 0, buf.length); + } catch (Exception e) { + throw (ClassNotFoundException) new ClassNotFoundException().initCause(e); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ProgressiveHeapVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ProgressiveHeapVisitor.java new file mode 100644 index 00000000000..9cf69201916 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ProgressiveHeapVisitor.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; + +/** This class wraps a user's chosen HeapVisitor with the + functionality that a chosen "thunk" is called periodically during + the heap traversal. This allows a progress bar to be displayed + during long heap scans. */ + +public class ProgressiveHeapVisitor implements HeapVisitor { + private HeapVisitor userHeapVisitor; + private HeapProgressThunk thunk; + private long usedSize; + private long visitedSize; + private double lastNotificationFraction; + private static double MINIMUM_NOTIFICATION_FRACTION = 0.01; + + public ProgressiveHeapVisitor(HeapVisitor userHeapVisitor, + HeapProgressThunk thunk) { + this.userHeapVisitor = userHeapVisitor; + this.thunk = thunk; + } + + public void prologue(long usedSize) { + this.usedSize = usedSize; + visitedSize = 0; + userHeapVisitor.prologue(usedSize); + } + + public boolean doObj(Oop obj) { + userHeapVisitor.doObj(obj); + visitedSize += obj.getObjectSize(); + double curFrac = (double) visitedSize / (double) usedSize; + if (curFrac > lastNotificationFraction + MINIMUM_NOTIFICATION_FRACTION) { + thunk.heapIterationFractionUpdate(curFrac); + lastNotificationFraction = curFrac; + } + return false; + } + + public void epilogue() { + userHeapVisitor.epilogue(); + thunk.heapIterationComplete(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RBColor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RBColor.java new file mode 100644 index 00000000000..67eee589327 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RBColor.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** Type-safe enum for the colors in a red-black tree. */ + +public class RBColor { + public static final RBColor RED = new RBColor("red"); + public static final RBColor BLACK = new RBColor("black"); + + public String getName() { + return name; + } + + private RBColor(String name) { + this.name = name; + } + private String name; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RBNode.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RBNode.java new file mode 100644 index 00000000000..797c0f62467 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RBNode.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** This class implements a node in a red-black tree. It provides + accessors for the left and right children as well as the color of + the node. */ + +public class RBNode { + private Object data; + private RBNode left; + private RBNode right; + private RBNode parent; + private RBColor color; + + /** Newly-created nodes are colored red */ + public RBNode(Object data) { + this.data = data; + color = RBColor.RED; + } + + public Object getData() { + return data; + } + + /** Must copy all user-defined fields from the given node. For + example, the base implementation copies the "data" field. + However, it does not (and must not) copy the link fields + (parent, left child, right child). It also does not need to copy + any computed information for the node, as the node will be + updated when necessary. Subclasses must be careful to call the + superclass implementation. */ + public void copyFrom(RBNode arg) { + this.data = arg.data; + } + + /** This is called by the base RBTree's insertion and deletion + methods when necessary. Subclasses can use this to update any + computed information based on the information in their left or + right children. For multi-node updates it is guaranteed that + this method will be called in the correct order. This should + return true if an update actually occurred, false if not. */ + public boolean update() { + return false; + } + + public RBColor getColor() { return color; } + public void setColor(RBColor color) { this.color = color; } + + public RBNode getParent() { return parent; } + public void setParent(RBNode parent) { this.parent = parent; } + + /** Access to left child */ + public RBNode getLeft() { return left; } + public void setLeft(RBNode left) { this.left = left; } + + /** Access to right child */ + public RBNode getRight() { return right; } + public void setRight(RBNode right) { this.right = right; } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RBTree.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RBTree.java new file mode 100644 index 00000000000..1462e423063 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RBTree.java @@ -0,0 +1,616 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/**

This class implements a Red-Black tree as described in Cormen, + Leiserson, Rivest, Introduction to Algorithms, MIT Press: + Cambridge, MA, 1990.

+ +

A property of this implementation is that it is designed to + allow straightforward augmentation of the data structure. A valid + augmentation is one in which a node contains auxiliary information + that can be computed by examining only this node and its left and + right children (see CLR, section 15.2).

+ +

An RBTree is a structure of RBNodes, each of which contains a + user data element. When the user inserts a piece of data into the + tree, a new RBNode is constructed around it.

+ +

An RBTree takes a Comparator as argument to its constructor + which is used internally to order the nodes in the tree. The + comparator's arguments are obtained by calling the routine + "getNodeData" on two nodes; the default implementaion returns the + node data. This Comparator is also used to perform the generic + "find" operation, which returns the RBNode containing user data + precisely equalling the query data. Different types of user data + will typically require different types of traversals as well as + additional comparison operations; these are left to the RBTree + subclass.

+ +*/ + +import java.io.PrintStream; +import java.util.Comparator; +import java.util.Random; + +public class RBTree { + private RBNode root; + private Comparator comparator; + protected static final boolean DEBUGGING = true; + protected static final boolean VERBOSE = true; + protected static final boolean REALLY_VERBOSE = false; + + public RBTree(Comparator comparator) { + this.comparator = comparator; + } + + public RBNode getRoot() { + return root; + } + + public void insertNode(RBNode x) { + treeInsert(x); + + x.setColor(RBColor.RED); + boolean shouldPropagate = x.update(); + + if (DEBUGGING && REALLY_VERBOSE) { + System.err.println("RBTree.insertNode"); + } + + RBNode propagateStart = x; + + // Loop invariant: x has been updated. + while ((x != root) && (x.getParent().getColor() == RBColor.RED)) { + if (x.getParent() == x.getParent().getParent().getLeft()) { + RBNode y = x.getParent().getParent().getRight(); + if ((y != null) && (y.getColor() == RBColor.RED)) { + // Case 1 + if (DEBUGGING && REALLY_VERBOSE) { + System.err.println(" Case 1/1"); + } + x.getParent().setColor(RBColor.BLACK); + y.setColor(RBColor.BLACK); + x.getParent().getParent().setColor(RBColor.RED); + x.getParent().update(); + x = x.getParent().getParent(); + shouldPropagate = x.update(); + propagateStart = x; + } else { + if (x == x.getParent().getRight()) { + // Case 2 + if (DEBUGGING && REALLY_VERBOSE) { + System.err.println(" Case 1/2"); + } + x = x.getParent(); + leftRotate(x); + } + // Case 3 + if (DEBUGGING && REALLY_VERBOSE) { + System.err.println(" Case 1/3"); + } + x.getParent().setColor(RBColor.BLACK); + x.getParent().getParent().setColor(RBColor.RED); + shouldPropagate = rightRotate(x.getParent().getParent()); + propagateStart = x.getParent(); + } + } else { + // Same as then clause with "right" and "left" exchanged + RBNode y = x.getParent().getParent().getLeft(); + if ((y != null) && (y.getColor() == RBColor.RED)) { + // Case 1 + if (DEBUGGING && REALLY_VERBOSE) { + System.err.println(" Case 2/1"); + } + x.getParent().setColor(RBColor.BLACK); + y.setColor(RBColor.BLACK); + x.getParent().getParent().setColor(RBColor.RED); + x.getParent().update(); + x = x.getParent().getParent(); + shouldPropagate = x.update(); + propagateStart = x; + } else { + if (x == x.getParent().getLeft()) { + // Case 2 + if (DEBUGGING && REALLY_VERBOSE) { + System.err.println(" Case 2/2"); + } + x = x.getParent(); + rightRotate(x); + } + // Case 3 + if (DEBUGGING && REALLY_VERBOSE) { + System.err.println(" Case 2/3"); + } + x.getParent().setColor(RBColor.BLACK); + x.getParent().getParent().setColor(RBColor.RED); + shouldPropagate = leftRotate(x.getParent().getParent()); + propagateStart = x.getParent(); + } + } + } + + while (shouldPropagate && (propagateStart != root)) { + if (DEBUGGING && REALLY_VERBOSE) { + System.err.println(" Propagating"); + } + propagateStart = propagateStart.getParent(); + shouldPropagate = propagateStart.update(); + } + + root.setColor(RBColor.BLACK); + + if (DEBUGGING) { + verify(); + } + } + + /** FIXME: this does not work properly yet for augmented red-black + trees since it doesn't update nodes. Need to figure out exactly + from which points we need to propagate updates upwards. */ + public void deleteNode(RBNode z) { + // This routine splices out a node. Note that we may not actually + // delete the RBNode z from the tree, but may instead remove + // another node from the tree, copying its contents into z. + + // y is the node to be unlinked from the tree + RBNode y; + if ((z.getLeft() == null) || (z.getRight() == null)) { + y = z; + } else { + y = treeSuccessor(z); + } + // y is guaranteed to be non-null at this point + RBNode x; + if (y.getLeft() != null) { + x = y.getLeft(); + } else { + x = y.getRight(); + } + // x is the potential child of y to replace it in the tree. + // x may be null at this point + RBNode xParent; + if (x != null) { + x.setParent(y.getParent()); + xParent = x.getParent(); + } else { + xParent = y.getParent(); + } + if (y.getParent() == null) { + root = x; + } else { + if (y == y.getParent().getLeft()) { + y.getParent().setLeft(x); + } else { + y.getParent().setRight(x); + } + } + if (y != z) { + z.copyFrom(y); + } + if (y.getColor() == RBColor.BLACK) { + deleteFixup(x, xParent); + } + + if (DEBUGGING) { + verify(); + } + } + + public void print() { + printOn(System.out); + } + + public void printOn(PrintStream tty) { + printFromNode(root, tty, 0); + } + + //---------------------------------------------------------------------- + // Functionality overridable by subclass + // + + protected Object getNodeValue(RBNode node) { + return node.getData(); + } + + /** Verify invariants are preserved */ + protected void verify() { + verifyFromNode(root); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + // + // Vanilla binary search tree operations + // + + private void treeInsert(RBNode z) { + RBNode y = null; + RBNode x = root; + + while (x != null) { + y = x; + if (comparator.compare(getNodeValue(z), getNodeValue(x)) < 0) { + x = x.getLeft(); + } else { + x = x.getRight(); + } + } + z.setParent(y); + if (y == null) { + root = z; + } else { + if (comparator.compare(getNodeValue(z), getNodeValue(y)) < 0) { + y.setLeft(z); + } else { + y.setRight(z); + } + } + } + + private RBNode treeSuccessor(RBNode x) { + if (x.getRight() != null) { + return treeMinimum(x.getRight()); + } + RBNode y = x.getParent(); + while ((y != null) && (x == y.getRight())) { + x = y; + y = y.getParent(); + } + return y; + } + + private RBNode treeMinimum(RBNode x) { + while (x.getLeft() != null) { + x = x.getLeft(); + } + return x; + } + + // + // Insertion and deletion helpers + // + + /** Returns true if updates must continue propagating up the tree */ + private boolean leftRotate(RBNode x) { + // Set y. + RBNode y = x.getRight(); + // Turn y's left subtree into x's right subtree. + x.setRight(y.getLeft()); + if (y.getLeft() != null) { + y.getLeft().setParent(x); + } + // Link x's parent to y. + y.setParent(x.getParent()); + if (x.getParent() == null) { + root = y; + } else { + if (x == x.getParent().getLeft()) { + x.getParent().setLeft(y); + } else { + x.getParent().setRight(y); + } + } + // Put x on y's left. + y.setLeft(x); + x.setParent(y); + // Update nodes in appropriate order (lowest to highest) + boolean res = x.update(); + res = y.update() || res; + return res; + } + + /** Returns true if updates must continue propagating up the tree */ + private boolean rightRotate(RBNode y) { + // Set x. + RBNode x = y.getLeft(); + // Turn x's right subtree into y's left subtree. + y.setLeft(x.getRight()); + if (x.getRight() != null) { + x.getRight().setParent(y); + } + // Link y's parent into x. + x.setParent(y.getParent()); + if (y.getParent() == null) { + root = x; + } else { + if (y == y.getParent().getLeft()) { + y.getParent().setLeft(x); + } else { + y.getParent().setRight(x); + } + } + // Put y on x's right. + x.setRight(y); + y.setParent(x); + // Update nodes in appropriate order (lowest to highest) + boolean res = y.update(); + res = x.update() || res; + return res; + } + + /** Restores red-black property to tree after splicing out node + during deletion. Note that x may be null, which is why xParent + must be specified. */ + private void deleteFixup(RBNode x, RBNode xParent) { + while ((x != root) && ((x == null) || (x.getColor() == RBColor.BLACK))) { + if (x == xParent.getLeft()) { + // NOTE: the text points out that w can not be null. The + // reason is not obvious from simply examining the code; it + // comes about because of properties of the red-black tree. + RBNode w = xParent.getRight(); + if (DEBUGGING) { + if (w == null) { + throw new RuntimeException("x's sibling should not be null"); + } + } + if (w.getColor() == RBColor.RED) { + // Case 1 + w.setColor(RBColor.BLACK); + xParent.setColor(RBColor.RED); + leftRotate(xParent); + w = xParent.getRight(); + } + if (((w.getLeft() == null) || (w.getLeft().getColor() == RBColor.BLACK)) && + ((w.getRight() == null) || (w.getRight().getColor() == RBColor.BLACK))) { + // Case 2 + w.setColor(RBColor.RED); + x = xParent; + xParent = x.getParent(); + } else { + if ((w.getRight() == null) || (w.getRight().getColor() == RBColor.BLACK)) { + // Case 3 + w.getLeft().setColor(RBColor.BLACK); + w.setColor(RBColor.RED); + rightRotate(w); + w = xParent.getRight(); + } + // Case 4 + w.setColor(xParent.getColor()); + xParent.setColor(RBColor.BLACK); + if (w.getRight() != null) { + w.getRight().setColor(RBColor.BLACK); + } + leftRotate(xParent); + x = root; + xParent = x.getParent(); + } + } else { + // Same as clause above with "right" and "left" + // exchanged + + // NOTE: the text points out that w can not be null. The + // reason is not obvious from simply examining the code; it + // comes about because of properties of the red-black tree. + RBNode w = xParent.getLeft(); + if (DEBUGGING) { + if (w == null) { + throw new RuntimeException("x's sibling should not be null"); + } + } + if (w.getColor() == RBColor.RED) { + // Case 1 + w.setColor(RBColor.BLACK); + xParent.setColor(RBColor.RED); + rightRotate(xParent); + w = xParent.getLeft(); + } + if (((w.getRight() == null) || (w.getRight().getColor() == RBColor.BLACK)) && + ((w.getLeft() == null) || (w.getLeft().getColor() == RBColor.BLACK))) { + // Case 2 + w.setColor(RBColor.RED); + x = xParent; + xParent = x.getParent(); + } else { + if ((w.getLeft() == null) || (w.getLeft().getColor() == RBColor.BLACK)) { + // Case 3 + w.getRight().setColor(RBColor.BLACK); + w.setColor(RBColor.RED); + leftRotate(w); + w = xParent.getLeft(); + } + // Case 4 + w.setColor(xParent.getColor()); + xParent.setColor(RBColor.BLACK); + if (w.getLeft() != null) { + w.getLeft().setColor(RBColor.BLACK); + } + rightRotate(xParent); + x = root; + xParent = x.getParent(); + } + } + } + if (x != null) { + x.setColor(RBColor.BLACK); + } + } + + // Returns the number of black children along all paths to all + // leaves of the given node + private int verifyFromNode(RBNode node) { + // Bottoms out at leaf nodes + if (node == null) { + return 1; + } + + // Each node is either red or black + if (!((node.getColor() == RBColor.RED) || + (node.getColor() == RBColor.BLACK))) { + if (DEBUGGING) { + System.err.println("Verify failed:"); + printOn(System.err); + } + throw new RuntimeException("Verify failed (1)"); + } + + // Every leaf (null) is black + + if (node.getColor() == RBColor.RED) { + // Both its children are black + if (node.getLeft() != null) { + if (node.getLeft().getColor() != RBColor.BLACK) { + if (DEBUGGING) { + System.err.println("Verify failed:"); + printOn(System.err); + } + throw new RuntimeException("Verify failed (2)"); + } + } + if (node.getRight() != null) { + if (node.getRight().getColor() != RBColor.BLACK) { + if (DEBUGGING) { + System.err.println("Verify failed:"); + printOn(System.err); + } + throw new RuntimeException("Verify failed (3)"); + } + } + } + + // Every simple path to a leaf contains the same number of black + // nodes + int i = verifyFromNode(node.getLeft()); + int j = verifyFromNode(node.getRight()); + if (i != j) { + if (DEBUGGING) { + System.err.println("Verify failed:"); + printOn(System.err); + } + throw new RuntimeException("Verify failed (4) (left black count = " + + i + ", right black count = " + j + ")"); + } + + return i + ((node.getColor() == RBColor.RED) ? 0 : 1); + } + + /** Debugging */ + private void printFromNode(RBNode node, PrintStream tty, int indentDepth) { + for (int i = 0; i < indentDepth; i++) { + tty.print(" "); + } + + tty.print("-"); + if (node == null) { + tty.println(); + return; + } + + tty.println(" " + getNodeValue(node) + + ((node.getColor() == RBColor.RED) ? " (red)" : " (black)")); + printFromNode(node.getLeft(), tty, indentDepth + 2); + printFromNode(node.getRight(), tty, indentDepth + 2); + } + + //---------------------------------------------------------------------- + // Test harness + // + + public static void main(String[] args) { + int treeSize = 10000; + int maxVal = treeSize; + System.err.println("Building tree..."); + RBTree tree = new RBTree(new Comparator() { + public int compare(Object o1, Object o2) { + Integer i1 = (Integer) o1; + Integer i2 = (Integer) o2; + if (i1.intValue() < i2.intValue()) { + return -1; + } else if (i1.intValue() == i2.intValue()) { + return 0; + } + return 1; + } + }); + Random rand = new Random(System.currentTimeMillis()); + for (int i = 0; i < treeSize; i++) { + Integer val = new Integer(rand.nextInt(maxVal) + 1); + try { + tree.insertNode(new RBNode(val)); + if ((i > 0) && (i % 100 == 0)) { + System.err.print(i + "..."); + System.err.flush(); + } + } + catch (Exception e) { + e.printStackTrace(); + System.err.println("While inserting value " + val); + tree.printOn(System.err); + System.exit(1); + } + } + // Now churn data in tree by deleting and inserting lots of nodes + System.err.println(); + System.err.println("Churning tree..."); + for (int i = 0; i < treeSize; i++) { + if (DEBUGGING && VERBOSE) { + System.err.println("Iteration " + i + ":"); + tree.printOn(System.err); + } + // Pick a random value to remove (NOTE that this is not + // uniformly distributed) + RBNode xParent = null; + RBNode x = tree.getRoot(); + int depth = 0; + while (x != null) { + // Traverse path down to leaf + xParent = x; + if (rand.nextBoolean()) { + x = x.getLeft(); + } else { + x = x.getRight(); + } + ++depth; + } + // Pick a random height at which to remove value + int height = rand.nextInt(depth); + if (DEBUGGING) { + if (height >= depth) { + throw new RuntimeException("bug in java.util.Random"); + } + } + // Walk that far back up (FIXME: off by one?) + while (height > 0) { + xParent = xParent.getParent(); + --height; + } + // Tell the tree to remove this node + if (DEBUGGING && VERBOSE) { + System.err.println("(Removing value " + tree.getNodeValue(xParent) + ")"); + } + tree.deleteNode(xParent); + + // Now create and insert a new value + Integer newVal = new Integer(rand.nextInt(maxVal) + 1); + if (DEBUGGING && VERBOSE) { + System.err.println("(Inserting value " + newVal + ")"); + } + tree.insertNode(new RBNode(newVal)); + } + System.err.println("All tests passed."); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ReversePtrs.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ReversePtrs.java new file mode 100644 index 00000000000..fa2cf65839b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ReversePtrs.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.*; +import sun.jvm.hotspot.oops.*; + +/** ReversePtrs hashtable. */ + +public class ReversePtrs { + private HashMap rp; + + public ReversePtrs() { + rp = new HashMap(); + } + + public void put(LivenessPathElement from, Oop to) { + if (to == null) return; + ArrayList al = (ArrayList) rp.get((Object) to); + if (al == null) al = new ArrayList(); + // Inserting at the beginning is a hack to change the reported + // paths from LivenessAnalysis to look more like they used to; + // otherwise paths through the Finalizer queue to popular objects + // seem to be preferred + al.add(0, (Object) from); + rp.put((Object) to, (Object) al); + } + + /** Returns an ArrayList of the incoming references to this Oop if + it is alive, and null if it is dead according to the + ReversePtrsAnalysis. Currently not all roots are scanned so this + result is frequently inaccurate for JVM-internal objects, but is + usually correct for Java-level objects. */ + public ArrayList/**/ get(Oop obj) { + return (ArrayList) rp.get((Object)obj); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ReversePtrsAnalysis.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ReversePtrsAnalysis.java new file mode 100644 index 00000000000..f3e219c2430 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ReversePtrsAnalysis.java @@ -0,0 +1,299 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.gc_interface.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +/** For a set of known roots, descends recursively into the object + graph, for each object recording those objects (and their fields) + which point to it. NOTE: currently only a subset of the roots + known to the VM is exposed to the SA: objects on the stack, static + fields in classes, and JNI handles. These should be most of the + user-level roots keeping objects alive. */ + +public class ReversePtrsAnalysis { + // Used for debugging this code + private static final boolean DEBUG = false; + + public ReversePtrsAnalysis() { + } + + /** Sets an optional progress thunk */ + public void setHeapProgressThunk(HeapProgressThunk thunk) { + progressThunk = thunk; + } + + + /** Runs the analysis algorithm */ + public void run() { + if (VM.getVM().getRevPtrs() != null) { + return; // Assume already done + } + + VM vm = VM.getVM(); + rp = new ReversePtrs(); + vm.setRevPtrs(rp); + Universe universe = vm.getUniverse(); + CollectedHeap collHeap = universe.heap(); + usedSize = collHeap.used(); + visitedSize = 0; + + // Note that an experiment to iterate the heap linearly rather + // than in recursive-descent order has been done. It turns out + // that the recursive-descent algorithm is nearly twice as fast + // due to the fact that it scans only live objects and (currently) + // only a fraction of the perm gen, namely the static fields + // contained in instanceKlasses. (Iterating the heap linearly + // would also change the semantics of the result so that + // ReversePtrs.get() would return a non-null value even for dead + // objects.) Nonetheless, the reverse pointer computation is still + // quite slow and optimization in field iteration of objects + // should be done. + + if (progressThunk != null) { + // Get it started + progressThunk.heapIterationFractionUpdate(0); + } + + // Allocate mark bits for heap + markBits = new MarkBits(collHeap); + + // Get a hold of the object heap + heap = vm.getObjectHeap(); + + // Do each thread's roots + for (JavaThread thread = VM.getVM().getThreads().first(); + thread != null; + thread = thread.next()) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + thread.printThreadIDOn(new PrintStream(bos)); + String threadDesc = + " in thread \"" + thread.getThreadName() + + "\" (id " + bos.toString() + ")"; + doStack(thread, + new RootVisitor("Stack root" + threadDesc)); + doJNIHandleBlock(thread.activeHandles(), + new RootVisitor("JNI handle root" + threadDesc)); + } + + // Do global JNI handles + JNIHandles handles = VM.getVM().getJNIHandles(); + doJNIHandleBlock(handles.globalHandles(), + new RootVisitor("Global JNI handle root")); + doJNIHandleBlock(handles.weakGlobalHandles(), + new RootVisitor("Weak global JNI handle root")); + + // Do Java-level static fields in perm gen + heap.iteratePerm(new DefaultHeapVisitor() { + public boolean doObj(Oop obj) { + if (obj instanceof InstanceKlass) { + final InstanceKlass ik = (InstanceKlass) obj; + ik.iterateFields( + new DefaultOopVisitor() { + public void doOop(OopField field, boolean isVMField) { + Oop next = field.getValue(ik); + LivenessPathElement lp = new LivenessPathElement(null, + new NamedFieldIdentifier("Static field \"" + + field.getID().getName() + + "\" in class \"" + + ik.getName().asString() + "\"")); + rp.put(lp, next); + try { + markAndTraverse(next); + } catch (AddressException e) { + System.err.print("RevPtrs analysis: WARNING: AddressException at 0x" + + Long.toHexString(e.getAddress()) + + " while traversing static fields of InstanceKlass "); + ik.printValueOn(System.err); + System.err.println(); + } catch (UnknownOopException e) { + System.err.println("RevPtrs analysis: WARNING: UnknownOopException while " + + "traversing static fields of InstanceKlass "); + ik.printValueOn(System.err); + System.err.println(); + } + } + }, + false); + } + return false; + } + }); + + if (progressThunk != null) { + progressThunk.heapIterationComplete(); + } + + // Clear out markBits + markBits = null; + } + + + //--------------------------------------------------------------------------- + // Internals only below this point + // + private HeapProgressThunk progressThunk; + private long usedSize; + private long visitedSize; + private double lastNotificationFraction; + private static final double MINIMUM_NOTIFICATION_FRACTION = 0.01; + private ObjectHeap heap; + private MarkBits markBits; + private int depth; // Debugging only + private ReversePtrs rp; + + private void markAndTraverse(OopHandle handle) { + try { + markAndTraverse(heap.newOop(handle)); + } catch (AddressException e) { + System.err.println("RevPtrs analysis: WARNING: AddressException at 0x" + + Long.toHexString(e.getAddress()) + + " while traversing oop at " + handle); + } catch (UnknownOopException e) { + System.err.println("RevPtrs analysis: WARNING: UnknownOopException for " + + "oop at " + handle); + } + } + + private void printHeader() { + for (int i = 0; i < depth; i++) { + System.err.print(" "); + } + } + + private void markAndTraverse(final Oop obj) { + + // End of path + if (obj == null) { + return; + } + + // Visited object + if (!markBits.mark(obj)) { + return; + } + + // Root of work list for objects to be visited. A simple + // stack for saving new objects to be analyzed. + + final Stack workList = new Stack(); + + // Next object to be visited. + Oop next = obj; + + try { + // Node in the list currently being visited. + + while (true) { + final Oop currObj = next; + + // For the progress meter + if (progressThunk != null) { + visitedSize += currObj.getObjectSize(); + double curFrac = (double) visitedSize / (double) usedSize; + if (curFrac > + lastNotificationFraction + MINIMUM_NOTIFICATION_FRACTION) { + progressThunk.heapIterationFractionUpdate(curFrac); + lastNotificationFraction = curFrac; + } + } + + if (DEBUG) { + ++depth; + printHeader(); + System.err.println("ReversePtrs.markAndTraverse(" + + currObj.getHandle() + ")"); + } + + // Iterate over the references in the object. Do the + // reverse pointer analysis for each reference. + // Add the reference to the work-list so that its + // references will be visited. + currObj.iterate(new DefaultOopVisitor() { + public void doOop(OopField field, boolean isVMField) { + // "field" refers to a reference in currObj + Oop next = field.getValue(currObj); + rp.put(new LivenessPathElement(currObj, field.getID()), next); + if ((next != null) && markBits.mark(next)) { + workList.push(next); + } + } + }, false); + + if (DEBUG) { + --depth; + } + + // Get the next object to visit. + next = (Oop) workList.pop(); + } + } catch (EmptyStackException e) { + // Done + } catch (NullPointerException e) { + System.err.println("ReversePtrs: WARNING: " + e + + " during traversal"); + } catch (Exception e) { + System.err.println("ReversePtrs: WARNING: " + e + + " during traversal"); + } + } + + + class RootVisitor implements AddressVisitor { + RootVisitor(String baseRootDescription) { + this.baseRootDescription = baseRootDescription; + } + + public void visitAddress(Address addr) { + Oop next = heap.newOop(addr.getOopHandleAt(0)); + LivenessPathElement lp = new LivenessPathElement(null, + new NamedFieldIdentifier(baseRootDescription + + " @ " + addr)); + rp.put(lp, next); + markAndTraverse(next); + } + + private String baseRootDescription; + } + + // Traverse the roots on a given thread's stack + private void doStack(JavaThread thread, AddressVisitor oopVisitor) { + for (StackFrameStream fst = new StackFrameStream(thread); !fst.isDone(); fst.next()) { + fst.getCurrent().oopsDo(oopVisitor, fst.getRegisterMap()); + } + } + + // Traverse a JNIHandleBlock + private void doJNIHandleBlock(JNIHandleBlock handles, AddressVisitor oopVisitor) { + handles.oopsDo(oopVisitor); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RobustOopDeterminator.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RobustOopDeterminator.java new file mode 100644 index 00000000000..405609f25df --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/RobustOopDeterminator.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + +/** This class determines to the best of its ability, and in a + reasonably robust fashion, whether a given pointer is an intact + oop or not. It does this by checking the integrity of the + metaclass hierarchy. This is only intended for use in the + debugging system. It may provide more resilience to unexpected VM + states than the ObjectHeap code. */ + +public class RobustOopDeterminator { + private static OopField klassField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("oopDesc"); + + klassField = type.getOopField("_klass"); + } + + public static boolean oopLooksValid(OopHandle oop) { + if (oop == null) { + return false; + } + if (!VM.getVM().getUniverse().isIn(oop)) { + return false; + } + try { + for (int i = 0; i < 4; ++i) { + OopHandle next = klassField.getValue(oop); + if (next == null) { + return false; + } + if (next.equals(oop)) { + return true; + } + oop = next; + } + return false; + } + catch (AddressException e) { + return false; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/StreamMonitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/StreamMonitor.java new file mode 100644 index 00000000000..b8a66e033e5 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/StreamMonitor.java @@ -0,0 +1,226 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.io.*; +import java.util.*; + +/** Reads all of the data from the given InputStream, and allows the + caller to wait for a given string to come in or watch for many + possible strings. */ + +public class StreamMonitor implements Runnable { + private BufferedReader input; + private boolean printStreamContents; + + private String waitString; + private boolean waitStringSeen; + private List triggers = new LinkedList(); + private List triggersSeen = new LinkedList(); + + private String prefixString; + private boolean printContents; + + private StringBuffer captureBuffer; + + class Trigger { + private String[] triggerStrings; + private int triggerVal; + + Trigger(String str, int val) { + triggerStrings = new String[] { str }; + triggerVal = val; + } + + // Hack because we don't have a regexp library yet. + // This requires all strings to be matched. + Trigger(String[] strs, int val) { + triggerStrings = strs; + triggerVal = val; + } + + boolean matches(String str) { + for (int i = 0; i < triggerStrings.length; i++) { + if (str.indexOf(triggerStrings[i]) == -1) { + return false; + } + } + return true; + } + + boolean equals(String[] strs) { + if (strs.length != triggerStrings.length) { + return false; + } + + for (int i = 0; i < strs.length; i++) { + if (!strs[i].equals(triggerStrings[i])) { + return false; + } + } + + return true; + } + } + + /** Equivalent to StreamMonitor(istr, null, false) */ + public StreamMonitor(InputStream istr) { + this(istr, null, false); + } + + public StreamMonitor(InputStream istr, String prefixString, boolean printContents) { + input = new BufferedReader(new InputStreamReader(istr)); + this.prefixString = prefixString; + this.printContents = printContents; + Thread thr = new Thread(this); + thr.setDaemon(true); + thr.start(); + } + + /** Adds a "trigger", which the stream watches for and, if seen, + reports the trigger value of via the getTriggers() method. + Returns true if the addition was successful, false if the string + was already present as a trigger. */ + public boolean addTrigger(String str, int value) { + return addTrigger(new String[] { str }, value); + } + + /** Adds a "trigger", which the stream watches for and, if seen, + reports the trigger value of via the getTriggers() method. + Returns true if the addition was successful, false if the string + was already present as a trigger. */ + public boolean addTrigger(String[] strs, int value) { + for (Iterator iter = triggers.iterator(); iter.hasNext(); ) { + Trigger trigger = (Trigger) iter.next(); + if (trigger.equals(strs)) { + return false; + } + } + Trigger trigger = new Trigger(strs, value); + return triggers.add(trigger); + } + + /** Removes a previously added trigger. Returns true if it was + present, false if not. */ + public boolean removeTrigger(String str) { + return removeTrigger(new String[] { str }); + } + + /** Removes a previously added trigger. Returns true if it was + present, false if not. */ + public boolean removeTrigger(String[] strs) { + for (ListIterator iter = triggers.listIterator(); iter.hasNext(); ) { + Trigger trigger = (Trigger) iter.next(); + if (trigger.equals(strs)) { + iter.remove(); + return true; + } + } + return false; + } + + /** Returns an List of java.lang.Integer objects indicating the + values of the triggers seen since the last call to + getTriggersSeen. If there were no triggers seen, returns an + empty list; does not return null. */ + public synchronized List getTriggersSeen() { + List tmpList = triggersSeen; + triggersSeen = new LinkedList(); + return tmpList; + } + + /** Waits for the specified string to come in for the given period + of time (measured in milliseconds). */ + public synchronized boolean waitFor(String str, long millis) { + waitString = str; + waitStringSeen = false; + try { + wait(millis); + } + catch (InterruptedException e) { + } + + waitString = null; + return waitStringSeen; + } + + public synchronized void startCapture() { + captureBuffer = new StringBuffer(); + } + + public synchronized String stopCapture() { + String ret = captureBuffer.toString(); + captureBuffer = null; + return ret; + } + + public void run() { + byte[] buf = new byte[10240]; + boolean shouldContinue = true; + + try { + do { + String str = input.readLine(); + if (str == null) { + shouldContinue = false; + } else { + if (printContents) { + System.err.println(prefixString + ": " + str); + } + synchronized (this) { + + if (captureBuffer != null) { + captureBuffer.append(str); + captureBuffer.append("\n"); + } + + // Check wait string + if ((waitString != null) && + (str.indexOf(waitString) != -1)) { + waitStringSeen = true; + notifyAll(); + } + + // Check all triggers + for (Iterator iter = triggers.iterator(); iter.hasNext(); ) { + Trigger trigger = (Trigger) iter.next(); + if (trigger.matches(str)) { + triggersSeen.add(new Integer(trigger.triggerVal)); + } + } + } + } + } while (shouldContinue); + } + catch (IOException e) { + } + + System.err.print("StreamMonitor "); + if (prefixString != null) { + System.err.print("\"" + prefixString + "\" "); + } + System.err.println("exiting"); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/SystemDictionaryHelper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/SystemDictionaryHelper.java new file mode 100644 index 00000000000..936baa597db --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/SystemDictionaryHelper.java @@ -0,0 +1,141 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; + +public class SystemDictionaryHelper { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(); + } + }); + } + + private static synchronized void initialize() { + klasses = null; + } + + // Instance klass array sorted by name. + private static InstanceKlass[] klasses; + + // side-effect!. caches the instance klass array. + public static synchronized InstanceKlass[] getAllInstanceKlasses() { + if (klasses != null) { + return klasses; + } + + final Vector tmp = new Vector(); + SystemDictionary dict = VM.getVM().getSystemDictionary(); + dict.classesDo(new SystemDictionary.ClassVisitor() { + public void visit(Klass k) { + if (k instanceof InstanceKlass) { + InstanceKlass ik = (InstanceKlass) k; + tmp.add(ik); + } + } + }); + + Object[] tmpArray = tmp.toArray(); + klasses = new InstanceKlass[tmpArray.length]; + System.arraycopy(tmpArray, 0, klasses, 0, tmpArray.length); + Arrays.sort(klasses, new Comparator() { + public int compare(Object o1, Object o2) { + InstanceKlass k1 = (InstanceKlass) o1; + InstanceKlass k2 = (InstanceKlass) o2; + Symbol s1 = k1.getName(); + Symbol s2 = k2.getName(); + return s1.asString().compareTo(s2.asString()); + } + }); + return klasses; + } + + // returns array of instance klasses whose name contains given namePart + public static InstanceKlass[] findInstanceKlasses(String namePart) { + namePart = namePart.replace('.', '/'); + InstanceKlass[] tmpKlasses = getAllInstanceKlasses(); + + Vector tmp = new Vector(); + for (int i = 0; i < tmpKlasses.length; i++) { + String name = tmpKlasses[i].getName().asString(); + if (name.indexOf(namePart) != -1) { + tmp.add(tmpKlasses[i]); + } + } + + Object[] tmpArray = tmp.toArray(); + InstanceKlass[] searchResult = new InstanceKlass[tmpArray.length]; + System.arraycopy(tmpArray, 0, searchResult, 0, tmpArray.length); + return searchResult; + } + + // find first class whose name matches exactly the given argument. + public static InstanceKlass findInstanceKlass(String className) { + // convert to internal name + className = className.replace('.', '/'); + SystemDictionary sysDict = VM.getVM().getSystemDictionary(); + + // check whether we have a bootstrap class of given name + Klass klass = sysDict.find(className, null, null); + if (klass != null) { + return (InstanceKlass) klass; + } + + // check whether we have a system class of given name + klass = sysDict.find(className, sysDict.javaSystemLoader(), null); + if (klass != null) { + return (InstanceKlass) klass; + } + + // didn't find bootstrap or system class of given name. + // search through the entire dictionary.. + InstanceKlass[] tmpKlasses = getAllInstanceKlasses(); + // instance klass array is sorted by name. do binary search + int low = 0; + int high = tmpKlasses.length-1; + + int mid = -1; + while (low <= high) { + mid = (low + high) >> 1; + InstanceKlass midVal = tmpKlasses[mid]; + int cmp = midVal.getName().asString().compareTo(className); + + if (cmp < 0) { + low = mid + 1; + } else if (cmp > 0) { + high = mid - 1; + } else { // match found + return tmpKlasses[mid]; + } + } + // no match .. + return null; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/TwoOopHashtable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/TwoOopHashtable.java new file mode 100644 index 00000000000..73cdcd1e95a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/TwoOopHashtable.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; + +public class TwoOopHashtable extends Hashtable { + public TwoOopHashtable(Address addr) { + super(addr); + } + + public long computeHash(Symbol name, Oop loader) { + return ((int) name.identityHash() + ^ (int) (loader == null ? 0 : loader.identityHash())) & 0xFFFFFFFFL; + } + + public int indexFor(Symbol name, Oop loader) { + return hashToIndex(computeHash(name, loader)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/UnsupportedPlatformException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/UnsupportedPlatformException.java new file mode 100644 index 00000000000..5ff6ea2a45c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/UnsupportedPlatformException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +public class UnsupportedPlatformException extends RuntimeException { + public UnsupportedPlatformException() { + super(); + } + + public UnsupportedPlatformException(String message) { + super(message); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/WorkerThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/WorkerThread.java new file mode 100644 index 00000000000..002df263f3e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/WorkerThread.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities; + +/** This class abstracts the notion of a worker thread which is fed + tasks in the form of Runnables. */ + +public class WorkerThread { + private volatile boolean done = false; + private MessageQueueBackend mqb; + private MessageQueue mq; + + public WorkerThread() { + mqb = new MessageQueueBackend(); + mq = mqb.getFirstQueue(); + new Thread(new MainLoop()).start(); + } + + /** Runs the given Runnable in the thread represented by this + WorkerThread object at an unspecified later time. */ + public void invokeLater(Runnable runnable) { + mq.writeMessage(runnable); + } + + /** Can be used to dispose of the internal worker thread. Note that + this method may return before the internal worker thread + terminates. */ + public void shutdown() { + done = true; + mq.writeMessage(new Runnable() { public void run() {} }); + } + + class MainLoop implements Runnable { + private MessageQueue myMq; + + public MainLoop() { + myMq = mqb.getSecondQueue(); + } + + public void run() { + while (!done) { + Runnable runnable = (Runnable) myMq.readMessage(); + try { + runnable.run(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedBoolean.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedBoolean.java new file mode 100644 index 00000000000..421304296a8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedBoolean.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.memo; + +/** A memoized boolean. Override {@link #computeValue} in subclasses; + call {@link #getValue} in using code. */ + +public abstract class MemoizedBoolean { + private boolean computed; + private boolean value; + + /** Should compute the value of this memoized object. This will only + be called once, upon the first call to {@link #getValue}. */ + protected abstract boolean computeValue(); + + /** Public accessor for the memoized value. */ + public boolean getValue() { + if (!computed) { + value = computeValue(); + computed = true; + } + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedByte.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedByte.java new file mode 100644 index 00000000000..7227e39bc78 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedByte.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.memo; + +/** A memoized byte. Override {@link #computeValue} in subclasses; + call {@link #getValue} in using code. */ + +public abstract class MemoizedByte { + private boolean computed; + private byte value; + + /** Should compute the value of this memoized object. This will only + be called once, upon the first call to {@link #getValue}. */ + protected abstract byte computeValue(); + + /** Public accessor for the memoized value. */ + public byte getValue() { + if (!computed) { + value = computeValue(); + computed = true; + } + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedChar.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedChar.java new file mode 100644 index 00000000000..252882c591e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedChar.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.memo; + +/** A memoized char. Override {@link #computeValue} in subclasses; + call {@link #getValue} in using code. */ + +public abstract class MemoizedChar { + private boolean computed; + private char value; + + /** Should compute the value of this memoized object. This will only + be called once, upon the first call to {@link #getValue}. */ + protected abstract char computeValue(); + + /** Public accessor for the memoized value. */ + public char getValue() { + if (!computed) { + value = computeValue(); + computed = true; + } + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedDouble.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedDouble.java new file mode 100644 index 00000000000..88a57fdf885 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedDouble.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.memo; + +/** A memoized double. Override {@link #computeValue} in subclasses; + call {@link #getValue} in using code. */ + +public abstract class MemoizedDouble { + private boolean computed; + private double value; + + /** Should compute the value of this memoized object. This will only + be called once, upon the first call to {@link #getValue}. */ + protected abstract double computeValue(); + + /** Public accessor for the memoized value. */ + public double getValue() { + if (!computed) { + value = computeValue(); + computed = true; + } + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedFloat.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedFloat.java new file mode 100644 index 00000000000..a3d68e929f4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedFloat.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.memo; + +/** A memoized float. Override {@link #computeValue} in subclasses; + call {@link #getValue} in using code. */ + +public abstract class MemoizedFloat { + private boolean computed; + private float value; + + /** Should compute the value of this memoized object. This will only + be called once, upon the first call to {@link #getValue}. */ + protected abstract float computeValue(); + + /** Public accessor for the memoized value. */ + public float getValue() { + if (!computed) { + value = computeValue(); + computed = true; + } + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedInt.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedInt.java new file mode 100644 index 00000000000..bbaef4215d2 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedInt.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.memo; + +/** A memoized int. Override {@link #computeValue} in subclasses; call + {@link #getValue} in using code. */ + +public abstract class MemoizedInt { + private boolean computed; + private int value; + + /** Should compute the value of this memoized object. This will only + be called once, upon the first call to {@link #getValue}. */ + protected abstract int computeValue(); + + /** Public accessor for the memoized value. */ + public int getValue() { + if (!computed) { + value = computeValue(); + computed = true; + } + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedLong.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedLong.java new file mode 100644 index 00000000000..5468e086c88 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedLong.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.memo; + +/** A memoized long. Override {@link #computeValue} in subclasses; + call {@link #getValue} in using code. */ + +public abstract class MemoizedLong { + private boolean computed; + private long value; + + /** Should compute the value of this memoized object. This will only + be called once, upon the first call to {@link #getValue}. */ + protected abstract long computeValue(); + + /** Public accessor for the memoized value. */ + public long getValue() { + if (!computed) { + value = computeValue(); + computed = true; + } + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedObject.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedObject.java new file mode 100644 index 00000000000..0602d9e9a7e --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedObject.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.memo; + +/** A memoized object. Override {@link #computeValue} in subclasses; + call {@link #getValue} in using code. */ + +public abstract class MemoizedObject { + private boolean computed; + private Object value; + + /** Should compute the value of this memoized object. This will only + be called once, upon the first call to {@link #getValue}. */ + protected abstract Object computeValue(); + + /** Public accessor for the memoized value. */ + public Object getValue() { + if (!computed) { + value = computeValue(); + computed = true; + } + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedShort.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedShort.java new file mode 100644 index 00000000000..fd60efd31b4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/memo/MemoizedShort.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.memo; + +/** A memoized short. Override {@link #computeValue} in subclasses; + call {@link #getValue} in using code. */ + +public abstract class MemoizedShort { + private boolean computed; + private short value; + + /** Should compute the value of this memoized object. This will only + be called once, upon the first call to {@link #getValue}. */ + protected abstract short computeValue(); + + /** Public accessor for the memoized value. */ + public short getValue() { + if (!computed) { + value = computeValue(); + computed = true; + } + return value; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/Callable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/Callable.java new file mode 100644 index 00000000000..f5fad84f237 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/Callable.java @@ -0,0 +1,38 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.jvm.hotspot.utilities.soql; + +import javax.script.ScriptException; + +/** + * This interface is used to represent "function" valued + * properties in ScriptObjects. + */ +public interface Callable { + /** + * Call the underlying function passing the given + * arguments and return the result. + */ + public Object call(Object[] args) throws ScriptException; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/DefaultScriptObject.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/DefaultScriptObject.java new file mode 100644 index 00000000000..82d102cdf50 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/DefaultScriptObject.java @@ -0,0 +1,68 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; + +/** + * Dummy implementation for ScriptObject interface. This class + * supports empty set of named and indexed properties. Returns + * false always for "has" calls. And ignores "delete" and "put" + * calls. + */ +public class DefaultScriptObject implements ScriptObject { + public Object[] getIds() { + return EMPTY_ARRAY; + } + + public Object get(String name) { + return UNDEFINED; + } + + public Object get(int index) { + return UNDEFINED; + } + + public void put(String name, Object value) { + } + + public void put(int index, Object value) { + } + + public boolean has(String name) { + return false; + } + + public boolean has(int index) { + return false; + } + + public boolean delete(String name) { + return false; + } + + public boolean delete(int index) { + return false; + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/InvocableCallable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/InvocableCallable.java new file mode 100644 index 00000000000..29766326731 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/InvocableCallable.java @@ -0,0 +1,58 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.jvm.hotspot.utilities.soql; + +import javax.script.Invocable; +import javax.script.ScriptException; + +/** + * This Callable implementation invokes a script + * function of given name when called. If the target + * object is non-null, script "method" is invoked, else + * a "global" script function is invoked. + */ +public class InvocableCallable implements Callable { + private Object target; + private String name; + private Invocable invocable; + + public InvocableCallable(Object target, String name, + Invocable invocable) { + this.target = target; + this.name = name; + this.invocable = invocable; + } + + public Object call(Object[] args) throws ScriptException { + try { + if (target == null) { + return invocable.invokeFunction(name, args); + } else { + return invocable.invokeMethod(target, name, args); + } + } catch (NoSuchMethodException nme) { + throw new ScriptException(nme); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaArray.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaArray.java new file mode 100644 index 00000000000..551ce574219 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaArray.java @@ -0,0 +1,105 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import sun.jvm.hotspot.oops.*; + +/** + This is JavaScript wrapper for Java Array. +*/ + +public abstract class JSJavaArray extends JSJavaObject { + public JSJavaArray(Array array, JSJavaFactory fac) { + super(array, fac); + type = (JSJavaArrayKlass) fac.newJSJavaKlass(array.getKlass()); + } + + public final Array getArray() { + return (Array) getOop(); + } + + public final JSJavaClass getJSJavaClass() { + return type.getJSJavaClass(); + } + + public Object get(String name) { + if (name.equals("length")) { + return new Integer((int)getArray().getLength()); + } else { + return super.get(name); + } + } + + public Object get(int index) { + return (isInRange(index)) ? type.getFieldValue(index, getArray()) + : super.get(index); + } + + public Object[] getIds() { + Object[] superFields = super.getIds(); + final int len = (int) getArray().getLength(); + Object[] res = new Object[superFields.length + len]; + for (int i = 0; i < len; i++) { + res[i] = new Integer(i); + } + System.arraycopy(superFields, 0, res, len, superFields.length); + return res; + } + + public boolean has(String name) { + if (name.equals("length")) { + return true; + } else { + return super.has(name); + } + } + + public boolean has(int index) { + if (isInRange(index)) { + return true; + } else { + return super.has(index); + } + } + + public void put(String name, Object value) { + if (! name.equals("length")) { + super.put(name, value); + } + } + + public void put(int index, Object value) { + if (! isInRange(index)) { + super.put(index, value); + } + } + + //-- Internals only below this point + private boolean isInRange(int index) { + return index >= 0 && index < getArray().getLength(); + } + + private JSJavaArrayKlass type; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaArrayKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaArrayKlass.java new file mode 100644 index 00000000000..c9098672f65 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaArrayKlass.java @@ -0,0 +1,72 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; + +/** + This is JavaScript wrapper for Java ArrayKlass. +*/ + +public abstract class JSJavaArrayKlass extends JSJavaKlass { + public JSJavaArrayKlass(ArrayKlass kls, JSJavaFactory fac) { + super(kls, fac); + } + + public final ArrayKlass getArrayKlass() { + return (ArrayKlass) getKlass(); + } + + public Object getMetaClassFieldValue(String name) { + if (name.equals("dimension")) { + return new Long(getArrayKlass().getDimension()); + } else { + return super.getMetaClassFieldValue(name); + } + } + + public boolean hasMetaClassField(String name) { + if (name.equals("dimension")) { + return true; + } else { + return super.hasMetaClassField(name); + } + } + + public boolean isArray() { + return true; + } + + public String[] getMetaClassFieldNames() { + String[] superFields = super.getMetaClassFieldNames(); + String[] res = new String[superFields.length + 1]; + System.arraycopy(superFields, 0, res, 0, superFields.length); + res[superFields.length] = "dimension"; + return res; + } + + public abstract Object getFieldValue(int index, Array array); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaClass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaClass.java new file mode 100644 index 00000000000..e65c5fc010f --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaClass.java @@ -0,0 +1,64 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +public class JSJavaClass extends JSJavaInstance { + public JSJavaClass(Instance instance, JSJavaKlass jk, JSJavaFactory fac) { + super(instance, fac); + this.jklass = jk; + } + + public JSJavaKlass getJSJavaKlass() { + return jklass; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("Class (address="); + buf.append(getOop().getHandle()); + buf.append(", name="); + buf.append(jklass.getName()); + buf.append(')'); + return buf.toString(); + } + + protected Object getFieldValue(String name) { + return jklass.getMetaClassFieldValue(name); + } + + protected String[] getFieldNames() { + return jklass.getMetaClassFieldNames(); + } + + protected boolean hasField(String name) { + return jklass.hasMetaClassField(name); + } + + private JSJavaKlass jklass; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaFactory.java new file mode 100644 index 00000000000..a737527f06d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +public interface JSJavaFactory { + public JSJavaObject newJSJavaObject(Oop oop); + public JSJavaKlass newJSJavaKlass(Klass klass); + public JSJavaField newJSJavaField(Field f); + public JSJavaThread newJSJavaThread(JavaThread jt); + public JSJavaFrame newJSJavaFrame(JavaVFrame vf); + public JSList newJSList(List l); + public JSMap newJSMap(Map m); + public JSJavaHeap newJSJavaHeap(); + public JSJavaVM newJSJavaVM(); + // checks for one of the above special cases + public Object newJSJavaWrapper(Object o); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaFactoryImpl.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaFactoryImpl.java new file mode 100644 index 00000000000..bddde0f09d4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaFactoryImpl.java @@ -0,0 +1,196 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.lang.ref.*; +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +public class JSJavaFactoryImpl implements JSJavaFactory { + public JSJavaObject newJSJavaObject(Oop oop) { + if (oop == null) return null; + SoftReference sref = (SoftReference) om.get(oop); + JSJavaObject res = (sref != null)? (JSJavaObject) sref.get() : null; + if (res == null) { + if (oop instanceof TypeArray) { + res = new JSJavaTypeArray((TypeArray)oop, this); + } else if (oop instanceof ObjArray) { + res = new JSJavaObjArray((ObjArray)oop, this); + } else if (oop instanceof Instance) { + res = newJavaInstance((Instance) oop); + } else if (oop instanceof Method) { + res = new JSJavaMethod((Method) oop, this); + } + } + if (res != null) { + om.put(oop, new SoftReference(res)); + } + return res; + } + + public JSJavaKlass newJSJavaKlass(Klass klass) { + JSJavaKlass res = null; + if (klass instanceof InstanceKlass) { + res = new JSJavaInstanceKlass((InstanceKlass) klass, this); + } else if (klass instanceof ObjArrayKlass) { + res = new JSJavaObjArrayKlass((ObjArrayKlass) klass, this); + } else if (klass instanceof TypeArrayKlass) { + res = new JSJavaTypeArrayKlass((TypeArrayKlass) klass, this); + } + if (res != null) { + om.put(klass, new SoftReference(res)); + } + return res; + } + + public JSJavaField newJSJavaField(Field field) { + if (field == null) return null; + return new JSJavaField(field, this); + } + + public JSJavaThread newJSJavaThread(JavaThread jthread) { + if (jthread == null) return null; + return new JSJavaThread(jthread, this); + } + + public JSJavaFrame newJSJavaFrame(JavaVFrame jvf) { + if (jvf == null) return null; + return new JSJavaFrame(jvf, this); + } + + public JSList newJSList(List list) { + if (list == null) return null; + return new JSList(list, this); + } + + public JSMap newJSMap(Map map) { + if (map == null) return null; + return new JSMap(map, this); + } + + public Object newJSJavaWrapper(Object item) { + if (item == null) return null; + if (item instanceof Oop) { + return newJSJavaObject((Oop) item); + } else if (item instanceof Field) { + return newJSJavaField((Field) item); + } else if (item instanceof JavaThread) { + return newJSJavaThread((JavaThread) item); + } else if (item instanceof JavaVFrame) { + return newJSJavaFrame((JavaVFrame) item); + } else if (item instanceof List) { + return newJSList((List) item); + } else if (item instanceof Map) { + return newJSMap((Map) item); + } else { + // not-a-special-type, just return the input item + return item; + } + } + + public JSJavaHeap newJSJavaHeap() { + return new JSJavaHeap(this); + } + + public JSJavaVM newJSJavaVM() { + return new JSJavaVM(this); + } + + // -- Internals only below this point + private Symbol javaLangString() { + if (javaLangString == null) { + javaLangString = getSymbol("java/lang/String"); + } + return javaLangString; + } + + private Symbol javaLangThread() { + if (javaLangThread == null) { + javaLangThread = getSymbol("java/lang/Thread"); + } + return javaLangThread; + } + + private Symbol javaLangClass() { + if (javaLangClass == null) { + javaLangClass = getSymbol("java/lang/Class"); + } + return javaLangClass; + } + + private Symbol getSymbol(String str) { + return VM.getVM().getSymbolTable().probe(str); + } + + private JSJavaObject newJavaInstance(Instance instance) { + // look for well-known classes + Symbol className = instance.getKlass().getName(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(className != null, "Null class name"); + } + JSJavaObject res = null; + if (className.equals(javaLangString())) { + res = new JSJavaString(instance, this); + } else if (className.equals(javaLangThread())) { + res = new JSJavaThread(instance, this); + } else if (className.equals(javaLangClass())) { + Klass reflectedType = OopUtilities.classOopToKlass(instance); + if (reflectedType != null) { + JSJavaKlass jk = newJSJavaKlass(reflectedType); + // we don't support mirrors of VM internal Klasses + if (jk == null) return null; + res = new JSJavaClass(instance, jk, this); + } else { + // for primitive Classes, the reflected type is null + return null; + } + } else { + // not a well-known class. But the base class may be + // one of the known classes. + Klass kls = instance.getKlass().getSuper(); + while (kls != null) { + className = kls.getName(); + // java.lang.Class and java.lang.String are final classes + if (className.equals(javaLangThread())) { + res = new JSJavaThread(instance, this); + break; + } + kls = kls.getSuper(); + } + } + if (res == null) { + res = new JSJavaInstance(instance, this); + } + return res; + } + + // Map> + private Map om = new HashMap(); + private Symbol javaLangString; + private Symbol javaLangThread; + private Symbol javaLangClass; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaField.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaField.java new file mode 100644 index 00000000000..b5becf5b3cc --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaField.java @@ -0,0 +1,161 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +public class JSJavaField extends DefaultScriptObject { + private static final int FIELD_NAME = 0; + private static final int FIELD_SIGNATURE = 1; + private static final int FIELD_HOLDER = 2; + private static final int FIELD_IS_PRIVATE = 3; + private static final int FIELD_IS_PUBLIC = 4; + private static final int FIELD_IS_PROTECTED = 5; + private static final int FIELD_IS_PACKAGE_PRIVATE = 6; + private static final int FIELD_IS_STATIC = 7; + private static final int FIELD_IS_FINAL = 8; + private static final int FIELD_IS_VOLATILE = 9; + private static final int FIELD_IS_TRANSIENT = 10; + private static final int FIELD_IS_SYNTHETIC = 11; + private static final int FIELD_UNDEFINED = -1; + + public JSJavaField(Field f, JSJavaFactory fac) { + this.field = f; + this.factory = fac; + } + + public Object get(String name) { + int fieldID = getFieldID(name); + switch (fieldID) { + case FIELD_NAME: + return field.getID().getName(); + case FIELD_SIGNATURE: + return field.getSignature().asString(); + case FIELD_HOLDER: + return getFieldHolder(); + case FIELD_IS_PRIVATE: + return Boolean.valueOf(field.isPrivate()); + case FIELD_IS_PUBLIC: + return Boolean.valueOf(field.isPublic()); + case FIELD_IS_PROTECTED: + return Boolean.valueOf(field.isProtected()); + case FIELD_IS_PACKAGE_PRIVATE: + return Boolean.valueOf(field.isPackagePrivate()); + case FIELD_IS_STATIC: + return Boolean.valueOf(field.isStatic()); + case FIELD_IS_FINAL: + return Boolean.valueOf(field.isFinal()); + case FIELD_IS_VOLATILE: + return Boolean.valueOf(field.isVolatile()); + case FIELD_IS_TRANSIENT: + return Boolean.valueOf(field.isTransient()); + case FIELD_IS_SYNTHETIC: + return Boolean.valueOf(field.isSynthetic()); + case FIELD_UNDEFINED: + default: + return super.get(name); + } + } + + public Object[] getIds() { + Object[] fieldNames = fields.keySet().toArray(); + Object[] superFields = super.getIds(); + Object[] res = new Object[fieldNames.length + superFields.length]; + System.arraycopy(fieldNames, 0, res, 0, fieldNames.length); + System.arraycopy(superFields, 0, res, fieldNames.length, superFields.length); + return res; + } + + public boolean has(String name) { + if (getFieldID(name) != FIELD_UNDEFINED) { + return true; + } else { + return super.has(name); + } + } + + public void put(String name, Object value) { + if (getFieldID(name) == FIELD_UNDEFINED) { + super.put(name, value); + } + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof JSJavaField)) { + return false; + } + + JSJavaField other = (JSJavaField) o; + return field.equals(other.field); + } + + public int hashCode() { + return field.hashCode(); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("Field "); + buf.append(field.getFieldHolder().getName().asString().replace('/', '.')); + buf.append('.'); + buf.append(field.getID().getName()); + return buf.toString(); + } + + //-- Internals only below this point + private JSJavaObject getFieldHolder() { + return factory.newJSJavaKlass(field.getFieldHolder()).getJSJavaClass(); + } + + private static Map fields = new HashMap(); + private static void addField(String name, int fieldId) { + fields.put(name, new Integer(fieldId)); + } + + private static int getFieldID(String name) { + Integer res = (Integer) fields.get(name); + return (res != null)? res.intValue() : FIELD_UNDEFINED; + } + + static { + addField("name", FIELD_NAME); + addField("signature", FIELD_SIGNATURE); + addField("holder", FIELD_HOLDER); + addField("isPrivate", FIELD_IS_PRIVATE); + addField("isPublic", FIELD_IS_PUBLIC); + addField("isProtected", FIELD_IS_PROTECTED); + addField("isPackagePrivate", FIELD_IS_PACKAGE_PRIVATE); + addField("isStatic", FIELD_IS_STATIC); + addField("isFinal", FIELD_IS_FINAL); + addField("isVolatile", FIELD_IS_VOLATILE); + addField("isTransient", FIELD_IS_TRANSIENT); + addField("isSynthetic", FIELD_IS_SYNTHETIC); + } + + private final Field field; + private final JSJavaFactory factory; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaFrame.java new file mode 100644 index 00000000000..7d0fb603ba4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaFrame.java @@ -0,0 +1,224 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +public class JSJavaFrame extends DefaultScriptObject { + private static final int FIELD_METHOD = 0; + private static final int FIELD_BCI = 1; + private static final int FIELD_LINE_NUMBER = 2; + private static final int FIELD_LOCALS = 3; + private static final int FIELD_THIS_OBJECT = 4; + private static final int FIELD_THREAD = 5; + private static final int FIELD_UNDEFINED = -1; + + public JSJavaFrame(JavaVFrame jvf, JSJavaFactory fac) { + this.jvf = jvf; + this.factory = fac; + } + + public Object get(String name) { + int fieldID = getFieldID(name); + switch (fieldID) { + case FIELD_METHOD: + return getMethod(); + case FIELD_BCI: + return new Integer(getBCI()); + case FIELD_LINE_NUMBER: + return new Integer(getLineNumber()); + case FIELD_LOCALS: + return getLocals(); + case FIELD_THIS_OBJECT: + return getThisObject(); + case FIELD_THREAD: + return getThread(); + case FIELD_UNDEFINED: + default: + return super.get(name); + } + } + + public Object[] getIds() { + Object[] fieldNames = fields.keySet().toArray(); + Object[] superFields = super.getIds(); + Object[] res = new Object[fieldNames.length + superFields.length]; + System.arraycopy(fieldNames, 0, res, 0, fieldNames.length); + System.arraycopy(superFields, 0, res, fieldNames.length, superFields.length); + return res; + } + + public boolean has(String name) { + if (getFieldID(name) != FIELD_UNDEFINED) { + return true; + } else { + return super.has(name); + } + } + + public void put(String name, Object value) { + if (getFieldID(name) == FIELD_UNDEFINED) { + super.put(name, value); + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("Frame (method="); + buf.append(jvf.getMethod().externalNameAndSignature()); + buf.append(", bci="); + buf.append(getBCI()); + buf.append(", line="); + buf.append(getLineNumber()); + buf.append(')'); + return buf.toString(); + } + + //-- Internals only below this point + private static Map fields = new HashMap(); + private static void addField(String name, int fieldId) { + fields.put(name, new Integer(fieldId)); + } + + private static int getFieldID(String name) { + Integer res = (Integer) fields.get(name); + return (res != null)? res.intValue() : FIELD_UNDEFINED; + } + + static { + addField("method", FIELD_METHOD); + addField("bci", FIELD_BCI); + addField("line", FIELD_LINE_NUMBER); + addField("locals", FIELD_LOCALS); + addField("thisObject", FIELD_THIS_OBJECT); + addField("thread", FIELD_THREAD); + } + + private JSJavaObject getMethod() { + return factory.newJSJavaObject(jvf.getMethod()); + } + + private int getBCI() { + return jvf.getBCI(); + } + + private int getLineNumber() { + int bci = jvf.getBCI(); + if (bci == -1) { + return 0; + } else { + int lineNum = jvf.getMethod().getLineNumberFromBCI(bci); + return (lineNum <= 0)? 0 : lineNum; + } + } + + private synchronized JSMap getLocals() { + if (localsCache == null) { + Map map = new HashMap(); + localsCache = factory.newJSMap(map); + StackValueCollection values = jvf.getLocals(); + Method method = jvf.getMethod(); + if (method.isNative() || ! method.hasLocalVariableTable() || + values == null) { + return localsCache; + } + + LocalVariableTableElement[] localVars = method.getLocalVariableTable(); + int bci = getBCI(); + List visibleVars = new ArrayList(0); + for (int i = 0; i < localVars.length; i++) { + LocalVariableTableElement cur = localVars[i]; + if (cur.getStartBCI() >= bci && cur.getLength() > 0) { + visibleVars.add(cur); + } + } + + OopHandle handle = null; + ObjectHeap heap = VM.getVM().getObjectHeap(); + for (Iterator varItr = visibleVars.iterator(); varItr.hasNext();) { + LocalVariableTableElement cur = (LocalVariableTableElement) varItr.next(); + String name = method.getConstants().getSymbolAt(cur.getNameCPIndex()).asString(); + int slot = cur.getSlot(); + + String signature = method.getConstants().getSymbolAt(cur.getDescriptorCPIndex()).asString(); + BasicType variableType = BasicType.charToBasicType(signature.charAt(0)); + Object value = null; + if (variableType == BasicType.T_BOOLEAN) { + value = Boolean.valueOf(values.booleanAt(slot)); + } else if (variableType == BasicType.T_CHAR) { + value = new Character(values.charAt(slot)); + } else if (variableType == BasicType.T_FLOAT) { + value = new Float(values.floatAt(slot)); + } else if (variableType == BasicType.T_DOUBLE) { + value = new Double(values.doubleAt(slot)); + } else if (variableType == BasicType.T_BYTE) { + value = new Byte(values.byteAt(slot)); + } else if (variableType == BasicType.T_SHORT) { + value = new Short(values.shortAt(slot)); + } else if (variableType == BasicType.T_INT) { + value = new Integer(values.intAt(slot)); + } else if (variableType == BasicType.T_LONG) { + value = new Long(values.longAt(slot)); + } else if (variableType == BasicType.T_OBJECT || + variableType == BasicType.T_ARRAY) { + handle = values.oopHandleAt(slot); + value = factory.newJSJavaObject(heap.newOop(handle)); + } else { + // ignore + } + map.put(name, value); + } + } + return localsCache; + } + + private JSJavaObject getThisObject() { + Method method = jvf.getMethod(); + if (method.isStatic()) { + return null; + } + StackValueCollection values = jvf.getLocals(); + if (values != null) { + // 'this' at index 0. + OopHandle handle = values.oopHandleAt(0); + ObjectHeap heap = VM.getVM().getObjectHeap(); + return factory.newJSJavaObject(heap.newOop(handle)); + } else { + // can't get locals, return null. + return null; + } + } + + private JSJavaThread getThread() { + return factory.newJSJavaThread(jvf.getThread()); + } + + private final JavaVFrame jvf; + private final JSJavaFactory factory; + private JSMap localsCache; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaHeap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaHeap.java new file mode 100644 index 00000000000..62af0853473 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaHeap.java @@ -0,0 +1,263 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import javax.script.ScriptException; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; +import java.lang.reflect.Method; + +public class JSJavaHeap extends DefaultScriptObject { + private static final int FIELD_CAPACITY = 0; + private static final int FIELD_USED = 1; + private static final int FIELD_FOR_EACH_OBJECT = 2; + private static final int FIELD_FOR_EACH_CLASS = 3; + + private static final int FIELD_UNDEFINED = -1; + + public JSJavaHeap(JSJavaFactory fac) { + this.factory = fac; + } + + public Object get(String name) { + int fieldID = getFieldID(name); + switch (fieldID) { + case FIELD_CAPACITY: + return new Long(getCapacity()); + case FIELD_USED: + return new Long(getUsed()); + case FIELD_FOR_EACH_OBJECT: + return new MethodCallable(this, forEachObjectMethod); + case FIELD_FOR_EACH_CLASS: + return new MethodCallable(this, forEachClassMethod); + case FIELD_UNDEFINED: + default: + return super.get(name); + } + } + + public Object[] getIds() { + Object[] superIds = super.getIds(); + Object[] tmp = fields.keySet().toArray(); + Object[] res = new Object[superIds.length + tmp.length]; + System.arraycopy(tmp, 0, res, 0, tmp.length); + System.arraycopy(superIds, 0, res, tmp.length, superIds.length); + return res; + } + + public boolean has(String name) { + if (getFieldID(name) != FIELD_UNDEFINED) { + return true; + } else { + return super.has(name); + } + } + + public void put(String name, Object value) { + if (getFieldID(name) == FIELD_UNDEFINED) { + super.put(name, value); + } + } + + public void forEachObject(Object[] args) { + boolean subtypes = true; + Klass kls = null; + Callable func = null; + switch (args.length) { + case 3: { + Object b = args[2]; + if (b != null && b instanceof Boolean) { + subtypes = ((Boolean)b).booleanValue(); + } + } + case 2: { + Object k = args[1]; + if (k == null) return; + if (k instanceof JSJavaKlass) { + kls = ((JSJavaKlass)k).getKlass(); + } else if (k instanceof String) { + kls = SystemDictionaryHelper.findInstanceKlass((String)k); + if (kls == null) return; + } + } + case 1: { + Object f = args[0]; + if (f != null && f instanceof Callable) { + func = (Callable) f; + } else { + // unknown target - just return + return ; + } + } + break; + + default: + return; + } + + final Callable finalFunc = func; + HeapVisitor visitor = new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + JSJavaObject jo = factory.newJSJavaObject(oop); + if (jo != null) { + try { + finalFunc.call(new Object[] { jo }); + } catch (ScriptException exp) { + throw new RuntimeException(exp); + } + } + return false; + } + }; + ObjectHeap heap = VM.getVM().getObjectHeap(); + if (kls == null) { + kls = SystemDictionaryHelper.findInstanceKlass("java.lang.Object"); + } + heap.iterateObjectsOfKlass(visitor, kls, subtypes); + } + + public void forEachClass(Object[] args) { + boolean withLoader = false; + Callable func = null; + switch (args.length) { + case 2: { + Object b = args[1]; + if (b instanceof Boolean) { + withLoader = ((Boolean)b).booleanValue(); + } + } + case 1: { + Object f = args[0]; + if (f instanceof Callable) { + func = (Callable) f; + } else { + return; + } + } + break; + default: + return; + } + + final Callable finalFunc = func; + SystemDictionary sysDict = VM.getVM().getSystemDictionary(); + if (withLoader) { + sysDict.classesDo(new SystemDictionary.ClassAndLoaderVisitor() { + public void visit(Klass kls, Oop loader) { + JSJavaKlass jk = factory.newJSJavaKlass(kls); + if (jk == null) { + return; + } + JSJavaObject k = jk.getJSJavaClass(); + JSJavaObject l = factory.newJSJavaObject(loader); + if (k != null) { + if (k != null) { + try { + finalFunc.call(new Object[] { k, l }); + } catch (ScriptException exp) { + throw new RuntimeException(exp); + } + } + } + } + }); + + } else { + sysDict.classesDo(new SystemDictionary.ClassVisitor() { + public void visit(Klass kls) { + JSJavaKlass jk = factory.newJSJavaKlass(kls); + if (jk == null) { + return; + } + JSJavaClass k = jk.getJSJavaClass(); + if (k != null) { + if (k != null) { + try { + finalFunc.call(new Object[] { k }); + } catch (ScriptException exp) { + throw new RuntimeException(exp); + } + } + } + } + }); + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("Java Heap (capacity="); + buf.append(getCapacity()); + buf.append(", used="); + buf.append(getUsed()); + buf.append(")"); + return buf.toString(); + } + + //-- Internals only below this point + private static Map fields = new HashMap(); + private static void addField(String name, int fieldId) { + fields.put(name, new Integer(fieldId)); + } + + private static int getFieldID(String name) { + Integer res = (Integer) fields.get(name); + return (res != null)? res.intValue() : FIELD_UNDEFINED; + } + + static { + addField("capacity", FIELD_CAPACITY); + addField("used", FIELD_USED); + addField("forEachObject", FIELD_FOR_EACH_OBJECT); + addField("forEachClass", FIELD_FOR_EACH_CLASS); + try { + Class myClass = JSJavaHeap.class; + forEachObjectMethod = myClass.getMethod("forEachObject", + new Class[] { Object[].class }); + forEachClassMethod = myClass.getMethod("forEachClass", + new Class[] {Object[].class }); + } catch (RuntimeException re) { + throw re; + } catch (Exception exp) { + throw new RuntimeException(exp); + } + } + + private long getCapacity() { + return VM.getVM().getUniverse().heap().capacity(); + } + + private long getUsed() { + return VM.getVM().getUniverse().heap().used(); + } + + private final JSJavaFactory factory; + private static Method forEachObjectMethod; + private static Method forEachClassMethod; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaInstance.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaInstance.java new file mode 100644 index 00000000000..7a06bd785f4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaInstance.java @@ -0,0 +1,93 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import sun.jvm.hotspot.oops.*; + +/** This is JavaScript wrapper for Java Instance in debuggee.*/ + +public class JSJavaInstance extends JSJavaObject { + public JSJavaInstance(Instance instance, JSJavaFactory fac) { + super(instance, fac); + this.type = (JSJavaInstanceKlass) fac.newJSJavaKlass(instance.getKlass()); + } + + public final Instance getInstance() { + return (Instance) getOop(); + } + + public final JSJavaClass getJSJavaClass() { + return type.getJSJavaClass(); + } + + public Object get(String name) { + if (hasField(name)) { + return getFieldValue(name); + } else { + return super.get(name); + } + } + + public Object[] getIds() { + String[] fieldNames = getFieldNames(); + Object[] superFields = super.getIds(); + Object[] res = new Object[fieldNames.length + superFields.length]; + System.arraycopy(fieldNames, 0, res, 0, fieldNames.length); + System.arraycopy(superFields, 0, res, fieldNames.length, superFields.length); + return res; + } + + public boolean has(String name) { + if (hasField(name)) { + return true; + } else { + return super.has(name); + } + } + + public void put(String name, Object value) { + if (! hasField(name)) { + super.put(name, value); + } + } + + protected Object getFieldValue(String name) { + try { + return type.getInstanceFieldValue(name, getInstance()); + } catch (NoSuchFieldException exp) { + return UNDEFINED; + } + } + + protected String[] getFieldNames() { + return type.getInstanceFieldNames(); + } + + protected boolean hasField(String name) { + return type.hasInstanceField(name); + } + + protected final JSJavaInstanceKlass type; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaInstanceKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaInstanceKlass.java new file mode 100644 index 00000000000..bce2a28de63 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaInstanceKlass.java @@ -0,0 +1,361 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +/** + This is JavaScript wrapper for InstanceKlass. +*/ +public class JSJavaInstanceKlass extends JSJavaKlass { + private static final int FIELD_SOURCE_FILE = 1; + private static final int FIELD_INTERFACES = 2; + private static final int FIELD_FIELDS = 3; + private static final int FIELD_METHODS = 4; + private static final int FIELD_IS_PRIVATE = 5; + private static final int FIELD_IS_PUBLIC = 6; + private static final int FIELD_IS_PROTECTED = 7; + private static final int FIELD_IS_PACKAGE_PRIVATE = 8; + private static final int FIELD_IS_STATIC = 9; + private static final int FIELD_IS_FINAL = 10; + private static final int FIELD_IS_ABSTRACT = 11; + private static final int FIELD_IS_STRICT = 12; + private static final int FIELD_IS_SYNTHETIC = 13; + private static final int FIELD_IS_INTERFACE = 14; + private static final int FIELD_CLASS_LOADER = 15; + private static final int FIELD_PROTECTION_DOMAIN = 16; + private static final int FIELD_SIGNERS = 17; + private static final int FIELD_STATICS = 18; + private static final int FIELD_UNDEFINED = -1; + + public JSJavaInstanceKlass(InstanceKlass kls, JSJavaFactory fac) { + super(kls, fac); + this.instanceFields = new HashMap(); + this.staticFields = new HashMap(); + } + + public final InstanceKlass getInstanceKlass() { + return (InstanceKlass) getKlass(); + } + + public Object getMetaClassFieldValue(String name) { + int fieldID = getFieldID(name); + InstanceKlass ik = getInstanceKlass(); + switch (fieldID) { + case FIELD_SOURCE_FILE: { + Symbol sourceFile = ik.getSourceFileName(); + return (sourceFile != null)? sourceFile.asString() : ""; + } + case FIELD_INTERFACES: + return getInterfaces(); + case FIELD_FIELDS: + return factory.newJSList(ik.getImmediateFields()); + case FIELD_METHODS: + return factory.newJSList(ik.getImmediateMethods()); + case FIELD_IS_PRIVATE: + return Boolean.valueOf(getAccessFlags().isPrivate()); + case FIELD_IS_PUBLIC: + return Boolean.valueOf(getAccessFlags().isPublic()); + case FIELD_IS_PROTECTED: + return Boolean.valueOf(getAccessFlags().isProtected()); + case FIELD_IS_PACKAGE_PRIVATE: { + AccessFlags acc = getAccessFlags(); + return Boolean.valueOf(!acc.isPrivate() && !acc.isPublic() && !acc.isProtected()); + } + case FIELD_IS_STATIC: + return Boolean.valueOf(getAccessFlags().isStatic()); + case FIELD_IS_FINAL: + return Boolean.valueOf(getAccessFlags().isFinal()); + case FIELD_IS_ABSTRACT: + return Boolean.valueOf(getAccessFlags().isAbstract()); + case FIELD_IS_STRICT: + return Boolean.valueOf(getAccessFlags().isStrict()); + case FIELD_IS_SYNTHETIC: + return Boolean.valueOf(getAccessFlags().isSynthetic()); + case FIELD_IS_INTERFACE: + return Boolean.valueOf(ik.isInterface()); + case FIELD_CLASS_LOADER: + return factory.newJSJavaObject(ik.getClassLoader()); + case FIELD_PROTECTION_DOMAIN: + return factory.newJSJavaObject(ik.getProtectionDomain()); + case FIELD_SIGNERS: + return factory.newJSJavaObject(ik.getSigners()); + case FIELD_STATICS: + return getStatics(); + case FIELD_UNDEFINED: + default: + return super.getMetaClassFieldValue(name); + } + } + + public boolean hasMetaClassField(String name) { + if (getFieldID(name) != FIELD_UNDEFINED) { + return true; + } else { + return super.hasMetaClassField(name); + } + } + + public String getName() { + return getInstanceKlass().getName().asString().replace('/', '.'); + } + + public boolean isArray() { + return false; + } + + public String[] getMetaClassFieldNames() { + String[] superFields = super.getMetaClassFieldNames(); + Set k = fields.keySet(); + String[] res = new String[k.size() + superFields.length]; + System.arraycopy(superFields, 0, res, 0, superFields.length); + int i = superFields.length; + for (Iterator itr = k.iterator(); itr.hasNext();) { + res[i] = (String) itr.next(); + i++; + } + return res; + } + + public Object getInstanceFieldValue(String name, Instance instance) throws NoSuchFieldException { + Field fld = findInstanceField(name); + if (fld != null) { + return getFieldValue(fld, name, instance); + } else { + throw new NoSuchFieldException(name + " is not field of " + + getInstanceKlass().getName().asString().replace('/', '.')); + } + } + + public Object getStaticFieldValue(String name) throws NoSuchFieldException { + Field fld = findStaticField(name); + if (fld != null) { + return getFieldValue(fld, name, getInstanceKlass()); + } else { + throw new NoSuchFieldException(name + " is not field of " + + getInstanceKlass().getName().asString().replace('/', '.')); + } + } + + public String[] getInstanceFieldNames() { + if (instanceFieldNames == null) { + InstanceKlass current = getInstanceKlass(); + while (current != null) { + List tmp = current.getImmediateFields(); + for (Iterator itr = tmp.iterator(); itr.hasNext();) { + Field fld = (Field) itr.next(); + if (!fld.isStatic()) { + String name = fld.getID().getName(); + if (instanceFields.get(name) == null) { + instanceFields.put(name, fld); + } + } + } + current = (InstanceKlass) current.getSuper(); + } + + Set s = instanceFields.keySet(); + instanceFieldNames = new String[s.size()]; + int i = 0; + for (Iterator itr = s.iterator(); itr.hasNext(); i++) { + instanceFieldNames[i] = (String) itr.next(); + } + } + return instanceFieldNames; + } + + public boolean hasInstanceField(String name) { + Field fld = findInstanceField(name); + return (fld != null)? true: false; + } + + public String[] getStaticFieldNames() { + if (staticFieldNames == null) { + InstanceKlass current = getInstanceKlass(); + List tmp = current.getImmediateFields(); + for (Iterator itr = tmp.iterator(); itr.hasNext();) { + Field fld = (Field) itr.next(); + if (fld.isStatic()) { + staticFields.put(fld.getID().getName(), fld); + } + } + + Set s = staticFields.keySet(); + staticFieldNames = new String[s.size()]; + int i = 0; + for (Iterator itr = s.iterator(); itr.hasNext(); i++) { + staticFieldNames[i] = (String) itr.next(); + } + } + return staticFieldNames; + } + + public boolean hasStaticField(String name) { + Field fld = findStaticField(name); + return (fld != null)? true: false; + } + + //-- Intenals only below this point + private static Map fields = new HashMap(); + private static void addField(String name, int fieldId) { + fields.put(name, new Integer(fieldId)); + } + + private static int getFieldID(String name) { + Integer res = (Integer) fields.get(name); + return (res != null)? res.intValue() : FIELD_UNDEFINED; + } + + static { + addField("sourceFile", FIELD_SOURCE_FILE); + addField("interfaces", FIELD_INTERFACES); + addField("fields", FIELD_FIELDS); + addField("methods", FIELD_METHODS); + addField("isPrivate", FIELD_IS_PRIVATE); + addField("isPublic", FIELD_IS_PUBLIC); + addField("isProtected", FIELD_IS_PROTECTED); + addField("isPackagePrivate", FIELD_IS_PACKAGE_PRIVATE); + addField("isStatic", FIELD_IS_STATIC); + addField("isFinal", FIELD_IS_FINAL); + addField("isAbstract", FIELD_IS_ABSTRACT); + addField("isStrict", FIELD_IS_STRICT); + addField("isSynthetic", FIELD_IS_SYNTHETIC); + addField("isInterface", FIELD_IS_INTERFACE); + addField("classLoader", FIELD_CLASS_LOADER); + addField("protectionDomain", FIELD_PROTECTION_DOMAIN); + addField("signers", FIELD_SIGNERS); + addField("statics", FIELD_STATICS); + } + + private AccessFlags getAccessFlags() { + if (accFlags == null) { + accFlags = new AccessFlags(getInstanceKlass().computeModifierFlags()); + } + return accFlags; + } + + private Object getFieldValue(Field fld, String name, Oop oop) { + FieldType fd = fld.getFieldType(); + if (fd.isObject() || fd.isArray()) { + return factory.newJSJavaObject(((OopField)fld).getValue(oop)); + } else if (fd.isByte()) { + return new Byte(((ByteField)fld).getValue(oop)); + } else if (fd.isChar()) { + return new String(new char[] { ((CharField)fld).getValue(oop) }); + } else if (fd.isDouble()) { + return new Double(((DoubleField)fld).getValue(oop)); + } else if (fd.isFloat()) { + return new Float(((FloatField)fld).getValue(oop)); + } else if (fd.isInt()) { + return new Integer(((IntField)fld).getValue(oop)); + } else if (fd.isLong()) { + return new Long(((LongField)fld).getValue(oop)); + } else if (fd.isShort()) { + return new Short(((ShortField)fld).getValue(oop)); + } else if (fd.isBoolean()) { + return Boolean.valueOf(((BooleanField)fld).getValue(oop)); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "invalid field type for " + name); + } + return null; + } + } + + private Field findInstanceField(String name) { + Field fld = (Field) instanceFields.get(name); + if (fld != null) { + return fld; + } else { + InstanceKlass current = getInstanceKlass(); + while (current != null) { + List tmp = current.getImmediateFields(); + for (Iterator itr = tmp.iterator(); itr.hasNext();) { + fld = (Field) itr.next(); + if (fld.getID().getName().equals(name) && !fld.isStatic()) { + instanceFields.put(name, fld); + return fld; + } + } + // lookup in super class. + current = (InstanceKlass) current.getSuper(); + } + } + // no match + return null; + } + + private Field findStaticField(String name) { + Field fld = (Field) staticFields.get(name); + if (fld != null) { + return fld; + } else { + // static fields are searched only in current. + // Direct/indirect super classes and interfaces + // are not included in search. + InstanceKlass current = getInstanceKlass(); + List tmp = current.getImmediateFields(); + for (Iterator itr = tmp.iterator(); itr.hasNext();) { + fld = (Field) itr.next(); + if (fld.getID().getName().equals(name) && fld.isStatic()) { + staticFields.put(name, fld); + return fld; + } + } + // no match + return null; + } + } + + private JSList getInterfaces() { + InstanceKlass ik = getInstanceKlass(); + List intfs = ik.getDirectImplementedInterfaces(); + List res = new ArrayList(0); + for (Iterator itr = intfs.iterator(); itr.hasNext();) { + Klass k = (Klass) itr.next(); + res.add(k.getJavaMirror()); + } + return factory.newJSList(res); + } + + private JSMap getStatics() { + String[] names = getStaticFieldNames(); + Map map = new HashMap(); + for (int i=0; i < names.length; i++) { + try { + map.put(names[i], getStaticFieldValue(names[i])); + } catch (NoSuchFieldException exp) {} + } + return factory.newJSMap(map); + } + + private Map instanceFields; + private Map staticFields; + private String[] instanceFieldNames; + private String[] staticFieldNames; + private AccessFlags accFlags; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaKlass.java new file mode 100644 index 00000000000..daeff4ecd8a --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaKlass.java @@ -0,0 +1,104 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +/** + This is JavaScript wrapper for Klass. +*/ +public abstract class JSJavaKlass { + private static final int FIELD_SUPER_CLASS = 0; + private static final int FIELD_NAME = 1; + private static final int FIELD_IS_ARRAY_CLASS = 2; + private static final int FIELD_UNDEFINED = -1; + + public JSJavaKlass(Klass klass, JSJavaFactory factory) { + this.factory = factory; + this.klass = klass; + } + + public final Klass getKlass() { + return klass; + } + + public JSJavaClass getJSJavaClass() { + return (JSJavaClass) factory.newJSJavaObject(getKlass().getJavaMirror()); + } + + public Object getMetaClassFieldValue(String name) { + int fieldID = getFieldID(name); + switch (fieldID) { + case FIELD_SUPER_CLASS: { + JSJavaKlass jk = factory.newJSJavaKlass(getKlass().getSuper()); + return (jk != null) ? jk.getJSJavaClass() : null; + } + case FIELD_NAME: + return getName(); + case FIELD_IS_ARRAY_CLASS: + return Boolean.valueOf(isArray()); + case FIELD_UNDEFINED: + default: + return ScriptObject.UNDEFINED; + } + } + + public boolean hasMetaClassField(String name) { + return getFieldID(name) != FIELD_UNDEFINED; + } + + + public String[] getMetaClassFieldNames() { + String[] res = { "name", "superClass", "isArrayClass" }; + return res; + } + + public abstract String getName(); + public abstract boolean isArray(); + + //-- Internals only below this point + private static Map fields = new HashMap(); + private static void addField(String name, int fieldId) { + fields.put(name, new Integer(fieldId)); + } + + private static int getFieldID(String name) { + Integer res = (Integer) fields.get(name); + return (res != null)? res.intValue() : FIELD_UNDEFINED; + } + + static { + addField("base", FIELD_SUPER_CLASS); + addField("baseClass", FIELD_SUPER_CLASS); + addField("superClass", FIELD_SUPER_CLASS); + addField("name", FIELD_NAME); + addField("isArrayClass", FIELD_IS_ARRAY_CLASS); + } + + protected final JSJavaFactory factory; + private final Klass klass; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaMethod.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaMethod.java new file mode 100644 index 00000000000..4cbdcf37e31 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaMethod.java @@ -0,0 +1,165 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/** + * Wraps a methodOop from the debuggee VM. + */ +public class JSJavaMethod extends JSJavaObject { + private static final int FIELD_NAME = 0; + private static final int FIELD_SIGNATURE = 1; + private static final int FIELD_HOLDER = 2; + private static final int FIELD_IS_PRIVATE = 3; + private static final int FIELD_IS_PUBLIC = 4; + private static final int FIELD_IS_PROTECTED = 5; + private static final int FIELD_IS_PACKAGE_PRIVATE = 6; + private static final int FIELD_IS_STATIC = 7; + private static final int FIELD_IS_FINAL = 8; + private static final int FIELD_IS_SYNCHRONIZED = 9; + private static final int FIELD_IS_NATIVE = 10; + private static final int FIELD_IS_ABSTRACT = 11; + private static final int FIELD_IS_STRICT = 12; + private static final int FIELD_IS_SYNTHETIC = 13; + private static final int FIELD_IS_OBSOLETE = 14; + private static final int FIELD_UNDEFINED = -1; + + public JSJavaMethod(Method m, JSJavaFactory fac) { + super(m, fac); + } + + public final Method getMethod() { + return (Method) getOop(); + } + + public Object get(String name) { + int fieldID = getFieldID(name); + Method method = getMethod(); + switch (fieldID) { + case FIELD_NAME: + return method.getName().asString(); + case FIELD_SIGNATURE: + return method.getSignature().asString(); + case FIELD_HOLDER: + return getMethodHolder(); + case FIELD_IS_PRIVATE: + return Boolean.valueOf(method.isPrivate()); + case FIELD_IS_PUBLIC: + return Boolean.valueOf(method.isPublic()); + case FIELD_IS_PROTECTED: + return Boolean.valueOf(method.isProtected()); + case FIELD_IS_PACKAGE_PRIVATE: + return Boolean.valueOf(method.isPackagePrivate()); + case FIELD_IS_STATIC: + return Boolean.valueOf(method.isStatic()); + case FIELD_IS_FINAL: + return Boolean.valueOf(method.isFinal()); + case FIELD_IS_SYNCHRONIZED: + return Boolean.valueOf(method.isSynchronized()); + case FIELD_IS_NATIVE: + return Boolean.valueOf(method.isNative()); + case FIELD_IS_ABSTRACT: + return Boolean.valueOf(method.isAbstract()); + case FIELD_IS_STRICT: + return Boolean.valueOf(method.isStrict()); + case FIELD_IS_SYNTHETIC: + return Boolean.valueOf(method.isSynthetic()); + case FIELD_IS_OBSOLETE: + return Boolean.valueOf(method.isObsolete()); + case FIELD_UNDEFINED: + default: + return super.get(name); + } + } + + public Object[] getIds() { + Object[] fieldNames = fields.keySet().toArray(); + Object[] superFields = super.getIds(); + Object[] res = new Object[fieldNames.length + superFields.length]; + System.arraycopy(fieldNames, 0, res, 0, fieldNames.length); + System.arraycopy(superFields, 0, res, fieldNames.length, superFields.length); + return res; + } + + public boolean has(String name) { + if (getFieldID(name) != FIELD_UNDEFINED) { + return true; + } else { + return super.has(name); + } + } + + public void put(String name, Object value) { + if (getFieldID(name) != FIELD_UNDEFINED) { + return; + } else { + super.put(name, value); + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("Method "); + buf.append(getMethod().externalNameAndSignature()); + return buf.toString(); + } + + //-- Internals only below this point + private JSJavaObject getMethodHolder() { + Klass k = getMethod().getMethodHolder(); + return factory.newJSJavaKlass(k).getJSJavaClass(); + } + + private static Map fields = new HashMap(); + private static void addField(String name, int fieldId) { + fields.put(name, new Integer(fieldId)); + } + + private static int getFieldID(String name) { + Integer res = (Integer) fields.get(name); + return (res != null)? res.intValue() : FIELD_UNDEFINED; + } + + static { + addField("name", FIELD_NAME); + addField("signature", FIELD_SIGNATURE); + addField("holder", FIELD_HOLDER); + addField("isPrivate", FIELD_IS_PRIVATE); + addField("isPublic", FIELD_IS_PUBLIC); + addField("isProtected", FIELD_IS_PROTECTED); + addField("isPackagePrivate", FIELD_IS_PACKAGE_PRIVATE); + addField("isStatic", FIELD_IS_STATIC); + addField("isFinal", FIELD_IS_FINAL); + addField("isSynchronized", FIELD_IS_SYNCHRONIZED); + addField("isNative", FIELD_IS_NATIVE); + addField("isAbstract", FIELD_IS_ABSTRACT); + addField("isStrict", FIELD_IS_STRICT); + addField("isSynthetic", FIELD_IS_SYNTHETIC); + addField("isObsolete", FIELD_IS_OBSOLETE); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaObjArray.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaObjArray.java new file mode 100644 index 00000000000..85b193e17f4 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaObjArray.java @@ -0,0 +1,41 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import sun.jvm.hotspot.oops.ObjArray; + +/** + This is JavaScript wrapper for Java Object Array. +*/ + +public class JSJavaObjArray extends JSJavaArray { + public JSJavaObjArray(ObjArray array, JSJavaFactory fac) { + super(array, fac); + } + + public final ObjArray getObjArray() { + return (ObjArray) getArray(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaObjArrayKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaObjArrayKlass.java new file mode 100644 index 00000000000..4ae4ef06699 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaObjArrayKlass.java @@ -0,0 +1,61 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; + +/** + This is JavaScript wrapper for ObjArrayKlass. +*/ + +public class JSJavaObjArrayKlass extends JSJavaArrayKlass { + public JSJavaObjArrayKlass(ObjArrayKlass kls, JSJavaFactory fac) { + super(kls, fac); + } + + public ObjArrayKlass getObjArrayKlass() { + return (ObjArrayKlass) getArrayKlass(); + } + + public String getName() { + Klass botKls = getObjArrayKlass().getBottomKlass(); + int dimension = (int) getObjArrayKlass().getDimension(); + StringBuffer buf = new StringBuffer(); + if (botKls instanceof TypeArrayKlass) { + dimension--; + } + buf.append(factory.newJSJavaKlass(botKls).getName()); + for (int i = 0; i < dimension; i++) { + buf.append("[]"); + } + return buf.toString(); + } + + public Object getFieldValue(int index, Array array) { + Oop obj = ((ObjArray)array).getObjAt(index); + return factory.newJSJavaObject(obj); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaObject.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaObject.java new file mode 100644 index 00000000000..b8292d052da --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaObject.java @@ -0,0 +1,60 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import sun.jvm.hotspot.oops.Oop; + +/** This is JavaScript wrapper for a Java Object in debuggee.*/ + +public abstract class JSJavaObject extends DefaultScriptObject { + public JSJavaObject(Oop oop, JSJavaFactory factory) { + this.oop = oop; + this.factory = factory; + } + + public final Oop getOop() { + return oop; + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof JSJavaObject)) { + return false; + } + + JSJavaObject other = (JSJavaObject) o; + return oop.equals(other.oop); + } + + public int hashCode() { + return oop.hashCode(); + } + + public String toString() { + return "Object " + oop.getHandle().toString(); + } + + private final Oop oop; + protected final JSJavaFactory factory; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaScriptEngine.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaScriptEngine.java new file mode 100644 index 00000000000..c2a39551c33 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaScriptEngine.java @@ -0,0 +1,648 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.io.*; +import java.util.*; +import javax.script.Invocable; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.tools.*; +import sun.jvm.hotspot.tools.jcore.*; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Simple wrapper around jsr-223 JavaScript script engine. + * In addition to wrapping useful functionality of jsr-223 engine, + * this class exposed certain "global" functions to the script. + */ +public abstract class JSJavaScriptEngine extends MapScriptObject { + /** + * Start a read-eval-print loop with this engine. + */ + public void startConsole() { + start(true); + } + + /** + * Initialize the engine so that we can "eval" strings + * and files later. + */ + public void start() { + start(false); + } + + /** + * Define a global function that invokes given Method. + */ + public void defineFunction(Object target, Method method) { + putFunction(target, method, false); + } + + /** + * Call the script function of given name passing the + * given arguments. + */ + public Object call(String name, Object[] args) { + Invocable invocable = (Invocable)engine; + try { + return invocable.invokeFunction(name, args); + } catch (RuntimeException re) { + throw re; + } catch (Exception exp) { + throw new RuntimeException(exp); + } + } + + /** + address function returns address of JSJavaObject as String. For other + type of objects, the result is undefined. + */ + public Object address(Object[] args) { + if (args.length != 1) return UNDEFINED; + Object o = args[0]; + if (o != null && o instanceof JSJavaObject) { + return ((JSJavaObject)o).getOop().getHandle().toString(); + } else { + return UNDEFINED; + } + } + + + /** + classof function gets type of given JSJavaInstance or JSJavaArray. Or + given a string class name, this function gets the class object. For + other type of objects, the result is undefined. + */ + public Object classof(Object[] args) { + if (args.length != 1) { + return UNDEFINED; + } + Object o = args[0]; + if (o != null) { + if (o instanceof JSJavaObject) { + if (o instanceof JSJavaInstance) { + return ((JSJavaInstance)o).getJSJavaClass(); + } else if (o instanceof JSJavaArray) { + return ((JSJavaArray)o).getJSJavaClass(); + } else { + return UNDEFINED; + } + } else if (o instanceof String) { + InstanceKlass ik = SystemDictionaryHelper.findInstanceKlass((String) o); + return getJSJavaFactory().newJSJavaKlass(ik).getJSJavaClass(); + } else { + return UNDEFINED; + } + } else { + return UNDEFINED; + } + } + + /** + * dumpClass function creates a .class file for a given Class object. + * On success, returns true. Else, returns false. Second optional argument + * specifies the directory in which .class content is dumped. This defaults + * to '.' + */ + public Object dumpClass(Object[] args) { + if (args.length == 0) { + return Boolean.FALSE; + } + Object clazz = args[0]; + if (clazz == null) { + return Boolean.FALSE; + } + InstanceKlass ik = null; + if (clazz instanceof String) { + String name = (String) clazz; + if (name.startsWith("0x")) { + // treat it as address + VM vm = VM.getVM(); + Address addr = vm.getDebugger().parseAddress(name); + Oop oop = vm.getObjectHeap().newOop(addr.addOffsetToAsOopHandle(0)); + if (oop instanceof InstanceKlass) { + ik = (InstanceKlass) oop; + } else { + return Boolean.FALSE; + } + } else { + ik = SystemDictionaryHelper.findInstanceKlass((String) clazz); + } + } else if (clazz instanceof JSJavaClass) { + JSJavaKlass jk = ((JSJavaClass)clazz).getJSJavaKlass(); + if (jk != null && jk instanceof JSJavaInstanceKlass) { + ik = ((JSJavaInstanceKlass)jk).getInstanceKlass(); + } + } else { + return Boolean.FALSE; + } + + if (ik == null) return Boolean.FALSE; + StringBuffer buf = new StringBuffer(); + if (args.length > 1) { + buf.append(args[1].toString()); + } else { + buf.append('.'); + } + + buf.append(File.separatorChar); + buf.append(ik.getName().asString().replace('/', File.separatorChar)); + buf.append(".class"); + String fileName = buf.toString(); + File file = new File(fileName); + + try { + int index = fileName.lastIndexOf(File.separatorChar); + File dir = new File(fileName.substring(0, index)); + dir.mkdirs(); + FileOutputStream fos = new FileOutputStream(file); + ClassWriter cw = new ClassWriter(ik, fos); + cw.write(); + fos.close(); + } catch (IOException exp) { + printError(exp.toString(), exp); + return Boolean.FALSE; + } + + return Boolean.TRUE; + } + + /** + * dumpHeap function creates a heap dump file. + * On success, returns true. Else, returns false. + */ + public Object dumpHeap(Object[] args) { + String fileName = "heap.bin"; + if (args.length > 0) { + fileName = args[0].toString(); + } + return new JMap().writeHeapHprofBin(fileName)? Boolean.TRUE: Boolean.FALSE; + } + + /** + help function prints help message for global functions and variables. + */ + public void help(Object[] args) { + println("Function/Variable Description"); + println("================= ==========="); + println("address(jobject) returns the address of the Java object"); + println("classof(jobject) returns the class object of the Java object"); + println("dumpClass(jclass,[dir]) writes .class for the given Java Class"); + println("dumpHeap([file]) writes heap in hprof binary format"); + println("help() prints this help message"); + println("identityHash(jobject) returns the hashCode of the Java object"); + println("mirror(jobject) returns a local mirror of the Java object"); + println("load([file1, file2,...]) loads JavaScript file(s). With no files, reads "); + println("object(string) converts a string address into Java object"); + println("owner(jobject) returns the owner thread of this monitor or null"); + println("sizeof(jobject) returns the size of Java object in bytes"); + println("staticof(jclass, field) returns a static field of the given Java class"); + println("read([prompt]) reads a single line from standard input"); + println("quit() quits the interactive load call"); + println("jvm the target jvm that is being debugged"); + } + + /** + identityHash function gets identity hash code value of given + JSJavaObject. For other type of objects, the result is undefined. + */ + public Object identityHash(Object[] args) { + if (args.length != 1) return UNDEFINED; + Object o = args[0]; + if (o != null && o instanceof JSJavaObject) { + return new Long(((JSJavaObject)o).getOop().identityHash()); + } else { + return UNDEFINED; + } + } + + + /** + * Load and execute a set of JavaScript source files. + * This method is defined as a JavaScript function. + */ + public void load(Object[] args) { + for (int i = 0; i < args.length; i++) { + processSource(args[i].toString()); + } + } + + /** + mirror function creats local copy of the Oop wrapper supplied. + if mirror can not be created, return undefined. For other types, + mirror is undefined. + */ + public Object mirror(Object[] args) { + Object o = args[0]; + if (o != null && o instanceof JSJavaObject) { + Oop oop = ((JSJavaObject)o).getOop(); + Object res = null; + try { + if (oop instanceof InstanceKlass) { + res = getObjectReader().readClass((InstanceKlass) oop); + } else { + res = getObjectReader().readObject(oop); + } + } catch (Exception e) { + if (debug) e.printStackTrace(getErrorStream()); + } + return (res != null)? res : UNDEFINED; + } else { + return UNDEFINED; + } + } + + /** + owner function gets owning thread of given JSJavaObjec, if any, else + returns null. For other type of objects, the result is undefined. + */ + public Object owner(Object[] args) { + Object o = args[0]; + if (o != null && o instanceof JSJavaObject) { + return getOwningThread((JSJavaObject)o); + } else { + return UNDEFINED; + } + } + + /** + object function takes a string address and returns a JSJavaObject. + For other type of objects, the result is undefined. + */ + public Object object(Object[] args) { + Object o = args[0]; + if (o != null && o instanceof String) { + VM vm = VM.getVM(); + Address addr = vm.getDebugger().parseAddress((String)o); + Oop oop = vm.getObjectHeap().newOop(addr.addOffsetToAsOopHandle(0)); + return getJSJavaFactory().newJSJavaObject(oop); + } else { + return UNDEFINED; + } + } + + /** + sizeof function returns size of a Java object in bytes. For other type + of objects, the result is undefined. + */ + public Object sizeof(Object[] args) { + if (args.length != 1) return UNDEFINED; + Object o = args[0]; + if (o != null && o instanceof JSJavaObject) { + return new Long(((JSJavaObject)o).getOop().getObjectSize()); + } else { + return UNDEFINED; + } + } + + /** + staticof function gets static field of given class. Both class and + field name are specified as strings. undefined is returned if there is + no such named field. + */ + public Object staticof(Object[] args) { + Object classname = args[0]; + Object fieldname = args[1]; + if (fieldname == null || classname == null || + !(fieldname instanceof String)) { + return UNDEFINED; + } + + InstanceKlass ik = null; + if (classname instanceof JSJavaClass) { + JSJavaClass jclass = (JSJavaClass) classname; + JSJavaKlass jk = jclass.getJSJavaKlass(); + if (jk != null && jk instanceof JSJavaInstanceKlass) { + ik = ((JSJavaInstanceKlass)jk).getInstanceKlass(); + } + } else if (classname instanceof String) { + ik = SystemDictionaryHelper.findInstanceKlass((String)classname); + } else { + return UNDEFINED; + } + + if (ik == null) { + return UNDEFINED; + } + JSJavaFactory factory = getJSJavaFactory(); + try { + return ((JSJavaInstanceKlass) factory.newJSJavaKlass(ik)).getStaticFieldValue((String)fieldname); + } catch (NoSuchFieldException e) { + return UNDEFINED; + } + } + + /** + * read function reads a single line of input from standard input + */ + public Object read(Object[] args) { + BufferedReader in = getInputReader(); + if (in == null) { + return null; + } + if (args.length > 0) { + print(args[0].toString()); + print(":"); + } + try { + return in.readLine(); + } catch (IOException exp) { + exp.printStackTrace(); + throw new RuntimeException(exp); + } + } + + /** + * Quit the shell. + * This only affects the interactive mode. + */ + public void quit(Object[] args) { + quit(); + } + + public void writeln(Object[] args) { + for (int i = 0; i < args.length; i++) { + print(args[i].toString()); + print(" "); + } + println(""); + } + + public void write(Object[] args) { + for (int i = 0; i < args.length; i++) { + print(args[i].toString()); + print(" "); + } + } + + //-- Internals only below this point + protected void start(boolean console) { + ScriptContext context = engine.getContext(); + OutputStream out = getOutputStream(); + if (out != null) { + context.setWriter(new PrintWriter(out)); + } + OutputStream err = getErrorStream(); + if (err != null) { + context.setErrorWriter(new PrintWriter(err)); + } + // load "sa.js" initialization file + loadInitFile(); + // load "~/jsdb.js" (if found) to perform user specific + // initialization steps, if any. + loadUserInitFile(); + + JSJavaFactory fac = getJSJavaFactory(); + JSJavaVM jvm = (fac != null)? fac.newJSJavaVM() : null; + // call "main" function from "sa.js" -- main expects + // 'this' object and jvm object + call("main", new Object[] { this, jvm }); + + // if asked, start read-eval-print console + if (console) { + processSource(null); + } + } + + protected JSJavaScriptEngine(boolean debug) { + this.debug = debug; + ScriptEngineManager manager = new ScriptEngineManager(); + engine = manager.getEngineByName("javascript"); + if (engine == null) { + throw new RuntimeException("can't load JavaScript engine"); + } + Method[] methods = getClass().getMethods(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (! Modifier.isPublic(m.getModifiers())) { + continue; + } + Class[] argTypes = m.getParameterTypes(); + if (argTypes.length == 1 && + argTypes[0] == Object[].class) { + putFunction(this, m); + } + } + } + + protected JSJavaScriptEngine() { + this(false); + } + + protected abstract ObjectReader getObjectReader(); + protected abstract JSJavaFactory getJSJavaFactory(); + protected void printPrompt(String str) { + System.err.print(str); + System.err.flush(); + } + + protected void loadInitFile() { + InputStream is = JSJavaScriptEngine.class.getResourceAsStream("sa.js"); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + evalReader(reader, "sa.js"); + } + + protected void loadUserInitFile() { + File initFile = new File(getUserInitFileDir(), getUserInitFileName()); + if (initFile.exists() && initFile.isFile()) { + // load the init script + processSource(initFile.getAbsolutePath()); + } + } + + protected String getUserInitFileDir() { + return System.getProperty("user.home"); + } + + protected String getUserInitFileName() { + return "jsdb.js"; + } + + protected BufferedReader getInputReader() { + if (inReader == null) { + inReader = new BufferedReader(new InputStreamReader(System.in)); + } + return inReader; + } + + protected PrintStream getOutputStream() { + return System.out; + } + + protected PrintStream getErrorStream() { + return System.err; + } + + protected void print(String name) { + getOutputStream().print(name); + } + + protected void println(String name) { + getOutputStream().println(name); + } + + protected void printError(String message) { + printError(message, null); + } + + protected void printError(String message, Exception exp) { + getErrorStream().println(message); + if (exp != null && debug) { + exp.printStackTrace(getErrorStream()); + } + } + + protected boolean isQuitting() { + return quitting; + } + + protected void quit() { + quitting = true; + } + + protected ScriptEngine getScriptEngine() { + return engine; + } + + private JSJavaThread getOwningThread(JSJavaObject jo) { + Oop oop = jo.getOop(); + Mark mark = oop.getMark(); + ObjectMonitor mon = null; + Address owner = null; + JSJavaThread owningThread = null; + // check for heavyweight monitor + if (! mark.hasMonitor()) { + // check for lightweight monitor + if (mark.hasLocker()) { + owner = mark.locker().getAddress(); // save the address of the Lock word + } + // implied else: no owner + } else { + // this object has a heavyweight monitor + mon = mark.monitor(); + + // The owner field of a heavyweight monitor may be NULL for no + // owner, a JavaThread * or it may still be the address of the + // Lock word in a JavaThread's stack. A monitor can be inflated + // by a non-owning JavaThread, but only the owning JavaThread + // can change the owner field from the Lock word to the + // JavaThread * and it may not have done that yet. + owner = mon.owner(); + } + + // find the owning thread + if (owner != null) { + JSJavaFactory factory = getJSJavaFactory(); + owningThread = (JSJavaThread) factory.newJSJavaThread(VM.getVM().getThreads().owningThreadFromMonitor(owner)); + } + return owningThread; + } + + /** + * Evaluate JavaScript source. + * @param filename the name of the file to compile, or null + * for interactive mode. + */ + private void processSource(String filename) { + if (filename == null) { + BufferedReader in = getInputReader(); + String sourceName = ""; + int lineno = 0; + boolean hitEOF = false; + do { + int startline = lineno; + printPrompt("jsdb> "); + Object source = read(EMPTY_ARRAY); + if (source == null) { + hitEOF = true; + break; + } + lineno++; + Object result = evalString(source.toString(), sourceName, startline); + if (result != null) { + printError(result.toString()); + } + if (isQuitting()) { + // The user executed the quit() function. + break; + } + } while (!hitEOF); + } else { + Reader in = null; + try { + in = new BufferedReader(new FileReader(filename)); + evalReader(in, filename); + } catch (FileNotFoundException ex) { + println("File '" + filename + "' not found"); + throw new RuntimeException(ex); + } + } + } + + protected Object evalString(String source, String filename, int lineNum) { + try { + engine.put(ScriptEngine.FILENAME, filename); + return engine.eval(source); + } catch (ScriptException sexp) { + printError(sexp.toString(), sexp); + } catch (Exception exp) { + printError(exp.toString(), exp); + } + return null; + } + + private Object evalReader(Reader in, String filename) { + try { + engine.put(ScriptEngine.FILENAME, filename); + return engine.eval(in); + } catch (ScriptException sexp) { + System.err.println(sexp); + printError(sexp.toString(), sexp); + } finally { + try { + in.close(); + } catch (IOException ioe) { + printError(ioe.toString(), ioe); + } + } + return null; + } + + // lazily initialized input reader + private BufferedReader inReader; + // debug mode or not + protected final boolean debug; + private boolean quitting; + // underlying jsr-223 script engine + private ScriptEngine engine; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaString.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaString.java new file mode 100644 index 00000000000..b53b2a4a68c --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaString.java @@ -0,0 +1,77 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import sun.jvm.hotspot.oops.*; + +/** + * Wraps a java.lang.String instance of the target VM. + */ +public class JSJavaString extends JSJavaInstance { + public JSJavaString(Instance instance, JSJavaFactory fac) { + super(instance, fac); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("String (address="); + buf.append(getOop().getHandle()); + buf.append(", value="); + buf.append("'"); + buf.append(getString()); + buf.append('\''); + buf.append(')'); + return buf.toString(); + } + + protected Object getFieldValue(String name) { + if (name.equals("stringValue")) { + return getString(); + } else { + return super.getFieldValue(name); + } + } + + protected String[] getFieldNames() { + String[] fields = super.getFieldNames(); + String[] res = new String[fields.length + 1]; + System.arraycopy(fields, 0, res, 0, fields.length); + res[fields.length] = "stringValue"; + return res; + } + + protected boolean hasField(String name) { + if (name.equals("stringValue")) { + return true; + } else { + return super.hasField(name); + } + } + + //-- Internals only below this point + private String getString() { + return OopUtilities.stringOopToString(getOop()); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaThread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaThread.java new file mode 100644 index 00000000000..40402b5a3a8 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaThread.java @@ -0,0 +1,186 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +/** + * Wraps a JavaThread instance in the target VM. + */ +public class JSJavaThread extends JSJavaInstance { + public JSJavaThread(Instance threadOop, JSJavaFactory fac) { + super(threadOop, fac); + // JavaThread retrieved from java.lang.Thread instance may be null. + // This is the case for threads not-started and for zombies. Wherever + // appropriate, check for null instead of resulting in NullPointerException. + this.jthread = OopUtilities.threadOopGetJavaThread(threadOop); + } + + public JSJavaThread(JavaThread jt, JSJavaFactory fac) { + super((Instance) jt.getThreadObj(), fac); + this.jthread = jt; + } + + public String toString() { + String name = getName(); + StringBuffer buf = new StringBuffer(); + buf.append("Thread (address="); + buf.append(getOop().getHandle()); + buf.append(", name="); + if (name != null) { + buf.append(name); + } else { + buf.append(""); + } + buf.append(')'); + return buf.toString(); + } + + protected Object getFieldValue(String name) { + if (name.equals("name")) { + return getName(); + } else if (name.equals("frames")) { + return getFrames(); + } else if (name.equals("monitors")) { + return getOwnedMonitors(); + } else { + return super.getFieldValue(name); + } + } + + protected String[] getFieldNames() { + String[] flds = super.getFieldNames(); + String[] res = new String[flds.length + 2]; + System.arraycopy(flds, 0, res, 0, flds.length); + res[flds.length] = "frames"; + res[flds.length + 1] = "monitors"; + return res; + } + + protected boolean hasField(String name) { + if (name.equals("frames") || name.equals("monitors")) { + return true; + } else { + return super.hasField(name); + } + } + + //-- Internals only below this point + private String getName() { + return OopUtilities.threadOopGetName(getOop()); + } + + private synchronized JSList getFrames() { + if (framesCache == null) { + final List list = new ArrayList(0); + if (jthread != null) { + JavaVFrame jvf = jthread.getLastJavaVFrameDbg(); + while (jvf != null) { + list.add(jvf); + jvf = jvf.javaSender(); + } + } + framesCache = factory.newJSList(list); + } + return framesCache; + } + + private synchronized JSList getOwnedMonitors() { + if (monitorsCache == null) { + final List ownedMonitors = new ArrayList(0); + if (jthread != null) { + List lockedObjects = new ArrayList(); // List + + ObjectMonitor waitingMonitor = jthread.getCurrentWaitingMonitor(); + OopHandle waitingObj = null; + if (waitingMonitor != null) { + // save object of current wait() call (if any) for later comparison + waitingObj = waitingMonitor.object(); + } + + ObjectMonitor pendingMonitor = jthread.getCurrentPendingMonitor(); + OopHandle pendingObj = null; + if (pendingMonitor != null) { + // save object of current enter() call (if any) for later comparison + pendingObj = pendingMonitor.object(); + } + + JavaVFrame frame = jthread.getLastJavaVFrameDbg(); + while (frame != null) { + List frameMonitors = frame.getMonitors(); // List + for (Iterator miItr = frameMonitors.iterator(); miItr.hasNext(); ) { + MonitorInfo mi = (MonitorInfo) miItr.next(); + OopHandle obj = mi.owner(); + if (obj == null) { + // this monitor doesn't have an owning object so skip it + continue; + } + + if (obj.equals(waitingObj)) { + // the thread is waiting on this monitor so it isn't really owned + continue; + } + + if (obj.equals(pendingObj)) { + // the thread is pending on this monitor so it isn't really owned + continue; + } + + boolean found = false; + for (Iterator loItr = lockedObjects.iterator(); loItr.hasNext(); ) { + // check for recursive locks + if (obj.equals(loItr.next())) { + found = true; + break; + } + } + if (found) { + // already have this object so don't include it + continue; + } + // add the owning object to our list + lockedObjects.add(obj); + } + frame = (JavaVFrame) frame.javaSender(); + } + + // now convert List to List + ObjectHeap heap = VM.getVM().getObjectHeap(); + for (Iterator loItr = lockedObjects.iterator(); loItr.hasNext(); ) { + ownedMonitors.add(heap.newOop((OopHandle)loItr.next())); + } + } + monitorsCache = factory.newJSList(ownedMonitors); + } + return monitorsCache; + } + + private JavaThread jthread; + private JSList framesCache; + private JSList monitorsCache; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaTypeArray.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaTypeArray.java new file mode 100644 index 00000000000..dbf05aa6dd6 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaTypeArray.java @@ -0,0 +1,41 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import sun.jvm.hotspot.oops.TypeArray; + +/** + This is JavaScript wrapper for Java Primitive Array. +*/ + +public class JSJavaTypeArray extends JSJavaArray { + public JSJavaTypeArray(TypeArray array, JSJavaFactory fac) { + super(array, fac); + } + + public final TypeArray getTypeArray() { + return (TypeArray) getArray(); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaTypeArrayKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaTypeArrayKlass.java new file mode 100644 index 00000000000..f18337ffcf1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaTypeArrayKlass.java @@ -0,0 +1,98 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.utilities.*; + +/** + This is JavaScript wrapper for TypeArrayKlass. +*/ + +public class JSJavaTypeArrayKlass extends JSJavaArrayKlass { + public JSJavaTypeArrayKlass(TypeArrayKlass kls, JSJavaFactory fac) { + super(kls, fac); + } + + public final TypeArrayKlass getTypeArrayKlass() { + return (TypeArrayKlass) getArrayKlass(); + } + + public String getName() { + int type = (int) getTypeArrayKlass().getElementType(); + switch (type) { + case TypeArrayKlass.T_BOOLEAN: + return "boolean[]"; + case TypeArrayKlass.T_CHAR: + return "char[]"; + case TypeArrayKlass.T_FLOAT: + return "float[]"; + case TypeArrayKlass.T_DOUBLE: + return "double[]"; + case TypeArrayKlass.T_BYTE: + return "byte[]"; + case TypeArrayKlass.T_SHORT: + return "short[]"; + case TypeArrayKlass.T_INT: + return "int[]"; + case TypeArrayKlass.T_LONG: + return "long[]"; + default: + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "Unknown primitive array type"); + } + return null; + } + } + + public Object getFieldValue(int index, Array array) { + TypeArray typeArr = (TypeArray) array; + int type = (int) getTypeArrayKlass().getElementType(); + switch (type) { + case TypeArrayKlass.T_BOOLEAN: + return Boolean.valueOf(typeArr.getBooleanAt(index)); + case TypeArrayKlass.T_CHAR: + return new String(new char[] { typeArr.getCharAt(index) }); + case TypeArrayKlass.T_FLOAT: + return new Float(typeArr.getFloatAt(index)); + case TypeArrayKlass.T_DOUBLE: + return new Double(typeArr.getDoubleAt(index)); + case TypeArrayKlass.T_BYTE: + return new Byte(typeArr.getByteAt(index)); + case TypeArrayKlass.T_SHORT: + return new Short(typeArr.getShortAt(index)); + case TypeArrayKlass.T_INT: + return new Integer(typeArr.getIntAt(index)); + case TypeArrayKlass.T_LONG: + return new Long(typeArr.getLongAt(index)); + default: + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "Unknown primitive array type"); + } + return null; + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaVM.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaVM.java new file mode 100644 index 00000000000..71d57c087f0 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaVM.java @@ -0,0 +1,234 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; + +public class JSJavaVM extends DefaultScriptObject { + private static final int FIELD_ADDRESS_SIZE = 0; + private static final int FIELD_BUILD_INFO = 1; + private static final int FIELD_CPU = 2; + private static final int FIELD_FLAGS = 3; + private static final int FIELD_HEAP = 4; + private static final int FIELD_OS = 5; + private static final int FIELD_SYS_PROPS = 6; + private static final int FIELD_THREADS = 7; + private static final int FIELD_TYPE = 8; + private static final int FIELD_VERSION = 9; + private static final int FIELD_CLASS_PATH = 10; + private static final int FIELD_BOOT_CLASS_PATH = 11; + private static final int FIELD_USER_DIR = 12; + private static final int FIELD_UNDEFINED = -1; + + public JSJavaVM(JSJavaFactory factory) { + this.factory = factory; + this.vm = VM.getVM(); + } + + public Object get(String name) { + int fieldID = getFieldID(name); + switch (fieldID) { + case FIELD_ADDRESS_SIZE: + return new Long(getVMBit()); + case FIELD_BUILD_INFO: + return vm.getVMInternalInfo(); + case FIELD_CPU: + return vm.getCPU(); + case FIELD_FLAGS: + return getFlags(); + case FIELD_HEAP: + return getHeap(); + case FIELD_OS: + return vm.getOS(); + case FIELD_SYS_PROPS: + return getSysProps(); + case FIELD_THREADS: + return getThreads(); + case FIELD_TYPE: + return getType(); + case FIELD_VERSION: + return vm.getVMRelease(); + case FIELD_CLASS_PATH: + return getClassPath(); + case FIELD_BOOT_CLASS_PATH: + return getBootClassPath(); + case FIELD_USER_DIR: + return getUserDir(); + case FIELD_UNDEFINED: + default: + return super.get(name); + } + } + + public Object[] getIds() { + Object[] superIds = super.getIds(); + Object[] tmp = fields.keySet().toArray(); + Object[] res = new Object[superIds.length + tmp.length]; + System.arraycopy(tmp, 0, res, 0, tmp.length); + System.arraycopy(superIds, 0, res, tmp.length, superIds.length); + return res; + } + + public boolean has(String name) { + if (getFieldID(name) != FIELD_UNDEFINED) { + return true; + } else { + return super.has(name); + } + } + + public void put(String name, Object value) { + if (getFieldID(name) == FIELD_UNDEFINED) { + super.put(name, value); + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("Java Hotspot "); + buf.append(getType()); + buf.append(' '); + buf.append(getVMBit()); + buf.append(" bit VM (build "); + buf.append(vm.getVMRelease()); + buf.append(")"); + return buf.toString(); + } + + //-- Internals only below this point + private static Map fields = new HashMap(); + private static void addField(String name, int fieldId) { + fields.put(name, new Integer(fieldId)); + } + + private static int getFieldID(String name) { + Integer res = (Integer) fields.get(name); + return (res != null)? res.intValue() : FIELD_UNDEFINED; + } + + static { + addField("addressSize", FIELD_ADDRESS_SIZE); + addField("buildInfo", FIELD_BUILD_INFO); + addField("cpu", FIELD_CPU); + addField("flags", FIELD_FLAGS); + addField("heap", FIELD_HEAP); + addField("os", FIELD_OS); + addField("sysProps", FIELD_SYS_PROPS); + addField("threads", FIELD_THREADS); + addField("type", FIELD_TYPE); + addField("version", FIELD_VERSION); + addField("classPath", FIELD_CLASS_PATH); + addField("bootClassPath", FIELD_BOOT_CLASS_PATH); + addField("userDir", FIELD_USER_DIR); + } + + private long getVMBit() { + // address size in bits + return vm.getAddressSize() * 8; + } + + private synchronized JSMap getFlags() { + if (flagsCache == null) { + VM.Flag[] flags = vm.getCommandLineFlags(); + Map map = new HashMap(); + if (flags != null) { + for (int f = 0; f < flags.length; f++) { + VM.Flag flag = flags[f]; + map.put(flag.getName(), flag.getValue()); + } + } + flagsCache = factory.newJSMap(map); + } + return flagsCache; + } + + private synchronized JSJavaHeap getHeap() { + if (heapCache == null) { + heapCache = factory.newJSJavaHeap(); + } + return heapCache; + } + + private synchronized JSMap getSysProps() { + if (sysPropsCache == null) { + Properties props = vm.getSystemProperties(); + Map map = new HashMap(); + if (props != null) { + Enumeration e = props.propertyNames(); + while (e.hasMoreElements()) { + String key = (String) e.nextElement(); + map.put(key, props.getProperty(key)); + } + } + sysPropsCache = factory.newJSMap(map); + } + return sysPropsCache; + } + + private synchronized JSList getThreads() { + if (threadsCache == null) { + List threads = new ArrayList(0); + threadsCache = factory.newJSList(threads); + JavaThread jthread = vm.getThreads().first(); + while (jthread != null) { + threads.add(jthread); + jthread = jthread.next(); + } + } + return threadsCache; + } + + private String getType() { + if (vm.isClientCompiler()) { + return "Client"; + } else if (vm.isServerCompiler()) { + return "Server"; + } else { + return "Core"; + } + } + + private String getClassPath() { + return vm.getSystemProperty("java.class.path"); + } + + private String getBootClassPath() { + return vm.getSystemProperty("sun.boot.class.path"); + } + + private String getUserDir() { + return vm.getSystemProperty("user.dir"); + } + + private JSMap flagsCache; + private JSJavaHeap heapCache; + private JSMap sysPropsCache; + private JSList threadsCache; + private final JSJavaFactory factory; + private final VM vm; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSList.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSList.java new file mode 100644 index 00000000000..9300cf72136 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSList.java @@ -0,0 +1,120 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; + +/** + This is JavaScript wrapper for Java List. +*/ + +public class JSList extends DefaultScriptObject { + public JSList(List list, JSJavaFactory fac) { + this.list = list; + this.factory = fac; + } + + public Object get(String name) { + if (name.equals("length")) { + return new Integer(list.size()); + } else { + return super.get(name); + } + } + + public Object get(int index) { + if (isInRange(index)) { + Object item = list.get(index); + return wrapObject(item); + } else { + return super.get(index); + } + } + + public Object[] getIds() { + Object[] superIds = super.getIds(); + final int size = list.size(); + Object[] res = new Object[superIds.length + size]; + for (int i = 0; i < size; i++) { + res[i] = new Integer(i); + } + System.arraycopy(superIds, 0, res, size, superIds.length); + return res; + } + + public boolean has(String name) { + if (name.equals("length")) { + return true; + } else { + return super.has(name); + } + } + + public boolean has(int index) { + if (isInRange(index)) { + return true; + } else { + return super.has(index); + } + } + + public void put(String name, Object value) { + if (! name.equals("length")) { + super.put(name, value); + } + } + + public void put(int index, Object value) { + if (! isInRange(index)) { + super.put(index, value); + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append('['); + for (Iterator itr = list.iterator(); itr.hasNext();) { + buf.append(wrapObject(itr.next())); + if (itr.hasNext()) { + buf.append(", "); + } + } + buf.append(']'); + return buf.toString(); + } + + //-- Internals only below this point + private boolean isInRange(int index) { + return index >= 0 && index < list.size(); + } + + private Object wrapObject(Object obj) { + return factory.newJSJavaWrapper(obj); + } + + private final List list; + private final JSJavaFactory factory; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSMap.java new file mode 100644 index 00000000000..8244734098b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSMap.java @@ -0,0 +1,94 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; + +/** + This is JavaScript wrapper for a Map. +*/ + +public class JSMap extends DefaultScriptObject { + public JSMap(Map map, JSJavaFactory fac) { + this.map = map; + this.factory = fac; + } + + public Object get(String name) { + if (map.containsKey(name)) { + return wrapObject(map.get(name)); + } else { + return super.get(name); + } + } + + public Object[] getIds() { + Object[] superIds = super.getIds(); + Object[] tmp = map.keySet().toArray(); + Object[] res = new Object[superIds.length + tmp.length]; + System.arraycopy(tmp, 0, res, 0, tmp.length); + System.arraycopy(superIds, 0, res, tmp.length, superIds.length); + return res; + } + + public boolean has(String name) { + if (map.containsKey(name)) { + return true; + } else { + return super.has(name); + } + } + + public void put(String name, Object value) { + if (! map.containsKey(name)) { + super.put(name, value); + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + Set keys = map.keySet(); + buf.append('{'); + for (Iterator itr = keys.iterator(); itr.hasNext();) { + Object key = itr.next(); + buf.append(key); + buf.append('='); + buf.append(wrapObject(map.get(key))); + if (itr.hasNext()) { + buf.append(", "); + } + } + buf.append('}'); + return buf.toString(); + } + + private Object wrapObject(Object obj) { + return factory.newJSJavaWrapper(obj); + } + + private final Map map; + private final JSJavaFactory factory; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/MapScriptObject.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/MapScriptObject.java new file mode 100644 index 00000000000..4322f76a530 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/MapScriptObject.java @@ -0,0 +1,122 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.Map; +import java.util.HashMap; +import java.util.Collections; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import javax.script.Invocable; + +/** + * Simple implementation of ScriptObject interface + * with property storage backed by a Map. This class + * can be extended to override any of the methods, in + * particular to add "function" valued properties. + */ +public class MapScriptObject implements ScriptObject { + // map to store the properties + private Map map; + + public MapScriptObject() { + this(new HashMap()); + } + + public MapScriptObject(Map map) { + // make it synchronized + this.map = Collections.synchronizedMap(map); + } + + public Object[] getIds() { + return map.keySet().toArray(); + } + + public Object get(String name) { + if (has(name)) { + return map.get(name); + } else { + return UNDEFINED; + } + } + + public Object get(int index) { + if (has(index)) { + Object key = Integer.valueOf(index); + return map.get(key); + } else { + return UNDEFINED; + } + } + + public void put(String name, Object value) { + map.put(name, value); + } + + public void put(int index, Object value) { + map.put(Integer.valueOf(index), value); + } + + public boolean has(String name) { + return map.containsKey(name); + } + + public boolean has(int index) { + return map.containsKey(Integer.valueOf(index)); + } + + public boolean delete(String name) { + if (map.containsKey(name)) { + map.remove(name); + return true; + } else { + return false; + } + } + + public boolean delete(int index) { + Object key = Integer.valueOf(index); + if (map.containsKey(key)) { + map.remove(key); + return true; + } else { + return false; + } + } + + // add a function valued property that invokes given Method + protected void putFunction(Object target, Method method) { + putFunction(target, method, true); + } + + // add a function valued property that invokes given Method + protected void putFunction(Object target, Method method, boolean wrapArgs) { + map.put(method.getName(), new MethodCallable(target, method, wrapArgs)); + } + + // add a function valued property that invokes given script function + protected void putFunction(Object target, String name, Invocable invocable) { + map.put(name, new InvocableCallable(target, name, invocable)); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/MethodCallable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/MethodCallable.java new file mode 100644 index 00000000000..c9d6b138608 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/MethodCallable.java @@ -0,0 +1,66 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.lang.reflect.Method; +import javax.script.ScriptException; + +/** + * An implementation of Callable interface that + * invokes an instance or static Java method when + * called. + */ +public class MethodCallable implements Callable { + private Object target; + private Method method; + private boolean wrapArgs; + + // "wrapArgs" tells whether the underlying java Method + // accepts one Object[] argument or it wants usual way of + // passing arguments. The former method is used when you + // want to implement a Callable that is variadic. + public MethodCallable(Object target, Method method, boolean wrapArgs) { + this.method = method; + this.target = target; + this.wrapArgs = wrapArgs; + } + + public MethodCallable(Object target, Method method) { + this(target, method, true); + } + + public Object call(Object[] args) throws ScriptException { + try { + if (wrapArgs) { + return method.invoke(target, new Object[] { args }); + } else { + return method.invoke(target, args); + } + } catch (RuntimeException re) { + throw re; + } catch (Exception exp) { + throw new ScriptException(exp); + } + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/ObjectVisitor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/ObjectVisitor.java new file mode 100644 index 00000000000..37d4b629626 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/ObjectVisitor.java @@ -0,0 +1,34 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +/** + This visitor is supplied to SOQLEngine.executeQuery + to receive result set objects one by one. +*/ + +public interface ObjectVisitor { + public void visit(Object o); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/SOQLEngine.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/SOQLEngine.java new file mode 100644 index 00000000000..ef51b7e3c27 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/SOQLEngine.java @@ -0,0 +1,265 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +import java.util.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; + +/** + * This is SOQL (Simple Object Query Language) engine. This + * uses JavaScript engine for the "select" and "where" expression + * parts. + */ +public class SOQLEngine extends JSJavaScriptEngine { + public static synchronized SOQLEngine getEngine() { + if (soleInstance == null) { + soleInstance = new SOQLEngine(); + } + return soleInstance; + } + + /** + Query is of the form + + select <java script code to select> + [ from [instanceof] <class name> [<identifier>] + [ where <java script boolean expression> ] + ] + */ + public synchronized void executeQuery(String query, ObjectVisitor visitor) + throws SOQLException { + debugPrint("query : " + query); + StringTokenizer st = new StringTokenizer(query); + if (st.hasMoreTokens()) { + String first = st.nextToken(); + if (! first.equals("select") ) { + throw new SOQLException("query syntax error: no 'select' clause"); + } + } else { + throw new SOQLException("query syntax error: no 'select' clause"); + } + + int selectStart = query.indexOf("select"); + int fromStart = query.indexOf("from"); + + String selectExpr = null; + String className = null; + boolean isInstanceOf = false; + String whereExpr = null; + String identifier = null; + + if (fromStart != -1) { + selectExpr = query.substring(selectStart + "select".length(), fromStart); + st = new StringTokenizer(query.substring(fromStart + "from".length())); + + if (st.hasMoreTokens()) { + String tmp = st.nextToken(); + if (tmp.equals("instanceof")) { + isInstanceOf = true; + if (! st.hasMoreTokens()) { + throw new SOQLException("no class name after 'instanceof'"); + } + className = st.nextToken(); + } else { + className = tmp; + } + } else { + throw new SOQLException("query syntax error: class name must follow 'from'"); + } + + if (st.hasMoreTokens()) { + identifier = st.nextToken(); + if (identifier.equals("where")) { + throw new SOQLException("query syntax error: identifier should follow class name"); + } + if (st.hasMoreTokens()) { + String tmp = st.nextToken(); + if (! tmp.equals("where")) { + throw new SOQLException("query syntax error: 'where' clause expected after 'from' clause"); + } + int whereEnd = query.lastIndexOf("where") + 5; // "where".length + whereExpr = query.substring(whereEnd); + } + } else { + throw new SOQLException("query syntax error: identifier should follow class name"); + } + } else { // no from clause + selectExpr = query.substring(selectStart + "select".length(), query.length()); + } + + executeQuery(new SOQLQuery(selectExpr, isInstanceOf, className, identifier, whereExpr), visitor); + } + + private void executeQuery(SOQLQuery q, ObjectVisitor visitor) throws SOQLException { + InstanceKlass kls = null; + if (q.className != null) { + kls = SystemDictionaryHelper.findInstanceKlass(q.className); + if (kls == null) { + throw new SOQLException(q.className + " is not found!"); + } + } + + + StringBuffer buf = new StringBuffer(); + buf.append("function result("); + if (q.identifier != null) { + buf.append(q.identifier); + } + buf.append(") { return "); + buf.append(q.selectExpr.replace('\n', ' ')); + buf.append("; }"); + + String selectCode = buf.toString(); + debugPrint(selectCode); + String whereCode = null; + if (q.whereExpr != null) { + buf = new StringBuffer(); + buf.append("function filter("); + buf.append(q.identifier); + buf.append(") { return "); + buf.append(q.whereExpr.replace('\n', ' ')); + buf.append("; }"); + whereCode = buf.toString(); + debugPrint(whereCode); + } else { + whereCode = "filter = null;"; + } + + beginQuery(); + // compile select expression and where condition + evalString(selectCode, "", 1); + evalString(whereCode, "", 1); + + // iterate thru heap, if needed + if (q.className != null) { + try { + iterateOops(kls, visitor, q.isInstanceOf); + } finally { + endQuery(); + } + } else { + // simple "select " query + try { + Object select = call("result", new Object[] {}); + visitor.visit(select); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private void dispatchObject(Oop oop, ObjectVisitor visitor, boolean filterExists) { + JSJavaObject jsObj = factory.newJSJavaObject(oop); + Object[] args = new Object[] { jsObj }; + boolean b = true; + + try { + if (filterExists) { + Object res = call("filter", args); + if (res instanceof Boolean) { + b = ((Boolean)res).booleanValue(); + } else if (res instanceof Number) { + b = ((Number)res).intValue() != 0; + } else { + b = (res != null); + } + } + + if (b) { + Object select = call("result", args); + visitor.visit(select); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void iterateOops(final InstanceKlass ik, final ObjectVisitor visitor, + boolean includeSubtypes) { + ObjectHeap oh = VM.getVM().getObjectHeap(); + oh.iterateObjectsOfKlass(new HeapVisitor() { + boolean filterExists; + public void prologue(long usedSize) { + filterExists = getScriptEngine().get("filter") != null; + } + public boolean doObj(Oop obj) { + dispatchObject(obj, visitor, filterExists); + return false; + } + public void epilogue() {} + }, ik, includeSubtypes); + } + + // we create fresh ObjectReader and factory to avoid + // excessive cache across queries. + private void beginQuery() { + objReader = new ObjectReader(); + factory = new JSJavaFactoryImpl(); + } + + // at the end of query we clear object reader cache + // and factory cache + private void endQuery() { + objReader = null; + factory = null; + } + + protected ObjectReader getObjectReader() { + return objReader; + } + + protected JSJavaFactory getJSJavaFactory() { + return factory; + } + + protected boolean isQuitting() { + return false; + } + + protected void quit() { + // do nothing + } + + private static void debugPrint(String msg) { + if (debug) System.out.println(msg); + } + + private static final boolean debug; + static { + debug = System.getProperty("sun.jvm.hotspot.utilities.soql.SOQLEngine.debug") != null; + } + + protected SOQLEngine() { + super(debug); + start(); + } + + private ObjectReader objReader; + private JSJavaFactory factory; + private static SOQLEngine soleInstance; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/SOQLException.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/SOQLException.java new file mode 100644 index 00000000000..b96835685af --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/SOQLException.java @@ -0,0 +1,39 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +public class SOQLException extends Exception { + public SOQLException(String msg) { + super(msg); + } + + public SOQLException(String msg, Throwable cause) { + super(msg, cause); + } + + public SOQLException(Throwable cause) { + super(cause); + } +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/SOQLQuery.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/SOQLQuery.java new file mode 100644 index 00000000000..b3b0d1bcad1 --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/SOQLQuery.java @@ -0,0 +1,42 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.utilities.soql; + +class SOQLQuery { + SOQLQuery(String selectExpr, boolean isInstanceOf, + String className, String identifier, String whereExpr) { + this.selectExpr = selectExpr; + this.isInstanceOf = isInstanceOf; + this.className = className; + this.identifier = identifier; + this.whereExpr = whereExpr; + } + + String selectExpr; + boolean isInstanceOf; + String className; + String identifier; + String whereExpr; +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/ScriptObject.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/ScriptObject.java new file mode 100644 index 00000000000..ac2fb6bae7d --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/ScriptObject.java @@ -0,0 +1,89 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.jvm.hotspot.utilities.soql; + +/** + * Any Java object supporting this interface can be + * accessed from scripts with "simpler" access pattern. + * For example, a script engine may support natural + * property/field access syntax for the properties exposed + * via this interface. We use this interface so that we + * can dynamically add/delete/modify fields exposed to + * scripts. If we stick to JavaBean pattern, then property + * set is fixed. + */ +public interface ScriptObject { + // special sentinel to denote no-result -- so that + // null could be used as proper value + public static final Object UNDEFINED = new Object(); + // empty object array + public static final Object[] EMPTY_ARRAY = new Object[0]; + + /* + * Returns all property names supported by this object. + * Property "name" is either a String or an Integer". + */ + public Object[] getIds(); + + /** + * Get the value of the named property. + */ + public Object get(String name); + + /** + * Get the value of the "indexed" property. + * Returns UNDEFINED if the property does not exist. + */ + public Object get(int index); + + /** + * Set the value of the named property. + */ + public void put(String name, Object value); + + /** + * Set the value of the indexed property. + */ + public void put(int index, Object value); + + /** + * Returns whether the named property exists or not. + */ + public boolean has(String name); + + /** + * Returns whether the indexed property exists or not. + */ + public boolean has(int index); + + /** + * Deletes the named property. Returns true on success. + */ + public boolean delete(String name); + + /** + * Deletes the indexed property. Returns true on success. + */ + public boolean delete(int index); +} diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/sa.js b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/sa.js new file mode 100644 index 00000000000..226e793e78b --- /dev/null +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/sa.js @@ -0,0 +1,1162 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// shorter names for SA packages + + +// SA package name abbreviations are kept in 'sapkg' object +// to avoid global namespace pollution +var sapkg = new Object(); + +sapkg.hotspot = Packages.sun.jvm.hotspot; +sapkg.asm = sapkg.hotspot.asm; +sapkg.bugspot = sapkg.hotspot.bugspot; +sapkg.c1 = sapkg.hotspot.c1; +sapkg.code = sapkg.hotspot.code; +sapkg.compiler = sapkg.hotspot.compiler; + +// 'debugger' is a JavaScript keyword :-( +// sapkg.debugger = sapkg.hotspot.debugger; + +sapkg.interpreter = sapkg.hotspot.interpreter; +sapkg.livejvm = sapkg.hotspot.livejvm; +sapkg.jdi = sapkg.hotspot.jdi; +sapkg.memory = sapkg.hotspot.memory; +sapkg.oops = sapkg.hotspot.oops; +sapkg.runtime = sapkg.hotspot.runtime; +sapkg.tools = sapkg.hotspot.tools; +sapkg.types = sapkg.hotspot.types; +sapkg.ui = sapkg.hotspot.ui; +sapkg.utilities = sapkg.hotspot.utilities; + +// SA singletons are kept in 'sa' object +var sa = new Object(); +sa.vm = sapkg.runtime.VM.getVM(); +sa.dbg = sa.vm.getDebugger(); +sa.cdbg = sa.dbg.CDebugger; +sa.heap = sa.vm.universe.heap(); +sa.systemDictionary = sa.vm.systemDictionary; +sa.sysDict = sa.systemDictionary; +sa.symbolTable = sa.vm.symbolTable; +sa.symTbl = sa.symbolTable; +sa.threads = sa.vm.threads; +sa.interpreter = sa.vm.interpreter; +sa.typedb = sa.vm.typeDataBase; +sa.codeCache = sa.vm.codeCache; +// 'objHeap' is different from 'heap'!. +// This is SA's Oop factory and heap-walker +sa.objHeap = sa.vm.objectHeap; + +// few useful global variables +var OS = sa.vm.OS; +var CPU = sa.vm.CPU; +var LP64 = sa.vm.LP64; +var isClient = sa.vm.clientCompiler; +var isServer = sa.vm.serverCompiler; +var isCore = sa.vm.isCore(); +var addressSize = sa.vm.addressSize; +var oopSize = sa.vm.oopSize; + +// this "main" function is called immediately +// after loading this script file +function main(globals, jvmarg) { + // wrap a sun.jvm.hotspot.utilities.soql.ScriptObject + // object so that the properties of it can be accessed + // in natural object.field syntax. + function wrapScriptObject(so) { + function unwrapScriptObject(wso) { + var objType = typeof(wso); + if ((objType == 'object' || + objType == 'function') + && "__wrapped__" in wso) { + return wso.__wrapped__; + } else { + return wso; + } + } + + function prepareArgsArray(array) { + var args = new Array(array.length); + for (var a = 0; a < array.length; a++) { + var elem = array[a]; + elem = unwrapScriptObject(elem); + if (typeof(elem) == 'function') { + args[a] = new sapkg.utilities.soql.Callable() { + call: function(myargs) { + var tmp = new Array(myargs.length); + for (var i = 0; i < myargs.length; i++) { + tmp[i] = wrapScriptObject(myargs[i]); + } + return elem.apply(this, tmp); + } + } + } else { + args[a] = elem; + } + } + return args; + } + + if (so instanceof sapkg.utilities.soql.ScriptObject) { + return new JSAdapter() { + __getIds__: function() { + return so.getIds(); + }, + + __has__ : function(name) { + if (typeof(name) == 'number') { + return so["has(int)"](name); + } else { + if (name == '__wrapped__') { + return true; + } else if (so["has(java.lang.String)"](name)) { + return true; + } else if (name.equals('toString')) { + return true; + } else { + return false; + } + } + }, + + __delete__ : function(name) { + if (typeof(name) == 'number') { + return so["delete(int)"](name); + } else { + return so["delete(java.lang.String)"](name); + } + }, + + __get__ : function(name) { + if (! this.__has__(name)) { + return undefined; + } + if (typeof(name) == 'number') { + return wrapScriptObject(so["get(int)"](name)); + } else { + if (name == '__wrapped__') { + return so; + } else { + var value = so["get(java.lang.String)"](name); + if (value instanceof sapkg.utilities.soql.Callable) { + return function() { + var args = prepareArgsArray(arguments); + var r; + try { + r = value.call(args); + } catch (e) { + println("call to " + name + " failed!"); + throw e; + } + return wrapScriptObject(r); + } + } else if (name == 'toString') { + return function() { + return so.toString(); + } + } else { + return wrapScriptObject(value); + } + } + } + } + }; + } else { + return so; + } + } + + // set "jvm" global variable that wraps a + // sun.jvm.hotspot.utilities.soql.JSJavaVM instance + if (jvmarg != null) { + jvm = wrapScriptObject(jvmarg); + // expose "heap" global variable + heap = jvm.heap; + } + + // expose all "function" type properties of + // sun.jvm.hotspot.utilitites.soql.JSJavaScriptEngine + // as global functions here. + globals = wrapScriptObject(globals); + for (var prop in globals) { + if (typeof(globals[prop]) == 'function') { + this[prop] = globals[prop]; + } + } + + // define "writeln" and "write" if not defined + if (typeof(writeln) == 'undefined') { + writeln = println; + } + + if (typeof(write) == 'undefined') { + write = print; + } + + // "registerCommand" function is defined if we + // are running as part of "CLHSDB" tool. CLHSDB + // tool exposes Unix-style commands. + + // if "registerCommand" function is defined + // then register few global functions as "commands". + if (typeof(registerCommand) == 'function') { + this.printDis = function(addr, len) { + if (!addr) { + writeln("Usage: dis address [ length ]"); + } else { + dis(addr, len); + } + } + registerCommand("dis", "dis address [ length ]", "printDis"); + + this.jclass = function(name) { + if (typeof(name) == "string") { + var clazz = sapkg.utilities.SystemDictionaryHelper.findInstanceKlass(name); + if (clazz) { + writeln(clazz.getName().asString() + " @" + clazz.getHandle().toString()); + } else { + writeln("class not found: " + name); + } + } else { + writeln("Usage: class name"); + } + } + registerCommand("class", "class name", "jclass"); + + this.jclasses = function() { + forEachKlass(function (clazz) { + writeln(clazz.getName().asString() + " @" + clazz.getHandle().toString()); + }); + } + registerCommand("classes", "classes", "jclasses"); + + this.printJDis = function(addr) { + if (!addr) { + writeln("Usage: jdis address"); + } else { + jdis(addr); + } + } + registerCommand("jdis", "jdis address", "printJDis"); + + this.dclass = function(clazz, dir) { + if (!clazz) { + writeln("Usage: dumpclass { address | name } [ directory ]"); + } else { + if (!dir) { dir = "."; } + dumpClass(clazz, dir); + } + } + registerCommand("dumpclass", "dumpclass { address | name } [ directory ]", "dclass"); + registerCommand("dumpheap", "dumpheap [ file ]", "dumpHeap"); + + this.jseval = function(str) { + if (!str) { + writeln("Usage: jseval script"); + } else { + var res = eval(str); + if (res) { writeln(res); } + } + } + registerCommand("jseval", "jseval script", "jseval"); + + this.jsload = function(file) { + if (!file) { + writeln("Usage: jsload file"); + } else { + load(file); + } + } + registerCommand("jsload", "jsload file", "jsload"); + + this.printMem = function(addr, len) { + if (!addr) { + writeln("Usage: mem [ length ]"); + } else { + mem(addr, len); + } + } + registerCommand("mem", "mem address [ length ]", "printMem"); + + this.sysProps = function() { + for (var i in jvm.sysProps) { + writeln(i + ' = ' + jvm.sysProps[i]); + } + } + registerCommand("sysprops", "sysprops", "sysProps"); + + this.printWhatis = function(addr) { + if (!addr) { + writeln("Usage: whatis address"); + } else { + writeln(whatis(addr)); + } + } + registerCommand("whatis", "whatis address", "printWhatis"); + } +} + +// debugger functionality + +// string-to-Address +function str2addr(str) { + return sa.dbg.parseAddress(str); +} + +// number-to-Address +if (addressSize == 4) { + eval("function num2addr(num) { \ + return str2addr('0x' + java.lang.Integer.toHexString(0xffffffff & num)); \ + }"); +} else { + eval("function num2addr(num) { \ + return str2addr('0x' + java.lang.Long.toHexString(num)); \ + }"); +} + +// generic any-type-to-Address +// use this convenience function to accept address in any +// format -- number, string or an Address instance. +function any2addr(addr) { + var type = typeof(addr); + if (type == 'number') { + return num2addr(addr); + } else if (type == 'string') { + return str2addr(addr); + } else { + return addr; + } +} + +// Address-to-string +function addr2str(addr) { + if (addr == null) { + return (addressSize == 4)? '0x00000000' : '0x0000000000000000'; + } else { + return addr + ''; + } +} + +// Address-to-number +function addr2num(addr) { + return sa.dbg.getAddressValue(addr); +} + +// symbol-to-Address +function sym2addr(dso, sym) { + return sa.dbg.lookup(dso, sym); +} + +// returns the ClosestSymbol or null +function closestSymbolFor(addr) { + if (sa.cdbg == null) { + // no CDebugger support, return null + return null; + } else { + var dso = sa.cdbg.loadObjectContainingPC(addr); + if (dso != null) { + return dso.closestSymbolToPC(addr); + } else { + return null; + } + } +} + +// Address-to-symbol +// returns nearest symbol as string if found +// else returns address as string +function addr2sym(addr) { + var sym = closestSymbolFor(addr); + if (sym != null) { + return sym.name + '+' + sym.offset; + } else { + return addr2str(addr); + } +} + +// read 'num' bytes at 'addr' and return an array as result. +// returns Java byte[] type result and not a JavaScript array. +function readBytesAt(addr, num) { + addr = any2addr(addr); + var res = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, num); + var i; + for (i = 0; i < num; i++) { + res[i] = addr.getJByteAt(i); + } + return res; +} + +// read 'num' words at 'addr' and return an array as result. +// returns Java long[] type result and not a JavaScript array. +function readWordsAt(addr, num) { + addr = any2addr(addr); + var res = java.lang.reflect.Array.newInstance(java.lang.Long.TYPE, num); + var i; + for (i = 0; i < num; i++) { + res[i] = addr2num(addr.getAddressAt(i * addressSize)); + } + return res; +} + +// read the 'C' string at 'addr' +function readCStrAt(addr) { + addr = any2addr(addr); + return sapkg.utilities.CStringUtilities.getString(addr); +} + +// read the length of the 'C' string at 'addr' +function readCStrLen(addr) { + addr = any2addr(addr); + return sapkg.utilities.CStringUtilities.getStringLength(addr); +} + +// iterate through ThreadList of CDebugger +function forEachThread(callback) { + if (sa.cdbg == null) { + // no CDebugger support + return; + } else { + var itr = sa.cdbg.threadList.iterator(); + while (itr.hasNext()) { + if (callback(itr.next()) == false) return; + } + } +} + +// read register set of a ThreadProxy as name-value pairs +function readRegs(threadProxy) { + var ctx = threadProxy.context; + var num = ctx.numRegisters; + var res = new Object(); + var i; + for (i = 0; i < num; i++) { + res[ctx.getRegisterName(i)]= addr2str(ctx.getRegisterAsAddress(i)); + } + return res; +} + +// print register set for a given ThreaProxy +function regs(threadProxy) { + var res = readRegs(threadProxy); + for (i in res) { + writeln(i, '=', res[i]); + } +} + +// iterate through each CFrame of a given ThreadProxy +function forEachCFrame(threadProxy, callback) { + if (sa.cdbg == null) { + // no CDebugger support + return; + } else { + var cframe = sa.cdbg.topFrameForThread(threadProxy); + while (cframe != null) { + if (callback(cframe) == false) return; + cframe = cframe.sender(); + } + } +} + +// iterate through list of load objects (DLLs, DSOs) +function forEachLoadObject(callback) { + if (sa.cdbg == null) { + // no CDebugger support + return; + } else { + var itr = sa.cdbg.loadObjectList.iterator(); + while (itr.hasNext()) { + if (callback(itr.next()) == false) return; + } + } +} + +// print 'num' words at 'addr' +function mem(addr, num) { + if (num == undefined) { + num = 1; + } + addr = any2addr(addr); + var i; + for (i = 0; i < num; i++) { + var value = addr.getAddressAt(0); + writeln(addr2sym(addr) + ':', addr2str(value)); + addr = addr.addOffsetTo(addressSize); + } + writeln(); +} + +// return the disassemble class for current CPU +function disassemblerClass() { + var DisAsmClass; + if (CPU == 'x86') { + DisAsmClass = sapkg.asm.x86.X86Disassembler; + } else if (CPU == 'sparc') { + DisAsmClass = sapkg.asm.sparc.SPARCV9Disassembler; + } + return DisAsmClass; +} + +// print native code disassembly of 'num' bytes at 'addr' +function dis(addr, num) { + addr = any2addr(addr); + var nmethod = findNMethod(addr); + if (nmethod != null) { + // disassemble it as nmethod + nmethoddis(nmethod); + } else { + // raw disassembly + if (num == undefined) { + // size of one SPARC instruction and + // unknown number of Intel instructions. + num = 4; + } + DisAsmClass = disassemblerClass(); + if (DisAsmClass == undefined) { + // unsupported CPU + writeln(CPU + " is not yet supported!"); + return; + } + + var bytes = readBytesAt(addr, num); + var disAsm = new DisAsmClass(addr2num(addr), bytes); + disAsm.decode(new sapkg.asm.InstructionVisitor() { + visit: function (pc, instr) { + write(addr2sym(num2addr(pc)) + ':', '\t'); + writeln(instr.asString(pc, + new sapkg.asm.SymbolFinder() { + getSymbolFor: function(addr) { + return addr2sym(num2addr(addr)); + } + })); + } + }); + } +} + +// System dictionary functions + +// find InstanceKlass by name +function findInstanceKlass(name) { + return sapkg.utilities.SystemDictionaryHelper.findInstanceKlass(name); +} + +// get Java system loader (i.e., application launcher loader) +function systemLoader() { + return sa.sysDict.javaSystemLoader(); +} + +// iterate system dictionary for each 'Klass' +function forEachKlass(callback) { + var VisitorClass = sapkg.memory.SystemDictionary.ClassVisitor; + var visitor = new VisitorClass() { visit: callback }; + sa.sysDict["classesDo(sun.jvm.hotspot.memory.SystemDictionary$ClassVisitor)"](visitor); +} + +// iterate system dictionary for each 'Klass' and initiating loader +function forEachKlassAndLoader(callback) { + var VisitorClass = sapkg.memory.SystemDictionary.ClassAndLoaderVisitor; + var visitor = new VisitorClass() { visit: callback }; + sa.sysDict["classesDo(sun.jvm.hotspot.memory.SystemDictionary$ClassAndLoaderVisitor)"](visitor); +} + +// iterate system dictionary for each primitive array klass +function forEachPrimArrayKlass(callback) { + var VisitorClass = sapkg.memory.SystemDictionary.ClassAndLoaderVisitor; + sa.sysDict.primArrayClassesDo(new VisitorClass() { visit: callback }); +} + +// (hotspot) symbol table functions + +// String-to-Symbol +function str2sym(str) { + return sa.symTbl.probe(str); +} + +// Symbol-to-String +function sym2str(sym) { + return sym.asString(); +} + +// oop functions + +// Address-to-Oop +function addr2oop(addr) { + addr = any2addr(addr); + return sa.objHeap.newOop(addr.addOffsetToAsOopHandle(0)); +} + +// Oop-to-Address +function oop2addr(oop) { + return oop.handle; +} + +// 'oop' to higher-level java object wrapper in which for(i in o) +// works by iterating java level fields and javaobject.javafield +// syntax works. +function oop2obj(oop) { + return object(addr2str(oop.handle)); +} + +// higher level java object wrapper to oop +function obj2oop(obj) { + return addr2oop(str2addr(address(obj))); +} + +// Java heap iteration + +// iterates Java heap for each Oop +function forEachOop(callback) { + sa.objHeap.iterate(new sapkg.oops.HeapVisitor() { doObj: callback }); +} + +// iterates Java heap for each Oop of given 'klass'. +// 'includeSubtypes' tells whether to include objects +// of subtypes of 'klass' or not +function forEachOopOfKlass(callback, klass, includeSubtypes) { + if (klass == undefined) { + klass = findInstanceKlass("java.lang.Object"); + } + + if (includeSubtypes == undefined) { + includeSubtypes = true; + } + sa.objHeap.iterateObjectsOfKlass( + new sapkg.oops.HeapVisitor() { doObj: callback }, + klass, includeSubtypes); +} + +// code cache functions + +// iterates CodeCache for each 'CodeBlob' +function forEachCodeBlob(callback) { + var VisitorClass = sapkg.code.CodeCacheVisitor; + sa.codeCache.iterate(new VisitorClass() { visit: callback }); +} + +// find the ClodBlob (if any) that contains given address +function findCodeBlob(addr) { + addr = any2addr(addr); + return sa.codeCache.findBlobUnsafe(addr); +} + +// find the NMethod (if any) that contains given address +function findNMethod(addr) { + var codeBlob = findCodeBlob(addr); + return (codeBlob != null && codeBlob.isNMethod())? codeBlob : null; +} + +// returns PcDesc at given address or null +function pcDescAt(addr) { + addr = any2addr(addr); + var nmethod = findNMethod(addr); + return (nmethod != null)? nmethod.safepoints.get(addr) : null; +} + +// helpers for nmethod disassembler +function printScope(scopeDesc) { + if (scopeDesc == null) { + return; + } + printScope(scopeDesc.sender()); + var method = scopeDesc.method; + var bci = scopeDesc.BCI; + var line = -1; + if (method.hasLineNumberTable()) { + line = method.getLineNumberFromBCI(bci); + } + + write('\t', method.externalNameAndSignature(), '@', method.handle, 'bci=' + bci); + if (line != -1) { + write('line=' + line); + } + writeln(); +} + +function printSafepointInfo(nmethod, pcDesc) { + var scopeDesc = nmethod.getScopeDescAt( + pcDesc.getRealPC(nmethod), + pcDesc.isAtCall()); + printScope(scopeDesc); +} + +// print disassembly for a given nmethod +function nmethoddis(nmethod) { + var DisAsmClass = disassemblerClass(); + if (DisAsmClass == undefined) { + writeln(CPU + " is not yet supported!"); + return; + } + + var method = nmethod.method; + writeln('NMethod:', method.externalNameAndSignature(), '@', method.handle); + + var codeBegin = nmethod.codeBegin(); + var codeEnd = nmethod.codeEnd(); + var size = codeEnd.minus(codeBegin); + var code = readBytesAt(codeBegin, size); + var startPc = addr2num(codeBegin); + var verifiedEntryPoint = addr2num(nmethod.verifiedEntryPoint); + var entryPoint = addr2num(nmethod.entryPoint); + var interpreterEntryPoint = addr2num(nmethod.interpreterEntryPointOrNull); + var safepoints = nmethod.safepoints; + var disAsm = new DisAsmClass(startPc, code); + disAsm.decode(new sapkg.asm.InstructionVisitor() { + visit: function(curPc, instr) { + if (curPc == verifiedEntryPoint) { + writeln(); + writeln("Verified Entry Point:"); + } + if (curPc == entryPoint) { + writeln(); + writeln("Entry Point:"); + } + if (curPc == interpreterEntryPoint) { + writeln(""); + writeln("Interpreter Entry Point:"); + } + + var pcDesc = safepoints.get(num2addr(curPc)); + var isSafepoint = (pcDesc != null); + if (isSafepoint && pcDesc.isAtCall()) { + printSafepointInfo(nmethod, pcDesc); + } + + write(num2addr(curPc) + ':', '\t'); + writeln(instr.asString(curPc, + new sapkg.asm.SymbolFinder() { + getSymbolFor: function(addr) { + return addr2sym(num2addr(addr)); + } + })); + + if (isSafepoint && !pcDesc.isAtCall()) { + printSafepointInfo(nmethod, pcDesc); + } + } + }); +} + +// bytecode interpreter functions + +// iterates interpreter codelets for each interpreter codelet +function forEachInterpCodelet(callback) { + var stubQueue = sa.interpreter.code; + var stub = stubQueue.first; + while (stub != null) { + if (callback(stub) == false) return; + stub = stubQueue.getNext(stub); + } +} + +// helper for bytecode disassembler +function printExceptionTable(method) { + var expTbl = method.getExceptionTable(); + var len = expTbl.getLength(); + if (len != 0) { + var i; + var cpool = method.constants; + writeln("start", '\t', "end", '\t', "handler", '\t', "exception"); + writeln(""); + for (i = 0; i < len; i += 4) { + write(expTbl.getIntAt(i), '\t', + expTbl.getIntAt(i + 1), '\t', + expTbl.getIntAt(i + 2), '\t'); + var cpIndex = expTbl.getIntAt(i + 3); + var oop = (cpIndex == 0)? null : cpool.getObjAt(cpIndex); + if (oop == null) { + writeln(""); + } else if (oop.isSymbol()) { + writeln(oop.asString().replace('/', '.')); + } else if (oop.isKlass()) { + writeln(oop.name.asString().replace('/', '.')); + } else { + writeln(cpIndex); + } + } + } +} + +// print Java bytecode disassembly +function jdis(method) { + if (method.getByteCode == undefined) { + // method oop may be specified by address + method = addr2oop(any2addr(method)); + } + writeln(method, '-', method.externalNameAndSignature()); + if (method.isNative()) { + writeln("native method"); + return; + } + if (method.isAbstract()) { + writeln("abstract method"); + return; + } + + writeln(); + var BytecodeDisAsmClass = sapkg.interpreter.BytecodeDisassembler; + var disAsm = new BytecodeDisAsmClass(method); + var bci = 0; + var hasLines = method.hasLineNumberTable(); + if (hasLines) { + writeln("bci", '\t', "line", '\t', "instruction"); + } else { + writeln("bci", '\t', "instruction"); + } + writeln(""); + disAsm.decode(new sapkg.interpreter.BytecodeVisitor() { + visit: function(bytecode) { + if (hasLines) { + var line = method.getLineNumberFromBCI(bci); + writeln(bci, '\t', line, '\t', bytecode); + } else { + writeln(bci, '\t', bytecode); + } + bci++; + } + }); + + writeln(); + printExceptionTable(method); +} + +// Java thread + +// iterates each Thread +function forEachJavaThread(callback) { + var threads = sa.threads; + var thread = threads.first(); + while (thread != null) { + if (callback(thread) == false) return; + thread = thread.next(); + } +} + +// iterate Frames of a given thread +function forEachFrame(javaThread, callback) { + var fr = javaThread.getLastFrameDbg(); + while (fr != null) { + if (callback(fr) == false) return; + fr = fr.sender(); + } +} + +// iterate JavaVFrames of a given JavaThread +function forEachVFrame(javaThread, callback) { + var vfr = javaThread.getLastJavaVFrameDbg(); + while (vfr != null) { + if (callback(vfr) == false) return; + vfr = vfr.javaSender(); + } +} + +function printStackTrace(javaThread) { + write("Thread "); + javaThread.printThreadIDOn(java.lang.System.out); + writeln(); + forEachVFrame(javaThread, function (vf) { + var method = vf.method; + write(' - ', method.externalNameAndSignature(), '@bci =', vf.getBCI()); + var line = method.getLineNumberFromBCI(vf.getBCI()); + if (line != -1) { write(', line=', line); } + if (vf.isCompiledFrame()) { write(" (Compiled Frame)"); } + if (vf.isInterpretedFrame()) { write(" (Interpreted Frame)"); } + writeln(); + }); + writeln(); + writeln(); +} + +// print Java stack trace for all threads +function where(javaThread) { + if (javaThread == undefined) { + forEachJavaThread(function (jt) { printStackTrace(jt); }); + } else { + printStackTrace(javaThread); + } +} + +// vmStructs access -- type database functions + +// find a VM type +function findVMType(typeName) { + return sa.typedb.lookupType(typeName); +} + +// iterate VM types +function forEachVMType(callback) { + var itr = sa.typedb.types; + while (itr.hasNext()) { + if (callback(itr.next()) == false) return; + } +} + +// find VM int constant +function findVMIntConst(name) { + return sa.typedb.lookupIntConstant(name); +} + +// find VM long constant +function findVMLongConst(name) { + return sa.typedb.lookupLongConstant(name); +} + +// iterate VM int constants +function forEachVMIntConst(callback) { + var itr = sa.typedb.intConstants; + while (itr.hasNext()) { + if (callback(itr.next()) == false) return; + } +} + +// iterate VM long constants +function forEachVMLongConst(callback) { + var itr = sa.typedb.longConstants; + while (itr.hasNext()) { + if (callback(itr.next()) == false) return; + } +} + +// returns VM Type at address +function vmTypeof(addr) { + addr = any2addr(addr); + return sa.typedb.guessTypeForAddress(addr); +} + +// does the given 'addr' points to an object of given 'type'? +// OR any valid Type at all (if type is undefined) +function isOfVMType(addr, type) { + addr = any2addr(addr); + if (type == undefined) { + return vmTypeof(addr) != null; + } else { + if (typeof(type) == 'string') { + type = findVMType(type); + } + return sa.typedb.addressTypeIsEqualToType(addr, type); + } +} + +// reads static field value +function readVMStaticField(field) { + var type = field.type; + if (type.isCIntegerType() || type.isJavaPrimitiveType()) { + return field.value; + } else if (type.isPointerType()) { + return field.address; + } else if (type.isOopType()) { + return field.oopHandle; + } else { + return field.staticFieldAddress; + } +} + +// reads given instance field of VM object at 'addr' +function readVMInstanceField(field, addr) { + var type = field.type; + if (type.isCIntegerType() || type.isJavaPrimitiveType()) { + return field.getValue(addr); + } else if (type.isPointerType()) { + return field.getAddress(addr); + } else if (type.isOopType()) { + return field.getOopHandle(addr); + } else { + return addr.addOffsetTo(field.offset); + } +} + +// returns name-value of pairs of VM type at given address. +// If address is unspecified, reads static fields as name-value pairs. +function readVMType(type, addr) { + if (typeof(type) == 'string') { + type = findVMType(type); + } + if (addr != undefined) { + addr = any2addr(addr); + } + + var result = new Object(); + var staticOnly = (addr == undefined); + while (type != null) { + var itr = type.fields; + while (itr.hasNext()) { + var field = itr.next(); + var isStatic = field.isStatic(); + if (staticOnly && isStatic) { + result[field.name] = readVMStaticField(field); + } else if (!staticOnly && !isStatic) { + result[field.name] = readVMInstanceField(field, addr); + } + } + type = type.superclass; + } + return result; +} + +function printVMType(type, addr) { + if (typeof(type) == 'string') { + type = findVMType(type); + } + var obj = readVMType(type, addr); + while (type != null) { + var itr = type.fields; + while (itr.hasNext()) { + var field = itr.next(); + var name = field.name; + var value = obj[name]; + if (value != undefined) { + writeln(field.type.name, type.name + '::' + name, '=', value); + } + } + type = type.superclass; + } +} + +// define readXXX and printXXX functions for each VM struct/class Type +tmp = new Object(); +tmp.itr = sa.typedb.types; +while (tmp.itr.hasNext()) { + tmp.type = tmp.itr.next(); + tmp.name = tmp.type.name; + if (tmp.type.isPointerType() || tmp.type.isOopType() || + tmp.type.isCIntegerType() || tmp.type.isJavaPrimitiveType() || + tmp.name.equals('address') || + tmp.name.equals("")) { + // ignore; + continue; + } else { + // some type names have ':'. replace to make it as a + // JavaScript identifier + tmp.name = tmp.name.replace(':', '_'); + eval("function read" + tmp.name + "(addr) {" + + " return readVMType('" + tmp.name + "', addr);}"); + eval("function print" + tmp.name + "(addr) {" + + " printVMType('" + tmp.name + "', addr); }"); + + /* FIXME: do we need this? + if (typeof(registerCommand) != 'undefined') { + var name = "print" + tmp.name; + registerCommand(name, name + " [address]", name); + } + */ + } +} +//clean-up the temporary +delete tmp; + +// VMObject factory + +// VM type to SA class map +var vmType2Class = new Object(); + +// This is *not* exhaustive. Add more if needed. +// code blobs +vmType2Class["BufferBlob"] = sapkg.code.BufferBlob; +vmType2Class["nmethod"] = sapkg.code.NMethod; +vmType2Class["RuntimeStub"] = sapkg.code.RuntimeStub; +vmType2Class["SafepointBlob"] = sapkg.code.SafepointBlob; +vmType2Class["C2IAdapter"] = sapkg.code.C2IAdapter; +vmType2Class["DeoptimizationBlob"] = sapkg.code.DeoptimizationBlob; +vmType2Class["ExceptionBlob"] = sapkg.code.ExceptionBlob; +vmType2Class["I2CAdapter"] = sapkg.code.I2CAdapter; +vmType2Class["OSRAdapter"] = sapkg.code.OSRAdapter; +vmType2Class["UncommonTrapBlob"] = sapkg.code.UncommonTrapBlob; +vmType2Class["PCDesc"] = sapkg.code.PCDesc; + +// interpreter +vmType2Class["InterpreterCodelet"] = sapkg.interpreter.InterpreterCodelet; + +// Java Threads +vmType2Class["JavaThread"] = sapkg.runtime.JavaThread; +vmType2Class["CompilerThread"] = sapkg.runtime.CompilerThread; +vmType2Class["SurrogateLockerThread"] = sapkg.runtime.JavaThread; +vmType2Class["DebuggerThread"] = sapkg.runtime.DebuggerThread; + +// gc +vmType2Class["GenCollectedHeap"] = sapkg.memory.GenCollectedHeap; +vmType2Class["CompactingPermGenGen"] = sapkg.memory.CompactingPermGenGen; +vmType2Class["DefNewGeneration"] = sapkg.memory.DefNewGeneration; +vmType2Class["TenuredGeneration"] = sapkg.memory.TenuredGeneration; + +// generic VMObject factory for a given address +// This is equivalent to VirtualConstructor. +function newVMObject(addr) { + addr = any2addr(addr); + var result = null; + forEachVMType(function (type) { + if (isOfVMType(addr, type)) { + var clazz = vmType2Class[type.name]; + if (clazz != undefined) { + result = new clazz(addr); + } + return false; + } else { + return true; + } + }); + return result; +} + +function vmobj2addr(vmobj) { + return vmobj.address; +} + +function addr2vmobj(addr) { + return newVMObject(addr); +} + +// Miscellaneous utilities + +// returns PointerLocation that describes the given pointer +function findPtr(addr) { + addr = any2addr(addr); + return sapkg.utilities.PointerFinder.find(addr); +} + +// is given address a valid Oop? +function isOop(addr) { + addr = any2addr(addr); + var oopHandle = addr.addOffsetToAsOopHandle(0); + return sapkg.utilities.RobustOopDeterminator.oopLooksValid(oopHandle); +} + +// returns description of given pointer as a String +function whatis(addr) { + addr = any2addr(addr); + var ptrLoc = findPtr(addr); + if (ptrLoc.isUnknown()) { + var vmType = vmTypeof(addr); + if (vmType != null) { + return "pointer to " + vmType.name; + } else { + var sym = closestSymbolFor(addr); + if (sym != null) { + return sym.name + '+' + sym.offset; + } else { + return ptrLoc.toString(); + } + } + } else { + return ptrLoc.toString(); + } +} diff --git a/hotspot/agent/src/share/lib/jlfgr-1_0.jar b/hotspot/agent/src/share/lib/jlfgr-1_0.jar new file mode 100644 index 00000000000..0a63e06972d Binary files /dev/null and b/hotspot/agent/src/share/lib/jlfgr-1_0.jar differ diff --git a/hotspot/agent/src/share/lib/maf-1_0.jar b/hotspot/agent/src/share/lib/maf-1_0.jar new file mode 100644 index 00000000000..5c8aafa5ea2 Binary files /dev/null and b/hotspot/agent/src/share/lib/maf-1_0.jar differ diff --git a/hotspot/agent/src/share/native/jvmdi/sa.cpp b/hotspot/agent/src/share/native/jvmdi/sa.cpp new file mode 100644 index 00000000000..e93521a7f5b --- /dev/null +++ b/hotspot/agent/src/share/native/jvmdi/sa.cpp @@ -0,0 +1,601 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include +#include +#include +#include "sa.hpp" +#include "jni.h" +#include "jvmdi.h" + +#ifndef WIN32 + #include +#else + typedef int int32_t; +#endif + +#ifdef WIN32 + #include + #define YIELD() Sleep(0) + #define SLEEP() Sleep(10) + #define vsnprintf _vsnprintf +#else + Error: please port YIELD() and SLEEP() macros to your platform +#endif + +using namespace std; + +////////////////////////////////////////////////////////////////////// +// // +// Exported "interface" for Java language-level interaction between // +// the SA and the VM. Note that the SA knows about the layout of // +// certain VM data structures and that knowledge is taken advantage // +// of in this code, although this interfaces with the VM via JVMDI. // +// // +////////////////////////////////////////////////////////////////////// + +extern "C" { + ///////////////////////////////////// + // // + // Events sent by the VM to the SA // + // // + ///////////////////////////////////// + + // Set by the SA when it attaches. Indicates that events should be + // posted via these exported variables, and that the VM should wait + // for those events to be acknowledged by the SA (via its setting + // saEventPending to 0). + JNIEXPORT volatile int32_t saAttached = 0; + + // Set to nonzero value by the VM when an event has been posted; set + // back to 0 by the SA when it has processed that event. + JNIEXPORT volatile int32_t saEventPending = 0; + + // Kind of the event (from jvmdi.h) + JNIEXPORT volatile int32_t saEventKind = 0; + + // + // Exception events + // + JNIEXPORT jthread saExceptionThread; + JNIEXPORT jclass saExceptionClass; + JNIEXPORT jmethodID saExceptionMethod; + JNIEXPORT int32_t saExceptionLocation; + JNIEXPORT jobject saExceptionException; + JNIEXPORT jclass saExceptionCatchClass; + JNIEXPORT jmethodID saExceptionCatchMethod; + JNIEXPORT int32_t saExceptionCatchLocation; + + // + // Breakpoint events + // + JNIEXPORT jthread saBreakpointThread; + JNIEXPORT jclass saBreakpointClass; + JNIEXPORT jmethodID saBreakpointMethod; + JNIEXPORT jlocation saBreakpointLocation; + + /////////////////////////////////////// + // // + // Commands sent by the SA to the VM // + // // + /////////////////////////////////////// + + extern JNIEXPORT const int32_t SA_CMD_SUSPEND_ALL = 0; + extern JNIEXPORT const int32_t SA_CMD_RESUME_ALL = 1; + extern JNIEXPORT const int32_t SA_CMD_TOGGLE_BREAKPOINT = 2; + extern JNIEXPORT const int32_t SA_CMD_BUF_SIZE = 1024; + + // SA sets this to a nonzero value when it is requesting a command + // to be processed; VM sets it back to 0 when the command has been + // executed + JNIEXPORT volatile int32_t saCmdPending = 0; + + // SA sets this to one of the manifest constants above to indicate + // the kind of command to be executed + JNIEXPORT volatile int32_t saCmdType = 0; + + // VM sets this to 0 if the last command succeeded or a nonzero + // value if it failed + JNIEXPORT volatile int32_t saCmdResult = 0; + + // If last command failed, this buffer will contain a descriptive + // error message + JNIEXPORT char saCmdResultErrMsg[SA_CMD_BUF_SIZE]; + + // + // Toggling of breakpoint command arguments. + // + // Originally there were separate set/clear breakpoint commands + // taking a class name, method name and signature, and the iteration + // through the debug information was done in the SA. It turns out + // that doing this work in the target VM is significantly faster, + // and since interactivity when setting and clearing breakpoints is + // important, the solution which resulted in more C/C++ code was used. + // + + // Source file name + JNIEXPORT char saCmdBkptSrcFileName[SA_CMD_BUF_SIZE]; + + // Package name ('/' as separator instead of '.') + JNIEXPORT char saCmdBkptPkgName[SA_CMD_BUF_SIZE]; + + // Line number + JNIEXPORT int32_t saCmdBkptLineNumber; + + // Output back to SA: indicator whether the last failure of a + // breakpoint toggle command was really an error or just a lack of + // debug information covering the requested line. 0 if not error. + // Valid only if saCmdResult != 0. + JNIEXPORT int32_t saCmdBkptResWasError; + + // Output back to SA: resulting line number at which the breakpoint + // was set or cleared (valid only if saCmdResult == 0) + JNIEXPORT int32_t saCmdBkptResLineNumber; + + // Output back to SA: resulting byte code index at which the + // breakpoint was set or cleared (valid only if saCmdResult == 0) + JNIEXPORT int32_t saCmdBkptResBCI; + + // Output back to SA: indicator whether the breakpoint operation + // resulted in a set or cleared breakpoint; nonzero if set, zero if + // cleared (valid only if saCmdResult == 0) + JNIEXPORT int32_t saCmdBkptResWasSet; + + // Output back to SA: method name the breakpoint was set in (valid + // only if saCmdResult == 0) + JNIEXPORT char saCmdBkptResMethodName[SA_CMD_BUF_SIZE]; + + // Output back to SA: method signature (JNI style) the breakpoint + // was set in (valid only if saCmdResult == 0) + JNIEXPORT char saCmdBkptResMethodSig[SA_CMD_BUF_SIZE]; +} + +// Internal state +static JavaVM* jvm = NULL; +static JVMDI_Interface_1* jvmdi = NULL; +static jthread debugThreadObj = NULL; +static bool suspended = false; +static vector suspendedThreads; +static JVMDI_RawMonitor eventLock = NULL; + +class MonitorLocker { +private: + JVMDI_RawMonitor lock; +public: + MonitorLocker(JVMDI_RawMonitor lock) { + this->lock = lock; + if (lock != NULL) { + jvmdi->RawMonitorEnter(lock); + } + } + ~MonitorLocker() { + if (lock != NULL) { + jvmdi->RawMonitorExit(lock); + } + } +}; + +class JvmdiDeallocator { +private: + void* ptr; +public: + JvmdiDeallocator(void* ptr) { + this->ptr = ptr; + } + ~JvmdiDeallocator() { + jvmdi->Deallocate((jbyte*) ptr); + } +}; + +class JvmdiRefListDeallocator { +private: + JNIEnv* env; + jobject* refList; + jint refCount; +public: + JvmdiRefListDeallocator(JNIEnv* env, jobject* refList, jint refCount) { + this->env = env; + this->refList = refList; + this->refCount = refCount; + } + ~JvmdiRefListDeallocator() { + for (int i = 0; i < refCount; i++) { + env->DeleteGlobalRef(refList[i]); + } + jvmdi->Deallocate((jbyte*) refList); + } +}; + +static void +stop(char* msg) { + fprintf(stderr, "%s", msg); + fprintf(stderr, "\n"); + exit(1); +} + +// This fills in the command result error message, sets the command +// result to -1, and clears the pending command flag +static void +reportErrorToSA(const char* str, ...) { + va_list varargs; + va_start(varargs, str); + vsnprintf(saCmdResultErrMsg, sizeof(saCmdResultErrMsg), str, varargs); + va_end(varargs); + saCmdResult = -1; + saCmdPending = 0; +} + +static bool +packageNameMatches(char* clazzName, char* pkg) { + int pkgLen = strlen(pkg); + int clazzNameLen = strlen(clazzName); + + if (pkgLen >= clazzNameLen + 1) { + return false; + } + + if (strncmp(clazzName, pkg, pkgLen)) { + return false; + } + + // Ensure that '/' is the next character if non-empty package name + int l = pkgLen; + if (l > 0) { + if (clazzName[l] != '/') { + return false; + } + l++; + } + // Ensure that there are no more trailing slashes + while (l < clazzNameLen) { + if (clazzName[l++] == '/') { + return false; + } + } + return true; +} + +static void +executeOneCommand(JNIEnv* env) { + switch (saCmdType) { + case SA_CMD_SUSPEND_ALL: { + if (suspended) { + reportErrorToSA("Target process already suspended"); + return; + } + + // We implement this by getting all of the threads and calling + // SuspendThread on each one, except for the thread object + // corresponding to this thread. Each thread for which the call + // succeeded (i.e., did not return JVMDI_ERROR_INVALID_THREAD) + // is added to a list which is remembered for later resumption. + // Note that this currently has race conditions since a thread + // might be started after we call GetAllThreads and since a + // thread for which we got an error earlier might be resumed by + // the VM while we are busy suspending other threads. We could + // solve this by looping until there are no more threads we can + // suspend, but a more robust and scalable solution is to add + // this functionality to the JVMDI interface (i.e., + // "suspendAll"). Probably need to provide an exclude list for + // such a routine. + jint threadCount; + jthread* threads; + if (jvmdi->GetAllThreads(&threadCount, &threads) != JVMDI_ERROR_NONE) { + reportErrorToSA("Error while getting thread list"); + return; + } + + + for (int i = 0; i < threadCount; i++) { + jthread thr = threads[i]; + if (!env->IsSameObject(thr, debugThreadObj)) { + jvmdiError err = jvmdi->SuspendThread(thr); + if (err == JVMDI_ERROR_NONE) { + // Remember this thread and do not free it + suspendedThreads.push_back(thr); + continue; + } else { + fprintf(stderr, " SA: Error %d while suspending thread\n", err); + // FIXME: stop, resume all threads, report error + } + } + env->DeleteGlobalRef(thr); + } + + // Free up threads + jvmdi->Deallocate((jbyte*) threads); + + // Suspension is complete + suspended = true; + break; + } + + case SA_CMD_RESUME_ALL: { + if (!suspended) { + reportErrorToSA("Target process already suspended"); + return; + } + + saCmdResult = 0; + bool errorOccurred = false; + jvmdiError firstError; + for (int i = 0; i < suspendedThreads.size(); i++) { + jthread thr = suspendedThreads[i]; + jvmdiError err = jvmdi->ResumeThread(thr); + env->DeleteGlobalRef(thr); + if (err != JVMDI_ERROR_NONE) { + if (!errorOccurred) { + errorOccurred = true; + firstError = err; + } + } + } + suspendedThreads.clear(); + suspended = false; + if (errorOccurred) { + reportErrorToSA("Error %d while resuming threads", firstError); + return; + } + break; + } + + case SA_CMD_TOGGLE_BREAKPOINT: { + saCmdBkptResWasError = 1; + + // Search line number info for all loaded classes + jint classCount; + jclass* classes; + + jvmdiError glcRes = jvmdi->GetLoadedClasses(&classCount, &classes); + if (glcRes != JVMDI_ERROR_NONE) { + reportErrorToSA("Error %d while getting loaded classes", glcRes); + return; + } + JvmdiRefListDeallocator rld(env, (jobject*) classes, classCount); + + bool done = false; + bool gotOne = false; + jclass targetClass; + jmethodID targetMethod; + jlocation targetLocation; + jint targetLineNumber; + + for (int i = 0; i < classCount && !done; i++) { + fflush(stderr); + jclass clazz = classes[i]; + char* srcName; + jvmdiError sfnRes = jvmdi->GetSourceFileName(clazz, &srcName); + if (sfnRes == JVMDI_ERROR_NONE) { + JvmdiDeallocator de1(srcName); + if (!strcmp(srcName, saCmdBkptSrcFileName)) { + // Got a match. Now see whether the package name of the class also matches + char* clazzName; + jvmdiError sigRes = jvmdi->GetClassSignature(clazz, &clazzName); + if (sigRes != JVMDI_ERROR_NONE) { + reportErrorToSA("Error %d while getting a class's signature", sigRes); + return; + } + JvmdiDeallocator de2(clazzName); + if (packageNameMatches(clazzName + 1, saCmdBkptPkgName)) { + // Iterate through all methods + jint methodCount; + jmethodID* methods; + if (jvmdi->GetClassMethods(clazz, &methodCount, &methods) != JVMDI_ERROR_NONE) { + reportErrorToSA("Error while getting methods of class %s", clazzName); + return; + } + JvmdiDeallocator de3(methods); + for (int j = 0; j < methodCount && !done; j++) { + jmethodID m = methods[j]; + jint entryCount; + JVMDI_line_number_entry* table; + jvmdiError lnRes = jvmdi->GetLineNumberTable(clazz, m, &entryCount, &table); + if (lnRes == JVMDI_ERROR_NONE) { + JvmdiDeallocator de4(table); + // Look for line number greater than or equal to requested line + for (int k = 0; k < entryCount && !done; k++) { + JVMDI_line_number_entry& entry = table[k]; + if (entry.line_number >= saCmdBkptLineNumber && + (!gotOne || entry.line_number < targetLineNumber)) { + gotOne = true; + targetClass = clazz; + targetMethod = m; + targetLocation = entry.start_location; + targetLineNumber = entry.line_number; + done = (targetLineNumber == saCmdBkptLineNumber); + } + } + } else if (lnRes != JVMDI_ERROR_ABSENT_INFORMATION) { + reportErrorToSA("Unexpected error %d while fetching line number table", lnRes); + return; + } + } + } + } + } else if (sfnRes != JVMDI_ERROR_ABSENT_INFORMATION) { + reportErrorToSA("Unexpected error %d while fetching source file name", sfnRes); + return; + } + } + + bool wasSet = true; + if (gotOne) { + // Really toggle this breakpoint + jvmdiError bpRes; + bpRes = jvmdi->SetBreakpoint(targetClass, targetMethod, targetLocation); + if (bpRes == JVMDI_ERROR_DUPLICATE) { + bpRes = jvmdi->ClearBreakpoint(targetClass, targetMethod, targetLocation); + wasSet = false; + } + if (bpRes != JVMDI_ERROR_NONE) { + reportErrorToSA("Unexpected error %d while setting or clearing breakpoint at bci %d, line %d", + bpRes, targetLocation, targetLineNumber); + return; + } + } else { + saCmdBkptResWasError = 0; + reportErrorToSA("No debug information found covering this line"); + return; + } + + // Provide result + saCmdBkptResLineNumber = targetLineNumber; + saCmdBkptResBCI = targetLocation; + saCmdBkptResWasSet = (wasSet ? 1 : 0); + { + char* methodName; + char* methodSig; + if (jvmdi->GetMethodName(targetClass, targetMethod, &methodName, &methodSig) + == JVMDI_ERROR_NONE) { + JvmdiDeallocator mnd(methodName); + JvmdiDeallocator msd(methodSig); + strncpy(saCmdBkptResMethodName, methodName, SA_CMD_BUF_SIZE); + strncpy(saCmdBkptResMethodSig, methodSig, SA_CMD_BUF_SIZE); + } else { + strncpy(saCmdBkptResMethodName, "", SA_CMD_BUF_SIZE); + strncpy(saCmdBkptResMethodSig, "", SA_CMD_BUF_SIZE); + } + } + break; + } + + default: + reportErrorToSA("Command %d not yet supported", saCmdType); + return; + } + + // Successful command execution + saCmdResult = 0; + saCmdPending = 0; +} + +static void +saCommandThread(void *arg) { + JNIEnv* env = NULL; + if (jvm->GetEnv((void **) &env, JNI_VERSION_1_2) != JNI_OK) { + stop("Error while starting Serviceability Agent " + "command thread: could not get JNI environment"); + } + + while (1) { + // Wait for command + while (!saCmdPending) { + SLEEP(); + } + + executeOneCommand(env); + } +} + +static void +saEventHook(JNIEnv *env, JVMDI_Event *event) +{ + MonitorLocker ml(eventLock); + + saEventKind = event->kind; + + if (event->kind == JVMDI_EVENT_VM_INIT) { + // Create event lock + if (jvmdi->CreateRawMonitor("Serviceability Agent Event Lock", &eventLock) + != JVMDI_ERROR_NONE) { + stop("Unable to create Serviceability Agent's event lock"); + } + // Start thread which receives commands from the SA. + jclass threadClass = env->FindClass("java/lang/Thread"); + if (threadClass == NULL) stop("Unable to find class java/lang/Thread"); + jstring threadName = env->NewStringUTF("Serviceability Agent Command Thread"); + if (threadName == NULL) stop("Unable to allocate debug thread name"); + jmethodID ctor = env->GetMethodID(threadClass, "", "(Ljava/lang/String;)V"); + if (ctor == NULL) stop("Unable to find appropriate constructor for java/lang/Thread"); + // Allocate thread object + jthread thr = (jthread) env->NewObject(threadClass, ctor, threadName); + if (thr == NULL) stop("Unable to allocate debug thread's java/lang/Thread instance"); + // Remember which thread this is + debugThreadObj = env->NewGlobalRef(thr); + if (debugThreadObj == NULL) stop("Unable to allocate global ref for debug thread object"); + // Start thread + jvmdiError err; + if ((err = jvmdi->RunDebugThread(thr, &saCommandThread, NULL, JVMDI_THREAD_NORM_PRIORITY)) + != JVMDI_ERROR_NONE) { + char buf[256]; + sprintf(buf, "Error %d while starting debug thread", err); + stop(buf); + } + // OK, initialization is done + return; + } + + if (!saAttached) { + return; + } + + switch (event->kind) { + case JVMDI_EVENT_EXCEPTION: { + fprintf(stderr, "SA: Exception thrown -- ignoring\n"); + saExceptionThread = event->u.exception.thread; + saExceptionClass = event->u.exception.clazz; + saExceptionMethod = event->u.exception.method; + saExceptionLocation = event->u.exception.location; + saExceptionException = event->u.exception.exception; + saExceptionCatchClass = event->u.exception.catch_clazz; + saExceptionCatchClass = event->u.exception.catch_clazz; + saExceptionCatchMethod = event->u.exception.catch_method; + saExceptionCatchLocation = event->u.exception.catch_location; + // saEventPending = 1; + break; + } + + case JVMDI_EVENT_BREAKPOINT: { + saBreakpointThread = event->u.breakpoint.thread; + saBreakpointClass = event->u.breakpoint.clazz; + saBreakpointMethod = event->u.breakpoint.method; + saBreakpointLocation = event->u.breakpoint.location; + saEventPending = 1; + break; + } + + default: + break; + } + + while (saAttached && saEventPending) { + SLEEP(); + } +} + +extern "C" { +JNIEXPORT jint JNICALL +JVM_OnLoad(JavaVM *vm, char *options, void *reserved) +{ + jvm = vm; + if (jvm->GetEnv((void**) &jvmdi, JVMDI_VERSION_1) != JNI_OK) { + return -1; + } + if (jvmdi->SetEventHook(&saEventHook) != JVMDI_ERROR_NONE) { + return -1; + } + return 0; +} +}; diff --git a/hotspot/agent/src/share/native/jvmdi/sa.dsp b/hotspot/agent/src/share/native/jvmdi/sa.dsp new file mode 100644 index 00000000000..0339afbdefa --- /dev/null +++ b/hotspot/agent/src/share/native/jvmdi/sa.dsp @@ -0,0 +1,105 @@ +# Microsoft Developer Studio Project File - Name="sa" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=sa - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sa.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sa.mak" CFG="sa - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sa - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "sa - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sa - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SA_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "D:\jdk1.4\include" /I "D:\jdk1.4\include\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SA_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 + +!ELSEIF "$(CFG)" == "sa - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SA_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "D:\jdk1.4\include" /I "D:\jdk1.4\include\win32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SA_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "sa - Win32 Release" +# Name "sa - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\sa.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/hotspot/agent/src/share/native/jvmdi/sa.dsw b/hotspot/agent/src/share/native/jvmdi/sa.dsw new file mode 100644 index 00000000000..d16525a403d --- /dev/null +++ b/hotspot/agent/src/share/native/jvmdi/sa.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "sa"=.\sa.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/hotspot/agent/src/share/native/jvmdi/sa.hpp b/hotspot/agent/src/share/native/jvmdi/sa.hpp new file mode 100644 index 00000000000..21793229600 --- /dev/null +++ b/hotspot/agent/src/share/native/jvmdi/sa.hpp @@ -0,0 +1,30 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "jni.h" + +extern "C" { +JNIEXPORT jint JNICALL +JVM_OnLoad(JavaVM *vm, char *options, void *reserved); +} diff --git a/hotspot/agent/test/jdi/README.jjh b/hotspot/agent/test/jdi/README.jjh new file mode 100644 index 00000000000..33b284fc6c0 --- /dev/null +++ b/hotspot/agent/test/jdi/README.jjh @@ -0,0 +1,39 @@ + +This dir contains a test for the JDI-SA implementation. + +sagtest.java, sagtarg.java are a normal JDI regression test +that uses TargetAdapter.java, TargetListener.java, TestScaffold.java, +and VMConnection.java. + +This test starts the debuggee, sagtarg.java, which just does a wait. +The test then calls sagdoit.java which calls all the JDJI interface +functions. Well, it doesn't call them all yet, but that is the plan. +At least all that are interesting to the JDI-SA client. The result of +each call is sent to stdout + +The script runjpda.sh runs this test. It then runs the targ part of +the test and calls gcore on it to get a core dump into file sagcore. +Do + runjpda.sh >& kk + +to run this. + + NOTE that this produces 1000s of lines of output + so be sure to redirect to a file. + +File sagclient.java is a test program that uses the JDI-SA +client to connect to a core file or pid and then calls sagdoit +which calls the JDI methods. + +The script runsa.sh can be used to run sagclient on sagcore: + runsa.sh sagcore >& kk1 + +You can then look at the differences between the runjpda.sh +and the runsa.sh run to see if there are bugs. Note that the +order of things might be different. + + +----------------------------------------- + +runjdb.sh contains a script that will run jdb on a core file +using the JDI-sa binding. diff --git a/hotspot/agent/test/jdi/SASanityChecker.java b/hotspot/agent/test/jdi/SASanityChecker.java new file mode 100644 index 00000000000..7c8a94b582a --- /dev/null +++ b/hotspot/agent/test/jdi/SASanityChecker.java @@ -0,0 +1,134 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import sun.jvm.hotspot.tools.*; +import sun.jvm.hotspot.runtime.*; +import java.io.*; +import java.util.*; +import java.util.jar.*; + +/** +This is a sanity checking tool for Serviceability Agent. To use this class, +refer to sasanity.sh script in the current directory. +*/ + +public class SASanityChecker extends Tool { + private static final String saJarName; + private static final Map c2types; + + static { + saJarName = System.getProperty("SASanityChecker.SAJarName", "sa-jdi.jar"); + c2types = new HashMap(); + Object value = new Object(); + c2types.put("sun.jvm.hotspot.code.ExceptionBlob", value); + c2types.put("sun.jvm.hotspot.code.DeoptimizationBlob", value); + c2types.put("sun.jvm.hotspot.code.UncommonTrapBlob", value); + + } + + public void run() { + String classPath = System.getProperty("java.class.path"); + StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator); + String saJarPath = null; + while (st.hasMoreTokens()) { + saJarPath = st.nextToken(); + if (saJarPath.endsWith(saJarName)) { + break; + } + } + + if (saJarPath == null) { + throw new RuntimeException(saJarName + " is not the CLASSPATH"); + } + + String cpuDot = "." + VM.getVM().getCPU() + "."; + String platformDot = "." + VM.getVM().getOS() + "_" + VM.getVM().getCPU() + "."; + boolean isClient = VM.getVM().isClientCompiler(); + + try { + FileInputStream fis = new FileInputStream(saJarPath); + JarInputStream jis = new JarInputStream(fis); + JarEntry je = null; + while ( (je = jis.getNextJarEntry()) != null) { + String entryName = je.getName(); + int dotClassIndex = entryName.indexOf(".class"); + if (dotClassIndex == -1) { + // skip non-.class stuff + continue; + } + + entryName = entryName.substring(0, dotClassIndex).replace('/', '.'); + + // skip debugger, asm classes, type classes and jdi binding classes + if (entryName.startsWith("sun.jvm.hotspot.debugger.") || + entryName.startsWith("sun.jvm.hotspot.asm.") || + entryName.startsWith("sun.jvm.hotspot.type.") || + entryName.startsWith("sun.jvm.hotspot.jdi.") ) { + continue; + } + + String runtimePkgPrefix = "sun.jvm.hotspot.runtime."; + int runtimeIndex = entryName.indexOf(runtimePkgPrefix); + if (runtimeIndex != -1) { + // look for further dot. if there, it has to be sub-package. + // in runtime sub-packages include only current platform classes. + if (entryName.substring(runtimePkgPrefix.length() + 1, entryName.length()).indexOf('.') != -1) { + if (entryName.indexOf(cpuDot) == -1 && + entryName.indexOf(platformDot) == -1) { + continue; + } + } + } + + if (isClient) { + if (c2types.get(entryName) != null) { + continue; + } + } else { + if (entryName.equals("sun.jvm.hotspot.c1.Runtime1")) { + continue; + } + } + + System.out.println("checking " + entryName + " .."); + // force init of the class to uncover any vmStructs mismatch + Class.forName(entryName); + } + } catch (Exception exp) { + System.out.println(); + System.out.println("FAILED"); + System.out.println(); + throw new RuntimeException(exp.getMessage()); + } + System.out.println(); + System.out.println("PASSED"); + System.out.println(); + } + + public static void main(String[] args) { + SASanityChecker checker = new SASanityChecker(); + checker.start(args); + checker.stop(); + } +} diff --git a/hotspot/agent/test/jdi/TEST.ROOT b/hotspot/agent/test/jdi/TEST.ROOT new file mode 100644 index 00000000000..ee11d9654ef --- /dev/null +++ b/hotspot/agent/test/jdi/TEST.ROOT @@ -0,0 +1,30 @@ +# +# Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This file identifies the root of the test-suite hierarchy. +# It also contains test-suite configuration information. +# DO NOT EDIT without first contacting jdk-regtest@eng. + +# The list of keywords supported in this test suite +keys=2d dnd i18n diff --git a/hotspot/agent/test/jdi/TargetAdapter.java b/hotspot/agent/test/jdi/TargetAdapter.java new file mode 100644 index 00000000000..354c5fe2f05 --- /dev/null +++ b/hotspot/agent/test/jdi/TargetAdapter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import com.sun.jdi.event.*; + +/** + * Base TargetListener implementation + */ +public class TargetAdapter implements TargetListener { + boolean shouldRemoveListener = false; + + public void removeThisListener() { + shouldRemoveListener = true; + } + + public boolean shouldRemoveListener() { + return shouldRemoveListener; + } + + public void eventSetReceived(EventSet set) {} + public void eventSetComplete(EventSet set) {} + public void eventReceived(Event event) {} + public void breakpointReached(BreakpointEvent event) {} + public void exceptionThrown(ExceptionEvent event) {} + public void stepCompleted(StepEvent event) {} + public void classPrepared(ClassPrepareEvent event) {} + public void classUnloaded(ClassUnloadEvent event) {} + public void methodEntered(MethodEntryEvent event) {} + public void methodExited(MethodExitEvent event) {} + public void fieldAccessed(AccessWatchpointEvent event) {} + public void fieldModified(ModificationWatchpointEvent event) {} + public void threadStarted(ThreadStartEvent event) {} + public void threadDied(ThreadDeathEvent event) {} + public void vmStarted(VMStartEvent event) {} + public void vmDied(VMDeathEvent event) {} + public void vmDisconnected(VMDisconnectEvent event) {} +} diff --git a/hotspot/agent/test/jdi/TargetListener.java b/hotspot/agent/test/jdi/TargetListener.java new file mode 100644 index 00000000000..eea5d7192c1 --- /dev/null +++ b/hotspot/agent/test/jdi/TargetListener.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import com.sun.jdi.event.*; + +/** + * Event listener framework + */ +public interface TargetListener { + boolean shouldRemoveListener(); + + void eventSetReceived(EventSet set); + void eventSetComplete(EventSet set); + void eventReceived(Event event); + void breakpointReached(BreakpointEvent event); + void exceptionThrown(ExceptionEvent event); + void stepCompleted(StepEvent event); + void classPrepared(ClassPrepareEvent event); + void classUnloaded(ClassUnloadEvent event); + void methodEntered(MethodEntryEvent event); + void methodExited(MethodExitEvent event); + void fieldAccessed(AccessWatchpointEvent event); + void fieldModified(ModificationWatchpointEvent event); + void threadStarted(ThreadStartEvent event); + void threadDied(ThreadDeathEvent event); + void vmStarted(VMStartEvent event); + void vmDied(VMDeathEvent event); + void vmDisconnected(VMDisconnectEvent event); +} diff --git a/hotspot/agent/test/jdi/TestScaffold.java b/hotspot/agent/test/jdi/TestScaffold.java new file mode 100644 index 00000000000..9ab066792b4 --- /dev/null +++ b/hotspot/agent/test/jdi/TestScaffold.java @@ -0,0 +1,758 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import com.sun.jdi.*; +import com.sun.jdi.request.*; +import com.sun.jdi.event.*; +import java.util.*; +import java.io.*; + +/** + * Framework used by all JDI regression tests + */ +abstract public class TestScaffold extends TargetAdapter { + private boolean shouldTrace = false; + private VMConnection connection; + private VirtualMachine vm; + private EventRequestManager requestManager; + private List listeners = Collections.synchronizedList(new LinkedList()); + + /** + * We create a VMDeathRequest, SUSPEND_ALL, to sync the BE and FE. + */ + //private VMDeathRequest ourVMDeathRequest = null; + Object ourVMDeathRequest = null; + + /** + * We create an ExceptionRequest, SUSPEND_NONE so that we can + * catch it and output a msg if an exception occurs in the + * debuggee. + */ + private ExceptionRequest ourExceptionRequest = null; + + /** + * If we do catch an uncaught exception, we set this true + * so the testcase can find out if it wants to. + */ + private boolean exceptionCaught = false; + ThreadReference vmStartThread = null; + boolean vmDied = false; + boolean vmDisconnected = false; + final String[] args; + protected boolean testFailed = false; + + static private class ArgInfo { + String targetVMArgs = ""; + String targetAppCommandLine = ""; + String connectorSpec = "com.sun.jdi.CommandLineLaunch:"; + int traceFlags = 0; + } + + /** + * An easy way to sleep for awhile + */ + public void mySleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException ee) { + } + } + + boolean getExceptionCaught() { + return exceptionCaught; + } + + void setExceptionCaught(boolean value) { + exceptionCaught = value; + } + + /** + * Return true if eventSet contains the VMDeathEvent for the request in + * the ourVMDeathRequest ivar. + */ + private boolean containsOurVMDeathRequest(EventSet eventSet) { + if (ourVMDeathRequest != null) { + Iterator myIter = eventSet.iterator(); + while (myIter.hasNext()) { + Event myEvent = (Event)myIter.next(); + if (!(myEvent instanceof VMDeathEvent)) { + // We assume that an EventSet contains only VMDeathEvents + // or no VMDeathEvents. + break; + } + if (ourVMDeathRequest.equals(myEvent.request())) { + return true; + } + } + } + return false; + } + + /************************************************************************ + * The following methods override those in our base class, TargetAdapter. + *************************************************************************/ + + /** + * Events handled directly by scaffold always resume (well, almost always) + */ + public void eventSetComplete(EventSet set) { + // The listener in connect(..) resumes after receiving our + // special VMDeathEvent. We can't also do the resume + // here or we will probably get a VMDisconnectedException + if (!containsOurVMDeathRequest(set)) { + traceln("TS: set.resume() called"); + set.resume(); + } + } + + /** + * This method sets up default requests. + * Testcases can override this to change default behavior. + */ + protected void createDefaultEventRequests() { + createDefaultVMDeathRequest(); + createDefaultExceptionRequest(); + } + + /** + * We want the BE to stop when it issues a VMDeathEvent in order to + * give the FE time to complete handling events that occured before + * the VMDeath. When we get the VMDeathEvent for this request in + * the listener in connect(), we will do a resume. + * If a testcase wants to do something special with VMDeathEvent's, + * then it should override this method with an empty method or + * whatever in order to suppress the automatic resume. The testcase + * will then be responsible for the handling of VMDeathEvents. It + * has to be sure that it does a resume if it gets a VMDeathEvent + * with SUSPEND_ALL, and it has to be sure that it doesn't do a + * resume after getting a VMDeath with SUSPEND_NONE (the automatically + * generated VMDeathEvent.) + */ + protected void createDefaultVMDeathRequest() { +// ourVMDeathRequest = requestManager.createVMDeathRequest(); +// ourVMDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); +// ourVMDeathRequest.enable(); + } + + /** + * This will allow us to print a warning if a debuggee gets an + * unexpected exception. The unexpected exception will be handled in + * the exceptionThrown method in the listener created in the connect() + * method. + * If a testcase does not want an uncaught exception to cause a + * msg, it must override this method. + */ + protected void createDefaultExceptionRequest() { + ourExceptionRequest = requestManager.createExceptionRequest(null, + false, true); + + // We can't afford to make this be other than SUSPEND_NONE. Otherwise, + // it would have to be resumed. If our connect() listener resumes it, + // what about the case where the EventSet contains other events with + // SUSPEND_ALL and there are other listeners who expect the BE to still + // be suspended when their handlers get called? + ourExceptionRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); + ourExceptionRequest.enable(); + } + + private class EventHandler implements Runnable { + EventHandler() { + Thread thread = new Thread(this); + thread.setDaemon(true); + thread.start(); + } + + private void notifyEvent(TargetListener listener, Event event) { + if (event instanceof BreakpointEvent) { + listener.breakpointReached((BreakpointEvent)event); + } else if (event instanceof ExceptionEvent) { + listener.exceptionThrown((ExceptionEvent)event); + } else if (event instanceof StepEvent) { + listener.stepCompleted((StepEvent)event); + } else if (event instanceof ClassPrepareEvent) { + listener.classPrepared((ClassPrepareEvent)event); + } else if (event instanceof ClassUnloadEvent) { + listener.classUnloaded((ClassUnloadEvent)event); + } else if (event instanceof MethodEntryEvent) { + listener.methodEntered((MethodEntryEvent)event); + } else if (event instanceof MethodExitEvent) { + listener.methodExited((MethodExitEvent)event); + } else if (event instanceof AccessWatchpointEvent) { + listener.fieldAccessed((AccessWatchpointEvent)event); + } else if (event instanceof ModificationWatchpointEvent) { + listener.fieldModified((ModificationWatchpointEvent)event); + } else if (event instanceof ThreadStartEvent) { + listener.threadStarted((ThreadStartEvent)event); + } else if (event instanceof ThreadDeathEvent) { + listener.threadDied((ThreadDeathEvent)event); + } else if (event instanceof VMStartEvent) { + listener.vmStarted((VMStartEvent)event); + } else if (event instanceof VMDeathEvent) { + listener.vmDied((VMDeathEvent)event); + } else if (event instanceof VMDisconnectEvent) { + listener.vmDisconnected((VMDisconnectEvent)event); + } else { + throw new InternalError("Unknown event type: " + event.getClass()); + } + } + + private void traceSuspendPolicy(int policy) { + if (shouldTrace) { + switch (policy) { + case EventRequest.SUSPEND_NONE: + traceln("TS: eventHandler: suspend = SUSPEND_NONE"); + break; + case EventRequest.SUSPEND_ALL: + traceln("TS: eventHandler: suspend = SUSPEND_ALL"); + break; + case EventRequest.SUSPEND_EVENT_THREAD: + traceln("TS: eventHandler: suspend = SUSPEND_EVENT_THREAD"); + break; + } + } + } + + public void run() { + boolean connected = true; + do { + try { + EventSet set = vm.eventQueue().remove(); + traceSuspendPolicy(set.suspendPolicy()); + synchronized (listeners) { + ListIterator iter = listeners.listIterator(); + while (iter.hasNext()) { + TargetListener listener = (TargetListener)iter.next(); + traceln("TS: eventHandler: listener = " + listener); + listener.eventSetReceived(set); + if (listener.shouldRemoveListener()) { + iter.remove(); + } else { + Iterator jter = set.iterator(); + while (jter.hasNext()) { + Event event = (Event)jter.next(); + traceln("TS: eventHandler: event = " + event.getClass()); + + if (event instanceof VMDisconnectEvent) { + connected = false; + } + listener.eventReceived(event); + if (listener.shouldRemoveListener()) { + iter.remove(); + break; + } + notifyEvent(listener, event); + if (listener.shouldRemoveListener()) { + iter.remove(); + break; + } + } + traceln("TS: eventHandler: end of events loop"); + if (!listener.shouldRemoveListener()) { + traceln("TS: eventHandler: calling ESC"); + listener.eventSetComplete(set); + if (listener.shouldRemoveListener()) { + iter.remove(); + } + } + } + traceln("TS: eventHandler: end of listeners loop"); + } + } + } catch (InterruptedException e) { + traceln("TS: eventHandler: InterruptedException"); + } catch (Exception e) { + failure("FAILED: Exception occured in eventHandler: " + e); + e.printStackTrace(); + connected = false; + synchronized(TestScaffold.this) { + // This will make the waiters such as waitForVMDisconnect + // exit their wait loops. + vmDisconnected = true; + TestScaffold.this.notifyAll(); + } + } + traceln("TS: eventHandler: End of outerloop"); + } while (connected); + traceln("TS: eventHandler: finished"); + } + } + + /** + * Constructor + */ + public TestScaffold(String[] args) { + this.args = args; + } + + public void enableScaffoldTrace() { + this.shouldTrace = true; + } + + public void disableScaffoldTrace() { + this.shouldTrace = false; + } + + + protected void startUp(String targetName) { + List argList = new ArrayList(Arrays.asList(args)); + argList.add(targetName); + println("run args: " + argList); + connect((String[]) argList.toArray(args)); + waitForVMStart(); + } + + protected BreakpointEvent startToMain(String targetName) { + startUp(targetName); + traceln("TS: back from startUp"); + BreakpointEvent bpr = resumeTo(targetName, "main", "([Ljava/lang/String;)V"); + waitForInput(); + return bpr; + } + + protected void waitForInput() { + if (System.getProperty("jpda.wait") != null) { + try { + System.err.println("Press to continue"); + System.in.read(); + System.err.println("running..."); + + } catch(Exception e) { + } + } + } + + /* + * Test cases should implement tests in runTests and should + * initiate testing by calling run(). + */ + abstract protected void runTests() throws Exception; + + final public void startTests() throws Exception { + try { + runTests(); + } finally { + shutdown(); + } + } + + protected void println(String str) { + System.err.println(str); + } + + protected void print(String str) { + System.err.print(str); + } + + protected void traceln(String str) { + if (shouldTrace) { + println(str); + } + } + + protected void failure(String str) { + println(str); + testFailed = true; + } + + private ArgInfo parseArgs(String args[]) { + ArgInfo argInfo = new ArgInfo(); + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-connect")) { + i++; + argInfo.connectorSpec = args[i]; + } else if (args[i].equals("-trace")) { + i++; + argInfo.traceFlags = Integer.decode(args[i]).intValue(); + } else if (args[i].startsWith("-J")) { + argInfo.targetVMArgs += (args[i].substring(2) + ' '); + + /* + * classpath can span two arguments so we need to handle + * it specially. + */ + if (args[i].equals("-J-classpath")) { + i++; + argInfo.targetVMArgs += (args[i] + ' '); + } + } else { + argInfo.targetAppCommandLine += (args[i] + ' '); + } + } + return argInfo; + } + + /** + * This is called to connect to a debuggee VM. It starts the VM and + * installs a listener to catch VMStartEvent, our default events, and + * VMDisconnectedEvent. When these events appear, that is remembered + * and waiters are notified. + * This is normally called in the main thread of the test case. + * It starts up an EventHandler thread that gets events coming in + * from the debuggee and distributes them to listeners. That thread + * keeps running until a VMDisconnectedEvent occurs or some exception + * occurs during its processing. + * + * The 'listenUntilVMDisconnect' method adds 'this' as a listener. + * This means that 'this's vmDied method will get called. This has a + * default impl in TargetAdapter.java which can be overridden in the + * testcase. + * + * waitForRequestedEvent also adds an adaptor listener that listens + * for the particular event it is supposed to wait for (and it also + * catches VMDisconnectEvents.) This listener is removed once + * its eventReceived method is called. + * waitForRequestedEvent is called by most of the methods to do bkpts, + * etc. + */ + public void connect(String args[]) { + ArgInfo argInfo = parseArgs(args); + + argInfo.targetVMArgs += VMConnection.getDebuggeeVMOptions(); + connection = new VMConnection(argInfo.connectorSpec, + argInfo.traceFlags); + + addListener(new TargetAdapter() { + public void eventSetComplete(EventSet set) { + if (TestScaffold.this.containsOurVMDeathRequest(set)) { + traceln("TS: connect: set.resume() called"); + set.resume(); + + // Note that we want to do the above resume before + // waking up any sleepers. + synchronized(TestScaffold.this) { + TestScaffold.this.notifyAll(); + } + } + } + + public void vmStarted(VMStartEvent event) { + synchronized(TestScaffold.this) { + vmStartThread = event.thread(); + TestScaffold.this.notifyAll(); + } + } + /** + * By default, we catch uncaught exceptions and print a msg. + * The testcase must override the createDefaultExceptionRequest + * method if it doesn't want this behavior. + */ + public void exceptionThrown(ExceptionEvent event) { + if (TestScaffold.this.ourExceptionRequest != null && + TestScaffold.this.ourExceptionRequest.equals( + event.request())) { + println("Note: Unexpected Debuggee Exception: " + + event.exception().referenceType().name() + + " at line " + event.location().lineNumber()); + TestScaffold.this.exceptionCaught = true; + } + } + + public void vmDied(VMDeathEvent event) { + vmDied = true; + traceln("TS: vmDied called"); + } + + public void vmDisconnected(VMDisconnectEvent event) { + synchronized(TestScaffold.this) { + vmDisconnected = true; + TestScaffold.this.notifyAll(); + } + } + }); + if (connection.connector().name().equals("com.sun.jdi.CommandLineLaunch")) { + if (argInfo.targetVMArgs.length() > 0) { + if (connection.connectorArg("options").length() > 0) { + throw new IllegalArgumentException("VM options in two places"); + } + connection.setConnectorArg("options", argInfo.targetVMArgs); + } + if (argInfo.targetAppCommandLine.length() > 0) { + if (connection.connectorArg("main").length() > 0) { + throw new IllegalArgumentException("Command line in two places"); + } + connection.setConnectorArg("main", argInfo.targetAppCommandLine); + } + } + + vm = connection.open(); + requestManager = vm.eventRequestManager(); + createDefaultEventRequests(); + new EventHandler(); + } + + + public VirtualMachine vm() { + return vm; + } + + public EventRequestManager eventRequestManager() { + return requestManager; + } + + public void addListener(TargetListener listener) { + traceln("TS: Adding listener " + listener); + listeners.add(listener); + } + + public void removeListener(TargetListener listener) { + traceln("TS: Removing listener " + listener); + listeners.remove(listener); + } + + + protected void listenUntilVMDisconnect() { + try { + addListener (this); + } catch (Exception ex){ + ex.printStackTrace(); + testFailed = true; + } finally { + // Allow application to complete and shut down + resumeToVMDisconnect(); + } + } + + public synchronized ThreadReference waitForVMStart() { + while ((vmStartThread == null) && !vmDisconnected) { + try { + wait(); + } catch (InterruptedException e) { + } + } + + if (vmStartThread == null) { + throw new VMDisconnectedException(); + } + + return vmStartThread; + } + + public synchronized void waitForVMDisconnect() { + traceln("TS: waitForVMDisconnect"); + while (!vmDisconnected) { + try { + wait(); + } catch (InterruptedException e) { + } + } + traceln("TS: waitForVMDisconnect: done"); + } + + public Event waitForRequestedEvent(final EventRequest request) { + class EventNotification { + Event event; + boolean disconnected = false; + } + final EventNotification en = new EventNotification(); + + TargetAdapter adapter = new TargetAdapter() { + public void eventReceived(Event event) { + if (request.equals(event.request())) { + traceln("TS:Listener2: got requested event"); + synchronized (en) { + en.event = event; + en.notifyAll(); + } + removeThisListener(); + } else if (event instanceof VMDisconnectEvent) { + traceln("TS:Listener2: got VMDisconnectEvent"); + synchronized (en) { + en.disconnected = true; + en.notifyAll(); + } + removeThisListener(); + } + } + }; + + addListener(adapter); + + try { + synchronized (en) { + traceln("TS: waitForRequestedEvent: vm.resume called"); + vm.resume(); + + while (!en.disconnected && (en.event == null)) { + en.wait(); + } + } + } catch (InterruptedException e) { + return null; + } + + if (en.disconnected) { + throw new RuntimeException("VM Disconnected before requested event occurred"); + } + return en.event; + } + + private StepEvent doStep(ThreadReference thread, int gran, int depth) { + final StepRequest sr = + requestManager.createStepRequest(thread, gran, depth); + + sr.addClassExclusionFilter("java.*"); + sr.addClassExclusionFilter("sun.*"); + sr.addClassExclusionFilter("com.sun.*"); + sr.addCountFilter(1); + sr.enable(); + StepEvent retEvent = (StepEvent)waitForRequestedEvent(sr); + requestManager.deleteEventRequest(sr); + return retEvent; + } + + public StepEvent stepIntoInstruction(ThreadReference thread) { + return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO); + } + + public StepEvent stepIntoLine(ThreadReference thread) { + return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO); + } + + public StepEvent stepOverInstruction(ThreadReference thread) { + return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER); + } + + public StepEvent stepOverLine(ThreadReference thread) { + return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER); + } + + public StepEvent stepOut(ThreadReference thread) { + return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OUT); + } + + public BreakpointEvent resumeTo(Location loc) { + final BreakpointRequest request = + requestManager.createBreakpointRequest(loc); + request.addCountFilter(1); + request.enable(); + return (BreakpointEvent)waitForRequestedEvent(request); + } + + public ReferenceType findReferenceType(String name) { + List rts = vm.classesByName(name); + Iterator iter = rts.iterator(); + while (iter.hasNext()) { + ReferenceType rt = (ReferenceType)iter.next(); + if (rt.name().equals(name)) { + return rt; + } + } + return null; + } + + public Method findMethod(ReferenceType rt, String name, String signature) { + List methods = rt.methods(); + Iterator iter = methods.iterator(); + while (iter.hasNext()) { + Method method = (Method)iter.next(); + if (method.name().equals(name) && + method.signature().equals(signature)) { + return method; + } + } + return null; + } + + public Location findLocation(ReferenceType rt, int lineNumber) + throws AbsentInformationException { + List locs = rt.locationsOfLine(lineNumber); + if (locs.size() == 0) { + throw new IllegalArgumentException("Bad line number"); + } else if (locs.size() > 1) { + throw new IllegalArgumentException("Line number has multiple locations"); + } + + return (Location)locs.get(0); + } + + public BreakpointEvent resumeTo(String clsName, String methodName, + String methodSignature) { + ReferenceType rt = findReferenceType(clsName); + if (rt == null) { + rt = resumeToPrepareOf(clsName).referenceType(); + } + + Method method = findMethod(rt, methodName, methodSignature); + if (method == null) { + throw new IllegalArgumentException("Bad method name/signature"); + } + + return resumeTo(method.location()); + } + + public BreakpointEvent resumeTo(String clsName, int lineNumber) throws AbsentInformationException { + ReferenceType rt = findReferenceType(clsName); + if (rt == null) { + rt = resumeToPrepareOf(clsName).referenceType(); + } + + return resumeTo(findLocation(rt, lineNumber)); + } + + public ClassPrepareEvent resumeToPrepareOf(String className) { + final ClassPrepareRequest request = + requestManager.createClassPrepareRequest(); + request.addClassFilter(className); + request.addCountFilter(1); + request.enable(); + return (ClassPrepareEvent)waitForRequestedEvent(request); + } + + public void resumeToVMDisconnect() { + try { + traceln("TS: resumeToVMDisconnect: vm.resume called"); + vm.resume(); + } catch (VMDisconnectedException e) { + // clean up below + } + waitForVMDisconnect(); + } + + public void shutdown() { + shutdown(null); + } + + public void shutdown(String message) { + traceln("TS: shutdown: vmDied= " + vmDied + + ", vmDisconnected= " + vmDisconnected + + ", connection = " + connection); + + if ((connection != null)) { + try { + connection.disposeVM(); + } catch (VMDisconnectedException e) { + // Shutting down after the VM has gone away. This is + // not an error, and we just ignore it. + } + } else { + traceln("TS: shutdown: disposeVM not called"); + } + if (message != null) { + println(message); + } + + vmDied = true; + vmDisconnected = true; + } +} diff --git a/hotspot/agent/test/jdi/VMConnection.java b/hotspot/agent/test/jdi/VMConnection.java new file mode 100644 index 00000000000..e6ecf95a712 --- /dev/null +++ b/hotspot/agent/test/jdi/VMConnection.java @@ -0,0 +1,378 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import com.sun.jdi.*; +import com.sun.jdi.connect.*; +import com.sun.jdi.request.EventRequestManager; + +import java.util.*; +import java.io.*; + + +/** + * Manages a VM conection for the JDI test framework. + */ +class VMConnection { + private VirtualMachine vm; + private Process process = null; + private int outputCompleteCount = 0; + + private final Connector connector; + private final Map connectorArgs; + private final int traceFlags; + + /** + * Return a String containing VM Options to pass to the debugee + * or an empty string if there are none. + * These are read from the first non-comment line + * in file test/com/sun/jdi/@debuggeeVMOptions. + */ + static public String getDebuggeeVMOptions() { + + // When we run under jtreg, test.src contains the pathname of + // the test/com/sun/jdi dir. + BufferedReader reader; + final String filename = "@debuggeeVMOptions"; + String srcDir = System.getProperty("test.src"); + + if (srcDir == null) { + srcDir = System.getProperty("user.dir"); + } + srcDir = srcDir + File.separator; + + File myDir = new File(srcDir); + + File myFile = new File(myDir, filename); + if (!myFile.canRead()) { + try { + // We have some subdirs of test/com/sun/jdi so in case we + // are in one of them, look in our parent dir for the file. + myFile = new File(myDir.getCanonicalFile().getParent(), + filename); + if (!myFile.canRead()) { + return ""; + } + } catch (IOException ee) { + System.out.println("-- Error 1 trying to access file " + + myFile.getPath() + ": " + ee); + return ""; + } + } + String wholePath = myFile.getPath(); + try { + reader = new BufferedReader(new FileReader(myFile)); + } catch (FileNotFoundException ee) { + System.out.println("-- Error 2 trying to access file " + + wholePath + ": " + ee); + return ""; + } + + String line; + String retVal = ""; + while (true) { + try { + line = reader.readLine(); + } catch (IOException ee) { + System.out.println("-- Error reading options from file " + + wholePath + ": " + ee); + break; + } + if (line == null) { + System.out.println("-- No debuggee VM options found in file " + + wholePath); + break; + } + line = line.trim(); + if (line.length() != 0 && !line.startsWith("#")) { + System.out.println("-- Added debuggeeVM options from file " + + wholePath + ": " + line); + retVal = line; + break; + } + // Else, read he next line. + } + try { + reader.close(); + } catch (IOException ee) { + } + return retVal; + } + + private Connector findConnector(String name) { + List connectors = Bootstrap.virtualMachineManager().allConnectors(); + Iterator iter = connectors.iterator(); + while (iter.hasNext()) { + Connector connector = (Connector)iter.next(); + if (connector.name().equals(name)) { + return connector; + } + } + return null; + } + + private Map parseConnectorArgs(Connector connector, String argString) { + StringTokenizer tokenizer = new StringTokenizer(argString, ","); + Map arguments = connector.defaultArguments(); + + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + int index = token.indexOf('='); + if (index == -1) { + throw new IllegalArgumentException("Illegal connector argument: " + + token); + } + String name = token.substring(0, index); + String value = token.substring(index + 1); + Connector.Argument argument = (Connector.Argument)arguments.get(name); + if (argument == null) { + throw new IllegalArgumentException("Argument " + name + + "is not defined for connector: " + + connector.name()); + } + argument.setValue(value); + } + return arguments; + } + + VMConnection(String connectSpec, int traceFlags) { + String nameString; + String argString; + int index = connectSpec.indexOf(':'); + if (index == -1) { + nameString = connectSpec; + argString = ""; + } else { + nameString = connectSpec.substring(0, index); + argString = connectSpec.substring(index + 1); + } + + connector = findConnector(nameString); + if (connector == null) { + throw new IllegalArgumentException("No connector named: " + + nameString); + } + + connectorArgs = parseConnectorArgs(connector, argString); + this.traceFlags = traceFlags; + } + + synchronized VirtualMachine open() { + if (connector instanceof LaunchingConnector) { + vm = launchTarget(); + } else if (connector instanceof AttachingConnector) { + vm = attachTarget(); + } else if (connector instanceof ListeningConnector) { + vm = listenTarget(); + } else { + throw new InternalError("Invalid connect type"); + } + vm.setDebugTraceMode(traceFlags); + System.out.println("JVM version:" + vm.version()); + System.out.println("JDI version: " + Bootstrap.virtualMachineManager().majorInterfaceVersion() + + "." + Bootstrap.virtualMachineManager().minorInterfaceVersion()); + System.out.println("JVM description: " + vm.description()); + + return vm; + } + + boolean setConnectorArg(String name, String value) { + /* + * Too late if the connection already made + */ + if (vm != null) { + return false; + } + + Connector.Argument argument = (Connector.Argument)connectorArgs.get(name); + if (argument == null) { + return false; + } + argument.setValue(value); + return true; + } + + String connectorArg(String name) { + Connector.Argument argument = (Connector.Argument)connectorArgs.get(name); + if (argument == null) { + return ""; + } + return argument.value(); + } + + public synchronized VirtualMachine vm() { + if (vm == null) { + throw new InternalError("VM not connected"); + } else { + return vm; + } + } + + boolean isOpen() { + return (vm != null); + } + + boolean isLaunch() { + return (connector instanceof LaunchingConnector); + } + + Connector connector() { + return connector; + } + + boolean isListen() { + return (connector instanceof ListeningConnector); + } + + boolean isAttach() { + return (connector instanceof AttachingConnector); + } + + private synchronized void notifyOutputComplete() { + outputCompleteCount++; + notifyAll(); + } + + private synchronized void waitOutputComplete() { + // Wait for stderr and stdout + if (process != null) { + while (outputCompleteCount < 2) { + try {wait();} catch (InterruptedException e) {} + } + } + } + + public void disposeVM() { + try { + if (vm != null) { + vm.dispose(); + vm = null; + } + } finally { + if (process != null) { + process.destroy(); + process = null; + } + waitOutputComplete(); + } + } + + private void dumpStream(InputStream stream) throws IOException { + PrintStream outStream = System.out; + BufferedReader in = + new BufferedReader(new InputStreamReader(stream)); + String line; + while ((line = in.readLine()) != null) { + outStream.println(line); + } + } + + /** + * Create a Thread that will retrieve and display any output. + * Needs to be high priority, else debugger may exit before + * it can be displayed. + */ + private void displayRemoteOutput(final InputStream stream) { + Thread thr = new Thread("output reader") { + public void run() { + try { + dumpStream(stream); + } catch (IOException ex) { + System.err.println("IOException reading output of child java interpreter:" + + ex.getMessage()); + } finally { + notifyOutputComplete(); + } + } + }; + thr.setPriority(Thread.MAX_PRIORITY-1); + thr.start(); + } + + private void dumpFailedLaunchInfo(Process process) { + try { + dumpStream(process.getErrorStream()); + dumpStream(process.getInputStream()); + } catch (IOException e) { + System.err.println("Unable to display process output: " + + e.getMessage()); + } + } + + /* launch child target vm */ + private VirtualMachine launchTarget() { + LaunchingConnector launcher = (LaunchingConnector)connector; + try { + VirtualMachine vm = launcher.launch(connectorArgs); + process = vm.process(); + displayRemoteOutput(process.getErrorStream()); + displayRemoteOutput(process.getInputStream()); + return vm; + } catch (IOException ioe) { + ioe.printStackTrace(); + System.err.println("\n Unable to launch target VM."); + } catch (IllegalConnectorArgumentsException icae) { + icae.printStackTrace(); + System.err.println("\n Internal debugger error."); + } catch (VMStartException vmse) { + System.err.println(vmse.getMessage() + "\n"); + dumpFailedLaunchInfo(vmse.process()); + System.err.println("\n Target VM failed to initialize."); + } + return null; // Shuts up the compiler + } + + /* attach to running target vm */ + private VirtualMachine attachTarget() { + AttachingConnector attacher = (AttachingConnector)connector; + try { + return attacher.attach(connectorArgs); + } catch (IOException ioe) { + ioe.printStackTrace(); + System.err.println("\n Unable to attach to target VM."); + } catch (IllegalConnectorArgumentsException icae) { + icae.printStackTrace(); + System.err.println("\n Internal debugger error."); + } + return null; // Shuts up the compiler + } + + /* listen for connection from target vm */ + private VirtualMachine listenTarget() { + ListeningConnector listener = (ListeningConnector)connector; + try { + String retAddress = listener.startListening(connectorArgs); + System.out.println("Listening at address: " + retAddress); + vm = listener.accept(connectorArgs); + listener.stopListening(connectorArgs); + return vm; + } catch (IOException ioe) { + ioe.printStackTrace(); + System.err.println("\n Unable to attach to target VM."); + } catch (IllegalConnectorArgumentsException icae) { + icae.printStackTrace(); + System.err.println("\n Internal debugger error."); + } + return null; // Shuts up the compiler + } +} diff --git a/hotspot/agent/test/jdi/jstack.sh b/hotspot/agent/test/jdi/jstack.sh new file mode 100644 index 00000000000..b11df413e16 --- /dev/null +++ b/hotspot/agent/test/jdi/jstack.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +OS=`uname` + +if [ "$OS" != "Linux" ]; then + OPTIONS="-Dsun.jvm.hotspot.debugger.useProcDebugger" +fi + +$JAVA_HOME/bin/java -showversion ${OPTIONS} -classpath $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.tools.StackTrace $* diff --git a/hotspot/agent/test/jdi/jstack64.sh b/hotspot/agent/test/jdi/jstack64.sh new file mode 100644 index 00000000000..e1019e2b53c --- /dev/null +++ b/hotspot/agent/test/jdi/jstack64.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +OPTIONS="-Dsun.jvm.hotspot.debugger.useProcDebugger" + +$JAVA_HOME/bin/java -d64 -showversion ${OPTIONS} -classpath $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.tools.StackTrace $* diff --git a/hotspot/agent/test/jdi/multivm.java b/hotspot/agent/test/jdi/multivm.java new file mode 100644 index 00000000000..3867648344a --- /dev/null +++ b/hotspot/agent/test/jdi/multivm.java @@ -0,0 +1,133 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import com.sun.jdi.*; +import com.sun.jdi.connect.*; + +import java.util.Map; +import java.util.List; +import java.util.Iterator; +import java.io.IOException; + +/* This class is used to test multi VM connectivity feature of + * SA/JDI. Accepts two PIDs as arguments. Connects to first VM + *, Connects to second VM and disposes them in that order. + */ + +public class multivm { + static AttachingConnector myPIDConn; + static VirtualMachine vm1; + static VirtualMachine vm2; + static VirtualMachineManager vmmgr; + + public static void println(String msg) { + System.out.println(msg); + } + + private static void usage() { + System.err.println("Usage: java multivm "); + System.exit(1); + } + + public static void main(String args[]) { + vmmgr = Bootstrap.virtualMachineManager(); + List attachingConnectors = vmmgr.attachingConnectors(); + if (attachingConnectors.isEmpty()) { + System.err.println( "ERROR: No attaching connectors"); + return; + } + Iterator myIt = attachingConnectors.iterator(); + while (myIt.hasNext()) { + AttachingConnector tmpCon = (AttachingConnector)myIt.next(); + if (tmpCon.name().equals( + "sun.jvm.hotspot.jdi.SAPIDAttachingConnector")) { + myPIDConn = tmpCon; + break; + } + } + + int pid1 = 0, pid2 = 0; + String pidText = null; + switch (args.length) { + case (2): + try { + pidText = args[0]; + pid1 = Integer.parseInt(pidText); + System.out.println( "pid1: " + pid1); + vm1 = attachPID(pid1); + pidText = args[1]; + pid2 = Integer.parseInt(pidText); + System.out.println( "pid2: " + pid2); + vm2 = attachPID(pid2); + } catch (NumberFormatException e) { + println(e.getMessage()); + usage(); + } + break; + default: + usage(); + } + + if (vm1 != null) { + System.out.println("vm1: attached ok!"); + System.out.println(vm1.version()); + sagdoit mine = new sagdoit(vm1); + mine.doAll(); + } + + if (vm2 != null) { + System.out.println("vm2: attached ok!"); + System.out.println(vm2.version()); + sagdoit mine = new sagdoit(vm2); + mine.doAll(); + } + + if (vm1 != null) { + vm1.dispose(); + } + + if (vm2 != null) { + vm2.dispose(); + } + } + + private static VirtualMachine attachPID(int pid) { + Map connArgs = myPIDConn.defaultArguments(); + System.out.println("connArgs = " + connArgs); + VirtualMachine vm; + Connector.StringArgument connArg = (Connector.StringArgument)connArgs.get("pid"); + connArg.setValue(Integer.toString(pid)); + + try { + vm = myPIDConn.attach(connArgs); + } catch (IOException ee) { + System.err.println("ERROR: myPIDConn.attach got IO Exception:" + ee); + vm = null; + } catch (IllegalConnectorArgumentsException ee) { + System.err.println("ERROR: myPIDConn.attach got illegal args exception:" + ee); + vm = null; + } + return vm; + } +} diff --git a/hotspot/agent/test/jdi/multivm.sh b/hotspot/agent/test/jdi/multivm.sh new file mode 100644 index 00000000000..70fa5b4e9a6 --- /dev/null +++ b/hotspot/agent/test/jdi/multivm.sh @@ -0,0 +1,58 @@ +#!/bin/ksh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +doUsage() +{ + cat < + +EOF +} + +if [ $# = 4 ] ; then + doUsage + exit 1 +fi + +jdk=$1 +javacp="$jdk/lib/sa-jdi.jar:$classesDir:$jdk/lib/tools.jar:$jdk/classes:./workdir" + +mkdir -p workdir +if [ sagdoit.java -nt ./workdir/sagdoit.class ] ; then + $jdk/bin/javac -d ./workdir -classpath $javacp sagdoit.java + if [ $? != 0 ] ; then + exit 1 + fi +fi +if [ multivm.java -nt ./workdir/multivm.class ] ; then + $jdk/bin/javac -d ./workdir -classpath $javacp multivm.java + if [ $? != 0 ] ; then + exit 1 + fi +fi + +$jdk/bin/java -Dsun.jvm.hotspot.jdi.ConnectorImpl.DEBUG -Dsun.jvm.hotspot.jdi.SAJDIClassLoader.DEBUG -Djava.class.path=$javacp multivm $2 $3 diff --git a/hotspot/agent/test/jdi/runjdb.sh b/hotspot/agent/test/jdi/runjdb.sh new file mode 100644 index 00000000000..719964c1c5a --- /dev/null +++ b/hotspot/agent/test/jdi/runjdb.sh @@ -0,0 +1,108 @@ +#!/bin/sh +# +# Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# jdb is a .c file that seems to discard the setting of CLASSPATH. +# So, we have to run jdb by calling java directly :-( + +# License file for development version of dbx +LM_LICENSE_FILE=7588@extend.eng:/usr/dist/local/config/sparcworks/license.dat:7588@setlicense +export LM_LICENSE_FILE + +doUsage() +{ + cat < /dev/null + if [ $? = 0 ] ; then + # it is a pid + args="$args $1" + echo "Error: A pid is not yet allowed" + exit 1 + else + # It is a core. + # We have to pass the name of the program that produced the + # core, and the core file itself. + args="$1" + fi + ;; + esac + shift +done + +if [ -z "$jdk" ] ; then + echo "Error: -jdk jdk-pathname is required" + exit 1 +fi +if [ -z "$sa" ] ; then + echo "Error: -sa sa-pathname is required" + exit 1 +fi + +if [ -z "$args" ] ; then + echo "Error: a core file or pid must be specified" + exit 1 +fi + +set -x +$jdk/bin/jdb -J-Xbootclasspath/a:$sa -connect \ + sun.jvm.hotspot.jdi.SACoreAttachingConnector:core=$args,javaExecutable=$jdk/bin/java + + +#$jdk/bin/java -Xbootclasspath/a:$mmm/ws/merlin-sa/build/agent \ +# com.sun.tools.example.debug.tty.TTY -connect \ +# sun.jvm.hotspot.jdi.SACoreAttachingConnector:core=sagcore,javaExecutable=$jdk/bin/java diff --git a/hotspot/agent/test/jdi/runjpda.sh b/hotspot/agent/test/jdi/runjpda.sh new file mode 100644 index 00000000000..3cd3474d8d1 --- /dev/null +++ b/hotspot/agent/test/jdi/runjpda.sh @@ -0,0 +1,133 @@ +#!/bin/ksh +# +# Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This script runs the test program, sagtest.java, with the regular +# JPDA jdi. +# It then starts up the debuggee part of the test, sagtarg.java, +# and calls gcore to create file sagcore for use in running +# the SA JDI client. + +set -x +# jdk is a jdk with the vm from the sa workspace +while [ $# != 0 ] ; do + case $1 in + -vv) + set -x + ;; + -gui) + theClass=sun.jvm.hotspot.HSDB + ;; + -jdk) + jdk=$2 + shift + ;; + -jdbx) + do=jdbx + ;; + -jdb) + do=jdb + ;; + -help | help) + doUsage + exit + ;; + -dontkill) + dontkill=true + ;; + -d64) + d64=-d64 + ;; + -*) + javaArgs="$javaArgs $1" + ;; + *) + echo "$1" | grep -s '^[0-9]*$' > /dev/null + if [ $? = 0 ] ; then + # it is a pid + args="$args $1" + else + # It is a core. + # We have to pass the name of the program that produced the + # core, and the core file itself. + args="$jdk/bin/java $1" + fi + ;; + esac + shift +done + +# First, run the sagtest.java with the regular JPDA jdi +workdir=./workdir +mkdir -p $workdir +CLASSPATH=$jdk/classes:$jdk/lib/tools.jar:$workdir +export CLASSPATH + +$jdk/bin/javac -g -source 1.5 -classpath $jdk/classes:$jdk/lib/tools.jar:$workdir -J-Xms40m -d $workdir \ + TestScaffold.java \ + VMConnection.java \ + TargetListener.java \ + TargetAdapter.java \ + sagdoit.java \ + sagtarg.java \ + sagtest.java + +if [ $? != 0 ] ; then + exit 1 +fi + +$jdk/bin/java $javaArgs -Dtest.classes=$workdir sagtest + +# Now run create a core file for use in running sa-jdi + +if [ ! core.satest -nt sagtarg.class ] ; then + tmp=/tmp/sagsetup + rm -f $tmp + $jdk/bin/java $d64 sagtarg > $tmp & + pid=$! + while [ ! -s $tmp ] ; do + # Kludge alert! + sleep 2 + done + #rm -f $tmp + + # force core dump of the debuggee + OS=`uname` + if [ "$OS" = "Linux" ]; then + # Linux does not have gcore command. Instead, we use 'gdb's + # gcore command. Note that only some versions of gdb support + # gdb command. + echo "gcore" > gdbscript + gdb -batch -p $pid -x gdbscript + rm -f gdbscript + else + gcore $* $pid + fi + mv core.$pid sagcore + + if [ "$dontkill" != "true" ]; then + kill -9 $pid + fi +fi + diff --git a/hotspot/agent/test/jdi/runsa.sh b/hotspot/agent/test/jdi/runsa.sh new file mode 100644 index 00000000000..23f09a6f98f --- /dev/null +++ b/hotspot/agent/test/jdi/runsa.sh @@ -0,0 +1,184 @@ +#!/bin/ksh +# +# Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + + +# This jdk must be hopper or better; it must have the +# SA connectors in VirtualMachineManagerImpl. +jdk=/java/re/jdk/1.4.1/promoted/latest/binaries/solaris-sparc +#jdk=/net/mmm/export/mmm/jdk1.4fcs.sa + +doUsage() +{ + cat <] [-jdb] [ -jdbx ] [ -d64 ] [ -remote ] [ pid | corefile | debugserver ] + + -jdk means to use that jdk. Default is 1.4.1/latest. + -jdbx means to run it under jdbx + -jdb means to connect using jdb instead of the sagclient program. + -remote debugserver means you want to connect to a remote debug server + + The corefile must have been produced by the same java as is running SA. + +EOF +} + +if [ $# = 0 ] ; then + doUsage + exit 1 +fi + +# License file for development version of dbx +#LM_LICENSE_FILE=7588@extend.eng:/usr/dist/local/config/sparcworks/license.dat:7588@setlicense +#export LM_LICENSE_FILE + +do= +args= +theClass=sagclient +javaArgs= + +while [ $# != 0 ] ; do + case $1 in + -vv) + set -x + ;; + -jdk) + jdk=$2 + shift + ;; + -jdbx) + do=jdbx + ;; + -jdb) + do=jdb + ;; + -help | help) + doUsage + exit + ;; + -d64) + d64=-d64 + ;; + -remote) + shift + args="$1" + do=remote + ;; + -*) + javaArgs="$javaArgs $1" + ;; + *) + echo "$1" | grep -s '^[0-9]*$' > /dev/null + if [ $? = 0 ] ; then + # it is a pid + args="$args $1" + else + # It is a core. + # We have to pass the name of the program that produced the + # core, and the core file itself. + args="$jdk/bin/java $1" + fi + ;; + esac + shift +done + +if [ -z "$jdk" ] ; then + error "--Error: runsa.sh: Must specify -jdk ." + error " Do runsa.sh -help for more info" + exit 1 +fi + +set -x +#setenv USE_LIBPROC_DEBUGGER "-Dsun.jvm.hotspot.debugger.useProcDebugger -Djava.library.path=$saprocdir" + +# If jjh makes this, then the classes are in .../build/agent. +# if someone else does, they are in . +classesDir=../../../../../../build/agent +if [ ! -r $classesDir ] ; then + classesDir=. + if [ ! -r $classesDir ] ; then + echo "-- Error: runsa.sh can't find the SA classes" + exit 1 + fi +fi +#javacp="/net/mmm/export/mmm/ws/sabaseline/build/solaris/solaris_sparc_compiler1/generated/sa-jdi.jar:$classesDir:$jdk/lib/tools.jar:$jdk/classes:./workdir" + +javacp="$jdk/lib/sa-jdi.jar:$classesDir:$jdk/lib/tools.jar:$jdk/classes:./workdir" + + +extraArgs="-showversion $javaArgs" +#extraArgs="-DdbxSvcAgentDSOPathName=/net/mmm/export/mmm/ws/m/b2/sa/src/os/solaris/agent/64bit/libsvc_agent_dbx.so $extraArgs" +#extraArgs="-DdbxSvcAgentDSOPathName=/net/jano.eng/export/disk05/hotspot/sa/solaris/sparcv9/lib/libsvc_agent_dbx.so $extraArgs" + +mkdir -p workdir +if [ sagclient.java -nt ./workdir/sagclient.class ] ; then + $jdk/bin/javac -d ./workdir -classpath $javacp sagclient.java + if [ $? != 0 ] ; then + exit 1 + fi +fi +if [ sagdoit.java -nt ./workdir/sagdoit.class ] ; then + $jdk/bin/javac -d ./workdir -classpath $javacp sagdoit.java + if [ $? != 0 ] ; then + exit 1 + fi +fi + +if [ "$do" = jdbx ] ; then + set -x + dbx=/net/sparcworks.eng/export/set/sparcworks2/dbx_70_nightly/dev/buildbin/Derived-sparc-S2-opt/bin/dbx + + # Have to do this export for jdbx to work. -cp and -classpath don't work. + CLASSPATH=$javacp + export CLASSPATH + #extraArgs="-Djava.class.path=$mhs/../sa/build/agent sun.jvm.hotspot.HSDB $*" + jvm_invocation="$jdk/bin/java -Xdebug \ + -Dsun.boot.class.path=$jdk/classes \ + $extraArgs" + #export jvm_invocation + + JAVASRCPATH=$mhs/../sa/src/share/vm/agent + export JAVASRCPATH + + #operand is pathname of .class file, eg ./jj.class. + echo run $args + clss=`echo $theClass | sed -e 's@\.@/@'` + if [ -r ./workdir/$clss.class ] ; then + # kludge for running sagclient + $dbx ./workdir/$clss.class + else + # kludge for running HSDB + $dbx $mhs/../sa/build/agent/$clss.class + fi +elif [ "$do" = jdb ] ; then + # This hasn't been tested. + $jdk/bin/jdb -J-Xbootclasspath/a:$classesDir -connect sun.jvm.hotspot.jdi.SACoreAttachingConnector:core=sagcore +elif [ "$do" = remote ] ; then + $jdk/bin/java $d64 -Djava.class.path=$javacp $extraArgs $theClass $args +else + $jdk/bin/java $d64 -Djava.class.path=$javacp $extraArgs $theClass $args + +fi diff --git a/hotspot/agent/test/jdi/sagclient.java b/hotspot/agent/test/jdi/sagclient.java new file mode 100644 index 00000000000..abde523be80 --- /dev/null +++ b/hotspot/agent/test/jdi/sagclient.java @@ -0,0 +1,167 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import com.sun.jdi.*; +import com.sun.jdi.connect.*; + +import java.util.Map; +import java.util.List; +import java.util.Iterator; +import java.io.IOException; + +public class sagclient { + static AttachingConnector myCoreConn; + static AttachingConnector myPIDConn; + static AttachingConnector myDbgSvrConn; + static VirtualMachine vm; + static VirtualMachineManager vmmgr; + + public static void println(String msg) { + System.out.println("jj: " + msg); + } + + + public static void main(String args[]) { + vmmgr = Bootstrap.virtualMachineManager(); + List attachingConnectors = vmmgr.attachingConnectors(); + if (attachingConnectors.isEmpty()) { + System.err.println( "ERROR: No attaching connectors"); + return; + } + Iterator myIt = attachingConnectors.iterator(); + while (myIt.hasNext()) { + AttachingConnector tmpCon = (AttachingConnector)myIt.next(); + if (tmpCon.name().equals( + "sun.jvm.hotspot.jdi.SACoreAttachingConnector")) { + myCoreConn = tmpCon; + } else if (tmpCon.name().equals( + "sun.jvm.hotspot.jdi.SAPIDAttachingConnector")) { + myPIDConn = tmpCon; + } else if (tmpCon.name().equals( + "sun.jvm.hotspot.jdi.SADebugServerAttachingConnector")) { + myDbgSvrConn = tmpCon; + } + } + String execPath = null; + String pidText = null; + String coreFilename = null; + String debugServer = null; + int pid = 0; + switch (args.length) { + case (0): + break; + case (1): + // If all numbers, it is a PID to attach to + // Else, it is a pathname to a .../bin/java for a core file. + try { + pidText = args[0]; + pid = Integer.parseInt(pidText); + System.out.println( "pid: " + pid); + vm = attachPID(pid); + } catch (NumberFormatException e) { + System.out.println("trying remote server .."); + debugServer = args[0]; + System.out.println( "remote server: " + debugServer); + vm = attachDebugServer(debugServer); + } + break; + + case (2): + execPath = args[0]; + coreFilename = args[1]; + System.out.println( "jdk: " + execPath); + System.out.println( "core: " + coreFilename); + vm = attachCore(coreFilename, execPath); + break; + } + + + if (vm != null) { + System.out.println("sagclient: attached ok!"); + sagdoit mine = new sagdoit(vm); + mine.doAll(); + vm.dispose(); + } + } + + private static VirtualMachine attachCore(String coreFilename, String execPath) { + Map connArgs = myCoreConn.defaultArguments(); + System.out.println("connArgs = " + connArgs); + VirtualMachine vm; + Connector.StringArgument connArg = (Connector.StringArgument)connArgs.get("core"); + connArg.setValue(coreFilename); + + connArg = (Connector.StringArgument)connArgs.get("javaExecutable"); + connArg.setValue(execPath); + try { + vm = myCoreConn.attach(connArgs); + } catch (IOException ee) { + System.err.println("ERROR: myCoreConn.attach got IO Exception:" + ee); + vm = null; + } catch (IllegalConnectorArgumentsException ee) { + System.err.println("ERROR: myCoreConn.attach got illegal args exception:" + ee); + vm = null; + } + return vm; + } + + private static VirtualMachine attachPID(int pid) { + Map connArgs = myPIDConn.defaultArguments(); + System.out.println("connArgs = " + connArgs); + VirtualMachine vm; + Connector.StringArgument connArg = (Connector.StringArgument)connArgs.get("pid"); + connArg.setValue(Integer.toString(pid)); + + try { + vm = myPIDConn.attach(connArgs); + } catch (IOException ee) { + System.err.println("ERROR: myPIDConn.attach got IO Exception:" + ee); + vm = null; + } catch (IllegalConnectorArgumentsException ee) { + System.err.println("ERROR: myPIDConn.attach got illegal args exception:" + ee); + vm = null; + } + return vm; + } + + + private static VirtualMachine attachDebugServer(String debugServer) { + Map connArgs = myDbgSvrConn.defaultArguments(); + System.out.println("connArgs = " + connArgs); + VirtualMachine vm; + Connector.StringArgument connArg = (Connector.StringArgument)connArgs.get("debugServerName"); + connArg.setValue(debugServer); + + try { + vm = myDbgSvrConn.attach(connArgs); + } catch (IOException ee) { + System.err.println("ERROR: myDbgSvrConn.attach got IO Exception:" + ee); + vm = null; + } catch (IllegalConnectorArgumentsException ee) { + System.err.println("ERROR: myDbgSvrConn.attach got illegal args exception:" + ee); + vm = null; + } + return vm; + } +} diff --git a/hotspot/agent/test/jdi/sagdoit.java b/hotspot/agent/test/jdi/sagdoit.java new file mode 100644 index 00000000000..c9cfc7f071e --- /dev/null +++ b/hotspot/agent/test/jdi/sagdoit.java @@ -0,0 +1,329 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import com.sun.jdi.*; +import java.util.*; + + +// This just contains a bunch of methods that call various JDI methods. +// It is called from the sagtest.java jtreg test to get this info for the standard +// JDI and from the sagclient.java test to get this info for the SA JDI. + +class comparator implements Comparator { + + public int compare(Object o1, Object o2) { + ReferenceType rt1 = (ReferenceType)o1; + ReferenceType rt2 = (ReferenceType)o2; + return rt1.signature().compareTo(rt2.signature()); + } + + public boolean equals(Object oo) { + return false; + } +} + +public class sagdoit { + + VirtualMachine myVm; + int margin = 0; + static String blanks = " "; + static int nblanks = blanks.length(); + + sagdoit(VirtualMachine vm) { + super(); + myVm = vm; + } + + void indent(int count) { + margin += count; + } + + void pp(String msg) { + System.out.println(blanks.substring(nblanks - margin) + msg); + } + + public void doAll() { + doThreadGroups(); + //System.out.println("NOTE: dumping of class info is disabled in sagdoit.java"); + //System.out.println(" just to keep the output small while working on objects"); + doClasses(); //fixme jj: uncomment this to see all class info + + } + public void doThreadGroups() { + doThreadGroupList(myVm.topLevelThreadGroups()); + } + + private void doThreadGroupList(List groups) { + // sort; need a comparator + if (groups == null) { + return; + } + + Iterator myIter = groups.iterator(); + while(myIter.hasNext()) { + ThreadGroupReference aGroup = (ThreadGroupReference)myIter.next(); + doOneThreadGroup(aGroup); + } + + } + + public void doOneThreadGroup(ThreadGroupReference xx) { + pp("threadGroup:" + xx.name()); + indent(4); + pp("parent() = " + xx.parent()); + pp("threads:"); + indent(4); + doThreadList(xx.threads()); + indent(-4); + pp("threadGroups:"); + indent(4); + doThreadGroupList(xx.threadGroups()); + indent(-4); + indent(-4); + } + + public void doThreads() { + doThreadList(myVm.allThreads()); + } + + public void doThreadList(List threads) { + if (threads == null) { + return; + } + Iterator myIter = threads.iterator(); + while(myIter.hasNext()) { + ThreadReference aThread = (ThreadReference)myIter.next(); + doOneThread(aThread); + } + } + + public void doOneThread(ThreadReference xx) { + pp("Thread: " + xx.name()); + indent(4); + pp("suspendCount() = " + xx.suspendCount()); + + //void stop(ObjectReference throwable) throws InvalidTypeException; + //void interrupt(); + pp("status() = " + xx.status()); + pp("isSuspended() = " + xx.isSuspended()); + pp("isAtBreakpoint() = " + xx.isAtBreakpoint()); + + pp("threadGroup() = " + xx.threadGroup()); + indent(-4); + + indent(4); + try { + List allFrames = xx.frames(); + for (int ii = 0; ii < xx.frameCount(); ii++) { + StackFrame oneFrame = xx.frame(ii); + pp("frame(" + ii + ") = " + oneFrame); + doOneFrame(oneFrame); + } + //List frames(int start, int length) throws IncompatibleThreadStateException; + // unsupported List allMonitors = xx.ownedMonitors(); + // unsupported pp("currentContendedMonitor() = " + xx.currentContendedMonitor()); + } catch (IncompatibleThreadStateException ee) { + pp("GOT IncompatibleThreadStateException: " + ee); + } + indent(-4); + } + + public void doOneFrame(StackFrame frame) { + + List localVars = null; + try { + localVars = frame.visibleVariables(); + } catch (AbsentInformationException ee) { + // we compile with -g so this shouldn't happen + return; + } + indent(4); + for (Iterator it = localVars.iterator(); it.hasNext();) { + LocalVariable lv = (LocalVariable) it.next(); + pp("lv name = " + lv.name() + + ", type = " + lv.typeName() + + ", sig = " + lv.signature() + + ", gsig = " + lv.genericSignature() + + ", isVis = " + lv.isVisible(frame) + + ", isArg = " + lv.isArgument()); + } + indent(-4); + } + + public void doClasses() { + List myClasses = myVm.allClasses(); + myClasses = new ArrayList(myClasses); + Collections.sort(myClasses, new comparator()); + for (int ii = 0; ii < myClasses.size(); ii++) { + // Spec says each is a ReferenceType + //System.out.println("class " + (ii + 1) + " is " + myClasses.get(ii)); + ReferenceType aClass = (ReferenceType)myClasses.get(ii); + System.out.println("class " + (ii + 1) + " is " + aClass.signature()); + doOneClass(aClass); + // Uncomment this to just do a few classes. + //if ( ii > 4) break; + } + } + + public void doOneClass(ReferenceType xx) { + indent(5); + // inherited from Mirror + pp("toString() = " + xx.toString()); + pp("virtualMachine() = " + xx.virtualMachine()); + + // inherited from Type + pp("name() = " + xx.name()); + pp("signature() = " + xx.signature()); + + // ReferenceType fields + doReferenceTypeFields(xx); + + + + + + String className = xx.getClass().getName(); + pp("subclass = " + className); + + Class referenceType = null; + Class arrayType = null; + Class classType = null; + Class interfaceType = null; + + try { + referenceType = Class.forName("com.sun.jdi.ReferenceType"); + arrayType = Class.forName("com.sun.jdi.ArrayType"); + interfaceType = Class.forName("com.sun.jdi.InterfaceType"); + classType = Class.forName("com.sun.jdi.ClassType"); + } catch (ClassNotFoundException ee) { + } + + + if (referenceType.isInstance(xx)) { + pp("ReferenceType fields"); + ReferenceType rr = (ReferenceType)xx; + + if (arrayType.isInstance(xx)) { + pp("ArrayType fields"); + } + + if (classType.isInstance(xx)) { + pp("ClassType fields"); + } + + if (interfaceType.isInstance(xx)) { + pp("InterfaceType fields"); + } + } + indent(-5); + + } + + + public void doReferenceTypeFields(ReferenceType xx) { + Object zz; + pp("classLoader() = " + xx.classLoader()); + try {zz =xx.sourceName();} catch(AbsentInformationException ee) { zz = ee;} pp("sourceName() = " + zz); + try {zz =xx.sourceNames("stratum");} catch(AbsentInformationException ee) { zz = ee;} pp("sourceNames() = " + zz); + try {zz =xx.sourcePaths("stratum");} catch(AbsentInformationException ee) { zz = ee;} pp("sourcePaths() = " + zz); + //try {zz =xx.sourceDebugExtension();} catch(AbsentInformationException ee) { zz = ee;} pp("sourceDebugExtension() = " + zz); + //fixme: jj; should sourceDebugExtension throw UnsupportedOperationException? + try {zz =xx.sourceDebugExtension();} catch(Exception ee) { zz = ee;} pp("sourceDebugExtension() = " + zz); + // If xx is an array, this can cause a ClassNotLoadedException on the + // component type. Is that a JDI bug? + pp("isStatic() = " + xx.isStatic()); + pp("isAbstract() = " + xx.isAbstract()); + pp("isFinal() = " + xx.isFinal()); + pp("isPrepared() = " + xx.isPrepared()); + pp("isVerified() = " + xx.isVerified()); + pp("isInitialized() = " + xx.isInitialized()); + pp("failedToInitialize() = " + xx.failedToInitialize()); + pp("fields() = " + xx.fields()); + pp("visibleFields() = " + xx.visibleFields()); + pp("allFields() = " + xx.allFields()); + pp("fieldByName(String fieldName) = " + xx.fieldByName("fieldName")); + pp("methods() = " + xx.methods()); + + + List meths = xx.methods(); + Iterator iter = meths.iterator(); + while (iter.hasNext()) { + Method mm = (Method)iter.next(); + pp(" name/sig:" + mm.name() + "/" + mm.signature()); + } + + pp(" visibleMethods() = " + xx.visibleMethods()); + + //if (1 == 1) return; + + pp("allMethods() = " + xx.allMethods()); + + + pp("methodsByName(String name) = " + xx.methodsByName("name")); + pp("methodsByName(String name, String signature) = " + xx.methodsByName("name", "signature")); + pp("nestedTypes() = " + xx.nestedTypes()); + //pp("getValue(Field field) = " + xx.getValue("field")); + pp("getValue(Field field) = " + "fixme: jjh"); + //pp("getValues(List fields) = " + xx.getValues(new List[] = {"fields"})); + pp("getValues(List fields) = " + "fixme: jjh"); + pp("classObject() = " + xx.classObject()); + //x pp("allLineLocations() = " + xx.allLineLocations()); + //x pp("allLineLocations(String stratum, String sourceName) = " + xx.allLineLocations("stratum", "sourceName")); + //x pp("locationsOfLine(int lineNumber) = " + xx.locationsOfLine(89)); + //x pp("locationsOfLine(String stratum, String sourceName, int lineNumber) = " + xx.locationsOfLine("stratum", "sourceName", 89)); + pp("availableStrata() = " + xx.availableStrata()); + pp("defaultStratum() = " + xx.defaultStratum()); + pp("equals(Object obj) = " + xx.equals(xx)); + pp("hashCode() = " + xx.hashCode()); + } + +} + +// try { +// ReferenceType rr = (ReferenceType)xx; +// pp("ReferenceType fields"); + +// try { +// ArrayType ff = (ArrayType)xx; +// pp("ArrayType fields"); + +// } catch(ClassCastException ee) { +// } + +// try { +// ClassType ff = (ClassType)xx; +// pp("ClassType fields"); + +// } catch(ClassCastException ee) { +// } + +// try { +// InterfaceType ff = (InterfaceType)xx; +// pp("InterfaceType fields"); + +// } catch(ClassCastException ee) { +// } + +// } catch(ClassCastException ee) { +// } diff --git a/hotspot/agent/test/jdi/sagtarg.java b/hotspot/agent/test/jdi/sagtarg.java new file mode 100644 index 00000000000..b1f77d8e515 --- /dev/null +++ b/hotspot/agent/test/jdi/sagtarg.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This is the target debuggee for sagtest.java. +// It just waits which lets the test call all the JDI +// methods on it. + +import java.util.List; + +interface MyInterface { + public void myMethod(); +} + + +abstract class MySuper implements MyInterface { +} + +class sagtarg extends MySuper { + public static void main(String[] args){ + String stringVar = "localVar1"; + int intVar = 89; + List genVar = null; + System.out.println("Howdy!"); + String myStr = ""; + synchronized(myStr) { + try { + myStr.wait(); + } catch (InterruptedException ee) { + } + } + System.out.println("Goodbye from sagtarg!"); + } + + public void myMethod() { + } +} diff --git a/hotspot/agent/test/jdi/sagtest.java b/hotspot/agent/test/jdi/sagtest.java new file mode 100644 index 00000000000..7dda2c60769 --- /dev/null +++ b/hotspot/agent/test/jdi/sagtest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/** + * @test * @bug 0000000 + * @summary This is just an exercise of various JDI elements for use in + * testing the SA/JDI client + * + * @author jjh + * + * @run build TestScaffold VMConnection TargetListener TargetAdapter sagdoit + * @run compile -g -source 1.5 sagtarg.java + * @run main sagtest + */ +import com.sun.jdi.*; +import com.sun.jdi.event.*; +import com.sun.jdi.request.*; + +import java.util.*; + + /********** target program **********/ + +// The target program is sagtarg.java + + /********** test program **********/ + +public class sagtest extends TestScaffold { + ReferenceType targetClass; + ThreadReference mainThread; + + sagtest (String args[]) { + super(args); + } + + public static void main(String[] args) throws Exception { + new sagtest(args).startTests(); + } + + /********** event handlers **********/ + + + /********** test core **********/ + + protected void runTests() throws Exception { + /* + * Get to the top of main() + * to determine targetClass and mainThread + */ + BreakpointEvent bpe = startToMain("sagtarg"); + targetClass = bpe.location().declaringType(); + mainThread = bpe.thread(); + EventRequestManager erm = vm().eventRequestManager(); + stepOverLine(mainThread); //stop on 18 + stepOverLine(mainThread); //stop on 19 + stepOverLine(mainThread); //stop on 20 + stepOverLine(mainThread); //stop on 21 + stepOverLine(mainThread); //stop on 22 + + sagdoit mine = new sagdoit(vm()); + mine.doAll(); + + if (!testFailed) { + println("sagtest: passed"); + } else { + throw new Exception("sagtest: failed"); + } + } +} diff --git a/hotspot/agent/test/jdi/sasanity.sh b/hotspot/agent/test/jdi/sasanity.sh new file mode 100644 index 00000000000..ac79cced427 --- /dev/null +++ b/hotspot/agent/test/jdi/sasanity.sh @@ -0,0 +1,79 @@ +#!/bin/ksh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This script is used to run sanity check on vmStructs. +# Each SA class is checked against a given VM. "PASSED" is +# printed if vmStructs are consistent. Else, "FAILED" is +# printed and an exception stack trace follows. + +usage() { + echo "usage: ./sasanity.sh " + echo " is the 1.5 j2se directory against which you want to run sanity check" + exit 1 +} + +if [ "$1" == "" ]; then + usage +fi + +if [ "$1" == "-help" ]; then + usage +fi + +jdk=$1 +OS=`uname` + +if [ "$OS" != "Linux" ]; then + OPTIONS="-Dsun.jvm.hotspot.debugger.useProcDebugger" +fi + +javacp=$jdk/lib/sa-jdi.jar:./workdir + +mkdir -p workdir +if [ SASanityChecker.java -nt ./workdir/SASanityChecker.class ] ; then + $jdk/bin/javac -d ./workdir -classpath $javacp SASanityChecker.java + if [ $? != 0 ] ; then + exit 1 + fi +fi + +if [ sagtarg.java -nt ./workdir/sagtarg.class ]; then + $jdk/bin/javac -g -classpath -d $workdir sagtarg.java + if [ $? != 0 ] ; then + exit 1 + fi +fi + +tmp=/tmp/sagsetup +rm -f $tmp +$jdk/bin/java sagtarg > $tmp & +pid=$! +while [ ! -s $tmp ] ; do + # Kludge alert! + sleep 2 +done + +$jdk/bin/java -showversion ${OPTIONS} -classpath $javacp SASanityChecker $pid +kill -9 $pid diff --git a/hotspot/agent/test/jdi/serialvm.java b/hotspot/agent/test/jdi/serialvm.java new file mode 100644 index 00000000000..bb66afe481f --- /dev/null +++ b/hotspot/agent/test/jdi/serialvm.java @@ -0,0 +1,137 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import com.sun.jdi.*; +import com.sun.jdi.connect.*; + +import java.util.Map; +import java.util.List; +import java.util.Iterator; +import java.io.IOException; + + +/* This class is used to test multi VM connectivity feature of + * SA/JDI. Accepts two PIDs as arguments. Connects to first VM + *, disposes it, connects to second VM, disposes second VM. + */ + + +public class serialvm { + static AttachingConnector myPIDConn; + static VirtualMachine vm1; + static VirtualMachine vm2; + static VirtualMachineManager vmmgr; + + public static void println(String msg) { + System.out.println(msg); + } + + private static void usage() { + System.err.println("Usage: java serialvm "); + System.exit(1); + } + + public static void main(String args[]) { + vmmgr = Bootstrap.virtualMachineManager(); + List attachingConnectors = vmmgr.attachingConnectors(); + if (attachingConnectors.isEmpty()) { + System.err.println( "ERROR: No attaching connectors"); + return; + } + Iterator myIt = attachingConnectors.iterator(); + while (myIt.hasNext()) { + AttachingConnector tmpCon = (AttachingConnector)myIt.next(); + if (tmpCon.name().equals( + "sun.jvm.hotspot.jdi.SAPIDAttachingConnector")) { + myPIDConn = tmpCon; + break; + } + } + + int pid1 = 0, pid2 = 0; + String pidText = null; + switch (args.length) { + case (2): + try { + pidText = args[0]; + pid1 = Integer.parseInt(pidText); + System.out.println( "pid1: " + pid1); + pidText = args[1]; + pid2 = Integer.parseInt(pidText); + System.out.println( "pid2: " + pid2); + } catch (NumberFormatException e) { + println(e.getMessage()); + usage(); + } + break; + default: + usage(); + } + + // attach, dispose, attach2, dispose2 pattern + // as opposed to attach1, attach2, dispose1, dispose2 + vm1 = attachPID(pid1); + if (vm1 != null) { + System.out.println("vm1: attached ok!"); + System.out.println(vm1.version()); + sagdoit mine = new sagdoit(vm1); + mine.doAll(); + } + if (vm1 != null) { + vm1.dispose(); + } + + vm2 = attachPID(pid2); + if (vm2 != null) { + System.out.println("vm2: attached ok!"); + System.out.println(vm2.version()); + sagdoit mine = new sagdoit(vm2); + mine.doAll(); + } + + + if (vm2 != null) { + vm2.dispose(); + } + } + + private static VirtualMachine attachPID(int pid) { + Map connArgs = myPIDConn.defaultArguments(); + System.out.println("connArgs = " + connArgs); + VirtualMachine vm; + Connector.StringArgument connArg = (Connector.StringArgument)connArgs.get("pid"); + connArg.setValue(Integer.toString(pid)); + + try { + vm = myPIDConn.attach(connArgs); + } catch (IOException ee) { + System.err.println("ERROR: myPIDConn.attach got IO Exception:" + ee); + vm = null; + } catch (IllegalConnectorArgumentsException ee) { + System.err.println("ERROR: myPIDConn.attach got illegal args exception:" + ee); + vm = null; + } + return vm; + } +} diff --git a/hotspot/agent/test/jdi/serialvm.sh b/hotspot/agent/test/jdi/serialvm.sh new file mode 100644 index 00000000000..f88b7c033fe --- /dev/null +++ b/hotspot/agent/test/jdi/serialvm.sh @@ -0,0 +1,58 @@ +#!/bin/ksh +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +doUsage() +{ + cat < + +EOF +} + +if [ $# = 4 ] ; then + doUsage + exit 1 +fi + +jdk=$1 +javacp="$jdk/lib/sa-jdi.jar:$classesDir:$jdk/lib/tools.jar:$jdk/classes:./workdir" + +mkdir -p workdir +if [ sagdoit.java -nt ./workdir/sagdoit.class ] ; then + $jdk/bin/javac -d ./workdir -classpath $javacp sagdoit.java + if [ $? != 0 ] ; then + exit 1 + fi +fi +if [ serialvm.java -nt ./workdir/serialvm.class ] ; then + $jdk/bin/javac -d ./workdir -classpath $javacp serialvm.java + if [ $? != 0 ] ; then + exit 1 + fi +fi + +$jdk/bin/java -Dsun.jvm.hotspot.jdi.ConnectorImpl.DEBUG -Dsun.jvm.hotspot.jdi.SAJDIClassLoader.DEBUG -Djava.class.path=$javacp serialvm $2 $3 diff --git a/hotspot/agent/test/libproc/LibprocClient.java b/hotspot/agent/test/libproc/LibprocClient.java new file mode 100644 index 00000000000..37679173254 --- /dev/null +++ b/hotspot/agent/test/libproc/LibprocClient.java @@ -0,0 +1,164 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.tools.*; +import sun.jvm.hotspot.utilities.*; + +/** + We don't run any of the "standard" SA command line tools for sanity + check. This is because the standard tools print addresses in hex + which could change legally. Also, textual comparison of output may + not match because of other reasons as well. This tool checks + validity of threads and frames logically. This class has reference + frame names from "known" threads. The debuggee is assumed to run + "LibprocTest.java". +*/ + +public class LibprocClient extends Tool { + + public void run() { + // try to get VM version and check + String version = VM.getVM().getVMRelease(); + Assert.that(version.startsWith("1.5"), "1.5 expected"); + + // try getting threads + Threads threads = VM.getVM().getThreads(); + boolean mainTested = false; + + // check frames of each thread + for (JavaThread cur = threads.first(); cur != null; cur = cur.next()) { + if (cur.isJavaThread()) { + String name = cur.getThreadName(); + // testing of basic frame walking for all threads + for (JavaVFrame vf = getLastJavaVFrame(cur); vf != null; vf = vf.javaSender()) { + checkFrame(vf); + } + + // special testing for "known" threads. For now, only "main" thread. + if (name.equals("main")) { + checkMainThread(cur); + mainTested = true; + } + } + } + Assert.that(mainTested, "main thread missing"); + } + + public static void main(String[] args) { + try { + LibprocClient lc = new LibprocClient(); + lc.start(args); + lc.getAgent().detach(); + System.out.println("\nPASSED\n"); + } catch (Exception exp) { + System.out.println("\nFAILED\n"); + exp.printStackTrace(); + } + } + + // -- Internals only below this point + private static JavaVFrame getLastJavaVFrame(JavaThread cur) { + RegisterMap regMap = cur.newRegisterMap(true); + Frame f = cur.getCurrentFrameGuess(); + if (f == null) { + System.err.println(" (Unable to get a top most frame)"); + return null; + } + VFrame vf = VFrame.newVFrame(f, regMap, cur, true, true); + if (vf == null) { + System.err.println(" (Unable to create vframe for topmost frame guess)"); + return null; + } + if (vf.isJavaFrame()) { + return (JavaVFrame) vf; + } + return (JavaVFrame) vf.javaSender(); + } + + private void checkMethodSignature(Symbol sig) { + SignatureIterator itr = new SignatureIterator(sig) { + public void doBool () {} + public void doChar () {} + public void doFloat () {} + public void doDouble() {} + public void doByte () {} + public void doShort () {} + public void doInt () {} + public void doLong () {} + public void doVoid () {} + public void doObject(int begin, int end) {} + public void doArray (int begin, int end) {} + }; + // this will throw RuntimeException for any invalid item in signature. + itr.iterate(); + } + + private void checkBCI(Method m, int bci) { + if (! m.isNative()) { + byte[] buf = m.getByteCode(); + Assert.that(bci >= 0 && bci < buf.length, "invalid bci, not in code range"); + if (m.hasLineNumberTable()) { + int lineNum = m.getLineNumberFromBCI(bci); + Assert.that(lineNum >= 0, "expecting non-negative line number"); + } + } + } + + private void checkMethodHolder(Method method) { + Klass klass = method.getMethodHolder(); + Assert.that(klass != null, "expecting non-null instance klass"); + } + + private void checkFrame(JavaVFrame vf) { + Method method = vf.getMethod(); + Assert.that(method != null, "expecting a non-null method here"); + Assert.that(method.getName() != null, "expecting non-null method name"); + checkMethodHolder(method); + checkMethodSignature(method.getSignature()); + checkBCI(method, vf.getBCI()); + } + + // from the test case LibprocTest.java - in the main thread we + // should see frames as below + private static String[] mainThreadMethods = new String[] { + "java.lang.Object.wait(long)", + "java.lang.Object.wait()", + "LibprocTest.main(java.lang.String[])" + }; + + private void checkMainThread(JavaThread thread) { + checkFrames(thread, mainThreadMethods); + } + + private void checkFrames(JavaThread thread, String[] expectedMethodNames) { + int i = 0; + for (JavaVFrame vf = getLastJavaVFrame(thread); vf != null; vf = vf.javaSender(), i++) { + Method m = vf.getMethod(); + Assert.that(m.externalNameAndSignature().equals(expectedMethodNames[i]), + "expected frame missing"); + } + } +} diff --git a/hotspot/agent/test/libproc/LibprocTest.java b/hotspot/agent/test/libproc/LibprocTest.java new file mode 100644 index 00000000000..f67d8db23e6 --- /dev/null +++ b/hotspot/agent/test/libproc/LibprocTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/** + This is test case run by debuggee for running LibprocClient.java. +*/ + +public class LibprocTest { + public static void main(String[] args) throws Exception { + String myStr = ""; + System.out.println("main start"); + synchronized(myStr) { + try { + myStr.wait(); + } catch (InterruptedException ee) { + } + } + System.out.println("main end"); + } +} diff --git a/hotspot/agent/test/libproc/Makefile b/hotspot/agent/test/libproc/Makefile new file mode 100644 index 00000000000..0a07ad52f2a --- /dev/null +++ b/hotspot/agent/test/libproc/Makefile @@ -0,0 +1,30 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +all: + javac LibprocTest.java + javac -classpath ../../build/classes LibprocClient.java + +clean: + rm -rf *.class diff --git a/hotspot/agent/test/libproc/README b/hotspot/agent/test/libproc/README new file mode 100644 index 00000000000..df2729d9fb5 --- /dev/null +++ b/hotspot/agent/test/libproc/README @@ -0,0 +1,42 @@ +# +# Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +After making any changes to libproc.so, the shell scripts described +below can be run to verify that it does not break Java Serviceability +Agent. + +Setup: + +You need to have jdk 1.5 installed to run this test. Set environment +variable SA_JAVA to point to the java executable of jdk +1.5. Otherwise, the script picks-up 'java' from PATH. + +Running the tests: + +run libproctest.sh (32-bit debuggee) and libproctest64.sh (64-bit +debuggee) + +Interpreting result: + +"PASSED" or "FAILED" is printed in standard output. diff --git a/hotspot/agent/test/libproc/libproctest.sh b/hotspot/agent/test/libproc/libproctest.sh new file mode 100644 index 00000000000..30f9bfbf376 --- /dev/null +++ b/hotspot/agent/test/libproc/libproctest.sh @@ -0,0 +1,68 @@ +#!/bin/ksh + +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This script is used to run consistency check of Serviceabilty Agent +# after making any libproc.so changes. Prints "PASSED" or "FAILED" in +# standard output. + +usage() { + echo "usage: $0" + echo " set SA_JAVA to be java executable from JDK 1.5" + exit 1 +} + +STARTDIR=`dirname $0` + +if [ "$1" == "-help" ]; then + usage +fi + +if [ "x$SA_JAVA" = "x" ]; then + SA_JAVA=java +fi + +# create java process with test case +tmp=/tmp/libproctest +rm -f $tmp +$SA_JAVA -classpath $STARTDIR LibprocTest > $tmp & +pid=$! +while [ ! -s $tmp ] ; do + # Kludge alert! + sleep 2 +done + +# dump core +gcore $pid +kill -9 $pid + + +OPTIONS="-Djava.library.path=$STARTDIR/../src/os/solaris/proc/`uname -p`:$STARTDIR/../solaris/`uname -p` -Dsun.jvm.hotspot.debugger.useProcDebugger" + +# run libproc client +$SA_JAVA -showversion ${OPTIONS} -cp $STARTDIR/../../build/classes::$STARTDIR/../sa.jar:$STARTDIR LibprocClient x core.$pid + +# delete core +rm -f core.$pid diff --git a/hotspot/agent/test/libproc/libproctest64.sh b/hotspot/agent/test/libproc/libproctest64.sh new file mode 100644 index 00000000000..75e0f6987be --- /dev/null +++ b/hotspot/agent/test/libproc/libproctest64.sh @@ -0,0 +1,67 @@ +#!/bin/ksh + +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This script is used to run consistency check of Serviceabilty Agent +# after making any libproc.so changes. Prints "PASSED" or "FAILED" in +# standard output. + +usage() { + echo "usage: $0" + echo " set SA_JAVA to be the java executable from JDK 1.5" + exit 1 +} + +if [ "$1" == "-help" ]; then + usage +fi + +if [ "x$SA_JAVA" = "x" ]; then + SA_JAVA=java +fi + +STARTDIR=`dirname $0` + +# create java process with test case +tmp=/tmp/libproctest +rm -f $tmp +$SA_JAVA -d64 -classpath $STARTDIR LibprocTest > $tmp & +pid=$! +while [ ! -s $tmp ] ; do + # Kludge alert! + sleep 2 +done + +# dump core +gcore $pid +kill -9 $pid + +OPTIONS="-Djava.library.path=$STARTDIR/../src/os/solaris/proc/sparcv9:$STARTDIR/../solaris/sparcv9 -Dsun.jvm.hotspot.debugger.useProcDebugger" + +# run libproc client +$SA_JAVA -d64 -showversion ${OPTIONS} -cp $STARTDIR/../../build/classes::$STARTDIR/../sa.jar:$STARTDIR LibprocClient x core.$pid + +# delete core +rm -f core.$pid diff --git a/hotspot/build/hotspot_distro b/hotspot/build/hotspot_distro new file mode 100644 index 00000000000..5eb900d9fcb --- /dev/null +++ b/hotspot/build/hotspot_distro @@ -0,0 +1,32 @@ +# +# Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# +# This file format must remain compatible with both +# GNU Makefile and Microsoft nmake formats. +# + +# Don't put quotes (fail windows build). +HOTSPOT_VM_DISTRO=OpenJDK +COMPANY_NAME= +PRODUCT_NAME=OpenJDK diff --git a/hotspot/build/linux/Makefile b/hotspot/build/linux/Makefile new file mode 100644 index 00000000000..4b595a50ffc --- /dev/null +++ b/hotspot/build/linux/Makefile @@ -0,0 +1,307 @@ +# +# Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This makefile creates a build tree and lights off a build. +# You can go back into the build tree and perform rebuilds or +# incremental builds as desired. Be sure to reestablish +# environment variable settings for LD_LIBRARY_PATH and JAVA_HOME. + +# The make process now relies on java and javac. These can be +# specified either implicitly on the PATH, by setting the +# (JDK-inherited) ALT_BOOTDIR environment variable to full path to a +# JDK in which bin/java and bin/javac are present and working (e.g., +# /usr/local/java/jdk1.3/solaris), or via the (JDK-inherited) +# default BOOTDIR path value. Note that one of ALT_BOOTDIR +# or BOOTDIR has to be set. We do *not* search javac, javah, rmic etc. +# from the PATH. +# +# One can set ALT_BOOTDIR or BOOTDIR to point to a jdk that runs on +# an architecture that differs from the target architecture, as long +# as the bootstrap jdk runs under the same flavor of OS as the target +# (i.e., if the target is linux, point to a jdk that runs on a linux +# box). In order to use such a bootstrap jdk, set the make variable +# REMOTE to the desired remote command mechanism, e.g., +# +# make REMOTE="rsh -l me myotherlinuxbox" + +# Along with VM, Serviceability Agent (SA) is built for SA/JDI binding. +# JDI binding on SA produces two binaries: +# 1. sa-jdi.jar - This is build before building libjvm[_g].so +# Please refer to ./makefiles/sa.make +# 2. libsa[_g].so - Native library for SA - This is built after +# libjsig[_g].so (signal interposition library) +# Please refer to ./makefiles/vm.make +# If $(GAMMADIR)/agent dir is not present, SA components are not built. + +ifeq ($(GAMMADIR),) +include ../../make/defs.make +else +include $(GAMMADIR)/make/defs.make +endif +include $(GAMMADIR)/build/$(OSNAME)/makefiles/rules.make + +ifndef LP64 +ifndef CC_INTERP +FORCE_TIERED=1 +endif +endif + +ifdef LP64 + ifeq ("$(filter $(LP64_ARCH),$(BUILDARCH))","") + _JUNK_ := $(shell echo >&2 \ + $(OSNAME) $(ARCH) "*** ERROR: this platform does not support 64-bit compilers!") + @exit 1 + endif +endif + +# we need to set up LP64 correctly to satisfy sanity checks in adlc +ifneq ("$(filter $(LP64_ARCH),$(BUILDARCH))","") + MFLAGS += " LP64=1 " +endif + +# The following renders pathnames in generated Makefiles valid on +# machines other than the machine containing the build tree. +# +# For example, let's say my build tree lives on /files12 on +# exact.east.sun.com. This logic will cause GAMMADIR to begin with +# /net/exact/files12/... +# +# We only do this on SunOS variants, for a couple of reasons: +# * It is extremely rare that source trees exist on other systems +# * It has been claimed that the Linux automounter is flakey, so +# changing GAMMADIR in a way that exercises the automounter could +# prove to be a source of unreliability in the build process. +# Obviously, this Makefile is only relevant on SunOS boxes to begin +# with, but the SunOS conditionalization will make it easier to +# combine Makefiles in the future (assuming we ever do that). + +ifeq ($(OSNAME),solaris) + + # prepend current directory to relative pathnames. + NEW_GAMMADIR := \ + $(shell echo $(GAMMADIR) | \ + sed -e "s=^\([^/].*\)=$(shell pwd)/\1=" \ + ) + unexport NEW_GAMMADIR + + # If NEW_GAMMADIR doesn't already start with "/net/": + ifeq ($(strip $(filter /net/%,$(NEW_GAMMADIR))),) + # prepend /net/$(HOST) + # remove /net/$(HOST) if name already began with /home/ + # remove /net/$(HOST) if name already began with /java/ + # remove /net/$(HOST) if name already began with /lab/ + NEW_GAMMADIR := \ + $(shell echo $(NEW_GAMMADIR) | \ + sed -e "s=^\(.*\)=/net/$(HOST)\1=" \ + -e "s=^/net/$(HOST)/home/=/home/=" \ + -e "s=^/net/$(HOST)/java/=/java/=" \ + -e "s=^/net/$(HOST)/lab/=/lab/=" \ + ) + # Don't use the new value for GAMMADIR unless a file with the new + # name actually exists. + ifneq ($(wildcard $(NEW_GAMMADIR)),) + GAMMADIR := $(NEW_GAMMADIR) + endif + endif + +endif + + +# There is a (semi-) regular correspondence between make targets and actions: +# +# Target Tree Type Build Dir +# +# debug compiler2 __compiler2/debug +# fastdebug compiler2 __compiler2/fastdebug +# jvmg compiler2 __compiler2/jvmg +# optimized compiler2 __compiler2/optimized +# profiled compiler2 __compiler2/profiled +# product compiler2 __compiler2/product +# +# debug1 compiler1 __compiler1/debug +# fastdebug1 compiler1 __compiler1/fastdebug +# jvmg1 compiler1 __compiler1/jvmg +# optimized1 compiler1 __compiler1/optimized +# profiled1 compiler1 __compiler1/profiled +# product1 compiler1 __compiler1/product +# +# debugcore core __core/debug +# fastdebugcore core __core/fastdebug +# jvmgcore core __core/jvmg +# optimizedcore core __core/optimized +# profiledcore core __core/profiled +# productcore core __core/product +# +# What you get with each target: +# +# debug* - "thin" libjvm_g - debug info linked into the gamma_g launcher +# fastdebug* - optimized compile, but with asserts enabled +# jvmg* - "fat" libjvm_g - debug info linked into libjvm_g.so +# optimized* - optimized compile, no asserts +# profiled* - gprof +# product* - the shippable thing: optimized compile, no asserts, -DPRODUCT + +# This target list needs to be coordinated with the usage message +# in the build.sh script: +TARGETS = debug jvmg fastdebug optimized profiled product + +SUBDIR_DOCS = $(OSNAME)_$(BUILDARCH)_docs +SUBDIRS_C1 = $(addprefix $(OSNAME)_$(BUILDARCH)_compiler1/,$(TARGETS)) +SUBDIRS_C2 = $(addprefix $(OSNAME)_$(BUILDARCH)_compiler2/,$(TARGETS)) +SUBDIRS_TIERED = $(addprefix $(OSNAME)_$(BUILDARCH)_tiered/,$(TARGETS)) +SUBDIRS_CORE = $(addprefix $(OSNAME)_$(BUILDARCH)_core/,$(TARGETS)) + +TARGETS_C2 = $(TARGETS) +TARGETS_C1 = $(addsuffix 1,$(TARGETS)) +TARGETS_TIERED = $(addsuffix tiered,$(TARGETS)) +TARGETS_CORE = $(addsuffix core,$(TARGETS)) + +BUILDTREE_MAKE = $(GAMMADIR)/build/$(OSNAME)/makefiles/buildtree.make +BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OSNAME) ARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) +BUILDTREE_VARS += HOTSPOT_RELEASE_VERSION=$(HOTSPOT_RELEASE_VERSION) HOTSPOT_BUILD_VERSION=$(HOTSPOT_BUILD_VERSION) JRE_RELEASE_VERSION=$(JRE_RELEASE_VERSION) + +BUILDTREE = $(MAKE) -f $(BUILDTREE_MAKE) $(BUILDTREE_VARS) + +#------------------------------------------------------------------------------- + +# Could make everything by default, but that would take a while. +all: + @echo "Try '$(MAKE) ...' where is one or more of" + @echo " $(TARGETS_C2)" + @echo " $(TARGETS_C1)" + @echo " $(TARGETS_CORE)" + +checks: check_os_version check_j2se_version + +# We do not want people accidentally building on old systems (e.g. Linux 2.2.x, +# Solaris 2.5.1, 2.6). +# Disable this check by setting DISABLE_HOTSPOT_OS_VERSION_CHECK=ok. + +SUPPORTED_OS_VERSION = 2.4% 2.5% 2.6% 2.7% +OS_VERSION := $(shell uname -r) +EMPTY_IF_NOT_SUPPORTED = $(filter $(SUPPORTED_OS_VERSION),$(OS_VERSION)) + +check_os_version: +ifeq ($(DISABLE_HOTSPOT_OS_VERSION_CHECK)$(EMPTY_IF_NOT_SUPPORTED),) + $(QUIETLY) >&2 echo "*** This OS is not supported:" `uname -a`; exit 1; +endif + +# jvmti.make requires XSLT (J2SE 1.4.x or newer): +XSLT_CHECK = $(REMOTE) $(RUN.JAVAP) javax.xml.transform.TransformerFactory +# If not found then fail fast. +check_j2se_version: + $(QUIETLY) $(XSLT_CHECK) > /dev/null 2>&1; \ + if [ $$? -ne 0 ]; then \ + $(REMOTE) $(RUN.JAVA) -version; \ + echo "*** An XSLT processor (J2SE 1.4.x or newer) is required" \ + "to bootstrap this build" 1>&2; \ + exit 1; \ + fi + +$(SUBDIRS_TIERED): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=tiered + +$(SUBDIRS_C2): $(BUILDTREE_MAKE) +ifdef FORCE_TIERED + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=tiered FORCE_TIERED=1 +else + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=compiler2 +endif + +$(SUBDIRS_C1): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=compiler1 + +$(SUBDIRS_CORE): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=core + +# Define INSTALL=y at command line to automatically copy JVM into JAVA_HOME + +$(TARGETS_C2): $(SUBDIRS_C2) + cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_TIERED): $(SUBDIRS_TIERED) + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_C1): $(SUBDIRS_C1) + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_CORE): $(SUBDIRS_CORE) + cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) install +endif + +# Just build the tree, and nothing else: +tree: $(SUBDIRS_C2) +tree1: $(SUBDIRS_C1) +treecore: $(SUBDIRS_CORE) + +# Doc target. This is the same for all build options. +# Hence create a docs directory beside ...$(ARCH)_[...] +docs: checks + $(QUIETLY) mkdir -p $(SUBDIR_DOCS) + $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/makefiles/jvmti.make $(MFLAGS) $(BUILDTREE_VARS) JvmtiOutDir=$(SUBDIR_DOCS) jvmtidocs + +# Synonyms for win32-like targets. +compiler2: jvmg product + +compiler1: jvmg1 product1 + +core: jvmgcore productcore + +clean_docs: + rm -rf $(SUBDIR_DOCS) + +clean_compiler1 clean_compiler2 clean_core: + rm -rf $(OSNAME)_$(BUILDARCH)_$(subst clean_,,$@) + +clean: clean_compiler2 clean_compiler1 clean_core clean_docs + +include $(GAMMADIR)/build/$(OSNAME)/makefiles/cscope.make + +#------------------------------------------------------------------------------- + +.PHONY: $(TARGETS_C2) $(TARGETS_C1) $(TARGETS_CORE) +.PHONY: tree tree1 treecore +.PHONY: all compiler1 compiler2 core +.PHONY: clean clean_compiler1 clean_compiler2 clean_core docs clean_docs +.PHONY: checks check_os_version check_j2se_version diff --git a/hotspot/build/linux/Queens.class b/hotspot/build/linux/Queens.class new file mode 100644 index 00000000000..d4582a04411 Binary files /dev/null and b/hotspot/build/linux/Queens.class differ diff --git a/hotspot/build/linux/README b/hotspot/build/linux/README new file mode 100644 index 00000000000..3180e8d8878 --- /dev/null +++ b/hotspot/build/linux/README @@ -0,0 +1,26 @@ +Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + +This code is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 only, as +published by the Free Software Foundation. + +This code is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +version 2 for more details (a copy is included in the LICENSE file that +accompanied this code). + +You should have received a copy of the GNU General Public License version +2 along with this work; if not, write to the Free Software Foundation, +Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +CA 95054 USA or visit www.sun.com if you need additional information or +have any questions. + +________________________________________________________________________ + +Please refer to the comments in the Makefile in this directory +for instructions how to build the Solaris versions. + diff --git a/hotspot/build/linux/adlc_updater b/hotspot/build/linux/adlc_updater new file mode 100644 index 00000000000..6a97b79931d --- /dev/null +++ b/hotspot/build/linux/adlc_updater @@ -0,0 +1,11 @@ +#! /bin/sh +# +# This file is used by adlc.make to selectively update generated +# adlc files. Because source and target diretories are relative +# paths, this file is copied to the target build directory before +# use. +# +# adlc-updater +# +[ -f $3/$1 ] && cmp -s $2/$1 $3/$1 || \ +( [ -f $3/$1 ]; echo Updating $3/$1 ; touch $2/made-change ; mv $2/$1 $3/$1 ) diff --git a/hotspot/build/linux/build.sh b/hotspot/build/linux/build.sh new file mode 100644 index 00000000000..a8b2630e3cf --- /dev/null +++ b/hotspot/build/linux/build.sh @@ -0,0 +1,95 @@ +#! /bin/sh +# +# Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Make sure the variable JAVA_HOME is set before running this script. + +set -u + + +if [ $# != 2 ]; then + echo "Usage : $0 Build_Options Location" + echo "Build Options : debug or optimized or basicdebug or basic or clean" + echo "Location : specify any workspace which has gamma sources" + exit 1 +fi + +# Just in case: +case ${JAVA_HOME} in +/*) true;; +?*) JAVA_HOME=`( cd $JAVA_HOME; pwd )`;; +esac + +case `uname -m` in + i386|i486|i586|i686) + mach=i386 + ;; + *) + echo "Unsupported machine: " `uname -m` + exit 1 + ;; +esac + +if [ "${JAVA_HOME}" = "" -o ! -d "${JAVA_HOME}" -o ! -d ${JAVA_HOME}/jre/lib/${mach} ]; then + echo "JAVA_HOME needs to be set to a valid JDK path" + echo "ksh : export JAVA_HOME=/net/tetrasparc/export/gobi/JDK1.2_fcs_V/linux" + echo "csh : setenv JAVA_HOME /net/tetrasparc/export/gobi/JDK1.2_fcs_V/linux" + exit 1 +fi + + +LD_LIBRARY_PATH=${JAVA_HOME}/jre/lib/`uname -p`:\ +${JAVA_HOME}/jre/lib/`uname -p`/native_threads:${LD_LIBRARY_PATH-.} + +# This is necessary as long as we are using the old launcher +# with the new distribution format: +CLASSPATH=${JAVA_HOME}/jre/lib/rt.jar:${CLASSPATH-.} + + +for gm in gmake gnumake +do + if [ "${GNUMAKE-}" != "" ]; then break; fi + ($gm --version >/dev/null) 2>/dev/null && GNUMAKE=$gm +done +: ${GNUMAKE:?'Cannot locate the gnumake program. Stop.'} + + +echo "### ENVIRONMENT SETTINGS:" +export JAVA_HOME ; echo "JAVA_HOME=$JAVA_HOME" +export LD_LIBRARY_PATH ; echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" +export CLASSPATH ; echo "CLASSPATH=$CLASSPATH" +export GNUMAKE ; echo "GNUMAKE=$GNUMAKE" +echo "###" + +Build_Options=$1 +Location=$2 + +case ${Location} in +/*) true;; +?*) Location=`(cd ${Location}; pwd)`;; +esac + +echo \ +${GNUMAKE} -f ${Location}/build/linux/Makefile $Build_Options GAMMADIR=${Location} +${GNUMAKE} -f ${Location}/build/linux/Makefile $Build_Options GAMMADIR=${Location} diff --git a/hotspot/build/linux/makefiles/adjust-mflags.sh b/hotspot/build/linux/makefiles/adjust-mflags.sh new file mode 100644 index 00000000000..325e02d4fb1 --- /dev/null +++ b/hotspot/build/linux/makefiles/adjust-mflags.sh @@ -0,0 +1,87 @@ +#! /bin/sh +# +# Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This script is used only from top.make. +# The macro $(MFLAGS-adjusted) calls this script to +# adjust the "-j" arguments to take into account +# the HOTSPOT_BUILD_JOBS variable. The default +# handling of the "-j" argument by gnumake does +# not meet our needs, so we must adjust it ourselves. + +# This argument adjustment applies to two recursive +# calls to "$(MAKE) $(MFLAGS-adjusted)" in top.make. +# One invokes adlc.make, and the other invokes vm.make. +# The adjustment propagates the desired concurrency +# level down to the sub-make (of the adlc or vm). +# The default behavior of gnumake is to run all +# sub-makes without concurrency ("-j1"). + +# Also, we use a make variable rather than an explicit +# "-j" argument to control this setting, so that +# the concurrency setting (which must be tuned separately +# for each MP system) can be set via an environment variable. +# The recommended setting is 1.5x to 2x the number of available +# CPUs on the MP system, which is large enough to keep the CPUs +# busy (even though some jobs may be I/O bound) but not too large, +# we may presume, to overflow the system's swap space. + +set -eu + +default_build_jobs=4 + +case $# in +[12]) true;; +*) >&2 echo "Usage: $0 ${MFLAGS} ${HOTSPOT_BUILD_JOBS}"; exit 2;; +esac + +MFLAGS=$1 +HOTSPOT_BUILD_JOBS=${2-} + +# Normalize any -jN argument to the form " -j${HBJ}" +MFLAGS=` + echo "$MFLAGS" \ + | sed ' + s/^-/ -/ + s/ -\([^ ][^ ]*\)j/ -\1 -j/ + s/ -j[0-9][0-9]*/ -j/ + s/ -j\([^ ]\)/ -j -\1/ + s/ -j/ -j'${HOTSPOT_BUILD_JOBS:-${default_build_jobs}}'/ + ' ` + +case ${HOTSPOT_BUILD_JOBS} in \ + +'') case ${MFLAGS} in + *\ -j*) + >&2 echo "# Note: -jN is ineffective for setting parallelism in this makefile." + >&2 echo "# please set HOTSPOT_BUILD_JOBS=${default_build_jobs} in the command line or environment." + esac;; + +?*) case ${MFLAGS} in + *\ -j*) true;; + *) MFLAGS="-j${HOTSPOT_BUILD_JOBS} ${MFLAGS}";; + esac;; +esac + +echo "${MFLAGS}" diff --git a/hotspot/build/linux/makefiles/adlc.make b/hotspot/build/linux/makefiles/adlc.make new file mode 100644 index 00000000000..9ed67794f07 --- /dev/null +++ b/hotspot/build/linux/makefiles/adlc.make @@ -0,0 +1,201 @@ +# +# Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This makefile (adlc.make) is included from the adlc.make in the +# build directories. +# It knows how to compile, link, and run the adlc. + +include $(GAMMADIR)/build/$(Platform_os_family)/makefiles/rules.make + +# ######################################################################### + +# OUTDIR must be the same as AD_Dir = $(GENERATED)/adfiles in top.make: +GENERATED = ../generated +OUTDIR = $(GENERATED)/adfiles + +ARCH = $(Platform_arch) +OS = $(Platform_os_family) + +SOURCE.AD = $(OUTDIR)/$(OS)_$(Platform_arch_model).ad + +SOURCES.AD = $(GAMMADIR)/src/cpu/$(ARCH)/vm/$(Platform_arch_model).ad \ + $(GAMMADIR)/src/os_cpu/$(OS)_$(ARCH)/vm/$(OS)_$(Platform_arch_model).ad + +Src_Dirs += $(GAMMADIR)/src/share/vm/adlc + +EXEC = $(OUTDIR)/adlc + +# set VPATH so make knows where to look for source files +Src_Dirs_V = ${Src_Dirs} $(GENERATED)/incls +VPATH += $(Src_Dirs_V:%=%:) + +# set INCLUDES for C preprocessor +Src_Dirs_I = ${Src_Dirs} $(GENERATED) +INCLUDES += $(Src_Dirs_I:%=-I%) + +# Force assertions on. +SYSDEFS += -DASSERT +CPPFLAGS = $(SYSDEFS) $(INCLUDES) + +# CFLAGS_WARN holds compiler options to suppress/enable warnings. +# Suppress warnings (for now) +CFLAGS_WARN = -w +CFLAGS += $(CFLAGS_WARN) + +OBJECTNAMES = \ + adlparse.o \ + archDesc.o \ + arena.o \ + dfa.o \ + dict2.o \ + filebuff.o \ + forms.o \ + formsopt.o \ + formssel.o \ + main.o \ + adlc-opcodes.o \ + output_c.o \ + output_h.o \ + +OBJECTS = $(OBJECTNAMES:%=$(OUTDIR)/%) + +GENERATEDNAMES = \ + ad_$(Platform_arch_model).cpp \ + ad_$(Platform_arch_model).hpp \ + ad_$(Platform_arch_model)_clone.cpp \ + ad_$(Platform_arch_model)_expand.cpp \ + ad_$(Platform_arch_model)_format.cpp \ + ad_$(Platform_arch_model)_gen.cpp \ + ad_$(Platform_arch_model)_misc.cpp \ + ad_$(Platform_arch_model)_peephole.cpp \ + ad_$(Platform_arch_model)_pipeline.cpp \ + adGlobals_$(Platform_arch_model).hpp \ + dfa_$(Platform_arch_model).cpp \ + +GENERATEDFILES = $(GENERATEDNAMES:%=$(OUTDIR)/%) + +# ######################################################################### + +all: $(EXEC) + +$(EXEC) : $(OBJECTS) + @echo Making adlc + $(QUIETLY) $(LINK_NOPROF.CC) -o $(EXEC) $(OBJECTS) + +# Random dependencies: +$(OBJECTS): opcodes.hpp classes.hpp adlc.hpp adlcVMDeps.hpp adlparse.hpp archDesc.hpp arena.hpp dict2.hpp filebuff.hpp forms.hpp formsopt.hpp formssel.hpp + +# The source files refer to ostream.h, which sparcworks calls iostream.h +$(OBJECTS): ostream.h + +ostream.h : + @echo >$@ '#include ' + +dump: + : OUTDIR=$(OUTDIR) + : OBJECTS=$(OBJECTS) + : products = $(GENERATEDFILES) + +all: $(GENERATEDFILES) + +$(GENERATEDFILES): refresh_adfiles + +# Get a unique temporary directory name, so multiple makes can run in parallel. +# Note that product files are updated via "mv", which is atomic. +TEMPDIR := $(OUTDIR)/mktmp$(shell echo $$$$) + +ADLCFLAGS = -q -T + +ifdef LP64 +ADLCFLAGS += -D_LP64 +else +ADLCFLAGS += -U_LP64 +endif + +# +# adlc_updater is a simple sh script, under sccs control. It is +# used to selectively update generated adlc files. This should +# provide a nice compilation speed improvement. +# +ADLC_UPDATER_DIRECTORY = $(GAMMADIR)/build/$(OS) +ADLC_UPDATER = adlc_updater + +# This action refreshes all generated adlc files simultaneously. +# The way it works is this: +# 1) create a scratch directory to work in. +# 2) if the current working directory does not have $(ADLC_UPDATER), copy it. +# 3) run the compiled adlc executable. This will create new adlc files in the scratch directory. +# 4) call $(ADLC_UPDATER) on each generated adlc file. It will selectively update changed or missing files. +# 5) If we actually updated any files, echo a notice. +# +refresh_adfiles: $(EXEC) $(SOURCE.AD) + @rm -rf $(TEMPDIR); mkdir $(TEMPDIR) + $(QUIETLY) [ -f $(ADLC_UPDATER) ] || ( cp $(ADLC_UPDATER_DIRECTORY)/$(ADLC_UPDATER) . ; chmod +x $(ADLC_UPDATER) ) + $(QUIETLY) $(EXEC) $(ADLCFLAGS) $(SOURCE.AD) \ + -c$(TEMPDIR)/ad_$(Platform_arch_model).cpp -h$(TEMPDIR)/ad_$(Platform_arch_model).hpp -a$(TEMPDIR)/dfa_$(Platform_arch_model).cpp -v$(TEMPDIR)/adGlobals_$(Platform_arch_model).hpp \ + || { rm -rf $(TEMPDIR); exit 1; } + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model).cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model).hpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_clone.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_expand.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_format.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_gen.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_misc.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_peephole.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_pipeline.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) adGlobals_$(Platform_arch_model).hpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) dfa_$(Platform_arch_model).cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) [ -f $(TEMPDIR)/made-change ] \ + || echo "Rescanned $(SOURCE.AD) but encountered no changes." + $(QUIETLY) rm -rf $(TEMPDIR) + + +# ######################################################################### + +$(SOURCE.AD): $(SOURCES.AD) + $(QUIETLY) cat $(SOURCES.AD) > $(SOURCE.AD) + +$(OUTDIR)/%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(COMPILE.CC) -o $@ $< $(COMPILE_DONE) + +# Some object files are given a prefix, to disambiguate +# them from objects of the same name built for the VM. +$(OUTDIR)/adlc-%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(COMPILE.CC) -o $@ $< $(COMPILE_DONE) + +# ######################################################################### + +clean : + rm $(OBJECTS) + +cleanall : + rm $(OBJECTS) $(EXEC) + +# ######################################################################### + +.PHONY: all dump refresh_adfiles clean cleanall diff --git a/hotspot/build/linux/makefiles/amd64.make b/hotspot/build/linux/makefiles/amd64.make new file mode 100644 index 00000000000..6b961fab558 --- /dev/null +++ b/hotspot/build/linux/makefiles/amd64.make @@ -0,0 +1,40 @@ +# +# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Not included in includeDB because it has no dependencies +Obj_Files += linux_x86_64.o + +# The copied fdlibm routines in sharedRuntimeTrig.o must not be optimized +OPT_CFLAGS/sharedRuntimeTrig.o = $(OPT_CFLAGS/NOOPT) +# The copied fdlibm routines in sharedRuntimeTrans.o must not be optimized +OPT_CFLAGS/sharedRuntimeTrans.o = $(OPT_CFLAGS/NOOPT) +# Must also specify if CPU is little endian +CFLAGS += -DVM_LITTLE_ENDIAN + +CFLAGS += -D_LP64=1 + +# The serviceability agent relies on frame pointer (%rbp) to walk thread stack +CFLAGS += -fno-omit-frame-pointer + +OPT_CFLAGS/compactingPermGenGen.o = -O1 diff --git a/hotspot/build/linux/makefiles/buildtree.make b/hotspot/build/linux/makefiles/buildtree.make new file mode 100644 index 00000000000..7e5b92df37f --- /dev/null +++ b/hotspot/build/linux/makefiles/buildtree.make @@ -0,0 +1,346 @@ +# +# Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Usage: +# +# $(MAKE) -f buildtree.make ARCH=arch BUILDARCH=buildarch LIBARCH=libarch +# GAMMADIR=dir OS_FAMILY=os VARIANT=variant +# +# The macros ARCH, GAMMADIR, OS_FAMILY and VARIANT must be defined in the +# environment or on the command-line: +# +# ARCH - sparc, i486, ... HotSpot cpu and os_cpu source directory +# BUILDARCH - build directory +# LIBARCH - the corresponding directory in JDK/JRE +# GAMMADIR - top of workspace +# OS_FAMILY - operating system +# VARIANT - core, compiler1, compiler2, or tiered +# HOTSPOT_RELEASE_VERSION - .-b (11.0-b07) +# HOTSPOT_BUILD_VERSION - internal, PRTjob ID, JPRTjob ID +# JRE_RELEASE_VERSION - .. (1.7.0) +# +# Builds the directory trees with makefiles plus some convenience files in +# each directory: +# +# Makefile - for "make foo" +# flags.make - with macro settings +# vm.make - to support making "$(MAKE) -v vm.make" in makefiles +# adlc.make - +# jvmti.make - generate JVMTI bindings from the spec (JSR-163) +# sa.make - generate SA jar file and natives +# env.[ck]sh - environment settings +# test_gamma - script to run the Queens program +# +# The makefiles are split this way so that "make foo" will run faster by not +# having to read the dependency files for the vm. + +include $(GAMMADIR)/make/scm.make + +# 'gmake MAKE_VERBOSE=y' or 'gmake QUIETLY=' gives all the gory details. +QUIETLY$(MAKE_VERBOSE) = @ + +# For now, until the compiler is less wobbly: +TESTFLAGS = -Xbatch -showversion + +PLATFORM_FILE = $(GAMMADIR)/build/$(OS_FAMILY)/platform_$(BUILDARCH) + +ifdef FORCE_TIERED +ifeq ($(VARIANT),tiered) +PLATFORM_DIR = $(OS_FAMILY)_$(BUILDARCH)_compiler2 +else +PLATFORM_DIR = $(OS_FAMILY)_$(BUILDARCH)_$(VARIANT) +endif +else +PLATFORM_DIR = $(OS_FAMILY)_$(BUILDARCH)_$(VARIANT) +endif + +# +# We do two levels of exclusion in the shared directory. +# TOPLEVEL excludes are pruned, they are not recursively searched, +# but lower level directories can be named without fear of collision. +# ALWAYS excludes are excluded at any level in the directory tree. +# + +ALWAYS_EXCLUDE_DIRS = $(SCM_DIRS) + +ifeq ($(VARIANT),tiered) +TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name agent +else +ifeq ($(VARIANT),compiler2) +TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name c1 -o -name agent +else +# compiler1 and core use the same exclude list +TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name opto -o -name libadt -o -name agent +endif +endif + +# Get things from the platform file. +COMPILER = $(shell sed -n 's/^compiler[ ]*=[ ]*//p' $(PLATFORM_FILE)) + +SIMPLE_DIRS = \ + $(PLATFORM_DIR)/generated/incls \ + $(PLATFORM_DIR)/generated/adfiles \ + $(PLATFORM_DIR)/generated/jvmtifiles + +TARGETS = debug fastdebug jvmg optimized product profiled +SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) + +# For dependencies and recursive makes. +BUILDTREE_MAKE = $(GAMMADIR)/build/$(OS_FAMILY)/makefiles/buildtree.make + +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make sa.make \ + env.sh env.csh .dbxrc test_gamma + +BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ + ARCH=$(ARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) + +# Define variables to be set in flags.make. +# Default values are set in make/defs.make. +ifeq ($(HOTSPOT_BUILD_VERSION),) + HS_BUILD_VER=$(HOTSPOT_RELEASE_VERSION) +else + HS_BUILD_VER=$(HOTSPOT_RELEASE_VERSION)-$(HOTSPOT_BUILD_VERSION) +endif +# Set BUILD_USER from system-dependent hints: $LOGNAME, $(whoami) +ifndef HOTSPOT_BUILD_USER + HOTSPOT_BUILD_USER := $(shell echo $$LOGNAME) +endif +ifndef HOTSPOT_BUILD_USER + HOTSPOT_BUILD_USER := $(shell whoami) +endif +# Define HOTSPOT_VM_DISTRO based on settings in build/hotspot_distro +# or build/closed/hotspot_distro. +ifndef HOTSPOT_VM_DISTRO + CLOSED_DIR_EXISTS := $(shell \ + if [ -d $(GAMMADIR)/build/closed ] ; then \ + echo true; \ + else \ + echo false; \ + fi) + ifeq ($(CLOSED_DIR_EXISTS), true) + include $(GAMMADIR)/build/closed/hotspot_distro + else + include $(GAMMADIR)/build/hotspot_distro + endif +endif + +BUILDTREE_VARS += HOTSPOT_RELEASE_VERSION=$(HS_BUILD_VER) HOTSPOT_BUILD_VERSION= JRE_RELEASE_VERSION=$(JRE_RELEASE_VERSION) + +BUILDTREE = \ + $(MAKE) -f $(BUILDTREE_MAKE) $(BUILDTREE_TARGETS) $(BUILDTREE_VARS) + +BUILDTREE_COMMENT = echo "\# Generated by $(BUILDTREE_MAKE)" + +all: $(SUBMAKE_DIRS) + +# Run make in each subdirectory recursively. +$(SUBMAKE_DIRS): $(SIMPLE_DIRS) FORCE + $(QUIETLY) [ -d $@ ] || { mkdir -p $@; } + $(QUIETLY) cd $@ && $(BUILDTREE) TARGET=$(@F) + $(QUIETLY) touch $@ + +$(SIMPLE_DIRS): + $(QUIETLY) mkdir -p $@ + +flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo "Platform_file = $(PLATFORM_FILE)" | sed 's|$(GAMMADIR)|$$(GAMMADIR)|'; \ + sed -n '/=/s/^ */Platform_/p' < $(PLATFORM_FILE); \ + echo; \ + echo "GAMMADIR = $(GAMMADIR)"; \ + echo "SYSDEFS = \$$(Platform_sysdefs)"; \ + echo "SRCARCH = $(ARCH)"; \ + echo "BUILDARCH = $(BUILDARCH)"; \ + echo "LIBARCH = $(LIBARCH)"; \ + echo "TARGET = $(TARGET)"; \ + echo "HS_BUILD_VER = $(HS_BUILD_VER)"; \ + echo "JRE_RELEASE_VER = $(JRE_RELEASE_VERSION)"; \ + echo "SA_BUILD_VERSION = $(HS_BUILD_VER)"; \ + echo "HOTSPOT_BUILD_USER = $(HOTSPOT_BUILD_USER)"; \ + echo "HOTSPOT_VM_DISTRO = $(HOTSPOT_VM_DISTRO)"; \ + echo; \ + echo "Src_Dirs = \\"; \ + sed 's/$$/ \\/;s|$(GAMMADIR)|$$(GAMMADIR)|' ../shared_dirs.lst; \ + echo "\$$(GAMMADIR)/src/cpu/$(ARCH)/vm \\"; \ + echo "\$$(GAMMADIR)/src/os/$(OS_FAMILY)/vm \\"; \ + echo "\$$(GAMMADIR)/src/os_cpu/$(OS_FAMILY)_$(ARCH)/vm"; \ + [ -n "$(CFLAGS_BROWSE)" ] && \ + echo && echo "CFLAGS_BROWSE = $(CFLAGS_BROWSE)"; \ + [ -n "$(HOTSPOT_EXTRA_SYSDEFS)" ] && \ + echo && \ + echo "HOTSPOT_EXTRA_SYSDEFS\$$(HOTSPOT_EXTRA_SYSDEFS) = $(HOTSPOT_EXTRA_SYSDEFS)" && \ + echo "SYSDEFS += \$$(HOTSPOT_EXTRA_SYSDEFS)"; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(VARIANT).make"; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(COMPILER).make"; \ + ) > $@ + +flags_vm.make: $(BUILDTREE_MAKE) ../shared_dirs.lst + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + [ "$(TARGET)" = profiled ] && \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/optimized.make"; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(TARGET).make"; \ + ) > $@ + +../shared_dirs.lst: $(BUILDTREE_MAKE) $(GAMMADIR)/src/share/vm + @echo Creating directory list $@ + $(QUIETLY) find $(GAMMADIR)/src/share/vm/* -prune \ + -type d \! \( $(TOPLEVEL_EXCLUDE_DIRS) \) -exec find {} \ + \( $(ALWAYS_EXCLUDE_DIRS) \) -prune -o -type d -print \; > $@ + +Makefile: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/top.make"; \ + ) > $@ + +vm.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo include flags_vm.make; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +adlc.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +jvmti.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +sa.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +env.sh: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + [ -n "$$JAVA_HOME" ] && { echo ": \$${JAVA_HOME:=$${JAVA_HOME}}"; }; \ + { \ + echo "LD_LIBRARY_PATH=.:$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ + echo "CLASSPATH=$${CLASSPATH:+$$CLASSPATH:}.:\$${JAVA_HOME}/jre/lib/rt.jar:\$${JAVA_HOME}/jre/lib/i18n.jar"; \ + } | sed s:$${JAVA_HOME:--------}:\$${JAVA_HOME}:g; \ + echo "HOTSPOT_BUILD_USER=\"$${LOGNAME:-$$USER} in `basename $(GAMMADIR)`\""; \ + echo "export JAVA_HOME LD_LIBRARY_PATH CLASSPATH HOTSPOT_BUILD_USER"; \ + ) > $@ + +env.csh: env.sh + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + [ -n "$$JAVA_HOME" ] && \ + { echo "if (! \$$?JAVA_HOME) setenv JAVA_HOME \"$$JAVA_HOME\""; }; \ + sed -n 's/^\([A-Za-z_][A-Za-z0-9_]*\)=/setenv \1 /p' $?; \ + ) > $@ + +.dbxrc: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + echo "echo '# Loading $(PLATFORM_DIR)/$(TARGET)/.dbxrc'"; \ + echo "if [ -f \"\$${HOTSPOT_DBXWARE}\" ]"; \ + echo "then"; \ + echo " source \"\$${HOTSPOT_DBXWARE}\""; \ + echo "elif [ -f \"\$$HOME/.dbxrc\" ]"; \ + echo "then"; \ + echo " source \"\$$HOME/.dbxrc\""; \ + echo "fi"; \ + ) > $@ + +# Skip the test for product builds (which only work when installed in a JDK), to +# avoid exiting with an error and causing make to halt. +NO_TEST_MSG = \ + echo "$@: skipping the test--this build must be tested in a JDK." + +NO_JAVA_HOME_MSG = \ + echo "JAVA_HOME must be set to run this test." + +DATA_MODE = $(DATA_MODE/$(BUILDARCH)) +JAVA_FLAG = $(JAVA_FLAG/$(DATA_MODE)) + +DATA_MODE/i486 = 32 +DATA_MODE/sparc = 32 +DATA_MODE/sparcv9 = 64 +DATA_MODE/amd64 = 64 +DATA_MODE/ia64 = 64 + +JAVA_FLAG/32 = -d32 +JAVA_FLAG/64 = -d64 + +WRONG_DATA_MODE_MSG = \ + echo "JAVA_HOME must point to $(DATA_MODE)bit JDK." + +test_gamma: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + echo '#!/bin/sh'; \ + $(BUILDTREE_COMMENT); \ + echo '. ./env.sh'; \ + echo "if [ -z \$$JAVA_HOME ]; then { $(NO_JAVA_HOME_MSG); exit 0; }; fi"; \ + echo "if ! \$${JAVA_HOME}/bin/java $(JAVA_FLAG) -fullversion 2>1 > /dev/null"; \ + echo "then"; \ + echo " $(WRONG_DATA_MODE_MSG); exit 0;"; \ + echo "fi"; \ + echo 'CLASSPATH="$(GAMMADIR)/build/$(OS_FAMILY):$$CLASSPATH"'; \ + echo '[ -f gamma_g ] && { gamma=gamma_g; }'; \ + echo './$${gamma:-gamma} $(TESTFLAGS) Queens < /dev/null'; \ + ) > $@ + $(QUIETLY) chmod +x $@ + +FORCE: + +.PHONY: all FORCE diff --git a/hotspot/build/linux/makefiles/compiler1.make b/hotspot/build/linux/makefiles/compiler1.make new file mode 100644 index 00000000000..3573eccfac4 --- /dev/null +++ b/hotspot/build/linux/makefiles/compiler1.make @@ -0,0 +1,31 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making client version of VM + +TYPE=COMPILER1 + +VM_SUBDIR = client + +CFLAGS += -DCOMPILER1 diff --git a/hotspot/build/linux/makefiles/compiler2.make b/hotspot/build/linux/makefiles/compiler2.make new file mode 100644 index 00000000000..5ef2129ae5a --- /dev/null +++ b/hotspot/build/linux/makefiles/compiler2.make @@ -0,0 +1,31 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making server version of VM + +TYPE=COMPILER2 + +VM_SUBDIR = server + +CFLAGS += -DCOMPILER2 diff --git a/hotspot/build/linux/makefiles/core.make b/hotspot/build/linux/makefiles/core.make new file mode 100644 index 00000000000..dbe2ee3a8e3 --- /dev/null +++ b/hotspot/build/linux/makefiles/core.make @@ -0,0 +1,34 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making core version of VM + +# Note the effect on includeDB lists in top.make: +# includeDB_compiler* and ad_.*pp are excluded from the build, +TYPE=CORE + +# There is no "core" directory in JDK. Install core build in server directory. +VM_SUBDIR = server + +# Note: macros.hpp defines CORE diff --git a/hotspot/build/linux/makefiles/cscope.make b/hotspot/build/linux/makefiles/cscope.make new file mode 100644 index 00000000000..149da265870 --- /dev/null +++ b/hotspot/build/linux/makefiles/cscope.make @@ -0,0 +1,156 @@ +# +# Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# +# The cscope.out file is made in the current directory and spans the entire +# source tree. +# +# Things to note: +# 1. We use relative names for cscope. +# 2. We *don't* remove the old cscope.out file, because cscope is smart +# enough to only build what has changed. It can be confused, however, +# if files are renamed or removed, so it may be necessary to manually +# remove cscope.out if a lot of reorganization has occurred. +# + +include $(GAMMADIR)/make/scm.make + +NAWK = awk +RM = rm -f +CS_TOP = ../.. + +CSDIRS = $(CS_TOP)/src $(CS_TOP)/build +CSINCS = $(CSDIRS:%=-I%) + +CSCOPE = cscope +CSCOPE_FLAGS = -b + +# Allow .java files to be added from the environment (CSCLASSES=yes). +ifdef CSCLASSES +ADDCLASSES= -o -name '*.java' +endif + +# Adding CClassHeaders also pushes the file count of a full workspace up about +# 200 files (these files also don't exist in a new workspace, and thus will +# cause the recreation of the database as they get created, which might seem +# a little confusing). Thus allow these files to be added from the environment +# (CSHEADERS=yes). +ifndef CSHEADERS +RMCCHEADERS= -o -name CClassHeaders +endif + +# Use CS_GENERATED=x to include auto-generated files in the build directories. +ifdef CS_GENERATED +CS_ADD_GENERATED = -o -name '*.incl' +else +CS_PRUNE_GENERATED = -o -name '${OS}_*_core' -o -name '${OS}_*_compiler?' +endif + +# OS-specific files for other systems are excluded by default. Use CS_OS=yes +# to include platform-specific files for other platforms. +ifndef CS_OS +CS_OS = linux macos solaris win32 +CS_PRUNE_OS = $(patsubst %,-o -name '*%*',$(filter-out ${OS},${CS_OS})) +endif + +# Processor-specific files for other processors are excluded by default. Use +# CS_CPU=x to include platform-specific files for other platforms. +ifndef CS_CPU +CS_CPU = i486 sparc amd64 ia64 +CS_PRUNE_CPU = $(patsubst %,-o -name '*%*',$(filter-out ${SRCARCH},${CS_CPU})) +endif + +# What files should we include? A simple rule might be just those files under +# SCCS control, however this would miss files we create like the opcodes and +# CClassHeaders. The following attempts to find everything that is *useful*. +# (.del files are created by sccsrm, demo directories contain many .java files +# that probably aren't useful for development, and the pkgarchive may contain +# duplicates of files within the source hierarchy). + +# Directories to exclude. +CS_PRUNE_STD = $(SCM_DIRS) \ + -o -name '.del-*' \ + -o -name '*demo' \ + -o -name pkgarchive + +CS_PRUNE = $(CS_PRUNE_STD) \ + $(CS_PRUNE_OS) \ + $(CS_PRUNE_CPU) \ + $(CS_PRUNE_GENERATED) \ + $(RMCCHEADERS) + +# File names to include. +CSFILENAMES = -name '*.[ch]pp' \ + -o -name '*.[Ccshlxy]' \ + $(CS_ADD_GENERATED) \ + -o -name '*.il' \ + -o -name '*.cc' \ + -o -name '*[Mm]akefile*' \ + -o -name '*.gmk' \ + -o -name '*.make' \ + -o -name '*.ad' \ + $(ADDCLASSES) + +.PRECIOUS: cscope.out + +cscope cscope.out: cscope.files FORCE + $(CSCOPE) $(CSCOPE_FLAGS) + +# The .raw file is reordered here in an attempt to make cscope display the most +# relevant files first. +cscope.files: .cscope.files.raw + echo "$(CSINCS)" > $@ + -egrep -v "\.java|\/build\/" $< >> $@ + -fgrep ".java" $< >> $@ + -fgrep "/build/" $< >> $@ + +.cscope.files.raw: .nametable.files + -find $(CSDIRS) -type d \( $(CS_PRUNE) \) -prune -o \ + -type f \( $(CSFILENAMES) \) -print > $@ + +cscope.clean: nametable.clean + -$(RM) cscope.out cscope.files .cscope.files.raw + +TAGS: cscope.files FORCE + egrep -v '^-|^$$' $< | etags --members - + +TAGS.clean: nametable.clean + -$(RM) TAGS + +# .nametable.files and .nametable.files.tmp are used to determine if any files +# were added to/deleted from/renamed in the workspace. If not, then there's +# normally no need to run find. To force a 'find': gmake nametable.clean. +.nametable.files: .nametable.files.tmp + cmp -s $@ $< || cp $< $@ + +.nametable.files.tmp: $(CS_TOP)/Codemgr_wsdata/nametable + $(NAWK) \ + '{ if (sub("( [a-z0-9]{2,8}){4}$$", "")) print $$0; }' $< > $@ + +nametable.clean: + -$(RM) .nametable.files .nametable.files.tmp + +FORCE: + +.PHONY: cscope cscope.clean TAGS.clean nametable.clean FORCE diff --git a/hotspot/build/linux/makefiles/debug.make b/hotspot/build/linux/makefiles/debug.make new file mode 100644 index 00000000000..0ef44d6200a --- /dev/null +++ b/hotspot/build/linux/makefiles/debug.make @@ -0,0 +1,44 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making debug version of VM + +# Compiler specific DEBUG_CFLAGS are passed in from gcc.make, sparcWorks.make +DEBUG_CFLAGS/DEFAULT= $(DEBUG_CFLAGS) +DEBUG_CFLAGS/BYFILE = $(DEBUG_CFLAGS/$@)$(DEBUG_CFLAGS/DEFAULT$(DEBUG_CFLAGS/$@)) +CFLAGS += $(DEBUG_CFLAGS/BYFILE) + +# Linker mapfile +MAPFILE = $(GAMMADIR)/build/linux/makefiles/mapfile-vers-debug + +_JUNK_ := $(shell echo -e >&2 ""\ + "----------------------------------------------------------------------\n" \ + "WARNING: 'make debug' is deprecated. It will be removed in the future.\n" \ + "Please use 'make jvmg' to build debug JVM. \n" \ + "----------------------------------------------------------------------\n") + +G_SUFFIX = +VERSION = debug +SYSDEFS += -DASSERT -DDEBUG +PICFLAGS = DEFAULT diff --git a/hotspot/build/linux/makefiles/defs.make b/hotspot/build/linux/makefiles/defs.make new file mode 100644 index 00000000000..7a06eb51276 --- /dev/null +++ b/hotspot/build/linux/makefiles/defs.make @@ -0,0 +1,113 @@ +# +# Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# The common definitions for hotspot linux builds. +# Include the top level defs.make under make directory instead of this one. +# This file is included into make/defs.make. + +SLASH_JAVA ?= /java + +# Need PLATFORM (os-arch combo names) for jdk and hotspot, plus libarch name +ARCH:=$(shell uname -m) +PATH_SEP = : +ifeq ($(LP64), 1) + ARCH_DATA_MODEL ?= 64 +else + ARCH_DATA_MODEL ?= 32 +endif + +# ia64 +ifeq ($(ARCH), ia64) + ARCH_DATA_MODEL = 64 + MAKE_ARGS += LP64=1 + PLATFORM = linux-ia64 + VM_PLATFORM = linux_ia64 + HS_ARCH = ia64 +endif + +# sparc +ifeq ($(ARCH), sparc64) + ifeq ($(ARCH_DATA_MODEL), 64) + ARCH_DATA_MODEL = 64 + MAKE_ARGS += LP64=1 + PLATFORM = linux-sparcv9 + VM_PLATFORM = linux_sparcv9 + else + ARCH_DATA_MODEL = 32 + PLATFORM = linux-sparc + VM_PLATFORM = linux_sparc + endif + HS_ARCH = sparc +endif + +# x86_64 +ifeq ($(ARCH), x86_64) + ifeq ($(ARCH_DATA_MODEL), 64) + ARCH_DATA_MODEL = 64 + MAKE_ARGS += LP64=1 + PLATFORM = linux-amd64 + VM_PLATFORM = linux_amd64 + HS_ARCH = x86 + else + ARCH_DATA_MODEL = 32 + PLATFORM = linux-i586 + VM_PLATFORM = linux_i486 + HS_ARCH = x86 + # We have to reset ARCH to i686 since SRCARCH relies on it + ARCH = i686 + endif +endif + +# i686 +ifeq ($(ARCH), i686) + ARCH_DATA_MODEL = 32 + PLATFORM = linux-i586 + VM_PLATFORM = linux_i486 + HS_ARCH = x86 +endif + +JDK_INCLUDE_SUBDIR=linux + +# FIXUP: The subdirectory for a debug build is NOT the same on all platforms +VM_DEBUG=jvmg + +EXPORT_LIST += $(EXPORT_DOCS_DIR)/platform/jvmti/jvmti.html +EXPORT_SERVER_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/server +EXPORT_LIST += $(EXPORT_SERVER_DIR)/Xusage.txt +EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjsig.so +EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm.so +ifeq ($(ARCH_DATA_MODEL), 32) + EXPORT_CLIENT_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/client + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/Xusage.txt + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjsig.so + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm.so + EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.so + EXPORT_LIST += $(EXPORT_LIB_DIR)/sa-jdi.jar +else + ifeq ($(ARCH),ia64) + else + EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.so + EXPORT_LIST += $(EXPORT_LIB_DIR)/sa-jdi.jar + endif +endif diff --git a/hotspot/build/linux/makefiles/dtrace.make b/hotspot/build/linux/makefiles/dtrace.make new file mode 100644 index 00000000000..ee02f7a7d7c --- /dev/null +++ b/hotspot/build/linux/makefiles/dtrace.make @@ -0,0 +1,27 @@ +# +# Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Linux does not build jvm_db +LIBJVM_DB = + diff --git a/hotspot/build/linux/makefiles/fastdebug.make b/hotspot/build/linux/makefiles/fastdebug.make new file mode 100644 index 00000000000..628f8bb99ba --- /dev/null +++ b/hotspot/build/linux/makefiles/fastdebug.make @@ -0,0 +1,64 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making debug version of VM + +# Compiler specific OPT_CFLAGS are passed in from gcc.make, sparcWorks.make +OPT_CFLAGS/DEFAULT= $(OPT_CFLAGS) +OPT_CFLAGS/BYFILE = $(OPT_CFLAGS/$@)$(OPT_CFLAGS/DEFAULT$(OPT_CFLAGS/$@)) + +# (OPT_CFLAGS/SLOWER is also available, to alter compilation of buggy files) + +ifeq ($(BUILDARCH), ia64) + # Bug in GCC, causes hang. -O1 will override the -O3 specified earlier + OPT_CFLAGS/callGenerator.o += -O1 + OPT_CFLAGS/ciTypeFlow.o += -O1 + OPT_CFLAGS/compile.o += -O1 + OPT_CFLAGS/concurrentMarkSweepGeneration.o += -O1 + OPT_CFLAGS/doCall.o += -O1 + OPT_CFLAGS/generateOopMap.o += -O1 + OPT_CFLAGS/generateOptoStub.o += -O1 + OPT_CFLAGS/graphKit.o += -O1 + OPT_CFLAGS/instanceKlass.o += -O1 + OPT_CFLAGS/interpreterRT_ia64.o += -O1 + OPT_CFLAGS/output.o += -O1 + OPT_CFLAGS/parse1.o += -O1 + OPT_CFLAGS/runtime.o += -O1 + OPT_CFLAGS/synchronizer.o += -O1 +endif + + +# If you set HOTSPARC_GENERIC=yes, you disable all OPT_CFLAGS settings +CFLAGS$(HOTSPARC_GENERIC) += $(OPT_CFLAGS/BYFILE) + +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +# Linker mapfile +MAPFILE = $(GAMMADIR)/build/linux/makefiles/mapfile-vers-debug + +G_SUFFIX = +VERSION = optimized +SYSDEFS += -DASSERT -DFASTDEBUG +PICFLAGS = DEFAULT diff --git a/hotspot/build/linux/makefiles/gcc.make b/hotspot/build/linux/makefiles/gcc.make new file mode 100644 index 00000000000..66173061293 --- /dev/null +++ b/hotspot/build/linux/makefiles/gcc.make @@ -0,0 +1,158 @@ +# +# Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +#------------------------------------------------------------------------ +# CC, CPP & AS + +CPP = g++ +CC = gcc +AS = $(CC) -c + +# -dumpversion in gcc-2.91 shows "egcs-2.91.66". In later version, it only +# prints the numbers (e.g. "2.95", "3.2.1") +CC_VER_MAJOR := $(shell $(CC) -dumpversion | sed 's/egcs-//' | cut -d'.' -f1) +CC_VER_MINOR := $(shell $(CC) -dumpversion | sed 's/egcs-//' | cut -d'.' -f2) + +# check for precompiled headers support +ifneq "$(shell expr \( $(CC_VER_MAJOR) \> 3 \) \| \( \( $(CC_VER_MAJOR) = 3 \) \& \( $(CC_VER_MINOR) \>= 4 \) \))" "0" +USE_PRECOMPILED_HEADER=1 +PRECOMPILED_HEADER_DIR=. +PRECOMPILED_HEADER=$(PRECOMPILED_HEADER_DIR)/incls/_precompiled.incl.gch +endif + + +#------------------------------------------------------------------------ +# Compiler flags + +# position-independent code +PICFLAG = -fPIC + +VM_PICFLAG/LIBJVM = $(PICFLAG) +VM_PICFLAG/AOUT = + +ifneq ($(BUILDARCH), i486) +VM_PICFLAG = $(VM_PICFLAG/$(LINK_INTO)) +else +# PIC has significant overhead on x86, build nonpic VM for now. +# Link JVM at a "good" base location to avoid unnecessary .text patching. +JVM_BASE_ADDR = 0x06000000 +endif + +CFLAGS += $(VM_PICFLAG) +CFLAGS += -fno-rtti +CFLAGS += -fno-exceptions +CFLAGS += -D_REENTRANT +CFLAGS += -fcheck-new + +ARCHFLAG = $(ARCHFLAG/$(BUILDARCH)) +ARCHFLAG/i486 = -m32 -march=i586 +ARCHFLAG/amd64 = -m64 +ARCHFLAG/ia64 = +ARCHFLAG/sparc = -m32 -mcpu=v9 +ARCHFLAG/sparcv9 = -m64 -mcpu=v9 + +CFLAGS += $(ARCHFLAG) +AOUT_FLAGS += $(ARCHFLAG) +LFLAGS += $(ARCHFLAG) +ASFLAGS += $(ARCHFLAG) + +# Use C++ Interpreter +ifdef CC_INTERP + CFLAGS += -DCC_INTERP +endif + +# Keep temporary files (.ii, .s) +ifdef NEED_ASM + CFLAGS += -save-temps +else + CFLAGS += -pipe +endif + +# Compiler warnings are treated as errors +WARNINGS_ARE_ERRORS = -Werror +# Except for a few acceptable ones +ACCEPTABLE_WARNINGS = -Wpointer-arith -Wconversion -Wsign-compare +CFLAGS_WARN/DEFAULT = $(WARNINGS_ARE_ERRORS) $(ACCEPTABLE_WARNINGS) +# Special cases +CFLAGS_WARN/BYFILE = $(CFLAGS_WARN/$@)$(CFLAGS_WARN/DEFAULT$(CFLAGS_WARN/$@)) + +# The flags to use for an Optimized g++ build +OPT_CFLAGS += -O3 + +# Hotspot uses very unstrict aliasing turn this optimization off +OPT_CFLAGS += -fno-strict-aliasing + +# The gcc compiler segv's on ia64 when compiling bytecodeInterpreter.cpp +# if we use expensive-optimizations +ifeq ($(BUILDARCH), ia64) +OPT_CFLAGS += -fno-expensive-optimizations +endif + +OPT_CFLAGS/NOOPT=-O0 + +#------------------------------------------------------------------------ +# Linker flags + +# statically link libstdc++.so, work with gcc but ignored by g++ +STATIC_STDCXX = -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic + +# statically link libgcc and/or libgcc_s, libgcc does not exist before gcc-3.x. +ifneq ("${CC_VER_MAJOR}", "2") +STATIC_LIBGCC += -static-libgcc +endif + +ifeq ($(BUILDARCH), ia64) +LFLAGS += -Wl,-relax +endif + +# Enable linker optimization +LFLAGS += -Xlinker -O1 + +# Use $(MAPFLAG:FILENAME=real_file_name) to specify a map file. +MAPFLAG = -Xlinker --version-script=FILENAME + +# Use $(SONAMEFLAG:SONAME=soname) to specify the intrinsic name of a shared obj +SONAMEFLAG = -Xlinker -soname=SONAME + +# Build shared library +SHARED_FLAG = -shared + +# Keep symbols even they are not used +AOUT_FLAGS += -export-dynamic + +#------------------------------------------------------------------------ +# Debug flags + +# Use the stabs format for debugging information (this is the default +# on gcc-2.91). It's good enough, has all the information about line +# numbers and local variables, and libjvm_g.so is only about 16M. +# Change this back to "-g" if you want the most expressive format. +# (warning: that could easily inflate libjvm_g.so to 150M!) +# Note: The Itanium gcc compiler crashes when using -gstabs. +DEBUG_CFLAGS/ia64 = -g +DEBUG_CFLAGS/amd64 = -g +DEBUG_CFLAGS += $(DEBUG_CFLAGS/$(BUILDARCH)) +ifeq ($(DEBUG_CFLAGS/$(BUILDARCH)),) +DEBUG_CFLAGS += -gstabs +endif diff --git a/hotspot/build/linux/makefiles/hp.make b/hotspot/build/linux/makefiles/hp.make new file mode 100644 index 00000000000..5ead8272c16 --- /dev/null +++ b/hotspot/build/linux/makefiles/hp.make @@ -0,0 +1,29 @@ +# +# Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making premium version of VM + +TYPE=HP + +CFLAGS += -DCOMPILER2 diff --git a/hotspot/build/linux/makefiles/hp1.make b/hotspot/build/linux/makefiles/hp1.make new file mode 100644 index 00000000000..7d62469a7f9 --- /dev/null +++ b/hotspot/build/linux/makefiles/hp1.make @@ -0,0 +1,29 @@ +# +# Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making premium version of VM + +TYPE=HP1 + +CFLAGS += -DCOMPILER1 diff --git a/hotspot/build/linux/makefiles/i486.make b/hotspot/build/linux/makefiles/i486.make new file mode 100644 index 00000000000..63b67762f34 --- /dev/null +++ b/hotspot/build/linux/makefiles/i486.make @@ -0,0 +1,36 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# TLS helper, assembled from .s file +# Not included in includeDB because it has no dependencies +Obj_Files += linux_x86_32.o + +# The copied fdlibm routines in sharedRuntimeTrig.o must not be optimized +OPT_CFLAGS/sharedRuntimeTrig.o = $(OPT_CFLAGS/NOOPT) +# The copied fdlibm routines in sharedRuntimeTrans.o must not be optimized +OPT_CFLAGS/sharedRuntimeTrans.o = $(OPT_CFLAGS/NOOPT) +# Must also specify if CPU is little endian +CFLAGS += -DVM_LITTLE_ENDIAN + +OPT_CFLAGS/compactingPermGenGen.o = -O1 diff --git a/hotspot/build/linux/makefiles/jsig.make b/hotspot/build/linux/makefiles/jsig.make new file mode 100644 index 00000000000..32d3c2a18d6 --- /dev/null +++ b/hotspot/build/linux/makefiles/jsig.make @@ -0,0 +1,53 @@ +# +# Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Rules to build signal interposition library, used by vm.make + +# libjsig[_g].so: signal interposition library +JSIG = jsig$(G_SUFFIX) +LIBJSIG = lib$(JSIG).so + +JSIGSRCDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/vm + +DEST_JSIG = $(JDK_LIBDIR)/$(LIBJSIG) + +LIBJSIG_MAPFILE = $(MAKEFILES_DIR)/mapfile-vers-jsig + +# On Linux we really dont want a mapfile, as this library is small +# and preloaded using LD_PRELOAD, making functions private will +# cause problems with interposing. See CR: 6466665 +# LFLAGS_JSIG += $(MAPFLAG:FILENAME=$(LIBJSIG_MAPFILE)) + +LFLAGS_JSIG += -D_GNU_SOURCE -D_REENTRANT + +$(LIBJSIG): $(JSIGSRCDIR)/jsig.c $(LIBJSIG_MAPFILE) + @echo Making signal interposition lib... + $(QUIETLY) $(CC) $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ + $(LFLAGS_JSIG) -o $@ $< -ldl + +install_jsig: $(LIBJSIG) + @echo "Copying $(LIBJSIG) to $(DEST_JSIG)" + $(QUIETLY) cp -f $(LIBJSIG) $(DEST_JSIG) && echo "Done" + +.PHONY: install_jsig diff --git a/hotspot/build/linux/makefiles/jvmg.make b/hotspot/build/linux/makefiles/jvmg.make new file mode 100644 index 00000000000..dcdb2e82bb5 --- /dev/null +++ b/hotspot/build/linux/makefiles/jvmg.make @@ -0,0 +1,41 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making debug version of VM + +# Compiler specific DEBUG_CFLAGS are passed in from gcc.make, sparcWorks.make +DEBUG_CFLAGS/DEFAULT= $(DEBUG_CFLAGS) +DEBUG_CFLAGS/BYFILE = $(DEBUG_CFLAGS/$@)$(DEBUG_CFLAGS/DEFAULT$(DEBUG_CFLAGS/$@)) +CFLAGS += $(DEBUG_CFLAGS/BYFILE) + +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +# Linker mapfile +MAPFILE = $(GAMMADIR)/build/linux/makefiles/mapfile-vers-debug + +G_SUFFIX = +VERSION = debug +SYSDEFS += -DASSERT -DDEBUG +PICFLAGS = DEFAULT diff --git a/hotspot/build/linux/makefiles/jvmti.make b/hotspot/build/linux/makefiles/jvmti.make new file mode 100644 index 00000000000..d00e773aea1 --- /dev/null +++ b/hotspot/build/linux/makefiles/jvmti.make @@ -0,0 +1,118 @@ +# +# Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This makefile (jvmti.make) is included from the jvmti.make in the +# build directories. +# +# It knows how to build and run the tools to generate jvmti. + +include $(GAMMADIR)/build/linux/makefiles/rules.make + +# ######################################################################### + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated +JvmtiOutDir = $(GENERATED)/jvmtifiles + +JvmtiSrcDir = $(GAMMADIR)/src/share/vm/prims +InterpreterSrcDir = $(GAMMADIR)/src/share/vm/interpreter +Src_Dirs += $(JvmtiSrcDir) + +# set VPATH so make knows where to look for source files +Src_Dirs_V = ${Src_Dirs} +VPATH += $(Src_Dirs_V:%=%:) + +JvmtiGeneratedNames = \ + jvmtiEnv.hpp \ + jvmtiEnter.cpp \ + jvmtiEnterTrace.cpp \ + jvmtiEnvRecommended.cpp \ + bytecodeInterpreterWithChecks.cpp \ + jvmti.h \ + +JvmtiEnvFillSource = $(JvmtiSrcDir)/jvmtiEnvFill.java +JvmtiEnvFillClass = $(JvmtiOutDir)/jvmtiEnvFill.class + +JvmtiGenSource = $(JvmtiSrcDir)/jvmtiGen.java +JvmtiGenClass = $(JvmtiOutDir)/jvmtiGen.class + +JvmtiGeneratedFiles = $(JvmtiGeneratedNames:%=$(JvmtiOutDir)/%) + +XSLT = $(QUIETLY) $(REMOTE) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiGen + +.PHONY: all jvmtidocs clean cleanall + +# ######################################################################### + +all: $(JvmtiGeneratedFiles) + +both = $(JvmtiGenClass) $(JvmtiSrcDir)/jvmti.xml $(JvmtiSrcDir)/jvmtiLib.xsl + +$(JvmtiGenClass): $(JvmtiGenSource) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -g -d $(JvmtiOutDir) $(JvmtiGenSource) + +$(JvmtiEnvFillClass): $(JvmtiEnvFillSource) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -g -d $(JvmtiOutDir) $(JvmtiEnvFillSource) + +$(JvmtiOutDir)/jvmtiEnter.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnter.xsl -OUT $(JvmtiOutDir)/jvmtiEnter.cpp -PARAM interface jvmti + +$(JvmtiOutDir)/bytecodeInterpreterWithChecks.cpp: $(JvmtiGenClass) $(InterpreterSrcDir)/bytecodeInterpreter.cpp $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xml $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xsl + @echo Generating $@ + $(XSLT) -IN $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xml -XSL $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xsl -OUT $(JvmtiOutDir)/bytecodeInterpreterWithChecks.cpp + +$(JvmtiOutDir)/jvmtiEnterTrace.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnter.xsl -OUT $(JvmtiOutDir)/jvmtiEnterTrace.cpp -PARAM interface jvmti -PARAM trace Trace + +$(JvmtiOutDir)/jvmtiEnvRecommended.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnv.xsl $(JvmtiSrcDir)/jvmtiEnv.cpp $(JvmtiEnvFillClass) + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnv.xsl -OUT $(JvmtiOutDir)/jvmtiEnvStub.cpp + $(QUIETLY) $(REMOTE) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiEnvFill $(JvmtiSrcDir)/jvmtiEnv.cpp $(JvmtiOutDir)/jvmtiEnvStub.cpp $(JvmtiOutDir)/jvmtiEnvRecommended.cpp + +$(JvmtiOutDir)/jvmtiEnv.hpp: $(both) $(JvmtiSrcDir)/jvmtiHpp.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiHpp.xsl -OUT $(JvmtiOutDir)/jvmtiEnv.hpp + +$(JvmtiOutDir)/jvmti.h: $(both) $(JvmtiSrcDir)/jvmtiH.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiH.xsl -OUT $(JvmtiOutDir)/jvmti.h + +jvmtidocs: $(JvmtiOutDir)/jvmti.html + +$(JvmtiOutDir)/jvmti.html: $(both) $(JvmtiSrcDir)/jvmti.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmti.xsl -OUT $(JvmtiOutDir)/jvmti.html + +# ######################################################################### + +clean : + rm $(JvmtiGenClass) $(JvmtiEnvFillClass) $(JvmtiGeneratedFiles) + +cleanall : + rm $(JvmtiGenClass) $(JvmtiEnvFillClass) $(JvmtiGeneratedFiles) + +# ######################################################################### + diff --git a/hotspot/build/linux/makefiles/launcher.make b/hotspot/build/linux/makefiles/launcher.make new file mode 100644 index 00000000000..7284ce3a09e --- /dev/null +++ b/hotspot/build/linux/makefiles/launcher.make @@ -0,0 +1,73 @@ +# +# Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Rules to build gamma launcher, used by vm.make + +# gamma[_g]: launcher +LAUNCHER = gamma$(G_SUFFIX) + +LAUNCHERDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/launcher +LAUNCHERFLAGS = $(ARCHFLAG) \ + -I$(LAUNCHERDIR) -I$(GAMMADIR)/src/share/vm/prims \ + -DFULL_VERSION=\"$(HOTSPOT_RELEASE_VERSION)\" \ + -DARCH=\"$(LIBARCH)\" \ + -DGAMMA \ + -DLAUNCHER_TYPE=\"gamma\" \ + -DLINK_INTO_$(LINK_INTO) + +ifeq ($(LINK_INTO),AOUT) + LAUNCHER.o = launcher.o $(JVM_OBJ_FILES) + LAUNCHER_MAPFILE = mapfile_reorder + LFLAGS_LAUNCHER$(LDNOMAP) += $(MAPFLAG:FILENAME=$(LAUNCHER_MAPFILE)) + LFLAGS_LAUNCHER += $(SONAMEFLAG:SONAME=$(LIBJVM)) $(STATIC_LIBGCC) + LIBS_LAUNCHER += $(STATIC_STDCXX) $(LIBS) +else + LAUNCHER.o = launcher.o + LFLAGS_LAUNCHER += -L `pwd` + LIBS_LAUNCHER += -l$(JVM) $(LIBS) +endif + +LINK_LAUNCHER = $(LINK.c) + +LINK_LAUNCHER/PRE_HOOK = $(LINK_LIB.CC/PRE_HOOK) +LINK_LAUNCHER/POST_HOOK = $(LINK_LIB.CC/POST_HOOK) + +launcher.o: launcher.c $(LAUNCHERDIR)/java.c $(LAUNCHERDIR)/java_md.c + $(CC) -g -c -o $@ launcher.c $(LAUNCHERFLAGS) $(CPPFLAGS) + +launcher.c: + @echo Generating $@ + $(QUIETLY) { \ + echo '#define debug launcher_debug'; \ + echo '#include "java.c"'; \ + echo '#include "java_md.c"'; \ + } > $@ + +$(LAUNCHER): $(LAUNCHER.o) $(LIBJVM) $(LAUNCHER_MAPFILE) + $(QUIETLY) { \ + echo Linking launcher...; \ + $(LINK_LAUNCHER/PRE_HOOK) \ + $(LINK_LAUNCHER) $(LFLAGS_LAUNCHER) -o $@ $(LAUNCHER.o) $(LIBS_LAUNCHER); \ + $(LINK_LAUNCHER/POST_HOOK) \ + } diff --git a/hotspot/build/linux/makefiles/makedeps.make b/hotspot/build/linux/makefiles/makedeps.make new file mode 100644 index 00000000000..d503926456e --- /dev/null +++ b/hotspot/build/linux/makefiles/makedeps.make @@ -0,0 +1,43 @@ +# +# Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +include $(GAMMADIR)/build/linux/makefiles/rules.make + +COMPILE.JAVAC.FLAGS += -d $(OUTDIR) + +MakeDepsSources=\ + $(GAMMADIR)/src/share/tools/MakeDeps/Database.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/DirectoryTree.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/DirectoryTreeNode.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/FileFormatException.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/FileList.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/FileName.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/Macro.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/MacroDefinitions.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/MakeDeps.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/MetroWerksMacPlatform.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/Platform.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/UnixPlatform.java + +MakeDepsOptions= diff --git a/hotspot/build/linux/makefiles/mapfile-vers-debug b/hotspot/build/linux/makefiles/mapfile-vers-debug new file mode 100644 index 00000000000..3f73de30725 --- /dev/null +++ b/hotspot/build/linux/makefiles/mapfile-vers-debug @@ -0,0 +1,285 @@ +# + +# +# Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + + # JVM + JVM_Accept; + JVM_ActiveProcessorCount; + JVM_AllocateNewArray; + JVM_AllocateNewObject; + JVM_ArrayCopy; + JVM_AssertionStatusDirectives; + JVM_Available; + JVM_Bind; + JVM_ClassDepth; + JVM_ClassLoaderDepth; + JVM_Clone; + JVM_Close; + JVM_CX8Field; + JVM_CompileClass; + JVM_CompileClasses; + JVM_CompilerCommand; + JVM_Connect; + JVM_ConstantPoolGetClassAt; + JVM_ConstantPoolGetClassAtIfLoaded; + JVM_ConstantPoolGetDoubleAt; + JVM_ConstantPoolGetFieldAt; + JVM_ConstantPoolGetFieldAtIfLoaded; + JVM_ConstantPoolGetFloatAt; + JVM_ConstantPoolGetIntAt; + JVM_ConstantPoolGetLongAt; + JVM_ConstantPoolGetMethodAt; + JVM_ConstantPoolGetMethodAtIfLoaded; + JVM_ConstantPoolGetMemberRefInfoAt; + JVM_ConstantPoolGetSize; + JVM_ConstantPoolGetStringAt; + JVM_ConstantPoolGetUTF8At; + JVM_CountStackFrames; + JVM_CurrentClassLoader; + JVM_CurrentLoadedClass; + JVM_CurrentThread; + JVM_CurrentTimeMillis; + JVM_DefineClass; + JVM_DefineClassWithSource; + JVM_DesiredAssertionStatus; + JVM_DisableCompiler; + JVM_DoPrivileged; + JVM_DumpAllStacks; + JVM_DumpThreads; + JVM_EnableCompiler; + JVM_Exit; + JVM_FillInStackTrace; + JVM_FindClassFromClass; + JVM_FindClassFromClassLoader; + JVM_FindLibraryEntry; + JVM_FindLoadedClass; + JVM_FindPrimitiveClass; + JVM_FindSignal; + JVM_FreeMemory; + JVM_GC; + JVM_GetAllThreads; + JVM_GetArrayElement; + JVM_GetArrayLength; + JVM_GetCPClassNameUTF; + JVM_GetCPFieldClassNameUTF; + JVM_GetCPFieldModifiers; + JVM_GetCPFieldNameUTF; + JVM_GetCPFieldSignatureUTF; + JVM_GetCPMethodClassNameUTF; + JVM_GetCPMethodModifiers; + JVM_GetCPMethodNameUTF; + JVM_GetCPMethodSignatureUTF; + JVM_GetCallerClass; + JVM_GetClassAccessFlags; + JVM_GetClassAnnotations; + JVM_GetClassCPEntriesCount; + JVM_GetClassCPTypes; + JVM_GetClassConstantPool; + JVM_GetClassContext; + JVM_GetClassDeclaredConstructors; + JVM_GetClassDeclaredFields; + JVM_GetClassDeclaredMethods; + JVM_GetClassFieldsCount; + JVM_GetClassInterfaces; + JVM_GetClassLoader; + JVM_GetClassMethodsCount; + JVM_GetClassModifiers; + JVM_GetClassName; + JVM_GetClassNameUTF; + JVM_GetClassSignature; + JVM_GetClassSigners; + JVM_GetComponentType; + JVM_GetDeclaredClasses; + JVM_GetDeclaringClass; + JVM_GetEnclosingMethodInfo; + JVM_GetFieldAnnotations; + JVM_GetFieldIxModifiers; + JVM_GetHostName; + JVM_GetInheritedAccessControlContext; + JVM_GetInterfaceVersion; + JVM_GetLastErrorString; + JVM_GetManagement; + JVM_GetMethodAnnotations; + JVM_GetMethodDefaultAnnotationValue; + JVM_GetMethodIxArgsSize; + JVM_GetMethodIxByteCode; + JVM_GetMethodIxByteCodeLength; + JVM_GetMethodIxExceptionIndexes; + JVM_GetMethodIxExceptionTableEntry; + JVM_GetMethodIxExceptionTableLength; + JVM_GetMethodIxExceptionsCount; + JVM_GetMethodIxLocalsCount; + JVM_GetMethodIxMaxStack; + JVM_GetMethodIxModifiers; + JVM_GetMethodIxNameUTF; + JVM_GetMethodIxSignatureUTF; + JVM_GetMethodParameterAnnotations; + JVM_GetPrimitiveArrayElement; + JVM_GetProtectionDomain; + JVM_GetSockName; + JVM_GetSockOpt; + JVM_GetStackAccessControlContext; + JVM_GetStackTraceDepth; + JVM_GetStackTraceElement; + JVM_GetSystemPackage; + JVM_GetSystemPackages; + JVM_GetThreadStateNames; + JVM_GetThreadStateValues; + JVM_GetVersionInfo; + JVM_Halt; + JVM_HoldsLock; + JVM_IHashCode; + JVM_InitAgentProperties; + JVM_InitProperties; + JVM_InitializeCompiler; + JVM_InitializeSocketLibrary; + JVM_InternString; + JVM_Interrupt; + JVM_InvokeMethod; + JVM_IsArrayClass; + JVM_IsConstructorIx; + JVM_IsInterface; + JVM_IsInterrupted; + JVM_IsNaN; + JVM_IsPrimitiveClass; + JVM_IsSameClassPackage; + JVM_IsSilentCompiler; + JVM_IsSupportedJNIVersion; + JVM_IsThreadAlive; + JVM_LatestUserDefinedLoader; + JVM_Listen; + JVM_LoadClass0; + JVM_LoadLibrary; + JVM_Lseek; + JVM_MaxObjectInspectionAge; + JVM_MaxMemory; + JVM_MonitorNotify; + JVM_MonitorNotifyAll; + JVM_MonitorWait; + JVM_NanoTime; + JVM_NativePath; + JVM_NewArray; + JVM_NewInstanceFromConstructor; + JVM_NewMultiArray; + JVM_OnExit; + JVM_Open; + JVM_PrintStackTrace; + JVM_RaiseSignal; + JVM_RawMonitorCreate; + JVM_RawMonitorDestroy; + JVM_RawMonitorEnter; + JVM_RawMonitorExit; + JVM_Read; + JVM_Recv; + JVM_RecvFrom; + JVM_RegisterSignal; + JVM_ReleaseUTF; + JVM_ResolveClass; + JVM_ResumeThread; + JVM_Send; + JVM_SendTo; + JVM_SetArrayElement; + JVM_SetClassSigners; + JVM_SetLength; + JVM_SetPrimitiveArrayElement; + JVM_SetProtectionDomain; + JVM_SetSockOpt; + JVM_SetThreadPriority; + JVM_Sleep; + JVM_Socket; + JVM_SocketAvailable; + JVM_SocketClose; + JVM_SocketShutdown; + JVM_StartThread; + JVM_StopThread; + JVM_SuspendThread; + JVM_SupportsCX8; + JVM_Sync; + JVM_Timeout; + JVM_TotalMemory; + JVM_TraceInstructions; + JVM_TraceMethodCalls; + JVM_UnloadLibrary; + JVM_Write; + JVM_Yield; + JVM_handle_linux_signal; + + # Old reflection routines + # These do not need to be present in the product build in JDK 1.4 + # but their code has not been removed yet because there will not + # be a substantial code savings until JVM_InvokeMethod and + # JVM_NewInstanceFromConstructor can also be removed; see + # reflectionCompat.hpp. + JVM_GetClassConstructor; + JVM_GetClassConstructors; + JVM_GetClassField; + JVM_GetClassFields; + JVM_GetClassMethod; + JVM_GetClassMethods; + JVM_GetField; + JVM_GetPrimitiveField; + JVM_NewInstance; + JVM_SetField; + JVM_SetPrimitiveField; + + # Needed for dropping VM into JDK 1.3.x, 1.4 + _JVM_native_threads; + jdk_sem_init; + jdk_sem_post; + jdk_sem_wait; + jdk_pthread_sigmask; + jdk_waitpid; + + # debug JVM + JVM_AccessVMBooleanFlag; + JVM_AccessVMIntFlag; + JVM_VMBreakPoint; + + # miscellaneous functions + jio_fprintf; + jio_printf; + jio_snprintf; + jio_vfprintf; + jio_vsnprintf; + fork1; + + # Needed because there is no JVM interface for this. + sysThreadAvailableStackWithSlack; + + # This is for Forte Analyzer profiling support. + AsyncGetCallTrace; + local: + *; +}; + diff --git a/hotspot/build/linux/makefiles/mapfile-vers-jsig b/hotspot/build/linux/makefiles/mapfile-vers-jsig new file mode 100644 index 00000000000..f7ae7fb22b9 --- /dev/null +++ b/hotspot/build/linux/makefiles/mapfile-vers-jsig @@ -0,0 +1,40 @@ +# + +# +# Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define library interface. + +SUNWprivate_1.1 { + global: + JVM_begin_signal_setting; + JVM_end_signal_setting; + JVM_get_libjsig_version; + JVM_get_signal_action; + sigaction; + signal; + sigset; + local: + *; +}; diff --git a/hotspot/build/linux/makefiles/mapfile-vers-product b/hotspot/build/linux/makefiles/mapfile-vers-product new file mode 100644 index 00000000000..df2994fcd49 --- /dev/null +++ b/hotspot/build/linux/makefiles/mapfile-vers-product @@ -0,0 +1,280 @@ +# + +# +# Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + + # JVM + JVM_Accept; + JVM_ActiveProcessorCount; + JVM_AllocateNewArray; + JVM_AllocateNewObject; + JVM_ArrayCopy; + JVM_AssertionStatusDirectives; + JVM_Available; + JVM_Bind; + JVM_ClassDepth; + JVM_ClassLoaderDepth; + JVM_Clone; + JVM_Close; + JVM_CX8Field; + JVM_CompileClass; + JVM_CompileClasses; + JVM_CompilerCommand; + JVM_Connect; + JVM_ConstantPoolGetClassAt; + JVM_ConstantPoolGetClassAtIfLoaded; + JVM_ConstantPoolGetDoubleAt; + JVM_ConstantPoolGetFieldAt; + JVM_ConstantPoolGetFieldAtIfLoaded; + JVM_ConstantPoolGetFloatAt; + JVM_ConstantPoolGetIntAt; + JVM_ConstantPoolGetLongAt; + JVM_ConstantPoolGetMethodAt; + JVM_ConstantPoolGetMethodAtIfLoaded; + JVM_ConstantPoolGetMemberRefInfoAt; + JVM_ConstantPoolGetSize; + JVM_ConstantPoolGetStringAt; + JVM_ConstantPoolGetUTF8At; + JVM_CountStackFrames; + JVM_CurrentClassLoader; + JVM_CurrentLoadedClass; + JVM_CurrentThread; + JVM_CurrentTimeMillis; + JVM_DefineClass; + JVM_DefineClassWithSource; + JVM_DesiredAssertionStatus; + JVM_DisableCompiler; + JVM_DoPrivileged; + JVM_DumpAllStacks; + JVM_DumpThreads; + JVM_EnableCompiler; + JVM_Exit; + JVM_FillInStackTrace; + JVM_FindClassFromClass; + JVM_FindClassFromClassLoader; + JVM_FindLibraryEntry; + JVM_FindLoadedClass; + JVM_FindPrimitiveClass; + JVM_FindSignal; + JVM_FreeMemory; + JVM_GC; + JVM_GetAllThreads; + JVM_GetArrayElement; + JVM_GetArrayLength; + JVM_GetCPClassNameUTF; + JVM_GetCPFieldClassNameUTF; + JVM_GetCPFieldModifiers; + JVM_GetCPFieldNameUTF; + JVM_GetCPFieldSignatureUTF; + JVM_GetCPMethodClassNameUTF; + JVM_GetCPMethodModifiers; + JVM_GetCPMethodNameUTF; + JVM_GetCPMethodSignatureUTF; + JVM_GetCallerClass; + JVM_GetClassAccessFlags; + JVM_GetClassAnnotations; + JVM_GetClassCPEntriesCount; + JVM_GetClassCPTypes; + JVM_GetClassConstantPool; + JVM_GetClassContext; + JVM_GetClassDeclaredConstructors; + JVM_GetClassDeclaredFields; + JVM_GetClassDeclaredMethods; + JVM_GetClassFieldsCount; + JVM_GetClassInterfaces; + JVM_GetClassLoader; + JVM_GetClassMethodsCount; + JVM_GetClassModifiers; + JVM_GetClassName; + JVM_GetClassNameUTF; + JVM_GetClassSignature; + JVM_GetClassSigners; + JVM_GetComponentType; + JVM_GetDeclaredClasses; + JVM_GetDeclaringClass; + JVM_GetEnclosingMethodInfo; + JVM_GetFieldAnnotations; + JVM_GetFieldIxModifiers; + JVM_GetHostName; + JVM_GetInheritedAccessControlContext; + JVM_GetInterfaceVersion; + JVM_GetLastErrorString; + JVM_GetManagement; + JVM_GetMethodAnnotations; + JVM_GetMethodDefaultAnnotationValue; + JVM_GetMethodIxArgsSize; + JVM_GetMethodIxByteCode; + JVM_GetMethodIxByteCodeLength; + JVM_GetMethodIxExceptionIndexes; + JVM_GetMethodIxExceptionTableEntry; + JVM_GetMethodIxExceptionTableLength; + JVM_GetMethodIxExceptionsCount; + JVM_GetMethodIxLocalsCount; + JVM_GetMethodIxMaxStack; + JVM_GetMethodIxModifiers; + JVM_GetMethodIxNameUTF; + JVM_GetMethodIxSignatureUTF; + JVM_GetMethodParameterAnnotations; + JVM_GetPrimitiveArrayElement; + JVM_GetProtectionDomain; + JVM_GetSockName; + JVM_GetSockOpt; + JVM_GetStackAccessControlContext; + JVM_GetStackTraceDepth; + JVM_GetStackTraceElement; + JVM_GetSystemPackage; + JVM_GetSystemPackages; + JVM_GetThreadStateNames; + JVM_GetThreadStateValues; + JVM_GetVersionInfo; + JVM_Halt; + JVM_HoldsLock; + JVM_IHashCode; + JVM_InitAgentProperties; + JVM_InitProperties; + JVM_InitializeCompiler; + JVM_InitializeSocketLibrary; + JVM_InternString; + JVM_Interrupt; + JVM_InvokeMethod; + JVM_IsArrayClass; + JVM_IsConstructorIx; + JVM_IsInterface; + JVM_IsInterrupted; + JVM_IsNaN; + JVM_IsPrimitiveClass; + JVM_IsSameClassPackage; + JVM_IsSilentCompiler; + JVM_IsSupportedJNIVersion; + JVM_IsThreadAlive; + JVM_LatestUserDefinedLoader; + JVM_Listen; + JVM_LoadClass0; + JVM_LoadLibrary; + JVM_Lseek; + JVM_MaxObjectInspectionAge; + JVM_MaxMemory; + JVM_MonitorNotify; + JVM_MonitorNotifyAll; + JVM_MonitorWait; + JVM_NanoTime; + JVM_NativePath; + JVM_NewArray; + JVM_NewInstanceFromConstructor; + JVM_NewMultiArray; + JVM_OnExit; + JVM_Open; + JVM_PrintStackTrace; + JVM_RaiseSignal; + JVM_RawMonitorCreate; + JVM_RawMonitorDestroy; + JVM_RawMonitorEnter; + JVM_RawMonitorExit; + JVM_Read; + JVM_Recv; + JVM_RecvFrom; + JVM_RegisterSignal; + JVM_ReleaseUTF; + JVM_ResolveClass; + JVM_ResumeThread; + JVM_Send; + JVM_SendTo; + JVM_SetArrayElement; + JVM_SetClassSigners; + JVM_SetLength; + JVM_SetPrimitiveArrayElement; + JVM_SetProtectionDomain; + JVM_SetSockOpt; + JVM_SetThreadPriority; + JVM_Sleep; + JVM_Socket; + JVM_SocketAvailable; + JVM_SocketClose; + JVM_SocketShutdown; + JVM_StartThread; + JVM_StopThread; + JVM_SuspendThread; + JVM_SupportsCX8; + JVM_Sync; + JVM_Timeout; + JVM_TotalMemory; + JVM_TraceInstructions; + JVM_TraceMethodCalls; + JVM_UnloadLibrary; + JVM_Write; + JVM_Yield; + JVM_handle_linux_signal; + + # Old reflection routines + # These do not need to be present in the product build in JDK 1.4 + # but their code has not been removed yet because there will not + # be a substantial code savings until JVM_InvokeMethod and + # JVM_NewInstanceFromConstructor can also be removed; see + # reflectionCompat.hpp. + JVM_GetClassConstructor; + JVM_GetClassConstructors; + JVM_GetClassField; + JVM_GetClassFields; + JVM_GetClassMethod; + JVM_GetClassMethods; + JVM_GetField; + JVM_GetPrimitiveField; + JVM_NewInstance; + JVM_SetField; + JVM_SetPrimitiveField; + + # Needed for dropping VM into JDK 1.3.x, 1.4 + _JVM_native_threads; + jdk_sem_init; + jdk_sem_post; + jdk_sem_wait; + jdk_pthread_sigmask; + jdk_waitpid; + + # miscellaneous functions + jio_fprintf; + jio_printf; + jio_snprintf; + jio_vfprintf; + jio_vsnprintf; + fork1; + + # Needed because there is no JVM interface for this. + sysThreadAvailableStackWithSlack; + + # This is for Forte Analyzer profiling support. + AsyncGetCallTrace; + local: + *; +}; + diff --git a/hotspot/build/linux/makefiles/optimized.make b/hotspot/build/linux/makefiles/optimized.make new file mode 100644 index 00000000000..d85775f6c6a --- /dev/null +++ b/hotspot/build/linux/makefiles/optimized.make @@ -0,0 +1,44 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making optimized version of Gamma VM +# (This is the "product", not the "release" version.) + +# Compiler specific OPT_CFLAGS are passed in from gcc.make, sparcWorks.make +OPT_CFLAGS/DEFAULT= $(OPT_CFLAGS) +OPT_CFLAGS/BYFILE = $(OPT_CFLAGS/$@)$(OPT_CFLAGS/DEFAULT$(OPT_CFLAGS/$@)) + +# (OPT_CFLAGS/SLOWER is also available, to alter compilation of buggy files) + +# If you set HOTSPARC_GENERIC=yes, you disable all OPT_CFLAGS settings +CFLAGS$(HOTSPARC_GENERIC) += $(OPT_CFLAGS/BYFILE) + +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +# Linker mapfile +MAPFILE = $(GAMMADIR)/build/linux/makefiles/mapfile-vers-debug + +G_SUFFIX = +VERSION = optimized diff --git a/hotspot/build/linux/makefiles/product.make b/hotspot/build/linux/makefiles/product.make new file mode 100644 index 00000000000..6b6ccab740b --- /dev/null +++ b/hotspot/build/linux/makefiles/product.make @@ -0,0 +1,54 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making optimized version of Gamma VM +# (This is the "product", not the "release" version.) + +# Compiler specific OPT_CFLAGS are passed in from gcc.make, sparcWorks.make +OPT_CFLAGS/DEFAULT= $(OPT_CFLAGS) +OPT_CFLAGS/BYFILE = $(OPT_CFLAGS/$@)$(OPT_CFLAGS/DEFAULT$(OPT_CFLAGS/$@)) + +# (OPT_CFLAGS/SLOWER is also available, to alter compilation of buggy files) + +# If you set HOTSPARC_GENERIC=yes, you disable all OPT_CFLAGS settings +CFLAGS$(HOTSPARC_GENERIC) += $(OPT_CFLAGS/BYFILE) + +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +# Linker mapfile +MAPFILE = $(GAMMADIR)/build/linux/makefiles/mapfile-vers-product + +G_SUFFIX = +SYSDEFS += -DPRODUCT +VERSION = optimized + +# use -g to strip library as -x will discard its symbol table; -x is fine for +# executables. +STRIP = strip +STRIP_LIBJVM = $(STRIP) -g $@ || exit 1; +STRIP_AOUT = $(STRIP) -x $@ || exit 1; + +# Don't strip in VM build; JDK build will strip libraries later +# LINK_LIB.CC/POST_HOOK += $(STRIP_$(LINK_INTO)) diff --git a/hotspot/build/linux/makefiles/profiled.make b/hotspot/build/linux/makefiles/profiled.make new file mode 100644 index 00000000000..c73acc848f8 --- /dev/null +++ b/hotspot/build/linux/makefiles/profiled.make @@ -0,0 +1,30 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making profiled version of Gamma VM +# (It is also optimized.) + +CFLAGS += -pg +AOUT_FLAGS += -pg +LDNOMAP = true diff --git a/hotspot/build/linux/makefiles/rules.make b/hotspot/build/linux/makefiles/rules.make new file mode 100644 index 00000000000..a81633de34a --- /dev/null +++ b/hotspot/build/linux/makefiles/rules.make @@ -0,0 +1,183 @@ +# +# Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Common rules/macros for the vm, adlc. + +# Tell make that .cpp is important +.SUFFIXES: .cpp $(SUFFIXES) + +# For now. Other makefiles use CPP as the c++ compiler, but that should really +# name the preprocessor. +ifeq ($(CCC),) +CCC = $(CPP) +endif + +DEMANGLER = c++filt +DEMANGLE = $(DEMANGLER) < $@ > .$@ && mv -f .$@ $@ + +# $(CC) is the c compiler (cc/gcc), $(CCC) is the c++ compiler (CC/g++). +C_COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) +CC_COMPILE = $(CCC) $(CPPFLAGS) $(CFLAGS) + +AS.S = $(AS) $(ASFLAGS) + +COMPILE.c = $(C_COMPILE) -c +GENASM.c = $(C_COMPILE) -S +LINK.c = $(CC) $(LFLAGS) $(AOUT_FLAGS) $(PROF_AOUT_FLAGS) +LINK_LIB.c = $(CC) $(LFLAGS) $(SHARED_FLAG) +PREPROCESS.c = $(C_COMPILE) -E + +COMPILE.CC = $(CC_COMPILE) -c +GENASM.CC = $(CC_COMPILE) -S +LINK.CC = $(CCC) $(LFLAGS) $(AOUT_FLAGS) $(PROF_AOUT_FLAGS) +LINK_NOPROF.CC = $(CCC) $(LFLAGS) $(AOUT_FLAGS) +LINK_LIB.CC = $(CCC) $(LFLAGS) $(SHARED_FLAG) +PREPROCESS.CC = $(CC_COMPILE) -E + +# Effect of REMOVE_TARGET is to delete out-of-date files during "gnumake -k". +REMOVE_TARGET = rm -f $@ + +# Synonyms. +COMPILE.cpp = $(COMPILE.CC) +GENASM.cpp = $(GENASM.CC) +LINK.cpp = $(LINK.CC) +LINK_LIB.cpp = $(LINK_LIB.CC) +PREPROCESS.cpp = $(PREPROCESS.CC) + +# Note use of ALT_BOOTDIR to explicitly specify location of java and +# javac; this is the same environment variable used in the J2SE build +# process for overriding the default spec, which is BOOTDIR. +# Note also that we fall back to using JAVA_HOME if neither of these is +# specified. + +ifdef ALT_BOOTDIR + +RUN.JAVA = $(ALT_BOOTDIR)/bin/java +RUN.JAVAP = $(ALT_BOOTDIR)/bin/javap +RUN.JAVAH = $(ALT_BOOTDIR)/bin/javah +RUN.JAR = $(ALT_BOOTDIR)/bin/jar +COMPILE.JAVAC = $(ALT_BOOTDIR)/bin/javac +COMPILE.RMIC = $(ALT_BOOTDIR)/bin/rmic +BOOT_JAVA_HOME = $(ALT_BOOTDIR) + +else + +ifdef BOOTDIR + +RUN.JAVA = $(BOOTDIR)/bin/java +RUN.JAVAP = $(BOOTDIR)/bin/javap +RUN.JAVAH = $(BOOTDIR)/bin/javah +RUN.JAR = $(BOOTDIR)/bin/jar +COMPILE.JAVAC = $(BOOTDIR)/bin/javac +COMPILE.RMIC = $(BOOTDIR)/bin/rmic +BOOT_JAVA_HOME = $(BOOTDIR) + +else + +ifdef JAVA_HOME + +RUN.JAVA = $(JAVA_HOME)/bin/java +RUN.JAVAP = $(JAVA_HOME)/bin/javap +RUN.JAVAH = $(JAVA_HOME)/bin/javah +RUN.JAR = $(JAVA_HOME)/bin/jar +COMPILE.JAVAC = $(JAVA_HOME)/bin/javac +COMPILE.RMIC = $(JAVA_HOME)/bin/rmic +BOOT_JAVA_HOME = $(JAVA_HOME) + +else + +# take from the PATH, if ALT_BOOTDIR, BOOTDIR and JAVA_HOME are not defined +# note that this is to support hotspot build without SA. To build +# SA along with hotspot, you need to define ALT_BOOTDIR, BOOTDIR or JAVA_HOME + +RUN.JAVA = java +RUN.JAVAP = javap +RUN.JAVAH = javah +RUN.JAR = jar +COMPILE.JAVAC = javac +COMPILE.RMIC = rmic + +endif +endif +endif + +SUM = /usr/bin/sum + +# 'gmake MAKE_VERBOSE=y' gives all the gory details. +QUIETLY$(MAKE_VERBOSE) = @ +RUN.JAR$(MAKE_VERBOSE) += >/dev/null + +# With parallel makes, print a message at the end of compilation. +ifeq ($(findstring j,$(MFLAGS)),j) +COMPILE_DONE = && { echo Done with $<; } +endif + +%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(COMPILE.CC) -o $@ $< $(COMPILE_DONE) + +%.o: %.s + @echo Assembling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(AS.S) -o $@ $< $(COMPILE_DONE) + +%.s: %.cpp + @echo Generating assembly for $< + $(QUIETLY) $(GENASM.CC) -o $@ $< + $(QUIETLY) $(DEMANGLE) $(COMPILE_DONE) + +# Intermediate files (for debugging macros) +%.i: %.cpp + @echo Preprocessing $< to $@ + $(QUIETLY) $(PREPROCESS.CC) $< > $@ $(COMPILE_DONE) + +# Override gnumake built-in rules which do sccs get operations badly. +# (They put the checked out code in the current directory, not in the +# directory of the original file.) Since this is a symptom of a teamware +# failure, and since not all problems can be detected by gnumake due +# to incomplete dependency checking... just complain and stop. +%:: s.% + @echo "=========================================================" + @echo File $@ + @echo is out of date with respect to its SCCS file. + @echo This file may be from an unresolved Teamware conflict. + @echo This is also a symptom of a Teamware bringover/putback failure + @echo in which SCCS files are updated but not checked out. + @echo Check for other out of date files in your workspace. + @echo "=========================================================" + @exit 666 + +%:: SCCS/s.% + @echo "=========================================================" + @echo File $@ + @echo is out of date with respect to its SCCS file. + @echo This file may be from an unresolved Teamware conflict. + @echo This is also a symptom of a Teamware bringover/putback failure + @echo in which SCCS files are updated but not checked out. + @echo Check for other out of date files in your workspace. + @echo "=========================================================" + @exit 666 + +.PHONY: default diff --git a/hotspot/build/linux/makefiles/sa.make b/hotspot/build/linux/makefiles/sa.make new file mode 100644 index 00000000000..83551b87a4e --- /dev/null +++ b/hotspot/build/linux/makefiles/sa.make @@ -0,0 +1,87 @@ +# +# Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This makefile (sa.make) is included from the sa.make in the +# build directories. + +# This makefile is used to build Serviceability Agent java code +# and generate JNI header file for native methods. + +include $(GAMMADIR)/build/linux/makefiles/rules.make + +AGENT_DIR = $(GAMMADIR)/agent + +include $(GAMMADIR)/build/sa.files + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated + +# tools.jar is needed by the JDI - SA binding +SA_CLASSPATH = $(BOOT_JAVA_HOME)/lib/tools.jar + +# gnumake 3.78.1 does not accept the *s that +# are in AGENT_ALLFILES, so use the shell to expand them +AGENT_ALLFILES := $(shell /usr/bin/test -d $(AGENT_DIR) && /bin/ls $(AGENT_ALLFILES)) + +SA_CLASSDIR = $(GENERATED)/saclasses + +SA_BUILD_VERSION_PROP = "sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VERSION)" + +SA_PROPERTIES = $(SA_CLASSDIR)/sa.properties + +# if $(AGENT_DIR) does not exist, we don't build SA +# also, we don't build SA on Itanium. + +all: + if [ -d $(AGENT_DIR) -a "$(SRCARCH)" != "ia64" ] ; then \ + $(MAKE) -f sa.make $(GENERATED)/sa-jdi.jar; \ + fi + +$(GENERATED)/sa-jdi.jar: $(AGENT_ALLFILES) + $(QUIETLY) echo "Making $@" + $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ + echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ + exit 1; \ + fi + $(QUIETLY) if [ ! -f $(SA_CLASSPATH) ] ; then \ + echo "Missing $(SA_CLASSPATH) file. Use 1.6.0 or later version of JDK";\ + echo ""; \ + exit 1; \ + fi + $(QUIETLY) if [ ! -d $(SA_CLASSDIR) ] ; then \ + mkdir -p $(SA_CLASSDIR); \ + fi + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -g -d $(SA_CLASSDIR) $(AGENT_ALLFILES) + $(QUIETLY) $(REMOTE) $(COMPILE.RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer + $(QUIETLY) echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) + $(QUIETLY) $(REMOTE) $(RUN.JAR) cf $@ -C $(SA_CLASSDIR)/ . + $(QUIETLY) $(REMOTE) $(RUN.JAR) uf $@ -C $(AGENT_SRC_DIR) META-INF/services/com.sun.jdi.connect.Connector + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.x86.X86ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.ia64.IA64ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.sparc.SPARCThreadContext + +clean: + rm -rf $(SA_CLASSDIR) + rm -rf $(GENERATED)/sa-jdi.jar diff --git a/hotspot/build/linux/makefiles/saproc.make b/hotspot/build/linux/makefiles/saproc.make new file mode 100644 index 00000000000..f0e5d90f382 --- /dev/null +++ b/hotspot/build/linux/makefiles/saproc.make @@ -0,0 +1,79 @@ +# +# Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Rules to build serviceability agent library, used by vm.make + +# libsaproc[_g].so: serviceability agent +SAPROC = saproc$(G_SUFFIX) +LIBSAPROC = lib$(SAPROC).so + +AGENT_DIR = $(GAMMADIR)/agent + +SASRCDIR = $(AGENT_DIR)/src/os/$(Platform_os_family) + +SASRCFILES = $(SASRCDIR)/salibelf.c \ + $(SASRCDIR)/symtab.c \ + $(SASRCDIR)/libproc_impl.c \ + $(SASRCDIR)/ps_proc.c \ + $(SASRCDIR)/ps_core.c \ + $(SASRCDIR)/LinuxDebuggerLocal.c + +SAMAPFILE = $(SASRCDIR)/mapfile + +DEST_SAPROC = $(JDK_LIBDIR)/$(LIBSAPROC) + +# if $(AGENT_DIR) does not exist, we don't build SA +# also, we don't build SA on Itanium. + +checkAndBuildSA: + $(QUIETLY) if [ -d $(AGENT_DIR) -a "$(SRCARCH)" != "ia64" ] ; then \ + $(MAKE) -f vm.make $(LIBSAPROC); \ + fi + +SA_LFLAGS = $(MAPFLAG:FILENAME=$(SAMAPFILE)) + +$(LIBSAPROC): $(SASRCFILES) $(SAMAPFILE) + $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ + echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ + exit 1; \ + fi + @echo Making SA debugger back-end... + $(QUIETLY) $(CC) -D$(BUILDARCH) -D_GNU_SOURCE \ + $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ + -I$(SASRCDIR) \ + -I$(GENERATED) \ + -I$(BOOT_JAVA_HOME)/include \ + -I$(BOOT_JAVA_HOME)/include/$(Platform_os_family) \ + $(SASRCFILES) \ + $(SA_LFLAGS) \ + -o $@ \ + -lthread_db + +install_saproc: checkAndBuildSA + $(QUIETLY) if [ -e $(LIBSAPROC) ] ; then \ + echo "Copying $(LIBSAPROC) to $(DEST_SAPROC)"; \ + cp -f $(LIBSAPROC) $(DEST_SAPROC) && echo "Done"; \ + fi + +.PHONY: checkAndBuildSA install_saproc diff --git a/hotspot/build/linux/makefiles/tiered.make b/hotspot/build/linux/makefiles/tiered.make new file mode 100644 index 00000000000..220124e50e1 --- /dev/null +++ b/hotspot/build/linux/makefiles/tiered.make @@ -0,0 +1,31 @@ +# +# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making tiered version of VM + +TYPE=TIERED + +VM_SUBDIR = server + +CFLAGS += -DCOMPILER2 -DCOMPILER1 diff --git a/hotspot/build/linux/makefiles/top.make b/hotspot/build/linux/makefiles/top.make new file mode 100644 index 00000000000..5da9a12d04f --- /dev/null +++ b/hotspot/build/linux/makefiles/top.make @@ -0,0 +1,189 @@ +# +# Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# top.make is included in the Makefile in the build directories. +# It DOES NOT include the vm dependency info in order to be faster. +# It's main job is to implement the incremental form of make lists. +# It also: +# -builds and runs adlc via adlc.make +# -generates JVMTI source and docs via jvmti.make (JSR-163) +# -generate sa-jdi.jar (JDI binding to core files) + +# It assumes the following flags are set: +# CFLAGS Platform_file, Src_Dirs, SYSDEFS, AOUT, Obj_Files + +# -- D. Ungar (5/97) from a file by Bill Bush + +# Don't override the built-in $(MAKE). +# Instead, use "gmake" (or "gnumake") from the command line. --Rose +#MAKE = gmake + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated +VM = $(GAMMADIR)/src/share/vm +Plat_File = $(Platform_file) +CDG = cd $(GENERATED); + +# Pick up MakeDeps' sources and definitions +include $(GAMMADIR)/build/$(Platform_os_family)/makefiles/makedeps.make +MakeDepsClass = MakeDeps.class + +ifdef USE_PRECOMPILED_HEADER +PrecompiledOption = -DUSE_PRECOMPILED_HEADER +UpdatePCH = $(MAKE) -f vm.make $(PRECOMPILED_HEADER) $(MFLAGS) +else +UpdatePCH = \# precompiled header is not used +PrecompiledOption = +endif + +MakeDeps = $(RUN.JAVA) $(PrecompiledOption) -classpath $(GENERATED) MakeDeps + +Include_DBs/GC = $(VM)/includeDB_gc \ + $(VM)/includeDB_gc_parallel \ + $(VM)/gc_implementation/includeDB_gc_parallelScavenge \ + $(VM)/gc_implementation/includeDB_gc_concurrentMarkSweep \ + $(VM)/gc_implementation/includeDB_gc_parNew \ + $(VM)/gc_implementation/includeDB_gc_serial \ + $(VM)/gc_implementation/includeDB_gc_shared + +Include_DBs/CORE = $(VM)/includeDB_core $(Include_DBs/GC) \ + $(VM)/includeDB_jvmti \ + $(VM)/includeDB_features +Include_DBs/COMPILER1 = $(Include_DBs/CORE) $(VM)/includeDB_compiler1 +Include_DBs/COMPILER2 = $(Include_DBs/CORE) $(VM)/includeDB_compiler2 +Include_DBs/TIERED = $(Include_DBs/CORE) $(VM)/includeDB_compiler1 $(VM)/includeDB_compiler2 +Include_DBs = $(Include_DBs/$(TYPE)) + +Cached_plat = $(GENERATED)/platform.current +Cached_db = $(GENERATED)/includeDB.current + +Incremental_Lists = $(Cached_db) +# list generation also creates $(GENERATED)/$(Cached_plat) + + +AD_Dir = $(GENERATED)/adfiles +ADLC = $(AD_Dir)/adlc +AD_Spec = $(GAMMADIR)/src/cpu/$(Platform_arch)/vm/$(Platform_arch).ad +AD_Src = $(GAMMADIR)/src/share/vm/adlc +AD_Names = ad_$(Platform_arch).hpp ad_$(Platform_arch).cpp +AD_Files = $(AD_Names:%=$(AD_Dir)/%) + +# AD_Files_If_Required/COMPILER1 = ad_stuff +AD_Files_If_Required/COMPILER2 = ad_stuff +AD_Files_If_Required/TIERED = ad_stuff +AD_Files_If_Required = $(AD_Files_If_Required/$(TYPE)) + +# Wierd argument adjustment for "gnumake -j..." +adjust-mflags = $(GENERATED)/adjust-mflags +MFLAGS-adjusted = -r `$(adjust-mflags) "$(MFLAGS)" "$(HOTSPOT_BUILD_JOBS)"` + + +# default target: make makeDeps, update lists, make vm +# done in stages to force sequential order with parallel make +# + +default: vm_build_preliminaries the_vm + @echo All done. + +# This is an explicit dependency for the sake of parallel makes. +vm_build_preliminaries: checks $(Incremental_Lists) $(AD_Files_If_Required) jvmti_stuff sa_stuff + @# We need a null action here, so implicit rules don't get consulted. + +# make makeDeps: (and zap the cached db files to force a nonincremental run) + +$(GENERATED)/$(MakeDepsClass): $(MakeDepsSources) + @$(REMOTE) $(COMPILE.JAVAC) -classpath $(GAMMADIR)/src/share/tools/MakeDeps -g -d $(GENERATED) $(MakeDepsSources) + @echo Removing $(Incremental_Lists) to force regeneration. + @rm -f $(Incremental_Lists) + @$(CDG) echo >$(Cached_plat) + +# make incremental_lists, if cached files out of date, run makeDeps + +$(Incremental_Lists): $(Include_DBs) $(Plat_File) $(GENERATED)/$(MakeDepsClass) + $(CDG) cat $(Include_DBs) > $(GENERATED)/includeDB + $(CDG) if [ ! -r incls ] ; then \ + mkdir incls ; \ + fi + $(CDG) (echo $(CDG) echo $(MakeDeps) diffs UnixPlatform $(Cached_plat) $(Cached_db) $(Plat_File) $(GENERATED)/includeDB $(MakeDepsOptions)) > makeDeps.sh + $(CDG) $(REMOTE) sh $(GENERATED)/makeDeps.sh + $(CDG) cp includeDB $(Cached_db) + $(CDG) cp $(Plat_File) $(Cached_plat) + +# symbolic target for command lines +lists: $(Incremental_Lists) + @: lists are now up to date + +# make AD files as necessary +ad_stuff: $(Incremental_Lists) $(adjust-mflags) + @$(MAKE) -f adlc.make $(MFLAGS-adjusted) + +# generate JVMTI files from the spec +jvmti_stuff: $(Incremental_Lists) $(adjust-mflags) + @$(MAKE) -f jvmti.make $(MFLAGS-adjusted) + +# generate SA jar files and native header +sa_stuff: + @$(MAKE) -f sa.make $(MFLAGS-adjusted) + +# and the VM: must use other makefile with dependencies included + +# We have to go to great lengths to get control over the -jN argument +# to the recursive invocation of vm.make. The problem is that gnumake +# resets -jN to -j1 for recursive runs. (How helpful.) +# Note that the user must specify the desired parallelism level via a +# command-line or environment variable name HOTSPOT_BUILD_JOBS. +$(adjust-mflags): $(GAMMADIR)/build/$(Platform_os_family)/makefiles/adjust-mflags.sh + @+rm -f $@ $@+ + @+cat $< > $@+ + @+chmod +x $@+ + @+mv $@+ $@ + +the_vm: vm_build_preliminaries $(adjust-mflags) + @$(UpdatePCH) + @$(MAKE) -f vm.make $(MFLAGS-adjusted) + +install: the_vm + @$(MAKE) -f vm.make install + +# next rules support "make foo.[oi]" + +%.o %.i %.s: + $(UpdatePCH) + $(MAKE) -f vm.make $(MFLAGS) $@ + #$(MAKE) -f vm.make $@ + +# this should force everything to be rebuilt +clean: + rm -f $(GENERATED)/*.class + $(MAKE) $(MFLAGS) $(GENERATED)/$(MakeDepsClass) + $(MAKE) -f vm.make $(MFLAGS) clean + +# just in case it doesn't, this should do it +realclean: + $(MAKE) -f vm.make $(MFLAGS) clean + rm -fr $(GENERATED) + +.PHONY: default vm_build_preliminaries +.PHONY: lists ad_stuff jvmti_stuff sa_stuff the_vm clean realclean +.PHONY: checks check_os_version install diff --git a/hotspot/build/linux/makefiles/vm.make b/hotspot/build/linux/makefiles/vm.make new file mode 100644 index 00000000000..567e6779145 --- /dev/null +++ b/hotspot/build/linux/makefiles/vm.make @@ -0,0 +1,226 @@ +# +# Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Rules to build JVM and related libraries, included from vm.make in the build +# directory. + +# Common build rules. +MAKEFILES_DIR=$(GAMMADIR)/build/$(Platform_os_family)/makefiles +include $(MAKEFILES_DIR)/rules.make + +default: build + +#---------------------------------------------------------------------- +# Defs + +GENERATED = ../generated + +# read a generated file defining the set of .o's and the .o .h dependencies +include $(GENERATED)/Dependencies + +# read machine-specific adjustments (%%% should do this via buildtree.make?) +include $(MAKEFILES_DIR)/$(BUILDARCH).make + +# set VPATH so make knows where to look for source files +# Src_Dirs is everything in src/share/vm/*, plus the right os/*/vm and cpu/*/vm +# The incls directory contains generated header file lists for inclusion. +# The adfiles directory contains ad_.[ch]pp. +# The jvmtifiles directory contains jvmti*.[ch]pp +Src_Dirs_V = $(GENERATED)/adfiles $(GENERATED)/jvmtifiles ${Src_Dirs} $(GENERATED)/incls +VPATH += $(Src_Dirs_V:%=%:) + +# set INCLUDES for C preprocessor +Src_Dirs_I = $(PRECOMPILED_HEADER_DIR) $(GENERATED)/adfiles $(GENERATED)/jvmtifiles ${Src_Dirs} $(GENERATED) +INCLUDES += $(Src_Dirs_I:%=-I%) + +ifeq (${VERSION}, debug) + SYMFLAG = -g +else + SYMFLAG = +endif + +# HOTSPOT_RELEASE_VERSION and HOTSPOT_BUILD_VERSION are defined +# in $(GAMMADIR)/make/defs.make +ifeq ($(HOTSPOT_BUILD_VERSION),) + BUILD_VERSION = -DHOTSPOT_RELEASE_VERSION="\"$(HOTSPOT_RELEASE_VERSION)\"" +else + BUILD_VERSION = -DHOTSPOT_RELEASE_VERSION="\"$(HOTSPOT_RELEASE_VERSION)-$(HOTSPOT_BUILD_VERSION)\"" +endif + +# The following variables are defined in the generated flags.make file. +BUILD_VERSION = -DHOTSPOT_RELEASE_VERSION="\"$(HS_BUILD_VER)\"" +JRE_VERSION = -DJRE_RELEASE_VERSION="\"$(JRE_RELEASE_VER)\"" +BUILD_TARGET = -DHOTSPOT_BUILD_TARGET="\"$(TARGET)\"" +BUILD_USER = -DHOTSPOT_BUILD_USER="\"$(HOTSPOT_BUILD_USER)\"" +VM_DISTRO = -DHOTSPOT_VM_DISTRO="\"$(HOTSPOT_VM_DISTRO)\"" + +CPPFLAGS = \ + ${SYSDEFS} \ + ${INCLUDES} \ + ${BUILD_VERSION} \ + ${BUILD_TARGET} \ + ${BUILD_USER} \ + ${JRE_VERSION} \ + ${VM_DISTRO} + +# CFLAGS_WARN holds compiler options to suppress/enable warnings. +CFLAGS += $(CFLAGS_WARN/BYFILE) + +# Do not use C++ exception handling +CFLAGS += $(CFLAGS/NOEX) + +# Extra flags from gnumake's invocation or environment +CFLAGS += $(EXTRA_CFLAGS) + +LIBS += -lm -ldl -lpthread + +# By default, link the *.o into the library, not the executable. +LINK_INTO$(LINK_INTO) = LIBJVM + +JDK_LIBDIR = $(JAVA_HOME)/jre/lib/$(LIBARCH) + +#---------------------------------------------------------------------- +# jvm_db & dtrace +include $(MAKEFILES_DIR)/dtrace.make + +#---------------------------------------------------------------------- +# JVM + +JVM = jvm$(G_SUFFIX) +LIBJVM = lib$(JVM).so + +JVM_OBJ_FILES = $(Obj_Files) + +vm_version.o: $(filter-out vm_version.o,$(JVM_OBJ_FILES)) + +mapfile : $(MAPFILE) + rm -f $@ + cat $^ > $@ + +mapfile_reorder : mapfile $(REORDERFILE) + rm -f $@ + cat $^ > $@ + +STATIC_CXX = true + +ifeq ($(LINK_INTO),AOUT) + LIBJVM.o = + LIBJVM_MAPFILE = + LIBS_VM = $(LIBS) +else + LIBJVM.o = $(JVM_OBJ_FILES) + LIBJVM_MAPFILE$(LDNOMAP) = mapfile_reorder + LFLAGS_VM$(LDNOMAP) += $(MAPFLAG:FILENAME=$(LIBJVM_MAPFILE)) + LFLAGS_VM += $(SONAMEFLAG:SONAME=$(LIBJVM)) + + # JVM is statically linked with libgcc[_s] and libstdc++; this is needed to + # get around library dependency and compatibility issues. Must use gcc not + # g++ to link. + ifeq ($(STATIC_CXX), true) + LFLAGS_VM += $(STATIC_LIBGCC) + LIBS_VM += $(STATIC_STDCXX) + else + LIBS_VM += -lstdc++ + endif + + LIBS_VM += $(LIBS) +endif + +LINK_VM = $(LINK_LIB.c) + +# rule for building precompiled header +$(PRECOMPILED_HEADER): $(Precompiled_Files) + $(QUIETLY) echo Generating precompiled header $@ + $(QUIETLY) mkdir -p $(PRECOMPILED_HEADER_DIR)/incls + $(QUIETLY) $(COMPILE.CC) -x c++-header -c $(GENERATED)/incls/_precompiled.incl -o $@ $(COMPILE_DONE) + +# making the library: + +ifneq ($(JVM_BASE_ADDR),) +# By default shared library is linked at base address == 0. Modify the +# linker script if JVM prefers a different base location. It can also be +# implemented with 'prelink -r'. But 'prelink' is not (yet) available on +# our build platform (AS-2.1). +LD_SCRIPT = libjvm.so.lds +$(LD_SCRIPT): $(LIBJVM_MAPFILE) + $(QUIETLY) { \ + rm -rf $@; \ + $(LINK_VM) -Wl,--verbose $(LFLAGS_VM) 2>&1 | \ + sed -e '/^======/,/^======/!d' \ + -e '/^======/d' \ + -e 's/0\( + SIZEOF_HEADERS\)/$(JVM_BASE_ADDR)\1/' \ + > $@; \ + } +LD_SCRIPT_FLAG = -Wl,-T,$(LD_SCRIPT) +endif + +# With more recent Redhat releases (or the cutting edge version Fedora), if +# SELinux is configured to be enabled, the runtime linker will fail to apply +# the text relocation to libjvm.so considering that it is built as a non-PIC +# DSO. To workaround that, we run chcon to libjvm.so after it is built. See +# details in bug 6538311. +$(LIBJVM): $(LIBJVM.o) $(LIBJVM_MAPFILE) $(LD_SCRIPT) + $(QUIETLY) { \ + echo Linking vm...; \ + $(LINK_LIB.CC/PRE_HOOK) \ + $(LINK_VM) $(LD_SCRIPT_FLAG) \ + $(LFLAGS_VM) -o $@ $(LIBJVM.o) $(LIBS_VM); \ + $(LINK_LIB.CC/POST_HOOK) \ + rm -f $@.1; ln -s $@ $@.1; \ + if [ -x /usr/sbin/selinuxenabled ] ; then \ + /usr/sbin/selinuxenabled; \ + if [ $$? = 0 ] ; then \ + /usr/bin/chcon -t textrel_shlib_t $@; \ + if [ $$? != 0 ]; then \ + echo "ERROR: Cannot chcon $@"; exit 1; \ + fi \ + fi \ + fi \ + } + +DEST_JVM = $(JDK_LIBDIR)/$(VM_SUBDIR)/$(LIBJVM) + +install_jvm: $(LIBJVM) + @echo "Copying $(LIBJVM) to $(DEST_JVM)" + $(QUIETLY) cp -f $(LIBJVM) $(DEST_JVM) && echo "Done" + +#---------------------------------------------------------------------- +# Other files + +# Gamma launcher +include $(MAKEFILES_DIR)/launcher.make + +# Signal interposition library +include $(MAKEFILES_DIR)/jsig.make + +# Serviceability agent +include $(MAKEFILES_DIR)/saproc.make + +#---------------------------------------------------------------------- + +build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) checkAndBuildSA + +install: install_jvm install_jsig install_saproc + +.PHONY: default build install install_jvm diff --git a/hotspot/build/linux/platform_amd64 b/hotspot/build/linux/platform_amd64 new file mode 100644 index 00000000000..2a76633d259 --- /dev/null +++ b/hotspot/build/linux/platform_amd64 @@ -0,0 +1,17 @@ +os_family = linux + +arch = x86 + +arch_model = x86_64 + +os_arch = linux_x86 + +os_arch_model = linux_x86_64 + +lib_arch = amd64 + +compiler = gcc + +gnu_dis_arch = amd64 + +sysdefs = -DLINUX -D_GNU_SOURCE -DAMD64 diff --git a/hotspot/build/linux/platform_i486 b/hotspot/build/linux/platform_i486 new file mode 100644 index 00000000000..7f8b111675b --- /dev/null +++ b/hotspot/build/linux/platform_i486 @@ -0,0 +1,17 @@ +os_family = linux + +arch = x86 + +arch_model = x86_32 + +os_arch = linux_x86 + +os_arch_model = linux_x86_32 + +lib_arch = i386 + +compiler = gcc + +gnu_dis_arch = i386 + +sysdefs = -DLINUX -D_GNU_SOURCE -DIA32 diff --git a/hotspot/build/linux/platform_sparc b/hotspot/build/linux/platform_sparc new file mode 100644 index 00000000000..2fda1971bd6 --- /dev/null +++ b/hotspot/build/linux/platform_sparc @@ -0,0 +1,17 @@ +os_family = linux + +arch = sparc + +arch_model = sparc + +os_arch = linux_sparc + +os_arch_model = linux_sparc + +lib_arch = sparc + +compiler = gcc + +gnu_dis_arch = sparc + +sysdefs = -DLINUX -D_GNU_SOURCE -DSPARC diff --git a/hotspot/build/sa.files b/hotspot/build/sa.files new file mode 100644 index 00000000000..9b524940f3b --- /dev/null +++ b/hotspot/build/sa.files @@ -0,0 +1,133 @@ +# +# Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This filelist macro is included in platform specific sa.make +# included all packages/*.java. package list can be generated by +# $(GAMMADIR)/agent/make/build-pkglist. Then manually removed all +# classes in sun.jvm.hotspot.ui (and subpackages), all ui classes +# in sun.jvm.hotspot.bugspot/hotspot and SPARC and x86 disassembler +# classes and sun.jvm.hotspot.utilities.soql. + +# define AGENT_DIR before including this file in sa.make + +AGENT_SRC_DIR = $(AGENT_DIR)/src/share/classes + +AGENT_ALLFILES = \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/DebugServer.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/HelloWorld.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/HotSpotAgent.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/HotSpotSolarisVtblAccess.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/HotSpotTypeDataBase.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/LinuxVtblAccess.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/ObjectHistogram.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/RMIHelper.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/StackTrace.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/TestDebugger.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/Win32VtblAccess.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/Immediate.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/ImmediateOrRegister.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/Operand.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/Register.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/amd64/AMD64Register.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/amd64/AMD64Registers.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/ia64/IA64Register.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/ia64/IA64Registers.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/sparc/SPARCArgument.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/sparc/SPARCRegister.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/sparc/SPARCRegisterType.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/sparc/SPARCRegisters.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/x86/X86Register.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/x86/X86RegisterPart.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/x86/X86Registers.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/x86/X86SegmentRegister.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/x86/X86SegmentRegisters.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/bugspot/BugSpotAgent.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/c1/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/code/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/compiler/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/cdbg/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/cdbg/basic/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/cdbg/basic/x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/cdbg/basic/amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/dbx/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/dbx/sparc/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/dbx/x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/dummy/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/ia64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/ia64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/sparc/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/posix/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/posix/elf/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/sparc/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/sparc/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/sparc/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/win32/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/win32/coff/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/ia64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/gc_implementation/parallelScavenge/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/gc_implementation/shared/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/gc_interface/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/interpreter/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/jdi/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/livejvm/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/memory/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/oops/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/ia64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_ia64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_sparc/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/posix/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_sparc/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/sparc/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/win32_amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/win32_ia64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/win32_x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/tools/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/tools/jcore/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/types/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/types/basic/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/memo/*.java \ diff --git a/hotspot/build/solaris/Makefile b/hotspot/build/solaris/Makefile new file mode 100644 index 00000000000..0a7615aa794 --- /dev/null +++ b/hotspot/build/solaris/Makefile @@ -0,0 +1,307 @@ +# +# Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This makefile creates a build tree and lights off a build. +# You can go back into the build tree and perform rebuilds or +# incremental builds as desired. Be sure to reestablish +# environment variable settings for LD_LIBRARY_PATH and JAVA_HOME. + +# The make process now relies on java and javac. These can be +# specified either implicitly on the PATH, by setting the +# (JDK-inherited) ALT_BOOTDIR environment variable to full path to a +# JDK in which bin/java and bin/javac are present and working (e.g., +# /usr/local/java/jdk1.3/solaris), or via the (JDK-inherited) +# default BOOTDIR path value. Note that one of ALT_BOOTDIR +# or BOOTDIR has to be set. We do *not* search javac, javah, rmic etc. +# from the PATH. + +# Along with VM, Serviceability Agent (SA) is built for SA/JDI binding. +# JDI binding on SA produces two binaries: +# 1. sa-jdi.jar - This is build before building libjvm[_g].so +# Please refer to ./makefiles/sa.make +# 2. libsaproc[_g].so - Native library for SA - This is built after +# libjsig[_g].so (signal interposition library) +# Please refer to ./makefiles/vm.make +# If $(GAMMADIR)/agent dir is not present, SA components are not built. + +ifeq ($(GAMMADIR),) +include ../../make/defs.make +else +include $(GAMMADIR)/make/defs.make +endif +include $(GAMMADIR)/build/$(OSNAME)/makefiles/rules.make + +ifndef LP64 +ifndef CC_INTERP +FORCE_TIERED=1 +endif +endif + +ifdef LP64 + ifeq ("$(filter $(LP64_ARCH),$(BUILDARCH))","") + _JUNK_ := $(shell echo >&2 \ + $(OSNAME) $(ARCH) "*** ERROR: this platform does not support 64-bit compilers!") + @exit 1 + endif +endif + +# The following renders pathnames in generated Makefiles valid on +# machines other than the machine containing the build tree. +# +# For example, let's say my build tree lives on /files12 on +# exact.east.sun.com. This logic will cause GAMMADIR to begin with +# /net/exact/files12/... +# +# We only do this on SunOS variants, for a couple of reasons: +# * It is extremely rare that source trees exist on other systems +# * It has been claimed that the Linux automounter is flakey, so +# changing GAMMADIR in a way that exercises the automounter could +# prove to be a source of unreliability in the build process. +# Obviously, this Makefile is only relevant on SunOS boxes to begin +# with, but the SunOS conditionalization will make it easier to +# combine Makefiles in the future (assuming we ever do that). + +ifeq ($(OSNAME),solaris) + + # prepend current directory to relative pathnames. + NEW_GAMMADIR := \ + $(shell echo $(GAMMADIR) | \ + sed -e "s=^\([^/].*\)=$(shell pwd)/\1=" \ + ) + unexport NEW_GAMMADIR + + # If NEW_GAMMADIR doesn't already start with "/net/": + ifeq ($(strip $(filter /net/%,$(NEW_GAMMADIR))),) + # prepend /net/$(HOST) + # remove /net/$(HOST) if name already began with /home/ + # remove /net/$(HOST) if name already began with /java/ + # remove /net/$(HOST) if name already began with /lab/ + NEW_GAMMADIR := \ + $(shell echo $(NEW_GAMMADIR) | \ + sed -e "s=^\(.*\)=/net/$(HOST)\1=" \ + -e "s=^/net/$(HOST)/home/=/home/=" \ + -e "s=^/net/$(HOST)/java/=/java/=" \ + -e "s=^/net/$(HOST)/lab/=/lab/=" \ + ) + # Don't use the new value for GAMMADIR unless a file with the new + # name actually exists. + ifneq ($(wildcard $(NEW_GAMMADIR)),) + GAMMADIR := $(NEW_GAMMADIR) + endif + endif + +endif + + +# There is a (semi-) regular correspondence between make targets and actions: +# +# Target Tree Type Build Dir +# +# debug compiler2 __compiler2/debug +# fastdebug compiler2 __compiler2/fastdebug +# jvmg compiler2 __compiler2/jvmg +# optimized compiler2 __compiler2/optimized +# profiled compiler2 __compiler2/profiled +# product compiler2 __compiler2/product +# +# debug1 compiler1 __compiler1/debug +# fastdebug1 compiler1 __compiler1/fastdebug +# jvmg1 compiler1 __compiler1/jvmg +# optimized1 compiler1 __compiler1/optimized +# profiled1 compiler1 __compiler1/profiled +# product1 compiler1 __compiler1/product +# +# debugcore core __core/debug +# fastdebugcore core __core/fastdebug +# jvmgcore core __core/jvmg +# optimizedcore core __core/optimized +# profiledcore core __core/profiled +# productcore core __core/product +# +# What you get with each target: +# +# debug* - "thin" libjvm_g - debug info linked into the gamma_g launcher +# fastdebug* - optimized compile, but with asserts enabled +# jvmg* - "fat" libjvm_g - debug info linked into libjvm_g.so +# optimized* - optimized compile, no asserts +# profiled* - gprof +# product* - the shippable thing: optimized compile, no asserts, -DPRODUCT + +# This target list needs to be coordinated with the usage message +# in the build.sh script: +TARGETS = debug jvmg fastdebug optimized profiled product + +SUBDIR_DOCS = $(OSNAME)_$(BUILDARCH)_docs +SUBDIRS_C1 = $(addprefix $(OSNAME)_$(BUILDARCH)_compiler1/,$(TARGETS)) +SUBDIRS_C2 = $(addprefix $(OSNAME)_$(BUILDARCH)_compiler2/,$(TARGETS)) +SUBDIRS_TIERED = $(addprefix $(OSNAME)_$(BUILDARCH)_tiered/,$(TARGETS)) +SUBDIRS_CORE = $(addprefix $(OSNAME)_$(BUILDARCH)_core/,$(TARGETS)) +SUBDIRS_KERNEL = $(addprefix $(OSNAME)_$(BUILDARCH)_kernel/,$(TARGETS)) + +TARGETS_C2 = $(TARGETS) +TARGETS_C1 = $(addsuffix 1,$(TARGETS)) +TARGETS_TIERED = $(addsuffix tiered,$(TARGETS)) +TARGETS_CORE = $(addsuffix core,$(TARGETS)) +TARGETS_KERNEL = $(addsuffix kernel,$(TARGETS)) + +BUILDTREE_MAKE = $(GAMMADIR)/build/$(OSNAME)/makefiles/buildtree.make +BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OSNAME) ARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) +BUILDTREE_VARS += HOTSPOT_RELEASE_VERSION=$(HOTSPOT_RELEASE_VERSION) HOTSPOT_BUILD_VERSION=$(HOTSPOT_BUILD_VERSION) JRE_RELEASE_VERSION=$(JRE_RELEASE_VERSION) + +BUILDTREE = $(MAKE) -f $(BUILDTREE_MAKE) $(BUILDTREE_VARS) + +#------------------------------------------------------------------------------- + +# Could make everything by default, but that would take a while. +all: + @echo "Try '$(MAKE) ...' where is one or more of" + @echo " $(TARGETS_C2)" + @echo " $(TARGETS_C1)" + @echo " $(TARGETS_CORE)" + +checks: check_os_version check_j2se_version + +# We do not want people accidentally building on old systems (e.g. Linux 2.2.x, +# Solaris 2.5.1, 2.6). +# Disable this check by setting DISABLE_HOTSPOT_OS_VERSION_CHECK=ok. + +SUPPORTED_OS_VERSION = 5.7 5.8 5.9 5.10 5.11 +OS_VERSION := $(shell uname -r) +EMPTY_IF_NOT_SUPPORTED = $(filter $(SUPPORTED_OS_VERSION),$(OS_VERSION)) + +check_os_version: +ifeq ($(DISABLE_HOTSPOT_OS_VERSION_CHECK)$(EMPTY_IF_NOT_SUPPORTED),) + $(QUIETLY) >&2 echo "*** This OS is not supported:" `uname -a`; exit 1; +endif + +# jvmti.make requires XSLT (J2SE 1.4.x or newer): +XSLT_CHECK = $(RUN.JAVAP) javax.xml.transform.TransformerFactory +# If not found then fail fast. +check_j2se_version: + $(QUIETLY) $(XSLT_CHECK) > /dev/null 2>&1; \ + if [ $$? -ne 0 ]; then \ + $(RUN.JAVA) -version; \ + echo "*** An XSLT processor (J2SE 1.4.x or newer) is required" \ + "to bootstrap this build" 1>&2; \ + exit 1; \ + fi + +$(SUBDIRS_TIERED): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=tiered + +$(SUBDIRS_C2): $(BUILDTREE_MAKE) +ifdef FORCE_TIERED + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=tiered FORCE_TIERED=1 +else + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=compiler2 +endif + +$(SUBDIRS_C1): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=compiler1 + +$(SUBDIRS_CORE): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=core + +$(SUBDIRS_KERNEL): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=kernel + +# Define INSTALL=y at command line to automatically copy JVM into JAVA_HOME + +$(TARGETS_C2): $(SUBDIRS_C2) + cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_TIERED): $(SUBDIRS_TIERED) + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_C1): $(SUBDIRS_C1) + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_CORE): $(SUBDIRS_CORE) + cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_KERNEL): $(SUBDIRS_KERNEL) + cd $(OSNAME)_$(BUILDARCH)_kernel/$(patsubst %kernel,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_kernel/$(patsubst %kernel,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_kernel/$(patsubst %kernel,%,$@) && $(MAKE) $(MFLAGS) install +endif + +# Just build the tree, and nothing else: +tree: $(SUBDIRS_C2) +tree1: $(SUBDIRS_C1) +treecore: $(SUBDIRS_CORE) +treekernel: $(SUBDIRS_KERNEL) + +# Doc target. This is the same for all build options. +# Hence create a docs directory beside ...$(ARCH)_[...] +docs: checks + $(QUIETLY) mkdir -p $(SUBDIR_DOCS) + $(MAKE) -f $(GAMMADIR)/build/$(OSNAME)/makefiles/jvmti.make $(MFLAGS) $(BUILDTREE_VARS) JvmtiOutDir=$(SUBDIR_DOCS) jvmtidocs + +# Synonyms for win32-like targets. +compiler2: jvmg product + +compiler1: jvmg1 product1 + +core: jvmgcore productcore + +clean_docs: + rm -rf $(SUBDIR_DOCS) + +clean_compiler1 clean_compiler2 clean_core clean_kernel: + rm -rf $(OSNAME)_$(BUILDARCH)_$(subst clean_,,$@) + +clean: clean_compiler2 clean_compiler1 clean_core clean_docs clean_kernel + +include $(GAMMADIR)/build/$(OSNAME)/makefiles/cscope.make + +#------------------------------------------------------------------------------- + +.PHONY: $(TARGETS_C2) $(TARGETS_C1) $(TARGETS_CORE) +.PHONY: tree tree1 treecore +.PHONY: all compiler1 compiler2 core +.PHONY: clean clean_compiler1 clean_compiler2 clean_core docs clean_docs +.PHONY: checks check_os_version check_j2se_version diff --git a/hotspot/build/solaris/Queens.class b/hotspot/build/solaris/Queens.class new file mode 100644 index 00000000000..d4582a04411 Binary files /dev/null and b/hotspot/build/solaris/Queens.class differ diff --git a/hotspot/build/solaris/adlc_updater b/hotspot/build/solaris/adlc_updater new file mode 100644 index 00000000000..6a97b79931d --- /dev/null +++ b/hotspot/build/solaris/adlc_updater @@ -0,0 +1,11 @@ +#! /bin/sh +# +# This file is used by adlc.make to selectively update generated +# adlc files. Because source and target diretories are relative +# paths, this file is copied to the target build directory before +# use. +# +# adlc-updater +# +[ -f $3/$1 ] && cmp -s $2/$1 $3/$1 || \ +( [ -f $3/$1 ]; echo Updating $3/$1 ; touch $2/made-change ; mv $2/$1 $3/$1 ) diff --git a/hotspot/build/solaris/build.sh b/hotspot/build/solaris/build.sh new file mode 100644 index 00000000000..57920f388ba --- /dev/null +++ b/hotspot/build/solaris/build.sh @@ -0,0 +1,127 @@ +#! /bin/sh +# +# Copyright 1998-2000 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Make sure the variable JAVA_HOME is set before running this script. + +set -u + + +usage() { + ( + echo "Usage : $0 [-sb | -sbfast] config ws_path" + echo "" + echo "Where:" + echo " -sb ::= enable source browser info generation for" + echo " all configs during compilation" + echo "" + echo " -sbfast ::= enable source browser info generation for" + echo " all configs without compilation" + echo "" + echo " config ::= debug | debug1 | debugcore" + echo " fastdebug | fastdebug1 | fastdebugcore" + echo " jvmg | jvmg1 | jvmgcore" + echo " optimized | optimized1 | optimizedcore" + echo " profiled | profiled1 | profiledcore" + echo " product | product1 | productcore" + echo "" + echo " ws_path ::= path to HotSpot workspace" + ) >&2 + exit 1 +} + +# extract possible options +options="" +if [ $# -gt 2 ]; then + case "$1" in + -sb) + options="CFLAGS_BROWSE=-xsb" + shift + ;; + -sbfast) + options="CFLAGS_BROWSE=-xsbfast" + shift + ;; + *) + echo "Unknown option: '$1'" >&2 + usage + ;; + esac +fi + +# should be just two args left at this point +if [ $# != 2 ]; then + usage +fi + +# Just in case: +case ${JAVA_HOME} in +/*) true;; +?*) JAVA_HOME=`( cd $JAVA_HOME; pwd )`;; +esac + +if [ "${JAVA_HOME}" = "" -o ! -d "${JAVA_HOME}" -o ! -d ${JAVA_HOME}/jre/lib/`uname -p` ]; then + echo "JAVA_HOME needs to be set to a valid JDK path" + echo "ksh : export JAVA_HOME=/net/tetrasparc/export/gobi/JDK1.2_fcs_V/solaris" + echo "csh : setenv JAVA_HOME /net/tetrasparc/export/gobi/JDK1.2_fcs_V/solaris" + exit 1 +fi + + +LD_LIBRARY_PATH=${JAVA_HOME}/jre/lib/`uname -p`:\ +${JAVA_HOME}/jre/lib/`uname -p`/native_threads:${LD_LIBRARY_PATH-.} + +# This is necessary as long as we are using the old launcher +# with the new distribution format: +CLASSPATH=${JAVA_HOME}/jre/lib/rt.jar:${CLASSPATH-.} + + +for gm in gmake gnumake +do + if [ "${GNUMAKE-}" != "" ]; then break; fi + ($gm --version >/dev/null) 2>/dev/null && GNUMAKE=$gm +done +: ${GNUMAKE:?'Cannot locate the gnumake program. Stop.'} + + +echo "### ENVIRONMENT SETTINGS:" +export JAVA_HOME ; echo "JAVA_HOME=$JAVA_HOME" +export LD_LIBRARY_PATH ; echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" +export CLASSPATH ; echo "CLASSPATH=$CLASSPATH" +export GNUMAKE ; echo "GNUMAKE=$GNUMAKE" +echo "###" + +config=$1 +ws_path=$2 + +case ${ws_path} in +/*) true;; +?*) ws_path=`(cd ${ws_path}; pwd)`;; +esac + +echo \ +${GNUMAKE} -f ${ws_path}/build/solaris/Makefile \ + $config GAMMADIR=${ws_path} $options +${GNUMAKE} -f ${ws_path}/build/solaris/Makefile \ + $config GAMMADIR=${ws_path} $options diff --git a/hotspot/build/solaris/makefiles/adjust-mflags.sh b/hotspot/build/solaris/makefiles/adjust-mflags.sh new file mode 100644 index 00000000000..325e02d4fb1 --- /dev/null +++ b/hotspot/build/solaris/makefiles/adjust-mflags.sh @@ -0,0 +1,87 @@ +#! /bin/sh +# +# Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This script is used only from top.make. +# The macro $(MFLAGS-adjusted) calls this script to +# adjust the "-j" arguments to take into account +# the HOTSPOT_BUILD_JOBS variable. The default +# handling of the "-j" argument by gnumake does +# not meet our needs, so we must adjust it ourselves. + +# This argument adjustment applies to two recursive +# calls to "$(MAKE) $(MFLAGS-adjusted)" in top.make. +# One invokes adlc.make, and the other invokes vm.make. +# The adjustment propagates the desired concurrency +# level down to the sub-make (of the adlc or vm). +# The default behavior of gnumake is to run all +# sub-makes without concurrency ("-j1"). + +# Also, we use a make variable rather than an explicit +# "-j" argument to control this setting, so that +# the concurrency setting (which must be tuned separately +# for each MP system) can be set via an environment variable. +# The recommended setting is 1.5x to 2x the number of available +# CPUs on the MP system, which is large enough to keep the CPUs +# busy (even though some jobs may be I/O bound) but not too large, +# we may presume, to overflow the system's swap space. + +set -eu + +default_build_jobs=4 + +case $# in +[12]) true;; +*) >&2 echo "Usage: $0 ${MFLAGS} ${HOTSPOT_BUILD_JOBS}"; exit 2;; +esac + +MFLAGS=$1 +HOTSPOT_BUILD_JOBS=${2-} + +# Normalize any -jN argument to the form " -j${HBJ}" +MFLAGS=` + echo "$MFLAGS" \ + | sed ' + s/^-/ -/ + s/ -\([^ ][^ ]*\)j/ -\1 -j/ + s/ -j[0-9][0-9]*/ -j/ + s/ -j\([^ ]\)/ -j -\1/ + s/ -j/ -j'${HOTSPOT_BUILD_JOBS:-${default_build_jobs}}'/ + ' ` + +case ${HOTSPOT_BUILD_JOBS} in \ + +'') case ${MFLAGS} in + *\ -j*) + >&2 echo "# Note: -jN is ineffective for setting parallelism in this makefile." + >&2 echo "# please set HOTSPOT_BUILD_JOBS=${default_build_jobs} in the command line or environment." + esac;; + +?*) case ${MFLAGS} in + *\ -j*) true;; + *) MFLAGS="-j${HOTSPOT_BUILD_JOBS} ${MFLAGS}";; + esac;; +esac + +echo "${MFLAGS}" diff --git a/hotspot/build/solaris/makefiles/adlc.make b/hotspot/build/solaris/makefiles/adlc.make new file mode 100644 index 00000000000..467c7d499ed --- /dev/null +++ b/hotspot/build/solaris/makefiles/adlc.make @@ -0,0 +1,217 @@ +# +# Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This makefile (adlc.make) is included from the adlc.make in the +# build directories. +# It knows how to compile, link, and run the adlc. + +include $(GAMMADIR)/build/$(Platform_os_family)/makefiles/rules.make + +# ######################################################################### + +# OUTDIR must be the same as AD_Dir = $(GENERATED)/adfiles in top.make: +GENERATED = ../generated +OUTDIR = $(GENERATED)/adfiles + +ARCH = $(Platform_arch) +OS = $(Platform_os_family) + +SOURCE.AD = $(OUTDIR)/$(OS)_$(Platform_arch_model).ad + +SOURCES.AD = $(GAMMADIR)/src/cpu/$(ARCH)/vm/$(Platform_arch_model).ad \ + $(GAMMADIR)/src/os_cpu/$(OS)_$(ARCH)/vm/$(OS)_$(Platform_arch_model).ad + +Src_Dirs += $(GAMMADIR)/src/share/vm/adlc + +EXEC = $(OUTDIR)/adlc + +# set VPATH so make knows where to look for source files +Src_Dirs_V = ${Src_Dirs} $(GENERATED)/incls +VPATH += $(Src_Dirs_V:%=%:) + +# set INCLUDES for C preprocessor +Src_Dirs_I = ${Src_Dirs} $(GENERATED) +INCLUDES += $(Src_Dirs_I:%=-I%) + +# Force assertions on. +SYSDEFS += -DASSERT +CPPFLAGS = $(SYSDEFS) $(INCLUDES) + +ifndef USE_GCC + # We need libCstd.so for adlc + CFLAGS += -library=Cstd -g + LFLAGS += -library=Cstd -g +endif + +# CFLAGS_WARN holds compiler options to suppress/enable warnings. +CFLAGS += $(CFLAGS_WARN) + +ifeq ("${Platform_compiler}", "sparcWorks") +# Enable the following CFLAGS addition if you need to compare the +# built ELF objects. +# +# The -g option makes static data global and the "-Qoption ccfe +# -xglobalstatic" option tells the compiler to not globalize static +# data using a unique globalization prefix. Instead force the use +# of a static globalization prefix based on the source filepath so +# the objects from two identical compilations are the same. +#CFLAGS += -Qoption ccfe -xglobalstatic +endif # Platform_compiler == sparcWorks + +OBJECTNAMES = \ + adlparse.o \ + archDesc.o \ + arena.o \ + dfa.o \ + dict2.o \ + filebuff.o \ + forms.o \ + formsopt.o \ + formssel.o \ + main.o \ + adlc-opcodes.o \ + output_c.o \ + output_h.o \ + +OBJECTS = $(OBJECTNAMES:%=$(OUTDIR)/%) + +GENERATEDNAMES = \ + ad_$(Platform_arch_model).cpp \ + ad_$(Platform_arch_model).hpp \ + ad_$(Platform_arch_model)_clone.cpp \ + ad_$(Platform_arch_model)_expand.cpp \ + ad_$(Platform_arch_model)_format.cpp \ + ad_$(Platform_arch_model)_gen.cpp \ + ad_$(Platform_arch_model)_misc.cpp \ + ad_$(Platform_arch_model)_peephole.cpp \ + ad_$(Platform_arch_model)_pipeline.cpp \ + adGlobals_$(Platform_arch_model).hpp \ + dfa_$(Platform_arch_model).cpp \ + +GENERATEDFILES = $(GENERATEDNAMES:%=$(OUTDIR)/%) + +# ######################################################################### + +all: $(EXEC) + +$(EXEC) : $(OBJECTS) + @echo Making adlc + $(QUIETLY) $(LINK_NOPROF.CC) -o $(EXEC) $(OBJECTS) + +# Random dependencies: +$(OBJECTS): opcodes.hpp classes.hpp adlc.hpp adlcVMDeps.hpp adlparse.hpp archDesc.hpp arena.hpp dict2.hpp filebuff.hpp forms.hpp formsopt.hpp formssel.hpp + +# The source files refer to ostream.h, which sparcworks calls iostream.h +$(OBJECTS): ostream.h + +ostream.h : + @echo >$@ '#include ' + +dump: + : OUTDIR=$(OUTDIR) + : OBJECTS=$(OBJECTS) + : products = $(GENERATEDFILES) + +all: $(GENERATEDFILES) + +$(GENERATEDFILES): refresh_adfiles + +# Get a unique temporary directory name, so multiple makes can run in parallel. +# Note that product files are updated via "mv", which is atomic. +TEMPDIR := $(OUTDIR)/mktmp$(shell echo $$$$) + +ADLCFLAGS = -q -T + +ifdef LP64 +ADLCFLAGS += -D_LP64 +else +ADLCFLAGS += -U_LP64 +endif + +# +# adlc_updater is a simple sh script, under sccs control. It is +# used to selectively update generated adlc files. This should +# provide a nice compilation speed improvement. +# +ADLC_UPDATER_DIRECTORY = $(GAMMADIR)/build/$(OS) +ADLC_UPDATER = adlc_updater + +# This action refreshes all generated adlc files simultaneously. +# The way it works is this: +# 1) create a scratch directory to work in. +# 2) if the current working directory does not have $(ADLC_UPDATER), copy it. +# 3) run the compiled adlc executable. This will create new adlc files in the scratch directory. +# 4) call $(ADLC_UPDATER) on each generated adlc file. It will selectively update changed or missing files. +# 5) If we actually updated any files, echo a notice. +# +refresh_adfiles: $(EXEC) $(SOURCE.AD) + @rm -rf $(TEMPDIR); mkdir $(TEMPDIR) + $(QUIETLY) [ -f $(ADLC_UPDATER) ] || ( cp $(ADLC_UPDATER_DIRECTORY)/$(ADLC_UPDATER) . ; chmod +x $(ADLC_UPDATER) ) + $(QUIETLY) $(EXEC) $(ADLCFLAGS) $(SOURCE.AD) \ + -c$(TEMPDIR)/ad_$(Platform_arch_model).cpp -h$(TEMPDIR)/ad_$(Platform_arch_model).hpp -a$(TEMPDIR)/dfa_$(Platform_arch_model).cpp -v$(TEMPDIR)/adGlobals_$(Platform_arch_model).hpp \ + || { rm -rf $(TEMPDIR); exit 1; } + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model).cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model).hpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_clone.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_expand.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_format.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_gen.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_misc.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_peephole.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_pipeline.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) adGlobals_$(Platform_arch_model).hpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) dfa_$(Platform_arch_model).cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) [ -f $(TEMPDIR)/made-change ] \ + || echo "Rescanned $(SOURCE.AD) but encountered no changes." + $(QUIETLY) rm -rf $(TEMPDIR) + + +# ######################################################################### + +$(SOURCE.AD): $(SOURCES.AD) + $(QUIETLY) cat $(SOURCES.AD) > $(SOURCE.AD) + +$(OUTDIR)/%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(COMPILE.CC) -o $@ $< $(COMPILE_DONE) + +# Some object files are given a prefix, to disambiguate +# them from objects of the same name built for the VM. +$(OUTDIR)/adlc-%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(COMPILE.CC) -o $@ $< $(COMPILE_DONE) + +# ######################################################################### + +clean : + rm $(OBJECTS) + +cleanall : + rm $(OBJECTS) $(EXEC) + +# ######################################################################### + +.PHONY: all dump refresh_adfiles clean cleanall diff --git a/hotspot/build/solaris/makefiles/amd64.make b/hotspot/build/solaris/makefiles/amd64.make new file mode 100644 index 00000000000..d4a09b7b778 --- /dev/null +++ b/hotspot/build/solaris/makefiles/amd64.make @@ -0,0 +1,63 @@ +# +# Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Must also specify if CPU is little endian +CFLAGS += -DVM_LITTLE_ENDIAN + +# Not included in includeDB because it has no dependencies +# Obj_Files += solaris_amd64.o +Obj_Files += solaris_x86_64.o + +# +# Special case flags for compilers and compiler versions on amd64. +# +ifeq ("${Platform_compiler}", "sparcWorks") + +# Temporary until C++ compiler is fixed + +# _lwp_create_interpose must have a frame +OPT_CFLAGS/os_solaris_x86_64.o = -xO1 +# force C++ interpreter to be full optimization +#OPT_CFLAGS/interpret.o = -fast -O4 + +# Temporary until SS10 C++ compiler is fixed +OPT_CFLAGS/generateOptoStub.o = -xO2 +OPT_CFLAGS/thread.o = -xO2 + +else + +ifeq ("${Platform_compiler}", "gcc") +# gcc +# The serviceability agent relies on frame pointer (%rbp) to walk thread stack +CFLAGS += -fno-omit-frame-pointer +# force C++ interpreter to be full optimization +#OPT_CFLAGS/interpret.o = -O3 + +else +# error +_JUNK2_ := $(shell echo >&2 \ + "*** ERROR: this compiler is not yet supported by this code base!") + @exit 1 +endif +endif diff --git a/hotspot/build/solaris/makefiles/buildtree.make b/hotspot/build/solaris/makefiles/buildtree.make new file mode 100644 index 00000000000..84d2beb9ed2 --- /dev/null +++ b/hotspot/build/solaris/makefiles/buildtree.make @@ -0,0 +1,362 @@ +# +# Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Usage: +# +# $(MAKE) -f buildtree.make ARCH=arch BUILDARCH=buildarch LIBARCH=libarch +# GAMMADIR=dir OS_FAMILY=os VARIANT=variant +# +# The macros ARCH, GAMMADIR, OS_FAMILY and VARIANT must be defined in the +# environment or on the command-line: +# +# ARCH - sparc, i486, ... HotSpot cpu and os_cpu source directory +# BUILDARCH - build directory +# LIBARCH - the corresponding directory in JDK/JRE +# GAMMADIR - top of workspace +# OS_FAMILY - operating system +# VARIANT - core, compiler1, compiler2, or tiered +# HOTSPOT_RELEASE_VERSION - .-b (11.0-b07) +# HOTSPOT_BUILD_VERSION - internal, PRTjob ID, JPRTjob ID +# JRE_RELEASE_VERSION - .. (1.7.0) +# +# Builds the directory trees with makefiles plus some convenience files in +# each directory: +# +# Makefile - for "make foo" +# flags.make - with macro settings +# vm.make - to support making "$(MAKE) -v vm.make" in makefiles +# adlc.make - +# jvmti.make - generate JVMTI bindings from the spec (JSR-163) +# sa.make - generate SA jar file and natives +# env.[ck]sh - environment settings +# test_gamma - script to run the Queens program +# +# The makefiles are split this way so that "make foo" will run faster by not +# having to read the dependency files for the vm. + +include $(GAMMADIR)/make/scm.make + +# 'gmake MAKE_VERBOSE=y' or 'gmake QUIETLY=' gives all the gory details. +QUIETLY$(MAKE_VERBOSE) = @ + +# For now, until the compiler is less wobbly: +TESTFLAGS = -Xbatch -showversion + +### maye ARCH_XXX instead? +ifdef USE_GCC +PLATFORM_FILE = $(GAMMADIR)/build/$(OS_FAMILY)/platform_$(BUILDARCH).gcc +GCC_LIB = /usr/local/lib +else +PLATFORM_FILE = $(GAMMADIR)/build/$(OS_FAMILY)/platform_$(BUILDARCH) +GCC_LIB = +endif + +ifdef FORCE_TIERED +ifeq ($(VARIANT),tiered) +PLATFORM_DIR = $(OS_FAMILY)_$(BUILDARCH)_compiler2 +else +PLATFORM_DIR = $(OS_FAMILY)_$(BUILDARCH)_$(VARIANT) +endif +else +PLATFORM_DIR = $(OS_FAMILY)_$(BUILDARCH)_$(VARIANT) +endif + +# +# We do two levels of exclusion in the shared directory. +# TOPLEVEL excludes are pruned, they are not recursively searched, +# but lower level directories can be named without fear of collision. +# ALWAYS excludes are excluded at any level in the directory tree. +# + +ALWAYS_EXCLUDE_DIRS = $(SCM_DIRS) + +ifeq ($(VARIANT),tiered) +TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name agent +else +ifeq ($(VARIANT),compiler2) +TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name c1 -o -name agent +else +# compiler1 and core use the same exclude list +TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name opto -o -name libadt -o -name agent +endif +endif + +# Get things from the platform file. +COMPILER = $(shell sed -n 's/^compiler[ ]*=[ ]*//p' $(PLATFORM_FILE)) + +SIMPLE_DIRS = \ + $(PLATFORM_DIR)/generated/incls \ + $(PLATFORM_DIR)/generated/adfiles \ + $(PLATFORM_DIR)/generated/jvmtifiles + +TARGETS = debug fastdebug jvmg optimized product profiled +SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) + +# For dependencies and recursive makes. +BUILDTREE_MAKE = $(GAMMADIR)/build/$(OS_FAMILY)/makefiles/buildtree.make + +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make sa.make \ + env.ksh env.csh .dbxrc test_gamma + +BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ + ARCH=$(ARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) + +# Define variables to be set in flags.make. +# Default values are set in make/defs.make. +ifeq ($(HOTSPOT_BUILD_VERSION),) + HS_BUILD_VER=$(HOTSPOT_RELEASE_VERSION) +else + HS_BUILD_VER=$(HOTSPOT_RELEASE_VERSION)-$(HOTSPOT_BUILD_VERSION) +endif +# Set BUILD_USER from system-dependent hints: $LOGNAME, $(whoami) +ifndef HOTSPOT_BUILD_USER + HOTSPOT_BUILD_USER := $(shell echo $$LOGNAME) +endif +ifndef HOTSPOT_BUILD_USER + HOTSPOT_BUILD_USER := $(shell whoami) +endif +# Define HOTSPOT_VM_DISTRO based on settings in build/hotspot_distro +# or build/closed/hotspot_distro. +ifndef HOTSPOT_VM_DISTRO + CLOSED_DIR_EXISTS := $(shell \ + if [ -d $(GAMMADIR)/build/closed ] ; then \ + echo true; \ + else \ + echo false; \ + fi) + ifeq ($(CLOSED_DIR_EXISTS), true) + include $(GAMMADIR)/build/closed/hotspot_distro + else + include $(GAMMADIR)/build/hotspot_distro + endif +endif + +BUILDTREE_VARS += HOTSPOT_RELEASE_VERSION=$(HS_BUILD_VER) HOTSPOT_BUILD_VERSION= JRE_RELEASE_VERSION=$(JRE_RELEASE_VERSION) + +BUILDTREE = \ + $(MAKE) -f $(BUILDTREE_MAKE) $(BUILDTREE_TARGETS) $(BUILDTREE_VARS) + +BUILDTREE_COMMENT = echo "\# Generated by $(BUILDTREE_MAKE)" + +all: $(SUBMAKE_DIRS) + +# Run make in each subdirectory recursively. +$(SUBMAKE_DIRS): $(SIMPLE_DIRS) FORCE + $(QUIETLY) [ -d $@ ] || { mkdir -p $@; } + $(QUIETLY) cd $@ && $(BUILDTREE) TARGET=$(@F) + $(QUIETLY) touch $@ + +$(SIMPLE_DIRS): + $(QUIETLY) mkdir -p $@ + +flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo "Platform_file = $(PLATFORM_FILE)" | sed 's|$(GAMMADIR)|$$(GAMMADIR)|'; \ + sed -n '/=/s/^ */Platform_/p' < $(PLATFORM_FILE); \ + echo; \ + echo "GAMMADIR = $(GAMMADIR)"; \ + echo "SYSDEFS = \$$(Platform_sysdefs)"; \ + echo "SRCARCH = $(ARCH)"; \ + echo "BUILDARCH = $(BUILDARCH)"; \ + echo "LIBARCH = $(LIBARCH)"; \ + echo "TARGET = $(TARGET)"; \ + echo "HS_BUILD_VER = $(HS_BUILD_VER)"; \ + echo "JRE_RELEASE_VER = $(JRE_RELEASE_VERSION)"; \ + echo "SA_BUILD_VERSION = $(HS_BUILD_VER)"; \ + echo "HOTSPOT_BUILD_USER = $(HOTSPOT_BUILD_USER)"; \ + echo "HOTSPOT_VM_DISTRO = $(HOTSPOT_VM_DISTRO)"; \ + echo "$(LP64_SETTING/$(DATA_MODE))"; \ + echo; \ + echo "Src_Dirs = \\"; \ + sed 's/$$/ \\/;s|$(GAMMADIR)|$$(GAMMADIR)|' ../shared_dirs.lst; \ + echo "\$$(GAMMADIR)/src/cpu/$(ARCH)/vm \\"; \ + echo "\$$(GAMMADIR)/src/os/$(OS_FAMILY)/vm \\"; \ + echo "\$$(GAMMADIR)/src/os_cpu/$(OS_FAMILY)_$(ARCH)/vm"; \ + [ -n "$(CFLAGS_BROWSE)" ] && \ + echo && echo "CFLAGS_BROWSE = $(CFLAGS_BROWSE)"; \ + [ -n "$(HOTSPOT_EXTRA_SYSDEFS)" ] && \ + echo && \ + echo "HOTSPOT_EXTRA_SYSDEFS\$$(HOTSPOT_EXTRA_SYSDEFS) = $(HOTSPOT_EXTRA_SYSDEFS)" && \ + echo "SYSDEFS += \$$(HOTSPOT_EXTRA_SYSDEFS)"; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(VARIANT).make"; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(COMPILER).make"; \ + ) > $@ + +flags_vm.make: $(BUILDTREE_MAKE) ../shared_dirs.lst + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + [ "$(TARGET)" = profiled ] && \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/optimized.make"; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(TARGET).make"; \ + ) > $@ + +../shared_dirs.lst: $(BUILDTREE_MAKE) $(GAMMADIR)/src/share/vm + @echo Creating directory list $@ + $(QUIETLY) find $(GAMMADIR)/src/share/vm/* -prune \ + -type d \! \( $(TOPLEVEL_EXCLUDE_DIRS) \) -exec find {} \ + \( $(ALWAYS_EXCLUDE_DIRS) \) -prune -o -type d -print \; > $@ + +Makefile: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/top.make"; \ + ) > $@ + +vm.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo include flags_vm.make; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +adlc.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +jvmti.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +sa.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/build/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +env.ksh: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + [ -n "$$JAVA_HOME" ] && { echo ": \$${JAVA_HOME:=$${JAVA_HOME}}"; }; \ + { \ + echo "LD_LIBRARY_PATH=.:$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ + echo "unset LD_LIBRARY_PATH_32"; \ + echo "unset LD_LIBRARY_PATH_64"; \ + echo "CLASSPATH=$${CLASSPATH:+$$CLASSPATH:}.:\$${JAVA_HOME}/jre/lib/rt.jar:\$${JAVA_HOME}/jre/lib/i18n.jar"; \ + } | sed s:$${JAVA_HOME:--------}:\$${JAVA_HOME}:g; \ + echo "HOTSPOT_BUILD_USER=\"$${LOGNAME:-$$USER} in `basename $(GAMMADIR)`\""; \ + echo "export JAVA_HOME LD_LIBRARY_PATH CLASSPATH HOTSPOT_BUILD_USER"; \ + ) > $@ + +env.csh: env.ksh + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + [ -n "$$JAVA_HOME" ] && \ + { echo "if (! \$$?JAVA_HOME) setenv JAVA_HOME \"$$JAVA_HOME\""; }; \ + sed -n 's/^\([A-Za-z_][A-Za-z0-9_]*\)=/setenv \1 /p' $?; \ + ) > $@ + +.dbxrc: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + echo "echo '# Loading $(PLATFORM_DIR)/$(TARGET)/.dbxrc'"; \ + echo "if [ -f \"\$${HOTSPOT_DBXWARE}\" ]"; \ + echo "then"; \ + echo " source \"\$${HOTSPOT_DBXWARE}\""; \ + echo "elif [ -f \"\$$HOME/.dbxrc\" ]"; \ + echo "then"; \ + echo " source \"\$$HOME/.dbxrc\""; \ + echo "fi"; \ + ) > $@ + +# Skip the test for product builds (which only work when installed in a JDK), to +# avoid exiting with an error and causing make to halt. +NO_TEST_MSG = \ + echo "$@: skipping the test--this build must be tested in a JDK." + +NO_JAVA_HOME_MSG = \ + echo "JAVA_HOME must be set to run this test." + +DATA_MODE = $(DATA_MODE/$(BUILDARCH)) +JAVA_FLAG = $(JAVA_FLAG/$(DATA_MODE)) + +DATA_MODE/i486 = 32 +DATA_MODE/sparc = 32 +DATA_MODE/sparcv9 = 64 +DATA_MODE/amd64 = 64 +DATA_MODE/ia64 = 64 + +# This bit is needed to enable local rebuilds. +# Unless the makefile itself sets LP64, any environmental +# setting of LP64 will interfere with the build. +LP64_SETTING/32 = LP64 = \#empty +LP64_SETTING/64 = LP64 = 1 + +JAVA_FLAG/32 = -d32 +JAVA_FLAG/64 = -d64 + +WRONG_DATA_MODE_MSG = \ + echo "JAVA_HOME must point to $(DATA_MODE)bit JDK." + +test_gamma: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + echo '#!/bin/ksh'; \ + $(BUILDTREE_COMMENT); \ + echo '. ./env.ksh'; \ + echo "if [ -z \$$JAVA_HOME ]; then { $(NO_JAVA_HOME_MSG); exit 0; }; fi"; \ + echo "if ! \$${JAVA_HOME}/bin/java $(JAVA_FLAG) -fullversion 2>&1 > /dev/null"; \ + echo "then"; \ + echo " $(WRONG_DATA_MODE_MSG); exit 0;"; \ + echo "fi"; \ + echo 'CLASSPATH="$(GAMMADIR)/build/$(OS_FAMILY):$$CLASSPATH"'; \ + echo '[ -f gamma_g ] && { gamma=gamma_g; }'; \ + echo './$${gamma:-gamma} $(TESTFLAGS) Queens < /dev/null'; \ + ) > $@ + $(QUIETLY) chmod +x $@ + +FORCE: + +.PHONY: all FORCE diff --git a/hotspot/build/solaris/makefiles/compiler1.make b/hotspot/build/solaris/makefiles/compiler1.make new file mode 100644 index 00000000000..3573eccfac4 --- /dev/null +++ b/hotspot/build/solaris/makefiles/compiler1.make @@ -0,0 +1,31 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making client version of VM + +TYPE=COMPILER1 + +VM_SUBDIR = client + +CFLAGS += -DCOMPILER1 diff --git a/hotspot/build/solaris/makefiles/compiler2.make b/hotspot/build/solaris/makefiles/compiler2.make new file mode 100644 index 00000000000..5ef2129ae5a --- /dev/null +++ b/hotspot/build/solaris/makefiles/compiler2.make @@ -0,0 +1,31 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making server version of VM + +TYPE=COMPILER2 + +VM_SUBDIR = server + +CFLAGS += -DCOMPILER2 diff --git a/hotspot/build/solaris/makefiles/core.make b/hotspot/build/solaris/makefiles/core.make new file mode 100644 index 00000000000..e52d5b1ecca --- /dev/null +++ b/hotspot/build/solaris/makefiles/core.make @@ -0,0 +1,34 @@ +# +# Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making core version of VM + +# Note the effect on includeDB lists in top.make: +# includeDB_compiler* and ad_.*pp are excluded from the build, +TYPE=CORE + +# There is no "core" directory in JDK. Install core build in server directory. +VM_SUBDIR = server + +# Note: macros.hpp defines CORE diff --git a/hotspot/build/solaris/makefiles/cscope.make b/hotspot/build/solaris/makefiles/cscope.make new file mode 100644 index 00000000000..20644ec8151 --- /dev/null +++ b/hotspot/build/solaris/makefiles/cscope.make @@ -0,0 +1,157 @@ +# +# Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# +# The cscope.out file is made in the current directory and spans the entire +# source tree. +# +# Things to note: +# 1. We use relative names for cscope. +# 2. We *don't* remove the old cscope.out file, because cscope is smart +# enough to only build what has changed. It can be confused, however, +# if files are renamed or removed, so it may be necessary to manually +# remove cscope.out if a lot of reorganization has occurred. +# + +include $(GAMMADIR)/make/scm.make + +NAWK = /usr/xpg4/bin/awk +RM = rm -f +CS_TOP = ../.. + +CSDIRS = $(CS_TOP)/src $(CS_TOP)/build +CSINCS = $(CSDIRS:%=-I%) + +CSCOPE = cscope +CSCOPE_FLAGS = -b + +# Allow .java files to be added from the environment (CSCLASSES=yes). +ifdef CSCLASSES +ADDCLASSES= -o -name '*.java' +endif + +# Adding CClassHeaders also pushes the file count of a full workspace up about +# 200 files (these files also don't exist in a new workspace, and thus will +# cause the recreation of the database as they get created, which might seem +# a little confusing). Thus allow these files to be added from the environment +# (CSHEADERS=yes). +ifndef CSHEADERS +RMCCHEADERS= -o -name CClassHeaders +endif + +# Use CS_GENERATED=x to include auto-generated files in the build directories. +ifdef CS_GENERATED +CS_ADD_GENERATED = -o -name '*.incl' +else +CS_PRUNE_GENERATED = -o -name '${OS}_*_core' -o -name '${OS}_*_compiler?' +endif + +# OS-specific files for other systems are excluded by default. Use CS_OS=yes +# to include platform-specific files for other platforms. +ifndef CS_OS +CS_OS = linux macos solaris win32 +CS_PRUNE_OS = $(patsubst %,-o -name '*%*',$(filter-out ${OS},${CS_OS})) +endif + +# Processor-specific files for other processors are excluded by default. Use +# CS_CPU=x to include platform-specific files for other platforms. +ifndef CS_CPU +CS_CPU = i486 sparc amd64 ia64 +CS_PRUNE_CPU = $(patsubst %,-o -name '*%*',$(filter-out ${SRCARCH},${CS_CPU})) +endif + +# What files should we include? A simple rule might be just those files under +# SCCS control, however this would miss files we create like the opcodes and +# CClassHeaders. The following attempts to find everything that is *useful*. +# (.del files are created by sccsrm, demo directories contain many .java files +# that probably aren't useful for development, and the pkgarchive may contain +# duplicates of files within the source hierarchy). + +# Directories to exclude. +CS_PRUNE_STD = $(SCM_DIRS) \ + -o -name '.del-*' \ + -o -name '*demo' \ + -o -name pkgarchive + +CS_PRUNE = $(CS_PRUNE_STD) \ + $(CS_PRUNE_OS) \ + $(CS_PRUNE_CPU) \ + $(CS_PRUNE_GENERATED) \ + $(RMCCHEADERS) + +# File names to include. +CSFILENAMES = -name '*.[ch]pp' \ + -o -name '*.[Ccshlxy]' \ + $(CS_ADD_GENERATED) \ + -o -name '*.d' \ + -o -name '*.il' \ + -o -name '*.cc' \ + -o -name '*[Mm]akefile*' \ + -o -name '*.gmk' \ + -o -name '*.make' \ + -o -name '*.ad' \ + $(ADDCLASSES) + +.PRECIOUS: cscope.out + +cscope cscope.out: cscope.files FORCE + $(CSCOPE) $(CSCOPE_FLAGS) + +# The .raw file is reordered here in an attempt to make cscope display the most +# relevant files first. +cscope.files: .cscope.files.raw + echo "$(CSINCS)" > $@ + -egrep -v "\.java|\/build\/" $< >> $@ + -fgrep ".java" $< >> $@ + -fgrep "/build/" $< >> $@ + +.cscope.files.raw: .nametable.files + -find $(CSDIRS) -type d \( $(CS_PRUNE) \) -prune -o \ + -type f \( $(CSFILENAMES) \) -print > $@ + +cscope.clean: nametable.clean + -$(RM) cscope.out cscope.files .cscope.files.raw + +TAGS: cscope.files FORCE + egrep -v '^-|^$$' $< | etags --members - + +TAGS.clean: nametable.clean + -$(RM) TAGS + +# .nametable.files and .nametable.files.tmp are used to determine if any files +# were added to/deleted from/renamed in the workspace. If not, then there's +# normally no need to run find. To force a 'find': gmake nametable.clean. +.nametable.files: .nametable.files.tmp + cmp -s $@ $< || cp $< $@ + +.nametable.files.tmp: $(CS_TOP)/Codemgr_wsdata/nametable + $(NAWK) \ + '{ if (sub("( [a-z0-9]{2,8}){4}$$", "")) print $$0; }' $< > $@ + +nametable.clean: + -$(RM) .nametable.files .nametable.files.tmp + +FORCE: + +.PHONY: cscope cscope.clean TAGS.clean nametable.clean FORCE diff --git a/hotspot/build/solaris/makefiles/debug.make b/hotspot/build/solaris/makefiles/debug.make new file mode 100644 index 00000000000..f2a2786ecf6 --- /dev/null +++ b/hotspot/build/solaris/makefiles/debug.make @@ -0,0 +1,59 @@ +# +# Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making debug version of VM + +# Compiler specific DEBUG_CFLAGS are passed in from gcc.make, sparcWorks.make +DEBUG_CFLAGS/DEFAULT= $(DEBUG_CFLAGS) +DEBUG_CFLAGS/BYFILE = $(DEBUG_CFLAGS/$@)$(DEBUG_CFLAGS/DEFAULT$(DEBUG_CFLAGS/$@)) + +ifeq ("${Platform_compiler}", "sparcWorks") +ifeq ($(shell expr $(COMPILER_REV) \>= 5.8), 1) + # SS11 SEGV when compiling with -g and -xarch=v8, using different backend + DEBUG_CFLAGS/compileBroker.o = $(DEBUG_CFLAGS) -xO0 + DEBUG_CFLAGS/jvmtiTagMap.o = $(DEBUG_CFLAGS) -xO0 +endif +endif + +CFLAGS += $(DEBUG_CFLAGS/BYFILE) + +# Linker mapfiles +MAPFILE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers \ + $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-debug \ + $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-nonproduct + +# This mapfile is only needed when compiling with dtrace support, +# and mustn't be otherwise. +MAPFILE_DTRACE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-$(TYPE) + +_JUNK_ := $(shell echo >&2 ""\ + "-------------------------------------------------------------------------\n" \ + "WARNING: 'gnumake debug' is deprecated. It will be removed in the future.\n" \ + "Please use 'gnumake jvmg' to build debug JVM. \n" \ + "-------------------------------------------------------------------------\n") + +G_SUFFIX = +VERSION = debug +SYSDEFS += -DASSERT -DDEBUG +PICFLAGS = DEFAULT diff --git a/hotspot/build/solaris/makefiles/defs.make b/hotspot/build/solaris/makefiles/defs.make new file mode 100644 index 00000000000..a56e01b65d4 --- /dev/null +++ b/hotspot/build/solaris/makefiles/defs.make @@ -0,0 +1,90 @@ +# +# Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# The common definitions for hotspot solaris builds. +# Include the top level defs.make under make directory instead of this one. +# This file is included into make/defs.make. + +# Need PLATFORM (os-arch combo names) for jdk and hotspot, plus libarch name +SLASH_JAVA ?= /java +ARCH:=$(shell uname -p) +PATH_SEP = : +ifeq ($(LP64), 1) + ARCH_DATA_MODEL=64 +else + ARCH_DATA_MODEL=32 +endif + +ifeq ($(ARCH),sparc) + ifeq ($(ARCH_DATA_MODEL), 64) + MAKE_ARGS += LP64=1 + PLATFORM=solaris-sparcv9 + VM_PLATFORM=solaris_sparcv9 + else + PLATFORM=solaris-sparc + VM_PLATFORM=solaris_sparc + endif + HS_ARCH=sparc +else + ifeq ($(ARCH_DATA_MODEL), 64) + MAKE_ARGS += LP64=1 + PLATFORM=solaris-amd64 + VM_PLATFORM=solaris_amd64 + HS_ARCH=x86 + else + PLATFORM=solaris-i586 + VM_PLATFORM=solaris_i486 + HS_ARCH=x86 + endif +endif + +JDK_INCLUDE_SUBDIR=solaris + +# FIXUP: The subdirectory for a debug build is NOT the same on all platforms +VM_DEBUG=jvmg + +EXPORT_LIST += $(EXPORT_DOCS_DIR)/platform/jvmti/jvmti.html +EXPORT_SERVER_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/server +EXPORT_LIST += $(EXPORT_SERVER_DIR)/Xusage.txt +EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjsig.so +EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm.so +EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm_db.so +EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm_dtrace.so +ifeq ($(ARCH_DATA_MODEL), 32) + EXPORT_CLIENT_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/client + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/Xusage.txt + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjsig.so + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm.so + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm_db.so + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm_dtrace.so + ifeq ($(ARCH),sparc) + EXPORT_LIST += $(EXPORT_SERVER_DIR)/64/libjvm_db.so + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/64/libjvm_db.so + EXPORT_LIST += $(EXPORT_SERVER_DIR)/64/libjvm_dtrace.so + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/64/libjvm_dtrace.so + endif +endif + +EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.so +EXPORT_LIST += $(EXPORT_LIB_DIR)/sa-jdi.jar diff --git a/hotspot/build/solaris/makefiles/dtrace.make b/hotspot/build/solaris/makefiles/dtrace.make new file mode 100644 index 00000000000..4c974ede212 --- /dev/null +++ b/hotspot/build/solaris/makefiles/dtrace.make @@ -0,0 +1,249 @@ +# +# Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Rules to build jvm_db/dtrace, used by vm.make + +# we build libjvm_dtrace/libjvm_db/dtrace for COMPILER1 and COMPILER2 +# but not for CORE configuration + +ifneq ("${TYPE}", "CORE") +ifneq ("${TYPE}", "KERNEL") + +ifdef USE_GCC + +dtraceCheck: + $(QUIETLY) echo "**NOTICE** Dtrace support disabled for gcc builds" + +else + + +JVM_DB = libjvm_db +LIBJVM_DB = libjvm$(G_SUFFIX)_db.so + +JVM_DTRACE = jvm_dtrace +LIBJVM_DTRACE = libjvm$(G_SUFFIX)_dtrace.so + +JVMOFFS = JvmOffsets +JVMOFFS.o = $(JVMOFFS).o +GENOFFS = generate$(JVMOFFS) + +DTRACE_SRCDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/dtrace +DTRACE = dtrace +DTRACE.o = $(DTRACE).o + +# to remove '-g' option which causes link problems +# also '-z nodefs' is used as workaround +GENOFFS_CFLAGS = $(shell echo $(CFLAGS) | sed -e 's/ -g / /g' -e 's/ -g0 / /g';) + +ifdef LP64 +DTRACE_OPTS = -64 -D_LP64 +endif + +# making libjvm_db + +INCLS = $(GENERATED)/incls + +# Use mapfile with libjvm_db.so +LIBJVM_DB_MAPFILE = $(MAKEFILES_DIR)/mapfile-vers-jvm_db +LFLAGS_JVM_DB += $(MAPFLAG:FILENAME=$(LIBJVM_DB_MAPFILE)) + +# Use mapfile with libjvm_dtrace.so +LIBJVM_DTRACE_MAPFILE = $(MAKEFILES_DIR)/mapfile-vers-jvm_dtrace +LFLAGS_JVM_DTRACE += $(MAPFLAG:FILENAME=$(LIBJVM_DTRACE_MAPFILE)) + +ifdef USE_GCC +LFLAGS_JVM_DB += -D_REENTRANT $(PICFLAG) +LFLAGS_JVM_DTRACE += -D_REENTRANT $(PICFLAG) +else +LFLAGS_JVM_DB += -mt $(PICFLAG) -xnolib +LFLAGS_JVM_DTRACE += -mt $(PICFLAG) -xnolib +endif + +ISA = $(subst i386,i486,$(shell isainfo -n)) + +# Making 64/libjvm_db.so: 64-bit version of libjvm_db.so which handles 32-bit libjvm.so +ifneq ("${ISA}","${BUILDARCH}") + +XLIBJVM_DB = 64/$(LIBJVM_DB) +XLIBJVM_DTRACE = 64/$(LIBJVM_DTRACE) +XARCH = $(subst sparcv9,v9,$(shell echo $(ISA))) + +$(XLIBJVM_DB): $(DTRACE_SRCDIR)/$(JVM_DB).c $(JVMOFFS).h $(LIBJVM_DB_MAPFILE) + @echo Making $@ + $(QUIETLY) mkdir -p 64/ ; \ + $(CC) $(SYMFLAG) -xarch=$(XARCH) -D$(TYPE) -I. -I$(GENERATED) \ + $(SHARED_FLAG) $(LFLAGS_JVM_DB) -o $@ $(DTRACE_SRCDIR)/$(JVM_DB).c -lc +$(XLIBJVM_DTRACE): $(DTRACE_SRCDIR)/$(JVM_DTRACE).c $(DTRACE_SRCDIR)/$(JVM_DTRACE).h $(LIBJVM_DTRACE_MAPFILE) + @echo Making $@ + $(QUIETLY) mkdir -p 64/ ; \ + $(CC) $(SYMFLAG) -xarch=$(XARCH) -D$(TYPE) -I. \ + $(SHARED_FLAG) $(LFLAGS_JVM_DTRACE) -o $@ $(DTRACE_SRCDIR)/$(JVM_DTRACE).c -lc -lthread -ldoor +endif # ifneq ("${ISA}","${BUILDARCH}") + +ifdef USE_GCC +LFLAGS_GENOFFS += -D_REENTRANT +else +LFLAGS_GENOFFS += -mt -xnolib -norunpath +endif + +lib$(GENOFFS).so: $(DTRACE_SRCDIR)/$(GENOFFS).cpp $(DTRACE_SRCDIR)/$(GENOFFS).h \ + $(INCLS)/_vmStructs.cpp.incl $(LIBJVM.o) + $(QUIETLY) $(CCC) $(CPPFLAGS) $(GENOFFS_CFLAGS) $(SHARED_FLAG) $(PICFLAG) \ + $(LFLAGS_GENOFFS) -o $@ $(DTRACE_SRCDIR)/$(GENOFFS).cpp -lc + +$(GENOFFS): $(DTRACE_SRCDIR)/$(GENOFFS)Main.c lib$(GENOFFS).so + $(QUIETLY) $(LINK.CC) -z nodefs -o $@ $(DTRACE_SRCDIR)/$(GENOFFS)Main.c \ + ./lib$(GENOFFS).so + +# $@.tmp is created first. It's to avoid empty $(JVMOFFS).h produced in error case. +$(JVMOFFS).h: $(GENOFFS) + $(QUIETLY) LD_LIBRARY_PATH=. ./$(GENOFFS) -header > $@.tmp ; \ + if [ `diff $@.tmp $@ > /dev/null 2>&1; echo $$?` -ne 0 ] ; \ + then rm -f $@; mv $@.tmp $@; echo Updated $@ ; \ + else rm -f $@.tmp; \ + fi + +$(JVMOFFS)Index.h: $(GENOFFS) + $(QUIETLY) LD_LIBRARY_PATH=. ./$(GENOFFS) -index > $@.tmp ; \ + if [ `diff $@.tmp $@ > /dev/null 2>&1; echo $$?` -ne 0 ] ; \ + then rm -f $@; mv $@.tmp $@; echo Updated $@ ; \ + else rm -f $@.tmp; \ + fi + +$(JVMOFFS).cpp: $(GENOFFS) $(JVMOFFS).h $(JVMOFFS)Index.h + $(QUIETLY) LD_LIBRARY_PATH=. ./$(GENOFFS) -table > $@.tmp ; \ + if [ `diff $@.tmp $@ > /dev/null 2>&1; echo $$?` -ne 0 ] ; \ + then rm -f $@; mv $@.tmp $@; echo Updated $@ ; \ + else rm -f $@.tmp; \ + fi + +$(JVMOFFS.o): $(JVMOFFS).h $(JVMOFFS).cpp + $(QUIETLY) $(CCC) -c -I. -o $@ $(ARCHFLAG) -D$(TYPE) $(JVMOFFS).cpp + +$(LIBJVM_DB): $(DTRACE_SRCDIR)/$(JVM_DB).c $(JVMOFFS.o) $(XLIBJVM_DB) $(LIBJVM_DB_MAPFILE) + @echo Making $@ + $(QUIETLY) $(CC) $(SYMFLAG) $(ARCHFLAG) -D$(TYPE) -I. -I$(GENERATED) \ + $(SHARED_FLAG) $(LFLAGS_JVM_DB) -o $@ $(DTRACE_SRCDIR)/$(JVM_DB).c -lc + +$(LIBJVM_DTRACE): $(DTRACE_SRCDIR)/$(JVM_DTRACE).c $(XLIBJVM_DTRACE) $(DTRACE_SRCDIR)/$(JVM_DTRACE).h $(LIBJVM_DTRACE_MAPFILE) + @echo Making $@ + $(QUIETLY) $(CC) $(SYMFLAG) $(ARCHFLAG) -D$(TYPE) -I. \ + $(SHARED_FLAG) $(LFLAGS_JVM_DTRACE) -o $@ $(DTRACE_SRCDIR)/$(JVM_DTRACE).c -lc -lthread -ldoor + +$(DTRACE).d: $(DTRACE_SRCDIR)/hotspot.d $(DTRACE_SRCDIR)/hotspot_jni.d \ + $(DTRACE_SRCDIR)/hs_private.d $(DTRACE_SRCDIR)/jhelper.d + $(QUIETLY) cat $^ > $@ + +# Dtrace is available, so we build $(DTRACE.o) +$(DTRACE.o): $(DTRACE).d $(JVMOFFS).h $(JVMOFFS)Index.h $(DTraced_Files) + @echo Compiling $(DTRACE).d + + $(QUIETLY) $(DTRACE_PROG) $(DTRACE_OPTS) -C -I. -G -o $@ -s $(DTRACE).d \ + $(DTraced_Files) ||\ + STATUS=$$?;\ + if [ x"$$STATUS" = x"1" -a \ + x`uname -r` = x"5.10" -a \ + x`uname -p` = x"sparc" ]; then\ + echo "*****************************************************************";\ + echo "* If you are building server compiler, and the error message is ";\ + echo "* \"incorrect ELF machine type...\", you have run into solaris bug ";\ + echo "* 6213962, \"dtrace -G doesn't work on sparcv8+ object files\".";\ + echo "* Either patch/upgrade your system (>= S10u1_15), or set the ";\ + echo "* environment variable HOTSPOT_DISABLE_DTRACE_PROBES to disable ";\ + echo "* dtrace probes for this build.";\ + echo "*****************************************************************";\ + fi;\ + exit $$STATUS + # Since some DTraced_Files are in LIBJVM.o and they are touched by this + # command, and libgenerateJvmOffsets.so depends on LIBJVM.o, 'make' will + # think it needs to rebuild libgenerateJvmOffsets.so and thus JvmOffsets* + # files, but it doesn't, so we touch the necessary files to prevent later + # recompilation. Note: we only touch the necessary files if they already + # exist in order to close a race where an empty file can be created + # before the real build rule is executed. + # But, we can't touch the *.h files: This rule depends + # on them, and that would cause an infinite cycle of rebuilding. + # Neither the *.h or *.ccp files need to be touched, since they have + # rules which do not update them when the generator file has not + # changed their contents. + $(QUIETLY) if [ -f lib$(GENOFFS).so ]; then touch lib$(GENOFFS).so; fi + $(QUIETLY) if [ -f $(GENOFFS) ]; then touch $(GENOFFS); fi + $(QUIETLY) if [ -f $(JVMOFFS.o) ]; then touch $(JVMOFFS.o); fi + +.PHONY: dtraceCheck + +SYSTEM_DTRACE_PROG = /usr/sbin/dtrace +PATCH_DTRACE_PROG = /opt/SUNWdtrd/sbin/dtrace +systemDtraceFound := $(wildcard ${SYSTEM_DTRACE_PROG}) +patchDtraceFound := $(wildcard ${PATCH_DTRACE_PROG}) + +ifneq ("$(patchDtraceFound)", "") +DTRACE_PROG=$(PATCH_DTRACE_PROG) +DTRACE_INCL=-I/opt/SUNWdtrd/include +else +ifneq ("$(systemDtraceFound)", "") +DTRACE_PROG=$(SYSTEM_DTRACE_PROG) +else + +endif # ifneq ("$(systemDtraceFound)", "") +endif # ifneq ("$(patchDtraceFound)", "") + +ifneq ("${DTRACE_PROG}", "") +ifeq ("${HOTSPOT_DISABLE_DTRACE_PROBES}", "") + +DTRACE_OBJS = $(DTRACE.o) $(JVMOFFS.o) +CFLAGS += $(DTRACE_INCL) -DDTRACE_ENABLED +MAPFILE_DTRACE_OPT = $(MAPFILE_DTRACE) + +dtraceCheck: + +else # manually disabled + +dtraceCheck: + $(QUIETLY) echo "**NOTICE** Dtrace support disabled via environment variable" + +endif # ifeq ("${HOTSPOT_DISABLE_DTRACE_PROBES}", "") + +else # No dtrace program found + +dtraceCheck: + $(QUIETLY) echo "**NOTICE** Dtrace support disabled: not supported by system" + +endif # ifneq ("${dtraceFound}", "") + +endif # ifdef USE_GCC + +else # KERNEL build + +dtraceCheck: + $(QUIETLY) echo "**NOTICE** Dtrace support disabled for KERNEL builds" + +endif # ifneq ("${TYPE}", "KERNEL") + +else # CORE build + +dtraceCheck: + $(QUIETLY) echo "**NOTICE** Dtrace support disabled for CORE builds" + +endif # ifneq ("${TYPE}", "CORE") diff --git a/hotspot/build/solaris/makefiles/fastdebug.make b/hotspot/build/solaris/makefiles/fastdebug.make new file mode 100644 index 00000000000..7f16a0e655b --- /dev/null +++ b/hotspot/build/solaris/makefiles/fastdebug.make @@ -0,0 +1,115 @@ +# +# Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making debug version of VM + +# Compiler specific DEBUG_CFLAGS are passed in from gcc.make, sparcWorks.make +# They may also specify FASTDEBUG_CFLAGS, but it defaults to DEBUG_FLAGS. + +FASTDEBUG_CFLAGS$(FASTDEBUG_CFLAGS) = $(DEBUG_CFLAGS) + +# Compiler specific OPT_CFLAGS are passed in from gcc.make, sparcWorks.make +OPT_CFLAGS/DEFAULT= $(OPT_CFLAGS) +OPT_CFLAGS/BYFILE = $(OPT_CFLAGS/$@)$(OPT_CFLAGS/DEFAULT$(OPT_CFLAGS/$@)) + +ifeq ("${Platform_compiler}", "sparcWorks") +OPT_CFLAGS/SLOWER = -xO2 +ifeq ($(shell expr $(COMPILER_REV) \>= 5.5), 1) +# CC 5.5 has bug 4908364 with -xO4 +OPT_CFLAGS/library_call.o = $(OPT_CFLAGS/SLOWER) +else # COMPILER_REV >= 5.5 +# Compilation of *_.cpp can take an hour or more at O3. Use O2 +# See comments at top of sparc.make. +OPT_CFLAGS/ad_$(Platform_arch).o = $(OPT_CFLAGS/SLOWER) +OPT_CFLAGS/dfa_$(Platform_arch).o = $(OPT_CFLAGS/SLOWER) +endif # COMPILER_REV >= 5.5 + +ifeq (${COMPILER_REV}, 5.0) +# Avoid a compiler bug caused by using -xO -g +# Since the bug also occurs with -xO0, use an innocuous value (must not be null) +OPT_CFLAGS/c1_LIROptimizer_i486.o = -c +endif + +ifeq ($(shell expr $(COMPILER_REV) \< 5.5), 1) +# Same problem with Solaris/x86 compiler (both 5.0 and 5.2) on ad_i486.cpp. +# CC build time is also too long for ad_i486_{gen,misc}.o +OPT_CFLAGS/ad_i486.o = -c +OPT_CFLAGS/ad_i486_gen.o = -c +OPT_CFLAGS/ad_i486_misc.o = -c +ifeq ($(Platform_arch), i486) +# Same problem for the wrapper roosts: jni.o jvm.o +OPT_CFLAGS/jni.o = -c +OPT_CFLAGS/jvm.o = -c +# Same problem in parse2.o (probably the Big Switch over bytecodes) +OPT_CFLAGS/parse2.o = -c +endif # Platform_arch == i486 +endif + +# Frame size > 100k if we allow inlining via -g0! +DEBUG_CFLAGS/bytecodeInterpreter.o = -g +DEBUG_CFLAGS/bytecodeInterpreterWithChecks.o = -g +ifeq ($(Platform_arch), i486) +# ube explodes on x86 +OPT_CFLAGS/bytecodeInterpreter.o = -xO1 +OPT_CFLAGS/bytecodeInterpreterWithChecks.o = -xO1 +endif # Platform_arch == i486 + +endif # Platform_compiler == sparcWorks + +# Workaround for a bug in dtrace. If ciEnv::post_compiled_method_load_event() +# is inlined, the resulting dtrace object file needs a reference to this +# function, whose symbol name is too long for dtrace. So disable inlining +# for this method for now. (fix this when dtrace bug 6258412 is fixed) +OPT_CFLAGS/ciEnv.o = $(OPT_CFLAGS) -xinline=no%__1cFciEnvbFpost_compiled_method_load_event6MpnHnmethod__v_ + + +# (OPT_CFLAGS/SLOWER is also available, to alter compilation of buggy files) + +# If you set HOTSPARC_GENERIC=yes, you disable all OPT_CFLAGS settings +CFLAGS$(HOTSPARC_GENERIC) += $(OPT_CFLAGS/BYFILE) + +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +# The following lines are copied from debug.make, except that we +# consult FASTDEBUG_CFLAGS instead of DEBUG_CFLAGS. +# Compiler specific DEBUG_CFLAGS are passed in from gcc.make, sparcWorks.make +DEBUG_CFLAGS/DEFAULT= $(FASTDEBUG_CFLAGS) +DEBUG_CFLAGS/BYFILE = $(DEBUG_CFLAGS/$@)$(DEBUG_CFLAGS/DEFAULT$(DEBUG_CFLAGS/$@)) +CFLAGS += $(DEBUG_CFLAGS/BYFILE) + +# Linker mapfiles +MAPFILE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers \ + $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-debug \ + $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-nonproduct + +# This mapfile is only needed when compiling with dtrace support, +# and mustn't be otherwise. +MAPFILE_DTRACE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-$(TYPE) + + +G_SUFFIX = +VERSION = optimized +SYSDEFS += -DASSERT -DFASTDEBUG -DCHECK_UNHANDLED_OOPS +PICFLAGS = DEFAULT diff --git a/hotspot/build/solaris/makefiles/gcc.make b/hotspot/build/solaris/makefiles/gcc.make new file mode 100644 index 00000000000..d7314b97a85 --- /dev/null +++ b/hotspot/build/solaris/makefiles/gcc.make @@ -0,0 +1,183 @@ +# +# Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +#------------------------------------------------------------------------ +# CC, CPP & AS + +CPP = g++ +CC = gcc +AS = $(CC) -c + +Compiler = gcc + +# -dumpversion in gcc-2.91 shows "egcs-2.91.66". In later version, it only +# prints the numbers (e.g. "2.95", "3.2.1") +CC_VER_MAJOR := $(shell $(CC) -dumpversion | sed 's/egcs-//' | cut -d'.' -f1) +CC_VER_MINOR := $(shell $(CC) -dumpversion | sed 's/egcs-//' | cut -d'.' -f2) + +# Check for the versions of C++ and C compilers ($CPP and $CC) used. + +# Get the last thing on the line that looks like x.x+ (x is a digit). +COMPILER_REV := \ +$(shell $(CPP) -dumpversion | sed 's/egcs-//' | cut -d'.' -f1) +C_COMPILER_REV := \ +$(shell $(CC) -dumpversion | sed 's/egcs-//' | cut -d'.' -f2) + + +# check for precompiled headers support +ifneq "$(shell expr \( $(CC_VER_MAJOR) \> 3 \) \| \( \( $(CC_VER_MAJOR) = 3 \) \& \( $(CC_VER_MINOR) \>= 4 \) \))" "0" +USE_PRECOMPILED_HEADER=1 +PRECOMPILED_HEADER_DIR=. +PRECOMPILED_HEADER=$(PRECOMPILED_HEADER_DIR)/incls/_precompiled.incl.gch +endif + + +#------------------------------------------------------------------------ +# Compiler flags + +# position-independent code +PICFLAG = -fPIC + +VM_PICFLAG/LIBJVM = $(PICFLAG) +VM_PICFLAG/AOUT = +VM_PICFLAG = $(VM_PICFLAG/$(LINK_INTO)) + +CFLAGS += $(VM_PICFLAG) +CFLAGS += -fno-rtti +CFLAGS += -fno-exceptions +CFLAGS += -D_REENTRANT +CFLAGS += -fcheck-new + +ARCHFLAG = $(ARCHFLAG/$(BUILDARCH)) + +ARCHFLAG/sparc = -m32 -mcpu=v9 +ARCHFLAG/sparcv9 = -m64 -mcpu=v9 +ARCHFLAG/i486 = -m32 -march=i586 +ARCHFLAG/amd64 = -m64 -march=k8 + + +# Optional sub-directory in /usr/lib where BUILDARCH libraries are kept. +ISA_DIR=$(ISA_DIR/$(BUILDARCH)) +ISA_DIR/amd64=/amd64 +ISA_DIR/i486= +ISA_DIR/sparcv9=/64 + + +CFLAGS += $(ARCHFLAG) +AOUT_FLAGS += $(ARCHFLAG) +LFLAGS += $(ARCHFLAG) +ASFLAGS += $(ARCHFLAG) + +ifeq ($(BUILDARCH), amd64) +ASFLAGS += -march=k8 -march=amd64 +LFLAGS += -march=k8 +endif + + +# Use C++ Interpreter +ifdef CC_INTERP + CFLAGS += -DCC_INTERP +endif + +# Keep temporary files (.ii, .s) +ifdef NEED_ASM + CFLAGS += -save-temps +else + CFLAGS += -pipe +endif + + +# Compiler warnings are treated as errors +WARNINGS_ARE_ERRORS = -Werror +# Enable these warnings. See 'info gcc' about details on these options +ADDITIONAL_WARNINGS = -Wpointer-arith -Wconversion -Wsign-compare +CFLAGS_WARN/DEFAULT = $(WARNINGS_ARE_ERRORS) $(ADDITIONAL_WARNINGS) +# Special cases +CFLAGS_WARN/BYFILE = $(CFLAGS_WARN/$@)$(CFLAGS_WARN/DEFAULT$(CFLAGS_WARN/$@)) + +# The flags to use for an Optimized g++ build +OPT_CFLAGS += -O3 + +# Hotspot uses very unstrict aliasing turn this optimization off +OPT_CFLAGS += -fno-strict-aliasing + +# The gcc compiler segv's on ia64 when compiling bytecodeInterpreter.cpp +# if we use expensive-optimizations +# Note: all ia64 setting reflect the ones for linux +# No actial testing was performed: there is no Solaris on ia64 presently +ifeq ($(BUILDARCH), ia64) +OPT_CFLAGS/bytecodeInterpreter.o += -fno-expensive-optimizations +endif + +OPT_CFLAGS/NOOPT=-O0 +#------------------------------------------------------------------------ +# Linker flags + +# statically link libstdc++.so, work with gcc but ignored by g++ +STATIC_STDCXX = -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic + +# statically link libgcc and/or libgcc_s, libgcc does not exist before gcc-3.x. +ifneq ("${CC_VER_MAJOR}", "2") +STATIC_LIBGCC += -static-libgcc +endif + +ifeq ($(BUILDARCH), ia64) +# Note: all ia64 setting reflect the ones for linux +# No actial testing was performed: there is no Solaris on ia64 presently +LFLAGS += -Wl,-relax +endif + +ifdef USE_GNULD +# Enable linker optimization +LFLAGS += -Xlinker -O1 + +# Use $(MAPFLAG:FILENAME=real_file_name) to specify a map file. +MAPFLAG = -Xlinker --version-script=FILENAME +else +MAPFLAG = -Xlinker -M -Xlinker FILENAME +endif + +# Use $(SONAMEFLAG:SONAME=soname) to specify the intrinsic name of a shared obj +SONAMEFLAG = -Xlinker -soname=SONAME + +# Build shared library +SHARED_FLAG = -shared + +#------------------------------------------------------------------------ +# Debug flags + +# Use the stabs format for debugging information (this is the default +# on gcc-2.91). It's good enough, has all the information about line +# numbers and local variables, and libjvm_g.so is only about 16M. +# Change this back to "-g" if you want the most expressive format. +# (warning: that could easily inflate libjvm_g.so to 150M!) +# Note: The Itanium gcc compiler crashes when using -gstabs. +DEBUG_CFLAGS/ia64 = -g +DEBUG_CFLAGS/amd64 = -g +DEBUG_CFLAGS += $(DEBUG_CFLAGS/$(BUILDARCH)) +ifeq ($(DEBUG_CFLAGS/$(BUILDARCH)),) +DEBUG_CFLAGS += -gstabs +endif + +MCS = /usr/ccs/bin/mcs diff --git a/hotspot/build/solaris/makefiles/hp.make b/hotspot/build/solaris/makefiles/hp.make new file mode 100644 index 00000000000..ddf659bfe19 --- /dev/null +++ b/hotspot/build/solaris/makefiles/hp.make @@ -0,0 +1,29 @@ +# +# Copyright 1998-1999 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making premium version of VM + +TYPE=HP + +CFLAGS += -DCOMPILER2 diff --git a/hotspot/build/solaris/makefiles/hp1.make b/hotspot/build/solaris/makefiles/hp1.make new file mode 100644 index 00000000000..7d62469a7f9 --- /dev/null +++ b/hotspot/build/solaris/makefiles/hp1.make @@ -0,0 +1,29 @@ +# +# Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making premium version of VM + +TYPE=HP1 + +CFLAGS += -DCOMPILER1 diff --git a/hotspot/build/solaris/makefiles/i486.make b/hotspot/build/solaris/makefiles/i486.make new file mode 100644 index 00000000000..10b5949bacd --- /dev/null +++ b/hotspot/build/solaris/makefiles/i486.make @@ -0,0 +1,67 @@ +# +# Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Must also specify if CPU is little endian +CFLAGS += -DVM_LITTLE_ENDIAN + +# TLS helper, assembled from .s file +# Not included in includeDB because it has no dependencies +Obj_Files += solaris_x86_32.o + +# +# Special case flags for compilers and compiler versions on i486. +# +ifeq ("${Platform_compiler}", "sparcWorks") + +# _lwp_create_interpose must have a frame +OPT_CFLAGS/os_solaris_i486.o = -xO1 +# force C++ interpreter to be full optimization +OPT_CFLAGS/interpret.o = -fast -O4 +else + +ifeq ("${Platform_compiler}", "gcc") +# gcc +# _lwp_create_interpose must have a frame +OPT_CFLAGS/os_solaris_i486.o = -fno-omit-frame-pointer +# force C++ interpreter to be full optimization +OPT_CFLAGS/interpret.o = -O3 +# +else +# error +_JUNK2_ := $(shell echo >&2 \ + "*** ERROR: this compiler is not yet supported by this code base!") + @exit 1 +endif +endif + +ifeq ("${Platform_compiler}", "sparcWorks") +# ILD is gone as of SS11 (5.8), not supported in SS10 (5.7) +ifeq ($(shell expr $(COMPILER_REV) \< 5.7), 1) + # + # Bug in ild causes it to fail randomly. Until we get a fix we can't + # use ild. + # + ILDFLAG/debug = -xildoff +endif +endif diff --git a/hotspot/build/solaris/makefiles/jsig.make b/hotspot/build/solaris/makefiles/jsig.make new file mode 100644 index 00000000000..29e4c238a9a --- /dev/null +++ b/hotspot/build/solaris/makefiles/jsig.make @@ -0,0 +1,54 @@ +# +# Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Rules to build signal interposition library, used by vm.make + +# libjsig[_g].so: signal interposition library +JSIG = jsig$(G_SUFFIX) +LIBJSIG = lib$(JSIG).so + +JSIGSRCDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/vm + +DEST_JSIG = $(JDK_LIBDIR)/$(LIBJSIG) + +LIBJSIG_MAPFILE = $(MAKEFILES_DIR)/mapfile-vers-jsig + +LFLAGS_JSIG += $(MAPFLAG:FILENAME=$(LIBJSIG_MAPFILE)) + +ifdef USE_GCC +LFLAGS_JSIG += -D_REENTRANT +else +LFLAGS_JSIG += -mt -xnolib +endif + +$(LIBJSIG): $(JSIGSRCDIR)/jsig.c $(LIBJSIG_MAPFILE) + @echo Making signal interposition lib... + $(QUIETLY) $(CC) $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ + $(LFLAGS_JSIG) -o $@ $< -ldl + +install_jsig: $(LIBJSIG) + @echo "Copying $(LIBJSIG) to $(DEST_JSIG)" + $(QUIETLY) cp -f $(LIBJSIG) $(DEST_JSIG) && echo "Done" + +.PHONY: install_jsig diff --git a/hotspot/build/solaris/makefiles/jvmg.make b/hotspot/build/solaris/makefiles/jvmg.make new file mode 100644 index 00000000000..ee0e4616a3c --- /dev/null +++ b/hotspot/build/solaris/makefiles/jvmg.make @@ -0,0 +1,56 @@ +# +# Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making debug version of VM + +# Compiler specific DEBUG_CFLAGS are passed in from gcc.make, sparcWorks.make +DEBUG_CFLAGS/DEFAULT= $(DEBUG_CFLAGS) +DEBUG_CFLAGS/BYFILE = $(DEBUG_CFLAGS/$@)$(DEBUG_CFLAGS/DEFAULT$(DEBUG_CFLAGS/$@)) + +ifeq ("${Platform_compiler}", "sparcWorks") +ifeq ($(shell expr $(COMPILER_REV) \>= 5.8), 1) + # SS11 SEGV when compiling with -g and -xarch=v8, using different backend + DEBUG_CFLAGS/compileBroker.o = $(DEBUG_CFLAGS) -xO0 + DEBUG_CFLAGS/jvmtiTagMap.o = $(DEBUG_CFLAGS) -xO0 +endif +endif + +CFLAGS += $(DEBUG_CFLAGS/BYFILE) + +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +# Linker mapfiles +MAPFILE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers \ + $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-debug \ + $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-nonproduct + +# This mapfile is only needed when compiling with dtrace support, +# and mustn't be otherwise. +MAPFILE_DTRACE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-$(TYPE) + +G_SUFFIX = +VERSION = debug +SYSDEFS += -DASSERT -DDEBUG +PICFLAGS = DEFAULT diff --git a/hotspot/build/solaris/makefiles/jvmti.make b/hotspot/build/solaris/makefiles/jvmti.make new file mode 100644 index 00000000000..ba93d87975c --- /dev/null +++ b/hotspot/build/solaris/makefiles/jvmti.make @@ -0,0 +1,117 @@ +# +# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This makefile (jvmti.make) is included from the jvmti.make in the +# build directories. +# +# It knows how to build and run the tools to generate jvmti. + +include $(GAMMADIR)/build/solaris/makefiles/rules.make + +# ######################################################################### + +GENERATED = ../generated +JvmtiOutDir = $(GENERATED)/jvmtifiles + +JvmtiSrcDir = $(GAMMADIR)/src/share/vm/prims +InterpreterSrcDir = $(GAMMADIR)/src/share/vm/interpreter +Src_Dirs += $(JvmtiSrcDir) + +# set VPATH so make knows where to look for source files +Src_Dirs_V = ${Src_Dirs} +VPATH += $(Src_Dirs_V:%=%:) + +JvmtiGeneratedNames = \ + jvmtiEnv.hpp \ + jvmtiEnter.cpp \ + jvmtiEnterTrace.cpp \ + jvmtiEnvRecommended.cpp\ + bytecodeInterpreterWithChecks.cpp\ + jvmti.h \ + +JvmtiEnvFillSource = $(JvmtiSrcDir)/jvmtiEnvFill.java +JvmtiEnvFillClass = $(JvmtiOutDir)/jvmtiEnvFill.class + +JvmtiGenSource = $(JvmtiSrcDir)/jvmtiGen.java +JvmtiGenClass = $(JvmtiOutDir)/jvmtiGen.class + +JvmtiGeneratedFiles = $(JvmtiGeneratedNames:%=$(JvmtiOutDir)/%) + +XSLT = $(QUIETLY) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiGen + +.PHONY: all jvmtidocs clean cleanall + +# ######################################################################### + +all: $(JvmtiGeneratedFiles) + +both = $(JvmtiGenClass) $(JvmtiSrcDir)/jvmti.xml $(JvmtiSrcDir)/jvmtiLib.xsl + +$(JvmtiGenClass): $(JvmtiGenSource) + $(QUIETLY) $(COMPILE.JAVAC) -g -d $(JvmtiOutDir) $(JvmtiGenSource) + +$(JvmtiEnvFillClass): $(JvmtiEnvFillSource) + $(QUIETLY) $(COMPILE.JAVAC) -g -d $(JvmtiOutDir) $(JvmtiEnvFillSource) + +$(JvmtiOutDir)/jvmtiEnter.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnter.xsl -OUT $(JvmtiOutDir)/jvmtiEnter.cpp -PARAM interface jvmti + +$(JvmtiOutDir)/bytecodeInterpreterWithChecks.cpp: $(JvmtiGenClass) $(InterpreterSrcDir)/bytecodeInterpreter.cpp $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xml $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xsl + @echo Generating $@ + $(XSLT) -IN $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xml -XSL $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xsl -OUT $(JvmtiOutDir)/bytecodeInterpreterWithChecks.cpp + +$(JvmtiOutDir)/jvmtiEnterTrace.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnter.xsl -OUT $(JvmtiOutDir)/jvmtiEnterTrace.cpp -PARAM interface jvmti -PARAM trace Trace + +$(JvmtiOutDir)/jvmtiEnvRecommended.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnv.xsl $(JvmtiSrcDir)/jvmtiEnv.cpp $(JvmtiEnvFillClass) + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnv.xsl -OUT $(JvmtiOutDir)/jvmtiEnvStub.cpp + $(QUIETLY) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiEnvFill $(JvmtiSrcDir)/jvmtiEnv.cpp $(JvmtiOutDir)/jvmtiEnvStub.cpp $(JvmtiOutDir)/jvmtiEnvRecommended.cpp + +$(JvmtiOutDir)/jvmtiEnv.hpp: $(both) $(JvmtiSrcDir)/jvmtiHpp.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiHpp.xsl -OUT $(JvmtiOutDir)/jvmtiEnv.hpp + +$(JvmtiOutDir)/jvmti.h: $(both) $(JvmtiSrcDir)/jvmtiH.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiH.xsl -OUT $(JvmtiOutDir)/jvmti.h + +jvmtidocs: $(JvmtiOutDir)/jvmti.html + +$(JvmtiOutDir)/jvmti.html: $(both) $(JvmtiSrcDir)/jvmti.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmti.xsl -OUT $(JvmtiOutDir)/jvmti.html + +# ######################################################################### + +clean : + rm $(JvmtiGenClass) $(JvmtiEnvFillClass) $(JvmtiGeneratedFiles) + +cleanall : + rm $(JvmtiGenClass) $(JvmtiEnvFillClass) $(JvmtiGeneratedFiles) + +# ######################################################################### + diff --git a/hotspot/build/solaris/makefiles/kernel.make b/hotspot/build/solaris/makefiles/kernel.make new file mode 100644 index 00000000000..f8ba99b0245 --- /dev/null +++ b/hotspot/build/solaris/makefiles/kernel.make @@ -0,0 +1,32 @@ +# +# Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# +# +# Sets make macros for making kernel version of VM. +# This target on solaris is just tempoarily for debugging the kernel build. + +TYPE=KERNEL + +VM_SUBDIR = client + +CFLAGS += -DKERNEL diff --git a/hotspot/build/solaris/makefiles/launcher.make b/hotspot/build/solaris/makefiles/launcher.make new file mode 100644 index 00000000000..67693bb90b3 --- /dev/null +++ b/hotspot/build/solaris/makefiles/launcher.make @@ -0,0 +1,92 @@ +# +# Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Rules to build gamma launcher, used by vm.make + +# gamma[_g]: launcher +LAUNCHER = gamma$(G_SUFFIX) + +LAUNCHERDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/launcher +LAUNCHERFLAGS = $(ARCHFLAG) \ + -I$(LAUNCHERDIR) -I$(GAMMADIR)/src/share/vm/prims \ + -DFULL_VERSION=\"$(HOTSPOT_RELEASE_VERSION)\" \ + -DARCH=\"$(LIBARCH)\" \ + -DGAMMA \ + -DLAUNCHER_TYPE=\"gamma\" \ + -DLINK_INTO_$(LINK_INTO) + +ifeq ($(LINK_INTO),AOUT) + LAUNCHER.o = launcher.o $(JVM_OBJ_FILES) + LAUNCHER_MAPFILE = mapfile_reorder + LFLAGS_LAUNCHER$(LDNOMAP) += $(MAPFLAG:FILENAME=$(LAUNCHER_MAPFILE)) + LIBS_LAUNCHER += $(LIBS) +else + LAUNCHER.o = launcher.o + LFLAGS_LAUNCHER += -L `pwd` + LIBS_LAUNCHER += -l$(JVM) $(LIBS) +endif + +LINK_LAUNCHER = $(LINK.CC) + +LINK_LAUNCHER/PRE_HOOK = $(LINK_LIB.CC/PRE_HOOK) +LINK_LAUNCHER/POST_HOOK = $(LINK_LIB.CC/POST_HOOK) + +ifeq ("${Platform_compiler}", "sparcWorks") +# Enable the following LAUNCHERFLAGS addition if you need to compare the +# built ELF objects. +# +# The -g option makes static data global and the "-W0,-noglobal" +# option tells the compiler to not globalize static data using a unique +# globalization prefix. Instead force the use of a static globalization +# prefix based on the source filepath so the objects from two identical +# compilations are the same. +# +# Note: The blog says to use "-W0,-xglobalstatic", but that doesn't +# seem to work. I got "-W0,-noglobal" from Kelly and that works. +#LAUNCHERFLAGS += -W0,-noglobal +endif # Platform_compiler == sparcWorks + +launcher.o: launcher.c $(LAUNCHERDIR)/java.c $(LAUNCHERDIR)/java_md.c + $(CC) -g -c -o $@ launcher.c $(LAUNCHERFLAGS) $(CPPFLAGS) + +launcher.c: + @echo Generating $@ + $(QUIETLY) { \ + echo '#define debug launcher_debug'; \ + echo '#include "java.c"'; \ + echo '#include "java_md.c"'; \ + } > $@ + +$(LAUNCHER): $(LAUNCHER.o) $(LIBJVM) $(LAUNCHER_MAPFILE) + $(QUIETLY) \ + case "$(CFLAGS_BROWSE)" in \ + -sbfast|-xsbfast) \ + ;; \ + *) \ + echo Linking launcher...; \ + $(LINK_LAUNCHER/PRE_HOOK) \ + $(LINK_LAUNCHER) $(LFLAGS_LAUNCHER) -o $@ $(LAUNCHER.o) $(LIBS_LAUNCHER); \ + $(LINK_LAUNCHER/POST_HOOK) \ + ;; \ + esac diff --git a/hotspot/build/solaris/makefiles/makedeps.make b/hotspot/build/solaris/makefiles/makedeps.make new file mode 100644 index 00000000000..71c8f455bcc --- /dev/null +++ b/hotspot/build/solaris/makefiles/makedeps.make @@ -0,0 +1,43 @@ +# +# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +include $(GAMMADIR)/build/solaris/makefiles/rules.make + +COMPILE.JAVAC.FLAGS += -d $(OUTDIR) + +MakeDepsSources=\ + $(GAMMADIR)/src/share/tools/MakeDeps/Database.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/DirectoryTree.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/DirectoryTreeNode.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/FileFormatException.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/FileList.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/FileName.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/Macro.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/MacroDefinitions.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/MakeDeps.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/MetroWerksMacPlatform.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/Platform.java \ + $(GAMMADIR)/src/share/tools/MakeDeps/UnixPlatform.java + +MakeDepsOptions= diff --git a/hotspot/build/solaris/makefiles/mapfile-vers b/hotspot/build/solaris/makefiles/mapfile-vers new file mode 100644 index 00000000000..7b1a1743e79 --- /dev/null +++ b/hotspot/build/solaris/makefiles/mapfile-vers @@ -0,0 +1,252 @@ +# + +# +# Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + + # JVM + JVM_Accept; + JVM_ActiveProcessorCount; + JVM_AllocateNewArray; + JVM_AllocateNewObject; + JVM_ArrayCopy; + JVM_AssertionStatusDirectives; + JVM_Available; + JVM_Bind; + JVM_ClassDepth; + JVM_ClassLoaderDepth; + JVM_Clone; + JVM_Close; + JVM_CX8Field; + JVM_CompileClass; + JVM_CompileClasses; + JVM_CompilerCommand; + JVM_Connect; + JVM_ConstantPoolGetClassAt; + JVM_ConstantPoolGetClassAtIfLoaded; + JVM_ConstantPoolGetDoubleAt; + JVM_ConstantPoolGetFieldAt; + JVM_ConstantPoolGetFieldAtIfLoaded; + JVM_ConstantPoolGetFloatAt; + JVM_ConstantPoolGetIntAt; + JVM_ConstantPoolGetLongAt; + JVM_ConstantPoolGetMethodAt; + JVM_ConstantPoolGetMethodAtIfLoaded; + JVM_ConstantPoolGetMemberRefInfoAt; + JVM_ConstantPoolGetSize; + JVM_ConstantPoolGetStringAt; + JVM_ConstantPoolGetUTF8At; + JVM_CountStackFrames; + JVM_CurrentClassLoader; + JVM_CurrentLoadedClass; + JVM_CurrentThread; + JVM_CurrentTimeMillis; + JVM_DefineClass; + JVM_DefineClassWithSource; + JVM_DesiredAssertionStatus; + JVM_DisableCompiler; + JVM_DoPrivileged; + JVM_DumpAllStacks; + JVM_DumpThreads; + JVM_EnableCompiler; + JVM_Exit; + JVM_FillInStackTrace; + JVM_FindClassFromClass; + JVM_FindClassFromClassLoader; + JVM_FindLibraryEntry; + JVM_FindLoadedClass; + JVM_FindPrimitiveClass; + JVM_FindSignal; + JVM_FreeMemory; + JVM_GC; + JVM_GetAllThreads; + JVM_GetArrayElement; + JVM_GetArrayLength; + JVM_GetCPClassNameUTF; + JVM_GetCPFieldClassNameUTF; + JVM_GetCPFieldModifiers; + JVM_GetCPFieldNameUTF; + JVM_GetCPFieldSignatureUTF; + JVM_GetCPMethodClassNameUTF; + JVM_GetCPMethodModifiers; + JVM_GetCPMethodNameUTF; + JVM_GetCPMethodSignatureUTF; + JVM_GetCallerClass; + JVM_GetClassAccessFlags; + JVM_GetClassAnnotations; + JVM_GetClassCPEntriesCount; + JVM_GetClassCPTypes; + JVM_GetClassConstantPool; + JVM_GetClassContext; + JVM_GetClassDeclaredConstructors; + JVM_GetClassDeclaredFields; + JVM_GetClassDeclaredMethods; + JVM_GetClassFieldsCount; + JVM_GetClassInterfaces; + JVM_GetClassLoader; + JVM_GetClassMethodsCount; + JVM_GetClassModifiers; + JVM_GetClassName; + JVM_GetClassNameUTF; + JVM_GetClassSignature; + JVM_GetClassSigners; + JVM_GetComponentType; + JVM_GetDeclaredClasses; + JVM_GetDeclaringClass; + JVM_GetEnclosingMethodInfo; + JVM_GetFieldAnnotations; + JVM_GetFieldIxModifiers; + JVM_GetHostName; + JVM_GetInheritedAccessControlContext; + JVM_GetInterfaceVersion; + JVM_GetLastErrorString; + JVM_GetManagement; + JVM_GetMethodAnnotations; + JVM_GetMethodDefaultAnnotationValue; + JVM_GetMethodIxArgsSize; + JVM_GetMethodIxByteCode; + JVM_GetMethodIxByteCodeLength; + JVM_GetMethodIxExceptionIndexes; + JVM_GetMethodIxExceptionTableEntry; + JVM_GetMethodIxExceptionTableLength; + JVM_GetMethodIxExceptionsCount; + JVM_GetMethodIxLocalsCount; + JVM_GetMethodIxMaxStack; + JVM_GetMethodIxModifiers; + JVM_GetMethodIxNameUTF; + JVM_GetMethodIxSignatureUTF; + JVM_GetMethodParameterAnnotations; + JVM_GetPrimitiveArrayElement; + JVM_GetProtectionDomain; + JVM_GetSockName; + JVM_GetSockOpt; + JVM_GetStackAccessControlContext; + JVM_GetStackTraceDepth; + JVM_GetStackTraceElement; + JVM_GetSystemPackage; + JVM_GetSystemPackages; + JVM_GetThreadStateNames; + JVM_GetThreadStateValues; + JVM_GetVersionInfo; + JVM_Halt; + JVM_HoldsLock; + JVM_IHashCode; + JVM_InitAgentProperties; + JVM_InitProperties; + JVM_InitializeCompiler; + JVM_InitializeSocketLibrary; + JVM_InternString; + JVM_Interrupt; + JVM_InvokeMethod; + JVM_IsArrayClass; + JVM_IsConstructorIx; + JVM_IsInterface; + JVM_IsInterrupted; + JVM_IsNaN; + JVM_IsPrimitiveClass; + JVM_IsSameClassPackage; + JVM_IsSilentCompiler; + JVM_IsSupportedJNIVersion; + JVM_IsThreadAlive; + JVM_LatestUserDefinedLoader; + JVM_Listen; + JVM_LoadClass0; + JVM_LoadLibrary; + JVM_Lseek; + JVM_MaxObjectInspectionAge; + JVM_MaxMemory; + JVM_MonitorNotify; + JVM_MonitorNotifyAll; + JVM_MonitorWait; + JVM_NativePath; + JVM_NanoTime; + JVM_NewArray; + JVM_NewInstanceFromConstructor; + JVM_NewMultiArray; + JVM_OnExit; + JVM_Open; + JVM_PrintStackTrace; + JVM_RaiseSignal; + JVM_RawMonitorCreate; + JVM_RawMonitorDestroy; + JVM_RawMonitorEnter; + JVM_RawMonitorExit; + JVM_Read; + JVM_Recv; + JVM_RecvFrom; + JVM_RegisterSignal; + JVM_ReleaseUTF; + JVM_ResolveClass; + JVM_ResumeThread; + JVM_Send; + JVM_SendTo; + JVM_SetArrayElement; + JVM_SetClassSigners; + JVM_SetLength; + JVM_SetPrimitiveArrayElement; + JVM_SetProtectionDomain; + JVM_SetSockOpt; + JVM_SetThreadPriority; + JVM_Sleep; + JVM_Socket; + JVM_SocketAvailable; + JVM_SocketClose; + JVM_SocketShutdown; + JVM_StartThread; + JVM_StopThread; + JVM_SuspendThread; + JVM_SupportsCX8; + JVM_Sync; + JVM_Timeout; + JVM_TotalMemory; + JVM_TraceInstructions; + JVM_TraceMethodCalls; + JVM_UnloadLibrary; + JVM_Write; + JVM_Yield; + JVM_handle_solaris_signal; + + # miscellaneous functions + jio_fprintf; + jio_printf; + jio_snprintf; + jio_vfprintf; + jio_vsnprintf; + + # Needed because there is no JVM interface for this. + sysThreadAvailableStackWithSlack; + + # This is for Forte Analyzer profiling support. + AsyncGetCallTrace; + local: + *; +}; diff --git a/hotspot/build/solaris/makefiles/mapfile-vers-COMPILER1 b/hotspot/build/solaris/makefiles/mapfile-vers-COMPILER1 new file mode 100644 index 00000000000..b4b420867be --- /dev/null +++ b/hotspot/build/solaris/makefiles/mapfile-vers-COMPILER1 @@ -0,0 +1,43 @@ +# + +# +# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # Dtrace support + __1cIUniverseP_methodKlassObj_; + __1cJCodeCacheF_heap_; + __1cIUniverseO_collectedHeap_; + __1cHnmethodG__vtbl_; + __1cICodeBlobG__vtbl_; + __1cKBufferBlobG__vtbl_; + __1cLRuntimeStubG__vtbl_; + __1cNSafepointBlobG__vtbl_; + __1cSDeoptimizationBlobG__vtbl_; + + __JvmOffsets; +}; diff --git a/hotspot/build/solaris/makefiles/mapfile-vers-COMPILER2 b/hotspot/build/solaris/makefiles/mapfile-vers-COMPILER2 new file mode 100644 index 00000000000..9e2e5356f55 --- /dev/null +++ b/hotspot/build/solaris/makefiles/mapfile-vers-COMPILER2 @@ -0,0 +1,46 @@ +# + +# +# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # Dtrace support + __1cIUniverseP_methodKlassObj_; + __1cJCodeCacheF_heap_; + __1cIUniverseO_collectedHeap_; + __1cHnmethodG__vtbl_; + __1cICodeBlobG__vtbl_; + __1cKBufferBlobG__vtbl_; + __1cLRuntimeStubG__vtbl_; + __1cNSafepointBlobG__vtbl_; + __1cSDeoptimizationBlobG__vtbl_; + + __1cNExceptionBlobG__vtbl_; + __1cQUncommonTrapBlobG__vtbl_; + + __JvmOffsets; +}; diff --git a/hotspot/build/solaris/makefiles/mapfile-vers-CORE b/hotspot/build/solaris/makefiles/mapfile-vers-CORE new file mode 100644 index 00000000000..25069d55c96 --- /dev/null +++ b/hotspot/build/solaris/makefiles/mapfile-vers-CORE @@ -0,0 +1,31 @@ +# + +# +# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: +}; diff --git a/hotspot/build/solaris/makefiles/mapfile-vers-TIERED b/hotspot/build/solaris/makefiles/mapfile-vers-TIERED new file mode 100644 index 00000000000..5149a01b598 --- /dev/null +++ b/hotspot/build/solaris/makefiles/mapfile-vers-TIERED @@ -0,0 +1,45 @@ +# + +# +# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # Dtrace support + __1cIUniverseP_methodKlassObj_; + __1cJCodeCacheF_heap_; + __1cIUniverseO_collectedHeap_; + __1cHnmethodG__vtbl_; + __1cICodeBlobG__vtbl_; + __1cKBufferBlobG__vtbl_; + __1cLRuntimeStubG__vtbl_; + __1cNSafepointBlobG__vtbl_; + __1cSDeoptimizationBlobG__vtbl_; + __1cNExceptionBlobG__vtbl_; + __1cQUncommonTrapBlobG__vtbl_; + + __JvmOffsets; +}; diff --git a/hotspot/build/solaris/makefiles/mapfile-vers-debug b/hotspot/build/solaris/makefiles/mapfile-vers-debug new file mode 100644 index 00000000000..98ce4887aa0 --- /dev/null +++ b/hotspot/build/solaris/makefiles/mapfile-vers-debug @@ -0,0 +1,37 @@ +# + +# +# Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # debug JVM + JVM_AccessVMBooleanFlag; + JVM_AccessVMIntFlag; + JVM_VMBreakPoint; + + # miscellaneous +}; diff --git a/hotspot/build/solaris/makefiles/mapfile-vers-jsig b/hotspot/build/solaris/makefiles/mapfile-vers-jsig new file mode 100644 index 00000000000..f7ae7fb22b9 --- /dev/null +++ b/hotspot/build/solaris/makefiles/mapfile-vers-jsig @@ -0,0 +1,40 @@ +# + +# +# Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define library interface. + +SUNWprivate_1.1 { + global: + JVM_begin_signal_setting; + JVM_end_signal_setting; + JVM_get_libjsig_version; + JVM_get_signal_action; + sigaction; + signal; + sigset; + local: + *; +}; diff --git a/hotspot/build/solaris/makefiles/mapfile-vers-jvm_db b/hotspot/build/solaris/makefiles/mapfile-vers-jvm_db new file mode 100644 index 00000000000..11ac2157b93 --- /dev/null +++ b/hotspot/build/solaris/makefiles/mapfile-vers-jvm_db @@ -0,0 +1,38 @@ +# + +# +# Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define library interface. + +SUNWprivate_1.1 { + global: + Jagent_create; + Jagent_destroy; + Jframe_iter; + #Jget_vframe; + #Jlookup_by_regs; + local: + *; +}; diff --git a/hotspot/build/solaris/makefiles/mapfile-vers-jvm_dtrace b/hotspot/build/solaris/makefiles/mapfile-vers-jvm_dtrace new file mode 100644 index 00000000000..e10f098330a --- /dev/null +++ b/hotspot/build/solaris/makefiles/mapfile-vers-jvm_dtrace @@ -0,0 +1,37 @@ +# + +# +# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define library interface for JVM-DTrace interface + +SUNWprivate_1.1 { + global: + jvm_attach; + jvm_get_last_error; + jvm_enable_dtprobes; + jvm_detach; + local: + *; +}; diff --git a/hotspot/build/solaris/makefiles/mapfile-vers-nonproduct b/hotspot/build/solaris/makefiles/mapfile-vers-nonproduct new file mode 100644 index 00000000000..b1544b8b6e1 --- /dev/null +++ b/hotspot/build/solaris/makefiles/mapfile-vers-nonproduct @@ -0,0 +1,48 @@ +# + +# +# Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # Old reflection routines + # These do not need to be present in the product build in JDK 1.4 + # but their code has not been removed yet because there will not + # be a substantial code savings until JVM_InvokeMethod and + # JVM_NewInstanceFromConstructor can also be removed; see + # reflectionCompat.hpp. + JVM_GetClassConstructor; + JVM_GetClassConstructors; + JVM_GetClassField; + JVM_GetClassFields; + JVM_GetClassMethod; + JVM_GetClassMethods; + JVM_GetField; + JVM_GetPrimitiveField; + JVM_NewInstance; + JVM_SetField; + JVM_SetPrimitiveField; +}; diff --git a/hotspot/build/solaris/makefiles/optimized.make b/hotspot/build/solaris/makefiles/optimized.make new file mode 100644 index 00000000000..31323ed12a6 --- /dev/null +++ b/hotspot/build/solaris/makefiles/optimized.make @@ -0,0 +1,57 @@ +# +# Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making optimized version of Gamma VM +# (This is the "product", not the "release" version.) + +# Compiler specific OPT_CFLAGS are passed in from gcc.make, sparcWorks.make +OPT_CFLAGS/DEFAULT= $(OPT_CFLAGS) +OPT_CFLAGS/BYFILE = $(OPT_CFLAGS/$@)$(OPT_CFLAGS/DEFAULT$(OPT_CFLAGS/$@)) + +# (OPT_CFLAGS/SLOWER is also available, to alter compilation of buggy files) + +# Workaround SS11 bug 6345274 (all platforms) +ifeq ("${Platform_compiler}", "sparcWorks") +ifeq ($(shell expr $(COMPILER_REV) \>= 5.8), 1) +OPT_CFLAGS/ciTypeFlow.o = $(OPT_CFLAGS/O2) +endif # COMPILER_REV >= 5.8 +endif # Platform_compiler == sparcWorks + +# If you set HOTSPARC_GENERIC=yes, you disable all OPT_CFLAGS settings +CFLAGS$(HOTSPARC_GENERIC) += $(OPT_CFLAGS/BYFILE) + +# Linker mapfiles +# NOTE: inclusion of nonproduct mapfile not necessary; read it for details +MAPFILE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers \ + $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-nonproduct + +# This mapfile is only needed when compiling with dtrace support, +# and mustn't be otherwise. +MAPFILE_DTRACE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-$(TYPE) + +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +G_SUFFIX = +VERSION = optimized diff --git a/hotspot/build/solaris/makefiles/product.make b/hotspot/build/solaris/makefiles/product.make new file mode 100644 index 00000000000..2bc592702d2 --- /dev/null +++ b/hotspot/build/solaris/makefiles/product.make @@ -0,0 +1,75 @@ +# +# Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making optimized version of Gamma VM +# (This is the "product", not the "release" version.) + +# Compiler specific OPT_CFLAGS are passed in from gcc.make, sparcWorks.make +OPT_CFLAGS/DEFAULT= $(OPT_CFLAGS) +OPT_CFLAGS/BYFILE = $(OPT_CFLAGS/$@)$(OPT_CFLAGS/DEFAULT$(OPT_CFLAGS/$@)) + +# Workaround for a bug in dtrace. If ciEnv::post_compiled_method_load_event() +# is inlined, the resulting dtrace object file needs a reference to this +# function, whose symbol name is too long for dtrace. So disable inlining +# for this method for now. (fix this when dtrace bug 6258412 is fixed) +ifndef USE_GCC +OPT_CFLAGS/ciEnv.o = $(OPT_CFLAGS) -xinline=no%__1cFciEnvbFpost_compiled_method_load_event6MpnHnmethod__v_ +endif + +# (OPT_CFLAGS/SLOWER is also available, to alter compilation of buggy files) + +# Workaround SS11 bug 6345274 (all platforms) +ifeq ("${Platform_compiler}", "sparcWorks") +ifeq ($(shell expr $(COMPILER_REV) \>= 5.8), 1) +OPT_CFLAGS/ciTypeFlow.o = $(OPT_CFLAGS/O2) +endif # COMPILER_REV >= 5.8 +endif # Platform_compiler == sparcWorks + +# If you set HOTSPARC_GENERIC=yes, you disable all OPT_CFLAGS settings +CFLAGS$(HOTSPARC_GENERIC) += $(OPT_CFLAGS/BYFILE) +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +# Linker mapfiles +# NOTE: inclusion of nonproduct mapfile not necessary; read it for details +ifdef USE_GCC +MAPFILE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers +else +MAPFILE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers \ + $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-nonproduct + +# This mapfile is only needed when compiling with dtrace support, +# and mustn't be otherwise. +MAPFILE_DTRACE = $(GAMMADIR)/build/solaris/makefiles/mapfile-vers-$(TYPE) + +REORDERFILE = $(GAMMADIR)/build/solaris/makefiles/reorder_$(TYPE)_$(BUILDARCH) +endif + +# Don't strip in VM build; JDK build will strip libraries later +# LINK_LIB.CC/POST_HOOK += $(STRIP_LIB.CC/POST_HOOK) + +G_SUFFIX = +SYSDEFS += -DPRODUCT +SYSDEFS += $(REORDER_FLAG) +VERSION = optimized diff --git a/hotspot/build/solaris/makefiles/profiled.make b/hotspot/build/solaris/makefiles/profiled.make new file mode 100644 index 00000000000..297e6285183 --- /dev/null +++ b/hotspot/build/solaris/makefiles/profiled.make @@ -0,0 +1,46 @@ +# +# Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making profiled version of Gamma VM +# (It is also optimized.) + +CFLAGS += -pg + +# On x86 Solaris 2.6, 7, and 8 if LD_LIBRARY_PATH has /usr/lib in it then +# adlc linked with -pg puts out empty header files. To avoid linking adlc +# with -pg the profile flag is split out separately and used in rules.make + +PROF_AOUT_FLAGS += -pg + +SYSDEFS += $(REORDER_FLAG) + +# To do a profiled build of the product, such as for generating the +# reordering file, set PROFILE_PRODUCT. Otherwise the reordering file will +# contain references to functions which are not defined in the PRODUCT build. + +ifdef PROFILE_PRODUCT + SYSDEFS += -DPRODUCT +endif + +LDNOMAP = true diff --git a/hotspot/build/solaris/makefiles/reorder_COMPILER1_i486 b/hotspot/build/solaris/makefiles/reorder_COMPILER1_i486 new file mode 100644 index 00000000000..cee930fd1ec --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_COMPILER1_i486 @@ -0,0 +1,5450 @@ +data = R0x2000; +text = LOAD ?RXO; + + +# Test Null +text: .text%__cplus_fini_at_exit: CCrti.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: arguments.o; +text: .text%__1cQAgentLibraryList2t6M_v_: arguments.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_AllocTable.o; +text: .text%__1cFRInfo2t6M_v_: c1_AllocTable.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_AllocTable_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_AllocTable_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_CacheLocals.o; +text: .text%__1cFRInfo2t6M_v_: c1_CacheLocals.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_CacheLocals_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_CacheLocals_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Canonicalizer.o; +text: .text%__1cFRInfo2t6M_v_: c1_Canonicalizer.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_CodeGenerator.o; +text: .text%__1cFRInfo2t6M_v_: c1_CodeGenerator.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_CodeGenerator_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_CodeGenerator_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_CodeStubs_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_CodeStubs_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Compilation.o; +text: .text%__1cFRInfo2t6M_v_: c1_Compilation.o; +text: .text%__1cMelapsedTimer2t6M_v_: c1_Compilation.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Compiler.o; +text: .text%__1cFRInfo2t6M_v_: c1_Compiler.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_FrameMap.o; +text: .text%__1cFRInfo2t6M_v_: c1_FrameMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_FrameMap_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_FrameMap_i486.o; +text: .text%__1cKc1_RegMask2t6M_v_: c1_FrameMap_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_GraphBuilder.o; +text: .text%__1cFRInfo2t6M_v_: c1_GraphBuilder.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_IR.o; +text: .text%__1cFRInfo2t6M_v_: c1_IR.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Instruction.o; +text: .text%__1cFRInfo2t6M_v_: c1_Instruction.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_InstructionPrinter.o; +text: .text%__1cFRInfo2t6M_v_: c1_InstructionPrinter.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Items.o; +text: .text%__1cFRInfo2t6M_v_: c1_Items.o; +text: .text%__1cIHintItem2t6MpnJValueType_i_v_: c1_Items.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Items_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_Items_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIR.o; +text: .text%__1cFRInfo2t6M_v_: c1_LIR.o; +text: .text%__1cLLIR_OprFactHillegal6F_pnLLIR_OprDesc__: c1_LIR.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIRAssembler.o; +text: .text%__1cFRInfo2t6M_v_: c1_LIRAssembler.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIRAssembler_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_LIRAssembler_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIREmitter.o; +text: .text%__1cFRInfo2t6M_v_: c1_LIREmitter.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIREmitter_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_LIREmitter_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIROptimizer.o; +text: .text%__1cFRInfo2t6M_v_: c1_LIROptimizer.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Loops.o; +text: .text%__1cFRInfo2t6M_v_: c1_Loops.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_MacroAssembler_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_MacroAssembler_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Optimizer.o; +text: .text%__1cFRInfo2t6M_v_: c1_Optimizer.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_RInfo.o; +text: .text%__1cFRInfo2t6M_v_: c1_RInfo.o; +text: .text%__1cKc1_RegMask2t6M_v_: c1_RInfo.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_RInfo_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_RInfo_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_RegAlloc.o; +text: .text%__1cFRInfo2t6M_v_: c1_RegAlloc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_RegAlloc_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_RegAlloc_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Runtime1.o; +text: .text%__1cFRInfo2t6M_v_: c1_Runtime1.o; +text: .text%__1cIiEntries2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Runtime1_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_Runtime1_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_ScanBlocks.o; +text: .text%__1cFRInfo2t6M_v_: c1_ScanBlocks.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_ValueMap.o; +text: .text%__1cFRInfo2t6M_v_: c1_ValueMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_ValueSet.o; +text: .text%__1cFRInfo2t6M_v_: c1_ValueSet.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_ValueStack.o; +text: .text%__1cFRInfo2t6M_v_: c1_ValueStack.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeBlob.o; +text: .text%__1cFRInfo2t6M_v_: codeBlob.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeCache.o; +text: .text%__1cICHeapObj2n6FI_pv_; +text: .text%__1cCosGmalloc6FI_pv_; +text: .text%__1cICodeHeap2t6M_v_; +text: .text%__1cMVirtualSpace2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compilationPolicy.o; +text: .text%__1cMelapsedTimer2t6M_v_: compilationPolicy.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compileBroker.o; +text: .text%__1cMelapsedTimer2t6M_v_: compileBroker.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compiledIC.o; +text: .text%__1cFRInfo2t6M_v_: compiledIC.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: deoptimization.o; +text: .text%__1cFRInfo2t6M_v_: deoptimization.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: fprofiler.o; +text: .text%__1cMelapsedTimer2t6M_v_: fprofiler.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: frame.o; +text: .text%__1cFRInfo2t6M_v_: frame.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: frame_i486.o; +text: .text%__1cFRInfo2t6M_v_: frame_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: genCollectedHeap.o; +text: .text%__1cTAssertIsPermClosure2t6M_v_: genCollectedHeap.o; +text: .text%__1cRAlwaysTrueClosure2t6M_v_: genCollectedHeap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: generateOopMap.o; +text: .text%__1cNCellTypeStateLmake_bottom6F_0_: generateOopMap.o; +text: .text%__1cNCellTypeStateImake_any6Fi_0_: generateOopMap.o; +text: .text%__1cNCellTypeStateImake_top6F_0_: generateOopMap.o; +text: .text%__1cMelapsedTimer2t6M_v_: generateOopMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter.o; +text: .text%__1cKEntryPoint2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter_i486.o; +text: .text%__1cFRInfo2t6M_v_: interpreter_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: java.o; +text: .text%__1cFRInfo2t6M_v_: java.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEnvBase.o; +text: .text%__1cWNonPrintingResourceObj2n6FInLResourceObjPallocation_type__pv_: jvmtiEnvBase.o; +text: .text%__1cLResourceObj2n6FIn0APallocation_type__pv_; +text: .text%__1cNGrowableArray4CpnMJvmtiEnvBase__2t6Mii_v_: jvmtiEnvBase.o; +text: .text%__1cUGenericGrowableArray2t6Mii_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEventController.o; +text: .text%__1cRJvmtiEventEnabled2t6M_v_; +text: .text%__1cRJvmtiEventEnabledFclear6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiImpl.o; +text: .text%__1cWNonPrintingResourceObj2n6FInLResourceObjPallocation_type__pv_: jvmtiImpl.o; +text: .text%__1cNGrowableArray4CpnPJvmtiRawMonitor__2t6Mii_v_: jvmtiImpl.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiTagMap.o; +text: .text%__1cJMemRegion2t6M_v_: jvmtiTagMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: klassVtable.o; +text: .text%__1cFRInfo2t6M_v_: klassVtable.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: management.o; +text: .text%__1cJTimeStamp2t6M_v_: management.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: markSweep.o; +text: .text%__1cJMarkSweepSMarkAndPushClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepRFollowRootClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepSFollowStackClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepUAdjustPointerClosure2t6Mi_v_: markSweep.o; +text: .text%__1cJMarkSweepOIsAliveClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepQKeepAliveClosure2t6M_v_: markSweep.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: memoryService.o; +text: .text%__1cWNonPrintingResourceObj2n6FInLResourceObjPallocation_type__pv_: memoryService.o; +text: .text%__1cNGrowableArray4CpnKMemoryPool__2t6Mii_v_: memoryService.o; +text: .text%__1cNGrowableArray4CpnNMemoryManager__2t6Mii_v_: memoryService.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: methodOop.o; +text: .text%__1cFRInfo2t6M_v_: methodOop.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: nativeInst_i486.o; +text: .text%__1cFRInfo2t6M_v_: nativeInst_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: nmethod.o; +text: .text%__1cFRInfo2t6M_v_: nmethod.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: oopMap.o; +text: .text%__1cQDoNothingClosure2t6M_v_: oopMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: os_solaris.o; +text: .text%__1cFRInfo2t6M_v_: os_solaris.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: os_solaris_i486.o; +text: .text%__1cFRInfo2t6M_v_: os_solaris_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: parGCAllocBuffer.o; +text: .text%__1cMarrayOopDescLheader_size6FnJBasicType__i_: parGCAllocBuffer.o; +text: .text%__1cRalign_object_size6Fi_i_: parGCAllocBuffer.o; +text: .text%__1cHoopDescLheader_size6F_i_: parGCAllocBuffer.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psAdaptiveSizePolicy.o; +text: .text%__1cMelapsedTimer2t6M_v_: psAdaptiveSizePolicy.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psMarkSweep.o; +text: .text%__1cMelapsedTimer2t6M_v_: psMarkSweep.o; +text: .text%__1cTPSAlwaysTrueClosure2t6M_v_: psMarkSweep.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psPromotionLAB.o; +text: .text%__1cMarrayOopDescLheader_size6FnJBasicType__i_: psPromotionLAB.o; +text: .text%__1cRalign_object_size6Fi_i_: psPromotionLAB.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psScavenge.o; +text: .text%__1cMelapsedTimer2t6M_v_: psScavenge.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: relocInfo.o; +text: .text%__1cQRelocationHolder2t6M_v_: relocInfo.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: runtimeService.o; +text: .text%__1cJTimeStamp2t6M_v_: runtimeService.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: safepoint.o; +text: .text%__1cFRInfo2t6M_v_: safepoint.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: safepoint_solaris_i486.o; +text: .text%__1cFRInfo2t6M_v_: safepoint_solaris_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedHeap.o; +text: .text%__1cTAssertIsPermClosure2t6M_v_: sharedHeap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedRuntime.o; +text: .text%__1cFRInfo2t6M_v_: sharedRuntime.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: tenuredGeneration.o; +text: .text%__1cRCardTableModRefBSbCpar_chunk_heapword_alignment6F_I_: tenuredGeneration.o; +text: .text%__1cEMIN24CI_6FTA0_0_: tenuredGeneration.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vframeArray.o; +text: .text%__1cFRInfo2t6M_v_: vframeArray.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vmStructs.o; +text: .text%__1cFRInfo2t6M_v_: vmStructs.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vm_version.o; +text: .text%__1cTAbstract_VM_VersionKvm_release6F_pkc_; +text: .text%__1cTAbstract_VM_VersionXinternal_vm_info_string6F_pkc_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vtableStubs_i486.o; +text: .text%__1cFRInfo2t6M_v_: vtableStubs_i486.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIROptimizer_i486.o; +text: .text%__1cFRInfo2t6M_v_: c1_LIROptimizer_i486.o; +text: .text%JNI_CreateJavaVM; +text: .text%__1cCosVatomic_xchg_bootstrap6Fipoi_i_; +text: .text%__1cHThreadsJcreate_vm6FpnOJavaVMInitArgs_pi_i_; +text: .text%__1cHThreadsYis_supported_jni_version6Fi_C_; +text: .text%__1cMostream_init6F_v_; +text: .text%__1cMoutputStream2t6Mi_v_; +text: .text%__1cCosEinit6F_v_; +text: .text%__1cCosLinit_random6Fl_v_; +text: .text%__1cCosHSolarisWinitialize_system_info6F_v_; +text: .text%__1cOThreadCriticalKinitialize6F_v_; +text: .text%__1cCosMvm_page_size6F_i_; +text: .text%__1cJArgumentsWinit_system_properties6F_v_; +text: .text%__1cJArgumentsQPropertyList_add6FppnOSystemProperty_2_v_; +text: .text%__1cTAbstract_VM_VersionHvm_name6F_pkc_; +text: .text%__1cTAbstract_VM_VersionJvm_vendor6F_pkc_; +text: .text%__1cTAbstract_VM_VersionOvm_info_string6F_pkc_; +text: .text%__1cCosbDinit_system_properties_values6F_v_; +text: .text%__1cCosIjvm_path6Fpci_v_; +text: .text%__1cCosNset_boot_path6Fcc_i_; +text: .text%__1cJArgumentsFparse6FpknOJavaVMInitArgs__i_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_pkcp4_i_: arguments.o; +text: .text%__1cJArgumentsVprocess_settings_file6Fpkcii_i_; +text: .text%__1cJArgumentsSparse_vm_init_args6FpknOJavaVMInitArgs__i_; +text: .text%__1cMSysClassPath2t6Mpkc_v_; +text: .text%__1cJArgumentsbSparse_java_tool_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cCosGgetenv6Fpkcpci_i_; +text: .text%__1cJArgumentsWparse_each_vm_init_arg6FpknOJavaVMInitArgs_pnMSysClassPath_pi_i_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_ppkc5i_i_: arguments.o; +text: .text%__1cJArgumentsMadd_property6Fpkc_i_; +text: .text%__1cJArgumentsXPropertyList_unique_add6FppnOSystemProperty_pkcpc_v_; +text: .text%__1cCosEfree6Fpv_v_; +text: .text%__1cJArgumentsbNparse_java_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsVfinalize_vm_init_args6FpnMSysClassPath_i_i_; +text: .text%__1cMSysClassPathPexpand_endorsed6M_v_; +text: .text%__1cJArgumentsMget_property6Fpkc_2_; +text: .text%__1cJArgumentsWPropertyList_get_value6FpnOSystemProperty_pkc_4_; +text: .text%__1cMSysClassPathQadd_jars_to_path6Fpcpkc_1_; +text: .text%__1cJArgumentsZcheck_vm_args_consistency6F_i_; +text: .text%__1cJArgumentsRverify_percentage6FIpkc_i_; +text: .text%__1cMSysClassPath2T6M_v_; +text: .text%__1cMSysClassPathNreset_item_at6Mi_v_: arguments.o; +text: .text%__1cJArgumentsbOparse_java_compiler_environment_variable6F_v_; +text: .text%__1cJArgumentsVset_parallel_gc_flags6F_v_; +text: .text%__1cJArgumentsbBset_cms_and_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsTset_parnew_gc_flags6F_v_; +text: .text%__1cSCommandLineFlagsExKis_default6FnPCommandLineFlag__i_; +text: .text%__1cJArgumentsUset_ergonomics_flags6F_v_; +text: .text%__1cCosXis_server_class_machine6F_i_; +text: .text%__1cJTimeStampJupdate_to6Mx_v_; +text: .text%__1cCosOjavaTimeMillis6F_x_; +text: .text%__1cJTraceTime2t6MpkciipnMoutputStream__v_; +text: .text%__1cCosGinit_26F_i_; +text: .text%__1cCosHSolarisKmmap_chunk6FpcIii_2_; +text: .text%__1cCosXnon_memory_address_word6F_pc_; +text: .text%__1cCosHSolarisRmpss_sanity_check6F_v_; +text: .text%__1cCosHSolarisOset_mpss_range6FpcII_i_; +text: .text%__1cCosPuncommit_memory6FpcI_i_; +text: .text%__1cCosHSolarisOlibthread_init6F_v_; +text: .text%__1cOisT2_libthread6F_i_; +text: .text%__1cRlwp_priocntl_init6F_i_: os_solaris.o; +text: .text%__1cNpriocntl_stub6FinGidtype_lipc_l_: os_solaris.o; +text: .text%__1cOresolve_symbol6Fpkc_pC_: os_solaris.o; +text: .text%__1cCosHSolarisQsignal_sets_init6F_v_; +text: .text%__1cCosHSolarisOis_sig_ignored6Fi_i_; +text: .text%__1cCosHSolarisPinit_signal_mem6F_v_; +text: .text%__1cCosHSolarisXinstall_signal_handlers6F_v_; +text: .text%__1cCosHSolarisSset_signal_handler6Fiii_v_; +text: .text%__1cCosHSolarisUsynchronization_init6F_v_; +text: .text%__1cDhpiKinitialize6F_i_; +text: .text%__1cDhpiYinitialize_get_interface6FpnIvm_calls__v_; +text: .text%__1cQostream_init_log6F_v_; +text: .text%__1cNdefaultStreamMhas_log_file6M_i_; +text: .text%__1cRis_error_reported6F_i_; +text: .text%__1cNdefaultStreamEinit6M_v_; +text: .text%__1cSThreadLocalStorageEinit6F_v_; +text: .text%__1cSThreadLocalStorageHpd_init6F_v_; +text: .text%__1cCosbDallocate_thread_local_storage6F_i_; +text: .text%__1cSThreadLocalStoragebCgenerate_code_for_get_thread6F_v_; +text: .text%__1cRAllocateTLSOffset6F_v_: threadLS_solaris_i486.o; +text: .text%__1cPvm_init_globals6F_v_; +text: .text%__1cScheck_ThreadShadow6F_v_; +text: .text%__1cRcheck_basic_types6F_v_; +text: .text%__1cNeventlog_init6F_v_; +text: .text%__1cKmutex_init6F_v_; +text: .text%__1cFMutex2t6Mipkci_v_; +text: .text%lwp_cond_init: os_solaris.o; +text: .text%lwp_mutex_init: os_solaris.o; +text: .text%__1cHMonitor2t6Mipkci_v_; +text: .text%__1cOchunkpool_init6F_v_; +text: .text%__1cPperfMemory_init6F_v_; +text: .text%__1cKPerfMemoryKinitialize6F_v_; +text: .text%__1cCosZvm_allocation_granularity6F_i_; +text: .text%__1cKPerfMemoryUcreate_memory_region6FI_v_; +text: .text%__1cUcreate_shared_memory6FI_pc_: perfMemory_solaris.o; +text: .text%__1cSmmap_create_shared6FI_pc_: perfMemory_solaris.o; +text: .text%__1cCosScurrent_process_id6F_i_; +text: .text%__1cNget_user_name6Fl_pc_: perfMemory_solaris.o; +text: .text%__1cQget_user_tmp_dir6Fpkc_pc_: perfMemory_solaris.o; +text: .text%__1cCosSget_temp_directory6F_pkc_; +text: .text%__1cWget_sharedmem_filename6Fpkci_pc_: perfMemory_solaris.o; +text: .text%__1cbBcleanup_sharedmem_resources6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cTis_directory_secure6Fpkc_i_: perfMemory_solaris.o; +text: .text%lstat: perfMemory_solaris.o; +text: .text%__1cPfilename_to_pid6Fpkc_l_: perfMemory_solaris.o; +text: .text%__1cbAcreate_sharedmem_resources6Fpkc1I_i_: perfMemory_solaris.o; +text: .text%__1cRmake_user_tmp_dir6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cKJavaThread2t6M_v_; +text: .text%__1cGThread2t6M_v_; +text: .text%__1cFArena2t6M_v_; +text: .text%__1cFChunk2n6FII_pv_; +text: .text%__1cOThreadCritical2t6M_v_; +text: .text%__1cOThreadCritical2T6M_v_; +text: .text%__1cFChunk2t6MI_v_; +text: .text%__1cKHandleMarkKinitialize6MpnGThread__v_; +text: .text%__1cKJavaThreadKinitialize6M_v_; +text: .text%__1cNjni_functions6F_pknTJNINativeInterface___; +text: .text%__1cQThreadStatistics2t6M_v_; +text: .text%__1cGParker2t6M_v_; +text: .text%__1cWThreadLocalAllocBufferKinitialize6M_v_; +text: .text%__1cWThreadLocalAllocBufferKinitialize6MpnIHeapWord_22_v_; +text: .text%__1cWThreadLocalAllocBufferMinitial_size6F_I_; +text: .text%__1cWThreadLocalAllocBufferVinitialize_statistics6M_v_; +text: .text%__1cMFlatProfilerJis_active6F_i_; +text: .text%__1cUThreadSafepointStateGcreate6FpnKJavaThread__v_; +text: .text%__1cUThreadSafepointState2t6MpnKJavaThread__v_; +text: .text%__1cGThreadbArecord_stack_base_and_size6M_v_; +text: .text%__1cCosScurrent_stack_base6F_pC_; +text: .text%__1cCosScurrent_stack_size6F_I_; +text: .text%__1cGThreadbFinitialize_thread_local_storage6M_v_; +text: .text%__1cSThreadLocalStorageKset_thread6FpnGThread__v_; +text: .text%__1cSThreadLocalStorageNpd_set_thread6FpnGThread__v_; +text: .text%__1cCosbBthread_local_storage_at_put6Fipv_v_; +text: .text%__1cSThreadLocalStorageSset_thread_in_slot6FpnGThread__v_; +text: .text%get_thread; +text: .text%__1cSThreadLocalStoragebBget_thread_via_cache_slowly6FIi_pnGThread__; +text: .text%__1cSThreadLocalStoragePget_thread_slow6F_pnGThread__; +text: .text%__1cCosXthread_local_storage_at6Fi_pv_; +text: .text%__1cCosVcurrent_stack_pointer6F_pC_; +text: .text%__1cCosRinitialize_thread6F_v_; +text: .text%__1cNReservedSpaceUpage_align_size_down6FI_I_; +text: .text%__1cCosHSolarisVinit_thread_fpu_state6F_v_; +text: .text%__1cOJNIHandleBlockOallocate_block6FpnGThread__p0_; +text: .text%__1cFMutexbClock_without_safepoint_check6M_v_; +text: .text%__1cFMutexGunlock6M_v_; +text: .text%__1cGThreadWset_as_starting_thread6M_i_; +text: .text%__1cCosScreate_main_thread6FpnGThread__i_; +text: .text%__1cQcreate_os_thread6FpnGThread_I_pnIOSThread__: os_solaris.o; +text: .text%__1cIOSThread2t6MpFpv_i1_v_; +text: .text%__1cIOSThreadNpd_initialize6M_v_; +text: .text%__1cCosHSolarisPhotspot_sigmask6FpnGThread__v_; +text: .text%__1cCosHSolarisRunblocked_signals6F_pnIsigset_t__; +text: .text%__1cGThreadMis_VM_thread6kM_i_: thread.o; +text: .text%__1cCosHSolarisKvm_signals6F_pnIsigset_t__; +text: .text%__1cKJavaThreadYcreate_stack_guard_pages6M_v_; +text: .text%__1cCosNcommit_memory6FpcI_i_; +text: .text%__1cCosMguard_memory6FpcI_i_; +text: .text%__1cMinit_globals6F_i_; +text: .text%__1cPmanagement_init6F_v_; +text: .text%__1cKManagementEinit6F_v_; +text: .text%__1cNExceptionMark2t6MrpnGThread__v_; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongVariable__; +text: .text%__1cIPerfLong2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability__v_; +text: .text%__1cIPerfData2t6MnJCounterNS_pkcn0AFUnits_n0ALVariability__v_; +text: .text%__1cIPerfDataMcreate_entry6MnJBasicType_II_v_; +text: .text%__1cKPerfMemoryFalloc6FI_pc_; +text: .text%__1cFMutexElock6M_v_; +text: .text%__1cFMutexElock6MpnGThread__v_; +text: .text%__1cKPerfMemoryMmark_updated6F_v_; +text: .text%__1cCosLelapsedTime6F_d_; +text: .text%__1cMgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cPoldgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cPPerfDataManagerIadd_item6FpnIPerfData_i_v_; +text: .text%__1cMPerfDataList2t6Mi_v_; +text: .text%__1cCosbCis_thread_cpu_time_supported6F_i_; +text: .text%__1cNExceptionMark2T6M_v_; +text: .text%__1cNThreadServiceEinit6F_v_; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnPPerfLongCounter__; +text: .text%__1cORuntimeServiceEinit6F_v_; +text: .text%__1cTClassLoadingServiceEinit6F_v_; +text: .text%__1cKvtune_init6F_v_; +text: .text%__1cObytecodes_init6F_v_; +text: .text%__1cJBytecodesKinitialize6F_v_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_iii_v_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii1i_v_; +text: .text%__1cJBytecodesNpd_initialize6F_v_; +text: .text%__1cQclassLoader_init6F_v_; +text: .text%__1cLClassLoaderKinitialize6F_v_; +text: .text%__1cLClassLoaderQload_zip_library6F_v_; +text: .text%__1cCosTnative_java_library6F_pv_; +text: .text%JVM_GetInterfaceVersion; +text: .text%__1cHThreadsbMis_supported_jni_version_including_1_16Fi_C_; +text: .text%__1cKHandleMark2T6M_v_; +text: .text%__1cLClassLoaderbBsetup_bootstrap_search_path6F_v_; +text: .text%__1cCosGstrdup6Fpkc_pc_; +text: .text%__1cLClassLoaderbCupdate_class_path_entry_list6Fpkc_v_; +text: .text%__1cCosEstat6FpkcpnEstat__i_; +text: .text%stat: os_solaris.o; +text: .text%__1cLClassLoaderXcreate_class_path_entry6FpcnEstat_ppnOClassPathEntry__v_; +text: .text%__1cLClassLoaderSget_canonical_path6Fpc1i_i_; +text: .text%JVM_RawMonitorCreate; +text: .text%JVM_NativePath; +text: .text%JVM_RawMonitorEnter; +text: .text%__1cFMutexMjvm_raw_lock6M_v_; +text: .text%JVM_RawMonitorExit; +text: .text%__1cFMutexOjvm_raw_unlock6M_v_; +text: .text%JVM_Open; +text: .text%JVM_Lseek; +text: .text%JVM_Close; +text: .text%__1cDhpiFclose6Fi_i_: jvm.o; +text: .text%__1cRClassPathZipEntry2t6Mppvpc_v_; +text: .text%__1cOClassPathEntry2t6M_v_; +text: .text%__1cLClassLoaderLadd_to_list6FpnOClassPathEntry__v_; +text: .text%__1cOcodeCache_init6F_v_; +text: .text%__1cJCodeCacheKinitialize6F_v_; +text: .text%__1cICodeHeapHreserve6MIII_i_; +text: .text%__1cLlog2_intptr6Fi_i_: heap.o; +text: .text%__1cYalign_to_allocation_size6FI_I_: heap.o; +text: .text%__1cNReservedSpace2t6MI_v_; +text: .text%__1cNReservedSpaceKinitialize6MIIipc_v_; +text: .text%__1cCosOreserve_memory6FIpc_1_; +text: .text%__1cMVirtualSpaceKinitialize6MnNReservedSpace_I_i_; +text: .text%__1cMVirtualSpaceJexpand_by6MI_i_; +text: .text%__1cMVirtualSpaceQuncommitted_size6kM_I_; +text: .text%__1cMVirtualSpaceNreserved_size6kM_I_; +text: .text%__1cMVirtualSpaceOcommitted_size6kM_I_; +text: .text%__1cCosNcommit_memory6FpcII_i_; +text: .text%__1cSalign_to_page_size6FI_I_: heap.o; +text: .text%__1cICodeHeapFclear6M_v_; +text: .text%__1cICodeHeapTmark_segmap_as_free6MII_v_; +text: .text%__1cNMemoryServiceZadd_code_heap_memory_pool6FpnICodeHeap__v_; +text: .text%__1cMCodeHeapPool2t6MpnICodeHeap_pkci_v_; +text: .text%__1cICodeHeapIcapacity6kM_I_; +text: .text%__1cICodeHeapMmax_capacity6kM_I_; +text: .text%__1cKMemoryPool2t6Mpkcn0AIPoolType_IIii_v_; +text: .text%__1cNMemoryManagerbDget_code_cache_memory_manager6F_p0_; +text: .text%__1cNMemoryManager2t6M_v_; +text: .text%__1cNMemoryManagerIadd_pool6MpnKMemoryPool__v_; +text: .text%__1cKMemoryPoolLadd_manager6MpnNMemoryManager__v_; +text: .text%__1cLicache_init6F_v_; +text: .text%__1cPVM_Version_init6F_v_; +text: .text%__1cKVM_VersionKinitialize6F_v_; +text: .text%__1cKBufferBlobGcreate6Fpkci_p0_; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: thread.o; +text: .text%__1cRalign_code_offset6Fi_I_; +text: .text%__1cICodeHeapLheader_size6F_I_; +text: .text%__1cKBufferBlob2n6FII_pv_; +text: .text%__1cJCodeCacheIallocate6Fi_pnICodeBlob__; +text: .text%__1cICodeHeapIallocate6MI_pv_; +text: .text%__1cICodeHeapPsearch_freelist6MI_pnJFreeBlock__; +text: .text%__1cICodeHeapTmark_segmap_as_used6MII_v_; +text: .text%__1cKBufferBlob2t6Mpkci_v_; +text: .text%__1cICodeBlob2t6Mpkcii_v_; +text: .text%__1cICodeBlobMset_oop_maps6MpnJOopMapSet__v_; +text: .text%__1cNMemoryServiceXtrack_memory_pool_usage6FpnKMemoryPool__v_; +text: .text%__1cKMemoryPoolYrecord_peak_memory_usage6M_v_; +text: .text%__1cMCodeHeapPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cMCodeHeapPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cICodeHeapSallocated_capacity6kM_I_; +text: .text%__1cKMemoryPoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cXresource_allocate_bytes6FI_pc_; +text: .text%__1cKCodeBuffer2t6MpCi_v_; +text: .text%__1cRAbstractAssembler2t6MpnKCodeBuffer__v_; +text: .text%__1cYVM_Version_StubGeneratorTgenerate_getPsrInfo6M_pC_: vm_version_i486.o; +text: .text%__1cMStubCodeMark2t6MpnRStubCodeGenerator_pkc4_v_; +text: .text%__1cRStubCodeGeneratorLstub_prolog6MpnMStubCodeDesc__v_; +text: .text%__1cJAssemblerFpushl6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerGpushfd6M_v_; +text: .text%__1cJAssemblerEpopl6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerExorl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerExorl6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerFpopfd6M_v_; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerDjcc6Mn0AJCondition_rnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cJAssemblerFcpuid6M_v_; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerDjmp6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cRAbstractAssemblerEbind6MrnFLabel__v_; +text: .text%__1cRAbstractAssemblerHbind_to6MrnFLabel_i_v_; +text: .text%__1cMDisplacementEbind6MrnFLabel_ipnRAbstractAssembler__v_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerMemit_operand6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerMemit_operand6MpnMRegisterImpl_22nHAddressLScaleFactor_irknQRelocationHolder__v_; +text: .text%__1cKRelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cJAssemblerEmovl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerDret6Mi_v_; +text: .text%__1cMStubCodeMark2T6M_v_; +text: .text%__1cRAbstractAssemblerFflush6M_v_; +text: .text%__1cRStubCodeGeneratorLstub_epilog6MpnMStubCodeDesc__v_; +text: .text%__1cFVTuneNregister_stub6FpkcpC3_v_; +text: .text%__1cFForteNregister_stub6FpkcpC3_v_; +text: .text%__1cKVM_VersionWget_processor_features6F_v_; +text: .text%__1cCosMsupports_sse6F_i_; +text: .text%__1cVcheck_for_sse_support6F_v_: os_solaris_i486.o; +text: .text%jio_snprintf; +text: .text%jio_vsnprintf; +text: .text%__1cPlocal_vsnprintf6FpcIpkcpv_i_; +text: .text%__1cSstubRoutines_init16F_v_; +text: .text%__1cMStubRoutinesLinitialize16F_v_; +text: .text%__1cWStubGenerator_generate6FpnKCodeBuffer_i_v_; +text: .text%__1cNStubGeneratorbAgenerate_forward_exception6M_pC_: stubGenerator_i486.o; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCi_v_; +text: .text%__1cOMacroAssemblerRcall_VM_leaf_base6MpCi_v_; +text: .text%__1cJAssemblerEcall6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cJAssemblerJemit_data6MinJrelocInfoJrelocType_i_v_; +text: .text%__1cKRelocationLspec_simple6FnJrelocInfoJrelocType__nQRelocationHolder__; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: relocInfo.o; +text: .text%__1cKRelocationLunpack_data6M_v_: codeBlob.o; +text: .text%__1cJAssemblerJemit_data6MirknQRelocationHolder_i_v_; +text: .text%__1cKCodeBufferIrelocate6MpCrknQRelocationHolder_i_v_; +text: .text%__1cXruntime_call_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cOMacroAssemblerJincrement6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_i_v_; +text: .text%__1cOMacroAssemblerKget_thread6MpnMRegisterImpl__v_; +text: .text%__1cSThreadLocalStorageTpd_getTlsAccessMode6F_n0AQpd_tlsAccessMode__; +text: .text%__1cJAssemblerFpushl6Mi_v_; +text: .text%__1cJAssemblerEleal6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerEmovl6MnHAddress_i_v_; +text: .text%__1cOMacroAssemblerKverify_oop6MpnMRegisterImpl_pkc_v_; +text: .text%__1cJAssemblerDjmp6MpnMRegisterImpl_nJrelocInfoJrelocType__v_; +text: .text%__1cNStubGeneratorSgenerate_call_stub6MrpC_1_: stubGenerator_i486.o; +text: .text%__1cOMacroAssemblerFenter6M_v_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEdecl6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEcall6MpnMRegisterImpl_nJrelocInfoJrelocType__v_; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerGfstp_s6MnHAddress__v_; +text: .text%__1cJAssemblerGfstp_d6MnHAddress__v_; +text: .text%__1cNStubGeneratorYgenerate_catch_exception6M_pC_: stubGenerator_i486.o; +text: .text%__1cJAssemblerDjmp6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cNStubGeneratorUgenerate_atomic_xchg6M_pC_: stubGenerator_i486.o; +text: .text%__1cJAssemblerExchg6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerGpushad6M_v_; +text: .text%__1cJAssemblerFpopad6M_v_; +text: .text%__1cNStubGeneratorYgenerate_get_previous_fp6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorUgenerate_d2i_wrapper6MpC_1_: stubGenerator_i486.o; +text: .text%__1cOMacroAssemblerOpush_FPU_state6M_v_; +text: .text%__1cJAssemblerGfnsave6MnHAddress__v_; +text: .text%__1cJAssemblerFfwait6M_v_; +text: .text%__1cJAssemblerFfld_d6MnHAddress__v_; +text: .text%__1cJAssemblerFfst_d6MnHAddress__v_; +text: .text%__1cOMacroAssemblerPempty_FPU_stack6M_v_; +text: .text%__1cJAssemblerFffree6Mi_v_; +text: .text%__1cJAssemblerLemit_farith6Miii_v_; +text: .text%__1cOMacroAssemblerNpop_FPU_state6M_v_; +text: .text%__1cJAssemblerGfrstor6MnHAddress__v_; +text: .text%__1cNStubGeneratorUcreate_control_words6M_v_: stubGenerator_i486.o; +text: .text%__1cJTraceTime2T6M_v_; +text: .text%__1cNcarSpace_init6F_v_; +text: .text%__1cICarSpaceEinit6F_v_; +text: .text%__1cNuniverse_init6F_i_; +text: .text%__1cLJavaClassesbAcompute_hard_coded_offsets6F_v_; +text: .text%__1cLFileMapInfoKinitialize6M_i_; +text: .text%__1cLFileMapInfoNfail_continue6MpkcE_v_; +text: .text%__1cLFileMapInfoFclose6M_v_; +text: .text%__1cIUniversePinitialize_heap6F_i_; +text: .text%__1cPMarkSweepPolicy2t6M_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyQinitialize_flags6M_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyMrem_set_name6M_nJGenRemSetEName__: collectorPolicy.o; +text: .text%__1cJGenRemSetYmax_alignment_constraint6Fn0AEName__I_; +text: .text%__1cRCardTableModRefBSbBct_max_alignment_constraint6F_I_; +text: .text%__1cbCTwoGenerationCollectorPolicyUinitialize_size_info6M_v_; +text: .text%__1cPMarkSweepPolicyWinitialize_generations6M_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyVnumber_of_generations6M_i_: collectorPolicy.o; +text: .text%__1cXPermanentGenerationSpec2t6MnHPermGenEName_IIIIII_v_; +text: .text%__1cQGCPolicyCounters2t6Mpkcii_v_; +text: .text%__1cPPerfDataManagerMcounter_name6Fpkc2_pc_; +text: .text%__1cPPerfDataManagerWcreate_string_constant6FnJCounterNS_pkc3pnGThread__pnSPerfStringConstant__; +text: .text%__1cSPerfStringConstant2t6MnJCounterNS_pkc3_v_; +text: .text%__1cNPerfByteArray2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_i_v_; +text: .text%__1cKPerfStringKset_string6Mpkc_v_; +text: .text%__1cPPerfDataManagerUcreate_long_constant6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongConstant__; +text: .text%__1cQGenCollectedHeap2t6MpnPCollectorPolicy__v_; +text: .text%__1cKSharedHeap2t6MpnPCollectorPolicy__v_; +text: .text%__1cNCollectedHeap2t6M_v_; +text: .text%__1cHGCCauseJto_string6Fn0AFCause__pkc_; +text: .text%__1cPPerfDataManagerWcreate_string_variable6FnJCounterNS_pkci3pnGThread__pnSPerfStringVariable__; +text: .text%__1cMSubTasksDone2t6Mi_v_; +text: .text%__1cMSubTasksDoneFclear6M_v_; +text: .text%__1cMSubTasksDoneFvalid6M_i_; +text: .text%__1cQGenCollectedHeapKinitialize6M_i_; +text: .text%__1cPCollectorPolicyLgenerations6M_ppnOGenerationSpec__: collectorPolicy.o; +text: .text%__1cPCollectorPolicyUpermanent_generation6M_pnXPermanentGenerationSpec__: collectorPolicy.o; +text: .text%__1cXPermanentGenerationSpecFalign6MI_v_; +text: .text%__1cQGenCollectedHeapIallocate6MIpnXPermanentGenerationSpec_pIpipnNReservedSpace__pc_; +text: .text%__1cOGenerationSpecRn_covered_regions6kM_i_: collectorPolicy.o; +text: .text%__1cNReservedSpace2t6MIIipc_v_; +text: .text%__1cPCollectorPolicyOcreate_rem_set6MnJMemRegion_i_pnJGenRemSet__; +text: .text%__1cbCTwoGenerationCollectorPolicyQbarrier_set_name6M_nKBarrierSetEName__: collectorPolicy.o; +text: .text%__1cLCardTableRS2t6MnJMemRegion_i_v_; +text: .text%__1cRCardTableModRefBS2t6MnJMemRegion_i_v_; +text: .text%__1cNReservedSpaceYallocation_align_size_up6FI_I_; +text: .text%__1cJMemRegion2t6M_v_: cardTableModRefBS.o; +text: .text%__1cKSharedHeapPset_barrier_set6MpnKBarrierSet__v_; +text: .text%__1cNReservedSpaceKfirst_part6MIii_0_; +text: .text%__1cNReservedSpace2t6MpcI_v_; +text: .text%__1cOGenerationSpecEinit6MnNReservedSpace_ipnJGenRemSet__pnKGeneration__; +text: .text%__1cQDefNewGeneration2t6MnNReservedSpace_Iipkc_v_; +text: .text%__1cKGeneration2t6MnNReservedSpace_Ii_v_; +text: .text%__1cIageTable2t6Mi_v_; +text: .text%__1cIageTableFclear6M_v_; +text: .text%__1cFArenaEgrow6MI_pv_; +text: .text%__1cFChunkJnext_chop6M_v_; +text: .text%__1cFChunkEchop6M_v_; +text: .text%__1cFChunk2k6Fpv_v_; +text: .text%__1cRCardTableModRefBSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cRCardTableModRefBSbCfind_covering_region_by_base6MpnIHeapWord__i_; +text: .text%__1cRCardTableModRefBSbAlargest_prev_committed_end6kMi_pnIHeapWord__; +text: .text%__1cWSequentialSubTasksDoneFclear6M_v_; +text: .text%__1cSGenerationCounters2t6MpkciipnMVirtualSpace__v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkci_pc_; +text: .text%__1cRCollectorCounters2t6Mpkci_v_; +text: .text%__1cOCSpaceCounters2t6MpkciIpnPContiguousSpace_pnSGenerationCounters__v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkc2i_pc_; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnQPerfLongVariable__; +text: .text%__1cPPerfLongVariant2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_pnUPerfLongSampleHelper__v_; +text: .text%__1cPPerfLongVariantGsample6M_v_; +text: .text%__1cZContiguousSpaceUsedHelperLtake_sample6M_x_: cSpaceCounters.o; +text: .text%__1cPContiguousSpaceEused6kM_I_: space.o; +text: .text%__1cQDefNewGenerationYcompute_space_boundaries6MI_v_; +text: .text%__1cQCompactibleSpaceKinitialize6MnJMemRegion_i_v_; +text: .text%__1cFSpaceKinitialize6MnJMemRegion_i_v_; +text: .text%__1cFSpaceKset_bottom6MpnIHeapWord__v_: space.o; +text: .text%__1cJEdenSpaceHset_end6MpnIHeapWord__v_: space.o; +text: .text%__1cJEdenSpaceFclear6M_v_; +text: .text%__1cPContiguousSpaceFclear6M_v_; +text: .text%__1cFSpaceFclear6M_v_; +text: .text%__1cFSpaceHset_end6MpnIHeapWord__v_: space.o; +text: .text%__1cQDefNewGenerationPupdate_counters6M_v_; +text: .text%__1cSGenerationCountersKupdate_all6M_v_: generationCounters.o; +text: .text%__1cNReservedSpaceJlast_part6MI_0_; +text: .text%__1cRTenuredGeneration2t6MnNReservedSpace_IipnJGenRemSet__v_; +text: .text%__1cOCardGeneration2t6MnNReservedSpace_IipnJGenRemSet__v_; +text: .text%__1cWBlockOffsetSharedArray2t6MnJMemRegion_I_v_; +text: .text%__1cWBlockOffsetSharedArrayGresize6MI_v_; +text: .text%__1cNReservedSpaceSpage_align_size_up6FI_I_; +text: .text%__1cLCardTableRSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cLCardTableRSKis_aligned6MpnIHeapWord__i_: cardTableRS.o; +text: .text%__1cHGCStats2t6M_v_; +text: .text%__1cWOffsetTableContigSpace2t6MpnWBlockOffsetSharedArray_nJMemRegion__v_; +text: .text%__1cQBlockOffsetArray2t6MpnWBlockOffsetSharedArray_nJMemRegion_i_v_; +text: .text%__1cWOffsetTableContigSpaceKset_bottom6MpnIHeapWord__v_; +text: .text%__1cQBlockOffsetArrayGresize6MI_v_: blockOffsetTable.o; +text: .text%__1cWOffsetTableContigSpaceHset_end6MpnIHeapWord__v_; +text: .text%__1cWOffsetTableContigSpaceFclear6M_v_; +text: .text%__1cbBBlockOffsetArrayContigSpaceUinitialize_threshold6M_pnIHeapWord__; +text: .text%__1cUGenericGrowableArrayEgrow6Mi_v_; +text: .text%__1cXPermanentGenerationSpecEinit6MnNReservedSpace_IpnJGenRemSet__pnHPermGen__; +text: .text%__1cRCompactingPermGen2t6MnNReservedSpace_1IpnJGenRemSet_pnXPermanentGenerationSpec__v_; +text: .text%__1cUCompactingPermGenGen2t6MnNReservedSpace_1IipnJGenRemSet_pnPContiguousSpace_pnXPermanentGenerationSpec__v_; +text: .text%__1cUCompactingPermGenGenbFinitialize_performance_counters6M_v_; +text: .text%__1cbCOneContigSpaceCardGenerationIcapacity6kM_I_; +text: .text%__1cPCollectorPolicybFis_concurrent_mark_sweep_policy6M_i_: collectorPolicy.o; +text: .text%__1cWThreadLocalAllocBufferWstartup_initialization6F_v_; +text: .text%__1cPGlobalTLABStats2t6M_v_; +text: .text%__1cPGlobalTLABStatsKinitialize6M_v_; +text: .text%__1cXAdaptiveWeightedAverageGsample6Mf_v_; +text: .text%__1cXAdaptiveWeightedAverageYcompute_adaptive_average6Mff_f_; +text: .text%__1cQGenCollectedHeapNtlab_capacity6kM_I_; +text: .text%__1cQDefNewGenerationYsupports_tlab_allocation6kM_i_: defNewGeneration.o; +text: .text%__1cQDefNewGenerationNtlab_capacity6kM_I_: defNewGeneration.o; +text: .text%__1cKGenerationYsupports_tlab_allocation6kM_i_: tenuredGeneration.o; +text: .text%__1cWThreadLocalAllocBufferImax_size6F_I_; +text: .text%__1cOBasicHashtable2t6Mii_v_: universe.o; +text: .text%__1cLClassLoaderZcreate_package_info_table6F_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: classLoader.o; +text: .text%__1cQinterpreter_init6F_v_; +text: .text%__1cTAbstractInterpreterKinitialize6F_v_; +text: .text%__1cNTemplateTableKinitialize6F_v_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pF_vc_v_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFi_vi_v_; +text: .text%__1cITemplateKinitialize6MinITosState_1pFi_vi_v_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFn0AJOperation__v4_v_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFn0AJCondition__v4_v_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pF3_v3_v_; +text: .text%__1cNTemplateTableNpd_initialize6F_v_; +text: .text%__1cRInvocationCounterMreinitialize6Fi_v_; +text: .text%__1cRInvocationCounterDdef6Fn0AFState_ipFnMmethodHandle_pnGThread__pC_v_; +text: .text%__1cJStubQdDueue2t6MpnNStubInterface_ipnFMutex_pkc_v_; +text: .text%__1cICodeHeapJexpand_by6MI_i_; +text: .text%__1cJStubQdDueueOregister_queue6Fp0_v_; +text: .text%__1cUInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cbCAbstractInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cbCAbstractInterpreterGeneratorMgenerate_all6M_v_; +text: .text%__1cLCodeletMark2t6MrpnZInterpreterMacroAssembler_pkcinJBytecodesECode__v_: interpreter.o; +text: .text%__1cJStubQdDueueHrequest6Mi_pnEStub__; +text: .text%__1cbBInterpreterCodeletInterfaceRcode_size_to_size6kMi_i_: interpreter.o; +text: .text%__1cbBInterpreterCodeletInterfaceKinitialize6MpnEStub_i_v_: interpreter.o; +text: .text%__1cSInterpreterCodeletKinitialize6MpkcinJBytecodesECode__v_; +text: .text%__1cbCAbstractInterpreterGeneratorTgenerate_error_exit6Mpkc_pC_; +text: .text%__1cOMacroAssemblerEstop6Mpkc_v_; +text: .text%__1cJAssemblerEcall6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cJAssemblerDhlt6M_v_; +text: .text%__1cOMacroAssemblerFalign6Mi_v_; +text: .text%__1cJAssemblerDnop6M_v_; +text: .text%__1cJStubQdDueueGcommit6Mi_v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_return_entry_for6MnITosState_i_pC_; +text: .text%__1cZInterpreterMacroAssemblerbAget_cache_and_index_at_bcp6MpnMRegisterImpl_2i_v_; +text: .text%__1cOMacroAssemblerSload_unsigned_word6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cJAssemblerGmovzxw6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerEshll6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerEandl6MpnMRegisterImpl_i_v_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_next6MnITosState_i_v_; +text: .text%__1cOMacroAssemblerSload_unsigned_byte6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cJAssemblerGmovzxb6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_base6MnITosState_ppCi_v_; +text: .text%__1cZInterpreterMacroAssemblerKverify_FPU6MinITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerKverify_oop6MpnMRegisterImpl_nITosState__v_; +text: .text%__1cJAssemblerDjmp6MnHAddress__v_; +text: .text%__1cOMacroAssemblerKverify_FPU6Mipkc_v_; +text: .text%__1cKEntryPoint2t6MpC11111111_v_; +text: .text%__1cJAssemblerEincl6MpnMRegisterImpl__v_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_deopt_entry_for6MnITosState_i_pC_; +text: .text%__1cJAssemblerEcmpl6MnHAddress_i_v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pCi_v_; +text: .text%__1cOMacroAssemblerOcall_VM_helper6MpnMRegisterImpl_pCii_v_; +text: .text%__1cZInterpreterMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cOMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cZInterpreterMacroAssemblerZcheck_and_handle_popframe6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerDjcc6Mn0AJCondition_pCnJrelocInfoJrelocType__v_; +text: .text%__1cTAbstractInterpreterSBasicType_as_index6FnJBasicType__i_; +text: .text%__1cbCAbstractInterpreterGeneratorbBgenerate_result_handler_for6MnJBasicType__pC_; +text: .text%__1cOMacroAssemblerGc2bool6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEsetb6Mn0AJCondition_pnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerQsign_extend_byte6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerGmovsxb6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerRsign_extend_short6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerGmovsxw6MpnMRegisterImpl_2_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEtemp6F_pnMRegisterImpl__; +text: .text%__1cZInterpreterMacroAssemblerDpop6MnITosState__v_; +text: .text%__1cbCAbstractInterpreterGeneratorbFgenerate_slow_signature_handler6M_pC_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC222i_v_; +text: .text%__1cTAbstractInterpreterRTosState_as_index6FnITosState__i_; +text: .text%__1cTAbstractInterpreterMreturn_entry6FnITosState_i_pC_; +text: .text%__1cKEntryPointFentry6kMnITosState__pC_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_continuation_for6MnITosState__pC_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_safept_entry_for6MnITosState_pC_2_; +text: .text%__1cZInterpreterMacroAssemblerEpush6MnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerMdispatch_via6MnITosState_ppC_v_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_throw_exception6M_v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_i_v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpCpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpCpnMRegisterImpl_33_v_; +text: .text%__1cZInterpreterMacroAssemblerRremove_activation6MnITosState_pnMRegisterImpl_iii_v_; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_i_v_; +text: .text%__1cZInterpreterMacroAssemblerNunlock_object6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerElock6M_v_; +text: .text%__1cJAssemblerHcmpxchg6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cHAddress2t6MinJrelocInfoJrelocType__v_; +text: .text%__1cOMacroAssemblerFleave6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbMgenerate_ArrayIndexOutOfBounds_handler6Mpkc_pC_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC22i_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbHgenerate_exception_handler_common6Mpkc2i_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_StackOverflowError_handler6M_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_method_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cUInterpreterGeneratorbEgenerate_asm_interpreter_entry6Mi_pC_; +text: .text%__1cUInterpreterGeneratorXcheck_for_compiled_code6MrnFLabel__v_; +text: .text%__1cUInterpreterGeneratorbDgenerate_stack_overflow_check6M_v_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cUInterpreterGeneratorUgenerate_fixed_frame6Mi_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_counter_incr6MpnFLabel_22_v_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cbCAbstractInterpreterGeneratorXbang_stack_shadow_pages6Mi_v_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: interp_masm_i486.o; +text: .text%__1cZInterpreterMacroAssemblerTnotify_method_entry6M_v_; +text: .text%__1cUInterpreterGeneratorZgenerate_counter_overflow6MpC_v_; +text: .text%__1cJAssemblerEnegl6MpnMRegisterImpl__v_; +text: .text%__1cUInterpreterGeneratorbAgenerate_run_compiled_code6M_v_; +text: .text%__1cZInterpreterMacroAssemblerNsuper_call_VM6MpnMRegisterImpl_2pC22_v_; +text: .text%__1cUInterpreterGeneratorLlock_method6M_v_; +text: .text%__1cZInterpreterMacroAssemblerLlock_object6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cUInterpreterGeneratorUgenerate_empty_entry6M_pC_; +text: .text%__1cUInterpreterGeneratorXgenerate_accessor_entry6M_pC_; +text: .text%__1cJAssemblerEshrl6MpnMRegisterImpl_i_v_; +text: .text%__1cLlog2_intptr6Fi_i_: interpreter_i486.o; +text: .text%__1cOMacroAssemblerQload_signed_byte6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cJAssemblerGmovsxb6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cOMacroAssemblerQload_signed_word6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cJAssemblerGmovsxw6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cUInterpreterGeneratorXgenerate_abstract_entry6M_pC_; +text: .text%__1cUInterpreterGeneratorTgenerate_math_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cOMacroAssemblerGsincos6Miii_v_; +text: .text%__1cJAssemblerFfld_s6Mi_v_; +text: .text%__1cJAssemblerEfabs6M_v_; +text: .text%__1cOMacroAssemblerEfcmp6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerHfucomip6Mi_v_; +text: .text%__1cOMacroAssemblerEfpop6M_v_; +text: .text%__1cJAssemblerHfincstp6M_v_; +text: .text%__1cJAssemblerEfsin6M_v_; +text: .text%__1cJAssemblerEfcos6M_v_; +text: .text%__1cJAssemblerFfsqrt6M_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_native_entry6Mi_pC_; +text: .text%__1cNSharedRuntimebWnative_method_throw_unsatisfied_link_error_entry6F_pC_; +text: .text%__1cJAssemblerGmembar6M_v_; +text: .text%__1cJAssemblerEaddl6MnHAddress_i_v_; +text: .text%__1cJAssemblerSemit_arith_operand6MipnMRegisterImpl_nHAddress_i_v_; +text: .text%__1cZInterpreterMacroAssemblerSnotify_method_exit6MnITosState__v_; +text: .text%__1cbCAbstractInterpreterGeneratorbEset_entry_points_for_all_bytes6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorQset_entry_points6MnJBytecodesECode__v_; +text: .text%__1cbCAbstractInterpreterGeneratorWset_short_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cbCAbstractInterpreterGeneratorVset_vtos_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_and_dispatch6MpnITemplate_nITosState__v_; +text: .text%__1cITemplateIbytecode6kM_nJBytecodesECode__; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_prolog6MnITosState_i_v_; +text: .text%__1cITemplateIgenerate6MpnZInterpreterMacroAssembler__v_; +text: .text%__1cNTemplateTableDnop6F_v_; +text: .text%__1cNTemplateTableKtransition6FnITosState_1_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_epilog6MnITosState_i_v_; +text: .text%__1cNDispatchTableJset_entry6MirnKEntryPoint__v_; +text: .text%__1cNTemplateTableLaconst_null6F_v_; +text: .text%__1cNTemplateTableGiconst6Fi_v_; +text: .text%__1cNTemplateTableGlconst6Fi_v_; +text: .text%__1cNTemplateTableGfconst6Fi_v_; +text: .text%__1cJAssemblerEfldz6M_v_; +text: .text%__1cJAssemblerEfld16M_v_; +text: .text%__1cJAssemblerFfaddp6Mi_v_; +text: .text%__1cNTemplateTableGdconst6Fi_v_; +text: .text%__1cNTemplateTableGbipush6F_v_; +text: .text%__1cNTemplateTableGsipush6F_v_; +text: .text%__1cJAssemblerFbswap6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEsarl6MpnMRegisterImpl_i_v_; +text: .text%__1cNTemplateTableDldc6Fi_v_; +text: .text%__1cJAssemblerEmovb6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC2_v_; +text: .text%__1cJAssemblerFfld_s6MnHAddress__v_; +text: .text%__1cZInterpreterMacroAssemblerbGget_unsigned_2_byte_index_at_bcp6MpnMRegisterImpl_i_v_; +text: .text%__1cNTemplateTableGldc2_w6F_v_; +text: .text%__1cJAssemblerEcmpb6MnHAddress_i_v_; +text: .text%__1cNTemplateTableFiload6F_v_; +text: .text%__1cNTemplateTableMlocals_index6FpnMRegisterImpl_i_v_; +text: .text%__1cbCAbstractInterpreterGeneratorUset_wide_entry_point6MpnITemplate_rpC_v_; +text: .text%__1cNTemplateTableKwide_iload6F_v_; +text: .text%__1cNTemplateTableRlocals_index_wide6FpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableFlload6F_v_; +text: .text%__1cNTemplateTableKwide_lload6F_v_; +text: .text%__1cNTemplateTableFfload6F_v_; +text: .text%__1cNTemplateTableKwide_fload6F_v_; +text: .text%__1cNTemplateTableFdload6F_v_; +text: .text%__1cNTemplateTableKwide_dload6F_v_; +text: .text%__1cNTemplateTableFaload6F_v_; +text: .text%__1cNTemplateTableKwide_aload6F_v_; +text: .text%__1cNTemplateTableFiload6Fi_v_; +text: .text%__1cNTemplateTableFlload6Fi_v_; +text: .text%__1cNTemplateTableFfload6Fi_v_; +text: .text%__1cNTemplateTableFdload6Fi_v_; +text: .text%__1cNTemplateTableHaload_06F_v_; +text: .text%__1cNTemplateTableFaload6Fi_v_; +text: .text%__1cNTemplateTableGiaload6F_v_; +text: .text%__1cNTemplateTableLindex_check6FpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerKnull_check6MpnMRegisterImpl_i_v_; +text: .text%__1cOMacroAssemblerZneeds_explicit_null_check6Fi_i_; +text: .text%__1cNTemplateTableGlaload6F_v_; +text: .text%__1cNTemplateTableGfaload6F_v_; +text: .text%__1cNTemplateTableGdaload6F_v_; +text: .text%__1cNTemplateTableGaaload6F_v_; +text: .text%__1cNTemplateTableGbaload6F_v_; +text: .text%__1cNTemplateTableGcaload6F_v_; +text: .text%__1cNTemplateTableGsaload6F_v_; +text: .text%__1cNTemplateTableGistore6F_v_; +text: .text%__1cNTemplateTableLwide_istore6F_v_; +text: .text%__1cNTemplateTableGlstore6F_v_; +text: .text%__1cNTemplateTableLwide_lstore6F_v_; +text: .text%__1cNTemplateTableGfstore6F_v_; +text: .text%__1cNTemplateTableLwide_fstore6F_v_; +text: .text%__1cNTemplateTableGdstore6F_v_; +text: .text%__1cNTemplateTableLwide_dstore6F_v_; +text: .text%__1cNTemplateTableGastore6F_v_; +text: .text%__1cNTemplateTableLwide_astore6F_v_; +text: .text%__1cNTemplateTableGistore6Fi_v_; +text: .text%__1cNTemplateTableGlstore6Fi_v_; +text: .text%__1cNTemplateTableGfstore6Fi_v_; +text: .text%__1cNTemplateTableGdstore6Fi_v_; +text: .text%__1cNTemplateTableGastore6Fi_v_; +text: .text%__1cNTemplateTableHiastore6F_v_; +text: .text%__1cNTemplateTableHlastore6F_v_; +text: .text%__1cNTemplateTableHfastore6F_v_; +text: .text%__1cNTemplateTableHdastore6F_v_; +text: .text%__1cNTemplateTableHaastore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerRprofile_checkcast6MipnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerRgen_subtype_check6MpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cJAssemblerKrepne_scan6M_v_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerSstore_check_part_16MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerSstore_check_part_26MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEmovb6MnHAddress_i_v_; +text: .text%__1cNTemplateTableHbastore6F_v_; +text: .text%__1cJAssemblerEmovb6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cNTemplateTableHcastore6F_v_; +text: .text%__1cJAssemblerEmovw6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cNTemplateTableHsastore6F_v_; +text: .text%__1cNTemplateTableDpop6F_v_; +text: .text%__1cNTemplateTableEpop26F_v_; +text: .text%__1cNTemplateTableDdup6F_v_; +text: .text%__1cJAssemblerFpushl6MnHAddress__v_; +text: .text%__1cNTemplateTableGdup_x16F_v_; +text: .text%__1cNTemplateTableGdup_x26F_v_; +text: .text%__1cNTemplateTableEdup26F_v_; +text: .text%__1cNTemplateTableHdup2_x16F_v_; +text: .text%__1cNTemplateTableHdup2_x26F_v_; +text: .text%__1cNTemplateTableEswap6F_v_; +text: .text%__1cNTemplateTableEiop26Fn0AJOperation__v_; +text: .text%__1cNTemplateTableElop26Fn0AJOperation__v_; +text: .text%__1cJAssemblerEadcl6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableEfop26Fn0AJOperation__v_; +text: .text%__1cJAssemblerGfadd_s6MnHAddress__v_; +text: .text%__1cZInterpreterMacroAssemblerGf2ieee6M_v_; +text: .text%__1cNTemplateTableEdop26Fn0AJOperation__v_; +text: .text%__1cJAssemblerGfadd_d6MnHAddress__v_; +text: .text%__1cZInterpreterMacroAssemblerGd2ieee6M_v_; +text: .text%__1cJAssemblerEsbbl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerHfsubr_s6MnHAddress__v_; +text: .text%__1cJAssemblerHfsubr_d6MnHAddress__v_; +text: .text%__1cJAssemblerFimull6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableElmul6F_v_; +text: .text%__1cOMacroAssemblerElmul6Mii_v_; +text: .text%__1cJAssemblerEmull6MnHAddress__v_; +text: .text%__1cJAssemblerEmull6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerGfmul_s6MnHAddress__v_; +text: .text%__1cJAssemblerGfmul_d6MnHAddress__v_; +text: .text%__1cJAssemblerFfld_x6MnHAddress__v_; +text: .text%__1cJAssemblerFfmulp6Mi_v_; +text: .text%__1cNTemplateTableEidiv6F_v_; +text: .text%__1cOMacroAssemblerPcorrected_idivl6MpnMRegisterImpl__i_; +text: .text%__1cJAssemblerEcdql6M_v_; +text: .text%__1cJAssemblerFidivl6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableEldiv6F_v_; +text: .text%__1cZInterpreterMacroAssemblerRcall_VM_leaf_base6MpCi_v_; +text: .text%__1cJAssemblerHfdivr_s6MnHAddress__v_; +text: .text%__1cJAssemblerHfdivr_d6MnHAddress__v_; +text: .text%__1cJAssemblerGfdivrp6Mi_v_; +text: .text%__1cNTemplateTableEirem6F_v_; +text: .text%__1cNTemplateTableElrem6F_v_; +text: .text%__1cOMacroAssemblerFfremr6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerIsave_eax6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerFfprem6M_v_; +text: .text%__1cJAssemblerJfnstsw_ax6M_v_; +text: .text%__1cJAssemblerEsahf6M_v_; +text: .text%__1cOMacroAssemblerLrestore_eax6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEfxch6Mi_v_; +text: .text%__1cNTemplateTableEineg6F_v_; +text: .text%__1cNTemplateTableElneg6F_v_; +text: .text%__1cOMacroAssemblerElneg6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEadcl6MpnMRegisterImpl_i_v_; +text: .text%__1cNTemplateTableEfneg6F_v_; +text: .text%__1cJAssemblerEfchs6M_v_; +text: .text%__1cNTemplateTableEdneg6F_v_; +text: .text%__1cJAssemblerEshll6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableElshl6F_v_; +text: .text%__1cOMacroAssemblerElshl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerFshldl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEsarl6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableElshr6F_v_; +text: .text%__1cOMacroAssemblerElshr6MpnMRegisterImpl_2i_v_; +text: .text%__1cJAssemblerFshrdl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEshrl6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableFlushr6F_v_; +text: .text%__1cJAssemblerEandl6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableEiinc6F_v_; +text: .text%__1cJAssemblerEaddl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cNTemplateTableJwide_iinc6F_v_; +text: .text%__1cNTemplateTableHconvert6F_v_; +text: .text%__1cOMacroAssemblerLextend_sign6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerGfild_s6MnHAddress__v_; +text: .text%__1cJAssemblerGfild_d6MnHAddress__v_; +text: .text%__1cNTemplateTableElcmp6F_v_; +text: .text%__1cOMacroAssemblerIlcmp2int6MpnMRegisterImpl_222_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fi_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fii_v_; +text: .text%__1cOMacroAssemblerIfcmp2int6MpnMRegisterImpl_i_v_; +text: .text%__1cNTemplateTableKdouble_cmp6Fi_v_; +text: .text%__1cNTemplateTableHif_0cmp6Fn0AJCondition__v_; +text: .text%__1cFj_not6FnNTemplateTableJCondition__nJAssemblerJCondition__: templateTable_i486.o; +text: .text%__1cNTemplateTableGbranch6Fii_v_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_taken_branch6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_only6MnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerYprofile_not_taken_branch6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableHif_icmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_acmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableF_goto6F_v_; +text: .text%__1cNTemplateTableDjsr6F_v_; +text: .text%__1cZInterpreterMacroAssemblerWdispatch_only_noverify6MnITosState__v_; +text: .text%__1cNTemplateTableDret6F_v_; +text: .text%__1cZInterpreterMacroAssemblerLprofile_ret6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableIwide_ret6F_v_; +text: .text%__1cNTemplateTableLtableswitch6F_v_; +text: .text%__1cZInterpreterMacroAssemblerTprofile_switch_case6MpnMRegisterImpl_22_v_; +text: .text%__1cZInterpreterMacroAssemblerWprofile_switch_default6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableMlookupswitch6F_v_; +text: .text%__1cNTemplateTableH_return6FnITosState__v_; +text: .text%__1cNTemplateTableJgetstatic6Fi_v_; +text: .text%__1cNTemplateTableSgetfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableZload_field_cp_cache_entry6FipnMRegisterImpl_22i_v_; +text: .text%__1cNTemplateTableXresolve_cache_and_index6FipnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerHfistp_d6MnHAddress__v_; +text: .text%__1cNTemplateTableJputstatic6Fi_v_; +text: .text%__1cNTemplateTableSputfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableUjvmti_post_field_mod6Fii_v_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cNTemplateTableQvolatile_barrier6F_v_; +text: .text%__1cNTemplateTableIgetfield6Fi_v_; +text: .text%__1cNTemplateTableOpatch_bytecode6FnJBytecodesECode_pnMRegisterImpl_4i_v_; +text: .text%__1cNTemplateTableIputfield6Fi_v_; +text: .text%__1cNTemplateTableNinvokevirtual6Fi_v_; +text: .text%__1cNTemplateTableOprepare_invoke6FpnMRegisterImpl_2inJBytecodesECode__v_; +text: .text%__1cNTemplateTablebAload_invoke_cp_cache_entry6FipnMRegisterImpl_22ii_v_; +text: .text%__1cNTemplateTableUinvokevirtual_helper6FpnMRegisterImpl_22_v_; +text: .text%__1cZInterpreterMacroAssemblerSprofile_final_call6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_virtual_call6MpnMRegisterImpl_22_v_; +text: .text%__1cNTemplateTableNinvokespecial6Fi_v_; +text: .text%__1cZInterpreterMacroAssemblerMprofile_call6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableMinvokestatic6Fi_v_; +text: .text%__1cNTemplateTablePinvokeinterface6Fi_v_; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_; +text: .text%__1cbCAbstractInterpreterGeneratorRset_unimplemented6Mi_v_; +text: .text%__1cNTemplateTableE_new6F_v_; +text: .text%__1cQGenCollectedHeapItop_addr6kM_ppnIHeapWord__; +text: .text%__1cQDefNewGenerationItop_addr6kM_ppnIHeapWord__; +text: .text%__1cQGenCollectedHeapIend_addr6kM_ppnIHeapWord__; +text: .text%__1cQDefNewGenerationIend_addr6kM_ppnIHeapWord__; +text: .text%__1cOMacroAssemblerJdecrement6MpnMRegisterImpl_i_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC22_v_; +text: .text%__1cNTemplateTableInewarray6F_v_; +text: .text%__1cNTemplateTableJanewarray6F_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC222_v_; +text: .text%__1cNTemplateTableLarraylength6F_v_; +text: .text%__1cNTemplateTableGathrow6F_v_; +text: .text%__1cNTemplateTableJcheckcast6F_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC_v_; +text: .text%__1cNTemplateTableKinstanceof6F_v_; +text: .text%__1cNTemplateTableMmonitorenter6F_v_; +text: .text%__1cJAssemblerFcmovl6Mn0AJCondition_pnMRegisterImpl_3_v_; +text: .text%__1cNTemplateTableLmonitorexit6F_v_; +text: .text%__1cNTemplateTableEwide6F_v_; +text: .text%__1cNTemplateTableOmultianewarray6F_v_; +text: .text%__1cNTemplateTableKif_nullcmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableGgoto_w6F_v_; +text: .text%__1cNTemplateTableFjsr_w6F_v_; +text: .text%__1cNTemplateTableL_breakpoint6F_v_; +text: .text%__1cZInterpreterMacroAssemblerUdispatch_only_normal6MnITosState__v_; +text: .text%__1cNTemplateTableQfast_accessfield6FnITosState__v_; +text: .text%__1cNTemplateTablePfast_storefield6FnITosState__v_; +text: .text%__1cNTemplateTableZjvmti_post_fast_field_mod6F_v_; +text: .text%__1cNTemplateTableMfast_xaccess6FnITosState__v_; +text: .text%__1cNTemplateTableKfast_iload6F_v_; +text: .text%__1cNTemplateTableLfast_iload26F_v_; +text: .text%__1cNTemplateTableMfast_icaload6F_v_; +text: .text%__1cNTemplateTableRfast_invokevfinal6Fi_v_; +text: .text%__1cNTemplateTableRfast_linearswitch6F_v_; +text: .text%__1cNTemplateTableRfast_binaryswitch6F_v_; +text: .text%__1cNTemplateTableSshouldnotreachhere6F_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbCset_safepoints_for_all_bytes6M_v_; +text: .text%__1cWinvocationCounter_init6F_v_; +text: .text%__1cOmarksweep_init6F_v_; +text: .text%__1cQaccessFlags_init6F_v_; +text: .text%__1cStemplateTable_init6F_v_; +text: .text%__1cVInterfaceSupport_init6F_v_; +text: .text%__1cOuniverse2_init6F_v_; +text: .text%__1cIUniverseHgenesis6FpnGThread__v_; +text: .text%__1cIUniverseYcompute_base_vtable_size6F_v_; +text: .text%__1cLClassLoaderVcompute_Object_vtable6F_i_; +text: .text%__1cKklassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassVbase_create_klass_oop6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__pnMklassOopDesc__; +text: .text%__1cKklassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: klassKlass.o; +text: .text%__1cKKlass_vtbl2n6FIrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: klass.o; +text: .text%__1cKSharedHeapWpermanent_mem_allocate6MI_pnIHeapWord__: genCollectedHeap.o; +text: .text%__1cRCompactingPermGenMmem_allocate6MI_pnIHeapWord__; +text: .text%__1cbCOneContigSpaceCardGenerationIallocate6MIii_pnIHeapWord__: compactingPermGenGen.o; +text: .text%__1cWOffsetTableContigSpaceIallocate6MI_pnIHeapWord__: space.o; +text: .text%__1cPContiguousSpaceIallocate6MI_pnIHeapWord__; +text: .text%__1cbBBlockOffsetArrayContigSpaceLalloc_block6MpnIHeapWord_2_v_: blockOffsetTable.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: klass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: klass.o; +text: .text%__1cRCardTableModRefBSEkind6M_nKBarrierSetEName__: cardTableModRefBS.o; +text: .text%__1cFKlassMset_subklass6MpnMklassOopDesc__v_; +text: .text%__1cFKlassQset_next_sibling6MpnMklassOopDesc__v_; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: klassKlass.o; +text: .text%__1cParrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassRbase_create_klass6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__1_; +text: .text%__1cParrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: arrayKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: arrayKlassKlass.o; +text: .text%__1cPjava_lang_ClassNcreate_mirror6FnLKlassHandle_pnGThread__pnHoopDesc__; +text: .text%__1cFKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cSobjArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: objArrayKlassKlass.o; +text: .text%__1cSinstanceKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cSinstanceKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: instanceKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cTtypeArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlassKlass.o; +text: .text%__1cbBBlockOffsetArrayContigSpaceQalloc_block_work6MpnIHeapWord_2_v_; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: typeArrayKlassKlass.o; +text: .text%__1cLsymbolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cLsymbolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: symbolKlass.o; +text: .text%__1cLsymbolKlassOset_alloc_size6MI_v_: symbolKlass.o; +text: .text%__1cKoopFactoryKnew_symbol6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cLSymbolTableGlookup6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cJHashtableLhash_symbol6Fpkci_I_: symbolTable.o; +text: .text%__1cLSymbolTableGlookup6MipkciI_pnNsymbolOopDesc__; +text: .text%__1cLSymbolTableJbasic_add6MipCiIpnGThread__pnNsymbolOopDesc__; +text: .text%__1cLsymbolKlassPallocate_symbol6MpCipnGThread__pnNsymbolOopDesc__; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: symbolKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: symbolKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: symbolKlass.o; +text: .text%__1cJHashtableJnew_entry6MIpnHoopDesc__pnOHashtableEntry__; +text: .text%__1cOBasicHashtableJnew_entry6MI_pnTBasicHashtableEntry__; +text: .text%__1cOtypeArrayKlassMcreate_klass6FnJBasicType_ipnGThread__pnMklassOopDesc__; +text: .text%__1cOtypeArrayKlassNexternal_name6FnJBasicType__pkc_; +text: .text%__1cKarrayKlassXbase_create_array_klass6FrknKKlass_vtbl_inLKlassHandle_pnGThread__nQarrayKlassHandle__; +text: .text%__1cOtypeArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlass.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: typeArrayKlass.o; +text: .text%__1cLAccessFlagsPatomic_set_bits6Mi_v_; +text: .text%__1cLlog2_intptr6Fi_i_: typeArrayKlass.o; +text: .text%__1cKarrayKlassbBcomplete_create_array_klass6FnQarrayKlassHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cFKlassRinitialize_supers6MpnMklassOopDesc_pnGThread__v_; +text: .text%__1cKarrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cKarrayKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cLklassVtableRinitialize_vtable6MpnGThread__v_; +text: .text%__1cKarrayKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cKarrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cLmethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cLmethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodKlass.o; +text: .text%__1cLmethodKlassOset_alloc_size6MI_v_: methodKlass.o; +text: .text%__1cQconstMethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cQconstMethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassOset_alloc_size6MI_v_: constMethodKlass.o; +text: .text%__1cPmethodDataKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cPmethodDataKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodDataKlass.o; +text: .text%__1cPmethodDataKlassOset_alloc_size6MI_v_: methodDataKlass.o; +text: .text%__1cRconstantPoolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cRconstantPoolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constantPoolKlass.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: constantPoolKlass.o; +text: .text%__1cWconstantPoolCacheKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cWconstantPoolCacheKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: cpCacheKlass.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: cpCacheKlass.o; +text: .text%__1cVcompiledICHolderKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cVcompiledICHolderKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: compiledICHolderKlass.o; +text: .text%__1cVcompiledICHolderKlassOset_alloc_size6MI_v_: compiledICHolderKlass.o; +text: .text%__1cSobjArrayKlassKlassbEallocate_system_objArray_klass6MpnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassXallocate_objArray_klass6MinLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassbCallocate_objArray_klass_impl6FnYobjArrayKlassKlassHandle_inLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlass.o; +text: .text%__1cNsymbolOopDescLas_C_string6kM_pc_; +text: .text%__1cNsymbolOopDescLas_C_string6kMpci_1_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlass.o; +text: .text%__1cNobjArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlass.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: objArrayKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlass.o; +text: .text%__1cLlog2_intptr6Fi_i_: objArrayKlassKlass.o; +text: .text%__1cNobjArrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNobjArrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cKoopFactoryXnew_permanent_byteArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cOtypeArrayKlassSallocate_permanent6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: typeArrayKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: typeArrayKlass.o; +text: .text%__1cKoopFactoryYnew_permanent_shortArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cKoopFactoryWnew_permanent_intArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cKoopFactoryTnew_system_objArray6FipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: oopFactory.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: oopFactory.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: oopFactory.o; +text: .text%__1cJvmSymbolsKinitialize6FpnGThread__v_; +text: .text%__1cNsymbolOopDescGequals6kMpkci_i_; +text: .text%__1cQSystemDictionaryKinitialize6FpnGThread__v_; +text: .text%__1cKDictionary2t6Mi_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: dictionary.o; +text: .text%__1cQPlaceholderTable2t6Mi_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: placeholders.o; +text: .text%__1cVLoaderConstraintTable2t6Mi_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: loaderConstraints.o; +text: .text%__1cQSystemDictionarybCinitialize_preloaded_classes6FpnGThread__v_; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_nGHandle_2ipnGThread__pnMklassOopDesc__; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cQSystemDictionarybEresolve_instance_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cVjava_lang_ClassLoaderbBnon_reflection_class_loader6FpnHoopDesc__2_; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_: systemDictionary.o; +text: .text%__1cHoopDescSslow_identity_hash6M_i_; +text: .text%__1cSObjectSynchronizerXidentity_hash_value_for6FnGHandle__i_; +text: .text%__1cNget_next_hash6F_i_: synchronizer.o; +text: .text%__1cCosGrandom6F_l_; +text: .text%__1cKDictionaryEfind6MiInMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cKDictionaryJget_entry6MiInMsymbolHandle_nGHandle__pnPDictionaryEntry__; +text: .text%__1cQSystemDictionarybAcompute_loader_lock_object6FnGHandle_pnGThread__1_; +text: .text%__1cMObjectLocker2t6MnGHandle_pnGThread__v_; +text: .text%__1cSObjectSynchronizerKfast_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cQSystemDictionaryKfind_class6FiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cKDictionaryKfind_class6MiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cQPlaceholderTableKfind_entry6MiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cQPlaceholderTableJadd_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cQPlaceholderTableJnew_entry6MipnNsymbolOopDesc_pnHoopDesc__pnQPlaceholderEntry__; +text: .text%__1cQSystemDictionaryTload_instance_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cQSystemDictionaryRload_shared_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cQSystemDictionaryRfind_shared_class6FnMsymbolHandle__pnMklassOopDesc__; +text: .text%__1cQSystemDictionaryRload_shared_class6FnTinstanceKlassHandle_nGHandle_pnGThread__1_; +text: .text%__1cLClassLoaderOload_classfile6FnMsymbolHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cFVTuneQstart_class_load6F_v_; +text: .text%__1cJEventMark2t6MpkcE_v_: classLoader.o; +text: .text%__1cSThreadProfilerMark2t6Mn0AGRegion__v_; +text: .text%__1cMstringStream2t6MI_v_; +text: .text%__1cMstringStreamFwrite6MpkcI_v_; +text: .text%__1cMoutputStreamPupdate_position6MpkcI_v_; +text: .text%__1cMstringStreamJas_string6M_pc_; +text: .text%__1cMelapsedTimerFstart6M_v_; +text: .text%__1cCosPelapsed_counter6F_x_; +text: .text%__1cRClassPathZipEntryLopen_stream6Mpkc_pnPClassFileStream__; +text: .text%__1cPClassFileStream2t6MpCipc_v_; +text: .text%__1cMelapsedTimerEstop6M_v_; +text: .text%__1cPClassFileParserOparseClassFile6MnMsymbolHandle_nGHandle_2r1pnGThread__nTinstanceKlassHandle__; +text: .text%__1cIVerifierRshould_verify_for6FpnHoopDesc__i_; +text: .text%__1cPClassFileStreamGget_u46MpnGThread__I_; +text: .text%__1cPClassFileStreamGget_u26MpnGThread__H_; +text: .text%__1cIVerifierQrelax_verify_for6FpnHoopDesc__i_; +text: .text%__1cVjava_lang_ClassLoaderRis_trusted_loader6FpnHoopDesc__i_; +text: .text%__1cQSystemDictionarySjava_system_loader6F_pnHoopDesc__; +text: .text%__1cPClassFileParserTparse_constant_pool6MpnGThread__nSconstantPoolHandle__; +text: .text%__1cPClassFileParserOcheck_property6MipkcipnGThread__v_; +text: .text%__1cKoopFactoryQnew_constantPool6FipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cRconstantPoolKlassIallocate6MipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: constantPoolKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: constantPoolKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: constantPoolKlass.o; +text: .text%__1cPClassFileParserbBparse_constant_pool_entries6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileStreamGget_u16MpnGThread__C_; +text: .text%__1cPClassFileParserbFparse_constant_pool_class_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserbJparse_constant_pool_methodref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserbGparse_constant_pool_string_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserbHparse_constant_pool_integer_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserbEparse_constant_pool_utf8_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserRverify_legal_utf86MpkCipnGThread__v_; +text: .text%__1cPClassFileStreamHskip_u16MipnGThread__v_; +text: .text%__1cPClassFileParserbLparse_constant_pool_nameandtype_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cTconstantPoolOopDescSklass_ref_index_at6Mi_i_; +text: .text%__1cTconstantPoolOopDescbAname_and_type_ref_index_at6Mi_i_; +text: .text%__1cTconstantPoolOopDescRname_ref_index_at6Mi_i_; +text: .text%__1cTconstantPoolOopDescWsignature_ref_index_at6Mi_i_; +text: .text%__1cPClassFileParserbCverify_legal_class_modifiers6MipnGThread__v_; +text: .text%__1cPClassFileParserQparse_interfaces6MnSconstantPoolHandle_nGHandle_2pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserMparse_fields6MnSconstantPoolHandle_ipnUFieldAllocationCount_pnOobjArrayHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cUinitialize_hashtable6FppnLNameSigHash__v_; +text: .text%__1cPclear_hashtable6FppnLNameSigHash__v_; +text: .text%__1cPClassFileParserNparse_methods6MnSconstantPoolHandle_ipnLAccessFlags_ppnPobjArrayOopDesc_66pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserMparse_method6MnSconstantPoolHandle_ipnLAccessFlags_pnPtypeArrayHandle_55pnGThread__nMmethodHandle__; +text: .text%__1cPClassFileParserYverify_legal_method_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbDverify_legal_method_modifiers6MiinMsymbolHandle_pnGThread__v_; +text: .text%__1cPClassFileParserWparse_linenumber_table6MIIpipnGThread__pC_; +text: .text%__1cbFCompressedLineNumberWriteStream2t6Mi_v_; +text: .text%__1cVCompressedWriteStream2t6Mi_v_; +text: .text%__1cQCompressedStream2t6MpCi_v_; +text: .text%__1cbFCompressedLineNumberWriteStreamKwrite_pair6Mii_v_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: methodOop.o; +text: .text%__1cKoopFactoryKnew_method6FinLAccessFlags_iiipnGThread__pnNmethodOopDesc__; +text: .text%__1cKoopFactoryPnew_constMethod6FiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cQconstMethodKlassIallocate6MiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cSconstMethodOopDescLobject_size6Fiiii_i_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: constMethodKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: constMethodKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: constMethodKlass.o; +text: .text%__1cSconstMethodOopDescZset_inlined_tables_length6Miii_v_; +text: .text%__1cLmethodKlassIallocate6MnRconstMethodHandle_nLAccessFlags_pnGThread__pnNmethodOopDesc__; +text: .text%__1cNmethodOopDescLobject_size6Fi_i_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: methodKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: methodKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: methodKlass.o; +text: .text%__1cNmethodOopDescJinit_code6M_v_; +text: .text%__1cRInvocationCounterEinit6M_v_; +text: .text%__1cRInvocationCounterFreset6M_v_; +text: .text%__1cRInvocationCounterJset_state6Mn0AFState__v_; +text: .text%__1cLmethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNmethodOopDescbAcompute_size_of_parameters6MpnGThread__v_; +text: .text%__1cRSignatureIterator2t6MnMsymbolHandle__v_; +text: .text%__1cRSignatureIteratorSiterate_parameters6M_v_; +text: .text%__1cRSignatureIteratorGexpect6Mc_v_; +text: .text%__1cSconstMethodOopDescbBcompressed_linenumber_table6kM_pC_; +text: .text%__1cPClassFileParserUassemble_annotations6MpCi1ipnGThread__nPtypeArrayHandle__; +text: .text%__1cNmethodOopDescVclear_native_function6M_v_; +text: .text%__1cNmethodOopDescTset_native_function6MpC_v_; +text: .text%__1cNmethodOopDescVset_signature_handler6MpC_v_; +text: .text%__1cVCompressedWriteStreamEgrow6M_v_; +text: .text%__1cRSignatureIteratorKparse_type6M_i_; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: frame.o; +text: .text%__1cUArgumentSizeComputerDset6MinJBasicType__v_: frame.o; +text: .text%__1cPClassFileParserYparse_checked_exceptions6MpHInSconstantPoolHandle_pnGThread__1_; +text: .text%__1cPClassFileStreamHskip_u26MipnGThread__v_; +text: .text%__1cSconstMethodOopDescbEchecked_exceptions_length_addr6kM_pH_; +text: .text%__1cSconstMethodOopDescYchecked_exceptions_start6kM_pnXCheckedExceptionElement__; +text: .text%__1cXcopy_u2_with_conversion6FpH0i_v_: classFileParser.o; +text: .text%__1cNSignatureInfoHdo_long6M_v_: frame.o; +text: .text%__1cNSignatureInfoGdo_int6M_v_: frame.o; +text: .text%__1cPClassFileParserbDcompute_transitive_interfaces6MnTinstanceKlassHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cPClassFileParserMsort_methods6MnOobjArrayHandle_111pnGThread__nPtypeArrayHandle__; +text: .text%__1cNmethodOopDescMsort_methods6FpnPobjArrayOopDesc_222_v_; +text: .text%method_compare: methodOop.o; +text: .text%__1cLklassVtablebKcompute_vtable_size_and_num_mirandas6Fri1pnMklassOopDesc_pnPobjArrayOopDesc_nLAccessFlags_pnHoopDesc_pnNsymbolOopDesc_5_v_; +text: .text%__1cLklassVtableWneeds_new_vtable_entry6FpnNmethodOopDesc_pnMklassOopDesc_pnHoopDesc_pnNsymbolOopDesc_nLAccessFlags__i_; +text: .text%__1cLklassVtableQget_num_mirandas6FpnMklassOopDesc_pnPobjArrayOopDesc_4_i_; +text: .text%__1cLklassVtableMget_mirandas6FpnNGrowableArray4CpnNmethodOopDesc___pnMklassOopDesc_pnPobjArrayOopDesc_8_v_; +text: .text%__1cLklassItableTcompute_itable_size6FnOobjArrayHandle__i_; +text: .text%__1cUvisit_all_interfaces6FpnPobjArrayOopDesc_pnXInterfaceVisiterClosure__v_; +text: .text%__1cPClassFileParserUcompute_oop_map_size6MnTinstanceKlassHandle_ii_i_; +text: .text%__1cKoopFactoryRnew_instanceKlass6FiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cSinstanceKlassKlassXallocate_instance_klass6MiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlass.o; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceKlass.o; +text: .text%__1cNinstanceKlassQinit_implementor6M_v_; +text: .text%__1cNinstanceKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cTconstantPoolOopDescNklass_name_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cFKlassMoop_is_klass6kM_i_: symbolKlass.o; +text: .text%__1cPClassFileParserbAparse_classfile_attributes6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbKparse_classfile_sourcefile_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassWdo_local_static_fields6MpFpnPfieldDescriptor_pnGThread__v4_v_; +text: .text%__1cNinstanceKlassbBdo_local_static_fields_impl6FnTinstanceKlassHandle_pFpnPfieldDescriptor_pnGThread__v5_v_; +text: .text%__1cNinstanceKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cLklassItableZsetup_itable_offset_table6FnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserNfill_oop_maps6MnTinstanceKlassHandle_ii_v_; +text: .text%__1cFKlassKsuperklass6kM_pnNinstanceKlass__; +text: .text%__1cPClassFileParserVset_precomputed_flags6MnTinstanceKlassHandle__v_; +text: .text%__1cFKlassNlookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cNinstanceKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cNinstanceKlassLfind_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cNinstanceKlassLfind_method6FpnPobjArrayOopDesc_pnNsymbolOopDesc_4_pnNmethodOopDesc__; +text: .text%__1cNmethodOopDescPis_empty_method6kM_i_; +text: .text%__1cPClassFileParserYcheck_super_class_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCcheck_super_interface_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbBcheck_final_method_override6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cTClassLoadingServiceTnotify_class_loaded6FpnNinstanceKlass_i_v_; +text: .text%__1cTClassLoadingServiceScompute_class_size6FpnNinstanceKlass__I_; +text: .text%__1cSinstanceKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceKlass.o; +text: .text%__1cRconstantPoolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLClassLoaderLadd_package6Fpkci_i_; +text: .text%__1cLClassLoaderOlookup_package6Fpkc_pnLPackageInfo__; +text: .text%__1cQPackageHashtableMcompute_hash6Mpkci_I_: classLoader.o; +text: .text%__1cQPackageHashtableJget_entry6MiIpkcI_pnLPackageInfo__: classLoader.o; +text: .text%__1cMstringStream2T6M_v_; +text: .text%__1cSThreadProfilerMark2T6M_v_; +text: .text%__1cFVTuneOend_class_load6F_v_; +text: .text%__1cQSystemDictionaryVdefine_instance_class6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cQSystemDictionaryRcheck_constraints6FiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cVLoaderConstraintTablePcheck_or_update6MnTinstanceKlassHandle_nGHandle_nMsymbolHandle__pkc_; +text: .text%__1cVLoaderConstraintTableWfind_loader_constraint6MnMsymbolHandle_nGHandle__ppnVLoaderConstraintEntry__; +text: .text%__1cQSystemDictionaryQadd_to_hierarchy6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cFKlassWappend_to_sibling_list6M_v_; +text: .text%__1cNinstanceKlassSprocess_interfaces6MpnGThread__v_; +text: .text%__1cIUniverseTflush_dependents_on6FnTinstanceKlassHandle__v_; +text: .text%__1cJCodeCachebKnumber_of_nmethods_with_dependencies6F_i_; +text: .text%__1cQSystemDictionaryRupdate_dictionary6FiIiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cQSystemDictionaryQfind_placeholder6FiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cQPlaceholderTableMremove_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cKDictionaryJadd_klass6MnMsymbolHandle_nGHandle_nLKlassHandle__v_; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_: dictionary.o; +text: .text%__1cKDictionaryJnew_entry6MIpnMklassOopDesc_pnHoopDesc__pnPDictionaryEntry__; +text: .text%__1cNinstanceKlassQeager_initialize6MpnGThread__v_; +text: .text%__1cMObjectLocker2T6M_v_; +text: .text%__1cSObjectSynchronizerJfast_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cPClassFileParserbIparse_constant_pool_fieldref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserbSparse_constant_pool_interfacemethodref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserbEparse_constant_pool_long_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileStreamGget_u86MpnGThread__X_; +text: .text%__1cSObjectSynchronizerKslow_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cKJavaThreadNis_lock_owned6kMpC_i_; +text: .text%__1cGThreadLis_in_stack6kMpC_i_; +text: .text%__1cQSystemDictionaryVresolve_super_or_fail6FnMsymbolHandle_1nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cKReflectionTverify_class_access6FpnMklassOopDesc_2i_i_; +text: .text%__1cPClassFileParserbBcheck_illegal_static_method6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbJparse_classfile_signature_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_field_modifiers6MiipnGThread__v_; +text: .text%__1cPClassFileParserXverify_legal_field_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_field_signature6MnMsymbolHandle_1pnGThread__v_; +text: .text%__1cPClassFileParserWparse_field_attributes6MnSconstantPoolHandle_iHpHpi2pnPtypeArrayHandle_pnGThread__v_; +text: .text%__1cTconstantPoolOopDescbBbasic_type_for_signature_at6Mi_nJBasicType__; +text: .text%__1cJFieldTypeKbasic_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cJchar2type6Fc_nJBasicType__: fieldType.o; +text: .text%__1cRSignatureIteratorSskip_optional_size6M_v_; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: frame.o; +text: .text%__1cPClassFileParserVparse_exception_table6MIInSconstantPoolHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: frame.o; +text: .text%__1cNSignatureInfoHdo_char6M_v_: frame.o; +text: .text%__1cNSignatureInfoIdo_float6M_v_: frame.o; +text: .text%__1cNSignatureInfoJdo_double6M_v_: frame.o; +text: .text%__1cbDreorder_based_on_method_index6FpnPobjArrayOopDesc_1ppnHoopDesc__v_: methodOop.o; +text: .text%__1cLklassVtableYadd_new_mirandas_to_list6FpnNGrowableArray4CpnNmethodOopDesc___pnPobjArrayOopDesc_6pnMklassOopDesc__v_; +text: .text%__1cLklassVtableKis_miranda6FpnNmethodOopDesc_pnPobjArrayOopDesc_pnMklassOopDesc__i_; +text: .text%__1cWCountInterfacesClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cPClassFileParserbNparse_classfile_inner_classes_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__H_; +text: .text%__1cPfieldDescriptorKinitialize6MpnMklassOopDesc_i_v_; +text: .text%__1cXinitialize_static_field6FpnPfieldDescriptor_pnGThread__v_: classFileParser.o; +text: .text%__1cPfieldDescriptorSlong_initial_value6kM_x_; +text: .text%__1cFKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cSSetupItableClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cNmethodOopDescWis_vanilla_constructor6kM_i_; +text: .text%__1cNinstanceKlassPadd_implementor6MpnMklassOopDesc__v_; +text: .text%__1cPClassFileParserXjava_lang_Class_fix_pre6MpnOobjArrayHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cPClassFileParserYjava_lang_Class_fix_post6Mpi_v_; +text: .text%__1cPfieldDescriptorRint_initial_value6kM_i_; +text: .text%__1cIUniverseNfixup_mirrors6FpnGThread__v_; +text: .text%__1cKSharedHeapYpermanent_object_iterate6MpnNObjectClosure__v_: genCollectedHeap.o; +text: .text%__1cHPermGenOobject_iterate6MpnNObjectClosure__v_: permGen.o; +text: .text%__1cRCompactingPermGenGas_gen6kM_pnKGeneration__: permGen.o; +text: .text%__1cbCOneContigSpaceCardGenerationOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cPContiguousSpaceOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cPContiguousSpaceTobject_iterate_from6MnJWaterMark_pnNObjectClosure__v_; +text: .text%__1cSFixupMirrorClosureJdo_object6MpnHoopDesc__v_: universe.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: klassKlass.o; +text: .text%__1cNinstanceKlassbBallocate_permanent_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: instanceKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: instanceKlass.o; +text: .text%__1cKklassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: klassKlass.o; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: arrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassOklass_oop_size6kM_i_: objArrayKlassKlass.o; +text: .text%__1cSinstanceKlassKlassOklass_oop_size6kM_i_: instanceKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassOklass_oop_size6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cLsymbolKlassOklass_oop_size6kM_i_: symbolKlass.o; +text: .text%__1cLsymbolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassIoop_size6kMpnHoopDesc__i_: typeArrayKlassKlass.o; +text: .text%__1cKarrayKlassLobject_size6kMi_i_; +text: .text%__1cLmethodKlassOklass_oop_size6kM_i_: methodKlass.o; +text: .text%__1cQconstMethodKlassOklass_oop_size6kM_i_: constMethodKlass.o; +text: .text%__1cPmethodDataKlassOklass_oop_size6kM_i_: methodDataKlass.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: arrayKlassKlass.o; +text: .text%__1cRconstantPoolKlassOklass_oop_size6kM_i_: constantPoolKlass.o; +text: .text%__1cWconstantPoolCacheKlassOklass_oop_size6kM_i_: cpCacheKlass.o; +text: .text%__1cVcompiledICHolderKlassOklass_oop_size6kM_i_: compiledICHolderKlass.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassIoop_size6kMpnHoopDesc__i_: objArrayKlassKlass.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: objArrayKlass.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodKlass.o; +text: .text%__1cSinstanceKlassKlassMoop_is_klass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: instanceKlass.o; +text: .text%__1cFKlassXsearch_secondary_supers6kMpnMklassOopDesc__i_; +text: .text%__1cPClassFileParserbFjava_lang_ref_Reference_fix_pre6MpnPtypeArrayHandle_nSconstantPoolHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cQinstanceRefKlassZupdate_nonstatic_oop_maps6FpnMklassOopDesc__v_; +text: .text%__1cQinstanceRefKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceRefKlass.o; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceRefKlass.o; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceRefKlass.o; +text: .text%__1cKReflectionVis_same_class_package6FpnMklassOopDesc_2_i_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnMklassOopDesc__i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceRefKlass.o; +text: .text%__1cNinstanceKlassVis_same_class_package6FpnHoopDesc_pnNsymbolOopDesc_24_i_; +text: .text%__1cEUTF8Hstrrchr6FpWiW_1_; +text: .text%__1cEUTF8Fequal6FpWi1i_i_; +text: .text%__1cPClassFileParserbFparse_constant_pool_float_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: frame.o; +text: .text%__1cNSignatureInfoIdo_short6M_v_: frame.o; +text: .text%__1cRappend_interfaces6FnOobjArrayHandle_ripnPobjArrayOopDesc__v_; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlass.o; +text: .text%__1cPfieldDescriptorTfloat_initial_value6kM_f_; +text: .text%__1cPClassFileParserbGparse_constant_pool_double_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPfieldDescriptorUdouble_initial_value6kM_d_; +text: .text%__1cQSystemDictionarybDinitialize_basic_type_mirrors6FpnGThread__v_; +text: .text%__1cPjava_lang_ClassYcreate_basic_type_mirror6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cNobjArrayKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cXreferenceProcessor_init6F_v_; +text: .text%__1cSReferenceProcessorMinit_statics6F_v_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJset_clock6Fx_v_; +text: .text%__1cQjni_handles_init6F_v_; +text: .text%__1cKJNIHandlesKinitialize6F_v_; +text: .text%__1cOvmStructs_init6F_v_; +text: .text%__1cVverificationType_init6F_v_; +text: .text%__1cQVerificationTypeKinitialize6F_v_; +text: .text%__1cOcompiler1_init6F_v_; +text: .text%__1cKSharedInfoKset_stack06Fi_v_; +text: .text%__1cKSharedInfoLset_regName6F_v_; +text: .text%__1cIRegAllocYinit_register_allocation6F_v_; +text: .text%__1cIFrameMapEinit6F_v_; +text: .text%__1cKc1_RegMaskKinit_masks6Fi_v_; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_FrameMap_i486.o; +text: .text%__1cNc1_AllocTableLinit_tables6F_v_; +text: .text%__1cIFrameMapOfirst_register6F_pnMRegisterImpl__; +text: .text%__1cIFrameMapLcpu_reg2rnr6FpnMRegisterImpl__i_; +text: .text%__1cIFrameMapLcpu_rnr2reg6Fi_pnMRegisterImpl__; +text: .text%__1cIRuntime1Kinitialize6F_v_; +text: .text%__1cKCodeBufferRinsts_memory_size6Fi_i_; +text: .text%__1cKCodeBufferQlocs_memory_size6Fi_i_; +text: .text%__1cIRuntime1Ninitialize_pd6F_v_; +text: .text%__1cNSharedRuntimeTgenerate_deopt_blob6F_v_; +text: .text%__1cKCodeBuffer2t6MiiiiiipnKBufferBlob_pnJrelocInfo_pnORelocateBuffer_ipnLOopRecorder_pkcii_v_; +text: .text%__1cKCodeBufferQalloc_relocation6MI_v_; +text: .text%__1cJOopMapSet2t6M_v_; +text: .text%__1cJAssemblerEsubl6MnHAddress_i_v_; +text: .text%__1cTsave_live_registers6FpnOMacroAssembler_i_pnGOopMap__: c1_Runtime1_i486.o; +text: .text%__1cJAssemblerGfldenv6MnHAddress__v_; +text: .text%__1cGOopMap2t6Mii_v_; +text: .text%__1cGOopMapQset_callee_saved6MnHOptoRegEName_ii2_v_; +text: .text%__1cGOopMapHset_xxx6MnHOptoRegEName_nLOopMapValueJoop_types_ii2_v_; +text: .text%__1cGOopMapbEmap_compiler_reg_to_oopmap_reg6MnHOptoRegEName_ii_nFVMRegEName__; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: oopMap.o; +text: .text%__1cIFrameMapRfpu_stack_regname6Fi_nHOptoRegEName__; +text: .text%__1cKRelocationJpack_data6M_i_: codeBlob.o; +text: .text%__1cJrelocInfoNfinish_prefix6Mph_p0_; +text: .text%__1cJrelocInfo2t6Mn0AJrelocType_ii_v_; +text: .text%__1cOMacroAssemblerTset_last_Java_frame6MpnMRegisterImpl_22pC_v_; +text: .text%__1cJOopMapSetKadd_gc_map6MiipnGOopMap__v_; +text: .text%__1cOMacroAssemblerVreset_last_Java_frame6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerEdecl6MnHAddress__v_; +text: .text%__1cSDeoptimizationBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_iiii_p0_; +text: .text%__1cICodeBlobPallocation_size6FpnKCodeBuffer_ii_I_; +text: .text%__1cNRelocIteratorTlocs_and_index_size6Fii_i_; +text: .text%__1cSDeoptimizationBlob2n6FII_pv_; +text: .text%__1cSDeoptimizationBlob2t6MpnKCodeBuffer_ipnJOopMapSet_iiii_v_; +text: .text%__1cICodeBlob2t6MpkcpnKCodeBuffer_iiipnJOopMapSet_i_v_; +text: .text%__1cKCodeBufferPcopy_relocation6MpnICodeBlob__v_; +text: .text%__1cNRelocIteratorMcreate_index6FpnKCodeBuffer_pnJrelocInfo_4_4_; +text: .text%__1cKCodeBufferJcopy_code6MpnICodeBlob__v_; +text: .text%__1cRAbstractAssemblerOcode_fill_byte6F_i_; +text: .text%__1cICodeBlobWfix_relocation_at_move6Mi_v_; +text: .text%__1cNRelocIteratorKinitialize6MipnICodeBlob_pC3_v_; +text: .text%__1cNRelocIteratorKset_limits6MpC1_v_; +text: .text%__1cVPatchingRelocIteratorHprepass6M_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: relocInfo.o; +text: .text%__1cNRelocIteratorEnext6M_i_: codeBlob.o; +text: .text%__1cNRelocIteratorFreloc6M_pnKRelocation__; +text: .text%__1cOCallRelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cOCallRelocationFvalue6M_pC_: codeBlob.o; +text: .text%__1cKRelocationTpd_call_destination6M_pC_; +text: .text%__1cKNativeCallLdestination6kM_pC_; +text: .text%__1cOCallRelocationPset_destination6MpCi_v_; +text: .text%__1cKRelocationXpd_set_call_destination6MpCi_v_; +text: .text%__1cRNativeInstructionFwrote6Mi_v_; +text: .text%__1cKRelocationLunpack_data6M_v_: relocInfo.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: relocInfo.o; +text: .text%__1cVPatchingRelocIteratorIpostpass6M_v_; +text: .text%__1cJOopMapSetJheap_size6kM_i_; +text: .text%__1cGOopMapJheap_size6kM_i_; +text: .text%__1cJOopMapSetHcopy_to6MpC_v_; +text: .text%__1cGOopMapHcopy_to6MpC_v_; +text: .text%__1cIRuntime1Rgenerate_blob_for6Fn0AGStubID__v_; +text: .text%__1cIRuntime1Pnew_code_buffer6F_pnKCodeBuffer__; +text: .text%__1cLOopRecorder2t6MpnFArena__v_; +text: .text%__1cNStubAssembler2t6MpnKCodeBuffer__v_; +text: .text%__1cIRuntime1Rgenerate_code_for6Fn0AGStubID_pnNStubAssembler_pi_pnJOopMapSet__; +text: .text%__1cIRuntime1Iname_for6Fn0AGStubID__pkc_; +text: .text%__1cLRuntimeStubQnew_runtime_stub6FpkcpnKCodeBuffer_ipnJOopMapSet_i_p0_; +text: .text%__1cLRuntimeStub2n6FII_pv_; +text: .text%__1cLRuntimeStub2t6MpkcpnKCodeBuffer_iipnJOopMapSet_i_v_; +text: .text%__1cJStubFrame2t6MpnNStubAssembler_pkci_v_; +text: .text%__1cNStubAssemblerIset_info6Mpkci_v_; +text: .text%__1cNStubAssemblerHcall_RT6MpnMRegisterImpl_2pC2_i_; +text: .text%__1cNStubAssemblerHcall_RT6MpnMRegisterImpl_2pCi_i_; +text: .text%__1cJStubFrame2T6M_v_; +text: .text%__1cIRuntime1Ygenerate_exception_throw6FpnNStubAssembler_pCpnMRegisterImpl__pnJOopMapSet__; +text: .text%__1cOMacroAssemblerLtlab_refill6MrnFLabel_22_v_; +text: .text%__1cLlog2_intptr6Fi_i_: assembler_i486.o; +text: .text%__1cOMacroAssemblerNeden_allocate6MpnMRegisterImpl_2i2rnFLabel__v_; +text: .text%__1cOMacroAssemblerLverify_tlab6M_v_; +text: .text%__1cLlog2_intptr6Fi_i_: c1_Runtime1_i486.o; +text: .text%__1cOMacroAssemblerNtlab_allocate6MpnMRegisterImpl_2i22rnFLabel__v_; +text: .text%__1cRC1_MacroAssemblerRinitialize_object6MpnMRegisterImpl_22i22_v_; +text: .text%__1cRC1_MacroAssemblerRinitialize_header6MpnMRegisterImpl_22_v_; +text: .text%__1cRC1_MacroAssemblerPinitialize_body6MpnMRegisterImpl_2i2_v_; +text: .text%__1cNStubAssemblerHcall_RT6MpnMRegisterImpl_2pC22_i_; +text: .text%__1cNStubAssemblerHcall_RT6MpnMRegisterImpl_2pC222_i_; +text: .text%__1cIRuntime1Iblob_for6Fn0AGStubID__pnICodeBlob__; +text: .text%__1cJStubFrame2t6MpnNStubAssembler_pkcipnMRegisterImpl_6_v_; +text: .text%__1cJStubFrame2t6MpnNStubAssembler_pkcipnMRegisterImpl__v_; +text: .text%__1cIiEntries2t6Miiii_v_; +text: .text%__1cRNativeGeneralJumpQjump_destination6kM_pC_; +text: .text%__1cJAssemblerOlocate_operand6FpCn0AMWhichOperand__1_; +text: .text%__1cIRuntime1Rgenerate_patching6FpnNStubAssembler_pC_pnJOopMapSet__; +text: .text%__1cWrestore_live_registers6FpnOMacroAssembler__v_: c1_Runtime1_i486.o; +text: .text%__1cNSafepointBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNSafepointBlob2n6FII_pv_; +text: .text%__1cNSafepointBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cJAssemblerFfldcw6MnHAddress__v_; +text: .text%__1cJAssemblerGfnstcw6MnHAddress__v_; +text: .text%__1cJAssemblerHfcomp_d6MnHAddress__v_; +text: .text%__1cIiEntriesIset_base6MpC_v_; +text: .text%__1cQvtableStubs_init6F_v_; +text: .text%__1cLVtableStubsKinitialize6F_v_; +text: .text%__1cWInlineCacheBuffer_init6F_v_; +text: .text%__1cRInlineCacheBufferKinitialize6F_v_; +text: .text%__1cRInlineCacheBufferOinit_next_stub6F_v_; +text: .text%__1cRInlineCacheBufferRic_stub_code_size6F_i_; +text: .text%__1cJStubQdDueueRrequest_committed6Mi_pnEStub__; +text: .text%__1cPICStubInterfaceRcode_size_to_size6kMi_i_: icBuffer.o; +text: .text%__1cPICStubInterfaceKinitialize6MpnEStub_i_v_: icBuffer.o; +text: .text%__1cTcompilerOracle_init6F_v_; +text: .text%__1cOCompilerOracleRparse_from_string6Fpkc_v_; +text: .text%__1cOCompilerOracleOread_from_line6Fpc_v_; +text: .text%__1cOCompilerOraclePparse_from_file6F_v_; +text: .text%__1cHcc_file6F_pkc_: compilerOracle.o; +text: .text%__1cXonStackReplacement_init6F_v_; +text: .text%__1cSOnStackReplacementKinitialize6F_v_; +text: .text%__1cUGenericGrowableArrayPraw_at_put_grow6MipknEGrET_3_v_; +text: .text%__1cWcompilationPolicy_init6F_v_; +text: .text%__1cSuniverse_post_init6F_v_; +text: .text%__1cIUniverseWreinitialize_vtable_of6FpnFKlass_pnGThread__v_; +text: .text%__1cNinstanceKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceKlass.o; +text: .text%__1cLklassVtableVinitialize_from_super6MnLKlassHandle__i_; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlass.o; +text: .text%__1cLklassVtableTupdate_super_vtable6MpnNinstanceKlass_pnNmethodOopDesc_i_i_; +text: .text%__1cLklassVtableNput_method_at6MpnNmethodOopDesc_i_v_; +text: .text%__1cLklassVtableQfill_in_mirandas6Mri_v_; +text: .text%__1cFKlassIsubklass6kM_p0_; +text: .text%__1cLklassVtableOcopy_vtable_to6MpnLvtableEntry__v_; +text: .text%__1cLklassVtableXvtable_accessibility_at6Mi_n0AKAccessType__; +text: .text%__1cFKlassMnext_sibling6kM_p0_; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: objArrayKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlass.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: constantPoolKlass.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: typeArrayKlass.o; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceRefKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceRefKlass.o; +text: .text%__1cIUniverseUreinitialize_itables6F_v_; +text: .text%__1cQSystemDictionaryKclasses_do6FpFpnMklassOopDesc__v_v_; +text: .text%__1cKDictionaryKclasses_do6MpFpnMklassOopDesc__v_v_; +text: .text%__1cbBinitialize_itable_for_klass6FpnMklassOopDesc__v_; +text: .text%__1cNinstanceKlassGitable6kM_pnLklassItable__; +text: .text%__1cLklassItable2t6MnTinstanceKlassHandle__v_; +text: .text%__1cLklassItableRinitialize_itable6M_v_; +text: .text%__1cLklassItablebFinitialize_itable_for_interface6MpnMklassOopDesc_pnRitableMethodEntry__v_; +text: .text%__1cRitableMethodEntryKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cKoopFactoryMnew_objArray6FpnMklassOopDesc_ipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassRallocate_objArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6FnTinstanceKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassTarray_klass_or_null6M_pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlass.o; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6FnTobjArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassTarray_klass_or_null6Mi_pnMklassOopDesc__; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: instanceKlass.o; +text: .text%__1cNCollectedHeapXallocate_from_tlab_slow6FpnGThread_I_pnIHeapWord__; +text: .text%__1cQGenCollectedHeapVunsafe_max_tlab_alloc6kM_I_; +text: .text%__1cQDefNewGenerationVunsafe_max_tlab_alloc6kM_I_: defNewGeneration.o; +text: .text%__1cQDefNewGenerationVunsafe_max_alloc_nogc6kM_I_; +text: .text%__1cPContiguousSpaceEfree6kM_I_: space.o; +text: .text%__1cWThreadLocalAllocBufferXclear_before_allocation6M_v_; +text: .text%__1cWThreadLocalAllocBufferFreset6M_v_; +text: .text%__1cQGenCollectedHeapRallocate_new_tlab6MI_pnIHeapWord__; +text: .text%__1cQGenCollectedHeapMmem_allocate6MIii_pnIHeapWord__; +text: .text%__1cbCTwoGenerationCollectorPolicyRmem_allocate_work6MIii_pnIHeapWord__; +text: .text%__1cQGenCollectedHeapEheap6F_p0_; +text: .text%__1cQDefNewGenerationPshould_allocate6MIii_i_: defNewGeneration.o; +text: .text%__1cQDefNewGenerationMpar_allocate6MIii_pnIHeapWord__: defNewGeneration.o; +text: .text%__1cJEdenSpaceMpar_allocate6MI_pnIHeapWord__; +text: .text%__1cPContiguousSpaceRpar_allocate_impl6MIkpnIHeapWord__2_: space.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: collectedHeap.o; +text: .text%__1cWThreadLocalAllocBufferEfill6MpnIHeapWord_2I_v_; +text: .text%__1cQjava_lang_StringPcreate_from_str6FpkcpnGThread__nGHandle__; +text: .text%__1cQjava_lang_StringTcreate_oop_from_str6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cKoopFactoryNnew_charArray6FpkcpnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cEUTF8Ounicode_length6Fpkc_i_; +text: .text%__1cEUTF8Enext6FpkcpH_pc_; +text: .text%__1cOtypeArrayKlassIallocate6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cQGenCollectedHeapVlarge_typearray_limit6M_I_; +text: .text%__1cbCTwoGenerationCollectorPolicyYis_two_generation_policy6M_i_: collectorPolicy.o; +text: .text%__1cbCTwoGenerationCollectorPolicyVlarge_typearray_limit6M_I_; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: typeArrayKlass.o; +text: .text%__1cEUTF8Sconvert_to_unicode6FpkcpHi_v_; +text: .text%__1cQjava_lang_StringQbasic_create_oop6FpnQtypeArrayOopDesc_ipnGThread__pnHoopDesc__; +text: .text%__1cNinstanceKlassRallocate_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cNCollectedHeapMobj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cTjava_lang_ThrowableLset_message6FpnHoopDesc_2_v_; +text: .text%__1cNinstanceKlassKlink_class6MpnGThread__v_; +text: .text%__1cNinstanceKlassPlink_class_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassLverify_code6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIVerifierRverify_byte_codes6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassWadd_loader_constraints6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassNrewrite_class6MpnGThread__v_; +text: .text%__1cIRewriterHrewrite6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIRewriterScompute_index_maps6FnSconstantPoolHandle_rpnIintArray_rpnIintStack__v_; +text: .text%__1cIintArray2t6Mki1_v_: rewriter.o; +text: .text%__1cIRewriterXnew_constant_pool_cache6FrnIintArray_pnGThread__nXconstantPoolCacheHandle__; +text: .text%__1cKoopFactoryVnew_constantPoolCache6FipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cWconstantPoolCacheKlassIallocate6MipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: cpCacheKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: cpCacheKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: cpCacheKlass.o; +text: .text%__1cYconstantPoolCacheOopDescKinitialize6MrnIintArray__v_; +text: .text%__1cWConstantPoolCacheEntryRset_initial_state6Mi_v_; +text: .text%__1cIRewriterOrewrite_method6FnMmethodHandle_rnIintArray_pnGThread__1_; +text: .text%__1cNmethodOopDescLlink_method6FnMmethodHandle__v_; +text: .text%__1cTAbstractInterpreterLmethod_kind6FnMmethodHandle__n0AKMethodKind__; +text: .text%__1cNmethodOopDescbGupdate_compiled_code_entry_point6Mi_v_; +text: .text%__1cIRuntime1Mientries_for6FnMmethodHandle__pnIiEntries__; +text: .text%__1cNmethodOopDescLis_accessor6kM_i_; +text: .text%__1cNmethodOopDescMintrinsic_id6kM_n0ALIntrinsicId__; +text: .text%__1cQSystemDictionaryXcheck_signature_loaders6FnMsymbolHandle_nGHandle_2ipnGThread__v_; +text: .text%__1cNSharedRuntimebIinitialize_StrictMath_entry_points6F_v_; +text: .text%__1cNSharedRuntimeUlookup_function_DD_D6FrpFpnHJNIEnv__pnH_jclass_dd_dpkc_v_; +text: .text%__1cMNativeLookupTbase_library_lookup6Fpkc22_pC_; +text: .text%__1cMNativeLookupGlookup6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cNmethodOopDescThas_native_function6kM_i_; +text: .text%__1cMNativeLookupLlookup_base6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cMNativeLookupNpure_jni_name6FnMmethodHandle__pc_; +text: .text%__1cMoutputStreamFprint6MpkcE_v_; +text: .text%__1cMoutputStreamMdo_vsnprintf6FpcIpkcpvirI_3_; +text: .text%__1cNmethodOopDescKklass_name6kM_pnNsymbolOopDesc__; +text: .text%__1cOmangle_name_on6FpnMoutputStream_pnNsymbolOopDesc__v_: nativeLookup.o; +text: .text%__1cOmangle_name_on6FpnMoutputStream_pnNsymbolOopDesc_ii_v_: nativeLookup.o; +text: .text%__1cMoutputStreamDput6Mc_v_; +text: .text%__1cMNativeLookupMlookup_style6FnMmethodHandle_pcpkciiripnGThread__pC_; +text: .text%__1cCosYprint_jni_name_prefix_on6FpnMoutputStream_i_v_; +text: .text%__1cCosYprint_jni_name_suffix_on6FpnMoutputStream_i_v_; +text: .text%__1cVlookup_special_native6Fpc_pC_: nativeLookup.o; +text: .text%__1cbEinitialize_converter_functions6F_v_; +text: .text%__1cIUniverseWupdate_heap_info_at_gc6F_v_; +text: .text%__1cQGenCollectedHeapIcapacity6kM_I_; +text: .text%__1cQDefNewGenerationIcapacity6kM_I_; +text: .text%__1cQGenCollectedHeapEused6kM_I_; +text: .text%__1cQDefNewGenerationEused6kM_I_; +text: .text%__1cbCOneContigSpaceCardGenerationEused6kM_I_; +text: .text%__1cQGenCollectedHeapPpost_initialize6M_v_; +text: .text%__1cQGenCollectedHeapTref_processing_init6M_v_; +text: .text%__1cKSharedHeapTref_processing_init6M_v_; +text: .text%__1cKGenerationSref_processor_init6M_v_; +text: .text%__1cKGenerationYrefs_discovery_is_atomic6kM_i_: compactingPermGenGen.o; +text: .text%__1cKGenerationUrefs_discovery_is_mt6kM_i_: compactingPermGenGen.o; +text: .text%__1cSReferenceProcessor2t6MnJMemRegion_iii_v_; +text: .text%__1cKGenerationYrefs_discovery_is_atomic6kM_i_: defNewGeneration.o; +text: .text%__1cKGenerationUrefs_discovery_is_mt6kM_i_: defNewGeneration.o; +text: .text%__1cKGenerationYrefs_discovery_is_atomic6kM_i_: tenuredGeneration.o; +text: .text%__1cKGenerationUrefs_discovery_is_mt6kM_i_: tenuredGeneration.o; +text: .text%__1cNMemoryServiceRset_universe_heap6FpnNCollectedHeap__v_; +text: .text%__1cQGenCollectedHeapEkind6M_nNCollectedHeapEName__: genCollectedHeap.o; +text: .text%__1cNMemoryServicebBadd_gen_collected_heap_info6FpnQGenCollectedHeap__v_; +text: .text%__1cPMarkSweepPolicyUis_mark_sweep_policy6M_i_: collectorPolicy.o; +text: .text%__1cNMemoryManagerXget_copy_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cPGCMemoryManager2t6M_v_; +text: .text%__1cNMemoryManagerWget_msc_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cNMemoryServicebAadd_generation_memory_pool6FpnKGeneration_pnNMemoryManager_4_v_; +text: .text%__1cQDefNewGenerationEkind6M_nKGenerationEName__: defNewGeneration.o; +text: .text%__1cNMemoryServiceJadd_space6FpnPContiguousSpace_pkciIi_pnKMemoryPool__; +text: .text%__1cTContiguousSpacePool2t6MpnPContiguousSpace_pkcnKMemoryPoolIPoolType_Ii_v_; +text: .text%__1cNMemoryServiceTadd_survivor_spaces6FpnQDefNewGeneration_pkciIi_pnKMemoryPool__; +text: .text%__1cbBSurvivorContiguousSpacePool2t6MpnQDefNewGeneration_pkcnKMemoryPoolIPoolType_Ii_v_; +text: .text%__1cRTenuredGenerationEkind6M_nKGenerationEName__: tenuredGeneration.o; +text: .text%__1cNMemoryServiceHadd_gen6FpnKGeneration_pkcii_pnKMemoryPool__; +text: .text%__1cOGenerationPool2t6MpnKGeneration_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cKGenerationMmax_capacity6kM_I_; +text: .text%__1cNMemoryServicebGadd_compact_perm_gen_memory_pool6FpnUCompactingPermGenGen_pnNMemoryManager__v_; +text: .text%__1cQGenCollectedHeapNgc_threads_do6kMpnNThreadClosure__v_; +text: .text%__1cPGCMemoryManagerXinitialize_gc_stat_info6M_v_; +text: .text%__1cKGCStatInfo2t6Mi_v_; +text: .text%__1cQjavaClasses_init6F_v_; +text: .text%__1cLJavaClassesPcompute_offsets6F_v_; +text: .text%__1cQjava_lang_SystemPcompute_offsets6F_v_; +text: .text%__1cQjava_lang_ThreadPcompute_offsets6F_v_; +text: .text%__1cNinstanceKlassQfind_local_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__i_; +text: .text%__1cVjava_lang_ThreadGroupPcompute_offsets6F_v_; +text: .text%__1cbIjava_security_AccessControlContextPcompute_offsets6F_v_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectPcompute_offsets6F_v_; +text: .text%__1cYjava_lang_reflect_MethodPcompute_offsets6F_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldPcompute_offsets6F_v_; +text: .text%__1cPjava_nio_BufferPcompute_offsets6F_v_; +text: .text%__1cYsun_reflect_ConstantPoolPcompute_offsets6F_v_; +text: .text%__1cZsun_misc_AtomicLongCSImplPcompute_offsets6F_v_; +text: .text%__1cSstubRoutines_init26F_v_; +text: .text%__1cMStubRoutinesLinitialize26F_v_; +text: .text%__1cNStubGeneratorYgenerate_throw_exception6MpkcpCi_3_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorTgenerate_verify_oop6M_pC_: stubGenerator_i486.o; +text: .text%__1cJAssemblerEincl6MnHAddress__v_; +text: .text%__1cHThreadsDadd6FpnKJavaThread_i_v_; +text: .text%__1cNThreadServiceKadd_thread6FpnKJavaThread_i_v_; +text: .text%__1cGThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cGThreadVis_jvmti_agent_thread6kM_i_: thread.o; +text: .text%__1cGEventsDlog6FpkcE_v_: thread.o; +text: .text%__1cLJvmtiExportbMtransition_pending_onload_raw_monitors6F_v_; +text: .text%__1cUJvmtiPendingMonitorsXtransition_raw_monitors6F_v_; +text: .text%__1cUGenericGrowableArrayUclear_and_deallocate6M_v_; +text: .text%__1cIVMThreadGcreate6F_v_; +text: .text%__1cIVMThread2t6M_v_; +text: .text%__1cQVMOperationQdDueue2t6M_v_; +text: .text%__1cCosNcreate_thread6FpnGThread_n0AKThreadType_I_i_; +text: .text%__1cCosMstart_thread6FpnGThread__v_; +text: .text%__1cCosPpd_start_thread6FpnGThread__v_; +text: .text%__1cHMonitorEwait6Mil_i_; +text: .text%_start: os_solaris.o; +text: .text%__1cCosTset_native_priority6FpnGThread_i_nIOSReturn__; +text: .text%__1cQset_lwp_priority6Fiii_i_; +text: .text%__1cVscale_to_lwp_priority6Fiii_i_: os_solaris.o; +text: .text%__1cIVMThreadMis_VM_thread6kM_i_: vmThread.o; +text: .text%__1cIVMThreadDrun6M_v_; +text: .text%__1cHMonitorGnotify6M_i_; +text: .text%__1cIVMThreadEloop6M_v_; +text: .text%__1cQVMOperationQdDueueLremove_next6M_pnMVM_Operation__; +text: .text%__1cQVMOperationQdDueueLqueue_empty6Mi_i_; +text: .text%__1cQVMOperationQdDueueSqueue_remove_front6Mi_pnMVM_Operation__; +text: .text%__1cLJvmtiExportRenter_start_phase6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_start6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_start6F_v_; +text: .text%__1cQinitialize_class6FnMsymbolHandle_pnGThread__v_: thread.o; +text: .text%__1cNinstanceKlassKinitialize6MpnGThread__v_; +text: .text%__1cNinstanceKlassVshould_be_initialized6kM_i_; +text: .text%__1cNinstanceKlassPinitialize_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassWcall_class_initializer6MpnGThread__v_; +text: .text%__1cNinstanceKlassbBcall_class_initializer_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassRclass_initializer6M_pnNmethodOopDesc__; +text: .text%__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_; +text: .text%__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cRCompilationPolicyOmustBeCompiled6FnMmethodHandle__i_; +text: .text%__1cRCompilationPolicyNcanBeCompiled6FnMmethodHandle__i_; +text: .text%__1cNmethodOopDescRis_not_compilable6kMi_i_; +text: .text%__1cRruntime_type_from6FpnJJavaValue__nJBasicType__: javaCalls.o; +text: .text%__1cCosbCstack_shadow_pages_available6FpnGThread_nMmethodHandle__i_; +text: .text%__1cTAbstractInterpreterbFsize_top_interpreter_activation6FpnNmethodOopDesc__i_; +text: .text%__1cPJavaCallWrapper2t6MnMmethodHandle_nGHandle_pnJJavaValue_pnGThread__v_; +text: .text%__1cGThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%__1cGThreadXclear_pending_exception6M_v_; +text: .text%__1cRJavaCallArgumentsKparameters6M_pi_; +text: .text%__1cSInterpreterRuntimeOresolve_invoke6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cKJavaThreadPcook_last_frame6MnFframe__1_; +text: .text%__1cFframeYinterpreter_frame_method6kM_pnNmethodOopDesc__; +text: .text%__1cFframeVinterpreter_frame_bcp6kM_pC_; +text: .text%__1cMLinkResolverOresolve_invoke6FrnICallInfo_nGHandle_nSconstantPoolHandle_inJBytecodesECode_pnGThread__v_; +text: .text%__1cMLinkResolverUresolve_invokestatic6FrnICallInfo_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverMresolve_pool6FrnLKlassHandle_rnMsymbolHandle_42nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverNresolve_klass6FrnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cTconstantPoolOopDescMklass_ref_at6MipnGThread__pnMklassOopDesc__; +text: .text%__1cTconstantPoolOopDescNklass_at_impl6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceKlass.o; +text: .text%__1cTconstantPoolOopDescbCverify_constant_pool_resolve6FnSconstantPoolHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cMLinkResolverZcheck_klass_accessability6FnLKlassHandle_1pnGThread__v_; +text: .text%__1cTconstantPoolOopDescMklass_at_put6MipnMklassOopDesc__v_: constantPoolOop.o; +text: .text%__1cTconstantPoolOopDescLname_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cTconstantPoolOopDescQsignature_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cMLinkResolverTresolve_static_call6FrnICallInfo_rnLKlassHandle_nMsymbolHandle_53iipnGThread__v_; +text: .text%__1cMLinkResolverbElinktime_resolve_static_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverYlookup_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cMLinkResolverbAcheck_method_accessability6FnLKlassHandle_11nMmethodHandle_pnGThread__v_; +text: .text%__1cKReflectionTverify_field_access6FpnMklassOopDesc_22nLAccessFlags_ii_i_; +text: .text%__1cICallInfoDset6MnLKlassHandle_nMmethodHandle_pnGThread__v_; +text: .text%__1cICallInfoDset6MnLKlassHandle_1nMmethodHandle_2ipnGThread__v_; +text: .text%__1cSInterpreterRuntimeLcache_entry6FpnKJavaThread__pnWConstantPoolCacheEntry__: interpreterRuntime.o; +text: .text%__1cWConstantPoolCacheEntryLis_resolved6kMnJBytecodesECode__i_: interpreterRuntime.o; +text: .text%__1cWConstantPoolCacheEntryPbytecode_number6FnJBytecodesECode__i_: interpreterRuntime.o; +text: .text%__1cWConstantPoolCacheEntryKset_method6MnJBytecodesECode_nMmethodHandle_i_v_; +text: .text%__1cNmethodOopDescLresult_type6kM_nJBasicType__; +text: .text%__1cRSignatureIteratorSiterate_returntype6M_v_; +text: .text%__1cNSignatureInfoHdo_void6M_v_: bytecode.o; +text: .text%__1cQResultTypeFinderDset6MinJBasicType__v_: bytecode.o; +text: .text%__1cRSignatureIteratorTcheck_signature_end6M_v_; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: cpCacheOop.o; +text: .text%__1cNmethodOopDescPis_final_method6kM_i_; +text: .text%__1cWConstantPoolCacheEntryIas_flags6MnITosState_iiiii_i_; +text: .text%__1cWConstantPoolCacheEntryOset_bytecode_16MnJBytecodesECode__v_; +text: .text%__1cWConstantPoolCacheEntryGverify6kMpnMoutputStream__v_; +text: .text%__1cSInterpreterRuntimeTprepare_native_call6FpnKJavaThread_pnNmethodOopDesc__v_; +text: .text%__1cXSignatureHandlerLibraryDadd6FnMmethodHandle__v_; +text: .text%__1cXSignatureHandlerLibraryKinitialize6F_v_; +text: .text%__1cXSignatureHandlerLibraryQset_handler_blob6F_pC_; +text: .text%__1cRSignatureIterator2t6MpnNsymbolOopDesc__v_; +text: .text%__1cNFingerprinterLfingerprint6M_X_: interpreterRuntime.o; +text: .text%__1cNGrowableArray4CX_Efind6kMkX_i_: interpreterRuntime.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIgenerate6MX_v_; +text: .text%__1cRSignatureIteratorSiterate_parameters6MX_v_; +text: .text%__1cXSignatureHandlerLibraryLset_handler6FpnKCodeBuffer__pC_; +text: .text%__1cXSignatureHandlerLibraryOpd_set_handler6FpC_v_; +text: .text%jni_RegisterNatives: jni.o; +text: .text%__1cPjava_lang_ClassLas_klassOop6FpnHoopDesc__pnMklassOopDesc__; +text: .text%__1cLSymbolTableFprobe6Fpkci_pnNsymbolOopDesc__; +text: .text%__1cPregister_native6FnLKlassHandle_nMsymbolHandle_1pCpnGThread__i_: jni.o; +text: .text%__1cPJavaCallWrapper2T6M_v_; +text: .text%__1cOJNIHandleBlockNrelease_block6Fp0pnGThread__v_; +text: .text%__1cNinstanceKlassbJset_initialization_state_and_notify6Mn0AKClassState_pnGThread__v_; +text: .text%__1cNinstanceKlassbOset_initialization_state_and_notify_impl6FnTinstanceKlassHandle_n0AKClassState_pnGThread__v_; +text: .text%__1cSObjectSynchronizerJnotifyall6FnGHandle_pnGThread__v_; +text: .text%__1cSInterpreterRuntimeJanewarray6FpnKJavaThread_pnTconstantPoolOopDesc_ii_v_; +text: .text%__1cSInterpreterRuntimePresolve_get_put6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_ipnGThread__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_iipnGThread__v_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cMLinkResolverZcheck_field_accessability6FnLKlassHandle_11rnPfieldDescriptor_pnGThread__v_; +text: .text%__1cPFieldAccessInfoDset6MnLKlassHandle_nMsymbolHandle_iinJBasicType_nLAccessFlags__v_; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: interpreterRuntime.o; +text: .text%__1cWConstantPoolCacheEntryJset_field6MnJBytecodesECode_2nLKlassHandle_iinITosState_ii_v_; +text: .text%__1cWConstantPoolCacheEntryOset_bytecode_26MnJBytecodesECode__v_; +text: .text%__1cSInterpreterRuntimeE_new6FpnKJavaThread_pnTconstantPoolOopDesc_i_v_; +text: .text%__1cNinstanceKlassbDcheck_valid_for_instantiation6MipnGThread__v_; +text: .text%__1cMLinkResolverVresolve_invokespecial6FrnICallInfo_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverUresolve_special_call6FrnICallInfo_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverbFlinktime_resolve_special_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_special_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_4ipnGThread__v_; +text: .text%__1cWConstantPoolCacheEntryLis_resolved6kMnJBytecodesECode__i_: cpCacheOop.o; +text: .text%__1cWConstantPoolCacheEntryPbytecode_number6FnJBytecodesECode__i_: cpCacheOop.o; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: bytecode.o; +text: .text%__1cNSignatureInfoHdo_long6M_v_: bytecode.o; +text: .text%JVM_CurrentTimeMillis; +text: .text%__1cbBcreate_initial_thread_group6FpnGThread__nGHandle__: thread.o; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cSInterpreterRuntimeDldc6FpnKJavaThread_i_v_; +text: .text%__1cTconstantPoolOopDescOstring_at_impl6FnSconstantPoolHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cLsymbolKlassNoop_is_symbol6kM_i_: symbolKlass.o; +text: .text%__1cLStringTableGintern6FpnNsymbolOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cNsymbolOopDescKas_unicode6kMri_pH_; +text: .text%__1cEUTF8Ounicode_length6Fpkci_i_; +text: .text%__1cLStringTableGintern6FnGHandle_pHipnGThread__pnHoopDesc__; +text: .text%__1cLStringTableLhash_string6FpHi_i_; +text: .text%__1cLStringTableGlookup6MipHiI_pnHoopDesc__; +text: .text%__1cLStringTableJbasic_add6MinGHandle_pHiIpnGThread__pnHoopDesc__; +text: .text%__1cQjava_lang_StringbBcreate_tenured_from_unicode6FpHipnGThread__nGHandle__; +text: .text%__1cKoopFactoryXnew_permanent_charArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cQjava_lang_StringMbasic_create6FpnQtypeArrayOopDesc_ipnGThread__nGHandle__; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cNmethodOopDescIbci_from6kMpC_i_; +text: .text%__1cPBytecode_invokeJsignature6kM_pnNsymbolOopDesc__; +text: .text%__1cPBytecode_invokeFindex6kM_i_; +text: .text%__1cNmethodOopDescIbcp_from6kMi_pC_; +text: .text%__1cFframebGinterpreter_callee_receiver_addr6MnMsymbolHandle__ppnHoopDesc__; +text: .text%__1cMLinkResolverVresolve_invokevirtual6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverUresolve_virtual_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cMLinkResolverbFlinktime_resolve_virtual_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_virtual_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cFKlassXcan_be_statically_bound6FpnNmethodOopDesc__i_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: bytecode.o; +text: .text%__1cNSignatureInfoHdo_char6M_v_: bytecode.o; +text: .text%__1cFKlassOis_subclass_of6kMpnMklassOopDesc__i_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnHoopDesc_pnNsymbolOopDesc__i_; +text: .text%__1cJBytecodesRspecial_length_at6FpC_i_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_5pnGThread__v_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cLklassVtableIindex_of6kMpnNmethodOopDesc_i_i_; +text: .text%__1cMNativeLookupNlong_jni_name6FnMmethodHandle__pc_; +text: .text%__1cNFingerprinterJdo_object6Mii_v_: dump.o; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_: interpreterRuntime.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_object6M_v_: interpreterRuntime.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorDbox6Mii_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEfrom6F_pnMRegisterImpl__; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorCto6F_pnMRegisterImpl__; +text: .text%JVM_DoPrivileged; +text: .text%__1cLmethodKlassNoop_is_method6kM_i_: methodKlass.o; +text: .text%__1cMvframeStream2t6MpnKJavaThread_i_v_; +text: .text%__1cLRegisterMap2t6MpnKJavaThread_i_v_; +text: .text%__1cLRegisterMapFclear6Mpi_v_; +text: .text%__1cSvframeStreamCommonPfill_from_frame6M_i_; +text: .text%__1cFframeUis_interpreted_frame6kM_i_; +text: .text%__1cSvframeStreamCommonbBfill_from_interpreter_frame6M_v_; +text: .text%__1cSvframeStreamCommonZsecurity_get_caller_frame6Mi_v_; +text: .text%__1cSvframeStreamCommonbHskip_method_invoke_and_aux_frames6M_v_; +text: .text%__1cSvframeStreamCommonEnext6M_v_; +text: .text%__1cFframeGsender6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cFframeOis_entry_frame6kM_i_; +text: .text%__1cRPrivilegedElementKinitialize6MpnMvframeStream_pnHoopDesc_p0pnGThread__v_; +text: .text%__1cKJNIHandlesKmake_local6FpnHJNIEnv__pnHoopDesc__pnI_jobject__; +text: .text%__1cOJNIHandleBlockPallocate_handle6MpnHoopDesc__pnI_jobject__; +text: .text%__1cSInterpreterRuntimeNquicken_io_cc6FpnKJavaThread__v_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: bytecode.o; +text: .text%__1cNSharedRuntimeDf2i6Ff_i_; +text: .text%jni_FindClass: jni.o; +text: .text%__1cKJavaThreadZsecurity_get_caller_class6Mi_pnMklassOopDesc__; +text: .text%__1cbCfind_class_from_class_loader6FpnHJNIEnv__nMsymbolHandle_CnGHandle_3CpnGThread__pnH_jclass__; +text: .text%__1cRCompilationPolicyUcompleted_vm_startup6F_v_; +text: .text%jni_NewGlobalRef: jni.o; +text: .text%__1cKJNIHandlesLmake_global6FnGHandle_i_pnI_jobject__; +text: .text%jni_GetStringUTFChars: jni.o; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc__pc_; +text: .text%__1cHUNICODEHas_utf86FpHi_pc_; +text: .text%__1cHUNICODELutf8_length6FpHi_i_; +text: .text%__1cKutf8_write6FpCH_0_: utf8.o; +text: .text%JVM_FindPrimitiveClass; +text: .text%__1cJname2type6Fpkc_nJBasicType__; +text: .text%jni_ReleaseStringUTFChars; +text: .text%__1cVcreate_initial_thread6FnGHandle_pnKJavaThread_pnGThread__pnHoopDesc__: thread.o; +text: .text%__1cQjava_lang_ThreadKset_thread6FpnHoopDesc_pnKJavaThread__v_; +text: .text%__1cQjava_lang_ThreadMset_priority6FpnHoopDesc_nOThreadPriority__v_; +text: .text%JVM_CurrentThread; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: bytecode.o; +text: .text%__1cSInterpreterRuntimeInewarray6FpnKJavaThread_nJBasicType_i_v_; +text: .text%__1cKoopFactoryNnew_typeArray6FnJBasicType_ipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNFingerprinterGdo_int6M_v_: dump.o; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_: interpreterRuntime.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIpass_int6M_v_: interpreterRuntime.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEmove6Mii_v_; +text: .text%JVM_ArrayCopy; +text: .text%__1cOtypeArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cOtypeArrayKlassQoop_is_typeArray6kM_i_: typeArrayKlass.o; +text: .text%JVM_GetStackAccessControlContext; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2t6M_v_; +text: .text%__1cJCodeCacheJfind_blob6Fpv_pnICodeBlob__; +text: .text%__1cJCodeCacheQfind_blob_unsafe6Fpv_pnICodeBlob__; +text: .text%__1cICodeHeapKfind_start6kMpv_1_; +text: .text%__1cICodeBlobJis_zombie6kM_i_: codeBlob.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: codeBlob.o; +text: .text%__1cFframeOis_first_frame6kM_i_; +text: .text%__1cFframeUentry_frame_is_first6kM_i_; +text: .text%__1cbIjava_security_AccessControlContextGcreate6FnOobjArrayHandle_inGHandle_pnGThread__pnHoopDesc__; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2T6M_v_; +text: .text%__1cTJvmtiEventCollectorYunset_jvmti_thread_state6M_v_; +text: .text%JVM_GetInheritedAccessControlContext; +text: .text%__1cQjava_lang_ThreadbGinherited_access_control_context6FpnHoopDesc__2_; +text: .text%JVM_SetThreadPriority; +text: .text%__1cQjava_lang_ThreadGthread6FpnHoopDesc__pnKJavaThread__; +text: .text%__1cGThreadMset_priority6Fp0nOThreadPriority__v_; +text: .text%__1cCosMset_priority6FpnGThread_nOThreadPriority__nIOSReturn__; +text: .text%__1cQjava_lang_ThreadRset_thread_status6FpnHoopDesc_n0AMThreadStatus__v_; +text: .text%JVM_IsThreadAlive; +text: .text%__1cQjava_lang_ThreadIis_alive6FpnHoopDesc__i_; +text: .text%JVM_StartThread; +text: .text%__1cQjava_lang_ThreadMis_stillborn6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_ThreadJstackSize6FpnHoopDesc__x_; +text: .text%__1cKJavaThread2t6MpFp0pnGThread__vI_v_; +text: .text%__1cKJavaThreadHprepare6MpnI_jobject_nOThreadPriority__v_; +text: .text%__1cQjava_lang_ThreadIpriority6FpnHoopDesc__nOThreadPriority__; +text: .text%__1cQjava_lang_ThreadJis_daemon6FpnHoopDesc__i_; +text: .text%__1cGThreadFstart6Fp0_v_; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceRefKlass.o; +text: .text%__1cbAcall_initializeSystemClass6FpnGThread__v_: thread.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cKJavaThreadDrun6M_v_; +text: .text%__1cKJavaThreadRthread_main_inner6M_v_; +text: .text%__1cMthread_entry6FpnKJavaThread_pnGThread__v_: jvm.o; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cIBytecodeIset_code6MnJBytecodesECode__v_; +text: .text%__1cNFingerprinterHdo_long6M_v_: dump.o; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: interpreterRuntime.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorJpass_long6M_v_: interpreterRuntime.o; +text: .text%JVM_MonitorWait; +text: .text%__1cQjava_lang_ThreadRget_thread_status6FpnHoopDesc__n0AMThreadStatus__; +text: .text%__1cSObjectSynchronizerEwait6FnGHandle_xpnGThread__v_; +text: .text%__1cSObjectSynchronizerHinflate6FpnHoopDesc__pnNObjectMonitor__; +text: .text%__1cNObjectMonitor2t6M_v_; +text: .text%__1cNObjectMonitorHRecycle6M_v_; +text: .text%__1cNObjectMonitorEwait6MxipnGThread__v_; +text: .text%__1cGThreadOis_interrupted6Fp0i_i_; +text: .text%__1cCosOis_interrupted6FpnGThread_i_i_; +text: .text%__1cNObjectMonitorEexit6MpnGThread__v_; +text: .text%__1cCosHSolarisFEventEpark6M_v_: objectMonitor_solaris.o; +text: .text%jni_GetObjectClass: jni.o; +text: .text%jni_GetMethodID: jni.o; +text: .text%__1cNget_method_id6FpnHJNIEnv__pnH_jclass_pkc5ipnGThread__pnK_jmethodID__: jni.o; +text: .text%__1cPjava_lang_ClassMis_primitive6FpnHoopDesc__i_; +text: .text%__1cNmethodOopDescKjmethod_id6M_pnK_jmethodID__; +text: .text%__1cMjniIdSupportNto_jmethod_id6FpnNmethodOopDesc__pnK_jmethodID__; +text: .text%__1cMjniIdPrivateGid_for6FnTinstanceKlassHandle_i_i_: jniId.o; +text: .text%__1cIjniIdMapGcreate6FnTinstanceKlassHandle__p0_; +text: .text%__1cIjniIdMapRcompute_index_cnt6FnTinstanceKlassHandle__i_; +text: .text%__1cIjniIdMap2t6MpnMklassOopDesc_i_v_; +text: .text%__1cLjniIdBucket2t6MpnIjniIdMap_p0_v_; +text: .text%jni_NewStringUTF: jni.o; +text: .text%jni_CallObjectMethod: jni.o; +text: .text%__1cMjniIdSupportNto_method_oop6FpnK_jmethodID__pnNmethodOopDesc__; +text: .text%__1cRSignatureIterator2t6MpnGThread_pnNsymbolOopDesc__v_; +text: .text%__1cUjni_invoke_nonstatic6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cNFingerprinterLfingerprint6M_X_: jni.o; +text: .text%__1cXJNI_ArgumentPusherVaArgHiterate6MX_v_: jni.o; +text: .text%__1cXJNI_ArgumentPusherVaArgKget_object6M_v_: jni.o; +text: .text%jni_ExceptionOccurred: jni.o; +text: .text%__1cbAjni_check_async_exceptions6FpnKJavaThread__v_: jni.o; +text: .text%__1cKJavaThreadbHcheck_and_handle_async_exceptions6Mi_v_; +text: .text%jni_DeleteLocalRef: jni.o; +text: .text%__1cOJNIHandleBlockRrebuild_free_list6M_v_; +text: .text%jni_EnsureLocalCapacity; +text: .text%jni_GetStaticMethodID: jni.o; +text: .text%jni_CallStaticObjectMethodV: jni.o; +text: .text%__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cMLinkResolverbHlookup_instance_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%jni_ExceptionCheck: jni.o; +text: .text%jni_NewString: jni.o; +text: .text%__1cQjava_lang_StringXcreate_oop_from_unicode6FpHipnGThread__pnHoopDesc__; +text: .text%JVM_InitProperties; +text: .text%__1cMset_property6FnGHandle_pkc2pnGThread__v_: jvm.o; +text: .text%__1cQjava_lang_StringbHcreate_from_platform_depended_str6FpkcpnGThread__nGHandle__; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cKSharedHeapXfill_region_with_object6FnJMemRegion__v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2t6M_v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: sharedHeap.o; +text: .text%__1cYNoJvmtiVMObjectAllocMark2T6M_v_; +text: .text%__1cPfieldDescriptorUstring_initial_value6kMpnGThread__pnHoopDesc__; +text: .text%jni_GetFieldID: jni.o; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2ipnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassSregister_finalizer6FpnPinstanceOopDesc_pnGThread__2_; +text: .text%__1cJFieldTypeYis_valid_array_signature6FpnNsymbolOopDesc__i_; +text: .text%__1cQSystemDictionarybBresolve_array_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cJFieldTypeOget_array_info6FpnNsymbolOopDesc_pip2pnGThread__nJBasicType__; +text: .text%__1cJFieldTypeSskip_optional_size6FpnNsymbolOopDesc_pi_v_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6FnUtypeArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%JVM_RegisterUnsafeMethods; +text: .text%JVM_IsArrayClass; +text: .text%JVM_GetComponentType; +text: .text%__1cKReflectionUarray_component_type6FpnHoopDesc_pnGThread__2_; +text: .text%__1cKReflectionbFbasic_type_arrayklass_to_mirror6FpnMklassOopDesc_pnGThread__pnHoopDesc__; +text: .text%JVM_IsPrimitiveClass; +text: .text%JVM_GetClassLoader; +text: .text%JVM_DesiredAssertionStatus; +text: .text%__1cOJavaAssertionsHenabled6Fpkci_i_; +text: .text%__1cOJavaAssertionsLmatch_class6Fpkc_pn0AKOptionList__: javaAssertions.o; +text: .text%__1cOJavaAssertionsNmatch_package6Fpkc_pn0AKOptionList__; +text: .text%__1cNinstanceKlassUfind_interface_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%JVM_InternString; +text: .text%__1cLStringTableGintern6FpnHoopDesc_pnGThread__2_; +text: .text%__1cQjava_lang_StringRas_unicode_string6FpnHoopDesc_ri_pH_; +text: .text%__1cKSharedHeapPis_in_permanent6kMpkv_i_: genCollectedHeap.o; +text: .text%__1cQjava_lang_StringGequals6FpnHoopDesc_pHi_i_; +text: .text%JVM_NanoTime; +text: .text%__1cCosNjavaTimeNanos6F_x_; +text: .text%JVM_GetCallerClass; +text: .text%JVM_SupportsCX8; +text: .text%__1cNFingerprinterHdo_bool6M_v_: dump.o; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_: interpreterRuntime.o; +text: .text%JVM_GetClassDeclaredFields; +text: .text%__1cKReflectionJnew_field6FpnPfieldDescriptor_ipnGThread__pnHoopDesc__; +text: .text%__1cKReflectionInew_type6FnMsymbolHandle_nLKlassHandle_pnGThread__nGHandle__; +text: .text%__1cJvmSymbolsOsignature_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cXjava_lang_reflect_FieldGcreate6FpnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_name6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_type6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectMset_override6FpnHoopDesc_C_v_; +text: .text%__1cXjava_lang_reflect_FieldThas_signature_field6F_i_; +text: .text%__1cXjava_lang_reflect_FieldVhas_annotations_field6F_i_; +text: .text%__1cPfieldDescriptorLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cXjava_lang_reflect_FieldPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cMLinkResolverXresolve_invokeinterface6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverWresolve_interface_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cMLinkResolverbHlinktime_resolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverbGruntime_resolve_interface_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cICallInfoDset6MnLKlassHandle_1nMmethodHandle_2pnGThread__v_; +text: .text%__1cLklassItableUcompute_itable_index6FpnNmethodOopDesc__i_; +text: .text%__1cWConstantPoolCacheEntrySset_interface_call6MnMmethodHandle_i_v_; +text: .text%Unsafe_ObjectFieldOffset; +text: .text%__1cRfind_field_offset6FpnI_jobject_ipnGThread__i_; +text: .text%__1cXjava_lang_reflect_FieldFclazz6FpnHoopDesc__2_; +text: .text%__1cXjava_lang_reflect_FieldEslot6FpnHoopDesc__i_; +text: .text%__1cXjava_lang_reflect_FieldJmodifiers6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringScreate_from_symbol6FnMsymbolHandle_pnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldNset_signature6FpnHoopDesc_2_v_; +text: .text%JVM_IHashCode; +text: .text%jni_GetStaticFieldID: jni.o; +text: .text%__1cNinstanceKlassKjni_id_for6Mi_pnFJNIid__; +text: .text%__1cNinstanceKlassPjni_id_for_impl6FnTinstanceKlassHandle_i_pnFJNIid__; +text: .text%__1cFJNIid2t6MpnMklassOopDesc_ip0_v_; +text: .text%jni_SetStaticObjectField: jni.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceRefKlass.o; +text: .text%__1cNobjArrayKlassPoop_is_objArray6kM_i_: objArrayKlass.o; +text: .text%__1cKarrayKlassTallocate_arrayArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlass.o; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: arrayKlass.o; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: arrayKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: arrayKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: arrayKlass.o; +text: .text%jni_GetStringUTFLength: jni.o; +text: .text%__1cQjava_lang_StringLutf8_length6FpnHoopDesc__i_; +text: .text%jni_GetStringLength: jni.o; +text: .text%__1cQjava_lang_StringGlength6FpnHoopDesc__i_; +text: .text%jni_GetStringUTFRegion: jni.o; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc_ii_pc_; +text: .text%JVM_FindClassFromClassLoader; +text: .text%JVM_IsInterface; +text: .text%JVM_GetClassDeclaredConstructors; +text: .text%__1cNmethodOopDescOis_initializer6kM_i_; +text: .text%__1cKReflectionPnew_constructor6FnMmethodHandle_pnGThread__pnHoopDesc__; +text: .text%__1cNinstanceKlassQmethod_index_for6kMpnNmethodOopDesc_pnGThread__i_; +text: .text%__1cKReflectionTget_parameter_types6FnMmethodHandle_ippnHoopDesc_pnGThread__nOobjArrayHandle__; +text: .text%__1cPSignatureStream2t6MnMsymbolHandle_i_v_; +text: .text%__1cPSignatureStreamEnext6M_v_; +text: .text%__1cKReflectionTget_exception_types6FnMmethodHandle_pnGThread__nOobjArrayHandle__; +text: .text%__1cNmethodOopDescbGresolved_checked_exceptions_impl6Fp0pnGThread__nOobjArrayHandle__; +text: .text%__1cSconstMethodOopDescZchecked_exceptions_length6kM_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorGcreate6FpnGThread__nGHandle__; +text: .text%__1cbDjava_lang_reflect_ConstructorJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorThas_signature_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorVhas_annotations_field6F_i_; +text: .text%__1cNmethodOopDescLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cbDjava_lang_reflect_ConstructorPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorbFhas_parameter_annotations_field6F_i_; +text: .text%__1cNmethodOopDescVparameter_annotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cbDjava_lang_reflect_ConstructorZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cKarrayKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%JVM_Clone; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: jvm.o; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: jvm.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: jvm.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: jvm.o; +text: .text%__1cRCardTableModRefBSPdirty_MemRegion6MnJMemRegion__v_; +text: .text%JVM_GetClassAccessFlags; +text: .text%JVM_GetClassName; +text: .text%__1cFKlassNexternal_name6kM_pkc_; +text: .text%__1cNsymbolOopDescWas_klass_external_name6kM_pkc_; +text: .text%__1cLStringTableGintern6FpkcpnGThread__pnHoopDesc__; +text: .text%JVM_GetClassModifiers; +text: .text%jni_GetSuperclass: jni.o; +text: .text%__1cKJNIHandlesKmake_local6FpnHoopDesc__pnI_jobject__; +text: .text%__1cNFingerprinterIdo_array6Mii_v_: dump.o; +text: .text%JVM_NewInstanceFromConstructor; +text: .text%__1cKReflectionSinvoke_constructor6FpnHoopDesc_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cbDjava_lang_reflect_ConstructorFclazz6FpnHoopDesc__2_; +text: .text%__1cbDjava_lang_reflect_ConstructorEslot6FpnHoopDesc__i_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectIoverride6FpnHoopDesc__C_; +text: .text%__1cbDjava_lang_reflect_ConstructorPparameter_types6FpnHoopDesc__2_; +text: .text%__1cKReflectionGinvoke6FnTinstanceKlassHandle_nMmethodHandle_nGHandle_inOobjArrayHandle_nJBasicType_4ipnGThread__pnHoopDesc__; +text: .text%__1cKReflectionDbox6FpnGjvalue_nJBasicType_pnGThread__pnHoopDesc__; +text: .text%JVM_MaxMemory; +text: .text%__1cQGenCollectedHeapMmax_capacity6kM_I_; +text: .text%__1cQDefNewGenerationMmax_capacity6kM_I_; +text: .text%Unsafe_AllocateMemory; +text: .text%Unsafe_SetNativeLong; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: bytecode.o; +text: .text%Unsafe_GetNativeByte; +text: .text%Unsafe_FreeMemory; +text: .text%__1cNSignatureInfoIdo_float6M_v_: bytecode.o; +text: .text%__1cFJNIidEfind6Mi_p0_; +text: .text%jni_NewObjectV: jni.o; +text: .text%__1cMalloc_object6FpnH_jclass_pnGThread__pnPinstanceOopDesc__: jni.o; +text: .text%jni_GetStringRegion: jni.o; +text: .text%__1cQjava_lang_StringGoffset6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringFvalue6FpnHoopDesc__pnQtypeArrayOopDesc__; +text: .text%jni_GetObjectField: jni.o; +text: .text%jni_GetStringCritical: jni.o; +text: .text%__1cJGC_lockerNlock_critical6FpnKJavaThread__v_: jni.o; +text: .text%jni_ReleaseStringCritical: jni.o; +text: .text%JVM_LoadLibrary; +text: .text%JVM_FindLibraryEntry; +text: .text%jni_GetJavaVM; +text: .text%JVM_IsSupportedJNIVersion; +text: .text%jni_SetIntField: jni.o; +text: .text%jni_SetLongField: jni.o; +text: .text%JVM_FindSignal; +text: .text%JVM_RegisterSignal; +text: .text%__1cCosMuser_handler6F_pv_; +text: .text%__1cCosGsignal6Fipv_1_; +text: .text%__1cWreset_vm_info_property6FpnGThread__v_: thread.o; +text: .text%__1cVquicken_jni_functions6F_v_; +text: .text%__1cQJNI_FastGetFieldbFgenerate_fast_get_boolean_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_int_field06FnJBasicType__pC_; +text: .text%__1cJAssemblerFtestb6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerMemit_arith_b6MiipnMRegisterImpl_i_v_; +text: .text%__1cYjni_GetBooleanField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_byte_field6F_pC_; +text: .text%__1cVjni_GetByteField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_char_field6F_pC_; +text: .text%__1cVjni_GetCharField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_short_field6F_pC_; +text: .text%__1cWjni_GetShortField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbBgenerate_fast_get_int_field6F_pC_; +text: .text%__1cUjni_GetIntField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_long_field6F_pC_; +text: .text%__1cVjni_GetLongField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_float_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_float_field06FnJBasicType__pC_; +text: .text%__1cJAssemblerFfst_s6MnHAddress__v_; +text: .text%__1cJAssemblerGfstp_d6Mi_v_; +text: .text%__1cWjni_GetFloatField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_double_field6F_pC_; +text: .text%__1cXjni_GetDoubleField_addr6F_pC_; +text: .text%__1cSset_init_completed6F_v_; +text: .text%__1cJTimeStampGupdate6M_v_; +text: .text%__1cQSystemDictionarybAcompute_java_system_loader6FpnGThread__v_; +text: .text%jni_NewObjectArray: jni.o; +text: .text%__1cNobjArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cNobjArrayKlassIallocate6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: objArrayKlass.o; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: objArrayKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: objArrayKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: objArrayKlass.o; +text: .text%jni_SetObjectArrayElement: jni.o; +text: .text%jni_GetObjectArrayElement: jni.o; +text: .text%__1cSInterpreterRuntimebAfrequency_counter_overflow6FpnKJavaThread_pC_x_; +text: .text%__1cQSimpleCompPolicyXmethod_invocation_event6MnMmethodHandle_pnGThread__v_; +text: .text%__1cRCompilationPolicybIreset_counter_for_invocation_event6MnMmethodHandle__v_; +text: .text%__1cRInvocationCounterJset_carry6M_v_; +text: .text%__1cNCompileBrokerOcompile_method6FnMmethodHandle_i1ipkcpnGThread__pnHnmethod__; +text: .text%__1cQSimpleCompPolicyRcompilation_level6MnMmethodHandle_i_i_; +text: .text%__1cNCompileBrokerTcompile_method_base6FnMmethodHandle_ii1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNobjArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cLJvmtiExportQenter_live_phase6F_v_; +text: .text%__1cLJvmtiExportTpost_vm_initialized6F_v_; +text: .text%__1cUJvmtiEventControllerHvm_init6F_v_; +text: .text%__1cHMonitorKnotify_all6M_i_; +text: .text%__1cFChunkbDstart_chunk_pool_cleaner_task6F_v_; +text: .text%__1cMPeriodicTask2t6MI_v_; +text: .text%__1cMPeriodicTaskGenroll6M_v_; +text: .text%__1cURecompilationMonitorbGstart_recompilation_monitor_task6F_v_; +text: .text%__1cCosLsignal_init6F_v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_53pnGThread__v_; +text: .text%__1cCosOsignal_init_pd6F_v_; +text: .text%__1cQjava_lang_ThreadKset_daemon6FpnHoopDesc__v_; +text: .text%__1cICompiler2t6M_v_; +text: .text%__1cNCompileBrokerQcompilation_init6FpnQAbstractCompiler__v_; +text: .text%__1cNCompileBrokerVinit_compiler_threads6Fi_v_; +text: .text%__1cICompilerOneeds_adapters6M_i_: c1_Compiler.o; +text: .text%__1cQCompilerCounters2t6MpkcipnGThread__v_; +text: .text%__1cNCompileBrokerUmake_compiler_thread6FpkcpnMCompileQdDueue_pnQCompilerCounters_pnGThread__pnOCompilerThread__; +text: .text%__1cOCompilerThread2t6MpnMCompileQdDueue_pnQCompilerCounters__v_; +text: .text%__1cOCompilerThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cCosFyield6F_v_; +text: .text%__1cCosFsleep6FpnGThread_xi_i_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cTsignal_thread_entry6FpnKJavaThread_pnGThread__v_: os.o; +text: .text%__1cCosLsignal_wait6F_i_; +text: .text%__1cVcheck_pending_signals6Fi_i_: os_solaris.o; +text: .text%__1cVcompiler_thread_entry6FpnKJavaThread_pnGThread__v_: thread.o; +text: .text%__1cNCompileBrokerUcompiler_thread_loop6F_v_; +text: .text%__1cICompilerKinitialize6M_v_; +text: .text%__1cMCompileQdDueueDget6M_pnLCompileTask__; +text: .text%__1cKManagementKinitialize6FpnGThread__v_; +text: .text%__1cRLowMemoryDetectorKinitialize6F_v_; +text: .text%__1cXLowMemoryDetectorThreadbCis_hidden_from_external_view6kM_i_: lowMemoryDetector.o; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cLStatSamplerGengage6F_v_; +text: .text%__1cLStatSamplerKinitialize6F_v_; +text: .text%__1cLStatSamplerUcreate_misc_perfdata6F_v_; +text: .text%__1cCosRelapsed_frequency6F_x_; +text: .text%__1cLStatSamplerbMcreate_system_property_instrumentation6FpnGThread__v_; +text: .text%__1cLStatSamplerTget_system_property6FpkcpnGThread__2_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_pnGThread__v_; +text: .text%__1cLStatSamplerXcreate_sampled_perfdata6F_v_; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnPPerfLongCounter__; +text: .text%__1cSHighResTimeSamplerLtake_sample6M_x_: statSampler.o; +text: .text%__1cPPerfDataManagerHsampled6F_pnMPerfDataList__; +text: .text%__1cMPerfDataListFclone6M_p0_; +text: .text%__1cMPerfDataList2t6Mp0_v_; +text: .text%__1cUGenericGrowableArrayNraw_appendAll6Mpk0_v_; +text: .text%__1cNWatcherThreadFstart6F_v_; +text: .text%__1cNWatcherThread2t6M_v_; +text: .text%__1cJTimeStampMmilliseconds6kM_x_; +text: .text%__1cKManagementWrecord_vm_startup_time6Fxx_v_; +text: .text%__1cORuntimeServiceYrecord_application_start6F_v_; +text: .text%__1cNWatcherThreadDrun6M_v_; +text: .text%__1cMPeriodicTaskMtime_to_wait6F_I_: thread.o; +text: .text%__1cNWatcherThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cIos_sleep6Fxi_i_: os_solaris.o; +text: .text%__1cNgetTimeMillis6F_x_; +text: .text%__1cQjava_lang_StringOchar_converter6FnGHandle_HHpnGThread__1_; +text: .text%JVM_FindLoadedClass; +text: .text%__1cQSystemDictionarybCfind_instance_or_array_klass6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cQSystemDictionaryEfind6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cSInterpreterRuntimeLmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cSObjectSynchronizerJslow_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%jni_CallStaticObjectMethod: jni.o; +text: .text%jni_NewByteArray: jni.o; +text: .text%jni_SetByteArrayRegion: jni.o; +text: .text%__1cSInterpreterRuntimeMmonitorenter6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cNObjectMonitorFenter6MpnGThread__v_; +text: .text%jni_NewObject: jni.o; +text: .text%__1cGThreadMis_VM_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cRLowMemoryDetectorbGlow_memory_detector_thread_entry6FpnKJavaThread_pnGThread__v_; +text: .text%__1cRLowMemoryDetectorUhas_pending_requests6F_i_; +text: .text%__1cVjava_lang_ClassLoaderGparent6FpnHoopDesc__2_; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcipnNsymbolOopDesc_4_v_; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcinMsymbolHandle_4nGHandle_6_v_; +text: .text%__1cKExceptionsRspecial_exception6FpnGThread_pkcinMsymbolHandle_4_i_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_pkcnGHandle_6_6_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_3pnRJavaCallArguments_nGHandle_6_6_; +text: .text%JVM_FillInStackTrace; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle__v_; +text: .text%__1cIUniverseWis_out_of_memory_error6FnGHandle__i_; +text: .text%__1cVPreserveExceptionMark2t6MrpnGThread__v_; +text: .text%__1cKJavaThreadGactive6F_p0_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle_pnGThread__v_; +text: .text%__1cTjava_lang_ThrowableNset_backtrace6FpnHoopDesc_2_v_; +text: .text%__1cTjava_lang_ThrowableQclear_stacktrace6FpnHoopDesc__v_; +text: .text%__1cVPreserveExceptionMark2T6M_v_; +text: .text%__1cKExceptionsG_throw6FpnGThread_pkcinGHandle__v_; +text: .text%__1cKExceptionsRspecial_exception6FpnGThread_pkcinGHandle__i_; +text: .text%__1cGThreadVset_pending_exception6MpnHoopDesc_pkci_v_; +text: .text%__1cGEventsDlog6FpkcE_v_: exceptions.o; +text: .text%__1cSInterpreterRuntimeXthrow_pending_exception6FpnKJavaThread__v_; +text: .text%__1cNSharedRuntimebKexception_handler_for_return_address6FpC_1_; +text: .text%__1cNSharedRuntimebOraw_exception_handler_for_return_address6FpC_1_; +text: .text%__1cSInterpreterRuntimebFexception_handler_for_exception6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cNmethodOopDescbEfast_exception_handler_bci_for6MnLKlassHandle_ipnGThread__i_; +text: .text%__1cKJavaThreadNreguard_stack6MpC_i_; +text: .text%__1cSInterpreterRuntimePset_bcp_and_mdp6FpCpnKJavaThread__v_; +text: .text%__1cFframeZinterpreter_frame_set_bcp6MpC_v_; +text: .text%__1cFframeZinterpreter_frame_set_bcx6Mi_v_; +text: .text%__1cNCompileBrokerYcheck_compilation_result6FnMmethodHandle_iippnHnmethod__i_; +text: .text%__1cNCompileBrokerXcompilation_is_in_queue6FnMmethodHandle_i_i_; +text: .text%__1cNCompileBrokerZcompilation_is_prohibited6FnMmethodHandle_i_i_; +text: .text%__1cNCompileBrokerTis_not_compile_only6FnMmethodHandle__i_; +text: .text%__1cOCompilerOracleOshould_exclude6FnMmethodHandle__i_; +text: .text%__1cNCompileBrokerRassign_compile_id6FnMmethodHandle_i_I_; +text: .text%__1cNCompileBrokerTis_compile_blocking6FnMmethodHandle_i_i_; +text: .text%__1cNCompileBrokerTcreate_compile_task6FpnMCompileQdDueue_inMmethodHandle_i3ipkcii_pnLCompileTask__; +text: .text%__1cNCompileBrokerNallocate_task6F_pnLCompileTask__; +text: .text%__1cLCompileTaskKinitialize6MinMmethodHandle_i1ipkcii_v_; +text: .text%__1cMCompileQdDueueDadd6MpnLCompileTask__v_; +text: .text%__1cNCompileBrokerTwait_for_completion6FpnLCompileTask__pnHnmethod__; +text: .text%__1cCosPhint_no_preempt6F_v_; +text: .text%__1cSCompileTaskWrapper2t6MpnLCompileTask__v_; +text: .text%__1cNCompileBrokerZinvoke_compiler_on_method6FpnLCompileTask__v_; +text: .text%__1cNCompileBrokerQset_last_compile6FpnOCompilerThread_nMmethodHandle_ii_v_; +text: .text%__1cNCompileBrokerVpush_jni_handle_block6F_v_; +text: .text%__1cNCompileBrokerOcheck_break_at6FnMmethodHandle_iii_i_; +text: .text%__1cOCompilerOraclePshould_break_at6FnMmethodHandle__i_; +text: .text%__1cFciEnv2t6MpnHJNIEnv__iii_v_; +text: .text%__1cPciObjectFactory2t6MpnFArena_i_v_; +text: .text%__1cPciObjectFactoryTinit_shared_objects6M_v_; +text: .text%__1cUGenericGrowableArray2t6MpnFArena_iipnEGrET__v_; +text: .text%__1cIciSymbol2t6MnMsymbolHandle__v_; +text: .text%__1cIciObject2t6MnGHandle__v_; +text: .text%__1cPciObjectFactoryEfind6MpnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cPciObjectFactoryLis_found_at6MipnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cPciObjectFactoryNinit_ident_of6MpnIciObject__v_; +text: .text%__1cIciObjectJset_ident6MI_v_; +text: .text%__1cPciObjectFactoryGinsert6MipnIciObject_pnNGrowableArray4C2___v_; +text: .text%__1cGciType2t6MnJBasicType__v_; +text: .text%__1cIciObject2t6M_v_; +text: .text%__1cPciObjectFactoryDget6MpnHoopDesc__pnIciObject__; +text: .text%__1cPciObjectFactoryNfind_non_perm6MpnHoopDesc__rpn0ANNonPermObject__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: klassKlass.o; +text: .text%__1cPciObjectFactoryRcreate_new_object6MpnHoopDesc__pnIciObject__; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodKlass.o; +text: .text%__1cIciSymbolEmake6Fpkc_p0_; +text: .text%__1cFciEnvIis_in_vm6F_i_; +text: .text%__1cIciSymbolJmake_impl6Fpkc_p0_; +text: .text%__1cHciKlass2t6MnLKlassHandle_pnIciSymbol__v_; +text: .text%__1cGciType2t6MnLKlassHandle__v_; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: symbolKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: symbolKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: symbolKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: symbolKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: symbolKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: klassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassUoop_is_instanceKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cSinstanceKlassKlassUoop_is_instanceKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassVoop_is_typeArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassUoop_is_objArrayKlass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: objArrayKlassKlass.o; +text: .text%__1cPciInstanceKlass2t6MnLKlassHandle__v_; +text: .text%__1cHciKlass2t6MnLKlassHandle__v_; +text: .text%__1cPciInstanceKlassFsuper6M_p0_; +text: .text%__1cPciInstanceKlassTis_java_lang_Object6M_i_; +text: .text%__1cIciObjectGequals6Mp0_i_; +text: .text%__1cPciInstanceKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cHciKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlass.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: instanceKlass.o; +text: .text%__1cUciInstanceKlassKlassEmake6F_p0_; +text: .text%__1cHciKlass2t6MpnIciSymbol_p0_v_; +text: .text%__1cGciType2t6MpnHciKlass__v_; +text: .text%__1cIciObject2t6MpnHciKlass__v_; +text: .text%__1cIciObjectUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cPciObjArrayKlass2t6MpnIciSymbol_pnHciKlass_i_v_; +text: .text%__1cUciObjArrayKlassKlassEmake6F_p0_; +text: .text%__1cMciArrayKlass2t6MpnIciSymbol_ipnHciKlass__v_; +text: .text%__1cRciArrayKlassKlassUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cQciTypeArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cMciArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cFciEnvWget_method_from_handle6MpnI_jobject__pnIciMethod__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodKlass.o; +text: .text%__1cIciMethod2t6MnMmethodHandle__v_; +text: .text%__1cLciSignature2t6MpnHciKlass_pnIciSymbol__v_; +text: .text%__1cPSignatureStreamJis_object6kM_i_; +text: .text%__1cGciTypeEmake6FnJBasicType__p0_; +text: .text%__1cJTraceTime2t6MpkcpnMelapsedTimer_iipnMoutputStream__v_; +text: .text%__1cICompilerOcompile_method6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cLCompilation2t6MpnQAbstractCompiler_pnFciEnv_pnIciMethod_ipnRC1_MacroAssembler__v_; +text: .text%__1cTExceptionRangeTable2t6Mi_v_; +text: .text%__1cWImplicitExceptionTableIset_size6MI_v_; +text: .text%__1cLCompilationOcompile_method6M_v_; +text: .text%__1cLCompilationKinitialize6M_v_; +text: .text%__1cLCompilationEcode6kM_pnKCodeBuffer__; +text: .text%__1cYDebugInformationRecorder2t6MpnLOopRecorder__v_; +text: .text%__1cUDebugInfoWriteStream2t6MpnYDebugInformationRecorder_i_v_; +text: .text%__1cLCompilationTdebug_info_recorder6kM_pnYDebugInformationRecorder__; +text: .text%__1cLCompilationbBis_optimized_library_method6kM_i_; +text: .text%__1cLCompilationTcompile_java_method6MpnLCodeOffsets__i_; +text: .text%__1cLCompilationTinitialize_oop_maps6M_v_; +text: .text%__1cIciMethodMall_oop_maps6M_pnKciLocalMap__; +text: .text%__1cSciGenerateLocalMap2t6MpnFArena_nMmethodHandle__v_; +text: .text%__1cOGenerateOopMap2t6MnMmethodHandle__v_; +text: .text%__1cSciGenerateLocalMapWfind_jsr_return_points6MnMmethodHandle__v_; +text: .text%__1cRRawBytecodeStream2t6MnMmethodHandle__v_; +text: .text%__1cRRawBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: ciOopMap.o; +text: .text%__1cOGenerateOopMapLcompute_map6MpnGThread__v_; +text: .text%__1cIRetTableRcompute_ret_table6MnMmethodHandle__v_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: generateOopMap.o; +text: .text%__1cOGenerateOopMapbImark_bbheaders_and_count_gc_points6M_v_; +text: .text%__1cOGenerateOopMapNinitialize_bb6M_v_; +text: .text%__1cOGenerateOopMapLbb_mark_fct6Fp0ipi_v_; +text: .text%__1cOGenerateOopMapOset_bbmark_bit6Mi_v_; +text: .text%__1cOGenerateOopMapPjump_targets_do6MpnOBytecodeStream_pFp0ipi_v4_i_; +text: .text%__1cSciGenerateLocalMapRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cSciGenerateLocalMapUbytecode_is_gc_point6FnJBytecodesECode_ii_i_; +text: .text%__1cOGenerateOopMapRdo_interpretation6M_v_; +text: .text%__1cOGenerateOopMapRinit_basic_blocks6M_v_; +text: .text%__1cOGenerateOopMapKinit_state6M_v_; +text: .text%__1cOGenerateOopMapTmark_reachable_code6M_v_; +text: .text%__1cOGenerateOopMapUreachable_basicblock6Fp0ipi_v_; +text: .text%__1cOGenerateOopMapSget_basic_block_at6kMi_pnKBasicBlock__; +text: .text%__1cOGenerateOopMapbAget_basic_block_containing6kMi_pnKBasicBlock__; +text: .text%__1cOGenerateOopMapYsetup_method_entry_state6M_v_; +text: .text%__1cOGenerateOopMapbAmake_context_uninitialized6M_v_; +text: .text%__1cOGenerateOopMapTmethodsig_to_effect6MpnNsymbolOopDesc_ipnNCellTypeState__i_; +text: .text%__1cOGenerateOopMapPinitialize_vars6M_v_; +text: .text%__1cOGenerateOopMapTmerge_state_into_bb6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapKcopy_state6MpnNCellTypeState_2_v_; +text: .text%__1cOGenerateOopMapKinterp_all6M_v_; +text: .text%__1cOGenerateOopMapJinterp_bb6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapNrestore_state6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapQnext_bb_start_pc6MpnKBasicBlock__i_; +text: .text%__1cOGenerateOopMapHinterp16MpnOBytecodeStream__v_; +text: .text%__1cOGenerateOopMapGppload6MpnNCellTypeState_i_v_; +text: .text%__1cOGenerateOopMapHget_var6Mi_nNCellTypeState__; +text: .text%__1cOGenerateOopMapEpush6MnNCellTypeState__v_; +text: .text%__1cOGenerateOopMapIdo_field6Miiii_v_; +text: .text%__1cOGenerateOopMapRsigchar_to_effect6McipnNCellTypeState__2_; +text: .text%__1cOGenerateOopMapCpp6MpnNCellTypeState_2_v_; +text: .text%__1cOGenerateOopMapEppop6MpnNCellTypeState__v_; +text: .text%__1cOGenerateOopMapFppop16MnNCellTypeState__v_; +text: .text%__1cOGenerateOopMapDpop6M_nNCellTypeState__; +text: .text%__1cOGenerateOopMapKcheck_type6MnNCellTypeState_1_v_; +text: .text%__1cOGenerateOopMapFppush6MpnNCellTypeState__v_; +text: .text%__1cOGenerateOopMapGppush16MnNCellTypeState__v_; +text: .text%__1cOGenerateOopMapHppstore6MpnNCellTypeState_i_v_; +text: .text%__1cOGenerateOopMapHset_var6MinNCellTypeState__v_; +text: .text%__1cOGenerateOopMapLmerge_state6Fp0ipi_v_; +text: .text%__1cOGenerateOopMapJdo_astore6Mi_v_; +text: .text%__1cOGenerateOopMapTmerge_state_vectors6MpnNCellTypeState_2_i_; +text: .text%__1cNCellTypeStateFmerge6kM0i_0_; +text: .text%__1cOGenerateOopMapIcopy_cts6MpnNCellTypeState_2_i_; +text: .text%__1cOGenerateOopMapXdo_return_monitor_check6M_v_; +text: .text%__1cOGenerateOopMapYrewrite_refval_conflicts6M_v_; +text: .text%__1cSciGenerateLocalMapOreport_results6kM_i_: ciOopMap.o; +text: .text%__1cOGenerateOopMapNreport_result6M_v_; +text: .text%__1cSciGenerateLocalMapUfill_stackmap_prolog6Mi_v_; +text: .text%__1cSciGenerateLocalMapZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cKciLocalMap2t6MpnFArena_iii_v_; +text: .text%__1cKciLocalMapRset_bci_for_index6Mii_v_; +text: .text%__1cSciGenerateLocalMapUfill_stackmap_epilog6M_v_: ciOopMap.o; +text: .text%__1cSciGenerateLocalMapOfill_init_vars6MpnNGrowableArray4Ci___v_; +text: .text%__1cKciLocalMapSset_nof_initialize6Mi_v_; +text: .text%__1cLCompilationJbuild_hir6M_v_; +text: .text%__1cCIR2t6MpnLCompilation_pnIciMethod_i_v_; +text: .text%__1cJValueTypeKinitialize6F_v_; +text: .text%__1cMciNullObjectEmake6F_p0_; +text: .text%__1cMGraphBuilderKinitialize6F_v_; +text: .text%__1cHIRScope2t6MpnLCompilation_p0ipnIciMethod_ii_v_; +text: .text%__1cOLocalSlotArray2t6MkikpnJLocalSlot__v_: c1_IR.o; +text: .text%__1cGBitMap2t6MI_v_; +text: .text%__1cGBitMapGresize6MI_v_; +text: .text%__1cNWordSizeArray2t6Mki1_v_: c1_IR.o; +text: .text%__1cJXHandlers2t6MpnIciMethod__v_; +text: .text%__1cIciMethodJload_code6M_v_; +text: .text%__1cIciMethodVhas_balanced_monitors6M_i_; +text: .text%__1cHIRScopeLbuild_graph6MpnLCompilation_i_pnKBlockBegin__; +text: .text%__1cQBlockListBuilder2t6MpnHIRScope_ii_v_; +text: .text%__1cPBlockBeginArray2t6MkikpnKBlockBegin__v_: c1_GraphBuilder.o; +text: .text%__1cQBlockListBuilderLset_leaders6M_v_; +text: .text%__1cQciBytecodeStream2t6MpnIciMethod__v_; +text: .text%__1cQciBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cQBlockListBuilderMnew_block_at6MinKBlockBeginEFlag__p1_; +text: .text%__1cQBlockListBuilderUset_xhandler_entries6M_v_; +text: .text%__1cKValueStack2t6MpnHIRScope_ii_v_; +text: .text%__1cKValueArray2t6MkikpnLInstruction__v_: c1_ValueStack.o; +text: .text%__1cJLocalSlot2t6M_v_; +text: .text%__1cJLocalSlotIfor_type6MpnJValueType_ii_pnFLocal__: c1_IR.o; +text: .text%__1cKObjectTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cMGraphBuilder2t6MpnLCompilation_pnHIRScope_pnJBlockList_pnKBlockBegin__v_; +text: .text%__1cMGraphBuilderPpush_root_scope6MpnHIRScope_pnJBlockList_pnKBlockBegin__v_; +text: .text%__1cMGraphBuilderJScopeData2t6Mp1i_v_; +text: .text%__1cMGraphBuilderJScopeDataJset_scope6MpnHIRScope__v_; +text: .text%__1cMGraphBuilderUpush_exception_scope6M_v_; +text: .text%__1cOExceptionScope2t6M_v_; +text: .text%__1cOExceptionScopeEinit6M_v_; +text: .text%__1cIValueMap2t6M_v_; +text: .text%__1cMGraphBuilderJScopeDataQadd_to_work_list6MpnKBlockBegin__v_; +text: .text%__1cNResourceArrayGexpand6MIiri_v_; +text: .text%__1cMGraphBuilderSiterate_all_blocks6Mi_v_; +text: .text%__1cMGraphBuilderJScopeDataVremove_from_work_list6M_pnKBlockBegin__; +text: .text%__1cMGraphBuilderJScopeDataSis_work_list_empty6kM_i_; +text: .text%__1cMGraphBuilderOconnect_to_end6MpnKBlockBegin__pnIBlockEnd__; +text: .text%__1cIValueMapIkill_all6M_v_; +text: .text%__1cIValueMapRnumber_of_buckets6kM_i_; +text: .text%__1cIValueMapJbucket_at6Mi_pnGBucket__; +text: .text%__1cGBucketIkill_all6M_v_; +text: .text%__1cKValueStackEcopy6M_p0_; +text: .text%__1cGValuesIpush_all6Mpk0_v_: c1_ValueStack.o; +text: .text%__1cMGraphBuilderbBiterate_bytecodes_for_block6Mi_pnIBlockEnd__; +text: .text%__1cLInstructionLas_BlockEnd6M_pnIBlockEnd__: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderJScopeDataIblock_at6Mi_pnKBlockBegin__; +text: .text%__1cMGraphBuilderKload_local6MpnJValueType_i_v_; +text: .text%__1cJLocalSlotIfor_type6MpnJValueType_ii_pnFLocal__: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderGappend6MpnLInstruction__2_; +text: .text%__1cMGraphBuilderLappend_base6MpnLInstruction__2_; +text: .text%__1cJLoadLocalFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerMdo_LoadLocal6MpnJLoadLocal__v_; +text: .text%__1cIValueMapEfind6MpnLInstruction__2_; +text: .text%__1cLInstructionOas_AccessField6M_pnLAccessField__: c1_GraphBuilder.o; +text: .text%__1cLInstructionLas_UnsafeOp6M_pnIUnsafeOp__: c1_GraphBuilder.o; +text: .text%__1cLInstructionMas_Intrinsic6M_pnJIntrinsic__: c1_GraphBuilder.o; +text: .text%__1cLInstructionEhash6kM_i_: c1_GraphBuilder.o; +text: .text%__1cLInstructionNas_StateSplit6M_pnKStateSplit__: c1_GraphBuilder.o; +text: .text%__1cLInstructionIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cKValueStackLclear_store6Mi_v_; +text: .text%__1cKValueStackEpush6MpnJValueType_pnLInstruction__v_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderMaccess_field6MnJBytecodesECode__v_; +text: .text%__1cQciBytecodeStreamJget_field6kM_pnHciField__; +text: .text%__1cQciBytecodeStreamPget_field_index6kM_i_; +text: .text%__1cFciEnvSget_field_by_index6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cFciEnvXget_field_by_index_impl6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cPciInstanceKlassLfield_cache6M_pnTciConstantPoolCache__; +text: .text%__1cHciField2t6MpnPciInstanceKlass_i_v_; +text: .text%__1cFciEnvSget_klass_by_index6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cFciEnvXget_klass_by_index_impl6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cTconstantPoolOopDescSklass_at_if_loaded6FnSconstantPoolHandle_i_pnMklassOopDesc__; +text: .text%__1cPciObjectFactorySget_unloaded_klass6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cPciInstanceKlassGloader6M_pnHoopDesc__; +text: .text%__1cPciInstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cHciFieldPinitialize_from6MpnPfieldDescriptor__v_; +text: .text%__1cMas_ValueType6FnJBasicType__pnJValueType__; +text: .text%__1cHciFieldJwill_link6MpnPciInstanceKlass_nJBytecodesECode__i_; +text: .text%__1cMLinkResolverXresolve_klass_no_update6FrnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cTconstantPoolOopDescbCklass_ref_at_if_loaded_check6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cMGraphBuilderKlock_stack6M_pnKValueStack__; +text: .text%__1cKValueStackKcopy_locks6M_p0_; +text: .text%__1cJLoadFieldFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerMdo_LoadField6MpnJLoadField__v_; +text: .text%__1cLAccessFieldOas_AccessField6M_p0_: c1_Instruction.o; +text: .text%__1cJLoadFieldEhash6kM_i_: c1_Instruction.o; +text: .text%__1cJLoadFieldEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cIValueMapNlookup_bucket6Mi_pnGBucket__; +text: .text%__1cGBucketEfind6MpnLInstruction_i_2_; +text: .text%__1cGBucketGappend6MpnLInstruction_i_v_; +text: .text%__1cLInstructionNas_StateSplit6M_pnKStateSplit__: c1_Instruction.o; +text: .text%__1cLInstructionLas_BlockEnd6M_pnIBlockEnd__: c1_Instruction.o; +text: .text%__1cLAccessFieldIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cOExceptionScopeEcopy6M_p0_; +text: .text%__1cOExceptionScopeGlength6kM_i_; +text: .text%__1cHIntTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cMGraphBuilderLstore_local6MpnJValueType_i_v_; +text: .text%__1cKValueStackDpop6MpnJValueType__pnLInstruction__: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderLstore_local6MpnKValueStack_pnLInstruction_pnJValueType_ii_v_; +text: .text%__1cJValueTypeNas_ObjectType6M_pnKObjectType__: c1_ValueType.o; +text: .text%__1cKStoreLocalFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerNdo_StoreLocal6MpnKStoreLocal__v_; +text: .text%__1cKValueStackLstore_local6MpnKStoreLocal_i_v_; +text: .text%__1cKValueStackQpin_stack_locals6Mi_v_; +text: .text%__1cKValueStackNpin_stack_all6MnLInstructionJPinReason__v_; +text: .text%__1cHIntTypeEsize6kM_i_: c1_ValueType.o; +text: .text%__1cMGraphBuilderHif_zero6MpnJValueType_nLInstructionJCondition__v_; +text: .text%__1cIConstantFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerLdo_Constant6MpnIConstant__v_; +text: .text%__1cLInstructionOas_AccessField6M_pnLAccessField__: c1_Instruction.o; +text: .text%__1cLInstructionLas_UnsafeOp6M_pnIUnsafeOp__: c1_Instruction.o; +text: .text%__1cLInstructionMas_Intrinsic6M_pnJIntrinsic__: c1_Instruction.o; +text: .text%__1cIConstantEhash6kM_i_; +text: .text%__1cHIntTypeDtag6kM_nIValueTag__: c1_Canonicalizer.o; +text: .text%__1cLIntConstantOas_IntConstant6M_p0_: c1_Canonicalizer.o; +text: .text%__1cIConstantEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cIConstantIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cMGraphBuilderHif_node6MpnLInstruction_n0BJCondition_2pnKValueStack__v_; +text: .text%__1cCIf2t6MpnLInstruction_n0BJCondition_i2pnKBlockBegin_5pnKValueStack_i_v_: c1_GraphBuilder.o; +text: .text%__1cCIfFvisit6MpnSInstructionVisitor__v_: c1_Canonicalizer.o; +text: .text%__1cNCanonicalizerFdo_If6MpnCIf__v_; +text: .text%__1cJValueTypeLis_constant6kM_i_: c1_ValueType.o; +text: .text%__1cLInstructionMas_CompareOp6M_pnJCompareOp__: c1_Instruction.o; +text: .text%__1cLInstructionNas_InstanceOf6M_pnKInstanceOf__: c1_Instruction.o; +text: .text%__1cLInstructionOas_AccessField6M_pnLAccessField__: c1_Canonicalizer.o; +text: .text%__1cLInstructionLas_UnsafeOp6M_pnIUnsafeOp__: c1_Canonicalizer.o; +text: .text%__1cLInstructionMas_Intrinsic6M_pnJIntrinsic__: c1_Canonicalizer.o; +text: .text%__1cLInstructionEhash6kM_i_: c1_Canonicalizer.o; +text: .text%__1cKStateSplitNas_StateSplit6M_p0_: c1_Canonicalizer.o; +text: .text%__1cIBlockEndLas_BlockEnd6M_p0_: c1_Canonicalizer.o; +text: .text%__1cLInstructionIcan_trap6kM_i_: c1_Canonicalizer.o; +text: .text%__1cLInstructionJas_Return6M_pnGReturn__: c1_Canonicalizer.o; +text: .text%__1cLInstructionIas_Throw6M_pnFThrow__: c1_Canonicalizer.o; +text: .text%__1cKBlockBeginItry_join6MpnKValueStack__i_; +text: .text%__1cKValueStack2t6Mp0_v_; +text: .text%__1cKValueStackEinit6Mp0_v_; +text: .text%__1cMGraphBuilderNmethod_return6MpnLInstruction__v_; +text: .text%__1cGReturnFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerJdo_Return6MpnGReturn__v_; +text: .text%__1cKStateSplitNas_StateSplit6M_p0_: c1_GraphBuilder.o; +text: .text%__1cIBlockEndLas_BlockEnd6M_p0_: c1_GraphBuilder.o; +text: .text%__1cGReturnJas_Return6M_p0_: c1_GraphBuilder.o; +text: .text%__1cKValueStackbAeliminate_all_scope_stores6Mi_v_; +text: .text%__1cKValueStackQeliminate_stores6Mi_v_; +text: .text%__1cKValueStackMcaller_state6kM_p0_; +text: .text%__1cFciEnvWget_klass_by_name_impl6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cQSystemDictionarybOfind_constrained_instance_or_array_klass6FnMsymbolHandle_nGHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cHciKlassGloader6M_pnHoopDesc__: ciTypeArrayKlass.o; +text: .text%__1cFciEnvZcheck_klass_accessibility6MpnHciKlass_pnMklassOopDesc__i_; +text: .text%__1cIciObjectMis_obj_array6M_i_: ciInstanceKlass.o; +text: .text%__1cPciInstanceKlassRis_instance_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cKObjectTypeNas_ObjectType6M_p0_: c1_ValueType.o; +text: .text%__1cJValueTypeOas_AddressType6M_pnLAddressType__: c1_ValueType.o; +text: .text%__1cKObjectTypeEsize6kM_i_: c1_ValueType.o; +text: .text%__1cMGraphBuilderHif_same6MpnJValueType_nLInstructionJCondition__v_; +text: .text%__1cJValueTypeOas_IntConstant6M_pnLIntConstant__: c1_ValueType.o; +text: .text%__1cKStoreFieldFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerNdo_StoreField6MpnKStoreField__v_; +text: .text%__1cLAccessFieldOas_AccessField6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLAccessFieldIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cIValueMapKkill_field6MpnHciField__v_; +text: .text%__1cGBucketKkill_field6MpnHciField__v_; +text: .text%__1cKValueStackQpin_stack_fields6MpnHciField__v_; +text: .text%__1cKValueStackVis_same_across_scopes6Mp0_i_; +text: .text%__1cMGraphBuilderNarithmetic_op6MpnJValueType_nJBytecodesECode_pnKValueStack__v_; +text: .text%__1cJValueTypeEmeet6kMp0_1_; +text: .text%__1cHIntTypeEbase6kM_pnJValueType__: c1_Canonicalizer.o; +text: .text%__1cMArithmeticOpIcan_trap6kM_i_; +text: .text%__1cMArithmeticOpFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerPdo_ArithmeticOp6MpnMArithmeticOp__v_; +text: .text%__1cNCanonicalizerGdo_Op26MpnDOp2__v_; +text: .text%__1cLIntConstantLis_constant6kM_i_: c1_Canonicalizer.o; +text: .text%__1cNCanonicalizerTmove_const_to_right6MpnDOp2__v_; +text: .text%__1cMArithmeticOpOis_commutative6kM_i_; +text: .text%__1cMArithmeticOpEhash6kM_i_: c1_Instruction.o; +text: .text%__1cMArithmeticOpEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cMGraphBuilderJincrement6M_v_; +text: .text%__1cHIntTypeEbase6kM_pnJValueType__: c1_ValueType.o; +text: .text%__1cMGraphBuilderMload_indexed6MnJBasicType__v_; +text: .text%__1cLLoadIndexedFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerOdo_LoadIndexed6MpnLLoadIndexed__v_; +text: .text%__1cLLoadIndexedEhash6kM_i_: c1_Instruction.o; +text: .text%__1cLLoadIndexedEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cLAccessArrayIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cIConstantIis_equal6kMpnLInstruction__i_; +text: .text%__1cIConstantLas_Constant6M_p0_: c1_Instruction.o; +text: .text%__1cEGotoFvisit6MpnSInstructionVisitor__v_: c1_Canonicalizer.o; +text: .text%__1cNCanonicalizerHdo_Goto6MpnEGoto__v_; +text: .text%__1cHIRScopeMheader_block6MpnKBlockBegin_n0BEFlag__2_; +text: .text%__1cCIRIoptimize6M_v_; +text: .text%__1cJOptimizer2t6MpnCIR__v_; +text: .text%__1cJOptimizerbHeliminate_conditional_expressions6M_v_; +text: .text%__1cCIRQiterate_preorder6MpnMBlockClosure__v_; +text: .text%__1cKBlockBeginQiterate_preorder6MpnMBlockClosure__v_; +text: .text%__1cJboolArray2t6Mki1_v_: c1_Instruction.o; +text: .text%__1cKBlockBeginQiterate_preorder6MrnJboolArray_pnMBlockClosure__v_; +text: .text%__1cNCE_EliminatorIblock_do6MpnKBlockBegin__v_: c1_Optimizer.o; +text: .text%__1cLInstructionFas_If6M_pnCIf__: c1_IR.o; +text: .text%__1cLInstructionFas_If6M_pnCIf__: c1_Canonicalizer.o; +text: .text%__1cCIfFas_If6M_p0_: c1_Canonicalizer.o; +text: .text%__1cHIntTypeKas_IntType6M_p0_: c1_ValueType.o; +text: .text%__1cNCE_EliminatorRsimple_value_copy6MpnLInstruction__2_: c1_Optimizer.o; +text: .text%__1cLInstructionLas_Constant6M_pnIConstant__: c1_GraphBuilder.o; +text: .text%__1cJLoadLocalMas_LoadLocal6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLInstructionHas_Goto6M_pnEGoto__: c1_GraphBuilder.o; +text: .text%__1cLInstructionFas_If6M_pnCIf__: c1_GraphBuilder.o; +text: .text%__1cJOptimizerQeliminate_blocks6M_v_; +text: .text%__1cUGenericGrowableArray2t6MiipnEGrET_i_v_; +text: .text%__1cSPredecessorCounterIblock_do6MpnKBlockBegin__v_: c1_Optimizer.o; +text: .text%__1cLBlockMergerIblock_do6MpnKBlockBegin__v_: c1_Optimizer.o; +text: .text%__1cLBlockMergerJtry_merge6MpnKBlockBegin__i_: c1_Optimizer.o; +text: .text%__1cLInstructionHas_Goto6M_pnEGoto__: c1_IR.o; +text: .text%__1cEGotoHas_Goto6M_p0_: c1_Canonicalizer.o; +text: .text%__1cLInstructionHas_Goto6M_pnEGoto__: c1_Canonicalizer.o; +text: .text%__1cJOptimizerVeliminate_null_checks6M_v_; +text: .text%__1cGBitMapFclear6M_v_; +text: .text%__1cGBitMapUclear_range_of_words6MII_v_: bitMap.o; +text: .text%__1cNValueSetArray2t6MkikpnIValueSet__v_: c1_Optimizer.o; +text: .text%__1cTNullCheckEliminatorHiterate6MpnKBlockBegin__v_; +text: .text%__1cTNullCheckEliminatorLiterate_all6M_v_; +text: .text%__1cTNullCheckEliminatorLiterate_one6MpnKBlockBegin__v_; +text: .text%__1cJLocalSlotIfor_type6MpnJValueType_ii_pnFLocal__: c1_Optimizer.o; +text: .text%__1cGBitMapIset_from6M0_v_; +text: .text%__1cKStateSplitPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cKBlockBeginFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorNdo_BlockBegin6MpnKBlockBegin__v_; +text: .text%__1cKStateSplitPinput_values_do6MpFppnLInstruction__v_v_: c1_IR.o; +text: .text%__1cEBaseFvisit6MpnSInstructionVisitor__v_: c1_IR.o; +text: .text%__1cQNullCheckVisitorHdo_Base6MpnEBase__v_; +text: .text%__1cTNullCheckEliminatorPmerge_state_for6MpnKBlockBegin_pnKValueStack_pnIValueSet__i_; +text: .text%__1cPBlockBeginArrayIindex_of6kMkpnKBlockBegin__i_: c1_Optimizer.o; +text: .text%__1cKStateSplitPinput_values_do6MpFppnLInstruction__v_v_: c1_Canonicalizer.o; +text: .text%__1cQNullCheckVisitorHdo_Goto6MpnEGoto__v_; +text: .text%__1cLInstructionMas_NullCheck6M_pnJNullCheck__: c1_GraphBuilder.o; +text: .text%__1cLInstructionMas_NullCheck6M_pnJNullCheck__: c1_Instruction.o; +text: .text%__1cKStoreLocalPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cTNullCheckEliminatorIdo_value6FppnLInstruction__v_; +text: .text%__1cFLocalPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cFLocalFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorIdo_Local6MpnFLocal__v_; +text: .text%__1cLAccessFieldPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cLAccessLocalPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorMdo_LoadLocal6MpnJLoadLocal__v_; +text: .text%__1cTNullCheckEliminatorQhandle_LoadLocal6MpnJLoadLocal__v_; +text: .text%__1cQNullCheckVisitorMdo_LoadField6MpnJLoadField__v_; +text: .text%__1cTNullCheckEliminatorShandle_AccessField6MpnLAccessField__v_; +text: .text%__1cQNullCheckVisitorNdo_StoreLocal6MpnKStoreLocal__v_; +text: .text%__1cTNullCheckEliminatorRhandle_StoreLocal6MpnKStoreLocal__v_; +text: .text%__1cCIfPinput_values_do6MpFppnLInstruction__v_v_: c1_Canonicalizer.o; +text: .text%__1cIConstantPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorLdo_Constant6MpnIConstant__v_; +text: .text%__1cQNullCheckVisitorFdo_If6MpnCIf__v_; +text: .text%__1cDOp2Pinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorPdo_ArithmeticOp6MpnMArithmeticOp__v_; +text: .text%__1cNAccessIndexedPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorOdo_LoadIndexed6MpnLLoadIndexed__v_; +text: .text%__1cTNullCheckEliminatorShandle_LoadIndexed6MpnLLoadIndexed__v_; +text: .text%__1cGBitMapbCset_intersection_with_result6M0_i_; +text: .text%__1cKStoreFieldPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorNdo_StoreField6MpnKStoreField__v_; +text: .text%__1cGReturnPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorJdo_Return6MpnGReturn__v_; +text: .text%__1cJboolArray2t6Mki1_v_: c1_Optimizer.o; +text: .text%__1cCIRTcompute_locals_size6M_v_; +text: .text%__1cHIRScopePallocate_locals6MipnMWordSizeList__i_; +text: .text%__1cHIRScopeGlocals6M_pnJLocalList__; +text: .text%__1cJLocalSlotOcollect_locals6MpnJLocalList__v_; +text: .text%__1cHIRScopePargument_locals6M_pnJLocalList__; +text: .text%__1cJLocalSlotXcollect_argument_locals6MpnJLocalList__v_; +text: .text%__1cCIRTallocate_local_name6M_i_; +text: .text%__1cMWordSizeListEgrow6Mki1_v_: c1_IR.o; +text: .text%__1cCIRSnotice_used_offset6Mi_v_; +text: .text%__1cCIRNcompute_loops6M_v_; +text: .text%__1cIciMethodJhas_loops6kM_i_; +text: .text%__1cNmethodOopDescWcompute_has_loops_flag6M_i_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: methodOop.o; +text: .text%__1cKLoopFinder2t6MpnCIR_i_v_; +text: .text%__1cSBlockLoopInfoArray2t6MkikpnNBlockLoopInfo__v_: c1_Loops.o; +text: .text%__1cKLoopFinderNcompute_loops6Mi_pnILoopList__; +text: .text%__1cJboolArray2t6Mki1_v_: c1_Loops.o; +text: .text%__1cKLoopFinderScompute_dominators6MpnJboolArray__v_; +text: .text%__1cGBitMapGat_put6MIi_v_; +text: .text%__1cRCreateInfoClosureIblock_do6MpnKBlockBegin__v_: c1_Loops.o; +text: .text%__1cNBlockLoopInfo2t6MpnKBlockBegin_i_v_; +text: .text%__1cPSetPredsClosureIblock_do6MpnKBlockBegin__v_: c1_Loops.o; +text: .text%__1cKLoopFinderSdominator_walk_sux6MpnKBlockBegin_pnJboolArray__v_; +text: .text%__1cGBitMapQset_intersection6M0_v_; +text: .text%__1cGBitMapHis_same6M0_i_; +text: .text%__1cKLoopFinderOfind_backedges6MpnJboolArray__pnILoopList__; +text: .text%__1cELoop2t6MpnKBlockBegin_2_v_: c1_Loops.o; +text: .text%__1cKLoopFinderSgather_loop_blocks6MpnILoopList__v_; +text: .text%__1cPBlockBeginArrayIindex_of6kMkpnKBlockBegin__i_: c1_Loops.o; +text: .text%__1cKLoopFinderKfind_loops6MpnILoopList_i_2_; +text: .text%__1cKScanBlocks2t6MpnJBlockList__v_; +text: .text%__1cIintStack2t6M_v_: c1_ScanBlocks.o; +text: .text%__1cKScanBlocksEscan6MpnKScanResult_i_v_; +text: .text%__1cKScanBlocksKscan_block6MpnKBlockBegin_pnKScanResult_i_v_; +text: .text%__1cLIllegalTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cLInstructionJas_Invoke6M_pnGInvoke__: c1_GraphBuilder.o; +text: .text%__1cLInstructionLas_NewArray6M_pnINewArray__: c1_GraphBuilder.o; +text: .text%__1cLInstructionOas_NewInstance6M_pnLNewInstance__: c1_GraphBuilder.o; +text: .text%__1cLInstructionQas_AccessMonitor6M_pnNAccessMonitor__: c1_GraphBuilder.o; +text: .text%__1cLInstructionOas_AccessLocal6M_pnLAccessLocal__: c1_Instruction.o; +text: .text%__1cLAccessLocalOas_AccessLocal6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLInstructionNas_StoreLocal6M_pnKStoreLocal__: c1_GraphBuilder.o; +text: .text%__1cKStoreLocalNas_StoreLocal6M_p0_: c1_GraphBuilder.o; +text: .text%__1cKScanBlocksRaccumulate_access6MinIValueTag_i_v_; +text: .text%__1cKScanBlocksPincrement_count6MnIValueTag_ii_v_; +text: .text%__1cKScanBlocksJget_array6MnIValueTag__pnIintStack__; +text: .text%__1cIintStackEgrow6Mki1_v_: c1_ScanBlocks.o; +text: .text%__1cKScanBlocksLupdate_type6MinIValueTag__v_; +text: .text%__1cLInstructionJas_Invoke6M_pnGInvoke__: c1_Canonicalizer.o; +text: .text%__1cLInstructionLas_NewArray6M_pnINewArray__: c1_Canonicalizer.o; +text: .text%__1cLInstructionOas_NewInstance6M_pnLNewInstance__: c1_Canonicalizer.o; +text: .text%__1cLInstructionQas_AccessMonitor6M_pnNAccessMonitor__: c1_Canonicalizer.o; +text: .text%__1cJ_LoopListIpush_all6Mpk0_v_: c1_Loops.o; +text: .text%__1cKLoopFinderbEcompute_loop_exits_and_entries6MpnILoopList__v_; +text: .text%__1cKLoopFinderRfind_loop_entries6MpnKBlockBegin_pnELoop__v_; +text: .text%__1cKLoopFinderPfind_loop_exits6MpnKBlockBegin_pnELoop__v_; +text: .text%__1cKLoopFinderbDcompute_single_precision_flag6MpnILoopList__v_; +text: .text%__1cKLoopFinderNinsert_blocks6MpnILoopList__v_; +text: .text%__1cIintArray2t6Mki1_v_: c1_Loops.o; +text: .text%__1cJBlockListPiterate_forward6MpnMBlockClosure__v_; +text: .text%__1cGTaggerIblock_do6MpnKBlockBegin__v_: c1_Loops.o; +text: .text%__1cNPairCollectorIblock_do6MpnKBlockBegin__v_: c1_Loops.o; +text: .text%__1cNResourceArrayEsort6MIpGpkv2_i_v_; +text: .text%__1cRsort_by_block_ids6FppnJBlockPair_2_i_: c1_Loops.o; +text: .text%__1cKLoopFinderUinsert_caching_block6MpnILoopList_pnKBlockBegin_4_4_; +text: .text%__1cLInstructionQas_CachingChange6M_pnNCachingChange__: c1_GraphBuilder.o; +text: .text%__1cKStateSplitFscope6kM_pnHIRScope__; +text: .text%__1cKLoopFinderJnew_block6MpnHIRScope_i_pnKBlockBegin__; +text: .text%__1cIBlockEndOsubstitute_sux6MpnKBlockBegin_2_v_; +text: .text%__1cILoopListMupdate_loops6MpnKBlockBegin_22_v_; +text: .text%__1cELoopSupdate_loop_blocks6MpnKBlockBegin_22_v_; +text: .text%__1cCIRMcompute_code6M_v_; +text: .text%__1cJboolArray2t6Mki1_v_: c1_IR.o; +text: .text%__1cCIRWiterate_and_set_weight6kMrnJboolArray_pnKBlockBegin_pnJBlockList_i_v_; +text: .text%__1cKBlockBeginKset_weight6Mi_v_; +text: .text%__1cLInstructionIas_Throw6M_pnFThrow__: c1_IR.o; +text: .text%__1cLInstructionJas_Return6M_pnGReturn__: c1_IR.o; +text: .text%__1cLInstructionIas_Throw6M_pnFThrow__: c1_GraphBuilder.o; +text: .text%__1cDcmp6FppnKBlockBegin_2_i_: c1_IR.o; +text: .text%__1cUSuxAndWeightAdjusterIblock_do6MpnKBlockBegin__v_: c1_IR.o; +text: .text%__1cJBlockListJblocks_do6MpFpnKBlockBegin__v_v_; +text: .text%__1cQUseCountComputerRcompute_use_count6FpnKBlockBegin__v_: c1_IR.o; +text: .text%__1cQUseCountComputerXbasic_compute_use_count6FpnKBlockBegin__v_: c1_IR.o; +text: .text%__1cQUseCountComputerQupdate_use_count6FppnLInstruction__v_: c1_IR.o; +text: .text%__1cFLocalIas_Local6M_p0_: c1_GraphBuilder.o; +text: .text%__1cKStateSplitPstate_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cKValueStackJvalues_do6MpFppnLInstruction__v_v_; +text: .text%__1cQUseCountComputerPupdated_pinning6FpnKBlockBegin__i_: c1_IR.o; +text: .text%__1cNCachingChangePinput_values_do6MpFppnLInstruction__v_v_: c1_Loops.o; +text: .text%__1cLInstructionLas_BlockEnd6M_pnIBlockEnd__: c1_Loops.o; +text: .text%__1cIBlockEndLas_BlockEnd6M_p0_: c1_IR.o; +text: .text%__1cLCompilationIemit_lir6M_v_; +text: .text%__1cIFrameMap2t6Mi_v_; +text: .text%__1cIFrameMapLFpuStackSim2t6M_v_; +text: .text%__1cLCompilationNinit_framemap6MpnIFrameMap__v_; +text: .text%__1cIFrameMapbCset_local_name_to_offset_map6MpnMWordSizeList__v_; +text: .text%__1cLLIR_Emitter2t6MpnLCompilation__v_; +text: .text%__1cIValueGenOinit_value_gen6F_v_; +text: .text%__1cIRegAlloc2t6M_v_; +text: .text%__1cNc1_AllocTable2t6Mi_v_; +text: .text%__1cIRegAllocFclear6M_v_; +text: .text%__1cNCodeGenerator2t6MpnIValueGen_pnRValueGenInvariant__v_; +text: .text%__1cNCodeGeneratorIblock_do6MpnKBlockBegin__v_; +text: .text%__1cLLIR_EmitterMmust_bailout6kM_i_; +text: .text%__1cNCodeGeneratorPblock_do_prolog6MpnKBlockBegin__v_; +text: .text%__1cIValueGenLstart_block6MpnKBlockBegin__v_; +text: .text%__1cLLIR_EmitterLstart_block6MpnKBlockBegin__v_; +text: .text%__1cILIR_List2t6MpnLCompilation__v_; +text: .text%__1cIValueGenQbind_block_entry6MpnKBlockBegin__v_; +text: .text%__1cLLIR_EmitterQbind_block_entry6MpnKBlockBegin__v_; +text: .text%__1cIValueGenMblock_prolog6MpnKBlockBegin__v_; +text: .text%__1cIValueGenHdo_root6MpnLInstruction__v_; +text: .text%__1cLInstructionGas_Phi6M_pnDPhi__: c1_GraphBuilder.o; +text: .text%__1cIValueGenNdo_BlockBegin6MpnKBlockBegin__v_; +text: .text%__1cQDelayedSpillMark2T6M_v_: c1_CodeGenerator.o; +text: .text%__1cLInstructionGas_Phi6M_pnDPhi__: c1_IR.o; +text: .text%__1cIValueGenHdo_Base6MpnEBase__v_; +text: .text%__1cIValueGenNreceiverRInfo6F_nFRInfo__; +text: .text%__1cIValueGenMicKlassRInfo6F_nFRInfo__; +text: .text%__1cLCompilationNget_init_vars6M_pnIintStack__; +text: .text%__1cLLIR_EmitterJstd_entry6MpnHIRScope_pnIintStack_nFRInfo_5_v_; +text: .text%__1cILIR_ListWunverified_entry_point6MnFRInfo_1_v_: c1_LIREmitter.o; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_LIREmitter.o; +text: .text%__1cLLIR_EmitterGmethod6kM_pnIciMethod__; +text: .text%__1cMCodeEmitInfo2t6MpnLLIR_Emitter_ipnIintStack_pnKValueStack_pnOExceptionScope_pnPRInfoCollection__v_; +text: .text%__1cLCompilationVvalue_stack2lir_stack6MpnKValueStack__pnNGrowableArray4CpnLLIR_OprDesc____; +text: .text%__1cIValueGenMblock_epilog6MpnKBlockBegin__v_; +text: .text%__1cNCodeGeneratorPblock_do_epilog6MpnKBlockBegin__v_; +text: .text%__1cLInstructionGas_Phi6M_pnDPhi__: c1_Canonicalizer.o; +text: .text%__1cIValueGenHdo_Goto6MpnEGoto__v_; +text: .text%__1cIValueGenNset_no_result6MpnLInstruction__v_; +text: .text%__1cIValueGenLmove_to_phi6MpnKValueStack_i_i_; +text: .text%__1cIValueGenWgoto_default_successor6MpnIBlockEnd_pnMCodeEmitInfo__v_; +text: .text%__1cLInstructionGas_Phi6M_pnDPhi__: c1_Instruction.o; +text: .text%__1cIValueGenMdo_LoadField6MpnJLoadField__v_; +text: .text%__1cLAccessFieldKlock_stack6kM_pnKValueStack__: c1_Instruction.o; +text: .text%__1cIValueGenEwalk6MpnLInstruction__v_; +text: .text%__1cIValueGenMdo_LoadLocal6MpnJLoadLocal__v_; +text: .text%__1cIValueGenJload_item6MpnEItem__v_; +text: .text%__1cEItemGupdate6M_v_; +text: .text%__1cIValueGenQset_maynot_spill6MpnEItem__v_; +text: .text%__1cIValueGenSfpu_fanout_handled6MpnEItem__i_; +text: .text%__1cEItemEtype6kM_pnJValueType__: c1_Items.o; +text: .text%__1cIValueGenPlock_free_rinfo6MpnLInstruction_pnJValueType__nFRInfo__; +text: .text%__1cIRegAllocMhas_free_reg6kMpnJValueType__i_; +text: .text%__1cIRegAllocMhas_free_reg6kMnIValueTag__i_; +text: .text%__1cNc1_AllocTableMhas_one_free6kM_i_; +text: .text%__1cIRegAllocNget_lock_temp6MpnLInstruction_pnJValueType__nFRInfo__; +text: .text%__1cIRegAllocMget_free_reg6MpnJValueType__nFRInfo__; +text: .text%__1cIRegAllocMget_free_reg6MnIValueTag__nFRInfo__; +text: .text%__1cNc1_AllocTableIget_free6M_i_; +text: .text%__1cNc1_AllocTablePget_free_helper6Mi_i_; +text: .text%__1cIRegAllocIlock_reg6MpnLInstruction_nFRInfo_i_v_; +text: .text%__1cJRInfo2RegFdo_it6M_v_: c1_RegAlloc.o; +text: .text%__1cHLockRegGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocOset_locked_cpu6MipnLInstruction_i_v_; +text: .text%__1cNc1_AllocTableKset_locked6Mi_v_; +text: .text%__1cLCompilationIitem2lir6MpknEItem__pnLLIR_OprDesc__; +text: .text%__1cLCompilationKitem2stack6MpknEItem__i_; +text: .text%__1cJValueTypeNas_DoubleType6M_pnKDoubleType__: c1_ValueType.o; +text: .text%__1cMas_BasicType6FpnJValueType__nJBasicType__; +text: .text%__1cJValueTypeMas_ArrayType6M_pnJArrayType__: c1_ValueType.o; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_Compilation.o; +text: .text%__1cLLIR_EmitterEmove6MpnLLIR_OprDesc_nFRInfo__v_; +text: .text%__1cILIR_ListEmove6MpnLLIR_OprDesc_2pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenJitem_free6MpnEItem__v_; +text: .text%__1cIRegAllocPincr_spill_lock6MnFRInfo__v_; +text: .text%__1cQChangeSpillCountGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIValueGenFrfree6MpnEItem__v_; +text: .text%__1cIRegAllocPdecr_spill_lock6MnFRInfo__v_; +text: .text%__1cIRegAllocIfree_reg6MnFRInfo__v_; +text: .text%__1cHFreeRegGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocMset_free_cpu6Mi_v_; +text: .text%__1cNc1_AllocTableIset_free6Mi_v_; +text: .text%__1cIValueGenWrlock_result_with_hint6MpnLInstruction_pknEItem__nFRInfo__; +text: .text%__1cIValueGenFrlock6MpnLInstruction_pknEItem__nFRInfo__; +text: .text%__1cIRegAllocMget_lock_reg6MpnLInstruction_pnJValueType__nFRInfo__; +text: .text%__1cLLIR_EmitterKfield_load6MnFRInfo_pnHciField_pnLLIR_OprDesc_iiipnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListMload_mem_reg6MnFRInfo_i1nJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_LIR.o; +text: .text%__1cIRegAllocHset_reg6MnFRInfo_ipnLInstruction__v_; +text: .text%__1cGSetRegGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocLset_cpu_reg6MiipnLInstruction__v_; +text: .text%__1cIValueGenNdo_StoreLocal6MpnKStoreLocal__v_; +text: .text%__1cEItemRhandle_float_kind6M_v_; +text: .text%__1cEItemNset_from_item6Mpk0_v_: c1_Items.o; +text: .text%__1cIValueGenXcan_inline_any_constant6kM_i_; +text: .text%__1cIValueGenSmust_copy_register6MpnEItem__i_; +text: .text%__1cIValueGenUcheck_float_register6MpnEItem__v_; +text: .text%__1cIRegAllocLis_free_reg6kMnFRInfo__i_; +text: .text%__1cJIsFreeRegGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cNc1_AllocTableHis_free6kMi_i_; +text: .text%__1cLLIR_EmitterJopr2local6MipnLLIR_OprDesc__v_; +text: .text%__1cILIR_ListQreg2single_stack6MnFRInfo_inJBasicType__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenFdo_If6MpnCIf__v_; +text: .text%__1cIHintItemNset_from_item6MpknEItem__v_; +text: .text%__1cIHintItemEtype6kM_pnJValueType__: c1_Items.o; +text: .text%__1cJValueTypeMas_FloatType6M_pnJFloatType__: c1_ValueType.o; +text: .text%__1cIValueGenLdo_Constant6MpnIConstant__v_; +text: .text%__1cJValueTypeRas_ObjectConstant6M_pnOObjectConstant__: c1_Canonicalizer.o; +text: .text%__1cIValueGenOdont_load_item6MpnEItem__v_; +text: .text%__1cIValueGenWdont_load_item_nocheck6MpnEItem__v_; +text: .text%__1cLLIR_OprFactKvalue_type6FpnJValueType__pnLLIR_OprDesc__; +text: .text%__1cLLIR_EmitterFif_op6MinLInstructionJCondition_pnLLIR_OprDesc_4pnKBlockBegin_66pnMCodeEmitInfo__v_; +text: .text%__1cJLIR_ConstEtype6kM_nJBasicType__: c1_CacheLocals.o; +text: .text%__1cJLIR_ConstLas_constant6M_p0_: c1_CacheLocals.o; +text: .text%__1cLLIR_EmitterIlir_cond6MnLInstructionJCondition__nMLIR_OpBranchNLIR_Condition__; +text: .text%__1cILIR_ListDcmp6MnMLIR_OpBranchNLIR_Condition_pnLLIR_OprDesc_4pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListGbranch6MnMLIR_OpBranchNLIR_Condition_pnKBlockBegin__v_; +text: .text%__1cMLIR_OpBranch2t6Mn0ANLIR_Condition_pnKBlockBegin_pnMCodeEmitInfo__v_; +text: .text%__1cEItemEtype6kM_pnJValueType__: c1_CodeGenerator.o; +text: .text%__1cJArrayTypeMas_ArrayType6M_p0_: c1_ValueType.o; +text: .text%__1cLLIR_EmitterHopr2int6MpnLLIR_OprDesc__i_; +text: .text%__1cILIR_ListJint2stack6Mii_v_: c1_LIREmitter.o; +text: .text%__1cLInstructionGas_Phi6M_pnDPhi__: c1_Loops.o; +text: .text%__1cNCachingChangeFvisit6MpnSInstructionVisitor__v_: c1_Loops.o; +text: .text%__1cIValueGenQdo_CachingChange6MpnNCachingChange__v_; +text: .text%__1cIValueGenPdo_ArithmeticOp6MpnMArithmeticOp__v_; +text: .text%__1cIValueGenTdo_ArithmeticOp_Int6MpnMArithmeticOp__v_; +text: .text%__1cIValueGenOload_item_hint6MpnEItem_pk1_v_; +text: .text%__1cEItemRget_jint_constant6kM_i_; +text: .text%__1cLLIR_EmitterRarithmetic_op_int6MnJBytecodesECode_pnLLIR_OprDesc_44nFRInfo__v_; +text: .text%__1cLLIR_EmitterNarithmetic_op6MnJBytecodesECode_pnLLIR_OprDesc_44inFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cLLIR_EmitterYstrength_reduce_multiply6MpnLLIR_OprDesc_i22_i_; +text: .text%__1cILIR_ListHreg2reg6MnFRInfo_1nJBasicType__v_: c1_LIREmitter_i486.o; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_LIREmitter_i486.o; +text: .text%__1cLlog2_intptr6Fi_i_: c1_LIREmitter_i486.o; +text: .text%__1cILIR_ListKshift_left6MpnLLIR_OprDesc_222_v_; +text: .text%__1cILIR_ListDsub6MpnLLIR_OprDesc_22pnMCodeEmitInfo__v_: c1_LIREmitter_i486.o; +text: .text%__1cIValueGenWcan_inline_as_constant6MpnEItem__i_; +text: .text%__1cIRegAllocPget_register_rc6kMnFRInfo__i_; +text: .text%__1cLGetRefCountGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cILIR_ListHreg2reg6MnFRInfo_1nJBasicType__v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListDadd6MpnLLIR_OprDesc_22_v_: c1_LIREmitter.o; +text: .text%__1cIValueGenOdo_LoadIndexed6MpnLLoadIndexed__v_; +text: .text%__1cJValueTypeLas_LongType6M_pnILongType__: c1_ValueType.o; +text: .text%__1cLAccessArrayKlock_stack6kM_pnKValueStack__: c1_Instruction.o; +text: .text%__1cMCodeEmitInfoVfill_expression_stack6M_v_; +text: .text%__1cLLIR_EmitterRarray_range_check6MpnLLIR_OprDesc_2pnMCodeEmitInfo_4_v_; +text: .text%__1cORangeCheckStub2t6MpnMCodeEmitInfo_nFRInfo_ii_v_; +text: .text%__1cMCodeEmitInfo2t6Mp0i_v_; +text: .text%__1cLLIR_EmitterLcmp_reg_mem6MnMLIR_OpBranchNLIR_Condition_nFRInfo_3inJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListLcmp_reg_mem6MnMLIR_OpBranchNLIR_Condition_nFRInfo_pnLLIR_Address_nJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListGbranch6MnMLIR_OpBranchNLIR_Condition_pnICodeStub__v_; +text: .text%__1cMLIR_OpBranch2t6Mn0ANLIR_Condition_pnICodeStub_pnMCodeEmitInfo__v_; +text: .text%__1cLLIR_EmitterMindexed_load6MnFRInfo_nJBasicType_pnLLIR_OprDesc_4pnMCodeEmitInfo__v_; +text: .text%__1cLLIR_EmitterNarray_address6MpnLLIR_OprDesc_2inJBasicType__pnLLIR_Address__; +text: .text%__1cLLIR_AddressFscale6FnJBasicType__n0AFScale__; +text: .text%__1cILIR_ListEmove6MpnLLIR_Address_pnLLIR_OprDesc_pnMCodeEmitInfo__v_: c1_LIREmitter_i486.o; +text: .text%__1cIRegAllocNoops_in_spill6kM_pnIintStack__; +text: .text%__1cIRegAllocRoops_in_registers6kM_pnPRInfoCollection__; +text: .text%__1cIValueGenbDsafepoint_poll_needs_register6F_i_; +text: .text%__1cILIR_ListJsafepoint6MnFRInfo_pnMCodeEmitInfo__v_: c1_CodeGenerator.o; +text: .text%__1cLLIR_EmitterHgoto_op6MpnKBlockBegin_pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListEjump6MpnKBlockBegin_pnMCodeEmitInfo__v_; +text: .text%__1cIValueGenNdo_StoreField6MpnKStoreField__v_; +text: .text%__1cIValueGenOscratch1_RInfo6kM_nFRInfo__; +text: .text%__1cIValueGenUprefer_alu_registers6kM_i_; +text: .text%__1cLLIR_EmitterLfield_store6MpnHciField_pnLLIR_OprDesc_i4iipnMCodeEmitInfo_nFRInfo__v_; +text: .text%__1cILIR_ListNstore_mem_reg6MnFRInfo_1inJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cIValueGenJdo_Return6MpnGReturn__v_; +text: .text%__1cJValueTypeLas_VoidType6M_pnIVoidType__: c1_ValueType.o; +text: .text%__1cIValueGenTresult_register_for6FpnJValueType_i_nFRInfo__; +text: .text%__1cIValueGenMreturn1RInfo6F_nFRInfo__; +text: .text%__1cIValueGenPload_item_force6MpnEItem_nFRInfo__v_; +text: .text%__1cIValueGenPlock_spill_temp6MpnLInstruction_nFRInfo__v_; +text: .text%__1cIRegAllocJlock_temp6MpnLInstruction_nFRInfo__v_; +text: .text%__1cLLIR_EmitterJreturn_op6MpnLLIR_OprDesc__v_; +text: .text%__1cNCodeGeneratorXclear_instruction_items6FpnKBlockBegin__v_; +text: .text%__1cQLIR_LocalCaching2t6MpnCIR__v_; +text: .text%__1cQLIR_LocalCachingQpreferred_locals6MpknIciMethod__pnMLocalMapping__; +text: .text%__1cMLocalMappingQinit_cached_regs6M_v_; +text: .text%__1cPRegisterManager2t6M_v_; +text: .text%__1cMLocalMappingNget_cache_reg6kMi_nFRInfo__; +text: .text%__1cQLIR_LocalCachingVcompute_cached_locals6M_v_; +text: .text%__1cQLIR_LocalCachingMcache_locals6M_v_; +text: .text%__1cLInstructionQas_CachingChange6M_pnNCachingChange__: c1_IR.o; +text: .text%__1cLInstructionQas_CachingChange6M_pnNCachingChange__: c1_Canonicalizer.o; +text: .text%__1cNCachingChangeQas_CachingChange6M_p0_: c1_Loops.o; +text: .text%__1cRBlockListScanInfo2t6MpnJBlockList__v_: c1_CacheLocals.o; +text: .text%__1cOLIR_OprRefList2t6M_v_: c1_CacheLocals.o; +text: .text%__1cRBlockListScanInfoItraverse6MpnKBlockBegin_pnKLIR_OpList__v_: c1_CacheLocals.o; +text: .text%__1cLLIR_OpLabelFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cHLIR_Op1Fvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cPRegisterManagerElock6MnFRInfo__v_; +text: .text%__1cHLIR_Op2Fvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cMLIR_OpBranchFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cORangeCheckStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_i486.o; +text: .text%__1cQLIR_OpVisitStateGappend6MnFRInfo__v_: c1_CodeStubs_i486.o; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_CodeStubs_i486.o; +text: .text%__1cNc1_AllocTableFmerge6Mp0_v_; +text: .text%__1cGLIR_OpFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cQLIR_LocalCachingXcache_locals_for_blocks6MpnJBlockList_pnPRegisterManager_i_pnMLocalMapping__; +text: .text%__1cLInstructionNas_StateSplit6M_pnKStateSplit__: c1_Loops.o; +text: .text%__1cLInstructionOas_AccessField6M_pnLAccessField__: c1_Loops.o; +text: .text%__1cLInstructionOas_AccessLocal6M_pnLAccessLocal__: c1_Loops.o; +text: .text%__1cKStateSplitNas_StateSplit6M_p0_: c1_IR.o; +text: .text%__1cLInstructionJas_Invoke6M_pnGInvoke__: c1_IR.o; +text: .text%__1cLInstructionLas_NewArray6M_pnINewArray__: c1_IR.o; +text: .text%__1cLInstructionOas_NewInstance6M_pnLNewInstance__: c1_IR.o; +text: .text%__1cLInstructionQas_AccessMonitor6M_pnNAccessMonitor__: c1_IR.o; +text: .text%__1cLInstructionMas_Intrinsic6M_pnJIntrinsic__: c1_IR.o; +text: .text%__1cKScanBlocksQmost_used_locals6M_pnKALocalList__; +text: .text%__1cKScanBlocksMint_count_at6kMi_i_; +text: .text%__1cKScanBlocksIcount_at6kMnIValueTag_i_i_; +text: .text%__1cKScanBlocksJget_array6kMnIValueTag__pknIintStack__; +text: .text%__1cKScanBlocksNlong_count_at6kMi_i_; +text: .text%__1cKScanBlocksMobj_count_at6kMi_i_; +text: .text%__1cKScanBlocksLis_obj_only6kMi_i_; +text: .text%__1cKScanBlocksLis_int_only6kMi_i_; +text: .text%__1cGALocalUsort_by_access_count6Fpp02_i_: c1_ScanBlocks.o; +text: .text%__1cQLIR_LocalCachingPcompute_caching6MpnKALocalList_pnPRegisterManager__pnMLocalMapping__; +text: .text%__1cPRegisterManagerMnum_free_cpu6M_i_; +text: .text%__1cMLocalMappingNget_cache_reg6kMinIValueTag__nFRInfo__; +text: .text%__1cPRegisterManagerMhas_free_reg6MnIValueTag__i_; +text: .text%__1cPRegisterManagerNlock_free_reg6MnIValueTag__nFRInfo__; +text: .text%__1cQLIR_LocalCachingQadd_at_all_names6FpnPRInfoCollection_inFRInfo_pnMWordSizeList__v_; +text: .text%__1cIintStackEgrow6Mki1_v_: c1_CacheLocals.o; +text: .text%__1cMLocalMappingFmerge6Mp0_v_; +text: .text%__1cGALocalNsort_by_index6Fpp02_i_: c1_CacheLocals.o; +text: .text%__1cSLocalMappingSetterIblock_do6MpnKBlockBegin__v_; +text: .text%__1cMLocalMappingEjoin6Mp0_v_; +text: .text%__1cPRegisterManagerLis_free_reg6MnFRInfo__i_; +text: .text%__1cQLIR_LocalCachingYinsert_transition_blocks6M_v_; +text: .text%__1cPBlockTransitionIblock_do6MpnKBlockBegin__v_: c1_CacheLocals.o; +text: .text%__1cGLIR_OpLas_OpBranch6M_pnMLIR_OpBranch__: c1_LIR.o; +text: .text%__1cMLocalMappingPemit_transition6FpnILIR_List_p03pnCIR__v_; +text: .text%__1cCIRThighest_used_offset6kM_i_; +text: .text%__1cILIR_ListQreg2single_stack6MnFRInfo_inJBasicType__v_: c1_CacheLocals.o; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_CacheLocals.o; +text: .text%__1cILIR_ListQsingle_stack2reg6MinFRInfo_nJBasicType__v_; +text: .text%__1cLInstructionQas_CachingChange6M_pnNCachingChange__: c1_Instruction.o; +text: .text%__1cLCompilationbBemit_code_prolog_non_native6MpnIFrameMap__v_; +text: .text%__1cHIRScopeJmax_stack6kM_i_; +text: .text%__1cNLIR_Optimizer2t6MpnCIR__v_; +text: .text%__1cRLIR_PeepholeState2t6M_v_; +text: .text%__1cRLIR_PeepholeStateKinitialize6MpnMLocalMapping__v_; +text: .text%__1cRLIR_PeepholeStateMclear_values6M_v_; +text: .text%__1cOLIR_OprRefList2t6M_v_: c1_LIROptimizer.o; +text: .text%__1cNLIR_OptimizerIoptimize6M_v_; +text: .text%__1cNLIR_OptimizerIoptimize6MpnJBlockList__v_; +text: .text%__1cNLIR_OptimizerIoptimize6MpnKBlockBegin__v_; +text: .text%__1cNLIR_OptimizerMblock_prolog6M_v_; +text: .text%__1cNLIR_OptimizerKprocess_op6M_v_; +text: .text%__1cGLIR_OpGas_Op16M_pnHLIR_Op1__: c1_LIR.o; +text: .text%__1cLLIR_OpLabelKas_OpLabel6M_p0_: c1_LIR.o; +text: .text%__1cRLIR_PeepholeStateVfinish_forward_branch6MpnFLabel__v_; +text: .text%__1cJLabelListIindex_of6kMkpnFLabel__i_: c1_LIROptimizer.o; +text: .text%__1cRLIR_PeepholeStateYset_disable_optimization6Mi_v_; +text: .text%__1cLLIR_OpLabelJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerMemit_opLabel6MpnLLIR_OpLabel__v_; +text: .text%__1cNLIR_OptimizerFvisit6M_v_: c1_LIROptimizer_i486.o; +text: .text%__1cHLIR_Op0Jemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerIemit_op06MpnHLIR_Op0__v_; +text: .text%__1cHLIR_Op2Jemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerIemit_op26MpnHLIR_Op2__v_; +text: .text%__1cNLIR_OptimizerKhandle_opr6MpnLLIR_OprDesc_nQLIR_OpVisitStateHOprMode__2_; +text: .text%__1cNLIR_OptimizerJis_cached6MpnLLIR_OprDesc__i_; +text: .text%__1cNLIR_OptimizerUrecord_opr_reference6MpnLLIR_OprDesc__v_; +text: .text%__1cRLIR_PeepholeStateUrecord_opr_reference6MpnLLIR_OprDesc__i_; +text: .text%__1cRLIR_PeepholeStateLdefining_op6MpnLLIR_OprDesc__i_; +text: .text%__1cRLIR_PeepholeStateJreg2index6MpnLLIR_OprDesc__i_; +text: .text%__1cIintStackEgrow6Mki1_v_: c1_LIROptimizer.o; +text: .text%__1cNLIR_OptimizerMblock_epilog6M_v_; +text: .text%__1cRLIR_PeepholeStateRis_safe_to_delete6kMi_i_; +text: .text%__1cHLIR_Op1Gas_Op16M_p0_: c1_LIR.o; +text: .text%__1cHLIR_Op1Jemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerIemit_op16MpnHLIR_Op1__v_; +text: .text%__1cNLIR_OptimizerMprocess_move6MpnHLIR_Op1__v_; +text: .text%__1cMLocalMappingNget_cache_reg6kMpnLLIR_OprDesc__2_; +text: .text%__1cRLIR_PeepholeStateTmark_safe_to_delete6Mi_v_; +text: .text%__1cNLIR_OptimizerRreplace_stack_opr6MpnLLIR_OprDesc__2_; +text: .text%__1cNLIR_OptimizerNoptimize_move6MpnHLIR_Op1_rpnLLIR_OprDesc_5_i_; +text: .text%__1cRLIR_PeepholeStatebFequivalent_register_or_constant6MpnLLIR_OprDesc__2_; +text: .text%__1cRLIR_PeepholeStateOequivalent_opr6MpnLLIR_OprDesc__2_; +text: .text%__1cNLIR_OptimizerKmaybe_opto6MpnLLIR_OprDesc_2_2_: c1_LIROptimizer_i486.o; +text: .text%__1cNLIR_OptimizerMis_cache_reg6MpnLLIR_OprDesc__i_; +text: .text%__1cMLocalMappingMis_cache_reg6kMpnLLIR_OprDesc__i_; +text: .text%__1cMLocalMappingMis_cache_reg6kMnFRInfo__i_; +text: .text%__1cRLIR_PeepholeStateSequivalent_address6MpnLLIR_OprDesc__2_; +text: .text%__1cNLIR_OptimizerRresult_substitute6M_v_; +text: .text%__1cNLIR_OptimizerRnext_op_with_code6MnILIR_Code__pnGLIR_Op__; +text: .text%__1cNLIR_OptimizerFop_at6Mi_pnGLIR_Op__; +text: .text%__1cRLIR_PeepholeStateMkill_operand6MpnLLIR_OprDesc_i_v_; +text: .text%__1cRLIR_PeepholeStateQkill_equivalents6MpnLLIR_OprDesc__v_; +text: .text%__1cRLIR_PeepholeStateNkill_register6Mi_v_; +text: .text%__1cRLIR_PeepholeStateSrecord_defining_op6MpnLLIR_OprDesc_i_v_; +text: .text%__1cRLIR_PeepholeStatePset_defining_op6Mii_v_; +text: .text%__1cRLIR_PeepholeStateHdo_move6MpnLLIR_OprDesc_2_v_; +text: .text%__1cLLIR_OprListEgrow6MkikpnLLIR_OprDesc__v_: c1_LIROptimizer.o; +text: .text%__1cLLIR_AddressKas_address6M_p0_: c1_LIR.o; +text: .text%__1cRLIR_PeepholeStateTequivalent_register6MpnLLIR_OprDesc__2_; +text: .text%__1cKLIR_OprPtrLas_constant6M_pnJLIR_Const__: c1_LIR.o; +text: .text%__1cNLIR_OptimizerKallow_opto6M_i_; +text: .text%__1cNLIR_OptimizerLrecord_opto6MpnLLIR_OprDesc_2_2_; +text: .text%__1cLLIR_AddressEtype6kM_nJBasicType__: c1_LIR.o; +text: .text%__1cRLIR_PeepholeStateNincrement_ref6Mi_v_; +text: .text%__1cKLIR_OprPtrKas_address6M_pnLLIR_Address__: c1_CacheLocals.o; +text: .text%__1cMLIR_OpBranchLas_OpBranch6M_p0_: c1_LIR.o; +text: .text%__1cMLIR_OpBranchJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerNemit_opBranch6MpnMLIR_OpBranch__v_; +text: .text%__1cNLIR_OptimizerQopr_live_on_exit6MpnLLIR_OprDesc__i_; +text: .text%__1cNResourceArrayJremove_at6MIi_v_; +text: .text%__1cRLIR_PeepholeStateLstack2index6MpnLLIR_OprDesc__i_; +text: .text%__1cRLIR_PeepholeStatePkill_stack_slot6Mi_v_; +text: .text%__1cRLIR_PeepholeStatebCequivalent_register_or_stack6MpnLLIR_OprDesc__2_; +text: .text%__1cNLIR_OptimizerKmaybe_opto6MpnLLIR_OprDesc_2_2_: c1_LIROptimizer.o; +text: .text%__1cNLIR_OptimizerLhandle_info6MpnMCodeEmitInfo__v_; +text: .text%__1cMCodeEmitInfoRset_local_mapping6MpnMLocalMapping__v_; +text: .text%__1cNLIR_OptimizerUrecord_register_oops6MpnMCodeEmitInfo__v_; +text: .text%__1cNLIR_OptimizerOemit_code_stub6MpnICodeStub__v_; +text: .text%__1cLCompilationOemit_code_body6MpnLCodeOffsets__i_; +text: .text%__1cNLIR_Assembler2t6MpnLCompilation_pnLCodeOffsets__v_; +text: .text%__1cNConstantTable2t6M_v_; +text: .text%__1cNLIR_AssemblerJemit_code6MpnJBlockList__v_; +text: .text%__1cQCollectConstantsIblock_do6MpnKBlockBegin__v_: c1_LIRAssembler.o; +text: .text%__1cLInstructionLas_Constant6M_pnIConstant__: c1_IR.o; +text: .text%__1cLInstructionLas_Constant6M_pnIConstant__: c1_Canonicalizer.o; +text: .text%__1cLInstructionLas_Constant6M_pnIConstant__: c1_Instruction.o; +text: .text%__1cJValueTypeNas_DoubleType6M_pnKDoubleType__: c1_Canonicalizer.o; +text: .text%__1cJValueTypeMas_FloatType6M_pnJFloatType__: c1_Canonicalizer.o; +text: .text%__1cLInstructionLas_Constant6M_pnIConstant__: c1_Loops.o; +text: .text%__1cNLIR_AssemblerOemit_constants6M_v_; +text: .text%__1cNConstantTableMemit_entries6MpnOMacroAssembler_i_v_; +text: .text%__1cLLIR_CodeGenIblock_do6MpnKBlockBegin__v_; +text: .text%__1cNLIR_AssemblerPcheck_codespace6M_v_; +text: .text%__1cNLIR_AssemblerMemit_opLabel6MpnLLIR_OpLabel__v_; +text: .text%__1cNLIR_AssemblerIemit_op06MpnHLIR_Op0__v_; +text: .text%__1cNLIR_AssemblerIemit_op26MpnHLIR_Op2__v_; +text: .text%__1cNLIR_AssemblerMneeds_icache6kMpnIciMethod__i_; +text: .text%__1cFRInfoLas_register6kM_pnMRegisterImpl__; +text: .text%__1cNLIR_AssemblerMcheck_icache6MpnMRegisterImpl_2_i_; +text: .text%__1cRC1_MacroAssemblerSinline_cache_check6MpnMRegisterImpl_2_v_; +text: .text%__1cRC1_MacroAssemblerOverified_entry6M_v_; +text: .text%__1cNLIR_AssemblerLbuild_frame6M_v_; +text: .text%__1cNLIR_AssemblerbBinitial_frame_size_in_bytes6M_i_; +text: .text%__1cIFrameMapJframesize6kM_i_; +text: .text%__1cRC1_MacroAssemblerLbuild_frame6Mi_v_; +text: .text%__1cRAbstractAssemblerbDgenerate_stack_overflow_check6Mi_v_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: c1_Compiler.o; +text: .text%__1cNLIR_AssemblerVsetup_locals_at_entry6M_v_; +text: .text%__1cIFrameMapYsignature_type_array_for6FpknIciMethod__pnNBasicTypeList__; +text: .text%__1cIFrameMapScalling_convention6FpknIciMethod_pnIintArray__pnRCallingConvention__; +text: .text%__1cIFrameMapScalling_convention6FirknOBasicTypeArray_pnIintArray__pnRCallingConvention__; +text: .text%__1cIintArray2t6Mki1_v_: c1_FrameMap_i486.o; +text: .text%__1cIFrameMapRname_for_argument6Fi_i_; +text: .text%__1cIFrameMapSfp_offset_for_name6kMiii_i_; +text: .text%__1cIFrameMapPnum_local_names6kM_i_; +text: .text%__1cIFrameMapNlocal_to_slot6kMii_i_; +text: .text%__1cIFrameMapSfp_offset_for_slot6kMi_i_; +text: .text%__1cQArgumentLocation2t6Mci_v_: c1_FrameMap_i486.o; +text: .text%__1cQArgumentLocationSset_stack_location6Mi_v_; +text: .text%__1cIFrameMapQaddress_for_name6kMiii_nHAddress__; +text: .text%__1cIFrameMapQmake_new_address6kMi_nHAddress__; +text: .text%__1cNLIR_AssemblerIemit_op16MpnHLIR_Op1__v_; +text: .text%__1cNLIR_AssemblerHmove_op6MpnLLIR_OprDesc_2nJBasicType_nHLIR_Op1NLIR_PatchCode_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerHmem2reg6MpnLLIR_Address_nFRInfo_nJBasicType_nHLIR_Op1NLIR_PatchCode_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerKas_Address6MpnLLIR_Address__nHAddress__; +text: .text%__1cNLIR_AssemblerHcomp_op6MnMLIR_OpBranchNLIR_Condition_pnLLIR_OprDesc_4nJBasicType__v_; +text: .text%__1cNLIR_AssemblerNemit_opBranch6MpnMLIR_OpBranch__v_; +text: .text%__1cNLIR_AssemblerJreg2stack6MnFRInfo_inJBasicType__v_; +text: .text%__1cNLIR_AssemblerLconst2stack6MpnJLIR_Const_i_v_; +text: .text%__1cNLIR_AssemblerJstack2reg6MpnLLIR_OprDesc_2nJBasicType__v_; +text: .text%__1cNLIR_AssemblerHreg2reg6MnFRInfo_1_v_; +text: .text%__1cNLIR_AssemblerJmove_regs6MpnMRegisterImpl_2_v_; +text: .text%__1cNLIR_AssemblerIshift_op6MnILIR_Code_nFRInfo_i2_v_; +text: .text%__1cNLIR_AssemblerIarith_op6MnILIR_Code_pnLLIR_OprDesc_33pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerbIadd_debug_info_for_null_check_here6MpnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerLcode_offset6kM_i_; +text: .text%__1cNLIR_AssemblerbDadd_debug_info_for_null_check6MipnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerOemit_code_stub6MpnICodeStub__v_; +text: .text%__1cVImplicitNullCheckStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cNLIR_AssemblerCpc6kM_pC_; +text: .text%__1cICodeStubLset_code_pc6MpC_v_: c1_CodeStubs_i486.o; +text: .text%__1cICodeStubMis_call_stub6kM_i_: c1_CodeStubs_i486.o; +text: .text%__1cNCodeStubArrayIindex_of6kMkpnICodeStub__i_: c1_LIRAssembler.o; +text: .text%__1cORangeCheckStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cNLIR_AssemblerOsafepoint_poll6MnFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerZadd_debug_info_for_branch6MpnMCodeEmitInfo__v_; +text: .text%__1cPpoll_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cMCodeEmitInfoRrecord_debug_info6MpnYDebugInformationRecorder_ii_v_; +text: .text%__1cMCodeEmitInfoHoop_map6M_pnGOopMap__; +text: .text%__1cMCodeEmitInfoScompute_debug_info6M_v_; +text: .text%__1cMCodeEmitInfoOcreate_oop_map6M_pnGOopMap__; +text: .text%__1cIFrameMapRoop_map_arg_count6M_i_; +text: .text%__1cMCodeEmitInfoTrecord_spilled_oops6kMpnIFrameMap_pnGOopMap__v_; +text: .text%__1cKciLocalMapNindex_for_bci6kMi_i_; +text: .text%__1cSciLocalMapIteratorJfind_next6M_v_: c1_LIREmitter.o; +text: .text%__1cJLocalSlotIfor_type6MpnJValueType_ii_pnFLocal__: c1_LIREmitter.o; +text: .text%__1cMCodeEmitInfoNget_cache_reg6kMinIValueTag__nFRInfo__; +text: .text%__1cIFrameMapTsingle_word_regname6kMi_nHOptoRegEName__; +text: .text%__1cIFrameMapMfp2sp_offset6kMi_i_; +text: .text%__1cGOopMapHset_oop6MnHOptoRegEName_ii_v_; +text: .text%__1cMCodeEmitInfoVlir_stack2value_stack6MpnNGrowableArray4CpnLLIR_OprDesc____pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cMCodeEmitInfobCcompute_debug_info_for_scope6MpnHIRScope_ipnNGrowableArray4CpnKScopeValue___inGValues_i_pnQIRScopeDebugInfo__; +text: .text%__1cMCodeEmitInfobCscope_value_for_local_offset6MinILocationEType_ppnKScopeValue__4_; +text: .text%__1cMCodeEmitInfobEget_cache_reg_for_local_offset6kMi_nFRInfo__; +text: .text%__1cMLocalMappingbEget_cache_reg_for_local_offset6kMi_nFRInfo__; +text: .text%__1cMCodeEmitInfoZlocation_for_local_offset6MinILocationEType__1_; +text: .text%__1cIFrameMapZlocation_for_local_offset6kMinILocationEType_p1_i_; +text: .text%__1cIFrameMapWlocation_for_fp_offset6kMinILocationEType_p1_i_; +text: .text%__1cILocationVlegal_offset_in_bytes6Fi_i_; +text: .text%__1cMCodeEmitInfoYscope_value_for_register6MnFRInfo_ppnKScopeValue_4nILocationEType__v_; +text: .text%__1cGOopMapJdeep_copy6M_p0_; +text: .text%__1cGOopMap2t6Mn0ANDeepCopyToken_p0_v_; +text: .text%__1cMOopMapStream2t6MpnGOopMap__v_; +text: .text%__1cMOopMapStreamJfind_next6M_v_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: oopMap.o; +text: .text%__1cYDebugInformationRecorderNadd_safepoint6MiipnGOopMap__v_; +text: .text%__1cYDebugInformationRecorderLcheck_phase6Mn0AFPhase__v_; +text: .text%__1cYDebugInformationRecorderKadd_oopmap6MiipnGOopMap__v_; +text: .text%__1cYDebugInformationRecorderTcreate_scope_values6MpnNGrowableArray4CpnKScopeValue____pnKDebugToken__; +text: .text%__1cYDebugInformationRecorderWserialize_scope_values6MpnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: debugInfoRec.o; +text: .text%__1cNLocationValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: debugInfo.o; +text: .text%__1cILocationIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: location.o; +text: .text%__1cYDebugInformationRecorderVcreate_monitor_values6MpnNGrowableArray4CpnMMonitorValue____pnKDebugToken__; +text: .text%__1cYDebugInformationRecorderYserialize_monitor_values6MpnNGrowableArray4CpnMMonitorValue____i_; +text: .text%__1cYDebugInformationRecorderOdescribe_scope6MpnIciMethod_ipnKDebugToken_44_v_; +text: .text%__1cYDebugInformationRecorderNappend_handle6MpnI_jobject__i_; +text: .text%__1cLOopRecorderOallocate_index6MpnI_jobject__i_; +text: .text%__1cLCompilationbEadd_exception_handlers_for_pco6MiipnOExceptionScope__v_; +text: .text%__1cNExceptionInfo2t6MiipnOExceptionScope__v_; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cNLIR_AssemblerHreg2mem6MnFRInfo_pnLLIR_Address_nJBasicType_nHLIR_Op1NLIR_PatchCode_pnMCodeEmitInfo__v_; +text: .text%__1cLLIR_OprDescGis_oop6kM_i_; +text: .text%__1cNLIR_AssemblerJreturn_op6MnFRInfo_i_v_; +text: .text%__1cWpoll_return_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cRC1_MacroAssemblerLmethod_exit6Mi_v_; +text: .text%__1cLCompilationQemit_code_epilog6MpnNLIR_Assembler__v_; +text: .text%__1cNLIR_AssemblerUemit_slow_case_stubs6M_v_; +text: .text%__1cNLIR_AssemblerKemit_stubs6MpnMCodeStubList__v_; +text: .text%__1cVImplicitNullCheckStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cWImplicitExceptionTableGappend6MII_v_; +text: .text%__1cZresource_reallocate_bytes6FpcII_0_; +text: .text%__1cFArenaIArealloc6MpvII_1_; +text: .text%__1cNLIR_AssemblerNadd_call_info6MipnMCodeEmitInfo__v_; +text: .text%__1cOdummy_location6FnIValueTag__pnKScopeValue__: c1_LIREmitter.o; +text: .text%__1cQConstantIntValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cORangeCheckStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cNLIR_AssemblerWemit_exception_handler6M_i_; +text: .text%__1cRC1_MacroAssemblerRexception_handler6Mii_v_; +text: .text%__1cNLIR_AssemblerPemit_call_stubs6M_v_; +text: .text%__1cNLIR_AssemblerbCmaybe_adjust_stack_alignment6MpnIciMethod__v_; +text: .text%__1cKreal_index6FpnIFrameMap_i_i_: c1_LIRAssembler_i486.o; +text: .text%__1cLCompilationbEgenerate_exception_range_table6M_v_; +text: .text%__1cOExceptionScopeGequals6kMp0_i_; +text: .text%__1cLCompilationbBadd_exception_range_entries6MiipnOExceptionScope_ip2pi_v_; +text: .text%__1cTExceptionRangeTablebCcompute_modified_at_call_pco6Fii_i_; +text: .text%__1cOExceptionScopeMcaller_scope6kM_p0_; +text: .text%__1cLLIR_EmitterKframe_size6M_i_; +text: .text%__1cNLIR_Assembler2T6M_v_; +text: .text%__1cLCompilationMinstall_code6MpnLCodeOffsets_i_v_; +text: .text%__1cFciEnvPregister_method6MpnIciMethod_iiiiiipnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler_ii_v_; +text: .text%__1cFciEnvbOcheck_for_system_dictionary_modification6MpnIciMethod__v_; +text: .text%__1cFciEnvbUsystem_dictionary_modification_counter_changed6M_i_; +text: .text%__1cHnmethodLnew_nmethod6FnMmethodHandle_iiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__p0_; +text: .text%__1cLOopRecorderIoop_size6M_i_; +text: .text%__1cYDebugInformationRecorderIpcs_size6M_i_; +text: .text%__1cYDebugInformationRecorderJdata_size6M_i_; +text: .text%__1cHnmethod2n6FIi_pv_; +text: .text%__1cHnmethod2t6MpnNmethodOopDesc_iiiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__v_; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: codeBlob.o; +text: .text%__1cLPcDescCache2t6M_v_; +text: .text%__1cHnmFlagsFclear6M_v_; +text: .text%__1cYDebugInformationRecorderHcopy_to6MpnHnmethod__v_; +text: .text%__1cLOopRecorderHcopy_to6MpnICodeBlob__v_; +text: .text%__1cICodeBlobJcopy_oops6MppnI_jobject_i_v_; +text: .text%__1cIUniverseMnon_oop_word6F_pv_; +text: .text%__1cHnmethodQcopy_scopes_data6MpCi_v_; +text: .text%__1cGPcDesc2t6Miii_v_; +text: .text%__1cHnmethodKcopy_pc_at6MipnGPcDesc__v_; +text: .text%__1cHnmethodSresolve_JNIHandles6M_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: nmethod.o; +text: .text%__1cJCodeCacheGcommit6FpnICodeBlob__v_; +text: .text%__1cHnmethodKis_nmethod6kM_i_: nmethod.o; +text: .text%__1cHnmethodUnumber_of_dependents6kM_i_: nmethod.o; +text: .text%__1cFVTuneOcreate_nmethod6FpnHnmethod__v_; +text: .text%__1cWImplicitExceptionTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cTExceptionRangeTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cKNativeJumpbEcheck_verified_entry_alignment6FpC1_v_; +text: .text%__1cGEventsDlog6FpkcE_v_: nmethod.o; +text: .text%__1cFciEnvKcompile_id6M_I_; +text: .text%__1cNmethodOopDescIset_code6MpnHnmethod__v_; +text: .text%__1cFciEnvbFpost_compiled_method_load_event6MpnHnmethod__v_; +text: .text%__1cLCompilation2T6M_v_; +text: .text%__1cFArena2T6M_v_; +text: .text%__1cFArenaRdestruct_contents6M_v_; +text: .text%__1cICHeapObj2k6Fpv_v_; +text: .text%__1cTExceptionRangeTable2T6M_v_; +text: .text%__1cFciEnvVnum_inlined_bytecodes6kM_i_; +text: .text%__1cMelapsedTimerDadd6M0_v_; +text: .text%__1cFciEnv2T6M_v_; +text: .text%__1cNCompileBrokerUpop_jni_handle_block6F_v_; +text: .text%__1cNCompileBrokerScollect_statistics6FpnOCompilerThread_nMelapsedTimer_pnLCompileTask__v_; +text: .text%__1cHnmethodKtotal_size6kM_i_; +text: .text%__1cHnmethodJcode_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodOexception_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodJstub_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodQscopes_data_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodPscopes_pcs_size6kM_i_: nmethod.o; +text: .text%__1cLAccessFlagsRatomic_clear_bits6Mi_v_; +text: .text%__1cSCompileTaskWrapper2T6M_v_; +text: .text%__1cNCompileBrokerJfree_task6FpnLCompileTask__v_; +text: .text%__1cLCompileTaskEfree6M_v_; +text: .text%__1cKJNIHandlesOdestroy_global6FpnI_jobject_i_v_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: reflection.o; +text: .text%__1cNArgumentCountDset6MinJBasicType__v_: reflection.o; +text: .text%__1cZget_mirror_from_signature6FnMmethodHandle_pnPSignatureStream_pnGThread__pnHoopDesc__; +text: .text%__1cPjava_lang_ClassQprimitive_mirror6FnJBasicType__pnHoopDesc__; +text: .text%__1cNSignatureInfoHdo_long6M_v_: reflection.o; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: reflection.o; +text: .text%__1cPSignatureStreamJas_symbol6MpnGThread__pnNsymbolOopDesc__; +text: .text%__1cKReflectionbFbasic_type_mirror_to_basic_type6FpnHoopDesc_pnGThread__nJBasicType__; +text: .text%__1cPjava_lang_ClassOprimitive_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cQSystemDictionaryQjava_mirror_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cKReflectionTunbox_for_primitive6FpnHoopDesc_pnGjvalue_pnGThread__nJBasicType__; +text: .text%__1cXjava_lang_boxing_objectJget_value6FpnHoopDesc_pnGjvalue__nJBasicType__; +text: .text%__1cUGenericGrowableArrayGgrow646Mi_v_; +text: .text%__1cRComputeEntryStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cOGenerateOopMapJppdupswap6Mipkc_v_; +text: .text%__1cOGenerateOopMapJdo_method6Miiii_v_; +text: .text%__1cQComputeCallStackHdo_void6M_v_: generateOopMap.o; +text: .text%__1cQComputeCallStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cLciSignatureHtype_at6kMi_pnGciType__; +text: .text%__1cLInstructionMas_CompareOp6M_pnJCompareOp__: c1_GraphBuilder.o; +text: .text%__1cLInstructionNas_InstanceOf6M_pnKInstanceOf__: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderMnew_instance6Mi_v_; +text: .text%__1cQciBytecodeStreamJget_klass6kM_pnHciKlass__; +text: .text%__1cQciBytecodeStreamPget_klass_index6kM_i_; +text: .text%__1cVLoaderConstraintTableWfind_constrained_klass6MnMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cIciSymbolHbyte_at6Mi_i_; +text: .text%__1cPciInstanceKlassNloader_handle6M_pnI_jobject__; +text: .text%__1cPciInstanceKlassYprotection_domain_handle6M_pnI_jobject__; +text: .text%__1cGciTypeMis_classless6kM_i_: ciInstanceKlass.o; +text: .text%__1cMGraphBuilderMappend_split6MpnKStateSplit__pnLInstruction__; +text: .text%__1cLNewInstanceFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerOdo_NewInstance6MpnLNewInstance__v_; +text: .text%__1cLInstructionEhash6kM_i_: c1_Instruction.o; +text: .text%__1cKStateSplitNas_StateSplit6M_p0_: c1_Instruction.o; +text: .text%__1cLInstructionJas_Invoke6M_pnGInvoke__: c1_Instruction.o; +text: .text%__1cKValueStackMclear_locals6M_v_; +text: .text%__1cKValueStackMclear_stores6M_v_; +text: .text%__1cKValueStackZpin_stack_for_state_split6M_v_; +text: .text%__1cLNewInstanceIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cMGraphBuilderIstack_op6MnJBytecodesECode__v_; +text: .text%__1cMGraphBuilderGinvoke6MnJBytecodesECode__v_; +text: .text%__1cQciBytecodeStreamKget_method6kM_pnIciMethod__; +text: .text%__1cQciBytecodeStreamQget_method_index6kM_i_; +text: .text%__1cFciEnvTget_method_by_index6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvYget_method_by_index_impl6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvbTget_instance_klass_for_declared_method_holder6FpnHciKlass__pnPciInstanceKlass__; +text: .text%__1cPciObjectFactoryTget_unloaded_method6MpnPciInstanceKlass_pnIciSymbol_4_pnIciMethod__; +text: .text%__1cIciMethod2t6MpnPciInstanceKlass_pnIciSymbol_4_v_; +text: .text%__1cNciMethodKlassEmake6F_p0_; +text: .text%__1cIciObjectMis_classless6kM_i_: ciMethod.o; +text: .text%__1cQciBytecodeStreambAget_declared_method_holder6M_pnHciKlass__; +text: .text%__1cQciBytecodeStreamXget_method_holder_index6M_i_; +text: .text%__1cLciSignatureLreturn_type6kM_pnGciType__; +text: .text%__1cKValueStackNpop_arguments6Mi_pnGValues__; +text: .text%__1cGInvoke2t6MnJBytecodesECode_pnJValueType_pnLInstruction_pnGValues_iiii_v_; +text: .text%__1cGInvokeFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerJdo_Invoke6MpnGInvoke__v_; +text: .text%__1cGInvokeJas_Invoke6M_p0_: c1_Instruction.o; +text: .text%__1cLInstructionMas_LoadLocal6M_pnJLoadLocal__: c1_Instruction.o; +text: .text%__1cGInvokeIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cMGraphBuilderIthrow_op6M_v_; +text: .text%__1cFThrowFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerIdo_Throw6MpnFThrow__v_; +text: .text%__1cIBlockEndLas_BlockEnd6M_p0_: c1_Instruction.o; +text: .text%__1cFThrowIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cLInstructionJas_Return6M_pnGReturn__: c1_Instruction.o; +text: .text%__1cFThrowIas_Throw6M_p0_: c1_Instruction.o; +text: .text%__1cLInstructionFas_If6M_pnCIf__: c1_Instruction.o; +text: .text%__1cLInstructionHas_Goto6M_pnEGoto__: c1_Instruction.o; +text: .text%__1cKStateSplitPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorOdo_NewInstance6MpnLNewInstance__v_; +text: .text%__1cTNullCheckEliminatorShandle_NewInstance6MpnLNewInstance__v_; +text: .text%__1cGInvokePinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorJdo_Invoke6MpnGInvoke__v_; +text: .text%__1cTNullCheckEliminatorNhandle_Invoke6MpnGInvoke__v_; +text: .text%__1cFThrowPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorIdo_Throw6MpnFThrow__v_; +text: .text%__1cPBlockBeginArrayIindex_of6kMkpnKBlockBegin__i_: c1_IR.o; +text: .text%__1cLInstructionGnegate6Fn0AJCondition__1_; +text: .text%__1cFThrowPstate_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cFRInfoIoverlaps6kMk0_i_; +text: .text%__1cIValueGenOdo_NewInstance6MpnLNewInstance__v_; +text: .text%__1cIValueGenVspill_values_on_stack6MpnKValueStack_nFRInfo_i_v_; +text: .text%__1cIRegAllocNlock_register6MpnLInstruction_nFRInfo__v_; +text: .text%__1cHHideReg2t6MpnIValueGen_pnJValueType__v_; +text: .text%__1cHHideReg2T6M_v_; +text: .text%__1cLLIR_EmitterMnew_instance6MnFRInfo_pnPciInstanceKlass_1111pnMCodeEmitInfo__v_; +text: .text%__1cLLIR_EmitterZjobject2reg_with_patching6MnFRInfo_pnIciObject_pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListNoop2reg_patch6MpnI_jobject_nFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cPNewInstanceStub2t6MnFRInfo_pnLLIR_OprDesc_pnPciInstanceKlass_pnMCodeEmitInfo_nIRuntime1GStubID__v_; +text: .text%__1cIValueGenJdo_Invoke6MpnGInvoke__v_; +text: .text%__1cIValueGenWinvoke_visit_arguments6MpnGInvoke_pnRCallingConvention__pnJItemArray__; +text: .text%__1cIValueGenNis_free_rinfo6MnFRInfo__i_; +text: .text%__1cGInvokeRsize_of_arguments6kM_i_; +text: .text%__1cLLIR_EmitterVstore_stack_parameter6MpnLLIR_OprDesc_i_v_; +text: .text%__1cILIR_ListFstore6MpnLLIR_OprDesc_pnLLIR_Address_nJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cHHideReg2t6MpnIValueGen_nFRInfo_i_v_; +text: .text%__1cIValueGenVinvoke_load_arguments6MpnGInvoke_pnJItemArray_pnRCallingConvention__v_; +text: .text%__1cIValueGenPinvoke_do_spill6MpnGInvoke_nFRInfo__v_; +text: .text%__1cIValueGenXis_caller_save_register6FnFRInfo__i_; +text: .text%__1cIValueGenLspill_value6MpnLInstruction__v_; +text: .text%__1cIValueGenKspill_item6MpnEItem__v_; +text: .text%__1cIValueGenQround_spill_item6MpnEItem_i_v_; +text: .text%__1cIRegAllocOget_lock_spill6MpnLInstruction_i_i_; +text: .text%__1cIValueGenJraw_rfree6MpnEItem__v_; +text: .text%__1cLLIR_EmitterFspill6MipnLLIR_OprDesc__v_; +text: .text%__1cIFrameMapKspill_name6kMi_i_; +text: .text%__1cIValueGenQinvoke_do_result6MpnGInvoke_ipnEItem__v_; +text: .text%__1cIVoidTypeLas_VoidType6M_p0_: c1_ValueType.o; +text: .text%__1cLCompilationXlir_opr_for_instruction6MpnLInstruction__pnLLIR_OprDesc__; +text: .text%__1cLLIR_EmitterHcall_op6MnJBytecodesECode_pknOBasicTypeArray_pnMCodeEmitInfo_iiinFRInfo_pnLLIR_OprDesc__v_; +text: .text%__1cILIR_ListKnull_check6MnFRInfo_pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListQcall_opt_virtual6MnFRInfo_pnLLIR_OprDesc_pCpnMCodeEmitInfo_pnOStaticCallStub__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenIdo_Throw6MpnFThrow__v_; +text: .text%__1cLNewInstanceKexact_type6kM_pnGciType__; +text: .text%__1cOExceptionScopeLcould_catch6kMpnPciInstanceKlass_i_i_; +text: .text%__1cIValueGenRexceptionOopRInfo6F_nFRInfo__; +text: .text%__1cIValueGenFsfree6MpnEItem__v_; +text: .text%__1cIRegAllocKfree_spill6MipnJValueType__v_; +text: .text%__1cIRegAllocNis_free_spill6kMipnJValueType__i_; +text: .text%__1cLNewInstanceOas_NewInstance6M_p0_: c1_Instruction.o; +text: .text%__1cIValueGenQexceptionPcRInfo6F_nFRInfo__; +text: .text%__1cILIR_ListPthrow_exception6MnFRInfo_1pnMCodeEmitInfo__v_: c1_CodeGenerator.o; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_CodeGenerator.o; +text: .text%__1cPNewInstanceStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_i486.o; +text: .text%__1cOLIR_OpJavaCallFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cQLIR_OpVisitStateGappend6MnFRInfo__v_: c1_LIR.o; +text: .text%__1cOStaticCallStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_i486.o; +text: .text%__1cIFrameMapWcaller_save_cpu_reg_at6Fi_pnLLIR_OprDesc__; +text: .text%__1cLInstructionLas_NewArray6M_pnINewArray__: c1_Instruction.o; +text: .text%__1cIVoidTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cLInstructionOas_NewInstance6M_pnLNewInstance__: c1_Instruction.o; +text: .text%__1cLInstructionQas_AccessMonitor6M_pnNAccessMonitor__: c1_Instruction.o; +text: .text%__1cRLIR_PeepholeStateHdo_call6M_v_; +text: .text%__1cOLIR_OpJavaCallJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerJemit_call6MpnOLIR_OpJavaCall__v_; +text: .text%__1cNLIR_AssemblerJconst2reg6MpnJLIR_Const_nFRInfo_nHLIR_Op1NLIR_PatchCode_pnMCodeEmitInfo__v_; +text: .text%__1cMPatchingStubQalign_patch_site6MpnOMacroAssembler__v_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_pnI_jobject__v_; +text: .text%__1cOoop_RelocationLunpack_data6M_v_; +text: .text%__1cKRelocationNunpack_2_ints6Mri1_v_: relocInfo.o; +text: .text%__1cOoop_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cOoop_RelocationJpack_data6M_i_; +text: .text%__1cNLIR_AssemblerPpatching_epilog6MpnMPatchingStub_nHLIR_Op1NLIR_PatchCode_pnMRegisterImpl_pnMCodeEmitInfo__v_; +text: .text%__1cMPatchingStubHinstall6MpnOMacroAssembler_nHLIR_Op1NLIR_PatchCode_pnMRegisterImpl_pnMCodeEmitInfo__v_: c1_LIRAssembler.o; +text: .text%__1cNLIR_AssemblerUappend_patching_stub6MpnMPatchingStub__v_; +text: .text%__1cPNewInstanceStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cNLIR_AssemblerJemit_call6MpnOLIR_OpJavaCall__v_; +text: .text%__1cNLIR_AssemblerKalign_call6MnILIR_Code__v_; +text: .text%__1cICodeStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cOStaticCallStubLset_code_pc6MpC_v_: c1_CodeStubs_i486.o; +text: .text%__1cOStaticCallStubMis_call_stub6kM_i_: c1_CodeStubs_i486.o; +text: .text%__1cNLIR_AssemblerEcall6MpCnJrelocInfoJrelocType_pnMCodeEmitInfo__v_; +text: .text%__1cbBopt_virtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cKRelocationJpack_data6M_i_: relocInfo.o; +text: .text%__1cMCodeEmitInfoSappend_scope_value6MpnLLIR_OprDesc_pnNGrowableArray4CpnKScopeValue____v_; +text: .text%__1cMCodeEmitInfoRopr2location_type6MpnLLIR_OprDesc__nILocationEType__; +text: .text%__1cMCodeEmitInfoRlocation_for_name6MinILocationEType_ii_1_; +text: .text%__1cIFrameMapRlocation_for_name6kMinILocationEType_p1ii_i_; +text: .text%__1cNLIR_AssemblerIthrow_op6MnFRInfo_1pnMCodeEmitInfo_i_v_; +text: .text%__1cMCodeEmitInfoQadd_register_oop6MnFRInfo__v_; +text: .text%__1cIintArrayIindex_of6kMki_i_: c1_LIREmitter.o; +text: .text%__1cMCodeEmitInfoYadd_registers_to_oop_map6MpnGOopMap__v_; +text: .text%__1cYinternal_word_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cYinternal_word_RelocationJpack_data6M_i_; +text: .text%__1cJrelocInfoKset_format6Mi_v_; +text: .text%__1cMPatchingStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cRAbstractAssemblerGa_byte6Mi_v_; +text: .text%__1cRNativeGeneralJumpUinsert_unconditional6FpC1_v_; +text: .text%__1cNRelocIterator2t6MpnKCodeBuffer_pC3_v_; +text: .text%__1cJrelocInfobDchange_reloc_info_for_address6FpnNRelocIterator_pCn0AJrelocType_4_v_; +text: .text%__1cJrelocInfoIset_type6Mn0AJrelocType__v_; +text: .text%__1cPNewInstanceStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cJOopMapSetMgrow_om_data6M_v_; +text: .text%__1cOStaticCallStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cWstatic_stub_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cWstatic_stub_RelocationJpack_data6M_i_; +text: .text%__1cNRelocIteratorTadvance_over_prefix6M_v_; +text: .text%__1cOCallRelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cYinternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cYinternal_word_RelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cYinternal_word_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cYinternal_word_RelocationGtarget6M_pC_; +text: .text%__1cODataRelocationJset_value6MpC_v_: relocInfo.o; +text: .text%__1cODataRelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cKRelocationRpd_set_data_value6MpCi_v_; +text: .text%__1cKRelocationSpd_address_in_code6M_ppC_; +text: .text%__1cWstatic_stub_RelocationLunpack_data6M_v_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nmethod.o; +text: .text%__1cOoop_RelocationHoops_do6MpFppnHoopDesc__v_v_; +text: .text%__1cOoop_RelocationIoop_addr6M_ppnHoopDesc__; +text: .text%__1cRresolve_and_patch6FppnHoopDesc__v_; +text: .text%__1cMPeriodicTaskOreal_time_tick6FI_v_; +text: .text%__1cPStatSamplerTaskEtask6M_v_: statSampler.o; +text: .text%__1cLStatSamplerOcollect_sample6F_v_; +text: .text%__1cLStatSamplerLsample_data6FpnMPerfDataList__v_; +text: .text%jni_GetPrimitiveArrayCritical: jni.o; +text: .text%jni_ReleasePrimitiveArrayCritical: jni.o; +text: .text%jni_SetBooleanField: jni.o; +text: .text%__1cNFingerprinterIdo_float6M_v_: dump.o; +text: .text%__1cXNativeSignatureIteratorIdo_float6M_v_: interpreterRuntime.o; +text: .text%JVM_IsNaN; +text: .text%__1cNFingerprinterJdo_double6M_v_: dump.o; +text: .text%__1cXNativeSignatureIteratorJdo_double6M_v_: interpreterRuntime.o; +text: .text%__1cXNativeSignatureIteratorLpass_double6M_v_: interpreterRuntime.o; +text: .text%__1cKExceptionsL_throw_args6FpnGThread_pkcinMsymbolHandle_5pnRJavaCallArguments__v_; +text: .text%jni_GetArrayLength: jni.o; +text: .text%JVM_Read; +text: .text%__1cDhpiEread6FipvI_I_: jvm.o; +text: .text%__1cKJNIHandlesKmake_local6FpnGThread_pnHoopDesc__pnI_jobject__; +text: .text%__1cRComputeEntryStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cQComputeCallStackHdo_char6M_v_: generateOopMap.o; +text: .text%__1cQComputeCallStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cFciEnvNlookup_method6MpnNinstanceKlass_2pnNsymbolOopDesc_4nJBytecodesECode__pnNmethodOopDesc__; +text: .text%__1cMLinkResolverbNlinktime_resolve_virtual_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cIciMethodXfind_monomorphic_target6MpnHciKlass_22_p0_; +text: .text%__1cDCHAManalyze_call6FnLKlassHandle_11nMsymbolHandle_2_pnJCHAResult__; +text: .text%__1cMLinkResolverbCresolve_virtual_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cJCHAResult2t6MnLKlassHandle_nMsymbolHandle_2pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___n0E_i_v_; +text: .text%__1cJCHAResultOis_monomorphic6kM_i_; +text: .text%__1cJCHAResultSmonomorphic_target6kM_nMmethodHandle__; +text: .text%__1cIciMethodJwill_link6MpnHciKlass_2nJBytecodesECode__i_; +text: .text%__1cMGraphBuilderKtry_inline6MpnIciMethod_i_i_; +text: .text%__1cMGraphBuilderUclear_inline_bailout6M_v_; +text: .text%__1cIciMethodOshould_exclude6M_i_; +text: .text%__1cIciMethodPcan_be_compiled6M_i_; +text: .text%__1cMGraphBuilderVtry_inline_intrinsics6MpnIciMethod__i_; +text: .text%__1cMGraphBuilderPtry_inline_full6MpnIciMethod_i_i_; +text: .text%__1cIciMethodIhas_jsrs6kM_i_; +text: .text%__1cMGraphBuilderWrecursive_inline_level6kMpnIciMethod__i_; +text: .text%__1cPciObjectFactoryMvm_symbol_at6Fi_pnIciSymbol__; +text: .text%__1cJNullCheckFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerMdo_NullCheck6MpnJNullCheck__v_; +text: .text%__1cJNullCheckIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cKObjectTypeEbase6kM_pnJValueType__: c1_ValueType.o; +text: .text%__1cKValueStackEpush6MpnJValueType_pnLInstruction__v_: c1_ValueStack.o; +text: .text%__1cMGraphBuilderKpush_scope6MpnIciMethod_pnKBlockBegin_i_v_; +text: .text%__1cKValueStackKpush_scope6MpnHIRScope__p0_; +text: .text%__1cOExceptionScopeKpush_scope6M_p0_; +text: .text%__1cOExceptionScope2t6Mp0_v_; +text: .text%__1cHIRScopeXcompute_lock_stack_size6M_v_; +text: .text%__1cMGraphBuilderJScopeDataRcaller_stack_size6kM_i_; +text: .text%__1cMGraphBuilderJScopeDataLnum_returns6M_i_; +text: .text%__1cMGraphBuilderJScopeDataXset_inline_cleanup_info6MpnKBlockBegin_pnLInstruction_pnKValueStack__v_; +text: .text%__1cMGraphBuilderJScopeDataQincr_num_returns6M_v_; +text: .text%__1cKValueStackJpop_scope6Mii_p0_; +text: .text%__1cMGraphBuilderJpop_scope6M_v_; +text: .text%__1cMGraphBuilderTpop_exception_scope6M_v_; +text: .text%__1cOExceptionScopeJpop_scope6M_p0_; +text: .text%__1cLCompilationVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cFciEnvVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cMLinkResolverbCresolve_special_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cMGraphBuilderOinline_bailout6Mpkc_v_; +text: .text%__1cLInstructionEprev6MpnKBlockBegin__p0_; +text: .text%__1cKBlockBeginUresolve_substitution6M_v_; +text: .text%__1cKBlockBeginPblock_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cZresolve_substituted_value6FppnLInstruction__v_: c1_Instruction.o; +text: .text%__1cLInstructionFsubst6M_p0_: c1_Instruction.o; +text: .text%__1cLInstructionPother_values_do6MpFpp0_v_v_: c1_GraphBuilder.o; +text: .text%__1cLInstructionPstate_values_do6MpFpp0_v_v_: c1_GraphBuilder.o; +text: .text%__1cLInstructionPstate_values_do6MpFpp0_v_v_: c1_Instruction.o; +text: .text%__1cIConstantPother_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cIBlockEndPother_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cHIntTypeEsize6kM_i_: c1_Canonicalizer.o; +text: .text%__1cJNullCheckPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorMdo_NullCheck6MpnJNullCheck__v_; +text: .text%__1cTNullCheckEliminatorQhandle_NullCheck6MpnJNullCheck__v_; +text: .text%__1cLInstructionOas_AccessLocal6M_pnLAccessLocal__: c1_GraphBuilder.o; +text: .text%__1cHIRScopeNtop_scope_bci6kM_i_; +text: .text%__1cQUseCountComputerPclear_use_count6FpnKBlockBegin__v_: c1_IR.o; +text: .text%__1cIValueGenMdo_NullCheck6MpnJNullCheck__v_; +text: .text%__1cJNullCheckKlock_stack6kM_pnKValueStack__: c1_GraphBuilder.o; +text: .text%__1cLLIR_EmitterKnull_check6MpnLLIR_OprDesc_pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListDsub6MpnLLIR_OprDesc_22pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenQlock_spill_rinfo6MpnLInstruction_nFRInfo__v_; +text: .text%__1cQIRScopeDebugInfoRrecord_debug_info6MpnYDebugInformationRecorder__v_: c1_LIREmitter.o; +text: .text%__1cIRuntime1Yresolve_opt_virtual_call6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cNSharedRuntimeOresolve_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cNSharedRuntimeSresolve_sub_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cFframeZsender_for_compiled_frame6kMpnLRegisterMap_pnICodeBlob_i_0_; +text: .text%__1cLRuntimeStubYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cHnmethodJis_zombie6kM_i_: nmethod.o; +text: .text%__1cNnmethodLocker2t6MpnHnmethod__v_; +text: .text%__1cNSharedRuntimeQfind_callee_info6FpnKJavaThread_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cHnmethodQis_native_method6kM_i_: nmethod.o; +text: .text%__1cHnmethodKpc_desc_at6MpCi_pnGPcDesc__; +text: .text%__1cGPcDescHreal_pc6kMpknHnmethod__pC_; +text: .text%__1cLPcDescCacheKpc_desc_at6kMpnHnmethod_pCi_pnGPcDesc__; +text: .text%__1cLPcDescCacheLadd_pc_desc6MpnGPcDesc__v_; +text: .text%__1cSvframeStreamCommonYfill_from_compiled_frame6MpnHnmethod_i_v_; +text: .text%__1cUCompressedReadStreamMraw_read_int6FrpC_i_: vframe.o; +text: .text%__1cICodeBlobLoop_addr_at6kMi_ppnHoopDesc__; +text: .text%__1cNSharedRuntimeXfind_callee_info_helper6FpnKJavaThread_rnMvframeStream_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cPBytecode_invokeNstatic_target6MpnGThread__nMmethodHandle__; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKCompiledICZcompute_monomorphic_entry6FnMmethodHandle_nLKlassHandle_iirnOCompiledICInfo_pnGThread__v_; +text: .text%__1cKCompiledIC2t6MpnKNativeCall__v_; +text: .text%__1cXvirtual_call_RelocationIparse_ic6FrpnICodeBlob_rpC5rppnHoopDesc_pi_nNRelocIterator__; +text: .text%__1cKCompiledICIis_clean6kM_i_; +text: .text%__1cKCompiledICOic_destination6kM_pC_; +text: .text%__1cKCompiledICWis_in_transition_state6kM_i_; +text: .text%__1cRInlineCacheBufferIcontains6FpC_i_; +text: .text%__1cKCompiledICSset_to_monomorphic6MrknOCompiledICInfo__v_; +text: .text%__1cSCompiledStaticCallSset_to_interpreted6MnMmethodHandle_pC_v_; +text: .text%__1cSCompiledStaticCallJfind_stub6M_pC_; +text: .text%__1cNRelocIteratorEnext6M_i_: compiledIC.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: compiledIC.o; +text: .text%__1cbBopt_virtual_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cKNativeCallXset_destination_mt_safe6MpC_v_; +text: .text%__1cNnmethodLocker2T6M_v_; +text: .text%__1cNmethodOopDescTverified_code_entry6M_pC_; +text: .text%jni_GetByteArrayRegion: jni.o; +text: .text%JVM_DefineClassWithSource; +text: .text%__1cXjvm_define_class_common6FpnHJNIEnv__pkcpnI_jobject_pkWi53pnGThread__pnH_jclass__: jvm.o; +text: .text%__1cQSystemDictionaryTresolve_from_stream6FnMsymbolHandle_nGHandle_2pnPClassFileStream_pnGThread__pnMklassOopDesc__; +text: .text%__1cPClassFileParserbDverify_legal_method_signature6MnMsymbolHandle_1pnGThread__i_; +text: .text%__1cPClassFileParserZskip_over_field_signature6MpciIpnGThread__1_; +text: .text%__1cPClassFileParserXverify_legal_class_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cQput_after_lookup6FnMsymbolHandle_0ppnLNameSigHash__i_; +text: .text%__1cEhash6Fpkc1_I_; +text: .text%__1cKDictionarybAis_valid_protection_domain6MiInMsymbolHandle_nGHandle_2_i_; +text: .text%__1cPDictionaryEntrybAcontains_protection_domain6kMpnHoopDesc__i_; +text: .text%__1cQSystemDictionarybAvalidate_protection_domain6FnTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cKDictionaryVadd_protection_domain6MiInTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cPDictionaryEntryVadd_protection_domain6MpnHoopDesc__v_; +text: .text%__1cUverify_byte_codes_fn6F_pv_: verifier.o; +text: .text%JVM_GetClassCPEntriesCount; +text: .text%JVM_GetClassCPTypes; +text: .text%JVM_GetClassNameUTF; +text: .text%JVM_ReleaseUTF; +text: .text%JVM_FindClassFromClass; +text: .text%jni_IsSameObject: jni.o; +text: .text%JVM_GetClassFieldsCount; +text: .text%JVM_GetClassMethodsCount; +text: .text%JVM_GetMethodIxModifiers; +text: .text%JVM_GetMethodIxByteCodeLength; +text: .text%JVM_GetMethodIxByteCode; +text: .text%JVM_GetMethodIxExceptionTableLength; +text: .text%JVM_GetMethodIxLocalsCount; +text: .text%JVM_GetMethodIxArgsSize; +text: .text%JVM_GetMethodIxSignatureUTF; +text: .text%JVM_GetMethodIxMaxStack; +text: .text%JVM_GetMethodIxExceptionsCount; +text: .text%JVM_GetMethodIxExceptionIndexes; +text: .text%JVM_GetCPMethodNameUTF; +text: .text%JVM_GetCPMethodClassNameUTF; +text: .text%jni_NewLocalRef: jni.o; +text: .text%JVM_GetCPMethodModifiers; +text: .text%JVM_IsConstructorIx; +text: .text%JVM_GetCPMethodSignatureUTF; +text: .text%jni_DeleteGlobalRef: jni.o; +text: .text%__1cQSystemDictionaryVadd_loader_constraint6FnMsymbolHandle_nGHandle_2pnGThread__v_; +text: .text%__1cVLoaderConstraintTableJadd_entry6MnMsymbolHandle_pnMklassOopDesc_nGHandle_34pnGThread__i_; +text: .text%__1cVLoaderConstraintTableJnew_entry6MIpnNsymbolOopDesc_pnMklassOopDesc_ii_pnVLoaderConstraintEntry__; +text: .text%jni_ToReflectedMethod: jni.o; +text: .text%__1cKReflectionKnew_method6FnMmethodHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: reflection.o; +text: .text%__1cYjava_lang_reflect_MethodGcreate6FpnGThread__nGHandle__; +text: .text%__1cYjava_lang_reflect_MethodJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodIset_name6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodPset_return_type6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodThas_signature_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodVhas_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodbFhas_parameter_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodbChas_annotation_default_field6F_i_; +text: .text%__1cNmethodOopDescSannotation_default6kM_pnQtypeArrayOopDesc__; +text: .text%__1cYjava_lang_reflect_MethodWset_annotation_default6FpnHoopDesc_2_v_; +text: .text%jni_CallIntMethod: jni.o; +text: .text%jni_CallStaticVoidMethod: jni.o; +text: .text%jni_DetachCurrentThread; +text: .text%__1cKJavaThreadEexit6Mi_v_; +text: .text%__1cQjava_lang_ThreadLthreadGroup6FpnHoopDesc__2_; +text: .text%JVM_MonitorNotifyAll; +text: .text%__1cNThreadServiceWcurrent_thread_exiting6FpnKJavaThread__v_; +text: .text%__1cLensure_join6FpnKJavaThread__v_: thread.o; +text: .text%__1cQjava_lang_ThreadNset_stillborn6FpnHoopDesc__v_; +text: .text%__1cKJavaThreadYremove_stack_guard_pages6M_v_; +text: .text%__1cWThreadLocalAllocBufferFclear6M_v_; +text: .text%__1cHThreadsGremove6FpnKJavaThread__v_; +text: .text%__1cNThreadServiceNremove_thread6FpnKJavaThread_i_v_; +text: .text%__SLIP.DELETER__A: thread.o; +text: .text%__1cKJavaThread2T6M_v_; +text: .text%__1cGParker2T6M_v_; +text: .text%__1cHMonitor2T6M_v_; +text: .text%__1cFMutex2T6M_v_; +text: .text%lwp_cond_destroy: os_solaris.o; +text: .text%lwp_mutex_destroy: os_solaris.o; +text: .text%__1cUThreadSafepointStateHdestroy6FpnKJavaThread__v_; +text: .text%__1cUThreadSafepointState2T6M_v_; +text: .text%__1cGThread2T5B6M_v_; +text: .text%__1cCosLfree_thread6FpnIOSThread__v_; +text: .text%__1cIOSThread2T6M_v_; +text: .text%__1cIOSThreadKpd_destroy6M_v_; +text: .text%jni_DestroyJavaVM; +text: .text%jni_AttachCurrentThread; +text: .text%attach_current_thread: jni.o; +text: .text%__1cCosWcreate_attached_thread6FpnGThread__i_; +text: .text%__1cKJavaThreadSallocate_threadObj6MnGHandle_pcipnGThread__v_; +text: .text%__1cHThreadsKdestroy_vm6F_i_; +text: .text%__1cKJavaThreadVinvoke_shutdown_hooks6M_v_; +text: .text%__1cLbefore_exit6FpnKJavaThread__v_; +text: .text%__1cNWatcherThreadEstop6F_v_; +text: .text%__1cLStatSamplerJdisengage6F_v_; +text: .text%__1cMPeriodicTaskJdisenroll6M_v_; +text: .text%__1cMPeriodicTask2T5B6M_v_; +text: .text%__1cMPeriodicTaskLis_enrolled6kM_i_; +text: .text%__1cLStatSamplerHdestroy6F_v_; +text: .text%__1cMPerfDataList2T6M_v_; +text: .text%__1cLJvmtiExportNpost_vm_death6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_death6F_v_; +text: .text%__1cCosXterminate_signal_thread6F_v_; +text: .text%__1cCosNsigexitnum_pd6F_i_; +text: .text%__1cCosNsignal_notify6Fi_v_; +text: .text%__1cQprint_statistics6F_v_; +text: .text%__1cFVTuneEexit6F_v_; +text: .text%__1cIVMThreadXwait_for_vm_thread_exit6F_v_; +text: .text%__1cUSafepointSynchronizeFbegin6F_v_; +text: .text%__1cORuntimeServiceWrecord_safepoint_begin6F_v_; +text: .text%__1cJTimeStampSticks_since_update6kM_x_; +text: .text%__1cTAbstractInterpreterRnotice_safepoints6F_v_; +text: .text%__1cKcopy_table6FppC1i_v_: interpreter.o; +text: .text%__1cUSafepointSynchronizeFblock6FpnKJavaThread__v_; +text: .text%__1cCosRcurrent_thread_id6F_i_; +text: .text%__1cJttyLockerbCbreak_tty_lock_for_safepoint6Fi_v_; +text: .text%__1cCosbCmake_polling_page_unreadable6F_v_; +text: .text%__1cUThreadSafepointStateXexamine_state_of_thread6Mi_v_; +text: .text%__1cUSafepointSynchronizeOsafepoint_safe6FpnKJavaThread_nPJavaThreadState__i_; +text: .text%__1cUThreadSafepointStateMroll_forward6Mn0AMsuspend_type_pnHnmethod_i_v_; +text: .text%__1cORuntimeServicebDrecord_safepoint_synchronized6F_v_; +text: .text%__1cUSafepointSynchronizeQdo_cleanup_tasks6F_v_; +text: .text%__1cSObjectSynchronizerVdeflate_idle_monitors6F_v_; +text: .text%__1cNObjectMonitorHis_busy6kM_i_; +text: .text%__1cRInlineCacheBufferUupdate_inline_caches6F_v_; +text: .text%__1cMCounterDecayFdecay6F_v_; +text: .text%__1cQSystemDictionaryRnumber_of_classes6F_i_; +text: .text%__1cQSystemDictionaryStry_get_next_class6F_pnMklassOopDesc__; +text: .text%__1cKDictionaryStry_get_next_class6M_pnMklassOopDesc__; +text: .text%__1cNinstanceKlassKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cJdo_method6FpnNmethodOopDesc__v_: recompilationMonitor.o; +text: .text%__1cONMethodSweeperFsweep6F_v_; +text: .text%__1cNCompileBrokerQset_should_block6F_v_; +text: .text%__1cHVM_ExitbJwait_for_threads_in_native_to_block6F_i_; +text: .text%__1cURecompilationMonitorbFstop_recompilation_monitor_task6F_v_; +text: .text%__1cIVMThreadHdestroy6F_v_; +text: .text%__SLIP.DELETER__A: vmThread.o; +text: .text%__1cSThreadLocalStorageRpd_invalidate_all6F_v_; +text: .text%__1cHVM_ExitNset_vm_exited6F_i_; +text: .text%__1cMexit_globals6F_v_; +text: .text%__1cVverificationType_exit6F_v_; +text: .text%__1cQVerificationTypeIfinalize6F_v_; +text: .text%__1cPperfMemory_exit6F_v_; +text: .text%__1cPPerfDataManagerHdestroy6F_v_; +text: .text%__1cIPerfData2T6M_v_; +text: .text%__1cKPerfMemoryHdestroy6F_v_; +text: .text%__1cKPerfMemoryUdelete_memory_region6F_v_; +text: .text%__1cUdelete_shared_memory6FpcI_v_: perfMemory_solaris.o; +text: .text%__1cLremove_file6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cMostream_exit6F_v_; +text: .text%__SLIP.DELETER__C: ostream.o; +text: .text%__SLIP.FINAL__A: c1_Items.o; +# Test Exit +text: .text%__1cPSignatureStreamHis_done6kM_i_; +text: .text%JVM_Halt; +text: .text%__1cHvm_exit6Fi_v_; +text: .text%__1cIVMThreadHexecute6FpnMVM_Operation__v_; +text: .text%__1cMVM_OperationNdoit_prologue6M_i_: vm_operations.o; +text: .text%__1cGThreadMget_priority6Fkpk0_nOThreadPriority__; +text: .text%__1cCosMget_priority6FkpknGThread_rnOThreadPriority__nIOSReturn__; +text: .text%__1cCosTget_native_priority6FkpknGThread_pi_nIOSReturn__; +text: .text%__1cMVM_OperationSset_calling_thread6MpnGThread_nOThreadPriority__v_; +text: .text%__1cMVM_OperationPevaluation_mode6kM_n0AEMode__: vm_operations.o; +text: .text%__1cMVM_OperationSis_cheap_allocated6kM_i_: vm_operations.o; +text: .text%__1cQVMOperationQdDueueDadd6MpnMVM_Operation__i_; +text: .text%__1cQVMOperationQdDueueOqueue_add_back6MipnMVM_Operation__v_; +text: .text%__1cQVMOperationQdDueueGinsert6MpnMVM_Operation_2_v_; +text: .text%__1cQVMOperationQdDueueGunlink6MpnMVM_Operation__v_; +text: .text%__1cHVM_ExitEname6kM_pkc_: vm_operations.o; +text: .text%__1cJEventMark2t6MpkcE_v_: vmThread.o; +text: .text%__1cCosJyield_all6Fi_v_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: vmThread.o; +text: .text%__1cSInterpreterRuntimeMat_safepoint6FpnKJavaThread__v_; +text: .text%__1cIVMThreadSevaluate_operation6MpnMVM_Operation__v_; +text: .text%__1cMVM_OperationIevaluate6M_v_; +text: .text%__1cHVM_ExitEdoit6M_v_; +# Test Hello +text: .text%JVM_GetCPFieldSignatureUTF; +text: .text%JVM_Write; +text: .text%__1cDhpiFwrite6FipkvI_I_: jvm.o; +# Test Sleep +text: .text%JVM_GetMethodIxExceptionTableEntry; +text: .text%JVM_GetCPClassNameUTF; +text: .text%JVM_Sleep; +text: .text%__1cCosHSolarisTsetup_interruptible6F_pnKJavaThread__; +text: .text%__1cCosHSolarisTsetup_interruptible6FpnKJavaThread__v_; +text: .text%__1cUSafepointSynchronizeRis_cleanup_needed6F_i_; +text: .text%__1cRInlineCacheBufferIis_empty6F_i_; +text: .text%__1cCosHSolarisVcleanup_interruptible6FpnKJavaThread__v_; +text: .text%__1cCosOunguard_memory6FpcI_i_; +# Test IntToString +text: .text%__1cQChunkPoolCleanerEtask6M_v_: allocation.o; +text: .text%__1cJChunkPoolMfree_all_but6MI_v_: allocation.o; +# Test LoadToolkit +text: .text%JVM_GetClassContext; +text: .text%__1cNCollectedHeapMobj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: jvm.o; +text: .text%jni_IsAssignableFrom: jni.o; +text: .text%__1cOGenerateOopMapGdo_ldc6Mii_v_; +text: .text%__1cQComputeCallStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cMGraphBuilderNload_constant6M_v_; +text: .text%__1cQciBytecodeStreamMget_constant6kM_nKciConstant__; +text: .text%__1cQciBytecodeStreamSget_constant_index6kM_i_; +text: .text%__1cFciEnvVget_constant_by_index6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cFciEnvbAget_constant_by_index_impl6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cMLinkResolverbBresolve_static_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cLInstructionMas_LoadLocal6M_pnJLoadLocal__: c1_Canonicalizer.o; +text: .text%__1cTsort_by_start_block6FppnELoop_2_i_: c1_Loops.o; +text: .text%__1cILIR_ListLcall_static6MpnLLIR_OprDesc_pCpnMCodeEmitInfo_pnOStaticCallStub__v_: c1_LIREmitter.o; +text: .text%__1cLLIR_EmitterLcmp_mem_int6MnMLIR_OpBranchNLIR_Condition_nFRInfo_iipnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListLcmp_mem_int6MnMLIR_OpBranchNLIR_Condition_nFRInfo_iipnMCodeEmitInfo__v_; +text: .text%__1cJValueTypeLas_VoidType6M_pnIVoidType__: c1_Canonicalizer.o; +text: .text%__1cILIR_ListHint2reg6MinFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cWstatic_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cRComputeEntryStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cKValueStackEpush6MpnJValueType_pnLInstruction__v_: c1_Optimizer.o; +text: .text%__1cEIfOpPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cEIfOpFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorHdo_IfOp6MpnEIfOp__v_; +text: .text%__1cIValueGenHdo_IfOp6MpnEIfOp__v_; +text: .text%__1cLLIR_EmitterLifop_phase16MnLInstructionJCondition_pnLLIR_OprDesc_4_v_; +text: .text%__1cLLIR_EmitterLifop_phase26MnFRInfo_pnLLIR_OprDesc_3nLInstructionJCondition__v_; +text: .text%__1cILIR_ListGbranch6MnMLIR_OpBranchNLIR_Condition_pnFLabel__v_; +text: .text%__1cRLIR_PeepholeStateUstart_forward_branch6MpnFLabel__v_; +text: .text%__1cOGenerateOopMapMdo_checkcast6M_v_; +text: .text%__1cMGraphBuilderLinstance_of6Mi_v_; +text: .text%__1cKInstanceOfFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerNdo_InstanceOf6MpnKInstanceOf__v_; +text: .text%__1cJTypeCheckIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderOdirect_compare6MpnHciKlass__i_; +text: .text%__1cKInstanceOfNas_InstanceOf6M_p0_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderKcheck_cast6Mi_v_; +text: .text%__1cJCheckCastFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerMdo_CheckCast6MpnJCheckCast__v_; +text: .text%__1cJValueTypeKas_IntType6M_pnHIntType__: c1_ValueType.o; +text: .text%__1cJTypeCheckPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorNdo_InstanceOf6MpnKInstanceOf__v_; +text: .text%__1cQNullCheckVisitorMdo_CheckCast6MpnJCheckCast__v_; +text: .text%__1cIValueGenNdo_InstanceOf6MpnKInstanceOf__v_; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_CodeGenerator_i486.o; +text: .text%__1cLLIR_EmitterNinstanceof_op6MpnLLIR_OprDesc_2pnHciKlass_nFRInfo_5ipnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListKinstanceof6MpnLLIR_OprDesc_2pnHciKlass_22ipnMCodeEmitInfo__v_; +text: .text%__1cPLIR_OpTypeCheck2t6MnILIR_Code_pnLLIR_OprDesc_3pnHciKlass_33ipnMCodeEmitInfo_7pnICodeStub__v_; +text: .text%__1cIValueGenMdo_CheckCast6MpnJCheckCast__v_; +text: .text%__1cILIR_ListJcheckcast6MpnLLIR_OprDesc_2pnHciKlass_22ipnMCodeEmitInfo_6pnICodeStub__v_; +text: .text%__1cILIR_ListJsafepoint6MnFRInfo_pnMCodeEmitInfo__v_: c1_CodeGenerator_i486.o; +text: .text%__1cPLIR_OpTypeCheckFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cTSimpleExceptionStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_i486.o; +text: .text%__1cPLIR_OpTypeCheckJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerQemit_opTypeCheck6MpnPLIR_OpTypeCheck__v_; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_LIROptimizer.o; +text: .text%__1cIintArrayIindex_of6kMki_i_: c1_LIROptimizer.o; +text: .text%__1cNLIR_AssemblerQemit_opTypeCheck6MpnPLIR_OpTypeCheck__v_; +text: .text%__1cIciObjectIencoding6M_pnI_jobject__; +text: .text%__1cJAssemblerEcmpl6MnHAddress_pnI_jobject__v_; +text: .text%__1cTSimpleExceptionStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cTSimpleExceptionStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cJLoadFieldIis_equal6kMpnLInstruction__i_: c1_Instruction.o; +text: .text%__1cJLoadFieldMas_LoadField6M_p0_: c1_Instruction.o; +text: .text%__1cDPhiPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cDPhiFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorGdo_Phi6MpnDPhi__v_; +text: .text%__1cLInstructionIas_Local6M_pnFLocal__: c1_GraphBuilder.o; +text: .text%__1cDPhiGas_Phi6M_p0_: c1_GraphBuilder.o; +text: .text%__1cIValueGenScompute_phi_arrays6MpnKValueStack_pnGValues_pnIintStack_i_pnLInstruction__; +text: .text%__1cLLIR_EmitterTset_fpu_stack_empty6M_v_; +text: .text%__1cIRegAllocKlock_spill6MpnLInstruction_ii_v_; +text: .text%__1cIRegAllocRextend_spill_area6Mi_v_; +text: .text%__1cRclear_state_items6FppnLInstruction__v_: c1_CodeGenerator.o; +text: .text%__1cNLIR_AssemblerTset_fpu_stack_empty6M_v_; +text: .text%__1cIFrameMapLFpuStackSimFclear6M_v_; +text: .text%jni_GetEnv; +text: .text%jni_CallStaticBooleanMethod: jni.o; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cOtypeArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cVcreate_gc_point_array6FpnFArena_i_pnNGrowableArray4Ci___; +text: .text%__1cOGenerateOopMapRdo_exception_edge6MpnOBytecodeStream__v_; +text: .text%__1cOGenerateOopMapIppop_any6Mi_v_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: c1_IR.o; +text: .text%__1cMGraphBuilderQhandle_exception6Mi_v_; +text: .text%__1cOExceptionScopeFclear6M_v_; +text: .text%__1cMGraphBuilderJScopeDataJxhandlers6kM_pnJXHandlers__; +text: .text%__1cTciConstantPoolCache2t6MpnFArena_i_v_; +text: .text%__1cTciConstantPoolCacheDget6Mi_pv_; +text: .text%__1cTciConstantPoolCacheEfind6Mi_i_; +text: .text%__1cTciConstantPoolCacheGinsert6Mipv_v_; +text: .text%__1cMGraphBuilderHif_null6MpnJValueType_nLInstructionJCondition__v_; +text: .text%__1cOObjectConstantRas_ObjectConstant6M_p0_: c1_ValueType.o; +text: .text%__1cMas_ValueType6FnKciConstant__pnJValueType__; +text: .text%__1cLInstructionGmirror6Fn0AJCondition__1_; +text: .text%__1cHis_true6FxnLInstructionJCondition_x_i_: c1_Canonicalizer.o; +text: .text%__1cNCanonicalizerNset_canonical6MpnLInstruction__v_; +text: .text%__1cKBlockBeginVadd_exception_handler6Mp0_v_; +text: .text%__1cPBlockBeginArrayIindex_of6kMkpnKBlockBegin__i_: c1_Instruction.o; +text: .text%__1cOExceptionScopeLadd_handler6MpnIXHandler__v_; +text: .text%__1cIciObjectEhash6M_i_; +text: .text%__1cPciObjectFactoryPinsert_non_perm6Mrpn0ANNonPermObject_pnHoopDesc_pnIciObject__v_; +text: .text%__1cIciObjectMhas_encoding6M_i_; +text: .text%__1cJValueTypeRas_ObjectConstant6M_pnOObjectConstant__: c1_ValueType.o; +text: .text%__1cNClassConstantQas_ClassConstant6M_p0_: c1_ValueType.o; +text: .text%__1cOExceptionScopeKhandler_at6kMi_pnIXHandler__; +text: .text%__1cLInstructionMas_LoadLocal6M_pnJLoadLocal__: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderIlogic_op6MpnJValueType_nJBytecodesECode__v_; +text: .text%__1cHLogicOpFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerKdo_LogicOp6MpnHLogicOp__v_; +text: .text%__1cHLogicOpEhash6kM_i_: c1_Instruction.o; +text: .text%__1cHLogicOpEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cLInstructionIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cMGraphBuilderHconvert6MnJBytecodesECode_nJBasicType_3_v_; +text: .text%__1cHConvertFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerKdo_Convert6MpnHConvert__v_; +text: .text%__1cHConvertEhash6kM_i_: c1_GraphBuilder.o; +text: .text%__1cHConvertEname6kM_pkc_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderNstore_indexed6MnJBasicType__v_; +text: .text%__1cIValueMapKkill_array6MpnJValueType__v_; +text: .text%__1cGBucketKkill_array6MpnJValueType__v_; +text: .text%__1cLInstructionOas_LoadIndexed6M_pnLLoadIndexed__: c1_GraphBuilder.o; +text: .text%__1cLInstructionOas_LoadIndexed6M_pnLLoadIndexed__: c1_Instruction.o; +text: .text%__1cKValueStackRpin_stack_indexed6MpnJValueType__v_; +text: .text%__1cMStoreIndexedFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerPdo_StoreIndexed6MpnMStoreIndexed__v_; +text: .text%__1cLAccessArrayIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cLAccessFieldPother_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cLInstructionPother_values_do6MpFpp0_v_v_: c1_Instruction.o; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstance.o; +text: .text%__1cMStoreIndexedPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cHConvertPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorKdo_LogicOp6MpnHLogicOp__v_; +text: .text%__1cQNullCheckVisitorKdo_Convert6MpnHConvert__v_; +text: .text%__1cQNullCheckVisitorPdo_StoreIndexed6MpnMStoreIndexed__v_; +text: .text%__1cTNullCheckEliminatorThandle_StoreIndexed6MpnMStoreIndexed__v_; +text: .text%__1cMciNullObjectMis_classless6kM_i_: ciNullObject.o; +text: .text%__1cJValueTypeQas_ClassConstant6M_pnNClassConstant__: c1_ValueType.o; +text: .text%__1cOObjectConstantIencoding6kM_pnI_jobject__; +text: .text%__1cIValueGenbBrlock_byte_result_with_hint6MpnLInstruction_pknEItem__nFRInfo__; +text: .text%__1cNc1_AllocTableThas_one_free_masked6kMnKc1_RegMask__i_; +text: .text%__1cIRegAllocMget_lock_reg6MpnLInstruction_nKc1_RegMask__nFRInfo__; +text: .text%__1cIRegAllocMget_free_reg6MnKc1_RegMask__nFRInfo__; +text: .text%__1cNc1_AllocTablePget_free_masked6MnKc1_RegMask__i_; +text: .text%__1cNClassConstantIencoding6kM_pnI_jobject__; +text: .text%__1cLLIR_EmitterLopr2jobject6MpnLLIR_OprDesc__pnI_jobject__; +text: .text%__1cILIR_ListHoop2reg6MpnI_jobject_nFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenMrelease_item6MpnEItem__v_; +text: .text%__1cIValueGenPdo_StoreIndexed6MpnMStoreIndexed__v_; +text: .text%__1cIValueGenKdo_Convert6MpnHConvert__v_; +text: .text%__1cIValueGenKdo_LogicOp6MpnHLogicOp__v_; +text: .text%__1cLLIR_EmitterIlogic_op6MnJBytecodesECode_nFRInfo_pnLLIR_OprDesc_5_v_; +text: .text%__1cILIR_ListLlogical_and6MnFRInfo_pnLLIR_OprDesc_1_v_: c1_LIREmitter.o; +text: .text%__1cLLIR_EmitterKconvert_op6MnJBytecodesECode_pnLLIR_OprDesc_nFRInfo_i_v_; +text: .text%__1cILIR_ListHconvert6MnJBytecodesECode_pnLLIR_OprDesc_4i_v_: c1_LIREmitter.o; +text: .text%__1cIValueGenKmust_round6MpnLInstruction_pknEItem__i_; +text: .text%__1cLAccessArrayKlock_stack6kM_pnKValueStack__: c1_GraphBuilder.o; +text: .text%__1cLLIR_EmitterNindexed_store6MnJBasicType_pnLLIR_OprDesc_33nFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cLLIR_EmitterXlo_word_offset_in_bytes6kM_i_; +text: .text%__1cLLIR_EmitterXhi_word_offset_in_bytes6kM_i_; +text: .text%__1cILIR_ListLstore_array6MnFRInfo_pnLLIR_Address_nJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cIValueGenXexception_handler_start6MpnHIRScope_ipnKValueStack__v_; +text: .text%__1cLLIR_EmitterNhandler_entry6M_v_; +text: .text%__1cLLIR_OprFactQdummy_value_type6FpnJValueType__pnLLIR_OprDesc__; +text: .text%__1cLInstructionKexact_type6kM_pnGciType__: c1_GraphBuilder.o; +text: .text%__1cLInstructionNdeclared_type6kM_pnGciType__: c1_GraphBuilder.o; +text: .text%__1cILIR_ListKnull_check6MnFRInfo_pnMCodeEmitInfo__v_: c1_CodeGenerator.o; +text: .text%__1cNLIR_OpConvertJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerOemit_opConvert6MpnNLIR_OpConvert__v_; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_pnI_jobject__v_; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_pnI_jobject__v_; +text: .text%__1cNLIR_AssemblerIlogic_op6MnILIR_Code_pnLLIR_OprDesc_33_v_; +text: .text%__1cNLIR_AssemblerOemit_opConvert6MpnNLIR_OpConvert__v_; +text: .text%__1cNLIR_AssemblerNarray_move_op6MpnLLIR_OprDesc_2nJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerJreg2array6MnFRInfo_pnLLIR_Address_nJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerPas_ArrayAddress6MpnLLIR_Address_nJBasicType__nHAddress__; +text: .text%__1cVConstantOopWriteValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cUDebugInfoWriteStreamMwrite_handle6MpnI_jobject__v_; +text: .text%__1cTExceptionRangeTableJadd_entry6Miiiiii_v_; +text: .text%__1cTExceptionRangeEntry2t6Miiiiii_v_; +text: .text%__1cTExceptionRangeTableJadd_entry6MnTExceptionRangeEntry__v_; +text: .text%__1cOExceptionScopeCid6kM_i_; +text: .text%__1cTExceptionRangeTableTentry_index_for_pco6kMi_i_; +text: .text%__1cTExceptionRangeTableIentry_at6kMi_pnTExceptionRangeEntry__; +text: .text%jni_CallStaticVoidMethodV: jni.o; +text: .text%JVM_GetLastErrorString; +text: .text%jni_Throw: jni.o; +text: .text%__1cKExceptionsK_throw_oop6FpnGThread_pkcipnHoopDesc__v_; +text: .text%JVM_DisableCompiler; +text: .text%__1cNinstanceKlassbFlookup_method_in_all_interfaces6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%JVM_Available; +text: .text%__1cOGenerateOopMapKpp_new_ref6MpnNCellTypeState_i_v_; +text: .text%__1cLInstructionMas_LoadField6M_pnJLoadField__: c1_Instruction.o; +text: .text%__1cHLogicOpOis_commutative6kM_i_; +text: .text%__1cDCHANprocess_class6FnLKlassHandle_pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___nMsymbolHandle_6_v_; +text: .text%__1cUGenericGrowableArrayMraw_contains6kMpknEGrET__i_; +text: .text%__1cLArrayLengthFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerOdo_ArrayLength6MpnLArrayLength__v_; +text: .text%__1cLArrayLengthEhash6kM_i_: c1_GraphBuilder.o; +text: .text%__1cLArrayLengthEname6kM_pkc_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderOnew_type_array6M_v_; +text: .text%__1cMNewTypeArrayFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerPdo_NewTypeArray6MpnMNewTypeArray__v_; +text: .text%__1cINewArrayIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cJIntrinsicFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerMdo_Intrinsic6MpnJIntrinsic__v_; +text: .text%__1cJIntrinsicMas_Intrinsic6M_p0_: c1_GraphBuilder.o; +text: .text%__1cJIntrinsicIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cLAccessArrayPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorOdo_ArrayLength6MpnLArrayLength__v_; +text: .text%__1cTNullCheckEliminatorShandle_ArrayLength6MpnLArrayLength__v_; +text: .text%__1cINewArrayPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorPdo_NewTypeArray6MpnMNewTypeArray__v_; +text: .text%__1cTNullCheckEliminatorPhandle_NewArray6MpnINewArray__v_; +text: .text%__1cJIntrinsicPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorMdo_Intrinsic6MpnJIntrinsic__v_; +text: .text%__1cJLoopArrayIindex_of6kMkpnELoop__i_: c1_Loops.o; +text: .text%__1cINewArrayLas_NewArray6M_p0_: c1_Instruction.o; +text: .text%__1cILIR_ListOcall_icvirtual6MnFRInfo_pnLLIR_OprDesc_pCpnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListNstore_mem_int6MinFRInfo_inJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cIValueGenOdo_ArrayLength6MpnLArrayLength__v_; +text: .text%__1cLLIR_EmitterMarray_length6MnFRInfo_pnLLIR_OprDesc_pnMCodeEmitInfo__v_; +text: .text%__1cLlog2_intptr6Fi_i_: c1_LIREmitter.o; +text: .text%__1cIValueGenPdo_NewTypeArray6MpnMNewTypeArray__v_; +text: .text%__1cLLIR_EmitterOnew_type_array6MnFRInfo_nJBasicType_pnLLIR_OprDesc_11111pnMCodeEmitInfo__v_; +text: .text%__1cQNewTypeArrayStub2t6MnFRInfo_11pnMCodeEmitInfo__v_; +text: .text%__1cQciTypeArrayKlassEmake6FnJBasicType__p0_; +text: .text%__1cQciTypeArrayKlassJmake_impl6FnJBasicType__p0_; +text: .text%__1cILIR_ListHoop2reg6MpnI_jobject_nFRInfo__v_: c1_LIREmitter_i486.o; +text: .text%__1cILIR_ListOallocate_array6MnFRInfo_11111nJBasicType_1pnICodeStub__v_; +text: .text%__1cIValueGenMdo_Intrinsic6MpnJIntrinsic__v_; +text: .text%__1cIValueGenMdo_ArrayCopy6MpnJIntrinsic__v_; +text: .text%__1cIValueGenQarraycopy_helper6MpnJIntrinsic_pippnMciArrayKlass__v_; +text: .text%__1cJLoadFieldKexact_type6kM_pnGciType__; +text: .text%__1cJLoadFieldNdeclared_type6kM_pnGciType__; +text: .text%__1cQciTypeArrayKlassTis_type_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cOas_array_klass6FpnGciType__pnMciArrayKlass__: c1_CodeGenerator.o; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cMNewTypeArrayKexact_type6kM_pnGciType__; +text: .text%__1cLInstructionNdeclared_type6kM_pnGciType__: c1_Instruction.o; +text: .text%__1cRpositive_constant6FpnLInstruction__i_: c1_CodeGenerator.o; +text: .text%__1cLArrayLengthOas_ArrayLength6M_p0_: c1_GraphBuilder.o; +text: .text%__1cQis_constant_zero6FpnLInstruction__i_: c1_CodeGenerator.o; +text: .text%__1cILIR_ListJarraycopy6MpnLLIR_OprDesc_22222pnMciArrayKlass_ipnMCodeEmitInfo__v_: c1_CodeGenerator_i486.o; +text: .text%__1cLLIR_EmitterNwrite_barrier6MpnLLIR_OprDesc_2_v_; +text: .text%__1cILIR_ListUunsigned_shift_right6MnFRInfo_i1_v_: c1_LIREmitter_i486.o; +text: .text%__1cILIR_ListUunsigned_shift_right6MpnLLIR_OprDesc_222_v_; +text: .text%__1cQLIR_OpAllocArrayFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cQNewTypeArrayStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_i486.o; +text: .text%__1cPLIR_OpArrayCopyFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cQLIR_OpAllocArrayJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerQemit_alloc_array6MpnQLIR_OpAllocArray__v_; +text: .text%__1cPLIR_OpArrayCopyJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerOemit_arraycopy6MpnPLIR_OpArrayCopy__v_; +text: .text%__1cNLIR_AssemblerHic_call6MpCpnMCodeEmitInfo__v_; +text: .text%__1cJAssemblerEcall6MpCrknQRelocationHolder__v_; +text: .text%__1cXvirtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cXvirtual_call_RelocationJpack_data6M_i_; +text: .text%__1cNLIR_AssemblerJconst2mem6MpnJLIR_Const_pnLLIR_Address_nJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerQemit_alloc_array6MpnQLIR_OpAllocArray__v_; +text: .text%__1cNLIR_AssemblerSarray_element_size6kMnJBasicType__nHAddressLScaleFactor__; +text: .text%__1cRC1_MacroAssemblerOallocate_array6MpnMRegisterImpl_222inHAddressLScaleFactor_2rnFLabel__v_; +text: .text%__1cRC1_MacroAssemblerMtry_allocate6MpnMRegisterImpl_2i22rnFLabel__v_; +text: .text%__1cQNewTypeArrayStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cNLIR_AssemblerOemit_arraycopy6MpnPLIR_OpArrayCopy__v_; +text: .text%__1cMciArrayKlassMelement_type6M_pnGciType__; +text: .text%__1cNArrayCopyStub2t6MpnMCodeEmitInfo_pnOStaticCallStub__v_; +text: .text%__1cFRInfoMset_word_reg6MkpnMRegisterImpl__v_; +text: .text%__1cNArrayCopyStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cNLIR_AssemblerOpush_parameter6MpnMRegisterImpl_i_v_; +text: .text%__1cQNewTypeArrayStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cNArrayCopyStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cXvirtual_call_RelocationLunpack_data6M_v_; +text: .text%__1cIRuntime1Uresolve_virtual_call6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cKoopFactoryUnew_compiledICHolder6FnMmethodHandle_nLKlassHandle_pnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cVcompiledICHolderKlassIallocate6MpnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: compiledICHolderKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: compiledICHolderKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: compiledICHolderKlass.o; +text: .text%__1cXvirtual_call_RelocationJfirst_oop6M_pC_; +text: .text%__1cXvirtual_call_RelocationJoop_limit6M_pC_; +text: .text%__1cNRelocIteratorJset_limit6MpC_v_; +text: .text%__1cRInlineCacheBufferWcreate_transition_stub6FpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cGICStubIset_stub6MpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cRInlineCacheBufferXassemble_ic_buffer_code6FpCpnHoopDesc_1_v_; +text: .text%__1cKCompiledICSset_ic_destination6MpC_v_; +text: .text%__1cRInlineCacheBufferLnew_ic_stub6F_pnGICStub__; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlass.o; +text: .text%JVM_NewArray; +text: .text%__1cKReflectionRreflect_new_array6FpnHoopDesc_ipnGThread__pnMarrayOopDesc__; +text: .text%__1cSInterpreterRuntimeOmultianewarray6FpnKJavaThread_pi_v_; +text: .text%__1cNobjArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cNinstanceKlassSlookup_osr_nmethod6kMkpnNmethodOopDesc_i_pnHnmethod__; +text: .text%__1cQSimpleCompPolicyYmethod_back_branch_event6MnMmethodHandle_iipnGThread__v_; +text: .text%__1cICompilerMsupports_osr6M_i_: c1_Compiler.o; +text: .text%__1cHciKlassOis_subclass_of6Mp0_i_; +text: .text%__1cMGraphBuilderQnew_object_array6M_v_; +text: .text%__1cONewObjectArrayFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerRdo_NewObjectArray6MpnONewObjectArray__v_; +text: .text%__1cPciObjArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cPciObjArrayKlassGloader6M_pnHoopDesc__: ciObjArrayKlass.o; +text: .text%__1cMGraphBuilderIshift_op6MpnJValueType_nJBytecodesECode__v_; +text: .text%__1cHShiftOpFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerKdo_ShiftOp6MpnHShiftOp__v_; +text: .text%__1cHShiftOpEhash6kM_i_: c1_GraphBuilder.o; +text: .text%__1cHShiftOpEname6kM_pkc_: c1_GraphBuilder.o; +text: .text%__1cLLoadIndexedOas_LoadIndexed6M_p0_: c1_Instruction.o; +text: .text%__1cMArithmeticOpIis_equal6kMpnLInstruction__i_: c1_Instruction.o; +text: .text%__1cDOp2Gas_Op26M_p0_: c1_Instruction.o; +text: .text%__1cLInstructionMas_LoadField6M_pnJLoadField__: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorRdo_NewObjectArray6MpnONewObjectArray__v_; +text: .text%__1cDOp2Pinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorKdo_ShiftOp6MpnHShiftOp__v_; +text: .text%__1cHciKlassMaccess_flags6M_i_; +text: .text%__1cILIR_ListPallocate_object6MnFRInfo_111ii1pnICodeStub__v_; +text: .text%__1cLLIR_EmitterOmembar_release6M_v_; +text: .text%__1cLLIR_EmitterGmembar6M_v_; +text: .text%__1cIValueGenRdo_NewObjectArray6MpnONewObjectArray__v_; +text: .text%__1cLLIR_EmitterQnew_object_array6MnFRInfo_pnHciKlass_pnLLIR_OprDesc_11111pnMCodeEmitInfo_7_v_; +text: .text%__1cSNewObjectArrayStub2t6MnFRInfo_11pnMCodeEmitInfo__v_; +text: .text%__1cPciObjArrayKlassEmake6FpnHciKlass__p0_; +text: .text%__1cPciObjArrayKlassJmake_impl6FpnHciKlass__p0_; +text: .text%__1cLLIR_EmitterOmembar_acquire6M_v_; +text: .text%__1cIValueGenKdo_ShiftOp6MpnHShiftOp__v_; +text: .text%__1cIValueGenPshiftCountRInfo6F_nFRInfo__; +text: .text%__1cLLIR_EmitterIshift_op6MnJBytecodesECode_nFRInfo_pnLLIR_OprDesc_53_v_; +text: .text%__1cILIR_ListKshift_left6MnFRInfo_i1_v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListKlogical_or6MnFRInfo_pnLLIR_OprDesc_1_v_: c1_LIREmitter.o; +text: .text%__1cOLIR_OpAllocObjFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cSNewObjectArrayStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_i486.o; +text: .text%__1cOLIR_OpAllocObjJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerOemit_alloc_obj6MpnOLIR_OpAllocObj__v_; +text: .text%__1cNLIR_AssemblerOemit_alloc_obj6MpnOLIR_OpAllocObj__v_; +text: .text%__1cRC1_MacroAssemblerPallocate_object6MpnMRegisterImpl_22ii2rnFLabel__v_; +text: .text%__1cNLIR_AssemblerOmembar_release6M_v_; +text: .text%__1cNLIR_AssemblerGmembar6M_v_; +text: .text%__1cSNewObjectArrayStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cNLIR_AssemblerOmembar_acquire6M_v_; +text: .text%__1cEBaseHas_Base6M_p0_: c1_IR.o; +text: .text%__1cNLIR_AssemblerOemit_osr_entry6MpnHIRScope_ipnFLabel_i_v_; +text: .text%__1cSNewObjectArrayStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cNinstanceKlassPadd_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cUGenericGrowableArrayLraw_at_grow6MipknEGrET__pv_; +text: .text%__1cNSignatureInfoIdo_short6M_v_: bytecode.o; +text: .text%jni_MonitorEnter: jni.o; +text: .text%__1cSObjectSynchronizerJjni_enter6FnGHandle_pnGThread__v_; +text: .text%jni_MonitorExit: jni.o; +text: .text%__1cSObjectSynchronizerIjni_exit6FpnHoopDesc_pnGThread__v_; +text: .text%jni_CallVoidMethod: jni.o; +text: .text%__1cXJNI_ArgumentPusherVaArgHget_int6M_v_: jni.o; +text: .text%jni_CallStaticBooleanMethodV: jni.o; +text: .text%JVM_GetStackTraceDepth; +text: .text%__1cTjava_lang_ThrowableVget_stack_trace_depth6FpnHoopDesc_pnGThread__i_; +text: .text%__1cTjava_lang_ThrowableJbacktrace6FpnHoopDesc__2_; +text: .text%JVM_GetStackTraceElement; +text: .text%__1cTjava_lang_ThrowableXget_stack_trace_element6FpnHoopDesc_ipnGThread__2_; +text: .text%__1cbBjava_lang_StackTraceElementGcreate6FnMmethodHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cbBjava_lang_StackTraceElementNset_className6FpnHoopDesc_2_v_; +text: .text%__1cbBjava_lang_StackTraceElementOset_methodName6FpnHoopDesc_2_v_; +text: .text%__1cbBjava_lang_StackTraceElementMset_fileName6FpnHoopDesc_2_v_; +text: .text%__1cNmethodOopDescUline_number_from_bci6kMi_i_; +text: .text%__1cbECompressedLineNumberReadStream2t6MpC_v_; +text: .text%__1cbECompressedLineNumberReadStreamJread_pair6M_i_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: methodOop.o; +text: .text%__1cbBjava_lang_StackTraceElementOset_lineNumber6FpnHoopDesc_i_v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: typeArrayKlass.o; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciObjectFactory.o; +text: .text%__1cNObjectMonitorJnotifyAll6MpnGThread__v_; +text: .text%__1cNObjectMonitorREntryQdDueue_insert6MpnMObjectWaiter_i_v_; +text: .text%__1cNObjectMonitorbAEntryQdDueue_SelectSuccessor6M_pnMObjectWaiter__; +text: .text%__1cLServiceUtilLvisible_oop6FpnHoopDesc__i_: objectMonitor_solaris.o; +text: .text%__1cNObjectMonitorGEnterI6MpnGThread__v_; +text: .text%JVM_EnableCompiler; +text: .text%__1cCosHSolarisFEventEpark6Mx_i_: objectMonitor_solaris.o; +text: .text%__1cJStubQdDueueKremove_all6M_v_; +text: .text%__1cJStubQdDueueMremove_first6Mi_v_; +text: .text%__1cJStubQdDueueMremove_first6M_v_; +text: .text%__1cPICStubInterfaceIfinalize6MpnEStub__v_: icBuffer.o; +text: .text%__1cGICStubIfinalize6M_v_; +text: .text%__1cGICStubKcached_oop6kM_pnHoopDesc__; +text: .text%__1cRInlineCacheBufferUic_buffer_cached_oop6FpC_pnHoopDesc__; +text: .text%__1cKCompiledICOset_cached_oop6MpnHoopDesc__v_; +text: .text%__1cOoop_RelocationSfix_oop_relocation6M_v_; +text: .text%__1cGICStubLdestination6kM_pC_; +text: .text%__1cRInlineCacheBufferVic_buffer_entry_point6FpC_1_; +text: .text%__1cPICStubInterfaceEsize6kMpnEStub__i_: icBuffer.o; +text: .text%__1cUSafepointSynchronizeDend6F_v_; +text: .text%__1cCosbAmake_polling_page_readable6F_v_; +text: .text%__1cTAbstractInterpreterRignore_safepoints6F_v_; +text: .text%__1cGThreadQunboost_priority6Fp0_v_; +text: .text%__1cUThreadSafepointStateHrestart6M_v_; +text: .text%__1cORuntimeServiceUrecord_safepoint_end6F_v_; +text: .text%__1cKJavaThreadbScheck_safepoint_and_suspend_for_native_trans6Fp0_v_; +# Test LoadFrame +text: .text%__1cNObjectMonitorGenter26MpnGThread__v_; +text: .text%__1cICompilerPsupports_native6M_i_: c1_Compiler.o; +text: .text%__1cLCompilationVcompile_native_method6MpnLCodeOffsets__i_; +text: .text%__1cIciMethodMnative_entry6M_pC_; +text: .text%__1cLCompilationUemit_code_for_native6MpCpnLCodeOffsets__v_; +text: .text%__1cLCompilationXemit_code_prolog_native6MpnIFrameMap__v_; +text: .text%__1cNLIR_AssemblerRemit_method_entry6MpnLLIR_Emitter_pnHIRScope__v_; +text: .text%__1cOMacroAssemblerHfat_nop6M_v_; +text: .text%__1cNLIR_AssemblerQemit_native_call6MpCpnMCodeEmitInfo__v_; +text: .text%__1cMCodeEmitInfobGcreate_oop_map_for_own_signature6M_pnGOopMap__; +text: .text%__1cNLIR_AssemblerXemit_native_method_exit6MpnMCodeEmitInfo__v_; +text: .text%__1cNSignatureInfoHdo_char6M_v_: reflection.o; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: reflection.o; +text: .text%jni_CallObjectMethodV: jni.o; +text: .text%jni_SetObjectField: jni.o; +text: .text%jni_IsInstanceOf: jni.o; +text: .text%jni_GetStaticObjectField: jni.o; +text: .text%__1cbCTwoGenerationCollectorPolicybMshould_try_older_generation_allocation6kMI_i_; +text: .text%__1cQGenCollectedHeapSattempt_allocation6MIiii_pnIHeapWord__; +text: .text%__1cQDefNewGenerationIallocate6MIii_pnIHeapWord__: defNewGeneration.o; +text: .text%__1cKGenerationInext_gen6kM_p0_; +text: .text%__1cKGenerationYallocation_limit_reached6MpnFSpace_pnIHeapWord_I_4_: tenuredGeneration.o; +text: .text%__1cQDefNewGenerationTallocate_from_space6MI_pnIHeapWord__; +text: .text%__1cPVM_GC_OperationNdoit_prologue6M_i_; +text: .text%__1cPVM_GC_OperationZacquire_pending_list_lock6M_v_; +text: .text%__1cQinstanceRefKlassZacquire_pending_list_lock6FpnJBasicLock__v_; +text: .text%__1cXjava_lang_ref_ReferenceWpending_list_lock_addr6F_ppnHoopDesc__; +text: .text%__1cPVM_GC_OperationQgc_count_changed6kM_i_; +text: .text%__1cbAVM_GenCollectForAllocationEname6kM_pkc_: vm_operations.o; +text: .text%__1cbAVM_GenCollectForAllocationEdoit6M_v_; +text: .text%__1cNJvmtiGCMarker2t6Mi_v_; +text: .text%__1cQGenCollectedHeapZsatisfy_failed_allocation6MIiipi_pnIHeapWord__; +text: .text%__1cbCTwoGenerationCollectorPolicyZsatisfy_failed_allocation6MIiipi_pnIHeapWord__; +text: .text%__1cQGenCollectedHeapNdo_collection6MiiIiiipi_v_; +text: .text%__1cXTraceMemoryManagerStats2t6Mi_v_; +text: .text%__1cNMemoryServiceIgc_begin6Fi_v_; +text: .text%__1cPGCMemoryManagerIgc_begin6M_v_; +text: .text%__1cKManagementJtimestamp6F_x_; +text: .text%__1cKGCStatInfoMset_gc_usage6MinLMemoryUsage_i_v_; +text: .text%__1cTContiguousSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cTContiguousSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cbBSurvivorContiguousSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cbBSurvivorContiguousSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cOGenerationPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cOGenerationPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cQGenCollectedHeapLgc_prologue6Mi_v_; +text: .text%__1cNCollectedHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cWThreadLocalAllocBufferbFaccumulate_statistics_before_gc6F_v_; +text: .text%__1cWThreadLocalAllocBufferVaccumulate_statistics6MIi_v_; +text: .text%__1cPGlobalTLABStatsHpublish6M_v_; +text: .text%__1cQGenCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapOfill_all_tlabs6M_v_; +text: .text%__1cQGenCollectedHeapSgeneration_iterate6Mpn0AKGenClosure_i_v_; +text: .text%__1cbCGenEnsureParseabilityClosureNdo_generation6MpnKGeneration__v_: genCollectedHeap.o; +text: .text%__1cKGenerationTensure_parseability6M_v_: defNewGeneration.o; +text: .text%__1cKGenerationTensure_parseability6M_v_: tenuredGeneration.o; +text: .text%__1cKGenerationTensure_parseability6M_v_: compactingPermGenGen.o; +text: .text%__1cSAllocationProfilerViterate_since_last_gc6F_v_; +text: .text%__1cUGenGCPrologueClosureNdo_generation6MpnKGeneration__v_: genCollectedHeap.o; +text: .text%__1cQDefNewGenerationLgc_prologue6Mi_v_: defNewGeneration.o; +text: .text%__1cRTenuredGenerationLgc_prologue6Mi_v_; +text: .text%__1cKGenerationLgc_prologue6Mi_v_: compactingPermGenGen.o; +text: .text%__1cKGenerationOshould_collect6MiIii_i_: defNewGeneration.o; +text: .text%__1cQDefNewGenerationKshort_name6kM_pkc_: defNewGeneration.o; +text: .text%__1cKGenerationIcounters6M_pnRCollectorCounters__: defNewGeneration.o; +text: .text%__1cQGenCollectedHeapKsave_marks6M_v_; +text: .text%__1cQDefNewGenerationKsave_marks6M_v_; +text: .text%__1cbCOneContigSpaceCardGenerationKsave_marks6M_v_; +text: .text%__1cQDefNewGenerationHcollect6MiiIii_v_; +text: .text%__1cQDefNewGenerationbAcollection_attempt_is_safe6M_i_; +text: .text%__1cRTenuredGenerationZpromotion_attempt_is_safe6kMIi_i_; +text: .text%__1cKGenerationYmax_contiguous_available6kM_I_; +text: .text%__1cbCOneContigSpaceCardGenerationUcontiguous_available6kM_I_; +text: .text%__1cQDefNewGenerationbIinit_assuming_no_promotion_failure6M_v_; +text: .text%__1cQDefNewGenerationOIsAliveClosure2t6MpnKGeneration__v_; +text: .text%__1cSScanWeakRefClosure2t6MpnQDefNewGeneration__v_; +text: .text%__1cLCardTableRSbGprepare_for_younger_refs_iterate6Mi_v_; +text: .text%__1cULRUCurrentHeapPolicy2t6M_v_; +text: .text%__1cPCollectorPolicyPis_train_policy6M_i_: collectorPolicy.o; +text: .text%__1cPFastScanClosure2t6MpnQDefNewGeneration_i_v_; +text: .text%__1cQDefNewGenerationbCFastEvacuateFollowersClosure2t6MpnQGenCollectedHeap_ip0pnPFastScanClosure_6_v_; +text: .text%__1cQGenCollectedHeapUprocess_strong_roots6Miiin0ATClassScanningOption_pnQOopsInGenClosure_3_v_; +text: .text%__1cKSharedHeapbAchange_strong_roots_parity6M_v_; +text: .text%__1cMSubTasksDonePis_task_claimed6Mi_i_; +text: .text%__1cIUniverseHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cPFastScanClosureGdo_oop6MppnHoopDesc__v_: defNewGeneration.o; +text: .text%__1cQDefNewGenerationWcopy_to_survivor_space6MpnHoopDesc_p2_2_; +text: .text%__1cKJNIHandlesHoops_do6FpnKOopClosure__v_; +text: .text%__1cOJNIHandleBlockHoops_do6MpnKOopClosure__v_; +text: .text%__1cHThreadsHoops_do6FpnKOopClosure__v_; +text: .text%__1cKJavaThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cGThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cKHandleAreaHoops_do6MpnKOopClosure__v_; +text: .text%__1cNchunk_oops_do6FpnKOopClosure_pnFChunk_pc_I_: handles.o; +text: .text%__1cQStackFrameStream2t6MpnKJavaThread_i_v_; +text: .text%__1cFframeQoops_do_internal6MpnKOopClosure_pnLRegisterMap_i_v_; +text: .text%__1cFframeToops_interpreted_do6MpnKOopClosure_pknLRegisterMap_i_v_; +text: .text%__1cFframeVinterpreter_frame_bci6kM_i_; +text: .text%__1cFframebDinterpreter_frame_monitor_end6kM_pnPBasicObjectLock__; +text: .text%__1cFframebFinterpreter_frame_monitor_begin6kM_pnPBasicObjectLock__; +text: .text%__1cRInterpreterOopMap2t6M_v_; +text: .text%__1cRInterpreterOopMapKinitialize6M_v_; +text: .text%__1cNmethodOopDescImask_for6MipnRInterpreterOopMap__v_; +text: .text%__1cNinstanceKlassImask_for6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cLOopMapCache2t6M_v_; +text: .text%__1cLOopMapCacheGlookup6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cLOopMapCacheIentry_at6kMi_pnQOopMapCacheEntry__; +text: .text%__1cRInterpreterOopMapIis_empty6M_i_; +text: .text%__1cQOopMapCacheEntryEfill6MnMmethodHandle_i_v_; +text: .text%__1cQOopMapCacheEntryFflush6M_v_; +text: .text%__1cQOopMapCacheEntryTdeallocate_bit_mask6M_v_; +text: .text%__1cQOopMapCacheEntryPfill_for_native6M_v_; +text: .text%__1cQOopMapCacheEntryRallocate_bit_mask6M_v_; +text: .text%__1cTMaskFillerForNative2t6MnMmethodHandle_pIi_v_: oopMapCache.o; +text: .text%__1cNFingerprinterLfingerprint6M_X_: oopMapCache.o; +text: .text%__1cTMaskFillerForNativeLpass_object6M_v_: oopMapCache.o; +text: .text%__1cRInterpreterOopMapNresource_copy6MpnQOopMapCacheEntry__v_; +text: .text%__1cRInterpreterOopMapLiterate_oop6MpnNOffsetClosure__v_; +text: .text%__1cXInterpreterFrameClosureJoffset_do6Mi_v_: frame.o; +text: .text%__1cRInterpreterOopMap2T6M_v_; +text: .text%__1cTOopMapForCacheEntry2t6MnMmethodHandle_ipnQOopMapCacheEntry__v_; +text: .text%__1cTOopMapForCacheEntryLcompute_map6MpnGThread__v_; +text: .text%__1cTOopMapForCacheEntryRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cTOopMapForCacheEntryOreport_results6kM_i_: oopMapCache.o; +text: .text%__1cOGenerateOopMapVresult_for_basicblock6Mi_v_; +text: .text%__1cTOopMapForCacheEntryZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cQOopMapCacheEntryIset_mask6MpnNCellTypeState_2i_v_; +text: .text%__1cFframeNoops_entry_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cPJavaCallWrapperHoops_do6MpnKOopClosure__v_; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: oopMapCache.o; +text: .text%__1cTMaskFillerForNativeJpass_long6M_v_: oopMapCache.o; +text: .text%__1cFframebHnext_monitor_in_interpreter_frame6kMpnPBasicObjectLock__2_; +text: .text%__1cOGenerateOopMapPdo_monitorenter6Mi_v_; +text: .text%__1cOGenerateOopMapXreplace_all_CTS_matches6MnNCellTypeState_1_v_; +text: .text%__1cOGenerateOopMapMmonitor_push6MnNCellTypeState__v_; +text: .text%__1cQComputeCallStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cQComputeCallStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cOGenerateOopMapOdo_monitorexit6Mi_v_; +text: .text%__1cOGenerateOopMapLmonitor_pop6M_nNCellTypeState__; +text: .text%__1cRComputeEntryStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: frame.o; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_: oopMapCache.o; +text: .text%__1cFframebDoops_interpreted_arguments_do6MnMsymbolHandle_ipnKOopClosure__v_; +text: .text%__1cRArgumentOopFinderDset6MinJBasicType__v_: frame.o; +text: .text%__1cIVMThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cQVMOperationQdDueueHoops_do6MpnKOopClosure__v_; +text: .text%__1cQVMOperationQdDueueNqueue_oops_do6MipnKOopClosure__v_; +text: .text%__1cSObjectSynchronizerHoops_do6FpnKOopClosure__v_; +text: .text%__1cMFlatProfilerHoops_do6FpnKOopClosure__v_; +text: .text%__1cKManagementHoops_do6FpnKOopClosure__v_; +text: .text%__1cNMemoryServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cKMemoryPoolHoops_do6MpnKOopClosure__v_; +text: .text%__1cNMemoryManagerHoops_do6MpnKOopClosure__v_; +text: .text%__1cNThreadServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cLJvmtiExportHoops_do6FpnKOopClosure__v_; +text: .text%__1cXJvmtiCurrentBreakpointsHoops_do6FpnKOopClosure__v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollectorXoops_do_for_all_threads6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryHoops_do6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryRpreloaded_oops_do6FpnKOopClosure__v_; +text: .text%__1cKDictionaryHoops_do6MpnKOopClosure__v_; +text: .text%__1cPDictionaryEntrybDprotection_domain_set_oops_do6MpnKOopClosure__v_: dictionary.o; +text: .text%__1cQPlaceholderTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cVLoaderConstraintTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cUCompactingPermGenGenUyounger_refs_iterate6MpnQOopsInGenClosure__v_; +text: .text%__1cbCOneContigSpaceCardGenerationUyounger_refs_iterate6MpnQOopsInGenClosure__v_; +text: .text%__1cKGenerationbDyounger_refs_in_space_iterate6MpnFSpace_pnQOopsInGenClosure__v_; +text: .text%__1cLCardTableRSbDyounger_refs_in_space_iterate6MpnFSpace_pnQOopsInGenClosure__v_; +text: .text%__1cPContiguousSpaceLnew_dcto_cl6MpnKOopClosure_nRCardTableModRefBSOPrecisionStyle_pnIHeapWord__pnVDirtyCardToOopClosure__; +text: .text%__1cPContiguousSpaceZused_region_at_save_marks6kM_nJMemRegion__: space.o; +text: .text%__1cRCardTableModRefBSWnon_clean_card_iterate6MpnFSpace_nJMemRegion_pnVDirtyCardToOopClosure_pnQMemRegionClosure_i_v_; +text: .text%__1cRCardTableModRefBSbBnon_clean_card_iterate_work6MnJMemRegion_pnQMemRegionClosure_i_v_; +text: .text%__1cJMemRegionMintersection6kMk0_0_; +text: .text%__1cYClearNoncleanCardWrapperMdo_MemRegion6MnJMemRegion__v_: cardTableRS.o; +text: .text%__1cYClearNoncleanCardWrapperKclear_card6MpW_i_: cardTableRS.o; +text: .text%__1cVDirtyCardToOopClosureMdo_MemRegion6MnJMemRegion__v_; +text: .text%__1cWOffsetTableContigSpaceLblock_start6kMpkv_pnIHeapWord__: space.o; +text: .text%__1cbBBlockOffsetArrayContigSpaceSblock_start_unsafe6kMpkv_pnIHeapWord__; +text: .text%__1cPContiguousSpaceKblock_size6kMpknIHeapWord__I_; +text: .text%__1cUContiguousSpaceDCTOCOget_actual_top6MpnIHeapWord_2_2_; +text: .text%__1cPContiguousSpaceRtoContiguousSpace6M_p0_: space.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlass.o; +text: .text%__1cPFiltering_DCTOCPwalk_mem_region6MnJMemRegion_pnIHeapWord_3_v_; +text: .text%__1cUContiguousSpaceDCTOCXwalk_mem_region_with_cl6MnJMemRegion_pnIHeapWord_3pnQFilteringClosure__v_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: methodKlass.o; +text: .text%__1cLmethodKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: instanceKlassKlass.o; +text: .text%__1cSinstanceKlassKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cNinstanceKlassViterate_static_fields6MpnKOopClosure__v_; +text: .text%__1cLklassVtablePoop_oop_iterate6MpnKOopClosure__v_; +text: .text%__1cQFilteringClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cLklassItablePoop_oop_iterate6MpnKOopClosure__v_; +text: .text%__1cKklassKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cKOopClosureXshould_remember_klasses6kM_ki_: space.o; +text: .text%__1cNinstanceKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_; +text: .text%__1cWconstantPoolCacheKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: cpCacheKlass.o; +text: .text%__1cWconstantPoolCacheKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cWConstantPoolCacheEntryLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cParrayKlassKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cNobjArrayKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_; +text: .text%__1cNinstanceKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_; +text: .text%__1cNobjArrayKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: methodKlass.o; +text: .text%__1cLmethodKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: constantPoolKlass.o; +text: .text%__1cRconstantPoolKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constMethodKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constMethodKlass.o; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: symbolKlass.o; +text: .text%__1cLsymbolKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: symbolKlass.o; +text: .text%__1cLsymbolKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: constantPoolKlass.o; +text: .text%__1cRconstantPoolKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: cpCacheKlass.o; +text: .text%__1cWconstantPoolCacheKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cWConstantPoolCacheEntryNoop_iterate_m6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: cpCacheKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: cpCacheKlass.o; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: instanceKlassKlass.o; +text: .text%__1cSinstanceKlassKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cNinstanceKlassViterate_static_fields6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cLklassVtableRoop_oop_iterate_m6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cLklassItableRoop_oop_iterate_m6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cKklassKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cRInterpreterOopMapLoop_iterate6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cKOopClosureIdo_oop_v6MppnHoopDesc__v_: space.o; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cRInterpreterOopMapLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cVcompiledICHolderKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: compiledICHolderKlass.o; +text: .text%__1cVcompiledICHolderKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: klassKlass.o; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: klassKlass.o; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: arrayKlassKlass.o; +text: .text%__1cLCardTableRSUyounger_refs_iterate6MpnKGeneration_pnQOopsInGenClosure__v_; +text: .text%__1cMSubTasksDoneTall_tasks_completed6M_v_; +text: .text%__1cQDefNewGenerationbCFastEvacuateFollowersClosureHdo_void6M_v_; +text: .text%__1cQGenCollectedHeapbCoop_since_save_marks_iterate6MipnPFastScanClosure_2_v_; +text: .text%__1cQDefNewGenerationbFoop_since_save_marks_iterate_nv6MpnPFastScanClosure__v_; +text: .text%__1cPContiguousSpacebFoop_since_save_marks_iterate_nv6MpnPFastScanClosure__v_; +text: .text%__1cNobjArrayKlassSoop_oop_iterate_nv6MpnHoopDesc_pnPFastScanClosure__i_; +text: .text%__1cNinstanceKlassSoop_oop_iterate_nv6MpnHoopDesc_pnPFastScanClosure__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnPFastScanClosure__i_: typeArrayKlass.o; +text: .text%__1cQinstanceRefKlassSoop_oop_iterate_nv6MpnHoopDesc_pnPFastScanClosure__i_; +text: .text%__1cXjava_lang_ref_ReferenceNreferent_addr6FpnHoopDesc__p2_; +text: .text%__1cSReferenceProcessorSdiscover_reference6MpnHoopDesc_nNReferenceType__i_; +text: .text%__1cXjava_lang_ref_ReferenceJnext_addr6FpnHoopDesc__p2_; +text: .text%__1cXjava_lang_ref_ReferencePdiscovered_addr6FpnHoopDesc__p2_; +text: .text%__1cSReferenceProcessorTget_discovered_list6MnNReferenceType__ppnHoopDesc__; +text: .text%__1cKGenerationHpromote6MpnHoopDesc_Ip2_2_; +text: .text%__1cbCOneContigSpaceCardGenerationIallocate6MIii_pnIHeapWord__: tenuredGeneration.o; +text: .text%__1cbCOneContigSpaceCardGenerationbFoop_since_save_marks_iterate_nv6MpnPFastScanClosure__v_; +text: .text%__1cQGenCollectedHeapbAno_allocs_since_save_marks6Mi_i_; +text: .text%__1cQDefNewGenerationbAno_allocs_since_save_marks6M_i_; +text: .text%__1cbCOneContigSpaceCardGenerationbAno_allocs_since_save_marks6M_i_; +text: .text%__1cQDefNewGenerationUFastKeepAliveClosure2t6Mp0pnSScanWeakRefClosure__v_; +text: .text%__1cQDefNewGenerationQKeepAliveClosure2t6MpnSScanWeakRefClosure__v_; +text: .text%__1cbDReferenceProcessorInitializerIis_clean6kM_v_: concurrentMarkSweepGeneration.o; +text: .text%__1cSReferenceProcessorbDprocess_discovered_references6M_v_; +text: .text%__1cSReferenceProcessorbAprocess_discovered_reflist6MppnHoopDesc_pnPReferencePolicy_i_v_; +text: .text%__1cSReferenceProcessorOprocess_phase16MppnHoopDesc_pnPReferencePolicy_pnRBoolObjectClosure_pnKOopClosure_pnLVoidClosure__v_; +text: .text%__1cQDefNewGenerationOIsAliveClosureLdo_object_b6MpnHoopDesc__i_; +text: .text%__1cULRUCurrentHeapPolicyWshould_clear_reference6MpnHoopDesc__i_; +text: .text%__1cbBjava_lang_ref_SoftReferenceFclock6F_x_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJtimestamp6FpnHoopDesc__x_; +text: .text%__1cXjava_lang_ref_ReferenceIset_next6FpnHoopDesc_2_v_; +text: .text%__1cQDefNewGenerationUFastKeepAliveClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cSReferenceProcessorOprocess_phase26MppnHoopDesc_pnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSReferenceProcessorOprocess_phase36MppnHoopDesc_ipnRBoolObjectClosure_pnKOopClosure_pnLVoidClosure__v_; +text: .text%__1cSReferenceProcessorQprocess_phaseJNI6M_v_; +text: .text%__1cKJNIHandlesMweak_oops_do6FpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cOJNIHandleBlockMweak_oops_do6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cQDefNewGenerationLswap_spaces6M_v_; +text: .text%__1cIageTablebAcompute_tenuring_threshold6MI_i_; +text: .text%__1cKGenerationWupdate_time_of_last_gc6Mx_v_: defNewGeneration.o; +text: .text%__1cSReferenceProcessorbDenqueue_discovered_references6M_i_; +text: .text%__1cXjava_lang_ref_ReferenceRpending_list_addr6F_ppnHoopDesc__; +text: .text%__1cSReferenceProcessorbBenqueue_discovered_reflists6MppnHoopDesc__v_; +text: .text%__1cSReferenceProcessorbAenqueue_discovered_reflist6MpnHoopDesc_p2_v_; +text: .text%__1cQGenCollectedHeapPupdate_gc_stats6Mii_v_: genCollectedHeap.o; +text: .text%__1cKGenerationPupdate_gc_stats6Mii_v_: defNewGeneration.o; +text: .text%__1cRTenuredGenerationPupdate_gc_stats6Mii_v_; +text: .text%__1cVAdaptivePaddedAverageGsample6Mf_v_; +text: .text%__1cKGenerationPupdate_gc_stats6Mii_v_: compactingPermGenGen.o; +text: .text%__1cRTenuredGenerationOshould_collect6MiIii_i_; +text: .text%__1cKGenerationPshould_allocate6MIii_i_: tenuredGeneration.o; +text: .text%__1cbCOneContigSpaceCardGenerationEfree6kM_I_; +text: .text%__1cQDefNewGenerationQcompute_new_size6M_v_; +text: .text%__1cNMemoryServiceStrack_memory_usage6F_v_; +text: .text%__1cRLowMemoryDetectorRdetect_low_memory6F_v_; +text: .text%__1cQGenCollectedHeapLgc_epilogue6Mi_v_; +text: .text%__1cNCollectedHeapQresize_all_tlabs6M_v_; +text: .text%__1cWThreadLocalAllocBufferQresize_all_tlabs6F_v_; +text: .text%__1cWThreadLocalAllocBufferGresize6M_v_; +text: .text%__1cUGenGCEpilogueClosureNdo_generation6MpnKGeneration__v_: genCollectedHeap.o; +text: .text%__1cQDefNewGenerationLgc_epilogue6Mi_v_; +text: .text%__1cRTenuredGenerationLgc_epilogue6Mi_v_; +text: .text%__1cbCOneContigSpaceCardGenerationLgc_epilogue6Mi_v_; +text: .text%__1cRTenuredGenerationPupdate_counters6M_v_; +text: .text%__1cUCompactingPermGenGenPupdate_counters6M_v_; +text: .text%__1cXTraceMemoryManagerStats2T6M_v_; +text: .text%__1cNMemoryServiceGgc_end6Fi_v_; +text: .text%__1cPGCMemoryManagerGgc_end6M_v_; +text: .text%__1cRLowMemoryDetectorWdetect_after_gc_memory6FpnKMemoryPool__v_; +text: .text%__1cNJvmtiGCMarker2T6M_v_; +text: .text%__1cPVM_GC_OperationNdoit_epilogue6M_v_; +text: .text%__1cPVM_GC_OperationbKrelease_and_notify_pending_list_lock6M_v_; +text: .text%__1cQinstanceRefKlassbKrelease_and_notify_pending_list_lock6FipnJBasicLock__v_; +text: .text%__1cXJNI_ArgumentPusherVaArgIget_long6M_v_: jni.o; +text: .text%jni_GetIntArrayRegion: jni.o; +text: .text%jni_SetIntArrayRegion: jni.o; +text: .text%jni_PushLocalFrame: jni.o; +text: .text%jni_PopLocalFrame: jni.o; +text: .text%__1cMGraphBuilderJnegate_op6MpnJValueType__v_; +text: .text%__1cINegateOpFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerLdo_NegateOp6MpnINegateOp__v_; +text: .text%__1cMLinkResolverbPlinktime_resolve_interface_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cPciInstanceKlassLimplementor6M_p0_; +text: .text%__1cINegateOpPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorLdo_NegateOp6MpnINegateOp__v_; +text: .text%__1cIValueGenJspill_one6MpnJValueType__v_; +text: .text%__1cIRegAllocbBget_smallest_value_to_spill6kMpnJValueType__pnLInstruction__; +text: .text%__1cLLIR_EmitterRarray_store_check6MpnLLIR_OprDesc_2nFRInfo_33pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListLstore_check6MpnLLIR_OprDesc_2222pnMCodeEmitInfo__v_; +text: .text%__1cPLIR_OpTypeCheck2t6MnILIR_Code_pnLLIR_OprDesc_3333pnMCodeEmitInfo__v_; +text: .text%__1cXArrayStoreExceptionStub2t6MpnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListLshift_right6MnFRInfo_i1_v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListLshift_right6MpnLLIR_OprDesc_222_v_; +text: .text%__1cIValueGenLdo_NegateOp6MpnINegateOp__v_; +text: .text%__1cLLIR_EmitterGnegate6MnFRInfo_pnLLIR_OprDesc__v_; +text: .text%__1cILIR_ListGnegate6MnFRInfo_1_v_: c1_LIREmitter.o; +text: .text%__1cXArrayStoreExceptionStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_i486.o; +text: .text%__1cXArrayStoreExceptionStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cNLIR_AssemblerEleal6MpnLLIR_OprDesc_2_v_; +text: .text%__1cNLIR_AssemblerGnegate6MpnLLIR_OprDesc_2_v_; +text: .text%__1cNCodeStubArrayIindex_of6kMkpnICodeStub__i_: c1_LIRAssembler_i486.o; +text: .text%__1cXArrayStoreExceptionStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cIRuntime1Tresolve_static_call6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cSCompiledStaticCallNcompute_entry6FnMmethodHandle_rnOStaticCallInfo__v_; +text: .text%__1cSCompiledStaticCallIis_clean6kM_i_; +text: .text%__1cSCompiledStaticCallDset6MrknOStaticCallInfo__v_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cHciKlassSsuper_check_offset6M_I_; +text: .text%__1cIRuntime1Thandle_wrong_method6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cNSharedRuntimeTreresolve_call_site6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cFframeRis_compiled_frame6kMpi_i_; +text: .text%__1cHnmethodOis_java_method6kM_i_: nmethod.o; +text: .text%__1cGEventsDlog6FpkcE_v_: sharedRuntime.o; +text: .text%__1cJCodeCacheMfind_nmethod6Fpv_pnHnmethod__; +text: .text%__1cNRelocIteratorEnext6M_i_: sharedRuntime.o; +text: .text%__1cKCompiledICMset_to_clean6M_v_; +text: .text%__1cKCompiledICMstub_address6kM_pC_; +text: .text%__1cGICStubFclear6M_v_; +text: .text%__1cNSharedRuntimeSfind_callee_method6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cRInlineCacheBufferSic_destination_for6FpnKCompiledIC__pC_; +text: .text%__1cIRuntime1Jarraycopy6FpnHoopDesc_i2ii_i_; +text: .text%__1cMGraphBuilderNadd_dependent6MpnPciInstanceKlass_pnIciMethod__v_; +text: .text%__1cYDebugInformationRecorderNadd_dependent6MpnPciInstanceKlass_pnIciMethod__v_; +text: .text%__1cNinstanceKlassVadd_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cJCodeCacheXmark_for_deoptimization6FpnMklassOopDesc__i_; +text: .text%__1cNinstanceKlassXmark_dependent_nmethods6MpnMklassOopDesc__i_; +text: .text%jni_NewWeakGlobalRef: jni.o; +text: .text%__1cKJNIHandlesQmake_weak_global6FnGHandle__pnI_jobject__; +text: .text%__1cMLinkResolverbBlookup_method_in_interfaces6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%jni_CallIntMethodV: jni.o; +text: .text%Unsafe_GetObject; +text: .text%jni_CallBooleanMethod: jni.o; +text: .text%jni_CallVoidMethodV: jni.o; +text: .text%JVM_GetClassDeclaredMethods; +text: .text%JVM_InvokeMethod; +text: .text%__1cKReflectionNinvoke_method6FpnHoopDesc_nGHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cYjava_lang_reflect_MethodFclazz6FpnHoopDesc__2_; +text: .text%__1cYjava_lang_reflect_MethodEslot6FpnHoopDesc__i_; +text: .text%__1cYjava_lang_reflect_MethodPparameter_types6FpnHoopDesc__2_; +text: .text%__1cYjava_lang_reflect_MethodLreturn_type6FpnHoopDesc__2_; +text: .text%JVM_IsInterrupted; +# Test LoadJFrame +text: .text%__1cTresource_free_bytes6FpcI_v_; +text: .text%__1cRComputeEntryStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cJFloatTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cJFloatTypeEbase6kM_pnJValueType__: c1_ValueType.o; +text: .text%__1cJFloatTypeMas_FloatType6M_p0_: c1_ValueType.o; +text: .text%__1cIValueGenTdo_ArithmeticOp_FPU6MpnMArithmeticOp__v_; +text: .text%__1cHLockRegIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocOset_locked_fpu6MipnLInstruction_i_v_; +text: .text%__1cIValueGenNis_32bit_mode6M_i_; +text: .text%__1cLGetRefCountIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cJFloatTypeEsize6kM_i_: c1_ValueType.o; +text: .text%__1cHFreeRegIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocMset_free_fpu6Mi_v_; +text: .text%__1cQChangeSpillCountIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cLLIR_EmitterRarithmetic_op_fpu6MnJBytecodesECode_pnLLIR_OprDesc_44i_v_; +text: .text%__1cILIR_ListDmul6MpnLLIR_OprDesc_22_v_: c1_LIREmitter.o; +text: .text%__1cIValueGenKround_item6MpnEItem__v_; +text: .text%__1cLLIR_EmitterFround6MipnLLIR_OprDesc__v_; +text: .text%__1cILIR_ListKround32bit6MnFRInfo_i_v_: c1_LIREmitter.o; +text: .text%__1cIValueGenOspill_register6MnFRInfo__v_; +text: .text%__1cIRegAllocTget_value_for_rinfo6kMnFRInfo__pnLInstruction__; +text: .text%__1cLGetValueForGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIValueGenKdivInRInfo6F_nFRInfo__; +text: .text%__1cIValueGenLremOutRInfo6F_nFRInfo__; +text: .text%__1cMArithmeticOpKlock_stack6kM_pnKValueStack__: c1_Instruction.o; +text: .text%__1cLLIR_EmitterParithmetic_idiv6MnJBytecodesECode_pnLLIR_OprDesc_44nFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListEirem6MnFRInfo_111pnMCodeEmitInfo__v_; +text: .text%__1cHLIR_Op3Fvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cHLIR_Op3Jemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerIemit_op36MpnHLIR_Op3__v_; +text: .text%__1cNLIR_AssemblerIfpu_push6MnFRInfo__v_; +text: .text%__1cIFrameMapLFpuStackSimEpush6Mi_v_; +text: .text%__1cNLIR_AssemblerKfpu_on_tos6MnFRInfo__v_; +text: .text%__1cIFrameMapLFpuStackSimPoffset_from_tos6kMi_i_; +text: .text%__1cIintArrayIindex_of6kMki_i_: c1_FrameMap_i486.o; +text: .text%__1cNLIR_AssemblerHfpu_pop6MnFRInfo__v_; +text: .text%__1cIFrameMapLFpuStackSimDpop6Mi_i_; +text: .text%__1cNLIR_AssemblerKround32_op6MpnLLIR_OprDesc_2_v_; +text: .text%__1cJAssemblerGfist_s6MnHAddress__v_; +text: .text%__1cNLIR_AssemblerJreset_FPU6M_v_; +text: .text%__1cNLIR_AssemblerIemit_op36MpnHLIR_Op3__v_; +text: .text%__1cNLIR_AssemblerParithmetic_idiv6MnILIR_Code_pnLLIR_OprDesc_333pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerXadd_debug_info_for_div06MipnMCodeEmitInfo__v_; +text: .text%__1cNDivByZeroStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cNDivByZeroStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cLInstructionOas_ArrayLength6M_pnLArrayLength__: c1_GraphBuilder.o; +text: .text%__1cLInstructionKas_ShiftOp6M_pnHShiftOp__: c1_Instruction.o; +text: .text%__1cILIR_ListLlogical_xor6MnFRInfo_pnLLIR_OprDesc_1_v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListUunsigned_shift_right6MnFRInfo_i1_v_: c1_LIREmitter.o; +text: .text%__1cIRuntime1Ohandle_ic_miss6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cNSharedRuntimeVhandle_ic_miss_helper6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2t6M_v_; +text: .text%__1cKCompiledICOis_megamorphic6kM_i_; +text: .text%__1cLVtableStubsOis_entry_point6FpC_i_; +text: .text%__1cKCompiledICSset_to_megamorphic6MpnICallInfo_nJBytecodesECode_pnGThread__v_; +text: .text%__1cLVtableStubsLcreate_stub6FiipnNmethodOopDesc__pC_; +text: .text%__1cLVtableStubsGlookup6Fiii_pnKVtableStub__; +text: .text%__1cLVtableStubsScreate_vtable_stub6Fii_pnKVtableStub__; +text: .text%__1cKVtableStubSpd_code_size_limit6Fi_i_; +text: .text%__1cKVtableStub2n6FIi_pv_; +text: .text%__1cKVtableStubRpd_code_alignment6F_i_; +text: .text%__1cLVtableStubsFenter6FiiipnKVtableStub__v_; +text: .text%__1cGEventsDlog6FpkcE_v_: compiledIC.o; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2T6M_v_; +text: .text%Unsafe_EnsureClassInitialized; +text: .text%Unsafe_StaticFieldOffset; +text: .text%Unsafe_StaticFieldBaseFromField; +text: .text%Unsafe_GetIntVolatile; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: generateOopMap.o; +text: .text%__1cUBytecode_tableswitchOdest_offset_at6kMi_i_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: c1_GraphBuilder.o; +text: .text%__1cLInstructionKas_ShiftOp6M_pnHShiftOp__: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderMtable_switch6M_v_; +text: .text%__1cLTableSwitchFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerOdo_TableSwitch6MpnLTableSwitch__v_; +text: .text%__1cLInstructionJas_Return6M_pnGReturn__: c1_GraphBuilder.o; +text: .text%__1cGSwitchPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorOdo_TableSwitch6MpnLTableSwitch__v_; +text: .text%__1cIValueGenOdo_TableSwitch6MpnLTableSwitch__v_; +text: .text%__1cIValueGenVsetup_phis_for_switch6MpnEItem_pnKValueStack__v_; +text: .text%__1cLLIR_EmitterOtableswitch_op6MpnLLIR_OprDesc_ipnKBlockBegin__v_; +text: .text%__1cWstatic_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cSCompiledStaticCallMset_to_clean6M_v_; +# Test JHello +text: .text%__1cYjava_lang_reflect_MethodNset_signature6FpnHoopDesc_2_v_; +text: .text%JVM_InitializeSocketLibrary; +text: .text%__1cDhpiZinitialize_socket_library6F_i_; +text: .text%JVM_Socket; +text: .text%Unsafe_PageSize; +text: .text%__1cNFingerprinterHdo_byte6M_v_: dump.o; +text: .text%__1cXNativeSignatureIteratorHdo_byte6M_v_: interpreterRuntime.o; +text: .text%Unsafe_SetMemory; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: unsafe.o; +text: .text%__1cNSharedRuntimeElrem6Fxx_x_; +text: .text%Unsafe_DefineClass1; +text: .text%__1cSUnsafe_DefineClass6FpnHJNIEnv__pnI_jstring_pnL_jbyteArray_iipnI_jobject_7_pnH_jclass__: unsafe.o; +text: .text%JVM_DefineClass; +text: .text%__1cPClassFileParserXverify_unqualified_name6MpcIi_i_; +text: .text%__1cVLoaderConstraintTableYextend_loader_constraint6MpnVLoaderConstraintEntry_nGHandle_pnMklassOopDesc__v_; +text: .text%__1cVLoaderConstraintTablebHensure_loader_constraint_capacity6MpnVLoaderConstraintEntry_i_v_; +text: .text%__1cIciObjectIis_klass6M_i_: ciInstance.o; +text: .text%__1cQInstanceConstantIencoding6kM_pnI_jobject__; +text: .text%__1cLInstructionOas_ArrayLength6M_pnLArrayLength__: c1_Instruction.o; +text: .text%__1cILIR_ListQunwind_exception6MnFRInfo_1pnMCodeEmitInfo__v_: c1_CodeGenerator.o; +text: .text%__1cIRuntime1Tprimitive_arraycopy6FpnIHeapWord_2i_v_; +text: .text%__1cRComputeEntryStackHdo_char6M_v_: generateOopMap.o; +text: .text%jni_NewDirectByteBuffer; +text: .text%__1cbDinitializeDirectBufferSupport6FpnHJNIEnv___i_: jni.o; +text: .text%lookupDirectBufferClasses: jni.o; +text: .text%__1cJlookupOne6FpnHJNIEnv__pkcpnGThread__pnH_jclass__: jni.o; +text: .text%__1cHJNIEnv_JNewObject6MpnH_jclass_pnK_jmethodID_E_pnI_jobject__: jni.o; +text: .text%jni_GetDoubleArrayRegion: jni.o; +text: .text%__1cNSignatureInfoJdo_double6M_v_: bytecode.o; +text: .text%__1cXJNI_ArgumentPusherVaArgJget_float6M_v_: jni.o; +text: .text%__1cQComputeCallStackHdo_byte6M_v_: generateOopMap.o; +text: .text%__1cFKlassQup_cast_abstract6M_p0_; +text: .text%__1cRComputeEntryStackHdo_byte6M_v_: generateOopMap.o; +text: .text%__1cNSharedRuntimeDd2i6Fd_i_; +text: .text%__1cSInterpreterRuntimeWslow_signature_handler6FpnKJavaThread_pnNmethodOopDesc_pi5_pC_; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_: interpreterRT_i486.o; +text: .text%__1cUSlowSignatureHandlerLpass_object6M_v_: interpreterRT_i486.o; +text: .text%__1cXNativeSignatureIteratorIdo_array6Mii_v_: interpreterRT_i486.o; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_: interpreterRT_i486.o; +text: .text%__1cUSlowSignatureHandlerIpass_int6M_v_: interpreterRT_i486.o; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_: interpreterRT_i486.o; +text: .text%jni_GetFloatArrayRegion: jni.o; +text: .text%jni_GetCharArrayRegion: jni.o; +text: .text%jni_SetFloatField: jni.o; +text: .text%jni_NewFloatArray: jni.o; +text: .text%jni_SetFloatArrayRegion: jni.o; +# SwingSet +text: .text%JVM_GetFieldIxModifiers; +text: .text%JVM_GetCPFieldClassNameUTF; +text: .text%JVM_GetCPFieldModifiers; +text: .text%__1cPClassFileParserUverify_constantvalue6MiinSconstantPoolHandle_pnGThread__v_; +text: .text%__1cXjava_lang_ref_ReferenceOset_discovered6FpnHoopDesc_2_v_; +text: .text%__1cMStoreIndexedPother_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%JVM_MonitorNotify; +text: .text%__1cSObjectSynchronizerGnotify6FnGHandle_pnGThread__v_; +text: .text%__1cKValueStackElock6MpnHIRScope_pnLInstruction__i_; +text: .text%__1cKValueStackGunlock6M_i_; +text: .text%__1cLLIR_EmitterVmonitorenter_at_entry6MnFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cLLIR_EmitterNmonitor_enter6MnFRInfo_111ipnMCodeEmitInfo_3_v_; +text: .text%__1cQMonitorEnterStub2t6MnFRInfo_1pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListbAload_stack_address_monitor6MinFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListLlock_object6MnFRInfo_111pnICodeStub_pnMCodeEmitInfo__v_; +text: .text%__1cIValueGenNsyncTempRInfo6F_nFRInfo__; +text: .text%__1cLLIR_EmitterQreturn_op_prolog6Mi_v_; +text: .text%__1cLLIR_EmitterMmonitor_exit6MnFRInfo_11i_v_; +text: .text%__1cILIR_ListNunlock_object6MnFRInfo_11pnICodeStub__v_; +text: .text%__1cKLIR_OpLockFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cQMonitorEnterStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_i486.o; +text: .text%__1cRMonitorAccessStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_i486.o; +text: .text%__1cKLIR_OpLockJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerJemit_lock6MpnKLIR_OpLock__v_; +text: .text%__1cNLIR_AssemblerPmonitor_address6MinFRInfo__v_; +text: .text%__1cIFrameMapbEaddress_for_monitor_lock_index6kMi_nHAddress__; +text: .text%__1cIFrameMapbAfp_offset_for_monitor_lock6kMi_i_; +text: .text%__1cNLIR_AssemblerJemit_lock6MpnKLIR_OpLock__v_; +text: .text%__1cRC1_MacroAssemblerLlock_object6MpnMRegisterImpl_22rnFLabel__v_; +text: .text%__1cQMonitorEnterStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_i486.o; +text: .text%__1cIFrameMapWmonitor_object_regname6kMi_nHOptoRegEName__; +text: .text%__1cIFrameMapbCfp_offset_for_monitor_object6kMi_i_; +text: .text%__1cMCodeEmitInfobHlocation_for_monitor_object_index6Mi_nILocation__; +text: .text%__1cIFrameMapbHlocation_for_monitor_object_index6kMipnILocation__i_; +text: .text%__1cMCodeEmitInfobFlocation_for_monitor_lock_index6Mi_nILocation__; +text: .text%__1cIFrameMapbFlocation_for_monitor_lock_index6kMipnILocation__i_; +text: .text%__1cMMonitorValue2t6MpnKScopeValue_nILocation__v_; +text: .text%__1cMMonitorValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cRC1_MacroAssemblerNunlock_object6MpnMRegisterImpl_22rnFLabel__v_; +text: .text%__1cPMonitorExitStubMis_call_stub6kM_i_: c1_CodeStubs_i486.o; +text: .text%__1cQMonitorEnterStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cNLIR_AssemblerRload_receiver_reg6MpnMRegisterImpl__v_; +text: .text%__1cNLIR_AssemblerLmonitorexit6MnFRInfo_1pnMRegisterImpl_i3_v_; +text: .text%__1cPMonitorExitStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%jni_NewIntArray: jni.o; +text: .text%__1cNCollectedHeapYlarge_typearray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cQinstanceRefKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_; +text: .text%__1cRTenuredGenerationKshort_name6kM_pkc_: tenuredGeneration.o; +text: .text%__1cKGenerationIcounters6M_pnRCollectorCounters__: tenuredGeneration.o; +text: .text%__1cRTenuredGenerationHcollect6MiiIii_v_; +text: .text%__1cRTenuredGenerationbJretire_alloc_buffers_before_full_gc6M_v_; +text: .text%__1cbCOneContigSpaceCardGenerationHcollect6MiiIii_v_; +text: .text%__1cMGenMarkSweepTinvoke_at_safepoint6FipnSReferenceProcessor_i_v_; +text: .text%__1cJCodeCacheLgc_prologue6F_v_; +text: .text%__1cHThreadsLgc_prologue6F_v_; +text: .text%__1cKJavaThreadLgc_prologue6M_v_; +text: .text%__1cKJavaThreadJframes_do6MpFpnFframe_pknLRegisterMap__v_v_; +text: .text%__1cRframe_gc_prologue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeLgc_prologue6M_v_; +text: .text%__1cQGenCollectedHeapRsave_used_regions6Mii_v_; +text: .text%__1cKGenerationQsave_used_region6M_v_: tenuredGeneration.o; +text: .text%__1cbCOneContigSpaceCardGenerationLused_region6kM_nJMemRegion__; +text: .text%__1cPContiguousSpaceLused_region6kM_nJMemRegion__: space.o; +text: .text%__1cKGenerationQsave_used_region6M_v_: defNewGeneration.o; +text: .text%__1cKGenerationLused_region6kM_nJMemRegion__: defNewGeneration.o; +text: .text%__1cKGenerationQsave_used_region6M_v_: compactingPermGenGen.o; +text: .text%__1cMGenMarkSweepPallocate_stacks6F_v_; +text: .text%__1cQGenCollectedHeapOgather_scratch6MpnKGeneration_I_pnMScratchBlock__; +text: .text%__1cQDefNewGenerationScontribute_scratch6MrpnMScratchBlock_pnKGeneration_I_v_; +text: .text%__1cKGenerationScontribute_scratch6MrpnMScratchBlock_p0I_v_: tenuredGeneration.o; +text: .text%__1cRsort_scratch_list6FrpnMScratchBlock__v_: genCollectedHeap.o; +text: .text%__1cVremoveSmallestScratch6FppnMScratchBlock__1_: genCollectedHeap.o; +text: .text%__1cMGenMarkSweepRmark_sweep_phase16Firii_v_; +text: .text%__1cJEventMark2t6MpkcE_v_: genMarkSweep.o; +text: .text%__1cJMarkSweepRFollowRootClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cJMarkSweepLfollow_root6FppnHoopDesc__v_; +text: .text%__1cParrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLklassVtableToop_follow_contents6M_v_; +text: .text%__1cJMarkSweepO_mark_and_push6FppnHoopDesc__v_; +text: .text%__1cKklassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJMarkSweepXrevisit_weak_klass_link6FpnFKlass__v_; +text: .text%__1cJMarkSweepMfollow_stack6F_v_; +text: .text%__1cNinstanceKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cSinstanceKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNinstanceKlassUfollow_static_fields6M_v_; +text: .text%__1cLklassItableToop_follow_contents6M_v_; +text: .text%__1cJMarkSweepNpreserve_mark6FpnHoopDesc_pnLmarkOopDesc__v_; +text: .text%__1cLsymbolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cOtypeArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMjniIdMapBaseHoops_do6MpnKOopClosure__v_; +text: .text%__1cIjniIdMapHoops_do6MpnKOopClosure__v_; +text: .text%__1cJMarkSweepSMarkAndPushClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cNobjArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJMarkSweepPmark_and_follow6FppnHoopDesc__v_; +text: .text%__1cSobjArrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cRconstantPoolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWconstantPoolCacheKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWConstantPoolCacheEntryPfollow_contents6M_v_; +text: .text%__1cLmethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQconstMethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQinstanceRefKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cFJNIidHoops_do6MpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryValways_strong_oops_do6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryYalways_strong_classes_do6FpnKOopClosure__v_; +text: .text%__1cKDictionaryYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryPplaceholders_do6FpnKOopClosure__v_; +text: .text%__1cVLoaderConstraintTableYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cJvmSymbolsHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cJMarkSweepOIsAliveClosureLdo_object_b6MpnHoopDesc__i_: markSweep.o; +text: .text%__1cJMarkSweepQKeepAliveClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cJMarkSweepSFollowStackClosureHdo_void6M_v_: markSweep.o; +text: .text%__1cQSystemDictionaryMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cKDictionaryMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cVLoaderConstraintTableYpurge_loader_constraints6MpnRBoolObjectClosure__v_; +text: .text%__1cJCodeCacheMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cJCodeCacheFfirst6F_pnICodeBlob__; +text: .text%__1cICodeHeapLfirst_block6kM_pnJHeapBlock__; +text: .text%__1cICodeHeapJnext_free6kMpnJHeapBlock__pv_; +text: .text%__1cJCodeCacheFalive6FpnICodeBlob__2_; +text: .text%__1cKBufferBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cKBufferBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cJCodeCacheEnext6FpnICodeBlob__2_; +text: .text%__1cICodeHeapLblock_start6kMpv_pnJHeapBlock__; +text: .text%__1cICodeHeapKnext_block6kMpnJHeapBlock__2_; +text: .text%__1cNSingletonBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cNSingletonBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cLRuntimeStubIis_alive6kM_i_: codeBlob.o; +text: .text%__1cLRuntimeStubbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cHnmethodIis_alive6kM_i_: nmethod.o; +text: .text%__1cHnmethodbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cHnmethodOis_not_entrant6kM_i_: nmethod.o; +text: .text%__1cHnmethodbHfollow_root_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_ppnHoopDesc_iri_v_; +text: .text%__1cOoop_RelocationJoop_value6M_pnHoopDesc__; +text: .text%__1cVcompiledICHolderKlassSoop_being_unloaded6MpnRBoolObjectClosure_pnHoopDesc__i_; +text: .text%__1cVcompiledICHolderKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJMarkSweepXfollow_weak_klass_links6F_v_; +text: .text%__1cFKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cNinstanceKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cJHashtableGunlink6MpnRBoolObjectClosure__v_; +text: .text%__1cMGenMarkSweepRmark_sweep_phase26F_v_; +text: .text%__1cQGenCollectedHeapWprepare_for_compaction6M_v_; +text: .text%__1cKGenerationWprepare_for_compaction6MpnMCompactPoint__v_; +text: .text%__1cbCOneContigSpaceCardGenerationWfirst_compaction_space6kM_pnQCompactibleSpace__: tenuredGeneration.o; +text: .text%__1cPContiguousSpaceWprepare_for_compaction6MpnMCompactPoint__v_; +text: .text%__1cWOffsetTableContigSpaceUinitialize_threshold6M_pnIHeapWord__; +text: .text%__1cMTenuredSpaceSallowed_dead_ratio6kM_i_; +text: .text%__1cQCompactibleSpaceHforward6MpnHoopDesc_IpnMCompactPoint_pnIHeapWord__6_; +text: .text%__1cWOffsetTableContigSpacePcross_threshold6MpnIHeapWord_2_2_; +text: .text%__1cQCompactibleSpaceQinsert_deadspace6MrIpnIHeapWord_I_i_; +text: .text%__1cQCompactibleSpaceVnext_compaction_space6kM_p0_: space.o; +text: .text%__1cQDefNewGenerationWfirst_compaction_space6kM_pnQCompactibleSpace__: defNewGeneration.o; +text: .text%__1cQCompactibleSpaceSallowed_dead_ratio6kM_i_: space.o; +text: .text%__1cbCOneContigSpaceCardGenerationWfirst_compaction_space6kM_pnQCompactibleSpace__: compactingPermGenGen.o; +text: .text%__1cPContigPermSpaceSallowed_dead_ratio6kM_i_; +text: .text%__1cMGenMarkSweepRmark_sweep_phase36Fi_v_; +text: .text%__1cUCompactingPermGenGenTpre_adjust_pointers6M_v_; +text: .text%__1cJMarkSweepUAdjustPointerClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cQGenCollectedHeapSprocess_weak_roots6MpnKOopClosure_2_v_; +text: .text%__1cJCodeCacheHoops_do6FpnKOopClosure__v_; +text: .text%__1cKBufferBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cSDeoptimizationBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cLRuntimeStubHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cNSafepointBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cHnmethodHoops_do6MpnKOopClosure__v_; +text: .text%__1cJHashtableHoops_do6MpnKOopClosure__v_; +text: .text%__1cSReferenceProcessorPoops_do_statics6FpnKOopClosure__v_; +text: .text%__1cSReferenceProcessorHoops_do6MpnKOopClosure__v_; +text: .text%__1cJMarkSweepMadjust_marks6F_v_; +text: .text%__1cYGenAdjustPointersClosureNdo_generation6MpnKGeneration__v_: genMarkSweep.o; +text: .text%__1cKGenerationPadjust_pointers6M_v_; +text: .text%__1cbCOneContigSpaceCardGenerationNspace_iterate6MpnMSpaceClosure_i_v_; +text: .text%__1cVAdjustPointersClosureIdo_space6MpnFSpace__v_: generation.o; +text: .text%__1cQCompactibleSpacePadjust_pointers6M_v_; +text: .text%__1cOtypeArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNobjArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQinstanceRefKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQDefNewGenerationNspace_iterate6MpnMSpaceClosure_i_v_; +text: .text%__1cUCompactingPermGenGenPadjust_pointers6M_v_; +text: .text%__1cKklassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLsymbolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cParrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLklassVtableToop_adjust_pointers6M_v_; +text: .text%__1cSobjArrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cRconstantPoolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQconstMethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSinstanceKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassUadjust_static_fields6M_v_; +text: .text%__1cLklassItableToop_adjust_pointers6M_v_; +text: .text%__1cWconstantPoolCacheKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cWConstantPoolCacheEntryPadjust_pointers6M_v_; +text: .text%__1cVcompiledICHolderKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cMGenMarkSweepRmark_sweep_phase46F_v_; +text: .text%__1cUCompactingPermGenGenHcompact6M_v_; +text: .text%__1cQCompactibleSpaceHcompact6M_v_; +text: .text%__1cPContiguousSpaceWreset_after_compaction6M_v_: space.o; +text: .text%__1cRGenCompactClosureNdo_generation6MpnKGeneration__v_: genMarkSweep.o; +text: .text%__1cKGenerationHcompact6M_v_; +text: .text%__1cUCompactingPermGenGenMpost_compact6M_v_; +text: .text%__1cJMarkSweepNrestore_marks6F_v_; +text: .text%__1cMGenMarkSweepRdeallocate_stacks6F_v_; +text: .text%__1cLCardTableRSSclear_into_younger6MpnKGeneration_i_v_; +text: .text%__1cLCardTableRSFclear6MnJMemRegion__v_: cardTableRS.o; +text: .text%__1cRCardTableModRefBSFclear6MnJMemRegion__v_; +text: .text%__1cRCardTableModRefBSPclear_MemRegion6MnJMemRegion__v_; +text: .text%__1cHThreadsLgc_epilogue6F_v_; +text: .text%__1cKJavaThreadLgc_epilogue6M_v_; +text: .text%__1cRframe_gc_epilogue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeLgc_epilogue6M_v_; +text: .text%__1cFframeMpd_gc_epilog6M_v_; +text: .text%__1cJCodeCacheLgc_epilogue6F_v_; +text: .text%__1cICodeBlobTfix_oop_relocations6M_v_; +text: .text%__1cICodeBlobTfix_oop_relocations6MpC1_v_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: codeBlob.o; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: relocInfo.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: onStackReplacement.o; +text: .text%__1cQGenCollectedHeapWupdate_time_of_last_gc6Mx_v_: genMarkSweep.o; +text: .text%__1cKGenerationWupdate_time_of_last_gc6Mx_v_: tenuredGeneration.o; +text: .text%__1cKGenerationWupdate_time_of_last_gc6Mx_v_: compactingPermGenGen.o; +text: .text%__1cbCOneContigSpaceCardGenerationVunsafe_max_alloc_nogc6kM_I_; +text: .text%__1cRTenuredGenerationQcompute_new_size6M_v_; +text: .text%__1cKGenerationEspec6M_pnOGenerationSpec__; +text: .text%Unsafe_CompareAndSwapObject; +text: .text%__1cLVtableStubsScreate_itable_stub6Fii_pnKVtableStub__; +text: .text%__1cLLIR_EmitterDnop6M_v_; +text: .text%__1cJAssemblerEmovl6MnHAddress_pnI_jobject__v_; +text: .text%__1cMLinkResolverbEvtable_index_of_miranda_method6FnLKlassHandle_nMsymbolHandle_2pnGThread__i_; +text: .text%__1cLklassVtableQindex_of_miranda6MpnNsymbolOopDesc_2_i_; +text: .text%__1cLklassVtableTis_miranda_entry_at6Mi_i_; +text: .text%__1cRPrivilegedElementHoops_do6MpnKOopClosure__v_; +text: .text%__1cJCodeCacheIcontains6Fpv_i_; +text: .text%__1cFframeRoops_code_blob_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cJOopMapSetHoops_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cJOopMapSetGall_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure_pFppnHoopDesc_9E_v9B9B_v_; +text: .text%__1cICodeBlobbAoop_map_for_return_address6MpCi_pnGOopMap__; +text: .text%__1cJOopMapSetSfind_map_at_offset6kMii_pnGOopMap__; +text: .text%__1cMOopMapStream2t6MpnGOopMap_i_v_; +text: .text%__1cFframeVoopmapreg_to_location6kMnFVMRegEName_pknLRegisterMap__ppnHoopDesc__; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: defNewGeneration.o; +text: .text%__1cJOopMapSetTupdate_register_map6FpknFframe_pnICodeBlob_pnLRegisterMap__v_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: nmethod.o; +text: .text%__1cQComputeCallStackIdo_float6M_v_: generateOopMap.o; +text: .text%jni_DeleteWeakGlobalRef: jni.o; +text: .text%__1cKJNIHandlesTdestroy_weak_global6FpnI_jobject__v_; +text: .text%__1cILIR_ListJoop2stack6MpnI_jobject_i_v_: c1_LIREmitter.o; +text: .text%__1cNObjectMonitorREntryQdDueue_unlink6MpnMObjectWaiter__v_; +text: .text%JVM_IsSameClassPackage; +text: .text%__1cTGeneratePairingInfoRpossible_gc_point6MpnOBytecodeStream__i_: ciMethod.o; +text: .text%__1cTGeneratePairingInfoOreport_results6kM_i_: ciMethod.o; +text: .text%__1cMGraphBuilderMmonitorenter6MpnLInstruction__v_; +text: .text%__1cMMonitorEnterFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerPdo_MonitorEnter6MpnMMonitorEnter__v_; +text: .text%__1cNAccessMonitorIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderLmonitorexit6MpnLInstruction__v_; +text: .text%__1cLMonitorExitFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerOdo_MonitorExit6MpnLMonitorExit__v_; +text: .text%__1cILongTypeDtag6kM_nIValueTag__: c1_Canonicalizer.o; +text: .text%__1cILongTypeEsize6kM_i_: c1_Canonicalizer.o; +text: .text%__1cNAccessMonitorPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorPdo_MonitorEnter6MpnMMonitorEnter__v_; +text: .text%__1cTNullCheckEliminatorUhandle_AccessMonitor6MpnNAccessMonitor__v_; +text: .text%__1cQNullCheckVisitorOdo_MonitorExit6MpnLMonitorExit__v_; +text: .text%__1cIValueGenPdo_MonitorEnter6MpnMMonitorEnter__v_; +text: .text%__1cNc1_AllocTableMhas_two_free6kM_i_; +text: .text%__1cMLongConstantPas_LongConstant6M_p0_: c1_Canonicalizer.o; +text: .text%__1cFRInfoLas_rinfo_lo6kM_0_; +text: .text%__1cLLIR_EmitterJopr2intLo6MpnLLIR_OprDesc__i_; +text: .text%__1cFRInfoLas_rinfo_hi6kM_0_; +text: .text%__1cLLIR_EmitterJopr2intHi6MpnLLIR_OprDesc__i_; +text: .text%__1cIValueGenOdo_MonitorExit6MpnLMonitorExit__v_; +text: .text%__1cNAccessMonitorQas_AccessMonitor6M_p0_: c1_GraphBuilder.o; +text: .text%__1cJAssemblerFpushl6MpnI_jobject__v_; +text: .text%__1cNLIR_AssemblerNas_Address_hi6MpnLLIR_Address__nHAddress__; +text: .text%__1cFRInfoOas_register_hi6kM_pnMRegisterImpl__; +text: .text%__1cNLIR_AssemblerNas_Address_lo6MpnLLIR_Address__nHAddress__; +text: .text%__1cFRInfoOas_register_lo6kM_pnMRegisterImpl__; +text: .text%__1cCosHrealloc6FpvI_1_; +text: .text%Unsafe_GetNativeFloat; +text: .text%__1cIValueGenQdo_currentThread6MpnJIntrinsic__v_; +text: .text%__1cILIR_ListKget_thread6MnFRInfo__v_: c1_CodeGenerator_i486.o; +text: .text%__1cNLIR_AssemblerKget_thread6MpnLLIR_OprDesc__v_; +text: .text%__1cIValueGenSload_item_patching6MpnHIRScope_ipnEItem_pnKValueStack_pnOExceptionScope__v_; +text: .text%__1cEItemUget_jobject_constant6kM_pnIciObject__; +text: .text%__1cJValueTypeTas_InstanceConstant6M_pnQInstanceConstant__: c1_ValueType.o; +text: .text%__1cIintArrayIindex_of6kMki_i_: c1_CodeGenerator.o; +text: .text%__1cMLinkResolverbEresolve_interface_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cGciTypeNis_subtype_of6Mp0_i_; +text: .text%__1cIValueGenOload_byte_item6MpnEItem__v_; +text: .text%__1cIValueGenPlock_free_rinfo6MpnLInstruction_nKc1_RegMask__nFRInfo__; +text: .text%__1cIRegAllocNget_lock_temp6MpnLInstruction_nKc1_RegMask__nFRInfo__; +text: .text%__1cQComputeCallStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cRComputeEntryStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cIFrameMapNis_byte_rinfo6FnFRInfo__i_; +text: .text%Unsafe_AllocateInstance; +text: .text%jni_AllocObject: jni.o; +text: .text%__1cQinstanceRefKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_; +text: .text%__1cNCanonicalizerMset_constant6Mi_v_: c1_Canonicalizer.o; +text: .text%__1cJTypeCheckPother_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cNLIR_AssemblerMcheck_icache6M_i_; +text: .text%__1cRC1_MacroAssemblerTfast_ObjectHashCode6MpnMRegisterImpl_2_v_; +text: .text%__1cNLIR_AssemblerZjobject2reg_with_patching6MpnMRegisterImpl_pnMCodeEmitInfo__v_; +text: .text%__1cHLogicOpIis_equal6kMpnLInstruction__i_: c1_Instruction.o; +text: .text%__1cLAccessFieldKlock_stack6kM_pnKValueStack__: c1_GraphBuilder.o; +text: .text%__1cIRuntime1Mnew_instance6FpnKJavaThread_pnMklassOopDesc__v_; +text: .text%__1cQGenCollectedHeapXhandle_failed_promotion6MpnKGeneration_pnHoopDesc_Ip4_4_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceRefKlass.o; +text: .text%__1cbCOneContigSpaceCardGenerationTexpand_and_allocate6MIiii_pnIHeapWord__; +text: .text%__1cbCOneContigSpaceCardGenerationGexpand6MII_v_; +text: .text%__1cNGCMutexLocker2t6MpnFMutex__v_; +text: .text%__1cbCOneContigSpaceCardGenerationHgrow_by6MI_i_; +text: .text%__1cPContiguousSpaceNmangle_region6MnJMemRegion__v_; +text: .text%__1cJMarkSweepRFollowRootClosureLdo_nmethods6kM_ki_: markSweep.o; +text: .text%__1cQCompactibleSpaceUinitialize_threshold6M_pnIHeapWord__: space.o; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: markSweep.o; +text: .text%__1cRAlwaysTrueClosureLdo_object_b6MpnHoopDesc__i_: genCollectedHeap.o; +text: .text%__1cLCardTableRSTinvalidate_or_clear6MpnKGeneration_ii_v_; +text: .text%__1cJMemRegionFminus6kMk0_0_; +text: .text%__1cLCardTableRSKinvalidate6MnJMemRegion__v_: cardTableRS.o; +text: .text%__1cRCardTableModRefBSKinvalidate6MnJMemRegion__v_; +text: .text%__1cIRuntime1Onew_type_array6FpnKJavaThread_pnMklassOopDesc_i_v_; +text: .text%__1cJFloatTypeDtag6kM_nIValueTag__: c1_Canonicalizer.o; +text: .text%__1cNFloatConstantQas_FloatConstant6M_p0_: c1_Canonicalizer.o; +text: .text%__1cILIR_ListNstore_mem_oop6MpnI_jobject_nFRInfo_inJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cJFloatTypeMas_FloatType6M_p0_: c1_Canonicalizer.o; +text: .text%__1cNConstantTableMappend_float6Mf_v_; +text: .text%__1cRAbstractAssemblerGa_long6Mi_v_; +text: .text%__1cNObjectMonitorGnotify6MpnGThread__v_; +text: .text%__1cDCHARprocess_interface6FnTinstanceKlassHandle_pnNGrowableArray4nLKlassHandle___pnNGrowableArray4nMmethodHandle___nMsymbolHandle_6_v_; +text: .text%__1cINewArrayPother_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cLLIR_EmitterQfield_store_byte6MpnLLIR_OprDesc_i2nFRInfo_ipnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerIshift_op6MnILIR_Code_nFRInfo_222_v_; +text: .text%__1cIRuntime1Mmonitorenter6FpnKJavaThread_pnHoopDesc_pnPBasicObjectLock__v_; +text: .text%__1cIRuntime1Lmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cHnmethodPis_dependent_on6MpnMklassOopDesc__i_; +text: .text%__1cHnmethodVis_dependent_on_entry6MpnMklassOopDesc_2pnNmethodOopDesc__i_; +text: .text%__1cNVM_DeoptimizeEname6kM_pkc_: vm_operations.o; +text: .text%__1cNVM_DeoptimizeEdoit6M_v_; +text: .text%__1cODeoptimizationVdeoptimize_dependents6F_i_; +text: .text%__1cHThreadsbFdeoptimized_wrt_marked_nmethods6F_v_; +text: .text%__1cKJavaThreadbFdeoptimized_wrt_marked_nmethods6M_v_; +text: .text%__1cFframeVshould_be_deoptimized6kM_i_; +text: .text%__1cICodeBlobOis_java_method6kM_i_: codeBlob.o; +text: .text%__1cJCodeCachebGmake_marked_nmethods_not_entrant6F_v_; +text: .text%__1cJCodeCacheNalive_nmethod6FpnICodeBlob__pnHnmethod__; +text: .text%__1cHnmethodbAmake_not_entrant_or_zombie6Mi_v_; +text: .text%__1cHnmethodNis_osr_method6kM_i_: nmethod.o; +text: .text%__1cKNativeJumpUpatch_verified_entry6FpC11_v_; +text: .text%__1cHnmethodVmark_as_seen_on_stack6M_v_; +text: .text%__1cTinc_decompile_count6FpnHnmethod__v_: nmethod.o; +text: .text%__1cMVM_OperationNdoit_epilogue6M_v_: vm_operations.o; +text: .text%__1cHThreadsLnmethods_do6F_v_; +text: .text%__1cKJavaThreadLnmethods_do6M_v_; +text: .text%__1cGThreadLnmethods_do6M_v_; +text: .text%__1cFframeLnmethods_do6M_v_; +text: .text%__1cFframeVnmethods_code_blob_do6M_v_; +text: .text%__1cILIR_ListEidiv6MnFRInfo_i11pnMCodeEmitInfo__v_; +text: .text%__1cLlog2_intptr6Fi_i_: c1_LIRAssembler_i486.o; +text: .text%__1cONMethodSweeperPprocess_nmethod6FpnHnmethod__v_; +text: .text%__1cHnmethodPis_locked_by_vm6kM_i_: nmethod.o; +text: .text%__1cHnmethodLis_unloaded6kM_i_: nmethod.o; +text: .text%__1cHnmethodVcleanup_inline_caches6M_v_; +text: .text%__1cKCompiledIC2t6MpnKRelocation__v_; +text: .text%__1cILongTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cILongTypeEsize6kM_i_: c1_ValueType.o; +text: .text%JVM_HoldsLock; +text: .text%__1cSObjectSynchronizerZcurrent_thread_holds_lock6FpnKJavaThread_nGHandle__i_; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cLLoadIndexedIis_equal6kMpnLInstruction__i_: c1_Instruction.o; +text: .text%__1cFciEnvWis_dependence_violated6FpnMklassOopDesc_pnNmethodOopDesc__i_; +text: .text%__1cFciEnvZcall_has_multiple_targets6FpnNinstanceKlass_nMsymbolHandle_3ri_i_; +text: .text%__1cFMutexbLwait_for_lock_blocking_implementation6MpnKJavaThread__v_; +text: .text%__1cHnmethodbCcan_not_entrant_be_converted6M_i_; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_: oopMapCache.o; +text: .text%__1cTMaskFillerForNativeIpass_int6M_v_: oopMapCache.o; +text: .text%__1cGThreadOis_Java_thread6kM_i_: vmThread.o; +text: .text%__1cMLocalMappingDadd6MinFRInfo__v_; +text: .text%__1cILongTypeEbase6kM_pnJValueType__: c1_ValueType.o; +text: .text%__1cLLIR_EmitterQfield_store_long6MpnLLIR_OprDesc_i2ipnMCodeEmitInfo__v_; +text: .text%__1cKScanBlocksMis_long_only6kMi_i_; +text: .text%__1cRLIR_PeepholeStateLreg2indexLo6MpnLLIR_OprDesc__i_; +text: .text%__1cRLIR_PeepholeStateLreg2indexHi6MpnLLIR_OprDesc__i_; +text: .text%__1cNSharedRuntimeDf2l6Ff_x_; +text: .text%__1cIValueGenLdo_getClass6MpnJIntrinsic__v_; +text: .text%__1cLLIR_EmitterIgetClass6MnFRInfo_1pnMCodeEmitInfo__v_; +text: .text%__1cMGraphBuilderKcompare_op6MpnJValueType_nJBytecodesECode__v_; +text: .text%__1cJCompareOpFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerMdo_CompareOp6MpnJCompareOp__v_; +text: .text%__1cJCompareOpEhash6kM_i_: c1_Instruction.o; +text: .text%__1cJCompareOpEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cJCompareOpMas_CompareOp6M_p0_: c1_Instruction.o; +text: .text%__1cCIf2t6MpnLInstruction_n0BJCondition_i2pnKBlockBegin_5pnKValueStack_i_v_: c1_Canonicalizer.o; +text: .text%__1cGSetRegIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocLset_fpu_reg6MiipnLInstruction__v_; +text: .text%__1cJIsFreeRegIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cILIR_ListJfloat2reg6MfnFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListMbranch_float6MnMLIR_OpBranchNLIR_Condition_pnFLabel_4_v_; +text: .text%__1cIValueGenNreturnF0RInfo6F_nFRInfo__; +text: .text%__1cLLIR_EmitterOset_fpu_result6MnFRInfo__v_; +text: .text%__1cILIR_ListIpush_fpu6MnFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cNConstantTableZaddress_of_float_constant6Mf_pC_; +text: .text%__1cNLIR_AssemblerOfpu_two_on_tos6MnFRInfo_1i_v_; +text: .text%__1cIFrameMapLFpuStackSimEswap6M_v_; +text: .text%__1cIFrameMapLFpuStackSimRexchange_with_tos6Mi_v_; +text: .text%__1cHnmethodSflush_dependencies6MpnRBoolObjectClosure__v_; +text: .text%__1cNinstanceKlassYremove_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cFVTuneOdelete_nmethod6FpnHnmethod__v_; +text: .text%__1cQPlaceholderEntryHoops_do6MpnKOopClosure__v_; +text: .text%__1cHnmethodFflush6M_v_; +text: .text%__1cJEventMark2t6MpkcE_v_: nmethod.o; +text: .text%__1cICodeBlobFflush6M_v_; +text: .text%__1cJCodeCacheEfree6FpnICodeBlob__v_; +text: .text%__1cICodeHeapKdeallocate6Mpv_v_; +text: .text%__1cICodeHeapPadd_to_freelist6MpnJHeapBlock__v_; +text: .text%__1cICodeHeapPfollowing_block6MpnJFreeBlock__2_; +text: .text%__1cRComputeEntryStackIdo_float6M_v_: generateOopMap.o; +text: .text%__1cICodeHeapMinsert_after6MpnJFreeBlock_2_v_; +text: .text%__1cICodeHeapLmerge_right6MpnJFreeBlock__v_; +text: .text%__1cHnmethodbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cUCompressedReadStreamMraw_read_int6FrpC_i_: nmethod.o; +text: .text%__1cFframebAoops_compiled_arguments_do6MnMsymbolHandle_ipknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cQComputeCallStackJdo_double6M_v_: generateOopMap.o; +text: .text%__1cbCOneContigSpaceCardGenerationGshrink6MI_v_; +text: .text%__1cbCOneContigSpaceCardGenerationJshrink_by6MI_v_; +text: .text%__1cMVirtualSpaceJshrink_by6MI_v_; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_: oopMapCache.o; +text: .text%__1cRComputeEntryStackJdo_double6M_v_: generateOopMap.o; +text: .text%__1cKDoubleTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cKDoubleTypeDtag6kM_nIValueTag__: c1_Canonicalizer.o; +text: .text%__1cODoubleConstantRas_DoubleConstant6M_p0_: c1_Canonicalizer.o; +text: .text%__1cKDoubleTypeEbase6kM_pnJValueType__: c1_ValueType.o; +text: .text%__1cODoubleConstantLis_constant6kM_i_: c1_Canonicalizer.o; +text: .text%__1cKDoubleTypeEsize6kM_i_: c1_ValueType.o; +text: .text%__1cHLockRegJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocRset_locked_double6MipnLInstruction_i_v_; +text: .text%__1cKDoubleTypeNas_DoubleType6M_p0_: c1_ValueType.o; +text: .text%__1cIFrameMapUare_adjacent_indeces6kMii_i_; +text: .text%__1cQChangeSpillCountJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocZchange_double_spill_count6Mii_v_; +text: .text%__1cILIR_ListKdouble2reg6MdnFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cHFreeRegJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocPset_free_double6Mi_v_; +text: .text%__1cILIR_ListDrem6MpnLLIR_OprDesc_22pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cLGetRefCountJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocNget_double_rc6kMi_i_; +text: .text%__1cLLIR_EmitterUcheck_double_address6Mi_v_; +text: .text%__1cILIR_ListQreg2double_stack6MnFRInfo_inJBasicType__v_: c1_LIREmitter.o; +text: .text%__1cRLIR_PeepholeStateNstack2indexHi6MpnLLIR_OprDesc__i_; +text: .text%__1cRLIR_PeepholeStateNstack2indexLo6MpnLLIR_OprDesc__i_; +text: .text%__1cKDoubleTypeNas_DoubleType6M_p0_: c1_Canonicalizer.o; +text: .text%__1cNConstantTableNappend_double6Md_v_; +text: .text%__1cNConstantTablebAaddress_of_double_constant6Md_pC_; +text: .text%__1cQGenCollectedHeapHcollect6MnHGCCauseFCause_i_v_; +text: .text%__1cQGenCollectedHeapOcollect_locked6MnHGCCauseFCause_i_v_; +text: .text%__1cRVM_GenCollectFullEname6kM_pkc_: vm_operations.o; +text: .text%__1cRVM_GenCollectFullEdoit6M_v_; +text: .text%__1cQGenCollectedHeapYmust_clear_all_soft_refs6M_i_; +text: .text%__1cQGenCollectedHeapSdo_full_collection6Miipi_v_; +text: .text%__1cKGenerationbHfull_collects_younger_generations6kM_i_: defNewGeneration.o; +text: .text%__1cKDoubleTypeEsize6kM_i_: c1_Canonicalizer.o; +text: .text%__1cKDoubleTypeEbase6kM_pnJValueType__: c1_Canonicalizer.o; +text: .text%__1cIValueMapNresize_bucket6MpnGBucket__v_; +text: .text%__1cNFloatConstantLis_constant6kM_i_: c1_Canonicalizer.o; +text: .text%__1cJNullCheckMas_NullCheck6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLLIR_EmitterIopr2long6MpnLLIR_OprDesc__x_; +text: .text%__1cILIR_ListKlong2stack6Mxi_v_: c1_LIREmitter.o; +text: .text%__1cIValueGenNreturnD0RInfo6F_nFRInfo__; +text: .text%__1cJIsFreeRegJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocOis_free_double6kMi_i_; +text: .text%__1cGSetRegJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocOset_double_reg6MiipnLInstruction__v_; +text: .text%__1cLLIR_EmitterNcopy_fpu_item6MnFRInfo_pnLLIR_OprDesc__v_; +text: .text%__1cILIR_ListHdup_fpu6MnFRInfo_1_v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListDdiv6MpnLLIR_OprDesc_22pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cJAssemblerFfsubp6Mi_v_; +text: .text%__1cNLIR_AssemblerHdup_fpu6MnFRInfo_1_v_; +text: .text%__1cIFrameMapLFpuStackSimLmove_on_tos6Mi_i_; +text: .text%__1cJAssemblerGfdiv_d6MnHAddress__v_; +text: .text%__1cJAssemblerFfdivp6Mi_v_; +text: .text%__1cIValueGenMreturn2RInfo6F_nFRInfo__; +text: .text%__1cJValueTypeQas_FloatConstant6M_pnNFloatConstant__: c1_Canonicalizer.o; +text: .text%__1cIRuntime1Qnew_object_array6FpnKJavaThread_pnMklassOopDesc_i_v_; +text: .text%__1cIValueGenLdivOutRInfo6F_nFRInfo__; +text: .text%__1cILIR_ListEidiv6MnFRInfo_111pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListVvolatile_load_mem_reg6MnFRInfo_i1nJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cEItemSget_jlong_constant6kM_x_; +text: .text%__1cNLIR_AssemblerQvolatile_move_op6MpnLLIR_OprDesc_2nJBasicType_nHLIR_Op1NLIR_PatchCode_pnMCodeEmitInfo__v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlass.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: objArrayKlass.o; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cONewObjectArrayKexact_type6kM_pnGciType__; +text: .text%__1cPciObjArrayKlassSis_obj_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cPciObjArrayKlassNelement_klass6M_pnHciKlass__; +text: .text%__1cIRuntime1Noop_arraycopy6FpnIHeapWord_2i_v_; +text: .text%__1cILongTypeEbase6kM_pnJValueType__: c1_Canonicalizer.o; +text: .text%__1cMLongConstantLis_constant6kM_i_: c1_Canonicalizer.o; +text: .text%__1cIValueGenUdo_ArithmeticOp_Long6MpnMArithmeticOp__v_; +text: .text%__1cLLIR_EmitterSarithmetic_op_long6MnJBytecodesECode_pnLLIR_OprDesc_44pnMCodeEmitInfo__v_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: compiledICHolderKlass.o; +text: .text%__1cVcompiledICHolderKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cLArrayLengthIis_equal6kMpnLInstruction__i_: c1_GraphBuilder.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: compiledICHolderKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: compiledICHolderKlass.o; +text: .text%__1cTunsafe_intrinsic_id6FpnNsymbolOopDesc_1_nNmethodOopDescLIntrinsicId__; +text: .text%__1cMGraphBuilderVappend_unsafe_put_raw6MpnIciMethod_nJBasicType__i_; +text: .text%__1cMUnsafePutRawFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerPdo_UnsafePutRaw6MpnMUnsafePutRaw__v_; +text: .text%__1cNCanonicalizerOdo_UnsafeRawOp6MpnLUnsafeRawOp__v_; +text: .text%__1cFmatch6FpnLUnsafeRawOp_ppnLInstruction_4pi_i_: c1_Canonicalizer.o; +text: .text%__1cLInstructionPas_ArithmeticOp6M_pnMArithmeticOp__: c1_Instruction.o; +text: .text%__1cIUnsafeOpLas_UnsafeOp6M_p0_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderVappend_unsafe_get_raw6MpnIciMethod_nJBasicType__i_; +text: .text%__1cMUnsafeGetRawFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerPdo_UnsafeGetRaw6MpnMUnsafeGetRaw__v_; +text: .text%__1cMGraphBuilderNlookup_switch6M_v_; +text: .text%__1cIintArray2t6Mki1_v_: c1_GraphBuilder.o; +text: .text%__1cMLookupSwitchFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerPdo_LookupSwitch6MpnMLookupSwitch__v_; +text: .text%__1cMUnsafePutRawPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorPdo_UnsafePutRaw6MpnMUnsafePutRaw__v_; +text: .text%__1cTNullCheckEliminatorPhandle_UnsafeOp6MpnIUnsafeOp__v_; +text: .text%__1cLUnsafeRawOpPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorPdo_UnsafeGetRaw6MpnMUnsafeGetRaw__v_; +text: .text%__1cQNullCheckVisitorPdo_LookupSwitch6MpnMLookupSwitch__v_; +text: .text%__1cIValueGenPdo_UnsafePutRaw6MpnMUnsafePutRaw__v_; +text: .text%__1cLLIR_EmitterOput_raw_unsafe6MpnLLIR_OprDesc_2i2nJBasicType__v_; +text: .text%__1cLLIR_EmitterMlong2address6MpnLLIR_OprDesc__nFRInfo__; +text: .text%__1cILIR_ListNstore_mem_reg6MnFRInfo_pnLLIR_Address_nJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cIValueGenPdo_UnsafeGetRaw6MpnMUnsafeGetRaw__v_; +text: .text%__1cLLIR_EmitterOget_raw_unsafe6MnFRInfo_pnLLIR_OprDesc_3inJBasicType__v_; +text: .text%__1cILIR_ListMload_mem_reg6MpnLLIR_Address_nFRInfo_nJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cIValueGenPdo_LookupSwitch6MpnMLookupSwitch__v_; +text: .text%__1cUcreate_lookup_ranges6FpnMLookupSwitch__pnQLookupRangeArray__: c1_CodeGenerator_i486.o; +text: .text%__1cLLIR_EmitterVlookupswitch_range_op6MpnLLIR_OprDesc_iipnKBlockBegin__v_; +text: .text%__1cNSharedRuntimeEldiv6Fxx_x_; +text: .text%Unsafe_GetObjectVolatile; +text: .text%signalHandler; +text: .text%JVM_handle_solaris_signal; +text: .text%__1cKJavaThreadUin_stack_yellow_zone6MpC_i_: os_solaris_i486.o; +text: .text%__1cICodeBlobRis_at_poll_return6MpC_i_; +text: .text%__1cUSafepointSynchronizebDhandle_polling_page_exception6FpnKJavaThread__pC_; +text: .text%__1cbCCompiledCodeSafepointHandlerbDhandle_polling_page_exception6M_pC_; +text: .text%__1cFframebDsender_for_raw_compiled_frame6kMpnLRegisterMap__0_; +text: .text%__1cNSafepointBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_; +text: .text%__1cUThreadSafepointStateYcaller_must_gc_arguments6kM_i_; +text: .text%__1cbCCompiledCodeSafepointHandlerYcaller_must_gc_arguments6kM_i_: safepoint.o; +text: .text%__1cFframeIpatch_pc6MpnGThread_pC_v_; +text: .text%__1cNSafepointBlobbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cSvframeStreamCommonbFfill_in_compiled_inlined_sender6M_i_; +text: .text%__1cJFloatTypeEsize6kM_i_: c1_Canonicalizer.o; +text: .text%__1cIValueGenNrelease_roots6MpnKValueStack__v_; +text: .text%__1cSciExceptionHandlerLcatch_klass6M_pnPciInstanceKlass__; +text: .text%__1cHciKlassNis_subtype_of6Mp0_i_; +text: .text%__1cNSharedRuntimeDd2l6Fd_x_; +text: .text%__1cOObjectConstantLis_constant6kM_i_: c1_ValueType.o; +text: .text%__1cILIR_ListLstore_array6MipnLLIR_Address_nJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerLconst2array6MpnJLIR_Const_pnLLIR_Address_nJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cQInstanceConstantLis_constant6kM_i_: c1_ValueType.o; diff --git a/hotspot/build/solaris/makefiles/reorder_COMPILER1_sparc b/hotspot/build/solaris/makefiles/reorder_COMPILER1_sparc new file mode 100644 index 00000000000..d135e73cfac --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_COMPILER1_sparc @@ -0,0 +1,4511 @@ +data = R0x2000; +text = LOAD ?RXO; + + +# Test Null +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: arguments.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_AllocTable.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_AllocTable_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_CacheLocals.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_CacheLocals_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Canonicalizer.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_CodeGenerator.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_CodeGenerator_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_CodeStubs_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Compilation.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Compiler.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_FrameMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_FrameMap_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_GraphBuilder.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_IR.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Instruction.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_InstructionPrinter.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Items.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Items_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIR.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIRAssembler.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIRAssembler_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIREmitter.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIREmitter_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIROptimizer.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Loops.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_MacroAssembler_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Optimizer.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_RInfo.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_RInfo_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_RegAlloc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_RegAlloc_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Runtime1.o; +text: .text%__1cIiEntries2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_Runtime1_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_ScanBlocks.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_ValueMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_ValueSet.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_ValueStack.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeBlob.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeCache.o; +text: .text%__1cICHeapObj2n6FI_pv_; +text: .text%__1cCosGmalloc6FI_pv_; +text: .text%__1cICodeHeap2t6M_v_; +text: .text%__1cMVirtualSpace2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compilationPolicy.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compileBroker.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compiledIC.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: deoptimization.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: fprofiler.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: frame.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: frame_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: genCollectedHeap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: generateOopMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interp_masm_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter.o; +text: .text%__1cKEntryPoint2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: java.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEnvBase.o; +text: .text%__1cLResourceObj2n6FIn0APallocation_type__pv_; +text: .text%__1cUGenericGrowableArray2t6Mii_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEventController.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiImpl.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiTagMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: klassVtable.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: management.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: markSweep.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: memoryService.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: methodOop.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: nativeInst_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: nmethod.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: oopMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: os_solaris.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: os_solaris_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: parGCAllocBuffer.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psAdaptiveSizePolicy.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psMarkSweep.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psPromotionLAB.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psScavenge.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: relocInfo.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: runtimeService.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: safepoint.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: safepoint_solaris_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedHeap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedRuntime.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: tenuredGeneration.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vframeArray.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vmStructs.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vm_version.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vtableStubs_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: c1_LIROptimizer_sparc.o; +text: .text%JNI_CreateJavaVM; +text: .text%__1cCosUatomic_add_bootstrap6Fipoi_i_; +text: .text%__1cCosVatomic_xchg_bootstrap6Fipoi_i_; +text: .text%__1cHThreadsJcreate_vm6FpnOJavaVMInitArgs_pi_i_; +text: .text%__1cMostream_init6F_v_; +text: .text%__1cMoutputStream2t6Mi_v_; +text: .text%__1cCosEinit6F_v_; +text: .text%__1cCosLinit_random6Fl_v_; +text: .text%__1cOThreadCriticalKinitialize6F_v_; +text: .text%__1cJArgumentsWinit_system_properties6F_v_; +text: .text%__1cJArgumentsQPropertyList_add6FppnOSystemProperty_2_v_; +text: .text%__1cTAbstract_VM_VersionKvm_release6F_pkc_; +text: .text%__1cTAbstract_VM_VersionHvm_name6F_pkc_; +text: .text%__1cTAbstract_VM_VersionJvm_vendor6F_pkc_; +text: .text%__1cTAbstract_VM_VersionOvm_info_string6F_pkc_; +text: .text%__1cCosbDinit_system_properties_values6F_v_; +text: .text%__1cCosNset_boot_path6Fcc_i_; +text: .text%__1cJArgumentsFparse6FpknOJavaVMInitArgs__i_; +text: .text%__1cCosIjvm_path6Fpci_v_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_pkcp4_i_: arguments.o; +text: .text%__1cJArgumentsVprocess_settings_file6Fpkcii_i_; +text: .text%__1cJArgumentsSparse_vm_init_args6FpknOJavaVMInitArgs__i_; +text: .text%__1cJArgumentsbSparse_java_tool_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cCosGgetenv6Fpkcpci_i_; +text: .text%__1cJArgumentsWparse_each_vm_init_arg6FpknOJavaVMInitArgs_pnMSysClassPath_pi_i_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_ppkc5i_i_: arguments.o; +text: .text%__1cJArgumentsMadd_property6Fpkc_i_; +text: .text%__1cJArgumentsXPropertyList_unique_add6FppnOSystemProperty_pkcpc_v_; +text: .text%__1cCosEfree6Fpv_v_; +text: .text%__1cJArgumentsMbuild_string6Fppcpkc_v_; +text: .text%__1cSCommandLineFlagsExJboolAtPut6FnXCommandLineFlagWithType_i_v_; +text: .text%__1cJArgumentsbNparse_java_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsVfinalize_vm_init_args6FpnMSysClassPath_i_i_; +text: .text%__1cMSysClassPathPexpand_endorsed6M_v_; +text: .text%__1cJArgumentsWPropertyList_get_value6FpnOSystemProperty_pkc_4_; +text: .text%__1cMSysClassPathQadd_jars_to_path6Fpcpkc_1_; +text: .text%__1cJArgumentsZcheck_vm_args_consistency6F_i_; +text: .text%__1cJArgumentsVset_parallel_gc_flags6F_v_; +text: .text%__1cJArgumentsbBset_cms_and_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsTset_parnew_gc_flags6F_v_; +text: .text%__1cSCommandLineFlagsExKis_default6FnPCommandLineFlag__i_; +text: .text%__1cJArgumentsUset_ergonomics_flags6F_v_; +text: .text%__1cCosXis_server_class_machine6F_i_; +text: .text%__1cJTimeStampJupdate_to6Mx_v_; +text: .text%__1cCosOjavaTimeMillis6F_x_; +text: .text%__1cJTraceTime2t6MpkciipnMoutputStream__v_; +text: .text%__1cCosGinit_26F_i_; +text: .text%__1cCosHSolarisKmmap_chunk6FpcIii_2_; +text: .text%__1cCosHSolarisRmpss_sanity_check6F_v_; +text: .text%__1cCosHSolarisOlibthread_init6F_v_; +text: .text%__1cOisT2_libthread6F_i_; +text: .text%__1cRlwp_priocntl_init6F_i_: os_solaris.o; +text: .text%__1cNpriocntl_stub6FinGidtype_lipc_l_: os_solaris.o; +text: .text%__1cCosHSolarisQsignal_sets_init6F_v_; +text: .text%__1cCosHSolarisPinit_signal_mem6F_v_; +text: .text%__1cCosHSolarisXinstall_signal_handlers6F_v_; +text: .text%__1cCosHSolarisSset_signal_handler6Fiii_v_; +text: .text%__1cCosHSolarisUsynchronization_init6F_v_; +text: .text%__1cDhpiKinitialize6F_i_; +text: .text%__1cDhpiYinitialize_get_interface6FpnIvm_calls__v_; +text: .text%__1cQostream_init_log6F_v_; +text: .text%__1cRis_error_reported6F_i_; +text: .text%__1cSThreadLocalStorageEinit6F_v_; +text: .text%__1cSThreadLocalStorageHpd_init6F_v_; +text: .text%__1cCosbDallocate_thread_local_storage6F_i_; +text: .text%__1cSThreadLocalStoragebCgenerate_code_for_get_thread6F_v_; +text: .text%__1cPvm_init_globals6F_v_; +text: .text%__1cScheck_ThreadShadow6F_v_; +text: .text%__1cRcheck_basic_types6F_v_; +text: .text%__1cNeventlog_init6F_v_; +text: .text%__1cKmutex_init6F_v_; +text: .text%__1cFMutex2t6Mipkci_v_; +text: .text%lwp_cond_init: os_solaris.o; +text: .text%lwp_mutex_init: os_solaris.o; +text: .text%__1cHMonitor2t6Mipkci_v_; +text: .text%__1cOchunkpool_init6F_v_; +text: .text%__1cPperfMemory_init6F_v_; +text: .text%__1cCosZvm_allocation_granularity6F_i_; +text: .text%__1cKPerfMemoryUcreate_memory_region6FI_v_; +text: .text%__1cCosScurrent_process_id6F_i_; +text: .text%__1cCosSget_temp_directory6F_pkc_; +text: .text%__1cbBcleanup_sharedmem_resources6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cGThread2t6M_v_; +text: .text%__1cFArena2t6M_v_; +text: .text%__1cOThreadCritical2t6M_v_; +text: .text%__1cOThreadCritical2T6M_v_; +text: .text%__1cKHandleMarkKinitialize6MpnGThread__v_; +text: .text%__1cKJavaThreadKinitialize6M_v_; +text: .text%__1cNjni_functions6F_pknTJNINativeInterface___; +text: .text%__1cQThreadStatistics2t6M_v_; +text: .text%__1cGParker2t6M_v_; +text: .text%__1cWThreadLocalAllocBufferKinitialize6M_v_; +text: .text%__1cMFlatProfilerJis_active6F_i_; +text: .text%__1cUThreadSafepointStateGcreate6FpnKJavaThread__v_; +text: .text%__1cCosScurrent_stack_base6F_pC_; +text: .text%__1cCosScurrent_stack_size6F_I_; +text: .text%__1cSThreadLocalStorageKset_thread6FpnGThread__v_; +text: .text%__1cSThreadLocalStorageNpd_set_thread6FpnGThread__v_; +text: .text%__1cSThreadLocalStorageSset_thread_in_slot6FpnGThread__v_; +text: .text%get_thread; +text: .text%__1cSThreadLocalStoragebBget_thread_via_cache_slowly6FIi_pnGThread__; +text: .text%__1cSThreadLocalStoragePget_thread_slow6F_pnGThread__; +text: .text%__1cCosXthread_local_storage_at6Fi_pv_; +text: .text%__1cCosRinitialize_thread6F_v_; +text: .text%__1cNReservedSpaceUpage_align_size_down6FI_I_; +text: .text%__1cCosMvm_page_size6F_i_; +text: .text%__1cCosHSolarisVinit_thread_fpu_state6F_v_; +text: .text%__1cOJNIHandleBlockOallocate_block6FpnGThread__p0_; +text: .text%__1cFMutexbClock_without_safepoint_check6M_v_; +text: .text%__1cFMutexGunlock6M_v_; +text: .text%__1cCosScreate_main_thread6FpnGThread__i_; +text: .text%__1cQcreate_os_thread6FpnGThread_I_pnIOSThread__: os_solaris.o; +text: .text%__1cIOSThread2t6MpFpv_i1_v_; +text: .text%__1cIOSThreadNpd_initialize6M_v_; +text: .text%__1cCosHSolarisPhotspot_sigmask6FpnGThread__v_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: thread.o; +text: .text%__1cCosNcommit_memory6FpcI_i_; +text: .text%__1cCosMguard_memory6FpcI_i_; +text: .text%__1cMinit_globals6F_i_; +text: .text%__1cPmanagement_init6F_v_; +text: .text%__1cNExceptionMark2t6MrpnGThread__v_; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongVariable__; +text: .text%__1cIPerfLong2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability__v_; +text: .text%__1cIPerfData2t6MnJCounterNS_pkcn0AFUnits_n0ALVariability__v_; +text: .text%__1cKPerfMemoryFalloc6FI_pc_; +text: .text%__1cFMutexElock6M_v_; +text: .text%__1cKPerfMemoryMmark_updated6F_v_; +text: .text%__1cCosLelapsedTime6F_d_; +text: .text%__1cMgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cPoldgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cCosYatomic_cmpxchg_bootstrap6Fipoii_i_; +text: .text%__1cCosbCis_thread_cpu_time_supported6F_i_; +text: .text%__1cNExceptionMark2T6M_v_; +text: .text%__1cNThreadServiceEinit6F_v_; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnPPerfLongCounter__; +text: .text%__1cPPerfDataManagerIadd_item6FpnIPerfData_i_v_; +text: .text%__1cORuntimeServiceEinit6F_v_; +text: .text%__1cTClassLoadingServiceEinit6F_v_; +text: .text%__1cKvtune_init6F_v_; +text: .text%__1cObytecodes_init6F_v_; +text: .text%__1cJBytecodesKinitialize6F_v_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii1i_v_; +text: .text%__1cJBytecodesNpd_initialize6F_v_; +text: .text%__1cQclassLoader_init6F_v_; +text: .text%__1cLClassLoaderKinitialize6F_v_; +text: .text%__1cLClassLoaderQload_zip_library6F_v_; +text: .text%__1cCosTnative_java_library6F_pv_; +text: .text%__1cPJavaFrameAnchorNmake_walkable6MpnKJavaThread__v_; +text: .text%bootstrap_flush_windows; +text: .text%__1cCosPfence_bootstrap6F_v_; +text: .text%JVM_GetInterfaceVersion; +text: .text%__1cHThreadsbMis_supported_jni_version_including_1_16Fi_C_; +text: .text%__1cKHandleMark2T6M_v_; +text: .text%__1cLClassLoaderbBsetup_bootstrap_search_path6F_v_; +text: .text%__1cCosGstrdup6Fpkc_pc_; +text: .text%__1cCosEstat6FpkcpnEstat__i_; +text: .text%__1cLClassLoaderXcreate_class_path_entry6FpcnEstat_ppnOClassPathEntry__v_; +text: .text%JVM_RawMonitorCreate; +text: .text%JVM_NativePath; +text: .text%JVM_RawMonitorEnter; +text: .text%__1cFMutexMjvm_raw_lock6M_v_; +text: .text%JVM_RawMonitorExit; +text: .text%__1cFMutexOjvm_raw_unlock6M_v_; +text: .text%JVM_Open; +text: .text%JVM_Lseek; +text: .text%JVM_Close; +text: .text%__1cOClassPathEntry2t6M_v_; +text: .text%__1cOcodeCache_init6F_v_; +text: .text%__1cICodeHeapHreserve6MIII_i_; +text: .text%__1cNReservedSpace2t6MI_v_; +text: .text%__1cCosOreserve_memory6FIpc_1_; +text: .text%__1cMVirtualSpaceKinitialize6MnNReservedSpace_I_i_; +text: .text%__1cCosNcommit_memory6FpcII_i_; +text: .text%__1cMVirtualSpaceOcommitted_size6kM_I_; +text: .text%__1cMVirtualSpaceNreserved_size6kM_I_; +text: .text%__1cNMemoryServiceZadd_code_heap_memory_pool6FpnICodeHeap__v_; +text: .text%__1cMCodeHeapPool2t6MpnICodeHeap_pkci_v_; +text: .text%__1cICodeHeapIcapacity6kM_I_; +text: .text%__1cICodeHeapMmax_capacity6kM_I_; +text: .text%__1cKMemoryPool2t6Mpkcn0AIPoolType_IIii_v_; +text: .text%__1cNMemoryManagerbDget_code_cache_memory_manager6F_p0_; +text: .text%__1cNMemoryManager2t6M_v_; +text: .text%__1cNMemoryManagerIadd_pool6MpnKMemoryPool__v_; +text: .text%__1cKMemoryPoolLadd_manager6MpnNMemoryManager__v_; +text: .text%__1cLicache_init6F_v_; +text: .text%__1cKBufferBlobGcreate6Fpkci_p0_; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: thread.o; +text: .text%__1cICodeHeapLheader_size6F_I_; +text: .text%__1cJCodeCacheIallocate6Fi_pnICodeBlob__; +text: .text%__1cICodeHeapIallocate6MI_pv_; +text: .text%__1cICodeHeapPsearch_freelist6MI_pnJFreeBlock__; +text: .text%__1cICodeBlob2t6Mpkcii_v_; +text: .text%__1cNMemoryServiceXtrack_memory_pool_usage6FpnKMemoryPool__v_; +text: .text%__1cKMemoryPoolYrecord_peak_memory_usage6M_v_; +text: .text%__1cMCodeHeapPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cMCodeHeapPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cICodeHeapSallocated_capacity6kM_I_; +text: .text%__1cKMemoryPoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cXresource_allocate_bytes6FI_pc_; +text: .text%__1cKCodeBuffer2t6MpCi_v_; +text: .text%__1cRAbstractAssembler2t6MpnKCodeBuffer__v_; +text: .text%__1cTICacheStubGeneratorVgenerate_icache_flush6MppFpCii_i_v_; +text: .text%__1cMStubCodeMark2t6MpnRStubCodeGenerator_pkc4_v_; +text: .text%__1cRStubCodeGeneratorLstub_prolog6MpnMStubCodeDesc__v_; +text: .text%__1cRAbstractAssemblerEbind6MrnFLabel__v_; +text: .text%__1cKCodeBufferIrelocate6MpCrknQRelocationHolder_i_v_; +text: .text%__1cKRelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cMStubCodeMark2T6M_v_; +text: .text%__1cRAbstractAssemblerFflush6M_v_; +text: .text%__1cOAbstractICacheQinvalidate_range6FpCi_v_; +text: .text%__1cRStubCodeGeneratorLstub_epilog6MpnMStubCodeDesc__v_; +text: .text%__1cFVTuneNregister_stub6FpkcpC3_v_; +text: .text%__1cFForteNregister_stub6FpkcpC3_v_; +text: .text%__1cPVM_Version_init6F_v_; +text: .text%jio_snprintf; +text: .text%__1cPlocal_vsnprintf6FpcIpkcpv_i_; +text: .text%__1cSstubRoutines_init16F_v_; +text: .text%__1cWStubGenerator_generate6FpnKCodeBuffer_i_v_; +text: .text%__1cNStubGenerator2t6MpnKCodeBuffer_i_v_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbAgenerate_forward_exception6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorLstub_prolog6MpnMStubCodeDesc__v_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorFalign6Mi_v_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerKget_thread6M_v_; +text: .text%__1cOMacroAssemblerKsave_frame6Mi_v_; +text: .text%__1cKRelocationLspec_simple6FnJrelocInfoJrelocType__nQRelocationHolder__; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: relocInfo.o; +text: .text%__1cKRelocationLunpack_data6M_v_: c1_Runtime1.o; +text: .text%__1cXruntime_call_RelocationEtype6M_nJrelocInfoJrelocType__: c1_Runtime1.o; +text: .text%__1cOMacroAssemblerKverify_oop6MpnMRegisterImpl_pkc_v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC2_v_; +text: .text%__1cOMacroAssemblerLsave_thread6MkpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerNverify_thread6M_v_; +text: .text%__1cNStubGeneratorSgenerate_call_stub6MrpC_1_: stubGenerator_sparc.o; +text: .text%__1cHAddress2t6Mn0AJaddr_type_i_v_; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: stubGenerator_sparc.o; +text: .text%__1cJAssemblerSbranch_destination6Fii_i_; +text: .text%__1cJAssemblerOpatched_branch6Fiii_i_; +text: .text%__1cOMacroAssemblerDret6Mi_v_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorYgenerate_catch_exception6M_pC_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: stubGenerator_sparc.o; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerIjumpl_to6MrnHAddress_pnMRegisterImpl_i_v_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorSgenerate_test_stop6M_pC_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerEstop6Mpkc_v_; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: assembler_sparc.o; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: assembler_sparc.o; +text: .text%__1cOMacroAssemblerRload_ptr_contents6MrnHAddress_pnMRegisterImpl_i_v_: assembler_sparc.o; +text: .text%__1cOMacroAssemblerPstop_subroutine6M_v_; +text: .text%__1cVRegistersForDebuggingOsave_registers6FpnOMacroAssembler__v_: assembler_sparc.o; +text: .text%__1cVRegistersForDebuggingRrestore_registers6FpnOMacroAssembler_pnMRegisterImpl__v_: assembler_sparc.o; +text: .text%__1cNStubGeneratorbNgenerate_flush_callers_register_windows6M_pC_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerNflush_windows6M_v_; +text: .text%__1cNStubGeneratorUgenerate_atomic_xchg6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorXgenerate_atomic_cmpxchg6M_pC_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerOcas_under_lock6MpnMRegisterImpl_22pCi_v_; +text: .text%__1cNStubGeneratorTgenerate_atomic_add6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbCgenerate_atomic_cmpxchg_long6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorOgenerate_fence6M_pC_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerGmembar6MnJAssemblerQMembar_mask_bits__v_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbIgenerate_copy_words_aligned8_lower6M_pC_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerFalign6Mi_v_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbJgenerate_copy_words_aligned8_higher6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbBgenerate_set_words_aligned86M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbCgenerate_zero_words_aligned86M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbEgenerate_partial_subtype_check6M_pC_: stubGenerator_sparc.o; +text: .text%__1cJTraceTime2T6M_v_; +text: .text%__1cNcarSpace_init6F_v_; +text: .text%__1cNuniverse_init6F_i_; +text: .text%__1cLJavaClassesbAcompute_hard_coded_offsets6F_v_; +text: .text%__1cLFileMapInfoKinitialize6M_i_; +text: .text%__1cLFileMapInfoIvalidate6M_i_; +text: .text%__1cTAbstract_VM_VersionXinternal_vm_info_string6F_pkc_; +text: .text%__1cRClassPathZipEntryLis_jar_file6M_i_: classLoader.o; +text: .text%__1cRClassPathZipEntryEname6M_pkc_: classLoader.o; +text: .text%__1cPMarkSweepPolicy2t6M_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyQinitialize_flags6M_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyMrem_set_name6M_nJGenRemSetEName__: collectorPolicy.o; +text: .text%__1cJGenRemSetYmax_alignment_constraint6Fn0AEName__I_; +text: .text%__1cRCardTableModRefBSbBct_max_alignment_constraint6F_I_; +text: .text%__1cbCTwoGenerationCollectorPolicyUinitialize_size_info6M_v_; +text: .text%__1cPMarkSweepPolicyWinitialize_generations6M_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyVnumber_of_generations6M_i_: collectorPolicy.o; +text: .text%__1cXPermanentGenerationSpec2t6MnHPermGenEName_IIIIII_v_; +text: .text%__1cQGCPolicyCounters2t6Mpkcii_v_; +text: .text%__1cPPerfDataManagerMcounter_name6Fpkc2_pc_; +text: .text%__1cPPerfDataManagerWcreate_string_constant6FnJCounterNS_pkc3pnGThread__pnSPerfStringConstant__; +text: .text%__1cNPerfByteArray2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_i_v_; +text: .text%__1cCosbDatomic_cmpxchg_long_bootstrap6Fxpoxx_x_; +text: .text%__1cPPerfDataManagerUcreate_long_constant6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongConstant__; +text: .text%__1cQGenCollectedHeap2t6MpnPCollectorPolicy__v_; +text: .text%__1cKSharedHeap2t6MpnPCollectorPolicy__v_; +text: .text%__1cNCollectedHeap2t6M_v_; +text: .text%__1cHGCCauseJto_string6Fn0AFCause__pkc_; +text: .text%__1cPPerfDataManagerWcreate_string_variable6FnJCounterNS_pkci3pnGThread__pnSPerfStringVariable__; +text: .text%__1cMSubTasksDone2t6Mi_v_; +text: .text%__1cMSubTasksDoneFvalid6M_i_; +text: .text%__1cQGenCollectedHeapKinitialize6M_i_; +text: .text%__1cPCollectorPolicyLgenerations6M_ppnOGenerationSpec__: collectorPolicy.o; +text: .text%__1cPCollectorPolicyUpermanent_generation6M_pnXPermanentGenerationSpec__: collectorPolicy.o; +text: .text%__1cXPermanentGenerationSpecFalign6MI_v_; +text: .text%__1cOGenerationSpecRn_covered_regions6kM_i_: collectorPolicy.o; +text: .text%__1cNReservedSpace2t6MIIipc_v_; +text: .text%__1cCosZattempt_reserve_memory_at6FIpc_1_; +text: .text%__1cPCollectorPolicyOcreate_rem_set6MnJMemRegion_i_pnJGenRemSet__; +text: .text%__1cbCTwoGenerationCollectorPolicyQbarrier_set_name6M_nKBarrierSetEName__: collectorPolicy.o; +text: .text%__1cLCardTableRS2t6MnJMemRegion_i_v_; +text: .text%__1cRCardTableModRefBS2t6MnJMemRegion_i_v_; +text: .text%__1cNReservedSpaceYallocation_align_size_up6FI_I_; +text: .text%__1cJMemRegion2t6M_v_: cardTableModRefBS.o; +text: .text%__1cKSharedHeapPset_barrier_set6MpnKBarrierSet__v_; +text: .text%__1cNReservedSpaceKfirst_part6MIii_0_; +text: .text%__1cOGenerationSpecEinit6MnNReservedSpace_ipnJGenRemSet__pnKGeneration__; +text: .text%__1cQDefNewGeneration2t6MnNReservedSpace_Iipkc_v_; +text: .text%__1cKGeneration2t6MnNReservedSpace_Ii_v_; +text: .text%__1cIageTable2t6Mi_v_; +text: .text%__1cFArenaEgrow6MI_pv_; +text: .text%__1cFChunkJnext_chop6M_v_; +text: .text%__1cRCardTableModRefBSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cWSequentialSubTasksDoneFclear6M_v_; +text: .text%__1cSGenerationCounters2t6MpkciipnMVirtualSpace__v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkci_pc_; +text: .text%__1cRCollectorCounters2t6Mpkci_v_; +text: .text%__1cOCSpaceCounters2t6MpkciIpnPContiguousSpace_pnSGenerationCounters__v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkc2i_pc_; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnQPerfLongVariable__; +text: .text%__1cPPerfLongVariant2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_pnUPerfLongSampleHelper__v_; +text: .text%__1cPPerfLongVariantGsample6M_v_; +text: .text%__1cZContiguousSpaceUsedHelperLtake_sample6M_x_: cSpaceCounters.o; +text: .text%__1cPContiguousSpaceEused6kM_I_: space.o; +text: .text%__1cQDefNewGenerationYcompute_space_boundaries6MI_v_; +text: .text%__1cQCompactibleSpaceKinitialize6MnJMemRegion_i_v_; +text: .text%__1cFSpaceKset_bottom6MpnIHeapWord__v_: space.o; +text: .text%__1cJEdenSpaceHset_end6MpnIHeapWord__v_: space.o; +text: .text%__1cJEdenSpaceFclear6M_v_; +text: .text%__1cFSpaceHset_end6MpnIHeapWord__v_: space.o; +text: .text%__1cPContiguousSpaceFclear6M_v_; +text: .text%__1cQDefNewGenerationPupdate_counters6M_v_; +text: .text%__1cSGenerationCountersKupdate_all6M_v_: generationCounters.o; +text: .text%__1cNReservedSpaceJlast_part6MI_0_; +text: .text%__1cRTenuredGeneration2t6MnNReservedSpace_IipnJGenRemSet__v_; +text: .text%__1cOCardGeneration2t6MnNReservedSpace_IipnJGenRemSet__v_; +text: .text%__1cWBlockOffsetSharedArray2t6MnJMemRegion_I_v_; +text: .text%__1cNReservedSpaceSpage_align_size_up6FI_I_; +text: .text%__1cMVirtualSpaceJexpand_by6MI_i_; +text: .text%__1cLCardTableRSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cLCardTableRSKis_aligned6MpnIHeapWord__i_: cardTableRS.o; +text: .text%__1cHGCStats2t6M_v_; +text: .text%__1cWOffsetTableContigSpace2t6MpnWBlockOffsetSharedArray_nJMemRegion__v_; +text: .text%__1cQBlockOffsetArray2t6MpnWBlockOffsetSharedArray_nJMemRegion_i_v_; +text: .text%__1cWOffsetTableContigSpaceKset_bottom6MpnIHeapWord__v_; +text: .text%__1cQBlockOffsetArrayGresize6MI_v_: blockOffsetTable.o; +text: .text%__1cWOffsetTableContigSpaceHset_end6MpnIHeapWord__v_; +text: .text%__1cWOffsetTableContigSpaceFclear6M_v_; +text: .text%__1cbBBlockOffsetArrayContigSpaceUinitialize_threshold6M_pnIHeapWord__; +text: .text%__1cUGenericGrowableArrayEgrow6Mi_v_; +text: .text%__1cXPermanentGenerationSpecEinit6MnNReservedSpace_IpnJGenRemSet__pnHPermGen__; +text: .text%__1cRCompactingPermGen2t6MnNReservedSpace_1IpnJGenRemSet_pnXPermanentGenerationSpec__v_; +text: .text%__1cUCompactingPermGenGen2t6MnNReservedSpace_1IipnJGenRemSet_pnPContiguousSpace_pnXPermanentGenerationSpec__v_; +text: .text%__1cLFileMapInfoJmap_space6MinNReservedSpace_pnPContiguousSpace__i_; +text: .text%__1cNReservedSpaceHrelease6M_v_; +text: .text%__1cCosOrelease_memory6FpcI_i_; +text: .text%__1cCosKmap_memory6FipkcIpcIii_3_; +text: .text%__1cUCompactingPermGenGenbFinitialize_performance_counters6M_v_; +text: .text%__1cbCOneContigSpaceCardGenerationIcapacity6kM_I_; +text: .text%__1cPCollectorPolicybFis_concurrent_mark_sweep_policy6M_i_: collectorPolicy.o; +text: .text%__1cWThreadLocalAllocBufferWstartup_initialization6F_v_; +text: .text%__1cPGlobalTLABStats2t6M_v_; +text: .text%__1cXAdaptiveWeightedAverageGsample6Mf_v_; +text: .text%__1cQGenCollectedHeapNtlab_capacity6kM_I_; +text: .text%__1cQDefNewGenerationYsupports_tlab_allocation6kM_i_: defNewGeneration.o; +text: .text%__1cQDefNewGenerationNtlab_capacity6kM_I_: defNewGeneration.o; +text: .text%__1cKGenerationYsupports_tlab_allocation6kM_i_: tenuredGeneration.o; +text: .text%__1cUCompactingPermGenGenPinitialize_oops6F_v_; +text: .text%__1cQSystemDictionaryVset_shared_dictionary6FpnPHashtableBucket_ii_v_; +text: .text%__1cKDictionary2t6MipnPHashtableBucket_i_v_; +text: .text%__1cLClassLoaderZcreate_package_info_table6FpnPHashtableBucket_ii_v_; +text: .text%__1cUCompactingPermGenGenOserialize_oops6FpnTSerializeOopClosure__v_; +text: .text%__1cLReadClosureGdo_tag6Mi_v_: restore.o; +text: .text%__1cLReadClosureGdo_int6Mpi_v_: restore.o; +text: .text%__1cLFileMapInfoLassert_mark6Fi_v_; +text: .text%__1cQGenCollectedHeapEheap6F_p0_; +text: .text%__1cRCompactingPermGenGas_gen6kM_pnKGeneration__: permGen.o; +text: .text%__1cWBlockOffsetSharedArrayJserialize6MpnTSerializeOopClosure_pnIHeapWord_4_v_; +text: .text%__1cLReadClosureJdo_region6MpCI_v_: restore.o; +text: .text%__1cWOffsetTableContigSpacebKserialize_block_offset_array_offsets6MpnTSerializeOopClosure__v_; +text: .text%__1cbBBlockOffsetArrayContigSpaceJserialize6MpnTSerializeOopClosure__v_; +text: .text%__1cLReadClosureHreading6kM_i_: restore.o; +text: .text%__1cLReadClosureGdo_ptr6MppnIHeapWord__v_: restore.o; +text: .text%__1cLReadClosureGdo_ptr6Mppv_v_: restore.o; +text: .text%__1cLReadClosureJdo_size_t6MpI_v_: restore.o; +text: .text%__1cIUniverseHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cLReadClosureGdo_oop6MppnHoopDesc__v_: restore.o; +text: .text%__1cJvmSymbolsHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cJCodeCacheHoops_do6FpnKOopClosure__v_; +text: .text%__1cICodeHeapLfirst_block6kM_pnJHeapBlock__; +text: .text%__1cICodeHeapJnext_free6kMpnJHeapBlock__pv_; +text: .text%__1cKBufferBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cKBufferBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cICodeHeapLblock_start6kMpv_pnJHeapBlock__; +text: .text%__1cICodeHeapKfind_start6kMpv_1_; +text: .text%__1cICodeHeapKnext_block6kMpnJHeapBlock__2_; +text: .text%__1cLFileMapInfoFclose6M_v_; +text: .text%__1cQinterpreter_init6F_v_; +text: .text%__1cTAbstractInterpreterKinitialize6F_v_; +text: .text%__1cNTemplateTableKinitialize6F_v_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFi_vi_v_; +text: .text%__1cITemplateKinitialize6MinITosState_1pFi_vi_v_; +text: .text%__1cNTemplateTableNpd_initialize6F_v_; +text: .text%__1cRInvocationCounterMreinitialize6Fi_v_; +text: .text%__1cJStubQdDueue2t6MpnNStubInterface_ipnFMutex_pkc_v_; +text: .text%__1cICodeHeapJexpand_by6MI_i_; +text: .text%__1cUInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cbCAbstractInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cbCAbstractInterpreterGeneratorMgenerate_all6M_v_; +text: .text%__1cLCodeletMark2t6MrpnZInterpreterMacroAssembler_pkcinJBytecodesECode__v_: interpreter.o; +text: .text%__1cJStubQdDueueHrequest6Mi_pnEStub__; +text: .text%__1cbBInterpreterCodeletInterfaceRcode_size_to_size6kMi_i_: interpreter.o; +text: .text%__1cbBInterpreterCodeletInterfaceKinitialize6MpnEStub_i_v_: interpreter.o; +text: .text%__1cJStubQdDueueGcommit6Mi_v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_return_entry_for6MnITosState_i_pC_; +text: .text%__1cZInterpreterMacroAssemblerbAget_cache_and_index_at_bcp6MpnMRegisterImpl_2i_v_; +text: .text%__1cZInterpreterMacroAssemblerZget_2_byte_integer_at_bcp6MipnMRegisterImpl_2n0ALsignedOrNot_n0AKsetCCOrNot__v_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_next6MnITosState_i_v_; +text: .text%__1cZInterpreterMacroAssemblerTdispatch_Lbyte_code6MnITosState_ppCii_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interp_masm_sparc.o; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: interp_masm_sparc.o; +text: .text%__1cOMacroAssemblerKverify_FPU6Mipkc_v_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_deopt_entry_for6MnITosState_i_pC_; +text: .text%__1cZInterpreterMacroAssemblerXget_constant_pool_cache6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interpreter_sparc.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pCii_v_; +text: .text%__1cZInterpreterMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cOMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cOMacroAssemblerTset_last_Java_frame6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerVreset_last_Java_frame6M_v_; +text: .text%__1cOMacroAssemblerbBcheck_and_forward_exception6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerZcheck_and_handle_popframe6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: assembler_sparc.o; +text: .text%__1cTAbstractInterpreterSBasicType_as_index6FnJBasicType__i_; +text: .text%__1cbCAbstractInterpreterGeneratorbBgenerate_result_handler_for6MnJBasicType__pC_; +text: .text%__1cZInterpreterMacroAssemblerKverify_oop6MpnMRegisterImpl_nITosState__v_; +text: .text%__1cbCAbstractInterpreterGeneratorbFgenerate_slow_signature_handler6M_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_continuation_for6MnITosState__pC_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_safept_entry_for6MnITosState_pC_2_; +text: .text%__1cZInterpreterMacroAssemblerEpush6MnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerMdispatch_via6MnITosState_ppC_v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_i6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerIpush_ptr6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerUstore_unaligned_long6MpnMRegisterImpl_2i_v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cOMacroAssemblerWstore_unaligned_double6MpnRFloatRegisterImpl_pnMRegisterImpl_i_v_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_throw_exception6M_v_; +text: .text%__1cZInterpreterMacroAssemblerWempty_expression_stack6M_v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interp_masm_sparc.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cOMacroAssemblerNget_vm_result6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerDjmp6MpnMRegisterImpl_ipkci_v_; +text: .text%__1cZInterpreterMacroAssemblerbDunlock_if_synchronized_method6MnITosState_ii_v_; +text: .text%__1cOMacroAssemblerKbr_notnull6MpnMRegisterImpl_inJAssemblerHPredict_rnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerNunlock_object6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerHbr_null6MpnMRegisterImpl_inJAssemblerHPredict_rnFLabel__v_; +text: .text%__1cOMacroAssemblerPcasx_under_lock6MpnMRegisterImpl_22pCi_v_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: interp_masm_sparc.o; +text: .text%__1cQRelocationHolderEplus6kMi_0_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC222_v_; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: interpreter_sparc.o; +text: .text%__1cZInterpreterMacroAssemblerHpop_ptr6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerNset_vm_result6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerNload_contents6MrnHAddress_pnMRegisterImpl_i_v_: interp_masm_sparc.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: interp_masm_sparc.o; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpnMRegisterImpl_pC2_v_; +text: .text%__1cOMacroAssemblerRcall_VM_leaf_base6MpnMRegisterImpl_pCi_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbMgenerate_ArrayIndexOutOfBounds_handler6Mpkc_pC_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC22i_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbHgenerate_exception_handler_common6Mpkc2i_pC_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interpreter_sparc.o; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_StackOverflowError_handler6M_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_method_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cUInterpreterGeneratorbEgenerate_asm_interpreter_entry6Mi_pC_; +text: .text%__1cUInterpreterGeneratorbCgenerate_check_compiled_code6MrnFLabel__v_; +text: .text%__1cUInterpreterGeneratorUgenerate_fixed_frame6Mi_v_; +text: .text%__1cUInterpreterGeneratorbDgenerate_stack_overflow_check6MpnMRegisterImpl_22_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_counter_incr6MpnFLabel_22_v_; +text: .text%__1cZInterpreterMacroAssemblerbCincrement_invocation_counter6MpnMRegisterImpl_2_v_; +text: .text%__1cbCAbstractInterpreterGeneratorXbang_stack_shadow_pages6Mi_v_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: interp_masm_sparc.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: interp_masm_sparc.o; +text: .text%__1cZInterpreterMacroAssemblerTnotify_method_entry6M_v_; +text: .text%__1cUInterpreterGeneratorZgenerate_counter_overflow6MirnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerbIcompute_extra_locals_size_in_bytes6MpnMRegisterImpl_22_v_; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_: interp_masm_sparc.o; +text: .text%__1cUInterpreterGeneratorLlock_method6M_v_; +text: .text%__1cZInterpreterMacroAssemblerUadd_monitor_to_stack6MipnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerLlock_object6MpnMRegisterImpl_2_v_; +text: .text%__1cUInterpreterGeneratorUgenerate_empty_entry6M_pC_; +text: .text%__1cUInterpreterGeneratorXgenerate_accessor_entry6M_pC_; +text: .text%__1cUInterpreterGeneratorVgenerate_native_entry6Mi_pC_; +text: .text%__1cOMacroAssemblerUcalc_mem_param_words6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerOrestore_thread6MkpnMRegisterImpl__v_; +text: .text%__1cUInterpreterGeneratorVrestore_native_result6M_v_; +text: .text%__1cZInterpreterMacroAssemblerSnotify_method_exit6MinITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerQtop_most_monitor6M_nHAddress__; +text: .text%__1cbCAbstractInterpreterGeneratorbEset_entry_points_for_all_bytes6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorQset_entry_points6MnJBytecodesECode__v_; +text: .text%__1cbCAbstractInterpreterGeneratorWset_short_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cbCAbstractInterpreterGeneratorVset_vtos_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_l6MpnMRegisterImpl__v_; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_and_dispatch6MpnITemplate_nITosState__v_; +text: .text%__1cITemplateIbytecode6kM_nJBytecodesECode__; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_prolog6MnITosState_i_v_; +text: .text%__1cITemplateIgenerate6MpnZInterpreterMacroAssembler__v_; +text: .text%__1cNTemplateTableDnop6F_v_; +text: .text%__1cNTemplateTableKtransition6FnITosState_1_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_epilog6MnITosState_i_v_; +text: .text%__1cNDispatchTableJset_entry6MirnKEntryPoint__v_; +text: .text%__1cNTemplateTableLaconst_null6F_v_; +text: .text%__1cNTemplateTableGiconst6Fi_v_; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: templateTable_sparc.o; +text: .text%__1cNTemplateTableGlconst6Fi_v_; +text: .text%__1cNTemplateTableGfconst6Fi_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: templateTable_sparc.o; +text: .text%__1cNTemplateTableGdconst6Fi_v_; +text: .text%__1cNTemplateTableGbipush6F_v_; +text: .text%__1cNTemplateTableGsipush6F_v_; +text: .text%__1cNTemplateTableDldc6Fi_v_; +text: .text%__1cZInterpreterMacroAssemblerSget_cpool_and_tags6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: templateTable_sparc.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: templateTable_sparc.o; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC2_v_; +text: .text%__1cNTemplateTableGldc2_w6F_v_; +text: .text%__1cOMacroAssemblerVload_unaligned_double6MpnMRegisterImpl_ipnRFloatRegisterImpl__v_; +text: .text%__1cOMacroAssemblerTload_unaligned_long6MpnMRegisterImpl_i2_v_; +text: .text%__1cNTemplateTableFiload6F_v_; +text: .text%__1cNTemplateTableMlocals_index6FpnMRegisterImpl_i_v_; +text: .text%__1cZInterpreterMacroAssemblerQaccess_local_int6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableKwide_iload6F_v_; +text: .text%__1cNTemplateTableFlload6F_v_; +text: .text%__1cZInterpreterMacroAssemblerRaccess_local_long6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableKwide_lload6F_v_; +text: .text%__1cNTemplateTableFfload6F_v_; +text: .text%__1cZInterpreterMacroAssemblerSaccess_local_float6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cNTemplateTableKwide_fload6F_v_; +text: .text%__1cNTemplateTableFdload6F_v_; +text: .text%__1cZInterpreterMacroAssemblerTaccess_local_double6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cNTemplateTableKwide_dload6F_v_; +text: .text%__1cNTemplateTableFaload6F_v_; +text: .text%__1cZInterpreterMacroAssemblerQaccess_local_ptr6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableKwide_aload6F_v_; +text: .text%__1cNTemplateTableFiload6Fi_v_; +text: .text%__1cNTemplateTableFlload6Fi_v_; +text: .text%__1cNTemplateTableFfload6Fi_v_; +text: .text%__1cNTemplateTableFdload6Fi_v_; +text: .text%__1cNTemplateTableHaload_06F_v_; +text: .text%__1cNTemplateTableFaload6Fi_v_; +text: .text%__1cZInterpreterMacroAssemblerDpop6MnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerFpop_i6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableGiaload6F_v_; +text: .text%__1cZInterpreterMacroAssemblerLindex_check6MpnMRegisterImpl_2i22_v_; +text: .text%__1cZInterpreterMacroAssemblerXindex_check_without_pop6MpnMRegisterImpl_2i22_v_; +text: .text%__1cOMacroAssemblerIjumpl_to6MrnHAddress_pnMRegisterImpl_i_v_: interp_masm_sparc.o; +text: .text%__1cNTemplateTableGlaload6F_v_; +text: .text%__1cNTemplateTableGfaload6F_v_; +text: .text%__1cNTemplateTableGdaload6F_v_; +text: .text%__1cNTemplateTableGaaload6F_v_; +text: .text%__1cNTemplateTableGbaload6F_v_; +text: .text%__1cNTemplateTableGcaload6F_v_; +text: .text%__1cNTemplateTableGsaload6F_v_; +text: .text%__1cNTemplateTableGistore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerPstore_local_int6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableLwide_istore6F_v_; +text: .text%__1cNTemplateTableGlstore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerQstore_local_long6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableLwide_lstore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerFpop_l6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerFpop_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cNTemplateTableGfstore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerRstore_local_float6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cNTemplateTableLwide_fstore6F_v_; +text: .text%__1cNTemplateTableGdstore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerSstore_local_double6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cNTemplateTableLwide_dstore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerFpop_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cNTemplateTableGastore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerbCverify_oop_or_return_address6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerPstore_local_ptr6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableLwide_astore6F_v_; +text: .text%__1cNTemplateTableGistore6Fi_v_; +text: .text%__1cNTemplateTableGlstore6Fi_v_; +text: .text%__1cNTemplateTableGfstore6Fi_v_; +text: .text%__1cNTemplateTableGdstore6Fi_v_; +text: .text%__1cNTemplateTableGastore6Fi_v_; +text: .text%__1cNTemplateTableHiastore6F_v_; +text: .text%__1cNTemplateTableHlastore6F_v_; +text: .text%__1cNTemplateTableHfastore6F_v_; +text: .text%__1cNTemplateTableHdastore6F_v_; +text: .text%__1cNTemplateTableHaastore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerRprofile_checkcast6MipnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerRgen_subtype_check6MpnMRegisterImpl_2222rnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerOthrow_if_not_x6MnJAssemblerJCondition_pCpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: assembler_sparc.o; +text: .text%__1cNTemplateTableHbastore6F_v_; +text: .text%__1cNTemplateTableHcastore6F_v_; +text: .text%__1cNTemplateTableHsastore6F_v_; +text: .text%__1cNTemplateTableDpop6F_v_; +text: .text%__1cNTemplateTableEpop26F_v_; +text: .text%__1cNTemplateTableDdup6F_v_; +text: .text%__1cNTemplateTableGdup_x16F_v_; +text: .text%__1cNTemplateTableGdup_x26F_v_; +text: .text%__1cNTemplateTableEdup26F_v_; +text: .text%__1cNTemplateTableHdup2_x16F_v_; +text: .text%__1cNTemplateTableHdup2_x26F_v_; +text: .text%__1cNTemplateTableEswap6F_v_; +text: .text%__1cNTemplateTableEiop26Fn0AJOperation__v_; +text: .text%__1cNTemplateTableElop26Fn0AJOperation__v_; +text: .text%__1cNTemplateTableEfop26Fn0AJOperation__v_; +text: .text%__1cNTemplateTableEdop26Fn0AJOperation__v_; +text: .text%__1cNTemplateTableElmul6F_v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pCi_v_; +text: .text%__1cNTemplateTableEidiv6F_v_; +text: .text%__1cZInterpreterMacroAssemblerQthrow_if_not_icc6MnJAssemblerJCondition_pCpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableEldiv6F_v_; +text: .text%__1cNTemplateTableEirem6F_v_; +text: .text%__1cNTemplateTableElrem6F_v_; +text: .text%__1cNTemplateTableEineg6F_v_; +text: .text%__1cNTemplateTableElneg6F_v_; +text: .text%__1cOMacroAssemblerElneg6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableEfneg6F_v_; +text: .text%__1cNTemplateTableEdneg6F_v_; +text: .text%__1cNTemplateTableElshl6F_v_; +text: .text%__1cOMacroAssemblerElshl6MpnMRegisterImpl_22222_v_; +text: .text%__1cNTemplateTableElshr6F_v_; +text: .text%__1cOMacroAssemblerElshr6MpnMRegisterImpl_22222_v_; +text: .text%__1cNTemplateTableFlushr6F_v_; +text: .text%__1cOMacroAssemblerFlushr6MpnMRegisterImpl_22222_v_; +text: .text%__1cNTemplateTableEiinc6F_v_; +text: .text%__1cNTemplateTableJwide_iinc6F_v_; +text: .text%__1cNTemplateTableHconvert6F_v_; +text: .text%__1cOMacroAssemblerCfb6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: templateTable_sparc.o; +text: .text%__1cNTemplateTableElcmp6F_v_; +text: .text%__1cOMacroAssemblerElcmp6MpnMRegisterImpl_2222_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fi_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fii_v_; +text: .text%__1cOMacroAssemblerJfloat_cmp6MiipnRFloatRegisterImpl_2pnMRegisterImpl__v_; +text: .text%__1cNTemplateTableKdouble_cmp6Fi_v_; +text: .text%__1cNTemplateTableHif_0cmp6Fn0AJCondition__v_; +text: .text%__1cZInterpreterMacroAssemblerGif_cmp6MnJAssemblerJCondition_i_v_; +text: .text%__1cNTemplateTableGbranch6Fii_v_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_taken_branch6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerbAincrement_backedge_counter6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerbBtest_backedge_count_for_osr6MpnMRegisterImpl_22_v_; +text: .text%__1cNTemplateTableHif_icmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_acmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableF_goto6F_v_; +text: .text%__1cNTemplateTableDjsr6F_v_; +text: .text%__1cZInterpreterMacroAssemblerbAdispatch_next_noverify_oop6MnITosState_i_v_; +text: .text%__1cNTemplateTableDret6F_v_; +text: .text%__1cZInterpreterMacroAssemblerLprofile_ret6MnITosState_pnMRegisterImpl_3_v_; +text: .text%__1cNTemplateTableIwide_ret6F_v_; +text: .text%__1cNTemplateTableLtableswitch6F_v_; +text: .text%__1cZInterpreterMacroAssemblerTprofile_switch_case6MpnMRegisterImpl_222_v_; +text: .text%__1cZInterpreterMacroAssemblerWprofile_switch_default6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableMlookupswitch6F_v_; +text: .text%__1cNTemplateTableH_return6FnITosState__v_; +text: .text%__1cOMacroAssemblerDret6Mi_v_: templateTable_sparc.o; +text: .text%__1cNTemplateTableJgetstatic6Fi_v_; +text: .text%__1cNTemplateTableSgetfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableXjvmti_post_field_access6Fiii_v_; +text: .text%__1cNTemplateTableZload_field_cp_cache_entry6FipnMRegisterImpl_22i_v_; +text: .text%__1cNTemplateTableXresolve_cache_and_index6FipnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableJputstatic6Fi_v_; +text: .text%__1cNTemplateTableSputfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableUjvmti_post_field_mod6Fii_v_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_22_v_; +text: .text%__1cOMacroAssemblerGmembar6MnJAssemblerQMembar_mask_bits__v_: templateTable_sparc.o; +text: .text%__1cNTemplateTableIgetfield6Fi_v_; +text: .text%__1cOMacroAssemblerKnull_check6MpnMRegisterImpl_i_v_; +text: .text%__1cNTemplateTableOpatch_bytecode6FnJBytecodesECode_pnMRegisterImpl_4i_v_; +text: .text%__1cNTemplateTableIputfield6Fi_v_; +text: .text%__1cNTemplateTableNinvokevirtual6Fi_v_; +text: .text%__1cNTemplateTablebAload_invoke_cp_cache_entry6FipnMRegisterImpl_22ii_v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: templateTable_sparc.o; +text: .text%__1cNTemplateTableTinvokevfinal_helper6FpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerSprofile_final_call6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: templateTable_sparc.o; +text: .text%__1cZInterpreterMacroAssemblerUprofile_virtual_call6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableUgenerate_vtable_call6FpnMRegisterImpl_22_v_; +text: .text%__1cNTemplateTableNinvokespecial6Fi_v_; +text: .text%__1cZInterpreterMacroAssemblerMprofile_call6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableMinvokestatic6Fi_v_; +text: .text%__1cNTemplateTablePinvokeinterface6Fi_v_; +text: .text%__1cNTemplateTablebDinvokeinterface_object_method6FpnMRegisterImpl_222_v_; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_: templateTable_sparc.o; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC_v_; +text: .text%__1cNTemplateTableE_new6F_v_; +text: .text%__1cQGenCollectedHeapItop_addr6kM_ppnIHeapWord__; +text: .text%__1cQDefNewGenerationItop_addr6kM_ppnIHeapWord__; +text: .text%__1cQGenCollectedHeapIend_addr6kM_ppnIHeapWord__; +text: .text%__1cQDefNewGenerationIend_addr6kM_ppnIHeapWord__; +text: .text%__1cZInterpreterMacroAssemblerRget_constant_pool6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC22_v_; +text: .text%__1cNTemplateTableInewarray6F_v_; +text: .text%__1cNTemplateTableJanewarray6F_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC222_v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC222i_v_; +text: .text%__1cNTemplateTableLarraylength6F_v_; +text: .text%__1cZInterpreterMacroAssemblerQthrow_if_not_1_x6MnJAssemblerJCondition_rnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerOthrow_if_not_26MpCpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cNTemplateTableGathrow6F_v_; +text: .text%__1cNTemplateTableJcheckcast6F_v_; +text: .text%__1cNTemplateTableKinstanceof6F_v_; +text: .text%__1cNTemplateTableMmonitorenter6F_v_; +text: .text%__1cNTemplateTableLmonitorexit6F_v_; +text: .text%__1cNTemplateTableEwide6F_v_; +text: .text%__1cNTemplateTableOmultianewarray6F_v_; +text: .text%__1cNTemplateTableKif_nullcmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableGgoto_w6F_v_; +text: .text%__1cZInterpreterMacroAssemblerZget_4_byte_integer_at_bcp6MipnMRegisterImpl_2n0AKsetCCOrNot__v_; +text: .text%__1cNTemplateTableFjsr_w6F_v_; +text: .text%__1cNTemplateTableL_breakpoint6F_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_normal6MnITosState__v_; +text: .text%__1cNTemplateTableQfast_accessfield6FnITosState__v_; +text: .text%__1cNTemplateTablePfast_storefield6FnITosState__v_; +text: .text%__1cNTemplateTableZjvmti_post_fast_field_mod6F_v_; +text: .text%__1cNTemplateTableMfast_xaccess6FnITosState__v_; +text: .text%__1cNTemplateTableKfast_iload6F_v_; +text: .text%__1cNTemplateTableLfast_iload26F_v_; +text: .text%__1cNTemplateTableMfast_icaload6F_v_; +text: .text%__1cNTemplateTableRfast_invokevfinal6Fi_v_; +text: .text%__1cNTemplateTableRfast_linearswitch6F_v_; +text: .text%__1cNTemplateTableRfast_binaryswitch6F_v_; +text: .text%__1cNTemplateTableSshouldnotreachhere6F_v_; +text: .text%__1cWinvocationCounter_init6F_v_; +text: .text%__1cOmarksweep_init6F_v_; +text: .text%__1cQaccessFlags_init6F_v_; +text: .text%__1cStemplateTable_init6F_v_; +text: .text%__1cVInterfaceSupport_init6F_v_; +text: .text%__1cOuniverse2_init6F_v_; +text: .text%__1cIUniverseHgenesis6FpnGThread__v_; +text: .text%__1cLClassLoaderVcompute_Object_vtable6F_i_; +text: .text%__1cIUniversebCinit_self_patching_vtbl_list6Fppvi_v_; +text: .text%__1cJvmSymbolsKinitialize6FpnGThread__v_; +text: .text%__1cQSystemDictionaryKinitialize6FpnGThread__v_; +text: .text%__1cKDictionary2t6Mi_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: dictionary.o; +text: .text%__1cQPlaceholderTable2t6Mi_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: placeholders.o; +text: .text%__1cVLoaderConstraintTable2t6Mi_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: loaderConstraints.o; +text: .text%__1cKoopFactoryTnew_system_objArray6FipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cKSharedHeapWpermanent_mem_allocate6MI_pnIHeapWord__: genCollectedHeap.o; +text: .text%__1cRCompactingPermGenMmem_allocate6MI_pnIHeapWord__; +text: .text%__1cbCOneContigSpaceCardGenerationIallocate6MIii_pnIHeapWord__: compactingPermGenGen.o; +text: .text%__1cWOffsetTableContigSpaceIallocate6MI_pnIHeapWord__: space.o; +text: .text%__1cbBBlockOffsetArrayContigSpaceLalloc_block6MpnIHeapWord_2_v_: blockOffsetTable.o; +text: .text%__1cQSystemDictionarybCinitialize_preloaded_classes6FpnGThread__v_; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_nGHandle_2ipnGThread__pnMklassOopDesc__; +text: .text%__1cQSystemDictionarybEresolve_instance_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cVjava_lang_ClassLoaderbBnon_reflection_class_loader6FpnHoopDesc__2_; +text: .text%__1cKDictionaryEfind6MiInMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cMObjectLocker2t6MnGHandle_pnGThread__v_; +text: .text%__1cFMutexElock6MpnGThread__v_; +text: .text%__1cKDictionaryKfind_class6MiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cQPlaceholderTableKfind_entry6MiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cQPlaceholderTableJadd_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cJHashtableJnew_entry6MIpnHoopDesc__pnOHashtableEntry__; +text: .text%__1cQSystemDictionaryTload_instance_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_: systemDictionary.o; +text: .text%__1cKDictionaryRfind_shared_class6MiInMsymbolHandle__pnMklassOopDesc__; +text: .text%__1cQSystemDictionaryRload_shared_class6FnTinstanceKlassHandle_nGHandle_pnGThread__1_; +text: .text%__1cFKlassMset_subklass6MpnMklassOopDesc__v_; +text: .text%__1cFKlassQset_next_sibling6MpnMklassOopDesc__v_; +text: .text%__1cNmethodOopDescVclear_native_function6M_v_; +text: .text%__1cNSharedRuntimebWnative_method_throw_unsatisfied_link_error_entry6F_pC_; +text: .text%__1cTClassLoadingServiceTnotify_class_loaded6FpnNinstanceKlass_i_v_; +text: .text%__1cSinstanceKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceKlass.o; +text: .text%__1cRconstantPoolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cQSystemDictionaryVdefine_instance_class6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cQSystemDictionaryRcheck_constraints6FiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cVLoaderConstraintTablePcheck_or_update6MnTinstanceKlassHandle_nGHandle_nMsymbolHandle__pkc_; +text: .text%__1cFKlassWappend_to_sibling_list6M_v_; +text: .text%__1cNinstanceKlassSprocess_interfaces6MpnGThread__v_; +text: .text%__1cIUniverseTflush_dependents_on6FnTinstanceKlassHandle__v_; +text: .text%__1cJCodeCachebKnumber_of_nmethods_with_dependencies6F_i_; +text: .text%__1cQPlaceholderTableMremove_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cKDictionaryJadd_klass6MnMsymbolHandle_nGHandle_nLKlassHandle__v_; +text: .text%__1cNinstanceKlassQeager_initialize6MpnGThread__v_; +text: .text%__1cMObjectLocker2T6M_v_; +text: .text%__1cSObjectSynchronizerKslow_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cKJavaThreadNis_lock_owned6kMpC_i_; +text: .text%__1cGThreadLis_in_stack6kMpC_i_; +text: .text%__1cCosVcurrent_stack_pointer6F_pC_; +text: .text%__1cNinstanceKlassPadd_implementor6MpnMklassOopDesc__v_; +text: .text%__1cIUniverseNfixup_mirrors6FpnGThread__v_; +text: .text%__1cKSharedHeapYpermanent_object_iterate6MpnNObjectClosure__v_: genCollectedHeap.o; +text: .text%__1cHPermGenOobject_iterate6MpnNObjectClosure__v_: permGen.o; +text: .text%__1cbCOneContigSpaceCardGenerationOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cPContiguousSpaceOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cPContiguousSpaceTobject_iterate_from6MnJWaterMark_pnNObjectClosure__v_; +text: .text%__1cSFixupMirrorClosureJdo_object6MpnHoopDesc__v_: universe.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: objArrayKlass.o; +text: .text%__1cQinstanceRefKlassZupdate_nonstatic_oop_maps6FpnMklassOopDesc__v_; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceRefKlass.o; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassLfind_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cNinstanceKlassLfind_method6FpnPobjArrayOopDesc_pnNsymbolOopDesc_4_pnNmethodOopDesc__; +text: .text%__1cQSystemDictionarybDinitialize_basic_type_mirrors6FpnGThread__v_; +text: .text%__1cPjava_lang_ClassYcreate_basic_type_mirror6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cNinstanceKlassbBallocate_permanent_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cXreferenceProcessor_init6F_v_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJset_clock6Fx_v_; +text: .text%__1cQjni_handles_init6F_v_; +text: .text%__1cOvmStructs_init6F_v_; +text: .text%__1cVverificationType_init6F_v_; +text: .text%__1cOcompiler1_init6F_v_; +text: .text%__1cKSharedInfoKset_stack06Fi_v_; +text: .text%__1cKSharedInfoLset_regName6F_v_; +text: .text%__1cMRegisterImplEname6kM_pkc_; +text: .text%__1cRFloatRegisterImplEname6kM_pkc_; +text: .text%__1cIRegAllocYinit_register_allocation6F_v_; +text: .text%__1cIFrameMapEinit6F_v_; +text: .text%__1cKc1_RegMaskKinit_masks6Fi_v_; +text: .text%__1cFRInfoMset_long_reg6MkpnMRegisterImpl_k2_v_; +text: .text%__1cIFrameMapLcpu_reg2rnr6FpnMRegisterImpl__i_; +text: .text%__1cFRInfoMset_word_reg6MkpnMRegisterImpl__v_; +text: .text%__1cFRInfoNset_float_reg6MrkpnRFloatRegisterImpl__v_; +text: .text%__1cFRInfoOset_double_reg6MrkpnRFloatRegisterImpl__v_; +text: .text%__1cIFrameMapLcpu_rnr2reg6Fi_pnMRegisterImpl__; +text: .text%__1cIFrameMapXis_caller_save_register6FpnMRegisterImpl__i_; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_FrameMap_sparc.o; +text: .text%__1cNc1_AllocTableLinit_tables6F_v_; +text: .text%__1cIRuntime1Kinitialize6F_v_; +text: .text%__1cKCodeBufferRinsts_memory_size6Fi_i_; +text: .text%__1cKCodeBufferQlocs_memory_size6Fi_i_; +text: .text%__1cIRuntime1Ninitialize_pd6F_v_; +text: .text%__1cNSharedRuntimeTgenerate_deopt_blob6F_v_; +text: .text%__1cKCodeBuffer2t6MiiiiiipnKBufferBlob_pnJrelocInfo_pnORelocateBuffer_ipnLOopRecorder_pkcii_v_; +text: .text%__1cOMacroAssemblerZtotal_frame_size_in_bytes6Mi_i_; +text: .text%__1cJOopMapSet2t6M_v_; +text: .text%__1cOMacroAssemblerNsave_frame_c16Mi_v_; +text: .text%__1cTsave_live_registers6FpnOMacroAssembler_i_pnGOopMap__: c1_Runtime1_sparc.o; +text: .text%__1cGOopMap2t6Mii_v_; +text: .text%__1cVCompressedWriteStream2t6Mi_v_; +text: .text%__1cQCompressedStream2t6MpCi_v_; +text: .text%__1cGOopMapQset_callee_saved6MnHOptoRegEName_ii2_v_; +text: .text%__1cGOopMapHset_xxx6MnHOptoRegEName_nLOopMapValueJoop_types_ii2_v_; +text: .text%__1cIFrameMapLfpu_regname6Fi_nHOptoRegEName__; +text: .text%__1cKRelocationJpack_data6M_i_: c1_Runtime1.o; +text: .text%__1cJrelocInfoNfinish_prefix6Mph_p0_; +text: .text%__1cJrelocInfo2t6Mn0AJrelocType_ii_v_; +text: .text%__1cJOopMapSetKadd_gc_map6MiipnGOopMap__v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC22_v_; +text: .text%__1cSDeoptimizationBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_iiii_p0_; +text: .text%__1cNRelocIteratorTlocs_and_index_size6Fii_i_; +text: .text%__1cICodeBlob2t6MpkcpnKCodeBuffer_iiipnJOopMapSet_i_v_; +text: .text%__1cKCodeBufferPcopy_relocation6MpnICodeBlob__v_; +text: .text%__1cNRelocIteratorMcreate_index6FpnKCodeBuffer_pnJrelocInfo_4_4_; +text: .text%__1cKCodeBufferJcopy_code6MpnICodeBlob__v_; +text: .text%__1cRAbstractAssemblerOcode_fill_byte6F_i_; +text: .text%__1cICodeBlobWfix_relocation_at_move6Mi_v_; +text: .text%__1cNRelocIteratorKinitialize6MipnICodeBlob_pC3_v_; +text: .text%__1cNRelocIteratorKset_limits6MpC1_v_; +text: .text%__1cVPatchingRelocIteratorHprepass6M_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: relocInfo.o; +text: .text%__1cNRelocIteratorEnext6M_i_: codeBlob.o; +text: .text%__1cNRelocIteratorFreloc6M_pnKRelocation__; +text: .text%__1cOCallRelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cOCallRelocationFvalue6M_pC_: c1_Runtime1.o; +text: .text%__1cKRelocationTpd_call_destination6M_pC_; +text: .text%__1cKRelocationXpd_set_call_destination6MpCi_v_; +text: .text%__1cRNativeInstructionLset_long_at6Mii_v_; +text: .text%__1cOAbstractICachePinvalidate_word6FpC_v_; +text: .text%__1cKRelocationLunpack_data6M_v_: relocInfo.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: relocInfo.o; +text: .text%__1cVPatchingRelocIteratorIpostpass6M_v_; +text: .text%__1cJOopMapSetJheap_size6kM_i_; +text: .text%__1cJOopMapSetHcopy_to6MpC_v_; +text: .text%__1cLOopRecorder2t6MpnFArena__v_; +text: .text%__1cIRuntime1Rgenerate_code_for6Fn0AGStubID_pnNStubAssembler_pi_pnJOopMapSet__; +text: .text%__1cNStubAssemblerIset_info6Mpkci_v_; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: c1_Runtime1_sparc.o; +text: .text%__1cNStubAssemblerHcall_RT6MpnMRegisterImpl_2pC2_i_; +text: .text%__1cNStubAssemblerHcall_RT6MpnMRegisterImpl_2pCi_i_; +text: .text%__1cLRuntimeStubQnew_runtime_stub6FpkcpnKCodeBuffer_ipnJOopMapSet_i_p0_; +text: .text%__1cOMacroAssemblerPget_vm_result_26MpnMRegisterImpl__v_; +text: .text%__1cIRuntime1Ygenerate_exception_throw6FpnNStubAssembler_pCpnMRegisterImpl__pnJOopMapSet__; +text: .text%__1cOMacroAssemblerDret6Mi_v_: c1_Runtime1_sparc.o; +text: .text%__1cOMacroAssemblerLtlab_refill6MrnFLabel_22_v_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: assembler_sparc.o; +text: .text%__1cOMacroAssemblerNeden_allocate6MpnMRegisterImpl_2i22rnFLabel__v_; +text: .text%__1cOMacroAssemblerNtlab_allocate6MpnMRegisterImpl_2i2rnFLabel__v_; +text: .text%__1cRC1_MacroAssemblerRinitialize_object6MpnMRegisterImpl_22i22_v_; +text: .text%__1cRC1_MacroAssemblerRinitialize_header6MpnMRegisterImpl_222_v_; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: c1_MacroAssembler_sparc.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: c1_MacroAssembler_sparc.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: c1_Runtime1_sparc.o; +text: .text%__1cRC1_MacroAssemblerPinitialize_body6MpnMRegisterImpl_2_v_; +text: .text%__1cNStubAssemblerHcall_RT6MpnMRegisterImpl_2pC22_i_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: c1_Runtime1_sparc.o; +text: .text%__1cIRuntime1Iblob_for6Fn0AGStubID__pnICodeBlob__; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: c1_Runtime1_sparc.o; +text: .text%__1cOMacroAssemblerIjumpl_to6MrnHAddress_pnMRegisterImpl_i_v_: c1_Runtime1_sparc.o; +text: .text%__1cIiEntries2t6Miiii_v_; +text: .text%__1cIRuntime1Rgenerate_patching6FpnNStubAssembler_pC_pnJOopMapSet__; +text: .text%__1cIRuntime1Qgenerate_handler6FpnNStubAssembler_pCi_pnJOopMapSet__; +text: .text%__1cNSafepointBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cQvtableStubs_init6F_v_; +text: .text%__1cWInlineCacheBuffer_init6F_v_; +text: .text%__1cRInlineCacheBufferRic_stub_code_size6F_i_; +text: .text%__1cJStubQdDueueRrequest_committed6Mi_pnEStub__; +text: .text%__1cPICStubInterfaceRcode_size_to_size6kMi_i_: icBuffer.o; +text: .text%__1cPICStubInterfaceKinitialize6MpnEStub_i_v_: icBuffer.o; +text: .text%__1cTcompilerOracle_init6F_v_; +text: .text%__1cOCompilerOracleOread_from_line6Fpc_v_; +text: .text%__1cXonStackReplacement_init6F_v_; +text: .text%__1cICodeBlobPallocation_size6FpnKCodeBuffer_ii_I_; +text: .text%__1cUGenericGrowableArrayPraw_at_put_grow6MipknEGrET_3_v_; +text: .text%__1cWcompilationPolicy_init6F_v_; +text: .text%__1cSuniverse_post_init6F_v_; +text: .text%__1cQjava_lang_StringPcreate_from_str6FpkcpnGThread__nGHandle__; +text: .text%__1cKoopFactoryNnew_charArray6FpkcpnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cEUTF8Ounicode_length6Fpkc_i_; +text: .text%__1cOtypeArrayKlassIallocate6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cQGenCollectedHeapVlarge_typearray_limit6M_I_; +text: .text%__1cbCTwoGenerationCollectorPolicyYis_two_generation_policy6M_i_: collectorPolicy.o; +text: .text%__1cbCTwoGenerationCollectorPolicyVlarge_typearray_limit6M_I_; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: typeArrayKlass.o; +text: .text%__1cNCollectedHeapXallocate_from_tlab_slow6FpnGThread_I_pnIHeapWord__; +text: .text%__1cQGenCollectedHeapVunsafe_max_tlab_alloc6kM_I_; +text: .text%__1cQDefNewGenerationVunsafe_max_tlab_alloc6kM_I_: defNewGeneration.o; +text: .text%__1cQDefNewGenerationVunsafe_max_alloc_nogc6kM_I_; +text: .text%__1cPContiguousSpaceEfree6kM_I_: space.o; +text: .text%__1cWThreadLocalAllocBufferXclear_before_allocation6M_v_; +text: .text%__1cQGenCollectedHeapRallocate_new_tlab6MI_pnIHeapWord__; +text: .text%__1cQGenCollectedHeapMmem_allocate6MIii_pnIHeapWord__; +text: .text%__1cbCTwoGenerationCollectorPolicyRmem_allocate_work6MIii_pnIHeapWord__; +text: .text%__1cQDefNewGenerationPshould_allocate6MIii_i_: defNewGeneration.o; +text: .text%__1cQDefNewGenerationMpar_allocate6MIii_pnIHeapWord__: defNewGeneration.o; +text: .text%__1cJEdenSpaceMpar_allocate6MI_pnIHeapWord__; +text: .text%__1cWThreadLocalAllocBufferEfill6MpnIHeapWord_2I_v_; +text: .text%__1cEUTF8Sconvert_to_unicode6FpkcpHi_v_; +text: .text%__1cQjava_lang_StringQbasic_create_oop6FpnQtypeArrayOopDesc_ipnGThread__pnHoopDesc__; +text: .text%__1cNinstanceKlassRallocate_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cRCardTableModRefBSEkind6M_nKBarrierSetEName__: cardTableModRefBS.o; +text: .text%__1cTjava_lang_ThrowableLset_message6FpnHoopDesc_2_v_; +text: .text%__1cNSharedRuntimebIinitialize_StrictMath_entry_points6F_v_; +text: .text%__1cMNativeLookupTbase_library_lookup6Fpkc22_pC_; +text: .text%__1cKoopFactoryKnew_symbol6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cLSymbolTableGlookup6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cNsymbolOopDescGequals6kMpkci_i_; +text: .text%__1cNinstanceKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cNmethodOopDescThas_native_function6kM_i_; +text: .text%__1cMNativeLookupLlookup_base6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cMstringStream2t6MI_v_; +text: .text%__1cMoutputStreamFprint6MpkcE_v_; +text: .text%__1cMstringStreamFwrite6MpkcI_v_; +text: .text%__1cNmethodOopDescKklass_name6kM_pnNsymbolOopDesc__; +text: .text%__1cEUTF8Enext6FpkcpH_pc_; +text: .text%__1cMoutputStreamDput6Mc_v_; +text: .text%__1cMstringStreamJas_string6M_pc_; +text: .text%__1cMstringStream2T6M_v_; +text: .text%__1cCosYprint_jni_name_prefix_on6FpnMoutputStream_i_v_; +text: .text%__1cCosYprint_jni_name_suffix_on6FpnMoutputStream_i_v_; +text: .text%__1cNmethodOopDescTset_native_function6MpC_v_; +text: .text%__1cbEinitialize_converter_functions6F_v_; +text: .text%__1cQGenCollectedHeapIcapacity6kM_I_; +text: .text%__1cQDefNewGenerationIcapacity6kM_I_; +text: .text%__1cQGenCollectedHeapEused6kM_I_; +text: .text%__1cQDefNewGenerationEused6kM_I_; +text: .text%__1cbCOneContigSpaceCardGenerationEused6kM_I_; +text: .text%__1cQGenCollectedHeapPpost_initialize6M_v_; +text: .text%__1cQGenCollectedHeapTref_processing_init6M_v_; +text: .text%__1cKSharedHeapTref_processing_init6M_v_; +text: .text%__1cKGenerationSref_processor_init6M_v_; +text: .text%__1cKGenerationYrefs_discovery_is_atomic6kM_i_: compactingPermGenGen.o; +text: .text%__1cKGenerationUrefs_discovery_is_mt6kM_i_: compactingPermGenGen.o; +text: .text%__1cSReferenceProcessor2t6MnJMemRegion_iii_v_; +text: .text%__1cKGenerationYrefs_discovery_is_atomic6kM_i_: defNewGeneration.o; +text: .text%__1cKGenerationUrefs_discovery_is_mt6kM_i_: defNewGeneration.o; +text: .text%__1cKGenerationYrefs_discovery_is_atomic6kM_i_: tenuredGeneration.o; +text: .text%__1cKGenerationUrefs_discovery_is_mt6kM_i_: tenuredGeneration.o; +text: .text%__1cNMemoryServiceRset_universe_heap6FpnNCollectedHeap__v_; +text: .text%__1cQGenCollectedHeapEkind6M_nNCollectedHeapEName__: genCollectedHeap.o; +text: .text%__1cNMemoryServicebBadd_gen_collected_heap_info6FpnQGenCollectedHeap__v_; +text: .text%__1cPMarkSweepPolicyUis_mark_sweep_policy6M_i_: collectorPolicy.o; +text: .text%__1cNMemoryManagerXget_copy_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cPGCMemoryManager2t6M_v_; +text: .text%__1cNMemoryManagerWget_msc_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cQDefNewGenerationEkind6M_nKGenerationEName__: defNewGeneration.o; +text: .text%__1cTContiguousSpacePool2t6MpnPContiguousSpace_pkcnKMemoryPoolIPoolType_Ii_v_; +text: .text%__1cbBSurvivorContiguousSpacePool2t6MpnQDefNewGeneration_pkcnKMemoryPoolIPoolType_Ii_v_; +text: .text%__1cRTenuredGenerationEkind6M_nKGenerationEName__: tenuredGeneration.o; +text: .text%__1cOGenerationPool2t6MpnKGeneration_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cKGenerationMmax_capacity6kM_I_; +text: .text%__1cQGenCollectedHeapNgc_threads_do6kMpnNThreadClosure__v_; +text: .text%__1cPGCMemoryManagerXinitialize_gc_stat_info6M_v_; +text: .text%__1cQjavaClasses_init6F_v_; +text: .text%__1cQjava_lang_ThreadPcompute_offsets6F_v_; +text: .text%__1cNinstanceKlassQfind_local_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__i_; +text: .text%__1cPfieldDescriptorKinitialize6MpnMklassOopDesc_i_v_; +text: .text%__1cVjava_lang_ThreadGroupPcompute_offsets6F_v_; +text: .text%__1cYjava_lang_reflect_MethodPcompute_offsets6F_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldPcompute_offsets6F_v_; +text: .text%__1cSstubRoutines_init26F_v_; +text: .text%__1cNStubGeneratorYgenerate_throw_exception6MpkcpCi_3_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbIgenerate_handler_for_unsafe_access6M_pC_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerVverify_oop_subroutine6M_v_; +text: .text%__1cNThreadServiceKadd_thread6FpnKJavaThread_i_v_; +text: .text%__1cGThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cGThreadVis_jvmti_agent_thread6kM_i_: thread.o; +text: .text%__1cGEventsDlog6FpkcE_v_: thread.o; +text: .text%__1cLJvmtiExportbMtransition_pending_onload_raw_monitors6F_v_; +text: .text%__1cUJvmtiPendingMonitorsXtransition_raw_monitors6F_v_; +text: .text%__1cUGenericGrowableArrayUclear_and_deallocate6M_v_; +text: .text%__1cIVMThreadGcreate6F_v_; +text: .text%__1cCosNcreate_thread6FpnGThread_n0AKThreadType_I_i_; +text: .text%__1cCosMstart_thread6FpnGThread__v_; +text: .text%__1cCosPpd_start_thread6FpnGThread__v_; +text: .text%__1cHMonitorEwait6Mil_i_; +text: .text%_start: os_solaris.o; +text: .text%__1cCosTset_native_priority6FpnGThread_i_nIOSReturn__; +text: .text%__1cQset_lwp_priority6Fiii_i_; +text: .text%__1cIVMThreadMis_VM_thread6kM_i_: vmThread.o; +text: .text%__1cIVMThreadDrun6M_v_; +text: .text%__1cGThreadbFinitialize_thread_local_storage6M_v_; +text: .text%__1cGThreadbArecord_stack_base_and_size6M_v_; +text: .text%__1cHMonitorGnotify6M_i_; +text: .text%__1cLJvmtiExportRenter_start_phase6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_start6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_start6F_v_; +text: .text%__1cNinstanceKlassKinitialize6MpnGThread__v_; +text: .text%__1cNinstanceKlassVshould_be_initialized6kM_i_; +text: .text%__1cNinstanceKlassPinitialize_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassPlink_class_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cMelapsedTimerFstart6M_v_; +text: .text%__1cCosPelapsed_counter6F_x_; +text: .text%__1cIVerifierRverify_byte_codes6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cMelapsedTimerEstop6M_v_; +text: .text%__1cNinstanceKlassWadd_loader_constraints6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassNrewrite_class6MpnGThread__v_; +text: .text%__1cRInvocationCounterFreset6M_v_; +text: .text%__1cNmethodOopDescbGupdate_compiled_code_entry_point6Mi_v_; +text: .text%__1cIRuntime1Mientries_for6FnMmethodHandle__pnIiEntries__; +text: .text%__1cTAbstractInterpreterLmethod_kind6FnMmethodHandle__n0AKMethodKind__; +text: .text%__1cNmethodOopDescPis_empty_method6kM_i_; +text: .text%__1cNmethodOopDescLis_accessor6kM_i_; +text: .text%__1cNmethodOopDescMintrinsic_id6kM_n0ALIntrinsicId__; +text: .text%__1cNinstanceKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cLklassVtableRinitialize_vtable6MpnGThread__v_; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlass.o; +text: .text%__1cLklassVtableTupdate_super_vtable6MpnNinstanceKlass_pnNmethodOopDesc_i_i_; +text: .text%__1cLklassVtableMget_mirandas6FpnNGrowableArray4CpnNmethodOopDesc___pnMklassOopDesc_pnPobjArrayOopDesc_8_v_; +text: .text%__1cLklassItable2t6MnTinstanceKlassHandle__v_; +text: .text%__1cLklassItableRinitialize_itable6M_v_; +text: .text%__1cQSystemDictionaryXcheck_signature_loaders6FnMsymbolHandle_nGHandle_2ipnGThread__v_; +text: .text%__1cLklassVtableXvtable_accessibility_at6Mi_n0AKAccessType__; +text: .text%__1cLklassVtableYadd_new_mirandas_to_list6FpnNGrowableArray4CpnNmethodOopDesc___pnPobjArrayOopDesc_6pnMklassOopDesc__v_; +text: .text%__1cLklassItablebFinitialize_itable_for_interface6MpnMklassOopDesc_pnRitableMethodEntry__v_; +text: .text%__1cNinstanceKlassbBcall_class_initializer_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_; +text: .text%__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cRCompilationPolicyOmustBeCompiled6FnMmethodHandle__i_; +text: .text%__1cNmethodOopDescRis_not_compilable6kMi_i_; +text: .text%__1cCosbCstack_shadow_pages_available6FpnGThread_nMmethodHandle__i_; +text: .text%__1cTAbstractInterpreterbFsize_top_interpreter_activation6FpnNmethodOopDesc__i_; +text: .text%__1cGThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%__1cGThreadXclear_pending_exception6M_v_; +text: .text%__1cSInterpreterRuntimeOresolve_invoke6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cKJavaThreadPcook_last_frame6MnFframe__1_; +text: .text%__1cFframeYinterpreter_frame_method6kM_pnNmethodOopDesc__; +text: .text%__1cFframeVinterpreter_frame_bcp6kM_pC_; +text: .text%__1cMLinkResolverOresolve_invoke6FrnICallInfo_nGHandle_nSconstantPoolHandle_inJBytecodesECode_pnGThread__v_; +text: .text%__1cMLinkResolverMresolve_pool6FrnLKlassHandle_rnMsymbolHandle_42nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cTconstantPoolOopDescMklass_ref_at6MipnGThread__pnMklassOopDesc__; +text: .text%__1cTconstantPoolOopDescNklass_at_impl6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassMoop_is_klass6kM_i_: symbolKlass.o; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceKlass.o; +text: .text%__1cMLinkResolverZcheck_klass_accessability6FnLKlassHandle_1pnGThread__v_; +text: .text%__1cKReflectionTverify_class_access6FpnMklassOopDesc_2i_i_; +text: .text%__1cTconstantPoolOopDescLname_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cTconstantPoolOopDescQsignature_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cMLinkResolverTresolve_static_call6FrnICallInfo_rnLKlassHandle_nMsymbolHandle_53iipnGThread__v_; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverbAcheck_method_accessability6FnLKlassHandle_11nMmethodHandle_pnGThread__v_; +text: .text%__1cKReflectionTverify_field_access6FpnMklassOopDesc_22nLAccessFlags_ii_i_; +text: .text%__1cWConstantPoolCacheEntryKset_method6MnJBytecodesECode_nMmethodHandle_i_v_; +text: .text%__1cNmethodOopDescLresult_type6kM_nJBasicType__; +text: .text%__1cRSignatureIterator2t6MnMsymbolHandle__v_; +text: .text%__1cRSignatureIteratorSiterate_returntype6M_v_; +text: .text%__1cNSignatureInfoHdo_void6M_v_: bytecode.o; +text: .text%__1cQResultTypeFinderDset6MinJBasicType__v_: bytecode.o; +text: .text%__1cNmethodOopDescPis_final_method6kM_i_; +text: .text%__1cSInterpreterRuntimeTprepare_native_call6FpnKJavaThread_pnNmethodOopDesc__v_; +text: .text%__1cMNativeLookupGlookup6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cXSignatureHandlerLibraryDadd6FnMmethodHandle__v_; +text: .text%__1cRSignatureIterator2t6MpnNsymbolOopDesc__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIgenerate6MX_v_; +text: .text%__1cRSignatureIteratorSiterate_parameters6MX_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interpreterRT_sparc.o; +text: .text%__1cXSignatureHandlerLibraryOpd_set_handler6FpC_v_; +text: .text%__1cNmethodOopDescVset_signature_handler6MpC_v_; +text: .text%jni_RegisterNatives: jni.o; +text: .text%__1cPjava_lang_ClassLas_klassOop6FpnHoopDesc__pnMklassOopDesc__; +text: .text%__1cLSymbolTableFprobe6Fpkci_pnNsymbolOopDesc__; +text: .text%__1cFKlassNlookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cOJNIHandleBlockNrelease_block6Fp0pnGThread__v_; +text: .text%__1cSObjectSynchronizerJnotifyall6FnGHandle_pnGThread__v_; +text: .text%__1cSInterpreterRuntimeJanewarray6FpnKJavaThread_pnTconstantPoolOopDesc_ii_v_; +text: .text%__1cKoopFactoryMnew_objArray6FpnMklassOopDesc_ipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassRallocate_objArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6FnTinstanceKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassXallocate_objArray_klass6MinLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassbCallocate_objArray_klass_impl6FnYobjArrayKlassKlassHandle_inLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassTarray_klass_or_null6M_pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassTarray_klass_or_null6Mi_pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6FnTobjArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlass.o; +text: .text%__1cNsymbolOopDescLas_C_string6kM_pc_; +text: .text%__1cLSymbolTableJbasic_add6MipCiIpnGThread__pnNsymbolOopDesc__; +text: .text%__1cLsymbolKlassPallocate_symbol6MpCipnGThread__pnNsymbolOopDesc__; +text: .text%__1cKarrayKlassXbase_create_array_klass6FrknKKlass_vtbl_inLKlassHandle_pnGThread__nQarrayKlassHandle__; +text: .text%__1cFKlassRbase_create_klass6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__1_; +text: .text%__1cFKlassVbase_create_klass_oop6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlass.o; +text: .text%__1cKKlass_vtbl2n6FIrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: klass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: klass.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: objArrayKlass.o; +text: .text%__1cLAccessFlagsPatomic_set_bits6Mi_v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlass.o; +text: .text%__1cKarrayKlassbBcomplete_create_array_klass6FnQarrayKlassHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cFKlassRinitialize_supers6MpnMklassOopDesc_pnGThread__v_; +text: .text%__1cNobjArrayKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cNobjArrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cKarrayKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cKarrayKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: objArrayKlass.o; +text: .text%__1cPjava_lang_ClassNcreate_mirror6FnLKlassHandle_pnGThread__pnHoopDesc__; +text: .text%__1cNobjArrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cNinstanceKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cFKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cSInterpreterRuntimePresolve_get_put6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_ipnGThread__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_iipnGThread__v_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cMLinkResolverZcheck_field_accessability6FnLKlassHandle_11rnPfieldDescriptor_pnGThread__v_; +text: .text%__1cJFieldTypeKbasic_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cWConstantPoolCacheEntryJset_field6MnJBytecodesECode_2nLKlassHandle_iinITosState_ii_v_; +text: .text%__1cSInterpreterRuntimeE_new6FpnKJavaThread_pnTconstantPoolOopDesc_i_v_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnMklassOopDesc__i_; +text: .text%__1cNinstanceKlassVis_same_class_package6FpnHoopDesc_pnNsymbolOopDesc_24_i_; +text: .text%__1cEUTF8Hstrrchr6FpWiW_1_; +text: .text%__1cEUTF8Fequal6FpWi1i_i_; +text: .text%__1cNinstanceKlassbDcheck_valid_for_instantiation6MipnGThread__v_; +text: .text%__1cSinstanceKlassKlassMoop_is_klass6kM_i_: instanceKlassKlass.o; +text: .text%__1cMLinkResolverbFlinktime_resolve_special_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_special_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_4ipnGThread__v_; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: bytecode.o; +text: .text%__1cNSignatureInfoHdo_long6M_v_: bytecode.o; +text: .text%JVM_CurrentTimeMillis; +text: .text%__1cbBcreate_initial_thread_group6FpnGThread__nGHandle__: thread.o; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cMLinkResolverUresolve_special_call6FrnICallInfo_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cNmethodOopDescIbci_from6kMpC_i_; +text: .text%__1cPBytecode_invokeJsignature6kM_pnNsymbolOopDesc__; +text: .text%__1cNmethodOopDescIbcp_from6kMi_pC_; +text: .text%__1cFframebGinterpreter_callee_receiver_addr6MnMsymbolHandle__ppnHoopDesc__; +text: .text%__1cRSignatureIteratorSiterate_parameters6M_v_; +text: .text%__1cMLinkResolverVresolve_invokevirtual6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_virtual_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cFKlassXcan_be_statically_bound6FpnNmethodOopDesc__i_; +text: .text%__1cTconstantPoolOopDescNklass_name_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cNSignatureInfoGdo_int6M_v_: bytecode.o; +text: .text%__1cNSignatureInfoGdo_int6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cUArgumentSizeComputerDset6MinJBasicType__v_: c1_Runtime1_sparc.o; +text: .text%__1cNSignatureInfoHdo_char6M_v_: bytecode.o; +text: .text%__1cFKlassOis_subclass_of6kMpnMklassOopDesc__i_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnHoopDesc_pnNsymbolOopDesc__i_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_5pnGThread__v_; +text: .text%__1cLklassVtableIindex_of6kMpnNmethodOopDesc_i_i_; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_: interpreterRT_sparc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_object6M_v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interpreterRT_sparc.o; +text: .text%JVM_DoPrivileged; +text: .text%__1cLmethodKlassNoop_is_method6kM_i_: methodKlass.o; +text: .text%__1cMvframeStream2t6MpnKJavaThread_i_v_; +text: .text%__1cLRegisterMap2t6MpnKJavaThread_i_v_; +text: .text%__1cSvframeStreamCommonPfill_from_frame6M_i_; +text: .text%__1cFframeUis_interpreted_frame6kM_i_; +text: .text%__1cSvframeStreamCommonZsecurity_get_caller_frame6Mi_v_; +text: .text%__1cSvframeStreamCommonbHskip_method_invoke_and_aux_frames6M_v_; +text: .text%__1cSvframeStreamCommonEnext6M_v_; +text: .text%__1cFframeGsender6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cFframeZsender_with_pc_adjustment6kMpnLRegisterMap_pnICodeBlob_i_0_; +text: .text%__1cRPrivilegedElementKinitialize6MpnMvframeStream_pnHoopDesc_p0pnGThread__v_; +text: .text%__1cKJNIHandlesKmake_local6FpnHJNIEnv__pnHoopDesc__pnI_jobject__; +text: .text%__1cOJNIHandleBlockPallocate_handle6MpnHoopDesc__pnI_jobject__; +text: .text%__1cSInterpreterRuntimeNquicken_io_cc6FpnKJavaThread__v_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: bytecode.o; +text: .text%jni_FindClass: jni.o; +text: .text%__1cKJavaThreadZsecurity_get_caller_class6Mi_pnMklassOopDesc__; +text: .text%__1cbCfind_class_from_class_loader6FpnHJNIEnv__nMsymbolHandle_CnGHandle_3CpnGThread__pnH_jclass__; +text: .text%__1cRCompilationPolicyUcompleted_vm_startup6F_v_; +text: .text%jni_NewGlobalRef: jni.o; +text: .text%__1cKJNIHandlesLmake_global6FnGHandle_i_pnI_jobject__; +text: .text%jni_GetStringUTFChars: jni.o; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc__pc_; +text: .text%__1cHUNICODEHas_utf86FpHi_pc_; +text: .text%JVM_FindPrimitiveClass; +text: .text%__1cJname2type6Fpkc_nJBasicType__; +text: .text%jni_ReleaseStringUTFChars; +text: .text%__1cVcreate_initial_thread6FnGHandle_pnKJavaThread_pnGThread__pnHoopDesc__: thread.o; +text: .text%__1cQjava_lang_ThreadKset_thread6FpnHoopDesc_pnKJavaThread__v_; +text: .text%__1cQjava_lang_ThreadMset_priority6FpnHoopDesc_nOThreadPriority__v_; +text: .text%JVM_CurrentThread; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: bytecode.o; +text: .text%__1cSInterpreterRuntimeInewarray6FpnKJavaThread_nJBasicType_i_v_; +text: .text%__1cKoopFactoryNnew_typeArray6FnJBasicType_ipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: c1_Runtime1_sparc.o; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_: interpreterRT_sparc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIpass_int6M_v_: interpreterRT_sparc.o; +text: .text%JVM_ArrayCopy; +text: .text%__1cOtypeArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cOtypeArrayKlassQoop_is_typeArray6kM_i_: typeArrayKlass.o; +text: .text%JVM_GetStackAccessControlContext; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2t6M_v_; +text: .text%__1cJCodeCacheJfind_blob6Fpv_pnICodeBlob__; +text: .text%__1cICodeBlobJis_zombie6kM_i_: codeBlob.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: codeBlob.o; +text: .text%__1cFframeOis_first_frame6kM_i_; +text: .text%__1cFframeOis_entry_frame6kM_i_; +text: .text%__1cFframeUentry_frame_is_first6kM_i_; +text: .text%__1cbIjava_security_AccessControlContextGcreate6FnOobjArrayHandle_inGHandle_pnGThread__pnHoopDesc__; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2T6M_v_; +text: .text%JVM_GetInheritedAccessControlContext; +text: .text%__1cQjava_lang_ThreadbGinherited_access_control_context6FpnHoopDesc__2_; +text: .text%JVM_SetThreadPriority; +text: .text%__1cQjava_lang_ThreadGthread6FpnHoopDesc__pnKJavaThread__; +text: .text%__1cGThreadMset_priority6Fp0nOThreadPriority__v_; +text: .text%__1cCosMset_priority6FpnGThread_nOThreadPriority__nIOSReturn__; +text: .text%__1cQjava_lang_ThreadRset_thread_status6FpnHoopDesc_n0AMThreadStatus__v_; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceRefKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceRefKlass.o; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: c1_Runtime1_sparc.o; +text: .text%JVM_IsThreadAlive; +text: .text%__1cQjava_lang_ThreadIis_alive6FpnHoopDesc__i_; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: c1_Runtime1_sparc.o; +text: .text%JVM_StartThread; +text: .text%__1cQjava_lang_ThreadMis_stillborn6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_ThreadJstackSize6FpnHoopDesc__x_; +text: .text%__1cKJavaThread2t6MpFp0pnGThread__vI_v_; +text: .text%__1cKJavaThreadHprepare6MpnI_jobject_nOThreadPriority__v_; +text: .text%__1cQjava_lang_ThreadIpriority6FpnHoopDesc__nOThreadPriority__; +text: .text%__1cQjava_lang_ThreadJis_daemon6FpnHoopDesc__i_; +text: .text%__1cGThreadFstart6Fp0_v_; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceRefKlass.o; +text: .text%__1cbAcall_initializeSystemClass6FpnGThread__v_: thread.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cKJavaThreadDrun6M_v_; +text: .text%__1cMthread_entry6FpnKJavaThread_pnGThread__v_: jvm.o; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cMLinkResolverUresolve_virtual_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceRefKlass.o; +text: .text%__1cNSignatureInfoHdo_long6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: interpreterRT_sparc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorJpass_long6M_v_; +text: .text%__1cOMacroAssemblerOstore_argument6MpnMRegisterImpl_rnIArgument__v_: interpreterRT_sparc.o; +text: .text%JVM_MonitorWait; +text: .text%__1cQjava_lang_ThreadRget_thread_status6FpnHoopDesc__n0AMThreadStatus__; +text: .text%__1cSObjectSynchronizerEwait6FnGHandle_xpnGThread__v_; +text: .text%__1cNObjectMonitor2t6M_v_; +text: .text%__1cNObjectMonitorHRecycle6M_v_; +text: .text%__1cNObjectMonitorEwait6MxipnGThread__v_; +text: .text%__1cGThreadOis_interrupted6Fp0i_i_; +text: .text%__1cCosOis_interrupted6FpnGThread_i_i_; +text: .text%__1cNObjectMonitorEexit6MpnGThread__v_; +text: .text%jni_GetObjectClass: jni.o; +text: .text%jni_GetMethodID: jni.o; +text: .text%__1cNget_method_id6FpnHJNIEnv__pnH_jclass_pkc5ipnGThread__pnK_jmethodID__: jni.o; +text: .text%__1cPjava_lang_ClassMis_primitive6FpnHoopDesc__i_; +text: .text%__1cNmethodOopDescKjmethod_id6M_pnK_jmethodID__; +text: .text%__1cMjniIdSupportNto_jmethod_id6FpnNmethodOopDesc__pnK_jmethodID__; +text: .text%jni_NewStringUTF: jni.o; +text: .text%__1cQjava_lang_StringTcreate_oop_from_str6FpkcpnGThread__pnHoopDesc__; +text: .text%jni_CallObjectMethod: jni.o; +text: .text%__1cMjniIdSupportNto_method_oop6FpnK_jmethodID__pnNmethodOopDesc__; +text: .text%__1cRSignatureIterator2t6MpnGThread_pnNsymbolOopDesc__v_; +text: .text%__1cUjni_invoke_nonstatic6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cXJNI_ArgumentPusherVaArgHiterate6MX_v_: jni.o; +text: .text%__1cXJNI_ArgumentPusherVaArgKget_object6M_v_: jni.o; +text: .text%jni_ExceptionOccurred: jni.o; +text: .text%__1cKJavaThreadbHcheck_and_handle_async_exceptions6Mi_v_; +text: .text%jni_DeleteLocalRef: jni.o; +text: .text%jio_vsnprintf; +text: .text%jni_EnsureLocalCapacity; +text: .text%jni_GetStaticMethodID: jni.o; +text: .text%jni_CallStaticObjectMethodV: jni.o; +text: .text%__1cMLinkResolverbHlookup_instance_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%jni_ExceptionCheck: jni.o; +text: .text%jni_NewString: jni.o; +text: .text%__1cQjava_lang_StringXcreate_oop_from_unicode6FpHipnGThread__pnHoopDesc__; +text: .text%JVM_InitProperties; +text: .text%__1cQjava_lang_StringbHcreate_from_platform_depended_str6FpkcpnGThread__nGHandle__; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%jni_GetFieldID: jni.o; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2ipnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cSInterpreterRuntimeDldc6FpnKJavaThread_i_v_; +text: .text%__1cJFieldTypeYis_valid_array_signature6FpnNsymbolOopDesc__i_; +text: .text%__1cQSystemDictionarybBresolve_array_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cJFieldTypeOget_array_info6FpnNsymbolOopDesc_pip2pnGThread__nJBasicType__; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6FnUtypeArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlass.o; +text: .text%JVM_RegisterUnsafeMethods; +text: .text%JVM_IsArrayClass; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: typeArrayKlass.o; +text: .text%__1cNSignatureInfoHdo_char6M_v_: c1_Runtime1_sparc.o; +text: .text%JVM_GetComponentType; +text: .text%__1cKReflectionUarray_component_type6FpnHoopDesc_pnGThread__2_; +text: .text%JVM_IsPrimitiveClass; +text: .text%JVM_GetClassLoader; +text: .text%JVM_DesiredAssertionStatus; +text: .text%__1cOJavaAssertionsHenabled6Fpkci_i_; +text: .text%JVM_InternString; +text: .text%__1cLStringTableGintern6FpnHoopDesc_pnGThread__2_; +text: .text%__1cQjava_lang_StringRas_unicode_string6FpnHoopDesc_ri_pH_; +text: .text%__1cQjava_lang_StringGequals6FpnHoopDesc_pHi_i_; +text: .text%JVM_NanoTime; +text: .text%__1cCosNjavaTimeNanos6F_x_; +text: .text%JVM_GetCallerClass; +text: .text%JVM_SupportsCX8; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_: interpreterRT_sparc.o; +text: .text%JVM_GetClassDeclaredFields; +text: .text%__1cNinstanceKlassKlink_class6MpnGThread__v_; +text: .text%__1cKReflectionJnew_field6FpnPfieldDescriptor_ipnGThread__pnHoopDesc__; +text: .text%__1cLStringTableGintern6FpnNsymbolOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cNsymbolOopDescKas_unicode6kMri_pH_; +text: .text%__1cEUTF8Ounicode_length6Fpkci_i_; +text: .text%__1cKReflectionInew_type6FnMsymbolHandle_nLKlassHandle_pnGThread__nGHandle__; +text: .text%__1cJvmSymbolsOsignature_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cXjava_lang_reflect_FieldGcreate6FpnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_name6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_type6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectMset_override6FpnHoopDesc_C_v_; +text: .text%__1cXjava_lang_reflect_FieldThas_signature_field6F_i_; +text: .text%__1cXjava_lang_reflect_FieldVhas_annotations_field6F_i_; +text: .text%__1cPfieldDescriptorLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cXjava_lang_reflect_FieldPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cQjava_lang_StringbBcreate_tenured_from_unicode6FpHipnGThread__nGHandle__; +text: .text%__1cKoopFactoryXnew_permanent_charArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cOtypeArrayKlassSallocate_permanent6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cMLinkResolverXresolve_invokeinterface6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverbGruntime_resolve_interface_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cFKlassXsearch_secondary_supers6kMpnMklassOopDesc__i_; +text: .text%__1cLklassItableUcompute_itable_index6FpnNmethodOopDesc__i_; +text: .text%__1cWConstantPoolCacheEntrySset_interface_call6MnMmethodHandle_i_v_; +text: .text%Unsafe_ObjectFieldOffset; +text: .text%__1cXjava_lang_reflect_FieldFclazz6FpnHoopDesc__2_; +text: .text%__1cXjava_lang_reflect_FieldEslot6FpnHoopDesc__i_; +text: .text%__1cXjava_lang_reflect_FieldJmodifiers6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringScreate_from_symbol6FnMsymbolHandle_pnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldNset_signature6FpnHoopDesc_2_v_; +text: .text%JVM_IHashCode; +text: .text%__1cHoopDescSslow_identity_hash6M_i_; +text: .text%__1cSObjectSynchronizerXidentity_hash_value_for6FnGHandle__i_; +text: .text%__1cCosGrandom6F_l_; +text: .text%jni_GetStaticFieldID: jni.o; +text: .text%__1cNinstanceKlassKjni_id_for6Mi_pnFJNIid__; +text: .text%jni_SetStaticObjectField: jni.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceRefKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlass.o; +text: .text%__1cNobjArrayKlassPoop_is_objArray6kM_i_: objArrayKlass.o; +text: .text%__1cKarrayKlassTallocate_arrayArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlass.o; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: arrayKlass.o; +text: .text%jni_GetStringUTFLength: jni.o; +text: .text%__1cQjava_lang_StringLutf8_length6FpnHoopDesc__i_; +text: .text%__1cHUNICODELutf8_length6FpHi_i_; +text: .text%jni_GetStringLength: jni.o; +text: .text%__1cQjava_lang_StringGlength6FpnHoopDesc__i_; +text: .text%jni_GetStringUTFRegion: jni.o; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc_ii_pc_; +text: .text%JVM_FindClassFromClassLoader; +text: .text%JVM_IsInterface; +text: .text%JVM_GetClassDeclaredConstructors; +text: .text%__1cNmethodOopDescOis_initializer6kM_i_; +text: .text%__1cKReflectionPnew_constructor6FnMmethodHandle_pnGThread__pnHoopDesc__; +text: .text%__1cNinstanceKlassQmethod_index_for6kMpnNmethodOopDesc_pnGThread__i_; +text: .text%__1cKReflectionTget_parameter_types6FnMmethodHandle_ippnHoopDesc_pnGThread__nOobjArrayHandle__; +text: .text%__1cPSignatureStream2t6MnMsymbolHandle_i_v_; +text: .text%__1cPSignatureStreamEnext6M_v_; +text: .text%__1cNmethodOopDescbGresolved_checked_exceptions_impl6Fp0pnGThread__nOobjArrayHandle__; +text: .text%__1cSconstMethodOopDescZchecked_exceptions_length6kM_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorGcreate6FpnGThread__nGHandle__; +text: .text%__1cbDjava_lang_reflect_ConstructorJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorThas_signature_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorVhas_annotations_field6F_i_; +text: .text%__1cNmethodOopDescLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cbDjava_lang_reflect_ConstructorPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorbFhas_parameter_annotations_field6F_i_; +text: .text%__1cNmethodOopDescVparameter_annotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cbDjava_lang_reflect_ConstructorZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cKarrayKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%JVM_Clone; +text: .text%__1cRCardTableModRefBSPdirty_MemRegion6MnJMemRegion__v_; +text: .text%JVM_GetClassAccessFlags; +text: .text%JVM_GetClassName; +text: .text%__1cFKlassNexternal_name6kM_pkc_; +text: .text%__1cNsymbolOopDescWas_klass_external_name6kM_pkc_; +text: .text%__1cLStringTableGintern6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cLStringTableJbasic_add6MinGHandle_pHiIpnGThread__pnHoopDesc__; +text: .text%JVM_GetClassModifiers; +text: .text%jni_GetSuperclass: jni.o; +text: .text%__1cKJNIHandlesKmake_local6FpnHoopDesc__pnI_jobject__; +text: .text%JVM_NewInstanceFromConstructor; +text: .text%__1cKReflectionSinvoke_constructor6FpnHoopDesc_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cbDjava_lang_reflect_ConstructorFclazz6FpnHoopDesc__2_; +text: .text%__1cbDjava_lang_reflect_ConstructorEslot6FpnHoopDesc__i_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectIoverride6FpnHoopDesc__C_; +text: .text%__1cbDjava_lang_reflect_ConstructorPparameter_types6FpnHoopDesc__2_; +text: .text%__1cKReflectionGinvoke6FnTinstanceKlassHandle_nMmethodHandle_nGHandle_inOobjArrayHandle_nJBasicType_4ipnGThread__pnHoopDesc__; +text: .text%__1cLClassLoaderOload_classfile6FnMsymbolHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cFVTuneQstart_class_load6F_v_; +text: .text%__1cJEventMark2t6MpkcE_v_: classLoader.o; +text: .text%__1cSThreadProfilerMark2t6Mn0AGRegion__v_; +text: .text%__1cRClassPathZipEntryLopen_stream6Mpkc_pnPClassFileStream__; +text: .text%__1cPClassFileStream2t6MpCipc_v_; +text: .text%__1cPClassFileParserOparseClassFile6MnMsymbolHandle_nGHandle_2r1pnGThread__nTinstanceKlassHandle__; +text: .text%__1cIVerifierRshould_verify_for6FpnHoopDesc__i_; +text: .text%__1cPClassFileStreamGget_u46MpnGThread__I_; +text: .text%__1cPClassFileStreamGget_u26MpnGThread__H_; +text: .text%__1cIVerifierQrelax_verify_for6FpnHoopDesc__i_; +text: .text%__1cVjava_lang_ClassLoaderRis_trusted_loader6FpnHoopDesc__i_; +text: .text%__1cQSystemDictionarySjava_system_loader6F_pnHoopDesc__; +text: .text%__1cPClassFileParserTparse_constant_pool6MpnGThread__nSconstantPoolHandle__; +text: .text%__1cKoopFactoryQnew_constantPool6FipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cRconstantPoolKlassIallocate6MipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cKoopFactoryXnew_permanent_byteArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cPClassFileParserbBparse_constant_pool_entries6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileStreamGget_u16MpnGThread__C_; +text: .text%__1cPClassFileParserbEparse_constant_pool_utf8_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserRverify_legal_utf86MpkCipnGThread__v_; +text: .text%__1cPClassFileStreamHskip_u16MipnGThread__v_; +text: .text%__1cTconstantPoolOopDescSklass_ref_index_at6Mi_i_; +text: .text%__1cTconstantPoolOopDescbAname_and_type_ref_index_at6Mi_i_; +text: .text%__1cTconstantPoolOopDescRname_ref_index_at6Mi_i_; +text: .text%__1cTconstantPoolOopDescWsignature_ref_index_at6Mi_i_; +text: .text%__1cPClassFileParserbCverify_legal_class_modifiers6MipnGThread__v_; +text: .text%__1cPClassFileParserQparse_interfaces6MnSconstantPoolHandle_nGHandle_2pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserMparse_fields6MnSconstantPoolHandle_ipnUFieldAllocationCount_pnOobjArrayHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cKoopFactoryYnew_permanent_shortArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cPClassFileParserbCverify_legal_field_modifiers6MiipnGThread__v_; +text: .text%__1cPClassFileParserXverify_legal_field_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_field_signature6MnMsymbolHandle_1pnGThread__v_; +text: .text%__1cPClassFileParserWparse_field_attributes6MnSconstantPoolHandle_iHpHpi2pnPtypeArrayHandle_pnGThread__v_; +text: .text%__1cPClassFileParserUassemble_annotations6MpCi1ipnGThread__nPtypeArrayHandle__; +text: .text%__1cTconstantPoolOopDescbBbasic_type_for_signature_at6Mi_nJBasicType__; +text: .text%__1cPClassFileParserNparse_methods6MnSconstantPoolHandle_ipnLAccessFlags_ppnPobjArrayOopDesc_66pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserMparse_method6MnSconstantPoolHandle_ipnLAccessFlags_pnPtypeArrayHandle_55pnGThread__nMmethodHandle__; +text: .text%__1cPClassFileParserYverify_legal_method_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbDverify_legal_method_modifiers6MiinMsymbolHandle_pnGThread__v_; +text: .text%__1cPClassFileParserWparse_linenumber_table6MIIpipnGThread__pC_; +text: .text%__1cbFCompressedLineNumberWriteStream2t6Mi_v_; +text: .text%__1cbFCompressedLineNumberWriteStreamKwrite_pair6Mii_v_; +text: .text%__1cKoopFactoryKnew_method6FinLAccessFlags_iiipnGThread__pnNmethodOopDesc__; +text: .text%__1cQconstMethodKlassIallocate6MiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cSconstMethodOopDescLobject_size6Fiiii_i_; +text: .text%__1cSconstMethodOopDescZset_inlined_tables_length6Miii_v_; +text: .text%__1cLmethodKlassIallocate6MnRconstMethodHandle_nLAccessFlags_pnGThread__pnNmethodOopDesc__; +text: .text%__1cNmethodOopDescLobject_size6Fi_i_; +text: .text%__1cNmethodOopDescJinit_code6M_v_; +text: .text%__1cRInvocationCounterEinit6M_v_; +text: .text%__1cLmethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNmethodOopDescbAcompute_size_of_parameters6MpnGThread__v_; +text: .text%__1cSconstMethodOopDescbBcompressed_linenumber_table6kM_pC_; +text: .text%__1cVCompressedWriteStreamEgrow6M_v_; +text: .text%__1cPClassFileParserVparse_exception_table6MIInSconstantPoolHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cKoopFactoryWnew_permanent_intArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cQSystemDictionaryVresolve_super_or_fail6FnMsymbolHandle_1nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cPClassFileParserbDcompute_transitive_interfaces6MnTinstanceKlassHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cPClassFileParserMsort_methods6MnOobjArrayHandle_111pnGThread__nPtypeArrayHandle__; +text: .text%__1cNmethodOopDescMsort_methods6FpnPobjArrayOopDesc_222_v_; +text: .text%method_compare: methodOop.o; +text: .text%__1cLklassVtablebKcompute_vtable_size_and_num_mirandas6Fri1pnMklassOopDesc_pnPobjArrayOopDesc_nLAccessFlags_pnHoopDesc_pnNsymbolOopDesc_5_v_; +text: .text%__1cLklassVtableWneeds_new_vtable_entry6FpnNmethodOopDesc_pnMklassOopDesc_pnHoopDesc_pnNsymbolOopDesc_nLAccessFlags__i_; +text: .text%__1cLklassItableTcompute_itable_size6FnOobjArrayHandle__i_; +text: .text%__1cUvisit_all_interfaces6FpnPobjArrayOopDesc_pnXInterfaceVisiterClosure__v_; +text: .text%__1cKoopFactoryRnew_instanceKlass6FiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cSinstanceKlassKlassXallocate_instance_klass6MiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlass.o; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceKlass.o; +text: .text%__1cNinstanceKlassQinit_implementor6M_v_; +text: .text%__1cPClassFileParserbAparse_classfile_attributes6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbKparse_classfile_sourcefile_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbNparse_classfile_inner_classes_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__H_; +text: .text%__1cNinstanceKlassWdo_local_static_fields6MpFpnPfieldDescriptor_pnGThread__v4_v_; +text: .text%__1cXinitialize_static_field6FpnPfieldDescriptor_pnGThread__v_: classFileParser.o; +text: .text%__1cNinstanceKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cNinstanceKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cLklassItableZsetup_itable_offset_table6FnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserNfill_oop_maps6MnTinstanceKlassHandle_ii_v_; +text: .text%__1cFKlassKsuperklass6kM_pnNinstanceKlass__; +text: .text%__1cPClassFileParserVset_precomputed_flags6MnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserbCcheck_super_interface_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbBcheck_final_method_override6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cLClassLoaderLadd_package6Fpkci_i_; +text: .text%__1cSThreadProfilerMark2T6M_v_; +text: .text%__1cFVTuneOend_class_load6F_v_; +text: .text%__1cIRewriterHrewrite6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cKoopFactoryVnew_constantPoolCache6FipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cWconstantPoolCacheKlassIallocate6MipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cYconstantPoolCacheOopDescKinitialize6MrnIintArray__v_; +text: .text%__1cIRewriterOrewrite_method6FnMmethodHandle_rnIintArray_pnGThread__1_; +text: .text%__1cNmethodOopDescLlink_method6FnMmethodHandle__v_; +text: .text%JVM_MaxMemory; +text: .text%__1cQGenCollectedHeapMmax_capacity6kM_I_; +text: .text%__1cQDefNewGenerationMmax_capacity6kM_I_; +text: .text%Unsafe_AllocateMemory; +text: .text%Unsafe_SetNativeLong; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: bytecode.o; +text: .text%Unsafe_GetNativeByte; +text: .text%Unsafe_FreeMemory; +text: .text%__1cNSignatureInfoIdo_float6M_v_: bytecode.o; +text: .text%jni_NewObjectV: jni.o; +text: .text%jni_GetStringRegion: jni.o; +text: .text%__1cQjava_lang_StringGoffset6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringFvalue6FpnHoopDesc__pnQtypeArrayOopDesc__; +text: .text%jni_GetObjectField: jni.o; +text: .text%jni_GetStringCritical: jni.o; +text: .text%jni_ReleaseStringCritical: jni.o; +text: .text%__1cSInterpreterRuntimebAfrequency_counter_overflow6FpnKJavaThread_pC_x_; +text: .text%__1cQSimpleCompPolicyXmethod_invocation_event6MnMmethodHandle_pnGThread__v_; +text: .text%__1cRInvocationCounterJset_carry6M_v_; +text: .text%__1cNCompileBrokerOcompile_method6FnMmethodHandle_i1ipkcpnGThread__pnHnmethod__; +text: .text%__1cQSimpleCompPolicyRcompilation_level6MnMmethodHandle_i_i_; +text: .text%__1cNCompileBrokerTcompile_method_base6FnMmethodHandle_ii1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNinstanceKlassUfind_interface_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%JVM_LoadLibrary; +text: .text%JVM_FindLibraryEntry; +text: .text%jni_GetJavaVM; +text: .text%JVM_IsSupportedJNIVersion; +text: .text%jni_SetIntField: jni.o; +text: .text%jni_SetLongField: jni.o; +text: .text%JVM_FindSignal; +text: .text%JVM_RegisterSignal; +text: .text%__1cCosMuser_handler6F_pv_; +text: .text%__1cCosHSolarisOis_sig_ignored6Fi_i_; +text: .text%__1cCosGsignal6Fipv_1_; +text: .text%__1cWreset_vm_info_property6FpnGThread__v_: thread.o; +text: .text%__1cVquicken_jni_functions6F_v_; +text: .text%__1cQJNI_FastGetFieldbFgenerate_fast_get_boolean_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_int_field06FnJBasicType__pC_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: jniFastGetField_sparc.o; +text: .text%__1cYjni_GetBooleanField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_byte_field6F_pC_; +text: .text%__1cVjni_GetByteField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_char_field6F_pC_; +text: .text%__1cVjni_GetCharField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_short_field6F_pC_; +text: .text%__1cWjni_GetShortField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbBgenerate_fast_get_int_field6F_pC_; +text: .text%__1cUjni_GetIntField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_long_field6F_pC_; +text: .text%__1cVjni_GetLongField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_float_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_float_field06FnJBasicType__pC_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: jniFastGetField_sparc.o; +text: .text%__1cWjni_GetFloatField_addr6F_pC_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_double_field6F_pC_; +text: .text%__1cXjni_GetDoubleField_addr6F_pC_; +text: .text%__1cSset_init_completed6F_v_; +text: .text%__1cJTimeStampGupdate6M_v_; +text: .text%__1cQSystemDictionarybAcompute_java_system_loader6FpnGThread__v_; +text: .text%jni_NewObjectArray: jni.o; +text: .text%__1cNobjArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cNobjArrayKlassIallocate6MipnGThread__pnPobjArrayOopDesc__; +text: .text%jni_SetObjectArrayElement: jni.o; +text: .text%jni_GetObjectArrayElement: jni.o; +text: .text%__1cKSharedHeapXfill_region_with_object6FnJMemRegion__v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2t6M_v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2T6M_v_; +text: .text%__1cNobjArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cLRegisterMapFclear6Mpi_v_; +text: .text%__1cLJvmtiExportQenter_live_phase6F_v_; +text: .text%__1cLJvmtiExportTpost_vm_initialized6F_v_; +text: .text%__1cUJvmtiEventControllerHvm_init6F_v_; +text: .text%__1cHMonitorKnotify_all6M_i_; +text: .text%__1cFChunkbDstart_chunk_pool_cleaner_task6F_v_; +text: .text%__1cMPeriodicTask2t6MI_v_; +text: .text%__1cMPeriodicTaskGenroll6M_v_; +text: .text%__1cURecompilationMonitorbGstart_recompilation_monitor_task6F_v_; +text: .text%__1cCosLsignal_init6F_v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_53pnGThread__v_; +text: .text%__1cCosOsignal_init_pd6F_v_; +text: .text%__1cQjava_lang_ThreadKset_daemon6FpnHoopDesc__v_; +text: .text%__1cHThreadsDadd6FpnKJavaThread_i_v_; +text: .text%__1cICompiler2t6M_v_; +text: .text%__1cNCompileBrokerQcompilation_init6FpnQAbstractCompiler__v_; +text: .text%__1cNCompileBrokerVinit_compiler_threads6Fi_v_; +text: .text%__1cQCompilerCounters2t6MpkcipnGThread__v_; +text: .text%__1cNCompileBrokerUmake_compiler_thread6FpkcpnMCompileQdDueue_pnQCompilerCounters_pnGThread__pnOCompilerThread__; +text: .text%__1cTsignal_thread_entry6FpnKJavaThread_pnGThread__v_: os.o; +text: .text%__1cCosLsignal_wait6F_i_; +text: .text%__1cVcheck_pending_signals6Fi_i_: os_solaris.o; +text: .text%__1cOCompilerThread2t6MpnMCompileQdDueue_pnQCompilerCounters__v_; +text: .text%__1cOCompilerThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cCosFyield6F_v_; +text: .text%__1cCosFsleep6FpnGThread_xi_i_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cVcompiler_thread_entry6FpnKJavaThread_pnGThread__v_: thread.o; +text: .text%__1cNCompileBrokerUcompiler_thread_loop6F_v_; +text: .text%__1cICompilerKinitialize6M_v_; +text: .text%__1cKManagementKinitialize6FpnGThread__v_; +text: .text%__1cRLowMemoryDetectorKinitialize6F_v_; +text: .text%__1cXLowMemoryDetectorThreadbCis_hidden_from_external_view6kM_i_: lowMemoryDetector.o; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cLStatSamplerGengage6F_v_; +text: .text%__1cCosRelapsed_frequency6F_x_; +text: .text%__1cLStatSamplerbMcreate_system_property_instrumentation6FpnGThread__v_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_pnGThread__v_; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnPPerfLongCounter__; +text: .text%__1cSHighResTimeSamplerLtake_sample6M_x_: statSampler.o; +text: .text%__1cPPerfDataManagerHsampled6F_pnMPerfDataList__; +text: .text%__1cUGenericGrowableArrayNraw_appendAll6Mpk0_v_; +text: .text%__1cJTimeStampMmilliseconds6kM_x_; +text: .text%__1cKManagementWrecord_vm_startup_time6Fxx_v_; +text: .text%__1cORuntimeServiceYrecord_application_start6F_v_; +text: .text%__1cNWatcherThreadDrun6M_v_; +text: .text%__1cNWatcherThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cIos_sleep6Fxi_i_: os_solaris.o; +text: .text%__1cQjava_lang_StringOchar_converter6FnGHandle_HHpnGThread__1_; +text: .text%JVM_FindLoadedClass; +text: .text%__1cQSystemDictionarybCfind_instance_or_array_klass6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cSInterpreterRuntimeLmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cSObjectSynchronizerJslow_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%jni_CallStaticObjectMethod: jni.o; +text: .text%__1cNCompileBrokerZcompilation_is_prohibited6FnMmethodHandle_i_i_; +text: .text%__1cOCompilerOracleOshould_exclude6FnMmethodHandle__i_; +text: .text%__1cNCompileBrokerTwait_for_completion6FpnLCompileTask__pnHnmethod__; +text: .text%__1cCosPhint_no_preempt6F_v_; +text: .text%__1cNCompileBrokerZinvoke_compiler_on_method6FpnLCompileTask__v_; +text: .text%__1cNCompileBrokerQset_last_compile6FpnOCompilerThread_nMmethodHandle_ii_v_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cRLowMemoryDetectorbGlow_memory_detector_thread_entry6FpnKJavaThread_pnGThread__v_; +text: .text%__1cKPerfStringKset_string6Mpkc_v_; +text: .text%__1cOCompilerOraclePshould_break_at6FnMmethodHandle__i_; +text: .text%__1cFciEnv2t6MpnHJNIEnv__iii_v_; +text: .text%__1cPciObjectFactory2t6MpnFArena_i_v_; +text: .text%__1cPciObjectFactoryTinit_shared_objects6M_v_; +text: .text%__1cUGenericGrowableArray2t6MpnFArena_iipnEGrET__v_; +text: .text%__1cIciSymbol2t6MnMsymbolHandle__v_; +text: .text%__1cIciObject2t6MnGHandle__v_; +text: .text%__1cIciObjectJset_ident6MI_v_; +text: .text%__1cGciType2t6MnJBasicType__v_; +text: .text%__1cIciObject2t6M_v_; +text: .text%__1cPciObjectFactoryEfind6MpnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cPciObjectFactoryLis_found_at6MipnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cPciObjectFactoryNfind_non_perm6MpnHoopDesc__rpn0ANNonPermObject__; +text: .text%__1cKSharedHeapPis_in_permanent6kMpkv_i_: genCollectedHeap.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: klassKlass.o; +text: .text%__1cPciObjectFactoryRcreate_new_object6MpnHoopDesc__pnIciObject__; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodKlass.o; +text: .text%__1cIciSymbolEmake6Fpkc_p0_; +text: .text%__1cFciEnvIis_in_vm6F_i_; +text: .text%__1cPciObjectFactoryDget6MpnHoopDesc__pnIciObject__; +text: .text%__1cLsymbolKlassNoop_is_symbol6kM_i_: symbolKlass.o; +text: .text%__1cHciKlass2t6MnLKlassHandle_pnIciSymbol__v_; +text: .text%__1cGciType2t6MnLKlassHandle__v_; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodKlass.o; +text: .text%__1cPciObjectFactoryGinsert6MipnIciObject_pnNGrowableArray4C2___v_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: symbolKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: symbolKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: symbolKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: symbolKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: symbolKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: klassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassUoop_is_instanceKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cSinstanceKlassKlassUoop_is_instanceKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassVoop_is_typeArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlassKlass.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassUoop_is_objArrayKlass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: objArrayKlassKlass.o; +text: .text%__1cPciInstanceKlass2t6MnLKlassHandle__v_; +text: .text%__1cHciKlass2t6MnLKlassHandle__v_; +text: .text%__1cFKlassIsubklass6kM_p0_; +text: .text%__1cPciInstanceKlassTis_java_lang_Object6M_i_; +text: .text%__1cIciObjectGequals6Mp0_i_; +text: .text%__1cPciInstanceKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cHciKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cFKlassMoop_is_klass6kM_i_: instanceKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlass.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: instanceKlass.o; +text: .text%__1cUciInstanceKlassKlassEmake6F_p0_; +text: .text%__1cHciKlass2t6MpnIciSymbol_p0_v_; +text: .text%__1cGciType2t6MpnHciKlass__v_; +text: .text%__1cIciObject2t6MpnHciKlass__v_; +text: .text%__1cIciObjectUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cPciObjArrayKlass2t6MpnIciSymbol_pnHciKlass_i_v_; +text: .text%__1cUciObjArrayKlassKlassEmake6F_p0_; +text: .text%__1cMciArrayKlass2t6MpnIciSymbol_ipnHciKlass__v_; +text: .text%__1cRciArrayKlassKlassUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cQciTypeArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cMciArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cFciEnvWget_method_from_handle6MpnI_jobject__pnIciMethod__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodKlass.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodKlass.o; +text: .text%__1cIciMethod2t6MnMmethodHandle__v_; +text: .text%__1cLciSignature2t6MpnHciKlass_pnIciSymbol__v_; +text: .text%__1cPSignatureStreamJis_object6kM_i_; +text: .text%__1cPSignatureStreamJas_symbol6MpnGThread__pnNsymbolOopDesc__; +text: .text%__1cFciEnvWget_klass_by_name_impl6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cPciObjectFactorySget_unloaded_klass6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cPciInstanceKlassGloader6M_pnHoopDesc__; +text: .text%__1cPciInstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cQSystemDictionarybOfind_constrained_instance_or_array_klass6FnMsymbolHandle_nGHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cGciTypeEmake6FnJBasicType__p0_; +text: .text%__1cJTraceTime2t6MpkcpnMelapsedTimer_iipnMoutputStream__v_; +text: .text%__1cICompilerOcompile_method6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cIRuntime1Pnew_code_buffer6F_pnKCodeBuffer__; +text: .text%__1cLCompilation2t6MpnQAbstractCompiler_pnFciEnv_pnIciMethod_ipnRC1_MacroAssembler__v_; +text: .text%__1cTExceptionRangeTable2t6Mi_v_; +text: .text%__1cWImplicitExceptionTableIset_size6MI_v_; +text: .text%__1cLCompilationOcompile_method6M_v_; +text: .text%__1cYDebugInformationRecorder2t6MpnLOopRecorder__v_; +text: .text%__1cUDebugInfoWriteStream2t6MpnYDebugInformationRecorder_i_v_; +text: .text%__1cLCompilationbBis_optimized_library_method6kM_i_; +text: .text%__1cLCompilationTcompile_java_method6MpnLCodeOffsets__i_; +text: .text%__1cIciMethodMall_oop_maps6M_pnKciLocalMap__; +text: .text%__1cSciGenerateLocalMap2t6MpnFArena_nMmethodHandle__v_; +text: .text%__1cOGenerateOopMap2t6MnMmethodHandle__v_; +text: .text%__1cRRawBytecodeStream2t6MnMmethodHandle__v_; +text: .text%__1cOGenerateOopMapLcompute_map6MpnGThread__v_; +text: .text%__1cIRetTableRcompute_ret_table6MnMmethodHandle__v_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: generateOopMap.o; +text: .text%__1cOGenerateOopMapbImark_bbheaders_and_count_gc_points6M_v_; +text: .text%__1cOGenerateOopMapNinitialize_bb6M_v_; +text: .text%__1cOGenerateOopMapLbb_mark_fct6Fp0ipi_v_; +text: .text%__1cOGenerateOopMapPjump_targets_do6MpnOBytecodeStream_pFp0ipi_v4_i_; +text: .text%__1cSciGenerateLocalMapRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cOGenerateOopMapRdo_interpretation6M_v_; +text: .text%__1cOGenerateOopMapRinit_basic_blocks6M_v_; +text: .text%__1cOGenerateOopMapKinit_state6M_v_; +text: .text%__1cOGenerateOopMapTmark_reachable_code6M_v_; +text: .text%__1cRRawBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cOGenerateOopMapUreachable_basicblock6Fp0ipi_v_; +text: .text%__1cOGenerateOopMapbAget_basic_block_containing6kMi_pnKBasicBlock__; +text: .text%__1cOGenerateOopMapYsetup_method_entry_state6M_v_; +text: .text%__1cOGenerateOopMapbAmake_context_uninitialized6M_v_; +text: .text%__1cOGenerateOopMapTmethodsig_to_effect6MpnNsymbolOopDesc_ipnNCellTypeState__i_; +text: .text%__1cRComputeEntryStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cOGenerateOopMapPinitialize_vars6M_v_; +text: .text%__1cOGenerateOopMapTmerge_state_into_bb6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapKcopy_state6MpnNCellTypeState_2_v_; +text: .text%__1cOGenerateOopMapKinterp_all6M_v_; +text: .text%__1cOGenerateOopMapJinterp_bb6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapQnext_bb_start_pc6MpnKBasicBlock__i_; +text: .text%__1cOGenerateOopMapHinterp16MpnOBytecodeStream__v_; +text: .text%__1cOGenerateOopMapGppload6MpnNCellTypeState_i_v_; +text: .text%__1cOGenerateOopMapHget_var6Mi_nNCellTypeState__; +text: .text%__1cOGenerateOopMapEpush6MnNCellTypeState__v_; +text: .text%__1cOGenerateOopMapDpop6M_nNCellTypeState__; +text: .text%__1cOGenerateOopMapLmerge_state6Fp0ipi_v_; +text: .text%__1cOGenerateOopMapFppush6MpnNCellTypeState__v_; +text: .text%__1cOGenerateOopMapJdo_astore6Mi_v_; +text: .text%__1cOGenerateOopMapIdo_field6Miiii_v_; +text: .text%__1cOGenerateOopMapRsigchar_to_effect6McipnNCellTypeState__2_; +text: .text%__1cOGenerateOopMapHppstore6MpnNCellTypeState_i_v_; +text: .text%__1cOGenerateOopMapTmerge_state_vectors6MpnNCellTypeState_2_i_; +text: .text%__1cNCellTypeStateFmerge6kM0i_0_; +text: .text%__1cOGenerateOopMapJppdupswap6Mipkc_v_; +text: .text%__1cOGenerateOopMapYrewrite_refval_conflicts6M_v_; +text: .text%__1cSciGenerateLocalMapOreport_results6kM_i_: ciOopMap.o; +text: .text%__1cOGenerateOopMapNreport_result6M_v_; +text: .text%__1cSciGenerateLocalMapUfill_stackmap_prolog6Mi_v_; +text: .text%__1cSciGenerateLocalMapZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cSciGenerateLocalMapUfill_stackmap_epilog6M_v_: ciOopMap.o; +text: .text%__1cSciGenerateLocalMapOfill_init_vars6MpnNGrowableArray4Ci___v_; +text: .text%__1cLCompilationJbuild_hir6M_v_; +text: .text%__1cCIR2t6MpnLCompilation_pnIciMethod_i_v_; +text: .text%__1cJValueTypeKinitialize6F_v_; +text: .text%__1cMciNullObjectEmake6F_p0_; +text: .text%__1cMGraphBuilderKinitialize6F_v_; +text: .text%__1cHIRScope2t6MpnLCompilation_p0ipnIciMethod_ii_v_; +text: .text%__1cOLocalSlotArray2t6MkikpnJLocalSlot__v_: c1_IR.o; +text: .text%__1cGBitMap2t6MI_v_; +text: .text%__1cNWordSizeArray2t6Mki1_v_: c1_IR.o; +text: .text%__1cJXHandlers2t6MpnIciMethod__v_; +text: .text%__1cIciMethodJload_code6M_v_; +text: .text%__1cIciMethodVhas_balanced_monitors6M_i_; +text: .text%__1cLCompilationTdebug_info_recorder6kM_pnYDebugInformationRecorder__; +text: .text%__1cHIRScopeLbuild_graph6MpnLCompilation_i_pnKBlockBegin__; +text: .text%__1cQBlockListBuilder2t6MpnHIRScope_ii_v_; +text: .text%__1cPBlockBeginArray2t6MkikpnKBlockBegin__v_: c1_GraphBuilder.o; +text: .text%__1cQBlockListBuilderLset_leaders6M_v_; +text: .text%__1cQciBytecodeStream2t6MpnIciMethod__v_; +text: .text%__1cQBlockListBuilderMnew_block_at6MinKBlockBeginEFlag__p1_; +text: .text%__1cKValueStack2t6MpnHIRScope_ii_v_; +text: .text%__1cKValueArray2t6MkikpnLInstruction__v_: c1_ValueStack.o; +text: .text%__1cJLocalSlotIfor_type6MpnJValueType_ii_pnFLocal__: c1_IR.o; +text: .text%__1cKObjectTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cLciSignatureHtype_at6kMi_pnGciType__; +text: .text%__1cMas_ValueType6FnJBasicType__pnJValueType__; +text: .text%__1cMGraphBuilder2t6MpnLCompilation_pnHIRScope_pnJBlockList_pnKBlockBegin__v_; +text: .text%__1cOExceptionScope2t6M_v_; +text: .text%__1cIValueMap2t6M_v_; +text: .text%__1cNResourceArrayGexpand6MIiri_v_; +text: .text%__1cIValueMapIkill_all6M_v_; +text: .text%__1cKValueStackEcopy6M_p0_; +text: .text%__1cMGraphBuilderbBiterate_bytecodes_for_block6Mi_pnIBlockEnd__; +text: .text%__1cQciBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cLInstructionLas_BlockEnd6M_pnIBlockEnd__: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderJScopeDataIblock_at6Mi_pnKBlockBegin__; +text: .text%__1cMGraphBuilderKload_local6MpnJValueType_i_v_; +text: .text%__1cMGraphBuilderLappend_base6MpnLInstruction__2_; +text: .text%__1cJLoadLocalFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerMdo_LoadLocal6MpnJLoadLocal__v_; +text: .text%__1cIValueMapEfind6MpnLInstruction__2_; +text: .text%__1cLInstructionOas_AccessField6M_pnLAccessField__: c1_GraphBuilder.o; +text: .text%__1cLInstructionLas_UnsafeOp6M_pnIUnsafeOp__: c1_GraphBuilder.o; +text: .text%__1cLInstructionMas_Intrinsic6M_pnJIntrinsic__: c1_GraphBuilder.o; +text: .text%__1cLInstructionEhash6kM_i_: c1_GraphBuilder.o; +text: .text%__1cLInstructionNas_StateSplit6M_pnKStateSplit__: c1_GraphBuilder.o; +text: .text%__1cLInstructionIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cKValueStackLclear_store6Mi_v_; +text: .text%__1cKValueStackEpush6MpnJValueType_pnLInstruction__v_: c1_GraphBuilder.o; +text: .text%__1cKValueStackDpop6MpnJValueType__pnLInstruction__: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderHif_node6MpnLInstruction_n0BJCondition_2pnKValueStack__v_; +text: .text%__1cCIf2t6MpnLInstruction_n0BJCondition_i2pnKBlockBegin_5pnKValueStack_i_v_: c1_GraphBuilder.o; +text: .text%__1cCIfFvisit6MpnSInstructionVisitor__v_: c1_Canonicalizer.o; +text: .text%__1cNCanonicalizerFdo_If6MpnCIf__v_; +text: .text%__1cJValueTypeLis_constant6kM_i_: c1_ValueType.o; +text: .text%__1cJValueTypeOas_IntConstant6M_pnLIntConstant__: c1_ValueType.o; +text: .text%__1cLInstructionOas_AccessField6M_pnLAccessField__: c1_Canonicalizer.o; +text: .text%__1cLInstructionLas_UnsafeOp6M_pnIUnsafeOp__: c1_Canonicalizer.o; +text: .text%__1cLInstructionMas_Intrinsic6M_pnJIntrinsic__: c1_Canonicalizer.o; +text: .text%__1cLInstructionEhash6kM_i_: c1_Canonicalizer.o; +text: .text%__1cKStateSplitNas_StateSplit6M_p0_: c1_Canonicalizer.o; +text: .text%__1cIBlockEndLas_BlockEnd6M_p0_: c1_Canonicalizer.o; +text: .text%__1cLInstructionIcan_trap6kM_i_: c1_Canonicalizer.o; +text: .text%__1cLInstructionJas_Return6M_pnGReturn__: c1_Canonicalizer.o; +text: .text%__1cLInstructionIas_Throw6M_pnFThrow__: c1_Canonicalizer.o; +text: .text%__1cKValueStackNpin_stack_all6MnLInstructionJPinReason__v_; +text: .text%__1cKBlockBeginItry_join6MpnKValueStack__i_; +text: .text%__1cKValueStack2t6Mp0_v_; +text: .text%__1cKValueStackEinit6Mp0_v_; +text: .text%__1cMGraphBuilderJScopeDataQadd_to_work_list6MpnKBlockBegin__v_; +text: .text%__1cMGraphBuilderLinstance_of6Mi_v_; +text: .text%__1cQciBytecodeStreamJget_klass6kM_pnHciKlass__; +text: .text%__1cFciEnvSget_klass_by_index6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cFciEnvXget_klass_by_index_impl6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cTconstantPoolOopDescSklass_at_if_loaded6FnSconstantPoolHandle_i_pnMklassOopDesc__; +text: .text%__1cKInstanceOfFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerNdo_InstanceOf6MpnKInstanceOf__v_; +text: .text%__1cKStateSplitNas_StateSplit6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLInstructionJas_Invoke6M_pnGInvoke__: c1_GraphBuilder.o; +text: .text%__1cKValueStackMclear_locals6M_v_; +text: .text%__1cKValueStackZpin_stack_for_state_split6M_v_; +text: .text%__1cJTypeCheckIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cOExceptionScopeEcopy6M_p0_; +text: .text%__1cMGraphBuilderOdirect_compare6MpnHciKlass__i_; +text: .text%__1cPciInstanceKlassRis_instance_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cIConstantFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerLdo_Constant6MpnIConstant__v_; +text: .text%__1cLInstructionOas_AccessField6M_pnLAccessField__: c1_Instruction.o; +text: .text%__1cLInstructionLas_UnsafeOp6M_pnIUnsafeOp__: c1_Instruction.o; +text: .text%__1cLInstructionMas_Intrinsic6M_pnJIntrinsic__: c1_Instruction.o; +text: .text%__1cIConstantEhash6kM_i_; +text: .text%__1cHIntTypeDtag6kM_nIValueTag__: c1_Canonicalizer.o; +text: .text%__1cLIntConstantOas_IntConstant6M_p0_: c1_Canonicalizer.o; +text: .text%__1cIConstantEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cLInstructionNas_StateSplit6M_pnKStateSplit__: c1_Instruction.o; +text: .text%__1cLInstructionLas_BlockEnd6M_pnIBlockEnd__: c1_Instruction.o; +text: .text%__1cIConstantIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cLInstructionMas_CompareOp6M_pnJCompareOp__: c1_GraphBuilder.o; +text: .text%__1cKInstanceOfNas_InstanceOf6M_p0_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderNmethod_return6MpnLInstruction__v_; +text: .text%__1cGReturnFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerJdo_Return6MpnGReturn__v_; +text: .text%__1cIBlockEndLas_BlockEnd6M_p0_: c1_GraphBuilder.o; +text: .text%__1cGReturnJas_Return6M_p0_: c1_GraphBuilder.o; +text: .text%__1cKValueStackbAeliminate_all_scope_stores6Mi_v_; +text: .text%__1cKValueStackQeliminate_stores6Mi_v_; +text: .text%__1cMGraphBuilderKcheck_cast6Mi_v_; +text: .text%__1cJCheckCastFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerMdo_CheckCast6MpnJCheckCast__v_; +text: .text%__1cMGraphBuilderLstore_local6MpnKValueStack_pnLInstruction_pnJValueType_ii_v_; +text: .text%__1cKObjectTypeNas_ObjectType6M_p0_: c1_ValueType.o; +text: .text%__1cJValueTypeOas_AddressType6M_pnLAddressType__: c1_ValueType.o; +text: .text%__1cJLocalSlot2t6M_v_; +text: .text%__1cKStoreLocalFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerNdo_StoreLocal6MpnKStoreLocal__v_; +text: .text%__1cKValueStackLstore_local6MpnKStoreLocal_i_v_; +text: .text%__1cKValueStackQpin_stack_locals6Mi_v_; +text: .text%__1cKObjectTypeEsize6kM_i_: c1_ValueType.o; +text: .text%__1cMGraphBuilderMaccess_field6MnJBytecodesECode__v_; +text: .text%__1cQciBytecodeStreamJget_field6kM_pnHciField__; +text: .text%__1cFciEnvSget_field_by_index6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cPciInstanceKlassLfield_cache6M_pnTciConstantPoolCache__; +text: .text%__1cHciField2t6MpnPciInstanceKlass_i_v_; +text: .text%__1cHciFieldPinitialize_from6MpnPfieldDescriptor__v_; +text: .text%__1cHciFieldJwill_link6MpnPciInstanceKlass_nJBytecodesECode__i_; +text: .text%__1cTconstantPoolOopDescbCklass_ref_at_if_loaded_check6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cKValueStackKcopy_locks6M_p0_; +text: .text%__1cJLoadFieldFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerMdo_LoadField6MpnJLoadField__v_; +text: .text%__1cLAccessFieldOas_AccessField6M_p0_: c1_Instruction.o; +text: .text%__1cJLoadFieldEhash6kM_i_: c1_Instruction.o; +text: .text%__1cJLoadFieldEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cLAccessFieldIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cHIntTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cJValueTypeNas_ObjectType6M_pnKObjectType__: c1_ValueType.o; +text: .text%__1cHIntTypeEsize6kM_i_: c1_ValueType.o; +text: .text%__1cKValueStackVis_same_across_scopes6Mp0_i_; +text: .text%__1cQSystemDictionaryEfind6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cHciKlassGloader6M_pnHoopDesc__: ciTypeArrayKlass.o; +text: .text%__1cIciObjectMis_obj_array6M_i_: ciInstanceKlass.o; +text: .text%__1cMGraphBuilderIstack_op6MnJBytecodesECode__v_; +text: .text%__1cMGraphBuilderNarithmetic_op6MpnJValueType_nJBytecodesECode_pnKValueStack__v_; +text: .text%__1cJValueTypeEmeet6kMp0_1_; +text: .text%__1cHIntTypeEbase6kM_pnJValueType__: c1_ValueType.o; +text: .text%__1cMArithmeticOpIcan_trap6kM_i_; +text: .text%__1cMArithmeticOpFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerPdo_ArithmeticOp6MpnMArithmeticOp__v_; +text: .text%__1cNCanonicalizerGdo_Op26MpnDOp2__v_; +text: .text%__1cLIntConstantLis_constant6kM_i_: c1_Canonicalizer.o; +text: .text%__1cMArithmeticOpEhash6kM_i_: c1_Instruction.o; +text: .text%__1cMArithmeticOpEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cLInstructionNas_InstanceOf6M_pnKInstanceOf__: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderJincrement6M_v_; +text: .text%__1cMGraphBuilderMload_indexed6MnJBasicType__v_; +text: .text%__1cLArrayLengthFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerOdo_ArrayLength6MpnLArrayLength__v_; +text: .text%__1cLInstructionLas_NewArray6M_pnINewArray__: c1_GraphBuilder.o; +text: .text%__1cLArrayLengthEhash6kM_i_: c1_GraphBuilder.o; +text: .text%__1cLArrayLengthEname6kM_pkc_: c1_GraphBuilder.o; +text: .text%__1cLAccessArrayIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cLLoadIndexedFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerOdo_LoadIndexed6MpnLLoadIndexed__v_; +text: .text%__1cLLoadIndexedEhash6kM_i_: c1_Instruction.o; +text: .text%__1cLLoadIndexedEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cLAccessArrayIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cIConstantIis_equal6kMpnLInstruction__i_; +text: .text%__1cIConstantLas_Constant6M_p0_: c1_Instruction.o; +text: .text%__1cHIRScopeMheader_block6MpnKBlockBegin_n0BEFlag__2_; +text: .text%__1cCIRIoptimize6M_v_; +text: .text%__1cJOptimizer2t6MpnCIR__v_; +text: .text%__1cJOptimizerbHeliminate_conditional_expressions6M_v_; +text: .text%__1cCIRQiterate_preorder6MpnMBlockClosure__v_; +text: .text%__1cKBlockBeginQiterate_preorder6MpnMBlockClosure__v_; +text: .text%__1cNCE_EliminatorIblock_do6MpnKBlockBegin__v_: c1_Optimizer.o; +text: .text%__1cLInstructionFas_If6M_pnCIf__: c1_IR.o; +text: .text%__1cLInstructionFas_If6M_pnCIf__: c1_Canonicalizer.o; +text: .text%__1cKBlockBeginQiterate_preorder6MrnJboolArray_pnMBlockClosure__v_; +text: .text%__1cCIfFas_If6M_p0_: c1_Canonicalizer.o; +text: .text%__1cJValueTypeKas_IntType6M_pnHIntType__: c1_ValueType.o; +text: .text%__1cNCE_EliminatorRsimple_value_copy6MpnLInstruction__2_: c1_Optimizer.o; +text: .text%__1cLInstructionLas_Constant6M_pnIConstant__: c1_GraphBuilder.o; +text: .text%__1cJLoadLocalMas_LoadLocal6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLInstructionHas_Goto6M_pnEGoto__: c1_GraphBuilder.o; +text: .text%__1cLInstructionFas_If6M_pnCIf__: c1_GraphBuilder.o; +text: .text%__1cHIntTypeKas_IntType6M_p0_: c1_ValueType.o; +text: .text%__1cLInstructionHas_Goto6M_pnEGoto__: c1_Instruction.o; +text: .text%__1cJOptimizerQeliminate_blocks6M_v_; +text: .text%__1cUGenericGrowableArray2t6MiipnEGrET_i_v_; +text: .text%__1cSPredecessorCounterIblock_do6MpnKBlockBegin__v_: c1_Optimizer.o; +text: .text%__1cLBlockMergerIblock_do6MpnKBlockBegin__v_: c1_Optimizer.o; +text: .text%__1cLBlockMergerJtry_merge6MpnKBlockBegin__i_: c1_Optimizer.o; +text: .text%__1cLInstructionHas_Goto6M_pnEGoto__: c1_IR.o; +text: .text%__1cEGotoHas_Goto6M_p0_: c1_Canonicalizer.o; +text: .text%__1cLInstructionHas_Goto6M_pnEGoto__: c1_Canonicalizer.o; +text: .text%__1cJOptimizerVeliminate_null_checks6M_v_; +text: .text%__1cGBitMapFclear6M_v_; +text: .text%__1cNValueSetArray2t6MkikpnIValueSet__v_: c1_Optimizer.o; +text: .text%__1cTNullCheckEliminatorLiterate_one6MpnKBlockBegin__v_; +text: .text%__1cGBitMapIset_from6M0_v_; +text: .text%__1cKStateSplitPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cKBlockBeginFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorNdo_BlockBegin6MpnKBlockBegin__v_; +text: .text%__1cKStateSplitPinput_values_do6MpFppnLInstruction__v_v_: c1_IR.o; +text: .text%__1cEBaseFvisit6MpnSInstructionVisitor__v_: c1_IR.o; +text: .text%__1cQNullCheckVisitorHdo_Base6MpnEBase__v_; +text: .text%__1cTNullCheckEliminatorPmerge_state_for6MpnKBlockBegin_pnKValueStack_pnIValueSet__i_; +text: .text%__1cKStateSplitPinput_values_do6MpFppnLInstruction__v_v_: c1_Canonicalizer.o; +text: .text%__1cEGotoFvisit6MpnSInstructionVisitor__v_: c1_Canonicalizer.o; +text: .text%__1cQNullCheckVisitorHdo_Goto6MpnEGoto__v_; +text: .text%__1cLInstructionMas_NullCheck6M_pnJNullCheck__: c1_GraphBuilder.o; +text: .text%__1cCIfPinput_values_do6MpFppnLInstruction__v_v_: c1_Canonicalizer.o; +text: .text%__1cTNullCheckEliminatorIdo_value6FppnLInstruction__v_; +text: .text%__1cLAccessLocalPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cFLocalPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cFLocalFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorIdo_Local6MpnFLocal__v_; +text: .text%__1cQNullCheckVisitorMdo_LoadLocal6MpnJLoadLocal__v_; +text: .text%__1cQNullCheckVisitorFdo_If6MpnCIf__v_; +text: .text%__1cLInstructionMas_NullCheck6M_pnJNullCheck__: c1_Instruction.o; +text: .text%__1cGReturnPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cIConstantPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorLdo_Constant6MpnIConstant__v_; +text: .text%__1cQNullCheckVisitorJdo_Return6MpnGReturn__v_; +text: .text%__1cJTypeCheckPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorNdo_InstanceOf6MpnKInstanceOf__v_; +text: .text%__1cQNullCheckVisitorMdo_CheckCast6MpnJCheckCast__v_; +text: .text%__1cKStoreLocalPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorNdo_StoreLocal6MpnKStoreLocal__v_; +text: .text%__1cLAccessFieldPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorMdo_LoadField6MpnJLoadField__v_; +text: .text%__1cTNullCheckEliminatorShandle_AccessField6MpnLAccessField__v_; +text: .text%__1cGBitMapbCset_intersection_with_result6M0_i_; +text: .text%__1cDOp2Pinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorPdo_ArithmeticOp6MpnMArithmeticOp__v_; +text: .text%__1cLAccessArrayPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorOdo_ArrayLength6MpnLArrayLength__v_; +text: .text%__1cTNullCheckEliminatorShandle_ArrayLength6MpnLArrayLength__v_; +text: .text%__1cNAccessIndexedPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorOdo_LoadIndexed6MpnLLoadIndexed__v_; +text: .text%__1cTNullCheckEliminatorShandle_LoadIndexed6MpnLLoadIndexed__v_; +text: .text%__1cCIRTcompute_locals_size6M_v_; +text: .text%__1cHIRScopePallocate_locals6MipnMWordSizeList__i_; +text: .text%__1cHIRScopePargument_locals6M_pnJLocalList__; +text: .text%__1cCIRNcompute_loops6M_v_; +text: .text%__1cIciMethodJhas_loops6kM_i_; +text: .text%__1cNmethodOopDescWcompute_has_loops_flag6M_i_; +text: .text%__1cKLoopFinder2t6MpnCIR_i_v_; +text: .text%__1cSBlockLoopInfoArray2t6MkikpnNBlockLoopInfo__v_: c1_Loops.o; +text: .text%__1cKLoopFinderNcompute_loops6Mi_pnILoopList__; +text: .text%__1cKLoopFinderScompute_dominators6MpnJboolArray__v_; +text: .text%__1cGBitMapGat_put6MIi_v_; +text: .text%__1cRCreateInfoClosureIblock_do6MpnKBlockBegin__v_: c1_Loops.o; +text: .text%__1cPSetPredsClosureIblock_do6MpnKBlockBegin__v_: c1_Loops.o; +text: .text%__1cKLoopFinderSdominator_walk_sux6MpnKBlockBegin_pnJboolArray__v_; +text: .text%__1cGBitMapQset_intersection6M0_v_; +text: .text%__1cGBitMapHis_same6M0_i_; +text: .text%__1cKLoopFinderOfind_backedges6MpnJboolArray__pnILoopList__; +text: .text%__1cKLoopFinderSgather_loop_blocks6MpnILoopList__v_; +text: .text%__1cKLoopFinderKfind_loops6MpnILoopList_i_2_; +text: .text%__1cKScanBlocks2t6MpnJBlockList__v_; +text: .text%__1cIintStack2t6M_v_: c1_ScanBlocks.o; +text: .text%__1cKScanBlocksEscan6MpnKScanResult_i_v_; +text: .text%__1cKScanBlocksKscan_block6MpnKBlockBegin_pnKScanResult_i_v_; +text: .text%__1cLIllegalTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cLInstructionOas_NewInstance6M_pnLNewInstance__: c1_GraphBuilder.o; +text: .text%__1cLInstructionQas_AccessMonitor6M_pnNAccessMonitor__: c1_GraphBuilder.o; +text: .text%__1cLAccessLocalOas_AccessLocal6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLInstructionNas_StoreLocal6M_pnKStoreLocal__: c1_GraphBuilder.o; +text: .text%__1cLInstructionOas_AccessLocal6M_pnLAccessLocal__: c1_Instruction.o; +text: .text%__1cLInstructionKas_Convert6M_pnHConvert__: c1_Instruction.o; +text: .text%__1cLInstructionPas_ArithmeticOp6M_pnMArithmeticOp__: c1_Instruction.o; +text: .text%__1cMArithmeticOpPas_ArithmeticOp6M_p0_: c1_Instruction.o; +text: .text%__1cKStoreLocalNas_StoreLocal6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLInstructionOas_AccessLocal6M_pnLAccessLocal__: c1_GraphBuilder.o; +text: .text%__1cLInstructionKas_Convert6M_pnHConvert__: c1_GraphBuilder.o; +text: .text%__1cLInstructionPas_ArithmeticOp6M_pnMArithmeticOp__: c1_GraphBuilder.o; +text: .text%__1cLInstructionJas_Invoke6M_pnGInvoke__: c1_Canonicalizer.o; +text: .text%__1cLInstructionLas_NewArray6M_pnINewArray__: c1_Canonicalizer.o; +text: .text%__1cLInstructionOas_NewInstance6M_pnLNewInstance__: c1_Canonicalizer.o; +text: .text%__1cLInstructionQas_AccessMonitor6M_pnNAccessMonitor__: c1_Canonicalizer.o; +text: .text%__1cKLoopFinderPfind_loop_exits6MpnKBlockBegin_pnELoop__v_; +text: .text%__1cKLoopFinderNinsert_blocks6MpnILoopList__v_; +text: .text%__1cIintArray2t6Mki1_v_: c1_Loops.o; +text: .text%__1cJBlockListPiterate_forward6MpnMBlockClosure__v_; +text: .text%__1cGTaggerIblock_do6MpnKBlockBegin__v_: c1_Loops.o; +text: .text%__1cNPairCollectorIblock_do6MpnKBlockBegin__v_: c1_Loops.o; +text: .text%__1cNResourceArrayEsort6MIpGpkv2_i_v_; +text: .text%__1cRsort_by_block_ids6FppnJBlockPair_2_i_: c1_Loops.o; +text: .text%__1cKLoopFinderUinsert_caching_block6MpnILoopList_pnKBlockBegin_4_4_; +text: .text%__1cLInstructionQas_CachingChange6M_pnNCachingChange__: c1_GraphBuilder.o; +text: .text%__1cKStateSplitFscope6kM_pnHIRScope__; +text: .text%__1cKLoopFinderJnew_block6MpnHIRScope_i_pnKBlockBegin__; +text: .text%__1cIBlockEndOsubstitute_sux6MpnKBlockBegin_2_v_; +text: .text%__1cCIRMcompute_code6M_v_; +text: .text%__1cCIRWiterate_and_set_weight6kMrnJboolArray_pnKBlockBegin_pnJBlockList_i_v_; +text: .text%__1cKBlockBeginKset_weight6Mi_v_; +text: .text%__1cLInstructionIas_Throw6M_pnFThrow__: c1_IR.o; +text: .text%__1cLInstructionJas_Return6M_pnGReturn__: c1_IR.o; +text: .text%__1cLInstructionIas_Throw6M_pnFThrow__: c1_GraphBuilder.o; +text: .text%__1cDcmp6FppnKBlockBegin_2_i_: c1_IR.o; +text: .text%__1cUSuxAndWeightAdjusterIblock_do6MpnKBlockBegin__v_: c1_IR.o; +text: .text%__1cLInstructionGnegate6Fn0AJCondition__1_; +text: .text%__1cJBlockListJblocks_do6MpFpnKBlockBegin__v_v_; +text: .text%__1cQUseCountComputerRcompute_use_count6FpnKBlockBegin__v_: c1_IR.o; +text: .text%__1cQUseCountComputerQupdate_use_count6FppnLInstruction__v_: c1_IR.o; +text: .text%__1cKStateSplitPstate_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cKValueStackJvalues_do6MpFppnLInstruction__v_v_; +text: .text%__1cNCachingChangePinput_values_do6MpFppnLInstruction__v_v_: c1_Loops.o; +text: .text%__1cLInstructionLas_BlockEnd6M_pnIBlockEnd__: c1_Loops.o; +text: .text%__1cFLocalIas_Local6M_p0_: c1_GraphBuilder.o; +text: .text%__1cIBlockEndLas_BlockEnd6M_p0_: c1_IR.o; +text: .text%__1cLCompilationIemit_lir6M_v_; +text: .text%__1cIFrameMap2t6Mi_v_; +text: .text%__1cLCompilationNinit_framemap6MpnIFrameMap__v_; +text: .text%__1cIFrameMapbCset_local_name_to_offset_map6MpnMWordSizeList__v_; +text: .text%__1cLLIR_Emitter2t6MpnLCompilation__v_; +text: .text%__1cIValueGenOinit_value_gen6F_v_; +text: .text%__1cIRegAlloc2t6M_v_; +text: .text%__1cNc1_AllocTable2t6Mi_v_; +text: .text%__1cNCodeGenerator2t6MpnIValueGen_pnRValueGenInvariant__v_; +text: .text%__1cNCodeGeneratorIblock_do6MpnKBlockBegin__v_; +text: .text%__1cLLIR_EmitterMmust_bailout6kM_i_; +text: .text%__1cLLIR_EmitterLstart_block6MpnKBlockBegin__v_; +text: .text%__1cILIR_List2t6MpnLCompilation__v_; +text: .text%__1cLLIR_EmitterQbind_block_entry6MpnKBlockBegin__v_; +text: .text%__1cILIR_ListSbranch_destination6MpnFLabel__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenMblock_prolog6MpnKBlockBegin__v_; +text: .text%__1cIValueGenHdo_root6MpnLInstruction__v_; +text: .text%__1cLInstructionGas_Phi6M_pnDPhi__: c1_GraphBuilder.o; +text: .text%__1cIValueGenNdo_BlockBegin6MpnKBlockBegin__v_; +text: .text%__1cQDelayedSpillMark2T6M_v_: c1_CodeGenerator.o; +text: .text%__1cLInstructionGas_Phi6M_pnDPhi__: c1_IR.o; +text: .text%__1cIValueGenHdo_Base6MpnEBase__v_; +text: .text%__1cLCompilationNget_init_vars6M_pnIintStack__; +text: .text%__1cLLIR_EmitterJstd_entry6MpnHIRScope_pnIintStack_nFRInfo_5_v_; +text: .text%__1cILIR_ListLalign_entry6M_v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListWunverified_entry_point6MnFRInfo_1_v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListUverified_entry_point6M_v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListLbuild_frame6M_v_: c1_LIREmitter.o; +text: .text%__1cLCompilationVvalue_stack2lir_stack6MpnKValueStack__pnNGrowableArray4CpnLLIR_OprDesc____; +text: .text%__1cNCodeGeneratorPblock_do_epilog6MpnKBlockBegin__v_; +text: .text%__1cLInstructionGas_Phi6M_pnDPhi__: c1_Canonicalizer.o; +text: .text%__1cIValueGenHdo_Goto6MpnEGoto__v_; +text: .text%__1cIValueGenLmove_to_phi6MpnKValueStack_i_i_; +text: .text%__1cIValueGenWgoto_default_successor6MpnIBlockEnd_pnMCodeEmitInfo__v_; +text: .text%__1cIValueGenFdo_If6MpnCIf__v_; +text: .text%__1cIValueGenEwalk6MpnLInstruction__v_; +text: .text%__1cIValueGenMdo_LoadLocal6MpnJLoadLocal__v_; +text: .text%__1cIValueGenJload_item6MpnEItem__v_; +text: .text%__1cEItemGupdate6M_v_; +text: .text%__1cIValueGenSfpu_fanout_handled6MpnEItem__i_; +text: .text%__1cEItemEtype6kM_pnJValueType__: c1_Items.o; +text: .text%__1cIRegAllocMhas_free_reg6kMpnJValueType__i_; +text: .text%__1cNc1_AllocTableMhas_one_free6kM_i_; +text: .text%__1cIRegAllocNget_lock_temp6MpnLInstruction_pnJValueType__nFRInfo__; +text: .text%__1cIRegAllocMget_free_reg6MpnJValueType__nFRInfo__; +text: .text%__1cNc1_AllocTableIget_free6M_i_; +text: .text%__1cJRInfo2RegFdo_it6M_v_: c1_RegAlloc.o; +text: .text%__1cHLockRegGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cNc1_AllocTableKset_locked6Mi_v_; +text: .text%__1cLCompilationIitem2lir6MpknEItem__pnLLIR_OprDesc__; +text: .text%__1cLCompilationKitem2stack6MpknEItem__i_; +text: .text%__1cJValueTypeNas_DoubleType6M_pnKDoubleType__: c1_ValueType.o; +text: .text%__1cMas_BasicType6FpnJValueType__nJBasicType__; +text: .text%__1cJValueTypeMas_ArrayType6M_pnJArrayType__: c1_ValueType.o; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_Compilation.o; +text: .text%__1cLLIR_EmitterEmove6MpnLLIR_OprDesc_nFRInfo__v_; +text: .text%__1cILIR_ListEmove6MpnLLIR_OprDesc_2pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenJitem_free6MpnEItem__v_; +text: .text%__1cIRegAllocPincr_spill_lock6MnFRInfo__v_; +text: .text%__1cQChangeSpillCountGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIValueGenFrfree6MpnEItem__v_; +text: .text%__1cIRegAllocPdecr_spill_lock6MnFRInfo__v_; +text: .text%__1cIRegAllocIfree_reg6MnFRInfo__v_; +text: .text%__1cHFreeRegGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cNc1_AllocTableIset_free6Mi_v_; +text: .text%__1cIValueGenNset_no_result6MpnLInstruction__v_; +text: .text%__1cLLIR_EmitterFif_op6MinLInstructionJCondition_pnLLIR_OprDesc_4pnKBlockBegin_66pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListDcmp6MnMLIR_OpBranchNLIR_Condition_pnLLIR_OprDesc_4pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListGbranch6MnMLIR_OpBranchNLIR_Condition_pnKBlockBegin__v_; +text: .text%__1cIValueGenNdo_InstanceOf6MpnKInstanceOf__v_; +text: .text%__1cIValueGenVspill_values_on_stack6MpnKValueStack_nFRInfo_i_v_; +text: .text%__1cIValueGenWrlock_result_with_hint6MpnLInstruction_pknEItem__nFRInfo__; +text: .text%__1cIValueGenFrlock6MpnLInstruction_pknEItem__nFRInfo__; +text: .text%__1cIRegAllocMget_lock_reg6MpnLInstruction_pnJValueType__nFRInfo__; +text: .text%__1cLLIR_EmitterNinstanceof_op6MpnLLIR_OprDesc_2pnHciKlass_nFRInfo_5ipnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListKinstanceof6MpnLLIR_OprDesc_2pnHciKlass_22ipnMCodeEmitInfo__v_; +text: .text%__1cIRegAllocHset_reg6MnFRInfo_ipnLInstruction__v_; +text: .text%__1cGSetRegGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cEItemNset_from_item6Mpk0_v_: c1_Items.o; +text: .text%__1cIValueGenLdo_Constant6MpnIConstant__v_; +text: .text%__1cJValueTypeRas_ObjectConstant6M_pnOObjectConstant__: c1_Canonicalizer.o; +text: .text%__1cIValueGenUcheck_float_register6MpnEItem__v_; +text: .text%__1cEItemRget_jint_constant6kM_i_; +text: .text%__1cIValueGenOdont_load_item6MpnEItem__v_; +text: .text%__1cIRegAllocLis_free_reg6kMnFRInfo__i_; +text: .text%__1cJIsFreeRegGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cNc1_AllocTableHis_free6kMi_i_; +text: .text%__1cLLIR_OprFactKvalue_type6FpnJValueType__pnLLIR_OprDesc__; +text: .text%__1cJLIR_ConstEtype6kM_nJBasicType__: c1_CacheLocals.o; +text: .text%__1cJLIR_ConstLas_constant6M_p0_: c1_CacheLocals.o; +text: .text%__1cIValueGenMdo_CheckCast6MpnJCheckCast__v_; +text: .text%__1cMCodeEmitInfo2t6MpnLLIR_Emitter_ipnIintStack_pnKValueStack_pnOExceptionScope_pnPRInfoCollection__v_; +text: .text%__1cKValueStackMcaller_state6kM_p0_; +text: .text%__1cILIR_ListJcheckcast6MpnLLIR_OprDesc_2pnHciKlass_22ipnMCodeEmitInfo_6pnICodeStub__v_; +text: .text%__1cIValueGenNdo_StoreLocal6MpnKStoreLocal__v_; +text: .text%__1cEItemRhandle_float_kind6M_v_; +text: .text%__1cIValueGenXcan_inline_any_constant6kM_i_; +text: .text%__1cLLIR_EmitterJopr2local6MipnLLIR_OprDesc__v_; +text: .text%__1cILIR_ListQreg2single_stack6MnFRInfo_inJBasicType__v_: c1_LIREmitter.o; +text: .text%__1cLInstructionGas_Phi6M_pnDPhi__: c1_Instruction.o; +text: .text%__1cIValueGenMdo_LoadField6MpnJLoadField__v_; +text: .text%__1cLAccessFieldKlock_stack6kM_pnKValueStack__: c1_Instruction.o; +text: .text%__1cLLIR_EmitterKfield_load6MnFRInfo_pnHciField_pnLLIR_OprDesc_iiipnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListMload_mem_reg6MnFRInfo_i1nJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cIRegAllocNoops_in_spill6kM_pnIintStack__; +text: .text%__1cEItemEtype6kM_pnJValueType__: c1_CodeGenerator.o; +text: .text%__1cJArrayTypeMas_ArrayType6M_p0_: c1_ValueType.o; +text: .text%__1cLInstructionGas_Phi6M_pnDPhi__: c1_Loops.o; +text: .text%__1cNCachingChangeFvisit6MpnSInstructionVisitor__v_: c1_Loops.o; +text: .text%__1cIValueGenQdo_CachingChange6MpnNCachingChange__v_; +text: .text%__1cIValueGenPdo_ArithmeticOp6MpnMArithmeticOp__v_; +text: .text%__1cIValueGenTdo_ArithmeticOp_Int6MpnMArithmeticOp__v_; +text: .text%__1cIRegAllocRoops_in_registers6kM_pnPRInfoCollection__; +text: .text%__1cLLIR_EmitterRarithmetic_op_int6MnJBytecodesECode_pnLLIR_OprDesc_44nFRInfo__v_; +text: .text%__1cLLIR_EmitterNarithmetic_op6MnJBytecodesECode_pnLLIR_OprDesc_44inFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListDsub6MpnLLIR_OprDesc_22pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenWcan_inline_as_constant6MpnEItem__i_; +text: .text%__1cILIR_ListDadd6MpnLLIR_OprDesc_22_v_: c1_LIREmitter.o; +text: .text%__1cIValueGenOdo_ArrayLength6MpnLArrayLength__v_; +text: .text%__1cLAccessArrayKlock_stack6kM_pnKValueStack__: c1_GraphBuilder.o; +text: .text%__1cLLIR_EmitterMarray_length6MnFRInfo_pnLLIR_OprDesc_pnMCodeEmitInfo__v_; +text: .text%__1cIValueGenOdo_LoadIndexed6MpnLLoadIndexed__v_; +text: .text%__1cJValueTypeLas_LongType6M_pnILongType__: c1_ValueType.o; +text: .text%__1cLAccessArrayKlock_stack6kM_pnKValueStack__: c1_Instruction.o; +text: .text%__1cLLIR_EmitterSlength_range_check6MpnLLIR_OprDesc_2pnMCodeEmitInfo__v_; +text: .text%__1cORangeCheckStub2t6MpnMCodeEmitInfo_nFRInfo_ii_v_; +text: .text%__1cMCodeEmitInfo2t6Mp0i_v_; +text: .text%__1cILIR_ListGbranch6MnMLIR_OpBranchNLIR_Condition_pnICodeStub__v_; +text: .text%__1cLLIR_EmitterMindexed_load6MnFRInfo_nJBasicType_pnLLIR_OprDesc_4pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListKshift_left6MnFRInfo_i1_v_: c1_LIREmitter_sparc.o; +text: .text%__1cILIR_ListKshift_left6MpnLLIR_OprDesc_222_v_; +text: .text%__1cLCompilationXlir_opr_for_instruction6MpnLInstruction__pnLLIR_OprDesc__; +text: .text%__1cIValueGenPlock_free_rinfo6MpnLInstruction_pnJValueType__nFRInfo__; +text: .text%__1cILIR_ListJsafepoint6MnFRInfo_pnMCodeEmitInfo__v_: c1_CodeGenerator_sparc.o; +text: .text%__1cLLIR_EmitterHgoto_op6MpnKBlockBegin_pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListEjump6MpnKBlockBegin_pnMCodeEmitInfo__v_; +text: .text%__1cIValueGenJdo_Return6MpnGReturn__v_; +text: .text%__1cJValueTypeLas_VoidType6M_pnIVoidType__: c1_Canonicalizer.o; +text: .text%__1cIValueGenTresult_register_for6FpnJValueType_i_nFRInfo__; +text: .text%__1cIValueGenTcallee_return1RInfo6F_nFRInfo__; +text: .text%__1cIValueGenPload_item_force6MpnEItem_nFRInfo__v_; +text: .text%__1cIRegAllocJlock_temp6MpnLInstruction_nFRInfo__v_; +text: .text%__1cILIR_ListHint2reg6MinFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cLLIR_EmitterJreturn_op6MpnLLIR_OprDesc__v_; +text: .text%__1cILIR_ListJreturn_op6MpnLLIR_OprDesc__v_: c1_LIREmitter.o; +text: .text%__1cNCodeGeneratorXclear_instruction_items6FpnKBlockBegin__v_; +text: .text%__1cQLIR_LocalCaching2t6MpnCIR__v_; +text: .text%__1cQLIR_LocalCachingQpreferred_locals6MpknIciMethod__pnMLocalMapping__; +text: .text%__1cIFrameMapScalling_convention6FpknIciMethod_pnIintArray__pnRCallingConvention__; +text: .text%__1cIFrameMapScalling_convention6FirknOBasicTypeArray_pnIintArray__pnRCallingConvention__; +text: .text%__1cIintArray2t6Mki1_v_: c1_FrameMap_sparc.o; +text: .text%__1cQArgumentLocation2t6Mci_v_: c1_FrameMap_sparc.o; +text: .text%__1cQArgumentLocationVincoming_reg_location6kM_nFRInfo__; +text: .text%__1cQArgumentLocationPis_register_arg6kM_i_; +text: .text%__1cMLocalMappingQinit_cached_regs6M_v_; +text: .text%__1cPRegisterManager2t6M_v_; +text: .text%__1cPRegisterManagerElock6MnFRInfo__v_; +text: .text%__1cQLIR_LocalCachingVcompute_cached_locals6M_v_; +text: .text%__1cQLIR_LocalCachingMcache_locals6M_v_; +text: .text%__1cLInstructionQas_CachingChange6M_pnNCachingChange__: c1_IR.o; +text: .text%__1cLInstructionQas_CachingChange6M_pnNCachingChange__: c1_Canonicalizer.o; +text: .text%__1cNCachingChangeQas_CachingChange6M_p0_: c1_Loops.o; +text: .text%__1cLInstructionQas_CachingChange6M_pnNCachingChange__: c1_Instruction.o; +text: .text%__1cRBlockListScanInfo2t6MpnJBlockList__v_: c1_CacheLocals.o; +text: .text%__1cOLIR_OprRefList2t6M_v_: c1_CacheLocals.o; +text: .text%__1cRBlockListScanInfoItraverse6MpnKBlockBegin_pnKLIR_OpList__v_: c1_CacheLocals.o; +text: .text%__1cLLIR_OpLabelFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cHLIR_Op1Fvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cHLIR_Op2Fvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cMLIR_OpBranchFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cORangeCheckStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_sparc.o; +text: .text%__1cQLIR_OpVisitStateGappend6MnFRInfo__v_: c1_CodeStubs_sparc.o; +text: .text%__1cNc1_AllocTableFmerge6Mp0_v_; +text: .text%__1cGLIR_OpFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cPLIR_OpTypeCheckFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cTSimpleExceptionStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_sparc.o; +text: .text%__1cQLIR_LocalCachingXcache_locals_for_blocks6MpnJBlockList_pnPRegisterManager_i_pnMLocalMapping__; +text: .text%__1cLInstructionNas_StateSplit6M_pnKStateSplit__: c1_Loops.o; +text: .text%__1cLInstructionOas_AccessField6M_pnLAccessField__: c1_Loops.o; +text: .text%__1cLInstructionOas_AccessLocal6M_pnLAccessLocal__: c1_Loops.o; +text: .text%__1cLInstructionKas_Convert6M_pnHConvert__: c1_Loops.o; +text: .text%__1cLInstructionPas_ArithmeticOp6M_pnMArithmeticOp__: c1_Loops.o; +text: .text%__1cKStateSplitNas_StateSplit6M_p0_: c1_IR.o; +text: .text%__1cLInstructionJas_Invoke6M_pnGInvoke__: c1_IR.o; +text: .text%__1cLInstructionLas_NewArray6M_pnINewArray__: c1_IR.o; +text: .text%__1cLInstructionOas_NewInstance6M_pnLNewInstance__: c1_IR.o; +text: .text%__1cLInstructionQas_AccessMonitor6M_pnNAccessMonitor__: c1_IR.o; +text: .text%__1cLInstructionMas_Intrinsic6M_pnJIntrinsic__: c1_IR.o; +text: .text%__1cKScanBlocksQmost_used_locals6M_pnKALocalList__; +text: .text%__1cGALocalUsort_by_access_count6Fpp02_i_: c1_ScanBlocks.o; +text: .text%__1cKScanBlocksWmost_used_float_locals6M_pnKALocalList__; +text: .text%__1cGALocalUsort_by_access_count6Fpp02_i_: c1_CacheLocals.o; +text: .text%__1cQLIR_LocalCachingPcompute_caching6MpnKALocalList_pnPRegisterManager__pnMLocalMapping__; +text: .text%__1cPRegisterManagerMnum_free_cpu6M_i_; +text: .text%__1cGALocalNsort_by_index6Fpp02_i_: c1_CacheLocals.o; +text: .text%__1cMLocalMappingNget_cache_reg6kMinIValueTag__nFRInfo__; +text: .text%__1cPRegisterManagerLis_free_reg6MnFRInfo__i_; +text: .text%__1cQLIR_LocalCachingQadd_at_all_names6FpnPRInfoCollection_inFRInfo_pnMWordSizeList__v_; +text: .text%__1cPRegisterManagerMhas_free_reg6MnIValueTag__i_; +text: .text%__1cPRegisterManagerNlock_free_reg6MnIValueTag__nFRInfo__; +text: .text%__1cMLocalMappingFmerge6Mp0_v_; +text: .text%__1cSLocalMappingSetterIblock_do6MpnKBlockBegin__v_; +text: .text%__1cMLocalMappingNget_cache_reg6kMi_nFRInfo__; +text: .text%__1cMLocalMappingEjoin6Mp0_v_; +text: .text%__1cQLIR_LocalCachingYinsert_transition_blocks6M_v_; +text: .text%__1cPBlockTransitionIblock_do6MpnKBlockBegin__v_: c1_CacheLocals.o; +text: .text%__1cGLIR_OpLas_OpBranch6M_pnMLIR_OpBranch__: c1_LIR.o; +text: .text%__1cMLocalMappingPemit_transition6FpnILIR_List_p03pnCIR__v_; +text: .text%__1cCIRThighest_used_offset6kM_i_; +text: .text%__1cMLIR_OpBranchLas_OpBranch6M_p0_: c1_LIR.o; +text: .text%__1cNResourceArrayJremove_at6MIi_v_; +text: .text%__1cHIRScopeJmax_stack6kM_i_; +text: .text%__1cNLIR_Optimizer2t6MpnCIR__v_; +text: .text%__1cRLIR_PeepholeState2t6M_v_; +text: .text%__1cOLIR_OprRefList2t6M_v_: c1_LIROptimizer.o; +text: .text%__1cNLIR_OptimizerIoptimize6M_v_; +text: .text%__1cNLIR_OptimizerIoptimize6MpnJBlockList__v_; +text: .text%__1cNLIR_OptimizerIoptimize6MpnKBlockBegin__v_; +text: .text%__1cNLIR_OptimizerKprocess_op6M_v_; +text: .text%__1cGLIR_OpGas_Op16M_pnHLIR_Op1__: c1_LIR.o; +text: .text%__1cLLIR_OpLabelKas_OpLabel6M_p0_: c1_LIR.o; +text: .text%__1cRLIR_PeepholeStateYset_disable_optimization6Mi_v_; +text: .text%__1cLLIR_OpLabelJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerMemit_opLabel6MpnLLIR_OpLabel__v_; +text: .text%__1cHLIR_Op0Jemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerIemit_op06MpnHLIR_Op0__v_; +text: .text%__1cHLIR_Op2Jemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerIemit_op26MpnHLIR_Op2__v_; +text: .text%__1cNLIR_OptimizerKhandle_opr6MpnLLIR_OprDesc_nQLIR_OpVisitStateHOprMode__2_; +text: .text%__1cNLIR_OptimizerJis_cached6MpnLLIR_OprDesc__i_; +text: .text%__1cRLIR_PeepholeStateUrecord_opr_reference6MpnLLIR_OprDesc__i_; +text: .text%__1cRLIR_PeepholeStateLdefining_op6MpnLLIR_OprDesc__i_; +text: .text%__1cRLIR_PeepholeStateJreg2index6MpnLLIR_OprDesc__i_; +text: .text%__1cNLIR_OptimizerMblock_epilog6M_v_; +text: .text%__1cHLIR_Op1Gas_Op16M_p0_: c1_LIR.o; +text: .text%__1cHLIR_Op1Jemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerIemit_op16MpnHLIR_Op1__v_; +text: .text%__1cNLIR_OptimizerMprocess_move6MpnHLIR_Op1__v_; +text: .text%__1cMLocalMappingNget_cache_reg6kMpnLLIR_OprDesc__2_; +text: .text%__1cNLIR_OptimizerRreplace_stack_opr6MpnLLIR_OprDesc__2_; +text: .text%__1cNLIR_OptimizerNoptimize_move6MpnHLIR_Op1_rpnLLIR_OprDesc_5_i_; +text: .text%__1cRLIR_PeepholeStatebFequivalent_register_or_constant6MpnLLIR_OprDesc__2_; +text: .text%__1cRLIR_PeepholeStateOequivalent_opr6MpnLLIR_OprDesc__2_; +text: .text%__1cNLIR_OptimizerRresult_substitute6M_v_; +text: .text%__1cRLIR_PeepholeStateMkill_operand6MpnLLIR_OprDesc_i_v_; +text: .text%__1cRLIR_PeepholeStateQkill_equivalents6MpnLLIR_OprDesc__v_; +text: .text%__1cRLIR_PeepholeStateNkill_register6Mi_v_; +text: .text%__1cRLIR_PeepholeStateSrecord_defining_op6MpnLLIR_OprDesc_i_v_; +text: .text%__1cRLIR_PeepholeStatePset_defining_op6Mii_v_; +text: .text%__1cRLIR_PeepholeStateHdo_move6MpnLLIR_OprDesc_2_v_; +text: .text%__1cRLIR_PeepholeStateTequivalent_register6MpnLLIR_OprDesc__2_; +text: .text%__1cNLIR_OptimizerKmaybe_opto6MpnLLIR_OprDesc_2_2_: c1_LIROptimizer_sparc.o; +text: .text%__1cNLIR_OptimizerMis_cache_reg6MpnLLIR_OprDesc__i_; +text: .text%__1cMLocalMappingMis_cache_reg6kMpnLLIR_OprDesc__i_; +text: .text%__1cMLocalMappingMis_cache_reg6kMnFRInfo__i_; +text: .text%__1cFRInfoLas_register6kM_pnMRegisterImpl__; +text: .text%__1cNLIR_OptimizerKallow_opto6M_i_; +text: .text%__1cNLIR_OptimizerLrecord_opto6MpnLLIR_OprDesc_2_2_; +text: .text%__1cMLIR_OpBranchJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerNemit_opBranch6MpnMLIR_OpBranch__v_; +text: .text%__1cNLIR_OptimizerMselect_delay6MpnMCodeEmitInfo__pnGLIR_Op__; +text: .text%__1cNLIR_OptimizerFop_at6Mi_pnGLIR_Op__; +text: .text%__1cNLIR_AssemblerVis_single_instruction6FpnGLIR_Op_pnIFrameMap__i_; +text: .text%__1cNLIR_OptimizerMdelayed_emit6MpnGLIR_Op__v_; +text: .text%__1cLLIR_OpDelayJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerKemit_delay6MpnLLIR_OpDelay__v_; +text: .text%__1cNLIR_OptimizerQopr_live_on_exit6MpnLLIR_OprDesc__i_; +text: .text%__1cPLIR_OpTypeCheckJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerQemit_opTypeCheck6MpnPLIR_OpTypeCheck__v_; +text: .text%__1cNLIR_OptimizerFvisit6M_v_: c1_LIROptimizer_sparc.o; +text: .text%__1cKLIR_OprPtrKas_address6M_pnLLIR_Address__: c1_CacheLocals.o; +text: .text%__1cNLIR_AssemblerRis_small_constant6FpnLLIR_OprDesc__i_; +text: .text%__1cNLIR_OptimizerOemit_code_stub6MpnICodeStub__v_; +text: .text%__1cNLIR_OptimizerLhandle_info6MpnMCodeEmitInfo__v_; +text: .text%__1cRLIR_PeepholeStateNincrement_ref6Mi_v_; +text: .text%__1cMCodeEmitInfoRset_local_mapping6MpnMLocalMapping__v_; +text: .text%__1cNLIR_OptimizerUrecord_register_oops6MpnMCodeEmitInfo__v_; +text: .text%__1cLLIR_AddressKas_address6M_p0_: c1_LIR.o; +text: .text%__1cRLIR_PeepholeStateSequivalent_address6MpnLLIR_OprDesc__2_; +text: .text%__1cLLIR_AddressEtype6kM_nJBasicType__: c1_LIR.o; +text: .text%__1cKLIR_OprPtrLas_constant6M_pnJLIR_Const__: c1_LIR.o; +text: .text%__1cORangeCheckStubXis_exception_throw_stub6kM_i_: c1_CodeStubs_sparc.o; +text: .text%__1cLLIR_OpDelayKas_OpDelay6M_p0_: c1_LIR.o; +text: .text%__1cNLIR_OptimizerKreplace_op6MipnGLIR_Op__v_; +text: .text%__1cNLIR_Assembler2t6MpnLCompilation_pnLCodeOffsets__v_; +text: .text%__1cNLIR_AssemblerJemit_code6MpnJBlockList__v_; +text: .text%__1cQCollectConstantsIblock_do6MpnKBlockBegin__v_: c1_LIRAssembler.o; +text: .text%__1cLInstructionLas_Constant6M_pnIConstant__: c1_IR.o; +text: .text%__1cLInstructionLas_Constant6M_pnIConstant__: c1_Canonicalizer.o; +text: .text%__1cJValueTypeNas_DoubleType6M_pnKDoubleType__: c1_Canonicalizer.o; +text: .text%__1cJValueTypeMas_FloatType6M_pnJFloatType__: c1_Canonicalizer.o; +text: .text%__1cLInstructionLas_Constant6M_pnIConstant__: c1_Instruction.o; +text: .text%__1cLInstructionLas_Constant6M_pnIConstant__: c1_Loops.o; +text: .text%__1cNLIR_AssemblerOemit_constants6M_v_; +text: .text%__1cNConstantTableMemit_entries6MpnOMacroAssembler_i_v_; +text: .text%__1cLLIR_CodeGenIblock_do6MpnKBlockBegin__v_; +text: .text%__1cNLIR_AssemblerMemit_opLabel6MpnLLIR_OpLabel__v_; +text: .text%__1cNLIR_AssemblerIemit_op06MpnHLIR_Op0__v_; +text: .text%__1cNLIR_AssemblerIemit_op26MpnHLIR_Op2__v_; +text: .text%__1cNLIR_AssemblerMcheck_icache6MpnMRegisterImpl_2_i_; +text: .text%__1cRC1_MacroAssemblerSinline_cache_check6MpnMRegisterImpl_2_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: c1_MacroAssembler_sparc.o; +text: .text%__1cOMacroAssemblerIjumpl_to6MrnHAddress_pnMRegisterImpl_i_v_: c1_MacroAssembler_sparc.o; +text: .text%__1cRC1_MacroAssemblerOverified_entry6M_v_; +text: .text%__1cNLIR_AssemblerbBinitial_frame_size_in_bytes6M_i_; +text: .text%__1cIFrameMapJframesize6kM_i_; +text: .text%__1cRC1_MacroAssemblerLbuild_frame6Mi_v_; +text: .text%__1cRAbstractAssemblerbDgenerate_stack_overflow_check6Mi_v_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: c1_Compiler.o; +text: .text%__1cNLIR_AssemblerVsetup_locals_at_entry6M_v_; +text: .text%__1cIFrameMapYsignature_type_array_for6FpknIciMethod__pnNBasicTypeList__; +text: .text%__1cNLIR_AssemblerMsetup_locals6MpnRCallingConvention_pnOBasicTypeArray__v_; +text: .text%__1cRDependenceBreakerPget_store_order6MnFRInfo__pnSDependenceEdgeList__: c1_LIRAssembler_sparc.o; +text: .text%__1cNLIR_AssemblerHreg2reg6MnFRInfo_1_v_; +text: .text%__1cQArgumentLocationMis_stack_arg6kM_i_; +text: .text%__1cNLIR_AssemblerHcomp_op6MnMLIR_OpBranchNLIR_Condition_pnLLIR_OprDesc_4nJBasicType__v_; +text: .text%__1cNLIR_AssemblerNemit_opBranch6MpnMLIR_OpBranch__v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: c1_LIRAssembler_sparc.o; +text: .text%__1cNLIR_AssemblerKemit_delay6MpnLLIR_OpDelay__v_; +text: .text%__1cNLIR_AssemblerLcode_offset6kM_i_; +text: .text%__1cNLIR_AssemblerQemit_opTypeCheck6MpnPLIR_OpTypeCheck__v_; +text: .text%__1cIciObjectIencoding6M_pnI_jobject__; +text: .text%__1cLOopRecorderKfind_index6MpnI_jobject__i_; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: c1_LIRAssembler_sparc.o; +text: .text%__1cOoop_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cOoop_RelocationJpack_data6M_i_; +text: .text%__1cNLIR_AssemblerEload6MpnMRegisterImpl_i2nJBasicType_pnMCodeEmitInfo__i_; +text: .text%__1cNLIR_AssemblerIemit_op16MpnHLIR_Op1__v_; +text: .text%__1cNLIR_AssemblerHmove_op6MpnLLIR_OprDesc_2nJBasicType_nHLIR_Op1NLIR_PatchCode_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerOemit_code_stub6MpnICodeStub__v_; +text: .text%__1cTSimpleExceptionStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cICodeStubLset_code_pc6MpC_v_: c1_CodeStubs_sparc.o; +text: .text%__1cICodeStubMis_call_stub6kM_i_: c1_CodeStubs_sparc.o; +text: .text%__1cNLIR_AssemblerHmem2reg6MpnLLIR_Address_nFRInfo_nJBasicType_nHLIR_Op1NLIR_PatchCode_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerbDadd_debug_info_for_null_check6MipnMCodeEmitInfo__v_; +text: .text%__1cVImplicitNullCheckStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cNLIR_AssemblerIarith_op6MnILIR_Code_pnLLIR_OprDesc_33pnMCodeEmitInfo__v_; +text: .text%__1cORangeCheckStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cNLIR_AssemblerIshift_op6MnILIR_Code_nFRInfo_i2_v_; +text: .text%__1cNLIR_AssemblerOsafepoint_poll6MnFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerZadd_debug_info_for_branch6MpnMCodeEmitInfo__v_; +text: .text%__1cPpoll_RelocationEtype6M_nJrelocInfoJrelocType__: c1_Runtime1.o; +text: .text%__1cMCodeEmitInfoRrecord_debug_info6MpnYDebugInformationRecorder_ii_v_; +text: .text%__1cMCodeEmitInfoScompute_debug_info6M_v_; +text: .text%__1cMCodeEmitInfoOcreate_oop_map6M_pnGOopMap__; +text: .text%__1cIFrameMapRoop_map_arg_count6M_i_; +text: .text%__1cKciLocalMapNindex_for_bci6kMi_i_; +text: .text%__1cGOopMapHset_oop6MnHOptoRegEName_ii_v_; +text: .text%__1cMCodeEmitInfoSappend_scope_value6MpnLLIR_OprDesc_pnNGrowableArray4CpnKScopeValue____v_; +text: .text%__1cLLIR_OprDescGis_oop6kM_i_; +text: .text%__1cMCodeEmitInfoYscope_value_for_register6MnFRInfo_ppnKScopeValue_4nILocationEType__v_; +text: .text%__1cMCodeEmitInfobCcompute_debug_info_for_scope6MpnHIRScope_ipnNGrowableArray4CpnKScopeValue___inGValues_i_pnQIRScopeDebugInfo__; +text: .text%__1cSciGenerateLocalMapUbytecode_is_gc_point6FnJBytecodesECode_ii_i_; +text: .text%__1cMLocalMappingbEget_cache_reg_for_local_offset6kMi_nFRInfo__; +text: .text%__1cGOopMapJdeep_copy6M_p0_; +text: .text%__1cGOopMap2t6Mn0ANDeepCopyToken_p0_v_; +text: .text%__1cYDebugInformationRecorderNadd_safepoint6MiipnGOopMap__v_; +text: .text%__1cYDebugInformationRecorderTcreate_scope_values6MpnNGrowableArray4CpnKScopeValue____pnKDebugToken__; +text: .text%__1cNLocationValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cILocationIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cYDebugInformationRecorderVcreate_monitor_values6MpnNGrowableArray4CpnMMonitorValue____pnKDebugToken__; +text: .text%__1cYDebugInformationRecorderOdescribe_scope6MpnIciMethod_ipnKDebugToken_44_v_; +text: .text%__1cLOopRecorderOallocate_index6MpnI_jobject__i_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: debugInfoRec.o; +text: .text%__1cLCompilationbEadd_exception_handlers_for_pco6MiipnOExceptionScope__v_; +text: .text%__1cNLIR_AssemblerJconst2reg6MpnJLIR_Const_nFRInfo_nHLIR_Op1NLIR_PatchCode_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerJreturn_op6MnFRInfo_i_v_; +text: .text%__1cWpoll_return_RelocationEtype6M_nJrelocInfoJrelocType__: c1_Runtime1.o; +text: .text%__1cRC1_MacroAssemblerLmethod_exit6Mi_v_; +text: .text%__1cRreturn_RelocationEtype6M_nJrelocInfoJrelocType__: c1_Runtime1.o; +text: .text%__1cLCompilationQemit_code_epilog6MpnNLIR_Assembler__v_; +text: .text%__1cNLIR_AssemblerUemit_slow_case_stubs6M_v_; +text: .text%__1cTSimpleExceptionStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cNLIR_AssemblerNadd_call_info6MipnMCodeEmitInfo__v_; +text: .text%__1cVImplicitNullCheckStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cWImplicitExceptionTableGappend6MII_v_; +text: .text%__1cZresource_reallocate_bytes6FpcII_0_; +text: .text%__1cFArenaIArealloc6MpvII_1_; +text: .text%__1cQConstantIntValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cORangeCheckStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cNLIR_AssemblerWemit_exception_handler6M_i_; +text: .text%__1cRC1_MacroAssemblerRexception_handler6Mii_v_; +text: .text%__1cHnmethodZsize_of_exception_handler6F_i_; +text: .text%__1cNLIR_AssemblerPemit_call_stubs6M_v_; +text: .text%__1cNLIR_AssemblerbQinterpreter_to_compiler_calling_convention6MpnIciMethod__v_; +text: .text%__1cQArgumentLocationVoutgoing_reg_location6kM_nFRInfo__; +text: .text%__1cNLIR_AssemblerKemit_stubs6MpnMCodeStubList__v_; +text: .text%__1cLCompilationbEgenerate_exception_range_table6M_v_; +text: .text%__1cOExceptionScopeGequals6kMp0_i_; +text: .text%__1cLCompilationbBadd_exception_range_entries6MiipnOExceptionScope_ip2pi_v_; +text: .text%__1cTExceptionRangeTablebCcompute_modified_at_call_pco6Fii_i_; +text: .text%__1cOExceptionScopeGlength6kM_i_; +text: .text%__1cOExceptionScopeMcaller_scope6kM_p0_; +text: .text%__1cLLIR_EmitterKframe_size6M_i_; +text: .text%__1cNLIR_Assembler2T6M_v_; +text: .text%__1cFciEnvPregister_method6MpnIciMethod_iiiiiipnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler_ii_v_; +text: .text%__1cFciEnvbOcheck_for_system_dictionary_modification6MpnIciMethod__v_; +text: .text%__1cHnmethodLnew_nmethod6FnMmethodHandle_iiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__p0_; +text: .text%__1cLOopRecorderIoop_size6M_i_; +text: .text%__1cYDebugInformationRecorderIpcs_size6M_i_; +text: .text%__1cYDebugInformationRecorderJdata_size6M_i_; +text: .text%__1cHnmethod2t6MpnNmethodOopDesc_iiiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__v_; +text: .text%__1cNRelocIteratorTadvance_over_prefix6M_v_; +text: .text%__1cOoop_RelocationLunpack_data6M_v_; +text: .text%__1cKRelocationNunpack_2_ints6Mri1_v_: relocInfo.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: c1_Runtime1.o; +text: .text%__1cYDebugInformationRecorderHcopy_to6MpnHnmethod__v_; +text: .text%__1cLOopRecorderHcopy_to6MpnICodeBlob__v_; +text: .text%__1cICodeBlobJcopy_oops6MppnI_jobject_i_v_; +text: .text%__1cIUniverseMnon_oop_word6F_pv_; +text: .text%__1cCosXnon_memory_address_word6F_pc_; +text: .text%__1cHnmethodQcopy_scopes_data6MpCi_v_; +text: .text%__1cGPcDesc2t6Miii_v_; +text: .text%__1cHnmethodKcopy_pc_at6MipnGPcDesc__v_; +text: .text%__1cHnmethodSresolve_JNIHandles6M_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: nmethod.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nmethod.o; +text: .text%__1cOoop_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cICodeBlobLoop_addr_at6kMi_ppnHoopDesc__; +text: .text%__1cODataRelocationJset_value6MpC_v_: relocInfo.o; +text: .text%__1cOoop_RelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cKRelocationRpd_set_data_value6MpCi_v_; +text: .text%__1cJCodeCacheGcommit6FpnICodeBlob__v_; +text: .text%__1cHnmethodKis_nmethod6kM_i_: nmethod.o; +text: .text%__1cHnmethodUnumber_of_dependents6kM_i_: nmethod.o; +text: .text%__1cFVTuneOcreate_nmethod6FpnHnmethod__v_; +text: .text%__1cWImplicitExceptionTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cTExceptionRangeTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cGEventsDlog6FpkcE_v_: nmethod.o; +text: .text%__1cNmethodOopDescIset_code6MpnHnmethod__v_; +text: .text%__1cLCompilation2T6M_v_; +text: .text%__1cFArena2T6M_v_; +text: .text%__1cICHeapObj2k6Fpv_v_; +text: .text%__1cTExceptionRangeTable2T6M_v_; +text: .text%__1cFciEnvVnum_inlined_bytecodes6kM_i_; +text: .text%__1cFciEnv2T6M_v_; +text: .text%__1cNCompileBrokerScollect_statistics6FpnOCompilerThread_nMelapsedTimer_pnLCompileTask__v_; +text: .text%__1cHnmethodKtotal_size6kM_i_; +text: .text%__1cHnmethodJcode_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodOexception_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodJstub_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodQscopes_data_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodPscopes_pcs_size6kM_i_: nmethod.o; +text: .text%__1cLAccessFlagsRatomic_clear_bits6Mi_v_; +text: .text%__1cKJNIHandlesOdestroy_global6FpnI_jobject_i_v_; +text: .text%jni_NewByteArray: jni.o; +text: .text%jni_SetByteArrayRegion: jni.o; +text: .text%__1cSInterpreterRuntimeMmonitorenter6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cNObjectMonitorFenter6MpnGThread__v_; +text: .text%jni_NewObject: jni.o; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcipnNsymbolOopDesc_4_v_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_3pnRJavaCallArguments_nGHandle_6_6_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%JVM_FillInStackTrace; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle__v_; +text: .text%__1cIUniverseWis_out_of_memory_error6FnGHandle__i_; +text: .text%__1cVPreserveExceptionMark2t6MrpnGThread__v_; +text: .text%__1cKJavaThreadGactive6F_p0_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle_pnGThread__v_; +text: .text%__1cVPreserveExceptionMark2T6M_v_; +text: .text%__1cGThreadVset_pending_exception6MpnHoopDesc_pkci_v_; +text: .text%__1cGEventsDlog6FpkcE_v_: exceptions.o; +text: .text%__1cSInterpreterRuntimeXthrow_pending_exception6FpnKJavaThread__v_; +text: .text%__1cSThreadLocalStorageGthread6F_pnGThread__: assembler_sparc.o; +text: .text%__1cNSharedRuntimebKexception_handler_for_return_address6FpC_1_; +text: .text%__1cSInterpreterRuntimebFexception_handler_for_exception6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cNmethodOopDescbEfast_exception_handler_bci_for6MnLKlassHandle_ipnGThread__i_; +text: .text%__1cKJavaThreadNreguard_stack6MpC_i_; +text: .text%__1cFframeZinterpreter_frame_set_bcp6MpC_v_; +text: .text%__1cFframeZinterpreter_frame_set_bcx6Mi_v_; +text: .text%__1cRComputeEntryStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cOGenerateOopMapJdo_method6Miiii_v_; +text: .text%__1cQComputeCallStackHdo_void6M_v_: generateOopMap.o; +text: .text%__1cQComputeCallStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cMGraphBuilderHif_zero6MpnJValueType_nLInstructionJCondition__v_; +text: .text%__1cMGraphBuilderMnew_instance6Mi_v_; +text: .text%__1cVLoaderConstraintTableWfind_constrained_klass6MnMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cIciSymbolHbyte_at6Mi_i_; +text: .text%__1cPciInstanceKlassNloader_handle6M_pnI_jobject__; +text: .text%__1cPciInstanceKlassYprotection_domain_handle6M_pnI_jobject__; +text: .text%__1cGciTypeMis_classless6kM_i_: ciInstanceKlass.o; +text: .text%__1cLNewInstanceFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerOdo_NewInstance6MpnLNewInstance__v_; +text: .text%__1cLInstructionEhash6kM_i_: c1_Instruction.o; +text: .text%__1cKStateSplitNas_StateSplit6M_p0_: c1_Instruction.o; +text: .text%__1cLInstructionJas_Invoke6M_pnGInvoke__: c1_Instruction.o; +text: .text%__1cLNewInstanceIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cMGraphBuilderGinvoke6MnJBytecodesECode__v_; +text: .text%__1cQciBytecodeStreamKget_method6kM_pnIciMethod__; +text: .text%__1cFciEnvTget_method_by_index6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvYget_method_by_index_impl6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cPciObjectFactoryTget_unloaded_method6MpnPciInstanceKlass_pnIciSymbol_4_pnIciMethod__; +text: .text%__1cIciMethod2t6MpnPciInstanceKlass_pnIciSymbol_4_v_; +text: .text%__1cNciMethodKlassEmake6F_p0_; +text: .text%__1cIciObjectMis_classless6kM_i_: ciMethod.o; +text: .text%__1cQciBytecodeStreambAget_declared_method_holder6M_pnHciKlass__; +text: .text%__1cFciEnvbTget_instance_klass_for_declared_method_holder6FpnHciKlass__pnPciInstanceKlass__; +text: .text%__1cLciSignatureLreturn_type6kM_pnGciType__; +text: .text%__1cKValueStackNpop_arguments6Mi_pnGValues__; +text: .text%__1cGInvoke2t6MnJBytecodesECode_pnJValueType_pnLInstruction_pnGValues_iiii_v_; +text: .text%__1cGInvokeFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerJdo_Invoke6MpnGInvoke__v_; +text: .text%__1cGInvokeJas_Invoke6M_p0_: c1_Instruction.o; +text: .text%__1cLInstructionMas_LoadLocal6M_pnJLoadLocal__: c1_Instruction.o; +text: .text%__1cGInvokeIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cMGraphBuilderIthrow_op6M_v_; +text: .text%__1cFThrowFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerIdo_Throw6MpnFThrow__v_; +text: .text%__1cIBlockEndLas_BlockEnd6M_p0_: c1_Instruction.o; +text: .text%__1cFThrowIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cLInstructionJas_Return6M_pnGReturn__: c1_Instruction.o; +text: .text%__1cFThrowIas_Throw6M_p0_: c1_Instruction.o; +text: .text%__1cKValueStackMclear_stores6M_v_; +text: .text%__1cLInstructionLas_NewArray6M_pnINewArray__: c1_Instruction.o; +text: .text%__1cLInstructionFas_If6M_pnCIf__: c1_Instruction.o; +text: .text%__1cKStateSplitPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorOdo_NewInstance6MpnLNewInstance__v_; +text: .text%__1cGInvokePinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorJdo_Invoke6MpnGInvoke__v_; +text: .text%__1cTNullCheckEliminatorNhandle_Invoke6MpnGInvoke__v_; +text: .text%__1cFThrowPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorIdo_Throw6MpnFThrow__v_; +text: .text%__1cFThrowPstate_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cJValueTypeLas_VoidType6M_pnIVoidType__: c1_ValueType.o; +text: .text%__1cFRInfoIoverlaps6kMk0_i_; +text: .text%__1cILIR_ListHreg2reg6MnFRInfo_1nJBasicType__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenOdo_NewInstance6MpnLNewInstance__v_; +text: .text%__1cIValueGenYset_with_result_register6MpnLInstruction__nFRInfo__; +text: .text%__1cIValueGenMreturn1RInfo6F_nFRInfo__; +text: .text%__1cIRegAllocNlock_register6MpnLInstruction_nFRInfo__v_; +text: .text%__1cLLIR_EmitterMnew_instance6MnFRInfo_pnPciInstanceKlass_1111pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListNoop2reg_patch6MpnI_jobject_nFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cPNewInstanceStub2t6MnFRInfo_pnLLIR_OprDesc_pnPciInstanceKlass_pnMCodeEmitInfo_nIRuntime1GStubID__v_; +text: .text%__1cIValueGenJdo_Invoke6MpnGInvoke__v_; +text: .text%__1cIValueGenWinvoke_visit_arguments6MpnGInvoke_pnRCallingConvention__pnJItemArray__; +text: .text%__1cIValueGenXis_caller_save_register6FnFRInfo__i_; +text: .text%__1cIFrameMapXis_caller_save_register6FnFRInfo__i_; +text: .text%__1cIValueGenUtry_caller_to_callee6MpnLInstruction_nFRInfo__i_; +text: .text%__1cIRootItemHas_root6M_p0_: c1_CodeGenerator.o; +text: .text%__1cNc1_AllocTableThas_one_free_masked6kMnKc1_RegMask__i_; +text: .text%__1cIRegAllocKreallocate6MnFRInfo_nKc1_RegMask__1_; +text: .text%__1cLGetValueForGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cLGetRefCountGdo_cpu6Mi_v_: c1_RegAlloc.o; +text: .text%__1cNc1_AllocTablePget_free_masked6MnKc1_RegMask__i_; +text: .text%__1cIVoidTypeLas_VoidType6M_p0_: c1_ValueType.o; +text: .text%__1cLLIR_EmitterHcall_op6MnJBytecodesECode_pknOBasicTypeArray_pnMCodeEmitInfo_iiinFRInfo_pnLLIR_OprDesc__v_; +text: .text%__1cILIR_ListKnull_check6MnFRInfo_pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListQcall_opt_virtual6MnFRInfo_pnLLIR_OprDesc_pCpnMCodeEmitInfo_pnOStaticCallStub__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenIdo_Throw6MpnFThrow__v_; +text: .text%__1cLNewInstanceKexact_type6kM_pnGciType__; +text: .text%__1cOExceptionScopeLcould_catch6kMpnPciInstanceKlass_i_i_; +text: .text%__1cIValueGenRexceptionOopRInfo6F_nFRInfo__; +text: .text%__1cLNewInstanceOas_NewInstance6M_p0_: c1_Instruction.o; +text: .text%__1cIValueGenQexceptionPcRInfo6F_nFRInfo__; +text: .text%__1cILIR_ListPthrow_exception6MnFRInfo_1pnMCodeEmitInfo__v_: c1_CodeGenerator.o; +text: .text%__1cPNewInstanceStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_sparc.o; +text: .text%__1cOLIR_OpJavaCallFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cQLIR_OpVisitStateGappend6MnFRInfo__v_: c1_LIR.o; +text: .text%__1cOStaticCallStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_sparc.o; +text: .text%__1cIFrameMapWcaller_save_cpu_reg_at6Fi_pnLLIR_OprDesc__; +text: .text%__1cIVoidTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cLInstructionOas_NewInstance6M_pnLNewInstance__: c1_Instruction.o; +text: .text%__1cLInstructionQas_AccessMonitor6M_pnNAccessMonitor__: c1_Instruction.o; +text: .text%__1cPRegisterManagerMlock_all_fpu6M_v_; +text: .text%__1cICodeStubXis_exception_throw_stub6kM_i_: c1_CodeStubs_sparc.o; +text: .text%__1cLLIR_OprDescIsize_for6FnJBasicType__n0AHOprSize__: c1_LIROptimizer.o; +text: .text%__1cOLIR_OpJavaCallJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerJemit_call6MpnOLIR_OpJavaCall__v_; +text: .text%__1cNLIR_AssemblerZjobject2reg_with_patching6MpnMRegisterImpl_pnMCodeEmitInfo__v_; +text: .text%__1cMPatchingStubQalign_patch_site6MpnOMacroAssembler__v_; +text: .text%__1cNLIR_AssemblerPpatching_epilog6MpnMPatchingStub_nHLIR_Op1NLIR_PatchCode_pnMRegisterImpl_pnMCodeEmitInfo__v_; +text: .text%__1cMPatchingStubHinstall6MpnOMacroAssembler_nHLIR_Op1NLIR_PatchCode_pnMRegisterImpl_pnMCodeEmitInfo__v_: c1_LIRAssembler.o; +text: .text%__1cPNewInstanceStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cNLIR_AssemblerJemit_call6MpnOLIR_OpJavaCall__v_; +text: .text%__1cNLIR_AssemblerKalign_call6MnILIR_Code__v_; +text: .text%__1cICodeStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cOStaticCallStubLset_code_pc6MpC_v_: c1_CodeStubs_sparc.o; +text: .text%__1cOStaticCallStubMis_call_stub6kM_i_: c1_CodeStubs_sparc.o; +text: .text%__1cNLIR_AssemblerEcall6MpCnJrelocInfoJrelocType_pnMCodeEmitInfo__v_; +text: .text%__1cbBopt_virtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cKRelocationJpack_data6M_i_: relocInfo.o; +text: .text%__1cNLIR_AssemblerIthrow_op6MnFRInfo_1pnMCodeEmitInfo_i_v_; +text: .text%__1cMCodeEmitInfoQadd_register_oop6MnFRInfo__v_; +text: .text%__1cYinternal_word_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cYinternal_word_RelocationJpack_data6M_i_; +text: .text%__1cMPatchingStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cRAbstractAssemblerGa_byte6Mi_v_; +text: .text%__1cRNativeGeneralJumpUinsert_unconditional6FpC1_v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: c1_CodeStubs_sparc.o; +text: .text%__1cNRelocIterator2t6MpnKCodeBuffer_pC3_v_; +text: .text%__1cJrelocInfobDchange_reloc_info_for_address6FpnNRelocIterator_pCn0AJrelocType_4_v_; +text: .text%__1cPNewInstanceStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cOStaticCallStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cWstatic_stub_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cWstatic_stub_RelocationJpack_data6M_i_; +text: .text%__1cOMacroAssemblerUallocate_oop_address6MpnI_jobject_pnMRegisterImpl__nHAddress__; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: c1_CodeStubs_sparc.o; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: c1_CodeStubs_sparc.o; +text: .text%__1cOCallRelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cYinternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cYinternal_word_RelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cYinternal_word_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cODataRelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cWstatic_stub_RelocationLunpack_data6M_v_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: reflection.o; +text: .text%__1cNArgumentCountDset6MinJBasicType__v_: reflection.o; +text: .text%__1cPjava_lang_ClassQprimitive_mirror6FnJBasicType__pnHoopDesc__; +text: .text%__1cNSignatureInfoHdo_long6M_v_: reflection.o; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: reflection.o; +text: .text%__1cPjava_lang_ClassOprimitive_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cQSystemDictionaryQjava_mirror_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cXjava_lang_boxing_objectJget_value6FpnHoopDesc_pnGjvalue__nJBasicType__; +text: .text%__1cUGenericGrowableArrayGgrow646Mi_v_; +text: .text%__1cKJNIHandlesKmake_local6FpnGThread_pnHoopDesc__pnI_jobject__; +text: .text%__1cQComputeCallStackHdo_char6M_v_: generateOopMap.o; +text: .text%__1cQComputeCallStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cFciEnvNlookup_method6MpnNinstanceKlass_2pnNsymbolOopDesc_4nJBytecodesECode__pnNmethodOopDesc__; +text: .text%__1cMLinkResolverbNlinktime_resolve_virtual_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cIciMethodXfind_monomorphic_target6MpnHciKlass_22_p0_; +text: .text%__1cDCHAManalyze_call6FnLKlassHandle_11nMsymbolHandle_2_pnJCHAResult__; +text: .text%__1cMLinkResolverbCresolve_virtual_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cJCHAResultOis_monomorphic6kM_i_; +text: .text%__1cJCHAResultSmonomorphic_target6kM_nMmethodHandle__; +text: .text%__1cIciMethodJwill_link6MpnHciKlass_2nJBytecodesECode__i_; +text: .text%__1cIciMethodOshould_exclude6M_i_; +text: .text%__1cIciMethodPcan_be_compiled6M_i_; +text: .text%__1cMGraphBuilderVtry_inline_intrinsics6MpnIciMethod__i_; +text: .text%__1cMGraphBuilderPtry_inline_full6MpnIciMethod_i_i_; +text: .text%__1cIciMethodIhas_jsrs6kM_i_; +text: .text%__1cPciObjectFactoryMvm_symbol_at6Fi_pnIciSymbol__; +text: .text%__1cJNullCheckFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerMdo_NullCheck6MpnJNullCheck__v_; +text: .text%__1cJNullCheckIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cKObjectTypeEbase6kM_pnJValueType__: c1_ValueType.o; +text: .text%__1cKValueStackEpush6MpnJValueType_pnLInstruction__v_: c1_ValueStack.o; +text: .text%__1cMGraphBuilderKpush_scope6MpnIciMethod_pnKBlockBegin_i_v_; +text: .text%__1cKValueStackKpush_scope6MpnHIRScope__p0_; +text: .text%__1cOExceptionScopeKpush_scope6M_p0_; +text: .text%__1cHIRScopeXcompute_lock_stack_size6M_v_; +text: .text%__1cOExceptionScopeEinit6M_v_; +text: .text%__1cMGraphBuilderJScopeDataLnum_returns6M_i_; +text: .text%__1cNCanonicalizerHdo_Goto6MpnEGoto__v_; +text: .text%__1cMGraphBuilderJScopeDataQincr_num_returns6M_v_; +text: .text%__1cKValueStackJpop_scope6Mii_p0_; +text: .text%__1cGValuesIpush_all6Mpk0_v_: c1_ValueStack.o; +text: .text%__1cOExceptionScopeJpop_scope6M_p0_; +text: .text%__1cLCompilationVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cFciEnvVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cMLinkResolverbCresolve_special_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cLInstructionEprev6MpnKBlockBegin__p0_; +text: .text%__1cKBlockBeginUresolve_substitution6M_v_; +text: .text%__1cZresolve_substituted_value6FppnLInstruction__v_: c1_Instruction.o; +text: .text%__1cLInstructionFsubst6M_p0_: c1_Instruction.o; +text: .text%__1cLInstructionPother_values_do6MpFpp0_v_v_: c1_GraphBuilder.o; +text: .text%__1cLInstructionPstate_values_do6MpFpp0_v_v_: c1_GraphBuilder.o; +text: .text%__1cLInstructionPstate_values_do6MpFpp0_v_v_: c1_Instruction.o; +text: .text%__1cIConstantPother_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cIBlockEndPother_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cHIntTypeEsize6kM_i_: c1_Canonicalizer.o; +text: .text%__1cJNullCheckPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorMdo_NullCheck6MpnJNullCheck__v_; +text: .text%__1cHIRScopeNtop_scope_bci6kM_i_; +text: .text%__1cIValueGenMdo_NullCheck6MpnJNullCheck__v_; +text: .text%__1cJNullCheckKlock_stack6kM_pnKValueStack__: c1_GraphBuilder.o; +text: .text%__1cLLIR_EmitterKnull_check6MpnLLIR_OprDesc_pnMCodeEmitInfo__v_; +text: .text%__1cIValueGenbDsafepoint_poll_needs_register6F_i_; +text: .text%__1cILIR_ListJsafepoint6MnFRInfo_pnMCodeEmitInfo__v_: c1_CodeGenerator.o; +text: .text%__1cIValueGenQlock_spill_rinfo6MpnLInstruction_nFRInfo__v_; +text: .text%__1cILIR_ListQreg2single_stack6MnFRInfo_inJBasicType__v_: c1_CacheLocals.o; +text: .text%__1cILIR_ListQsingle_stack2reg6MinFRInfo_nJBasicType__v_; +text: .text%__1cRLIR_PeepholeStatePkill_stack_slot6Mi_v_; +text: .text%__1cIFrameMapQaddress_for_name6kMiii_nHAddress__; +text: .text%__1cIFrameMapSfp_offset_for_name6kMiii_i_; +text: .text%__1cIFrameMapPnum_local_names6kM_i_; +text: .text%__1cIFrameMapNlocal_to_slot6kMii_i_; +text: .text%__1cIFrameMapQmake_new_address6kMi_nHAddress__; +text: .text%__1cRLIR_PeepholeStateTmark_safe_to_delete6Mi_v_; +text: .text%__1cNLIR_AssemblerJreg2stack6MnFRInfo_inJBasicType__v_; +text: .text%__1cNLIR_AssemblerFstore6MpnMRegisterImpl_2inJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerJstack2reg6MpnLLIR_OprDesc_2nJBasicType__v_; +text: .text%__1cIFrameMapTsingle_word_regname6kMi_nHOptoRegEName__; +text: .text%__1cIFrameMapZlocation_for_local_offset6kMinILocationEType_p1_i_; +text: .text%__1cIFrameMapWlocation_for_fp_offset6kMinILocationEType_p1_i_; +text: .text%__1cILocationVlegal_offset_in_bytes6Fi_i_; +text: .text%jni_GetPrimitiveArrayCritical: jni.o; +text: .text%jni_ReleasePrimitiveArrayCritical: jni.o; +text: .text%jni_SetBooleanField: jni.o; +text: .text%__1cXNativeSignatureIteratorIdo_float6M_v_: interpreterRT_sparc.o; +text: .text%JVM_IsNaN; +text: .text%__1cXNativeSignatureIteratorJdo_double6M_v_: interpreterRT_sparc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_double6M_v_; +text: .text%__1cKValueStackEpush6MpnJValueType_pnLInstruction__v_: c1_Optimizer.o; +text: .text%__1cEIfOpPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cEIfOpFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorHdo_IfOp6MpnEIfOp__v_; +text: .text%__1cIValueGenHdo_IfOp6MpnEIfOp__v_; +text: .text%__1cLLIR_EmitterLifop_phase16MnLInstructionJCondition_pnLLIR_OprDesc_4_v_; +text: .text%__1cIHintItemNset_from_item6MpknEItem__v_; +text: .text%__1cIHintItemEtype6kM_pnJValueType__: c1_Items.o; +text: .text%__1cLLIR_EmitterLifop_phase26MnFRInfo_pnLLIR_OprDesc_3nLInstructionJCondition__v_; +text: .text%__1cILIR_ListGbranch6MnMLIR_OpBranchNLIR_Condition_pnFLabel__v_; +text: .text%__1cRLIR_PeepholeStateUstart_forward_branch6MpnFLabel__v_; +text: .text%__1cIintStackEgrow6Mki1_v_: c1_LIROptimizer.o; +text: .text%__1cOGenerateOopMapIcopy_cts6MpnNCellTypeState_2_i_; +text: .text%__1cQComputeCallStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cQComputeCallStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cOGenerateOopMapEppop6MpnNCellTypeState__v_; +text: .text%__1cQComputeCallStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cTciConstantPoolCache2t6MpnFArena_i_v_; +text: .text%__1cTciConstantPoolCacheDget6Mi_pv_; +text: .text%__1cTciConstantPoolCacheGinsert6Mipv_v_; +text: .text%__1cLInstructionMas_CompareOp6M_pnJCompareOp__: c1_Instruction.o; +text: .text%__1cLInstructionNas_InstanceOf6M_pnKInstanceOf__: c1_Instruction.o; +text: .text%__1cKStoreFieldFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerNdo_StoreField6MpnKStoreField__v_; +text: .text%__1cLAccessFieldOas_AccessField6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLAccessFieldIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cIValueMapKkill_field6MpnHciField__v_; +text: .text%__1cLInstructionMas_LoadField6M_pnJLoadField__: c1_Instruction.o; +text: .text%__1cKValueStackQpin_stack_fields6MpnHciField__v_; +text: .text%__1cMGraphBuilderHif_null6MpnJValueType_nLInstructionJCondition__v_; +text: .text%__1cOObjectConstantRas_ObjectConstant6M_p0_: c1_ValueType.o; +text: .text%__1cDCHANprocess_class6FnLKlassHandle_pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___nMsymbolHandle_6_v_; +text: .text%__1cFKlassMnext_sibling6kM_p0_; +text: .text%__1cYDebugInformationRecorderNadd_dependent6MpnPciInstanceKlass_pnIciMethod__v_; +text: .text%__1cILongTypeEbase6kM_pnJValueType__: c1_ValueType.o; +text: .text%__1cILongTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cILongTypeEsize6kM_i_: c1_ValueType.o; +text: .text%__1cHConvertFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerKdo_Convert6MpnHConvert__v_; +text: .text%__1cHConvertEhash6kM_i_: c1_GraphBuilder.o; +text: .text%__1cHConvertEname6kM_pkc_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderOnew_type_array6M_v_; +text: .text%__1cMNewTypeArrayFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerPdo_NewTypeArray6MpnMNewTypeArray__v_; +text: .text%__1cINewArrayIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cHciKlassOis_subclass_of6Mp0_i_; +text: .text%__1cUGenericGrowableArrayMraw_contains6kMpknEGrET__i_; +text: .text%__1cJValueTypeRas_ObjectConstant6M_pnOObjectConstant__: c1_ValueType.o; +text: .text%__1cNClassConstantQas_ClassConstant6M_p0_: c1_ValueType.o; +text: .text%__1cHIntTypeEbase6kM_pnJValueType__: c1_Canonicalizer.o; +text: .text%__1cMArithmeticOpOis_commutative6kM_i_; +text: .text%__1cMGraphBuilderIlogic_op6MpnJValueType_nJBytecodesECode__v_; +text: .text%__1cHLogicOpFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerKdo_LogicOp6MpnHLogicOp__v_; +text: .text%__1cHLogicOpEhash6kM_i_: c1_Instruction.o; +text: .text%__1cHLogicOpEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cLInstructionIcan_trap6kM_i_: c1_Instruction.o; +text: .text%__1cMLinkResolverbBresolve_static_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cLInstructionPother_values_do6MpFpp0_v_v_: c1_Instruction.o; +text: .text%__1cKStoreFieldPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorNdo_StoreField6MpnKStoreField__v_; +text: .text%__1cINewArrayPinput_values_do6MpFppnLInstruction__v_v_: c1_Instruction.o; +text: .text%__1cHConvertPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorKdo_Convert6MpnHConvert__v_; +text: .text%__1cQNullCheckVisitorPdo_NewTypeArray6MpnMNewTypeArray__v_; +text: .text%__1cJLoadFieldMas_LoadField6M_p0_: c1_Instruction.o; +text: .text%__1cQNullCheckVisitorKdo_LogicOp6MpnHLogicOp__v_; +text: .text%__1cDPhiPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cDPhiFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorGdo_Phi6MpnDPhi__v_; +text: .text%__1cTsort_by_start_block6FppnELoop_2_i_: c1_Loops.o; +text: .text%__1cHConvertKas_Convert6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLInstructionIas_Local6M_pnFLocal__: c1_GraphBuilder.o; +text: .text%__1cDPhiGas_Phi6M_p0_: c1_GraphBuilder.o; +text: .text%__1cIValueGenbBrlock_byte_result_with_hint6MpnLInstruction_pknEItem__nFRInfo__; +text: .text%__1cIValueGenNdo_StoreField6MpnKStoreField__v_; +text: .text%__1cIValueGenOscratch1_RInfo6kM_nFRInfo__; +text: .text%__1cIValueGenUprefer_alu_registers6kM_i_; +text: .text%__1cIValueGenOload_byte_item6MpnEItem__v_; +text: .text%__1cLLIR_EmitterLfield_store6MpnHciField_pnLLIR_OprDesc_i4iipnMCodeEmitInfo_nFRInfo__v_; +text: .text%__1cLLIR_EmitterQfield_store_byte6MpnLLIR_OprDesc_i2nFRInfo_ipnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListNstore_mem_reg6MnFRInfo_1inJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cMciNullObjectMis_classless6kM_i_: ciNullObject.o; +text: .text%__1cEItemUget_jobject_constant6kM_pnIciObject__; +text: .text%__1cMciNullObjectOis_null_object6kM_i_: ciNullObject.o; +text: .text%__1cJValueTypeQas_ClassConstant6M_pnNClassConstant__: c1_ValueType.o; +text: .text%__1cOObjectConstantIencoding6kM_pnI_jobject__; +text: .text%__1cNc1_AllocTableMhas_two_free6kM_i_; +text: .text%__1cIRegAllocOset_locked_cpu6MipnLInstruction_i_v_; +text: .text%__1cIRegAllocMset_free_cpu6Mi_v_; +text: .text%__1cFRInfoLas_rinfo_lo6kM_0_; +text: .text%__1cFRInfoLas_rinfo_hi6kM_0_; +text: .text%__1cLLIR_EmitterXlo_word_offset_in_bytes6kM_i_; +text: .text%__1cLLIR_EmitterXhi_word_offset_in_bytes6kM_i_; +text: .text%__1cIValueGenPdo_NewTypeArray6MpnMNewTypeArray__v_; +text: .text%__1cIValueGenKdo_Convert6MpnHConvert__v_; +text: .text%__1cLLIR_EmitterKconvert_op6MnJBytecodesECode_pnLLIR_OprDesc_nFRInfo_i_v_; +text: .text%__1cILIR_ListHconvert6MnJBytecodesECode_pnLLIR_OprDesc_4i_v_: c1_LIREmitter.o; +text: .text%__1cLLIR_EmitterOnew_type_array6MnFRInfo_nJBasicType_pnLLIR_OprDesc_11111pnMCodeEmitInfo__v_; +text: .text%__1cQNewTypeArrayStub2t6MnFRInfo_11pnMCodeEmitInfo__v_; +text: .text%__1cQciTypeArrayKlassEmake6FnJBasicType__p0_; +text: .text%__1cILIR_ListHoop2reg6MpnI_jobject_nFRInfo__v_: c1_LIREmitter_sparc.o; +text: .text%__1cILIR_ListOallocate_array6MnFRInfo_11111nJBasicType_1pnICodeStub__v_; +text: .text%__1cILIR_ListHoop2reg6MpnI_jobject_nFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cHciKlassMaccess_flags6M_i_; +text: .text%__1cILIR_ListPallocate_object6MnFRInfo_111ii1pnICodeStub__v_; +text: .text%__1cLLIR_OprFactQdummy_value_type6FpnJValueType__pnLLIR_OprDesc__; +text: .text%__1cIRegAllocTget_value_for_rinfo6kMnFRInfo__pnLInstruction__; +text: .text%__1cIValueGenQround_spill_item6MpnEItem_i_v_; +text: .text%__1cIRegAllocPget_register_rc6kMnFRInfo__i_; +text: .text%__1cIRegAllocOget_lock_spill6MpnLInstruction_i_i_; +text: .text%__1cLLIR_EmitterFspill6MipnLLIR_OprDesc__v_; +text: .text%__1cIFrameMapKspill_name6kMi_i_; +text: .text%__1cIRegAllocKfree_spill6MipnJValueType__v_; +text: .text%__1cIRegAllocNis_free_spill6kMipnJValueType__i_; +text: .text%__1cLLIR_EmitterOmembar_release6M_v_; +text: .text%__1cLLIR_EmitterNwrite_barrier6MpnLLIR_OprDesc_2_v_; +text: .text%__1cILIR_ListUunsigned_shift_right6MnFRInfo_i1_v_: c1_LIREmitter_sparc.o; +text: .text%__1cILIR_ListUunsigned_shift_right6MpnLLIR_OprDesc_222_v_; +text: .text%__1cILIR_ListHint2reg6MinFRInfo__v_: c1_LIREmitter_sparc.o; +text: .text%__1cILIR_ListFstore6MpnLLIR_OprDesc_pnLLIR_Address_nJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cLLIR_EmitterGmembar6M_v_; +text: .text%__1cILIR_ListGmembar6M_v_: c1_LIREmitter.o; +text: .text%__1cJValueTypeNas_ObjectType6M_pnKObjectType__: c1_Canonicalizer.o; +text: .text%__1cLLIR_EmitterOmembar_acquire6M_v_; +text: .text%__1cILIR_ListOcall_icvirtual6MnFRInfo_pnLLIR_OprDesc_pCpnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cNClassConstantIencoding6kM_pnI_jobject__; +text: .text%__1cILIR_ListDadd6MpnLLIR_OprDesc_22_v_: c1_LIREmitter_sparc.o; +text: .text%__1cIValueGenKdo_LogicOp6MpnHLogicOp__v_; +text: .text%__1cLLIR_EmitterIlogic_op6MnJBytecodesECode_nFRInfo_pnLLIR_OprDesc_5_v_; +text: .text%__1cILIR_ListKlogical_or6MnFRInfo_pnLLIR_OprDesc_1_v_: c1_LIREmitter.o; +text: .text%__1cIValueGenScompute_phi_arrays6MpnKValueStack_pnGValues_pnIintStack_i_pnLInstruction__; +text: .text%__1cIValueGenOload_item_hint6MpnEItem_pk1_v_; +text: .text%__1cLLIR_EmitterTset_fpu_stack_empty6M_v_; +text: .text%__1cILIR_ListTset_fpu_stack_empty6M_v_: c1_LIREmitter.o; +text: .text%__1cJValueTypeMas_FloatType6M_pnJFloatType__: c1_ValueType.o; +text: .text%__1cILIR_ListLlogical_and6MnFRInfo_pnLLIR_OprDesc_1_v_: c1_LIREmitter.o; +text: .text%__1cRclear_state_items6FppnLInstruction__v_: c1_CodeGenerator.o; +text: .text%__1cQLIR_OpAllocArrayFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cQNewTypeArrayStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_sparc.o; +text: .text%__1cOLIR_OpAllocObjFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cINewArrayLas_NewArray6M_p0_: c1_Instruction.o; +text: .text%__1cNLIR_OpConvertJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerOemit_opConvert6MpnNLIR_OpConvert__v_; +text: .text%__1cQLIR_OpAllocArrayJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerQemit_alloc_array6MpnQLIR_OpAllocArray__v_; +text: .text%__1cOLIR_OpAllocObjJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerOemit_alloc_obj6MpnOLIR_OpAllocObj__v_; +text: .text%__1cNLIR_AssemblerHreg2mem6MnFRInfo_pnLLIR_Address_nJBasicType_nHLIR_Op1NLIR_PatchCode_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerFstore6MnFRInfo_pnMRegisterImpl_inJBasicType__i_; +text: .text%__1cNLIR_AssemblerOemit_opConvert6MpnNLIR_OpConvert__v_; +text: .text%__1cFRInfoOas_register_lo6kM_pnMRegisterImpl__; +text: .text%__1cFRInfoOas_register_hi6kM_pnMRegisterImpl__; +text: .text%__1cNLIR_AssemblerQemit_alloc_array6MpnQLIR_OpAllocArray__v_; +text: .text%__1cRC1_MacroAssemblerOallocate_array6MpnMRegisterImpl_2222ii2rnFLabel__v_; +text: .text%__1cQNewTypeArrayStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cNLIR_AssemblerOemit_alloc_obj6MpnOLIR_OpAllocObj__v_; +text: .text%__1cRC1_MacroAssemblerPallocate_object6MpnMRegisterImpl_222ii2rnFLabel__v_; +text: .text%__1cIFrameMapRlocation_for_name6kMinILocationEType_p1ii_i_; +text: .text%__1cNLIR_AssemblerOmembar_release6M_v_; +text: .text%__1cNLIR_AssemblerFstore6MnFRInfo_pnMRegisterImpl_3nJBasicType__i_; +text: .text%__1cNLIR_AssemblerGmembar6M_v_; +text: .text%__1cNLIR_AssemblerOmembar_acquire6M_v_; +text: .text%__1cNLIR_AssemblerHic_call6MpCpnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerCpc6kM_pC_; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: c1_LIRAssembler_sparc.o; +text: .text%__1cXvirtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cXvirtual_call_RelocationJpack_data6M_i_; +text: .text%__1cNLIR_AssemblerIlogic_op6MnILIR_Code_pnLLIR_OprDesc_33_v_; +text: .text%__1cNLIR_AssemblerTset_fpu_stack_empty6M_v_; +text: .text%__1cQNewTypeArrayStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cVConstantOopWriteValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cYDebugInformationRecorderNappend_handle6MpnI_jobject__i_; +text: .text%__1cXvirtual_call_RelocationLunpack_data6M_v_; +text: .text%__1cNinstanceKlassVadd_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cIRuntime1Yresolve_opt_virtual_call6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cNSharedRuntimeOresolve_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cNSharedRuntimeSresolve_sub_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cLRuntimeStubYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cHnmethodJis_zombie6kM_i_: nmethod.o; +text: .text%__1cNnmethodLocker2t6MpnHnmethod__v_; +text: .text%__1cHnmethodQis_native_method6kM_i_: nmethod.o; +text: .text%__1cHnmethodKpc_desc_at6MpCi_pnGPcDesc__; +text: .text%__1cGPcDescHreal_pc6kMpknHnmethod__pC_; +text: .text%__1cNSharedRuntimeXfind_callee_info_helper6FpnKJavaThread_rnMvframeStream_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cPBytecode_invokeFindex6kM_i_; +text: .text%__1cLRegisterMapIpd_clear6M_v_; +text: .text%__1cPBytecode_invokeNstatic_target6MpnGThread__nMmethodHandle__; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cLRegisterMapLpd_location6kMnFVMRegEName__pC_; +text: .text%__1cKCompiledICZcompute_monomorphic_entry6FnMmethodHandle_nLKlassHandle_iirnOCompiledICInfo_pnGThread__v_; +text: .text%__1cKCompiledIC2t6MpnKNativeCall__v_; +text: .text%__1cXvirtual_call_RelocationIparse_ic6FrpnICodeBlob_rpC5rppnHoopDesc_pi_nNRelocIterator__; +text: .text%__1cKCompiledICIis_clean6kM_i_; +text: .text%__1cRInlineCacheBufferIcontains6FpC_i_; +text: .text%__1cKCompiledICSset_to_monomorphic6MrknOCompiledICInfo__v_; +text: .text%__1cSCompiledStaticCallSset_to_interpreted6MnMmethodHandle_pC_v_; +text: .text%__1cSCompiledStaticCallJfind_stub6M_pC_; +text: .text%__1cNRelocIteratorEnext6M_i_: compiledIC.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: compiledIC.o; +text: .text%__1cbBopt_virtual_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cRNativeMovConstRegIset_data6Mi_v_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nativeInst_sparc.o; +text: .text%__1cOoop_RelocationIoop_addr6M_ppnHoopDesc__; +text: .text%__1cKNativeCallXset_destination_mt_safe6MpC_v_; +text: .text%__1cNnmethodLocker2T6M_v_; +text: .text%__1cNmethodOopDescTverified_code_entry6M_pC_; +text: .text%__1cIRuntime1Yprepare_interpreter_call6FpnKJavaThread_pnNmethodOopDesc__v_; +text: .text%__1cLLIR_EmitterYstrength_reduce_multiply6MpnLLIR_OprDesc_i22_i_; +text: .text%__1cOGenerateOopMapGdo_ldc6Mii_v_; +text: .text%__1cJLoadFieldIis_equal6kMpnLInstruction__i_: c1_Instruction.o; +text: .text%__1cMGraphBuilderNload_constant6M_v_; +text: .text%__1cQciBytecodeStreamMget_constant6kM_nKciConstant__; +text: .text%__1cFciEnvVget_constant_by_index6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cFciEnvbAget_constant_by_index_impl6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cLInstructionMas_LoadLocal6M_pnJLoadLocal__: c1_Canonicalizer.o; +text: .text%__1cIRegAllocKlock_spill6MpnLInstruction_ii_v_; +text: .text%__1cIValueGenVinvoke_load_arguments6MpnGInvoke_pnJItemArray_pnRCallingConvention__v_; +text: .text%__1cILIR_ListLcall_static6MpnLLIR_OprDesc_pCpnMCodeEmitInfo_pnOStaticCallStub__v_: c1_LIREmitter.o; +text: .text%__1cIintStackEgrow6Mki1_v_: c1_CacheLocals.o; +text: .text%__1cWstatic_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: c1_CodeStubs_sparc.o; +text: .text%__1cKExceptionsL_throw_args6FpnGThread_pkcinMsymbolHandle_5pnRJavaCallArguments__v_; +text: .text%__1cJCodeCacheXmark_for_deoptimization6FpnMklassOopDesc__i_; +text: .text%__1cNinstanceKlassXmark_dependent_nmethods6MpnMklassOopDesc__i_; +text: .text%__1cPParameterMapperJdo_object6Mii_v_: c1_Runtime1_sparc.o; +text: .text%__1cPParameterMapperGdo_int6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cMPeriodicTaskOreal_time_tick6FI_v_; +text: .text%__1cPStatSamplerTaskEtask6M_v_: statSampler.o; +text: .text%jni_GetArrayLength: jni.o; +text: .text%JVM_Read; +text: .text%jni_GetByteArrayRegion: jni.o; +text: .text%JVM_DefineClassWithSource; +text: .text%__1cQSystemDictionaryTresolve_from_stream6FnMsymbolHandle_nGHandle_2pnPClassFileStream_pnGThread__pnMklassOopDesc__; +text: .text%__1cPClassFileParserbDverify_legal_method_signature6MnMsymbolHandle_1pnGThread__i_; +text: .text%__1cPClassFileParserZskip_over_field_signature6MpciIpnGThread__1_; +text: .text%__1cPClassFileParserXverify_legal_class_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cQput_after_lookup6FnMsymbolHandle_0ppnLNameSigHash__i_; +text: .text%__1cKDictionarybAis_valid_protection_domain6MiInMsymbolHandle_nGHandle_2_i_; +text: .text%__1cQSystemDictionarybAvalidate_protection_domain6FnTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cKDictionaryVadd_protection_domain6MiInTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cNmethodOopDescWis_vanilla_constructor6kM_i_; +text: .text%JVM_GetClassCPEntriesCount; +text: .text%JVM_GetClassCPTypes; +text: .text%JVM_GetClassNameUTF; +text: .text%JVM_ReleaseUTF; +text: .text%JVM_FindClassFromClass; +text: .text%jni_IsSameObject: jni.o; +text: .text%JVM_GetClassFieldsCount; +text: .text%JVM_GetClassMethodsCount; +text: .text%JVM_GetMethodIxModifiers; +text: .text%JVM_GetMethodIxByteCodeLength; +text: .text%JVM_GetMethodIxByteCode; +text: .text%JVM_GetMethodIxExceptionTableLength; +text: .text%JVM_GetMethodIxLocalsCount; +text: .text%JVM_GetMethodIxArgsSize; +text: .text%JVM_GetMethodIxSignatureUTF; +text: .text%JVM_GetMethodIxMaxStack; +text: .text%JVM_GetMethodIxExceptionsCount; +text: .text%JVM_GetMethodIxExceptionIndexes; +text: .text%JVM_GetCPMethodNameUTF; +text: .text%JVM_GetCPMethodClassNameUTF; +text: .text%jni_NewLocalRef: jni.o; +text: .text%JVM_GetCPMethodModifiers; +text: .text%JVM_IsConstructorIx; +text: .text%JVM_GetCPMethodSignatureUTF; +text: .text%jni_DeleteGlobalRef: jni.o; +text: .text%__1cQSystemDictionaryVadd_loader_constraint6FnMsymbolHandle_nGHandle_2pnGThread__v_; +text: .text%__1cVLoaderConstraintTableJadd_entry6MnMsymbolHandle_pnMklassOopDesc_nGHandle_34pnGThread__i_; +text: .text%jni_ToReflectedMethod: jni.o; +text: .text%__1cKReflectionKnew_method6FnMmethodHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: reflection.o; +text: .text%__1cYjava_lang_reflect_MethodGcreate6FpnGThread__nGHandle__; +text: .text%__1cYjava_lang_reflect_MethodJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodIset_name6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodPset_return_type6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodThas_signature_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodVhas_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodbFhas_parameter_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodbChas_annotation_default_field6F_i_; +text: .text%__1cNmethodOopDescSannotation_default6kM_pnQtypeArrayOopDesc__; +text: .text%__1cYjava_lang_reflect_MethodWset_annotation_default6FpnHoopDesc_2_v_; +text: .text%jni_CallIntMethod: jni.o; +text: .text%jni_CallStaticVoidMethod: jni.o; +text: .text%__1cNFingerprinterIdo_array6Mii_v_: c1_Runtime1_sparc.o; +text: .text%jni_DetachCurrentThread; +text: .text%__1cKJavaThreadEexit6Mi_v_; +text: .text%__1cQjava_lang_ThreadLthreadGroup6FpnHoopDesc__2_; +text: .text%JVM_MonitorNotifyAll; +text: .text%__1cNThreadServiceWcurrent_thread_exiting6FpnKJavaThread__v_; +text: .text%__1cLensure_join6FpnKJavaThread__v_: thread.o; +text: .text%__1cQjava_lang_ThreadNset_stillborn6FpnHoopDesc__v_; +text: .text%__1cKJavaThreadYremove_stack_guard_pages6M_v_; +text: .text%__1cCosPuncommit_memory6FpcI_i_; +text: .text%__1cWThreadLocalAllocBufferFclear6M_v_; +text: .text%__1cHThreadsGremove6FpnKJavaThread__v_; +text: .text%__1cNThreadServiceNremove_thread6FpnKJavaThread_i_v_; +text: .text%__SLIP.DELETER__A: thread.o; +text: .text%__1cKJavaThread2T6M_v_; +text: .text%__1cGParker2T6M_v_; +text: .text%__1cHMonitor2T6M_v_; +text: .text%__1cFMutex2T6M_v_; +text: .text%lwp_cond_destroy: os_solaris.o; +text: .text%lwp_mutex_destroy: os_solaris.o; +text: .text%__1cUThreadSafepointStateHdestroy6FpnKJavaThread__v_; +text: .text%__1cGThread2T5B6M_v_; +text: .text%__1cCosLfree_thread6FpnIOSThread__v_; +text: .text%__1cIOSThread2T6M_v_; +text: .text%__1cIOSThreadKpd_destroy6M_v_; +text: .text%jni_DestroyJavaVM; +text: .text%jni_AttachCurrentThread; +text: .text%__1cKJavaThread2t6M_v_; +text: .text%__1cCosWcreate_attached_thread6FpnGThread__i_; +text: .text%__1cHThreadsYis_supported_jni_version6Fi_C_; +text: .text%__1cKJavaThreadSallocate_threadObj6MnGHandle_pcipnGThread__v_; +text: .text%__1cKJavaThreadYcreate_stack_guard_pages6M_v_; +text: .text%__1cHThreadsKdestroy_vm6F_i_; +text: .text%__1cKJavaThreadVinvoke_shutdown_hooks6M_v_; +text: .text%__1cLbefore_exit6FpnKJavaThread__v_; +text: .text%__1cNWatcherThreadEstop6F_v_; +text: .text%__1cLStatSamplerJdisengage6F_v_; +text: .text%__1cMPeriodicTaskJdisenroll6M_v_; +text: .text%__1cMPeriodicTask2T5B6M_v_; +text: .text%__1cLStatSamplerHdestroy6F_v_; +text: .text%__1cMPerfDataList2T6M_v_; +text: .text%__1cLJvmtiExportNpost_vm_death6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_death6F_v_; +text: .text%__1cCosXterminate_signal_thread6F_v_; +text: .text%__1cCosNsigexitnum_pd6F_i_; +text: .text%__1cCosNsignal_notify6Fi_v_; +text: .text%__1cFVTuneEexit6F_v_; +text: .text%__1cIVMThreadXwait_for_vm_thread_exit6F_v_; +text: .text%__1cUSafepointSynchronizeFbegin6F_v_; +text: .text%__1cORuntimeServiceWrecord_safepoint_begin6F_v_; +text: .text%__1cJTimeStampSticks_since_update6kM_x_; +text: .text%__1cTAbstractInterpreterRnotice_safepoints6F_v_; +text: .text%__1cCosbCmake_polling_page_unreadable6F_v_; +text: .text%__1cUThreadSafepointStateXexamine_state_of_thread6Mi_v_; +text: .text%__1cUSafepointSynchronizeOsafepoint_safe6FpnKJavaThread_nPJavaThreadState__i_; +text: .text%__1cORuntimeServicebDrecord_safepoint_synchronized6F_v_; +text: .text%__1cSObjectSynchronizerVdeflate_idle_monitors6F_v_; +text: .text%__1cNObjectMonitorHis_busy6kM_i_; +text: .text%__1cRInlineCacheBufferUupdate_inline_caches6F_v_; +text: .text%__1cMCounterDecayFdecay6F_v_; +text: .text%__1cQSystemDictionaryRnumber_of_classes6F_i_; +text: .text%__1cQSystemDictionaryStry_get_next_class6F_pnMklassOopDesc__; +text: .text%__1cKDictionaryStry_get_next_class6M_pnMklassOopDesc__; +text: .text%__1cNinstanceKlassKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cJdo_method6FpnNmethodOopDesc__v_: recompilationMonitor.o; +text: .text%__1cONMethodSweeperFsweep6F_v_; +text: .text%__1cNCompileBrokerQset_should_block6F_v_; +text: .text%__1cHVM_ExitbJwait_for_threads_in_native_to_block6F_i_; +text: .text%__1cURecompilationMonitorbFstop_recompilation_monitor_task6F_v_; +text: .text%__1cIVMThreadHdestroy6F_v_; +text: .text%__SLIP.DELETER__A: vmThread.o; +text: .text%__1cSThreadLocalStorageRpd_invalidate_all6F_v_; +text: .text%__1cHVM_ExitNset_vm_exited6F_i_; +text: .text%__1cUSafepointSynchronizeFblock6FpnKJavaThread__v_; +text: .text%__1cCosRcurrent_thread_id6F_i_; +text: .text%__1cJttyLockerbCbreak_tty_lock_for_safepoint6Fi_v_; +text: .text%__1cMexit_globals6F_v_; +text: .text%__1cVverificationType_exit6F_v_; +text: .text%__1cPperfMemory_exit6F_v_; +text: .text%__1cPPerfDataManagerHdestroy6F_v_; +text: .text%__1cKPerfMemoryUdelete_memory_region6F_v_; +text: .text%__1cMostream_exit6F_v_; +text: .text%__SLIP.DELETER__C: ostream.o; +text: .text%__SLIP.FINAL__A: c1_Items.o; +# Test Exit +text: .text%__1cPSignatureStreamHis_done6kM_i_; +text: .text%JVM_Halt; +text: .text%__1cHvm_exit6Fi_v_; +text: .text%__1cIVMThreadHexecute6FpnMVM_Operation__v_; +text: .text%__1cMVM_OperationNdoit_prologue6M_i_: vm_operations.o; +text: .text%__1cGThreadMget_priority6Fkpk0_nOThreadPriority__; +text: .text%__1cCosMget_priority6FkpknGThread_rnOThreadPriority__nIOSReturn__; +text: .text%__1cCosTget_native_priority6FkpknGThread_pi_nIOSReturn__; +text: .text%__1cMVM_OperationSset_calling_thread6MpnGThread_nOThreadPriority__v_; +text: .text%__1cMVM_OperationPevaluation_mode6kM_n0AEMode__: vm_operations.o; +text: .text%__1cMVM_OperationSis_cheap_allocated6kM_i_: vm_operations.o; +text: .text%__1cHVM_ExitEname6kM_pkc_: vm_operations.o; +text: .text%__1cJEventMark2t6MpkcE_v_: vmThread.o; +text: .text%__1cCosJyield_all6Fi_v_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: vmThread.o; +text: .text%__1cSInterpreterRuntimeMat_safepoint6FpnKJavaThread__v_; +text: .text%__1cMVM_OperationIevaluate6M_v_; +text: .text%__1cHVM_ExitEdoit6M_v_; +# Test Hello +text: .text%JVM_GetCPFieldSignatureUTF; +text: .text%__1cTconstantPoolOopDescOstring_at_impl6FnSconstantPoolHandle_ipnGThread__pnHoopDesc__; +text: .text%JVM_Write; +# Test Sleep +text: .text%__1cPClassFileStreamGget_u86MpnGThread__X_; +text: .text%JVM_GetMethodIxExceptionTableEntry; +text: .text%JVM_GetCPClassNameUTF; +text: .text%JVM_Sleep; +text: .text%__1cCosHSolarisTsetup_interruptible6FpnKJavaThread__v_; +text: .text%__1cUSafepointSynchronizeRis_cleanup_needed6F_i_; +text: .text%__1cRInlineCacheBufferIis_empty6F_i_; +# Test IntToString +text: .text%__1cQChunkPoolCleanerEtask6M_v_: allocation.o; +# Test LoadToolkit +text: .text%JVM_GetClassContext; +text: .text%jni_IsAssignableFrom: jni.o; +text: .text%__1cRComputeEntryStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cIFrameMapRname_for_argument6Fi_i_; +text: .text%__1cNLIR_AssemblerEload6MpnMRegisterImpl_inFRInfo_nJBasicType__i_; +text: .text%__1cRComputeEntryStackHdo_char6M_v_: generateOopMap.o; +text: .text%__1cMGraphBuilderNstore_indexed6MnJBasicType__v_; +text: .text%__1cIValueMapKkill_array6MpnJValueType__v_; +text: .text%__1cLInstructionOas_LoadIndexed6M_pnLLoadIndexed__: c1_GraphBuilder.o; +text: .text%__1cLInstructionOas_LoadIndexed6M_pnLLoadIndexed__: c1_Instruction.o; +text: .text%__1cKValueStackRpin_stack_indexed6MpnJValueType__v_; +text: .text%__1cMStoreIndexedFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerPdo_StoreIndexed6MpnMStoreIndexed__v_; +text: .text%__1cMStoreIndexedPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorPdo_StoreIndexed6MpnMStoreIndexed__v_; +text: .text%__1cIValueGenPdo_StoreIndexed6MpnMStoreIndexed__v_; +text: .text%__1cLLIR_EmitterNindexed_store6MnJBasicType_pnLLIR_OprDesc_33nFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cKValueStackElock6MpnHIRScope_pnLInstruction__i_; +text: .text%__1cKValueStackGunlock6M_i_; +text: .text%__1cLLIR_EmitterVmonitorenter_at_entry6MnFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cLLIR_EmitterGmethod6kM_pnIciMethod__; +text: .text%__1cLLIR_EmitterNmonitor_enter6MnFRInfo_111ipnMCodeEmitInfo_3_v_; +text: .text%__1cQMonitorEnterStub2t6MnFRInfo_1pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListbAload_stack_address_monitor6MinFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListLlock_object6MnFRInfo_111pnICodeStub_pnMCodeEmitInfo__v_; +text: .text%__1cIValueGenMrelease_item6MpnEItem__v_; +text: .text%__1cLLIR_EmitterQreturn_op_prolog6Mi_v_; +text: .text%__1cLLIR_EmitterMmonitor_exit6MnFRInfo_11i_v_; +text: .text%__1cILIR_ListNunlock_object6MnFRInfo_11pnICodeStub__v_; +text: .text%__1cKLIR_OpLockFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cQMonitorEnterStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_sparc.o; +text: .text%__1cRMonitorAccessStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_sparc.o; +text: .text%__1cKLIR_OpLockJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerJemit_lock6MpnKLIR_OpLock__v_; +text: .text%__1cNLIR_AssemblerPmonitor_address6MinFRInfo__v_; +text: .text%__1cIFrameMapbEaddress_for_monitor_lock_index6kMi_nHAddress__; +text: .text%__1cNLIR_AssemblerJemit_lock6MpnKLIR_OpLock__v_; +text: .text%__1cRC1_MacroAssemblerLlock_object6MpnMRegisterImpl_222rnFLabel__v_; +text: .text%__1cQMonitorEnterStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cIFrameMapWmonitor_object_regname6kMi_nHOptoRegEName__; +text: .text%__1cIFrameMapbHlocation_for_monitor_object_index6kMipnILocation__i_; +text: .text%__1cIFrameMapbFlocation_for_monitor_lock_index6kMipnILocation__i_; +text: .text%__1cMMonitorValue2t6MpnKScopeValue_nILocation__v_; +text: .text%__1cMMonitorValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cRC1_MacroAssemblerNunlock_object6MpnMRegisterImpl_22rnFLabel__v_; +text: .text%__1cPMonitorExitStubMis_call_stub6kM_i_: c1_CodeStubs_sparc.o; +text: .text%__1cQMonitorEnterStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cNLIR_AssemblerLmonitorexit6MpnMRegisterImpl_22i_v_; +text: .text%__1cPMonitorExitStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cRComputeEntryStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cMGraphBuilderIshift_op6MpnJValueType_nJBytecodesECode__v_; +text: .text%__1cHShiftOpFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerKdo_ShiftOp6MpnHShiftOp__v_; +text: .text%__1cHShiftOpEhash6kM_i_: c1_GraphBuilder.o; +text: .text%__1cHShiftOpEname6kM_pkc_: c1_GraphBuilder.o; +text: .text%__1cHLogicOpOis_commutative6kM_i_; +text: .text%__1cTconstantPoolOopDescbCverify_constant_pool_resolve6FnSconstantPoolHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cJIntrinsicFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerMdo_Intrinsic6MpnJIntrinsic__v_; +text: .text%__1cJIntrinsicMas_Intrinsic6M_p0_: c1_GraphBuilder.o; +text: .text%__1cJIntrinsicIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cMas_ValueType6FnKciConstant__pnJValueType__; +text: .text%__1cJIntrinsicPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorMdo_Intrinsic6MpnJIntrinsic__v_; +text: .text%__1cDOp2Pinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorKdo_ShiftOp6MpnHShiftOp__v_; +text: .text%__1cIValueGenKdo_ShiftOp6MpnHShiftOp__v_; +text: .text%__1cLLIR_EmitterIshift_op6MnJBytecodesECode_nFRInfo_pnLLIR_OprDesc_53_v_; +text: .text%__1cILIR_ListLshift_right6MnFRInfo_i1_v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListLshift_right6MpnLLIR_OprDesc_222_v_; +text: .text%__1cIValueGenMdo_Intrinsic6MpnJIntrinsic__v_; +text: .text%__1cIValueGenRspill_caller_save6M_v_; +text: .text%__1cIFrameMapVcaller_save_registers6F_pnPRInfoCollection__; +text: .text%__1cJIsFreeRegIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIValueGenXload_item_with_reg_mask6MpnEItem_nKc1_RegMask__v_; +text: .text%__1cIRegAllocNget_lock_temp6MpnLInstruction_nKc1_RegMask__nFRInfo__; +text: .text%__1cIValueGenQarraycopy_helper6MpnJIntrinsic_pippnMciArrayKlass__v_; +text: .text%__1cLInstructionKexact_type6kM_pnGciType__: c1_GraphBuilder.o; +text: .text%__1cOas_array_klass6FpnGciType__pnMciArrayKlass__: c1_CodeGenerator.o; +text: .text%__1cLInstructionNdeclared_type6kM_pnGciType__: c1_GraphBuilder.o; +text: .text%__1cMNewTypeArrayKexact_type6kM_pnGciType__; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cLInstructionNdeclared_type6kM_pnGciType__: c1_Instruction.o; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cLInstructionOas_ArrayLength6M_pnLArrayLength__: c1_GraphBuilder.o; +text: .text%__1cHHideReg2t6MpnIValueGen_nKc1_RegMask__v_; +text: .text%__1cHHideReg2T6M_v_; +text: .text%__1cPLIR_OpArrayCopyFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cPLIR_OpArrayCopyJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerOemit_arraycopy6MpnPLIR_OpArrayCopy__v_; +text: .text%__1cNLIR_AssemblerOemit_arraycopy6MpnPLIR_OpArrayCopy__v_; +text: .text%__1cMciArrayKlassMelement_type6M_pnGciType__; +text: .text%__1cQciTypeArrayKlassTis_type_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cNArrayCopyStub2t6MpnMCodeEmitInfo_pnOStaticCallStub__v_; +text: .text%__1cNArrayCopyStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cNArrayCopyStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%jni_GetEnv; +text: .text%jni_CallStaticBooleanMethod: jni.o; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlass.o; +text: .text%__1cKarrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cOtypeArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cOGenerateOopMapRdo_exception_edge6MpnOBytecodeStream__v_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: c1_IR.o; +text: .text%__1cMGraphBuilderQhandle_exception6Mi_v_; +text: .text%__1cOExceptionScopeFclear6M_v_; +text: .text%__1cLInstructionGmirror6Fn0AJCondition__1_; +text: .text%__1cKBlockBeginVadd_exception_handler6Mp0_v_; +text: .text%__1cOExceptionScopeLadd_handler6MpnIXHandler__v_; +text: .text%__1cIciObjectEhash6M_i_; +text: .text%__1cIciObjectMhas_encoding6M_i_; +text: .text%__1cLInstructionMas_LoadLocal6M_pnJLoadLocal__: c1_GraphBuilder.o; +text: .text%__1cLAccessFieldPother_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstance.o; +text: .text%__1cLLIR_EmitterNhandler_entry6M_v_; +text: .text%__1cILIR_ListDnop6MpnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cILIR_ListKnull_check6MnFRInfo_pnMCodeEmitInfo__v_: c1_CodeGenerator.o; +text: .text%__1cFChunk2n6FII_pv_; +text: .text%__1cOExceptionScopeKhandler_at6kMi_pnIXHandler__; +text: .text%__1cTExceptionRangeTableJadd_entry6Miiiiii_v_; +text: .text%__1cTExceptionRangeTableTentry_index_for_pco6kMi_i_; +text: .text%__1cTExceptionRangeTableIentry_at6kMi_pnTExceptionRangeEntry__; +text: .text%jni_CallStaticVoidMethodV: jni.o; +text: .text%JVM_GetLastErrorString; +text: .text%jni_Throw: jni.o; +text: .text%__1cKExceptionsK_throw_oop6FpnGThread_pkcipnHoopDesc__v_; +text: .text%JVM_DisableCompiler; +text: .text%__1cNinstanceKlassbFlookup_method_in_all_interfaces6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%JVM_Available; +text: .text%__1cLArrayLengthIis_equal6kMpnLInstruction__i_: c1_GraphBuilder.o; +text: .text%__1cLArrayLengthOas_ArrayLength6M_p0_: c1_GraphBuilder.o; +text: .text%__1cJLoadFieldKexact_type6kM_pnGciType__; +text: .text%__1cJLoadFieldNdeclared_type6kM_pnGciType__; +text: .text%__1cIRuntime1Uresolve_virtual_call6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cKoopFactoryUnew_compiledICHolder6FnMmethodHandle_nLKlassHandle_pnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cVcompiledICHolderKlassIallocate6MpnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cRInlineCacheBufferWcreate_transition_stub6FpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cKCompiledICWis_in_transition_state6kM_i_; +text: .text%__1cRInlineCacheBufferXassemble_ic_buffer_code6FpCpnHoopDesc_1_v_; +text: .text%__1cOMacroAssemblerEjump6MrnHAddress_ipkci_v_; +text: .text%__1cOMacroAssemblerFjumpl6MrnHAddress_pnMRegisterImpl_ipkci_v_; +text: .text%__1cKCompiledICSset_ic_destination6MpC_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlass.o; +text: .text%JVM_NewArray; +text: .text%__1cKReflectionRreflect_new_array6FpnHoopDesc_ipnGThread__pnMarrayOopDesc__; +text: .text%__1cNSignatureInfoIdo_float6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cNSignatureInfoJdo_double6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cWCountInterfacesClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cPfieldDescriptorRint_initial_value6kM_i_; +text: .text%__1cSSetupItableClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cJBytecodesRspecial_length_at6FpC_i_; +text: .text%__1cFBytesNget_native_u46FpC_I_: bytecodes.o; +text: .text%__1cSInterpreterRuntimeOmultianewarray6FpnKJavaThread_pi_v_; +text: .text%__1cNobjArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cNinstanceKlassSlookup_osr_nmethod6kMkpnNmethodOopDesc_i_pnHnmethod__; +text: .text%__1cQSimpleCompPolicyYmethod_back_branch_event6MnMmethodHandle_iipnGThread__v_; +text: .text%__1cICompilerMsupports_osr6M_i_: c1_Compiler.o; +text: .text%__1cMGraphBuilderQnew_object_array6M_v_; +text: .text%__1cONewObjectArrayFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerRdo_NewObjectArray6MpnONewObjectArray__v_; +text: .text%__1cPciObjArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cPciObjArrayKlassGloader6M_pnHoopDesc__: ciObjArrayKlass.o; +text: .text%__1cLLoadIndexedOas_LoadIndexed6M_p0_: c1_Instruction.o; +text: .text%__1cMArithmeticOpIis_equal6kMpnLInstruction__i_: c1_Instruction.o; +text: .text%__1cDOp2Gas_Op26M_p0_: c1_Instruction.o; +text: .text%__1cLInstructionMas_LoadField6M_pnJLoadField__: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorRdo_NewObjectArray6MpnONewObjectArray__v_; +text: .text%__1cIValueGenRdo_NewObjectArray6MpnONewObjectArray__v_; +text: .text%__1cLLIR_EmitterQnew_object_array6MnFRInfo_pnHciKlass_pnLLIR_OprDesc_11111pnMCodeEmitInfo_7_v_; +text: .text%__1cSNewObjectArrayStub2t6MnFRInfo_11pnMCodeEmitInfo__v_; +text: .text%__1cPciObjArrayKlassEmake6FpnHciKlass__p0_; +text: .text%__1cLLIR_EmitterZjobject2reg_with_patching6MnFRInfo_pnIciObject_pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListKshift_left6MnFRInfo_i1_v_: c1_LIREmitter.o; +text: .text%__1cSNewObjectArrayStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_sparc.o; +text: .text%__1cSNewObjectArrayStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cEBaseHas_Base6M_p0_: c1_IR.o; +text: .text%__1cNLIR_AssemblerOemit_osr_entry6MpnHIRScope_ipnFLabel_i_v_; +text: .text%__1cHIRScopeGlocals6M_pnJLocalList__; +text: .text%__1cSNewObjectArrayStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cNinstanceKlassPadd_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cUGenericGrowableArrayLraw_at_grow6MipknEGrET__pv_; +text: .text%__1cNSignatureInfoIdo_short6M_v_: bytecode.o; +text: .text%__1cICompilerPsupports_native6M_i_: c1_Compiler.o; +text: .text%__1cIciMethodMnative_entry6M_pC_; +text: .text%__1cLCompilationUemit_code_for_native6MpCpnLCodeOffsets__v_; +text: .text%__1cLCompilationXemit_code_prolog_native6MpnIFrameMap__v_; +text: .text%__1cNLIR_AssemblerRemit_method_entry6MpnLLIR_Emitter_pnHIRScope__v_; +text: .text%__1cNLIR_AssemblerMneeds_icache6kMpnIciMethod__i_; +text: .text%__1cNLIR_AssemblerQemit_native_call6MpCpnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerSpass_oop_to_native6MinHAddress_nIArgument__v_; +text: .text%__1cOMacroAssemblerOstore_argument6MpnMRegisterImpl_rnIArgument__v_: c1_LIRAssembler_sparc.o; +text: .text%__1cMCodeEmitInfobDcreate_oop_map_inside_natives6M_pnGOopMap__; +text: .text%__1cNLIR_AssemblerXemit_native_method_exit6MpnMCodeEmitInfo__v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: c1_LIRAssembler_sparc.o; +text: .text%__1cNLIR_AssemblerVsave_native_fp_result6MnJBasicType_nHAddress__v_; +text: .text%__1cNLIR_AssemblerYrestore_native_fp_result6MnJBasicType_nHAddress__v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: typeArrayKlass.o; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciObjectFactory.o; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: generateOopMap.o; +text: .text%__1cUBytecode_tableswitchOdest_offset_at6kMi_i_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: c1_GraphBuilder.o; +text: .text%__1cLInstructionKas_ShiftOp6M_pnHShiftOp__: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderMtable_switch6M_v_; +text: .text%__1cLTableSwitchFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerOdo_TableSwitch6MpnLTableSwitch__v_; +text: .text%__1cLInstructionJas_Return6M_pnGReturn__: c1_GraphBuilder.o; +text: .text%__1cGSwitchPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorOdo_TableSwitch6MpnLTableSwitch__v_; +text: .text%__1cILIR_ListUunsigned_shift_right6MnFRInfo_i1_v_: c1_LIREmitter.o; +text: .text%__1cIValueGenOdo_TableSwitch6MpnLTableSwitch__v_; +text: .text%__1cLLIR_EmitterOtableswitch_op6MpnLLIR_OprDesc_ipnKBlockBegin__v_; +text: .text%__1cIRuntime1Tresolve_static_call6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cSCompiledStaticCallNcompute_entry6FnMmethodHandle_rnOStaticCallInfo__v_; +text: .text%__1cSCompiledStaticCallIis_clean6kM_i_; +text: .text%__1cSCompiledStaticCallDset6MrknOStaticCallInfo__v_; +text: .text%__1cWstatic_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cJValueTypeQas_ClassConstant6M_pnNClassConstant__: c1_Canonicalizer.o; +text: .text%__1cIRuntime1Thandle_wrong_method6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cNSharedRuntimeTreresolve_call_site6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cFframeRis_compiled_frame6kMpi_i_; +text: .text%__1cHnmethodOis_java_method6kM_i_: nmethod.o; +text: .text%__1cGEventsDlog6FpkcE_v_: sharedRuntime.o; +text: .text%__1cJCodeCacheMfind_nmethod6Fpv_pnHnmethod__; +text: .text%__1cSCompiledStaticCallMset_to_clean6M_v_; +text: .text%__1cNSharedRuntimeSfind_callee_method6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cIciObjectIis_klass6M_i_: ciInstance.o; +text: .text%__1cQInstanceConstantTas_InstanceConstant6M_p0_: c1_ValueType.o; +text: .text%__1cQInstanceConstantIencoding6kM_pnI_jobject__; +text: .text%__1cLInstructionOas_ArrayLength6M_pnLArrayLength__: c1_Instruction.o; +text: .text%__1cILIR_ListQunwind_exception6MnFRInfo_1pnMCodeEmitInfo__v_: c1_CodeGenerator.o; +text: .text%__1cIRuntime1Tprimitive_arraycopy6FpnIHeapWord_2i_v_; +text: .text%__1cKCompiledICMset_to_clean6M_v_; +text: .text%jni_MonitorEnter: jni.o; +text: .text%__1cSObjectSynchronizerJjni_enter6FnGHandle_pnGThread__v_; +text: .text%jni_MonitorExit: jni.o; +text: .text%__1cSObjectSynchronizerIjni_exit6FpnHoopDesc_pnGThread__v_; +text: .text%jni_CallVoidMethod: jni.o; +text: .text%__1cXJNI_ArgumentPusherVaArgHget_int6M_v_: jni.o; +text: .text%jni_CallStaticBooleanMethodV: jni.o; +text: .text%JVM_GetStackTraceDepth; +text: .text%__1cTjava_lang_ThrowableVget_stack_trace_depth6FpnHoopDesc_pnGThread__i_; +text: .text%JVM_GetStackTraceElement; +text: .text%__1cTjava_lang_ThrowableXget_stack_trace_element6FpnHoopDesc_ipnGThread__2_; +text: .text%__1cbBjava_lang_StackTraceElementGcreate6FnMmethodHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cNmethodOopDescUline_number_from_bci6kMi_i_; +text: .text%__1cNObjectMonitorJnotifyAll6MpnGThread__v_; +text: .text%__1cLServiceUtilLvisible_oop6FpnHoopDesc__i_: objectMonitor_solaris.o; +text: .text%__1cNObjectMonitorGEnterI6MpnGThread__v_; +text: .text%JVM_EnableCompiler; +text: .text%__1cNFingerprinterHdo_bool6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cCosHSolarisFEventEpark6Mx_i_: objectMonitor_solaris.o; +text: .text%__1cJStubQdDueueKremove_all6M_v_; +text: .text%__1cPICStubInterfaceIfinalize6MpnEStub__v_: icBuffer.o; +text: .text%__1cRInlineCacheBufferUic_buffer_cached_oop6FpC_pnHoopDesc__; +text: .text%__1cRNativeMovConstRegEdata6kM_i_; +text: .text%__1cKCompiledICOset_cached_oop6MpnHoopDesc__v_; +text: .text%__1cOoop_RelocationSfix_oop_relocation6M_v_; +text: .text%__1cRInlineCacheBufferVic_buffer_entry_point6FpC_1_; +text: .text%__1cPICStubInterfaceEsize6kMpnEStub__i_: icBuffer.o; +text: .text%__1cUSafepointSynchronizeDend6F_v_; +text: .text%__1cCosbAmake_polling_page_readable6F_v_; +text: .text%__1cTAbstractInterpreterRignore_safepoints6F_v_; +text: .text%__1cGThreadQunboost_priority6Fp0_v_; +text: .text%__1cORuntimeServiceUrecord_safepoint_end6F_v_; +text: .text%__1cCosOunguard_memory6FpcI_i_; +text: .text%__1cKJavaThreadbScheck_safepoint_and_suspend_for_native_trans6Fp0_v_; +# Test LoadFrame +text: .text%__1cNSignatureInfoHdo_char6M_v_: reflection.o; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: reflection.o; +text: .text%__1cLInstructionKas_ShiftOp6M_pnHShiftOp__: c1_Instruction.o; +text: .text%__1cILIR_ListLlogical_xor6MnFRInfo_pnLLIR_OprDesc_1_v_: c1_LIREmitter.o; +text: .text%__1cKCompiledICOic_destination6kM_pC_; +text: .text%jni_CallObjectMethodV: jni.o; +text: .text%jni_SetObjectField: jni.o; +text: .text%__1cNObjectMonitorGenter26MpnGThread__v_; +text: .text%jni_IsInstanceOf: jni.o; +text: .text%jni_GetIntArrayRegion: jni.o; +text: .text%jni_SetIntArrayRegion: jni.o; +text: .text%jni_PushLocalFrame: jni.o; +text: .text%jni_PopLocalFrame: jni.o; +text: .text%jni_GetStaticObjectField: jni.o; +text: .text%__1cXJNI_ArgumentPusherVaArgIget_long6M_v_: jni.o; +text: .text%__1cINegateOpFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerLdo_NegateOp6MpnINegateOp__v_; +text: .text%__1cMLinkResolverbPlinktime_resolve_interface_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cPciInstanceKlassLimplementor6M_p0_; +text: .text%__1cINegateOpPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorLdo_NegateOp6MpnINegateOp__v_; +text: .text%__1cLLIR_EmitterRarray_store_check6MpnLLIR_OprDesc_2nFRInfo_33pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListLstore_check6MpnLLIR_OprDesc_2222pnMCodeEmitInfo__v_; +text: .text%__1cXArrayStoreExceptionStub2t6MpnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListDadd6MnFRInfo_i1_v_: c1_LIREmitter_sparc.o; +text: .text%__1cIValueGenLdo_NegateOp6MpnINegateOp__v_; +text: .text%__1cLLIR_EmitterGnegate6MnFRInfo_pnLLIR_OprDesc__v_; +text: .text%__1cILIR_ListGnegate6MnFRInfo_1_v_: c1_LIREmitter.o; +text: .text%__1cXArrayStoreExceptionStubFvisit6MpnQLIR_OpVisitState__v_: c1_CodeStubs_sparc.o; +text: .text%__1cNLIR_AssemblerbIadd_debug_info_for_null_check_here6MpnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerEload6MpnMRegisterImpl_2nFRInfo_nJBasicType__i_; +text: .text%__1cXArrayStoreExceptionStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cNLIR_AssemblerGnegate6MpnLLIR_OprDesc_2_v_; +text: .text%__1cOMacroAssemblerHbr_zero6MnJAssemblerJCondition_in0BHPredict_pnMRegisterImpl_rnFLabel__v_; +text: .text%__1cXArrayStoreExceptionStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cIRuntime1Jarraycopy6FpnHoopDesc_i2ii_i_; +text: .text%__1cHciKlassSsuper_check_offset6M_I_; +text: .text%__1cKCompiledICMstub_address6kM_pC_; +text: .text%__1cRInlineCacheBufferSic_destination_for6FpnKCompiledIC__pC_; +text: .text%jni_NewWeakGlobalRef: jni.o; +text: .text%__1cKJNIHandlesQmake_weak_global6FnGHandle__pnI_jobject__; +text: .text%__1cMLinkResolverbBlookup_method_in_interfaces6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%jni_CallIntMethodV: jni.o; +text: .text%__1cIRuntime1Ohandle_ic_miss6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cNSharedRuntimeVhandle_ic_miss_helper6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2t6M_v_; +text: .text%__1cKCompiledICOis_megamorphic6kM_i_; +text: .text%__1cLVtableStubsOis_entry_point6FpC_i_; +text: .text%__1cKCompiledICSset_to_megamorphic6MpnICallInfo_nJBytecodesECode_pnGThread__v_; +text: .text%__1cLVtableStubsLcreate_stub6FiipnNmethodOopDesc__pC_; +text: .text%__1cLVtableStubsScreate_vtable_stub6Fii_pnKVtableStub__; +text: .text%__1cKVtableStub2n6FIi_pv_; +text: .text%__1cKVtableStubRpd_code_alignment6F_i_; +text: .text%__1cGEventsDlog6FpkcE_v_: compiledIC.o; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2T6M_v_; +text: .text%Unsafe_GetObject; +text: .text%jni_CallBooleanMethod: jni.o; +text: .text%jni_CallVoidMethodV: jni.o; +text: .text%JVM_GetClassDeclaredMethods; +text: .text%__1cSconstMethodOopDescYchecked_exceptions_start6kM_pnXCheckedExceptionElement__; +text: .text%JVM_InvokeMethod; +text: .text%__1cKReflectionNinvoke_method6FpnHoopDesc_nGHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cYjava_lang_reflect_MethodFclazz6FpnHoopDesc__2_; +text: .text%__1cYjava_lang_reflect_MethodEslot6FpnHoopDesc__i_; +text: .text%__1cYjava_lang_reflect_MethodPparameter_types6FpnHoopDesc__2_; +text: .text%__1cYjava_lang_reflect_MethodLreturn_type6FpnHoopDesc__2_; +text: .text%JVM_IsInterrupted; +text: .text%__1cFMutexbLwait_for_lock_blocking_implementation6MpnKJavaThread__v_; +# Test LoadJFrame +text: .text%__1cMArithmeticOpKlock_stack6kM_pnKValueStack__: c1_Instruction.o; +text: .text%__1cLLIR_EmitterParithmetic_idiv6MnJBytecodesECode_pnLLIR_OprDesc_44nFRInfo_pnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListEirem6MnFRInfo_111pnMCodeEmitInfo__v_; +text: .text%__1cHLIR_Op3Fvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cHLIR_Op3Jemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerIemit_op36MpnHLIR_Op3__v_; +text: .text%__1cNLIR_AssemblerIemit_op36MpnHLIR_Op3__v_; +text: .text%__1cNLIR_AssemblerbCadd_debug_info_for_div0_here6MpnMCodeEmitInfo__v_; +text: .text%__1cNDivByZeroStubEinfo6kM_pnMCodeEmitInfo__: c1_CodeStubs_sparc.o; +text: .text%__1cNDivByZeroStubJemit_code6MpnNLIR_Assembler__v_; +text: .text%__1cJFloatTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cJFloatTypeEbase6kM_pnJValueType__: c1_ValueType.o; +text: .text%__1cIValueGenTdo_ArithmeticOp_FPU6MpnMArithmeticOp__v_; +text: .text%__1cHLockRegIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cQChangeSpillCountIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cHFreeRegIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cLLIR_EmitterRarithmetic_op_fpu6MnJBytecodesECode_pnLLIR_OprDesc_44i_v_; +text: .text%__1cILIR_ListDmul6MpnLLIR_OprDesc_22_v_: c1_LIREmitter.o; +text: .text%__1cLGetRefCountIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIFrameMapPadd_spill_slots6Mi_v_; +text: .text%__1cFRInfoMas_float_reg6kM_pnRFloatRegisterImpl__; +text: .text%__1cIFrameMapLnr2floatreg6Fi_pnRFloatRegisterImpl__; +text: .text%__1cOMacroAssemblerCfb6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: c1_LIRAssembler_sparc.o; +text: .text%__1cRCompilationPolicybIreset_counter_for_invocation_event6MnMmethodHandle__v_; +text: .text%__1cMStoreIndexedPother_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%Unsafe_EnsureClassInitialized; +text: .text%__1cPClassFileParserYparse_checked_exceptions6MpHInSconstantPoolHandle_pnGThread__1_; +text: .text%__1cPClassFileStreamHskip_u26MipnGThread__v_; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cNSignatureInfoIdo_short6M_v_: c1_Runtime1_sparc.o; +text: .text%Unsafe_StaticFieldOffset; +text: .text%Unsafe_StaticFieldBaseFromField; +text: .text%Unsafe_GetIntVolatile; +text: .text%JVM_Yield; +text: .text%__1cCosKdont_yield6F_i_; +# Test JHello +text: .text%__1cNSharedRuntimeElmul6Fxx_x_; +text: .text%__1cYjava_lang_reflect_MethodNset_signature6FpnHoopDesc_2_v_; +text: .text%JVM_InitializeSocketLibrary; +text: .text%__1cDhpiZinitialize_socket_library6F_i_; +text: .text%JVM_Socket; +text: .text%Unsafe_PageSize; +text: .text%__1cXNativeSignatureIteratorHdo_byte6M_v_: interpreterRT_sparc.o; +text: .text%Unsafe_SetMemory; +text: .text%__1cNSharedRuntimeElrem6Fxx_x_; +text: .text%__1cRComputeEntryStackHdo_byte6M_v_: generateOopMap.o; +text: .text%__1cQComputeCallStackHdo_byte6M_v_: generateOopMap.o; +text: .text%__1cMLinkResolverbEresolve_interface_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cGciTypeNis_subtype_of6Mp0_i_; +text: .text%__1cPParameterMapperHdo_byte6M_v_: c1_Runtime1_sparc.o; +text: .text%Unsafe_DefineClass1; +text: .text%JVM_DefineClass; +text: .text%jni_NewDirectByteBuffer; +text: .text%__1cHJNIEnv_JNewObject6MpnH_jclass_pnK_jmethodID_E_pnI_jobject__: jni.o; +text: .text%jni_GetDoubleArrayRegion: jni.o; +text: .text%__1cNSignatureInfoJdo_double6M_v_: bytecode.o; +text: .text%__1cXJNI_ArgumentPusherVaArgJget_float6M_v_: jni.o; +text: .text%__1cFKlassQup_cast_abstract6M_p0_; +text: .text%__1cNSharedRuntimeDd2i6Fd_i_; +text: .text%jni_GetLongField: jni.o; +text: .text%__1cPfieldDescriptorTfloat_initial_value6kM_f_; +text: .text%__1cPClassFileParserbBcheck_illegal_static_method6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cRappend_interfaces6FnOobjArrayHandle_ripnPobjArrayOopDesc__v_; +text: .text%__1cSInterpreterRuntimeWslow_signature_handler6FpnKJavaThread_pnNmethodOopDesc_pi5_pC_; +text: .text%__1cUSlowSignatureHandlerLpass_object6M_v_: interpreterRT_sparc.o; +text: .text%__1cUSlowSignatureHandlerNadd_signature6Mi_v_: interpreterRT_sparc.o; +text: .text%__1cXNativeSignatureIteratorIdo_array6Mii_v_: interpreterRT_sparc.o; +text: .text%__1cUSlowSignatureHandlerIpass_int6M_v_: interpreterRT_sparc.o; +text: .text%jni_GetFloatArrayRegion: jni.o; +text: .text%jni_GetCharArrayRegion: jni.o; +text: .text%jni_SetFloatField: jni.o; +text: .text%jni_NewFloatArray: jni.o; +text: .text%jni_SetFloatArrayRegion: jni.o; +text: .text%__1cNFingerprinterGdo_int6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cOBasicHashtableJnew_entry6MI_pnTBasicHashtableEntry__; +# SwingSet +text: .text%JVM_GetFieldIxModifiers; +text: .text%JVM_GetCPFieldClassNameUTF; +text: .text%JVM_GetCPFieldModifiers; +text: .text%__1cPClassFileParserUverify_constantvalue6MiinSconstantPoolHandle_pnGThread__v_; +text: .text%__1cPfieldDescriptorUstring_initial_value6kMpnGThread__pnHoopDesc__; +text: .text%__1cCosHrealloc6FpvI_1_; +text: .text%__1cHnmethodIis_alive6kM_i_: nmethod.o; +text: .text%__1cHnmethodPis_dependent_on6MpnMklassOopDesc__i_; +text: .text%JVM_MonitorNotify; +text: .text%__1cSObjectSynchronizerGnotify6FnGHandle_pnGThread__v_; +text: .text%__1cNFingerprinterJdo_object6Mii_v_: c1_Runtime1_sparc.o; +text: .text%jni_NewIntArray: jni.o; +text: .text%__1cbCTwoGenerationCollectorPolicybMshould_try_older_generation_allocation6kMI_i_; +text: .text%__1cQGenCollectedHeapSattempt_allocation6MIiii_pnIHeapWord__; +text: .text%__1cQDefNewGenerationIallocate6MIii_pnIHeapWord__: defNewGeneration.o; +text: .text%__1cKGenerationInext_gen6kM_p0_; +text: .text%__1cKGenerationYallocation_limit_reached6MpnFSpace_pnIHeapWord_I_4_: tenuredGeneration.o; +text: .text%__1cPVM_GC_OperationNdoit_prologue6M_i_; +text: .text%__1cQinstanceRefKlassZacquire_pending_list_lock6FpnJBasicLock__v_; +text: .text%__1cXjava_lang_ref_ReferenceWpending_list_lock_addr6F_ppnHoopDesc__; +text: .text%__1cSObjectSynchronizerKfast_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cPVM_GC_OperationQgc_count_changed6kM_i_; +text: .text%__1cbAVM_GenCollectForAllocationEname6kM_pkc_: vm_operations.o; +text: .text%__1cbAVM_GenCollectForAllocationEdoit6M_v_; +text: .text%__1cNJvmtiGCMarker2t6Mi_v_; +text: .text%__1cQGenCollectedHeapZsatisfy_failed_allocation6MIiipi_pnIHeapWord__; +text: .text%__1cbCTwoGenerationCollectorPolicyZsatisfy_failed_allocation6MIiipi_pnIHeapWord__; +text: .text%__1cQGenCollectedHeapNdo_collection6MiiIiiipi_v_; +text: .text%__1cXTraceMemoryManagerStats2t6Mi_v_; +text: .text%__1cPGCMemoryManagerIgc_begin6M_v_; +text: .text%__1cKManagementJtimestamp6F_x_; +text: .text%__1cTContiguousSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cTContiguousSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cbBSurvivorContiguousSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cbBSurvivorContiguousSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cOGenerationPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cOGenerationPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cQGenCollectedHeapLgc_prologue6Mi_v_; +text: .text%__1cNCollectedHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cWThreadLocalAllocBufferbFaccumulate_statistics_before_gc6F_v_; +text: .text%__1cQGenCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapOfill_all_tlabs6M_v_; +text: .text%__1cbCGenEnsureParseabilityClosureNdo_generation6MpnKGeneration__v_: genCollectedHeap.o; +text: .text%__1cKGenerationTensure_parseability6M_v_: defNewGeneration.o; +text: .text%__1cKGenerationTensure_parseability6M_v_: tenuredGeneration.o; +text: .text%__1cKGenerationTensure_parseability6M_v_: compactingPermGenGen.o; +text: .text%__1cSAllocationProfilerViterate_since_last_gc6F_v_; +text: .text%__1cUGenGCPrologueClosureNdo_generation6MpnKGeneration__v_: genCollectedHeap.o; +text: .text%__1cQDefNewGenerationLgc_prologue6Mi_v_: defNewGeneration.o; +text: .text%__1cRTenuredGenerationLgc_prologue6Mi_v_; +text: .text%__1cKGenerationLgc_prologue6Mi_v_: compactingPermGenGen.o; +text: .text%__1cKGenerationOshould_collect6MiIii_i_: defNewGeneration.o; +text: .text%__1cQDefNewGenerationKshort_name6kM_pkc_: defNewGeneration.o; +text: .text%__1cKGenerationIcounters6M_pnRCollectorCounters__: defNewGeneration.o; +text: .text%__1cQDefNewGenerationKsave_marks6M_v_; +text: .text%__1cbCOneContigSpaceCardGenerationKsave_marks6M_v_; +text: .text%__1cQDefNewGenerationHcollect6MiiIii_v_; +text: .text%__1cRTenuredGenerationZpromotion_attempt_is_safe6kMIi_i_; +text: .text%__1cKGenerationYmax_contiguous_available6kM_I_; +text: .text%__1cbCOneContigSpaceCardGenerationUcontiguous_available6kM_I_; +text: .text%__1cMVirtualSpaceQuncommitted_size6kM_I_; +text: .text%__1cIageTableFclear6M_v_; +text: .text%__1cLCardTableRSbGprepare_for_younger_refs_iterate6Mi_v_; +text: .text%__1cULRUCurrentHeapPolicy2t6M_v_; +text: .text%__1cPCollectorPolicyPis_train_policy6M_i_: collectorPolicy.o; +text: .text%__1cQGenCollectedHeapUprocess_strong_roots6Miiin0ATClassScanningOption_pnQOopsInGenClosure_3_v_; +text: .text%__1cKSharedHeapbAchange_strong_roots_parity6M_v_; +text: .text%__1cMSubTasksDonePis_task_claimed6Mi_i_; +text: .text%__1cPFastScanClosureGdo_oop6MppnHoopDesc__v_: defNewGeneration.o; +text: .text%__1cQDefNewGenerationWcopy_to_survivor_space6MpnHoopDesc_p2_2_; +text: .text%__1cPContiguousSpaceIallocate6MI_pnIHeapWord__; +text: .text%__1cKJNIHandlesHoops_do6FpnKOopClosure__v_; +text: .text%__1cHThreadsHoops_do6FpnKOopClosure__v_; +text: .text%__1cKJavaThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cOJNIHandleBlockHoops_do6MpnKOopClosure__v_; +text: .text%__1cKHandleAreaHoops_do6MpnKOopClosure__v_; +text: .text%__1cQStackFrameStream2t6MpnKJavaThread_i_v_; +text: .text%__1cFframeQoops_do_internal6MpnKOopClosure_pnLRegisterMap_i_v_; +text: .text%__1cFframeToops_interpreted_do6MpnKOopClosure_pknLRegisterMap_i_v_; +text: .text%__1cFframeVinterpreter_frame_bci6kM_i_; +text: .text%__1cRInterpreterOopMap2t6M_v_; +text: .text%__1cNmethodOopDescImask_for6MipnRInterpreterOopMap__v_; +text: .text%__1cNinstanceKlassImask_for6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cLOopMapCache2t6M_v_; +text: .text%__1cLOopMapCacheGlookup6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cQOopMapCacheEntryEfill6MnMmethodHandle_i_v_; +text: .text%__1cTOopMapForCacheEntryLcompute_map6MpnGThread__v_; +text: .text%__1cTOopMapForCacheEntryRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cOGenerateOopMapPdo_monitorenter6Mi_v_; +text: .text%__1cOGenerateOopMapXreplace_all_CTS_matches6MnNCellTypeState_1_v_; +text: .text%__1cOGenerateOopMapMmonitor_push6MnNCellTypeState__v_; +text: .text%__1cOGenerateOopMapOdo_monitorexit6Mi_v_; +text: .text%__1cOGenerateOopMapLmonitor_pop6M_nNCellTypeState__; +text: .text%__1cTOopMapForCacheEntryOreport_results6kM_i_: oopMapCache.o; +text: .text%__1cOGenerateOopMapVresult_for_basicblock6Mi_v_; +text: .text%__1cTOopMapForCacheEntryZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cQOopMapCacheEntryIset_mask6MpnNCellTypeState_2i_v_; +text: .text%__1cRInterpreterOopMapNresource_copy6MpnQOopMapCacheEntry__v_; +text: .text%__1cRInterpreterOopMapLiterate_oop6MpnNOffsetClosure__v_; +text: .text%__1cXInterpreterFrameClosureJoffset_do6Mi_v_: frame.o; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: frame.o; +text: .text%__1cRInterpreterOopMap2T6M_v_; +text: .text%__1cFframeNoops_entry_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cPJavaCallWrapperHoops_do6MpnKOopClosure__v_; +text: .text%__1cQOopMapCacheEntryPfill_for_native6M_v_; +text: .text%__1cTMaskFillerForNative2t6MnMmethodHandle_pIi_v_: oopMapCache.o; +text: .text%__1cNFingerprinterLfingerprint6M_X_: oopMapCache.o; +text: .text%__1cTMaskFillerForNativeLpass_object6M_v_: oopMapCache.o; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_: oopMapCache.o; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: oopMapCache.o; +text: .text%__1cTMaskFillerForNativeJpass_long6M_v_: oopMapCache.o; +text: .text%__1cRComputeEntryStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cIVMThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cGThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cSObjectSynchronizerHoops_do6FpnKOopClosure__v_; +text: .text%__1cMFlatProfilerHoops_do6FpnKOopClosure__v_; +text: .text%__1cKManagementHoops_do6FpnKOopClosure__v_; +text: .text%__1cNMemoryServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cKMemoryPoolHoops_do6MpnKOopClosure__v_; +text: .text%__1cNMemoryManagerHoops_do6MpnKOopClosure__v_; +text: .text%__1cNThreadServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cLJvmtiExportHoops_do6FpnKOopClosure__v_; +text: .text%__1cXJvmtiCurrentBreakpointsHoops_do6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryHoops_do6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryRpreloaded_oops_do6FpnKOopClosure__v_; +text: .text%__1cKDictionaryHoops_do6MpnKOopClosure__v_; +text: .text%__1cQPlaceholderTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cVLoaderConstraintTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cUCompactingPermGenGenUyounger_refs_iterate6MpnQOopsInGenClosure__v_; +text: .text%__1cbCOneContigSpaceCardGenerationUyounger_refs_iterate6MpnQOopsInGenClosure__v_; +text: .text%__1cLCardTableRSbDyounger_refs_in_space_iterate6MpnFSpace_pnQOopsInGenClosure__v_; +text: .text%__1cPContiguousSpaceLnew_dcto_cl6MpnKOopClosure_nRCardTableModRefBSOPrecisionStyle_pnIHeapWord__pnVDirtyCardToOopClosure__; +text: .text%__1cPContiguousSpaceZused_region_at_save_marks6kM_nJMemRegion__: space.o; +text: .text%__1cRCardTableModRefBSWnon_clean_card_iterate6MpnFSpace_nJMemRegion_pnVDirtyCardToOopClosure_pnQMemRegionClosure_i_v_; +text: .text%__1cRCardTableModRefBSbBnon_clean_card_iterate_work6MnJMemRegion_pnQMemRegionClosure_i_v_; +text: .text%__1cJMemRegionMintersection6kMk0_0_; +text: .text%__1cYClearNoncleanCardWrapperMdo_MemRegion6MnJMemRegion__v_: cardTableRS.o; +text: .text%__1cVDirtyCardToOopClosureMdo_MemRegion6MnJMemRegion__v_; +text: .text%__1cWOffsetTableContigSpaceLblock_start6kMpkv_pnIHeapWord__: space.o; +text: .text%__1cbBBlockOffsetArrayContigSpaceSblock_start_unsafe6kMpkv_pnIHeapWord__; +text: .text%__1cPContiguousSpaceKblock_size6kMpknIHeapWord__I_; +text: .text%__1cQconstMethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cWconstantPoolCacheKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cUContiguousSpaceDCTOCOget_actual_top6MpnIHeapWord_2_2_; +text: .text%__1cPContiguousSpaceRtoContiguousSpace6M_p0_: space.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: cpCacheKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: cpCacheKlass.o; +text: .text%__1cPFiltering_DCTOCPwalk_mem_region6MnJMemRegion_pnIHeapWord_3_v_; +text: .text%__1cUContiguousSpaceDCTOCXwalk_mem_region_with_cl6MnJMemRegion_pnIHeapWord_3pnQFilteringClosure__v_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: methodKlass.o; +text: .text%__1cLmethodKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cQFilteringClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: instanceKlassKlass.o; +text: .text%__1cSinstanceKlassKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cNinstanceKlassViterate_static_fields6MpnKOopClosure__v_; +text: .text%__1cLklassVtablePoop_oop_iterate6MpnKOopClosure__v_; +text: .text%__1cNinstanceKlassGitable6kM_pnLklassItable__; +text: .text%__1cLklassItablePoop_oop_iterate6MpnKOopClosure__v_; +text: .text%__1cKklassKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cKOopClosureXshould_remember_klasses6kM_ki_: space.o; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cNinstanceKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: cpCacheKlass.o; +text: .text%__1cWconstantPoolCacheKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cWConstantPoolCacheEntryNoop_iterate_m6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cLsymbolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: symbolKlass.o; +text: .text%__1cLsymbolKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cNobjArrayKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: methodKlass.o; +text: .text%__1cLmethodKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: constantPoolKlass.o; +text: .text%__1cRconstantPoolKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: symbolKlass.o; +text: .text%__1cLsymbolKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: cpCacheKlass.o; +text: .text%__1cWconstantPoolCacheKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cWConstantPoolCacheEntryLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: constantPoolKlass.o; +text: .text%__1cRconstantPoolKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cSobjArrayKlassKlassIoop_size6kMpnHoopDesc__i_: objArrayKlassKlass.o; +text: .text%__1cKarrayKlassLobject_size6kMi_i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cParrayKlassKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constMethodKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constMethodKlass.o; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cVcompiledICHolderKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_: compiledICHolderKlass.o; +text: .text%__1cVcompiledICHolderKlassPoop_oop_iterate6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: instanceKlassKlass.o; +text: .text%__1cSinstanceKlassKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cNinstanceKlassViterate_static_fields6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cLklassVtableRoop_oop_iterate_m6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cLklassItableRoop_oop_iterate_m6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cKklassKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cKOopClosureIdo_oop_v6MppnHoopDesc__v_: space.o; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure_nJMemRegion__v_; +text: .text%__1cNinstanceKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_; +text: .text%__1cNobjArrayKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlass.o; +text: .text%__1cLCardTableRSUyounger_refs_iterate6MpnKGeneration_pnQOopsInGenClosure__v_; +text: .text%__1cMSubTasksDoneTall_tasks_completed6M_v_; +text: .text%__1cQGenCollectedHeapbCoop_since_save_marks_iterate6MipnPFastScanClosure_2_v_; +text: .text%__1cQDefNewGenerationbFoop_since_save_marks_iterate_nv6MpnPFastScanClosure__v_; +text: .text%__1cPContiguousSpacebFoop_since_save_marks_iterate_nv6MpnPFastScanClosure__v_; +text: .text%__1cNinstanceKlassSoop_oop_iterate_nv6MpnHoopDesc_pnPFastScanClosure__i_; +text: .text%__1cFKlassSoop_oop_iterate_nv6MpnHoopDesc_pnPFastScanClosure__i_: typeArrayKlass.o; +text: .text%__1cKGenerationHpromote6MpnHoopDesc_Ip2_2_; +text: .text%__1cbCOneContigSpaceCardGenerationIallocate6MIii_pnIHeapWord__: tenuredGeneration.o; +text: .text%__1cNobjArrayKlassSoop_oop_iterate_nv6MpnHoopDesc_pnPFastScanClosure__i_; +text: .text%__1cQinstanceRefKlassSoop_oop_iterate_nv6MpnHoopDesc_pnPFastScanClosure__i_; +text: .text%__1cXjava_lang_ref_ReferenceNreferent_addr6FpnHoopDesc__p2_; +text: .text%__1cSReferenceProcessorSdiscover_reference6MpnHoopDesc_nNReferenceType__i_; +text: .text%__1cXjava_lang_ref_ReferenceJnext_addr6FpnHoopDesc__p2_; +text: .text%__1cXjava_lang_ref_ReferencePdiscovered_addr6FpnHoopDesc__p2_; +text: .text%__1cSReferenceProcessorTget_discovered_list6MnNReferenceType__ppnHoopDesc__; +text: .text%__1cbCOneContigSpaceCardGenerationbFoop_since_save_marks_iterate_nv6MpnPFastScanClosure__v_; +text: .text%__1cQGenCollectedHeapbAno_allocs_since_save_marks6Mi_i_; +text: .text%__1cQDefNewGenerationbAno_allocs_since_save_marks6M_i_; +text: .text%__1cbCOneContigSpaceCardGenerationbAno_allocs_since_save_marks6M_i_; +text: .text%__1cQDefNewGenerationQKeepAliveClosure2t6MpnSScanWeakRefClosure__v_; +text: .text%__1cbDReferenceProcessorInitializerIis_clean6kM_v_: concurrentMarkSweepGeneration.o; +text: .text%__1cSReferenceProcessorbDprocess_discovered_references6M_v_; +text: .text%__1cSReferenceProcessorbAprocess_discovered_reflist6MppnHoopDesc_pnPReferencePolicy_i_v_; +text: .text%__1cQDefNewGenerationOIsAliveClosureLdo_object_b6MpnHoopDesc__i_; +text: .text%__1cULRUCurrentHeapPolicyWshould_clear_reference6MpnHoopDesc__i_; +text: .text%__1cbBjava_lang_ref_SoftReferenceFclock6F_x_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJtimestamp6FpnHoopDesc__x_; +text: .text%__1cXjava_lang_ref_ReferenceIset_next6FpnHoopDesc_2_v_; +text: .text%__1cQDefNewGenerationUFastKeepAliveClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cQDefNewGenerationbCFastEvacuateFollowersClosureHdo_void6M_v_; +text: .text%__1cSReferenceProcessorQprocess_phaseJNI6M_v_; +text: .text%__1cKJNIHandlesMweak_oops_do6FpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cIageTablebAcompute_tenuring_threshold6MI_i_; +text: .text%__1cKGenerationWupdate_time_of_last_gc6Mx_v_: defNewGeneration.o; +text: .text%__1cSReferenceProcessorbDenqueue_discovered_references6M_i_; +text: .text%__1cXjava_lang_ref_ReferenceRpending_list_addr6F_ppnHoopDesc__; +text: .text%__1cSReferenceProcessorbBenqueue_discovered_reflists6MppnHoopDesc__v_; +text: .text%__1cSReferenceProcessorbAenqueue_discovered_reflist6MpnHoopDesc_p2_v_; +text: .text%__1cKGenerationPupdate_gc_stats6Mii_v_: defNewGeneration.o; +text: .text%__1cRTenuredGenerationPupdate_gc_stats6Mii_v_; +text: .text%__1cVAdaptivePaddedAverageGsample6Mf_v_; +text: .text%__1cKGenerationPupdate_gc_stats6Mii_v_: compactingPermGenGen.o; +text: .text%__1cRTenuredGenerationOshould_collect6MiIii_i_; +text: .text%__1cKGenerationPshould_allocate6MIii_i_: tenuredGeneration.o; +text: .text%__1cbCOneContigSpaceCardGenerationEfree6kM_I_; +text: .text%__1cQDefNewGenerationQcompute_new_size6M_v_; +text: .text%__1cNMemoryServiceStrack_memory_usage6F_v_; +text: .text%__1cRLowMemoryDetectorRdetect_low_memory6F_v_; +text: .text%__1cQGenCollectedHeapLgc_epilogue6Mi_v_; +text: .text%__1cNCollectedHeapQresize_all_tlabs6M_v_; +text: .text%__1cWThreadLocalAllocBufferQresize_all_tlabs6F_v_; +text: .text%__1cUGenGCEpilogueClosureNdo_generation6MpnKGeneration__v_: genCollectedHeap.o; +text: .text%__1cQDefNewGenerationLgc_epilogue6Mi_v_; +text: .text%__1cRTenuredGenerationLgc_epilogue6Mi_v_; +text: .text%__1cbCOneContigSpaceCardGenerationLgc_epilogue6Mi_v_; +text: .text%__1cRTenuredGenerationPupdate_counters6M_v_; +text: .text%__1cUCompactingPermGenGenPupdate_counters6M_v_; +text: .text%__1cXTraceMemoryManagerStats2T6M_v_; +text: .text%__1cPGCMemoryManagerGgc_end6M_v_; +text: .text%__1cRLowMemoryDetectorWdetect_after_gc_memory6FpnKMemoryPool__v_; +text: .text%__1cNJvmtiGCMarker2T6M_v_; +text: .text%__1cPVM_GC_OperationNdoit_epilogue6M_v_; +text: .text%__1cQinstanceRefKlassbKrelease_and_notify_pending_list_lock6FipnJBasicLock__v_; +text: .text%__1cSObjectSynchronizerJfast_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%Unsafe_CompareAndSwapObject; +text: .text%__1cLVtableStubsScreate_itable_stub6Fii_pnKVtableStub__; +text: .text%__1cLLIR_EmitterDnop6M_v_; +text: .text%__1cMLinkResolverbEvtable_index_of_miranda_method6FnLKlassHandle_nMsymbolHandle_2pnGThread__i_; +text: .text%__1cLklassVtableQindex_of_miranda6MpnNsymbolOopDesc_2_i_; +text: .text%__1cIValueGenSload_item_patching6MpnHIRScope_ipnEItem_pnKValueStack_pnOExceptionScope__v_; +text: .text%__1cJValueTypeTas_InstanceConstant6M_pnQInstanceConstant__: c1_ValueType.o; +text: .text%__1cPParameterMapperHdo_bool6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cTGeneratePairingInfoRpossible_gc_point6MpnOBytecodeStream__i_: ciMethod.o; +text: .text%__1cTGeneratePairingInfoOreport_results6kM_i_: ciMethod.o; +text: .text%__1cMGraphBuilderMmonitorenter6MpnLInstruction__v_; +text: .text%__1cMMonitorEnterFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerPdo_MonitorEnter6MpnMMonitorEnter__v_; +text: .text%__1cNAccessMonitorIcan_trap6kM_i_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderLmonitorexit6MpnLInstruction__v_; +text: .text%__1cLMonitorExitFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerOdo_MonitorExit6MpnLMonitorExit__v_; +text: .text%__1cILongTypeDtag6kM_nIValueTag__: c1_Canonicalizer.o; +text: .text%__1cILongTypeEsize6kM_i_: c1_Canonicalizer.o; +text: .text%__1cNAccessMonitorPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorPdo_MonitorEnter6MpnMMonitorEnter__v_; +text: .text%__1cQNullCheckVisitorOdo_MonitorExit6MpnLMonitorExit__v_; +text: .text%__1cIValueGenPdo_MonitorEnter6MpnMMonitorEnter__v_; +text: .text%__1cMLongConstantPas_LongConstant6M_p0_: c1_Canonicalizer.o; +text: .text%__1cLLIR_EmitterJopr2intLo6MpnLLIR_OprDesc__i_; +text: .text%__1cLLIR_EmitterJopr2intHi6MpnLLIR_OprDesc__i_; +text: .text%__1cQArgumentLocationVstack_offset_in_words6kM_i_; +text: .text%__1cLLIR_EmitterVstore_stack_parameter6MpnLLIR_OprDesc_i_v_; +text: .text%__1cIValueGenOdo_MonitorExit6MpnLMonitorExit__v_; +text: .text%__1cNAccessMonitorQas_AccessMonitor6M_p0_: c1_GraphBuilder.o; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: c1_MacroAssembler_sparc.o; +text: .text%JVM_IsSameClassPackage; +text: .text%__1cKReflectionVis_same_class_package6FpnMklassOopDesc_2_i_; +text: .text%__1cIRuntime1Mmonitorenter6FpnKJavaThread_pnHoopDesc_pnPBasicObjectLock__v_; +text: .text%__1cIRuntime1Lmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%Unsafe_GetNativeFloat; +text: .text%__1cQComputeCallStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cRComputeEntryStackIdo_short6M_v_: generateOopMap.o; +text: .text%Unsafe_AllocateInstance; +text: .text%jni_AllocObject: jni.o; +text: .text%__1cNCanonicalizerMset_constant6Mi_v_: c1_Canonicalizer.o; +text: .text%__1cJTypeCheckPother_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cIBytecodeIset_code6MnJBytecodesECode__v_; +text: .text%__1cbDreorder_based_on_method_index6FpnPobjArrayOopDesc_1ppnHoopDesc__v_: methodOop.o; +text: .text%__1cRC1_MacroAssemblerTfast_ObjectHashCode6MpnMRegisterImpl_2_v_; +text: .text%__1cNFingerprinterHdo_char6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cPParameterMapperHdo_char6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cQComputeCallStackIdo_float6M_v_: generateOopMap.o; +text: .text%__1cJCodeCacheIcontains6Fpv_i_; +text: .text%__1cFframeRoops_code_blob_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cJOopMapSetHoops_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cJOopMapSetGall_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure_pFppnHoopDesc_9E_v9B9B_v_; +text: .text%__1cICodeBlobbAoop_map_for_return_address6MpCi_pnGOopMap__; +text: .text%__1cJOopMapSetSfind_map_at_offset6kMii_pnGOopMap__; +text: .text%__1cFframeVoopmapreg_to_location6kMnFVMRegEName_pknLRegisterMap__ppnHoopDesc__; +text: .text%__1cMOopMapStreamJfind_next6M_v_; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: defNewGeneration.o; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: nmethod.o; +text: .text%__1cJOopMapSetTupdate_register_map6FpknFframe_pnICodeBlob_pnLRegisterMap__v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceRefKlass.o; +text: .text%__1cQinstanceRefKlassSoop_oop_iterate_nv6MpnHoopDesc_pnQFilteringClosure__i_; +text: .text%__1cQinstanceRefKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_; +text: .text%__1cQGenCollectedHeapXhandle_failed_promotion6MpnKGeneration_pnHoopDesc_Ip4_4_; +text: .text%__1cbCOneContigSpaceCardGenerationTexpand_and_allocate6MIiii_pnIHeapWord__; +text: .text%__1cNGCMutexLocker2t6MpnFMutex__v_; +text: .text%__1cbCOneContigSpaceCardGenerationHgrow_by6MI_i_; +text: .text%__1cWBlockOffsetSharedArrayGresize6MI_v_; +text: .text%__1cPContiguousSpaceNmangle_region6MnJMemRegion__v_; +text: .text%__1cXjava_lang_ref_ReferenceOset_discovered6FpnHoopDesc_2_v_; +text: .text%__1cRTenuredGenerationKshort_name6kM_pkc_: tenuredGeneration.o; +text: .text%__1cKGenerationIcounters6M_pnRCollectorCounters__: tenuredGeneration.o; +text: .text%__1cRTenuredGenerationHcollect6MiiIii_v_; +text: .text%__1cbCOneContigSpaceCardGenerationHcollect6MiiIii_v_; +text: .text%__1cMGenMarkSweepTinvoke_at_safepoint6FipnSReferenceProcessor_i_v_; +text: .text%__1cJCodeCacheLgc_prologue6F_v_; +text: .text%__1cHThreadsLgc_prologue6F_v_; +text: .text%__1cRframe_gc_prologue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeLgc_prologue6M_v_; +text: .text%__1cQGenCollectedHeapRsave_used_regions6Mii_v_; +text: .text%__1cKGenerationQsave_used_region6M_v_: tenuredGeneration.o; +text: .text%__1cbCOneContigSpaceCardGenerationLused_region6kM_nJMemRegion__; +text: .text%__1cPContiguousSpaceLused_region6kM_nJMemRegion__: space.o; +text: .text%__1cKGenerationQsave_used_region6M_v_: defNewGeneration.o; +text: .text%__1cKGenerationLused_region6kM_nJMemRegion__: defNewGeneration.o; +text: .text%__1cKGenerationQsave_used_region6M_v_: compactingPermGenGen.o; +text: .text%__1cQGenCollectedHeapOgather_scratch6MpnKGeneration_I_pnMScratchBlock__; +text: .text%__1cQDefNewGenerationScontribute_scratch6MrpnMScratchBlock_pnKGeneration_I_v_; +text: .text%__1cKGenerationScontribute_scratch6MrpnMScratchBlock_p0I_v_: tenuredGeneration.o; +text: .text%__1cJEventMark2t6MpkcE_v_: genMarkSweep.o; +text: .text%__1cJMarkSweepRFollowRootClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cParrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLklassVtableToop_follow_contents6M_v_; +text: .text%__1cJMarkSweepO_mark_and_push6FppnHoopDesc__v_; +text: .text%__1cKklassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJMarkSweepXrevisit_weak_klass_link6FpnFKlass__v_; +text: .text%__1cNinstanceKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cSinstanceKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNinstanceKlassUfollow_static_fields6M_v_; +text: .text%__1cLklassItableToop_follow_contents6M_v_; +text: .text%__1cOtypeArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMjniIdMapBaseHoops_do6MpnKOopClosure__v_; +text: .text%__1cJMarkSweepSMarkAndPushClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cRconstantPoolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWconstantPoolCacheKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWConstantPoolCacheEntryPfollow_contents6M_v_; +text: .text%__1cLmethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNobjArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJMarkSweepPmark_and_follow6FppnHoopDesc__v_; +text: .text%__1cSobjArrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLsymbolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQinstanceRefKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQconstMethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cFJNIidHoops_do6MpnKOopClosure__v_; +text: .text%__1cJMarkSweepRFollowRootClosureLdo_nmethods6kM_ki_: markSweep.o; +text: .text%__1cHnmethodHoops_do6MpnKOopClosure__v_; +text: .text%__1cHnmethodOis_not_entrant6kM_i_: nmethod.o; +text: .text%__1cQSystemDictionaryValways_strong_oops_do6FpnKOopClosure__v_; +text: .text%__1cKDictionaryYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cVLoaderConstraintTableYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cJMarkSweepOIsAliveClosureLdo_object_b6MpnHoopDesc__i_: markSweep.o; +text: .text%__1cJMarkSweepQKeepAliveClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cJMarkSweepSFollowStackClosureHdo_void6M_v_: markSweep.o; +text: .text%__1cQSystemDictionaryMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cKDictionaryMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cVLoaderConstraintTableYpurge_loader_constraints6MpnRBoolObjectClosure__v_; +text: .text%__1cJMarkSweepMfollow_stack6F_v_; +text: .text%__1cJCodeCacheMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cKBufferBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cNSingletonBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cNSingletonBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cLRuntimeStubIis_alive6kM_i_: codeBlob.o; +text: .text%__1cLRuntimeStubbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cHnmethodbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cVcompiledICHolderKlassSoop_being_unloaded6MpnRBoolObjectClosure_pnHoopDesc__i_; +text: .text%__1cVcompiledICHolderKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJMarkSweepXfollow_weak_klass_links6F_v_; +text: .text%__1cFKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cNinstanceKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cJHashtableGunlink6MpnRBoolObjectClosure__v_; +text: .text%__1cQGenCollectedHeapWprepare_for_compaction6M_v_; +text: .text%__1cKGenerationWprepare_for_compaction6MpnMCompactPoint__v_; +text: .text%__1cbCOneContigSpaceCardGenerationWfirst_compaction_space6kM_pnQCompactibleSpace__: tenuredGeneration.o; +text: .text%__1cPContiguousSpaceWprepare_for_compaction6MpnMCompactPoint__v_; +text: .text%__1cWOffsetTableContigSpaceUinitialize_threshold6M_pnIHeapWord__; +text: .text%__1cMTenuredSpaceSallowed_dead_ratio6kM_i_; +text: .text%__1cQCompactibleSpaceHforward6MpnHoopDesc_IpnMCompactPoint_pnIHeapWord__6_; +text: .text%__1cWOffsetTableContigSpacePcross_threshold6MpnIHeapWord_2_2_; +text: .text%__1cbBBlockOffsetArrayContigSpaceQalloc_block_work6MpnIHeapWord_2_v_; +text: .text%__1cQCompactibleSpaceVnext_compaction_space6kM_p0_: space.o; +text: .text%__1cQDefNewGenerationWfirst_compaction_space6kM_pnQCompactibleSpace__: defNewGeneration.o; +text: .text%__1cQCompactibleSpaceSallowed_dead_ratio6kM_i_: space.o; +text: .text%__1cQCompactibleSpaceUinitialize_threshold6M_pnIHeapWord__: space.o; +text: .text%__1cbCOneContigSpaceCardGenerationWfirst_compaction_space6kM_pnQCompactibleSpace__: compactingPermGenGen.o; +text: .text%__1cPContigPermSpaceSallowed_dead_ratio6kM_i_; +text: .text%__1cUCompactingPermGenGenTpre_adjust_pointers6M_v_; +text: .text%__1cbIRecursiveAdjustSharedObjectClosureGdo_oop6MppnHoopDesc__v_: compactingPermGenGen.o; +text: .text%__1cFKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_: methodKlass.o; +text: .text%__1cFKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_: constantPoolKlass.o; +text: .text%__1cNinstanceKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cFKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_: instanceKlassKlass.o; +text: .text%__1cKOopClosureIdo_oop_v6MppnHoopDesc__v_: compactingPermGenGen.o; +text: .text%__1cNinstanceKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cFKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_: objArrayKlassKlass.o; +text: .text%__1cNobjArrayKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_; +text: .text%__1cKOopClosureXshould_remember_klasses6kM_ki_: compactingPermGenGen.o; +text: .text%__1cFKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_: klassKlass.o; +text: .text%__1cKklassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cSobjArrayKlassKlassOklass_oop_size6kM_i_: objArrayKlassKlass.o; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: klassKlass.o; +text: .text%__1cKklassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSobjArrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cParrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLklassVtableToop_adjust_pointers6M_v_; +text: .text%__1cFKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cFKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_: cpCacheKlass.o; +text: .text%__1cWconstantPoolCacheKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cWConstantPoolCacheEntryPadjust_pointers6M_v_; +text: .text%__1cRconstantPoolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNobjArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSinstanceKlassKlassOklass_oop_size6kM_i_: instanceKlassKlass.o; +text: .text%__1cSinstanceKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassUadjust_static_fields6M_v_; +text: .text%__1cLklassItableToop_adjust_pointers6M_v_; +text: .text%__1cJMarkSweepUAdjustPointerClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cTtypeArrayKlassKlassIoop_size6kMpnHoopDesc__i_: typeArrayKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassOklass_oop_size6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cLsymbolKlassOklass_oop_size6kM_i_: symbolKlass.o; +text: .text%__1cLmethodKlassOklass_oop_size6kM_i_: methodKlass.o; +text: .text%__1cQconstMethodKlassOklass_oop_size6kM_i_: constMethodKlass.o; +text: .text%__1cPmethodDataKlassOklass_oop_size6kM_i_: methodDataKlass.o; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: arrayKlassKlass.o; +text: .text%__1cFKlassRoop_oop_iterate_v6MpnHoopDesc_pnKOopClosure__i_: arrayKlassKlass.o; +text: .text%__1cRconstantPoolKlassOklass_oop_size6kM_i_: constantPoolKlass.o; +text: .text%__1cWconstantPoolCacheKlassOklass_oop_size6kM_i_: cpCacheKlass.o; +text: .text%__1cVcompiledICHolderKlassOklass_oop_size6kM_i_: compiledICHolderKlass.o; +text: .text%__1cJHashtableHoops_do6MpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryYalways_strong_classes_do6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryPplaceholders_do6FpnKOopClosure__v_; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: markSweep.o; +text: .text%__1cQGenCollectedHeapSprocess_weak_roots6MpnKOopClosure_2_v_; +text: .text%__1cRAlwaysTrueClosureLdo_object_b6MpnHoopDesc__i_: genCollectedHeap.o; +text: .text%__1cSDeoptimizationBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cLRuntimeStubHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cNSafepointBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cbASkipAdjustingSharedStringsGdo_oop6MppnHoopDesc__v_: genCollectedHeap.o; +text: .text%__1cSReferenceProcessorPoops_do_statics6FpnKOopClosure__v_; +text: .text%__1cSReferenceProcessorHoops_do6MpnKOopClosure__v_; +text: .text%__1cJMarkSweepMadjust_marks6F_v_; +text: .text%__1cQGenCollectedHeapSgeneration_iterate6Mpn0AKGenClosure_i_v_; +text: .text%__1cYGenAdjustPointersClosureNdo_generation6MpnKGeneration__v_: genMarkSweep.o; +text: .text%__1cKGenerationPadjust_pointers6M_v_; +text: .text%__1cbCOneContigSpaceCardGenerationNspace_iterate6MpnMSpaceClosure_i_v_; +text: .text%__1cVAdjustPointersClosureIdo_space6MpnFSpace__v_: generation.o; +text: .text%__1cQCompactibleSpacePadjust_pointers6M_v_; +text: .text%__1cQinstanceRefKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQDefNewGenerationNspace_iterate6MpnMSpaceClosure_i_v_; +text: .text%__1cUCompactingPermGenGenPadjust_pointers6M_v_; +text: .text%__1cLsymbolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQconstMethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cVcompiledICHolderKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cUCompactingPermGenGenHcompact6M_v_; +text: .text%__1cQCompactibleSpaceHcompact6M_v_; +text: .text%__1cPContiguousSpaceWreset_after_compaction6M_v_: space.o; +text: .text%__1cRGenCompactClosureNdo_generation6MpnKGeneration__v_: genMarkSweep.o; +text: .text%__1cKGenerationHcompact6M_v_; +text: .text%__1cUCompactingPermGenGenMpost_compact6M_v_; +text: .text%__1cJMarkSweepNrestore_marks6F_v_; +text: .text%__1cQGenCollectedHeapKsave_marks6M_v_; +text: .text%__1cLCardTableRSTinvalidate_or_clear6MpnKGeneration_ii_v_; +text: .text%__1cJMemRegionFminus6kMk0_0_; +text: .text%__1cLCardTableRSKinvalidate6MnJMemRegion__v_: cardTableRS.o; +text: .text%__1cRCardTableModRefBSKinvalidate6MnJMemRegion__v_; +text: .text%__1cHThreadsLgc_epilogue6F_v_; +text: .text%__1cRframe_gc_epilogue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeLgc_epilogue6M_v_; +text: .text%__1cFframeMpd_gc_epilog6M_v_; +text: .text%__1cJCodeCacheLgc_epilogue6F_v_; +text: .text%__1cICodeBlobTfix_oop_relocations6M_v_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: c1_Runtime1.o; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: relocInfo.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: onStackReplacement.o; +text: .text%__1cIUniverseWupdate_heap_info_at_gc6F_v_; +text: .text%__1cKGenerationWupdate_time_of_last_gc6Mx_v_: tenuredGeneration.o; +text: .text%__1cKGenerationWupdate_time_of_last_gc6Mx_v_: compactingPermGenGen.o; +text: .text%__1cRTenuredGenerationQcompute_new_size6M_v_; +text: .text%__1cKGenerationEspec6M_pnOGenerationSpec__; +text: .text%__1cbCOneContigSpaceCardGenerationGexpand6MII_v_; +text: .text%jni_DeleteWeakGlobalRef: jni.o; +text: .text%__1cKJNIHandlesTdestroy_weak_global6FpnI_jobject__v_; +text: .text%__1cHLogicOpIis_equal6kMpnLInstruction__i_: c1_Instruction.o; +text: .text%__1cLAccessFieldKlock_stack6kM_pnKValueStack__: c1_GraphBuilder.o; +text: .text%__1cLLIR_EmitterLcmp_mem_int6MnMLIR_OpBranchNLIR_Condition_nFRInfo_iipnMCodeEmitInfo__v_; +text: .text%__1cILIR_ListDcmp6MnMLIR_OpBranchNLIR_Condition_pnLLIR_OprDesc_4pnMCodeEmitInfo__v_: c1_LIREmitter_sparc.o; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cJFloatTypeDtag6kM_nIValueTag__: c1_Canonicalizer.o; +text: .text%__1cNFloatConstantQas_FloatConstant6M_p0_: c1_Canonicalizer.o; +text: .text%__1cILIR_ListJfloat2reg6MfnFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cJFloatTypeMas_FloatType6M_p0_: c1_Canonicalizer.o; +text: .text%__1cRAbstractAssemblerGa_long6Mi_v_; +text: .text%__1cNConstantTableZaddress_of_float_constant6Mf_pC_; +text: .text%__1cIRuntime1Onew_type_array6FpnKJavaThread_pnMklassOopDesc_i_v_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: oopMap.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: compiledICHolderKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: compiledICHolderKlass.o; +text: .text%__1cFKlassUoop_oop_iterate_nv_m6MpnHoopDesc_pnQFilteringClosure_nJMemRegion__i_: compiledICHolderKlass.o; +text: .text%__1cVcompiledICHolderKlassRoop_oop_iterate_m6MpnHoopDesc_pnKOopClosure_nJMemRegion__i_; +text: .text%__1cNObjectMonitorGnotify6MpnGThread__v_; +text: .text%__1cOMacroAssemblerEmult6MpnMRegisterImpl_22_v_; +text: .text%__1cINewArrayPother_values_do6MpFppnLInstruction__v_v_; +text: .text%__1cJValueTypeLas_LongType6M_pnILongType__: c1_Canonicalizer.o; +text: .text%__1cNLIR_AssemblerIshift_op6MnILIR_Code_nFRInfo_222_v_; +text: .text%__1cNVM_DeoptimizeEname6kM_pkc_: vm_operations.o; +text: .text%__1cNVM_DeoptimizeEdoit6M_v_; +text: .text%__1cODeoptimizationVdeoptimize_dependents6F_i_; +text: .text%__1cHThreadsbFdeoptimized_wrt_marked_nmethods6F_v_; +text: .text%__1cKJavaThreadbFdeoptimized_wrt_marked_nmethods6M_v_; +text: .text%__1cFframeVshould_be_deoptimized6kM_i_; +text: .text%__1cICodeBlobOis_java_method6kM_i_: codeBlob.o; +text: .text%__1cJCodeCachebGmake_marked_nmethods_not_entrant6F_v_; +text: .text%__1cHnmethodbAmake_not_entrant_or_zombie6Mi_v_; +text: .text%__1cHnmethodNis_osr_method6kM_i_: nmethod.o; +text: .text%__1cKNativeJumpUpatch_verified_entry6FpC11_v_; +text: .text%__1cMVM_OperationNdoit_epilogue6M_v_: vm_operations.o; +text: .text%signalHandler; +text: .text%JVM_handle_solaris_signal; +text: .text%__1cJCodeCacheQfind_blob_unsafe6Fpv_pnICodeBlob__; +text: .text%__1cILIR_ListEidiv6MnFRInfo_i11pnMCodeEmitInfo__v_; +text: .text%__1cPParameterMapperHdo_long6M_v_: c1_Runtime1_sparc.o; +text: .text%__1cLLoadIndexedIis_equal6kMpnLInstruction__i_: c1_Instruction.o; +text: .text%JVM_HoldsLock; +text: .text%__1cSObjectSynchronizerZcurrent_thread_holds_lock6FpnKJavaThread_nGHandle__i_; +text: .text%__1cIValueGenLdo_getClass6MpnJIntrinsic__v_; +text: .text%__1cLLIR_EmitterIgetClass6MnFRInfo_1pnMCodeEmitInfo__v_; +text: .text%__1cQinstanceRefKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceRefKlass.o; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceRefKlass.o; +text: .text%__1cJCodeCacheFfirst6F_pnICodeBlob__; +text: .text%__1cHThreadsLnmethods_do6F_v_; +text: .text%__1cFframeLnmethods_do6M_v_; +text: .text%__1cJCodeCacheEnext6FpnICodeBlob__2_; +text: .text%__1cONewObjectArrayKexact_type6kM_pnGciType__; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cPciObjArrayKlassSis_obj_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cPciObjArrayKlassNelement_klass6M_pnHciKlass__; +text: .text%__1cIRuntime1Noop_arraycopy6FpnIHeapWord_2i_v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlass.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: objArrayKlass.o; +text: .text%__1cMGraphBuilderKcompare_op6MpnJValueType_nJBytecodesECode__v_; +text: .text%__1cJCompareOpFvisit6MpnSInstructionVisitor__v_: c1_Instruction.o; +text: .text%__1cNCanonicalizerMdo_CompareOp6MpnJCompareOp__v_; +text: .text%__1cJCompareOpEhash6kM_i_: c1_Instruction.o; +text: .text%__1cJCompareOpEname6kM_pkc_: c1_Instruction.o; +text: .text%__1cJCompareOpMas_CompareOp6M_p0_: c1_Instruction.o; +text: .text%__1cCIf2t6MpnLInstruction_n0BJCondition_i2pnKBlockBegin_5pnKValueStack_i_v_: c1_Canonicalizer.o; +text: .text%__1cILIR_ListVvolatile_load_mem_reg6MnFRInfo_i1nJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cJValueTypeOas_IntConstant6M_pnLIntConstant__: c1_Canonicalizer.o; +text: .text%__1cEItemSget_jlong_constant6kM_x_; +text: .text%__1cRLIR_PeepholeStateLreg2indexLo6MpnLLIR_OprDesc__i_; +text: .text%__1cRLIR_PeepholeStateLreg2indexHi6MpnLLIR_OprDesc__i_; +text: .text%__1cNLIR_AssemblerQvolatile_move_op6MpnLLIR_OprDesc_2nJBasicType_nHLIR_Op1NLIR_PatchCode_pnMCodeEmitInfo__v_; +text: .text%__1cHIntTypeKas_IntType6M_p0_: c1_Canonicalizer.o; +text: .text%__1cLLIR_EmitterQfield_store_long6MpnLLIR_OprDesc_i2ipnMCodeEmitInfo__v_; +text: .text%__1cNSharedRuntimeDf2l6Ff_x_; +text: .text%__1cJFloatTypeEsize6kM_i_: c1_ValueType.o; +text: .text%__1cJValueTypeQas_FloatConstant6M_pnNFloatConstant__: c1_Canonicalizer.o; +text: .text%__1cKDoubleTypeDtag6kM_nIValueTag__: c1_Canonicalizer.o; +text: .text%__1cODoubleConstantRas_DoubleConstant6M_p0_: c1_Canonicalizer.o; +text: .text%__1cKDoubleTypeDtag6kM_nIValueTag__: c1_ValueType.o; +text: .text%__1cKDoubleTypeEbase6kM_pnJValueType__: c1_Canonicalizer.o; +text: .text%__1cODoubleConstantLis_constant6kM_i_: c1_Canonicalizer.o; +text: .text%__1cGSetRegIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIValueGenNreturnF0RInfo6F_nFRInfo__; +text: .text%__1cILIR_ListMbranch_float6MnMLIR_OpBranchNLIR_Condition_pnFLabel_4_v_; +text: .text%__1cJFloatTypeMas_FloatType6M_p0_: c1_ValueType.o; +text: .text%__1cLLIR_EmitterOset_fpu_result6MnFRInfo__v_; +text: .text%__1cILIR_ListIpush_fpu6MnFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cNc1_AllocTableNhas_pair_free6kM_i_; +text: .text%__1cNc1_AllocTableNget_pair_free6M_i_; +text: .text%__1cHLockRegJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocRset_locked_double6MipnLInstruction_i_v_; +text: .text%__1cNc1_AllocTablePset_pair_locked6Mi_v_; +text: .text%__1cQChangeSpillCountJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocZchange_double_spill_count6Mii_v_; +text: .text%__1cILIR_ListKdouble2reg6MdnFRInfo__v_: c1_LIREmitter.o; +text: .text%__1cHFreeRegJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocPset_free_double6Mi_v_; +text: .text%__1cNc1_AllocTableNset_pair_free6Mi_v_; +text: .text%__1cIValueGenPlock_spill_temp6MpnLInstruction_nFRInfo__v_; +text: .text%__1cLLIR_EmitterPcall_convert_op6MnJBytecodesECode_nFRInfo__v_; +text: .text%__1cILIR_ListRcall_runtime_leaf6MpCnFRInfo_ii_v_: c1_LIREmitter_sparc.o; +text: .text%__1cMLIR_OpRTCallFvisit6MpnQLIR_OpVisitState__v_; +text: .text%__1cMLIR_OpRTCallJemit_code6MpnVLIR_AbstractAssembler__v_; +text: .text%__1cNLIR_OptimizerLemit_rtcall6MpnMLIR_OpRTCall__v_; +text: .text%__1cKDoubleTypeNas_DoubleType6M_p0_: c1_Canonicalizer.o; +text: .text%__1cNLIR_AssemblerFstore6MpnRFloatRegisterImpl_pnMRegisterImpl_inJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerEload6MpnMRegisterImpl_ipnRFloatRegisterImpl_nJBasicType_pnMCodeEmitInfo__v_; +text: .text%__1cNLIR_AssemblerIfpu_push6MnFRInfo__v_; +text: .text%__1cFRInfoNas_double_reg6kM_pnRFloatRegisterImpl__; +text: .text%__1cNConstantTablebAaddress_of_double_constant6Md_pC_; +text: .text%__1cNLIR_AssemblerLemit_rtcall6MpnMLIR_OpRTCall__v_; +text: .text%__1cNLIR_AssemblerHrt_call6MpCnFRInfo_ii_v_; +text: .text%__1cPciObjArrayKlassJmake_impl6FpnHciKlass__p0_; +text: .text%__1cHnmethodPis_locked_by_vm6kM_i_: nmethod.o; +text: .text%__1cHnmethodLis_unloaded6kM_i_: nmethod.o; +text: .text%__1cHnmethodVcleanup_inline_caches6M_v_; +text: .text%__1cKCompiledIC2t6MpnKRelocation__v_; +text: .text%__1cRComputeEntryStackIdo_float6M_v_: generateOopMap.o; +text: .text%__1cIValueGenMreturn2RInfo6F_nFRInfo__; +text: .text%__1cILIR_ListRcall_runtime_leaf6MpCnFRInfo_ii_v_: c1_CodeGenerator_sparc.o; +text: .text%__1cIFrameMapUare_adjacent_indeces6kMii_i_; +text: .text%__1cILIR_ListQreg2double_stack6MnFRInfo_inJBasicType__v_: c1_LIREmitter.o; +text: .text%__1cIValueGenUdo_ArithmeticOp_Long6MpnMArithmeticOp__v_; +text: .text%__1cLLIR_EmitterSarithmetic_op_long6MnJBytecodesECode_pnLLIR_OprDesc_44pnMCodeEmitInfo__v_; +text: .text%__1cRLIR_PeepholeStateNstack2indexHi6MpnLLIR_OprDesc__i_; +text: .text%__1cHnmethodbCcan_not_entrant_be_converted6M_i_; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_: oopMapCache.o; +text: .text%__1cTMaskFillerForNativeIpass_int6M_v_: oopMapCache.o; +text: .text%__1cHnmethodSflush_dependencies6MpnRBoolObjectClosure__v_; +text: .text%__1cNinstanceKlassYremove_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cFVTuneOdelete_nmethod6FpnHnmethod__v_; +text: .text%__1cFframebDoops_interpreted_arguments_do6MnMsymbolHandle_ipnKOopClosure__v_; +text: .text%__1cLCardTableRSSclear_into_younger6MpnKGeneration_i_v_; +text: .text%__1cLCardTableRSFclear6MnJMemRegion__v_: cardTableRS.o; +text: .text%__1cRCardTableModRefBSFclear6MnJMemRegion__v_; +text: .text%__1cHnmethodFflush6M_v_; +text: .text%__1cJEventMark2t6MpkcE_v_: nmethod.o; +text: .text%__1cICodeBlobFflush6M_v_; +text: .text%__1cJCodeCacheEfree6FpnICodeBlob__v_; +text: .text%__1cICodeHeapKdeallocate6Mpv_v_; +text: .text%__1cQComputeCallStackJdo_double6M_v_: generateOopMap.o; +text: .text%__1cNSharedRuntimeEdrem6Fdd_d_; +text: .text%__1cRComputeEntryStackJdo_double6M_v_: generateOopMap.o; +text: .text%__1cKDoubleTypeEbase6kM_pnJValueType__: c1_ValueType.o; +text: .text%__1cKDoubleTypeEsize6kM_i_: c1_ValueType.o; +text: .text%__1cKDoubleTypeNas_DoubleType6M_p0_: c1_ValueType.o; +text: .text%__1cLLIR_EmitterSarithmetic_call_op6MnJBytecodesECode_nFRInfo__v_; +text: .text%__1cILIR_ListRcall_runtime_leaf6MpCnFRInfo_ii_v_: c1_LIREmitter.o; +text: .text%__1cGSetRegJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocOset_double_reg6MiipnLInstruction__v_; +text: .text%__1cJIsFreeRegJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocOis_free_double6kMi_i_; +text: .text%__1cNc1_AllocTableMis_pair_free6kMi_i_; +text: .text%__1cNc1_AllocTableIare_free6kMii_i_; +text: .text%__1cLGetValueForIdo_float6Mi_v_: c1_RegAlloc.o; +text: .text%__1cLGetRefCountJdo_double6Mi_v_: c1_RegAlloc.o; +text: .text%__1cIRegAllocNget_double_rc6kMi_i_; +text: .text%__1cJValueTypeMas_ArrayType6M_pnJArrayType__: c1_Canonicalizer.o; +text: .text%__1cKDoubleTypeEsize6kM_i_: c1_Canonicalizer.o; +text: .text%__1cOMacroAssemblerEfneg6MnRFloatRegisterImplFWidth_p13_v_; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: frame.o; +text: .text%__1cRArgumentOopFinderDset6MinJBasicType__v_: frame.o; +text: .text%__1cQArgumentLocationXincoming_stack_location6kM_nHAddress__; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_: oopMapCache.o; +text: .text%__1cNFloatConstantLis_constant6kM_i_: c1_Canonicalizer.o; +text: .text%__1cJNullCheckMas_NullCheck6M_p0_: c1_GraphBuilder.o; +text: .text%__1cLLIR_EmitterIpop_item6MpnLLIR_OprDesc__v_; +text: .text%__1cIValueGenNreturnD0RInfo6F_nFRInfo__; +text: .text%__1cILIR_ListDdiv6MpnLLIR_OprDesc_22pnMCodeEmitInfo__v_: c1_LIREmitter.o; +text: .text%__1cHnmethodbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cFframebAoops_compiled_arguments_do6MnMsymbolHandle_ipknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cKGC_SupportbDpreserve_callee_argument_oops6FknFframe_nMsymbolHandle_ipnKOopClosure__v_; +text: .text%__1cSFindSignatureTypesDset6MinJBasicType__v_: c1_Runtime1_sparc.o; +text: .text%__1cUC1_ArgumentOopFinderDset6MinJBasicType__v_: c1_Runtime1_sparc.o; +text: .text%__1cILongTypeEbase6kM_pnJValueType__: c1_Canonicalizer.o; +text: .text%__1cMLongConstantLis_constant6kM_i_: c1_Canonicalizer.o; +text: .text%__1cRPrivilegedElementHoops_do6MpnKOopClosure__v_; +text: .text%__1cIValueGenLspill_value6MpnLInstruction__v_; +text: .text%__1cTunsafe_intrinsic_id6FpnNsymbolOopDesc_1_nNmethodOopDescLIntrinsicId__; +text: .text%__1cMGraphBuilderVappend_unsafe_put_raw6MpnIciMethod_nJBasicType__i_; +text: .text%__1cMUnsafePutRawFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerPdo_UnsafePutRaw6MpnMUnsafePutRaw__v_; +text: .text%__1cFmatch6FpnLUnsafeRawOp_ppnLInstruction_4pi_i_: c1_Canonicalizer.o; +text: .text%__1cIUnsafeOpLas_UnsafeOp6M_p0_: c1_GraphBuilder.o; +text: .text%__1cMGraphBuilderVappend_unsafe_get_raw6MpnIciMethod_nJBasicType__i_; +text: .text%__1cMUnsafeGetRawFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerPdo_UnsafeGetRaw6MpnMUnsafeGetRaw__v_; +text: .text%__1cMGraphBuilderNlookup_switch6M_v_; +text: .text%__1cMLookupSwitchFvisit6MpnSInstructionVisitor__v_: c1_GraphBuilder.o; +text: .text%__1cNCanonicalizerPdo_LookupSwitch6MpnMLookupSwitch__v_; +text: .text%__1cMUnsafePutRawPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorPdo_UnsafePutRaw6MpnMUnsafePutRaw__v_; +text: .text%__1cLUnsafeRawOpPinput_values_do6MpFppnLInstruction__v_v_: c1_GraphBuilder.o; +text: .text%__1cQNullCheckVisitorPdo_UnsafeGetRaw6MpnMUnsafeGetRaw__v_; +text: .text%__1cQNullCheckVisitorPdo_LookupSwitch6MpnMLookupSwitch__v_; +text: .text%__1cIValueGenPdo_UnsafePutRaw6MpnMUnsafePutRaw__v_; +text: .text%__1cLLIR_EmitterOput_raw_unsafe6MpnLLIR_OprDesc_2i2nJBasicType__v_; +text: .text%__1cLLIR_EmitterMlong2address6MpnLLIR_OprDesc__nFRInfo__; +text: .text%__1cILIR_ListNstore_mem_reg6MnFRInfo_pnLLIR_Address_nJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cIValueGenPdo_UnsafeGetRaw6MpnMUnsafeGetRaw__v_; +text: .text%__1cLLIR_EmitterOget_raw_unsafe6MnFRInfo_pnLLIR_OprDesc_3inJBasicType__v_; +text: .text%__1cILIR_ListMload_mem_reg6MpnLLIR_Address_nFRInfo_nJBasicType_pnMCodeEmitInfo_nHLIR_Op1NLIR_PatchCode__v_; +text: .text%__1cIValueGenPdo_LookupSwitch6MpnMLookupSwitch__v_; +text: .text%__1cLLIR_EmitterPlookupswitch_op6MpnLLIR_OprDesc_ipnKBlockBegin__v_; +text: .text%__1cQInstanceConstantLis_constant6kM_i_: c1_ValueType.o; +text: .text%__1cOObjectConstantLis_constant6kM_i_: c1_ValueType.o; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cIRuntime1Mnew_instance6FpnKJavaThread_pnMklassOopDesc__v_; +text: .text%__1cNSharedRuntimeEldiv6Fxx_x_; +text: .text%Unsafe_GetObjectVolatile; +text: .text%__1cGThreadOis_Java_thread6kM_i_: vmThread.o; +text: .text%__1cIValueGenTcallee_return2RInfo6F_nFRInfo__; +text: .text%__1cILongTypeLas_LongType6M_p0_: c1_Canonicalizer.o; +text: .text%__1cJFloatTypeEsize6kM_i_: c1_Canonicalizer.o; +text: .text%__1cIValueGenNrelease_roots6MpnKValueStack__v_; +text: .text%__1cSciExceptionHandlerLcatch_klass6M_pnPciInstanceKlass__; +text: .text%__1cHciKlassNis_subtype_of6Mp0_i_; +text: .text%__1cFciEnvWis_dependence_violated6FpnMklassOopDesc_pnNmethodOopDesc__i_; +text: .text%__1cFciEnvZcall_has_multiple_targets6FpnNinstanceKlass_nMsymbolHandle_3ri_i_; +text: .text%__1cbCOneContigSpaceCardGenerationVunsafe_max_alloc_nogc6kM_I_; +text: .text%__1cILongTypeLas_LongType6M_p0_: c1_ValueType.o; +text: .text%__1cHciKlassIis_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cPfieldDescriptorSlong_initial_value6kM_x_; +text: .text%__1cNSharedRuntimeDd2l6Fd_x_; +text: .text%__1cIRuntime1Qnew_object_array6FpnKJavaThread_pnMklassOopDesc_i_v_; diff --git a/hotspot/build/solaris/makefiles/reorder_COMPILER1_sparcv9 b/hotspot/build/solaris/makefiles/reorder_COMPILER1_sparcv9 new file mode 100644 index 00000000000..1d2684a6685 --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_COMPILER1_sparcv9 @@ -0,0 +1 @@ +# Place holder for LP64 data. diff --git a/hotspot/build/solaris/makefiles/reorder_COMPILER2_amd64 b/hotspot/build/solaris/makefiles/reorder_COMPILER2_amd64 new file mode 100644 index 00000000000..b4bc7db5b72 --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_COMPILER2_amd64 @@ -0,0 +1,8114 @@ +data = R0x2000; +text = LOAD ?RXO; + + +text: .text%__1cECopyRpd_disjoint_words6FpnIHeapWord_2L_v_; +text: .text%__1cSPSPromotionManagerWcopy_to_survivor_space6MpnHoopDesc__2_; +text: .text%__1cNinstanceKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cJMarkSweepO_mark_and_push6FppnHoopDesc__v_; +text: .text%__1cNinstanceKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cOtypeArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cOtypeArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQIndexSetIteratorEnext6M_I_; +text: .text%__1cJMarkSweepPmark_and_follow6FppnHoopDesc__v_; +text: .text%__1cCosOjavaTimeMillis6F_x_; +text: .text%__1cNRelocIteratorEnext6M_i_; +text: .text%__1cQIndexSetIteratorQadvance_and_next6M_I_; +text: .text%__1cIUniverseMnon_oop_word6F_pv_; +text: .text%__1cNobjArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNobjArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNobjArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQObjectStartArrayMobject_start6kMpnIHeapWord__2_; +text: .text%__1cJMarkSweepUAdjustPointerClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cJMarkSweepOIsAliveClosureLdo_object_b6MpnHoopDesc__i_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__; +text: .text%__1cIPhaseIFGIadd_edge6MII_i_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_; +text: .text%__1cIMachNodeHis_Mach6M_p0_; +text: .text%__1cENodeHis_Copy6kM_I_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_; +text: .text%__1cQIndexSetIterator2t6MpnIIndexSet__v_; +text: .text%__1cOPhaseIdealLoopOidom_no_update6kMpnENode__2_; +text: .text%__1cHnmethodKcan_unload6MpnRBoolObjectClosure_pnKOopClosure_ppnHoopDesc_i_i_; +text: .text%__1cIMachNodeNrematerialize6kM_i_; +text: .text%__1cHRegMaskFis_UP6kM_i_; +text: .text%__1cXresource_allocate_bytes6FL_pc_; +text: .text%__1cNRelocIteratorFreloc6M_pnKRelocation__; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__; +text: .text%__1cHRegMaskESize6kM_I_; +text: .text%__1cIIndexSetLalloc_block6M_pn0AIBitBlock__; +text: .text%__1cWConstantPoolCacheEntryPadjust_pointers6M_v_; +text: .text%__1cWConstantPoolCacheEntryPfollow_contents6M_v_; +text: .text%__1cJMarkSweepSMarkAndPushClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cDLRGOcompute_degree6kMr0_i_; +text: .text%__1cLsymbolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cWPSScavengeRootsClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cLsymbolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLsymbolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cYPSPromotionFailedClosureJdo_object6MpnHoopDesc__v_; +text: .text%__1cENodeEjvms6kM_pnIJVMState__; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_; +text: .text%__1cIIndexSetWalloc_block_containing6MI_pn0AIBitBlock__; +text: .text%__1cENodeHdel_out6Mp0_v_; +text: .text%__1cKRelocationLunpack_data6M_v_; +text: .text%__1cIMachNodeJideal_reg6kM_I_; +text: .text%__1cJAssemblerOlocate_operand6FpCn0AMWhichOperand__1_; +text: .text%__1cKRelocationSpd_address_in_code6M_ppC_; +text: .text%__1cOoop_RelocationIoop_addr6M_ppnHoopDesc__; +text: .text%__1cRMachSpillCopyNodeMis_SpillCopy6M_p0_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__; +text: .text%__1cENodeGis_CFG6kM_i_; +text: .text%__1cKRelocationNunpack_2_ints6Mri1_v_; +text: .text%__1cETypeDcmp6Fpk02_i_; +text: .text%__1cQObjectStartArrayWobject_starts_in_range6kMpnIHeapWord_2_i_; +text: .text%__1cOoop_RelocationLunpack_data6M_v_; +text: .text%__1cHRegMaskJis_bound16kM_i_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__; +text: .text%__1cOtypeArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cHRegMaskJis_bound26kM_i_; +text: .text%__1cRmethodDataOopDescHdata_at6Mi_pnLProfileData__; +text: .text%__1cNGrowableArray4CI_Hat_grow6MirkI_I_; +text: .text%__1cLmethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cQconstMethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQconstMethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQconstMethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cRmethodDataOopDescJnext_data6MpnLProfileData__2_; +text: .text%__1cMOopTaskQdDueueOpop_local_slow6MInOTaskQdDueueSuperDAge__i_; +text: .text%__1cRMachSpillCopyNodeHis_Copy6kM_I_; +text: .text%__1cENodeGpinned6kM_i_; +text: .text%__1cOoop_RelocationJoop_value6M_pnHoopDesc__; +text: .text%__1cJrRegPOperEtype6kM_pknEType__; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_LI_v_; +text: .text%__1cMOopTaskQdDueueKpop_global6MrpnHoopDesc__i_; +text: .text%__1cPOopTaskQdDueueSetPsteal_best_of_26MipirpnHoopDesc__i_; +text: .text%__1cJVectorSet2R6MI_rnDSet__; +text: .text%__1cNRelocIteratorTadvance_over_prefix6M_v_; +text: .text%__1cSPSPromotionManagerUflush_prefetch_queue6M_v_; +text: .text%__1cIProjNodeHis_Proj6M_p0_; +text: .text%__1cPDictionaryEntrybDprotection_domain_set_oops_do6MpnKOopClosure__v_; +text: .text%__1cMOopMapStreamJfind_next6M_v_; +text: .text%__1cMloadConPNodePoper_input_base6kM_I_; +text: .text%__1cMloadConPNodeHtwo_adr6kM_I_; +text: .text%__1cMloadConPNodeErule6kM_I_; +text: .text%__1cHPhiNodeGis_Phi6M_p0_; +text: .text%__1cITypeNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__; +text: .text%__1cDff16FI_i_; +text: .text%__1cENodeNrematerialize6kM_i_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_; +text: .text%__1cFframeVoopmapreg_to_location6kMipknLRegisterMap__ppnHoopDesc__; +text: .text%__1cIIndexSetKinitialize6MI_v_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__; +text: .text%__1cMMutableSpaceMcas_allocate6ML_pnIHeapWord__; +text: .text%__1cIMachNodeGOpcode6kM_i_; +text: .text%__1cMget_live_bit6Fpii_i_: buildOopMap.o; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__; +text: .text%__1cENodeHadd_req6Mp0_v_; +text: .text%__1cENodeIout_grow6MI_v_; +text: .text%__1cMset_live_bit6Fpii_v_: buildOopMap.o; +text: .text%__1cKTypeOopPtrFklass6kM_pnHciKlass__; +text: .text%__1cIIndexSetKfree_block6MI_v_; +text: .text%__1cJCProjNodeGis_CFG6kM_i_; +text: .text%__1cETypeFuhash6Fpk0_i_; +text: .text%__1cJrRegIOperEtype6kM_pknEType__; +text: .text%__1cMPhaseChaitinLskip_copies6MpnENode__2_; +text: .text%__1cIMachNodeMcisc_operand6kM_i_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_; +text: .text%__1cICallNodeKmatch_edge6kMI_I_; +text: .text%__1cENodeHis_Call6M_pnICallNode__; +text: .text%__1cNCollectedHeapbDcheck_for_bad_heap_word_value6MpnIHeapWord_L_v_; +text: .text%__1cEDictGInsert6Mpv1i_1_; +text: .text%JVM_CurrentTimeMillis; +text: .text%__1cINodeHashLhash_delete6MpknENode__i_; +text: .text%__1cETypeJtype_dict6F_pnEDict__; +text: .text%__1cOPSPromotionLABKinitialize6MnJMemRegion__v_; +text: .text%__1cOPSPromotionLABFflush6M_v_; +text: .text%__1cIrc_class6Fi_nCRC__: ad_amd64.o; +text: .text%__1cMPhaseChaitinTinterfere_with_live6MIpnIIndexSet__v_; +text: .text%__1cINodeHashQhash_find_insert6MpnENode__2_; +text: .text%__1cNCollectedHeapbAcommon_mem_allocate_noinit6FLipnGThread__pnIHeapWord__; +text: .text%__1cENodeHis_Load6M_pnILoadNode__; +text: .text%__1cFArenaIcontains6kMpkv_i_; +text: .text%__1cJMultiNodeGis_CFG6kM_i_; +text: .text%__1cHPhiNodeGOpcode6kM_i_; +text: .text%__1cMPhaseChaitinKelide_copy6MpnENode_ipnFBlock_rnJNode_List_6i_i_; +text: .text%__1cKjmpDirNodeNis_block_proj6kM_pknENode__; +text: .text%__1cIProjNodeGis_CFG6kM_i_; +text: .text%__1cRMachSpillCopyNodeJideal_reg6kM_I_; +text: .text%__1cETypeIhashcons6M_pk0_; +text: .text%__1cENodeEhash6kM_I_; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_; +text: .text%__1cOlower_pressure6FpnDLRG_IpnFBlock_pI4_v_: ifg.o; +text: .text%__1cPOopTaskQdDueueSetFsteal6MipirpnHoopDesc__i_; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cKRegionNodeGis_CFG6kM_i_; +text: .text%__1cHCompileRvalid_bundle_info6MpknENode__i_; +text: .text%__1cIProjNodeGOpcode6kM_i_; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__; +text: .text%__1cOoop_RelocationSfix_oop_relocation6M_v_; +text: .text%__1cPClassFileStreamGget_u26MpnGThread__H_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__; +text: .text%__1cPVirtualCallDataKcell_count6M_i_; +text: .text%__1cIProjNodeGpinned6kM_i_; +text: .text%__1cENodeMcisc_operand6kM_i_; +text: .text%__1cRInterpreterOopMapLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cMMachCallNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cMloadConINodePoper_input_base6kM_I_; +text: .text%__1cMloadConINodeHtwo_adr6kM_I_; +text: .text%__1cHNTarjanEEVAL6M_p0_; +text: .text%__1cNMachIdealNodeErule6kM_I_; +text: .text%__1cKHandleMarkKinitialize6MpnGThread__v_; +text: .text%__1cKHandleMark2T6M_v_; +text: .text%__1cETypeLisa_oop_ptr6kM_i_; +text: .text%__1cOPhaseIdealLoopUbuild_loop_late_post6MpnENode_pk0_v_; +text: .text%__1cENode2t6MI_v_; +text: .text%__1cJloadPNodeErule6kM_I_; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__; +text: .text%__1cMloadConINodeErule6kM_I_; +text: .text%__1cICodeHeapKfind_start6kMpv_1_; +text: .text%__1cWShouldNotReachHereNodeNis_block_proj6kM_pknENode__; +text: .text%__1cHTypeIntCeq6kMpknEType__i_; +text: .text%__1cLProfileDataPfollow_contents6M_v_; +text: .text%__1cLProfileDataPadjust_pointers6M_v_; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__; +text: .text%__1cHRegMaskMClearToPairs6M_v_; +text: .text%__1cJPhaseLiveLadd_liveout6MpnFBlock_IrnJVectorSet__v_; +text: .text%__1cLemit_opcode6FrnKCodeBuffer_i_v_; +text: .text%__1cIPhaseIFGQeffective_degree6kMI_i_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__; +text: .text%__1cFArenaIArealloc6MpvLL_1_; +text: .text%__1cGIfNodeGOpcode6kM_i_; +text: .text%__1cHTypePtrEhash6kM_i_; +text: .text%__1cIPhaseIFGLremove_node6MI_pnIIndexSet__; +text: .text%__1cIPhaseIFGJre_insert6MI_v_; +text: .text%__1cRMachSpillCopyNodeLbottom_type6kM_pknEType__; +text: .text%__1cMclr_live_bit6Fpii_v_: buildOopMap.o; +text: .text%__1cETypeEmeet6kMpk0_2_; +text: .text%__1cMPhaseChaitinQis_high_pressure6MpnFBlock_pnDLRG_I_i_; +text: .text%__1cKBranchDataKcell_count6M_i_; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cOPhaseIdealLoopYsplit_if_with_blocks_pre6MpnENode__2_; +text: .text%__1cNIdealLoopTreeJis_member6kMpk0_i_; +text: .text%__1cOPhaseIdealLoopZsplit_if_with_blocks_post6MpnENode__v_; +text: .text%__1cDfh16FI_i_; +text: .text%__1cJraw_score6Fdd_d_: chaitin.o; +text: .text%__1cDLRGFscore6kM_d_; +text: .text%__1cKTypeOopPtrEhash6kM_i_; +text: .text%__1cIAddPNodeGOpcode6kM_i_; +text: .text%__1cKIfTrueNodeGOpcode6kM_i_; +text: .text%__1cMPhaseChaitinMchoose_color6MrnDLRG_i_i_; +text: .text%__1cMPhaseIterGVNNtransform_old6MpnENode__2_; +text: .text%__1cGcmpkey6Fpkv1_i_; +text: .text%__1cETypeJsingleton6kM_i_; +text: .text%__1cIMachNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cSPSPromotionManagerMdrain_stacks6M_v_; +text: .text%__1cHConNodeGOpcode6kM_i_; +text: .text%__1cITypeLongCeq6kMpknEType__i_; +text: .text%__1cUParallelScavengeHeapVlarge_typearray_limit6M_L_; +text: .text%__1cOtypeArrayKlassIallocate6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cKoopFactoryNnew_typeArray6FnJBasicType_ipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cMPhaseChaitinKbias_color6MrnDLRG_i_i_; +text: .text%__1cICodeHeapKnext_block6kMpnJHeapBlock__2_; +text: .text%__1cICodeHeapJnext_free6kMpnJHeapBlock__pv_; +text: .text%__1cICodeHeapLblock_start6kMpv_pnJHeapBlock__; +text: .text%__1cJCodeCacheEnext6FpnICodeBlob__2_; +text: .text%__1cRMachSpillCopyNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cMMachProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cLOptoRuntimeXdeoptimize_caller_frame6FpnKJavaThread_i_v_; +text: .text%__1cSCallStaticJavaNodeGOpcode6kM_i_; +text: .text%__1cYCallStaticJavaDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cJCodeCacheFalive6FpnICodeBlob__2_; +text: .text%__1cHRegMaskQis_aligned_Pairs6kM_i_; +text: .text%__1cSis_single_register6FI_i_: postaloc.o; +text: .text%__1cRMachSpillCopyNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMPhaseIterGVNWadd_users_to_worklist06MpnENode__v_; +text: .text%__1cECopyYconjoint_words_to_higher6FpnIHeapWord_2L_v_; +text: .text%__1cILRG_ListGextend6MII_v_; +text: .text%__1cWstatic_stub_RelocationLunpack_data6M_v_; +text: .text%__1cIciObjectGequals6Mp0_i_; +text: .text%__1cJPhaseLiveGgetset6MpnFBlock__pnIIndexSet__; +text: .text%__1cIConINodeGOpcode6kM_i_; +text: .text%__1cENodeRis_cisc_alternate6kM_i_; +text: .text%__1cLIfFalseNodeGOpcode6kM_i_; +text: .text%__1cKNode_ArrayGinsert6MIpnENode__v_; +text: .text%__1cLCounterDataKcell_count6M_i_; +text: .text%__1cWThreadLocalAllocBufferFreset6M_v_; +text: .text%__1cMMachProjNodeGOpcode6kM_i_; +text: .text%__1cENodeEgrow6MI_v_; +text: .text%__1cIMachNodePcompute_padding6kMi_i_; +text: .text%__1cKup_one_dom6FpnENode__1_: ifnode.o; +text: .text%__1cIMachNodeSalignment_required6kM_i_; +text: .text%__1cOPhaseIdealLoopEsort6MpnNIdealLoopTree_2_2_; +text: .text%__1cFframeOis_entry_frame6kM_i_; +text: .text%__1cNCollectedHeapXallocate_from_tlab_slow6FpnGThread_L_pnIHeapWord__; +text: .text%__1cUParallelScavengeHeapVunsafe_max_tlab_alloc6kM_L_; +text: .text%__1cHNTarjanICOMPRESS6M_v_; +text: .text%__1cWThreadLocalAllocBufferXclear_before_allocation6M_v_; +text: .text%__1cFMutexGunlock6M_v_; +text: .text%__1cJMultiNodeIis_Multi6M_p0_; +text: .text%__1cIBoolNodeGOpcode6kM_i_; +text: .text%__1cSInterpreterRuntimeInewarray6FpnKJavaThread_nJBasicType_i_v_; +text: .text%__1cUParallelScavengeHeapRallocate_new_tlab6ML_pnIHeapWord__; +text: .text%__1cKSharedHeapXfill_region_with_object6FnJMemRegion__v_; +text: .text%__1cWThreadLocalAllocBufferKinitialize6MpnIHeapWord_22_v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2t6M_v_; +text: .text%__1cWThreadLocalAllocBufferEfill6MpnIHeapWord_2L_v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2T6M_v_; +text: .text%__1cKTypeOopPtrCeq6kMpknEType__i_; +text: .text%__1cHTypePtrCeq6kMpknEType__i_; +text: .text%__1cRMachSpillCopyNodePoper_input_base6kM_I_; +text: .text%__1cIIndexSetFclear6M_v_; +text: .text%__1cHTypeIntJsingleton6kM_i_; +text: .text%__1cJPhaseLiveLadd_liveout6MpnFBlock_pnIIndexSet_rnJVectorSet__v_; +text: .text%__1cECopyXconjoint_words_to_lower6FpnIHeapWord_2L_v_; +text: .text%__1cNRelocIteratorKset_limits6MpC1_v_; +text: .text%__1cNRelocIteratorKinitialize6MlpnICodeBlob_pC3_v_; +text: .text%__1cNinstanceKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cSinstanceKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cITypeNodeJideal_reg6kM_I_; +text: .text%__1cFframeUis_interpreted_frame6kM_i_; +text: .text%__1cHTypeIntEhash6kM_i_; +text: .text%__1cIPhaseGVNJtransform6MpnENode__2_; +text: .text%__1cNinstanceKlassGitable6kM_pnLklassItable__; +text: .text%__1cLklassItable2t6MnTinstanceKlassHandle__v_; +text: .text%__1cIMachNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cIciObjectEhash6M_i_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__; +text: .text%__1cFState2T6M_v_; +text: .text%__1cPVirtualCallDataPfollow_contents6M_v_; +text: .text%__1cPVirtualCallDataPadjust_pointers6M_v_; +text: .text%__1cENodeGis_Con6kM_I_; +text: .text%__1cJrRegIOperJnum_edges6kM_I_; +text: .text%__1cENodeIget_long6kM_x_; +text: .text%__1cNCellTypeStateFmerge6kM0i_0_; +text: .text%__1cOPhaseIdealLoopUbuild_loop_tree_impl6MpnENode_i_i_; +text: .text%__1cWNode_Backward_IteratorEnext6M_pnENode__; +text: .text%__1cHemit_rm6FrnKCodeBuffer_iii_v_; +text: .text%__1cHPhiNodeGpinned6kM_i_; +text: .text%__1cITypeNodeEhash6kM_I_; +text: .text%__1cIIndexSetKinitialize6MIpnFArena__v_; +text: .text%__1cJPhaseLiveKgetfreeset6M_pnIIndexSet__; +text: .text%__1cMMachProjNodeJideal_reg6kM_I_; +text: .text%__1cHPhiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIParmNodeGis_CFG6kM_i_; +text: .text%__1cENodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNCollectedHeapMobj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cNinstanceKlassRallocate_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cKRegionNodeGOpcode6kM_i_; +text: .text%__1cMMachProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOptoRuntimeFnew_C6FpnMklassOopDesc_pnKJavaThread__v_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__; +text: .text%__1cIProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_; +text: .text%__1cLTypeInstPtrEhash6kM_i_; +text: .text%__1cLuse_dom_lca6FpnFBlock_pnENode_3rnLBlock_Array__1_: gcm.o; +text: .text%__1cITypeLongEhash6kM_i_; +text: .text%__1cIBoolNodeHis_Bool6M_p0_; +text: .text%__1cOPhaseIdealLoopOget_early_ctrl6MpnENode__2_; +text: .text%__1cOPhaseIdealLoopOset_early_ctrl6MpnENode__v_; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__; +text: .text%__1cIJumpDataKcell_count6M_i_; +text: .text%__1cHNTarjanELINK6Mp01_v_; +text: .text%__1cENodeIIdentity6MpnOPhaseTransform__p0_; +text: .text%__1cIMachNode2t6M_v_; +text: .text%__1cFStateRMachOperGenerator6MipnIMachNode_pnHCompile__pnIMachOper__; +text: .text%__1cRPSOldPromotionLABFflush6M_v_; +text: .text%__1cICallNodeHis_Call6M_p0_; +text: .text%__1cENodeFIdeal6MpnIPhaseGVN_i_p0_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cOJNIHandleBlockPallocate_handle6MpnHoopDesc__pnI_jobject__; +text: .text%__1cENodeNis_block_proj6kM_pk0_; +text: .text%__1cUParallelScavengeHeapPis_in_permanent6kMpkv_i_; +text: .text%__1cHdom_lca6FpnFBlock_1_1_: gcm.o; +text: .text%__1cOPhaseIdealLoopThas_local_phi_input6MpnENode__2_; +text: .text%__1cJVectorSet2F6kMI_i_; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cICallNodeLbottom_type6kM_pknEType__; +text: .text%JVM_ArrayCopy; +text: .text%__1cOtypeArrayKlassQoop_is_typeArray6kM_i_; +text: .text%__1cOtypeArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cIMachNodeLbottom_type6kM_pknEType__; +text: .text%__1cPClassFileParserOcheck_property6MipkcipnGThread__v_; +text: .text%__1cFState2t6M_v_; +text: .text%__1cFStateDDFA6MipknENode__i_; +text: .text%__1cJPhaseLiveHfreeset6MpknFBlock__v_; +text: .text%__1cOPhaseIdealLoopbIdom_lca_for_get_late_ctrl_internal6MpnENode_22_2_; +text: .text%__1cKRegionNodeGpinned6kM_i_; +text: .text%__1cPjava_lang_ClassLas_klassOop6FpnHoopDesc__pnMklassOopDesc__; +text: .text%__1cRSignatureIteratorGexpect6Mc_v_; +text: .text%__1cIProjNodeEhash6kM_I_; +text: .text%__1cENodeFclone6kM_p0_; +text: .text%__1cILocationIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cIPhaseCCPOtransform_once6MpnENode__2_; +text: .text%__1cHCompileMFillLocArray6MpnENode_pnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cHRegMaskPfind_first_pair6kM_i_; +text: .text%__1cENodeKmatch_edge6kMI_I_; +text: .text%__1cKJNIHandlesKmake_local6FpnHJNIEnv__pnHoopDesc__pnI_jobject__; +text: .text%__1cNLocationValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cNPhaseRegAllocKreg2offset6kMi_i_; +text: .text%__1cNPhaseRegAllocUreg2offset_unchecked6kMi_i_; +text: .text%__1cbAfinal_graph_reshaping_impl6FpnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cETypeFxmeet6kMpk0_2_; +text: .text%__1cENodeFis_If6M_pnGIfNode__; +text: .text%__1cJStartNodeLbottom_type6kM_pknEType__; +text: .text%__1cICmpPNodeGOpcode6kM_i_; +text: .text%__1cOMethodLivenessKBasicBlockXcompute_gen_kill_single6MpnQciBytecodeStream__v_; +text: .text%__1cKjmpDirNodeMideal_Opcode6kM_i_; +text: .text%__1cOPhaseIdealLoopZremix_address_expressions6MpnENode__2_; +text: .text%__1cFMutexElock6MpnGThread__v_; +text: .text%__1cILoadNodeHis_Load6M_p0_; +text: .text%__1cGciTypeEmake6FnJBasicType__p0_; +text: .text%__1cQUnique_Node_ListGremove6MpnENode__v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_; +text: .text%__1cRMachSafePointNodeEjvms6kM_pnIJVMState__; +text: .text%__1cNinstanceKlassMclass_loader6kM_pnHoopDesc__; +text: .text%__1cOPhaseIdealLoopNget_late_ctrl6MpnENode_2_2_; +text: .text%__1cRMachSpillCopyNodeOimplementation6kMpnKCodeBuffer_pnNPhaseRegAlloc_i_I_; +text: .text%__1cKTypeAryPtrEhash6kM_i_; +text: .text%__1cIIndexSet2t6Mp0_v_; +text: .text%__1cNrFlagsRegOperEtype6kM_pknEType__; +text: .text%__1cHTypeInt2t6Miii_v_; +text: .text%__1cTconstantPoolOopDescbAname_and_type_ref_index_at6Mi_i_; +text: .text%__1cMPhaseChaitinSuse_prior_register6MpnENode_I2pnFBlock_rnJNode_List_6_i_; +text: .text%__1cIGraphKitHstopped6M_i_; +text: .text%__1cKTypeOopPtrJsingleton6kM_i_; +text: .text%__1cENodeQIdeal_DU_postCCP6MpnIPhaseCCP__p0_; +text: .text%__1cNSafePointNodeGpinned6kM_i_; +text: .text%__1cQCompressedStream2t6MpCi_v_; +text: .text%__1cIConLNodeGOpcode6kM_i_; +text: .text%__1cHPhiNodeEhash6kM_I_; +text: .text%__1cGIfNodeGpinned6kM_i_; +text: .text%__1cOis_diamond_phi6FpnENode__i_: cfgnode.o; +text: .text%__1cLTypeInstPtrCeq6kMpknEType__i_; +text: .text%__1cENodeHsize_of6kM_I_; +text: .text%__1cENodeSremove_dead_region6MpnIPhaseGVN_i_i_; +text: .text%__1cHRegMaskMSmearToPairs6M_v_; +text: .text%__1cSCallStaticJavaNodeEhash6kM_I_; +text: .text%jni_GetObjectField: jni.o; +text: .text%__1cJMarkSweepXrevisit_weak_klass_link6FpnFKlass__v_; +text: .text%__1cKklassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cKklassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cFKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cKNode_ArrayEgrow6MI_v_; +text: .text%__1cLklassVtableToop_adjust_pointers6M_v_; +text: .text%__1cLklassVtableToop_follow_contents6M_v_; +text: .text%__1cCosOis_interrupted6FpnGThread_i_i_; +text: .text%__1cICmpINodeGOpcode6kM_i_; +text: .text%__1cHMatcherKLabel_Root6MpknENode_pnFState_p16_6_; +text: .text%__1cMPhaseChaitinHnew_lrg6MpknENode_I_v_; +text: .text%__1cFframeYinterpreter_frame_method6kM_pnNmethodOopDesc__; +text: .text%__1cUGenericGrowableArrayMraw_allocate6Mi_pv_; +text: .text%__1cJMarkSweepNpreserve_mark6FpnHoopDesc_pnLmarkOopDesc__v_; +text: .text%__1cIMachNodeKconst_size6kM_i_; +text: .text%__1cGIfNodeFis_If6M_p0_; +text: .text%__1cINodeHashLhash_insert6MpnENode__v_; +text: .text%__1cOPhaseIdealLoopMis_dominator6MpnENode_2_i_; +text: .text%__1cKTypeOopPtrLxadd_offset6kMi_i_; +text: .text%JVM_Read; +text: .text%__1cDhpiEread6FipvI_L_; +text: .text%__1cIMachNodeIpeephole6MpnFBlock_ipnNPhaseRegAlloc_ri_p0_; +text: .text%__1cMPhaseIterGVNVadd_users_to_worklist6MpnENode__v_; +text: .text%__1cIHaltNodeGis_CFG6kM_i_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__; +text: .text%__1cPClassFileStreamGget_u16MpnGThread__C_; +text: .text%__1cMMergeMemNodeGOpcode6kM_i_; +text: .text%__1cPciObjectFactoryEfind6MpnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cJCodeCacheQfind_blob_unsafe6Fpv_pnICodeBlob__; +text: .text%__1cHemit_d86FrnKCodeBuffer_i_v_; +text: .text%__1cKCodeBuffer2t6MiiiiiipnKBufferBlob_pnJrelocInfo_pnORelocateBuffer_ipnLOopRecorder_pkcii_v_; +text: .text%__1cLOopRecorder2t6MpnFArena__v_; +text: .text%__1cKCodeBuffer2T6M_v_; +text: .text%__1cKCodeBufferQalloc_relocation6MI_v_; +text: .text%__1cIMachNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIMachNodeJemit_size6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLTypeInstPtr2t6MnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_v_; +text: .text%__1cLOptoRuntimeKjbyte_copy6FpW1L_v_; +text: .text%__1cJloadINodeErule6kM_I_; +text: .text%__1cMOopMapStream2t6MpnGOopMap_i_v_; +text: .text%__1cPciObjectFactoryLis_found_at6MipnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode__i_; +text: .text%__1cIAddINodeGOpcode6kM_i_; +text: .text%__1cWShouldNotReachHereNodeMideal_Opcode6kM_i_; +text: .text%__1cOmatch_into_reg6FpnENode_iii1_i_: matcher.o; +text: .text%__1cENodeJis_Branch6kM_I_; +text: .text%__1cITypeLong2t6Mxxi_v_; +text: .text%__1cRconstantPoolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cIJVMStateIof_depth6kMi_p0_; +text: .text%__1cFMutexElock6M_v_; +text: .text%__1cJrRegLOperEtype6kM_pknEType__; +text: .text%__1cENode2t6Mp0_v_; +text: .text%__1cLTypeInstPtrEmake6FnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_pk0_; +text: .text%__1cJStartNodeGpinned6kM_i_; +text: .text%__1cHhashptr6Fpkv_i_; +text: .text%__1cLklassItableToop_adjust_pointers6M_v_; +text: .text%__1cLklassItableToop_follow_contents6M_v_; +text: .text%__1cRconstantPoolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cRconstantPoolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSinstanceKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSinstanceKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNinstanceKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cNinstanceKlassUadjust_static_fields6M_v_; +text: .text%__1cNinstanceKlassUfollow_static_fields6M_v_; +text: .text%__1cFBlockLis_uncommon6kMrnLBlock_Array__i_; +text: .text%__1cEDict2F6kMpkv_pv_; +text: .text%__1cIProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cNCatchProjNodeGOpcode6kM_i_; +text: .text%__1cJCodeCacheJfind_blob6Fpv_pnICodeBlob__; +text: .text%__1cICallNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJdo_method6FpnNmethodOopDesc__v_: recompilationMonitor.o; +text: .text%__1cWconstantPoolCacheKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cKjmpConNodeMideal_Opcode6kM_i_; +text: .text%__1cJLoadPNodeGOpcode6kM_i_; +text: .text%__1cLSymbolTableGlookup6MipkciI_pnNsymbolOopDesc__; +text: .text%__1cWconstantPoolCacheKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWconstantPoolCacheKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cOindOffset8OperJnum_edges6kM_I_; +text: .text%__1cKBufferBlobIis_alive6kM_i_; +text: .text%__1cFframeGsender6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cJTypeTupleJsingleton6kM_i_; +text: .text%__1cKTypeAryPtrCeq6kMpknEType__i_; +text: .text%__1cHPhiNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cETypeKhas_memory6kM_i_; +text: .text%__1cHMatcherKReduceOper6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cGIfNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMachNodeFreloc6kM_i_; +text: .text%__1cPciObjectFactoryDget6MpnHoopDesc__pnIciObject__; +text: .text%__1cOThreadCritical2T6M_v_; +text: .text%__1cOThreadCritical2t6M_v_; +text: .text%__1cFframeOis_first_frame6kM_i_; +text: .text%__1cICodeBlobOis_osr_adapter6kM_i_; +text: .text%__1cHConNodeGis_Con6kM_I_; +text: .text%__1cIAddPNodeKmatch_edge6kMI_I_; +text: .text%__1cILoadNodeEhash6kM_I_; +text: .text%__1cRMachSpillCopyNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cICodeBlobTfix_oop_relocations6MpC1_v_; +text: .text%__1cICodeBlobTfix_oop_relocations6M_v_; +text: .text%__1cIimmIOperIconstant6kM_l_; +text: .text%__1cHCmpNodeGis_Cmp6kM_pk0_; +text: .text%__1cFMutexbClock_without_safepoint_check6M_v_; +text: .text%__1cENodeHdel_req6MI_v_; +text: .text%__1cJCProjNodeEhash6kM_I_; +text: .text%__1cHCompileJcan_alias6MpknHTypePtr_i_i_; +text: .text%__1cJMultiNodeEhash6kM_I_; +text: .text%__1cRSignatureIteratorKparse_type6M_i_; +text: .text%__1cHnmethodIis_alive6kM_i_; +text: .text%__1cIHaltNodeGOpcode6kM_i_; +text: .text%__1cMMergeMemNodeLis_MergeMem6M_p0_; +text: .text%__1cUPSMarkSweepDecoratorQinsert_deadspace6MrlpnIHeapWord_L_i_; +text: .text%__1cHPhiNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMPhaseIterGVNZremove_globally_dead_node6MpnENode__v_; +text: .text%__1cETypeEhash6kM_i_; +text: .text%__1cJHashtableLhash_symbol6Fpkci_I_; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pnIciObject_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cYDebugInformationRecorderLcheck_phase6Mn0AFPhase__v_; +text: .text%__1cOMachReturnNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cFframeVinterpreter_frame_bcp6kM_pC_; +text: .text%__1cICodeBlobKis_nmethod6kM_i_; +text: .text%__1cICmpUNodeGOpcode6kM_i_; +text: .text%__1cJOopMapSetSfind_map_at_offset6kMi_pnGOopMap__; +text: .text%__1cICodeBlobbAoop_map_for_return_address6MpC_pnGOopMap__; +text: .text%__1cIConPNodeGOpcode6kM_i_; +text: .text%__1cIMachNodeHtwo_adr6kM_I_; +text: .text%__1cIParmNodeGOpcode6kM_i_; +text: .text%__1cTconvI2L_reg_regNodeErule6kM_I_; +text: .text%__1cHTypeIntFxmeet6kMpknEType__3_; +text: .text%__1cLAdapterInfoFequal6kMp0_i_; +text: .text%__1cGOopMapbEmap_compiler_reg_to_oopmap_reg6Miii_i_; +text: .text%__1cTconstantPoolOopDescSklass_ref_index_at6Mi_i_; +text: .text%__1cLSymbolTableGlookup6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cKoopFactoryKnew_symbol6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cMloadConINodeMideal_Opcode6kM_i_; +text: .text%__1cGTarjanEEVAL6M_p0_; +text: .text%__1cKRelocationYindex_to_runtime_address6Fl_pC_; +text: .text%__1cYexternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cJCatchNodeGOpcode6kM_i_; +text: .text%__1cZPhaseConservativeCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cITypeLongJsingleton6kM_i_; +text: .text%__1cNencode_RegMem6FrnKCodeBuffer_iiiiii_v_; +text: .text%__1cFBlockGselect6MrnJNode_List_rnLBlock_Array_pirnJVectorSet_IrnNGrowableArray4CI___pnENode__; +text: .text%__1cGOopMapHset_xxx6MinLOopMapValueJoop_types_iii_v_; +text: .text%__1cFMutexNowned_by_self6kM_i_; +text: .text%__1cTCreateExceptionNodeErule6kM_I_; +text: .text%__1cIJVMStateLdebug_start6kM_I_; +text: .text%__1cRCardTableModRefBSEkind6M_nKBarrierSetEName__; +text: .text%__1cMloadConPNodeMideal_Opcode6kM_i_; +text: .text%__1cIMachNodeNoperand_index6kMI_i_; +text: .text%__1cKMachIfNodeJis_MachIf6kM_pk0_; +text: .text%__1cFBlockIis_Empty6kM_i_; +text: .text%__1cMMachTypeNodeLbottom_type6kM_pknEType__; +text: .text%__1cIAddPNodeLbottom_type6kM_pknEType__; +text: .text%__1cFBlockOcode_alignment6M_I_; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_; +text: .text%__1cGBitMapUclear_range_of_words6MLL_v_; +text: .text%__1cGBitMapFclear6M_v_; +text: .text%__1cSinstanceKlassKlassMoop_is_klass6kM_i_; +text: .text%__1cJrRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKCastPPNodeGOpcode6kM_i_; +text: .text%__1cIPhaseIFGMtest_edge_sq6kMII_i_; +text: .text%__1cFframeQoops_do_internal6MpnKOopClosure_pnLRegisterMap_i_v_; +text: .text%__1cNinstanceKlassLfind_method6FpnPobjArrayOopDesc_pnNsymbolOopDesc_4_pnNmethodOopDesc__; +text: .text%__1cFStateRMachNodeGenerator6MipnHCompile__pnIMachNode__; +text: .text%__1cHMatcherKReduceInst6MpnFState_irpnENode__pnIMachNode__; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__; +text: .text%__1cPClassFileStreamHskip_u16MipnGThread__v_; +text: .text%__1cMMachCallNodeGpinned6kM_i_; +text: .text%__1cHnmethodJis_zombie6kM_i_; +text: .text%__1cMMergeMemNodeLbottom_type6kM_pknEType__; +text: .text%__1cNinstanceKlassLfind_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cKTypeAryPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cKCodeBufferIrelocate6MpCrknQRelocationHolder_i_v_; +text: .text%__1cILoadNodeLbottom_type6kM_pknEType__; +text: .text%__1cLConvI2LNodeGOpcode6kM_i_; +text: .text%__1cHTypeIntEmake6Fi_pk0_; +text: .text%__1cbFCompressedLineNumberWriteStreamKwrite_pair6Mii_v_; +text: .text%__1cJMultiNodeIproj_out6kMI_pnIProjNode__; +text: .text%__1cFKlassMoop_is_array6kM_i_; +text: .text%__1cIBoolNodeEhash6kM_I_; +text: .text%__1cIimmPOperEtype6kM_pknEType__; +text: .text%__1cMloadConPNodeLbottom_type6kM_pknEType__; +text: .text%__1cJrRegPOperJnum_edges6kM_I_; +text: .text%__1cOrFlagsRegUOperEtype6kM_pknEType__; +text: .text%__1cGIfNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNsymbolOopDescLas_C_string6kM_pc_; +text: .text%__1cNsymbolOopDescLas_C_string6kMpci_1_; +text: .text%__1cGOopMapJset_value6Miii_v_; +text: .text%__1cITypeLongFxmeet6kMpknEType__3_; +text: .text%__1cNCollectedHeapbHcheck_for_non_bad_heap_word_value6MpnIHeapWord_L_v_; +text: .text%__1cHMatcherTReduceInst_Interior6MpnFState_ipnIMachNode_IrpnENode__I_; +text: .text%__1cNsymbolOopDescGequals6kMpkci_i_; +text: .text%__1cMPhaseChaitinLinsert_proj6MpnFBlock_IpnENode_I_v_; +text: .text%__1cKjmpConNodeGpinned6kM_i_; +text: .text%__1cNSafePointNodebBneeds_polling_address_input6F_i_; +text: .text%__1cHCompileRprobe_alias_cache6MpknHTypePtr__pn0APAliasCacheEntry__; +text: .text%__1cHnmethodOis_not_entrant6kM_i_; +text: .text%__1cIAddPNodeHis_AddP6M_p0_; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6FnTobjArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cIMachNodeGExpand6MpnFState_rnJNode_List__p0_; +text: .text%__1cXInterpreterFrameClosureJoffset_do6Mi_v_; +text: .text%__1cNinstanceKlassQarray_klass_impl6FnTinstanceKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cENodeIdestruct6M_v_; +text: .text%__1cIAddPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPClassFileParserbEparse_constant_pool_utf8_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserRverify_legal_utf86MpkCipnGThread__v_; +text: .text%__1cMMachHaltNodeEjvms6kM_pnIJVMState__; +text: .text%__1cKoopFactoryMnew_objArray6FpnMklassOopDesc_ipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassRallocate_objArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cGBitMapJset_union6M0_v_; +text: .text%__1cPciInstanceKlassMis_interface6M_i_; +text: .text%__1cHTypeIntEmake6Fiii_pk0_; +text: .text%__1cJTypeTupleEhash6kM_i_; +text: .text%__1cFParsePdo_one_bytecode6M_v_; +text: .text%__1cFParseNdo_exceptions6M_v_; +text: .text%__1cKRegionNodeEhash6kM_I_; +text: .text%__1cMMutableSpaceIallocate6ML_pnIHeapWord__; +text: .text%__1cUParallelScavengeHeapWpermanent_mem_allocate6ML_pnIHeapWord__; +text: .text%__1cJPSPermGenSallocate_permanent6ML_pnIHeapWord__; +text: .text%__1cPmethodDataKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cIHaltNodeKmatch_edge6kMI_I_; +text: .text%__1cENodeOis_block_start6kM_i_; +text: .text%__1cHMatcherQis_save_on_entry6Mi_i_; +text: .text%__1cLsymbolKlassNoop_is_symbol6kM_i_; +text: .text%__1cITypeLongEmake6Fxxi_pk0_; +text: .text%__1cPmethodDataKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cPmethodDataKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMPhaseIterGVNKis_IterGVN6M_p0_; +text: .text%__1cGBitMap2t6MpLL_v_; +text: .text%__1cLOptoRuntimePnew_typeArray_C6FnJBasicType_ipnKJavaThread__v_; +text: .text%__1cHCompilePfind_alias_type6MpknHTypePtr_i_pn0AJAliasType__; +text: .text%__1cNnew_loc_value6FpnNPhaseRegAlloc_inILocationEType__pnNLocationValue__: output.o; +text: .text%__1cKjmpDirNodePoper_input_base6kM_I_; +text: .text%__1cMMachCallNodeLis_MachCall6M_p0_; +text: .text%__1cHPhiNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cXjava_lang_ref_ReferenceNreferent_addr6FpnHoopDesc__p2_; +text: .text%__1cOindOffset8OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOindOffset8OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cZPhaseConservativeCoalesceJcopy_copy6MpnENode_2pnFBlock_I_i_; +text: .text%__1cKutf8_write6FpCH_0_: utf8.o; +text: .text%__1cKNode_ArrayGremove6MI_v_; +text: .text%__1cTconstantPoolOopDescQsignature_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cHPhiNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRSignatureIterator2t6MnMsymbolHandle__v_; +text: .text%__1cJloadPNodeMideal_Opcode6kM_i_; +text: .text%__1cPSignatureStreamEnext6M_v_; +text: .text%__1cOindOffset8OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLjmpConUNodeMideal_Opcode6kM_i_; +text: .text%__1cFBlockJfind_node6kMpknENode__I_; +text: .text%__1cOis_range_check6FpnENode_r12ri_i_: ifnode.o; +text: .text%__1cENodeIis_Start6M_pnJStartNode__; +text: .text%__1cHhashkey6Fpkv_i_; +text: .text%__1cNLoadRangeNodeGOpcode6kM_i_; +text: .text%__1cJLoadINodeGOpcode6kM_i_; +text: .text%__1cKis_x2logic6FpnIPhaseGVN_pnENode__3_: cfgnode.o; +text: .text%__1cHAbsNodeLis_absolute6FpnIPhaseGVN_pnENode__4_; +text: .text%__1cGTarjanICOMPRESS6M_v_; +text: .text%__1cTconstantPoolOopDescNklass_at_impl6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cENodeHis_Goto6kM_I_; +text: .text%__1cLPCTableNodeGpinned6kM_i_; +text: .text%JVM_ReleaseUTF; +text: .text%__1cHSubNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFChunk2t6ML_v_; +text: .text%__1cFChunk2n6FLL_pv_; +text: .text%__1cOindOffset8OperFscale6kM_i_; +text: .text%__1cMCreateExNodeGOpcode6kM_i_; +text: .text%__1cFframebCsender_for_interpreter_frame6kMpnLRegisterMap__0_; +text: .text%__1cFChunk2k6Fpv_v_; +text: .text%__1cOMachReturnNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cJrelocInfo2t6Mn0AJrelocType_ii_v_; +text: .text%__1cHMatcherKmatch_tree6MpknENode__pnIMachNode__; +text: .text%__1cLTypeInstPtrFxmeet6kMpknEType__3_; +text: .text%__1cKjmpConNodePoper_input_base6kM_I_; +text: .text%__1cIJVMState2t6MpnIciMethod_p0_v_; +text: .text%__1cMciMethodDataHdata_at6Mi_pnLProfileData__; +text: .text%__1cJrelocInfoNfinish_prefix6Mph_p0_; +text: .text%__1cIProjNodeHsize_of6kM_I_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__; +text: .text%__1cLTypeInstPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cHBitDataKcell_count6M_i_; +text: .text%__1cFframeZsender_for_compiled_frame6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cFArenaEgrow6ML_pv_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cMPhaseIterGVNMsubsume_node6MpnENode_2_v_; +text: .text%__1cLBoxLockNodeNrematerialize6kM_i_; +text: .text%__1cRMachSafePointNodeQis_MachSafePoint6M_p0_; +text: .text%__1cJloadBNodeErule6kM_I_; +text: .text%__1cITypeNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cHConNodeEhash6kM_I_; +text: .text%__1cICodeBlobLlink_offset6M_i_; +text: .text%__1cENodeRdisconnect_inputs6Mp0_i_; +text: .text%__1cIHaltNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cICodeBlobOis_i2c_adapter6kM_i_; +text: .text%__1cTconstantPoolOopDescWsignature_ref_index_at6Mi_i_; +text: .text%__1cIJVMStateNclone_shallow6kM_p0_; +text: .text%__1cRMachSpillCopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGOopMapQset_callee_saved6Miiii_v_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__; +text: .text%__1cJOopMapSetTupdate_register_map6FpknFframe_pnICodeBlob_pnLRegisterMap__v_; +text: .text%__1cIJVMStateJdebug_end6kM_I_; +text: .text%__1cXjava_lang_ref_ReferenceJnext_addr6FpnHoopDesc__p2_; +text: .text%__1cENode2t6Mp011_v_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__; +text: .text%__1cHMatcherTcollect_null_checks6MpnENode__v_; +text: .text%__1cNSafePointNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHMemNodeMIdeal_common6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cGBitMapGat_put6MLi_v_; +text: .text%__1cFciEnvIis_in_vm6F_i_; +text: .text%__1cNSafePointNodeHsize_of6kM_I_; +text: .text%__1cMgetTimeNanos6F_x_; +text: .text%__1cKciTypeFlowLStateVectorSapply_one_bytecode6MpnQciBytecodeStream__i_; +text: .text%__1cKPSScavengeUoop_promotion_failed6FpnHoopDesc_pnLmarkOopDesc__v_; +text: .text%__1cHTypePtrLmeet_offset6kMi_i_; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_; +text: .text%__1cNPhaseRegAllocGis_oop6kMpknENode__i_; +text: .text%__1cSPSPromotionManagerUoop_promotion_failed6MpnHoopDesc_pnLmarkOopDesc__2_; +text: .text%__1cIMachOperLdisp_is_oop6kM_i_; +text: .text%__1cRSignatureIteratorSiterate_parameters6M_v_; +text: .text%__1cNmethodOopDescIbci_from6kMpC_i_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_; +text: .text%__1cLLShiftLNodeGOpcode6kM_i_; +text: .text%__1cOMethodLivenessKBasicBlockIload_one6Mi_v_; +text: .text%__1cJTypeTupleCeq6kMpknEType__i_; +text: .text%__1cHCmpNodeLbottom_type6kM_pknEType__; +text: .text%__1cFDictI2i6M_v_; +text: .text%__1cPjava_lang_ClassMis_primitive6FpnHoopDesc__i_; +text: .text%JVM_GetMethodIxExceptionTableLength; +text: .text%__1cKC2IAdapterIis_alive6kM_i_; +text: .text%__1cENodeHget_int6kM_i_; +text: .text%__1cMMachCallNodeLbottom_type6kM_pknEType__; +text: .text%__1cOGenerateOopMapHinterp16MpnOBytecodeStream__v_; +text: .text%__1cIAddPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMloadConINodeLbottom_type6kM_pknEType__; +text: .text%__1cPCheckCastPPNodeGOpcode6kM_i_; +text: .text%__1cMloadConINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeXis_iteratively_computed6M_i_; +text: .text%__1cNloadConI0NodePoper_input_base6kM_I_; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_; +text: .text%__1cENodeKreplace_by6Mp0_v_; +text: .text%__1cHPhiNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cIBoolTestKcc2logical6kMpknEType__3_; +text: .text%__1cIBoolNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJrelocInfoKset_format6Mi_v_; +text: .text%__1cXruntime_call_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cKRelocationJpack_data6M_i_; +text: .text%__1cXjava_lang_ref_ReferencePdiscovered_addr6FpnHoopDesc__p2_; +text: .text%__1cNPhaseCoalesceRcombine_these_two6MpnENode_2_v_; +text: .text%__1cNtestP_regNodeMideal_Opcode6kM_i_; +text: .text%__1cETypeFempty6kM_i_; +text: .text%__1cHMemNodeGis_Mem6M_p0_; +text: .text%__1cZload_can_see_stored_value6FpnILoadNode_pnENode_pnOPhaseTransform__3_: memnode.o; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cICodeBlobJis_zombie6kM_i_; +text: .text%__1cJTypeTupleGfields6FI_ppknEType__; +text: .text%__1cOGenerateOopMapPjump_targets_do6MpnOBytecodeStream_pFp0ipi_v4_i_; +text: .text%__1cPClassFileParserUassemble_annotations6MpCi1ipnGThread__nPtypeArrayHandle__; +text: .text%method_compare: methodOop.o; +text: .text%__1cMMergeMemNodeEhash6kM_I_; +text: .text%__1cJloadPNodePoper_input_base6kM_I_; +text: .text%__1cILoadNodeKmatch_edge6kMI_I_; +text: .text%__1cFBlockUneeded_for_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cIIndexSetSpopulate_free_list6F_v_; +text: .text%__1cGIfNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cCosPelapsed_counter6F_x_; +text: .text%__1cIAddPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIemit_d326FrnKCodeBuffer_i_v_; +text: .text%__1cNloadConI0NodeHtwo_adr6kM_I_; +text: .text%__1cJStoreNodeKmatch_edge6kMI_I_; +text: .text%__1cITypeNodeDcmp6kMrknENode__I_; +text: .text%__1cMPhaseChaitinFUnion6MpknENode_3_v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_; +text: .text%__1cRSignatureIteratorTcheck_signature_end6M_v_; +text: .text%__1cRSignatureIteratorSiterate_returntype6M_v_; +text: .text%__1cNinstanceKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cHnmethodKis_nmethod6kM_i_; +text: .text%__1cILoadNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNSafePointNodeKmatch_edge6kMI_I_; +text: .text%__1cPClassFileStreamGget_u46MpnGThread__I_; +text: .text%__1cSCallStaticJavaNodeRis_CallStaticJava6kM_pk0_; +text: .text%__1cKarrayKlassLobject_size6kMi_i_; +text: .text%__1cPciInstanceKlassRis_instance_klass6M_i_; +text: .text%__1cOno_flip_branch6FpnFBlock__i_: block.o; +text: .text%__1cKarrayKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cYCallStaticJavaDirectNodePoper_input_base6kM_I_; +text: .text%__1cOGenerateOopMapEpush6MnNCellTypeState__v_; +text: .text%__1cKjmpDirNodeGpinned6kM_i_; +text: .text%__1cTconstantPoolOopDescLname_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cKjmpDirNodeHtwo_adr6kM_I_; +text: .text%__1cPciInstanceKlassGloader6M_pnHoopDesc__; +text: .text%__1cJrRegLOperJnum_edges6kM_I_; +text: .text%__1cOGenerateOopMapDpop6M_nNCellTypeState__; +text: .text%__1cNGrowableArray4CpnKScopeValue__2t6Mii_v_; +text: .text%__1cYDebugInformationRecorderWserialize_scope_values6MpnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cYDebugInformationRecorderTcreate_scope_values6MpnNGrowableArray4CpnKScopeValue____pnKDebugToken__; +text: .text%__1cITypeNodeHsize_of6kM_I_; +text: .text%__1cILoadNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPPerfLongVariantGsample6M_v_; +text: .text%__1cNloadConI0NodeErule6kM_I_; +text: .text%__1cJCodeCacheIcontains6Fpv_i_; +text: .text%__1cQciBytecodeStreamMreset_to_bci6Mi_v_; +text: .text%__1cJOopMapSetGall_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure_pFppnHoopDesc_9E_v9B9B_v_; +text: .text%__1cFframeRoops_code_blob_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cJOopMapSetHoops_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cHPhiNodeHsize_of6kM_I_; +text: .text%__1cSInterpreterRuntimeJanewarray6FpnKJavaThread_pnTconstantPoolOopDesc_ii_v_; +text: .text%__1cMPhaseChaitinNFind_compress6MI_I_; +text: .text%__1cIMachNodeRget_base_and_disp6kMrlrpknHTypePtr__pknENode__; +text: .text%__1cLOopMapCacheIentry_at6kMi_pnQOopMapCacheEntry__; +text: .text%__1cOGenerateOopMapKcheck_type6MnNCellTypeState_1_v_; +text: .text%__1cPciInstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cSobjArrayKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cILoadNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMPhaseIterGVNbGregister_new_node_with_optimizer6MpnENode__2_; +text: .text%__1cKBufferBlobMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cKBufferBlobHoops_do6MpnKOopClosure__v_; +text: .text%__1cLRegisterMapFclear6M_v_; +text: .text%__1cIBoolNodeLbottom_type6kM_pknEType__; +text: .text%__1cITypeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKRegionNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLBlock_Array2t6MpnFArena__v_; +text: .text%__1cPSignatureStreamHis_done6kM_i_; +text: .text%__1cNrFlagsRegOperJnum_edges6kM_I_; +text: .text%__1cLPhaseValuesGintcon6Mi_pnIConINode__; +text: .text%__1cKStoreINodeGOpcode6kM_i_; +text: .text%__1cLRegisterMap2t6MpnKJavaThread_i_v_; +text: .text%__1cNmethodOopDescPis_empty_method6kM_i_; +text: .text%__1cJcmpOpOperJnum_edges6kM_I_; +text: .text%__1cNExceptionMark2t6MrpnGThread__v_; +text: .text%__1cNExceptionMark2T6M_v_; +text: .text%__1cCosMvm_page_size6F_i_; +text: .text%__1cJloadPNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cOcompU_rRegNodePoper_input_base6kM_I_; +text: .text%__1cNmethodOopDescLis_accessor6kM_i_; +text: .text%__1cPloadConUL32NodePoper_input_base6kM_I_; +text: .text%__1cMPhaseChaitinSget_spillcopy_wide6MpnENode_2I_2_; +text: .text%__1cKI2CAdapterIis_alive6kM_i_; +text: .text%__1cHnmethodHoops_do6MpnKOopClosure__v_; +text: .text%__1cIGraphKitJsync_jvms6kM_pnIJVMState__; +text: .text%__1cFframebFinterpreter_frame_monitor_begin6kM_pnPBasicObjectLock__; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__; +text: .text%__1cQSystemDictionaryXcheck_signature_loaders6FnMsymbolHandle_nGHandle_2ipnGThread__v_; +text: .text%__1cIGraphKitEstop6M_v_; +text: .text%__1cNidealize_test6FpnIPhaseGVN_pnGIfNode__3_: ifnode.o; +text: .text%__1cPloadConUL32NodeHtwo_adr6kM_I_; +text: .text%__1cNSafePointNodeLbottom_type6kM_pknEType__; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_; +text: .text%__1cRSignatureIterator2t6MpnNsymbolOopDesc__v_; +text: .text%__1cKStorePNodeGOpcode6kM_i_; +text: .text%__1cHMonitorKnotify_all6M_i_; +text: .text%__1cFframeVinterpreter_frame_bci6kM_i_; +text: .text%__1cHRetNodeNis_block_proj6kM_pknENode__; +text: .text%__1cMPhaseChaitinMyank_if_dead6MpnENode_pnFBlock_pnJNode_List_6_i_; +text: .text%__1cENodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFBlockLfind_remove6MpknENode__v_; +text: .text%__1cOGenerateOopMapRdo_exception_edge6MpnOBytecodeStream__v_; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_; +text: .text%__1cGOopMapHset_oop6Miii_v_; +text: .text%__1cNSafePointNodeSset_next_exception6Mp0_v_; +text: .text%__1cPloadConUL32NodeErule6kM_I_; +text: .text%__1cFChunkEchop6M_v_; +text: .text%__1cMciMethodDataJnext_data6MpnLProfileData__2_; +text: .text%__1cJStoreNodeLbottom_type6kM_pknEType__; +text: .text%__1cPciObjectFactorySget_unloaded_klass6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cLBlock_StackXmost_frequent_successor6MpnFBlock__I_; +text: .text%__1cFBlockScall_catch_cleanup6MrnLBlock_Array__v_; +text: .text%__1cFBlockOschedule_local6MrnHMatcher_rnLBlock_Array_pirnJVectorSet_rnNGrowableArray4CI___i_; +text: .text%__1cXPhaseAggressiveCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cOGenerateOopMapFppop16MnNCellTypeState__v_; +text: .text%__1cKstorePNodePoper_input_base6kM_I_; +text: .text%__1cMPhaseIterGVNFwiden6kMpknEType_3_3_; +text: .text%__1cKRegionNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNmethodOopDescRis_not_compilable6kMi_i_; +text: .text%__1cLis_cond_add6FpnIPhaseGVN_pnHPhiNode__pnENode__; +text: .text%__1cPsplit_flow_path6FpnIPhaseGVN_pnHPhiNode__pnENode__: cfgnode.o; +text: .text%__1cKRegionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGTarjanELINK6Mp01_v_; +text: .text%__1cKTypeRawPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cWMutableSpaceUsedHelperLtake_sample6M_x_; +text: .text%__1cIBoolNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIsplit_if6FpnGIfNode_pnMPhaseIterGVN__pnENode__: ifnode.o; +text: .text%__1cTremove_useless_bool6FpnGIfNode_pnIPhaseGVN__pnENode__: ifnode.o; +text: .text%__1cMPhaseChaitinNFind_compress6MpknENode__I_; +text: .text%__1cRInterpreterOopMapKinitialize6M_v_; +text: .text%__1cJTypeTupleEmake6FIppknEType__pk0_; +text: .text%__1cYCallStaticJavaDirectNodeHtwo_adr6kM_I_; +text: .text%__1cKDictionaryJget_entry6MiInMsymbolHandle_nGHandle__pnPDictionaryEntry__; +text: .text%__1cLLShiftINodeGOpcode6kM_i_; +text: .text%__1cQResultTypeFinderDset6MinJBasicType__v_; +text: .text%__1cHnmethodMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cNmethodOopDescLresult_type6kM_nJBasicType__; +text: .text%__1cIMachOperOindex_position6kM_i_; +text: .text%__1cKReflectionTverify_field_access6FpnMklassOopDesc_22nLAccessFlags_ii_i_; +text: .text%__1cOemit_d32_reloc6FrnKCodeBuffer_irknQRelocationHolder_i_v_; +text: .text%__1cLjmpConUNodePoper_input_base6kM_I_; +text: .text%__1cHAddNodeEhash6kM_I_; +text: .text%__1cNtestP_regNodePoper_input_base6kM_I_; +text: .text%__1cHSubNodeGis_Sub6M_p0_; +text: .text%__1cPcheckCastPPNodePoper_input_base6kM_I_; +text: .text%__1cIRootNodeGOpcode6kM_i_; +text: .text%__1cFframebDinterpreter_frame_monitor_end6kM_pnPBasicObjectLock__; +text: .text%__1cNmethodOopDescImask_for6MipnRInterpreterOopMap__v_; +text: .text%__1cNinstanceKlassImask_for6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cRInterpreterOopMapLiterate_oop6MpnNOffsetClosure__v_; +text: .text%__1cRInterpreterOopMap2T6M_v_; +text: .text%__1cRInterpreterOopMapNresource_copy6MpnQOopMapCacheEntry__v_; +text: .text%__1cLOopMapCacheGlookup6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cRInterpreterOopMap2t6M_v_; +text: .text%__1cFframeToops_interpreted_do6MpnKOopClosure_pknLRegisterMap_i_v_; +text: .text%__1cSInterpreterRuntimeLcache_entry6FpnKJavaThread__pnWConstantPoolCacheEntry__; +text: .text%__1cOindOffset8OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOJNIHandleBlockHoops_do6MpnKOopClosure__v_; +text: .text%__1cKstorePNodeMideal_Opcode6kM_i_; +text: .text%__1cFChunkJnext_chop6M_v_; +text: .text%__1cENode2t6Mp0111_v_; +text: .text%__1cITypeLongEmake6Fx_pk0_; +text: .text%__1cLjmpConUNodeGpinned6kM_i_; +text: .text%__1cPciObjectFactoryNfind_non_perm6MpnHoopDesc__rpn0ANNonPermObject__; +text: .text%__1cHCmpNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKTypeRawPtrJsingleton6kM_i_; +text: .text%__1cNGCTaskManagerNresource_flag6MI_i_; +text: .text%__1cNGCTaskManagerYshould_release_resources6MI_i_; +text: .text%__1cIAddINodeLbottom_type6kM_pknEType__; +text: .text%__1cNloadRangeNodeErule6kM_I_; +text: .text%__1cNrFlagsRegOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIIndexSetJlrg_union6MIIIpknIPhaseIFG_rknHRegMask__I_; +text: .text%__1cISubINodeGOpcode6kM_i_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_; +text: .text%__1cOBasicHashtableJnew_entry6MI_pnTBasicHashtableEntry__; +text: .text%__1cJHashtableJnew_entry6MIpnHoopDesc__pnOHashtableEntry__; +text: .text%__1cSCountedLoopEndNodeGOpcode6kM_i_; +text: .text%__1cTOopMapForCacheEntryRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cKstoreINodePoper_input_base6kM_I_; +text: .text%__1cNtestP_regNodeHtwo_adr6kM_I_; +text: .text%__1cCosVcurrent_stack_pointer6F_pC_; +text: .text%__1cMMergeMemNodePiteration_setup6Mpk0_v_; +text: .text%__1cRMachSafePointNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cJStoreNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMMergeMemNodeJmemory_at6kMI_pnENode__; +text: .text%__1cJloadSNodeErule6kM_I_; +text: .text%__1cLLShiftLNodeLbottom_type6kM_pknEType__; +text: .text%__1cMBasicAdapterHoops_do6MpnKOopClosure__v_; +text: .text%__1cKjmpConNodeJnum_opnds6kM_I_; +text: .text%__1cLOptoRuntimeOnew_objArray_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cTconstantPoolOopDescSklass_at_if_loaded6FnSconstantPoolHandle_i_pnMklassOopDesc__; +text: .text%__1cFciEnvXget_klass_by_index_impl6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cKjmpDirNodeHsize_of6kM_I_; +text: .text%__1cJloadPNodeJnum_opnds6kM_I_; +text: .text%__1cJCProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJMultiNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cHOopFlowNcompute_reach6MpnNPhaseRegAlloc_ipnEDict__v_; +text: .text%__1cJCatchNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cGGCTaskKinitialize6M_v_; +text: .text%__1cNGCTaskManagerIget_task6MI_pnGGCTask__; +text: .text%__1cNGCTaskManagerWdecrement_busy_workers6M_I_; +text: .text%__1cLGCTaskQdDueueGremove6M_pnGGCTask__; +text: .text%__1cLGCTaskQdDueueHenqueue6MpnGGCTask__v_; +text: .text%__1cNGCTaskManagerWincrement_busy_workers6M_I_; +text: .text%__1cLGCTaskQdDueueHdequeue6M_pnGGCTask__; +text: .text%__1cNGCTaskManagerPnote_completion6MI_v_; +text: .text%__1cHMatcherXadjust_outgoing_stk_arg6Miiri_i_; +text: .text%__1cTCreateExceptionNodeMideal_Opcode6kM_i_; +text: .text%__1cKIfTrueNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cXvirtual_call_RelocationLunpack_data6M_v_; +text: .text%__1cMBasicAdapterMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cXindIndexScaleOffsetOperJnum_edges6kM_I_; +text: .text%__1cLRShiftINodeGOpcode6kM_i_; +text: .text%__1cJStoreNodeIis_Store6kM_pk0_; +text: .text%Unsafe_CompareAndSwapLong; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cNtestI_regNodeMideal_Opcode6kM_i_; +text: .text%__1cTAbstractInterpreterLmethod_kind6FnMmethodHandle__n0AKMethodKind__; +text: .text%__1cJStoreNodeEhash6kM_I_; +text: .text%__1cOcompI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cKmethodOperGmethod6kM_l_; +text: .text%__1cHTypeAryRary_must_be_exact6kM_i_; +text: .text%__1cKstoreINodeMideal_Opcode6kM_i_; +text: .text%__1cGGCTask2t6M_v_; +text: .text%__1cOcompU_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cNSafePointNodeOnext_exception6kM_p0_; +text: .text%__1cOindOffset8OperNbase_position6kM_i_; +text: .text%__1cOindOffset8OperNconstant_disp6kM_i_; +text: .text%__1cKjmpDirNodeHis_Goto6kM_I_; +text: .text%__1cENodeHset_req6MIp0_v_; +text: .text%__1cTconstantPoolOopDescNklass_name_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cSObjectSynchronizerKfast_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cSObjectSynchronizerJfast_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cWShouldNotReachHereNodePoper_input_base6kM_I_; +text: .text%__1cMMergeMemNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cGThreadLis_in_stack6kMpC_i_; +text: .text%__1cLIfFalseNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKJavaThreadNis_lock_owned6kMpC_i_; +text: .text%__1cXAdaptiveWeightedAverageYcompute_adaptive_average6Mff_f_; +text: .text%__1cOcompU_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cJloadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvI2L_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cSPSPromotionManagerbBgc_thread_promotion_manager6Fi_p0_; +text: .text%__1cNmethodOopDescMintrinsic_id6kM_nMvmIntrinsicsCID__; +text: .text%__1cKstorePNodeJnum_opnds6kM_I_; +text: .text%__1cQinstanceRefKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQinstanceRefKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMMergeMemNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLciSignatureLreturn_type6kM_pnGciType__; +text: .text%__1cMMergeMemNodeQclone_all_memory6FpnENode__p0_; +text: .text%__1cIGraphKitJclone_map6M_pnNSafePointNode__; +text: .text%__1cLMachNopNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJchar2type6Fc_nJBasicType__; +text: .text%__1cJFieldTypeKbasic_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cKRelocationLspec_simple6FnJrelocInfoJrelocType__nQRelocationHolder__; +text: .text%__1cICallNodeLis_CallLeaf6kM_pknMCallLeafNode__; +text: .text%__1cLrecord_bias6FpknIPhaseIFG_ii_v_: coalesce.o; +text: .text%__1cJrRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMObjectLocker2t6MnGHandle_pnGThread__v_; +text: .text%__1cMObjectLocker2T6M_v_; +text: .text%__1cNloadRangeNodeMideal_Opcode6kM_i_; +text: .text%__1cRMachSafePointNodeRis_safepoint_node6kM_i_; +text: .text%__1cHTypeIntFempty6kM_i_; +text: .text%__1cRInvocationCounterJset_state6Mn0AFState__v_; +text: .text%__1cRInvocationCounterFreset6M_v_; +text: .text%__1cKTypeOopPtrWmake_from_klass_common6FpnHciKlass_ii_pk0_; +text: .text%__1cRInvocationCounterEinit6M_v_; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_; +text: .text%__1cLPhaseValuesFwiden6kMpknEType_3_3_; +text: .text%__1cTconstantPoolOopDescRname_ref_index_at6Mi_i_; +text: .text%__1cIHaltNodeLbottom_type6kM_pknEType__; +text: .text%__1cRRawBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cNtestI_regNodeHtwo_adr6kM_I_; +text: .text%__1cSsafePoint_pollNodeMideal_Opcode6kM_i_; +text: .text%__1cLOopRecorderOallocate_index6MpnI_jobject__i_; +text: .text%__1cYDebugInformationRecorderNappend_handle6MpnI_jobject__i_; +text: .text%__1cYCallStaticJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYCallStaticJavaDirectNodeSalignment_required6kM_i_; +text: .text%__1cMloadConPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGPcDescHreal_pc6kMpknHnmethod__pC_; +text: .text%__1cKRegionNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOPhaseIdealLoopIsplit_up6MpnENode_22_i_; +text: .text%__1cWConstantPoolCacheEntryRset_initial_state6Mi_v_; +text: .text%__1cKNativeCallGverify6M_v_; +text: .text%__1cNCompileBrokerLmaybe_block6F_v_; +text: .text%__1cFPhase2t6Mn0ALPhaseNumber__v_; +text: .text%__1cJloadINodeMideal_Opcode6kM_i_; +text: .text%__1cNLoadKlassNodeGOpcode6kM_i_; +text: .text%__1cIBoolNodeKmatch_edge6kMI_I_; +text: .text%__1cITypeLongEmake6Fxx_pk0_; +text: .text%__1cKRegionNodeOis_block_start6kM_i_; +text: .text%__1cCosGmalloc6FL_pv_; +text: .text%__1cXAdaptiveWeightedAverageGsample6Mf_v_; +text: .text%__1cFStateM_sub_Op_RegP6MpknENode__v_; +text: .text%__1cPcheckCastPPNodeHtwo_adr6kM_I_; +text: .text%__1cRMachNullCheckNodeQis_MachNullCheck6M_p0_; +text: .text%__1cOGenerateOopMapGppush16MnNCellTypeState__v_; +text: .text%__1cGBitMapOset_difference6M0_v_; +text: .text%__1cMvalue_of_loc6FppnHoopDesc__l_; +text: .text%__1cRmethodDataOopDescTbytecode_cell_count6FnJBytecodesECode__i_; +text: .text%__1cRmethodDataOopDescRcompute_data_size6FpnOBytecodeStream__i_; +text: .text%__1cRmethodDataOopDescPinitialize_data6MpnOBytecodeStream_i_i_; +text: .text%__1cOcompI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cLSymbolTableJbasic_add6MipCiIpnGThread__pnNsymbolOopDesc__; +text: .text%__1cLsymbolKlassPallocate_symbol6MpCipnGThread__pnNsymbolOopDesc__; +text: .text%__1cNGrowableArray4CpnKciTypeFlowFBlock__2t6MpnFArena_iirk2_v_; +text: .text%__1cLPhaseValuesHlongcon6Mx_pnIConLNode__; +text: .text%__1cKDictionaryEfind6MiInMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cOcompI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cKTypeAryPtrFxmeet6kMpknEType__3_; +text: .text%__1cMPhaseChaitinJsplit_USE6MpnENode_pnFBlock_2IIiinNGrowableArray4CI__i_I_; +text: .text%__1cNCatchProjNodeMis_CatchProj6kM_pk0_; +text: .text%__1cOcompU_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cNSafePointNodeGOpcode6kM_i_; +text: .text%__1cTconvI2L_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cMTypeKlassPtrEhash6kM_i_; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverYlookup_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cMciMethodDataLbci_to_data6Mi_pnLProfileData__; +text: .text%JVM_GetCPMethodSignatureUTF; +text: .text%__1cPfieldDescriptorKinitialize6MpnMklassOopDesc_i_v_; +text: .text%__1cIciMethodbCinterpreter_invocation_count6M_i_; +text: .text%__1cMMergeMemNodeNset_memory_at6MIpnENode__v_; +text: .text%JVM_CurrentThread; +text: .text%__1cPClassFileParserbLparse_constant_pool_nameandtype_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKstoreINodeJnum_opnds6kM_I_; +text: .text%__1cHTypeAryEhash6kM_i_; +text: .text%JVM_GetClassModifiers; +text: .text%JVM_GetClassAccessFlags; +text: .text%__1cRAbstractAssemblerGa_byte6Mi_v_; +text: .text%__1cJAssemblerGprefix6Mn0AGPrefix__v_; +text: .text%__1cKMemBarNodeKmatch_edge6kMI_I_; +text: .text%__1cMMergeMemNodePset_base_memory6MpnENode__v_; +text: .text%__1cRAbstractAssembler2t6MpnKCodeBuffer__v_; +text: .text%__1cCosEfree6Fpv_v_; +text: .text%__1cENodeDcmp6kMrk0_I_; +text: .text%__1cMLinkResolverbAcheck_method_accessability6FnLKlassHandle_11nMmethodHandle_pnGThread__v_; +text: .text%__1cParrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cParrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMrax_RegPOperEtype6kM_pknEType__; +text: .text%__1cKTypeRawPtrEhash6kM_i_; +text: .text%__1cLmethodKlassNoop_is_method6kM_i_; +text: .text%__1cXindIndexScaleOffsetOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cPSignatureStreamJis_object6kM_i_; +text: .text%__1cVCompressedWriteStream2t6Mi_v_; +text: .text%__1cENode2t6Mp01_v_; +text: .text%__1cHAddNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJStartNodeIis_Start6M_p0_; +text: .text%__1cMURShiftINodeGOpcode6kM_i_; +text: .text%__1cNGrowableArray4CpnMMonitorValue__2t6Mii_v_; +text: .text%__1cYDebugInformationRecorderOdescribe_scope6MpnIciMethod_ipnKDebugToken_44_v_; +text: .text%__1cYDebugInformationRecorderVcreate_monitor_values6MpnNGrowableArray4CpnMMonitorValue____pnKDebugToken__; +text: .text%__1cYDebugInformationRecorderYserialize_monitor_values6MpnNGrowableArray4CpnMMonitorValue____i_; +text: .text%__1cMloadConINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMachNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cKReflectionTverify_class_access6FpnMklassOopDesc_2i_i_; +text: .text%__1cOMethodLivenessKBasicBlockWcompute_gen_kill_range6MpnQciBytecodeStream__v_; +text: .text%__1cJAssemblerJemit_data6MirknQRelocationHolder_i_v_; +text: .text%__1cIMachOperMdisp_as_type6kM_pknHTypePtr__; +text: .text%__1cKCompiledICWis_in_transition_state6kM_i_; +text: .text%__1cRInlineCacheBufferIcontains6FpC_i_; +text: .text%__1cOcompU_rRegNodeErule6kM_I_; +text: .text%__1cRMemBarReleaseNodeGOpcode6kM_i_; +text: .text%__1cKRelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cETypeOget_const_type6FpnGciType__pk0_; +text: .text%__1cMPhaseChaitinPset_was_spilled6MpnENode__v_; +text: .text%__1cXvirtual_call_RelocationIparse_ic6FrpnICodeBlob_rpC5rppnHoopDesc_pi_nNRelocIterator__; +text: .text%__1cLCounterDataOis_CounterData6M_i_; +text: .text%__1cRCompilationPolicyNcanBeCompiled6FnMmethodHandle__i_; +text: .text%__1cOcompU_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cKcmpOpUOperJnum_edges6kM_I_; +text: .text%__1cOrFlagsRegUOperJnum_edges6kM_I_; +text: .text%__1cKCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIGraphKitMsaved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cMtlsLoadPNodeErule6kM_I_; +text: .text%__1cIGraphKitGmemory6MI_pnENode__; +text: .text%__1cNRelocIteratorJset_limit6MpC_v_; +text: .text%__1cPcheckCastPPNodeErule6kM_I_; +text: .text%__1cOGenerateOopMapFppush6MpnNCellTypeState__v_; +text: .text%__1cSobjArrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSobjArrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQciBytecodeStreamPget_field_index6M_i_; +text: .text%__1cGThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cKHandleAreaHoops_do6MpnKOopClosure__v_; +text: .text%__1cNchunk_oops_do6FpnKOopClosure_pnFChunk_pc_L_: handles.o; +text: .text%__1cKCompiledIC2t6MpnKRelocation__v_; +text: .text%__1cMUniverseOperFclone6kM_pnIMachOper__; +text: .text%__1cJlabelOperFclone6kM_pnIMachOper__; +text: .text%__1cRaddI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cLPhaseValuesHmakecon6MpknEType__pnHConNode__; +text: .text%__1cENodeHins_req6MIp0_v_; +text: .text%__1cIGraphKitLclean_stack6Mi_v_; +text: .text%__1cKRegionNodeHhas_phi6kM_pnHPhiNode__; +text: .text%__1cNloadRangeNodePoper_input_base6kM_I_; +text: .text%__1cOMachReturnNodeNis_MachReturn6M_p0_; +text: .text%__1cNaddI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cQPreserveJVMState2t6MpnIGraphKit_i_v_; +text: .text%__1cNtestP_regNodeMcisc_operand6kM_i_; +text: .text%__1cKjmpConNodeHtwo_adr6kM_I_; +text: .text%__1cPClassFileParserbJparse_constant_pool_methodref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_; +text: .text%__1cWConstantPoolCacheEntryLis_resolved6kMnJBytecodesECode__i_; +text: .text%__1cWConstantPoolCacheEntryPbytecode_number6FnJBytecodesECode__i_; +text: .text%__1cMPhaseChaitinVmay_be_copy_of_callee6kMpnENode__i_; +text: .text%__1cNtestP_regNodeErule6kM_I_; +text: .text%__1cUThreadSafepointStateXexamine_state_of_thread6Mi_v_; +text: .text%__1cUSafepointSynchronizeOsafepoint_safe6FpnKJavaThread_nPJavaThreadState__i_; +text: .text%__1cFStateM_sub_Op_ConI6MpknENode__v_; +text: .text%__1cUArgumentSizeComputerDset6MinJBasicType__v_; +text: .text%__1cOcompU_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cHPhiNodeEmake6FpnENode_2pknEType_pknHTypePtr__p0_; +text: .text%__1cKCodeBufferOadd_stub_reloc6MpCrknQRelocationHolder_i_v_; +text: .text%__1cKCodeBufferOalloc_relocate6M_pnORelocateBuffer__; +text: .text%__1cNtestI_regNodeErule6kM_I_; +text: .text%__1cOGenerateOopMapbAget_basic_block_containing6kMi_pnKBasicBlock__; +text: .text%__1cFParseKensure_phi6Mii_pnHPhiNode__; +text: .text%__1cMLinkResolverZcheck_klass_accessability6FnLKlassHandle_1pnGThread__v_; +text: .text%__1cOGenerateOopMapSget_basic_block_at6kMi_pnKBasicBlock__; +text: .text%__1cHAddress2t6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cFciEnvSget_klass_by_index6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cKJavaThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cFStateM_sub_Op_AddP6MpknENode__v_; +text: .text%__1cICallNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cOcompI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cPciObjectFactoryNinit_ident_of6MpnIciObject__v_; +text: .text%__1cIciObjectJset_ident6MI_v_; +text: .text%__1cLMachNopNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLMachNopNodeMideal_Opcode6kM_i_; +text: .text%__1cKJNIHandlesKmake_local6FpnHoopDesc__pnI_jobject__; +text: .text%__1cITypeFuncEhash6kM_i_; +text: .text%__1cOcompI_rRegNodeErule6kM_I_; +text: .text%__1cIciMethodPliveness_at_bci6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessPget_liveness_at6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessKBasicBlockPget_liveness_at6MpnIciMethod_i_nGBitMap__; +text: .text%__1cITypeLongFempty6kM_i_; +text: .text%__1cMTypeKlassPtrCeq6kMpknEType__i_; +text: .text%__1cJAssemblerJemit_data6MinJrelocInfoJrelocType_i_v_; +text: .text%__1cJLoadSNodeGOpcode6kM_i_; +text: .text%__1cRMemBarAcquireNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitObasic_plus_adr6MpnENode_2l_2_; +text: .text%__1cJVectorSet2L6MI_rnDSet__; +text: .text%__1cJVectorSetEgrow6MI_v_; +text: .text%__1cLCastP2LNodeGOpcode6kM_i_; +text: .text%__1cFKlassNlookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cJAssemblerEcall6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cPcheckCastPPNodeMideal_Opcode6kM_i_; +text: .text%__1cJLoadLNodeGOpcode6kM_i_; +text: .text%__1cMLinkResolverNresolve_klass6FrnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cTconstantPoolOopDescMklass_ref_at6MipnGThread__pnMklassOopDesc__; +text: .text%__1cRCompilationPolicyOmustBeCompiled6FnMmethodHandle__i_; +text: .text%__1cIAndINodeGOpcode6kM_i_; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_; +text: .text%__1cUParallelScavengeHeapNtlab_capacity6kM_L_; +text: .text%__1cTCreateExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIPhaseCCPFwiden6kMpknEType_3_3_; +text: .text%__1cHTypePtrJsingleton6kM_i_; +text: .text%__1cNArgumentCountDset6MinJBasicType__v_; +text: .text%__1cWShouldNotReachHereNodeGpinned6kM_i_; +text: .text%__1cNsubI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cNtestI_regNodePoper_input_base6kM_I_; +text: .text%__1cLBoxLockNodeGOpcode6kM_i_; +text: .text%__1cWShouldNotReachHereNodeHtwo_adr6kM_I_; +text: .text%__1cQMachCallJavaNodePis_MachCallJava6M_p0_; +text: .text%__1cKStoreBNodeGOpcode6kM_i_; +text: .text%__1cJAssemblerMemit_operand6MpnMRegisterImpl_22nHAddressLScaleFactor_ipCrknQRelocationHolder__v_; +text: .text%__1cJAssemblerMemit_operand6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cOGenerateOopMapHget_var6Mi_nNCellTypeState__; +text: .text%__1cITypeFuncCeq6kMpknEType__i_; +text: .text%__1cMloadConLNodePoper_input_base6kM_I_; +text: .text%__1cMPhaseIterGVNHmakecon6MpknEType__pnHConNode__; +text: .text%__1cWShouldNotReachHereNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHoopDescGverify6M_v_; +text: .text%__1cQconstMethodKlassSoop_is_constMethod6kM_i_; +text: .text%__1cRconstantPoolKlassToop_is_constantPool6kM_i_; +text: .text%__1cOcompP_rRegNodePoper_input_base6kM_I_; +text: .text%__1cHTypePtrHget_con6kM_l_; +text: .text%__1cHMatcherWis_short_branch_offset6Mi_i_; +text: .text%__1cMloadConLNodeHtwo_adr6kM_I_; +text: .text%__1cMTypeKlassPtr2t6MnHTypePtrDPTR_pnHciKlass_i_v_; +text: .text%__1cYCallStaticJavaDirectNodeKmethod_set6Ml_v_; +text: .text%__1cWMachCallStaticJavaNodePret_addr_offset6M_i_; +text: .text%__1cYCallStaticJavaDirectNodePcompute_padding6kMi_i_; +text: .text%__1cJVectorSet2t6MpnFArena__v_; +text: .text%__1cFStateM_sub_Op_RegI6MpknENode__v_; +text: .text%__1cICmpPNodeDsub6kMpknEType_3_3_; +text: .text%__1cNloadConP0NodePoper_input_base6kM_I_; +text: .text%__1cOAbstractICachePinvalidate_word6FpC_v_; +text: .text%__1cRNativeInstructionFwrote6Mi_v_; +text: .text%__1cMURShiftLNodeGOpcode6kM_i_; +text: .text%__1cOrFlagsRegUOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIciMethodLscale_count6Mi_i_; +text: .text%__1cLjmpConUNodeJnum_opnds6kM_I_; +text: .text%__1cQciBytecodeStreamQget_method_index6M_i_; +text: .text%__1cFciEnvbTget_instance_klass_for_declared_method_holder6FpnHciKlass__pnPciInstanceKlass__; +text: .text%__1cPSignatureStream2t6MnMsymbolHandle_i_v_; +text: .text%JVM_GetMethodIxLocalsCount; +text: .text%__1cJlabelOperFlabel6kM_pnFLabel__; +text: .text%__1cGOopMapJheap_size6kM_i_; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cMloadConLNodeErule6kM_I_; +text: .text%__1cOMacroAssemblerZneeds_explicit_null_check6Fi_i_; +text: .text%__1cNSafePointNode2t6MIpnIJVMState__v_; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__; +text: .text%__1cNloadKlassNodeMideal_Opcode6kM_i_; +text: .text%__1cWConstantPoolCacheEntryIas_flags6MnITosState_iiiii_i_; +text: .text%__1cNloadConP0NodeHtwo_adr6kM_I_; +text: .text%__1cWThreadLocalAllocBufferVinitialize_statistics6M_v_; +text: .text%__1cWThreadLocalAllocBufferFclear6M_v_; +text: .text%__1cWThreadLocalAllocBufferVaccumulate_statistics6MLi_v_; +text: .text%__1cWThreadLocalAllocBufferImax_size6F_L_; +text: .text%__1cWThreadLocalAllocBufferGresize6M_v_; +text: .text%__1cJloadINodePoper_input_base6kM_I_; +text: .text%__1cIRootNodeLbottom_type6kM_pknEType__; +text: .text%__1cWConstantPoolCacheEntryGverify6kMpnMoutputStream__v_; +text: .text%__1cHCompileYout_preserve_stack_slots6F_I_; +text: .text%__1cFParsePload_state_from6Mpn0AFBlock__v_; +text: .text%__1cFParseMmerge_common6Mpn0AFBlock_i_v_; +text: .text%__1cNloadRangeNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cIGraphKitQkill_dead_locals6M_v_; +text: .text%__1cKMemBarNodeLbottom_type6kM_pknEType__; +text: .text%__1cKimmL32OperJconstantL6kM_x_; +text: .text%__1cIciObject2t6MnGHandle__v_; +text: .text%__1cITypeFuncEmake6FpknJTypeTuple_3_pk0_; +text: .text%__1cLConvI2LNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPciObjectFactoryGinsert6MipnIciObject_pnNGrowableArray4C2___v_; +text: .text%__1cRaddP_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cLBoxLockNodeJideal_reg6kM_I_; +text: .text%__1cPClassFileParserYverify_legal_method_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cICHeapObj2n6FL_pv_; +text: .text%__1cFframeUentry_frame_is_first6kM_i_; +text: .text%__1cKInlineTreeJcallee_at6kMipnIciMethod__p0_; +text: .text%__1cOFastUnlockNodeGOpcode6kM_i_; +text: .text%__1cFStateM_sub_Op_ConL6MpknENode__v_; +text: .text%__1cIHaltNodeGpinned6kM_i_; +text: .text%__1cMTypeKlassPtrEmake6FnHTypePtrDPTR_pnHciKlass_i_pk0_; +text: .text%__1cOoop_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cOCallRelocationFvalue6M_pC_; +text: .text%__1cKRelocationXpd_set_call_destination6MpCl_v_; +text: .text%__1cOCallRelocationWfix_relocation_at_move6Ml_v_; +text: .text%__1cKRelocationTpd_call_destination6M_pC_; +text: .text%__1cOCallRelocationPset_destination6MpCl_v_; +text: .text%__1cHcommute6FpnENode_ii_i_: addnode.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__; +text: .text%__1cENodeQlatency_from_use6kMrnLBlock_Array_rnNGrowableArray4CI__pk0p0_i_; +text: .text%__1cHAddNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNinstanceKlassQfind_local_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__i_; +text: .text%__1cMmerge_region6FpnKRegionNode_pnIPhaseGVN__pnENode__: cfgnode.o; +text: .text%__1cNloadConP0NodeErule6kM_I_; +text: .text%__1cIGraphKitMreset_memory6M_pnENode__; +text: .text%__1cPciObjectFactoryRcreate_new_object6MpnHoopDesc__pnIciObject__; +text: .text%__1cNloadRangeNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNloadConI0NodeMideal_Opcode6kM_i_; +text: .text%__1cJloadPNodeHtwo_adr6kM_I_; +text: .text%__1cNSignatureInfoGdo_int6M_v_; +text: .text%__1cKjmpDirNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPThreadRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cPThreadRootsTaskEname6M_pc_; +text: .text%__1cUThreadSafepointStateMroll_forward6Mn0AMsuspend_type__v_; +text: .text%__1cUThreadSafepointStateHrestart6M_v_; +text: .text%__1cLciSignatureHtype_at6kMi_pnGciType__; +text: .text%__1cJrRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIAddLNodeGOpcode6kM_i_; +text: .text%__1cJLoadPNodeJideal_reg6kM_I_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cOkill_dead_code6FpnENode_pnMPhaseIterGVN__i_: node.o; +text: .text%__1cKjmpDirNodeFclone6kM_pnENode__; +text: .text%__1cOcompI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cFParseFBlockRsuccessor_for_bci6Mi_p1_; +text: .text%__1cNGrowableArray4CpnOMethodLivenessKBasicBlock__2t6MpnFArena_iirk2_v_; +text: .text%__1cQSystemDictionaryEfind6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNtestP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNincI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cICallInfoDset6MnLKlassHandle_1nMmethodHandle_2ipnGThread__v_; +text: .text%__1cHOrINodeGOpcode6kM_i_; +text: .text%__1cOcompI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMPhaseChaitinJsplit_DEF6MpnENode_pnFBlock_iIp25nNGrowableArray4CI__i_I_; +text: .text%__1cENodeHis_Type6M_pnITypeNode__; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_; +text: .text%__1cICHeapObj2k6Fpv_v_; +text: .text%__1cRaddP_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cICmpINodeDsub6kMpknEType_3_3_; +text: .text%__1cENode2n6FLi_pv_; +text: .text%__1cJTraceTime2t6MpkcpnMelapsedTimer_iipnMoutputStream__v_; +text: .text%__1cIAddINodeGadd_id6kM_pknEType__; +text: .text%__1cKStoreCNodeGOpcode6kM_i_; +text: .text%__1cTconvI2L_reg_regNodeMcisc_operand6kM_i_; +text: .text%__1cHMemNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cSvframeStreamCommonPfill_from_frame6M_i_; +text: .text%__1cILoadNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cJStoreNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNFingerprinterLfingerprint6M_L_; +text: .text%__1cLConvI2LNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOGenerateOopMapGppload6MpnNCellTypeState_i_v_; +text: .text%__1cIGraphKitRnull_check_common6MpnENode_nJBasicType_i_2_; +text: .text%__1cKBlock_ListGremove6MI_v_; +text: .text%__1cKMemoryPoolYrecord_peak_memory_usage6M_v_; +text: .text%__1cKciTypeFlowGJsrSetJcopy_into6Mp1_v_; +text: .text%__1cICmpLNodeGOpcode6kM_i_; +text: .text%__1cLklassVtableTupdate_super_vtable6MpnNinstanceKlass_pnNmethodOopDesc_i_i_; +text: .text%__1cLcastP2LNodePoper_input_base6kM_I_; +text: .text%__1cQStackFrameStream2t6MpnKJavaThread_i_v_; +text: .text%__1cTciConstantPoolCacheEfind6Mi_i_; +text: .text%__1cOPhaseIdealLoopGspinup6MpnENode_2222pnLsmall_cache__2_; +text: .text%__1cMVirtualSpaceOcommitted_size6kM_L_; +text: .text%__1cNGrowableArray4CpnIciObject__Praw_at_put_grow6Mirk14_v_; +text: .text%__1cIRootNodeNis_block_proj6kM_pknENode__; +text: .text%__1cHOopFlowEmake6FpnFArena_i_p0_; +text: .text%__1cJloadLNodeErule6kM_I_; +text: .text%__1cNloadConI0NodeLbottom_type6kM_pknEType__; +text: .text%__1cJimmI0OperIconstant6kM_l_; +text: .text%__1cScompI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cLBoxLockNodeLbottom_type6kM_pknEType__; +text: .text%__1cNaddI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cOPhaseIdealLoopKhandle_use6MpnENode_2pnLsmall_cache_22222_v_; +text: .text%__1cOPhaseIdealLoopOfind_use_block6MpnENode_22222_2_; +text: .text%__1cNmethodOopDescbGupdate_compiled_code_entry_point6Mi_v_; +text: .text%__1cJStoreNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNSignatureInfoHdo_void6M_v_; +text: .text%__1cLAdapterInfoKhash_value6kM_l_; +text: .text%JVM_GetCPMethodClassNameUTF; +text: .text%__1cHOopFlowFclone6Mp0i_v_; +text: .text%__1cRSignatureIteratorSiterate_parameters6ML_v_; +text: .text%__1cILoopNodeHis_Loop6M_p0_; +text: .text%__1cPindOffset32OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cPindOffset32OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMCallLeafNodeGOpcode6kM_i_; +text: .text%__1cNmethodOopDescPis_final_method6kM_i_; +text: .text%__1cSComputeAdapterInfoHcompute6Mplii_v_; +text: .text%__1cLAdapterInfoHcompute6MnMmethodHandle_i_v_; +text: .text%__1cLAdapterInfo2T6M_v_; +text: .text%__1cSComputeAdapterInfoLreturn_type6MnJBasicType__i_; +text: .text%__1cSComputeAdapterInfoMsize_in_bits6FnMmethodHandle__i_; +text: .text%__1cMAdapterCacheGlookup6MpnLAdapterInfo__pnMBasicAdapter__; +text: .text%__1cJloadINodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cPadd_derived_oop6FppnHoopDesc_2_v_: oopMap.o; +text: .text%__1cTDerivedPointerTableDadd6FppnHoopDesc_3_v_; +text: .text%__1cFParseFBlockJinit_node6Mp0i_v_; +text: .text%__1cFParseFBlockKinit_graph6Mp0_v_; +text: .text%__1cOcompP_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cSObjectSynchronizerKslow_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cKjmpDirNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMLinkResolverbFlinktime_resolve_virtual_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_; +text: .text%__1cKCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJTraceTime2T6M_v_; +text: .text%__1cKciTypeFlowNmake_range_at6Mi_pn0AFRange__; +text: .text%__1cNmethodOopDescbAcompute_size_of_parameters6MpnGThread__v_; +text: .text%__1cKoopFactoryKnew_method6FinLAccessFlags_iiipnGThread__pnNmethodOopDesc__; +text: .text%__1cNmethodOopDescLobject_size6Fi_i_; +text: .text%__1cSconstMethodOopDescLobject_size6Fiiii_i_; +text: .text%__1cLklassVtableWneeds_new_vtable_entry6FpnNmethodOopDesc_pnMklassOopDesc_pnHoopDesc_pnNsymbolOopDesc_nLAccessFlags__i_; +text: .text%__1cSconstMethodOopDescZset_inlined_tables_length6Miii_v_; +text: .text%__1cLmethodKlassIallocate6MnRconstMethodHandle_nLAccessFlags_pnGThread__pnNmethodOopDesc__; +text: .text%__1cQconstMethodKlassIallocate6MiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cNmethodOopDescJinit_code6M_v_; +text: .text%__1cKoopFactoryPnew_constMethod6FiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cPClassFileParserMparse_method6MnSconstantPoolHandle_ipnLAccessFlags_pnPtypeArrayHandle_55pnGThread__nMmethodHandle__; +text: .text%__1cFBlockUhoist_LCA_above_defs6Mp01IrnLBlock_Array__1_; +text: .text%__1cScompI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cPciInstanceKlassLfield_cache6M_pnTciConstantPoolCache__; +text: .text%__1cHciFieldJwill_link6MpnPciInstanceKlass_nJBytecodesECode__i_; +text: .text%__1cFciEnvXget_field_by_index_impl6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cQciBytecodeStreamJget_field6Mri_pnHciField__; +text: .text%__1cFciEnvSget_field_by_index6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cKTypeOopPtrFempty6kM_i_; +text: .text%__1cWConstantPoolCacheEntryOset_bytecode_16MnJBytecodesECode__v_; +text: .text%__1cKCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMMergeMemNodeNgrow_to_match6Mpk0_v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_; +text: .text%__1cTCreateExceptionNodePoper_input_base6kM_I_; +text: .text%__1cPciInstanceKlassYunique_concrete_subklass6M_p0_; +text: .text%__1cLStringTableGlookup6MipHiI_pnHoopDesc__; +text: .text%__1cLBoxLockNodeHsize_of6kM_I_; +text: .text%__1cIciObjectIencoding6M_pnI_jobject__; +text: .text%__1cPClassFileParserbDverify_legal_method_modifiers6MiinMsymbolHandle_pnGThread__v_; +text: .text%__1cIPhaseGVNUtransform_no_reclaim6MpnENode__2_; +text: .text%__1cIRewriterOrewrite_method6FnMmethodHandle_rnIintArray_pnGThread__1_; +text: .text%__1cNmethodOopDescLlink_method6FnMmethodHandle__v_; +text: .text%__1cNloadKlassNodePoper_input_base6kM_I_; +text: .text%__1cNObjectMonitorEexit6MpnGThread__v_; +text: .text%__1cPClassFileParserZskip_over_field_signature6MpciIpnGThread__1_; +text: .text%__1cMMergeMemNode2t6MpnENode__v_; +text: .text%__1cMMergeMemNodeRmake_empty_memory6F_pnENode__; +text: .text%__1cNinstanceKlassVshould_be_initialized6kM_i_; +text: .text%__1cNtestP_regNodeJnum_opnds6kM_I_; +text: .text%__1cJStartNodeGis_CFG6kM_i_; +text: .text%__1cRaddI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_; +text: .text%__1cMindIndexOperJnum_edges6kM_I_; +text: .text%__1cRInterpretedRFrameKtop_method6kM_nMmethodHandle__; +text: .text%__1cKGCStatInfoMset_gc_usage6MinLMemoryUsage_i_v_; +text: .text%__1cXmembar_acquire_lockNodeLbottom_type6kM_pknEType__; +text: .text%__1cQPreserveJVMState2T6M_v_; +text: .text%__1cLRuntimeStubIis_alive6kM_i_; +text: .text%__1cMWarmCallInfoHis_cold6kM_i_; +text: .text%__1cNObjectMonitorFenter6MpnGThread__v_; +text: .text%__1cKjmpConNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIemit_d646FrnKCodeBuffer_l_v_; +text: .text%__1cFParseFmerge6Mi_v_; +text: .text%__1cOPhaseIdealLoopIset_idom6MpnENode_2I_v_; +text: .text%__1cIAddINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFParseMdo_one_block6M_v_; +text: .text%__1cFParseFBlockMrecord_state6Mp0_v_; +text: .text%__1cNtestP_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cJloadLNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cIPhaseIFGFUnion6MII_v_; +text: .text%__1cNloadRangeNodeJnum_opnds6kM_I_; +text: .text%__1cWConstantPoolCacheEntryOset_bytecode_26MnJBytecodesECode__v_; +text: .text%__1cOcompP_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cIBoolNodeJideal_reg6kM_I_; +text: .text%__1cHCmpNodeJideal_reg6kM_I_; +text: .text%__1cFStateM_sub_Op_Bool6MpknENode__v_; +text: .text%__1cJCatchNodeIis_Catch6kM_pk0_; +text: .text%__1cJLoadBNodeGOpcode6kM_i_; +text: .text%__1cENodeHlatency6MI_I_; +text: .text%__1cIGraphKit2t6MpnIJVMState__v_; +text: .text%__1cKTypeAryPtrFklass6kM_pnHciKlass__; +text: .text%__1cTciConstantPoolCacheDget6Mi_pv_; +text: .text%__1cNmethodOopDescIbcp_from6kMi_pC_; +text: .text%__1cKTypeOopPtrHget_con6kM_l_; +text: .text%__1cMPhaseChaitinKprompt_use6MpnFBlock_I_i_; +text: .text%__1cIJVMStateLdebug_depth6kM_I_; +text: .text%__1cIGraphKitTadd_safepoint_edges6MpnNSafePointNode_i_v_; +text: .text%__1cENodeNadd_req_batch6Mp0I_v_; +text: .text%__1cIJVMStateKclone_deep6kM_p0_; +text: .text%__1cFStateK_sub_Op_If6MpknENode__v_; +text: .text%__1cXindIndexScaleOffsetOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cXindIndexScaleOffsetOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cGBitMapVset_union_with_result6M0_i_; +text: .text%__1cNSafePointNodeEhash6kM_I_; +text: .text%__1cMelapsedTimerFstart6M_v_; +text: .text%__1cJStartNodeGOpcode6kM_i_; +text: .text%__1cMelapsedTimerEstop6M_v_; +text: .text%__1cOPhaseIdealLoopQconditional_move6MpnENode__2_; +text: .text%__1cJloadLNodeMideal_Opcode6kM_i_; +text: .text%__1cOMethodLivenessKBasicBlockJstore_one6Mi_v_; +text: .text%__1cTC2IAdapterGeneratorXlazy_std_verified_entry6FnMmethodHandle__pC_; +text: .text%__1cPindOffset32OperJnum_edges6kM_I_; +text: .text%__1cPFieldAccessInfoDset6MnLKlassHandle_nMsymbolHandle_iinJBasicType_nLAccessFlags__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_iipnGThread__v_; +text: .text%__1cMLinkResolverZcheck_field_accessability6FnLKlassHandle_11rnPfieldDescriptor_pnGThread__v_; +text: .text%__1cNsubI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cTCreateExceptionNodeHtwo_adr6kM_I_; +text: .text%__1cPindOffset32OperFscale6kM_i_; +text: .text%__1cHAddNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cICmpPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cbFCompressedLineNumberWriteStream2t6Mi_v_; +text: .text%__1cPClassFileParserWparse_linenumber_table6MIIpipnGThread__pC_; +text: .text%__1cSconstMethodOopDescbBcompressed_linenumber_table6kM_pC_; +text: .text%__1cHTypePtrLdual_offset6kM_i_; +text: .text%__1cNMachIdealNodePoper_input_base6kM_I_; +text: .text%__1cSObjectSynchronizerOinflate_helper6FpnHoopDesc__pnNObjectMonitor__; +text: .text%__1cKciTypeFlowIblock_at6Mipn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cKciTypeFlowFRangeNget_block_for6Mpn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cSvframeStreamCommonbBfill_from_interpreter_frame6M_v_; +text: .text%__1cHMonitorEwait6Mil_i_; +text: .text%__1cLcastP2LNodeMideal_Opcode6kM_i_; +text: .text%__1cNloadKlassNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cSObjectSynchronizerJslow_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cPJavaCallWrapperHoops_do6MpnKOopClosure__v_; +text: .text%__1cFframeNoops_entry_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cXindIndexScaleOffsetOperFscale6kM_i_; +text: .text%__1cQciBytecodeStreamKget_method6Mri_pnIciMethod__; +text: .text%__1cFciEnvTget_method_by_index6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvYget_method_by_index_impl6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvNlookup_method6MpnNinstanceKlass_2pnNsymbolOopDesc_4nJBytecodesECode__pnNmethodOopDesc__; +text: .text%__1cYinternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cNstoreImmBNodePoper_input_base6kM_I_; +text: .text%__1cNLoadRangeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cITypeFuncEmake6FpnIciMethod__pk0_; +text: .text%__1cMindirectOperJnum_edges6kM_I_; +text: .text%__1cKCompiledICOic_destination6kM_pC_; +text: .text%__1cTconvI2L_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cICmpUNodeDsub6kMpknEType_3_3_; +text: .text%__1cPSignatureStreamJas_symbol6MpnGThread__pnNsymbolOopDesc__; +text: .text%__1cRshrL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__; +text: .text%__1cPClassFileParserbFparse_constant_pool_class_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cOMethodLivenessKBasicBlockMmerge_normal6MnGBitMap__i_; +text: .text%__1cTleaPIdxScaleOffNodeHtwo_adr6kM_I_; +text: .text%__1cETypeFwiden6kMpk0_2_; +text: .text%__1cKciTypeFlowLStateVector2t6Mp0_v_; +text: .text%__1cNCatchProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cOcompU_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCatchProjNodeHsize_of6kM_I_; +text: .text%__1cNCatchProjNodeEhash6kM_I_; +text: .text%__1cSvframeStreamCommonEnext6M_v_; +text: .text%__1cFKlassIsubklass6kM_p0_; +text: .text%__1cKciTypeFlowFBlockPis_simpler_than6Mp1_i_; +text: .text%__1cJimmI8OperIconstant6kM_l_; +text: .text%__1cIAddPNodeQmach_bottom_type6FpknIMachNode__pknEType__; +text: .text%__1cILoadNodeHsize_of6kM_I_; +text: .text%__1cHMatcherVReduceInst_Chain_Rule6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cURethrowExceptionNodeNis_block_proj6kM_pknENode__; +text: .text%__1cNincI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cLjmpConUNodeHtwo_adr6kM_I_; +text: .text%__1cHMatcherScalling_convention6FpnLOptoRegPair_Ii_v_; +text: .text%__1cKPerfStringKset_string6Mpkc_v_; +text: .text%__1cENodeLnonnull_req6kM_p0_; +text: .text%__1cICmpINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHTypeAryCeq6kMpknEType__i_; +text: .text%__1cQSystemDictionaryKfind_class6FiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cKDictionaryKfind_class6MiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cQUnique_Node_ListEpush6MpnENode__v_; +text: .text%__1cILoopNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitTadd_exception_state6MpnNSafePointNode__v_; +text: .text%__1cJloadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__; +text: .text%__1cOPhaseIdealLoopRregister_new_node6MpnENode_2_v_; +text: .text%__1cQPSGenerationPoolImax_size6kM_L_; +text: .text%__1cQPSGenerationPoolNused_in_bytes6M_L_; +text: .text%__1cQPSGenerationPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cOMethodLivenessNwork_list_get6M_pn0AKBasicBlock__; +text: .text%__1cNinstanceKlassKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cICallNodeOis_CallRuntime6kM_pknPCallRuntimeNode__; +text: .text%__1cHTypeAryFxmeet6kMpknEType__3_; +text: .text%__1cNstoreImmBNodeMideal_Opcode6kM_i_; +text: .text%__1cKciTypeFlowLStateVectorEmeet6Mpk1_i_; +text: .text%__1cIMachNodeTmay_be_short_branch6kM_i_; +text: .text%__1cJloadINodeJnum_opnds6kM_I_; +text: .text%__1cNaddI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cRMachSafePointNode2t6M_v_; +text: .text%__1cHMatcherKmatch_sfpt6MpnNSafePointNode__pnIMachNode__; +text: .text%__1cOcompP_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cMPhaseChaitinKFind_const6kMpknENode__I_; +text: .text%__1cMPhaseChaitinKFind_const6kMI_I_; +text: .text%__1cNinstanceKlassKinitialize6MpnGThread__v_; +text: .text%__1cRInterpretedRFrameEinit6M_v_; +text: .text%__1cHemit_cc6FrnKCodeBuffer_ii_v_; +text: .text%__1cNtestI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframeMpd_gc_epilog6M_v_; +text: .text%__1cFframeLgc_epilogue6M_v_; +text: .text%__1cRframe_gc_prologue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cRframe_gc_epilogue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeLgc_prologue6M_v_; +text: .text%__1cOGenerateOopMapUreachable_basicblock6Fp0ipi_v_; +text: .text%__1cXvirtual_call_RelocationJfirst_oop6M_pC_; +text: .text%__1cXvirtual_call_RelocationJoop_limit6M_pC_; +text: .text%__1cMciMethodDataLhas_trap_at6MpnLProfileData_i_i_; +text: .text%__1cKciTypeFlowLStateVectorOpush_translate6MpnGciType__v_; +text: .text%__1cOGenerateOopMapJdo_method6Miiii_v_; +text: .text%__1cMloadConPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIimmPOperIconstant6kM_l_; +text: .text%__1cIimmPOperPconstant_is_oop6kM_i_; +text: .text%__1cOleaPIdxOffNodeHtwo_adr6kM_I_; +text: .text%__1cHGCCauseJto_string6Fn0AFCause__pkc_; +text: .text%__1cTleaPIdxScaleOffNodeErule6kM_I_; +text: .text%JVM_IsNaN; +text: .text%__1cXinsert_anti_dependences6FrpnFBlock_pnENode_rnLBlock_Array__i_: gcm.o; +text: .text%__1cLOptoRuntimebCcomplete_monitor_unlocking_C6FpnHoopDesc_pnJBasicLock__v_; +text: .text%__1cJloadINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOptoRuntimebAcomplete_monitor_locking_C6FpnHoopDesc_pnJBasicLock_pnKJavaThread__v_; +text: .text%__1cHCompileKTracePhase2t6MpkcpnMelapsedTimer_i_v_; +text: .text%__1cHCompileKTracePhase2T6M_v_; +text: .text%__1cOGenerateOopMapTmerge_state_into_bb6MpnKBasicBlock__v_; +text: .text%__1cHMatcherPc_frame_pointer6kM_i_; +text: .text%__1cFBlockKsched_call6MrnHMatcher_rnLBlock_Array_IrnJNode_List_pipnMMachCallNode_rnJVectorSet__I_; +text: .text%__1cMMachCallNode2t6M_v_; +text: .text%__1cICallNodeJideal_reg6kM_I_; +text: .text%__1cOleaPIdxOffNodeErule6kM_I_; +text: .text%__1cOGenerateOopMapEppop6MpnNCellTypeState__v_; +text: .text%__1cSCallLeafDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cOcompP_rRegNodeErule6kM_I_; +text: .text%__1cMany_RegPOperJnum_edges6kM_I_; +text: .text%__1cIGraphKitbLset_predefined_input_for_runtime_call6MpnNSafePointNode__v_; +text: .text%__1cMany_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cScompI_rReg_immNodeErule6kM_I_; +text: .text%__1cLBoxLockNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cIGraphKitOset_all_memory6MpnENode__v_; +text: .text%__1cLRegisterMap2t6Mpk0_v_; +text: .text%__1cGvframe2t6MpknFframe_pknLRegisterMap_pnKJavaThread__v_; +text: .text%__1cNmethodOopDescWwas_executed_more_than6kMi_i_; +text: .text%__1cKstoreCNodePoper_input_base6kM_I_; +text: .text%__1cVjava_lang_ClassLoaderbBnon_reflection_class_loader6FpnHoopDesc__2_; +text: .text%__1cHi2sNodeErule6kM_I_; +text: .text%__1cIMulLNodeGOpcode6kM_i_; +text: .text%__1cOPhaseIdealLoopHdom_lca6kMpnENode_2_2_; +text: .text%__1cMPrefetchNodeGOpcode6kM_i_; +text: .text%__1cSReferenceProcessorSdiscover_reference6MpnHoopDesc_nNReferenceType__i_; +text: .text%__1cSReferenceProcessorTget_discovered_list6MnNReferenceType__ppnHoopDesc__; +text: .text%__1cXjava_lang_ref_ReferenceIset_next6FpnHoopDesc_2_v_; +text: .text%__1cKciTypeFlowGJsrSet2t6MpnFArena_i_v_; +text: .text%__1cNtestI_regNodeJnum_opnds6kM_I_; +text: .text%__1cIAddINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIMachOperNconstant_disp6kM_i_; +text: .text%__1cIMachOperFscale6kM_i_; +text: .text%__1cFframeNis_java_frame6kM_i_; +text: .text%__1cOMethodLivenessKBasicBlockJpropagate6Mp0_v_; +text: .text%__1cQSystemDictionaryStry_get_next_class6F_pnMklassOopDesc__; +text: .text%__1cKDictionaryStry_get_next_class6M_pnMklassOopDesc__; +text: .text%__1cNloadKlassNodeErule6kM_I_; +text: .text%__1cIciMethodRhas_compiled_code6M_i_; +text: .text%__1cOoop_RelocationJpack_data6M_i_; +text: .text%__1cKCompiledICKcached_oop6kM_pnHoopDesc__; +text: .text%__1cGOopMap2t6Mii_v_; +text: .text%__1cJOopMapSetKadd_gc_map6MipnGOopMap__v_; +text: .text%__1cNincI_rRegNodeErule6kM_I_; +text: .text%__1cRMachSafePointNodePis_MachCallLeaf6M_pnQMachCallLeafNode__; +text: .text%__1cRMachSafePointNodeLset_oop_map6MpnGOopMap__v_; +text: .text%__1cYDebugInformationRecorderNadd_safepoint6MipnGOopMap__v_; +text: .text%__1cHCompileTProcess_OopMap_Node6MpnIMachNode_i_v_; +text: .text%__1cYDebugInformationRecorderKadd_oopmap6MipnGOopMap__v_; +text: .text%__1cHOopFlowNbuild_oop_map6MpnENode_ipnNPhaseRegAlloc_pi_pnGOopMap__; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNsubI_rRegNodeMcisc_operand6kM_i_; +text: .text%JVM_GetCPFieldClassNameUTF; +text: .text%__1cRaddI_rReg_immNodeErule6kM_I_; +text: .text%__1cRMachNullCheckNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cHRetNodeMideal_Opcode6kM_i_; +text: .text%__1cGvframeKnew_vframe6FpknFframe_pknLRegisterMap_pnKJavaThread__p0_; +text: .text%__1cNsubI_rRegNodeErule6kM_I_; +text: .text%__1cRaddP_rReg_immNodeErule6kM_I_; +text: .text%__1cPClassFileParserbGparse_constant_pool_string_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cJloadLNodePoper_input_base6kM_I_; +text: .text%__1cRshrL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cITypeLongFxdual6kM_pknEType__; +text: .text%__1cRMachSafePointNodeSis_MachCallRuntime6M_pnTMachCallRuntimeNode__; +text: .text%__1cNaddI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cENodeJset_req_X6MIp0pnMPhaseIterGVN__v_; +text: .text%__1cOcompP_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cSInterpreterRuntimePresolve_get_put6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_ipnGThread__v_; +text: .text%__1cWConstantPoolCacheEntryJset_field6MnJBytecodesECode_2nLKlassHandle_iinITosState_ii_v_; +text: .text%__1cIAndLNodeGOpcode6kM_i_; +text: .text%__1cMindIndexOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOtypeArrayKlassSallocate_permanent6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cOGenerateOopMapCpp6MpnNCellTypeState_2_v_; +text: .text%__1cMCallJavaNodeLis_CallJava6kM_pk0_; +text: .text%__1cICallNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cHCompileSflatten_alias_type6kMpknHTypePtr__3_; +text: .text%__1cRcmpFastUnlockNodePoper_input_base6kM_I_; +text: .text%__1cYCallStaticJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcompP_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cFStateW_sub_Op_CallStaticJava6MpknENode__v_; +text: .text%__1cWMachCallStaticJavaNodeVis_MachCallStaticJava6M_p0_; +text: .text%__1cRaddP_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cICallInfoDset6MnLKlassHandle_nMmethodHandle_pnGThread__v_; +text: .text%__1cSComputeAdapterInfoJdo_object6Mii_v_; +text: .text%__1cRMachSafePointNodeWis_MachCallInterpreter6M_pnXMachCallInterpreterNode__; +text: .text%__1cIGraphKitbDtransfer_exceptions_into_jvms6M_pnIJVMState__; +text: .text%__1cLConvL2INodeGOpcode6kM_i_; +text: .text%__1cOcompI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionarybEresolve_instance_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNaddI_rRegNodeErule6kM_I_; +text: .text%__1cHConNodeEmake6FpknEType__p0_; +text: .text%__1cScompI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cJLoadCNodeGOpcode6kM_i_; +text: .text%__1cYCallStaticJavaDirectNodeFreloc6kM_i_; +text: .text%__1cRcmpFastUnlockNodeMideal_Opcode6kM_i_; +text: .text%__1cJTraceTime2t6MpkciipnMoutputStream__v_; +text: .text%__1cWstatic_stub_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cIGraphKitQset_saved_ex_oop6FpnNSafePointNode_pnENode__v_; +text: .text%__1cIGraphKitUmake_exception_state6MpnENode__pnNSafePointNode__; +text: .text%__1cJloadBNodeMideal_Opcode6kM_i_; +text: .text%__1cMLinkResolverOresolve_invoke6FrnICallInfo_nGHandle_nSconstantPoolHandle_inJBytecodesECode_pnGThread__v_; +text: .text%__1cMLinkResolverMresolve_pool6FrnLKlassHandle_rnMsymbolHandle_42nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cGOopMapHcopy_to6MpC_v_; +text: .text%__1cNstoreImmBNodeJnum_opnds6kM_I_; +text: .text%__1cVLoaderConstraintTableWfind_loader_constraint6MnMsymbolHandle_nGHandle__ppnVLoaderConstraintEntry__; +text: .text%__1cJTimeStampJupdate_to6Mx_v_; +text: .text%__1cJTimeStampGupdate6M_v_; +text: .text%__1cFframeZinterpreter_frame_set_bcx6Ml_v_; +text: .text%__1cCosFsleep6FpnGThread_xi_i_; +text: .text%__1cNgetTimeMillis6F_x_; +text: .text%__1cRaddP_rReg_immNodeLbottom_type6kM_pknEType__; +text: .text%__1cIos_sleep6Fxi_i_: os_solaris.o; +text: .text%__1cLPhaseValuesHzerocon6MnJBasicType__pnHConNode__; +text: .text%__1cMCreateExNodeKmatch_edge6kMI_I_; +text: .text%__1cTconvI2L_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cIGraphKitNuncommon_trap6MipnHciKlass_pkci_v_; +text: .text%__1cILoadNodeEmake6FpnENode_22pknHTypePtr_pknEType_nJBasicType__p0_; +text: .text%__1cIGraphKitJmake_load6MpnENode_2pknEType_nJBasicType_i_2_; +text: .text%__1cTconvI2L_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cTno_rax_rbx_RegPOperJnum_edges6kM_I_; +text: .text%__1cLPCTableNodeLbottom_type6kM_pknEType__; +text: .text%__1cLOptoRuntimeSuncommon_trap_Type6F_pknITypeFunc__; +text: .text%__1cIHaltNode2t6MpnENode_2_v_; +text: .text%__1cNSafePointNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNaddI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cGPcDesc2t6Mii_v_; +text: .text%__1cHnmethodKcopy_pc_at6MipnGPcDesc__v_; +text: .text%__1cKciTypeFlowLStateVectorJcopy_into6kMp1_v_; +text: .text%__1cXmembar_release_lockNodeMideal_Opcode6kM_i_; +text: .text%__1cOcompL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cMoutputStreamPupdate_position6MpkcL_v_; +text: .text%__1cMstringStreamFwrite6MpkcL_v_; +text: .text%__1cKciTypeFlowQadd_to_work_list6Mpn0AFBlock__v_; +text: .text%__1cKciTypeFlowKflow_block6Mpn0AFBlock_pn0ALStateVector_pn0AGJsrSet__v_; +text: .text%__1cKciTypeFlowFBlockKsuccessors6MpnQciBytecodeStream_pn0ALStateVector_pn0AGJsrSet__pnNGrowableArray4Cp1___; +text: .text%__1cKciTypeFlowOwork_list_next6M_pn0AFBlock__; +text: .text%__1cIPipelineXfunctional_unit_latency6kMIpk0_I_; +text: .text%__1cMPhaseIterGVNJtransform6MpnENode__2_; +text: .text%__1cQSystemDictionarybCfind_instance_or_array_klass6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cSCompareAndSwapNodeGis_CFG6kM_i_; +text: .text%__1cNGrowableArray4CpnOMethodLivenessKBasicBlock__Icontains6kMrk2_i_; +text: .text%__1cKciTypeFlowFBlock2t6Mp0pn0AFRange_pn0AGJsrSet__v_; +text: .text%__1cNGrowableArray4CpnKciTypeFlowJJsrRecord__2t6MpnFArena_iirk2_v_; +text: .text%__1cKstoreCNodeJnum_opnds6kM_I_; +text: .text%__1cNmodI_rRegNodeErule6kM_I_; +text: .text%__1cKInlineTreeWfind_subtree_from_root6Fp0pnIJVMState_pnIciMethod_i_1_; +text: .text%__1cNGrowableArray4CpnPciInstanceKlass__2t6MpnFArena_iirk1_v_; +text: .text%__1cKciTypeFlowFBlockScompute_exceptions6M_v_; +text: .text%__1cYciExceptionHandlerStreamFcount6M_i_; +text: .text%__1cINodeHashJhash_find6MpknENode__p1_; +text: .text%__1cFParsePdo_field_access6Mii_v_; +text: .text%__1cPThreadLocalNodeLbottom_type6kM_pknEType__; +text: .text%__1cOMethodLivenessNmake_block_at6Mipn0AKBasicBlock__2_; +text: .text%__1cKstorePNodeHtwo_adr6kM_I_; +text: .text%__1cKciTypeFlowPflow_successors6MpnNGrowableArray4Cpn0AFBlock___pn0ALStateVector__v_; +text: .text%__1cGciTypeMis_classless6kM_i_; +text: .text%__1cRsalI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cJloadFNodeErule6kM_I_; +text: .text%__1cKBranchDataNis_BranchData6M_i_; +text: .text%__1cIJumpDataLis_JumpData6M_i_; +text: .text%__1cSMemBarCPUOrderNodeGOpcode6kM_i_; +text: .text%__1cLklassVtableNput_method_at6MpnNmethodOopDesc_i_v_; +text: .text%__1cHi2sNodeMideal_Opcode6kM_i_; +text: .text%__1cKstoreCNodeMideal_Opcode6kM_i_; +text: .text%__1cRshrI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNloadConI0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNloadKlassNodeJnum_opnds6kM_I_; +text: .text%__1cHCompileKalias_type6MpnHciField__pn0AJAliasType__; +text: .text%__1cLStringTableGintern6FnGHandle_pHipnGThread__pnHoopDesc__; +text: .text%__1cLStringTableLhash_string6FpHi_i_; +text: .text%__1cMCreateExNodeGpinned6kM_i_; +text: .text%__1cFciEnvWget_klass_by_name_impl6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cNloadKlassNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRScavengeRootsTaskEname6M_pc_; +text: .text%__1cRScavengeRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cNtestP_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cKstoreINodeHtwo_adr6kM_I_; +text: .text%__1cSInterpreterRuntimeOresolve_invoke6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cPCountedLoopNodeGOpcode6kM_i_; +text: .text%__1cHTypeIntEmake6Fii_pk0_; +text: .text%__1cRcmpFastUnlockNodeHtwo_adr6kM_I_; +text: .text%__1cJloadSNodeMideal_Opcode6kM_i_; +text: .text%__1cPDictionaryEntrybAcontains_protection_domain6kMpnHoopDesc__i_; +text: .text%__1cIregFOperEtype6kM_pknEType__; +text: .text%__1cLLShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%JVM_GetCPMethodNameUTF; +text: .text%__1cMLinkResolverbNlinktime_resolve_virtual_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cNsubI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cLTypeInstPtrFxdual6kM_pknEType__; +text: .text%__1cNsubI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cIGraphKitOreplace_in_map6MpnENode_2_v_; +text: .text%__1cMPhaseChaitinLclone_projs6MpnFBlock_IpnENode_4rI_i_; +text: .text%__1cTconstantPoolOopDescbCverify_constant_pool_resolve6FnSconstantPoolHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cLcastP2LNodeJnum_opnds6kM_I_; +text: .text%__1cOMethodLivenessNwork_list_add6Mpn0AKBasicBlock__v_; +text: .text%__1cFParseFBlockNlocal_type_at6kMi_pknEType__; +text: .text%__1cWConstantPoolCacheEntryKset_method6MnJBytecodesECode_nMmethodHandle_i_v_; +text: .text%__1cHTypeIntFxdual6kM_pknEType__; +text: .text%__1cEUTF8Hstrrchr6FpWiW_1_; +text: .text%__1cQConstantIntValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_nGHandle_2ipnGThread__pnMklassOopDesc__; +text: .text%__1cNLoadKlassNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHCompileZintrinsic_insertion_index6MpnIciMethod_i_i_; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_; +text: .text%__1cJVectorSetFClear6M_v_; +text: .text%__1cMMergeMemNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKMemoryPoolHoops_do6MpnKOopClosure__v_; +text: .text%__1cICodeHeapSallocated_capacity6kM_L_; +text: .text%__1cICodeBlobRis_at_poll_return6MpC_i_; +text: .text%__1cIMachOperEtype6kM_pknEType__; +text: .text%__1cLjmpConUNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQSystemDictionarybOfind_constrained_instance_or_array_klass6FnMsymbolHandle_nGHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cNCallGenerator2t6MpnIciMethod__v_; +text: .text%__1cSHighResTimeSamplerLtake_sample6M_x_; +text: .text%__1cRis_error_reported6F_i_; +text: .text%__1cLStatSamplerLsample_data6FpnMPerfDataList__v_; +text: .text%__1cPStatSamplerTaskEtask6M_v_; +text: .text%__1cMPeriodicTaskMtime_to_wait6F_L_; +text: .text%__1cMPeriodicTaskOreal_time_tick6FL_v_; +text: .text%__1cNWatcherThreadRis_Watcher_thread6kM_i_; +text: .text%__1cLStatSamplerOcollect_sample6F_v_; +text: .text%__1cJloadBNodePoper_input_base6kM_I_; +text: .text%__1cMVM_OperationPevaluation_mode6kM_n0AEMode__; +text: .text%__1cIGraphKit2t6M_v_; +text: .text%__1cOemit_d64_reloc6FrnKCodeBuffer_lnJrelocInfoJrelocType_i_v_; +text: .text%__1cNinstanceKlassKlink_class6MpnGThread__v_; +text: .text%__1cIGraphKitNset_map_clone6MpnNSafePointNode__v_; +text: .text%__1cRRawBytecodeStream2t6MnMmethodHandle__v_; +text: .text%__1cENodeHget_ptr6kM_l_; +text: .text%__1cFStateM_sub_Op_ConP6MpknENode__v_; +text: .text%__1cJloadPNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKI2CAdapterOis_i2c_adapter6kM_i_; +text: .text%__1cOcompU_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cNinstanceKlassPinitialize_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cQciBytecodeStreamWget_field_holder_index6M_i_; +text: .text%__1cQciBytecodeStreamZget_declared_field_holder6M_pnPciInstanceKlass__; +text: .text%__1cRinterpretedVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cMorI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cIciSymbol2t6MnMsymbolHandle__v_; +text: .text%__1cFParseRensure_memory_phi6Mii_pnHPhiNode__; +text: .text%__1cNdecI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cJLoadINodeJideal_reg6kM_I_; +text: .text%__1cKRelocationWfix_relocation_at_move6Ml_v_; +text: .text%__1cPindOffset32OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cHAddNodePadd_of_identity6kMpknEType_3_3_; +text: .text%__1cMFastLockNodeGOpcode6kM_i_; +text: .text%__1cScompU_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cHCompilebAallow_range_check_smearing6kM_i_; +text: .text%__1cLBuildCutout2T6M_v_; +text: .text%__1cLBuildCutout2t6MpnIGraphKit_pnENode_ff_v_; +text: .text%__1cTconstantPoolOopDescOstring_at_impl6FnSconstantPoolHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cQjava_lang_StringQbasic_create_oop6FpnQtypeArrayOopDesc_ipnGThread__pnHoopDesc__; +text: .text%__1cICodeHeapIcapacity6kM_L_; +text: .text%__1cKMemoryPoolImax_size6kM_L_; +text: .text%__1cMCodeHeapPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cMCodeHeapPoolNused_in_bytes6M_L_; +text: .text%__1cMPhaseChaitinTsplit_Rematerialize6MpnENode_pnFBlock_IrInNGrowableArray4CI__ipIp2i_2_; +text: .text%__1cJcmpOpOperFccode6kM_i_; +text: .text%__1cKjmpDirNodeTmay_be_short_branch6kM_i_; +text: .text%__1cKjmpDirNodeOis_pc_relative6kM_i_; +text: .text%__1cPClassFileParserXverify_legal_field_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_field_signature6MnMsymbolHandle_1pnGThread__v_; +text: .text%__1cEUTF8Enext6FpkcpH_pc_; +text: .text%__1cOcompL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cbAPSEvacuateFollowersClosureHdo_void6M_v_; +text: .text%__1cFParseKdo_get_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_; +text: .text%__1cHMulNodeEhash6kM_I_; +text: .text%__1cGRFrame2t6MnFframe_pnKJavaThread_p0_v_; +text: .text%__1cTconvI2L_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cOGenerateOopMapLbb_mark_fct6Fp0ipi_v_; +text: .text%__1cScompU_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNFingerprinterJdo_object6Mii_v_; +text: .text%__1cMloadConFNodePoper_input_base6kM_I_; +text: .text%__1cOGenerateOopMapTmerge_state_vectors6MpnNCellTypeState_2_i_; +text: .text%__1cMloadConFNodeHtwo_adr6kM_I_; +text: .text%__1cICallNodeSis_CallDynamicJava6kM_pknTCallDynamicJavaNode__; +text: .text%__1cRcmpFastUnlockNodeJnum_opnds6kM_I_; +text: .text%__1cEUTF8Sconvert_to_unicode6FpkcpHi_v_; +text: .text%__1cUParallelScavengeHeapMmem_allocate6MLii_pnIHeapWord__; +text: .text%__1cNinstanceKlassbBallocate_permanent_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cKjmpConNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMethodLivenessKBasicBlockQcompute_gen_kill6MpnIciMethod__v_; +text: .text%__1cOMethodLivenessKBasicBlock2t6Mp0ii_v_; +text: .text%__1cMloadConFNodeErule6kM_I_; +text: .text%__1cLcastP2LNodeHtwo_adr6kM_I_; +text: .text%__1cIMachOperIconstant6kM_l_; +text: .text%__1cJloadSNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cPcheckCastPPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMTypeKlassPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cUEdenMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolImax_size6kM_L_; +text: .text%__1cUEdenMutableSpacePoolNused_in_bytes6M_L_; +text: .text%__1cUEdenMutableSpacePoolImax_size6kM_L_; +text: .text%__1cYSurvivorMutableSpacePoolNused_in_bytes6M_L_; +text: .text%__1cKjmpConNodeTmay_be_short_branch6kM_i_; +text: .text%__1cKjmpConNodeOis_pc_relative6kM_i_; +text: .text%__1cHConNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKCodeBufferKend_a_stub6M_v_; +text: .text%__1cTemit_java_to_interp6FrnKCodeBuffer__v_; +text: .text%__1cKCodeBufferMstart_a_stub6M_v_; +text: .text%__1cFParseUprofile_taken_branch6Mi_v_; +text: .text%__1cKciTypeFlowGJsrSetNapply_control6Mp0pnQciBytecodeStream_pn0ALStateVector__v_; +text: .text%__1cKReturnNodeGis_CFG6kM_i_; +text: .text%__1cRSignatureIteratorSskip_optional_size6M_v_; +text: .text%__1cRaddI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHTypeIntFwiden6kMpknEType__3_; +text: .text%__1cTCompareAndSwapLNodeGOpcode6kM_i_; +text: .text%__1cNmethodOopDescbHhas_unloaded_classes_in_signature6FnMmethodHandle_pnGThread__i_; +text: .text%__1cIciObjectRis_instance_klass6M_i_; +text: .text%__1cOGenerateOopMapRsigchar_to_effect6McipnNCellTypeState__2_; +text: .text%__1cOGenerateOopMapIdo_field6Miiii_v_; +text: .text%__1cPloadConUL32NodeMideal_Opcode6kM_i_; +text: .text%__1cEUTF8Ounicode_length6Fpkci_i_; +text: .text%__1cIciMethodRget_flow_analysis6M_pnKciTypeFlow__; +text: .text%__1cICodeBlobLoop_addr_at6kMi_ppnHoopDesc__; +text: .text%__1cNloadRangeNodeHtwo_adr6kM_I_; +text: .text%__1cJloadLNodeJnum_opnds6kM_I_; +text: .text%__1cSmembar_acquireNodeMideal_Opcode6kM_i_; +text: .text%__1cSObjectSynchronizerXidentity_hash_value_for6FnGHandle__l_; +text: .text%__1cHoopDescSslow_identity_hash6M_l_; +text: .text%__1cKMemBarNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOGenerateOopMapLmerge_state6Fp0ipi_v_; +text: .text%__1cIMachNodeOpipeline_class6F_pknIPipeline__; +text: .text%__1cIMachNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadCNodeErule6kM_I_; +text: .text%__1cKOSRAdapterIis_alive6kM_i_; +text: .text%__1cQjava_lang_StringMbasic_create6FpnQtypeArrayOopDesc_ipnGThread__nGHandle__; +text: .text%__1cRMachNullCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGThreadXclear_pending_exception6M_v_; +text: .text%__1cXindIndexScaleOffsetOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOcompL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cTconstantPoolOopDescbBbasic_type_for_signature_at6Mi_nJBasicType__; +text: .text%__1cETypeRget_typeflow_type6FpnGciType__pk0_; +text: .text%__1cLStringTableGintern6FpnNsymbolOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cNsymbolOopDescKas_unicode6kMri_pH_; +text: .text%__1cKciTypeFlowLStateVectorJdo_invoke6MpnQciBytecodeStream_i_v_; +text: .text%__1cKstorePNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKReturnNodeKmatch_edge6kMI_I_; +text: .text%__1cKRegionNodeJideal_reg6kM_I_; +text: .text%__1cJloadINodeHtwo_adr6kM_I_; +text: .text%__1cQmark_inner_loops6FpnIPhaseCFG_pnFBlock__v_: block.o; +text: .text%__1cIHaltNodeJideal_reg6kM_I_; +text: .text%__1cFStateM_sub_Op_Halt6MpknENode__v_; +text: .text%__1cWShouldNotReachHereNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReturnNodeGOpcode6kM_i_; +text: .text%__1cJTypeTupleKmake_range6FpnLciSignature__pk0_; +text: .text%__1cKStoreLNodeGOpcode6kM_i_; +text: .text%__1cPCountedLoopNodeOis_CountedLoop6M_p0_; +text: .text%__1cJTypeTupleLmake_domain6FpnPciInstanceKlass_pnLciSignature__pk0_; +text: .text%__1cPClassFileParserWparse_field_attributes6MnSconstantPoolHandle_iHpHpi2pnPtypeArrayHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_field_modifiers6MiipnGThread__v_; +text: .text%__1cMindirectOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMindirectOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMLinkResolverUresolve_virtual_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_virtual_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cFKlassXcan_be_statically_bound6FpnNmethodOopDesc__i_; +text: .text%__1cLProfileDataOtranslate_from6Mp0_v_; +text: .text%__1cKstorePNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKstoreINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSPSKeepAliveClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cNloadConI0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvI2L_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMWarmCallInfoKalways_hot6F_p0_; +text: .text%__1cMWarmCallInfoGis_hot6kM_i_; +text: .text%__1cNprefetchwNodeMideal_Opcode6kM_i_; +text: .text%__1cIAddINodeJideal_reg6kM_I_; +text: .text%__1cNCatchProjNode2t6MpnENode_Ii_v_; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__; +text: .text%__1cLBoxLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmulL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cKciTypeFlowIcan_trap6MrnQciBytecodeStream__i_; +text: .text%__1cQVMOperationQdDueueLqueue_empty6Mi_i_; +text: .text%__1cIProjNodeDcmp6kMrknENode__I_; +text: .text%__1cSComputeAdapterInfoGdo_int6M_v_; +text: .text%__1cNCatchProjNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%JVM_GetCPFieldSignatureUTF; +text: .text%__1cIGraphKitTtoo_many_recompiles6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cHCompileFstart6kM_pnJStartNode__; +text: .text%__1cNmulL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cLPCTableNodeEhash6kM_I_; +text: .text%__1cIGraphKitZadd_exception_states_from6MpnIJVMState__v_; +text: .text%__1cJStartNodeOis_block_start6kM_i_; +text: .text%__1cQComputeCallStackHdo_void6M_v_; +text: .text%__1cNaddI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitOtoo_many_traps6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cNciCallProfileRapply_prof_factor6Mf_v_; +text: .text%__1cIciMethodTcall_profile_at_bci6Mi_nNciCallProfile__; +text: .text%__1cHCompileOcall_generator6MpnIciMethod_ipnIJVMState_if_pnNCallGenerator__; +text: .text%__1cHCompileOfind_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cMindIndexOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMindIndexOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%jni_GetPrimitiveArrayCritical: jni.o; +text: .text%jni_ReleasePrimitiveArrayCritical: jni.o; +text: .text%__1cSInterpreterRuntimeDldc6FpnKJavaThread_i_v_; +text: .text%__1cPClassFileParserbIparse_constant_pool_fieldref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNdecI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cFKlassOis_subclass_of6kMpnMklassOopDesc__i_; +text: .text%__1cNGrowableArray4Cl_2t6Mii_v_; +text: .text%__1cNinstanceKlassSlookup_osr_nmethod6kMpnNmethodOopDesc_i_pnHnmethod__; +text: .text%__1cIAddINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cLPCTableNodeKis_PCTable6kM_pk0_; +text: .text%__1cLPCTableNodeHsize_of6kM_I_; +text: .text%__1cNincI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKJNIHandlesKmake_local6FpnGThread_pnHoopDesc__pnI_jobject__; +text: .text%__1cQciBytecodeStreamXget_method_holder_index6M_i_; +text: .text%__1cMorI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cLimmUL32OperJconstantL6kM_x_; +text: .text%__1cIGraphKitWround_double_arguments6MpnIciMethod__v_; +text: .text%__1cFParseHdo_call6M_v_; +text: .text%__1cIGraphKitTround_double_result6MpnIciMethod__v_; +text: .text%__1cFParseZcan_not_compile_call_site6MpnIciMethod_pnPciInstanceKlass__i_; +text: .text%__1cQciBytecodeStreambAget_declared_method_holder6M_pnHciKlass__; +text: .text%__1cFParseMprofile_call6MpnENode__v_; +text: .text%__1cScompP_mem_rRegNodePoper_input_base6kM_I_; +text: .text%__1cICodeHeapLheader_size6F_L_; +text: .text%__1cJloadBNodeJnum_opnds6kM_I_; +text: .text%__1cENodeLbottom_type6kM_pknEType__; +text: .text%__1cXindIndexScaleOffsetOperNconstant_disp6kM_i_; +text: .text%__1cSindIndexOffsetOperJnum_edges6kM_I_; +text: .text%__1cFKlassMnext_sibling6kM_p0_; +text: .text%__1cOGenerateOopMapNrestore_state6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapQnext_bb_start_pc6MpnKBasicBlock__i_; +text: .text%__1cOGenerateOopMapJinterp_bb6MpnKBasicBlock__v_; +text: .text%__1cKciTypeFlowLStateVectorStype_meet_internal6FpnGciType_3p0_3_; +text: .text%__1cRcmpFastUnlockNodeErule6kM_I_; +text: .text%__1cIBoolNodeHsize_of6kM_I_; +text: .text%__1cLLShiftINodeLbottom_type6kM_pknEType__; +text: .text%jni_DeleteLocalRef: jni.o; +text: .text%__1cJloadSNodePoper_input_base6kM_I_; +text: .text%__1cPno_rax_RegPOperJnum_edges6kM_I_; +text: .text%__1cOcompI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%jni_SetIntField: jni.o; +text: .text%__1cMURShiftLNodeLbottom_type6kM_pknEType__; +text: .text%__1cMMutableSpaceFclear6M_v_; +text: .text%__1cNtestI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQjava_lang_StringbBcreate_tenured_from_unicode6FpHipnGThread__nGHandle__; +text: .text%__1cKoopFactoryXnew_permanent_charArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cLStringTableJbasic_add6MinGHandle_pHiIpnGThread__pnHoopDesc__; +text: .text%__1cNprefetchwNodePoper_input_base6kM_I_; +text: .text%__1cTCreateExceptionNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cHMulNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cVExceptionHandlerTableJadd_entry6MnRHandlerTableEntry__v_; +text: .text%__1cPsalI_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cRaddP_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMLinkResolverbFlinktime_resolve_special_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverUresolve_special_call6FrnICallInfo_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_special_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_4ipnGThread__v_; +text: .text%__1cQComputeCallStackJdo_object6Mii_v_; +text: .text%__1cIGraphKitNcast_not_null6MpnENode__2_; +text: .text%__1cTconvL2I_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cKPSYoungGenNused_in_bytes6kM_L_; +text: .text%__1cOCompilerOracleOshould_exclude6FnMmethodHandle__i_; +text: .text%__1cOGenerateOopMapHset_var6MinNCellTypeState__v_; +text: .text%__1cPcheckCastPPNodeJnum_opnds6kM_I_; +text: .text%__1cLLShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cGIfNodeHsize_of6kM_I_; +text: .text%__1cPciInstanceKlassFsuper6M_p0_; +text: .text%__1cOcompL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cLLShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cScompI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMMergeMemNodeJideal_reg6kM_I_; +text: .text%__1cNandL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cIciMethodWwas_executed_more_than6Mi_i_; +text: .text%__1cSReferenceProcessorbAprocess_discovered_reflist6MppnHoopDesc_pnPReferencePolicy_i_v_; +text: .text%__1cSReferenceProcessorOprocess_phase36MppnHoopDesc_ipnRBoolObjectClosure_pnKOopClosure_pnLVoidClosure__v_; +text: .text%__1cSReferenceProcessorOprocess_phase26MppnHoopDesc_pnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSReferenceProcessorbAenqueue_discovered_reflist6MpnHoopDesc_p2_v_; +text: .text%__1cTleaPIdxScaleOffNodeMideal_Opcode6kM_i_; +text: .text%__1cTleaPIdxScaleOffNodePoper_input_base6kM_I_; +text: .text%__1cFLabelJadd_patch6Mi_v_; +text: .text%__1cKMemBarNodeEhash6kM_I_; +text: .text%__1cOcompP_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNloadConL0NodePoper_input_base6kM_I_; +text: .text%__1cNsubI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJMarkSweepSMarkAndPushClosureLdo_nmethods6kM_ki_; +text: .text%__1cIXorINodeGOpcode6kM_i_; +text: .text%__1cTStackWalkCompPolicyRcompilation_level6MnMmethodHandle_i_i_; +text: .text%__1cMoutputStreamDput6Mc_v_; +text: .text%__1cPindOffset32OperNbase_position6kM_i_; +text: .text%__1cPindOffset32OperNconstant_disp6kM_i_; +text: .text%__1cOcompU_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeHtwo_adr6kM_I_; +text: .text%__1cPBytecode_invokeFindex6kM_i_; +text: .text%__1cFframeNis_glue_frame6kM_i_; +text: .text%__1cLRShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cZPhaseConservativeCoalesceKupdate_ifg6MIIpnIIndexSet_2_v_; +text: .text%__1cZPhaseConservativeCoalesceMunion_helper6MpnENode_2II222pnFBlock_I_v_; +text: .text%__1cIIndexSetEswap6Mp0_v_; +text: .text%__1cOPhaseIdealLoopIsink_use6MpnENode_2_v_; +text: .text%__1cRshrL_rReg_immNodeErule6kM_I_; +text: .text%__1cKInlineTreeMok_to_inline6MpnIciMethod_pnIJVMState_rnNciCallProfile_pnMWarmCallInfo__8_; +text: .text%__1cTpass_initial_checks6FpnIciMethod_i1_i_; +text: .text%__1cKInlineTreeMshouldInline6kMpnIciMethod_irnNciCallProfile_pnMWarmCallInfo__pkc_; +text: .text%__1cOCompilerOracleNshould_inline6FnMmethodHandle__i_; +text: .text%__1cKInlineTreeNtry_to_inline6MpnIciMethod_irnNciCallProfile_pnMWarmCallInfo__pkc_; +text: .text%__1cIciMethodbAinterpreter_throwout_count6kM_i_; +text: .text%__1cIciMethodNshould_inline6M_i_; +text: .text%__1cIciMethodOshould_exclude6M_i_; +text: .text%__1cScompU_rReg_immNodeErule6kM_I_; +text: .text%__1cKjmpDirNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKjmpDirNodeJis_Branch6kM_I_; +text: .text%__1cKjmpDirNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMindirectOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cILoadNodeDcmp6kMrknENode__I_; +text: .text%__1cLTypeInstPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cIGraphKitHjava_bc6kM_nJBytecodesECode__; +text: .text%__1cFLabelSpatch_instructions6MpnRAbstractAssembler__v_; +text: .text%__1cRAbstractAssemblerHbind_to6MrnFLabel_i_v_; +text: .text%__1cRAbstractAssemblerEbind6MrnFLabel__v_; +text: .text%__1cNloadConL0NodeErule6kM_I_; +text: .text%__1cJStoreNodeSIdeal_masked_input6MpnIPhaseGVN_I_pnENode__; +text: .text%__1cIGraphKitNbuiltin_throw6MnODeoptimizationLDeoptReason_pnENode__v_; +text: .text%__1cbBopt_virtual_call_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cJrRegIOperFclone6kM_pnIMachOper__; +text: .text%__1cMindIndexOperFscale6kM_i_; +text: .text%__1cScompP_mem_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cFKlassPoop_is_instance6kM_i_; +text: .text%__1cHciFieldPinitialize_from6MpnPfieldDescriptor__v_; +text: .text%__1cRandI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cMMachProjNodeHsize_of6kM_I_; +text: .text%__1cJStoreNodeZIdeal_sign_extended_input6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cScompP_mem_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cTconvF2D_reg_memNodeErule6kM_I_; +text: .text%__1cPindOffset32OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFStateM_sub_Op_CmpP6MpknENode__v_; +text: .text%__1cPciInstanceKlassUget_canonical_holder6Mi_p0_; +text: .text%__1cMloadConLNodeMideal_Opcode6kM_i_; +text: .text%__1cSCompiledStaticCallJfind_stub6M_pC_; +text: .text%__1cIciSymbolEmake6Fpkc_p0_; +text: .text%__1cIciSymbolJmake_impl6Fpkc_p0_; +text: .text%__1cScompU_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cKimmL32OperIconstant6kM_l_; +text: .text%__1cHi2sNodePoper_input_base6kM_I_; +text: .text%__1cKimmL32OperJnum_edges6kM_I_; +text: .text%__1cMMonitorValue2t6MpnKScopeValue_nILocation__v_; +text: .text%__1cMMonitorValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cLBoxLockNodeKstack_slot6FpnENode__i_; +text: .text%__1cLBoxLockNodeKis_BoxLock6kM_pk0_; +text: .text%__1cIBoolNodeDcmp6kMrknENode__I_; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_; +text: .text%__1cKDataLayoutPneeds_array_len6FC_i_; +text: .text%__1cKDataLayoutKinitialize6MCHi_v_; +text: .text%__1cJloadLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframebHnext_monitor_in_interpreter_frame6kMpnPBasicObjectLock__2_; +text: .text%__1cLRuntimeStubHoops_do6MpnKOopClosure__v_; +text: .text%__1cFParseRbranch_prediction6Mrf_f_; +text: .text%__1cPshrI_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cHciKlassNis_subtype_of6Mp0_i_; +text: .text%__1cOcompL_rRegNodeErule6kM_I_; +text: .text%__1cNGrowableArray4Cpv_Praw_at_put_grow6Mirk03_v_; +text: .text%__1cNGrowableArray4Cl_Praw_at_put_grow6Mirkl2_v_; +text: .text%__1cISubINodeLbottom_type6kM_pknEType__; +text: .text%__1cIGraphKitZset_all_rewritable_memory6MpnENode__v_; +text: .text%__1cIGraphKitTset_all_memory_call6MpnENode__v_; +text: .text%__1cMtlsLoadPNodeLbottom_type6kM_pknEType__; +text: .text%__1cJAssemblerEmovq6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cRsalI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cNloadRangeNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_; +text: .text%__1cGRFrameMset_distance6Mi_v_; +text: .text%__1cICodeBlobOis_c2i_adapter6kM_i_; +text: .text%__1cFframeTis_first_java_frame6kM_i_; +text: .text%__1cFframeLreal_sender6kMpnLRegisterMap__0_; +text: .text%__1cGRFrameGcaller6M_p0_; +text: .text%__1cTStackWalkCompPolicyIsenderOf6MpnGRFrame_pnNGrowableArray4C2___2_; +text: .text%__1cGRFrameKnew_RFrame6FnFframe_pnKJavaThread_p0_4_; +text: .text%__1cKstoreLNodePoper_input_base6kM_I_; +text: .text%__1cTconstantPoolOopDescMklass_at_put6MipnMklassOopDesc__v_; +text: .text%__1cNFingerprinterGdo_int6M_v_; +text: .text%__1cNSafepointBlobbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cRaddI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cRshrL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHPhiNodeEmake6FpnENode_2_p0_; +text: .text%__1cScompI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__; +text: .text%__1cWstatic_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cLRethrowNodeGis_CFG6kM_i_; +text: .text%__1cIciObjectFklass6M_pnHciKlass__; +text: .text%__1cNloadConP0NodeMideal_Opcode6kM_i_; +text: .text%__1cOPhaseIdealLoopOsplit_thru_phi6MpnENode_2i_2_; +text: .text%__1cNGCTaskManagerRset_resource_flag6MIi_v_; +text: .text%__1cRshrI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cUmembar_cpu_orderNodeMideal_Opcode6kM_i_; +text: .text%__1cLklassVtableIindex_of6kMpnNmethodOopDesc_i_i_; +text: .text%__1cKEntryPointFentry6kMnITosState__pC_; +text: .text%__1cJloadCNodeMideal_Opcode6kM_i_; +text: .text%__1cKJavaThreadJframes_do6MpFpnFframe_pknLRegisterMap__v_v_; +text: .text%__1cNMemoryManagerHoops_do6MpnKOopClosure__v_; +text: .text%__1cRInvocationCounterJset_carry6M_v_; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cFStateM_sub_Op_RegL6MpknENode__v_; +text: .text%__1cNdecI_rRegNodeErule6kM_I_; +text: .text%__1cKjmpConNodeJis_Branch6kM_I_; +text: .text%__1cKjmpConNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmpFastLockNodeMideal_Opcode6kM_i_; +text: .text%__1cKjmpConNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cJttyLockerbCbreak_tty_lock_for_safepoint6Fl_v_; +text: .text%__1cNCallGeneratorJis_inline6kM_i_; +text: .text%__1cUSafepointSynchronizeFblock6FpnKJavaThread__v_; +text: .text%__1cCosRcurrent_thread_id6F_l_; +text: .text%__1cKciTypeFlowLStateVectorMdo_getstatic6MpnQciBytecodeStream__v_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_; +text: .text%__1cLLShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKstoreLNodeMideal_Opcode6kM_i_; +text: .text%__1cNloadKlassNodeHtwo_adr6kM_I_; +text: .text%__1cFParseYprofile_not_taken_branch6M_v_; +text: .text%__1cHPhiNodeMslice_memory6kMpknHTypePtr__p0_; +text: .text%__1cOcompL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cLRuntimeStubMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cLSymbolTableFprobe6Fpkci_pnNsymbolOopDesc__; +text: .text%__1cOcompL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cPcmpFastLockNodePoper_input_base6kM_I_; +text: .text%__1cNCallGeneratorKis_virtual6kM_i_; +text: .text%__1cKInlineTreePshouldNotInline6kMpnIciMethod_pnMWarmCallInfo__pkc_; +text: .text%__1cLcastP2LNodeErule6kM_I_; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cNPhaseRegAllocKoffset2reg6kMi_i_; +text: .text%__1cQjmpCon_shortNodeMideal_Opcode6kM_i_; +text: .text%__1cQjmpCon_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_pnGRFrame__v_; +text: .text%__1cTconvI2L_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cENodeRlatency_from_uses6kMrnLBlock_Array_rnNGrowableArray4CI___i_; +text: .text%__1cNGrowableArray4CI_Praw_at_put_grow6MirkI2_v_; +text: .text%__1cFParseFdo_if6MpnENode_2nIBoolTestEmask_2_v_; +text: .text%__1cXmembar_acquire_lockNodeMideal_Opcode6kM_i_; +text: .text%__1cKklassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cXindIndexScaleOffsetOperOindex_position6kM_i_; +text: .text%__1cXindIndexScaleOffsetOperNbase_position6kM_i_; +text: .text%__1cPsalI_rReg_1NodePoper_input_base6kM_I_; +text: .text%__1cIGraphKitJpush_node6MnJBasicType_pnENode__v_; +text: .text%__1cISubINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSconstMethodOopDescbEchecked_exceptions_length_addr6kM_pH_; +text: .text%__1cPThreadLocalNodeGOpcode6kM_i_; +text: .text%__1cRsubI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cJloadCNodePoper_input_base6kM_I_; +text: .text%__1cSThreadLocalStoragePget_thread_slow6F_pnGThread__; +text: .text%__1cCosXthread_local_storage_at6Fi_pv_; +text: .text%__1cLRuntimeStubYcaller_must_gc_arguments6kMpnKJavaThread__i_; +text: .text%__1cIAddPNodeJideal_reg6kM_I_; +text: .text%__1cTleaPIdxScaleOffNodeJnum_opnds6kM_I_; +text: .text%__1cRaddI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cFParseNthrow_to_exit6MpnNSafePointNode__v_; +text: .text%__1cQCallLeafNoFPNodeGOpcode6kM_i_; +text: .text%__1cKTypeRawPtrHget_con6kM_l_; +text: .text%__1cOClearArrayNodeGOpcode6kM_i_; +text: .text%__1cOoop_RelocationHoops_do6MpFppnHoopDesc__v_v_; +text: .text%__1cIciMethodbHhas_unloaded_classes_in_signature6M_i_; +text: .text%__1cScompP_mem_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cFStateM_sub_Op_CmpI6MpknENode__v_; +text: .text%__1cNincI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cJimmP0OperEtype6kM_pknEType__; +text: .text%__1cNloadConP0NodeLbottom_type6kM_pknEType__; +text: .text%__1cPloadConUL32NodeLbottom_type6kM_pknEType__; +text: .text%__1cNloadConI0NodeHsize_of6kM_I_; +text: .text%__1cRaddI_rReg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cPshrI_rReg_1NodePoper_input_base6kM_I_; +text: .text%JVM_handle_solaris_signal; +text: .text%signalHandler; +text: .text%__1cQJNI_FastGetFieldQfind_slowcase_pc6FpC_1_; +text: .text%__1cMLinkResolverbElinktime_resolve_static_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cRresolve_and_patch6FppnHoopDesc__v_; +text: .text%__1cFStateN_sub_Op_LoadP6MpknENode__v_; +text: .text%__1cISubINodeDsub6kMpknEType_3_3_; +text: .text%__1cPClassFileParserbDverify_legal_method_signature6MnMsymbolHandle_1pnGThread__i_; +text: .text%__1cMLinkResolverVresolve_invokevirtual6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cRInterpretedRFrameOis_interpreted6kM_i_; +text: .text%__1cGRFrameLis_compiled6kM_i_; +text: .text%__1cUPSGenerationCountersKupdate_all6M_v_; +text: .text%__1cTStackWalkCompPolicyMshouldInline6FnMmethodHandle_fi_pkc_; +text: .text%__1cFArenaRdestruct_contents6M_v_; +text: .text%__1cIGraphKitPstore_to_memory6MpnENode_22nJBasicType_i_2_; +text: .text%__1cJStoreNodeEmake6FpnENode_22pknHTypePtr_2nJBasicType__p0_; +text: .text%__1cXPhaseAggressiveCoalesceYinsert_copy_with_overlap6MpnFBlock_pnENode_II_v_; +text: .text%__1cULinearLeastSquareFitGupdate6Mdd_v_; +text: .text%__1cKciTypeFlowGJsrSetSis_compatible_with6Mp1_i_; +text: .text%__1cENodeIadd_prec6Mp0_v_; +text: .text%__1cKOSRAdapterOis_osr_adapter6kM_i_; +text: .text%__1cIMulINodeGOpcode6kM_i_; +text: .text%__1cLciSignature2t6MpnHciKlass_pnIciSymbol__v_; +text: .text%__1cNGrowableArray4CpnGciType__2t6MpnFArena_iirk1_v_; +text: .text%__1cKTypeAryPtrFempty6kM_i_; +text: .text%__1cHTypeAryFempty6kM_i_; +text: .text%__1cJloadCNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cIciMethod2t6MnMmethodHandle__v_; +text: .text%__1cRandI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cJAssemblerDjcc6Mn0AJCondition_rnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cCosRelapsed_frequency6F_x_; +text: .text%__1cNinstanceKlassVis_same_class_package6FpnHoopDesc_pnNsymbolOopDesc_24_i_; +text: .text%__1cMelapsedTimerHseconds6kM_d_; +text: .text%__1cOJNIHandleBlockOallocate_block6FpnGThread__p0_; +text: .text%__1cJAssemblerDnop6M_v_; +text: .text%__1cDCHANprocess_class6FnLKlassHandle_pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___nMsymbolHandle_6_v_; +text: .text%__1cEUTF8Fequal6FpWi1i_i_; +text: .text%__1cKstoreLNodeJnum_opnds6kM_I_; +text: .text%__1cIjniIdMapHoops_do6MpnKOopClosure__v_; +text: .text%__1cMjniIdMapBaseHoops_do6MpnKOopClosure__v_; +text: .text%__1cJArrayDataKcell_count6M_i_; +text: .text%__1cGBitMapIset_from6M0_v_; +text: .text%__1cPBytecode_invokeJsignature6kM_pnNsymbolOopDesc__; +text: .text%__1cKType_ArrayEgrow6MI_v_; +text: .text%JVM_Write; +text: .text%__1cDhpiFwrite6FipkvI_L_; +text: .text%__1cMStartC2INodeGOpcode6kM_i_; +text: .text%__1cSindIndexOffsetOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFframebGinterpreter_callee_receiver_addr6MnMsymbolHandle__ppnHoopDesc__; +text: .text%__1cIAddLNodeLbottom_type6kM_pknEType__; +text: .text%__1cKMemBarNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cIParmNodeJideal_reg6kM_I_; +text: .text%__1cVAdaptivePaddedAverageGsample6Mf_v_; +text: .text%__1cMtlsLoadPNodeMideal_Opcode6kM_i_; +text: .text%__1cRshrI_rReg_immNodeErule6kM_I_; +text: .text%__1cJcmpOpOperGnegate6M_v_; +text: .text%__1cHTypeAryEmake6FpknEType_pknHTypeInt__pk0_; +text: .text%__1cFParseRoptimize_inlining6MpnIciMethod_ipnPciInstanceKlass_24irnKInlineTreeLInlineStyle_r2_v_; +text: .text%__1cQimprove_receiver6FpnPciInstanceKlass_pknLTypeInstPtr_ri_1_; +text: .text%__1cPcmpFastLockNodeHtwo_adr6kM_I_; +text: .text%__1cMLinkResolverTresolve_static_call6FrnICallInfo_rnLKlassHandle_nMsymbolHandle_53iipnGThread__v_; +text: .text%__1cPCheckCastPPNodeJideal_reg6kM_I_; +text: .text%__1cKJavaThreadUin_stack_yellow_zone6MpC_i_; +text: .text%__1cFParseSmerge_memory_edges6MpnMMergeMemNode_ii_v_; +text: .text%__1cJAssemblerEmovq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cUSafepointSynchronizebDhandle_polling_page_exception6FpnKJavaThread__v_; +text: .text%__1cUThreadSafepointStatebDhandle_polling_page_exception6M_v_; +text: .text%__1cLjmpConUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNandL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cKcmpOpUOperFccode6kM_i_; +text: .text%__1cLjmpConUNodeTmay_be_short_branch6kM_i_; +text: .text%__1cLjmpConUNodeOis_pc_relative6kM_i_; +text: .text%__1cIPipelinePoperand_latency6kMIpk0_I_; +text: .text%__1cWCallLeafNoFPDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cQPlaceholderTableKfind_entry6MiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cJTimeStampSticks_since_update6kM_x_; +text: .text%__1cURethrowExceptionNodeMideal_Opcode6kM_i_; +text: .text%__1cJloadPNodeFreloc6kM_i_; +text: .text%__1cTno_rax_rbx_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNprefetchwNodeJnum_opnds6kM_I_; +text: .text%__1cKjmpConNodeGnegate6M_v_; +text: .text%__1cMindirectOperFscale6kM_i_; +text: .text%__1cQSystemDictionaryVadd_loader_constraint6FnMsymbolHandle_nGHandle_2pnGThread__v_; +text: .text%__1cVLoaderConstraintTableJadd_entry6MnMsymbolHandle_pnMklassOopDesc_nGHandle_34pnGThread__i_; +text: .text%__1cRsubI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cTOopMapForCacheEntryZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cQComputeCallStackGdo_int6M_v_; +text: .text%__1cNloadRangeNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNtestP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadRangeNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPCheckCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFParseMvisit_blocks6M_v_; +text: .text%__1cQjmpDir_shortNodeMideal_Opcode6kM_i_; +text: .text%__1cQjmpDir_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKciTypeFlowLStateVectorLdo_getfield6MpnQciBytecodeStream__v_; +text: .text%__1cNaddI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cScompP_mem_rRegNodeErule6kM_I_; +text: .text%__1cPSignatureStreamRas_symbol_or_null6M_pnNsymbolOopDesc__; +text: .text%__1cNSafePointNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadSNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKMemBarNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOjmpLoopEndNodeMideal_Opcode6kM_i_; +text: .text%__1cFBlockTimplicit_null_check6MrnLBlock_Array_rnNGrowableArray4CI__pnENode_6_v_; +text: .text%__1cKCastPPNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cOGenerateOopMapKcopy_state6MpnNCellTypeState_2_v_; +text: .text%__1cGThreadSis_Compiler_thread6kM_i_; +text: .text%__1cRCardTableModRefBSPdirty_MemRegion6MnJMemRegion__v_; +text: .text%__1cJloadBNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cQVMOperationQdDueueSqueue_remove_front6Mi_pnMVM_Operation__; +text: .text%__1cOcompI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVCompressedWriteStreamEgrow6M_v_; +text: .text%__1cNCompileBrokerYcheck_compilation_result6FnMmethodHandle_iippnHnmethod__i_; +text: .text%__1cKReflectionVis_same_class_package6FpnMklassOopDesc_2_i_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnMklassOopDesc__i_; +text: .text%JVM_RawMonitorEnter; +text: .text%__1cFMutexMjvm_raw_lock6M_v_; +text: .text%JVM_RawMonitorExit; +text: .text%__1cFMutexOjvm_raw_unlock6M_v_; +text: .text%__1cHUNICODELutf8_length6FpHi_i_; +text: .text%__1cRaddP_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cIciMethodLis_accessor6kM_i_; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__; +text: .text%__1cLBoxLockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSmembar_releaseNodeMideal_Opcode6kM_i_; +text: .text%__1cQciBytecodeStreamSget_constant_index6kM_i_; +text: .text%__1cOGenerateOopMapOset_bbmark_bit6Mi_v_; +text: .text%__1cFParseOreturn_current6MpnENode__v_; +text: .text%__1cLConvI2LNodeJideal_reg6kM_I_; +text: .text%__1cJStartNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cMorI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cMloadConPNodeFreloc6kM_i_; +text: .text%__1cGThreadMis_VM_thread6kM_i_; +text: .text%__1cSPSPromotionManagerFreset6M_v_; +text: .text%__1cNPrefetchQdDueueFclear6M_v_; +text: .text%__1cSPSPromotionManagerKflush_labs6M_v_; +text: .text%__1cFciEnvVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cOJNIHandleBlockNrelease_block6Fp0pnGThread__v_; +text: .text%__1cNincI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cTJvmtiEventCollectorYunset_jvmti_thread_state6M_v_; +text: .text%__1cLRShiftINodeLbottom_type6kM_pknEType__; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2t6M_v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2T6M_v_; +text: .text%__1cJTypeTupleFxdual6kM_pknEType__; +text: .text%__1cOcompP_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cHi2sNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_2_v_; +text: .text%__1cLcastP2LNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRciVirtualCallDataOtranslate_from6MpnLProfileData__v_; +text: .text%__1cXinitialize_static_field6FpnPfieldDescriptor_pnGThread__v_: classFileParser.o; +text: .text%__1cKstoreCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNCompileBrokerXcompilation_is_in_queue6FnMmethodHandle_i_i_; +text: .text%__1cRsubI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cETypeCeq6kMpk0_i_; +text: .text%__1cHMatcherPstack_alignment6F_I_; +text: .text%__1cNloadKlassNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadSNodeJnum_opnds6kM_I_; +text: .text%__1cJMultiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRshrL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cTconvI2L_reg_memNodeErule6kM_I_; +text: .text%__1cMURShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRcmpFastUnlockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSInterpreterRuntimebAfrequency_counter_overflow6FpnKJavaThread_pC_n0AJIcoResult__; +text: .text%__1cHSubNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQjava_lang_StringGlength6FpnHoopDesc__i_; +text: .text%__1cLRethrowNodeGOpcode6kM_i_; +text: .text%__1cPcmpFastLockNodeJnum_opnds6kM_I_; +text: .text%__1cIciMethodPcan_be_compiled6M_i_; +text: .text%__1cFParseQcreate_entry_map6M_pnNSafePointNode__; +text: .text%__1cFParseIdo_exits6M_v_; +text: .text%__1cFParseLbuild_exits6M_v_; +text: .text%__1cFParseLinit_blocks6M_v_; +text: .text%__1cFParse2t6MpnIJVMState_pnIciMethod_f_v_; +text: .text%__1cIciMethodVhas_balanced_monitors6M_i_; +text: .text%__1cFParseNdo_all_blocks6M_v_; +text: .text%__1cOParseGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cOParseGeneratorJcan_parse6FpnIciMethod_i_i_; +text: .text%__1cFArenaEused6kM_L_; +text: .text%__1cRandI_rReg_immNodeErule6kM_I_; +text: .text%jni_GetSuperclass: jni.o; +text: .text%__1cPno_rax_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFStateM_sub_Op_AddI6MpknENode__v_; +text: .text%__1cPClassFileParserUskip_over_field_name6MpciI_1_; +text: .text%__1cFParsePdo_method_entry6M_v_; +text: .text%__1cNCallGeneratorKfor_inline6FpnIciMethod_f_p0_; +text: .text%__1cKciTypeFlowFBlockPclone_loop_head6Mp0ip1pn0AGJsrSet__3_; +text: .text%__1cLOpaque1NodeGOpcode6kM_i_; +text: .text%__1cHciKlassOis_subclass_of6Mp0_i_; +text: .text%__1cbBjava_lang_ref_SoftReferenceFclock6F_x_; +text: .text%__1cTAbstractInterpreterbFsize_top_interpreter_activation6FpnNmethodOopDesc__i_; +text: .text%__1cRruntime_type_from6FpnJJavaValue__nJBasicType__: javaCalls.o; +text: .text%__1cCosbCstack_shadow_pages_available6FpnGThread_nMmethodHandle__i_; +text: .text%__1cPJavaCallWrapper2t6MnMmethodHandle_nGHandle_pnJJavaValue_pnGThread__v_; +text: .text%__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_; +text: .text%__1cRJavaCallArgumentsKparameters6M_pl_; +text: .text%__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cLCastP2LNodeLbottom_type6kM_pknEType__; +text: .text%__1cPJavaCallWrapper2T6M_v_; +text: .text%__1cVPreserveExceptionMark2T6M_v_; +text: .text%__1cVPreserveExceptionMark2t6MrpnGThread__v_; +text: .text%__1cMrax_RegPOperJnum_edges6kM_I_; +text: .text%__1cMrax_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cITypeFuncFxdual6kM_pknEType__; +text: .text%__1cIimmLOperJconstantL6kM_x_; +text: .text%__1cIMulLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLRShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNCompileBrokerTcompile_method_base6FnMmethodHandle_ii1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNCompileBrokerOcompile_method6FnMmethodHandle_i1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNmethodOopDescWload_signature_classes6FnMmethodHandle_pnGThread__i_; +text: .text%__1cTconstantPoolOopDescbDresolve_string_constants_impl6FnSconstantPoolHandle_pnGThread__v_; +text: .text%__1cYDebugInformationRecorderNadd_dependent6MpnPciInstanceKlass_pnIciMethod__v_; +text: .text%__1cHciField2t6MpnPciInstanceKlass_i_v_; +text: .text%__1cTconstantPoolOopDescbCklass_ref_at_if_loaded_check6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cMLinkResolverXresolve_klass_no_update6FrnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNaddL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cISubINodeGadd_id6kM_pknEType__; +text: .text%__1cNsubI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cGciType2t6MnLKlassHandle__v_; +text: .text%__1cMMutableSpaceKinitialize6MnJMemRegion_i_v_; +text: .text%__1cHciKlass2t6MnLKlassHandle__v_; +text: .text%__1cKInlineTree2t6MpnHCompile_pk0pnIciMethod_pnIJVMState_if_v_; +text: .text%__1cJEventMark2t6MpkcE_v_; +text: .text%__1cJloadCNodeJnum_opnds6kM_I_; +text: .text%__1cNaddI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cQComputeCallStackHdo_long6M_v_; +text: .text%__1cMindirectOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cRaddI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRLowMemoryDetectorWdetect_after_gc_memory6FpnKMemoryPool__v_; +text: .text%__1cQVMOperationQdDueueNqueue_oops_do6MipnKOopClosure__v_; +text: .text%__1cMCreateExNodeJideal_reg6kM_I_; +text: .text%__1cMorI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMorI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cRmethodDataOopDescLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cNSCMemProjNodeGOpcode6kM_i_; +text: .text%__1cNSignatureInfoHdo_long6M_v_; +text: .text%__1cLPCTableNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMCreateExNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRinterpretedVFrameDbci6kM_i_; +text: .text%__1cKInlineTreeYcompute_callee_frequency6kMi_f_; +text: .text%__1cKInlineTreebCbuild_inline_tree_for_callee6MpnIciMethod_pnIJVMState_i_p0_; +text: .text%__1cRinterpretedVFrameDbcp6kM_pC_; +text: .text%__1cRInterpretedRFrameKtop_vframe6kM_pnKjavaVFrame__; +text: .text%__1cIciMethodbBinterpreter_call_site_count6Mi_i_; +text: .text%__1cLRShiftLNodeGOpcode6kM_i_; +text: .text%__1cPsarI_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cNsubI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cOMethodLivenessKBasicBlockIload_two6Mi_v_; +text: .text%__1cFKlassTarray_klass_or_null6Mi_pnMklassOopDesc__; +text: .text%__1cNmulL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cNobjArrayKlassPoop_is_objArray6kM_i_; +text: .text%__1cLklassVtableXvtable_accessibility_at6Mi_n0AKAccessType__; +text: .text%__1cNrFlagsRegOperFclone6kM_pnIMachOper__; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_; +text: .text%__1cNCatchProjNodeDcmp6kMrknENode__I_; +text: .text%__1cIGraphKitRmake_slow_call_ex6MpnENode_pnPciInstanceKlass__v_; +text: .text%__1cTcompareAndSwapLNodePoper_input_base6kM_I_; +text: .text%__1cMloadConINodeHsize_of6kM_I_; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_i_v_; +text: .text%__1cRMachSafePointNodeLis_MachCall6M_pnMMachCallNode__; +text: .text%__1cNstoreImmINodeMideal_Opcode6kM_i_; +text: .text%__1cJScopeDescGis_top6kM_i_; +text: .text%__1cHOrINodeLbottom_type6kM_pknEType__; +text: .text%__1cPstoreImmI16NodeMideal_Opcode6kM_i_; +text: .text%__1cMindIndexOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cFStateQ_sub_Op_CreateEx6MpknENode__v_; +text: .text%__1cRshrL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cLjmpConUNodeJis_Branch6kM_I_; +text: .text%__1cLjmpConUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLjmpConUNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cRaddI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cPciInstanceKlass2t6MnLKlassHandle__v_; +text: .text%__1cHCompileXin_preserve_stack_slots6M_I_; +text: .text%__1cMMachCallNodeHis_Call6M_pnICallNode__; +text: .text%__1cNdecI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKStoreCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLLShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cTtypeArrayKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLklassVtableKis_miranda6FpnNmethodOopDesc_pnPobjArrayOopDesc_pnMklassOopDesc__i_; +text: .text%__1cTconvL2I_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cRalign_code_offset6Fi_I_; +text: .text%__1cMURShiftINodeLbottom_type6kM_pknEType__; +text: .text%__1cMorI_rRegNodeErule6kM_I_; +text: .text%__1cMLinkResolverVresolve_invokespecial6FrnICallInfo_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cHMulNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cTconvL2I_reg_regNodeErule6kM_I_; +text: .text%__1cRmethodDataOopDescJbci_to_dp6Mi_pC_; +text: .text%__1cIAddLNodeGadd_id6kM_pknEType__; +text: .text%__1cRaddL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cLRShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFTypeFEhash6kM_i_; +text: .text%__1cIGraphKitMarray_length6MpnENode__2_; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_; +text: .text%__1cPsalI_rReg_1NodeErule6kM_I_; +text: .text%__1cIJVMState2t6Mi_v_; +text: .text%__1cNstoreImmBNodeHtwo_adr6kM_I_; +text: .text%__1cLLShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cScompU_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cNGrowableArray4Cl_Icontains6kMrkl_i_; +text: .text%__1cScompU_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_StringGequals6FpnHoopDesc_pHi_i_; +text: .text%__1cOcompP_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTCreateExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMURShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKRegionNodeOhas_unique_phi6kM_pnHPhiNode__; +text: .text%__1cIMulINodeLbottom_type6kM_pknEType__; +text: .text%__1cKTypeAryPtrFxdual6kM_pknEType__; +text: .text%__1cVExceptionHandlerTableMadd_subtable6MipnNGrowableArray4Cl__22_v_; +text: .text%__1cNandL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cNObjectMonitorHis_busy6kM_l_; +text: .text%__1cEDict2t6MpFpkv2_ipF2_i_v_; +text: .text%__1cJAssemblerElock6M_v_; +text: .text%__1cJAssemblerIcmpxchgq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cSsafePoint_pollNodePoper_input_base6kM_I_; +text: .text%__1cLRuntimeStubbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cPshrI_rReg_1NodeErule6kM_I_; +text: .text%__1cRmethodDataOopDescKmileage_of6FpnNmethodOopDesc__i_; +text: .text%__1cKManagementJtimestamp6F_x_; +text: .text%__1cbDReferenceProcessorInitializerIis_clean6kM_v_; +text: .text%__1cIPSOldGenPupdate_counters6M_v_; +text: .text%__1cNSingletonBlobIis_alive6kM_i_; +text: .text%__1cKTypeRawPtrCeq6kMpknEType__i_; +text: .text%__1cIregDOperEtype6kM_pknEType__; +text: .text%__1cQleaPIdxScaleNodeHtwo_adr6kM_I_; +text: .text%__1cTStackWalkCompPolicyPshouldNotInline6FnMmethodHandle__pkc_; +text: .text%__1cMPrefetchNodeLbottom_type6kM_pknEType__; +text: .text%__1cPcmpFastLockNodeErule6kM_I_; +text: .text%__1cFArena2t6M_v_; +text: .text%__1cSCallLeafDirectNodePoper_input_base6kM_I_; +text: .text%__1cMCallLeafNodeLis_CallLeaf6kM_pk0_; +text: .text%__1cQleaPIdxScaleNodeMideal_Opcode6kM_i_; +text: .text%__1cJcmpOpOperFequal6kM_i_; +text: .text%__1cScompI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_IsSameObject: jni.o; +text: .text%__1cNmulL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNmulL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cIGraphKitYcombine_exception_states6MpnNSafePointNode_2_v_; +text: .text%__1cJloadBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMrcx_RegIOperJnum_edges6kM_I_; +text: .text%__1cFKlassNoop_is_method6kM_i_; +text: .text%__1cIMulLNodeLbottom_type6kM_pknEType__; +text: .text%__1cHnmethodPis_locked_by_vm6kM_i_; +text: .text%__1cONMethodSweeperPprocess_nmethod6FpnHnmethod__v_; +text: .text%__1cRaddP_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXjava_lang_ref_ReferenceWpending_list_lock_addr6F_ppnHoopDesc__; +text: .text%__1cJloadLNodeHtwo_adr6kM_I_; +text: .text%__1cHMulNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMrep_stosNodePoper_input_base6kM_I_; +text: .text%__1cRsalI_rReg_immNodeErule6kM_I_; +text: .text%__1cJFieldTypeSskip_optional_size6FpnNsymbolOopDesc_pi_v_; +text: .text%__1cMloadConPNodeHsize_of6kM_I_; +text: .text%__1cSCallLeafDirectNodeHtwo_adr6kM_I_; +text: .text%__1cHnmethodVcleanup_inline_caches6M_v_; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__; +text: .text%__1cQsolaris_mprotect6FpcLi_i_: os_solaris.o; +text: .text%__1cRaddI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cHnmethodLis_unloaded6kM_i_; +text: .text%__1cOGenerateOopMapHppstore6MpnNCellTypeState_i_v_; +text: .text%__1cIGraphKitXset_edges_for_java_call6MpnMCallJavaNode_i_v_; +text: .text%__1cTconvI2L_reg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cHi2sNodeJnum_opnds6kM_I_; +text: .text%__1cSconstMethodOopDescZchecked_exceptions_length6kM_i_; +text: .text%__1cHMatcherXadjust_incoming_stk_arg6Mi_i_; +text: .text%__1cNIdealLoopTreeIset_nest6MI_i_; +text: .text%__1cNIdealLoopTreeMcounted_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cRsubI_rReg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cIGraphKitZset_results_for_java_call6MpnMCallJavaNode__pnENode__; +text: .text%__1cTconvI2L_reg_memNodePoper_input_base6kM_I_; +text: .text%__1cFStateM_sub_Op_CmpU6MpknENode__v_; +text: .text%__1cLRethrowNodeKmatch_edge6kMI_I_; +text: .text%__1cKcopy_table6FppC1i_v_: interpreter.o; +text: .text%__1cUBytecode_tableswitchOdest_offset_at6kMi_i_; +text: .text%__1cNobjArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cKTypeRawPtrFxmeet6kMpknEType__3_; +text: .text%__1cMVM_OperationVevaluate_at_safepoint6kM_i_; +text: .text%__1cMVM_OperationVevaluate_concurrently6kM_i_; +text: .text%__1cMVM_OperationSis_cheap_allocated6kM_i_; +text: .text%__1cXmembar_release_lockNodePoper_input_base6kM_I_; +text: .text%__1cRaddL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cScompP_mem_rRegNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cJFieldTypeOget_array_info6FpnNsymbolOopDesc_pip2pnGThread__nJBasicType__; +text: .text%__1cJFieldTypeYis_valid_array_signature6FpnNsymbolOopDesc__i_; +text: .text%__1cNincI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObject2t6M_v_; +text: .text%__1cPstoreImmI16NodePoper_input_base6kM_I_; +text: .text%__1cNinstanceKlassbDcheck_valid_for_instantiation6MipnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_class_modifiers6MipnGThread__v_; +text: .text%__1cQLibraryIntrinsicKis_virtual6kM_i_; +text: .text%__1cPciObjectFactoryUget_empty_methodData6M_pnMciMethodData__; +text: .text%__1cMciMethodData2t6M_v_; +text: .text%__1cPsarI_rReg_1NodePoper_input_base6kM_I_; +text: .text%__1cNstoreImmBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFTypeDEhash6kM_i_; +text: .text%__1cMPrefetchNodeKmatch_edge6kMI_I_; +text: .text%__1cHCompileQcan_generate_C2I6MpnIciMethod_i_i_; +text: .text%__1cPloadConUL32NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPciObjArrayKlassSis_obj_array_klass6M_i_; +text: .text%__1cLOpaque1NodeEhash6kM_I_; +text: .text%__1cXmembar_release_lockNodeHtwo_adr6kM_I_; +text: .text%JVM_GetMethodIxModifiers; +text: .text%__1cJloadBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNandL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cNandL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cFKlassMset_subklass6MpnMklassOopDesc__v_; +text: .text%__1cTCallDynamicJavaNodeGOpcode6kM_i_; +text: .text%__1cJloadINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKklassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cUCompressedReadStreamMraw_read_int6FrpC_i_; +text: .text%__1cIHaltNodeEhash6kM_I_; +text: .text%__1cNstoreImmINodePoper_input_base6kM_I_; +text: .text%__1cLAccessFlagsPatomic_set_bits6Mi_v_; +text: .text%__1cNinstanceKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cNinstanceKlassScopy_static_fields6MpnSPSPromotionManager__v_; +text: .text%__1cSinstanceKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cOcompL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQVMOperationQdDueueLremove_next6M_pnMVM_Operation__; +text: .text%__1cQciTypeArrayKlassTis_type_array_klass6M_i_; +text: .text%__1cRsubI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFStateP_sub_Op_LShiftL6MpknENode__v_; +text: .text%__1cLjmpConUNodeGnegate6M_v_; +text: .text%__1cKcmpOpUOperGnegate6M_v_; +text: .text%__1cMrax_RegLOperJnum_edges6kM_I_; +text: .text%__1cLGCTaskQdDueueKinitialize6M_v_; +text: .text%__1cJStealTask2t6Mi_v_; +text: .text%__1cJStealTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cTOldToYoungRootsTaskEname6M_pc_; +text: .text%__1cTOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cNGCTaskManagerMnote_release6MI_v_; +text: .text%__1cJStealTaskEname6M_pc_; +text: .text%__1cSCardTableExtensionbAscavenge_contents_parallel6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager_I_v_; +text: .text%__1cFciEnvbAget_constant_by_index_impl6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cQciBytecodeStreamMget_constant6M_nKciConstant__; +text: .text%__1cFciEnvVget_constant_by_index6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cJcmpOpOperFclone6kM_pnIMachOper__; +text: .text%__1cMrep_stosNodeMideal_Opcode6kM_i_; +text: .text%__1cEhash6Fpkc1_I_; +text: .text%__1cQput_after_lookup6FnMsymbolHandle_0ppnLNameSigHash__i_; +text: .text%__1cKJavaThreadLgc_epilogue6M_v_; +text: .text%__1cKJavaThreadLgc_prologue6M_v_; +text: .text%__1cTsize_java_to_interp6F_I_; +text: .text%__1cUreloc_java_to_interp6F_I_; +text: .text%__1cQinit_input_masks6FIrnHRegMask_1_p0_: matcher.o; +text: .text%__1cKOSRAdapterHoops_do6MpnKOopClosure__v_; +text: .text%__1cRCompilationPolicybIreset_counter_for_invocation_event6MnMmethodHandle__v_; +text: .text%__1cRitableMethodEntryKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cTcompareAndSwapLNodeMideal_Opcode6kM_i_; +text: .text%__1cNinstanceKlassPlink_class_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIGraphKitbBset_arguments_for_java_call6MpnMCallJavaNode__v_; +text: .text%__1cNCallGeneratorCtf6kM_pknITypeFunc__; +text: .text%__1cMloadConLNodeLbottom_type6kM_pknEType__; +text: .text%__1cKStoreBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNaddL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cTcompareAndSwapLNodeJnum_opnds6kM_I_; +text: .text%__1cFStateO_sub_Op_StoreI6MpknENode__v_; +text: .text%__1cQleaPIdxScaleNodePoper_input_base6kM_I_; +text: .text%__1cNGrowableArray4CpnNmethodOopDesc__2t6Mii_v_; +text: .text%__1cLklassVtableMget_mirandas6FpnNGrowableArray4CpnNmethodOopDesc___pnMklassOopDesc_pnPobjArrayOopDesc_8_v_; +text: .text%__1cXJNI_ArgumentPusherVaArgKget_object6M_v_; +text: .text%__1cNloadConP0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFKlassKsuperklass6kM_pnNinstanceKlass__; +text: .text%__1cWstatic_stub_RelocationJpack_data6M_i_; +text: .text%__1cNsubL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cbAjni_check_async_exceptions6FpnKJavaThread__v_: jni.o; +text: .text%__1cKJavaThreadbHcheck_and_handle_async_exceptions6Mi_v_; +text: .text%__1cRsalI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMindIndexOperNbase_position6kM_i_; +text: .text%__1cMindIndexOperOindex_position6kM_i_; +text: .text%__1cMindIndexOperNconstant_disp6kM_i_; +text: .text%__1cJLoadSNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNGrowableArray4CpnOMethodLivenessKBasicBlock__2t6Mii_v_; +text: .text%__1cKstoreINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCallGeneratorPfor_direct_call6FpnIciMethod__p0_; +text: .text%__1cTDirectCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cMWarmCallInfoLalways_cold6F_p0_; +text: .text%__1cFframeIpatch_pc6MpnGThread_pC_v_; +text: .text%JVM_IsInterface; +text: .text%__1cFKlassQset_next_sibling6MpnMklassOopDesc__v_; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_; +text: .text%__1cRshrL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQjmpCon_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQjmpCon_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKjmpConNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cQjmpCon_shortNodeJis_Branch6kM_I_; +text: .text%__1cKJavaThreadNpd_last_frame6M_nFframe__; +text: .text%__1cTStackWalkCompPolicyVfindTopInlinableFrame6MpnNGrowableArray4CpnGRFrame____2_; +text: .text%__1cKJavaThreadQlast_java_vframe6MpnLRegisterMap__pnKjavaVFrame__; +text: .text%__1cTStackWalkCompPolicyXmethod_invocation_event6MnMmethodHandle_pnGThread__v_; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_nMmethodHandle__v_; +text: .text%__1cNGrowableArray4CpnGRFrame__2t6Mii_v_; +text: .text%__1cKjavaVFrameNis_java_frame6kM_i_; +text: .text%__1cIVerifierRshould_verify_for6FpnHoopDesc__i_; +text: .text%__1cQciBytecodeStreamPget_klass_index6M_i_; +text: .text%__1cRMachNullCheckNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRMachNullCheckNode2t6MpnENode_2I_v_; +text: .text%__1cRsarI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNMachIdealNodeJnum_opnds6kM_I_; +text: .text%__1cRMachSafePointNodePis_MachCallJava6M_pnQMachCallJavaNode__; +text: .text%__1cKstorePNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWImplicitExceptionTableGappend6MII_v_; +text: .text%__1cHUNICODEHas_utf86FpHi_pc_; +text: .text%__1cMStartI2CNodeGOpcode6kM_i_; +text: .text%__1cKOSRAdapterMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cSvframeStreamCommonbHskip_method_invoke_and_aux_frames6M_v_; +text: .text%__1cNdecI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cIMinINodeGOpcode6kM_i_; +text: .text%__1cNinstanceKlassbCfind_local_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cNinstanceKlassWfind_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cPciInstanceKlassTget_field_by_offset6Mii_pnHciField__; +text: .text%__1cFArena2T6M_v_; +text: .text%__1cKmethodOperJnum_edges6kM_I_; +text: .text%__1cSconstMethodOopDescYchecked_exceptions_start6kM_pnXCheckedExceptionElement__; +text: .text%__1cISubLNodeGOpcode6kM_i_; +text: .text%__1cFStateO_sub_Op_StoreP6MpknENode__v_; +text: .text%__1cRshrI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRsarL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNstoreImmBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstorePNodeFreloc6kM_i_; +text: .text%__1cYCallStaticJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cYCallStaticJavaDirectNodeJnum_opnds6kM_I_; +text: .text%__1cQleaPIdxScaleNodeErule6kM_I_; +text: .text%__1cTciConstantPoolCacheGinsert6Mipv_v_; +text: .text%__1cNinstanceKlassXmark_dependent_nmethods6MpnMklassOopDesc__i_; +text: .text%__1cMvframeStream2t6MpnKJavaThread_i_v_; +text: .text%__1cIGraphKitTuse_exception_state6MpnNSafePointNode__pnENode__; +text: .text%__1cIGraphKitSclear_saved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cNloadConI0NodeFclone6kM_pnENode__; +text: .text%__1cJimmI0OperFclone6kM_pnIMachOper__; +text: .text%__1cLCastP2LNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMLinkResolverbBresolve_static_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cKcmpOpUOperNgreater_equal6kM_i_; +text: .text%__1cPClassFileParserYparse_checked_exceptions6MpHInSconstantPoolHandle_pnGThread__1_; +text: .text%__1cXcopy_u2_with_conversion6FpH0i_v_: classFileParser.o; +text: .text%__1cENodeGis_Sub6M_pnHSubNode__; +text: .text%__1cJAssemblerFtestq6MpnMRegisterImpl_2_v_; +text: .text%__1cJCMoveNodeLis_cmove_id6FpnOPhaseTransform_pnENode_44pnIBoolNode__4_; +text: .text%__1cZresource_reallocate_bytes6FpcLL_0_; +text: .text%__1cKstoreINodeFreloc6kM_i_; +text: .text%__1cLsymbolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQciBytecodeStreamJget_klass6Mri_pnHciKlass__; +text: .text%__1cKMemBarNode2t6M_v_; +text: .text%__1cIDivINodeGOpcode6kM_i_; +text: .text%__1cFframeRis_compiled_frame6kMpi_i_; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_; +text: .text%__1cPshrI_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMorI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFParseQarray_addressing6MnJBasicType_ippknEType__pnENode__; +text: .text%__1cPsalI_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHciField2t6MpnPfieldDescriptor__v_; +text: .text%__1cIAddLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIModINodeGOpcode6kM_i_; +text: .text%__1cNmulL_rRegNodeErule6kM_I_; +text: .text%__1cSsafePoint_pollNodeHtwo_adr6kM_I_; +text: .text%__1cDCHAManalyze_call6FnLKlassHandle_11nMsymbolHandle_2_pnJCHAResult__; +text: .text%__1cJCHAResult2t6MnLKlassHandle_nMsymbolHandle_2pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___n0E_i_v_; +text: .text%__1cMLinkResolverbCresolve_virtual_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cJCHAResultOis_monomorphic6kM_i_; +text: .text%__1cIciMethodXfind_monomorphic_target6MpnHciKlass_22_p0_; +text: .text%__1cQconstMethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cKMemBarNodeJis_MemBar6kM_pk0_; +text: .text%__1cIGraphKitOinsert_mem_bar6MpnKMemBarNode__v_; +text: .text%__1cHi2sNodeHtwo_adr6kM_I_; +text: .text%__1cJCodeCacheMfind_nmethod6Fpv_pnHnmethod__; +text: .text%__1cLConvL2INodeLbottom_type6kM_pknEType__; +text: .text%__1cMLinkResolverbCresolve_special_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cNIdealLoopTreeObeautify_loops6MpnOPhaseIdealLoop__i_; +text: .text%__1cScompP_mem_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreBNodePoper_input_base6kM_I_; +text: .text%__1cRandI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSCallLeafDirectNodeRis_safepoint_node6kM_i_; +text: .text%__1cJloadLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCallLeafDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKMemBarNodeJideal_reg6kM_I_; +text: .text%__1cJloadSNodeHtwo_adr6kM_I_; +text: .text%__1cVConstantOopWriteValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cUDebugInfoWriteStreamMwrite_handle6MpnI_jobject__v_; +text: .text%__1cLmethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNaddI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJlog2_long6Fx_i_; +text: .text%__1cTconvL2I_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitPpush_pair_local6Mi_v_; +text: .text%__1cOjmpLoopEndNodePoper_input_base6kM_I_; +text: .text%__1cMURShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHCompileSregister_intrinsic6MpnNCallGenerator__v_; +text: .text%__1cIAddLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNloadKlassNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQjava_lang_StringRas_unicode_string6FpnHoopDesc_ri_pH_; +text: .text%__1cHCmpNodeGadd_id6kM_pknEType__; +text: .text%JVM_InternString; +text: .text%__1cLStringTableGintern6FpnHoopDesc_pnGThread__2_; +text: .text%__1cFKlassQup_cast_abstract6M_p0_; +text: .text%__1cNGrowableArray4CpnENode__2t6Mii_v_; +text: .text%__1cPCheckCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSInterpreterRuntimeMmonitorenter6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cSCountedLoopEndNodeKstride_con6kM_i_; +text: .text%__1cTconvI2L_reg_memNodeJnum_opnds6kM_I_; +text: .text%__1cPCheckCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPClassFileStreamHskip_u26MipnGThread__v_; +text: .text%__1cLOpaque1NodeLbottom_type6kM_pknEType__; +text: .text%__1cOPhaseIdealLoopRsplit_thru_region6MpnENode_2_2_; +text: .text%__1cFTypeFCeq6kMpknEType__i_; +text: .text%__1cNmethodOopDescOis_initializer6kM_i_; +text: .text%__1cFciEnvRfind_system_klass6MpnIciSymbol__pnHciKlass__; +text: .text%__1cNandL_rRegNodeErule6kM_I_; +text: .text%__1cQjmpDir_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cQjmpDir_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKjmpDirNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cQjmpDir_shortNodeJis_Branch6kM_I_; +text: .text%__1cLBlock_ArrayEgrow6MI_v_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cSCompareAndSwapNodeLbottom_type6kM_pknEType__; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6FnUtypeArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cMPhaseChaitinVfind_base_for_derived6MppnENode_2rI_2_; +text: .text%__1cSindIndexOffsetOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cSindIndexOffsetOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cNGrowableArray4CI_Egrow6Mi_v_; +text: .text%__1cHMatcherMreturn_value6Fii_nLOptoRegPair__; +text: .text%__1cFStateP_sub_Op_ConvI2L6MpknENode__v_; +text: .text%__1cOjmpLoopEndNodeGpinned6kM_i_; +text: .text%__1cNxorI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cJCHAResultSmonomorphic_target6kM_nMmethodHandle__; +text: .text%__1cNsubI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_; +text: .text%__1cRcmpFastUnlockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJtimestamp6FpnHoopDesc__x_; +text: .text%__1cQLRUMaxHeapPolicyWshould_clear_reference6MpnHoopDesc__i_; +text: .text%__1cLcastP2LNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcheckCastPPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPciInstanceKlassLfind_method6MpnIciSymbol_2_pnIciMethod__; +text: .text%__1cZCallInterpreterDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cILoopNodeHsize_of6kM_I_; +text: .text%__1cSindIndexOffsetOperFscale6kM_i_; +text: .text%__1cMjniIdSupportNto_method_oop6FpnK_jmethodID__pnNmethodOopDesc__; +text: .text%__1cLBoxLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRSignatureIterator2t6MpnGThread_pnNsymbolOopDesc__v_; +text: .text%__1cRaddI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_; +text: .text%__1cGOopMapPset_derived_oop6Miiii_v_; +text: .text%__1cKstoreBNodeMideal_Opcode6kM_i_; +text: .text%__1cHi2bNodeErule6kM_I_; +text: .text%__1cFStateN_sub_Op_LoadI6MpknENode__v_; +text: .text%__1cMloadConDNodePoper_input_base6kM_I_; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__; +text: .text%__1cICmpLNodeDsub6kMpknEType_3_3_; +text: .text%__1cRjmpConU_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRjmpConU_shortNodeMideal_Opcode6kM_i_; +text: .text%__1cMloadConDNodeHtwo_adr6kM_I_; +text: .text%__1cHnmethodKpc_desc_at6MpC_pnGPcDesc__; +text: .text%__1cJrRegPOperFclone6kM_pnIMachOper__; +text: .text%__1cFParseNpush_constant6MnKciConstant__i_; +text: .text%__1cMrep_stosNodeJnum_opnds6kM_I_; +text: .text%__1cOClearArrayNodeKmatch_edge6kMI_I_; +text: .text%__1cUvisit_all_interfaces6FpnPobjArrayOopDesc_pnXInterfaceVisiterClosure__v_; +text: .text%__1cXmembar_release_lockNodeLbottom_type6kM_pknEType__; +text: .text%__1cPThreadLocalNodeJideal_reg6kM_I_; +text: .text%__1cPstoreImmI16NodeJnum_opnds6kM_I_; +text: .text%__1cTleaPIdxScaleOffNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitbMset_predefined_output_for_runtime_call6MpnENode_pnMMergeMemNode__v_; +text: .text%__1cFKlassXsearch_secondary_supers6kMpnMklassOopDesc__i_; +text: .text%__1cPsarI_rReg_1NodeErule6kM_I_; +text: .text%__1cOPhaseIdealLoopPis_counted_loop6MpnENode_pnNIdealLoopTree__2_; +text: .text%__1cIGraphKitOhas_ex_handler6M_i_; +text: .text%__1cMloadConDNodeErule6kM_I_; +text: .text%__1cHCompileQsync_stack_slots6kM_i_; +text: .text%__1cNMemoryServiceXtrack_memory_pool_usage6FpnKMemoryPool__v_; +text: .text%__1cMURShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKoopFactoryTnew_system_objArray6FipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNdecI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cPClassFileParserbHparse_constant_pool_integer_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cTDebugInfoReadStream2t6MpknHnmethod_i_v_; +text: .text%__1cRsalI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cJScopeDescJstream_at6kMi_pnTDebugInfoReadStream__; +text: .text%__1cVjava_lang_ClassLoaderGparent6FpnHoopDesc__2_; +text: .text%__1cIPhaseIFGEinit6MI_v_; +text: .text%__1cMPhaseChaitinQgather_lrg_masks6Mi_v_; +text: .text%__1cJPhaseLiveHcompute6MI_v_; +text: .text%JVM_GetCPClassNameUTF; +text: .text%__1cMLinkResolverUresolve_invokestatic6FrnICallInfo_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNstoreImmINodeJnum_opnds6kM_I_; +text: .text%__1cITypeNodeHis_Type6M_p0_; +text: .text%__1cHRetNodePoper_input_base6kM_I_; +text: .text%__1cLCastP2LNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%jni_GetStringLength: jni.o; +text: .text%__1cPloadConUL32NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOFastUnlockNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPciObjArrayKlassNelement_klass6M_pnHciKlass__; +text: .text%__1cNprefetchwNodeHtwo_adr6kM_I_; +text: .text%__1cNnmethodLocker2T6M_v_; +text: .text%__1cKoopFactoryYnew_permanent_shortArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cKstoreCNodeHtwo_adr6kM_I_; +text: .text%__1cQleaPIdxScaleNodeJnum_opnds6kM_I_; +text: .text%__1cNaddL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cOcompL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cTDebugInfoReadStreamLread_handle6M_nGHandle__; +text: .text%__1cJScopeDesc2t6MpknHnmethod_i_v_; +text: .text%__1cFStateR_sub_Op_LoadRange6MpknENode__v_; +text: .text%__1cOcompiledVFrame2t6MpknFframe_pknLRegisterMap_pnKJavaThread_pnJScopeDesc__v_; +text: .text%__1cOcompU_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmovI_reg_gNodePoper_input_base6kM_I_; +text: .text%__1cLProfileDataSis_VirtualCallData6M_i_; +text: .text%__1cSmembar_acquireNodePoper_input_base6kM_I_; +text: .text%__1cNsubL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cJMarkSweepMfollow_stack6F_v_; +text: .text%__1cNnmethodLocker2t6MpnHnmethod__v_; +text: .text%__1cNloadRangeNodeFreloc6kM_i_; +text: .text%__1cNGrowableArray4CpnKciTypeFlowJJsrRecord__2t6Miirk2i_v_; +text: .text%__1cTcompareAndSwapLNodeErule6kM_I_; +text: .text%__1cZCallDynamicJavaDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cMURShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOcompiledVFrameGis_top6kM_i_; +text: .text%__1cSInterpreterRuntimeLmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cNxorI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cRshrI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cKciTypeFlow2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cKciTypeFlowXmark_known_range_starts6M_v_; +text: .text%__1cKciTypeFlowLfind_ranges6M_v_; +text: .text%__1cSFixupMirrorClosureJdo_object6MpnHoopDesc__v_; +text: .text%__1cKciTypeFlowKmap_blocks6M_v_; +text: .text%__1cKciTypeFlowHdo_flow6M_v_; +text: .text%__1cKciTypeFlowPget_start_state6M_pkn0ALStateVector__; +text: .text%__1cKciTypeFlowKflow_types6M_v_; +text: .text%__1cIAndINodeGadd_id6kM_pknEType__; +text: .text%__1cMURShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadBNodeHtwo_adr6kM_I_; +text: .text%__1cKPSYoungGenRcapacity_in_bytes6kM_L_; +text: .text%__1cHMonitorGnotify6M_i_; +text: .text%__1cYciExceptionHandlerStreamPcount_remaining6M_i_; +text: .text%__1cFParseXcatch_inline_exceptions6MpnNSafePointNode__v_; +text: .text%__1cHMatcherNfind_receiver6Fi_i_; +text: .text%__1cMciMethodDataJload_data6M_v_; +text: .text%__1cIciMethodJload_code6M_v_; +text: .text%__1cJCmpL3NodeGOpcode6kM_i_; +text: .text%__1cIAndINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cWstatic_call_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cUParallelScavengeHeapEused6kM_L_; +text: .text%__1cOGenerateOopMapGdo_ldc6Mii_v_; +text: .text%__1cMTypeKlassPtrFxdual6kM_pknEType__; +text: .text%__1cIMaxINodeGOpcode6kM_i_; +text: .text%__1cOPhaseTransform2t6MnFPhaseLPhaseNumber__v_; +text: .text%__1cPsalI_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cQSystemDictionarybAcompute_loader_lock_object6FnGHandle_pnGThread__1_; +text: .text%__1cHciKlassMis_interface6M_i_; +text: .text%__1cPmethodDataKlassRoop_is_methodData6kM_i_; +text: .text%__1cIMulLNodeGadd_id6kM_pknEType__; +text: .text%__1cJloadCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionaryRpreloaded_oops_do6FpnKOopClosure__v_; +text: .text%__1cIVMThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cLJvmtiExportHoops_do6FpnKOopClosure__v_; +text: .text%__1cMFlatProfilerHoops_do6FpnKOopClosure__v_; +text: .text%__1cQVMOperationQdDueueHoops_do6MpnKOopClosure__v_; +text: .text%__1cNMemoryServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cOJNIHandleBlockMweak_oops_do6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cKJNIHandlesMweak_oops_do6FpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSObjectSynchronizerHoops_do6FpnKOopClosure__v_; +text: .text%__1cKManagementHoops_do6FpnKOopClosure__v_; +text: .text%__1cQPlaceholderTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cNThreadServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cKJNIHandlesHoops_do6FpnKOopClosure__v_; +text: .text%__1cXJvmtiCurrentBreakpointsHoops_do6FpnKOopClosure__v_; +text: .text%__1cIUniverseHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollectorXoops_do_for_all_threads6FpnKOopClosure__v_; +text: .text%__1cRindIndexScaleOperJnum_edges6kM_I_; +text: .text%__1cRindIndexScaleOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKstoreBNodeJnum_opnds6kM_I_; +text: .text%__1cNSignatureInfoJdo_double6M_v_; +text: .text%__1cJAssemblerEmovl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cRsalI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cMrdx_RegIOperEtype6kM_pknEType__; +text: .text%__1cMciMethodData2t6MnQmethodDataHandle__v_; +text: .text%__1cSmembar_acquireNodeHtwo_adr6kM_I_; +text: .text%__1cRshrI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cKJNIHandlesLmake_global6FnGHandle_i_pnI_jobject__; +text: .text%jni_ExceptionOccurred: jni.o; +text: .text%jni_SetObjectArrayElement: jni.o; +text: .text%__1cSCompareAndSwapNodeKmatch_edge6kMI_I_; +text: .text%__1cISubINodeJideal_reg6kM_I_; +text: .text%__1cRMachSafePointNodeGpinned6kM_i_; +text: .text%__1cIimmIOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConINodeFclone6kM_pnENode__; +text: .text%__1cICodeHeapIallocate6ML_pv_; +text: .text%__1cICodeHeapPsearch_freelist6ML_pnJFreeBlock__; +text: .text%__1cbACallCompiledJavaDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cPcmpFastLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLCastP2LNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNmulL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJLoadBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cVmerge_point_too_heavy6FpnHCompile_pnENode__i_: loopopts.o; +text: .text%jni_GetByteArrayRegion: jni.o; +text: .text%__1cFParseKdo_put_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cHnmethodOis_java_method6kM_i_; +text: .text%__1cQjava_lang_StringLutf8_length6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc_ii_pc_; +text: .text%jni_GetStringUTFRegion: jni.o; +text: .text%jni_GetStringUTFLength: jni.o; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_; +text: .text%__1cRsarL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cScompU_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFciEnvZcheck_klass_accessibility6MpnHciKlass_pnMklassOopDesc__i_; +text: .text%__1cIciObjectMis_obj_array6M_i_; +text: .text%__1cOLibraryCallKitOgenerate_guard6MpnENode_pnKRegionNode_f_v_; +text: .text%__1cMoutputStream2t6Mi_v_; +text: .text%__1cMstringStreamJas_string6M_pc_; +text: .text%__1cMstringStream2t6ML_v_; +text: .text%__1cJloadINodeFreloc6kM_i_; +text: .text%__1cMstringStream2T6M_v_; +text: .text%__1cOMethodLivenessKBasicBlockJstore_two6Mi_v_; +text: .text%__1cJloadINodeIpeephole6MpnFBlock_ipnNPhaseRegAlloc_ri_pnIMachNode__; +text: .text%__1cTconvL2I_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cPClassFileParserXverify_legal_class_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cRandI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cOAbstractICacheQinvalidate_range6FpCi_v_; +text: .text%__1cOAbstractICachePcall_flush_stub6FpCi_v_; +text: .text%__1cICodeBlobMset_oop_maps6MpnJOopMapSet__v_; +text: .text%__1cRClassPathZipEntryLopen_stream6Mpkc_pnPClassFileStream__; +text: .text%__1cJCodeCacheIallocate6Fi_pnICodeBlob__; +text: .text%__1cIGraphKitOmake_slow_call6MpknITypeFunc_pCpkcpnENode_88_8_; +text: .text%__1cICodeHeapPfollowing_block6MpnJFreeBlock__2_; +text: .text%__1cOClearArrayNodeLbottom_type6kM_pknEType__; +text: .text%__1cPshrI_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cEDictIdoubhash6M_v_; +text: .text%__1cTleaPIdxScaleOffNodeLbottom_type6kM_pknEType__; +text: .text%__1cIProjNodeJideal_reg6kM_I_; +text: .text%__1cHi2sNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLimmI_16OperJnum_edges6kM_I_; +text: .text%__1cUmembar_cpu_orderNodePoper_input_base6kM_I_; +text: .text%__1cPfieldDescriptorRint_initial_value6kM_i_; +text: .text%__1cTCallInterpreterNodeGOpcode6kM_i_; +text: .text%__1cMloadConLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRaddL_rReg_immNodeErule6kM_I_; +text: .text%__1cJLoadLNodeJideal_reg6kM_I_; +text: .text%__1cTleaPIdxScaleOffNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cHCompileTset_cached_top_node6MpnENode__v_; +text: .text%__1cENodeMsetup_is_top6M_v_; +text: .text%__1cIGotoNodeGOpcode6kM_i_; +text: .text%__1cOMachPrologNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHCompilePneed_stack_bang6kMi_i_; +text: .text%__1cKBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cNFingerprinterIdo_array6Mii_v_; +text: .text%jni_GetArrayLength: jni.o; +text: .text%__1cJloadSNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKReturnNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSInterpreterRuntimeE_new6FpnKJavaThread_pnTconstantPoolOopDesc_i_v_; +text: .text%__1cMorI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cKTypeRawPtrFempty6kM_i_; +text: .text%__1cHRetNodeGpinned6kM_i_; +text: .text%__1cHRetNodeHtwo_adr6kM_I_; +text: .text%__1cPsalI_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cNinstanceKlassVadd_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cHRetNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPGlobalTLABStatsKinitialize6M_v_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJset_clock6Fx_v_; +text: .text%__1cUParallelScavengeHeapTensure_parseability6M_v_; +text: .text%__1cTDerivedPointerTableFclear6F_v_; +text: .text%__1cNMemoryServiceGgc_end6Fi_v_; +text: .text%__1cSReferenceProcessorQprocess_phaseJNI6M_v_; +text: .text%__1cRLowMemoryDetectorRdetect_low_memory6F_v_; +text: .text%__1cSReferenceProcessorbDenqueue_discovered_references6M_i_; +text: .text%__1cSReferenceProcessorbDprocess_discovered_references6M_v_; +text: .text%__1cNCollectedHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cTDerivedPointerTablePupdate_pointers6F_v_; +text: .text%__1cNCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapOfill_all_tlabs6M_v_; +text: .text%__1cKDictionaryHoops_do6MpnKOopClosure__v_; +text: .text%__1cSReferenceProcessorbBenqueue_discovered_reflists6MppnHoopDesc__v_; +text: .text%__1cWThreadLocalAllocBufferbFaccumulate_statistics_before_gc6F_v_; +text: .text%__1cSReferenceProcessorOprocess_phase16MppnHoopDesc_pnPReferencePolicy_pnRBoolObjectClosure_pnKOopClosure_pnLVoidClosure__v_; +text: .text%__1cQLRUMaxHeapPolicy2t6M_v_; +text: .text%__1cPGCMemoryManagerIgc_begin6M_v_; +text: .text%__1cPGCMemoryManagerGgc_end6M_v_; +text: .text%__1cVLoaderConstraintTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cUParallelScavengeHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cKPSYoungGenPupdate_counters6M_v_; +text: .text%__1cXjava_lang_ref_ReferenceRpending_list_addr6F_ppnHoopDesc__; +text: .text%__1cNMemoryServiceStrack_memory_usage6F_v_; +text: .text%__1cQSystemDictionaryHoops_do6FpnKOopClosure__v_; +text: .text%__1cNMemoryServiceIgc_begin6Fi_v_; +text: .text%__1cUParallelScavengeHeapOfill_all_tlabs6M_v_; +text: .text%__1cXTraceMemoryManagerStats2T6M_v_; +text: .text%__1cXTraceMemoryManagerStats2t6Mi_v_; +text: .text%__1cUParallelScavengeHeapPupdate_counters6M_v_; +text: .text%__1cQPlaceholderTableJnew_entry6MipnNsymbolOopDesc_pnHoopDesc__pnQPlaceholderEntry__; +text: .text%__1cQPlaceholderTableMremove_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cQPlaceholderTableJadd_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cNCollectedHeapQresize_all_tlabs6M_v_; +text: .text%__1cUParallelScavengeHeapQresize_all_tlabs6M_v_; +text: .text%__1cWThreadLocalAllocBufferQresize_all_tlabs6F_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersPupdate_counters6M_v_; +text: .text%__1cYGCAdaptivePolicyCountersbBupdate_counters_from_policy6M_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersbBupdate_counters_from_policy6M_v_; +text: .text%__1cNmethodOopDescbEfast_exception_handler_bci_for6MnLKlassHandle_ipnGThread__i_; +text: .text%__1cSInterpreterRuntimebFexception_handler_for_exception6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cNaddL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cNaddL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cKstoreLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cUPSAdaptiveSizePolicyZdecay_supplemental_growth6Mi_v_; +text: .text%__1cUPSAdaptiveSizePolicybPeden_increment_with_supplement_aligned_up6ML_L_; +text: .text%__1cUPSAdaptiveSizePolicyQdecaying_gc_cost6kM_d_; +text: .text%__1cUPSAdaptiveSizePolicybDcompute_generation_free_space6MLLLLLLLi_v_; +text: .text%__1cIPSOldGenMmax_gen_size6M_L_; +text: .text%__1cUPSAdaptiveSizePolicybHclear_generation_free_space_flags6M_v_; +text: .text%__1cUPSAdaptiveSizePolicyOeden_increment6MLI_L_; +text: .text%__1cUPSAdaptiveSizePolicyVadjust_for_throughput6MipL1_v_; +text: .text%__1cQSystemDictionaryTload_instance_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cUmembar_cpu_orderNodeHtwo_adr6kM_I_; +text: .text%__1cPjava_lang_ClassNcreate_mirror6FnLKlassHandle_pnGThread__pnHoopDesc__; +text: .text%__1cLklassVtableRinitialize_vtable6MpnGThread__v_; +text: .text%__1cJAssemblerDjmp6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cPshrI_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cRmulI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNandI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cOMachEpilogNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLklassVtableVinitialize_from_super6MnLKlassHandle__i_; +text: .text%__1cIVMThreadHexecute6FpnMVM_Operation__v_; +text: .text%__1cLklassVtableOcopy_vtable_to6MpnLvtableEntry__v_; +text: .text%__1cQinstanceRefKlassZacquire_pending_list_lock6FpnJBasicLock__v_; +text: .text%__1cQinstanceRefKlassbKrelease_and_notify_pending_list_lock6FipnJBasicLock__v_; +text: .text%__1cPVM_GC_OperationbKrelease_and_notify_pending_list_lock6M_v_; +text: .text%__1cPVM_GC_OperationOskip_operation6kM_i_; +text: .text%__1cPVM_GC_OperationNdoit_prologue6M_i_; +text: .text%__1cPVM_GC_OperationZacquire_pending_list_lock6M_v_; +text: .text%__1cMTypeKlassPtrFxmeet6kMpknEType__3_; +text: .text%__1cKReturnNodeEhash6kM_I_; +text: .text%__1cHnmethodVis_dependent_on_entry6MpnMklassOopDesc_2pnNmethodOopDesc__i_; +text: .text%__1cbDVM_ParallelGCFailedAllocation2t6MLiiI_v_; +text: .text%__1cLlog2_intptr6Fl_i_; +text: .text%__1cKKlass_vtbl2n6FLrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cFKlassVbase_create_klass_oop6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassRinitialize_supers6MpnMklassOopDesc_pnGThread__v_; +text: .text%__1cMloadConPNodeFclone6kM_pnENode__; +text: .text%__1cIimmPOperFclone6kM_pnIMachOper__; +text: .text%__1cFKlassRbase_create_klass6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__1_; +text: .text%__1cSCallLeafDirectNodeKmethod_set6Ml_v_; +text: .text%__1cJcmpOpOperJnot_equal6kM_i_; +text: .text%__1cJloadLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRAbstractAssemblerbDgenerate_stack_overflow_check6Mi_v_; +text: .text%__1cCosOunguard_memory6FpcL_i_; +text: .text%__1cNandL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLLShiftINodeJideal_reg6kM_I_; +text: .text%__1cRsarI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_; +text: .text%__1cJLoadSNodeJideal_reg6kM_I_; +text: .text%__1cTconvL2I_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cIPhaseIFGISquareUp6M_v_; +text: .text%__1cPciObjectFactoryMvm_symbol_at6Fi_pnIciSymbol__; +text: .text%__1cCosJyield_all6Fi_v_; +text: .text%__1cKciTypeFlowLStateVectorOmeet_exception6MpnPciInstanceKlass_pk1_i_; +text: .text%__1cCosbCmake_polling_page_unreadable6F_v_; +text: .text%__1cONMethodSweeperFsweep6F_v_; +text: .text%__1cSObjectSynchronizerVdeflate_idle_monitors6F_v_; +text: .text%__1cMCounterDecayFdecay6F_v_; +text: .text%__1cCosOprotect_memory6FpcL_i_; +text: .text%__1cORuntimeServiceWrecord_safepoint_begin6F_v_; +text: .text%__1cORuntimeServicebDrecord_safepoint_synchronized6F_v_; +text: .text%__1cCosXserialize_thread_states6F_v_; +text: .text%__1cUSafepointSynchronizeFbegin6F_v_; +text: .text%__1cUSafepointSynchronizeRis_cleanup_needed6F_i_; +text: .text%__1cUSafepointSynchronizeQdo_cleanup_tasks6F_v_; +text: .text%__1cRInlineCacheBufferIis_empty6F_i_; +text: .text%__1cRInlineCacheBufferUupdate_inline_caches6F_v_; +text: .text%__1cTAbstractInterpreterRnotice_safepoints6F_v_; +text: .text%__1cNloadConP0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosbAmake_polling_page_readable6F_v_; +text: .text%__1cORuntimeServiceUrecord_safepoint_end6F_v_; +text: .text%__1cUSafepointSynchronizeDend6F_v_; +text: .text%__1cTAbstractInterpreterRignore_safepoints6F_v_; +text: .text%__1cQVMOperationQdDueueGinsert6MpnMVM_Operation_2_v_; +text: .text%__1cQVMOperationQdDueueGunlink6MpnMVM_Operation__v_; +text: .text%__1cMVM_OperationIevaluate6M_v_; +text: .text%__1cMVM_OperationSset_calling_thread6MpnGThread_nOThreadPriority__v_; +text: .text%__1cCosMget_priority6FpknGThread_rnOThreadPriority__nIOSReturn__; +text: .text%__1cQVMOperationQdDueueOqueue_add_back6MipnMVM_Operation__v_; +text: .text%__1cGThreadMget_priority6Fpk0_nOThreadPriority__; +text: .text%__1cCosTget_native_priority6FpknGThread_pi_nIOSReturn__; +text: .text%__1cIVMThreadSevaluate_operation6MpnMVM_Operation__v_; +text: .text%__1cQVMOperationQdDueueDadd6MpnMVM_Operation__i_; +text: .text%__1cSmembar_releaseNodeLbottom_type6kM_pknEType__; +text: .text%__1cCosGrandom6F_l_; +text: .text%__1cNget_next_hash6F_l_: synchronizer.o; +text: .text%__1cNJvmtiGCMarker2t6Mi_v_; +text: .text%__1cPVM_GC_OperationNdoit_epilogue6M_v_; +text: .text%__1cKPSScavengeXshould_attempt_scavenge6F_i_; +text: .text%__1cKPSScavengeQinvoke_no_policy6Fpi_i_; +text: .text%__1cPGlobalTLABStatsHpublish6M_v_; +text: .text%__1cUinitialize_hashtable6FppnLNameSigHash__v_; +text: .text%__1cPclear_hashtable6FppnLNameSigHash__v_; +text: .text%__1cQciBytecodeStreamUis_unresolved_string6kM_i_; +text: .text%__1cFciEnvUis_unresolved_string6kMpnPciInstanceKlass_i_i_; +text: .text%__1cFciEnvZis_unresolved_string_impl6kMpnNinstanceKlass_i_i_; +text: .text%__1cNtestP_regNodeFreloc6kM_i_; +text: .text%__1cNSCMemProjNodeGis_CFG6kM_i_; +text: .text%__1cKPSScavengeGinvoke6Fpi_v_; +text: .text%__1cUParallelScavengeHeapTfailed_mem_allocate6MpiLii_pnIHeapWord__; +text: .text%__1cbDVM_ParallelGCFailedAllocationEname6kM_pkc_; +text: .text%__1cbDVM_ParallelGCFailedAllocationEdoit6M_v_; +text: .text%__1cKDictionaryJnew_entry6MIpnMklassOopDesc_pnHoopDesc__pnPDictionaryEntry__; +text: .text%__1cKDictionaryJadd_klass6MnMsymbolHandle_nGHandle_nLKlassHandle__v_; +text: .text%__1cQSystemDictionaryRupdate_dictionary6FiIiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cQSystemDictionaryRcheck_constraints6FiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cQSystemDictionaryQfind_placeholder6FiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cVLoaderConstraintTablePcheck_or_update6MnTinstanceKlassHandle_nGHandle_nMsymbolHandle__pkc_; +text: .text%__1cKoopFactoryXnew_permanent_byteArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNIdealLoopTreeTcheck_inner_safepts6MpnOPhaseIdealLoop__v_; +text: .text%__1cPsarI_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKciTypeFlowPflow_exceptions6MpnNGrowableArray4Cpn0AFBlock___pnNGrowableArray4CpnPciInstanceKlass___pn0ALStateVector__v_; +text: .text%__1cIAndINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciObjectOis_null_object6kM_i_; +text: .text%__1cNIdealLoopTreeNDCE_loop_body6M_v_; +text: .text%__1cNprefetchwNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXmembar_release_lockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFStateP_sub_Op_LShiftI6MpknENode__v_; +text: .text%__1cNIdealLoopTreeVadjust_loop_exit_prob6MpnOPhaseIdealLoop__v_; +text: .text%__1cKstoreCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMPhaseChaitinMreset_uf_map6MI_v_; +text: .text%__1cMPhaseChaitinSbuild_ifg_physical6MpnMResourceArea__I_; +text: .text%__1cNPhaseCoalescePcoalesce_driver6M_v_; +text: .text%__1cNdecI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSComputeAdapterInfoHdo_long6M_v_; +text: .text%__1cOGenerateOopMapJdo_astore6Mi_v_; +text: .text%__1cSTailCalljmpIndNodeNis_block_proj6kM_pknENode__; +text: .text%__1cIciObjectMhas_encoding6M_i_; +text: .text%__1cMrcx_RegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIMulLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cHPhiNodeKmake_blank6FpnENode_2_p0_; +text: .text%JVM_GetMethodIxSignatureUTF; +text: .text%JVM_GetMethodIxMaxStack; +text: .text%JVM_GetMethodIxArgsSize; +text: .text%JVM_GetMethodIxByteCodeLength; +text: .text%JVM_GetMethodIxByteCode; +text: .text%JVM_GetMethodIxExceptionIndexes; +text: .text%JVM_GetMethodIxExceptionsCount; +text: .text%__1cENodeUdepends_only_on_test6kM_i_; +text: .text%__1cXmembar_acquire_lockNodePoper_input_base6kM_I_; +text: .text%__1cOPhaseIdealLoopMdominated_by6MpnENode_2_v_; +text: .text%__1cNGrowableArray4nLKlassHandle__Icontains6kMrkn0A__i_; +text: .text%__1cLGCTaskQdDueue2t6Mi_v_; +text: .text%__1cNaddL_rRegNodeErule6kM_I_; +text: .text%__1cGGCTask2t6Mn0AEKindEkind__v_; +text: .text%__1cZSerialOldToYoungRootsTaskEname6M_pc_; +text: .text%__1cZSerialOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cSPSPromotionManagerNpost_scavenge6F_v_; +text: .text%__1cSPSPromotionManagerMpre_scavenge6F_v_; +text: .text%__1cUWaitForBarrierGCTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cSPSPromotionManagerbBvm_thread_promotion_manager6F_p0_; +text: .text%__1cSAdaptiveSizePolicyWminor_collection_begin6M_v_; +text: .text%__1cSAdaptiveSizePolicyUminor_collection_end6MnHGCCauseFCause__v_; +text: .text%__1cUWaitForBarrierGCTaskEname6M_pc_; +text: .text%__1cNMonitorSupplyHrelease6FpnHMonitor__v_; +text: .text%__1cNMonitorSupplyHreserve6F_pnHMonitor__; +text: .text%__1cUWaitForBarrierGCTaskIwait_for6M_v_; +text: .text%__1cUWaitForBarrierGCTaskIdestruct6M_v_; +text: .text%__1cUWaitForBarrierGCTaskHdestroy6Fp0_v_; +text: .text%__1cUWaitForBarrierGCTask2t6Mi_v_; +text: .text%__1cUWaitForBarrierGCTaskGcreate6F_p0_; +text: .text%__1cNBarrierGCTaskIdestruct6M_v_; +text: .text%__1cNBarrierGCTaskOdo_it_internal6MpnNGCTaskManager_I_v_; +text: .text%__1cNGCTaskManagerVrelease_all_resources6M_v_; +text: .text%__1cNGCTaskManagerIadd_list6MpnLGCTaskQdDueue__v_; +text: .text%__1cLGCTaskQdDueueHenqueue6Mp0_v_; +text: .text%__1cLGCTaskQdDueueGcreate6F_p0_; +text: .text%__1cGGCTaskIdestruct6M_v_; +text: .text%__1cSCardTableExtensionRscavenge_contents6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager__v_; +text: .text%__1cHThreadsZcreate_thread_roots_tasks6FpnLGCTaskQdDueue__v_; +text: .text%__1cKPSYoungGenLswap_spaces6M_v_; +text: .text%__1cUPSAdaptiveSizePolicybPcompute_survivor_space_size_and_threshold6MiiL_i_; +text: .text%__1cUParallelScavengeHeapQresize_young_gen6MLL_v_; +text: .text%__1cUPSAdaptiveSizePolicyPupdate_averages6MiLL_v_; +text: .text%__1cKPSYoungGenRresize_generation6MLL_i_; +text: .text%__1cKPSYoungGenGresize6MLL_v_; +text: .text%__1cKPSYoungGenNresize_spaces6MLL_v_; +text: .text%__1cHMatcherKcan_be_arg6Fi_i_; +text: .text%__1cHMatcherQis_spillable_arg6Fi_i_; +text: .text%__1cUPSAdaptiveSizePolicyOshould_full_GC6ML_i_; +text: .text%__1cSAdaptiveSizePolicybIupdate_minor_pause_young_estimator6Md_v_; +text: .text%__1cUPSAdaptiveSizePolicybGupdate_minor_pause_old_estimator6Md_v_; +text: .text%__1cNsubL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cMStartOSRNodeGOpcode6kM_i_; +text: .text%__1cRsubI_rReg_memNodeErule6kM_I_; +text: .text%__1cQinstanceRefKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cXmembar_acquire_lockNodeHtwo_adr6kM_I_; +text: .text%__1cNandI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cNcmovI_regNodePoper_input_base6kM_I_; +text: .text%__1cMURShiftINodeJideal_reg6kM_I_; +text: .text%__1cMorI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cLRShiftINodeJideal_reg6kM_I_; +text: .text%__1cLklassItableRinitialize_itable6M_v_; +text: .text%__1cLklassVtableQfill_in_mirandas6Mri_v_; +text: .text%__1cRandI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cSmembar_releaseNodePoper_input_base6kM_I_; +text: .text%__1cFKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cJrRegLOperFclone6kM_pnIMachOper__; +text: .text%__1cFStateR_sub_Op_LoadKlass6MpknENode__v_; +text: .text%__1cRmethodDataOopDescJis_mature6kM_i_; +text: .text%__1cJcmpOpOperEless6kM_i_; +text: .text%__1cFKlassWappend_to_sibling_list6M_v_; +text: .text%__1cOcompL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQSystemDictionarySjava_system_loader6F_pnHoopDesc__; +text: .text%__1cNloadKlassNodeFreloc6kM_i_; +text: .text%__1cRshrI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAndINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cTjava_lang_ThrowableNset_backtrace6FpnHoopDesc_2_v_; +text: .text%__1cPcmovI_reg_gNodeMideal_Opcode6kM_i_; +text: .text%__1cIAndINodeGmul_id6kM_pknEType__; +text: .text%__1cTClassLoadingServiceScompute_class_size6FpnNinstanceKlass__L_; +text: .text%__1cLklassVtableQget_num_mirandas6FpnMklassOopDesc_pnPobjArrayOopDesc_4_i_; +text: .text%__1cIVerifierQrelax_verify_for6FpnHoopDesc__i_; +text: .text%__1cLklassVtablebKcompute_vtable_size_and_num_mirandas6Fri1pnMklassOopDesc_pnPobjArrayOopDesc_nLAccessFlags_pnHoopDesc_pnNsymbolOopDesc_5_v_; +text: .text%__1cPClassFileParserbAparse_classfile_attributes6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cRaddI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cVjava_lang_ClassLoaderRis_trusted_loader6FpnHoopDesc__i_; +text: .text%__1cNmethodOopDescMsort_methods6FpnPobjArrayOopDesc_222_v_; +text: .text%__1cQSystemDictionaryQadd_to_hierarchy6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserUcompute_oop_map_size6MnTinstanceKlassHandle_ii_i_; +text: .text%__1cPClassFileParserOparseClassFile6MnMsymbolHandle_nGHandle_2r1pnGThread__nTinstanceKlassHandle__; +text: .text%__1cPClassFileParserVset_precomputed_flags6MnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserNfill_oop_maps6MnTinstanceKlassHandle_ii_v_; +text: .text%__1cPClassFileParserbDcompute_transitive_interfaces6MnTinstanceKlassHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cQSystemDictionaryVdefine_instance_class6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCcheck_super_interface_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserYcheck_super_class_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbBcheck_final_method_override6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cSinstanceKlassKlassXallocate_instance_klass6MiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cPClassFileParserQparse_interfaces6MnSconstantPoolHandle_nGHandle_2pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserTparse_constant_pool6MpnGThread__nSconstantPoolHandle__; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_; +text: .text%__1cTClassLoadingServiceTnotify_class_loaded6FpnNinstanceKlass_i_v_; +text: .text%__1cLklassItableZsetup_itable_offset_table6FnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserMparse_fields6MnSconstantPoolHandle_ipnUFieldAllocationCount_pnOobjArrayHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserNparse_methods6MnSconstantPoolHandle_ipnLAccessFlags_ppnPobjArrayOopDesc_66pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserMsort_methods6MnOobjArrayHandle_111pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserbBparse_constant_pool_entries6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cIUniverseTflush_dependents_on6FnTinstanceKlassHandle__v_; +text: .text%__1cKoopFactoryQnew_constantPool6FipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cRconstantPoolKlassIallocate6MipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cPClassFileStream2t6MpCipc_v_; +text: .text%__1cNinstanceKlassbBdo_local_static_fields_impl6FnTinstanceKlassHandle_pFpnPfieldDescriptor_pnGThread__v5_v_; +text: .text%__1cJCodeCachebKnumber_of_nmethods_with_dependencies6F_i_; +text: .text%__1cNinstanceKlassWdo_local_static_fields6MpFpnPfieldDescriptor_pnGThread__v4_v_; +text: .text%__1cNinstanceKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassSprocess_interfaces6MpnGThread__v_; +text: .text%__1cNinstanceKlassQinit_implementor6M_v_; +text: .text%__1cNinstanceKlassQeager_initialize6MpnGThread__v_; +text: .text%__1cKoopFactoryRnew_instanceKlass6FiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cQSystemDictionaryVresolve_super_or_fail6FnMsymbolHandle_1nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cKTypeRawPtrEmake6FpC_pk0_; +text: .text%__1cScompI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cScompI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cOMethodLiveness2t6MpnFArena_pnIciMethod__v_; +text: .text%__1cOMethodLivenessSpropagate_liveness6M_v_; +text: .text%__1cOMethodLivenessRinit_basic_blocks6M_v_; +text: .text%__1cOMethodLivenessNinit_gen_kill6M_v_; +text: .text%__1cOMethodLivenessQcompute_liveness6M_v_; +text: .text%__1cFKlassRoop_is_methodData6kM_i_; +text: .text%__1cFVTuneQstart_class_load6F_v_; +text: .text%__1cSThreadProfilerMark2t6Mn0AGRegion__v_; +text: .text%__1cLClassLoaderOload_classfile6FnMsymbolHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cFVTuneOend_class_load6F_v_; +text: .text%__1cQSystemDictionaryRload_shared_class6FnTinstanceKlassHandle_nGHandle_pnGThread__1_; +text: .text%__1cQSystemDictionaryRload_shared_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cQSystemDictionaryRfind_shared_class6FnMsymbolHandle__pnMklassOopDesc__; +text: .text%__1cSThreadProfilerMark2T6M_v_; +text: .text%__1cNinstanceKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cSMemBarVolatileNodeGOpcode6kM_i_; +text: .text%__1cRaddL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPClassFileParserbKparse_classfile_sourcefile_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cKciTypeFlowLStateVectorGdo_ldc6MpnQciBytecodeStream__v_; +text: .text%__1cMPhaseIterGVNIoptimize6M_v_; +text: .text%__1cOrFlagsRegUOperFclone6kM_pnIMachOper__; +text: .text%__1cNmulL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cMrdi_RegPOperJnum_edges6kM_I_; +text: .text%__1cRsalI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLklassVtableYadd_new_mirandas_to_list6FpnNGrowableArray4CpnNmethodOopDesc___pnPobjArrayOopDesc_6pnMklassOopDesc__v_; +text: .text%__1cQPackageHashtableMcompute_hash6Mpkci_I_; +text: .text%__1cWconstantPoolCacheKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cRsalL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cIConINodeHget_int6kMpi_i_; +text: .text%__1cJMarkSweepSFollowStackClosureHdo_void6M_v_; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__; +text: .text%__1cLOpaque2NodeGOpcode6kM_i_; +text: .text%__1cOGenerateOopMapJppdupswap6Mipkc_v_; +text: .text%__1cILoopNode2t6MpnENode_2_v_; +text: .text%__1cJloadBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPClassFileStreamGget_u86MpnGThread__X_; +text: .text%__1cKstoreINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTconvI2L_reg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cScompP_mem_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIVerifierRverify_byte_codes6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIRewriterHrewrite6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIRewriterScompute_index_maps6FnSconstantPoolHandle_rpnIintArray_rpnIintStack__v_; +text: .text%__1cIRewriterXnew_constant_pool_cache6FrnIintArray_pnGThread__nXconstantPoolCacheHandle__; +text: .text%__1cIintArray2t6Mii_v_; +text: .text%__1cNinstanceKlassNrewrite_class6MpnGThread__v_; +text: .text%__1cKoopFactoryVnew_constantPoolCache6FipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cNinstanceKlassWadd_loader_constraints6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassLverify_code6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cWconstantPoolCacheKlassIallocate6MipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cYconstantPoolCacheOopDescKinitialize6MrnIintArray__v_; +text: .text%__1cFframeWsender_for_entry_frame6kMpnLRegisterMap__0_; +text: .text%__1cHPhiNodeDcmp6kMrknENode__I_; +text: .text%__1cSmembar_releaseNodeHtwo_adr6kM_I_; +text: .text%__1cSObjectSynchronizerJnotifyall6FnGHandle_pnGThread__v_; +text: .text%__1cKoopFactoryWnew_permanent_intArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cPClassFileParserVparse_exception_table6MIInSconstantPoolHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserbSparse_constant_pool_interfacemethodref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cWCountInterfacesClosureEdoit6MpnMklassOopDesc_i_v_; +text: .text%__1cMtlsLoadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmodI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cNtestL_regNodeMideal_Opcode6kM_i_; +text: .text%__1cRaddI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIConFNodeGOpcode6kM_i_; +text: .text%__1cLOpaque1NodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cTconvI2L_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNSharedRuntimebOraw_exception_handler_for_return_address6FpC_1_; +text: .text%__1cNSharedRuntimebKexception_handler_for_return_address6FpC_1_; +text: .text%__1cOMethodLivenessKBasicBlockPmerge_exception6MnGBitMap__i_; +text: .text%__1cTconvI2L_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAndINodeKmul_opcode6kM_i_; +text: .text%__1cIAndINodeKadd_opcode6kM_i_; +text: .text%__1cPcmovI_reg_gNodeJnum_opnds6kM_I_; +text: .text%__1cKCMoveINodeGOpcode6kM_i_; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_; +text: .text%__1cIRootNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPloadConUL32NodeHsize_of6kM_I_; +text: .text%__1cJAssemblerEandq6MpnMRegisterImpl_i_v_; +text: .text%__1cLClassLoaderOlookup_package6Fpkc_pnLPackageInfo__; +text: .text%__1cQPackageHashtableJget_entry6MiIpkcL_pnLPackageInfo__; +text: .text%__1cIGraphKitRmerge_fast_memory6MpnENode_2i_v_; +text: .text%JVM_Clone; +text: .text%__1cLklassItableTcompute_itable_size6FnOobjArrayHandle__i_; +text: .text%__1cUCallCompiledJavaNodeGOpcode6kM_i_; +text: .text%__1cPsalI_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKadd_n_reqs6FpnENode_1_v_: graphKit.o; +text: .text%__1cSTailCalljmpIndNodeMideal_Opcode6kM_i_; +text: .text%__1cQComputeCallStackJdo_double6M_v_; +text: .text%__1cKciTypeFlowLStateVectorMdo_putstatic6MpnQciBytecodeStream__v_; +text: .text%__1cLClassLoaderLadd_package6Fpkci_i_; +text: .text%__1cIGraphKitHopt_iff6MpnENode_2_2_; +text: .text%__1cIGraphKitOmake_merge_mem6MpnENode_22_v_; +text: .text%__1cGEventsDlog6FpkcE_v_; +text: .text%__1cMLinkResolverbHlookup_instance_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cSsafePoint_pollNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNSharedRuntimebWnative_method_throw_unsatisfied_link_error_entry6F_pC_; +text: .text%__1cPshrI_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFParseWensure_phis_everywhere6M_v_; +text: .text%__1cNsubL_rRegNodeErule6kM_I_; +text: .text%__1cNIdealLoopTreeUiteration_split_impl6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cNIdealLoopTreebBpolicy_do_remove_empty_loop6MpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeOpolicy_peeling6kMpnOPhaseIdealLoop__i_; +text: .text%__1cIBoolNodeZis_counted_loop_exit_test6M_i_; +text: .text%__1cJloadCNodeHtwo_adr6kM_I_; +text: .text%__1cUPSMarkSweepDecoratorVdestination_decorator6F_p0_; +text: .text%__1cTGeneratePairingInfoRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cENode2n6FL_pv_; +text: .text%__1cSvframeStreamCommonZsecurity_get_caller_frame6Mi_v_; +text: .text%__1cKBufferBlob2n6FLI_pv_; +text: .text%__1cFParseKarray_load6MnJBasicType__v_; +text: .text%__1cICodeBlob2t6Mpkcii_v_; +text: .text%__1cKBufferBlob2t6Mpkci_v_; +text: .text%__1cKBufferBlobGcreate6Fpkci_p0_; +text: .text%__1cKciTypeFlowLStateVectorLdo_putfield6MpnQciBytecodeStream__v_; +text: .text%__1cHnmethodNscope_desc_at6MpC_pnJScopeDesc__; +text: .text%__1cHnmethodJcode_size6kM_i_; +text: .text%__1cRtestP_reg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cRtestP_reg_memNodePoper_input_base6kM_I_; +text: .text%__1cOPhaseIdealLoopQset_subtree_ctrl6MpnENode__v_; +text: .text%__1cOjmpLoopEndNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_i_v_; +text: .text%__1cRconstantPoolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNinstanceKlassbBcall_class_initializer_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassRclass_initializer6M_pnNmethodOopDesc__; +text: .text%__1cNinstanceKlassWcall_class_initializer6MpnGThread__v_; +text: .text%__1cNinstanceKlassbOset_initialization_state_and_notify_impl6FnTinstanceKlassHandle_n0AKClassState_pnGThread__v_; +text: .text%__1cNinstanceKlassbJset_initialization_state_and_notify6Mn0AKClassState_pnGThread__v_; +text: .text%__1cNRelocIteratorTlocs_and_index_size6Fii_i_; +text: .text%__1cMrdi_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cTStackWalkCompPolicyYmethod_back_branch_event6MnMmethodHandle_iipnGThread__v_; +text: .text%__1cFTypeDCeq6kMpknEType__i_; +text: .text%__1cJLoadCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNtestL_regNodeHtwo_adr6kM_I_; +text: .text%__1cTconvL2I_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRCompilationPolicybJreset_counter_for_back_branch_event6MnMmethodHandle__v_; +text: .text%__1cMrax_RegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNmodI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cNSignatureInfoIdo_short6M_v_; +text: .text%JVM_GetFieldIxModifiers; +text: .text%__1cNsubL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNandL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cNsubL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cTMachCallRuntimeNodePret_addr_offset6M_i_; +text: .text%__1cOcompiledVFrameEcode6kM_pnHnmethod__; +text: .text%__1cICodeHeapTmark_segmap_as_used6MLL_v_; +text: .text%__1cMorI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAddLNodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cHOrINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%JVM_IsConstructorIx; +text: .text%__1cEDict2t6MpFpkv2_ipF2_ipnFArena_i_v_; +text: .text%__1cHMatcherLfind_shared6MpnENode__v_; +text: .text%__1cHMatcherFxform6MpnENode_i_2_; +text: .text%__1cJStartNodeHsize_of6kM_I_; +text: .text%__1cILRG_List2t6MI_v_; +text: .text%__1cHMatcherLreturn_addr6kM_i_; +text: .text%__1cSindIndexOffsetOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cGBundlePinitialize_nops6FppnIMachNode__v_; +text: .text%__1cOMachPrologNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cHMemNodeHsize_of6kM_I_; +text: .text%__1cNSignatureInfoIdo_float6M_v_; +text: .text%__1cRaddI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRmulI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cFParseNadd_safepoint6M_v_; +text: .text%__1cFStateT_sub_Op_CheckCastPP6MpknENode__v_; +text: .text%__1cRaddI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOCompiledRFrameEinit6M_v_; +text: .text%__1cGvframeDtop6kM_p0_; +text: .text%__1cPsarI_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cRmethodDataOopDescYcompute_extra_data_count6Fii_i_; +text: .text%__1cPcheckCastPPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObjectIis_klass6M_i_; +text: .text%__1cFStateM_sub_Op_SubI6MpknENode__v_; +text: .text%__1cRxorI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNloadConP0NodeHsize_of6kM_I_; +text: .text%__1cJAssemblerEaddq6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerEsubq6MpnMRegisterImpl_2_v_; +text: .text%__1cXJNI_ArgumentPusherVaArgHiterate6ML_v_; +text: .text%__1cTresource_free_bytes6FpcL_v_; +text: .text%__1cNSingletonBlobMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cUPSMarkSweepDecoratorPadjust_pointers6M_v_; +text: .text%__1cUPSMarkSweepDecoratorHcompact6Mi_v_; +text: .text%__1cUPSMarkSweepDecoratorKprecompact6M_v_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeMideal_Opcode6kM_i_; +text: .text%__1cRindIndexScaleOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cRindIndexScaleOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cPCountedLoopNodeHsize_of6kM_I_; +text: .text%__1cENodeHrm_prec6MI_v_; +text: .text%__1cHAddNodeGis_Add6kM_pk0_; +text: .text%__1cHCompilebAvarargs_C_out_slots_killed6kM_I_; +text: .text%__1cTMachCallRuntimeNodeSis_MachCallRuntime6M_p0_; +text: .text%__1cMrax_RegIOperJnum_edges6kM_I_; +text: .text%__1cICodeHeapLmerge_right6MpnJFreeBlock__v_; +text: .text%__1cNaddI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNmulL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cWandI_rReg_imm65535NodeMideal_Opcode6kM_i_; +text: .text%__1cKReturnNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRjmpConU_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLjmpConUNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cRjmpConU_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cRjmpConU_shortNodeJis_Branch6kM_I_; +text: .text%__1cKcmpOpUOperFclone6kM_pnIMachOper__; +text: .text%__1cRtestP_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFMutexbLwait_for_lock_blocking_implementation6MpnKJavaThread__v_; +text: .text%__1cIregDOperJnum_edges6kM_I_; +text: .text%__1cPciInstanceKlassTis_java_lang_Object6M_i_; +text: .text%__1cSciExceptionHandlerLcatch_klass6M_pnPciInstanceKlass__; +text: .text%__1cSindIndexOffsetOperNconstant_disp6kM_i_; +text: .text%__1cIAndLNodeGadd_id6kM_pknEType__; +text: .text%__1cLConvL2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQleaPIdxScaleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIAndLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cRaddP_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMloadConLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHMatcherQpost_fast_unlock6FpknENode__i_; +text: .text%__1cFStateV_sub_Op_MemBarRelease6MpknENode__v_; +text: .text%__1cOleaPIdxOffNodeMideal_Opcode6kM_i_; +text: .text%__1cILoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cScompI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cScompI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerDorq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cScompI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKJNIHandlesOdestroy_global6FpnI_jobject_i_v_; +text: .text%__1cPcmpFastLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXmembar_release_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTcompareAndSwapLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescTset_native_function6MpC_v_; +text: .text%__1cKciTypeFlowLStateVectorJhalf_type6FpnGciType__3_; +text: .text%__1cQmerge_point_safe6FpnENode__i_: loopopts.o; +text: .text%__1cRaddL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cHMatcherUc_calling_convention6FpnLOptoRegPair_I_v_; +text: .text%__1cPCallRuntimeNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cUjni_invoke_nonstatic6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cQSystemDictionaryRnumber_of_classes6F_i_; +text: .text%__1cNaddL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNxorI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cWCallLeafNoFPDirectNodePoper_input_base6kM_I_; +text: .text%__1cENodeHget_int6kMpi_i_; +text: .text%__1cPCountedLoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJLoadFNodeGOpcode6kM_i_; +text: .text%__1cQSystemDictionarybBresolve_array_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNincI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPClassFileParserbEparse_constant_pool_long_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPcmovI_reg_lNodePoper_input_base6kM_I_; +text: .text%__1cJAssemblerEleaq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cNinstanceKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cJloadINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHTypePtrFxmeet6kMpknEType__3_; +text: .text%__1cNprefetchwNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cFKlassTarray_klass_or_null6M_pnMklassOopDesc__; +text: .text%__1cIPhaseIFGYCompute_Effective_Degree6M_v_; +text: .text%__1cbCfind_class_from_class_loader6FpnHJNIEnv__nMsymbolHandle_CnGHandle_3CpnGThread__pnH_jclass__; +text: .text%__1cMPhaseChaitinOcache_lrg_info6M_v_; +text: .text%__1cMPhaseChaitinISimplify6M_v_; +text: .text%__1cMPhaseChaitinGSelect6M_I_; +text: .text%__1cRsarL_rReg_immNodeErule6kM_I_; +text: .text%__1cKcmpOpUOperJnot_equal6kM_i_; +text: .text%__1cScompU_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cScompU_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cRNativeGeneralJumpQjump_destination6kM_pC_; +text: .text%__1cLRethrowNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cWCallLeafNoFPDirectNodeHtwo_adr6kM_I_; +text: .text%__1cNSafepointBlobHoops_do6MpnKOopClosure__v_; +text: .text%__1cSvframeStreamCommonYfill_from_compiled_frame6MpnHnmethod_i_v_; +text: .text%__1cNandL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cHnmethodQis_native_method6kM_i_; +text: .text%__1cTleaPIdxScaleOffNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNmulL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cODataRelocationGoffset6M_i_; +text: .text%__1cODataRelocationJset_value6MpC_v_; +text: .text%__1cKRelocationRpd_set_data_value6MpCl_v_; +text: .text%__1cLOptoRuntimeJstub_name6FpC_pkc_; +text: .text%__1cIMulINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFStateO_sub_Op_StoreB6MpknENode__v_; +text: .text%__1cRaddL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cIregFOperJnum_edges6kM_I_; +text: .text%__1cRandI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIRootNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIRootNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOleaPIdxOffNodePoper_input_base6kM_I_; +text: .text%__1cJcmpOpOperKless_equal6kM_i_; +text: .text%__1cNmethodOopDescVset_signature_handler6MpC_v_; +text: .text%__1cIMulLNodeGmul_id6kM_pknEType__; +text: .text%__1cMrep_stosNodeHtwo_adr6kM_I_; +text: .text%__1cHMemNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cNsubI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cHMemNodeScalculate_adr_type6FpknEType_pknHTypePtr__6_; +text: .text%__1cRmulI_rReg_immNodeErule6kM_I_; +text: .text%__1cURethrowExceptionNodePoper_input_base6kM_I_; +text: .text%__1cNaddP_rRegNodePoper_input_base6kM_I_; +text: .text%__1cFStateP_sub_Op_RShiftI6MpknENode__v_; +text: .text%__1cKstoreLNodeHtwo_adr6kM_I_; +text: .text%__1cNnegI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodePoper_input_base6kM_I_; +text: .text%__1cbFloadConL_0x6666666666666667NodeErule6kM_I_; +text: .text%__1cNSharedRuntimeXfind_callee_info_helper6FpnKJavaThread_rnMvframeStream_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cIGraphKitNstore_barrier6MpnENode_22_v_; +text: .text%__1cNmethodOopDescTverified_code_entry6M_pC_; +text: .text%__1cNloadKlassNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmembar_acquireNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMoutputStreamMdo_vsnprintf6FpcLpkcpnR__va_list_element_irL_3_; +text: .text%__1cMoutputStreamFprint6MpkcE_v_; +text: .text%__1cMURShiftLNodeJideal_reg6kM_I_; +text: .text%__1cZPhaseConservativeCoalesce2t6MrnMPhaseChaitin__v_; +text: .text%__1cMPhaseChaitinFSplit6MI_I_; +text: .text%__1cMPhaseChaitinHcompact6M_v_; +text: .text%__1cMPhaseChaitinZcompress_uf_map_for_nodes6M_v_; +text: .text%__1cZPhaseConservativeCoalesceGverify6M_v_; +text: .text%__1cQComputeCallStackIdo_short6M_v_; +text: .text%__1cNFingerprinterHdo_long6M_v_; +text: .text%__1cIciMethodRinstructions_size6M_i_; +text: .text%__1cSsafePoint_pollNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNloadConL0NodeLbottom_type6kM_pknEType__; +text: .text%__1cJimmL0OperJconstantL6kM_x_; +text: .text%__1cWandI_rReg_imm65535NodePoper_input_base6kM_I_; +text: .text%__1cIAndINodeJideal_reg6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerKverify_oop6MpnMRegisterImpl_nITosState__v_; +text: .text%__1cYexternal_word_RelocationJpack_data6M_i_; +text: .text%__1cJimmP0OperFclone6kM_pnIMachOper__; +text: .text%__1cKRelocationYruntime_address_to_index6FpC_l_; +text: .text%__1cOemit_d32_reloc6FrnKCodeBuffer_inJrelocInfoJrelocType_i_v_; +text: .text%__1cYexternal_word_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cRsalL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cLPhaseValues2T5B6M_v_; +text: .text%__1cNstoreImmBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateQ_sub_Op_URShiftL6MpknENode__v_; +text: .text%__1cJNode_ListEyank6MpnENode__v_; +text: .text%__1cNxorI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNxorI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerEmovq6MpnMRegisterImpl_l_v_; +text: .text%jni_ExceptionCheck: jni.o; +text: .text%__1cMFastLockNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTCallDynamicJavaNodeEhash6kM_I_; +text: .text%__1cMalloc_object6FpnH_jclass_pnGThread__pnPinstanceOopDesc__: jni.o; +text: .text%__1cRshrL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIXorINodeLbottom_type6kM_pknEType__; +text: .text%__1cJAssemblerEsubq6MpnMRegisterImpl_i_v_; +text: .text%__1cNloadConL0NodeMideal_Opcode6kM_i_; +text: .text%__1cLPcDescCacheKpc_desc_at6kMpnHnmethod_pC_pnGPcDesc__; +text: .text%__1cKBlock_ListGinsert6MIpnFBlock__v_; +text: .text%__1cKtype2basic6FpknEType__nJBasicType__; +text: .text%__1cQleaPIdxScaleNodeLbottom_type6kM_pknEType__; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_; +text: .text%__1cIGraphKitOnull_check_oop6MpnKRegionNode_pnENode_i_4_; +text: .text%__1cJloadCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRxorI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cKTypeAryPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cKTypeRawPtrEmake6FnHTypePtrDPTR__pk0_; +text: .text%__1cJCodeCacheEfree6FpnICodeBlob__v_; +text: .text%__1cICodeHeapPadd_to_freelist6MpnJHeapBlock__v_; +text: .text%__1cICodeHeapKdeallocate6Mpv_v_; +text: .text%__1cFframeLnmethods_do6M_v_; +text: .text%__1cJVectorSetGslamin6Mrk0_v_; +text: .text%__1cFStateQ_sub_Op_URShiftI6MpknENode__v_; +text: .text%__1cScompI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRaddI_rReg_memNodeErule6kM_I_; +text: .text%__1cYexternal_word_RelocationWfix_relocation_at_move6Ml_v_; +text: .text%__1cKRelocationYpd_get_address_from_code6M_pC_; +text: .text%__1cRxorI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cXJNI_ArgumentPusherVaArgIget_long6M_v_; +text: .text%__1cNandL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOCompilerOracleMshould_print6FnMmethodHandle__i_; +text: .text%__1cNstoreImmBNodeFreloc6kM_i_; +text: .text%__1cJcmpOpOperNgreater_equal6kM_i_; +text: .text%__1cKBufferBlobEfree6Fp0_v_; +text: .text%__1cZInterpreterMacroAssemblerKverify_FPU6MinITosState__v_; +text: .text%__1cKNativeCallXset_destination_mt_safe6MpC_v_; +text: .text%__1cOGenerateOopMapIppop_any6Mi_v_; +text: .text%__1cKNode_ArrayFclear6M_v_; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc__pc_; +text: .text%__1cJAssemblerFpushq6MpnMRegisterImpl__v_; +text: .text%__1cIRootNodeHis_Root6M_p0_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cIJumpDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cRsalL_rReg_immNodeErule6kM_I_; +text: .text%__1cPstoreImmI16NodeHtwo_adr6kM_I_; +text: .text%__1cQjava_lang_StringOchar_converter6FnGHandle_HHpnGThread__1_; +text: .text%jni_NewObject: jni.o; +text: .text%__1cNaddP_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cPsarI_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cFciEnvKcompile_id6M_I_; +text: .text%__1cOPhaseIdealLoopNreorg_offsets6MpnNIdealLoopTree__v_; +text: .text%__1cNtestL_regNodeErule6kM_I_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_exit_Type6F_pknITypeFunc__; +text: .text%__1cNstoreImmINodeHtwo_adr6kM_I_; +text: .text%__1cIGraphKitNshared_unlock6MpnENode_2_v_; +text: .text%__1cNSafePointNodeLpop_monitor6M_v_; +text: .text%__1cRsarI_rReg_immNodeErule6kM_I_; +text: .text%__1cNtestL_regNodePoper_input_base6kM_I_; +text: .text%__1cRsarL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMindirectOperNbase_position6kM_i_; +text: .text%__1cMindirectOperNconstant_disp6kM_i_; +text: .text%__1cMTailCallNodeKmatch_edge6kMI_I_; +text: .text%__1cKciTypeFlowLStateVectorGdo_new6MpnQciBytecodeStream__v_; +text: .text%__1cHMatcherPprior_fast_lock6FpknENode__i_; +text: .text%__1cGIfNodeMdominated_by6MpnENode_pnMPhaseIterGVN__v_; +text: .text%__1cFStateV_sub_Op_MemBarAcquire6MpknENode__v_; +text: .text%__1cNSharedRuntimeQfind_callee_info6FpnKJavaThread_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cFKlassDLCA6Mp0_1_; +text: .text%__1cRtestP_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRtestP_reg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cRtestP_reg_memNodeJnum_opnds6kM_I_; +text: .text%__1cHciKlassVleast_common_ancestor6Mp0_1_; +text: .text%__1cUmembar_cpu_orderNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUmembar_cpu_orderNodeLbottom_type6kM_pknEType__; +text: .text%__1cTcompareAndSwapLNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cNSCMemProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cTcompareAndSwapLNodeHtwo_adr6kM_I_; +text: .text%__1cJScopeDescGsender6kM_p0_; +text: .text%__1cSindIndexOffsetOperOindex_position6kM_i_; +text: .text%__1cSindIndexOffsetOperNbase_position6kM_i_; +text: .text%__1cNSharedRuntimeOresolve_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cNSafePointNodeMpush_monitor6MpknMFastLockNode__v_; +text: .text%__1cNSharedRuntimeSresolve_sub_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cOcompiledVFrameGsender6kM_pnGvframe__; +text: .text%__1cNtestU_regNodeHtwo_adr6kM_I_; +text: .text%__1cTciConstantPoolCache2t6MpnFArena_i_v_; +text: .text%__1cNGrowableArray4Cpv_2t6MpnFArena_iirk0_v_; +text: .text%__1cKstoreFNodePoper_input_base6kM_I_; +text: .text%__1cNGrowableArray4Cl_2t6MpnFArena_iirkl_v_; +text: .text%__1cNstoreImmINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOMachEpilogNodeFreloc6kM_i_; +text: .text%__1cOMachEpilogNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovI_regNodeMideal_Opcode6kM_i_; +text: .text%__1cKCompiledIC2t6MpnKNativeCall__v_; +text: .text%__1cFStateN_sub_Op_LoadL6MpknENode__v_; +text: .text%__1cNmodI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cSCallLeafDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSignatureInfoHdo_char6M_v_; +text: .text%__1cNtestU_regNodeMideal_Opcode6kM_i_; +text: .text%__1cFStateQ_sub_Op_CallLeaf6MpknENode__v_; +text: .text%__1cRAbstractAssemblerFflush6M_v_; +text: .text%__1cJloadLNodeIpeephole6MpnFBlock_ipnNPhaseRegAlloc_ri_pnIMachNode__; +text: .text%__1cJloadLNodeFreloc6kM_i_; +text: .text%__1cSCallLeafDirectNodeFreloc6kM_i_; +text: .text%__1cIGraphKitNgen_checkcast6MpnENode_2p2_2_; +text: .text%__1cIGraphKitRgen_subtype_check6MpnENode_2_2_; +text: .text%__1cNsubL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXmembar_release_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cJAssemblerEmovq6MpnMRegisterImpl_2_v_; +text: .text%__1cRmulL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cRsubI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cFframeRretrieve_receiver6MpnLRegisterMap__pnHoopDesc__; +text: .text%__1cPBytecode_invokeNstatic_target6MpnGThread__nMmethodHandle__; +text: .text%jni_NewGlobalRef: jni.o; +text: .text%__1cKciTypeFlowFRangeSprivate_copy_count6kMpn0AGJsrSet__i_; +text: .text%__1cOleaPIdxOffNodeJnum_opnds6kM_I_; +text: .text%__1cOPhaseIdealLoopLdo_split_if6MpnENode__v_; +text: .text%__1cNandI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cHOrINodeGadd_id6kM_pknEType__; +text: .text%__1cIPhaseCFGOinsert_goto_at6MII_v_; +text: .text%__1cOPhaseIdealLoop2t6MrnMPhaseIterGVN_pk0i_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_tree6M_v_; +text: .text%__1cRsubI_rReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMinINodeLbottom_type6kM_pknEType__; +text: .text%__1cOjmpLoopEndNodeHtwo_adr6kM_I_; +text: .text%__1cJLoadBNodeJideal_reg6kM_I_; +text: .text%__1cNnegI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cFStateS_sub_Op_FastUnlock6MpknENode__v_; +text: .text%__1cXmembar_release_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRcmpFastUnlockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMVirtualSpaceNreserved_size6kM_L_; +text: .text%__1cScompU_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsarI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKStoreFNodeGOpcode6kM_i_; +text: .text%__1cLCastP2LNodeJideal_reg6kM_I_; +text: .text%__1cPcmovI_reg_gNodeErule6kM_I_; +text: .text%__1cFStateP_sub_Op_CastP2L6MpknENode__v_; +text: .text%__1cScompU_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cScompU_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cScompU_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cWCallLeafNoFPDirectNodeRis_safepoint_node6kM_i_; +text: .text%__1cQjava_lang_ThreadRset_thread_status6FpnHoopDesc_n0AMThreadStatus__v_; +text: .text%__1cWCallLeafNoFPDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerDjcc6Mn0AJCondition_pCnJrelocInfoJrelocType__v_; +text: .text%__1cKstoreFNodeMideal_Opcode6kM_i_; +text: .text%__1cIimmFOperJconstantF6kM_f_; +text: .text%__1cNcmovI_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cKTypeOopPtrSmake_from_constant6FpnIciObject__pk0_; +text: .text%__1cNcmovI_regNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerEmovq6MnHAddress_i_v_; +text: .text%__1cIciObjectJis_method6M_i_; +text: .text%__1cIciObjectOis_method_data6M_i_; +text: .text%__1cIDivINodeLbottom_type6kM_pknEType__; +text: .text%__1cHOrINodeJideal_reg6kM_I_; +text: .text%__1cNcmovI_regNodeMcisc_operand6kM_i_; +text: .text%__1cIAndLNodeGmul_id6kM_pknEType__; +text: .text%__1cIAndLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNinstanceKlassUfind_interface_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cJloadFNodeMideal_Opcode6kM_i_; +text: .text%__1cbFunnecessary_membar_volatileNodeMideal_Opcode6kM_i_; +text: .text%__1cSmembar_acquireNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cIAndLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIAndLNodeKadd_opcode6kM_i_; +text: .text%__1cFStateO_sub_Op_StoreC6MpknENode__v_; +text: .text%__1cIAndLNodeKmul_opcode6kM_i_; +text: .text%__1cRaddL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMrep_stosNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXmembar_acquire_lockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFParseJdo_ifnull6MnIBoolTestEmask__v_; +text: .text%__1cMtlsLoadPNodeHtwo_adr6kM_I_; +text: .text%__1cIGraphKitOset_pair_local6MipnENode__v_; +text: .text%__1cJLoadCNodeJideal_reg6kM_I_; +text: .text%__1cPcmovI_reg_lNodeMideal_Opcode6kM_i_; +text: .text%__1cJCodeCacheXmark_for_deoptimization6FpnMklassOopDesc__i_; +text: .text%__1cMrcx_RegIOperEtype6kM_pknEType__; +text: .text%__1cLConvL2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOPhaseIdealLoopKDominators6M_v_; +text: .text%__1cHNTarjanDDFS6Fp0rnJVectorSet_pnOPhaseIdealLoop_pI_i_; +text: .text%__1cHNTarjanIsetdepth6MIpI_v_; +text: .text%__1cIMulLNodeKmul_opcode6kM_i_; +text: .text%__1cIMulLNodeKadd_opcode6kM_i_; +text: .text%jni_SetLongField: jni.o; +text: .text%__1cOPhaseIdealLoopQbuild_loop_early6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_late6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopRinit_dom_lca_tags6M_v_; +text: .text%__1cKstoreLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLPcDescCacheLadd_pc_desc6MpnGPcDesc__v_; +text: .text%__1cScheck_phi_clipping6FpnHPhiNode_rpnHConNode_rI45rpnENode_5_i_: cfgnode.o; +text: .text%__1cJloadSNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTconvI2L_reg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cRsubI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMloadConFNodeMideal_Opcode6kM_i_; +text: .text%__1cTC2IAdapterGeneratorUgenerate_c2i_adapter6FnMmethodHandle__pnKC2IAdapter__; +text: .text%__1cKCompiledICIis_clean6kM_i_; +text: .text%__1cNaddP_rRegNodeErule6kM_I_; +text: .text%__1cRmulL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cNmethodOopDescVclear_native_function6M_v_; +text: .text%__1cICodeBlobOis_java_method6kM_i_; +text: .text%__1cKVtableStubSpd_code_size_limit6Fi_i_; +text: .text%__1cIUniverseWis_out_of_memory_error6FnGHandle__i_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle_pnGThread__v_; +text: .text%__1cTjava_lang_ThrowableQclear_stacktrace6FpnHoopDesc__v_; +text: .text%__1cKJavaThreadGactive6F_p0_; +text: .text%JVM_FillInStackTrace; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle__v_; +text: .text%__1cSInterpreterRuntimePset_bcp_and_mdp6FpCpnKJavaThread__v_; +text: .text%__1cKJavaThreadNreguard_stack6MpC_i_; +text: .text%__1cFframeZinterpreter_frame_set_bcp6MpC_v_; +text: .text%jni_DeleteGlobalRef: jni.o; +text: .text%__1cKCompiledICZcompute_monomorphic_entry6FnMmethodHandle_nLKlassHandle_iirnOCompiledICInfo_pnGThread__v_; +text: .text%__1cNGrowableArray4nMmethodHandle__Icontains6kMrkn0A__i_; +text: .text%__1cLOpaque2NodeEhash6kM_I_; +text: .text%__1cICodeHeapLfirst_block6kM_pnJHeapBlock__; +text: .text%__1cJCodeCacheFfirst6F_pnICodeBlob__; +text: .text%__1cJBytecodesRspecial_length_at6FpC_i_; +text: .text%__1cFParseGdo_new6M_v_; +text: .text%__1cFParseFBlockMadd_new_path6M_i_; +text: .text%__1cLklassItablebFinitialize_itable_for_interface6MpnMklassOopDesc_pnRitableMethodEntry__v_; +text: .text%__1cJimmI0OperJnum_edges6kM_I_; +text: .text%__1cRmulI_rReg_immNodeMcisc_operand6kM_i_; +text: .text%__1cICodeHeapMmax_capacity6kM_L_; +text: .text%__1cMciMethodDataStrap_recompiled_at6MpnLProfileData__i_; +text: .text%__1cRindIndexScaleOperFscale6kM_i_; +text: .text%__1cNxorI_rRegNodeErule6kM_I_; +text: .text%__1cFParseFBlockNstack_type_at6kMi_pknEType__; +text: .text%__1cLConvL2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFTypeFEmake6Ff_pk0_; +text: .text%__1cIModINodeLbottom_type6kM_pknEType__; +text: .text%__1cJcmpOpOperHgreater6kM_i_; +text: .text%__1cQComputeCallStackHdo_bool6M_v_; +text: .text%__1cJMemRegionMintersection6kM0_0_; +text: .text%__1cNobjArrayKlassIallocate6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cRmulI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHciKlassGloader6M_pnHoopDesc__; +text: .text%__1cIConDNodeGOpcode6kM_i_; +text: .text%__1cNandI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cLRethrowNodeEhash6kM_I_; +text: .text%__1cTC2IAdapterGeneratorSstd_verified_entry6FnMmethodHandle__pC_; +text: .text%__1cIDivLNodeGOpcode6kM_i_; +text: .text%__1cNandI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cGThreadOis_Java_thread6kM_i_; +text: .text%__1cSmembar_releaseNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cHMatcherQinline_cache_reg6F_i_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeJnum_opnds6kM_I_; +text: .text%__1cbBInterpreterCodeletInterfaceRcode_size_to_size6kMi_i_; +text: .text%__1cbBInterpreterCodeletInterfaceKinitialize6MpnEStub_i_v_; +text: .text%jni_NewLocalRef: jni.o; +text: .text%__1cSSetupItableClosureEdoit6MpnMklassOopDesc_i_v_; +text: .text%__1cLOptoRuntimebAresolve_opt_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cPstoreImmI16NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLRShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIemit_d166FrnKCodeBuffer_i_v_; +text: .text%__1cKimmI16OperIconstant6kM_l_; +text: .text%__1cPClassFileParserbNparse_classfile_inner_classes_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__H_; +text: .text%__1cMloadConFNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__; +text: .text%__1cJCodeCacheNalive_nmethod6FpnICodeBlob__pnHnmethod__; +text: .text%__1cJAssemblerGmovzbl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cIVMThreadMis_VM_thread6kM_i_; +text: .text%__1cPcmovI_reg_lNodeJnum_opnds6kM_I_; +text: .text%__1cMloadConLNodeHsize_of6kM_I_; +text: .text%__1cOMacroAssemblerSload_unsigned_byte6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cTconvI2L_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNaddL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cKstoreFNodeJnum_opnds6kM_I_; +text: .text%__1cNaddL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cSComputeAdapterInfoJdo_double6M_v_; +text: .text%__1cLimmUL32OperFclone6kM_pnIMachOper__; +text: .text%__1cPloadConUL32NodeFclone6kM_pnENode__; +text: .text%__1cLLShiftLNodeJideal_reg6kM_I_; +text: .text%__1cMtlsLoadPNodePoper_input_base6kM_I_; +text: .text%__1cPlocal_vsnprintf6FpcLpkcpnR__va_list_element__i_; +text: .text%__1cSComputeAdapterInfoHdo_bool6M_v_; +text: .text%jio_vsnprintf; +text: .text%__1cURethrowExceptionNodeGpinned6kM_i_; +text: .text%__1cNstoreImmINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAndLNodeJideal_reg6kM_I_; +text: .text%__1cURethrowExceptionNodeHtwo_adr6kM_I_; +text: .text%__1cNDispatchTableJset_entry6MirnKEntryPoint__v_; +text: .text%jio_snprintf; +text: .text%__1cNSafePointNodeKgrow_stack6MpnIJVMState_I_v_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeErule6kM_I_; +text: .text%__1cURethrowExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsarL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cTcompareAndSwapLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_GetCPMethodModifiers; +text: .text%__1cFStateR_sub_Op_SafePoint6MpknENode__v_; +text: .text%__1cSsafePoint_pollNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOCompilerOraclePshould_break_at6FnMmethodHandle__i_; +text: .text%__1cJloadCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQorI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cPsarI_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLRShiftLNodeLbottom_type6kM_pknEType__; +text: .text%__1cKReturnNode2t6MpnENode_2222_v_; +text: .text%__1cKReturnNodeJideal_reg6kM_I_; +text: .text%__1cNinstanceKlassPadd_implementor6MpnMklassOopDesc__v_; +text: .text%__1cRPrivilegedElementKinitialize6MpnMvframeStream_pnHoopDesc_p0pnGThread__v_; +text: .text%JVM_DoPrivileged; +text: .text%__1cOGenerateOopMapXreplace_all_CTS_matches6MnNCellTypeState_1_v_; +text: .text%__1cNIdealLoopTreeMis_loop_exit6kMpnENode_pnOPhaseIdealLoop__2_; +text: .text%__1cPpoll_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cSsafePoint_pollNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSsafePoint_pollNodeFreloc6kM_i_; +text: .text%__1cLStrCompNodeGOpcode6kM_i_; +text: .text%__1cJOopMapSet2t6M_v_; +text: .text%__1cKloadUBNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cNobjArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cKstoreCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeGOpcode6kM_i_; +text: .text%__1cOLibraryCallKitNtry_to_inline6M_i_; +text: .text%__1cNFingerprinterHdo_bool6M_v_; +text: .text%__1cOPhaseIdealLoopUsplit_if_with_blocks6MrnJVectorSet_rnKNode_Stack__v_; +text: .text%__1cNmethodOopDescbDbuild_interpreter_method_data6FnMmethodHandle_pnGThread__v_; +text: .text%__1cQLibraryIntrinsicIgenerate6MpnIJVMState__2_; +text: .text%__1cLOopRecorderIoop_size6M_i_; +text: .text%__1cHnmethodOexception_size6kM_i_; +text: .text%__1cHnmethodPscopes_pcs_size6kM_i_; +text: .text%__1cYDebugInformationRecorderIpcs_size6M_i_; +text: .text%__1cYDebugInformationRecorderJdata_size6M_i_; +text: .text%__1cHnmethodQscopes_data_size6kM_i_; +text: .text%__1cHnmethodJstub_size6kM_i_; +text: .text%__1cHnmethodKtotal_size6kM_i_; +text: .text%__1cNtestU_regNodeErule6kM_I_; +text: .text%__1cJOopMapSetJheap_size6kM_i_; +text: .text%__1cICodeBlobWfix_relocation_at_move6Ml_v_; +text: .text%__1cKCodeBufferJcopy_code6MpnICodeBlob__v_; +text: .text%__1cNRelocIteratorMcreate_index6FpnKCodeBuffer_pnJrelocInfo_4_4_; +text: .text%__1cICodeBlobPallocation_size6FpnKCodeBuffer_ii_I_; +text: .text%__1cRAbstractAssemblerOcode_fill_byte6F_i_; +text: .text%__1cICodeBlob2t6MpkcpnKCodeBuffer_iiipnJOopMapSet_i_v_; +text: .text%__1cMrdx_RegLOperEtype6kM_pknEType__; +text: .text%__1cKCodeBufferPcopy_relocation6MpnICodeBlob__v_; +text: .text%__1cVPatchingRelocIteratorHprepass6M_v_; +text: .text%__1cVPatchingRelocIteratorIpostpass6M_v_; +text: .text%__1cJOopMapSetHcopy_to6MpC_v_; +text: .text%__1cLPhaseValues2t6Mp0_v_; +text: .text%__1cINodeHash2t6Mp0_v_; +text: .text%__1cOPhaseTransform2t6Mp0nFPhaseLPhaseNumber__v_; +text: .text%__1cJAssemblerDjmp6MnHAddress__v_; +text: .text%__1cOJNIHandleBlockRrebuild_free_list6M_v_; +text: .text%__1cSstring_compareNodeZcheck_for_anti_dependence6kM_i_; +text: .text%jni_GetObjectArrayElement: jni.o; +text: .text%__1cKCompiledICSset_to_monomorphic6MrknOCompiledICInfo__v_; +text: .text%__1cIDivINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cVCallRuntimeDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cICmpDNodeGOpcode6kM_i_; +text: .text%__1cPcmovI_reg_gNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_AndI6MpknENode__v_; +text: .text%__1cHCompilebBregister_library_intrinsics6M_v_; +text: .text%__1cNGrowableArray4CpnNCallGenerator__2t6Mii_v_; +text: .text%__1cETypeKInitialize6FpnHCompile__v_; +text: .text%__1cYDebugInformationRecorder2t6MpnLOopRecorder__v_; +text: .text%__1cOCompileWrapper2t6MpnHCompile__v_; +text: .text%__1cHCompileEInit6Mi_v_; +text: .text%__1cVExceptionHandlerTable2t6Mi_v_; +text: .text%__1cFDictIFreset6MpknEDict__v_; +text: .text%__1cUDebugInfoWriteStream2t6MpnYDebugInformationRecorder_i_v_; +text: .text%JVM_FindLoadedClass; +text: .text%__1cHMatcherZnumber_of_saved_registers6F_i_; +text: .text%__1cXPhaseAggressiveCoalesceNinsert_copies6MrnHMatcher__v_; +text: .text%__1cIPhaseCFGDDFS6MpnGTarjan__I_; +text: .text%__1cFArenaNmove_contents6Mp0_1_; +text: .text%__1cIPhaseIFG2t6MpnFArena__v_; +text: .text%__1cHMatcherUvalidate_null_checks6M_v_; +text: .text%__1cHCompileOcompute_old_SP6M_i_; +text: .text%__1cJPhaseLive2t6MrknIPhaseCFG_rnILRG_List_pnFArena__v_; +text: .text%__1cMPhaseChaitinRbuild_ifg_virtual6M_v_; +text: .text%__1cXPhaseAggressiveCoalesceGverify6M_v_; +text: .text%__1cJStartNodeJideal_reg6kM_I_; +text: .text%__1cHMatcher2t6MrnJNode_List__v_; +text: .text%__1cFArena2t6ML_v_; +text: .text%__1cIPhaseCFGOschedule_early6MrnJVectorSet_rnJNode_List_rnLBlock_Array__i_; +text: .text%__1cWNode_Backward_Iterator2t6MpnENode_rnJVectorSet_rnJNode_List_rnLBlock_Array__v_; +text: .text%__1cHMatcherFmatch6M_v_; +text: .text%__1cFStateM_sub_Op_Goto6MpknENode__v_; +text: .text%__1cIPhaseCFGNschedule_late6MrnJVectorSet_rnJNode_List_rnNGrowableArray4CI___v_; +text: .text%__1cIPhaseCFGQFind_Inner_Loops6M_v_; +text: .text%__1cIPhaseCFGQGlobalCodeMotion6MrnHMatcher_IrnJNode_List__v_; +text: .text%__1cIPhaseCFGYEstimate_Block_Frequency6M_v_; +text: .text%__1cIPhaseCFGJbuild_cfg6M_I_; +text: .text%__1cHCompileICode_Gen6M_v_; +text: .text%__1cMPhaseChaitin2t6MIrnIPhaseCFG_rnHMatcher__v_; +text: .text%__1cMPhaseChaitinRRegister_Allocate6M_v_; +text: .text%__1cMPhaseChaitinGde_ssa6M_v_; +text: .text%__1cMPhaseChaitinbGstretch_base_pointer_live_ranges6MpnMResourceArea__i_; +text: .text%__1cNPhaseRegAllocTpd_preallocate_hook6M_v_; +text: .text%__1cHMatcherPinit_spill_mask6MpnENode__v_; +text: .text%__1cHMatcherTFixup_Save_On_Entry6M_v_; +text: .text%__1cHMatcherVinit_first_stack_mask6M_v_; +text: .text%__1cIPhaseCFG2t6MpnFArena_pnIRootNode_rnHMatcher__v_; +text: .text%__1cGTarjanIsetdepth6MI_v_; +text: .text%__1cIPhaseCFGKDominators6M_v_; +text: .text%__1cNPhaseRegAlloc2t6MIrnIPhaseCFG_rnHMatcher_pF_v_v_; +text: .text%__1cIPhaseCFGVschedule_pinned_nodes6MrnJVectorSet__v_; +text: .text%__1cHCompileTframe_size_in_words6kM_i_; +text: .text%__1cOCompileWrapper2T6M_v_; +text: .text%__1cHCompileYinit_scratch_buffer_blob6M_v_; +text: .text%__1cHCompileYinit_scratch_locs_memory6M_v_; +text: .text%__1cNPhasePeephole2t6MpnNPhaseRegAlloc_rnIPhaseCFG__v_; +text: .text%__1cJPhaseLive2T6M_v_; +text: .text%__1cNPhasePeephole2T6M_v_; +text: .text%__1cHCompileGOutput6M_v_; +text: .text%__1cHCompileQShorten_branches6MpnFLabel_ri333_v_; +text: .text%__1cHCompileLFill_buffer6M_v_; +text: .text%__1cHCompileTFillExceptionTables6MIpI1pnFLabel__v_; +text: .text%__1cHCompileRScheduleAndBundle6M_v_; +text: .text%__1cOMachPrologNodeFreloc6kM_i_; +text: .text%__1cNtestU_regNodePoper_input_base6kM_I_; +text: .text%__1cWemit_exception_handler6FrnKCodeBuffer__v_; +text: .text%__1cWsize_exception_handler6F_I_; +text: .text%__1cWImplicitExceptionTableIset_size6MI_v_; +text: .text%__1cNPhasePeepholeMdo_transform6M_v_; +text: .text%__1cMPhaseChaitinMfixup_spills6M_v_; +text: .text%__1cMPhaseChaitin2T6M_v_; +text: .text%__1cNPhaseRegAllocPalloc_node_regs6Mi_v_; +text: .text%__1cKCodeBufferOrelocate_stubs6M_v_; +text: .text%__1cIPhaseCFGLRemoveEmpty6M_v_; +text: .text%__1cLdo_liveness6FpnNPhaseRegAlloc_pnIPhaseCFG_pnKBlock_List_ipnFArena_pnEDict__v_: buildOopMap.o; +text: .text%__1cHCompileMBuildOopMaps6M_v_; +text: .text%__1cMPhaseChaitinbApost_allocate_copy_removal6M_v_; +text: .text%__1cNGrowableArray4CpnJNode_List__2t6Mii_v_; +text: .text%__1cRsarL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cOGenerateOopMapIcopy_cts6MpnNCellTypeState_2_i_; +text: .text%__1cFStateM_sub_Op_CmpL6MpknENode__v_; +text: .text%__1cJloadSNodeFreloc6kM_i_; +text: .text%__1cFStateN_sub_Op_LoadS6MpknENode__v_; +text: .text%__1cSInterpreterRuntimeOprofile_method6FpnKJavaThread_pC_i_; +text: .text%__1cOCompiledRFrame2t6MnFframe_pnKJavaThread_pnGRFrame__v_; +text: .text%__1cKC2IAdapterOis_c2i_adapter6kM_i_; +text: .text%__1cOCompiledRFrameKtop_method6kM_nMmethodHandle__; +text: .text%__1cOCompiledRFrameLis_compiled6kM_i_; +text: .text%__1cRmethodDataOopDescKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cKoopFactoryOnew_methodData6FnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_bytes6FpnNmethodOopDesc__i_; +text: .text%__1cPmethodDataKlassIallocate6MnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cNxorI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_words6FpnNmethodOopDesc__i_; +text: .text%__1cRmethodDataOopDescPpost_initialize6MpnOBytecodeStream__v_; +text: .text%__1cHRetNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateO_sub_Op_Return6MpknENode__v_; +text: .text%__1cHRetNodeFreloc6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_base6MnITosState_ppCi_v_; +text: .text%__1cZCallInterpreterDirectNodeHtwo_adr6kM_I_; +text: .text%__1cNloadConP0NodeFclone6kM_pnENode__; +text: .text%__1cOClearArrayNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKScopeValueJread_from6FpnTDebugInfoReadStream__p0_; +text: .text%__1cOcompiledVFrameScreate_stack_value6kMpnKScopeValue__pnKStackValue__; +text: .text%__1cQleaPIdxScaleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRindIndexScaleOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cIGraphKitNallocate_heap6MpnENode_222pknITypeFunc_pC22ipknKTypeOopPtr__2_; +text: .text%__1cPciInstanceKlassbBcompute_shared_has_subklass6M_i_; +text: .text%__1cNSignatureInfoHdo_byte6M_v_; +text: .text%__1cQorI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cKCompiledICSset_ic_destination6MpC_v_; +text: .text%__1cNIdealLoopTreePiteration_split6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cNandI_rRegNodeErule6kM_I_; +text: .text%__1cRsarI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cIMulINodeGadd_id6kM_pknEType__; +text: .text%__1cVcompiledICHolderKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNmodI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cKloadUBNodeMideal_Opcode6kM_i_; +text: .text%__1cHBitDataKis_BitData6M_i_; +text: .text%__1cQsalI_rReg_CLNodeMideal_Opcode6kM_i_; +text: .text%__1cNaddP_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerEcmpq6MnHAddress_i_v_; +text: .text%__1cNloadConP0NodeFreloc6kM_i_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cSmembar_acquireNodeLbottom_type6kM_pknEType__; +text: .text%__1cOMacroAssemblerKincrementq6MpnMRegisterImpl_i_v_; +text: .text%__1cRsarI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_next6MnITosState_i_v_; +text: .text%__1cNGrowableArray4nMmethodHandle__2t6Mii_v_; +text: .text%__1cLConvL2INodeJideal_reg6kM_I_; +text: .text%__1cNGrowableArray4nLKlassHandle__2t6Mii_v_; +text: .text%__1cNmethodOopDescThas_native_function6kM_i_; +text: .text%JVM_GetClassNameUTF; +text: .text%__1cMPrefetchNodeJideal_reg6kM_I_; +text: .text%__1cKCodeBuffer2t6MpCi_v_; +text: .text%__1cNprefetchwNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateQ_sub_Op_Prefetch6MpknENode__v_; +text: .text%__1cOjmpLoopEndNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNprefetchwNodeFreloc6kM_i_; +text: .text%__1cIAddLNodeJideal_reg6kM_I_; +text: .text%__1cILocation2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cKstoreCNodeFreloc6kM_i_; +text: .text%__1cNdecI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNmethodOopDescWis_vanilla_constructor6kM_i_; +text: .text%__1cWCallLeafNoFPDirectNodeKmethod_set6Ml_v_; +text: .text%__1cOPhaseIdealLoopOplace_near_use6kMpnENode__2_; +text: .text%__1cHi2bNodeMideal_Opcode6kM_i_; +text: .text%__1cNLocationValueLis_location6kM_i_; +text: .text%__1cNLocationValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cIMulLNodeJideal_reg6kM_I_; +text: .text%__1cNsubL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cFStateN_sub_Op_LoadB6MpknENode__v_; +text: .text%__1cNnegI_rRegNodeErule6kM_I_; +text: .text%__1cNFingerprinterJdo_double6M_v_; +text: .text%JVM_FindClassFromClass; +text: .text%__1cKcmpOpUOperEless6kM_i_; +text: .text%__1cVcompiledICHolderKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cVcompiledICHolderKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cKReflectionGinvoke6FnTinstanceKlassHandle_nMmethodHandle_nGHandle_inOobjArrayHandle_nJBasicType_4ipnGThread__pnHoopDesc__; +text: .text%__1cITypeLongFwiden6kMpknEType__3_; +text: .text%__1cQsalI_rReg_CLNodePoper_input_base6kM_I_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectIoverride6FpnHoopDesc__C_; +text: .text%__1cKReflectionDbox6FpnGjvalue_nJBasicType_pnGThread__pnHoopDesc__; +text: .text%__1cMLinkResolverbHlinktime_resolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cLBoxLockNodeEhash6kM_I_; +text: .text%__1cJOopMapSetMgrow_om_data6M_v_; +text: .text%__1cRxorI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cKciTypeFlowFBlockQset_private_copy6Mi_v_; +text: .text%__1cWandI_rReg_imm65535NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWandI_rReg_imm65535NodeErule6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerGpush_i6MpnMRegisterImpl__v_; +text: .text%__1cNcmovI_regNodeErule6kM_I_; +text: .text%__1cRsalL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNGrowableArray4CpnKInlineTree__Egrow6Mi_v_; +text: .text%__1cSComputeAdapterInfoIdo_short6M_v_; +text: .text%__1cNtestL_regNodeJnum_opnds6kM_I_; +text: .text%__1cLConvF2DNodeGOpcode6kM_i_; +text: .text%__1cISubLNodeLbottom_type6kM_pknEType__; +text: .text%__1cSmembar_acquireNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmembar_acquireNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNaddP_rRegNodeLbottom_type6kM_pknEType__; +text: .text%__1cNmodL_rRegNodeErule6kM_I_; +text: .text%__1cRsalI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerDret6Mi_v_; +text: .text%__1cRshrI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbDjava_lang_reflect_ConstructorPparameter_types6FpnHoopDesc__2_; +text: .text%__1cKReflectionSinvoke_constructor6FpnHoopDesc_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cbDjava_lang_reflect_ConstructorFclazz6FpnHoopDesc__2_; +text: .text%JVM_NewInstanceFromConstructor; +text: .text%__1cbDjava_lang_reflect_ConstructorEslot6FpnHoopDesc__i_; +text: .text%__1cFStateL_sub_Op_OrI6MpknENode__v_; +text: .text%JVM_IHashCode; +text: .text%__1cMtlsLoadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cEUTF8Ounicode_length6Fpkc_i_; +text: .text%__1cHTypePtrEmake6FnETypeFTYPES_n0ADPTR_i_pk0_; +text: .text%__1cPfieldDescriptorUstring_initial_value6kMpnGThread__pnHoopDesc__; +text: .text%__1cNxorI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cIregFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLMachUEPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLOpaque2NodeLbottom_type6kM_pknEType__; +text: .text%__1cNtestL_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadFNodePoper_input_base6kM_I_; +text: .text%__1cHRetDataKcell_count6M_i_; +text: .text%__1cSinstanceKlassKlassOklass_oop_size6kM_i_; +text: .text%__1cSobjArrayKlassKlassOklass_oop_size6kM_i_; +text: .text%__1cPCallRuntimeNodeGOpcode6kM_i_; +text: .text%__1cLsymbolKlassOklass_oop_size6kM_i_; +text: .text%__1cLmethodKlassOklass_oop_size6kM_i_; +text: .text%__1cMdecI_memNodePoper_input_base6kM_I_; +text: .text%__1cRconstantPoolKlassOklass_oop_size6kM_i_; +text: .text%__1cVcompiledICHolderKlassOklass_oop_size6kM_i_; +text: .text%__1cTtypeArrayKlassKlassOklass_oop_size6kM_i_; +text: .text%__1cWconstantPoolCacheKlassOklass_oop_size6kM_i_; +text: .text%__1cQconstMethodKlassOklass_oop_size6kM_i_; +text: .text%__1cPmethodDataKlassOklass_oop_size6kM_i_; +text: .text%__1cGThreadOis_interrupted6Fp0i_i_; +text: .text%__1cHThreadsHoops_do6FpnKOopClosure__v_; +text: .text%__1cJHashtableHoops_do6MpnKOopClosure__v_; +text: .text%__1cSsafePoint_pollNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIPSOldGenHcompact6M_v_; +text: .text%__1cSsafePoint_pollNodeJnum_opnds6kM_I_; +text: .text%__1cFJNIidHoops_do6MpnKOopClosure__v_; +text: .text%__1cJvmSymbolsHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cJHashtableGunlink6MpnRBoolObjectClosure__v_; +text: .text%__1cSReferenceProcessorHoops_do6MpnKOopClosure__v_; +text: .text%__1cQObjectStartArrayFreset6M_v_; +text: .text%__1cIPSOldGenPadjust_pointers6M_v_; +text: .text%__1cScompP_mem_rRegNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJloadBNodeFreloc6kM_i_; +text: .text%__1cUandI_rReg_imm255NodeMideal_Opcode6kM_i_; +text: .text%__1cNGrowableArray4CpnKciTypeFlowFBlock__Icontains6kMrk2_i_; +text: .text%__1cScompP_mem_rRegNodeFreloc6kM_i_; +text: .text%__1cNcmovP_regNodePoper_input_base6kM_I_; +text: .text%__1cTno_rax_rdx_RegIOperJnum_edges6kM_I_; +text: .text%__1cKciTypeFlowLStateVectorJdo_aaload6MpnQciBytecodeStream__v_; +text: .text%__1cJAssemblerMemit_operand6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerMemit_operand6MpnRFloatRegisterImpl_pnMRegisterImpl_4nHAddressLScaleFactor_ipCrknQRelocationHolder__v_; +text: .text%__1cNaddL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLRethrowNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRaddI_rReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRsubI_rReg_memNodeHtwo_adr6kM_I_; +text: .text%__1cIModLNodeGOpcode6kM_i_; +text: .text%__1cIMaxINodeLbottom_type6kM_pknEType__; +text: .text%__1cFParseMdo_checkcast6M_v_; +text: .text%__1cIMulINodeGmul_id6kM_pknEType__; +text: .text%__1cMloadConINodeGis_Con6kM_I_; +text: .text%__1cIregDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIMulDNodeGOpcode6kM_i_; +text: .text%__1cRsarL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNsubL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cHnmethodUnumber_of_dependents6kM_i_; +text: .text%__1cTconvI2L_reg_memNodeFreloc6kM_i_; +text: .text%__1cSComputeAdapterInfoIdo_float6M_v_; +text: .text%__1cFParseLarray_store6MnJBasicType__v_; +text: .text%__1cOmangle_name_on6FpnMoutputStream_pnNsymbolOopDesc_ii_v_: nativeLookup.o; +text: .text%JVM_FindClassFromClassLoader; +text: .text%__1cZCallInterpreterDirectNodeSalignment_required6kM_i_; +text: .text%__1cZCallInterpreterDirectNodePoper_input_base6kM_I_; +text: .text%__1cZCallInterpreterDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRmulL_rReg_immNodeMcisc_operand6kM_i_; +text: .text%__1cNloadConI0NodeGis_Con6kM_I_; +text: .text%__1cKstoreBNodeHtwo_adr6kM_I_; +text: .text%__1cOmangle_name_on6FpnMoutputStream_pnNsymbolOopDesc__v_: nativeLookup.o; +text: .text%__1cKRegionNodeUdepends_only_on_test6kM_i_; +text: .text%__1cMMergeMemNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cFciEnvZcall_has_multiple_targets6FpnNinstanceKlass_nMsymbolHandle_3ri_i_; +text: .text%__1cMadjust_check6FpnENode_11iipnMPhaseIterGVN__v_: ifnode.o; +text: .text%__1cPsalI_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTconvI2L_reg_memNodeHtwo_adr6kM_I_; +text: .text%__1cLPhaseValuesKis_IterGVN6M_pnMPhaseIterGVN__; +text: .text%__1cQciTypeArrayKlassJmake_impl6FnJBasicType__p0_; +text: .text%__1cFStateM_sub_Op_AddL6MpknENode__v_; +text: .text%__1cQciTypeArrayKlassEmake6FnJBasicType__p0_; +text: .text%__1cUmembar_cpu_orderNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUmembar_cpu_orderNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSCMemProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSCompareAndSwapNodeJideal_reg6kM_I_; +text: .text%__1cFStateW_sub_Op_MemBarCPUOrder6MpknENode__v_; +text: .text%__1cKciTypeFlowLStateVectorMdo_checkcast6MpnQciBytecodeStream__v_; +text: .text%__1cMorI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMrax_RegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%lwp_mutex_init: os_solaris.o; +text: .text%__1cJStubQdDueueGcommit6Mi_v_; +text: .text%__1cJStubQdDueueHrequest6Mi_pnEStub__; +text: .text%__1cNmodL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cQjava_lang_ThreadRget_thread_status6FpnHoopDesc__n0AMThreadStatus__; +text: .text%__1cMincI_memNodePoper_input_base6kM_I_; +text: .text%__1cOGenerateOopMapOdo_monitorexit6Mi_v_; +text: .text%__1cOGenerateOopMapLmonitor_pop6M_nNCellTypeState__; +text: .text%__1cJAssemblerEmovl6MnHAddress_i_v_; +text: .text%__1cKoopFactoryNnew_charArray6FpkcpnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cPshrI_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cQjava_lang_StringTcreate_oop_from_str6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cRmulI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cNandI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cbACallCompiledJavaDirectNodeHtwo_adr6kM_I_; +text: .text%__1cIModINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnHoopDesc_pnNsymbolOopDesc__i_; +text: .text%__1cbLtransform_int_divide_to_long_multiply6FpnIPhaseGVN_pnENode_i_3_: divnode.o; +text: .text%__1cTno_rax_rdx_RegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cJAssemblerGmovzwl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cRmulL_rReg_immNodeErule6kM_I_; +text: .text%__1cZCallDynamicJavaDirectNodePoper_input_base6kM_I_; +text: .text%__1cHTypePtrFempty6kM_i_; +text: .text%__1cOMacroAssemblerSload_unsigned_word6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cOGenerateOopMapXdo_return_monitor_check6M_v_; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cFStateP_sub_Op_ConvL2I6MpknENode__v_; +text: .text%__1cLOptoRuntimebBcomplete_monitor_enter_Type6F_pknITypeFunc__; +text: .text%__1cIGraphKitMnext_monitor6M_i_; +text: .text%__1cLBoxLockNode2t6Mi_v_; +text: .text%__1cRmulI_rReg_immNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cJloadFNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cIplus_adr6FpnENode_l_1_: generateOptoStub.o; +text: .text%__1cIGraphKitLshared_lock6MpnENode__pnMFastLockNode__; +text: .text%__1cHConNode2t6MpknEType__v_; +text: .text%__1cMloadConDNodeMideal_Opcode6kM_i_; +text: .text%__1cNCompileBrokerTcreate_compile_task6FpnMCompileQdDueue_inMmethodHandle_i3ipkcii_pnLCompileTask__; +text: .text%__1cLCompileTaskKinitialize6MinMmethodHandle_i1ipkcii_v_; +text: .text%__1cNCompileBrokerNallocate_task6F_pnLCompileTask__; +text: .text%__1cMCompileQdDueueDadd6MpnLCompileTask__v_; +text: .text%__1cRxorI_rReg_memNodeErule6kM_I_; +text: .text%__1cMCompileQdDueueDget6M_pnLCompileTask__; +text: .text%__1cRsarI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQleaPIdxScaleNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cSCompileTaskWrapper2t6MpnLCompileTask__v_; +text: .text%__1cQjava_lang_ThreadGthread6FpnHoopDesc__pnKJavaThread__; +text: .text%__1cXmembar_acquire_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_MulL6MpknENode__v_; +text: .text%__1cCosPhint_no_preempt6F_v_; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_i_v_; +text: .text%__1cObox_handleNodePoper_input_base6kM_I_; +text: .text%__1cNCompileBrokerJfree_task6FpnLCompileTask__v_; +text: .text%__1cSCompileTaskWrapper2T6M_v_; +text: .text%__1cLCompileTaskEfree6M_v_; +text: .text%__1cNnegI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKDictionarybAis_valid_protection_domain6MiInMsymbolHandle_nGHandle_2_i_; +text: .text%__1cMincI_memNodeMideal_Opcode6kM_i_; +text: .text%__1cRandL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cRaddI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cQjava_lang_StringXcreate_oop_from_unicode6FpHipnGThread__pnHoopDesc__; +text: .text%jni_NewString: jni.o; +text: .text%__1cRxorI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cFStateM_sub_Op_AndL6MpknENode__v_; +text: .text%__1cKloadUBNodePoper_input_base6kM_I_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVcompiledICHolderKlassXoop_is_compiledICHolder6kM_i_; +text: .text%__1cJStoreNodeUdepends_only_on_test6kM_i_; +text: .text%__1cPcmovI_reg_lNodeErule6kM_I_; +text: .text%__1cOloadConL32NodePoper_input_base6kM_I_; +text: .text%__1cNSharedRuntimebJcontinuation_for_implicit_exception6FpnKJavaThread_pCn0AVImplicitExceptionKind__3_; +text: .text%__1cRtestI_reg_immNodeHtwo_adr6kM_I_; +text: .text%__1cIimmDOperJconstantD6kM_d_; +text: .text%__1cFParsePmerge_exception6Mi_v_; +text: .text%__1cXmembar_acquire_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cNGrowableArray4CpnIciObject__2t6MpnFArena_iirk1_v_; +text: .text%__1cICallInfoDset6MnLKlassHandle_1nMmethodHandle_2pnGThread__v_; +text: .text%__1cZCallDynamicJavaDirectNodeHtwo_adr6kM_I_; +text: .text%__1cMLinkResolverbGruntime_resolve_interface_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cMLinkResolverWresolve_interface_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cNGrowableArray4CpnIciObject__JappendAll6Mpk2_v_; +text: .text%__1cFciEnv2t6MpnHJNIEnv__iii_v_; +text: .text%__1cRtestP_reg_memNodeFreloc6kM_i_; +text: .text%__1cNtestP_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNGrowableArray4CpnIciMethod__2t6MpnFArena_iirk1_v_; +text: .text%__1cNGrowableArray4CpnHciKlass__2t6MpnFArena_iirk1_v_; +text: .text%__1cPciObjectFactory2t6MpnFArena_i_v_; +text: .text%__1cNGrowableArray4CpnPciReturnAddress__2t6MpnFArena_iirk1_v_; +text: .text%__1cFciEnvWget_method_from_handle6MpnI_jobject__pnIciMethod__; +text: .text%__1cNCompileBrokerVpush_jni_handle_block6F_v_; +text: .text%__1cIJVMStateNmonitor_depth6kM_i_; +text: .text%__1cHciKlassSsuper_check_offset6M_I_; +text: .text%__1cFciEnv2T6M_v_; +text: .text%__1cKstoreBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNCompileBrokerUpop_jni_handle_block6F_v_; +text: .text%__1cbBopt_virtual_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cNinstanceKlassbFlookup_method_in_all_interfaces6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cPcmpFastLockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateQ_sub_Op_FastLock6MpknENode__v_; +text: .text%__1cXmembar_acquire_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvD2I_reg_regNodeErule6kM_I_; +text: .text%__1cITemplateIbytecode6kM_nJBytecodesECode__; +text: .text%__1cLOptoRuntimeRmultianewarray1_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cWImplicitExceptionTableCat6kMI_I_; +text: .text%__1cWImplicitExceptionTable2t6MpknHnmethod__v_; +text: .text%__1cLVtableStubsPstub_containing6FpC_pnKVtableStub__; +text: .text%__1cLVtableStubsIcontains6FpC_i_; +text: .text%__1cNFingerprinterIdo_float6M_v_; +text: .text%__1cHnmethodbJcontinuation_for_implicit_exception6MpC_1_; +text: .text%__1cLRShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cUjmpLoopEnd_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNmodI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cUjmpLoopEnd_shortNodeMideal_Opcode6kM_i_; +text: .text%__1cKEntryPoint2t6MpC11111111_v_; +text: .text%jni_GetObjectClass: jni.o; +text: .text%__1cRappend_interfaces6FnOobjArrayHandle_ripnPobjArrayOopDesc__v_; +text: .text%__1cRandI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cISubLNodeDsub6kMpknEType_3_3_; +text: .text%__1cJloadSNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRtestI_reg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cOloadConL32NodeHtwo_adr6kM_I_; +text: .text%__1cQshrI_rReg_CLNodePoper_input_base6kM_I_; +text: .text%__1cSstring_compareNodePoper_input_base6kM_I_; +text: .text%__1cNcmovI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMdecI_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMrax_RegLOperEtype6kM_pknEType__; +text: .text%__1cRmulI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cIXorINodeGadd_id6kM_pknEType__; +text: .text%__1cNtestP_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmovI_reg_gNodeHtwo_adr6kM_I_; +text: .text%__1cOPhaseIdealLoopKclone_loop6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cHi2bNodePoper_input_base6kM_I_; +text: .text%__1cRsalL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cKBinaryNodeGOpcode6kM_i_; +text: .text%__1cNxorI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cFStateO_sub_Op_Binary6MpknENode__v_; +text: .text%JVM_GetClassLoader; +text: .text%__1cMstoreSSPNodeMideal_Opcode6kM_i_; +text: .text%__1cNmulL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRxorI_rReg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cINodeHashIround_up6FI_I_; +text: .text%__1cHCompileKinit_start6MpnJStartNode__v_; +text: .text%__1cOPhaseTransform2t6MpnFArena_nFPhaseLPhaseNumber__v_; +text: .text%__1cLPhaseValues2t6MpnFArena_I_v_; +text: .text%__1cRaddP_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cINodeHash2t6MpnFArena_I_v_; +text: .text%__1cRaddI_rReg_memNodeHtwo_adr6kM_I_; +text: .text%__1cIJVMState2n6FL_pv_; +text: .text%__1cOMacroAssemblerFalign6Mi_v_; +text: .text%__1cOleaPIdxOffNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cFVTuneNregister_stub6FpkcpC3_v_; +text: .text%__1cFForteNregister_stub6FpkcpC3_v_; +text: .text%__1cMdecI_memNodeJnum_opnds6kM_I_; +text: .text%__1cIModINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMLinkResolverXresolve_invokeinterface6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%lwp_cond_init: os_solaris.o; +text: .text%__1cSmembar_releaseNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmembar_releaseNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOPhaseIdealLoopVclone_up_backedge_goo6MpnENode_22_2_; +text: .text%__1cSInterpreterCodeletKinitialize6MpkcnJBytecodesECode__v_; +text: .text%__1cTconvI2L_reg_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNxorI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNaddP_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerZcheck_and_handle_popframe6MpnMRegisterImpl__v_; +text: .text%__1cOloadConL32NodeErule6kM_I_; +text: .text%__1cOMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cFframeVnmethods_code_blob_do6M_v_; +text: .text%__1cHi2bNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKcmpOpUOperKless_equal6kM_i_; +text: .text%__1cWandI_rReg_imm65535NodeJnum_opnds6kM_I_; +text: .text%__1cFParseTprofile_switch_case6Mi_v_; +text: .text%__1cKNativeJumpbEcheck_verified_entry_alignment6FpC1_v_; +text: .text%__1cFParseSjump_switch_ranges6MpnENode_pnLSwitchRange_4i_v_; +text: .text%__1cFParseOmerge_new_path6Mi_v_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_; +text: .text%__1cNandI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cNmodL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cMloadConLNodeFclone6kM_pnENode__; +text: .text%__1cNtestU_regNodeJnum_opnds6kM_I_; +text: .text%__1cIimmLOperFclone6kM_pnIMachOper__; +text: .text%__1cRandL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cOleaPIdxOffNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_pkcp4_i_: arguments.o; +text: .text%__1cNCompileBrokerTis_not_compile_only6FnMmethodHandle__i_; +text: .text%__1cNCompileBrokerRassign_compile_id6FnMmethodHandle_i_I_; +text: .text%__1cNCompileBrokerTis_compile_blocking6FnMmethodHandle_i_i_; +text: .text%__1cIMulFNodeGOpcode6kM_i_; +text: .text%__1cNIdealLoopTreeQpolicy_peel_only6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeSpolicy_range_check6kMpnOPhaseIdealLoop__i_; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cNIdealLoopTreeMpolicy_align6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeNpolicy_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNtestU_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCompileBrokerZcompilation_is_prohibited6FnMmethodHandle_i_i_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFi_vi_v_; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_and_dispatch6MpnITemplate_nITosState__v_; +text: .text%__1cITemplateIgenerate6MpnZInterpreterMacroAssembler__v_; +text: .text%__1cKC2CompilerOneeds_adapters6M_i_; +text: .text%__1cLServiceUtilLvisible_oop6FpnHoopDesc__i_; +text: .text%__1cITemplateKinitialize6MinITosState_1pFi_vi_v_; +text: .text%__1cNObjectMonitorGEnterI6MpnGThread__v_; +text: .text%__1cIciMethodJhas_loops6kM_i_; +text: .text%__1cIciMethodVshould_print_assembly6M_i_; +text: .text%__1cOMacroAssemblerOcall_VM_helper6MpnMRegisterImpl_pCii_v_; +text: .text%__1cNloadConL0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cMincI_memNodeJnum_opnds6kM_I_; +text: .text%__1cNCompileBrokerOcheck_break_at6FnMmethodHandle_iii_i_; +text: .text%__1cJAssemblerEcall6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cNCompileBrokerQset_last_compile6FpnOCompilerThread_nMmethodHandle_ii_v_; +text: .text%__1cNCompileBrokerbAeager_compile_c2i_adapters6FpnFciEnv_pnIciMethod__v_; +text: .text%__1cNCompileBrokerbAeager_compile_i2c_adapters6FpnFciEnv_pnIciMethod__v_; +text: .text%__1cNCompileBrokerZinvoke_compiler_on_method6FpnLCompileTask__v_; +text: .text%__1cKC2CompilerOcompile_method6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cMstoreSSPNodeHis_Copy6kM_I_; +text: .text%__1cQshrI_rReg_CLNodeMideal_Opcode6kM_i_; +text: .text%__1cFciEnvPregister_method6MpnIciMethod_iiiiiipnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnQAbstractCompiler_ii_v_; +text: .text%__1cLAccessFlagsRatomic_clear_bits6Mi_v_; +text: .text%__1cIciMethodQbreak_at_execute6M_i_; +text: .text%__1cFciEnvbOcheck_for_system_dictionary_modification6MpnIciMethod__v_; +text: .text%__1cFciEnvbUsystem_dictionary_modification_counter_changed6M_i_; +text: .text%__1cMelapsedTimerDadd6M0_v_; +text: .text%__1cNCompileBrokerScollect_statistics6FpnOCompilerThread_nMelapsedTimer_pnLCompileTask__v_; +text: .text%__1cJStartNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cICodeHeapMinsert_after6MpnJFreeBlock_2_v_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_3pnRJavaCallArguments_nGHandle_6_6_; +text: .text%__1cFMutex2t6Mipkci_v_; +text: .text%__1cKloadUBNodeErule6kM_I_; +text: .text%__1cQsalL_rReg_CLNodeMideal_Opcode6kM_i_; +text: .text%__1cbACallCompiledJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbACallCompiledJavaDirectNodePoper_input_base6kM_I_; +text: .text%__1cTbasictype2arraycopy6FnJBasicType_i_pC_; +text: .text%__1cOLibraryCallKitQinline_arraycopy6M_i_; +text: .text%__1cPstoreImmI16NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLOptoRuntimeOarraycopy_Type6F_pknITypeFunc__; +text: .text%__1cFciEnvbFpost_compiled_method_load_event6MpnHnmethod__v_; +text: .text%__1cJCodeCacheGcommit6FpnICodeBlob__v_; +text: .text%__1cLPcDescCache2t6M_v_; +text: .text%__1cFciEnvVnum_inlined_bytecodes6kM_i_; +text: .text%__1cHnmethodSresolve_JNIHandles6M_v_; +text: .text%__1cRmulL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVExceptionHandlerTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cWImplicitExceptionTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cFVTuneOcreate_nmethod6FpnHnmethod__v_; +text: .text%__1cYDebugInformationRecorderHcopy_to6MpnHnmethod__v_; +text: .text%__1cHnmethodLnew_nmethod6FnMmethodHandle_iiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnQAbstractCompiler__p0_; +text: .text%__1cPcmovI_reg_lNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHnmFlagsFclear6M_v_; +text: .text%__1cHnmethod2n6FLi_pv_; +text: .text%__1cHnmethod2t6MpnNmethodOopDesc_iiiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnQAbstractCompiler__v_; +text: .text%__1cNaddI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cKTypeRawPtrFxdual6kM_pknEType__; +text: .text%__1cHnmethodQcopy_scopes_data6MpCi_v_; +text: .text%__1cLOopRecorderHcopy_to6MpnICodeBlob__v_; +text: .text%__1cICodeBlobJcopy_oops6MppnI_jobject_i_v_; +text: .text%__1cFStateN_sub_Op_LoadC6MpknENode__v_; +text: .text%__1cJloadCNodeFreloc6kM_i_; +text: .text%__1cFParseQjump_if_fork_int6MpnENode_2nIBoolTestEmask__pnGIfNode__; +text: .text%__1cWandI_rReg_imm65535NodeHtwo_adr6kM_I_; +text: .text%__1cNdivL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cNmethodOopDescIset_code6MpnHnmethod__v_; +text: .text%__1cINodeHashUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cQUnique_Node_ListUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cKInlineTreeWbuild_inline_tree_root6F_p0_; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod_ii_v_; +text: .text%__1cHCompileWprint_compile_messages6M_v_; +text: .text%__1cPClassFileParserbGparse_constant_pool_double_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cQsalI_rReg_CLNodeErule6kM_I_; +text: .text%__1cHCompileRbuild_start_state6MpnJStartNode_pknITypeFunc__pnIJVMState__; +text: .text%__1cHCompileVidentify_useful_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cIciMethodRbuild_method_data6M_v_; +text: .text%__1cHCompileIOptimize6M_v_; +text: .text%__1cHCompileUremove_useless_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cSPhaseRemoveUseless2t6MpnIPhaseGVN_pnQUnique_Node_List__v_; +text: .text%__1cHCompileLFinish_Warm6M_v_; +text: .text%__1cHCompileLInline_Warm6M_i_; +text: .text%__1cPno_rax_RegLOperJnum_edges6kM_I_; +text: .text%__1cMPhaseIterGVN2t6MpnIPhaseGVN__v_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii1_v_; +text: .text%__1cIciMethodRbuild_method_data6MnMmethodHandle__v_; +text: .text%__1cSstring_compareNodeErule6kM_I_; +text: .text%__1cbAfinal_graph_reshaping_walk6FrnKNode_Stack_pnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cHCompileVfinal_graph_reshaping6M_i_; +text: .text%__1cOcompI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cScompI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cJStartNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTableKtransition6FnITosState_1_v_; +text: .text%__1cIPhaseCCPJtransform6MpnENode__2_; +text: .text%__1cHCompileNreturn_values6MpnIJVMState__v_; +text: .text%__1cbCAbstractInterpreterGeneratorQset_entry_points6MnJBytecodesECode__v_; +text: .text%__1cbCAbstractInterpreterGeneratorWset_short_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cMPhaseIterGVN2t6Mp0_v_; +text: .text%__1cIPhaseCCP2t6MpnMPhaseIterGVN__v_; +text: .text%__1cIPhaseCCP2T6M_v_; +text: .text%__1cIPhaseCCPHanalyze6M_v_; +text: .text%__1cIPhaseCCPMdo_transform6M_v_; +text: .text%__1cOcompI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNsubL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConPcNodeMideal_Opcode6kM_i_; +text: .text%__1cKExceptionsG_throw6FpnGThread_pkcinGHandle__v_; +text: .text%__1cKExceptionsRspecial_exception6FpnGThread_pkcinGHandle__i_; +text: .text%__1cGThreadVset_pending_exception6MpnHoopDesc_pkci_v_; +text: .text%__1cNandL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLklassItableUcompute_itable_index6FpnNmethodOopDesc__i_; +text: .text%__1cWpoll_return_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cWConstantPoolCacheEntrySset_interface_call6MnMmethodHandle_i_v_; +text: .text%__1cJloadFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPClassFileParserUverify_constantvalue6MiinSconstantPoolHandle_pnGThread__v_; +text: .text%__1cQsalI_rReg_CLNodeJnum_opnds6kM_I_; +text: .text%__1cIMachNodeJis_MachIf6kM_pknKMachIfNode__; +text: .text%__1cPsalL_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cKExceptionsRspecial_exception6FpnGThread_pkcinMsymbolHandle_4_i_; +text: .text%__1cQjava_lang_StringScreate_from_symbol6FnMsymbolHandle_pnGThread__nGHandle__; +text: .text%__1cOClearArrayNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHMatcherbDinterpreter_frame_pointer_reg6F_i_; +text: .text%__1cQorI_rReg_immNodeErule6kM_I_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_53pnGThread__v_; +text: .text%__1cSInterpreterRuntimeXthrow_pending_exception6FpnKJavaThread__v_; +text: .text%__1cKcmpOpUOperHgreater6kM_i_; +text: .text%__1cNCompileBrokerUcheck_adapter_result6FnMmethodHandle_ippnMBasicAdapter__i_; +text: .text%__1cJloadFNodeJnum_opnds6kM_I_; +text: .text%__1cYinternal_word_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cSMachC2IEntriesNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cbFunnecessary_membar_volatileNodePoper_input_base6kM_I_; +text: .text%__1cRmulI_rReg_immNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cQPSIsAliveClosureLdo_object_b6MpnHoopDesc__i_; +text: .text%__1cTCallInterpreterNodeSis_CallInterpreter6kM_pk0_; +text: .text%__1cZCallInterpreterDirectNodePcompute_padding6kMi_i_; +text: .text%__1cSMachC2IcheckICNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_epilog6MnITosState_i_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_prolog6MnITosState_i_v_; +text: .text%__1cZCallInterpreterDirectNodeKmethod_set6Ml_v_; +text: .text%__1cXMachCallInterpreterNodePret_addr_offset6M_i_; +text: .text%__1cQjava_lang_StringPcreate_from_str6FpkcpnGThread__nGHandle__; +text: .text%__1cLOptoRuntimeInew_Type6F_pknITypeFunc__; +text: .text%__1cLBoxLockNodeDcmp6kMrknENode__I_; +text: .text%__1cMTailCallNodeGOpcode6kM_i_; +text: .text%__1cJChunkPoolMfree_all_but6ML_v_; +text: .text%__1cIGraphKitMnew_instance6MpnPciInstanceKlass__pnENode__; +text: .text%__1cPcmpD_cc_regNodePoper_input_base6kM_I_; +text: .text%__1cRsalL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cLOptoRuntimeSnew_typeArray_Type6F_pknITypeFunc__; +text: .text%__1cObox_handleNodeMideal_Opcode6kM_i_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cIGraphKitJnew_array6MpnENode_nJBasicType_pknEType_pknMTypeKlassPtr__2_; +text: .text%__1cNdecL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cKJavaThreadZsecurity_get_caller_class6Mi_pnMklassOopDesc__; +text: .text%__1cZCallDynamicJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOemit_d64_reloc6FrnKCodeBuffer_lrknQRelocationHolder_i_v_; +text: .text%__1cRtestI_reg_immNodeErule6kM_I_; +text: .text%__1cIAddFNodeGOpcode6kM_i_; +text: .text%__1cSstring_compareNodeMideal_Opcode6kM_i_; +text: .text%__1cNmethodOopDescWcompute_has_loops_flag6M_i_; +text: .text%__1cKloadUBNodeJnum_opnds6kM_I_; +text: .text%__1cNGrowableArray4CpnHoopDesc__2t6Mii_v_; +text: .text%__1cZCallDynamicJavaDirectNodeSalignment_required6kM_i_; +text: .text%__1cXvirtual_call_RelocationJpack_data6M_i_; +text: .text%__1cXvirtual_call_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cQorI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUParallelScavengeHeapIcapacity6kM_L_; +text: .text%__1cJAssemblerEcmpq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cNnegI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cYinternal_word_RelocationJpack_data6M_i_; +text: .text%__1cKsplit_once6FpnMPhaseIterGVN_pnENode_333_v_: cfgnode.o; +text: .text%__1cQSystemDictionarybAvalidate_protection_domain6FnTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cPDictionaryEntryVadd_protection_domain6MpnHoopDesc__v_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectMset_override6FpnHoopDesc_C_v_; +text: .text%__1cRCardTableModRefBSPclear_MemRegion6MnJMemRegion__v_; +text: .text%__1cOleaPIdxOffNodeLbottom_type6kM_pknEType__; +text: .text%__1cNdivL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cFParsebLincrement_and_test_invocation_counter6Mi_v_; +text: .text%__1cKDictionaryVadd_protection_domain6MiInTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%JVM_IsInterrupted; +text: .text%__1cFParseRarray_store_check6M_v_; +text: .text%__1cNobjArrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cScompL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cTmembar_volatileNodeMideal_Opcode6kM_i_; +text: .text%__1cTCallDynamicJavaNodeSis_CallDynamicJava6kM_pk0_; +text: .text%__1cCosHSolarisFEventEpark6M_v_; +text: .text%__1cIMinINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cODeoptimizationYtrap_state_is_recompiled6Fi_i_; +text: .text%__1cXSignatureHandlerLibraryKinitialize6F_v_; +text: .text%__1cXSignatureHandlerLibraryDadd6FnMmethodHandle__v_; +text: .text%__1cSInterpreterRuntimeTprepare_native_call6FpnKJavaThread_pnNmethodOopDesc__v_; +text: .text%__1cNGrowableArray4CL_Efind6kMrkL_i_; +text: .text%__1cUandI_rReg_imm255NodePoper_input_base6kM_I_; +text: .text%__1cSReferenceProcessorZadd_to_discovered_list_mt6MppnHoopDesc_23_v_; +text: .text%__1cSInterpreterRuntimeNquicken_io_cc6FpnKJavaThread__v_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii_v_; +text: .text%__1cHOrINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cNObjectMonitorbAEntryQdDueue_SelectSuccessor6M_pnMObjectWaiter__; +text: .text%__1cNObjectMonitorREntryQdDueue_insert6MpnMObjectWaiter_i_v_; +text: .text%__1cSobjArrayKlassKlassXallocate_objArray_klass6MinLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cJAssemblerEpopq6MpnMRegisterImpl__v_; +text: .text%__1cSobjArrayKlassKlassbCallocate_objArray_klass_impl6FnYobjArrayKlassKlassHandle_inLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cISubLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMTypeKlassPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cNloadConI0NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSComputeAdapterInfoHdo_char6M_v_; +text: .text%__1cKPerfMemoryFalloc6FL_pc_; +text: .text%__1cIPerfData2T6M_v_; +text: .text%__1cIPerfDataMcreate_entry6MnJBasicType_LL_v_; +text: .text%__1cKPerfMemoryMmark_updated6F_v_; +text: .text%__1cPcmovI_reg_gNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEfrom6F_pnMRegisterImpl__; +text: .text%__1cXJNI_ArgumentPusherVaArgHget_int6M_v_; +text: .text%__1cIPerfData2t6MnJCounterNS_pkcn0AFUnits_n0ALVariability__v_; +text: .text%__1cPPerfDataManagerIadd_item6FpnIPerfData_i_v_; +text: .text%__1cLStrCompNodeKmatch_edge6kMI_I_; +text: .text%__1cOjmpLoopEndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOjmpLoopEndNodeOis_pc_relative6kM_i_; +text: .text%__1cOjmpLoopEndNodeTmay_be_short_branch6kM_i_; +text: .text%jni_ReleaseStringUTFChars: jni.o; +text: .text%jni_GetStringUTFChars: jni.o; +text: .text%__1cFStateW_sub_Op_CountedLoopEnd6MpknENode__v_; +text: .text%__1cNFingerprinterIdo_short6M_v_; +text: .text%__1cOcompU_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cJAssemblerEincq6MpnMRegisterImpl__v_; +text: .text%__1cFTypeDEmake6Fd_pk0_; +text: .text%__1cScompU_rReg_memNodeFreloc6kM_i_; +text: .text%__1cNtestL_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFParseLdo_newarray6MnJBasicType__v_; +text: .text%__1cWCallLeafNoFPDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMNativeLookupGlookup6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cWCallLeafNoFPDirectNodeFreloc6kM_i_; +text: .text%__1cSstring_compareNodeJnum_opnds6kM_I_; +text: .text%__1cFStateU_sub_Op_CallLeafNoFP6MpknENode__v_; +text: .text%JVM_FindLibraryEntry; +text: .text%__1cSObjectSynchronizerHinflate6FpnHoopDesc__pnNObjectMonitor__; +text: .text%JVM_GetMethodIxExceptionTableEntry; +text: .text%__1cNObjectMonitorHRecycle6M_v_; +text: .text%__1cISubLNodeGadd_id6kM_pknEType__; +text: .text%__1cNmodI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHMatcherOc_return_value6Fii_nLOptoRegPair__; +text: .text%__1cRxorI_rReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMachNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQsarL_rReg_63NodeMideal_Opcode6kM_i_; +text: .text%__1cRmulI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMachOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cIMachOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cZInterpreterMacroAssemblerDpop6MnITosState__v_; +text: .text%__1cNandI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cIDivINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNnegI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cFStateS_sub_Op_ClearArray6MpknENode__v_; +text: .text%__1cRaddL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFciEnvWis_dependence_violated6FpnMklassOopDesc_pnNmethodOopDesc__i_; +text: .text%__1cHCompile2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cIXorINodeJideal_reg6kM_I_; +text: .text%__1cMrep_stosNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRtestI_reg_immNodePoper_input_base6kM_I_; +text: .text%__1cKC2CompilerPcompile_adapter6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cMrep_stosNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMAdapterCacheGinsert6MpnLAdapterInfo_pnMBasicAdapter__v_; +text: .text%__1cFStateO_sub_Op_StoreL6MpknENode__v_; +text: .text%__1cLAdapterInfoHcopy_to6Mp0_v_; +text: .text%__1cQjava_lang_StringFvalue6FpnHoopDesc__pnQtypeArrayOopDesc__; +text: .text%__1cPciObjArrayKlassGloader6M_pnHoopDesc__; +text: .text%__1cIMinINodeGadd_id6kM_pknEType__; +text: .text%__1cNdecL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cQjava_lang_StringGoffset6FpnHoopDesc__i_; +text: .text%__1cKstoreLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGThreadLnmethods_do6M_v_; +text: .text%__1cKmul_hiNodeMideal_Opcode6kM_i_; +text: .text%__1cKstoreLNodeFreloc6kM_i_; +text: .text%__1cMstoreSSPNodeLbottom_type6kM_pknEType__; +text: .text%__1cRsubI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cPsarL_rReg_2NodeMideal_Opcode6kM_i_; +text: .text%__1cTconvF2D_reg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cRmulL_rReg_immNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNmodL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cRmulL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeHtwo_adr6kM_I_; +text: .text%__1cSCompiledStaticCallSset_to_interpreted6MnMmethodHandle_pC_v_; +text: .text%__1cScompU_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRInterpreterOopMapIis_empty6M_i_; +text: .text%__1cNFingerprinterHdo_char6M_v_; +text: .text%__1cOrepush_if_args6FpnFParse_pnENode_3_v_: parse2.o; +text: .text%__1cMloadConDNodeLbottom_type6kM_pknEType__; +text: .text%__1cNGrowableArray4CpnHoopDesc__Uclear_and_deallocate6M_v_; +text: .text%__1cMrdx_RegLOperJnum_edges6kM_I_; +text: .text%__1cLMachUEPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZget_mirror_from_signature6FnMmethodHandle_pnPSignatureStream_pnGThread__pnHoopDesc__; +text: .text%__1cNGrowableArray4CpnJNode_List__Egrow6Mi_v_; +text: .text%__1cMciArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cNObjectMonitorGenter26MpnGThread__v_; +text: .text%__1cKarrayKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cIUniverseWupdate_heap_info_at_gc6F_v_; +text: .text%__1cJCodeCacheLgc_prologue6F_v_; +text: .text%__1cJCodeCacheLgc_epilogue6F_v_; +text: .text%__1cVLoaderConstraintTableYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cJMarkSweepXfollow_weak_klass_links6F_v_; +text: .text%__1cJMarkSweepMadjust_marks6F_v_; +text: .text%__1cJMarkSweepNrestore_marks6F_v_; +text: .text%__1cHThreadsLgc_epilogue6F_v_; +text: .text%__1cKDictionaryMdo_unloading6MpnRBoolObjectClosure__i_; +text: .text%__1cHThreadsLgc_prologue6F_v_; +text: .text%__1cNGrowableArray4CpnFKlass__2t6Mii_v_; +text: .text%__1cQSystemDictionaryMdo_unloading6FpnRBoolObjectClosure__i_; +text: .text%__1cQSystemDictionaryPplaceholders_do6FpnKOopClosure__v_; +text: .text%__1cSReferenceProcessorPoops_do_statics6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryYalways_strong_classes_do6FpnKOopClosure__v_; +text: .text%__1cUPSAdaptiveSizePolicyWmajor_collection_begin6M_v_; +text: .text%__1cUPSAdaptiveSizePolicyUmajor_collection_end6MLnHGCCauseFCause__v_; +text: .text%__1cQSystemDictionaryValways_strong_oops_do6FpnKOopClosure__v_; +text: .text%__1cNGrowableArray4CpnFKlass__Uclear_and_deallocate6M_v_; +text: .text%__1cKPSYoungGenHcompact6M_v_; +text: .text%__1cKPSYoungGenPadjust_pointers6M_v_; +text: .text%__1cKPSYoungGenKprecompact6M_v_; +text: .text%__1cLPSMarkSweepQinvoke_no_policy6Fpii_v_; +text: .text%__1cLPSMarkSweepPallocate_stacks6F_v_; +text: .text%__1cLPSMarkSweepRdeallocate_stacks6F_v_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase16Fi_v_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase26F_v_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase36F_v_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase46F_v_; +text: .text%__1cLPSMarkSweepbAreset_millis_since_last_gc6F_v_; +text: .text%__1cUPSMarkSweepDecoratorbHset_destination_decorator_tenured6F_v_; +text: .text%__1cUPSMarkSweepDecoratorbIset_destination_decorator_perm_gen6F_v_; +text: .text%__1cNExceptionBlobHoops_do6MpnKOopClosure__v_; +text: .text%__1cKDictionaryYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cQUncommonTrapBlobHoops_do6MpnKOopClosure__v_; +text: .text%__1cSDeoptimizationBlobHoops_do6MpnKOopClosure__v_; +text: .text%__1cIPSOldGenKprecompact6M_v_; +text: .text%__1cVLoaderConstraintTableYpurge_loader_constraints6MpnRBoolObjectClosure__v_; +text: .text%__1cRCardTableModRefBSEis_a6MnKBarrierSetEName__i_; +text: .text%__1cJPSPermGenQcompute_new_size6ML_v_; +text: .text%__1cJPSPermGenKprecompact6M_v_; +text: .text%__1cJCodeCacheMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cJCodeCacheHoops_do6FpnKOopClosure__v_; +text: .text%__1cPcmpD_cc_regNodeHtwo_adr6kM_I_; +text: .text%__1cbFunnecessary_membar_volatileNodeHtwo_adr6kM_I_; +text: .text%__1cIDivINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIciSymbolHas_utf86M_pkc_; +text: .text%__1cQorI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cPfieldDescriptorSlong_initial_value6kM_x_; +text: .text%__1cKJavaThreadLnmethods_do6M_v_; +text: .text%__1cCosTnative_java_library6F_pv_; +text: .text%__1cSTailCalljmpIndNodePoper_input_base6kM_I_; +text: .text%__1cOstackSlotPOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOstackSlotPOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cScompL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cNandI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2ipnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cPciObjArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cPstoreImmI16NodeFreloc6kM_i_; +text: .text%__1cPstoreImmI16NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosYprint_jni_name_prefix_on6FpnMoutputStream_i_v_; +text: .text%__1cVlookup_special_native6Fpc_pC_: nativeLookup.o; +text: .text%jni_ReleaseStringCritical: jni.o; +text: .text%__1cMNativeLookupMlookup_style6FnMmethodHandle_pcpkciiripnGThread__pC_; +text: .text%__1cCosYprint_jni_name_suffix_on6FpnMoutputStream_i_v_; +text: .text%jni_GetStringCritical: jni.o; +text: .text%__1cSInterpreterRuntimeTnmethod_entry_point6FpnKJavaThread_pnNmethodOopDesc_pnHnmethod__pC_; +text: .text%__1cIPSOldGenOgen_size_limit6M_L_; +text: .text%__1cTI2CAdapterGeneratorSstd_verified_entry6FnMmethodHandle__pC_; +text: .text%__1cTI2CAdapterGeneratorUgenerate_i2c_adapter6FnMmethodHandle__pnKI2CAdapter__; +text: .text%__1cUPSAdaptiveSizePolicybQpromo_increment_with_supplement_aligned_up6ML_L_; +text: .text%__1cHnmethodXinterpreter_entry_point6M_pC_; +text: .text%__1cUParallelScavengeHeapOresize_old_gen6ML_v_; +text: .text%__1cUPSAdaptiveSizePolicyPpromo_increment6MLI_L_; +text: .text%__1cWandI_rReg_imm65535NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIPSOldGenGresize6ML_v_; +text: .text%__1cKConv2BNodeGOpcode6kM_i_; +text: .text%__1cObox_handleNodeHtwo_adr6kM_I_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_; +text: .text%__1cKarrayKlassXbase_create_array_klass6FrknKKlass_vtbl_inLKlassHandle_pnGThread__nQarrayKlassHandle__; +text: .text%__1cNstoreImmINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKarrayKlassbBcomplete_create_array_klass6FnQarrayKlassHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cLConvI2FNodeGOpcode6kM_i_; +text: .text%__1cNmethodOopDescVparameter_annotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cNmethodOopDescLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cNcmovI_regNodeHtwo_adr6kM_I_; +text: .text%__1cNmethodOopDescbGresolved_checked_exceptions_impl6Fp0pnGThread__nOobjArrayHandle__; +text: .text%__1cIGraphKitbKcombine_and_pop_all_exception_states6M_pnNSafePointNode__; +text: .text%__1cLRethrowNode2t6MpnENode_22222_v_; +text: .text%__1cHCompileSrethrow_exceptions6MpnIJVMState__v_; +text: .text%__1cKReflectionTget_parameter_types6FnMmethodHandle_ippnHoopDesc_pnGThread__nOobjArrayHandle__; +text: .text%__1cKReflectionTget_exception_types6FnMmethodHandle_pnGThread__nOobjArrayHandle__; +text: .text%__1cNinstanceKlassQmethod_index_for6kMpnNmethodOopDesc_pnGThread__i_; +text: .text%__1cJAssemblerEandl6MpnMRegisterImpl_i_v_; +text: .text%__1cNmethodOopDescKklass_name6kM_pnNsymbolOopDesc__; +text: .text%__1cFStateP_sub_Op_Rethrow6MpknENode__v_; +text: .text%__1cQComputeCallStackIdo_array6Mii_v_; +text: .text%__1cQsalL_rReg_CLNodePoper_input_base6kM_I_; +text: .text%__1cNdecL_rRegNodeErule6kM_I_; +text: .text%__1cLRethrowNodeJideal_reg6kM_I_; +text: .text%__1cMNativeLookupLlookup_base6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cNstoreImmINodeFreloc6kM_i_; +text: .text%__1cURethrowExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMNativeLookupNpure_jni_name6FnMmethodHandle__pc_; +text: .text%__1cIPerfLong2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability__v_; +text: .text%__1cURethrowExceptionNodeFreloc6kM_i_; +text: .text%__1cTCompareAndSwapLNode2t6MpnENode_2222_v_; +text: .text%__1cQshrI_rReg_CLNodeJnum_opnds6kM_I_; +text: .text%__1cSCompareAndSwapNode2t6MpnENode_2222_v_; +text: .text%__1cTcompareAndSwapLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTcompareAndSwapLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOLibraryCallKitRinline_unsafe_CAS6MnJBasicType__i_; +text: .text%__1cIProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateX_sub_Op_CompareAndSwapL6MpknENode__v_; +text: .text%__1cNSCMemProjNodeJideal_reg6kM_I_; +text: .text%__1cTcompareAndSwapLNodeFreloc6kM_i_; +text: .text%__1cOGenerateOopMapMmonitor_push6MnNCellTypeState__v_; +text: .text%__1cOcompP_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPsarI_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOGenerateOopMapPdo_monitorenter6Mi_v_; +text: .text%__1cMmulD_immNodeMideal_Opcode6kM_i_; +text: .text%__1cPmethodDataKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cMaddF_regNodePoper_input_base6kM_I_; +text: .text%__1cPcmpD_cc_regNodeMideal_Opcode6kM_i_; +text: .text%__1cJLoadPNodeMstore_Opcode6kM_i_; +text: .text%__1cbACallCompiledJavaDirectNodeKmethod_set6Ml_v_; +text: .text%__1cYMachCallCompiledJavaNodePret_addr_offset6M_i_; +text: .text%__1cJCMoveNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKciTypeFlowLStateVectorEtrap6MpnQciBytecodeStream_pnHciKlass_i_v_; +text: .text%__1cIDivLNodeLbottom_type6kM_pknEType__; +text: .text%__1cNobjArrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cJloadFNodeHtwo_adr6kM_I_; +text: .text%__1cNinstanceKlassYremove_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cOGenerateOopMapTmark_reachable_code6M_v_; +text: .text%__1cNobjArrayKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cOGenerateOopMapKinterp_all6M_v_; +text: .text%__1cOGenerateOopMapYrewrite_refval_conflicts6M_v_; +text: .text%__1cOGenerateOopMapRinit_basic_blocks6M_v_; +text: .text%__1cOGenerateOopMapYsetup_method_entry_state6M_v_; +text: .text%__1cFframeZinterpreter_frame_set_mdx6Ml_v_; +text: .text%__1cOGenerateOopMapbAmake_context_uninitialized6M_v_; +text: .text%__1cQorI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cOGenerateOopMapTmethodsig_to_effect6MpnNsymbolOopDesc_ipnNCellTypeState__i_; +text: .text%__1cOGenerateOopMapPinitialize_vars6M_v_; +text: .text%__1cNobjArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cOGenerateOopMapKinit_state6M_v_; +text: .text%__1cPClassFileParserbFparse_constant_pool_float_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cRmulL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cOGenerateOopMapNinitialize_bb6M_v_; +text: .text%__1cOGenerateOopMapRdo_interpretation6M_v_; +text: .text%__1cQciBytecodeStreamFtable6MnJBytecodesECode__2_; +text: .text%__1cOGenerateOopMap2t6MnMmethodHandle__v_; +text: .text%__1cOGenerateOopMapbImark_bbheaders_and_count_gc_points6M_v_; +text: .text%__1cOGenerateOopMapLcompute_map6MpnGThread__v_; +text: .text%__1cIRetTableRcompute_ret_table6MnMmethodHandle__v_; +text: .text%__1cRxorI_rReg_memNodeHtwo_adr6kM_I_; +text: .text%__1cNmulI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cFStateM_sub_Op_XorI6MpknENode__v_; +text: .text%__1cISubLNodeJideal_reg6kM_I_; +text: .text%__1cLStrCompNodeLbottom_type6kM_pknEType__; +text: .text%__1cQOopMapCacheEntryFflush6M_v_; +text: .text%__1cQOopMapCacheEntryRallocate_bit_mask6M_v_; +text: .text%__1cQOopMapCacheEntryTdeallocate_bit_mask6M_v_; +text: .text%__1cQOopMapCacheEntryEfill6MnMmethodHandle_i_v_; +text: .text%__1cRsalL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPsalL_rReg_1NodePoper_input_base6kM_I_; +text: .text%__1cOstackSlotPOperJnum_edges6kM_I_; +text: .text%__1cOPhaseIdealLoopOadd_constraint6MiipnENode_22p23_v_; +text: .text%jni_IsAssignableFrom: jni.o; +text: .text%__1cOstackSlotPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFParseWcheck_interpreter_type6MpnENode_pknEType_rpnNSafePointNode__2_; +text: .text%__1cFParseXfetch_interpreter_state6MipknEType_pnENode__5_; +text: .text%__1cObox_handleNodeJnum_opnds6kM_I_; +text: .text%__1cNaddP_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cSComputeAdapterInfoHdo_byte6M_v_; +text: .text%__1cPPerfDataManagerMcounter_name6Fpkc2_pc_; +text: .text%__1cOGenerateOopMapKpp_new_ref6MpnNCellTypeState_i_v_; +text: .text%__1cNcmovI_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cQOopMapCacheEntryIset_mask6MpnNCellTypeState_2i_v_; +text: .text%__1cOGenerateOopMapVresult_for_basicblock6Mi_v_; +text: .text%__1cTOopMapForCacheEntry2t6MnMmethodHandle_ipnQOopMapCacheEntry__v_; +text: .text%__1cPcmpD_cc_immNodeMideal_Opcode6kM_i_; +text: .text%__1cTOopMapForCacheEntryLcompute_map6MpnGThread__v_; +text: .text%__1cTOopMapForCacheEntryOreport_results6kM_i_; +text: .text%__1cTconvF2D_reg_memNodePoper_input_base6kM_I_; +text: .text%__1cNdivL_rRegNodeErule6kM_I_; +text: .text%__1cRmulL_rReg_immNodeQuse_cisc_RegMask6M_v_; +text: .text%JVM_GetCallerClass; +text: .text%__1cQsalL_rReg_CLNodeErule6kM_I_; +text: .text%__1cJloadLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNloadConP0NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXMachCallDynamicJavaNodePret_addr_offset6M_i_; +text: .text%__1cRxorI_rReg_immNodeErule6kM_I_; +text: .text%__1cZCallDynamicJavaDirectNodePcompute_padding6kMi_i_; +text: .text%__1cFParseScreate_jump_tables6MpnENode_pnLSwitchRange_4_i_; +text: .text%__1cZCallDynamicJavaDirectNodeKmethod_set6Ml_v_; +text: .text%__1cPcmovI_reg_lNodeHtwo_adr6kM_I_; +text: .text%__1cLConvD2INodeGOpcode6kM_i_; +text: .text%__1cNcmovP_regNodeMideal_Opcode6kM_i_; +text: .text%__1cTconvI2F_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cKstorePNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWPredictedCallGeneratorJis_inline6kM_i_; +text: .text%__1cUjmpLoopEnd_shortNodeJis_Branch6kM_I_; +text: .text%__1cQorI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cSInterpreterRuntimeOmultianewarray6FpnKJavaThread_pi_v_; +text: .text%__1cUjmpLoopEnd_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cFParseSjump_if_false_fork6MpnGIfNode_ii_v_; +text: .text%__1cbFloadConL_0x6666666666666667NodeMideal_Opcode6kM_i_; +text: .text%__1cJAssemblerEshll6MpnMRegisterImpl_i_v_; +text: .text%__1cOjmpLoopEndNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cUjmpLoopEnd_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPCountedLoopNodeGstride6kM_pnENode__; +text: .text%__1cHi2bNodeJnum_opnds6kM_I_; +text: .text%__1cHTypeAryFxdual6kM_pknEType__; +text: .text%__1cOMacroAssemblerKverify_oop6MpnMRegisterImpl_pkc_v_; +text: .text%__1cKstoreFNodeHtwo_adr6kM_I_; +text: .text%__1cNnegI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLencode_copy6FrnKCodeBuffer_ii_v_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvI2F_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cUCallNativeDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cKmul_hiNodePoper_input_base6kM_I_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_pkcnGHandle_6_6_; +text: .text%__1cPcmpD_cc_regNodeMcisc_operand6kM_i_; +text: .text%__1cPICStubInterfaceKinitialize6MpnEStub_i_v_; +text: .text%__1cPICStubInterfaceRcode_size_to_size6kMi_i_; +text: .text%__1cPcmpD_cc_regNodeErule6kM_I_; +text: .text%__1cNtestU_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPSPromotionLABRunallocate_object6MpnHoopDesc__i_; +text: .text%__1cPcmpD_cc_immNodeHtwo_adr6kM_I_; +text: .text%__1cOPhaseIdealLoopJdo_unroll6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cRandL_rReg_immNodeErule6kM_I_; +text: .text%__1cNloadConP0NodeGis_Con6kM_I_; +text: .text%__1cIMulINodeKmul_opcode6kM_i_; +text: .text%__1cNdivL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cIMulINodeKadd_opcode6kM_i_; +text: .text%__1cRxorI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPregister_native6FnLKlassHandle_nMsymbolHandle_1pCpnGThread__i_: jni.o; +text: .text%__1cTno_rax_rdx_RegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOtypeArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cTno_rax_rdx_RegLOperJnum_edges6kM_I_; +text: .text%__1cOCallNativeNodeGOpcode6kM_i_; +text: .text%__1cQsalI_rReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSobjArrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cJAssemblerDjmp6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cLRShiftLNodeJideal_reg6kM_I_; +text: .text%jni_SetBooleanField: jni.o; +text: .text%__1cVLoaderConstraintTableWfind_constrained_klass6MnMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cIModLNodeLbottom_type6kM_pknEType__; +text: .text%__1cRxorI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHMonitor2t6Mipkci_v_; +text: .text%__1cGHandle2t6MpnGThread_pnHoopDesc__v_; +text: .text%__1cQorI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cODeoptimizationVtrap_state_has_reason6Fii_i_; +text: .text%__1cOloadConL32NodeMideal_Opcode6kM_i_; +text: .text%__1cNGrowableArray4Cpv_Egrow6Mi_v_; +text: .text%jni_GetFieldID: jni.o; +text: .text%__1cNGrowableArray4Cl_Egrow6Mi_v_; +text: .text%__1cXjava_lang_ref_ReferenceOset_discovered6FpnHoopDesc_2_v_; +text: .text%__1cLResourceObj2n6FLn0APallocation_type__pv_; +text: .text%__1cFframeZinterpreter_frame_set_mdp6MpC_v_; +text: .text%__1cJLoadPNodeUdepends_only_on_test6kM_i_; +text: .text%__1cFBlockNset_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cIciObject2t6MpnHciKlass__v_; +text: .text%__1cScompL_rReg_immNodeErule6kM_I_; +text: .text%__1cQshrI_rReg_CLNodeErule6kM_I_; +text: .text%__1cNaddL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateT_sub_Op_ThreadLocal6MpknENode__v_; +text: .text%__1cVCallRuntimeDirectNodeHtwo_adr6kM_I_; +text: .text%__1cKciTypeFlowOsplit_range_at6Mi_pn0AFRange__; +text: .text%__1cQSystemDictionaryTresolve_from_stream6FnMsymbolHandle_nGHandle_2pnPClassFileStream_pnGThread__pnMklassOopDesc__; +text: .text%__1cXjvm_define_class_common6FpnHJNIEnv__pkcpnI_jobject_pkWi53pnGThread__pnH_jclass__: jvm.o; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcipnNsymbolOopDesc_4_v_; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcinMsymbolHandle_4nGHandle_6_v_; +text: .text%__1cMmulD_immNodePoper_input_base6kM_I_; +text: .text%__1cRInlineCacheBufferRic_stub_code_size6F_i_; +text: .text%__1cMmulF_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNGrowableArray4CpnKStackValue__2t6Mii_v_; +text: .text%__1cZInterpreterMacroAssemblerbAget_cache_and_index_at_bcp6MpnMRegisterImpl_2i_v_; +text: .text%__1cRandL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pCi_v_; +text: .text%__1cUandI_rReg_imm255NodeErule6kM_I_; +text: .text%__1cRmulL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPsarL_rReg_2NodePoper_input_base6kM_I_; +text: .text%__1cJAssemblerGpushaq6M_v_; +text: .text%__1cOMethodLivenessKBasicBlockFsplit6Mi_p1_; +text: .text%__1cFStateP_sub_Op_RShiftL6MpknENode__v_; +text: .text%__1cMrsi_RegPOperJnum_edges6kM_I_; +text: .text%__1cMstoreSSPNodePoper_input_base6kM_I_; +text: .text%__1cScompL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cKCodeBufferWinsert_double_constant6Md_pC_; +text: .text%__1cPClassFileParserbJparse_classfile_signature_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNaddP_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFParseHdo_irem6M_v_; +text: .text%__1cRsarL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_2_v_; +text: .text%__1cHi2bNodeHtwo_adr6kM_I_; +text: .text%__1cPClassFileParserbBcheck_illegal_static_method6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cJAssemblerFmovsd6MnHAddress_pnRFloatRegisterImpl__v_; +text: .text%__1cTCallInterpreterNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cXMachCallInterpreterNodeWis_MachCallInterpreter6M_p0_; +text: .text%__1cFStateX_sub_Op_CallInterpreter6MpknENode__v_; +text: .text%__1cZCallInterpreterDirectNodeFreloc6kM_i_; +text: .text%__1cMStartC2INodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cZInterpreterMacroAssemblerYtest_method_data_pointer6MpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cMStartC2INodeKc2i_domain6FpknJTypeTuple__3_; +text: .text%__1cHCompilebMGenerate_Compiled_To_Interpreter_Graph6MpknITypeFunc_pC_v_; +text: .text%__1cZCallInterpreterDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFciEnvUregister_c2i_adapter6MpnIciMethod_pnJOopMapSet_pnKCodeBuffer_ii_v_; +text: .text%__1cSMachC2IcheckICNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSMachC2IEntriesNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRtestI_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHMatcherbAinterpreter_method_oop_reg6F_i_; +text: .text%__1cHMatcherXcompiler_method_oop_reg6F_i_; +text: .text%__1cIciMethodRinterpreter_entry6M_pC_; +text: .text%__1cSTailCalljmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmpD_cc_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cSTailCalljmpIndNodeGpinned6kM_i_; +text: .text%__1cSTailCalljmpIndNodeHtwo_adr6kM_I_; +text: .text%jni_SetByteArrayRegion: jni.o; +text: .text%__1cHBoxNodeGOpcode6kM_i_; +text: .text%__1cPcmpD_cc_regNodeJnum_opnds6kM_I_; +text: .text%__1cKC2IAdapter2t6MpnKCodeBuffer_iIpnJOopMapSet_i_v_; +text: .text%__1cKC2IAdapterPnew_c2i_adapter6FpnKCodeBuffer_IpnJOopMapSet_i_p0_; +text: .text%__1cKC2IAdapter2n6FLI_pv_; +text: .text%__1cJAssemblerFmovss6MnHAddress_pnRFloatRegisterImpl__v_; +text: .text%__1cIMulINodeJideal_reg6kM_I_; +text: .text%__1cKCMovePNodeGOpcode6kM_i_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pF_vc_v_; +text: .text%__1cGciType2t6MpnHciKlass__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_object6M_v_; +text: .text%__1cHciKlass2t6MpnIciSymbol_p0_v_; +text: .text%__1cFParseTjump_if_always_fork6Mii_v_; +text: .text%__1cKloadUBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMmulF_immNodePoper_input_base6kM_I_; +text: .text%__1cPcmpD_cc_immNodePoper_input_base6kM_I_; +text: .text%__1cUciInstanceKlassKlassEmake6F_p0_; +text: .text%__1cJAssemblerDhlt6M_v_; +text: .text%__1cOMacroAssemblerEstop6Mpkc_v_; +text: .text%__1cQComputeCallStackIdo_float6M_v_; +text: .text%__1cIciObjectUis_array_klass_klass6M_i_; +text: .text%__1cKciTypeFlowLStateVectorLdo_newarray6MpnQciBytecodeStream__v_; +text: .text%__1cWResolveOopMapConflictsRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cJloadFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmovI_reg_lNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciSymbolHbyte_at6Mi_i_; +text: .text%__1cNFingerprinterHdo_byte6M_v_; +text: .text%__1cENode2t6Mp0111111_v_; +text: .text%__1cIMaxINodeGadd_id6kM_pknEType__; +text: .text%__1cRCardTableModRefBSFclear6MnJMemRegion__v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cKExceptionsL_throw_args6FpnGThread_pkcinMsymbolHandle_5pnRJavaCallArguments__v_; +text: .text%__1cMmulD_immNodeErule6kM_I_; +text: .text%__1cMnegD_regNodePoper_input_base6kM_I_; +text: .text%__1cFframeVshould_be_deoptimized6kM_i_; +text: .text%__1cMaddF_regNodeMideal_Opcode6kM_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorThas_signature_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorVhas_annotations_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorbFhas_parameter_annotations_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cKReflectionPnew_constructor6FnMmethodHandle_pnGThread__pnHoopDesc__; +text: .text%__1cbDjava_lang_reflect_ConstructorPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorNset_modifiers6FpnHoopDesc_i_v_; +text: .text%JVM_DefineClassWithSource; +text: .text%__1cbDjava_lang_reflect_ConstructorZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cPciInstanceKlassNloader_handle6M_pnI_jobject__; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cPciInstanceKlassYprotection_domain_handle6M_pnI_jobject__; +text: .text%__1cbDjava_lang_reflect_ConstructorJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorGcreate6FpnGThread__nGHandle__; +text: .text%__1cKmul_hiNodeJnum_opnds6kM_I_; +text: .text%__1cKstoreFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstoreBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRxorI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cNsubI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cJAssemblerExorl6MpnMRegisterImpl_2_v_; +text: .text%__1cHMatcherXinterpreter_arg_ptr_reg6F_i_; +text: .text%__1cINegDNodeGOpcode6kM_i_; +text: .text%__1cNdecL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPjava_lang_ClassQprimitive_mirror6FnJBasicType__pnHoopDesc__; +text: .text%__1cRsarI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cNcmovP_regNodeJnum_opnds6kM_I_; +text: .text%__1cSCompiledStaticCallNcompute_entry6FnMmethodHandle_rnOStaticCallInfo__v_; +text: .text%__1cMloadConDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOptoRuntimeVresolve_static_call_C6FpnKJavaThread__pC_; +text: .text%__1cSCompiledStaticCallIis_clean6kM_i_; +text: .text%__1cMrsi_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cTAbstractInterpreterLdeopt_entry6FnITosState_i_pC_; +text: .text%__1cRindIndexScaleOperNconstant_disp6kM_i_; +text: .text%__1cQorI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cRtestI_reg_immNodeJnum_opnds6kM_I_; +text: .text%jni_NewStringUTF: jni.o; +text: .text%__1cTAbstractInterpreterSBasicType_as_index6FnJBasicType__i_; +text: .text%__1cNtestI_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerGpush_l6MpnMRegisterImpl__v_; +text: .text%__1cRxorI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerFpop_i6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cObox_handleNodeErule6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerIpush_ptr6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cOleaPIdxOffNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsarL_rReg_63NodePoper_input_base6kM_I_; +text: .text%__1cSCompiledStaticCallDset6MrknOStaticCallInfo__v_; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_i_v_; +text: .text%__1cHCompileRmake_vm_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cNmulI_rRegNodeErule6kM_I_; +text: .text%__1cNGrowableArray4Ci_2t6Mii_v_; +text: .text%__1cQsalL_rReg_CLNodeJnum_opnds6kM_I_; +text: .text%__1cNGrowableArray4Ci_Uclear_and_deallocate6M_v_; +text: .text%__1cPCountedLoopNode2t6MpnENode_2_v_; +text: .text%__1cSCountedLoopEndNode2t6MpnENode_2ff_v_; +text: .text%__1cJloadDNodeMideal_Opcode6kM_i_; +text: .text%__1cENodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIDivLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMmulD_regNodePoper_input_base6kM_I_; +text: .text%__1cTconvF2D_reg_memNodeJnum_opnds6kM_I_; +text: .text%__1cIModINodeJideal_reg6kM_I_; +text: .text%__1cYinternal_word_RelocationGtarget6M_pC_; +text: .text%__1cObox_handleNodeLbottom_type6kM_pknEType__; +text: .text%__1cbDreorder_based_on_method_index6FpnPobjArrayOopDesc_1ppnHoopDesc__v_: methodOop.o; +text: .text%__1cPshrL_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cUverify_byte_codes_fn6F_pv_: verifier.o; +text: .text%__1cMLinkResolverbPlinktime_resolve_interface_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%JVM_GetClassCPTypes; +text: .text%__1cQComputeCallStackHdo_byte6M_v_; +text: .text%JVM_GetClassCPEntriesCount; +text: .text%JVM_GetClassFieldsCount; +text: .text%JVM_GetClassMethodsCount; +text: .text%__1cINodeHashEgrow6M_v_; +text: .text%__1cJloadBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKmul_hiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerEnegq6MpnMRegisterImpl__v_; +text: .text%__1cNmodL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKloadUBNodeHtwo_adr6kM_I_; +text: .text%__1cRxorI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cVCallRuntimeDirectNodePoper_input_base6kM_I_; +text: .text%__1cSstring_compareNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongVariable__; +text: .text%__1cNsubL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOjmpLoopEndNodeGnegate6M_v_; +text: .text%__1cQorI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVCallRuntimeDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPsalL_rReg_1NodeErule6kM_I_; +text: .text%__1cPcmpD_cc_immNodeErule6kM_I_; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_2_v_; +text: .text%__1cbCAbstractInterpreterGeneratorVset_vtos_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cHCompileQgrow_alias_types6M_v_; +text: .text%__1cUandI_rReg_imm255NodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_CallIntMethod: jni.o; +text: .text%__1cNSharedRuntimeSfind_callee_method6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cPno_rax_RegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMrdx_RegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNmulI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cNxorI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTconvF2D_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIUniverseWreinitialize_vtable_of6FpnFKlass_pnGThread__v_; +text: .text%__1cJLoadINodeMstore_Opcode6kM_i_; +text: .text%__1cNSharedRuntimeTreresolve_call_site6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cbFunnecessary_membar_volatileNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cENodeEgetd6kM_d_; +text: .text%__1cICmpFNodeGOpcode6kM_i_; +text: .text%__1cLOptoRuntimeThandle_wrong_method6FpnKJavaThread__pC_; +text: .text%__1cNGrowableArray4CpnOMethodLivenessKBasicBlock__Egrow6Mi_v_; +text: .text%__1cKstoreFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%JVM_SetClassSigners; +text: .text%__1cNdivL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompI_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZCallDynamicJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRandL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cOstackSlotPOperFscale6kM_i_; +text: .text%__1cHnmethodPis_dependent_on6MpnMklassOopDesc__i_; +text: .text%__1cMdecI_memNodeHtwo_adr6kM_I_; +text: .text%__1cSalign_to_page_size6FL_L_: heap.o; +text: .text%__1cNmulI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cOstackSlotPOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cPsarL_rReg_2NodeErule6kM_I_; +text: .text%__1cOPhaseIdealLoopUpeeled_dom_test_elim6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%jni_NewByteArray: jni.o; +text: .text%__1cYinternal_word_RelocationFvalue6M_pC_; +text: .text%__1cYinternal_word_RelocationWfix_relocation_at_move6Ml_v_; +text: .text%__1cFStateM_sub_Op_SubL6MpknENode__v_; +text: .text%__1cJAssemblerSemit_arith_operand6MipnMRegisterImpl_nHAddress_i_v_; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cMaddF_regNodeMcisc_operand6kM_i_; +text: .text%__1cRsubI_rReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNloadConPcNodeHtwo_adr6kM_I_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_deopt_entry_for6MnITosState_i_pC_; +text: .text%__1cKLoadPCNodeGOpcode6kM_i_; +text: .text%__1cTconvI2F_reg_regNodeMcisc_operand6kM_i_; +text: .text%__1cOstackSlotPOperEtype6kM_pknEType__; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_return_entry_for6MnITosState_i_pC_; +text: .text%__1cTconvD2I_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cMstoreSSPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNGrowableArray4Ci_Icontains6kMrki_i_; +text: .text%__1cKstoreBNodeFreloc6kM_i_; +text: .text%__1cObox_handleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMjniIdPrivateGid_for6FnTinstanceKlassHandle_i_l_; +text: .text%__1cNget_method_id6FpnHJNIEnv__pnH_jclass_pkc5ipnGThread__pnK_jmethodID__: jni.o; +text: .text%__1cQshrI_rReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_SystemTout_offset_in_bytes6F_i_; +text: .text%__1cMjniIdSupportNto_jmethod_id6FpnNmethodOopDesc__pnK_jmethodID__; +text: .text%__1cNmethodOopDescKjmethod_id6M_pnK_jmethodID__; +text: .text%__1cQjava_lang_SystemSin_offset_in_bytes6F_i_; +text: .text%__1cRandL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_2_v_; +text: .text%__1cbACallCompiledJavaDirectNodeFreloc6kM_i_; +text: .text%__1cIAddFNodeLbottom_type6kM_pknEType__; +text: .text%__1cUCallCompiledJavaNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cFStateY_sub_Op_CallCompiledJava6MpknENode__v_; +text: .text%__1cFciEnvUregister_i2c_adapter6MpnIciMethod_pnJOopMapSet_pnKCodeBuffer_i_v_; +text: .text%__1cbACallCompiledJavaDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMStartI2CNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cHCompilebMGenerate_Interpreter_To_Compiled_Graph6MpknITypeFunc__v_; +text: .text%__1cPciObjectFactoryPinsert_non_perm6Mrpn0ANNonPermObject_pnHoopDesc_pnIciObject__v_; +text: .text%__1cKI2CAdapter2n6FLI_pv_; +text: .text%__1cKCodeBufferVinsert_float_constant6Mf_pC_; +text: .text%__1cbACallCompiledJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKI2CAdapterPnew_i2c_adapter6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cKmul_hiNodeErule6kM_I_; +text: .text%__1cKI2CAdapter2t6MpnKCodeBuffer_pnJOopMapSet_ii_v_; +text: .text%__1cbBinitialize_itable_for_klass6FpnMklassOopDesc__v_; +text: .text%__1cFJNIidEfind6Mi_p0_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_5pnGThread__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIgenerate6ML_v_; +text: .text%__1cXSignatureHandlerLibraryOpd_set_handler6FpC_v_; +text: .text%__1cILogDNodeGOpcode6kM_i_; +text: .text%__1cXSignatureHandlerLibraryLset_handler6FpnKCodeBuffer__pC_; +text: .text%JVM_IsPrimitiveClass; +text: .text%__1cIDivDNodeGOpcode6kM_i_; +text: .text%__1cMnegD_regNodeMideal_Opcode6kM_i_; +text: .text%__1cJCMoveNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPciObjArrayKlassJmake_impl6FpnHciKlass__p0_; +text: .text%__1cQjava_lang_ThreadJis_daemon6FpnHoopDesc__i_; +text: .text%__1cJAssemblerEcmpl6MnHAddress_i_v_; +text: .text%__1cHi2bNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLimmI_24OperJnum_edges6kM_I_; +text: .text%__1cRxorI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFTypeDJsingleton6kM_i_; +text: .text%__1cPsalI_rReg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIModLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cUPipeline_Use_Element2t6MIIIinXPipeline_Use_Cycle_Mask__v_; +text: .text%__1cTmembar_volatileNodePoper_input_base6kM_I_; +text: .text%__1cNloadConPcNodePoper_input_base6kM_I_; +text: .text%__1cXPipeline_Use_Cycle_Mask2t6MI_v_; +text: .text%__1cKCompiledICMset_to_clean6M_v_; +text: .text%__1cNdecL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cIciMethodOresolve_invoke6MpnHciKlass_2_p0_; +text: .text%__1cQChunkPoolCleanerEtask6M_v_; +text: .text%__1cICmpDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPsalL_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_MulI6MpknENode__v_; +text: .text%__1cZCallDynamicJavaDirectNodeFreloc6kM_i_; +text: .text%__1cQMachCallJavaNodeVis_MachCallStaticJava6M_pnWMachCallStaticJavaNode__; +text: .text%__1cUandI_rReg_imm255NodeJnum_opnds6kM_I_; +text: .text%__1cFStateX_sub_Op_CallDynamicJava6MpknENode__v_; +text: .text%__1cNObjectMonitorEwait6MxipnGThread__v_; +text: .text%jni_FindClass: jni.o; +text: .text%__1cKarrayKlassTallocate_arrayArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cIMinINodeJideal_reg6kM_I_; +text: .text%__1cJCMoveNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSObjectSynchronizerEwait6FnGHandle_xpnGThread__v_; +text: .text%__1cNCallGeneratorSfor_predicted_call6FpnHciKlass_p03_3_; +text: .text%__1cLTypeInstPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cTconvI2F_reg_regNodeErule6kM_I_; +text: .text%__1cWPredictedCallGeneratorKis_virtual6kM_i_; +text: .text%__1cTconvF2D_reg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cNcmovP_regNodeErule6kM_I_; +text: .text%__1cMaddF_regNodeJnum_opnds6kM_I_; +text: .text%__1cWPredictedCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%JVM_MonitorWait; +text: .text%__1cPshrL_rReg_1NodePoper_input_base6kM_I_; +text: .text%__1cMaddF_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNCallGeneratorQfor_virtual_call6FpnIciMethod__p0_; +text: .text%__1cUVirtualCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cLPSMarkSweepbAabsorb_live_data_from_eden6FpnUPSAdaptiveSizePolicy_pnKPSYoungGen_pnIPSOldGen__i_; +text: .text%__1cUPSMarkSweepDecoratorbDadvance_destination_decorator6F_v_; +text: .text%__1cNmulI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNmulI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cSOnStackReplacementPget_osr_adapter6FnFframe_nMmethodHandle__pnKOSRAdapter__; +text: .text%__1cNGrowableArray4CpnKOSRAdapter__Hat_grow6Mirk1_1_; +text: .text%__1cPciObjArrayKlassEmake6FpnHciKlass__p0_; +text: .text%JVM_GetClassDeclaredConstructors; +text: .text%__1cRCardTableModRefBSKinvalidate6MnJMemRegion__v_; +text: .text%__1cJLoadFNodeJideal_reg6kM_I_; +text: .text%__1cJAssemblerEaddq6MpnMRegisterImpl_2_v_; +text: .text%__1cFTypeFJsingleton6kM_i_; +text: .text%__1cTconvF2D_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cMstoreSSPNodeErule6kM_I_; +text: .text%__1cOloadConL32NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMstoreSSPNodeHtwo_adr6kM_I_; +text: .text%__1cMincI_memNodeHtwo_adr6kM_I_; +text: .text%__1cKcmpOpUOperFequal6kM_i_; +text: .text%__1cTconvF2D_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cHRegMask2t6M_v_; +text: .text%__1cIGraphKitPdstore_rounding6MpnENode__2_; +text: .text%__1cNGrowableArray4Ci_2t6MpnFArena_iirki_v_; +text: .text%__1cNloadConL0NodeHsize_of6kM_I_; +text: .text%__1cQset_lwp_priority6Fiii_i_; +text: .text%__1cJCmpD3NodeGOpcode6kM_i_; +text: .text%__1cKloadUBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmpD_cc_immNodeJnum_opnds6kM_I_; +text: .text%__1cLConvL2DNodeGOpcode6kM_i_; +text: .text%__1cRmulI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerFcmovq6Mn0AJCondition_pnMRegisterImpl_3_v_; +text: .text%__1cNminI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMmulD_regNodeMideal_Opcode6kM_i_; +text: .text%__1cNminI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cFStateM_sub_Op_MinI6MpknENode__v_; +text: .text%__1cCosTset_native_priority6FpnGThread_i_nIOSReturn__; +text: .text%__1cJStubQdDueueRrequest_committed6Mi_pnEStub__; +text: .text%__1cScompL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGICStubIfinalize6M_v_; +text: .text%__1cNCallGeneratorRfor_uncommon_trap6FpnIciMethod_nODeoptimizationLDeoptReason_n0CLDeoptAction__p0_; +text: .text%__1cUandI_rReg_imm255NodeHtwo_adr6kM_I_; +text: .text%__1cJStubQdDueueMremove_first6M_v_; +text: .text%__1cOPhaseIdealLoopOdo_range_check6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cUVirtualCallGeneratorKis_virtual6kM_i_; +text: .text%__1cPICStubInterfaceIfinalize6MpnEStub__v_; +text: .text%__1cZUncommonTrapCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cPICStubInterfaceEsize6kMpnEStub__i_; +text: .text%__1cJAssemblerEcmpq6MpnMRegisterImpl_2_v_; +text: .text%__1cNGrowableArray4CpnIciObject__Egrow6Mi_v_; +text: .text%__1cFStateM_sub_Op_ModI6MpknENode__v_; +text: .text%__1cNObjectMonitor2t6M_v_; +text: .text%__1cNmodI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFParseNdo_instanceof6M_v_; +text: .text%__1cPcmpD_cc_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNIdealLoopTreeXpolicy_maximally_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cTmembar_volatileNodeHtwo_adr6kM_I_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cIGraphKitOgen_instanceof6MpnENode_2_2_; +text: .text%__1cHciKlassOsuper_of_depth6MI_p0_; +text: .text%__1cJLoadDNodeGOpcode6kM_i_; +text: .text%__1cNcmovL_regNodePoper_input_base6kM_I_; +text: .text%__1cMdecI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRaddI_rReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_; +text: .text%__1cQsalL_rReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNandI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbFunnecessary_membar_volatileNodeLbottom_type6kM_pknEType__; +text: .text%__1cHMatcherXpost_store_load_barrier6FpknENode__i_; +text: .text%__1cYjava_lang_reflect_MethodThas_signature_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodbFhas_parameter_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodbChas_annotation_default_field6F_i_; +text: .text%__1cJloadDNodePoper_input_base6kM_I_; +text: .text%__1cENodeEgetf6kM_f_; +text: .text%__1cYjava_lang_reflect_MethodIset_name6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodPset_return_type6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodGcreate6FpnGThread__nGHandle__; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_; +text: .text%__1cKReflectionKnew_method6FnMmethodHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cYjava_lang_reflect_MethodVhas_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodWset_annotation_default6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cNmethodOopDescSannotation_default6kM_pnQtypeArrayOopDesc__; +text: .text%__1cYjava_lang_reflect_MethodTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cVCallRuntimeDirectNodeKmethod_set6Ml_v_; +text: .text%__1cTconvD2I_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cSTailCalljmpIndNodeJnum_opnds6kM_I_; +text: .text%__1cTconvI2D_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cQorI_rReg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cKstoreFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRaddI_mem_rRegNodePoper_input_base6kM_I_; +text: .text%__1cMmulF_immNodeErule6kM_I_; +text: .text%__1cJAssemblerGmovlpd6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cPcmpF_cc_regNodePoper_input_base6kM_I_; +text: .text%__1cNCompileBrokerTcompile_adapter_for6FnMmethodHandle_ii_pnMBasicAdapter__; +text: .text%__1cCosbBthread_local_storage_at_put6Fipv_v_; +text: .text%__1cNCompileBrokerbBwait_for_adapter_completion6FpnLCompileTask__pnMBasicAdapter__; +text: .text%__1cOjmpLoopEndNodeJis_Branch6kM_I_; +text: .text%__1cOjmpLoopEndNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cNinstanceKlassSregister_finalizer6FpnPinstanceOopDesc_pnGThread__2_; +text: .text%__1cSThreadLocalStorageNpd_set_thread6FpnGThread__v_; +text: .text%__1cKCMoveINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSThreadLocalStoragebBget_thread_via_cache_slowly6FLi_pnGThread__; +text: .text%__1cMrax_RegIOperEtype6kM_pknEType__; +text: .text%__1cOjmpLoopEndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateW_sub_Op_MemBarVolatile6MpknENode__v_; +text: .text%jni_GetMethodID: jni.o; +text: .text%__1cSThreadLocalStorageSset_thread_in_slot6FpnGThread__v_; +text: .text%get_thread; +text: .text%__1cMincI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIGraphKitXinsert_mem_bar_volatile6MpnKMemBarNode_i_v_; +text: .text%__1cSThreadLocalStorageKset_thread6FpnGThread__v_; +text: .text%__1cCosHSolarisKmmap_chunk6FpcLii_2_; +text: .text%__1cbFloadConL_0x6666666666666667NodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_MonitorNotifyAll; +text: .text%__1cOloadConL32NodeLbottom_type6kM_pknEType__; +text: .text%__1cRxorI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cMmulD_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNcmovP_regNodeLbottom_type6kM_pknEType__; +text: .text%__1cJScopeDescTdecode_scope_values6Mi_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cTAbstractInterpreterWlayout_activation_impl6FpnNmethodOopDesc_iiiipnFframe_4i_i_; +text: .text%__1cLconvI2BNodeMideal_Opcode6kM_i_; +text: .text%__1cIDivLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQsalI_rReg_CLNodeHtwo_adr6kM_I_; +text: .text%__1cPsarL_rReg_2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmodL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cScompL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cQshrL_rReg_CLNodePoper_input_base6kM_I_; +text: .text%__1cJCMoveNode2t6MpnENode_22pknEType__v_; +text: .text%__1cJCMoveNodeEmake6FpnENode_222pknEType__p0_; +text: .text%__1cVLoaderConstraintTableYextend_loader_constraint6MpnVLoaderConstraintEntry_nGHandle_pnMklassOopDesc__v_; +text: .text%__1cIimmIOperJnum_edges6kM_I_; +text: .text%__1cJAssemblerFmovss6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cRtestI_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRandL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNGrowableArray4CpnKciTypeFlowFBlock__Gremove6Mrk2_v_; +text: .text%__1cVLoaderConstraintTablebHensure_loader_constraint_capacity6MpnVLoaderConstraintEntry_i_v_; +text: .text%__1cPsalL_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cMsubD_regNodePoper_input_base6kM_I_; +text: .text%__1cMstoreSSPNodeJnum_opnds6kM_I_; +text: .text%__1cMnegD_regNodeHtwo_adr6kM_I_; +text: .text%__1cIBytecodeIset_code6MnJBytecodesECode__v_; +text: .text%__1cPClassFileParserXverify_unqualified_name6MpcIi_i_; +text: .text%__1cMdivD_immNodeMideal_Opcode6kM_i_; +text: .text%__1cTconvI2F_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_ThreadMis_stillborn6FpnHoopDesc__i_; +text: .text%__1cQsarL_rReg_63NodeErule6kM_I_; +text: .text%__1cRsubL_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMVirtualSpaceQuncommitted_size6kM_L_; +text: .text%__1cRsubL_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cMVirtualSpaceJexpand_by6ML_i_; +text: .text%__1cNstoreImmPNodeMideal_Opcode6kM_i_; +text: .text%__1cQjava_lang_ThreadKset_thread6FpnHoopDesc_pnKJavaThread__v_; +text: .text%__1cLOopMapCache2t6M_v_; +text: .text%__1cJloadDNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cQComputeCallStackHdo_char6M_v_; +text: .text%__1cNdivI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cOcmovI_regUNodePoper_input_base6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerEpush6MnITosState__v_; +text: .text%__1cSvframeArrayElementDbci6kM_i_; +text: .text%__1cMaddF_regNodeErule6kM_I_; +text: .text%__1cTconvF2D_reg_memNodeHtwo_adr6kM_I_; +text: .text%__1cNdecL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cMdecI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFStateN_sub_Op_LoadF6MpknENode__v_; +text: .text%__1cIMulDNodeLbottom_type6kM_pknEType__; +text: .text%__1cNaddI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerEdecl6MpnMRegisterImpl__v_; +text: .text%__1cOPhaseIdealLoopVinsert_pre_post_loops6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cJAssemblerGbswapl6MpnMRegisterImpl__v_; +text: .text%__1cRInlineCacheBufferLnew_ic_stub6F_pnGICStub__; +text: .text%__1cRInlineCacheBufferWcreate_transition_stub6FpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cIAddDNodeGOpcode6kM_i_; +text: .text%__1cMincI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIpass_int6M_v_; +text: .text%__1cNloadConPcNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGICStubIset_stub6MpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cRInlineCacheBufferXassemble_ic_buffer_code6FpCpnHoopDesc_1_v_; +text: .text%__1cTconvD2I_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerHpop_ptr6MpnMRegisterImpl__v_; +text: .text%__1cGThreadbCis_hidden_from_external_view6kM_i_; +text: .text%__1cMelapsedTimer2t6M_v_; +text: .text%__1cGThreadVis_jvmti_agent_thread6kM_i_; +text: .text%__1cMdivD_immNodeErule6kM_I_; +text: .text%__1cTconvI2D_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cFTypeFFxmeet6kMpknEType__3_; +text: .text%__1cJAssemblerEshrl6MpnMRegisterImpl_i_v_; +text: .text%__1cGICStubLdestination6kM_pC_; +text: .text%__1cRsalL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRInlineCacheBufferVic_buffer_entry_point6FpC_1_; +text: .text%__1cPcmpD_cc_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMaddD_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNdivI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cNobjArrayKlassKinitialize6MpnGThread__v_; +text: .text%jni_NewObjectArray: jni.o; +text: .text%__1cIMulFNodeLbottom_type6kM_pknEType__; +text: .text%__1cTmembar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUandI_rReg_imm255NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNdivL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cKcastPPNodePoper_input_base6kM_I_; +text: .text%__1cMaddD_immNodePoper_input_base6kM_I_; +text: .text%__1cFTypeDFxmeet6kMpknEType__3_; +text: .text%__1cKloadUBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIDivLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJloadDNodeErule6kM_I_; +text: .text%__1cRaddI_mem_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cPsarL_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cKCompiledICMstub_address6kM_pC_; +text: .text%__1cMmulD_regNodeMcisc_operand6kM_i_; +text: .text%__1cMmulF_memNodePoper_input_base6kM_I_; +text: .text%lwp_cond_destroy: os_solaris.o; +text: .text%__1cHnmethodNis_osr_method6kM_i_; +text: .text%lwp_mutex_destroy: os_solaris.o; +text: .text%__1cFParseScan_rerun_bytecode6M_i_; +text: .text%__1cISubFNodeGOpcode6kM_i_; +text: .text%__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cFTypeDGis_nan6kM_i_; +text: .text%__1cTconvI2F_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cOJavaAssertionsNmatch_package6Fpkc_pn0AKOptionList__; +text: .text%__1cOJavaAssertionsHenabled6Fpkci_i_; +text: .text%__1cOJavaAssertionsLmatch_class6Fpkc_pn0AKOptionList__; +text: .text%JVM_DesiredAssertionStatus; +text: .text%__1cHTypePtrFxdual6kM_pknEType__; +text: .text%__1cTconvI2F_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cQjava_lang_ThreadLthreadGroup6FpnHoopDesc__2_; +text: .text%__1cLConvI2FNodeLbottom_type6kM_pknEType__; +text: .text%__1cXjava_lang_reflect_FieldNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cMaddF_immNodeMideal_Opcode6kM_i_; +text: .text%__1cQshrL_rReg_CLNodeMideal_Opcode6kM_i_; +text: .text%__1cKReflectionInew_type6FnMsymbolHandle_nLKlassHandle_pnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_name6FpnHoopDesc_2_v_; +text: .text%__1cJvmSymbolsOsignature_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cMloadConFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNnegI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPcmpF_cc_regNodeHtwo_adr6kM_I_; +text: .text%__1cKReflectionJnew_field6FpnPfieldDescriptor_ipnGThread__pnHoopDesc__; +text: .text%__1cXjava_lang_reflect_FieldIset_type6FpnHoopDesc_2_v_; +text: .text%__1cKJavaThreadbScheck_safepoint_and_suspend_for_native_trans6Fp0_v_; +text: .text%__1cXjava_lang_reflect_FieldPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldGcreate6FpnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldVhas_annotations_field6F_i_; +text: .text%__1cISubDNodeGOpcode6kM_i_; +text: .text%__1cJloadFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSstring_compareNodeHtwo_adr6kM_I_; +text: .text%__1cPfieldDescriptorLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cRaddI_mem_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cXjava_lang_reflect_FieldIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cKScopeValueLis_location6kM_i_; +text: .text%__1cXjava_lang_reflect_FieldThas_signature_field6F_i_; +text: .text%__1cMmulF_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSObjectSynchronizerGnotify6FnGHandle_pnGThread__v_; +text: .text%JVM_MonitorNotify; +text: .text%__1cQsarL_rReg_63NodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_GetStaticFieldID: jni.o; +text: .text%__1cIModLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPjava_lang_ClassOprimitive_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cIjniIdMapGcreate6FnTinstanceKlassHandle__p0_; +text: .text%__1cPsarL_rReg_2NodeJnum_opnds6kM_I_; +text: .text%__1cKReflectionbFbasic_type_mirror_to_basic_type6FpnHoopDesc_pnGThread__nJBasicType__; +text: .text%__1cPcmpF_cc_regNodeMideal_Opcode6kM_i_; +text: .text%__1cQSystemDictionaryQjava_mirror_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cIjniIdMap2t6MpnMklassOopDesc_i_v_; +text: .text%__1cIjniIdMapRcompute_index_cnt6FnTinstanceKlassHandle__i_; +text: .text%__1cLjniIdBucket2t6MpnIjniIdMap_p0_v_; +text: .text%__1cNinstanceKlassKjni_id_for6Mi_pnFJNIid__; +text: .text%__1cJLoadSNodeMstore_Opcode6kM_i_; +text: .text%__1cLTypeInstPtrLmirror_type6kM_pnGciType__; +text: .text%__1cMsubF_regNodePoper_input_base6kM_I_; +text: .text%__1cPcmpD_cc_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMlogD_regNodeMideal_Opcode6kM_i_; +text: .text%__1cbFunnecessary_membar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvL2FNodeGOpcode6kM_i_; +text: .text%__1cFKlassNexternal_name6kM_pkc_; +text: .text%__1cMPipeline_Use2t6MIIIpnUPipeline_Use_Element__v_; +text: .text%__1cKstorePNodeErule6kM_I_; +text: .text%__1cNsymbolOopDescWas_klass_external_name6kM_pkc_; +text: .text%__1cbFunnecessary_membar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConPcNodeErule6kM_I_; +text: .text%__1cIPipeline2t6MIIiIIiiiipnSmachPipelineStages_2pInMPipeline_Use__v_; +text: .text%__1cRComputeEntryStackGdo_int6M_v_; +text: .text%__1cMstoreSSPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSMachBreakpointNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRsubL_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNtestU_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPsalL_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cNmodL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvF2D_reg_regNodeErule6kM_I_; +text: .text%__1cJAssemblerDjmp6MpnMRegisterImpl_nJrelocInfoJrelocType__v_; +text: .text%__1cObox_handleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsalI_rReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQorI_rReg_memNodeErule6kM_I_; +text: .text%__1cLloadSSDNodePoper_input_base6kM_I_; +text: .text%__1cNCompileBrokerbAinvoke_compiler_on_adapter6FpnLCompileTask__v_; +text: .text%__1cLConvF2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMaddF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRxorI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cMaddF_immNodePoper_input_base6kM_I_; +text: .text%__1cKCMoveLNodeGOpcode6kM_i_; +text: .text%__1cICodeHeapTmark_segmap_as_free6MLL_v_; +text: .text%__1cRaddL_rReg_memNodePoper_input_base6kM_I_; +text: .text%JVM_IsArrayClass; +text: .text%__1cJAssemblerEsbbq6MnHAddress_i_v_; +text: .text%__1cZInterpreterMacroAssemblerFpop_l6MpnMRegisterImpl__v_; +text: .text%__1cMmulD_regNodeJnum_opnds6kM_I_; +text: .text%__1cODeoptimizationYquery_update_method_data6FnQmethodDataHandle_in0ALDeoptReason_rIri4_pnLProfileData__; +text: .text%__1cICodeHeapJexpand_by6ML_i_; +text: .text%__1cMmulD_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cLConvF2DNodeLbottom_type6kM_pknEType__; +text: .text%__1cJAssemblerEaddq6MnHAddress_i_v_; +text: .text%JVM_GetClassName; +text: .text%__1cTconvF2D_reg_regNodeMcisc_operand6kM_i_; +text: .text%__1cLStringTableGintern6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cMmulD_immNodeJnum_opnds6kM_I_; +text: .text%__1cNmulI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQorI_rReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnPPerfLongCounter__; +text: .text%__1cNObjectMonitorGnotify6MpnGThread__v_; +text: .text%__1cQjava_lang_ThreadMset_priority6FpnHoopDesc_nOThreadPriority__v_; +text: .text%__1cRsubL_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRaddL_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cRsubL_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cRsubL_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cPshrL_rReg_1NodeErule6kM_I_; +text: .text%__1cQshrI_rReg_CLNodeHtwo_adr6kM_I_; +text: .text%__1cFStateO_sub_Op_CMoveI6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_RegD6MpknENode__v_; +text: .text%__1cQorI_rReg_memNodeHtwo_adr6kM_I_; +text: .text%__1cUCallNativeDirectNodeHtwo_adr6kM_I_; +text: .text%__1cTconvI2D_reg_regNodeMcisc_operand6kM_i_; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_; +text: .text%__1cIMaxINodeJideal_reg6kM_I_; +text: .text%__1cFJNIid2t6MpnMklassOopDesc_ip0_v_; +text: .text%__1cNinstanceKlassPjni_id_for_impl6FnTinstanceKlassHandle_i_pnFJNIid__; +text: .text%__1cJAssemblerEaddq6MpnMRegisterImpl_nHAddress__v_; +text: .text%JVM_Open; +text: .text%__1cHRegMask2t6Miiiiiii_v_; +text: .text%__1cbFloadConL_0x6666666666666667NodeHtwo_adr6kM_I_; +text: .text%__1cNsubI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMmulF_regNodePoper_input_base6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_i_v_; +text: .text%__1cQConstantIntValuePis_constant_int6kM_i_; +text: .text%__1cRmulL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPsarL_rReg_2NodeHtwo_adr6kM_I_; +text: .text%__1cKmul_hiNodeHtwo_adr6kM_I_; +text: .text%__1cQConstantIntValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cRxorI_rReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateM_sub_Op_ConD6MpknENode__v_; +text: .text%__1cLConvI2DNodeGOpcode6kM_i_; +text: .text%__1cVLoaderConstraintTableJnew_entry6MIpnNsymbolOopDesc_pnMklassOopDesc_ii_pnVLoaderConstraintEntry__; +text: .text%__1cNaddP_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPcmpF_cc_regNodeMcisc_operand6kM_i_; +text: .text%__1cJAssemblerFcmovl6Mn0AJCondition_pnMRegisterImpl_3_v_; +text: .text%__1cVscale_to_lwp_priority6Fiii_i_: os_solaris.o; +text: .text%__1cLOptoRuntimeWresolve_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cNPerfByteArray2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_i_v_; +text: .text%__1cLStrCompNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMmulF_memNodeMideal_Opcode6kM_i_; +text: .text%__1cKConv2BNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJloadDNodeJnum_opnds6kM_I_; +text: .text%__1cFStateM_sub_Op_RegF6MpknENode__v_; +text: .text%__1cMmulF_immNodeJnum_opnds6kM_I_; +text: .text%__1cOMacroAssemblerKnull_check6MpnMRegisterImpl_i_v_; +text: .text%__1cNcmovP_regNodeHtwo_adr6kM_I_; +text: .text%jni_GetStaticObjectField: jni.o; +text: .text%__1cIGraphKitSprecision_rounding6MpnENode__2_; +text: .text%__1cScompL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSTailCalljmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvD2F_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cLCastP2LNodeUdepends_only_on_test6kM_i_; +text: .text%__1cTconvF2D_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulD_immNodeHtwo_adr6kM_I_; +text: .text%__1cOMacroAssemblerFleave6M_v_; +text: .text%__1cMloadConDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMTailCallNode2t6MpnENode_222222_v_; +text: .text%__1cICmpDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cXPartialSubtypeCheckNodeGOpcode6kM_i_; +text: .text%__1cSTailCalljmpIndNodeFreloc6kM_i_; +text: .text%__1cObox_handleNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cOloadConL32NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMlogD_regNodePoper_input_base6kM_I_; +text: .text%__1cTconvI2F_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cMnegD_regNodeErule6kM_I_; +text: .text%__1cLvframeArrayRregister_location6kMi_pC_; +text: .text%__1cQorI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateQ_sub_Op_TailCall6MpknENode__v_; +text: .text%__1cIOSThreadNpd_initialize6M_v_; +text: .text%__1cCosScurrent_process_id6F_i_; +text: .text%__1cMaddD_immNodeErule6kM_I_; +text: .text%__1cNmaxI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cPshrL_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvI2F_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cNmaxI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIMaxINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cCosRinitialize_thread6F_v_; +text: .text%__1cCosHSolarisKvm_signals6F_pnIsigset_t__; +text: .text%__1cCosHSolarisRunblocked_signals6F_pnIsigset_t__; +text: .text%__1cMaddF_immNodeErule6kM_I_; +text: .text%__1cCosHSolarisPhotspot_sigmask6FpnGThread__v_; +text: .text%__1cFStateM_sub_Op_MaxI6MpknENode__v_; +text: .text%__1cGThread2t6M_v_; +text: .text%__1cGThreadbFinitialize_thread_local_storage6M_v_; +text: .text%__1cGThreadbArecord_stack_base_and_size6M_v_; +text: .text%__1cCosHSolarisVinit_thread_fpu_state6F_v_; +text: .text%__1cIOSThread2t6MpFpv_i1_v_; +text: .text%jni_CallStaticVoidMethod: jni.o; +text: .text%__1cCosScurrent_stack_size6F_L_; +text: .text%__1cNPhaseRegAllocHset_oop6MpknENode_i_v_; +text: .text%__1cCosScurrent_stack_base6F_pC_; +text: .text%__1cJloadFNodeFreloc6kM_i_; +text: .text%__1cCosMstart_thread6FpnGThread__v_; +text: .text%__1cKstoreFNodeFreloc6kM_i_; +text: .text%__1cKstoreLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_; +text: .text%__1cNcmovL_memNodeErule6kM_I_; +text: .text%__1cFStateO_sub_Op_StoreF6MpknENode__v_; +text: .text%__1cHnmethodbAmake_not_entrant_or_zombie6Mi_v_; +text: .text%__1cCosPpd_start_thread6FpnGThread__v_; +text: .text%__1cNcmovL_regNodeMcisc_operand6kM_i_; +text: .text%__1cCosNcreate_thread6FpnGThread_n0AKThreadType_L_i_; +text: .text%__1cLconvI2BNodePoper_input_base6kM_I_; +text: .text%__1cOGenerateOopMapMdo_checkcast6M_v_; +text: .text%JVM_SetThreadPriority; +text: .text%__1cG_start6Fpv_0_: os_solaris.o; +text: .text%__1cLOptoRuntimeMrethrow_Type6F_pknITypeFunc__; +text: .text%JVM_GetStackAccessControlContext; +text: .text%JVM_IsThreadAlive; +text: .text%__1cTconvL2D_reg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cSstring_compareNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNdivI_rRegNodeErule6kM_I_; +text: .text%__1cNdecL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOLibraryCallKitYinline_native_time_funcs6Mi_i_; +text: .text%__1cNGrowableArray4CpknEType__2t6MpnFArena_iirk2_v_; +text: .text%__1cFParseVcatch_call_exceptions6MrnYciExceptionHandlerStream__v_; +text: .text%__1cTconvL2F_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cQjava_lang_ThreadIis_alive6FpnHoopDesc__i_; +text: .text%jni_CallObjectMethod: jni.o; +text: .text%__1cJAssemblerExorq6MpnMRegisterImpl_2_v_; +text: .text%__1cNcmovL_regNodeJnum_opnds6kM_I_; +text: .text%__1cLOptoRuntimeYcurrent_time_millis_Type6F_pknITypeFunc__; +text: .text%__1cOcmovI_regUNodeMideal_Opcode6kM_i_; +text: .text%__1cNcmovL_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNObjectMonitorJnotifyAll6MpnGThread__v_; +text: .text%__1cMsubD_regNodeMideal_Opcode6kM_i_; +text: .text%__1cNloadConL0NodeFclone6kM_pnENode__; +text: .text%__1cPcmpF_cc_regNodeErule6kM_I_; +text: .text%__1cJimmL0OperFclone6kM_pnIMachOper__; +text: .text%__1cNmodI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmpF_cc_regNodeJnum_opnds6kM_I_; +text: .text%__1cPcmpF_cc_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cbFloadConL_0x6666666666666667NodePoper_input_base6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerFpop_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cTconvL2D_reg_memNodePoper_input_base6kM_I_; +text: .text%__1cLConvD2FNodeGOpcode6kM_i_; +text: .text%__1cWThreadLocalAllocBufferKinitialize6M_v_; +text: .text%__1cJloadDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEtemp6F_pnMRegisterImpl__; +text: .text%__1cMmulF_immNodeHtwo_adr6kM_I_; +text: .text%__1cQsarL_rReg_63NodeHtwo_adr6kM_I_; +text: .text%__1cQsarL_rReg_63NodeJnum_opnds6kM_I_; +text: .text%__1cQjava_lang_StringbHcreate_from_platform_depended_str6FpkcpnGThread__nGHandle__; +text: .text%__1cMsubF_regNodeMideal_Opcode6kM_i_; +text: .text%__1cTconvI2L_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWThreadLocalAllocBufferMinitial_size6F_L_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorJpass_long6M_v_; +text: .text%__1cSInterpreterRuntimeMat_safepoint6FpnKJavaThread__v_; +text: .text%__1cTconvF2I_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cRandI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cRandI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cCosNcommit_memory6FpcL_i_; +text: .text%__1cNdivI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cENodeJis_MemBar6kM_pknKMemBarNode__; +text: .text%__1cNjni_functions6F_pknTJNINativeInterface___; +text: .text%__1cNThreadServiceKadd_thread6FpnKJavaThread_i_v_; +text: .text%JVM_NativePath; +text: .text%__1cKJavaThreadKinitialize6M_v_; +text: .text%__1cHThreadsDadd6FpnKJavaThread_i_v_; +text: .text%__1cGParker2t6M_v_; +text: .text%__1cOPhaseIdealLoopKdo_peeling6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cUThreadSafepointStateGcreate6FpnKJavaThread__v_; +text: .text%__1cPciInstanceKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cKJavaThreadYcreate_stack_guard_pages6M_v_; +text: .text%__1cUThreadSafepointState2t6MpnKJavaThread__v_; +text: .text%__1cCosMguard_memory6FpcL_i_; +text: .text%__1cMnegD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUCallNativeDirectNodePoper_input_base6kM_I_; +text: .text%__1cHnmethodTinc_decompile_count6M_v_; +text: .text%__1cIMinINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cMResourceMarkNreset_to_mark6M_v_; +text: .text%__1cQThreadStatistics2t6M_v_; +text: .text%__1cMFlatProfilerJis_active6F_i_; +text: .text%__1cNloadConPcNodeLbottom_type6kM_pknEType__; +text: .text%__1cMmulD_regNodeErule6kM_I_; +text: .text%__1cMdivD_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKJavaThread2t6MpFp0pnGThread__vL_v_; +text: .text%__1cPcmpD_cc_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvI2D_reg_regNodeErule6kM_I_; +text: .text%__1cQshrL_rReg_CLNodeJnum_opnds6kM_I_; +text: .text%__1cNcmovL_memNodePoper_input_base6kM_I_; +text: .text%__1cNdivL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmpD_cc_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateT_sub_Op_CallRuntime6MpknENode__v_; +text: .text%__1cXJNI_ArgumentPusherVaArgJget_float6M_v_; +text: .text%__1cKcastPPNodeHtwo_adr6kM_I_; +text: .text%__1cKarrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cMsubD_regNodeMcisc_operand6kM_i_; +text: .text%__1cGThreadFstart6Fp0_v_; +text: .text%__1cVCallRuntimeDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsalL_rReg_CLNodeHtwo_adr6kM_I_; +text: .text%__1cVCallRuntimeDirectNodeFreloc6kM_i_; +text: .text%__1cIGraphKitIset_jvms6MpnIJVMState__v_; +text: .text%__1cKJavaThreadDrun6M_v_; +text: .text%__1cTconvD2I_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cOsalI_mem_1NodePoper_input_base6kM_I_; +text: .text%__1cSMachCallNativeNodePret_addr_offset6M_i_; +text: .text%__1cMLinkResolverbEresolve_interface_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cZInterpreterMacroAssemblerFpop_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cMrdi_RegIOperEtype6kM_pknEType__; +text: .text%__1cVThreadStateTransitionKtransition6FpnKJavaThread_nPJavaThreadState_3_v_; +text: .text%__1cUCallNativeDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKJavaThreadRthread_main_inner6M_v_; +text: .text%__1cQorI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIGraphKitbAgen_stub_or_native_wrapper6MpCpkcpnIciMethod_iiiii_v_; +text: .text%__1cPsalL_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMResourceMark2t6M_v_; +text: .text%__1cQshrI_rReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJScopeDescGlocals6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cJScopeDescVdecode_monitor_values6Mi_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cSvframeArrayElementPunpack_on_stack6MiipnFframe_ii_v_; +text: .text%__1cTAbstractInterpreterRlayout_activation6FpnNmethodOopDesc_iiiipnFframe_4i_v_; +text: .text%__1cOcompiledVFrameLexpressions6kM_pnUStackValueCollection__; +text: .text%__1cOcompiledVFrameImonitors6kM_pnNGrowableArray4CpnLMonitorInfo____; +text: .text%__1cOcompiledVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cSvframeArrayElementHfill_in6MpnOcompiledVFrame__v_; +text: .text%__1cOcompiledVFrameHraw_bci6kM_i_; +text: .text%__1cNGrowableArray4CpnLMonitorInfo__2t6Mii_v_; +text: .text%__1cPPerfDataManagerWcreate_string_constant6FnJCounterNS_pkc3pnGThread__pnSPerfStringConstant__; +text: .text%__1cOMacroAssemblerKincrementl6MpnMRegisterImpl_i_v_; +text: .text%__1cFframebCinterpreter_frame_set_locals6Mpl_v_; +text: .text%__1cFframebHinterpreter_frame_set_monitor_end6MpnPBasicObjectLock__v_; +text: .text%__1cTAbstractInterpreterPsize_activation6FpnNmethodOopDesc_iiiii_i_; +text: .text%__1cSPerfStringConstant2t6MnJCounterNS_pkc3_v_; +text: .text%__1cTAbstractInterpreterQcontinuation_for6FpnNmethodOopDesc_pCiiri_3_; +text: .text%__1cZInterpreterMacroAssemblerLcall_VM_Ico6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cFframebCinterpreter_frame_set_method6MpnNmethodOopDesc__v_; +text: .text%__1cMmulF_regNodeMideal_Opcode6kM_i_; +text: .text%__1cFframebBinterpreter_frame_sender_sp6kM_pl_; +text: .text%__1cMaddF_regNodeHtwo_adr6kM_I_; +text: .text%__1cKCompiledICOset_cached_oop6MpnHoopDesc__v_; +text: .text%__1cKstoreINodeErule6kM_I_; +text: .text%__1cJScopeDescImonitors6M_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_i_v_; +text: .text%__1cOcompiledVFrameGlocals6kM_pnUStackValueCollection__; +text: .text%__1cJScopeDescLexpressions6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cTconvF2D_reg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cSvframeArrayElementNon_stack_size6kMiiii_i_; +text: .text%__1cMaddD_regNodePoper_input_base6kM_I_; +text: .text%__1cXjava_lang_boxing_objectJget_value6FpnHoopDesc_pnGjvalue__nJBasicType__; +text: .text%__1cMorL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cOcmovD_regUNodePoper_input_base6kM_I_; +text: .text%__1cPcmovI_reg_gNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMdivD_immNodePoper_input_base6kM_I_; +text: .text%__1cJloadDNodeHtwo_adr6kM_I_; +text: .text%__1cKReflectionTunbox_for_primitive6FpnHoopDesc_pnGjvalue_pnGThread__nJBasicType__; +text: .text%__1cCosMset_priority6FpnGThread_nOThreadPriority__nIOSReturn__; +text: .text%__1cMmulF_memNodeJnum_opnds6kM_I_; +text: .text%__1cIMulDNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cODeoptimizationVtrap_state_add_reason6Fii_i_; +text: .text%__1cDhpiFclose6Fi_i_; +text: .text%__1cJMemRegionFminus6kM0_0_; +text: .text%__1cMmulD_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_i_v_; +text: .text%__1cNcmovL_regNodeMideal_Opcode6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerWupdate_mdp_by_constant6MpnMRegisterImpl_i_v_; +text: .text%__1cOtailjmpIndNodeNis_block_proj6kM_pknENode__; +text: .text%__1cRaddL_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cTconvI2F_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvL2F_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cGICStubKcached_oop6kM_pnHoopDesc__; +text: .text%__1cRInlineCacheBufferUic_buffer_cached_oop6FpC_pnHoopDesc__; +text: .text%__1cTconvD2F_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cJAssemblerFpushq6Mi_v_; +text: .text%JVM_Close; +text: .text%__1cMnegF_regNodePoper_input_base6kM_I_; +text: .text%__1cOcmovI_regUNodeJnum_opnds6kM_I_; +text: .text%__1cbCAbstractInterpreterGeneratorRset_unimplemented6Mi_v_; +text: .text%__1cRComputeEntryStackJdo_object6Mii_v_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cOcmovI_regUNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cGThreadMset_priority6Fp0nOThreadPriority__v_; +text: .text%jni_NewObjectV: jni.o; +text: .text%__1cKConv2BNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQshrL_rReg_CLNodeErule6kM_I_; +text: .text%__1cTconvF2D_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHnmethodbCcan_not_entrant_be_converted6M_i_; +text: .text%__1cNSafePointNodeQpeek_monitor_obj6kM_pnENode__; +text: .text%__1cOcmovI_regUNodeMcisc_operand6kM_i_; +text: .text%__1cNSafePointNodeQpeek_monitor_box6kM_pnENode__; +text: .text%__1cJLoadBNodeMstore_Opcode6kM_i_; +text: .text%__1cVCompressedWriteStreamKwrite_long6Mx_v_; +text: .text%__1cTconvF2I_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cLConvF2INodeGOpcode6kM_i_; +text: .text%__1cPMultiBranchDataScompute_cell_count6FpnOBytecodeStream__i_; +text: .text%__1cFParsePdo_monitor_exit6M_v_; +text: .text%__1cMStubCodeMark2T6M_v_; +text: .text%__1cMStubCodeMark2t6MpnRStubCodeGenerator_pkc4_v_; +text: .text%__1cRStubCodeGeneratorLstub_epilog6MpnMStubCodeDesc__v_; +text: .text%__1cRStubCodeGeneratorLstub_prolog6MpnMStubCodeDesc__v_; +text: .text%__1cKcastPPNodeErule6kM_I_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pF3_v3_v_; +text: .text%__1cOsalI_mem_1NodeJnum_opnds6kM_I_; +text: .text%__1cKJavaThreadbFdeoptimized_wrt_marked_nmethods6M_v_; +text: .text%__1cKJavaThreadHprepare6MpnI_jobject_nOThreadPriority__v_; +text: .text%__1cPshrL_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cRandI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cRandI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cQorI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cJAssemblerGmovslq6MpnMRegisterImpl_2_v_; +text: .text%__1cRandI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQjava_lang_ThreadJstackSize6FpnHoopDesc__x_; +text: .text%__1cRConstantLongValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%JVM_StartThread; +text: .text%__1cMthread_entry6FpnKJavaThread_pnGThread__v_: jvm.o; +text: .text%__1cTconvF2D_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMsubD_regNodeErule6kM_I_; +text: .text%__1cQjava_lang_ThreadIpriority6FpnHoopDesc__nOThreadPriority__; +text: .text%__1cNmulI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cIMulFNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cLRuntimeStub2n6FLI_pv_; +text: .text%__1cLRuntimeStubQnew_runtime_stub6FpkcpnKCodeBuffer_ipnJOopMapSet_i_p0_; +text: .text%__1cLRuntimeStub2t6MpkcpnKCodeBuffer_iipnJOopMapSet_i_v_; +text: .text%__1cTconvF2D_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cRxorI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMmulF_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOLibraryCallKitbDis_method_invoke_or_aux_frame6MpnIJVMState__i_; +text: .text%__1cbFloadConL_0x6666666666666667NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAddFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOloadConL32NodeHsize_of6kM_I_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFn0AJOperation__v4_v_; +text: .text%__1cRaddL_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivLNodeJideal_reg6kM_I_; +text: .text%__1cGICStubFclear6M_v_; +text: .text%__1cTconvI2D_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMsubD_regNodeJnum_opnds6kM_I_; +text: .text%__1cMsubD_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cHCompileWget_MethodAccessorImpl6M_pnPciInstanceKlass__; +text: .text%__1cHCompileRget_Method_invoke6M_pnIciMethod__; +text: .text%__1cNdecI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFTypeFFxdual6kM_pknEType__; +text: .text%__1cTconvL2D_reg_memNodeJnum_opnds6kM_I_; +text: .text%__1cTconvI2D_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cTunsafe_intrinsic_id6FpnNsymbolOopDesc_1_nMvmIntrinsicsCID__; +text: .text%__1cPPerfDataManagerUcreate_long_constant6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongConstant__; +text: .text%__1cFStateM_sub_Op_ConF6MpknENode__v_; +text: .text%__1cMloadConFNodeHsize_of6kM_I_; +text: .text%__1cPsarL_rReg_2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsarL_rReg_63NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPoldgetTimeNanos6F_x_; +text: .text%__1cPno_rax_RegLOperFclone6kM_pnIMachOper__; +text: .text%__1cTAbstractInterpreterMreturn_entry6FnITosState_i_pC_; +text: .text%__1cPsarL_rReg_1NodePoper_input_base6kM_I_; +text: .text%__1cMnegD_regNodeJnum_opnds6kM_I_; +text: .text%__1cKmul_hiNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerEjccb6Mn0AJCondition_rnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cNcmovP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHMonitor2T6M_v_; +text: .text%__1cINegDNodeLbottom_type6kM_pknEType__; +text: .text%__1cLvframeArrayIallocate6FpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pnLRegisterMap_nFframe_9A9A9A_p0_; +text: .text%__1cNThreadServiceNremove_thread6FpnKJavaThread_i_v_; +text: .text%__1cQjava_lang_ThreadbGinherited_access_control_context6FpnHoopDesc__2_; +text: .text%__1cODeoptimizationScreate_vframeArray6FpnKJavaThread_nFframe_pnLRegisterMap__pnLvframeArray__; +text: .text%__1cHThreadsGremove6FpnKJavaThread__v_; +text: .text%__1cODeoptimizationTuncommon_trap_inner6FpnKJavaThread_i_v_; +text: .text%__1cODeoptimizationPget_method_data6FpnKJavaThread_nMmethodHandle_i_pnRmethodDataOopDesc__; +text: .text%__1cLensure_join6FpnKJavaThread__v_: thread.o; +text: .text%__1cIOSThread2T6M_v_; +text: .text%__1cODeoptimizationNuncommon_trap6FpnKJavaThread_i_pn0ALUnrollBlock__; +text: .text%__1cTconvI2D_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationRgather_statistics6Fn0ALDeoptReason_n0ALDeoptAction_nJBytecodesECode__v_; +text: .text%__1cUThreadSafepointStateHdestroy6FpnKJavaThread__v_; +text: .text%__1cIGraphKitTdprecision_rounding6MpnENode__2_; +text: .text%__1cNGrowableArray4CpnOcompiledVFrame__2t6Mii_v_; +text: .text%__1cOcmovI_regUNodeErule6kM_I_; +text: .text%__1cKJavaThreadEexit6Mi_v_; +text: .text%__1cGParker2T6M_v_; +text: .text%__1cCosLfree_thread6FpnIOSThread__v_; +text: .text%__1cXpartialSubtypeCheckNodeMideal_Opcode6kM_i_; +text: .text%JVM_GetInheritedAccessControlContext; +text: .text%__1cNThreadServiceWcurrent_thread_exiting6FpnKJavaThread__v_; +text: .text%__1cLvframeArrayZdeallocate_monitor_chunks6M_v_; +text: .text%__1cGThread2T5B6M_v_; +text: .text%__1cIAddDNodeLbottom_type6kM_pknEType__; +text: .text%__1cKJavaThread2T6M_v_; +text: .text%__1cQjava_lang_ThreadNset_stillborn6FpnHoopDesc__v_; +text: .text%__1cMmulD_memNodePoper_input_base6kM_I_; +text: .text%__1cRInlineCacheBufferSic_destination_for6FpnKCompiledIC__pC_; +text: .text%__1cLvframeArrayPunpack_to_stack6MrnFframe_i_v_; +text: .text%__1cOcompL_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cODeoptimizationLUnrollBlock2t6MiiiiiplppCnJBasicType__v_; +text: .text%__1cLvframeArrayHfill_in6MpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pknLRegisterMap_i_v_; +text: .text%__SLIP.DELETER__A: thread.o; +text: .text%__1cbIjava_security_AccessControlContextGcreate6FnOobjArrayHandle_inGHandle_pnGThread__pnHoopDesc__; +text: .text%__1cIOSThreadKpd_destroy6M_v_; +text: .text%__1cMaddF_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationYfetch_unroll_info_helper6FpnKJavaThread__pn0ALUnrollBlock__; +text: .text%__1cODeoptimizationRlast_frame_adjust6Fii_i_; +text: .text%__1cKJavaThreadYremove_stack_guard_pages6M_v_; +text: .text%__1cODeoptimizationNunpack_frames6FpnKJavaThread_i_nJBasicType__; +text: .text%__1cNnmethodLocker2t6MpC_v_; +text: .text%__1cTconvD2I_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cOtailjmpIndNodeMideal_Opcode6kM_i_; +text: .text%__1cLconvI2BNodeErule6kM_I_; +text: .text%__1cTconvF2I_reg_regNodeErule6kM_I_; +text: .text%__1cIciMethodVget_osr_flow_analysis6Mi_pnKciTypeFlow__; +text: .text%__1cSCallLeafDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQAbstractCompilerMsupports_osr6M_i_; +text: .text%__1cRaddL_mem_rRegNodePoper_input_base6kM_I_; +text: .text%__1cSCallLeafDirectNodeJnum_opnds6kM_I_; +text: .text%__1cXjava_lang_reflect_FieldJmodifiers6FpnHoopDesc__i_; +text: .text%__1cMmulL_memNodePoper_input_base6kM_I_; +text: .text%__1cODeoptimizationLUnrollBlock2T6M_v_; +text: .text%__1cHciKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cMaddF_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cSCompiledStaticCallMset_to_clean6M_v_; +text: .text%__1cMincL_memNodePoper_input_base6kM_I_; +text: .text%jni_NewDirectByteBuffer; +text: .text%__1cMmulL_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMaddD_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHJNIEnv_JNewObject6MpnH_jclass_pnK_jmethodID_E_pnI_jobject__; +text: .text%__1cJAssemblerEmovb6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cJSubFPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_only6MnITosState__v_; +text: .text%__1cRcmpFastUnlockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKloadUBNodeFreloc6kM_i_; +text: .text%__1cMStartOSRNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cMStartOSRNodeKosr_domain6F_pknJTypeTuple__; +text: .text%__1cMloadConPNodeGis_Con6kM_I_; +text: .text%__1cMmulD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXjava_lang_reflect_FieldFclazz6FpnHoopDesc__2_; +text: .text%__1cOPSVirtualSpaceJexpand_by6ML_i_; +text: .text%__1cNCallGeneratorHfor_osr6FpnIciMethod_i_p0_; +text: .text%__1cFParseWload_interpreter_state6MpnENode_2_v_; +text: .text%__1cKstoreINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOstackSlotDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%jni_GetFloatArrayRegion: jni.o; +text: .text%__1cPfieldDescriptorUdouble_initial_value6kM_d_; +text: .text%__1cKEntryPoint2t6M_v_; +text: .text%__1cJAssemblerEsarl6MpnMRegisterImpl_i_v_; +text: .text%__1cSCardTableExtensionbEresize_covered_region_by_start6MnJMemRegion__v_; +text: .text%__1cQshrL_rReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRaddL_mem_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cIAddFNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMlogD_regNodeErule6kM_I_; +text: .text%__1cXpartialSubtypeCheckNodePoper_input_base6kM_I_; +text: .text%__1cNmulI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cMdecI_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsalL_rReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKemit_break6FrnKCodeBuffer__v_; +text: .text%jni_GetStaticMethodID: jni.o; +text: .text%__1cOstackSlotDOperJnum_edges6kM_I_; +text: .text%__1cMsubF_regNodeMcisc_operand6kM_i_; +text: .text%__1cMdecI_memNodeFreloc6kM_i_; +text: .text%__1cMdecI_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRCardTableModRefBSbCfind_covering_region_by_base6MpnIHeapWord__i_; +text: .text%__1cRCardTableModRefBSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cINegFNodeGOpcode6kM_i_; +text: .text%__1cRCardTableModRefBSbAlargest_prev_committed_end6kMi_pnIHeapWord__; +text: .text%__1cLloadSSDNodeJnum_opnds6kM_I_; +text: .text%__1cSMachBreakpointNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCardTableExtensionVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cLconvI2BNodeJnum_opnds6kM_I_; +text: .text%__1cNstoreImmPNodePoper_input_base6kM_I_; +text: .text%__1cKReflectionUarray_component_type6FpnHoopDesc_pnGThread__2_; +text: .text%__1cKoopFactoryUnew_compiledICHolder6FnMmethodHandle_nLKlassHandle_pnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cHCompile2t6MpnFciEnv_pF_pknITypeFunc_pCpkciiii_v_; +text: .text%__1cTconvL2F_reg_regNodeMcisc_operand6kM_i_; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_; +text: .text%__1cNloadConPcNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKarrayKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cFStateM_sub_Op_CmpD6MpknENode__v_; +text: .text%__1cNloadConL0NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cUCallNativeDirectNodeKmethod_set6Ml_v_; +text: .text%__1cKcastPPNodeMideal_Opcode6kM_i_; +text: .text%__1cNcmovL_memNodeJnum_opnds6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_taken_branch6MpnMRegisterImpl_2_v_; +text: .text%__1cPshrL_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cIGraphKitIgen_stub6MpCpkciii_v_; +text: .text%__1cbDcatch_cleanup_find_cloned_def6FpnFBlock_pnENode_1rnLBlock_Array_i_3_: lcm.o; +text: .text%__1cVcompiledICHolderKlassIallocate6MpnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cTC2IAdapterGeneratorUmkh_unverified_entry6FnMmethodHandle__pC_; +text: .text%__1cRaddL_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cLOptoRuntimeNgenerate_stub6FpnFciEnv_pF_pknITypeFunc_pCpkciiii_8_; +text: .text%__1cISubDNodeLbottom_type6kM_pknEType__; +text: .text%__1cbCcatch_cleanup_fix_all_inputs6FpnENode_11_v_: lcm.o; +text: .text%__1cISubFNodeLbottom_type6kM_pknEType__; +text: .text%__1cNdivI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableGbranch6Fii_v_; +text: .text%__1cNstoreImmPNodeHtwo_adr6kM_I_; +text: .text%__1cLOptoRuntimeRnew_objArray_Type6F_pknITypeFunc__; +text: .text%JVM_GetComponentType; +text: .text%__1cIMulDNodeJideal_reg6kM_I_; +text: .text%__1cTconvF2D_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cJAssemblerEsbbq6MpnMRegisterImpl_i_v_; +text: .text%__1cNcmovL_memNodeMideal_Opcode6kM_i_; +text: .text%jni_GetStringRegion: jni.o; +text: .text%jni_EnsureLocalCapacity: jni.o; +text: .text%__1cLloadSSDNodeHtwo_adr6kM_I_; +text: .text%__1cMaddF_memNodePoper_input_base6kM_I_; +text: .text%__1cFParseMdo_anewarray6M_v_; +text: .text%__1cLConvI2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLconvI2BNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC22i_v_; +text: .text%__1cHThreadsYis_supported_jni_version6Fi_C_; +text: .text%__1cMincL_memNodeJnum_opnds6kM_I_; +text: .text%__1cRandL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKarrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cRaddL_mem_rRegNodeJnum_opnds6kM_I_; +text: .text%JVM_NewArray; +text: .text%JVM_FreeMemory; +text: .text%JVM_TotalMemory; +text: .text%__1cMaddD_immNodeJnum_opnds6kM_I_; +text: .text%__1cMsubF_regNodeJnum_opnds6kM_I_; +text: .text%__1cLloadSSINodePoper_input_base6kM_I_; +text: .text%__1cNinstanceKlassPadd_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cMincI_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReflectionRreflect_new_array6FpnHoopDesc_ipnGThread__pnMarrayOopDesc__; +text: .text%__1cMsubF_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMmulF_memNodeErule6kM_I_; +text: .text%__1cODeoptimizationbJupdate_method_data_from_interpreter6FnQmethodDataHandle_ii_v_; +text: .text%__1cLClassLoaderSget_system_package6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cMTailJumpNodeKmatch_edge6kMI_I_; +text: .text%__1cFStateL_sub_Op_Box6MpknENode__v_; +text: .text%__1cRaddL_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTconvL2F_reg_regNodeErule6kM_I_; +text: .text%__1cKPSYoungGenLpost_resize6M_v_; +text: .text%__1cNcmovL_regNodeErule6kM_I_; +text: .text%__1cOcmovD_regUNodeJnum_opnds6kM_I_; +text: .text%__1cRandI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeHtwo_adr6kM_I_; +text: .text%__1cTAbstractInterpreterRTosState_as_index6FnITosState__i_; +text: .text%__1cHThreadsbMis_supported_jni_version_including_1_16Fi_C_; +text: .text%__1cKstoreBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPBytecode_invokeLresult_type6kMpnGThread__nJBasicType__; +text: .text%__1cMincL_memNodeMideal_Opcode6kM_i_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJloadCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosPuncommit_memory6FpcL_i_; +text: .text%__1cSInterpreterRuntimeJnote_trap6FpnKJavaThread_ipnGThread__v_; +text: .text%__1cRSignatureIteratorHiterate6M_v_; +text: .text%__1cIModLNodeJideal_reg6kM_I_; +text: .text%__1cNTemplateTableOpatch_bytecode6FnJBytecodesECode_pnMRegisterImpl_4i_v_; +text: .text%__1cLConvD2INodeLbottom_type6kM_pknEType__; +text: .text%__1cMaddF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHBoxNodeLbottom_type6kM_pknEType__; +text: .text%__1cFStateM_sub_Op_DivL6MpknENode__v_; +text: .text%__1cTconvL2D_reg_memNodeErule6kM_I_; +text: .text%JVM_GetSystemPackage; +text: .text%__1cCosNcommit_memory6FpcLL_i_; +text: .text%__1cOMacroAssemblerFenter6M_v_; +text: .text%__1cLConvF2DNodeJideal_reg6kM_I_; +text: .text%__1cNTemplateTableLindex_check6FpnMRegisterImpl_2_v_; +text: .text%__1cKNativeJumpUpatch_verified_entry6FpC11_v_; +text: .text%__1cLMoveL2DNodeGOpcode6kM_i_; +text: .text%__1cMincI_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateP_sub_Op_ConvF2D6MpknENode__v_; +text: .text%__1cMmulL_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%Unsafe_DefineClass1; +text: .text%__1cSUnsafe_DefineClass6FpnHJNIEnv__pnI_jstring_pnL_jbyteArray_iipnI_jobject_7_pnH_jclass__: unsafe.o; +text: .text%__1cFTypeDFxdual6kM_pknEType__; +text: .text%__1cMincI_memNodeFreloc6kM_i_; +text: .text%__1cPcmpF_cc_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMsubF_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMsubF_memNodePoper_input_base6kM_I_; +text: .text%__1cTconvF2D_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%JVM_DefineClass; +text: .text%__1cMaddF_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMmulL_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cMmulL_memNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerEshrq6MpnMRegisterImpl_i_v_; +text: .text%__1cTC2IAdapterGeneratorLadapter_for6FnMmethodHandle__pnKC2IAdapter__; +text: .text%__1cPcmpFastLockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerQtest_mdp_data_at6MpnMRegisterImpl_i2rnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerYprofile_not_taken_branch6MpnMRegisterImpl__v_; +text: .text%__1cTleaPIdxScaleOffNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cYjava_lang_reflect_MethodFclazz6FpnHoopDesc__2_; +text: .text%__1cYjava_lang_reflect_MethodEslot6FpnHoopDesc__i_; +text: .text%__1cYjava_lang_reflect_MethodLreturn_type6FpnHoopDesc__2_; +text: .text%__1cYjava_lang_reflect_MethodPparameter_types6FpnHoopDesc__2_; +text: .text%__1cNloadConL0NodeGis_Con6kM_I_; +text: .text%__1cMset_property6FnGHandle_pkc2pnGThread__v_: jvm.o; +text: .text%JVM_GetCPFieldModifiers; +text: .text%JVM_InvokeMethod; +text: .text%__1cFKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cZcatch_cleanup_inter_block6FpnENode_pnFBlock_13rnLBlock_Array_i_v_: lcm.o; +text: .text%__1cOsalI_mem_1NodeMideal_Opcode6kM_i_; +text: .text%__1cMaddF_immNodeJnum_opnds6kM_I_; +text: .text%__1cMsubD_immNodePoper_input_base6kM_I_; +text: .text%__1cMmulF_regNodeMcisc_operand6kM_i_; +text: .text%__1cMmulF_regNodeJnum_opnds6kM_I_; +text: .text%__1cMmulF_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cMmulD_regNodeHtwo_adr6kM_I_; +text: .text%__1cTconvD2F_reg_regNodeMcisc_operand6kM_i_; +text: .text%jni_AllocObject: jni.o; +text: .text%__1cCosHSolarisOset_mpss_range6FpcLL_i_; +text: .text%__1cTconvF2I_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFParseOdo_tableswitch6M_v_; +text: .text%__1cTmembar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReflectionNinvoke_method6FpnHoopDesc_nGHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cMrdx_RegLOperFclone6kM_pnIMachOper__; +text: .text%__1cICmpFNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFn0AJCondition__v4_v_; +text: .text%__1cFj_not6FnNTemplateTableJCondition__nJAssemblerJCondition__: templateTable_amd64.o; +text: .text%__1cNTemplateTableMlocals_index6FpnMRegisterImpl_i_v_; +text: .text%__1cTconvF2D_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMmulF_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%Unsafe_AllocateInstance; +text: .text%__1cZInterpreterMacroAssemblerNunlock_object6MpnMRegisterImpl__v_; +text: .text%__1cOcmovD_regUNodeMideal_Opcode6kM_i_; +text: .text%__1cIciObjectMis_classless6kM_i_; +text: .text%__1cMsubD_immNodeMideal_Opcode6kM_i_; +text: .text%__1cRInlineCacheBufferOinit_next_stub6F_v_; +text: .text%__1cPshrL_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_immNodeErule6kM_I_; +text: .text%__1cHTypePtrKadd_offset6kMi_pk0_; +text: .text%__1cNTemplateTableHconvert6F_v_; +text: .text%__1cMnegD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQorI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLMoveF2INodeGOpcode6kM_i_; +text: .text%__1cNcmovL_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_2_v_; +text: .text%__1cOcmovD_regUNodeErule6kM_I_; +text: .text%__1cKConv2BNodeLbottom_type6kM_pknEType__; +text: .text%__1cHnmethodSflush_dependencies6MpnRBoolObjectClosure__v_; +text: .text%__1cMorL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cTconvD2I_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cScompL_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cXpartialSubtypeCheckNodeErule6kM_I_; +text: .text%__1cOstackSlotDOperEtype6kM_pknEType__; +text: .text%__1cHThreadsLnmethods_do6F_v_; +text: .text%__1cLloadSSDNodeErule6kM_I_; +text: .text%__1cMsubD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRComputeEntryStackIdo_short6M_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorCto6F_pnMRegisterImpl__; +text: .text%__1cTconvF2D_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulL_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPciObjectFactoryTget_unloaded_method6MpnPciInstanceKlass_pnIciSymbol_4_pnIciMethod__; +text: .text%__1cLTypeInstPtrOxmeet_unloaded6kMpk0_2_; +text: .text%__1cMloadConFNodeKconst_size6kM_i_; +text: .text%__1cMorL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cMmulD_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMaddD_regNodeMideal_Opcode6kM_i_; +text: .text%__1cTconvI2D_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMnegF_regNodeMideal_Opcode6kM_i_; +text: .text%__1cMloadConFNodeFreloc6kM_i_; +text: .text%__1cILogDNodeLbottom_type6kM_pknEType__; +text: .text%__1cLConvI2DNodeLbottom_type6kM_pknEType__; +text: .text%__1cNstoreImmPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLStrCompNodeJideal_reg6kM_I_; +text: .text%__1cMlogD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFVTuneOdelete_nmethod6FpnHnmethod__v_; +text: .text%__1cMaddD_regNodeMcisc_operand6kM_i_; +text: .text%__1cMaddD_regNodeErule6kM_I_; +text: .text%__1cScompL_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cIAddFNodeJideal_reg6kM_I_; +text: .text%__1cJimmP0OperPconstant_is_oop6kM_i_; +text: .text%__1cJimmP0OperIconstant6kM_l_; +text: .text%__1cNciMethodKlassEmake6F_p0_; +text: .text%__1cETypeJis_finite6kM_i_; +text: .text%__1cHnmethodFflush6M_v_; +text: .text%JVM_GetClassContext; +text: .text%__1cIciObjectTis_type_array_klass6M_i_; +text: .text%__1cNsubL_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cIregFOperFclone6kM_pnIMachOper__; +text: .text%__1cRfind_field_offset6FpnI_jobject_ipnGThread__i_; +text: .text%__1cHBoxNodeJideal_reg6kM_I_; +text: .text%__1cXPartialSubtypeCheckNodeLbottom_type6kM_pknEType__; +text: .text%__1cIciMethod2t6MpnPciInstanceKlass_pnIciSymbol_4_v_; +text: .text%__1cPfieldDescriptorTfloat_initial_value6kM_f_; +text: .text%__1cLloadSSDNodeMideal_Opcode6kM_i_; +text: .text%__1cMsubF_regNodeErule6kM_I_; +text: .text%__1cRsubL_rReg_memNodeFreloc6kM_i_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_pnNsymbolOopDesc_pkc_nGHandle__; +text: .text%__1cTconvL2F_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMmulF_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStubQdDueueKremove_all6M_v_; +text: .text%__1cIDivDNodeLbottom_type6kM_pknEType__; +text: .text%__1cLStatSamplerTget_system_property6FpkcpnGThread__2_; +text: .text%__1cRmethodDataOopDescRbci_to_extra_data6Mii_pnLProfileData__; +text: .text%__1cICodeBlobFflush6M_v_; +text: .text%__1cVMoveF2I_reg_stackNodeMideal_Opcode6kM_i_; +text: .text%__1cNmodL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJArgumentsQPropertyList_add6FppnOSystemProperty_2_v_; +text: .text%__1cKstoreCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmembar_releaseNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cQsalI_rReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSInterpreterRuntimeQcreate_exception6FpnKJavaThread_pc3_v_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_2_v_; +text: .text%__1cJStubQdDueueMremove_first6Mi_v_; +text: .text%__1cQinitialize_class6FnMsymbolHandle_pnGThread__v_: thread.o; +text: .text%__1cJAssemblerFcmovq6Mn0AJCondition_pnMRegisterImpl_nHAddress__v_; +text: .text%__1cXpartialSubtypeCheckNodeJnum_opnds6kM_I_; +text: .text%__1cMmulD_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMaddF_immNodeHtwo_adr6kM_I_; +text: .text%__1cIMulDNodeGmul_id6kM_pknEType__; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_method_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cTconvL2D_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvD2F_reg_regNodeErule6kM_I_; +text: .text%__1cVMoveL2D_reg_stackNodeMideal_Opcode6kM_i_; +text: .text%__1cFStateM_sub_Op_MulD6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_ModL6MpknENode__v_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_pnGThread__v_; +text: .text%__1cXjava_lang_reflect_FieldEslot6FpnHoopDesc__i_; +text: .text%__1cPloadConUL32NodeGis_Con6kM_I_; +text: .text%__1cQshrL_rReg_CLNodeHtwo_adr6kM_I_; +text: .text%__1cKJavaThreadbOcheck_special_condition_for_native_trans6Fp0_v_; +text: .text%__1cODeoptimizationYreset_invocation_counter6FpnJScopeDesc_i_v_; +text: .text%__1cZCallDynamicJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTconvF2I_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cMmulD_memNodeJnum_opnds6kM_I_; +text: .text%__1cHOrLNodeGOpcode6kM_i_; +text: .text%__1cIMulFNodeGmul_id6kM_pknEType__; +text: .text%__1cMnegF_regNodeErule6kM_I_; +text: .text%__1cMsubF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvD2F_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_RawMonitorCreate; +text: .text%__1cOresolve_symbol6Fpkc_pC_: os_solaris.o; +text: .text%__1cMMutableSpaceOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cKCMoveDNodeGOpcode6kM_i_; +text: .text%__1cFParseQdo_monitor_enter6M_v_; +text: .text%__1cPMultiBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cXpartialSubtypeCheckNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvD2INodeJideal_reg6kM_I_; +text: .text%__1cKcastPPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZCallDynamicJavaDirectNodeJnum_opnds6kM_I_; +text: .text%__1cMlogD_regNodeJnum_opnds6kM_I_; +text: .text%Unsafe_CompareAndSwapInt; +text: .text%__1cOstackSlotIOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOstackSlotIOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_ppkc5i_i_: arguments.o; +text: .text%__1cMmulD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNmulI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKimmL32OperFclone6kM_pnIMachOper__; +text: .text%__1cIimmFOperFclone6kM_pnIMachOper__; +text: .text%__1cOMacroAssemblerTset_last_Java_frame6MpnMRegisterImpl_22pC_v_; +text: .text%__1cOindOffset8OperFclone6kM_pnIMachOper__; +text: .text%__1cOMacroAssemblerVreset_last_Java_frame6MpnMRegisterImpl_i_v_; +text: .text%__1cOloadConL32NodeFclone6kM_pnENode__; +text: .text%__1cMloadConFNodeFclone6kM_pnENode__; +text: .text%__1cScompL_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQinstanceRefKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cNTemplateTableRlocals_index_wide6FpnMRegisterImpl__v_; +text: .text%__1cVMoveL2D_reg_stackNodePoper_input_base6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_data_at6MpnMRegisterImpl_i2_v_; +text: .text%__1cKOSRAdapter2n6FLI_pv_; +text: .text%__1cKOSRAdapterPnew_osr_adapter6FpnKCodeBuffer_pnJOopMapSet_ii_p0_; +text: .text%__1cJAssemblerEincl6MnHAddress__v_; +text: .text%__1cKOSRAdapter2t6MpnKCodeBuffer_pnJOopMapSet_iii_v_; +text: .text%__1cTconvI2D_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cNSharedRuntimeRgenerate_osr_blob6Fi_pnKOSRAdapter__; +text: .text%__1cMaddD_regNodeJnum_opnds6kM_I_; +text: .text%__1cbCAbstractInterpreterGeneratorUset_wide_entry_point6MpnITemplate_rpC_v_; +text: .text%__1cMmulF_regNodeErule6kM_I_; +text: .text%__1cIMulFNodeJideal_reg6kM_I_; +text: .text%__1cFStateM_sub_Op_MulF6MpknENode__v_; +text: .text%__1cJOopMapSetQsingular_oop_map6M_pnGOopMap__; +text: .text%__1cHnmethodVmark_as_seen_on_stack6M_v_; +text: .text%__1cMloadConDNodeHsize_of6kM_I_; +text: .text%__1cOcmovI_regUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLconvI2BNodeHtwo_adr6kM_I_; +text: .text%__1cMorL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cQorI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cMaddD_immNodeHtwo_adr6kM_I_; +text: .text%__1cMloadConDNodeKconst_size6kM_i_; +text: .text%__1cLConvL2FNodeLbottom_type6kM_pknEType__; +text: .text%__1cLConvL2DNodeLbottom_type6kM_pknEType__; +text: .text%__1cLloadSSINodeMideal_Opcode6kM_i_; +text: .text%__1cOstackSlotDOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cTconvF2D_reg_memNodeFreloc6kM_i_; +text: .text%__1cLConvD2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTconvL2D_reg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cMloadConDNodeFreloc6kM_i_; +text: .text%JVM_Lseek; +text: .text%__1cPsarL_rReg_1NodeErule6kM_I_; +text: .text%__1cPsarL_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVcompiledICHolderKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cMaddD_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cOstackSlotDOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMorL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMmulF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMlogD_regNodeHtwo_adr6kM_I_; +text: .text%__1cRaddI_mem_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cFStateM_sub_Op_AddF6MpknENode__v_; +text: .text%__1cIXorINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cTconvL2F_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cSstring_compareNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNGrowableArray4CpnKOSRAdapter__Praw_at_put_grow6Mirk14_v_; +text: .text%__1cFStateP_sub_Op_StrComp6MpknENode__v_; +text: .text%__1cTconvL2F_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cUInterpreterGeneratorUgenerate_fixed_frame6Mi_v_; +text: .text%__1cGciType2t6MnJBasicType__v_; +text: .text%__1cMaddF_memNodeJnum_opnds6kM_I_; +text: .text%__1cUInterpreterGeneratorbAgenerate_run_compiled_code6M_v_; +text: .text%__1cScompL_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cUInterpreterGeneratorZgenerate_counter_overflow6MpC_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_counter_incr6MpnFLabel_22_v_; +text: .text%__1cPcmpF_cc_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_; +text: .text%__1cQmulI_mem_immNodePoper_input_base6kM_I_; +text: .text%__1cbCAbstractInterpreterGeneratorXbang_stack_shadow_pages6Mi_v_; +text: .text%__1cZInterpreterMacroAssemblerTnotify_method_entry6M_v_; +text: .text%__1cNdecL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOLibraryCallKitXinline_string_compareTo6M_i_; +text: .text%__1cScompL_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cJAssemblerEsubq6MpnMRegisterImpl_nHAddress__v_; +text: .text%jni_GetEnv; +text: .text%JVM_NanoTime; +text: .text%__1cCosNjavaTimeNanos6F_x_; +text: .text%__1cZInterpreterMacroAssemblerNsuper_call_VM6MpnMRegisterImpl_2pC22_v_; +text: .text%__1cFTypeFJis_finite6kM_i_; +text: .text%__1cRmulI_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOMacroAssemblerRcall_VM_leaf_base6MpCi_v_; +text: .text%__1cScompL_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cHMulNodeGis_Mul6kM_pk0_; +text: .text%__1cETypeEmake6Fn0AFTYPES__pk0_; +text: .text%__1cQmulI_mem_immNodeMideal_Opcode6kM_i_; +text: .text%__1cParrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cJAssemblerLemit_data646MxnJrelocInfoJrelocType_i_v_; +text: .text%__1cJAssemblerFpushq6MnHAddress__v_; +text: .text%__1cIGraphKitSgen_native_wrapper6MpnIciMethod__v_; +text: .text%__1cRComputeEntryStackIdo_array6Mii_v_; +text: .text%__1cPcmpD_cc_immNodeKconst_size6kM_i_; +text: .text%__1cKLoadPCNodeJideal_reg6kM_I_; +text: .text%__1cMorL_rRegNodeErule6kM_I_; +text: .text%__1cUCallNativeDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompP_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cScompP_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cSvframeStreamCommonbFfill_in_compiled_inlined_sender6M_i_; +text: .text%__1cNdivI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cKcastPPNodeJnum_opnds6kM_I_; +text: .text%__1cTconvL2D_reg_memNodeHtwo_adr6kM_I_; +text: .text%__1cOLibraryCallKitbNinline_native_Reflection_getCallerClass6M_i_; +text: .text%__1cOLibraryCallKitZinline_native_Class_query6MnMvmIntrinsicsCID__i_; +text: .text%__1cMnegF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod__v_; +text: .text%__1cKciTypeFlowLStateVectorOdo_null_assert6MpnHciKlass__v_; +text: .text%__1cMsubD_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cJLoadLNodeMstore_Opcode6kM_i_; +text: .text%__1cNGrowableArray4CpnGciType__Egrow6Mi_v_; +text: .text%__1cMdivD_immNodeJnum_opnds6kM_I_; +text: .text%__1cNstoreImmPNodeJnum_opnds6kM_I_; +text: .text%__1cMdivD_immNodeHtwo_adr6kM_I_; +text: .text%__1cLloadSSINodeHtwo_adr6kM_I_; +text: .text%__1cLConvI2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIciMethodMnative_entry6M_pC_; +text: .text%__1cPcmpD_cc_immNodeFreloc6kM_i_; +text: .text%__1cUCallNativeDirectNodeFreloc6kM_i_; +text: .text%__1cNloadConPcNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulD_memNodeErule6kM_I_; +text: .text%jni_CallVoidMethod: jni.o; +text: .text%__1cFStateS_sub_Op_CallNative6MpknENode__v_; +text: .text%__1cFStateO_sub_Op_LoadPC6MpknENode__v_; +text: .text%__1cQAbstractCompilerPsupports_native6M_i_; +text: .text%__1cQorI_rReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbCAbstractInterpreterGeneratorbBgenerate_result_handler_for6MnJBasicType__pC_; +text: .text%__1cMmulF_regNodeHtwo_adr6kM_I_; +text: .text%__1cPsalL_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cQshrI_rReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableQvolatile_barrier6FnJAssemblerQMembar_mask_bits__v_; +text: .text%__1cNdivL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cVMoveL2D_reg_stackNodeErule6kM_I_; +text: .text%__1cRsalI_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNRelocIterator2t6MpnKCodeBuffer_pC3_v_; +text: .text%__1cJBasicLockHmove_to6MpnHoopDesc_p0_v_; +text: .text%__1cYinternal_word_RelocationMforce_target6MpC_v_; +text: .text%__1cOstackSlotIOperEtype6kM_pknEType__; +text: .text%__1cLloadSSINodeJnum_opnds6kM_I_; +text: .text%__1cKPSYoungGenRavailable_to_live6M_L_; +text: .text%__1cOstackSlotIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNcmovL_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cSstore_to_stackslot6FrnKCodeBuffer_iii_v_; +text: .text%__1cFTypeFGis_nan6kM_i_; +text: .text%__1cQshrL_rReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvD2F_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_safept_entry_for6MnITosState_pC_2_; +text: .text%__1cUInterpreterGeneratorbDgenerate_stack_overflow_check6M_v_; +text: .text%__1cRComputeEntryStackHdo_bool6M_v_; +text: .text%__1cMmulD_immNodeFreloc6kM_i_; +text: .text%__1cQmulI_mem_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_continuation_for6MnITosState__pC_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%JVM_FindPrimitiveClass; +text: .text%__1cOstackSlotIOperJnum_edges6kM_I_; +text: .text%JVM_IsSameClassPackage; +text: .text%__1cUInterpreterGeneratorXcheck_for_compiled_code6MrnFLabel__v_; +text: .text%__1cRaddI_mem_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMaddF_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPjava_lang_ClassYcreate_basic_type_mirror6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cLconvI2BNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMaddD_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMlogD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPSVirtualSpaceJshrink_by6ML_i_; +text: .text%__1cTconvD2F_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cRCardTableModRefBSYcommitted_unique_to_self6kMinJMemRegion__1_; +text: .text%__1cNTemplateTableEiop26Fn0AJOperation__v_; +text: .text%__1cFStateN_sub_Op_LoadD6MpknENode__v_; +text: .text%__1cTconvL2F_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cZInterpreterMacroAssemblerRremove_activation6MnITosState_pnMRegisterImpl_iii_v_; +text: .text%__1cZInterpreterMacroAssemblerMdispatch_via6MnITosState_ppC_v_; +text: .text%__1cUInterpreterGeneratorbEgenerate_asm_interpreter_entry6Mi_pC_; +text: .text%__1cPcmpF_cc_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubF_memNodeJnum_opnds6kM_I_; +text: .text%__1cKPSYoungGenUavailable_to_min_gen6M_L_; +text: .text%__1cJAssemblerKrepne_scan6M_v_; +text: .text%__1cJname2type6Fpkc_nJBasicType__; +text: .text%__1cKPSYoungGenbCreset_survivors_after_shrink6M_v_; +text: .text%__1cKPSYoungGenQlimit_gen_shrink6ML_L_; +text: .text%__1cTconvI2D_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateP_sub_Op_ConvI2F6MpknENode__v_; +text: .text%__1cMmulD_immNodeKconst_size6kM_i_; +text: .text%__1cMmulD_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cMmulF_immNodeFreloc6kM_i_; +text: .text%__1cJloadBNodeHsize_of6kM_I_; +text: .text%__1cOcompI_rRegNodeHsize_of6kM_I_; +text: .text%__1cPPerfDataManagerWcreate_string_variable6FnJCounterNS_pkci3pnGThread__pnSPerfStringVariable__; +text: .text%__1cJloadPNodeHsize_of6kM_I_; +text: .text%__1cOtypeArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cOtypeArrayKlassNexternal_name6FnJBasicType__pkc_; +text: .text%Unsafe_StaticFieldOffset; +text: .text%__1cFTypeFFempty6kM_i_; +text: .text%__1cNcmovL_regNodeHtwo_adr6kM_I_; +text: .text%__1cLloadSSDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateO_sub_Op_CMoveP6MpknENode__v_; +text: .text%__1cVVM_ParallelGCSystemGCEdoit6M_v_; +text: .text%__1cETypeFxdual6kM_pk0_; +text: .text%__1cVVM_ParallelGCSystemGC2t6MIInHGCCauseFCause__v_; +text: .text%__1cJCmpF3NodeGOpcode6kM_i_; +text: .text%Unsafe_GetObjectVolatile; +text: .text%__1cMsubD_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLdivL_10NodePoper_input_base6kM_I_; +text: .text%__1cVVM_ParallelGCSystemGCEname6kM_pkc_; +text: .text%__1cOtypeArrayKlassMcreate_klass6FnJBasicType_ipnGThread__pnMklassOopDesc__; +text: .text%Unsafe_EnsureClassInitialized; +text: .text%__1cJAssemblerEjmpb6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cOcmovI_regUNodeHtwo_adr6kM_I_; +text: .text%__1cMaddD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerEmovw6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerGmovsbl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cMrax_RegLOperFclone6kM_pnIMachOper__; +text: .text%__1cMorL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvD2F_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cUParallelScavengeHeapHcollect6MnHGCCauseFCause__v_; +text: .text%__1cJLoadDNodeJideal_reg6kM_I_; +text: .text%__1cQciTypeArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cQmulI_mem_immNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCi_v_; +text: .text%__1cPcheckCastPPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerFpopaq6M_v_; +text: .text%__1cSmembar_acquireNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKimmL10OperJnum_edges6kM_I_; +text: .text%Unsafe_StaticFieldBaseFromField; +text: .text%__1cLcastP2LNodeHsize_of6kM_I_; +text: .text%__1cQmulI_mem_immNodeRis_cisc_alternate6kM_i_; +text: .text%__1cMsubD_regNodeHtwo_adr6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerSnotify_method_exit6MnITosState__v_; +text: .text%__1cRsubI_rReg_memNodeHsize_of6kM_I_; +text: .text%__1cTconvL2F_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cNReservedSpace2t6MpcL_v_; +text: .text%__1cKmul_hiNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSmembar_acquireNodeJnum_opnds6kM_I_; +text: .text%__1cQsarL_rReg_63NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerMemit_arith_b6MiipnMRegisterImpl_i_v_; +text: .text%__1cPsarL_rReg_2NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLdivL_10NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableXresolve_cache_and_index6FipnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableZjvmti_post_fast_field_mod6F_v_; +text: .text%JVM_GC; +text: .text%__1cNTemplateTablePfast_storefield6FnITosState__v_; +text: .text%__1cNTemplateTableQfast_accessfield6FnITosState__v_; +text: .text%__1cScompP_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cIPSOldGenSexpand_to_reserved6M_i_; +text: .text%__1cQmulI_mem_immNodeJnum_opnds6kM_I_; +text: .text%__1cIPSOldGenJexpand_by6ML_i_; +text: .text%__1cIPSOldGenGexpand6ML_v_; +text: .text%__1cIPSOldGenXexpand_and_cas_allocate6ML_pnIHeapWord__; +text: .text%__1cPsarL_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cJAssemblerFtestb6MpnMRegisterImpl_i_v_; +text: .text%__1cXpartialSubtypeCheckNodeHtwo_adr6kM_I_; +text: .text%__1cMsubF_regNodeHtwo_adr6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerRget_constant_pool6MpnMRegisterImpl__v_; +text: .text%__1cRaddL_rReg_memNodeFreloc6kM_i_; +text: .text%__1cVMoveL2D_reg_stackNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompP_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPsarL_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cOGenerateOopMapGdo_jsr6Mi_v_; +text: .text%__1cMmulF_memNodeHtwo_adr6kM_I_; +text: .text%__1cScompP_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cLPSMarkSweepGinvoke6Fpii_v_; +text: .text%__1cOcmovD_regUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNcmovL_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvF2I_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cMmulF_immNodeKconst_size6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerbGget_unsigned_2_byte_index_at_bcp6MpnMRegisterImpl_i_v_; +text: .text%__1cZInterpreterMacroAssemblerRcall_VM_leaf_base6MpCi_v_; +text: .text%__1cNTemplateTableGiconst6Fi_v_; +text: .text%__1cMdecI_memNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosMuser_handler6F_pv_; +text: .text%__1cJloadDNodeFreloc6kM_i_; +text: .text%__1cMincL_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNaddL_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cCosHSolarisSset_signal_handler6Fiii_v_; +text: .text%__1cNinstanceKlassSremove_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cTconvD2F_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cKMemoryPoolLadd_manager6MpnNMemoryManager__v_; +text: .text%__1cMmulD_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLClassLoaderbCupdate_class_path_entry_list6Fpkc_v_; +text: .text%__1cMsubF_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcmovI_regUNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cTconvL2D_reg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cLOptoRuntimeRresolve_call_Type6F_pknITypeFunc__; +text: .text%__1cHciKlassIis_klass6M_i_; +text: .text%__1cPPerfDataManagerKname_space6Fpkci_pc_; +text: .text%__1cKScopeValuePis_constant_int6kM_i_; +text: .text%jni_RegisterNatives: jni.o; +text: .text%Unsafe_GetNativeFloat; +text: .text%__1cMsubF_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%JVM_GetClassDeclaredFields; +text: .text%__1cMsubF_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cJMemRegion2t6M_v_; +text: .text%jni_SetStaticObjectField: jni.o; +text: .text%__1cQsalL_rReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cCosEstat6FpkcpnEstat__i_; +text: .text%__1cJArgumentsRverify_percentage6FLpkc_i_; +text: .text%__1cLOptoRuntimeTmultianewarray_Type6Fi_pknITypeFunc__; +text: .text%__1cRComputeEntryStackHdo_long6M_v_; +text: .text%__1cHnmethodVinvalidate_osr_method6M_v_; +text: .text%__1cMaddF_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cNMemoryManagerIadd_pool6MpnKMemoryPool__v_; +text: .text%jni_SetObjectField: jni.o; +text: .text%__1cLConvD2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJAssemblerEcall6MpnMRegisterImpl_nJrelocInfoJrelocType__v_; +text: .text%__1cJloadDNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLConvD2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOBasicHashtable2t6Mii_v_; +text: .text%__1cCosHSolarisOis_sig_ignored6Fi_i_; +text: .text%__1cNandI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNcmovL_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cOPhaseIdealLoopJclone_iff6MpnHPhiNode_pnNIdealLoopTree__pnIBoolNode__; +text: .text%__1cMTailJumpNodeGOpcode6kM_i_; +text: .text%__1cCosHSolarisVcleanup_interruptible6FpnKJavaThread__v_; +text: .text%__1cCosHSolarisTsetup_interruptible6F_pnKJavaThread__; +text: .text%__1cCosHSolarisTsetup_interruptible6FpnKJavaThread__v_; +text: .text%__1cMdivD_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_IsSupportedJNIVersion; +text: .text%JVM_LoadLibrary; +text: .text%JVM_Sleep; +text: .text%__1cNReservedSpaceKinitialize6MLLipc_v_; +text: .text%__1cHOrLNodeLbottom_type6kM_pknEType__; +text: .text%__1cOstackSlotIOperFscale6kM_i_; +text: .text%__1cLConvD2FNodeLbottom_type6kM_pknEType__; +text: .text%__1cLConvF2INodeLbottom_type6kM_pknEType__; +text: .text%jint_cmp: parse2.o; +text: .text%__1cOstackSlotIOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cLloadSSINodeErule6kM_I_; +text: .text%__1cLConvI2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cVMoveF2I_reg_stackNodePoper_input_base6kM_I_; +text: .text%__1cLConvL2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIDivDNodeJideal_reg6kM_I_; +text: .text%__1cRandI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_int_field06FnJBasicType__pC_; +text: .text%jni_GetJavaVM: jni.o; +text: .text%__1cPPerfLongVariant2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_pnUPerfLongSampleHelper__v_; +text: .text%jni_MonitorExit: jni.o; +text: .text%jni_MonitorEnter: jni.o; +text: .text%__1cHciKlass2t6MnLKlassHandle_pnIciSymbol__v_; +text: .text%__1cPciInstanceKlassbDcompute_shared_is_initialized6M_i_; +text: .text%__1cNGrowableArray4CpnIPerfData__Praw_at_put_grow6Mirk14_v_; +text: .text%__1cFciEnvOrecord_failure6Mpkc_v_; +text: .text%__1cMciArrayKlassRbase_element_type6M_pnGciType__; +text: .text%__1cLConvL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOstackSlotDOperFscale6kM_i_; +text: .text%__1cOstackSlotDOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOcmovI_regUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKReturnNodeUdepends_only_on_test6kM_i_; +text: .text%__1cNcmovL_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvD2F_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTconvF2I_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvL2F_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_immNodeJnum_opnds6kM_I_; +text: .text%__1cVMoveL2D_reg_stackNodeJnum_opnds6kM_I_; +text: .text%__1cRaddI_mem_rRegNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNTemplateTableH_return6FnITosState__v_; +text: .text%__1cNTemplateTableHif_icmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_0cmp6Fn0AJCondition__v_; +text: .text%__1cTconvL2D_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXpartialSubtypeCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSharedRuntimeEdrem6Fdd_d_; +text: .text%__1cRaddI_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMloadConDNodeFclone6kM_pnENode__; +text: .text%__1cScompP_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKC2IAdapterXreturn_from_interpreter6M_pC_; +text: .text%__1cKC2IAdapterRsetup_stack_frame6MnFframe_pnLvframeArray__v_; +text: .text%__1cIregDOperFclone6kM_pnIMachOper__; +text: .text%__1cJAssemblerGmovswl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cMsubF_memNodeErule6kM_I_; +text: .text%__1cIimmDOperFclone6kM_pnIMachOper__; +text: .text%__1cOMacroAssemblerQload_signed_byte6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cKC2IAdapterSunpack_c2i_adapter6MnFframe_1pnLvframeArray__v_; +text: .text%__1cNdivI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRMachSpillCopyNodeHsize_of6kM_I_; +text: .text%__1cFframebFset_interpreter_frame_sender_sp6Mpl_v_; +text: .text%__1cPsarL_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cULinearLeastSquareFit2t6MI_v_; +text: .text%__1cSObjectSynchronizerIjni_exit6FpnHoopDesc_pnGThread__v_; +text: .text%__1cSObjectSynchronizerJjni_enter6FnGHandle_pnGThread__v_; +text: .text%__1cMaddF_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulF_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosZvm_allocation_granularity6F_i_; +text: .text%__1cJAssemblerFpopfq6M_v_; +text: .text%__1cCosOreserve_memory6FLpc_1_; +text: .text%Unsafe_ObjectFieldOffset; +text: .text%__1cUParallelScavengeHeapEkind6M_nNCollectedHeapEName__; +text: .text%__1cKMemoryPool2t6Mpkcn0AIPoolType_LLii_v_; +text: .text%__1cNSpaceCounters2t6MpkciLpnMMutableSpace_pnSGenerationCounters__v_; +text: .text%__1cMNativeLookupTbase_library_lookup6Fpkc22_pC_; +text: .text%__1cMaddF_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cOcompiledVFrameUresolve_monitor_lock6kMnILocation__pnJBasicLock__; +text: .text%__1cTconvD2I_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cQjava_lang_ThreadKset_daemon6FpnHoopDesc__v_; +text: .text%__1cKCompiledICSset_to_megamorphic6MpnICallInfo_nJBytecodesECode_pnGThread__v_; +text: .text%__1cNReservedSpaceKfirst_part6MLii_0_; +text: .text%__1cNCellTypeStateImake_any6Fi_0_; +text: .text%__1cMorL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cNTemplateTableFaload6Fi_v_; +text: .text%__1cISubFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMnegF_regNodeJnum_opnds6kM_I_; +text: .text%__1cINegDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKCompiledICOis_megamorphic6kM_i_; +text: .text%__1cXNativeSignatureIteratorJdo_double6M_v_; +text: .text%__1cISubDNodeGadd_id6kM_pknEType__; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_double6M_v_; +text: .text%__1cNTemplateTableElop26Fn0AJOperation__v_; +text: .text%__1cNTemplateTableEfop26Fn0AJOperation__v_; +text: .text%__1cNTemplateTableEdop26Fn0AJOperation__v_; +text: .text%__1cMaddD_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMMonitorChunk2t6Mi_v_; +text: .text%__1cLMoveL2DNodeLbottom_type6kM_pknEType__; +text: .text%__1cZCompiledArgumentOopFinderDset6MinJBasicType__v_; +text: .text%__1cNstoreImmPNodeFreloc6kM_i_; +text: .text%__1cNSharedRuntimeVhandle_ic_miss_helper6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cLOptoRuntimebBhandle_wrong_method_ic_miss6FpnKJavaThread__pC_; +text: .text%__1cKJavaThreadUremove_monitor_chunk6MpnMMonitorChunk__v_; +text: .text%__1cKJavaThreadRadd_monitor_chunk6MpnMMonitorChunk__v_; +text: .text%__1cNReservedSpace2t6ML_v_; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnQPerfLongVariable__; +text: .text%__1cNmulL_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNmulI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTGeneratePairingInfoOreport_results6kM_i_; +text: .text%Unsafe_GetNativeByte; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2T6M_v_; +text: .text%__1cFframebLprevious_monitor_in_interpreter_frame6kMpnPBasicObjectLock__2_; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2t6M_v_; +text: .text%__1cOMacroAssemblerQload_signed_word6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cQOopMapCacheEntryPfill_for_native6M_v_; +text: .text%__1cFStateP_sub_Op_ConvD2I6MpknENode__v_; +text: .text%__1cJAssemblerGpushfq6M_v_; +text: .text%__1cKVtableStubRpd_code_alignment6F_i_; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_2_v_; +text: .text%__1cIDivFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cINegDNodeJideal_reg6kM_I_; +text: .text%__1cODeoptimizationZtrap_state_set_recompiled6Fii_i_; +text: .text%__1cPshrL_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTconvF2D_reg_memNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_; +text: .text%__1cNandI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTMaskFillerForNative2t6MnMmethodHandle_pLi_v_; +text: .text%__1cMsubF_memNodeHtwo_adr6kM_I_; +text: .text%__1cINegFNodeLbottom_type6kM_pknEType__; +text: .text%__1cRaddL_rReg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cLVtableStubsLcreate_stub6FiipnNmethodOopDesc__pC_; +text: .text%__1cMmulL_memNodeFreloc6kM_i_; +text: .text%__1cLVtableStubsGlookup6Fiii_pnKVtableStub__; +text: .text%__1cMMonitorValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cFStateM_sub_Op_NegD6MpknENode__v_; +text: .text%__1cOtailjmpIndNodePoper_input_base6kM_I_; +text: .text%__1cNStubGeneratorYgenerate_throw_exception6MpkcpCi_3_; +text: .text%__1cISubDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPPerfDataManagerKname_space6Fpkc2i_pc_; +text: .text%__1cLVtableStubsOis_entry_point6FpC_i_; +text: .text%__1cNstoreImmPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHRetNodeJnum_opnds6kM_I_; +text: .text%__1cIDivINodeJideal_reg6kM_I_; +text: .text%__1cRInvocationCounterDdef6Fn0AFState_ipFnMmethodHandle_pnGThread__pC_v_; +text: .text%__1cMNativeLookupNlong_jni_name6FnMmethodHandle__pc_; +text: .text%__1cMaddF_memNodeErule6kM_I_; +text: .text%__1cOcmovD_regUNodeHtwo_adr6kM_I_; +text: .text%__1cMaddF_memNodeHtwo_adr6kM_I_; +text: .text%__1cOGenerateOopMapTret_jump_targets_do6MpnOBytecodeStream_pFp0ipi_vi4_v_; +text: .text%__1cOClassPathEntry2t6M_v_; +text: .text%__1cMorL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cOMacroAssemblerNpop_CPU_state6M_v_; +text: .text%__1cMmulF_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cOMacroAssemblerOpush_CPU_state6M_v_; +text: .text%__1cOMacroAssemblerNpop_FPU_state6M_v_; +text: .text%__1cOMacroAssemblerOpush_FPU_state6M_v_; +text: .text%__1cOMacroAssemblerMpop_IU_state6M_v_; +text: .text%__1cOMacroAssemblerNpush_IU_state6M_v_; +text: .text%__1cOMacroAssemblerSstore_check_part_26MpnMRegisterImpl__v_; +text: .text%__1cTconvL2D_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOMacroAssemblerSstore_check_part_16MpnMRegisterImpl__v_; +text: .text%__1cRaddL_rReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMaddF_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRClassPathZipEntry2t6Mppvpc_v_; +text: .text%__1cNTemplateTableOprepare_invoke6FpnMRegisterImpl_2inJBytecodesECode__v_; +text: .text%__1cVMoveF2I_reg_stackNodeErule6kM_I_; +text: .text%__1cJAssemblerEandq6MpnMRegisterImpl_2_v_; +text: .text%__1cFParsePdo_lookupswitch6M_v_; +text: .text%__1cLConvF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFParseRjump_if_true_fork6MpnGIfNode_ii_v_; +text: .text%__1cIAddDNodeJideal_reg6kM_I_; +text: .text%__1cJloadFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRComputeEntryStackJdo_double6M_v_; +text: .text%__1cMaddD_regNodeHtwo_adr6kM_I_; +text: .text%__1cLConvD2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTablebAload_invoke_cp_cache_entry6FipnMRegisterImpl_22ii_v_; +text: .text%__1cNTemplateTableZload_field_cp_cache_entry6FipnMRegisterImpl_22i_v_; +text: .text%__1cJAssemblerEcmpb6MnHAddress_i_v_; +text: .text%__1cCosGsignal6Fipv_1_; +text: .text%__1cLClassLoaderSget_canonical_path6Fpc1i_i_; +text: .text%__1cLClassLoaderXcreate_class_path_entry6FpcnEstat_ppnOClassPathEntry__v_; +text: .text%__1cMsubD_immNodeHtwo_adr6kM_I_; +text: .text%__1cLklassVtableTis_miranda_entry_at6Mi_i_; +text: .text%__1cKPSScavengeZclean_up_failed_promotion6F_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fii_v_; +text: .text%JVM_Available; +text: .text%__1cJAssemblerHucomiss6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerRprofile_checkcast6MipnMRegisterImpl__v_; +text: .text%__1cIAddDNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJAssemblerFimulq6MpnMRegisterImpl_2_v_; +text: .text%__1cIRetTableUfind_jsrs_for_target6Mi_pnNRetTableEntry__; +text: .text%__1cNRegisterSaverWrestore_live_registers6FpnOMacroAssembler__v_; +text: .text%__1cLClassLoaderLadd_to_list6FpnOClassPathEntry__v_; +text: .text%__1cNTemplateTableGastore6Fi_v_; +text: .text%__1cNTemplateTableGdstore6Fi_v_; +text: .text%__1cNTemplateTableGfstore6Fi_v_; +text: .text%__1cNTemplateTableGlstore6Fi_v_; +text: .text%__1cNRegisterSaverTsave_live_registers6FpnOMacroAssembler_ipi_pnGOopMap__; +text: .text%__1cNTemplateTableGistore6Fi_v_; +text: .text%__1cIRetTableHadd_jsr6Mii_v_; +text: .text%__1cMincL_memNodeHtwo_adr6kM_I_; +text: .text%__1cKPSYoungGenOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cNGrowableArray4CpnLmarkOopDesc__2t6Mii_v_; +text: .text%__1cUCompressedReadStreamJread_long6M_x_; +text: .text%__1cISubDNodeJideal_reg6kM_I_; +text: .text%__1cWNonPrintingResourceObj2n6FLnLResourceObjPallocation_type__pv_; +text: .text%__1cNTemplateTableFdload6Fi_v_; +text: .text%__1cNTemplateTableFfload6Fi_v_; +text: .text%__1cPaddress_of_flag6FnXCommandLineFlagWithType__pnEFlag__: globals.o; +text: .text%__1cNTemplateTableFlload6Fi_v_; +text: .text%__1cNTemplateTableFiload6Fi_v_; +text: .text%__1cMmulI_memNodePoper_input_base6kM_I_; +text: .text%__1cOcompL_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNGrowableArray4CpnLmarkOopDesc__Uclear_and_deallocate6M_v_; +text: .text%__1cMLinkResolverbBlookup_method_in_interfaces6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cOcompI_rRegNodeFclone6kM_pnENode__; +text: .text%__1cRsubI_rReg_memNodeFclone6kM_pnENode__; +text: .text%__1cLcastP2LNodeFclone6kM_pnENode__; +text: .text%__1cKExceptionsK_throw_oop6FpnGThread_pkcipnHoopDesc__v_; +text: .text%__1cRaddL_rReg_memNodeErule6kM_I_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl__v_; +text: .text%__1cOsalI_mem_1NodeHtwo_adr6kM_I_; +text: .text%__1cHRetNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVMoveL2D_reg_stackNodeHtwo_adr6kM_I_; +text: .text%__1cRaddL_mem_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cJloadPNodeFclone6kM_pnENode__; +text: .text%__1cJloadBNodeFclone6kM_pnENode__; +text: .text%__1cRaddL_rReg_memNodeHtwo_adr6kM_I_; +text: .text%__1cMmulF_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMaddF_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerEmovb6MnHAddress_i_v_; +text: .text%__1cIAddDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%Unsafe_AllocateMemory; +text: .text%__1cVMoveF2I_reg_stackNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerHfxrstor6MnHAddress__v_; +text: .text%__1cJAssemblerGfxsave6MnHAddress__v_; +text: .text%__1cHCompilePget_invoke_name6M_pnIciSymbol__; +text: .text%__1cJAssemblerEsetb6Mn0AJCondition_pnMRegisterImpl__v_; +text: .text%__1cNxorI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cMVM_OperationNdoit_prologue6M_i_; +text: .text%__1cNGCTaskManagerGthread6MI_pnMGCTaskThread__; +text: .text%__1cRConstantLongValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cRConstantLongValueQis_constant_long6kM_i_; +text: .text%__1cKScopeValuePis_constant_oop6kM_i_; +text: .text%__1cKScopeValueSis_constant_double6kM_i_; +text: .text%__1cMmulD_memNodeHtwo_adr6kM_I_; +text: .text%__1cVMoveF2I_reg_stackNodeHtwo_adr6kM_I_; +text: .text%jni_CallStaticObjectMethod: jni.o; +text: .text%__1cNcmovL_memNodeHtwo_adr6kM_I_; +text: .text%__1cFStateM_sub_Op_AddD6MpknENode__v_; +text: .text%__1cMmulI_memNodeMideal_Opcode6kM_i_; +text: .text%__1cScompL_rReg_memNodeFreloc6kM_i_; +text: .text%__1cLloadSSINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNGrowableArray4CpnIPerfData__2t6Mii_v_; +text: .text%__1cOCompilerThreadSis_Compiler_thread6kM_i_; +text: .text%__1cTjava_lang_ThrowableLset_message6FpnHoopDesc_2_v_; +text: .text%__1cMPerfDataList2T6M_v_; +text: .text%__1cLVtableStubsFenter6FiiipnKVtableStub__v_; +text: .text%__1cMmulI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOcmovD_regUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNCompileBrokerUmake_compiler_thread6FpkcpnMCompileQdDueue_pnQCompilerCounters_pnGThread__pnOCompilerThread__; +text: .text%__1cNnegI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cbCAbstractInterpreterGeneratorbHgenerate_exception_handler_common6Mpkc2i_pC_; +text: .text%__1cSCommandLineFlagsExKis_default6FnPCommandLineFlag__i_; +text: .text%__1cJAssemblerEnegl6MpnMRegisterImpl__v_; +text: .text%__1cUConstantOopReadValuePis_constant_oop6kM_i_; +text: .text%__1cHMatcherNlogDSupported6F_ki_; +text: .text%__1cOGenerateOopMapRdo_multianewarray6Mii_v_; +text: .text%__1cLconvI2BNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cUPSGenerationCounters2t6MpkciipnOPSVirtualSpace__v_; +text: .text%__1cMPerfDataList2t6Mi_v_; +text: .text%__1cFStateP_sub_Op_ConvI2D6MpknENode__v_; +text: .text%__1cQCompilerCounters2t6MpkcipnGThread__v_; +text: .text%__1cJCodeCachebCmake_marked_nmethods_zombies6F_v_; +text: .text%__1cNVM_DeoptimizeEname6kM_pkc_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC2_v_; +text: .text%__1cHThreadsbFdeoptimized_wrt_marked_nmethods6F_v_; +text: .text%__1cSCommandLineFlagsExJboolAtPut6FnXCommandLineFlagWithType_i_v_; +text: .text%__1cMmulI_memNodeJnum_opnds6kM_I_; +text: .text%__1cFStateM_sub_Op_CmpF6MpknENode__v_; +text: .text%__1cODeoptimizationVdeoptimize_dependents6F_i_; +text: .text%__1cOtailjmpIndNodeGpinned6kM_i_; +text: .text%__1cQshrL_rReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerGmovzbl6MpnMRegisterImpl_2_v_; +text: .text%__1cILogDNodeJideal_reg6kM_I_; +text: .text%__1cMmulI_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cRaddL_mem_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMincL_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateO_sub_Op_Conv2B6MpknENode__v_; +text: .text%__1cNcmovL_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_CallStaticObjectMethodV: jni.o; +text: .text%__1cMOopTaskQdDueueKinitialize6M_v_; +text: .text%__1cMmulD_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMOopTaskQdDueue2t6M_v_; +text: .text%__1cOLibraryCallKitbBinline_native_currentThread6M_i_; +text: .text%__1cNMemoryManager2t6M_v_; +text: .text%__1cMaddF_immNodeKconst_size6kM_i_; +text: .text%__1cVMoveL2D_reg_stackNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOLibraryCallKitSinline_math_native6MnMvmIntrinsicsCID__i_; +text: .text%__1cFciEnvbNArrayIndexOutOfBoundsException_instance6M_pnKciInstance__; +text: .text%__1cMsubD_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMaddF_immNodeFreloc6kM_i_; +text: .text%__1cMaddD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOCompilerThreadbCis_hidden_from_external_view6kM_i_; +text: .text%__1cNReservedSpaceJlast_part6ML_0_; +text: .text%__1cMnegF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMaddD_immNodeFreloc6kM_i_; +text: .text%jni_IsInstanceOf: jni.o; +text: .text%__1cMaddD_immNodeKconst_size6kM_i_; +text: .text%jni_Throw: jni.o; +text: .text%__1cRmulI_rReg_immNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cOsalI_mem_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSPSPromotionManager2t6M_v_; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cOLibraryCallKitXgenerate_current_thread6MrpnENode__2_; +text: .text%__1cMsubF_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerFmovss6MpnRFloatRegisterImpl_2_v_; +text: .text%JVM_GetLastErrorString; +text: .text%__1cJAssemblerFmovsd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerTprofile_switch_case6MpnMRegisterImpl_22_v_; +text: .text%__1cZInterpreterMacroAssemblerWprofile_switch_default6MpnMRegisterImpl__v_; +text: .text%__1cFStateM_sub_Op_SubF6MpknENode__v_; +text: .text%__1cLVtableStubsScreate_vtable_stub6Fii_pnKVtableStub__; +text: .text%JVM_GetInterfaceVersion; +text: .text%__1cKstoreBNodeErule6kM_I_; +text: .text%__1cKVtableStub2n6FLi_pv_; +text: .text%__1cJAssemblerEdecq6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_virtual_call6MpnMRegisterImpl_22_v_; +text: .text%__1cOtailjmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosFyield6F_v_; +text: .text%__1cRaddI_mem_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOPSVirtualSpace2t6MnNReservedSpace_L_v_; +text: .text%__1cOLibraryCallKitMinline_trans6MnMvmIntrinsicsCID__i_; +text: .text%Unsafe_SetMemory; +text: .text%__1cCosIjvm_path6Fpci_v_; +text: .text%__1cJTimeStamp2t6M_v_; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_2i_v_; +text: .text%__1cLconvI2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorKpass_float6M_v_; +text: .text%__1cISubFNodeJideal_reg6kM_I_; +text: .text%__1cNGrowableArray4CpnIPerfData__Egrow6Mi_v_; +text: .text%__1cMSysClassPathNreset_item_at6Mi_v_; +text: .text%__1cFStateM_sub_Op_LogD6MpknENode__v_; +text: .text%__1cFTypeDFempty6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_2i_v_; +text: .text%__1cJlookupOne6FpnHJNIEnv__pkcpnGThread__pnH_jclass__: jni.o; +text: .text%__1cLloadSSINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosGgetenv6Fpkcpci_i_; +text: .text%__1cZInterpreterMacroAssemblerLlock_object6MpnMRegisterImpl__v_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cISubFNodeGadd_id6kM_pknEType__; +text: .text%__1cJArgumentsMbuild_string6Fppcpkc_v_; +text: .text%__1cFStateM_sub_Op_SubD6MpknENode__v_; +text: .text%JVM_RegisterSignal; +text: .text%JVM_FindSignal; +text: .text%__1cVcompiler_thread_entry6FpnKJavaThread_pnGThread__v_: thread.o; +text: .text%__1cMorL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableMfast_xaccess6FnITosState__v_; +text: .text%__1cKConv2BNodeJideal_reg6kM_I_; +text: .text%__1cXNativeSignatureIteratorIdo_float6M_v_; +text: .text%jni_GetDoubleArrayRegion: jni.o; +text: .text%__1cLloadSSDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveF2I_reg_stackNodeJnum_opnds6kM_I_; +text: .text%__1cJArgumentsObuild_jvm_args6Fpkc_v_; +text: .text%__1cOLibraryCallKitMpop_math_arg6M_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerRgen_subtype_check6MpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cFStateO_sub_Op_CMoveL6MpknENode__v_; +text: .text%__1cJArgumentsMadd_property6Fpkc_i_; +text: .text%__1cMVM_OperationNdoit_epilogue6M_v_; +text: .text%__1cOCompilerThread2t6MpnMCompileQdDueue_pnQCompilerCounters__v_; +text: .text%__1cRaddI_mem_rRegNodeFreloc6kM_i_; +text: .text%__1cSInterpreterRuntimebKthrow_ArrayIndexOutOfBoundsException6FpnKJavaThread_pci_v_; +text: .text%__1cVMoveF2I_reg_stackNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableGfconst6Fi_v_; +text: .text%__1cNCompileBrokerUcompiler_thread_loop6F_v_; +text: .text%__1cOtailjmpIndNodeHtwo_adr6kM_I_; +text: .text%__1cQmulI_mem_immNodeFreloc6kM_i_; +text: .text%__1cNincI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNVM_DeoptimizeEdoit6M_v_; +text: .text%__1cLMoveF2INodeLbottom_type6kM_pknEType__; +text: .text%__1cUConstantOopReadValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cRaddI_mem_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMdivD_immNodeKconst_size6kM_i_; +text: .text%__1cMmulD_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cQObjectStartArrayKinitialize6MnJMemRegion__v_; +text: .text%__1cQObjectStartArraySset_covered_region6MnJMemRegion__v_; +text: .text%__1cMsubF_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cUGcThreadCountClosureJdo_thread6MpnGThread__v_; +text: .text%__1cNGrowableArray4CpnTDerivedPointerEntry__Egrow6Mi_v_; +text: .text%__1cOtailjmpIndNodeJnum_opnds6kM_I_; +text: .text%__1cPGCMemoryManagerXinitialize_gc_stat_info6M_v_; +text: .text%__1cPGCMemoryManager2t6M_v_; +text: .text%__1cKGCStatInfo2t6Mi_v_; +text: .text%__1cTMaskFillerForNativeLpass_object6M_v_; +text: .text%__1cTMaskFillerForNativeJpass_long6M_v_; +text: .text%__1cJMarkSweepUAdjustPointerClosure2t6Mi_v_; +text: .text%__1cCosHrealloc6FpvL_1_; +text: .text%__1cCosWactive_processor_count6F_i_; +text: .text%__1cSestimate_path_freq6FpnENode__f_: loopnode.o; +text: .text%__1cCosNsigexitnum_pd6F_i_; +text: .text%__1cMLinkResolverbEvtable_index_of_miranda_method6FnLKlassHandle_nMsymbolHandle_2pnGThread__i_; +text: .text%__1cOLibraryCallKitVinline_fp_conversions6MnMvmIntrinsicsCID__i_; +text: .text%__1cZcatch_cleanup_intra_block6FpnENode_1pnFBlock_ii_v_: lcm.o; +text: .text%__1cCosbCis_thread_cpu_time_supported6F_i_; +text: .text%__1cLklassVtableQindex_of_miranda6MpnNsymbolOopDesc_2_i_; +text: .text%__1cNdefaultStreamMhas_log_file6M_i_; +text: .text%__1cNcmovL_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cRalign_object_size6Fl_l_; +text: .text%__1cMarrayOopDescLheader_size6FnJBasicType__i_; +text: .text%__1cNstoreImmBNodeErule6kM_I_; +text: .text%__1cNstoreImmINodeErule6kM_I_; +text: .text%__1cLloadSSDNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cFParseMjump_if_join6MpnENode_2_2_; +text: .text%__1cKJNIHandlesQmake_weak_global6FnGHandle__pnI_jobject__; +text: .text%__1cLloadSSINodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_float_field06FnJBasicType__pC_; +text: .text%__1cFParseRdo_multianewarray6M_v_; +text: .text%__1cMloadConDNodeGis_Con6kM_I_; +text: .text%jni_NewWeakGlobalRef: jni.o; +text: .text%__1cPfilename_to_pid6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cTis_directory_secure6Fpkc_i_: perfMemory_solaris.o; +text: .text%jni_CallStaticVoidMethodV: jni.o; +text: .text%jni_CallStaticBooleanMethod: jni.o; +text: .text%__1cNGrowableArray4CpnNmethodOopDesc__Egrow6Mi_v_; +text: .text%__1cRInvocationCounterMreinitialize6Fi_v_; +text: .text%__1cXpartialSubtypeCheckNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTconvF2I_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cUInterpreterGeneratorVgenerate_native_entry6Mi_pC_; +text: .text%__1cUInterpreterGeneratorLlock_method6M_v_; +text: .text%__1cNGrowableArray4CpC_Egrow6Mi_v_; +text: .text%__1cNGrowableArray4CL_Egrow6Mi_v_; +text: .text%__1cObox_handleNodeHsize_of6kM_I_; +text: .text%__1cPsarL_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIPSOldGenKinitialize6MnNReservedSpace_Lpkci_v_; +text: .text%__1cIPSOldGenYinitialize_virtual_space6MnNReservedSpace_L_v_; +text: .text%__1cIPSOldGenPinitialize_work6Mpkci_v_; +text: .text%__1cNdivI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbCAbstractInterpreterGeneratorTgenerate_error_exit6Mpkc_pC_; +text: .text%__1cTAbstractInterpreterKinitialize6F_v_; +text: .text%__1cZInterpreterMacroAssemblerLprofile_ret6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerSprofile_final_call6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerMprofile_call6MpnMRegisterImpl__v_; +text: .text%__1cOPSVirtualSpace2t6M_v_; +text: .text%__1cOPSVirtualSpaceKinitialize6MnNReservedSpace_L_i_; +text: .text%__1cZInterpreterMacroAssemblerSupdate_mdp_for_ret6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_flag_at6MpnMRegisterImpl_i_v_; +text: .text%__1cZInterpreterMacroAssemblerWdispatch_only_noverify6MnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpCpnMRegisterImpl__v_; +text: .text%__1cKReflectionbFbasic_type_arrayklass_to_mirror6FpnMklassOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cMAdapterCache2t6M_v_; +text: .text%__1cSComputeAdapterInfoIdo_array6Mii_v_; +text: .text%__1cGatomll6Fpkcpx_i_: arguments.o; +text: .text%__1cJArgumentsRcheck_memory_size6Fxx_n0AJArgsRange__; +text: .text%__1cJArgumentsVset_parallel_gc_flags6F_v_; +text: .text%__1cYalign_to_allocation_size6FL_L_: heap.o; +text: .text%__1cJArgumentsRparse_memory_size6Fpkcpxx_n0AJArgsRange__; +text: .text%__1cJArgumentsXPropertyList_unique_add6FppnOSystemProperty_pkcpc_v_; +text: .text%__1cQAgentLibraryList2t6M_v_; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_; +text: .text%__1cMmulF_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJAssemblerGmovsbl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerGmovswl6MpnMRegisterImpl_2_v_; +text: .text%__1cLOptoRuntimebDlazy_c2i_adapter_generation_C6FpnKJavaThread__pC_; +text: .text%__1cLOptoRuntimeVgenerate_handler_blob6FpCi_pnNSafepointBlob__; +text: .text%__1cRaddL_mem_rRegNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJAssemblerGmovzwl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerFmovdq6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cRComputeEntryStackIdo_float6M_v_; +text: .text%__1cJAssemblerFcmovl6Mn0AJCondition_pnMRegisterImpl_nHAddress__v_; +text: .text%__1cSReferenceProcessor2t6MnJMemRegion_iii_v_; +text: .text%__1cJAssemblerEaddl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cOGenerateOopMapTadd_to_ref_init_set6Mi_v_; +text: .text%__1cJAssemblerEcmpq6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerHucomisd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cJAssemblerFidivl6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerFidivq6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEcdql6M_v_; +text: .text%__1cJAssemblerEcdqq6M_v_; +text: .text%__1cJAssemblerEleal6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerDorq6MnHAddress_i_v_; +text: .text%__1cWStubGenerator_generate6FpnKCodeBuffer_i_v_; +text: .text%__1cJStubQdDueue2t6MpnNStubInterface_ipnFMutex_pkc_v_; +text: .text%__1cMGCTaskThreadDrun6M_v_; +text: .text%__1cMGCTaskThreadFstart6M_v_; +text: .text%__1cMGCTaskThread2t6MpnNGCTaskManager_II_v_; +text: .text%__1cJStubQdDueueOregister_queue6Fp0_v_; +text: .text%__1cISubFNodeDsub6kMpknEType_3_3_; +text: .text%__1cJAssemblerFxaddl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cNGCTaskManagerKset_thread6MIpnMGCTaskThread__v_; +text: .text%__1cJAssemblerHldmxcsr6MnHAddress__v_; +text: .text%__1cJAssemblerFxorps6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cKcastPPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMPeriodicTask2t6ML_v_; +text: .text%__1cMPeriodicTaskGenroll6M_v_; +text: .text%__1cPOopTaskQdDueueSetOregister_queue6MipnMOopTaskQdDueue__v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC222i_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC22_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fi_v_; +text: .text%__1cNTemplateTableKdouble_cmp6Fi_v_; +text: .text%__1cNTemplateTableKinitialize6F_v_; +text: .text%__1cNTemplateTableGlconst6Fi_v_; +text: .text%__1cNTemplateTableGdconst6Fi_v_; +text: .text%__1cNTemplateTableDldc6Fi_v_; +text: .text%__1cNTemplateTableHcastore6F_v_; +text: .text%__1cPdouble_quadword6Fpxxx_0_: templateTable_amd64.o; +text: .text%__1cNTemplateTableKif_nullcmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_acmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableSgetfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableUjvmti_post_field_mod6Fii_v_; +text: .text%__1cNTemplateTableSputfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableUinvokevirtual_helper6FpnMRegisterImpl_22_v_; +text: .text%__1cEMIN24CL_6FTA0_0_; +text: .text%__1cRCardTableModRefBSbCpar_chunk_heapword_alignment6F_L_; +text: .text%__1cOMacroAssemblerPcorrected_idivl6MpnMRegisterImpl__i_; +text: .text%__1cOMacroAssemblerPcorrected_idivq6MpnMRegisterImpl__i_; +text: .text%__1cLNamedThread2t6M_v_; +text: .text%__1cLNamedThreadIset_name6MpkcE_v_; +text: .text%__1cOMacroAssemblerQserialize_memory6MpnMRegisterImpl_22_v_; +text: .text%__1cIDivDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIDivDNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFStatebB_sub_Op_PartialSubtypeCheck6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_DivI6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_DivD6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_ConvL2F6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_ConvL2D6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_ConvF2I6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_ConvD2F6MpknENode__v_; +text: .text%__1cRcheck_if_clipping6FpknKRegionNode_rpnGIfNode_5_i_: cfgnode.o; +text: .text%__1cWcheck_compare_clipping6FipnGIfNode_pnHConNode_rpnENode__i_: cfgnode.o; +text: .text%__1cIciObjectOis_array_klass6M_i_; +text: .text%__1cScompP_rReg_memNodeFreloc6kM_i_; +text: .text%__1cKCastPPNodeJideal_reg6kM_I_; +text: .text%__1cLMoveF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFTypeDJis_finite6kM_i_; +text: .text%__1cLMoveL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTconvL2D_reg_memNodeFreloc6kM_i_; +text: .text%__1cMdivD_immNodeFreloc6kM_i_; +text: .text%__1cMmulF_memNodeFreloc6kM_i_; +text: .text%__1cMaddF_memNodeFreloc6kM_i_; +text: .text%__1cLConvF2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLConvF2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLConvD2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOcompP_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cKciTypeFlowLStateVectorRdo_multianewarray6MpnQciBytecodeStream__v_; +text: .text%__1cMorI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cRCollectorCounters2t6Mpkci_v_; +text: .text%Unsafe_CompareAndSwapObject; +text: .text%__1cNSafepointBlob2n6FLI_pv_; +text: .text%__1cNSafepointBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNSafepointBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cINegFNodeJideal_reg6kM_I_; +text: .text%__1cMVirtualSpace2t6M_v_; +text: .text%__1cMVirtualSpaceKinitialize6MnNReservedSpace_L_i_; +text: .text%__1cHMatcherQconvL2FSupported6F_ki_; +text: .text%__1cLConvD2FNodeJideal_reg6kM_I_; +text: .text%__1cLConvF2INodeJideal_reg6kM_I_; +text: .text%__1cLConvL2DNodeJideal_reg6kM_I_; +text: .text%__1cLConvL2FNodeJideal_reg6kM_I_; +text: .text%__1cXPartialSubtypeCheckNodeJideal_reg6kM_I_; +text: .text%__1cTAbstract_VM_VersionOvm_info_string6F_pkc_; +text: .text%__1cTAbstract_VM_VersionKvm_release6F_pkc_; +text: .text%__1cTAbstract_VM_VersionXinternal_vm_info_string6F_pkc_; +text: .text%__1cJAssemblerEshrq6MpnMRegisterImpl__v_; +text: .text%__1cMsubF_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cParrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerEsubq6MnHAddress_i_v_; +text: .text%__1cLOptoRuntimeIgenerate6FpnFciEnv__v_; +text: .text%__1cMmulD_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cYSurvivorMutableSpacePool2t6MpnKPSYoungGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cParrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassUoop_is_instanceKlass6kM_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: arguments.o; +text: .text%__1cJArgumentsWPropertyList_get_value6FpnOSystemProperty_pkc_4_; +text: .text%__1cJArgumentsFparse6FpknOJavaVMInitArgs__i_; +text: .text%__1cKNoopGCTaskQcreate_on_c_heap6F_p0_; +text: .text%__1cJArgumentsbSparse_java_tool_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cWResolveOopMapConflictsOreport_results6kM_i_; +text: .text%__1cMCodeHeapPool2t6MpnICodeHeap_pkci_v_; +text: .text%__1cJAssemblerFxchgl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJArgumentsbNparse_java_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJAssemblerFxchgq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJArgumentsVfinalize_vm_init_args6FpnMSysClassPath_i_i_; +text: .text%__1cJArgumentsWparse_each_vm_init_arg6FpknOJavaVMInitArgs_pnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsSparse_vm_init_args6FpknOJavaVMInitArgs__i_; +text: .text%__1cJAssemblerIcmpxchgl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cINegFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJArgumentsZcheck_vm_args_consistency6F_i_; +text: .text%__1cICodeHeap2t6M_v_; +text: .text%__1cJArgumentsbOparse_java_compiler_environment_variable6F_v_; +text: .text%__1cHVM_ExitNset_vm_exited6F_i_; +text: .text%__1cICodeHeapHreserve6MLLL_i_; +text: .text%__1cQRelocationHolder2t6M_v_; +text: .text%__1cICodeHeapFclear6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: relocInfo.o; +text: .text%__1cLsymbolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cJArgumentsSset_bytecode_flags6F_v_; +text: .text%__1cJArgumentsUset_ergonomics_flags6F_v_; +text: .text%__1cJArgumentsbBset_cms_and_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsTset_parnew_gc_flags6F_v_; +text: .text%__1cQno_shared_spaces6F_v_: arguments.o; +text: .text%__1cJArgumentsMget_property6Fpkc_2_; +text: .text%__1cLsymbolKlassOset_alloc_size6MI_v_; +text: .text%__1cLsymbolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cJArgumentsVprocess_settings_file6Fpkcii_i_; +text: .text%__1cNGCTaskManagerKthreads_do6MpnNThreadClosure__v_; +text: .text%__1cNGCTaskManagerKinitialize6M_v_; +text: .text%__1cNGCTaskManager2t6MI_v_; +text: .text%__1cXSynchronizedGCTaskQdDueue2t6MpnLGCTaskQdDueue_pnFMutex__v_; +text: .text%__1cDhpiKinitialize6F_i_; +text: .text%__1cDhpiZinitialize_socket_library6F_i_; +text: .text%__1cDhpiYinitialize_get_interface6FpnIvm_calls__v_; +text: .text%__1cRInlineCacheBufferKinitialize6F_v_; +text: .text%__1cWInlineCacheBuffer_init6F_v_; +text: .text%__1cJAssemblerHclflush6MnHAddress__v_; +text: .text%__1cOAbstractICacheKinitialize6F_v_; +text: .text%__1cLGCTaskQdDueueQcreate_on_c_heap6F_p0_; +text: .text%__1cMSysClassPathQadd_jars_to_path6Fpcpkc_1_; +text: .text%__1cHGCStats2t6M_v_; +text: .text%__1cQGCPolicyCounters2t6Mpkcii_v_; +text: .text%__1cMSysClassPathPexpand_endorsed6M_v_; +text: .text%__1cMSysClassPath2T6M_v_; +text: .text%__1cLicache_init6F_v_; +text: .text%__1cYGCAdaptivePolicyCounters2t6MpkciipnSAdaptiveSizePolicy__v_; +text: .text%__1cHVM_ExitbJwait_for_threads_in_native_to_block6F_i_; +text: .text%__1cJAssemblerHstmxcsr6MnHAddress__v_; +text: .text%__1cJAssemblerFaddss6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerFsubss6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cTICacheStubGeneratorVgenerate_icache_flush6MppFpCii_i_v_; +text: .text%__1cMSysClassPath2t6Mpkc_v_; +text: .text%__1cJArgumentsWinit_system_properties6F_v_; +text: .text%__1cJAssemblerFmulss6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerFdivss6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cJAssemblerFaddsd6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cFChunkbDstart_chunk_pool_cleaner_task6F_v_; +text: .text%__1cJAssemblerFsubsd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cOchunkpool_init6F_v_; +text: .text%__1cJAssemblerFmulsd6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerFdivsd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cQSystemDictionarybAcompute_java_system_loader6FpnGThread__v_; +text: .text%__1cJAssemblerGsqrtsd6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cHVM_ExitEdoit6M_v_; +text: .text%__1cRArgumentOopFinderDset6MinJBasicType__v_; +text: .text%__1cWAdjoiningVirtualSpaces2t6MnNReservedSpace_LLL_v_; +text: .text%__1cUAdjoiningGenerations2t6MnNReservedSpace_LLLLLLL_v_; +text: .text%__1cHOrLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cZCompiledArgumentOopFinderRhandle_oop_offset6M_v_; +text: .text%__1cJAssemblerFxorps6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerFxorpd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cJAssemblerFxorpd6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerJcvtsi2ssl6MpnRFloatRegisterImpl_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerJcvtsi2ssq6MpnRFloatRegisterImpl_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerJcvtsi2sdl6MpnRFloatRegisterImpl_pnMRegisterImpl__v_; +text: .text%__1cFframebAoops_compiled_arguments_do6MnMsymbolHandle_ipknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cJAssemblerJcvtsi2sdq6MpnRFloatRegisterImpl_pnMRegisterImpl__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: adaptiveSizePolicy.o; +text: .text%__1cSAdaptiveSizePolicy2t6ML_v_; +text: .text%__1cFframebDoops_interpreted_arguments_do6MnMsymbolHandle_ipnKOopClosure__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: regmask.o; +text: .text%__1cJAssemblerKcvttss2sil6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cJAssemblerKcvttss2siq6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: indexSet.o; +text: .text%__1cJAssemblerKcvttsd2sil6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cFframeVinterpreter_frame_mdp6kM_pC_; +text: .text%__1cPvm_init_globals6F_v_; +text: .text%__1cJAssemblerKcvttsd2siq6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cQSystemDictionaryKclasses_do6FpFpnMklassOopDesc__v_v_; +text: .text%__1cQSystemDictionaryKmethods_do6FpFpnNmethodOopDesc__v_v_; +text: .text%__1cQSystemDictionaryKinitialize6FpnGThread__v_; +text: .text%__1cQSystemDictionarybCinitialize_preloaded_classes6FpnGThread__v_; +text: .text%__1cQSystemDictionarybDinitialize_basic_type_mirrors6FpnGThread__v_; +text: .text%__1cJAssemblerIcvtss2sd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cJAssemblerIcvtsd2ss6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cMinit_globals6F_i_; +text: .text%__1cMexit_globals6F_v_; +text: .text%__1cOMacroAssemblerKdecrementl6MpnMRegisterImpl_i_v_; +text: .text%__1cHVM_ExitEname6kM_pkc_; +text: .text%__1cKcastPPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: fprofiler.o; +text: .text%__1cNMemoryServiceRset_universe_heap6FpnNCollectedHeap__v_; +text: .text%__1cMPeriodicTask2T5B6M_v_; +text: .text%__1cMPeriodicTaskLis_enrolled6kM_i_; +text: .text%__1cNMemoryServicebFadd_parallel_scavenge_heap_info6FpnUParallelScavengeHeap__v_; +text: .text%__1cMPeriodicTaskJdisenroll6M_v_; +text: .text%__1cSset_init_completed6F_v_; +text: .text%__1cMadapter_init6F_v_; +text: .text%__1cTI2CAdapterGeneratorKinitialize6F_v_; +text: .text%__1cNMemoryServiceXadd_psYoung_memory_pool6FpnKPSYoungGen_pnNMemoryManager_4_v_; +text: .text%__1cTC2IAdapterGeneratorKinitialize6F_v_; +text: .text%__1cOstackSlotPOperFclone6kM_pnIMachOper__; +text: .text%__1cObox_handleNodeFclone6kM_pnENode__; +text: .text%__1cTAbstract_VM_VersionHvm_name6F_pkc_; +text: .text%__1cTAbstract_VM_VersionJvm_vendor6F_pkc_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_amd64_pipeline.o; +text: .text%__1cSobjArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC222_v_; +text: .text%__1cKmutex_init6F_v_; +text: .text%__1cHnmethodbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cNTemplateTableF_goto6F_v_; +text: .text%__1cNTemplateTableGgoto_w6F_v_; +text: .text%__1cNTemplateTableFjsr_w6F_v_; +text: .text%__1cNTemplateTableDjsr6F_v_; +text: .text%__1cNinstanceKlassZrelease_C_heap_structures6M_v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCpnMRegisterImpl_3_v_; +text: .text%__1cFJNIidKdeallocate6Fp0_v_; +text: .text%__1cSinstanceKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vm_version.o; +text: .text%__1cStemplateTable_init6F_v_; +text: .text%__1cNTemplateTableNpd_initialize6F_v_; +text: .text%__1cSinstanceKlassKlassUoop_is_instanceKlass6kM_i_; +text: .text%__1cNTemplateTableDnop6F_v_; +text: .text%__1cNTemplateTableSshouldnotreachhere6F_v_; +text: .text%__1cNTemplateTableLaconst_null6F_v_; +text: .text%__1cSinstanceKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cKVM_VersionKinitialize6F_v_; +text: .text%__1cQinstanceRefKlassZupdate_nonstatic_oop_maps6FpnMklassOopDesc__v_; +text: .text%__1cPVM_Version_init6F_v_; +text: .text%__1cNTemplateTableGbipush6F_v_; +text: .text%__1cNTemplateTableGsipush6F_v_; +text: .text%__1cYVM_Version_StubGeneratorTgenerate_getPsrInfo6M_pC_; +text: .text%__1cNTemplateTableGldc2_w6F_v_; +text: .text%__1cNTemplateTableFiload6F_v_; +text: .text%__1cNTemplateTableLfast_iload26F_v_; +text: .text%__1cNTemplateTableKfast_iload6F_v_; +text: .text%__1cNTemplateTableFlload6F_v_; +text: .text%__1cNTemplateTableFfload6F_v_; +text: .text%__1cNTemplateTableFdload6F_v_; +text: .text%__1cNTemplateTableFaload6F_v_; +text: .text%__1cVInterfaceSupport_init6F_v_; +text: .text%__1cNTemplateTableKwide_iload6F_v_; +text: .text%__1cNTemplateTableKwide_lload6F_v_; +text: .text%__1cNTemplateTableKwide_fload6F_v_; +text: .text%__1cNTemplateTableKwide_dload6F_v_; +text: .text%__1cNTemplateTableKwide_aload6F_v_; +text: .text%__1cScheck_ThreadShadow6F_v_; +text: .text%__1cNTemplateTableGiaload6F_v_; +text: .text%__1cNTemplateTableGlaload6F_v_; +text: .text%__1cNTemplateTableGfaload6F_v_; +text: .text%__1cNTemplateTableGdaload6F_v_; +text: .text%__1cNTemplateTableGaaload6F_v_; +text: .text%__1cNTemplateTableGbaload6F_v_; +text: .text%__1cNTemplateTableGcaload6F_v_; +text: .text%__1cNTemplateTableMfast_icaload6F_v_; +text: .text%__1cNTemplateTableGsaload6F_v_; +text: .text%__1cSReferenceProcessorMinit_statics6F_v_; +text: .text%__1cXreferenceProcessor_init6F_v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpCpnMRegisterImpl_33_v_; +text: .text%__1cURecompilationMonitorbGstart_recompilation_monitor_task6F_v_; +text: .text%__1cZInterpreterMacroAssemblerUdispatch_only_normal6MnITosState__v_; +text: .text%__1cNTemplateTableHaload_06F_v_; +text: .text%__1cNTemplateTableGistore6F_v_; +text: .text%__1cNTemplateTableGlstore6F_v_; +text: .text%__1cNTemplateTableGfstore6F_v_; +text: .text%__1cNTemplateTableGdstore6F_v_; +text: .text%__1cNTemplateTableGastore6F_v_; +text: .text%__1cNTemplateTableLwide_istore6F_v_; +text: .text%__1cNTemplateTableLwide_lstore6F_v_; +text: .text%__1cNTemplateTableLwide_fstore6F_v_; +text: .text%__1cNTemplateTableLwide_dstore6F_v_; +text: .text%__1cNTemplateTableLwide_astore6F_v_; +text: .text%__1cNTemplateTableHiastore6F_v_; +text: .text%__1cNTemplateTableHlastore6F_v_; +text: .text%__1cNTemplateTableHfastore6F_v_; +text: .text%__1cNTemplateTableHdastore6F_v_; +text: .text%__1cNTemplateTableHaastore6F_v_; +text: .text%__1cNTemplateTableHbastore6F_v_; +text: .text%__1cNMemoryServiceVadd_psOld_memory_pool6FpnIPSOldGen_pnNMemoryManager__v_; +text: .text%__1cNTemplateTableHsastore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerbFset_method_data_pointer_for_bcp6M_v_; +text: .text%__1cKPSYoungGenUset_space_boundaries6MLL_v_; +text: .text%__1cKPSYoungGenbGcompute_initial_space_boundaries6M_v_; +text: .text%__1cKPSYoungGenPinitialize_work6M_v_; +text: .text%__1cKPSYoungGenKinitialize6MnNReservedSpace_L_v_; +text: .text%__1cNTemplateTableDpop6F_v_; +text: .text%__1cNTemplateTableEpop26F_v_; +text: .text%__1cNTemplateTableDdup6F_v_; +text: .text%__1cNTemplateTableGdup_x16F_v_; +text: .text%__1cNTemplateTableGdup_x26F_v_; +text: .text%__1cNTemplateTableEdup26F_v_; +text: .text%__1cNTemplateTableHdup2_x16F_v_; +text: .text%__1cNTemplateTableHdup2_x26F_v_; +text: .text%__1cNTemplateTableEswap6F_v_; +text: .text%__1cKPSYoungGenYinitialize_virtual_space6MnNReservedSpace_L_v_; +text: .text%__1cKPSYoungGen2t6MLLL_v_; +text: .text%__1cNTemplateTableEidiv6F_v_; +text: .text%__1cNTemplateTableEirem6F_v_; +text: .text%__1cNTemplateTableElmul6F_v_; +text: .text%__1cNTemplateTableEldiv6F_v_; +text: .text%__1cNTemplateTableElrem6F_v_; +text: .text%__1cNTemplateTableElshl6F_v_; +text: .text%__1cNTemplateTableElshr6F_v_; +text: .text%__1cNTemplateTableFlushr6F_v_; +text: .text%__1cRaddL_rReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRaddL_mem_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableEineg6F_v_; +text: .text%__1cNTemplateTableElneg6F_v_; +text: .text%__1cLVtableStubsKinitialize6F_v_; +text: .text%__1cNTemplateTableEfneg6F_v_; +text: .text%__1cNTemplateTableEdneg6F_v_; +text: .text%__1cNTemplateTableEiinc6F_v_; +text: .text%__1cNTemplateTableJwide_iinc6F_v_; +text: .text%__1cMincL_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableElcmp6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psScavenge.o; +text: .text%__1cKPSScavengeKinitialize6F_v_; +text: .text%__1cQinterpreter_init6F_v_; +text: .text%__1cbCAbstractInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cNMemoryServiceWadd_psPerm_memory_pool6FpnJPSPermGen_pnNMemoryManager__v_; +text: .text%__1cNMemoryServiceZadd_code_heap_memory_pool6FpnICodeHeap__v_; +text: .text%__1cNTemplateTableDret6F_v_; +text: .text%__1cNTemplateTableIwide_ret6F_v_; +text: .text%__1cNTemplateTableLtableswitch6F_v_; +text: .text%__1cNTemplateTableMlookupswitch6F_v_; +text: .text%__1cNTemplateTableRfast_linearswitch6F_v_; +text: .text%__1cNTemplateTableRfast_binaryswitch6F_v_; +text: .text%__1cbCAbstractInterpreterGeneratorMgenerate_all6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbEset_entry_points_for_all_bytes6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbCset_safepoints_for_all_bytes6M_v_; +text: .text%__1cOsalI_mem_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSPSPromotionManagerKinitialize6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: memoryService.o; +text: .text%__1cNTemplateTableIgetfield6Fi_v_; +text: .text%__1cNTemplateTableJgetstatic6Fi_v_; +text: .text%__1cNGrowableArray4CpnNMemoryManager__2t6Mii_v_; +text: .text%__1cNGrowableArray4CpnKMemoryPool__2t6Mii_v_; +text: .text%__1cNTemplateTableIputfield6Fi_v_; +text: .text%__1cNTemplateTableJputstatic6Fi_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psPromotionLAB.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter.o; +text: .text%__1cJPSPermGen2t6MnNReservedSpace_LLLLpkci_v_; +text: .text%__1cXNativeSignatureIteratorHdo_byte6M_v_; +text: .text%__1cIPSOldGen2t6MLLLpkci_v_; +text: .text%__1cPmethodDataKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cNTemplateTableNinvokevirtual6Fi_v_; +text: .text%__1cNTemplateTableNinvokespecial6Fi_v_; +text: .text%__1cNTemplateTableMinvokestatic6Fi_v_; +text: .text%__1cNTemplateTableRfast_invokevfinal6Fi_v_; +text: .text%__1cNTemplateTablePinvokeinterface6Fi_v_; +text: .text%__1cNTemplateTableE_new6F_v_; +text: .text%__1cNTemplateTableInewarray6F_v_; +text: .text%__1cNTemplateTableJanewarray6F_v_; +text: .text%__1cNTemplateTableLarraylength6F_v_; +text: .text%__1cNTemplateTableJcheckcast6F_v_; +text: .text%__1cNTemplateTableKinstanceof6F_v_; +text: .text%__1cNTemplateTableL_breakpoint6F_v_; +text: .text%__1cNTemplateTableGathrow6F_v_; +text: .text%__1cNTemplateTableMmonitorenter6F_v_; +text: .text%__1cNTemplateTableLmonitorexit6F_v_; +text: .text%__1cNTemplateTableEwide6F_v_; +text: .text%__1cNTemplateTableOmultianewarray6F_v_; +text: .text%__1cIPSOldGen2t6MnNReservedSpace_LLLLpkci_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: tenuredGeneration.o; +text: .text%__1cQvtableStubs_init6F_v_; +text: .text%__1cQaccessFlags_init6F_v_; +text: .text%__1cSInterpreterRuntimeYthrow_ClassCastException6FpnKJavaThread_pnHoopDesc__v_; +text: .text%__1cSInterpreterRuntimeSupdate_mdp_for_ret6FpnKJavaThread_i_v_; +text: .text%__1cNeventlog_init6F_v_; +text: .text%__1cOMacroAssemblerGc2bool6MpnMRegisterImpl__v_; +text: .text%__1cPmethodDataKlassOset_alloc_size6MI_v_; +text: .text%__1cFVTuneEexit6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psMarkSweep.o; +text: .text%__1cTPSAlwaysTrueClosure2t6M_v_: psMarkSweep.o; +text: .text%__1cXSignatureHandlerLibraryQset_handler_blob6F_pC_; +text: .text%__1cNGrowableArray4CpC_2t6Mii_v_; +text: .text%__1cNGrowableArray4CL_2t6Mii_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_StackOverflowError_handler6M_pC_; +text: .text%__1cOMacroAssemblerRsign_extend_short6MpnMRegisterImpl__v_; +text: .text%__1cbCAbstractInterpreterGeneratorbMgenerate_ArrayIndexOutOfBounds_handler6Mpkc_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_ClassCastException_handler6M_pC_; +text: .text%__1cGThreadWset_as_starting_thread6M_i_; +text: .text%__1cLPSMarkSweepKinitialize6F_v_; +text: .text%__1cbBcreate_initial_thread_group6FpnGThread__nGHandle__: thread.o; +text: .text%__1cVcreate_initial_thread6FnGHandle_pnKJavaThread_pnGThread__pnHoopDesc__: thread.o; +text: .text%__1cbAcall_initializeSystemClass6FpnGThread__v_: thread.o; +text: .text%__1cWreset_vm_info_property6FpnGThread__v_: thread.o; +text: .text%__1cPmethodDataKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cHRetDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cNWatcherThread2t6M_v_; +text: .text%__1cNWatcherThreadDrun6M_v_; +text: .text%__1cNWatcherThreadFstart6F_v_; +text: .text%__1cNWatcherThreadEstop6F_v_; +text: .text%__1cOMacroAssemblerQsign_extend_byte6MpnMRegisterImpl__v_; +text: .text%__1cKJavaThread2t6M_v_; +text: .text%__1cHRetDataJfixup_ret6MinQmethodDataHandle__pC_; +text: .text%__1cKvtune_init6F_v_; +text: .text%__1cLmethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cbAPSGCAdaptivePolicyCounters2t6MpkciipnUPSAdaptiveSizePolicy__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psAdaptiveSizePolicy.o; +text: .text%__1cKDictionaryKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cKDictionaryKclasses_do6MpFpnMklassOopDesc__v_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbFgenerate_slow_signature_handler6M_pC_; +text: .text%__1cUInterpreterGeneratorTgenerate_math_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cUInterpreterGeneratorXgenerate_abstract_entry6M_pC_; +text: .text%__1cUInterpreterGeneratorUgenerate_empty_entry6M_pC_; +text: .text%__1cKDictionaryKfree_entry6MpnPDictionaryEntry__v_; +text: .text%__1cUInterpreterGeneratorXgenerate_accessor_entry6M_pC_; +text: .text%__1cKDictionary2t6Mi_v_; +text: .text%__1cJBytecodesKinitialize6F_v_; +text: .text%__1cObytecodes_init6F_v_; +text: .text%__1cUPSAdaptiveSizePolicy2t6MLLLLLddI_v_; +text: .text%__1cJBytecodesNpd_initialize6F_v_; +text: .text%__1cHCompileRpd_compiler2_init6F_v_; +text: .text%__1cKC2CompilerKinitialize6M_v_; +text: .text%__1cFStateQ_sub_Op_TailJump6MpknENode__v_; +text: .text%__1cMorL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_throw_exception6M_v_; +text: .text%__1cUInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cWinvocationCounter_init6F_v_; +text: .text%__1cQPlaceholderTable2t6Mi_v_; +text: .text%__1cHThreadsJcreate_vm6FpnOJavaVMInitArgs_pi_i_; +text: .text%__1cFStateL_sub_Op_OrL6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_NegF6MpknENode__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_amd64_expand.o; +text: .text%__1cQprint_statistics6F_v_; +text: .text%__1cLbefore_exit6FpnKJavaThread__v_; +text: .text%__1cFStateP_sub_Op_MoveL2D6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_MoveF2I6MpknENode__v_; +text: .text%__1cHvm_exit6Fi_v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cQjava_lang_ThreadPcompute_offsets6F_v_; +text: .text%__1cVjava_lang_ThreadGroupPcompute_offsets6F_v_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectPcompute_offsets6F_v_; +text: .text%__1cYjava_lang_reflect_MethodPcompute_offsets6F_v_; +text: .text%__1cOThreadCriticalKinitialize6F_v_; +text: .text%__1cRAllocateTLSOffset6F_v_: threadLS_solaris_amd64.o; +text: .text%__1cSThreadLocalStoragebCgenerate_code_for_get_thread6F_v_; +text: .text%__1cYjava_lang_reflect_MethodNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cYsun_reflect_ConstantPoolPcompute_offsets6F_v_; +text: .text%__1cbIjava_security_AccessControlContextPcompute_offsets6F_v_; +text: .text%__1cQjava_lang_SystemPcompute_offsets6F_v_; +text: .text%__1cPjava_nio_BufferPcompute_offsets6F_v_; +text: .text%__1cFStateO_sub_Op_CMoveD6MpknENode__v_; +text: .text%__1cZsun_misc_AtomicLongCSImplPcompute_offsets6F_v_; +text: .text%__1cFStateO_sub_Op_CastPP6MpknENode__v_; +text: .text%__1cWThreadLocalAllocBufferWstartup_initialization6F_v_; +text: .text%__1cLJavaClassesbAcompute_hard_coded_offsets6F_v_; +text: .text%__1cLJavaClassesPcompute_offsets6F_v_; +text: .text%__1cPGlobalTLABStats2t6M_v_; +text: .text%__1cQjavaClasses_init6F_v_; +text: .text%__1cMTailJumpNode2t6MpnENode_22222_v_; +text: .text%jni_ToReflectedMethod: jni.o; +text: .text%__1cSThreadLocalStorageEinit6F_v_; +text: .text%__1cNThreadServiceEinit6F_v_; +text: .text%__1cYjni_GetBooleanField_addr6F_pC_; +text: .text%__1cVjni_GetByteField_addr6F_pC_; +text: .text%__1cVjni_GetCharField_addr6F_pC_; +text: .text%__1cWjni_GetShortField_addr6F_pC_; +text: .text%__1cUjni_GetIntField_addr6F_pC_; +text: .text%__1cVjni_GetLongField_addr6F_pC_; +text: .text%__1cWjni_GetFloatField_addr6F_pC_; +text: .text%__1cRCardTableModRefBS2t6MnJMemRegion_i_v_; +text: .text%__1cXjni_GetDoubleField_addr6F_pC_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: phase.o; +text: .text%__1cJTimeStampMmilliseconds6kM_x_; +text: .text%__1cTConstantDoubleValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cKPerfMemoryUdelete_memory_region6F_v_; +text: .text%__1cKPerfMemoryUcreate_memory_region6FL_v_; +text: .text%__1cRCardTableModRefBSbBct_max_alignment_constraint6F_L_; +text: .text%__1cUdelete_shared_memory6FpcL_v_: perfMemory_solaris.o; +text: .text%__1cUcreate_shared_memory6FL_pc_: perfMemory_solaris.o; +text: .text%__1cOtailjmpIndNodeFreloc6kM_i_; +text: .text%__1cSmmap_create_shared6FL_pc_: perfMemory_solaris.o; +text: .text%__1cETypeRInitialize_shared6FpnHCompile__v_; +text: .text%__1cWconstantPoolCacheKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cbAcreate_sharedmem_resources6Fpkc1L_i_: perfMemory_solaris.o; +text: .text%__1cWconstantPoolCacheKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cRmake_user_tmp_dir6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cbBcleanup_sharedmem_resources6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cMciArrayKlass2t6MpnIciSymbol_ipnHciKlass__v_; +text: .text%__1cLremove_file6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cWget_sharedmem_filename6Fpkci_pc_: perfMemory_solaris.o; +text: .text%__1cNget_user_name6Fi_pc_: perfMemory_solaris.o; +text: .text%__1cQget_user_tmp_dir6Fpkc_pc_: perfMemory_solaris.o; +text: .text%__1cRconstantPoolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cFciEnvXget_or_create_exception6MrpnI_jobject_nMsymbolHandle__pnKciInstance__; +text: .text%__1cMloadConFNodeGis_Con6kM_I_; +text: .text%__1cRconstantPoolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cKPerfMemoryHdestroy6F_v_; +text: .text%__1cQconstMethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cKPerfMemoryKinitialize6F_v_; +text: .text%__1cPperfMemory_exit6F_v_; +text: .text%__1cQconstMethodKlassOset_alloc_size6MI_v_; +text: .text%__1cPperfMemory_init6F_v_; +text: .text%__1cNGrowableArray4CpnIPerfData__JappendAll6Mpk2_v_; +text: .text%__1cMPerfDataListFclone6M_p0_; +text: .text%__1cQconstMethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cMciKlassKlassEmake6F_p0_; +text: .text%__1cMPerfDataList2t6Mp0_v_; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnPPerfLongCounter__; +text: .text%__1cPPerfDataManagerHsampled6F_pnMPerfDataList__; +text: .text%__1cMmulD_memNodeFreloc6kM_i_; +text: .text%__1cPPerfDataManagerHdestroy6F_v_; +text: .text%__1cMsubD_immNodeFreloc6kM_i_; +text: .text%__1cMsubF_memNodeFreloc6kM_i_; +text: .text%lookupDirectBufferClasses: jni.o; +text: .text%__1cbDinitializeDirectBufferSupport6FpnHJNIEnv___i_: jni.o; +text: .text%__1cVquicken_jni_functions6F_v_; +text: .text%JNI_CreateJavaVM; +text: .text%__1cFParseWprofile_null_checkcast6M_v_; +text: .text%__1cQJNI_FastGetFieldbFgenerate_fast_get_boolean_field6F_pC_; +text: .text%__1cOsalI_mem_1NodeFreloc6kM_i_; +text: .text%__1cIciMethodMvtable_index6M_i_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_byte_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_char_field6F_pC_; +text: .text%__1cMmulI_memNodeFreloc6kM_i_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_short_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbBgenerate_fast_get_int_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_long_field6F_pC_; +text: .text%__1cMincL_memNodeFreloc6kM_i_; +text: .text%__1cRaddL_mem_rRegNodeFreloc6kM_i_; +text: .text%__1cRaddL_mem_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMincL_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_float_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_double_field6F_pC_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jniFastGetField_amd64.o; +text: .text%__1cNcmovL_memNodeFreloc6kM_i_; +text: .text%__1cKJNIHandlesKinitialize6F_v_; +text: .text%__1cQjni_handles_init6F_v_; +text: .text%JVM_InitProperties; +text: .text%JVM_Halt; +text: .text%JVM_MaxMemory; +text: .text%JVM_GetClassDeclaredMethods; +text: .text%__1cKCMoveDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOsalI_mem_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHRetDataKis_RetData6M_i_; +text: .text%JVM_InitializeSocketLibrary; +text: .text%JVM_Socket; +text: .text%__1cPciObjArrayKlass2t6MpnIciSymbol_pnHciKlass_i_v_; +text: .text%__1cPOopTaskQdDueueSet2t6Mi_v_; +text: .text%__1cbEinitialize_converter_functions6F_v_; +text: .text%JVM_SupportsCX8; +text: .text%__1cUciObjArrayKlassKlassEmake6F_p0_; +text: .text%__1cTcompilerOracle_init6F_v_; +text: .text%__1cOCompilerOracleRparse_from_string6Fpkc_v_; +text: .text%__1cOCompilerOraclePparse_from_file6F_v_; +text: .text%__1cHcc_file6F_pkc_: compilerOracle.o; +text: .text%__1cKTypeOopPtrEmake6FnHTypePtrDPTR_i_pk0_; +text: .text%__1cKTypeOopPtrFxdual6kM_pknEType__; +text: .text%__1cOCompilerOracleOread_from_line6Fpc_v_; +text: .text%__1cPciObjectFactoryTinit_shared_objects6M_v_; +text: .text%__1cVcompiledICHolderKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cVcompiledICHolderKlassOset_alloc_size6MI_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEnvBase.o; +text: .text%__1cVcompiledICHolderKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cNGrowableArray4CpnMJvmtiEnvBase__2t6Mii_v_; +text: .text%__1cRJvmtiEventEnabled2t6M_v_; +text: .text%__1cRciArrayKlassKlassUis_array_klass_klass6M_i_; +text: .text%__1cRJvmtiEventEnabledFclear6M_v_; +text: .text%__1cNGrowableArray4CpnOCompilerThread__2t6Mii_v_; +text: .text%__1cFParseNfetch_monitor6MipnENode_2_2_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compileBroker.o; +text: .text%__1cNGrowableArray4CpnIciMethod__Egrow6Mi_v_; +text: .text%__1cNCompileBrokerQset_should_block6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_start6F_v_; +text: .text%__1cPGenerationSizerQinitialize_flags6M_v_; +text: .text%__1cUJvmtiEventControllerHvm_init6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_death6F_v_; +text: .text%__1cNCompileBrokerVinit_compiler_threads6Fi_v_; +text: .text%__1cUParallelScavengeHeapbCsupports_inline_contig_alloc6kM_i_; +text: .text%__1cUParallelScavengeHeapItop_addr6kM_ppnIHeapWord__; +text: .text%__1cNCompileBrokerQcompilation_init6FpnQAbstractCompiler__v_; +text: .text%__1cUParallelScavengeHeapIend_addr6kM_ppnIHeapWord__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEventController.o; +text: .text%__1cLJvmtiExportRenter_start_phase6F_v_; +text: .text%__1cUParallelScavengeHeapEheap6F_p0_; +text: .text%__1cUParallelScavengeHeapNgc_threads_do6kMpnNThreadClosure__v_; +text: .text%__1cUParallelScavengeHeapYpermanent_object_iterate6MpnNObjectClosure__v_; +text: .text%__1cLJvmtiExportQenter_live_phase6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_start6F_v_; +text: .text%__1cOcompiler2_init6F_v_; +text: .text%__1cLJvmtiExportTpost_vm_initialized6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_death6F_v_; +text: .text%__1cLJvmtiExportbMtransition_pending_onload_raw_monitors6F_v_; +text: .text%__1cUParallelScavengeHeapMmax_capacity6kM_L_; +text: .text%__1cUJvmtiPendingMonitorsXtransition_raw_monitors6F_v_; +text: .text%__1cMaddF_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cUParallelScavengeHeapPpost_initialize6M_v_; +text: .text%__1cUParallelScavengeHeapKinitialize6M_i_; +text: .text%__1cHoopDescLheader_size6F_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compilationPolicy.o; +text: .text%__1cPClassFileParserbFjava_lang_ref_Reference_fix_pre6MpnPtypeArrayHandle_nSconstantPoolHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cPClassFileParserXjava_lang_Class_fix_pre6MpnOobjArrayHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cPClassFileParserYjava_lang_Class_fix_post6Mpi_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: parGCAllocBuffer.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiImpl.o; +text: .text%__1cNGrowableArray4CpnPJvmtiRawMonitor__2t6Mii_v_; +text: .text%__1cNGrowableArray4CpnPJvmtiRawMonitor__Uclear_and_deallocate6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiTagMap.o; +text: .text%__1cRCompilationPolicyUcompleted_vm_startup6F_v_; +text: .text%__1cOtypeArrayKlassKinitialize6MpnGThread__v_; +text: .text%__SLIP.DELETER__C: ostream.o; +text: .text%__1cWcompilationPolicy_init6F_v_; +text: .text%__1cMostream_exit6F_v_; +text: .text%__1cTtypeArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cTtypeArrayKlassKlassVoop_is_typeArrayKlass6kM_i_; +text: .text%__1cbCTwoGenerationCollectorPolicyMrem_set_name6M_nJGenRemSetEName__; +text: .text%__1cTtypeArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cQostream_init_log6F_v_; +text: .text%__1cIUniversePcheck_alignment6FLLpkc_v_; +text: .text%__1cIUniverseHgenesis6FpnGThread__v_; +text: .text%__1cIUniverseNfixup_mirrors6FpnGThread__v_; +text: .text%__1cMostream_init6F_v_; +text: .text%__1cNdefaultStreamEinit6M_v_; +text: .text%__1cIUniverseUreinitialize_itables6F_v_; +text: .text%__1cKklassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cKklassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cNuniverse_init6F_i_; +text: .text%__1cIUniversePinitialize_heap6F_i_; +text: .text%__1cLClassLoaderbBsetup_bootstrap_search_path6F_v_; +text: .text%__1cOuniverse2_init6F_v_; +text: .text%__1cSuniverse_post_init6F_v_; +text: .text%__1cIUniverseYcompute_base_vtable_size6F_v_; +text: .text%__1cCosXnon_memory_address_word6F_pc_; +text: .text%__1cCosGinit_26F_i_; +text: .text%__1cbCTwoGenerationCollectorPolicyUinitialize_size_info6M_v_; +text: .text%__1cCosEinit6F_v_; +text: .text%__1cCosHSolarisUsynchronization_init6F_v_; +text: .text%Unsafe_SetNativeLong; +text: .text%__1cCosHSolarisOlibthread_init6F_v_; +text: .text%__1cJLoadFNodeMstore_Opcode6kM_i_; +text: .text%__1cOisT2_libthread6F_i_; +text: .text%Unsafe_FreeMemory; +text: .text%__1cCosHSolarisXinstall_signal_handlers6F_v_; +text: .text%Unsafe_PageSize; +text: .text%__1cRlwp_priocntl_init6F_i_: os_solaris.o; +text: .text%__1cNpriocntl_stub6FinGidtype_iipc_l_: os_solaris.o; +text: .text%__1cCosHSolarisRmpss_sanity_check6F_v_; +text: .text%__1cOLibraryCallKitWinline_native_hashcode6Mii_i_; +text: .text%__1cbCTwoGenerationCollectorPolicyQinitialize_flags6M_v_; +text: .text%__1cCosOrelease_memory6FpcL_i_; +text: .text%__1cCosLsignal_wait6F_i_; +text: .text%JVM_RegisterUnsafeMethods; +text: .text%__1cVcheck_pending_signals6Fi_i_: os_solaris.o; +text: .text%__1cCosNsignal_notify6Fi_v_; +text: .text%__1cCosOsignal_init_pd6F_v_; +text: .text%__1cLClassLoaderQload_zip_library6F_v_; +text: .text%__1cLClassLoaderZcreate_package_info_table6F_v_; +text: .text%__1cNmulI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cLClassLoaderKinitialize6F_v_; +text: .text%__1cLClassLoaderVcompute_Object_vtable6F_i_; +text: .text%__1cCosHSolarisPinit_signal_mem6F_v_; +text: .text%__1cNCollectedHeap2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeCache.o; +text: .text%__1cQclassLoader_init6F_v_; +text: .text%__1cCosSget_temp_directory6F_pkc_; +text: .text%__1cVLoaderConstraintTable2t6Mi_v_; +text: .text%__1cCosbDallocate_thread_local_storage6F_i_; +text: .text%__1cOcodeCache_init6F_v_; +text: .text%__1cVverificationType_init6F_v_; +text: .text%__1cVverificationType_exit6F_v_; +text: .text%__1cQVerificationTypeKinitialize6F_v_; +text: .text%__1cQVerificationTypeIfinalize6F_v_; +text: .text%__1cJCodeCacheKinitialize6F_v_; +text: .text%__1cNIdealLoopTreeQsplit_outer_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cKfix_parent6FpnNIdealLoopTree_1_v_: loopnode.o; +text: .text%__1cCosHSolarisQsignal_sets_init6F_v_; +text: .text%__1cTClassLoadingServiceEinit6F_v_; +text: .text%__1cTClassLoadingServiceVnotify_class_unloaded6FpnNinstanceKlass_i_v_; +text: .text%__1cCosScreate_main_thread6FpnGThread__i_; +text: .text%__1cNIdealLoopTreeUmerge_many_backedges6MpnOPhaseIdealLoop__v_; +text: .text%__1cQcreate_os_thread6FpnGThread_I_pnIOSThread__: os_solaris.o; +text: .text%__1cRLowMemoryDetectorKinitialize6F_v_; +text: .text%__1cLmethodKlassOset_alloc_size6MI_v_; +text: .text%__1cNExceptionBlob2n6FLI_pv_; +text: .text%__1cNExceptionBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNExceptionBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cQUncommonTrapBlob2n6FLI_pv_; +text: .text%__1cQUncommonTrapBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cQUncommonTrapBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cSDeoptimizationBlob2n6FLI_pv_; +text: .text%__1cSDeoptimizationBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_iiii_p0_; +text: .text%__1cSDeoptimizationBlob2t6MpnKCodeBuffer_ipnJOopMapSet_iiii_v_; +text: .text%__1cRLowMemoryDetectorUhas_pending_requests6F_i_; +text: .text%__1cCosbDinit_system_properties_values6F_v_; +text: .text%__1cCosHSolarisWinitialize_system_info6F_v_; +text: .text%__1cCosPphysical_memory6F_X_; +text: .text%__1cMFastLockNodeLis_FastLock6kM_pk0_; +text: .text%__1cRLowMemoryDetectorbGlow_memory_detector_thread_entry6FpnKJavaThread_pnGThread__v_; +text: .text%__1cXLowMemoryDetectorThreadbCis_hidden_from_external_view6kM_i_; +text: .text%__1cSThreadLocalStorageHpd_init6F_v_; +text: .text%__1cLmethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: machnode.o; +text: .text%__1cPmanagement_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: cmsAdaptiveSizePolicy.o; +text: .text%__1cKManagementEinit6F_v_; +text: .text%__1cKManagementKinitialize6FpnGThread__v_; +text: .text%__1cKManagementWrecord_vm_startup_time6Fxx_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: management.o; +text: .text%__1cOmarksweep_init6F_v_; +text: .text%__1cCosXis_server_class_machine6F_i_; +text: .text%__1cJMarkSweepQKeepAliveClosure2t6M_v_: markSweep.o; +text: .text%__1cNReservedSpace2t6MLLipc_v_; +text: .text%__1cJMarkSweepOIsAliveClosure2t6M_v_: markSweep.o; +text: .text%__1cCosZset_memory_serialize_page6FpC_v_; +text: .text%__1cCosNset_boot_path6Fcc_i_; +text: .text%__1cJMarkSweepSFollowStackClosure2t6M_v_: markSweep.o; +text: .text%__1cNReservedSpaceUpage_align_size_down6FL_L_; +text: .text%__1cNReservedSpaceYallocation_align_size_up6FL_L_; +text: .text%__1cNGrowableArray4CpnKOSRAdapter__2t6Mii_v_; +text: .text%__1cXonStackReplacement_init6F_v_; +text: .text%__1cSOnStackReplacementKinitialize6F_v_; +text: .text%__1cJMarkSweepRFollowRootClosure2t6M_v_: markSweep.o; +text: .text%__1cCosLinit_random6Fl_v_; +text: .text%__1cHOrLNodeJideal_reg6kM_I_; +text: .text%__1cOvmStructs_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vmStructs.o; +text: .text%__1cJvmSymbolsKinitialize6FpnGThread__v_; +text: .text%__1cJMarkSweepSMarkAndPushClosure2t6M_v_: markSweep.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: markSweep.o; +text: .text%__1cQVMOperationQdDueue2t6M_v_; +text: .text%__1cCosGstrdup6Fpkc_pc_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: methodLiveness.o; +text: .text%__1cCosXterminate_signal_thread6F_v_; +text: .text%__1cCosLsignal_init6F_v_; +text: .text%__1cMsubD_immNodeKconst_size6kM_i_; +text: .text%__1cTsignal_thread_entry6FpnKJavaThread_pnGThread__v_: os.o; +text: .text%__1cOtailjmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSobjArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cHMatcherVfind_callee_arguments6FpnNsymbolOopDesc_ipi_pnLOptoRegPair__; +text: .text%__1cIVMThreadGcreate6F_v_; +text: .text%__1cIVMThread2t6M_v_; +text: .text%__1cNSharedRuntimeUlookup_function_DD_D6FrpFpnHJNIEnv__pnH_jclass_dd_dpkc_v_; +text: .text%__1cNSharedRuntimebIinitialize_StrictMath_entry_points6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedHeap.o; +text: .text%__1cTAssertIsPermClosure2t6M_v_: sharedHeap.o; +text: .text%__1cIVMThreadDrun6M_v_; +text: .text%__1cWResolveOopMapConflictsUdo_potential_rewrite6MpnGThread__nMmethodHandle__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: generateOopMap.o; +text: .text%__1cNCellTypeStateImake_top6F_0_; +text: .text%__1cJAssemblerEandl6MpnMRegisterImpl_2_v_; +text: .text%__1cNCellTypeStateLmake_bottom6F_0_; +text: .text%__1cNcmovL_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRcheck_basic_types6F_v_; +text: .text%__1cLOptoRuntimeYgenerate_arraycopy_stubs6F_v_; +text: .text%__1cSobjArrayKlassKlassUoop_is_objArrayKlass6kM_i_; +text: .text%__1cNSharedRuntimebBgenerate_class_cast_message6FpnKJavaThread_pkc_pc_; +text: .text%__1cNSharedRuntimebBgenerate_class_cast_message6Fpkc2_pc_; +text: .text%__1cLOptoRuntimebPgenerate_polling_page_return_handler_blob6F_v_; +text: .text%__1cIVMThreadEloop6M_v_; +text: .text%__1cLOptoRuntimebSgenerate_polling_page_safepoint_handler_blob6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: matcher.o; +text: .text%__1cLOptoRuntimeUsetup_exception_blob6F_v_; +text: .text%__1cLOptoRuntimeWfill_in_exception_blob6F_v_; +text: .text%__1cNMemoryManagerbDget_code_cache_memory_manager6F_p0_; +text: .text%__1cNMemoryManagerbDget_psScavenge_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cNMemoryManagerbEget_psMarkSweep_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cQPSGenerationPool2t6MpnIPSOldGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cJAssemblerFimull6MpnMRegisterImpl_2_v_; +text: .text%__1cLOptoRuntimebBgenerate_uncommon_trap_blob6F_v_; +text: .text%__1cNSharedRuntimeTgenerate_deopt_blob6F_v_; +text: .text%__1cQPSGenerationPool2t6MpnJPSPermGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cNRegisterSaverYrestore_result_registers6FpnOMacroAssembler__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: runtimeService.o; +text: .text%__1cORuntimeServiceYrecord_application_start6F_v_; +text: .text%__1cJGenRemSetYmax_alignment_constraint6Fn0AEName__L_; +text: .text%__1cICarSpaceEinit6F_v_; +text: .text%__1cNcarSpace_init6F_v_; +text: .text%__1cORuntimeServiceEinit6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: genCollectedHeap.o; +text: .text%__1cTAssertIsPermClosure2t6M_v_: genCollectedHeap.o; +text: .text%__1cRAlwaysTrueClosure2t6M_v_: genCollectedHeap.o; +text: .text%__1cLOptoRuntimeVhandle_exception_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeSfetch_monitor_Type6F_pknITypeFunc__; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_i_v_; +text: .text%__1cLStatSamplerKinitialize6F_v_; +text: .text%__1cLStatSamplerGengage6F_v_; +text: .text%__1cLStatSamplerJdisengage6F_v_; +text: .text%__1cLStatSamplerHdestroy6F_v_; +text: .text%__1cSCommandLineFlagsExKuintxAtPut6FnXCommandLineFlagWithType_L_v_; +text: .text%__1cSobjArrayKlassKlassbEallocate_system_objArray_klass6MpnGThread__pnMklassOopDesc__; +text: .text%__1cLOptoRuntimeUmultianewarray5_Type6F_pknITypeFunc__; +text: .text%__1cLStatSamplerbMcreate_system_property_instrumentation6FpnGThread__v_; +text: .text%__1cLStatSamplerUcreate_misc_perfdata6F_v_; +text: .text%__1cLStatSamplerXcreate_sampled_perfdata6F_v_; +text: .text%__1cJAssemblerDorq6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEsarl6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEsarq6MpnMRegisterImpl__v_; +text: .text%__1cLOptoRuntimeUmultianewarray4_Type6F_pknITypeFunc__; +text: .text%__1cJAssemblerEshll6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEshlq6MpnMRegisterImpl__v_; +text: .text%__1cUEdenMutableSpacePool2t6MpnKPSYoungGen_pnMMutableSpace_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cNStubGeneratorQgenerate_initial6M_v_; +text: .text%__1cNStubGeneratorXgenerate_atomic_add_ptr6M_pC_; +text: .text%__1cNStubGeneratorTgenerate_atomic_add6M_pC_; +text: .text%__1cNStubGeneratorbCgenerate_atomic_cmpxchg_long6M_pC_; +text: .text%__1cNStubGeneratorXgenerate_atomic_cmpxchg6M_pC_; +text: .text%__1cNStubGeneratorYgenerate_atomic_xchg_ptr6M_pC_; +text: .text%__1cNStubGeneratorUgenerate_atomic_xchg6M_pC_; +text: .text%__1cNStubGeneratorYgenerate_catch_exception6M_pC_; +text: .text%__1cNStubGeneratorSgenerate_call_stub6MrpC_1_; +text: .text%__1cNStubGeneratorbAgenerate_forward_exception6M_pC_; +text: .text%__1cNStubGeneratorMgenerate_all6M_v_; +text: .text%__1cNStubGeneratorSgenerate_d2l_fixup6M_pC_; +text: .text%__1cNStubGeneratorSgenerate_d2i_fixup6M_pC_; +text: .text%__1cNStubGeneratorSgenerate_f2l_fixup6M_pC_; +text: .text%__1cNStubGeneratorSgenerate_f2i_fixup6M_pC_; +text: .text%__1cLOptoRuntimeUmultianewarray3_Type6F_pknITypeFunc__; +text: .text%__1cNStubGeneratorTgenerate_verify_oop6M_pC_; +text: .text%__1cNStubGeneratorVgenerate_verify_mxcsr6M_pC_; +text: .text%__1cNStubGeneratorYgenerate_get_previous_fp6M_pC_; +text: .text%__1cNStubGeneratorbIgenerate_handler_for_unsafe_access6M_pC_; +text: .text%__1cMStubRoutinesLinitialize16F_v_; +text: .text%__1cMStubRoutinesLinitialize26F_v_; +text: .text%__1cSstubRoutines_init16F_v_; +text: .text%__1cSstubRoutines_init26F_v_; +text: .text%__1cLMoveL2DNodeJideal_reg6kM_I_; +text: .text%__1cNGrowableArray4CpnTDerivedPointerEntry__2t6Mii_v_; +text: .text%__1cLMoveF2INodeJideal_reg6kM_I_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: oopMap.o; +text: .text%__1cNGrowableArray4CpnHMonitor__2t6Mii_v_; +text: .text%__1cLOptoRuntimeUmultianewarray2_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray1_Type6F_pknITypeFunc__; +text: .text%__1cQDoNothingClosure2t6M_v_: oopMap.o; +text: .text%__1cJAssemblerEshrl6MpnMRegisterImpl__v_; diff --git a/hotspot/build/solaris/makefiles/reorder_COMPILER2_i486 b/hotspot/build/solaris/makefiles/reorder_COMPILER2_i486 new file mode 100644 index 00000000000..4113159bf8d --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_COMPILER2_i486 @@ -0,0 +1,8394 @@ +data = R0x2000; +text = LOAD ?RXO; + + +text: .text%__1cQIndexSetIteratorEnext6M_I_: ifg.o; +text: .text%__1cSPSPromotionManagerWcopy_to_survivor_space6MpnHoopDesc__2_; +text: .text%__1cNinstanceKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQIndexSetIteratorQadvance_and_next6M_I_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: indexSet.o; +text: .text%__1cNSharedRuntimeElrem6Fxx_x_; +text: .text%__1cCosOjavaTimeMillis6F_x_; +text: .text%__1cJMarkSweepO_mark_and_push6FppnHoopDesc__v_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: chaitin.o; +text: .text%__1cNSharedRuntimeEldiv6Fxx_x_; +text: .text%__1cIPhaseIFGIadd_edge6MII_i_; +text: .text%__1cQIndexSetIterator2t6MpnIIndexSet__v_; +text: .text%__1cIMachNodeNrematerialize6kM_i_; +text: .text%__1cNinstanceKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMOopTaskQdDueueKpop_global6MrpnHoopDesc__i_; +text: .text%__1cPOopTaskQdDueueSetPsteal_best_of_26MipirpnHoopDesc__i_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: live.o; +text: .text%__1cOtypeArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cOtypeArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cIIndexSetLalloc_block6M_pn0AIBitBlock__; +text: .text%__1cHRegMaskFis_UP6kM_i_; +text: .text%__1cIMachNodeHis_Mach6M_p0_: ad_i486_misc.o; +text: .text%__1cDLRGOcompute_degree6kMr0_i_; +text: .text%__1cIIndexSetWalloc_block_containing6MI_pn0AIBitBlock__; +text: .text%__1cRMachSpillCopyNodeMis_SpillCopy6M_p0_: ad_i486.o; +text: .text%__1cENodeEjvms6kM_pnIJVMState__; +text: .text%__1cIMachNodeJideal_reg6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: classes.o; +text: .text%__1cJMarkSweepPmark_and_follow6FppnHoopDesc__v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: ad_i486_misc.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: classes.o; +text: .text%__1cHRegMaskJis_bound16kM_i_; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__: classes.o; +text: .text%__1cENodeHis_Copy6kM_I_: classes.o; +text: .text%__1cQObjectStartArrayMobject_start6MpnIHeapWord__2_: cardTableExtension.o; +text: .text%__1cRMachSpillCopyNodeHis_Copy6kM_I_: ad_i486.o; +text: .text%__1cNobjArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cENodeHis_Copy6kM_I_: ad_i486_misc.o; +text: .text%__1cETypeDcmp6Fkpk03_i_; +text: .text%__1cOPhaseIdealLoopOidom_no_update6kMpnENode__2_: loopnode.o; +text: .text%__1cHRegMaskJis_bound26kM_i_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: ad_i486_misc.o; +text: .text%__1cHRegMaskESize6kM_I_; +text: .text%__1cNRelocIteratorEnext6M_i_: relocInfo.o; +text: .text%__1cJeRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cUGenericGrowableArrayLraw_at_grow6MipknEGrET__pv_; +text: .text%__1cJVectorSet2R6MI_rnDSet__; +text: .text%__1cIMachNodeHis_Mach6M_p0_: ad_i486.o; +text: .text%__1cXresource_allocate_bytes6FI_pc_; +text: .text%__1cDff16FI_i_; +text: .text%__1cJeRegPOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cIProjNodeHis_Proj6M_p0_; +text: .text%__1cENodeGis_CFG6kM_i_: classes.o; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopopts.o; +text: .text%__1cIIndexSetKinitialize6MI_v_; +text: .text%__1cMloadConINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPOopTaskQdDueueSetFsteal6MipirpnHoopDesc__i_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopnode.o; +text: .text%__1cMloadConINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cHPhiNodeGis_Phi6M_p0_: cfgnode.o; +text: .text%__1cENodeGpinned6kM_i_: classes.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: ad_i486.o; +text: .text%__1cIIndexSetKfree_block6MI_v_; +text: .text%__1cIMachNodeGOpcode6kM_i_; +text: .text%__1cWPSScavengeRootsClosureGdo_oop6MppnHoopDesc__v_: psTasks.o; +text: .text%__1cITypeNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: ad_i486_misc.o; +text: .text%__1cENodeIout_grow6MI_v_; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_: callnode.o; +text: .text%__1cENodeHadd_req6Mp0_v_; +text: .text%__1cKTypeOopPtrFklass6kM_pnHciKlass__: type.o; +text: .text%__1cMPhaseChaitinTinterfere_with_live6MIpnIIndexSet__v_; +text: .text%__1cNRelocIteratorEnext6M_i_: nmethod.o; +text: .text%__1cETypeFuhash6Fkpk0_i_; +text: .text%__1cJCProjNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeNrematerialize6kM_i_: classes.o; +text: .text%__1cJloadPNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIrc_class6FnHOptoRegEName__nCRC__: ad_i486.o; +text: .text%__1cNMachIdealNodeErule6kM_I_: ad_i486.o; +text: .text%__1cKjmpDirNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cRMachSpillCopyNodeJideal_reg6kM_I_: ad_i486.o; +text: .text%__1cOlower_pressure6FpnDLRG_IpnFBlock_pI4_v_: ifg.o; +text: .text%__1cMget_live_bit6Fpii_i_: buildOopMap.o; +text: .text%__1cMPhaseChaitinLskip_copies6MpnENode__2_; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__: ad_i486_misc.o; +text: .text%__1cINodeHashLhash_delete6MpknENode__i_; +text: .text%__1cEDictGInsert6Mpv1i_1_; +text: .text%__1cICallNodeKmatch_edge6kMI_I_; +text: .text%__1cJMultiNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cHTypeIntCeq6kMpknEType__i_; +text: .text%__1cENodeGis_CFG6kM_i_: cfgnode.o; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_: ad_i486.o; +text: .text%__1cETypeJtype_dict6F_pnEDict__; +text: .text%__1cHPhiNodeGOpcode6kM_i_; +text: .text%__1cJMarkSweepUAdjustPointerClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cENodeHdel_out6Mp0_v_: matcher.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: classes.o; +text: .text%__1cFArenaIcontains6kMpkv_i_; +text: .text%__1cINodeHashQhash_find_insert6MpnENode__2_; +text: .text%__1cKRegionNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cIPhaseIFGQeffective_degree6kMI_i_; +text: .text%__1cIProjNodeGis_CFG6kM_i_; +text: .text%__1cIUniverseMnon_oop_word6F_pv_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: classes.o; +text: .text%__1cIPhaseIFGLremove_node6MI_pnIIndexSet__; +text: .text%__1cIPhaseIFGJre_insert6MI_v_; +text: .text%__1cJraw_score6Fdd_d_: chaitin.o; +text: .text%__1cDLRGFscore6kM_d_; +text: .text%__1cJMarkSweepOIsAliveClosureLdo_object_b6MpnHoopDesc__i_: markSweep.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: classes.o; +text: .text%__1cETypeIhashcons6M_pk0_; +text: .text%__1cPClassFileStreamGget_u26MpnGThread__H_; +text: .text%__1cENodeEhash6kM_I_; +text: .text%__1cIProjNodeGpinned6kM_i_; +text: .text%__1cMloadConPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMPhaseChaitinKelide_copy6MpnENode_ipnFBlock_rnJNode_List_6i_i_; +text: .text%__1cHNTarjanEEVAL6M_p0_; +text: .text%__1cMloadConPNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: classes.o; +text: .text%__1cQIndexSetIteratorEnext6M_I_: coalesce.o; +text: .text%__1cWShouldNotReachHereNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cMMachCallNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cMset_live_bit6Fpii_v_: buildOopMap.o; +text: .text%__1cHCompileRvalid_bundle_info6MpknENode__i_; +text: .text%__1cOPhaseIdealLoopUbuild_loop_late_post6MpnENode_pk0_v_; +text: .text%__1cIProjNodeGOpcode6kM_i_; +text: .text%__1cENodeHdel_out6Mp0_v_: phaseX.o; +text: .text%__1cNobjArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNobjArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJPhaseLiveLadd_liveout6MpnFBlock_IrnJVectorSet__v_; +text: .text%__1cDfh16FI_i_; +text: .text%__1cRMachSpillCopyNodeLbottom_type6kM_pknEType__: ad_i486.o; +text: .text%__1cMPhaseChaitinMchoose_color6MrnDLRG_i_nHOptoRegEName__; +text: .text%__1cNIdealLoopTreeJis_member6kMpk0_i_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: oopMap.o; +text: .text%__1cENodeHis_Copy6kM_I_: cfgnode.o; +text: .text%__1cIMachNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cIConINodeGOpcode6kM_i_; +text: .text%__1cGIfNodeGOpcode6kM_i_; +text: .text%__1cOtypeArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cHTypePtrEhash6kM_i_; +text: .text%__1cMPhaseChaitinQis_high_pressure6MpnFBlock_pnDLRG_I_i_; +text: .text%__1cENode2t6MI_v_; +text: .text%__1cMPhaseChaitinKbias_color6MrnDLRG_i_nHOptoRegEName__; +text: .text%__1cENodeMcisc_operand6kM_i_: classes.o; +text: .text%__1cMOopTaskQdDueueOpop_local_slow6MInOTaskQdDueueSuperDAge__i_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: typeArrayKlass.o; +text: .text%JVM_ArrayCopy; +text: .text%__1cOtypeArrayKlassQoop_is_typeArray6kM_i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cOPhaseIdealLoopYsplit_if_with_blocks_pre6MpnENode__2_; +text: .text%__1cOPhaseIdealLoopZsplit_if_with_blocks_post6MpnENode__v_; +text: .text%__1cETypeEmeet6kMpk0_2_; +text: .text%__1cETypeLisa_oop_ptr6kM_i_; +text: .text%__1cFArenaIArealloc6MpvII_1_; +text: .text%__1cRMachSpillCopyNodeKin_RegMask6kMI_rknHRegMask__: ad_i486.o; +text: .text%__1cKTypeOopPtrEhash6kM_i_; +text: .text%__1cRMachSpillCopyNodeLout_RegMask6kM_rknHRegMask__: ad_i486.o; +text: .text%__1cIMachNodeMcisc_operand6kM_i_: ad_i486.o; +text: .text%__1cIMachNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: ad_i486_misc.o; +text: .text%__1cKIfTrueNodeGOpcode6kM_i_; +text: .text%__1cIAddPNodeGOpcode6kM_i_; +text: .text%__1cENodeHdel_out6Mp0_v_: graphKit.o; +text: .text%__1cPDictionaryEntrybDprotection_domain_set_oops_do6MpnKOopClosure__v_: dictionary.o; +text: .text%__1cHTypeIntEhash6kM_i_; +text: .text%__1cSPSPromotionManagerUflush_prefetch_queue6M_v_: psPromotionManager.o; +text: .text%__1cMMachProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cMPhaseIterGVNNtransform_old6MpnENode__2_; +text: .text%__1cMMachProjNodeGOpcode6kM_i_; +text: .text%__1cETypeJsingleton6kM_i_; +text: .text%__1cRMachSpillCopyNodePoper_input_base6kM_I_: ad_i486.o; +text: .text%__1cJleaP8NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJleaP8NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMclr_live_bit6Fpii_v_: buildOopMap.o; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__: cfgnode.o; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__: ad_i486.o; +text: .text%__1cIMachNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cJPhaseLiveGgetset6MpnFBlock__pnIIndexSet__; +text: .text%__1cHConNodeGOpcode6kM_i_; +text: .text%__1cJMarkSweepSMarkAndPushClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cJloadINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSis_single_register6FI_i_: postaloc.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: relocInfo.o; +text: .text%__1cILRG_ListGextend6MII_v_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: classes.o; +text: .text%__1cLIfFalseNodeGOpcode6kM_i_; +text: .text%__1cECopyYconjoint_words_to_higher6FpnIHeapWord_2I_v_: node.o; +text: .text%__1cHTypeIntJsingleton6kM_i_; +text: .text%__1cSCallStaticJavaNodeGOpcode6kM_i_; +text: .text%__1cMPhaseIterGVNWadd_users_to_worklist06MpnENode__v_; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopEsort6MpnNIdealLoopTree_2_2_; +text: .text%__1cJPhaseLiveLadd_liveout6MpnFBlock_pnIIndexSet_rnJVectorSet__v_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: cfgnode.o; +text: .text%__1cMMutableSpaceMcas_allocate6MI_pnIHeapWord__; +text: .text%__1cHNTarjanICOMPRESS6M_v_; +text: .text%__1cLsymbolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cIciObjectGequals6Mp0_i_; +text: .text%__1cKNode_ArrayGinsert6MIpnENode__v_; +text: .text%__1cKTypeOopPtrCeq6kMpknEType__i_; +text: .text%__1cIBoolNodeGOpcode6kM_i_; +text: .text%__1cNRelocIteratorFreloc6M_pnKRelocation__; +text: .text%__1cENodeEgrow6MI_v_; +text: .text%__1cHTypePtrCeq6kMpknEType__i_; +text: .text%__1cYCallStaticJavaDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNRelocIteratorEnext6M_i_: codeBlob.o; +text: .text%__1cWConstantPoolCacheEntryPfollow_contents6M_v_; +text: .text%__1cWConstantPoolCacheEntryPadjust_pointers6M_v_; +text: .text%__1cJeRegIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cLemit_opcode6FrnKCodeBuffer_i_v_; +text: .text%__1cMMachProjNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cENodeNrematerialize6kM_i_: cfgnode.o; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: ad_i486_misc.o; +text: .text%__1cKup_one_dom6FpnENode__1_: ifnode.o; +text: .text%__1cJMultiNodeIis_Multi6M_p0_; +text: .text%__1cLsymbolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLsymbolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cIIndexSetKinitialize6MIpnFArena__v_; +text: .text%__1cJPhaseLiveKgetfreeset6M_pnIIndexSet__; +text: .text%__1cHnmethodbHfollow_root_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_ppnHoopDesc_iri_v_; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: classes.o; +text: .text%__1cMMachProjNodeLout_RegMask6kM_rknHRegMask__: classes.o; +text: .text%__1cOPhaseIdealLoopUbuild_loop_tree_impl6MpnENode_i_i_; +text: .text%__1cFState2T6M_v_; +text: .text%__1cIParmNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cHTypeInt2t6Miii_v_; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: ad_i486_misc.o; +text: .text%__1cECopyXconjoint_words_to_lower6FpnIHeapWord_2I_v_: node.o; +text: .text%__1cEUTF8Enext6FpkcpH_pc_; +text: .text%__1cHemit_rm6FrnKCodeBuffer_iii_v_; +text: .text%__1cIciObjectEhash6M_i_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: typeArrayKlass.o; +text: .text%__1cKRegionNodeGOpcode6kM_i_; +text: .text%__1cWNode_Backward_IteratorEnext6M_pnENode__; +text: .text%__1cIMachNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopOget_early_ctrl6MpnENode__2_; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: typeArrayKlass.o; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassIallocate6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cUParallelScavengeHeapVlarge_typearray_limit6M_I_: parallelScavengeHeap.o; +text: .text%__1cOPhaseIdealLoopOset_early_ctrl6MpnENode__v_; +text: .text%__1cHPhiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: cfgnode.o; +text: .text%__1cKoopFactoryNnew_typeArray6FnJBasicType_ipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cHNTarjanELINK6Mp01_v_; +text: .text%__1cENodeHdel_out6Mp0_v_: node.o; +text: .text%__1cLuse_dom_lca6FpnFBlock_pnENode_3rnLBlock_Array__1_: gcm.o; +text: .text%__1cIPhaseGVNJtransform6MpnENode__2_; +text: .text%__1cIIndexSetFclear6M_v_: live.o; +text: .text%__1cOPSPromotionLABKinitialize6MnJMemRegion__v_; +text: .text%__1cOPSPromotionLABFflush6M_v_; +text: .text%__1cITypeNodeEhash6kM_I_; +text: .text%__1cJVectorSet2F6kMI_i_; +text: .text%__1cJPhaseLiveHfreeset6MpknFBlock__v_; +text: .text%__1cENodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOPhaseIdealLoopThas_local_phi_input6MpnENode__2_; +text: .text%__1cIBoolNodeHis_Bool6M_p0_: subnode.o; +text: .text%__1cTleaPIdxScaleOffNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNPhaseRegAllocUreg2offset_unchecked6kMnHOptoRegEName__i_; +text: .text%__1cNPhaseRegAllocKreg2offset6kMnHOptoRegEName__i_; +text: .text%__1cTleaPIdxScaleOffNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachNode2t6M_v_; +text: .text%__1cITypeNodeJideal_reg6kM_I_; +text: .text%__1cLTypeInstPtrEhash6kM_i_; +text: .text%__1cFStateRMachOperGenerator6MipnIMachNode_pnHCompile__pnIMachOper__; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: classes.o; +text: .text%__1cHdom_lca6FpnFBlock_1_1_: gcm.o; +text: .text%__1cENodeNis_block_proj6kM_pk0_; +text: .text%__1cSInterpreterRuntimeInewarray6FpnKJavaThread_nJBasicType_i_v_; +text: .text%__1cPClassFileParserOcheck_property6MipkcipnGThread__v_; +text: .text%__1cKRegionNodeGpinned6kM_i_: classes.o; +text: .text%__1cFMutexGunlock6M_v_; +text: .text%__1cHPhiNodeGpinned6kM_i_: cfgnode.o; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: ad_i486.o; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: ad_i486.o; +text: .text%__1cPjava_lang_ClassLas_klassOop6FpnHoopDesc__pnMklassOopDesc__; +text: .text%__1cFStateDDFA6MipknENode__i_; +text: .text%__1cFState2t6M_v_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: classes.o; +text: .text%__1cKRelocationLunpack_data6M_v_: ad_i486.o; +text: .text%__1cHRegMaskMSmearToPairs6M_v_; +text: .text%__1cKjmpDirNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIIndexSet2t6Mp0_v_; +text: .text%__1cENodeFclone6kM_p0_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: cfgnode.o; +text: .text%__1cRmethodDataOopDescHdata_at6Mi_pnLProfileData__; +text: .text%__1cETypeFxmeet6kMpk0_2_; +text: .text%__1cOPhaseIdealLoopZremix_address_expressions6MpnENode__2_; +text: .text%__1cENodeKmatch_edge6kMI_I_; +text: .text%__1cIPhaseCCPOtransform_once6MpnENode__2_; +text: .text%__1cICallNodeLbottom_type6kM_pknEType__; +text: .text%__1cKTypeAryPtrEhash6kM_i_; +text: .text%__1cENodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cbAfinal_graph_reshaping_impl6FpnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cOPhaseIdealLoopNget_late_ctrl6MpnENode_2_2_; +text: .text%__1cLmethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cOMethodLivenessKBasicBlockXcompute_gen_kill_single6MpnQciByteCodeStream__v_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: callnode.o; +text: .text%__1cICallNodeHis_Call6M_p0_: callnode.o; +text: .text%__1cRMachSpillCopyNodeOimplementation6kMpnKCodeBuffer_pnNPhaseRegAlloc_i_I_; +text: .text%__1cIProjNodeEhash6kM_I_; +text: .text%__1cHemit_d86FrnKCodeBuffer_i_v_; +text: .text%__1cENodeIIdentity6MpnOPhaseTransform__p0_; +text: .text%__1cRMachSafePointNodeEjvms6kM_pnIJVMState__: ad_i486_misc.o; +text: .text%__1cENodeFIdeal6MpnIPhaseGVN_i_p0_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: debugInfo.o; +text: .text%__1cGIfNodeGpinned6kM_i_: classes.o; +text: .text%__1cRSignatureIteratorGexpect6Mc_v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: memnode.o; +text: .text%__1cENodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: instanceKlass.o; +text: .text%__1cNeFlagsRegOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cQUnique_Node_ListGremove6MpnENode__v_; +text: .text%__1cICmpPNodeGOpcode6kM_i_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: location.o; +text: .text%__1cILocationIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cHPhiNodeEhash6kM_I_; +text: .text%__1cRmethodDataOopDescJnext_data6MpnLProfileData__2_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: callnode.o; +text: .text%__1cQconstMethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cTCreateExceptionNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cGciTypeEmake6FnJBasicType__p0_; +text: .text%__1cNLocationValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: cfgnode.o; +text: .text%__1cOis_diamond_phi6FpnENode__i_: cfgnode.o; +text: .text%__1cHCompileMFillLocArray6MpnENode_pnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: ad_i486.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: ad_i486.o; +text: .text%__1cENodeQIdeal_DU_postCCP6MpnIPhaseCCP__p0_; +text: .text%__1cLTypeInstPtrCeq6kMpknEType__i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: memnode.o; +text: .text%__1cMPhaseChaitinSuse_prior_register6MpnENode_I2pnFBlock_rnJNode_List_6_i_; +text: .text%__1cLimpl_helper6FpnKCodeBuffer_iiiiipkci_i_: ad_i486.o; +text: .text%__1cKTypeOopPtrJsingleton6kM_i_; +text: .text%__1cENodeHsize_of6kM_I_; +text: .text%__1cMOopMapStreamJfind_next6M_v_; +text: .text%__1cKJavaThreadPcook_last_frame6MnFframe__1_; +text: .text%__1cIAddINodeGOpcode6kM_i_; +text: .text%__1cIGraphKitHstopped6M_i_; +text: .text%__1cJStartNodeLbottom_type6kM_pknEType__; +text: .text%__1cGIfNodeFis_If6M_p0_: classes.o; +text: .text%__1cMPhaseChaitinHnew_lrg6MpknENode_I_v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: callnode.o; +text: .text%__1cFMutexElock6MpnGThread__v_; +text: .text%__1cTconstantPoolOopDescbAname_and_type_ref_index_at6Mi_i_; +text: .text%__1cENodeSremove_dead_region6MpnIPhaseGVN_i_i_; +text: .text%__1cKTypeOopPtrLxadd_offset6kMi_i_; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_: coalesce.o; +text: .text%__1cHMatcherKLabel_Root6MpknENode_pnFState_p16_6_; +text: .text%__1cJAssemblerOlocate_operand6FpCn0AMWhichOperand__1_; +text: .text%__1cPClassFileStreamGget_u16MpnGThread__C_; +text: .text%__1cHTypeIntFxmeet6kMpknEType__3_; +text: .text%__1cKRelocationSpd_address_in_code6M_ppC_; +text: .text%__1cNSafePointNodeGpinned6kM_i_: callnode.o; +text: .text%__1cOoop_RelocationIoop_addr6M_ppnHoopDesc__; +text: .text%__1cQconstMethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLmethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQconstMethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cICmpINodeGOpcode6kM_i_; +text: .text%__1cFBlockLis_uncommon6kMrnLBlock_Array__i_; +text: .text%__1cKRelocationNunpack_2_ints6Mri1_v_: relocInfo.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: ad_i486_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: classes.o; +text: .text%JVM_CurrentTimeMillis; +text: .text%__1cSCallStaticJavaNodeEhash6kM_I_: callnode.o; +text: .text%__1cMPhaseIterGVNVadd_users_to_worklist6MpnENode__v_; +text: .text%__1cIMachNodeSalignment_required6kM_i_: ad_i486_misc.o; +text: .text%__1cOoop_RelocationLunpack_data6M_v_; +text: .text%__1cIHaltNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: classes.o; +text: .text%__1cNSharedRuntimeDd2i6Fd_i_; +text: .text%__1cGcmpkey6Fpkv1_i_; +text: .text%__1cMMergeMemNodeGOpcode6kM_i_; +text: .text%__1cIMachNodeIpeephole6MpnFBlock_ipnNPhaseRegAlloc_ri_p0_; +text: .text%__1cJCProjNodeGis_CFG6kM_i_: cfgnode.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: cfgnode.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: classes.o; +text: .text%__1cPciObjectFactoryEfind6MpnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cOmatch_into_reg6FpnENode_iii1_i_: matcher.o; +text: .text%__1cENodeHdel_out6Mp0_v_: reg_split.o; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: debugInfoRec.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: ad_i486.o; +text: .text%__1cIMachNodeSalignment_required6kM_i_: ad_i486.o; +text: .text%__1cINodeHashLhash_insert6MpnENode__v_; +text: .text%__1cKTypeAryPtrCeq6kMpknEType__i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: callnode.o; +text: .text%__1cOindOffset8OperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cPciObjectFactoryLis_found_at6MipnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cLTypeInstPtr2t6MnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_v_; +text: .text%__1cENode2t6Mp0_v_; +text: .text%__1cIimmIOperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cLSymbolTableGlookup6MipkciI_pnNsymbolOopDesc__; +text: .text%__1cENodeFis_If6M_pnGIfNode__: classes.o; +text: .text%__1cIProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cETypeKhas_memory6kM_i_; +text: .text%__1cNloadRangeNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLTypeInstPtrEmake6FnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_pk0_; +text: .text%__1cKjmpConNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIJVMStateIof_depth6kMi_p0_; +text: .text%__1cKNode_ArrayEgrow6MI_v_; +text: .text%__1cJStartNodeGpinned6kM_i_: callnode.o; +text: .text%__1cRPSOldPromotionLABFflush6M_v_; +text: .text%__1cNCatchProjNodeGOpcode6kM_i_; +text: .text%__1cENodeGis_CFG6kM_i_: connode.o; +text: .text%__1cHMatcherKReduceOper6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cWShouldNotReachHereNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIPhaseIFGMtest_edge_sq6kMII_i_; +text: .text%__1cGIfNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIConPNodeGOpcode6kM_i_; +text: .text%__1cHTypeIntEmake6Fiii_pk0_; +text: .text%__1cRMachSpillCopyNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFframeVoopmapreg_to_location6kMnFVMRegEName_pknLRegisterMap__ppnHoopDesc__; +text: .text%__1cHPhiNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cICallNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: instanceKlass.o; +text: .text%__1cJTypeTupleJsingleton6kM_i_; +text: .text%__1cJLoadPNodeGOpcode6kM_i_; +text: .text%__1cKCodeBuffer2t6MiiiiiipnKBufferBlob_pnJrelocInfo_pnORelocateBuffer_ipnLOopRecorder_pkcii_v_; +text: .text%__1cLOopRecorder2t6MpnFArena__v_; +text: .text%__1cKCodeBuffer2T6M_v_; +text: .text%__1cKCodeBufferQalloc_relocation6MI_v_; +text: .text%__1cIAddPNodeKmatch_edge6kMI_I_; +text: .text%__1cIMachNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIMachNodeJemit_size6kMpnNPhaseRegAlloc__I_; +text: .text%__1cZPhaseConservativeCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cHCmpNodeGis_Cmp6kM_pk0_: classes.o; +text: .text%__1cFMutexElock6M_v_; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: instanceKlass.o; +text: .text%__1cHTypeIntEmake6Fi_pk0_; +text: .text%__1cPciObjectFactoryDget6MpnHoopDesc__pnIciObject__; +text: .text%__1cENodeRis_cisc_alternate6kM_i_: ad_i486.o; +text: .text%__1cENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNencode_RegMem6FrnKCodeBuffer_iiiiii_v_; +text: .text%__1cMloadConINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cILoadNodeEhash6kM_I_; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pnIciObject_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cENodeMcisc_operand6kM_i_: cfgnode.o; +text: .text%__1cJeRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cICmpUNodeGOpcode6kM_i_; +text: .text%__1cJHashtableLhash_symbol6Fpkci_I_: symbolTable.o; +text: .text%__1cFframeUis_interpreted_frame6kM_i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: cfgnode.o; +text: .text%__1cJCProjNodeEhash6kM_I_: classes.o; +text: .text%__1cHPhiNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFframeYinterpreter_frame_method6kM_pnNmethodOopDesc__; +text: .text%__1cJMultiNodeEhash6kM_I_: classes.o; +text: .text%__1cENodeHdel_req6MI_v_; +text: .text%__1cHCompileJcan_alias6MpknHTypePtr_i_i_; +text: .text%__1cSPSPromotionManagerMdrain_stacks6M_v_; +text: .text%__1cETypeEhash6kM_i_; +text: .text%__1cLOptoRuntimeXdeoptimize_caller_frame6FpnKJavaThread_i_v_; +text: .text%__1cLSymbolTableGlookup6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cKoopFactoryKnew_symbol6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cIHaltNodeGOpcode6kM_i_; +text: .text%__1cZPhaseConservativeCoalesceJcopy_copy6MpnENode_2pnFBlock_I_i_; +text: .text%__1cENodeGis_CFG6kM_i_: subnode.o; +text: .text%__1cMPhaseIterGVNZremove_globally_dead_node6MpnENode__v_; +text: .text%__1cIParmNodeGOpcode6kM_i_; +text: .text%__1cIJVMStateLdebug_start6kM_I_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: classes.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: multnode.o; +text: .text%__1cGTarjanEEVAL6M_p0_; +text: .text%__1cOThreadCritical2T6M_v_; +text: .text%__1cOThreadCritical2t6M_v_; +text: .text%__1cRSignatureIteratorKparse_type6M_i_; +text: .text%__1cFframeVinterpreter_frame_bcp6kM_pC_; +text: .text%__1cNRelocIteratorTadvance_over_prefix6M_v_; +text: .text%__1cOPhaseIdealLoopbIdom_lca_for_get_late_ctrl_internal6MpnENode_22_2_; +text: .text%__1cTconstantPoolOopDescSklass_ref_index_at6Mi_i_; +text: .text%__1cOeFlagsRegUOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_: classes.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: cfgnode.o; +text: .text%__1cFBlockGselect6MrnJNode_List_rnLBlock_Array_pirnJVectorSet_IrnNGrowableArray4CI___pnENode__; +text: .text%__1cKMachIfNodeJis_MachIf6kM_pk0_: ad_i486_misc.o; +text: .text%__1cEDict2F6kMpkv_pv_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nmethod.o; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: multnode.o; +text: .text%__1cICodeHeapKfind_start6kMpv_1_; +text: .text%__1cKTypeAryPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cIMachNodeHtwo_adr6kM_I_: ad_i486.o; +text: .text%__1cHhashptr6Fpkv_i_; +text: .text%__1cIAddPNodeLbottom_type6kM_pknEType__; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cENodeHget_int6kM_i_; +text: .text%__1cMMachTypeNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cJCatchNodeGOpcode6kM_i_; +text: .text%__1cPClassFileStreamHskip_u16MipnGThread__v_; +text: .text%__1cOoop_RelocationJoop_value6M_pnHoopDesc__; +text: .text%__1cHConNodeGis_Con6kM_I_: classes.o; +text: .text%__1cNinstanceKlassLfind_method6FpnPobjArrayOopDesc_pnNsymbolOopDesc_4_pnNmethodOopDesc__; +text: .text%__1cFBlockIis_Empty6kM_i_; +text: .text%__1cWThreadLocalAllocBufferFreset6M_v_; +text: .text%__1cENodeGis_Con6kM_I_: classes.o; +text: .text%__1cGBitMapUclear_range_of_words6MII_v_: bitMap.o; +text: .text%__1cGBitMapFclear6M_v_; +text: .text%__1cbFCompressedLineNumberWriteStreamKwrite_pair6Mii_v_; +text: .text%__1cOPhaseIdealLoopOidom_no_update6kMpnENode__2_: split_if.o; +text: .text%__1cFframeOis_entry_frame6kM_i_; +text: .text%__1cYDebugInformationRecorderLcheck_phase6Mn0AFPhase__v_; +text: .text%__1cNinstanceKlassLfind_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cMMergeMemNodeLbottom_type6kM_pknEType__: memnode.o; +text: .text%__1cLLShiftINodeGOpcode6kM_i_; +text: .text%__1cRCardTableModRefBSEkind6M_nKBarrierSetEName__: cardTableExtension.o; +text: .text%__1cFBlockOcode_alignment6M_I_; +text: .text%__1cMPhaseChaitinLinsert_proj6MpnFBlock_IpnENode_I_v_; +text: .text%__1cKCastPPNodeGOpcode6kM_i_; +text: .text%__1cMMachCallNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cFStateRMachNodeGenerator6MipnHCompile__pnIMachNode__; +text: .text%__1cHMatcherKReduceInst6MpnFState_irpnENode__pnIMachNode__; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: oopMap.o; +text: .text%__1cNCollectedHeapXallocate_from_tlab_slow6FpnGThread_I_pnIHeapWord__; +text: .text%__1cENodeHis_Copy6kM_I_: memnode.o; +text: .text%__1cNsymbolOopDescGequals6kMpkci_i_; +text: .text%__1cUParallelScavengeHeapVunsafe_max_tlab_alloc6kM_I_; +text: .text%__1cJMultiNodeIproj_out6kMI_pnIProjNode__; +text: .text%__1cILoadNodeLbottom_type6kM_pknEType__; +text: .text%__1cIMachNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__: classes.o; +text: .text%__1cWThreadLocalAllocBufferXclear_before_allocation6M_v_; +text: .text%__1cENodeGpinned6kM_i_: connode.o; +text: .text%__1cWThreadLocalAllocBufferKinitialize6MpnIHeapWord_22_v_; +text: .text%__1cUParallelScavengeHeapRallocate_new_tlab6MI_pnIHeapWord__; +text: .text%__1cWThreadLocalAllocBufferEfill6MpnIHeapWord_2I_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: collectedHeap.o; +text: .text%__1cYNoJvmtiVMObjectAllocMark2T6M_v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2t6M_v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: sharedHeap.o; +text: .text%__1cKSharedHeapXfill_region_with_object6FnJMemRegion__v_; +text: .text%__1cIBoolNodeEhash6kM_I_; +text: .text%__1cQciByteCodeStreamEjava6MnJBytecodesECode__2_; +text: .text%__1cSinstanceKlassKlassMoop_is_klass6kM_i_: instanceKlassKlass.o; +text: .text%__1cOindOffset8OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKjmpDirNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKCodeBufferIrelocate6MpCrknQRelocationHolder_i_v_; +text: .text%__1cGIfNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: subnode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: cfgnode.o; +text: .text%__1cPClassFileParserRverify_legal_utf86MpkCipnGThread__v_; +text: .text%__1cPClassFileParserbEparse_constant_pool_utf8_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMMachHaltNodeEjvms6kM_pnIJVMState__; +text: .text%__1cIMachNodeHis_Mach6M_p0_: machnode.o; +text: .text%__1cKjmpConNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cUGenericGrowableArray2t6Mii_v_; +text: .text%__1cOPhaseIdealLoopOidom_no_update6kMpnENode__2_: loopopts.o; +text: .text%__1cNsymbolOopDescLas_C_string6kMpci_1_; +text: .text%__1cNsymbolOopDescLas_C_string6kM_pc_; +text: .text%__1cHMatcherTReduceInst_Interior6MpnFState_ipnIMachNode_IrpnENode__I_; +text: .text%__1cENodeJis_Branch6kM_I_: ad_i486.o; +text: .text%__1cIAddPNodeHis_AddP6M_p0_: classes.o; +text: .text%__1cIMachNodeNis_MachEpilog6M_pnOMachEpilogNode__: ad_i486.o; +text: .text%__1cPSignatureStreamEnext6M_v_; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode__i_; +text: .text%__1cJloadSNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJPSPermGenSallocate_permanent6MI_pnIHeapWord__; +text: .text%__1cMMutableSpaceIallocate6MI_pnIHeapWord__; +text: .text%__1cUParallelScavengeHeapWpermanent_mem_allocate6MI_pnIHeapWord__; +text: .text%__1cHCompileRprobe_alias_cache6MpknHTypePtr__pn0APAliasCacheEntry__; +text: .text%__1cENodeIdestruct6M_v_; +text: .text%__1cMloadConINodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cIMachNodeNoperand_index6kMI_i_; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cKRegionNodeEhash6kM_I_: classes.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: multnode.o; +text: .text%__1cOMachReturnNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cGBitMapJset_union6M0_v_; +text: .text%__1cIMachNodeGExpand6MpnFState_rnJNode_List__p0_: ad_i486_misc.o; +text: .text%__1cIAddPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNCollectedHeapMobj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cNinstanceKlassRallocate_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cMPhaseIterGVNKis_IterGVN6M_p0_: phaseX.o; +text: .text%__1cENodeOis_block_start6kM_i_; +text: .text%__1cPciInstanceKlassMis_interface6M_i_: ciInstanceKlass.o; +text: .text%__1cJeRegLOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cHPhiNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJTypeTupleEhash6kM_i_; +text: .text%__1cNCellTypeStateFmerge6kM0i_0_; +text: .text%__1cHPhiNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKjmpConNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFParseNdo_exceptions6M_v_; +text: .text%__1cFParsePdo_one_bytecode6M_v_; +text: .text%__1cFBlockJfind_node6kMpknENode__I_; +text: .text%__1cRSignatureIterator2t6MnMsymbolHandle__v_; +text: .text%__1cLjmpConUNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cGBitMap2t6MpII_v_; +text: .text%__1cLOptoRuntimeFnew_C6FpnMklassOopDesc_pnKJavaThread__v_; +text: .text%method_compare: methodOop.o; +text: .text%__1cIHaltNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: classes.o; +text: .text%__1cKis_x2logic6FpnIPhaseGVN_pnENode__3_: cfgnode.o; +text: .text%__1cHAbsNodeLis_absolute6FpnIPhaseGVN_pnENode__4_; +text: .text%__1cJloadPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNLoadRangeNodeGOpcode6kM_i_; +text: .text%__1cLPhaseValuesGintcon6Mi_pnIConINode__; +text: .text%__1cHCompilePfind_alias_type6MpknHTypePtr_i_pn0AJAliasType__; +text: .text%__1cKHandleMarkKinitialize6MpnGThread__v_; +text: .text%__1cKHandleMark2T6M_v_; +text: .text%__1cJloadLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOMachReturnNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cGTarjanICOMPRESS6M_v_; +text: .text%__1cTconstantPoolOopDescQsignature_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cHRegMaskMClearToPairs6M_v_; +text: .text%__1cENodeHdel_out6Mp0_v_: block.o; +text: .text%__1cENodeHdel_out6Mp0_v_: parse1.o; +text: .text%__1cMMachCallNodeLis_MachCall6M_p0_: ad_i486_misc.o; +text: .text%__1cNSafePointNodebBneeds_polling_address_input6F_i_; +text: .text%__1cIJVMStateJdebug_end6kM_I_; +text: .text%__1cIMachNodeKconst_size6kM_i_: ad_i486.o; +text: .text%__1cHSubNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJLoadINodeGOpcode6kM_i_; +text: .text%__1cPjava_lang_ClassMis_primitive6FpnHoopDesc__i_; +text: .text%__1cIMachNodeFreloc6kM_i_: ad_i486.o; +text: .text%__1cIProjNodeHsize_of6kM_I_; +text: .text%__1cHMatcherQis_save_on_entry6Mi_i_; +text: .text%__1cLBoxLockNodeNrematerialize6kM_i_: classes.o; +text: .text%__1cRMachSpillCopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPVirtualCallDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cHMatcherKmatch_tree6MpknENode__pnIMachNode__; +text: .text%__1cOindOffset8OperFscale6kM_i_: ad_i486.o; +text: .text%__1cNloadConI0NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIIndexSetSpopulate_free_list6F_v_; +text: .text%__1cLPCTableNodeGpinned6kM_i_: classes.o; +text: .text%__1cNnew_loc_value6FpnNPhaseRegAlloc_nHOptoRegEName_nILocationEType__pnNLocationValue__: output.o; +text: .text%__1cMloadConINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUGenericGrowableArray2t6MpnFArena_iipnEGrET__v_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: cfgnode.o; +text: .text%__1cENodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%JVM_ReleaseUTF; +text: .text%__1cKutf8_write6FpCH_0_: utf8.o; +text: .text%__1cKNode_ArrayGremove6MI_v_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOis_range_check6FpnENode_r12ri_i_: ifnode.o; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: methodDataOop.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: multnode.o; +text: .text%__1cMPhaseIterGVNMsubsume_node6MpnENode_2_v_; +text: .text%__1cJrelocInfo2t6Mn0AJrelocType_ii_v_; +text: .text%__1cMciMethodDataHdata_at6Mi_pnLProfileData__; +text: .text%__1cENodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachNodeNis_MachEpilog6M_pnOMachEpilogNode__: ad_i486_misc.o; +text: .text%__1cJrelocInfoNfinish_prefix6Mph_p0_; +text: .text%__1cMCreateExNodeGOpcode6kM_i_; +text: .text%__1cSloadL_volatileNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cGOopMapbEmap_compiler_reg_to_oopmap_reg6MnHOptoRegEName_ii_nFVMRegEName__; +text: .text%__1cHhashkey6Fpkv_i_; +text: .text%__1cLTypeInstPtrFxmeet6kMpknEType__3_; +text: .text%__1cIJVMState2t6MpnIciMethod_p0_v_; +text: .text%__1cGOopMapHset_xxx6MnHOptoRegEName_nLOopMapValueJoop_types_ii2_v_; +text: .text%__1cLTypeInstPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cENodeHdel_out6Mp0_v_: coalesce.o; +text: .text%__1cGBitMapGat_put6MIi_v_; +text: .text%__1cJloadBNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHdel_out6Mp0_v_: parse2.o; +text: .text%__1cHMatcherTcollect_null_checks6MpnENode__v_; +text: .text%__1cNloadConI0NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENode2t6Mp011_v_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: generateOopMap.o; +text: .text%__1cTconstantPoolOopDescNklass_at_impl6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cHMemNodeMIdeal_common6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cITypeLongCeq6kMpknEType__i_; +text: .text%__1cHCmpNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMMachCallNodeLbottom_type6kM_pknEType__; +text: .text%__1cRMachSafePointNodeQis_MachSafePoint6M_p0_: ad_i486_misc.o; +text: .text%__1cOMethodLivenessKBasicBlockIload_one6Mi_v_; +text: .text%__1cNSafePointNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: ad_i486_misc.o; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: classes.o; +text: .text%__1cIemit_d326FrnKCodeBuffer_i_v_; +text: .text%__1cFDictI2i6M_v_; +text: .text%__1cIJVMStateNclone_shallow6kM_p0_; +text: .text%__1cNloadConI0NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: subnode.o; +text: .text%__1cIMachNodeFreloc6kM_i_: ad_i486_misc.o; +text: .text%__1cFciEnvIis_in_vm6F_i_; +text: .text%__1cNSafePointNodeHsize_of6kM_I_; +text: .text%__1cPCheckCastPPNodeGOpcode6kM_i_; +text: .text%__1cJCodeCacheQfind_blob_unsafe6Fpv_pnICodeBlob__; +text: .text%__1cHTypePtrLmeet_offset6kMi_i_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: connode.o; +text: .text%__1cIHaltNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFChunk2t6MI_v_; +text: .text%__1cFChunk2n6FII_pv_; +text: .text%__1cKciTypeFlowLStateVectorSapply_one_bytecode6MpnQciByteCodeStream__i_; +text: .text%__1cGOopMapJset_value6MnHOptoRegEName_ii_v_; +text: .text%__1cIMachOperLdisp_is_oop6kM_i_; +text: .text%__1cJloadPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFChunk2k6Fpv_v_; +text: .text%__1cOcompU_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeRdisconnect_inputs6Mp0_i_; +text: .text%__1cIIndexSetFclear6M_v_: indexSet.o; +text: .text%__1cIIndexSetJlrg_union6MIIkIpknIPhaseIFG_rknHRegMask__I_; +text: .text%JVM_GetMethodIxExceptionTableLength; +text: .text%__1cETypeFempty6kM_i_; +text: .text%__1cPClassFileParserUassemble_annotations6MpCi1ipnGThread__nPtypeArrayHandle__; +text: .text%__1cJTypeTupleCeq6kMpknEType__i_; +text: .text%__1cRSignatureIteratorSiterate_parameters6M_v_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: split_if.o; +text: .text%__1cENodeHdel_out6Mp0_v_: memnode.o; +text: .text%__1cNPhaseCoalesceRcombine_these_two6MpnENode_2_v_; +text: .text%__1cFMutexbClock_without_safepoint_check6M_v_; +text: .text%__1cPSignatureStreamHis_done6kM_i_; +text: .text%__1cJStoreNodeKmatch_edge6kMI_I_; +text: .text%__1cIBoolNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIBoolTestKcc2logical6kMpknEType__3_; +text: .text%__1cIAddPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFArenaEgrow6MI_pv_; +text: .text%__1cQCompressedStream2t6MpCi_v_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: multnode.o; +text: .text%__1cJeRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cUParallelScavengeHeapPis_in_permanent6kMpkv_i_: parallelScavengeHeap.o; +text: .text%__1cOJNIHandleBlockPallocate_handle6MpnHoopDesc__pnI_jobject__; +text: .text%__1cXruntime_call_RelocationEtype6M_nJrelocInfoJrelocType__: ad_i486.o; +text: .text%__1cENodeHdel_out6Mp0_v_: loopopts.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: cfgnode.o; +text: .text%__1cKBranchDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cENodeGis_CFG6kM_i_: memnode.o; +text: .text%__1cKjmpDirNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cPClassFileStreamGget_u46MpnGThread__I_; +text: .text%__1cFBlockUneeded_for_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cTconstantPoolOopDescWsignature_ref_index_at6Mi_i_; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlass.o; +text: .text%__1cILoadNodeKmatch_edge6kMI_I_; +text: .text%__1cYCallStaticJavaDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHMemNodeGis_Mem6M_p0_: classes.o; +text: .text%__1cMPhaseIterGVNbGregister_new_node_with_optimizer6MpnENode__2_; +text: .text%__1cENodeKreplace_by6Mp0_v_; +text: .text%__1cNPhaseRegAllocGis_oop6kMpknENode__i_; +text: .text%__1cGIfNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNinstanceKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: classes.o; +text: .text%__1cSCountedLoopEndNodeGOpcode6kM_i_; +text: .text%__1cKjmpDirNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHPhiNodeIadr_type6kM_pknHTypePtr__: cfgnode.o; +text: .text%__1cHPhiNodeHsize_of6kM_I_: cfgnode.o; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6FnTobjArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cJTypeTupleGfields6FI_ppknEType__; +text: .text%__1cMPhaseChaitinFUnion6MpknENode_3_v_; +text: .text%__1cJdo_method6FpnNmethodOopDesc__v_: recompilationMonitor.o; +text: .text%__1cZload_can_see_stored_value6FpnILoadNode_pnENode_pnOPhaseTransform__3_: memnode.o; +text: .text%__1cFframeGsender6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cNtestP_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKRegionNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLrecord_bias6FpknIPhaseIFG_ii_v_: coalesce.o; +text: .text%__1cMPhaseChaitinSget_spillcopy_wide6MpnENode_2I_2_; +text: .text%__1cKNativeCallLdestination6kM_pC_; +text: .text%__1cNinstanceKlassQarray_klass_impl6FnTinstanceKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: memnode.o; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__: callnode.o; +text: .text%__1cSCallStaticJavaNodeRis_CallStaticJava6kM_pk0_: callnode.o; +text: .text%__1cIAddPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMMergeMemNodeEhash6kM_I_; +text: .text%__1cJcmpOpOperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cUPSMarkSweepDecoratorQinsert_deadspace6MripnIHeapWord_I_i_; +text: .text%__1cMMergeMemNodeLis_MergeMem6M_p0_: memnode.o; +text: .text%__1cKoopFactoryMnew_objArray6FpnMklassOopDesc_ipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cOoop_RelocationSfix_oop_relocation6M_v_; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cNinstanceKlassRallocate_objArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cPciInstanceKlassRis_instance_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cILoadNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIBoolNodeLbottom_type6kM_pknEType__: subnode.o; +text: .text%__1cMPhaseChaitinNFind_compress6MI_I_; +text: .text%__1cKStorePNodeGOpcode6kM_i_; +text: .text%__1cTconstantPoolOopDescLname_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: methodOop.o; +text: .text%__1cNmethodOopDescPis_empty_method6kM_i_; +text: .text%__1cLjmpConUNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPciInstanceKlassGloader6M_pnHoopDesc__; +text: .text%__1cOno_flip_branch6FpnFBlock__i_: block.o; +text: .text%__1cICodeHeapKnext_block6kMpnJHeapBlock__2_; +text: .text%__1cNmethodOopDescRis_not_compilable6kMi_i_; +text: .text%__1cICodeHeapJnext_free6kMpnJHeapBlock__pv_; +text: .text%__1cKRelocationJpack_data6M_i_: ad_i486.o; +text: .text%__1cICodeHeapLblock_start6kMpv_pnJHeapBlock__; +text: .text%__1cJCodeCacheEnext6FpnICodeBlob__2_; +text: .text%__1cNmethodOopDescLis_accessor6kM_i_; +text: .text%__1cILoadNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cISubINodeGOpcode6kM_i_; +text: .text%__1cKStoreINodeGOpcode6kM_i_; +text: .text%__1cNeFlagsRegOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cQciByteCodeStreamMreset_to_bci6Mi_v_; +text: .text%__1cHRetNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: lcm.o; +text: .text%__1cJStoreNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: machnode.o; +text: .text%__1cRSignatureIteratorTcheck_signature_end6M_v_; +text: .text%__1cRSignatureIteratorSiterate_returntype6M_v_; +text: .text%__1cENodeHis_Goto6kM_I_: classes.o; +text: .text%__1cILoadNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPciInstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceKlass.o; +text: .text%__1cITypeLongEhash6kM_i_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: block.o; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: relocInfo.o; +text: .text%__1cJloadPNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cMPhaseIterGVNFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cLis_cond_add6FpnIPhaseGVN_pnHPhiNode__pnENode__; +text: .text%__1cPsplit_flow_path6FpnIPhaseGVN_pnHPhiNode__pnENode__: cfgnode.o; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: classes.o; +text: .text%__1cKRegionNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKRelocationLunpack_data6M_v_: relocInfo.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: block.o; +text: .text%__1cITypeNodeHsize_of6kM_I_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: memnode.o; +text: .text%__1cFMutexNowned_by_self6kM_i_; +text: .text%__1cIMachNodeRget_base_and_disp6kMrirpknHTypePtr__pknENode__; +text: .text%__1cIAddINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIIndexSetFclear6M_v_: chaitin.o; +text: .text%__1cXPhaseAggressiveCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cFBlockOschedule_local6MrnHMatcher_rnLBlock_Array_pirnJVectorSet_rnNGrowableArray4CI___i_; +text: .text%__1cFBlockScall_catch_cleanup6MrnLBlock_Array__v_; +text: .text%__1cLBlock_StackXmost_frequent_successor6MpnFBlock__I_; +text: .text%__1cNidealize_test6FpnIPhaseGVN_pnGIfNode__3_: ifnode.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: symbolKlass.o; +text: .text%__1cENodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQSystemDictionaryXcheck_signature_loaders6FnMsymbolHandle_nGHandle_2ipnGThread__v_; +text: .text%__1cENodeGpinned6kM_i_: subnode.o; +text: .text%__1cLLShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLCounterDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cKRegionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGTarjanELINK6Mp01_v_; +text: .text%__1cMPhaseChaitinMyank_if_dead6MpnENode_pnFBlock_pnJNode_List_6_i_; +text: .text%__1cYDebugInformationRecorderWserialize_scope_values6MpnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cYDebugInformationRecorderTcreate_scope_values6MpnNGrowableArray4CpnKScopeValue____pnKDebugToken__; +text: .text%__1cIGraphKitJsync_jvms6kM_pnIJVMState__; +text: .text%__1cENodeGis_Con6kM_I_: cfgnode.o; +text: .text%__1cMciMethodDataJnext_data6MpnLProfileData__2_; +text: .text%__1cENodeGpinned6kM_i_: memnode.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: subnode.o; +text: .text%__1cIBoolNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNmethodOopDescIbci_from6kMpC_i_; +text: .text%__1cNSafePointNodeSset_next_exception6Mp0_v_; +text: .text%__1cQResultTypeFinderDset6MinJBasicType__v_: bytecode.o; +text: .text%__1cOcompU_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJCodeCacheJfind_blob6Fpv_pnICodeBlob__; +text: .text%__1cMPhaseChaitinNFind_compress6MpknENode__I_; +text: .text%__1cNmethodOopDescLresult_type6kM_nJBasicType__; +text: .text%__1cFStateM_sub_Op_ConI6MpknENode__v_; +text: .text%__1cNRelocIteratorKset_limits6MpC1_v_; +text: .text%__1cNRelocIteratorKinitialize6MipnICodeBlob_pC3_v_; +text: .text%__1cLsymbolKlassNoop_is_symbol6kM_i_: symbolKlass.o; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: parse1.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: ad_i486.o; +text: .text%__1cIsplit_if6FpnGIfNode_pnMPhaseIterGVN__pnENode__: ifnode.o; +text: .text%__1cIGraphKitEstop6M_v_; +text: .text%__1cTremove_useless_bool6FpnGIfNode_pnIPhaseGVN__pnENode__: ifnode.o; +text: .text%__1cPciObjectFactorySget_unloaded_klass6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cJeRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: subnode.o; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_: systemDictionary.o; +text: .text%__1cMloadConLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cILoadNodeHis_Load6M_p0_: classes.o; +text: .text%__1cSInterpreterRuntimeJanewarray6FpnKJavaThread_pnTconstantPoolOopDesc_ii_v_; +text: .text%__1cKjmpConNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHAddNodeEhash6kM_I_; +text: .text%__1cNSafePointNodeLbottom_type6kM_pknEType__: callnode.o; +text: .text%__1cOemit_d32_reloc6FrnKCodeBuffer_irknQRelocationHolder_i_v_; +text: .text%__1cNSafePointNodeKmatch_edge6kMI_I_; +text: .text%__1cMURShiftINodeGOpcode6kM_i_; +text: .text%__1cOcompI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLjmpConUNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cIRootNodeGOpcode6kM_i_; +text: .text%__1cFChunkEchop6M_v_; +text: .text%__1cIMachOperOindex_position6kM_i_; +text: .text%__1cMloadConPNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cIimmPOperEtype6kM_pknEType__: ad_i486_clone.o; +text: .text%__1cPcheckCastPPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOindOffset8OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOindOffset8OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOindOffset8OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cHRegMaskQis_aligned_Pairs6kM_i_; +text: .text%__1cMloadConLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompU_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cHSubNodeGis_Sub6M_p0_: classes.o; +text: .text%__1cJTypeTupleEmake6FIppknEType__pk0_; +text: .text%__1cYCallStaticJavaDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNeFlagsRegOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKReflectionTverify_field_access6FpnMklassOopDesc_22nLAccessFlags_ii_i_; +text: .text%__1cIregDOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cSInterpreterRuntimeLcache_entry6FpnKJavaThread__pnWConstantPoolCacheEntry__: interpreterRuntime.o; +text: .text%__1cJStoreNodeIis_Store6kM_pk0_: classes.o; +text: .text%__1cKDictionaryJget_entry6MiInMsymbolHandle_nGHandle__pnPDictionaryEntry__; +text: .text%__1cHCmpNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRSignatureIterator2t6MpnNsymbolOopDesc__v_; +text: .text%__1cOBasicHashtableJnew_entry6MI_pnTBasicHashtableEntry__; +text: .text%__1cHi2sNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNtestI_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJHashtableJnew_entry6MIpnHoopDesc__pnOHashtableEntry__; +text: .text%__1cJloadPNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPimpl_mov_helper6FpnKCodeBuffer_iiii_i_: ad_i486.o; +text: .text%__1cHConNodeEhash6kM_I_; +text: .text%__1cKjmpDirNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cKstoreINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%Unsafe_CompareAndSwapLong; +text: .text%__1cJStoreNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPciObjectFactoryNfind_non_perm6MpnHoopDesc__rpn0ANNonPermObject__; +text: .text%__1cFBlockLfind_remove6MpknENode__v_; +text: .text%__1cNinstanceKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cKTypeRawPtrJsingleton6kM_i_; +text: .text%__1cMloadConDNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConDNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJStoreNodeEhash6kM_I_; +text: .text%__1cMPhaseChaitinJsplit_USE6MpnENode_pnFBlock_2IIiinNGrowableArray4CI__i_I_; +text: .text%__1cMloadConDNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENode2t6Mp0111_v_; +text: .text%__1cXindIndexScaleOffsetOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cWShouldNotReachHereNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKTypeRawPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cTAbstractInterpreterLmethod_kind6FnMmethodHandle__n0AKMethodKind__; +text: .text%__1cGOopMapHset_oop6MnHOptoRegEName_ii_v_; +text: .text%__1cHMatcherXadjust_outgoing_stk_arg6MinHOptoRegEName_r2_2_; +text: .text%__1cMMergeMemNodePiteration_setup6Mpk0_v_; +text: .text%__1cJeRegLOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cJMultiNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cJCProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstorePNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cGPcDescHreal_pc6kMpknHnmethod__pC_; +text: .text%__1cLRegisterMapFclear6Mpi_v_; +text: .text%__1cNtestI_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFframeOis_first_frame6kM_i_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: cfgnode.o; +text: .text%__1cENodeMcisc_operand6kM_i_: memnode.o; +text: .text%__1cNtestP_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFChunkJnext_chop6M_v_; +text: .text%__1cHTypeAryRary_must_be_exact6kM_i_; +text: .text%__1cRMachNullCheckNodeQis_MachNullCheck6M_p0_: machnode.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: callnode.o; +text: .text%__1cKIfTrueNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJCodeCacheFalive6FpnICodeBlob__2_; +text: .text%__1cLRShiftINodeGOpcode6kM_i_; +text: .text%__1cOcompU_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_: reg_split.o; +text: .text%__1cLRegisterMap2t6MpnKJavaThread_i_v_; +text: .text%__1cHTypeIntFempty6kM_i_; +text: .text%__1cKJNIHandlesKmake_local6FpnHJNIEnv__pnHoopDesc__pnI_jobject__; +text: .text%__1cJloadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJchar2type6Fc_nJBasicType__: fieldType.o; +text: .text%__1cJFieldTypeKbasic_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cSinstanceKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNmethodOopDescMintrinsic_id6kM_n0ALIntrinsicId__; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceKlass.o; +text: .text%__1cMMergeMemNodeJmemory_at6kMI_pnENode__; +text: .text%__1cNloadRangeNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: subnode.o; +text: .text%__1cITypeNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cTconstantPoolOopDescSklass_at_if_loaded6FnSconstantPoolHandle_i_pnMklassOopDesc__; +text: .text%__1cFciEnvXget_klass_by_index_impl6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cNExceptionMark2T6M_v_; +text: .text%__1cNExceptionMark2t6MrpnGThread__v_; +text: .text%__1cMgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cMloadConPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHOopFlowNcompute_reach6MpnNPhaseRegAlloc_ipnEDict__v_; +text: .text%__1cRInvocationCounterFreset6M_v_; +text: .text%__1cRInvocationCounterJset_state6Mn0AFState__v_; +text: .text%__1cLOptoRuntimePnew_typeArray_C6FnJBasicType_ipnKJavaThread__v_; +text: .text%__1cRInvocationCounterEinit6M_v_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: multnode.o; +text: .text%__1cRMachSafePointNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cUGenericGrowableArrayPraw_at_put_grow6MipknEGrET_3_v_; +text: .text%__1cMMergeMemNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%JVM_GetClassModifiers; +text: .text%__1cTconstantPoolOopDescNklass_name_at6Mi_pnNsymbolOopDesc__; +text: .text%JVM_GetClassAccessFlags; +text: .text%__1cOcompU_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTCreateExceptionNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJCatchNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLIfFalseNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: codeBlob.o; +text: .text%__1cKstoreINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKjmpDirNodeHis_Goto6kM_I_: ad_i486_misc.o; +text: .text%__1cSObjectSynchronizerJfast_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cSObjectSynchronizerKfast_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cNSafePointNodeOnext_exception6kM_p0_; +text: .text%__1cTconstantPoolOopDescRname_ref_index_at6Mi_i_; +text: .text%__1cMMergeMemNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cENodeHdel_out6Mp0_v_: split_if.o; +text: .text%__1cLMachNopNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKRegionNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLPhaseValuesFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: connode.o; +text: .text%__1cOcompU_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNloadKlassNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cICallNodeLis_CallLeaf6kM_pknMCallLeafNode__: callnode.o; +text: .text%__1cMObjectLocker2t6MnGHandle_pnGThread__v_; +text: .text%__1cMObjectLocker2T6M_v_; +text: .text%__1cKmethodOperGmethod6kM_i_: ad_i486.o; +text: .text%__1cWConstantPoolCacheEntryRset_initial_state6Mi_v_; +text: .text%__1cPSignatureStreamJis_object6kM_i_; +text: .text%__1cJloadINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopOidom_no_update6kMpnENode__2_: loopTransform.o; +text: .text%__1cLProfileDataPfollow_contents6M_v_: methodDataOop.o; +text: .text%__1cLProfileDataPadjust_pointers6M_v_: methodDataOop.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: classes.o; +text: .text%__1cOindOffset8OperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cOcompI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNaddI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRmethodDataOopDescPinitialize_data6MpnOBytecodeStream_i_i_; +text: .text%__1cRmethodDataOopDescTbytecode_cell_count6FnJBytecodesECode__i_; +text: .text%__1cRmethodDataOopDescRcompute_data_size6FpnOBytecodeStream__i_; +text: .text%__1cMMergeMemNodeQclone_all_memory6FpnENode__p0_; +text: .text%__1cIGraphKitJclone_map6M_pnNSafePointNode__; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: multnode.o; +text: .text%__1cGOopMapQset_callee_saved6MnHOptoRegEName_ii2_v_; +text: .text%__1cOPhaseIdealLoopIsplit_up6MpnENode_22_i_; +text: .text%__1cKTypeOopPtrWmake_from_klass_common6FpnHciKlass_ii_pk0_; +text: .text%__1cWstatic_stub_RelocationLunpack_data6M_v_; +text: .text%__1cENodeHdel_out6Mp0_v_: postaloc.o; +text: .text%__1cOcompU_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMOopMapStream2t6MpnGOopMap_i_v_; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: subnode.o; +text: .text%__1cNtestI_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIBoolNodeKmatch_edge6kMI_I_: subnode.o; +text: .text%__1cLklassItable2t6MnTinstanceKlassHandle__v_; +text: .text%__1cNinstanceKlassGitable6kM_pnLklassItable__; +text: .text%__1cNCompileBrokerLmaybe_block6F_v_; +text: .text%__1cFStateM_sub_Op_RegP6MpknENode__v_; +text: .text%__1cFPhase2t6Mn0ALPhaseNumber__v_; +text: .text%__1cLciSignatureLreturn_type6kM_pnGciType__; +text: .text%__1cIJumpDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cKstorePNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: symbolKlass.o; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: symbolKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: symbolKlass.o; +text: .text%__1cLsymbolKlassPallocate_symbol6MpCipnGThread__pnNsymbolOopDesc__; +text: .text%__1cLSymbolTableJbasic_add6MipCiIpnGThread__pnNsymbolOopDesc__; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__: callnode.o; +text: .text%__1cKRelocationLspec_simple6FnJrelocInfoJrelocType__nQRelocationHolder__; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_: machnode.o; +text: .text%__1cHRegMaskPfind_first_pair6kM_nHOptoRegEName__; +text: .text%__1cIHaltNodeLbottom_type6kM_pknEType__; +text: .text%__1cRMachSafePointNodeRis_safepoint_node6kM_i_: ad_i486_misc.o; +text: .text%__1cKRegionNodeOis_block_start6kM_i_: classes.o; +text: .text%__1cKTypeAryPtrFxmeet6kMpknEType__3_; +text: .text%__1cMMergeMemNodeNset_memory_at6MIpnENode__v_; +text: .text%__1cRMemBarReleaseNodeGOpcode6kM_i_; +text: .text%__1cOeFlagsRegUOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cKcmpOpUOperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cENodeHdel_out6Mp0_v_: cfgnode.o; +text: .text%__1cCosPelapsed_counter6F_x_; +text: .text%__1cMtlsLoadPNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPClassFileParserbLparse_constant_pool_nameandtype_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cGBitMapOset_difference6M0_v_; +text: .text%__1cPcheckCastPPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKMemBarNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cYCallStaticJavaDirectNodeSalignment_required6kM_i_: ad_i486_misc.o; +text: .text%__1cYCallStaticJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNtestP_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: memnode.o; +text: .text%__1cKstorePNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOindOffset8OperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cXindIndexScaleOffsetOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cRaddI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKstoreINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: memnode.o; +text: .text%__1cOcompI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cKRegionNodeGis_CFG6kM_i_: loopnode.o; +text: .text%__1cNloadRangeNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQciByteCodeStreamFEOBCs6M_nJBytecodesECode__; +text: .text%__1cITypeNodeDcmp6kMrknENode__I_; +text: .text%__1cXInterpreterFrameClosureJoffset_do6Mi_v_: frame.o; +text: .text%__1cMPhaseChaitinPset_was_spilled6MpnENode__v_; +text: .text%__1cHTypeAryEhash6kM_i_; +text: .text%__1cKDictionaryEfind6MiInMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cPfieldDescriptorKinitialize6MpnMklassOopDesc_i_v_; +text: .text%__1cNsubI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverYlookup_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cPVirtualCallDataPadjust_pointers6M_v_; +text: .text%__1cPVirtualCallDataPfollow_contents6M_v_; +text: .text%__1cMMergeMemNodePset_base_memory6MpnENode__v_; +text: .text%__1cSsafePoint_pollNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNLoadKlassNodeGOpcode6kM_i_; +text: .text%__1cLOopRecorderOallocate_index6MpnI_jobject__i_; +text: .text%__1cYDebugInformationRecorderNappend_handle6MpnI_jobject__i_; +text: .text%__1cKTypeRawPtrEhash6kM_i_; +text: .text%__1cIciObjectIencoding6M_pnI_jobject__; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: machnode.o; +text: .text%__1cPPerfLongVariantGsample6M_v_; +text: .text%__1cENodeHis_Copy6kM_I_: machnode.o; +text: .text%__1cIAndINodeGOpcode6kM_i_; +text: .text%__1cRCompilationPolicyNcanBeCompiled6FnMmethodHandle__i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: multnode.o; +text: .text%__1cVCompressedWriteStream2t6Mi_v_; +text: .text%__1cHAddNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJlabelOperFclone6kM_pnIMachOper__; +text: .text%__1cMLinkResolverbAcheck_method_accessability6FnLKlassHandle_11nMmethodHandle_pnGThread__v_; +text: .text%__1cNCatchProjNodeMis_CatchProj6kM_pk0_: cfgnode.o; +text: .text%__1cENode2t6Mp01_v_; +text: .text%__1cRAbstractAssembler2t6MpnKCodeBuffer__v_; +text: .text%__1cNtestI_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: subnode.o; +text: .text%__1cKTypeOopPtrHget_con6kM_i_; +text: .text%__1cMloadConINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMUniverseOperFclone6kM_pnIMachOper__; +text: .text%__1cKRelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: multnode.o; +text: .text%__1cJloadINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMTypeKlassPtrEhash6kM_i_; +text: .text%__1cKRegionNodeHhas_phi6kM_pnHPhiNode__; +text: .text%__1cIMachNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cENodeHins_req6MIp0_v_; +text: .text%__1cPconvI2L_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIciMethodbCinterpreter_invocation_count6M_i_; +text: .text%__1cMciMethodDataLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cKStoreCNodeGOpcode6kM_i_; +text: .text%JVM_GetCPMethodSignatureUTF; +text: .text%__1cOcompI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cRshrI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerJemit_data6MirknQRelocationHolder_i_v_; +text: .text%__1cFframeQoops_do_internal6MpnKOopClosure_pnLRegisterMap_i_v_; +text: .text%__1cRInterpreterOopMapLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cOeFlagsRegUOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLjmpConUNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNSafePointNodeGOpcode6kM_i_; +text: .text%__1cJVectorSet2L6MI_rnDSet__; +text: .text%__1cJVectorSetEgrow6MI_v_; +text: .text%__1cOMethodLivenessKBasicBlockWcompute_gen_kill_range6MpnQciByteCodeStream__v_; +text: .text%__1cHnmethodJis_zombie6kM_i_: nmethod.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: gcm.o; +text: .text%__1cKStoreBNodeGOpcode6kM_i_; +text: .text%__1cPClassFileParserbJparse_constant_pool_methodref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: connode.o; +text: .text%__1cOFastUnlockNodeGOpcode6kM_i_; +text: .text%__1cHPhiNodeEmake6FpnENode_2pknEType_pknHTypePtr__p0_; +text: .text%__1cLeAXRegPOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cUArgumentSizeComputerDset6MinJBasicType__v_: frame.o; +text: .text%__1cENodeHdel_out6Mp0_v_: gcm.o; +text: .text%__1cETypeOget_const_type6FpnGciType__pk0_; +text: .text%__1cPcheckCastPPNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJVectorSet2t6MpnFArena__v_; +text: .text%__1cIGraphKitGmemory6MI_pnENode__; +text: .text%__1cITypeLong2t6Mxxi_v_; +text: .text%__1cKCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQciByteCodeStreamPget_field_index6M_i_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: memnode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: memnode.o; +text: .text%__1cCosMvm_page_size6F_i_; +text: .text%__1cENodeDcmp6kMrk0_I_; +text: .text%__1cKReflectionTverify_class_access6FpnMklassOopDesc_2i_i_; +text: .text%__1cNloadRangeNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_RegI6MpknENode__v_; +text: .text%__1cJloadINodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: methodOop.o; +text: .text%__1cFKlassNlookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cKjmpConNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_AddP6MpknENode__v_; +text: .text%__1cMPhaseChaitinVmay_be_copy_of_callee6kMpnENode__i_; +text: .text%__1cXroundDouble_mem_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitMsaved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cIPhaseCCPFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cFParseKensure_phi6Mii_pnHPhiNode__; +text: .text%__1cJloadFNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: postaloc.o; +text: .text%__1cOMachReturnNodeNis_MachReturn6M_p0_: ad_i486_misc.o; +text: .text%__1cIMachOperMdisp_as_type6kM_pknHTypePtr__: ad_i486.o; +text: .text%__1cPcheckCastPPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIciObjectJset_ident6MI_v_; +text: .text%__1cPciObjectFactoryNinit_ident_of6MpnIciObject__v_; +text: .text%__1cQPreserveJVMState2T6M_v_; +text: .text%__1cQPreserveJVMState2t6MpnIGraphKit_i_v_; +text: .text%__1cNtestP_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKJNIHandlesKmake_local6FpnHoopDesc__pnI_jobject__; +text: .text%__1cIGraphKitLclean_stack6Mi_v_; +text: .text%__1cYDebugInformationRecorderYserialize_monitor_values6MpnNGrowableArray4CpnMMonitorValue____i_; +text: .text%__1cYDebugInformationRecorderVcreate_monitor_values6MpnNGrowableArray4CpnMMonitorValue____pnKDebugToken__; +text: .text%__1cYDebugInformationRecorderOdescribe_scope6MpnIciMethod_ipnKDebugToken_44_v_; +text: .text%__1cWMutableSpaceUsedHelperLtake_sample6M_x_: spaceCounters.o; +text: .text%__1cICallNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cFciEnvSget_klass_by_index6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cLcastP2INodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRCompilationPolicyOmustBeCompiled6FnMmethodHandle__i_; +text: .text%__1cLBoxLockNodeGOpcode6kM_i_; +text: .text%__1cHTypePtrHget_con6kM_i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: callnode.o; +text: .text%__1cITypeFuncEhash6kM_i_; +text: .text%__1cJAssemblerJemit_data6MinJrelocInfoJrelocType_i_v_; +text: .text%__1cJAssemblerMemit_operand6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerMemit_operand6MpnMRegisterImpl_22nHAddressLScaleFactor_irknQRelocationHolder__v_; +text: .text%__1cLBoxLockNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMLinkResolverNresolve_klass6FrnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cTconstantPoolOopDescMklass_ref_at6MipnGThread__pnMklassOopDesc__; +text: .text%__1cNloadRangeNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cLPhaseValuesHmakecon6MpknEType__pnHConNode__; +text: .text%__1cOcompI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIciMethodPliveness_at_bci6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessPget_liveness_at6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessKBasicBlockPget_liveness_at6MpnIciMethod_i_nGBitMap__; +text: .text%__1cPSignatureStream2t6MnMsymbolHandle_i_v_; +text: .text%__1cITypeLongJsingleton6kM_i_; +text: .text%__1cOcompI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cRMemBarAcquireNodeGOpcode6kM_i_; +text: .text%__1cJOopMapSetSfind_map_at_offset6kMii_pnGOopMap__; +text: .text%__1cICodeBlobbAoop_map_for_return_address6MpCi_pnGOopMap__; +text: .text%__1cLCounterDataOis_CounterData6M_i_: ciMethodData.o; +text: .text%__1cHnmethodKis_nmethod6kM_i_: nmethod.o; +text: .text%__1cOGenerateOopMapHinterp16MpnOBytecodeStream__v_; +text: .text%__1cLCastP2INodeGOpcode6kM_i_; +text: .text%__1cKCodeBufferOadd_stub_reloc6MpCrknQRelocationHolder_i_v_; +text: .text%__1cKCodeBufferOalloc_relocate6M_pnORelocateBuffer__; +text: .text%__1cMCallLeafNodeGOpcode6kM_i_; +text: .text%__1cJLoadSNodeGOpcode6kM_i_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: subnode.o; +text: .text%__1cKjmpDirNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHTypePtrJsingleton6kM_i_; +text: .text%__1cJAssemblerEcall6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cMLinkResolverZcheck_klass_accessability6FnLKlassHandle_1pnGThread__v_; +text: .text%__1cIGraphKitObasic_plus_adr6MpnENode_2i_2_; +text: .text%__1cKMemBarNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIimmPOperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cMmerge_region6FpnKRegionNode_pnIPhaseGVN__pnENode__: cfgnode.o; +text: .text%__1cRconstantPoolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cTCreateExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRshrI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMPhaseIterGVNHmakecon6MpknEType__pnHConNode__; +text: .text%__1cMTypeKlassPtrCeq6kMpknEType__i_; +text: .text%__1cRaddI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%JVM_GetMethodIxLocalsCount; +text: .text%__1cRNativeInstructionFwrote6Mi_v_; +text: .text%__1cWShouldNotReachHereNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cICmpPNodeDsub6kMpknEType_3_3_; +text: .text%__1cWConstantPoolCacheEntryPbytecode_number6FnJBytecodesECode__i_: interpreterRuntime.o; +text: .text%__1cWConstantPoolCacheEntryLis_resolved6kMnJBytecodesECode__i_: interpreterRuntime.o; +text: .text%__1cWConstantPoolCacheEntryIas_flags6MnITosState_iiiii_i_; +text: .text%__1cPClassFileParserYverify_legal_method_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cQMachCallJavaNodePis_MachCallJava6M_p0_: ad_i486_misc.o; +text: .text%__1cWShouldNotReachHereNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJimmI0OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cNloadConI0NodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cHMatcherWis_short_branch_offset6Mi_i_; +text: .text%__1cWConstantPoolCacheEntryGverify6kMpnMoutputStream__v_; +text: .text%__1cNloadKlassNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cITypeFuncCeq6kMpknEType__i_; +text: .text%__1cNSafePointNode2t6MIpnIJVMState__v_; +text: .text%__1cOGenerateOopMapPjump_targets_do6MpnOBytecodeStream_pFp0ipi_v4_i_; +text: .text%__1cHcommute6FpnENode_ii_i_: addnode.o; +text: .text%__1cJeRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: cfgnode.o; +text: .text%__1cGOopMapJheap_size6kM_i_; +text: .text%__1cHAddNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLMachNopNodeMideal_Opcode6kM_i_: ad_i486.o; +text: .text%__1cLMachNopNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHCompileYout_preserve_stack_slots6F_I_; +text: .text%__1cIAddINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKjmpDirNodeFclone6kM_pnENode__; +text: .text%__1cIciObject2t6MnGHandle__v_; +text: .text%__1cJTraceTime2T6M_v_; +text: .text%__1cPciObjectFactoryGinsert6MipnIciObject_pnNGrowableArray4C2___v_; +text: .text%__1cWShouldNotReachHereNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKRelocationTpd_call_destination6M_pC_; +text: .text%__1cOCallRelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cKRelocationXpd_set_call_destination6MpCi_v_; +text: .text%__1cOCallRelocationPset_destination6MpCi_v_; +text: .text%__1cJlabelOperFlabel6kM_pnFLabel__: ad_i486.o; +text: .text%__1cIHaltNodeGpinned6kM_i_: classes.o; +text: .text%__1cITypeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIRootNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cWMachCallStaticJavaNodePret_addr_offset6M_i_; +text: .text%__1cYCallStaticJavaDirectNodePcompute_padding6kMi_i_; +text: .text%__1cYCallStaticJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cJStartNodeGpinned6kM_i_: classes.o; +text: .text%__1cMTypeKlassPtr2t6MnHTypePtrDPTR_pnHciKlass_i_v_; +text: .text%__1cJloadPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%JVM_IsNaN; +text: .text%__1cJStoreNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLBoxLockNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQciByteCodeStreamQget_method_index6M_i_; +text: .text%__1cFciEnvbTget_instance_klass_for_declared_method_holder6FpnHciKlass__pnPciInstanceKlass__; +text: .text%__1cRInterpretedRFrameKtop_method6kM_nMmethodHandle__: rframe.o; +text: .text%__1cITypeFuncEmake6FpknJTypeTuple_3_pk0_; +text: .text%__1cPciObjectFactoryRcreate_new_object6MpnHoopDesc__pnIciObject__; +text: .text%__1cIGraphKitQkill_dead_locals6M_v_; +text: .text%__1cCosVcurrent_stack_pointer6F_pC_; +text: .text%__1cICallNodeHis_Call6M_p0_: classes.o; +text: .text%__1cJTraceTime2t6MpkcpnMelapsedTimer_iipnMoutputStream__v_; +text: .text%__1cNaddI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cXmembar_acquire_lockNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cJLoadPNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIGraphKitMreset_memory6M_pnENode__; +text: .text%__1cLklassVtableTupdate_super_vtable6MpnNinstanceKlass_pnNmethodOopDesc_i_i_; +text: .text%__1cNinstanceKlassQfind_local_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__i_; +text: .text%__1cNaddI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeQlatency_from_use6kMrnLBlock_Array_rnNGrowableArray4CI__pk0p0_i_; +text: .text%__1cMPhaseChaitinKprompt_use6MpnFBlock_I_i_; +text: .text%__1cICallInfoDset6MnLKlassHandle_1nMmethodHandle_2ipnGThread__v_; +text: .text%__1cOkill_dead_code6FpnENode_pnMPhaseIterGVN__i_: node.o; +text: .text%__1cFParsePload_state_from6Mpn0AFBlock__v_; +text: .text%__1cFParseMmerge_common6Mpn0AFBlock_i_v_; +text: .text%__1cNloadConI0NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJStartNodeIis_Start6M_p0_: callnode.o; +text: .text%__1cICmpINodeDsub6kMpknEType_3_3_; +text: .text%__1cHMemNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cIAddINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLProfileDataPfollow_contents6M_v_: ciMethodData.o; +text: .text%__1cLProfileDataPadjust_pointers6M_v_: ciMethodData.o; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cNsubI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cICodeBlobJis_zombie6kM_i_: codeBlob.o; +text: .text%__1cJStoreNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNmethodOopDescbGupdate_compiled_code_entry_point6Mi_v_; +text: .text%__1cSvframeStreamCommonPfill_from_frame6M_i_; +text: .text%__1cNloadRangeNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFBlockUhoist_LCA_above_defs6Mp01IrnLBlock_Array__1_; +text: .text%__1cLciSignatureHtype_at6kMi_pnGciType__; +text: .text%__1cIciMethodLscale_count6Mi_i_; +text: .text%__1cOGenerateOopMapEpush6MnNCellTypeState__v_; +text: .text%__1cHMonitorKnotify_all6M_i_; +text: .text%__1cKklassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cKklassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cJMarkSweepXrevisit_weak_klass_link6FpnFKlass__v_; +text: .text%__1cFKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cOCallRelocationFvalue6M_pC_: ad_i486.o; +text: .text%__1cNtestP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMTypeKlassPtrEmake6FnHTypePtrDPTR_pnHciKlass_i_pk0_; +text: .text%__1cOoop_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cScompP_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRSignatureIteratorSiterate_parameters6MX_v_; +text: .text%__1cOPhaseIdealLoopIset_idom6MpnENode_2I_v_; +text: .text%__1cNSafePointNodeGpinned6kM_i_: classes.o; +text: .text%__1cOGenerateOopMapDpop6M_nNCellTypeState__; +text: .text%__1cWconstantPoolCacheKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLklassVtableToop_follow_contents6M_v_; +text: .text%__1cLklassVtableToop_adjust_pointers6M_v_; +text: .text%__1cIRootNodeNis_block_proj6kM_pknENode__: classes.o; +text: .text%__1cENodeHis_Type6M_pnITypeNode__: classes.o; +text: .text%__1cNmethodOopDescPis_final_method6kM_i_; +text: .text%__1cOPhaseIdealLoopQconditional_move6MpnENode__2_; +text: .text%__1cNSignatureInfoHdo_void6M_v_: bytecode.o; +text: .text%__1cNloadKlassNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitRnull_check_common6MpnENode_nJBasicType_i_2_; +text: .text%__1cICmpLNodeGOpcode6kM_i_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: constMethodKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: constMethodKlass.o; +text: .text%__1cSconstMethodOopDescLobject_size6Fiiii_i_; +text: .text%__1cKoopFactoryPnew_constMethod6FiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cPClassFileParserMparse_method6MnSconstantPoolHandle_ipnLAccessFlags_pnPtypeArrayHandle_55pnGThread__nMmethodHandle__; +text: .text%__1cKoopFactoryKnew_method6FinLAccessFlags_iiipnGThread__pnNmethodOopDesc__; +text: .text%__1cLklassVtableWneeds_new_vtable_entry6FpnNmethodOopDesc_pnMklassOopDesc_pnHoopDesc_pnNsymbolOopDesc_nLAccessFlags__i_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: constMethodKlass.o; +text: .text%__1cQconstMethodKlassIallocate6MiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cSconstMethodOopDescZset_inlined_tables_length6Miii_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: methodKlass.o; +text: .text%__1cNmethodOopDescLobject_size6Fi_i_; +text: .text%__1cNmethodOopDescJinit_code6M_v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: methodKlass.o; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: methodKlass.o; +text: .text%__1cLmethodKlassIallocate6MnRconstMethodHandle_nLAccessFlags_pnGThread__pnNmethodOopDesc__; +text: .text%__1cNmethodOopDescbAcompute_size_of_parameters6MpnGThread__v_; +text: .text%__1cOGenerateOopMapRdo_exception_edge6MpnOBytecodeStream__v_; +text: .text%__1cOcompU_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTciConstantPoolCacheEfind6Mi_i_; +text: .text%__1cKciTypeFlowGJsrSetJcopy_into6Mp1_v_; +text: .text%__1cJLoadLNodeGOpcode6kM_i_; +text: .text%__1cHOrINodeGOpcode6kM_i_; +text: .text%__1cILoadNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cJMarkSweepNpreserve_mark6FpnHoopDesc_pnLmarkOopDesc__v_; +text: .text%__1cIRewriterOrewrite_method6FnMmethodHandle_rnIintArray_pnGThread__1_; +text: .text%__1cNmethodOopDescLlink_method6FnMmethodHandle__v_; +text: .text%__1cPClassFileParserbDverify_legal_method_modifiers6MiinMsymbolHandle_pnGThread__v_; +text: .text%__1cJloadINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQSystemDictionaryEfind6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cFParseFBlockRsuccessor_for_bci6Mi_p1_; +text: .text%__1cQleaPIdxScaleNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKTypeOopPtrFempty6kM_i_; +text: .text%__1cJleaP8NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%JVM_CurrentThread; +text: .text%__1cPindOffset32OperFscale6kM_i_: ad_i486.o; +text: .text%__1cKBranchDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cLOopMapCacheIentry_at6kMi_pnQOopMapCacheEntry__; +text: .text%__1cRInlineCacheBufferIcontains6FpC_i_; +text: .text%__1cKCompiledICWis_in_transition_state6kM_i_; +text: .text%__1cHOopFlowEmake6FpnFArena_i_p0_; +text: .text%__1cOGenerateOopMapKcheck_type6MnNCellTypeState_1_v_; +text: .text%__1cMMergeMemNodeNgrow_to_match6Mpk0_v_; +text: .text%__1cVeADXRegL_low_onlyOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cVloadConL_low_onlyNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQleaPIdxScaleNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHTypeIntFxdual6kM_pknEType__; +text: .text%__1cVloadConL_low_onlyNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNMachIdealNodePoper_input_base6kM_I_: machnode.o; +text: .text%__1cNSharedRuntimeDf2i6Ff_i_; +text: .text%__1cLLShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cVloadConL_low_onlyNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKTypeAryPtrFklass6kM_pnHciKlass__; +text: .text%__1cRaddI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cRRawBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cRInterpretedRFrameEinit6M_v_; +text: .text%__1cMMergeMemNodeRmake_empty_memory6F_pnENode__; +text: .text%__1cMMergeMemNode2t6MpnENode__v_; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_: callnode.o; +text: .text%__1cHciFieldJwill_link6MpnPciInstanceKlass_nJBytecodesECode__i_; +text: .text%__1cFciEnvXget_field_by_index_impl6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cPciInstanceKlassLfield_cache6M_pnTciConstantPoolCache__; +text: .text%__1cFciEnvSget_field_by_index6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cQciByteCodeStreamJget_field6Mri_pnHciField__; +text: .text%__1cKBlock_ListGremove6MI_v_; +text: .text%__1cECopyXconjoint_words_to_lower6FpnIHeapWord_2I_v_: block.o; +text: .text%__1cXvirtual_call_RelocationIparse_ic6FrpnICodeBlob_rpC5rppnHoopDesc_pi_nNRelocIterator__; +text: .text%__1cWConstantPoolCacheEntryOset_bytecode_16MnJBytecodesECode__v_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: connode.o; +text: .text%__1cIPhaseGVNUtransform_no_reclaim6MpnENode__2_; +text: .text%__1cMLinkResolverbFlinktime_resolve_virtual_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%JVM_GetCPMethodClassNameUTF; +text: .text%__1cKCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNstoreImmBNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHOopFlowFclone6Mp0i_v_; +text: .text%__1cSCallLeafDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKCompiledICOic_destination6kM_pC_; +text: .text%__1cMPhaseChaitinJsplit_DEF6MpnENode_pnFBlock_iIp25nNGrowableArray4CI__i_I_; +text: .text%__1cNGCTaskManagerNresource_flag6MI_i_; +text: .text%__1cNGCTaskManagerYshould_release_resources6MI_i_; +text: .text%__1cNtestP_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLStringTableGlookup6MipHiI_pnHoopDesc__; +text: .text%__1cIBoolNodeJideal_reg6kM_I_: subnode.o; +text: .text%__1cFStateM_sub_Op_Bool6MpknENode__v_; +text: .text%__1cHCmpNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cPSignatureStreamJas_symbol6MpnGThread__pnNsymbolOopDesc__; +text: .text%__1cPciInstanceKlassYunique_concrete_subklass6M_p0_; +text: .text%__1cRcmpFastUnlockNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHAddNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cENodeFis_If6M_pnGIfNode__: subnode.o; +text: .text%__1cKciTypeFlowNmake_range_at6Mi_pn0AFRange__; +text: .text%__1cTCreateExceptionNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKjmpDirNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNaddI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNinstanceKlassVshould_be_initialized6kM_i_; +text: .text%__1cGThreadLis_in_stack6kMpC_i_; +text: .text%__1cKJavaThreadNis_lock_owned6kMpC_i_; +text: .text%__1cJStartNodeGOpcode6kM_i_; +text: .text%__1cPconvF2D_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: subnode.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: subnode.o; +text: .text%__1cOMethodLivenessKBasicBlockJstore_one6Mi_v_; +text: .text%__1cbFCompressedLineNumberWriteStream2t6Mi_v_; +text: .text%__1cSconstMethodOopDescbBcompressed_linenumber_table6kM_pC_; +text: .text%__1cPClassFileParserWparse_linenumber_table6MIIpipnGThread__pC_; +text: .text%__1cNsubI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cMWarmCallInfoHis_cold6kM_i_; +text: .text%__1cFStateK_sub_Op_If6MpknENode__v_; +text: .text%__1cJleaP8NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFParseFBlockJinit_node6Mp0i_v_; +text: .text%__1cFParseFBlockKinit_graph6Mp0_v_; +text: .text%__1cGBitMapVset_union_with_result6M0_i_; +text: .text%__1cOPhaseIdealLoopRregister_new_node6MpnENode_2_v_; +text: .text%__1cNtestI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIPhaseIFGFUnion6MII_v_; +text: .text%__1cIGraphKit2t6MpnIJVMState__v_; +text: .text%__1cKCompiledIC2t6MpnKRelocation__v_; +text: .text%__1cLcastP2INodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadKlassNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cWConstantPoolCacheEntryOset_bytecode_26MnJBytecodesECode__v_; +text: .text%__1cJStartNodeGis_CFG6kM_i_: callnode.o; +text: .text%__1cLBoxLockNodeHsize_of6kM_I_; +text: .text%__1cNinstanceKlassUfollow_static_fields6M_v_; +text: .text%__1cNinstanceKlassUadjust_static_fields6M_v_; +text: .text%__1cRconstantPoolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLklassItableToop_adjust_pointers6M_v_; +text: .text%__1cSinstanceKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLklassItableToop_follow_contents6M_v_; +text: .text%__1cSinstanceKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cRconstantPoolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cICmpUNodeDsub6kMpknEType_3_3_; +text: .text%__1cNincI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKjmpConNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: graphKit.o; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cTciConstantPoolCacheDget6Mi_pv_; +text: .text%__1cScompI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeHis_Goto6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: memnode.o; +text: .text%__1cOGenerateOopMapFppop16MnNCellTypeState__v_; +text: .text%__1cNLoadRangeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cScompI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: subnode.o; +text: .text%__1cHTypePtrLdual_offset6kM_i_; +text: .text%__1cNGCTaskManagerIget_task6MI_pnGGCTask__; +text: .text%__1cGGCTaskKinitialize6M_v_; +text: .text%__1cNGCTaskManagerWdecrement_busy_workers6M_I_; +text: .text%__1cLGCTaskQdDueueGremove6M_pnGGCTask__; +text: .text%__1cNGCTaskManagerWincrement_busy_workers6M_I_; +text: .text%__1cLGCTaskQdDueueHdequeue6M_pnGGCTask__; +text: .text%__1cLGCTaskQdDueueHenqueue6MpnGGCTask__v_; +text: .text%__1cNGCTaskManagerPnote_completion6MI_v_; +text: .text%__1cENodeHlatency6MI_I_; +text: .text%__1cNsubI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNtestI_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFframeZsender_for_compiled_frame6kMpnLRegisterMap_pnICodeBlob_i_0_; +text: .text%__1cPClassFileParserbFparse_constant_pool_class_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNstoreImmPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cICodeBlobLlink_offset6M_i_; +text: .text%__1cFParseFBlockMrecord_state6Mp0_v_; +text: .text%__1cFParseMdo_one_block6M_v_; +text: .text%__1cJCatchNodeIis_Catch6kM_pk0_: classes.o; +text: .text%__1cENodeHdel_out6Mp0_v_: addnode.o; +text: .text%__1cOMethodLivenessKBasicBlockMmerge_normal6MnGBitMap__i_; +text: .text%__1cLConvI2LNodeGOpcode6kM_i_; +text: .text%__1cPClassFileParserZskip_over_field_signature6MpciIpnGThread__1_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: callnode.o; +text: .text%__1cIGraphKitTadd_safepoint_edges6MpnNSafePointNode_i_v_; +text: .text%__1cIJVMStateKclone_deep6kM_p0_; +text: .text%__1cENodeNadd_req_batch6Mp0I_v_; +text: .text%__1cIJVMStateLdebug_depth6kM_I_; +text: .text%__1cSvframeStreamCommonbBfill_from_interpreter_frame6M_v_; +text: .text%__1cJloadINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFParseFmerge6Mi_v_; +text: .text%__1cMLinkResolverZcheck_field_accessability6FnLKlassHandle_11rnPfieldDescriptor_pnGThread__v_; +text: .text%__1cPFieldAccessInfoDset6MnLKlassHandle_nMsymbolHandle_iinJBasicType_nLAccessFlags__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_iipnGThread__v_; +text: .text%__1cNaddI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cICmpPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cWconstantPoolCacheKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWconstantPoolCacheKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cKBufferBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cFframebFinterpreter_frame_monitor_begin6kM_pnPBasicObjectLock__; +text: .text%__1cGGCTask2t6M_v_; +text: .text%__1cOMacroAssemblerZneeds_explicit_null_check6Fi_i_; +text: .text%__1cNstoreImmBNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cETypeFwiden6kMpk0_2_: type.o; +text: .text%__1cIAddPNodeQmach_bottom_type6FpknIMachNode__pknEType__; +text: .text%__1cJLoadBNodeGOpcode6kM_i_; +text: .text%__1cRsalI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQleaPIdxScaleNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cFframeNis_java_frame6kM_i_; +text: .text%__1cOPhaseIdealLoopGspinup6MpnENode_2222pnLsmall_cache__2_; +text: .text%__1cRcmpFastUnlockNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: callnode.o; +text: .text%__1cJloadCNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNSafePointNodeEhash6kM_I_: callnode.o; +text: .text%__1cMnabxRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cPCountedLoopNodeGOpcode6kM_i_; +text: .text%__1cKciTypeFlowIblock_at6Mipn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cKciTypeFlowFRangeNget_block_for6Mpn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cHTypeAryFxmeet6kMpknEType__3_; +text: .text%__1cLjmpConUNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNaddI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTCreateExceptionNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cGvframe2t6MpknFframe_pknLRegisterMap_pnKJavaThread__v_; +text: .text%__1cLRegisterMap2t6Mpk0_v_; +text: .text%__1cUGenericGrowableArrayMraw_contains6kMpknEGrET__i_; +text: .text%__1cSvframeStreamCommonEnext6M_v_; +text: .text%__1cIMachNodeMcisc_operand6kM_i_: machnode.o; +text: .text%__1cITypeFuncEmake6FpnIciMethod__pk0_; +text: .text%__1cENodeLnonnull_req6kM_p0_; +text: .text%__1cSPSPromotionManagerbBgc_thread_promotion_manager6Fi_p0_; +text: .text%__1cITypeLongFxmeet6kMpknEType__3_; +text: .text%__1cRcmpFastUnlockNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIAddINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKstoreCNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowLStateVector2t6Mp0_v_; +text: .text%__1cOMethodLivenessNwork_list_get6M_pn0AKBasicBlock__; +text: .text%__1cIIndexSetFclear6M_v_: coalesce.o; +text: .text%__1cOPhaseIdealLoopKhandle_use6MpnENode_2pnLsmall_cache_22222_v_; +text: .text%__1cOPhaseIdealLoopOfind_use_block6MpnENode_22222_2_; +text: .text%__1cXmembar_release_lockNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cGvframeKnew_vframe6FpknFframe_pknLRegisterMap_pnKJavaThread__p0_; +text: .text%__1cCosGmalloc6FI_pv_; +text: .text%__1cNmethodOopDescWwas_executed_more_than6kMi_i_; +text: .text%__1cRInterpreterOopMapKinitialize6M_v_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: machnode.o; +text: .text%__1cRMachNullCheckNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cILoopNodeGOpcode6kM_i_; +text: .text%__1cICmpINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: bytecode.o; +text: .text%__1cHCompileKTracePhase2t6MpkcpnMelapsedTimer_i_v_; +text: .text%__1cHCompileKTracePhase2T6M_v_; +text: .text%__1cILoadNodeHsize_of6kM_I_; +text: .text%__1cKciTypeFlowFBlockPis_simpler_than6Mp1_i_; +text: .text%__1cRshrI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cVjava_lang_ClassLoaderbBnon_reflection_class_loader6FpnHoopDesc__2_; +text: .text%__1cFciEnvYget_method_by_index_impl6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvTget_method_by_index6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cQciByteCodeStreamKget_method6Mri_pnIciMethod__; +text: .text%__1cFciEnvNlookup_method6MpnNinstanceKlass_2pnNsymbolOopDesc_4nJBytecodesECode__pnNmethodOopDesc__; +text: .text%__1cQleaPIdxScaleNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNCatchProjNodeLbottom_type6kM_pknEType__: cfgnode.o; +text: .text%__1cXindIndexScaleOffsetOperFscale6kM_i_: ad_i486.o; +text: .text%__1cNCatchProjNodeHsize_of6kM_I_: cfgnode.o; +text: .text%__1cKciTypeFlowLStateVectorEmeet6Mpk1_i_; +text: .text%__1cTOopMapForCacheEntryRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cRMachSafePointNode2t6M_v_; +text: .text%__1cHMatcherKmatch_sfpt6MpnNSafePointNode__pnIMachNode__; +text: .text%__1cFframeVinterpreter_frame_bci6kM_i_; +text: .text%__1cQleaPIdxScaleNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOcompI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHemit_cc6FrnKCodeBuffer_ii_v_; +text: .text%__1cJloadLNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cNCatchProjNodeEhash6kM_I_; +text: .text%__1cNincI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cURethrowExceptionNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopHdom_lca6kMpnENode_2_2_; +text: .text%__1cHMatcherScalling_convention6FpnLRegPair_Ii_v_; +text: .text%__1cIGraphKitTadd_exception_state6MpnNSafePointNode__v_; +text: .text%__1cQindOffset32XOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cICallNodeOis_CallRuntime6kM_pknPCallRuntimeNode__: callnode.o; +text: .text%__1cXinsert_anti_dependences6FrpnFBlock_pnENode_rnLBlock_Array__i_: gcm.o; +text: .text%__1cIimmLOperJconstantL6kM_x_: ad_i486_clone.o; +text: .text%__1cWflagsReg_long_LTGEOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cENodeHdel_out6Mp0_v_: lcm.o; +text: .text%__1cXAdaptiveWeightedAverageYcompute_adaptive_average6Mff_f_; +text: .text%__1cIGraphKitOset_all_memory6MpnENode__v_; +text: .text%__1cFKlassIsubklass6kM_p0_; +text: .text%__1cFframebDinterpreter_frame_monitor_end6kM_pnPBasicObjectLock__; +text: .text%__1cHMatcherVReduceInst_Chain_Rule6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cMciMethodDataLhas_trap_at6MpnLProfileData_i_i_; +text: .text%__1cICodeBlobTfix_oop_relocations6MpC1_v_; +text: .text%__1cICodeBlobTfix_oop_relocations6M_v_; +text: .text%__1cNmethodOopDescImask_for6MipnRInterpreterOopMap__v_; +text: .text%__1cLOopMapCacheGlookup6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cRInterpreterOopMap2t6M_v_; +text: .text%__1cRInterpreterOopMap2T6M_v_; +text: .text%__1cRInterpreterOopMapLiterate_oop6MpnNOffsetClosure__v_; +text: .text%__1cRInterpreterOopMapNresource_copy6MpnQOopMapCacheEntry__v_; +text: .text%__1cNinstanceKlassImask_for6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cFframeToops_interpreted_do6MpnKOopClosure_pknLRegisterMap_i_v_; +text: .text%__1cNinstanceKlassKinitialize6MpnGThread__v_; +text: .text%__1cHMatcherPc_frame_pointer6kM_nHOptoRegEName__; +text: .text%__1cMMachCallNode2t6M_v_; +text: .text%__1cFBlockKsched_call6MrnHMatcher_rnLBlock_Array_IrnJNode_List_pipnMMachCallNode_rnJVectorSet__I_; +text: .text%__1cOJNIHandleBlockHoops_do6MpnKOopClosure__v_; +text: .text%__1cHTypeIntEmake6Fii_pk0_; +text: .text%__1cOMethodLivenessKBasicBlockJpropagate6Mp0_v_; +text: .text%__1cNsubI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJOopMapSetTupdate_register_map6FpknFframe_pnICodeBlob_pnLRegisterMap__v_; +text: .text%__1cNsubI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cQSystemDictionaryKfind_class6FiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cKDictionaryKfind_class6MiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cIGraphKitbLset_predefined_input_for_runtime_call6MpnNSafePointNode__v_; +text: .text%JVM_GetCPFieldClassNameUTF; +text: .text%__1cLcastP2INodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: ciStreams.o; +text: .text%__1cHRetNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHBitDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cScompP_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowLStateVectorOpush_translate6MpnGciType__v_; +text: .text%__1cQSystemDictionarybEresolve_instance_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cKciTypeFlowGJsrSet2t6MpnFArena_i_v_; +text: .text%__1cENodeJset_req_X6MIp0pnMPhaseIterGVN__v_; +text: .text%__1cOcompU_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassSallocate_permanent6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNstoreImmPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRcmpFastUnlockNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cScompI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: connode.o; +text: .text%__1cPindOffset32OperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: frame.o; +text: .text%__1cLBoxLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHnmethodIis_alive6kM_i_: nmethod.o; +text: .text%__1cPClassFileParserbGparse_constant_pool_string_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNloadConI0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: connode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: connode.o; +text: .text%__1cHCompileSflatten_alias_type6kMpknHTypePtr__3_; +text: .text%__1cNincI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: callnode.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: memnode.o; +text: .text%__1cLBoxLockNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cMorI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cGOopMap2t6Mii_v_; +text: .text%__1cJOopMapSetKadd_gc_map6MiipnGOopMap__v_; +text: .text%__1cYDebugInformationRecorderKadd_oopmap6MiipnGOopMap__v_; +text: .text%__1cMoutputStreamPupdate_position6MpkcI_v_; +text: .text%__1cMstringStreamFwrite6MpkcI_v_; +text: .text%__1cMFastLockNodeGOpcode6kM_i_; +text: .text%__1cIciMethodRhas_compiled_code6M_i_; +text: .text%__1cMLinkResolverMresolve_pool6FrnLKlassHandle_rnMsymbolHandle_42nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverOresolve_invoke6FrnICallInfo_nGHandle_nSconstantPoolHandle_inJBytecodesECode_pnGThread__v_; +text: .text%__1cNmethodOopDescIbcp_from6kMi_pC_; +text: .text%__1cICallInfoDset6MnLKlassHandle_nMmethodHandle_pnGThread__v_; +text: .text%__1cKstoreCNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNloadKlassNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvL2INodeGOpcode6kM_i_; +text: .text%__1cRMachSafePointNodeSis_MachCallRuntime6M_pnTMachCallRuntimeNode__: ad_i486_misc.o; +text: .text%__1cHnmethodOis_not_entrant6kM_i_: nmethod.o; +text: .text%__1cIGraphKitbDtransfer_exceptions_into_jvms6M_pnIJVMState__; +text: .text%__1cITypeLongEmake6Fx_pk0_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: ciTypeFlow.o; +text: .text%__1cJloadSNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cICallNodeJideal_reg6kM_I_: callnode.o; +text: .text%__1cRMachSafePointNodePis_MachCallLeaf6M_pnQMachCallLeafNode__: ad_i486_misc.o; +text: .text%__1cRMachSafePointNodeLset_oop_map6MpnGOopMap__v_: ad_i486_misc.o; +text: .text%__1cYDebugInformationRecorderNadd_safepoint6MiipnGOopMap__v_; +text: .text%__1cHOopFlowNbuild_oop_map6MpnENode_ipnNPhaseRegAlloc_pi_pnGOopMap__; +text: .text%__1cHCompileTProcess_OopMap_Node6MpnIMachNode_i_v_; +text: .text%__1cENodeGis_Con6kM_I_: callnode.o; +text: .text%__1cILoopNodeHis_Loop6M_p0_: classes.o; +text: .text%__1cOoop_RelocationJpack_data6M_i_; +text: .text%__1cJVectorSetFClear6M_v_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: cfgnode.o; +text: .text%__1cMCallJavaNodeLis_CallJava6kM_pk0_: callnode.o; +text: .text%__1cICallNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_ipnGThread__v_; +text: .text%__1cWConstantPoolCacheEntryJset_field6MnJBytecodesECode_2nLKlassHandle_iinITosState_ii_v_; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: interpreterRuntime.o; +text: .text%__1cSInterpreterRuntimePresolve_get_put6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cYCallStaticJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMachOperIconstant6kM_i_; +text: .text%__1cRaddI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXAdaptiveWeightedAverageGsample6Mf_v_; +text: .text%__1cWMachCallStaticJavaNodeVis_MachCallStaticJava6M_p0_: ad_i486_misc.o; +text: .text%__1cFStateW_sub_Op_CallStaticJava6MpknENode__v_; +text: .text%__1cRinterpretedVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cLklassVtableNput_method_at6MpnNmethodOopDesc_i_v_; +text: .text%__1cMloadConPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNdecI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPVirtualCallDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cENodeHdel_out6Mp0_v_: parse3.o; +text: .text%__1cMloadConLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadKlassNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJleaP8NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKRelocationLunpack_data6M_v_: codeBlob.o; +text: .text%__1cJimmI8OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cKstoreCNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cGOopMapHcopy_to6MpC_v_; +text: .text%__1cRMachSafePointNodeWis_MachCallInterpreter6M_pnXMachCallInterpreterNode__: ad_i486_misc.o; +text: .text%__1cLPhaseValuesHzerocon6MnJBasicType__pnHConNode__; +text: .text%__1cScompI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: classes.o; +text: .text%__1cOGenerateOopMapGppush16MnNCellTypeState__v_; +text: .text%__1cSInterpreterRuntimeOresolve_invoke6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: cpCacheOop.o; +text: .text%__1cMPhaseChaitinLclone_projs6MpnFBlock_IpnENode_4rI_i_; +text: .text%__1cCosEfree6Fpv_v_; +text: .text%__1cKInlineTreeJcallee_at6kMipnIciMethod__p0_; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: thread.o; +text: .text%__1cRcmpFastUnlockNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOleaPIdxOffNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJCodeCacheIcontains6Fpv_i_; +text: .text%__1cQSystemDictionarybCfind_instance_or_array_klass6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cMVirtualSpaceOcommitted_size6kM_I_; +text: .text%__1cNinstanceKlassSlookup_osr_nmethod6kMkpnNmethodOopDesc_i_pnHnmethod__; +text: .text%__1cGRFrame2t6MnFframe_pnKJavaThread_kp0_v_; +text: .text%__1cYCallStaticJavaDirectNodeFreloc6kM_i_; +text: .text%__1cKciTypeFlowLStateVectorJcopy_into6kMp1_v_; +text: .text%__1cPThreadLocalNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJLoadCNodeGOpcode6kM_i_; +text: .text%__1cKciTypeFlowFBlockKsuccessors6MpnQciByteCodeStream_pn0ALStateVector_pn0AGJsrSet__pnNGrowableArray4Cp1___; +text: .text%__1cKciTypeFlowQadd_to_work_list6Mpn0AFBlock__v_; +text: .text%__1cKciTypeFlowOwork_list_next6M_pn0AFBlock__; +text: .text%__1cKciTypeFlowKflow_block6Mpn0AFBlock_pn0ALStateVector_pn0AGJsrSet__v_; +text: .text%__1cWConstantPoolCacheEntryKset_method6MnJBytecodesECode_nMmethodHandle_i_v_; +text: .text%__1cJOopMapSetGall_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure_pFppnHoopDesc_9E_v9B9B_v_; +text: .text%__1cFframeRoops_code_blob_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cIGraphKitNuncommon_trap6MipnHciKlass_pkci_v_; +text: .text%__1cJOopMapSetHoops_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cIGraphKitJmake_load6MpnENode_2pknEType_nJBasicType_i_2_; +text: .text%__1cILoadNodeEmake6FpnENode_22pknHTypePtr_pknEType_nJBasicType__p0_; +text: .text%__1cOleaPIdxOffNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLOptoRuntimeSuncommon_trap_Type6F_pknITypeFunc__; +text: .text%__1cIHaltNode2t6MpnENode_2_v_; +text: .text%__1cMindirectOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cKciTypeFlowFBlock2t6Mp0pn0AFRange_pn0AGJsrSet__v_; +text: .text%__1cJloadLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLPCTableNodeLbottom_type6kM_pknEType__; +text: .text%__1cMCreateExNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cYciExceptionHandlerStreamFcount6M_i_; +text: .text%__1cKciTypeFlowFBlockScompute_exceptions6M_v_; +text: .text%__1cScompU_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFciEnvWget_klass_by_name_impl6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: psTasks.o; +text: .text%__1cWstatic_stub_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cIimmPOperPconstant_is_oop6kM_i_: ad_i486_clone.o; +text: .text%__1cLanyRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cLanyRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOMethodLivenessNwork_list_add6Mpn0AKBasicBlock__v_; +text: .text%__1cMURShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFParsePdo_field_access6Mii_v_; +text: .text%__1cPCountedLoopNodeOis_CountedLoop6M_p0_: classes.o; +text: .text%__1cIAndLNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitQset_saved_ex_oop6FpnNSafePointNode_pnENode__v_; +text: .text%__1cKciTypeFlowPflow_successors6MpnNGrowableArray4Cpn0AFBlock___pn0ALStateVector__v_; +text: .text%__1cIGraphKitUmake_exception_state6MpnENode__pnNSafePointNode__; +text: .text%__1cLcastP2INodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRshrI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGPcDesc2t6Miii_v_; +text: .text%__1cHnmethodKcopy_pc_at6MipnGPcDesc__v_; +text: .text%__1cIPipelineXfunctional_unit_latency6kMIpk0_I_; +text: .text%__1cLTypeInstPtrFxdual6kM_pknEType__; +text: .text%__1cIJumpDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cNdecI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFTypeDCeq6kMpknEType__i_; +text: .text%__1cSindIndexOffsetOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cIMulLNodeGOpcode6kM_i_; +text: .text%__1cOMethodLivenessNmake_block_at6Mipn0AKBasicBlock__2_; +text: .text%__1cENodeHis_Goto6kM_I_: cfgnode.o; +text: .text%__1cHCompileKalias_type6MpnHciField__pn0AJAliasType__; +text: .text%__1cINodeHashJhash_find6MpknENode__p1_; +text: .text%__1cNloadConL0NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_nGHandle_2ipnGThread__pnMklassOopDesc__; +text: .text%__1cLLShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRsalI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJLoadINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLnaxRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cFParseFBlockNlocal_type_at6kMi_pknEType__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodKlass.o; +text: .text%__1cXjava_lang_ref_ReferenceNreferent_addr6FpnHoopDesc__p2_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: connode.o; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__: loopnode.o; +text: .text%__1cMelapsedTimerFstart6M_v_; +text: .text%__1cMelapsedTimerEstop6M_v_; +text: .text%__1cNaddI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: methodLiveness.o; +text: .text%__1cNloadConL0NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cLStringTableGintern6FnGHandle_pHipnGThread__pnHoopDesc__; +text: .text%__1cLStringTableLhash_string6FpHi_i_; +text: .text%__1cMMergeMemNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cEUTF8Hstrrchr6FpWiW_1_; +text: .text%__1cPshlI_eReg_1NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMCreateExNodeGpinned6kM_i_: classes.o; +text: .text%__1cIXorINodeGOpcode6kM_i_; +text: .text%__1cRindIndexScaleOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cNloadConL0NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cVLoaderConstraintTableWfind_loader_constraint6MnMsymbolHandle_nGHandle__ppnVLoaderConstraintEntry__; +text: .text%__1cMPhaseIterGVNJtransform6MpnENode__2_; +text: .text%__1cHi2sNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLLShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRindIndexScaleOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMPhaseChaitinTsplit_Rematerialize6MpnENode_pnFBlock_IrInNGrowableArray4CI__ipIp2i_2_; +text: .text%__1cOGenerateOopMapFppush6MpnNCellTypeState__v_; +text: .text%__1cENodeHis_Call6M_pnICallNode__: ad_i486_misc.o; +text: .text%__1cIConLNodeGOpcode6kM_i_; +text: .text%__1cHCompileZintrinsic_insertion_index6MpnIciMethod_i_i_; +text: .text%__1cNstoreImmBNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQSystemDictionarybOfind_constrained_instance_or_array_klass6FnMsymbolHandle_nGHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cILoopNodeHis_Loop6M_p0_: loopnode.o; +text: .text%__1cRandI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKstoreINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cGciTypeMis_classless6kM_i_: ciType.o; +text: .text%__1cTleaPIdxScaleOffNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHTypeIntFwiden6kMpknEType__3_; +text: .text%__1cNCallGenerator2t6MpnIciMethod__v_; +text: .text%__1cIGraphKit2t6M_v_; +text: .text%__1cHMulNodeEhash6kM_I_; +text: .text%__1cFStateM_sub_Op_ConP6MpknENode__v_; +text: .text%__1cIciSymbol2t6MnMsymbolHandle__v_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: codeBlob.o; +text: .text%__1cIAddLNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitNset_map_clone6MpnNSafePointNode__v_; +text: .text%__1cIGraphKitOreplace_in_map6MpnENode_2_v_; +text: .text%__1cPClassFileParserbCverify_legal_field_signature6MnMsymbolHandle_1pnGThread__v_; +text: .text%__1cPClassFileParserXverify_legal_field_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cScompU_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHCompilebAallow_range_check_smearing6kM_i_; +text: .text%__1cITypeLongEmake6Fxxi_pk0_; +text: .text%JVM_GetCPMethodNameUTF; +text: .text%__1cPmethodDataKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNmodI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLjmpConUNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNLoadKlassNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJeRegIOperFclone6kM_pnIMachOper__; +text: .text%__1cQciByteCodeStreamZget_declared_field_holder6M_pnPciInstanceKlass__; +text: .text%__1cQciByteCodeStreamWget_field_holder_index6M_i_; +text: .text%__1cNSafePointNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: classes.o; +text: .text%__1cTconstantPoolOopDescOstring_at_impl6FnSconstantPoolHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cTconstantPoolOopDescbCverify_constant_pool_resolve6FnSconstantPoolHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cNdecI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLOptoRuntimeOnew_objArray_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cQjava_lang_StringQbasic_create_oop6FpnQtypeArrayOopDesc_ipnGThread__pnHoopDesc__; +text: .text%__1cPcheckCastPPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadSNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKReturnNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cMLinkResolverbNlinktime_resolve_virtual_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cNmethodOopDescbHhas_unloaded_classes_in_signature6FnMmethodHandle_pnGThread__i_; +text: .text%__1cPindOffset32OperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cPindOffset32OperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cNinstanceKlassbBallocate_permanent_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cQmark_inner_loops6FpnIPhaseCFG_pnFBlock__v_: block.o; +text: .text%__1cPindOffset32OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cPindOffset32OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cPindOffset32OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOemit_d32_reloc6FrnKCodeBuffer_inJrelocInfoJrelocType_i_v_; +text: .text%__1cJloadLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadSNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cEUTF8Sconvert_to_unicode6FpkcpHi_v_; +text: .text%__1cKjmpConNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeFis_If6M_pnGIfNode__: memnode.o; +text: .text%__1cLBuildCutout2t6MpnIGraphKit_pnENode_ff_v_; +text: .text%__1cLBuildCutout2T6M_v_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: graphKit.o; +text: .text%__1cPDictionaryEntrybAcontains_protection_domain6kMpnHoopDesc__i_; +text: .text%__1cNPhaseRegAllocKoffset2reg6kMi_nHOptoRegEName__; +text: .text%__1cFParseRensure_memory_phi6Mii_pnHPhiNode__; +text: .text%__1cHTypeAryCeq6kMpknEType__i_; +text: .text%__1cMorI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadRangeNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIAddINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOPhaseIdealLoopIsink_use6MpnENode_2_v_; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: memnode.o; +text: .text%__1cKMemoryPoolYrecord_peak_memory_usage6M_v_; +text: .text%__1cNinstanceKlassKlink_class6MpnGThread__v_; +text: .text%__1cTStackWalkCompPolicyRcompilation_level6MnMmethodHandle_i_i_; +text: .text%__1cIMachNodeTmay_be_short_branch6kM_i_: ad_i486_misc.o; +text: .text%__1cKRegionNodeGpinned6kM_i_: loopnode.o; +text: .text%__1cJloadINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKBranchDataNis_BranchData6M_i_: ciMethodData.o; +text: .text%__1cJloadBNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKMemBarNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIJumpDataLis_JumpData6M_i_: ciMethodData.o; +text: .text%__1cNinstanceKlassPinitialize_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: frame.o; +text: .text%__1cJxRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cTconstantPoolOopDescbBbasic_type_for_signature_at6Mi_nJBasicType__; +text: .text%__1cUSafepointSynchronizeOsafepoint_safe6FpnKJavaThread_nPJavaThreadState__i_; +text: .text%__1cUThreadSafepointStateXexamine_state_of_thread6Mi_v_; +text: .text%__1cNsubI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMethodLivenessKBasicBlockQcompute_gen_kill6MpnIciMethod__v_; +text: .text%__1cOMethodLivenessKBasicBlock2t6Mp0ii_v_; +text: .text%__1cOcompI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cJcmpOpOperFccode6kM_i_: ad_i486_clone.o; +text: .text%__1cOGenerateOopMapbAget_basic_block_containing6kMi_pnKBasicBlock__; +text: .text%__1cIciMethodRget_flow_analysis6M_pnKciTypeFlow__; +text: .text%__1cENodeHdel_out6Mp0_v_: ifnode.o; +text: .text%__1cEUTF8Ounicode_length6Fpkci_i_; +text: .text%__1cRSignatureIteratorSskip_optional_size6M_v_; +text: .text%__1cKjmpDirNodeOis_pc_relative6kM_i_: ad_i486_misc.o; +text: .text%__1cKjmpDirNodeTmay_be_short_branch6kM_i_: ad_i486_misc.o; +text: .text%__1cKRegionNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFParseKdo_get_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cScompP_mem_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapSget_basic_block_at6kMi_pnKBasicBlock__; +text: .text%__1cJleaP8NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cISubINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cRaddI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKReturnNodeKmatch_edge6kMI_I_; +text: .text%__1cRshrI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPcmpFastLockNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowGJsrSetNapply_control6Mp0pnQciByteCodeStream_pn0ALStateVector__v_; +text: .text%__1cPClassFileParserWparse_field_attributes6MnSconstantPoolHandle_iHpHpi2pnPtypeArrayHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_field_modifiers6MiipnGThread__v_; +text: .text%__1cKReturnNodeGOpcode6kM_i_; +text: .text%__1cKHandleAreaHoops_do6MpnKOopClosure__v_; +text: .text%__1cNchunk_oops_do6FpnKOopClosure_pnFChunk_pc_I_: handles.o; +text: .text%__1cGThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_virtual_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cMLinkResolverUresolve_virtual_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cKstorePNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLStringTableGintern6FpnNsymbolOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cNsymbolOopDescKas_unicode6kMri_pH_; +text: .text%__1cFKlassXcan_be_statically_bound6FpnNmethodOopDesc__i_; +text: .text%__1cQjava_lang_StringMbasic_create6FpnQtypeArrayOopDesc_ipnGThread__nGHandle__; +text: .text%__1cIMachNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIMachNodeOpipeline_class6F_pknIPipeline__; +text: .text%__1cUParallelScavengeHeapNtlab_capacity6kM_I_; +text: .text%__1cKjmpConNodeOis_pc_relative6kM_i_: ad_i486_misc.o; +text: .text%__1cKjmpConNodeTmay_be_short_branch6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: connode.o; +text: .text%__1cPshlI_eReg_1NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachNodeHtwo_adr6kM_I_: machnode.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: machnode.o; +text: .text%__1cHi2sNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: memnode.o; +text: .text%__1cENodeRis_cisc_alternate6kM_i_: machnode.o; +text: .text%__1cICallNodeSis_CallDynamicJava6kM_pknTCallDynamicJavaNode__: callnode.o; +text: .text%__1cKTypeRawPtrHget_con6kM_i_; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: machnode.o; +text: .text%__1cKStoreLNodeGOpcode6kM_i_; +text: .text%__1cPClassFileParserbIparse_constant_pool_fieldref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cScompU_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cHConNodeLout_RegMask6kM_rknHRegMask__: classes.o; +text: .text%__1cETypeRget_typeflow_type6FpnGciType__pk0_; +text: .text%__1cHPhiNodeEmake6FpnENode_2_p0_; +text: .text%__1cHAddNodePadd_of_identity6kMpknEType_3_3_; +text: .text%__1cQindOffset32XOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cRandI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMWarmCallInfoKalways_hot6F_p0_; +text: .text%__1cMWarmCallInfoGis_hot6kM_i_; +text: .text%__1cMTypeKlassPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cMPhaseChaitinKFind_const6kMI_I_; +text: .text%__1cMPhaseChaitinKFind_const6kMpknENode__I_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopTransform.o; +text: .text%__1cHCompileFstart6kM_pnJStartNode__; +text: .text%JVM_GetCPFieldSignatureUTF; +text: .text%__1cTemit_java_to_interp6FrnKCodeBuffer__v_; +text: .text%__1cKRelocationJpack_data6M_i_: relocInfo.o; +text: .text%__1cKCodeBufferMstart_a_stub6M_v_; +text: .text%__1cKCodeBufferKend_a_stub6M_v_; +text: .text%__1cPThreadRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cPThreadRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cIBoolNodeHsize_of6kM_I_; +text: .text%__1cFParseUprofile_taken_branch6Mi_v_; +text: .text%__1cPmethodDataKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cPmethodDataKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMloadConLNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cTleaPIdxScaleOffNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFframeNis_glue_frame6kM_i_; +text: .text%__1cPcmpFastLockNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cXjava_lang_ref_ReferenceJnext_addr6FpnHoopDesc__p2_; +text: .text%__1cKstoreLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRMachNullCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: machnode.o; +text: .text%__1cICodeHeapLheader_size6F_I_; +text: .text%__1cScompU_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_: methodDataOop.o; +text: .text%__1cXjava_lang_ref_ReferencePdiscovered_addr6FpnHoopDesc__p2_; +text: .text%__1cKJavaThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cTleaPIdxScaleOffNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJTypeTupleKmake_range6FpnLciSignature__pk0_; +text: .text%__1cPcheckCastPPNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJStoreNodeSIdeal_masked_input6MpnIPhaseGVN_I_pnENode__; +text: .text%__1cIAddINodeIadd_ring6kMpknEType_3_3_; +text: .text%jni_DeleteLocalRef: jni.o; +text: .text%__1cJTypeTupleLmake_domain6FpnPciInstanceKlass_pnLciSignature__pk0_; +text: .text%__1cSMemBarCPUOrderNodeGOpcode6kM_i_; +text: .text%__1cWShouldNotReachHereNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_Halt6MpknENode__v_; +text: .text%__1cIHaltNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cWflagsReg_long_EQdDNEOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cMoutputStreamDput6Mc_v_; +text: .text%__1cJStoreNodeZIdeal_sign_extended_input6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cWThreadLocalAllocBufferVinitialize_statistics6M_v_; +text: .text%__1cWThreadLocalAllocBufferImax_size6F_I_; +text: .text%__1cWThreadLocalAllocBufferFclear6M_v_; +text: .text%__1cSInterpreterRuntimeDldc6FpnKJavaThread_i_v_; +text: .text%__1cLProfileDataOtranslate_from6Mp0_v_: ciMethodData.o; +text: .text%__1cFframeLreal_sender6kMpnLRegisterMap__0_; +text: .text%__1cTStackWalkCompPolicyIsenderOf6MpnGRFrame_pnNGrowableArray4C2___2_; +text: .text%__1cENodeLbottom_type6kM_pknEType__; +text: .text%__1cFframeTis_first_java_frame6kM_i_; +text: .text%__1cGRFrameGcaller6M_p0_; +text: .text%__1cGRFrameMset_distance6Mi_v_; +text: .text%__1cGRFrameKnew_RFrame6FnFframe_pnKJavaThread_kp0_4_; +text: .text%__1cHConNodeEmake6FpknEType__p0_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlassKlass.o; +text: .text%__1cWThreadLocalAllocBufferVaccumulate_statistics6MIi_v_; +text: .text%__1cWThreadLocalAllocBufferGresize6M_v_; +text: .text%__1cJloadPNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIGraphKitOtoo_many_traps6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cWflagsReg_long_LEGTOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cKJNIHandlesKmake_local6FpnGThread_pnHoopDesc__pnI_jobject__; +text: .text%__1cSObjectSynchronizerKslow_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cQindOffset32XOperFscale6kM_i_: ad_i486.o; +text: .text%__1cUThreadSafepointStateMroll_forward6Mn0AMsuspend_type_pnHnmethod_i_v_; +text: .text%__1cGThreadQunboost_priority6Fp0_v_; +text: .text%__1cUThreadSafepointStateHrestart6M_v_; +text: .text%__1cIGraphKitTtoo_many_recompiles6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cGIfNodeHsize_of6kM_I_: classes.o; +text: .text%__1cZPhaseConservativeCoalesceKupdate_ifg6MIIpnIIndexSet_2_v_; +text: .text%__1cIIndexSetEswap6Mp0_v_; +text: .text%__1cZPhaseConservativeCoalesceMunion_helper6MpnENode_2II222pnFBlock_I_v_; +text: .text%__1cLcastP2INodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitZadd_exception_states_from6MpnIJVMState__v_; +text: .text%__1cScompP_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKoopFactoryXnew_permanent_charArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cQjava_lang_StringbBcreate_tenured_from_unicode6FpHipnGThread__nGHandle__; +text: .text%__1cLStringTableJbasic_add6MinGHandle_pHiIpnGThread__pnHoopDesc__; +text: .text%__1cPcmpFastLockNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cScompP_mem_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapHget_var6Mi_nNCellTypeState__; +text: .text%__1cXcmpL_reg_flags_LTGENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: relocInfo.o; +text: .text%__1cQCallLeafNoFPNodeGOpcode6kM_i_; +text: .text%__1cHCompileOcall_generator6MpnIciMethod_ipnIJVMState_if_pnNCallGenerator__; +text: .text%__1cNciCallProfileRapply_prof_factor6Mf_v_; +text: .text%__1cIciMethodTcall_profile_at_bci6Mi_nNciCallProfile__; +text: .text%__1cHCompileOfind_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cXmembar_acquire_lockNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: multnode.o; +text: .text%__1cENodeHdel_out6Mp0_v_: compile.o; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_: codeBlob.o; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_kpnGRFrame__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_special_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_4ipnGThread__v_; +text: .text%__1cMLinkResolverbFlinktime_resolve_special_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverUresolve_special_call6FrnICallInfo_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cXindIndexScaleOffsetOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cXindIndexScaleOffsetOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cXindIndexScaleOffsetOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cPBytecode_invokeFindex6kM_i_; +text: .text%__1cNCatchProjNode2t6MpnENode_Ii_v_; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_: ciMethodData.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: cfgnode.o; +text: .text%__1cQciByteCodeStreamXget_method_holder_index6M_i_; +text: .text%__1cFParseHdo_call6M_v_; +text: .text%__1cIGraphKitWround_double_arguments6MpnIciMethod__v_; +text: .text%__1cIGraphKitTround_double_result6MpnIciMethod__v_; +text: .text%__1cFParseZcan_not_compile_call_site6MpnIciMethod_pnPciInstanceKlass__i_; +text: .text%__1cQciByteCodeStreambAget_declared_method_holder6M_pnHciKlass__; +text: .text%__1cFParseMprofile_call6MpnENode__v_; +text: .text%__1cKstorePNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMMachProjNodeHsize_of6kM_I_: classes.o; +text: .text%__1cPciInstanceKlassFsuper6M_p0_; +text: .text%__1cNCatchProjNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFKlassOis_subclass_of6kMpnMklassOopDesc__i_; +text: .text%__1cSindIndexOffsetOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMeADXRegLOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cKciTypeFlowLStateVectorJdo_invoke6MpnQciByteCodeStream_i_v_; +text: .text%__1cHMulNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMtlsLoadPNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cNstoreImmPNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLPCTableNodeHsize_of6kM_I_: classes.o; +text: .text%__1cLPCTableNodeKis_PCTable6kM_pk0_: classes.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: cfgnode.o; +text: .text%__1cJStartNodeIis_Start6M_p0_: classes.o; +text: .text%__1cLPCTableNodeEhash6kM_I_; +text: .text%__1cMURShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNObjectMonitorEexit6MpnGThread__v_; +text: .text%__1cIProjNodeDcmp6kMrknENode__I_; +text: .text%__1cXindIndexScaleOffsetOperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cSmembar_acquireNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKDataLayoutKinitialize6MCHi_v_; +text: .text%__1cKDataLayoutPneeds_array_len6FC_i_; +text: .text%__1cScompP_mem_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNincI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMindirectOperKin_RegMask6kMi_pknHRegMask__; +text: .text%jni_GetObjectField: jni.o; +text: .text%__1cSCompareAndSwapNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cRInvocationCounterJset_carry6M_v_; +text: .text%__1cJloadBNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRAbstractAssemblerHbind_to6MrnFLabel_i_v_; +text: .text%__1cRAbstractAssemblerEbind6MrnFLabel__v_; +text: .text%__1cOCompilerOracleOshould_exclude6FnMmethodHandle__i_; +text: .text%__1cMnabxRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNObjectMonitorFenter6MpnGThread__v_; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: ad_i486_misc.o; +text: .text%__1cFframeUentry_frame_is_first6kM_i_; +text: .text%__1cHciFieldPinitialize_from6MpnPfieldDescriptor__v_; +text: .text%__1cHPhiNodeMslice_memory6kMpknHTypePtr__p0_; +text: .text%__1cKMemBarNodeEhash6kM_I_; +text: .text%__1cPstoreImmI16NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIciSymbolJmake_impl6Fpkc_p0_; +text: .text%__1cIciSymbolEmake6Fpkc_p0_; +text: .text%__1cXcmpL_reg_flags_LTGENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNloadConI0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNtestI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitNcast_not_null6MpnENode__2_; +text: .text%__1cKEntryPointFentry6kMnITosState__pC_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: nmethod.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: loopnode.o; +text: .text%__1cJleaP8NodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cKGCStatInfoMset_gc_usage6MinLMemoryUsage_i_v_; +text: .text%__1cWCallLeafNoFPDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cGRFrameLis_compiled6kM_i_: rframe.o; +text: .text%__1cRInterpretedRFrameOis_interpreted6kM_i_: rframe.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: callnode.o; +text: .text%__1cRsalI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPciInstanceKlassUget_canonical_holder6Mi_p0_; +text: .text%__1cENodeHdel_out6Mp0_v_: callnode.o; +text: .text%__1cISubINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cILoadNodeDcmp6kMrknENode__I_; +text: .text%__1cMDisplacementEbind6MrnFLabel_ipnRAbstractAssembler__v_; +text: .text%__1cTStackWalkCompPolicyMshouldInline6FnMmethodHandle_fi_pkc_; +text: .text%__1cTconstantPoolOopDescMklass_at_put6MipnMklassOopDesc__v_: constantPoolOop.o; +text: .text%__1cFTypeDEhash6kM_i_; +text: .text%__1cIGraphKitHjava_bc6kM_nJBytecodesECode__; +text: .text%__1cKarrayKlassLobject_size6kMi_i_; +text: .text%__1cIGraphKitNbuiltin_throw6MnODeoptimizationLDeoptReason_pnENode__v_; +text: .text%__1cIciMethodOshould_exclude6M_i_; +text: .text%__1cKInlineTreeWfind_subtree_from_root6Fp0pnIJVMState_pnIciMethod_i_1_; +text: .text%__1cIciMethodNshould_inline6M_i_; +text: .text%__1cKInlineTreeMshouldInline6kMpnIciMethod_irnNciCallProfile_pnMWarmCallInfo__pkc_; +text: .text%__1cTpass_initial_checks6FpnIciMethod_i1_i_; +text: .text%__1cOCompilerOracleNshould_inline6FnMmethodHandle__i_; +text: .text%__1cIciMethodbAinterpreter_throwout_count6kM_i_; +text: .text%__1cKInlineTreeMok_to_inline6MpnIciMethod_pnIJVMState_rnNciCallProfile_pnMWarmCallInfo__8_; +text: .text%__1cKInlineTreeNtry_to_inline6MpnIciMethod_irnNciCallProfile_pnMWarmCallInfo__pkc_; +text: .text%__1cVExceptionHandlerTableJadd_entry6MnRHandlerTableEntry__v_; +text: .text%__1cKstoreINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLRShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRaddI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTCreateExceptionNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOjmpLoopEndNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKstoreLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cGThreadXclear_pending_exception6M_v_; +text: .text%__1cLRethrowNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cLklassVtableIindex_of6kMpnNmethodOopDesc_i_i_; +text: .text%__1cFStateM_sub_Op_CmpP6MpknENode__v_; +text: .text%__1cMorI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cLTypeInstPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cIciMethodWwas_executed_more_than6Mi_i_; +text: .text%__1cRshrI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConINodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cKstoreLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHMonitorEwait6Mil_i_; +text: .text%__1cOGenerateOopMapGppload6MpnNCellTypeState_i_v_; +text: .text%__1cTCompareAndSwapLNodeGOpcode6kM_i_; +text: .text%__1cScompP_mem_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKarrayKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cJAssemblerEmovl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cLOpaque1NodeGOpcode6kM_i_; +text: .text%__1cSObjectSynchronizerJslow_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cbBopt_virtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cPcmpFastLockNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLnaxRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNCompileBrokerYcheck_compilation_result6FnMmethodHandle_iippnHnmethod__i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlass.o; +text: .text%__1cJloadCNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKJavaThreadbHcheck_and_handle_async_exceptions6Mi_v_; +text: .text%__1cbAjni_check_async_exceptions6FpnKJavaThread__v_: jni.o; +text: .text%__1cKciTypeFlowLStateVectorStype_meet_internal6FpnGciType_3p0_3_; +text: .text%__1cLeDXRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cJFieldTypeSskip_optional_size6FpnNsymbolOopDesc_pi_v_; +text: .text%__1cQjmpCon_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQjmpCon_shortNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSobjArrayKlassKlassIoop_size6kMpnHoopDesc__i_: objArrayKlassKlass.o; +text: .text%__1cIGraphKitZset_all_rewritable_memory6MpnENode__v_; +text: .text%__1cIGraphKitTset_all_memory_call6MpnENode__v_; +text: .text%__1cJloadSNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKjmpDirNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cKjmpDirNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIBoolNodeDcmp6kMrknENode__I_; +text: .text%__1cKjmpDirNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cScompI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cICodeBlobLoop_addr_at6kMi_ppnHoopDesc__; +text: .text%__1cRaddI_eReg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadRangeNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJFieldTypeYis_valid_array_signature6FpnNsymbolOopDesc__i_; +text: .text%__1cJFieldTypeOget_array_info6FpnNsymbolOopDesc_pip2pnGThread__nJBasicType__; +text: .text%__1cFKlassMnext_sibling6kM_p0_; +text: .text%__1cPCheckCastPPNodeJideal_reg6kM_I_: connode.o; +text: .text%__1cJloadCNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cOClearArrayNodeGOpcode6kM_i_; +text: .text%__1cPThreadLocalNodeGOpcode6kM_i_; +text: .text%__1cRcmpFastUnlockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMeADXRegLOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cFParseRbranch_prediction6Mrf_f_; +text: .text%__1cRandI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLeAXRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLeAXRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cPshlI_eReg_1NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowLStateVectorMdo_getstatic6MpnQciByteCodeStream__v_; +text: .text%__1cISubINodeDsub6kMpknEType_3_3_; +text: .text%__1cNCompileBrokerXcompilation_is_in_queue6FnMmethodHandle_i_i_; +text: .text%__1cSInterpreterRuntimebAfrequency_counter_overflow6FpnKJavaThread_pC_x_; +text: .text%__1cRindIndexScaleOperFscale6kM_i_: ad_i486.o; +text: .text%__1cNaddI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cLmethodKlassNoop_is_method6kM_i_: methodKlass.o; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceKlass.o; +text: .text%__1cENodeHdel_out6Mp0_v_: loopnode.o; +text: .text%__1cJxRegIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cMLinkResolverVresolve_invokevirtual6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_: loopnode.o; +text: .text%__1cIParmNodeJideal_reg6kM_I_; +text: .text%__1cICodeHeapSallocated_capacity6kM_I_; +text: .text%__1cHciKlassNis_subtype_of6Mp0_i_; +text: .text%__1cNtestP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICHeapObj2n6FI_pv_; +text: .text%__1cENodeHdel_out6Mp0_v_: loopTransform.o; +text: .text%__1cQleaPIdxScaleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cNloadConI0NodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cHTypeAryFempty6kM_i_; +text: .text%__1cKTypeAryPtrFempty6kM_i_; +text: .text%__1cCosFsleep6FpnGThread_xi_i_; +text: .text%__1cNgetTimeMillis6F_x_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: scopeDesc.o; +text: .text%__1cIos_sleep6Fxi_i_: os_solaris.o; +text: .text%__1cOPhaseIdealLoopOsplit_thru_phi6MpnENode_2i_2_; +text: .text%__1cENodeRlatency_from_uses6kMrnLBlock_Array_rnNGrowableArray4CI___i_; +text: .text%__1cXPhaseAggressiveCoalesceYinsert_copy_with_overlap6MpnFBlock_pnENode_II_v_; +text: .text%__1cCosOis_interrupted6FpnGThread_i_i_; +text: .text%__1cNaddI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_i_v_; +text: .text%__1cNloadKlassNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateN_sub_Op_LoadP6MpknENode__v_; +text: .text%__1cNmethodOopDescWload_signature_classes6FnMmethodHandle_pnGThread__i_; +text: .text%__1cTconstantPoolOopDescbDresolve_string_constants_impl6FnSconstantPoolHandle_pnGThread__v_; +text: .text%__1cNCompileBrokerOcompile_method6FnMmethodHandle_i1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNCompileBrokerTcompile_method_base6FnMmethodHandle_ii1ipkcpnGThread__pnHnmethod__; +text: .text%__1cITypeLongFempty6kM_i_; +text: .text%__1cJloadINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFArenaRdestruct_contents6M_v_; +text: .text%__1cJloadSNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_CmpI6MpknENode__v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodKlass.o; +text: .text%__1cIAddPNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJAssemblerDjcc6Mn0AJCondition_rnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cPcmpFastLockNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNmulL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJStoreNodeEmake6FpnENode_22pknHTypePtr_2nJBasicType__p0_; +text: .text%__1cIGraphKitPstore_to_memory6MpnENode_22nJBasicType_i_2_; +text: .text%__1cHi2sNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMapJdo_method6Miiii_v_; +text: .text%__1cLSymbolTableFprobe6Fpkci_pnNsymbolOopDesc__; +text: .text%__1cLcastP2INodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompP_mem_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNincI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNdecI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIciObjectFklass6M_pnHciKlass__; +text: .text%__1cQPSGenerationPoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cQPSGenerationPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cQPSGenerationPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cFParseYprofile_not_taken_branch6M_v_; +text: .text%__1cLRShiftLNodeGOpcode6kM_i_; +text: .text%__1cKMemBarNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cMURShiftLNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: parse2.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: cfgnode.o; +text: .text%__1cIMulINodeGOpcode6kM_i_; +text: .text%__1cPBytecode_invokeJsignature6kM_pnNsymbolOopDesc__; +text: .text%__1cIConDNodeGOpcode6kM_i_; +text: .text%__1cKInlineTreePshouldNotInline6kMpnIciMethod_pnMWarmCallInfo__pkc_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: bytecode.o; +text: .text%__1cFframebGinterpreter_callee_receiver_addr6MnMsymbolHandle__ppnHoopDesc__; +text: .text%__1cNandL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6FnUtypeArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cKciTypeFlowIcan_trap6MrnQciByteCodeStream__i_; +text: .text%__1cNxorI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cZload_long_indOffset32OperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cFParseNthrow_to_exit6MpnNSafePointNode__v_; +text: .text%__1cJloadLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKjmpConNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKjmpConNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKjmpConNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cMLinkResolverbElinktime_resolve_static_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cLencode_Copy6FrnKCodeBuffer_ii_v_; +text: .text%__1cQjmpDir_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQjmpDir_shortNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOJNIHandleBlockOallocate_block6FpnGThread__p0_; +text: .text%__1cENodeIadd_prec6Mp0_v_; +text: .text%__1cLjmpConUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMachNodeSalignment_required6kM_i_: machnode.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: machnode.o; +text: .text%__1cKType_ArrayEgrow6MI_v_; +text: .text%__1cMorI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMorI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cUmembar_cpu_orderNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSconstMethodOopDescbEchecked_exceptions_length_addr6kM_pH_; +text: .text%__1cFParseFdo_if6MpnENode_2nIBoolTestEmask_2_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: ciMethodData.o; +text: .text%__1cLBoxLockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJxRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLciSignature2t6MpnHciKlass_pnIciSymbol__v_; +text: .text%__1cIciMethod2t6MnMmethodHandle__v_; +text: .text%__1cIConINodeHget_int6kMpi_i_: classes.o; +text: .text%__1cIciMethodbHhas_unloaded_classes_in_signature6M_i_; +text: .text%__1cNinstanceKlassVis_same_class_package6FpnHoopDesc_pnNsymbolOopDesc_24_i_; +text: .text%__1cNloadRangeNodeIpipeline6kM_pknIPipeline__; +text: .text%jni_ExceptionOccurred: jni.o; +text: .text%__1cQConstantIntValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cHTypeAryEmake6FpknEType_pknHTypeInt__pk0_; +text: .text%__1cJloadCNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNsubI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cEUTF8Fequal6FpWi1i_i_; +text: .text%__1cSHighResTimeSamplerLtake_sample6M_x_: statSampler.o; +text: .text%__1cLStatSamplerLsample_data6FpnMPerfDataList__v_; +text: .text%__1cRis_error_reported6F_i_; +text: .text%__1cMPeriodicTaskOreal_time_tick6FI_v_; +text: .text%__1cLStatSamplerOcollect_sample6F_v_; +text: .text%__1cPStatSamplerTaskEtask6M_v_: statSampler.o; +text: .text%__1cNWatcherThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cMPeriodicTaskMtime_to_wait6F_I_: thread.o; +text: .text%jni_GetByteArrayRegion: jni.o; +text: .text%__1cSCallLeafDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2I_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQPlaceholderTableKfind_entry6MiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cOoop_RelocationHoops_do6MpFppnHoopDesc__v_v_; +text: .text%__1cKBufferBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cKBufferBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cSdivD_reg_roundNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQleaPIdxScaleNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cKstorePNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateP_sub_Op_LShiftI6MpknENode__v_; +text: .text%jni_GetArrayLength: jni.o; +text: .text%__1cICodeHeapIcapacity6kM_I_; +text: .text%__1cMCodeHeapPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cKMemoryPoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cMCodeHeapPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cRresolve_and_patch6FppnHoopDesc__v_; +text: .text%__1cMorI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIPipelinePoperand_latency6kMIpk0_I_; +text: .text%__1cIMachOperEtype6kM_pknEType__; +text: .text%__1cVCompressedWriteStreamEgrow6M_v_; +text: .text%__1cPClassFileParserbDverify_legal_method_signature6MnMsymbolHandle_1pnGThread__i_; +text: .text%__1cIregFOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cNloadRangeNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJAssemblerFpushl6MpnMRegisterImpl__v_; +text: .text%JVM_Write; +text: .text%__1cDhpiFwrite6FipkvI_I_: jvm.o; +text: .text%__1cPstoreImmI16NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPCheckCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJStartNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cKciTypeFlowLStateVectorLdo_getfield6MpnQciByteCodeStream__v_; +text: .text%__1cFParseMvisit_blocks6M_v_; +text: .text%__1cNsubI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMLinkResolverTresolve_static_call6FrnICallInfo_rnLKlassHandle_nMsymbolHandle_53iipnGThread__v_; +text: .text%__1cRsalI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvD2I_reg_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowGJsrSetSis_compatible_with6Mp1_i_; +text: .text%__1cOcompU_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadBNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLOpaque1NodeEhash6kM_I_; +text: .text%__1cXcmpL_reg_flags_LTGENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_: classes.o; +text: .text%__1cJcmpOpOperGnegate6M_v_: ad_i486_clone.o; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_2_v_; +text: .text%__1cGBitMapIset_from6M0_v_; +text: .text%__1cGThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%__1cKMemBarNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKjmpConNodeGnegate6M_v_: ad_i486_misc.o; +text: .text%__1cFMutexMjvm_raw_lock6M_v_; +text: .text%__1cFMutexOjvm_raw_unlock6M_v_; +text: .text%JVM_RawMonitorEnter; +text: .text%JVM_RawMonitorExit; +text: .text%__1cXmembar_release_lockNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cITypeLongEmake6Fxx_pk0_; +text: .text%__1cNaddL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKcmpOpUOperFccode6kM_i_: ad_i486_clone.o; +text: .text%__1cPClassFileParserUskip_over_field_name6MpciI_1_; +text: .text%__1cLjmpConUNodeOis_pc_relative6kM_i_: ad_i486_misc.o; +text: .text%__1cLjmpConUNodeTmay_be_short_branch6kM_i_: ad_i486_misc.o; +text: .text%__1cNstoreImmPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cURethrowExceptionNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFParseOreturn_current6MpnENode__v_; +text: .text%__1cETypeCeq6kMpk0_i_; +text: .text%__1cXinitialize_static_field6FpnPfieldDescriptor_pnGThread__v_: classFileParser.o; +text: .text%__1cHSubNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKReflectionVis_same_class_package6FpnMklassOopDesc_2_i_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnMklassOopDesc__i_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: bytecode.o; +text: .text%__1cOJNIHandleBlockNrelease_block6Fp0pnGThread__v_; +text: .text%__1cOGenerateOopMapTmerge_state_into_bb6MpnKBasicBlock__v_; +text: .text%__1cRaddI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLeCXRegIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cMMonitorValue2t6MpnKScopeValue_nILocation__v_; +text: .text%__1cMMonitorValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cLBoxLockNodeKis_BoxLock6kM_pk0_: classes.o; +text: .text%__1cLBoxLockNodeKstack_slot6FpnENode__nHOptoRegEName__; +text: .text%__1cKCastPPNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cFParseRoptimize_inlining6MpnIciMethod_ipnPciInstanceKlass_24irnKInlineTreeLInlineStyle_r2_v_; +text: .text%__1cQimprove_receiver6FpnPciInstanceKlass_pknLTypeInstPtr_ri_1_; +text: .text%__1cJloadPNodeFreloc6kM_i_; +text: .text%__1cQStackFrameStream2t6MpnKJavaThread_i_v_; +text: .text%__1cQSystemDictionaryStry_get_next_class6F_pnMklassOopDesc__; +text: .text%__1cNinstanceKlassKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cFciEnvVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cKDictionaryStry_get_next_class6M_pnMklassOopDesc__; +text: .text%__1cLRShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFParseSmerge_memory_edges6MpnMMergeMemNode_ii_v_; +text: .text%__1cJloadBNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cFBlockTimplicit_null_check6MrnLBlock_Array_rnNGrowableArray4CI__pnENode_6_v_; +text: .text%__1cJTypeTupleFxdual6kM_pknEType__; +text: .text%__1cJTimeStampJupdate_to6Mx_v_; +text: .text%__1cJTimeStampGupdate6M_v_; +text: .text%__1cMloadConLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmulL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSmembar_releaseNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRaddI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cISubINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: loopnode.o; +text: .text%__1cRsarI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_AddI6MpknENode__v_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__: loopnode.o; +text: .text%__1cOGenerateOopMapEppop6MpnNCellTypeState__v_; +text: .text%__1cLCastP2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIciMethodLis_accessor6kM_i_; +text: .text%__1cRScavengeRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cRScavengeRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cOGenerateOopMapUreachable_basicblock6Fp0ipi_v_; +text: .text%__1cPsarI_eReg_1NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cXindIndexScaleOffsetOperOindex_position6kM_i_: ad_i486.o; +text: .text%__1cXindIndexScaleOffsetOperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cNSafePointNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRmethodDataOopDescLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cZload_long_indOffset32OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMloadConPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cScompU_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHUNICODELutf8_length6FpHi_i_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: memnode.o; +text: .text%__1cRInterpretedRFrameKtop_vframe6kM_pnKjavaVFrame__: rframe.o; +text: .text%__1cRinterpretedVFrameDbcp6kM_pC_; +text: .text%__1cRinterpretedVFrameDbci6kM_i_; +text: .text%__1cMCallLeafNodeLis_CallLeaf6kM_pk0_: classes.o; +text: .text%__1cJAssemblerEpopl6MpnMRegisterImpl__v_; +text: .text%__1cKStoreCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLRethrowNodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: loopnode.o; +text: .text%__1cPsarI_eReg_1NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cZload_long_indOffset32OperFscale6kM_i_: ad_i486.o; +text: .text%__1cIciMethodVhas_balanced_monitors6M_i_; +text: .text%__1cFArenaEused6kM_I_; +text: .text%__1cFParseNdo_all_blocks6M_v_; +text: .text%__1cOParseGeneratorJcan_parse6FpnIciMethod_i_i_; +text: .text%__1cFParseLbuild_exits6M_v_; +text: .text%__1cFParseIdo_exits6M_v_; +text: .text%__1cFParse2t6MpnIJVMState_pnIciMethod_f_v_; +text: .text%__1cFParseQcreate_entry_map6M_pnNSafePointNode__; +text: .text%__1cOParseGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cFParseLinit_blocks6M_v_; +text: .text%__1cIciMethodPcan_be_compiled6M_i_; +text: .text%__1cFframeNoops_entry_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cPJavaCallWrapperHoops_do6MpnKOopClosure__v_; +text: .text%__1cRRawBytecodeStream2t6MnMmethodHandle__v_; +text: .text%__1cNCallGeneratorKfor_inline6FpnIciMethod_f_p0_; +text: .text%__1cFParsePdo_method_entry6M_v_; +text: .text%__1cKstoreCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQComputeCallStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cTStackWalkCompPolicyPshouldNotInline6FnMmethodHandle__pkc_; +text: .text%__1cRaddI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTcompareAndSwapLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPSignatureStreamRas_symbol_or_null6M_pnNsymbolOopDesc__; +text: .text%__1cYDebugInformationRecorderNadd_dependent6MpnPciInstanceKlass_pnIciMethod__v_; +text: .text%__1cHi2sNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIget_long6kM_x_; +text: .text%__1cQciByteCodeStreamSget_constant_index6kM_i_; +text: .text%__1cICHeapObj2k6Fpv_v_; +text: .text%__1cFArena2T6M_v_; +text: .text%jni_GetSuperclass: jni.o; +text: .text%__1cLklassVtableXvtable_accessibility_at6Mi_n0AKAccessType__; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmBNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: cfgnode.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: cfgnode.o; +text: .text%__1cNdecI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cScompU_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNstoreImmINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cPJavaCallWrapper2t6MnMmethodHandle_nGHandle_pnJJavaValue_pnGThread__v_; +text: .text%__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_; +text: .text%__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cRJavaCallArgumentsKparameters6M_pi_; +text: .text%__1cCosbCstack_shadow_pages_available6FpnGThread_nMmethodHandle__i_; +text: .text%__1cRruntime_type_from6FpnJJavaValue__nJBasicType__: javaCalls.o; +text: .text%__1cTAbstractInterpreterbFsize_top_interpreter_activation6FpnNmethodOopDesc__i_; +text: .text%__1cJMultiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPJavaCallWrapper2T6M_v_; +text: .text%__1cUGenericGrowableArrayEgrow6Mi_v_; +text: .text%__1cSCallLeafDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowFBlockPclone_loop_head6Mp0ip1pn0AGJsrSet__3_; +text: .text%__1cITypeFuncFxdual6kM_pknEType__; +text: .text%__1cNxorI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHMatcherPstack_alignment6F_I_; +text: .text%__1cKInlineTree2t6MpnHCompile_pk0pnIciMethod_pnIJVMState_if_v_; +text: .text%__1cMLinkResolverXresolve_klass_no_update6FrnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cHciField2t6MpnPciInstanceKlass_i_v_; +text: .text%__1cTconstantPoolOopDescbCklass_ref_at_if_loaded_check6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cMURShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLeSIRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cQSystemDictionaryVadd_loader_constraint6FnMsymbolHandle_nGHandle_2pnGThread__v_; +text: .text%__1cVLoaderConstraintTableJadd_entry6MnMsymbolHandle_pnMklassOopDesc_nGHandle_34pnGThread__i_; +text: .text%__1cNloadKlassNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSloadL_volatileNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapCpp6MpnNCellTypeState_2_v_; +text: .text%__1cDCHANprocess_class6FnLKlassHandle_pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___nMsymbolHandle_6_v_; +text: .text%__1cSObjectSynchronizerXidentity_hash_value_for6FnGHandle__i_; +text: .text%__1cQjava_lang_StringGlength6FpnHoopDesc__i_; +text: .text%__1cHoopDescSslow_identity_hash6M_i_; +text: .text%__1cQindOffset32XOperLdisp_is_oop6kM_i_: ad_i486.o; +text: .text%__1cQindOffset32XOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cQindOffset32XOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cQindOffset32XOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cIMulINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cGciType2t6MnLKlassHandle__v_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodDataKlass.o; +text: .text%__1cHciKlass2t6MnLKlassHandle__v_; +text: .text%__1cLRShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKInlineTreeYcompute_callee_frequency6kMi_f_; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cKInlineTreebCbuild_inline_tree_for_callee6MpnIciMethod_pnIJVMState_i_p0_; +text: .text%__1cIciMethodbBinterpreter_call_site_count6Mi_i_; +text: .text%__1cHciKlassOis_subclass_of6Mp0_i_; +text: .text%__1cMtlsLoadPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHnmethodHoops_do6MpnKOopClosure__v_; +text: .text%__1cJGC_lockerNlock_critical6FpnKJavaThread__v_: jni.o; +text: .text%__1cJAssemblerElock6M_v_; +text: .text%__1cRindIndexScaleOperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cKRegionNodeEhash6kM_I_: loopnode.o; +text: .text%__1cFKlassTarray_klass_or_null6Mi_pnMklassOopDesc__; +text: .text%__1cXmembar_release_lockNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHnmethodbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cLklassVtableKis_miranda6FpnNmethodOopDesc_pnPobjArrayOopDesc_pnMklassOopDesc__i_; +text: .text%__1cENodeHdel_out6Mp0_v_: library_call.o; +text: .text%__1cRandI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNIdealLoopTreeIset_nest6MI_i_; +text: .text%__1cNIdealLoopTreeMcounted_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cPshlI_eReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlass.o; +text: .text%__1cRalign_code_offset6Fi_I_; +text: .text%__1cRciVirtualCallDataOtranslate_from6MpnLProfileData__v_; +text: .text%__1cMLinkResolverVresolve_invokespecial6FrnICallInfo_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cJAssemblerHcmpxchg6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cHnmethodPis_locked_by_vm6kM_i_: nmethod.o; +text: .text%__1cONMethodSweeperPprocess_nmethod6FpnHnmethod__v_; +text: .text%__1cKTypeAryPtrFxdual6kM_pknEType__; +text: .text%__1cRmethodDataOopDescJbci_to_dp6Mi_pC_; +text: .text%__1cOjmpLoopEndNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: ad_i486.o; +text: .text%__1cMVM_OperationPevaluation_mode6kM_n0AEMode__: vm_operations.o; +text: .text%__1cRsalI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cbAPSEvacuateFollowersClosureHdo_void6M_v_: psScavenge.o; +text: .text%__1cHnmethodVcleanup_inline_caches6M_v_; +text: .text%__1cIDivINodeGOpcode6kM_i_; +text: .text%__1cMURShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHnmethodLis_unloaded6kM_i_: nmethod.o; +text: .text%__1cIimmDOperJconstantD6kM_d_: ad_i486_clone.o; +text: .text%__1cQSystemDictionarybBresolve_array_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cSInterpreterRuntimeMmonitorenter6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2I_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cScompU_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cScompU_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPciInstanceKlass2t6MnLKlassHandle__v_; +text: .text%jni_GetPrimitiveArrayCritical: jni.o; +text: .text%jni_ReleasePrimitiveArrayCritical: jni.o; +text: .text%__1cXcmpL_reg_flags_LEGTNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMrep_stosNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJStartNodeOis_block_start6kM_i_: callnode.o; +text: .text%__1cSsafePoint_pollNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cWConstantPoolCacheEntryPbytecode_number6FnJBytecodesECode__i_: cpCacheOop.o; +text: .text%__1cWConstantPoolCacheEntryLis_resolved6kMnJBytecodesECode__i_: cpCacheOop.o; +text: .text%__1cOMethodLivenessKBasicBlockIload_two6Mi_v_; +text: .text%__1cIGraphKitMarray_length6MpnENode__2_; +text: .text%__1cJloadCNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXmembar_release_lockNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cMCreateExNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNincI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFArena2t6M_v_; +text: .text%__1cMindirectOperFscale6kM_i_: ad_i486.o; +text: .text%__1cHMulNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLsymbolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cLPCTableNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMCreateExNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSInterpreterRuntimeLmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cLRShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJcmpOpOperFclone6kM_pnIMachOper__; +text: .text%__1cIJVMState2t6Mi_v_; +text: .text%__1cKPerfStringKset_string6Mpkc_v_; +text: .text%__1cJStartNodeOis_block_start6kM_i_: classes.o; +text: .text%__1cPciObjArrayKlassSis_obj_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cNeFlagsRegOperFclone6kM_pnIMachOper__; +text: .text%__1cMMachCallNodeHis_Call6M_pnICallNode__: ad_i486_misc.o; +text: .text%__1cKTypeRawPtrCeq6kMpknEType__i_; +text: .text%__1cFStateQ_sub_Op_CreateEx6MpknENode__v_; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjectFactory.o; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciObjectFactory.o; +text: .text%__1cLjmpConUNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cLjmpConUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLjmpConUNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cLOpaque1NodeLbottom_type6kM_pknEType__: connode.o; +text: .text%__1cEDict2t6MpFpkv2_ipF2_i_v_; +text: .text%__1cEDict2T6M_v_; +text: .text%__1cNCatchProjNodeDcmp6kMrknENode__I_; +text: .text%__1cIGraphKitRmake_slow_call_ex6MpnENode_pnPciInstanceKlass__v_; +text: .text%__1cSCountedLoopEndNodeKstride_con6kM_i_; +text: .text%__1cRmethodDataOopDescKmileage_of6FpnNmethodOopDesc__i_; +text: .text%__1cQconstMethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cHCompileXin_preserve_stack_slots6M_I_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: symbolKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: symbolKlass.o; +text: .text%__1cPconvL2I_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLmethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cKTypeRawPtrFxmeet6kMpknEType__3_; +text: .text%__1cTJvmtiEventCollectorYunset_jvmti_thread_state6M_v_; +text: .text%__1cZload_long_indOffset32OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cZload_long_indOffset32OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cZload_long_indOffset32OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2T6M_v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2t6M_v_; +text: .text%__1cIMinINodeGOpcode6kM_i_; +text: .text%__1cHMemNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cHMemNodeScalculate_adr_type6FpknEType_pknHTypePtr__6_; +text: .text%__1cHMatcherXadjust_incoming_stk_arg6MnHOptoRegEName__2_; +text: .text%__1cFStateM_sub_Op_CmpU6MpknENode__v_; +text: .text%__1cSPSKeepAliveClosureGdo_oop6MppnHoopDesc__v_: psScavenge.o; +text: .text%__1cFTypeDEmake6Fd_pk0_; +text: .text%jni_IsSameObject: jni.o; +text: .text%__1cRCompilationPolicybIreset_counter_for_invocation_event6MnMmethodHandle__v_; +text: .text%__1cKMemoryPoolHoops_do6MpnKOopClosure__v_; +text: .text%__1cQindOffset32XOperMdisp_as_type6kM_pknHTypePtr__: ad_i486.o; +text: .text%__1cQindOffset32XOperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cQindOffset32XOperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cNstoreImmBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTcompareAndSwapLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLlog2_intptr6Fi_i_: mulnode.o; +text: .text%__1cHOrINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConDNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cScompP_mem_eRegNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cHMulNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRaddI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LEGTNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKJavaThreadQlast_java_vframe6MpnLRegisterMap__pnKjavaVFrame__; +text: .text%__1cKjavaVFrameNis_java_frame6kM_i_: vframe.o; +text: .text%__1cTStackWalkCompPolicyXmethod_invocation_event6MnMmethodHandle_pnGThread__v_; +text: .text%__1cTStackWalkCompPolicyVfindTopInlinableFrame6MpnNGrowableArray4CpnGRFrame____2_; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_nMmethodHandle__v_; +text: .text%__1cRMachSafePointNodeLis_MachCall6M_pnMMachCallNode__: ad_i486_misc.o; +text: .text%__1cKStoreBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNandL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLCounterDataOis_CounterData6M_i_: methodDataOop.o; +text: .text%__1cPClassFileParserbCverify_legal_class_modifiers6MipnGThread__v_; +text: .text%__1cENodeHget_int6kMpi_i_; +text: .text%__1cOcompI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNinstanceKlassbDcheck_valid_for_instantiation6MipnGThread__v_; +text: .text%__1cYSurvivorMutableSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cTleaPIdxScaleOffNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJcmpOpOperFequal6kM_i_: ad_i486_clone.o; +text: .text%__1cMorI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFKlassMset_subklass6MpnMklassOopDesc__v_; +text: .text%__1cKRegionNodeOhas_unique_phi6kM_pnHPhiNode__; +text: .text%__1cNnegI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRsarI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPsarI_eReg_1NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLRethrowNodeKmatch_edge6kMI_I_; +text: .text%__1cNinstanceKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cPshrI_eReg_1NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLAccessFlagsPatomic_set_bits6Mi_v_; +text: .text%__1cQVMOperationQdDueueLqueue_empty6Mi_i_; +text: .text%__1cTCreateExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFTypeFEhash6kM_i_; +text: .text%__1cVExceptionHandlerTableMadd_subtable6MipnNGrowableArray4Ci__2_v_; +text: .text%__1cQLibraryIntrinsicKis_virtual6kM_i_: library_call.o; +text: .text%__1cScompP_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNxorI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cScompI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cScompI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cUCompressedReadStreamMraw_read_int6FrpC_i_: vframe.o; +text: .text%__1cRMachSafePointNodePis_MachCallJava6M_pnQMachCallJavaNode__: ad_i486_misc.o; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_: classes.o; +text: .text%__1cIimmIOperFclone6kM_pnIMachOper__; +text: .text%__1cJleaP8NodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cIciObject2t6M_v_; +text: .text%__1cMloadConINodeFclone6kM_pnENode__; +text: .text%__1cIMulLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIGraphKitYcombine_exception_states6MpnNSafePointNode_2_v_; +text: .text%__1cNinstanceKlassPlink_class_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cMciMethodData2t6M_v_; +text: .text%__1cPciObjectFactoryUget_empty_methodData6M_pnMciMethodData__; +text: .text%__1cRitableMethodEntryKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cRaddL_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQinstanceRefKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQinstanceRefKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cJloadBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIHaltNodeEhash6kM_I_: classes.o; +text: .text%__1cNmulL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQinit_input_masks6FIrnHRegMask_1_p0_: matcher.o; +text: .text%__1cXvirtual_call_RelocationLunpack_data6M_v_; +text: .text%__1cSconstMethodOopDescZchecked_exceptions_length6kM_i_; +text: .text%__1cNIdealLoopTreeObeautify_loops6MpnOPhaseIdealLoop__i_; +text: .text%__1cOjmpLoopEndNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cLklassVtableMget_mirandas6FpnNGrowableArray4CpnNmethodOopDesc___pnMklassOopDesc_pnPobjArrayOopDesc_8_v_; +text: .text%__1cMrep_stosNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapLbb_mark_fct6Fp0ipi_v_; +text: .text%__1cFStateO_sub_Op_StoreP6MpknENode__v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: phaseX.o; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: callnode.o; +text: .text%__1cPshlI_eReg_1NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFKlassKsuperklass6kM_pnNinstanceKlass__; +text: .text%__1cSindIndexOffsetOperFscale6kM_i_: ad_i486.o; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: cfgnode.o; +text: .text%__1cNaddL_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cXJNI_ArgumentPusherVaArgKget_object6M_v_: jni.o; +text: .text%__1cMMutableSpaceFclear6M_v_; +text: .text%__1cQjmpCon_shortNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cQjmpCon_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cQjmpCon_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKjmpConNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cPshrI_eReg_1NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMindirectOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cMindirectOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cMindirectOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cYmulI_imm_RShift_highNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPstoreImmI16NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cParrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cParrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cFKlassQset_next_sibling6MpnMklassOopDesc__v_; +text: .text%__1cUParallelScavengeHeapMmem_allocate6MIii_pnIHeapWord__; +text: .text%__1cNstoreImmBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cOGenerateOopMapIdo_field6Miiii_v_; +text: .text%__1cOGenerateOopMapRsigchar_to_effect6McipnNCellTypeState__2_; +text: .text%__1cIGraphKitXset_edges_for_java_call6MpnMCallJavaNode_i_v_; +text: .text%__1cQciTypeArrayKlassTis_type_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cIVerifierRshould_verify_for6FpnHoopDesc__i_; +text: .text%__1cKcmpOpUOperGnegate6M_v_: ad_i486_clone.o; +text: .text%__1cLjmpConUNodeGnegate6M_v_: ad_i486_misc.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: loopnode.o; +text: .text%__1cRandI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKstoreBNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapTmerge_state_vectors6MpnNCellTypeState_2_i_; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_2_v_; +text: .text%__1cNnegI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitZset_results_for_java_call6MpnMCallJavaNode__pnENode__; +text: .text%__1cOstackSlotDOperEtype6kM_pknEType__: ad_i486.o; +text: .text%JVM_GetMethodIxModifiers; +text: .text%__1cNSCMemProjNodeGOpcode6kM_i_; +text: .text%__1cNandL_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cJleaP8NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQjava_lang_StringGequals6FpnHoopDesc_pHi_i_; +text: .text%__1cENodeGis_Sub6M_pnHSubNode__: classes.o; +text: .text%__1cTcompareAndSwapLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cScompU_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cScompU_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cScompU_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cYmulI_imm_RShift_highNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPcmpFastLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNinstanceKlassbCfind_local_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cFStateO_sub_Op_StoreI6MpknENode__v_; +text: .text%__1cJLoadSNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%JVM_IsInterface; +text: .text%__1cNinstanceKlassWfind_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cPciInstanceKlassTget_field_by_offset6Mii_pnHciField__; +text: .text%__1cFStateM_sub_Op_RegL6MpknENode__v_; +text: .text%__1cRMachNullCheckNodeLout_RegMask6kM_rknHRegMask__: machnode.o; +text: .text%__1cRshrI_eReg_immNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cScompP_mem_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadSNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cXconvI2L_reg_reg_zexNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMvframeStream2t6MpnKJavaThread_i_v_; +text: .text%__1cRsalI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJimmI0OperFclone6kM_pnIMachOper__; +text: .text%__1cNloadConI0NodeFclone6kM_pnENode__; +text: .text%__1cWflagsReg_long_LTGEOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cQciByteCodeStreamMget_constant6M_nKciConstant__; +text: .text%__1cFciEnvbAget_constant_by_index_impl6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cFciEnvVget_constant_by_index6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cRMachNullCheckNode2t6MpnENode_2I_v_; +text: .text%__1cNMachIdealNodeJnum_opnds6kM_I_: machnode.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: machnode.o; +text: .text%__1cXconvI2L_reg_reg_zexNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLLShiftLNodeGOpcode6kM_i_; +text: .text%__1cTciConstantPoolCacheGinsert6Mipv_v_; +text: .text%__1cSobjArrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSobjArrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cEhash6Fpkc1_I_; +text: .text%__1cQput_after_lookup6FnMsymbolHandle_0ppnLNameSigHash__i_; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cZresource_reallocate_bytes6FpcII_0_; +text: .text%__1cLeCXRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cScompI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLCastP2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cUreloc_java_to_interp6F_I_; +text: .text%__1cTsize_java_to_interp6F_I_; +text: .text%__1cKstoreINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompU_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOstackSlotLOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: graphKit.o; +text: .text%__1cSCallLeafDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCallLeafDirectNodeRis_safepoint_node6kM_i_: ad_i486_misc.o; +text: .text%__1cQciByteCodeStreamPget_klass_index6M_i_; +text: .text%__1cQComputeCallStackHdo_void6M_v_: generateOopMap.o; +text: .text%__1cMnadxRegIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cJCHAResultOis_monomorphic6kM_i_; +text: .text%__1cJCHAResult2t6MnLKlassHandle_nMsymbolHandle_2pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___n0E_i_v_; +text: .text%__1cIciMethodXfind_monomorphic_target6MpnHciKlass_22_p0_; +text: .text%__1cDCHAManalyze_call6FnLKlassHandle_11nMsymbolHandle_2_pnJCHAResult__; +text: .text%__1cRaddL_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKMemBarNode2t6M_v_; +text: .text%__1cHciField2t6MpnPfieldDescriptor__v_; +text: .text%__1cKMemBarNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMloadConFNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKPSYoungGenNused_in_bytes6kM_I_; +text: .text%__1cNSafepointBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_; +text: .text%__1cbCCompiledCodeSafepointHandlerYcaller_must_gc_arguments6kM_i_: safepoint.o; +text: .text%__1cUThreadSafepointStateYcaller_must_gc_arguments6kM_i_; +text: .text%__1cWstatic_stub_RelocationJpack_data6M_i_; +text: .text%__1cMloadConFNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSReferenceProcessorOprocess_phase36MppnHoopDesc_ipnRBoolObjectClosure_pnKOopClosure_pnLVoidClosure__v_; +text: .text%__1cSReferenceProcessorOprocess_phase26MppnHoopDesc_pnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSReferenceProcessorbAprocess_discovered_reflist6MppnHoopDesc_pnPReferencePolicy_i_v_; +text: .text%__1cSReferenceProcessorbAenqueue_discovered_reflist6MpnHoopDesc_p2_v_; +text: .text%__1cNandL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLLShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOFastUnlockNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cOGenerateOopMapLmerge_state6Fp0ipi_v_; +text: .text%__1cMnegF_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIModINodeGOpcode6kM_i_; +text: .text%__1cHUNICODEHas_utf86FpHi_pc_; +text: .text%__1cKcmpOpUOperNgreater_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cMLinkResolverbCresolve_virtual_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cIMaxINodeGOpcode6kM_i_; +text: .text%__1cQjmpDir_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKjmpDirNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cQjmpDir_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cQjmpDir_shortNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConFNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKMemBarNodeJis_MemBar6kM_pk0_: classes.o; +text: .text%__1cIMachNodeKconst_size6kM_i_: machnode.o; +text: .text%__1cIMachNodeFreloc6kM_i_: machnode.o; +text: .text%__1cIMachNodeTmay_be_short_branch6kM_i_: machnode.o; +text: .text%__1cWImplicitExceptionTableGappend6MII_v_; +text: .text%__1cIGraphKitOinsert_mem_bar6MpnKMemBarNode__v_; +text: .text%__1cIGraphKitbBset_arguments_for_java_call6MpnMCallJavaNode__v_; +text: .text%__1cIGraphKitJpush_node6MnJBasicType_pnENode__v_: callGenerator.o; +text: .text%__1cNCallGeneratorCtf6kM_pknITypeFunc__; +text: .text%__1cNCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cLOptoRuntimebCcomplete_monitor_unlocking_C6FpnHoopDesc_pnJBasicLock__v_; +text: .text%__1cITypeNodeHis_Type6M_p0_: classes.o; +text: .text%__1cNdecI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHdel_out6Mp0_v_: chaitin.o; +text: .text%__1cYmulI_imm_RShift_highNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cICodeBlobRis_at_poll_return6MpC_i_; +text: .text%__1cHGCCauseJto_string6Fn0AFCause__pkc_; +text: .text%__1cSconstMethodOopDescYchecked_exceptions_start6kM_pnXCheckedExceptionElement__; +text: .text%__1cLBoxLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTleaPIdxScaleOffNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNGCTaskManagerRset_resource_flag6MIi_v_; +text: .text%__1cScompI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cScompI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cScompI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFParseQarray_addressing6MnJBasicType_ippknEType__pnENode__; +text: .text%__1cHCompileSregister_intrinsic6MpnNCallGenerator__v_; +text: .text%__1cNmulL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMeADXRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cRcmpFastUnlockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQleaPIdxScaleNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cXcopy_u2_with_conversion6FpH0i_v_: classFileParser.o; +text: .text%__1cPClassFileParserYparse_checked_exceptions6MpHInSconstantPoolHandle_pnGThread__1_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_locking_C6FpnHoopDesc_pnJBasicLock_pnKJavaThread__v_; +text: .text%__1cSvframeStreamCommonbHskip_method_invoke_and_aux_frames6M_v_; +text: .text%__1cIGraphKitbMset_predefined_output_for_runtime_call6MpnENode_pnMMergeMemNode__v_; +text: .text%__1cNminI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMLinkResolverbCresolve_special_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cPciInstanceKlassLfind_method6MpnIciSymbol_2_pnIciMethod__; +text: .text%__1cJloadLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitJpush_node6MnJBasicType_pnENode__v_: parse1.o; +text: .text%__1cJCHAResultSmonomorphic_target6kM_nMmethodHandle__; +text: .text%__1cNCallGeneratorPfor_direct_call6FpnIciMethod__p0_; +text: .text%__1cTDirectCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cMWarmCallInfoLalways_cold6F_p0_; +text: .text%__1cNaddL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMrep_stosNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFciEnvRfind_system_klass6MpnIciSymbol__pnHciKlass__; +text: .text%__1cPClassFileStreamHskip_u26MipnGThread__v_; +text: .text%__1cZCallInterpreterDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOClearArrayNodeKmatch_edge6kMI_I_; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_: methodDataOop.o; +text: .text%__1cLConvI2LNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPThreadLocalNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLBlock_ArrayEgrow6MI_v_; +text: .text%__1cLConvL2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQciByteCodeStreamJget_klass6Mri_pnHciKlass__; +text: .text%__1cNandL_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNandL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHi2sNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMjniIdSupportNto_method_oop6FpnK_jmethodID__pnNmethodOopDesc__; +text: .text%__1cRSignatureIterator2t6MpnGThread_pnNsymbolOopDesc__v_; +text: .text%__1cIAndINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cOGenerateOopMapQnext_bb_start_pc6MpnKBasicBlock__i_; +text: .text%__1cMLinkResolverbBresolve_static_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: callnode.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: callnode.o; +text: .text%__1cOGenerateOopMapNrestore_state6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapJinterp_bb6MpnKBasicBlock__v_; +text: .text%__1cPCheckCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cISubINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIAndINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cTCallDynamicJavaNodeGOpcode6kM_i_; +text: .text%__1cXcmpL_reg_flags_LEGTNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPCheckCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKRegionNodeOis_block_start6kM_i_: loopnode.o; +text: .text%__1cSloadL_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUvisit_all_interfaces6FpnPobjArrayOopDesc_pnXInterfaceVisiterClosure__v_; +text: .text%__1cJPhaseLiveHcompute6MI_v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: live.o; +text: .text%__1cIPhaseIFGEinit6MI_v_; +text: .text%__1cMPhaseChaitinQgather_lrg_masks6Mi_v_; +text: .text%__1cRjmpConU_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRjmpConU_shortNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cVmerge_point_too_heavy6FpnHCompile_pnENode__i_: loopopts.o; +text: .text%__1cMloadConPNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHRetNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: loopnode.o; +text: .text%__1cNandI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIAddLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFStateN_sub_Op_LoadI6MpknENode__v_; +text: .text%__1cPsarI_eReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXmembar_acquire_lockNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHCmpNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKstoreBNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXconvI2L_reg_reg_zexNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: connode.o; +text: .text%__1cNinstanceKlassXmark_dependent_nmethods6MpnMklassOopDesc__i_; +text: .text%__1cOPhaseIdealLoopPis_counted_loop6MpnENode_pnNIdealLoopTree__2_; +text: .text%__1cKoopFactoryTnew_system_objArray6FipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: oopFactory.o; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: oopFactory.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: oopFactory.o; +text: .text%__1cOGenerateOopMapHset_var6MinNCellTypeState__v_; +text: .text%__1cOleaPIdxOffNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescOis_initializer6kM_i_; +text: .text%__1cPciObjArrayKlassNelement_klass6M_pnHciKlass__; +text: .text%__1cSFixupMirrorClosureJdo_object6MpnHoopDesc__v_: universe.o; +text: .text%__1cNloadKlassNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIGraphKitPpush_pair_local6Mi_v_: parse2.o; +text: .text%__1cNMemoryServiceXtrack_memory_pool_usage6FpnKMemoryPool__v_; +text: .text%__1cPshlI_eReg_1NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPClassFileParserbHparse_constant_pool_integer_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKstoreBNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerFpushl6Mi_v_; +text: .text%__1cKmethodOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: reg_split.o; +text: .text%__1cMLinkResolverUresolve_invokestatic6FrnICallInfo_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKklassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNxorI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNxorI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHMatcherMreturn_value6Fii_nLRegPair__; +text: .text%__1cNinstanceKlassScopy_static_fields6MpnSPSPromotionManager__v_; +text: .text%__1cSinstanceKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cbACallCompiledJavaDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRandI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSsafePoint_pollNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSloadL_volatileNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKoopFactoryYnew_permanent_shortArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cLcastP2INodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: frame.o; +text: .text%__1cFStateR_sub_Op_LoadRange6MpknENode__v_; +text: .text%__1cOcompU_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICmpLNodeDsub6kMpknEType_3_3_; +text: .text%__1cHMemNodeHsize_of6kM_I_; +text: .text%__1cYCallStaticJavaDirectNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cYCallStaticJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNRelocIteratorJset_limit6MpC_v_; +text: .text%__1cIMachNodeTmay_be_short_branch6kM_i_: ad_i486.o; +text: .text%__1cRsarI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cHPhiNodeDcmp6kMrknENode__I_; +text: .text%jni_SetObjectArrayElement: jni.o; +text: .text%__1cNnmethodLocker2T6M_v_; +text: .text%__1cVjava_lang_ClassLoaderGparent6FpnHoopDesc__2_; +text: .text%__1cLOpaque2NodeGOpcode6kM_i_; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: bytecode.o; +text: .text%__1cMloadConLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLCastP2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIGraphKitSclear_saved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cIGraphKitTuse_exception_state6MpnNSafePointNode__pnENode__; +text: .text%__1cKstoreINodeFreloc6kM_i_; +text: .text%__1cRCardTableModRefBSPdirty_MemRegion6MnJMemRegion__v_; +text: .text%__1cNnmethodLocker2t6MpnHnmethod__v_; +text: .text%__1cNaddL_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNaddL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitOmake_slow_call6MpknITypeFunc_pCpkcpnENode_88_8_; +text: .text%__1cPcheckCastPPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUGenericGrowableArray2t6MiipnEGrET_i_v_; +text: .text%__1cSReferenceProcessorSdiscover_reference6MpnHoopDesc_nNReferenceType__i_; +text: .text%__1cTStackWalkCompPolicyYmethod_back_branch_event6MnMmethodHandle_iipnGThread__v_; +text: .text%__1cRCompilationPolicybJreset_counter_for_back_branch_event6MnMmethodHandle__v_; +text: .text%__1cNnegI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJStartNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cKciTypeFlowHdo_flow6M_v_; +text: .text%__1cKciTypeFlowKmap_blocks6M_v_; +text: .text%__1cKciTypeFlowKflow_types6M_v_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_i_v_; +text: .text%__1cKciTypeFlowLfind_ranges6M_v_; +text: .text%__1cKciTypeFlowXmark_known_range_starts6M_v_; +text: .text%__1cKciTypeFlow2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cKciTypeFlowPget_start_state6M_pkn0ALStateVector__; +text: .text%__1cXjava_lang_ref_ReferenceIset_next6FpnHoopDesc_2_v_; +text: .text%__1cOeFlagsRegUOperFclone6kM_pnIMachOper__; +text: .text%__1cSReferenceProcessorTget_discovered_list6MnNReferenceType__ppnHoopDesc__; +text: .text%__1cOPhaseIdealLoopRsplit_thru_region6MpnENode_2_2_; +text: .text%__1cOPhaseTransform2t6MnFPhaseLPhaseNumber__v_; +text: .text%__1cFKlassQup_cast_abstract6M_p0_; +text: .text%__1cFframebHnext_monitor_in_interpreter_frame6kMpnPBasicObjectLock__2_; +text: .text%__1cNsubL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMorI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cVPreserveExceptionMark2t6MrpnGThread__v_; +text: .text%__1cVPreserveExceptionMark2T6M_v_; +text: .text%__1cIciMethodJload_code6M_v_; +text: .text%__1cMciMethodDataJload_data6M_v_; +text: .text%__1cPconvL2I_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJimmI0OperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: multnode.o; +text: .text%__1cOCallRelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cPconvI2L_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJCMoveNodeLis_cmove_id6FpnOPhaseTransform_pnENode_44pnIBoolNode__4_; +text: .text%__1cNMemoryManagerHoops_do6MpnKOopClosure__v_; +text: .text%JVM_GetCPClassNameUTF; +text: .text%__1cLConvI2LNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNIdealLoopTreeNDCE_loop_body6M_v_; +text: .text%__1cNIdealLoopTreeVadjust_loop_exit_prob6MpnOPhaseIdealLoop__v_; +text: .text%__1cJAssemblerEleal6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cKstoreCNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%jni_GetStringLength: jni.o; +text: .text%__1cICodeHeapPsearch_freelist6MI_pnJFreeBlock__; +text: .text%__1cICodeHeapIallocate6MI_pv_; +text: .text%__1cKstorePNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionarybAcompute_loader_lock_object6FnGHandle_pnGThread__1_; +text: .text%__1cLeAXRegIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cFParseKdo_put_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cPmethodDataKlassRoop_is_methodData6kM_i_: methodDataKlass.o; +text: .text%__1cMciMethodData2t6MnQmethodDataHandle__v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodDataKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: methodDataKlass.o; +text: .text%__1cTcompareAndSwapLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOLibraryCallKitOgenerate_guard6MpnENode_pnKRegionNode_f_v_; +text: .text%__1cLOpaque1NodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKJNIHandlesLmake_global6FnGHandle_i_pnI_jobject__; +text: .text%__1cOClearArrayNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: jvm.o; +text: .text%__1cUPSGenerationCountersKupdate_all6M_v_: psGenerationCounters.o; +text: .text%__1cMoutputStream2t6Mi_v_; +text: .text%__1cWflagsReg_long_LTGEOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNloadRangeNodeFreloc6kM_i_; +text: .text%__1cFParseNpush_constant6MnKciConstant__i_; +text: .text%__1cMstringStream2t6MI_v_; +text: .text%__1cMstringStreamJas_string6M_pc_; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_: classes.o; +text: .text%__1cMstringStream2T6M_v_; +text: .text%__1cNaddL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMURShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMloadConDNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJrelocInfoKset_format6Mi_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: objArrayKlass.o; +text: .text%__1cPfieldDescriptorRint_initial_value6kM_i_; +text: .text%__1cJTimeStampSticks_since_update6kM_x_; +text: .text%__1cISubLNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse2.o; +text: .text%__1cNminI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOcompP_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRconstantPoolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cICodeBlobMset_oop_maps6MpnJOopMapSet__v_; +text: .text%__1cJCodeCacheIallocate6Fi_pnICodeBlob__; +text: .text%__1cQVMOperationQdDueueSqueue_remove_front6Mi_pnMVM_Operation__; +text: .text%__1cLProfileDataSis_VirtualCallData6M_i_: ciMethodData.o; +text: .text%__1cJScopeDescGis_top6kM_i_; +text: .text%__1cMorI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cOjmpLoopEndNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKTypeRawPtrFempty6kM_i_; +text: .text%__1cTCallInterpreterNodeGOpcode6kM_i_; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cScompI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRxorI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cVAdaptivePaddedAverageGsample6Mf_v_; +text: .text%__1cXmembar_acquire_lockNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNPrefetchQdDueueFclear6M_v_: psPromotionManager.o; +text: .text%__1cSPSPromotionManagerFreset6M_v_; +text: .text%__1cXconvI2L_reg_reg_zexNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cSPSPromotionManagerKflush_labs6M_v_; +text: .text%__1cRaddI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cIAndINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJloadSNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIregDOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cKBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cRClassPathZipEntryLopen_stream6Mpkc_pnPClassFileStream__; +text: .text%__1cSCompareAndSwapNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cICodeHeapPfollowing_block6MpnJFreeBlock__2_; +text: .text%__1cLRuntimeStubIis_alive6kM_i_: codeBlob.o; +text: .text%__1cFStateQ_sub_Op_URShiftI6MpknENode__v_; +text: .text%__1cUSafepointSynchronizeFblock6FpnKJavaThread__v_; +text: .text%__1cJttyLockerbCbreak_tty_lock_for_safepoint6Fi_v_; +text: .text%__1cIciObjectMis_obj_array6M_i_: ciInstanceKlass.o; +text: .text%__1cFciEnvZcheck_klass_accessibility6MpnHciKlass_pnMklassOopDesc__i_; +text: .text%__1cCosRcurrent_thread_id6F_i_; +text: .text%__1cMTypeKlassPtrFxdual6kM_pknEType__; +text: .text%__1cHciKlassMis_interface6M_i_: ciObjArrayKlass.o; +text: .text%__1cNtestP_regNodeFreloc6kM_i_; +text: .text%__1cHCompileTset_cached_top_node6MpnENode__v_; +text: .text%__1cIGotoNodeGOpcode6kM_i_; +text: .text%__1cENodeMsetup_is_top6M_v_; +text: .text%__1cTOopMapForCacheEntryZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cOleaPIdxOffNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPconvL2I_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHCompilePneed_stack_bang6kMi_i_; +text: .text%__1cOMachPrologNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cULinearLeastSquareFitGupdate6Mdd_v_; +text: .text%__1cSInterpreterRuntimeE_new6FpnKJavaThread_pnTconstantPoolOopDesc_i_v_; +text: .text%__1cNsubI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSmembar_acquireNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHRetNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cNobjArrayKlassPoop_is_objArray6kM_i_: objArrayKlass.o; +text: .text%__1cHRetNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKimmL32OperJconstantL6kM_x_: ad_i486_clone.o; +text: .text%__1cRindIndexScaleOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cRindIndexScaleOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cHRetNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRindIndexScaleOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cQleaPIdxScaleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLklassVtableRinitialize_vtable6MpnGThread__v_; +text: .text%__1cOPhaseIdealLoopQset_subtree_ctrl6MpnENode__v_; +text: .text%__1cRxorI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOMethodLivenessKBasicBlockJstore_two6Mi_v_; +text: .text%__1cPClassFileParserXverify_legal_class_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cJloadINodeIpeephole6MpnFBlock_ipnNPhaseRegAlloc_ri_pnIMachNode__; +text: .text%__1cJloadINodeFreloc6kM_i_; +text: .text%__1cNinstanceKlassVadd_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cKReturnNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOleaPIdxOffNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPjava_lang_ClassNcreate_mirror6FnLKlassHandle_pnGThread__pnHoopDesc__; +text: .text%__1cLklassVtableVinitialize_from_super6MnLKlassHandle__i_; +text: .text%__1cLklassVtableOcopy_vtable_to6MpnLvtableEntry__v_; +text: .text%__1cJloadBNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSCallLeafDirectNodeKmethod_set6Mi_v_; +text: .text%__1cKReturnNodeEhash6kM_I_: classes.o; +text: .text%__1cTleaPIdxScaleOffNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescbEfast_exception_handler_bci_for6MnLKlassHandle_ipnGThread__i_; +text: .text%__1cSInterpreterRuntimebFexception_handler_for_exception6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cIPhaseIFGISquareUp6M_v_; +text: .text%__1cYmulI_imm_RShift_highNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LEGTNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNxorI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKKlass_vtbl2n6FIrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cFKlassVbase_create_klass_oop6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__pnMklassOopDesc__; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: klass.o; +text: .text%__1cFKlassRinitialize_supers6MpnMklassOopDesc_pnGThread__v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: klass.o; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: klass.o; +text: .text%jni_GetStringUTFLength: jni.o; +text: .text%__1cWstatic_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cQjava_lang_StringLutf8_length6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc_ii_pc_; +text: .text%__1cFKlassRbase_create_klass6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__1_; +text: .text%jni_GetStringUTFRegion: jni.o; +text: .text%__1cIAndINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__: classes.o; +text: .text%__1cQPlaceholderTableJnew_entry6MipnNsymbolOopDesc_pnHoopDesc__pnQPlaceholderEntry__; +text: .text%__1cQPlaceholderTableMremove_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cQPlaceholderTableJadd_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cJcmpOpOperJnot_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cIAndINodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cKCMoveINodeGOpcode6kM_i_; +text: .text%__1cOMachEpilogNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: loopnode.o; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopnode.o; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: loopnode.o; +text: .text%__1cMPhaseChaitinSbuild_ifg_physical6MpnMResourceArea__I_; +text: .text%__1cMPhaseChaitinMreset_uf_map6MI_v_; +text: .text%__1cPsarI_eReg_1NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNPhaseCoalescePcoalesce_driver6M_v_; +text: .text%__1cHnmethodKpc_desc_at6MpCi_pnGPcDesc__; +text: .text%__1cHCompileQsync_stack_slots6kM_i_; +text: .text%__1cPindOffset32OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cJloadLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQSystemDictionaryTload_instance_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cUDebugInfoWriteStreamMwrite_handle6MpnI_jobject__v_; +text: .text%__1cVConstantOopWriteValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cPconvI2D_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNIdealLoopTreeTcheck_inner_safepts6MpnOPhaseIdealLoop__v_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: assembler_i486.o; +text: .text%__1cRAbstractAssemblerbDgenerate_stack_overflow_check6Mi_v_; +text: .text%__1cRaddI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNIdealLoopTreeUiteration_split_impl6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cNIdealLoopTreeOpolicy_peeling6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreebBpolicy_do_remove_empty_loop6MpnOPhaseIdealLoop__i_; +text: .text%__1cbBjava_lang_ref_SoftReferenceFclock6F_x_; +text: .text%__1cLStringTableGintern6FpnHoopDesc_pnGThread__2_; +text: .text%JVM_InternString; +text: .text%__1cXcmpL_reg_flags_LEGTNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_StringRas_unicode_string6FpnHoopDesc_ri_pH_; +text: .text%__1cCosGrandom6F_l_; +text: .text%__1cNget_next_hash6F_i_: synchronizer.o; +text: .text%__1cPshrI_eReg_1NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cUimpl_fp_store_helper6FpnKCodeBuffer_iiiiiii_i_: ad_i486.o; +text: .text%__1cVloadConL_low_onlyNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cXmembar_release_lockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOMacroAssemblerJincrement6MpnMRegisterImpl_i_v_; +text: .text%__1cKoopFactoryXnew_permanent_byteArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cENodeHdel_out6Mp0_v_: callGenerator.o; +text: .text%__1cJCmpL3NodeGOpcode6kM_i_; +text: .text%__1cOGenerateOopMapKcopy_state6MpnNCellTypeState_2_v_; +text: .text%__1cPclear_hashtable6FppnLNameSigHash__v_; +text: .text%__1cWflagsReg_long_LEGTOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cEDictIdoubhash6M_v_; +text: .text%__1cUinitialize_hashtable6FppnLNameSigHash__v_; +text: .text%__1cRcmpOp_commuteOperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cScompU_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSshrL_eReg_1_31NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPCountedLoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRMachSafePointNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cUmembar_cpu_orderNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPciObjectFactoryMvm_symbol_at6Fi_pnIciSymbol__; +text: .text%__1cNmodI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLRShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMMutableSpaceKinitialize6MnJMemRegion_i_v_; +text: .text%__1cKstoreLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%jni_SetIntField: jni.o; +text: .text%__1cENodeHis_Copy6kM_I_: ad_i486.o; +text: .text%__1cKstorePNodeFreloc6kM_i_; +text: .text%__1cNincI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPCountedLoopNodeHsize_of6kM_I_: classes.o; +text: .text%__1cNcmovI_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cTresource_free_bytes6FpcI_v_; +text: .text%__1cVLoaderConstraintTablePcheck_or_update6MnTinstanceKlassHandle_nGHandle_nMsymbolHandle__pkc_; +text: .text%__1cQSystemDictionaryRupdate_dictionary6FiIiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_: dictionary.o; +text: .text%__1cQSystemDictionaryRcheck_constraints6FiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cQSystemDictionaryQfind_placeholder6FiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cIProjNodeJideal_reg6kM_I_; +text: .text%__1cKDictionaryJadd_klass6MnMsymbolHandle_nGHandle_nLKlassHandle__v_; +text: .text%__1cKDictionaryJnew_entry6MIpnMklassOopDesc_pnHoopDesc__pnPDictionaryEntry__; +text: .text%__1cVloadConL_low_onlyNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPstoreImmI16NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: instanceKlass.o; +text: .text%__1cWflagsReg_long_EQdDNEOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cIAndINodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIAndINodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cRandI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLRuntimeStubYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cTGeneratePairingInfoRpossible_gc_point6MpnOBytecodeStream__i_: ciMethod.o; +text: .text%__1cJLoadBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFKlassXsearch_secondary_supers6kMpnMklassOopDesc__i_; +text: .text%__1cNmodI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRshrI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJLoadSNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOGenerateOopMapOset_bbmark_bit6Mi_v_; +text: .text%__1cIGraphKitOhas_ex_handler6M_i_; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_: ciMethodData.o; +text: .text%__1cRaddI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRsubI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopMdominated_by6MpnENode_2_v_; +text: .text%__1cPRoundDoubleNodeGOpcode6kM_i_; +text: .text%__1cRsarI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cICallNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJAssemblerEandl6MpnMRegisterImpl_i_v_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: doCall.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: connode.o; +text: .text%__1cLklassVtableQfill_in_mirandas6Mri_v_; +text: .text%__1cLklassItableRinitialize_itable6M_v_; +text: .text%__1cZCallDynamicJavaDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSshlL_eReg_1_31NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cCosXthread_local_storage_at6Fi_pv_; +text: .text%__1cSThreadLocalStoragePget_thread_slow6F_pnGThread__; +text: .text%__1cLregFPR1OperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cQinstanceRefKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cSmembar_releaseNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cLeDIRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cLregFPR1OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cSmembar_acquireNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitRmerge_fast_memory6MpnENode_2i_v_; +text: .text%__1cFKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cSCompareAndSwapNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cSindIndexOffsetOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cSindIndexOffsetOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cSindIndexOffsetOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cNstoreImmBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIGraphKitOmake_merge_mem6MpnENode_22_v_; +text: .text%__1cFKlassWappend_to_sibling_list6M_v_; +text: .text%__1cIGraphKitHopt_iff6MpnENode_2_2_; +text: .text%__1cOcompP_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQSystemDictionarySjava_system_loader6F_pnHoopDesc__; +text: .text%__1cSmembar_releaseNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: classes.o; +text: .text%__1cJLoadFNodeGOpcode6kM_i_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_2_v_; +text: .text%__1cFframeRis_compiled_frame6kMpi_i_; +text: .text%__1cTjava_lang_ThrowableNset_backtrace6FpnHoopDesc_2_v_; +text: .text%__1cKstoreCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMStartOSRNodeGOpcode6kM_i_; +text: .text%__1cHPhiNodeKmake_blank6FpnENode_2_p0_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: thread.o; +text: .text%__1cTClassLoadingServiceScompute_class_size6FpnNinstanceKlass__I_; +text: .text%__1cTClassLoadingServiceTnotify_class_loaded6FpnNinstanceKlass_i_v_; +text: .text%__1cNinstanceKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNmethodOopDescMsort_methods6FpnPobjArrayOopDesc_222_v_; +text: .text%__1cQSystemDictionaryVdefine_instance_class6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIVerifierQrelax_verify_for6FpnHoopDesc__i_; +text: .text%__1cNinstanceKlassQeager_initialize6MpnGThread__v_; +text: .text%__1cPClassFileStream2t6MpCipc_v_; +text: .text%__1cNinstanceKlassSprocess_interfaces6MpnGThread__v_; +text: .text%__1cNinstanceKlassQinit_implementor6M_v_; +text: .text%__1cNinstanceKlassbBdo_local_static_fields_impl6FnTinstanceKlassHandle_pFpnPfieldDescriptor_pnGThread__v5_v_; +text: .text%__1cRconstantPoolKlassIallocate6MipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cSinstanceKlassKlassXallocate_instance_klass6MiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cKoopFactoryRnew_instanceKlass6FiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cLklassVtableQget_num_mirandas6FpnMklassOopDesc_pnPobjArrayOopDesc_4_i_; +text: .text%__1cKoopFactoryQnew_constantPool6FipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cLklassItableZsetup_itable_offset_table6FnTinstanceKlassHandle__v_; +text: .text%__1cIUniverseTflush_dependents_on6FnTinstanceKlassHandle__v_; +text: .text%__1cLklassVtablebKcompute_vtable_size_and_num_mirandas6Fri1pnMklassOopDesc_pnPobjArrayOopDesc_nLAccessFlags_pnHoopDesc_pnNsymbolOopDesc_5_v_; +text: .text%__1cJCodeCachebKnumber_of_nmethods_with_dependencies6F_i_; +text: .text%__1cPClassFileParserbBparse_constant_pool_entries6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: constantPoolKlass.o; +text: .text%__1cVjava_lang_ClassLoaderRis_trusted_loader6FpnHoopDesc__i_; +text: .text%__1cQSystemDictionaryQadd_to_hierarchy6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserNparse_methods6MnSconstantPoolHandle_ipnLAccessFlags_ppnPobjArrayOopDesc_66pnGThread__nOobjArrayHandle__; +text: .text%__1cNinstanceKlassWdo_local_static_fields6MpFpnPfieldDescriptor_pnGThread__v4_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: constantPoolKlass.o; +text: .text%__1cPClassFileParserUcompute_oop_map_size6MnTinstanceKlassHandle_ii_i_; +text: .text%__1cPClassFileParserNfill_oop_maps6MnTinstanceKlassHandle_ii_v_; +text: .text%__1cPClassFileParserVset_precomputed_flags6MnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserTparse_constant_pool6MpnGThread__nSconstantPoolHandle__; +text: .text%__1cPClassFileParserbDcompute_transitive_interfaces6MnTinstanceKlassHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cPClassFileParserYcheck_super_class_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserMparse_fields6MnSconstantPoolHandle_ipnUFieldAllocationCount_pnOobjArrayHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserbCcheck_super_interface_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbBcheck_final_method_override6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbAparse_classfile_attributes6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: constantPoolKlass.o; +text: .text%__1cPClassFileParserMsort_methods6MnOobjArrayHandle_111pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserQparse_interfaces6MnSconstantPoolHandle_nGHandle_2pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserOparseClassFile6MnMsymbolHandle_nGHandle_2r1pnGThread__nTinstanceKlassHandle__; +text: .text%__1cQSystemDictionaryVresolve_super_or_fail6FnMsymbolHandle_1nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cLloadSSFNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNObjectMonitorHis_busy6kM_i_; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceKlass.o; +text: .text%__1cNinstanceKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlass.o; +text: .text%__1cSindIndexOffsetOperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cSThreadProfilerMark2T6M_v_; +text: .text%__1cJEventMark2t6MpkcE_v_: classLoader.o; +text: .text%__1cSThreadProfilerMark2t6Mn0AGRegion__v_; +text: .text%__1cFVTuneOend_class_load6F_v_; +text: .text%__1cFVTuneQstart_class_load6F_v_; +text: .text%__1cLClassLoaderOload_classfile6FnMsymbolHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cENodeHis_Call6M_pnICallNode__: loopnode.o; +text: .text%__1cQSystemDictionaryRfind_shared_class6FnMsymbolHandle__pnMklassOopDesc__; +text: .text%__1cQSystemDictionaryRload_shared_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cQSystemDictionaryRload_shared_class6FnTinstanceKlassHandle_nGHandle_pnGThread__1_; +text: .text%__1cHMatcherNfind_receiver6Fi_nFVMRegEName__; +text: .text%__1cNnegI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframeZinterpreter_frame_set_bcx6Mi_v_; +text: .text%__1cPClassFileParserbKparse_classfile_sourcefile_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cENodeHdel_out6Mp0_v_: generateOptoStub.o; +text: .text%__1cRsubI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNandI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cILoopNode2t6MpnENode_2_v_; +text: .text%__1cVeADXRegL_low_onlyOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cENodeGis_Con6kM_I_: memnode.o; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: debugInfo.o; +text: .text%__1cMPhaseIterGVNIoptimize6M_v_; +text: .text%__1cJloadFNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQPackageHashtableMcompute_hash6Mpkci_I_: classLoader.o; +text: .text%__1cOMethodLivenessRinit_basic_blocks6M_v_; +text: .text%__1cOMethodLivenessNinit_gen_kill6M_v_; +text: .text%__1cWCallLeafNoFPDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOMethodLivenessSpropagate_liveness6M_v_; +text: .text%__1cOMethodLiveness2t6MpnFArena_pnIciMethod__v_; +text: .text%__1cOMethodLivenessQcompute_liveness6M_v_; +text: .text%__1cHCompilebAvarargs_C_out_slots_killed6kM_I_; +text: .text%__1cSMemBarVolatileNodeGOpcode6kM_i_; +text: .text%__1cTMachCallRuntimeNodeSis_MachCallRuntime6M_p0_: ad_i486_misc.o; +text: .text%__1cMloadConPNodeFreloc6kM_i_; +text: .text%__1cSTailCalljmpIndNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cYconstantPoolCacheOopDescKinitialize6MrnIintArray__v_; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: cpCacheKlass.o; +text: .text%__1cNinstanceKlassLverify_code6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIVerifierRverify_byte_codes6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassNrewrite_class6MpnGThread__v_; +text: .text%__1cNinstanceKlassWadd_loader_constraints6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cLklassVtableYadd_new_mirandas_to_list6FpnNGrowableArray4CpnNmethodOopDesc___pnPobjArrayOopDesc_6pnMklassOopDesc__v_; +text: .text%__1cIRewriterScompute_index_maps6FnSconstantPoolHandle_rpnIintArray_rpnIintStack__v_; +text: .text%__1cKoopFactoryVnew_constantPoolCache6FipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cIintArray2t6Mki1_v_: rewriter.o; +text: .text%__1cWconstantPoolCacheKlassIallocate6MipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cIRewriterHrewrite6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: cpCacheKlass.o; +text: .text%__1cIRewriterXnew_constant_pool_cache6FrnIintArray_pnGThread__nXconstantPoolCacheHandle__; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: cpCacheKlass.o; +text: .text%__1cRmethodDataOopDescJis_mature6kM_i_; +text: .text%__1cJCodeCacheMfind_nmethod6Fpv_pnHnmethod__; +text: .text%__1cQciByteCodeStreamUis_unresolved_string6kM_i_; +text: .text%__1cFciEnvUis_unresolved_string6kMpnPciInstanceKlass_i_i_; +text: .text%JVM_GetMethodIxExceptionIndexes; +text: .text%JVM_GetMethodIxSignatureUTF; +text: .text%JVM_GetMethodIxExceptionsCount; +text: .text%JVM_GetMethodIxMaxStack; +text: .text%JVM_GetMethodIxByteCode; +text: .text%JVM_GetMethodIxArgsSize; +text: .text%JVM_GetMethodIxByteCodeLength; +text: .text%__1cFciEnvZis_unresolved_string_impl6kMpnNinstanceKlass_i_i_; +text: .text%__1cKoopFactoryWnew_permanent_intArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cWCountInterfacesClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cKTypeRawPtrEmake6FpC_pk0_; +text: .text%__1cPClassFileParserVparse_exception_table6MIInSconstantPoolHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cFParseXcatch_inline_exceptions6MpnNSafePointNode__v_; +text: .text%__1cNmulL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNminI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__: classes.o; +text: .text%__1cYciExceptionHandlerStreamPcount_remaining6M_i_; +text: .text%__1cPconvI2L_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSshrL_eReg_1_31NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJLoadLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cPshrI_eReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWconstantPoolCacheKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cYmulI_imm_RShift_highNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHMatcherUc_calling_convention6FpnLRegPair_I_v_; +text: .text%__1cPCallRuntimeNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__: classes.o; +text: .text%__1cHAddNodeGis_Add6kM_pk0_: classes.o; +text: .text%__1cUBytecode_tableswitchOdest_offset_at6kMi_i_; +text: .text%__1cSObjectSynchronizerJnotifyall6FnGHandle_pnGThread__v_; +text: .text%__1cCosRelapsed_frequency6F_x_; +text: .text%__1cRshrI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMelapsedTimerHseconds6kM_d_; +text: .text%__1cRaddL_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNaddI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIciObjectMhas_encoding6M_i_; +text: .text%__1cSloadL_volatileNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNandI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cQPackageHashtableJget_entry6MiIpkcI_pnLPackageInfo__: classLoader.o; +text: .text%__1cLClassLoaderOlookup_package6Fpkc_pnLPackageInfo__; +text: .text%__1cLeDIRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cENodeUdepends_only_on_test6kM_i_: connode.o; +text: .text%__1cFStateR_sub_Op_LoadKlass6MpknENode__v_; +text: .text%__1cQVMOperationQdDueueNqueue_oops_do6MipnKOopClosure__v_; +text: .text%__1cUCallCompiledJavaNodeGOpcode6kM_i_; +text: .text%__1cRLowMemoryDetectorWdetect_after_gc_memory6FpnKMemoryPool__v_; +text: .text%__1cUmembar_cpu_orderNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPClassFileParserbSparse_constant_pool_interfacemethodref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cRsalI_eReg_immNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLClassLoaderLadd_package6Fpkci_i_; +text: .text%__1cNloadKlassNodeFreloc6kM_i_; +text: .text%__1cFStateV_sub_Op_MemBarRelease6MpknENode__v_; +text: .text%__1cMloadConLNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cHMatcherQpost_fast_unlock6FpknENode__i_; +text: .text%__1cZInterpreterMacroAssemblerKverify_oop6MpnMRegisterImpl_nITosState__v_; +text: .text%__1cFStateM_sub_Op_SubI6MpknENode__v_; +text: .text%__1cLklassItableTcompute_itable_size6FnOobjArrayHandle__i_; +text: .text%__1cLeSIRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKciTypeFlowLStateVectorMdo_putstatic6MpnQciByteCodeStream__v_; +text: .text%__1cIRootNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cIXorINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNaddP_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNSafepointBlobbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cLregFPR1OperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: instanceKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlass.o; +text: .text%__1cKciTypeFlowLStateVectorGdo_ldc6MpnQciByteCodeStream__v_; +text: .text%__1cbDReferenceProcessorInitializerIis_clean6kM_v_: concurrentMarkSweepGeneration.o; +text: .text%__1cKManagementJtimestamp6F_x_; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: cfgnode.o; +text: .text%__1cIPSOldGenPupdate_counters6M_v_; +text: .text%__1cNSharedRuntimebOraw_exception_handler_for_return_address6FpC_1_; +text: .text%__1cNSharedRuntimebKexception_handler_for_return_address6FpC_1_; +text: .text%__1cNaddI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cHOrINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cMnegF_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMnegF_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompP_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowLStateVectorLdo_putfield6MpnQciByteCodeStream__v_; +text: .text%__1cXjava_lang_ref_ReferenceWpending_list_lock_addr6F_ppnHoopDesc__; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstance.o; +text: .text%__1cSshlL_eReg_1_31NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%signalHandler; +text: .text%JVM_handle_solaris_signal; +text: .text%__1cMnegF_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNinstanceKlassbBcall_class_initializer_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassRclass_initializer6M_pnNmethodOopDesc__; +text: .text%__1cNinstanceKlassbOset_initialization_state_and_notify_impl6FnTinstanceKlassHandle_n0AKClassState_pnGThread__v_; +text: .text%__1cNinstanceKlassbJset_initialization_state_and_notify6Mn0AKClassState_pnGThread__v_; +text: .text%__1cNinstanceKlassWcall_class_initializer6MpnGThread__v_; +text: .text%__1cKcopy_table6FppC1i_v_: interpreter.o; +text: .text%__1cMtlsLoadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQVMOperationQdDueueLremove_next6M_pnMVM_Operation__; +text: .text%__1cOPhaseIdealLoopNreorg_offsets6MpnNIdealLoopTree__v_; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopopts.o; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_i_v_; +text: .text%__1cPcmpFastLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKBufferBlob2t6Mpkci_v_; +text: .text%__1cKBufferBlobGcreate6Fpkci_p0_; +text: .text%__1cNaddL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKBufferBlob2n6FII_pv_; +text: .text%__1cICodeBlob2t6Mpkcii_v_; +text: .text%__1cMeBCXRegLOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cNSharedRuntimebWnative_method_throw_unsatisfied_link_error_entry6F_pC_; +text: .text%__1cNRelocIteratorTlocs_and_index_size6Fii_i_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: compile.o; +text: .text%__1cScompP_mem_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCCompiledCodeSafepointHandlerbDhandle_polling_page_exception6M_pC_; +text: .text%__1cKJavaThreadUin_stack_yellow_zone6MpC_i_: os_solaris_i486.o; +text: .text%__1cFframebDsender_for_raw_compiled_frame6kMpnLRegisterMap__0_; +text: .text%__1cMVM_OperationSis_cheap_allocated6kM_i_: vm_operations.o; +text: .text%__1cUSafepointSynchronizebDhandle_polling_page_exception6FpnKJavaThread__pC_; +text: .text%__1cKloadUBNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cLGCTaskQdDueueKinitialize6M_v_; +text: .text%__1cSCardTableExtensionbAscavenge_contents_parallel6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager_I_v_; +text: .text%__1cTOldToYoungRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cJStealTaskEname6M_pc_: psTasks.o; +text: .text%__1cTOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cJStealTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cJStealTask2t6Mi_v_; +text: .text%__1cNGCTaskManagerMnote_release6MI_v_; +text: .text%__1cHnmethodJcode_size6kM_i_: nmethod.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: memnode.o; +text: .text%__1cJcmpOpOperEless6kM_i_: ad_i486_clone.o; +text: .text%__1cSaddD_reg_roundNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSmembar_releaseNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cICodeHeapTmark_segmap_as_used6MII_v_; +text: .text%__1cJloadCNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cNsubL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJimmL0OperJconstantL6kM_x_: ad_i486_clone.o; +text: .text%__1cFParseWensure_phis_everywhere6M_v_; +text: .text%__1cLConvL2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNxorI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNandL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStartNodeHsize_of6kM_I_; +text: .text%__1cHMatcherFxform6MpnENode_i_2_; +text: .text%__1cHMatcherLfind_shared6MpnENode__v_; +text: .text%__1cTMachCallRuntimeNodePret_addr_offset6M_i_; +text: .text%__1cILRG_List2t6MI_v_; +text: .text%__1cILoopNodeHsize_of6kM_I_: classes.o; +text: .text%__1cEDict2t6MpFpkv2_ipF2_ipnFArena_i_v_; +text: .text%__1cGBundlePinitialize_nops6FppnIMachNode__v_; +text: .text%__1cOMachPrologNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNstoreImmPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLeAXRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cNsubL_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cHi2bNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cRmethodDataOopDescYcompute_extra_data_count6Fii_i_; +text: .text%__1cWCallLeafNoFPDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse3.o; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMLinkResolverbHlookup_instance_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cXJNI_ArgumentPusherVaArgHiterate6MX_v_: jni.o; +text: .text%__1cNFingerprinterLfingerprint6M_X_: jni.o; +text: .text%__1cKimmI16OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cSshrL_eReg_1_31NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPstoreImmI16NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIemit_d166FrnKCodeBuffer_i_v_; +text: .text%__1cRxorI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRsubI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJeRegPOperFclone6kM_pnIMachOper__; +text: .text%__1cJAssemblerEmovl6MnHAddress_i_v_; +text: .text%__1cNobjArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cFStateT_sub_Op_CheckCastPP6MpknENode__v_; +text: .text%__1cKciTypeFlowLStateVectorJhalf_type6FpnGciType__3_: ciTypeFlow.o; +text: .text%__1cJAssemblerDnop6M_v_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: connode.o; +text: .text%__1cMFastLockNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%JVM_GetFieldIxModifiers; +text: .text%__1cNmodI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKReturnNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRsarI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFParseKarray_load6MnJBasicType__v_; +text: .text%__1cRjmpConU_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cRjmpConU_shortNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cLjmpConUNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cRjmpConU_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKcmpOpUOperFclone6kM_pnIMachOper__; +text: .text%__1cFStateO_sub_Op_StoreB6MpknENode__v_; +text: .text%__1cFTypeFCeq6kMpknEType__i_; +text: .text%__1cFParseNadd_safepoint6M_v_; +text: .text%__1cLOpaque2NodeEhash6kM_I_; +text: .text%JVM_IsConstructorIx; +text: .text%__1cIimmPOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConPNodeFclone6kM_pnENode__; +text: .text%__1cSshrL_eReg_1_31NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: connode.o; +text: .text%__1cRxorI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXmembar_release_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassTarray_klass_or_null6M_pnMklassOopDesc__; +text: .text%__1cKadd_n_reqs6FpnENode_1_v_: graphKit.o; +text: .text%__1cNdecI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cILoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cICodeHeapLmerge_right6MpnJFreeBlock__v_; +text: .text%__1cIregDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNsubI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXconvI2L_reg_reg_zexNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescTset_native_function6MpC_v_; +text: .text%__1cIPhaseIFGYCompute_Effective_Degree6M_v_; +text: .text%__1cMPhaseChaitinISimplify6M_v_; +text: .text%__1cMPhaseChaitinGSelect6M_I_; +text: .text%__1cMPhaseChaitinOcache_lrg_info6M_v_; +text: .text%__1cNtestU_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJScopeDescJstream_at6kMi_pnTDebugInfoReadStream__; +text: .text%__1cTDebugInfoReadStream2t6MpknHnmethod_i_v_; +text: .text%__1cLOptoRuntimeJstub_name6FpC_pkc_; +text: .text%__1cNSignatureInfoHdo_long6M_v_: frame.o; +text: .text%__1cSvframeStreamCommonZsecurity_get_caller_frame6Mi_v_; +text: .text%__1cENodeHrm_prec6MI_v_; +text: .text%__1cUjni_invoke_nonstatic6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cKciTypeFlowLStateVectorOmeet_exception6MpnPciInstanceKlass_pk1_i_; +text: .text%__1cKloadUBNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cWflagsReg_long_EQdDNEOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKciTypeFlowPflow_exceptions6MpnNGrowableArray4Cpn0AFBlock___pnNGrowableArray4CpnPciInstanceKlass___pn0ALStateVector__v_; +text: .text%__1cIBoolNodeZis_counted_loop_exit_test6M_i_; +text: .text%__1cPciInstanceKlassTis_java_lang_Object6M_i_; +text: .text%__1cRaddL_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXmembar_release_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cQsalI_eReg_CLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNnegI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompP_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cILoopNodeHsize_of6kM_I_: loopnode.o; +text: .text%__1cMPhaseChaitinFSplit6MI_I_; +text: .text%__1cZPhaseConservativeCoalesce2t6MrnMPhaseChaitin__v_; +text: .text%__1cMPhaseChaitinHcompact6M_v_; +text: .text%__1cMPhaseChaitinZcompress_uf_map_for_nodes6M_v_; +text: .text%__1cZPhaseConservativeCoalesceGverify6M_v_; +text: .text%__1cHTypePtrFxmeet6kMpknEType__3_; +text: .text%__1cOcompP_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cHMatcherQis_spillable_arg6Fi_i_; +text: .text%__1cSsafePoint_pollNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cHMatcherKcan_be_arg6Fi_i_; +text: .text%__1cSTailCalljmpIndNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIregFOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cTDebugInfoReadStreamLread_handle6M_nGHandle__; +text: .text%__1cJScopeDesc2t6MpknHnmethod_i_v_; +text: .text%__1cYcmpL_zero_flags_LEGTNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOcompiledVFrame2t6MpknFframe_pknLRegisterMap_pnKJavaThread_pnJScopeDesc__v_; +text: .text%__1cNSignatureInfoHdo_long6M_v_: bytecode.o; +text: .text%__1cXvirtual_call_RelocationJfirst_oop6M_pC_; +text: .text%__1cXvirtual_call_RelocationJoop_limit6M_pC_; +text: .text%__1cQComputeCallStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cHnmethodOis_java_method6kM_i_: nmethod.o; +text: .text%__1cNminI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSCallLeafDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYcmpL_zero_flags_LEGTNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompiledVFrameGis_top6kM_i_; +text: .text%__1cFStateQ_sub_Op_CallLeaf6MpknENode__v_; +text: .text%__1cKJNIHandlesOdestroy_global6FpnI_jobject_i_v_; +text: .text%__1cHi2sNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRNativeGeneralJumpQjump_destination6kM_pC_; +text: .text%__1cMoutputStreamFprint6MpkcE_v_; +text: .text%__1cIciObjectIis_klass6M_i_: ciInstance.o; +text: .text%__1cMoutputStreamMdo_vsnprintf6FpcIpkcpvirI_3_; +text: .text%__1cOGenerateOopMapGdo_ldc6Mii_v_; +text: .text%__1cRaddI_eReg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cITypeLongFxdual6kM_pknEType__; +text: .text%__1cSloadL_volatileNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_immNodeFreloc6kM_i_; +text: .text%__1cPcheckCastPPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSvframeStreamCommonYfill_from_compiled_frame6MpnHnmethod_i_v_; +text: .text%__1cHnmethodQis_native_method6kM_i_: nmethod.o; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: cfgnode.o; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: cfgnode.o; +text: .text%__1cTshrL_eReg_32_63NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: objArrayKlass.o; +text: .text%__1cPClassFileStreamGget_u86MpnGThread__X_; +text: .text%__1cPshrI_eReg_1NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cbBjava_lang_ref_SoftReferenceJtimestamp6FpnHoopDesc__x_; +text: .text%__1cJNode_ListEyank6MpnENode__v_; +text: .text%__1cQLRUMaxHeapPolicyWshould_clear_reference6MpnHoopDesc__i_; +text: .text%__1cIGraphKitNstore_barrier6MpnENode_22_v_; +text: .text%__1cMrep_stosNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cWflagsReg_long_LEGTOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cSThreadLocalStorageTpd_getTlsAccessMode6F_n0AQpd_tlsAccessMode__; +text: .text%__1cOMacroAssemblerKget_thread6MpnMRegisterImpl__v_; +text: .text%__1cKloadUBNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLRethrowNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHMonitorGnotify6M_i_; +text: .text%__1cIRootNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRcmpFastUnlockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIGraphKitNshared_unlock6MpnENode_2_v_; +text: .text%__1cNSafePointNodeLpop_monitor6M_v_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_exit_Type6F_pknITypeFunc__; +text: .text%__1cIRootNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cFStateS_sub_Op_FastUnlock6MpknENode__v_; +text: .text%__1cJloadFNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOstackSlotFOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cSstore_to_stackslot6FrnKCodeBuffer_iii_v_; +text: .text%__1cNmethodOopDescVset_signature_handler6MpC_v_; +text: .text%__1cMnadxRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_: callnode.o; +text: .text%__1cIAndINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cUmembar_cpu_orderNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMloadConINodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: cfgnode.o; +text: .text%__1cPconvI2L_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescTverified_code_entry6M_pC_; +text: .text%__1cNSharedRuntimeXfind_callee_info_helper6FpnKJavaThread_rnMvframeStream_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cOjmpLoopEndNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRmulI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPsarI_eReg_1NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%jni_ExceptionCheck: jni.o; +text: .text%__1cURethrowExceptionNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cKstoreLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateP_sub_Op_RShiftI6MpknENode__v_; +text: .text%__1cOGenerateOopMapHppstore6MpnNCellTypeState_i_v_; +text: .text%__1cIMulLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cOcompP_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMvalue_of_loc6FppnHoopDesc__i_; +text: .text%__1cNSafePointNodeMpush_monitor6MpknMFastLockNode__v_; +text: .text%__1cTcompareAndSwapLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNandI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cTshrL_eReg_32_63NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSshlL_eReg_1_31NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKcmpOpUOperJnot_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cOcompP_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNSCMemProjNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cNandI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNaddP_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNstoreImmPNodeFreloc6kM_i_; +text: .text%__1cKNativeCallXset_destination_mt_safe6MpC_v_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKtype2basic6FpknEType__nJBasicType__; +text: .text%__1cIAndLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cIAndLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cNstoreImmBNodeFreloc6kM_i_; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: jvm.o; +text: .text%JVM_Clone; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: jvm.o; +text: .text%__1cNcmovI_regNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadKlassNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVeADXRegL_low_onlyOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: objArrayKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: objArrayKlass.o; +text: .text%__1cNobjArrayKlassIallocate6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: objArrayKlass.o; +text: .text%__1cMeBCXRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNimmI_1_31OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cLPhaseValues2T5B6M_v_; +text: .text%__1cNtestU_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJLoadCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRsubI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIJumpDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cMalloc_object6FpnH_jclass_pnGThread__pnPinstanceOopDesc__: jni.o; +text: .text%__1cIGraphKitOnull_check_oop6MpnKRegionNode_pnENode_i_4_; +text: .text%__1cRmulI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHOrINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKTypeRawPtrEmake6FnHTypePtrDPTR__pk0_; +text: .text%__1cKTypeAryPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cFKlassDLCA6Mp0_1_; +text: .text%__1cHciKlassVleast_common_ancestor6Mp0_1_; +text: .text%__1cPconvL2I_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJeRegLOperFclone6kM_pnIMachOper__; +text: .text%__1cbCfind_class_from_class_loader6FpnHJNIEnv__nMsymbolHandle_CnGHandle_3CpnGThread__pnH_jclass__; +text: .text%__1cNstoreImmINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPshlI_eReg_1NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNmaxI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIMinINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cHnmethodVis_dependent_on_entry6MpnMklassOopDesc_2pnNmethodOopDesc__i_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlass.o; +text: .text%__1cKloadUBNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMTypeKlassPtrFxmeet6kMpknEType__3_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cICodeHeapPadd_to_freelist6MpnJHeapBlock__v_; +text: .text%__1cICodeHeapKdeallocate6Mpv_v_; +text: .text%__1cJCodeCacheEfree6FpnICodeBlob__v_; +text: .text%__1cJVectorSetGslamin6Mrk0_v_; +text: .text%__1cQsalI_eReg_CLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKNode_ArrayFclear6M_v_; +text: .text%__1cKBufferBlobEfree6Fp0_v_; +text: .text%__1cIRootNodeHis_Root6M_p0_: classes.o; +text: .text%__1cXJNI_ArgumentPusherVaArgIget_long6M_v_: jni.o; +text: .text%__1cOMethodLivenessKBasicBlockPmerge_exception6MnGBitMap__i_; +text: .text%__1cNminI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNsubI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYinternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cMorI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNtestU_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%jni_NewObject: jni.o; +text: .text%__1cMTailCallNodeKmatch_edge6kMI_I_; +text: .text%__1cIciMethodRinstructions_size6M_i_; +text: .text%__1cRsarI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPshrI_eReg_1NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKStoreFNodeGOpcode6kM_i_; +text: .text%__1cFStateO_sub_Op_StoreC6MpknENode__v_; +text: .text%__1cNobjArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cFciEnvKcompile_id6M_I_; +text: .text%__1cRAbstractAssemblerFflush6M_v_; +text: .text%__1cJloadINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRaddL_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNloadConL0NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNsubL_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNsubL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompU_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cScompU_eReg_memNodeFreloc6kM_i_; +text: .text%__1cTleaPIdxScaleOffNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMURShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNSharedRuntimeQfind_callee_info6FpnKJavaThread_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cNsubL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSCallLeafDirectNodeFreloc6kM_i_; +text: .text%__1cNSharedRuntimeOresolve_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cNSharedRuntimeSresolve_sub_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cKCompiledIC2t6MpnKNativeCall__v_; +text: .text%__1cNcmovI_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNcmovI_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cGIfNodeMdominated_by6MpnENode_pnMPhaseIterGVN__v_; +text: .text%__1cIMulDNodeGOpcode6kM_i_; +text: .text%__1cHMatcherPprior_fast_lock6FpknENode__i_; +text: .text%__1cFStateV_sub_Op_MemBarAcquire6MpknENode__v_; +text: .text%__1cLConvL2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMachEpilogNodeFreloc6kM_i_; +text: .text%__1cOMachEpilogNodeNis_MachEpilog6M_p0_: ad_i486.o; +text: .text%__1cOMachEpilogNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivLNodeGOpcode6kM_i_; +text: .text%__1cSstring_compareNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cSsafePoint_pollNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitNgen_checkcast6MpnENode_2p2_2_; +text: .text%__1cSindIndexOffsetOperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cLStrCompNodeGOpcode6kM_i_; +text: .text%__1cSindIndexOffsetOperOindex_position6kM_i_: ad_i486.o; +text: .text%__1cKRelocationJpack_data6M_i_: codeBlob.o; +text: .text%__1cGciTypeMis_classless6kM_i_: ciInstanceKlass.o; +text: .text%__1cLPcDescCacheKpc_desc_at6kMpnHnmethod_pCi_pnGPcDesc__; +text: .text%__1cXmembar_acquire_lockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLloadSSFNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNaddP_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cYcmpL_zero_flags_LEGTNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEtemp6F_pnMRegisterImpl__; +text: .text%__1cPstoreImmI16NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframeRretrieve_receiver6MpnLRegisterMap__pnHoopDesc__; +text: .text%__1cJcmpOpOperNgreater_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cPBytecode_invokeNstatic_target6MpnGThread__nMmethodHandle__; +text: .text%__1cNmodL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmulL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIDivINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJAssemblerDjmp6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cSloadL_volatileNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitRgen_subtype_check6MpnENode_2_2_; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc__pc_; +text: .text%__1cENodeHdel_out6Mp0_v_: parseHelper.o; +text: .text%__1cNandI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJTraceTime2t6MpkciipnMoutputStream__v_; +text: .text%__1cTciConstantPoolCache2t6MpnFArena_i_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_tree6M_v_; +text: .text%__1cLConvD2INodeGOpcode6kM_i_; +text: .text%__1cFframeIpatch_pc6MpnGThread_pC_v_; +text: .text%__1cOPhaseIdealLoop2t6MrnMPhaseIterGVN_pk0i_v_; +text: .text%__1cKciTypeFlowFRangeSprivate_copy_count6kMpn0AGJsrSet__i_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cXmembar_release_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXcmpL_reg_flags_LTGENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: frame.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: codeBlob.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: callnode.o; +text: .text%__1cNIdealLoopTreeMis_loop_exit6kMpnENode_pnOPhaseIdealLoop__2_; +text: .text%__1cJloadBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cZload_long_indOffset32OperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cZload_long_indOffset32OperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cNincI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJloadLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKstoreFNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cYcmpL_zero_flags_LEGTNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNxorI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNandL_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cNtestU_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: onStackReplacement.o; +text: .text%__1cSciExceptionHandlerLcatch_klass6M_pnPciInstanceKlass__; +text: .text%__1cXcmpL_reg_flags_LEGTNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNincI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: loopnode.o; +text: .text%__1cNSafePointNodeKgrow_stack6MpnIJVMState_I_v_; +text: .text%__1cNxorI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cLRShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRsubI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cKBlock_ListGinsert6MIpnFBlock__v_; +text: .text%__1cECopyYconjoint_words_to_higher6FpnIHeapWord_2I_v_: block.o; +text: .text%__1cLRShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: loopnode.o; +text: .text%__1cFStateP_sub_Op_CastP2I6MpknENode__v_; +text: .text%__1cLCastP2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNaddL_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cKstoreCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_NewGlobalRef: jni.o; +text: .text%__1cKPSYoungGenRcapacity_in_bytes6kM_I_; +text: .text%__1cMtlsLoadPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNmaxI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNandL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMVirtualSpaceNreserved_size6kM_I_; +text: .text%__1cHNTarjanIsetdepth6MIpI_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_late6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopQbuild_loop_early6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopKDominators6M_v_; +text: .text%__1cHNTarjanDDFS6Fp0rnJVectorSet_pnOPhaseIdealLoop_pI_i_; +text: .text%__1cOPhaseIdealLoopRinit_dom_lca_tags6M_v_; +text: .text%__1cNaddP_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvI2L_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLeCXRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cJcmpOpOperHgreater6kM_i_: ad_i486_clone.o; +text: .text%__1cSmembar_acquireNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLklassItablebFinitialize_itable_for_interface6MpnMklassOopDesc_pnRitableMethodEntry__v_; +text: .text%__1cScheck_phi_clipping6FpnHPhiNode_rpnHConNode_rI45rpnENode_5_i_: cfgnode.o; +text: .text%__1cQjava_lang_ThreadRset_thread_status6FpnHoopDesc_n0AMThreadStatus__v_; +text: .text%__1cbFunnecessary_membar_volatileNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKCompiledICIis_clean6kM_i_; +text: .text%__1cMPhaseChaitinVfind_base_for_derived6MppnENode_2rI_2_; +text: .text%__1cKciTypeFlowLStateVectorGdo_new6MpnQciByteCodeStream__v_; +text: .text%__1cNinstanceKlassUfind_interface_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cENodeGOpcode6kM_i_; +text: .text%__1cKJavaThreadGactive6F_p0_; +text: .text%__1cFframeZinterpreter_frame_set_bcp6MpC_v_; +text: .text%__1cKstoreLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKJavaThreadNreguard_stack6MpC_i_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle__v_; +text: .text%JVM_FillInStackTrace; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle_pnGThread__v_; +text: .text%__1cSInterpreterRuntimePset_bcp_and_mdp6FpCpnKJavaThread__v_; +text: .text%__1cTjava_lang_ThrowableQclear_stacktrace6FpnHoopDesc__v_; +text: .text%__1cIUniverseWis_out_of_memory_error6FnGHandle__i_; +text: .text%__1cJloadSNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSshlL_eReg_1_31NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_StringOchar_converter6FnGHandle_HHpnGThread__1_; +text: .text%__1cLPcDescCacheLadd_pc_desc6MpnGPcDesc__v_; +text: .text%__1cNcmovI_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOstackSlotFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOstackSlotFOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cLloadSSFNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateN_sub_Op_LoadL6MpknENode__v_; +text: .text%__1cNnegI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEcmpl6MnHAddress_i_v_; +text: .text%__1cJloadFNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cIModINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOGenerateOopMapJdo_astore6Mi_v_; +text: .text%__1cKCompiledICZcompute_monomorphic_entry6FnMmethodHandle_nLKlassHandle_iirnOCompiledICInfo_pnGThread__v_; +text: .text%__1cObox_handleNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cbBInterpreterCodeletInterfaceRcode_size_to_size6kMi_i_: interpreter.o; +text: .text%__1cbBInterpreterCodeletInterfaceKinitialize6MpnEStub_i_v_: interpreter.o; +text: .text%__1cIPhaseCFGOinsert_goto_at6MII_v_; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: memnode.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: memnode.o; +text: .text%__1cUParallelScavengeHeapEused6kM_I_; +text: .text%__1cOmulIS_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cLOpaque2NodeLbottom_type6kM_pknEType__: connode.o; +text: .text%__1cTshrL_eReg_32_63NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerGmovzxb6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cHciKlassSsuper_check_offset6M_I_; +text: .text%__1cOMacroAssemblerSload_unsigned_byte6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cICmpDNodeGOpcode6kM_i_; +text: .text%__1cLRuntimeStubbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: parse2.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: loopnode.o; +text: .text%__1cNDispatchTableJset_entry6MirnKEntryPoint__v_; +text: .text%__1cFParseJdo_ifnull6MnIBoolTestEmask__v_; +text: .text%__1cNmethodOopDescVclear_native_function6M_v_; +text: .text%__1cFframeLnmethods_do6M_v_; +text: .text%__1cJLoadBNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSSetupItableClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cLeAXRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%jni_NewLocalRef: jni.o; +text: .text%__1cKstoreFNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHMatcherQinline_cache_reg6F_nHOptoRegEName__; +text: .text%__1cLloadSSFNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapJppdupswap6Mipkc_v_; +text: .text%__1cGOopMapPset_derived_oop6MnHOptoRegEName_ii2_v_; +text: .text%__1cOimmI_32_63OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cLOptoRuntimebAresolve_opt_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cPClassFileParserbEparse_constant_pool_long_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cLConvF2DNodeGOpcode6kM_i_; +text: .text%__1cIMulINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cPconvL2I_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitOset_pair_local6MipnENode__v_: parse2.o; +text: .text%__1cFStateM_sub_Op_AndI6MpknENode__v_; +text: .text%__1cPClassFileParserbNparse_classfile_inner_classes_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__H_; +text: .text%__1cICodeHeapMmax_capacity6kM_I_; +text: .text%__1cHciKlassGloader6M_pnHoopDesc__: ciTypeArrayKlass.o; +text: .text%__1cKloadUBNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%jni_DeleteGlobalRef: jni.o; +text: .text%__1cJLoadCNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOPhaseIdealLoopLdo_split_if6MpnENode__v_; +text: .text%__1cRsubI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJcmpOpOperKless_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cNaddI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKTypeOopPtrSmake_from_constant6FpnIciObject__pk0_; +text: .text%__1cJArrayDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cMURShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%jni_GetObjectArrayElement: jni.o; +text: .text%__1cRmulI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQSystemDictionaryRpreloaded_oops_do6FpnKOopClosure__v_; +text: .text%__1cNloadConI0NodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cNThreadServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cKCompiledICSset_to_monomorphic6MrknOCompiledICInfo__v_; +text: .text%__1cMFlatProfilerHoops_do6FpnKOopClosure__v_; +text: .text%__1cNMemoryServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cIUniverseHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cOJNIHandleBlockMweak_oops_do6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cQVMOperationQdDueueHoops_do6MpnKOopClosure__v_; +text: .text%__1cKManagementHoops_do6FpnKOopClosure__v_; +text: .text%__1cSObjectSynchronizerHoops_do6FpnKOopClosure__v_; +text: .text%__1cKJNIHandlesHoops_do6FpnKOopClosure__v_; +text: .text%__1cIVMThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollectorXoops_do_for_all_threads6FpnKOopClosure__v_; +text: .text%__1cQPlaceholderTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cKJNIHandlesMweak_oops_do6FpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cXJvmtiCurrentBreakpointsHoops_do6FpnKOopClosure__v_; +text: .text%__1cLJvmtiExportHoops_do6FpnKOopClosure__v_; +text: .text%__1cNstoreImmINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPlocal_vsnprintf6FpcIpkcpv_i_; +text: .text%JVM_GetCPMethodModifiers; +text: .text%__1cIModLNodeGOpcode6kM_i_; +text: .text%__1cNSCMemProjNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cHnmethodNscope_desc_at6MpCi_pnJScopeDesc__; +text: .text%__1cTcompareAndSwapLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIAndLNodeGmul_id6kM_pknEType__: classes.o; +text: .text%jni_SetLongField: jni.o; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: multnode.o; +text: .text%__1cIAndLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%jio_vsnprintf; +text: .text%__1cLRethrowNodeEhash6kM_I_: classes.o; +text: .text%__1cWCallLeafNoFPDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsalI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRsalI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWCallLeafNoFPDirectNodeRis_safepoint_node6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerDjmp6MnHAddress__v_; +text: .text%__1cIimmLOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConLNodeFclone6kM_pnENode__; +text: .text%jio_snprintf; +text: .text%__1cTCallDynamicJavaNodeEhash6kM_I_: callnode.o; +text: .text%__1cNSignatureInfoJdo_double6M_v_: frame.o; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: subnode.o; +text: .text%__1cMstoreSSINodeHis_Copy6kM_I_: ad_i486_misc.o; +text: .text%__1cYmulI_imm_RShift_highNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQsalL_eReg_CLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLConvL2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIMulLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOjmpLoopEndNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOmulIS_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNinstanceKlassPadd_implementor6MpnMklassOopDesc__v_; +text: .text%__1cMdecI_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompiledVFrameEcode6kM_pnHnmethod__; +text: .text%__1cOleaPIdxOffNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReturnNode2t6MpnENode_2222_v_; +text: .text%__1cKReturnNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQComputeCallStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cPGlobalTLABStatsKinitialize6M_v_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJset_clock6Fx_v_; +text: .text%__1cRLowMemoryDetectorRdetect_low_memory6F_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersbBupdate_counters_from_policy6M_v_; +text: .text%__1cSReferenceProcessorOprocess_phase16MppnHoopDesc_pnPReferencePolicy_pnRBoolObjectClosure_pnKOopClosure_pnLVoidClosure__v_; +text: .text%__1cSReferenceProcessorbBenqueue_discovered_reflists6MppnHoopDesc__v_; +text: .text%__1cSReferenceProcessorbDenqueue_discovered_references6M_i_; +text: .text%__1cWThreadLocalAllocBufferQresize_all_tlabs6F_v_; +text: .text%__1cUParallelScavengeHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cPGCMemoryManagerIgc_begin6M_v_; +text: .text%__1cPGCMemoryManagerGgc_end6M_v_; +text: .text%__1cUParallelScavengeHeapTensure_parseability6M_v_; +text: .text%__1cUParallelScavengeHeapOfill_all_tlabs6M_v_; +text: .text%__1cNMemoryServiceStrack_memory_usage6F_v_; +text: .text%__1cNMemoryServiceIgc_begin6Fi_v_; +text: .text%__1cNMemoryServiceGgc_end6Fi_v_; +text: .text%__1cXTraceMemoryManagerStats2t6Mi_v_; +text: .text%__1cXTraceMemoryManagerStats2T6M_v_; +text: .text%__1cQSystemDictionaryHoops_do6FpnKOopClosure__v_; +text: .text%__1cRaddL_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKDictionaryHoops_do6MpnKOopClosure__v_; +text: .text%__1cXjava_lang_ref_ReferenceRpending_list_addr6F_ppnHoopDesc__; +text: .text%__1cUParallelScavengeHeapQresize_all_tlabs6M_v_; +text: .text%__1cVLoaderConstraintTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cUParallelScavengeHeapPupdate_counters6M_v_; +text: .text%__1cNCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersPupdate_counters6M_v_; +text: .text%__1cKPSYoungGenPupdate_counters6M_v_; +text: .text%__1cNCollectedHeapQresize_all_tlabs6M_v_; +text: .text%__1cTDerivedPointerTableFclear6F_v_; +text: .text%__1cNCollectedHeapOfill_all_tlabs6M_v_; +text: .text%__1cQLRUMaxHeapPolicy2t6M_v_; +text: .text%__1cTDerivedPointerTablePupdate_pointers6F_v_; +text: .text%__1cWThreadLocalAllocBufferbFaccumulate_statistics_before_gc6F_v_; +text: .text%__1cSReferenceProcessorQprocess_phaseJNI6M_v_; +text: .text%__1cSReferenceProcessorbDprocess_discovered_references6M_v_; +text: .text%__1cJOopMapSet2t6M_v_; +text: .text%__1cIVMThreadHexecute6FpnMVM_Operation__v_; +text: .text%__1cRInlineCacheBufferIis_empty6F_i_; +text: .text%__1cUSafepointSynchronizeRis_cleanup_needed6F_i_; +text: .text%__1cOPhaseIdealLoopUsplit_if_with_blocks6MrnJVectorSet_rnKNode_Stack__v_; +text: .text%__1cIMachOperFscale6kM_i_; +text: .text%__1cMtlsLoadPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachOperNconstant_disp6kM_i_; +text: .text%__1cPVM_GC_OperationQgc_count_changed6kM_i_; +text: .text%__1cPVM_GC_OperationZacquire_pending_list_lock6M_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constantPoolKlass.o; +text: .text%__1cQinstanceRefKlassZacquire_pending_list_lock6FpnJBasicLock__v_; +text: .text%__1cQinstanceRefKlassbKrelease_and_notify_pending_list_lock6FipnJBasicLock__v_; +text: .text%__1cPVM_GC_OperationNdoit_prologue6M_i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constantPoolKlass.o; +text: .text%__1cPVM_GC_OperationbKrelease_and_notify_pending_list_lock6M_v_; +text: .text%__1cIAndLNodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIAndLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cURethrowExceptionNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cIAndLNodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cJCodeCacheXmark_for_deoptimization6FpnMklassOopDesc__i_; +text: .text%__1cRmulI_eReg_immNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cNmodI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUSafepointSynchronizeFbegin6F_v_; +text: .text%__1cORuntimeServicebDrecord_safepoint_synchronized6F_v_; +text: .text%__1cCosbCmake_polling_page_unreadable6F_v_; +text: .text%__1cUSafepointSynchronizeQdo_cleanup_tasks6F_v_; +text: .text%__1cORuntimeServiceWrecord_safepoint_begin6F_v_; +text: .text%__1cSObjectSynchronizerVdeflate_idle_monitors6F_v_; +text: .text%__1cJloadFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMCounterDecayFdecay6F_v_; +text: .text%__1cTAbstractInterpreterRnotice_safepoints6F_v_; +text: .text%__1cONMethodSweeperFsweep6F_v_; +text: .text%__1cRInlineCacheBufferUupdate_inline_caches6F_v_; +text: .text%__1cUSafepointSynchronizeDend6F_v_; +text: .text%__1cORuntimeServiceUrecord_safepoint_end6F_v_; +text: .text%__1cTAbstractInterpreterRignore_safepoints6F_v_; +text: .text%__1cCosbAmake_polling_page_readable6F_v_; +text: .text%__1cURethrowExceptionNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOCompiledRFrameEinit6M_v_; +text: .text%__1cGvframeDtop6kM_p0_; +text: .text%__1cOCompilerOraclePshould_break_at6FnMmethodHandle__i_; +text: .text%__1cJOopMapSetJheap_size6kM_i_; +text: .text%__1cJOopMapSetHcopy_to6MpC_v_; +text: .text%__1cRAbstractAssemblerOcode_fill_byte6F_i_; +text: .text%__1cKCodeBufferJcopy_code6MpnICodeBlob__v_; +text: .text%__1cNSignatureInfoJdo_double6M_v_: bytecode.o; +text: .text%__1cICodeBlobWfix_relocation_at_move6Mi_v_; +text: .text%JVM_DoPrivileged; +text: .text%__1cVPatchingRelocIteratorIpostpass6M_v_; +text: .text%__1cVPatchingRelocIteratorHprepass6M_v_; +text: .text%__1cICodeBlobPallocation_size6FpnKCodeBuffer_ii_I_; +text: .text%__1cNRelocIteratorMcreate_index6FpnKCodeBuffer_pnJrelocInfo_4_4_; +text: .text%__1cICodeBlob2t6MpkcpnKCodeBuffer_iiipnJOopMapSet_i_v_; +text: .text%__1cKCodeBufferPcopy_relocation6MpnICodeBlob__v_; +text: .text%__1cIAddLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cRPrivilegedElementKinitialize6MpnMvframeStream_pnHoopDesc_p0pnGThread__v_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_base6MnITosState_ppCi_v_; +text: .text%__1cZInterpreterMacroAssemblerKverify_FPU6MinITosState__v_; +text: .text%__1cNmodL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cURethrowExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstoreFNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQVMOperationQdDueueDadd6MpnMVM_Operation__i_; +text: .text%__1cJEventMark2t6MpkcE_v_: vmThread.o; +text: .text%__1cGThreadMget_priority6Fkpk0_nOThreadPriority__; +text: .text%__1cIVMThreadSevaluate_operation6MpnMVM_Operation__v_; +text: .text%__1cQVMOperationQdDueueGunlink6MpnMVM_Operation__v_; +text: .text%__1cQVMOperationQdDueueOqueue_add_back6MipnMVM_Operation__v_; +text: .text%__1cMVM_OperationIevaluate6M_v_; +text: .text%__1cCosTget_native_priority6FkpknGThread_pi_nIOSReturn__; +text: .text%__1cMVM_OperationSset_calling_thread6MpnGThread_nOThreadPriority__v_; +text: .text%__1cQVMOperationQdDueueGinsert6MpnMVM_Operation_2_v_; +text: .text%__1cCosMget_priority6FkpknGThread_rnOThreadPriority__nIOSReturn__; +text: .text%__1cScompI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: jvm.o; +text: .text%__1cOcompI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cGGCTask2t6Mn0AEKindEkind__v_; +text: .text%__1cFParseGdo_new6M_v_; +text: .text%__1cLGCTaskQdDueue2t6Mi_v_; +text: .text%__1cJloadFNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNJvmtiGCMarker2t6Mi_v_; +text: .text%__1cNJvmtiGCMarker2T6M_v_; +text: .text%__1cKPSScavengeXshould_attempt_scavenge6F_i_; +text: .text%__1cUWaitForBarrierGCTask2t6Mi_v_; +text: .text%__1cUPSAdaptiveSizePolicyPupdate_averages6MiII_v_; +text: .text%__1cNBarrierGCTaskIdestruct6M_v_; +text: .text%__1cNBarrierGCTaskOdo_it_internal6MpnNGCTaskManager_I_v_; +text: .text%__1cGGCTaskIdestruct6M_v_; +text: .text%__1cLGCTaskQdDueueGcreate6F_p0_; +text: .text%__1cKPSYoungGenLswap_spaces6M_v_; +text: .text%__1cKPSYoungGenNresize_spaces6MII_v_; +text: .text%__1cKPSScavengeQinvoke_no_policy6Fpi_i_; +text: .text%__1cLGCTaskQdDueueHenqueue6Mp0_v_; +text: .text%__1cKPSYoungGenRresize_generation6MII_i_; +text: .text%__1cKPSYoungGenGresize6MII_v_; +text: .text%__1cZSerialOldToYoungRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cUParallelScavengeHeapQresize_young_gen6MII_v_; +text: .text%__1cZSerialOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cNGCTaskManagerIadd_list6MpnLGCTaskQdDueue__v_; +text: .text%__1cNGCTaskManagerVrelease_all_resources6M_v_; +text: .text%__1cUWaitForBarrierGCTaskEname6M_pc_: gcTaskManager.o; +text: .text%__1cUWaitForBarrierGCTaskGcreate6F_p0_; +text: .text%__1cSPSPromotionManagerMpre_scavenge6F_v_; +text: .text%__1cSPSPromotionManagerNpost_scavenge6F_v_; +text: .text%__1cPVM_GC_OperationNdoit_epilogue6M_v_; +text: .text%__1cSPSPromotionManagerbBvm_thread_promotion_manager6F_p0_; +text: .text%__1cUPSAdaptiveSizePolicyUminor_collection_end6MnHGCCauseFCause__v_; +text: .text%__1cSCardTableExtensionRscavenge_contents6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager__v_; +text: .text%__1cUPSAdaptiveSizePolicyWminor_collection_begin6M_v_; +text: .text%__1cNMonitorSupplyHrelease6FpnHMonitor__v_; +text: .text%__1cNMonitorSupplyHreserve6F_pnHMonitor__; +text: .text%__1cUWaitForBarrierGCTaskIwait_for6M_v_; +text: .text%__1cUWaitForBarrierGCTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cUWaitForBarrierGCTaskIdestruct6M_v_; +text: .text%__1cUWaitForBarrierGCTaskHdestroy6Fp0_v_; +text: .text%__1cUPSAdaptiveSizePolicybPcompute_survivor_space_size_and_threshold6MiiI_i_; +text: .text%__1cHThreadsZcreate_thread_roots_tasks6FpnLGCTaskQdDueue__v_; +text: .text%__1cPGlobalTLABStatsHpublish6M_v_; +text: .text%__1cMindirectOperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cMindirectOperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cbDVM_ParallelGCFailedAllocation2t6MIiiI_v_; +text: .text%__1cJloadSNodeFreloc6kM_i_; +text: .text%__1cMrep_stosNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateN_sub_Op_LoadS6MpknENode__v_; +text: .text%__1cQorI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQshrI_eReg_CLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cYDebugInformationRecorderIpcs_size6M_i_; +text: .text%__1cNIdealLoopTreePiteration_split6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cHnmethodPscopes_pcs_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodQscopes_data_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodJstub_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodOexception_size6kM_i_: nmethod.o; +text: .text%__1cIMulLNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cLOopRecorderIoop_size6M_i_; +text: .text%__1cYDebugInformationRecorderJdata_size6M_i_; +text: .text%__1cHnmethodKtotal_size6kM_i_; +text: .text%__1cVCallRuntimeDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNstoreImmINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cINodeHash2t6Mp0_v_; +text: .text%__1cOPhaseTransform2t6Mp0nFPhaseLPhaseNumber__v_; +text: .text%__1cLPhaseValues2t6Mp0_v_; +text: .text%__1cZCallInterpreterDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescbDbuild_interpreter_method_data6FnMmethodHandle_pnGThread__v_; +text: .text%__1cOJNIHandleBlockRrebuild_free_list6M_v_; +text: .text%__1cFStateM_sub_Op_Goto6MpknENode__v_; +text: .text%__1cFDictIFreset6MpknEDict__v_; +text: .text%__1cIPhaseCFGQFind_Inner_Loops6M_v_; +text: .text%__1cMPhaseChaitinRRegister_Allocate6M_v_; +text: .text%__1cIPhaseCFGJbuild_cfg6M_I_; +text: .text%__1cIPhaseCFG2t6MpnFArena_pnIRootNode_rnHMatcher__v_; +text: .text%__1cHMatcherZnumber_of_saved_registers6F_i_; +text: .text%__1cMPhaseChaitin2t6MIrnIPhaseCFG_rnHMatcher__v_; +text: .text%__1cOCompileWrapper2t6MpnHCompile__v_; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: cfgnode.o; +text: .text%__1cETypeKInitialize6FpnHCompile__v_; +text: .text%__1cIPhaseCFGKDominators6M_v_; +text: .text%__1cHCompileEInit6Mi_v_; +text: .text%__1cIPhaseCFGDDFS6MpnGTarjan__I_; +text: .text%__1cIPhaseCFGVschedule_pinned_nodes6MrnJVectorSet__v_; +text: .text%__1cIPhaseCFGOschedule_early6MrnJVectorSet_rnJNode_List_rnLBlock_Array__i_; +text: .text%__1cXPhaseAggressiveCoalesceGverify6M_v_: coalesce.o; +text: .text%__1cWNode_Backward_Iterator2t6MpnENode_rnJVectorSet_rnJNode_List_rnLBlock_Array__v_; +text: .text%__1cIPhaseCFGNschedule_late6MrnJVectorSet_rnJNode_List_rnNGrowableArray4CI___v_; +text: .text%__1cIPhaseCFGQGlobalCodeMotion6MrnHMatcher_IrnJNode_List__v_; +text: .text%__1cVExceptionHandlerTable2t6Mi_v_; +text: .text%__1cGTarjanIsetdepth6MI_v_; +text: .text%__1cHCompileICode_Gen6M_v_; +text: .text%__1cIPhaseCFGYEstimate_Block_Frequency6M_v_; +text: .text%__1cXPhaseAggressiveCoalesceNinsert_copies6MrnHMatcher__v_; +text: .text%__1cMPhaseChaitinbGstretch_base_pointer_live_ranges6MpnMResourceArea__i_; +text: .text%__1cNPhaseRegAllocTpd_preallocate_hook6M_v_; +text: .text%__1cQorI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cYDebugInformationRecorder2t6MpnLOopRecorder__v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: matcher.o; +text: .text%__1cHMatcherUvalidate_null_checks6M_v_; +text: .text%__1cHMatcherPinit_spill_mask6MpnENode__v_; +text: .text%__1cHMatcherTFixup_Save_On_Entry6M_v_; +text: .text%__1cHMatcherVinit_first_stack_mask6M_v_; +text: .text%__1cHMatcherFmatch6M_v_; +text: .text%__1cHCompileOcompute_old_SP6M_nHOptoRegEName__; +text: .text%__1cHMatcher2t6MrnJNode_List__v_; +text: .text%__1cUDebugInfoWriteStream2t6MpnYDebugInformationRecorder_i_v_; +text: .text%__1cOleaPIdxOffNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cHMatcherLreturn_addr6kM_nHOptoRegEName__; +text: .text%__1cHCompilebBregister_library_intrinsics6M_v_; +text: .text%__1cMURShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMPhaseChaitinRbuild_ifg_virtual6M_v_; +text: .text%__1cIAndLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJPhaseLive2t6MrknIPhaseCFG_rnILRG_List_pnFArena__v_; +text: .text%__1cIPhaseIFG2t6MpnFArena__v_; +text: .text%__1cMPhaseChaitinGde_ssa6M_v_; +text: .text%__1cFArenaNmove_contents6Mp0_1_; +text: .text%__1cNPhaseRegAlloc2t6MIrnIPhaseCFG_rnHMatcher_pF_v_v_; +text: .text%__1cFArena2t6MI_v_; +text: .text%__1cWsize_exception_handler6F_I_; +text: .text%__1cHCompileYinit_scratch_buffer_blob6M_v_; +text: .text%__1cOCompileWrapper2T6M_v_; +text: .text%__1cUPSAdaptiveSizePolicyZdecay_supplemental_growth6Mi_v_; +text: .text%__1cNPhasePeepholeMdo_transform6M_v_; +text: .text%__1cHCompileTframe_size_in_words6kM_i_; +text: .text%__1cNPhasePeephole2T6M_v_; +text: .text%__1cNPhasePeephole2t6MpnNPhaseRegAlloc_rnIPhaseCFG__v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: buildOopMap.o; +text: .text%__1cHCompileMBuildOopMaps6M_v_; +text: .text%__1cWemit_exception_handler6FrnKCodeBuffer__v_; +text: .text%__1cLdo_liveness6FpnNPhaseRegAlloc_pnIPhaseCFG_pnKBlock_List_ipnFArena_pnEDict__v_: buildOopMap.o; +text: .text%__1cMPhaseChaitin2T6M_v_; +text: .text%__1cMPhaseChaitinbApost_allocate_copy_removal6M_v_; +text: .text%__1cIPhaseCFGLRemoveEmpty6M_v_; +text: .text%__1cNPhaseRegAllocPalloc_node_regs6Mi_v_; +text: .text%__1cMPhaseChaitinMfixup_spills6M_v_; +text: .text%__1cHCompileGOutput6M_v_; +text: .text%__1cHCompileQShorten_branches6MpnFLabel_ri333_v_; +text: .text%__1cHCompileLFill_buffer6M_v_; +text: .text%__1cHCompileTFillExceptionTables6MIpI1pnFLabel__v_; +text: .text%__1cHCompileRScheduleAndBundle6M_v_; +text: .text%__1cKCodeBufferOrelocate_stubs6M_v_; +text: .text%__1cUPSAdaptiveSizePolicybPeden_increment_with_supplement_aligned_up6MI_I_; +text: .text%__1cUPSAdaptiveSizePolicyOeden_increment6MII_I_; +text: .text%__1cOMachPrologNodeFreloc6kM_i_; +text: .text%__1cUPSAdaptiveSizePolicyVadjust_for_throughput6MipI1_v_; +text: .text%__1cWImplicitExceptionTableIset_size6MI_v_; +text: .text%__1cJPhaseLive2T6M_v_; +text: .text%__1cUPSAdaptiveSizePolicybDcompute_generation_free_space6MIIIIIIIi_v_; +text: .text%__1cIPSOldGenMmax_gen_size6M_I_: psOldGen.o; +text: .text%__1cUPSAdaptiveSizePolicybHclear_generation_free_space_flags6M_v_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: loopnode.o; +text: .text%__1cUPSAdaptiveSizePolicyQdecaying_gc_cost6kM_d_; +text: .text%__1cHCompileYinit_scratch_locs_memory6M_v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: methodDataKlass.o; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_words6FpnNmethodOopDesc__i_; +text: .text%__1cRmethodDataOopDescPpost_initialize6MpnOBytecodeStream__v_; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_bytes6FpnNmethodOopDesc__i_; +text: .text%__1cRmethodDataOopDescKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: methodDataOop.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: methodDataKlass.o; +text: .text%__1cNmulL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbDVM_ParallelGCFailedAllocationEname6kM_pkc_: vm_operations.o; +text: .text%__1cbDVM_ParallelGCFailedAllocationEdoit6M_v_; +text: .text%__1cKoopFactoryOnew_methodData6FnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: methodDataKlass.o; +text: .text%__1cKPSScavengeGinvoke6Fpi_v_; +text: .text%__1cUParallelScavengeHeapTfailed_mem_allocate6MpiIii_pnIHeapWord__; +text: .text%__1cUPSAdaptiveSizePolicyOshould_full_GC6MI_i_; +text: .text%__1cPmethodDataKlassIallocate6MnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cOcmpD_cc_P6NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: loopTransform.o; +text: .text%__1cKklassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cHRetNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateO_sub_Op_Return6MpknENode__v_; +text: .text%__1cLlog2_intptr6Fi_i_: divnode.o; +text: .text%__1cbLtransform_int_divide_to_long_multiply6FpnIPhaseGVN_pnENode_i_3_: divnode.o; +text: .text%__1cFStateM_sub_Op_ConL6MpknENode__v_; +text: .text%__1cSmembar_acquireNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cNcmovI_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cHRetNodeFreloc6kM_i_; +text: .text%__1cOClearArrayNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciObjectOis_method_data6M_i_: ciInstance.o; +text: .text%__1cIciObjectJis_method6M_i_: ciInstance.o; +text: .text%__1cNaddP_eRegNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cIConFNodeGOpcode6kM_i_; +text: .text%__1cLRuntimeStubHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cJCodeCacheNalive_nmethod6FpnICodeBlob__pnHnmethod__; +text: .text%__1cIGraphKitNallocate_heap6MpnENode_222pknITypeFunc_pC22ipknKTypeOopPtr__2_; +text: .text%__1cPciInstanceKlassbBcompute_shared_has_subklass6M_i_; +text: .text%__1cOLibraryCallKitNtry_to_inline6M_i_; +text: .text%__1cQLibraryIntrinsicIgenerate6MpnIJVMState__2_; +text: .text%__1cLRuntimeStubbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cOcompP_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_CmpL6MpknENode__v_; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_: library_call.o; +text: .text%__1cJAssemblerDjcc6Mn0AJCondition_pCnJrelocInfoJrelocType__v_; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopTransform.o; +text: .text%__1cKCompiledICSset_ic_destination6MpC_v_; +text: .text%__1cLConvL2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cHBitDataKis_BitData6M_i_: ciMethodData.o; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_next6MnITosState_i_v_; +text: .text%__1cRxorI_eReg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNaddL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPshlI_eReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cGThreadOis_interrupted6Fp0i_i_; +text: .text%__1cSmembar_releaseNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNdecI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNandL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQshrI_eReg_CLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNdecI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJLoadDNodeGOpcode6kM_i_; +text: .text%__1cMstoreSSINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopOplace_near_use6kMpnENode__2_; +text: .text%__1cSInterpreterRuntimeOprofile_method6FpnKJavaThread_pC_i_; +text: .text%__1cOcmpD_cc_P6NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadLNodeFreloc6kM_i_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: multnode.o; +text: .text%__1cRxorI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLBoxLockNodeEhash6kM_I_: classes.o; +text: .text%__1cIAddLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRaddI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__: ad_i486_misc.o; +text: .text%__1cJlabelOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cNsubL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulLNodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cIMulLNodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cTconvF2I_reg_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: loopopts.o; +text: .text%__1cXmembar_acquire_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescThas_native_function6kM_i_; +text: .text%__1cRxorI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateR_sub_Op_SafePoint6MpknENode__v_; +text: .text%__1cMciMethodDataStrap_recompiled_at6MpnLProfileData__i_; +text: .text%__1cSsafePoint_pollNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSsafePoint_pollNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSsafePoint_pollNodeFreloc6kM_i_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlass.o; +text: .text%__1cPpoll_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cLOptoRuntimeRmultianewarray1_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cNmethodOopDescWis_vanilla_constructor6kM_i_; +text: .text%__1cKCodeBuffer2t6MpCi_v_; +text: .text%__1cNandI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSshlL_eReg_1_31NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: memnode.o; +text: .text%__1cOmulIS_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQsalI_eReg_CLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopKclone_loop6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cQsalL_eReg_CLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cXroundDouble_mem_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRandI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKciTypeFlowFBlockQset_private_copy6Mi_v_; +text: .text%__1cLimmI_16OperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cHi2sNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cHi2sNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRxorI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvI2L_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateL_sub_Op_OrI6MpknENode__v_; +text: .text%JVM_GetClassNameUTF; +text: .text%__1cSshrL_eReg_1_31NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKStoreDNodeGOpcode6kM_i_; +text: .text%__1cNcmovP_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: loopTransform.o; +text: .text%__1cIXorINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cOcmpD_cc_P6NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNmaxI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFframeMpd_gc_epilog6M_v_; +text: .text%__1cRframe_gc_epilogue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeLgc_epilogue6M_v_; +text: .text%__1cRframe_gc_prologue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeLgc_prologue6M_v_; +text: .text%__1cHTypePtrEmake6FnETypeFTYPES_n0ADPTR_i_pk0_; +text: .text%__1cIAddLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%JVM_FindLoadedClass; +text: .text%__1cIMulFNodeGOpcode6kM_i_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: vmThread.o; +text: .text%__1cCosJyield_all6Fi_v_; +text: .text%__1cNstoreImmINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJBytecodesRspecial_length_at6FpC_i_; +text: .text%__1cOstackSlotLOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cRaddI_eReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvI2DNodeGOpcode6kM_i_; +text: .text%__1cTcompareAndSwapLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSloadL_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUjmpLoopEnd_shortNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cUjmpLoopEnd_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMjniIdMapBaseHoops_do6MpnKOopClosure__v_; +text: .text%__1cNmodL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEfrom6F_pnMRegisterImpl__; +text: .text%__1cIjniIdMapHoops_do6MpnKOopClosure__v_; +text: .text%__1cTDerivedPointerTableDadd6FppnHoopDesc_3_v_; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: classes.o; +text: .text%__1cPadd_derived_oop6FppnHoopDesc_2_v_: oopMap.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: loopnode.o; +text: .text%__1cKstoreCNodeFreloc6kM_i_; +text: .text%__1cKRegionNodeJideal_reg6kM_I_: loopnode.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: loopnode.o; +text: .text%__1cMincI_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Copy6kM_I_: loopnode.o; +text: .text%__1cXconvI2L_reg_reg_zexNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJOopMapSetMgrow_om_data6M_v_; +text: .text%__1cScompP_mem_eRegNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKReflectionGinvoke6FnTinstanceKlassHandle_nMmethodHandle_nGHandle_inOobjArrayHandle_nJBasicType_4ipnGThread__pnHoopDesc__; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectIoverride6FpnHoopDesc__C_; +text: .text%__1cKReflectionDbox6FpnGjvalue_nJBasicType_pnGThread__pnHoopDesc__; +text: .text%__1cJAssemblerDret6Mi_v_; +text: .text%__1cTcmovII_reg_LEGTNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJAssemblerEcall6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cLBoxLockNode2t6Mi_v_; +text: .text%__1cIGraphKitMnext_monitor6M_i_; +text: .text%__1cTshrL_eReg_32_63NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cLOptoRuntimebBcomplete_monitor_enter_Type6F_pknITypeFunc__; +text: .text%__1cMdecI_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIMaxINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFStateQ_sub_Op_FastLock6MpknENode__v_; +text: .text%__1cPcmpFastLockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIGraphKitLshared_lock6MpnENode__pnMFastLockNode__; +text: .text%__1cQjava_lang_ThreadGthread6FpnHoopDesc__pnKJavaThread__; +text: .text%__1cKciTypeFlowLStateVectorJdo_aaload6MpnQciByteCodeStream__v_; +text: .text%__1cNaddI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cEUTF8Ounicode_length6Fpkc_i_; +text: .text%__1cPCallRuntimeNodeGOpcode6kM_i_; +text: .text%__1cFParseFBlockMadd_new_path6M_i_; +text: .text%__1cYcmpL_zero_flags_LEGTNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%JVM_FindClassFromClass; +text: .text%__1cOmangle_name_on6FpnMoutputStream_pnNsymbolOopDesc_ii_v_: nativeLookup.o; +text: .text%__1cIMulINodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cMdecI_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPshlI_eReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbDjava_lang_reflect_ConstructorFclazz6FpnHoopDesc__2_; +text: .text%__1cNSignatureInfoIdo_float6M_v_: frame.o; +text: .text%JVM_NewInstanceFromConstructor; +text: .text%__1cbDjava_lang_reflect_ConstructorPparameter_types6FpnHoopDesc__2_; +text: .text%__1cbDjava_lang_reflect_ConstructorEslot6FpnHoopDesc__i_; +text: .text%__1cLConvI2LNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKReflectionSinvoke_constructor6FpnHoopDesc_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cNminI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMorI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOmangle_name_on6FpnMoutputStream_pnNsymbolOopDesc__v_: nativeLookup.o; +text: .text%__1cNIdealLoopTreeMpolicy_align6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeNpolicy_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeSpolicy_range_check6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeQpolicy_peel_only6kMpnOPhaseIdealLoop__i_; +text: .text%__1cLloadSSFNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMtlsLoadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYmulI_imm_RShift_highNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFParseFBlockNstack_type_at6kMi_pknEType__; +text: .text%__1cJAssemblerGmovzxw6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cFStateN_sub_Op_LoadB6MpknENode__v_; +text: .text%__1cIMulLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOMacroAssemblerSload_unsigned_word6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cKstoreBNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cZCallInterpreterDirectNodeSalignment_required6kM_i_: ad_i486_misc.o; +text: .text%__1cZCallInterpreterDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNloadConL0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZCallInterpreterDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cTsarL_eReg_32_63NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cITemplateIbytecode6kM_nJBytecodesECode__; +text: .text%__1cLLShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMorI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLLShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: callnode.o; +text: .text%__1cNtestU_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMURShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLMachUEPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cWflagsReg_long_LTGEOperFclone6kM_pnIMachOper__; +text: .text%__1cIAddLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRsarI_eReg_immNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIJVMStateNmonitor_depth6kM_i_: graphKit.o; +text: .text%__1cWCallLeafNoFPDirectNodeKmethod_set6Mi_v_; +text: .text%__1cIregFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlassKlass.o; +text: .text%__1cHnmethodUnumber_of_dependents6kM_i_: nmethod.o; +text: .text%__1cNSingletonBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: node.o; +text: .text%lwp_mutex_init: os_solaris.o; +text: .text%__1cFParseLarray_store6MnJBasicType__v_; +text: .text%__1cLRShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLRethrowNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPsarI_eReg_1NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKcmpOpUOperEless6kM_i_: ad_i486_clone.o; +text: .text%__1cFParseMdo_checkcast6M_v_; +text: .text%__1cMloadConDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOPhaseIdealLoopVclone_up_backedge_goo6MpnENode_22_2_; +text: .text%__1cbACallCompiledJavaDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJStubQdDueueHrequest6Mi_pnEStub__; +text: .text%__1cJStubQdDueueGcommit6Mi_v_; +text: .text%__1cMLinkResolverbHlinktime_resolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnHoopDesc_pnNsymbolOopDesc__i_; +text: .text%__1cENodeHdel_out6Mp0_v_: doCall.o; +text: .text%__1cPfieldDescriptorUstring_initial_value6kMpnGThread__pnHoopDesc__; +text: .text%__1cKRegionNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%jni_NewString: jni.o; +text: .text%__1cQjava_lang_StringXcreate_oop_from_unicode6FpHipnGThread__pnHoopDesc__; +text: .text%__1cMincI_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHTypePtrFempty6kM_i_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_i_v_; +text: .text%__1cTshrL_eReg_32_63NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXmembar_acquire_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMMergeMemNodeIadr_type6kM_pknHTypePtr__: memnode.o; +text: .text%__1cITypeLongFwiden6kMpknEType__3_; +text: .text%__1cIplus_adr6FpnENode_i_1_: generateOptoStub.o; +text: .text%__1cIModINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQjava_lang_StringTcreate_oop_from_str6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cKoopFactoryNnew_charArray6FpkcpnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cScompP_mem_eRegNodeFreloc6kM_i_; +text: .text%__1cKciTypeFlowLStateVectorMdo_checkcast6MpnQciByteCodeStream__v_; +text: .text%__1cNSignatureInfoIdo_short6M_v_: frame.o; +text: .text%__1cMadjust_check6FpnENode_11iipnMPhaseIterGVN__v_: ifnode.o; +text: .text%__1cNmaxI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNtestU_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateP_sub_Op_ConvI2L6MpknENode__v_; +text: .text%__1cNdivL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJScopeDescGsender6kM_p0_; +text: .text%__1cOcompiledVFrameGsender6kM_pnGvframe__; +text: .text%__1cOstackSlotIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cQciTypeArrayKlassEmake6FnJBasicType__p0_; +text: .text%__1cLPhaseValuesKis_IterGVN6M_pnMPhaseIterGVN__: phaseX.o; +text: .text%__1cQsalI_eReg_CLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQciTypeArrayKlassJmake_impl6FnJBasicType__p0_; +text: .text%__1cJloadBNodeFreloc6kM_i_; +text: .text%__1cOcmpD_cc_P6NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOleaPIdxOffNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodKlass.o; +text: .text%__1cKJavaThreadJframes_do6MpFpnFframe_pknLRegisterMap__v_v_; +text: .text%__1cSmembar_acquireNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cKEntryPoint2t6MpC11111111_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: compiledIC.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constMethodKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constMethodKlass.o; +text: .text%__1cQjava_lang_ThreadRget_thread_status6FpnHoopDesc__n0AMThreadStatus__; +text: .text%__1cRmulI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTtypeArrayKlassKlassIoop_size6kMpnHoopDesc__i_: typeArrayKlassKlass.o; +text: .text%__1cPstoreImmI16NodeFreloc6kM_i_; +text: .text%__1cFStateP_sub_Op_ConvL2I6MpknENode__v_; +text: .text%__1cLloadSSDNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIXorINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cYcmpL_zero_flags_LEGTNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNcmovI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOmulIS_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cVloadConL_low_onlyNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRsubI_eReg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: typeArrayKlass.o; +text: .text%__1cOcmpD_cc_P6NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceRefKlass.o; +text: .text%__1cObox_handleNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadFNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSmembar_acquireNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCompileBrokerNallocate_task6F_pnLCompileTask__; +text: .text%__1cNCompileBrokerTcreate_compile_task6FpnMCompileQdDueue_inMmethodHandle_i3ipkcii_pnLCompileTask__; +text: .text%__1cMCompileQdDueueDadd6MpnLCompileTask__v_; +text: .text%__1cLCompileTaskKinitialize6MinMmethodHandle_i1ipkcii_v_; +text: .text%jni_GetObjectClass: jni.o; +text: .text%__1cMCompileQdDueueDget6M_pnLCompileTask__; +text: .text%__1cSmembar_acquireNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLBoxLockNodeDcmp6kMrknENode__I_; +text: .text%__1cXmembar_acquire_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cPconvF2D_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMincI_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cSCompileTaskWrapper2t6MpnLCompileTask__v_; +text: .text%__1cCosPhint_no_preempt6F_v_; +text: .text%__1cUGenericGrowableArrayNraw_appendAll6Mpk0_v_; +text: .text%__1cFStateM_sub_Op_MulL6MpknENode__v_; +text: .text%__1cOstackSlotFOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cLCompileTaskEfree6M_v_; +text: .text%__1cFciEnv2t6MpnHJNIEnv__iii_v_; +text: .text%__1cSCompileTaskWrapper2T6M_v_; +text: .text%__1cPciObjectFactory2t6MpnFArena_i_v_; +text: .text%__1cNCompileBrokerJfree_task6FpnLCompileTask__v_; +text: .text%__1cRappend_interfaces6FnOobjArrayHandle_ripnPobjArrayOopDesc__v_; +text: .text%__1cNCompileBrokerVpush_jni_handle_block6F_v_; +text: .text%__1cTmembar_volatileNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFciEnvWget_method_from_handle6MpnI_jobject__pnIciMethod__; +text: .text%__1cSshrL_eReg_1_31NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFciEnv2T6M_v_; +text: .text%__1cNmodI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cNinstanceKlassbFlookup_method_in_all_interfaces6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cNCompileBrokerUpop_jni_handle_block6F_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: arrayKlass.o; +text: .text%__1cNaddP_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionaryRnumber_of_classes6F_i_; +text: .text%__1cSstring_compareNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIimmFOperJconstantF6kM_f_: ad_i486_clone.o; +text: .text%__1cOcompP_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cOMacroAssemblerFalign6Mi_v_; +text: .text%__1cOstackSlotPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cObox_handleNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOstackSlotPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLCodeletMark2t6MrpnZInterpreterMacroAssembler_pkcinJBytecodesECode__v_: interpreter.o; +text: .text%__1cSInterpreterCodeletKinitialize6MpkcinJBytecodesECode__v_; +text: .text%__1cNandI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cLloadSSFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmodL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQmulD_reg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIMinINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cNandI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cITemplateIgenerate6MpnZInterpreterMacroAssembler__v_; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_and_dispatch6MpnITemplate_nITosState__v_; +text: .text%__1cITemplateKinitialize6MinITosState_1pFi_vi_v_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFi_vi_v_; +text: .text%JVM_FindClassFromClassLoader; +text: .text%__1cPshrI_eReg_1NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cHi2bNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMmatch_option6FpknMJavaVMOption_pkcp4_i_: arguments.o; +text: .text%__1cNnegI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSstring_compareNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOPhaseTransform2t6MpnFArena_nFPhaseLPhaseNumber__v_; +text: .text%__1cHCompileKinit_start6MpnJStartNode__v_; +text: .text%__1cMstoreSSINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLPhaseValues2t6MpnFArena_I_v_; +text: .text%__1cINodeHash2t6MpnFArena_I_v_; +text: .text%__1cINodeHashIround_up6FI_I_; +text: .text%__1cbACallCompiledJavaDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKScopeValueJread_from6FpnTDebugInfoReadStream__p0_; +text: .text%JVM_IHashCode; +text: .text%lwp_cond_init: os_solaris.o; +text: .text%__1cNCollectedHeapMobj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: jvm.o; +text: .text%__1cIVMThreadMis_VM_thread6kM_i_: vmThread.o; +text: .text%__1cOcompiledVFrameScreate_stack_value6kMpnKScopeValue__pnKStackValue__; +text: .text%__1cGThreadOis_Java_thread6kM_i_: vmThread.o; +text: .text%__1cKstoreBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorCto6F_pnMRegisterImpl__; +text: .text%__1cKDictionarybAis_valid_protection_domain6MiInMsymbolHandle_nGHandle_2_i_; +text: .text%__1cPconvI2L_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLStrCompNodeKmatch_edge6kMI_I_; +text: .text%__1cZInterpreterMacroAssemblerEpush6MnITosState__v_; +text: .text%__1cRtestI_reg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachNodeJis_MachIf6kM_pknKMachIfNode__: ad_i486_misc.o; +text: .text%__1cNmaxI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_AddL6MpknENode__v_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_i_v_; +text: .text%__1cFForteNregister_stub6FpkcpC3_v_; +text: .text%__1cXroundDouble_mem_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFVTuneNregister_stub6FpkcpC3_v_; +text: .text%__1cENodeEgetd6kM_d_; +text: .text%__1cNSignatureInfoHdo_char6M_v_: frame.o; +text: .text%__1cMstoreSSPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: nmethod.o; +text: .text%__1cFStateM_sub_Op_AndL6MpknENode__v_; +text: .text%__1cKConv2BNodeGOpcode6kM_i_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii1i_v_; +text: .text%JVM_GetClassLoader; +text: .text%__1cRmulI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorQset_entry_points6MnJBytecodesECode__v_; +text: .text%__1cbCAbstractInterpreterGeneratorWset_short_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cOjmpLoopEndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateW_sub_Op_CountedLoopEnd6MpknENode__v_; +text: .text%__1cXconvI2L_reg_reg_zexNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMinINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSCompareAndSwapNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKBinaryNodeGOpcode6kM_i_; +text: .text%__1cUmembar_cpu_orderNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsalL_eReg_CLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNSCMemProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFStateW_sub_Op_MemBarCPUOrder6MpknENode__v_; +text: .text%__1cFStateO_sub_Op_Binary6MpknENode__v_; +text: .text%__1cNSignatureInfoIdo_float6M_v_: bytecode.o; +text: .text%__1cOMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cZInterpreterMacroAssemblerZcheck_and_handle_popframe6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableKtransition6FnITosState_1_v_; +text: .text%__1cPClassFileParserbGparse_constant_pool_double_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cRaddI_eReg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadDNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSleaP_eReg_immINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOjmpLoopEndNodeOis_pc_relative6kM_i_: ad_i486_misc.o; +text: .text%__1cOjmpLoopEndNodeTmay_be_short_branch6kM_i_: ad_i486_misc.o; +text: .text%__1cIModINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRmulI_eReg_immNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cKNativeJumpbEcheck_verified_entry_alignment6FpC1_v_; +text: .text%__1cTbasictype2arraycopy6FnJBasicType_i_pC_; +text: .text%__1cFStateU_sub_Op_CallLeafNoFP6MpknENode__v_; +text: .text%__1cZInterpreterMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cLOptoRuntimeOarraycopy_Type6F_pknITypeFunc__; +text: .text%__1cMMachCallNodeXreturns_float_or_double6kM_i_; +text: .text%__1cOLibraryCallKitQinline_arraycopy6M_i_; +text: .text%__1cOMacroAssemblerOcall_VM_helper6MpnMRegisterImpl_pCii_v_; +text: .text%__1cWCallLeafNoFPDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTcmovII_reg_LTGENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cbACallCompiledJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: compiledIC.o; +text: .text%__1cNLocationValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cILocation2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: location.o; +text: .text%__1cNLocationValueLis_location6kM_i_: debugInfo.o; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_2_v_; +text: .text%__1cFParseRarray_store_check6M_v_; +text: .text%__1cQshrI_eReg_CLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNCompileBrokerTis_compile_blocking6FnMmethodHandle_i_i_; +text: .text%__1cNCompileBrokerRassign_compile_id6FnMmethodHandle_i_I_; +text: .text%__1cNCompileBrokerZcompilation_is_prohibited6FnMmethodHandle_i_i_; +text: .text%__1cNCompileBrokerTis_not_compile_only6FnMmethodHandle__i_; +text: .text%__1cNsubL_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cQsalL_eReg_CLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_prolog6MnITosState_i_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_epilog6MnITosState_i_v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constMethodKlass.o; +text: .text%__1cFMutex2t6Mipkci_v_; +text: .text%__1cJAssemblerLemit_farith6Miii_v_; +text: .text%__1cIciMethodJhas_loops6kM_i_; +text: .text%__1cRtestI_reg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJStartNodeJideal_reg6kM_I_: callnode.o; +text: .text%__1cNCompileBrokerQset_last_compile6FpnOCompilerThread_nMmethodHandle_ii_v_; +text: .text%__1cNCompileBrokerOcheck_break_at6FnMmethodHandle_iii_i_; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: callnode.o; +text: .text%__1cJStartNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cKC2CompilerOcompile_method6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cNCompileBrokerZinvoke_compiler_on_method6FpnLCompileTask__v_; +text: .text%__1cNSignatureInfoIdo_short6M_v_: bytecode.o; +text: .text%__1cOCompiledRFrameKtop_method6kM_nMmethodHandle__: rframe.o; +text: .text%__1cOCompiledRFrameLis_compiled6kM_i_: rframe.o; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_: nmethod.o; +text: .text%__1cNminI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOCompiledRFrame2t6MnFframe_pnKJavaThread_kpnGRFrame__v_; +text: .text%__1cLAccessFlagsRatomic_clear_bits6Mi_v_; +text: .text%__1cMelapsedTimerDadd6M0_v_; +text: .text%__1cSmembar_releaseNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFciEnvPregister_method6MpnIciMethod_iiiiiipnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler_ii_v_; +text: .text%__1cFciEnvbOcheck_for_system_dictionary_modification6MpnIciMethod__v_; +text: .text%__1cNCompileBrokerScollect_statistics6FpnOCompilerThread_nMelapsedTimer_pnLCompileTask__v_; +text: .text%__1cFciEnvbUsystem_dictionary_modification_counter_changed6M_i_; +text: .text%__1cLConvI2FNodeGOpcode6kM_i_; +text: .text%__1cIciMethodQbreak_at_execute6M_i_; +text: .text%__1cQComputeCallStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cICodeHeapMinsert_after6MpnJFreeBlock_2_v_; +text: .text%__1cHnmFlagsFclear6M_v_; +text: .text%__1cHnmethodQcopy_scopes_data6MpCi_v_; +text: .text%__1cHnmethodSresolve_JNIHandles6M_v_; +text: .text%__1cVExceptionHandlerTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cHnmethod2t6MpnNmethodOopDesc_iiiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__v_; +text: .text%__1cHnmethod2n6FIi_pv_; +text: .text%__1cWImplicitExceptionTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cHnmethodLnew_nmethod6FnMmethodHandle_iiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__p0_; +text: .text%__1cFVTuneOcreate_nmethod6FpnHnmethod__v_; +text: .text%__1cLPcDescCache2t6M_v_; +text: .text%__1cFciEnvbFpost_compiled_method_load_event6MpnHnmethod__v_; +text: .text%__1cFciEnvVnum_inlined_bytecodes6kM_i_; +text: .text%__1cJCodeCacheGcommit6FpnICodeBlob__v_; +text: .text%__1cICodeBlobJcopy_oops6MppnI_jobject_i_v_; +text: .text%__1cYDebugInformationRecorderHcopy_to6MpnHnmethod__v_; +text: .text%__1cTcmovII_reg_LEGTNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cLOopRecorderHcopy_to6MpnICodeBlob__v_; +text: .text%__1cZCallDynamicJavaDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLOptoRuntimeInew_Type6F_pknITypeFunc__; +text: .text%__1cQorI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_3pnRJavaCallArguments_nGHandle_6_6_; +text: .text%__1cNsubL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitMnew_instance6MpnPciInstanceKlass__pnENode__; +text: .text%__1cHCompileUremove_useless_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cIciMethodRbuild_method_data6MnMmethodHandle__v_; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod_ii_v_; +text: .text%__1cHCompileWprint_compile_messages6M_v_; +text: .text%__1cQUnique_Node_ListUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cHCompileVidentify_useful_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cINodeHashUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cSPhaseRemoveUseless2t6MpnIPhaseGVN_pnQUnique_Node_List__v_; +text: .text%__1cIciMethodRbuild_method_data6M_v_; +text: .text%__1cHCompileLInline_Warm6M_i_; +text: .text%__1cMPhaseIterGVN2t6MpnIPhaseGVN__v_; +text: .text%__1cKloadUBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIPhaseCCP2t6MpnMPhaseIterGVN__v_; +text: .text%__1cHCompileRbuild_start_state6MpnJStartNode_pknITypeFunc__pnIJVMState__; +text: .text%__1cIPhaseCCP2T6M_v_; +text: .text%__1cIPhaseCCPHanalyze6M_v_; +text: .text%__1cIPhaseCCPMdo_transform6M_v_; +text: .text%__1cHCompileNreturn_values6MpnIJVMState__v_; +text: .text%__1cKInlineTreeWbuild_inline_tree_root6F_p0_; +text: .text%__1cHCompileVfinal_graph_reshaping6M_i_; +text: .text%__1cbAfinal_graph_reshaping_walk6FrnKNode_Stack_pnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cIPhaseCCPJtransform6MpnENode__2_; +text: .text%__1cHCompileLFinish_Warm6M_v_; +text: .text%__1cMPhaseIterGVN2t6Mp0_v_; +text: .text%__1cJStartNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHCompileIOptimize6M_v_; +text: .text%__1cXMachCallInterpreterNodePret_addr_offset6M_i_; +text: .text%__1cZCallInterpreterDirectNodePcompute_padding6kMi_i_; +text: .text%__1cQComputeCallStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cZCallInterpreterDirectNodeKmethod_set6Mi_v_; +text: .text%__1cTsarL_eReg_32_63NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOMachEpilogNodeQsafepoint_offset6kM_i_; +text: .text%__1cWpoll_return_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cOGenerateOopMapIcopy_cts6MpnNCellTypeState_2_i_; +text: .text%__1cQComputeCallStackJdo_double6M_v_: generateOopMap.o; +text: .text%__1cICallInfoDset6MnLKlassHandle_1nMmethodHandle_2pnGThread__v_; +text: .text%__1cMLinkResolverWresolve_interface_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cMLinkResolverXresolve_invokeinterface6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverbGruntime_resolve_interface_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cKcmpOpUOperKless_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cRaddL_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescIset_code6MpnHnmethod__v_; +text: .text%__1cJAssemblerEincl6MpnMRegisterImpl__v_; +text: .text%__1cIDivINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKTypeRawPtrFxdual6kM_pknEType__; +text: .text%__1cMTailCallNodeGOpcode6kM_i_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_iii_v_; +text: .text%__1cMstoreSSPNodeHis_Copy6kM_I_: ad_i486_misc.o; +text: .text%__1cZCallDynamicJavaDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cISubLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNobjArrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cPClassFileParserUverify_constantvalue6MiinSconstantPoolHandle_pnGThread__v_; +text: .text%__1cSInterpreterRuntimeTprepare_native_call6FpnKJavaThread_pnNmethodOopDesc__v_; +text: .text%__1cXSignatureHandlerLibraryKinitialize6F_v_; +text: .text%__1cXSignatureHandlerLibraryDadd6FnMmethodHandle__v_; +text: .text%__1cNGrowableArray4CX_Efind6kMkX_i_: interpreterRuntime.o; +text: .text%__1cNFingerprinterLfingerprint6M_X_: interpreterRuntime.o; +text: .text%__1cGEventsDlog6FpkcE_v_: exceptions.o; +text: .text%__1cIAddFNodeGOpcode6kM_i_; +text: .text%__1cRaddL_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerFffree6Mi_v_; +text: .text%__1cGThreadVset_pending_exception6MpnHoopDesc_pkci_v_; +text: .text%__1cKExceptionsRspecial_exception6FpnGThread_pkcinGHandle__i_; +text: .text%__1cKExceptionsG_throw6FpnGThread_pkcinGHandle__v_; +text: .text%__1cSstring_compareNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_StringPcreate_from_str6FpkcpnGThread__nGHandle__; +text: .text%__1cJloadDNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapIppop_any6Mi_v_; +text: .text%__1cXroundDouble_mem_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStartNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSshlL_eReg_1_31NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKPerfMemoryMmark_updated6F_v_; +text: .text%__1cKPerfMemoryFalloc6FI_pc_; +text: .text%__1cPPerfDataManagerIadd_item6FpnIPerfData_i_v_; +text: .text%__1cIPerfDataMcreate_entry6MnJBasicType_II_v_; +text: .text%__1cIPerfData2T6M_v_; +text: .text%__1cIPerfData2t6MnJCounterNS_pkcn0AFUnits_n0ALVariability__v_; +text: .text%__1cSobjArrayKlassKlassXallocate_objArray_klass6MinLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassbCallocate_objArray_klass_impl6FnYobjArrayKlassKlassHandle_inLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cFStateM_sub_Op_XorI6MpknENode__v_; +text: .text%__1cCosLelapsedTime6F_d_; +text: .text%__1cKJavaThreadZsecurity_get_caller_class6Mi_pnMklassOopDesc__; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse1.o; +text: .text%jni_GetStringUTFChars: jni.o; +text: .text%jni_ReleaseStringUTFChars; +text: .text%__1cTconvD2I_reg_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNmulI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cObox_handleNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescWcompute_has_loops_flag6M_i_; +text: .text%__1cNloadConI0NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSInterpreterRuntimeXthrow_pending_exception6FpnKJavaThread__v_; +text: .text%__1cXcmpL_reg_flags_LEGTNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOClearArrayNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%JVM_IsInterrupted; +text: .text%__1cJStoreNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cSmembar_releaseNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKExceptionsRspecial_exception6FpnGThread_pkcinMsymbolHandle_4_i_; +text: .text%__1cSInterpreterRuntimeNquicken_io_cc6FpnKJavaThread__v_; +text: .text%__1cNSignatureInfoHdo_char6M_v_: bytecode.o; +text: .text%__1cSCompiledStaticCallJfind_stub6M_pC_; +text: .text%__1cSCompiledStaticCallSset_to_interpreted6MnMmethodHandle_pC_v_; +text: .text%__1cLOptoRuntimeSnew_typeArray_Type6F_pknITypeFunc__; +text: .text%__1cNdivL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_FindLibraryEntry; +text: .text%__1cPCountedLoopNodeGstride6kM_pnENode__: loopTransform.o; +text: .text%__1cIGraphKitJnew_array6MpnENode_nJBasicType_pknEType_pknMTypeKlassPtr__2_; +text: .text%__1cSleaP_eReg_immINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cWConstantPoolCacheEntrySset_interface_call6MnMmethodHandle_i_v_; +text: .text%__1cLklassItableUcompute_itable_index6FpnNmethodOopDesc__i_; +text: .text%__1cLlog2_intptr6Fi_i_: graphKit.o; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cJloadCNodeFreloc6kM_i_; +text: .text%__1cFStateN_sub_Op_LoadC6MpknENode__v_; +text: .text%__1cLLShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadDNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cZInterpreterMacroAssemblerDpop6MnITosState__v_; +text: .text%__1cTcmovII_reg_LEGTNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQmulD_reg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cTcmovII_reg_LEGTNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cFParsebLincrement_and_test_invocation_counter6Mi_v_; +text: .text%__1cMTypeKlassPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cQsalI_eReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPconvI2F_SSFNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSshrL_eReg_1_31NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJleaP8NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cObox_handleNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNcmpL_LTGENodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbFunnecessary_membar_volatileNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNcmpL_LTGENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFParsePmerge_exception6Mi_v_; +text: .text%__1cSaddF24_reg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopJdo_unroll6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cFStateS_sub_Op_ClearArray6MpknENode__v_; +text: .text%__1cMrep_stosNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMrep_stosNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOcompI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFTypeFEmake6Ff_pk0_; +text: .text%__1cKstoreLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSInterpreterRuntimeOmultianewarray6FpnKJavaThread_pi_v_; +text: .text%__1cIDivINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPmethodDataKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cLStrCompNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cXJNI_ArgumentPusherVaArgHget_int6M_v_: jni.o; +text: .text%__1cNtestP_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectMset_override6FpnHoopDesc_C_v_; +text: .text%__1cSstring_compareNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHMatcherOc_return_value6Fii_nLRegPair__; +text: .text%__1cRandL_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIDivLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSleaP_eReg_immINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: cpCacheKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: cpCacheKlass.o; +text: .text%__1cHi2bNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHCompile2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cOPhaseIdealLoopOadd_constraint6MiipnENode_22p23_v_; +text: .text%__1cKcmpOpUOperHgreater6kM_i_: ad_i486_clone.o; +text: .text%__1cFciEnvZcall_has_multiple_targets6FpnNinstanceKlass_nMsymbolHandle_3ri_i_; +text: .text%__1cRtestI_reg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMmulD_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMNativeLookupGlookup6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cJCMoveNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%jni_SetByteArrayRegion: jni.o; +text: .text%__1cENodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cUjmpLoopEnd_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cJloadFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOjmpLoopEndNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cKarrayKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cUjmpLoopEnd_shortNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvF2D_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cUjmpLoopEnd_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHi2bNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQPSIsAliveClosureLdo_object_b6MpnHoopDesc__i_: psScavenge.o; +text: .text%__1cFStateO_sub_Op_StoreL6MpknENode__v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cFParseLdo_newarray6MnJBasicType__v_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: ad_i486_misc.o; +text: .text%__1cSsafePoint_pollNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreLNodeFreloc6kM_i_; +text: .text%__1cENodeHdel_out6Mp0_v_: divnode.o; +text: .text%__1cGThreadLnmethods_do6M_v_; +text: .text%__1cSsafePoint_pollNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQorI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRsubI_eReg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSReferenceProcessorZadd_to_discovered_list_mt6MppnHoopDesc_23_v_; +text: .text%__1cPRoundDoubleNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cCosTnative_java_library6F_pv_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2ipnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cMloadConFNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cQjava_lang_StringFvalue6FpnHoopDesc__pnQtypeArrayOopDesc__; +text: .text%__1cQjava_lang_StringGoffset6FpnHoopDesc__i_; +text: .text%__1cIModLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cScompP_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQjava_lang_StringScreate_from_symbol6FnMsymbolHandle_pnGThread__nGHandle__; +text: .text%__1cWflagsReg_long_EQdDNEOperFclone6kM_pnIMachOper__; +text: .text%__1cWflagsReg_long_LEGTOperFclone6kM_pnIMachOper__; +text: .text%__1cTcmovII_reg_EQdDNENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNObjectMonitorGEnterI6MpnGThread__v_; +text: .text%__1cMNativeLookupMlookup_style6FnMmethodHandle_pcpkciiripnGThread__pC_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodKlass.o; +text: .text%__1cLServiceUtilLvisible_oop6FpnHoopDesc__i_: objectMonitor_solaris.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodKlass.o; +text: .text%__1cRmulI_eReg_immNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cCosYprint_jni_name_suffix_on6FpnMoutputStream_i_v_; +text: .text%__1cCosYprint_jni_name_prefix_on6FpnMoutputStream_i_v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_53pnGThread__v_; +text: .text%__1cRandI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cVlookup_special_native6Fpc_pC_: nativeLookup.o; +text: .text%__1cPciObjArrayKlassGloader6M_pnHoopDesc__: ciObjArrayKlass.o; +text: .text%__1cRandI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPDictionaryEntryVadd_protection_domain6MpnHoopDesc__v_; +text: .text%__1cRmulI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTsarL_eReg_32_63NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionarybAvalidate_protection_domain6FnTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cKDictionaryVadd_protection_domain6MiInTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cPshrI_eReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKarrayKlassbBcomplete_create_array_klass6FnQarrayKlassHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cKarrayKlassXbase_create_array_klass6FrknKKlass_vtbl_inLKlassHandle_pnGThread__nQarrayKlassHandle__; +text: .text%__1cRxorI_eReg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIPerfLong2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability__v_; +text: .text%__1cJMarkSweepMfollow_stack6F_v_; +text: .text%__1cbBopt_virtual_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cMloadConFNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLregDPR1OperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cLregDPR1OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMNativeLookupLlookup_base6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cNmethodOopDescKklass_name6kM_pnNsymbolOopDesc__; +text: .text%__1cJAssemblerSemit_arith_operand6MipnMRegisterImpl_nHAddress_i_v_; +text: .text%__1cISubLNodeDsub6kMpknEType_3_3_; +text: .text%__1cMNativeLookupNpure_jni_name6FnMmethodHandle__pc_; +text: .text%JVM_GetMethodIxExceptionTableEntry; +text: .text%__1cMciArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cMnegD_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMstoreSSINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSTailCalljmpIndNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRcmpOp_commuteOperFclone6kM_pnIMachOper__; +text: .text%__1cJLoadINodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cScompU_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerGfstp_d6MnHAddress__v_; +text: .text%__1cKciTypeFlowLStateVectorEtrap6MpnQciByteCodeStream_pnHciKlass_i_v_; +text: .text%__1cOmulF24_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQleaPIdxScaleNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRtestI_reg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPfieldDescriptorSlong_initial_value6kM_x_; +text: .text%__1cRsubI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cKstoreBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJLoadPNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cCosHSolarisFEventEpark6M_v_: objectMonitor_solaris.o; +text: .text%__1cKJavaThreadLnmethods_do6M_v_; +text: .text%__1cXvirtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cXvirtual_call_RelocationJpack_data6M_i_; +text: .text%__1cNcmovI_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cZCallDynamicJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZCallDynamicJavaDirectNodeSalignment_required6kM_i_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLMachUEPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNObjectMonitorREntryQdDueue_insert6MpnMObjectWaiter_i_v_; +text: .text%__1cQorI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapXreplace_all_CTS_matches6MnNCellTypeState_1_v_; +text: .text%__1cPClassFileParserbFparse_constant_pool_float_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cbFunnecessary_membar_volatileNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmINodeFreloc6kM_i_; +text: .text%__1cNobjArrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNobjArrayKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cNObjectMonitorbAEntryQdDueue_SelectSuccessor6M_pnMObjectWaiter__; +text: .text%__1cRsarI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsarI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: objArrayKlass.o; +text: .text%__1cKstoreDNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeFreloc6kM_i_; +text: .text%__1cLlog2_intptr6Fi_i_: objArrayKlassKlass.o; +text: .text%__1cNobjArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlass.o; +text: .text%jni_GetStringCritical: jni.o; +text: .text%jni_ReleaseStringCritical: jni.o; +text: .text%__1cPciObjArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cPsarI_eReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOaddF24_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvI2D_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPsarI_eReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__: callnode.o; +text: .text%__1cbACallCompiledJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cODeoptimizationYtrap_state_is_recompiled6Fi_i_; +text: .text%__1cPconvF2D_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYMachCallCompiledJavaNodePret_addr_offset6M_i_; +text: .text%__1cNdivI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPPerfDataManagerMcounter_name6Fpkc2_pc_; +text: .text%__1cRaddL_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSInterpreterRuntimeTnmethod_entry_point6FpnKJavaThread_pnNmethodOopDesc_pnHnmethod__pC_; +text: .text%__1cRandL_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHnmethodXinterpreter_entry_point6M_pC_; +text: .text%__1cFTypeDJsingleton6kM_i_; +text: .text%__1cHCompileSrethrow_exceptions6MpnIJVMState__v_; +text: .text%__1cLRethrowNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: frame.o; +text: .text%__1cIGraphKitbKcombine_and_pop_all_exception_states6M_pnNSafePointNode__: parse1.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cQshrL_eReg_CLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cURethrowExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOstackSlotIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cMmulD_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJloadDNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateP_sub_Op_Rethrow6MpknENode__v_; +text: .text%__1cOstackSlotIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLRethrowNode2t6MpnENode_22222_v_; +text: .text%__1cFTypeDFxmeet6kMpknEType__3_; +text: .text%__1cHOrINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cKloadUBNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTcmovII_reg_LEGTNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNsubI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cZget_mirror_from_signature6FnMmethodHandle_pnPSignatureStream_pnGThread__pnHoopDesc__; +text: .text%__1cNaddP_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cURethrowExceptionNodeFreloc6kM_i_; +text: .text%__1cKJavaThreadbScheck_safepoint_and_suspend_for_native_trans6Fp0_v_; +text: .text%__1cQorI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cLRShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cHMonitor2t6Mipkci_v_; +text: .text%__1cQshrI_eReg_CLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEshll6MpnMRegisterImpl_i_v_; +text: .text%__1cKJavaThreadLgc_prologue6M_v_; +text: .text%__1cRaddL_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOMacroAssemblerKverify_oop6MpnMRegisterImpl_pkc_v_; +text: .text%__1cLloadSSDNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPconvI2F_SSFNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFParseTprofile_switch_case6Mi_v_; +text: .text%__1cKJavaThreadLgc_epilogue6M_v_; +text: .text%__1cFParseOmerge_new_path6Mi_v_; +text: .text%__1cFParseSjump_switch_ranges6MpnENode_pnLSwitchRange_4i_v_; +text: .text%__1cTCallDynamicJavaNodeSis_CallDynamicJava6kM_pk0_: callnode.o; +text: .text%__1cNxorI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%jni_NewByteArray: jni.o; +text: .text%__1cNmulL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: cfgnode.o; +text: .text%__1cMstoreSSINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConPNodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cKReflectionTget_parameter_types6FnMmethodHandle_ippnHoopDesc_pnGThread__nOobjArrayHandle__; +text: .text%__1cNmethodOopDescbGresolved_checked_exceptions_impl6Fp0pnGThread__nOobjArrayHandle__; +text: .text%__1cNcmovP_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKReflectionTget_exception_types6FnMmethodHandle_pnGThread__nOobjArrayHandle__; +text: .text%__1cNmethodOopDescVparameter_annotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cNinstanceKlassQmethod_index_for6kMpnNmethodOopDesc_pnGThread__i_; +text: .text%__1cQorI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOstackSlotDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLloadSSDNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMstoreSSPNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cOstackSlotDOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cMstoreSSINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFMutexbLwait_for_lock_blocking_implementation6MpnKJavaThread__v_; +text: .text%__1cHnmethodbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cRxorI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOleaPIdxOffNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRaddL_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFframeZinterpreter_frame_set_mdx6Mi_v_; +text: .text%__1cSObjectSynchronizerHinflate6FpnHoopDesc__pnNObjectMonitor__; +text: .text%__1cPstoreImmI16NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOcmpD_cc_P6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTshrL_eReg_32_63NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRandI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cRandI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: loopnode.o; +text: .text%__1cNObjectMonitorHRecycle6M_v_; +text: .text%__1cRandI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNandL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFBlockNset_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cTcmovII_reg_LTGENodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cSleaP_eReg_immINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadSNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOtypeArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cPregister_native6FnLKlassHandle_nMsymbolHandle_1pCpnGThread__i_: jni.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEmove6Mii_v_; +text: .text%__1cNcmovI_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENode2t6Mp0111111_v_; +text: .text%__1cFStateP_sub_Op_RShiftL6MpknENode__v_; +text: .text%__1cMloadConLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIAddDNodeGOpcode6kM_i_; +text: .text%__1cNmodL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKarrayKlassTallocate_arrayArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cTconvD2I_reg_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: arrayKlass.o; +text: .text%__1cFParseQjump_if_fork_int6MpnENode_2nIBoolTestEmask__pnGIfNode__; +text: .text%__1cOcompP_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXis_positive_zero_double6Fd_i_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: arrayKlass.o; +text: .text%__1cNmaxI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: arrayKlass.o; +text: .text%__1cKstorePNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodDataKlass.o; +text: .text%__1cNmulI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodDataKlass.o; +text: .text%__1cHTypeAryFxdual6kM_pknEType__; +text: .text%__1cOMethodLivenessKBasicBlockFsplit6Mi_p1_; +text: .text%__1cLloadSSFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVLoaderConstraintTableWfind_constrained_klass6MnMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cTcmovII_reg_LTGENodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cOrepush_if_args6FpnFParse_pnENode_3_v_: parse2.o; +text: .text%__1cNaddL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOstackSlotFOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cTcmovII_reg_LTGENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOstackSlotFOperFscale6kM_i_: ad_i486.o; +text: .text%__1cSshlL_eReg_1_31NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOstackSlotFOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cJAssemblerGpushad6M_v_; +text: .text%JVM_GetCallerClass; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: bytecode.o; +text: .text%__1cIMaxINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cQorI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapXdo_return_monitor_check6M_v_; +text: .text%__1cQsalL_eReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKciTypeFlowOsplit_range_at6Mi_pn0AFRange__; +text: .text%__1cPshrI_eReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%jni_GetFieldID: jni.o; +text: .text%jni_IsAssignableFrom: jni.o; +text: .text%__1cISubLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNnegI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNnegI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSobjArrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cFParseXfetch_interpreter_state6MipknEType_pnENode__5_; +text: .text%__1cFParseWcheck_interpreter_type6MpnENode_pknEType_rpnNSafePointNode__2_; +text: .text%__1cJAssemblerEaddl6MnHAddress_i_v_; +text: .text%__1cQmulD_reg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTsarL_eReg_32_63NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOstackSlotDOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cJAssemblerGfstp_s6MnHAddress__v_; +text: .text%__1cYinternal_word_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cXroundDouble_mem_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLloadSSDNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQsalI_eReg_CLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKCMovePNodeGOpcode6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerbAget_cache_and_index_at_bcp6MpnMRegisterImpl_2i_v_; +text: .text%__1cNmodI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRcmpOp_commuteOperFccode6kM_i_: ad_i486_clone.o; +text: .text%__1cJloadDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSCountedLoopEndNode2t6MpnENode_2ff_v_; +text: .text%__1cPCountedLoopNode2t6MpnENode_2_v_; +text: .text%__1cNcmovP_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pCi_v_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_pkcnGHandle_6_6_; +text: .text%__1cNSCMemProjNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOmulF24_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFStateX_sub_Op_CompareAndSwapL6MpknENode__v_; +text: .text%__1cSloadL_volatileNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNtestU_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTCompareAndSwapLNode2t6MpnENode_2222_v_; +text: .text%__1cSCompareAndSwapNode2t6MpnENode_2222_v_; +text: .text%__1cOLibraryCallKitRinline_unsafe_CAS6MnJBasicType__i_; +text: .text%__1cSloadL_volatileNodeFreloc6kM_i_; +text: .text%__1cTcompareAndSwapLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cObox_handleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cISubLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cFframeVnmethods_code_blob_do6M_v_; +text: .text%__1cYcmpL_zero_flags_LTGENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cVCallRuntimeDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_ThreadJis_daemon6FpnHoopDesc__i_; +text: .text%__1cJloadDNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopUpeeled_dom_test_elim6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cJAssemblerDhlt6M_v_; +text: .text%__1cKstoreDNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cYcmpL_zero_flags_LEGTNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerEstop6Mpkc_v_; +text: .text%__1cTcmovII_reg_LTGENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPClassFileParserbJparse_classfile_signature_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cQshrI_eReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframeZinterpreter_frame_set_mdp6MpC_v_; +text: .text%__1cQciByteCodeStreamFtable6MnJBytecodesECode__2_; +text: .text%__1cYinternal_word_RelocationJpack_data6M_i_; +text: .text%__1cIDivLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%jni_SetBooleanField: jni.o; +text: .text%__1cSstoreD_roundedNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cTconvD2I_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreFNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: machnode.o; +text: .text%__1cHMatcherbDinterpreter_frame_pointer_reg6F_nHOptoRegEName__; +text: .text%__1cFStateT_sub_Op_ThreadLocal6MpknENode__v_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pF_vc_v_; +text: .text%__1cMtlsLoadPNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cWResolveOopMapConflictsRpossible_gc_point6MpnOBytecodeStream__i_: rewriter.o; +text: .text%__1cIciObject2t6MpnHciKlass__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_object6M_v_: interpreterRuntime.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorDbox6Mii_v_; +text: .text%__1cSsarL_eReg_1_31NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKsplit_once6FpnMPhaseIterGVN_pnENode_333_v_: cfgnode.o; +text: .text%__1cYmulI_imm_RShift_highNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLLShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPRoundDoubleNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKstoreDNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKloadUBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMURShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cPRoundDoubleNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIGraphKitPdstore_rounding6MpnENode__2_; +text: .text%__1cPClassFileParserbBcheck_illegal_static_method6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cTCallInterpreterNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cLPhaseValuesHlongcon6Mx_pnIConLNode__; +text: .text%__1cFStateX_sub_Op_CallInterpreter6MpknENode__v_; +text: .text%__1cZCallInterpreterDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZCallInterpreterDirectNodeFreloc6kM_i_; +text: .text%__1cTCallInterpreterNodeSis_CallInterpreter6kM_pk0_: classes.o; +text: .text%__1cHMatcherbAinterpreter_method_oop_reg6F_nHOptoRegEName__; +text: .text%__1cGciType2t6MpnHciKlass__v_; +text: .text%__1cHCompilebMGenerate_Compiled_To_Interpreter_Graph6MpknITypeFunc_pC_v_; +text: .text%__1cMdecI_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHMatcherXcompiler_method_oop_reg6F_nHOptoRegEName__; +text: .text%__1cXMachCallInterpreterNodeWis_MachCallInterpreter6M_p0_: ad_i486_misc.o; +text: .text%__1cHciKlass2t6MpnIciSymbol_p0_v_; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__: classes.o; +text: .text%__1cIciMethodRinterpreter_entry6M_pC_; +text: .text%__1cXjvm_define_class_common6FpnHJNIEnv__pkcpnI_jobject_pkWi53pnGThread__pnH_jclass__: jvm.o; +text: .text%__1cQSystemDictionaryTresolve_from_stream6FnMsymbolHandle_nGHandle_2pnPClassFileStream_pnGThread__pnMklassOopDesc__; +text: .text%__1cUciInstanceKlassKlassEmake6F_p0_; +text: .text%__1cIciObjectUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cLLShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSTailCalljmpIndNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOCallNativeNodeGOpcode6kM_i_; +text: .text%__1cOstackSlotIOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cIciSymbolHbyte_at6Mi_i_; +text: .text%__1cLregDPR1OperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cZCallDynamicJavaDirectNodePcompute_padding6kMi_i_; +text: .text%__1cZCallDynamicJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cSTailCalljmpIndNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cIModINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSTailCalljmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQorI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXMachCallDynamicJavaNodePret_addr_offset6M_i_; +text: .text%__1cJLoadPNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cNcmpL_EQdDNENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNcmpL_EQdDNENodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXroundDouble_mem_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapOdo_monitorexit6Mi_v_; +text: .text%__1cOGenerateOopMapLmonitor_pop6M_nNCellTypeState__; +text: .text%__1cOstoreF_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cOmulIS_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSalign_to_page_size6FI_I_: heap.o; +text: .text%__1cOaddF24_regNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cMstoreSSPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvI2D_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcinMsymbolHandle_4nGHandle_6_v_; +text: .text%__1cNmulI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcipnNsymbolOopDesc_4_v_; +text: .text%__1cPciInstanceKlassYprotection_domain_handle6M_pnI_jobject__; +text: .text%__1cNmulI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRandI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPciInstanceKlassNloader_handle6M_pnI_jobject__; +text: .text%__1cTcmovII_reg_EQdDNENodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cTcmovII_reg_LEGTNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJChunkPoolMfree_all_but6MI_v_: allocation.o; +text: .text%__1cRandL_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNxorI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOaddF24_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%JVM_MonitorWait; +text: .text%__1cNObjectMonitorEwait6MxipnGThread__v_; +text: .text%__1cSObjectSynchronizerEwait6FnGHandle_xpnGThread__v_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlassKlass.o; +text: .text%__1cZInterpreterMacroAssemblerYtest_method_data_pointer6MpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cMmulD_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQshrL_eReg_CLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNdivI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTAbstractInterpreterSBasicType_as_index6FnJBasicType__i_; +text: .text%__1cIMulDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPjava_lang_ClassQprimitive_mirror6FnJBasicType__pnHoopDesc__; +text: .text%__1cKExceptionsL_throw_args6FpnGThread_pkcinMsymbolHandle_5pnRJavaCallArguments__v_; +text: .text%__1cJMarkSweepSFollowStackClosureHdo_void6M_v_: markSweep.o; +text: .text%__1cNSingletonBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cPICStubInterfaceRcode_size_to_size6kMi_i_: icBuffer.o; +text: .text%__1cPICStubInterfaceKinitialize6MpnEStub_i_v_: icBuffer.o; +text: .text%__1cSleaP_eReg_immINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHciKlassMis_interface6M_i_: ciTypeArrayKlass.o; +text: .text%__1cNcmpL_LEGTNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateQ_sub_Op_URShiftL6MpknENode__v_; +text: .text%__1cNcmpL_LEGTNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIModLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_i_v_; +text: .text%__1cHMatcherXinterpreter_arg_ptr_reg6F_nHOptoRegEName__; +text: .text%__1cFciEnvWis_dependence_violated6FpnMklassOopDesc_pnNmethodOopDesc__i_; +text: .text%__1cSCompiledStaticCallNcompute_entry6FnMmethodHandle_rnOStaticCallInfo__v_; +text: .text%__1cSCompiledStaticCallIis_clean6kM_i_; +text: .text%__1cJloadLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLOptoRuntimeVresolve_static_call_C6FpnKJavaThread__pC_; +text: .text%__1cJAssemblerEnegl6MpnMRegisterImpl__v_; +text: .text%__1cOmulF24_regNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cRsubL_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadConL0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsubL_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRmulI_imm_highNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSCompiledStaticCallDset6MrknOStaticCallInfo__v_; +text: .text%__1cFParseHdo_irem6M_v_; +text: .text%__1cHi2bNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHBoxNodeGOpcode6kM_i_; +text: .text%__1cQmulD_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXjava_lang_ref_ReferenceOset_discovered6FpnHoopDesc_2_v_; +text: .text%__1cTcmovII_reg_EQdDNENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cTcmovII_reg_EQdDNENodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cRtestI_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNtestI_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerFpopfd6M_v_; +text: .text%__1cUParallelScavengeHeapIcapacity6kM_I_; +text: .text%__1cCosTset_native_priority6FpnGThread_i_nIOSReturn__; +text: .text%__1cQset_lwp_priority6Fiii_i_; +text: .text%__1cbDreorder_based_on_method_index6FpnPobjArrayOopDesc_1ppnHoopDesc__v_: methodOop.o; +text: .text%__1cNmaxI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMLinkResolverbPlinktime_resolve_interface_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongVariable__; +text: .text%__1cPconvF2D_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNIdealLoopTreeXpolicy_maximally_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cbCAbstractInterpreterGeneratorVset_vtos_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cKciTypeFlowLStateVectorLdo_newarray6MpnQciByteCodeStream__v_; +text: .text%__1cTmembar_volatileNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cNxorI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cPconvI2F_SSFNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMjniIdSupportNto_jmethod_id6FpnNmethodOopDesc__pnK_jmethodID__; +text: .text%__1cMjniIdPrivateGid_for6FnTinstanceKlassHandle_i_i_: jniId.o; +text: .text%__1cNget_method_id6FpnHJNIEnv__pnH_jclass_pkc5ipnGThread__pnK_jmethodID__: jni.o; +text: .text%__1cICmpFNodeGOpcode6kM_i_; +text: .text%__1cNmethodOopDescKjmethod_id6M_pnK_jmethodID__; +text: .text%__1cRmulI_imm_highNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSstring_compareNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIAddLNodeIadd_ring6kMpknEType_3_3_; +text: .text%jni_NewStringUTF: jni.o; +text: .text%__1cQorI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVloadConL_low_onlyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIUniverseWreinitialize_vtable_of6FpnFKlass_pnGThread__v_; +text: .text%__1cPshlI_eReg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%JVM_SetClassSigners; +text: .text%JVM_DefineClassWithSource; +text: .text%__1cJAssemblerGpushfd6M_v_; +text: .text%__1cGEventsDlog6FpkcE_v_: sharedRuntime.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cNloadConL0NodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cNmulI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulINodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIMulINodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cNmulI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cHAddress2t6MinJrelocInfoJrelocType__v_; +text: .text%__1cTsarL_eReg_32_63NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cTconvF2I_reg_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceRefKlass.o; +text: .text%__1cNsubL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVCallRuntimeDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRaddI_mem_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cODeoptimizationVtrap_state_has_reason6Fii_i_; +text: .text%__1cNaddL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cVCallRuntimeDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRmulI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLloadSSDNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cS__ieee754_rem_pio26Fdpd_i_: sharedRuntimeTrig.o; +text: .text%__1cQshrL_eReg_CLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cObox_handleNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cTshlL_eReg_32_63NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cTmembar_volatileNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRtestI_reg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cSThreadLocalStorageNpd_set_thread6FpnGThread__v_; +text: .text%__1cMstoreSSINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSThreadLocalStorageKset_thread6FpnGThread__v_; +text: .text%__1cSThreadLocalStoragebBget_thread_via_cache_slowly6FIi_pnGThread__; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: connode.o; +text: .text%__1cCosbBthread_local_storage_at_put6Fipv_v_; +text: .text%__1cLConvF2INodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: connode.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: cfgnode.o; +text: .text%get_thread; +text: .text%__1cSThreadLocalStorageSset_thread_in_slot6FpnGThread__v_; +text: .text%jni_CallIntMethod: jni.o; +text: .text%__1cPconvF2D_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOjmpLoopEndNodeGnegate6M_v_: ad_i486_misc.o; +text: .text%__1cHCompileRmake_vm_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: node.o; +text: .text%__1cJCMoveNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRsubI_eReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLloadSSFNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: node.o; +text: .text%__1cMnegD_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHnmethodPis_dependent_on6MpnMklassOopDesc__i_; +text: .text%__1cNSharedRuntimeSfind_callee_method6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: node.o; +text: .text%__1cSsarL_eReg_1_31NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cTshrL_eReg_32_63NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPhaseIdealLoopOdo_range_check6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cNRelocIteratorEnext6M_i_: sharedRuntime.o; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_deopt_entry_for6MnITosState_i_pC_; +text: .text%__1cLOptoRuntimeThandle_wrong_method6FpnKJavaThread__pC_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_return_entry_for6MnITosState_i_pC_; +text: .text%__1cTmembar_volatileNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNSharedRuntimeTreresolve_call_site6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cKstoreFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUPSMarkSweepDecoratorHcompact6Mi_v_; +text: .text%__1cUPSMarkSweepDecoratorPadjust_pointers6M_v_; +text: .text%__1cTcmovII_reg_EQdDNENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPconvI2F_SSFNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cUPSMarkSweepDecoratorVdestination_decorator6F_p0_; +text: .text%__1cRInlineCacheBufferRic_stub_code_size6F_i_; +text: .text%__1cUPSMarkSweepDecoratorKprecompact6M_v_; +text: .text%__1cQshrI_eReg_CLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadDNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cICmpDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOaddF24_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cIDivDNodeGOpcode6kM_i_; +text: .text%__1cOmulF24_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cOaddF24_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOmulF24_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHCompileQgrow_alias_types6M_v_; +text: .text%__1cFParseScreate_jump_tables6MpnENode_pnLSwitchRange_4_i_; +text: .text%__1cQjava_lang_ThreadMis_stillborn6FpnHoopDesc__i_; +text: .text%JVM_GetClassCPTypes; +text: .text%__1cUverify_byte_codes_fn6F_pv_: verifier.o; +text: .text%JVM_GetClassMethodsCount; +text: .text%JVM_GetClassCPEntriesCount; +text: .text%__1cOstoreF_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%JVM_GetClassFieldsCount; +text: .text%__1cQshrL_eReg_CLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKstoreBNodeFreloc6kM_i_; +text: .text%__1cPconvI2F_SSFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_ThreadKset_thread6FpnHoopDesc_pnKJavaThread__v_; +text: .text%__1cMdecI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGEventsDlog6FpkcE_v_: thread.o; +text: .text%__1cPconvI2L_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOMacroAssemblerKverify_FPU6Mipkc_v_; +text: .text%__1cJLoadFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMloadConDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstoreBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddD_reg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerGmembar6M_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIgenerate6MX_v_; +text: .text%__1cXSignatureHandlerLibraryLset_handler6FpnKCodeBuffer__pC_; +text: .text%__1cNcmovP_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_5pnGThread__v_; +text: .text%__1cXSignatureHandlerLibraryOpd_set_handler6FpC_v_; +text: .text%__1cFJNIidEfind6Mi_p0_; +text: .text%__1cbBinitialize_itable_for_klass6FpnMklassOopDesc__v_; +text: .text%__1cJCmpD3NodeGOpcode6kM_i_; +text: .text%__1cXconvI2L_reg_reg_zexNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbFunnecessary_membar_volatileNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%JVM_IsPrimitiveClass; +text: .text%__1cIMinINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRsubL_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cNSharedRuntimeDd2l6Fd_x_; +text: .text%__1cRsubL_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPciObjArrayKlassJmake_impl6FpnHciKlass__p0_; +text: .text%__1cRsubL_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%jni_FindClass: jni.o; +text: .text%__1cPmovI_nocopyNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cbDjava_lang_reflect_ConstructorThas_signature_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorVhas_annotations_field6F_i_; +text: .text%__1cHCompilebMGenerate_Interpreter_To_Compiled_Graph6MpknITypeFunc__v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorbFhas_parameter_annotations_field6F_i_; +text: .text%__1cFStateM_sub_Op_RegD6MpknENode__v_; +text: .text%__1cbDjava_lang_reflect_ConstructorZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cUCallCompiledJavaNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cGThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cGThreadVis_jvmti_agent_thread6kM_i_: thread.o; +text: .text%__1cbACallCompiledJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cbACallCompiledJavaDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKCompiledICMset_to_clean6M_v_; +text: .text%__1cTcmovII_reg_LEGTNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_2_v_; +text: .text%__1cJloadFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKReflectionPnew_constructor6FnMmethodHandle_pnGThread__pnHoopDesc__; +text: .text%__1cbDjava_lang_reflect_ConstructorGcreate6FpnGThread__nGHandle__; +text: .text%__1cFParseSjump_if_false_fork6MpnGIfNode_ii_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cbACallCompiledJavaDirectNodeFreloc6kM_i_; +text: .text%__1cUPipeline_Use_Element2t6MIIIinXPipeline_Use_Cycle_Mask__v_: ad_i486_pipeline.o; +text: .text%__1cbDjava_lang_reflect_ConstructorNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cFStateY_sub_Op_CallCompiledJava6MpknENode__v_; +text: .text%__1cXPipeline_Use_Cycle_Mask2t6MI_v_: ad_i486_pipeline.o; +text: .text%__1cMsubD_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIDivLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSleaP_eReg_immINodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cKstoreFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTshlL_eReg_32_63NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_2_v_; +text: .text%__1cNcmovI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRandL_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSstoreD_roundedNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSstoreD_roundedNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_MinI6MpknENode__v_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cNminI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFMutex2T6M_v_; +text: .text%lwp_mutex_destroy: os_solaris.o; +text: .text%__1cQmulD_reg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHi2bNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHMonitor2T6M_v_; +text: .text%__1cIimmIOperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cIMinINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cIAddFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%lwp_cond_destroy: os_solaris.o; +text: .text%__1cLConvD2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMmulD_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMstoreSSPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cISubLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cINodeHashEgrow6M_v_; +text: .text%__1cMnegD_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_ThreadLthreadGroup6FpnHoopDesc__2_; +text: .text%__1cIMulINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cICodeBlobOis_java_method6kM_i_: codeBlob.o; +text: .text%__1cLConvI2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOstackSlotPOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cJCMoveNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKLoadPCNodeGOpcode6kM_i_; +text: .text%__1cRaddI_mem_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKCMoveINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRcmpOp_commuteOperHgreater6kM_i_: ad_i486_clone.o; +text: .text%__1cNObjectMonitorGenter26MpnGThread__v_; +text: .text%__1cNmodL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRcmpOp_commuteOperGnegate6M_v_: ad_i486_clone.o; +text: .text%__1cPciObjArrayKlassEmake6FpnHciKlass__p0_; +text: .text%__1cQaddD_reg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFTypeFFxmeet6kMpknEType__3_; +text: .text%__1cYcmpL_zero_flags_LTGENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRmulI_imm_highNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJCMoveNode2t6MpnENode_22pknEType__v_: connode.o; +text: .text%__1cJCMoveNodeEmake6FpnENode_222pknEType__p0_; +text: .text%__1cOPhaseIdealLoopVinsert_pre_post_loops6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cLloadSSDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKCodeBufferWinsert_double_constant6Md_pC_; +text: .text%__1cXroundDouble_mem_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_regNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cJCodeCacheFfirst6F_pnICodeBlob__; +text: .text%__1cICodeBlobJis_zombie6kM_i_: onStackReplacement.o; +text: .text%__1cWis_positive_one_double6Fd_i_; +text: .text%__1cNaddP_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cICodeHeapLfirst_block6kM_pnJHeapBlock__; +text: .text%__1cXcmpL_reg_flags_LEGTNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPciObjectFactoryPinsert_non_perm6Mrpn0ANNonPermObject_pnHoopDesc_pnIciObject__v_; +text: .text%__1cFframeVshould_be_deoptimized6kM_i_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlass.o; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_: callnode.o; +text: .text%__1cMincI_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlass.o; +text: .text%__1cSleaP_eReg_immINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJEventMark2t6MpkcE_v_: psMarkSweep.o; +text: .text%__1cKloadUBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJAssemblerDjmp6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cISubDNodeGOpcode6kM_i_; +text: .text%__1cNObjectMonitor2t6M_v_; +text: .text%__1cTconvD2I_reg_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cSObjectSynchronizerGnotify6FnGHandle_pnGThread__v_; +text: .text%__1cOcmpD_cc_P6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvD2FNodeGOpcode6kM_i_; +text: .text%__1cTmembar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSshrL_eReg_1_31NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%JVM_MonitorNotify; +text: .text%__1cHMatcherXpost_store_load_barrier6FpknENode__i_; +text: .text%__1cFParseNdo_instanceof6M_v_; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_: interpreterRuntime.o; +text: .text%__1cSsarL_eReg_1_31NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEshrl6MpnMRegisterImpl_i_v_; +text: .text%__1cSmulF24_reg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIGraphKitOgen_instanceof6MpnENode_2_2_; +text: .text%__1cYjava_lang_reflect_MethodPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cLTypeInstPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cYjava_lang_reflect_MethodbFhas_parameter_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodbChas_annotation_default_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodVhas_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodThas_signature_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodPset_return_type6FpnHoopDesc_2_v_; +text: .text%__1cNCallGeneratorSfor_predicted_call6FpnHciKlass_p03_3_; +text: .text%__1cYjava_lang_reflect_MethodIset_name6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodWset_annotation_default6FpnHoopDesc_2_v_; +text: .text%__1cZCallDynamicJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFStateN_sub_Op_LoadF6MpknENode__v_; +text: .text%__1cNandI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKReflectionKnew_method6FnMmethodHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cTconvD2I_reg_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cWPredictedCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cWPredictedCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cWPredictedCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cNmethodOopDescSannotation_default6kM_pnQtypeArrayOopDesc__; +text: .text%__1cYjava_lang_reflect_MethodGcreate6FpnGThread__nGHandle__; +text: .text%__1cYjava_lang_reflect_MethodJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cMstoreSSPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%jni_GetMethodID: jni.o; +text: .text%__1cOstackSlotPOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cOGenerateOopMapKpp_new_ref6MpnNCellTypeState_i_v_; +text: .text%__1cRmulI_imm_highNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFParseTjump_if_always_fork6Mii_v_; +text: .text%__1cMnegD_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFStateW_sub_Op_MemBarVolatile6MpknENode__v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: typeArrayKlass.o; +text: .text%__1cNObjectMonitorGnotify6MpnGThread__v_; +text: .text%__1cHRegMask2t6M_v_: matcher.o; +text: .text%__1cJLoadSNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cPconvI2D_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_GetClassDeclaredConstructors; +text: .text%__1cINegDNodeGOpcode6kM_i_; +text: .text%__1cIciObjectOis_method_data6M_i_: ciObjectFactory.o; +text: .text%__1cNCallGeneratorRfor_uncommon_trap6FpnIciMethod_nODeoptimizationLDeoptReason_n0CLDeoptAction__p0_; +text: .text%__1cIciObjectJis_method6M_i_: ciObjectFactory.o; +text: .text%__1cNmodI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateP_sub_Op_LShiftL6MpknENode__v_; +text: .text%__1cNmodI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLConvI2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: callGenerator.o; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceRefKlass.o; +text: .text%__1cKcmpOpUOperFequal6kM_i_: ad_i486_clone.o; +text: .text%__1cZUncommonTrapCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciObjectFactory.o; +text: .text%__1cFStateM_sub_Op_ModI6MpknENode__v_; +text: .text%__1cMloadConDNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cPmovI_nocopyNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIci2bNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMVirtualSpaceQuncommitted_size6kM_I_; +text: .text%__1cMVirtualSpaceJexpand_by6MI_i_; +text: .text%__1cJAssemblerFfld_d6MnHAddress__v_; +text: .text%__1cJloadFNodeFreloc6kM_i_; +text: .text%__1cOaddF24_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMincI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cVCallRuntimeDirectNodeKmethod_set6Mi_v_; +text: .text%__1cRaddI_eReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOGenerateOopMapKinit_state6M_v_; +text: .text%__1cTcmovII_reg_LTGENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNSafepointBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cOGenerateOopMapTmark_reachable_code6M_v_; +text: .text%__1cOGenerateOopMapPinitialize_vars6M_v_; +text: .text%__1cSTailCalljmpIndNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNinstanceKlassSregister_finalizer6FpnPinstanceOopDesc_pnGThread__2_; +text: .text%__1cOGenerateOopMapRinit_basic_blocks6M_v_; +text: .text%__1cOGenerateOopMapYsetup_method_entry_state6M_v_; +text: .text%__1cMstoreSSPNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapYrewrite_refval_conflicts6M_v_; +text: .text%__1cOGenerateOopMapbAmake_context_uninitialized6M_v_; +text: .text%__1cOmulF24_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapTmethodsig_to_effect6MpnNsymbolOopDesc_ipnNCellTypeState__i_; +text: .text%__1cQsalI_eReg_CLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNdivI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMapKinterp_all6M_v_; +text: .text%__1cNandI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPClassFileParserXverify_unqualified_name6MpcIi_i_; +text: .text%__1cUGenericGrowableArrayKraw_remove6MpknEGrET__v_; +text: .text%__1cOcmovI_regUNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMmulD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_SystemSin_offset_in_bytes6F_i_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cFTypeFJsingleton6kM_i_; +text: .text%__1cRandL_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_SystemTout_offset_in_bytes6F_i_; +text: .text%__1cIBytecodeIset_code6MnJBytecodesECode__v_; +text: .text%__1cPconvI2D_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: memnode.o; +text: .text%__1cNmulI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIRetTableRcompute_ret_table6MnMmethodHandle__v_; +text: .text%__1cQsalL_eReg_CLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapLcompute_map6MpnGThread__v_; +text: .text%__1cNdivL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMaxINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cOGenerateOopMap2t6MnMmethodHandle__v_; +text: .text%__1cRsubL_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvF2D_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegD_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTAbstractInterpreterLdeopt_entry6FnITosState_i_pC_; +text: .text%__1cOGenerateOopMapRdo_interpretation6M_v_; +text: .text%__1cQshrL_eReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMapbImark_bbheaders_and_count_gc_points6M_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIpass_int6M_v_: interpreterRuntime.o; +text: .text%__1cOGenerateOopMapNinitialize_bb6M_v_; +text: .text%__1cNcmovP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQorI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceRefKlass.o; +text: .text%__1cMdecI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSstring_compareNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: markSweep.o; +text: .text%__1cRInterpreterOopMapIis_empty6M_i_; +text: .text%__1cJMarkSweepSMarkAndPushClosureLdo_nmethods6kM_ki_: markSweep.o; +text: .text%__1cTsarL_eReg_32_63NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNcmovP_regNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cQjava_lang_ThreadMset_priority6FpnHoopDesc_nOThreadPriority__v_; +text: .text%__1cIMulDNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cOmulF24_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMsubD_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cOGenerateOopMapPdo_monitorenter6Mi_v_; +text: .text%__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cMsubD_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOjmpLoopEndNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cJAssemblerFbswap6MpnMRegisterImpl__v_; +text: .text%__1cTconvF2I_reg_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMstoreSSPNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOjmpLoopEndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOGenerateOopMapMmonitor_push6MnNCellTypeState__v_; +text: .text%__1cOjmpLoopEndNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cbFunnecessary_membar_volatileNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cIGraphKitXinsert_mem_bar_volatile6MpnKMemBarNode_i_v_; +text: .text%__1cRsubI_eReg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_ThreadIis_alive6FpnHoopDesc__i_; +text: .text%__1cFStateX_sub_Op_CallDynamicJava6MpknENode__v_; +text: .text%JVM_IsThreadAlive; +text: .text%__1cQMachCallJavaNodeVis_MachCallStaticJava6M_pnWMachCallStaticJavaNode__: ad_i486_misc.o; +text: .text%__1cZCallDynamicJavaDirectNodeFreloc6kM_i_; +text: .text%__1cTcmovII_reg_LTGENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYcmpL_zero_flags_LTGENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEdecl6MpnMRegisterImpl__v_; +text: .text%__1cVLoaderConstraintTableYextend_loader_constraint6MpnVLoaderConstraintEntry_nGHandle_pnMklassOopDesc__v_; +text: .text%__1cUVirtualCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cVcompiledICHolderKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNCallGeneratorQfor_virtual_call6FpnIciMethod__p0_; +text: .text%__1cVLoaderConstraintTablebHensure_loader_constraint_capacity6MpnVLoaderConstraintEntry_i_v_; +text: .text%__1cIModLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQOopMapCacheEntryFflush6M_v_; +text: .text%__1cQOopMapCacheEntryEfill6MnMmethodHandle_i_v_; +text: .text%__1cQOopMapCacheEntryTdeallocate_bit_mask6M_v_; +text: .text%__1cQOopMapCacheEntryRallocate_bit_mask6M_v_; +text: .text%__1cOPSPromotionLABRunallocate_object6MpnHoopDesc__i_; +text: .text%__1cICodeHeapTmark_segmap_as_free6MII_v_; +text: .text%__1cNinstanceKlassYremove_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cNsubI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICodeHeapJexpand_by6MI_i_; +text: .text%__1cTcmovII_reg_LEGTNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cJAssemblerFfld_s6MnHAddress__v_; +text: .text%__1cOstoreF_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cVscale_to_lwp_priority6Fiii_i_: os_solaris.o; +text: .text%__1cMdivD_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddI_mem_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMdivD_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNxorI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQmulI_mem_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQmulI_mem_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQorI_eReg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cMsubD_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cUVirtualCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cPconvI2F_SSFNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRcmpOp_commuteOperKless_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cIci2bNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJMemRegionMintersection6kMk0_0_; +text: .text%__1cIDivLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMincI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJStubQdDueueRrequest_committed6Mi_pnEStub__; +text: .text%__1cQmulD_reg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_ConD6MpknENode__v_; +text: .text%__1cSaddD_reg_roundNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNmodL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNRelocIteratorEnext6M_i_: output.o; +text: .text%__1cYinternal_word_RelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cNaddI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosHSolarisKvm_signals6F_pnIsigset_t__; +text: .text%__1cODataRelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cODataRelocationJset_value6MpC_v_: relocInfo.o; +text: .text%__1cPICStubInterfaceEsize6kMpnEStub__i_: icBuffer.o; +text: .text%__1cFStateO_sub_Op_CMoveI6MpknENode__v_; +text: .text%__1cPICStubInterfaceIfinalize6MpnEStub__v_: icBuffer.o; +text: .text%__1cSdivD_reg_roundNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cTOopMapForCacheEntry2t6MnMmethodHandle_ipnQOopMapCacheEntry__v_; +text: .text%__1cCosHSolarisPhotspot_sigmask6FpnGThread__v_; +text: .text%__1cGICStubIfinalize6M_v_; +text: .text%__1cCosScurrent_stack_base6F_pC_; +text: .text%__1cOGenerateOopMapVresult_for_basicblock6Mi_v_; +text: .text%__1cGThread2t6M_v_; +text: .text%__1cCosScurrent_stack_size6F_I_; +text: .text%__1cCosHSolarisVinit_thread_fpu_state6F_v_; +text: .text%__1cJAssemblerExorl6MpnMRegisterImpl_2_v_; +text: .text%__1cCosHSolarisRunblocked_signals6F_pnIsigset_t__; +text: .text%__1cJStubQdDueueMremove_first6M_v_; +text: .text%__1cGThreadbArecord_stack_base_and_size6M_v_; +text: .text%__1cLMoveL2DNodeGOpcode6kM_i_; +text: .text%__1cIOSThreadNpd_initialize6M_v_; +text: .text%__1cQOopMapCacheEntryIset_mask6MpnNCellTypeState_2i_v_; +text: .text%__1cCosScurrent_process_id6F_i_; +text: .text%__1cIOSThread2t6MpFpv_i1_v_; +text: .text%__1cKRelocationRpd_set_data_value6MpCi_v_; +text: .text%__1cTshlL_eReg_32_63NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKConv2BNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cGThreadbFinitialize_thread_local_storage6M_v_; +text: .text%__1cTOopMapForCacheEntryOreport_results6kM_i_: oopMapCache.o; +text: .text%__1cCosRinitialize_thread6F_v_; +text: .text%__1cFTypeDGis_nan6kM_i_; +text: .text%__1cTOopMapForCacheEntryLcompute_map6MpnGThread__v_; +text: .text%__1cCosMstart_thread6FpnGThread__v_; +text: .text%_start: os_solaris.o; +text: .text%__1cYcmpL_zero_flags_LTGENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_SubL6MpknENode__v_; +text: .text%__1cLStrCompNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%JVM_SetThreadPriority; +text: .text%__1cNobjArrayKlassKinitialize6MpnGThread__v_; +text: .text%jni_NewObjectArray: jni.o; +text: .text%__1cKCompiledICKcached_oop6kM_pnHoopDesc__; +text: .text%__1cTcmovII_reg_EQdDNENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_MulI6MpknENode__v_; +text: .text%__1cMPipeline_Use2t6MIIIpnUPipeline_Use_Element__v_: ad_i486_pipeline.o; +text: .text%__1cCosPpd_start_thread6FpnGThread__v_; +text: .text%__1cNsubL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cCosNcreate_thread6FpnGThread_n0AKThreadType_I_i_; +text: .text%__1cPRoundDoubleNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cXjava_lang_reflect_FieldIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cFParseScan_rerun_bytecode6M_i_; +text: .text%__1cXjava_lang_reflect_FieldThas_signature_field6F_i_; +text: .text%__1cKReflectionInew_type6FnMsymbolHandle_nLKlassHandle_pnGThread__nGHandle__; +text: .text%__1cDhpiEread6FipvI_I_: jvm.o; +text: .text%__1cXjava_lang_reflect_FieldPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cXjava_lang_reflect_FieldVhas_annotations_field6F_i_; +text: .text%__1cMstoreSSINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJvmSymbolsOsignature_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cXjava_lang_reflect_FieldJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cSmembar_releaseNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cXjava_lang_reflect_FieldGcreate6FpnGThread__nGHandle__; +text: .text%__1cPfieldDescriptorLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cXjava_lang_reflect_FieldIset_name6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_type6FpnHoopDesc_2_v_; +text: .text%__1cKReflectionJnew_field6FpnPfieldDescriptor_ipnGThread__pnHoopDesc__; +text: .text%__1cRtestI_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPhaseIdealLoopKdo_peeling6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%JVM_GetStackAccessControlContext; +text: .text%__1cIPipeline2t6MIIiIIiiiikpnSmachPipelineStages_3kpInMPipeline_Use__v_: ad_i486_pipeline.o; +text: .text%JVM_Read; +text: .text%__1cOJavaAssertionsNmatch_package6Fpkc_pn0AKOptionList__; +text: .text%__1cHciKlassOsuper_of_depth6MI_p0_; +text: .text%JVM_DesiredAssertionStatus; +text: .text%__1cWThreadLocalAllocBufferKinitialize6M_v_; +text: .text%__1cOcmpF_cc_P6NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOJavaAssertionsHenabled6Fpkci_i_; +text: .text%__1cOcmpF_cc_P6NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeFreloc6kM_i_; +text: .text%__1cWThreadLocalAllocBufferMinitial_size6F_I_; +text: .text%__1cQaddD_reg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%jni_GetStaticFieldID: jni.o; +text: .text%__1cOJavaAssertionsLmatch_class6Fpkc_pn0AKOptionList__: javaAssertions.o; +text: .text%__1cNinstanceKlassKjni_id_for6Mi_pnFJNIid__; +text: .text%__1cENodeEgetf6kM_f_; +text: .text%__1cLConvL2FNodeGOpcode6kM_i_; +text: .text%__1cMFlatProfilerJis_active6F_i_; +text: .text%__1cNThreadServiceKadd_thread6FpnKJavaThread_i_v_; +text: .text%__1cIjniIdMapGcreate6FnTinstanceKlassHandle__p0_; +text: .text%__1cQorI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cHThreadsDadd6FpnKJavaThread_i_v_; +text: .text%__1cKJavaThreadKinitialize6M_v_; +text: .text%__1cLConvL2DNodeGOpcode6kM_i_; +text: .text%__1cNloadConL0NodeFclone6kM_pnENode__; +text: .text%__1cLConvF2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cGParker2t6M_v_; +text: .text%__1cQThreadStatistics2t6M_v_; +text: .text%__1cIAddDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJimmL0OperFclone6kM_pnIMachOper__; +text: .text%__1cENodeGis_Con6kM_I_: loopnode.o; +text: .text%__1cNjni_functions6F_pknTJNINativeInterface___; +text: .text%__1cLConvF2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIjniIdMap2t6MpnMklassOopDesc_i_v_; +text: .text%__1cIjniIdMapRcompute_index_cnt6FnTinstanceKlassHandle__i_; +text: .text%__1cLjniIdBucket2t6MpnIjniIdMap_p0_v_; +text: .text%__1cUThreadSafepointStateGcreate6FpnKJavaThread__v_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: loopnode.o; +text: .text%__1cUThreadSafepointState2t6MpnKJavaThread__v_; +text: .text%__1cIGraphKitSprecision_rounding6MpnENode__2_; +text: .text%__1cCosMguard_memory6FpcI_i_; +text: .text%__1cKJavaThreadYcreate_stack_guard_pages6M_v_; +text: .text%__1cNaddL_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cOaddF24_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKJavaThread2t6MpFp0pnGThread__vI_v_; +text: .text%__1cSmulF24_reg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQorl_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPjava_lang_ClassOprimitive_type6FpnHoopDesc__nJBasicType__; +text: .text%jni_GetFloatArrayRegion: jni.o; +text: .text%__1cKJavaThreadDrun6M_v_; +text: .text%__1cKJavaThreadRthread_main_inner6M_v_; +text: .text%__1cQSystemDictionaryQjava_mirror_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cKReflectionbFbasic_type_mirror_to_basic_type6FpnHoopDesc_pnGThread__nJBasicType__; +text: .text%__1cM__kernel_cos6Fdd_d_: sharedRuntimeTrig.o; +text: .text%__1cJAssemblerFcmovl6Mn0AJCondition_pnMRegisterImpl_3_v_; +text: .text%__1cM__kernel_sin6Fddi_d_: sharedRuntimeTrig.o; +text: .text%__1cSleaP_eReg_immINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPhaseIdealLoopJclone_iff6MpnHPhiNode_pnNIdealLoopTree__pnIBoolNode__; +text: .text%__1cGThreadFstart6Fp0_v_; +text: .text%__1cNSharedRuntimeEdsin6Fd_d_; +text: .text%__1cNSharedRuntimeEdcos6Fd_d_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: multnode.o; +text: .text%jni_CallStaticVoidMethod: jni.o; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnPPerfLongCounter__; +text: .text%__1cSshlL_eReg_1_31NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNsymbolOopDescWas_klass_external_name6kM_pkc_; +text: .text%__1cNdecI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTconvF2I_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNcmovP_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEsbbl6MnHAddress_i_v_; +text: .text%__1cFKlassNexternal_name6kM_pkc_; +text: .text%__1cHRegMask2t6Miiiii_v_: ad_i486_expand.o; +text: .text%__1cLConvD2INodeJideal_reg6kM_I_: classes.o; +text: .text%JVM_IsArrayClass; +text: .text%__1cVloadConL_low_onlyNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cHTypePtrFxdual6kM_pknEType__; +text: .text%__1cSMachBreakpointNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlass.o; +text: .text%__1cICmpDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cVMoveL2D_reg_stackNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQshrI_eReg_CLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRmulI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: output.o; +text: .text%JVM_GetClassName; +text: .text%__1cSsarL_eReg_1_31NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLStringTableGintern6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cQorl_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2D_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cCosMset_priority6FpnGThread_nOThreadPriority__nIOSReturn__; +text: .text%__1cFJNIid2t6MpnMklassOopDesc_ip0_v_; +text: .text%__1cENodeHdel_out6Mp0_v_: mulnode.o; +text: .text%__1cSstring_compareNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateM_sub_Op_RegF6MpknENode__v_; +text: .text%__1cPmovI_nocopyNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulFNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cNinstanceKlassPjni_id_for_impl6FnTinstanceKlassHandle_i_pnFJNIid__; +text: .text%__1cOcmpF_cc_P6NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKRelocationYpd_get_address_from_code6M_pC_; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_: interpreterRuntime.o; +text: .text%__1cSmulF24_reg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cbFunnecessary_membar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmulF24_reg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNtestU_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cbFunnecessary_membar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGThreadMset_priority6Fp0nOThreadPriority__v_; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerDjmp6MpnMRegisterImpl_nJrelocInfoJrelocType__v_; +text: .text%__1cJArrayDataKcell_count6M_i_: ciMethodData.o; +text: .text%JVM_Open; +text: .text%__1cOmulIS_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_ThreadIpriority6FpnHoopDesc__nOThreadPriority__; +text: .text%__1cQjava_lang_ThreadJstackSize6FpnHoopDesc__x_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNObjectMonitorJnotifyAll6MpnGThread__v_; +text: .text%__1cNPerfByteArray2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_i_v_; +text: .text%JVM_StartThread; +text: .text%__1cQComputeCallStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cMthread_entry6FpnKJavaThread_pnGThread__v_: jvm.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorJpass_long6M_v_: interpreterRuntime.o; +text: .text%__1cOMacroAssemblerKnull_check6MpnMRegisterImpl_i_v_; +text: .text%__1cGICStubLdestination6kM_pC_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cYcmpL_zero_flags_LEGTNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLConvD2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRxorI_eReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRInlineCacheBufferVic_buffer_entry_point6FpC_1_; +text: .text%__1cKJavaThreadHprepare6MpnI_jobject_nOThreadPriority__v_; +text: .text%jni_GetStaticObjectField: jni.o; +text: .text%__1cQmulI_mem_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cScompI_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOstackSlotDOperFscale6kM_i_: ad_i486.o; +text: .text%__1cOstackSlotDOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cMmulD_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOstackSlotDOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cXPartialSubtypeCheckNodeGOpcode6kM_i_; +text: .text%__1cQmulD_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOstackSlotPOperFscale6kM_i_: ad_i486.o; +text: .text%__1cOstackSlotPOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOstackSlotPOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOGenerateOopMapMdo_checkcast6M_v_; +text: .text%__1cQmulI_mem_immNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPconvI2D_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRandL_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTAbstractInterpreterWlayout_activation_impl6FpnNmethodOopDesc_iiiipnFframe_4i_i_; +text: .text%JVM_TotalMemory; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: generateOptoStub.o; +text: .text%JVM_FreeMemory; +text: .text%__1cObox_handleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMaxINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cTshlL_eReg_32_63NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQmulI_mem_immNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cQmulI_mem_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvI2F_SSFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJScopeDescTdecode_scope_values6Mi_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cUThreadSafepointStateHdestroy6FpnKJavaThread__v_; +text: .text%__1cKJavaThread2T6M_v_; +text: .text%__1cKJavaThreadYremove_stack_guard_pages6M_v_; +text: .text%__1cIOSThreadKpd_destroy6M_v_; +text: .text%__1cIOSThread2T6M_v_; +text: .text%__1cCosLfree_thread6FpnIOSThread__v_; +text: .text%__1cCosOunguard_memory6FpcI_i_; +text: .text%__1cHRetDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cGParker2T6M_v_; +text: .text%__1cQorI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRmulI_imm_highNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsGremove6FpnKJavaThread__v_; +text: .text%__1cKJavaThreadEexit6Mi_v_; +text: .text%__1cKloadUBNodeFreloc6kM_i_; +text: .text%__1cLensure_join6FpnKJavaThread__v_: thread.o; +text: .text%__1cGThread2T5B6M_v_; +text: .text%__1cNThreadServiceWcurrent_thread_exiting6FpnKJavaThread__v_; +text: .text%__1cNThreadServiceNremove_thread6FpnKJavaThread_i_v_; +text: .text%__SLIP.DELETER__A: thread.o; +text: .text%__1cUThreadSafepointState2T6M_v_; +text: .text%__1cFStateT_sub_Op_RoundDouble6MpknENode__v_; +text: .text%__1cQjava_lang_ThreadNset_stillborn6FpnHoopDesc__v_; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: klassKlass.o; +text: .text%__1cIGraphKitTdprecision_rounding6MpnENode__2_; +text: .text%__1cHOrLNodeGOpcode6kM_i_; +text: .text%__1cWconstantPoolCacheKlassOklass_oop_size6kM_i_: cpCacheKlass.o; +text: .text%__1cRconstantPoolKlassOklass_oop_size6kM_i_: constantPoolKlass.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: library_call.o; +text: .text%__1cSobjArrayKlassKlassOklass_oop_size6kM_i_: objArrayKlassKlass.o; +text: .text%__1cLmethodKlassOklass_oop_size6kM_i_: methodKlass.o; +text: .text%__1cPmethodDataKlassOklass_oop_size6kM_i_: methodDataKlass.o; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: generateOopMap.o; +text: .text%__1cTcmovII_reg_EQdDNENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTtypeArrayKlassKlassOklass_oop_size6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cSTailCalljmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: arrayKlassKlass.o; +text: .text%__1cSinstanceKlassKlassOklass_oop_size6kM_i_: instanceKlassKlass.o; +text: .text%__1cNcmovL_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQconstMethodKlassOklass_oop_size6kM_i_: constMethodKlass.o; +text: .text%__1cLsymbolKlassOklass_oop_size6kM_i_: symbolKlass.o; +text: .text%__1cMTailCallNode2t6MpnENode_222222_v_; +text: .text%__1cKstoreLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWstatic_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cFStateQ_sub_Op_TailCall6MpknENode__v_; +text: .text%__1cVcompiledICHolderKlassOklass_oop_size6kM_i_: compiledICHolderKlass.o; +text: .text%__1cLeDIRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cPmovI_nocopyNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSTailCalljmpIndNodeFreloc6kM_i_; +text: .text%__1cQChunkPoolCleanerEtask6M_v_: allocation.o; +text: .text%__1cNandI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cFStateO_sub_Op_StoreF6MpknENode__v_; +text: .text%__1cQaddD_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTsarL_eReg_32_63NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_CallObjectMethod: jni.o; +text: .text%__1cNaddP_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNaddP_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovI_regUNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLOptoRuntimeMrethrow_Type6F_pknITypeFunc__; +text: .text%__1cENodeJis_MemBar6kM_pknKMemBarNode__: classes.o; +text: .text%__1cMstoreSSPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUGenericGrowableArrayUclear_and_deallocate6M_v_; +text: .text%__1cRandI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cSsarL_eReg_1_31NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cRCardTableModRefBSPclear_MemRegion6MnJMemRegion__v_; +text: .text%__1cHThreadsHoops_do6FpnKOopClosure__v_; +text: .text%__1cRInlineCacheBufferLnew_ic_stub6F_pnGICStub__; +text: .text%__1cRInlineCacheBufferWcreate_transition_stub6FpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cVMoveL2D_reg_stackNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cObox_handleNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cRInlineCacheBufferXassemble_ic_buffer_code6FpCpnHoopDesc_1_v_; +text: .text%__1cGICStubIset_stub6MpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cTcmovII_reg_LEGTNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFJNIidHoops_do6MpnKOopClosure__v_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: onStackReplacement.o; +text: .text%__1cObox_handleNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: interpreterRuntime.o; +text: .text%__1cSaddD_reg_roundNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQorI_eReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmulF24_reg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSaddD_reg_roundNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOmulF24_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHi2bNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQjava_lang_StringbHcreate_from_platform_depended_str6FpkcpnGThread__nGHandle__; +text: .text%__1cOLibraryCallKitYinline_native_time_funcs6Mi_i_; +text: .text%__1cJHashtableHoops_do6MpnKOopClosure__v_; +text: .text%__1cLOptoRuntimeYcurrent_time_millis_Type6F_pknITypeFunc__; +text: .text%__1cIPSOldGenHcompact6M_v_; +text: .text%__1cJHashtableGunlink6MpnRBoolObjectClosure__v_; +text: .text%__1cOcmpF_cc_P6NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIPSOldGenPadjust_pointers6M_v_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: ciTypeFlow.o; +text: .text%__1cNmulI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cKdirectOperFscale6kM_i_: ad_i486_clone.o; +text: .text%__1cQorI_eReg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQorI_eReg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQObjectStartArrayFreset6M_v_; +text: .text%__1cFParseVcatch_call_exceptions6MrnYciExceptionHandlerStream__v_; +text: .text%__1cMsubD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJvmSymbolsHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cSReferenceProcessorHoops_do6MpnKOopClosure__v_; +text: .text%__1cIci2bNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIregDOperFclone6kM_pnIMachOper__; +text: .text%JVM_MonitorNotifyAll; +text: .text%__1cLConvI2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKConv2BNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNmaxI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMelapsedTimer2t6M_v_: phase.o; +text: .text%__1cSvframeArrayElementDbci6kM_i_; +text: .text%__1cIDivDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKarrayKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cODeoptimizationYquery_update_method_data6FnQmethodDataHandle_in0ALDeoptReason_rIri4_pnLProfileData__; +text: .text%__1cKstoreDNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNminI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMnegD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNPhaseRegAllocHset_oop6MpknENode_i_v_; +text: .text%__1cKarrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cFStateM_sub_Op_MaxI6MpknENode__v_; +text: .text%__1cXJNI_ArgumentPusherVaArgJget_float6M_v_: jni.o; +text: .text%__1cWis_positive_zero_float6Ff_i_; +text: .text%__1cTcmovII_reg_LTGENodeQuse_cisc_RegMask6M_v_; +text: .text%__1cSPerfStringConstant2t6MnJCounterNS_pkc3_v_; +text: .text%__1cIMulDNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cQorI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPPerfDataManagerWcreate_string_constant6FnJCounterNS_pkc3pnGThread__pnSPerfStringConstant__; +text: .text%__1cOMacroAssemblerFleave6M_v_; +text: .text%__1cMloadConDNodeFclone6kM_pnENode__; +text: .text%JVM_GetInheritedAccessControlContext; +text: .text%JVM_NativePath; +text: .text%__1cQjava_lang_ThreadbGinherited_access_control_context6FpnHoopDesc__2_; +text: .text%__1cVLoaderConstraintTableJnew_entry6MIpnNsymbolOopDesc_pnMklassOopDesc_ii_pnVLoaderConstraintEntry__; +text: .text%__1cVloadConL_low_onlyNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIimmDOperFclone6kM_pnIMachOper__; +text: .text%__1cIMachNodeOmemory_operand6kM_pknIMachOper__: ad_i486_misc.o; +text: .text%__1cbIjava_security_AccessControlContextGcreate6FnOobjArrayHandle_inGHandle_pnGThread__pnHoopDesc__; +text: .text%__1cHnmethodNis_osr_method6kM_i_: nmethod.o; +text: .text%__1cNmulI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQorl_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQsalI_eReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOaddF24_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_: callnode.o; +text: .text%__1cFStateT_sub_Op_CallRuntime6MpknENode__v_; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__: callnode.o; +text: .text%__1cOcmovI_regUNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cCosXnon_memory_address_word6F_pc_; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodDataKlass.o; +text: .text%__1cQsalI_eReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodDataKlass.o; +text: .text%__1cVCallRuntimeDirectNodeFreloc6kM_i_; +text: .text%__1cSdivD_reg_roundNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cISubFNodeGOpcode6kM_i_; +text: .text%__1cNandI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveL2D_reg_stackNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cVCallRuntimeDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOmulF24_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cOcmovI_regUNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRandL_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTcmovII_reg_LTGENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTshrL_eReg_32_63NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOcmpF_cc_P6NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOaddF24_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cSaddF24_reg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNcmovP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovI_regUNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cSmulF24_reg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKCompiledICOset_cached_oop6MpnHoopDesc__v_; +text: .text%__1cCosHSolarisKmmap_chunk6FpcIii_2_; +text: .text%__1cKstoreFNodeFreloc6kM_i_; +text: .text%jni_GetStaticMethodID: jni.o; +text: .text%__1cZInterpreterMacroAssemblerWupdate_mdp_by_constant6MpnMRegisterImpl_i_v_; +text: .text%__1cKCompiledICMstub_address6kM_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorRset_unimplemented6Mi_v_; +text: .text%__1cSstoreD_roundedNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_i_v_; +text: .text%__1cKstoreFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKCodeBufferVinsert_float_constant6Mf_pC_; +text: .text%__1cWroundFloat_mem_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOsubF24_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSaddF24_reg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSaddD_reg_roundNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMdecI_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTconvF2I_reg_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPciInstanceKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cISubDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKReflectionTunbox_for_primitive6FpnHoopDesc_pnGjvalue_pnGThread__nJBasicType__; +text: .text%__1cMloadConDNodeFreloc6kM_i_; +text: .text%__1cPMultiBranchDataScompute_cell_count6FpnOBytecodeStream__i_; +text: .text%__1cJloadDNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQaddD_reg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMdecI_memNodeFreloc6kM_i_; +text: .text%__1cTshlL_eReg_32_63NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFTypeDFxdual6kM_pknEType__; +text: .text%__1cXjava_lang_boxing_objectJget_value6FpnHoopDesc_pnGjvalue__nJBasicType__; +text: .text%__1cWCallLeafNoFPDirectNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConDNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cOtailjmpIndNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMdecI_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOptoRuntimeWresolve_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cLTypeInstPtrLmirror_type6kM_pnGciType__; +text: .text%__1cSmulF24_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationVtrap_state_add_reason6Fii_i_; +text: .text%__1cKstoreINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMdivD_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJLoadBNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pF3_v3_v_; +text: .text%__1cOCompilerThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%__1cYcmpL_zero_flags_LTGENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVcompiledICHolderKlassSoop_being_unloaded6MpnRBoolObjectClosure_pnHoopDesc__i_; +text: .text%__1cLRuntimeStub2n6FII_pv_; +text: .text%__1cYcmpL_zero_flags_LTGENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLRuntimeStubQnew_runtime_stub6FpkcpnKCodeBuffer_ipnJOopMapSet_i_p0_; +text: .text%__1cLRuntimeStub2t6MpkcpnKCodeBuffer_iipnJOopMapSet_i_v_; +text: .text%__1cOstoreF_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2F_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLloadSSDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvI2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQshrL_eReg_CLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQsalL_eReg_CLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNSafePointNodeQpeek_monitor_obj6kM_pnENode__; +text: .text%__1cVcompiledICHolderKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cHnmethodbAmake_not_entrant_or_zombie6Mi_v_; +text: .text%__1cFTypeDFempty6kM_i_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: methodDataOop.o; +text: .text%__1cPconvL2D_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKdirectOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486_clone.o; +text: .text%__1cKdirectOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486_clone.o; +text: .text%__1cPoldgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cKdirectOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486_clone.o; +text: .text%__1cKdirectOperLdisp_is_oop6kM_i_: ad_i486_clone.o; +text: .text%__1cLConvD2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cVcompiledICHolderKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cPconvI2F_SSFNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cRmulI_imm_highNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIDivLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cXpartialSubtypeCheckNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFn0AJOperation__v4_v_; +text: .text%__1cYinternal_word_RelocationGtarget6M_pC_; +text: .text%__1cRxorI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXpartialSubtypeCheckNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerEsarl6MpnMRegisterImpl_i_v_; +text: .text%__1cIModLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cDhpiFclose6Fi_i_: jvm.o; +text: .text%__1cFParsePdo_monitor_exit6M_v_; +text: .text%__1cFStateN_sub_Op_LoadD6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_MulD6MpknENode__v_; +text: .text%__1cSMachCallNativeNodePret_addr_offset6M_i_; +text: .text%__1cPPerfDataManagerUcreate_long_constant6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongConstant__; +text: .text%__1cOsubF24_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: deoptimization.o; +text: .text%__1cTAbstractInterpreterMreturn_entry6FnITosState_i_pC_; +text: .text%JVM_Close; +text: .text%__1cLOptoRuntimeRnew_objArray_Type6F_pknITypeFunc__; +text: .text%__1cLConvD2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNSafePointNodeQpeek_monitor_box6kM_pnENode__; +text: .text%__1cIMulDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOmulIS_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovL_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_i_v_; +text: .text%__1cNdivI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulFNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cTunsafe_intrinsic_id6FpnNsymbolOopDesc_1_nNmethodOopDescLIntrinsicId__; +text: .text%__1cSaddF24_reg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQComputeCallStackIdo_float6M_v_: generateOopMap.o; +text: .text%__1cQComputeCallStackHdo_byte6M_v_: generateOopMap.o; +text: .text%__1cFParseMdo_anewarray6M_v_; +text: .text%__1cIAddFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJLoadDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cTinc_decompile_count6FpnHnmethod__v_: nmethod.o; +text: .text%__1cXjava_lang_reflect_FieldFclazz6FpnHoopDesc__2_; +text: .text%__1cMsubD_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cOcmpD_cc_P6NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTcmovII_reg_EQdDNENodeQuse_cisc_RegMask6M_v_; +text: .text%__1cFStateM_sub_Op_CmpD6MpknENode__v_; +text: .text%__1cXjava_lang_reflect_FieldJmodifiers6FpnHoopDesc__i_; +text: .text%__1cJAssemblerEmovb6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_only6MnITosState__v_; +text: .text%__1cSCallLeafDirectNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPmovI_nocopyNodeErule6kM_I_: ad_i486_misc.o; +text: .text%jni_NewObjectV: jni.o; +text: .text%__1cSCallLeafDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIci2bNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSsarL_eReg_1_31NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRcmpFastUnlockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovI_regUNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOtailjmpIndNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLOopMapCache2t6M_v_; +text: .text%__1cGICStubKcached_oop6kM_pnHoopDesc__; +text: .text%__1cJSubFPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRInlineCacheBufferUic_buffer_cached_oop6FpC_pnHoopDesc__; +text: .text%__1cMdivD_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTconvF2I_reg_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSmulF24_reg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cTmembar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC2_v_; +text: .text%jni_EnsureLocalCapacity; +text: .text%__1cTcmovII_reg_LEGTNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPfieldDescriptorUdouble_initial_value6kM_d_; +text: .text%__1cQAbstractCompilerMsupports_osr6M_i_: c2compiler.o; +text: .text%__1cKEntryPoint2t6M_v_; +text: .text%__1cKstoreINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNTemplateTableGbranch6Fii_v_; +text: .text%__1cXcmpL_reg_flags_LTGENodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSMachBreakpointNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQshrI_eReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cHCompile2t6MpnFciEnv_pF_pknITypeFunc_pCpkciiii_v_; +text: .text%__1cKemit_break6FrnKCodeBuffer__v_; +text: .text%__1cQshrI_eReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvI2F_SSF_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIGraphKitIgen_stub6MpCpkciii_v_; +text: .text%__1cIciMethodVget_osr_flow_analysis6Mi_pnKciTypeFlow__; +text: .text%__1cLloadSSINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQmulD_reg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLResourceObj2n6FIn0APallocation_type__pv_; +text: .text%__1cMloadConFNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cQmulD_reg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIAddFNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cINegDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSmulF24_reg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJlog2_long6Fx_i_: mulnode.o; +text: .text%__1cJAssemblerEsbbl6MpnMRegisterImpl_i_v_; +text: .text%__1cHnmethodbCcan_not_entrant_be_converted6M_i_; +text: .text%__1cMStartOSRNodeKosr_domain6F_pknJTypeTuple__; +text: .text%__1cNCallGeneratorHfor_osr6FpnIciMethod_i_p0_; +text: .text%__1cINegFNodeGOpcode6kM_i_; +text: .text%__1cQorl_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVeADXRegL_low_onlyOperFclone6kM_pnIMachOper__; +text: .text%__1cZInterpreterMacroAssemblerUprofile_taken_branch6MpnMRegisterImpl_2_v_; +text: .text%__1cKimmL32OperFclone6kM_pnIMachOper__; +text: .text%__1cLOptoRuntimeNgenerate_stub6FpnFciEnv_pF_pknITypeFunc_pCpkciiii_8_; +text: .text%__1cVloadConL_low_onlyNodeFclone6kM_pnENode__; +text: .text%__1cHciKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cFParseWload_interpreter_state6MpnENode_2_v_; +text: .text%__1cVMoveL2D_reg_stackNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMStartOSRNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cPconvI2D_regNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cTshlL_eReg_32_63NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateO_sub_Op_StoreD6MpknENode__v_; +text: .text%__1cPmovI_nocopyNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsYis_supported_jni_version6Fi_C_; +text: .text%__1cFStateM_sub_Op_ConF6MpknENode__v_; +text: .text%__1cNcmovI_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKConv2BNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJLoadLNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC22i_v_; +text: .text%__1cNcmovI_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKarrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cIci2bNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSdivD_reg_roundNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cWroundFloat_mem_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOmulF24_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcompiledVFrameHraw_bci6kM_i_; +text: .text%__1cFTypeFFxdual6kM_pknEType__; +text: .text%__1cODeoptimizationbJupdate_method_data_from_interpreter6FnQmethodDataHandle_ii_v_; +text: .text%__1cOcompiledVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cSvframeArrayElementHfill_in6MpnOcompiledVFrame__v_; +text: .text%__1cRComputeEntryStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cTcmovII_reg_LTGENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQorI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cFframebHinterpreter_frame_set_monitor_end6MpnPBasicObjectLock__v_; +text: .text%__1cOstoreF_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvF2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cPmovP_nocopyNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFframebBinterpreter_frame_sender_sp6kM_pi_; +text: .text%__1cNcmovL_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cTAbstractInterpreterPsize_activation6FpnNmethodOopDesc_iiiii_i_; +text: .text%__1cJScopeDescImonitors6M_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cJScopeDescLexpressions6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cJScopeDescGlocals6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cJScopeDescVdecode_monitor_values6Mi_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cOcompiledVFrameGlocals6kM_pnUStackValueCollection__; +text: .text%__1cOcompiledVFrameLexpressions6kM_pnUStackValueCollection__; +text: .text%__1cTcmovII_reg_EQdDNENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSvframeArrayElementPunpack_on_stack6MiipnFframe_ii_v_; +text: .text%__1cTconvD2I_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFframebCinterpreter_frame_set_locals6Mpi_v_; +text: .text%__1cFframebCinterpreter_frame_set_method6MpnNmethodOopDesc__v_; +text: .text%__1cTAbstractInterpreterQcontinuation_for6FpnNmethodOopDesc_pCiiri_3_; +text: .text%__1cTAbstractInterpreterRTosState_as_index6FnITosState__i_; +text: .text%__1cTAbstractInterpreterRlayout_activation6FpnNmethodOopDesc_iiiipnFframe_4i_v_; +text: .text%__1cSvframeArrayElementNon_stack_size6kMiiii_i_; +text: .text%__1cSInterpreterRuntimeJnote_trap6FpnKJavaThread_ipnGThread__v_; +text: .text%__1cICmpFNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRmulI_imm_highNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcompiledVFrameImonitors6kM_pnNGrowableArray4CpnLMonitorInfo____; +text: .text%__1cRsubL_eReg_memNodeFreloc6kM_i_; +text: .text%__1cMTailJumpNodeKmatch_edge6kMI_I_; +text: .text%__1cFStateP_sub_Op_ConvF2D6MpknENode__v_; +text: .text%__1cCosNcommit_memory6FpcI_i_; +text: .text%__1cScompP_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cXpartialSubtypeCheckNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNinstanceKlassPadd_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cHThreadsbMis_supported_jni_version_including_1_16Fi_C_; +text: .text%__1cFStateM_sub_Op_DivL6MpknENode__v_; +text: .text%__1cNsubL_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cLimmI_32OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cNTemplateTableLindex_check6FpnMRegisterImpl_2_v_; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: klassKlass.o; +text: .text%__1cSCompiledStaticCallMset_to_clean6M_v_; +text: .text%__1cIUniverseWupdate_heap_info_at_gc6F_v_; +text: .text%Unsafe_DefineClass1; +text: .text%JVM_GetComponentType; +text: .text%JVM_DefineClass; +text: .text%__1cKJavaThreadbFdeoptimized_wrt_marked_nmethods6M_v_; +text: .text%__1cNmodI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLOptoRuntimeNfetch_monitor6FipnJBasicLock_pC_pnHoopDesc__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cLStrCompNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRaddD_reg_imm1NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNTemplateTableOpatch_bytecode6FnJBytecodesECode_pnMRegisterImpl_4i_v_; +text: .text%__1cJAssemblerGfrstor6MnHAddress__v_; +text: .text%JVM_GetCPFieldModifiers; +text: .text%__1cSUnsafe_DefineClass6FpnHJNIEnv__pnI_jstring_pnL_jbyteArray_iipnI_jobject_7_pnH_jclass__: unsafe.o; +text: .text%__1cJBasicLockHmove_to6MpnHoopDesc_p0_v_; +text: .text%__1cSObjectSynchronizerOinflate_helper6FpnHoopDesc__pnNObjectMonitor__: synchronizer.o; +text: .text%__1cSdivD_reg_roundNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvI2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNSharedRuntimebJcontinuation_for_implicit_exception6FpnKJavaThread_pCn0AVImplicitExceptionKind__3_; +text: .text%__1cKReflectionUarray_component_type6FpnHoopDesc_pnGThread__2_; +text: .text%__1cOsubF24_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2D_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLimmI_24OperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cYjava_lang_reflect_MethodEslot6FpnHoopDesc__i_; +text: .text%__1cLloadSSDNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cKPSYoungGenKprecompact6M_v_; +text: .text%__1cYjava_lang_reflect_MethodFclazz6FpnHoopDesc__2_; +text: .text%__1cKPSYoungGenPadjust_pointers6M_v_; +text: .text%__1cRCardTableModRefBSFclear6MnJMemRegion__v_; +text: .text%__1cOstackSlotPOperFclone6kM_pnIMachOper__; +text: .text%__1cLPSMarkSweepRmark_sweep_phase26F_v_; +text: .text%__1cKPSYoungGenHcompact6M_v_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase16Frii_v_; +text: .text%Unsafe_AllocateInstance; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceRefKlass.o; +text: .text%__1cYjava_lang_reflect_MethodLreturn_type6FpnHoopDesc__2_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase36F_v_; +text: .text%__1cRInlineCacheBufferSic_destination_for6FpnKCompiledIC__pC_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase46F_v_; +text: .text%__1cPconvL2F_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cYjava_lang_reflect_MethodPparameter_types6FpnHoopDesc__2_; +text: .text%__1cLPSMarkSweepbAreset_millis_since_last_gc6F_v_; +text: .text%__1cUPSAdaptiveSizePolicyUmajor_collection_end6MInHGCCauseFCause__v_; +text: .text%__1cSDeoptimizationBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cMsubD_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJCodeCacheLgc_epilogue6F_v_; +text: .text%__1cJMarkSweepNrestore_marks6F_v_; +text: .text%__1cQUncommonTrapBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cNExceptionBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cJCodeCacheMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cYinternal_word_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cJCodeCacheHoops_do6FpnKOopClosure__v_; +text: .text%__1cRCardTableModRefBSEis_a6MnKBarrierSetEName__i_: cardTableExtension.o; +text: .text%__1cMset_property6FnGHandle_pkc2pnGThread__v_: jvm.o; +text: .text%__1cJCodeCacheLgc_prologue6F_v_; +text: .text%__1cHBoxNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSReferenceProcessorPoops_do_statics6FpnKOopClosure__v_; +text: .text%__1cQorl_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLPSMarkSweepQinvoke_no_policy6Fpii_v_; +text: .text%__1cHJNIEnv_JNewObject6MpnH_jclass_pnK_jmethodID_E_pnI_jobject__: jni.o; +text: .text%__1cObox_handleNodeFclone6kM_pnENode__; +text: .text%__1cLPSMarkSweepPallocate_stacks6F_v_; +text: .text%jni_NewDirectByteBuffer; +text: .text%__1cMdivD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLPSMarkSweepRdeallocate_stacks6F_v_; +text: .text%__1cRInlineCacheBufferOinit_next_stub6F_v_; +text: .text%__1cKReflectionNinvoke_method6FpnHoopDesc_nGHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cMincI_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUPSAdaptiveSizePolicyWmajor_collection_begin6M_v_; +text: .text%__1cJMarkSweepMadjust_marks6F_v_; +text: .text%__1cJMarkSweepXfollow_weak_klass_links6F_v_; +text: .text%__1cQSystemDictionaryMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: symbolKlass.o; +text: .text%__1cNmodL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%jni_AllocObject: jni.o; +text: .text%__1cZInterpreterMacroAssemblerQtest_mdp_data_at6MpnMRegisterImpl_i2rnFLabel__v_; +text: .text%__1cPcmpFastLockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQSystemDictionaryYalways_strong_classes_do6FpnKOopClosure__v_; +text: .text%__1cVLoaderConstraintTableYpurge_loader_constraints6MpnRBoolObjectClosure__v_; +text: .text%__1cVLoaderConstraintTableYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cUPSMarkSweepDecoratorbIset_destination_decorator_perm_gen6F_v_; +text: .text%__1cFStateL_sub_Op_Box6MpknENode__v_; +text: .text%__1cUPSMarkSweepDecoratorbHset_destination_decorator_tenured6F_v_; +text: .text%JVM_InvokeMethod; +text: .text%__1cQSystemDictionaryValways_strong_oops_do6FpnKOopClosure__v_; +text: .text%__1cSmulF24_reg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cFKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: symbolKlass.o; +text: .text%__1cKDictionaryMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cHThreadsLnmethods_do6F_v_; +text: .text%__1cIPSOldGenKprecompact6M_v_; +text: .text%__1cHThreadsLgc_epilogue6F_v_; +text: .text%__1cHThreadsLgc_prologue6F_v_; +text: .text%__1cJPSPermGenQcompute_new_size6MI_v_; +text: .text%__1cXpartialSubtypeCheckNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cZInterpreterMacroAssemblerYprofile_not_taken_branch6MpnMRegisterImpl__v_; +text: .text%__1cHi2bNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQSystemDictionaryPplaceholders_do6FpnKOopClosure__v_; +text: .text%__1cFStateM_sub_Op_ModL6MpknENode__v_; +text: .text%__1cSaddF24_reg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJPSPermGenKprecompact6M_v_; +text: .text%__1cHi2bNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTleaPIdxScaleOffNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFj_not6FnNTemplateTableJCondition__nJAssemblerJCondition__: templateTable_i486.o; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cVMoveF2I_reg_stackNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMincI_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKDictionaryYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cNTemplateTableMlocals_index6FpnMRegisterImpl_i_v_; +text: .text%__1cQaddD_reg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFn0AJCondition__v4_v_; +text: .text%__1cVMoveL2D_reg_stackNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMincI_memNodeFreloc6kM_i_; +text: .text%__1cOaddF24_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_pnNsymbolOopDesc_pkc_nGHandle__; +text: .text%__1cMnegF_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJStubQdDueueMremove_first6Mi_v_; +text: .text%__1cSsarL_eReg_1_31NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%JVM_NewArray; +text: .text%__1cLConvF2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOcmpF_cc_P6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReflectionRreflect_new_array6FpnHoopDesc_ipnGThread__pnMarrayOopDesc__; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlass.o; +text: .text%__1cFStateM_sub_Op_MulF6MpknENode__v_; +text: .text%__1cTconvF2I_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTshlL_eReg_32_63NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerFpopad6M_v_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cNTemplateTableHconvert6F_v_; +text: .text%__1cZInterpreterMacroAssemblerNunlock_object6MpnMRegisterImpl__v_; +text: .text%__1cSmulF24_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovI_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cRmethodDataOopDescRbci_to_extra_data6Mii_pnLProfileData__; +text: .text%__1cIDivDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTconvD2I_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNcmovI_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFStateP_sub_Op_ConvD2I6MpknENode__v_; +text: .text%__1cLMoveF2INodeGOpcode6kM_i_; +text: .text%__1cPconvI2D_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJStubQdDueueKremove_all6M_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: compiledICHolderKlass.o; +text: .text%__1cVVM_ParallelGCSystemGC2t6MI_v_; +text: .text%__1cOcmovI_regUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIregFOperFclone6kM_pnIMachOper__; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cLCastP2INodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cPconvL2D_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPmovP_nocopyNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPfieldDescriptorTfloat_initial_value6kM_f_; +text: .text%__1cJArgumentsQPropertyList_add6FppnOSystemProperty_2_v_; +text: .text%__1cSaddF24_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKoopFactoryUnew_compiledICHolder6FnMmethodHandle_nLKlassHandle_pnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cMciArrayKlassRbase_element_type6M_pnGciType__; +text: .text%__1cHCompileWget_MethodAccessorImpl6M_pnPciInstanceKlass__; +text: .text%__1cSInterpreterRuntimeQcreate_exception6FpnKJavaThread_pc3_v_; +text: .text%__1cXjava_lang_reflect_FieldEslot6FpnHoopDesc__i_; +text: .text%__1cSmulF24_reg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cVcompiledICHolderKlassIallocate6MpnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cLTypeInstPtrOxmeet_unloaded6kMpk0_2_; +text: .text%__1cScompP_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cHCompileRget_Method_invoke6M_pnIciMethod__; +text: .text%__1cLPSMarkSweepGinvoke6Fpii_v_; +text: .text%JVM_GC; +text: .text%__1cVVM_ParallelGCSystemGCEdoit6M_v_; +text: .text%__1cXpartialSubtypeCheckNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLClassLoaderSget_system_package6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cWroundFloat_mem_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQinitialize_class6FnMsymbolHandle_pnGThread__v_: thread.o; +text: .text%__1cKScopeValueLis_location6kM_i_: debugInfo.o; +text: .text%__1cVVM_ParallelGCSystemGCEname6kM_pkc_: vm_operations.o; +text: .text%__1cRfind_field_offset6FpnI_jobject_ipnGThread__i_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: compiledICHolderKlass.o; +text: .text%__1cOLibraryCallKitbDis_method_invoke_or_aux_frame6MpnIJVMState__i_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_pnGThread__v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: objArrayKlass.o; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cUParallelScavengeHeapHcollect6MnHGCCauseFCause__v_; +text: .text%JVM_GetSystemPackage; +text: .text%__1cLStatSamplerTget_system_property6FpkcpnGThread__2_; +text: .text%__1cPconvL2D_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cETypeJis_finite6kM_i_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: compiledICHolderKlass.o; +text: .text%__1cXPartialSubtypeCheckNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSaddF24_reg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cCosNcommit_memory6FpcII_i_; +text: .text%__1cOresolve_symbol6Fpkc_pC_: os_solaris.o; +text: .text%__1cLloadSSINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: klassKlass.o; +text: .text%__1cNloadConL0NodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cPMultiBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%JVM_RawMonitorCreate; +text: .text%__1cImulINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: interpreter.o; +text: .text%__1cQConstantIntValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cOLibraryCallKitXinline_string_compareTo6M_i_; +text: .text%__1cImulINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOMacroAssemblerVreset_last_Java_frame6MpnMRegisterImpl_i_v_; +text: .text%__1cOMacroAssemblerTset_last_Java_frame6MpnMRegisterImpl_22pC_v_; +text: .text%__1cOMacroAssemblerRcall_VM_leaf_base6MpCi_v_; +text: .text%__1cSstring_compareNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cISubFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFStateP_sub_Op_StrComp6MpknENode__v_; +text: .text%__1cIcp2bNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateP_sub_Op_ConvI2F6MpknENode__v_; +text: .text%__1cOMacroAssemblerPempty_FPU_stack6M_v_; +text: .text%jni_GetStringRegion: jni.o; +text: .text%__1cQConstantIntValuePis_constant_int6kM_i_: debugInfo.o; +text: .text%__1cICodeBlobZis_at_poll_or_poll_return6MpC_i_; +text: .text%__1cSmulF24_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQaddD_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceRefKlass.o; +text: .text%__1cIAddFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cHOrLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cScompP_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHBoxNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvL2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLConvL2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cTcmovII_reg_EQdDNENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQinstanceRefKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceRefKlass.o; +text: .text%__1cLvframeArrayPunpack_to_stack6MrnFframe_i_v_; +text: .text%__1cLvframeArrayZdeallocate_monitor_chunks6M_v_; +text: .text%__1cLvframeArrayHfill_in6MpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pknLRegisterMap_i_v_; +text: .text%__1cLvframeArrayIallocate6FpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pnLRegisterMap_nFframe_9A9A9A_p0_; +text: .text%__1cOstoreF_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVcompiledICHolderKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNcmovI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cImulINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsalL_eReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cbCAbstractInterpreterGeneratorUset_wide_entry_point6MpnITemplate_rpC_v_; +text: .text%Unsafe_CompareAndSwapInt; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_data_at6MpnMRegisterImpl_i2_v_; +text: .text%__1cRsubI_eReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cQsalL_eReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYcmpL_zero_flags_LTGENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cODeoptimizationLUnrollBlock2t6MiiiiipippCnJBasicType__v_; +text: .text%__1cNTemplateTableRlocals_index_wide6FpnMRegisterImpl__v_; +text: .text%__1cKstorePNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2F_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConDNodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LEGTNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cSmulF24_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosHSolarisVcleanup_interruptible6FpnKJavaThread__v_; +text: .text%__1cCosHSolarisTsetup_interruptible6F_pnKJavaThread__; +text: .text%__1cQmulI_mem_immNodeFreloc6kM_i_; +text: .text%__1cJloadFNodeIpipeline6kM_pknIPipeline__; +text: .text%JVM_Sleep; +text: .text%JVM_Lseek; +text: .text%__1cNnmethodLocker2t6MpC_v_; +text: .text%__1cRmulI_eReg_immNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNTemplateTableQvolatile_barrier6F_v_; +text: .text%__1cHnmethodVmark_as_seen_on_stack6M_v_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_ppkc5i_i_: arguments.o; +text: .text%__1cQmulD_reg_immNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cKVtableStubSpd_code_size_limit6Fi_i_; +text: .text%__1cMdivD_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKNativeJumpUpatch_verified_entry6FpC11_v_; +text: .text%__1cQmulD_reg_immNodeFreloc6kM_i_; +text: .text%__1cODeoptimizationRlast_frame_adjust6Fii_i_; +text: .text%__1cFParseQdo_monitor_enter6M_v_; +text: .text%__1cCosHSolarisTsetup_interruptible6FpnKJavaThread__v_; +text: .text%__1cODeoptimizationScreate_vframeArray6FpnKJavaThread_nFframe_pnLRegisterMap__pnLvframeArray__; +text: .text%__1cLConvF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRaddI_mem_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cODeoptimizationNuncommon_trap6FpnKJavaThread_i_pn0ALUnrollBlock__; +text: .text%__1cODeoptimizationTuncommon_trap_inner6FpnKJavaThread_i_v_; +text: .text%__1cNcmovL_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: deoptimization.o; +text: .text%__1cODeoptimizationRgather_statistics6Fn0ALDeoptReason_n0ALDeoptAction_nJBytecodesECode__v_; +text: .text%__1cIcp2bNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cODeoptimizationPget_method_data6FpnKJavaThread_nMmethodHandle_i_pnRmethodDataOopDesc__; +text: .text%__1cODeoptimizationNunpack_frames6FpnKJavaThread_i_nJBasicType__; +text: .text%__1cIci2bNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cODeoptimizationYfetch_unroll_info_helper6FpnKJavaThread__pn0ALUnrollBlock__; +text: .text%__1cOMacroAssemblerFenter6M_v_; +text: .text%Unsafe_GetNativeByte; +text: .text%__1cOMacroAssemblerNpop_FPU_state6M_v_; +text: .text%__1cTsarL_eReg_32_63NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPconvL2F_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQmulD_reg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_: interpreterRuntime.o; +text: .text%__1cFTypeFGis_nan6kM_i_; +text: .text%__1cSaddF24_reg_memNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cKCMoveLNodeGOpcode6kM_i_; +text: .text%JVM_NanoTime; +text: .text%__1cIXorINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cFParseOdo_tableswitch6M_v_; +text: .text%__1cCosNjavaTimeNanos6F_x_; +text: .text%__1cETypeEmake6Fn0AFTYPES__pk0_; +text: .text%__1cVMoveF2I_reg_stackNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerFpushl6MnHAddress__v_; +text: .text%jni_GetEnv; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: methodLiveness.o; +text: .text%__1cKstoreCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cGICStubFclear6M_v_; +text: .text%__1cODeoptimizationLUnrollBlock2T6M_v_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: interp_masm_i486.o; +text: .text%__1cGciType2t6MnJBasicType__v_; +text: .text%__1cKstoreDNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRComputeEntryStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cMloadConFNodeFreloc6kM_i_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlassKlass.o; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCi_v_; +text: .text%__1cHTypePtrKadd_offset6kMi_pk0_; +text: .text%__1cMorI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cFStateP_sub_Op_ConvI2D6MpknENode__v_; +text: .text%__1cJAssemblerEmovw6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cQshrL_eReg_CLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIimmFOperFclone6kM_pnIMachOper__; +text: .text%__1cFKlassMoop_is_array6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFStateM_sub_Op_AddF6MpknENode__v_; +text: .text%__1cOcompI_eRegNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConFNodeFclone6kM_pnENode__; +text: .text%__1cTconvI2F_SSF_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cParrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%jint_cmp: parse2.o; +text: .text%__1cJAssemblerExorl6MpnMRegisterImpl_i_v_; +text: .text%__1cPBytecode_invokeLresult_type6kMpnGThread__nJBasicType__; +text: .text%jni_CallVoidMethod: jni.o; +text: .text%__1cOsubF24_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOstoreF_immNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cENodeHis_Call6M_pnICallNode__: machnode.o; +text: .text%__1cLloadSSINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLloadSSINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRStubCodeGeneratorLstub_epilog6MpnMStubCodeDesc__v_; +text: .text%__1cRStubCodeGeneratorLstub_prolog6MpnMStubCodeDesc__v_; +text: .text%__1cMnegF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConFNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%Unsafe_GetNativeFloat; +text: .text%__1cKstoreDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSaddD_reg_roundNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddI_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWroundFloat_mem_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMStubCodeMark2t6MpnRStubCodeGenerator_pkc4_v_; +text: .text%__1cPmovP_nocopyNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_method_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: callnode.o; +text: .text%__1cOsubF24_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLMoveL2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNstoreImmPNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cMStubCodeMark2T6M_v_; +text: .text%__1cRSignatureIteratorHiterate6M_v_; +text: .text%__1cNTemplateTableEiop26Fn0AJOperation__v_; +text: .text%__1cFStateO_sub_Op_CMoveP6MpknENode__v_; +text: .text%__1cOcmovI_regUNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddD_reg_imm1NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPmovI_nocopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerFfwait6M_v_; +text: .text%__1cJAssemblerKrepne_scan6M_v_; +text: .text%__1cPjava_lang_ClassYcreate_basic_type_mirror6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cPmovP_nocopyNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNRegisterSaverWrestore_live_registers6FpnOMacroAssembler__v_; +text: .text%__1cRandL_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNcmovL_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerMdispatch_via6MnITosState_ppC_v_; +text: .text%__1cZInterpreterMacroAssemblerRremove_activation6MnITosState_pnMRegisterImpl_iii_v_; +text: .text%__1cImulINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cImulINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSaddF24_reg_memNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cImulINodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cJname2type6Fpkc_nJBasicType__; +text: .text%__1cOcmpF_cc_P6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_continuation_for6MnITosState__pC_; +text: .text%__1cLvframeArrayRregister_location6kMi_pC_; +text: .text%JVM_FindPrimitiveClass; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constantPoolKlass.o; +text: .text%__1cVis_positive_one_float6Ff_i_; +text: .text%__1cRaddI_mem_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmulF24_reg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cSdivD_reg_roundNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSaddF24_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_safept_entry_for6MnITosState_pC_2_; +text: .text%__1cSaddF24_reg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cIDivINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSstoreD_roundedNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMdivD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAddDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNRelocIterator2t6MpnKCodeBuffer_pC3_v_; +text: .text%__1cFStateO_sub_Op_LoadPC6MpknENode__v_; +text: .text%__1cJAssemblerGmovsxb6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cSmulF24_reg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJCmpF3NodeGOpcode6kM_i_; +text: .text%__1cIAddDNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cHMulNodeGis_Mul6kM_pk0_: classes.o; +text: .text%__1cJAssemblerFtestb6MpnMRegisterImpl_i_v_; +text: .text%__1cIGraphKitSgen_native_wrapper6MpnIciMethod__v_; +text: .text%__1cNTemplateTableQfast_accessfield6FnITosState__v_; +text: .text%__1cOmulIS_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNtestI_regNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%Unsafe_StaticFieldBaseFromField; +text: .text%__1cSaddF24_reg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cOsubF24_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNTemplateTableZjvmti_post_fast_field_mod6F_v_; +text: .text%__1cKLoadPCNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTablePfast_storefield6FnITosState__v_; +text: .text%__1cXpartialSubtypeCheckNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%Unsafe_StaticFieldOffset; +text: .text%__1cVMoveL2D_reg_stackNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNTemplateTableXresolve_cache_and_index6FipnMRegisterImpl_2_v_; +text: .text%__1cKdirectOperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%Unsafe_GetObjectVolatile; +text: .text%__1cHnmethodFflush6M_v_; +text: .text%__1cHnmethodSflush_dependencies6MpnRBoolObjectClosure__v_; +text: .text%__1cTconvI2F_SSF_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cICodeBlobFflush6M_v_; +text: .text%__1cJAssemblerMemit_arith_b6MiipnMRegisterImpl_i_v_; +text: .text%__1cFTypeFFempty6kM_i_; +text: .text%__1cKdirectOperNconstant_disp6kM_i_: ad_i486_clone.o; +text: .text%__1cQciTypeArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cNReservedSpace2t6MpcI_v_; +text: .text%__1cNaddP_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJEventMark2t6MpkcE_v_: nmethod.o; +text: .text%__1cIMachOperNbase_position6kM_i_; +text: .text%__1cQorl_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2F_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2F_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cWroundFloat_mem_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cYinternal_word_RelocationMforce_target6MpC_v_: relocInfo.o; +text: .text%__1cQmulD_reg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cZInterpreterMacroAssemblerGf2ieee6M_v_; +text: .text%__1cQmulD_reg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cFVTuneOdelete_nmethod6FpnHnmethod__v_; +text: .text%__1cPconvL2D_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachOperMdisp_as_type6kM_pknHTypePtr__: ad_i486_clone.o; +text: .text%Unsafe_EnsureClassInitialized; +text: .text%__1cFStateS_sub_Op_CallNative6MpknENode__v_; +text: .text%__1cIciSymbolHas_utf86M_pkc_; +text: .text%__1cOtypeArrayKlassNexternal_name6FnJBasicType__pkc_; +text: .text%__1cENodeHdel_out6Mp0_v_: connode.o; +text: .text%JVM_GetClassContext; +text: .text%__1cPPerfDataManagerWcreate_string_variable6FnJCounterNS_pkci3pnGThread__pnSPerfStringVariable__; +text: .text%__1cOtypeArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlass.o; +text: .text%__1cIciMethodMnative_entry6M_pC_; +text: .text%__1cNdivI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJAssemblerHfincstp6M_v_; +text: .text%__1cZInterpreterMacroAssemblerSnotify_method_exit6MnITosState__v_; +text: .text%__1cETypeFxdual6kM_pk0_; +text: .text%__1cQorI_eReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOMacroAssemblerEfpop6M_v_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassMcreate_klass6FnJBasicType_ipnGThread__pnMklassOopDesc__; +text: .text%__1cLlog2_intptr6Fi_i_: typeArrayKlass.o; +text: .text%__1cLOptoRuntimeTmultianewarray_Type6Fi_pknITypeFunc__; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod__v_; +text: .text%__1cQAbstractCompilerPsupports_native6M_i_: c2compiler.o; +text: .text%__1cOGenerateOopMapGdo_jsr6Mi_v_; +text: .text%__1cZInterpreterMacroAssemblerbGget_unsigned_2_byte_index_at_bcp6MpnMRegisterImpl_i_v_; +text: .text%__1cSaddF24_reg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIAddDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cbCAbstractInterpreterGeneratorbBgenerate_result_handler_for6MnJBasicType__pC_; +text: .text%__1cOMacroAssemblerOpush_FPU_state6M_v_; +text: .text%__1cZInterpreterMacroAssemblerRcall_VM_leaf_base6MpCi_v_; +text: .text%__1cZInterpreterMacroAssemblerGd2ieee6M_v_; +text: .text%jni_RegisterNatives: jni.o; +text: .text%__1cSmulF24_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSaddF24_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%JVM_GetClassDeclaredFields; +text: .text%stat: os_solaris.o; +text: .text%JVM_IsSameClassPackage; +text: .text%__1cWroundFloat_mem_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cCosMuser_handler6F_pv_; +text: .text%__1cLConvF2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cLClassLoaderbCupdate_class_path_entry_list6Fpkc_v_; +text: .text%__1cOcmovI_regUNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cJArgumentsRverify_percentage6FIpkc_i_; +text: .text%jni_SetStaticObjectField: jni.o; +text: .text%__1cNTemplateTableGiconst6Fi_v_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cFStateM_sub_Op_AddD6MpknENode__v_; +text: .text%__1cLConvD2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJAssemblerEcall6MpnMRegisterImpl_nJrelocInfoJrelocType__v_; +text: .text%__1cRComputeEntryStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cFTypeFJis_finite6kM_i_; +text: .text%__1cCosEstat6FpkcpnEstat__i_; +text: .text%__1cLOptoRuntimeRresolve_call_Type6F_pknITypeFunc__; +text: .text%__1cCosHSolarisSset_signal_handler6Fiii_v_; +text: .text%__1cOPhaseIdealLoopTdo_maximally_unroll6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkci_pc_; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_2_v_; +text: .text%__1cNMemoryManagerIadd_pool6MpnKMemoryPool__v_; +text: .text%__1cSaddF24_reg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerGfnsave6MnHAddress__v_; +text: .text%__1cVMoveF2I_reg_stackNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMdecI_memNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKMemoryPoolLadd_manager6MpnNMemoryManager__v_; +text: .text%__1cJloadDNodeFreloc6kM_i_; +text: .text%__1cSObjectSynchronizerIjni_exit6FpnHoopDesc_pnGThread__v_; +text: .text%__1cMloadConLNodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cSObjectSynchronizerJjni_enter6FnGHandle_pnGThread__v_; +text: .text%__1cSaddF24_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMloadConFNodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cISubDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLloadSSINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cISubDNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cQorl_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_MonitorExit: jni.o; +text: .text%__1cNTemplateTableHif_0cmp6Fn0AJCondition__v_; +text: .text%jni_MonitorEnter: jni.o; +text: .text%__1cSaddD_reg_roundNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvL2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTableHif_icmp6Fn0AJCondition__v_; +text: .text%__1cKReturnNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cPcheckCastPPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFframebFset_interpreter_frame_sender_sp6Mpi_v_; +text: .text%__1cRmulI_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovI_regUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cULinearLeastSquareFit2t6MI_v_; +text: .text%__1cNTemplateTableH_return6FnITosState__v_; +text: .text%__1cOMacroAssemblerEfcmp6MpnMRegisterImpl__v_; +text: .text%__1cMTailJumpNodeGOpcode6kM_i_; +text: .text%__1cOsubF24_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWroundFloat_mem_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHciKlass2t6MnLKlassHandle_pnIciSymbol__v_; +text: .text%jni_GetJavaVM; +text: .text%__1cHciKlassIis_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cLConvL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJOopMapSetQsingular_oop_map6M_pnGOopMap__; +text: .text%__1cSaddF24_reg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLConvF2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cTconvI2F_SSF_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTconvI2F_SSF_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cCosZvm_allocation_granularity6F_i_; +text: .text%__1cNReservedSpaceKinitialize6MIIipc_v_; +text: .text%JVM_LoadLibrary; +text: .text%__1cCosOreserve_memory6FIpc_1_; +text: .text%JVM_IsSupportedJNIVersion; +text: .text%__1cOstoreF_immNodeFreloc6kM_i_; +text: .text%__1cVMoveF2I_reg_stackNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPPerfLongVariant2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_pnUPerfLongSampleHelper__v_; +text: .text%__1cNmulI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerGfild_d6MnHAddress__v_; +text: .text%__1cRmulI_imm_highNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerGmovsxw6MpnMRegisterImpl_nHAddress__v_; +text: .text%Unsafe_ObjectFieldOffset; +text: .text%__1cIcp2bNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIcp2bNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEincl6MnHAddress__v_; +text: .text%__1cXpartialSubtypeCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPmovP_nocopyNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cVMoveL2D_reg_stackNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosHSolarisOis_sig_ignored6Fi_i_; +text: .text%__1cRaddI_mem_eRegNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOLibraryCallKitbNinline_native_Reflection_getCallerClass6M_i_; +text: .text%__1cOMacroAssemblerQload_signed_byte6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cOLibraryCallKitZinline_native_Class_query6MnIciMethodLIntrinsicId__i_; +text: .text%__1cOstoreF_immNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cPconvL2D_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKciTypeFlowLStateVectorOdo_null_assert6MpnHciKlass__v_; +text: .text%__1cPconvL2F_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIci2bNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cXcmpL_reg_flags_LEGTNodeFclone6kM_pnENode__; +text: .text%__1cJMemRegion2t6M_v_: cardTableModRefBS.o; +text: .text%__1cQmulD_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerHfucomip6Mi_v_; +text: .text%__1cTGeneratePairingInfoOreport_results6kM_i_: ciMethod.o; +text: .text%__1cFciEnvbNArrayIndexOutOfBoundsException_instance6M_pnKciInstance__; +text: .text%__1cRsalI_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%jni_SetObjectField: jni.o; +text: .text%Unsafe_AllocateMemory; +text: .text%__1cNSharedRuntimeVhandle_ic_miss_helper6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: callnode.o; +text: .text%__1cTconvI2F_SSF_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMelapsedTimer2t6M_v_: methodLiveness.o; +text: .text%__1cISubDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvI2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvF2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKMemoryPool2t6Mpkcn0AIPoolType_IIii_v_; +text: .text%__1cRaddD_reg_imm1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosHSolarisOset_mpss_range6FpcII_i_; +text: .text%__1cNTemplateTableEfop26Fn0AJOperation__v_; +text: .text%__1cPciInstanceKlassbDcompute_shared_is_initialized6M_i_; +text: .text%jni_Throw: jni.o; +text: .text%__1cNSpaceCounters2t6MpkciIpnMMutableSpace_pnSGenerationCounters__v_; +text: .text%__1cOPSVirtualSpaceJexpand_by6MI_i_; +text: .text%__1cSaddF24_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableFaload6Fi_v_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_int_field06FnJBasicType__pC_; +text: .text%__1cNTemplateTableElop26Fn0AJOperation__v_; +text: .text%__1cQjava_lang_ThreadKset_daemon6FpnHoopDesc__v_; +text: .text%__1cPmovP_nocopyNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableEdop26Fn0AJOperation__v_; +text: .text%__1cIcp2bNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLklassVtableTis_miranda_entry_at6Mi_i_; +text: .text%__1cHnmethodbJcontinuation_for_implicit_exception6MpC_1_; +text: .text%__1cLVtableStubsIcontains6FpC_i_; +text: .text%__1cWImplicitExceptionTable2t6MpknHnmethod__v_; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnQPerfLongVariable__; +text: .text%__1cKExceptionsK_throw_oop6FpnGThread_pkcipnHoopDesc__v_; +text: .text%__1cOMacroAssemblerQload_signed_word6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cNdivI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXNativeSignatureIteratorLpass_double6M_v_: interpreterRuntime.o; +text: .text%__1cLVtableStubsPstub_containing6FpC_pnKVtableStub__; +text: .text%__1cOtailjmpIndNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMNativeLookupTbase_library_lookup6Fpkc22_pC_; +text: .text%__1cFStateP_sub_Op_ConvF2I6MpknENode__v_; +text: .text%__1cODeoptimizationYreset_invocation_counter6FpnJScopeDesc_i_v_; +text: .text%__1cQshrL_eReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvF2I_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXNativeSignatureIteratorJdo_double6M_v_: interpreterRuntime.o; +text: .text%__1cOcompI_eRegNodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerNsuper_call_VM6MpnMRegisterImpl_2pC22_v_; +text: .text%__1cLVtableStubsLcreate_stub6FiipnNmethodOopDesc__pC_; +text: .text%__1cbCAbstractInterpreterGeneratorXbang_stack_shadow_pages6Mi_v_; +text: .text%__1cNCellTypeStateImake_any6Fi_0_: generateOopMap.o; +text: .text%__1cJAssemblerEmovb6MnHAddress_i_v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkc2i_pc_; +text: .text%__1cNRegisterSaverTsave_live_registers6FpnOMacroAssembler_ipi_pnGOopMap__; +text: .text%__1cTshlL_eReg_32_63NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cWImplicitExceptionTableCat6kMI_I_; +text: .text%__1cQshrL_eReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLVtableStubsGlookup6Fiii_pnKVtableStub__; +text: .text%__1cSsarL_eReg_1_31NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNReservedSpaceKfirst_part6MIii_0_; +text: .text%__1cJAssemblerFfinit6M_v_; +text: .text%__1cLVtableStubsOis_entry_point6FpC_i_; +text: .text%__1cFParseRjump_if_true_fork6MpnGIfNode_ii_v_; +text: .text%__1cKVtableStubRpd_code_alignment6F_i_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: subnode.o; +text: .text%__1cUInterpreterGeneratorUgenerate_fixed_frame6Mi_v_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerSstore_check_part_26MpnMRegisterImpl__v_; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: frame.o; +text: .text%__1cNStubGeneratorYgenerate_throw_exception6MpkcpCi_3_: stubGenerator_i486.o; +text: .text%__1cGEventsDlog6FpkcE_v_: compiledIC.o; +text: .text%__1cLOptoRuntimebBhandle_wrong_method_ic_miss6FpnKJavaThread__pC_; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2t6M_v_; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2T6M_v_; +text: .text%__1cNReservedSpace2t6MI_v_; +text: .text%__1cVMoveF2I_reg_stackNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationZtrap_state_set_recompiled6Fii_i_; +text: .text%__1cINegFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSmulF24_reg_immNodeFreloc6kM_i_; +text: .text%__1cLloadSSINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerSstore_check_part_16MpnMRegisterImpl__v_; +text: .text%__1cKCompiledICOis_megamorphic6kM_i_; +text: .text%__1cUInterpreterGeneratorbAgenerate_run_compiled_code6M_v_; +text: .text%__1cUInterpreterGeneratorZgenerate_counter_overflow6MpC_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_counter_incr6MpnFLabel_22_v_; +text: .text%__1cIDivDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKCompiledICSset_to_megamorphic6MpnICallInfo_nJBytecodesECode_pnGThread__v_; +text: .text%JVM_GetLastErrorString; +text: .text%__1cIDivDNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cZInterpreterMacroAssemblerTnotify_method_entry6M_v_; +text: .text%__1cSmulF24_reg_immNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_DivD6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_DivI6MpknENode__v_; +text: .text%__1cRComputeEntryStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cINegDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNTemplateTableGdstore6Fi_v_; +text: .text%__1cNTemplateTableFdload6Fi_v_; +text: .text%__1cNTemplateTableOprepare_invoke6FpnMRegisterImpl_2inJBytecodesECode__v_; +text: .text%__1cLConvD2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTableGlstore6Fi_v_; +text: .text%__1cOcompP_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNTemplateTableGfstore6Fi_v_; +text: .text%__1cNTemplateTableGastore6Fi_v_; +text: .text%__1cKConv2BNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNGCTaskManagerGthread6MI_pnMGCTaskThread__; +text: .text%__1cPdouble_quadword6Fpxxx_0_: ad_i486.o; +text: .text%__1cFStateM_sub_Op_SubD6MpknENode__v_; +text: .text%__1cNTemplateTableZload_field_cp_cache_entry6FipnMRegisterImpl_22i_v_; +text: .text%__1cSmulF24_reg_memNodeFreloc6kM_i_; +text: .text%__1cScompP_eReg_memNodeFreloc6kM_i_; +text: .text%__1cLMoveL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTablebAload_invoke_cp_cache_entry6FipnMRegisterImpl_22ii_v_; +text: .text%__1cFStateM_sub_Op_NegD6MpknENode__v_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: gcTaskThread.o; +text: .text%__1cJlog2_long6Fx_i_: divnode.o; +text: .text%__1cMelapsedTimer2t6M_v_: compileBroker.o; +text: .text%__1cNcmovI_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cQaddD_reg_immNodeFreloc6kM_i_; +text: .text%__1cIRetTableHadd_jsr6Mii_v_; +text: .text%__1cNTemplateTableFfload6Fi_v_; +text: .text%__1cNTemplateTableFlload6Fi_v_; +text: .text%__1cNcmovI_memNodeFreloc6kM_i_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: unsafe.o; +text: .text%__1cPaddress_of_flag6FnXCommandLineFlagWithType__pnEFlag__: globals.o; +text: .text%__1cNTemplateTableFiload6Fi_v_; +text: .text%__1cOGenerateOopMapTret_jump_targets_do6MpnOBytecodeStream_pFp0ipi_vi4_v_; +text: .text%__1cNdivI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cImulINodeFreloc6kM_i_; +text: .text%__1cNmulI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFStateO_sub_Op_Conv2B6MpknENode__v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fii_v_; +text: .text%__1cIRetTableUfind_jsrs_for_target6Mi_pnNRetTableEntry__; +text: .text%__1cNmulI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNTemplateTableGistore6Fi_v_; +text: .text%__1cRClassPathZipEntry2t6Mppvpc_v_; +text: .text%__1cLClassLoaderXcreate_class_path_entry6FpcnEstat_ppnOClassPathEntry__v_; +text: .text%__1cLClassLoaderLadd_to_list6FpnOClassPathEntry__v_; +text: .text%__1cOLibraryCallKitbBinline_native_currentThread6M_i_; +text: .text%__1cOLibraryCallKitXgenerate_current_thread6MrpnENode__2_; +text: .text%__1cTconvI2F_SSF_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQaddD_reg_immNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cLClassLoaderSget_canonical_path6Fpc1i_i_; +text: .text%__1cSmembar_acquireNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMstoreSSINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRaddD_reg_imm1NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerEcmpb6MnHAddress_i_v_; +text: .text%__1cINegDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJAssemblerEdecl6MnHAddress__v_; +text: .text%__1cOstackSlotFOperFclone6kM_pnIMachOper__; +text: .text%__1cSaddF24_reg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cCosGsignal6Fipv_1_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%jni_CallStaticObjectMethod: jni.o; +text: .text%__1cMNativeLookupNlong_jni_name6FnMmethodHandle__pc_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceRefKlass.o; +text: .text%__1cZInterpreterMacroAssemblerRprofile_checkcast6MipnMRegisterImpl__v_; +text: .text%__1cHRetNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQmulD_reg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompP_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMPerfDataList2T6M_v_; +text: .text%__1cHRetNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeFclone6kM_pnENode__; +text: .text%__1cNtestI_regNodeFclone6kM_pnENode__; +text: .text%__1cOClassPathEntry2t6M_v_; +text: .text%__1cVMoveF2I_reg_stackNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTjava_lang_ThrowableLset_message6FpnHoopDesc_2_v_; +text: .text%Unsafe_SetMemory; +text: .text%__1cRaddL_eReg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIcp2bNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFciEnvOrecord_failure6Mpkc_v_; +text: .text%__1cNcmovL_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorbHgenerate_exception_handler_common6Mpkc2i_pC_; +text: .text%__1cENodeHdel_out6Mp0_v_: ifg.o; +text: .text%__1cJAssemblerEfld16M_v_; +text: .text%__1cSmembar_acquireNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerFfld_x6MnHAddress__v_; +text: .text%__1cLConvD2FNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJAssemblerHfistp_d6MnHAddress__v_; +text: .text%__1cKstoreFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerFimull6MpnMRegisterImpl_2_v_; +text: .text%__1cRInvocationCounterDdef6Fn0AFState_ipFnMmethodHandle_pnGThread__pC_v_; +text: .text%__1cOMacroAssemblerIfcmp2int6MpnMRegisterImpl_i_v_; +text: .text%__1cFciEnvXget_or_create_exception6MrpnI_jobject_nMsymbolHandle__pnKciInstance__; +text: .text%__1cMPerfDataList2t6Mi_v_; +text: .text%__1cLloadSSINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLVtableStubsScreate_vtable_stub6Fii_pnKVtableStub__; +text: .text%__1cRaddI_mem_eRegNodeFreloc6kM_i_; +text: .text%__1cUInterpreterGeneratorTgenerate_math_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cMLinkResolverbBlookup_method_in_interfaces6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cCosFyield6F_v_; +text: .text%__1cOCompilerThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cRComputeEntryStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cIDivFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSCardTableExtensionVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cSCardTableExtensionbEresize_covered_region_by_start6MnJMemRegion__v_; +text: .text%__1cJArgumentsMbuild_string6Fppcpkc_v_; +text: .text%__1cJArgumentsObuild_jvm_args6Fpkc_v_; +text: .text%__1cJArgumentsMadd_property6Fpkc_i_; +text: .text%__1cJAssemblerFfmulp6Mi_v_; +text: .text%__1cUInterpreterGeneratorbEgenerate_asm_interpreter_entry6Mi_pC_; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstanceKlass.o; +text: .text%__1cZInterpreterMacroAssemblerUprofile_virtual_call6MpnMRegisterImpl_22_v_; +text: .text%__1cNcmovL_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerWprofile_switch_default6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableMfast_xaccess6FnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerTprofile_switch_case6MpnMRegisterImpl_22_v_; +text: .text%__1cRaddI_mem_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_FindSignal; +text: .text%__1cKVtableStub2n6FIi_pv_; +text: .text%__1cSInterpreterRuntimebKthrow_ArrayIndexOutOfBoundsException6FpnKJavaThread_pci_v_; +text: .text%__1cOGenerateOopMapRdo_multianewarray6Mii_v_; +text: .text%__1cXNativeSignatureIteratorIdo_float6M_v_: interpreterRuntime.o; +text: .text%__1cLOptoRuntimeVgenerate_handler_blob6FpCi_pnNSafepointBlob__; +text: .text%JVM_RegisterSignal; +text: .text%__1cFParsePdo_lookupswitch6M_v_; +text: .text%__1cSaddF24_reg_memNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cQCompilerCounters2t6MpkcipnGThread__v_; +text: .text%__1cJAssemblerEandl6MpnMRegisterImpl_2_v_; +text: .text%__1cNCompileBrokerUmake_compiler_thread6FpkcpnMCompileQdDueue_pnQCompilerCounters_pnGThread__pnOCompilerThread__; +text: .text%__1cNCompileBrokerUcompiler_thread_loop6F_v_; +text: .text%__1cOcmpF_cc_P6NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerEcdql6M_v_; +text: .text%__1cMVM_OperationNdoit_prologue6M_i_: vm_operations.o; +text: .text%__1cUInterpreterGeneratorbDgenerate_stack_overflow_check6M_v_; +text: .text%__1cUInterpreterGeneratorXcheck_for_compiled_code6MrnFLabel__v_; +text: .text%__1cRCardTableModRefBSbCfind_covering_region_by_base6MpnIHeapWord__i_; +text: .text%__1cIci2bNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRCardTableModRefBSbAlargest_prev_committed_end6kMi_pnIHeapWord__; +text: .text%__1cMSysClassPathNreset_item_at6Mi_v_: arguments.o; +text: .text%__1cLconvI2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateM_sub_Op_CmpF6MpknENode__v_; +text: .text%__1cRCardTableModRefBSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cOLibraryCallKitVinline_fp_conversions6MnIciMethodLIntrinsicId__i_; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cHCompilePget_invoke_name6M_pnIciSymbol__; +text: .text%__1cFParseRdo_multianewarray6M_v_; +text: .text%__1cJAssemblerGfmul_d6MnHAddress__v_; +text: .text%__1cLVtableStubsFenter6FiiipnKVtableStub__v_; +text: .text%__1cZInterpreterMacroAssemblerLlock_object6MpnMRegisterImpl__v_; +text: .text%__1cLMoveF2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cRaddD_reg_imm1NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTMaskFillerForNative2t6MnMmethodHandle_pIi_v_: oopMapCache.o; +text: .text%__1cNTemplateTableLwide_istore6F_v_; +text: .text%__1cRaddD_reg_imm1NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cISubFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNandI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOtailjmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVcompiler_thread_entry6FpnKJavaThread_pnGThread__v_: thread.o; +text: .text%__1cOCompilerThread2t6MpnMCompileQdDueue_pnQCompilerCounters__v_; +text: .text%__1cNReservedSpaceJlast_part6MI_0_; +text: .text%__1cNSafepointBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cJlookupOne6FpnHJNIEnv__pkcpnGThread__pnH_jclass__: jni.o; +text: .text%__1cUPSGenerationCounters2t6MpkciipnOPSVirtualSpace__v_; +text: .text%__1cNSafepointBlob2n6FII_pv_; +text: .text%__1cQmulD_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvI2F_SSF_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPSVirtualSpace2t6MnNReservedSpace_I_v_; +text: .text%__1cFTypeDJis_finite6kM_i_; +text: .text%JVM_GetInterfaceVersion; +text: .text%__1cQOopMapCacheEntryPfill_for_native6M_v_; +text: .text%__1cNMemoryManager2t6M_v_; +text: .text%__1cSPSPromotionManager2t6M_v_; +text: .text%__1cOstackSlotIOperFscale6kM_i_: ad_i486.o; +text: .text%__1cOstackSlotIOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOstackSlotIOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cNFingerprinterLfingerprint6M_X_: oopMapCache.o; +text: .text%__1cMnegF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveF2I_reg_stackNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSaddF24_reg_memNodeFreloc6kM_i_; +text: .text%__1cNTemplateTableGfconst6Fi_v_; +text: .text%__1cSdivD_reg_roundNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSafepointBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cPmovP_nocopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_CallStaticObjectMethodV: jni.o; +text: .text%__1cIci2bNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMOopTaskQdDueue2t6M_v_; +text: .text%__1cSCommandLineFlagsExJboolAtPut6FnXCommandLineFlagWithType_i_v_; +text: .text%__1cSCommandLineFlagsExKis_default6FnPCommandLineFlag__i_; +text: .text%__1cZInterpreterMacroAssemblerRgen_subtype_check6MpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cRaddI_mem_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%JVM_Available; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_2i_v_; +text: .text%__1cCosIjvm_path6Fpci_v_; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_2i_v_; +text: .text%__1cMOopTaskQdDueueKinitialize6M_v_; +text: .text%__1cPmovI_nocopyNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOtailjmpIndNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIci2bNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosGgetenv6Fpkcpci_i_; +text: .text%__1cOtailjmpIndNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%jni_GetDoubleArrayRegion: jni.o; +text: .text%__1cIcp2bNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLconvI2BNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeFclone6kM_pnENode__; +text: .text%__1cKciTypeFlowLStateVectorRdo_multianewarray6MpnQciByteCodeStream__v_; +text: .text%__1cJAssemblerGfild_s6MnHAddress__v_; +text: .text%__1cJAssemblerGfstp_d6Mi_v_; +text: .text%__1cTMaskFillerForNativeLpass_object6M_v_: oopMapCache.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cJStubQdDueue2t6MpnNStubInterface_ipnFMutex_pkc_v_; +text: .text%__1cLMoveF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNStubGeneratorUgenerate_d2i_wrapper6MpC_1_: stubGenerator_i486.o; +text: .text%__1cTMaskFillerForNativeJpass_long6M_v_: oopMapCache.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cODeoptimizationVdeoptimize_dependents6F_i_; +text: .text%__1cNTemplateTableUinvokevirtual_helper6FpnMRegisterImpl_22_v_; +text: .text%__1cJAssemblerEfxch6Mi_v_; +text: .text%__1cJAssemblerFfprem6M_v_; +text: .text%__1cSvframeStreamCommonbFfill_in_compiled_inlined_sender6M_i_; +text: .text%__1cJAssemblerJfnstsw_ax6M_v_; +text: .text%__1cJAssemblerEsahf6M_v_; +text: .text%__1cQObjectStartArraySset_covered_region6MnJMemRegion__v_; +text: .text%__1cQObjectStartArrayKinitialize6MnJMemRegion__v_; +text: .text%__1cNVM_DeoptimizeEname6kM_pkc_: vm_operations.o; +text: .text%__1cOtailjmpIndNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMVM_OperationNdoit_epilogue6M_v_: vm_operations.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC222i_v_; +text: .text%__1cJAssemblerEfchs6M_v_; +text: .text%__1cNVM_DeoptimizeEdoit6M_v_; +text: .text%__1cTAbstract_VM_VersionOvm_info_string6F_pkc_; +text: .text%__1cSaddF24_reg_immNodeFreloc6kM_i_; +text: .text%__1cJAssemblerEfabs6M_v_; +text: .text%__1cJStubQdDueueOregister_queue6Fp0_v_; +text: .text%__1cOMacroAssemblerPcorrected_idivl6MpnMRegisterImpl__i_; +text: .text%__1cMLinkResolverbEvtable_index_of_miranda_method6FnLKlassHandle_nMsymbolHandle_2pnGThread__i_; +text: .text%__1cRsubI_eReg_memNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFStatebB_sub_Op_PartialSubtypeCheck6MpknENode__v_; +text: .text%__1cNTemplateTableKdouble_cmp6Fi_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fi_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC22_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC_v_; +text: .text%__1cFStateL_sub_Op_OrL6MpknENode__v_; +text: .text%__1cPconvF2D_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFStateM_sub_Op_SubF6MpknENode__v_; +text: .text%__1cPOopTaskQdDueueSetOregister_queue6MipnMOopTaskQdDueue__v_; +text: .text%__1cMPeriodicTaskGenroll6M_v_; +text: .text%__1cNTemplateTableSgetfield_or_static6Fii_v_; +text: .text%__1cMPeriodicTask2t6MI_v_; +text: .text%__1cSestimate_path_freq6FpnENode__f_: loopnode.o; +text: .text%__1cFStateP_sub_Op_MoveL2D6MpknENode__v_; +text: .text%__1cNincI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNTemplateTableHcastore6F_v_; +text: .text%__1cUGcThreadCountClosureJdo_thread6MpnGThread__v_; +text: .text%__1cWNonPrintingResourceObj2n6FInLResourceObjPallocation_type__pv_: memoryService.o; +text: .text%__1cPGCMemoryManagerXinitialize_gc_stat_info6M_v_; +text: .text%__1cNTemplateTableLwide_lstore6F_v_; +text: .text%__1cPGCMemoryManager2t6M_v_; +text: .text%__1cKGCStatInfo2t6Mi_v_; +text: .text%__1cJMarkSweepUAdjustPointerClosure2t6Mi_v_: markSweep.o; +text: .text%__1cNstoreImmBNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLloadSSINodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cNTemplateTableDldc6Fi_v_; +text: .text%__1cNTemplateTableKif_nullcmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_acmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableGdconst6Fi_v_; +text: .text%__1cNTemplateTableGlconst6Fi_v_; +text: .text%__1cNTemplateTableKinitialize6F_v_; +text: .text%__1cNTemplateTableUjvmti_post_field_mod6Fii_v_; +text: .text%__1cJAssemblerEaddl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerFidivl6MpnMRegisterImpl__v_; +text: .text%__1cOmulF24_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cJAssemblerEmull6MnHAddress__v_; +text: .text%__1cJAssemblerDorl6MnHAddress_i_v_; +text: .text%__1cJAssemblerEsarl6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEshll6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerFshrdl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEshrl6MpnMRegisterImpl__v_; +text: .text%__1cTAbstract_VM_VersionXinternal_vm_info_string6F_pkc_; +text: .text%__1cTAbstract_VM_VersionKvm_release6F_pkc_; +text: .text%__1cJAssemblerFcpuid6M_v_; +text: .text%__1cJAssemblerEfldz6M_v_; +text: .text%__1cJAssemblerFfld_s6Mi_v_; +text: .text%__1cJAssemblerFfst_s6MnHAddress__v_; +text: .text%__1cJAssemblerFfst_d6MnHAddress__v_; +text: .text%__1cOaddF24_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNTemplateTableSputfield_or_static6Fii_v_; +text: .text%__1cSaddF24_reg_immNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cHMatcherQconvL2FSupported6F_ki_; +text: .text%__1cFStateP_sub_Op_ConvL2F6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_ConvL2D6MpknENode__v_; +text: .text%__1cGatomll6Fpkcpx_i_: arguments.o; +text: .text%__1cJArgumentsRcheck_memory_size6Fxx_n0AJArgsRange__; +text: .text%__1cJArgumentsVset_parallel_gc_flags6F_v_; +text: .text%__1cPconvD2F_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJArgumentsRparse_memory_size6Fpkcpxx_n0AJArgsRange__; +text: .text%__1cFStateP_sub_Op_ConvD2F6MpknENode__v_; +text: .text%__1cHnmethodVinvalidate_osr_method6M_v_; +text: .text%__1cJArgumentsXPropertyList_unique_add6FppnOSystemProperty_pkcpc_v_; +text: .text%__1cQAgentLibraryList2t6M_v_: arguments.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: arrayKlassKlass.o; +text: .text%__1cJAssemblerEmovb6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cLklassVtableQindex_of_miranda6MpnNsymbolOopDesc_2_i_; +text: .text%__1cWStubGenerator_generate6FpnKCodeBuffer_i_v_; +text: .text%__1cPconvL2D_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: parse1.o; +text: .text%__1cPconvD2F_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNmulI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cINegFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMFastLockNodeLis_FastLock6kM_pk0_: classes.o; +text: .text%__1cSInterpreterRuntimeMat_safepoint6FpnKJavaThread__v_; +text: .text%__1cOGenerateOopMapTadd_to_ref_init_set6Mi_v_; +text: .text%__1cIPSOldGenOgen_size_limit6M_I_; +text: .text%__1cIciObjectMis_classless6kM_i_: ciMethod.o; +text: .text%__1cIPSOldGenGresize6MI_v_; +text: .text%__1cMVirtualSpaceKinitialize6MnNReservedSpace_I_i_; +text: .text%__1cQorl_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNciMethodKlassEmake6F_p0_; +text: .text%__1cPconvL2F_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cUParallelScavengeHeapItop_addr6kM_ppnIHeapWord__: parallelScavengeHeap.o; +text: .text%__1cRInvocationCounterMreinitialize6Fi_v_; +text: .text%__1cIciMethod2t6MpnPciInstanceKlass_pnIciSymbol_4_v_; +text: .text%__1cFParseNfetch_monitor6MipnENode_2_2_; +text: .text%__1cMGCTaskThread2t6MpnNGCTaskManager_II_v_; +text: .text%__1cMGCTaskThreadFstart6M_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_native_entry6Mi_pC_; +text: .text%__1cMGCTaskThreadDrun6M_v_; +text: .text%__1cJLoadFNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cGThreadOis_Java_thread6kM_i_: gcTaskThread.o; +text: .text%__1cUInterpreterGeneratorLlock_method6M_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: universe.o; +text: .text%__1cXpartialSubtypeCheckNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cISubFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cISubFNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cIPSOldGenPinitialize_work6Mpkci_v_; +text: .text%lstat: perfMemory_solaris.o; +text: .text%__1cRComputeEntryStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cPfilename_to_pid6Fpkc_l_: perfMemory_solaris.o; +text: .text%Unsafe_CompareAndSwapObject; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cJCodeCachebGmake_marked_nmethods_not_entrant6F_v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpCpnMRegisterImpl__v_; +text: .text%__1cTis_directory_secure6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cZInterpreterMacroAssemblerSprofile_final_call6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerMprofile_call6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerWdispatch_only_noverify6MnITosState__v_; +text: .text%__1cRCollectorCounters2t6Mpkci_v_; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_flag_at6MpnMRegisterImpl_i_v_; +text: .text%__1cZInterpreterMacroAssemblerSupdate_mdp_for_ret6MpnMRegisterImpl__v_; +text: .text%__1cHThreadsbFdeoptimized_wrt_marked_nmethods6F_v_; +text: .text%__1cZInterpreterMacroAssemblerLprofile_ret6MpnMRegisterImpl_2_v_; +text: .text%__1cJTimeStamp2t6M_v_: runtimeService.o; +text: .text%__1cIPSOldGenYinitialize_virtual_space6MnNReservedSpace_I_v_; +text: .text%__1cIPSOldGenKinitialize6MnNReservedSpace_Ipkci_v_; +text: .text%__1cMVirtualSpace2t6M_v_; +text: .text%__1cLOptoRuntimeSfetch_monitor_Type6F_pknITypeFunc__; +text: .text%__1cbCAbstractInterpreterGeneratorTgenerate_error_exit6Mpkc_pC_; +text: .text%__1cPciObjectFactoryTget_unloaded_method6MpnPciInstanceKlass_pnIciSymbol_4_pnIciMethod__; +text: .text%__1cNinstanceKlassSremove_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cTAbstractInterpreterKinitialize6F_v_; +text: .text%__1cLNamedThread2t6M_v_; +text: .text%__1cRMachSpillCopyNodeHsize_of6kM_I_: ad_i486.o; +text: .text%__1cLNamedThreadIset_name6MpkcE_v_; +text: .text%__1cMelapsedTimer2t6M_v_: psAdaptiveSizePolicy.o; +text: .text%__1cUPSAdaptiveSizePolicybQpromo_increment_with_supplement_aligned_up6MI_I_; +text: .text%__1cUPSAdaptiveSizePolicyPpromo_increment6MII_I_; +text: .text%__1cOMacroAssemblerElshr6MpnMRegisterImpl_2i_v_; +text: .text%__1cCosbCis_thread_cpu_time_supported6F_i_; +text: .text%__1cLConvD2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOMacroAssemblerIsave_eax6MpnMRegisterImpl__v_; +text: .text%jni_IsInstanceOf: jni.o; +text: .text%jni_CallStaticBooleanMethod: jni.o; +text: .text%__1cRCardTableModRefBSbCpar_chunk_heapword_alignment6F_I_: tenuredGeneration.o; +text: .text%jni_CallStaticVoidMethodV: jni.o; +text: .text%__1cSReferenceProcessor2t6MnJMemRegion_iii_v_; +text: .text%__1cCosWactive_processor_count6F_i_; +text: .text%jni_NewWeakGlobalRef: jni.o; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_float_field06FnJBasicType__pC_; +text: .text%__1cOMacroAssemblerLrestore_eax6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerFfremr6MpnMRegisterImpl__v_; +text: .text%__1cLMoveL2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKJNIHandlesQmake_weak_global6FnGHandle__pnI_jobject__; +text: .text%__1cHOrLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKReflectionbFbasic_type_arrayklass_to_mirror6FpnMklassOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cCosHrealloc6FpvI_1_; +text: .text%__1cOMacroAssemblerGsincos6Miii_v_; +text: .text%__1cEMIN24CI_6FTA0_0_: tenuredGeneration.o; +text: .text%__1cXPartialSubtypeCheckNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cCosNsigexitnum_pd6F_i_; +text: .text%__1cNGCTaskManagerKset_thread6MIpnMGCTaskThread__v_; +text: .text%__1cLConvL2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOPSVirtualSpace2t6M_v_; +text: .text%__1cNdefaultStreamMhas_log_file6M_i_; +text: .text%__1cUGenericGrowableArrayGgrow646Mi_v_; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: oopMapCache.o; +text: .text%__1cOPSVirtualSpaceKinitialize6MnNReservedSpace_I_i_; +text: .text%__1cYalign_to_allocation_size6FI_I_: heap.o; +text: .text%__1cWcheck_compare_clipping6FipnGIfNode_pnHConNode_rpnENode__i_: cfgnode.o; +text: .text%__1cLConvL2FNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cCosVatomic_xchg_bootstrap6Fipoi_i_; +text: .text%__1cUParallelScavengeHeapOresize_old_gen6MI_v_; +text: .text%__1cIciObjectOis_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cRcheck_if_clipping6FpknKRegionNode_rpnGIfNode_5_i_: cfgnode.o; +text: .text%__1cHOrLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cNTemplateTableEdup26F_v_; +text: .text%__1cHThreadsJcreate_vm6FpnOJavaVMInitArgs_pi_i_; +text: .text%__1cOThreadCriticalKinitialize6F_v_; +text: .text%__1cIUniverseHgenesis6FpnGThread__v_; +text: .text%__1cKJavaThread2t6M_v_; +text: .text%__1cNTemplateTableHdup2_x16F_v_; +text: .text%__1cNTemplateTableHdup2_x26F_v_; +text: .text%Unsafe_SetNativeLong; +text: .text%__1cNTemplateTableEswap6F_v_; +text: .text%__cplus_fini_at_exit: CCrti.o; +text: .text%__1cNTemplateTableGdup_x16F_v_; +text: .text%__1cNTemplateTableDpop6F_v_; +text: .text%__1cIUniverseUreinitialize_itables6F_v_; +text: .text%__1cNReservedSpace2t6MIIipc_v_; +text: .text%__1cNuniverse_init6F_i_; +text: .text%__1cIUniversePinitialize_heap6F_i_; +text: .text%__1cOuniverse2_init6F_v_; +text: .text%__1cNTemplateTableEpop26F_v_; +text: .text%__1cNTemplateTableIgetfield6Fi_v_; +text: .text%__1cNTemplateTableDdup6F_v_; +text: .text%__1cSuniverse_post_init6F_v_; +text: .text%__1cIUniverseNfixup_mirrors6FpnGThread__v_; +text: .text%__1cNTemplateTableGdup_x26F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vmStructs.o; +text: .text%__1cNTemplateTableElmul6F_v_; +text: .text%__1cNTemplateTableEldiv6F_v_; +text: .text%__1cNTemplateTableElrem6F_v_; +text: .text%__1cNTemplateTableElshl6F_v_; +text: .text%__1cNTemplateTableElshr6F_v_; +text: .text%__1cNTemplateTableFlushr6F_v_; +text: .text%__1cNTemplateTableElcmp6F_v_; +text: .text%__1cNTemplateTableJwide_iinc6F_v_; +text: .text%__1cNTemplateTableEiinc6F_v_; +text: .text%__1cNTemplateTableEineg6F_v_; +text: .text%__1cNTemplateTableElneg6F_v_; +text: .text%__1cNTemplateTableEdneg6F_v_; +text: .text%__1cNTemplateTableEfneg6F_v_; +text: .text%__1cNTemplateTableEirem6F_v_; +text: .text%__1cIUniverseYcompute_base_vtable_size6F_v_; +text: .text%__1cNTemplateTableRfast_binaryswitch6F_v_; +text: .text%__1cNReservedSpaceUpage_align_size_down6FI_I_; +text: .text%__1cQVMOperationQdDueue2t6M_v_; +text: .text%__1cJvmSymbolsKinitialize6FpnGThread__v_; +text: .text%__1cNReservedSpaceYallocation_align_size_up6FI_I_; +text: .text%__1cNTemplateTableRfast_linearswitch6F_v_; +text: .text%__1cNTemplateTableMlookupswitch6F_v_; +text: .text%__1cOvmStructs_init6F_v_; +text: .text%__1cNTemplateTableLtableswitch6F_v_; +text: .text%__1cNTemplateTableIwide_ret6F_v_; +text: .text%Unsafe_PageSize; +text: .text%__1cNTemplateTableDret6F_v_; +text: .text%Unsafe_FreeMemory; +text: .text%__1cNTemplateTableEidiv6F_v_; +text: .text%__1cJTimeStampMmilliseconds6kM_x_; +text: .text%__1cOtypeArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cHVM_ExitEdoit6M_v_; +text: .text%__1cHVM_ExitEname6kM_pkc_: vm_operations.o; +text: .text%__1cNThreadServiceEinit6F_v_; +text: .text%__1cSThreadLocalStorageEinit6F_v_; +text: .text%__1cNCollectedHeapYlarge_typearray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cTAbstract_VM_VersionHvm_name6F_pkc_; +text: .text%__1cTAbstract_VM_VersionJvm_vendor6F_pkc_; +text: .text%__1cIVMThreadEloop6M_v_; +text: .text%__1cPGlobalTLABStats2t6M_v_; +text: .text%__1cWThreadLocalAllocBufferWstartup_initialization6F_v_; +text: .text%__1cIVMThreadDrun6M_v_; +text: .text%__1cSThreadLocalStoragebCgenerate_code_for_get_thread6F_v_; +text: .text%__1cTtypeArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cTtypeArrayKlassKlassVoop_is_typeArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cKTypeOopPtrFxdual6kM_pknEType__; +text: .text%__1cKTypeOopPtrEmake6FnHTypePtrDPTR_i_pk0_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vm_version.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cKVM_VersionWget_processor_features6F_v_; +text: .text%__1cKVM_VersionKinitialize6F_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: tenuredGeneration.o; +text: .text%__1cNTemplateTableKinstanceof6F_v_; +text: .text%__1cNTemplateTableJcheckcast6F_v_; +text: .text%__1cNTemplateTableLarraylength6F_v_; +text: .text%__1cNTemplateTableJanewarray6F_v_; +text: .text%__1cNTemplateTableInewarray6F_v_; +text: .text%__1cNTemplateTableE_new6F_v_; +text: .text%__1cNTemplateTablePinvokeinterface6Fi_v_; +text: .text%__1cNTemplateTableRfast_invokevfinal6Fi_v_; +text: .text%__1cNTemplateTableMinvokestatic6Fi_v_; +text: .text%__1cNTemplateTableNinvokespecial6Fi_v_; +text: .text%__1cNTemplateTableNinvokevirtual6Fi_v_; +text: .text%__1cNTemplateTableL_breakpoint6F_v_; +text: .text%__1cNTemplateTableGathrow6F_v_; +text: .text%__1cNTemplateTableMmonitorenter6F_v_; +text: .text%__1cNTemplateTableLmonitorexit6F_v_; +text: .text%__1cNTemplateTableEwide6F_v_; +text: .text%__1cNTemplateTableOmultianewarray6F_v_; +text: .text%__1cETypeRInitialize_shared6FpnHCompile__v_; +text: .text%__1cHVM_ExitNset_vm_exited6F_i_; +text: .text%__1cHVM_ExitbJwait_for_threads_in_native_to_block6F_i_; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cNTemplateTableJputstatic6Fi_v_; +text: .text%__1cNWatcherThread2t6M_v_; +text: .text%__1cNWatcherThreadDrun6M_v_; +text: .text%__1cNWatcherThreadFstart6F_v_; +text: .text%__1cVverificationType_exit6F_v_; +text: .text%__1cQVerificationTypeKinitialize6F_v_; +text: .text%__1cQVerificationTypeIfinalize6F_v_; +text: .text%__1cNTemplateTableIputfield6Fi_v_; +text: .text%__1cNWatcherThreadEstop6F_v_; +text: .text%__1cNTemplateTableJgetstatic6Fi_v_; +text: .text%__1cPVM_Version_init6F_v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cRAllocateTLSOffset6F_v_: threadLS_solaris_i486.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cYVM_Version_StubGeneratorTgenerate_getPsrInfo6M_pC_: vm_version_i486.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cLVtableStubsKinitialize6F_v_; +text: .text%__1cVverificationType_init6F_v_; +text: .text%__1cIVMThread2t6M_v_; +text: .text%__1cGThreadWset_as_starting_thread6M_i_; +text: .text%__1cIVMThreadGcreate6F_v_; +text: .text%__1cbBcreate_initial_thread_group6FpnGThread__nGHandle__: thread.o; +text: .text%__1cVcreate_initial_thread6FnGHandle_pnKJavaThread_pnGThread__pnHoopDesc__: thread.o; +text: .text%__1cbAcall_initializeSystemClass6FpnGThread__v_: thread.o; +text: .text%__1cWreset_vm_info_property6FpnGThread__v_: thread.o; +text: .text%JVM_RegisterUnsafeMethods; +text: .text%__1cQvtableStubs_init6F_v_; +text: .text%__1cFVTuneEexit6F_v_; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: typeArrayKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlassKlass.o; +text: .text%__1cKvtune_init6F_v_; +text: .text%__1cIUniversePcheck_alignment6FIIpkc_v_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: constantPoolKlass.o; +text: .text%__1cSinstanceKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFJNIidKdeallocate6Fp0_v_; +text: .text%__1cNinstanceKlassZrelease_C_heap_structures6M_v_; +text: .text%__1cSset_init_completed6F_v_; +text: .text%__1cMexit_globals6F_v_; +text: .text%__1cMinit_globals6F_i_; +text: .text%__1cPvm_init_globals6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: indexSet.o; +text: .text%__1cLicache_init6F_v_; +text: .text%__1cWInlineCacheBuffer_init6F_v_; +text: .text%__1cRInlineCacheBufferKinitialize6F_v_; +text: .text%__1cDhpiYinitialize_get_interface6FpnIvm_calls__v_; +text: .text%__1cDhpiZinitialize_socket_library6F_i_; +text: .text%__1cDhpiKinitialize6F_i_; +text: .text%__1cLlog2_intptr6Fi_i_: heap.o; +text: .text%__1cICodeHeapFclear6M_v_; +text: .text%__1cICodeHeapHreserve6MIII_i_; +text: .text%__1cICodeHeap2t6M_v_; +text: .text%__1cSinstanceKlassKlassUoop_is_instanceKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cSInterpreterRuntimeSupdate_mdp_for_ret6FpnKJavaThread_i_v_; +text: .text%__1cSInterpreterRuntimeWcreate_klass_exception6FpnKJavaThread_pcpnHoopDesc__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter.o; +text: .text%__1cbCAbstractInterpreterGeneratorbCset_safepoints_for_all_bytes6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbEset_entry_points_for_all_bytes6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorMgenerate_all6M_v_; +text: .text%__1cbCAbstractInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cQinterpreter_init6F_v_; +text: .text%__1cZInterpreterMacroAssemblerbFset_method_data_pointer_for_bcp6M_v_; +text: .text%__1cZInterpreterMacroAssemblerUdispatch_only_normal6MnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpCpnMRegisterImpl_33_v_; +text: .text%__1cVInterfaceSupport_init6F_v_; +text: .text%__1cQinstanceRefKlassZupdate_nonstatic_oop_maps6FpnMklassOopDesc__v_; +text: .text%__1cSinstanceKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: instanceKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cSCommandLineFlagsExKuintxAtPut6FnXCommandLineFlagWithType_I_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: fprofiler.o; +text: .text%__1cMelapsedTimer2t6M_v_: fprofiler.o; +text: .text%__1cScheck_ThreadShadow6F_v_; +text: .text%__1cNeventlog_init6F_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: dictionary.o; +text: .text%__1cKDictionaryKclasses_do6MpFpnMklassOopDesc__v_v_; +text: .text%__1cKDictionaryKfree_entry6MpnPDictionaryEntry__v_; +text: .text%__1cKDictionary2t6Mi_v_; +text: .text%__1cFStateQ_sub_Op_TailJump6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_NegF6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_MoveF2I6MpknENode__v_; +text: .text%__1cFStateO_sub_Op_CMoveL6MpknENode__v_; +text: .text%__1cODeoptimizationTload_class_by_index6FnSconstantPoolHandle_i_v_; +text: .text%__1cODeoptimizationTload_class_by_index6FnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKScopeValuePis_constant_int6kM_i_: debugInfo.o; +text: .text%__1cUConstantOopReadValuePis_constant_oop6kM_i_: debugInfo.o; +text: .text%__1cUConstantOopReadValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cWconstantPoolCacheKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: cpCacheKlass.o; +text: .text%__1cFframeVinterpreter_frame_mdp6kM_pC_; +text: .text%__1cRcheck_basic_types6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: generateOopMap.o; +text: .text%__1cRComputeEntryStackIdo_float6M_v_: generateOopMap.o; +text: .text%__1cNCellTypeStateLmake_bottom6F_0_: generateOopMap.o; +text: .text%__1cNCellTypeStateImake_top6F_0_: generateOopMap.o; +text: .text%__1cMelapsedTimer2t6M_v_: generateOopMap.o; +text: .text%__1cWResolveOopMapConflictsUdo_potential_rewrite6MpnGThread__nMmethodHandle__; +text: .text%__1cJGenRemSetYmax_alignment_constraint6Fn0AEName__I_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: genCollectedHeap.o; +text: .text%__1cTAssertIsPermClosure2t6M_v_: genCollectedHeap.o; +text: .text%__1cRAlwaysTrueClosure2t6M_v_: genCollectedHeap.o; +text: .text%__1cKNoopGCTaskQcreate_on_c_heap6F_p0_; +text: .text%__1cNGCTaskManagerKthreads_do6MpnNThreadClosure__v_; +text: .text%__1cNGCTaskManagerKinitialize6M_v_; +text: .text%__1cNGCTaskManager2t6MI_v_; +text: .text%__1cXSynchronizedGCTaskQdDueue2t6MpnLGCTaskQdDueue_pnFMutex__v_; +text: .text%__1cLGCTaskQdDueueQcreate_on_c_heap6F_p0_; +text: .text%__1cHGCStats2t6M_v_; +text: .text%__1cQGCPolicyCounters2t6Mpkcii_v_; +text: .text%__1cXSignatureHandlerLibraryQset_handler_blob6F_pC_; +text: .text%JVM_MaxMemory; +text: .text%JVM_Halt; +text: .text%JVM_InitProperties; +text: .text%__1cQjni_handles_init6F_v_; +text: .text%__1cKJNIHandlesKinitialize6F_v_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_double_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_float_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_long_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbBgenerate_fast_get_int_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_short_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_char_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_byte_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbFgenerate_fast_get_boolean_field6F_pC_; +text: .text%JNI_CreateJavaVM; +text: .text%__1cVquicken_jni_functions6F_v_; +text: .text%__1cbDinitializeDirectBufferSupport6FpnHJNIEnv___i_: jni.o; +text: .text%lookupDirectBufferClasses: jni.o; +text: .text%__1cXjni_GetDoubleField_addr6F_pC_; +text: .text%JVM_GetClassDeclaredMethods; +text: .text%__1cLJvmtiExportNpost_vm_start6F_v_; +text: .text%__1cLJvmtiExportQenter_live_phase6F_v_; +text: .text%__1cLJvmtiExportRenter_start_phase6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEventController.o; +text: .text%__1cUJvmtiEventControllerIvm_death6F_v_; +text: .text%__1cUJvmtiEventControllerHvm_init6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_start6F_v_; +text: .text%__1cRJvmtiEventEnabledFclear6M_v_; +text: .text%__1cRJvmtiEventEnabled2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEnvBase.o; +text: .text%__1cWNonPrintingResourceObj2n6FInLResourceObjPallocation_type__pv_: jvmtiEnvBase.o; +text: .text%__1cNGrowableArray4CpnMJvmtiEnvBase__2t6Mii_v_: jvmtiEnvBase.o; +text: .text%JVM_SupportsCX8; +text: .text%__1cbEinitialize_converter_functions6F_v_; +text: .text%JVM_Socket; +text: .text%JVM_InitializeSocketLibrary; +text: .text%__1cWjni_GetFloatField_addr6F_pC_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectPcompute_offsets6F_v_; +text: .text%__1cVjava_lang_ThreadGroupPcompute_offsets6F_v_; +text: .text%__1cQjava_lang_ThreadPcompute_offsets6F_v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cHvm_exit6Fi_v_; +text: .text%__1cLbefore_exit6FpnKJavaThread__v_; +text: .text%__1cQprint_statistics6F_v_; +text: .text%__1cWinvocationCounter_init6F_v_; +text: .text%__1cLlog2_intptr6Fi_i_: interpreter_i486.o; +text: .text%__1cUInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_throw_exception6M_v_; +text: .text%__1cUInterpreterGeneratorXgenerate_accessor_entry6M_pC_; +text: .text%__1cUInterpreterGeneratorUgenerate_empty_entry6M_pC_; +text: .text%__1cUInterpreterGeneratorXgenerate_abstract_entry6M_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbFgenerate_slow_signature_handler6M_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbMgenerate_ArrayIndexOutOfBounds_handler6Mpkc_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_StackOverflowError_handler6M_pC_; +text: .text%__1cXNativeSignatureIteratorHdo_byte6M_v_: interpreterRuntime.o; +text: .text%__1cYjava_lang_reflect_MethodPcompute_offsets6F_v_; +text: .text%__1cVjni_GetLongField_addr6F_pC_; +text: .text%__1cUjni_GetIntField_addr6F_pC_; +text: .text%__1cWjni_GetShortField_addr6F_pC_; +text: .text%__1cVjni_GetCharField_addr6F_pC_; +text: .text%__1cVjni_GetByteField_addr6F_pC_; +text: .text%__1cYjni_GetBooleanField_addr6F_pC_; +text: .text%jni_ToReflectedMethod: jni.o; +text: .text%__1cQjavaClasses_init6F_v_; +text: .text%__1cLJavaClassesPcompute_offsets6F_v_; +text: .text%__1cLJavaClassesbAcompute_hard_coded_offsets6F_v_; +text: .text%__1cZsun_misc_AtomicLongCSImplPcompute_offsets6F_v_; +text: .text%__1cPjava_nio_BufferPcompute_offsets6F_v_; +text: .text%__1cQjava_lang_SystemPcompute_offsets6F_v_; +text: .text%__1cbIjava_security_AccessControlContextPcompute_offsets6F_v_; +text: .text%__1cYsun_reflect_ConstantPoolPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldPcompute_offsets6F_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPcompute_offsets6F_v_; +text: .text%__1cYjava_lang_reflect_MethodNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: cpCacheKlass.o; +text: .text%__1cJAssemblerEmull6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEadcl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEadcl6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerGmovsxw6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerGmovsxb6MpnMRegisterImpl_2_v_; +text: .text%__1cParrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: arrayKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: arrayKlassKlass.o; +text: .text%__1cParrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: arguments.o; +text: .text%__1cJArgumentsWPropertyList_get_value6FpnOSystemProperty_pkc_4_; +text: .text%__1cJArgumentsFparse6FpknOJavaVMInitArgs__i_; +text: .text%__1cJArgumentsbSparse_java_tool_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsbNparse_java_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsVfinalize_vm_init_args6FpnMSysClassPath_i_i_; +text: .text%__1cJArgumentsWparse_each_vm_init_arg6FpknOJavaVMInitArgs_pnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsSparse_vm_init_args6FpknOJavaVMInitArgs__i_; +text: .text%__1cJArgumentsZcheck_vm_args_consistency6F_i_; +text: .text%__1cJArgumentsbOparse_java_compiler_environment_variable6F_v_; +text: .text%__1cJAssemblerEsbbl6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerJdecrement6MpnMRegisterImpl_i_v_; +text: .text%__1cOMacroAssemblerLextend_sign6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerFfaddp6Mi_v_; +text: .text%__1cJAssemblerGfdivrp6Mi_v_; +text: .text%__1cJAssemblerHfdivr_d6MnHAddress__v_; +text: .text%__1cJAssemblerHfdivr_s6MnHAddress__v_; +text: .text%__1cJAssemblerGfmul_s6MnHAddress__v_; +text: .text%__1cJAssemblerHfsubr_d6MnHAddress__v_; +text: .text%__1cJAssemblerHfsubr_s6MnHAddress__v_; +text: .text%__1cJAssemblerGfadd_d6MnHAddress__v_; +text: .text%__1cJAssemblerGfadd_s6MnHAddress__v_; +text: .text%__1cJAssemblerFfsqrt6M_v_; +text: .text%__1cJAssemblerEfcos6M_v_; +text: .text%__1cJAssemblerEfsin6M_v_; +text: .text%__1cJAssemblerEsetb6Mn0AJCondition_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerExchg6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerEsubl6MnHAddress_i_v_; +text: .text%__1cJAssemblerFshldl6MpnMRegisterImpl_2_v_; +text: .text%__1cJArgumentsUset_ergonomics_flags6F_v_; +text: .text%__1cHi2sNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHi2bNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreBNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLconvP2BNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOtailjmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_i486_expand.o; +text: .text%__1cRaddL_eReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLconvP2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIcp2bNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_i486.o; +text: .text%__1cTconvI2F_SSF_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQmulD_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOtailjmpIndNodeFreloc6kM_i_; +text: .text%__1cTconvI2F_SSF_memNodeFreloc6kM_i_; +text: .text%__1cQmulD_reg_memNodeFreloc6kM_i_; +text: .text%__1cQaccessFlags_init6F_v_; +text: .text%__1cPconvI2L_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJArgumentsbBset_cms_and_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsTset_parnew_gc_flags6F_v_; +text: .text%__1cQno_shared_spaces6F_v_: arguments.o; +text: .text%__1cJArgumentsMget_property6Fpkc_2_; +text: .text%__1cJArgumentsVprocess_settings_file6Fpkcii_i_; +text: .text%__1cMSysClassPathQadd_jars_to_path6Fpcpkc_1_; +text: .text%__1cMSysClassPathPexpand_endorsed6M_v_; +text: .text%__1cMSysClassPath2T6M_v_; +text: .text%__1cMSysClassPath2t6Mpkc_v_; +text: .text%__1cJArgumentsWinit_system_properties6F_v_; +text: .text%__1cFChunkbDstart_chunk_pool_cleaner_task6F_v_; +text: .text%__1cOchunkpool_init6F_v_; +text: .text%__1cWAdjoiningVirtualSpaces2t6MnNReservedSpace_III_v_; +text: .text%__1cUAdjoiningGenerations2t6MnNReservedSpace_IIIIIII_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_i486_pipeline.o; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCpnMRegisterImpl__v_; +text: .text%__1cOcompiler2_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compilationPolicy.o; +text: .text%__1cMelapsedTimer2t6M_v_: compilationPolicy.o; +text: .text%__1cRCompilationPolicyUcompleted_vm_startup6F_v_; +text: .text%__1cWcompilationPolicy_init6F_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyUinitialize_size_info6M_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyQinitialize_flags6M_v_; +text: .text%__1cNCollectedHeap2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeCache.o; +text: .text%__1cOcodeCache_init6F_v_; +text: .text%__1cJCodeCacheKinitialize6F_v_; +text: .text%__1cNExceptionBlob2n6FII_pv_; +text: .text%__1cNExceptionBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNExceptionBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cQUncommonTrapBlob2n6FII_pv_; +text: .text%__1cQUncommonTrapBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cQUncommonTrapBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cSDeoptimizationBlob2n6FII_pv_; +text: .text%__1cNCompileBrokerQcompilation_init6FpnQAbstractCompiler__v_; +text: .text%__1cWconstantPoolCacheKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cRconstantPoolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constantPoolKlass.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: constantPoolKlass.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: constantPoolKlass.o; +text: .text%__1cRconstantPoolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cQconstMethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassOset_alloc_size6MI_v_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cTcompilerOracle_init6F_v_; +text: .text%__1cOCompilerOracleRparse_from_string6Fpkc_v_; +text: .text%__1cOCompilerOraclePparse_from_file6F_v_; +text: .text%__1cHcc_file6F_pkc_: compilerOracle.o; +text: .text%__1cOCompilerOracleOread_from_line6Fpc_v_; +text: .text%__1cVcompiledICHolderKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: compiledICHolderKlass.o; +text: .text%__1cVcompiledICHolderKlassOset_alloc_size6MI_v_: compiledICHolderKlass.o; +text: .text%__1cVcompiledICHolderKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compileBroker.o; +text: .text%__1cNCompileBrokerQset_should_block6F_v_; +text: .text%__1cNCompileBrokerVinit_compiler_threads6Fi_v_; +text: .text%__1cSDeoptimizationBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_iiii_p0_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: cfgnode.o; +text: .text%__1cRCardTableModRefBSbBct_max_alignment_constraint6F_I_; +text: .text%__1cRCardTableModRefBS2t6MnJMemRegion_i_v_; +text: .text%__1cMTailJumpNode2t6MpnENode_22222_v_; +text: .text%__1cKC2CompilerKinitialize6M_v_; +text: .text%__1cHCompileRpd_compiler2_init6F_v_; +text: .text%__1cJBytecodesNpd_initialize6F_v_; +text: .text%__1cObytecodes_init6F_v_; +text: .text%__1cJBytecodesKinitialize6F_v_; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_; +text: .text%__1cOMacroAssemblerQsign_extend_byte6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerRsign_extend_short6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerIlcmp2int6MpnMRegisterImpl_222_v_; +text: .text%__1cOMacroAssemblerElshl6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerElmul6Mii_v_; +text: .text%__1cOMacroAssemblerElneg6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerGc2bool6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCpnMRegisterImpl_3_v_; +text: .text%__1cMciArrayKlass2t6MpnIciSymbol_ipnHciKlass__v_; +text: .text%__1cSDeoptimizationBlob2t6MpnKCodeBuffer_ipnJOopMapSet_iiii_v_; +text: .text%__1cLMoveF2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cTClassLoadingServiceVnotify_class_unloaded6FpnNinstanceKlass_i_v_; +text: .text%__1cTClassLoadingServiceEinit6F_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: classLoader.o; +text: .text%__1cQclassLoader_init6F_v_; +text: .text%__1cLClassLoaderVcompute_Object_vtable6F_i_; +text: .text%__1cLClassLoaderKinitialize6F_v_; +text: .text%__1cLClassLoaderZcreate_package_info_table6F_v_; +text: .text%__1cLClassLoaderQload_zip_library6F_v_; +text: .text%__1cLClassLoaderbBsetup_bootstrap_search_path6F_v_; +text: .text%__1cPClassFileParserYjava_lang_Class_fix_post6Mpi_v_; +text: .text%__1cPClassFileParserXjava_lang_Class_fix_pre6MpnOobjArrayHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cPClassFileParserbFjava_lang_ref_Reference_fix_pre6MpnPtypeArrayHandle_nSconstantPoolHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cRciArrayKlassKlassUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cPciObjectFactoryTinit_shared_objects6M_v_; +text: .text%__1cUciObjArrayKlassKlassEmake6F_p0_; +text: .text%__1cPciObjArrayKlass2t6MpnIciSymbol_pnHciKlass_i_v_; +text: .text%__1cMciKlassKlassEmake6F_p0_; +text: .text%__1cLJvmtiExportTpost_vm_initialized6F_v_; +text: .text%__1cNRegisterSaverYrestore_result_registers6FpnOMacroAssembler__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: runtimeService.o; +text: .text%__1cORuntimeServiceYrecord_application_start6F_v_; +text: .text%__1cORuntimeServiceEinit6F_v_; +text: .text%__1cLOptoRuntimeVhandle_exception_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray5_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray4_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray3_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray2_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray1_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeIgenerate6FpnFciEnv__v_; +text: .text%__1cWResolveOopMapConflictsOreport_results6kM_i_: rewriter.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: relocInfo.o; +text: .text%__1cQRelocationHolder2t6M_v_: relocInfo.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: regmask.o; +text: .text%__1cNSharedRuntimeTgenerate_deopt_blob6F_v_; +text: .text%__1cLStatSamplerbMcreate_system_property_instrumentation6FpnGThread__v_; +text: .text%__1cLStatSamplerHdestroy6F_v_; +text: .text%__1cLStatSamplerJdisengage6F_v_; +text: .text%__1cLStatSamplerGengage6F_v_; +text: .text%__1cLStatSamplerKinitialize6F_v_; +text: .text%__1cNcarSpace_init6F_v_; +text: .text%__1cICarSpaceEinit6F_v_; +text: .text%__1cNSharedRuntimebIinitialize_StrictMath_entry_points6F_v_; +text: .text%__1cNSharedRuntimeUlookup_function_DD_D6FrpFpnHJNIEnv__pnH_jclass_dd_dpkc_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedHeap.o; +text: .text%__1cTAssertIsPermClosure2t6M_v_: sharedHeap.o; +text: .text%__1cLOptoRuntimeYgenerate_arraycopy_stubs6F_v_; +text: .text%__1cLOptoRuntimebPgenerate_polling_page_return_handler_blob6F_v_; +text: .text%__1cLOptoRuntimebSgenerate_polling_page_safepoint_handler_blob6F_v_; +text: .text%__1cLOptoRuntimebPgenerate_illegal_instruction_handler_blob6F_v_; +text: .text%__1cLOptoRuntimeUsetup_exception_blob6F_v_; +text: .text%__1cLOptoRuntimeWfill_in_exception_blob6F_v_; +text: .text%__1cLOptoRuntimebBgenerate_uncommon_trap_blob6F_v_; +text: .text%__1cHRegMask2t6Miiiii_v_: regmask.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psMarkSweep.o; +text: .text%__1cMelapsedTimer2t6M_v_: psMarkSweep.o; +text: .text%__1cTPSAlwaysTrueClosure2t6M_v_: psMarkSweep.o; +text: .text%__1cLPSMarkSweepKinitialize6F_v_; +text: .text%__1cbAPSGCAdaptivePolicyCounters2t6MpkciipnUPSAdaptiveSizePolicy__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psAdaptiveSizePolicy.o; +text: .text%__1cUPSAdaptiveSizePolicy2t6MIIIIIddI_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: placeholders.o; +text: .text%__1cQPlaceholderTable2t6Mi_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: phase.o; +text: .text%__1cKPerfMemoryUdelete_memory_region6F_v_; +text: .text%__1cKPerfMemoryUcreate_memory_region6FI_v_; +text: .text%__1cUdelete_shared_memory6FpcI_v_: perfMemory_solaris.o; +text: .text%__1cUcreate_shared_memory6FI_pc_: perfMemory_solaris.o; +text: .text%__1cSmmap_create_shared6FI_pc_: perfMemory_solaris.o; +text: .text%__1cbAcreate_sharedmem_resources6Fpkc1I_i_: perfMemory_solaris.o; +text: .text%__1cRmake_user_tmp_dir6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cbBcleanup_sharedmem_resources6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cIPSOldGen2t6MnNReservedSpace_IIIIpkci_v_; +text: .text%__1cSReferenceProcessorMinit_statics6F_v_; +text: .text%__1cXreferenceProcessor_init6F_v_; +text: .text%__1cURecompilationMonitorbGstart_recompilation_monitor_task6F_v_; +text: .text%__1cKPSYoungGenUset_space_boundaries6MII_v_; +text: .text%__1cKPSYoungGenbGcompute_initial_space_boundaries6M_v_; +text: .text%__1cKPSYoungGenPinitialize_work6M_v_; +text: .text%__1cKPSYoungGenKinitialize6MnNReservedSpace_I_v_; +text: .text%__1cKPSYoungGenYinitialize_virtual_space6MnNReservedSpace_I_v_; +text: .text%__1cKPSYoungGen2t6MIII_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psScavenge.o; +text: .text%__1cMelapsedTimer2t6M_v_: psScavenge.o; +text: .text%__1cKPSScavengeKinitialize6F_v_; +text: .text%__1cPOopTaskQdDueueSet2t6Mi_v_: psPromotionManager.o; +text: .text%__1cSPSPromotionManagerKinitialize6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psPromotionLAB.o; +text: .text%__1cMarrayOopDescLheader_size6FnJBasicType__i_: psPromotionLAB.o; +text: .text%__1cRalign_object_size6Fi_i_: psPromotionLAB.o; +text: .text%__1cJPSPermGen2t6MnNReservedSpace_IIIIpkci_v_; +text: .text%__1cIPSOldGen2t6MIIIpkci_v_; +text: .text%__1cLStatSamplerUcreate_misc_perfdata6F_v_; +text: .text%__1cNTemplateTableGbaload6F_v_; +text: .text%__1cNTemplateTableGaaload6F_v_; +text: .text%__1cNTemplateTableGdaload6F_v_; +text: .text%__1cNTemplateTableGfaload6F_v_; +text: .text%__1cNTemplateTableGlaload6F_v_; +text: .text%__1cNTemplateTableGiaload6F_v_; +text: .text%__1cNTemplateTableKwide_aload6F_v_; +text: .text%__1cNTemplateTableKwide_dload6F_v_; +text: .text%__1cNTemplateTableKwide_fload6F_v_; +text: .text%__1cNTemplateTableKwide_lload6F_v_; +text: .text%__1cNTemplateTableKwide_iload6F_v_; +text: .text%__1cNTemplateTableFaload6F_v_; +text: .text%__1cNTemplateTableFdload6F_v_; +text: .text%__1cNTemplateTableFfload6F_v_; +text: .text%__1cNTemplateTableFlload6F_v_; +text: .text%__1cNTemplateTableKfast_iload6F_v_; +text: .text%__1cNTemplateTableLfast_iload26F_v_; +text: .text%__1cNTemplateTableFiload6F_v_; +text: .text%__1cNTemplateTableGcaload6F_v_; +text: .text%__1cNTemplateTableHsastore6F_v_; +text: .text%__1cNTemplateTableHbastore6F_v_; +text: .text%__1cNTemplateTableHaastore6F_v_; +text: .text%__1cNTemplateTableHdastore6F_v_; +text: .text%__1cNTemplateTableHfastore6F_v_; +text: .text%__1cNTemplateTableHlastore6F_v_; +text: .text%__1cNTemplateTableHiastore6F_v_; +text: .text%__1cNTemplateTableLwide_astore6F_v_; +text: .text%__1cNTemplateTableLwide_dstore6F_v_; +text: .text%__1cNTemplateTableLwide_fstore6F_v_; +text: .text%__1cNTemplateTableGastore6F_v_; +text: .text%__1cNTemplateTableGdstore6F_v_; +text: .text%__1cNTemplateTableGfstore6F_v_; +text: .text%__1cNTemplateTableGlstore6F_v_; +text: .text%__1cNTemplateTableGistore6F_v_; +text: .text%__1cNTemplateTableHaload_06F_v_; +text: .text%__1cNTemplateTableGsaload6F_v_; +text: .text%__1cNTemplateTableMfast_icaload6F_v_; +text: .text%__1cNTemplateTableGldc2_w6F_v_; +text: .text%__1cLsymbolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: symbolKlass.o; +text: .text%__1cLsymbolKlassOset_alloc_size6MI_v_: symbolKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: symbolKlass.o; +text: .text%__1cLsymbolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cINegFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cISubFNodeDsub6kMpknEType_3_3_; +text: .text%__1cSstubRoutines_init26F_v_; +text: .text%__1cSstubRoutines_init16F_v_; +text: .text%__1cMStubRoutinesLinitialize26F_v_; +text: .text%__1cMStubRoutinesLinitialize16F_v_; +text: .text%__1cNStubGeneratorTgenerate_verify_oop6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorbAgenerate_forward_exception6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorSgenerate_call_stub6MrpC_1_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorYgenerate_catch_exception6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorUgenerate_atomic_xchg6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorYgenerate_get_previous_fp6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorUcreate_control_words6M_v_: stubGenerator_i486.o; +text: .text%__1cLStatSamplerXcreate_sampled_perfdata6F_v_; +text: .text%__1cQSystemDictionarybAcompute_java_system_loader6FpnGThread__v_; +text: .text%__1cNTemplateTableGsipush6F_v_; +text: .text%__1cNTemplateTableGbipush6F_v_; +text: .text%__1cNTemplateTableLaconst_null6F_v_; +text: .text%__1cNTemplateTableSshouldnotreachhere6F_v_; +text: .text%__1cNTemplateTableDnop6F_v_; +text: .text%__1cNTemplateTableNpd_initialize6F_v_; +text: .text%__1cStemplateTable_init6F_v_; +text: .text%__1cNTemplateTableDjsr6F_v_; +text: .text%__1cNTemplateTableFjsr_w6F_v_; +text: .text%__1cNTemplateTableGgoto_w6F_v_; +text: .text%__1cNTemplateTableF_goto6F_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC222_v_; +text: .text%__1cMPeriodicTaskJdisenroll6M_v_; +text: .text%__1cMPeriodicTaskLis_enrolled6kM_i_; +text: .text%__1cMPeriodicTask2T5B6M_v_; +text: .text%__1cQSystemDictionarybDinitialize_basic_type_mirrors6FpnGThread__v_; +text: .text%__1cQSystemDictionarybCinitialize_preloaded_classes6FpnGThread__v_; +text: .text%__1cQSystemDictionaryKinitialize6FpnGThread__v_; +text: .text%__1cQSystemDictionaryKclasses_do6FpFpnMklassOopDesc__v_v_; +text: .text%__1cLremove_file6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cNMemoryServicebFadd_parallel_scavenge_heap_info6FpnUParallelScavengeHeap__v_; +text: .text%__1cNMemoryServiceRset_universe_heap6FpnNCollectedHeap__v_; +text: .text%__1cMCodeHeapPool2t6MpnICodeHeap_pkci_v_; +text: .text%__1cYSurvivorMutableSpacePool2t6MpnKPSYoungGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cUEdenMutableSpacePool2t6MpnKPSYoungGen_pnMMutableSpace_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cQPSGenerationPool2t6MpnJPSPermGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cQPSGenerationPool2t6MpnIPSOldGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cNMemoryManagerbEget_psMarkSweep_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cNMemoryManagerbDget_psScavenge_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cNMemoryManagerbDget_code_cache_memory_manager6F_p0_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: matcher.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: markSweep.o; +text: .text%__1cJMarkSweepSMarkAndPushClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepRFollowRootClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepSFollowStackClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepOIsAliveClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepQKeepAliveClosure2t6M_v_: markSweep.o; +text: .text%__1cOmarksweep_init6F_v_; +text: .text%__1cNMemoryServiceXadd_psYoung_memory_pool6FpnKPSYoungGen_pnNMemoryManager_4_v_; +text: .text%__1cKmutex_init6F_v_; +text: .text%__1cMMutableSpaceOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: methodLiveness.o; +text: .text%__1cLmethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodKlass.o; +text: .text%__1cLmethodKlassOset_alloc_size6MI_v_: methodKlass.o; +text: .text%__1cLmethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cHRetDataKis_RetData6M_i_: methodDataOop.o; +text: .text%__1cHRetDataJfixup_ret6MinQmethodDataHandle__pC_; +text: .text%__1cHRetDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cPmethodDataKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodDataKlass.o; +text: .text%__1cPmethodDataKlassOset_alloc_size6MI_v_: methodDataKlass.o; +text: .text%__1cPmethodDataKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: memoryService.o; +text: .text%__1cNGrowableArray4CpnKMemoryPool__2t6Mii_v_: memoryService.o; +text: .text%__1cNGrowableArray4CpnNMemoryManager__2t6Mii_v_: memoryService.o; +text: .text%__1cNMemoryServiceZadd_code_heap_memory_pool6FpnICodeHeap__v_; +text: .text%__1cNMemoryServiceWadd_psPerm_memory_pool6FpnJPSPermGen_pnNMemoryManager__v_; +text: .text%__1cNMemoryServiceVadd_psOld_memory_pool6FpnIPSOldGen_pnNMemoryManager__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: management.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: klassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: klassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: klassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassUoop_is_instanceKlass6kM_i_: klassKlass.o; +text: .text%__1cKklassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiTagMap.o; +text: .text%__1cJMemRegion2t6M_v_: jvmtiTagMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiImpl.o; +text: .text%__1cWNonPrintingResourceObj2n6FInLResourceObjPallocation_type__pv_: jvmtiImpl.o; +text: .text%__1cNGrowableArray4CpnPJvmtiRawMonitor__2t6Mii_v_: jvmtiImpl.o; +text: .text%__1cUJvmtiPendingMonitorsXtransition_raw_monitors6F_v_; +text: .text%__1cLJvmtiExportbMtransition_pending_onload_raw_monitors6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_death6F_v_; +text: .text%__1cKklassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: klassKlass.o; +text: .text%__1cJTimeStamp2t6M_v_: management.o; +text: .text%__1cKManagementWrecord_vm_startup_time6Fxx_v_; +text: .text%__1cKManagementKinitialize6FpnGThread__v_; +text: .text%__1cKManagementEinit6F_v_; +text: .text%__1cPmanagement_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: machnode.o; +text: .text%__1cGThreadMis_VM_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cXLowMemoryDetectorThreadbCis_hidden_from_external_view6kM_i_: lowMemoryDetector.o; +text: .text%__1cRLowMemoryDetectorbGlow_memory_detector_thread_entry6FpnKJavaThread_pnGThread__v_; +text: .text%__1cRLowMemoryDetectorUhas_pending_requests6F_i_; +text: .text%__1cRLowMemoryDetectorKinitialize6F_v_; +text: .text%__1cKRegionNodeUdepends_only_on_test6kM_i_: loopnode.o; +text: .text%__1cNIdealLoopTreeUmerge_many_backedges6MpnOPhaseIdealLoop__v_; +text: .text%__1cKfix_parent6FpnNIdealLoopTree_1_v_: loopnode.o; +text: .text%__1cNIdealLoopTreeQsplit_outer_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: loaderConstraints.o; +text: .text%__1cVLoaderConstraintTable2t6Mi_v_; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: library_call.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: objArrayKlass.o; +text: .text%__1cUParallelScavengeHeapMmax_capacity6kM_I_; +text: .text%__1cUParallelScavengeHeapPpost_initialize6M_v_; +text: .text%__1cUParallelScavengeHeapKinitialize6M_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: parGCAllocBuffer.o; +text: .text%__1cMarrayOopDescLheader_size6FnJBasicType__i_: parGCAllocBuffer.o; +text: .text%__1cRalign_object_size6Fi_i_: parGCAllocBuffer.o; +text: .text%__1cHoopDescLheader_size6F_i_: parGCAllocBuffer.o; +text: .text%__SLIP.DELETER__C: ostream.o; +text: .text%__1cMostream_exit6F_v_; +text: .text%__1cQostream_init_log6F_v_; +text: .text%__1cMostream_init6F_v_; +text: .text%__1cNdefaultStreamEinit6M_v_; +text: .text%__1cCosMsupports_sse6F_i_; +text: .text%__1cVcheck_for_sse_support6F_v_: os_solaris_i486.o; +text: .text%__1cCosGinit_26F_i_; +text: .text%__1cCosEinit6F_v_; +text: .text%__1cCosHSolarisUsynchronization_init6F_v_; +text: .text%__1cCosHSolarisOlibthread_init6F_v_; +text: .text%__1cUParallelScavengeHeapYpermanent_object_iterate6MpnNObjectClosure__v_; +text: .text%__1cWget_sharedmem_filename6Fpkci_pc_: perfMemory_solaris.o; +text: .text%__1cNget_user_name6Fl_pc_: perfMemory_solaris.o; +text: .text%__1cQget_user_tmp_dir6Fpkc_pc_: perfMemory_solaris.o; +text: .text%__1cKPerfMemoryHdestroy6F_v_; +text: .text%__1cKPerfMemoryKinitialize6F_v_; +text: .text%__1cPperfMemory_exit6F_v_; +text: .text%__1cPperfMemory_init6F_v_; +text: .text%__1cMPerfDataListFclone6M_p0_; +text: .text%__1cMPerfDataList2t6Mp0_v_; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnPPerfLongCounter__; +text: .text%__1cPPerfDataManagerHsampled6F_pnMPerfDataList__; +text: .text%__1cPPerfDataManagerHdestroy6F_v_; +text: .text%__1cFParseMjump_if_join6MpnENode_2_2_; +text: .text%__1cPGenerationSizerQinitialize_flags6M_v_: parallelScavengeHeap.o; +text: .text%__1cbCTwoGenerationCollectorPolicyMrem_set_name6M_nJGenRemSetEName__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapEkind6M_nNCollectedHeapEName__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapIend_addr6kM_ppnIHeapWord__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapEheap6F_p0_; +text: .text%__1cUParallelScavengeHeapNgc_threads_do6kMpnNThreadClosure__v_; +text: .text%__1cOisT2_libthread6F_i_; +text: .text%__1cCosNset_boot_path6Fcc_i_; +text: .text%__1cCosLinit_random6Fl_v_; +text: .text%__1cCosGstrdup6Fpkc_pc_; +text: .text%__1cCosXterminate_signal_thread6F_v_; +text: .text%__1cCosLsignal_init6F_v_; +text: .text%__1cTsignal_thread_entry6FpnKJavaThread_pnGThread__v_: os.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: oopMap.o; +text: .text%__1cQDoNothingClosure2t6M_v_: oopMap.o; +text: .text%__1cXonStackReplacement_init6F_v_; +text: .text%__1cSOnStackReplacementKinitialize6F_v_; +text: .text%__1cNObjectMonitorREntryQdDueue_unlink6MpnMObjectWaiter__v_; +text: .text%__1cSobjArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: objArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassUoop_is_objArrayKlass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassbEallocate_system_objArray_klass6MpnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlass.o; +text: .text%__1cCosXis_server_class_machine6F_i_; +text: .text%__1cCosHSolarisXinstall_signal_handlers6F_v_; +text: .text%__1cRlwp_priocntl_init6F_i_: os_solaris.o; +text: .text%__1cNpriocntl_stub6FinGidtype_lipc_l_: os_solaris.o; +text: .text%__1cCosHSolarisRmpss_sanity_check6F_v_; +text: .text%__1cCosPuncommit_memory6FpcI_i_; +text: .text%__1cCosLsignal_wait6F_i_; +text: .text%__1cVcheck_pending_signals6Fi_i_: os_solaris.o; +text: .text%__1cCosNsignal_notify6Fi_v_; +text: .text%__1cCosOsignal_init_pd6F_v_; +text: .text%__1cCosHSolarisPinit_signal_mem6F_v_; +text: .text%__1cCosSget_temp_directory6F_pkc_; +text: .text%__1cCosbDallocate_thread_local_storage6F_i_; +text: .text%__1cCosHSolarisQsignal_sets_init6F_v_; +text: .text%__1cCosScreate_main_thread6FpnGThread__i_; +text: .text%__1cQcreate_os_thread6FpnGThread_I_pnIOSThread__: os_solaris.o; +text: .text%__1cCosbDinit_system_properties_values6F_v_; +text: .text%__1cCosHSolarisWinitialize_system_info6F_v_; +text: .text%__1cCosPphysical_memory6F_X_; +text: .text%__1cSThreadLocalStorageHpd_init6F_v_; diff --git a/hotspot/build/solaris/makefiles/reorder_COMPILER2_sparc b/hotspot/build/solaris/makefiles/reorder_COMPILER2_sparc new file mode 100644 index 00000000000..4eadf379ca5 --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_COMPILER2_sparc @@ -0,0 +1,7112 @@ +data = R0x2000; +text = LOAD ?RXO; + + +text: .text%__1cLOptoRuntimeLjshort_copy6Fph1I_v_; +text: .text%__1cLOptoRuntimeTarrayof_jshort_copy6FpnIHeapWord_2I_v_; +text: .text%__1cSPSPromotionManagerWcopy_to_survivor_space6MpnHoopDesc__2_; +text: .text%__1cCosOjavaTimeMillis6F_x_; +text: .text%__1cQIndexSetIteratorQadvance_and_next6M_I_; +text: .text%__1cNinstanceKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cJMarkSweepO_mark_and_push6FppnHoopDesc__v_; +text: .text%__1cNinstanceKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cOtypeArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cOtypeArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMOopTaskQdDueueKpop_global6MrpnHoopDesc__i_; +text: .text%__1cIMachNodeHis_Mach6M_p0_: ad_sparc_misc.o; +text: .text%__1cIPhaseIFGIadd_edge6MII_i_; +text: .text%__1cQIndexSetIterator2t6MpnIIndexSet__v_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: ifg.o; +text: .text%__1cJMarkSweepPmark_and_follow6FppnHoopDesc__v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: ad_sparc_misc.o; +text: .text%__1cENodeEjvms6kM_pnIJVMState__; +text: .text%__1cHRegMaskFis_UP6kM_i_; +text: .text%__1cUGenericGrowableArrayLraw_at_grow6MipknEGrET__pv_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: ad_sparc_misc.o; +text: .text%__1cIMachNodeNrematerialize6kM_i_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: classes.o; +text: .text%__1cIProjNodeHis_Proj6M_p0_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: classes.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: classes.o; +text: .text%__1cIIndexSetWalloc_block_containing6MI_pn0AIBitBlock__; +text: .text%__1cENodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cETypeDcmp6Fkpk03_i_; +text: .text%__1cENodeHis_Copy6kM_I_: classes.o; +text: .text%__1cENodeHlatency6MI_I_; +text: .text%__1cHRegMaskJis_bound16kM_i_; +text: .text%__1cDff16FI_i_; +text: .text%__1cHRegMaskESize6kM_I_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: ad_sparc_misc.o; +text: .text%__1cXresource_allocate_bytes6FI_pc_; +text: .text%__1cIMachNodeJideal_reg6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__: classes.o; +text: .text%__1cRMachSpillCopyNodeMis_SpillCopy6M_p0_: ad_sparc.o; +text: .text%__1cENodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJVectorSet2R6MI_rnDSet__; +text: .text%__1cHRegMaskJis_bound26kM_i_; +text: .text%__1cNSharedRuntimeElmul6Fxx_x_; +text: .text%__1cPOopTaskQdDueueSetFsteal6MipirpnHoopDesc__i_; +text: .text%__1cIMachNodeGOpcode6kM_i_; +text: .text%__1cENodeGpinned6kM_i_: classes.o; +text: .text%__1cJiRegIOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cIIndexSetKinitialize6MI_v_; +text: .text%__1cITypeNodeLbottom_type6kM_pknEType__; +text: .text%__1cNRelocIteratorEnext6M_i_: relocInfo.o; +text: .text%__1cRMachSpillCopyNodeHis_Copy6kM_I_: ad_sparc.o; +text: .text%__1cPClassFileStreamGget_u26MpnGThread__H_; +text: .text%__1cKTypeOopPtrFklass6kM_pnHciKlass__: type.o; +text: .text%__1cHPhiNodeGis_Phi6M_p0_: cfgnode.o; +text: .text%__1cETypeFuhash6Fkpk0_i_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: chaitin.o; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_: callnode.o; +text: .text%__1cENodeIout_grow6MI_v_; +text: .text%__1cIMachNodeHis_Mach6M_p0_: ad_sparc.o; +text: .text%__1cJCProjNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cOloadConI13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNobjArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cENodeHadd_req6Mp0_v_; +text: .text%__1cEDictGInsert6Mpv1i_1_; +text: .text%__1cJMarkSweepUAdjustPointerClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cJMultiNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeGis_CFG6kM_i_: cfgnode.o; +text: .text%__1cOloadConI13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOloadConI13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMarkSweepOIsAliveClosureLdo_object_b6MpnHoopDesc__i_: markSweep.o; +text: .text%__1cKRegionNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cIProjNodeGis_CFG6kM_i_; +text: .text%__1cNobjArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNobjArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cINodeHashLhash_delete6MpknENode__i_; +text: .text%__1cFArenaIcontains6kMpkv_i_; +text: .text%__1cOloadConI13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cICallNodeKmatch_edge6kMI_I_; +text: .text%__1cINodeHashQhash_find_insert6MpnENode__2_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: classes.o; +text: .text%__1cHPhiNodeGOpcode6kM_i_; +text: .text%__1cHTypeIntCeq6kMpknEType__i_; +text: .text%__1cKbranchNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cIProjNodeGpinned6kM_i_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: classes.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: classes.o; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__: ad_sparc_misc.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: cfgnode.o; +text: .text%__1cIProjNodeGOpcode6kM_i_; +text: .text%__1cETypeIhashcons6M_pk0_; +text: .text%__1cOPhaseIdealLoopUbuild_loop_late_post6MpnENode_pk0_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: nmethod.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: ad_sparc.o; +text: .text%__1cMPhaseChaitinTinterfere_with_live6MIpnIIndexSet__v_; +text: .text%__1cWNode_Backward_IteratorEnext6M_pnENode__; +text: .text%__1cNIdealLoopTreeJis_member6kMpk0_i_; +text: .text%__1cMPhaseChaitinKelide_copy6MpnENode_ipnFBlock_rnJNode_List_6i_i_; +text: .text%__1cQObjectStartArrayMobject_start6MpnIHeapWord__2_: cardTableExtension.o; +text: .text%__1cHCompileRvalid_bundle_info6MpknENode__i_; +text: .text%__1cENodeNrematerialize6kM_i_: classes.o; +text: .text%__1cMMachCallNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cHCompileNnode_bundling6MpknENode__pnGBundle__; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopopts.o; +text: .text%__1cOlower_pressure6FpnDLRG_IpnFBlock_pI4_v_: ifg.o; +text: .text%__1cGIfNodeGOpcode6kM_i_; +text: .text%__1cOPhaseIdealLoopYsplit_if_with_blocks_pre6MpnENode__2_; +text: .text%__1cOPhaseIdealLoopZsplit_if_with_blocks_post6MpnENode__v_; +text: .text%__1cIUniverseMnon_oop_word6F_pv_; +text: .text%__1cDLRGOcompute_degree6kMr0_i_; +text: .text%__1cFArenaIArealloc6MpvII_1_; +text: .text%__1cIConINodeGOpcode6kM_i_; +text: .text%__1cETypeEmeet6kMpk0_2_; +text: .text%__1cENode2t6MI_v_; +text: .text%__1cRMachSpillCopyNodeJideal_reg6kM_I_: ad_sparc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cIPipelineXfunctional_unit_latency6kMIpk0_I_; +text: .text%__1cWPSScavengeRootsClosureGdo_oop6MppnHoopDesc__v_: psTasks.o; +text: .text%__1cENodeHis_Copy6kM_I_: cfgnode.o; +text: .text%__1cKSchedulingLanti_do_def6MpnFBlock_pnENode_nHOptoRegEName_i_v_; +text: .text%__1cLsymbolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__: cfgnode.o; +text: .text%__1cKIfTrueNodeGOpcode6kM_i_; +text: .text%__1cNRelocIteratorTadvance_over_prefix6M_v_; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_: ad_sparc.o; +text: .text%__1cIMachNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cETypeJsingleton6kM_i_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: coalesce.o; +text: .text%__1cIMachNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cJloadPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIPhaseIFGQeffective_degree6kMI_i_; +text: .text%__1cWConstantPoolCacheEntryPfollow_contents6M_v_; +text: .text%__1cWConstantPoolCacheEntryPadjust_pointers6M_v_; +text: .text%__1cIAddPNodeGOpcode6kM_i_; +text: .text%__1cIPhaseIFGJre_insert6MI_v_; +text: .text%__1cIPhaseIFGLremove_node6MI_pnIIndexSet__; +text: .text%__1cKNode_ArrayGinsert6MIpnENode__v_; +text: .text%__1cHTypeIntEhash6kM_i_; +text: .text%__1cETypeLisa_oop_ptr6kM_i_; +text: .text%__1cLsymbolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLsymbolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cMPhaseIterGVNNtransform_old6MpnENode__2_; +text: .text%__1cDfh16FI_i_; +text: .text%__1cNMachIdealNodeErule6kM_I_: ad_sparc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: ad_sparc_misc.o; +text: .text%__1cIciObjectGequals6Mp0_i_; +text: .text%__1cIIndexSetKfree_block6MI_v_; +text: .text%__1cWShouldNotReachHereNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cHTypeIntJsingleton6kM_i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: classes.o; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: cfgnode.o; +text: .text%__1cLIfFalseNodeGOpcode6kM_i_; +text: .text%__1cSCallStaticJavaNodeGOpcode6kM_i_; +text: .text%__1cENodeEhash6kM_I_; +text: .text%__1cOPhaseIdealLoopEsort6MpnNIdealLoopTree_2_2_; +text: .text%__1cMMachProjNodeLbottom_type6kM_pknEType__; +text: .text%JVM_ArrayCopy; +text: .text%__1cOtypeArrayKlassQoop_is_typeArray6kM_i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cNSharedRuntimeDl2f6Fx_f_; +text: .text%__1cPjava_lang_ClassLas_klassOop6FpnHoopDesc__pnMklassOopDesc__; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: classes.o; +text: .text%__1cIParmNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cMPhaseChaitinKbias_color6MrnDLRG_i_nHOptoRegEName__; +text: .text%__1cHConNodeGOpcode6kM_i_; +text: .text%__1cMPhaseIterGVNWadd_users_to_worklist06MpnENode__v_; +text: .text%__1cMMachProjNodeGOpcode6kM_i_; +text: .text%__1cJiRegPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: classes.o; +text: .text%__1cJiRegIOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cENodeXpartial_latency_of_defs6MrnLBlock_Array_rnNGrowableArray4CI___v_; +text: .text%__1cXPipeline_Use_Cycle_Mask2L6Mi_r0_: ad_sparc_pipeline.o; +text: .text%__1cIBoolNodeGOpcode6kM_i_; +text: .text%__1cJMultiNodeIis_Multi6M_p0_; +text: .text%__1cYCallStaticJavaDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeEgrow6MI_v_; +text: .text%__1cIciObjectEhash6M_i_; +text: .text%__1cKRegionNodeGOpcode6kM_i_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: relocInfo.o; +text: .text%__1cOPhaseIdealLoopUbuild_loop_tree_impl6MpnENode_i_i_; +text: .text%__1cJMarkSweepSMarkAndPushClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cRMachSpillCopyNodeLbottom_type6kM_pknEType__: ad_sparc.o; +text: .text%__1cOPhaseIdealLoopOget_early_ctrl6MpnENode__2_; +text: .text%__1cIIndexSetKinitialize6MIpnFArena__v_; +text: .text%__1cLmethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cFMutexGunlock6M_v_; +text: .text%__1cIPhaseGVNJtransform6MpnENode__2_; +text: .text%__1cFStateRMachOperGenerator6MipnIMachNode_pnHCompile__pnIMachOper__; +text: .text%__1cKRelocationNunpack_2_ints6Mri1_v_: relocInfo.o; +text: .text%__1cOoop_RelocationLunpack_data6M_v_; +text: .text%__1cRmethodDataOopDescHdata_at6Mi_pnLProfileData__; +text: .text%__1cNRelocIteratorEnext6M_i_: codeBlob.o; +text: .text%__1cPJavaFrameAnchorNmake_walkable6MpnKJavaThread__v_; +text: .text%__1cENodeNis_block_proj6kM_pk0_; +text: .text%__1cNRelocIteratorFreloc6M_pnKRelocation__; +text: .text%__1cKJavaThreadPcook_last_frame6MnFframe__1_; +text: .text%__1cIProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQconstMethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cUGenericGrowableArrayPraw_at_put_grow6MipknEGrET_3_v_; +text: .text%__1cPClassFileStreamGget_u16MpnGThread__C_; +text: .text%__1cKRegionNodeGpinned6kM_i_: classes.o; +text: .text%__1cLTypeInstPtrEhash6kM_i_; +text: .text%__1cYCallStaticJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOPhaseIdealLoopThas_local_phi_input6MpnENode__2_; +text: .text%__1cJloadINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRMachSpillCopyNodeLout_RegMask6kM_rknHRegMask__: ad_sparc.o; +text: .text%__1cKbranchNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFStateDDFA6MipknENode__i_; +text: .text%__1cMMachProjNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cENodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cMMachProjNodeLout_RegMask6kM_rknHRegMask__: classes.o; +text: .text%__1cOMethodLivenessKBasicBlockXcompute_gen_kill_single6MpnQciByteCodeStream__v_; +text: .text%__1cRMachSpillCopyNodeKin_RegMask6kMI_rknHRegMask__: ad_sparc.o; +text: .text%__1cbAfinal_graph_reshaping_impl6FpnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cOtypeArrayKlassIallocate6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: typeArrayKlass.o; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cUParallelScavengeHeapVlarge_typearray_limit6M_I_: parallelScavengeHeap.o; +text: .text%__1cIPhaseCCPOtransform_once6MpnENode__2_; +text: .text%__1cGciTypeEmake6FnJBasicType__p0_; +text: .text%__1cKoopFactoryNnew_typeArray6FnJBasicType_ipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cENodeFclone6kM_p0_; +text: .text%__1cITypeNodeEhash6kM_I_; +text: .text%__1cIBoolNodeHis_Bool6M_p0_: subnode.o; +text: .text%__1cMPipeline_UseMfull_latency6kMIrk0_I_; +text: .text%__1cRMachSpillCopyNodePoper_input_base6kM_I_: ad_sparc.o; +text: .text%__1cENodeKmatch_edge6kMI_I_; +text: .text%__1cQconstMethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQconstMethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cHPhiNodeGpinned6kM_i_: cfgnode.o; +text: .text%__1cOPhaseIdealLoopZremix_address_expressions6MpnENode__2_; +text: .text%__1cSInterpreterRuntimeInewarray6FpnKJavaThread_nJBasicType_i_v_; +text: .text%__1cICallNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: ad_sparc_misc.o; +text: .text%__1cRMachSafePointNodeEjvms6kM_pnIJVMState__: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopNget_late_ctrl6MpnENode_2_2_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopnode.o; +text: .text%JVM_CurrentTimeMillis; +text: .text%__1cIMachNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cENodeIIdentity6MpnOPhaseTransform__p0_; +text: .text%__1cIPipelinePoperand_latency6kMIpk0_I_; +text: .text%__1cENodeFIdeal6MpnIPhaseGVN_i_p0_; +text: .text%__1cKTypeAryPtrEhash6kM_i_; +text: .text%__1cICallNodeHis_Call6M_p0_: callnode.o; +text: .text%__1cETypeFxmeet6kMpk0_2_; +text: .text%__1cILRG_ListGextend6MII_v_; +text: .text%__1cJVectorSet2F6kMI_i_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: ad_sparc.o; +text: .text%__1cENodeQIdeal_DU_postCCP6MpnIPhaseCCP__p0_; +text: .text%__1cOtypeArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cIProjNodeEhash6kM_I_; +text: .text%__1cGIfNodeGpinned6kM_i_: classes.o; +text: .text%__1cIAddINodeGOpcode6kM_i_; +text: .text%__1cIIndexSet2t6Mp0_v_; +text: .text%__1cRmethodDataOopDescJnext_data6MpnLProfileData__2_; +text: .text%__1cITypeNodeJideal_reg6kM_I_; +text: .text%__1cYCallStaticJavaDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLTypeInstPtrCeq6kMpknEType__i_; +text: .text%__1cMloadConPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: classes.o; +text: .text%__1cHPhiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMachNode2t6M_v_; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__: ad_sparc.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: callnode.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: callnode.o; +text: .text%__1cOis_diamond_phi6FpnENode__i_: cfgnode.o; +text: .text%__1cHMatcherKLabel_Root6MpknENode_pnFState_p16_6_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: memnode.o; +text: .text%__1cENodeHsize_of6kM_I_; +text: .text%__1cICmpPNodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: classes.o; +text: .text%__1cKNode_ArrayGremove6MI_v_; +text: .text%__1cNSafePointNodeGpinned6kM_i_: callnode.o; +text: .text%__1cMOopTaskQdDueueOpop_local_slow6MInOTaskQdDueueSuperDAge__i_; +text: .text%__1cHPhiNodeEhash6kM_I_; +text: .text%__1cKTypeOopPtrJsingleton6kM_i_; +text: .text%__1cPindOffset13OperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cFMutexElock6M_v_; +text: .text%__1cLSymbolTableGlookup6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cKoopFactoryKnew_symbol6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cJCProjNodeGis_CFG6kM_i_: cfgnode.o; +text: .text%__1cKmethodOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cGIfNodeFis_If6M_p0_: classes.o; +text: .text%__1cENodeNrematerialize6kM_i_: cfgnode.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nmethod.o; +text: .text%__1cJStartNodeLbottom_type6kM_pknEType__; +text: .text%__1cHTypeIntFxmeet6kMpknEType__3_; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: ad_sparc_misc.o; +text: .text%__1cOmatch_into_reg6FpnENode_iii1_i_: matcher.o; +text: .text%__1cENodeSremove_dead_region6MpnIPhaseGVN_i_i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: classes.o; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: memnode.o; +text: .text%__1cIProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cPciObjectFactoryDget6MpnHoopDesc__pnIciObject__; +text: .text%__1cKTypeAryPtrCeq6kMpknEType__i_; +text: .text%__1cILocationIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cICmpINodeGOpcode6kM_i_; +text: .text%Unsafe_CompareAndSwapLong; +text: .text%__1cNCatchProjNodeGOpcode6kM_i_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: cfgnode.o; +text: .text%__1cSinstanceKlassKlassMoop_is_klass6kM_i_: instanceKlassKlass.o; +text: .text%__1cQUnique_Node_ListGremove6MpnENode__v_; +text: .text%__1cENode2t6Mp0_v_; +text: .text%__1cNLocationValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: classes.o; +text: .text%__1cMloadConPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitHstopped6M_i_; +text: .text%__1cETypeKhas_memory6kM_i_; +text: .text%__1cFframeVinterpreter_frame_bcp6kM_pC_; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: cfgnode.o; +text: .text%__1cMloadConPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJStartNodeGpinned6kM_i_: callnode.o; +text: .text%__1cTCreateExceptionNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompileMFillLocArray6MpnENode_pnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cIHaltNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: ad_sparc_misc.o; +text: .text%__1cLTypeInstPtrEmake6FnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_pk0_; +text: .text%__1cPClassFileStreamHskip_u16MipnGThread__v_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: cfgnode.o; +text: .text%__1cHRegMaskMSmearToPairs6M_v_; +text: .text%__1cYCallStaticJavaDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMPhaseIterGVNVadd_users_to_worklist6MpnENode__v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: callnode.o; +text: .text%__1cKMachIfNodeJis_MachIf6kM_pk0_: ad_sparc_misc.o; +text: .text%__1cMloadConPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: classes.o; +text: .text%__1cbFCompressedLineNumberWriteStreamKwrite_pair6Mii_v_; +text: .text%__1cNinstanceKlassLfind_method6FpnPobjArrayOopDesc_pnNsymbolOopDesc_4_pnNmethodOopDesc__; +text: .text%__1cSCallStaticJavaNodeEhash6kM_I_: callnode.o; +text: .text%__1cHMatcherKReduceOper6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cMPipeline_UseJadd_usage6Mrk0_v_; +text: .text%__1cRCardTableModRefBSEkind6M_nKBarrierSetEName__: cardTableExtension.o; +text: .text%__1cIAddPNodeKmatch_edge6kMI_I_; +text: .text%__1cJiRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cGIfNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cGcmpkey6Fpkv1_i_; +text: .text%__1cNsymbolOopDescGequals6kMpkci_i_; +text: .text%__1cMPhaseChaitinMchoose_color6MrnDLRG_i_nHOptoRegEName__; +text: .text%__1cMMergeMemNodeGOpcode6kM_i_; +text: .text%__1cFframeYinterpreter_frame_method6kM_pnNmethodOopDesc__; +text: .text%__1cJTypeTupleJsingleton6kM_i_; +text: .text%__1cIParmNodeGOpcode6kM_i_; +text: .text%__1cJiRegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cPClassFileParserRverify_legal_utf86MpkCipnGThread__v_; +text: .text%__1cPClassFileParserbEparse_constant_pool_utf8_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cHTypeIntEmake6Fiii_pk0_; +text: .text%__1cENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNsymbolOopDescLas_C_string6kM_pc_; +text: .text%__1cKSchedulingWAddNodeToAvailableList6MpnENode__v_; +text: .text%__1cKSchedulingSChooseNodeToBundle6M_pnENode__; +text: .text%__1cKSchedulingPAddNodeToBundle6MpnENode_pknFBlock__v_; +text: .text%__1cKRelocationLunpack_data6M_v_: codeBlob.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: memnode.o; +text: .text%__1cICallNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: classes.o; +text: .text%__1cTconstantPoolOopDescNklass_at_impl6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cJLoadPNodeGOpcode6kM_i_; +text: .text%__1cMMutableSpaceIallocate6MI_pnIHeapWord__; +text: .text%__1cJPSPermGenSallocate_permanent6MI_pnIHeapWord__; +text: .text%__1cUParallelScavengeHeapWpermanent_mem_allocate6MI_pnIHeapWord__; +text: .text%__1cENodeGis_CFG6kM_i_: connode.o; +text: .text%__1cIMachNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMMutableSpaceMcas_allocate6MI_pnIHeapWord__; +text: .text%__1cNflagsRegPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cHPhiNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMMachTypeNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cJCatchNodeGOpcode6kM_i_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: cfgnode.o; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pnIciObject_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cHCmpNodeGis_Cmp6kM_pk0_: classes.o; +text: .text%__1cIJVMStateLdebug_start6kM_I_; +text: .text%__1cTconstantPoolOopDescSklass_ref_index_at6Mi_i_; +text: .text%__1cENodeHdel_req6MI_v_; +text: .text%__1cRSignatureIterator2t6MnMsymbolHandle__v_; +text: .text%__1cOAbstractICachePinvalidate_word6FpC_v_; +text: .text%__1cFBlockIis_Empty6kM_i_; +text: .text%__1cOThreadCritical2T6M_v_; +text: .text%__1cOThreadCritical2t6M_v_; +text: .text%method_compare: methodOop.o; +text: .text%__1cENodeGis_CFG6kM_i_: subnode.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: multnode.o; +text: .text%__1cICodeHeapKfind_start6kMpv_1_; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_: classes.o; +text: .text%__1cETypeEhash6kM_i_; +text: .text%__1cRNativeInstructionLset_long_at6Mii_v_; +text: .text%__1cJMultiNodeEhash6kM_I_: classes.o; +text: .text%__1cIAddPNodeLbottom_type6kM_pknEType__; +text: .text%__1cQciByteCodeStreamEjava6MnJBytecodesECode__2_; +text: .text%__1cJCProjNodeEhash6kM_I_: classes.o; +text: .text%__1cIHaltNodeGOpcode6kM_i_; +text: .text%__1cMMachCallNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cFBlockGselect6MrnJNode_List_rnLBlock_Array_pirnJVectorSet_IrnNGrowableArray4CI___pnENode__; +text: .text%__1cFStateRMachNodeGenerator6MipnHCompile__pnIMachNode__; +text: .text%__1cHMatcherKReduceInst6MpnFState_irpnENode__pnIMachNode__; +text: .text%__1cICmpUNodeGOpcode6kM_i_; +text: .text%__1cOPhaseIdealLoopbIdom_lca_for_get_late_ctrl_internal6MpnENode_22_2_; +text: .text%__1cXPipeline_Use_Cycle_MaskCOr6Mrk0_v_; +text: .text%__1cTconstantPoolOopDescQsignature_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cILoadNodeEhash6kM_I_; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: ad_sparc_misc.o; +text: .text%__1cKTypeAryPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cIMachNodeGExpand6MpnFState_rnJNode_List__p0_: ad_sparc_misc.o; +text: .text%__1cGBitMapFclear6M_v_; +text: .text%__1cHConNodeGis_Con6kM_I_: classes.o; +text: .text%__1cKHandleMarkKinitialize6MpnGThread__v_; +text: .text%__1cKHandleMark2T6M_v_; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlass.o; +text: .text%__1cFBlockLis_uncommon6kMrnLBlock_Array__i_; +text: .text%__1cZPhaseConservativeCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cMPhaseIterGVNZremove_globally_dead_node6MpnENode__v_; +text: .text%__1cWShouldNotReachHereNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHPhiNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cILoadNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: cfgnode.o; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: multnode.o; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6FnTobjArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%JVM_ReleaseUTF; +text: .text%__1cJloadPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJTypeTupleEhash6kM_i_; +text: .text%__1cFframeVoopmapreg_to_location6kMnFVMRegEName_pknLRegisterMap__ppnHoopDesc__; +text: .text%__1cENodeHget_int6kM_i_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: methodDataOop.o; +text: .text%__1cHMatcherTReduceInst_Interior6MpnFState_ipnIMachNode_IrpnENode__I_; +text: .text%__1cNinstanceKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6FnTinstanceKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cMflagsRegOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: codeBlob.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: cfgnode.o; +text: .text%__1cICodeBlobLoop_addr_at6kMi_ppnHoopDesc__; +text: .text%__1cObranchConPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKoopFactoryMnew_objArray6FpnMklassOopDesc_ipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassRallocate_objArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cMOopMapStreamJfind_next6M_v_; +text: .text%__1cFDictI2i6M_v_; +text: .text%__1cIMachNodeSalignment_required6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__: classes.o; +text: .text%__1cKNode_ArrayEgrow6MI_v_; +text: .text%__1cHTypeIntEmake6Fi_pk0_; +text: .text%__1cRAbstractAssembler2t6MpnKCodeBuffer__v_; +text: .text%__1cJloadPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMMergeMemNodeLbottom_type6kM_pknEType__: memnode.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: multnode.o; +text: .text%__1cSInterpreterRuntimeJanewarray6FpnKJavaThread_pnTconstantPoolOopDesc_ii_v_; +text: .text%__1cOPSPromotionLABKinitialize6MnJMemRegion__v_; +text: .text%__1cPciInstanceKlassMis_interface6M_i_: ciInstanceKlass.o; +text: .text%__1cJMultiNodeIproj_out6kMI_pnIProjNode__; +text: .text%__1cPindOffset13OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_: coalesce.o; +text: .text%__1cUcompI_iReg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cODataRelocationJset_value6MpC_v_: relocInfo.o; +text: .text%__1cKRelocationRpd_set_data_value6MpCi_v_; +text: .text%__1cKCastPPNodeGOpcode6kM_i_; +text: .text%__1cOoop_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cOoop_RelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cPSignatureStreamEnext6M_v_; +text: .text%__1cLLShiftINodeGOpcode6kM_i_; +text: .text%__1cENodeOis_block_start6kM_i_; +text: .text%__1cMPhaseChaitinSuse_prior_register6MpnENode_I2pnFBlock_rnJNode_List_6_i_; +text: .text%__1cGIfNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKCodeBufferIrelocate6MpCrknQRelocationHolder_i_v_; +text: .text%__1cKbranchNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPjava_lang_ClassMis_primitive6FpnHoopDesc__i_; +text: .text%__1cGBitMapJset_union6M0_v_; +text: .text%__1cPVirtualCallDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cIAddPNodeHis_AddP6M_p0_: classes.o; +text: .text%__1cIConPNodeGOpcode6kM_i_; +text: .text%__1cJLoadINodeGOpcode6kM_i_; +text: .text%__1cUGenericGrowableArray2t6Mii_v_; +text: .text%JVM_GetMethodIxExceptionTableLength; +text: .text%__1cOJNIHandleBlockPallocate_handle6MpnHoopDesc__pnI_jobject__; +text: .text%__1cPClassFileParserUassemble_annotations6MpCi1ipnGThread__nPtypeArrayHandle__; +text: .text%__1cNSharedRuntimeDd2i6Fd_i_; +text: .text%__1cVcompP_iRegP_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKRegionNodeEhash6kM_I_: classes.o; +text: .text%__1cNbranchConNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: subnode.o; +text: .text%__1cENodeHis_Copy6kM_I_: memnode.o; +text: .text%__1cOoop_RelocationSfix_oop_relocation6M_v_; +text: .text%__1cRSignatureIteratorSiterate_parameters6M_v_; +text: .text%__1cIAddPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cGBitMap2t6MpII_v_; +text: .text%__1cUGenericGrowableArray2t6MpnFArena_iipnEGrET__v_; +text: .text%__1cPClassFileStreamGget_u46MpnGThread__I_; +text: .text%__1cMMachCallNodeLbottom_type6kM_pknEType__; +text: .text%__1cFParsePdo_one_bytecode6M_v_; +text: .text%__1cFParseNdo_exceptions6M_v_; +text: .text%__1cITypeLongCeq6kMpknEType__i_; +text: .text%__1cLPCTableNodeGpinned6kM_i_: classes.o; +text: .text%__1cTconstantPoolOopDescbAname_and_type_ref_index_at6Mi_i_; +text: .text%__1cHPhiNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cENodeGpinned6kM_i_: connode.o; +text: .text%__1cHPhiNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cENodeGis_Con6kM_I_: classes.o; +text: .text%__1cHMatcherKmatch_tree6MpknENode__pnIMachNode__; +text: .text%__1cUParallelScavengeHeapPis_in_permanent6kMpkv_i_: parallelScavengeHeap.o; +text: .text%__1cMPhaseIterGVNKis_IterGVN6M_p0_: phaseX.o; +text: .text%__1cHCompileJcan_alias6MpknHTypePtr_i_i_; +text: .text%__1cKimmI13OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cCosVcurrent_stack_pointer6F_pC_; +text: .text%__1cENodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cEDict2F6kMpkv_pv_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cENodeIdestruct6M_v_; +text: .text%__1cIMachNodeNis_MachEpilog6M_pnOMachEpilogNode__: ad_sparc_misc.o; +text: .text%__1cMCreateExNodeGOpcode6kM_i_; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: cfgnode.o; +text: .text%__1cIBoolNodeEhash6kM_I_; +text: .text%__1cNinstanceKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cLTypeInstPtrFxmeet6kMpknEType__3_; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: classes.o; +text: .text%__1cKNode_ArrayFclear6M_v_; +text: .text%__1cObranchConPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIProjNodeHsize_of6kM_I_; +text: .text%__1cKis_x2logic6FpnIPhaseGVN_pnENode__3_: cfgnode.o; +text: .text%__1cHAbsNodeLis_absolute6FpnIPhaseGVN_pnENode__4_; +text: .text%__1cTconstantPoolOopDescWsignature_ref_index_at6Mi_i_; +text: .text%__1cMloadConINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cGBitMapGat_put6MIi_v_; +text: .text%__1cIHaltNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cLPhaseValuesGintcon6Mi_pnIConINode__; +text: .text%__1cJloadBNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: multnode.o; +text: .text%__1cGThreadLis_in_stack6kMpC_i_; +text: .text%__1cKJavaThreadNis_lock_owned6kMpC_i_; +text: .text%__1cFMutexElock6MpnGThread__v_; +text: .text%__1cKciTypeFlowLStateVectorSapply_one_bytecode6MpnQciByteCodeStream__i_; +text: .text%__1cHhashptr6Fpkv_i_; +text: .text%__1cMMachHaltNodeEjvms6kM_pnIJVMState__; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: classes.o; +text: .text%__1cGOopMapJset_value6MnHOptoRegEName_ii_v_; +text: .text%__1cHhashkey6Fpkv_i_; +text: .text%__1cMPhaseChaitinHnew_lrg6MpknENode_I_v_; +text: .text%__1cIJVMStateJdebug_end6kM_I_; +text: .text%__1cIPhaseIFGMtest_edge_sq6kMII_i_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: classes.o; +text: .text%__1cTconstantPoolOopDescLname_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cLBoxLockNodeNrematerialize6kM_i_: classes.o; +text: .text%__1cKBranchDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cJloadPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHSubNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: callnode.o; +text: .text%__1cJTypeTupleCeq6kMpknEType__i_; +text: .text%__1cRSignatureIteratorSiterate_returntype6M_v_; +text: .text%__1cSaddP_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMachNodeHtwo_adr6kM_I_: ad_sparc.o; +text: .text%__1cNSafePointNodeHsize_of6kM_I_; +text: .text%__1cObranchConPNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cLTypeInstPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cHCmpNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPcheckCastPPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNLoadRangeNodeGOpcode6kM_i_; +text: .text%__1cNbranchConNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENode2t6Mp011_v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: symbolKlass.o; +text: .text%__1cHCompilePfind_alias_type6MpknHTypePtr_i_pn0AJAliasType__; +text: .text%__1cJStoreNodeKmatch_edge6kMI_I_; +text: .text%__1cKbranchNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cOPSPromotionLABFflush6M_v_; +text: .text%__1cQResultTypeFinderDset6MinJBasicType__v_: bytecode.o; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: generateOopMap.o; +text: .text%__1cOcompU_iRegNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescLresult_type6kM_nJBasicType__; +text: .text%__1cICodeHeapJnext_free6kMpnJHeapBlock__pv_; +text: .text%__1cICodeHeapLblock_start6kMpv_pnJHeapBlock__; +text: .text%__1cICodeHeapKnext_block6kMpnJHeapBlock__2_; +text: .text%__1cQSystemDictionaryXcheck_signature_loaders6FnMsymbolHandle_nGHandle_2ipnGThread__v_; +text: .text%__1cKbranchNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: ad_sparc_misc.o; +text: .text%__1cJloadPNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cSCountedLoopEndNodeGOpcode6kM_i_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: cfgnode.o; +text: .text%__1cPciInstanceKlassGloader6M_pnHoopDesc__; +text: .text%__1cHMemNodeMIdeal_common6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPcheckCastPPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNCellTypeStateFmerge6kM0i_0_; +text: .text%__1cMPhaseIterGVNMsubsume_node6MpnENode_2_v_; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceKlass.o; +text: .text%__1cILoadNodeKmatch_edge6kMI_I_; +text: .text%__1cENodeGis_Con6kM_I_: subnode.o; +text: .text%__1cFframeUis_interpreted_frame6kM_i_; +text: .text%__1cLsymbolKlassNoop_is_symbol6kM_i_: symbolKlass.o; +text: .text%__1cJloadINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cETypeFempty6kM_i_; +text: .text%__1cNExceptionMark2T6M_v_; +text: .text%__1cNExceptionMark2t6MrpnGThread__v_; +text: .text%__1cMMachCallNodeLis_MachCall6M_p0_: ad_sparc_misc.o; +text: .text%__1cIMachNodeHis_Mach6M_p0_: machnode.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: connode.o; +text: .text%__1cITypeLongEhash6kM_i_; +text: .text%__1cNSafePointNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJHashtableJnew_entry6MIpnHoopDesc__pnOHashtableEntry__; +text: .text%__1cJiRegLOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: multnode.o; +text: .text%__1cKJNIHandlesKmake_local6FpnHJNIEnv__pnHoopDesc__pnI_jobject__; +text: .text%__1cMPhaseIterGVNbGregister_new_node_with_optimizer6MpnENode__2_; +text: .text%__1cPciInstanceKlassRis_instance_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cPciInstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cOloadConI13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOloadConI13NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cObranchConPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKStoreINodeGOpcode6kM_i_; +text: .text%__1cJcmpOpOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cRSignatureIterator2t6MpnNsymbolOopDesc__v_; +text: .text%__1cOno_flip_branch6FpnFBlock__i_: block.o; +text: .text%__1cMloadConINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJiRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKRegionNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKstorePNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHPhiNodeHsize_of6kM_I_: cfgnode.o; +text: .text%__1cJrelocInfo2t6Mn0AJrelocType_ii_v_; +text: .text%__1cPSignatureStreamHis_done6kM_i_; +text: .text%__1cJrelocInfoNfinish_prefix6Mph_p0_; +text: .text%__1cIAddPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQaddP_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescPis_empty_method6kM_i_; +text: .text%__1cSaddI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKReflectionTverify_field_access6FpnMklassOopDesc_22nLAccessFlags_ii_i_; +text: .text%__1cTAbstractInterpreterLmethod_kind6FnMmethodHandle__n0AKMethodKind__; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMflagsRegOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cIBoolNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLCounterDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cHRegMaskMClearToPairs6M_v_; +text: .text%__1cJiRegLOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cRshlI_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZPhaseConservativeCoalesceJcopy_copy6MpnENode_2pnFBlock_I_i_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: lcm.o; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode__i_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: relocInfo.o; +text: .text%__1cNPhaseCoalesceRcombine_these_two6MpnENode_2_v_; +text: .text%__1cNflagsRegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKcmpOpPOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cKTypeRawPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cMloadConINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescRis_not_compilable6kMi_i_; +text: .text%__1cNmethodOopDescLis_accessor6kM_i_; +text: .text%__1cFArenaEgrow6MI_pv_; +text: .text%__1cMPhaseChaitinLinsert_proj6MpnFBlock_IpnENode_I_v_; +text: .text%__1cILoadNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJStoreNodeLbottom_type6kM_pknEType__; +text: .text%__1cKTypeRawPtrJsingleton6kM_i_; +text: .text%__1cGIfNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHMemNodeGis_Mem6M_p0_: classes.o; +text: .text%__1cSCallStaticJavaNodeRis_CallStaticJava6kM_pk0_: callnode.o; +text: .text%__1cIBoolNodeLbottom_type6kM_pknEType__: subnode.o; +text: .text%__1cENodeHis_Goto6kM_I_: classes.o; +text: .text%__1cPciObjectFactorySget_unloaded_klass6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cNSafePointNodeSset_next_exception6Mp0_v_; +text: .text%__1cNmethodOopDescMintrinsic_id6kM_n0ALIntrinsicId__; +text: .text%__1cQaddP_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIHaltNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPCheckCastPPNodeGOpcode6kM_i_; +text: .text%__1cKStorePNodeGOpcode6kM_i_; +text: .text%__1cKRelocationLunpack_data6M_v_: relocInfo.o; +text: .text%__1cNflagsRegUOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cNinstanceKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cQciByteCodeStreamMreset_to_bci6Mi_v_; +text: .text%__1cPcheckCastPPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAddPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRInvocationCounterEinit6M_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: block.o; +text: .text%__1cTconstantPoolOopDescNklass_name_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cMMergeMemNodeLis_MergeMem6M_p0_: memnode.o; +text: .text%__1cFBlockOschedule_local6MrnHMatcher_rnLBlock_Array_pirnJVectorSet_rnNGrowableArray4CI___i_; +text: .text%__1cXPhaseAggressiveCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cFBlockScall_catch_cleanup6MrnLBlock_Array__v_; +text: .text%__1cObranchConUNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQCompressedStream2t6MpCi_v_; +text: .text%__1cTconstantPoolOopDescRname_ref_index_at6Mi_i_; +text: .text%__1cIAddINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cHRetNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cKRegionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstorePNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMObjectLocker2t6MnGHandle_pnGThread__v_; +text: .text%__1cMObjectLocker2T6M_v_; +text: .text%__1cNSafePointNodebBneeds_polling_address_input6F_i_; +text: .text%__1cKRegionNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOcompI_iRegNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__: callnode.o; +text: .text%__1cMURShiftINodeGOpcode6kM_i_; +text: .text%__1cRmethodDataOopDescPinitialize_data6MpnOBytecodeStream_i_i_; +text: .text%__1cENodeGis_CFG6kM_i_: memnode.o; +text: .text%__1cNRelocIteratorKset_limits6MpC1_v_; +text: .text%__1cIRootNodeGOpcode6kM_i_; +text: .text%__1cOloadConI13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cILoadNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cTCreateExceptionNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFStateM_sub_Op_ConI6MpknENode__v_; +text: .text%__1cRMachSafePointNodeQis_MachSafePoint6M_p0_: ad_sparc_misc.o; +text: .text%__1cENodeHdel_out6Mp0_v_: parse2.o; +text: .text%__1cPcheckCastPPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: classes.o; +text: .text%__1cISubINodeGOpcode6kM_i_; +text: .text%__1cNbranchConNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFframeZsender_with_pc_adjustment6kMpnLRegisterMap_pnICodeBlob_i_0_; +text: .text%__1cJTypeTupleEmake6FIppknEType__pk0_; +text: .text%__1cJTypeTupleGfields6FI_ppknEType__; +text: .text%__1cFframeGsender6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cENodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLSymbolTableJbasic_add6MipCiIpnGThread__pnNsymbolOopDesc__; +text: .text%__1cLsymbolKlassPallocate_symbol6MpCipnGThread__pnNsymbolOopDesc__; +text: .text%__1cSinstanceKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cRAbstractAssemblerEbind6MrnFLabel__v_; +text: .text%__1cILoadNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cENodeGpinned6kM_i_: subnode.o; +text: .text%__1cKbranchNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cHPhiNodeIadr_type6kM_pknHTypePtr__: cfgnode.o; +text: .text%__1cHAddNodeEhash6kM_I_; +text: .text%__1cMPhaseIterGVNFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cNbranchConNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSPSPromotionManagerMdrain_stacks6M_v_; +text: .text%__1cENodeRdisconnect_inputs6Mp0_i_; +text: .text%__1cLis_cond_add6FpnIPhaseGVN_pnHPhiNode__pnENode__; +text: .text%__1cPsplit_flow_path6FpnIPhaseGVN_pnHPhiNode__pnENode__: cfgnode.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: cfgnode.o; +text: .text%__1cNbranchConNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cSaddI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcompU_iRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJFieldTypeKbasic_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cHConNodeEhash6kM_I_; +text: .text%__1cLLShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNmethodOopDescIbci_from6kMpC_i_; +text: .text%__1cOMachReturnNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cJdo_method6FpnNmethodOopDesc__v_: recompilationMonitor.o; +text: .text%__1cNidealize_test6FpnIPhaseGVN_pnGIfNode__3_: ifnode.o; +text: .text%__1cILoadNodeHis_Load6M_p0_: classes.o; +text: .text%__1cYCallStaticJavaDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: subnode.o; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: split_if.o; +text: .text%__1cITypeNodeHsize_of6kM_I_; +text: .text%__1cVcompP_iRegP_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: parse1.o; +text: .text%__1cENodeGpinned6kM_i_: memnode.o; +text: .text%__1cNSafePointNodeLbottom_type6kM_pknEType__: callnode.o; +text: .text%__1cFciEnvXget_klass_by_index_impl6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cTconstantPoolOopDescSklass_at_if_loaded6FnSconstantPoolHandle_i_pnMklassOopDesc__; +text: .text%__1cLProfileDataPadjust_pointers6M_v_: methodDataOop.o; +text: .text%__1cLProfileDataPfollow_contents6M_v_: methodDataOop.o; +text: .text%__1cQaddP_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJStoreNodeIis_Store6kM_pk0_: classes.o; +text: .text%__1cJloadINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstorePNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassModifiers; +text: .text%__1cJCodeCacheJfind_blob6Fpv_pnICodeBlob__; +text: .text%__1cNSafePointNodeOnext_exception6kM_p0_; +text: .text%JVM_GetClassAccessFlags; +text: .text%__1cKbranchNodeHis_Goto6kM_I_: ad_sparc_misc.o; +text: .text%__1cLklassItable2t6MnTinstanceKlassHandle__v_; +text: .text%__1cIsplit_if6FpnGIfNode_pnMPhaseIterGVN__pnENode__: ifnode.o; +text: .text%__1cHTypeAryEhash6kM_i_; +text: .text%__1cTremove_useless_bool6FpnGIfNode_pnIPhaseGVN__pnENode__: ifnode.o; +text: .text%__1cGOopMapHset_xxx6MnHOptoRegEName_nLOopMapValueJoop_types_ii2_v_; +text: .text%__1cPfieldDescriptorKinitialize6MpnMklassOopDesc_i_v_; +text: .text%__1cJMultiNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cJCProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPPerfLongVariantGsample6M_v_; +text: .text%__1cJStoreNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMPhaseChaitinMyank_if_dead6MpnENode_pnFBlock_pnJNode_List_6_i_; +text: .text%__1cJloadINodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cPSignatureStreamJis_object6kM_i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: callnode.o; +text: .text%__1cIBoolNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJCatchNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: multnode.o; +text: .text%__1cIMachOperNconstant_disp6kM_i_; +text: .text%__1cIMachOperFscale6kM_i_; +text: .text%__1cENode2t6Mp0111_v_; +text: .text%__1cFPhase2t6Mn0ALPhaseNumber__v_; +text: .text%__1cNCompileBrokerLmaybe_block6F_v_; +text: .text%__1cFBlockOcode_alignment6M_I_; +text: .text%__1cMgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cNinstanceKlassGitable6kM_pnLklassItable__; +text: .text%__1cLciSignatureLreturn_type6kM_pnGciType__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: subnode.o; +text: .text%__1cFStateM_sub_Op_RegP6MpknENode__v_; +text: .text%JVM_GetCPMethodSignatureUTF; +text: .text%__1cFChunkJnext_chop6M_v_; +text: .text%__1cMMergeMemNodeEhash6kM_I_; +text: .text%__1cGOopMapHset_oop6MnHOptoRegEName_ii_v_; +text: .text%__1cKSchedulingbFComputeRegisterAntidependencies6MpnFBlock__v_; +text: .text%__1cKSchedulingPComputeUseCount6MpknFBlock__v_; +text: .text%__1cITypeNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cHTypePtrHget_con6kM_i_; +text: .text%__1cUcompI_iReg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceKlass.o; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: ad_sparc.o; +text: .text%__1cIJumpDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: ad_sparc.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: ad_sparc.o; +text: .text%__1cIMachNodeSalignment_required6kM_i_: ad_sparc.o; +text: .text%__1cENodeJis_Branch6kM_I_: ad_sparc.o; +text: .text%__1cMPhaseChaitinSget_spillcopy_wide6MpnENode_2I_2_; +text: .text%__1cYDebugInformationRecorderTcreate_scope_values6MpnNGrowableArray4CpnKScopeValue____pnKDebugToken__; +text: .text%__1cVcompP_iRegP_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cHSubNodeGis_Sub6M_p0_: classes.o; +text: .text%__1cNPhaseRegAllocGis_oop6kMpknENode__i_; +text: .text%__1cWstatic_stub_RelocationLunpack_data6M_v_; +text: .text%__1cQaddI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConUNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: multnode.o; +text: .text%__1cUParallelScavengeHeapVunsafe_max_tlab_alloc6kM_I_; +text: .text%__1cFBlockJfind_node6kMpknENode__I_; +text: .text%__1cUArgumentSizeComputerDset6MinJBasicType__v_: frame.o; +text: .text%__1cFMutexbClock_without_safepoint_check6M_v_; +text: .text%__1cHCmpNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNCollectedHeapXallocate_from_tlab_slow6FpnGThread_I_pnIHeapWord__; +text: .text%__1cWThreadLocalAllocBufferXclear_before_allocation6M_v_; +text: .text%__1cHTypePtrEhash6kM_i_; +text: .text%__1cIMachNodeNis_MachEpilog6M_pnOMachEpilogNode__: ad_sparc.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: codeBlob.o; +text: .text%__1cNinstanceKlassRallocate_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cSObjectSynchronizerKslow_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cWThreadLocalAllocBufferEfill6MpnIHeapWord_2I_v_; +text: .text%__1cUParallelScavengeHeapRallocate_new_tlab6MI_pnIHeapWord__; +text: .text%__1cYNoJvmtiVMObjectAllocMark2t6M_v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2T6M_v_; +text: .text%__1cKSharedHeapXfill_region_with_object6FnJMemRegion__v_; +text: .text%__1cFBlockLfind_remove6MpknENode__v_; +text: .text%__1cIIndexSetJlrg_union6MIIkIpknIPhaseIFG_rknHRegMask__I_; +text: .text%__1cKMemBarNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cUcompI_iReg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverbAcheck_method_accessability6FnLKlassHandle_11nMmethodHandle_pnGThread__v_; +text: .text%__1cNObjectMonitorEexit6MpnGThread__v_; +text: .text%__1cIimmPOperEtype6kM_pknEType__: ad_sparc_clone.o; +text: .text%__1cMloadConPNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cLMachNopNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadRangeNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cVCompressedWriteStream2t6Mi_v_; +text: .text%__1cNObjectMonitorFenter6MpnGThread__v_; +text: .text%__1cRlock_ptr_RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cPVirtualCallDataPadjust_pointers6M_v_; +text: .text%__1cPVirtualCallDataPfollow_contents6M_v_; +text: .text%__1cIJVMStateNclone_shallow6kM_p0_; +text: .text%__1cENodeKreplace_by6Mp0_v_; +text: .text%__1cSObjectSynchronizerJslow_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cMMergeMemNodePiteration_setup6Mpk0_v_; +text: .text%__1cFKlassNlookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cKDictionaryEfind6MiInMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cKRegionNodeOis_block_start6kM_i_: classes.o; +text: .text%__1cRMachSpillCopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRMachSpillCopyNodeOimplementation6kMpnKCodeBuffer_pnNPhaseRegAlloc_i_I_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: classes.o; +text: .text%__1cKRegionNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJStoreNodeEhash6kM_I_; +text: .text%__1cHMatcherQis_save_on_entry6Mi_i_; +text: .text%__1cSaddP_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQaddI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKTypeOopPtrWmake_from_klass_common6FpnHciKlass_ii_pk0_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: machnode.o; +text: .text%__1cIGraphKitJclone_map6M_pnNSafePointNode__; +text: .text%__1cMMergeMemNodeQclone_all_memory6FpnENode__p0_; +text: .text%__1cOcompU_iRegNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKIfTrueNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRMemBarReleaseNodeGOpcode6kM_i_; +text: .text%__1cHTypeIntFempty6kM_i_; +text: .text%__1cKbranchNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLPhaseValuesFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cIMachOperIconstant6kM_i_; +text: .text%__1cNCatchProjNodeMis_CatchProj6kM_pk0_: cfgnode.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: subnode.o; +text: .text%__1cWMutableSpaceUsedHelperLtake_sample6M_x_: spaceCounters.o; +text: .text%__1cQaddI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cGPcDescHreal_pc6kMpknHnmethod__pC_; +text: .text%__1cOcompU_iRegNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNRelocIteratorKinitialize6MipnICodeBlob_pC3_v_; +text: .text%__1cRPSOldPromotionLABFflush6M_v_; +text: .text%__1cTconstantPoolOopDescMklass_ref_at6MipnGThread__pnMklassOopDesc__; +text: .text%__1cPcompP_iRegPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCodeBufferRtransform_address6kMrk0pC_3_; +text: .text%__1cLBoxLockNodeGOpcode6kM_i_; +text: .text%__1cIciObjectJset_ident6MI_v_; +text: .text%__1cKJNIHandlesKmake_local6FpnHoopDesc__pnI_jobject__; +text: .text%__1cKTypeRawPtrEhash6kM_i_; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: subnode.o; +text: .text%__1cIBoolNodeKmatch_edge6kMI_I_: subnode.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: memnode.o; +text: .text%__1cMMergeMemNodePset_base_memory6MpnENode__v_; +text: .text%__1cOcompI_iRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLIfFalseNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cCosPelapsed_counter6F_x_; +text: .text%__1cNPhaseRegAllocKreg2offset6kMnHOptoRegEName__i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: multnode.o; +text: .text%__1cKReflectionTverify_class_access6FpnMklassOopDesc_2i_i_; +text: .text%__1cICallNodeLis_CallLeaf6kM_pknMCallLeafNode__: callnode.o; +text: .text%__1cRCompilationPolicyOmustBeCompiled6FnMmethodHandle__i_; +text: .text%__1cMMergeMemNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cGBitMapOset_difference6M0_v_; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__: callnode.o; +text: .text%__1cMPhaseChaitinJsplit_USE6MpnENode_pnFBlock_2IIiinNGrowableArray4CI__i_I_; +text: .text%__1cENodeGis_Con6kM_I_: cfgnode.o; +text: .text%__1cOoop_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cRshlI_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMMergeMemNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%JVM_GetMethodIxLocalsCount; +text: .text%__1cNloadRangeNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: multnode.o; +text: .text%JVM_CurrentThread; +text: .text%__1cENodeHget_ptr6kM_i_; +text: .text%__1cQciByteCodeStreamFEOBCs6M_nJBytecodesECode__; +text: .text%__1cRcmpFastUnlockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIAndINodeGOpcode6kM_i_; +text: .text%__1cPClassFileParserYverify_legal_method_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cENodeHins_req6MIp0_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: codeBuffer.o; +text: .text%__1cSaddI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMPhaseChaitinFUnion6MpknENode_3_v_; +text: .text%__1cMloadConLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHAddNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKRelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: codeBuffer.o; +text: .text%__1cPBoundRelocationMupdate_addrs6MpCrknKCodeBuffer_4_1_; +text: .text%__1cKstoreINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOFastUnlockNodeGOpcode6kM_i_; +text: .text%__1cLOptoRuntimeFnew_C6FpnMklassOopDesc_pnKJavaThread__v_; +text: .text%__1cITypeNodeDcmp6kMrknENode__I_; +text: .text%__1cIHaltNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: connode.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: gcm.o; +text: .text%__1cKstorePNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNflagsRegUOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKcmpOpUOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cLstoreI0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIciObject2t6MnGHandle__v_; +text: .text%__1cNSafePointNodeKmatch_edge6kMI_I_; +text: .text%__1cFframeOis_entry_frame6kM_i_; +text: .text%__1cIMachOperOindex_position6kM_i_; +text: .text%__1cLklassVtableTupdate_super_vtable6MpnNinstanceKlass_pnNmethodOopDesc_i_i_; +text: .text%__1cXmembar_release_lockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJVectorSet2L6MI_rnDSet__; +text: .text%__1cLOopRecorderOallocate_index6MpnI_jobject__i_; +text: .text%__1cOcompU_iRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMMergeMemNodeJmemory_at6kMI_pnENode__; +text: .text%__1cSaddP_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPindOffset13OperNconstant_disp6kM_i_: ad_sparc.o; +text: .text%__1cPindOffset13OperFscale6kM_i_: ad_sparc.o; +text: .text%__1cPindOffset13OperNbase_position6kM_i_: ad_sparc.o; +text: .text%__1cWShouldNotReachHereNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPciObjectFactoryRcreate_new_object6MpnHoopDesc__pnIciObject__; +text: .text%__1cUcompI_iReg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVcompP_iRegP_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQaddP_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQaddP_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSvframeStreamCommonPfill_from_frame6M_i_; +text: .text%__1cMLinkResolverZcheck_klass_accessability6FnLKlassHandle_1pnGThread__v_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: classes.o; +text: .text%__1cIJVMStateIof_depth6kMi_p0_; +text: .text%__1cNSharedRuntimeElrem6Fxx_x_; +text: .text%__1cRconstantPoolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cMciMethodDataLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cRMemBarAcquireNodeGOpcode6kM_i_; +text: .text%__1cRcmpFastUnlockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKo0RegPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cSaddI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cObranchConUNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJVectorSet2t6MpnFArena__v_; +text: .text%__1cKRegionNodeGis_CFG6kM_i_: loopnode.o; +text: .text%__1cKTypeAryPtrFxmeet6kMpknEType__3_; +text: .text%__1cVcompP_iRegP_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRSignatureIteratorSiterate_parameters6MX_v_; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: relocInfo.o; +text: .text%__1cICallNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cKRelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: postaloc.o; +text: .text%__1cOMethodLivenessKBasicBlockWcompute_gen_kill_range6MpnQciByteCodeStream__v_; +text: .text%__1cJTraceTime2T6M_v_; +text: .text%__1cITypeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cObranchConUNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cPcheckCastPPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKMemBarNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJloadPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cITypeLongJsingleton6kM_i_; +text: .text%__1cJTraceTime2t6MpkcpnMelapsedTimer_iipnMoutputStream__v_; +text: .text%__1cKoopFactoryKnew_method6FinLAccessFlags_iiipnGThread__pnNmethodOopDesc__; +text: .text%__1cSconstMethodOopDescZset_inlined_tables_length6Miii_v_; +text: .text%__1cNmethodOopDescLobject_size6Fi_i_; +text: .text%__1cNmethodOopDescbAcompute_size_of_parameters6MpnGThread__v_; +text: .text%__1cSconstMethodOopDescLobject_size6Fiiii_i_; +text: .text%__1cLmethodKlassIallocate6MnRconstMethodHandle_nLAccessFlags_pnGThread__pnNmethodOopDesc__; +text: .text%__1cLklassVtableWneeds_new_vtable_entry6FpnNmethodOopDesc_pnMklassOopDesc_pnHoopDesc_pnNsymbolOopDesc_nLAccessFlags__i_; +text: .text%__1cNmethodOopDescJinit_code6M_v_; +text: .text%__1cQconstMethodKlassIallocate6MiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cPClassFileParserMparse_method6MnSconstantPoolHandle_ipnLAccessFlags_pnPtypeArrayHandle_55pnGThread__nMmethodHandle__; +text: .text%__1cObranchConUNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMMergeMemNodeNset_memory_at6MIpnENode__v_; +text: .text%__1cLstoreI0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFciEnvSget_klass_by_index6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cNSignatureInfoHdo_void6M_v_: bytecode.o; +text: .text%__1cQaddI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENode2t6Mp01_v_; +text: .text%__1cNmethodOopDescPis_final_method6kM_i_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cKstoreINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIRewriterOrewrite_method6FnMmethodHandle_rnIintArray_pnGThread__1_; +text: .text%__1cNmethodOopDescLlink_method6FnMmethodHandle__v_; +text: .text%__1cPClassFileParserbDverify_legal_method_modifiers6MiinMsymbolHandle_pnGThread__v_; +text: .text%__1cHTypeAryRary_must_be_exact6kM_i_; +text: .text%__1cRshrI_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFStateM_sub_Op_AddP6MpknENode__v_; +text: .text%__1cTCreateExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJCatchNodeIis_Catch6kM_pk0_: classes.o; +text: .text%__1cIGraphKitEstop6M_v_; +text: .text%__1cOcompI_iRegNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIPhaseCCPFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cKCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPcompP_iRegPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRMachNullCheckNodeQis_MachNullCheck6M_p0_: machnode.o; +text: .text%__1cITypeFuncEhash6kM_i_; +text: .text%__1cLBoxLockNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_: machnode.o; +text: .text%__1cMTypeKlassPtrEhash6kM_i_; +text: .text%__1cMCallLeafNodeGOpcode6kM_i_; +text: .text%__1cENodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHnmethodKis_nmethod6kM_i_: nmethod.o; +text: .text%__1cOcompI_iRegNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHPhiNodeEmake6FpnENode_2pknEType_pknHTypePtr__p0_; +text: .text%__1cIAddPNodeQmach_bottom_type6FpknIMachNode__pknEType__; +text: .text%__1cOcompU_iRegNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: connode.o; +text: .text%__1cOCallRelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cKRelocationXpd_set_call_destination6MpCi_v_; +text: .text%__1cKRelocationTpd_call_destination6M_pC_; +text: .text%__1cJiRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNflagsRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cJStartNodeGis_CFG6kM_i_: callnode.o; +text: .text%__1cHOrINodeGOpcode6kM_i_; +text: .text%__1cXmembar_acquire_lockNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%JVM_GetCPMethodClassNameUTF; +text: .text%__1cMloadConDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMflagsRegOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cHnmethodJis_zombie6kM_i_: nmethod.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: callnode.o; +text: .text%__1cKSchedulingQNodeFitsInBundle6MpnENode__i_; +text: .text%__1cLProfileDataPfollow_contents6M_v_: ciMethodData.o; +text: .text%__1cLProfileDataPadjust_pointers6M_v_: ciMethodData.o; +text: .text%__1cFStateM_sub_Op_RegI6MpknENode__v_; +text: .text%__1cOMachReturnNodeNis_MachReturn6M_p0_: ad_sparc_misc.o; +text: .text%__1cKklassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cFKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cJMarkSweepXrevisit_weak_klass_link6FpnFKlass__v_; +text: .text%__1cKklassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassVshould_be_initialized6kM_i_; +text: .text%__1cWconstantPoolCacheKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cHCompileYout_preserve_stack_slots6F_I_; +text: .text%__1cPSignatureStream2t6MnMsymbolHandle_i_v_; +text: .text%__1cIGraphKitLclean_stack6Mi_v_; +text: .text%__1cKStoreBNodeGOpcode6kM_i_; +text: .text%__1cLklassVtableToop_adjust_pointers6M_v_; +text: .text%__1cLklassVtableToop_follow_contents6M_v_; +text: .text%__1cVcompP_iRegP_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconstMethodOopDescbBcompressed_linenumber_table6kM_pC_; +text: .text%__1cPClassFileParserWparse_linenumber_table6MIIpipnGThread__pC_; +text: .text%__1cbFCompressedLineNumberWriteStream2t6Mi_v_; +text: .text%__1cITypeFuncCeq6kMpknEType__i_; +text: .text%__1cUcompI_iReg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKbranchNodeFclone6kM_pnENode__; +text: .text%__1cMUniverseOperFclone6kM_pnIMachOper__; +text: .text%__1cJlabelOperFclone6kM_pnIMachOper__; +text: .text%__1cJlabelOperFlabel6kM_pnFLabel__: ad_sparc.o; +text: .text%__1cICallNodeHis_Call6M_p0_: classes.o; +text: .text%__1cLciSignatureHtype_at6kMi_pnGciType__; +text: .text%__1cIMachNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cIMachOperMdisp_as_type6kM_pknHTypePtr__: ad_sparc.o; +text: .text%__1cRshlI_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRelocationJpack_data6M_i_: codeBlob.o; +text: .text%__1cOPhaseIdealLoopIsplit_up6MpnENode_22_i_; +text: .text%__1cLCounterDataOis_CounterData6M_i_: ciMethodData.o; +text: .text%__1cJStartNodeGpinned6kM_i_: classes.o; +text: .text%__1cHAddNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOis_range_check6FpnENode_r12ri_i_: ifnode.o; +text: .text%JVM_IsNaN; +text: .text%__1cNloadRangeNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKbranchNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStartNodeGOpcode6kM_i_; +text: .text%__1cOMethodLivenessKBasicBlockPget_liveness_at6MpnIciMethod_i_nGBitMap__; +text: .text%__1cIciMethodPliveness_at_bci6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessPget_liveness_at6Mi_nGBitMap__; +text: .text%__1cQregF_to_stkINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: memnode.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: subnode.o; +text: .text%__1cENodeDcmp6kMrk0_I_; +text: .text%__1cFParseKensure_phi6Mii_pnHPhiNode__; +text: .text%__1cOoop_RelocationJpack_data6M_i_; +text: .text%__1cHTypeIntFxdual6kM_pknEType__; +text: .text%__1cIciObjectIencoding6M_pnI_jobject__; +text: .text%__1cNSafePointNode2t6MIpnIJVMState__v_; +text: .text%__1cHTypePtrJsingleton6kM_i_; +text: .text%__1cMmerge_region6FpnKRegionNode_pnIPhaseGVN__pnENode__: cfgnode.o; +text: .text%__1cIGraphKitObasic_plus_adr6MpnENode_2i_2_; +text: .text%__1cJAssemblerOpatched_branch6Fiii_i_; +text: .text%__1cJAssemblerSbranch_destination6Fii_i_; +text: .text%__1cRshlI_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPClassFileParserZskip_over_field_signature6MpciIpnGThread__1_; +text: .text%__1cENodeIadd_prec6Mp0_v_; +text: .text%__1cLBoxLockNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPSignatureStreamJas_symbol6MpnGThread__pnNsymbolOopDesc__; +text: .text%__1cSaddP_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWMachCallStaticJavaNodePret_addr_offset6M_i_; +text: .text%__1cICodeBlobJis_zombie6kM_i_: codeBlob.o; +text: .text%__1cITypeFuncEmake6FpknJTypeTuple_3_pk0_; +text: .text%__1cMloadConDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLRegisterMap2t6MpnKJavaThread_i_v_; +text: .text%__1cKTypeOopPtrHget_con6kM_i_; +text: .text%__1cQsubI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIRootNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLPhaseValuesHmakecon6MpknEType__pnHConNode__; +text: .text%__1cJloadLNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_iipnGThread__v_; +text: .text%__1cMLinkResolverZcheck_field_accessability6FnLKlassHandle_11rnPfieldDescriptor_pnGThread__v_; +text: .text%__1cJLoadBNodeGOpcode6kM_i_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: subnode.o; +text: .text%__1cLOptoRuntimebCcomplete_monitor_unlocking_C6FpnHoopDesc_pnJBasicLock__v_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_locking_C6FpnHoopDesc_pnJBasicLock_pnKJavaThread__v_; +text: .text%__1cOGenerateOopMapHinterp16MpnOBytecodeStream__v_; +text: .text%__1cLRegisterMapLpd_location6kMnFVMRegEName__pC_; +text: .text%__1cSvframeStreamCommonEnext6M_v_; +text: .text%__1cIAddINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cIRootNodeNis_block_proj6kM_pknENode__: classes.o; +text: .text%__1cMMergeMemNode2t6MpnENode__v_; +text: .text%__1cOcompI_iRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRMachSafePointNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cJloadINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXruntime_call_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cQsubI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPindOffset13OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cPindOffset13OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: machnode.o; +text: .text%__1cPindOffset13OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cICmpPNodeDsub6kMpknEType_3_3_; +text: .text%__1cKBufferBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cHMemNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cIAddINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIGraphKitQkill_dead_locals6M_v_; +text: .text%__1cCosMvm_page_size6F_i_; +text: .text%__1cHRegMaskPfind_first_pair6kM_nHOptoRegEName__; +text: .text%__1cMloadConLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cWShouldNotReachHereNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cRlock_ptr_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cVcompP_iRegP_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUcompI_iReg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: bytecode.o; +text: .text%__1cNloadRangeNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cRconstantPoolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNinstanceKlassUadjust_static_fields6M_v_; +text: .text%__1cRconstantPoolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLklassItableToop_adjust_pointers6M_v_; +text: .text%__1cNinstanceKlassUfollow_static_fields6M_v_; +text: .text%__1cLklassItableToop_follow_contents6M_v_; +text: .text%__1cSinstanceKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNinstanceKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSinstanceKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNSafePointNodeGOpcode6kM_i_; +text: .text%__1cWShouldNotReachHereNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: ad_sparc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: memnode.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: memnode.o; +text: .text%__1cOGenerateOopMapPjump_targets_do6MpnOBytecodeStream_pFp0ipi_v4_i_; +text: .text%__1cPcompP_iRegPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshlI_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadPNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMPhaseChaitinPset_was_spilled6MpnENode__v_; +text: .text%__1cYDebugInformationRecorderOdescribe_scope6MpnIciMethod_ipnKDebugToken_44_v_; +text: .text%__1cYDebugInformationRecorderVcreate_monitor_values6MpnNGrowableArray4CpnMMonitorValue____pnKDebugToken__; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: debugInfoRec.o; +text: .text%__1cIHaltNodeGpinned6kM_i_: classes.o; +text: .text%__1cMloadConPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNMachIdealNodeJnum_opnds6kM_I_: ad_sparc.o; +text: .text%__1cIGraphKit2t6MpnIJVMState__v_; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: cfgnode.o; +text: .text%__1cPsp_ptr_RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cPconvI2L_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKbranchNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cQPreserveJVMState2T6M_v_; +text: .text%__1cQPreserveJVMState2t6MpnIGraphKit_i_v_; +text: .text%__1cIGraphKitRnull_check_common6MpnENode_nJBasicType_i_2_; +text: .text%__1cPcompP_iRegPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMWarmCallInfoHis_cold6kM_i_; +text: .text%__1cLCastP2INodeGOpcode6kM_i_; +text: .text%__1cRshrI_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cWconstantPoolCacheKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWconstantPoolCacheKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cTCreateExceptionNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cXmembar_release_lockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMloadConLNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Copy6kM_I_: machnode.o; +text: .text%__1cFMutexNowned_by_self6kM_i_; +text: .text%__1cLConvI2LNodeGOpcode6kM_i_; +text: .text%__1cITypeLongFxmeet6kMpknEType__3_; +text: .text%__1cNinstanceKlassKinitialize6MpnGThread__v_; +text: .text%__1cSaddI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowNmake_range_at6Mi_pn0AFRange__; +text: .text%__1cFParseMmerge_common6Mpn0AFBlock_i_v_; +text: .text%__1cOCallRelocationFvalue6M_pC_: codeBlob.o; +text: .text%__1cENodeHis_Type6M_pnITypeNode__: classes.o; +text: .text%__1cPciInstanceKlassYunique_concrete_subklass6M_p0_; +text: .text%__1cENodeQlatency_from_use6kMrnLBlock_Array_rnNGrowableArray4CI__pk0p0_i_; +text: .text%__1cLBoxLockNodeHsize_of6kM_I_; +text: .text%__1cOPhaseIdealLoopIset_idom6MpnENode_2I_v_; +text: .text%__1cJStoreNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHTypeAryCeq6kMpknEType__i_; +text: .text%__1cJStartNodeIis_Start6M_p0_: callnode.o; +text: .text%JVM_GetCPFieldClassNameUTF; +text: .text%__1cHCompileKTracePhase2t6MpkcpnMelapsedTimer_i_v_; +text: .text%__1cMPhaseIterGVNHmakecon6MpknEType__pnHConNode__; +text: .text%__1cSaddI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNLoadKlassNodeGOpcode6kM_i_; +text: .text%__1cRcmpFastUnlockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJLoadCNodeGOpcode6kM_i_; +text: .text%__1cMTypeKlassPtrCeq6kMpknEType__i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: memnode.o; +text: .text%__1cHciFieldJwill_link6MpnPciInstanceKlass_nJBytecodesECode__i_; +text: .text%__1cPciInstanceKlassLfield_cache6M_pnTciConstantPoolCache__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: node.o; +text: .text%__1cQciByteCodeStreamJget_field6Mri_pnHciField__; +text: .text%__1cFciEnvSget_field_by_index6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cOcompI_iRegNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: graphKit.o; +text: .text%__1cRshlI_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescIbcp_from6kMi_pC_; +text: .text%__1cTCreateExceptionNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cGBitMapVset_union_with_result6M0_i_; +text: .text%__1cICmpINodeDsub6kMpknEType_3_3_; +text: .text%__1cLRShiftINodeGOpcode6kM_i_; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassSallocate_permanent6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: memnode.o; +text: .text%__1cSCallLeafDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: frame.o; +text: .text%__1cPcheckCastPPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOPhaseIdealLoopQconditional_move6MpnENode__2_; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_: callnode.o; +text: .text%__1cFframeOis_first_frame6kM_i_; +text: .text%__1cJStoreNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cITypeFuncEmake6FpnIciMethod__pk0_; +text: .text%__1cICodeBlobTfix_oop_relocations6M_v_; +text: .text%__1cOGenerateOopMapEpush6MnNCellTypeState__v_; +text: .text%__1cJloadSNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKStoreCNodeGOpcode6kM_i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: subnode.o; +text: .text%__1cOGenerateOopMapRdo_exception_edge6MpnOBytecodeStream__v_; +text: .text%__1cMstringStreamFwrite6MpkcI_v_; +text: .text%__1cOGenerateOopMapDpop6M_nNCellTypeState__; +text: .text%__1cHRetNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPcmpFastLockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverMresolve_pool6FrnLKlassHandle_rnMsymbolHandle_42nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverOresolve_invoke6FrnICallInfo_nGHandle_nSconstantPoolHandle_inJBytecodesECode_pnGThread__v_; +text: .text%__1cIBoolNodeJideal_reg6kM_I_: subnode.o; +text: .text%__1cHCmpNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRcmpFastUnlockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRloadConP_pollNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cETypeFwiden6kMpk0_2_: type.o; +text: .text%__1cRcmpFastUnlockNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cILoadNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cLstoreI0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFciEnvTget_method_by_index6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cQciByteCodeStreamKget_method6Mri_pnIciMethod__; +text: .text%__1cFciEnvYget_method_by_index_impl6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cMloadConPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFciEnvNlookup_method6MpnNinstanceKlass_2pnNsymbolOopDesc_4nJBytecodesECode__pnNmethodOopDesc__; +text: .text%__1cKDictionaryKfind_class6MiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cNSafePointNodeGpinned6kM_i_: classes.o; +text: .text%__1cPcompP_iRegPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cObranchConPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCodeCacheQfind_blob_unsafe6Fpv_pnICodeBlob__; +text: .text%__1cNloadRangeNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCatchProjNodeLbottom_type6kM_pknEType__: cfgnode.o; +text: .text%__1cNCatchProjNodeHsize_of6kM_I_: cfgnode.o; +text: .text%__1cFStateK_sub_Op_If6MpknENode__v_; +text: .text%__1cIciMethodbCinterpreter_invocation_count6M_i_; +text: .text%__1cTciConstantPoolCacheDget6Mi_pv_; +text: .text%__1cSInterpreterRuntimeMmonitorenter6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: subnode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: subnode.o; +text: .text%__1cSInterpreterRuntimePresolve_get_put6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cWConstantPoolCacheEntryJset_field6MnJBytecodesECode_2nLKlassHandle_iinITosState_ii_v_; +text: .text%__1cKCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_ipnGThread__v_; +text: .text%__1cKciTypeFlowFRangeNget_block_for6Mpn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cQsubI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cXmembar_acquire_lockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQaddP_reg_regNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cKCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPCountedLoopNodeGOpcode6kM_i_; +text: .text%__1cUGenericGrowableArrayMraw_contains6kMpknEGrET__i_; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_: systemDictionary.o; +text: .text%__1cSInterpreterRuntimeLmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: subnode.o; +text: .text%__1cIAndLNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitOset_all_memory6MpnENode__v_; +text: .text%__1cENodeHis_Goto6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionarybEresolve_instance_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cVjava_lang_ClassLoaderbBnon_reflection_class_loader6FpnHoopDesc__2_; +text: .text%__1cHnmethodIis_alive6kM_i_: nmethod.o; +text: .text%__1cFParseFBlockKinit_graph6Mp0_v_; +text: .text%__1cMTypeKlassPtrEmake6FnHTypePtrDPTR_pnHciKlass_i_pk0_; +text: .text%__1cKRelocationLspec_simple6FnJrelocInfoJrelocType__nQRelocationHolder__; +text: .text%__1cRshrI_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOkill_dead_code6FpnENode_pnMPhaseIterGVN__i_: node.o; +text: .text%__1cMPrefetchNodeGOpcode6kM_i_; +text: .text%__1cCosGmalloc6FI_pv_; +text: .text%__1cSInterpreterRuntimeOresolve_invoke6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cIGraphKitTadd_exception_state6MpnNSafePointNode__v_; +text: .text%__1cIimmPOperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: cfgnode.o; +text: .text%__1cRRawBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cIregDOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cKstoreINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICodeHeapLheader_size6F_I_; +text: .text%__1cQsubI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: connode.o; +text: .text%__1cFciEnvIis_in_vm6F_i_; +text: .text%__1cIMachNodeJis_MachIf6kM_pknKMachIfNode__: ad_sparc_misc.o; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: thread.o; +text: .text%__1cWConstantPoolCacheEntryKset_method6MnJBytecodesECode_nMmethodHandle_i_v_; +text: .text%__1cKciTypeFlowLStateVectorEmeet6Mpk1_i_; +text: .text%__1cNbranchConNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadRangeNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParseMdo_one_block6M_v_; +text: .text%__1cOPhaseIdealLoopRregister_new_node6MpnENode_2_v_; +text: .text%__1cLstoreB0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: callnode.o; +text: .text%__1cIAddINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIJVMStateKclone_deep6kM_p0_; +text: .text%__1cIJVMStateLdebug_depth6kM_I_; +text: .text%__1cENodeNadd_req_batch6Mp0I_v_; +text: .text%__1cIGraphKitTadd_safepoint_edges6MpnNSafePointNode_i_v_; +text: .text%__1cKciTypeFlowLStateVectorOpush_translate6MpnGciType__v_; +text: .text%__1cJloadFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: node.o; +text: .text%__1cMMachCallNodeHis_Call6M_pnICallNode__: ad_sparc_misc.o; +text: .text%__1cPVirtualCallDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cIMachNodeOpipeline_class6F_pknIPipeline__; +text: .text%__1cNloadRangeNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMindirectOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cHMatcherScalling_convention6FpnLRegPair_Ii_v_; +text: .text%__1cQSystemDictionarybCfind_instance_or_array_klass6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cIPhaseGVNUtransform_no_reclaim6MpnENode__2_; +text: .text%__1cIAddLNodeGOpcode6kM_i_; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_nGHandle_2ipnGThread__pnMklassOopDesc__; +text: .text%__1cLLShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOMethodLivenessKBasicBlockJpropagate6Mp0_v_; +text: .text%__1cFciEnvWget_klass_by_name_impl6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cKciTypeFlowGJsrSet2t6MpnFArena_i_v_; +text: .text%__1cRMachSafePointNode2t6M_v_; +text: .text%__1cHMatcherKmatch_sfpt6MpnNSafePointNode__pnIMachNode__; +text: .text%__1cMFastLockNodeGOpcode6kM_i_; +text: .text%__1cRInlineCacheBufferIcontains6FpC_i_; +text: .text%__1cLConvL2INodeGOpcode6kM_i_; +text: .text%__1cIXorINodeGOpcode6kM_i_; +text: .text%__1cICallNodeOis_CallRuntime6kM_pknPCallRuntimeNode__: callnode.o; +text: .text%__1cMVirtualSpaceOcommitted_size6kM_I_; +text: .text%__1cJloadCNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cXinsert_anti_dependences6FrpnFBlock_pnENode_rnLBlock_Array__i_: gcm.o; +text: .text%__1cPorI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcompU_iRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPorI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKTypeAryPtrFklass6kM_pnHciKlass__; +text: .text%__1cIGraphKitbDtransfer_exceptions_into_jvms6M_pnIJVMState__; +text: .text%__1cOPhaseIdealLoopHdom_lca6kMpnENode_2_2_; +text: .text%__1cLTypeInstPtrFxdual6kM_pknEType__; +text: .text%__1cNLoadRangeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHMatcherPc_frame_pointer6kM_nHOptoRegEName__; +text: .text%__1cFBlockKsched_call6MrnHMatcher_rnLBlock_Array_IrnJNode_List_pipnMMachCallNode_rnJVectorSet__I_; +text: .text%__1cSsafePoint_pollNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMMachCallNode2t6M_v_; +text: .text%__1cILoadNodeHsize_of6kM_I_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: methodLiveness.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodKlass.o; +text: .text%__1cRInterpretedRFrameKtop_method6kM_nMmethodHandle__: rframe.o; +text: .text%__1cICmpPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIGraphKitJsync_jvms6kM_pnIJVMState__; +text: .text%__1cICmpUNodeDsub6kMpknEType_3_3_; +text: .text%__1cHnmethodOis_not_entrant6kM_i_: nmethod.o; +text: .text%__1cNprefetch2NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cEUTF8Hstrrchr6FpWiW_1_; +text: .text%__1cIciMethodRhas_compiled_code6M_i_; +text: .text%__1cPcompP_iRegPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPsp_ptr_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cPClassFileParserbCverify_legal_field_signature6MnMsymbolHandle_1pnGThread__v_; +text: .text%__1cPClassFileParserXverify_legal_field_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cLOptoRuntimePnew_typeArray_C6FnJBasicType_ipnKJavaThread__v_; +text: .text%__1cRshrP_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXvirtual_call_RelocationIparse_ic6FrpnICodeBlob_rpC5rppnHoopDesc_pi_nNRelocIterator__; +text: .text%__1cITypeLongEmake6Fxxi_pk0_; +text: .text%__1cRloadConP_pollNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%JVM_GetCPMethodNameUTF; +text: .text%__1cMtlsLoadPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreB0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRloadConP_pollNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIimmIOperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cLstoreI0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: cfgnode.o; +text: .text%__1cNSharedRuntimeEldiv6Fxx_x_; +text: .text%__1cHBitDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cURethrowExceptionNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cQSystemDictionarybOfind_constrained_instance_or_array_klass6FnMsymbolHandle_nGHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cQsubI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: ciTypeFlow.o; +text: .text%__1cKReturnNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cJloadBNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIciSymbol2t6MnMsymbolHandle__v_; +text: .text%__1cQaddP_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKmethodOperGmethod6kM_i_: ad_sparc.o; +text: .text%__1cQjava_lang_StringQbasic_create_oop6FpnQtypeArrayOopDesc_ipnGThread__pnHoopDesc__; +text: .text%__1cFKlassIsubklass6kM_p0_; +text: .text%__1cNinstanceKlassbBallocate_permanent_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cXInterpreterFrameClosureJoffset_do6Mi_v_: frame.o; +text: .text%__1cENodeHis_Goto6kM_I_: cfgnode.o; +text: .text%__1cICmpINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPemit_call_reloc6FrnKCodeBuffer_inJrelocInfoJrelocType_iii_v_; +text: .text%__1cTconstantPoolOopDescOstring_at_impl6FnSconstantPoolHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cEUTF8Sconvert_to_unicode6FpkcpHi_v_; +text: .text%__1cIMulLNodeGOpcode6kM_i_; +text: .text%__1cKReturnNodeKmatch_edge6kMI_I_; +text: .text%__1cENodeHis_Call6M_pnICallNode__: callnode.o; +text: .text%__1cILoopNodeHis_Loop6M_p0_: classes.o; +text: .text%__1cGOopMap2t6Mii_v_; +text: .text%__1cJOopMapSetKadd_gc_map6MiipnGOopMap__v_; +text: .text%__1cNloadConP0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJLoadSNodeGOpcode6kM_i_; +text: .text%__1cLPCTableNodeLbottom_type6kM_pknEType__; +text: .text%__1cKBranchDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cMCreateExNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cRloadConP_pollNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCodeCacheEnext6FpnICodeBlob__2_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: connode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: connode.o; +text: .text%__1cRcmpFastUnlockNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadLNodeGOpcode6kM_i_; +text: .text%__1cMciMethodDataLhas_trap_at6MpnLProfileData_i_i_; +text: .text%__1cPThreadLocalNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOMacroAssemblerZneeds_explicit_null_check6Fi_i_; +text: .text%__1cSaddI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKReturnNodeGOpcode6kM_i_; +text: .text%__1cNinstanceKlassPinitialize_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: frame.o; +text: .text%__1cTconstantPoolOopDescbBbasic_type_for_signature_at6Mi_nJBasicType__; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: callnode.o; +text: .text%__1cNflagsRegUOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIGraphKitbLset_predefined_input_for_runtime_call6MpnNSafePointNode__v_; +text: .text%__1cRshlI_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConINodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cNCatchProjNodeEhash6kM_I_; +text: .text%__1cEUTF8Ounicode_length6Fpkci_i_; +text: .text%__1cHCompileZintrinsic_insertion_index6MpnIciMethod_i_i_; +text: .text%__1cTOopMapForCacheEntryRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cYDebugInformationRecorderNadd_safepoint6MiipnGOopMap__v_; +text: .text%__1cHCompileTProcess_OopMap_Node6MpnIMachNode_i_v_; +text: .text%__1cRMachSafePointNodePis_MachCallLeaf6M_pnQMachCallLeafNode__: ad_sparc_misc.o; +text: .text%__1cRMachSafePointNodeLset_oop_map6MpnGOopMap__v_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_ref_ReferenceNreferent_addr6FpnHoopDesc__p2_; +text: .text%__1cNCallGenerator2t6MpnIciMethod__v_; +text: .text%__1cRMachSafePointNodeSis_MachCallRuntime6M_pnTMachCallRuntimeNode__: ad_sparc_misc.o; +text: .text%__1cKCompiledIC2t6MpnKRelocation__v_; +text: .text%__1cNMachIdealNodePoper_input_base6kM_I_: machnode.o; +text: .text%__1cSCallLeafDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHRegMaskQis_aligned_Pairs6kM_i_; +text: .text%__1cKCompiledICOic_destination6kM_pC_; +text: .text%__1cHTypeAryFxmeet6kMpknEType__3_; +text: .text%__1cPClassFileParserbCverify_legal_field_modifiers6MiipnGThread__v_; +text: .text%__1cPClassFileParserWparse_field_attributes6MnSconstantPoolHandle_iHpHpi2pnPtypeArrayHandle_pnGThread__v_; +text: .text%__1cICallNodeJideal_reg6kM_I_: callnode.o; +text: .text%__1cLStringTableGintern6FpnNsymbolOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cNsymbolOopDescKas_unicode6kMri_pH_; +text: .text%__1cPmethodDataKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cKstorePNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowQadd_to_work_list6Mpn0AFBlock__v_; +text: .text%__1cKciTypeFlowKflow_block6Mpn0AFBlock_pn0ALStateVector_pn0AGJsrSet__v_; +text: .text%__1cKciTypeFlowFBlockKsuccessors6MpnQciByteCodeStream_pn0ALStateVector_pn0AGJsrSet__pnNGrowableArray4Cp1___; +text: .text%__1cRMachSafePointNodeRis_safepoint_node6kM_i_: ad_sparc_misc.o; +text: .text%__1cEUTF8Enext6FpkcpH_pc_; +text: .text%__1cJVectorSetFClear6M_v_; +text: .text%__1cHCompileSflatten_alias_type6kMpknHTypePtr__3_; +text: .text%__1cMCallJavaNodeLis_CallJava6kM_pk0_: callnode.o; +text: .text%__1cQMachCallJavaNodePis_MachCallJava6M_p0_: ad_sparc_misc.o; +text: .text%__1cCosEfree6Fpv_v_; +text: .text%__1cICallNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cKTypeOopPtrFempty6kM_i_; +text: .text%__1cKciTypeFlowFBlock2t6Mp0pn0AFRange_pn0AGJsrSet__v_; +text: .text%__1cRshrI_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmpFastLockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cYciExceptionHandlerStreamFcount6M_i_; +text: .text%__1cKciTypeFlowFBlockScompute_exceptions6M_v_; +text: .text%__1cIPhaseIFGFUnion6MII_v_; +text: .text%__1cLstoreB0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cYCallStaticJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWMachCallStaticJavaNodeVis_MachCallStaticJava6M_p0_: ad_sparc_misc.o; +text: .text%__1cILoopNodeGOpcode6kM_i_; +text: .text%__1cRMachSafePointNodeWis_MachCallInterpreter6M_pnXMachCallInterpreterNode__: ad_sparc_misc.o; +text: .text%__1cICmpLNodeGOpcode6kM_i_; +text: .text%__1cOPhaseIdealLoopGspinup6MpnENode_2222pnLsmall_cache__2_; +text: .text%__1cQaddI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMindIndexOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cIConLNodeGOpcode6kM_i_; +text: .text%__1cMLinkResolverbNlinktime_resolve_virtual_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cJloadCNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%JVM_GetCPFieldSignatureUTF; +text: .text%__1cFframeQoops_do_internal6MpnKOopClosure_pnLRegisterMap_i_v_; +text: .text%__1cMLinkResolverbEruntime_resolve_virtual_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cPCountedLoopNodeOis_CountedLoop6M_p0_: classes.o; +text: .text%__1cENodeLnonnull_req6kM_p0_; +text: .text%__1cGciTypeMis_classless6kM_i_: ciType.o; +text: .text%__1cFKlassXcan_be_statically_bound6FpnNmethodOopDesc__i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: connode.o; +text: .text%__1cHnmethodZsize_of_exception_handler6F_i_; +text: .text%__1cYCallStaticJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYCallStaticJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cMelapsedTimerFstart6M_v_; +text: .text%__1cMelapsedTimerEstop6M_v_; +text: .text%__1cQandL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMURShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSaddP_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cObranchConPNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopOfind_use_block6MpnENode_22222_2_; +text: .text%__1cOPhaseIdealLoopKhandle_use6MpnENode_2pnLsmall_cache_22222_v_; +text: .text%__1cOMethodLivenessNmake_block_at6Mipn0AKBasicBlock__2_; +text: .text%__1cPorI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%jni_DeleteLocalRef: jni.o; +text: .text%__1cIGraphKit2t6M_v_; +text: .text%__1cMoutputStreamDput6Mc_v_; +text: .text%__1cIGraphKitNset_map_clone6MpnNSafePointNode__v_; +text: .text%__1cRInterpretedRFrameEinit6M_v_; +text: .text%__1cHMulNodeEhash6kM_I_; +text: .text%__1cENodeJset_req_X6MIp0pnMPhaseIterGVN__v_; +text: .text%__1cSInterpreterRuntimeDldc6FpnKJavaThread_i_v_; +text: .text%__1cJLoadINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cINodeHashLhash_insert6MpnENode__v_; +text: .text%__1cHTypeIntEmake6Fii_pk0_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: ad_sparc.o; +text: .text%__1cKstoreCNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlassKlass.o; +text: .text%__1cNSafePointNodeEhash6kM_I_: callnode.o; +text: .text%__1cENodeLbottom_type6kM_pknEType__; +text: .text%__1cKJNIHandlesKmake_local6FpnGThread_pnHoopDesc__pnI_jobject__; +text: .text%__1cKstoreCNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMCreateExNodeGpinned6kM_i_: classes.o; +text: .text%__1cIAddPNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQjava_lang_StringbBcreate_tenured_from_unicode6FpHipnGThread__nGHandle__; +text: .text%__1cKoopFactoryXnew_permanent_charArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cFParsePdo_field_access6Mii_v_; +text: .text%__1cKMemBarNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cGvframe2t6MpknFframe_pknLRegisterMap_pnKJavaThread__v_; +text: .text%__1cLRegisterMap2t6Mpk0_v_; +text: .text%__1cXmembar_acquire_lockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cQmark_inner_loops6FpnIPhaseCFG_pnFBlock__v_: block.o; +text: .text%__1cILoadNodeEmake6FpnENode_22pknHTypePtr_pknEType_nJBasicType__p0_; +text: .text%__1cICallNodeSis_CallDynamicJava6kM_pknTCallDynamicJavaNode__: callnode.o; +text: .text%__1cJOopMapSetSfind_map_at_offset6kMii_pnGOopMap__; +text: .text%__1cICodeBlobbAoop_map_for_return_address6MpCi_pnGOopMap__; +text: .text%__1cNmethodOopDescWwas_executed_more_than6kMi_i_; +text: .text%__1cRshrI_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcompI_iRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIciSymbolEmake6Fpkc_p0_; +text: .text%__1cPorI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLPhaseValuesHzerocon6MnJBasicType__pnHConNode__; +text: .text%__1cGPcDesc2t6Miii_v_; +text: .text%__1cHnmethodKcopy_pc_at6MipnGPcDesc__v_; +text: .text%__1cHCompileKalias_type6MpnHciField__pn0AJAliasType__; +text: .text%__1cGvframeKnew_vframe6FpknFframe_pknLRegisterMap_pnKJavaThread__p0_; +text: .text%__1cPconvI2L_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMtlsLoadPNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cYcompareAndSwapL_boolNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAddINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIciMethodRget_flow_analysis6M_pnKciTypeFlow__; +text: .text%__1cWCallLeafNoFPDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmembar_acquireNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKbranchNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKbranchNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXjava_lang_ref_ReferencePdiscovered_addr6FpnHoopDesc__p2_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: loopnode.o; +text: .text%__1cOloadConI13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSaddL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetObjectField: jni.o; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_: methodDataOop.o; +text: .text%__1cSMemBarCPUOrderNodeGOpcode6kM_i_; +text: .text%__1cJFieldTypeOget_array_info6FpnNsymbolOopDesc_pip2pnGThread__nJBasicType__; +text: .text%__1cJFieldTypeYis_valid_array_signature6FpnNsymbolOopDesc__i_; +text: .text%__1cQandL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cWstatic_stub_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cQaddL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPmethodDataKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cPmethodDataKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cJloadBNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNbranchConNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRegionNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: ad_sparc_misc.o; +text: .text%__1cYcompareAndSwapL_boolNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: machnode.o; +text: .text%__1cRMachNullCheckNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cYinlineCallClearArrayNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConP0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitJmake_load6MpnENode_2pknEType_nJBasicType_i_2_; +text: .text%__1cOPhaseIdealLoopIsink_use6MpnENode_2_v_; +text: .text%__1cIGraphKitOreplace_in_map6MpnENode_2_v_; +text: .text%__1cENodeGis_Con6kM_I_: callnode.o; +text: .text%__1cNinstanceKlassLfind_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cIGraphKitNuncommon_trap6MipnHciKlass_pkci_v_; +text: .text%__1cHCompileKTracePhase2T6M_v_; +text: .text%__1cMPhaseChaitinLclone_projs6MpnFBlock_IpnENode_4rI_i_; +text: .text%__1cNinstanceKlassSlookup_osr_nmethod6kMkpnNmethodOopDesc_i_pnHnmethod__; +text: .text%__1cIJVMState2t6MpnIciMethod_p0_v_; +text: .text%__1cMLinkResolverbEruntime_resolve_special_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_4ipnGThread__v_; +text: .text%__1cMLinkResolverbFlinktime_resolve_special_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cIHaltNode2t6MpnENode_2_v_; +text: .text%__1cLOptoRuntimeSuncommon_trap_Type6F_pknITypeFunc__; +text: .text%__1cJloadLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadConP0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowGJsrSetNapply_control6Mp0pnQciByteCodeStream_pn0ALStateVector__v_; +text: .text%__1cSsafePoint_pollNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cINodeHashJhash_find6MpknENode__p1_; +text: .text%__1cQmulL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cOMethodLivenessKBasicBlock2t6Mp0ii_v_; +text: .text%__1cOMethodLivenessKBasicBlockQcompute_gen_kill6MpnIciMethod__v_; +text: .text%__1cQciByteCodeStreamZget_declared_field_holder6M_pnPciInstanceKlass__; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: classes.o; +text: .text%__1cOGenerateOopMapFppush6MpnNCellTypeState__v_; +text: .text%__1cJTypeTupleKmake_range6FpnLciSignature__pk0_; +text: .text%__1cJTypeTupleLmake_domain6FpnPciInstanceKlass_pnLciSignature__pk0_; +text: .text%__1cSmembar_acquireNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConUNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_ref_ReferenceJnext_addr6FpnHoopDesc__p2_; +text: .text%__1cMWarmCallInfoGis_hot6kM_i_; +text: .text%__1cMWarmCallInfoKalways_hot6F_p0_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6FnUtypeArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cSCompareAndSwapNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cLRethrowNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cTmembar_CPUOrderNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmpFastLockNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTCreateExceptionNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKciTypeFlowLStateVectorJdo_invoke6MpnQciByteCodeStream_i_v_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: connode.o; +text: .text%__1cQmulL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPcmpFastLockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreB0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadBNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cQaddP_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMtlsLoadPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cFTypeDCeq6kMpknEType__i_; +text: .text%__1cITypeLongEmake6Fx_pk0_; +text: .text%__1cKJavaThreadbHcheck_and_handle_async_exceptions6Mi_v_; +text: .text%__1cHciFieldPinitialize_from6MpnPfieldDescriptor__v_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: memnode.o; +text: .text%__1cMPhaseChaitinTsplit_Rematerialize6MpnENode_pnFBlock_IrInNGrowableArray4CI__ipIp2i_2_; +text: .text%__1cKimmI13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cJloadBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIGraphKitZadd_exception_states_from6MpnIJVMState__v_; +text: .text%__1cQandL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseKdo_get_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cMPhaseChaitinNFind_compress6MpknENode__I_; +text: .text%__1cQSystemDictionaryEfind6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNCatchProjNode2t6MpnENode_Ii_v_; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_: ciMethodData.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: cfgnode.o; +text: .text%__1cHPhiNodeEmake6FpnENode_2_p0_; +text: .text%__1cNCatchProjNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cWCallLeafNoFPDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassOis_subclass_of6kMpnMklassOopDesc__i_; +text: .text%__1cLPCTableNodeHsize_of6kM_I_: classes.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: cfgnode.o; +text: .text%__1cLPCTableNodeKis_PCTable6kM_pk0_: classes.o; +text: .text%__1cNciCallProfileRapply_prof_factor6Mf_v_; +text: .text%__1cIciMethodTcall_profile_at_bci6Mi_nNciCallProfile__; +text: .text%__1cHCompileOcall_generator6MpnIciMethod_ipnIJVMState_if_pnNCallGenerator__; +text: .text%__1cHCompileOfind_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cIProjNodeDcmp6kMrknENode__I_; +text: .text%__1cLklassVtableIindex_of6kMpnNmethodOopDesc_i_i_; +text: .text%__1cLLShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFParseMprofile_call6MpnENode__v_; +text: .text%__1cFciEnvbTget_instance_klass_for_declared_method_holder6FpnHciKlass__pnPciInstanceKlass__; +text: .text%__1cIGraphKitWround_double_arguments6MpnIciMethod__v_; +text: .text%__1cQciByteCodeStreambAget_declared_method_holder6M_pnHciKlass__; +text: .text%__1cIGraphKitTround_double_result6MpnIciMethod__v_; +text: .text%__1cFParseHdo_call6M_v_; +text: .text%__1cNloadConP0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIregFOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cHMulNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMPhaseIterGVNJtransform6MpnENode__2_; +text: .text%__1cHTypeIntFwiden6kMpknEType__3_; +text: .text%__1cQxorI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsafePoint_pollNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadSNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKarrayKlassLobject_size6kMi_i_; +text: .text%__1cIciMethodLscale_count6Mi_i_; +text: .text%__1cKMemBarNodeEhash6kM_I_; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlass.o; +text: .text%__1cMLinkResolverVresolve_invokevirtual6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKMemoryPoolYrecord_peak_memory_usage6M_v_; +text: .text%__1cMURShiftLNodeGOpcode6kM_i_; +text: .text%__1cOCompilerOracleOshould_exclude6FnMmethodHandle__i_; +text: .text%__1cIGraphKitUmake_exception_state6MpnENode__pnNSafePointNode__; +text: .text%__1cLProfileDataOtranslate_from6Mp0_v_: ciMethodData.o; +text: .text%__1cLstoreI0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarI_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKBranchDataNis_BranchData6M_i_: ciMethodData.o; +text: .text%__1cKRegionNodeGpinned6kM_i_: loopnode.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: memnode.o; +text: .text%__1cLBuildCutout2t6MpnIGraphKit_pnENode_ff_v_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: graphKit.o; +text: .text%__1cTCompareAndSwapLNodeGOpcode6kM_i_; +text: .text%__1cNloadRangeNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQxorI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIJumpDataLis_JumpData6M_i_: ciMethodData.o; +text: .text%__1cMMergeMemNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLLShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNflagsRegLOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cCosOis_interrupted6FpnGThread_i_i_; +text: .text%__1cLmethodKlassNoop_is_method6kM_i_: methodKlass.o; +text: .text%__1cQsubI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXmembar_release_lockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: memnode.o; +text: .text%__1cKarrayKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cRshrI_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__: loopnode.o; +text: .text%__1cTcan_branch_register6FpnENode_1_i_; +text: .text%__1cQCallLeafNoFPNodeGOpcode6kM_i_; +text: .text%__1cMURShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKstoreCNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHConNodeEmake6FpknEType__p0_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: memnode.o; +text: .text%__1cFStateM_sub_Op_ConP6MpknENode__v_; +text: .text%__1cRshrP_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitMsaved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cISubINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPciInstanceKlassFsuper6M_p0_; +text: .text%__1cIBoolNodeHsize_of6kM_I_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: callnode.o; +text: .text%__1cSobjArrayKlassKlassIoop_size6kMpnHoopDesc__i_: objArrayKlassKlass.o; +text: .text%__1cPcompP_iRegPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvI2D_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotLOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cJloadPNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSsafePoint_pollNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPBytecode_invokeJsignature6kM_pnNsymbolOopDesc__; +text: .text%__1cFframebGinterpreter_callee_receiver_addr6MnMsymbolHandle__ppnHoopDesc__; +text: .text%__1cHMonitorKnotify_all6M_i_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: bytecode.o; +text: .text%__1cOstackSlotLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIciMethodbAinterpreter_throwout_count6kM_i_; +text: .text%__1cOCompilerOracleNshould_inline6FnMmethodHandle__i_; +text: .text%__1cIciMethodOshould_exclude6M_i_; +text: .text%__1cKInlineTreeMok_to_inline6MpnIciMethod_pnIJVMState_rnNciCallProfile_pnMWarmCallInfo__8_; +text: .text%__1cKInlineTreeWfind_subtree_from_root6Fp0pnIJVMState_pnIciMethod_i_1_; +text: .text%__1cIciMethodNshould_inline6M_i_; +text: .text%__1cOGenerateOopMapbAget_basic_block_containing6kMi_pnKBasicBlock__; +text: .text%__1cGThreadXclear_pending_exception6M_v_; +text: .text%__1cICodeHeapSallocated_capacity6kM_I_; +text: .text%__1cSstkL_to_regD_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescbHhas_unloaded_classes_in_signature6FnMmethodHandle_pnGThread__i_; +text: .text%__1cTmembar_CPUOrderNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: connode.o; +text: .text%__1cNprefetch2NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cICHeapObj2n6FI_pv_; +text: .text%__1cQaddI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJStartNodeIis_Start6M_p0_: classes.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodKlass.o; +text: .text%__1cWCallLeafNoFPDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cCosFsleep6FpnGThread_xi_i_; +text: .text%__1cIos_sleep6Fxi_i_: os_solaris.o; +text: .text%__1cSstkL_to_regD_2NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFTypeDEhash6kM_i_; +text: .text%__1cKTypeRawPtrHget_con6kM_i_; +text: .text%__1cJStartNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%jni_ExceptionOccurred: jni.o; +text: .text%__1cPconvI2L_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowLStateVectorStype_meet_internal6FpnGciType_3p0_3_; +text: .text%__1cMloadConINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGIfNodeHsize_of6kM_I_: classes.o; +text: .text%__1cPconvL2I_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIimmLOperJconstantL6kM_x_: ad_sparc_clone.o; +text: .text%__1cTStackWalkCompPolicyRcompilation_level6MnMmethodHandle_i_i_; +text: .text%jni_GetByteArrayRegion: jni.o; +text: .text%__1cIGraphKitTset_all_memory_call6MpnENode__v_; +text: .text%__1cSHighResTimeSamplerLtake_sample6M_x_: statSampler.o; +text: .text%__1cHCompileFstart6kM_pnJStartNode__; +text: .text%__1cRis_error_reported6F_i_; +text: .text%__1cNWatcherThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cPStatSamplerTaskEtask6M_v_: statSampler.o; +text: .text%__1cMPeriodicTaskOreal_time_tick6FI_v_; +text: .text%__1cQPlaceholderTableKfind_entry6MiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cIParmNodeJideal_reg6kM_I_; +text: .text%__1cQandL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIMachNodeRget_base_and_disp6kMrirpknHTypePtr__pknENode__; +text: .text%__1cQSystemDictionarybBresolve_array_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cIregFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cHCompilebAallow_range_check_smearing6kM_i_; +text: .text%__1cRbranchLoopEndNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cGRFrame2t6MnFframe_pnKJavaThread_kp0_v_; +text: .text%__1cIciMethodWwas_executed_more_than6Mi_i_; +text: .text%jni_GetArrayLength: jni.o; +text: .text%__1cIMachNodeHtwo_adr6kM_I_: machnode.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: machnode.o; +text: .text%__1cPciInstanceKlassUget_canonical_holder6Mi_p0_; +text: .text%__1cJloadLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOClearArrayNodeGOpcode6kM_i_; +text: .text%__1cWCallLeafNoFPDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserbDverify_legal_method_signature6MnMsymbolHandle_1pnGThread__i_; +text: .text%__1cVCompressedWriteStreamEgrow6M_v_; +text: .text%__1cPcheckCastPPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_Write; +text: .text%__1cLciSignature2t6MpnHciKlass_pnIciSymbol__v_; +text: .text%__1cIciMethod2t6MnMmethodHandle__v_; +text: .text%__1cIHaltNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cWShouldNotReachHereNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKRelocationJpack_data6M_i_: relocInfo.o; +text: .text%__1cNinstanceKlassVis_same_class_package6FpnHoopDesc_pnNsymbolOopDesc_24_i_; +text: .text%__1cLOpaque1NodeGOpcode6kM_i_; +text: .text%__1cQmulL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cEUTF8Fequal6FpWi1i_i_; +text: .text%__1cSbranchCon_longNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKstoreCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHAddNodePadd_of_identity6kMpknEType_3_3_; +text: .text%__1cUcompU_iReg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cGThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%JVM_RawMonitorEnter; +text: .text%__1cFMutexMjvm_raw_lock6M_v_; +text: .text%JVM_RawMonitorExit; +text: .text%__1cFMutexOjvm_raw_unlock6M_v_; +text: .text%__1cKg1RegIOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: loopnode.o; +text: .text%__1cOMachReturnNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cPClassFileParserUskip_over_field_name6MpciI_1_; +text: .text%__1cMTypeKlassPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cIGraphKitNcast_not_null6MpnENode__2_; +text: .text%__1cWShouldNotReachHereNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitTtoo_many_recompiles6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cPcmpFastLockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cETypeRget_typeflow_type6FpnGciType__pk0_; +text: .text%__1cOJNIHandleBlockNrelease_block6Fp0pnGThread__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlass.o; +text: .text%__1cRcmpFastUnlockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXinitialize_static_field6FpnPfieldDescriptor_pnGThread__v_: classFileParser.o; +text: .text%__1cURethrowExceptionNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOJNIHandleBlockOallocate_block6FpnGThread__p0_; +text: .text%__1cIGraphKitOtoo_many_traps6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: bytecode.o; +text: .text%__1cKBufferBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cKBufferBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cSandI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIAddINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cLTypeInstPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cFParseRensure_memory_phi6Mii_pnHPhiNode__; +text: .text%__1cMloadConLNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cFParseFmerge6Mi_v_; +text: .text%__1cFParseUprofile_taken_branch6Mi_v_; +text: .text%__1cNSafePointNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cYcompareAndSwapL_boolNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cWstatic_stub_RelocationJpack_data6M_i_; +text: .text%__1cILoopNodeHis_Loop6M_p0_: loopnode.o; +text: .text%__1cNCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cQxorI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJTypeTupleFxdual6kM_pknEType__; +text: .text%__1cNLoadKlassNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPorI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRethrowNodeGOpcode6kM_i_; +text: .text%__1cYinlineCallClearArrayNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadSNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cYinlineCallClearArrayNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeHeapIcapacity6kM_I_; +text: .text%__1cKMemoryPoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cMCodeHeapPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cPcmpFastLockNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMCodeHeapPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cNloadKlassNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFArena2T6M_v_; +text: .text%__1cMLinkResolverTresolve_static_call6FrnICallInfo_rnLKlassHandle_nMsymbolHandle_53iipnGThread__v_; +text: .text%__1cKMemBarNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cOCallRelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cLRuntimeStubYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cHoopDescSslow_identity_hash6M_i_; +text: .text%__1cSObjectSynchronizerXidentity_hash_value_for6FnGHandle__i_; +text: .text%__1cJloadCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIimmPOperPconstant_is_oop6kM_i_: ad_sparc_clone.o; +text: .text%__1cLPCTableNodeEhash6kM_I_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnMklassOopDesc__i_; +text: .text%__1cHConNodeLout_RegMask6kM_rknHRegMask__: classes.o; +text: .text%__1cXPhaseAggressiveCoalesceYinsert_copy_with_overlap6MpnFBlock_pnENode_II_v_; +text: .text%__1cOloadConI13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMtlsLoadPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeGis_Con6kM_I_: multnode.o; +text: .text%__1cQandI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMPhaseChaitinNFind_compress6MI_I_; +text: .text%__1cITypeLongEmake6Fxx_pk0_; +text: .text%__1cMindIndexOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFStateN_sub_Op_LoadP6MpknENode__v_; +text: .text%__1cFframeVinterpreter_frame_bci6kM_i_; +text: .text%__1cJOopMapSetTupdate_register_map6FpknFframe_pnICodeBlob_pnLRegisterMap__v_; +text: .text%__1cNGCTaskManagerIget_task6MI_pnGGCTask__; +text: .text%__1cLGCTaskQdDueueGremove6M_pnGGCTask__; +text: .text%__1cNGCTaskManagerYshould_release_resources6MI_i_; +text: .text%__1cLGCTaskQdDueueHenqueue6MpnGGCTask__v_; +text: .text%__1cNGCTaskManagerPnote_completion6MI_v_; +text: .text%__1cITypeLongFempty6kM_i_; +text: .text%__1cJloadBNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQandI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLklassVtableXvtable_accessibility_at6Mi_n0AKAccessType__; +text: .text%__1cIGraphKitHjava_bc6kM_nJBytecodesECode__; +text: .text%__1cIGraphKitNbuiltin_throw6MnODeoptimizationLDeoptReason_pnENode__v_; +text: .text%__1cOGenerateOopMapHget_var6Mi_nNCellTypeState__; +text: .text%__1cRinterpretedVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cSmembar_acquireNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetSuperclass: jni.o; +text: .text%__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_; +text: .text%__1cTAbstractInterpreterbFsize_top_interpreter_activation6FpnNmethodOopDesc__i_; +text: .text%__1cCosbCstack_shadow_pages_available6FpnGThread_nMmethodHandle__i_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: ad_sparc.o; +text: .text%__1cIMulINodeGOpcode6kM_i_; +text: .text%__1cKInlineTreePshouldNotInline6kMpnIciMethod_pnMWarmCallInfo__pkc_; +text: .text%__1cRcompL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadCNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciMethodbHhas_unloaded_classes_in_signature6M_i_; +text: .text%__1cJloadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGGCTask2t6M_v_; +text: .text%__1cJloadSNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_: classes.o; +text: .text%__1cIJumpDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cObranchConUNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cObranchConPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cITypeFuncFxdual6kM_pknEType__; +text: .text%__1cQjava_lang_StringGlength6FpnHoopDesc__i_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: nmethod.o; +text: .text%__1cKstoreINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKMemBarNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFStateM_sub_Op_CmpI6MpknENode__v_; +text: .text%__1cJcmpOpOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cKTypeRawPtrCeq6kMpknEType__i_; +text: .text%__1cGciType2t6MnLKlassHandle__v_; +text: .text%__1cHciKlass2t6MnLKlassHandle__v_; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cMindirectOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFParseRoptimize_inlining6MpnIciMethod_ipnPciInstanceKlass_24irnKInlineTreeLInlineStyle_r2_v_; +text: .text%__1cSPSPromotionManagerbBgc_thread_promotion_manager6Fi_p0_; +text: .text%__1cQxorI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIregFOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cKcmpOpPOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cNloadKlassNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHPhiNodeMslice_memory6kMpknHTypePtr__p0_; +text: .text%__1cPCheckCastPPNodeJideal_reg6kM_I_: connode.o; +text: .text%__1cObranchConPNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cObranchConPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICHeapObj2k6Fpv_v_; +text: .text%__1cSaddL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRmethodDataOopDescJbci_to_dp6Mi_pC_; +text: .text%__1cMloadConFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRInvocationCounterJset_carry6M_v_; +text: .text%__1cFArena2t6M_v_; +text: .text%__1cRInterpreterOopMapLiterate_oop6MpnNOffsetClosure__v_; +text: .text%__1cRInterpreterOopMap2T6M_v_; +text: .text%__1cFframeToops_interpreted_do6MpnKOopClosure_pknLRegisterMap_i_v_; +text: .text%__1cLOopMapCacheGlookup6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cNinstanceKlassImask_for6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cNmethodOopDescImask_for6MipnRInterpreterOopMap__v_; +text: .text%__1cRInterpreterOopMap2t6M_v_; +text: .text%__1cJloadCNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadSNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cISubINodeDsub6kMpknEType_3_3_; +text: .text%__1cFParseOreturn_current6MpnENode__v_; +text: .text%__1cRsarI_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIGraphKitPstore_to_memory6MpnENode_22nJBasicType_i_2_; +text: .text%__1cJStoreNodeEmake6FpnENode_22pknHTypePtr_2nJBasicType__p0_; +text: .text%__1cMMonitorValue2t6MpnKScopeValue_nILocation__v_; +text: .text%__1cLBoxLockNodeKis_BoxLock6kM_pk0_: classes.o; +text: .text%__1cMMonitorValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cLBoxLockNodeKstack_slot6FpnENode__nHOptoRegEName__; +text: .text%__1cMloadConLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJStoreNodeSIdeal_masked_input6MpnIPhaseGVN_I_pnENode__; +text: .text%__1cHMatcherPstack_alignment6F_I_; +text: .text%jni_GetPrimitiveArrayCritical: jni.o; +text: .text%jni_ReleasePrimitiveArrayCritical: jni.o; +text: .text%__1cPconvI2L_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeIget_long6kM_x_; +text: .text%__1cNMemoryServiceXtrack_memory_pool_usage6FpnKMemoryPool__v_; +text: .text%__1cSmembar_releaseNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJimmU5OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cPciInstanceKlass2t6MnLKlassHandle__v_; +text: .text%__1cLOpaque1NodeEhash6kM_I_; +text: .text%__1cJStoreNodeZIdeal_sign_extended_input6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSbranchCon_longNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapGppload6MpnNCellTypeState_i_v_; +text: .text%__1cSmembar_releaseNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNbranchConNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRshrI_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFciEnvVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cKciTypeFlowGJsrSetSis_compatible_with6Mp1_i_; +text: .text%__1cFKlassTarray_klass_or_null6Mi_pnMklassOopDesc__; +text: .text%__1cZCallDynamicJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJMultiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKStoreLNodeGOpcode6kM_i_; +text: .text%__1cbBopt_virtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cTconstantPoolOopDescbCklass_ref_at_if_loaded_check6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cHciField2t6MpnPciInstanceKlass_i_v_; +text: .text%__1cNloadKlassNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cETypeCeq6kMpk0_i_; +text: .text%__1cOJNIHandleBlockHoops_do6MpnKOopClosure__v_; +text: .text%__1cOGenerateOopMapJdo_method6Miiii_v_; +text: .text%__1cFParseRbranch_prediction6Mrf_f_; +text: .text%__1cRsarI_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cHTypeAryFempty6kM_i_; +text: .text%__1cKTypeAryPtrFempty6kM_i_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodDataKlass.o; +text: .text%__1cOMacroAssemblerFjumpl6MrnHAddress_pnMRegisterImpl_ipkci_v_; +text: .text%__1cOMacroAssemblerEjump6MrnHAddress_ipkci_v_; +text: .text%__1cIciMethodLis_accessor6kM_i_; +text: .text%__1cRbranchLoopEndNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlass.o; +text: .text%__1cQmulL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFframeUentry_frame_is_first6kM_i_; +text: .text%__1cJiRegIOperFclone6kM_pnIMachOper__; +text: .text%__1cLstoreP0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRethrowNodeKmatch_edge6kMI_I_; +text: .text%__1cFTypeFEhash6kM_i_; +text: .text%__1cHnmethodbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cHnmethodHoops_do6MpnKOopClosure__v_; +text: .text%__1cFStateM_sub_Op_AddI6MpknENode__v_; +text: .text%__1cIciMethodPcan_be_compiled6M_i_; +text: .text%__1cOParseGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cFParseQcreate_entry_map6M_pnNSafePointNode__; +text: .text%__1cFArenaEused6kM_I_; +text: .text%__1cFParseLbuild_exits6M_v_; +text: .text%__1cFParseIdo_exits6M_v_; +text: .text%__1cIciMethodVhas_balanced_monitors6M_i_; +text: .text%__1cFParse2t6MpnIJVMState_pnIciMethod_f_v_; +text: .text%__1cIBoolNodeDcmp6kMrknENode__I_; +text: .text%__1cQsubI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParsePdo_method_entry6M_v_; +text: .text%__1cNCallGeneratorKfor_inline6FpnIciMethod_f_p0_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2t6M_v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2T6M_v_; +text: .text%__1cQconstMethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQSystemDictionaryVadd_loader_constraint6FnMsymbolHandle_nGHandle_2pnGThread__v_; +text: .text%__1cVLoaderConstraintTableJadd_entry6MnMsymbolHandle_pnMklassOopDesc_nGHandle_34pnGThread__i_; +text: .text%__1cRciVirtualCallDataOtranslate_from6MpnLProfileData__v_; +text: .text%jni_IsSameObject: jni.o; +text: .text%__1cMloadConINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNbranchConNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cNbranchConNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQandL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLmethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cLsymbolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQaddL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassbDcheck_valid_for_instantiation6MipnGThread__v_; +text: .text%__1cJStartNodeOis_block_start6kM_i_: callnode.o; +text: .text%__1cRsarI_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciObjectFklass6M_pnHciKlass__; +text: .text%__1cLSymbolTableFprobe6Fpkci_pnNsymbolOopDesc__; +text: .text%__1cSInterpreterRuntimebAfrequency_counter_overflow6FpnKJavaThread_pC_x_; +text: .text%__1cPThreadLocalNodeGOpcode6kM_i_; +text: .text%__1cENodeRlatency_from_uses6kMrnLBlock_Array_rnNGrowableArray4CI___i_; +text: .text%__1cPconvL2I_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cZPhaseConservativeCoalesceKupdate_ifg6MIIpnIIndexSet_2_v_; +text: .text%__1cZPhaseConservativeCoalesceMunion_helper6MpnENode_2II222pnFBlock_I_v_; +text: .text%__1cOMethodLivenessKBasicBlockJstore_one6Mi_v_; +text: .text%__1cIIndexSetEswap6Mp0_v_; +text: .text%__1cHTypeAryEmake6FpknEType_pknHTypeInt__pk0_; +text: .text%__1cXmembar_release_lockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserbCverify_legal_class_modifiers6MipnGThread__v_; +text: .text%__1cJloadLNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cRshrP_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKTypeAryPtrFxdual6kM_pknEType__; +text: .text%__1cFBlockTimplicit_null_check6MrnLBlock_Array_rnNGrowableArray4CI__pnENode_6_v_; +text: .text%__1cQandI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFframeNis_glue_frame6kM_i_; +text: .text%__1cLAccessFlagsPatomic_set_bits6Mi_v_; +text: .text%__1cFParseYprofile_not_taken_branch6M_v_; +text: .text%__1cQComputeCallStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cHciKlassNis_subtype_of6Mp0_i_; +text: .text%__1cbACallCompiledJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: cfgnode.o; +text: .text%__1cRRawBytecodeStream2t6MnMmethodHandle__v_; +text: .text%__1cNinstanceKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cKstoreBNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQLibraryIntrinsicKis_virtual6kM_i_: library_call.o; +text: .text%__1cMPrefetchNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKCastPPNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: callnode.o; +text: .text%__1cKstorePNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOPhaseIdealLoopOsplit_thru_phi6MpnENode_2i_2_; +text: .text%__1cYDebugInformationRecorderNadd_dependent6MpnPciInstanceKlass_pnIciMethod__v_; +text: .text%__1cENodeGOpcode6kM_i_; +text: .text%__1cRshrP_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQandI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMURShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciMethodbBinterpreter_call_site_count6Mi_i_; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cGBitMapIset_from6M0_v_; +text: .text%__1cNmethodOopDescWload_signature_classes6FnMmethodHandle_pnGThread__i_; +text: .text%__1cNCompileBrokerOcompile_method6FnMmethodHandle_i1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNCompileBrokerTcompile_method_base6FnMmethodHandle_ii1ipkcpnGThread__pnHnmethod__; +text: .text%__1cTconstantPoolOopDescbDresolve_string_constants_impl6FnSconstantPoolHandle_pnGThread__v_; +text: .text%__1cHSubNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNinstanceKlassPlink_class_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cMloadConFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFChunk2n6FII_pv_; +text: .text%__1cbACallCompiledJavaDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2L_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTCallDynamicJavaNodeGOpcode6kM_i_; +text: .text%__1cKstoreBNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cILoadNodeDcmp6kMrknENode__I_; +text: .text%__1cIciObject2t6M_v_; +text: .text%__1cENodeHdel_out6Mp0_v_: library_call.o; +text: .text%__1cSconstMethodOopDescZchecked_exceptions_length6kM_i_; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: cfgnode.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: cfgnode.o; +text: .text%__1cRcompL_reg_conNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHCompileXin_preserve_stack_slots6M_I_; +text: .text%__1cPciObjectFactoryUget_empty_methodData6M_pnMciMethodData__; +text: .text%__1cMciMethodData2t6M_v_; +text: .text%__1cLOopRecorderKfind_index6MpnI_jobject__i_; +text: .text%__1cJStartNodeOis_block_start6kM_i_: classes.o; +text: .text%__1cHOrINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFframeLreal_sender6kMpnLRegisterMap__0_; +text: .text%__1cFframeTis_first_java_frame6kM_i_; +text: .text%__1cGRFrameGcaller6M_p0_; +text: .text%__1cFframeNis_java_frame6kM_i_; +text: .text%__1cNprefetch2NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowFBlockPclone_loop_head6Mp0ip1pn0AGJsrSet__3_; +text: .text%__1cPCheckCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRshrP_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_: loopnode.o; +text: .text%__1cFParseFdo_if6MpnENode_2nIBoolTestEmask_2_v_; +text: .text%__1cLCastP2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cXmembar_release_lockNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cJloadINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSandI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: callnode.o; +text: .text%__1cMPrefetchNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cXJNI_ArgumentPusherVaArgKget_object6M_v_: jni.o; +text: .text%__1cMloadConFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMCreateExNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJCodeCacheIcontains6Fpv_i_; +text: .text%__1cQaddL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMCallLeafNodeLis_CallLeaf6kM_pk0_: classes.o; +text: .text%__1cLPCTableNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMCreateExNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIMachNodeJis_MachIf6kM_pknKMachIfNode__: ad_sparc.o; +text: .text%__1cISubINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cFKlassQset_next_sibling6MpnMklassOopDesc__v_; +text: .text%__1cQdivD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNCatchProjNodeDcmp6kMrknENode__I_; +text: .text%__1cIGraphKitRmake_slow_call_ex6MpnENode_pnPciInstanceKlass__v_; +text: .text%__1cKTypeOopPtrEhash6kM_i_; +text: .text%__1cIMinINodeGOpcode6kM_i_; +text: .text%__1cYinlineCallClearArrayNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNflagsRegLOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cMURShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIConINodeHget_int6kMpi_i_: classes.o; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: psTasks.o; +text: .text%__1cJOopMapSetGall_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure_pFppnHoopDesc_9E_v9B9B_v_; +text: .text%__1cFframeRoops_code_blob_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cJOopMapSetHoops_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cKTypeRawPtrFxmeet6kMpknEType__3_; +text: .text%__1cFBlockUhoist_LCA_above_defs6Mp01IrnLBlock_Array__1_; +text: .text%JVM_GetMethodIxModifiers; +text: .text%__1cMvframeStream2t6MpnKJavaThread_i_v_; +text: .text%__1cQjava_lang_StringGequals6FpnHoopDesc_pHi_i_; +text: .text%__1cIMulLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_: codeBlob.o; +text: .text%__1cVExceptionHandlerTableMadd_subtable6MipnNGrowableArray4Ci__2_v_; +text: .text%__1cPconvI2L_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLLShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cHMulNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cTCreateExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_IsInterface; +text: .text%__1cHciKlassOis_subclass_of6Mp0_i_; +text: .text%__1cJloadCNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRInterpretedRFrameOis_interpreted6kM_i_: rframe.o; +text: .text%__1cGRFrameLis_compiled6kM_i_: rframe.o; +text: .text%__1cPorI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIDivINodeGOpcode6kM_i_; +text: .text%__1cbACallCompiledJavaDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapTmerge_state_into_bb6MpnKBasicBlock__v_; +text: .text%__1cICodeHeapIallocate6MI_pv_; +text: .text%__1cICodeHeapPsearch_freelist6MI_pnJFreeBlock__; +text: .text%__1cRcompL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLOpaque1NodeLbottom_type6kM_pknEType__: connode.o; +text: .text%__1cNloadRangeNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQput_after_lookup6FnMsymbolHandle_0ppnLNameSigHash__i_; +text: .text%__1cSconvI2D_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHMatcherQis_spillable_arg6Fi_i_; +text: .text%__1cLRegisterMapFclear6Mpi_v_; +text: .text%__1cLRShiftLNodeGOpcode6kM_i_; +text: .text%__1cJCodeCacheIallocate6Fi_pnICodeBlob__; +text: .text%__1cSCountedLoopEndNodeKstride_con6kM_i_; +text: .text%__1cUPipeline_Use_Element2t6M_v_: output.o; +text: .text%__1cRshrL_reg_imm6NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHCompileSregister_intrinsic6MpnNCallGenerator__v_; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_: classes.o; +text: .text%__1cFParseSmerge_memory_edges6MpnMMergeMemNode_ii_v_; +text: .text%__1cNSCMemProjNodeGOpcode6kM_i_; +text: .text%__1cNimmP_pollOperEtype6kM_pknEType__: ad_sparc_clone.o; +text: .text%__1cRloadConP_pollNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cQinstanceRefKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQinstanceRefKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSconvI2D_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapUreachable_basicblock6Fp0ipi_v_; +text: .text%__1cIMulINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPciInstanceKlassLfind_method6MpnIciSymbol_2_pnIciMethod__; +text: .text%__1cXvirtual_call_RelocationLunpack_data6M_v_; +text: .text%__1cFciEnvRfind_system_klass6MpnIciSymbol__pnHciKlass__; +text: .text%__1cLRegisterMapIpd_clear6M_v_; +text: .text%__1cSstkL_to_regD_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHUNICODEHas_utf86FpHi_pc_; +text: .text%__1cLstoreP0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_2NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cParrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cParrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMnegF_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKo0RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cSaddL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCodeBuffer2t6MiiiiiipnKBufferBlob_pnJrelocInfo_pnORelocateBuffer_ipnLOopRecorder_pkcii_v_; +text: .text%__1cIGraphKitYcombine_exception_states6MpnNSafePointNode_2_v_; +text: .text%__1cSstkL_to_regD_2NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_ltNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassWfind_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cPciInstanceKlassTget_field_by_offset6Mii_pnHciField__; +text: .text%__1cRshrP_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconstMethodOopDescYchecked_exceptions_start6kM_pnXCheckedExceptionElement__; +text: .text%__1cSstkL_to_regD_2NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cWstatic_stub_RelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cSstkL_to_regD_2NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: codeBlob.o; +text: .text%__1cPClassFileParserYparse_checked_exceptions6MpHInSconstantPoolHandle_pnGThread__1_; +text: .text%__1cKstoreLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSvframeStreamCommonbHskip_method_invoke_and_aux_frames6M_v_; +text: .text%__1cRbranchLoopEndNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSconvI2D_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassXmark_dependent_nmethods6MpnMklassOopDesc__i_; +text: .text%__1cDCHANprocess_class6FnLKlassHandle_pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___nMsymbolHandle_6_v_; +text: .text%__1cQConstantIntValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cSconvI2D_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileStreamHskip_u26MipnGThread__v_; +text: .text%__1cHnmethodPis_locked_by_vm6kM_i_: nmethod.o; +text: .text%__1cUcompU_iReg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitXset_edges_for_java_call6MpnMCallJavaNode_i_v_; +text: .text%__1cOMacroAssemblerNverify_thread6M_v_; +text: .text%__1cJloadLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitZset_results_for_java_call6MpnMCallJavaNode__pnENode__; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_: methodDataOop.o; +text: .text%__1cSbranchCon_longNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHnmethodVcleanup_inline_caches6M_v_; +text: .text%__1cTciConstantPoolCacheGinsert6Mipv_v_; +text: .text%__1cIAddLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNloadKlassNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFStateO_sub_Op_StoreI6MpknENode__v_; +text: .text%__1cKHandleAreaHoops_do6MpnKOopClosure__v_; +text: .text%__1cRbranchLoopEndNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHciField2t6MpnPfieldDescriptor__v_; +text: .text%__1cMjniIdSupportNto_method_oop6FpnK_jmethodID__pnNmethodOopDesc__; +text: .text%__1cRSignatureIterator2t6MpnGThread_pnNsymbolOopDesc__v_; +text: .text%__1cMloadConLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZCallInterpreterDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadSNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodLis_unloaded6kM_i_: nmethod.o; +text: .text%__1cYcompareAndSwapL_boolNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFKlassMnext_sibling6kM_p0_; +text: .text%__1cSbranchCon_longNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKDictionaryStry_get_next_class6M_pnMklassOopDesc__; +text: .text%__1cNinstanceKlassKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cQSystemDictionaryStry_get_next_class6F_pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSobjArrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJimmU5OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cRbranchLoopEndNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: ad_sparc.o; +text: .text%__1cLBlock_ArrayEgrow6MI_v_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__: loopnode.o; +text: .text%__1cbCcatch_cleanup_fix_all_inputs6FpnENode_11_v_: lcm.o; +text: .text%__1cYinternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cOMacroAssemblerUallocate_oop_address6MpnI_jobject_pnMRegisterImpl__nHAddress__; +text: .text%__1cUThreadSafepointStateXexamine_state_of_thread6Mi_v_; +text: .text%__1cUSafepointSynchronizeOsafepoint_safe6FpnKJavaThread_nPJavaThreadState__i_; +text: .text%__1cUParallelScavengeHeapNtlab_capacity6kM_I_; +text: .text%__1cKcmpOpPOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cObranchConPNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cKstoreBNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cUvisit_all_interfaces6FpnPobjArrayOopDesc_pnXInterfaceVisiterClosure__v_; +text: .text%__1cLBoxLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmpFastLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHRetNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRegionNodeEhash6kM_I_: loopnode.o; +text: .text%__1cPconvL2I_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNnmethodLocker2T6M_v_; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cKoopFactoryTnew_system_objArray6FipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNnmethodLocker2t6MpnHnmethod__v_; +text: .text%__1cLstoreB0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescOis_initializer6kM_i_; +text: .text%__1cbDcatch_cleanup_find_cloned_def6FpnFBlock_pnENode_1rnLBlock_Array_i_3_: lcm.o; +text: .text%__1cQxorI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCodeBufferMstart_a_stub6M_v_; +text: .text%__1cKstoreLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCodeBufferKend_a_stub6M_v_; +text: .text%__1cFTypeFCeq6kMpknEType__i_; +text: .text%__1cFciEnvVget_constant_by_index6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cQciByteCodeStreamMget_constant6M_nKciConstant__; +text: .text%__1cFciEnvbAget_constant_by_index_impl6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cOClearArrayNodeKmatch_edge6kMI_I_; +text: .text%__1cPconvL2I_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadSNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSbranchCon_longNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cOFastUnlockNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cKJavaThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cSFixupMirrorClosureJdo_object6MpnHoopDesc__v_: universe.o; +text: .text%__1cFStateP_sub_Op_LShiftI6MpknENode__v_; +text: .text%__1cQandL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJiRegPOperFclone6kM_pnIMachOper__; +text: .text%__1cPSignatureStreamRas_symbol_or_null6M_pnNsymbolOopDesc__; +text: .text%__1cIregDOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKMemBarNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKoopFactoryYnew_permanent_shortArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cKMemBarNode2t6M_v_; +text: .text%__1cNIdealLoopTreeObeautify_loops6MpnOPhaseIdealLoop__i_; +text: .text%__1cRsarI_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: frame.o; +text: .text%__1cKimmI13OperFclone6kM_pnIMachOper__; +text: .text%__1cIGraphKitbBset_arguments_for_java_call6MpnMCallJavaNode__v_; +text: .text%__1cIGraphKitJpush_node6MnJBasicType_pnENode__v_: callGenerator.o; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: bytecode.o; +text: .text%__1cNCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cJcmpOpOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cNprefetch2NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRcompL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_SetObjectArrayElement: jni.o; +text: .text%__1cSandI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPThreadLocalNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNSafePointNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKMemBarNodeJis_MemBar6kM_pk0_: classes.o; +text: .text%__1cIciMethodXfind_monomorphic_target6MpnHciKlass_22_p0_; +text: .text%__1cJCHAResultOis_monomorphic6kM_i_; +text: .text%__1cDCHAManalyze_call6FnLKlassHandle_11nMsymbolHandle_2_pnJCHAResult__; +text: .text%__1cOloadConI13NodeFclone6kM_pnENode__; +text: .text%__1cObranchConUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitOinsert_mem_bar6MpnKMemBarNode__v_; +text: .text%__1cLOptoRuntimeOnew_objArray_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cRshlL_reg_imm6NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cbACallCompiledJavaDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQandI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRCardTableModRefBSPdirty_MemRegion6MnJMemRegion__v_; +text: .text%__1cZresource_reallocate_bytes6FpcII_0_; +text: .text%__1cLConvL2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOAbstractICacheQinvalidate_range6FpCi_v_; +text: .text%__1cKstorePNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMaxINodeGOpcode6kM_i_; +text: .text%__1cIGraphKitMarray_length6MpnENode__2_; +text: .text%__1cIGraphKitbMset_predefined_output_for_runtime_call6MpnENode_pnMMergeMemNode__v_; +text: .text%__1cWThreadLocalAllocBufferFclear6M_v_; +text: .text%__1cTDirectCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cNCallGeneratorPfor_direct_call6FpnIciMethod__p0_; +text: .text%__1cMWarmCallInfoLalways_cold6F_p0_; +text: .text%__1cMPhaseChaitinQgather_lrg_masks6Mi_v_; +text: .text%__1cIimmDOperJconstantD6kM_d_: ad_sparc_clone.o; +text: .text%__1cIPhaseIFGEinit6MI_v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: live.o; +text: .text%__1cJPhaseLiveHcompute6MI_v_; +text: .text%__1cMLinkResolverbCresolve_virtual_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cSaddI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRcompL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cZnoG3_iRegI_64bit_safeOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cFTypeDEmake6Fd_pk0_; +text: .text%__1cFKlassMoop_is_array6kM_i_: symbolKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: symbolKlass.o; +text: .text%__1cPThreadRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cPThreadRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cRshlI_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvL2I_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQciTypeArrayKlassTis_type_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cQaddL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHMatcherMreturn_value6Fii_nLRegPair__; +text: .text%__1cGThreadQunboost_priority6Fp0_v_; +text: .text%__1cMloadConDNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cFStateN_sub_Op_LoadI6MpknENode__v_; +text: .text%__1cIMachOperEtype6kM_pknEType__; +text: .text%JVM_GetCPClassNameUTF; +text: .text%__1cUcompU_iReg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cITypeNodeHis_Type6M_p0_: classes.o; +text: .text%__1cICodeBlob2t6Mpkcii_v_; +text: .text%__1cKBufferBlobGcreate6Fpkci_p0_; +text: .text%__1cTmembar_CPUOrderNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadBNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKcmpOpUOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cObranchConUNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cObranchConUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmembar_acquireNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJNIHandlesLmake_global6FnGHandle_i_pnI_jobject__; +text: .text%__1cTCallInterpreterNodeGOpcode6kM_i_; +text: .text%jni_GetStringLength: jni.o; +text: .text%__1cMLinkResolverbBresolve_static_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cRshrP_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCHAResultSmonomorphic_target6kM_nMmethodHandle__; +text: .text%__1cLConvI2LNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHMonitorEwait6Mil_i_; +text: .text%__1cJloadPNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cMoutputStream2t6Mi_v_; +text: .text%__1cMstringStreamJas_string6M_pc_; +text: .text%__1cMstringStream2T6M_v_; +text: .text%__1cMstringStream2t6MI_v_; +text: .text%__1cIGraphKitMreset_memory6M_pnENode__; +text: .text%__1cZCallDynamicJavaDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKstorePNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstoreLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNprefetch2NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeMsetup_is_top6M_v_; +text: .text%__1cIGotoNodeGOpcode6kM_i_; +text: .text%__1cPorI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPfieldDescriptorRint_initial_value6kM_i_; +text: .text%__1cSReferenceProcessorSdiscover_reference6MpnHoopDesc_nNReferenceType__i_; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: callnode.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: callnode.o; +text: .text%__1cHRetNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cNbranchConNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cHRetNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapLbb_mark_fct6Fp0ipi_v_; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjectFactory.o; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciObjectFactory.o; +text: .text%__1cKcmpOpPOperFequal6kM_i_: ad_sparc_clone.o; +text: .text%__1cSInterpreterRuntimeE_new6FpnKJavaThread_pnTconstantPoolOopDesc_i_v_; +text: .text%__1cKReturnNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSandI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_ref_ReferenceIset_next6FpnHoopDesc_2_v_; +text: .text%__1cSReferenceProcessorTget_discovered_list6MnNReferenceType__ppnHoopDesc__; +text: .text%__1cOGenerateOopMapRsigchar_to_effect6McipnNCellTypeState__2_; +text: .text%__1cXmembar_acquire_lockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapIdo_field6Miiii_v_; +text: .text%__1cKStoreBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJloadINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmembar_releaseNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cSaddL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLRShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cEDict2t6MpFpkv2_ipF2_i_v_; +text: .text%__1cEDict2T6M_v_; +text: .text%__1cKBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cLOopRecorder2t6MpnFArena__v_; +text: .text%__1cRClassPathZipEntryLopen_stream6Mpkc_pnPClassFileStream__; +text: .text%__1cMLinkResolverbCresolve_special_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cIModINodeGOpcode6kM_i_; +text: .text%__1cUGenericGrowableArray2t6MiipnEGrET_i_v_; +text: .text%__1cJloadCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeHget_int6kMpi_i_; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_nMmethodHandle__v_; +text: .text%__1cKJavaThreadQlast_java_vframe6MpnLRegisterMap__pnKjavaVFrame__; +text: .text%__1cTStackWalkCompPolicyVfindTopInlinableFrame6MpnNGrowableArray4CpnGRFrame____2_; +text: .text%__1cTStackWalkCompPolicyXmethod_invocation_event6MnMmethodHandle_pnGThread__v_; +text: .text%__1cKjavaVFrameNis_java_frame6kM_i_: vframe.o; +text: .text%__1cISubLNodeGOpcode6kM_i_; +text: .text%__1cKciTypeFlowXmark_known_range_starts6M_v_; +text: .text%__1cKciTypeFlow2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cKciTypeFlowLfind_ranges6M_v_; +text: .text%__1cKciTypeFlowPget_start_state6M_pkn0ALStateVector__; +text: .text%__1cKciTypeFlowHdo_flow6M_v_; +text: .text%__1cKciTypeFlowKflow_types6M_v_; +text: .text%__1cKciTypeFlowKmap_blocks6M_v_; +text: .text%__1cMloadConPNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_ltNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTconstantPoolOopDescbCverify_constant_pool_resolve6FnSconstantPoolHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cKStoreCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciMethodJload_code6M_v_; +text: .text%__1cMciMethodDataJload_data6M_v_; +text: .text%__1cIGraphKitTuse_exception_state6MpnNSafePointNode__pnENode__; +text: .text%__1cOcompU_iRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitGmemory6MI_pnENode__; +text: .text%__1cIHaltNodeEhash6kM_I_: classes.o; +text: .text%__1cFKlassQup_cast_abstract6M_p0_; +text: .text%__1cKReturnNodeEhash6kM_I_: classes.o; +text: .text%__1cLklassVtableRinitialize_vtable6MpnGThread__v_; +text: .text%__1cPClassFileParserXverify_legal_class_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cNobjArrayKlassPoop_is_objArray6kM_i_: objArrayKlass.o; +text: .text%__1cPjava_lang_ClassNcreate_mirror6FnLKlassHandle_pnGThread__pnHoopDesc__; +text: .text%__1cIAndINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodDataKlass.o; +text: .text%__1cPmethodDataKlassRoop_is_methodData6kM_i_: methodDataKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: methodDataKlass.o; +text: .text%__1cMciMethodData2t6MnQmethodDataHandle__v_; +text: .text%__1cIGraphKitOmake_slow_call6MpknITypeFunc_pCpkcpnENode_88_8_; +text: .text%__1cUGenericGrowableArrayEgrow6Mi_v_; +text: .text%__1cIAndINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cLOpaque2NodeGOpcode6kM_i_; +text: .text%__1cOClearArrayNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNmethodOopDescbEfast_exception_handler_bci_for6MnLKlassHandle_ipnGThread__i_; +text: .text%__1cSInterpreterRuntimebFexception_handler_for_exception6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cOPhaseIdealLoopPis_counted_loop6MpnENode_pnNIdealLoopTree__2_; +text: .text%__1cKstoreCNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackHdo_void6M_v_: generateOopMap.o; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: machnode.o; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: klass.o; +text: .text%__1cFKlassRinitialize_supers6MpnMklassOopDesc_pnGThread__v_; +text: .text%__1cKKlass_vtbl2n6FIrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: klass.o; +text: .text%__1cFKlassVbase_create_klass_oop6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__pnMklassOopDesc__; +text: .text%__1cQjava_lang_StringLutf8_length6FpnHoopDesc__i_; +text: .text%jni_GetStringUTFLength: jni.o; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc_ii_pc_; +text: .text%jni_GetStringUTFRegion: jni.o; +text: .text%__1cFKlassRbase_create_klass6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__1_; +text: .text%__1cHUNICODELutf8_length6FpHi_i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: loopnode.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: loopnode.o; +text: .text%__1cQPlaceholderTableJadd_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cQPlaceholderTableMremove_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cENodeHis_Copy6kM_I_: ad_sparc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: memnode.o; +text: .text%__1cKstoreBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQSystemDictionaryTload_instance_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cRsarI_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUcompU_iReg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHAddNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cUcompU_iReg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKPerfStringKset_string6Mpkc_v_; +text: .text%__1cQjava_lang_StringRas_unicode_string6FpnHoopDesc_ri_pH_; +text: .text%JVM_InternString; +text: .text%__1cLStringTableGintern6FpnHoopDesc_pnGThread__2_; +text: .text%__1cOGenerateOopMapTmerge_state_vectors6MpnNCellTypeState_2_i_; +text: .text%__1cRcompL_reg_conNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshrL_reg_imm6NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosGrandom6F_l_; +text: .text%__1cXmembar_acquire_lockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKimmP13OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cVcompP_iRegP_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKoopFactoryXnew_permanent_byteArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cENodeGis_Con6kM_I_: ad_sparc.o; +text: .text%__1cSaddL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRcompL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: machnode.o; +text: .text%__1cNMachIdealNodeJnum_opnds6kM_I_: machnode.o; +text: .text%__1cRMachNullCheckNodeLout_RegMask6kM_rknHRegMask__: machnode.o; +text: .text%__1cRMachNullCheckNode2t6MpnENode_2I_v_; +text: .text%__1cSTailCalljmpIndNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cIGraphKitPpush_pair_local6Mi_v_: parse2.o; +text: .text%__1cICodeHeapKdeallocate6Mpv_v_; +text: .text%__1cJCodeCacheEfree6FpnICodeBlob__v_; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_: classes.o; +text: .text%__1cSCompareAndSwapNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFParseNpush_constant6MnKciConstant__i_; +text: .text%__1cKTypeRawPtrEmake6FpC_pk0_; +text: .text%jni_SetIntField: jni.o; +text: .text%__1cENodeGis_Sub6M_pnHSubNode__: classes.o; +text: .text%__1cNIdealLoopTreeMcounted_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cNIdealLoopTreeTcheck_inner_safepts6MpnOPhaseIdealLoop__v_; +text: .text%__1cIciObjectMis_obj_array6M_i_: ciInstanceKlass.o; +text: .text%__1cKBufferBlobEfree6Fp0_v_; +text: .text%__1cIRootNodeHis_Root6M_p0_: classes.o; +text: .text%__1cPconvL2I_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLRuntimeStubIis_alive6kM_i_: codeBlob.o; +text: .text%__1cLCastP2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPciObjectFactoryMvm_symbol_at6Fi_pnIciSymbol__; +text: .text%__1cHnmethodKpc_desc_at6MpCi_pnGPcDesc__; +text: .text%__1cKDictionaryJadd_klass6MnMsymbolHandle_nGHandle_nLKlassHandle__v_; +text: .text%__1cQSystemDictionaryRcheck_constraints6FiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cVLoaderConstraintTablePcheck_or_update6MnTinstanceKlassHandle_nGHandle_nMsymbolHandle__pkc_; +text: .text%__1cVshrL_reg_imm6_L2INodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQmodI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassXsearch_secondary_supers6kMpnMklassOopDesc__i_; +text: .text%__1cZCallDynamicJavaDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: graphKit.o; +text: .text%__1cKTypeRawPtrFempty6kM_i_; +text: .text%__1cLConvI2LNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIMachNodeSalignment_required6kM_i_: machnode.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: machnode.o; +text: .text%__1cWImplicitExceptionTableGappend6MII_v_; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: machnode.o; +text: .text%__1cRMachNullCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_: ciMethodData.o; +text: .text%__1cQxorI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNIdealLoopTreeVadjust_loop_exit_prob6MpnOPhaseIdealLoop__v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: assembler_sparc.o; +text: .text%__1cNinstanceKlassVadd_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cOLibraryCallKitOgenerate_guard6MpnENode_pnKRegionNode_f_v_; +text: .text%__1cSandI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLstoreP0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIPhaseIFGISquareUp6M_v_; +text: .text%__1cLklassVtableMget_mirandas6FpnNGrowableArray4CpnNmethodOopDesc___pnMklassOopDesc_pnPobjArrayOopDesc_8_v_; +text: .text%__1cKCodeBuffer2T6M_v_; +text: .text%__1cLklassItableRinitialize_itable6M_v_; +text: .text%__1cQPSGenerationPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cQPSGenerationPoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cQPSGenerationPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cKNode_Array2t6MpnFArena__v_: reg_split.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: multnode.o; +text: .text%__1cLOpaque1NodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPconvI2L_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMURShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJLoadBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRcompL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cXAdaptiveWeightedAverageGsample6Mf_v_; +text: .text%__1cFKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cQciByteCodeStreamJget_klass6Mri_pnHciKlass__; +text: .text%__1cFKlassMoop_is_klass6kM_i_: instanceKlass.o; +text: .text%__1cFKlassWappend_to_sibling_list6M_v_; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__: classes.o; +text: .text%__1cQcmovI_reg_ltNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionarySjava_system_loader6F_pnHoopDesc__; +text: .text%__1cFKlassMset_subklass6MpnMklassOopDesc__v_; +text: .text%__1cOGenerateOopMapLmerge_state6Fp0ipi_v_; +text: .text%__1cUCallCompiledJavaNodeGOpcode6kM_i_; +text: .text%__1cMTypeKlassPtrFxdual6kM_pknEType__; +text: .text%__1cNprefetch2NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryVdefine_instance_class6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cSinstanceKlassKlassXallocate_instance_klass6MiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cPClassFileParserbBcheck_final_method_override6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cJCodeCachebKnumber_of_nmethods_with_dependencies6F_i_; +text: .text%__1cNinstanceKlassQinit_implementor6M_v_; +text: .text%__1cPClassFileParserNfill_oop_maps6MnTinstanceKlassHandle_ii_v_; +text: .text%__1cPClassFileStream2t6MpCipc_v_; +text: .text%__1cRconstantPoolKlassIallocate6MipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cNinstanceKlassSprocess_interfaces6MpnGThread__v_; +text: .text%__1cNmethodOopDescMsort_methods6FpnPobjArrayOopDesc_222_v_; +text: .text%__1cNinstanceKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cKoopFactoryQnew_constantPool6FipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cKoopFactoryRnew_instanceKlass6FiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cVjava_lang_ClassLoaderRis_trusted_loader6FpnHoopDesc__i_; +text: .text%__1cPClassFileParserOparseClassFile6MnMsymbolHandle_nGHandle_2r1pnGThread__nTinstanceKlassHandle__; +text: .text%__1cNinstanceKlassWdo_local_static_fields6MpFpnPfieldDescriptor_pnGThread__v4_v_; +text: .text%__1cPClassFileParserMsort_methods6MnOobjArrayHandle_111pnGThread__nPtypeArrayHandle__; +text: .text%__1cFKlassKsuperklass6kM_pnNinstanceKlass__; +text: .text%__1cPClassFileParserbBparse_constant_pool_entries6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserTparse_constant_pool6MpnGThread__nSconstantPoolHandle__; +text: .text%__1cTClassLoadingServiceTnotify_class_loaded6FpnNinstanceKlass_i_v_; +text: .text%__1cPClassFileParserQparse_interfaces6MnSconstantPoolHandle_nGHandle_2pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserbDcompute_transitive_interfaces6MnTinstanceKlassHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cIVerifierRshould_verify_for6FpnHoopDesc__i_; +text: .text%__1cIUniverseTflush_dependents_on6FnTinstanceKlassHandle__v_; +text: .text%__1cIVerifierQrelax_verify_for6FpnHoopDesc__i_; +text: .text%__1cPClassFileParserMparse_fields6MnSconstantPoolHandle_ipnUFieldAllocationCount_pnOobjArrayHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cLklassItableZsetup_itable_offset_table6FnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserbCcheck_super_interface_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassQeager_initialize6MpnGThread__v_; +text: .text%__1cLklassVtablebKcompute_vtable_size_and_num_mirandas6Fri1pnMklassOopDesc_pnPobjArrayOopDesc_nLAccessFlags_pnHoopDesc_pnNsymbolOopDesc_5_v_; +text: .text%__1cPClassFileParserVset_precomputed_flags6MnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserNparse_methods6MnSconstantPoolHandle_ipnLAccessFlags_ppnPobjArrayOopDesc_66pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserbAparse_classfile_attributes6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cQSystemDictionaryVresolve_super_or_fail6FnMsymbolHandle_1nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cKcmpOpPOperJnot_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cRMachSafePointNodePis_MachCallJava6M_pnQMachCallJavaNode__: ad_sparc_misc.o; +text: .text%__1cMPhaseIterGVNIoptimize6M_v_; +text: .text%__1cOPhaseTransform2t6MnFPhaseLPhaseNumber__v_; +text: .text%__1cISubINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceKlass.o; +text: .text%__1cNinstanceKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlass.o; +text: .text%__1cHMemNodeHsize_of6kM_I_; +text: .text%__1cFVTuneQstart_class_load6F_v_; +text: .text%__1cSThreadProfilerMark2T6M_v_; +text: .text%__1cFVTuneOend_class_load6F_v_; +text: .text%__1cLClassLoaderOload_classfile6FnMsymbolHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cJEventMark2t6MpkcE_v_: classLoader.o; +text: .text%__1cSThreadProfilerMark2t6Mn0AGRegion__v_; +text: .text%__1cQSystemDictionaryRload_shared_class6FnTinstanceKlassHandle_nGHandle_pnGThread__1_; +text: .text%__1cKklassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cPClassFileParserbKparse_classfile_sourcefile_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cQmodI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLRShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKCMoveINodeGOpcode6kM_i_; +text: .text%__1cLLShiftLNodeGOpcode6kM_i_; +text: .text%__1cYcompareAndSwapL_boolNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSinstanceKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNinstanceKlassScopy_static_fields6MpnSPSPromotionManager__v_; +text: .text%__1cMtlsLoadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateQ_sub_Op_URShiftI6MpknENode__v_; +text: .text%__1cKcmpOpUOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cObranchConUNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cQaddP_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOGenerateOopMapJinterp_bb6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapQnext_bb_start_pc6MpnKBasicBlock__i_; +text: .text%__1cLklassVtableYadd_new_mirandas_to_list6FpnNGrowableArray4CpnNmethodOopDesc___pnPobjArrayOopDesc_6pnMklassOopDesc__v_; +text: .text%__1cIRewriterHrewrite6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassNrewrite_class6MpnGThread__v_; +text: .text%__1cKoopFactoryVnew_constantPoolCache6FipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cYconstantPoolCacheOopDescKinitialize6MrnIintArray__v_; +text: .text%__1cWconstantPoolCacheKlassIallocate6MipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cIVerifierRverify_byte_codes6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassWadd_loader_constraints6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%JVM_GetMethodIxSignatureUTF; +text: .text%JVM_GetMethodIxMaxStack; +text: .text%JVM_GetMethodIxArgsSize; +text: .text%JVM_GetMethodIxByteCodeLength; +text: .text%JVM_GetMethodIxExceptionIndexes; +text: .text%JVM_GetMethodIxByteCode; +text: .text%JVM_GetMethodIxExceptionsCount; +text: .text%__1cPCheckCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLstoreP0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHCmpNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cMPhaseChaitinSbuild_ifg_physical6MpnMResourceArea__I_; +text: .text%__1cWCountInterfacesClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cQmulD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKoopFactoryWnew_permanent_intArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cPClassFileParserVparse_exception_table6MIInSconstantPoolHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cNPhaseCoalescePcoalesce_driver6M_v_; +text: .text%__1cLProfileDataSis_VirtualCallData6M_i_: ciMethodData.o; +text: .text%__1cLBuildCutout2T6M_v_; +text: .text%__1cNloadConL0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cITypeFuncMreturns_long6kM_i_; +text: .text%__1cJloadSNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse2.o; +text: .text%__1cENodeGis_Con6kM_I_: connode.o; +text: .text%__1cNloadConP0NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cJimmP0OperEtype6kM_pknEType__: ad_sparc_clone.o; +text: .text%__1cLstoreI0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPCheckCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSObjectSynchronizerJnotifyall6FnGHandle_pnGThread__v_; +text: .text%__1cIAndINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cTGeneratePairingInfoRpossible_gc_point6MpnOBytecodeStream__i_: ciMethod.o; +text: .text%__1cQandL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRegionNodeOis_block_start6kM_i_: loopnode.o; +text: .text%__1cHNTarjanICOMPRESS6M_v_; +text: .text%__1cNRelocIteratorTlocs_and_index_size6Fii_i_; +text: .text%__1cQsubL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQStackFrameStream2t6MpnKJavaThread_i_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: compile.o; +text: .text%__1cLClassLoaderLadd_package6Fpkci_i_; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: loopnode.o; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopnode.o; +text: .text%__1cOcompI_iRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: loopnode.o; +text: .text%__1cLklassItableTcompute_itable_size6FnOobjArrayHandle__i_; +text: .text%__1cHTypePtrCeq6kMpknEType__i_; +text: .text%__1cQandI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIXorINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cRmethodDataOopDescLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cSCompareAndSwapNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cRcompL_reg_conNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmembar_acquireNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPindOffset13OperFclone6kM_pnIMachOper__; +text: .text%__1cICallNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFframeZinterpreter_frame_set_bcx6Mi_v_; +text: .text%__1cJloadCNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodJcode_size6kM_i_: nmethod.o; +text: .text%__1cMnegF_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: loopnode.o; +text: .text%__1cLstoreI0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTOopMapForCacheEntryZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cPciObjArrayKlassSis_obj_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cSaddL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQshrL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNSharedRuntimebKexception_handler_for_return_address6FpC_1_; +text: .text%__1cILoopNodeHsize_of6kM_I_: classes.o; +text: .text%__1cHMatcherLfind_shared6MpnENode__v_; +text: .text%__1cJStartNodeHsize_of6kM_I_; +text: .text%__1cHMatcherFxform6MpnENode_i_2_; +text: .text%__1cEDict2t6MpFpkv2_ipF2_ipnFArena_i_v_; +text: .text%__1cRInterpretedRFrameKtop_vframe6kM_pnKjavaVFrame__: rframe.o; +text: .text%__1cQmodI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRinterpretedVFrameDbci6kM_i_; +text: .text%__1cRreturn_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cQmodI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarI_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseKdo_put_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cIAndINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMnegF_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNIdealLoopTreeOpolicy_peeling6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeUiteration_split_impl6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cNIdealLoopTreebBpolicy_do_remove_empty_loop6MpnOPhaseIdealLoop__i_; +text: .text%__1cQsubL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAndINodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cNinstanceKlassbBcall_class_initializer_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNloadRangeNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitRmerge_fast_memory6MpnENode_2i_v_; +text: .text%__1cRcompL_reg_conNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverbHlookup_instance_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cMnegF_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: instanceKlass.o; +text: .text%__1cNSharedRuntimebWnative_method_throw_unsatisfied_link_error_entry6F_pC_; +text: .text%__1cTStackWalkCompPolicyYmethod_back_branch_event6MnMmethodHandle_iipnGThread__v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlass.o; +text: .text%__1cbCCompiledCodeSafepointHandlerYcaller_must_gc_arguments6kM_i_: safepoint.o; +text: .text%__1cUThreadSafepointStateYcaller_must_gc_arguments6kM_i_; +text: .text%__1cRCompilationPolicybJreset_counter_for_back_branch_event6MnMmethodHandle__v_; +text: .text%__1cNSafepointBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_; +text: .text%__1cOMethodLivenessQcompute_liveness6M_v_; +text: .text%__1cOMethodLiveness2t6MpnFArena_pnIciMethod__v_; +text: .text%__1cOMethodLivenessNinit_gen_kill6M_v_; +text: .text%__1cOMethodLivenessSpropagate_liveness6M_v_; +text: .text%__1cOMethodLivenessRinit_basic_blocks6M_v_; +text: .text%__1cIGraphKitHopt_iff6MpnENode_2_2_; +text: .text%__1cLCastP2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLRShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cUcompU_iReg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJTimeStampGupdate6M_v_; +text: .text%__1cRmethodDataOopDescJis_mature6kM_i_; +text: .text%__1cRmethodDataOopDescKmileage_of6FpnNmethodOopDesc__i_; +text: .text%__1cWconstantPoolCacheKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cMMachCallNodeMreturns_long6kM_i_; +text: .text%__1cIGraphKitOhas_ex_handler6M_i_; +text: .text%__1cMloadConDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJStartNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cFParseQarray_addressing6MnJBasicType_ippknEType__pnENode__; +text: .text%__1cNloadConP0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReturnNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPCountedLoopNodeHsize_of6kM_I_: classes.o; +text: .text%__1cIProjNodeJideal_reg6kM_I_; +text: .text%__1cQaddI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQcmovI_reg_ltNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQcmovI_reg_gtNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRMachSafePointNodeLis_MachCall6M_pnMMachCallNode__: ad_sparc_misc.o; +text: .text%__1cSsafePoint_pollNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJcmpOpOperFequal6kM_i_: ad_sparc_clone.o; +text: .text%__1cHCompilebAvarargs_C_out_slots_killed6kM_I_; +text: .text%__1cXJNI_ArgumentPusherVaArgHiterate6MX_v_: jni.o; +text: .text%__1cbBjava_lang_ref_SoftReferenceFclock6F_x_; +text: .text%__1cTMachCallRuntimeNodeSis_MachCallRuntime6M_p0_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopQset_subtree_ctrl6MpnENode__v_; +text: .text%__1cFciEnvUis_unresolved_string6kMpnPciInstanceKlass_i_i_; +text: .text%__1cQciByteCodeStreamUis_unresolved_string6kM_i_; +text: .text%__1cWstatic_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cNflagsRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cZCallDynamicJavaDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerOcas_under_lock6MpnMRegisterImpl_22pCi_v_; +text: .text%__1cYciExceptionHandlerStreamPcount_remaining6M_i_; +text: .text%__1cFParseXcatch_inline_exceptions6MpnNSafePointNode__v_; +text: .text%__1cRconstantPoolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNobjArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cTmembar_CPUOrderNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKcmpOpUOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%JVM_GetFieldIxModifiers; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: connode.o; +text: .text%__1cRScavengeRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cRScavengeRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cQmulL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cILoopNode2t6MpnENode_2_v_; +text: .text%JVM_IsConstructorIx; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__: classes.o; +text: .text%__1cPJavaCallWrapperHoops_do6MpnKOopClosure__v_; +text: .text%__1cFframeNoops_entry_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cSaddP_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHMatcherUc_calling_convention6FpnLRegPair_I_v_; +text: .text%__1cPCallRuntimeNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cFKlassTarray_klass_or_null6M_pnMklassOopDesc__; +text: .text%__1cHnmethodQis_native_method6kM_i_: nmethod.o; +text: .text%__1cPCountedLoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__: classes.o; +text: .text%__1cKNativeCallXset_destination_mt_safe6MpC_v_; +text: .text%__1cUBytecode_tableswitchOdest_offset_at6kMi_i_; +text: .text%__1cPciObjArrayKlassNelement_klass6M_pnHciKlass__; +text: .text%__1cLRShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKg1RegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNSignatureInfoHdo_long6M_v_: frame.o; +text: .text%__1cSvframeStreamCommonZsecurity_get_caller_frame6Mi_v_; +text: .text%__1cUjni_invoke_nonstatic6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cIciObjectMhas_encoding6M_i_; +text: .text%__1cNinstanceKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cIAndINodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cIAndINodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cTMachCallRuntimeNodePret_addr_offset6M_i_; +text: .text%__1cLConvL2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKo0RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIregDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cVshrL_reg_imm6_L2INodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescbGupdate_compiled_code_entry_point6Mi_v_; +text: .text%__1cNmethodOopDescTverified_code_entry6M_pC_; +text: .text%__1cNSharedRuntimeXfind_callee_info_helper6FpnKJavaThread_rnMvframeStream_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cPBytecode_invokeFindex6kM_i_; +text: .text%__1cLRethrowNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKJNIHandlesOdestroy_global6FpnI_jobject_i_v_; +text: .text%__1cSPSKeepAliveClosureGdo_oop6MppnHoopDesc__v_: psScavenge.o; +text: .text%__1cFParseFBlockRsuccessor_for_bci6Mi_p1_; +text: .text%__1cVPreserveExceptionMark2T6M_v_; +text: .text%__1cVPreserveExceptionMark2t6MrpnGThread__v_; +text: .text%__1cHRetNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHMatcherQpost_fast_unlock6FpknENode__i_; +text: .text%__1cIRootNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cMoutputStreamFprint6MpkcE_v_; +text: .text%__1cOGenerateOopMapKcopy_state6MpnNCellTypeState_2_v_; +text: .text%__1cHAddNodeGis_Add6kM_pk0_: classes.o; +text: .text%__1cHCompileQsync_stack_slots6kM_i_; +text: .text%__1cJLoadCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHMulNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJLoadFNodeGOpcode6kM_i_; +text: .text%__1cNSignatureInfoHdo_long6M_v_: bytecode.o; +text: .text%__1cHPhiNodeDcmp6kMrknENode__I_; +text: .text%__1cKciTypeFlowLStateVectorMdo_putstatic6MpnQciByteCodeStream__v_; +text: .text%__1cHOrINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: objArrayKlass.o; +text: .text%__1cKciTypeFlowLStateVectorGdo_ldc6MpnQciByteCodeStream__v_; +text: .text%__1cSTailCalljmpIndNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKMemoryPoolHoops_do6MpnKOopClosure__v_; +text: .text%__1cKstoreINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRloadConP_pollNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPClassFileStreamGget_u86MpnGThread__X_; +text: .text%__1cQxorI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLMachNopNodeMideal_Opcode6kM_i_: ad_sparc.o; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstance.o; +text: .text%__1cLMachNopNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMFastLockNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopopts.o; +text: .text%__1cOPhaseIdealLoopNreorg_offsets6MpnNIdealLoopTree__v_; +text: .text%__1cSmembar_releaseNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMVM_OperationPevaluation_mode6kM_n0AEMode__: vm_operations.o; +text: .text%__1cRshrL_reg_imm6NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse3.o; +text: .text%__1cNmethodOopDescVset_signature_handler6MpC_v_; +text: .text%__1cQLRUMaxHeapPolicyWshould_clear_reference6MpnHoopDesc__i_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJtimestamp6FpnHoopDesc__x_; +text: .text%__1cPcompP_iRegPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSxorI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopRsplit_thru_region6MpnENode_2_2_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: output.o; +text: .text%__1cIAndLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cbAPSEvacuateFollowersClosureHdo_void6M_v_: psScavenge.o; +text: .text%jni_ExceptionCheck: jni.o; +text: .text%__1cIAndLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cJCodeCacheMfind_nmethod6Fpv_pnHnmethod__; +text: .text%__1cOPhaseIdealLoopMdominated_by6MpnENode_2_v_; +text: .text%__1cQshlI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseNthrow_to_exit6MpnNSafePointNode__v_; +text: .text%__1cWCallLeafNoFPDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvL2I_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLCounterDataOis_CounterData6M_i_: methodDataOop.o; +text: .text%__1cJloadPNodeFclone6kM_pnENode__; +text: .text%__1cQinstanceRefKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cYDebugInformationRecorderNappend_handle6MpnI_jobject__i_; +text: .text%__1cVConstantOopWriteValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cJVectorSetGslamin6Mrk0_v_; +text: .text%JVM_Clone; +text: .text%__1cRAbstractAssemblerFflush6M_v_; +text: .text%__1cYinlineCallClearArrayNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPciInstanceKlassTis_java_lang_Object6M_i_; +text: .text%__1cITypeLongFxdual6kM_pknEType__; +text: .text%__1cSmembar_releaseNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIJumpDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cRshrL_reg_imm6NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledIC2t6MpnKNativeCall__v_; +text: .text%__1cOstackSlotLOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cURethrowExceptionNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshrL_reg_imm6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXmembar_release_lockNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLOpaque2NodeEhash6kM_I_; +text: .text%__1cJloadFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUcompU_iReg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYSurvivorMutableSpacePoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cNSharedRuntimeOresolve_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cUEdenMutableSpacePoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cNSharedRuntimeSresolve_sub_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cYSurvivorMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cLOptoRuntimeJstub_name6FpC_pkc_; +text: .text%__1cLstoreP0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHOrINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cICmpLNodeDsub6kMpknEType_3_3_; +text: .text%__1cHPhiNodeKmake_blank6FpnENode_2_p0_; +text: .text%__1cXJNI_ArgumentPusherVaArgIget_long6M_v_: jni.o; +text: .text%__1cHMatcherNfind_receiver6Fi_nFVMRegEName__; +text: .text%__1cIMulINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cQandI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMachEpilogNodeNis_MachEpilog6M_p0_: ad_sparc.o; +text: .text%__1cOMachEpilogNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIimmPOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConPNodeFclone6kM_pnENode__; +text: .text%__1cNSCMemProjNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cFStateM_sub_Op_SubI6MpknENode__v_; +text: .text%__1cFframeRretrieve_receiver6MpnLRegisterMap__pnHoopDesc__; +text: .text%__1cPBytecode_invokeNstatic_target6MpnGThread__nMmethodHandle__; +text: .text%__1cNloadKlassNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRMachSafePointNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cMTailCallNodeKmatch_edge6kMI_I_; +text: .text%jni_NewObject: jni.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: assembler_sparc.o; +text: .text%__1cIPhaseIFGYCompute_Effective_Degree6M_v_; +text: .text%__1cHMemNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cXmembar_release_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cNobjArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cJNode_ListEyank6MpnENode__v_; +text: .text%__1cMPhaseChaitinISimplify6M_v_; +text: .text%__1cNIdealLoopTreeIset_nest6MI_i_; +text: .text%__1cQcmovI_reg_gtNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIMulLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cMStartOSRNodeGOpcode6kM_i_; +text: .text%__1cSCallLeafDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cLcmpD_ccNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJLoadSNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJcmpOpOperEless6kM_i_: ad_sparc_clone.o; +text: .text%__1cKciTypeFlowPflow_exceptions6MpnNGrowableArray4Cpn0AFBlock___pnNGrowableArray4CpnPciInstanceKlass___pn0ALStateVector__v_; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__: ad_sparc_misc.o; +text: .text%__1cKType_ArrayEgrow6MI_v_; +text: .text%__1cSaddL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConP0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKCompiledICWis_in_transition_state6kM_i_; +text: .text%__1cXmembar_release_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPconvF2D_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadCNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJScopeDescGis_top6kM_i_; +text: .text%__1cRshrL_reg_imm6NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKciTypeFlowLStateVectorOmeet_exception6MpnPciInstanceKlass_pk1_i_; +text: .text%__1cMURShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNloadConL0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cVshrL_reg_imm6_L2INodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSMemBarVolatileNodeGOpcode6kM_i_; +text: .text%__1cLstoreB0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRshrI_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: thread.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: onStackReplacement.o; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc__pc_; +text: .text%__1cRcmpFastUnlockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNSafePointNodeLpop_monitor6M_v_; +text: .text%__1cMPhaseChaitinVfind_base_for_derived6MppnENode_2rI_2_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_exit_Type6F_pknITypeFunc__; +text: .text%__1cOstackSlotIOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cIGraphKitNshared_unlock6MpnENode_2_v_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: connode.o; +text: .text%__1cFStateT_sub_Op_CheckCastPP6MpknENode__v_; +text: .text%__1cTCallDynamicJavaNodeEhash6kM_I_: callnode.o; +text: .text%__1cQsubI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassDLCA6Mp0_1_; +text: .text%__1cKTypeRawPtrEmake6FnHTypePtrDPTR__pk0_; +text: .text%__1cHciKlassVleast_common_ancestor6Mp0_1_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: frame.o; +text: .text%__1cOPhaseIdealLoopPbuild_loop_tree6M_v_; +text: .text%__1cRcompL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeGis_Con6kM_I_: memnode.o; +text: .text%__1cRshlL_reg_imm6NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoop2t6MrnMPhaseIterGVN_pk0i_v_; +text: .text%__1cIciObjectIis_klass6M_i_: ciInstance.o; +text: .text%__1cRloadConP_pollNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQshlL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMindirectOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindirectOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindirectOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cNSafePointNodeMpush_monitor6MpknMFastLockNode__v_; +text: .text%__1cSCallLeafDirectNodeRis_safepoint_node6kM_i_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCallLeafDirectNodeKmethod_set6Mi_v_; +text: .text%__1cIDivINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJLoadBNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSstring_compareNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cFBytesNget_native_u46FpC_I_: bytecodes.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlass.o; +text: .text%__1cRcompL_reg_conNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverUresolve_special_call6FrnICallInfo_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cJloadBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: cfgnode.o; +text: .text%__1cRcompL_reg_conNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: cfgnode.o; +text: .text%__1cPcheckCastPPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: connode.o; +text: .text%__1cOGenerateOopMapGdo_ldc6Mii_v_; +text: .text%__1cJCMoveNodeLis_cmove_id6FpnOPhaseTransform_pnENode_44pnIBoolNode__4_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interp_masm_sparc.o; +text: .text%__1cKTypeAryPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cGOopMapPset_derived_oop6MnHOptoRegEName_ii2_v_; +text: .text%__1cLConvL2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOPhaseIdealLoopKDominators6M_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_late6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopQbuild_loop_early6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cKCompiledICIis_clean6kM_i_; +text: .text%jni_NewGlobalRef: jni.o; +text: .text%__1cTciConstantPoolCache2t6MpnFArena_i_v_; +text: .text%__1cIAndINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cGOopMapQset_callee_saved6MnHOptoRegEName_ii2_v_; +text: .text%__1cYcompareAndSwapL_boolNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRbranchLoopEndNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: loopnode.o; +text: .text%__1cKCompiledICZcompute_monomorphic_entry6FnMmethodHandle_nLKlassHandle_iirnOCompiledICInfo_pnGThread__v_; +text: .text%__1cKtype2basic6FpknEType__nJBasicType__; +text: .text%__1cMPhaseChaitinFSplit6MI_I_; +text: .text%__1cMPhaseChaitinHcompact6M_v_; +text: .text%__1cZPhaseConservativeCoalesce2t6MrnMPhaseChaitin__v_; +text: .text%__1cNIdealLoopTreeMis_loop_exit6kMpnENode_pnOPhaseIdealLoop__2_; +text: .text%__1cMPhaseChaitinZcompress_uf_map_for_nodes6M_v_; +text: .text%__1cZPhaseConservativeCoalesceGverify6M_v_; +text: .text%__1cRcmpFastUnlockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQshlI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerWcompiler_unlock_object6MpnMRegisterImpl_222rnFLabel__v_; +text: .text%__1cXmembar_release_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interpreter_sparc.o; +text: .text%__1cKPSYoungGenNused_in_bytes6kM_I_; +text: .text%__1cOMachEpilogNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKCompiledICSset_to_monomorphic6MrknOCompiledICInfo__v_; +text: .text%__1cLklassItablebFinitialize_itable_for_interface6MpnMklassOopDesc_pnRitableMethodEntry__v_; +text: .text%__1cJloadFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIRootNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cIRootNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHMatcherPprior_fast_lock6FpknENode__i_; +text: .text%__1cJLoadLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSReferenceProcessorbAenqueue_discovered_reflist6MpnHoopDesc_p2_v_; +text: .text%__1cSReferenceProcessorbAprocess_discovered_reflist6MppnHoopDesc_pnPReferencePolicy_i_v_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle__v_; +text: .text%__1cKJavaThreadNreguard_stack6MpC_i_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle_pnGThread__v_; +text: .text%__1cFframeZinterpreter_frame_set_bcp6MpC_v_; +text: .text%__1cIUniverseWis_out_of_memory_error6FnGHandle__i_; +text: .text%JVM_FillInStackTrace; +text: .text%__1cKJavaThreadGactive6F_p0_; +text: .text%__1cKstoreFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodOis_java_method6kM_i_: nmethod.o; +text: .text%__1cMtlsLoadPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_StringOchar_converter6FnGHandle_HHpnGThread__1_; +text: .text%__1cENodeUdepends_only_on_test6kM_i_: connode.o; +text: .text%__1cMVirtualSpaceNreserved_size6kM_I_; +text: .text%__1cICodeHeapMmax_capacity6kM_I_; +text: .text%__1cSbranchCon_longNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cHTypePtrFxmeet6kMpknEType__3_; +text: .text%__1cbBInterpreterCodeletInterfaceRcode_size_to_size6kMi_i_: interpreter.o; +text: .text%__1cbBInterpreterCodeletInterfaceKinitialize6MpnEStub_i_v_: interpreter.o; +text: .text%__1cNflagsRegFOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cIMinINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFParseWensure_phis_everywhere6M_v_; +text: .text%__1cVshrL_reg_imm6_L2INodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadFNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cTDebugInfoReadStream2t6MpknHnmethod_i_v_; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: ad_sparc.o; +text: .text%__1cLRethrowNodeEhash6kM_I_: classes.o; +text: .text%__1cIDivLNodeGOpcode6kM_i_; +text: .text%__1cPlocal_vsnprintf6FpcIpkcpv_i_; +text: .text%__1cNDispatchTableJset_entry6MirnKEntryPoint__v_; +text: .text%__1cNmethodOopDescVclear_native_function6M_v_; +text: .text%__1cOloadConL13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerKverify_oop6MpnMRegisterImpl_pkc_v_; +text: .text%__1cNloadConL0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%jio_snprintf; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: node.o; +text: .text%__1cMloadConINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSSetupItableClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cSmulI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLOptoRuntimebAresolve_opt_virtual_call_C6FpnKJavaThread__pC_; +text: .text%jni_NewLocalRef: jni.o; +text: .text%__1cRsubI_zero_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulDNodeGOpcode6kM_i_; +text: .text%__1cLStrCompNodeGOpcode6kM_i_; +text: .text%__1cJCodeCacheXmark_for_deoptimization6FpnMklassOopDesc__i_; +text: .text%__1cQcmovI_reg_gtNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cURethrowExceptionNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: callnode.o; +text: .text%__1cKstoreBNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserbNparse_classfile_inner_classes_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__H_; +text: .text%__1cKStoreFNodeGOpcode6kM_i_; +text: .text%__1cLConvD2INodeGOpcode6kM_i_; +text: .text%__1cURethrowExceptionNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLPhaseValues2T5B6M_v_; +text: .text%__1cIAddLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKReturnNode2t6MpnENode_2222_v_; +text: .text%__1cMURShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKReturnNodeJideal_reg6kM_I_: classes.o; +text: .text%jni_DeleteGlobalRef: jni.o; +text: .text%__1cVPatchingRelocIteratorIpostpass6M_v_; +text: .text%__1cVPatchingRelocIteratorHprepass6M_v_; +text: .text%__1cKCodeBufferPcopy_relocation6MpnICodeBlob__v_; +text: .text%__1cKCodeBufferJcopy_code6MpnICodeBlob__v_; +text: .text%__1cZnoG3_iRegI_64bit_safeOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNRelocIteratorMcreate_index6FpnKCodeBuffer_pnJrelocInfo_4_4_; +text: .text%__1cRAbstractAssemblerOcode_fill_byte6F_i_; +text: .text%__1cQcmovI_reg_gtNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cICodeBlobWfix_relocation_at_move6Mi_v_; +text: .text%__1cICodeBlob2t6MpkcpnKCodeBuffer_iiipnJOopMapSet_i_v_; +text: .text%__1cIAndLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIAndLNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cJOopMapSet2t6M_v_; +text: .text%__1cNSCMemProjNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%JVM_GetCPMethodModifiers; +text: .text%jni_GetObjectArrayElement: jni.o; +text: .text%__1cOCompilerOraclePshould_break_at6FnMmethodHandle__i_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: memnode.o; +text: .text%__1cFParseKarray_load6MnJBasicType__v_; +text: .text%jni_SetLongField: jni.o; +text: .text%__1cHGCCauseJto_string6Fn0AFCause__pkc_; +text: .text%__1cJOopMapSetHcopy_to6MpC_v_; +text: .text%__1cQjava_lang_ThreadRset_thread_status6FpnHoopDesc_n0AMThreadStatus__v_; +text: .text%__1cJOopMapSetJheap_size6kM_i_; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: classes.o; +text: .text%__1cNSafePointNodeKgrow_stack6MpnIJVMState_I_v_; +text: .text%__1cIJVMState2t6Mi_v_; +text: .text%__1cIAndLNodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interp_masm_sparc.o; +text: .text%__1cIAndLNodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cIAndLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNSignatureInfoJdo_double6M_v_: frame.o; +text: .text%__1cJLoadSNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cNRelocIterator2t6MpnKCodeBuffer_pC3_v_; +text: .text%__1cMMachProjNodeHsize_of6kM_I_: classes.o; +text: .text%__1cTDebugInfoReadStreamLread_handle6M_nGHandle__; +text: .text%__1cOPhaseIdealLoopUsplit_if_with_blocks6MrnJVectorSet_rnKNode_Stack__v_; +text: .text%__1cMtlsLoadPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassPadd_implementor6MpnMklassOopDesc__v_; +text: .text%__1cLOopRecorderIoop_size6M_i_; +text: .text%__1cYDebugInformationRecorderJdata_size6M_i_; +text: .text%__1cHnmethodPscopes_pcs_size6kM_i_: nmethod.o; +text: .text%__1cYDebugInformationRecorderIpcs_size6M_i_; +text: .text%__1cHnmethodQscopes_data_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodJstub_size6kM_i_: nmethod.o; +text: .text%__1cOPhaseIdealLoopOset_early_ctrl6MpnENode__v_; +text: .text%__1cHnmethodKtotal_size6kM_i_; +text: .text%__1cHnmethodOexception_size6kM_i_: nmethod.o; +text: .text%__1cbFunnecessary_membar_volatileNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMloadConLNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cJiRegLOperFclone6kM_pnIMachOper__; +text: .text%__1cFParseNadd_safepoint6M_v_; +text: .text%__1cOPhaseTransform2t6Mp0nFPhaseLPhaseNumber__v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: phaseX.o; +text: .text%__1cLPhaseValues2t6Mp0_v_; +text: .text%__1cQcmovI_reg_ltNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cXPhaseAggressiveCoalesceGverify6M_v_: coalesce.o; +text: .text%__1cHCompilebBregister_library_intrinsics6M_v_; +text: .text%__1cXPhaseAggressiveCoalesceNinsert_copies6MrnHMatcher__v_; +text: .text%__1cNPhaseRegAlloc2t6MIrnIPhaseCFG_rnHMatcher_pF_v_v_; +text: .text%__1cIPhaseCFGJbuild_cfg6M_I_; +text: .text%__1cHCompileEInit6Mi_v_; +text: .text%__1cVExceptionHandlerTable2t6Mi_v_; +text: .text%__1cHMatcherLreturn_addr6kM_nHOptoRegEName__; +text: .text%__1cMPhaseChaitin2t6MIrnIPhaseCFG_rnHMatcher__v_; +text: .text%__1cMPhaseChaitinRRegister_Allocate6M_v_; +text: .text%__1cHCompileTset_cached_top_node6MpnENode__v_; +text: .text%__1cIPhaseCFGQFind_Inner_Loops6M_v_; +text: .text%__1cHMatcherZnumber_of_saved_registers6F_i_; +text: .text%__1cNPhaseRegAllocTpd_preallocate_hook6M_v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: block.o; +text: .text%__1cMPhaseChaitinMreset_uf_map6MI_v_; +text: .text%__1cMPhaseChaitinRbuild_ifg_virtual6M_v_; +text: .text%__1cIPhaseCFGQGlobalCodeMotion6MrnHMatcher_IrnJNode_List__v_; +text: .text%__1cHMatcherTFixup_Save_On_Entry6M_v_; +text: .text%__1cHMatcherPinit_spill_mask6MpnENode__v_; +text: .text%__1cHCompileICode_Gen6M_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: matcher.o; +text: .text%__1cFArena2t6MI_v_; +text: .text%__1cUDebugInfoWriteStream2t6MpnYDebugInformationRecorder_i_v_; +text: .text%__1cHMatcherVinit_first_stack_mask6M_v_; +text: .text%__1cFArenaNmove_contents6Mp0_1_; +text: .text%__1cKCodeBufferGresize6Miiii_v_; +text: .text%__1cFArenaRdestruct_contents6M_v_; +text: .text%__1cIPhaseIFG2t6MpnFArena__v_; +text: .text%__1cFDictIFreset6MpknEDict__v_; +text: .text%__1cHMatcherFmatch6M_v_; +text: .text%__1cHMatcher2t6MrnJNode_List__v_; +text: .text%__1cIPhaseCFGVschedule_pinned_nodes6MrnJVectorSet__v_; +text: .text%__1cIPhaseCFGOschedule_early6MrnJVectorSet_rnJNode_List_rnLBlock_Array__i_; +text: .text%__1cETypeKInitialize6FpnHCompile__v_; +text: .text%__1cIPhaseCFGNschedule_late6MrnJVectorSet_rnJNode_List_rnNGrowableArray4CI___v_; +text: .text%__1cIPhaseCFGYEstimate_Block_Frequency6M_v_; +text: .text%__1cYDebugInformationRecorder2t6MpnLOopRecorder__v_; +text: .text%__1cOCompileWrapper2t6MpnHCompile__v_; +text: .text%__1cIPhaseCFGKDominators6M_v_; +text: .text%__1cIPhaseCFG2t6MpnFArena_pnIRootNode_rnHMatcher__v_; +text: .text%__1cMPhaseChaitinbGstretch_base_pointer_live_ranges6MpnMResourceArea__i_; +text: .text%__1cJPhaseLive2t6MrknIPhaseCFG_rnILRG_List_pnFArena__v_; +text: .text%__1cJPhaseLive2T6M_v_; +text: .text%__1cWemit_exception_handler6FrnKCodeBuffer__v_; +text: .text%__1cHCompileYinit_scratch_buffer_blob6M_v_; +text: .text%__1cKCodeBufferOrelocate_stubs6M_v_; +text: .text%__1cOMachPrologNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMPhaseChaitin2T6M_v_; +text: .text%__1cHMatcherWis_short_branch_offset6Mi_i_; +text: .text%__1cHCompileTFillExceptionTables6MIpI1pnFLabel__v_; +text: .text%__1cMPhaseChaitinbApost_allocate_copy_removal6M_v_; +text: .text%__1cIPhaseCFGLRemoveEmpty6M_v_; +text: .text%__1cHCompileGOutput6M_v_; +text: .text%__1cWImplicitExceptionTableIset_size6MI_v_; +text: .text%__1cHCompileMBuildOopMaps6M_v_; +text: .text%__1cHCompilePneed_stack_bang6kMi_i_; +text: .text%__1cLdo_liveness6FpnNPhaseRegAlloc_pnIPhaseCFG_pnKBlock_List_ipnFArena_pnEDict__v_: buildOopMap.o; +text: .text%__1cGBundlePinitialize_nops6FppnIMachNode__v_; +text: .text%__1cMPhaseChaitinMfixup_spills6M_v_; +text: .text%__1cNPhaseRegAllocPalloc_node_regs6Mi_v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: buildOopMap.o; +text: .text%__1cHCompileLFill_buffer6M_v_; +text: .text%__1cVCallRuntimeDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZCallInterpreterDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSignatureInfoJdo_double6M_v_: bytecode.o; +text: .text%__1cWpoll_return_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cENodeHrm_prec6MI_v_; +text: .text%__1cLcmpD_ccNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cGciTypeMis_classless6kM_i_: ciInstanceKlass.o; +text: .text%__1cIMulLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHRetNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIGraphKitOset_pair_local6MipnENode__v_: parse2.o; +text: .text%__1cRPrivilegedElementKinitialize6MpnMvframeStream_pnHoopDesc_p0pnGThread__v_; +text: .text%JVM_DoPrivileged; +text: .text%__1cOcompiledVFrameGis_top6kM_i_; +text: .text%__1cRsubI_zero_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHRetNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICodeBlobPallocation_size6FpnKCodeBuffer_ii_I_; +text: .text%__1cQaddL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeBlobRis_at_poll_return6MpC_i_; +text: .text%__1cHciKlassMis_interface6M_i_: ciObjArrayKlass.o; +text: .text%__1cIConDNodeGOpcode6kM_i_; +text: .text%__1cObranchConFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cTresource_free_bytes6FpcI_v_; +text: .text%__1cNmethodOopDescbDbuild_interpreter_method_data6FnMmethodHandle_pnGThread__v_; +text: .text%__1cIAddLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRcompL_reg_conNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNMemoryManagerHoops_do6MpnKOopClosure__v_; +text: .text%__1cPconvL2I_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFciEnvKcompile_id6M_I_; +text: .text%__1cPpoll_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cRmethodDataOopDescKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_bytes6FpnNmethodOopDesc__i_; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_words6FpnNmethodOopDesc__i_; +text: .text%__1cIDivINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cILoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPmethodDataKlassIallocate6MnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cKoopFactoryOnew_methodData6FnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cNprefetch2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cILoopNodeHsize_of6kM_I_: loopnode.o; +text: .text%__1cIAndLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKCodeBuffer2t6MpCi_v_; +text: .text%__1cVshrL_reg_imm6_L2INodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nativeInst_sparc.o; +text: .text%__1cIConPNodeEmake6FpC_p0_; +text: .text%__1cIGraphKitNstore_barrier6MpnENode_22_v_; +text: .text%__1cOcmovII_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvL2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: callnode.o; +text: .text%__1cIciMethodRinstructions_size6M_i_; +text: .text%__1cSmulI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovII_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: memnode.o; +text: .text%__1cCosXthread_local_storage_at6Fi_pv_; +text: .text%__1cMindIndexOperNconstant_disp6kM_i_: ad_sparc.o; +text: .text%__1cMindIndexOperOindex_position6kM_i_: ad_sparc.o; +text: .text%__1cMindIndexOperFscale6kM_i_: ad_sparc.o; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: assembler_sparc.o; +text: .text%__1cRAbstractAssemblerbDgenerate_stack_overflow_check6Mi_v_; +text: .text%__1cMindIndexOperNbase_position6kM_i_: ad_sparc.o; +text: .text%__1cNloadKlassNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateR_sub_Op_LoadKlass6MpknENode__v_; +text: .text%__1cGTarjanICOMPRESS6M_v_; +text: .text%__1cKstoreCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKloadUBNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cHciKlassGloader6M_pnHoopDesc__: ciTypeArrayKlass.o; +text: .text%__1cICmpDNodeGOpcode6kM_i_; +text: .text%__1cNloadConL0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulLNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cOPhaseIdealLoopOplace_near_use6kMpnENode__2_; +text: .text%__1cVCallRuntimeDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOloadConL13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cYcompareAndSwapL_boolNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cZCallInterpreterDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLstoreB0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSInterpreterRuntimeOprofile_method6FpnKJavaThread_pC_i_; +text: .text%__1cHMonitorGnotify6M_i_; +text: .text%__1cNIdealLoopTreePiteration_split6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cOloadConL13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMURShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNRelocIteratorEnext6M_i_: compiledIC.o; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cLRShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLOptoRuntimeRmultianewarray1_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cIMachNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLOpaque2NodeLbottom_type6kM_pknEType__: connode.o; +text: .text%__1cGThreadOis_interrupted6Fp0i_i_; +text: .text%__1cSconvI2D_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: subnode.o; +text: .text%__1cUPSGenerationCountersKupdate_all6M_v_: psGenerationCounters.o; +text: .text%__1cQComputeCallStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cKTypeOopPtrSmake_from_constant6FpnIciObject__pk0_; +text: .text%__1cQregP_to_stkPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapHppstore6MpnNCellTypeState_i_v_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cJTimeStampSticks_since_update6kM_x_; +text: .text%__1cJArrayDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cQmodI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlass.o; +text: .text%__1cNmethodOopDescThas_native_function6kM_i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constantPoolKlass.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: loopnode.o; +text: .text%__1cIMulINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cURethrowExceptionNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNmethodOopDescWis_vanilla_constructor6kM_i_; +text: .text%__1cIAddLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQcmovI_reg_ltNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLstoreB0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRshrL_reg_imm6NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIModINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKstoreFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKklassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cJcmpOpOperHgreater6kM_i_: ad_sparc_clone.o; +text: .text%__1cQstkI_to_regFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJimmL0OperJconstantL6kM_x_: ad_sparc_clone.o; +text: .text%__1cFParseJdo_ifnull6MnIBoolTestEmask__v_; +text: .text%__1cQmulD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJimmI0OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cFStateM_sub_Op_ConL6MpknENode__v_; +text: .text%__1cOloadConL13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNObjectMonitorHis_busy6kM_i_; +text: .text%JVM_GetClassNameUTF; +text: .text%__1cKloadUBNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIXorINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cFStateM_sub_Op_AndI6MpknENode__v_; +text: .text%__1cVshrL_reg_imm6_L2INodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: multnode.o; +text: .text%__1cKcmpOpFOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cLRuntimeStubHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cLRuntimeStubbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cNflagsRegFOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cTmembar_volatileNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRshlL_reg_imm6NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_: callnode.o; +text: .text%__1cJloadFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: memnode.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: memnode.o; +text: .text%__1cFStateL_sub_Op_OrI6MpknENode__v_; +text: .text%__1cJCmpL3NodeGOpcode6kM_i_; +text: .text%__1cIciObjectOis_method_data6M_i_: ciInstance.o; +text: .text%__1cIciObjectJis_method6M_i_: ciInstance.o; +text: .text%JVM_FindLoadedClass; +text: .text%__1cLCastP2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIMulLNodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIMulLNodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cRbranchLoopEndNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cVAdaptivePaddedAverageGsample6Mf_v_; +text: .text%__1cMMutableSpaceFclear6M_v_; +text: .text%__1cIConFNodeGOpcode6kM_i_; +text: .text%__1cOClearArrayNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIGraphKitNallocate_heap6MpnENode_222pknITypeFunc_pC22ipknKTypeOopPtr__2_; +text: .text%__1cPciInstanceKlassbBcompute_shared_has_subklass6M_i_; +text: .text%__1cSmembar_acquireNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cMPrefetchNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQmulD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNprefetch2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIModLNodeGOpcode6kM_i_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectIoverride6FpnHoopDesc__C_; +text: .text%__1cKReflectionGinvoke6FnTinstanceKlassHandle_nMmethodHandle_nGHandle_inOobjArrayHandle_nJBasicType_4ipnGThread__pnHoopDesc__; +text: .text%__1cSbranchCon_longNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cQLibraryIntrinsicIgenerate6MpnIJVMState__2_; +text: .text%__1cOLibraryCallKitNtry_to_inline6M_i_; +text: .text%__1cLRShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKTypeRawPtrFxdual6kM_pknEType__; +text: .text%__1cNloadConL0NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_: library_call.o; +text: .text%__1cFTypeFEmake6Ff_pk0_; +text: .text%__1cIimmFOperJconstantF6kM_f_: ad_sparc_clone.o; +text: .text%__1cEUTF8Ounicode_length6Fpkc_i_; +text: .text%__1cCosRcurrent_thread_id6F_i_; +text: .text%__1cUSafepointSynchronizeFblock6FpnKJavaThread__v_; +text: .text%__1cOGenerateOopMapJppdupswap6Mipkc_v_; +text: .text%__1cJttyLockerbCbreak_tty_lock_for_safepoint6Fi_v_; +text: .text%__1cbACallCompiledJavaDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmembar_acquireNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cPorI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIPhaseCFGOinsert_goto_at6MII_v_; +text: .text%__1cENodeHis_Copy6kM_I_: node.o; +text: .text%__1cZCallInterpreterDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: loopTransform.o; +text: .text%__1cITypeLongFwiden6kMpknEType__3_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: loopnode.o; +text: .text%__1cSThreadLocalStoragePget_thread_slow6F_pnGThread__; +text: .text%__1cSxorI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPCallRuntimeNodeGOpcode6kM_i_; +text: .text%__1cJcmpOpOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cLcmpD_ccNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMindIndexOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindIndexOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindIndexOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%JVM_FindClassFromClass; +text: .text%__1cRshrP_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cObranchConFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cLRethrowNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMMutableSpaceKinitialize6MnJMemRegion_i_v_; +text: .text%__1cKstoreLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQshrI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: loopnode.o; +text: .text%__1cbDjava_lang_reflect_ConstructorFclazz6FpnHoopDesc__2_; +text: .text%__1cbDjava_lang_reflect_ConstructorEslot6FpnHoopDesc__i_; +text: .text%__1cbDjava_lang_reflect_ConstructorPparameter_types6FpnHoopDesc__2_; +text: .text%__1cKReflectionSinvoke_constructor6FpnHoopDesc_nOobjArrayHandle_pnGThread__2_; +text: .text%JVM_NewInstanceFromConstructor; +text: .text%__1cNSignatureInfoIdo_float6M_v_: frame.o; +text: .text%__1cObox_handleNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeEhash6kM_I_: classes.o; +text: .text%__1cFParseFBlockMadd_new_path6M_i_; +text: .text%__1cJBytecodesRspecial_length_at6FpC_i_; +text: .text%__1cIimmPOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cMloadConPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: loopnode.o; +text: .text%__1cQsubL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvF2DNodeGOpcode6kM_i_; +text: .text%__1cOstackSlotIOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cLConvI2DNodeGOpcode6kM_i_; +text: .text%__1cSciExceptionHandlerLcatch_klass6M_pnPciInstanceKlass__; +text: .text%__1cMloadConFNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cKcmpOpPOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: interp_masm_sparc.o; +text: .text%__1cLRShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKimmL13OperJconstantL6kM_x_: ad_sparc_clone.o; +text: .text%__1cSTailCalljmpIndNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlassKlass.o; +text: .text%__1cGIfNodeMdominated_by6MpnENode_pnMPhaseIterGVN__v_; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cMURShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRsubI_zero_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcompiledVFrame2t6MpknFframe_pknLRegisterMap_pnKJavaThread_pnJScopeDesc__v_; +text: .text%__1cJScopeDesc2t6MpknHnmethod_i_v_; +text: .text%__1cQshlI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHnmethodNscope_desc_at6MpCi_pnJScopeDesc__; +text: .text%__1cOGenerateOopMapJdo_astore6Mi_v_; +text: .text%__1cbFunnecessary_membar_volatileNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHnmethodUnumber_of_dependents6kM_i_: nmethod.o; +text: .text%__1cSmulI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: multnode.o; +text: .text%__1cIGraphKitOnull_check_oop6MpnKRegionNode_pnENode_i_4_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: compiledIC.o; +text: .text%__1cULinearLeastSquareFitGupdate6Mdd_v_; +text: .text%__1cOoop_RelocationIoop_addr6M_ppnHoopDesc__; +text: .text%__1cKstoreCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnHoopDesc_pnNsymbolOopDesc__i_; +text: .text%__1cMjniIdMapBaseHoops_do6MpnKOopClosure__v_; +text: .text%__1cJcmpOpOperKless_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cOcmovII_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cXmembar_acquire_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPfieldDescriptorUstring_initial_value6kMpnGThread__pnHoopDesc__; +text: .text%__1cOMacroAssemblerEsetx6MxpnMRegisterImpl_2nJrelocInfoJrelocType__v_; +text: .text%__1cMloadConLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMaxINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMindirectOperNconstant_disp6kM_i_: ad_sparc.o; +text: .text%__1cMindirectOperNbase_position6kM_i_: ad_sparc.o; +text: .text%__1cIAddLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMindirectOperFscale6kM_i_: ad_sparc.o; +text: .text%__1cYinternal_word_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cSsubL_reg_reg_2NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescTset_native_function6MpC_v_; +text: .text%jni_NewString: jni.o; +text: .text%__1cLConvL2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQjava_lang_StringXcreate_oop_from_unicode6FpHipnGThread__pnHoopDesc__; +text: .text%__1cQshlI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKoopFactoryNnew_charArray6FpkcpnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cHMatcherQinline_cache_reg6F_nHOptoRegEName__; +text: .text%__1cOcompiledVFrameEcode6kM_pnHnmethod__; +text: .text%__1cIGraphKitMnext_monitor6M_i_; +text: .text%__1cOloadConI13NodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNode2t6Mi_v_; +text: .text%__1cPconvF2D_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLOptoRuntimebBcomplete_monitor_enter_Type6F_pknITypeFunc__; +text: .text%__1cIGraphKitLshared_lock6MpnENode__pnMFastLockNode__; +text: .text%__1cPcmpFastLockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNloadConP0NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cLOptoRuntimeKjbyte_copy6FpW1I_v_; +text: .text%__1cRorI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNSignatureInfoIdo_short6M_v_: frame.o; +text: .text%__1cKcmpOpUOperEless6kM_i_: ad_sparc_clone.o; +text: .text%__1cQaddF_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRLowMemoryDetectorWdetect_after_gc_memory6FpnKMemoryPool__v_; +text: .text%lwp_mutex_init: os_solaris.o; +text: .text%__1cObox_handleNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframeLnmethods_do6M_v_; +text: .text%__1cQjava_lang_ThreadGthread6FpnHoopDesc__pnKJavaThread__; +text: .text%__1cQnotemp_iRegIOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cITemplateIbytecode6kM_nJBytecodesECode__; +text: .text%__1cODataRelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cYinternal_word_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cCosPhint_no_preempt6F_v_; +text: .text%__1cYinternal_word_RelocationJpack_data6M_i_; +text: .text%__1cOcmovII_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUGenericGrowableArrayNraw_appendAll6Mpk0_v_; +text: .text%__1cIMulLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cYinternal_word_RelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cIMulINodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cPciObjectFactory2t6MpnFArena_i_v_; +text: .text%__1cFciEnv2t6MpnHJNIEnv__iii_v_; +text: .text%__1cRsarL_reg_imm6NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFciEnvWget_method_from_handle6MpnI_jobject__pnIciMethod__; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: cfgnode.o; +text: .text%__1cSstring_compareNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFciEnv2T6M_v_; +text: .text%__1cIGraphKitNgen_checkcast6MpnENode_2p2_2_; +text: .text%__1cMMergeMemNodeIadr_type6kM_pknHTypePtr__: memnode.o; +text: .text%__1cbLtransform_int_divide_to_long_multiply6FpnIPhaseGVN_pnENode_i_3_: divnode.o; +text: .text%__1cJcmpOpOperJnot_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cQmulD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cGvframeDtop6kM_p0_; +text: .text%__1cOCompiledRFrameEinit6M_v_; +text: .text%__1cXmembar_acquire_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadSNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cVCallRuntimeDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPcmpFastLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerUcompiler_lock_object6MpnMRegisterImpl_222rnFLabel__v_; +text: .text%__1cQciTypeArrayKlassEmake6FnJBasicType__p0_; +text: .text%__1cIXorINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIGraphKitRgen_subtype_check6MpnENode_2_2_; +text: .text%__1cQregF_to_stkINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNSingletonBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cOMacroAssemblerLsave_thread6MkpnMRegisterImpl__v_; +text: .text%__1cOcmovII_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constMethodKlass.o; +text: .text%__1cLcmpD_ccNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: loopopts.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constMethodKlass.o; +text: .text%__1cMloadConINodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cScheck_phi_clipping6FpnHPhiNode_rpnHConNode_rI45rpnENode_5_i_: cfgnode.o; +text: .text%__1cOcmovII_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: ad_sparc.o; +text: .text%__1cRshlL_reg_imm6NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParseGdo_new6M_v_; +text: .text%__1cZCallDynamicJavaDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQshrI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIimmIOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cQmodI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvI2LNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOPhaseIdealLoopKclone_loop6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%jni_GetObjectClass: jni.o; +text: .text%__1cSxorI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOMacroAssemblerFalign6Mi_v_; +text: .text%__1cRappend_interfaces6FnOobjArrayHandle_ripnPobjArrayOopDesc__v_; +text: .text%__1cbDReferenceProcessorInitializerIis_clean6kM_v_: concurrentMarkSweepGeneration.o; +text: .text%__1cKManagementJtimestamp6F_x_; +text: .text%__1cIPSOldGenPupdate_counters6M_v_; +text: .text%__1cQshrI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNIdealLoopTreeNpolicy_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cFForteNregister_stub6FpkcpC3_v_; +text: .text%__1cNIdealLoopTreeSpolicy_range_check6kMpnOPhaseIdealLoop__i_; +text: .text%__1cFVTuneNregister_stub6FpkcpC3_v_; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: loopTransform.o; +text: .text%__1cNinstanceKlassbFlookup_method_in_all_interfaces6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cTloadL_unalignedNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOMacroAssemblerVreset_last_Java_frame6M_v_; +text: .text%__1cOMacroAssemblerTset_last_Java_frame6MpnMRegisterImpl_2_v_; +text: .text%__1cSstring_compareNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceRefKlass.o; +text: .text%__1cJloadFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cQregF_to_stkINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cINodeHash2t6MpnFArena_I_v_; +text: .text%__1cPconvI2L_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPhaseTransform2t6MpnFArena_nFPhaseLPhaseNumber__v_; +text: .text%__1cLPhaseValues2t6MpnFArena_I_v_; +text: .text%__1cLCodeletMark2t6MrpnZInterpreterMacroAssembler_pkcinJBytecodesECode__v_: interpreter.o; +text: .text%__1cJStubQdDueueGcommit6Mi_v_; +text: .text%__1cJStubQdDueueHrequest6Mi_pnEStub__; +text: .text%__1cGEventsDlog6FpkcE_v_: nmethod.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: typeArrayKlass.o; +text: .text%__1cOcmovII_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOMacroAssemblerKsave_frame6Mi_v_; +text: .text%__1cVshrL_reg_imm6_L2INodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLstoreC0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_and_dispatch6MpnITemplate_nITosState__v_; +text: .text%__1cOPhaseIdealLoopVclone_up_backedge_goo6MpnENode_22_2_; +text: .text%__1cITemplateKinitialize6MinITosState_1pFi_vi_v_; +text: .text%__1cITemplateIgenerate6MpnZInterpreterMacroAssembler__v_; +text: .text%__1cQregI_to_stkINodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%JVM_FindClassFromClassLoader; +text: .text%signalHandler; +text: .text%__1cTtypeArrayKlassKlassIoop_size6kMpnHoopDesc__i_: typeArrayKlassKlass.o; +text: .text%JVM_handle_solaris_signal; +text: .text%__1cRInlineCacheBufferIis_empty6F_i_; +text: .text%__1cUSafepointSynchronizeRis_cleanup_needed6F_i_; +text: .text%__1cQjava_lang_ThreadRget_thread_status6FpnHoopDesc__n0AMThreadStatus__; +text: .text%__1cNSignatureInfoIdo_float6M_v_: bytecode.o; +text: .text%__1cFStateM_sub_Op_AndL6MpknENode__v_; +text: .text%__1cKConv2BNodeGOpcode6kM_i_; +text: .text%__1cSstring_compareNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFframeRis_compiled_frame6kMpi_i_; +text: .text%__1cZInterpreterMacroAssemblerZcheck_and_handle_popframe6MpnMRegisterImpl__v_; +text: .text%JVM_IHashCode; +text: .text%__1cSconvI2D_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStartNodeJideal_reg6kM_I_: callnode.o; +text: .text%__1cJStartNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cOMacroAssemblerbBcheck_and_forward_exception6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cQshlI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_ltNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKDictionarybAis_valid_protection_domain6MiInMsymbolHandle_nGHandle_2_i_; +text: .text%__1cJloadCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQandL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovPP_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMVM_OperationSis_cheap_allocated6kM_i_: vm_operations.o; +text: .text%__1cNSignatureInfoHdo_char6M_v_: frame.o; +text: .text%__1cNCompileBrokerZcompilation_is_prohibited6FnMmethodHandle_i_i_; +text: .text%__1cLPhaseValuesKis_IterGVN6M_pnMPhaseIterGVN__: phaseX.o; +text: .text%__1cMLinkResolverXresolve_invokeinterface6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverbGruntime_resolve_interface_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cKC2CompilerOcompile_method6MpnFciEnv_pnIciMethod_i_v_; +text: .text%JVM_GetClassLoader; +text: .text%__1cNCompileBrokerZinvoke_compiler_on_method6FpnLCompileTask__v_; +text: .text%__1cNCompileBrokerQset_last_compile6FpnOCompilerThread_nMmethodHandle_ii_v_; +text: .text%__1cbCAbstractInterpreterGeneratorQset_entry_points6MnJBytecodesECode__v_; +text: .text%__1cbCAbstractInterpreterGeneratorWset_short_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cJTraceTime2t6MpkciipnMoutputStream__v_; +text: .text%__1cIciMethodJhas_loops6kM_i_; +text: .text%__1cSconvD2I_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cCosRelapsed_frequency6F_x_; +text: .text%__1cFStateP_sub_Op_ConvL2I6MpknENode__v_; +text: .text%__1cIciMethodQbreak_at_execute6M_i_; +text: .text%__1cOPhaseIdealLoopLdo_split_if6MpnENode__v_; +text: .text%__1cLAccessFlagsRatomic_clear_bits6Mi_v_; +text: .text%__1cKScheduling2t6MpnFArena_rnHCompile__v_; +text: .text%__1cKSchedulingMDoScheduling6M_v_; +text: .text%__1cNCompileBrokerScollect_statistics6FpnOCompilerThread_nMelapsedTimer_pnLCompileTask__v_; +text: .text%__1cFciEnvPregister_method6MpnIciMethod_iiiiiipnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler_ii_v_; +text: .text%__1cFciEnvbOcheck_for_system_dictionary_modification6MpnIciMethod__v_; +text: .text%__1cNCompileBrokerYcheck_compilation_result6FnMmethodHandle_iippnHnmethod__i_; +text: .text%__1cZInterpreterMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cSCardTableExtensionbAscavenge_contents_parallel6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager_I_v_; +text: .text%__1cRframe_gc_prologue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeMpd_gc_epilog6M_v_; +text: .text%__1cMelapsedTimerHseconds6kM_d_; +text: .text%__1cJStealTaskEname6M_pc_: psTasks.o; +text: .text%__1cRframe_gc_epilogue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cJStealTask2t6Mi_v_; +text: .text%__1cFframeLgc_epilogue6M_v_; +text: .text%__1cFframeLgc_prologue6M_v_; +text: .text%__1cTOldToYoungRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cJStealTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cTOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cNGCTaskManagerMnote_release6MI_v_; +text: .text%__1cMciMethodDataStrap_recompiled_at6MpnLProfileData__i_; +text: .text%__1cXjava_lang_ref_ReferenceWpending_list_lock_addr6F_ppnHoopDesc__; +text: .text%__1cNmethodOopDescIset_code6MpnHnmethod__v_; +text: .text%__1cQshrI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmembar_acquireNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmembar_acquireNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICodeBlobJcopy_oops6MppnI_jobject_i_v_; +text: .text%__1cYDebugInformationRecorderHcopy_to6MpnHnmethod__v_; +text: .text%__1cVExceptionHandlerTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cHnmethodSresolve_JNIHandles6M_v_; +text: .text%__1cJCodeCacheGcommit6FpnICodeBlob__v_; +text: .text%__1cFVTuneOcreate_nmethod6FpnHnmethod__v_; +text: .text%__1cHnmethodQcopy_scopes_data6MpCi_v_; +text: .text%__1cHnmethod2t6MpnNmethodOopDesc_iiiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__v_; +text: .text%__1cFciEnvVnum_inlined_bytecodes6kM_i_; +text: .text%__1cWImplicitExceptionTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cHnmethodLnew_nmethod6FnMmethodHandle_iiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__p0_; +text: .text%__1cLOopRecorderHcopy_to6MpnICodeBlob__v_; +text: .text%__1cIimmLOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConLNodeFclone6kM_pnENode__; +text: .text%__1cHCompileVfinal_graph_reshaping6M_i_; +text: .text%__1cIciMethodRbuild_method_data6M_v_; +text: .text%__1cHCompileIOptimize6M_v_; +text: .text%__1cHCompileLFinish_Warm6M_v_; +text: .text%__1cbAfinal_graph_reshaping_walk6FrnKNode_Stack_pnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cHCompileLInline_Warm6M_i_; +text: .text%__1cSPhaseRemoveUseless2t6MpnIPhaseGVN_pnQUnique_Node_List__v_; +text: .text%__1cJStartNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKInlineTreeWbuild_inline_tree_root6F_p0_; +text: .text%__1cHCompileRbuild_start_state6MpnJStartNode_pknITypeFunc__pnIJVMState__; +text: .text%__1cIPhaseCCPHanalyze6M_v_; +text: .text%__1cIPhaseCCPMdo_transform6M_v_; +text: .text%__1cIPhaseCCPJtransform6MpnENode__2_; +text: .text%__1cIPhaseCCP2T6M_v_; +text: .text%__1cIPhaseCCP2t6MpnMPhaseIterGVN__v_; +text: .text%__1cHCompileVidentify_useful_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cHCompileUremove_useless_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cQUnique_Node_ListUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cMPhaseIterGVN2t6MpnIPhaseGVN__v_; +text: .text%__1cMPhaseIterGVN2t6Mp0_v_; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod_ii_v_; +text: .text%__1cQmulI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableKtransition6FnITosState_1_v_; +text: .text%__1cHCompileNreturn_values6MpnIJVMState__v_; +text: .text%__1cOcmovII_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cXMachCallInterpreterNodePret_addr_offset6M_i_; +text: .text%__1cOMachEpilogNodeQsafepoint_offset6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_epilog6MnITosState_i_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_prolog6MnITosState_i_v_; +text: .text%__1cYDebugInformationRecorderKadd_oopmap6MiipnGOopMap__v_; +text: .text%__1cIModINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSxorI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFStateP_sub_Op_RShiftI6MpknENode__v_; +text: .text%__1cRsarI_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%lwp_cond_init: os_solaris.o; +text: .text%__1cTmembar_volatileNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadConL0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cbFunnecessary_membar_volatileNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: callnode.o; +text: .text%__1cRshlL_reg_imm6NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXmembar_acquire_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cKPSYoungGenRcapacity_in_bytes6kM_I_; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: interp_masm_sparc.o; +text: .text%__1cMloadConDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cbCCompiledCodeSafepointHandlerbDhandle_polling_page_exception6M_pC_; +text: .text%__1cZInterpreterMacroAssemblerTdispatch_Lbyte_code6MnITosState_ppCii_v_; +text: .text%__1cNSafepointBlobbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cFframebDsender_for_raw_compiled_frame6kMpnLRegisterMap__0_; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_: reg_split.o; +text: .text%__1cUSafepointSynchronizebDhandle_polling_page_exception6FpnKJavaThread__pC_; +text: .text%__1cOloadConI13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSconvI2F_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadSNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQaddF_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAddFNodeGOpcode6kM_i_; +text: .text%__1cObranchConFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowLStateVectorJdo_aaload6MpnQciByteCodeStream__v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cFStateO_sub_Op_Binary6MpknENode__v_; +text: .text%__1cKBinaryNodeGOpcode6kM_i_; +text: .text%__1cNSignatureInfoIdo_short6M_v_: bytecode.o; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_3pnRJavaCallArguments_nGHandle_6_6_; +text: .text%__1cNmethodOopDescWcompute_has_loops_flag6M_i_; +text: .text%__1cQstkI_to_regFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeDcmp6kMrknENode__I_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constMethodKlass.o; +text: .text%__1cSCompiledStaticCallSset_to_interpreted6MnMmethodHandle_pC_v_; +text: .text%__1cSCompiledStaticCallJfind_stub6M_pC_; +text: .text%__1cRNativeMovConstRegIset_data6Mi_v_; +text: .text%__1cFParsebLincrement_and_test_invocation_counter6Mi_v_; +text: .text%__1cJStartNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSsafePoint_pollNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMTailCallNodeGOpcode6kM_i_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii1i_v_; +text: .text%__1cQregP_to_stkPNodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeTprepare_native_call6FpnKJavaThread_pnNmethodOopDesc__v_; +text: .text%__1cHTypePtrFempty6kM_i_; +text: .text%__1cXSignatureHandlerLibraryDadd6FnMmethodHandle__v_; +text: .text%__1cSsafePoint_pollNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNobjArrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cPClassFileParserUverify_constantvalue6MiinSconstantPoolHandle_pnGThread__v_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_next6MnITosState_i_v_; +text: .text%__1cIMulFNodeGOpcode6kM_i_; +text: .text%__1cISubLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQmulD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPconvF2D_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOloadConI13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: exceptions.o; +text: .text%__1cTmembar_CPUOrderNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTmembar_CPUOrderNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodKlass.o; +text: .text%__1cSCompareAndSwapNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNSCMemProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSThreadLocalStorageGthread6F_pnGThread__: assembler_sparc.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodKlass.o; +text: .text%__1cGThreadVset_pending_exception6MpnHoopDesc_pkci_v_; +text: .text%jni_SetByteArrayRegion: jni.o; +text: .text%__1cFMutex2t6Mipkci_v_; +text: .text%__1cQregI_to_stkINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_StringPcreate_from_str6FpkcpnGThread__nGHandle__; +text: .text%__1cSdivL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFStateM_sub_Op_XorI6MpknENode__v_; +text: .text%__1cHTypePtrEmake6FnETypeFTYPES_n0ADPTR_i_pk0_; +text: .text%__1cCosLelapsedTime6F_d_; +text: .text%__1cKScopeValueJread_from6FpnTDebugInfoReadStream__p0_; +text: .text%__1cKPerfMemoryMmark_updated6F_v_; +text: .text%__1cSobjArrayKlassKlassbCallocate_objArray_klass_impl6FnYobjArrayKlassKlassHandle_inLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cIPerfData2t6MnJCounterNS_pkcn0AFUnits_n0ALVariability__v_; +text: .text%__1cKPerfMemoryFalloc6FI_pc_; +text: .text%__1cLStrCompNodeKmatch_edge6kMI_I_; +text: .text%__1cQmulL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cILocation2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cKJavaThreadZsecurity_get_caller_class6Mi_pnMklassOopDesc__; +text: .text%jni_ReleaseStringUTFChars; +text: .text%jni_GetStringUTFChars: jni.o; +text: .text%__1cSobjArrayKlassKlassXallocate_objArray_klass6MinLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cFParseLarray_store6MnJBasicType__v_; +text: .text%__1cSInterpreterRuntimeNquicken_io_cc6FpnKJavaThread__v_; +text: .text%__1cSInterpreterRuntimeXthrow_pending_exception6FpnKJavaThread__v_; +text: .text%__1cSmulI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_IsInterrupted; +text: .text%__1cLLShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNSignatureInfoHdo_char6M_v_: bytecode.o; +text: .text%JVM_FindLibraryEntry; +text: .text%__1cWConstantPoolCacheEntrySset_interface_call6MnMmethodHandle_i_v_; +text: .text%__1cLklassItableUcompute_itable_index6FpnNmethodOopDesc__i_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: assembler_sparc.o; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cRshlL_reg_imm6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHCompile2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cQshlL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: classes.o; +text: .text%__1cHBitDataKis_BitData6M_i_: ciMethodData.o; +text: .text%__1cNLocationValueLis_location6kM_i_: debugInfo.o; +text: .text%__1cPconvF2D_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLRShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSstring_compareNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIMinINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMacroAssemblerRload_ptr_contents6MrnHAddress_pnMRegisterImpl_i_v_: assembler_sparc.o; +text: .text%__1cOMacroAssemblerEstop6Mpkc_v_; +text: .text%__1cKRegionNodeJideal_reg6kM_I_: loopnode.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: loopnode.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: loopnode.o; +text: .text%__1cSconvI2F_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHMatcherOc_return_value6Fii_nLRegPair__; +text: .text%__1cENodeHis_Copy6kM_I_: loopnode.o; +text: .text%__1cKloadUBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKg3RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cSsubL_reg_reg_2NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddP_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cTloadL_unalignedNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cLcmpD_ccNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__: callnode.o; +text: .text%__1cMregD_lowOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cTCallDynamicJavaNodeSis_CallDynamicJava6kM_pk0_: callnode.o; +text: .text%__1cTloadL_unalignedNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLLShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectMset_override6FpnHoopDesc_C_v_; +text: .text%__1cObranchConFNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cObox_handleNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cXJNI_ArgumentPusherVaArgHget_int6M_v_: jni.o; +text: .text%__1cQmodI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRbranchLoopEndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddF_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKcmpOpUOperHgreater6kM_i_: ad_sparc_clone.o; +text: .text%__1cUParallelScavengeHeapEused6kM_I_; +text: .text%__1cIDivINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cVCallRuntimeDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQxorI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLOptoRuntimeOarraycopy_Type6F_pknITypeFunc__; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodKlass.o; +text: .text%__1cWCallLeafNoFPDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTbasictype2arraycopy6FnJBasicType_i_pC_; +text: .text%__1cOLibraryCallKitQinline_arraycopy6M_i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodKlass.o; +text: .text%__1cLcmpD_ccNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWCallLeafNoFPDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLOptoRuntimeSnew_typeArray_Type6F_pknITypeFunc__; +text: .text%__1cJloadINodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitJnew_array6MpnENode_nJBasicType_pknEType_pknMTypeKlassPtr__2_; +text: .text%__1cbBopt_virtual_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFi_vi_v_; +text: .text%__1cIMinINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKarrayKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cTmembar_CPUOrderNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cOClearArrayNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRbranchLoopEndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRbranchLoopEndNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cLMachUEPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTmembar_volatileNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKo1RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cCosTnative_java_library6F_pv_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cSInterpreterRuntimeOmultianewarray6FpnKJavaThread_pi_v_; +text: .text%__1cSxorI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cYinlineCallClearArrayNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYinlineCallClearArrayNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMPhaseChaitinGSelect6M_I_; +text: .text%__1cLOptoRuntimeInew_Type6F_pknITypeFunc__; +text: .text%__1cFParseSjump_switch_ranges6MpnENode_pnLSwitchRange_4i_v_; +text: .text%__1cSbranchCon_longNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cSbranchCon_longNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSbranchCon_longNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosYprint_jni_name_suffix_on6FpnMoutputStream_i_v_; +text: .text%__1cIModINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cCosYprint_jni_name_prefix_on6FpnMoutputStream_i_v_; +text: .text%__1cIGraphKitMnew_instance6MpnPciInstanceKlass__pnENode__; +text: .text%__1cLstoreP0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cMloadConLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseTprofile_switch_case6Mi_v_; +text: .text%__1cOcmovIL_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMNativeLookupGlookup6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cSandI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIimmLOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cFParseOmerge_new_path6Mi_v_; +text: .text%__1cYMachCallCompiledJavaNodePret_addr_offset6M_i_; +text: .text%__1cQregP_to_stkPNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_StringGoffset6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringFvalue6FpnHoopDesc__pnQtypeArrayOopDesc__; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2ipnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cQjava_lang_StringScreate_from_symbol6FnMsymbolHandle_pnGThread__nGHandle__; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: templateTable_sparc.o; +text: .text%__1cHnmethodXinterpreter_entry_point6M_pC_; +text: .text%__1cSmembar_releaseNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQPSIsAliveClosureLdo_object_b6MpnHoopDesc__i_: psScavenge.o; +text: .text%jni_NewByteArray: jni.o; +text: .text%__1cQdivL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_2NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_53pnGThread__v_; +text: .text%__1cUdivL_reg_imm13_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeTnmethod_entry_point6FpnKJavaThread_pnNmethodOopDesc_pnHnmethod__pC_; +text: .text%__1cQaddF_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionarybAvalidate_protection_domain6FnTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cKDictionaryVadd_protection_domain6MiInTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cFParseLdo_newarray6MnJBasicType__v_; +text: .text%__1cPmethodDataKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cMNativeLookupLlookup_base6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cNmethodOopDescKklass_name6kM_pnNsymbolOopDesc__; +text: .text%__1cKRegionNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cSconvI2D_helperNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreP0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHRetNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMciArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cSmembar_releaseNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIPerfLong2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability__v_; +text: .text%__1cKarrayKlassXbase_create_array_klass6FrknKKlass_vtbl_inLKlassHandle_pnGThread__nQarrayKlassHandle__; +text: .text%__1cKarrayKlassbBcomplete_create_array_klass6FnQarrayKlassHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cSTailCalljmpIndNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQcmovI_reg_gtNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cObox_handleNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetMethodIxExceptionTableEntry; +text: .text%__1cSReferenceProcessorZadd_to_discovered_list_mt6MppnHoopDesc_23_v_; +text: .text%__1cIDivINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLstoreP0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQstkI_to_regFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRethrowNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKloadUBNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompileSrethrow_exceptions6MpnIJVMState__v_; +text: .text%__1cURethrowExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLRethrowNode2t6MpnENode_22222_v_; +text: .text%__1cTLoadL_unalignedNodeGOpcode6kM_i_; +text: .text%__1cSmulI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerZget_2_byte_integer_at_bcp6MipnMRegisterImpl_2n0ALsignedOrNot_n0AKsetCCOrNot__v_; +text: .text%__1cQcmovI_reg_gtNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cURethrowExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPfieldDescriptorSlong_initial_value6kM_x_; +text: .text%__1cISubLNodeDsub6kMpknEType_3_3_; +text: .text%__1cPciObjArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cJLoadINodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cQandI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNobjArrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cQmulI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParsePmerge_exception6Mi_v_; +text: .text%__1cLStrCompNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cYinlineCallClearArrayNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNobjArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlass.o; +text: .text%__1cNloadConP0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_ReleaseStringCritical: jni.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: objArrayKlass.o; +text: .text%__1cNobjArrayKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cJCMoveNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%jni_GetStringCritical: jni.o; +text: .text%__1cUmulL_reg_imm13_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHciKlassSsuper_check_offset6M_I_; +text: .text%__1cSTailCalljmpIndNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPciObjArrayKlassGloader6M_pnHoopDesc__: ciObjArrayKlass.o; +text: .text%__1cRorI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregF_to_stkINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeKmethod_set6Mi_v_; +text: .text%__1cWCallLeafNoFPDirectNodeRis_safepoint_node6kM_i_: ad_sparc_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse1.o; +text: .text%__1cIDivLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPICStubInterfaceRcode_size_to_size6kMi_i_: icBuffer.o; +text: .text%__1cPICStubInterfaceKinitialize6MpnEStub_i_v_: icBuffer.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: frame.o; +text: .text%__1cMloadConFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMadjust_check6FpnENode_11iipnMPhaseIterGVN__v_: ifnode.o; +text: .text%__1cJScopeDescGsender6kM_p0_; +text: .text%__1cNloadConP0NodeFclone6kM_pnENode__; +text: .text%__1cSxorI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJimmP0OperFclone6kM_pnIMachOper__; +text: .text%__1cOcompiledVFrameGsender6kM_pnGvframe__; +text: .text%__1cZInterpreterMacroAssemblerDpop6MnITosState__v_; +text: .text%__1cNloadConPCNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_2NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cGThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cQPlaceholderTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cXJvmtiCurrentBreakpointsHoops_do6FpnKOopClosure__v_; +text: .text%__1cKciTypeFlowLStateVectorEtrap6MpnQciByteCodeStream_pnHciKlass_i_v_; +text: .text%__1cNMemoryServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cNThreadServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cQregI_to_stkINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJNIHandlesHoops_do6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryRpreloaded_oops_do6FpnKOopClosure__v_; +text: .text%__1cIregDOperFclone6kM_pnIMachOper__; +text: .text%__1cLJvmtiExportHoops_do6FpnKOopClosure__v_; +text: .text%__1cIUniverseHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cIVMThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cKJNIHandlesMweak_oops_do6FpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSObjectSynchronizerHoops_do6FpnKOopClosure__v_; +text: .text%__1cMFlatProfilerHoops_do6FpnKOopClosure__v_; +text: .text%__1cOPhaseIdealLoopOadd_constraint6MiipnENode_22p23_v_; +text: .text%__1cKManagementHoops_do6FpnKOopClosure__v_; +text: .text%__1cYinternal_word_RelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cIregFOperFclone6kM_pnIMachOper__; +text: .text%__1cJloadDNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cKstoreBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQshlL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQSystemDictionaryRnumber_of_classes6F_i_; +text: .text%__1cOcmovPI_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cObox_handleNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreI0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQciByteCodeStreamPget_klass_index6M_i_; +text: .text%__1cFframeIpatch_pc6MpnGThread_pC_v_; +text: .text%__1cNinstanceKlassQmethod_index_for6kMpnNmethodOopDesc_pnGThread__i_; +text: .text%__1cNmethodOopDescVparameter_annotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cNmethodOopDescLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cRtestI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescbGresolved_checked_exceptions_impl6Fp0pnGThread__nOobjArrayHandle__; +text: .text%__1cFParseMdo_checkcast6M_v_; +text: .text%__1cOCompiledRFrameLis_compiled6kM_i_: rframe.o; +text: .text%__1cOCompiledRFrameKtop_method6kM_nMmethodHandle__: rframe.o; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_: nmethod.o; +text: .text%__1cKReflectionTget_parameter_types6FnMmethodHandle_ippnHoopDesc_pnGThread__nOobjArrayHandle__; +text: .text%__1cRtestI_reg_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcmovIL_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJimmU6OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cHRegMask2t6M_v_: matcher.o; +text: .text%__1cOPhaseIdealLoopJdo_unroll6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cOGenerateOopMapIcopy_cts6MpnNCellTypeState_2_i_; +text: .text%__1cLServiceUtilLvisible_oop6FpnHoopDesc__i_: objectMonitor_solaris.o; +text: .text%__1cNObjectMonitorGEnterI6MpnGThread__v_; +text: .text%__1cIimmIOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConINodeFclone6kM_pnENode__; +text: .text%__1cSmulL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPstoreI_FregNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcmpD_ccNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXMachCallDynamicJavaNodePret_addr_offset6M_i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: ad_sparc_misc.o; +text: .text%__1cNflagsRegFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cXvirtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cXvirtual_call_RelocationJpack_data6M_i_; +text: .text%__1cPPerfDataManagerMcounter_name6Fpkc2_pc_; +text: .text%__1cSconvD2I_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHMonitor2t6Mipkci_v_; +text: .text%__1cIModLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOtypeArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cbBjava_lang_ref_SoftReferenceJset_clock6Fx_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersbBupdate_counters_from_policy6M_v_; +text: .text%__1cXTraceMemoryManagerStats2T6M_v_; +text: .text%__1cQSystemDictionaryHoops_do6FpnKOopClosure__v_; +text: .text%__1cQLRUMaxHeapPolicy2t6M_v_; +text: .text%__1cSReferenceProcessorbDprocess_discovered_references6M_v_; +text: .text%__1cSReferenceProcessorQprocess_phaseJNI6M_v_; +text: .text%__1cSReferenceProcessorbDenqueue_discovered_references6M_i_; +text: .text%__1cSReferenceProcessorbBenqueue_discovered_reflists6MppnHoopDesc__v_; +text: .text%__1cUParallelScavengeHeapQresize_all_tlabs6M_v_; +text: .text%__1cUParallelScavengeHeapPupdate_counters6M_v_; +text: .text%__1cUParallelScavengeHeapTensure_parseability6M_v_; +text: .text%__1cUParallelScavengeHeapOfill_all_tlabs6M_v_; +text: .text%__1cUParallelScavengeHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cVLoaderConstraintTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cXTraceMemoryManagerStats2t6Mi_v_; +text: .text%__1cTDerivedPointerTablePupdate_pointers6F_v_; +text: .text%__1cNCollectedHeapOfill_all_tlabs6M_v_; +text: .text%__1cNCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cNCollectedHeapQresize_all_tlabs6M_v_; +text: .text%__1cMTypeKlassPtrFxmeet6kMpknEType__3_; +text: .text%__1cXjava_lang_ref_ReferenceRpending_list_addr6F_ppnHoopDesc__; +text: .text%__1cKPSYoungGenPupdate_counters6M_v_; +text: .text%__1cWThreadLocalAllocBufferbFaccumulate_statistics_before_gc6F_v_; +text: .text%__1cWThreadLocalAllocBufferQresize_all_tlabs6F_v_; +text: .text%__1cPGCMemoryManagerIgc_begin6M_v_; +text: .text%__1cPGCMemoryManagerGgc_end6M_v_; +text: .text%__1cRLowMemoryDetectorRdetect_low_memory6F_v_; +text: .text%__1cNMemoryServiceStrack_memory_usage6F_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersPupdate_counters6M_v_; +text: .text%__1cTDerivedPointerTableFclear6F_v_; +text: .text%__1cKDictionaryHoops_do6MpnKOopClosure__v_; +text: .text%__1cORuntimeServiceWrecord_safepoint_begin6F_v_; +text: .text%__1cSObjectSynchronizerVdeflate_idle_monitors6F_v_; +text: .text%__1cMCounterDecayFdecay6F_v_; +text: .text%__1cCosbCmake_polling_page_unreadable6F_v_; +text: .text%__1cRInlineCacheBufferUupdate_inline_caches6F_v_; +text: .text%__1cLConvI2FNodeGOpcode6kM_i_; +text: .text%__1cORuntimeServicebDrecord_safepoint_synchronized6F_v_; +text: .text%__1cQaddF_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRshlL_reg_imm6NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUSafepointSynchronizeFbegin6F_v_; +text: .text%__1cKarrayKlassTallocate_arrayArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cONMethodSweeperFsweep6F_v_; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: arrayKlass.o; +text: .text%__1cTAbstractInterpreterRnotice_safepoints6F_v_; +text: .text%__1cCosbAmake_polling_page_readable6F_v_; +text: .text%__1cUSafepointSynchronizeDend6F_v_; +text: .text%__1cOcmovII_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cTAbstractInterpreterRignore_safepoints6F_v_; +text: .text%__1cORuntimeServiceUrecord_safepoint_end6F_v_; +text: .text%__1cJStoreNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cPconvF2D_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKimmU13OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cQshlL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cUcompU_iReg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_GetCallerClass; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: bytecode.o; +text: .text%__1cOcmovPP_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSobjArrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cLstoreC0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadL_unalignedNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cICmpFNodeGOpcode6kM_i_; +text: .text%__1cSdivL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cOstackSlotPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cQregF_to_stkINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJLoadDNodeGOpcode6kM_i_; +text: .text%__1cQmulD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvI2F_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%jni_IsAssignableFrom: jni.o; +text: .text%jni_GetFieldID: jni.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pCii_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: cpCacheKlass.o; +text: .text%__1cJLoadPNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: cpCacheKlass.o; +text: .text%__1cLstoreB0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cZInterpreterMacroAssemblerbAget_cache_and_index_at_bcp6MpnMRegisterImpl_2i_v_; +text: .text%__1cHTypeAryFxdual6kM_pknEType__; +text: .text%__1cMtlsLoadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJEventMark2t6MpkcE_v_: vmThread.o; +text: .text%__1cIVMThreadHexecute6FpnMVM_Operation__v_; +text: .text%__1cCosMget_priority6FkpknGThread_rnOThreadPriority__nIOSReturn__; +text: .text%__1cGThreadMget_priority6Fkpk0_nOThreadPriority__; +text: .text%__1cMVM_OperationIevaluate6M_v_; +text: .text%__1cMVM_OperationSset_calling_thread6MpnGThread_nOThreadPriority__v_; +text: .text%__1cCosTget_native_priority6FkpknGThread_pi_nIOSReturn__; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: interp_masm_sparc.o; +text: .text%__1cQdivL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMnegD_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUParallelScavengeHeapMmem_allocate6MIii_pnIHeapWord__; +text: .text%__1cQregP_to_stkPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_gtNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cWloadConI_x43300000NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cGGCTask2t6Mn0AEKindEkind__v_; +text: .text%__1cNGCTaskManagerVrelease_all_resources6M_v_; +text: .text%__1cUPSAdaptiveSizePolicyWminor_collection_begin6M_v_; +text: .text%__1cLGCTaskQdDueueHenqueue6Mp0_v_; +text: .text%__1cSCardTableExtensionRscavenge_contents6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager__v_; +text: .text%__1cUWaitForBarrierGCTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cNGCTaskManagerIadd_list6MpnLGCTaskQdDueue__v_; +text: .text%__1cHThreadsZcreate_thread_roots_tasks6FpnLGCTaskQdDueue__v_; +text: .text%__1cUWaitForBarrierGCTaskGcreate6F_p0_; +text: .text%__1cUWaitForBarrierGCTaskIdestruct6M_v_; +text: .text%__1cSObjectSynchronizerJfast_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cSObjectSynchronizerKfast_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cUPSAdaptiveSizePolicyUminor_collection_end6MnHGCCauseFCause__v_; +text: .text%__1cSPSPromotionManagerNpost_scavenge6F_v_; +text: .text%__1cNBarrierGCTaskOdo_it_internal6MpnNGCTaskManager_I_v_; +text: .text%__1cNJvmtiGCMarker2T6M_v_; +text: .text%__1cUWaitForBarrierGCTaskHdestroy6Fp0_v_; +text: .text%__1cLGCTaskQdDueueGcreate6F_p0_; +text: .text%__1cSPSPromotionManagerMpre_scavenge6F_v_; +text: .text%__1cZSerialOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cQinstanceRefKlassbKrelease_and_notify_pending_list_lock6FipnJBasicLock__v_; +text: .text%__1cQinstanceRefKlassZacquire_pending_list_lock6FpnJBasicLock__v_; +text: .text%__1cZSerialOldToYoungRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cKPSYoungGenLswap_spaces6M_v_; +text: .text%__1cUParallelScavengeHeapQresize_young_gen6MII_v_; +text: .text%__1cODeoptimizationYtrap_state_is_recompiled6Fi_i_; +text: .text%__1cKPSYoungGenGresize6MII_v_; +text: .text%__1cKPSYoungGenNresize_spaces6MII_v_; +text: .text%__1cUPSAdaptiveSizePolicyPupdate_averages6MiII_v_; +text: .text%__1cUPSAdaptiveSizePolicybPcompute_survivor_space_size_and_threshold6MiiI_i_; +text: .text%__1cSPSPromotionManagerbBvm_thread_promotion_manager6F_p0_; +text: .text%__1cUWaitForBarrierGCTaskIwait_for6M_v_; +text: .text%__1cKPSScavengeQinvoke_no_policy6Fpi_i_; +text: .text%__1cPVM_GC_OperationQgc_count_changed6kM_i_; +text: .text%__1cKPSYoungGenRresize_generation6MII_i_; +text: .text%__1cPVM_GC_OperationNdoit_prologue6M_i_; +text: .text%__1cPVM_GC_OperationNdoit_epilogue6M_v_; +text: .text%__1cNMonitorSupplyHreserve6F_pnHMonitor__; +text: .text%__1cNJvmtiGCMarker2t6Mi_v_; +text: .text%__1cNMonitorSupplyHrelease6FpnHMonitor__v_; +text: .text%__1cUWaitForBarrierGCTaskEname6M_pc_: gcTaskManager.o; +text: .text%__1cTmembar_volatileNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cVLoaderConstraintTableWfind_constrained_klass6MnMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cQjava_lang_ThreadJis_daemon6FpnHoopDesc__i_; +text: .text%__1cVCallRuntimeDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadL_unalignedNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovII_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQComputeCallStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cMLinkResolverbPlinktime_resolve_interface_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cMURShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSCompiledStaticCallIis_clean6kM_i_; +text: .text%__1cSCompiledStaticCallNcompute_entry6FnMmethodHandle_rnOStaticCallInfo__v_; +text: .text%__1cUmulL_reg_imm13_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserbJparse_classfile_signature_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cKstoreBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCompiledStaticCallDset6MrknOStaticCallInfo__v_; +text: .text%__1cOGenerateOopMapXreplace_all_CTS_matches6MnNCellTypeState_1_v_; +text: .text%__1cFframeZinterpreter_frame_set_mdp6MpC_v_; +text: .text%__1cLOptoRuntimeVresolve_static_call_C6FpnKJavaThread__pC_; +text: .text%__1cZInterpreterMacroAssemblerIpush_ptr6MpnMRegisterImpl__v_; +text: .text%__1cHMatcherbAinterpreter_method_oop_reg6F_nHOptoRegEName__; +text: .text%__1cTCallInterpreterNodeSis_CallInterpreter6kM_pk0_: classes.o; +text: .text%__1cTCallInterpreterNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cHCompilebMGenerate_Compiled_To_Interpreter_Graph6MpknITypeFunc_pC_v_; +text: .text%__1cISubLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cZCallInterpreterDirectNodeKmethod_set6Mi_v_; +text: .text%__1cIciMethodRinterpreter_entry6M_pC_; +text: .text%__1cQmulF_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cXMachCallInterpreterNodeWis_MachCallInterpreter6M_p0_: ad_sparc_misc.o; +text: .text%__1cZCallInterpreterDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPconvF2D_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZCallInterpreterDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRcompL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWloadConI_x41f00000NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadBNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%jni_SetBooleanField: jni.o; +text: .text%__1cQciByteCodeStreamFtable6MnJBytecodesECode__2_; +text: .text%__1cKimmL13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cRsarL_reg_imm6NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: sharedRuntime.o; +text: .text%__1cLcmpF_ccNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKloadUBNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRuntimeStubbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cRorI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWResolveOopMapConflictsRpossible_gc_point6MpnOBytecodeStream__i_: rewriter.o; +text: .text%__1cRsarL_reg_imm6NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQmulI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMnegD_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_object6M_v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_i6MpnMRegisterImpl__v_; +text: .text%__1cUdivL_reg_imm13_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserbBcheck_illegal_static_method6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cLLShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOcmovIL_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2D_memNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackJdo_double6M_v_: generateOopMap.o; +text: .text%__1cJloadSNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cRloadConP_pollNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstkL_to_regD_0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNObjectMonitorHRecycle6M_v_; +text: .text%__1cNSharedRuntimeSfind_callee_method6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cQshlL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOMacroAssemblerWstore_unaligned_double6MpnRFloatRegisterImpl_pnMRegisterImpl_i_v_; +text: .text%__1cJloadDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryTresolve_from_stream6FnMsymbolHandle_nGHandle_2pnPClassFileStream_pnGThread__pnMklassOopDesc__; +text: .text%__1cQstkI_to_regFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQregP_to_stkPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerFpop_i6MpnMRegisterImpl__v_; +text: .text%__1cSTailCalljmpIndNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSTailCalljmpIndNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cIMaxINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cLOptoRuntimeThandle_wrong_method6FpnKJavaThread__pC_; +text: .text%__1cOMacroAssemblerUstore_unaligned_long6MpnMRegisterImpl_2i_v_; +text: .text%__1cSmulL_reg_reg_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNSharedRuntimeTreresolve_call_site6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cOLibraryCallKitRinline_unsafe_CAS6MnJBasicType__i_; +text: .text%__1cTCompareAndSwapLNode2t6MpnENode_2222_v_; +text: .text%__1cYcompareAndSwapL_boolNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCompareAndSwapNode2t6MpnENode_2222_v_; +text: .text%__1cNSCMemProjNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cYcompareAndSwapL_boolNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUPSAdaptiveSizePolicyOshould_full_GC6MI_i_; +text: .text%__1cIPSOldGenMmax_gen_size6M_I_: psOldGen.o; +text: .text%__1cUPSAdaptiveSizePolicyQdecaying_gc_cost6kM_d_; +text: .text%__1cUPSAdaptiveSizePolicybDcompute_generation_free_space6MIIIIIIIi_v_; +text: .text%__1cSmulL_reg_reg_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKPSScavengeGinvoke6Fpi_v_; +text: .text%__1cUPSAdaptiveSizePolicyVadjust_for_throughput6MipI1_v_; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcipnNsymbolOopDesc_4_v_; +text: .text%__1cSsubL_reg_reg_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUPSAdaptiveSizePolicyZdecay_supplemental_growth6Mi_v_; +text: .text%__1cSdivL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cUParallelScavengeHeapTfailed_mem_allocate6MpiIii_pnIHeapWord__; +text: .text%__1cbDVM_ParallelGCFailedAllocation2t6MIiiI_v_; +text: .text%__1cbDVM_ParallelGCFailedAllocationEdoit6M_v_; +text: .text%__1cbDVM_ParallelGCFailedAllocationEname6kM_pkc_: vm_operations.o; +text: .text%__1cQaddL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPadd_derived_oop6FppnHoopDesc_2_v_: oopMap.o; +text: .text%__1cMregD_lowOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cHOrINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cOMethodLivenessKBasicBlockFsplit6Mi_p1_; +text: .text%__1cOcmovII_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseScreate_jump_tables6MpnENode_pnLSwitchRange_4_i_; +text: .text%__1cENodeEgetd6kM_d_; +text: .text%__1cZInterpreterMacroAssemblerKverify_oop6MpnMRegisterImpl_nITosState__v_; +text: .text%__1cOcmovIL_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTAbstractInterpreterSBasicType_as_index6FnJBasicType__i_; +text: .text%__1cZInterpreterMacroAssemblerGpush_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cIciObject2t6MpnHciKlass__v_; +text: .text%__1cRtestI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSxorI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPjava_lang_ClassQprimitive_mirror6FnJBasicType__pnHoopDesc__; +text: .text%__1cKExceptionsL_throw_args6FpnGThread_pkcinMsymbolHandle_5pnRJavaCallArguments__v_; +text: .text%__1cKstoreFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHMatcherXinterpreter_arg_ptr_reg6F_nHOptoRegEName__; +text: .text%__1cPstoreI_FregNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCMovePNodeGOpcode6kM_i_; +text: .text%__1cLstoreC0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKciTypeFlowOsplit_range_at6Mi_pn0AFRange__; +text: .text%__1cNObjectMonitorEwait6MxipnGThread__v_; +text: .text%__1cSmulI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%JVM_MonitorWait; +text: .text%__1cSObjectSynchronizerEwait6FnGHandle_xpnGThread__v_; +text: .text%__1cNloadConPCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIAddLNodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cHciKlass2t6MpnIciSymbol_p0_v_; +text: .text%__1cGciType2t6MpnHciKlass__v_; +text: .text%__1cQshlI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQdivD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciObjectUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cFParseSjump_if_false_fork6MpnGIfNode_ii_v_; +text: .text%__1cNloadConL0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHBoxNodeGOpcode6kM_i_; +text: .text%__1cRshrL_reg_imm6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMflagsRegOperFclone6kM_pnIMachOper__; +text: .text%__1cSconvI2F_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregF_to_stkINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cbFunnecessary_membar_volatileNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUciInstanceKlassKlassEmake6F_p0_; +text: .text%__1cENode2t6Mp0111111_v_; +text: .text%__1cIDivLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cZInterpreterMacroAssemblerGpush_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cPstoreI_FregNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseRarray_store_check6M_v_; +text: .text%__1cQsubF_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIciSymbolHbyte_at6Mi_i_; +text: .text%__1cKCompiledICSset_ic_destination6MpC_v_; +text: .text%__1cbDreorder_based_on_method_index6FpnPobjArrayOopDesc_1ppnHoopDesc__v_: methodOop.o; +text: .text%__1cQaddD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cCosTset_native_priority6FpnGThread_i_nIOSReturn__; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongVariable__; +text: .text%__1cQshlI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQset_lwp_priority6Fiii_i_; +text: .text%__1cSstkL_to_regD_0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_StringTcreate_oop_from_str6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cbCAbstractInterpreterGeneratorVset_vtos_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cQregI_to_stkINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%jni_NewStringUTF: jni.o; +text: .text%__1cZInterpreterMacroAssemblerGpush_l6MpnMRegisterImpl__v_; +text: .text%__1cQsubI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cZInterpreterMacroAssemblerXget_constant_pool_cache6MpnMRegisterImpl__v_; +text: .text%__1cSbranchCon_longNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_ref_ReferenceOset_discovered6FpnHoopDesc_2_v_; +text: .text%__1cKcmpOpUOperKless_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cMLinkResolverUresolve_virtual_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cPciInstanceKlassNloader_handle6M_pnI_jobject__; +text: .text%__1cPciInstanceKlassYprotection_domain_handle6M_pnI_jobject__; +text: .text%__1cUParallelScavengeHeapIcapacity6kM_I_; +text: .text%__1cNmethodOopDescKjmethod_id6M_pnK_jmethodID__; +text: .text%__1cSsubL_reg_reg_2NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQaddD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNget_method_id6FpnHJNIEnv__pnH_jclass_pkc5ipnGThread__pnK_jmethodID__: jni.o; +text: .text%__1cMjniIdSupportNto_jmethod_id6FpnNmethodOopDesc__pnK_jmethodID__; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceRefKlass.o; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: cfgnode.o; +text: .text%JVM_DefineClassWithSource; +text: .text%__1cLstoreF0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadINodeFclone6kM_pnENode__; +text: .text%JVM_SetClassSigners; +text: .text%__1cQdivL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cXconvI2D_regDHi_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledICMset_to_clean6M_v_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cSandL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRbranchLoopEndNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cLRShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNSingletonBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cJMarkSweepSFollowStackClosureHdo_void6M_v_: markSweep.o; +text: .text%__1cFParseXfetch_interpreter_state6MipknEType_pnENode__5_; +text: .text%__1cFParseWcheck_interpreter_type6MpnENode_pknEType_rpnNSafePointNode__2_; +text: .text%__1cOcmovPP_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSThreadLocalStorageSset_thread_in_slot6FpnGThread__v_; +text: .text%get_thread; +text: .text%__1cKstoreCNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSThreadLocalStoragebBget_thread_via_cache_slowly6FIi_pnGThread__; +text: .text%__1cSThreadLocalStorageKset_thread6FpnGThread__v_; +text: .text%jni_CallIntMethod: jni.o; +text: .text%__1cSThreadLocalStorageNpd_set_thread6FpnGThread__v_; +text: .text%__1cKloadUBNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregL_to_stkLNodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cUCallCompiledJavaNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cSconvD2I_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHCompilebMGenerate_Interpreter_To_Compiled_Graph6MpknITypeFunc__v_; +text: .text%__1cbACallCompiledJavaDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbACallCompiledJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSconvD2I_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cbACallCompiledJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cIMulDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cbACallCompiledJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_return_entry_for6MnITosState_i_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_deopt_entry_for6MnITosState_i_pC_; +text: .text%__1cSaddP_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIAddDNodeGOpcode6kM_i_; +text: .text%__1cOcmovPP_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvI2D_helperNodeFclone6kM_pnENode__; +text: .text%__1cOloadI_fregNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHCompileRmake_vm_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cOloadI_fregNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCountedLoopEndNode2t6MpnENode_2ff_v_; +text: .text%__1cQmulD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosJyield_all6Fi_v_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: vmThread.o; +text: .text%__1cKstoreLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPstoreI_FregNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFTypeDJsingleton6kM_i_; +text: .text%__1cLstoreC0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassMethodsCount; +text: .text%__1cKstoreINodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%JVM_GetClassFieldsCount; +text: .text%__1cLconvI2BNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_ThreadMis_stillborn6FpnHoopDesc__i_; +text: .text%__1cRorI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassCPEntriesCount; +text: .text%JVM_GetClassCPTypes; +text: .text%__1cGEventsDlog6FpkcE_v_: thread.o; +text: .text%__1cHciKlassMis_interface6M_i_: ciTypeArrayKlass.o; +text: .text%__1cQmulI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerKverify_FPU6Mipkc_v_; +text: .text%__1cVinline_cache_regPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cbCfind_class_from_class_loader6FpnHJNIEnv__nMsymbolHandle_CnGHandle_3CpnGThread__pnH_jclass__; +text: .text%__1cQjava_lang_ThreadKset_thread6FpnHoopDesc_pnKJavaThread__v_; +text: .text%__1cIAddFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPconvF2D_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregI_to_stkINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cbBinitialize_itable_for_klass6FpnMklassOopDesc__v_; +text: .text%__1cTloadL_unalignedNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_5pnGThread__v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interpreterRT_sparc.o; +text: .text%__1cXSignatureHandlerLibraryOpd_set_handler6FpC_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIgenerate6MX_v_; +text: .text%JVM_IsPrimitiveClass; +text: .text%__1cJimmU6OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cOPhaseIdealLoopUpeeled_dom_test_elim6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cIDivDNodeGOpcode6kM_i_; +text: .text%__1cObox_handleNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRorI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: cfgnode.o; +text: .text%__1cKloadUBNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cTmembar_volatileNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_reg_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLStringTableJbasic_add6MinGHandle_pHiIpnGThread__pnHoopDesc__; +text: .text%__1cIModLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cXconvI2D_regDHi_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%jni_FindClass: jni.o; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cOMacroAssemblerOstore_argument6MpnMRegisterImpl_rnIArgument__v_: interpreterRT_sparc.o; +text: .text%__1cFParseHdo_irem6M_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorThas_signature_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorVhas_annotations_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorbFhas_parameter_annotations_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorGcreate6FpnGThread__nGHandle__; +text: .text%__1cGThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cGThreadVis_jvmti_agent_thread6kM_i_: thread.o; +text: .text%__1cKReflectionPnew_constructor6FnMmethodHandle_pnGThread__pnHoopDesc__; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: machnode.o; +text: .text%__1cNloadConP0NodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarL_reg_imm6NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovII_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSdivL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTloadL_unalignedNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSharedRuntimeDd2l6Fd_x_; +text: .text%__1cJStubQdDueueRrequest_committed6Mi_pnEStub__; +text: .text%__1cRInlineCacheBufferRic_stub_code_size6F_i_; +text: .text%__1cFStateP_sub_Op_RShiftL6MpknENode__v_; +text: .text%__1cPICStubInterfaceEsize6kMpnEStub__i_: icBuffer.o; +text: .text%__1cPICStubInterfaceIfinalize6MpnEStub__v_: icBuffer.o; +text: .text%__1cObox_handleNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cQshrI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapOdo_monitorexit6Mi_v_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cQregI_to_stkINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRorI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOGenerateOopMapLmonitor_pop6M_nNCellTypeState__; +text: .text%__1cMregD_lowOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cLConvD2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSconvI2F_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHMonitor2T6M_v_; +text: .text%__1cFTypeDFxmeet6kMpknEType__3_; +text: .text%__1cFMutex2T6M_v_; +text: .text%__1cRtestI_reg_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%lwp_cond_destroy: os_solaris.o; +text: .text%lwp_mutex_destroy: os_solaris.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: multnode.o; +text: .text%__1cQdivI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cVcompiledICHolderKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLPhaseValuesHlongcon6Mx_pnIConLNode__; +text: .text%__1cQregP_to_stkPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQstkI_to_regFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQregI_to_stkINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQRelocationHolderEplus6kMi_0_; +text: .text%__1cUPSMarkSweepDecoratorHcompact6Mi_v_; +text: .text%__1cUPSMarkSweepDecoratorPadjust_pointers6M_v_; +text: .text%__1cUPSMarkSweepDecoratorKprecompact6M_v_; +text: .text%__1cTloadL_unalignedNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_ThreadLthreadGroup6FpnHoopDesc__2_; +text: .text%__1cQregF_to_stkINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompileQgrow_alias_types6M_v_; +text: .text%__1cLLShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cbFunnecessary_membar_volatileNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOcmovII_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNiRegIsafeOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cNinstanceKlassKlink_class6MpnGThread__v_; +text: .text%__1cUmulL_reg_imm13_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKloadUBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHnmethodPis_dependent_on6MpnMklassOopDesc__i_; +text: .text%__1cTloadD_unalignedNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cZInterpreterMacroAssemblerHpop_ptr6MpnMRegisterImpl__v_; +text: .text%__1cQdivD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: connode.o; +text: .text%__1cKLoadPCNodeGOpcode6kM_i_; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: connode.o; +text: .text%__1cOloadConL13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRcompL_reg_conNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQaddF_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodDataKlass.o; +text: .text%__1cICmpDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodDataKlass.o; +text: .text%__1cLcmpF_ccNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKsplit_once6FpnMPhaseIterGVN_pnENode_333_v_: cfgnode.o; +text: .text%__1cLLShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJloadFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJCMoveNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFciEnvWis_dependence_violated6FpnMklassOopDesc_pnNmethodOopDesc__i_; +text: .text%__1cOPhaseIdealLoopOdo_range_check6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cQaddD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceRefKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlassKlass.o; +text: .text%__1cObox_handleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvD2I_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitPdstore_rounding6MpnENode__2_; +text: .text%__1cJloadINodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSdivL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQregP_to_stkPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRloadConP_pollNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIModINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cZCallDynamicJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cFframeVshould_be_deoptimized6kM_i_; +text: .text%__1cZCallDynamicJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXvirtual_call_RelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cUdivL_reg_imm13_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvD2I_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCMoveINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_: callnode.o; +text: .text%__1cZCallDynamicJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsubF_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeBlobOis_java_method6kM_i_: codeBlob.o; +text: .text%__1cQMachCallJavaNodeVis_MachCallStaticJava6M_pnWMachCallStaticJavaNode__: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cUmulL_reg_imm13_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQdivL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__: classes.o; +text: .text%__1cOcmovPI_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cUdivL_reg_imm13_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMTypeKlassPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cUmulL_reg_imm13_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNCallGeneratorQfor_virtual_call6FpnIciMethod__p0_; +text: .text%__1cUVirtualCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cNObjectMonitor2t6M_v_; +text: .text%__1cTmembar_volatileNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulINodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIMulINodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cQdivD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJCmpD3NodeGOpcode6kM_i_; +text: .text%__1cJloadDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMinINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOBasicHashtableJnew_entry6MI_pnTBasicHashtableEntry__; +text: .text%__1cUVirtualCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cQmulF_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_MonitorNotify; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: templateTable_sparc.o; +text: .text%__1cFBlockNset_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cSObjectSynchronizerGnotify6FnGHandle_pnGThread__v_; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_: interpreterRT_sparc.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interpreterRT_sparc.o; +text: .text%__1cKstoreFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSstring_compareNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cYjava_lang_reflect_MethodVhas_annotations_field6F_i_; +text: .text%__1cVshrL_reg_imm6_L2INodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYjava_lang_reflect_MethodIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cOloadConL13NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cYjava_lang_reflect_MethodPset_return_type6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodGcreate6FpnGThread__nGHandle__; +text: .text%__1cYjava_lang_reflect_MethodbFhas_parameter_annotations_field6F_i_; +text: .text%__1cINegDNodeGOpcode6kM_i_; +text: .text%__1cYjava_lang_reflect_MethodJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodWset_annotation_default6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodThas_signature_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodbChas_annotation_default_field6F_i_; +text: .text%__1cOimmI_32_63OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cYjava_lang_reflect_MethodIset_name6FpnHoopDesc_2_v_; +text: .text%__1cNmethodOopDescSannotation_default6kM_pnQtypeArrayOopDesc__; +text: .text%__1cKReflectionKnew_method6FnMmethodHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNIdealLoopTreeXpolicy_maximally_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cSsubL_reg_reg_2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQregP_to_stkPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNRelocIteratorEnext6M_i_: output.o; +text: .text%__1cOcmovII_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%jni_GetMethodID: jni.o; +text: .text%__1cTloadD_unalignedNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cQshlL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceRefKlass.o; +text: .text%__1cObranchConFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNminI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRshlI_reg_imm5NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNminI_eRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOloadConL13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNObjectMonitorGnotify6MpnGThread__v_; +text: .text%__1cOMacroAssemblerDjmp6MpnMRegisterImpl_ipkci_v_; +text: .text%__1cIDivLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSsubD_regD_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassDeclaredConstructors; +text: .text%__1cUdivL_reg_imm13_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmulD_regD_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJavaThreadbScheck_safepoint_and_suspend_for_native_trans6Fp0_v_; +text: .text%__1cRInlineCacheBufferVic_buffer_entry_point6FpC_1_; +text: .text%__1cOcmovIF_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUmulL_reg_imm13_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsubD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUdivL_reg_imm13_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSandL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConPCNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cMmatch_option6FpknMJavaVMOption_pkcp4_i_: arguments.o; +text: .text%__1cZregDHi_regDLo_to_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJEventMark2t6MpkcE_v_: psMarkSweep.o; +text: .text%__1cQregP_to_stkPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCMoveNodeEmake6FpnENode_222pknEType__p0_; +text: .text%__1cJCMoveNode2t6MpnENode_22pknEType__v_: connode.o; +text: .text%__1cSconvI2F_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRInlineCacheBufferWcreate_transition_stub6FpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cRInlineCacheBufferXassemble_ic_buffer_code6FpCpnHoopDesc_1_v_; +text: .text%__1cOcmovIF_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MipnMRegisterImpl__v_; +text: .text%__1cQcmovI_reg_ltNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cKo1RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cRtestI_reg_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIBytecodeIset_code6MnJBytecodesECode__v_; +text: .text%__1cQshrL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRsarL_reg_imm6NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadBNodeFclone6kM_pnENode__; +text: .text%__1cJloadFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSaddD_regD_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeHeapLfirst_block6kM_pnJHeapBlock__; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIpass_int6M_v_: interpreterRT_sparc.o; +text: .text%__1cRorI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassQfind_local_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__i_; +text: .text%__1cQshrL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQshrI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOimmI_32_63OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cSstkL_to_regD_0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cUGenericGrowableArrayKraw_remove6MpknEGrET__v_; +text: .text%__1cOloadI_fregNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMnegD_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTAbstractInterpreterLdeopt_entry6FnITosState_i_pC_; +text: .text%__1cLConvI2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJloadSNodeFclone6kM_pnENode__; +text: .text%__1cQjava_lang_ThreadMset_priority6FpnHoopDesc_nOThreadPriority__v_; +text: .text%__1cMnegD_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledICKcached_oop6kM_pnHoopDesc__; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: output.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cISubFNodeGOpcode6kM_i_; +text: .text%JVM_IsThreadAlive; +text: .text%__1cQstkI_to_regINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQshrL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_ThreadIis_alive6FpnHoopDesc__i_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: typeArrayKlass.o; +text: .text%__1cXPartialSubtypeCheckNodeGOpcode6kM_i_; +text: .text%__1cSsubD_regD_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSmulD_regD_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLconvI2BNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovIF_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRsarL_reg_imm6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRtestI_reg_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregI_to_stkINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFTypeFJsingleton6kM_i_; +text: .text%__1cLconvI2BNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_reg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSconvD2I_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubF_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopVinsert_pre_post_loops6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cQaddD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovPI_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKConv2BNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSstring_compareNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQregL_to_stkLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_SystemTout_offset_in_bytes6F_i_; +text: .text%__1cQjava_lang_SystemSin_offset_in_bytes6F_i_; +text: .text%__1cLTypeInstPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cWPredictedCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cWPredictedCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cLcmpF_ccNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cWPredictedCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cNCallGeneratorSfor_predicted_call6FpnHciKlass_p03_3_; +text: .text%__1cSconvI2F_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXconvI2D_regDHi_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cObox_handleNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cNCallGeneratorRfor_uncommon_trap6FpnIciMethod_nODeoptimizationLDeoptReason_n0CLDeoptAction__p0_; +text: .text%__1cOcmovPP_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZUncommonTrapCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSaddD_regD_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIVMThreadMis_VM_thread6kM_i_: vmThread.o; +text: .text%__1cIMulFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQmulF_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcmpF_ccNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregL_to_stkLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cGThread2t6M_v_; +text: .text%__1cOcmovLI_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosHSolarisPhotspot_sigmask6FpnGThread__v_; +text: .text%__1cCosHSolarisVinit_thread_fpu_state6F_v_; +text: .text%__1cFTypeFFxmeet6kMpknEType__3_; +text: .text%__1cCosScurrent_stack_size6F_I_; +text: .text%__1cOcmovLL_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIOSThreadNpd_initialize6M_v_; +text: .text%__1cCosScurrent_stack_base6F_pC_; +text: .text%__1cIOSThread2t6MpFpv_i1_v_; +text: .text%__1cIMulDNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cCosRinitialize_thread6F_v_; +text: .text%__1cSdivL_reg_reg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cGThreadOis_Java_thread6kM_i_: vmThread.o; +text: .text%__1cCosPpd_start_thread6FpnGThread__v_; +text: .text%__1cLConvI2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNobjArrayKlassIallocate6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNobjArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlass.o; +text: .text%__1cFTypeDGis_nan6kM_i_; +text: .text%jni_NewObjectArray: jni.o; +text: .text%__1cSsubL_reg_reg_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%JVM_SetThreadPriority; +text: .text%__1cQaddF_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosMstart_thread6FpnGThread__v_; +text: .text%__1cCosNcreate_thread6FpnGThread_n0AKThreadType_I_i_; +text: .text%_start: os_solaris.o; +text: .text%__1cXjava_lang_reflect_FieldNset_modifiers6FpnHoopDesc_i_v_; +text: .text%JVM_GetStackAccessControlContext; +text: .text%__1cXjava_lang_reflect_FieldThas_signature_field6F_i_; +text: .text%__1cXjava_lang_reflect_FieldVhas_annotations_field6F_i_; +text: .text%__1cXjava_lang_reflect_FieldPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cQsubD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerTload_unaligned_long6MpnMRegisterImpl_i2_v_; +text: .text%__1cFStateM_sub_Op_ModI6MpknENode__v_; +text: .text%JVM_Read; +text: .text%__1cOcmovPI_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCompiledICOset_cached_oop6MpnHoopDesc__v_; +text: .text%__1cFStateM_sub_Op_SubL6MpknENode__v_; +text: .text%__1cKCompiledICMstub_address6kM_pC_; +text: .text%__1cFciEnvZcall_has_multiple_targets6FpnNinstanceKlass_nMsymbolHandle_3ri_i_; +text: .text%__1cKReflectionJnew_field6FpnPfieldDescriptor_ipnGThread__pnHoopDesc__; +text: .text%__1cJvmSymbolsOsignature_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cQsubL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQmodI_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cISubDNodeGOpcode6kM_i_; +text: .text%__1cQmodI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPfieldDescriptorLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cRsarI_reg_imm5NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: callGenerator.o; +text: .text%__1cKReflectionInew_type6FnMsymbolHandle_nLKlassHandle_pnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_type6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldGcreate6FpnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cLOptoRuntimeWresolve_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cXjava_lang_reflect_FieldIset_name6FpnHoopDesc_2_v_; +text: .text%__1cSmulD_regD_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassYremove_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cMloadConINodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetStaticFieldID: jni.o; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSstring_compareNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreF0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerIjumpl_to6MrnHAddress_pnMRegisterImpl_i_v_: interp_masm_sparc.o; +text: .text%__1cOJavaAssertionsHenabled6Fpkci_i_; +text: .text%__1cPciObjArrayKlassEmake6FpnHciKlass__p0_; +text: .text%__1cSsubD_regD_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassKjni_id_for6Mi_pnFJNIid__; +text: .text%__1cSstkL_to_regD_0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFStateO_sub_Op_CMoveI6MpknENode__v_; +text: .text%__1cRsarL_reg_imm6NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIModLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cENodeEgetf6kM_f_; +text: .text%JVM_DesiredAssertionStatus; +text: .text%__1cKJavaThreadKinitialize6M_v_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: node.o; +text: .text%__1cNThreadServiceKadd_thread6FpnKJavaThread_i_v_; +text: .text%__1cWThreadLocalAllocBufferKinitialize6M_v_; +text: .text%__1cUregI_to_stkLHi_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLConvL2DNodeGOpcode6kM_i_; +text: .text%__1cNjni_functions6F_pknTJNINativeInterface___; +text: .text%__1cCosMguard_memory6FpcI_i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: node.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: node.o; +text: .text%__1cQThreadStatistics2t6M_v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC22i_v_; +text: .text%__1cUThreadSafepointStateGcreate6FpnKJavaThread__v_; +text: .text%__1cQshrL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGParker2t6M_v_; +text: .text%__1cIDivLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSdivL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: interp_masm_sparc.o; +text: .text%__1cMFlatProfilerJis_active6F_i_; +text: .text%__1cOMacroAssemblerNload_contents6MrnHAddress_pnMRegisterImpl_i_v_: interp_masm_sparc.o; +text: .text%__1cGThreadFstart6Fp0_v_; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_: interp_masm_sparc.o; +text: .text%__1cPconvI2D_memNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%jni_GetFloatArrayRegion: jni.o; +text: .text%__1cJMarkSweepMfollow_stack6F_v_; +text: .text%__1cNimmP_pollOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cRtestI_reg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZregDHi_regDLo_to_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMemRegionMintersection6kMk0_0_; +text: .text%__1cMVirtualSpaceJexpand_by6MI_i_; +text: .text%__1cUregI_to_stkLHi_0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQdivI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJavaThread2t6MpFp0pnGThread__vI_v_; +text: .text%__1cOcmovIF_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryQjava_mirror_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cRloadConP_pollNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPPerfDataManagerIadd_item6FpnIPerfData_i_v_; +text: .text%__1cKJavaThreadDrun6M_v_; +text: .text%__1cNSafepointBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cPjava_lang_ClassOprimitive_type6FpnHoopDesc__nJBasicType__; +text: .text%JVM_IsArrayClass; +text: .text%jni_CallStaticVoidMethod: jni.o; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnPPerfLongCounter__; +text: .text%__1cJloadDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNObjectMonitorGenter26MpnGThread__v_; +text: .text%__1cLConvF2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNsymbolOopDescWas_klass_external_name6kM_pkc_; +text: .text%__1cHnmethodbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cKstoreBNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFKlassNexternal_name6kM_pkc_; +text: .text%__1cOloadI_fregNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHdel_out6Mp0_v_: generateOptoStub.o; +text: .text%__1cOGenerateOopMapYrewrite_refval_conflicts6M_v_; +text: .text%__1cKstoreLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNloadConPCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLstoreC0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeHeapJexpand_by6MI_i_; +text: .text%__1cOGenerateOopMapKinterp_all6M_v_; +text: .text%__1cOGenerateOopMapPinitialize_vars6M_v_; +text: .text%__1cObranchConFNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadD_unalignedNodeIpipeline6kM_pknIPipeline__; +text: .text%JVM_GetClassName; +text: .text%__1cOGenerateOopMapTmethodsig_to_effect6MpnNsymbolOopDesc_ipnNCellTypeState__i_; +text: .text%__1cOloadI_fregNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlass.o; +text: .text%__1cOGenerateOopMapbAmake_context_uninitialized6M_v_; +text: .text%__1cOGenerateOopMapKinit_state6M_v_; +text: .text%__1cOGenerateOopMapYsetup_method_entry_state6M_v_; +text: .text%__1cOGenerateOopMapTmark_reachable_code6M_v_; +text: .text%__1cOGenerateOopMapRinit_basic_blocks6M_v_; +text: .text%__1cSaddD_regD_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLStringTableGintern6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cOcmovIF_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cCosMset_priority6FpnGThread_nOThreadPriority__nIOSReturn__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: loopnode.o; +text: .text%__1cQshrL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHMatcherXpost_store_load_barrier6FpknENode__i_; +text: .text%__1cLConvD2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQstkI_to_regFNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcmovIL_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVcompiledICHolderKlassSoop_being_unloaded6MpnRBoolObjectClosure_pnHoopDesc__i_; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_: interpreterRT_sparc.o; +text: .text%__1cINodeHashEgrow6M_v_; +text: .text%__1cOGenerateOopMapPdo_monitorenter6Mi_v_; +text: .text%__1cOcmovPP_regNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cMloadConDNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLStrCompNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMacroAssemblerVload_unaligned_double6MpnMRegisterImpl_ipnRFloatRegisterImpl__v_; +text: .text%__1cIMaxINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cJloadSNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOGenerateOopMapLcompute_map6MpnGThread__v_; +text: .text%__1cHnmethodNis_osr_method6kM_i_: nmethod.o; +text: .text%__1cLConvF2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQsubI_reg_regNodeFclone6kM_pnENode__; +text: .text%JVM_Open; +text: .text%__1cRInvocationCounterFreset6M_v_; +text: .text%__1cRCompilationPolicybIreset_counter_for_invocation_event6MnMmethodHandle__v_; +text: .text%__1cOGenerateOopMap2t6MnMmethodHandle__v_; +text: .text%__1cSsubL_reg_reg_2NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapRdo_interpretation6M_v_; +text: .text%__1cIRetTableRcompute_ret_table6MnMmethodHandle__v_; +text: .text%__1cICodeBlobJis_zombie6kM_i_: onStackReplacement.o; +text: .text%__1cOGenerateOopMapMmonitor_push6MnNCellTypeState__v_; +text: .text%__1cOGenerateOopMapNinitialize_bb6M_v_; +text: .text%__1cOGenerateOopMapbImark_bbheaders_and_count_gc_points6M_v_; +text: .text%__1cSmulL_reg_reg_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerEpush6MnITosState__v_; +text: .text%JVM_StartThread; +text: .text%__1cMthread_entry6FpnKJavaThread_pnGThread__v_: jvm.o; +text: .text%jni_GetStaticObjectField: jni.o; +text: .text%__1cJArrayDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cIGraphKitSprecision_rounding6MpnENode__2_; +text: .text%__1cNPerfByteArray2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_i_v_; +text: .text%__1cHAddress2t6Mn0AJaddr_type_i_v_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: generateOptoStub.o; +text: .text%__1cQjava_lang_ThreadIpriority6FpnHoopDesc__nOThreadPriority__; +text: .text%__1cQjava_lang_ThreadJstackSize6FpnHoopDesc__x_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKJavaThreadHprepare6MpnI_jobject_nOThreadPriority__v_; +text: .text%__1cIciObjectJis_method6M_i_: ciObjectFactory.o; +text: .text%__1cNloadConPCNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cTLoadD_unalignedNodeGOpcode6kM_i_; +text: .text%__1cSstkL_to_regD_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQshrI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%JVM_FreeMemory; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlass.o; +text: .text%__1cVcompiledICHolderKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%JVM_TotalMemory; +text: .text%__1cVcompiledICHolderKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cMloadConDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciObjectFactory.o; +text: .text%__1cUmulL_reg_imm13_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCodeBufferWinsert_double_constant6Md_pC_; +text: .text%__1cTAbstractInterpreterWlayout_activation_impl6FpnNmethodOopDesc_iiiipnFframe_4i_i_; +text: .text%__1cQdivL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIciObjectOis_method_data6M_i_: ciObjectFactory.o; +text: .text%__1cOcmovIL_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2D_memNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: interpreter_sparc.o; +text: .text%__1cUdivL_reg_imm13_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHThreadsGremove6FpnKJavaThread__v_; +text: .text%__1cIOSThread2T6M_v_; +text: .text%__1cUThreadSafepointStateHdestroy6FpnKJavaThread__v_; +text: .text%__1cKJavaThreadYremove_stack_guard_pages6M_v_; +text: .text%__1cQandI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQjava_lang_ThreadNset_stillborn6FpnHoopDesc__v_; +text: .text%__1cGParker2T6M_v_; +text: .text%__1cSandL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRInterpreterOopMapNresource_copy6MpnQOopMapCacheEntry__v_; +text: .text%__SLIP.DELETER__A: thread.o; +text: .text%__1cCosOunguard_memory6FpcI_i_; +text: .text%__1cKJavaThreadEexit6Mi_v_; +text: .text%__1cLConvD2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIOSThreadKpd_destroy6M_v_; +text: .text%__1cWstatic_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cODeoptimizationYquery_update_method_data6FnQmethodDataHandle_in0ALDeoptReason_rIri4_pnLProfileData__; +text: .text%__1cKJavaThread2T6M_v_; +text: .text%__1cGThread2T5B6M_v_; +text: .text%__1cCosLfree_thread6FpnIOSThread__v_; +text: .text%__1cFStateM_sub_Op_MulI6MpknENode__v_; +text: .text%__1cNThreadServiceNremove_thread6FpnKJavaThread_i_v_; +text: .text%__1cNThreadServiceWcurrent_thread_exiting6FpnKJavaThread__v_; +text: .text%__1cLensure_join6FpnKJavaThread__v_: thread.o; +text: .text%__1cQOopMapCacheEntryEfill6MnMmethodHandle_i_v_; +text: .text%__1cUregI_to_stkLHi_0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSTailCalljmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMapEppop6MpnNCellTypeState__v_; +text: .text%__1cUregI_to_stkLHi_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSTailCalljmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsubF_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICmpDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLconvI2BNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRNativeMovConstRegEdata6kM_i_; +text: .text%__1cbFunnecessary_membar_volatileNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cLcmpF_ccNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNObjectMonitorJnotifyAll6MpnGThread__v_; +text: .text%jni_CallObjectMethod: jni.o; +text: .text%__1cMTailCallNode2t6MpnENode_222222_v_; +text: .text%__1cQaddD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: deoptimization.o; +text: .text%__1cPconvD2F_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRInlineCacheBufferUic_buffer_cached_oop6FpC_pnHoopDesc__; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNimmP_pollOperFclone6kM_pnIMachOper__; +text: .text%__1cQdivD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRtestI_reg_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerbEset_method_data_pointer_offset6MpnMRegisterImpl__v_; +text: .text%__1cSconvF2I_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIMaxINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQChunkPoolCleanerEtask6M_v_: allocation.o; +text: .text%__1cJLoadPNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cRloadConP_pollNodeFclone6kM_pnENode__; +text: .text%__1cHTypeInt2t6Miii_v_; +text: .text%__1cTOopMapForCacheEntryLcompute_map6MpnGThread__v_; +text: .text%__1cNloadConPCNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConPCNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIL_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cTOopMapForCacheEntryOreport_results6kM_i_: oopMapCache.o; +text: .text%__1cKConv2BNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cXconvI2D_regDHi_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: library_call.o; +text: .text%__1cSandL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLI_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadRangeNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_StringbHcreate_from_platform_depended_str6FpkcpnGThread__nGHandle__; +text: .text%__1cRshlI_reg_imm5NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorJpass_long6M_v_; +text: .text%__1cQregL_to_stkLNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapVresult_for_basicblock6Mi_v_; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: interpreterRT_sparc.o; +text: .text%__1cQOopMapCacheEntryIset_mask6MpnNCellTypeState_2i_v_; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: klassKlass.o; +text: .text%__1cLOptoRuntimeYcurrent_time_millis_Type6F_pknITypeFunc__; +text: .text%__1cHTypePtrFxdual6kM_pknEType__; +text: .text%__1cURethrowExceptionNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcastP2INodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotIOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotIOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOcmovLL_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_MonitorNotifyAll; +text: .text%__1cJloadDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOstackSlotIOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cIGraphKitXinsert_mem_bar_volatile6MpnKMemBarNode_i_v_; +text: .text%__1cKCMoveLNodeGOpcode6kM_i_; +text: .text%__1cRshlL_reg_imm6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOLibraryCallKitYinline_native_time_funcs6Mi_i_; +text: .text%__1cMnegD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationVtrap_state_has_reason6Fii_i_; +text: .text%__1cVMoveL2D_stack_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHRetDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cTloadD_unalignedNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNiRegIsafeOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNloadConP0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: ciTypeFlow.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: stubGenerator_sparc.o; +text: .text%__1cSinstanceKlassKlassOklass_oop_size6kM_i_: instanceKlassKlass.o; +text: .text%__1cZInterpreterMacroAssemblerFpop_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cIAddDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMnegD_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPmethodDataKlassOklass_oop_size6kM_i_: methodDataKlass.o; +text: .text%__1cKimmL13OperFclone6kM_pnIMachOper__; +text: .text%__1cKarrayKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cLmethodKlassOklass_oop_size6kM_i_: methodKlass.o; +text: .text%__1cLstoreF0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKarrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cWconstantPoolCacheKlassOklass_oop_size6kM_i_: cpCacheKlass.o; +text: .text%__1cQconstMethodKlassOklass_oop_size6kM_i_: constMethodKlass.o; +text: .text%__1cXJNI_ArgumentPusherVaArgJget_float6M_v_: jni.o; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: arrayKlassKlass.o; +text: .text%__1cQshlL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSobjArrayKlassKlassOklass_oop_size6kM_i_: objArrayKlassKlass.o; +text: .text%__1cLsymbolKlassOklass_oop_size6kM_i_: symbolKlass.o; +text: .text%__1cVcompiledICHolderKlassOklass_oop_size6kM_i_: compiledICHolderKlass.o; +text: .text%__1cSsubL_reg_reg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmulL_reg_reg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSdivL_reg_reg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRconstantPoolKlassOklass_oop_size6kM_i_: constantPoolKlass.o; +text: .text%__1cTtypeArrayKlassKlassOklass_oop_size6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cOloadI_fregNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRtestI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_ThreadbGinherited_access_control_context6FpnHoopDesc__2_; +text: .text%__1cJLoadSNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cbIjava_security_AccessControlContextGcreate6FnOobjArrayHandle_inGHandle_pnGThread__pnHoopDesc__; +text: .text%__1cLstoreF0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cUGenericGrowableArrayUclear_and_deallocate6M_v_; +text: .text%__1cIMinINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cNmaxI_eRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetInheritedAccessControlContext; +text: .text%__1cPPerfDataManagerWcreate_string_constant6FnJCounterNS_pkc3pnGThread__pnSPerfStringConstant__; +text: .text%__1cNmaxI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%JVM_NativePath; +text: .text%__1cOMacroAssemblerNflush_windows6M_v_; +text: .text%__1cNloadConL0NodeFclone6kM_pnENode__; +text: .text%__1cSsubD_regD_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVCallRuntimeDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFJNIidHoops_do6MpnKOopClosure__v_; +text: .text%__1cJHashtableHoops_do6MpnKOopClosure__v_; +text: .text%__1cSReferenceProcessorHoops_do6MpnKOopClosure__v_; +text: .text%__1cHCompileKinit_start6MpnJStartNode__v_; +text: .text%__1cKg3RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cVinline_cache_regPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cWloadConI_x41f00000NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstorePNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIGraphKitbAgen_stub_or_native_wrapper6MpCpkcpnIciMethod_iiiii_v_; +text: .text%__1cQObjectStartArrayFreset6M_v_; +text: .text%__1cPconvI2D_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsHoops_do6FpnKOopClosure__v_; +text: .text%__1cQaddD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvF2INodeGOpcode6kM_i_; +text: .text%__1cJimmL0OperFclone6kM_pnIMachOper__; +text: .text%__1cVCallRuntimeDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJHashtableGunlink6MpnRBoolObjectClosure__v_; +text: .text%__1cIPSOldGenPadjust_pointers6M_v_; +text: .text%__1cVCallRuntimeDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_: callnode.o; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__: callnode.o; +text: .text%__1cOcmovPI_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIPSOldGenHcompact6M_v_; +text: .text%__1cMtlsLoadPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJvmSymbolsHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cLcmpF_ccNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregL_to_stkLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cVCallRuntimeDirectNodeKmethod_set6Mi_v_; +text: .text%__1cKimmI11OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cSstkL_to_regD_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQcmovI_reg_gtNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQstkI_to_regINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreP0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovIF_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLL_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetStaticMethodID: jni.o; +text: .text%__1cIUniverseWreinitialize_vtable_of6FpnFKlass_pnGThread__v_; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MipnMRegisterImpl__v_; +text: .text%__1cRtestI_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHnmethodbAmake_not_entrant_or_zombie6Mi_v_; +text: .text%__1cPconvF2D_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodDataKlass.o; +text: .text%__1cOcmovDF_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopKdo_peeling6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cOcmovLL_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%jint_cmp: parse2.o; +text: .text%__1cXjava_lang_boxing_objectJget_value6FpnHoopDesc_pnGjvalue__nJBasicType__; +text: .text%__1cNloadConL0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerKnull_check6MpnMRegisterImpl_i_v_; +text: .text%__1cVMoveL2D_stack_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIMulDNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cIGraphKitTdprecision_rounding6MpnENode__2_; +text: .text%__1cSconvF2I_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodbCcan_not_entrant_be_converted6M_i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodDataKlass.o; +text: .text%__1cJloadCNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOloadI_fregNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcmovLL_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLConvD2FNodeGOpcode6kM_i_; +text: .text%__1cIMulFNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cWloadConI_x41f00000NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKcmpOpFOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cLstoreC0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQregL_to_stkLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLRuntimeStubQnew_runtime_stub6FpkcpnKCodeBuffer_ipnJOopMapSet_i_p0_; +text: .text%__1cIAddFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLcastP2INodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKo2RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cOcmovIF_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovDF_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQaddL_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%JVM_Close; +text: .text%__1cSmulD_regD_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKstoreFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSsubD_regD_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddD_regD_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cXconvI2D_regDHi_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadD_unalignedNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPPerfDataManagerUcreate_long_constant6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongConstant__; +text: .text%__1cOMacroAssemblerNget_vm_result6MpnMRegisterImpl__v_; +text: .text%__1cQsubF_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerbIcompute_extra_locals_size_in_bytes6MpnMRegisterImpl_22_v_; +text: .text%__1cLcmpF_ccNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPMultiBranchDataScompute_cell_count6FpnOBytecodeStream__i_; +text: .text%__1cPorI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUregI_to_stkLHi_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSxorI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvI2D_memNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: templateTable_sparc.o; +text: .text%__1cQdivI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2D_memNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLconvI2BNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cISubFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cWloadConI_x43300000NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cWloadConI_x41f00000NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: loopnode.o; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: templateTable_sparc.o; +text: .text%__1cSmulI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOtailjmpIndNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cRInlineCacheBufferSic_destination_for6FpnKCompiledIC__pC_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: loopnode.o; +text: .text%__1cENodeJis_MemBar6kM_pknKMemBarNode__: classes.o; +text: .text%__1cbFunnecessary_membar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJSubFPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFParseNdo_instanceof6M_v_; +text: .text%__1cLconvI2BNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitOgen_instanceof6MpnENode_2_2_; +text: .text%__1cbFunnecessary_membar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRshrL_reg_imm6NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJloadBNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJloadDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIDivLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvI2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSmulD_regD_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOstackSlotLOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cUregI_to_stkLHi_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKConv2BNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQshlI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXjava_lang_reflect_FieldFclazz6FpnHoopDesc__2_; +text: .text%__1cXjava_lang_reflect_FieldJmodifiers6FpnHoopDesc__i_; +text: .text%__1cPconvD2F_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovPP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsubF_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_NewObjectV: jni.o; +text: .text%__1cOcmovLI_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPciInstanceKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cCosHSolarisKmmap_chunk6FpcIii_2_; +text: .text%__1cXPartialSubtypeCheckNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%jni_EnsureLocalCapacity; +text: .text%__1cLstoreI0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIAddFNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cTunsafe_intrinsic_id6FpnNsymbolOopDesc_1_nNmethodOopDescLIntrinsicId__; +text: .text%__1cLConvD2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLConvD2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKoopFactoryUnew_compiledICHolder6FnMmethodHandle_nLKlassHandle_pnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cPorL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCompiledStaticCallMset_to_clean6M_v_; +text: .text%__1cIDivDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cUregI_to_stkLHi_0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cVcompiledICHolderKlassIallocate6MpnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cKVtableStubSpd_code_size_limit6Fi_i_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cSaddD_regD_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPfieldDescriptorUdouble_initial_value6kM_d_; +text: .text%__1cQsubD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovPP_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableGbranch6Fii_v_; +text: .text%__1cSsubL_reg_reg_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interpreter_sparc.o; +text: .text%__1cNSafePointNodeQpeek_monitor_obj6kM_pnENode__; +text: .text%__1cJloadFNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddI_reg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParsePdo_monitor_exit6M_v_; +text: .text%__1cSdivL_reg_reg_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cObranchConFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cObranchConFNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cSconvF2I_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmembar_releaseNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cOloadI_fregNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cObranchConFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPstoreI_FregNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcmpD_ccNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadLNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: stubGenerator_sparc.o; +text: .text%__1cSmulL_reg_reg_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerUprofile_taken_branch6MpnMRegisterImpl_2_v_; +text: .text%__1cHCompile2t6MpnFciEnv_pF_pknITypeFunc_pCpkciiii_v_; +text: .text%__1cLResourceObj2n6FIn0APallocation_type__pv_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: interp_masm_sparc.o; +text: .text%__1cNSafePointNodeQpeek_monitor_box6kM_pnENode__; +text: .text%__1cUregI_to_stkLHi_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitIgen_stub6MpCpkciii_v_; +text: .text%__1cNTemplateTableOpatch_bytecode6FnJBytecodesECode_pnMRegisterImpl_4i_v_; +text: .text%__1cFTypeFFxdual6kM_pknEType__; +text: .text%__1cICmpFNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlass.o; +text: .text%__1cKVtableStubRpd_code_alignment6F_i_; +text: .text%__1cSstkL_to_regD_2NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSstkL_to_regD_0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIMachNodeOmemory_operand6kM_pknIMachOper__: ad_sparc_misc.o; +text: .text%__1cKarrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cKloadUBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQaddD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadL_unalignedNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cINegDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cVMoveF2I_stack_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvI2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOcmovLL_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRorI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTloadL_unalignedNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTloadL_unalignedNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKloadUBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFMutexbLwait_for_lock_blocking_implementation6MpnKJavaThread__v_; +text: .text%__1cXconvI2D_regDHi_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSvframeArrayElementPunpack_on_stack6MiipnFframe_ii_v_; +text: .text%__1cSconvF2I_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerbFtest_invocation_counter_for_mdp6MpnMRegisterImpl_22rnFLabel__v_; +text: .text%__1cXconvI2D_regDHi_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSvframeArrayElementHfill_in6MpnOcompiledVFrame__v_; +text: .text%__1cFTypeDFxdual6kM_pknEType__; +text: .text%__1cSaddD_regD_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerbAincrement_backedge_counter6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerbBtest_backedge_count_for_osr6MpnMRegisterImpl_22_v_; +text: .text%__1cSmulL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcmovPI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKEntryPoint2t6M_v_; +text: .text%__1cTloadD_unalignedNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZregDHi_regDLo_to_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcompiledVFrameImonitors6kM_pnNGrowableArray4CpnLMonitorInfo____; +text: .text%__1cOcompiledVFrameLexpressions6kM_pnUStackValueCollection__; +text: .text%__1cHciKlassOsuper_of_depth6MI_p0_; +text: .text%__1cOcompiledVFrameGlocals6kM_pnUStackValueCollection__; +text: .text%__1cOcompiledVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cJimmP0OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cOcompiledVFrameHraw_bci6kM_i_; +text: .text%__1cQshrI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveL2D_stack_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cWloadConI_x43300000NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsbMis_supported_jni_version_including_1_16Fi_C_; +text: .text%__1cMTailJumpNodeKmatch_edge6kMI_I_; +text: .text%__1cSvframeArrayElementNon_stack_size6kMiiii_i_; +text: .text%__1cWloadConI_x41f00000NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationbJupdate_method_data_from_interpreter6FnQmethodDataHandle_ii_v_; +text: .text%__1cIimmDOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cFframeZinterpreter_frame_set_mdx6Mi_v_; +text: .text%__1cOstackSlotLOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotLOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cTloadD_unalignedNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadD_unalignedNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIModLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJimmU5OperFclone6kM_pnIMachOper__; +text: .text%__1cTAbstractInterpreterPsize_activation6FpnNmethodOopDesc_iiiii_i_; +text: .text%__1cOtailjmpIndNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSmulD_regD_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSstkL_to_regD_0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovDF_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTAbstractInterpreterQcontinuation_for6FpnNmethodOopDesc_pCiiri_3_; +text: .text%__1cUregI_to_stkLHi_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cINegFNodeGOpcode6kM_i_; +text: .text%__1cSsubD_regD_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTAbstractInterpreterRlayout_activation6FpnNmethodOopDesc_iiiipnFframe_4i_v_; +text: .text%__1cJScopeDescImonitors6M_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cUregI_to_stkLHi_0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJScopeDescLexpressions6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cJScopeDescGlocals6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: klassKlass.o; +text: .text%JVM_GetComponentType; +text: .text%__1cQdivI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%Unsafe_DefineClass1; +text: .text%__1cOcmovII_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLvframeArrayPunpack_to_stack6MrnFframe_i_v_; +text: .text%__1cKReflectionUarray_component_type6FpnHoopDesc_pnGThread__2_; +text: .text%__1cLConvF2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSvframeArrayElementDbci6kM_i_; +text: .text%__1cVMoveF2I_stack_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cICodeBlobZis_at_poll_or_poll_return6MpC_i_; +text: .text%__1cLvframeArrayIallocate6FpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pnLRegisterMap_nFframe_9A9A9A_p0_; +text: .text%JVM_GetCPFieldModifiers; +text: .text%__1cKJavaThreadbFdeoptimized_wrt_marked_nmethods6M_v_; +text: .text%__1cLcastP2INodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNnmethodLocker2t6MpC_v_; +text: .text%__1cNSharedRuntimebJcontinuation_for_implicit_exception6FpnKJavaThread_pCn0AVImplicitExceptionKind__3_; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: deoptimization.o; +text: .text%__1cODeoptimizationNuncommon_trap6FpnKJavaThread_i_pn0ALUnrollBlock__; +text: .text%__1cNloadConL0NodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cODeoptimizationTuncommon_trap_inner6FpnKJavaThread_i_v_; +text: .text%__1cODeoptimizationScreate_vframeArray6FpnKJavaThread_nFframe_pnLRegisterMap__pnLvframeArray__; +text: .text%__1cODeoptimizationNunpack_frames6FpnKJavaThread_i_nJBasicType__; +text: .text%__1cODeoptimizationYfetch_unroll_info_helper6FpnKJavaThread__pn0ALUnrollBlock__; +text: .text%__1cZInterpreterMacroAssemblerXindex_check_without_pop6MpnMRegisterImpl_2i22_v_; +text: .text%__1cRSignatureIteratorKparse_type6M_i_; +text: .text%__1cPconvD2F_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHciKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cODeoptimizationRlast_frame_adjust6Fii_i_; +text: .text%__1cQsubD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_DefineClass; +text: .text%JVM_InvokeMethod; +text: .text%__1cOcmovPP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_NewDirectByteBuffer; +text: .text%__1cHJNIEnv_JNewObject6MpnH_jclass_pnK_jmethodID_E_pnI_jobject__: jni.o; +text: .text%__1cHBoxNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%jni_AllocObject: jni.o; +text: .text%__1cNTemplateTableMlocals_index6FpnMRegisterImpl_i_v_; +text: .text%__1cFStateL_sub_Op_Box6MpknENode__v_; +text: .text%__1cTmembar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKCodeBufferVinsert_float_constant6Mf_pC_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: jniFastGetField_sparc.o; +text: .text%__1cMnegD_regNodeIpipeline6kM_pknIPipeline__; +text: .text%Unsafe_AllocateInstance; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: generateOopMap.o; +text: .text%__1cQComputeCallStackHdo_byte6M_v_: generateOopMap.o; +text: .text%__1cQstkI_to_regINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cYjava_lang_reflect_MethodEslot6FpnHoopDesc__i_; +text: .text%__1cYjava_lang_reflect_MethodFclazz6FpnHoopDesc__2_; +text: .text%__1cYinternal_word_RelocationGtarget6M_pC_; +text: .text%__1cYinternal_word_RelocationMforce_target6MpC_v_: relocInfo.o; +text: .text%__1cJStubQdDueueKremove_all6M_v_; +text: .text%__1cMloadConFNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cETypeJis_finite6kM_i_; +text: .text%__1cPconvI2D_memNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLconvI2BNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPorL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPorL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerGif_cmp6MnJAssemblerJCondition_i_v_; +text: .text%__1cZInterpreterMacroAssemblerLindex_check6MpnMRegisterImpl_2i22_v_; +text: .text%__1cOMacroAssemblerPcasx_under_lock6MpnMRegisterImpl_22pCi_v_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cQsubF_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshlI_reg_imm5NodeFclone6kM_pnENode__; +text: .text%__1cNloadRangeNodeFclone6kM_pnENode__; +text: .text%__1cSaddL_reg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovPI_regNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cKstfSSFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMloadConFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKReflectionNinvoke_method6FpnHoopDesc_nGHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cYjava_lang_reflect_MethodPparameter_types6FpnHoopDesc__2_; +text: .text%__1cTmembar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvI2L_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovII_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cYjava_lang_reflect_MethodLreturn_type6FpnHoopDesc__2_; +text: .text%__1cJCmpF3NodeGOpcode6kM_i_; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceRefKlass.o; +text: .text%__1cLMoveL2DNodeGOpcode6kM_i_; +text: .text%__1cFKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cLTypeInstPtrLmirror_type6kM_pnGciType__; +text: .text%__1cOstackSlotIOperFclone6kM_pnIMachOper__; +text: .text%__1cKReflectionRreflect_new_array6FpnHoopDesc_ipnGThread__pnMarrayOopDesc__; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_pkcnGHandle_6_6_; +text: .text%__1cOcmovII_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerHbr_null6MpnMRegisterImpl_inJAssemblerHPredict_rnFLabel__v_; +text: .text%__1cFParseScan_rerun_bytecode6M_i_; +text: .text%__1cQshrL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_pnNsymbolOopDesc_pkc_nGHandle__; +text: .text%__1cIAddFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKstfSSFNodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cHdom_lca6FpnFBlock_1_1_: gcm.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlass.o; +text: .text%JVM_NewArray; +text: .text%__1cHOrLNodeGOpcode6kM_i_; +text: .text%__1cLStrCompNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cLOopMapCache2t6M_v_; +text: .text%__1cNTemplateTableHconvert6F_v_; +text: .text%__1cOcmovDF_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerFpop_l6MpnMRegisterImpl__v_; +text: .text%__1cOcmovLI_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSMachBreakpointNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeQcreate_exception6FpnKJavaThread_pc3_v_; +text: .text%__1cFParseWload_interpreter_state6MpnENode_2_v_; +text: .text%__1cQComputeCallStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cKPSYoungGenKprecompact6M_v_; +text: .text%__1cXjava_lang_reflect_FieldEslot6FpnHoopDesc__i_; +text: .text%__1cSconvD2I_helperNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMnegF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsLgc_prologue6F_v_; +text: .text%__1cHThreadsLgc_epilogue6F_v_; +text: .text%__1cPconvI2L_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPconvD2I_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_pnGThread__v_; +text: .text%__1cUParallelScavengeHeapHcollect6MnHGCCauseFCause__v_; +text: .text%__1cJCodeCacheMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cMStartOSRNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: callnode.o; +text: .text%__1cRCardTableModRefBSEis_a6MnKBarrierSetEName__i_: cardTableExtension.o; +text: .text%__1cRCardTableModRefBSFclear6MnJMemRegion__v_; +text: .text%__1cVLoaderConstraintTableYpurge_loader_constraints6MpnRBoolObjectClosure__v_; +text: .text%__1cVLoaderConstraintTableYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cLconvP2BNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryValways_strong_oops_do6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cIciMethodVget_osr_flow_analysis6Mi_pnKciTypeFlow__; +text: .text%__1cPconvD2F_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvD2I_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLMoveF2INodeGOpcode6kM_i_; +text: .text%__1cKNativeJumpUpatch_verified_entry6FpC11_v_; +text: .text%__1cSReferenceProcessorPoops_do_statics6FpnKOopClosure__v_; +text: .text%__1cMStartOSRNodeKosr_domain6F_pknJTypeTuple__; +text: .text%__1cVVM_ParallelGCSystemGCEname6kM_pkc_: vm_operations.o; +text: .text%__1cVVM_ParallelGCSystemGCEdoit6M_v_; +text: .text%__1cVVM_ParallelGCSystemGC2t6MI_v_; +text: .text%__1cJArgumentsQPropertyList_add6FppnOSystemProperty_2_v_; +text: .text%__1cOMacroAssemblerPbreakpoint_trap6M_v_; +text: .text%__1cJBasicLockHmove_to6MpnHoopDesc_p0_v_; +text: .text%__1cJMarkSweepNrestore_marks6F_v_; +text: .text%__1cJMarkSweepMadjust_marks6F_v_; +text: .text%__1cJMarkSweepXfollow_weak_klass_links6F_v_; +text: .text%__1cRStubCodeGeneratorLstub_epilog6MpnMStubCodeDesc__v_; +text: .text%__1cMStubCodeMark2t6MpnRStubCodeGenerator_pkc4_v_; +text: .text%__1cMStubCodeMark2T6M_v_; +text: .text%__1cQAbstractCompilerMsupports_osr6M_i_: c2compiler.o; +text: .text%__1cNCallGeneratorHfor_osr6FpnIciMethod_i_p0_; +text: .text%__1cLClassLoaderSget_system_package6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cJPSPermGenKprecompact6M_v_; +text: .text%JVM_GC; +text: .text%__1cIPSOldGenKprecompact6M_v_; +text: .text%__1cUPSMarkSweepDecoratorbIset_destination_decorator_perm_gen6F_v_; +text: .text%__1cUPSMarkSweepDecoratorbHset_destination_decorator_tenured6F_v_; +text: .text%__1cKDictionaryYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cKDictionaryMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cLPSMarkSweepQinvoke_no_policy6Fpii_v_; +text: .text%__1cLPSMarkSweepGinvoke6Fpii_v_; +text: .text%__1cQmulL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cWloadConI_x43300000NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUPSAdaptiveSizePolicyUmajor_collection_end6MInHGCCauseFCause__v_; +text: .text%__1cUPSAdaptiveSizePolicyWmajor_collection_begin6M_v_; +text: .text%__1cWloadConI_x41f00000NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIUniverseWupdate_heap_info_at_gc6F_v_; +text: .text%__1cJPSPermGenQcompute_new_size6MI_v_; +text: .text%__1cKPSYoungGenHcompact6M_v_; +text: .text%JVM_GetSystemPackage; +text: .text%__1cPfieldDescriptorTfloat_initial_value6kM_f_; +text: .text%__1cKPSYoungGenPadjust_pointers6M_v_; +text: .text%__1cQUncommonTrapBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cSDeoptimizationBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cNExceptionBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: objArrayKlass.o; +text: .text%__1cJCodeCacheHoops_do6FpnKOopClosure__v_; +text: .text%__1cJCodeCacheLgc_prologue6F_v_; +text: .text%__1cJCodeCacheLgc_epilogue6F_v_; +text: .text%__1cIXorINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNStubGeneratorFalign6Mi_v_: stubGenerator_sparc.o; +text: .text%__1cQregL_to_stkLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLcastP2INodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKcmpOpFOperKless_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cOcmovPI_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCMoveNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQdivD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcmovIF_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKCMoveDNodeGOpcode6kM_i_; +text: .text%__1cJLoadDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIMulFNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cNStubGeneratorLstub_prolog6MpnMStubCodeDesc__v_: stubGenerator_sparc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: klassKlass.o; +text: .text%__1cQaddL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%jni_GetStringRegion: jni.o; +text: .text%JVM_RawMonitorCreate; +text: .text%__1cJloadLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMloadConFNodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNinstanceKlassPadd_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_ppkc5i_i_: arguments.o; +text: .text%__1cNloadConPCNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotPOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotPOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotPOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cZInterpreterMacroAssemblerNunlock_object6MpnMRegisterImpl__v_; +text: .text%__1cSaddP_reg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cOstackSlotFOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%JVM_Sleep; +text: .text%__1cHBoxNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cObox_handleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvL2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLstoreF0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceRefKlass.o; +text: .text%__1cQstkI_to_regFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQinstanceRefKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceRefKlass.o; +text: .text%__1cRorI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cVMoveF2I_stack_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotLOperFclone6kM_pnIMachOper__; +text: .text%Unsafe_CompareAndSwapInt; +text: .text%JVM_Lseek; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: templateTable_sparc.o; +text: .text%__1cNloadRangeNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPconvD2F_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRComputeEntryStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cPconvF2D_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cQmulI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQmulF_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSconvF2I_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRmethodDataOopDescRbci_to_extra_data6Mii_pnLProfileData__; +text: .text%__1cOcmovLI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPMultiBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cQregL_to_stkLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregP_to_stkPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvI2D_memNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddL_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerQtest_mdp_data_at6MipnMRegisterImpl_rnFLabel_2_v_; +text: .text%__1cVMoveL2D_stack_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: loopnode.o; +text: .text%__1cPconvD2F_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLI_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOLibraryCallKitXinline_string_compareTo6M_i_; +text: .text%__1cQdivI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cGciType2t6MnJBasicType__v_; +text: .text%__1cOMacroAssemblerKbr_notnull6MpnMRegisterImpl_inJAssemblerHPredict_rnFLabel__v_; +text: .text%__1cJLoadBNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: machnode.o; +text: .text%__1cLOptoRuntimeRnew_objArray_Type6F_pknITypeFunc__; +text: .text%__1cQaddF_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cETypeEmake6Fn0AFTYPES__pk0_; +text: .text%__1cSconvF2I_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRsarL_reg_imm6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstring_compareNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_GetEnv; +text: .text%__1cJloadDNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQstkI_to_regINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSstring_compareNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_: interpreterRT_sparc.o; +text: .text%Unsafe_GetNativeByte; +text: .text%JVM_NanoTime; +text: .text%__1cCosNjavaTimeNanos6F_x_; +text: .text%__1cOMacroAssemblerOrestore_thread6MkpnMRegisterImpl__v_; +text: .text%__1cVcompiledICHolderKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQandL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIimmFOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cHThreadsLnmethods_do6F_v_; +text: .text%__1cKcmpOpFOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cICodeBlobFflush6M_v_; +text: .text%__1cZInterpreterMacroAssemblerFpop_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cFParseMdo_anewarray6M_v_; +text: .text%__1cSdivL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_CallVoidMethod: jni.o; +text: .text%__1cJCodeCacheFfirst6F_pnICodeBlob__; +text: .text%__1cObranchConFNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cFParseOdo_tableswitch6M_v_; +text: .text%__1cOcmovIF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvI2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSaddL_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLstoreC0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%Unsafe_GetNativeFloat; +text: .text%__1cOMacroAssemblerGmembar6MnJAssemblerQMembar_mask_bits__v_: templateTable_sparc.o; +text: .text%__1cNTemplateTableXjvmti_post_field_access6Fiii_v_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: methodLiveness.o; +text: .text%__1cbCAbstractInterpreterGeneratorbBgenerate_result_handler_for6MnJBasicType__pC_; +text: .text%__1cOstackSlotFOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cHnmethodFflush6M_v_; +text: .text%__1cHnmethodSflush_dependencies6MpnRBoolObjectClosure__v_; +text: .text%__1cKo2RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cQregI_to_stkINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMloadConFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_method_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cMregD_lowOperFclone6kM_pnIMachOper__; +text: .text%__1cJloadFNodeFclone6kM_pnENode__; +text: .text%__1cJEventMark2t6MpkcE_v_: nmethod.o; +text: .text%__1cCosNcommit_memory6FpcII_i_; +text: .text%__1cJloadLNodeFclone6kM_pnENode__; +text: .text%__1cParrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cSaddI_reg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cFVTuneOdelete_nmethod6FpnHnmethod__v_; +text: .text%__1cLOptoRuntimeNgenerate_stub6FpnFciEnv_pF_pknITypeFunc_pCpkciiii_8_; +text: .text%__1cWloadConI_x43300000NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParseQdo_monitor_enter6M_v_; +text: .text%__1cPorL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLstoreC0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPSPromotionLABRunallocate_object6MpnHoopDesc__i_; +text: .text%JVM_FindPrimitiveClass; +text: .text%__1cVMoveL2D_stack_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNTemplateTableEiop26Fn0AJOperation__v_; +text: .text%__1cPjava_lang_ClassYcreate_basic_type_mirror6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cZInterpreterMacroAssemblerMdispatch_via6MnITosState_ppC_v_; +text: .text%__1cSmodL_reg_imm13NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRshrI_reg_imm5NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cSsubL_reg_reg_2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUmulL_reg_imm13_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPconvI2F_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNinstanceKlassUfind_interface_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cOstackSlotFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cUdivL_reg_imm13_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_continuation_for6MnITosState__pC_; +text: .text%__1cRSignatureIteratorHiterate6M_v_; +text: .text%__1cOcmovLL_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcastP2INodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constantPoolKlass.o; +text: .text%__1cJname2type6Fpkc_nJBasicType__; +text: .text%__1cSmulL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_safept_entry_for6MnITosState_pC_2_; +text: .text%__1cLcastP2INodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPBytecode_invokeLresult_type6kMpnGThread__nJBasicType__; +text: .text%__1cLCastP2INodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cOloadConL13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmodL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFTypeFJis_finite6kM_i_; +text: .text%__1cKcmpOpFOperHgreater6kM_i_: ad_sparc_clone.o; +text: .text%__1cIDivDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOMacroAssemblerKget_thread6M_v_; +text: .text%__1cPconvI2F_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovDF_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvI2F_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSaddD_regD_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKVtableStub2n6FIi_pv_; +text: .text%__1cNloadConPCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWloadConI_x41f00000NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKScopeValueLis_location6kM_i_: debugInfo.o; +text: .text%__1cLVtableStubsLcreate_stub6FiipnNmethodOopDesc__pC_; +text: .text%__1cLOptoRuntimebBhandle_wrong_method_ic_miss6FpnKJavaThread__pC_; +text: .text%__1cSmulD_regD_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2T6M_v_; +text: .text%__1cZregDHi_regDLo_to_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubD_regD_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLVtableStubsOis_entry_point6FpC_i_; +text: .text%__1cOtypeArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlass.o; +text: .text%__1cPconvD2F_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciMethodMnative_entry6M_pC_; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cLVtableStubsScreate_vtable_stub6Fii_pnKVtableStub__; +text: .text%__1cVMoveF2I_stack_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPPerfDataManagerWcreate_string_variable6FnJCounterNS_pkci3pnGThread__pnSPerfStringVariable__; +text: .text%__1cQAbstractCompilerPsupports_native6M_i_: c2compiler.o; +text: .text%__1cPorL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvD2F_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIciSymbolHas_utf86M_pkc_; +text: .text%__1cQandI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQciTypeArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cMnegD_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cFStateO_sub_Op_CMoveP6MpknENode__v_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: typeArrayKlass.o; +text: .text%__1cSconvF2I_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulD_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerZtotal_frame_size_in_bytes6Mi_i_; +text: .text%__1cNTemplateTableQfast_accessfield6FnITosState__v_; +text: .text%__1cKCompiledICOis_megamorphic6kM_i_; +text: .text%__1cVMoveF2I_stack_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledICSset_to_megamorphic6MpnICallInfo_nJBytecodesECode_pnGThread__v_; +text: .text%Unsafe_StaticFieldOffset; +text: .text%__1cQmulI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableXresolve_cache_and_index6FipnMRegisterImpl_2_v_; +text: .text%__1cQaddI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLI_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassContext; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod__v_; +text: .text%Unsafe_StaticFieldBaseFromField; +text: .text%Unsafe_EnsureClassInitialized; +text: .text%__1cOcmovIF_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pCi_v_; +text: .text%__1cKCodeBufferQalloc_relocation6MI_v_; +text: .text%__1cNTemplateTableZjvmti_post_fast_field_mod6F_v_; +text: .text%__1cWloadConI_x43300000NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%Unsafe_GetObjectVolatile; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlassKlass.o; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2t6M_v_; +text: .text%__1cKstoreFNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFKlassMoop_is_array6kM_i_: objArrayKlassKlass.o; +text: .text%__1cVMoveL2D_stack_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadLNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cSmulL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHMulNodeGis_Mul6kM_pk0_: classes.o; +text: .text%__1cNSharedRuntimeVhandle_ic_miss_helper6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cOloadConL13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cKLoadPCNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTablePfast_storefield6FnITosState__v_; +text: .text%__1cGEventsDlog6FpkcE_v_: compiledIC.o; +text: .text%__1cLstoreF0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPconvI2D_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cZInterpreterMacroAssemblerNsuper_call_VM6MpnMRegisterImpl_22pC22i_v_; +text: .text%__1cNloadConPCNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cETypeFxdual6kM_pk0_; +text: .text%__1cJOopMapSetQsingular_oop_map6M_pnGOopMap__; +text: .text%__1cKimmU13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cbCAbstractInterpreterGeneratorXbang_stack_shadow_pages6Mi_v_; +text: .text%__1cZInterpreterMacroAssemblerSnotify_method_exit6MinITosState__v_; +text: .text%__1cSaddL_reg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerTnotify_method_entry6M_v_; +text: .text%__1cZInterpreterMacroAssemblerbCincrement_invocation_counter6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerQaccess_local_int6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerbDunlock_if_synchronized_method6MnITosState_ii_v_; +text: .text%__1cOtypeArrayKlassMcreate_klass6FnJBasicType_ipnGThread__pnMklassOopDesc__; +text: .text%__1cIGraphKitSgen_native_wrapper6MpnIciMethod__v_; +text: .text%__1cPconvI2L_regNodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerWempty_expression_stack6M_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_counter_incr6MpnFLabel_22_v_; +text: .text%__1cUInterpreterGeneratorZgenerate_counter_overflow6MirnFLabel__v_; +text: .text%__1cOcmovIL_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUInterpreterGeneratorbAgenerate_run_compiled_code6M_v_; +text: .text%__1cOCompilerThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%__1cUInterpreterGeneratorUgenerate_fixed_frame6Mi_v_; +text: .text%__1cCosHSolarisSset_signal_handler6Fiii_v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkci_pc_; +text: .text%__1cLOptoRuntimeRresolve_call_Type6F_pknITypeFunc__; +text: .text%__1cOtailjmpIndNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNMemoryManagerIadd_pool6MpnKMemoryPool__v_; +text: .text%__1cCosEstat6FpkcpnEstat__i_; +text: .text%__1cQregF_to_stkINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRComputeEntryStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cRComputeEntryStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cMMonitorChunk2t6Mi_v_; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cOPhaseIdealLoopJclone_iff6MpnHPhiNode_pnNIdealLoopTree__pnIBoolNode__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: callnode.o; +text: .text%__1cKReflectionVis_same_class_package6FpnMklassOopDesc_2_i_; +text: .text%__1cQComputeCallStackIdo_float6M_v_: generateOopMap.o; +text: .text%__1cMMonitorValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cPciObjArrayKlassJmake_impl6FpnHciKlass__p0_; +text: .text%__1cPorL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOptoRuntimeMrethrow_Type6F_pknITypeFunc__; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: methodDataOop.o; +text: .text%jni_SetStaticObjectField: jni.o; +text: .text%jni_RegisterNatives: jni.o; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: templateTable_sparc.o; +text: .text%__1cFframebLprevious_monitor_in_interpreter_frame6kMpnPBasicObjectLock__2_; +text: .text%__1cQshlL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_GetClassDeclaredFields; +text: .text%__1cCosMuser_handler6F_pv_; +text: .text%JVM_IsSameClassPackage; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: interpreter.o; +text: .text%__1cKMemoryPoolLadd_manager6MpnNMemoryManager__v_; +text: .text%__1cKJavaThreadRadd_monitor_chunk6MpnMMonitorChunk__v_; +text: .text%__1cKJavaThreadUremove_monitor_chunk6MpnMMonitorChunk__v_; +text: .text%__1cLcastP2INodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVMoveL2D_stack_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableGiconst6Fi_v_; +text: .text%__1cLConvF2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%JVM_LoadLibrary; +text: .text%JVM_IsSupportedJNIVersion; +text: .text%Unsafe_ObjectFieldOffset; +text: .text%__1cZInterpreterMacroAssemblerYtest_method_data_pointer6MrnFLabel__v_; +text: .text%__1cNTemplateTableHif_0cmp6Fn0AJCondition__v_; +text: .text%__1cHCompileRget_Method_invoke6M_pnIciMethod__; +text: .text%__1cZInterpreterMacroAssemblerSget_cpool_and_tags6MpnMRegisterImpl_2_v_; +text: .text%__1cIAddDNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cHCompileWget_MethodAccessorImpl6M_pnPciInstanceKlass__; +text: .text%__1cNTemplateTableHif_icmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableH_return6FnITosState__v_; +text: .text%__1cOPSVirtualSpaceJexpand_by6MI_i_; +text: .text%__1cHOrLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKimmP13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cLConvD2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSObjectSynchronizerJjni_enter6FnGHandle_pnGThread__v_; +text: .text%__1cHnmethodbJcontinuation_for_implicit_exception6MpC_1_; +text: .text%__1cNSharedRuntimeEdrem6Fdd_d_; +text: .text%__1cQstkI_to_regINodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cUInterpreterGeneratorbEgenerate_asm_interpreter_entry6Mi_pC_; +text: .text%__1cSstkL_to_regD_2NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cIAddDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSstkL_to_regD_0NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cPstoreI_FregNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTloadD_unalignedNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLVtableStubsIcontains6FpC_i_; +text: .text%__1cOloadI_fregNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLconvP2BNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cUInterpreterGeneratorbCgenerate_check_compiled_code6MrnFLabel__v_; +text: .text%__1cUInterpreterGeneratorbDgenerate_stack_overflow_check6MpnMRegisterImpl_22_v_; +text: .text%__1cCosZvm_allocation_granularity6F_i_; +text: .text%__1cMTailJumpNodeGOpcode6kM_i_; +text: .text%__1cPconvF2D_regNodeFclone6kM_pnENode__; +text: .text%__1cOLibraryCallKitbDis_method_invoke_or_aux_frame6MpnIJVMState__i_; +text: .text%__1cTloadD_unalignedNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHciKlass2t6MnLKlassHandle_pnIciSymbol__v_; +text: .text%__1cJMemRegion2t6M_v_: cardTableModRefBS.o; +text: .text%__1cSestimate_path_freq6FpnENode__f_: loopnode.o; +text: .text%__1cCosOreserve_memory6FIpc_1_; +text: .text%__1cSObjectSynchronizerIjni_exit6FpnHoopDesc_pnGThread__v_; +text: .text%__1cSmulL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNRegisterSaverWrestore_live_registers6FpnOMacroAssembler__v_; +text: .text%__1cKstfSSFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLTypeInstPtrOxmeet_unloaded6kMpk0_2_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: subnode.o; +text: .text%__1cRtestI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosNcommit_memory6FpcI_i_; +text: .text%__1cPPerfLongVariant2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_pnUPerfLongSampleHelper__v_; +text: .text%__1cWImplicitExceptionTable2t6MpknHnmethod__v_; +text: .text%__1cWImplicitExceptionTableCat6kMI_I_; +text: .text%__1cFParseVcatch_call_exceptions6MrnYciExceptionHandlerStream__v_; +text: .text%jni_GetJavaVM; +text: .text%__1cOcmovDF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_MonitorEnter: jni.o; +text: .text%__1cQConstantIntValuePis_constant_int6kM_i_: debugInfo.o; +text: .text%jni_MonitorExit: jni.o; +text: .text%__1cOMacroAssemblerDret6Mi_v_: templateTable_sparc.o; +text: .text%__1cLConvL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cULinearLeastSquareFit2t6MI_v_; +text: .text%__1cQdivL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPciObjectFactoryTget_unloaded_method6MpnPciInstanceKlass_pnIciSymbol_4_pnIciMethod__; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cOLibraryCallKitbBinline_native_currentThread6M_i_; +text: .text%__1cIciObjectMis_classless6kM_i_: ciMethod.o; +text: .text%__1cTGeneratePairingInfoOreport_results6kM_i_: ciMethod.o; +text: .text%__1cNReservedSpaceKfirst_part6MIii_0_; +text: .text%__1cNReservedSpace2t6MI_v_; +text: .text%__1cSCardTableExtensionVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cOloadI_fregNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRCardTableModRefBSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cIAddDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJloadFNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKConv2BNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvI2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSconvD2I_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_Throw: jni.o; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC_v_; +text: .text%__1cFTypeFGis_nan6kM_i_; +text: .text%__1cLMoveL2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKReturnNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cIDivINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cISubDNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cPstoreI_FregNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cINegFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQciByteCodeStreamXget_method_holder_index6M_i_; +text: .text%__1cOLibraryCallKitXgenerate_current_thread6MrpnENode__2_; +text: .text%__1cOMacroAssemblerEfneg6MnRFloatRegisterImplFWidth_p13_v_; +text: .text%__1cXNativeSignatureIteratorJdo_double6M_v_: interpreterRT_sparc.o; +text: .text%__1cRtestI_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSpaceCounters2t6MpkciIpnMMutableSpace_pnSGenerationCounters__v_; +text: .text%__1cLcmpF_ccNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMNativeLookupTbase_library_lookup6Fpkc22_pC_; +text: .text%jni_SetObjectField: jni.o; +text: .text%__1cISubDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cISubFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnQPerfLongVariable__; +text: .text%__1cHThreadsDadd6FpnKJavaThread_i_v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkc2i_pc_; +text: .text%__1cNStubGeneratorYgenerate_throw_exception6MpkcpCi_3_: stubGenerator_sparc.o; +text: .text%bootstrap_flush_windows; +text: .text%__1cMloadConPNodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerbCverify_oop_or_return_address6MpnMRegisterImpl_2_v_; +text: .text%__1cFStateO_sub_Op_Conv2B6MpknENode__v_; +text: .text%__1cQmodL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNRegisterSaverTsave_live_registers6FpnOMacroAssembler_ipi_pnGOopMap__; +text: .text%__1cSmulL_reg_reg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: symbolKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: symbolKlass.o; +text: .text%__1cIDivFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKMemoryPool2t6Mpkcn0AIPoolType_IIii_v_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_int_field06FnJBasicType__pC_; +text: .text%__1cKExceptionsK_throw_oop6FpnGThread_pkcipnHoopDesc__v_; +text: .text%__1cSsubL_reg_reg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_double6M_v_; +text: .text%__1cPciInstanceKlassbDcompute_shared_is_initialized6M_i_; +text: .text%__1cQmulD_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%Unsafe_AllocateMemory; +text: .text%__1cSandL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_GetLastErrorString; +text: .text%__1cQmodL_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableElop26Fn0AJOperation__v_; +text: .text%__1cQjava_lang_ThreadKset_daemon6FpnHoopDesc__v_; +text: .text%__1cNTemplateTableEfop26Fn0AJOperation__v_; +text: .text%__1cPstoreI_FregNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableEdop26Fn0AJOperation__v_; +text: .text%__1cSandI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNTemplateTablebAload_invoke_cp_cache_entry6FipnMRegisterImpl_22ii_v_; +text: .text%__1cMnegD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNciMethodKlassEmake6F_p0_; +text: .text%__1cOLibraryCallKitZinline_native_Class_query6MnIciMethodLIntrinsicId__i_; +text: .text%__1cNTemplateTableGlstore6Fi_v_; +text: .text%__1cOLibraryCallKitbNinline_native_Reflection_getCallerClass6M_i_; +text: .text%__1cLConvF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQaddI_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: frame.o; +text: .text%__1cIciMethod2t6MpnPciInstanceKlass_pnIciSymbol_4_v_; +text: .text%__1cRcompL_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cLconvI2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLConvD2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_2_v_; +text: .text%__1cSconvD2I_helperNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2D_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cLClassLoaderXcreate_class_path_entry6FpcnEstat_ppnOClassPathEntry__v_; +text: .text%__1cKstfSSFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOClassPathEntry2t6M_v_; +text: .text%__1cZInterpreterMacroAssemblerRprofile_checkcast6MipnMRegisterImpl_2_v_; +text: .text%__1cINegDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerQaccess_local_ptr6MpnMRegisterImpl_2_v_; +text: .text%__1cOloadConL13NodeFclone6kM_pnENode__; +text: .text%__1cNTemplateTableGistore6Fi_v_; +text: .text%__1cIRetTableUfind_jsrs_for_target6Mi_pnNRetTableEntry__; +text: .text%__1cPconvL2I_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarI_reg_imm5NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_2NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableGastore6Fi_v_; +text: .text%__1cNTemplateTableZload_field_cp_cache_entry6FipnMRegisterImpl_22i_v_; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: assembler_sparc.o; +text: .text%__1cIRetTableHadd_jsr6Mii_v_; +text: .text%__1cMnegF_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cQregF_to_stkINodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cRComputeEntryStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cPorL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulD_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cNTemplateTableGdstore6Fi_v_; +text: .text%__1cNTemplateTableGfstore6Fi_v_; +text: .text%jni_CallStaticObjectMethod: jni.o; +text: .text%__1cPconvD2F_regNodeFclone6kM_pnENode__; +text: .text%__1cbCAbstractInterpreterGeneratorbHgenerate_exception_handler_common6Mpkc2i_pC_; +text: .text%__1cLconvP2BNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLconvP2BNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLL_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQandI_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cMnegD_regNodeFclone6kM_pnENode__; +text: .text%__1cOMacroAssemblerJfloat_cmp6MiipnRFloatRegisterImpl_2pnMRegisterImpl__v_; +text: .text%__1cLconvI2BNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cODeoptimizationLUnrollBlockOsize_of_frames6kM_i_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceRefKlass.o; +text: .text%__1cCosGsignal6Fipv_1_; +text: .text%__1cQaddD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cISubDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cISubFNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cISubFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNflagsRegLOperFclone6kM_pnIMachOper__; +text: .text%__1cNTemplateTableFlload6Fi_v_; +text: .text%__1cNTemplateTableFiload6Fi_v_; +text: .text%__1cJcmpOpOperFclone6kM_pnIMachOper__; +text: .text%__1cMOopMapStream2t6MpnGOopMap_i_v_; +text: .text%__1cFTypeFFempty6kM_i_; +text: .text%__1cLconvP2BNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVMoveF2I_stack_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC22_v_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC2_v_; +text: .text%__1cTjava_lang_ThrowableLset_message6FpnHoopDesc_2_v_; +text: .text%__1cKstfSSFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapTret_jump_targets_do6MpnOBytecodeStream_pFp0ipi_vi4_v_; +text: .text%__1cPconvI2D_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%Unsafe_SetMemory; +text: .text%__1cSstkL_to_regD_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstfSSFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerOthrow_if_not_x6MnJAssemblerJCondition_pCpnMRegisterImpl__v_; +text: .text%__1cVMoveF2I_stack_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHTypePtrKadd_offset6kMi_pk0_; +text: .text%__1cOcmovLI_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConL0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKg1RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cOcmovPI_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovDF_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableJfloat_cmp6Fii_v_; +text: .text%__1cQsubF_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstkL_to_regD_1NodeFclone6kM_pnENode__; +text: .text%__1cFParseRjump_if_true_fork6MpnGIfNode_ii_v_; +text: .text%__1cZInterpreterMacroAssemblerQthrow_if_not_icc6MnJAssemblerJCondition_pCpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableFfload6Fi_v_; +text: .text%__1cFParsePdo_lookupswitch6M_v_; +text: .text%__1cNTemplateTableFdload6Fi_v_; +text: .text%__1cNgen_new_frame6FpnOMacroAssembler_i_v_: runtime_sparc.o; +text: .text%__1cKstfSSFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cINegDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTableFaload6Fi_v_; +text: .text%__1cRMachSpillCopyNodeHsize_of6kM_I_: ad_sparc.o; +text: .text%__1cQCompilerCounters2t6MpkcipnGThread__v_; +text: .text%__1cOGenerateOopMapRdo_multianewarray6Mii_v_; +text: .text%__1cQOopMapCacheEntryPfill_for_native6M_v_; +text: .text%__1cNCompileBrokerUcompiler_thread_loop6F_v_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%jni_CallStaticObjectMethodV: jni.o; +text: .text%__1cNTemplateTableMfast_xaccess6FnITosState__v_; +text: .text%__1cIDivDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMloadConDNodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMemRegionFminus6kMk0_0_; +text: .text%__1cNCompileBrokerUmake_compiler_thread6FpkcpnMCompileQdDueue_pnQCompilerCounters_pnGThread__pnOCompilerThread__; +text: .text%__1cJArgumentsMbuild_string6Fppcpkc_v_; +text: .text%__1cSInterpreterRuntimebKthrow_ArrayIndexOutOfBoundsException6FpnKJavaThread_pci_v_; +text: .text%__1cOtailjmpIndNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNMemoryManager2t6M_v_; +text: .text%__1cFStatebB_sub_Op_PartialSubtypeCheck6MpknENode__v_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_22_v_; +text: .text%__1cVMoveL2D_stack_regNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cJArgumentsMadd_property6Fpkc_i_; +text: .text%__1cOtailjmpIndNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSObjectSynchronizerHinflate6FpnHoopDesc__pnNObjectMonitor__; +text: .text%__1cFStateM_sub_Op_DivI6MpknENode__v_; +text: .text%__1cCosHSolarisOis_sig_ignored6Fi_i_; +text: .text%__1cUPSGenerationCounters2t6MpkciipnOPSVirtualSpace__v_; +text: .text%__1cCosFyield6F_v_; +text: .text%__1cLOptoRuntimeVgenerate_handler_blob6FpCi_pnNSafepointBlob__; +text: .text%__1cKstfSSFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: runtime_sparc.o; +text: .text%__1cQsubD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXNativeSignatureIteratorIdo_float6M_v_: interpreterRT_sparc.o; +text: .text%__1cOtailjmpIndNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cIDivDNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFParseRdo_multianewarray6M_v_; +text: .text%__1cLOptoRuntimeTmultianewarray_Type6Fi_pknITypeFunc__; +text: .text%__1cZInterpreterMacroAssemblerRget_constant_pool6MpnMRegisterImpl__v_; +text: .text%__1cXPartialSubtypeCheckNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOcmovIF_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNFingerprinterLfingerprint6M_X_: oopMapCache.o; +text: .text%__1cLMoveF2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSconvI2D_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveF2I_stack_regNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cLstoreF0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerLlock_object6MpnMRegisterImpl_2_v_; +text: .text%__1cPstoreI_FregNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSCommandLineFlagsExJboolAtPut6FnXCommandLineFlagWithType_i_v_; +text: .text%__1cSCommandLineFlagsExKis_default6FnPCommandLineFlag__i_; +text: .text%__1cOcmovLL_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_i2_v_; +text: .text%__1cRNativeInstructionPis_ic_miss_trap6M_i_; +text: .text%__1cNSafepointBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cMciArrayKlassRbase_element_type6M_pnGciType__; +text: .text%JVM_GetInterfaceVersion; +text: .text%__1cZInterpreterMacroAssemblerRgen_subtype_check6MpnMRegisterImpl_2222rnFLabel__v_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableGfconst6Fi_v_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: stubGenerator_sparc.o; +text: .text%__1cOCompilerThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cGThreadbFinitialize_thread_local_storage6M_v_; +text: .text%__1cOcmovPI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGThreadbArecord_stack_base_and_size6M_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC2_v_; +text: .text%JVM_RegisterSignal; +text: .text%JVM_FindSignal; +text: .text%__1cTMaskFillerForNative2t6MnMmethodHandle_pIi_v_: oopMapCache.o; +text: .text%jio_vsnprintf; +text: .text%__1cQshrL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_i22_v_; +text: .text%__1cZInterpreterMacroAssemblerTprofile_switch_case6MpnMRegisterImpl_222_v_; +text: .text%__1cNReservedSpaceJlast_part6MI_0_; +text: .text%__1cOCompilerThread2t6MpnMCompileQdDueue_pnQCompilerCounters__v_; +text: .text%__1cOPSVirtualSpace2t6MnNReservedSpace_I_v_; +text: .text%__1cFTypeDFempty6kM_i_; +text: .text%__1cVcompiler_thread_entry6FpnKJavaThread_pnGThread__v_: thread.o; +text: .text%__1cNIdealLoopTreeUmerge_many_backedges6MpnOPhaseIdealLoop__v_; +text: .text%__1cODeoptimizationLUnrollBlock2T6M_v_; +text: .text%jni_GetDoubleArrayRegion: jni.o; +text: .text%__1cHciKlassIis_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cMLinkResolverbBlookup_method_in_interfaces6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cLconvP2BNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKfix_parent6FpnNIdealLoopTree_1_v_: loopnode.o; +text: .text%__1cZInterpreterMacroAssemblerUadd_monitor_to_stack6MipnMRegisterImpl_2_v_; +text: .text%JVM_Available; +text: .text%__1cZInterpreterMacroAssemblerSprofile_final_call6MpnMRegisterImpl__v_; +text: .text%__1cQshlL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMOopTaskQdDueueKinitialize6M_v_; +text: .text%__1cMOopTaskQdDueue2t6M_v_; +text: .text%__1cRNativeInstructionKis_illegal6M_i_; +text: .text%__1cZInterpreterMacroAssemblerQtop_most_monitor6M_nHAddress__; +text: .text%__1cLstoreF0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosGgetenv6Fpkcpci_i_; +text: .text%__1cMVM_OperationNdoit_prologue6M_i_: vm_operations.o; +text: .text%__1cZInterpreterMacroAssemblerWprofile_switch_default6MpnMRegisterImpl__v_; +text: .text%__1cKi0RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cTAbstract_VM_VersionOvm_info_string6F_pkc_; +text: .text%__1cPOopTaskQdDueueSetOregister_queue6MipnMOopTaskQdDueue__v_; +text: .text%__1cMVM_OperationNdoit_epilogue6M_v_: vm_operations.o; +text: .text%__1cNVM_DeoptimizeEname6kM_pkc_: vm_operations.o; +text: .text%__1cJStubQdDueue2t6MpnNStubInterface_ipnFMutex_pkc_v_; +text: .text%__1cSconvF2I_helperNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNloadConP0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHThreadsbFdeoptimized_wrt_marked_nmethods6F_v_; +text: .text%__1cbAconvL2D_reg_slow_fxtofNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOstackSlotFOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cNTemplateTableSputfield_or_static6Fii_v_; +text: .text%__1cOstackSlotFOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotFOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cPconvF2I_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableSgetfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableUjvmti_post_field_mod6Fii_v_; +text: .text%__1cHMatcherQconvL2FSupported6F_ki_; +text: .text%__1cNTemplateTableGlconst6Fi_v_; +text: .text%__1cLstoreC0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRcompL_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cUcompI_iReg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cMPeriodicTaskGenroll6M_v_; +text: .text%__1cMPeriodicTask2t6MI_v_; +text: .text%__1cPconvF2I_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFTypeDJis_finite6kM_i_; +text: .text%__1cPconvL2I_regNodeFclone6kM_pnENode__; +text: .text%__1cNTemplateTableHcastore6F_v_; +text: .text%Unsafe_CompareAndSwapObject; +text: .text%__1cLNamedThread2t6M_v_; +text: .text%__1cSconvD2I_helperNodeFclone6kM_pnENode__; +text: .text%__1cLNamedThreadIset_name6MpkcE_v_; +text: .text%__1cJloadDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQdivD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWloadConI_x43300000NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableKinitialize6F_v_; +text: .text%__1cKcmpOpFOperJnot_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cPconvD2F_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableKdouble_cmp6Fi_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fi_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC22_v_; +text: .text%__1cNTemplateTableGdconst6Fi_v_; +text: .text%__1cNTemplateTableDldc6Fi_v_; +text: .text%__1cSconvF2I_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovIF_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovIF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: universe.o; +text: .text%__1cJimmL0OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cLcastP2INodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovLL_regNodeFclone6kM_pnENode__; +text: .text%__1cbAconvL2D_reg_slow_fxtofNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLconvP2BNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddD_regD_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSsubD_regD_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQregF_to_stkINodeFclone6kM_pnENode__; +text: .text%__1cSstkL_to_regD_2NodeFclone6kM_pnENode__; +text: .text%__1cQregF_to_stkINodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNTemplateTableTinvokevfinal_helper6FpnMRegisterImpl_2_v_; +text: .text%__1cSmulD_regD_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableUgenerate_vtable_call6FpnMRegisterImpl_22_v_; +text: .text%__1cSstkL_to_regD_2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstkL_to_regD_0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableKif_nullcmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_acmp6Fn0AJCondition__v_; +text: .text%__1cSmembar_releaseNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJimmI0OperFclone6kM_pnIMachOper__; +text: .text%__1cNVM_DeoptimizeEdoit6M_v_; +text: .text%__1cNloadConL0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeFclone6kM_pnENode__; +text: .text%__1cQsubL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMVirtualSpace2t6M_v_; +text: .text%__1cRsarI_reg_imm5NodeFclone6kM_pnENode__; +text: .text%__1cWloadConI_x41f00000NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQdivI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMVirtualSpaceKinitialize6MnNReservedSpace_I_i_; +text: .text%__1cZregDHi_regDLo_to_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsubI_zero_regNodeFclone6kM_pnENode__; +text: .text%__1cXconvI2D_regDHi_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKloadUBNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNTemplateTableEidiv6F_v_; +text: .text%__1cOcmovLI_regNodeFclone6kM_pnENode__; +text: .text%__1cQstkI_to_regINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUregI_to_stkLHi_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLMoveL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLConvD2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLConvF2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLConvF2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_float_field06FnJBasicType__pC_; +text: .text%__1cLMoveF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLOptoRuntimeIl2f_Type6F_pknITypeFunc__; +text: .text%__1cOMacroAssemblerUcalc_mem_param_words6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerLprofile_ret6MnITosState_pnMRegisterImpl_3_v_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_virtual_call6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerMprofile_call6MpnMRegisterImpl__v_; +text: .text%__1cLklassVtableQindex_of_miranda6MpnNsymbolOopDesc_2_i_; +text: .text%__1cZInterpreterMacroAssemblerSupdate_mdp_for_ret6MnITosState_pnMRegisterImpl__v_; +text: .text%__1cOLibraryCallKitVinline_fp_conversions6MnIciMethodLIntrinsicId__i_; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_flag_at6MipnMRegisterImpl_2_v_; +text: .text%__1cMLinkResolverbEvtable_index_of_miranda_method6FnLKlassHandle_nMsymbolHandle_2pnGThread__i_; +text: .text%__1cUInterpreterGeneratorLlock_method6M_v_; +text: .text%__1cZInterpreterMacroAssemblerOthrow_if_not_26MpCpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerQthrow_if_not_1_x6MnJAssemblerJCondition_rnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerZget_4_byte_integer_at_bcp6MipnMRegisterImpl_2n0AKsetCCOrNot__v_; +text: .text%__1cUInterpreterGeneratorVgenerate_native_entry6Mi_pC_; +text: .text%__1cCosHrealloc6FpvI_1_; +text: .text%__1cUConstantOopReadValuePis_constant_oop6kM_i_: debugInfo.o; +text: .text%__1cKScopeValuePis_constant_int6kM_i_: debugInfo.o; +text: .text%__1cODeoptimizationVdeoptimize_dependents6F_i_; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: arrayKlassKlass.o; +text: .text%__1cFStateO_sub_Op_CMoveL6MpknENode__v_; +text: .text%__1cZInterpreterMacroAssemblerRaccess_local_long6MpnMRegisterImpl_2_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cIPSOldGenPinitialize_work6Mpkci_v_; +text: .text%__1cCosIjvm_path6Fpci_v_; +text: .text%__1cCosNsigexitnum_pd6F_i_; +text: .text%__1cCosScurrent_process_id6F_i_; +text: .text%__1cINegFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cCosPuncommit_memory6FpcI_i_; +text: .text%__1cSInterpreterRuntimeMat_safepoint6FpnKJavaThread__v_; +text: .text%__1cLConvL2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvF2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvD2FNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKJNIHandlesQmake_weak_global6FnGHandle__pnI_jobject__; +text: .text%__1cZInterpreterMacroAssemblerSaccess_local_float6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerTaccess_local_double6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerPstore_local_int6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerPstore_local_ptr6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerQstore_local_long6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerRstore_local_float6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerSstore_local_double6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cCosWactive_processor_count6F_i_; +text: .text%__1cSReferenceProcessor2t6MnJMemRegion_iii_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: jniFastGetField_sparc.o; +text: .text%__1cRcheck_if_clipping6FpknKRegionNode_rpnGIfNode_5_i_: cfgnode.o; +text: .text%__1cTAbstractInterpreterKinitialize6F_v_; +text: .text%__1cIciObjectOis_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstanceKlass.o; +text: .text%__1cPmake_new_frames6FpnOMacroAssembler_i_v_: runtime_sparc.o; +text: .text%jni_NewWeakGlobalRef: jni.o; +text: .text%__1cGatomll6Fpkcpx_i_: arguments.o; +text: .text%__1cRComputeEntryStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cTMaskFillerForNativeLpass_object6M_v_: oopMapCache.o; +text: .text%__1cOGenerateOopMapTadd_to_ref_init_set6Mi_v_; +text: .text%__1cUGcThreadCountClosureJdo_thread6MpnGThread__v_; +text: .text%__1cNinstanceKlassSremove_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cOPSVirtualSpace2t6M_v_; +text: .text%jni_IsInstanceOf: jni.o; +text: .text%__1cGThreadMis_VM_thread6kM_i_: gcTaskThread.o; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: oopMapCache.o; +text: .text%__1cMGCTaskThreadDrun6M_v_; +text: .text%__1cJCodeCachebGmake_marked_nmethods_not_entrant6F_v_; +text: .text%__1cTMaskFillerForNativeJpass_long6M_v_: oopMapCache.o; +text: .text%jni_CallStaticVoidMethodV: jni.o; +text: .text%__1cKCodeBufferGresize6M_v_; +text: .text%jni_CallStaticBooleanMethod: jni.o; +text: .text%__1cMGCTaskThread2t6MpnNGCTaskManager_II_v_; +text: .text%__1cOtailjmpIndNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMGCTaskThreadFstart6M_v_; +text: .text%__1cNStubGenerator2t6MpnKCodeBuffer_i_v_: stubGenerator_sparc.o; +text: .text%__1cWStubGenerator_generate6FpnKCodeBuffer_i_v_; +text: .text%__1cRFloatRegisterImplIencoding6kMn0AFWidth__i_: interpreter_sparc.o; +text: .text%__1cCosbCis_thread_cpu_time_supported6F_i_; +text: .text%__1cJArgumentsXPropertyList_unique_add6FppnOSystemProperty_pkcpc_v_; +text: .text%__1cQObjectStartArrayKinitialize6MnJMemRegion__v_; +text: .text%__1cQObjectStartArraySset_covered_region6MnJMemRegion__v_; +text: .text%__1cUGenericGrowableArrayGgrow646Mi_v_; +text: .text%__1cZInterpreterMacroAssemblerbAdispatch_next_noverify_oop6MnITosState_i_v_; +text: .text%__1cOMacroAssemblerDret6Mi_v_: stubGenerator_sparc.o; +text: .text%__1cRCollectorCounters2t6Mpkci_v_; +text: .text%__1cFParseDl2f6M_v_; +text: .text%__1cPGCMemoryManagerXinitialize_gc_stat_info6M_v_; +text: .text%__1cJArgumentsVset_parallel_gc_flags6F_v_; +text: .text%__1cPGCMemoryManager2t6M_v_; +text: .text%__1cRComputeEntryStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cOPSVirtualSpaceKinitialize6MnNReservedSpace_I_i_; +text: .text%__1cSInterpreterRuntimeWcreate_klass_exception6FpnKJavaThread_pcpnHoopDesc__v_; +text: .text%__1cQcreate_os_thread6FpnGThread_I_pnIOSThread__: os_solaris.o; +text: .text%__1cYjava_lang_reflect_MethodPcompute_offsets6F_v_; +text: .text%__1cSInterpreterRuntimeSupdate_mdp_for_ret6FpnKJavaThread_i_v_; +text: .text%__1cPorL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQjava_lang_ThreadPcompute_offsets6F_v_; +text: .text%__1cXNativeSignatureIteratorHdo_byte6M_v_: interpreterRT_sparc.o; +text: .text%__1cCosHSolarisQsignal_sets_init6F_v_; +text: .text%__1cCosScreate_main_thread6FpnGThread__i_; +text: .text%__1cCosbDallocate_thread_local_storage6F_i_; +text: .text%__1cUInterpreterGeneratorVrestore_native_result6M_v_; +text: .text%__1cVjava_lang_ThreadGroupPcompute_offsets6F_v_; +text: .text%__1cHMatcherbDinterpreter_frame_pointer_reg6F_nHOptoRegEName__; +text: .text%__1cLconvP2BNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVshrL_reg_imm6_L2INodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cCosGstrdup6Fpkc_pc_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_throw_exception6M_v_; +text: .text%__1cCosLinit_random6Fl_v_; +text: .text%__1cCosNset_boot_path6Fcc_i_; +text: .text%__1cUInterpreterGeneratorXgenerate_accessor_entry6M_pC_; +text: .text%__1cCosXis_server_class_machine6F_i_; +text: .text%__1cCosXterminate_signal_thread6F_v_; +text: .text%__1cCosLsignal_init6F_v_; +text: .text%__1cKTypeOopPtrCeq6kMpknEType__i_; +text: .text%__1cTsignal_thread_entry6FpnKJavaThread_pnGThread__v_: os.o; +text: .text%__1cOtailjmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUInterpreterGeneratorUgenerate_empty_entry6M_pC_; +text: .text%__1cUInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cbCAbstractInterpreterGeneratorbMgenerate_ArrayIndexOutOfBounds_handler6Mpkc_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_StackOverflowError_handler6M_pC_; +text: .text%__1cCosbDinit_system_properties_values6F_v_; +text: .text%__1cCosPphysical_memory6F_X_; +text: .text%__1cHvm_exit6Fi_v_; +text: .text%__1cLbefore_exit6FpnKJavaThread__v_; +text: .text%__1cbCAbstractInterpreterGeneratorbFgenerate_slow_signature_handler6M_pC_; +text: .text%__1cSThreadLocalStorageHpd_init6F_v_; +text: .text%__1cVMoveF2I_stack_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveL2D_stack_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWinvocationCounter_init6F_v_; +text: .text%__1cRInvocationCounterMreinitialize6Fi_v_; +text: .text%__1cKTypeOopPtrEmake6FnHTypePtrDPTR_i_pk0_; +text: .text%__1cKTypeOopPtrFxdual6kM_pknEType__; +text: .text%__1cFParseMjump_if_join6MpnENode_2_2_; +text: .text%__1cSinstanceKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cSinstanceKlassKlassUoop_is_instanceKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: instanceKlassKlass.o; +text: .text%__1cSinstanceKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlassKlass.o; +text: .text%__1cLconvP2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cETypeRInitialize_shared6FpnHCompile__v_; +text: .text%__1cQinstanceRefKlassZupdate_nonstatic_oop_maps6FpnMklassOopDesc__v_; +text: .text%__1cVInterfaceSupport_init6F_v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpnMRegisterImpl_pC2_v_; +text: .text%__1cFParseNfetch_monitor6MipnENode_2_2_; +text: .text%__1cPGenerationSizerQinitialize_flags6M_v_: parallelScavengeHeap.o; +text: .text%__1cbCTwoGenerationCollectorPolicyMrem_set_name6M_nJGenRemSetEName__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapEkind6M_nNCollectedHeapEName__: parallelScavengeHeap.o; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_normal6MnITosState__v_; +text: .text%__1cJTimeStampMmilliseconds6kM_x_; +text: .text%__1cDhpiZinitialize_socket_library6F_i_; +text: .text%__1cDhpiYinitialize_get_interface6FpnIvm_calls__v_; +text: .text%__1cWInlineCacheBuffer_init6F_v_; +text: .text%__1cWThreadLocalAllocBufferWstartup_initialization6F_v_; +text: .text%__1cPGlobalTLABStats2t6M_v_; +text: .text%__1cLicache_init6F_v_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: parse1.o; +text: .text%__1cSThreadLocalStorageEinit6F_v_; +text: .text%__1cNThreadServiceEinit6F_v_; +text: .text%__1cTICacheStubGeneratorVgenerate_icache_flush6MppFpCii_i_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: indexSet.o; +text: .text%__1cPvm_init_globals6F_v_; +text: .text%__1cMinit_globals6F_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_sparc_expand.o; +text: .text%__1cMexit_globals6F_v_; +text: .text%__1cSset_init_completed6F_v_; +text: .text%__1cNinstanceKlassZrelease_C_heap_structures6M_v_; +text: .text%__1cJTimeStampJupdate_to6Mx_v_; +text: .text%__1cUParallelScavengeHeapItop_addr6kM_ppnIHeapWord__: parallelScavengeHeap.o; +text: .text%__1cOisT2_libthread6F_i_; +text: .text%__1cCosHSolarisXinstall_signal_handlers6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interp_masm_sparc.o; +text: .text%__1cQinterpreter_init6F_v_; +text: .text%__1cbCAbstractInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cRlwp_priocntl_init6F_i_: os_solaris.o; +text: .text%__1cNpriocntl_stub6FinGidtype_lipc_l_: os_solaris.o; +text: .text%__1cbCAbstractInterpreterGeneratorMgenerate_all6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbEset_entry_points_for_all_bytes6M_v_; +text: .text%__1cCosHSolarisRmpss_sanity_check6F_v_; +text: .text%__1cCosLsignal_wait6F_i_; +text: .text%__1cVcheck_pending_signals6Fi_i_: os_solaris.o; +text: .text%__1cCosNsignal_notify6Fi_v_; +text: .text%__1cCosOsignal_init_pd6F_v_; +text: .text%__1cCosHSolarisPinit_signal_mem6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter.o; +text: .text%__1cCosSget_temp_directory6F_pkc_; +text: .text%__1cCosHSolarisOlibthread_init6F_v_; +text: .text%__1cUParallelScavengeHeapIend_addr6kM_ppnIHeapWord__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapEheap6F_p0_; +text: .text%__1cUParallelScavengeHeapNgc_threads_do6kMpnNThreadClosure__v_; +text: .text%__1cUParallelScavengeHeapYpermanent_object_iterate6MpnNObjectClosure__v_; +text: .text%__1cKcmpOpFOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cUParallelScavengeHeapMmax_capacity6kM_I_; +text: .text%__1cUParallelScavengeHeapPpost_initialize6M_v_; +text: .text%__1cUParallelScavengeHeapKinitialize6M_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: parGCAllocBuffer.o; +text: .text%__1cZInterpreterMacroAssemblerbFset_method_data_pointer_for_bcp6M_v_; +text: .text%__SLIP.DELETER__C: ostream.o; +text: .text%__1cMostream_exit6F_v_; +text: .text%__1cQostream_init_log6F_v_; +text: .text%__1cMostream_init6F_v_; +text: .text%__1cCosXnon_memory_address_word6F_pc_; +text: .text%__1cCosGinit_26F_i_; +text: .text%__1cCosEinit6F_v_; +text: .text%__1cCosHSolarisUsynchronization_init6F_v_; +text: .text%__1cVjni_GetLongField_addr6F_pC_; +text: .text%__1cNIdealLoopTreeQsplit_outer_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cRLowMemoryDetectorKinitialize6F_v_; +text: .text%__1cNReservedSpace2t6MIIipc_v_; +text: .text%__1cRLowMemoryDetectorbGlow_memory_detector_thread_entry6FpnKJavaThread_pnGThread__v_; +text: .text%__1cXLowMemoryDetectorThreadbCis_hidden_from_external_view6kM_i_: lowMemoryDetector.o; +text: .text%__1cNReservedSpaceUpage_align_size_down6FI_I_; +text: .text%__1cNReservedSpaceYallocation_align_size_up6FI_I_; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cGThreadMis_VM_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cTloadL_unalignedNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: machnode.o; +text: .text%__1cPmanagement_init6F_v_; +text: .text%__1cOvmStructs_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vmStructs.o; +text: .text%__1cJvmSymbolsKinitialize6FpnGThread__v_; +text: .text%__1cKManagementKinitialize6FpnGThread__v_; +text: .text%__1cKManagementWrecord_vm_startup_time6Fxx_v_; +text: .text%__1cIVMThreadGcreate6F_v_; +text: .text%__1cIVMThreadDrun6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: management.o; +text: .text%__1cOBasicHashtable2t6Mii_v_: loaderConstraints.o; +text: .text%__1cLJvmtiExportNpost_vm_start6F_v_; +text: .text%__1cLJvmtiExportTpost_vm_initialized6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_death6F_v_; +text: .text%__1cLJvmtiExportbMtransition_pending_onload_raw_monitors6F_v_; +text: .text%__1cUJvmtiPendingMonitorsXtransition_raw_monitors6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiImpl.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiTagMap.o; +text: .text%__1cKklassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassUoop_is_instanceKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: klassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: klassKlass.o; +text: .text%__1cKklassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: klassKlass.o; +text: .text%__1cOLibraryCallKitWinline_native_hashcode6Mii_i_; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: library_call.o; +text: .text%__1cVLoaderConstraintTable2t6Mi_v_; +text: .text%__1cQregL_to_stkLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHRetDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cTAbstract_VM_VersionKvm_release6F_pkc_; +text: .text%__1cTAbstract_VM_VersionXinternal_vm_info_string6F_pkc_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vm_version.o; +text: .text%__1cPVM_Version_init6F_v_; +text: .text%__1cKVM_VersionKinitialize6F_v_; +text: .text%__1cHRetDataJfixup_ret6MinQmethodDataHandle__pC_; +text: .text%__1cHRetDataKis_RetData6M_i_: methodDataOop.o; +text: .text%__1cLmethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cLmethodKlassOset_alloc_size6MI_v_: methodKlass.o; +text: .text%__1cQvtableStubs_init6F_v_; +text: .text%__1cKi0RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKg1RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFVTuneEexit6F_v_; +text: .text%__1cLmethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodKlass.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: methodLiveness.o; +text: .text%__1cMMutableSpaceOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cKvtune_init6F_v_; +text: .text%__1cKmutex_init6F_v_; +text: .text%__1cQaccessFlags_init6F_v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC222_v_; +text: .text%__1cTAbstract_VM_VersionJvm_vendor6F_pkc_; +text: .text%__1cOmarksweep_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: markSweep.o; +text: .text%__1cHMatcherVfind_callee_arguments6FpnNsymbolOopDesc_ipi_pnLRegPair__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: matcher.o; +text: .text%__1cNMemoryManagerbDget_code_cache_memory_manager6F_p0_; +text: .text%__1cNMemoryManagerbDget_psScavenge_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cNMemoryManagerbEget_psMarkSweep_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cQPSGenerationPool2t6MpnIPSOldGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cQPSGenerationPool2t6MpnJPSPermGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cUEdenMutableSpacePool2t6MpnKPSYoungGen_pnMMutableSpace_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cYSurvivorMutableSpacePool2t6MpnKPSYoungGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cMCodeHeapPool2t6MpnICodeHeap_pkci_v_; +text: .text%__1cHVM_ExitEdoit6M_v_; +text: .text%__1cHVM_ExitEname6kM_pkc_: vm_operations.o; +text: .text%__1cNMemoryServiceRset_universe_heap6FpnNCollectedHeap__v_; +text: .text%__1cNMemoryServiceZadd_code_heap_memory_pool6FpnICodeHeap__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: memoryService.o; +text: .text%__1cPmethodDataKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cPmethodDataKlassOset_alloc_size6MI_v_: methodDataKlass.o; +text: .text%__1cPmethodDataKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodDataKlass.o; +text: .text%__1cTAbstract_VM_VersionHvm_name6F_pkc_; +text: .text%__1cLstoreF0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%JNI_CreateJavaVM; +text: .text%__1cXonStackReplacement_init6F_v_; +text: .text%__1cQJNI_FastGetFieldbFgenerate_fast_get_boolean_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_byte_field6F_pC_; +text: .text%__1cTtypeArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cTtypeArrayKlassKlassVoop_is_typeArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_char_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_short_field6F_pC_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cQJNI_FastGetFieldbBgenerate_fast_get_int_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_long_field6F_pC_; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: typeArrayKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlassKlass.o; +text: .text%__1cIUniversePcheck_alignment6FIIpkc_v_; +text: .text%__1cIUniverseHgenesis6FpnGThread__v_; +text: .text%__1cVquicken_jni_functions6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: oopMap.o; +text: .text%__1cYjava_lang_reflect_MethodNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cQdivD_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLJavaClassesbAcompute_hard_coded_offsets6F_v_; +text: .text%__1cQjavaClasses_init6F_v_; +text: .text%jni_ToReflectedMethod: jni.o; +text: .text%__1cQsubD_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cYjni_GetBooleanField_addr6F_pC_; +text: .text%__1cVjni_GetByteField_addr6F_pC_; +text: .text%__1cQaddF_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cVjni_GetCharField_addr6F_pC_; +text: .text%__1cWjni_GetShortField_addr6F_pC_; +text: .text%__1cUjni_GetIntField_addr6F_pC_; +text: .text%__1cOtypeArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cWjni_GetFloatField_addr6F_pC_; +text: .text%__1cRsarL_reg_imm6NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cXjni_GetDoubleField_addr6F_pC_; +text: .text%__1cQshlI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIUniverseNfixup_mirrors6FpnGThread__v_; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: objArrayKlass.o; +text: .text%JVM_InitializeSocketLibrary; +text: .text%JVM_RegisterUnsafeMethods; +text: .text%__1cOcmovLI_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovLI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovDF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_Socket; +text: .text%__1cbEinitialize_converter_functions6F_v_; +text: .text%JVM_SupportsCX8; +text: .text%__1cOcmovIF_immNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEnvBase.o; +text: .text%__1cUJvmtiEventControllerIvm_start6F_v_; +text: .text%__1cUJvmtiEventControllerHvm_init6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_death6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEventController.o; +text: .text%__1cKstfSSFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVverificationType_init6F_v_; +text: .text%__1cVverificationType_exit6F_v_; +text: .text%__1cLJvmtiExportRenter_start_phase6F_v_; +text: .text%__1cLJvmtiExportQenter_live_phase6F_v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlass.o; +text: .text%__1cSmulL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_float_field6F_pC_; +text: .text%__1cSmulI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_double_field6F_pC_; +text: .text%__1cNuniverse_init6F_i_; +text: .text%__1cOuniverse2_init6F_v_; +text: .text%__1cSuniverse_post_init6F_v_; +text: .text%__1cQjni_handles_init6F_v_; +text: .text%__1cSobjArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: objArrayKlassKlass.o; +text: .text%Unsafe_SetNativeLong; +text: .text%JVM_InitProperties; +text: .text%JVM_Halt; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlassKlass.o; +text: .text%Unsafe_FreeMemory; +text: .text%Unsafe_PageSize; +text: .text%__1cSobjArrayKlassKlassUoop_is_objArrayKlass6kM_i_: objArrayKlassKlass.o; +text: .text%JVM_MaxMemory; +text: .text%__1cSobjArrayKlassKlassbEallocate_system_objArray_klass6MpnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%JVM_GetClassDeclaredMethods; +text: .text%__1cPPerfDataManagerHsampled6F_pnMPerfDataList__; +text: .text%__1cQSystemDictionaryKclasses_do6FpFpnMklassOopDesc__v_v_; +text: .text%__1cQSystemDictionaryKinitialize6FpnGThread__v_; +text: .text%__1cQSystemDictionarybCinitialize_preloaded_classes6FpnGThread__v_; +text: .text%__1cQSystemDictionarybDinitialize_basic_type_mirrors6FpnGThread__v_; +text: .text%__1cPciObjectFactoryTinit_shared_objects6M_v_; +text: .text%__1cRciArrayKlassKlassUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cPClassFileParserbFjava_lang_ref_Reference_fix_pre6MpnPtypeArrayHandle_nSconstantPoolHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cLClassLoaderbBsetup_bootstrap_search_path6F_v_; +text: .text%__1cLClassLoaderQload_zip_library6F_v_; +text: .text%__1cLClassLoaderZcreate_package_info_table6F_v_; +text: .text%__1cLClassLoaderKinitialize6F_v_; +text: .text%__1cLClassLoaderVcompute_Object_vtable6F_i_; +text: .text%__1cMPeriodicTask2T5B6M_v_; +text: .text%__1cQclassLoader_init6F_v_; +text: .text%__1cMPeriodicTaskJdisenroll6M_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: classLoader.o; +text: .text%__1cTClassLoadingServiceEinit6F_v_; +text: .text%__1cTClassLoadingServiceVnotify_class_unloaded6FpnNinstanceKlass_i_v_; +text: .text%__1cMFastLockNodeLis_FastLock6kM_pk0_: classes.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: regmask.o; +text: .text%__1cUciObjArrayKlassKlassEmake6F_p0_; +text: .text%__1cLOptoRuntimeUmultianewarray1_Type6F_pknITypeFunc__; +text: .text%__1cVRegistersForDebuggingRrestore_registers6FpnOMacroAssembler_pnMRegisterImpl__v_: assembler_sparc.o; +text: .text%__1cVRegistersForDebuggingOsave_registers6FpnOMacroAssembler__v_: assembler_sparc.o; +text: .text%__1cJBytecodesKinitialize6F_v_; +text: .text%__1cQSystemDictionarybAcompute_java_system_loader6FpnGThread__v_; +text: .text%__1cObytecodes_init6F_v_; +text: .text%__1cLOptoRuntimeIgenerate6FpnFciEnv__v_; +text: .text%__1cJBytecodesNpd_initialize6F_v_; +text: .text%__1cHCompileRpd_compiler2_init6F_v_; +text: .text%__1cKC2CompilerKinitialize6M_v_; +text: .text%__1cMTailJumpNode2t6MpnENode_22222_v_; +text: .text%__1cRCardTableModRefBS2t6MnJMemRegion_i_v_; +text: .text%__1cWResolveOopMapConflictsOreport_results6kM_i_: rewriter.o; +text: .text%__1cRCardTableModRefBSbBct_max_alignment_constraint6F_I_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: cfgnode.o; +text: .text%__1cMciArrayKlass2t6MpnIciSymbol_ipnHciKlass__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: relocInfo.o; +text: .text%__1cMciKlassKlassEmake6F_p0_; +text: .text%__1cIciMethodMvtable_index6M_i_; +text: .text%__1cPciObjArrayKlass2t6MpnIciSymbol_pnHciKlass_i_v_; +text: .text%__1cJLoadFNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cNTemplateTableGsipush6F_v_; +text: .text%__1cQUncommonTrapBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNTemplateTableGldc2_w6F_v_; +text: .text%__1cNExceptionBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNTemplateTableFiload6F_v_; +text: .text%__1cNTemplateTableLfast_iload26F_v_; +text: .text%__1cNTemplateTableKfast_iload6F_v_; +text: .text%__1cNTemplateTableFlload6F_v_; +text: .text%__1cNTemplateTableFfload6F_v_; +text: .text%__1cNTemplateTableFdload6F_v_; +text: .text%__1cNTemplateTableFaload6F_v_; +text: .text%__1cNTemplateTableKwide_iload6F_v_; +text: .text%__1cNTemplateTableKwide_lload6F_v_; +text: .text%__1cNTemplateTableKwide_fload6F_v_; +text: .text%__1cNTemplateTableKwide_dload6F_v_; +text: .text%__1cNTemplateTableKwide_aload6F_v_; +text: .text%__1cNTemplateTableGiaload6F_v_; +text: .text%__1cNTemplateTableGlaload6F_v_; +text: .text%__1cNTemplateTableGfaload6F_v_; +text: .text%__1cNTemplateTableGdaload6F_v_; +text: .text%__1cNTemplateTableGbipush6F_v_; +text: .text%__1cLMoveF2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLMoveL2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC222_v_; +text: .text%__1cHOrLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cHOrLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTableF_goto6F_v_; +text: .text%__1cNTemplateTableGgoto_w6F_v_; +text: .text%__1cNTemplateTableFjsr_w6F_v_; +text: .text%__1cNTemplateTableDjsr6F_v_; +text: .text%__1cXreferenceProcessor_init6F_v_; +text: .text%__1cICodeBlobMset_oop_maps6MpnJOopMapSet__v_; +text: .text%__1cStemplateTable_init6F_v_; +text: .text%__1cNTemplateTableNpd_initialize6F_v_; +text: .text%__1cURecompilationMonitorbGstart_recompilation_monitor_task6F_v_; +text: .text%__1cNTemplateTableDnop6F_v_; +text: .text%__1cNTemplateTableSshouldnotreachhere6F_v_; +text: .text%__1cNTemplateTableLaconst_null6F_v_; +text: .text%__1cKPSYoungGenbCreset_survivors_after_shrink6M_v_; +text: .text%__1cKPSYoungGenQlimit_gen_shrink6MI_I_; +text: .text%__1cKPSYoungGenRavailable_to_live6M_I_; +text: .text%__1cSDeoptimizationBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_iiii_p0_; +text: .text%__1cLOptoRuntimeUmultianewarray2_Type6F_pknITypeFunc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_sparc_pipeline.o; +text: .text%__1cNcarSpace_init6F_v_; +text: .text%__1cUAdjoiningGenerations2t6MnNReservedSpace_IIIIIII_v_; +text: .text%__1cWAdjoiningVirtualSpaces2t6MnNReservedSpace_III_v_; +text: .text%__1cOchunkpool_init6F_v_; +text: .text%__1cFChunkbDstart_chunk_pool_cleaner_task6F_v_; +text: .text%__1cJArgumentsWinit_system_properties6F_v_; +text: .text%__1cINegFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMSysClassPathPexpand_endorsed6M_v_; +text: .text%__1cMSysClassPathQadd_jars_to_path6Fpcpkc_1_; +text: .text%__1cJArgumentsVprocess_settings_file6Fpkcii_i_; +text: .text%__1cJArgumentsTset_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsbBset_cms_and_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsUset_ergonomics_flags6F_v_; +text: .text%__1cJArgumentsZcheck_vm_args_consistency6F_i_; +text: .text%__1cJArgumentsSparse_vm_init_args6FpknOJavaVMInitArgs__i_; +text: .text%__1cJArgumentsWparse_each_vm_init_arg6FpknOJavaVMInitArgs_pnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsVfinalize_vm_init_args6FpnMSysClassPath_i_i_; +text: .text%__1cLStatSamplerGengage6F_v_; +text: .text%__1cNStubGeneratorbNgenerate_flush_callers_register_windows6M_pC_: stubGenerator_sparc.o; +text: .text%__1cSstubRoutines_init16F_v_; +text: .text%__1cSstubRoutines_init26F_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerIjumpl_to6MrnHAddress_pnMRegisterImpl_i_v_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbIgenerate_handler_for_unsafe_access6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbAgenerate_forward_exception6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorSgenerate_call_stub6MrpC_1_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorYgenerate_catch_exception6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorSgenerate_test_stop6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbIgenerate_copy_words_aligned8_lower6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbJgenerate_copy_words_aligned8_higher6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbBgenerate_set_words_aligned86M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbCgenerate_zero_words_aligned86M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbEgenerate_partial_subtype_check6M_pC_: stubGenerator_sparc.o; +text: .text%__1cISubFNodeDsub6kMpknEType_3_3_; +text: .text%__1cRStubCodeGeneratorLstub_prolog6MpnMStubCodeDesc__v_; +text: .text%__1cLStatSamplerbMcreate_system_property_instrumentation6FpnGThread__v_; +text: .text%__1cLStatSamplerHdestroy6F_v_; +text: .text%__1cLStatSamplerJdisengage6F_v_; +text: .text%__1cJArgumentsbNparse_java_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cNRegisterSaverYrestore_result_registers6FpnOMacroAssembler__v_; +text: .text%__1cLOptoRuntimeYgenerate_arraycopy_stubs6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: runtimeService.o; +text: .text%__1cORuntimeServiceYrecord_application_start6F_v_; +text: .text%__1cOMacroAssemblerNset_vm_result6MpnMRegisterImpl__v_; +text: .text%__1cORuntimeServiceEinit6F_v_; +text: .text%__1cLOptoRuntimeVhandle_exception_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeSfetch_monitor_Type6F_pknITypeFunc__; +text: .text%__1cOMacroAssemblerVverify_oop_subroutine6M_v_; +text: .text%__1cOMacroAssemblerPstop_subroutine6M_v_; +text: .text%__1cOMacroAssemblerElcmp6MpnMRegisterImpl_2222_v_; +text: .text%__1cOMacroAssemblerElneg6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerElshl6MpnMRegisterImpl_22222_v_; +text: .text%__1cOMacroAssemblerElshr6MpnMRegisterImpl_22222_v_; +text: .text%__1cOMacroAssemblerFlushr6MpnMRegisterImpl_22222_v_; +text: .text%__1cLOptoRuntimeUmultianewarray5_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray4_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray3_Type6F_pknITypeFunc__; +text: .text%__1cLsymbolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: symbolKlass.o; +text: .text%__1cJArgumentsbSparse_java_tool_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsFparse6FpknOJavaVMInitArgs__i_; +text: .text%__1cJArgumentsWPropertyList_get_value6FpnOSystemProperty_pkc_4_; +text: .text%__1cNSharedRuntimebIinitialize_StrictMath_entry_points6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedHeap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: arguments.o; +text: .text%__1cParrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: arrayKlassKlass.o; +text: .text%__1cLsymbolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cParrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: arrayKlassKlass.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC222i_v_; +text: .text%__1cOMacroAssemblerRcall_VM_leaf_base6MpnMRegisterImpl_pCi_v_; +text: .text%__1cLOptoRuntimebPgenerate_polling_page_return_handler_blob6F_v_; +text: .text%__1cLOptoRuntimebSgenerate_polling_page_safepoint_handler_blob6F_v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: symbolKlass.o; +text: .text%__1cLOptoRuntimebPgenerate_illegal_instruction_handler_blob6F_v_; +text: .text%__1cLOptoRuntimebBgenerate_uncommon_trap_blob6F_v_; +text: .text%__1cNSharedRuntimeTgenerate_deopt_blob6F_v_; +text: .text%__1cLOptoRuntimeWfill_in_exception_blob6F_v_; +text: .text%__1cLOptoRuntimeUsetup_exception_blob6F_v_; +text: .text%__1cLsymbolKlassOset_alloc_size6MI_v_: symbolKlass.o; +text: .text%__1cNTemplateTableGaaload6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psAdaptiveSizePolicy.o; +text: .text%__1cKCMoveDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMacroAssemblerCfb6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: templateTable_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: tenuredGeneration.o; +text: .text%__1cUPSAdaptiveSizePolicy2t6MIIIIIddI_v_; +text: .text%__1cQconstMethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cQconstMethodKlassOset_alloc_size6MI_v_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constMethodKlass.o; +text: .text%__1cGThreadMset_priority6Fp0nOThreadPriority__v_; +text: .text%__1cRconstantPoolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: constantPoolKlass.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: constantPoolKlass.o; +text: .text%__1cOBasicHashtable2t6Mii_v_: placeholders.o; +text: .text%__1cRconstantPoolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constantPoolKlass.o; +text: .text%__1cQPlaceholderTable2t6Mi_v_; +text: .text%__1cbBcreate_initial_thread_group6FpnGThread__nGHandle__: thread.o; +text: .text%__1cVcreate_initial_thread6FnGHandle_pnKJavaThread_pnGThread__pnHoopDesc__: thread.o; +text: .text%__1cbAcall_initializeSystemClass6FpnGThread__v_: thread.o; +text: .text%__1cWreset_vm_info_property6FpnGThread__v_: thread.o; +text: .text%__1cbAPSGCAdaptivePolicyCounters2t6MpkciipnUPSAdaptiveSizePolicy__v_; +text: .text%__1cNTemplateTableRfast_invokevfinal6Fi_v_; +text: .text%__1cVcompiledICHolderKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: compiledICHolderKlass.o; +text: .text%__1cNTemplateTableNinvokespecial6Fi_v_; +text: .text%__1cNTemplateTableMinvokestatic6Fi_v_; +text: .text%__1cNTemplateTablebDinvokeinterface_object_method6FpnMRegisterImpl_222_v_; +text: .text%__1cNTemplateTablePinvokeinterface6Fi_v_; +text: .text%__1cNTemplateTableE_new6F_v_; +text: .text%__1cNTemplateTableInewarray6F_v_; +text: .text%__1cNTemplateTableJanewarray6F_v_; +text: .text%__1cNTemplateTableLarraylength6F_v_; +text: .text%__1cNTemplateTableJcheckcast6F_v_; +text: .text%__1cNTemplateTableKinstanceof6F_v_; +text: .text%__1cNTemplateTableL_breakpoint6F_v_; +text: .text%__1cNTemplateTableGathrow6F_v_; +text: .text%__1cNTemplateTableMmonitorenter6F_v_; +text: .text%__1cNTemplateTableLmonitorexit6F_v_; +text: .text%__1cNTemplateTableEwide6F_v_; +text: .text%__1cNTemplateTableOmultianewarray6F_v_; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_: templateTable_sparc.o; +text: .text%__1cOCompilerOracleOread_from_line6Fpc_v_; +text: .text%__1cTcompilerOracle_init6F_v_; +text: .text%__1cWconstantPoolCacheKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnPPerfLongCounter__; +text: .text%__1cHThreadsJcreate_vm6FpnOJavaVMInitArgs_pi_i_; +text: .text%__1cZCompiledArgumentOopFinderRhandle_oop_offset6M_v_: frame.o; +text: .text%__1cQGCPolicyCounters2t6Mpkcii_v_; +text: .text%__1cHGCStats2t6M_v_; +text: .text%__1cNGCTaskManager2t6MI_v_; +text: .text%__1cNGCTaskManagerKinitialize6M_v_; +text: .text%__1cNGCTaskManagerKthreads_do6MpnNThreadClosure__v_; +text: .text%__1cPPerfDataManagerHdestroy6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: genCollectedHeap.o; +text: .text%__1cJGenRemSetYmax_alignment_constraint6Fn0AEName__I_; +text: .text%__1cWResolveOopMapConflictsUdo_potential_rewrite6MpnGThread__nMmethodHandle__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: generateOopMap.o; +text: .text%__1cRcheck_basic_types6F_v_; +text: .text%__1cSCommandLineFlagsExKuintxAtPut6FnXCommandLineFlagWithType_I_v_; +text: .text%__1cOThreadCriticalKinitialize6F_v_; +text: .text%__1cSThreadLocalStoragebCgenerate_code_for_get_thread6F_v_; +text: .text%__1cICodeHeap2t6M_v_; +text: .text%__1cICodeHeapHreserve6MIII_i_; +text: .text%__1cDhpiKinitialize6F_i_; +text: .text%__1cMPerfDataList2T6M_v_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: cpCacheKlass.o; +text: .text%__1cNWatcherThreadDrun6M_v_; +text: .text%__1cNWatcherThreadEstop6F_v_; +text: .text%__1cWconstantPoolCacheKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: cpCacheKlass.o; +text: .text%__1cFStateO_sub_Op_CMoveD6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_MoveF2I6MpknENode__v_; +text: .text%__1cKDictionary2t6Mi_v_; +text: .text%__1cKDictionaryKclasses_do6MpFpnMklassOopDesc__v_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: dictionary.o; +text: .text%__1cNeventlog_init6F_v_; +text: .text%__1cScheck_ThreadShadow6F_v_; +text: .text%__1cOtailjmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: fprofiler.o; +text: .text%__1cFframeVinterpreter_frame_mdp6kM_pC_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: phase.o; +text: .text%__1cKPerfMemoryUdelete_memory_region6F_v_; +text: .text%__1cKPerfMemoryUcreate_memory_region6FI_v_; +text: .text%__1cbBcleanup_sharedmem_resources6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cFframebAoops_compiled_arguments_do6MnMsymbolHandle_ipknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cPperfMemory_exit6F_v_; +text: .text%__1cPperfMemory_init6F_v_; +text: .text%__1cNTemplateTableNinvokevirtual6Fi_v_; +text: .text%__1cNTemplateTableHfastore6F_v_; +text: .text%__1cNTemplateTableHdastore6F_v_; +text: .text%__1cNTemplateTableHaastore6F_v_; +text: .text%__1cNTemplateTableHbastore6F_v_; +text: .text%__1cNTemplateTableHsastore6F_v_; +text: .text%__1cOcodeCache_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeCache.o; +text: .text%__1cNTemplateTableDpop6F_v_; +text: .text%__1cNTemplateTableEpop26F_v_; +text: .text%__1cNTemplateTableDdup6F_v_; +text: .text%__1cNTemplateTableGdup_x16F_v_; +text: .text%__1cNTemplateTableGdup_x26F_v_; +text: .text%__1cNTemplateTableEdup26F_v_; +text: .text%__1cNTemplateTableHdup2_x16F_v_; +text: .text%__1cNTemplateTableHdup2_x26F_v_; +text: .text%__1cNTemplateTableEswap6F_v_; +text: .text%__1cNCollectedHeap2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psScavenge.o; +text: .text%__1cNTemplateTableEirem6F_v_; +text: .text%__1cNTemplateTableElmul6F_v_; +text: .text%__1cNTemplateTableHlastore6F_v_; +text: .text%__1cNTemplateTableGbaload6F_v_; +text: .text%__1cNTemplateTableGcaload6F_v_; +text: .text%__1cNTemplateTableMfast_icaload6F_v_; +text: .text%__1cNTemplateTableGsaload6F_v_; +text: .text%__1cKPSYoungGenPinitialize_work6M_v_; +text: .text%__1cKPSYoungGenKinitialize6MnNReservedSpace_I_v_; +text: .text%__1cKPSYoungGenYinitialize_virtual_space6MnNReservedSpace_I_v_; +text: .text%__1cKPSYoungGen2t6MIII_v_; +text: .text%__1cOPSVirtualSpaceJshrink_by6MI_i_; +text: .text%__1cNTemplateTableHaload_06F_v_; +text: .text%__1cNTemplateTableGistore6F_v_; +text: .text%__1cNTemplateTableGlstore6F_v_; +text: .text%__1cNTemplateTableGfstore6F_v_; +text: .text%__1cNTemplateTableGdstore6F_v_; +text: .text%__1cNTemplateTableGastore6F_v_; +text: .text%__1cNTemplateTableLwide_istore6F_v_; +text: .text%__1cNTemplateTableLwide_lstore6F_v_; +text: .text%__1cNTemplateTableLwide_fstore6F_v_; +text: .text%__1cNTemplateTableLwide_dstore6F_v_; +text: .text%__1cNTemplateTableLwide_astore6F_v_; +text: .text%__1cNTemplateTableHiastore6F_v_; +text: .text%__1cNTemplateTableEldiv6F_v_; +text: .text%__1cNTemplateTableLtableswitch6F_v_; +text: .text%__1cNTemplateTableMlookupswitch6F_v_; +text: .text%__1cNTemplateTableRfast_linearswitch6F_v_; +text: .text%__1cNTemplateTableRfast_binaryswitch6F_v_; +text: .text%__1cNCompileBrokerQcompilation_init6FpnQAbstractCompiler__v_; +text: .text%__1cNCompileBrokerVinit_compiler_threads6Fi_v_; +text: .text%__1cJPSPermGen2t6MnNReservedSpace_IIIIpkci_v_; +text: .text%__1cNCompileBrokerQset_should_block6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compileBroker.o; +text: .text%__1cNTemplateTableIgetfield6Fi_v_; +text: .text%__1cNTemplateTableJgetstatic6Fi_v_; +text: .text%__1cIPSOldGenKinitialize6MnNReservedSpace_Ipkci_v_; +text: .text%__1cIPSOldGen2t6MIIIpkci_v_; +text: .text%__1cIPSOldGen2t6MnNReservedSpace_IIIIpkci_v_; +text: .text%__1cVcompiledICHolderKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psMarkSweep.o; +text: .text%__1cNTemplateTableIputfield6Fi_v_; +text: .text%__1cNTemplateTableJputstatic6Fi_v_; +text: .text%__1cVcompiledICHolderKlassOset_alloc_size6MI_v_: compiledICHolderKlass.o; +text: .text%__1cLPSMarkSweepKinitialize6F_v_; +text: .text%__1cNTemplateTableIwide_ret6F_v_; +text: .text%__1cNTemplateTableElrem6F_v_; +text: .text%__1cNTemplateTableElshl6F_v_; +text: .text%__1cNTemplateTableElshr6F_v_; +text: .text%__1cNTemplateTableFlushr6F_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyQinitialize_flags6M_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyUinitialize_size_info6M_v_; +text: .text%__1cNTemplateTableEineg6F_v_; +text: .text%__1cNTemplateTableElneg6F_v_; +text: .text%__1cNTemplateTableEfneg6F_v_; +text: .text%__1cNTemplateTableEdneg6F_v_; +text: .text%__1cNTemplateTableEiinc6F_v_; +text: .text%__1cNTemplateTableJwide_iinc6F_v_; +text: .text%__1cKPSScavengeKinitialize6F_v_; +text: .text%__1cNTemplateTableElcmp6F_v_; +text: .text%__1cWcompilationPolicy_init6F_v_; +text: .text%__1cRCompilationPolicyUcompleted_vm_startup6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compilationPolicy.o; +text: .text%__1cOcompiler2_init6F_v_; +text: .text%__1cSPSPromotionManagerKinitialize6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psPromotionLAB.o; +text: .text%__1cNTemplateTableDret6F_v_; diff --git a/hotspot/build/solaris/makefiles/reorder_COMPILER2_sparcv9 b/hotspot/build/solaris/makefiles/reorder_COMPILER2_sparcv9 new file mode 100644 index 00000000000..3cc6b8b510a --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_COMPILER2_sparcv9 @@ -0,0 +1,7130 @@ +data = R0x2000; +text = LOAD ?RXO; + + +text: .text%__1cLOptoRuntimeLjshort_copy6Fph1L_v_; +text: .text%__1cLOptoRuntimeTarrayof_jshort_copy6FpnIHeapWord_2L_v_; +text: .text%__1cSPSPromotionManagerWcopy_to_survivor_space6MpnHoopDesc__2_; +text: .text%__1cNinstanceKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQIndexSetIteratorQadvance_and_next6M_I_; +text: .text%__1cJMarkSweepO_mark_and_push6FppnHoopDesc__v_; +text: .text%__1cCosOjavaTimeMillis6F_x_; +text: .text%__1cNinstanceKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cOtypeArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cOtypeArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cIMachNodeHis_Mach6M_p0_: ad_sparc_misc.o; +text: .text%__1cIPhaseIFGIadd_edge6MII_i_; +text: .text%__1cJMarkSweepPmark_and_follow6FppnHoopDesc__v_; +text: .text%__1cMOopTaskQdDueueKpop_global6MrpnHoopDesc__i_; +text: .text%__1cQIndexSetIterator2t6MpnIIndexSet__v_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: ifg.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: ad_sparc_misc.o; +text: .text%__1cUGenericGrowableArrayLraw_at_grow6MipknEGrET__pv_; +text: .text%__1cENodeEjvms6kM_pnIJVMState__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: ad_sparc_misc.o; +text: .text%__1cHRegMaskESize6kM_I_; +text: .text%__1cHRegMaskFis_UP6kM_i_; +text: .text%__1cIMachNodeNrematerialize6kM_i_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: classes.o; +text: .text%__1cIProjNodeHis_Proj6M_p0_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: classes.o; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: classes.o; +text: .text%__1cETypeDcmp6Fkpk03_i_; +text: .text%__1cENodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHlatency6MI_I_; +text: .text%__1cENodeHis_Copy6kM_I_: classes.o; +text: .text%__1cIIndexSetWalloc_block_containing6MI_pn0AIBitBlock__; +text: .text%__1cHRegMaskJis_bound16kM_i_; +text: .text%__1cDff16FI_i_; +text: .text%__1cXresource_allocate_bytes6FL_pc_; +text: .text%__1cIMachNodeJideal_reg6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: ad_sparc_misc.o; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__: classes.o; +text: .text%__1cHRegMaskJis_bound26kM_i_; +text: .text%__1cJVectorSet2R6MI_rnDSet__; +text: .text%__1cENodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRMachSpillCopyNodeMis_SpillCopy6M_p0_: ad_sparc.o; +text: .text%__1cIMachNodeGOpcode6kM_i_; +text: .text%__1cENodeGpinned6kM_i_: classes.o; +text: .text%__1cQIndexSetIteratorEnext6M_I_: chaitin.o; +text: .text%__1cITypeNodeLbottom_type6kM_pknEType__; +text: .text%__1cETypeFuhash6Fkpk0_i_; +text: .text%__1cJiRegIOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cNobjArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cHPhiNodeGis_Phi6M_p0_: cfgnode.o; +text: .text%__1cKTypeOopPtrFklass6kM_pnHciKlass__: type.o; +text: .text%__1cENodeIout_grow6MI_v_; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_: callnode.o; +text: .text%__1cPClassFileStreamGget_u26MpnGThread__H_; +text: .text%__1cIIndexSetKinitialize6MI_v_; +text: .text%__1cQObjectStartArrayMobject_start6MpnIHeapWord__2_: cardTableExtension.o; +text: .text%__1cNRelocIteratorEnext6M_i_: relocInfo.o; +text: .text%__1cRMachSpillCopyNodeHis_Copy6kM_I_: ad_sparc.o; +text: .text%__1cEDictGInsert6Mpv1i_1_; +text: .text%__1cPOopTaskQdDueueSetFsteal6MipirpnHoopDesc__i_; +text: .text%__1cJCProjNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cOloadConI13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJMarkSweepUAdjustPointerClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cJMultiNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeHadd_req6Mp0_v_; +text: .text%__1cIMachNodeHis_Mach6M_p0_: ad_sparc.o; +text: .text%__1cINodeHashQhash_find_insert6MpnENode__2_; +text: .text%__1cENodeGis_CFG6kM_i_: cfgnode.o; +text: .text%__1cNobjArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNobjArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cKRegionNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cJMarkSweepOIsAliveClosureLdo_object_b6MpnHoopDesc__i_: markSweep.o; +text: .text%__1cOloadConI13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHPhiNodeGOpcode6kM_i_; +text: .text%__1cOloadConI13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIProjNodeGis_CFG6kM_i_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: classes.o; +text: .text%__1cETypeIhashcons6M_pk0_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: classes.o; +text: .text%__1cOloadConI13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFArenaIcontains6kMpkv_i_; +text: .text%__1cENodeHis_Call6M_pnICallNode__: classes.o; +text: .text%__1cIProjNodeGpinned6kM_i_; +text: .text%__1cICallNodeKmatch_edge6kMI_I_; +text: .text%__1cOPhaseIdealLoopUbuild_loop_late_post6MpnENode_pk0_v_; +text: .text%__1cIProjNodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: cfgnode.o; +text: .text%__1cKbranchNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__: ad_sparc_misc.o; +text: .text%__1cNRelocIteratorEnext6M_i_: nmethod.o; +text: .text%__1cNIdealLoopTreeJis_member6kMpk0_i_; +text: .text%__1cWPSScavengeRootsClosureGdo_oop6MppnHoopDesc__v_: psTasks.o; +text: .text%__1cMPhaseChaitinKelide_copy6MpnENode_ipnFBlock_rnJNode_List_6i_i_; +text: .text%__1cWNode_Backward_IteratorEnext6M_pnENode__; +text: .text%__1cMPhaseChaitinTinterfere_with_live6MIpnIIndexSet__v_; +text: .text%__1cHCompileRvalid_bundle_info6MpknENode__i_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: ad_sparc.o; +text: .text%__1cGIfNodeGOpcode6kM_i_; +text: .text%__1cHCompileNnode_bundling6MpknENode__pnGBundle__; +text: .text%__1cIUniverseMnon_oop_word6F_pv_; +text: .text%__1cOPhaseIdealLoopYsplit_if_with_blocks_pre6MpnENode__2_; +text: .text%__1cOPhaseIdealLoopZsplit_if_with_blocks_post6MpnENode__v_; +text: .text%__1cETypeEmeet6kMpk0_2_; +text: .text%__1cDLRGOcompute_degree6kMr0_i_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: coalesce.o; +text: .text%__1cENode2t6MI_v_; +text: .text%__1cFArenaIArealloc6MpvLL_1_; +text: .text%__1cHTypeIntCeq6kMpknEType__i_; +text: .text%__1cENodeNrematerialize6kM_i_: classes.o; +text: .text%__1cMMachCallNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cKSchedulingLanti_do_def6MpnFBlock_pnENode_nHOptoRegEName_i_v_; +text: .text%__1cENodeHis_Copy6kM_I_: cfgnode.o; +text: .text%__1cOlower_pressure6FpnDLRG_IpnFBlock_pI4_v_: ifg.o; +text: .text%__1cIAddPNodeGOpcode6kM_i_; +text: .text%__1cKIfTrueNodeGOpcode6kM_i_; +text: .text%__1cIPipelineXfunctional_unit_latency6kMIpk0_I_; +text: .text%__1cLsymbolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cETypeLisa_oop_ptr6kM_i_; +text: .text%__1cITypeLongCeq6kMpknEType__i_; +text: .text%__1cETypeJsingleton6kM_i_; +text: .text%__1cMPhaseIterGVNNtransform_old6MpnENode__2_; +text: .text%__1cWConstantPoolCacheEntryPfollow_contents6M_v_; +text: .text%__1cWConstantPoolCacheEntryPadjust_pointers6M_v_; +text: .text%__1cRMachSpillCopyNodeJideal_reg6kM_I_: ad_sparc.o; +text: .text%__1cHRegMaskMClearToPairs6M_v_; +text: .text%__1cKNode_ArrayGinsert6MIpnENode__v_; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__: cfgnode.o; +text: .text%__1cLsymbolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLsymbolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_: ad_sparc.o; +text: .text%__1cJloadPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMachNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%JVM_ArrayCopy; +text: .text%__1cIMachNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cOtypeArrayKlassQoop_is_typeArray6kM_i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: classes.o; +text: .text%__1cLIfFalseNodeGOpcode6kM_i_; +text: .text%__1cENodeEhash6kM_I_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: ad_sparc_misc.o; +text: .text%__1cSCallStaticJavaNodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: cfgnode.o; +text: .text%__1cIPhaseIFGQeffective_degree6kMI_i_; +text: .text%__1cNRelocIteratorTadvance_over_prefix6M_v_; +text: .text%__1cIPhaseIFGJre_insert6MI_v_; +text: .text%__1cIPhaseIFGLremove_node6MI_pnIIndexSet__; +text: .text%__1cIciObjectGequals6Mp0_i_; +text: .text%__1cWShouldNotReachHereNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cMMachProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cOPhaseIdealLoopEsort6MpnNIdealLoopTree_2_2_; +text: .text%__1cJiRegPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopopts.o; +text: .text%__1cMPhaseIterGVNWadd_users_to_worklist06MpnENode__v_; +text: .text%__1cDfh16FI_i_; +text: .text%__1cIIndexSetKfree_block6MI_v_; +text: .text%__1cHTypeIntJsingleton6kM_i_; +text: .text%__1cIParmNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cNMachIdealNodeErule6kM_I_: ad_sparc.o; +text: .text%__1cIBoolNodeGOpcode6kM_i_; +text: .text%__1cIConINodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: classes.o; +text: .text%__1cNSharedRuntimeDl2f6Fx_f_; +text: .text%__1cHTypeIntEhash6kM_i_; +text: .text%__1cOPhaseIdealLoopOget_early_ctrl6MpnENode__2_; +text: .text%__1cJMultiNodeIis_Multi6M_p0_; +text: .text%__1cIPhaseGVNJtransform6MpnENode__2_; +text: .text%__1cHConNodeGOpcode6kM_i_; +text: .text%__1cPjava_lang_ClassLas_klassOop6FpnHoopDesc__pnMklassOopDesc__; +text: .text%__1cJMarkSweepSMarkAndPushClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cYCallStaticJavaDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: classes.o; +text: .text%__1cENodeXpartial_latency_of_defs6MrnLBlock_Array_rnNGrowableArray4CI___v_; +text: .text%__1cIciObjectEhash6M_i_; +text: .text%__1cXPipeline_Use_Cycle_Mask2L6Mi_r0_: ad_sparc_pipeline.o; +text: .text%__1cOPhaseIdealLoopUbuild_loop_tree_impl6MpnENode_i_i_; +text: .text%__1cENodeEgrow6MI_v_; +text: .text%__1cKRegionNodeGOpcode6kM_i_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: relocInfo.o; +text: .text%__1cMMachProjNodeGOpcode6kM_i_; +text: .text%__1cITypeNodeEhash6kM_I_; +text: .text%__1cITypeLongEhash6kM_i_; +text: .text%__1cLmethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cMPhaseChaitinKbias_color6MrnDLRG_i_nHOptoRegEName__; +text: .text%__1cHRegMaskQis_aligned_Pairs6kM_i_; +text: .text%__1cRMachSpillCopyNodeLbottom_type6kM_pknEType__: ad_sparc.o; +text: .text%__1cFMutexGunlock6M_v_; +text: .text%__1cFStateRMachOperGenerator6MipnIMachNode_pnHCompile__pnIMachOper__; +text: .text%__1cIProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOPhaseIdealLoopThas_local_phi_input6MpnENode__2_; +text: .text%__1cJiRegIOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cRmethodDataOopDescHdata_at6Mi_pnLProfileData__; +text: .text%__1cNRelocIteratorEnext6M_i_: codeBlob.o; +text: .text%__1cNRelocIteratorFreloc6M_pnKRelocation__; +text: .text%__1cIIndexSetKinitialize6MIpnFArena__v_; +text: .text%__1cQconstMethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cKRegionNodeGpinned6kM_i_: classes.o; +text: .text%__1cENodeIIdentity6MpnOPhaseTransform__p0_; +text: .text%__1cENodeNis_block_proj6kM_pk0_; +text: .text%__1cPJavaFrameAnchorNmake_walkable6MpnKJavaThread__v_; +text: .text%__1cUGenericGrowableArrayPraw_at_put_grow6MipknEGrET_3_v_; +text: .text%__1cKJavaThreadPcook_last_frame6MnFframe__1_; +text: .text%__1cGcmpkey6Fpkv1_i_; +text: .text%__1cOtypeArrayKlassIallocate6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cUParallelScavengeHeapVlarge_typearray_limit6M_L_: parallelScavengeHeap.o; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FLipnGThread__pnIHeapWord__: typeArrayKlass.o; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cLTypeInstPtrEhash6kM_i_; +text: .text%__1cIBoolNodeHis_Bool6M_p0_: subnode.o; +text: .text%__1cKoopFactoryNnew_typeArray6FnJBasicType_ipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cIPhaseCCPOtransform_once6MpnENode__2_; +text: .text%__1cOMethodLivenessKBasicBlockXcompute_gen_kill_single6MpnQciByteCodeStream__v_; +text: .text%__1cPClassFileStreamGget_u16MpnGThread__C_; +text: .text%__1cbAfinal_graph_reshaping_impl6FpnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cKTypeAryPtrEhash6kM_i_; +text: .text%__1cENodeFIdeal6MpnIPhaseGVN_i_p0_; +text: .text%__1cYCallStaticJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFStateDDFA6MipknENode__i_; +text: .text%__1cENodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeInewarray6FpnKJavaThread_nJBasicType_i_v_; +text: .text%__1cOPhaseIdealLoopZremix_address_expressions6MpnENode__2_; +text: .text%__1cOtypeArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cMPipeline_UseMfull_latency6kMIrk0_I_; +text: .text%__1cOPhaseIdealLoopNget_late_ctrl6MpnENode_2_2_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopnode.o; +text: .text%__1cHPhiNodeGpinned6kM_i_: cfgnode.o; +text: .text%__1cKbranchNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLmethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQconstMethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQconstMethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cENodeFclone6kM_p0_; +text: .text%__1cMloadConPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMMachProjNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cGciTypeEmake6FnJBasicType__p0_; +text: .text%__1cENodeKmatch_edge6kMI_I_; +text: .text%__1cMMachProjNodeLout_RegMask6kM_rknHRegMask__: classes.o; +text: .text%__1cICallNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: ad_sparc.o; +text: .text%__1cRMachSpillCopyNodeLout_RegMask6kM_rknHRegMask__: ad_sparc.o; +text: .text%__1cETypeFxmeet6kMpk0_2_; +text: .text%__1cJVectorSet2F6kMI_i_; +text: .text%__1cMOopTaskQdDueueOpop_local_slow6MInOTaskQdDueueSuperDAge__i_; +text: .text%__1cKRelocationNunpack_2_ints6Mri1_v_: relocInfo.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: ad_sparc_misc.o; +text: .text%__1cENodeQIdeal_DU_postCCP6MpnIPhaseCCP__p0_; +text: .text%__1cOoop_RelocationLunpack_data6M_v_; +text: .text%__1cICallNodeHis_Call6M_p0_: callnode.o; +text: .text%__1cIProjNodeEhash6kM_I_; +text: .text%__1cGIfNodeGpinned6kM_i_: classes.o; +text: .text%__1cRMachSpillCopyNodeKin_RegMask6kMI_rknHRegMask__: ad_sparc.o; +text: .text%__1cILRG_ListGextend6MII_v_; +text: .text%__1cIPipelinePoperand_latency6kMIpk0_I_; +text: .text%__1cRMachSafePointNodeEjvms6kM_pnIJVMState__: ad_sparc_misc.o; +text: .text%__1cICmpPNodeGOpcode6kM_i_; +text: .text%__1cIMachNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cIConLNodeGOpcode6kM_i_; +text: .text%__1cJStartNodeLbottom_type6kM_pknEType__; +text: .text%__1cRMachSpillCopyNodePoper_input_base6kM_I_: ad_sparc.o; +text: .text%__1cRmethodDataOopDescJnext_data6MpnLProfileData__2_; +text: .text%__1cOis_diamond_phi6FpnENode__i_: cfgnode.o; +text: .text%__1cHPhiNodeEhash6kM_I_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: callnode.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: callnode.o; +text: .text%__1cKTypeOopPtrJsingleton6kM_i_; +text: .text%__1cITypeNodeJideal_reg6kM_I_; +text: .text%__1cMloadConPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cGIfNodeFis_If6M_p0_: classes.o; +text: .text%__1cIMachNode2t6M_v_; +text: .text%__1cIAddINodeGOpcode6kM_i_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: memnode.o; +text: .text%__1cENodeHsize_of6kM_I_; +text: .text%__1cYCallStaticJavaDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKNode_ArrayGremove6MI_v_; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: classes.o; +text: .text%__1cIIndexSet2t6Mp0_v_; +text: .text%__1cHMatcherKLabel_Root6MpknENode_pnFState_p16_6_; +text: .text%__1cHPhiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: classes.o; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: cfgnode.o; +text: .text%__1cLTypeInstPtrCeq6kMpknEType__i_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: classes.o; +text: .text%__1cENodeSremove_dead_region6MpnIPhaseGVN_i_i_; +text: .text%__1cNSafePointNodeGpinned6kM_i_: callnode.o; +text: .text%__1cQUnique_Node_ListGremove6MpnENode__v_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: memnode.o; +text: .text%__1cJCProjNodeGis_CFG6kM_i_: cfgnode.o; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: ad_sparc_misc.o; +text: .text%__1cFMutexElock6M_v_; +text: .text%__1cIProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cKoopFactoryKnew_symbol6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cLSymbolTableGlookup6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: classes.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: classes.o; +text: .text%__1cOmatch_into_reg6FpnENode_iii1_i_: matcher.o; +text: .text%__1cKTypeAryPtrCeq6kMpknEType__i_; +text: .text%__1cMPhaseIterGVNVadd_users_to_worklist6MpnENode__v_; +text: .text%__1cIHaltNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cKmethodOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: callnode.o; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__: ad_sparc.o; +text: .text%__1cPindOffset13OperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cILocationIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cENodeNrematerialize6kM_i_: cfgnode.o; +text: .text%__1cMMergeMemNodeGOpcode6kM_i_; +text: .text%__1cHCompileMFillLocArray6MpnENode_pnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cETypeKhas_memory6kM_i_; +text: .text%__1cSCallStaticJavaNodeEhash6kM_I_: callnode.o; +text: .text%__1cNLocationValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: cfgnode.o; +text: .text%__1cENode2t6Mp0_v_; +text: .text%__1cPciObjectFactoryDget6MpnHoopDesc__pnIciObject__; +text: .text%__1cNCatchProjNodeGOpcode6kM_i_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: cfgnode.o; +text: .text%__1cIGraphKitHstopped6M_i_; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: ad_sparc_misc.o; +text: .text%__1cICmpINodeGOpcode6kM_i_; +text: .text%__1cJStartNodeGpinned6kM_i_: callnode.o; +text: .text%__1cHRegMaskPfind_first_pair6kM_nHOptoRegEName__; +text: .text%__1cFframeVinterpreter_frame_bcp6kM_pC_; +text: .text%__1cKMachIfNodeJis_MachIf6kM_pk0_: ad_sparc_misc.o; +text: .text%__1cLTypeInstPtrEmake6FnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_pk0_; +text: .text%__1cMPipeline_UseJadd_usage6Mrk0_v_; +text: .text%__1cGIfNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSinstanceKlassKlassMoop_is_klass6kM_i_: instanceKlassKlass.o; +text: .text%__1cPClassFileStreamHskip_u16MipnGThread__v_; +text: .text%__1cJloadINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cYCallStaticJavaDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIget_long6kM_x_; +text: .text%__1cHMatcherKReduceOper6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cMPhaseChaitinMchoose_color6MrnDLRG_i_nHOptoRegEName__; +text: .text%__1cRAbstractAssembler2t6MpnKCodeBuffer__v_; +text: .text%__1cbFCompressedLineNumberWriteStreamKwrite_pair6Mii_v_; +text: .text%__1cIAddPNodeKmatch_edge6kMI_I_; +text: .text%__1cJTypeTupleJsingleton6kM_i_; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pnIciObject_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cNinstanceKlassLfind_method6FpnPobjArrayOopDesc_pnNsymbolOopDesc_4_pnNmethodOopDesc__; +text: .text%__1cENodeGis_CFG6kM_i_: connode.o; +text: .text%__1cRCardTableModRefBSEkind6M_nKBarrierSetEName__: cardTableExtension.o; +text: .text%__1cKCodeBufferIrelocate6MpCrknQRelocationHolder_i_v_; +text: .text%__1cJLoadPNodeGOpcode6kM_i_; +text: .text%__1cHConNodeGis_Con6kM_I_: classes.o; +text: .text%__1cHRegMaskMSmearToPairs6M_v_; +text: .text%__1cMOopMapStreamJfind_next6M_v_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: memnode.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nmethod.o; +text: .text%__1cJiRegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cMMachTypeNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cIParmNodeGOpcode6kM_i_; +text: .text%__1cKSchedulingPAddNodeToBundle6MpnENode_pknFBlock__v_; +text: .text%__1cKSchedulingWAddNodeToAvailableList6MpnENode__v_; +text: .text%__1cKSchedulingSChooseNodeToBundle6M_pnENode__; +text: .text%__1cICodeHeapKfind_start6kMpv_1_; +text: .text%__1cOThreadCritical2T6M_v_; +text: .text%__1cOThreadCritical2t6M_v_; +text: .text%__1cENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICallNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHPhiNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: classes.o; +text: .text%__1cIMachNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNsymbolOopDescGequals6kMpkci_i_; +text: .text%__1cICmpUNodeGOpcode6kM_i_; +text: .text%__1cFframeYinterpreter_frame_method6kM_pnNmethodOopDesc__; +text: .text%__1cENodeHdel_req6MI_v_; +text: .text%__1cMMutableSpaceMcas_allocate6ML_pnIHeapWord__; +text: .text%__1cETypeEhash6kM_i_; +text: .text%__1cPClassFileParserRverify_legal_utf86MpkCipnGThread__v_; +text: .text%__1cPClassFileParserbEparse_constant_pool_utf8_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNsymbolOopDescLas_C_string6kM_pc_; +text: .text%__1cJiRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cHCmpNodeGis_Cmp6kM_pk0_: classes.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: cfgnode.o; +text: .text%__1cKTypeAryPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cTCreateExceptionNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAddPNodeLbottom_type6kM_pknEType__; +text: .text%__1cUParallelScavengeHeapWpermanent_mem_allocate6ML_pnIHeapWord__; +text: .text%__1cJPSPermGenSallocate_permanent6ML_pnIHeapWord__; +text: .text%__1cMMutableSpaceIallocate6ML_pnIHeapWord__; +text: .text%__1cNSharedRuntimeDd2i6Fd_i_; +text: .text%__1cOPhaseIdealLoopbIdom_lca_for_get_late_ctrl_internal6MpnENode_22_2_; +text: .text%__1cJMultiNodeEhash6kM_I_: classes.o; +text: .text%__1cITypeLongJsingleton6kM_i_; +text: .text%JVM_CurrentTimeMillis; +text: .text%__1cENodeGis_CFG6kM_i_: subnode.o; +text: .text%__1cFBlockIis_Empty6kM_i_; +text: .text%__1cILoadNodeEhash6kM_I_; +text: .text%__1cJCProjNodeEhash6kM_I_: classes.o; +text: .text%__1cQciByteCodeStreamEjava6MnJBytecodesECode__2_; +text: .text%__1cJCatchNodeGOpcode6kM_i_; +text: .text%__1cIHaltNodeGOpcode6kM_i_; +text: .text%__1cMloadConINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHTypeIntFxmeet6kMpknEType__3_; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_: classes.o; +text: .text%__1cNflagsRegPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cTconstantPoolOopDescNklass_at_impl6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cLConvI2LNodeGOpcode6kM_i_; +text: .text%__1cFframeVoopmapreg_to_location6kMnFVMRegEName_pknLRegisterMap__ppnHoopDesc__; +text: .text%__1cTconstantPoolOopDescSklass_ref_index_at6Mi_i_; +text: .text%__1cKRelocationLunpack_data6M_v_: ad_sparc.o; +text: .text%__1cMPhaseIterGVNZremove_globally_dead_node6MpnENode__v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: multnode.o; +text: .text%__1cIJVMStateLdebug_start6kM_I_; +text: .text%__1cRSignatureIterator2t6MnMsymbolHandle__v_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: cfgnode.o; +text: .text%__1cFBlockGselect6MrnJNode_List_rnLBlock_Array_pirnJVectorSet_IrnNGrowableArray4CI___pnENode__; +text: .text%__1cFStateRMachNodeGenerator6MipnHCompile__pnIMachNode__; +text: .text%__1cHMatcherKReduceInst6MpnFState_irpnENode__pnIMachNode__; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: ad_sparc_misc.o; +text: .text%method_compare: methodOop.o; +text: .text%__1cXPipeline_Use_Cycle_MaskCOr6Mrk0_v_; +text: .text%__1cMMachCallNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cIMachNodeGExpand6MpnFState_rnJNode_List__p0_: ad_sparc_misc.o; +text: .text%__1cILoadNodeLbottom_type6kM_pknEType__; +text: .text%__1cWShouldNotReachHereNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cGBitMapFclear6M_v_; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: classes.o; +text: .text%__1cTconstantPoolOopDescQsignature_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cKHandleMarkKinitialize6MpnGThread__v_; +text: .text%__1cKHandleMark2T6M_v_; +text: .text%__1cENodeHis_Call6M_pnICallNode__: cfgnode.o; +text: .text%__1cJiRegLOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: multnode.o; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__: classes.o; +text: .text%__1cMMergeMemNodeLbottom_type6kM_pknEType__: memnode.o; +text: .text%__1cHMatcherTReduceInst_Interior6MpnFState_ipnIMachNode_IrpnENode__I_; +text: .text%__1cJTypeTupleEhash6kM_i_; +text: .text%__1cJloadPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCastPPNodeGOpcode6kM_i_; +text: .text%__1cGOopMapJset_value6MnHOptoRegEName_ii_v_; +text: .text%__1cITypeLongFxmeet6kMpknEType__3_; +text: .text%__1cIMachNodeSalignment_required6kM_i_: ad_sparc_misc.o; +text: .text%__1cHPhiNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cMloadConINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2L_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadPNodeIpipeline6kM_pknIPipeline__; +text: .text%JVM_ReleaseUTF; +text: .text%__1cObranchConPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKNode_ArrayEgrow6MI_v_; +text: .text%__1cJMultiNodeIproj_out6kMI_pnIProjNode__; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: methodDataOop.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlass.o; +text: .text%__1cFBlockLis_uncommon6kMrnLBlock_Array__i_; +text: .text%__1cINodeHashLhash_delete6MpknENode__i_; +text: .text%__1cZPhaseConservativeCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: multnode.o; +text: .text%__1cOAbstractICachePinvalidate_word6FpC_v_; +text: .text%__1cMloadConINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cGIfNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIAddPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRNativeInstructionLset_long_at6Mii_v_; +text: .text%__1cPciInstanceKlassMis_interface6M_i_: ciInstanceKlass.o; +text: .text%__1cFDictI2i6M_v_; +text: .text%__1cNobjArrayKlassQarray_klass_impl6FnTobjArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cIAddPNodeHis_AddP6M_p0_: classes.o; +text: .text%__1cMPhaseChaitinSuse_prior_register6MpnENode_I2pnFBlock_rnJNode_List_6_i_; +text: .text%__1cNinstanceKlassQarray_klass_impl6FnTinstanceKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cHCompileJcan_alias6MpknHTypePtr_i_i_; +text: .text%__1cPSignatureStreamEnext6M_v_; +text: .text%__1cENodeGpinned6kM_i_: connode.o; +text: .text%__1cKoopFactoryMnew_objArray6FpnMklassOopDesc_ipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassRallocate_objArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cITypeLongEmake6Fxxi_pk0_; +text: .text%__1cENodeOis_block_start6kM_i_; +text: .text%__1cGBitMapJset_union6M0_v_; +text: .text%__1cJiRegLOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: subnode.o; +text: .text%__1cPVirtualCallDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cIConPNodeGOpcode6kM_i_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: ad_sparc.o; +text: .text%__1cUGenericGrowableArray2t6Mii_v_; +text: .text%__1cKRelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cHTypeIntEmake6Fi_pk0_; +text: .text%__1cKRegionNodeEhash6kM_I_: classes.o; +text: .text%__1cHPhiNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMflagsRegOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cPindOffset13OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cENodeHis_Copy6kM_I_: memnode.o; +text: .text%__1cPjava_lang_ClassMis_primitive6FpnHoopDesc__i_; +text: .text%__1cOPSPromotionLABKinitialize6MnJMemRegion__v_; +text: .text%__1cENodeGis_Con6kM_I_: classes.o; +text: .text%__1cJLoadINodeGOpcode6kM_i_; +text: .text%__1cHPhiNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHTypeIntEmake6Fiii_pk0_; +text: .text%__1cENodeIdestruct6M_v_; +text: .text%__1cIBoolNodeEhash6kM_I_; +text: .text%JVM_GetMethodIxExceptionTableLength; +text: .text%__1cSInterpreterRuntimeJanewarray6FpnKJavaThread_pnTconstantPoolOopDesc_ii_v_; +text: .text%__1cPClassFileParserUassemble_annotations6MpCi1ipnGThread__nPtypeArrayHandle__; +text: .text%__1cMPhaseIterGVNKis_IterGVN6M_p0_: phaseX.o; +text: .text%__1cKbranchNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cGBitMap2t6MpLL_v_; +text: .text%__1cENodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOJNIHandleBlockPallocate_handle6MpnHoopDesc__pnI_jobject__; +text: .text%__1cEDict2F6kMpkv_pv_; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_: coalesce.o; +text: .text%__1cIMachNodeNis_MachEpilog6M_pnOMachEpilogNode__: ad_sparc_misc.o; +text: .text%__1cFParsePdo_one_bytecode6M_v_; +text: .text%__1cFParseNdo_exceptions6M_v_; +text: .text%__1cRSignatureIteratorSiterate_parameters6M_v_; +text: .text%__1cNbranchConNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cICodeBlobLoop_addr_at6kMi_ppnHoopDesc__; +text: .text%__1cKis_x2logic6FpnIPhaseGVN_pnENode__3_: cfgnode.o; +text: .text%__1cHAbsNodeLis_absolute6FpnIPhaseGVN_pnENode__4_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cVcompP_iRegP_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNLoadRangeNodeGOpcode6kM_i_; +text: .text%__1cLPCTableNodeGpinned6kM_i_: classes.o; +text: .text%__1cPClassFileStreamGget_u46MpnGThread__I_; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: cfgnode.o; +text: .text%__1cUGenericGrowableArray2t6MpnFArena_iipnEGrET__v_; +text: .text%__1cGBitMapGat_put6MLi_v_; +text: .text%__1cHMatcherKmatch_tree6MpknENode__pnIMachNode__; +text: .text%__1cTconstantPoolOopDescbAname_and_type_ref_index_at6Mi_i_; +text: .text%__1cLTypeInstPtrFxmeet6kMpknEType__3_; +text: .text%__1cKNode_ArrayFclear6M_v_; +text: .text%__1cIHaltNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: classes.o; +text: .text%__1cIProjNodeHsize_of6kM_I_; +text: .text%__1cUParallelScavengeHeapPis_in_permanent6kMpkv_i_: parallelScavengeHeap.o; +text: .text%__1cMCreateExNodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Call6M_pnICallNode__: multnode.o; +text: .text%__1cHhashptr6Fpkv_i_; +text: .text%__1cLLShiftLNodeGOpcode6kM_i_; +text: .text%__1cMMachCallNodeLbottom_type6kM_pknEType__; +text: .text%__1cNinstanceKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cHhashkey6Fpkv_i_; +text: .text%__1cHSubNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cObranchConPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cTconstantPoolOopDescWsignature_ref_index_at6Mi_i_; +text: .text%__1cKRelocationRpd_set_data_value6MpCl_v_; +text: .text%__1cODataRelocationJset_value6MpC_v_: relocInfo.o; +text: .text%__1cKciTypeFlowLStateVectorSapply_one_bytecode6MpnQciByteCodeStream__i_; +text: .text%__1cOoop_RelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cOoop_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cKRelocationLunpack_data6M_v_: relocInfo.o; +text: .text%__1cOcompU_iRegNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKBranchDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cMMachHaltNodeEjvms6kM_pnIJVMState__; +text: .text%__1cITypeNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cHCompilePfind_alias_type6MpknHTypePtr_i_pn0AJAliasType__; +text: .text%__1cHConNodeEhash6kM_I_; +text: .text%__1cKimmI13OperIconstant6kM_l_: ad_sparc_clone.o; +text: .text%__1cMPhaseChaitinHnew_lrg6MpknENode_I_v_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: classes.o; +text: .text%__1cFMutexElock6MpnGThread__v_; +text: .text%__1cIMachNodeHtwo_adr6kM_I_: ad_sparc.o; +text: .text%__1cJTypeTupleCeq6kMpknEType__i_; +text: .text%__1cHCmpNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: callnode.o; +text: .text%__1cHMemNodeMIdeal_common6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMPhaseIterGVNMsubsume_node6MpnENode_2_v_; +text: .text%__1cENode2t6Mp011_v_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: relocInfo.o; +text: .text%__1cTconstantPoolOopDescLname_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cIPhaseIFGMtest_edge_sq6kMII_i_; +text: .text%__1cObranchConPNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cICodeHeapJnext_free6kMpnJHeapBlock__pv_; +text: .text%__1cNSafePointNodeHsize_of6kM_I_; +text: .text%__1cICodeHeapKnext_block6kMpnJHeapBlock__2_; +text: .text%__1cICodeHeapLblock_start6kMpv_pnJHeapBlock__; +text: .text%__1cOoop_RelocationSfix_oop_relocation6M_v_; +text: .text%__1cFframeUis_interpreted_frame6kM_i_; +text: .text%__1cLTypeInstPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cRSignatureIteratorSiterate_returntype6M_v_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: cfgnode.o; +text: .text%__1cIMachNodeHis_Mach6M_p0_: machnode.o; +text: .text%__1cENodeGis_Con6kM_I_: subnode.o; +text: .text%__1cJloadPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJStoreNodeKmatch_edge6kMI_I_; +text: .text%__1cETypeFempty6kM_i_; +text: .text%__1cENodeHget_int6kM_i_; +text: .text%__1cIAddPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMPhaseIterGVNbGregister_new_node_with_optimizer6MpnENode__2_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: generateOopMap.o; +text: .text%__1cIJVMStateJdebug_end6kM_I_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: connode.o; +text: .text%__1cNCellTypeStateFmerge6kM0i_0_; +text: .text%__1cJloadPNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeNrematerialize6kM_i_: classes.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: symbolKlass.o; +text: .text%__1cNSafePointNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPcheckCastPPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFArenaEgrow6ML_pv_; +text: .text%__1cJloadBNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKbranchNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKbranchNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cQResultTypeFinderDset6MinJBasicType__v_: bytecode.o; +text: .text%__1cNmethodOopDescLresult_type6kM_nJBasicType__; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceKlass.o; +text: .text%__1cQSystemDictionaryXcheck_signature_loaders6FnMsymbolHandle_nGHandle_2ipnGThread__v_; +text: .text%__1cMMachCallNodeLis_MachCall6M_p0_: ad_sparc_misc.o; +text: .text%__1cILoadNodeKmatch_edge6kMI_I_; +text: .text%__1cCosVcurrent_stack_pointer6F_pC_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: multnode.o; +text: .text%__1cPciInstanceKlassGloader6M_pnHoopDesc__; +text: .text%__1cNbranchConNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_CFG6kM_i_: memnode.o; +text: .text%__1cIAddPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQaddP_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIBoolNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKRegionNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSaddI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cPciInstanceKlassRis_instance_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cOno_flip_branch6FpnFBlock__i_: block.o; +text: .text%__1cLsymbolKlassNoop_is_symbol6kM_i_: symbolKlass.o; +text: .text%__1cPSignatureStreamHis_done6kM_i_; +text: .text%__1cKStorePNodeGOpcode6kM_i_; +text: .text%__1cJiRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKStoreINodeGOpcode6kM_i_; +text: .text%__1cHMemNodeGis_Mem6M_p0_: classes.o; +text: .text%__1cHPhiNodeHsize_of6kM_I_: cfgnode.o; +text: .text%__1cNmethodOopDescRis_not_compilable6kMi_i_; +text: .text%__1cObranchConUNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOloadConI13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJHashtableJnew_entry6MIpnHoopDesc__pnOHashtableEntry__; +text: .text%__1cQaddP_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIHaltNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPciInstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cJrelocInfo2t6Mn0AJrelocType_ii_v_; +text: .text%__1cOloadConI13NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cPcheckCastPPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJStoreNodeLbottom_type6kM_pknEType__; +text: .text%__1cQCompressedStream2t6MpCi_v_; +text: .text%__1cITypeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRSignatureIterator2t6MpnNsymbolOopDesc__v_; +text: .text%__1cGIfNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNflagsRegUOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cOPSPromotionLABFflush6M_v_; +text: .text%__1cKJNIHandlesKmake_local6FpnHJNIEnv__pnHoopDesc__pnI_jobject__; +text: .text%__1cSPSPromotionManagerMdrain_stacks6M_v_; +text: .text%__1cENodeHis_Goto6kM_I_: classes.o; +text: .text%__1cJdo_method6FpnNmethodOopDesc__v_: recompilationMonitor.o; +text: .text%__1cGThreadLis_in_stack6kMpC_i_; +text: .text%__1cKJavaThreadNis_lock_owned6kMpC_i_; +text: .text%__1cSCountedLoopEndNodeGOpcode6kM_i_; +text: .text%__1cITypeNodeDcmp6kMrknENode__I_; +text: .text%__1cKTypeRawPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cILoadNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJloadINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIBoolNodeLbottom_type6kM_pknEType__: subnode.o; +text: .text%__1cJrelocInfoNfinish_prefix6Mph_p0_; +text: .text%__1cNSafePointNodebBneeds_polling_address_input6F_i_; +text: .text%__1cNmethodOopDescPis_empty_method6kM_i_; +text: .text%__1cSCallStaticJavaNodeRis_CallStaticJava6kM_pk0_: callnode.o; +text: .text%__1cMMergeMemNodeLis_MergeMem6M_p0_: memnode.o; +text: .text%__1cLCounterDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cTAbstractInterpreterLmethod_kind6FnMmethodHandle__n0AKMethodKind__; +text: .text%__1cKReflectionTverify_field_access6FpnMklassOopDesc_22nLAccessFlags_ii_i_; +text: .text%__1cOMachReturnNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cJCodeCacheJfind_blob6Fpv_pnICodeBlob__; +text: .text%__1cPCheckCastPPNodeGOpcode6kM_i_; +text: .text%__1cNPhaseCoalesceRcombine_these_two6MpnENode_2_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: lcm.o; +text: .text%__1cKcmpOpPOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cNflagsRegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKTypeRawPtrJsingleton6kM_i_; +text: .text%__1cMPhaseChaitinLinsert_proj6MpnFBlock_IpnENode_I_v_; +text: .text%__1cNinstanceKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__: callnode.o; +text: .text%__1cJcmpOpOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cOAbstractICacheQinvalidate_range6FpCi_v_; +text: .text%__1cNmethodOopDescLis_accessor6kM_i_; +text: .text%__1cHPhiNodeIadr_type6kM_pknHTypePtr__: cfgnode.o; +text: .text%__1cZPhaseConservativeCoalesceJcopy_copy6MpnENode_2pnFBlock_I_i_; +text: .text%__1cKstorePNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSafePointNodeSset_next_exception6Mp0_v_; +text: .text%__1cNRelocIteratorKset_limits6MpC1_v_; +text: .text%__1cILoadNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cUParallelScavengeHeapVunsafe_max_tlab_alloc6kM_L_; +text: .text%__1cPciObjectFactorySget_unloaded_klass6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cNCollectedHeapXallocate_from_tlab_slow6FpnGThread_L_pnIHeapWord__; +text: .text%__1cNmethodOopDescMintrinsic_id6kM_n0ALIntrinsicId__; +text: .text%__1cQciByteCodeStreamMreset_to_bci6Mi_v_; +text: .text%__1cWThreadLocalAllocBufferXclear_before_allocation6M_v_; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: classes.o; +text: .text%__1cMPhaseIterGVNFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cFframeZsender_with_pc_adjustment6kMpnLRegisterMap_pnICodeBlob_i_0_; +text: .text%__1cPsplit_flow_path6FpnIPhaseGVN_pnHPhiNode__pnENode__: cfgnode.o; +text: .text%__1cLis_cond_add6FpnIPhaseGVN_pnHPhiNode__pnENode__; +text: .text%__1cUParallelScavengeHeapRallocate_new_tlab6ML_pnIHeapWord__; +text: .text%__1cWThreadLocalAllocBufferEfill6MpnIHeapWord_2L_v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2t6M_v_; +text: .text%__1cKSharedHeapXfill_region_with_object6FnJMemRegion__v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2T6M_v_; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode__i_; +text: .text%__1cKCodeBuffer2t6MpCi_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: block.o; +text: .text%__1cFframeGsender6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cMMergeMemNodeEhash6kM_I_; +text: .text%__1cMflagsRegOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cENodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cILoadNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKRegionNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRNativeInstructionQset_data64_sethi6FpCl_v_; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: nativeInst_sparc.o; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: nativeInst_sparc.o; +text: .text%__1cHRetNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cRInvocationCounterEinit6M_v_; +text: .text%__1cFBlockOschedule_local6MrnHMatcher_rnLBlock_Array_pirnJVectorSet_rnNGrowableArray4CI___i_; +text: .text%__1cXPhaseAggressiveCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cFBlockScall_catch_cleanup6MrnLBlock_Array__v_; +text: .text%__1cTconstantPoolOopDescNklass_name_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cITypeNodeHsize_of6kM_I_; +text: .text%__1cKRegionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstorePNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcompU_iRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cTconstantPoolOopDescRname_ref_index_at6Mi_i_; +text: .text%__1cNmethodOopDescIbci_from6kMpC_i_; +text: .text%__1cRMachSafePointNodeQis_MachSafePoint6M_p0_: ad_sparc_misc.o; +text: .text%__1cIRootNodeGOpcode6kM_i_; +text: .text%__1cLPhaseValuesGintcon6Mi_pnIConINode__; +text: .text%__1cOloadConI13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cObranchConPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGpinned6kM_i_: subnode.o; +text: .text%__1cFMutexbClock_without_safepoint_check6M_v_; +text: .text%__1cENodeRdisconnect_inputs6Mp0_i_; +text: .text%__1cMObjectLocker2T6M_v_; +text: .text%__1cMObjectLocker2t6MnGHandle_pnGThread__v_; +text: .text%__1cSinstanceKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cIAddINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNinstanceKlassRallocate_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cENodeGpinned6kM_i_: memnode.o; +text: .text%__1cJTypeTupleEmake6FIppknEType__pk0_; +text: .text%__1cJTypeTupleGfields6FI_ppknEType__; +text: .text%__1cENodeHdel_out6Mp0_v_: parse2.o; +text: .text%__1cOcompI_iRegNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPcheckCastPPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNidealize_test6FpnIPhaseGVN_pnGIfNode__3_: ifnode.o; +text: .text%__1cHAddNodeEhash6kM_I_; +text: .text%__1cRmethodDataOopDescPinitialize_data6MpnOBytecodeStream_i_i_; +text: .text%__1cRmethodDataOopDescTbytecode_cell_count6FnJBytecodesECode__i_; +text: .text%__1cRAbstractAssemblerEbind6MrnFLabel__v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: subnode.o; +text: .text%__1cQaddP_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNExceptionMark2T6M_v_; +text: .text%__1cNExceptionMark2t6MrpnGThread__v_; +text: .text%__1cHTypeAryRary_must_be_exact6kM_i_; +text: .text%__1cPconvI2L_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLLShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cILoadNodeHis_Load6M_p0_: classes.o; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: split_if.o; +text: .text%__1cTCreateExceptionNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJStoreNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLsymbolKlassPallocate_symbol6MpCipnGThread__pnNsymbolOopDesc__; +text: .text%__1cLSymbolTableJbasic_add6MipCiIpnGThread__pnNsymbolOopDesc__; +text: .text%__1cYCallStaticJavaDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNbranchConNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIMachOperNconstant_disp6kM_i_; +text: .text%__1cIMachOperFscale6kM_i_; +text: .text%__1cLProfileDataPadjust_pointers6M_v_: methodDataOop.o; +text: .text%__1cLProfileDataPfollow_contents6M_v_: methodDataOop.o; +text: .text%__1cIsplit_if6FpnGIfNode_pnMPhaseIterGVN__pnENode__: ifnode.o; +text: .text%__1cKbranchNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cTremove_useless_bool6FpnGIfNode_pnIPhaseGVN__pnENode__: ifnode.o; +text: .text%__1cJloadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSafePointNodeLbottom_type6kM_pknEType__: callnode.o; +text: .text%__1cIBoolNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLLShiftINodeGOpcode6kM_i_; +text: .text%__1cJStoreNodeIis_Store6kM_pk0_: classes.o; +text: .text%__1cUcompI_iReg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPSignatureStreamJis_object6kM_i_; +text: .text%__1cLklassItable2t6MnTinstanceKlassHandle__v_; +text: .text%__1cJFieldTypeKbasic_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cKbranchNodeHis_Goto6kM_I_: ad_sparc_misc.o; +text: .text%__1cFChunkJnext_chop6M_v_; +text: .text%__1cENode2t6Mp0111_v_; +text: .text%__1cGOopMapHset_oop6MnHOptoRegEName_ii_v_; +text: .text%__1cObranchConUNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPcheckCastPPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMPhaseChaitinMyank_if_dead6MpnENode_pnFBlock_pnJNode_List_6_i_; +text: .text%__1cNPhaseRegAllocGis_oop6kMpknENode__i_; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: parse1.o; +text: .text%__1cISubINodeGOpcode6kM_i_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: cfgnode.o; +text: .text%__1cHTypeAryEhash6kM_i_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: classes.o; +text: .text%__1cHSubNodeGis_Sub6M_p0_: classes.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: codeBlob.o; +text: .text%__1cNbranchConNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: ad_sparc.o; +text: .text%__1cENodeJis_Branch6kM_I_: ad_sparc.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: ad_sparc.o; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: ad_sparc.o; +text: .text%__1cIMachNodeSalignment_required6kM_i_: ad_sparc.o; +text: .text%__1cFciEnvXget_klass_by_index_impl6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cTconstantPoolOopDescSklass_at_if_loaded6FnSconstantPoolHandle_i_pnMklassOopDesc__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: subnode.o; +text: .text%__1cNinstanceKlassGitable6kM_pnLklassItable__; +text: .text%__1cNbranchConNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cJStoreNodeEhash6kM_I_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: memnode.o; +text: .text%__1cNSafePointNodeOnext_exception6kM_p0_; +text: .text%JVM_GetClassModifiers; +text: .text%__1cJCProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJMultiNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%JVM_GetClassAccessFlags; +text: .text%__1cITypeLongEmake6Fx_pk0_; +text: .text%__1cFBlockOcode_alignment6M_I_; +text: .text%__1cJloadPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKSchedulingPComputeUseCount6MpknFBlock__v_; +text: .text%__1cKSchedulingbFComputeRegisterAntidependencies6MpnFBlock__v_; +text: .text%__1cJCatchNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLOptoRuntimeFnew_C6FpnMklassOopDesc_pnKJavaThread__v_; +text: .text%__1cOis_range_check6FpnENode_r12ri_i_: ifnode.o; +text: .text%__1cENodeKreplace_by6Mp0_v_; +text: .text%__1cHTypePtrHget_con6kM_l_; +text: .text%__1cHCmpNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cMloadConPNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cIimmPOperEtype6kM_pknEType__: ad_sparc_clone.o; +text: .text%__1cIMachNodeNis_MachEpilog6M_pnOMachEpilogNode__: ad_sparc.o; +text: .text%Unsafe_CompareAndSwapLong; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceKlass.o; +text: .text%__1cIJumpDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cPfieldDescriptorKinitialize6MpnMklassOopDesc_i_v_; +text: .text%__1cPPerfLongVariantGsample6M_v_; +text: .text%__1cFStateM_sub_Op_RegP6MpknENode__v_; +text: .text%__1cYDebugInformationRecorderTcreate_scope_values6MpnNGrowableArray4CpnKScopeValue____pnKDebugToken__; +text: .text%__1cVcompP_iRegP_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHTypePtrEhash6kM_i_; +text: .text%__1cFBlockLfind_remove6MpknENode__v_; +text: .text%__1cNCompileBrokerLmaybe_block6F_v_; +text: .text%__1cFPhase2t6Mn0ALPhaseNumber__v_; +text: .text%__1cKTypeRawPtrEhash6kM_i_; +text: .text%__1cPcompP_iRegPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcompU_iRegNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: multnode.o; +text: .text%__1cRMachSpillCopyNodeOimplementation6kMpnKCodeBuffer_pnNPhaseRegAlloc_i_I_; +text: .text%__1cRMachSpillCopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLciSignatureLreturn_type6kM_pnGciType__; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: callnode.o; +text: .text%JVM_GetCPMethodSignatureUTF; +text: .text%__1cWstatic_stub_RelocationLunpack_data6M_v_; +text: .text%__1cHMatcherQis_save_on_entry6Mi_i_; +text: .text%__1cOcompU_iRegNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMachOperIconstant6kM_l_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: multnode.o; +text: .text%__1cJloadINodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cMPhaseChaitinSget_spillcopy_wide6MpnENode_2I_2_; +text: .text%__1cPVirtualCallDataPadjust_pointers6M_v_; +text: .text%__1cPVirtualCallDataPfollow_contents6M_v_; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cKstorePNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMURShiftINodeGOpcode6kM_i_; +text: .text%__1cKMemBarNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: machnode.o; +text: .text%__1cKIfTrueNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMMergeMemNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKRegionNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cUArgumentSizeComputerDset6MinJBasicType__v_: frame.o; +text: .text%__1cHTypeIntFempty6kM_i_; +text: .text%__1cSaddI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFframeOis_entry_frame6kM_i_; +text: .text%__1cLMachNopNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIJVMStateNclone_shallow6kM_p0_; +text: .text%__1cITypeLongEmake6Fxx_pk0_; +text: .text%__1cIIndexSetJlrg_union6MIIkIpknIPhaseIFG_rknHRegMask__I_; +text: .text%__1cKDictionaryEfind6MiInMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cVCompressedWriteStream2t6Mi_v_; +text: .text%__1cJiRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKRegionNodeOis_block_start6kM_i_: classes.o; +text: .text%__1cQaddI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHnmethodJis_zombie6kM_i_: nmethod.o; +text: .text%__1cMMergeMemNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: subnode.o; +text: .text%__1cMLinkResolverbAcheck_method_accessability6FnLKlassHandle_11nMmethodHandle_pnGThread__v_; +text: .text%__1cVcompP_iRegP_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: cfgnode.o; +text: .text%__1cNRelocIteratorKinitialize6MlpnICodeBlob_pC3_v_; +text: .text%__1cMMergeMemNodePiteration_setup6Mpk0_v_; +text: .text%__1cKCodeBufferRtransform_address6kMrk0pC_3_; +text: .text%__1cMMergeMemNodeQclone_all_memory6FpnENode__p0_; +text: .text%__1cIGraphKitJclone_map6M_pnNSafePointNode__; +text: .text%__1cKTypeOopPtrWmake_from_klass_common6FpnHciKlass_ii_pk0_; +text: .text%__1cFKlassNlookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cRMemBarReleaseNodeGOpcode6kM_i_; +text: .text%__1cGPcDescHreal_pc6kMpknHnmethod__pC_; +text: .text%__1cNSafePointNodeKmatch_edge6kMI_I_; +text: .text%__1cFBlockJfind_node6kMpknENode__I_; +text: .text%__1cRPSOldPromotionLABFflush6M_v_; +text: .text%__1cLIfFalseNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJStartNodeGpinned6kM_i_: classes.o; +text: .text%__1cLPhaseValuesFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cNPhaseRegAllocKreg2offset6kMnHOptoRegEName__i_; +text: .text%__1cKbranchNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLRegisterMapLpd_location6kMnFVMRegEName__pC_; +text: .text%__1cICallNodeLis_CallLeaf6kM_pknMCallLeafNode__: callnode.o; +text: .text%__1cOcompU_iRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: connode.o; +text: .text%__1cNloadRangeNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRlock_ptr_RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: subnode.o; +text: .text%__1cIBoolNodeKmatch_edge6kMI_I_: subnode.o; +text: .text%__1cNCatchProjNodeMis_CatchProj6kM_pk0_: cfgnode.o; +text: .text%__1cWMutableSpaceUsedHelperLtake_sample6M_x_: spaceCounters.o; +text: .text%__1cCosPelapsed_counter6F_x_; +text: .text%__1cNflagsRegUOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKcmpOpUOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cLRegisterMapFclear6Mpl_v_; +text: .text%__1cTconstantPoolOopDescMklass_ref_at6MipnGThread__pnMklassOopDesc__; +text: .text%__1cENodeFis_If6M_pnGIfNode__: multnode.o; +text: .text%__1cMMergeMemNodePset_base_memory6MpnENode__v_; +text: .text%__1cIciObjectJset_ident6MI_v_; +text: .text%__1cGBitMapOset_difference6M0_v_; +text: .text%__1cMPhaseChaitinFUnion6MpknENode_3_v_; +text: .text%__1cKJNIHandlesKmake_local6FpnHoopDesc__pnI_jobject__; +text: .text%__1cNLoadKlassNodeGOpcode6kM_i_; +text: .text%__1cLstoreI0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConUNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__: callnode.o; +text: .text%__1cSaddI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: multnode.o; +text: .text%__1cENodeHget_ptr6kM_l_; +text: .text%__1cLBoxLockNodeGOpcode6kM_i_; +text: .text%__1cKTypeAryPtrFxmeet6kMpknEType__3_; +text: .text%__1cQciByteCodeStreamFEOBCs6M_nJBytecodesECode__; +text: .text%__1cHAddNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIHaltNodeLbottom_type6kM_pknEType__; +text: .text%__1cQaddP_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKReflectionTverify_class_access6FpnMklassOopDesc_2i_i_; +text: .text%__1cENodeHins_req6MIp0_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: codeBuffer.o; +text: .text%__1cRCompilationPolicyOmustBeCompiled6FnMmethodHandle__i_; +text: .text%__1cRshlL_reg_imm6NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMTypeKlassPtrEhash6kM_i_; +text: .text%__1cLPhaseValuesHlongcon6Mx_pnIConLNode__; +text: .text%__1cOoop_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cQaddI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetMethodIxLocalsCount; +text: .text%__1cSaddP_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: classes.o; +text: .text%__1cOcompI_iRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cPBoundRelocationMupdate_addrs6MpCrknKCodeBuffer_4_1_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: codeBuffer.o; +text: .text%__1cPClassFileParserYverify_legal_method_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cObranchConUNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cObranchConUNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMMergeMemNodeJmemory_at6kMI_pnENode__; +text: .text%__1cOFastUnlockNodeGOpcode6kM_i_; +text: .text%__1cIMachOperOindex_position6kM_i_; +text: .text%__1cMPhaseChaitinJsplit_USE6MpnENode_pnFBlock_2IIiinNGrowableArray4CI__i_I_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: gcm.o; +text: .text%__1cRcmpFastUnlockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIAndINodeGOpcode6kM_i_; +text: .text%JVM_CurrentThread; +text: .text%__1cRMemBarAcquireNodeGOpcode6kM_i_; +text: .text%__1cIJVMStateIof_depth6kMi_p0_; +text: .text%__1cRconstantPoolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLOopRecorderOallocate_index6MpnI_jobject__i_; +text: .text%__1cRMachNullCheckNodeQis_MachNullCheck6M_p0_: machnode.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Ml_v_: relocInfo.o; +text: .text%__1cHnmethodKis_nmethod6kM_i_: nmethod.o; +text: .text%__1cPindOffset13OperNbase_position6kM_i_: ad_sparc.o; +text: .text%__1cPindOffset13OperNconstant_disp6kM_i_: ad_sparc.o; +text: .text%__1cPindOffset13OperFscale6kM_i_: ad_sparc.o; +text: .text%__1cKRegionNodeGis_CFG6kM_i_: loopnode.o; +text: .text%__1cHPhiNodeEmake6FpnENode_2pknEType_pknHTypePtr__p0_; +text: .text%__1cLstoreI0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshlI_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJVectorSet2L6MI_rnDSet__; +text: .text%__1cNloadRangeNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciObject2t6MnGHandle__v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: postaloc.o; +text: .text%__1cMciMethodDataLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cLklassVtableTupdate_super_vtable6MpnNinstanceKlass_pnNmethodOopDesc_i_i_; +text: .text%__1cXmembar_release_lockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cITypeLongFempty6kM_i_; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_: machnode.o; +text: .text%__1cIPhaseCCPFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cSObjectSynchronizerKslow_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cWShouldNotReachHereNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMethodLivenessKBasicBlockWcompute_gen_kill_range6MpnQciByteCodeStream__v_; +text: .text%__1cOCallRelocationWfix_relocation_at_move6Ml_v_; +text: .text%__1cKRelocationXpd_set_call_destination6MpCl_v_; +text: .text%__1cKRelocationTpd_call_destination6M_pC_; +text: .text%__1cKMemBarNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cVcompP_iRegP_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENode2t6Mp01_v_; +text: .text%__1cMloadConLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPciObjectFactoryRcreate_new_object6MpnHoopDesc__pnIciObject__; +text: .text%__1cMLinkResolverZcheck_klass_accessability6FnLKlassHandle_1pnGThread__v_; +text: .text%__1cSvframeStreamCommonPfill_from_frame6M_i_; +text: .text%__1cKstorePNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNObjectMonitorEexit6MpnGThread__v_; +text: .text%__1cIAddPNodeQmach_bottom_type6FpknIMachNode__pknEType__; +text: .text%__1cJVectorSet2t6MpnFArena__v_; +text: .text%__1cOcompU_iRegNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRSignatureIteratorSiterate_parameters6ML_v_; +text: .text%__1cNObjectMonitorFenter6MpnGThread__v_; +text: .text%__1cMMergeMemNodeNset_memory_at6MIpnENode__v_; +text: .text%__1cICallNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cPconvI2L_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSObjectSynchronizerJslow_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cJTraceTime2T6M_v_; +text: .text%__1cPcompP_iRegPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeDcmp6kMrk0_I_; +text: .text%__1cFStateM_sub_Op_ConI6MpknENode__v_; +text: .text%__1cJloadPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPcheckCastPPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNSignatureInfoHdo_void6M_v_: bytecode.o; +text: .text%__1cNmethodOopDescbAcompute_size_of_parameters6MpnGThread__v_; +text: .text%__1cSconstMethodOopDescLobject_size6Fiiii_i_; +text: .text%__1cSconstMethodOopDescZset_inlined_tables_length6Miii_v_; +text: .text%__1cPClassFileParserMparse_method6MnSconstantPoolHandle_ipnLAccessFlags_pnPtypeArrayHandle_55pnGThread__nMmethodHandle__; +text: .text%__1cNmethodOopDescLobject_size6Fi_i_; +text: .text%__1cLmethodKlassIallocate6MnRconstMethodHandle_nLAccessFlags_pnGThread__pnNmethodOopDesc__; +text: .text%__1cLklassVtableWneeds_new_vtable_entry6FpnNmethodOopDesc_pnMklassOopDesc_pnHoopDesc_pnNsymbolOopDesc_nLAccessFlags__i_; +text: .text%__1cQconstMethodKlassIallocate6MiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cKoopFactoryKnew_method6FinLAccessFlags_iiipnGThread__pnNmethodOopDesc__; +text: .text%__1cNmethodOopDescJinit_code6M_v_; +text: .text%__1cFStateM_sub_Op_AddP6MpknENode__v_; +text: .text%__1cFciEnvSget_klass_by_index6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cKo0RegPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cJTraceTime2t6MpkcpnMelapsedTimer_iipnMoutputStream__v_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: connode.o; +text: .text%__1cITypeFuncEhash6kM_i_; +text: .text%__1cRcmpFastUnlockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKStoreBNodeGOpcode6kM_i_; +text: .text%__1cNmethodOopDescPis_final_method6kM_i_; +text: .text%__1cNmethodOopDescLlink_method6FnMmethodHandle__v_; +text: .text%__1cIRewriterOrewrite_method6FnMmethodHandle_rnIintArray_pnGThread__1_; +text: .text%__1cLRShiftINodeGOpcode6kM_i_; +text: .text%__1cMURShiftLNodeGOpcode6kM_i_; +text: .text%__1cLCastP2LNodeGOpcode6kM_i_; +text: .text%__1cPClassFileParserbDverify_legal_method_modifiers6MiinMsymbolHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cQaddI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRshrI_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKklassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cKklassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJMarkSweepXrevisit_weak_klass_link6FpnFKlass__v_; +text: .text%__1cFKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cFMutexNowned_by_self6kM_i_; +text: .text%__1cMCallLeafNodeGOpcode6kM_i_; +text: .text%__1cKRelocationLunpack_data6M_v_: codeBlob.o; +text: .text%__1cKstoreINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cWconstantPoolCacheKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cOMachReturnNodeNis_MachReturn6M_p0_: ad_sparc_misc.o; +text: .text%__1cJCatchNodeIis_Catch6kM_pk0_: classes.o; +text: .text%__1cIGraphKitEstop6M_v_; +text: .text%__1cLklassVtableToop_adjust_pointers6M_v_; +text: .text%__1cLklassVtableToop_follow_contents6M_v_; +text: .text%__1cLProfileDataPadjust_pointers6M_v_: ciMethodData.o; +text: .text%__1cLProfileDataPfollow_contents6M_v_: ciMethodData.o; +text: .text%__1cLConvI2LNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPSignatureStream2t6MnMsymbolHandle_i_v_; +text: .text%__1cITypeFuncCeq6kMpknEType__i_; +text: .text%__1cSaddP_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: subnode.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: callnode.o; +text: .text%__1cLRegisterMap2t6MpnKJavaThread_i_v_; +text: .text%__1cIMachNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cIMachOperMdisp_as_type6kM_pknHTypePtr__: ad_sparc.o; +text: .text%__1cMloadConDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTCreateExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOPhaseIdealLoopIsplit_up6MpnENode_22_i_; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: memnode.o; +text: .text%__1cQaddI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cICallNodeHis_Call6M_p0_: classes.o; +text: .text%__1cOcompI_iRegNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetCPMethodClassNameUTF; +text: .text%__1cENodeIadd_prec6Mp0_v_; +text: .text%__1cHCompileYout_preserve_stack_slots6F_I_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: machnode.o; +text: .text%__1cOcompI_iRegNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFStateM_sub_Op_ConL6MpknENode__v_; +text: .text%__1cENodeHis_Call6M_pnICallNode__: memnode.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: memnode.o; +text: .text%__1cIAddLNodeGOpcode6kM_i_; +text: .text%__1cVcompP_iRegP_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJlabelOperFlabel6kM_pnFLabel__: ad_sparc.o; +text: .text%__1cSaddP_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: memnode.o; +text: .text%__1cMmerge_region6FpnKRegionNode_pnIPhaseGVN__pnENode__: cfgnode.o; +text: .text%__1cNloadRangeNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciMethodPliveness_at_bci6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessPget_liveness_at6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessKBasicBlockPget_liveness_at6MpnIciMethod_i_nGBitMap__; +text: .text%__1cHAddNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMTypeKlassPtrCeq6kMpknEType__i_; +text: .text%__1cHTypePtrJsingleton6kM_i_; +text: .text%__1cIGraphKitObasic_plus_adr6MpnENode_2l_2_; +text: .text%__1cNinstanceKlassVshould_be_initialized6kM_i_; +text: .text%__1cXmembar_acquire_lockNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLciSignatureHtype_at6kMi_pnGciType__; +text: .text%__1cFStateM_sub_Op_RegI6MpknENode__v_; +text: .text%__1cPClassFileParserWparse_linenumber_table6MIIpipnGThread__pC_; +text: .text%__1cbFCompressedLineNumberWriteStream2t6Mi_v_; +text: .text%__1cSconstMethodOopDescbBcompressed_linenumber_table6kM_pC_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: memnode.o; +text: .text%__1cNSafePointNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitLclean_stack6Mi_v_; +text: .text%__1cNflagsRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKbranchNodeFclone6kM_pnENode__; +text: .text%__1cMUniverseOperFclone6kM_pnIMachOper__; +text: .text%__1cJlabelOperFclone6kM_pnIMachOper__; +text: .text%__1cNSafePointNode2t6MIpnIJVMState__v_; +text: .text%__1cWMachCallStaticJavaNodePret_addr_offset6M_i_; +text: .text%__1cJStartNodeGis_CFG6kM_i_: callnode.o; +text: .text%__1cPSignatureStreamJas_symbol6MpnGThread__pnNsymbolOopDesc__; +text: .text%__1cLConvI2LNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cICmpPNodeDsub6kMpknEType_3_3_; +text: .text%__1cFParseKensure_phi6Mii_pnHPhiNode__; +text: .text%__1cNloadRangeNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cMloadConDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJAssemblerOpatched_branch6Fiii_i_; +text: .text%__1cJAssemblerSbranch_destination6Fii_i_; +text: .text%JVM_IsNaN; +text: .text%__1cIRootNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: subnode.o; +text: .text%__1cKstoreINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLCounterDataOis_CounterData6M_i_: ciMethodData.o; +text: .text%__1cQaddI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cVcompP_iRegP_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cITypeFuncEmake6FpknJTypeTuple_3_pk0_; +text: .text%__1cICodeBlobJis_zombie6kM_i_: codeBlob.o; +text: .text%__1cXruntime_call_RelocationEtype6M_nJrelocInfoJrelocType__: ad_sparc.o; +text: .text%__1cKbranchNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddP_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFframeOis_first_frame6kM_i_; +text: .text%__1cHMemNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cPcompP_iRegPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserZskip_over_field_signature6MpciIpnGThread__1_; +text: .text%__1cOGenerateOopMapHinterp16MpnOBytecodeStream__v_; +text: .text%__1cIHaltNodeGpinned6kM_i_: classes.o; +text: .text%__1cKBufferBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cJStoreNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOPhaseIdealLoopIset_idom6MpnENode_2I_v_; +text: .text%__1cWShouldNotReachHereNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cWShouldNotReachHereNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeMmonitorenter6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cMflagsRegOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cJLoadPNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRMachSafePointNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cKRelocationJpack_data6M_i_: ad_sparc.o; +text: .text%__1cIGraphKitQkill_dead_locals6M_v_; +text: .text%__1cLklassItableToop_adjust_pointers6M_v_; +text: .text%__1cNinstanceKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cNinstanceKlassUadjust_static_fields6M_v_; +text: .text%__1cSinstanceKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLklassItableToop_follow_contents6M_v_; +text: .text%__1cSinstanceKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cRconstantPoolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cRconstantPoolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNinstanceKlassUfollow_static_fields6M_v_; +text: .text%__1cPindOffset13OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cPindOffset13OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cPindOffset13OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cNloadKlassNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cIAddINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cSaddP_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHOrINodeGOpcode6kM_i_; +text: .text%__1cLBoxLockNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOGenerateOopMapPjump_targets_do6MpnOBytecodeStream_pFp0ipi_v4_i_; +text: .text%__1cMMergeMemNode2t6MpnENode__v_; +text: .text%__1cMLinkResolverZcheck_field_accessability6FnLKlassHandle_11rnPfieldDescriptor_pnGThread__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_iipnGThread__v_; +text: .text%__1cSInterpreterRuntimeLmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cMPhaseIterGVNHmakecon6MpknEType__pnHConNode__; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: ad_sparc.o; +text: .text%__1cIRootNodeNis_block_proj6kM_pknENode__: classes.o; +text: .text%__1cOCallRelocationFvalue6M_pC_: ad_sparc.o; +text: .text%__1cSCallLeafDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cCosMvm_page_size6F_i_; +text: .text%__1cNloadKlassNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPconvI2L_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJStartNodeGOpcode6kM_i_; +text: .text%__1cOcompI_iRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJCodeCacheQfind_blob_unsafe6Fpv_pnICodeBlob__; +text: .text%__1cUcompI_iReg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMTypeKlassPtrEmake6FnHTypePtrDPTR_pnHciKlass_i_pk0_; +text: .text%__1cSvframeStreamCommonEnext6M_v_; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: cfgnode.o; +text: .text%__1cYDebugInformationRecorderOdescribe_scope6MpnIciMethod_ipnKDebugToken_44_v_; +text: .text%__1cYDebugInformationRecorderVcreate_monitor_values6MpnNGrowableArray4CpnMMonitorValue____pnKDebugToken__; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: debugInfoRec.o; +text: .text%__1cJStoreNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSaddI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNMachIdealNodeJnum_opnds6kM_I_: ad_sparc.o; +text: .text%__1cENodeQlatency_from_use6kMrnLBlock_Array_rnNGrowableArray4CI__pk0p0_i_; +text: .text%__1cPcompP_iRegPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Copy6kM_I_: machnode.o; +text: .text%__1cWconstantPoolCacheKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWconstantPoolCacheKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQPreserveJVMState2t6MpnIGraphKit_i_v_; +text: .text%__1cQPreserveJVMState2T6M_v_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: codeBlob.o; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: bytecode.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: subnode.o; +text: .text%__1cMPhaseChaitinPset_was_spilled6MpnENode__v_; +text: .text%__1cQsubI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: node.o; +text: .text%__1cRshlL_reg_imm6NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeHis_Type6M_pnITypeNode__: classes.o; +text: .text%__1cKStoreCNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitRnull_check_common6MpnENode_nJBasicType_i_2_; +text: .text%__1cMloadConLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRloadConP_pollNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLstoreI0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIAddINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMloadConLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cVcompP_iRegP_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHTypeAryCeq6kMpknEType__i_; +text: .text%__1cIGraphKit2t6MpnIJVMState__v_; +text: .text%__1cKbranchNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cETypeFwiden6kMpk0_2_: type.o; +text: .text%__1cJloadINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMWarmCallInfoHis_cold6kM_i_; +text: .text%__1cSaddI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPciInstanceKlassLfield_cache6M_pnTciConstantPoolCache__; +text: .text%__1cHciFieldJwill_link6MpnPciInstanceKlass_nJBytecodesECode__i_; +text: .text%__1cFciEnvSget_field_by_index6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cQciByteCodeStreamJget_field6Mri_pnHciField__; +text: .text%__1cCosGmalloc6FL_pv_; +text: .text%__1cPciInstanceKlassYunique_concrete_subklass6M_p0_; +text: .text%__1cXmembar_release_lockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassKinitialize6MpnGThread__v_; +text: .text%__1cRlock_ptr_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOoop_RelocationJpack_data6M_i_; +text: .text%__1cKstoreINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowNmake_range_at6Mi_pn0AFRange__; +text: .text%__1cOPhaseIdealLoopQconditional_move6MpnENode__2_; +text: .text%__1cMloadConLNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseMmerge_common6Mpn0AFBlock_i_v_; +text: .text%__1cJloadSNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cILoadNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cICodeBlobTfix_oop_relocations6M_v_; +text: .text%__1cRcmpFastUnlockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHCompileKTracePhase2t6MpkcpnMelapsedTimer_i_v_; +text: .text%JVM_GetCPFieldClassNameUTF; +text: .text%__1cMtlsLoadPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_: callnode.o; +text: .text%__1cTCreateExceptionNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSafePointNodeGpinned6kM_i_: classes.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: subnode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: subnode.o; +text: .text%__1cQsubI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: graphKit.o; +text: .text%__1cOGenerateOopMapEpush6MnNCellTypeState__v_; +text: .text%__1cKCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciObjectIencoding6M_pnI_jobject__; +text: .text%__1cOGenerateOopMapDpop6M_nNCellTypeState__; +text: .text%__1cNmethodOopDescIbcp_from6kMi_pC_; +text: .text%__1cTCreateExceptionNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cITypeLongFxdual6kM_pknEType__; +text: .text%__1cOtypeArrayKlassSallocate_permanent6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cGBitMapVset_union_with_result6M0_i_; +text: .text%__1cQaddP_reg_regNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cKCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: frame.o; +text: .text%__1cLBoxLockNodeHsize_of6kM_I_; +text: .text%__1cICmpINodeDsub6kMpknEType_3_3_; +text: .text%__1cIBoolNodeJideal_reg6kM_I_: subnode.o; +text: .text%__1cHCmpNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJloadINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTciConstantPoolCacheDget6Mi_pv_; +text: .text%__1cJLoadSNodeGOpcode6kM_i_; +text: .text%__1cITypeFuncEmake6FpnIciMethod__pk0_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: subnode.o; +text: .text%__1cHRetNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPcompP_iRegPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeHis_Goto6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadRangeNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLPhaseValuesHmakecon6MpknEType__pnHConNode__; +text: .text%__1cOGenerateOopMapRdo_exception_edge6MpnOBytecodeStream__v_; +text: .text%__1cObranchConPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMstringStreamFwrite6MpkcL_v_; +text: .text%__1cOkill_dead_code6FpnENode_pnMPhaseIterGVN__i_: node.o; +text: .text%__1cIMachNodeJis_MachIf6kM_pknKMachIfNode__: ad_sparc_misc.o; +text: .text%__1cFStateK_sub_Op_If6MpknENode__v_; +text: .text%__1cVjava_lang_ClassLoaderbBnon_reflection_class_loader6FpnHoopDesc__2_; +text: .text%__1cRshlI_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionarybEresolve_instance_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cKTypeOopPtrHget_con6kM_l_; +text: .text%__1cPconvI2L_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: connode.o; +text: .text%__1cMLinkResolverMresolve_pool6FrnLKlassHandle_rnMsymbolHandle_42nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverOresolve_invoke6FrnICallInfo_nGHandle_nSconstantPoolHandle_inJBytecodesECode_pnGThread__v_; +text: .text%__1cXInterpreterFrameClosureJoffset_do6Mi_v_: frame.o; +text: .text%__1cRInlineCacheBufferIcontains6FpC_i_; +text: .text%__1cIAndLNodeGOpcode6kM_i_; +text: .text%__1cICmpUNodeDsub6kMpknEType_3_3_; +text: .text%__1cSCallLeafDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcompI_iRegNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNCatchProjNodeLbottom_type6kM_pknEType__: cfgnode.o; +text: .text%__1cQciByteCodeStreamKget_method6Mri_pnIciMethod__; +text: .text%__1cFciEnvTget_method_by_index6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvYget_method_by_index_impl6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvNlookup_method6MpnNinstanceKlass_2pnNsymbolOopDesc_4nJBytecodesECode__pnNmethodOopDesc__; +text: .text%__1cKTypeAryPtrFklass6kM_pnHciKlass__; +text: .text%__1cJloadFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPcheckCastPPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKDictionaryKfind_class6MiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cKRelocationLspec_simple6FnJrelocInfoJrelocType__nQRelocationHolder__; +text: .text%__1cJCodeCacheEnext6FpnICodeBlob__2_; +text: .text%__1cHnmethodIis_alive6kM_i_: nmethod.o; +text: .text%__1cNloadRangeNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSsafePoint_pollNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIciMethodbCinterpreter_invocation_count6M_i_; +text: .text%__1cNCatchProjNodeHsize_of6kM_I_: cfgnode.o; +text: .text%__1cKciTypeFlowFRangeNget_block_for6Mpn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cWConstantPoolCacheEntryJset_field6MnJBytecodesECode_2nLKlassHandle_iinITosState_ii_v_; +text: .text%__1cSInterpreterRuntimePresolve_get_put6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_ipnGThread__v_; +text: .text%__1cPcmpFastLockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cXvirtual_call_RelocationIparse_ic6FrpnICodeBlob_rpC5rppnHoopDesc_pi_nNRelocIterator__; +text: .text%__1cXmembar_acquire_lockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJLoadBNodeGOpcode6kM_i_; +text: .text%__1cRshlL_reg_imm6NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitOset_all_memory6MpnENode__v_; +text: .text%__1cRcmpFastUnlockNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJStartNodeIis_Start6M_p0_: callnode.o; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: node.o; +text: .text%__1cUGenericGrowableArrayMraw_contains6kMpknEGrET__i_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: callnode.o; +text: .text%__1cRcmpFastUnlockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHMatcherScalling_convention6FpnLRegPair_Ii_v_; +text: .text%__1cHBitDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cIJVMStateKclone_deep6kM_p0_; +text: .text%__1cENodeNadd_req_batch6Mp0I_v_; +text: .text%__1cIGraphKitTadd_safepoint_edges6MpnNSafePointNode_i_v_; +text: .text%__1cIJVMStateLdebug_depth6kM_I_; +text: .text%__1cNLoadRangeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJLoadLNodeGOpcode6kM_i_; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_: systemDictionary.o; +text: .text%__1cRshlL_reg_imm6NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRshrP_reg_imm6NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIGraphKitTadd_exception_state6MpnNSafePointNode__v_; +text: .text%__1cOcompU_iRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRInterpretedRFrameKtop_method6kM_nMmethodHandle__: rframe.o; +text: .text%__1cFframeQoops_do_internal6MpnKOopClosure_pnLRegisterMap_i_v_; +text: .text%__1cRshrI_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPsp_ptr_RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cPconvI2L_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLstoreB0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMMachCallNodeHis_Call6M_pnICallNode__: ad_sparc_misc.o; +text: .text%__1cICmpPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFParseFBlockKinit_graph6Mp0_v_; +text: .text%__1cSInterpreterRuntimeOresolve_invoke6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cRRawBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cIMachNodeOpipeline_class6F_pknIPipeline__; +text: .text%__1cMPrefetchNodeGOpcode6kM_i_; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: thread.o; +text: .text%__1cNloadRangeNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreB0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowLStateVectorOpush_translate6MpnGciType__v_; +text: .text%__1cICodeHeapLheader_size6F_L_; +text: .text%__1cNinstanceKlassSlookup_osr_nmethod6kMkpnNmethodOopDesc_i_pnHnmethod__; +text: .text%__1cRloadConP_pollNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPcompP_iRegPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodOis_not_entrant6kM_i_: nmethod.o; +text: .text%__1cKciTypeFlowLStateVectorEmeet6Mpk1_i_; +text: .text%__1cWConstantPoolCacheEntryKset_method6MnJBytecodesECode_nMmethodHandle_i_v_; +text: .text%__1cRloadConP_pollNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIPhaseGVNUtransform_no_reclaim6MpnENode__2_; +text: .text%__1cIimmIOperIconstant6kM_l_: ad_sparc_clone.o; +text: .text%__1cKCompiledICOic_destination6kM_pC_; +text: .text%__1cKCompiledIC2t6MpnKRelocation__v_; +text: .text%__1cCosEfree6Fpv_v_; +text: .text%__1cLConvL2INodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: cfgnode.o; +text: .text%__1cIMulLNodeGOpcode6kM_i_; +text: .text%__1cJOopMapSetSfind_map_at_offset6kMii_pnGOopMap__; +text: .text%__1cICodeBlobbAoop_map_for_return_address6MpCi_pnGOopMap__; +text: .text%__1cFciEnvIis_in_vm6F_i_; +text: .text%__1cFParseMdo_one_block6M_v_; +text: .text%__1cILoadNodeHsize_of6kM_I_; +text: .text%__1cQsubI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionarybCfind_instance_or_array_klass6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cPCountedLoopNodeGOpcode6kM_i_; +text: .text%__1cQaddP_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMVirtualSpaceOcommitted_size6kM_L_; +text: .text%__1cKTypeOopPtrFempty6kM_i_; +text: .text%__1cRMachSafePointNode2t6M_v_; +text: .text%__1cHMatcherKmatch_sfpt6MpnNSafePointNode__pnIMachNode__; +text: .text%__1cLOptoRuntimePnew_typeArray_C6FnJBasicType_ipnKJavaThread__v_; +text: .text%__1cNbranchConNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_nGHandle_2ipnGThread__pnMklassOopDesc__; +text: .text%__1cPVirtualCallDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cMindirectOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cICallNodeOis_CallRuntime6kM_pknPCallRuntimeNode__: callnode.o; +text: .text%__1cLstoreI0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAddINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cENodeFis_If6M_pnGIfNode__: cfgnode.o; +text: .text%__1cFciEnvWget_klass_by_name_impl6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cXinsert_anti_dependences6FrpnFBlock_pnENode_rnLBlock_Array__i_: gcm.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: callnode.o; +text: .text%__1cNloadConP0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRloadConP_pollNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMethodLivenessKBasicBlockJpropagate6Mp0_v_; +text: .text%__1cKciTypeFlowGJsrSet2t6MpnFArena_i_v_; +text: .text%__1cMFastLockNodeGOpcode6kM_i_; +text: .text%__1cHMatcherPc_frame_pointer6kM_nHOptoRegEName__; +text: .text%__1cOPhaseIdealLoopIsink_use6MpnENode_2_v_; +text: .text%__1cMMachCallNode2t6M_v_; +text: .text%__1cFBlockKsched_call6MrnHMatcher_rnLBlock_Array_IrnJNode_List_pipnMMachCallNode_rnJVectorSet__I_; +text: .text%__1cMloadConINodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cHTypeAryFxmeet6kMpknEType__3_; +text: .text%__1cKmethodOperGmethod6kM_l_: ad_sparc.o; +text: .text%__1cIGraphKitbDtransfer_exceptions_into_jvms6M_pnIJVMState__; +text: .text%__1cOPhaseIdealLoopRregister_new_node6MpnENode_2_v_; +text: .text%__1cIciMethodRhas_compiled_code6M_i_; +text: .text%__1cNflagsRegUOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFKlassIsubklass6kM_p0_; +text: .text%__1cMciMethodDataLhas_trap_at6MpnLProfileData_i_i_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: connode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: connode.o; +text: .text%__1cURethrowExceptionNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cIGraphKitJsync_jvms6kM_pnIJVMState__; +text: .text%__1cLTypeInstPtrFxdual6kM_pknEType__; +text: .text%__1cPemit_call_reloc6FrnKCodeBuffer_lnJrelocInfoJrelocType_iii_v_; +text: .text%__1cGOopMap2t6Mii_v_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodKlass.o; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: methodLiveness.o; +text: .text%__1cEUTF8Hstrrchr6FpWiW_1_; +text: .text%__1cSaddI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserXverify_legal_field_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_field_signature6MnMsymbolHandle_1pnGThread__v_; +text: .text%__1cJOopMapSetKadd_gc_map6MiipnGOopMap__v_; +text: .text%__1cTOopMapForCacheEntryRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cQsubI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNprefetch2NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%JVM_GetCPMethodNameUTF; +text: .text%__1cKstoreINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cGvframe2t6MpknFframe_pknLRegisterMap_pnKJavaThread__v_; +text: .text%__1cLRegisterMap2t6Mpk0_v_; +text: .text%__1cIGraphKitbLset_predefined_input_for_runtime_call6MpnNSafePointNode__v_; +text: .text%__1cKReturnNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeHis_Goto6kM_I_: cfgnode.o; +text: .text%__1cQSystemDictionarybOfind_constrained_instance_or_array_klass6FnMsymbolHandle_nGHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cJLoadCNodeGOpcode6kM_i_; +text: .text%__1cHConNodeEmake6FpknEType__p0_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: ciTypeFlow.o; +text: .text%__1cGvframeKnew_vframe6FpknFframe_pknLRegisterMap_pnKJavaThread__p0_; +text: .text%__1cHOopFlowNbuild_oop_map6MpnENode_ipnNPhaseRegAlloc_pi_pnGOopMap__; +text: .text%__1cRMachSafePointNodeLset_oop_map6MpnGOopMap__v_: ad_sparc_misc.o; +text: .text%__1cRMachSafePointNodePis_MachCallLeaf6M_pnQMachCallLeafNode__: ad_sparc_misc.o; +text: .text%__1cNMachIdealNodePoper_input_base6kM_I_: machnode.o; +text: .text%__1cQjava_lang_StringQbasic_create_oop6FpnQtypeArrayOopDesc_ipnGThread__pnHoopDesc__; +text: .text%__1cIciSymbol2t6MnMsymbolHandle__v_; +text: .text%__1cILoopNodeGOpcode6kM_i_; +text: .text%__1cYDebugInformationRecorderNadd_safepoint6MiipnGOopMap__v_; +text: .text%__1cHCompileTProcess_OopMap_Node6MpnIMachNode_i_v_; +text: .text%__1cPsp_ptr_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOMacroAssemblerZneeds_explicit_null_check6Fl_i_; +text: .text%__1cRshlL_reg_imm6NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cNinstanceKlassbBallocate_permanent_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: callnode.o; +text: .text%__1cHPhiNodeEmake6FpnENode_2_p0_; +text: .text%__1cICmpINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cTconstantPoolOopDescOstring_at_impl6FnSconstantPoolHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cEUTF8Sconvert_to_unicode6FpkcpHi_v_; +text: .text%__1cHTypeIntFxdual6kM_pknEType__; +text: .text%__1cLPCTableNodeLbottom_type6kM_pknEType__; +text: .text%__1cXjava_lang_ref_ReferenceNreferent_addr6FpnHoopDesc__p2_; +text: .text%__1cRshrI_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPorI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKReturnNodeKmatch_edge6kMI_I_; +text: .text%__1cKBranchDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cNCatchProjNodeEhash6kM_I_; +text: .text%__1cRMachSafePointNodeSis_MachCallRuntime6M_pnTMachCallRuntimeNode__: ad_sparc_misc.o; +text: .text%__1cRMachSafePointNodeRis_safepoint_node6kM_i_: ad_sparc_misc.o; +text: .text%__1cQsubI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKReturnNodeGOpcode6kM_i_; +text: .text%__1cOPhaseIdealLoopGspinup6MpnENode_2222pnLsmall_cache__2_; +text: .text%__1cNinstanceKlassPinitialize_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cMCreateExNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__: callnode.o; +text: .text%__1cNSafePointNodeEhash6kM_I_: callnode.o; +text: .text%__1cRMachSafePointNodeWis_MachCallInterpreter6M_pnXMachCallInterpreterNode__: ad_sparc_misc.o; +text: .text%__1cNSignatureInfoGdo_int6M_v_: frame.o; +text: .text%__1cKstoreCNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cICallNodeJideal_reg6kM_I_: callnode.o; +text: .text%__1cTconstantPoolOopDescbBbasic_type_for_signature_at6Mi_nJBasicType__; +text: .text%__1cPmethodDataKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLBoxLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerEcall6MpCnJrelocInfoJrelocType__v_: ad_sparc.o; +text: .text%__1cHCompileZintrinsic_insertion_index6MpnIciMethod_i_i_; +text: .text%__1cMPhaseIterGVNJtransform6MpnENode__2_; +text: .text%__1cEUTF8Ounicode_length6Fpkci_i_; +text: .text%__1cIPhaseIFGFUnion6MII_v_; +text: .text%__1cKStoreLNodeGOpcode6kM_i_; +text: .text%__1cMCallJavaNodeLis_CallJava6kM_pk0_: callnode.o; +text: .text%__1cQMachCallJavaNodePis_MachCallJava6M_p0_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopHdom_lca6kMpnENode_2_2_; +text: .text%__1cRshlI_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICallNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cYCallStaticJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCallGenerator2t6MpnIciMethod__v_; +text: .text%__1cWMachCallStaticJavaNodeVis_MachCallStaticJava6M_p0_: ad_sparc_misc.o; +text: .text%__1cPThreadLocalNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKstoreCNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: connode.o; +text: .text%__1cPClassFileParserbCverify_legal_field_modifiers6MiipnGThread__v_; +text: .text%__1cPClassFileParserWparse_field_attributes6MnSconstantPoolHandle_iHpHpi2pnPtypeArrayHandle_pnGThread__v_; +text: .text%__1cYCallStaticJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYCallStaticJavaDirectNodeKmethod_set6Ml_v_; +text: .text%__1cNsymbolOopDescKas_unicode6kMri_pH_; +text: .text%__1cLStringTableGintern6FpnNsymbolOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cOPhaseIdealLoopKhandle_use6MpnENode_2pnLsmall_cache_22222_v_; +text: .text%__1cOPhaseIdealLoopOfind_use_block6MpnENode_22222_2_; +text: .text%__1cKciTypeFlowKflow_block6Mpn0AFBlock_pn0ALStateVector_pn0AGJsrSet__v_; +text: .text%__1cKciTypeFlowFBlockKsuccessors6MpnQciByteCodeStream_pn0ALStateVector_pn0AGJsrSet__pnNGrowableArray4Cp1___; +text: .text%__1cKciTypeFlowQadd_to_work_list6Mpn0AFBlock__v_; +text: .text%__1cINodeHashLhash_insert6MpnENode__v_; +text: .text%__1cILoopNodeHis_Loop6M_p0_: classes.o; +text: .text%__1cSCallLeafDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJVectorSetFClear6M_v_; +text: .text%__1cEUTF8Enext6FpkcpH_pc_; +text: .text%__1cKstorePNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeJset_req_X6MIp0pnMPhaseIterGVN__v_; +text: .text%__1cJloadLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPorI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowFBlock2t6Mp0pn0AFRange_pn0AGJsrSet__v_; +text: .text%__1cKciTypeFlowFBlockScompute_exceptions6M_v_; +text: .text%__1cYciExceptionHandlerStreamFcount6M_i_; +text: .text%__1cMindIndexOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cSmembar_acquireNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSaddP_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLLShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRcmpFastUnlockNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cObranchConPNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: memnode.o; +text: .text%__1cLstoreB0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMelapsedTimerFstart6M_v_; +text: .text%__1cMelapsedTimerEstop6M_v_; +text: .text%__1cGciTypeMis_classless6kM_i_: ciType.o; +text: .text%__1cQmark_inner_loops6FpnIPhaseCFG_pnFBlock__v_: block.o; +text: .text%__1cHCompileSflatten_alias_type6kMpknHTypePtr__3_; +text: .text%__1cRInterpretedRFrameEinit6M_v_; +text: .text%__1cENodeLnonnull_req6kM_p0_; +text: .text%__1cRshrI_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverbNlinktime_resolve_virtual_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%JVM_GetCPFieldSignatureUTF; +text: .text%__1cYcompareAndSwapL_boolNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMLinkResolverbEruntime_resolve_virtual_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cPconvI2L_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cICmpLNodeGOpcode6kM_i_; +text: .text%__1cHnmethodZsize_of_exception_handler6F_i_; +text: .text%__1cFKlassXcan_be_statically_bound6FpnNmethodOopDesc__i_; +text: .text%__1cSMemBarCPUOrderNodeGOpcode6kM_i_; +text: .text%__1cKMemBarNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHMulNodeEhash6kM_I_; +text: .text%__1cHnmethodKcopy_pc_at6MipnGPcDesc__v_; +text: .text%__1cGPcDesc2t6Miii_v_; +text: .text%__1cLPhaseValuesHzerocon6MnJBasicType__pnHConNode__; +text: .text%__1cRshlI_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIGraphKit2t6M_v_; +text: .text%__1cYcompareAndSwapL_boolNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: loopnode.o; +text: .text%jni_DeleteLocalRef: jni.o; +text: .text%__1cIGraphKitNset_map_clone6MpnNSafePointNode__v_; +text: .text%__1cOMethodLivenessNmake_block_at6Mipn0AKBasicBlock__2_; +text: .text%__1cMoutputStreamDput6Mc_v_; +text: .text%__1cMtlsLoadPNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cENodeLbottom_type6kM_pknEType__; +text: .text%__1cIAddPNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cPcmpFastLockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cICallNodeSis_CallDynamicJava6kM_pknTCallDynamicJavaNode__: callnode.o; +text: .text%__1cMCreateExNodeGpinned6kM_i_: classes.o; +text: .text%__1cSInterpreterRuntimeDldc6FpnKJavaThread_i_v_; +text: .text%__1cIXorINodeGOpcode6kM_i_; +text: .text%__1cPCountedLoopNodeOis_CountedLoop6M_p0_: classes.o; +text: .text%__1cObranchConUNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlassKlass.o; +text: .text%__1cRshlL_reg_imm6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKJNIHandlesKmake_local6FpnGThread_pnHoopDesc__pnI_jobject__; +text: .text%__1cFParsePdo_field_access6Mii_v_; +text: .text%__1cILoadNodeEmake6FpnENode_22pknHTypePtr_pknEType_nJBasicType__p0_; +text: .text%__1cOloadConI13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXjava_lang_ref_ReferencePdiscovered_addr6FpnHoopDesc__p2_; +text: .text%__1cSmembar_acquireNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cRshrI_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddP_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: callnode.o; +text: .text%__1cKoopFactoryXnew_permanent_charArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cQjava_lang_StringbBcreate_tenured_from_unicode6FpHipnGThread__nGHandle__; +text: .text%__1cIGraphKitNuncommon_trap6MipnHciKlass_pkci_v_; +text: .text%__1cIregDOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cTmembar_CPUOrderNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNloadConP0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompileKalias_type6MpnHciField__pn0AJAliasType__; +text: .text%__1cLOptoRuntimeSuncommon_trap_Type6F_pknITypeFunc__; +text: .text%__1cIHaltNode2t6MpnENode_2_v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: ad_sparc.o; +text: .text%__1cRMachNullCheckNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: machnode.o; +text: .text%__1cPmethodDataKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cPmethodDataKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cTStackWalkCompPolicyRcompilation_level6MnMmethodHandle_i_i_; +text: .text%__1cRsarI_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cXmembar_acquire_lockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: ad_sparc.o; +text: .text%__1cQaddI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNinstanceKlassLfind_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cHMonitorKnotify_all6M_i_; +text: .text%__1cNloadKlassNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIciSymbolEmake6Fpkc_p0_; +text: .text%__1cRshlI_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCompareAndSwapNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cQmulL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: ad_sparc_misc.o; +text: .text%__1cJFieldTypeOget_array_info6FpnNsymbolOopDesc_pip2pnGThread__nJBasicType__; +text: .text%__1cJFieldTypeYis_valid_array_signature6FpnNsymbolOopDesc__i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: callnode.o; +text: .text%__1cNloadConP0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: ad_sparc.o; +text: .text%__1cWstatic_stub_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cMPhaseChaitinNFind_compress6MpknENode__I_; +text: .text%__1cIGraphKitJmake_load6MpnENode_2pknEType_nJBasicType_i_2_; +text: .text%__1cPorI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciMethodRget_flow_analysis6M_pnKciTypeFlow__; +text: .text%__1cSsafePoint_pollNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKbranchNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKbranchNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNLoadKlassNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%jni_GetObjectField: jni.o; +text: .text%__1cNbranchConNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRethrowNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cNmethodOopDescWwas_executed_more_than6kMi_i_; +text: .text%__1cOcompI_iRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitOreplace_in_map6MpnENode_2_v_; +text: .text%__1cKRegionNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIAddINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: connode.o; +text: .text%__1cNloadKlassNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQandL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cINodeHashJhash_find6MpknENode__p1_; +text: .text%__1cHCompileKTracePhase2T6M_v_; +text: .text%__1cXjava_lang_ref_ReferenceJnext_addr6FpnHoopDesc__p2_; +text: .text%__1cJloadSNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: nmethod.o; +text: .text%__1cJloadBNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_: methodDataOop.o; +text: .text%__1cYinlineCallClearArrayNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFChunk2n6FLL_pv_; +text: .text%__1cMPhaseChaitinLclone_projs6MpnFBlock_IpnENode_4rI_i_; +text: .text%__1cQciByteCodeStreamZget_declared_field_holder6M_pnPciInstanceKlass__; +text: .text%__1cHTypeIntFwiden6kMpknEType__3_; +text: .text%__1cKciTypeFlowGJsrSetNapply_control6Mp0pnQciByteCodeStream_pn0ALStateVector__v_; +text: .text%__1cMLinkResolverbFlinktime_resolve_special_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_special_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_4ipnGThread__v_; +text: .text%__1cIJVMState2t6MpnIciMethod_p0_v_; +text: .text%__1cOMethodLivenessKBasicBlock2t6Mp0ii_v_; +text: .text%__1cOMethodLivenessKBasicBlockQcompute_gen_kill6MpnIciMethod__v_; +text: .text%__1cHAddNodePadd_of_identity6kMpknEType_3_3_; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: memnode.o; +text: .text%__1cJTypeTupleKmake_range6FpnLciSignature__pk0_; +text: .text%__1cTCompareAndSwapLNodeGOpcode6kM_i_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6FnUtypeArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cJTypeTupleLmake_domain6FpnPciInstanceKlass_pnLciSignature__pk0_; +text: .text%__1cOGenerateOopMapFppush6MpnNCellTypeState__v_; +text: .text%__1cMTypeKlassPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cLstoreI0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshrP_reg_imm6NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cTCreateExceptionNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMtlsLoadPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadConP0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQandL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKMemoryPoolYrecord_peak_memory_usage6M_v_; +text: .text%__1cHTypeIntEmake6Fii_pk0_; +text: .text%__1cHCompilebAallow_range_check_smearing6kM_i_; +text: .text%__1cSsafePoint_pollNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_: ciMethodData.o; +text: .text%__1cMWarmCallInfoGis_hot6kM_i_; +text: .text%__1cMWarmCallInfoKalways_hot6F_p0_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: graphKit.o; +text: .text%__1cLBuildCutout2t6MpnIGraphKit_pnENode_ff_v_; +text: .text%__1cRshlI_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreCNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: memnode.o; +text: .text%__1cJloadLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHMulNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMURShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFParseKdo_get_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cKarrayKlassLobject_size6kMi_i_; +text: .text%__1cIProjNodeDcmp6kMrknENode__I_; +text: .text%__1cKMemBarNodeEhash6kM_I_; +text: .text%__1cPcompP_iRegPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__: loopnode.o; +text: .text%__1cKRegionNodeGpinned6kM_i_: loopnode.o; +text: .text%__1cLstoreB0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKJavaThreadbHcheck_and_handle_async_exceptions6Mi_v_; +text: .text%__1cLBoxLockNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cRsarI_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJOopMapSetTupdate_register_map6FpknFframe_pnICodeBlob_pnLRegisterMap__v_; +text: .text%__1cNloadKlassNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPorI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKciTypeFlowLStateVectorJdo_invoke6MpnQciByteCodeStream_i_v_; +text: .text%__1cMMergeMemNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKRelocationJpack_data6M_i_: relocInfo.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: memnode.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: cfgnode.o; +text: .text%__1cNCatchProjNode2t6MpnENode_Ii_v_; +text: .text%__1cNCatchProjNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFKlassOis_subclass_of6kMpnMklassOopDesc__i_; +text: .text%__1cIGraphKitZadd_exception_states_from6MpnIJVMState__v_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: cfgnode.o; +text: .text%__1cLPCTableNodeKis_PCTable6kM_pk0_: classes.o; +text: .text%__1cNloadRangeNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryEfind6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cHciFieldPinitialize_from6MpnPfieldDescriptor__v_; +text: .text%__1cGRFrame2t6MnFframe_pnKJavaThread_kp0_v_; +text: .text%__1cIciMethodTcall_profile_at_bci6Mi_nNciCallProfile__; +text: .text%__1cHCompileOcall_generator6MpnIciMethod_ipnIJVMState_if_pnNCallGenerator__; +text: .text%__1cNciCallProfileRapply_prof_factor6Mf_v_; +text: .text%__1cHCompileOfind_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cWCallLeafNoFPDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMPhaseChaitinTsplit_Rematerialize6MpnENode_pnFBlock_IrInNGrowableArray4CI__ipIp2i_2_; +text: .text%__1cFciEnvbTget_instance_klass_for_declared_method_holder6FpnHciKlass__pnPciInstanceKlass__; +text: .text%__1cFParseHdo_call6M_v_; +text: .text%__1cIGraphKitWround_double_arguments6MpnIciMethod__v_; +text: .text%__1cQciByteCodeStreambAget_declared_method_holder6M_pnHciKlass__; +text: .text%__1cIGraphKitTround_double_result6MpnIciMethod__v_; +text: .text%__1cFParseMprofile_call6MpnENode__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlass.o; +text: .text%__1cQmulL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKarrayKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cTmembar_CPUOrderNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLklassVtableIindex_of6kMpnNmethodOopDesc_i_i_; +text: .text%__1cNloadKlassNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLPCTableNodeHsize_of6kM_I_: classes.o; +text: .text%__1cNGCTaskManagerIget_task6MI_pnGGCTask__; +text: .text%__1cLGCTaskQdDueueGremove6M_pnGGCTask__; +text: .text%__1cNGCTaskManagerYshould_release_resources6MI_i_; +text: .text%__1cLGCTaskQdDueueHenqueue6MpnGGCTask__v_; +text: .text%__1cNGCTaskManagerPnote_completion6MI_v_; +text: .text%__1cPcmpFastLockNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitUmake_exception_state6MpnENode__pnNSafePointNode__; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceKlass.o; +text: .text%__1cPcmpFastLockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverVresolve_invokevirtual6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMloadConFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSobjArrayKlassKlassIoop_size6kMpnHoopDesc__i_: objArrayKlassKlass.o; +text: .text%__1cKstoreCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLProfileDataOtranslate_from6Mp0_v_: ciMethodData.o; +text: .text%__1cFStateM_sub_Op_ConP6MpknENode__v_; +text: .text%__1cIAddINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cFframeVinterpreter_frame_bci6kM_i_; +text: .text%__1cSaddP_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMachNodeHtwo_adr6kM_I_: machnode.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: machnode.o; +text: .text%__1cRsarI_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOCompilerOracleOshould_exclude6FnMmethodHandle__i_; +text: .text%__1cIciMethodLscale_count6Mi_i_; +text: .text%__1cIBoolNodeHsize_of6kM_I_; +text: .text%__1cGGCTask2t6M_v_; +text: .text%__1cSsafePoint_pollNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cGIfNodeHsize_of6kM_I_: classes.o; +text: .text%__1cICodeHeapSallocated_capacity6kM_L_; +text: .text%__1cOGenerateOopMapbAget_basic_block_containing6kMi_pnKBasicBlock__; +text: .text%__1cOCallRelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cCosOis_interrupted6FpnGThread_i_i_; +text: .text%__1cJloadCNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cOstackSlotLOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cLLShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIGraphKitMsaved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cKBranchDataNis_BranchData6M_i_: ciMethodData.o; +text: .text%__1cLmethodKlassNoop_is_method6kM_i_: methodKlass.o; +text: .text%__1cKimmL13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cOstackSlotLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNSafePointNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIJumpDataLis_JumpData6M_i_: ciMethodData.o; +text: .text%__1cILoopNodeHis_Loop6M_p0_: loopnode.o; +text: .text%__1cSstkL_to_regD_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshlL_reg_imm6NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cTcan_branch_register6FpnENode_1_i_; +text: .text%__1cQCallLeafNoFPNodeGOpcode6kM_i_; +text: .text%__1cGThreadXclear_pending_exception6M_v_; +text: .text%__1cSPSPromotionManagerbBgc_thread_promotion_manager6Fi_p0_; +text: .text%__1cSstkL_to_regD_2NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadPNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPBytecode_invokeJsignature6kM_pnNsymbolOopDesc__; +text: .text%__1cFframebGinterpreter_callee_receiver_addr6MnMsymbolHandle__ppnHoopDesc__; +text: .text%__1cNSignatureInfoGdo_int6M_v_: bytecode.o; +text: .text%__1cKInlineTreeMok_to_inline6MpnIciMethod_pnIJVMState_rnNciCallProfile_pnMWarmCallInfo__8_; +text: .text%__1cIciMethodbAinterpreter_throwout_count6kM_i_; +text: .text%__1cOCompilerOracleNshould_inline6FnMmethodHandle__i_; +text: .text%__1cIciMethodOshould_exclude6M_i_; +text: .text%__1cIciMethodNshould_inline6M_i_; +text: .text%__1cKInlineTreeWfind_subtree_from_root6Fp0pnIJVMState_pnIciMethod_i_1_; +text: .text%__1cRshrI_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXmembar_release_lockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cICHeapObj2n6FL_pv_; +text: .text%__1cIGraphKitTset_all_memory_call6MpnENode__v_; +text: .text%__1cFTypeDCeq6kMpknEType__i_; +text: .text%__1cIciMethodWwas_executed_more_than6Mi_i_; +text: .text%__1cWShouldNotReachHereNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIHaltNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIGraphKitOtoo_many_traps6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cWShouldNotReachHereNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitTtoo_many_recompiles6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cJloadBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMloadConINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: connode.o; +text: .text%__1cCosFsleep6FpnGThread_xi_i_; +text: .text%__1cIos_sleep6Fxi_i_: os_solaris.o; +text: .text%__1cRInvocationCounterJset_carry6M_v_; +text: .text%__1cKimmL13OperIconstant6kM_l_: ad_sparc_clone.o; +text: .text%__1cFTypeDEhash6kM_i_; +text: .text%__1cPconvL2I_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRInterpreterOopMap2T6M_v_; +text: .text%__1cLOopMapCacheGlookup6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cRInterpreterOopMapLiterate_oop6MpnNOffsetClosure__v_; +text: .text%__1cNinstanceKlassImask_for6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cRInterpreterOopMap2t6M_v_; +text: .text%__1cNmethodOopDescImask_for6MipnRInterpreterOopMap__v_; +text: .text%__1cFframeToops_interpreted_do6MpnKOopClosure_pknLRegisterMap_i_v_; +text: .text%__1cOClearArrayNodeGOpcode6kM_i_; +text: .text%__1cJStartNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cJloadSNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQandL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIMachNodeRget_base_and_disp6kMrlrpknHTypePtr__pknENode__; +text: .text%__1cJloadLNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodKlass.o; +text: .text%jni_ExceptionOccurred: jni.o; +text: .text%__1cJloadSNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cNNativeFarCallKis_call_at6FpC_i_; +text: .text%__1cHoopDescSslow_identity_hash6M_l_; +text: .text%__1cSObjectSynchronizerXidentity_hash_value_for6FnGHandle__l_; +text: .text%__1cYcompareAndSwapL_boolNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetByteArrayRegion: jni.o; +text: .text%__1cQSystemDictionarybBresolve_array_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cLstoreP0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConUNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cUcompU_iReg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQPlaceholderTableKfind_entry6MiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cKBufferBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cKBufferBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cSHighResTimeSamplerLtake_sample6M_x_: statSampler.o; +text: .text%__1cRis_error_reported6F_i_; +text: .text%__1cMPeriodicTaskOreal_time_tick6FL_v_; +text: .text%__1cPStatSamplerTaskEtask6M_v_: statSampler.o; +text: .text%__1cNWatcherThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cURethrowExceptionNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPciInstanceKlassFsuper6M_p0_; +text: .text%__1cKciTypeFlowLStateVectorStype_meet_internal6FpnGciType_3p0_3_; +text: .text%__1cETypeRget_typeflow_type6FpnGciType__pk0_; +text: .text%__1cNmethodOopDescbHhas_unloaded_classes_in_signature6FnMmethodHandle_pnGThread__i_; +text: .text%__1cWCallLeafNoFPDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetArrayLength: jni.o; +text: .text%__1cIGraphKitNcast_not_null6MpnENode__2_; +text: .text%__1cJStartNodeIis_Start6M_p0_: classes.o; +text: .text%__1cLLShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSInterpreterRuntimebAfrequency_counter_overflow6FpnKJavaThread_pC_n0AJIcoResult__; +text: .text%__1cQsubI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMindIndexOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cVCompressedWriteStreamEgrow6M_v_; +text: .text%__1cWCallLeafNoFPDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQandL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_Write; +text: .text%__1cPClassFileParserbDverify_legal_method_signature6MnMsymbolHandle_1pnGThread__i_; +text: .text%__1cOJNIHandleBlockHoops_do6MpnKOopClosure__v_; +text: .text%__1cOMachReturnNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cPcheckCastPPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassVis_same_class_package6FpnHoopDesc_pnNsymbolOopDesc_24_i_; +text: .text%__1cLciSignature2t6MpnHciKlass_pnIciSymbol__v_; +text: .text%__1cIciMethod2t6MnMmethodHandle__v_; +text: .text%__1cFParseRensure_memory_phi6Mii_pnHPhiNode__; +text: .text%__1cEUTF8Fequal6FpWi1i_i_; +text: .text%__1cICodeHeapIcapacity6kM_L_; +text: .text%__1cMCodeHeapPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cMCodeHeapPoolNused_in_bytes6M_L_: memoryPool.o; +text: .text%__1cKMemoryPoolImax_size6kM_L_: memoryPool.o; +text: .text%__1cENodeGis_Con6kM_I_: multnode.o; +text: .text%__1cGThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%JVM_RawMonitorExit; +text: .text%__1cFMutexMjvm_raw_lock6M_v_; +text: .text%JVM_RawMonitorEnter; +text: .text%__1cIParmNodeJideal_reg6kM_I_; +text: .text%__1cFMutexOjvm_raw_unlock6M_v_; +text: .text%__1cLRethrowNodeGOpcode6kM_i_; +text: .text%__1cPClassFileParserUskip_over_field_name6MpciI_1_; +text: .text%__1cRbranchLoopEndNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLTypeInstPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cSmembar_acquireNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cYinlineCallClearArrayNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMloadConFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNmethodOopDescWload_signature_classes6FnMmethodHandle_pnGThread__i_; +text: .text%__1cQConstantIntValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cTconstantPoolOopDescbDresolve_string_constants_impl6FnSconstantPoolHandle_pnGThread__v_; +text: .text%__1cNCompileBrokerOcompile_method6FnMmethodHandle_i1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNCompileBrokerTcompile_method_base6FnMmethodHandle_ii1ipkcpnGThread__pnHnmethod__; +text: .text%__1cWstatic_stub_RelocationJpack_data6M_i_; +text: .text%__1cOJNIHandleBlockNrelease_block6Fp0pnGThread__v_; +text: .text%__1cNprefetch2NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cXinitialize_static_field6FpnPfieldDescriptor_pnGThread__v_: classFileParser.o; +text: .text%__1cbBopt_virtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: cfgnode.o; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: classes.o; +text: .text%__1cOJNIHandleBlockOallocate_block6FpnGThread__p0_; +text: .text%__1cKg1RegIOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cIimmLOperJconstantL6kM_x_: ad_sparc_clone.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: loopnode.o; +text: .text%__1cLLShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJCodeCacheIcontains6Fpv_i_; +text: .text%__1cQmulL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: assembler_sparc.o; +text: .text%__1cPcmpFastLockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIGraphKitNbuiltin_throw6MnODeoptimizationLDeoptReason_pnENode__v_; +text: .text%__1cFParseUprofile_taken_branch6Mi_v_; +text: .text%__1cFParseFmerge6Mi_v_; +text: .text%__1cHConNodeLout_RegMask6kM_rknHRegMask__: classes.o; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: bytecode.o; +text: .text%__1cIGraphKitHjava_bc6kM_nJBytecodesECode__; +text: .text%__1cJloadBNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cMloadConFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKMemBarNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cPciInstanceKlassUget_canonical_holder6Mi_p0_; +text: .text%__1cNCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cLLShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMLinkResolverTresolve_static_call6FrnICallInfo_rnLKlassHandle_nMsymbolHandle_53iipnGThread__v_; +text: .text%__1cLPCTableNodeEhash6kM_I_; +text: .text%__1cFframeRoops_code_blob_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cJOopMapSetGall_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure_pFppnHoopDesc_9E_v9B9B_v_; +text: .text%__1cJOopMapSetHoops_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cOloadConI13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlass.o; +text: .text%__1cJloadLNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cSandI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFArena2T6M_v_; +text: .text%__1cSbranchCon_longNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLOptoRuntimebCcomplete_monitor_unlocking_C6FpnHoopDesc_pnJBasicLock__v_; +text: .text%__1cQandI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLOptoRuntimebAcomplete_monitor_locking_C6FpnHoopDesc_pnJBasicLock_pnKJavaThread__v_; +text: .text%__1cNflagsRegLOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cMPhaseChaitinNFind_compress6MI_I_; +text: .text%__1cJTypeTupleFxdual6kM_pknEType__; +text: .text%__1cQandI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLRuntimeStubYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cMloadConLNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cYinlineCallClearArrayNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJStoreNodeSIdeal_masked_input6MpnIPhaseGVN_I_pnENode__; +text: .text%__1cKTypeAryPtrFempty6kM_i_; +text: .text%__1cHTypeAryFempty6kM_i_; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: psTasks.o; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnMklassOopDesc__i_; +text: .text%__1cRcmpFastUnlockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMapHget_var6Mi_nNCellTypeState__; +text: .text%__1cJStoreNodeZIdeal_sign_extended_input6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKTypeRawPtrCeq6kMpknEType__i_; +text: .text%__1cRshlI_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFStateN_sub_Op_LoadP6MpknENode__v_; +text: .text%__1cIAddLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cJloadCNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadSNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsarI_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadBNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKMemBarNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLklassVtableXvtable_accessibility_at6Mi_n0AKAccessType__; +text: .text%__1cSaddL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%jni_GetSuperclass: jni.o; +text: .text%__1cKInlineTreePshouldNotInline6kMpnIciMethod_pnMWarmCallInfo__pkc_; +text: .text%__1cLOpaque1NodeGOpcode6kM_i_; +text: .text%__1cIJumpDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cQxorI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosbCstack_shadow_pages_available6FpnGThread_nMmethodHandle__i_; +text: .text%__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_; +text: .text%__1cTAbstractInterpreterbFsize_top_interpreter_activation6FpnNmethodOopDesc__i_; +text: .text%__1cIciMethodbHhas_unloaded_classes_in_signature6M_i_; +text: .text%__1cFframeNis_glue_frame6kM_i_; +text: .text%__1cObranchConPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMtlsLoadPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMindirectOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKcmpOpPOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cRshrP_reg_imm6NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmpFastLockNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cObranchConPNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cObranchConPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIimmPOperIconstant6kM_l_: ad_sparc_clone.o; +text: .text%__1cIciObjectFklass6M_pnHciKlass__; +text: .text%__1cISubINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLOptoRuntimeOnew_objArray_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cFframeUentry_frame_is_first6kM_i_; +text: .text%__1cGciType2t6MnLKlassHandle__v_; +text: .text%__1cQjava_lang_StringGlength6FpnHoopDesc__i_; +text: .text%__1cIGraphKitPstore_to_memory6MpnENode_22nJBasicType_i_2_; +text: .text%__1cJStoreNodeEmake6FpnENode_22pknHTypePtr_2nJBasicType__p0_; +text: .text%__1cHciKlass2t6MnLKlassHandle__v_; +text: .text%__1cKstoreLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRshrP_reg_imm6NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseRoptimize_inlining6MpnIciMethod_ipnPciInstanceKlass_24irnKInlineTreeLInlineStyle_r2_v_; +text: .text%__1cJcmpOpOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cQmulL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNloadKlassNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRinterpretedVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cITypeFuncFxdual6kM_pknEType__; +text: .text%__1cICHeapObj2k6Fpv_v_; +text: .text%__1cHPhiNodeMslice_memory6kMpknHTypePtr__p0_; +text: .text%__1cXPhaseAggressiveCoalesceYinsert_copy_with_overlap6MpnFBlock_pnENode_II_v_; +text: .text%__1cOGenerateOopMapGppload6MpnNCellTypeState_i_v_; +text: .text%__1cFTypeFEhash6kM_i_; +text: .text%__1cFKlassTarray_klass_or_null6Mi_pnMklassOopDesc__; +text: .text%__1cFArena2t6M_v_; +text: .text%__1cRmethodDataOopDescJbci_to_dp6Mi_pC_; +text: .text%__1cPorI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_: classes.o; +text: .text%__1cFParseOreturn_current6MpnENode__v_; +text: .text%__1cLstoreP0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMURShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPciInstanceKlass2t6MnLKlassHandle__v_; +text: .text%__1cILoadNodeDcmp6kMrknENode__I_; +text: .text%__1cPCheckCastPPNodeJideal_reg6kM_I_: connode.o; +text: .text%__1cENodeRlatency_from_uses6kMrnLBlock_Array_rnNGrowableArray4CI___i_; +text: .text%__1cNMemoryServiceXtrack_memory_pool_usage6FpnKMemoryPool__v_; +text: .text%__1cQxorI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHMatcherPstack_alignment6F_I_; +text: .text%__1cFStateM_sub_Op_CmpI6MpknENode__v_; +text: .text%__1cOGenerateOopMapJdo_method6Miiii_v_; +text: .text%__1cIBoolNodeDcmp6kMrknENode__I_; +text: .text%jni_ReleasePrimitiveArrayCritical: jni.o; +text: .text%jni_GetPrimitiveArrayCritical: jni.o; +text: .text%__1cETypeCeq6kMpk0_i_; +text: .text%__1cSmembar_releaseNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowGJsrSetSis_compatible_with6Mp1_i_; +text: .text%__1cHnmethodHoops_do6MpnKOopClosure__v_; +text: .text%__1cWstatic_stub_RelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cRloadConP_pollNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cNimmP_pollOperEtype6kM_pknEType__: ad_sparc_clone.o; +text: .text%__1cMMonitorValue2t6MpnKScopeValue_nILocation__v_; +text: .text%__1cMMonitorValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cLBoxLockNodeKstack_slot6FpnENode__nHOptoRegEName__; +text: .text%__1cLBoxLockNodeKis_BoxLock6kM_pk0_: classes.o; +text: .text%__1cFciEnvVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cHnmethodbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cLRethrowNodeKmatch_edge6kMI_I_; +text: .text%__1cIregFOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cKTypeAryPtrFxdual6kM_pknEType__; +text: .text%__1cOMacroAssemblerEjump6MrnHAddress_ipkci_v_; +text: .text%__1cOMacroAssemblerFjumpl6MrnHAddress_pnMRegisterImpl_ipkci_v_; +text: .text%__1cIMulINodeGOpcode6kM_i_; +text: .text%__1cHTypeAryEmake6FpknEType_pknHTypeInt__pk0_; +text: .text%__1cHciKlassNis_subtype_of6Mp0_i_; +text: .text%__1cRcompL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRshrI_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciMethodLis_accessor6kM_i_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlass.o; +text: .text%__1cKCastPPNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cFParseRbranch_prediction6Mrf_f_; +text: .text%__1cFStateM_sub_Op_AddI6MpknENode__v_; +text: .text%__1cMURShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKstorePNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJMultiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: callnode.o; +text: .text%__1cMloadConINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTconstantPoolOopDescbCklass_ref_at_if_loaded_check6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cHciField2t6MpnPciInstanceKlass_i_v_; +text: .text%__1cKstoreINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGOpcode6kM_i_; +text: .text%__1cIMachNodeJis_MachIf6kM_pknKMachIfNode__: ad_sparc.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: cfgnode.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: cfgnode.o; +text: .text%__1cOPhaseIdealLoopOsplit_thru_phi6MpnENode_2i_2_; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_: loopnode.o; +text: .text%__1cRshrP_reg_imm6NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMURShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSCallLeafDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cLCastP2LNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cISubINodeDsub6kMpknEType_3_3_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2T6M_v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2t6M_v_; +text: .text%__1cFParseLbuild_exits6M_v_; +text: .text%__1cIciMethodPcan_be_compiled6M_i_; +text: .text%__1cFParseIdo_exits6M_v_; +text: .text%__1cFParseQcreate_entry_map6M_pnNSafePointNode__; +text: .text%__1cIciMethodVhas_balanced_monitors6M_i_; +text: .text%__1cFParse2t6MpnIJVMState_pnIciMethod_f_v_; +text: .text%__1cFArenaEused6kM_L_; +text: .text%__1cOParseGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: callnode.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: callnode.o; +text: .text%__1cNbranchConNodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_IsSameObject: jni.o; +text: .text%__1cNCallGeneratorKfor_inline6FpnIciMethod_f_p0_; +text: .text%__1cFParsePdo_method_entry6M_v_; +text: .text%__1cGRFrameGcaller6M_p0_; +text: .text%__1cFframeLreal_sender6kMpnLRegisterMap__0_; +text: .text%__1cFframeTis_first_java_frame6kM_i_; +text: .text%__1cFframeNis_java_frame6kM_i_; +text: .text%__1cFBlockTimplicit_null_check6MrnLBlock_Array_rnNGrowableArray4CI__pnENode_6_v_; +text: .text%__1cNinstanceKlassbDcheck_valid_for_instantiation6MipnGThread__v_; +text: .text%__1cKstoreLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQandI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNbranchConNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cNbranchConNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmembar_releaseNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVLoaderConstraintTableJadd_entry6MnMsymbolHandle_pnMklassOopDesc_nGHandle_34pnGThread__i_; +text: .text%__1cbACallCompiledJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeHdel_out6Mp0_v_: library_call.o; +text: .text%__1cJiRegIOperFclone6kM_pnIMachOper__; +text: .text%__1cLSymbolTableFprobe6Fpkci_pnNsymbolOopDesc__; +text: .text%__1cKTypeRawPtrFxmeet6kMpknEType__3_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodDataKlass.o; +text: .text%__1cQandI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPClassFileParserbCverify_legal_class_modifiers6MipnGThread__v_; +text: .text%__1cPCheckCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHnmethodPis_locked_by_vm6kM_i_: nmethod.o; +text: .text%__1cQLibraryIntrinsicKis_virtual6kM_i_: library_call.o; +text: .text%__1cRciVirtualCallDataOtranslate_from6MpnLProfileData__v_; +text: .text%__1cZPhaseConservativeCoalesceKupdate_ifg6MIIpnIIndexSet_2_v_; +text: .text%__1cZPhaseConservativeCoalesceMunion_helper6MpnENode_2II222pnFBlock_I_v_; +text: .text%__1cIIndexSetEswap6Mp0_v_; +text: .text%__1cXmembar_release_lockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRRawBytecodeStream2t6MnMmethodHandle__v_; +text: .text%__1cJStartNodeOis_block_start6kM_i_: callnode.o; +text: .text%__1cLOpaque1NodeEhash6kM_I_; +text: .text%__1cLAccessFlagsPatomic_set_bits6Mi_v_; +text: .text%__1cFTypeFCeq6kMpknEType__i_; +text: .text%__1cbACallCompiledJavaDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKHandleAreaHoops_do6MpnKOopClosure__v_; +text: .text%__1cHCompileFstart6kM_pnJStartNode__; +text: .text%__1cNinstanceKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cMCallLeafNodeLis_CallLeaf6kM_pk0_: classes.o; +text: .text%__1cPThreadLocalNodeGOpcode6kM_i_; +text: .text%__1cHnmethodVcleanup_inline_caches6M_v_; +text: .text%__1cSbranchCon_longNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKDictionaryStry_get_next_class6M_pnMklassOopDesc__; +text: .text%__1cNinstanceKlassKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cQSystemDictionaryStry_get_next_class6F_pnMklassOopDesc__; +text: .text%__1cJStartNodeOis_block_start6kM_i_: classes.o; +text: .text%__1cUThreadSafepointStateXexamine_state_of_thread6Mi_v_; +text: .text%__1cUSafepointSynchronizeOsafepoint_safe6FpnKJavaThread_nPJavaThreadState__i_; +text: .text%__1cQxorI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cYDebugInformationRecorderNadd_dependent6MpnPciInstanceKlass_pnIciMethod__v_; +text: .text%__1cFParseYprofile_not_taken_branch6M_v_; +text: .text%__1cHnmethodLis_unloaded6kM_i_: nmethod.o; +text: .text%__1cLRegisterMapIpd_clear6M_v_; +text: .text%__1cIciObject2t6M_v_; +text: .text%__1cJloadCNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cUParallelScavengeHeapNtlab_capacity6kM_L_; +text: .text%__1cRbranchLoopEndNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMciMethodData2t6M_v_; +text: .text%__1cPciObjectFactoryUget_empty_methodData6M_pnMciMethodData__; +text: .text%__1cMPrefetchNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIregFOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cGBitMapIset_from6M0_v_; +text: .text%__1cIMulLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cIciMethodbBinterpreter_call_site_count6Mi_i_; +text: .text%__1cNinstanceKlassPlink_class_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cFParseFdo_if6MpnENode_2nIBoolTestEmask_2_v_; +text: .text%__1cZCallDynamicJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKciTypeFlowFBlockPclone_loop_head6Mp0ip1pn0AGJsrSet__3_; +text: .text%__1cSconstMethodOopDescZchecked_exceptions_length6kM_i_; +text: .text%__1cHCompileXin_preserve_stack_slots6M_I_; +text: .text%__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cKTypeOopPtrEhash6kM_i_; +text: .text%__1cIMulINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLPCTableNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMCreateExNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cXJNI_ArgumentPusherVaArgKget_object6M_v_: jni.o; +text: .text%__1cNCatchProjNodeDcmp6kMrknENode__I_; +text: .text%__1cIGraphKitRmake_slow_call_ex6MpnENode_pnPciInstanceKlass__v_; +text: .text%__1cIAddLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cJloadLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassQset_next_sibling6MpnMklassOopDesc__v_; +text: .text%__1cKJavaThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cHMulNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cYinlineCallClearArrayNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJCodeCacheMfind_nmethod6Fpv_pnHnmethod__; +text: .text%__1cMCreateExNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNloadRangeNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJloadBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSandI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshrP_reg_imm6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cbACallCompiledJavaDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: callnode.o; +text: .text%__1cFKlassMnext_sibling6kM_p0_; +text: .text%__1cPconvL2I_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapTmerge_state_into_bb6MpnKBasicBlock__v_; +text: .text%__1cFBlockUhoist_LCA_above_defs6Mp01IrnLBlock_Array__1_; +text: .text%__1cQaddL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQinstanceRefKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQinstanceRefKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%JVM_GetMethodIxModifiers; +text: .text%__1cHciKlassOis_subclass_of6Mp0_i_; +text: .text%__1cQjava_lang_StringGequals6FpnHoopDesc_pHi_i_; +text: .text%__1cMnegF_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHSubNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cVExceptionHandlerTableMadd_subtable6MipnNGrowableArray4Cl__2_v_; +text: .text%__1cTCreateExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRshrL_reg_imm6NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNSCMemProjNodeGOpcode6kM_i_; +text: .text%__1cMvframeStream2t6MpnKJavaThread_i_v_; +text: .text%__1cLRShiftLNodeGOpcode6kM_i_; +text: .text%__1cWThreadLocalAllocBufferFclear6M_v_; +text: .text%__1cDCHANprocess_class6FnLKlassHandle_pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___nMsymbolHandle_6_v_; +text: .text%JVM_IsInterface; +text: .text%__1cPThreadRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cPThreadRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cLsymbolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cIMulLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQaddL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHMatcherQis_spillable_arg6Fi_i_; +text: .text%__1cParrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cParrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cRsarI_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapUreachable_basicblock6Fp0ipi_v_; +text: .text%__1cNprefetch2NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cGThreadQunboost_priority6Fp0_v_; +text: .text%__1cSstkL_to_regD_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHOrINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJloadINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cICodeHeapIallocate6ML_pv_; +text: .text%__1cICodeHeapPsearch_freelist6ML_pnJFreeBlock__; +text: .text%__1cFParseSmerge_memory_edges6MpnMMergeMemNode_ii_v_; +text: .text%__1cMPrefetchNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cQput_after_lookup6FnMsymbolHandle_0ppnLNameSigHash__i_; +text: .text%__1cJCodeCacheIallocate6Fi_pnICodeBlob__; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_: codeBlob.o; +text: .text%__1cKstoreLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRcompL_reg_conNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQxorI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cUPipeline_Use_Element2t6M_v_: output.o; +text: .text%__1cKRelocationJpack_data6M_i_: codeBlob.o; +text: .text%__1cSstkL_to_regD_2NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cXmembar_release_lockNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cYcompareAndSwapL_boolNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadBNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNflagsRegLOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cJloadCNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompileSregister_intrinsic6MpnNCallGenerator__v_; +text: .text%__1cRInterpretedRFrameOis_interpreted6kM_i_: rframe.o; +text: .text%__1cGRFrameLis_compiled6kM_i_: rframe.o; +text: .text%__1cSstkL_to_regD_2NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKTypeRawPtrHget_con6kM_l_; +text: .text%__1cKRegionNodeEhash6kM_I_: loopnode.o; +text: .text%__1cSstkL_to_regD_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Ml_v_: codeBlob.o; +text: .text%__1cSstkL_to_regD_2NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cTCallDynamicJavaNodeGOpcode6kM_i_; +text: .text%__1cJloadCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSstkL_to_regD_2NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__: loopnode.o; +text: .text%__1cMloadConLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFciEnvRfind_system_klass6MpnIciSymbol__pnHciKlass__; +text: .text%__1cPciInstanceKlassLfind_method6MpnIciSymbol_2_pnIciMethod__; +text: .text%__1cIGraphKitYcombine_exception_states6MpnNSafePointNode_2_v_; +text: .text%__1cQaddL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHUNICODEHas_utf86FpHi_pc_; +text: .text%__1cLstoreB0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPciInstanceKlassTget_field_by_offset6Mii_pnHciField__; +text: .text%__1cNinstanceKlassWfind_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cIDivINodeGOpcode6kM_i_; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_: classes.o; +text: .text%__1cSobjArrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cSobjArrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cKo0RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciObjectFactory.o; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjectFactory.o; +text: .text%__1cHnmethodKpc_desc_at6MpCi_pnGPcDesc__; +text: .text%__1cHMonitorEwait6Mil_i_; +text: .text%__1cSconstMethodOopDescYchecked_exceptions_start6kM_pnXCheckedExceptionElement__; +text: .text%__1cUcompU_iReg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQconstMethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cPorI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadSNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMinINodeGOpcode6kM_i_; +text: .text%__1cTStackWalkCompPolicyXmethod_invocation_event6MnMmethodHandle_pnGThread__v_; +text: .text%__1cKJavaThreadQlast_java_vframe6MpnLRegisterMap__pnKjavaVFrame__; +text: .text%__1cKjavaVFrameNis_java_frame6kM_i_: vframe.o; +text: .text%__1cTStackWalkCompPolicyVfindTopInlinableFrame6MpnNGrowableArray4CpnGRFrame____2_; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_nMmethodHandle__v_; +text: .text%__1cLLShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPClassFileParserYparse_checked_exceptions6MpHInSconstantPoolHandle_pnGThread__1_; +text: .text%__1cSvframeStreamCommonbHskip_method_invoke_and_aux_frames6M_v_; +text: .text%__1cNNativeFarCallPset_destination6MpC_v_; +text: .text%__1cIGraphKitXset_edges_for_java_call6MpnMCallJavaNode_i_v_; +text: .text%__1cJStartNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cObranchConUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitZset_results_for_java_call6MpnMCallJavaNode__pnENode__; +text: .text%__1cISubLNodeGOpcode6kM_i_; +text: .text%__1cZCallInterpreterDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLOpaque1NodeLbottom_type6kM_pknEType__: connode.o; +text: .text%__1cKcmpOpUOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cObranchConUNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cObranchConUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPClassFileStreamHskip_u26MipnGThread__v_; +text: .text%__1cLmethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cOMacroAssemblerNverify_thread6M_v_; +text: .text%__1cRbranchLoopEndNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIGraphKitMarray_length6MpnENode__2_; +text: .text%__1cLBlock_ArrayEgrow6MI_v_; +text: .text%__1cTciConstantPoolCacheGinsert6Mipv_v_; +text: .text%__1cSaddL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_: methodDataOop.o; +text: .text%__1cSCountedLoopEndNodeKstride_con6kM_i_; +text: .text%__1cMjniIdSupportNto_method_oop6FpnK_jmethodID__pnNmethodOopDesc__; +text: .text%__1cRSignatureIterator2t6MpnGThread_pnNsymbolOopDesc__v_; +text: .text%__1cNSafePointNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPciObjArrayKlassSis_obj_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cHciField2t6MpnPfieldDescriptor__v_; +text: .text%__1cJloadLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLLShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNinstanceKlassXmark_dependent_nmethods6MpnMklassOopDesc__i_; +text: .text%__1cOMacroAssemblerUallocate_oop_address6MpnI_jobject_pnMRegisterImpl__nHAddress__; +text: .text%__1cKstoreBNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLBoxLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvL2I_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSbranchCon_longNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQciByteCodeStreamMget_constant6M_nKciConstant__; +text: .text%__1cFciEnvVget_constant_by_index6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cFciEnvbAget_constant_by_index_impl6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cJimmU5OperIconstant6kM_l_: ad_sparc_clone.o; +text: .text%__1cKMemBarNode2t6M_v_; +text: .text%__1cJloadSNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUvisit_all_interfaces6FpnPobjArrayOopDesc_pnXInterfaceVisiterClosure__v_; +text: .text%__1cSmembar_acquireNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIimmPOperPconstant_is_oop6kM_i_: ad_sparc_clone.o; +text: .text%__1cHRetNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIregFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cSaddI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRshrP_reg_imm6NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCodeBufferMstart_a_stub6M_v_; +text: .text%__1cTemit_java_to_interp6FrnKCodeBuffer__v_; +text: .text%__1cKCodeBufferKend_a_stub6M_v_; +text: .text%__1cObranchConPNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cKcmpOpPOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cLstoreP0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJiRegPOperFclone6kM_pnIMachOper__; +text: .text%__1cKMemBarNodeJis_MemBar6kM_pk0_: classes.o; +text: .text%__1cQandL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitOinsert_mem_bar6MpnKMemBarNode__v_; +text: .text%__1cRcompL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cXvirtual_call_RelocationLunpack_data6M_v_; +text: .text%__1cLRShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOMethodLivenessKBasicBlockJstore_one6Mi_v_; +text: .text%__1cKstoreBNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: memnode.o; +text: .text%__1cKoopFactoryTnew_system_objArray6FipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cOcompU_iRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOFastUnlockNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cLConvL2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cRbranchLoopEndNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescOis_initializer6kM_i_; +text: .text%__1cKStoreCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKMemBarNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMloadConPNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cKimmI13OperFclone6kM_pnIMachOper__; +text: .text%__1cNnmethodLocker2T6M_v_; +text: .text%__1cOloadConI13NodeFclone6kM_pnENode__; +text: .text%__1cKCodeBuffer2t6MiiiiiipnKBufferBlob_pnJrelocInfo_pnORelocateBuffer_ipnLOopRecorder_pkcii_v_; +text: .text%__1cIGraphKitJpush_node6MnJBasicType_pnENode__v_: callGenerator.o; +text: .text%jni_SetObjectArrayElement: jni.o; +text: .text%__1cIGraphKitbBset_arguments_for_java_call6MpnMCallJavaNode__v_; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cSFixupMirrorClosureJdo_object6MpnHoopDesc__v_: universe.o; +text: .text%__1cNCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cRbranchLoopEndNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarI_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKoopFactoryYnew_permanent_shortArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: bytecode.o; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: frame.o; +text: .text%__1cPSignatureStreamRas_symbol_or_null6M_pnNsymbolOopDesc__; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__: classes.o; +text: .text%__1cHnmethodOis_java_method6kM_i_: nmethod.o; +text: .text%__1cNnmethodLocker2t6MpnHnmethod__v_; +text: .text%__1cOClearArrayNodeKmatch_edge6kMI_I_; +text: .text%__1cSbranchCon_longNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_ltNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cbACallCompiledJavaDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cLstoreP0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cPcmpFastLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitbMset_predefined_output_for_runtime_call6MpnENode_pnMMergeMemNode__v_; +text: .text%__1cIGraphKitGmemory6MI_pnENode__; +text: .text%__1cZresource_reallocate_bytes6FpcLL_0_; +text: .text%__1cSbranchCon_longNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNprefetch2NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTDirectCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cNCallGeneratorPfor_direct_call6FpnIciMethod__p0_; +text: .text%__1cMWarmCallInfoLalways_cold6F_p0_; +text: .text%__1cRCardTableModRefBSPdirty_MemRegion6MnJMemRegion__v_; +text: .text%__1cIciMethodXfind_monomorphic_target6MpnHciKlass_22_p0_; +text: .text%__1cDCHAManalyze_call6FnLKlassHandle_11nMsymbolHandle_2_pnJCHAResult__; +text: .text%__1cJCHAResultOis_monomorphic6kM_i_; +text: .text%__1cXAdaptiveWeightedAverageGsample6Mf_v_; +text: .text%__1cTStackWalkCompPolicyYmethod_back_branch_event6MnMmethodHandle_iipnGThread__v_; +text: .text%__1cIAddLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLLShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRCompilationPolicybJreset_counter_for_back_branch_event6MnMmethodHandle__v_; +text: .text%__1cTmembar_CPUOrderNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQciTypeArrayKlassTis_type_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cMPhaseChaitinQgather_lrg_masks6Mi_v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: live.o; +text: .text%__1cJPhaseLiveHcompute6MI_v_; +text: .text%__1cIPhaseIFGEinit6MI_v_; +text: .text%__1cKStoreBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSandI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNIdealLoopTreeObeautify_loops6MpnOPhaseIdealLoop__i_; +text: .text%__1cLRShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLConvI2LNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: symbolKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: symbolKlass.o; +text: .text%__1cUcompU_iReg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cITypeNodeHis_Type6M_p0_: classes.o; +text: .text%__1cMLinkResolverbCresolve_virtual_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cJimmU6OperIconstant6kM_l_: ad_sparc_clone.o; +text: .text%__1cJcmpOpOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cKstorePNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMLinkResolverbBresolve_static_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cQmulL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitMreset_memory6M_pnENode__; +text: .text%__1cQPSGenerationPoolImax_size6kM_L_: memoryPool.o; +text: .text%__1cQPSGenerationPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cQPSGenerationPoolNused_in_bytes6M_L_: memoryPool.o; +text: .text%__1cPThreadLocalNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQaddL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: machnode.o; +text: .text%__1cSReferenceProcessorSdiscover_reference6MpnHoopDesc_nNReferenceType__i_; +text: .text%__1cKBufferBlobGcreate6Fpkci_p0_; +text: .text%__1cICodeBlob2t6Mpkcii_v_; +text: .text%__1cKstorePNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTCallInterpreterNodeGOpcode6kM_i_; +text: .text%JVM_GetCPClassNameUTF; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: loopnode.o; +text: .text%__1cOGenerateOopMapLbb_mark_fct6Fp0ipi_v_; +text: .text%__1cKimmL13OperJconstantL6kM_x_: ad_sparc_clone.o; +text: .text%__1cKJNIHandlesLmake_global6FnGHandle_i_pnI_jobject__; +text: .text%__1cSReferenceProcessorTget_discovered_list6MnNReferenceType__ppnHoopDesc__; +text: .text%__1cXjava_lang_ref_ReferenceIset_next6FpnHoopDesc_2_v_; +text: .text%__1cIimmDOperJconstantD6kM_d_: ad_sparc_clone.o; +text: .text%__1cbCcatch_cleanup_fix_all_inputs6FpnENode_11_v_: lcm.o; +text: .text%__1cQaddL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAddLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHMatcherMreturn_value6Fii_nLRegPair__; +text: .text%jni_GetStringLength: jni.o; +text: .text%__1cJCHAResultSmonomorphic_target6kM_nMmethodHandle__; +text: .text%__1cFTypeDEmake6Fd_pk0_; +text: .text%__1cIHaltNodeEhash6kM_I_: classes.o; +text: .text%__1cLCastP2LNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKReturnNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOGenerateOopMapRsigchar_to_effect6McipnNCellTypeState__2_; +text: .text%__1cMoutputStream2t6Mi_v_; +text: .text%__1cOGenerateOopMapIdo_field6Miiii_v_; +text: .text%__1cMstringStream2t6ML_v_; +text: .text%__1cMstringStreamJas_string6M_pc_; +text: .text%__1cMstringStream2T6M_v_; +text: .text%__1cMURShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cIModINodeGOpcode6kM_i_; +text: .text%__1cMloadConDNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cZnoG3_iRegI_64bit_safeOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cENodeMsetup_is_top6M_v_; +text: .text%__1cIGotoNodeGOpcode6kM_i_; +text: .text%__1cSInterpreterRuntimeE_new6FpnKJavaThread_pnTconstantPoolOopDesc_i_v_; +text: .text%__1cPfieldDescriptorRint_initial_value6kM_i_; +text: .text%__1cKstoreCNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMURShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIMachOperEtype6kM_pknEType__; +text: .text%__1cKPerfStringKset_string6Mpkc_v_; +text: .text%__1cMURShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cEDict2t6MpFpkv2_ipF2_i_v_; +text: .text%__1cEDict2T6M_v_; +text: .text%__1cSCompareAndSwapNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cbDcatch_cleanup_find_cloned_def6FpnFBlock_pnENode_1rnLBlock_Array_i_3_: lcm.o; +text: .text%__1cHRetNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHRetNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: loopnode.o; +text: .text%__1cPconvI2L_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJimmU5OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cKimmI13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: graphKit.o; +text: .text%__1cObranchConUNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cKcmpOpUOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cIGraphKitTuse_exception_state6MpnNSafePointNode__pnENode__; +text: .text%__1cKBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cRClassPathZipEntryLopen_stream6Mpkc_pnPClassFileStream__; +text: .text%__1cKcmpOpPOperFequal6kM_i_: ad_sparc_clone.o; +text: .text%__1cHMemNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cOClearArrayNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cTconstantPoolOopDescbCverify_constant_pool_resolve6FnSconstantPoolHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cNMachIdealNodeJnum_opnds6kM_I_: machnode.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: machnode.o; +text: .text%__1cRMachNullCheckNodeLout_RegMask6kM_rknHRegMask__: machnode.o; +text: .text%__1cRMachNullCheckNode2t6MpnENode_2I_v_; +text: .text%__1cSandI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKReturnNodeEhash6kM_I_: classes.o; +text: .text%__1cWImplicitExceptionTableGappend6MII_v_; +text: .text%__1cIMachNodeSalignment_required6kM_i_: machnode.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: machnode.o; +text: .text%__1cRMachNullCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: machnode.o; +text: .text%__1cQComputeCallStackHdo_void6M_v_: generateOopMap.o; +text: .text%__1cSTailCalljmpIndNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cPconvL2I_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Sub6M_pnHSubNode__: classes.o; +text: .text%__1cNloadConL0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopPis_counted_loop6MpnENode_pnNIdealLoopTree__2_; +text: .text%__1cNobjArrayKlassPoop_is_objArray6kM_i_: objArrayKlass.o; +text: .text%__1cIMaxINodeGOpcode6kM_i_; +text: .text%__1cQStackFrameStream2t6MpnKJavaThread_i_v_; +text: .text%__1cFStateN_sub_Op_LoadI6MpknENode__v_; +text: .text%__1cQandL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUGenericGrowableArray2t6MiipnEGrET_i_v_; +text: .text%__1cNprefetch2NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUGenericGrowableArrayEgrow6Mi_v_; +text: .text%__1cQaddP_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRcompL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNbranchConNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cLklassVtableRinitialize_vtable6MpnGThread__v_; +text: .text%__1cSaddL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQandI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassQup_cast_abstract6M_p0_; +text: .text%__1cIGraphKitOmake_slow_call6MpknITypeFunc_pCpkcpnENode_88_8_; +text: .text%__1cMLinkResolverbCresolve_special_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cPjava_lang_ClassNcreate_mirror6FnLKlassHandle_pnGThread__pnHoopDesc__; +text: .text%__1cLBuildCutout2T6M_v_; +text: .text%__1cFParseNpush_constant6MnKciConstant__i_; +text: .text%__1cUcompU_iReg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKciTypeFlow2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cKciTypeFlowPget_start_state6M_pkn0ALStateVector__; +text: .text%__1cKciTypeFlowXmark_known_range_starts6M_v_; +text: .text%__1cKciTypeFlowKflow_types6M_v_; +text: .text%__1cKciTypeFlowHdo_flow6M_v_; +text: .text%__1cKciTypeFlowLfind_ranges6M_v_; +text: .text%__1cKciTypeFlowKmap_blocks6M_v_; +text: .text%__1cJloadLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserXverify_legal_class_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cIAndINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKTypeRawPtrFempty6kM_i_; +text: .text%__1cJloadCNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cVshrL_reg_imm6_L2INodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIGraphKitPpush_pair_local6Mi_v_: parse2.o; +text: .text%__1cJScopeDescGis_top6kM_i_; +text: .text%__1cNmethodOopDescbEfast_exception_handler_bci_for6MnLKlassHandle_ipnGThread__i_; +text: .text%__1cSInterpreterRuntimebFexception_handler_for_exception6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cMURShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cUcompU_iReg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapTmerge_state_vectors6MpnNCellTypeState_2_i_; +text: .text%__1cMciMethodDataJload_data6M_v_; +text: .text%__1cIciMethodJload_code6M_v_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: klass.o; +text: .text%__1cXmembar_acquire_lockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKKlass_vtbl2n6FLrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cFKlassVbase_create_klass_oop6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__pnMklassOopDesc__; +text: .text%__1cIAndINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cRcompL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: klass.o; +text: .text%__1cFKlassRinitialize_supers6MpnMklassOopDesc_pnGThread__v_; +text: .text%__1cHUNICODELutf8_length6FpHi_i_; +text: .text%jni_GetStringUTFLength: jni.o; +text: .text%jni_GetStringUTFRegion: jni.o; +text: .text%__1cFKlassRbase_create_klass6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__1_; +text: .text%__1cQjava_lang_StringLutf8_length6FpnHoopDesc__i_; +text: .text%__1cENodeHis_Copy6kM_I_: ad_sparc.o; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc_ii_pc_; +text: .text%__1cLRuntimeStubIis_alive6kM_i_: codeBlob.o; +text: .text%__1cKTypeRawPtrEmake6FpC_pk0_; +text: .text%__1cQPlaceholderTableMremove_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cQPlaceholderTableJadd_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cKstoreBNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKcmpOpPOperJnot_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cPorI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMTypeKlassPtrFxdual6kM_pknEType__; +text: .text%__1cQSystemDictionaryTload_instance_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cUThreadSafepointStateYcaller_must_gc_arguments6kM_i_; +text: .text%__1cbCCompiledCodeSafepointHandlerYcaller_must_gc_arguments6kM_i_: safepoint.o; +text: .text%__1cNSafepointBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_; +text: .text%__1cTDebugInfoReadStream2t6MpknHnmethod_i_v_; +text: .text%__1cPconvL2I_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJTimeStampGupdate6M_v_; +text: .text%__1cLStringTableGintern6FpnHoopDesc_pnGThread__2_; +text: .text%__1cQjava_lang_StringRas_unicode_string6FpnHoopDesc_ri_pH_; +text: .text%JVM_InternString; +text: .text%__1cOLibraryCallKitOgenerate_guard6MpnENode_pnKRegionNode_f_v_; +text: .text%__1cRsarI_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosGrandom6F_l_; +text: .text%__1cNIdealLoopTreeTcheck_inner_safepts6MpnOPhaseIdealLoop__v_; +text: .text%__1cNIdealLoopTreeMcounted_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cJCodeCacheEfree6FpnICodeBlob__v_; +text: .text%__1cICodeHeapKdeallocate6Mpv_v_; +text: .text%__1cKoopFactoryXnew_permanent_byteArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cLOopRecorderKfind_index6MpnI_jobject__i_; +text: .text%__1cHCmpNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cHciKlassMis_interface6M_i_: ciObjArrayKlass.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodDataKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: methodDataKlass.o; +text: .text%__1cMciMethodData2t6MnQmethodDataHandle__v_; +text: .text%__1cPmethodDataKlassRoop_is_methodData6kM_i_: methodDataKlass.o; +text: .text%__1cKBufferBlobEfree6Fp0_v_; +text: .text%jni_SetIntField: jni.o; +text: .text%__1cQmulL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadPNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciObjectMis_obj_array6M_i_: ciInstanceKlass.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: memnode.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: memnode.o; +text: .text%__1cNloadConP0NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cJimmP0OperEtype6kM_pknEType__: ad_sparc_clone.o; +text: .text%__1cIRootNodeHis_Root6M_p0_: classes.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: multnode.o; +text: .text%__1cIregDOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cXmembar_acquire_lockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_: classes.o; +text: .text%__1cTDebugInfoReadStreamLread_handle6M_nGHandle__; +text: .text%__1cQSystemDictionaryRcheck_constraints6FiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cVLoaderConstraintTablePcheck_or_update6MnTinstanceKlassHandle_nGHandle_nMsymbolHandle__pkc_; +text: .text%__1cKDictionaryJadd_klass6MnMsymbolHandle_nGHandle_nLKlassHandle__v_; +text: .text%__1cRcompL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQxorI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZCallDynamicJavaDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRScavengeRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cKimmP13OperIconstant6kM_l_: ad_sparc_clone.o; +text: .text%__1cVcompP_iRegP_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRScavengeRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: reg_split.o; +text: .text%__1cFKlassXsearch_secondary_supers6kMpnMklassOopDesc__i_; +text: .text%__1cPciObjArrayKlassNelement_klass6M_pnHciKlass__; +text: .text%__1cJloadSNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPciObjectFactoryMvm_symbol_at6Fi_pnIciSymbol__; +text: .text%__1cFframeNoops_entry_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cPJavaCallWrapperHoops_do6MpnKOopClosure__v_; +text: .text%__1cYinternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cMtlsLoadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMapLmerge_state6Fp0ipi_v_; +text: .text%__1cKcmpOpUOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cHPhiNodeDcmp6kMrknENode__I_; +text: .text%__1cLstoreI0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcompiledVFrameGis_top6kM_i_; +text: .text%__1cIPhaseIFGISquareUp6M_v_; +text: .text%__1cPCheckCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHTypePtrCeq6kMpknEType__i_; +text: .text%__1cSmembar_acquireNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmodI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQcmovI_reg_ltNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLklassItableRinitialize_itable6M_v_; +text: .text%__1cLstoreI0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLklassVtableMget_mirandas6FpnNGrowableArray4CpnNmethodOopDesc___pnMklassOopDesc_pnPobjArrayOopDesc_8_v_; +text: .text%__1cQciByteCodeStreamJget_klass6Mri_pnHciKlass__; +text: .text%__1cSCompareAndSwapNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cIGraphKitOhas_ex_handler6M_i_; +text: .text%__1cLCastP2LNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_: ciMethodData.o; +text: .text%__1cENodeGis_Con6kM_I_: connode.o; +text: .text%__1cPCheckCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFKlassMoop_is_klass6kM_i_: instanceKlass.o; +text: .text%__1cSaddL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRegionNodeOis_block_start6kM_i_: loopnode.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse2.o; +text: .text%__1cNinstanceKlassVadd_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cFKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: connode.o; +text: .text%__1cFParseQarray_addressing6MnJBasicType_ippknEType__pnENode__; +text: .text%__1cRshrL_reg_imm6NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassWappend_to_sibling_list6M_v_; +text: .text%__1cFKlassMset_subklass6MpnMklassOopDesc__v_; +text: .text%__1cQSystemDictionarySjava_system_loader6F_pnHoopDesc__; +text: .text%__1cSandI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOopRecorder2t6MpnFArena__v_; +text: .text%__1cKklassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cOGenerateOopMapQnext_bb_start_pc6MpnKBasicBlock__i_; +text: .text%__1cOGenerateOopMapJinterp_bb6MpnKBasicBlock__v_; +text: .text%__1cKoopFactoryRnew_instanceKlass6FiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cIVerifierQrelax_verify_for6FpnHoopDesc__i_; +text: .text%__1cPClassFileParserbBcheck_final_method_override6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbDcompute_transitive_interfaces6MnTinstanceKlassHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cPClassFileParserVset_precomputed_flags6MnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserNfill_oop_maps6MnTinstanceKlassHandle_ii_v_; +text: .text%__1cPClassFileParserQparse_interfaces6MnSconstantPoolHandle_nGHandle_2pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserUcompute_oop_map_size6MnTinstanceKlassHandle_ii_i_; +text: .text%__1cPClassFileParserOparseClassFile6MnMsymbolHandle_nGHandle_2r1pnGThread__nTinstanceKlassHandle__; +text: .text%__1cPClassFileParserNparse_methods6MnSconstantPoolHandle_ipnLAccessFlags_ppnPobjArrayOopDesc_66pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserMsort_methods6MnOobjArrayHandle_111pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserbBparse_constant_pool_entries6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserbCcheck_super_interface_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbAparse_classfile_attributes6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cKoopFactoryQnew_constantPool6FipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cPClassFileParserTparse_constant_pool6MpnGThread__nSconstantPoolHandle__; +text: .text%__1cPClassFileParserMparse_fields6MnSconstantPoolHandle_ipnUFieldAllocationCount_pnOobjArrayHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cNmethodOopDescMsort_methods6FpnPobjArrayOopDesc_222_v_; +text: .text%__1cIUniverseTflush_dependents_on6FnTinstanceKlassHandle__v_; +text: .text%__1cJCodeCachebKnumber_of_nmethods_with_dependencies6F_i_; +text: .text%__1cNinstanceKlassWdo_local_static_fields6MpFpnPfieldDescriptor_pnGThread__v4_v_; +text: .text%__1cSinstanceKlassKlassXallocate_instance_klass6MiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cLklassItableZsetup_itable_offset_table6FnTinstanceKlassHandle__v_; +text: .text%__1cVjava_lang_ClassLoaderRis_trusted_loader6FpnHoopDesc__i_; +text: .text%__1cNinstanceKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cLklassVtablebKcompute_vtable_size_and_num_mirandas6Fri1pnMklassOopDesc_pnPobjArrayOopDesc_nLAccessFlags_pnHoopDesc_pnNsymbolOopDesc_5_v_; +text: .text%__1cNinstanceKlassSprocess_interfaces6MpnGThread__v_; +text: .text%__1cTClassLoadingServiceTnotify_class_loaded6FpnNinstanceKlass_i_v_; +text: .text%__1cNinstanceKlassQinit_implementor6M_v_; +text: .text%__1cNinstanceKlassQeager_initialize6MpnGThread__v_; +text: .text%__1cPClassFileStream2t6MpCipc_v_; +text: .text%__1cFKlassKsuperklass6kM_pnNinstanceKlass__; +text: .text%__1cIVerifierRshould_verify_for6FpnHoopDesc__i_; +text: .text%__1cRconstantPoolKlassIallocate6MipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cQSystemDictionaryVdefine_instance_class6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cQSystemDictionaryVresolve_super_or_fail6FnMsymbolHandle_1nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNIdealLoopTreeVadjust_loop_exit_prob6MpnOPhaseIdealLoop__v_; +text: .text%__1cSinstanceKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNinstanceKlassScopy_static_fields6MpnSPSPromotionManager__v_; +text: .text%__1cOPhaseTransform2t6MnFPhaseLPhaseNumber__v_; +text: .text%__1cMPhaseIterGVNIoptimize6M_v_; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cQmodI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadBNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceKlass.o; +text: .text%__1cNloadKlassNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQandL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlass.o; +text: .text%__1cFVTuneOend_class_load6F_v_; +text: .text%__1cJEventMark2t6MpkcE_v_: classLoader.o; +text: .text%__1cQSystemDictionaryRload_shared_class6FnTinstanceKlassHandle_nGHandle_pnGThread__1_; +text: .text%__1cFVTuneQstart_class_load6F_v_; +text: .text%__1cLClassLoaderOload_classfile6FnMsymbolHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cSThreadProfilerMark2T6M_v_; +text: .text%__1cSThreadProfilerMark2t6Mn0AGRegion__v_; +text: .text%__1cKstoreINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPClassFileParserbKparse_classfile_sourcefile_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: loopnode.o; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__: classes.o; +text: .text%__1cUCallCompiledJavaNodeGOpcode6kM_i_; +text: .text%__1cQmodI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadRangeNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHMemNodeHsize_of6kM_I_; +text: .text%__1cNPhaseCoalescePcoalesce_driver6M_v_; +text: .text%__1cMPhaseChaitinSbuild_ifg_physical6MpnMResourceArea__I_; +text: .text%__1cISubINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOMacroAssemblerDset6MlpnMRegisterImpl_rknQRelocationHolder__v_: ad_sparc.o; +text: .text%__1cJloadFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRMachSafePointNodePis_MachCallJava6M_pnQMachCallJavaNode__: ad_sparc_misc.o; +text: .text%__1cLklassVtableYadd_new_mirandas_to_list6FpnNGrowableArray4CpnNmethodOopDesc___pnPobjArrayOopDesc_6pnMklassOopDesc__v_; +text: .text%__1cYconstantPoolCacheOopDescKinitialize6MrnIintArray__v_; +text: .text%__1cNinstanceKlassWadd_loader_constraints6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIRewriterHrewrite6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cJScopeDesc2t6MpknHnmethod_i_v_; +text: .text%JVM_GetMethodIxExceptionIndexes; +text: .text%JVM_GetMethodIxExceptionsCount; +text: .text%__1cIVerifierRverify_byte_codes6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassNrewrite_class6MpnGThread__v_; +text: .text%JVM_GetMethodIxArgsSize; +text: .text%__1cKoopFactoryVnew_constantPoolCache6FipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%JVM_GetMethodIxByteCode; +text: .text%JVM_GetMethodIxByteCodeLength; +text: .text%__1cWconstantPoolCacheKlassIallocate6MipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cHAddNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHnmethodNscope_desc_at6MpCi_pnJScopeDesc__; +text: .text%JVM_GetMethodIxSignatureUTF; +text: .text%__1cOcompiledVFrame2t6MpknFframe_pknLRegisterMap_pnKJavaThread_pnJScopeDesc__v_; +text: .text%JVM_GetMethodIxMaxStack; +text: .text%__1cMnegF_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKoopFactoryWnew_permanent_intArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cPClassFileParserVparse_exception_table6MIInSconstantPoolHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cWCountInterfacesClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cMnegF_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFframeZinterpreter_frame_set_bcx6Ml_v_; +text: .text%__1cPconvL2I_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeGis_Con6kM_I_: ad_sparc.o; +text: .text%__1cMnegF_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSsafePoint_pollNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRMachSafePointNodeLis_MachCall6M_pnMMachCallNode__: ad_sparc_misc.o; +text: .text%__1cMVM_OperationPevaluation_mode6kM_n0AEMode__: vm_operations.o; +text: .text%__1cOcompiledVFrameEcode6kM_pnHnmethod__; +text: .text%__1cMStartOSRNodeGOpcode6kM_i_; +text: .text%__1cSObjectSynchronizerJnotifyall6FnGHandle_pnGThread__v_; +text: .text%__1cSPSKeepAliveClosureGdo_oop6MppnHoopDesc__v_: psScavenge.o; +text: .text%__1cRcompL_reg_conNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAndINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cTGeneratePairingInfoRpossible_gc_point6MpnOBytecodeStream__i_: ciMethod.o; +text: .text%__1cOcompI_iRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTmembar_CPUOrderNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConP0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSaddP_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbAPSEvacuateFollowersClosureHdo_void6M_v_: psScavenge.o; +text: .text%__1cNRelocIteratorTlocs_and_index_size6Fii_i_; +text: .text%__1cLCastP2LNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cWstatic_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cKMemoryPoolHoops_do6MpnKOopClosure__v_; +text: .text%__1cQcmovI_reg_ltNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cYcompareAndSwapL_boolNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNprefetch2NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cLProfileDataSis_VirtualCallData6M_i_: ciMethodData.o; +text: .text%__1cLOpaque1NodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOCompiledRFrameEinit6M_v_; +text: .text%__1cGvframeDtop6kM_p0_; +text: .text%__1cMURShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: compile.o; +text: .text%__1cOPhaseIdealLoopMdominated_by6MpnENode_2_v_; +text: .text%__1cLClassLoaderLadd_package6Fpkci_i_; +text: .text%__1cOGenerateOopMapHset_var6MinNCellTypeState__v_; +text: .text%__1cQandI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLklassItableTcompute_itable_size6FnOobjArrayHandle__i_; +text: .text%__1cJLoadBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIGraphKitRmerge_fast_memory6MpnENode_2i_v_; +text: .text%__1cRshlL_reg_imm6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQmulL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodJcode_size6kM_i_: nmethod.o; +text: .text%__1cFParseKdo_put_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cNSharedRuntimebKexception_handler_for_return_address6FpC_1_; +text: .text%__1cFciEnvUis_unresolved_string6kMpnPciInstanceKlass_i_i_; +text: .text%__1cQciByteCodeStreamUis_unresolved_string6kM_i_; +text: .text%__1cRreturn_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cIGraphKitHopt_iff6MpnENode_2_2_; +text: .text%__1cEDict2t6MpFpkv2_ipF2_ipnFArena_i_v_; +text: .text%__1cJloadCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHMatcherFxform6MpnENode_i_2_; +text: .text%__1cJStartNodeHsize_of6kM_I_; +text: .text%__1cHMatcherLreturn_addr6kM_nHOptoRegEName__; +text: .text%__1cHMatcherLfind_shared6MpnENode__v_; +text: .text%__1cILoopNodeHsize_of6kM_I_: classes.o; +text: .text%__1cTOopMapForCacheEntryZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cJLoadSNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLstoreP0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOloadConL13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: instanceKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlass.o; +text: .text%__1cUcompU_iReg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNinstanceKlassbBcall_class_initializer_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cLOpaque2NodeGOpcode6kM_i_; +text: .text%__1cIProjNodeJideal_reg6kM_I_; +text: .text%__1cICallNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNSharedRuntimebWnative_method_throw_unsatisfied_link_error_entry6F_pC_; +text: .text%__1cHNTarjanICOMPRESS6M_v_; +text: .text%__1cQdivD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAndINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNRelocIteratorEnext6M_i_: nativeInst_sparc.o; +text: .text%__1cbBjava_lang_ref_SoftReferenceFclock6F_x_; +text: .text%__1cIAndINodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cUEdenMutableSpacePoolImax_size6kM_L_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolImax_size6kM_L_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolNused_in_bytes6M_L_: memoryPool.o; +text: .text%__1cYSurvivorMutableSpacePoolNused_in_bytes6M_L_: memoryPool.o; +text: .text%__1cRmethodDataOopDescLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cYSurvivorMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cKReturnNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMethodLivenessSpropagate_liveness6M_v_; +text: .text%__1cOMethodLivenessRinit_basic_blocks6M_v_; +text: .text%__1cOMethodLivenessNinit_gen_kill6M_v_; +text: .text%__1cOMethodLivenessQcompute_liveness6M_v_; +text: .text%__1cOMethodLiveness2t6MpnFArena_pnIciMethod__v_; +text: .text%__1cPcompP_iRegPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRloadConP_pollNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWconstantPoolCacheKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQmodI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cZCallDynamicJavaDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cSaddL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNIdealLoopTreeOpolicy_peeling6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreebBpolicy_do_remove_empty_loop6MpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeUiteration_split_impl6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cVPreserveExceptionMark2T6M_v_; +text: .text%__1cVPreserveExceptionMark2t6MrpnGThread__v_; +text: .text%__1cFParseXcatch_inline_exceptions6MpnNSafePointNode__v_; +text: .text%__1cJLoadFNodeGOpcode6kM_i_; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__: classes.o; +text: .text%__1cYciExceptionHandlerStreamPcount_remaining6M_i_; +text: .text%__1cXJNI_ArgumentPusherVaArgHiterate6ML_v_: jni.o; +text: .text%__1cQaddI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRInterpretedRFrameKtop_vframe6kM_pnKjavaVFrame__: rframe.o; +text: .text%__1cRinterpretedVFrameDbci6kM_i_; +text: .text%__1cILoopNode2t6MpnENode_2_v_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: assembler_sparc.o; +text: .text%__1cOPhaseIdealLoopQset_subtree_ctrl6MpnENode__v_; +text: .text%__1cNobjArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cKCodeBuffer2T6M_v_; +text: .text%__1cRcompL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopnode.o; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: loopnode.o; +text: .text%JVM_GetFieldIxModifiers; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: loopnode.o; +text: .text%__1cMloadConLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLstoreB0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLLShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRmethodDataOopDescKmileage_of6FpnNmethodOopDesc__i_; +text: .text%__1cRmethodDataOopDescJis_mature6kM_i_; +text: .text%__1cIciObjectMhas_encoding6M_i_; +text: .text%__1cQsubL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRethrowNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRconstantPoolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%JVM_IsConstructorIx; +text: .text%__1cTMachCallRuntimeNodePret_addr_offset6M_i_; +text: .text%__1cKCMoveINodeGOpcode6kM_i_; +text: .text%__1cMLinkResolverbHlookup_instance_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cQcmovI_reg_ltNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFKlassTarray_klass_or_null6M_pnMklassOopDesc__; +text: .text%__1cTMachCallRuntimeNodeSis_MachCallRuntime6M_p0_: ad_sparc_misc.o; +text: .text%__1cHCompilebAvarargs_C_out_slots_killed6kM_I_; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: onStackReplacement.o; +text: .text%__1cHMulNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cUBytecode_tableswitchOdest_offset_at6kMi_i_; +text: .text%__1cOGenerateOopMapKcopy_state6MpnNCellTypeState_2_v_; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse3.o; +text: .text%__1cNSignatureInfoHdo_long6M_v_: frame.o; +text: .text%__1cIRootNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cRcompL_reg_conNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQxorI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUjni_invoke_nonstatic6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cPCountedLoopNodeHsize_of6kM_I_: classes.o; +text: .text%__1cSvframeStreamCommonZsecurity_get_caller_frame6Mi_v_; +text: .text%__1cSTailCalljmpIndNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMloadConPNodeFclone6kM_pnENode__; +text: .text%__1cIimmPOperFclone6kM_pnIMachOper__; +text: .text%__1cYDebugInformationRecorderNappend_handle6MpnI_jobject__i_; +text: .text%__1cVConstantOopWriteValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cNinstanceKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cHMatcherUc_calling_convention6FpnLRegPair_I_v_; +text: .text%__1cPCallRuntimeNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cVshrL_reg_imm6_L2INodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJNIHandlesOdestroy_global6FpnI_jobject_i_v_; +text: .text%__1cIAndINodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIConINodeHget_int6kMpi_i_: classes.o; +text: .text%__1cIAndINodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cHnmethodQis_native_method6kM_i_: nmethod.o; +text: .text%__1cKciTypeFlowLStateVectorMdo_putstatic6MpnQciByteCodeStream__v_; +text: .text%__1cJcmpOpOperFequal6kM_i_: ad_sparc_clone.o; +text: .text%__1cKNativeCallXset_destination_mt_safe6MpC_v_; +text: .text%__1cFStateP_sub_Op_LShiftI6MpknENode__v_; +text: .text%__1cFParseFBlockRsuccessor_for_bci6Mi_p1_; +text: .text%__1cMoutputStreamFprint6MpkcE_v_; +text: .text%__1cIAndLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cHCompileQsync_stack_slots6kM_i_; +text: .text%__1cIAndLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cQcmovI_reg_gtNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKo0RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLMachNopNodeMideal_Opcode6kM_i_: ad_sparc.o; +text: .text%__1cLMachNopNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvL2I_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHRetNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPBytecode_invokeFindex6kM_i_; +text: .text%__1cNSharedRuntimeXfind_callee_info_helper6FpnKJavaThread_rnMvframeStream_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cNmethodOopDescbGupdate_compiled_code_entry_point6Mi_v_; +text: .text%__1cNmethodOopDescTverified_code_entry6M_pC_; +text: .text%__1cRloadConP_pollNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSignatureInfoHdo_long6M_v_: bytecode.o; +text: .text%__1cRshlI_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPCountedLoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHMatcherQpost_fast_unlock6FpknENode__i_; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstance.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: objArrayKlass.o; +text: .text%__1cRMachSafePointNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowLStateVectorGdo_ldc6MpnQciByteCodeStream__v_; +text: .text%__1cENodeGis_Con6kM_I_: memnode.o; +text: .text%__1cQsubL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIXorINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPClassFileStreamGget_u86MpnGThread__X_; +text: .text%__1cMFastLockNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cHAddNodeGis_Add6kM_pk0_: classes.o; +text: .text%__1cPindOffset13OperFclone6kM_pnIMachOper__; +text: .text%__1cKg1RegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNSCMemProjNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cLConvL2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRshrL_reg_imm6NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNmethodOopDescVset_signature_handler6MpC_v_; +text: .text%__1cKPSYoungGenNused_in_bytes6kM_L_; +text: .text%__1cOPhaseIdealLoopRsplit_thru_region6MpnENode_2_2_; +text: .text%__1cFParseNthrow_to_exit6MpnNSafePointNode__v_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJtimestamp6FpnHoopDesc__x_; +text: .text%__1cQLRUMaxHeapPolicyWshould_clear_reference6MpnHoopDesc__i_; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__: ad_sparc_misc.o; +text: .text%__1cRshrL_reg_imm6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKNode_Array2t6MpnFArena__v_: output.o; +text: .text%__1cSxorI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%jni_ExceptionCheck: jni.o; +text: .text%__1cSReferenceProcessorbAprocess_discovered_reflist6MppnHoopDesc_pnPReferencePolicy_i_v_; +text: .text%__1cSReferenceProcessorbAenqueue_discovered_reflist6MpnHoopDesc_p2_v_; +text: .text%__1cHPhiNodeKmake_blank6FpnENode_2_p0_; +text: .text%__1cXmembar_release_lockNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPciInstanceKlassTis_java_lang_Object6M_i_; +text: .text%__1cNflagsRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cSmembar_releaseNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRcompL_reg_conNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%JVM_Clone; +text: .text%__1cNloadKlassNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateR_sub_Op_LoadKlass6MpknENode__v_; +text: .text%__1cQandI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJVectorSetGslamin6Mrk0_v_; +text: .text%__1cUcompU_iReg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cURethrowExceptionNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cYinlineCallClearArrayNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAddLNodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cHTypePtrFxmeet6kMpknEType__3_; +text: .text%__1cSMemBarVolatileNodeGOpcode6kM_i_; +text: .text%__1cIJumpDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cNloadConP0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJcmpOpOperEless6kM_i_: ad_sparc_clone.o; +text: .text%__1cWCallLeafNoFPDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMindirectOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindirectOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindirectOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindIndexOperOindex_position6kM_i_: ad_sparc.o; +text: .text%__1cMindIndexOperFscale6kM_i_: ad_sparc.o; +text: .text%__1cMindIndexOperNbase_position6kM_i_: ad_sparc.o; +text: .text%__1cMindIndexOperNconstant_disp6kM_i_: ad_sparc.o; +text: .text%__1cRAbstractAssemblerFflush6M_v_; +text: .text%__1cOstackSlotLOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cXmembar_release_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cKType_ArrayEgrow6MI_v_; +text: .text%__1cSmembar_releaseNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMTailCallNodeKmatch_edge6kMI_I_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: thread.o; +text: .text%__1cOMachEpilogNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMachEpilogNodeNis_MachEpilog6M_p0_: ad_sparc.o; +text: .text%__1cJLoadLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJloadCNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLOptoRuntimeJstub_name6FpC_pkc_; +text: .text%__1cOloadConL13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cXJNI_ArgumentPusherVaArgIget_long6M_v_: jni.o; +text: .text%__1cHOrINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cVshrL_reg_imm6_L2INodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHGCCauseJto_string6Fn0AFCause__pkc_; +text: .text%__1cKstoreLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKCompiledIC2t6MpnKNativeCall__v_; +text: .text%__1cIPhaseIFGYCompute_Effective_Degree6M_v_; +text: .text%__1cMPhaseChaitinISimplify6M_v_; +text: .text%__1cOloadConL13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRshrL_reg_imm6NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cICmpLNodeDsub6kMpknEType_3_3_; +text: .text%jni_NewObject: jni.o; +text: .text%__1cOPhaseIdealLoopNreorg_offsets6MpnNIdealLoopTree__v_; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopopts.o; +text: .text%__1cSCallLeafDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKTypeRawPtrEmake6FnHTypePtrDPTR__pk0_; +text: .text%__1cJLoadCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNSharedRuntimeOresolve_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cNSharedRuntimeSresolve_sub_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cNobjArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cFStateQ_sub_Op_URShiftI6MpknENode__v_; +text: .text%__1cLCounterDataOis_CounterData6M_i_: methodDataOop.o; +text: .text%__1cZCallDynamicJavaDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHget_int6kMpi_i_; +text: .text%__1cKTypeAryPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cHMatcherNfind_receiver6Fi_nFVMRegEName__; +text: .text%__1cQaddL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLRShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIConFNodeGOpcode6kM_i_; +text: .text%__1cKstoreCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cENodeUdepends_only_on_test6kM_i_: connode.o; +text: .text%__1cOGenerateOopMapGdo_ldc6Mii_v_; +text: .text%__1cFframeRretrieve_receiver6MpnLRegisterMap__pnHoopDesc__; +text: .text%__1cPBytecode_invokeNstatic_target6MpnGThread__nMmethodHandle__; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cJNode_ListEyank6MpnENode__v_; +text: .text%__1cNSafePointNodeLpop_monitor6M_v_; +text: .text%__1cIGraphKitNshared_unlock6MpnENode_2_v_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_exit_Type6F_pknITypeFunc__; +text: .text%__1cRshrI_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: connode.o; +text: .text%__1cKciTypeFlowPflow_exceptions6MpnNGrowableArray4Cpn0AFBlock___pnNGrowableArray4CpnPciInstanceKlass___pn0ALStateVector__v_; +text: .text%__1cENodeHis_Call6M_pnICallNode__: loopnode.o; +text: .text%__1cKstoreINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCallLeafDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNSafePointNodeMpush_monitor6MpknMFastLockNode__v_; +text: .text%__1cMPhaseChaitinVfind_base_for_derived6MppnENode_2rI_2_; +text: .text%__1cJLoadSNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQinstanceRefKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKciTypeFlowLStateVectorOmeet_exception6MpnPciInstanceKlass_pk1_i_; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc__pc_; +text: .text%__1cSCallLeafDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCallLeafDirectNodeKmethod_set6Ml_v_; +text: .text%__1cSCallLeafDirectNodeRis_safepoint_node6kM_i_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopPbuild_loop_tree6M_v_; +text: .text%__1cXmembar_release_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOPhaseIdealLoop2t6MrnMPhaseIterGVN_pk0i_v_; +text: .text%__1cKtype2basic6FpknEType__nJBasicType__; +text: .text%__1cFStateM_sub_Op_SubI6MpknENode__v_; +text: .text%__1cFStateT_sub_Op_CheckCastPP6MpknENode__v_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: frame.o; +text: .text%__1cRshrL_reg_imm6NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeBlobRis_at_poll_return6MpC_i_; +text: .text%__1cIciObjectIis_klass6M_i_: ciInstance.o; +text: .text%__1cPcheckCastPPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: interp_masm_sparc.o; +text: .text%__1cFframeRis_compiled_frame6kMpi_i_; +text: .text%__1cNIdealLoopTreeIset_nest6MI_i_; +text: .text%__1cKCompiledICWis_in_transition_state6kM_i_; +text: .text%__1cKstoreLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: classes.o; +text: .text%__1cNMemoryManagerHoops_do6MpnKOopClosure__v_; +text: .text%__1cIAndINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRcompL_reg_conNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadPNodeFclone6kM_pnENode__; +text: .text%__1cOloadConL13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRcmpFastUnlockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKstoreFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassDLCA6Mp0_1_; +text: .text%__1cHciKlassVleast_common_ancestor6Mp0_1_; +text: .text%__1cGOopMapPset_derived_oop6MnHOptoRegEName_ii2_v_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlass.o; +text: .text%__1cIRootNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cMLinkResolverUresolve_special_call6FrnICallInfo_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cIRootNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOPhaseIdealLoopKDominators6M_v_; +text: .text%__1cOPhaseIdealLoopQbuild_loop_early6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_late6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cIMulLNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cMindirectOperNbase_position6kM_i_: ad_sparc.o; +text: .text%__1cMindirectOperNconstant_disp6kM_i_: ad_sparc.o; +text: .text%__1cFBytesNget_native_u46FpC_I_: bytecodes.o; +text: .text%__1cMindirectOperFscale6kM_i_: ad_sparc.o; +text: .text%__1cZPhaseConservativeCoalesce2t6MrnMPhaseChaitin__v_; +text: .text%__1cYcompareAndSwapL_boolNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZPhaseConservativeCoalesceGverify6M_v_; +text: .text%__1cMPhaseChaitinZcompress_uf_map_for_nodes6M_v_; +text: .text%__1cMPhaseChaitinHcompact6M_v_; +text: .text%__1cMPhaseChaitinFSplit6MI_I_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: connode.o; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interp_masm_sparc.o; +text: .text%__1cNSCMemProjNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKStoreFNodeGOpcode6kM_i_; +text: .text%__1cIimmPOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cHMatcherPprior_fast_lock6FpknENode__i_; +text: .text%__1cRcmpFastUnlockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXmembar_release_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerWcompiler_unlock_object6MpnMRegisterImpl_222rnFLabel__v_; +text: .text%__1cQcmovI_reg_gtNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%jni_NewGlobalRef: jni.o; +text: .text%__1cNloadConL0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopOset_early_ctrl6MpnENode__v_; +text: .text%__1cOMachEpilogNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJCMoveNodeLis_cmove_id6FpnOPhaseTransform_pnENode_44pnIBoolNode__4_; +text: .text%__1cLRethrowNodeEhash6kM_I_: classes.o; +text: .text%__1cQsubI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmulI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFParseWensure_phis_everywhere6M_v_; +text: .text%__1cIregDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: cfgnode.o; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: cfgnode.o; +text: .text%__1cMloadConINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKCompiledICIis_clean6kM_i_; +text: .text%__1cHOrINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLklassItablebFinitialize_itable_for_interface6MpnMklassOopDesc_pnRitableMethodEntry__v_; +text: .text%__1cMURShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIDivINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cRbranchLoopEndNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledICZcompute_monomorphic_entry6FnMmethodHandle_nLKlassHandle_iirnOCompiledICInfo_pnGThread__v_; +text: .text%__1cQxorI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMVirtualSpaceNreserved_size6kM_L_; +text: .text%__1cKJavaThreadGactive6F_p0_; +text: .text%JVM_FillInStackTrace; +text: .text%__1cIUniverseWis_out_of_memory_error6FnGHandle__i_; +text: .text%__1cFParseKarray_load6MnJBasicType__v_; +text: .text%__1cKJavaThreadNreguard_stack6MpC_i_; +text: .text%__1cUPSGenerationCountersKupdate_all6M_v_: psGenerationCounters.o; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle_pnGThread__v_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle__v_; +text: .text%__1cFframeZinterpreter_frame_set_bcp6MpC_v_; +text: .text%__1cICodeHeapMmax_capacity6kM_L_; +text: .text%__1cJTimeStampSticks_since_update6kM_x_; +text: .text%__1cPpoll_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cQjava_lang_StringOchar_converter6FnGHandle_HHpnGThread__1_; +text: .text%__1cTciConstantPoolCache2t6MpnFArena_i_v_; +text: .text%__1cVCallRuntimeDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cILoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: node.o; +text: .text%__1cJloadFNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cILoopNodeHsize_of6kM_I_: loopnode.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: memnode.o; +text: .text%__1cFStateQ_sub_Op_URShiftL6MpknENode__v_; +text: .text%__1cNloadConL0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitNstore_barrier6MpnENode_22_v_; +text: .text%__1cOMacroAssemblerEcall6MpCnJrelocInfoJrelocType__v_: assembler_sparc.o; +text: .text%__1cMtlsLoadPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cbBInterpreterCodeletInterfaceRcode_size_to_size6kMi_i_: interpreter.o; +text: .text%__1cbBInterpreterCodeletInterfaceKinitialize6MpnEStub_i_v_: interpreter.o; +text: .text%__1cIAndLNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cIAndLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interpreter_sparc.o; +text: .text%__1cNIdealLoopTreeMis_loop_exit6kMpnENode_pnOPhaseIdealLoop__2_; +text: .text%__1cKCompiledICSset_to_monomorphic6MrknOCompiledICInfo__v_; +text: .text%__1cSbranchCon_longNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constantPoolKlass.o; +text: .text%__1cPlocal_vsnprintf6FpcLpkcpv_i_; +text: .text%__1cVshrL_reg_imm6_L2INodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cURethrowExceptionNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cURethrowExceptionNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescVclear_native_function6M_v_; +text: .text%__1cNDispatchTableJset_entry6MirnKEntryPoint__v_; +text: .text%__1cIAndLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIAndLNodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cIAndLNodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cKloadUBNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cSstring_compareNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cbFunnecessary_membar_volatileNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosXthread_local_storage_at6Fi_pv_; +text: .text%__1cFTypeFEmake6Ff_pk0_; +text: .text%jio_snprintf; +text: .text%__1cOMacroAssemblerKverify_oop6MpnMRegisterImpl_pkc_v_; +text: .text%__1cLOptoRuntimebAresolve_opt_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cSSetupItableClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%jni_NewLocalRef: jni.o; +text: .text%__1cIimmFOperJconstantF6kM_f_: ad_sparc_clone.o; +text: .text%__1cGOopMapQset_callee_saved6MnHOptoRegEName_ii2_v_; +text: .text%__1cFParseNadd_safepoint6M_v_; +text: .text%__1cRcompL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIDivLNodeGOpcode6kM_i_; +text: .text%__1cQregF_to_stkINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRcompL_reg_conNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIMinINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPClassFileParserbNparse_classfile_inner_classes_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__H_; +text: .text%__1cTCallDynamicJavaNodeEhash6kM_I_: callnode.o; +text: .text%__1cKReturnNode2t6MpnENode_2222_v_; +text: .text%__1cKReturnNodeJideal_reg6kM_I_: classes.o; +text: .text%jni_GetObjectArrayElement: jni.o; +text: .text%__1cLPhaseValues2T5B6M_v_; +text: .text%jni_DeleteGlobalRef: jni.o; +text: .text%__1cYcompareAndSwapL_boolNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cVAdaptivePaddedAverageGsample6Mf_v_; +text: .text%__1cMloadConFNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cENodeHrm_prec6MI_v_; +text: .text%__1cMMutableSpaceFclear6M_v_; +text: .text%__1cICodeBlob2t6MpkcpnKCodeBuffer_iiipnJOopMapSet_i_v_; +text: .text%__1cICodeBlobWfix_relocation_at_move6Ml_v_; +text: .text%__1cKCodeBufferPcopy_relocation6MpnICodeBlob__v_; +text: .text%__1cKCodeBufferJcopy_code6MpnICodeBlob__v_; +text: .text%__1cLConvL2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cVPatchingRelocIteratorIpostpass6M_v_; +text: .text%__1cVPatchingRelocIteratorHprepass6M_v_; +text: .text%__1cJOopMapSet2t6M_v_; +text: .text%__1cRAbstractAssemblerOcode_fill_byte6F_i_; +text: .text%__1cNRelocIteratorMcreate_index6FpnKCodeBuffer_pnJrelocInfo_4_4_; +text: .text%__1cIGraphKitOset_pair_local6MipnENode__v_: parse2.o; +text: .text%__1cJCodeCacheXmark_for_deoptimization6FpnMklassOopDesc__i_; +text: .text%__1cLcmpD_ccNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNRelocIterator2t6MpnKCodeBuffer_pC3_v_; +text: .text%__1cMtlsLoadPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLStrCompNodeGOpcode6kM_i_; +text: .text%__1cOCompiledRFrameLis_compiled6kM_i_: rframe.o; +text: .text%__1cOCompiledRFrameKtop_method6kM_nMmethodHandle__: rframe.o; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_: nmethod.o; +text: .text%JVM_GetCPMethodModifiers; +text: .text%__1cOCompilerOraclePshould_break_at6FnMmethodHandle__i_; +text: .text%__1cIConDNodeGOpcode6kM_i_; +text: .text%__1cJOopMapSetHcopy_to6MpC_v_; +text: .text%jni_SetLongField: jni.o; +text: .text%__1cJOopMapSetJheap_size6kM_i_; +text: .text%__1cQjava_lang_ThreadRset_thread_status6FpnHoopDesc_n0AMThreadStatus__v_; +text: .text%__1cIAndLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQregP_to_stkPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIJVMState2t6Mi_v_; +text: .text%__1cNSignatureInfoJdo_double6M_v_: frame.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: callnode.o; +text: .text%__1cLOpaque2NodeEhash6kM_I_; +text: .text%__1cKCodeBufferGresize6Miiii_v_; +text: .text%__1cNinstanceKlassPadd_implementor6MpnMklassOopDesc__v_; +text: .text%__1cLstoreB0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulDNodeGOpcode6kM_i_; +text: .text%__1cYDebugInformationRecorderJdata_size6M_i_; +text: .text%__1cYDebugInformationRecorderIpcs_size6M_i_; +text: .text%__1cHnmethodPscopes_pcs_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodJstub_size6kM_i_: nmethod.o; +text: .text%__1cOPhaseIdealLoopUsplit_if_with_blocks6MrnJVectorSet_rnKNode_Stack__v_; +text: .text%__1cZCallInterpreterDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLOopRecorderIoop_size6M_i_; +text: .text%__1cHnmethodOexception_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodKtotal_size6kM_i_; +text: .text%__1cIConPNodeEmake6FpC_p0_; +text: .text%__1cHMonitorGnotify6M_i_; +text: .text%__1cHnmethodQscopes_data_size6kM_i_: nmethod.o; +text: .text%__1cNObjectMonitorHis_busy6kM_l_; +text: .text%__1cCosRcurrent_thread_id6F_l_; +text: .text%__1cUSafepointSynchronizeFblock6FpnKJavaThread__v_; +text: .text%__1cJttyLockerbCbreak_tty_lock_for_safepoint6Fl_v_; +text: .text%__1cHCompileICode_Gen6M_v_; +text: .text%__1cOCompileWrapper2t6MpnHCompile__v_; +text: .text%__1cHCompileEInit6Mi_v_; +text: .text%__1cUDebugInfoWriteStream2t6MpnYDebugInformationRecorder_i_v_; +text: .text%__1cYDebugInformationRecorder2t6MpnLOopRecorder__v_; +text: .text%__1cETypeKInitialize6FpnHCompile__v_; +text: .text%__1cFDictIFreset6MpknEDict__v_; +text: .text%__1cHMatcher2t6MrnJNode_List__v_; +text: .text%__1cHCompilebBregister_library_intrinsics6M_v_; +text: .text%__1cVExceptionHandlerTable2t6Mi_v_; +text: .text%__1cFArenaNmove_contents6Mp0_1_; +text: .text%__1cHCompileGOutput6M_v_; +text: .text%__1cFArenaRdestruct_contents6M_v_; +text: .text%__1cFArena2t6ML_v_; +text: .text%__1cHCompileLFill_buffer6M_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: phaseX.o; +text: .text%__1cHMatcherFmatch6M_v_; +text: .text%__1cIPhaseCFGYEstimate_Block_Frequency6M_v_; +text: .text%__1cHCompileTset_cached_top_node6MpnENode__v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: block.o; +text: .text%__1cIPhaseCFGOschedule_early6MrnJVectorSet_rnJNode_List_rnLBlock_Array__i_; +text: .text%__1cLdo_liveness6FpnNPhaseRegAlloc_pnIPhaseCFG_pnKBlock_List_ipnFArena_pnEDict__v_: buildOopMap.o; +text: .text%__1cHCompileMBuildOopMaps6M_v_; +text: .text%__1cIPhaseCFGLRemoveEmpty6M_v_; +text: .text%__1cIPhaseCFGQFind_Inner_Loops6M_v_; +text: .text%__1cIPhaseCFGJbuild_cfg6M_I_; +text: .text%__1cIPhaseCFG2t6MpnFArena_pnIRootNode_rnHMatcher__v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: buildOopMap.o; +text: .text%__1cIPhaseCFGKDominators6M_v_; +text: .text%__1cOPhaseTransform2t6Mp0nFPhaseLPhaseNumber__v_; +text: .text%__1cLPhaseValues2t6Mp0_v_; +text: .text%__1cHCompileYinit_scratch_buffer_blob6M_v_; +text: .text%__1cIPhaseCFGVschedule_pinned_nodes6MrnJVectorSet__v_; +text: .text%__1cIPhaseCFGNschedule_late6MrnJVectorSet_rnJNode_List_rnNGrowableArray4CI___v_; +text: .text%__1cIPhaseCFGQGlobalCodeMotion6MrnHMatcher_IrnJNode_List__v_; +text: .text%__1cMPhaseChaitinbApost_allocate_copy_removal6M_v_; +text: .text%__1cJPhaseLive2T6M_v_; +text: .text%__1cMPhaseChaitin2t6MIrnIPhaseCFG_rnHMatcher__v_; +text: .text%__1cIPhaseIFG2t6MpnFArena__v_; +text: .text%__1cHMatcherWis_short_branch_offset6Mi_i_; +text: .text%__1cMPhaseChaitinRbuild_ifg_virtual6M_v_; +text: .text%__1cXPhaseAggressiveCoalesceNinsert_copies6MrnHMatcher__v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: matcher.o; +text: .text%__1cNPhaseRegAllocPalloc_node_regs6Mi_v_; +text: .text%__1cJPhaseLive2t6MrknIPhaseCFG_rnILRG_List_pnFArena__v_; +text: .text%__1cNPhaseRegAlloc2t6MIrnIPhaseCFG_rnHMatcher_pF_v_v_; +text: .text%__1cMPhaseChaitinRRegister_Allocate6M_v_; +text: .text%__1cHMatcherPinit_spill_mask6MpnENode__v_; +text: .text%__1cHMatcherVinit_first_stack_mask6M_v_; +text: .text%__1cMPhaseChaitinMfixup_spills6M_v_; +text: .text%__1cMPhaseChaitinMreset_uf_map6MI_v_; +text: .text%__1cHMatcherTFixup_Save_On_Entry6M_v_; +text: .text%__1cHMatcherZnumber_of_saved_registers6F_i_; +text: .text%__1cMPhaseChaitinbGstretch_base_pointer_live_ranges6MpnMResourceArea__i_; +text: .text%__1cNPhaseRegAllocTpd_preallocate_hook6M_v_; +text: .text%__1cXPhaseAggressiveCoalesceGverify6M_v_: coalesce.o; +text: .text%__1cGBundlePinitialize_nops6FppnIMachNode__v_; +text: .text%__1cWemit_exception_handler6FrnKCodeBuffer__v_; +text: .text%__1cWImplicitExceptionTableIset_size6MI_v_; +text: .text%__1cMPhaseChaitin2T6M_v_; +text: .text%__1cHCompileTFillExceptionTables6MIpI1pnFLabel__v_; +text: .text%__1cNSignatureInfoJdo_double6M_v_: bytecode.o; +text: .text%__1cKCodeBufferOrelocate_stubs6M_v_; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_: callnode.o; +text: .text%__1cOMachPrologNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHCompilePneed_stack_bang6kMi_i_; +text: .text%__1cWpoll_return_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: multnode.o; +text: .text%__1cLConvL2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOcmovII_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: callnode.o; +text: .text%__1cLstoreB0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHRetNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cNSafePointNodeKgrow_stack6MpnIJVMState_I_v_; +text: .text%__1cICodeBlobPallocation_size6FpnKCodeBuffer_ii_I_; +text: .text%__1cHRetNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTresource_free_bytes6FpcL_v_; +text: .text%__1cMMutableSpaceKinitialize6MnJMemRegion_i_v_; +text: .text%__1cJimmL0OperJconstantL6kM_x_: ad_sparc_clone.o; +text: .text%__1cRPrivilegedElementKinitialize6MpnMvframeStream_pnHoopDesc_p0pnGThread__v_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nativeInst_sparc.o; +text: .text%JVM_DoPrivileged; +text: .text%__1cKloadUBNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cGciTypeMis_classless6kM_i_: ciInstanceKlass.o; +text: .text%__1cULinearLeastSquareFitGupdate6Mdd_v_; +text: .text%__1cQcmovI_reg_gtNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIGraphKitOnull_check_oop6MpnKRegionNode_pnENode_i_4_; +text: .text%__1cVCallRuntimeDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQaddL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadCNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQcmovI_reg_gtNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFciEnvKcompile_id6M_I_; +text: .text%__1cVshrL_reg_imm6_L2INodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDset6MlpnMRegisterImpl_rknQRelocationHolder__v_: assembler_sparc.o; +text: .text%__1cSmembar_acquireNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cNprefetch2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJimmU6OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cNmethodOopDescbDbuild_interpreter_method_data6FnMmethodHandle_pnGThread__v_; +text: .text%__1cRshrL_reg_imm6NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: loopnode.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: loopnode.o; +text: .text%__1cSmembar_acquireNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_words6FpnNmethodOopDesc__i_; +text: .text%__1cRmethodDataOopDescKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cKoopFactoryOnew_methodData6FnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cPmethodDataKlassIallocate6MnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cZCallInterpreterDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cObox_handleNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHciKlassGloader6M_pnHoopDesc__: ciTypeArrayKlass.o; +text: .text%__1cNIdealLoopTreePiteration_split6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cSThreadLocalStoragePget_thread_slow6F_pnGThread__; +text: .text%__1cIMulLNodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIMulLNodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cURethrowExceptionNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICmpDNodeGOpcode6kM_i_; +text: .text%__1cKTypeOopPtrSmake_from_constant6FpnIciObject__pk0_; +text: .text%__1cLConvD2INodeGOpcode6kM_i_; +text: .text%__1cTmembar_volatileNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLCastP2LNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIModINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cObox_handleNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMindIndexOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindIndexOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindIndexOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOGenerateOopMapHppstore6MpnNCellTypeState_i_v_; +text: .text%__1cKstoreFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIDivINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRshrP_reg_imm6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitNgen_checkcast6MpnENode_2p2_2_; +text: .text%__1cNloadConL0NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cGThreadOis_interrupted6Fp0i_i_; +text: .text%__1cKklassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cJLoadBNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJArrayDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cSInterpreterRuntimeOprofile_method6FpnKJavaThread_pC_i_; +text: .text%__1cGTarjanICOMPRESS6M_v_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlass.o; +text: .text%__1cLOptoRuntimeRmultianewarray1_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cLRuntimeStubHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cLRuntimeStubbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cNmethodOopDescThas_native_function6kM_i_; +text: .text%__1cOGenerateOopMapJppdupswap6Mipkc_v_; +text: .text%__1cQmerge_point_safe6FpnENode__i_: loopopts.o; +text: .text%__1cOstackSlotIOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cNmethodOopDescWis_vanilla_constructor6kM_i_; +text: .text%__1cFframeLnmethods_do6M_v_; +text: .text%__1cRLowMemoryDetectorWdetect_after_gc_memory6FpnKMemoryPool__v_; +text: .text%__1cJcmpOpOperKless_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cVshrL_reg_imm6_L2INodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQLibraryIntrinsicIgenerate6MpnIJVMState__2_; +text: .text%__1cOLibraryCallKitNtry_to_inline6M_i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: loopnode.o; +text: .text%__1cFStateM_sub_Op_AndI6MpknENode__v_; +text: .text%__1cKstoreBNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConL0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_: library_call.o; +text: .text%__1cIciObjectJis_method6M_i_: ciInstance.o; +text: .text%__1cQsubL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRcompL_reg_conNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIciObjectOis_method_data6M_i_: ciInstance.o; +text: .text%__1cQaddF_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: loopnode.o; +text: .text%__1cJloadFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZnoG3_iRegI_64bit_safeOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cJcmpOpOperHgreater6kM_i_: ad_sparc_clone.o; +text: .text%__1cRsubI_zero_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassNameUTF; +text: .text%__1cIGraphKitRgen_subtype_check6MpnENode_2_2_; +text: .text%__1cKTypeRawPtrFxdual6kM_pknEType__; +text: .text%__1cGIfNodeMdominated_by6MpnENode_pnMPhaseIterGVN__v_; +text: .text%__1cIPhaseCFGOinsert_goto_at6MII_v_; +text: .text%__1cOGenerateOopMapJdo_astore6Mi_v_; +text: .text%__1cQComputeCallStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cQshlI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%JVM_FindLoadedClass; +text: .text%__1cNRelocIteratorEnext6M_i_: compiledIC.o; +text: .text%__1cENodeHis_Copy6kM_I_: node.o; +text: .text%__1cIModLNodeGOpcode6kM_i_; +text: .text%__1cRbranchLoopEndNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLRethrowNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLRShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cObranchConFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIPSOldGenPupdate_counters6M_v_; +text: .text%__1cJCmpL3NodeGOpcode6kM_i_; +text: .text%__1cMloadConINodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cbDReferenceProcessorInitializerIis_clean6kM_v_: concurrentMarkSweepGeneration.o; +text: .text%__1cLOptoRuntimeKjbyte_copy6FpW1L_v_; +text: .text%__1cKManagementJtimestamp6F_x_; +text: .text%__1cPCallRuntimeNodeGOpcode6kM_i_; +text: .text%__1cbFunnecessary_membar_volatileNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKReflectionGinvoke6FnTinstanceKlassHandle_nMmethodHandle_nGHandle_inOobjArrayHandle_nJBasicType_4ipnGThread__pnHoopDesc__; +text: .text%__1cNloadConP0NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectIoverride6FpnHoopDesc__C_; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cITypeLongFwiden6kMpknEType__3_; +text: .text%__1cZCallInterpreterDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: subnode.o; +text: .text%__1cKcmpOpFOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cNflagsRegFOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cPciInstanceKlassbBcompute_shared_has_subklass6M_i_; +text: .text%__1cIGraphKitNallocate_heap6MpnENode_222pknITypeFunc_pC22ipknKTypeOopPtr__2_; +text: .text%__1cOClearArrayNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSbranchCon_longNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cEUTF8Ounicode_length6Fpkc_i_; +text: .text%__1cKstoreCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNflagsRegFOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cOPhaseIdealLoopOplace_near_use6kMpnENode__2_; +text: .text%__1cLBoxLockNodeEhash6kM_I_: classes.o; +text: .text%JVM_FindClassFromClass; +text: .text%__1cJloadLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKstoreCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmulI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQshrL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_ltNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcmpD_ccNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cbACallCompiledJavaDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSignatureInfoIdo_float6M_v_: frame.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlassKlass.o; +text: .text%__1cScheck_phi_clipping6FpnHPhiNode_rpnHConNode_rI45rpnENode_5_i_: cfgnode.o; +text: .text%__1cMjniIdMapBaseHoops_do6MpnKOopClosure__v_; +text: .text%__1cLConvL2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cbDjava_lang_reflect_ConstructorEslot6FpnHoopDesc__i_; +text: .text%__1cbDjava_lang_reflect_ConstructorPparameter_types6FpnHoopDesc__2_; +text: .text%__1cKReflectionSinvoke_constructor6FpnHoopDesc_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cSxorI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cbDjava_lang_reflect_ConstructorFclazz6FpnHoopDesc__2_; +text: .text%JVM_NewInstanceFromConstructor; +text: .text%__1cFParseFBlockMadd_new_path6M_i_; +text: .text%__1cKScopeValueJread_from6FpnTDebugInfoReadStream__p0_; +text: .text%__1cOcompiledVFrameScreate_stack_value6kMpnKScopeValue__pnKStackValue__; +text: .text%signalHandler; +text: .text%JVM_handle_solaris_signal; +text: .text%__1cCosRelapsed_frequency6F_x_; +text: .text%__1cMelapsedTimerHseconds6kM_d_; +text: .text%__1cMVM_OperationSis_cheap_allocated6kM_i_: vm_operations.o; +text: .text%__1cHnmethodUnumber_of_dependents6kM_i_: nmethod.o; +text: .text%__1cJBytecodesRspecial_length_at6FpC_i_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cFStateL_sub_Op_OrI6MpknENode__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constMethodKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constMethodKlass.o; +text: .text%__1cIAddLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cTOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cQcmovI_reg_ltNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSCardTableExtensionbAscavenge_contents_parallel6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager_I_v_; +text: .text%__1cXjava_lang_ref_ReferenceWpending_list_lock_addr6F_ppnHoopDesc__; +text: .text%__1cKcmpOpUOperEless6kM_i_: ad_sparc_clone.o; +text: .text%__1cJStealTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cJStealTask2t6Mi_v_; +text: .text%__1cTOldToYoungRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cNGCTaskManagerMnote_release6MI_v_; +text: .text%__1cJStealTaskEname6M_pc_: psTasks.o; +text: .text%__1cJloadSNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cMPrefetchNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNprefetch2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvF2DNodeGOpcode6kM_i_; +text: .text%__1cMMergeMemNodeIadr_type6kM_pknHTypePtr__: memnode.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: multnode.o; +text: .text%__1cIMulLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cILocation2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cJcmpOpOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cFParseJdo_ifnull6MnIBoolTestEmask__v_; +text: .text%__1cQmodI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIimmIOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cMloadConINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKcmpOpPOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cSaddI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFframeLgc_prologue6M_v_; +text: .text%__1cFframeLgc_epilogue6M_v_; +text: .text%__1cJiRegLOperFclone6kM_pnIMachOper__; +text: .text%__1cOloadConI13NodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cFframeMpd_gc_epilog6M_v_; +text: .text%__1cRframe_gc_epilogue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cRframe_gc_prologue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cKciTypeFlowLStateVectorJdo_aaload6MpnQciByteCodeStream__v_; +text: .text%__1cQjava_lang_StringXcreate_oop_from_unicode6FpHipnGThread__pnHoopDesc__; +text: .text%__1cVCallRuntimeDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%jni_NewString: jni.o; +text: .text%__1cNLocationValueLis_location6kM_i_: debugInfo.o; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnHoopDesc_pnNsymbolOopDesc__i_; +text: .text%__1cJloadLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSTailCalljmpIndNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPfieldDescriptorUstring_initial_value6kMpnGThread__pnHoopDesc__; +text: .text%__1cHMatcherQinline_cache_reg6F_nHOptoRegEName__; +text: .text%__1cNmethodOopDescTset_native_function6MpC_v_; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: loopTransform.o; +text: .text%__1cPorI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitMnext_monitor6M_i_; +text: .text%__1cLOptoRuntimebBcomplete_monitor_enter_Type6F_pknITypeFunc__; +text: .text%__1cLBoxLockNode2t6Mi_v_; +text: .text%__1cIGraphKitLshared_lock6MpnENode__pnMFastLockNode__; +text: .text%__1cRorI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHTypePtrEmake6FnETypeFTYPES_n0ADPTR_i_pk0_; +text: .text%__1cKoopFactoryNnew_charArray6FpkcpnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNSafepointBlobbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cSmulI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmulI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cXmembar_acquire_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSciExceptionHandlerLcatch_klass6M_pnPciInstanceKlass__; +text: .text%__1cQciTypeArrayKlassEmake6FnJBasicType__p0_; +text: .text%__1cLOpaque2NodeLbottom_type6kM_pknEType__: connode.o; +text: .text%__1cOcmovII_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUGenericGrowableArrayNraw_appendAll6Mpk0_v_; +text: .text%__1cNSignatureInfoIdo_short6M_v_: frame.o; +text: .text%__1cFciEnv2t6MpnHJNIEnv__iii_v_; +text: .text%__1cRInlineCacheBufferIis_empty6F_i_; +text: .text%__1cSstring_compareNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosPhint_no_preempt6F_v_; +text: .text%__1cPciObjectFactory2t6MpnFArena_i_v_; +text: .text%lwp_mutex_init: os_solaris.o; +text: .text%__1cUSafepointSynchronizeRis_cleanup_needed6F_i_; +text: .text%__1cFciEnvWget_method_from_handle6MpnI_jobject__pnIciMethod__; +text: .text%__1cNSingletonBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cOcmovPP_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFciEnv2T6M_v_; +text: .text%__1cFframebDsender_for_raw_compiled_frame6kMpnLRegisterMap__0_; +text: .text%__1cbCCompiledCodeSafepointHandlerbDhandle_polling_page_exception6M_pC_; +text: .text%__1cFStateP_sub_Op_RShiftI6MpknENode__v_; +text: .text%__1cRsarI_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUSafepointSynchronizebDhandle_polling_page_exception6FpnKJavaThread__pC_; +text: .text%__1cQjava_lang_ThreadGthread6FpnHoopDesc__pnKJavaThread__; +text: .text%__1cOoop_RelocationIoop_addr6M_ppnHoopDesc__; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: compiledIC.o; +text: .text%__1cOcmovII_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQnotemp_iRegIOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cITemplateIbytecode6kM_nJBytecodesECode__; +text: .text%__1cJTraceTime2t6MpkciipnMoutputStream__v_; +text: .text%__1cVshrL_reg_imm6_L2INodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPcmpFastLockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJimmI0OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cSmembar_releaseNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: ad_sparc.o; +text: .text%__1cMloadConDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQregP_to_stkPNodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMaxINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPcmpFastLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXmembar_acquire_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciMethodRinstructions_size6M_i_; +text: .text%__1cQsubL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerUcompiler_lock_object6MpnMRegisterImpl_222rnFLabel__v_; +text: .text%__1cRsarL_reg_imm6NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cISubLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJloadFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseGdo_new6M_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: loopopts.o; +text: .text%__1cUParallelScavengeHeapMmem_allocate6MLii_pnIHeapWord__; +text: .text%__1cIXorINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cSmembar_acquireNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmembar_acquireNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerDbpr6Mn0AKRCondition_in0AHPredict_pnMRegisterImpl_rnFLabel__v_: assembler_sparc.o; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceRefKlass.o; +text: .text%__1cGEventsDlog6FpkcE_v_: nmethod.o; +text: .text%jni_GetObjectClass: jni.o; +text: .text%__1cTtypeArrayKlassKlassIoop_size6kMpnHoopDesc__i_: typeArrayKlassKlass.o; +text: .text%__1cOPhaseIdealLoopKclone_loop6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cSxorI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKPSYoungGenRcapacity_in_bytes6kM_L_; +text: .text%__1cOMacroAssemblerFalign6Mi_v_; +text: .text%__1cSstring_compareNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPconvL2I_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cbLtransform_int_divide_to_long_multiply6FpnIPhaseGVN_pnENode_i_3_: divnode.o; +text: .text%__1cFVTuneNregister_stub6FpkcpC3_v_; +text: .text%__1cFForteNregister_stub6FpkcpC3_v_; +text: .text%__1cFStateM_sub_Op_AndL6MpknENode__v_; +text: .text%__1cTmembar_volatileNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRappend_interfaces6FnOobjArrayHandle_ripnPobjArrayOopDesc__v_; +text: .text%__1cNinstanceKlassbFlookup_method_in_all_interfaces6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cOMacroAssemblerTset_last_Java_frame6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerVreset_last_Java_frame6M_v_; +text: .text%__1cLPhaseValuesKis_IterGVN6M_pnMPhaseIterGVN__: phaseX.o; +text: .text%__1cMciMethodDataStrap_recompiled_at6MpnLProfileData__i_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: memnode.o; +text: .text%__1cQandL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQstkI_to_regFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLPhaseValues2t6MpnFArena_I_v_; +text: .text%__1cLCodeletMark2t6MrpnZInterpreterMacroAssembler_pkcinJBytecodesECode__v_: interpreter.o; +text: .text%__1cLcmpD_ccNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOPhaseTransform2t6MpnFArena_nFPhaseLPhaseNumber__v_; +text: .text%__1cJStubQdDueueGcommit6Mi_v_; +text: .text%__1cINodeHash2t6MpnFArena_I_v_; +text: .text%__1cFStateP_sub_Op_ConvL2I6MpknENode__v_; +text: .text%__1cJStubQdDueueHrequest6Mi_pnEStub__; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: classes.o; +text: .text%__1cJloadSNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: typeArrayKlass.o; +text: .text%__1cSstring_compareNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: cfgnode.o; +text: .text%__1cQmulI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cITemplateIgenerate6MpnZInterpreterMacroAssembler__v_; +text: .text%__1cPconvF2D_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_and_dispatch6MpnITemplate_nITosState__v_; +text: .text%__1cITemplateKinitialize6MinITosState_1pFi_vi_v_; +text: .text%__1cQjava_lang_ThreadRget_thread_status6FpnHoopDesc__n0AMThreadStatus__; +text: .text%__1cIMulINodeGadd_id6kM_pknEType__: classes.o; +text: .text%JVM_FindClassFromClassLoader; +text: .text%__1cHTypePtrFempty6kM_i_; +text: .text%__1cQaddP_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cbFunnecessary_membar_volatileNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cSsafePoint_pollNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerKsave_frame6Mi_v_; +text: .text%__1cSsafePoint_pollNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerbBcheck_and_forward_exception6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cIModINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cZInterpreterMacroAssemblerZcheck_and_handle_popframe6MpnMRegisterImpl__v_; +text: .text%__1cJcmpOpOperJnot_equal6kM_i_: ad_sparc_clone.o; +text: .text%JVM_IHashCode; +text: .text%__1cKDictionarybAis_valid_protection_domain6MiInMsymbolHandle_nGHandle_2_i_; +text: .text%__1cQmodI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRsubI_zero_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcmovII_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSCompareAndSwapNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cTmembar_CPUOrderNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSCMemProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_: reg_split.o; +text: .text%__1cTmembar_CPUOrderNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOPhaseIdealLoopVclone_up_backedge_goo6MpnENode_22_2_; +text: .text%__1cQComputeCallStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cJloadBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSignatureInfoHdo_char6M_v_: frame.o; +text: .text%__1cNCompileBrokerZcompilation_is_prohibited6FnMmethodHandle_i_i_; +text: .text%__1cbCAbstractInterpreterGeneratorWset_short_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cbCAbstractInterpreterGeneratorQset_entry_points6MnJBytecodesECode__v_; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: loopTransform.o; +text: .text%__1cXmembar_acquire_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cKC2CompilerOcompile_method6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cNCompileBrokerZinvoke_compiler_on_method6FpnLCompileTask__v_; +text: .text%JVM_GetClassLoader; +text: .text%__1cIciMethodJhas_loops6kM_i_; +text: .text%__1cNCompileBrokerQset_last_compile6FpnOCompilerThread_nMmethodHandle_ii_v_; +text: .text%__1cOPhaseIdealLoopLdo_split_if6MpnENode__v_; +text: .text%__1cKScheduling2t6MpnFArena_rnHCompile__v_; +text: .text%__1cMTypeKlassPtrFxmeet6kMpknEType__3_; +text: .text%__1cNSignatureInfoIdo_float6M_v_: bytecode.o; +text: .text%__1cKSchedulingMDoScheduling6M_v_; +text: .text%__1cIciMethodQbreak_at_execute6M_i_; +text: .text%__1cZInterpreterMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cNCompileBrokerYcheck_compilation_result6FnMmethodHandle_iippnHnmethod__i_; +text: .text%__1cFciEnvbOcheck_for_system_dictionary_modification6MpnIciMethod__v_; +text: .text%__1cNCompileBrokerScollect_statistics6FpnOCompilerThread_nMelapsedTimer_pnLCompileTask__v_; +text: .text%__1cLAccessFlagsRatomic_clear_bits6Mi_v_; +text: .text%__1cFciEnvPregister_method6MpnIciMethod_iiiiiipnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler_ii_v_; +text: .text%__1cJStartNodeJideal_reg6kM_I_: callnode.o; +text: .text%__1cQstkI_to_regFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJStartNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cOstackSlotPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cObox_handleNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreP0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQmulD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQmulD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescIset_code6MpnHnmethod__v_; +text: .text%__1cWImplicitExceptionTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cNIdealLoopTreeNpolicy_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cFVTuneOcreate_nmethod6FpnHnmethod__v_; +text: .text%__1cLOopRecorderHcopy_to6MpnICodeBlob__v_; +text: .text%__1cTCallInterpreterNodeSis_CallInterpreter6kM_pk0_: classes.o; +text: .text%__1cVExceptionHandlerTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cNIdealLoopTreeSpolicy_range_check6kMpnOPhaseIdealLoop__i_; +text: .text%__1cYDebugInformationRecorderHcopy_to6MpnHnmethod__v_; +text: .text%__1cQmulL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHnmethodLnew_nmethod6FnMmethodHandle_iiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__p0_; +text: .text%__1cFciEnvVnum_inlined_bytecodes6kM_i_; +text: .text%__1cHnmethod2t6MpnNmethodOopDesc_iiiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__v_; +text: .text%__1cJCodeCacheGcommit6FpnICodeBlob__v_; +text: .text%__1cICodeBlobJcopy_oops6MppnI_jobject_i_v_; +text: .text%__1cXMachCallInterpreterNodePret_addr_offset6M_i_; +text: .text%__1cHnmethodQcopy_scopes_data6MpCi_v_; +text: .text%__1cHnmethodSresolve_JNIHandles6M_v_; +text: .text%__1cUParallelScavengeHeapEused6kM_L_; +text: .text%__1cQcmovI_reg_ltNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableKtransition6FnITosState_1_v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interp_masm_sparc.o; +text: .text%__1cIPhaseCCP2T6M_v_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: assembler_sparc.o; +text: .text%__1cKInlineTreeWbuild_inline_tree_root6F_p0_; +text: .text%__1cYinternal_word_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cIPhaseCCPMdo_transform6M_v_; +text: .text%__1cSPhaseRemoveUseless2t6MpnIPhaseGVN_pnQUnique_Node_List__v_; +text: .text%__1cHCompileLFinish_Warm6M_v_; +text: .text%__1cHCompileNreturn_values6MpnIJVMState__v_; +text: .text%__1cHCompileIOptimize6M_v_; +text: .text%__1cQUnique_Node_ListUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cIPhaseCCPJtransform6MpnENode__2_; +text: .text%__1cIPhaseCCPHanalyze6M_v_; +text: .text%__1cHCompileLInline_Warm6M_i_; +text: .text%__1cMPhaseIterGVN2t6Mp0_v_; +text: .text%__1cIciMethodRbuild_method_data6M_v_; +text: .text%__1cHCompileRbuild_start_state6MpnJStartNode_pknITypeFunc__pnIJVMState__; +text: .text%__1cJStartNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIPhaseCCP2t6MpnMPhaseIterGVN__v_; +text: .text%__1cHCompileVfinal_graph_reshaping6M_i_; +text: .text%__1cTmembar_CPUOrderNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cMPhaseIterGVN2t6MpnIPhaseGVN__v_; +text: .text%lwp_cond_init: os_solaris.o; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod_ii_v_; +text: .text%__1cHCompileUremove_useless_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cbAfinal_graph_reshaping_walk6FrnKNode_Stack_pnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cHCompileVidentify_useful_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cRAbstractAssemblerbDgenerate_stack_overflow_check6Mi_v_; +text: .text%__1cObranchConFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreP0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_epilog6MnITosState_i_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_prolog6MnITosState_i_v_; +text: .text%__1cSmulI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMachEpilogNodeQsafepoint_offset6kM_i_; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: callnode.o; +text: .text%__1cYDebugInformationRecorderKadd_oopmap6MiipnGOopMap__v_; +text: .text%__1cHBitDataKis_BitData6M_i_: ciMethodData.o; +text: .text%__1cSxorI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeDcmp6kMrknENode__I_; +text: .text%__1cKloadUBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSCompiledStaticCallSset_to_interpreted6MnMmethodHandle_pC_v_; +text: .text%__1cNSignatureInfoIdo_short6M_v_: bytecode.o; +text: .text%__1cSCompiledStaticCallJfind_stub6M_pC_; +text: .text%__1cRNativeMovConstRegIset_data6Ml_v_; +text: .text%__1cZInterpreterMacroAssemblerTdispatch_Lbyte_code6MnITosState_ppCii_v_; +text: .text%__1cQregP_to_stkPNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cISubLNodeDsub6kMpknEType_3_3_; +text: .text%__1cJStartNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMLinkResolverXresolve_invokeinterface6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverbGruntime_resolve_interface_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cKBinaryNodeGOpcode6kM_i_; +text: .text%__1cFStateO_sub_Op_Binary6MpknENode__v_; +text: .text%__1cMTailCallNodeGOpcode6kM_i_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_3pnRJavaCallArguments_nGHandle_6_6_; +text: .text%__1cQregF_to_stkINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constMethodKlass.o; +text: .text%__1cZCallDynamicJavaDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: loopnode.o; +text: .text%__1cENodeHis_Copy6kM_I_: loopnode.o; +text: .text%__1cKRegionNodeJideal_reg6kM_I_: loopnode.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: loopnode.o; +text: .text%__1cUGenericGrowableArrayIraw_find6kMpknEGrET__i_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii1i_v_; +text: .text%__1cXSignatureHandlerLibraryDadd6FnMmethodHandle__v_; +text: .text%__1cIAddFNodeGOpcode6kM_i_; +text: .text%__1cSInterpreterRuntimeTprepare_native_call6FpnKJavaThread_pnNmethodOopDesc__v_; +text: .text%__1cLstoreC0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNobjArrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cODeoptimizationVtrap_state_has_reason6Fii_i_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_next6MnITosState_i_v_; +text: .text%__1cLRShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPClassFileParserUverify_constantvalue6MiinSconstantPoolHandle_pnGThread__v_; +text: .text%__1cTmembar_volatileNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFMutex2t6Mipkci_v_; +text: .text%jni_SetByteArrayRegion: jni.o; +text: .text%__1cObox_handleNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParseLarray_store6MnJBasicType__v_; +text: .text%__1cGThreadVset_pending_exception6MpnHoopDesc_pkci_v_; +text: .text%__1cSThreadLocalStorageGthread6F_pnGThread__: assembler_sparc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: exceptions.o; +text: .text%__1cQjava_lang_StringPcreate_from_str6FpkcpnGThread__nGHandle__; +text: .text%__1cKcmpOpUOperHgreater6kM_i_: ad_sparc_clone.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodKlass.o; +text: .text%__1cKPerfMemoryMmark_updated6F_v_; +text: .text%__1cCosLelapsedTime6F_d_; +text: .text%__1cKPerfMemoryFalloc6FL_pc_; +text: .text%__1cIPerfData2t6MnJCounterNS_pkcn0AFUnits_n0ALVariability__v_; +text: .text%__1cSobjArrayKlassKlassbCallocate_objArray_klass_impl6FnYobjArrayKlassKlassHandle_inLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodKlass.o; +text: .text%__1cLStrCompNodeKmatch_edge6kMI_I_; +text: .text%__1cSobjArrayKlassKlassXallocate_objArray_klass6MinLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cKJavaThreadZsecurity_get_caller_class6Mi_pnMklassOopDesc__; +text: .text%jni_GetStringUTFChars: jni.o; +text: .text%__1cFParsebLincrement_and_test_invocation_counter6Mi_v_; +text: .text%jni_ReleaseStringUTFChars; +text: .text%__1cRsubI_zero_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulFNodeGOpcode6kM_i_; +text: .text%__1cSInterpreterRuntimeXthrow_pending_exception6FpnKJavaThread__v_; +text: .text%__1cSInterpreterRuntimeNquicken_io_cc6FpnKJavaThread__v_; +text: .text%__1cNmethodOopDescWcompute_has_loops_flag6M_i_; +text: .text%JVM_IsInterrupted; +text: .text%__1cIModINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNSignatureInfoHdo_char6M_v_: bytecode.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: cpCacheKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: cpCacheKlass.o; +text: .text%JVM_FindLibraryEntry; +text: .text%__1cKloadUBNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLklassItableUcompute_itable_index6FpnNmethodOopDesc__i_; +text: .text%__1cWConstantPoolCacheEntrySset_interface_call6MnMmethodHandle_i_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlass.o; +text: .text%__1cQmodI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodKlass.o; +text: .text%__1cHCompile2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: assembler_sparc.o; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cIMulINodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cQaddF_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLOptoRuntimeOarraycopy_Type6F_pknITypeFunc__; +text: .text%__1cOLibraryCallKitQinline_arraycopy6M_i_; +text: .text%__1cTbasictype2arraycopy6FnJBasicType_i_pC_; +text: .text%__1cJStoreNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cOstackSlotIOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cQregI_to_stkINodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cIXorINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOMacroAssemblerEstop6Mpkc_v_; +text: .text%__1cQshlI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcmpD_ccNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstring_compareNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQshlL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOloadConI13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cObox_handleNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFframeIpatch_pc6MpnGThread_pC_v_; +text: .text%__1cKManagementHoops_do6FpnKOopClosure__v_; +text: .text%__1cKJNIHandlesHoops_do6FpnKOopClosure__v_; +text: .text%__1cSObjectSynchronizerHoops_do6FpnKOopClosure__v_; +text: .text%__1cHMatcherOc_return_value6Fii_nLRegPair__; +text: .text%__1cMFlatProfilerHoops_do6FpnKOopClosure__v_; +text: .text%__1cXJvmtiCurrentBreakpointsHoops_do6FpnKOopClosure__v_; +text: .text%__1cQmulI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryRpreloaded_oops_do6FpnKOopClosure__v_; +text: .text%__1cKJNIHandlesMweak_oops_do6FpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cLJvmtiExportHoops_do6FpnKOopClosure__v_; +text: .text%__1cIVMThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cQPlaceholderTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cNMemoryServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cIUniverseHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cGThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cNThreadServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cVCallRuntimeDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cYinternal_word_RelocationJpack_data6M_i_; +text: .text%__1cYinternal_word_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cYinternal_word_RelocationWfix_relocation_at_move6Ml_v_; +text: .text%__1cODataRelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cPconvF2D_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOstackSlotPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cQshlI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMPhaseChaitinGSelect6M_I_; +text: .text%__1cPconvF2D_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQSystemDictionaryRnumber_of_classes6F_i_; +text: .text%__1cOClearArrayNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLOptoRuntimeSnew_typeArray_Type6F_pknITypeFunc__; +text: .text%__1cXJNI_ArgumentPusherVaArgHget_int6M_v_: jni.o; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectMset_override6FpnHoopDesc_C_v_; +text: .text%__1cIGraphKitJnew_array6MpnENode_nJBasicType_pknEType_pknMTypeKlassPtr__2_; +text: .text%__1cPconvL2I_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOloadConI13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIL_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cbBopt_virtual_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cQshrI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMadjust_check6FpnENode_11iipnMPhaseIterGVN__v_: ifnode.o; +text: .text%__1cSInterpreterRuntimeOmultianewarray6FpnKJavaThread_pi_v_; +text: .text%__1cKRegionNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cQmulD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddF_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKConv2BNodeGOpcode6kM_i_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFi_vi_v_; +text: .text%__1cFParseSjump_switch_ranges6MpnENode_pnLSwitchRange_4i_v_; +text: .text%__1cNloadConP0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: cfgnode.o; +text: .text%__1cFParseLdo_newarray6MnJBasicType__v_; +text: .text%__1cFParseOmerge_new_path6Mi_v_; +text: .text%__1cFParseTprofile_switch_case6Mi_v_; +text: .text%__1cKarrayKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cRbranchLoopEndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRbranchLoopEndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQregI_to_stkINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRbranchLoopEndNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJset_clock6Fx_v_; +text: .text%__1cNCollectedHeapQresize_all_tlabs6M_v_; +text: .text%__1cCosTnative_java_library6F_pv_; +text: .text%__1cSReferenceProcessorQprocess_phaseJNI6M_v_; +text: .text%__1cIimmIOperFclone6kM_pnIMachOper__; +text: .text%__1cCosbCmake_polling_page_unreadable6F_v_; +text: .text%__1cSReferenceProcessorbBenqueue_discovered_reflists6MppnHoopDesc__v_; +text: .text%__1cPGCMemoryManagerIgc_begin6M_v_; +text: .text%__1cPGCMemoryManagerGgc_end6M_v_; +text: .text%__1cUParallelScavengeHeapQresize_all_tlabs6M_v_; +text: .text%__1cUParallelScavengeHeapPupdate_counters6M_v_; +text: .text%__1cUSafepointSynchronizeFbegin6F_v_; +text: .text%__1cUParallelScavengeHeapTensure_parseability6M_v_; +text: .text%__1cMloadConINodeFclone6kM_pnENode__; +text: .text%__1cWThreadLocalAllocBufferbFaccumulate_statistics_before_gc6F_v_; +text: .text%__1cWThreadLocalAllocBufferQresize_all_tlabs6F_v_; +text: .text%__1cUParallelScavengeHeapOfill_all_tlabs6M_v_; +text: .text%__1cORuntimeServicebDrecord_safepoint_synchronized6F_v_; +text: .text%__1cORuntimeServiceWrecord_safepoint_begin6F_v_; +text: .text%__1cUParallelScavengeHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cPciObjArrayKlassGloader6M_pnHoopDesc__: ciObjArrayKlass.o; +text: .text%__1cRInlineCacheBufferUupdate_inline_caches6F_v_; +text: .text%__1cVLoaderConstraintTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cRLowMemoryDetectorRdetect_low_memory6F_v_; +text: .text%__1cSReferenceProcessorbDenqueue_discovered_references6M_i_; +text: .text%__1cIDivINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTDerivedPointerTablePupdate_pointers6F_v_; +text: .text%__1cXjava_lang_ref_ReferenceRpending_list_addr6F_ppnHoopDesc__; +text: .text%__1cONMethodSweeperFsweep6F_v_; +text: .text%__1cKPSYoungGenPupdate_counters6M_v_; +text: .text%__1cMCounterDecayFdecay6F_v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cQSystemDictionaryHoops_do6FpnKOopClosure__v_; +text: .text%__1cTAbstractInterpreterRnotice_safepoints6F_v_; +text: .text%__1cXTraceMemoryManagerStats2T6M_v_; +text: .text%__1cKDictionaryHoops_do6MpnKOopClosure__v_; +text: .text%__1cNCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapOfill_all_tlabs6M_v_; +text: .text%__1cSReferenceProcessorbDprocess_discovered_references6M_v_; +text: .text%__1cNMemoryServiceStrack_memory_usage6F_v_; +text: .text%__1cNloadConPCNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cbAPSGCAdaptivePolicyCountersbBupdate_counters_from_policy6M_v_; +text: .text%__1cQLRUMaxHeapPolicy2t6M_v_; +text: .text%__1cTDerivedPointerTableFclear6F_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersPupdate_counters6M_v_; +text: .text%__1cSsubL_reg_reg_2NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNCollectedHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cXTraceMemoryManagerStats2t6Mi_v_; +text: .text%__1cSObjectSynchronizerVdeflate_idle_monitors6F_v_; +text: .text%__1cUSafepointSynchronizeDend6F_v_; +text: .text%__1cORuntimeServiceUrecord_safepoint_end6F_v_; +text: .text%__1cQmulD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosbAmake_polling_page_readable6F_v_; +text: .text%__1cNloadConL0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTAbstractInterpreterRignore_safepoints6F_v_; +text: .text%__1cMciArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cFParseRarray_store_check6M_v_; +text: .text%__1cNloadConP0NodeFclone6kM_pnENode__; +text: .text%__1cJimmP0OperFclone6kM_pnIMachOper__; +text: .text%__1cCosYprint_jni_name_prefix_on6FpnMoutputStream_i_v_; +text: .text%__1cCosYprint_jni_name_suffix_on6FpnMoutputStream_i_v_; +text: .text%__1cIMinINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMNativeLookupGlookup6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cObranchConFNodeIpipeline6kM_pknIPipeline__; +text: .text%jni_NewByteArray: jni.o; +text: .text%__1cSmembar_releaseNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQdivL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2ipnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cSmembar_releaseNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLcmpD_ccNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQjava_lang_StringFvalue6FpnHoopDesc__pnQtypeArrayOopDesc__; +text: .text%__1cQjava_lang_StringGoffset6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringScreate_from_symbol6FnMsymbolHandle_pnGThread__nGHandle__; +text: .text%__1cLRethrowNode2t6MpnENode_22222_v_; +text: .text%__1cRsubI_zero_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompileSrethrow_exceptions6MpnIJVMState__v_; +text: .text%__1cQandI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSbranchCon_longNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSbranchCon_longNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cSbranchCon_longNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYinlineCallClearArrayNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_53pnGThread__v_; +text: .text%__1cYinlineCallClearArrayNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cURethrowExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionarybAvalidate_protection_domain6FnTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cKDictionaryVadd_protection_domain6MiInTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cNmethodOopDescKklass_name6kM_pnNsymbolOopDesc__; +text: .text%__1cLRethrowNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cObranchConFNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cMNativeLookupLlookup_base6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cKarrayKlassXbase_create_array_klass6FrknKKlass_vtbl_inLKlassHandle_pnGThread__nQarrayKlassHandle__; +text: .text%__1cYinlineCallClearArrayNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKarrayKlassbBcomplete_create_array_klass6FnQarrayKlassHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cURethrowExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cISubLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIPerfLong2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability__v_; +text: .text%__1cQPSIsAliveClosureLdo_object_b6MpnHoopDesc__i_: psScavenge.o; +text: .text%__1cSconvI2F_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cYMachCallCompiledJavaNodePret_addr_offset6M_i_; +text: .text%__1cWCallLeafNoFPDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse1.o; +text: .text%JVM_GetMethodIxExceptionTableEntry; +text: .text%__1cWCallLeafNoFPDirectNodeRis_safepoint_node6kM_i_: ad_sparc_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeKmethod_set6Ml_v_; +text: .text%__1cWCallLeafNoFPDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPciObjArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cSandI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLstoreP0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQmulF_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLMachUEPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRorI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSxorI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIVMThreadHexecute6FpnMVM_Operation__v_; +text: .text%__1cMVM_OperationIevaluate6M_v_; +text: .text%__1cCosTget_native_priority6FkpknGThread_pi_nIOSReturn__; +text: .text%__1cIMinINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cGThreadMget_priority6Fkpk0_nOThreadPriority__; +text: .text%__1cJEventMark2t6MpkcE_v_: vmThread.o; +text: .text%__1cOloadConL13NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cUdivL_reg_imm13_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMVM_OperationSset_calling_thread6MpnGThread_nOThreadPriority__v_; +text: .text%__1cCosMget_priority6FkpknGThread_rnOThreadPriority__nIOSReturn__; +text: .text%__1cSReferenceProcessorZadd_to_discovered_list_mt6MppnHoopDesc_23_v_; +text: .text%__1cMloadConLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerEsetx6MxpnMRegisterImpl_2nJrelocInfoJrelocType__v_; +text: .text%__1cZInterpreterMacroAssemblerZget_2_byte_integer_at_bcp6MipnMRegisterImpl_2n0ALsignedOrNot_n0AKsetCCOrNot__v_; +text: .text%__1cQmulI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPfieldDescriptorSlong_initial_value6kM_x_; +text: .text%__1cGGCTask2t6Mn0AEKindEkind__v_; +text: .text%__1cSconvI2F_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUParallelScavengeHeapQresize_young_gen6MLL_v_; +text: .text%__1cPVM_GC_OperationQgc_count_changed6kM_i_; +text: .text%__1cKPSYoungGenLswap_spaces6M_v_; +text: .text%__1cPVM_GC_OperationNdoit_prologue6M_i_; +text: .text%__1cUPSAdaptiveSizePolicyWminor_collection_begin6M_v_; +text: .text%__1cKPSYoungGenNresize_spaces6MLL_v_; +text: .text%__1cSPSPromotionManagerbBvm_thread_promotion_manager6F_p0_; +text: .text%__1cQinstanceRefKlassbKrelease_and_notify_pending_list_lock6FipnJBasicLock__v_; +text: .text%__1cSPSPromotionManagerMpre_scavenge6F_v_; +text: .text%__1cSPSPromotionManagerNpost_scavenge6F_v_; +text: .text%__1cKPSScavengeQinvoke_no_policy6Fpi_i_; +text: .text%__1cUPSAdaptiveSizePolicyPupdate_averages6MiLL_v_; +text: .text%__1cNJvmtiGCMarker2T6M_v_; +text: .text%__1cUPSAdaptiveSizePolicybPcompute_survivor_space_size_and_threshold6MiiL_i_; +text: .text%__1cNJvmtiGCMarker2t6Mi_v_; +text: .text%__1cSsubL_reg_reg_2NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZSerialOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cZSerialOldToYoungRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cUPSAdaptiveSizePolicyUminor_collection_end6MnHGCCauseFCause__v_; +text: .text%__1cKPSYoungGenGresize6MLL_v_; +text: .text%__1cPVM_GC_OperationNdoit_epilogue6M_v_; +text: .text%__1cKPSYoungGenRresize_generation6MLL_i_; +text: .text%__1cSmulI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQinstanceRefKlassZacquire_pending_list_lock6FpnJBasicLock__v_; +text: .text%__1cSCardTableExtensionRscavenge_contents6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager__v_; +text: .text%__1cHRetNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cUWaitForBarrierGCTaskHdestroy6Fp0_v_; +text: .text%__1cLGCTaskQdDueueGcreate6F_p0_; +text: .text%__1cLGCTaskQdDueueHenqueue6Mp0_v_; +text: .text%__1cUWaitForBarrierGCTaskEname6M_pc_: gcTaskManager.o; +text: .text%__1cNGCTaskManagerIadd_list6MpnLGCTaskQdDueue__v_; +text: .text%__1cObranchConFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cUWaitForBarrierGCTaskGcreate6F_p0_; +text: .text%__1cSaddL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNBarrierGCTaskOdo_it_internal6MpnNGCTaskManager_I_v_; +text: .text%__1cNMonitorSupplyHrelease6FpnHMonitor__v_; +text: .text%__1cNMonitorSupplyHreserve6F_pnHMonitor__; +text: .text%__1cUWaitForBarrierGCTaskIwait_for6M_v_; +text: .text%__1cNGCTaskManagerVrelease_all_resources6M_v_; +text: .text%__1cUWaitForBarrierGCTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cSObjectSynchronizerJfast_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cUWaitForBarrierGCTaskIdestruct6M_v_; +text: .text%__1cHThreadsZcreate_thread_roots_tasks6FpnLGCTaskQdDueue__v_; +text: .text%__1cSObjectSynchronizerKfast_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cQstkI_to_regFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvF2D_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreB0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMloadConDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNobjArrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cUmulL_reg_imm13_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cISubLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: ad_sparc_misc.o; +text: .text%jni_GetStringCritical: jni.o; +text: .text%jni_ReleaseStringCritical: jni.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: objArrayKlass.o; +text: .text%__1cFStateM_sub_Op_XorI6MpknENode__v_; +text: .text%__1cNobjArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlass.o; +text: .text%__1cNobjArrayKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cLOptoRuntimeInew_Type6F_pknITypeFunc__; +text: .text%__1cIDivINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLStrCompNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__: callnode.o; +text: .text%__1cIGraphKitMnew_instance6MpnPciInstanceKlass__pnENode__; +text: .text%__1cHnmethodXinterpreter_entry_point6M_pC_; +text: .text%__1cOcmovIL_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeTnmethod_entry_point6FpnKJavaThread_pnNmethodOopDesc_pnHnmethod__pC_; +text: .text%__1cQshlI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapIcopy_cts6MpnNCellTypeState_2_i_; +text: .text%__1cTCallDynamicJavaNodeSis_CallDynamicJava6kM_pk0_: callnode.o; +text: .text%__1cLRuntimeStubbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cQshlL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKg3RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cQaddF_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParsePmerge_exception6Mi_v_; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: frame.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFParseMdo_checkcast6M_v_; +text: .text%__1cIDivLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cRtestI_reg_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQregF_to_stkINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerDpop6MnITosState__v_; +text: .text%__1cKstoreBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cUPSAdaptiveSizePolicybDcompute_generation_free_space6MLLLLLLLi_v_; +text: .text%__1cIPSOldGenMmax_gen_size6M_L_: psOldGen.o; +text: .text%__1cUPSAdaptiveSizePolicyQdecaying_gc_cost6kM_d_; +text: .text%__1cUPSAdaptiveSizePolicyZdecay_supplemental_growth6Mi_v_; +text: .text%__1cUPSAdaptiveSizePolicyVadjust_for_throughput6MipL1_v_; +text: .text%__1cKPSScavengeGinvoke6Fpi_v_; +text: .text%__1cbDVM_ParallelGCFailedAllocationEname6kM_pkc_: vm_operations.o; +text: .text%__1cUPSAdaptiveSizePolicyOshould_full_GC6ML_i_; +text: .text%__1cUParallelScavengeHeapTfailed_mem_allocate6MpiLii_pnIHeapWord__; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cbDVM_ParallelGCFailedAllocation2t6MLiiI_v_; +text: .text%__1cJCMoveNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cbDVM_ParallelGCFailedAllocationEdoit6M_v_; +text: .text%__1cMTypeKlassPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cIMachNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: templateTable_sparc.o; +text: .text%__1cKstoreLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSxorI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQshlL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHdel_out6Mp0_v_: generateOptoStub.o; +text: .text%__1cRloadConP_pollNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKo1RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cQcmovI_reg_gtNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cPstoreI_FregNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNObjectMonitorGEnterI6MpnGThread__v_; +text: .text%__1cKReflectionTget_parameter_types6FnMmethodHandle_ippnHoopDesc_pnGThread__nOobjArrayHandle__; +text: .text%__1cNinstanceKlassQmethod_index_for6kMpnNmethodOopDesc_pnGThread__i_; +text: .text%__1cNmethodOopDescbGresolved_checked_exceptions_impl6Fp0pnGThread__nOobjArrayHandle__; +text: .text%__1cLServiceUtilLvisible_oop6FpnHoopDesc__i_: objectMonitor_solaris.o; +text: .text%__1cNmethodOopDescVparameter_annotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cJLoadDNodeGOpcode6kM_i_; +text: .text%__1cHRegMask2t6M_v_: matcher.o; +text: .text%__1cQcmovI_reg_gtNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cENodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cOPhaseIdealLoopOadd_constraint6MiipnENode_22p23_v_; +text: .text%__1cJLoadPNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cQshrI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKloadUBNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKimmU13OperIconstant6kM_l_: ad_sparc_clone.o; +text: .text%__1cUcompU_iReg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHciKlassSsuper_check_offset6M_I_; +text: .text%__1cQciByteCodeStreamPget_klass_index6M_i_; +text: .text%__1cKciTypeFlowLStateVectorEtrap6MpnQciByteCodeStream_pnHciKlass_i_v_; +text: .text%__1cPPerfDataManagerMcounter_name6Fpkc2_pc_; +text: .text%__1cHMonitor2t6Mipkci_v_; +text: .text%__1cIModLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLConvI2DNodeGOpcode6kM_i_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: interp_masm_sparc.o; +text: .text%__1cOtypeArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cQregP_to_stkPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOcmovPI_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMregD_lowOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cQxorI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMMachProjNodeHsize_of6kM_I_: classes.o; +text: .text%__1cQshrI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParseWcheck_interpreter_type6MpnENode_pknEType_rpnNSafePointNode__2_; +text: .text%__1cTmembar_volatileNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cKarrayKlassTallocate_arrayArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FLipnGThread__pnIHeapWord__: arrayKlass.o; +text: .text%__1cOcmovII_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseXfetch_interpreter_state6MipknEType_pnENode__5_; +text: .text%__1cSsubL_reg_reg_2NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPmethodDataKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cSTailCalljmpIndNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRloadConP_pollNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: interpreter_sparc.o; +text: .text%__1cOcmovII_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddF_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLstoreI0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovPP_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFframeZinterpreter_frame_set_mdp6MpC_v_; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: bytecode.o; +text: .text%JVM_GetCallerClass; +text: .text%__1cMloadConLNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: ad_sparc.o; +text: .text%__1cObox_handleNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJScopeDescGsender6kM_p0_; +text: .text%__1cICmpFNodeGOpcode6kM_i_; +text: .text%jni_GetFieldID: jni.o; +text: .text%__1cOcompiledVFrameGsender6kM_pnGvframe__; +text: .text%__1cSobjArrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%jni_IsAssignableFrom: jni.o; +text: .text%__1cLConvI2FNodeGOpcode6kM_i_; +text: .text%__1cCosJyield_all6Fi_v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pCii_v_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: vmThread.o; +text: .text%__1cQdivL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQciByteCodeStreamFtable6MnJBytecodesECode__2_; +text: .text%__1cGEventsDlog6FpkcE_v_: sharedRuntime.o; +text: .text%__1cOcmovII_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerbAget_cache_and_index_at_bcp6MpnMRegisterImpl_2i_v_; +text: .text%__1cUmulL_reg_imm13_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cYcompareAndSwapL_boolNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOLibraryCallKitRinline_unsafe_CAS6MnJBasicType__i_; +text: .text%__1cTCompareAndSwapLNode2t6MpnENode_2222_v_; +text: .text%__1cIProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: interp_masm_sparc.o; +text: .text%__1cNSCMemProjNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cVCallRuntimeDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cYcompareAndSwapL_boolNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSCompareAndSwapNode2t6MpnENode_2222_v_; +text: .text%__1cOCallNativeNodeGOpcode6kM_i_; +text: .text%__1cQshlL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHTypeAryFxdual6kM_pknEType__; +text: .text%__1cPconvF2D_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarL_reg_imm6NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOPhaseIdealLoopJdo_unroll6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cMtlsLoadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQregP_to_stkPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerDbpr6Mn0AKRCondition_in0AHPredict_pnMRegisterImpl_rnFLabel__v_: interp_masm_sparc.o; +text: .text%__1cMloadConLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cQshrI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIL_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cTCallInterpreterNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cHMatcherbAinterpreter_method_oop_reg6F_nHOptoRegEName__; +text: .text%__1cZCallInterpreterDirectNodeKmethod_set6Ml_v_; +text: .text%__1cOloadConL13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSTailCalljmpIndNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompilebMGenerate_Compiled_To_Interpreter_Graph6MpknITypeFunc_pC_v_; +text: .text%__1cPadd_derived_oop6FppnHoopDesc_2_v_: oopMap.o; +text: .text%__1cIciMethodRinterpreter_entry6M_pC_; +text: .text%__1cZCallInterpreterDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXMachCallInterpreterNodeWis_MachCallInterpreter6M_p0_: ad_sparc_misc.o; +text: .text%__1cZCallInterpreterDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPClassFileParserbJparse_classfile_signature_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cQjava_lang_ThreadJis_daemon6FpnHoopDesc__i_; +text: .text%__1cVLoaderConstraintTableWfind_constrained_klass6MnMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cRorI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNloadConPCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerIpush_ptr6MpnMRegisterImpl__v_; +text: .text%__1cODeoptimizationYtrap_state_is_recompiled6Fi_i_; +text: .text%__1cSmulI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPICStubInterfaceKinitialize6MpnEStub_i_v_: icBuffer.o; +text: .text%__1cJloadINodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIL_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPICStubInterfaceRcode_size_to_size6kMi_i_: icBuffer.o; +text: .text%__1cKstoreFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%jni_SetBooleanField: jni.o; +text: .text%__1cQregI_to_stkINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cWResolveOopMapConflictsRpossible_gc_point6MpnOBytecodeStream__i_: rewriter.o; +text: .text%__1cRtestI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cObox_handleNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerGpush_i6MpnMRegisterImpl__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_object6M_v_; +text: .text%__1cPClassFileParserbBcheck_illegal_static_method6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cSTailCalljmpIndNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSTailCalljmpIndNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNObjectMonitorHRecycle6M_v_; +text: .text%__1cRsarL_reg_imm6NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cXvirtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cRcompL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmulL_reg_reg_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cXvirtual_call_RelocationJpack_data6M_i_; +text: .text%__1cQcmovI_reg_gtNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cXMachCallDynamicJavaNodePret_addr_offset6M_i_; +text: .text%__1cOMacroAssemblerWstore_unaligned_double6MpnRFloatRegisterImpl_pnMRegisterImpl_i_v_; +text: .text%__1cNSharedRuntimeSfind_callee_method6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cLcmpD_ccNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshrL_reg_imm6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQSystemDictionaryTresolve_from_stream6FnMsymbolHandle_nGHandle_2pnPClassFileStream_pnGThread__pnMklassOopDesc__; +text: .text%__1cHMatcherXinterpreter_arg_ptr_reg6F_nHOptoRegEName__; +text: .text%__1cQshlL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOloadConL13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNSharedRuntimeTreresolve_call_site6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cHciKlassMis_interface6M_i_: ciTypeArrayKlass.o; +text: .text%__1cQmulF_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLOptoRuntimeThandle_wrong_method6FpnKJavaThread__pC_; +text: .text%__1cOMacroAssemblerUstore_unaligned_long6MpnMRegisterImpl_2i_v_; +text: .text%__1cQaddL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHBoxNodeGOpcode6kM_i_; +text: .text%__1cSCompiledStaticCallIis_clean6kM_i_; +text: .text%__1cFParseScreate_jump_tables6MpnENode_pnLSwitchRange_4_i_; +text: .text%__1cbFunnecessary_membar_volatileNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCompiledStaticCallDset6MrknOStaticCallInfo__v_; +text: .text%__1cSCompiledStaticCallNcompute_entry6FnMmethodHandle_rnOStaticCallInfo__v_; +text: .text%__1cLOptoRuntimeVresolve_static_call_C6FpnKJavaThread__pC_; +text: .text%__1cKsplit_once6FpnMPhaseIterGVN_pnENode_333_v_: cfgnode.o; +text: .text%__1cSconvD2I_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcipnNsymbolOopDesc_4_v_; +text: .text%__1cSdivL_reg_reg_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_reg_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPstoreI_FregNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFciEnvZcall_has_multiple_targets6FpnNinstanceKlass_nMsymbolHandle_3ri_i_; +text: .text%__1cQComputeCallStackJdo_double6M_v_: generateOopMap.o; +text: .text%__1cSsubL_reg_reg_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopUpeeled_dom_test_elim6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cZInterpreterMacroAssemblerKverify_oop6MpnMRegisterImpl_nITosState__v_; +text: .text%__1cSsubL_reg_reg_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerGpush_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cTAbstractInterpreterSBasicType_as_index6FnJBasicType__i_; +text: .text%__1cZInterpreterMacroAssemblerFpop_i6MpnMRegisterImpl__v_; +text: .text%__1cPjava_lang_ClassQprimitive_mirror6FnJBasicType__pnHoopDesc__; +text: .text%__1cPconvF2D_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKExceptionsL_throw_args6FpnGThread_pkcinMsymbolHandle_5pnRJavaCallArguments__v_; +text: .text%__1cSandL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSxorI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNObjectMonitorEwait6MxipnGThread__v_; +text: .text%__1cQSystemDictionaryVadd_loader_constraint6FnMsymbolHandle_nGHandle_2pnGThread__v_; +text: .text%__1cOMethodLivenessKBasicBlockFsplit6Mi_p1_; +text: .text%JVM_MonitorWait; +text: .text%__1cKcmpOpUOperKless_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cSObjectSynchronizerEwait6FnGHandle_xpnGThread__v_; +text: .text%__1cENodeEgetd6kM_d_; +text: .text%__1cKJavaThreadbScheck_safepoint_and_suspend_for_native_trans6Fp0_v_; +text: .text%__1cUdivL_reg_imm13_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcmpD_ccNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIciObject2t6MpnHciKlass__v_; +text: .text%__1cKloadUBNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseSjump_if_false_fork6MpnGIfNode_ii_v_; +text: .text%__1cLRShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLcmpF_ccNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKCMovePNodeGOpcode6kM_i_; +text: .text%__1cKciTypeFlowOsplit_range_at6Mi_pn0AFRange__; +text: .text%__1cIMaxINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cNloadConP0NodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cWloadConI_x43300000NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPstoreI_FregNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSdivL_reg_reg_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKloadUBNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvI2F_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cYinternal_word_RelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cENode2t6Mp0111111_v_; +text: .text%__1cOGenerateOopMapXreplace_all_CTS_matches6MnNCellTypeState_1_v_; +text: .text%__1cHciKlass2t6MpnIciSymbol_p0_v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cGciType2t6MpnHciKlass__v_; +text: .text%__1cRbranchLoopEndNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cKstoreBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQset_lwp_priority6Fiii_i_; +text: .text%__1cbDreorder_based_on_method_index6FpnPobjArrayOopDesc_1ppnHoopDesc__v_: methodOop.o; +text: .text%__1cSaddP_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cCosTset_native_priority6FpnGThread_i_nIOSReturn__; +text: .text%__1cUciInstanceKlassKlassEmake6F_p0_; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongVariable__; +text: .text%__1cJMarkSweepSFollowStackClosureHdo_void6M_v_: markSweep.o; +text: .text%__1cIciObjectUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cOcmovPP_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSingletonBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cXjava_lang_ref_ReferenceOset_discovered6FpnHoopDesc_2_v_; +text: .text%__1cIciSymbolHbyte_at6Mi_i_; +text: .text%__1cKstoreCNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUParallelScavengeHeapIcapacity6kM_L_; +text: .text%__1cbCAbstractInterpreterGeneratorVset_vtos_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cZInterpreterMacroAssemblerXget_constant_pool_cache6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_l6MpnMRegisterImpl__v_; +text: .text%__1cOcmovPP_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassYremove_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cIDivLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOcmovII_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNflagsRegFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cQregP_to_stkPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSdivL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMjniIdSupportNto_jmethod_id6FpnNmethodOopDesc__pnK_jmethodID__; +text: .text%__1cMLinkResolverUresolve_virtual_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cLstoreC0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cWloadConI_x41f00000NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCompiledICMset_to_clean6M_v_; +text: .text%__1cNmethodOopDescKjmethod_id6M_pnK_jmethodID__; +text: .text%__1cNget_method_id6FpnHJNIEnv__pnH_jclass_pkc5ipnGThread__pnK_jmethodID__: jni.o; +text: .text%__1cKstoreINodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%jni_NewStringUTF: jni.o; +text: .text%__1cQjava_lang_StringTcreate_oop_from_str6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cTAbstractInterpreterLdeopt_entry6FnITosState_i_pC_; +text: .text%JVM_SetClassSigners; +text: .text%__1cQregP_to_stkPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRorI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPciInstanceKlassNloader_handle6M_pnI_jobject__; +text: .text%JVM_DefineClassWithSource; +text: .text%__1cPciInstanceKlassYprotection_domain_handle6M_pnI_jobject__; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceRefKlass.o; +text: .text%__1cQRelocationHolderEplus6kMi_0_; +text: .text%__1cISubLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cTmembar_volatileNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKloadUBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMflagsRegOperFclone6kM_pnIMachOper__; +text: .text%__1cObox_handleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadSNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cMnegD_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHOrINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cOloadI_fregNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOloadI_fregNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovDF_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%jni_CallIntMethod: jni.o; +text: .text%__1cFParseHdo_irem6M_v_; +text: .text%__1cVinline_cache_regPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cSThreadLocalStoragebBget_thread_via_cache_slowly6FLi_pnGThread__; +text: .text%__1cSThreadLocalStorageNpd_set_thread6FpnGThread__v_; +text: .text%__1cSThreadLocalStorageKset_thread6FpnGThread__v_; +text: .text%get_thread; +text: .text%__1cJLoadINodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cSThreadLocalStorageSset_thread_in_slot6FpnGThread__v_; +text: .text%__1cRtestI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConPCNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_return_entry_for6MnITosState_i_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_deopt_entry_for6MnITosState_i_pC_; +text: .text%__1cHCompilebMGenerate_Interpreter_To_Compiled_Graph6MpknITypeFunc__v_; +text: .text%__1cbACallCompiledJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUCallCompiledJavaNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cFTypeDJsingleton6kM_i_; +text: .text%__1cSCountedLoopEndNode2t6MpnENode_2ff_v_; +text: .text%__1cSbranchCon_longNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cPstoreI_FregNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLstoreF0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cbACallCompiledJavaDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cQmulI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateP_sub_Op_RShiftL6MpknENode__v_; +text: .text%__1cbACallCompiledJavaDirectNodeKmethod_set6Ml_v_; +text: .text%__1cHCompileRmake_vm_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cbACallCompiledJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_GetClassMethodsCount; +text: .text%JVM_GetClassFieldsCount; +text: .text%JVM_GetClassCPTypes; +text: .text%__1cQjava_lang_ThreadMis_stillborn6FpnHoopDesc__i_; +text: .text%__1cOcmovII_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%JVM_GetClassCPEntriesCount; +text: .text%__1cOMacroAssemblerKverify_FPU6Mipkc_v_; +text: .text%__1cbCfind_class_from_class_loader6FpnHJNIEnv__nMsymbolHandle_CnGHandle_3CpnGThread__pnH_jclass__; +text: .text%__1cKstoreLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIAddFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIMulDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQshlI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_reg_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadDNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cQregF_to_stkINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: thread.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_5pnGThread__v_; +text: .text%__1cIAddDNodeGOpcode6kM_i_; +text: .text%__1cRtestI_reg_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_ThreadKset_thread6FpnHoopDesc_pnKJavaThread__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIgenerate6ML_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interpreterRT_sparc.o; +text: .text%__1cbBinitialize_itable_for_klass6FpnMklassOopDesc__v_; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: interpreterRT_sparc.o; +text: .text%__1cXSignatureHandlerLibraryOpd_set_handler6FpC_v_; +text: .text%__1cMloadConLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIimmLOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: machnode.o; +text: .text%__1cQmulD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRorI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUPSMarkSweepDecoratorKprecompact6M_v_; +text: .text%jni_FindClass: jni.o; +text: .text%__1cbFunnecessary_membar_volatileNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverbPlinktime_resolve_interface_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cLStringTableJbasic_add6MinGHandle_pHiIpnGThread__pnHoopDesc__; +text: .text%__1cUPSMarkSweepDecoratorPadjust_pointers6M_v_; +text: .text%__1cPconvI2L_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUPSMarkSweepDecoratorHcompact6Mi_v_; +text: .text%__1cMloadConFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_2NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHnmethodPis_dependent_on6MpnMklassOopDesc__i_; +text: .text%__1cRshlL_reg_imm6NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJLoadSNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cIModLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKLoadPCNodeGOpcode6kM_i_; +text: .text%__1cKReflectionPnew_constructor6FnMmethodHandle_pnGThread__pnHoopDesc__; +text: .text%__1cPconvF2D_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%JVM_IsPrimitiveClass; +text: .text%__1cRsarL_reg_imm6NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cUmulL_reg_imm13_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cbDjava_lang_reflect_ConstructorZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorVhas_annotations_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorThas_signature_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cGThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cbDjava_lang_reflect_ConstructorGcreate6FpnGThread__nGHandle__; +text: .text%__1cGThreadVis_jvmti_agent_thread6kM_i_: thread.o; +text: .text%__1cOcmovII_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cbDjava_lang_reflect_ConstructorbFhas_parameter_annotations_field6F_i_; +text: .text%__1cRorI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSconvI2F_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNimmP_pollOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cTmembar_volatileNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFTypeDFxmeet6kMpknEType__3_; +text: .text%__1cRloadConP_pollNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvI2D_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cQaddD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cIDivDNodeGOpcode6kM_i_; +text: .text%__1cJloadINodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLstoreC0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSdivL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSsubL_reg_reg_2NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNSharedRuntimeDd2l6Fd_x_; +text: .text%__1cIMulINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMnegD_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUmulL_reg_imm13_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cUdivL_reg_imm13_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompileQgrow_alias_types6M_v_; +text: .text%lwp_cond_destroy: os_solaris.o; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceRefKlass.o; +text: .text%__1cHMonitor2T6M_v_; +text: .text%__1cQdivD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIModINodeJideal_reg6kM_I_: classes.o; +text: .text%lwp_mutex_destroy: os_solaris.o; +text: .text%__1cVshrL_reg_imm6_L2INodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSconvI2D_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOimmI_32_63OperIconstant6kM_l_: ad_sparc_clone.o; +text: .text%__1cFMutex2T6M_v_; +text: .text%__1cQregI_to_stkINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRcompL_reg_conNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerHpop_ptr6MpnMRegisterImpl__v_; +text: .text%__1cQjava_lang_ThreadLthreadGroup6FpnHoopDesc__2_; +text: .text%__1cNinstanceKlassKlink_class6MpnGThread__v_; +text: .text%__1cOcmovII_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICmpDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_: callnode.o; +text: .text%__1cQaddD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIGraphKitPdstore_rounding6MpnENode__2_; +text: .text%__1cSdivL_reg_reg_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregI_to_stkINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFframeVshould_be_deoptimized6kM_i_; +text: .text%__1cUmulL_reg_imm13_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICodeBlobOis_java_method6kM_i_: codeBlob.o; +text: .text%__1cQregP_to_stkPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cFciEnvWis_dependence_violated6FpnMklassOopDesc_pnNmethodOopDesc__i_; +text: .text%__1cNObjectMonitor2t6M_v_; +text: .text%__1cQregP_to_stkPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFBlockNset_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cRtestI_reg_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledICSset_ic_destination6MpC_v_; +text: .text%JVM_MonitorNotify; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: multnode.o; +text: .text%__1cSObjectSynchronizerGnotify6FnGHandle_pnGThread__v_; +text: .text%__1cOBasicHashtableJnew_entry6MI_pnTBasicHashtableEntry__; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceRefKlass.o; +text: .text%__1cSstkL_to_regD_0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__: classes.o; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_: interpreterRT_sparc.o; +text: .text%__1cNimmP_pollOperFclone6kM_pnIMachOper__; +text: .text%__1cOPhaseIdealLoopOdo_range_check6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cRloadConP_pollNodeFclone6kM_pnENode__; +text: .text%__1cSstring_compareNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlassKlass.o; +text: .text%__1cOMacroAssemblerDset6MlpnMRegisterImpl_rknQRelocationHolder__v_: templateTable_sparc.o; +text: .text%__1cSconvI2F_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSdivL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRtestI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNloadKlassNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: interpreterRT_sparc.o; +text: .text%__1cQregL_to_stkLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: instanceKlassKlass.o; +text: .text%__1cNRelocIteratorEnext6M_i_: output.o; +text: .text%__1cINegDNodeGOpcode6kM_i_; +text: .text%__1cFStateM_sub_Op_SubL6MpknENode__v_; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodDataKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodDataKlass.o; +text: .text%__1cMloadConLNodeFclone6kM_pnENode__; +text: .text%__1cYjava_lang_reflect_MethodJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodPset_return_type6FpnHoopDesc_2_v_; +text: .text%__1cQaddF_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZget_mirror_from_signature6FnMmethodHandle_pnPSignatureStream_pnGThread__pnHoopDesc__; +text: .text%__1cYjava_lang_reflect_MethodNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cQregL_to_stkLNodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cYjava_lang_reflect_MethodTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodGcreate6FpnGThread__nGHandle__; +text: .text%__1cJCmpD3NodeGOpcode6kM_i_; +text: .text%__1cQsubL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYjava_lang_reflect_MethodTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cKstoreFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKReflectionKnew_method6FnMmethodHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cYjava_lang_reflect_MethodPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodbFhas_parameter_annotations_field6F_i_; +text: .text%__1cNmethodOopDescSannotation_default6kM_pnQtypeArrayOopDesc__; +text: .text%__1cIimmLOperFclone6kM_pnIMachOper__; +text: .text%__1cYjava_lang_reflect_MethodIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodIset_name6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodVhas_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodbChas_annotation_default_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodThas_signature_field6F_i_; +text: .text%__1cIregDOperFclone6kM_pnIMachOper__; +text: .text%__1cYjava_lang_reflect_MethodWset_annotation_default6FpnHoopDesc_2_v_; +text: .text%__1cOGenerateOopMapLmonitor_pop6M_nNCellTypeState__; +text: .text%__1cNIdealLoopTreeXpolicy_maximally_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cOGenerateOopMapOdo_monitorexit6Mi_v_; +text: .text%__1cIMulINodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cJEventMark2t6MpkcE_v_: psMarkSweep.o; +text: .text%jni_GetMethodID: jni.o; +text: .text%__1cUdivL_reg_imm13_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTAbstractInterpreterWlayout_activation_impl6FpnNmethodOopDesc_iiiipnFframe_4i_i_; +text: .text%__1cOcmovPP_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cICodeHeapLfirst_block6kM_pnJHeapBlock__; +text: .text%__1cUmulL_reg_imm13_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulINodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cJCMoveNodeEmake6FpnENode_222pknEType__p0_; +text: .text%__1cFTypeFJsingleton6kM_i_; +text: .text%__1cIMinINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJCMoveNode2t6MpnENode_22pknEType__v_: connode.o; +text: .text%__1cSandL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDjmp6MpnMRegisterImpl_ipkci_v_; +text: .text%__1cRorI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: connode.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: connode.o; +text: .text%__1cQshrL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%JVM_GetClassDeclaredConstructors; +text: .text%__1cQsubF_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLcmpF_ccNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcmovDF_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: deoptimization.o; +text: .text%__1cMmatch_option6FpknMJavaVMOption_pkcp4_i_: arguments.o; +text: .text%__1cQregI_to_stkINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMregD_lowOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cQstkI_to_regFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNObjectMonitorGnotify6MpnGThread__v_; +text: .text%__1cXconvI2D_regDHi_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJCMoveNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQmulD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQshlI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MipnMRegisterImpl__v_; +text: .text%__1cQmulF_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIDivLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIBytecodeIset_code6MnJBytecodesECode__v_; +text: .text%__1cNminI_eRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_ltNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvD2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNminI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cQaddD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreC0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeBlobJis_zombie6kM_i_: onStackReplacement.o; +text: .text%__1cObranchConFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cUdivL_reg_imm13_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSsubL_reg_reg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNloadConPCNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConPCNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadINodeFclone6kM_pnENode__; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: node.o; +text: .text%__1cRInlineCacheBufferRic_stub_code_size6F_i_; +text: .text%__1cQregI_to_stkINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQshlI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvD2I_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: node.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: node.o; +text: .text%__1cNloadConPCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStubQdDueueRrequest_committed6Mi_pnEStub__; +text: .text%__1cNinstanceKlassQfind_local_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__i_; +text: .text%__1cUGenericGrowableArrayKraw_remove6MpknEGrET__v_; +text: .text%__1cPICStubInterfaceEsize6kMpnEStub__i_: icBuffer.o; +text: .text%__1cPICStubInterfaceIfinalize6MpnEStub__v_: icBuffer.o; +text: .text%__1cQshrL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOloadI_fregNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRsarI_reg_imm5NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSconvD2I_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQregF_to_stkINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cINodeHashEgrow6M_v_; +text: .text%__1cZCallDynamicJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXvirtual_call_RelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cQMachCallJavaNodeVis_MachCallStaticJava6M_pnWMachCallStaticJavaNode__: ad_sparc_misc.o; +text: .text%__1cZCallDynamicJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQdivL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_ThreadMset_priority6FpnHoopDesc_nOThreadPriority__v_; +text: .text%__1cSdivL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: output.o; +text: .text%__1cOcmovDF_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZCallDynamicJavaDirectNodeKmethod_set6Ml_v_; +text: .text%__1cSmulL_reg_reg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovIF_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovIF_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIVMThreadMis_VM_thread6kM_i_: vmThread.o; +text: .text%__1cQsubD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%JVM_IsThreadAlive; +text: .text%__1cQjava_lang_ThreadIis_alive6FpnHoopDesc__i_; +text: .text%__1cOMacroAssemblerOstore_argument6MpnMRegisterImpl_rnIArgument__v_: interpreterRT_sparc.o; +text: .text%__1cKo1RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIpass_int6M_v_: interpreterRT_sparc.o; +text: .text%__1cHnmethodNis_osr_method6kM_i_: nmethod.o; +text: .text%__1cObox_handleNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cGThreadOis_Java_thread6kM_i_: vmThread.o; +text: .text%__1cENodeEgetf6kM_f_; +text: .text%__1cQmulF_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFKlassMoop_is_klass6kM_i_: typeArrayKlass.o; +text: .text%__1cLstoreP0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUVirtualCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cNCallGeneratorQfor_virtual_call6FpnIciMethod__p0_; +text: .text%__1cSsubL_reg_reg_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubF_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRInvocationCounterFreset6M_v_; +text: .text%__1cUdivL_reg_imm13_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovPP_regNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cNiRegIsafeOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKCMoveINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cXconvI2D_regDHi_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConL0NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIregFOperFclone6kM_pnIMachOper__; +text: .text%__1cHTypePtrFxdual6kM_pknEType__; +text: .text%__1cUVirtualCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cJloadDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstring_compareNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcmovII_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSafepointBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cISubDNodeGOpcode6kM_i_; +text: .text%__1cOPhaseIdealLoopKdo_peeling6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cQsubD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRsarL_reg_imm6NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarL_reg_imm6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_ModI6MpknENode__v_; +text: .text%__1cOPhaseIdealLoopVinsert_pre_post_loops6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cQmodI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFTypeFFxmeet6kMpknEType__3_; +text: .text%__1cOcmovPI_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKVtableStubSpd_code_size_limit6Fi_i_; +text: .text%__1cQmodI_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cCosScurrent_stack_base6F_pC_; +text: .text%__1cIOSThread2t6MpFpv_i1_v_; +text: .text%__1cCosHSolarisPhotspot_sigmask6FpnGThread__v_; +text: .text%__1cGThread2t6M_v_; +text: .text%__1cIOSThreadNpd_initialize6M_v_; +text: .text%__1cCosRinitialize_thread6F_v_; +text: .text%__1cCosHSolarisVinit_thread_fpu_state6F_v_; +text: .text%__1cCosScurrent_stack_size6F_L_; +text: .text%__1cIMulDNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cODeoptimizationYquery_update_method_data6FnQmethodDataHandle_in0ALDeoptReason_rIri4_pnLProfileData__; +text: .text%JVM_SetThreadPriority; +text: .text%__1cFTypeDGis_nan6kM_i_; +text: .text%__1cNobjArrayKlassIallocate6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cQjava_lang_SystemSin_offset_in_bytes6F_i_; +text: .text%__1cQregF_to_stkINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosPpd_start_thread6FpnGThread__v_; +text: .text%__1cQaddF_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_SystemTout_offset_in_bytes6F_i_; +text: .text%__1cQshlL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%_start: os_solaris.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlass.o; +text: .text%__1cQdivD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMloadConINodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosNcreate_thread6FpnGThread_n0AKThreadType_L_i_; +text: .text%jni_NewObjectArray: jni.o; +text: .text%__1cCosMstart_thread6FpnGThread__v_; +text: .text%__1cNobjArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cOcmovPI_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQshrL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMarkSweepMfollow_stack6F_v_; +text: .text%__1cHnmethodbCcan_not_entrant_be_converted6M_i_; +text: .text%__1cJloadLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovLL_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMemRegionMintersection6kMk0_0_; +text: .text%__1cPfieldDescriptorLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cKReflectionInew_type6FnMsymbolHandle_nLKlassHandle_pnGThread__nGHandle__; +text: .text%JVM_Read; +text: .text%__1cXjava_lang_reflect_FieldPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldVhas_annotations_field6F_i_; +text: .text%__1cXjava_lang_reflect_FieldThas_signature_field6F_i_; +text: .text%__1cXjava_lang_reflect_FieldNset_modifiers6FpnHoopDesc_i_v_; +text: .text%JVM_GetStackAccessControlContext; +text: .text%__1cIMulFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cXjava_lang_reflect_FieldIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_type6FpnHoopDesc_2_v_; +text: .text%__1cJvmSymbolsOsignature_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cOMacroAssemblerTload_unaligned_long6MpnMRegisterImpl_i2_v_; +text: .text%__1cKReflectionJnew_field6FpnPfieldDescriptor_ipnGThread__pnHoopDesc__; +text: .text%__1cXjava_lang_reflect_FieldGcreate6FpnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cQstkI_to_regINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_reflect_FieldIset_name6FpnHoopDesc_2_v_; +text: .text%__1cQaddD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: callGenerator.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQregI_to_stkINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetStaticFieldID: jni.o; +text: .text%__1cENodeGis_Con6kM_I_: loopnode.o; +text: .text%__1cIModLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: loopnode.o; +text: .text%__1cSsubD_regD_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvI2D_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassKjni_id_for6Mi_pnFJNIid__; +text: .text%__1cKimmL13OperFclone6kM_pnIMachOper__; +text: .text%__1cRsarL_reg_imm6NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerOthrow_if_not_26MpCpnMRegisterImpl_rnFLabel__v_; +text: .text%JVM_DesiredAssertionStatus; +text: .text%__1cSmulD_regD_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSstring_compareNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOJavaAssertionsHenabled6Fpkci_i_; +text: .text%__1cKJavaThreadKinitialize6M_v_; +text: .text%__1cMFlatProfilerJis_active6F_i_; +text: .text%__1cIDivLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMacroAssemblerDset6MlpnMRegisterImpl_rknQRelocationHolder__v_: interp_masm_sparc.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC22i_v_; +text: .text%__1cHnmethodbAmake_not_entrant_or_zombie6Mi_v_; +text: .text%__1cNjni_functions6F_pknTJNINativeInterface___; +text: .text%__1cLConvI2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cUThreadSafepointStateGcreate6FpnKJavaThread__v_; +text: .text%__1cWThreadLocalAllocBufferKinitialize6M_v_; +text: .text%__1cNloadConL0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNThreadServiceKadd_thread6FpnKJavaThread_i_v_; +text: .text%__1cGParker2t6M_v_; +text: .text%__1cCosMguard_memory6FpcL_i_; +text: .text%__1cQThreadStatistics2t6M_v_; +text: .text%__1cHMatcherXpost_store_load_barrier6FpknENode__i_; +text: .text%__1cNObjectMonitorGenter26MpnGThread__v_; +text: .text%__1cPPerfDataManagerIadd_item6FpnIPerfData_i_v_; +text: .text%__1cSandL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVcompiledICHolderKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cPjava_lang_ClassOprimitive_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cGThreadFstart6Fp0_v_; +text: .text%__1cKCMoveDNodeGOpcode6kM_i_; +text: .text%__1cOcmovIF_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLcmpF_ccNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryQjava_mirror_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cRtestI_reg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOloadI_fregNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_reg_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJavaThread2t6MpFp0pnGThread__vL_v_; +text: .text%__1cJloadSNodeFclone6kM_pnENode__; +text: .text%__1cKJavaThreadDrun6M_v_; +text: .text%jni_GetFloatArrayRegion: jni.o; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_: interp_masm_sparc.o; +text: .text%__1cOloadI_fregNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapTmark_reachable_code6M_v_; +text: .text%__1cOGenerateOopMapRinit_basic_blocks6M_v_; +text: .text%__1cNsymbolOopDescWas_klass_external_name6kM_pkc_; +text: .text%jni_CallStaticVoidMethod: jni.o; +text: .text%__1cOGenerateOopMapPinitialize_vars6M_v_; +text: .text%__1cOGenerateOopMapTmethodsig_to_effect6MpnNsymbolOopDesc_ipnNCellTypeState__i_; +text: .text%__1cOGenerateOopMapKinterp_all6M_v_; +text: .text%__1cJLoadPNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cOGenerateOopMapYsetup_method_entry_state6M_v_; +text: .text%__1cOGenerateOopMapbAmake_context_uninitialized6M_v_; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnPPerfLongCounter__; +text: .text%__1cOGenerateOopMapKinit_state6M_v_; +text: .text%__1cLConvI2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFKlassNexternal_name6kM_pkc_; +text: .text%__1cOcmovIL_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: cfgnode.o; +text: .text%__1cOGenerateOopMapYrewrite_refval_conflicts6M_v_; +text: .text%__1cSaddD_regD_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQsubD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvF2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJloadBNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvI2F_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJArrayDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cQsubD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlass.o; +text: .text%__1cQshrI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcmpF_ccNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvL2DNodeGOpcode6kM_i_; +text: .text%__1cFStateM_sub_Op_MulI6MpknENode__v_; +text: .text%JVM_GetClassName; +text: .text%__1cOMacroAssemblerVload_unaligned_double6MpnMRegisterImpl_ipnRFloatRegisterImpl__v_; +text: .text%JVM_IsArrayClass; +text: .text%__1cQdivI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubFNodeGOpcode6kM_i_; +text: .text%__1cSsubD_regD_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHAddress2t6Mn0AJaddr_type_i_v_; +text: .text%__1cSmulD_regD_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLStringTableGintern6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cMnegD_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2L_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapNinitialize_bb6M_v_; +text: .text%__1cOGenerateOopMapbImark_bbheaders_and_count_gc_points6M_v_; +text: .text%__1cLTypeInstPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cbFunnecessary_membar_volatileNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cQsubF_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIRetTableRcompute_ret_table6MnMmethodHandle__v_; +text: .text%__1cOGenerateOopMapLcompute_map6MpnGThread__v_; +text: .text%__1cCosMset_priority6FpnGThread_nOThreadPriority__nIOSReturn__; +text: .text%__1cQdivL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMap2t6MnMmethodHandle__v_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: generateOptoStub.o; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_: interpreterRT_sparc.o; +text: .text%__1cRtestI_reg_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvD2F_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapRdo_interpretation6M_v_; +text: .text%__1cLstoreF0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNCallGeneratorSfor_predicted_call6FpnHciKlass_p03_3_; +text: .text%__1cWPredictedCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cWPredictedCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cWPredictedCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: markSweep.o; +text: .text%__1cWstatic_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: interpreter_sparc.o; +text: .text%__1cJMarkSweepSMarkAndPushClosureLdo_nmethods6kM_ki_: markSweep.o; +text: .text%__1cJloadSNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cZregDHi_regDLo_to_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cZUncommonTrapCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%JVM_Open; +text: .text%__1cLconvI2BNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNCallGeneratorRfor_uncommon_trap6FpnIciMethod_nODeoptimizationLDeoptReason_n0CLDeoptAction__p0_; +text: .text%__1cSandL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: loopnode.o; +text: .text%__1cSconvI2F_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKConv2BNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMloadConDNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: loopnode.o; +text: .text%jni_GetStaticObjectField: jni.o; +text: .text%__1cNPerfByteArray2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_i_v_; +text: .text%__1cMthread_entry6FpnKJavaThread_pnGThread__v_: jvm.o; +text: .text%__1cQandI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIMaxINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cKCodeBufferWinsert_double_constant6Md_pC_; +text: .text%__1cSstkL_to_regD_0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJavaThreadHprepare6MpnI_jobject_nOThreadPriority__v_; +text: .text%JVM_StartThread; +text: .text%__1cOcmovDF_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQjava_lang_ThreadIpriority6FpnHoopDesc__nOThreadPriority__; +text: .text%__1cPciObjArrayKlassEmake6FpnHciKlass__p0_; +text: .text%__1cQjava_lang_ThreadJstackSize6FpnHoopDesc__x_; +text: .text%__1cMloadConDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRInlineCacheBufferWcreate_transition_stub6FpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cZInterpreterMacroAssemblerEpush6MnITosState__v_; +text: .text%__1cQdivD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: icBuffer_sparc.o; +text: .text%__1cRInlineCacheBufferXassemble_ic_buffer_code6FpCpnHoopDesc_1_v_; +text: .text%__1cSaddD_regD_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadConPCNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_2NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQOopMapCacheEntryEfill6MnMmethodHandle_i_v_; +text: .text%__1cObranchConFNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciObjectFactory.o; +text: .text%__1cOcmovIL_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUdivL_reg_imm13_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvF2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIciObjectOis_method_data6M_i_: ciObjectFactory.o; +text: .text%__1cRInlineCacheBufferVic_buffer_entry_point6FpC_1_; +text: .text%__1cLStrCompNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciObjectJis_method6M_i_: ciObjectFactory.o; +text: .text%JVM_TotalMemory; +text: .text%JVM_FreeMemory; +text: .text%__1cUmulL_reg_imm13_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cURethrowExceptionNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRInterpreterOopMapNresource_copy6MpnQOopMapCacheEntry__v_; +text: .text%__1cFStateO_sub_Op_CMoveI6MpknENode__v_; +text: .text%__1cMTailCallNode2t6MpnENode_222222_v_; +text: .text%__1cLConvD2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIGraphKitSprecision_rounding6MpnENode__2_; +text: .text%__1cQsubI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSconvD2I_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNObjectMonitorJnotifyAll6MpnGThread__v_; +text: .text%__1cSTailCalljmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMnegD_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSTailCalljmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosOunguard_memory6FpcL_i_; +text: .text%__1cNThreadServiceWcurrent_thread_exiting6FpnKJavaThread__v_; +text: .text%__1cUThreadSafepointStateHdestroy6FpnKJavaThread__v_; +text: .text%__1cOcmovPI_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodDataKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodDataKlass.o; +text: .text%__1cQaddI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cCosLfree_thread6FpnIOSThread__v_; +text: .text%__1cQjava_lang_ThreadNset_stillborn6FpnHoopDesc__v_; +text: .text%__1cICmpDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cGParker2T6M_v_; +text: .text%__1cNThreadServiceNremove_thread6FpnKJavaThread_i_v_; +text: .text%__1cQregF_to_stkINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKJavaThread2T6M_v_; +text: .text%__1cLensure_join6FpnKJavaThread__v_: thread.o; +text: .text%__1cKJavaThreadEexit6Mi_v_; +text: .text%__1cIGraphKitXinsert_mem_bar_volatile6MpnKMemBarNode_i_v_; +text: .text%__1cKJavaThreadYremove_stack_guard_pages6M_v_; +text: .text%__1cOcmovLI_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIOSThreadKpd_destroy6M_v_; +text: .text%__1cHThreadsGremove6FpnKJavaThread__v_; +text: .text%__1cIOSThread2T6M_v_; +text: .text%__1cGThread2T5B6M_v_; +text: .text%__SLIP.DELETER__A: thread.o; +text: .text%__1cQshrL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regFNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFMutexbLwait_for_lock_blocking_implementation6MpnKJavaThread__v_; +text: .text%__1cHRetDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cTOopMapForCacheEntryLcompute_map6MpnGThread__v_; +text: .text%__1cQOopMapCacheEntryIset_mask6MpnNCellTypeState_2i_v_; +text: .text%__1cQregL_to_stkLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%jni_CallObjectMethod: jni.o; +text: .text%__1cQChunkPoolCleanerEtask6M_v_: allocation.o; +text: .text%__1cTOopMapForCacheEntryOreport_results6kM_i_: oopMapCache.o; +text: .text%__1cOcmovIL_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapVresult_for_basicblock6Mi_v_; +text: .text%__1cLcmpF_ccNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSconvI2D_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerbEset_method_data_pointer_offset6MpnMRegisterImpl__v_; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: klassKlass.o; +text: .text%__1cHTypeInt2t6Miii_v_; +text: .text%__1cOtailjmpIndNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cSsubD_regD_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvF2I_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSobjArrayKlassKlassOklass_oop_size6kM_i_: objArrayKlassKlass.o; +text: .text%__1cSmulD_regD_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLmethodKlassOklass_oop_size6kM_i_: methodKlass.o; +text: .text%__1cPmethodDataKlassOklass_oop_size6kM_i_: methodDataKlass.o; +text: .text%__1cTtypeArrayKlassKlassOklass_oop_size6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cNloadRangeNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQconstMethodKlassOklass_oop_size6kM_i_: constMethodKlass.o; +text: .text%__1cRconstantPoolKlassOklass_oop_size6kM_i_: constantPoolKlass.o; +text: .text%__1cWconstantPoolCacheKlassOklass_oop_size6kM_i_: cpCacheKlass.o; +text: .text%__1cOcmovIF_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseNdo_instanceof6M_v_; +text: .text%__1cVcompiledICHolderKlassOklass_oop_size6kM_i_: compiledICHolderKlass.o; +text: .text%__1cSinstanceKlassKlassOklass_oop_size6kM_i_: instanceKlassKlass.o; +text: .text%__1cSdivL_reg_reg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerFpop_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cSsubL_reg_reg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitOgen_instanceof6MpnENode_2_2_; +text: .text%__1cSmulL_reg_reg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: interpreterRT_sparc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorJpass_long6M_v_; +text: .text%__1cLstoreC0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQshrL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: arrayKlassKlass.o; +text: .text%__1cLsymbolKlassOklass_oop_size6kM_i_: symbolKlass.o; +text: .text%__1cQjava_lang_StringbHcreate_from_platform_depended_str6FpkcpnGThread__nGHandle__; +text: .text%__1cJloadDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledICOset_cached_oop6MpnHoopDesc__v_; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: library_call.o; +text: .text%__1cUGenericGrowableArrayUclear_and_deallocate6M_v_; +text: .text%__1cOGenerateOopMapMmonitor_push6MnNCellTypeState__v_; +text: .text%__1cKCMoveLNodeGOpcode6kM_i_; +text: .text%__1cOGenerateOopMapPdo_monitorenter6Mi_v_; +text: .text%JVM_MonitorNotifyAll; +text: .text%__1cOloadI_fregNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreBNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovLL_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cVMoveL2D_stack_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXconvI2D_regDHi_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSReferenceProcessorHoops_do6MpnKOopClosure__v_; +text: .text%__1cJScopeDescGlocals6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cLConvD2FNodeGOpcode6kM_i_; +text: .text%__1cTAbstractInterpreterQcontinuation_for6FpnNmethodOopDesc_pCiiri_3_; +text: .text%__1cJScopeDescLexpressions6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cJScopeDescImonitors6M_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cTAbstractInterpreterPsize_activation6FpnNmethodOopDesc_iiiii_i_; +text: .text%__1cIPSOldGenPadjust_pointers6M_v_; +text: .text%__1cJvmSymbolsHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cIPSOldGenHcompact6M_v_; +text: .text%__1cTAbstractInterpreterRlayout_activation6FpnNmethodOopDesc_iiiipnFframe_4i_v_; +text: .text%__1cLOptoRuntimeYcurrent_time_millis_Type6F_pknITypeFunc__; +text: .text%__1cOcompiledVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cOcompiledVFrameHraw_bci6kM_i_; +text: .text%jint_cmp: parse2.o; +text: .text%__1cSvframeArrayElementPunpack_on_stack6MiipnFframe_ii_v_; +text: .text%__1cIMaxINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSvframeArrayElementNon_stack_size6kMiiii_i_; +text: .text%__1cFJNIidHoops_do6MpnKOopClosure__v_; +text: .text%__1cXJNI_ArgumentPusherVaArgJget_float6M_v_: jni.o; +text: .text%__1cOcompiledVFrameGlocals6kM_pnUStackValueCollection__; +text: .text%__1cJHashtableGunlink6MpnRBoolObjectClosure__v_; +text: .text%__1cJHashtableHoops_do6MpnKOopClosure__v_; +text: .text%__1cOcompiledVFrameLexpressions6kM_pnUStackValueCollection__; +text: .text%__1cOcompiledVFrameImonitors6kM_pnNGrowableArray4CpnLMonitorInfo____; +text: .text%__1cSvframeArrayElementHfill_in6MpnOcompiledVFrame__v_; +text: .text%__1cHThreadsHoops_do6FpnKOopClosure__v_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: ciTypeFlow.o; +text: .text%__1cOcmovLL_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOLibraryCallKitYinline_native_time_funcs6Mi_i_; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: templateTable_sparc.o; +text: .text%__1cOMacroAssemblerNflush_windows6M_v_; +text: .text%__1cKarrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cKarrayKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cKstorePNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFframeZinterpreter_frame_set_mdx6Ml_v_; +text: .text%__1cQdivI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDset6MlpnMRegisterImpl_rknQRelocationHolder__v_: stubGenerator_sparc.o; +text: .text%__1cQshrI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQObjectStartArrayFreset6M_v_; +text: .text%__1cKg3RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIAddDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQshlL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotIOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cLstoreF0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_: callnode.o; +text: .text%__1cKimmI11OperIconstant6kM_l_: ad_sparc_clone.o; +text: .text%__1cbIjava_security_AccessControlContextGcreate6FnOobjArrayHandle_inGHandle_pnGThread__pnHoopDesc__; +text: .text%__1cOstackSlotIOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cVCallRuntimeDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerDset6MlpnMRegisterImpl_rknQRelocationHolder__v_: interpreter_sparc.o; +text: .text%__1cRCompilationPolicybIreset_counter_for_invocation_event6MnMmethodHandle__v_; +text: .text%__1cHCompileKinit_start6MpnJStartNode__v_; +text: .text%__1cRtestI_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_ThreadbGinherited_access_control_context6FpnHoopDesc__2_; +text: .text%__1cVCallRuntimeDirectNodeKmethod_set6Ml_v_; +text: .text%__1cOstackSlotIOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cIGraphKitbAgen_stub_or_native_wrapper6MpCpkcpnIciMethod_iiiii_v_; +text: .text%__1cIMinINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cVCallRuntimeDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_GetInheritedAccessControlContext; +text: .text%__1cOcmovLL_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVinline_cache_regPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cVCallRuntimeDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%JVM_NativePath; +text: .text%__1cPPerfDataManagerWcreate_string_constant6FnJCounterNS_pkc3pnGThread__pnSPerfStringConstant__; +text: .text%__1cQdivD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSvframeArrayElementDbci6kM_i_; +text: .text%__1cQaddD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNnmethodLocker2t6MpC_v_; +text: .text%__1cICodeBlobZis_at_poll_or_poll_return6MpC_i_; +text: .text%__1cODeoptimizationRlast_frame_adjust6Fii_i_; +text: .text%__1cNloadConP0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSconvI2D_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovPI_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddD_regD_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cODeoptimizationYfetch_unroll_info_helper6FpnKJavaThread__pn0ALUnrollBlock__; +text: .text%__1cLvframeArrayIallocate6FpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pnLRegisterMap_nFframe_9A9A9A_p0_; +text: .text%__1cSandL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cODeoptimizationNuncommon_trap6FpnKJavaThread_i_pn0ALUnrollBlock__; +text: .text%__1cODeoptimizationNunpack_frames6FpnKJavaThread_i_nJBasicType__; +text: .text%__1cLvframeArrayPunpack_to_stack6MrnFframe_i_v_; +text: .text%__1cODeoptimizationTuncommon_trap_inner6FpnKJavaThread_i_v_; +text: .text%__1cLConvF2INodeGOpcode6kM_i_; +text: .text%__1cLOptoRuntimeWresolve_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cSsubD_regD_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: deoptimization.o; +text: .text%__1cODeoptimizationScreate_vframeArray6FpnKJavaThread_nFframe_pnLRegisterMap__pnLvframeArray__; +text: .text%__1cRSignatureIteratorKparse_type6M_i_; +text: .text%__1cIUniverseWreinitialize_vtable_of6FpnFKlass_pnGThread__v_; +text: .text%__1cMtlsLoadPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cVMoveL2D_stack_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSCompiledStaticCallMset_to_clean6M_v_; +text: .text%__1cVCompressedWriteStreamKwrite_long6Mx_v_; +text: .text%__1cbCAbstractInterpreterGeneratorRset_unimplemented6Mi_v_; +text: .text%__1cNSharedRuntimebJcontinuation_for_implicit_exception6FpnKJavaThread_pCn0AVImplicitExceptionKind__3_; +text: .text%__1cLstoreI0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MipnMRegisterImpl__v_; +text: .text%__1cKstoreFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmulI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstkL_to_regD_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSMachCallNativeNodePret_addr_offset6M_i_; +text: .text%__1cKstoreFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvD2I_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_GetStaticMethodID: jni.o; +text: .text%__1cJloadDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSstkL_to_regD_0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRInlineCacheBufferUic_buffer_cached_oop6FpC_pnHoopDesc__; +text: .text%__1cRNativeMovConstRegEdata6kM_l_; +text: .text%__1cLconvI2BNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKConv2BNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmaxI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXjava_lang_boxing_objectJget_value6FpnHoopDesc_pnGjvalue__nJBasicType__; +text: .text%__1cIGraphKitTdprecision_rounding6MpnENode__2_; +text: .text%__1cNmaxI_eRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRConstantLongValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cOcmovIF_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_gtNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMulDNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cPorI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovPP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerKnull_check6MpnMRegisterImpl_i_v_; +text: .text%__1cENodeJis_MemBar6kM_pknKMemBarNode__: classes.o; +text: .text%__1cVcompiledICHolderKlassSoop_being_unloaded6MpnRBoolObjectClosure_pnHoopDesc__i_; +text: .text%__1cVcompiledICHolderKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cVcompiledICHolderKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cUregI_to_stkLHi_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZregDHi_regDLo_to_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovLL_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cbFunnecessary_membar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOloadI_fregNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cLconvI2BNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cbFunnecessary_membar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcmovII_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMnegD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvD2I_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConL0NodeFclone6kM_pnENode__; +text: .text%__1cLRuntimeStubQnew_runtime_stub6FpkcpnKCodeBuffer_ipnJOopMapSet_i_p0_; +text: .text%__1cKcmpOpFOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cJimmL0OperFclone6kM_pnIMachOper__; +text: .text%__1cSaddP_reg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cPMultiBranchDataScompute_cell_count6FpnOBytecodeStream__i_; +text: .text%__1cIAddFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPPerfDataManagerUcreate_long_constant6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongConstant__; +text: .text%JVM_Close; +text: .text%__1cMtlsLoadPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOstackSlotLOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cJloadFNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerbIcompute_extra_locals_size_in_bytes6MpnMRegisterImpl_22_v_; +text: .text%__1cQsubF_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPconvF2D_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOimmI_32_63OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cQdivL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerNget_vm_result6MpnMRegisterImpl__v_; +text: .text%__1cQregL_to_stkLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQaddL_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSxorI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSandL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshrL_reg_imm6NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKloadUBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: templateTable_sparc.o; +text: .text%__1cIMulFNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cLcastP2LNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKloadUBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLstoreF0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvD2I_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKcastPPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQstkI_to_regINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_reflect_FieldJmodifiers6FpnHoopDesc__i_; +text: .text%__1cMStartOSRNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cFParseWload_interpreter_state6MpnENode_2_v_; +text: .text%__1cPciInstanceKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cNCallGeneratorHfor_osr6FpnIciMethod_i_p0_; +text: .text%__1cQAbstractCompilerMsupports_osr6M_i_: c2compiler.o; +text: .text%__1cJloadCNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOMacroAssemblerPbreakpoint_trap6M_v_; +text: .text%__1cIciMethodVget_osr_flow_analysis6Mi_pnKciTypeFlow__; +text: .text%__1cSconvF2I_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%jni_NewObjectV: jni.o; +text: .text%__1cUregI_to_stkLHi_0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvD2F_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvI2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMStartOSRNodeKosr_domain6F_pknJTypeTuple__; +text: .text%__1cSmulD_regD_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSMachBreakpointNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSaddD_regD_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIDivLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQaddD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcmovLI_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_reflect_FieldFclazz6FpnHoopDesc__2_; +text: .text%__1cKNativeJumpUpatch_verified_entry6FpC11_v_; +text: .text%__1cLConvD2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKCompiledICMstub_address6kM_pC_; +text: .text%__1cIAddFNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%jni_EnsureLocalCapacity; +text: .text%__1cPfieldDescriptorUdouble_initial_value6kM_d_; +text: .text%__1cTunsafe_intrinsic_id6FpnNsymbolOopDesc_1_nNmethodOopDescLIntrinsicId__; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cHciKlassOsuper_of_depth6MI_p0_; +text: .text%__1cJSubFPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: methodDataOop.o; +text: .text%__1cFTypeFFxdual6kM_pknEType__; +text: .text%__1cLcastP2LNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPorL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvD2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNiRegIsafeOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNTemplateTableGbranch6Fii_v_; +text: .text%__1cNSafePointNodeQpeek_monitor_obj6kM_pnENode__; +text: .text%__1cISubDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIMulDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKScopeValueLis_location6kM_i_: debugInfo.o; +text: .text%__1cRshlI_reg_imm5NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQdivI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQshrI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovPP_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompile2t6MpnFciEnv_pF_pknITypeFunc_pCpkciiii_v_; +text: .text%__1cSmulL_reg_reg_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParsePdo_monitor_exit6M_v_; +text: .text%__1cNSafePointNodeQpeek_monitor_box6kM_pnENode__; +text: .text%__1cSaddI_reg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLcmpF_ccNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPstoreI_FregNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: interp_masm_sparc.o; +text: .text%__1cIGraphKitIgen_stub6MpCpkciii_v_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_taken_branch6MpnMRegisterImpl_2_v_; +text: .text%__1cOtailjmpIndNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQregL_to_stkLNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cXconvI2D_regDHi_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotLOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotLOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cSconvF2I_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadBNodeFclone6kM_pnENode__; +text: .text%__1cOloadI_fregNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQregL_to_stkLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJimmP0OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cNinstanceKlassPadd_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cLcmpF_ccNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2L_regNodeFclone6kM_pnENode__; +text: .text%__1cLResourceObj2n6FLn0APallocation_type__pv_; +text: .text%__1cLcmpD_ccNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interpreter_sparc.o; +text: .text%__1cFTypeDFxdual6kM_pknEType__; +text: .text%__1cLMoveL2DNodeGOpcode6kM_i_; +text: .text%__1cKarrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cRorI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: typeArrayKlass.o; +text: .text%__1cOcmovDF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableOpatch_bytecode6FnJBytecodesECode_pnMRegisterImpl_4i_v_; +text: .text%__1cINegDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSstkL_to_regD_2NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVMoveF2I_stack_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMachNodeOmemory_operand6kM_pknIMachOper__: ad_sparc_misc.o; +text: .text%__1cXconvI2D_regDHi_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosHSolarisKmmap_chunk6FpcLii_2_; +text: .text%__1cINegFNodeGOpcode6kM_i_; +text: .text%__1cICmpFNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQregL_to_stkLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcmovDF_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerbAincrement_backedge_counter6MpnMRegisterImpl_2_v_; +text: .text%__1cKCodeBufferVinsert_float_constant6Mf_pC_; +text: .text%__1cSstkL_to_regD_0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKEntryPoint2t6M_v_; +text: .text%__1cMregD_lowOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cYinternal_word_RelocationMforce_target6MpC_v_: relocInfo.o; +text: .text%__1cObranchConFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYinternal_word_RelocationGtarget6M_pC_; +text: .text%__1cHciKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cZInterpreterMacroAssemblerbFtest_invocation_counter_for_mdp6MpnMRegisterImpl_22rnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerbBtest_backedge_count_for_osr6MpnMRegisterImpl_22_v_; +text: .text%__1cObranchConFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadBNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMloadConFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovPP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovPI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIModLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMnegD_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cODeoptimizationbJupdate_method_data_from_interpreter6FnQmethodDataHandle_ii_v_; +text: .text%__1cTmembar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsbMis_supported_jni_version_including_1_16Fi_C_; +text: .text%__1cObranchConFNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cOcmovPI_regNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cNloadConPCNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMTailJumpNodeKmatch_edge6kMI_I_; +text: .text%__1cQsubD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cTmembar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQstkI_to_regINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLconvP2BNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cVMoveL2D_stack_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConFNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOcmovII_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegD_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSstkL_to_regD_2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFVTuneOdelete_nmethod6FpnHnmethod__v_; +text: .text%__1cKReflectionUarray_component_type6FpnHoopDesc_pnGThread__2_; +text: .text%__1cPconvD2F_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKJavaThreadbFdeoptimized_wrt_marked_nmethods6M_v_; +text: .text%__1cMVirtualSpaceJexpand_by6ML_i_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cOloadConL13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKVtableStubRpd_code_alignment6F_i_; +text: .text%__1cLConvI2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLconvI2BNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPorL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHBoxNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%JVM_GetComponentType; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: klassKlass.o; +text: .text%JVM_GetCPFieldModifiers; +text: .text%__1cXPartialSubtypeCheckNodeGOpcode6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerXindex_check_without_pop6MpnMRegisterImpl_2i22_v_; +text: .text%__1cVMoveF2I_stack_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJEventMark2t6MpkcE_v_: nmethod.o; +text: .text%__1cOcmovLI_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHnmethodSflush_dependencies6MpnRBoolObjectClosure__v_; +text: .text%__1cIDivDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cHnmethodFflush6M_v_; +text: .text%__1cQConstantIntValuePis_constant_int6kM_i_: debugInfo.o; +text: .text%__1cFStateL_sub_Op_Box6MpknENode__v_; +text: .text%__1cICodeBlobFflush6M_v_; +text: .text%Unsafe_DefineClass1; +text: .text%__1cPorL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstfSSFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQsubF_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvI2D_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovII_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvF2I_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKReflectionNinvoke_method6FpnHoopDesc_nGHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cSmulL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJStubQdDueueKremove_all6M_v_; +text: .text%__1cLOopMapCache2t6M_v_; +text: .text%__1cNloadRangeNodeFclone6kM_pnENode__; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: generateOopMap.o; +text: .text%__1cSaddL_reg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerLindex_check6MpnMRegisterImpl_2i22_v_; +text: .text%__1cKcastPPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerGif_cmp6MnJAssemblerJCondition_i_v_; +text: .text%__1cOcmovIF_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceRefKlass.o; +text: .text%__1cUregI_to_stkLHi_0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvI2D_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerPcasx_under_lock6MpnMRegisterImpl_22pCi_v_; +text: .text%__1cWImplicitExceptionTable2t6MpknHnmethod__v_; +text: .text%__1cJloadLNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvF2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cHJNIEnv_JNewObject6MpnH_jclass_pnK_jmethodID_E_pnI_jobject__: jni.o; +text: .text%__1cFKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cLstoreF0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubF_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%JVM_DefineClass; +text: .text%__1cNloadConL0NodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cHOrLNodeGOpcode6kM_i_; +text: .text%__1cWImplicitExceptionTableCat6kMI_I_; +text: .text%__1cNTemplateTableMlocals_index6FpnMRegisterImpl_i_v_; +text: .text%JVM_InvokeMethod; +text: .text%__1cLVtableStubsIcontains6FpC_i_; +text: .text%__1cHnmethodbJcontinuation_for_implicit_exception6MpC_1_; +text: .text%jni_NewDirectByteBuffer; +text: .text%__1cYjava_lang_reflect_MethodFclazz6FpnHoopDesc__2_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cQsubD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYjava_lang_reflect_MethodPparameter_types6FpnHoopDesc__2_; +text: .text%__1cQstkI_to_regINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cYjava_lang_reflect_MethodEslot6FpnHoopDesc__i_; +text: .text%__1cYjava_lang_reflect_MethodLreturn_type6FpnHoopDesc__2_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cOMacroAssemblerHbr_null6MpnMRegisterImpl_inJAssemblerHPredict_rnFLabel__v_; +text: .text%__1cKcastPPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeHeapJexpand_by6ML_i_; +text: .text%__1cHThreadsLgc_prologue6F_v_; +text: .text%__1cHThreadsLgc_epilogue6F_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlass.o; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_pkcnGHandle_6_6_; +text: .text%__1cNTemplateTableHconvert6F_v_; +text: .text%__1cOcmovDF_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMarkSweepXfollow_weak_klass_links6F_v_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_pnNsymbolOopDesc_pkc_nGHandle__; +text: .text%__1cKDictionaryYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cRCardTableModRefBSFclear6MnJMemRegion__v_; +text: .text%__1cRCardTableModRefBSEis_a6MnKBarrierSetEName__i_: cardTableExtension.o; +text: .text%__1cQSystemDictionaryMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cSaddL_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQSystemDictionaryValways_strong_oops_do6FpnKOopClosure__v_; +text: .text%__1cJMarkSweepNrestore_marks6F_v_; +text: .text%__1cKDictionaryMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cJMarkSweepMadjust_marks6F_v_; +text: .text%__1cHdom_lca6FpnFBlock_1_1_: gcm.o; +text: .text%__1cNloadRangeNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKcmpOpFOperKless_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cJPSPermGenKprecompact6M_v_; +text: .text%__1cJPSPermGenQcompute_new_size6ML_v_; +text: .text%__1cQUncommonTrapBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cNExceptionBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cIPSOldGenKprecompact6M_v_; +text: .text%__1cKstfSSFNodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cIUniverseWupdate_heap_info_at_gc6F_v_; +text: .text%__1cUPSMarkSweepDecoratorbIset_destination_decorator_perm_gen6F_v_; +text: .text%__1cUPSMarkSweepDecoratorbHset_destination_decorator_tenured6F_v_; +text: .text%__1cLPSMarkSweepQinvoke_no_policy6Fpii_v_; +text: .text%Unsafe_AllocateInstance; +text: .text%__1cUPSAdaptiveSizePolicyUmajor_collection_end6MLnHGCCauseFCause__v_; +text: .text%__1cUPSAdaptiveSizePolicyWmajor_collection_begin6M_v_; +text: .text%jni_AllocObject: jni.o; +text: .text%__1cIAddFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKConv2BNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFParseScan_rerun_bytecode6M_i_; +text: .text%JVM_NewArray; +text: .text%__1cLStrCompNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSDeoptimizationBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cVLoaderConstraintTableYpurge_loader_constraints6MpnRBoolObjectClosure__v_; +text: .text%__1cVLoaderConstraintTableYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cJCodeCacheLgc_prologue6F_v_; +text: .text%__1cLTypeInstPtrLmirror_type6kM_pnGciType__; +text: .text%__1cKPSYoungGenKprecompact6M_v_; +text: .text%__1cKPSYoungGenPadjust_pointers6M_v_; +text: .text%__1cKPSYoungGenHcompact6M_v_; +text: .text%__1cJCodeCacheMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cSReferenceProcessorPoops_do_statics6FpnKOopClosure__v_; +text: .text%__1cJCodeCacheHoops_do6FpnKOopClosure__v_; +text: .text%__1cZInterpreterMacroAssemblerFpop_l6MpnMRegisterImpl__v_; +text: .text%__1cKReflectionRreflect_new_array6FpnHoopDesc_ipnGThread__pnMarrayOopDesc__; +text: .text%__1cJCodeCacheLgc_epilogue6F_v_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_pnGThread__v_; +text: .text%__1cXjava_lang_reflect_FieldEslot6FpnHoopDesc__i_; +text: .text%__1cMloadConPNodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GC; +text: .text%JVM_GetSystemPackage; +text: .text%__1cUregI_to_stkLHi_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreC0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLPSMarkSweepGinvoke6Fpii_v_; +text: .text%__1cRSignatureIteratorHiterate6M_v_; +text: .text%__1cRStubCodeGeneratorLstub_epilog6MpnMStubCodeDesc__v_; +text: .text%__1cMStubCodeMark2t6MpnRStubCodeGenerator_pkc4_v_; +text: .text%__1cPfieldDescriptorTfloat_initial_value6kM_f_; +text: .text%__1cMStubCodeMark2T6M_v_; +text: .text%__1cUregI_to_stkLHi_0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJArgumentsQPropertyList_add6FppnOSystemProperty_2_v_; +text: .text%__1cSInterpreterRuntimeQcreate_exception6FpnKJavaThread_pc3_v_; +text: .text%__1cKoopFactoryUnew_compiledICHolder6FnMmethodHandle_nLKlassHandle_pnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cUParallelScavengeHeapHcollect6MnHGCCauseFCause__v_; +text: .text%__1cPconvD2F_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cETypeJis_finite6kM_i_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: callnode.o; +text: .text%__1cLClassLoaderSget_system_package6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cQaddL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cVVM_ParallelGCSystemGC2t6MI_v_; +text: .text%__1cQshlI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPBytecode_invokeLresult_type6kMpnGThread__nJBasicType__; +text: .text%__1cLMoveF2INodeGOpcode6kM_i_; +text: .text%__1cJCmpF3NodeGOpcode6kM_i_; +text: .text%__1cVVM_ParallelGCSystemGCEdoit6M_v_; +text: .text%__1cVcompiledICHolderKlassIallocate6MpnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cLConvD2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cVVM_ParallelGCSystemGCEname6kM_pkc_: vm_operations.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: objArrayKlass.o; +text: .text%__1cJBasicLockHmove_to6MpnHoopDesc_p0_v_; +text: .text%__1cOstackSlotIOperFclone6kM_pnIMachOper__; +text: .text%jni_GetStringRegion: jni.o; +text: .text%__1cNStubGeneratorLstub_prolog6MpnMStubCodeDesc__v_: stubGenerator_sparc.o; +text: .text%__1cOstackSlotPOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cHBoxNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: klassKlass.o; +text: .text%__1cOcmovPI_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackHdo_byte6M_v_: generateOopMap.o; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOstackSlotPOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cJCMoveNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOstackSlotPOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cLcastP2LNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cObox_handleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQregP_to_stkPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQshrL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHThreadsLnmethods_do6F_v_; +text: .text%__1cJCodeCacheFfirst6F_pnICodeBlob__; +text: .text%JVM_RawMonitorCreate; +text: .text%__1cLOptoRuntimeNgenerate_stub6FpnFciEnv_pF_pknITypeFunc_pCpkciiii_8_; +text: .text%__1cIimmDOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%Unsafe_CompareAndSwapInt; +text: .text%__1cWloadConI_x43300000NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotLOperFclone6kM_pnIMachOper__; +text: .text%__1cXconvI2D_regDHi_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOPSPromotionLABRunallocate_object6MpnHoopDesc__i_; +text: .text%__1cZInterpreterMacroAssemblerQtest_mdp_data_at6MipnMRegisterImpl_rnFLabel_2_v_; +text: .text%__1cXconvI2D_regDHi_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVMoveL2D_stack_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKcastPPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSaddD_regD_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotFOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cMnegF_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVMoveF2I_stack_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPorL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadFNodeFclone6kM_pnENode__; +text: .text%__1cUregI_to_stkLHi_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvD2F_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerFpop_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cSsubD_regD_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsubL_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cPMultiBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cQinstanceRefKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceRefKlass.o; +text: .text%__1cQregL_to_stkLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRmethodDataOopDescRbci_to_extra_data6Mii_pnLProfileData__; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: interpreter.o; +text: .text%__1cPconvF2D_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCodeBufferGresize6M_v_; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceRefKlass.o; +text: .text%__1cQaddL_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerNunlock_object6MpnMRegisterImpl__v_; +text: .text%__1cSaddD_regD_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWloadConI_x41f00000NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cLConvL2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cWloadConI_x43300000NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMnegF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmulD_regD_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_Sleep; +text: .text%__1cMmatch_option6FpknMJavaVMOption_ppkc5i_i_: arguments.o; +text: .text%__1cUregI_to_stkLHi_0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLconvI2BNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRorI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUregI_to_stkLHi_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_Lseek; +text: .text%__1cQmulD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOloadConL13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulFNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cJOopMapSetQsingular_oop_map6M_pnGOopMap__; +text: .text%__1cSmulD_regD_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: templateTable_sparc.o; +text: .text%__1cSsubD_regD_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: templateTable_sparc.o; +text: .text%__1cQsubF_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmembar_releaseNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cMloadConDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackIdo_float6M_v_: generateOopMap.o; +text: .text%__1cMloadConFNodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cRComputeEntryStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cWloadConI_x41f00000NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_: interpreterRT_sparc.o; +text: .text%__1cOLibraryCallKitXinline_string_compareTo6M_i_; +text: .text%Unsafe_GetNativeByte; +text: .text%__1cQmulL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLconvI2BNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerKbr_notnull6MpnMRegisterImpl_inJAssemblerHPredict_rnFLabel__v_; +text: .text%__1cQmulF_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsarL_reg_imm6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQstkI_to_regFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQaddF_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJLoadBNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cGciType2t6MnJBasicType__v_; +text: .text%__1cQandL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%jni_GetEnv; +text: .text%__1cHTypePtrKadd_offset6kMi_pk0_; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cOcmovLI_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSstring_compareNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMciArrayKlassRbase_element_type6M_pnGciType__; +text: .text%__1cSstring_compareNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerEcall6MpCnJrelocInfoJrelocType__v_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: stubGenerator_sparc.o; +text: .text%JVM_NanoTime; +text: .text%__1cOMacroAssemblerOrestore_thread6MkpnMRegisterImpl__v_; +text: .text%__1cIMulFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cETypeEmake6Fn0AFTYPES__pk0_; +text: .text%__1cPconvD2F_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerLsave_thread6MkpnMRegisterImpl__v_; +text: .text%__1cCosNjavaTimeNanos6F_x_; +text: .text%__1cOcmovIF_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLCastP2LNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%Unsafe_GetNativeFloat; +text: .text%__1cENodeHis_Call6M_pnICallNode__: machnode.o; +text: .text%jni_CallVoidMethod: jni.o; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_method_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cKimmU13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cNloadConL0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJimmL0OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cFParseOdo_tableswitch6M_v_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableXjvmti_post_field_access6Fiii_v_; +text: .text%__1cKstoreFNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cWloadConI_x41f00000NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWloadConI_x43300000NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKcastPPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFTypeFJis_finite6kM_i_; +text: .text%__1cOstackSlotFOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cRInlineCacheBufferSic_destination_for6FpnKCompiledIC__pC_; +text: .text%__1cOPhaseIdealLoopJclone_iff6MpnHPhiNode_pnNIdealLoopTree__pnIBoolNode__; +text: .text%__1cSmulL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParseQdo_monitor_enter6M_v_; +text: .text%__1cLconvP2BNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMregD_lowOperFclone6kM_pnIMachOper__; +text: .text%__1cSaddI_reg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cParrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cOMacroAssemblerGmembar6MnJAssemblerQMembar_mask_bits__v_: templateTable_sparc.o; +text: .text%__1cLcastP2LNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: methodLiveness.o; +text: .text%__1cbCAbstractInterpreterGeneratorbBgenerate_result_handler_for6MnJBasicType__pC_; +text: .text%__1cQshrI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerEfmov6MnRFloatRegisterImplFWidth_p13_v_; +text: .text%__1cOcmovLL_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSdivL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQstkI_to_regINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIXorINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cNTemplateTableEiop26Fn0AJOperation__v_; +text: .text%__1cLConvI2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQAbstractCompilerPsupports_native6M_i_: c2compiler.o; +text: .text%__1cIDivDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%JVM_FindPrimitiveClass; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constantPoolKlass.o; +text: .text%__1cOMacroAssemblerKget_thread6M_v_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: stubGenerator_sparc.o; +text: .text%__1cUdivL_reg_imm13_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUmulL_reg_imm13_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSsubL_reg_reg_2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKLoadPCNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOcmovIL_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_continuation_for6MnITosState__pC_; +text: .text%__1cVMoveL2D_stack_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIciSymbolHas_utf86M_pkc_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_safept_entry_for6MnITosState_pC_2_; +text: .text%__1cQregI_to_stkINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciMethodMnative_entry6M_pC_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cOstackSlotFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKCodeBufferQalloc_relocation6MI_v_; +text: .text%__1cPjava_lang_ClassYcreate_basic_type_mirror6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cZInterpreterMacroAssemblerMdispatch_via6MnITosState_ppC_v_; +text: .text%__1cNloadConPCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNinstanceKlassUfind_interface_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cLcastP2LNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLOptoRuntimeRnew_objArray_Type6F_pknITypeFunc__; +text: .text%__1cNloadConPCNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSmodL_reg_imm13NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod__v_; +text: .text%__1cJname2type6Fpkc_nJBasicType__; +text: .text%__1cIGraphKitSgen_native_wrapper6MpnIciMethod__v_; +text: .text%__1cKReturnNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cSmodL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQdivD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKcastPPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvF2I_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLconvI2BNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cWloadConI_x41f00000NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarI_reg_imm5NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosNcommit_memory6FpcLL_i_; +text: .text%__1cUregI_to_stkLHi_0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPPerfDataManagerWcreate_string_variable6FnJCounterNS_pkci3pnGThread__pnSPerfStringVariable__; +text: .text%__1cQciTypeArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cWloadConI_x41f00000NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cWloadConI_x43300000NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFTypeFFempty6kM_i_; +text: .text%__1cETypeFxdual6kM_pk0_; +text: .text%__1cZInterpreterMacroAssemblerbCincrement_invocation_counter6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerTnotify_method_entry6M_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: jniFastGetField_sparc.o; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: jniFastGetField_sparc.o; +text: .text%__1cOtailjmpIndNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerEcall6MpCnJrelocInfoJrelocType__v_: jniFastGetField_sparc.o; +text: .text%__1cZInterpreterMacroAssemblerSnotify_method_exit6MinITosState__v_; +text: .text%__1cFParseMdo_anewarray6M_v_; +text: .text%__1cPorL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvI2F_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCAbstractInterpreterGeneratorXbang_stack_shadow_pages6Mi_v_; +text: .text%__1cSaddD_regD_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: objArrayKlassKlass.o; +text: .text%__1cOcmovLI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%Unsafe_StaticFieldBaseFromField; +text: .text%__1cVMoveF2I_stack_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUInterpreterGeneratorVgenerate_counter_incr6MpnFLabel_22_v_; +text: .text%__1cPconvI2F_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUInterpreterGeneratorZgenerate_counter_overflow6MirnFLabel__v_; +text: .text%__1cQaddI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOtypeArrayKlassMcreate_klass6FnJBasicType_ipnGThread__pnMklassOopDesc__; +text: .text%__1cLstoreC0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUInterpreterGeneratorUgenerate_fixed_frame6Mi_v_; +text: .text%__1cKcmpOpFOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cSconvF2I_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvF2I_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvD2F_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: typeArrayKlass.o; +text: .text%__1cWloadConI_x43300000NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%Unsafe_EnsureClassInitialized; +text: .text%Unsafe_StaticFieldOffset; +text: .text%__1cOMacroAssemblerZtotal_frame_size_in_bytes6Mi_i_; +text: .text%__1cUregI_to_stkLHi_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cVMoveL2D_stack_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%Unsafe_GetObjectVolatile; +text: .text%__1cRshlL_reg_imm6NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadDNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRshlI_reg_imm5NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvD2F_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVMoveF2I_stack_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOtypeArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlass.o; +text: .text%__1cPconvI2F_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateO_sub_Op_CMoveP6MpknENode__v_; +text: .text%__1cNTemplateTableQfast_accessfield6FnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerQaccess_local_int6MpnMRegisterImpl_2_v_; +text: .text%__1cOcmovIF_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJimmU5OperFclone6kM_pnIMachOper__; +text: .text%__1cZInterpreterMacroAssemblerNsuper_call_VM6MpnMRegisterImpl_22pC22i_v_; +text: .text%__1cSmulL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cObranchConFNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cLstoreC0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_GetClassContext; +text: .text%__1cZInterpreterMacroAssemblerWempty_expression_stack6M_v_; +text: .text%__1cJloadLNodeFclone6kM_pnENode__; +text: .text%__1cHMulNodeGis_Mul6kM_pk0_: classes.o; +text: .text%__1cMnegD_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQandI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulD_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerbDunlock_if_synchronized_method6MnITosState_ii_v_; +text: .text%__1cNTemplateTableZjvmti_post_fast_field_mod6F_v_; +text: .text%__1cSstkL_to_regD_1NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadLNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cOcmovIF_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTableXresolve_cache_and_index6FipnMRegisterImpl_2_v_; +text: .text%__1cUInterpreterGeneratorbAgenerate_run_compiled_code6M_v_; +text: .text%__1cSsubD_regD_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulD_regD_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTablePfast_storefield6FnITosState__v_; +text: .text%__1cSaddL_reg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cPciObjArrayKlassJmake_impl6FpnHciKlass__p0_; +text: .text%JVM_IsSameClassPackage; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cRComputeEntryStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cGEventsDlog6FpkcE_v_: compiledIC.o; +text: .text%jni_SetStaticObjectField: jni.o; +text: .text%__1cRComputeEntryStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cCosHSolarisSset_signal_handler6Fiii_v_; +text: .text%__1cLVtableStubsOis_entry_point6FpC_i_; +text: .text%__1cKCompiledICOis_megamorphic6kM_i_; +text: .text%__1cKVtableStub2n6FLi_pv_; +text: .text%__1cLVtableStubsLcreate_stub6FiipnNmethodOopDesc__pC_; +text: .text%__1cNSharedRuntimeVhandle_ic_miss_helper6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cKCompiledICSset_to_megamorphic6MpnICallInfo_nJBytecodesECode_pnGThread__v_; +text: .text%__1cKcmpOpFOperHgreater6kM_i_: ad_sparc_clone.o; +text: .text%__1cQregF_to_stkINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNMemoryManagerIadd_pool6MpnKMemoryPool__v_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: callnode.o; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2T6M_v_; +text: .text%__1cLVtableStubsScreate_vtable_stub6Fii_pnKVtableStub__; +text: .text%__1cLOptoRuntimeRresolve_call_Type6F_pknITypeFunc__; +text: .text%__1cQshlL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovLI_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPorL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosMuser_handler6F_pv_; +text: .text%__1cKMemoryPoolLadd_manager6MpnNMemoryManager__v_; +text: .text%__1cNTemplateTableGiconst6Fi_v_; +text: .text%__1cXPartialSubtypeCheckNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cCosEstat6FpkcpnEstat__i_; +text: .text%__1cKReflectionVis_same_class_package6FpnMklassOopDesc_2_i_; +text: .text%__1cLOptoRuntimeMrethrow_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimebBhandle_wrong_method_ic_miss6FpnKJavaThread__pC_; +text: .text%jni_RegisterNatives: jni.o; +text: .text%__1cLconvP2BNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassDeclaredFields; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2t6M_v_; +text: .text%__1cVMoveL2D_stack_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPPerfDataManagerKname_space6Fpkci_pc_; +text: .text%__1cOcmovLL_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUInterpreterGeneratorbCgenerate_check_compiled_code6MrnFLabel__v_; +text: .text%__1cNSharedRuntimeEdrem6Fdd_d_; +text: .text%__1cUInterpreterGeneratorbEgenerate_asm_interpreter_entry6Mi_pC_; +text: .text%__1cHCompileRget_Method_invoke6M_pnIciMethod__; +text: .text%__1cLTypeInstPtrOxmeet_unloaded6kMpk0_2_; +text: .text%__1cHCompileWget_MethodAccessorImpl6M_pnPciInstanceKlass__; +text: .text%__1cSstkL_to_regD_2NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cHciKlass2t6MnLKlassHandle_pnIciSymbol__v_; +text: .text%__1cSstkL_to_regD_1NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cUInterpreterGeneratorbDgenerate_stack_overflow_check6MpnMRegisterImpl_22_v_; +text: .text%__1cLConvL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFParseVcatch_call_exceptions6MrnYciExceptionHandlerStream__v_; +text: .text%__1cODeoptimizationLUnrollBlockOsize_of_frames6kM_i_; +text: .text%__1cMMonitorValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cOLibraryCallKitbBinline_native_currentThread6M_i_; +text: .text%__1cQdivL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOloadConL13NodeFclone6kM_pnENode__; +text: .text%__1cCosZvm_allocation_granularity6F_i_; +text: .text%__1cCosNcommit_memory6FpcL_i_; +text: .text%__1cOPSVirtualSpaceJexpand_by6ML_i_; +text: .text%__1cNRegisterSaverWrestore_live_registers6FpnOMacroAssembler__v_; +text: .text%__1cKJavaThreadUremove_monitor_chunk6MpnMMonitorChunk__v_; +text: .text%__1cKJavaThreadRadd_monitor_chunk6MpnMMonitorChunk__v_; +text: .text%__1cPconvF2D_regNodeFclone6kM_pnENode__; +text: .text%__1cOLibraryCallKitXgenerate_current_thread6MrpnENode__2_; +text: .text%__1cLcastP2LNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovII_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRMachSpillCopyNodeHsize_of6kM_I_: ad_sparc.o; +text: .text%__1cKstfSSFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_0NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDret6Mi_v_: templateTable_sparc.o; +text: .text%__1cNTemplateTableHif_icmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_0cmp6Fn0AJCondition__v_; +text: .text%__1cQstkI_to_regINodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cJMemRegion2t6M_v_: cardTableModRefBS.o; +text: .text%__1cZInterpreterMacroAssemblerSget_cpool_and_tags6MpnMRegisterImpl_2_v_; +text: .text%__1cMOopMapStream2t6MpnGOopMap_i_v_; +text: .text%__1cOLibraryCallKitbDis_method_invoke_or_aux_frame6MpnIJVMState__i_; +text: .text%__1cZInterpreterMacroAssemblerYtest_method_data_pointer6MrnFLabel__v_; +text: .text%__1cNTemplateTableH_return6FnITosState__v_; +text: .text%__1cKimmP13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cULinearLeastSquareFit2t6MI_v_; +text: .text%__1cLcastP2LNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframebLprevious_monitor_in_interpreter_frame6kMpnPBasicObjectLock__2_; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cPstoreI_FregNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRtestI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvF2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLConvD2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cHOrLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIAddDNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cMMonitorChunk2t6Mi_v_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPPerfLongVariant2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_pnUPerfLongSampleHelper__v_; +text: .text%jni_GetJavaVM; +text: .text%jni_MonitorExit: jni.o; +text: .text%jni_MonitorEnter: jni.o; +text: .text%__1cIAddDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOloadI_fregNodeOmemory_operand6kM_pknIMachOper__; +text: .text%Unsafe_ObjectFieldOffset; +text: .text%__1cSandL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSObjectSynchronizerIjni_exit6FpnHoopDesc_pnGThread__v_; +text: .text%JVM_IsSupportedJNIVersion; +text: .text%__1cMTailJumpNodeGOpcode6kM_i_; +text: .text%__1cNLoadKlassNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cCosOreserve_memory6FLpc_1_; +text: .text%__1cSObjectSynchronizerJjni_enter6FnGHandle_pnGThread__v_; +text: .text%__1cOloadConL13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_LoadLibrary; +text: .text%__1cXNativeSignatureIteratorJdo_double6M_v_: interpreterRT_sparc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_double6M_v_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_int_field06FnJBasicType__pC_; +text: .text%__1cISubFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cISubDNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cSdivL_reg_reg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSconvD2I_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTablebAload_invoke_cp_cache_entry6FipnMRegisterImpl_22ii_v_; +text: .text%__1cNRegisterSaverTsave_live_registers6FpnOMacroAssembler_ipi_pnGOopMap__; +text: .text%__1cNSpaceCounters2t6MpkciLpnMMutableSpace_pnSGenerationCounters__v_; +text: .text%__1cKExceptionsK_throw_oop6FpnGThread_pkcipnHoopDesc__v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkc2i_pc_; +text: .text%__1cSmulL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%bootstrap_flush_windows; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnQPerfLongVariable__; +text: .text%__1cRCardTableModRefBSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cMNativeLookupTbase_library_lookup6Fpkc22_pC_; +text: .text%__1cSCardTableExtensionVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cSmulL_reg_reg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cISubDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMacroAssemblerEfneg6MnRFloatRegisterImplFWidth_p13_v_; +text: .text%__1cPstoreI_FregNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_GetLastErrorString; +text: .text%__1cLcmpF_ccNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNReservedSpaceKfirst_part6MLii_0_; +text: .text%__1cFParseRjump_if_true_fork6MpnGIfNode_ii_v_; +text: .text%__1cOcmovLI_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNReservedSpace2t6ML_v_; +text: .text%__1cNTemplateTableElop26Fn0AJOperation__v_; +text: .text%__1cFParsePdo_lookupswitch6M_v_; +text: .text%__1cNStubGeneratorYgenerate_throw_exception6MpkcpCi_3_: stubGenerator_sparc.o; +text: .text%__1cLMoveL2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cHThreadsDadd6FpnKJavaThread_i_v_; +text: .text%__1cRtestI_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerbCverify_oop_or_return_address6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: jniFastGetField_sparc.o; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC_v_; +text: .text%__1cINegFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKMemoryPool2t6Mpkcn0AIPoolType_LLii_v_; +text: .text%__1cNTemplateTableEfop26Fn0AJOperation__v_; +text: .text%__1cNTemplateTableEdop26Fn0AJOperation__v_; +text: .text%__1cSsubL_reg_reg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_SetObjectField: jni.o; +text: .text%__1cPconvD2I_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLconvP2BNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKcastPPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmodL_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLconvP2BNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_ThreadKset_daemon6FpnHoopDesc__v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pCi_v_; +text: .text%__1cMnegD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTGeneratePairingInfoOreport_results6kM_i_: ciMethod.o; +text: .text%__1cPciInstanceKlassbDcompute_shared_is_initialized6M_i_; +text: .text%__1cSandI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQmodL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQmulD_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPstoreI_FregNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_Throw: jni.o; +text: .text%__1cOloadI_fregNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadFNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSconvD2I_helperNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOMacroAssemblerEcall6MpCnJrelocInfoJrelocType__v_: runtime_sparc.o; +text: .text%__1cPconvD2I_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%Unsafe_AllocateMemory; +text: .text%__1cOLibraryCallKitbNinline_native_Reflection_getCallerClass6M_i_; +text: .text%__1cNTemplateTableGlstore6Fi_v_; +text: .text%__1cUcompI_iReg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerJfloat_cmp6MiipnRFloatRegisterImpl_2pnMRegisterImpl__v_; +text: .text%__1cNTemplateTableGfstore6Fi_v_; +text: .text%__1cSstkL_to_regD_1NodeFclone6kM_pnENode__; +text: .text%__1cNTemplateTableGdstore6Fi_v_; +text: .text%__1cJcmpOpOperFclone6kM_pnIMachOper__; +text: .text%__1cMindirectOperFclone6kM_pnIMachOper__; +text: .text%__1cOLibraryCallKitZinline_native_Class_query6MnIciMethodLIntrinsicId__i_; +text: .text%__1cNTemplateTableGastore6Fi_v_; +text: .text%__1cNTemplateTableZload_field_cp_cache_entry6FipnMRegisterImpl_22i_v_; +text: .text%__1cRcompL_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableFaload6Fi_v_; +text: .text%__1cNTemplateTableGistore6Fi_v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC2_v_; +text: .text%__1cKstfSSFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableFdload6Fi_v_; +text: .text%__1cZInterpreterMacroAssemblerRprofile_checkcast6MipnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableFfload6Fi_v_; +text: .text%__1cOcmovDF_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLI_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableFlload6Fi_v_; +text: .text%__1cZInterpreterMacroAssemblerQaccess_local_ptr6MpnMRegisterImpl_2_v_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cZInterpreterMacroAssemblerOthrow_if_not_x6MnJAssemblerJCondition_pCpnMRegisterImpl__v_; +text: .text%__1cMnegF_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cKg1RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cIRetTableUfind_jsrs_for_target6Mi_pnNRetTableEntry__; +text: .text%__1cIRetTableHadd_jsr6Mii_v_; +text: .text%__1cSstkL_to_regD_2NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cJimmU6OperFclone6kM_pnIMachOper__; +text: .text%__1cNinstanceKlassSremove_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cSmulL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIimmFOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cOcmovLL_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDret6Mi_v_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: stubGenerator_sparc.o; +text: .text%__1cISubDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKstfSSFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNTemplateTableJfloat_cmp6Fii_v_; +text: .text%__1cKo2RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cIDivDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceRefKlass.o; +text: .text%__1cIDivFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSconvD2I_helperNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIDivINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQregF_to_stkINodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cNflagsRegLOperFclone6kM_pnIMachOper__; +text: .text%__1cNflagsRegFOperFclone6kM_pnIMachOper__; +text: .text%__1cTjava_lang_ThrowableLset_message6FpnHoopDesc_2_v_; +text: .text%__1cINegDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLcmpD_ccNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%Unsafe_SetMemory; +text: .text%__1cRshlI_reg_imm5NodeFclone6kM_pnENode__; +text: .text%__1cRshlL_reg_imm6NodeFclone6kM_pnENode__; +text: .text%__1cSconvI2D_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsarI_reg_imm5NodeFclone6kM_pnENode__; +text: .text%__1cQsubI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQmulD_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cbCAbstractInterpreterGeneratorbHgenerate_exception_handler_common6Mpkc2i_pC_; +text: .text%__1cLConvD2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOcmovPI_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_2_v_; +text: .text%__1cIAddDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cKstfSSFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFTypeFGis_nan6kM_i_; +text: .text%__1cOGenerateOopMapTret_jump_targets_do6MpnOBytecodeStream_pFp0ipi_vi4_v_; +text: .text%__1cOClassPathEntry2t6M_v_; +text: .text%__1cLClassLoaderXcreate_class_path_entry6FpcnEstat_ppnOClassPathEntry__v_; +text: .text%__1cPconvD2F_regNodeFclone6kM_pnENode__; +text: .text%__1cQandI_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cNgen_new_frame6FpnOMacroAssembler_i_v_: runtime_sparc.o; +text: .text%__1cVMoveF2I_stack_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSInterpreterRuntimeMat_safepoint6FpnKJavaThread__v_; +text: .text%__1cRsubI_zero_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cMnegD_regNodeFclone6kM_pnENode__; +text: .text%__1cPorL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableFiload6Fi_v_; +text: .text%__1cSstkL_to_regD_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQaddI_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cMloadConFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConDNodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cLconvP2BNodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_CallStaticObjectMethod: jni.o; +text: .text%__1cCosGsignal6Fipv_1_; +text: .text%__1cLstoreF0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvL2I_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cINegDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC22_v_; +text: .text%__1cLstoreF0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcmovII_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveF2I_stack_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: subnode.o; +text: .text%__1cQmulI_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cKstfSSFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCommandLineFlagsExKis_default6FnPCommandLineFlag__i_; +text: .text%__1cJArgumentsMadd_property6Fpkc_i_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: symbolKlass.o; +text: .text%__1cRshrI_reg_imm5NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLstoreF0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJMemRegionFminus6kMk0_0_; +text: .text%__1cQsubD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRComputeEntryStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cQsubF_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_22_v_; +text: .text%__1cLOptoRuntimeVgenerate_handler_blob6FpCi_pnNSafepointBlob__; +text: .text%__1cQOopMapCacheEntryPfill_for_native6M_v_; +text: .text%__1cJArgumentsMbuild_string6Fppcpkc_v_; +text: .text%__1cOMacroAssemblerFsethi6MrnHAddress_i_v_: runtime_sparc.o; +text: .text%__1cNFingerprinterLfingerprint6M_L_: oopMapCache.o; +text: .text%__1cRComputeEntryStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cSObjectSynchronizerHinflate6FpnHoopDesc__pnNObjectMonitor__; +text: .text%__1cMOopTaskQdDueueKinitialize6M_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC2_v_; +text: .text%__1cLOptoRuntimeTmultianewarray_Type6Fi_pknITypeFunc__; +text: .text%__1cQshrL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: symbolKlass.o; +text: .text%__1cTMaskFillerForNative2t6MnMmethodHandle_pLi_v_: oopMapCache.o; +text: .text%__1cNTemplateTableGfconst6Fi_v_; +text: .text%__1cMOopTaskQdDueue2t6M_v_; +text: .text%__1cCosGgetenv6Fpkcpci_i_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: gcTaskThread.o; +text: .text%__1cSCommandLineFlagsExJboolAtPut6FnXCommandLineFlagWithType_i_v_; +text: .text%__1cOGenerateOopMapRdo_multianewarray6Mii_v_; +text: .text%JVM_FindSignal; +text: .text%__1cOcmovIF_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKConv2BNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOcmovPI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovDF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosFyield6F_v_; +text: .text%__1cOtailjmpIndNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOtailjmpIndNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFTypeDFempty6kM_i_; +text: .text%__1cKstfSSFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseRdo_multianewarray6M_v_; +text: .text%__1cSInterpreterRuntimebKthrow_ArrayIndexOutOfBoundsException6FpnKJavaThread_pci_v_; +text: .text%__1cXNativeSignatureIteratorIdo_float6M_v_: interpreterRT_sparc.o; +text: .text%__1cOPSVirtualSpace2t6MnNReservedSpace_L_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorKpass_float6M_v_; +text: .text%__1cOCompilerThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cOtailjmpIndNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cKScopeValuePis_constant_int6kM_i_: debugInfo.o; +text: .text%__1cZInterpreterMacroAssemblerQtop_most_monitor6M_nHAddress__; +text: .text%__1cODeoptimizationLUnrollBlock2T6M_v_; +text: .text%JVM_GetInterfaceVersion; +text: .text%__1cNReservedSpaceJlast_part6ML_0_; +text: .text%__1cKCMoveDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cVMoveF2I_stack_regNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%jni_CallStaticObjectMethodV: jni.o; +text: .text%__1cPconvI2D_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSafepointBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cPciObjectFactoryTget_unloaded_method6MpnPciInstanceKlass_pnIciSymbol_4_pnIciMethod__; +text: .text%__1cVMoveL2D_stack_regNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cQregL_to_stkLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNciMethodKlassEmake6F_p0_; +text: .text%__1cIciObjectMis_classless6kM_i_: ciMethod.o; +text: .text%__1cKloadUBNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIciMethod2t6MpnPciInstanceKlass_pnIciSymbol_4_v_; +text: .text%__1cQciByteCodeStreamXget_method_holder_index6M_i_; +text: .text%__1cHciKlassIis_klass6M_i_: ciInstanceKlass.o; +text: .text%jni_GetDoubleArrayRegion: jni.o; +text: .text%__1cLconvP2BNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQCompilerCounters2t6MpkcipnGThread__v_; +text: .text%__1cNCompileBrokerUmake_compiler_thread6FpkcpnMCompileQdDueue_pnQCompilerCounters_pnGThread__pnOCompilerThread__; +text: .text%__1cUPSGenerationCounters2t6MpkciipnOPSVirtualSpace__v_; +text: .text%__1cKo2RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cPstoreI_FregNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPconvI2D_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOCompilerThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%__1cZInterpreterMacroAssemblerWprofile_switch_default6MpnMRegisterImpl__v_; +text: .text%__1cFStateO_sub_Op_Conv2B6MpknENode__v_; +text: .text%JVM_Available; +text: .text%__1cZInterpreterMacroAssemblerSprofile_final_call6MpnMRegisterImpl__v_; +text: .text%jio_vsnprintf; +text: .text%__1cISubFNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_i2_v_; +text: .text%__1cISubFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_i22_v_; +text: .text%__1cGThreadbArecord_stack_base_and_size6M_v_; +text: .text%__1cZInterpreterMacroAssemblerLlock_object6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerRget_constant_pool6MpnMRegisterImpl__v_; +text: .text%__1cGThreadbFinitialize_thread_local_storage6M_v_; +text: .text%__1cNMemoryManager2t6M_v_; +text: .text%__1cIDivDNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIDivDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerRgen_subtype_check6MpnMRegisterImpl_2222rnFLabel__v_; +text: .text%__1cMLinkResolverbBlookup_method_in_interfaces6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cMVM_OperationNdoit_prologue6M_i_: vm_operations.o; +text: .text%__1cNTemplateTableMfast_xaccess6FnITosState__v_; +text: .text%JVM_RegisterSignal; +text: .text%__1cCosHSolarisOis_sig_ignored6Fi_i_; +text: .text%__1cNCompileBrokerUcompiler_thread_loop6F_v_; +text: .text%__1cOCompilerThread2t6MpnMCompileQdDueue_pnQCompilerCounters__v_; +text: .text%__1cFStateO_sub_Op_CMoveD6MpknENode__v_; +text: .text%__1cQshlL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cVcompiler_thread_entry6FpnKJavaThread_pnGThread__v_: thread.o; +text: .text%__1cZInterpreterMacroAssemblerUadd_monitor_to_stack6MipnMRegisterImpl_2_v_; +text: .text%__1cLMoveF2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cZInterpreterMacroAssemblerTprofile_switch_case6MpnMRegisterImpl_222_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: universe.o; +text: .text%__1cMVirtualSpaceKinitialize6MnNReservedSpace_L_i_; +text: .text%__1cMVirtualSpace2t6M_v_; +text: .text%__1cOcmovLL_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRNativeInstructionPis_ic_miss_trap6M_i_; +text: .text%Unsafe_CompareAndSwapObject; +text: .text%__1cbAconvL2D_reg_slow_fxtofNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMVM_OperationNdoit_epilogue6M_v_: vm_operations.o; +text: .text%__1cMGCTaskThread2t6MpnNGCTaskManager_II_v_; +text: .text%__1cMGCTaskThreadDrun6M_v_; +text: .text%__1cJArgumentsXPropertyList_unique_add6FppnOSystemProperty_pkcpc_v_; +text: .text%__1cTAbstract_VM_VersionOvm_info_string6F_pkc_; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: arrayKlassKlass.o; +text: .text%__1cRNativeInstructionKis_illegal6M_i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cMGCTaskThreadFstart6M_v_; +text: .text%__1cUConstantOopReadValuePis_constant_oop6kM_i_: debugInfo.o; +text: .text%__1cLConvD2FNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJArgumentsVset_parallel_gc_flags6F_v_; +text: .text%__1cWloadConI_x43300000NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cINegFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cWloadConI_x41f00000NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSconvF2I_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cISubFNodeDsub6kMpknEType_3_3_; +text: .text%__1cZregDHi_regDLo_to_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXconvI2D_regDHi_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSaddD_regD_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSsubD_regD_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvD2F_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNVM_DeoptimizeEdoit6M_v_; +text: .text%__1cHMatcherQconvL2FSupported6F_ki_; +text: .text%__1cSmulD_regD_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvF2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLconvI2BNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNVM_DeoptimizeEname6kM_pkc_: vm_operations.o; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cLConvL2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTableHif_acmp6Fn0AJCondition__v_; +text: .text%__1cPOopTaskQdDueueSetOregister_queue6MipnMOopTaskQdDueue__v_; +text: .text%__1cHThreadsbFdeoptimized_wrt_marked_nmethods6F_v_; +text: .text%__1cPGCMemoryManagerXinitialize_gc_stat_info6M_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC22_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fi_v_; +text: .text%__1cNTemplateTableKdouble_cmp6Fi_v_; +text: .text%__1cNTemplateTableKinitialize6F_v_; +text: .text%__1cNTemplateTableGlconst6Fi_v_; +text: .text%__1cODeoptimizationVdeoptimize_dependents6F_i_; +text: .text%__1cNTemplateTableGdconst6Fi_v_; +text: .text%__1cNTemplateTableDldc6Fi_v_; +text: .text%__1cQObjectStartArrayKinitialize6MnJMemRegion__v_; +text: .text%__1cFStateO_sub_Op_CMoveL6MpknENode__v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: assembler_sparc.o; +text: .text%__1cNTemplateTableSgetfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableKif_nullcmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableUjvmti_post_field_mod6Fii_v_; +text: .text%__1cNTemplateTableSputfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableUgenerate_vtable_call6FpnMRegisterImpl_22_v_; +text: .text%__1cNTemplateTableTinvokevfinal_helper6FpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerUcalc_mem_param_words6MpnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableEidiv6F_v_; +text: .text%__1cLNamedThread2t6M_v_; +text: .text%__1cLNamedThreadIset_name6MpkcE_v_; +text: .text%__1cPGCMemoryManager2t6M_v_; +text: .text%__1cNTemplateTableHcastore6F_v_; +text: .text%__1cQObjectStartArraySset_covered_region6MnJMemRegion__v_; +text: .text%__1cFStateM_sub_Op_DivI6MpknENode__v_; +text: .text%__1cQdivI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVcompiledICHolderKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQdivD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObjectOis_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cRCollectorCounters2t6Mpkci_v_; +text: .text%__1cJCodeCachebGmake_marked_nmethods_not_entrant6F_v_; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstanceKlass.o; +text: .text%__1cPconvF2I_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUregI_to_stkLHi_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUregI_to_stkLHi_0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstkL_to_regD_2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstkL_to_regD_0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cLcastP2LNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMPeriodicTaskGenroll6M_v_; +text: .text%__1cKCastPPNodeJideal_reg6kM_I_: connode.o; +text: .text%__1cLMoveF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMPeriodicTask2t6ML_v_; +text: .text%__1cLMoveL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cUGcThreadCountClosureJdo_thread6MpnGThread__v_; +text: .text%__1cRcheck_if_clipping6FpknKRegionNode_rpnGIfNode_5_i_: cfgnode.o; +text: .text%__1cFTypeDJis_finite6kM_i_; +text: .text%__1cLConvF2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLConvF2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLConvD2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOcmovIF_immNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovIF_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovIF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: frame.o; +text: .text%__1cLcmpD_ccNodeFclone6kM_pnENode__; +text: .text%__1cUInterpreterGeneratorVgenerate_native_entry6Mi_pC_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_float_field06FnJBasicType__pC_; +text: .text%__1cRcompL_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cUcompI_iReg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cKJNIHandlesQmake_weak_global6FnGHandle__pnI_jobject__; +text: .text%__1cPconvL2I_regNodeFclone6kM_pnENode__; +text: .text%__1cFParseDl2f6M_v_; +text: .text%__1cCosPuncommit_memory6FpcL_i_; +text: .text%__1cRComputeEntryStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cKcmpOpFOperFclone6kM_pnIMachOper__; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_flag_at6MipnMRegisterImpl_2_v_; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: oopMapCache.o; +text: .text%__1cSconvD2I_helperNodeFclone6kM_pnENode__; +text: .text%__1cSstkL_to_regD_2NodeFclone6kM_pnENode__; +text: .text%__1cQregF_to_stkINodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerSupdate_mdp_for_ret6MnITosState_pnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerMprofile_call6MpnMRegisterImpl__v_; +text: .text%__1cOPSVirtualSpaceKinitialize6MnNReservedSpace_L_i_; +text: .text%__1cNloadConP0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTMaskFillerForNativeJpass_long6M_v_: oopMapCache.o; +text: .text%__1cOGenerateOopMapTadd_to_ref_init_set6Mi_v_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_virtual_call6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerLprofile_ret6MnITosState_pnMRegisterImpl_3_v_; +text: .text%__1cCosIjvm_path6Fpci_v_; +text: .text%__1cOcmovLL_regNodeFclone6kM_pnENode__; +text: .text%__1cKi0RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cZInterpreterMacroAssemblerbAdispatch_next_noverify_oop6MnITosState_i_v_; +text: .text%__1cJimmI0OperFclone6kM_pnIMachOper__; +text: .text%__1cTis_directory_secure6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cPfilename_to_pid6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cSconvF2I_helperNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPmake_new_frames6FpnOMacroAssembler_i_v_: runtime_sparc.o; +text: .text%__1cLconvI2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%jni_NewWeakGlobalRef: jni.o; +text: .text%__1cSestimate_path_freq6FpnENode__f_: loopnode.o; +text: .text%__1cIPSOldGenPinitialize_work6Mpkci_v_; +text: .text%__1cOstackSlotFOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cUInterpreterGeneratorLlock_method6M_v_; +text: .text%__1cKcmpOpFOperJnot_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cPconvF2I_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOLibraryCallKitVinline_fp_conversions6MnIciMethodLIntrinsicId__i_; +text: .text%__1cCosHrealloc6FpvL_1_; +text: .text%__1cZInterpreterMacroAssemblerQthrow_if_not_1_x6MnJAssemblerJCondition_rnFLabel__v_; +text: .text%__1cOtailjmpIndNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerQthrow_if_not_icc6MnJAssemblerJCondition_pCpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerQthrow_if_not_xcc6MnJAssemblerJCondition_pCpnMRegisterImpl__v_; +text: .text%__1cbAconvL2D_reg_slow_fxtofNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLOptoRuntimeIl2f_Type6F_pknITypeFunc__; +text: .text%__1cOstackSlotFOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotFOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOPSVirtualSpace2t6M_v_; +text: .text%__1cLklassVtableQindex_of_miranda6MpnNsymbolOopDesc_2_i_; +text: .text%__1cZInterpreterMacroAssemblerQstore_local_long6MpnMRegisterImpl_2_v_; +text: .text%__1cTMaskFillerForNativeLpass_object6M_v_: oopMapCache.o; +text: .text%__1cZInterpreterMacroAssemblerRstore_local_float6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerSstore_local_double6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cRsubI_zero_regNodeFclone6kM_pnENode__; +text: .text%__1cWStubGenerator_generate6FpnKCodeBuffer_i_v_; +text: .text%__1cJStubQdDueue2t6MpnNStubInterface_ipnFMutex_pkc_v_; +text: .text%jni_CallStaticBooleanMethod: jni.o; +text: .text%__1cJAssemblerCbp6Mn0AJCondition_in0ACCC_n0AHPredict_rnFLabel__v_: interp_masm_sparc.o; +text: .text%jni_IsInstanceOf: jni.o; +text: .text%__1cCosbCis_thread_cpu_time_supported6F_i_; +text: .text%__1cTAbstractInterpreterKinitialize6F_v_; +text: .text%__1cMnegF_regNodeFclone6kM_pnENode__; +text: .text%__1cCosWactive_processor_count6F_i_; +text: .text%__1cSReferenceProcessor2t6MnJMemRegion_iii_v_; +text: .text%__1cNStubGenerator2t6MpnKCodeBuffer_i_v_: stubGenerator_sparc.o; +text: .text%__1cZInterpreterMacroAssemblerRaccess_local_long6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerZget_4_byte_integer_at_bcp6MipnMRegisterImpl_2n0AKsetCCOrNot__v_; +text: .text%__1cNPhaseRegAllocHset_oop6MpknENode_i_v_; +text: .text%__1cGatomll6Fpkcpx_i_: arguments.o; +text: .text%__1cOcmovDF_regNodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerSaccess_local_float6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cOcmovLI_regNodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerTaccess_local_double6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerPstore_local_int6MpnMRegisterImpl_2_v_; +text: .text%jni_CallStaticVoidMethodV: jni.o; +text: .text%__1cOloadConL13NodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubI_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerPstore_local_ptr6MpnMRegisterImpl_2_v_; +text: .text%__1cHRetDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compileBroker.o; +text: .text%__1cVcompiledICHolderKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlass.o; +text: .text%__1cLPSMarkSweepKinitialize6F_v_; +text: .text%__1cNCompileBrokerQset_should_block6F_v_; +text: .text%__1cHRetDataJfixup_ret6MinQmethodDataHandle__pC_; +text: .text%__1cNCompileBrokerVinit_compiler_threads6Fi_v_; +text: .text%__1cNCompileBrokerQcompilation_init6FpnQAbstractCompiler__v_; +text: .text%__1cLstoreC0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPmethodDataKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodDataKlass.o; +text: .text%__1cHRetDataKis_RetData6M_i_: methodDataOop.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: klassKlass.o; +text: .text%__1cOBasicHashtable2t6Mii_v_: placeholders.o; +text: .text%__1cPmethodDataKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cCosHSolarisXinstall_signal_handlers6F_v_; +text: .text%__1cCosHSolarisUsynchronization_init6F_v_; +text: .text%__1cCosHSolarisOlibthread_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psMarkSweep.o; +text: .text%__1cOisT2_libthread6F_i_; +text: .text%__1cMciKlassKlassEmake6F_p0_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: klassKlass.o; +text: .text%__1cQPlaceholderTable2t6Mi_v_; +text: .text%__1cMciArrayKlass2t6MpnIciSymbol_ipnHciKlass__v_; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: objArrayKlass.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compilationPolicy.o; +text: .text%__1cUPSAdaptiveSizePolicy2t6MLLLLLddI_v_; +text: .text%__1cKTypeOopPtrEmake6FnHTypePtrDPTR_i_pk0_; +text: .text%__1cUInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cKTypeOopPtrFxdual6kM_pknEType__; +text: .text%__1cCosEinit6F_v_; +text: .text%__1cCosGinit_26F_i_; +text: .text%__1cRCompilationPolicyUcompleted_vm_startup6F_v_; +text: .text%__1cKTypeOopPtrCeq6kMpknEType__i_; +text: .text%__1cWcompilationPolicy_init6F_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyQinitialize_flags6M_v_; +text: .text%__1cOloadConL13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cbCTwoGenerationCollectorPolicyUinitialize_size_info6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_throw_exception6M_v_; +text: .text%__1cUInterpreterGeneratorXgenerate_accessor_entry6M_pC_; +text: .text%__1cbAPSGCAdaptivePolicyCounters2t6MpkciipnUPSAdaptiveSizePolicy__v_; +text: .text%__1cHvm_exit6Fi_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psAdaptiveSizePolicy.o; +text: .text%__1cPmethodDataKlassOset_alloc_size6MI_v_: methodDataKlass.o; +text: .text%__1cLbefore_exit6FpnKJavaThread__v_; +text: .text%__1cOcodeCache_init6F_v_; +text: .text%__1cWinvocationCounter_init6F_v_; +text: .text%__1cRInvocationCounterMreinitialize6Fi_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeCache.o; +text: .text%__1cOcompiler2_init6F_v_; +text: .text%__1cKklassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: klassKlass.o; +text: .text%__1cUInterpreterGeneratorUgenerate_empty_entry6M_pC_; +text: .text%__1cNCollectedHeap2t6M_v_; +text: .text%__1cOMacroAssemblerEcall6MpCnJrelocInfoJrelocType__v_: interpreter_sparc.o; +text: .text%__1cNThreadServiceEinit6F_v_; +text: .text%__1cKPSYoungGenRavailable_to_live6M_L_; +text: .text%__1cETypeRInitialize_shared6FpnHCompile__v_; +text: .text%__1cMTailJumpNode2t6MpnENode_22222_v_; +text: .text%__1cQsubL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQconstMethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassOset_alloc_size6MI_v_: constMethodKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlassKlass.o; +text: .text%__1cQconstMethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter.o; +text: .text%__1cXNativeSignatureIteratorHdo_byte6M_v_: interpreterRT_sparc.o; +text: .text%__1cKPSYoungGenPinitialize_work6M_v_; +text: .text%__1cSobjArrayKlassKlassUoop_is_objArrayKlass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cKPSYoungGenKinitialize6MnNReservedSpace_L_v_; +text: .text%__1cKPSYoungGenYinitialize_virtual_space6MnNReservedSpace_L_v_; +text: .text%__1cKPSYoungGen2t6MLLL_v_; +text: .text%__1cOPSVirtualSpaceJshrink_by6ML_i_; +text: .text%__1cRconstantPoolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cWconstantPoolCacheKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: cpCacheKlass.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: cpCacheKlass.o; +text: .text%__1cWconstantPoolCacheKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlassKlass.o; +text: .text%__1cRconstantPoolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constantPoolKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: objArrayKlassKlass.o; +text: .text%__1cURecompilationMonitorbGstart_recompilation_monitor_task6F_v_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: constantPoolKlass.o; +text: .text%__1cJTimeStampJupdate_to6Mx_v_; +text: .text%__1cCosLsignal_wait6F_i_; +text: .text%__1cJTimeStampMmilliseconds6kM_x_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: constantPoolKlass.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: constantPoolKlass.o; +text: .text%__1cSmulL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMCodeHeapPool2t6MpnICodeHeap_pkci_v_; +text: .text%__1cKPSYoungGenbCreset_survivors_after_shrink6M_v_; +text: .text%__1cKPSYoungGenQlimit_gen_shrink6ML_L_; +text: .text%__1cRCardTableModRefBS2t6MnJMemRegion_i_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_StackOverflowError_handler6M_pC_; +text: .text%__1cIPSOldGenOgen_size_limit6M_L_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: cfgnode.o; +text: .text%__1cIPSOldGenGresize6ML_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbFgenerate_slow_signature_handler6M_pC_; +text: .text%__1cKstfSSFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIPSOldGenKinitialize6MnNReservedSpace_Lpkci_v_; +text: .text%__1cSobjArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cUCompressedReadStreamJread_long6M_x_; +text: .text%__1cTcompilerOracle_init6F_v_; +text: .text%__1cOCompilerOracleOread_from_line6Fpc_v_; +text: .text%__1cVcompiledICHolderKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: compiledICHolderKlass.o; +text: .text%__1cVcompiledICHolderKlassOset_alloc_size6MI_v_: compiledICHolderKlass.o; +text: .text%__1cIPSOldGen2t6MLLLpkci_v_; +text: .text%__1cIPSOldGen2t6MnNReservedSpace_LLLLpkci_v_; +text: .text%__1cNMemoryServiceZadd_code_heap_memory_pool6FpnICodeHeap__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: memoryService.o; +text: .text%__1cbCAbstractInterpreterGeneratorbMgenerate_ArrayIndexOutOfBounds_handler6Mpkc_pC_; +text: .text%__1cCosHSolarisRmpss_sanity_check6F_v_; +text: .text%__1cSInterpreterRuntimeWcreate_klass_exception6FpnKJavaThread_pcpnHoopDesc__v_; +text: .text%__1cRCardTableModRefBSbBct_max_alignment_constraint6F_L_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psScavenge.o; +text: .text%__1cKPSScavengeKinitialize6F_v_; +text: .text%__1cNMemoryServiceRset_universe_heap6FpnNCollectedHeap__v_; +text: .text%__1cSobjArrayKlassKlassbEallocate_system_objArray_klass6MpnGThread__pnMklassOopDesc__; +text: .text%__1cKcastPPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeSupdate_mdp_for_ret6FpnKJavaThread_i_v_; +text: .text%__1cSPSPromotionManagerKinitialize6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psPromotionLAB.o; +text: .text%__1cOcmovLI_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovLI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKcmpOpFOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cNpriocntl_stub6FinGidtype_iipc_l_: os_solaris.o; +text: .text%__1cRlwp_priocntl_init6F_i_: os_solaris.o; +text: .text%__1cJPSPermGen2t6MnNReservedSpace_LLLLpkci_v_; +text: .text%__1cQjavaClasses_init6F_v_; +text: .text%JVM_InitProperties; +text: .text%JVM_Halt; +text: .text%__1cNReservedSpace2t6MLLipc_v_; +text: .text%__1cFParseMjump_if_join6MpnENode_2_2_; +text: .text%JVM_MaxMemory; +text: .text%__1cNReservedSpaceUpage_align_size_down6FL_L_; +text: .text%__1cNReservedSpaceYallocation_align_size_up6FL_L_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: parse1.o; +text: .text%JVM_GetClassDeclaredMethods; +text: .text%__1cLMoveL2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLMoveF2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOvmStructs_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vmStructs.o; +text: .text%__1cJvmSymbolsKinitialize6FpnGThread__v_; +text: .text%__1cMloadConLNodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cLJvmtiExportbMtransition_pending_onload_raw_monitors6F_v_; +text: .text%__1cIVMThreadGcreate6F_v_; +text: .text%__1cIVMThreadDrun6M_v_; +text: .text%__1cLJvmtiExportNpost_vm_death6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: parGCAllocBuffer.o; +text: .text%__1cMMutableSpaceOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cXPartialSubtypeCheckNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cVquicken_jni_functions6F_v_; +text: .text%__1cKg1RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%JNI_CreateJavaVM; +text: .text%__1cSmembar_releaseNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKklassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cQJNI_FastGetFieldbFgenerate_fast_get_boolean_field6F_pC_; +text: .text%__1cHOrLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cHOrLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_byte_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_char_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_short_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbBgenerate_fast_get_int_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_long_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_float_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_double_field6F_pC_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiTagMap.o; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiImpl.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_sparc_expand.o; +text: .text%__1cQjni_handles_init6F_v_; +text: .text%__1cUJvmtiPendingMonitorsXtransition_raw_monitors6F_v_; +text: .text%__1cUParallelScavengeHeapKinitialize6M_i_; +text: .text%__1cUJvmtiEventControllerIvm_start6F_v_; +text: .text%__1cTAbstract_VM_VersionKvm_release6F_pkc_; +text: .text%__1cTAbstract_VM_VersionXinternal_vm_info_string6F_pkc_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vm_version.o; +text: .text%__1cPVM_Version_init6F_v_; +text: .text%__1cKVM_VersionKinitialize6F_v_; +text: .text%__1cbEinitialize_converter_functions6F_v_; +text: .text%__1cUParallelScavengeHeapItop_addr6kM_ppnIHeapWord__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapIend_addr6kM_ppnIHeapWord__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapOresize_old_gen6ML_v_; +text: .text%__1cQvtableStubs_init6F_v_; +text: .text%__1cQaccessFlags_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEnvBase.o; +text: .text%__1cFVTuneEexit6F_v_; +text: .text%__1cUParallelScavengeHeapEheap6F_p0_; +text: .text%__1cUParallelScavengeHeapNgc_threads_do6kMpnNThreadClosure__v_; +text: .text%__1cUParallelScavengeHeapYpermanent_object_iterate6MpnNObjectClosure__v_; +text: .text%__1cKvtune_init6F_v_; +text: .text%JVM_SupportsCX8; +text: .text%__1cLOptoRuntimebSgenerate_polling_page_safepoint_handler_blob6F_v_; +text: .text%__1cTAbstract_VM_VersionJvm_vendor6F_pkc_; +text: .text%__1cMFastLockNodeLis_FastLock6kM_pk0_: classes.o; +text: .text%__1cUParallelScavengeHeapPpost_initialize6M_v_; +text: .text%__1cLJvmtiExportTpost_vm_initialized6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_start6F_v_; +text: .text%__1cLJvmtiExportQenter_live_phase6F_v_; +text: .text%__1cUParallelScavengeHeapMmax_capacity6kM_L_; +text: .text%__1cLJvmtiExportRenter_start_phase6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEventController.o; +text: .text%__1cKmutex_init6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_death6F_v_; +text: .text%JVM_InitializeSocketLibrary; +text: .text%__1cHVM_ExitEdoit6M_v_; +text: .text%__1cHVM_ExitEname6kM_pkc_: vm_operations.o; +text: .text%JVM_Socket; +text: .text%__1cFParseNfetch_monitor6MipnENode_2_2_; +text: .text%__1cUJvmtiEventControllerHvm_init6F_v_; +text: .text%__1cJLoadFNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cPGenerationSizerQinitialize_flags6M_v_: parallelScavengeHeap.o; +text: .text%__1cbCTwoGenerationCollectorPolicyMrem_set_name6M_nJGenRemSetEName__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapEkind6M_nNCollectedHeapEName__: parallelScavengeHeap.o; +text: .text%__1cTAbstract_VM_VersionHvm_name6F_pkc_; +text: .text%__1cKi0RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cMostream_init6F_v_; +text: .text%__1cUciObjArrayKlassKlassEmake6F_p0_; +text: .text%__1cYjni_GetBooleanField_addr6F_pC_; +text: .text%__1cVjni_GetByteField_addr6F_pC_; +text: .text%__1cVjni_GetCharField_addr6F_pC_; +text: .text%__1cWjni_GetShortField_addr6F_pC_; +text: .text%__1cTtypeArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cTtypeArrayKlassKlassVoop_is_typeArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cUjni_GetIntField_addr6F_pC_; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cPciObjectFactoryTinit_shared_objects6M_v_; +text: .text%__1cVjni_GetLongField_addr6F_pC_; +text: .text%__1cWjni_GetFloatField_addr6F_pC_; +text: .text%__1cFKlassMoop_is_array6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cNExceptionBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cQUncommonTrapBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: typeArrayKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlassKlass.o; +text: .text%__1cLmethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodKlass.o; +text: .text%__1cIciMethodMvtable_index6M_i_; +text: .text%__1cQjava_lang_ThreadPcompute_offsets6F_v_; +text: .text%__1cVjava_lang_ThreadGroupPcompute_offsets6F_v_; +text: .text%__1cCosXnon_memory_address_word6F_pc_; +text: .text%__1cYjava_lang_reflect_MethodPcompute_offsets6F_v_; +text: .text%__1cFKlassMoop_is_array6kM_i_: klassKlass.o; +text: .text%__1cLmethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: klassKlass.o; +text: .text%__1cYjava_lang_reflect_MethodNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldPcompute_offsets6F_v_; +text: .text%__1cPciObjArrayKlass2t6MpnIciSymbol_pnHciKlass_i_v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: klassKlass.o; +text: .text%__1cXjava_lang_reflect_FieldNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cQregF_to_stkINodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLJavaClassesbAcompute_hard_coded_offsets6F_v_; +text: .text%jni_ToReflectedMethod: jni.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: klassKlass.o; +text: .text%__1cOtypeArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cLmethodKlassOset_alloc_size6MI_v_: methodKlass.o; +text: .text%__1cSDeoptimizationBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_iiii_p0_; +text: .text%__1cNget_user_name6Fi_pc_: perfMemory_solaris.o; +text: .text%__1cPperfMemory_exit6F_v_; +text: .text%__1cPperfMemory_init6F_v_; +text: .text%__1cMPerfDataList2T6M_v_; +text: .text%JVM_RegisterUnsafeMethods; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnPPerfLongCounter__; +text: .text%__SLIP.DELETER__C: ostream.o; +text: .text%__1cPClassFileParserbFjava_lang_ref_Reference_fix_pre6MpnPtypeArrayHandle_nSconstantPoolHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cLClassLoaderbBsetup_bootstrap_search_path6F_v_; +text: .text%__1cPPerfDataManagerHsampled6F_pnMPerfDataList__; +text: .text%__1cFKlassUoop_is_instanceKlass6kM_i_: klassKlass.o; +text: .text%__1cLClassLoaderQload_zip_library6F_v_; +text: .text%__1cPPerfDataManagerHdestroy6F_v_; +text: .text%__1cLClassLoaderZcreate_package_info_table6F_v_; +text: .text%__1cLClassLoaderKinitialize6F_v_; +text: .text%__1cLClassLoaderVcompute_Object_vtable6F_i_; +text: .text%__1cQclassLoader_init6F_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: classLoader.o; +text: .text%__1cVverificationType_init6F_v_; +text: .text%__1cVverificationType_exit6F_v_; +text: .text%__1cTClassLoadingServiceEinit6F_v_; +text: .text%__1cbBcleanup_sharedmem_resources6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cIUniversePcheck_alignment6FLLpkc_v_; +text: .text%__1cIUniverseHgenesis6FpnGThread__v_; +text: .text%__1cIUniverseNfixup_mirrors6FpnGThread__v_; +text: .text%__1cXjni_GetDoubleField_addr6F_pC_; +text: .text%__1cRciArrayKlassKlassUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cQostream_init_log6F_v_; +text: .text%__1cICodeBlobMset_oop_maps6MpnJOopMapSet__v_; +text: .text%__1cNuniverse_init6F_i_; +text: .text%__1cOuniverse2_init6F_v_; +text: .text%__1cSuniverse_post_init6F_v_; +text: .text%__1cMostream_exit6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: phase.o; +text: .text%__1cOtailjmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: methodLiveness.o; +text: .text%Unsafe_SetNativeLong; +text: .text%__1cKPerfMemoryUdelete_memory_region6F_v_; +text: .text%__1cKPerfMemoryUcreate_memory_region6FL_v_; +text: .text%__1cSmmap_create_shared6FL_pc_: perfMemory_solaris.o; +text: .text%Unsafe_FreeMemory; +text: .text%__1cbAcreate_sharedmem_resources6Fpkc1L_i_: perfMemory_solaris.o; +text: .text%Unsafe_PageSize; +text: .text%__1cRmake_user_tmp_dir6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cKScopeValuePis_constant_oop6kM_i_: debugInfo.o; +text: .text%__1cQSystemDictionarybCinitialize_preloaded_classes6FpnGThread__v_; +text: .text%__1cQSystemDictionarybDinitialize_basic_type_mirrors6FpnGThread__v_; +text: .text%__1cLOptoRuntimebPgenerate_polling_page_return_handler_blob6F_v_; +text: .text%__1cLOptoRuntimebPgenerate_illegal_instruction_handler_blob6F_v_; +text: .text%__1cLOptoRuntimebBgenerate_uncommon_trap_blob6F_v_; +text: .text%__1cNSharedRuntimeTgenerate_deopt_blob6F_v_; +text: .text%__1cLOptoRuntimeWfill_in_exception_blob6F_v_; +text: .text%__1cQshrL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLOptoRuntimeUsetup_exception_blob6F_v_; +text: .text%__1cMPeriodicTask2T5B6M_v_; +text: .text%__1cLOptoRuntimeYgenerate_arraycopy_stubs6F_v_; +text: .text%__1cMPeriodicTaskJdisenroll6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: runtimeService.o; +text: .text%__1cORuntimeServiceYrecord_application_start6F_v_; +text: .text%__1cCosXis_server_class_machine6F_i_; +text: .text%__1cDhpiKinitialize6F_i_; +text: .text%__1cDhpiZinitialize_socket_library6F_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: matcher.o; +text: .text%__1cScheck_ThreadShadow6F_v_; +text: .text%__1cQSystemDictionaryKinitialize6FpnGThread__v_; +text: .text%__1cSCommandLineFlagsExKuintxAtPut6FnXCommandLineFlagWithType_L_v_; +text: .text%__1cQSystemDictionarybAcompute_java_system_loader6FpnGThread__v_; +text: .text%__1cHMatcherbDinterpreter_frame_pointer_reg6F_nHOptoRegEName__; +text: .text%__1cQdivD_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cCosGstrdup6Fpkc_pc_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_sparc_pipeline.o; +text: .text%__1cOmarksweep_init6F_v_; +text: .text%__1cQsubD_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC222_v_; +text: .text%__1cCosLinit_random6Fl_v_; +text: .text%__1cNIdealLoopTreeUmerge_many_backedges6MpnOPhaseIdealLoop__v_; +text: .text%__1cICodeHeap2t6M_v_; +text: .text%__1cQaddF_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cICodeHeapHreserve6MLLL_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: markSweep.o; +text: .text%__1cOMacroAssemblerNset_vm_result6MpnMRegisterImpl__v_; +text: .text%__1cCosNset_boot_path6Fcc_i_; +text: .text%__1cQSystemDictionaryKclasses_do6FpFpnMklassOopDesc__v_v_; +text: .text%__1cORuntimeServiceEinit6F_v_; +text: .text%__1cNTemplateTableGbipush6F_v_; +text: .text%__1cNTemplateTableGsipush6F_v_; +text: .text%__1cNTemplateTableGldc2_w6F_v_; +text: .text%__1cNTemplateTableFiload6F_v_; +text: .text%__1cNTemplateTableLfast_iload26F_v_; +text: .text%__1cNTemplateTableKfast_iload6F_v_; +text: .text%__1cNTemplateTableFlload6F_v_; +text: .text%__1cNTemplateTableFfload6F_v_; +text: .text%__1cNTemplateTableFdload6F_v_; +text: .text%__1cNTemplateTableFaload6F_v_; +text: .text%__1cNTemplateTableKwide_iload6F_v_; +text: .text%__1cNTemplateTableKwide_lload6F_v_; +text: .text%__1cNTemplateTableKwide_fload6F_v_; +text: .text%__1cNTemplateTableKwide_dload6F_v_; +text: .text%__1cNTemplateTableKwide_aload6F_v_; +text: .text%__1cNTemplateTableGiaload6F_v_; +text: .text%__1cNTemplateTableGlaload6F_v_; +text: .text%__1cNTemplateTableGfaload6F_v_; +text: .text%__1cNTemplateTableGdaload6F_v_; +text: .text%__1cNTemplateTableGaaload6F_v_; +text: .text%__1cWInlineCacheBuffer_init6F_v_; +text: .text%__1cLOptoRuntimeVhandle_exception_Type6F_pknITypeFunc__; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC222_v_; +text: .text%__1cLOptoRuntimeNfetch_monitor6FipnJBasicLock_pC_pnHoopDesc__; +text: .text%__1cLOptoRuntimeSfetch_monitor_Type6F_pknITypeFunc__; +text: .text%__1cNTemplateTableF_goto6F_v_; +text: .text%__1cNTemplateTableGgoto_w6F_v_; +text: .text%__1cNTemplateTableFjsr_w6F_v_; +text: .text%__1cNTemplateTableDjsr6F_v_; +text: .text%__1cNeventlog_init6F_v_; +text: .text%__1cOMacroAssemblerVverify_oop_subroutine6M_v_; +text: .text%__1cKfix_parent6FpnNIdealLoopTree_1_v_: loopnode.o; +text: .text%__1cStemplateTable_init6F_v_; +text: .text%__1cNTemplateTableNpd_initialize6F_v_; +text: .text%__1cNTemplateTableDnop6F_v_; +text: .text%__1cNTemplateTableSshouldnotreachhere6F_v_; +text: .text%__1cNTemplateTableLaconst_null6F_v_; +text: .text%__1cDhpiYinitialize_get_interface6FpnIvm_calls__v_; +text: .text%__1cNIdealLoopTreeQsplit_outer_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: fprofiler.o; +text: .text%__1cGThreadOis_Java_thread6kM_i_: gcTaskThread.o; +text: .text%__1cNStubGeneratorYgenerate_catch_exception6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorSgenerate_test_stop6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbIgenerate_copy_words_aligned8_lower6M_pC_: stubGenerator_sparc.o; +text: .text%__1cINegFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNStubGeneratorbJgenerate_copy_words_aligned8_higher6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbEgenerate_partial_subtype_check6M_pC_: stubGenerator_sparc.o; +text: .text%__1cJArgumentsVfinalize_vm_init_args6FpnMSysClassPath_i_i_; +text: .text%__1cJArgumentsbNparse_java_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsbSparse_java_tool_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cNGCTaskManagerKthreads_do6MpnNThreadClosure__v_; +text: .text%__1cNGCTaskManagerKinitialize6M_v_; +text: .text%__1cNGCTaskManager2t6MI_v_; +text: .text%__1cRStubCodeGeneratorLstub_prolog6MpnMStubCodeDesc__v_; +text: .text%__1cMSysClassPathPexpand_endorsed6M_v_; +text: .text%__1cOtailjmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLStatSamplerbMcreate_system_property_instrumentation6FpnGThread__v_; +text: .text%__1cLStatSamplerHdestroy6F_v_; +text: .text%__1cJArgumentsFparse6FpknOJavaVMInitArgs__i_; +text: .text%__1cLStatSamplerJdisengage6F_v_; +text: .text%__1cMSysClassPathQadd_jars_to_path6Fpcpkc_1_; +text: .text%__1cJArgumentsVprocess_settings_file6Fpkcii_i_; +text: .text%__1cRLowMemoryDetectorKinitialize6F_v_; +text: .text%__1cJArgumentsTset_parnew_gc_flags6F_v_; +text: .text%__1cSstubRoutines_init26F_v_; +text: .text%__1cJGenRemSetYmax_alignment_constraint6Fn0AEName__L_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: genCollectedHeap.o; +text: .text%__1cSstubRoutines_init16F_v_; +text: .text%__1cNStubGeneratorbNgenerate_flush_callers_register_windows6M_pC_: stubGenerator_sparc.o; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: stubGenerator_sparc.o; +text: .text%__1cJArgumentsbBset_cms_and_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsUset_ergonomics_flags6F_v_; +text: .text%__1cNStubGeneratorbIgenerate_handler_for_unsafe_access6M_pC_: stubGenerator_sparc.o; +text: .text%__1cJArgumentsZcheck_vm_args_consistency6F_i_; +text: .text%__1cRLowMemoryDetectorbGlow_memory_detector_thread_entry6FpnKJavaThread_pnGThread__v_; +text: .text%__1cXLowMemoryDetectorThreadbCis_hidden_from_external_view6kM_i_: lowMemoryDetector.o; +text: .text%__1cNStubGeneratorbAgenerate_forward_exception6M_pC_: stubGenerator_sparc.o; +text: .text%__1cJArgumentsSparse_vm_init_args6FpknOJavaVMInitArgs__i_; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cNStubGeneratorSgenerate_call_stub6MrpC_1_: stubGenerator_sparc.o; +text: .text%__1cJArgumentsWparse_each_vm_init_arg6FpknOJavaVMInitArgs_pnMSysClassPath_pi_i_; +text: .text%__1cVMoveL2D_stack_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: oopMap.o; +text: .text%__1cWResolveOopMapConflictsUdo_potential_rewrite6MpnGThread__nMmethodHandle__; +text: .text%__1cKRegionNodeUdepends_only_on_test6kM_i_: loopnode.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: arrayKlassKlass.o; +text: .text%__1cParrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: arrayKlassKlass.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: machnode.o; +text: .text%__1cPmanagement_init6F_v_; +text: .text%__1cTsignal_thread_entry6FpnKJavaThread_pnGThread__v_: os.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: generateOopMap.o; +text: .text%__1cFframeVinterpreter_frame_mdp6kM_pC_; +text: .text%__1cKManagementKinitialize6FpnGThread__v_; +text: .text%__1cCosLsignal_init6F_v_; +text: .text%__1cKManagementWrecord_vm_startup_time6Fxx_v_; +text: .text%__1cCosXterminate_signal_thread6F_v_; +text: .text%__1cRcheck_basic_types6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: management.o; +text: .text%__1cNSharedRuntimebIinitialize_StrictMath_entry_points6F_v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC222i_v_; +text: .text%__1cOMacroAssemblerRcall_VM_leaf_base6MpnMRegisterImpl_pCi_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedHeap.o; +text: .text%__1cParrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cVMoveF2I_stack_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLStatSamplerGengage6F_v_; +text: .text%__1cJArgumentsWPropertyList_get_value6FpnOSystemProperty_pkc_4_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cLsymbolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cJArgumentsWinit_system_properties6F_v_; +text: .text%__1cFChunkbDstart_chunk_pool_cleaner_task6F_v_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHGCStats2t6M_v_; +text: .text%__1cOchunkpool_init6F_v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: symbolKlass.o; +text: .text%__1cWAdjoiningVirtualSpaces2t6MnNReservedSpace_LLL_v_; +text: .text%__1cLconvP2BNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUAdjoiningGenerations2t6MnNReservedSpace_LLLLLLL_v_; +text: .text%__1cQGCPolicyCounters2t6Mpkcii_v_; +text: .text%__1cLsymbolKlassOset_alloc_size6MI_v_: symbolKlass.o; +text: .text%__1cLsymbolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: symbolKlass.o; +text: .text%__1cNcarSpace_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: arguments.o; +text: .text%__1cPorL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableGbaload6F_v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpnMRegisterImpl_pC2_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_normal6MnITosState__v_; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: library_call.o; +text: .text%__1cOMacroAssemblerCfb6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: templateTable_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: tenuredGeneration.o; +text: .text%__1cCosScurrent_process_id6F_i_; +text: .text%__1cCosbDallocate_thread_local_storage6F_i_; +text: .text%__1cZInterpreterMacroAssemblerbFset_method_data_pointer_for_bcp6M_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: dictionary.o; +text: .text%__1cOLibraryCallKitWinline_native_hashcode6Mii_i_; +text: .text%__1cGThreadMset_priority6Fp0nOThreadPriority__v_; +text: .text%__1cRsarL_reg_imm6NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNMemoryManagerbDget_code_cache_memory_manager6F_p0_; +text: .text%__1cKDictionaryKclasses_do6MpFpnMklassOopDesc__v_v_; +text: .text%__1cXonStackReplacement_init6F_v_; +text: .text%__1cNMemoryManagerbDget_psScavenge_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cNMemoryManagerbEget_psMarkSweep_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cKDictionary2t6Mi_v_; +text: .text%__1cbBcreate_initial_thread_group6FpnGThread__nGHandle__: thread.o; +text: .text%__1cNTemplateTableOmultianewarray6F_v_; +text: .text%__1cNTemplateTableIputfield6Fi_v_; +text: .text%__1cNTemplateTableJputstatic6Fi_v_; +text: .text%__1cVInterfaceSupport_init6F_v_; +text: .text%__1cNTemplateTableNinvokevirtual6Fi_v_; +text: .text%__1cNTemplateTableRfast_invokevfinal6Fi_v_; +text: .text%__1cNTemplateTableNinvokespecial6Fi_v_; +text: .text%__1cNTemplateTableMinvokestatic6Fi_v_; +text: .text%__1cNTemplateTablebDinvokeinterface_object_method6FpnMRegisterImpl_222_v_; +text: .text%__1cNTemplateTablePinvokeinterface6Fi_v_; +text: .text%__1cNTemplateTableE_new6F_v_; +text: .text%__1cNTemplateTableInewarray6F_v_; +text: .text%__1cNTemplateTableJanewarray6F_v_; +text: .text%__1cNTemplateTableLarraylength6F_v_; +text: .text%__1cNTemplateTableJcheckcast6F_v_; +text: .text%__1cNTemplateTableKinstanceof6F_v_; +text: .text%__1cNTemplateTableL_breakpoint6F_v_; +text: .text%__1cNTemplateTableGathrow6F_v_; +text: .text%__1cNTemplateTableMmonitorenter6F_v_; +text: .text%__1cNTemplateTableLmonitorexit6F_v_; +text: .text%__1cNTemplateTableEwide6F_v_; +text: .text%__1cVcreate_initial_thread6FnGHandle_pnKJavaThread_pnGThread__pnHoopDesc__: thread.o; +text: .text%__1cQPSGenerationPool2t6MpnIPSOldGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cQPSGenerationPool2t6MpnJPSPermGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cRConstantLongValueQis_constant_long6kM_i_: debugInfo.o; +text: .text%__1cUEdenMutableSpacePool2t6MpnKPSYoungGen_pnMMutableSpace_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cVcheck_pending_signals6Fi_i_: os_solaris.o; +text: .text%__1cbCAbstractInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cOThreadCriticalKinitialize6F_v_; +text: .text%__1cSThreadLocalStoragebCgenerate_code_for_get_thread6F_v_; +text: .text%__1cXreferenceProcessor_init6F_v_; +text: .text%__1cbCAbstractInterpreterGeneratorMgenerate_all6M_v_; +text: .text%__1cYSurvivorMutableSpacePool2t6MpnKPSYoungGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cTConstantDoubleValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cJBytecodesKinitialize6F_v_; +text: .text%__1cObytecodes_init6F_v_; +text: .text%__1cJBytecodesNpd_initialize6F_v_; +text: .text%__1cWThreadLocalAllocBufferWstartup_initialization6F_v_; +text: .text%__1cPGlobalTLABStats2t6M_v_; +text: .text%__1cHCompileRpd_compiler2_init6F_v_; +text: .text%__1cKC2CompilerKinitialize6M_v_; +text: .text%__1cSThreadLocalStorageEinit6F_v_; +text: .text%__1cQinterpreter_init6F_v_; +text: .text%__1cbAcall_initializeSystemClass6FpnGThread__v_: thread.o; +text: .text%__1cWreset_vm_info_property6FpnGThread__v_: thread.o; +text: .text%__1cNWatcherThreadDrun6M_v_; +text: .text%__1cNWatcherThreadEstop6F_v_; +text: .text%__1cOMacroAssemblerElcmp6MpnMRegisterImpl_22_v_; +text: .text%__1cFStatebB_sub_Op_PartialSubtypeCheck6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_MoveF2I6MpknENode__v_; +text: .text%__1cVRegistersForDebuggingRrestore_registers6FpnOMacroAssembler_pnMRegisterImpl__v_: assembler_sparc.o; +text: .text%__1cVRegistersForDebuggingOsave_registers6FpnOMacroAssembler__v_: assembler_sparc.o; +text: .text%__1cCosSget_temp_directory6F_pkc_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: regmask.o; +text: .text%__1cFStateO_sub_Op_CastPP6MpknENode__v_; +text: .text%__1cHThreadsJcreate_vm6FpnOJavaVMInitArgs_pi_i_; +text: .text%__1cCosNsigexitnum_pd6F_i_; +text: .text%__1cCosHSolarisPinit_signal_mem6F_v_; +text: .text%__1cCosOsignal_init_pd6F_v_; +text: .text%__1cCosNsignal_notify6Fi_v_; +text: .text%__1cKScopeValueSis_constant_double6kM_i_: debugInfo.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interp_masm_sparc.o; +text: .text%__1cQshlI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cCosHSolarisQsignal_sets_init6F_v_; +text: .text%__1cNTemplateTableHfastore6F_v_; +text: .text%__1cNTemplateTableHdastore6F_v_; +text: .text%__1cNTemplateTableHaastore6F_v_; +text: .text%__1cNTemplateTableHbastore6F_v_; +text: .text%__1cLOptoRuntimeUmultianewarray3_Type6F_pknITypeFunc__; +text: .text%__1cNTemplateTableHsastore6F_v_; +text: .text%__1cLOptoRuntimeUmultianewarray2_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray1_Type6F_pknITypeFunc__; +text: .text%__1cSThreadLocalStorageHpd_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: indexSet.o; +text: .text%__1cPvm_init_globals6F_v_; +text: .text%__1cNTemplateTableDpop6F_v_; +text: .text%__1cNTemplateTableEpop26F_v_; +text: .text%__1cNTemplateTableDdup6F_v_; +text: .text%__1cNTemplateTableGdup_x16F_v_; +text: .text%__1cNTemplateTableGdup_x26F_v_; +text: .text%__1cNTemplateTableEdup26F_v_; +text: .text%__1cNTemplateTableHdup2_x16F_v_; +text: .text%__1cNTemplateTableHdup2_x26F_v_; +text: .text%__1cNTemplateTableEswap6F_v_; +text: .text%__1cNTemplateTableHlastore6F_v_; +text: .text%__1cNTemplateTableGcaload6F_v_; +text: .text%__1cNTemplateTableMfast_icaload6F_v_; +text: .text%__1cNTemplateTableGsaload6F_v_; +text: .text%__1cLicache_init6F_v_; +text: .text%__1cTICacheStubGeneratorVgenerate_icache_flush6MppFpCii_i_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: loaderConstraints.o; +text: .text%__1cLOptoRuntimeUmultianewarray5_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray4_Type6F_pknITypeFunc__; +text: .text%__1cNTemplateTableHaload_06F_v_; +text: .text%__1cNTemplateTableGistore6F_v_; +text: .text%__1cNTemplateTableGlstore6F_v_; +text: .text%__1cNTemplateTableGfstore6F_v_; +text: .text%__1cNTemplateTableGdstore6F_v_; +text: .text%__1cNTemplateTableGastore6F_v_; +text: .text%__1cNTemplateTableLwide_istore6F_v_; +text: .text%__1cNTemplateTableLwide_lstore6F_v_; +text: .text%__1cNTemplateTableLwide_fstore6F_v_; +text: .text%__1cNTemplateTableLwide_dstore6F_v_; +text: .text%__1cNTemplateTableLwide_astore6F_v_; +text: .text%__1cNTemplateTableHiastore6F_v_; +text: .text%__1cMinit_globals6F_i_; +text: .text%__1cQcreate_os_thread6FpnGThread_I_pnIOSThread__: os_solaris.o; +text: .text%__1cSinstanceKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cSinstanceKlassKlassUoop_is_instanceKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cNTemplateTableDret6F_v_; +text: .text%__1cNTemplateTableIwide_ret6F_v_; +text: .text%__1cNTemplateTableLtableswitch6F_v_; +text: .text%__1cNTemplateTableMlookupswitch6F_v_; +text: .text%__1cNTemplateTableRfast_linearswitch6F_v_; +text: .text%__1cNTemplateTableRfast_binaryswitch6F_v_; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cWResolveOopMapConflictsOreport_results6kM_i_: rewriter.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: instanceKlassKlass.o; +text: .text%__1cSinstanceKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlassKlass.o; +text: .text%__1cNTemplateTableIgetfield6Fi_v_; +text: .text%__1cNTemplateTableJgetstatic6Fi_v_; +text: .text%__1cQinstanceRefKlassZupdate_nonstatic_oop_maps6FpnMklassOopDesc__v_; +text: .text%__1cCosScreate_main_thread6FpnGThread__i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: relocInfo.o; +text: .text%__1cVLoaderConstraintTable2t6Mi_v_; +text: .text%__1cCosPphysical_memory6F_X_; +text: .text%__1cNTemplateTableEirem6F_v_; +text: .text%__1cNTemplateTableElmul6F_v_; +text: .text%__1cNTemplateTableEldiv6F_v_; +text: .text%__1cNTemplateTableElrem6F_v_; +text: .text%__1cNTemplateTableElshl6F_v_; +text: .text%__1cNTemplateTableElshr6F_v_; +text: .text%__1cNTemplateTableFlushr6F_v_; +text: .text%__1cMexit_globals6F_v_; +text: .text%__1cCosbDinit_system_properties_values6F_v_; +text: .text%__1cNTemplateTableEineg6F_v_; +text: .text%__1cNTemplateTableElneg6F_v_; +text: .text%__1cNTemplateTableEfneg6F_v_; +text: .text%__1cNTemplateTableEdneg6F_v_; +text: .text%__1cNTemplateTableEiinc6F_v_; +text: .text%__1cNTemplateTableJwide_iinc6F_v_; +text: .text%__1cLOptoRuntimeIgenerate6FpnFciEnv__v_; +text: .text%__1cNTemplateTableElcmp6F_v_; +text: .text%__1cSset_init_completed6F_v_; +text: .text%__1cOMacroAssemblerPstop_subroutine6M_v_; diff --git a/hotspot/build/solaris/makefiles/reorder_CORE_amd64 b/hotspot/build/solaris/makefiles/reorder_CORE_amd64 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hotspot/build/solaris/makefiles/reorder_CORE_i486 b/hotspot/build/solaris/makefiles/reorder_CORE_i486 new file mode 100644 index 00000000000..d3b67173469 --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_CORE_i486 @@ -0,0 +1 @@ +# reordering not support for CORE builds diff --git a/hotspot/build/solaris/makefiles/reorder_CORE_sparc b/hotspot/build/solaris/makefiles/reorder_CORE_sparc new file mode 100644 index 00000000000..d3b67173469 --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_CORE_sparc @@ -0,0 +1 @@ +# reordering not support for CORE builds diff --git a/hotspot/build/solaris/makefiles/reorder_CORE_sparcv9 b/hotspot/build/solaris/makefiles/reorder_CORE_sparcv9 new file mode 100644 index 00000000000..d3b67173469 --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_CORE_sparcv9 @@ -0,0 +1 @@ +# reordering not support for CORE builds diff --git a/hotspot/build/solaris/makefiles/reorder_TIERED_amd64 b/hotspot/build/solaris/makefiles/reorder_TIERED_amd64 new file mode 100644 index 00000000000..b4bc7db5b72 --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_TIERED_amd64 @@ -0,0 +1,8114 @@ +data = R0x2000; +text = LOAD ?RXO; + + +text: .text%__1cECopyRpd_disjoint_words6FpnIHeapWord_2L_v_; +text: .text%__1cSPSPromotionManagerWcopy_to_survivor_space6MpnHoopDesc__2_; +text: .text%__1cNinstanceKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cJMarkSweepO_mark_and_push6FppnHoopDesc__v_; +text: .text%__1cNinstanceKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cOtypeArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cOtypeArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQIndexSetIteratorEnext6M_I_; +text: .text%__1cJMarkSweepPmark_and_follow6FppnHoopDesc__v_; +text: .text%__1cCosOjavaTimeMillis6F_x_; +text: .text%__1cNRelocIteratorEnext6M_i_; +text: .text%__1cQIndexSetIteratorQadvance_and_next6M_I_; +text: .text%__1cIUniverseMnon_oop_word6F_pv_; +text: .text%__1cNobjArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNobjArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNobjArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQObjectStartArrayMobject_start6kMpnIHeapWord__2_; +text: .text%__1cJMarkSweepUAdjustPointerClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cJMarkSweepOIsAliveClosureLdo_object_b6MpnHoopDesc__i_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__; +text: .text%__1cIPhaseIFGIadd_edge6MII_i_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_; +text: .text%__1cIMachNodeHis_Mach6M_p0_; +text: .text%__1cENodeHis_Copy6kM_I_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_; +text: .text%__1cQIndexSetIterator2t6MpnIIndexSet__v_; +text: .text%__1cOPhaseIdealLoopOidom_no_update6kMpnENode__2_; +text: .text%__1cHnmethodKcan_unload6MpnRBoolObjectClosure_pnKOopClosure_ppnHoopDesc_i_i_; +text: .text%__1cIMachNodeNrematerialize6kM_i_; +text: .text%__1cHRegMaskFis_UP6kM_i_; +text: .text%__1cXresource_allocate_bytes6FL_pc_; +text: .text%__1cNRelocIteratorFreloc6M_pnKRelocation__; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__; +text: .text%__1cHRegMaskESize6kM_I_; +text: .text%__1cIIndexSetLalloc_block6M_pn0AIBitBlock__; +text: .text%__1cWConstantPoolCacheEntryPadjust_pointers6M_v_; +text: .text%__1cWConstantPoolCacheEntryPfollow_contents6M_v_; +text: .text%__1cJMarkSweepSMarkAndPushClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cDLRGOcompute_degree6kMr0_i_; +text: .text%__1cLsymbolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cWPSScavengeRootsClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cLsymbolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLsymbolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cYPSPromotionFailedClosureJdo_object6MpnHoopDesc__v_; +text: .text%__1cENodeEjvms6kM_pnIJVMState__; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_; +text: .text%__1cIIndexSetWalloc_block_containing6MI_pn0AIBitBlock__; +text: .text%__1cENodeHdel_out6Mp0_v_; +text: .text%__1cKRelocationLunpack_data6M_v_; +text: .text%__1cIMachNodeJideal_reg6kM_I_; +text: .text%__1cJAssemblerOlocate_operand6FpCn0AMWhichOperand__1_; +text: .text%__1cKRelocationSpd_address_in_code6M_ppC_; +text: .text%__1cOoop_RelocationIoop_addr6M_ppnHoopDesc__; +text: .text%__1cRMachSpillCopyNodeMis_SpillCopy6M_p0_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__; +text: .text%__1cENodeGis_CFG6kM_i_; +text: .text%__1cKRelocationNunpack_2_ints6Mri1_v_; +text: .text%__1cETypeDcmp6Fpk02_i_; +text: .text%__1cQObjectStartArrayWobject_starts_in_range6kMpnIHeapWord_2_i_; +text: .text%__1cOoop_RelocationLunpack_data6M_v_; +text: .text%__1cHRegMaskJis_bound16kM_i_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__; +text: .text%__1cOtypeArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cHRegMaskJis_bound26kM_i_; +text: .text%__1cRmethodDataOopDescHdata_at6Mi_pnLProfileData__; +text: .text%__1cNGrowableArray4CI_Hat_grow6MirkI_I_; +text: .text%__1cLmethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cQconstMethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQconstMethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQconstMethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cRmethodDataOopDescJnext_data6MpnLProfileData__2_; +text: .text%__1cMOopTaskQdDueueOpop_local_slow6MInOTaskQdDueueSuperDAge__i_; +text: .text%__1cRMachSpillCopyNodeHis_Copy6kM_I_; +text: .text%__1cENodeGpinned6kM_i_; +text: .text%__1cOoop_RelocationJoop_value6M_pnHoopDesc__; +text: .text%__1cJrRegPOperEtype6kM_pknEType__; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_LI_v_; +text: .text%__1cMOopTaskQdDueueKpop_global6MrpnHoopDesc__i_; +text: .text%__1cPOopTaskQdDueueSetPsteal_best_of_26MipirpnHoopDesc__i_; +text: .text%__1cJVectorSet2R6MI_rnDSet__; +text: .text%__1cNRelocIteratorTadvance_over_prefix6M_v_; +text: .text%__1cSPSPromotionManagerUflush_prefetch_queue6M_v_; +text: .text%__1cIProjNodeHis_Proj6M_p0_; +text: .text%__1cPDictionaryEntrybDprotection_domain_set_oops_do6MpnKOopClosure__v_; +text: .text%__1cMOopMapStreamJfind_next6M_v_; +text: .text%__1cMloadConPNodePoper_input_base6kM_I_; +text: .text%__1cMloadConPNodeHtwo_adr6kM_I_; +text: .text%__1cMloadConPNodeErule6kM_I_; +text: .text%__1cHPhiNodeGis_Phi6M_p0_; +text: .text%__1cITypeNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__; +text: .text%__1cDff16FI_i_; +text: .text%__1cENodeNrematerialize6kM_i_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_; +text: .text%__1cFframeVoopmapreg_to_location6kMipknLRegisterMap__ppnHoopDesc__; +text: .text%__1cIIndexSetKinitialize6MI_v_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__; +text: .text%__1cMMutableSpaceMcas_allocate6ML_pnIHeapWord__; +text: .text%__1cIMachNodeGOpcode6kM_i_; +text: .text%__1cMget_live_bit6Fpii_i_: buildOopMap.o; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__; +text: .text%__1cENodeHadd_req6Mp0_v_; +text: .text%__1cENodeIout_grow6MI_v_; +text: .text%__1cMset_live_bit6Fpii_v_: buildOopMap.o; +text: .text%__1cKTypeOopPtrFklass6kM_pnHciKlass__; +text: .text%__1cIIndexSetKfree_block6MI_v_; +text: .text%__1cJCProjNodeGis_CFG6kM_i_; +text: .text%__1cETypeFuhash6Fpk0_i_; +text: .text%__1cJrRegIOperEtype6kM_pknEType__; +text: .text%__1cMPhaseChaitinLskip_copies6MpnENode__2_; +text: .text%__1cIMachNodeMcisc_operand6kM_i_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_; +text: .text%__1cICallNodeKmatch_edge6kMI_I_; +text: .text%__1cENodeHis_Call6M_pnICallNode__; +text: .text%__1cNCollectedHeapbDcheck_for_bad_heap_word_value6MpnIHeapWord_L_v_; +text: .text%__1cEDictGInsert6Mpv1i_1_; +text: .text%JVM_CurrentTimeMillis; +text: .text%__1cINodeHashLhash_delete6MpknENode__i_; +text: .text%__1cETypeJtype_dict6F_pnEDict__; +text: .text%__1cOPSPromotionLABKinitialize6MnJMemRegion__v_; +text: .text%__1cOPSPromotionLABFflush6M_v_; +text: .text%__1cIrc_class6Fi_nCRC__: ad_amd64.o; +text: .text%__1cMPhaseChaitinTinterfere_with_live6MIpnIIndexSet__v_; +text: .text%__1cINodeHashQhash_find_insert6MpnENode__2_; +text: .text%__1cNCollectedHeapbAcommon_mem_allocate_noinit6FLipnGThread__pnIHeapWord__; +text: .text%__1cENodeHis_Load6M_pnILoadNode__; +text: .text%__1cFArenaIcontains6kMpkv_i_; +text: .text%__1cJMultiNodeGis_CFG6kM_i_; +text: .text%__1cHPhiNodeGOpcode6kM_i_; +text: .text%__1cMPhaseChaitinKelide_copy6MpnENode_ipnFBlock_rnJNode_List_6i_i_; +text: .text%__1cKjmpDirNodeNis_block_proj6kM_pknENode__; +text: .text%__1cIProjNodeGis_CFG6kM_i_; +text: .text%__1cRMachSpillCopyNodeJideal_reg6kM_I_; +text: .text%__1cETypeIhashcons6M_pk0_; +text: .text%__1cENodeEhash6kM_I_; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_; +text: .text%__1cOlower_pressure6FpnDLRG_IpnFBlock_pI4_v_: ifg.o; +text: .text%__1cPOopTaskQdDueueSetFsteal6MipirpnHoopDesc__i_; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cKRegionNodeGis_CFG6kM_i_; +text: .text%__1cHCompileRvalid_bundle_info6MpknENode__i_; +text: .text%__1cIProjNodeGOpcode6kM_i_; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__; +text: .text%__1cOoop_RelocationSfix_oop_relocation6M_v_; +text: .text%__1cPClassFileStreamGget_u26MpnGThread__H_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__; +text: .text%__1cPVirtualCallDataKcell_count6M_i_; +text: .text%__1cIProjNodeGpinned6kM_i_; +text: .text%__1cENodeMcisc_operand6kM_i_; +text: .text%__1cRInterpreterOopMapLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cMMachCallNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cMloadConINodePoper_input_base6kM_I_; +text: .text%__1cMloadConINodeHtwo_adr6kM_I_; +text: .text%__1cHNTarjanEEVAL6M_p0_; +text: .text%__1cNMachIdealNodeErule6kM_I_; +text: .text%__1cKHandleMarkKinitialize6MpnGThread__v_; +text: .text%__1cKHandleMark2T6M_v_; +text: .text%__1cETypeLisa_oop_ptr6kM_i_; +text: .text%__1cOPhaseIdealLoopUbuild_loop_late_post6MpnENode_pk0_v_; +text: .text%__1cENode2t6MI_v_; +text: .text%__1cJloadPNodeErule6kM_I_; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__; +text: .text%__1cMloadConINodeErule6kM_I_; +text: .text%__1cICodeHeapKfind_start6kMpv_1_; +text: .text%__1cWShouldNotReachHereNodeNis_block_proj6kM_pknENode__; +text: .text%__1cHTypeIntCeq6kMpknEType__i_; +text: .text%__1cLProfileDataPfollow_contents6M_v_; +text: .text%__1cLProfileDataPadjust_pointers6M_v_; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__; +text: .text%__1cHRegMaskMClearToPairs6M_v_; +text: .text%__1cJPhaseLiveLadd_liveout6MpnFBlock_IrnJVectorSet__v_; +text: .text%__1cLemit_opcode6FrnKCodeBuffer_i_v_; +text: .text%__1cIPhaseIFGQeffective_degree6kMI_i_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__; +text: .text%__1cFArenaIArealloc6MpvLL_1_; +text: .text%__1cGIfNodeGOpcode6kM_i_; +text: .text%__1cHTypePtrEhash6kM_i_; +text: .text%__1cIPhaseIFGLremove_node6MI_pnIIndexSet__; +text: .text%__1cIPhaseIFGJre_insert6MI_v_; +text: .text%__1cRMachSpillCopyNodeLbottom_type6kM_pknEType__; +text: .text%__1cMclr_live_bit6Fpii_v_: buildOopMap.o; +text: .text%__1cETypeEmeet6kMpk0_2_; +text: .text%__1cMPhaseChaitinQis_high_pressure6MpnFBlock_pnDLRG_I_i_; +text: .text%__1cKBranchDataKcell_count6M_i_; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cOPhaseIdealLoopYsplit_if_with_blocks_pre6MpnENode__2_; +text: .text%__1cNIdealLoopTreeJis_member6kMpk0_i_; +text: .text%__1cOPhaseIdealLoopZsplit_if_with_blocks_post6MpnENode__v_; +text: .text%__1cDfh16FI_i_; +text: .text%__1cJraw_score6Fdd_d_: chaitin.o; +text: .text%__1cDLRGFscore6kM_d_; +text: .text%__1cKTypeOopPtrEhash6kM_i_; +text: .text%__1cIAddPNodeGOpcode6kM_i_; +text: .text%__1cKIfTrueNodeGOpcode6kM_i_; +text: .text%__1cMPhaseChaitinMchoose_color6MrnDLRG_i_i_; +text: .text%__1cMPhaseIterGVNNtransform_old6MpnENode__2_; +text: .text%__1cGcmpkey6Fpkv1_i_; +text: .text%__1cETypeJsingleton6kM_i_; +text: .text%__1cIMachNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cSPSPromotionManagerMdrain_stacks6M_v_; +text: .text%__1cHConNodeGOpcode6kM_i_; +text: .text%__1cITypeLongCeq6kMpknEType__i_; +text: .text%__1cUParallelScavengeHeapVlarge_typearray_limit6M_L_; +text: .text%__1cOtypeArrayKlassIallocate6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cKoopFactoryNnew_typeArray6FnJBasicType_ipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cMPhaseChaitinKbias_color6MrnDLRG_i_i_; +text: .text%__1cICodeHeapKnext_block6kMpnJHeapBlock__2_; +text: .text%__1cICodeHeapJnext_free6kMpnJHeapBlock__pv_; +text: .text%__1cICodeHeapLblock_start6kMpv_pnJHeapBlock__; +text: .text%__1cJCodeCacheEnext6FpnICodeBlob__2_; +text: .text%__1cRMachSpillCopyNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cMMachProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cLOptoRuntimeXdeoptimize_caller_frame6FpnKJavaThread_i_v_; +text: .text%__1cSCallStaticJavaNodeGOpcode6kM_i_; +text: .text%__1cYCallStaticJavaDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cJCodeCacheFalive6FpnICodeBlob__2_; +text: .text%__1cHRegMaskQis_aligned_Pairs6kM_i_; +text: .text%__1cSis_single_register6FI_i_: postaloc.o; +text: .text%__1cRMachSpillCopyNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMPhaseIterGVNWadd_users_to_worklist06MpnENode__v_; +text: .text%__1cECopyYconjoint_words_to_higher6FpnIHeapWord_2L_v_; +text: .text%__1cILRG_ListGextend6MII_v_; +text: .text%__1cWstatic_stub_RelocationLunpack_data6M_v_; +text: .text%__1cIciObjectGequals6Mp0_i_; +text: .text%__1cJPhaseLiveGgetset6MpnFBlock__pnIIndexSet__; +text: .text%__1cIConINodeGOpcode6kM_i_; +text: .text%__1cENodeRis_cisc_alternate6kM_i_; +text: .text%__1cLIfFalseNodeGOpcode6kM_i_; +text: .text%__1cKNode_ArrayGinsert6MIpnENode__v_; +text: .text%__1cLCounterDataKcell_count6M_i_; +text: .text%__1cWThreadLocalAllocBufferFreset6M_v_; +text: .text%__1cMMachProjNodeGOpcode6kM_i_; +text: .text%__1cENodeEgrow6MI_v_; +text: .text%__1cIMachNodePcompute_padding6kMi_i_; +text: .text%__1cKup_one_dom6FpnENode__1_: ifnode.o; +text: .text%__1cIMachNodeSalignment_required6kM_i_; +text: .text%__1cOPhaseIdealLoopEsort6MpnNIdealLoopTree_2_2_; +text: .text%__1cFframeOis_entry_frame6kM_i_; +text: .text%__1cNCollectedHeapXallocate_from_tlab_slow6FpnGThread_L_pnIHeapWord__; +text: .text%__1cUParallelScavengeHeapVunsafe_max_tlab_alloc6kM_L_; +text: .text%__1cHNTarjanICOMPRESS6M_v_; +text: .text%__1cWThreadLocalAllocBufferXclear_before_allocation6M_v_; +text: .text%__1cFMutexGunlock6M_v_; +text: .text%__1cJMultiNodeIis_Multi6M_p0_; +text: .text%__1cIBoolNodeGOpcode6kM_i_; +text: .text%__1cSInterpreterRuntimeInewarray6FpnKJavaThread_nJBasicType_i_v_; +text: .text%__1cUParallelScavengeHeapRallocate_new_tlab6ML_pnIHeapWord__; +text: .text%__1cKSharedHeapXfill_region_with_object6FnJMemRegion__v_; +text: .text%__1cWThreadLocalAllocBufferKinitialize6MpnIHeapWord_22_v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2t6M_v_; +text: .text%__1cWThreadLocalAllocBufferEfill6MpnIHeapWord_2L_v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2T6M_v_; +text: .text%__1cKTypeOopPtrCeq6kMpknEType__i_; +text: .text%__1cHTypePtrCeq6kMpknEType__i_; +text: .text%__1cRMachSpillCopyNodePoper_input_base6kM_I_; +text: .text%__1cIIndexSetFclear6M_v_; +text: .text%__1cHTypeIntJsingleton6kM_i_; +text: .text%__1cJPhaseLiveLadd_liveout6MpnFBlock_pnIIndexSet_rnJVectorSet__v_; +text: .text%__1cECopyXconjoint_words_to_lower6FpnIHeapWord_2L_v_; +text: .text%__1cNRelocIteratorKset_limits6MpC1_v_; +text: .text%__1cNRelocIteratorKinitialize6MlpnICodeBlob_pC3_v_; +text: .text%__1cNinstanceKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cSinstanceKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cITypeNodeJideal_reg6kM_I_; +text: .text%__1cFframeUis_interpreted_frame6kM_i_; +text: .text%__1cHTypeIntEhash6kM_i_; +text: .text%__1cIPhaseGVNJtransform6MpnENode__2_; +text: .text%__1cNinstanceKlassGitable6kM_pnLklassItable__; +text: .text%__1cLklassItable2t6MnTinstanceKlassHandle__v_; +text: .text%__1cIMachNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cIciObjectEhash6M_i_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__; +text: .text%__1cFState2T6M_v_; +text: .text%__1cPVirtualCallDataPfollow_contents6M_v_; +text: .text%__1cPVirtualCallDataPadjust_pointers6M_v_; +text: .text%__1cENodeGis_Con6kM_I_; +text: .text%__1cJrRegIOperJnum_edges6kM_I_; +text: .text%__1cENodeIget_long6kM_x_; +text: .text%__1cNCellTypeStateFmerge6kM0i_0_; +text: .text%__1cOPhaseIdealLoopUbuild_loop_tree_impl6MpnENode_i_i_; +text: .text%__1cWNode_Backward_IteratorEnext6M_pnENode__; +text: .text%__1cHemit_rm6FrnKCodeBuffer_iii_v_; +text: .text%__1cHPhiNodeGpinned6kM_i_; +text: .text%__1cITypeNodeEhash6kM_I_; +text: .text%__1cIIndexSetKinitialize6MIpnFArena__v_; +text: .text%__1cJPhaseLiveKgetfreeset6M_pnIIndexSet__; +text: .text%__1cMMachProjNodeJideal_reg6kM_I_; +text: .text%__1cHPhiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIParmNodeGis_CFG6kM_i_; +text: .text%__1cENodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNCollectedHeapMobj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cNinstanceKlassRallocate_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cKRegionNodeGOpcode6kM_i_; +text: .text%__1cMMachProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOptoRuntimeFnew_C6FpnMklassOopDesc_pnKJavaThread__v_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__; +text: .text%__1cIProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_; +text: .text%__1cLTypeInstPtrEhash6kM_i_; +text: .text%__1cLuse_dom_lca6FpnFBlock_pnENode_3rnLBlock_Array__1_: gcm.o; +text: .text%__1cITypeLongEhash6kM_i_; +text: .text%__1cIBoolNodeHis_Bool6M_p0_; +text: .text%__1cOPhaseIdealLoopOget_early_ctrl6MpnENode__2_; +text: .text%__1cOPhaseIdealLoopOset_early_ctrl6MpnENode__v_; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__; +text: .text%__1cIJumpDataKcell_count6M_i_; +text: .text%__1cHNTarjanELINK6Mp01_v_; +text: .text%__1cENodeIIdentity6MpnOPhaseTransform__p0_; +text: .text%__1cIMachNode2t6M_v_; +text: .text%__1cFStateRMachOperGenerator6MipnIMachNode_pnHCompile__pnIMachOper__; +text: .text%__1cRPSOldPromotionLABFflush6M_v_; +text: .text%__1cICallNodeHis_Call6M_p0_; +text: .text%__1cENodeFIdeal6MpnIPhaseGVN_i_p0_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cOJNIHandleBlockPallocate_handle6MpnHoopDesc__pnI_jobject__; +text: .text%__1cENodeNis_block_proj6kM_pk0_; +text: .text%__1cUParallelScavengeHeapPis_in_permanent6kMpkv_i_; +text: .text%__1cHdom_lca6FpnFBlock_1_1_: gcm.o; +text: .text%__1cOPhaseIdealLoopThas_local_phi_input6MpnENode__2_; +text: .text%__1cJVectorSet2F6kMI_i_; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cICallNodeLbottom_type6kM_pknEType__; +text: .text%JVM_ArrayCopy; +text: .text%__1cOtypeArrayKlassQoop_is_typeArray6kM_i_; +text: .text%__1cOtypeArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cIMachNodeLbottom_type6kM_pknEType__; +text: .text%__1cPClassFileParserOcheck_property6MipkcipnGThread__v_; +text: .text%__1cFState2t6M_v_; +text: .text%__1cFStateDDFA6MipknENode__i_; +text: .text%__1cJPhaseLiveHfreeset6MpknFBlock__v_; +text: .text%__1cOPhaseIdealLoopbIdom_lca_for_get_late_ctrl_internal6MpnENode_22_2_; +text: .text%__1cKRegionNodeGpinned6kM_i_; +text: .text%__1cPjava_lang_ClassLas_klassOop6FpnHoopDesc__pnMklassOopDesc__; +text: .text%__1cRSignatureIteratorGexpect6Mc_v_; +text: .text%__1cIProjNodeEhash6kM_I_; +text: .text%__1cENodeFclone6kM_p0_; +text: .text%__1cILocationIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cIPhaseCCPOtransform_once6MpnENode__2_; +text: .text%__1cHCompileMFillLocArray6MpnENode_pnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cHRegMaskPfind_first_pair6kM_i_; +text: .text%__1cENodeKmatch_edge6kMI_I_; +text: .text%__1cKJNIHandlesKmake_local6FpnHJNIEnv__pnHoopDesc__pnI_jobject__; +text: .text%__1cNLocationValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cNPhaseRegAllocKreg2offset6kMi_i_; +text: .text%__1cNPhaseRegAllocUreg2offset_unchecked6kMi_i_; +text: .text%__1cbAfinal_graph_reshaping_impl6FpnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cETypeFxmeet6kMpk0_2_; +text: .text%__1cENodeFis_If6M_pnGIfNode__; +text: .text%__1cJStartNodeLbottom_type6kM_pknEType__; +text: .text%__1cICmpPNodeGOpcode6kM_i_; +text: .text%__1cOMethodLivenessKBasicBlockXcompute_gen_kill_single6MpnQciBytecodeStream__v_; +text: .text%__1cKjmpDirNodeMideal_Opcode6kM_i_; +text: .text%__1cOPhaseIdealLoopZremix_address_expressions6MpnENode__2_; +text: .text%__1cFMutexElock6MpnGThread__v_; +text: .text%__1cILoadNodeHis_Load6M_p0_; +text: .text%__1cGciTypeEmake6FnJBasicType__p0_; +text: .text%__1cQUnique_Node_ListGremove6MpnENode__v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_; +text: .text%__1cRMachSafePointNodeEjvms6kM_pnIJVMState__; +text: .text%__1cNinstanceKlassMclass_loader6kM_pnHoopDesc__; +text: .text%__1cOPhaseIdealLoopNget_late_ctrl6MpnENode_2_2_; +text: .text%__1cRMachSpillCopyNodeOimplementation6kMpnKCodeBuffer_pnNPhaseRegAlloc_i_I_; +text: .text%__1cKTypeAryPtrEhash6kM_i_; +text: .text%__1cIIndexSet2t6Mp0_v_; +text: .text%__1cNrFlagsRegOperEtype6kM_pknEType__; +text: .text%__1cHTypeInt2t6Miii_v_; +text: .text%__1cTconstantPoolOopDescbAname_and_type_ref_index_at6Mi_i_; +text: .text%__1cMPhaseChaitinSuse_prior_register6MpnENode_I2pnFBlock_rnJNode_List_6_i_; +text: .text%__1cIGraphKitHstopped6M_i_; +text: .text%__1cKTypeOopPtrJsingleton6kM_i_; +text: .text%__1cENodeQIdeal_DU_postCCP6MpnIPhaseCCP__p0_; +text: .text%__1cNSafePointNodeGpinned6kM_i_; +text: .text%__1cQCompressedStream2t6MpCi_v_; +text: .text%__1cIConLNodeGOpcode6kM_i_; +text: .text%__1cHPhiNodeEhash6kM_I_; +text: .text%__1cGIfNodeGpinned6kM_i_; +text: .text%__1cOis_diamond_phi6FpnENode__i_: cfgnode.o; +text: .text%__1cLTypeInstPtrCeq6kMpknEType__i_; +text: .text%__1cENodeHsize_of6kM_I_; +text: .text%__1cENodeSremove_dead_region6MpnIPhaseGVN_i_i_; +text: .text%__1cHRegMaskMSmearToPairs6M_v_; +text: .text%__1cSCallStaticJavaNodeEhash6kM_I_; +text: .text%jni_GetObjectField: jni.o; +text: .text%__1cJMarkSweepXrevisit_weak_klass_link6FpnFKlass__v_; +text: .text%__1cKklassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cKklassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cFKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cKNode_ArrayEgrow6MI_v_; +text: .text%__1cLklassVtableToop_adjust_pointers6M_v_; +text: .text%__1cLklassVtableToop_follow_contents6M_v_; +text: .text%__1cCosOis_interrupted6FpnGThread_i_i_; +text: .text%__1cICmpINodeGOpcode6kM_i_; +text: .text%__1cHMatcherKLabel_Root6MpknENode_pnFState_p16_6_; +text: .text%__1cMPhaseChaitinHnew_lrg6MpknENode_I_v_; +text: .text%__1cFframeYinterpreter_frame_method6kM_pnNmethodOopDesc__; +text: .text%__1cUGenericGrowableArrayMraw_allocate6Mi_pv_; +text: .text%__1cJMarkSweepNpreserve_mark6FpnHoopDesc_pnLmarkOopDesc__v_; +text: .text%__1cIMachNodeKconst_size6kM_i_; +text: .text%__1cGIfNodeFis_If6M_p0_; +text: .text%__1cINodeHashLhash_insert6MpnENode__v_; +text: .text%__1cOPhaseIdealLoopMis_dominator6MpnENode_2_i_; +text: .text%__1cKTypeOopPtrLxadd_offset6kMi_i_; +text: .text%JVM_Read; +text: .text%__1cDhpiEread6FipvI_L_; +text: .text%__1cIMachNodeIpeephole6MpnFBlock_ipnNPhaseRegAlloc_ri_p0_; +text: .text%__1cMPhaseIterGVNVadd_users_to_worklist6MpnENode__v_; +text: .text%__1cIHaltNodeGis_CFG6kM_i_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__; +text: .text%__1cPClassFileStreamGget_u16MpnGThread__C_; +text: .text%__1cMMergeMemNodeGOpcode6kM_i_; +text: .text%__1cPciObjectFactoryEfind6MpnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cJCodeCacheQfind_blob_unsafe6Fpv_pnICodeBlob__; +text: .text%__1cHemit_d86FrnKCodeBuffer_i_v_; +text: .text%__1cKCodeBuffer2t6MiiiiiipnKBufferBlob_pnJrelocInfo_pnORelocateBuffer_ipnLOopRecorder_pkcii_v_; +text: .text%__1cLOopRecorder2t6MpnFArena__v_; +text: .text%__1cKCodeBuffer2T6M_v_; +text: .text%__1cKCodeBufferQalloc_relocation6MI_v_; +text: .text%__1cIMachNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIMachNodeJemit_size6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLTypeInstPtr2t6MnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_v_; +text: .text%__1cLOptoRuntimeKjbyte_copy6FpW1L_v_; +text: .text%__1cJloadINodeErule6kM_I_; +text: .text%__1cMOopMapStream2t6MpnGOopMap_i_v_; +text: .text%__1cPciObjectFactoryLis_found_at6MipnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode__i_; +text: .text%__1cIAddINodeGOpcode6kM_i_; +text: .text%__1cWShouldNotReachHereNodeMideal_Opcode6kM_i_; +text: .text%__1cOmatch_into_reg6FpnENode_iii1_i_: matcher.o; +text: .text%__1cENodeJis_Branch6kM_I_; +text: .text%__1cITypeLong2t6Mxxi_v_; +text: .text%__1cRconstantPoolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cIJVMStateIof_depth6kMi_p0_; +text: .text%__1cFMutexElock6M_v_; +text: .text%__1cJrRegLOperEtype6kM_pknEType__; +text: .text%__1cENode2t6Mp0_v_; +text: .text%__1cLTypeInstPtrEmake6FnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_pk0_; +text: .text%__1cJStartNodeGpinned6kM_i_; +text: .text%__1cHhashptr6Fpkv_i_; +text: .text%__1cLklassItableToop_adjust_pointers6M_v_; +text: .text%__1cLklassItableToop_follow_contents6M_v_; +text: .text%__1cRconstantPoolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cRconstantPoolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSinstanceKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSinstanceKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNinstanceKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cNinstanceKlassUadjust_static_fields6M_v_; +text: .text%__1cNinstanceKlassUfollow_static_fields6M_v_; +text: .text%__1cFBlockLis_uncommon6kMrnLBlock_Array__i_; +text: .text%__1cEDict2F6kMpkv_pv_; +text: .text%__1cIProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cNCatchProjNodeGOpcode6kM_i_; +text: .text%__1cJCodeCacheJfind_blob6Fpv_pnICodeBlob__; +text: .text%__1cICallNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJdo_method6FpnNmethodOopDesc__v_: recompilationMonitor.o; +text: .text%__1cWconstantPoolCacheKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cKjmpConNodeMideal_Opcode6kM_i_; +text: .text%__1cJLoadPNodeGOpcode6kM_i_; +text: .text%__1cLSymbolTableGlookup6MipkciI_pnNsymbolOopDesc__; +text: .text%__1cWconstantPoolCacheKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWconstantPoolCacheKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cOindOffset8OperJnum_edges6kM_I_; +text: .text%__1cKBufferBlobIis_alive6kM_i_; +text: .text%__1cFframeGsender6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cJTypeTupleJsingleton6kM_i_; +text: .text%__1cKTypeAryPtrCeq6kMpknEType__i_; +text: .text%__1cHPhiNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cETypeKhas_memory6kM_i_; +text: .text%__1cHMatcherKReduceOper6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cGIfNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMachNodeFreloc6kM_i_; +text: .text%__1cPciObjectFactoryDget6MpnHoopDesc__pnIciObject__; +text: .text%__1cOThreadCritical2T6M_v_; +text: .text%__1cOThreadCritical2t6M_v_; +text: .text%__1cFframeOis_first_frame6kM_i_; +text: .text%__1cICodeBlobOis_osr_adapter6kM_i_; +text: .text%__1cHConNodeGis_Con6kM_I_; +text: .text%__1cIAddPNodeKmatch_edge6kMI_I_; +text: .text%__1cILoadNodeEhash6kM_I_; +text: .text%__1cRMachSpillCopyNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cICodeBlobTfix_oop_relocations6MpC1_v_; +text: .text%__1cICodeBlobTfix_oop_relocations6M_v_; +text: .text%__1cIimmIOperIconstant6kM_l_; +text: .text%__1cHCmpNodeGis_Cmp6kM_pk0_; +text: .text%__1cFMutexbClock_without_safepoint_check6M_v_; +text: .text%__1cENodeHdel_req6MI_v_; +text: .text%__1cJCProjNodeEhash6kM_I_; +text: .text%__1cHCompileJcan_alias6MpknHTypePtr_i_i_; +text: .text%__1cJMultiNodeEhash6kM_I_; +text: .text%__1cRSignatureIteratorKparse_type6M_i_; +text: .text%__1cHnmethodIis_alive6kM_i_; +text: .text%__1cIHaltNodeGOpcode6kM_i_; +text: .text%__1cMMergeMemNodeLis_MergeMem6M_p0_; +text: .text%__1cUPSMarkSweepDecoratorQinsert_deadspace6MrlpnIHeapWord_L_i_; +text: .text%__1cHPhiNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMPhaseIterGVNZremove_globally_dead_node6MpnENode__v_; +text: .text%__1cETypeEhash6kM_i_; +text: .text%__1cJHashtableLhash_symbol6Fpkci_I_; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pnIciObject_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cYDebugInformationRecorderLcheck_phase6Mn0AFPhase__v_; +text: .text%__1cOMachReturnNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cFframeVinterpreter_frame_bcp6kM_pC_; +text: .text%__1cICodeBlobKis_nmethod6kM_i_; +text: .text%__1cICmpUNodeGOpcode6kM_i_; +text: .text%__1cJOopMapSetSfind_map_at_offset6kMi_pnGOopMap__; +text: .text%__1cICodeBlobbAoop_map_for_return_address6MpC_pnGOopMap__; +text: .text%__1cIConPNodeGOpcode6kM_i_; +text: .text%__1cIMachNodeHtwo_adr6kM_I_; +text: .text%__1cIParmNodeGOpcode6kM_i_; +text: .text%__1cTconvI2L_reg_regNodeErule6kM_I_; +text: .text%__1cHTypeIntFxmeet6kMpknEType__3_; +text: .text%__1cLAdapterInfoFequal6kMp0_i_; +text: .text%__1cGOopMapbEmap_compiler_reg_to_oopmap_reg6Miii_i_; +text: .text%__1cTconstantPoolOopDescSklass_ref_index_at6Mi_i_; +text: .text%__1cLSymbolTableGlookup6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cKoopFactoryKnew_symbol6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cMloadConINodeMideal_Opcode6kM_i_; +text: .text%__1cGTarjanEEVAL6M_p0_; +text: .text%__1cKRelocationYindex_to_runtime_address6Fl_pC_; +text: .text%__1cYexternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cJCatchNodeGOpcode6kM_i_; +text: .text%__1cZPhaseConservativeCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cITypeLongJsingleton6kM_i_; +text: .text%__1cNencode_RegMem6FrnKCodeBuffer_iiiiii_v_; +text: .text%__1cFBlockGselect6MrnJNode_List_rnLBlock_Array_pirnJVectorSet_IrnNGrowableArray4CI___pnENode__; +text: .text%__1cGOopMapHset_xxx6MinLOopMapValueJoop_types_iii_v_; +text: .text%__1cFMutexNowned_by_self6kM_i_; +text: .text%__1cTCreateExceptionNodeErule6kM_I_; +text: .text%__1cIJVMStateLdebug_start6kM_I_; +text: .text%__1cRCardTableModRefBSEkind6M_nKBarrierSetEName__; +text: .text%__1cMloadConPNodeMideal_Opcode6kM_i_; +text: .text%__1cIMachNodeNoperand_index6kMI_i_; +text: .text%__1cKMachIfNodeJis_MachIf6kM_pk0_; +text: .text%__1cFBlockIis_Empty6kM_i_; +text: .text%__1cMMachTypeNodeLbottom_type6kM_pknEType__; +text: .text%__1cIAddPNodeLbottom_type6kM_pknEType__; +text: .text%__1cFBlockOcode_alignment6M_I_; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_; +text: .text%__1cGBitMapUclear_range_of_words6MLL_v_; +text: .text%__1cGBitMapFclear6M_v_; +text: .text%__1cSinstanceKlassKlassMoop_is_klass6kM_i_; +text: .text%__1cJrRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKCastPPNodeGOpcode6kM_i_; +text: .text%__1cIPhaseIFGMtest_edge_sq6kMII_i_; +text: .text%__1cFframeQoops_do_internal6MpnKOopClosure_pnLRegisterMap_i_v_; +text: .text%__1cNinstanceKlassLfind_method6FpnPobjArrayOopDesc_pnNsymbolOopDesc_4_pnNmethodOopDesc__; +text: .text%__1cFStateRMachNodeGenerator6MipnHCompile__pnIMachNode__; +text: .text%__1cHMatcherKReduceInst6MpnFState_irpnENode__pnIMachNode__; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__; +text: .text%__1cPClassFileStreamHskip_u16MipnGThread__v_; +text: .text%__1cMMachCallNodeGpinned6kM_i_; +text: .text%__1cHnmethodJis_zombie6kM_i_; +text: .text%__1cMMergeMemNodeLbottom_type6kM_pknEType__; +text: .text%__1cNinstanceKlassLfind_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cKTypeAryPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cKCodeBufferIrelocate6MpCrknQRelocationHolder_i_v_; +text: .text%__1cILoadNodeLbottom_type6kM_pknEType__; +text: .text%__1cLConvI2LNodeGOpcode6kM_i_; +text: .text%__1cHTypeIntEmake6Fi_pk0_; +text: .text%__1cbFCompressedLineNumberWriteStreamKwrite_pair6Mii_v_; +text: .text%__1cJMultiNodeIproj_out6kMI_pnIProjNode__; +text: .text%__1cFKlassMoop_is_array6kM_i_; +text: .text%__1cIBoolNodeEhash6kM_I_; +text: .text%__1cIimmPOperEtype6kM_pknEType__; +text: .text%__1cMloadConPNodeLbottom_type6kM_pknEType__; +text: .text%__1cJrRegPOperJnum_edges6kM_I_; +text: .text%__1cOrFlagsRegUOperEtype6kM_pknEType__; +text: .text%__1cGIfNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNsymbolOopDescLas_C_string6kM_pc_; +text: .text%__1cNsymbolOopDescLas_C_string6kMpci_1_; +text: .text%__1cGOopMapJset_value6Miii_v_; +text: .text%__1cITypeLongFxmeet6kMpknEType__3_; +text: .text%__1cNCollectedHeapbHcheck_for_non_bad_heap_word_value6MpnIHeapWord_L_v_; +text: .text%__1cHMatcherTReduceInst_Interior6MpnFState_ipnIMachNode_IrpnENode__I_; +text: .text%__1cNsymbolOopDescGequals6kMpkci_i_; +text: .text%__1cMPhaseChaitinLinsert_proj6MpnFBlock_IpnENode_I_v_; +text: .text%__1cKjmpConNodeGpinned6kM_i_; +text: .text%__1cNSafePointNodebBneeds_polling_address_input6F_i_; +text: .text%__1cHCompileRprobe_alias_cache6MpknHTypePtr__pn0APAliasCacheEntry__; +text: .text%__1cHnmethodOis_not_entrant6kM_i_; +text: .text%__1cIAddPNodeHis_AddP6M_p0_; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6FnTobjArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cIMachNodeGExpand6MpnFState_rnJNode_List__p0_; +text: .text%__1cXInterpreterFrameClosureJoffset_do6Mi_v_; +text: .text%__1cNinstanceKlassQarray_klass_impl6FnTinstanceKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cENodeIdestruct6M_v_; +text: .text%__1cIAddPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPClassFileParserbEparse_constant_pool_utf8_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserRverify_legal_utf86MpkCipnGThread__v_; +text: .text%__1cMMachHaltNodeEjvms6kM_pnIJVMState__; +text: .text%__1cKoopFactoryMnew_objArray6FpnMklassOopDesc_ipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassRallocate_objArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cGBitMapJset_union6M0_v_; +text: .text%__1cPciInstanceKlassMis_interface6M_i_; +text: .text%__1cHTypeIntEmake6Fiii_pk0_; +text: .text%__1cJTypeTupleEhash6kM_i_; +text: .text%__1cFParsePdo_one_bytecode6M_v_; +text: .text%__1cFParseNdo_exceptions6M_v_; +text: .text%__1cKRegionNodeEhash6kM_I_; +text: .text%__1cMMutableSpaceIallocate6ML_pnIHeapWord__; +text: .text%__1cUParallelScavengeHeapWpermanent_mem_allocate6ML_pnIHeapWord__; +text: .text%__1cJPSPermGenSallocate_permanent6ML_pnIHeapWord__; +text: .text%__1cPmethodDataKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cIHaltNodeKmatch_edge6kMI_I_; +text: .text%__1cENodeOis_block_start6kM_i_; +text: .text%__1cHMatcherQis_save_on_entry6Mi_i_; +text: .text%__1cLsymbolKlassNoop_is_symbol6kM_i_; +text: .text%__1cITypeLongEmake6Fxxi_pk0_; +text: .text%__1cPmethodDataKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cPmethodDataKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMPhaseIterGVNKis_IterGVN6M_p0_; +text: .text%__1cGBitMap2t6MpLL_v_; +text: .text%__1cLOptoRuntimePnew_typeArray_C6FnJBasicType_ipnKJavaThread__v_; +text: .text%__1cHCompilePfind_alias_type6MpknHTypePtr_i_pn0AJAliasType__; +text: .text%__1cNnew_loc_value6FpnNPhaseRegAlloc_inILocationEType__pnNLocationValue__: output.o; +text: .text%__1cKjmpDirNodePoper_input_base6kM_I_; +text: .text%__1cMMachCallNodeLis_MachCall6M_p0_; +text: .text%__1cHPhiNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cXjava_lang_ref_ReferenceNreferent_addr6FpnHoopDesc__p2_; +text: .text%__1cOindOffset8OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOindOffset8OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cZPhaseConservativeCoalesceJcopy_copy6MpnENode_2pnFBlock_I_i_; +text: .text%__1cKutf8_write6FpCH_0_: utf8.o; +text: .text%__1cKNode_ArrayGremove6MI_v_; +text: .text%__1cTconstantPoolOopDescQsignature_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cHPhiNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRSignatureIterator2t6MnMsymbolHandle__v_; +text: .text%__1cJloadPNodeMideal_Opcode6kM_i_; +text: .text%__1cPSignatureStreamEnext6M_v_; +text: .text%__1cOindOffset8OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLjmpConUNodeMideal_Opcode6kM_i_; +text: .text%__1cFBlockJfind_node6kMpknENode__I_; +text: .text%__1cOis_range_check6FpnENode_r12ri_i_: ifnode.o; +text: .text%__1cENodeIis_Start6M_pnJStartNode__; +text: .text%__1cHhashkey6Fpkv_i_; +text: .text%__1cNLoadRangeNodeGOpcode6kM_i_; +text: .text%__1cJLoadINodeGOpcode6kM_i_; +text: .text%__1cKis_x2logic6FpnIPhaseGVN_pnENode__3_: cfgnode.o; +text: .text%__1cHAbsNodeLis_absolute6FpnIPhaseGVN_pnENode__4_; +text: .text%__1cGTarjanICOMPRESS6M_v_; +text: .text%__1cTconstantPoolOopDescNklass_at_impl6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cENodeHis_Goto6kM_I_; +text: .text%__1cLPCTableNodeGpinned6kM_i_; +text: .text%JVM_ReleaseUTF; +text: .text%__1cHSubNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFChunk2t6ML_v_; +text: .text%__1cFChunk2n6FLL_pv_; +text: .text%__1cOindOffset8OperFscale6kM_i_; +text: .text%__1cMCreateExNodeGOpcode6kM_i_; +text: .text%__1cFframebCsender_for_interpreter_frame6kMpnLRegisterMap__0_; +text: .text%__1cFChunk2k6Fpv_v_; +text: .text%__1cOMachReturnNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cJrelocInfo2t6Mn0AJrelocType_ii_v_; +text: .text%__1cHMatcherKmatch_tree6MpknENode__pnIMachNode__; +text: .text%__1cLTypeInstPtrFxmeet6kMpknEType__3_; +text: .text%__1cKjmpConNodePoper_input_base6kM_I_; +text: .text%__1cIJVMState2t6MpnIciMethod_p0_v_; +text: .text%__1cMciMethodDataHdata_at6Mi_pnLProfileData__; +text: .text%__1cJrelocInfoNfinish_prefix6Mph_p0_; +text: .text%__1cIProjNodeHsize_of6kM_I_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__; +text: .text%__1cLTypeInstPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cHBitDataKcell_count6M_i_; +text: .text%__1cFframeZsender_for_compiled_frame6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cFArenaEgrow6ML_pv_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cMPhaseIterGVNMsubsume_node6MpnENode_2_v_; +text: .text%__1cLBoxLockNodeNrematerialize6kM_i_; +text: .text%__1cRMachSafePointNodeQis_MachSafePoint6M_p0_; +text: .text%__1cJloadBNodeErule6kM_I_; +text: .text%__1cITypeNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cHConNodeEhash6kM_I_; +text: .text%__1cICodeBlobLlink_offset6M_i_; +text: .text%__1cENodeRdisconnect_inputs6Mp0_i_; +text: .text%__1cIHaltNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cICodeBlobOis_i2c_adapter6kM_i_; +text: .text%__1cTconstantPoolOopDescWsignature_ref_index_at6Mi_i_; +text: .text%__1cIJVMStateNclone_shallow6kM_p0_; +text: .text%__1cRMachSpillCopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGOopMapQset_callee_saved6Miiii_v_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__; +text: .text%__1cJOopMapSetTupdate_register_map6FpknFframe_pnICodeBlob_pnLRegisterMap__v_; +text: .text%__1cIJVMStateJdebug_end6kM_I_; +text: .text%__1cXjava_lang_ref_ReferenceJnext_addr6FpnHoopDesc__p2_; +text: .text%__1cENode2t6Mp011_v_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__; +text: .text%__1cHMatcherTcollect_null_checks6MpnENode__v_; +text: .text%__1cNSafePointNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHMemNodeMIdeal_common6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cGBitMapGat_put6MLi_v_; +text: .text%__1cFciEnvIis_in_vm6F_i_; +text: .text%__1cNSafePointNodeHsize_of6kM_I_; +text: .text%__1cMgetTimeNanos6F_x_; +text: .text%__1cKciTypeFlowLStateVectorSapply_one_bytecode6MpnQciBytecodeStream__i_; +text: .text%__1cKPSScavengeUoop_promotion_failed6FpnHoopDesc_pnLmarkOopDesc__v_; +text: .text%__1cHTypePtrLmeet_offset6kMi_i_; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_; +text: .text%__1cNPhaseRegAllocGis_oop6kMpknENode__i_; +text: .text%__1cSPSPromotionManagerUoop_promotion_failed6MpnHoopDesc_pnLmarkOopDesc__2_; +text: .text%__1cIMachOperLdisp_is_oop6kM_i_; +text: .text%__1cRSignatureIteratorSiterate_parameters6M_v_; +text: .text%__1cNmethodOopDescIbci_from6kMpC_i_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_; +text: .text%__1cLLShiftLNodeGOpcode6kM_i_; +text: .text%__1cOMethodLivenessKBasicBlockIload_one6Mi_v_; +text: .text%__1cJTypeTupleCeq6kMpknEType__i_; +text: .text%__1cHCmpNodeLbottom_type6kM_pknEType__; +text: .text%__1cFDictI2i6M_v_; +text: .text%__1cPjava_lang_ClassMis_primitive6FpnHoopDesc__i_; +text: .text%JVM_GetMethodIxExceptionTableLength; +text: .text%__1cKC2IAdapterIis_alive6kM_i_; +text: .text%__1cENodeHget_int6kM_i_; +text: .text%__1cMMachCallNodeLbottom_type6kM_pknEType__; +text: .text%__1cOGenerateOopMapHinterp16MpnOBytecodeStream__v_; +text: .text%__1cIAddPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMloadConINodeLbottom_type6kM_pknEType__; +text: .text%__1cPCheckCastPPNodeGOpcode6kM_i_; +text: .text%__1cMloadConINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeXis_iteratively_computed6M_i_; +text: .text%__1cNloadConI0NodePoper_input_base6kM_I_; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_; +text: .text%__1cENodeKreplace_by6Mp0_v_; +text: .text%__1cHPhiNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cIBoolTestKcc2logical6kMpknEType__3_; +text: .text%__1cIBoolNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJrelocInfoKset_format6Mi_v_; +text: .text%__1cXruntime_call_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cKRelocationJpack_data6M_i_; +text: .text%__1cXjava_lang_ref_ReferencePdiscovered_addr6FpnHoopDesc__p2_; +text: .text%__1cNPhaseCoalesceRcombine_these_two6MpnENode_2_v_; +text: .text%__1cNtestP_regNodeMideal_Opcode6kM_i_; +text: .text%__1cETypeFempty6kM_i_; +text: .text%__1cHMemNodeGis_Mem6M_p0_; +text: .text%__1cZload_can_see_stored_value6FpnILoadNode_pnENode_pnOPhaseTransform__3_: memnode.o; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cICodeBlobJis_zombie6kM_i_; +text: .text%__1cJTypeTupleGfields6FI_ppknEType__; +text: .text%__1cOGenerateOopMapPjump_targets_do6MpnOBytecodeStream_pFp0ipi_v4_i_; +text: .text%__1cPClassFileParserUassemble_annotations6MpCi1ipnGThread__nPtypeArrayHandle__; +text: .text%method_compare: methodOop.o; +text: .text%__1cMMergeMemNodeEhash6kM_I_; +text: .text%__1cJloadPNodePoper_input_base6kM_I_; +text: .text%__1cILoadNodeKmatch_edge6kMI_I_; +text: .text%__1cFBlockUneeded_for_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cIIndexSetSpopulate_free_list6F_v_; +text: .text%__1cGIfNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cCosPelapsed_counter6F_x_; +text: .text%__1cIAddPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIemit_d326FrnKCodeBuffer_i_v_; +text: .text%__1cNloadConI0NodeHtwo_adr6kM_I_; +text: .text%__1cJStoreNodeKmatch_edge6kMI_I_; +text: .text%__1cITypeNodeDcmp6kMrknENode__I_; +text: .text%__1cMPhaseChaitinFUnion6MpknENode_3_v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_; +text: .text%__1cRSignatureIteratorTcheck_signature_end6M_v_; +text: .text%__1cRSignatureIteratorSiterate_returntype6M_v_; +text: .text%__1cNinstanceKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cHnmethodKis_nmethod6kM_i_; +text: .text%__1cILoadNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNSafePointNodeKmatch_edge6kMI_I_; +text: .text%__1cPClassFileStreamGget_u46MpnGThread__I_; +text: .text%__1cSCallStaticJavaNodeRis_CallStaticJava6kM_pk0_; +text: .text%__1cKarrayKlassLobject_size6kMi_i_; +text: .text%__1cPciInstanceKlassRis_instance_klass6M_i_; +text: .text%__1cOno_flip_branch6FpnFBlock__i_: block.o; +text: .text%__1cKarrayKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cYCallStaticJavaDirectNodePoper_input_base6kM_I_; +text: .text%__1cOGenerateOopMapEpush6MnNCellTypeState__v_; +text: .text%__1cKjmpDirNodeGpinned6kM_i_; +text: .text%__1cTconstantPoolOopDescLname_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cKjmpDirNodeHtwo_adr6kM_I_; +text: .text%__1cPciInstanceKlassGloader6M_pnHoopDesc__; +text: .text%__1cJrRegLOperJnum_edges6kM_I_; +text: .text%__1cOGenerateOopMapDpop6M_nNCellTypeState__; +text: .text%__1cNGrowableArray4CpnKScopeValue__2t6Mii_v_; +text: .text%__1cYDebugInformationRecorderWserialize_scope_values6MpnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cYDebugInformationRecorderTcreate_scope_values6MpnNGrowableArray4CpnKScopeValue____pnKDebugToken__; +text: .text%__1cITypeNodeHsize_of6kM_I_; +text: .text%__1cILoadNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPPerfLongVariantGsample6M_v_; +text: .text%__1cNloadConI0NodeErule6kM_I_; +text: .text%__1cJCodeCacheIcontains6Fpv_i_; +text: .text%__1cQciBytecodeStreamMreset_to_bci6Mi_v_; +text: .text%__1cJOopMapSetGall_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure_pFppnHoopDesc_9E_v9B9B_v_; +text: .text%__1cFframeRoops_code_blob_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cJOopMapSetHoops_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cHPhiNodeHsize_of6kM_I_; +text: .text%__1cSInterpreterRuntimeJanewarray6FpnKJavaThread_pnTconstantPoolOopDesc_ii_v_; +text: .text%__1cMPhaseChaitinNFind_compress6MI_I_; +text: .text%__1cIMachNodeRget_base_and_disp6kMrlrpknHTypePtr__pknENode__; +text: .text%__1cLOopMapCacheIentry_at6kMi_pnQOopMapCacheEntry__; +text: .text%__1cOGenerateOopMapKcheck_type6MnNCellTypeState_1_v_; +text: .text%__1cPciInstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cSobjArrayKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cILoadNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMPhaseIterGVNbGregister_new_node_with_optimizer6MpnENode__2_; +text: .text%__1cKBufferBlobMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cKBufferBlobHoops_do6MpnKOopClosure__v_; +text: .text%__1cLRegisterMapFclear6M_v_; +text: .text%__1cIBoolNodeLbottom_type6kM_pknEType__; +text: .text%__1cITypeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKRegionNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLBlock_Array2t6MpnFArena__v_; +text: .text%__1cPSignatureStreamHis_done6kM_i_; +text: .text%__1cNrFlagsRegOperJnum_edges6kM_I_; +text: .text%__1cLPhaseValuesGintcon6Mi_pnIConINode__; +text: .text%__1cKStoreINodeGOpcode6kM_i_; +text: .text%__1cLRegisterMap2t6MpnKJavaThread_i_v_; +text: .text%__1cNmethodOopDescPis_empty_method6kM_i_; +text: .text%__1cJcmpOpOperJnum_edges6kM_I_; +text: .text%__1cNExceptionMark2t6MrpnGThread__v_; +text: .text%__1cNExceptionMark2T6M_v_; +text: .text%__1cCosMvm_page_size6F_i_; +text: .text%__1cJloadPNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cOcompU_rRegNodePoper_input_base6kM_I_; +text: .text%__1cNmethodOopDescLis_accessor6kM_i_; +text: .text%__1cPloadConUL32NodePoper_input_base6kM_I_; +text: .text%__1cMPhaseChaitinSget_spillcopy_wide6MpnENode_2I_2_; +text: .text%__1cKI2CAdapterIis_alive6kM_i_; +text: .text%__1cHnmethodHoops_do6MpnKOopClosure__v_; +text: .text%__1cIGraphKitJsync_jvms6kM_pnIJVMState__; +text: .text%__1cFframebFinterpreter_frame_monitor_begin6kM_pnPBasicObjectLock__; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__; +text: .text%__1cQSystemDictionaryXcheck_signature_loaders6FnMsymbolHandle_nGHandle_2ipnGThread__v_; +text: .text%__1cIGraphKitEstop6M_v_; +text: .text%__1cNidealize_test6FpnIPhaseGVN_pnGIfNode__3_: ifnode.o; +text: .text%__1cPloadConUL32NodeHtwo_adr6kM_I_; +text: .text%__1cNSafePointNodeLbottom_type6kM_pknEType__; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_; +text: .text%__1cRSignatureIterator2t6MpnNsymbolOopDesc__v_; +text: .text%__1cKStorePNodeGOpcode6kM_i_; +text: .text%__1cHMonitorKnotify_all6M_i_; +text: .text%__1cFframeVinterpreter_frame_bci6kM_i_; +text: .text%__1cHRetNodeNis_block_proj6kM_pknENode__; +text: .text%__1cMPhaseChaitinMyank_if_dead6MpnENode_pnFBlock_pnJNode_List_6_i_; +text: .text%__1cENodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFBlockLfind_remove6MpknENode__v_; +text: .text%__1cOGenerateOopMapRdo_exception_edge6MpnOBytecodeStream__v_; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_; +text: .text%__1cGOopMapHset_oop6Miii_v_; +text: .text%__1cNSafePointNodeSset_next_exception6Mp0_v_; +text: .text%__1cPloadConUL32NodeErule6kM_I_; +text: .text%__1cFChunkEchop6M_v_; +text: .text%__1cMciMethodDataJnext_data6MpnLProfileData__2_; +text: .text%__1cJStoreNodeLbottom_type6kM_pknEType__; +text: .text%__1cPciObjectFactorySget_unloaded_klass6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cLBlock_StackXmost_frequent_successor6MpnFBlock__I_; +text: .text%__1cFBlockScall_catch_cleanup6MrnLBlock_Array__v_; +text: .text%__1cFBlockOschedule_local6MrnHMatcher_rnLBlock_Array_pirnJVectorSet_rnNGrowableArray4CI___i_; +text: .text%__1cXPhaseAggressiveCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cOGenerateOopMapFppop16MnNCellTypeState__v_; +text: .text%__1cKstorePNodePoper_input_base6kM_I_; +text: .text%__1cMPhaseIterGVNFwiden6kMpknEType_3_3_; +text: .text%__1cKRegionNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNmethodOopDescRis_not_compilable6kMi_i_; +text: .text%__1cLis_cond_add6FpnIPhaseGVN_pnHPhiNode__pnENode__; +text: .text%__1cPsplit_flow_path6FpnIPhaseGVN_pnHPhiNode__pnENode__: cfgnode.o; +text: .text%__1cKRegionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGTarjanELINK6Mp01_v_; +text: .text%__1cKTypeRawPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cWMutableSpaceUsedHelperLtake_sample6M_x_; +text: .text%__1cIBoolNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIsplit_if6FpnGIfNode_pnMPhaseIterGVN__pnENode__: ifnode.o; +text: .text%__1cTremove_useless_bool6FpnGIfNode_pnIPhaseGVN__pnENode__: ifnode.o; +text: .text%__1cMPhaseChaitinNFind_compress6MpknENode__I_; +text: .text%__1cRInterpreterOopMapKinitialize6M_v_; +text: .text%__1cJTypeTupleEmake6FIppknEType__pk0_; +text: .text%__1cYCallStaticJavaDirectNodeHtwo_adr6kM_I_; +text: .text%__1cKDictionaryJget_entry6MiInMsymbolHandle_nGHandle__pnPDictionaryEntry__; +text: .text%__1cLLShiftINodeGOpcode6kM_i_; +text: .text%__1cQResultTypeFinderDset6MinJBasicType__v_; +text: .text%__1cHnmethodMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cNmethodOopDescLresult_type6kM_nJBasicType__; +text: .text%__1cIMachOperOindex_position6kM_i_; +text: .text%__1cKReflectionTverify_field_access6FpnMklassOopDesc_22nLAccessFlags_ii_i_; +text: .text%__1cOemit_d32_reloc6FrnKCodeBuffer_irknQRelocationHolder_i_v_; +text: .text%__1cLjmpConUNodePoper_input_base6kM_I_; +text: .text%__1cHAddNodeEhash6kM_I_; +text: .text%__1cNtestP_regNodePoper_input_base6kM_I_; +text: .text%__1cHSubNodeGis_Sub6M_p0_; +text: .text%__1cPcheckCastPPNodePoper_input_base6kM_I_; +text: .text%__1cIRootNodeGOpcode6kM_i_; +text: .text%__1cFframebDinterpreter_frame_monitor_end6kM_pnPBasicObjectLock__; +text: .text%__1cNmethodOopDescImask_for6MipnRInterpreterOopMap__v_; +text: .text%__1cNinstanceKlassImask_for6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cRInterpreterOopMapLiterate_oop6MpnNOffsetClosure__v_; +text: .text%__1cRInterpreterOopMap2T6M_v_; +text: .text%__1cRInterpreterOopMapNresource_copy6MpnQOopMapCacheEntry__v_; +text: .text%__1cLOopMapCacheGlookup6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cRInterpreterOopMap2t6M_v_; +text: .text%__1cFframeToops_interpreted_do6MpnKOopClosure_pknLRegisterMap_i_v_; +text: .text%__1cSInterpreterRuntimeLcache_entry6FpnKJavaThread__pnWConstantPoolCacheEntry__; +text: .text%__1cOindOffset8OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOJNIHandleBlockHoops_do6MpnKOopClosure__v_; +text: .text%__1cKstorePNodeMideal_Opcode6kM_i_; +text: .text%__1cFChunkJnext_chop6M_v_; +text: .text%__1cENode2t6Mp0111_v_; +text: .text%__1cITypeLongEmake6Fx_pk0_; +text: .text%__1cLjmpConUNodeGpinned6kM_i_; +text: .text%__1cPciObjectFactoryNfind_non_perm6MpnHoopDesc__rpn0ANNonPermObject__; +text: .text%__1cHCmpNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKTypeRawPtrJsingleton6kM_i_; +text: .text%__1cNGCTaskManagerNresource_flag6MI_i_; +text: .text%__1cNGCTaskManagerYshould_release_resources6MI_i_; +text: .text%__1cIAddINodeLbottom_type6kM_pknEType__; +text: .text%__1cNloadRangeNodeErule6kM_I_; +text: .text%__1cNrFlagsRegOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIIndexSetJlrg_union6MIIIpknIPhaseIFG_rknHRegMask__I_; +text: .text%__1cISubINodeGOpcode6kM_i_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_; +text: .text%__1cOBasicHashtableJnew_entry6MI_pnTBasicHashtableEntry__; +text: .text%__1cJHashtableJnew_entry6MIpnHoopDesc__pnOHashtableEntry__; +text: .text%__1cSCountedLoopEndNodeGOpcode6kM_i_; +text: .text%__1cTOopMapForCacheEntryRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cKstoreINodePoper_input_base6kM_I_; +text: .text%__1cNtestP_regNodeHtwo_adr6kM_I_; +text: .text%__1cCosVcurrent_stack_pointer6F_pC_; +text: .text%__1cMMergeMemNodePiteration_setup6Mpk0_v_; +text: .text%__1cRMachSafePointNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cJStoreNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMMergeMemNodeJmemory_at6kMI_pnENode__; +text: .text%__1cJloadSNodeErule6kM_I_; +text: .text%__1cLLShiftLNodeLbottom_type6kM_pknEType__; +text: .text%__1cMBasicAdapterHoops_do6MpnKOopClosure__v_; +text: .text%__1cKjmpConNodeJnum_opnds6kM_I_; +text: .text%__1cLOptoRuntimeOnew_objArray_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cTconstantPoolOopDescSklass_at_if_loaded6FnSconstantPoolHandle_i_pnMklassOopDesc__; +text: .text%__1cFciEnvXget_klass_by_index_impl6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cKjmpDirNodeHsize_of6kM_I_; +text: .text%__1cJloadPNodeJnum_opnds6kM_I_; +text: .text%__1cJCProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJMultiNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cHOopFlowNcompute_reach6MpnNPhaseRegAlloc_ipnEDict__v_; +text: .text%__1cJCatchNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cGGCTaskKinitialize6M_v_; +text: .text%__1cNGCTaskManagerIget_task6MI_pnGGCTask__; +text: .text%__1cNGCTaskManagerWdecrement_busy_workers6M_I_; +text: .text%__1cLGCTaskQdDueueGremove6M_pnGGCTask__; +text: .text%__1cLGCTaskQdDueueHenqueue6MpnGGCTask__v_; +text: .text%__1cNGCTaskManagerWincrement_busy_workers6M_I_; +text: .text%__1cLGCTaskQdDueueHdequeue6M_pnGGCTask__; +text: .text%__1cNGCTaskManagerPnote_completion6MI_v_; +text: .text%__1cHMatcherXadjust_outgoing_stk_arg6Miiri_i_; +text: .text%__1cTCreateExceptionNodeMideal_Opcode6kM_i_; +text: .text%__1cKIfTrueNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cXvirtual_call_RelocationLunpack_data6M_v_; +text: .text%__1cMBasicAdapterMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cXindIndexScaleOffsetOperJnum_edges6kM_I_; +text: .text%__1cLRShiftINodeGOpcode6kM_i_; +text: .text%__1cJStoreNodeIis_Store6kM_pk0_; +text: .text%Unsafe_CompareAndSwapLong; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cNtestI_regNodeMideal_Opcode6kM_i_; +text: .text%__1cTAbstractInterpreterLmethod_kind6FnMmethodHandle__n0AKMethodKind__; +text: .text%__1cJStoreNodeEhash6kM_I_; +text: .text%__1cOcompI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cKmethodOperGmethod6kM_l_; +text: .text%__1cHTypeAryRary_must_be_exact6kM_i_; +text: .text%__1cKstoreINodeMideal_Opcode6kM_i_; +text: .text%__1cGGCTask2t6M_v_; +text: .text%__1cOcompU_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cNSafePointNodeOnext_exception6kM_p0_; +text: .text%__1cOindOffset8OperNbase_position6kM_i_; +text: .text%__1cOindOffset8OperNconstant_disp6kM_i_; +text: .text%__1cKjmpDirNodeHis_Goto6kM_I_; +text: .text%__1cENodeHset_req6MIp0_v_; +text: .text%__1cTconstantPoolOopDescNklass_name_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cSObjectSynchronizerKfast_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cSObjectSynchronizerJfast_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cWShouldNotReachHereNodePoper_input_base6kM_I_; +text: .text%__1cMMergeMemNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cGThreadLis_in_stack6kMpC_i_; +text: .text%__1cLIfFalseNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKJavaThreadNis_lock_owned6kMpC_i_; +text: .text%__1cXAdaptiveWeightedAverageYcompute_adaptive_average6Mff_f_; +text: .text%__1cOcompU_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cJloadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvI2L_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cSPSPromotionManagerbBgc_thread_promotion_manager6Fi_p0_; +text: .text%__1cNmethodOopDescMintrinsic_id6kM_nMvmIntrinsicsCID__; +text: .text%__1cKstorePNodeJnum_opnds6kM_I_; +text: .text%__1cQinstanceRefKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQinstanceRefKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMMergeMemNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLciSignatureLreturn_type6kM_pnGciType__; +text: .text%__1cMMergeMemNodeQclone_all_memory6FpnENode__p0_; +text: .text%__1cIGraphKitJclone_map6M_pnNSafePointNode__; +text: .text%__1cLMachNopNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJchar2type6Fc_nJBasicType__; +text: .text%__1cJFieldTypeKbasic_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cKRelocationLspec_simple6FnJrelocInfoJrelocType__nQRelocationHolder__; +text: .text%__1cICallNodeLis_CallLeaf6kM_pknMCallLeafNode__; +text: .text%__1cLrecord_bias6FpknIPhaseIFG_ii_v_: coalesce.o; +text: .text%__1cJrRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMObjectLocker2t6MnGHandle_pnGThread__v_; +text: .text%__1cMObjectLocker2T6M_v_; +text: .text%__1cNloadRangeNodeMideal_Opcode6kM_i_; +text: .text%__1cRMachSafePointNodeRis_safepoint_node6kM_i_; +text: .text%__1cHTypeIntFempty6kM_i_; +text: .text%__1cRInvocationCounterJset_state6Mn0AFState__v_; +text: .text%__1cRInvocationCounterFreset6M_v_; +text: .text%__1cKTypeOopPtrWmake_from_klass_common6FpnHciKlass_ii_pk0_; +text: .text%__1cRInvocationCounterEinit6M_v_; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_; +text: .text%__1cLPhaseValuesFwiden6kMpknEType_3_3_; +text: .text%__1cTconstantPoolOopDescRname_ref_index_at6Mi_i_; +text: .text%__1cIHaltNodeLbottom_type6kM_pknEType__; +text: .text%__1cRRawBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cNtestI_regNodeHtwo_adr6kM_I_; +text: .text%__1cSsafePoint_pollNodeMideal_Opcode6kM_i_; +text: .text%__1cLOopRecorderOallocate_index6MpnI_jobject__i_; +text: .text%__1cYDebugInformationRecorderNappend_handle6MpnI_jobject__i_; +text: .text%__1cYCallStaticJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYCallStaticJavaDirectNodeSalignment_required6kM_i_; +text: .text%__1cMloadConPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGPcDescHreal_pc6kMpknHnmethod__pC_; +text: .text%__1cKRegionNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOPhaseIdealLoopIsplit_up6MpnENode_22_i_; +text: .text%__1cWConstantPoolCacheEntryRset_initial_state6Mi_v_; +text: .text%__1cKNativeCallGverify6M_v_; +text: .text%__1cNCompileBrokerLmaybe_block6F_v_; +text: .text%__1cFPhase2t6Mn0ALPhaseNumber__v_; +text: .text%__1cJloadINodeMideal_Opcode6kM_i_; +text: .text%__1cNLoadKlassNodeGOpcode6kM_i_; +text: .text%__1cIBoolNodeKmatch_edge6kMI_I_; +text: .text%__1cITypeLongEmake6Fxx_pk0_; +text: .text%__1cKRegionNodeOis_block_start6kM_i_; +text: .text%__1cCosGmalloc6FL_pv_; +text: .text%__1cXAdaptiveWeightedAverageGsample6Mf_v_; +text: .text%__1cFStateM_sub_Op_RegP6MpknENode__v_; +text: .text%__1cPcheckCastPPNodeHtwo_adr6kM_I_; +text: .text%__1cRMachNullCheckNodeQis_MachNullCheck6M_p0_; +text: .text%__1cOGenerateOopMapGppush16MnNCellTypeState__v_; +text: .text%__1cGBitMapOset_difference6M0_v_; +text: .text%__1cMvalue_of_loc6FppnHoopDesc__l_; +text: .text%__1cRmethodDataOopDescTbytecode_cell_count6FnJBytecodesECode__i_; +text: .text%__1cRmethodDataOopDescRcompute_data_size6FpnOBytecodeStream__i_; +text: .text%__1cRmethodDataOopDescPinitialize_data6MpnOBytecodeStream_i_i_; +text: .text%__1cOcompI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cLSymbolTableJbasic_add6MipCiIpnGThread__pnNsymbolOopDesc__; +text: .text%__1cLsymbolKlassPallocate_symbol6MpCipnGThread__pnNsymbolOopDesc__; +text: .text%__1cNGrowableArray4CpnKciTypeFlowFBlock__2t6MpnFArena_iirk2_v_; +text: .text%__1cLPhaseValuesHlongcon6Mx_pnIConLNode__; +text: .text%__1cKDictionaryEfind6MiInMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cOcompI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cKTypeAryPtrFxmeet6kMpknEType__3_; +text: .text%__1cMPhaseChaitinJsplit_USE6MpnENode_pnFBlock_2IIiinNGrowableArray4CI__i_I_; +text: .text%__1cNCatchProjNodeMis_CatchProj6kM_pk0_; +text: .text%__1cOcompU_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cNSafePointNodeGOpcode6kM_i_; +text: .text%__1cTconvI2L_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cMTypeKlassPtrEhash6kM_i_; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverYlookup_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cMciMethodDataLbci_to_data6Mi_pnLProfileData__; +text: .text%JVM_GetCPMethodSignatureUTF; +text: .text%__1cPfieldDescriptorKinitialize6MpnMklassOopDesc_i_v_; +text: .text%__1cIciMethodbCinterpreter_invocation_count6M_i_; +text: .text%__1cMMergeMemNodeNset_memory_at6MIpnENode__v_; +text: .text%JVM_CurrentThread; +text: .text%__1cPClassFileParserbLparse_constant_pool_nameandtype_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKstoreINodeJnum_opnds6kM_I_; +text: .text%__1cHTypeAryEhash6kM_i_; +text: .text%JVM_GetClassModifiers; +text: .text%JVM_GetClassAccessFlags; +text: .text%__1cRAbstractAssemblerGa_byte6Mi_v_; +text: .text%__1cJAssemblerGprefix6Mn0AGPrefix__v_; +text: .text%__1cKMemBarNodeKmatch_edge6kMI_I_; +text: .text%__1cMMergeMemNodePset_base_memory6MpnENode__v_; +text: .text%__1cRAbstractAssembler2t6MpnKCodeBuffer__v_; +text: .text%__1cCosEfree6Fpv_v_; +text: .text%__1cENodeDcmp6kMrk0_I_; +text: .text%__1cMLinkResolverbAcheck_method_accessability6FnLKlassHandle_11nMmethodHandle_pnGThread__v_; +text: .text%__1cParrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cParrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMrax_RegPOperEtype6kM_pknEType__; +text: .text%__1cKTypeRawPtrEhash6kM_i_; +text: .text%__1cLmethodKlassNoop_is_method6kM_i_; +text: .text%__1cXindIndexScaleOffsetOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cPSignatureStreamJis_object6kM_i_; +text: .text%__1cVCompressedWriteStream2t6Mi_v_; +text: .text%__1cENode2t6Mp01_v_; +text: .text%__1cHAddNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJStartNodeIis_Start6M_p0_; +text: .text%__1cMURShiftINodeGOpcode6kM_i_; +text: .text%__1cNGrowableArray4CpnMMonitorValue__2t6Mii_v_; +text: .text%__1cYDebugInformationRecorderOdescribe_scope6MpnIciMethod_ipnKDebugToken_44_v_; +text: .text%__1cYDebugInformationRecorderVcreate_monitor_values6MpnNGrowableArray4CpnMMonitorValue____pnKDebugToken__; +text: .text%__1cYDebugInformationRecorderYserialize_monitor_values6MpnNGrowableArray4CpnMMonitorValue____i_; +text: .text%__1cMloadConINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMachNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cKReflectionTverify_class_access6FpnMklassOopDesc_2i_i_; +text: .text%__1cOMethodLivenessKBasicBlockWcompute_gen_kill_range6MpnQciBytecodeStream__v_; +text: .text%__1cJAssemblerJemit_data6MirknQRelocationHolder_i_v_; +text: .text%__1cIMachOperMdisp_as_type6kM_pknHTypePtr__; +text: .text%__1cKCompiledICWis_in_transition_state6kM_i_; +text: .text%__1cRInlineCacheBufferIcontains6FpC_i_; +text: .text%__1cOcompU_rRegNodeErule6kM_I_; +text: .text%__1cRMemBarReleaseNodeGOpcode6kM_i_; +text: .text%__1cKRelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cETypeOget_const_type6FpnGciType__pk0_; +text: .text%__1cMPhaseChaitinPset_was_spilled6MpnENode__v_; +text: .text%__1cXvirtual_call_RelocationIparse_ic6FrpnICodeBlob_rpC5rppnHoopDesc_pi_nNRelocIterator__; +text: .text%__1cLCounterDataOis_CounterData6M_i_; +text: .text%__1cRCompilationPolicyNcanBeCompiled6FnMmethodHandle__i_; +text: .text%__1cOcompU_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cKcmpOpUOperJnum_edges6kM_I_; +text: .text%__1cOrFlagsRegUOperJnum_edges6kM_I_; +text: .text%__1cKCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIGraphKitMsaved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cMtlsLoadPNodeErule6kM_I_; +text: .text%__1cIGraphKitGmemory6MI_pnENode__; +text: .text%__1cNRelocIteratorJset_limit6MpC_v_; +text: .text%__1cPcheckCastPPNodeErule6kM_I_; +text: .text%__1cOGenerateOopMapFppush6MpnNCellTypeState__v_; +text: .text%__1cSobjArrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSobjArrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQciBytecodeStreamPget_field_index6M_i_; +text: .text%__1cGThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cKHandleAreaHoops_do6MpnKOopClosure__v_; +text: .text%__1cNchunk_oops_do6FpnKOopClosure_pnFChunk_pc_L_: handles.o; +text: .text%__1cKCompiledIC2t6MpnKRelocation__v_; +text: .text%__1cMUniverseOperFclone6kM_pnIMachOper__; +text: .text%__1cJlabelOperFclone6kM_pnIMachOper__; +text: .text%__1cRaddI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cLPhaseValuesHmakecon6MpknEType__pnHConNode__; +text: .text%__1cENodeHins_req6MIp0_v_; +text: .text%__1cIGraphKitLclean_stack6Mi_v_; +text: .text%__1cKRegionNodeHhas_phi6kM_pnHPhiNode__; +text: .text%__1cNloadRangeNodePoper_input_base6kM_I_; +text: .text%__1cOMachReturnNodeNis_MachReturn6M_p0_; +text: .text%__1cNaddI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cQPreserveJVMState2t6MpnIGraphKit_i_v_; +text: .text%__1cNtestP_regNodeMcisc_operand6kM_i_; +text: .text%__1cKjmpConNodeHtwo_adr6kM_I_; +text: .text%__1cPClassFileParserbJparse_constant_pool_methodref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_; +text: .text%__1cWConstantPoolCacheEntryLis_resolved6kMnJBytecodesECode__i_; +text: .text%__1cWConstantPoolCacheEntryPbytecode_number6FnJBytecodesECode__i_; +text: .text%__1cMPhaseChaitinVmay_be_copy_of_callee6kMpnENode__i_; +text: .text%__1cNtestP_regNodeErule6kM_I_; +text: .text%__1cUThreadSafepointStateXexamine_state_of_thread6Mi_v_; +text: .text%__1cUSafepointSynchronizeOsafepoint_safe6FpnKJavaThread_nPJavaThreadState__i_; +text: .text%__1cFStateM_sub_Op_ConI6MpknENode__v_; +text: .text%__1cUArgumentSizeComputerDset6MinJBasicType__v_; +text: .text%__1cOcompU_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cHPhiNodeEmake6FpnENode_2pknEType_pknHTypePtr__p0_; +text: .text%__1cKCodeBufferOadd_stub_reloc6MpCrknQRelocationHolder_i_v_; +text: .text%__1cKCodeBufferOalloc_relocate6M_pnORelocateBuffer__; +text: .text%__1cNtestI_regNodeErule6kM_I_; +text: .text%__1cOGenerateOopMapbAget_basic_block_containing6kMi_pnKBasicBlock__; +text: .text%__1cFParseKensure_phi6Mii_pnHPhiNode__; +text: .text%__1cMLinkResolverZcheck_klass_accessability6FnLKlassHandle_1pnGThread__v_; +text: .text%__1cOGenerateOopMapSget_basic_block_at6kMi_pnKBasicBlock__; +text: .text%__1cHAddress2t6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cFciEnvSget_klass_by_index6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cKJavaThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cFStateM_sub_Op_AddP6MpknENode__v_; +text: .text%__1cICallNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cOcompI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cPciObjectFactoryNinit_ident_of6MpnIciObject__v_; +text: .text%__1cIciObjectJset_ident6MI_v_; +text: .text%__1cLMachNopNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLMachNopNodeMideal_Opcode6kM_i_; +text: .text%__1cKJNIHandlesKmake_local6FpnHoopDesc__pnI_jobject__; +text: .text%__1cITypeFuncEhash6kM_i_; +text: .text%__1cOcompI_rRegNodeErule6kM_I_; +text: .text%__1cIciMethodPliveness_at_bci6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessPget_liveness_at6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessKBasicBlockPget_liveness_at6MpnIciMethod_i_nGBitMap__; +text: .text%__1cITypeLongFempty6kM_i_; +text: .text%__1cMTypeKlassPtrCeq6kMpknEType__i_; +text: .text%__1cJAssemblerJemit_data6MinJrelocInfoJrelocType_i_v_; +text: .text%__1cJLoadSNodeGOpcode6kM_i_; +text: .text%__1cRMemBarAcquireNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitObasic_plus_adr6MpnENode_2l_2_; +text: .text%__1cJVectorSet2L6MI_rnDSet__; +text: .text%__1cJVectorSetEgrow6MI_v_; +text: .text%__1cLCastP2LNodeGOpcode6kM_i_; +text: .text%__1cFKlassNlookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cJAssemblerEcall6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cPcheckCastPPNodeMideal_Opcode6kM_i_; +text: .text%__1cJLoadLNodeGOpcode6kM_i_; +text: .text%__1cMLinkResolverNresolve_klass6FrnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cTconstantPoolOopDescMklass_ref_at6MipnGThread__pnMklassOopDesc__; +text: .text%__1cRCompilationPolicyOmustBeCompiled6FnMmethodHandle__i_; +text: .text%__1cIAndINodeGOpcode6kM_i_; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_; +text: .text%__1cUParallelScavengeHeapNtlab_capacity6kM_L_; +text: .text%__1cTCreateExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIPhaseCCPFwiden6kMpknEType_3_3_; +text: .text%__1cHTypePtrJsingleton6kM_i_; +text: .text%__1cNArgumentCountDset6MinJBasicType__v_; +text: .text%__1cWShouldNotReachHereNodeGpinned6kM_i_; +text: .text%__1cNsubI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cNtestI_regNodePoper_input_base6kM_I_; +text: .text%__1cLBoxLockNodeGOpcode6kM_i_; +text: .text%__1cWShouldNotReachHereNodeHtwo_adr6kM_I_; +text: .text%__1cQMachCallJavaNodePis_MachCallJava6M_p0_; +text: .text%__1cKStoreBNodeGOpcode6kM_i_; +text: .text%__1cJAssemblerMemit_operand6MpnMRegisterImpl_22nHAddressLScaleFactor_ipCrknQRelocationHolder__v_; +text: .text%__1cJAssemblerMemit_operand6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cOGenerateOopMapHget_var6Mi_nNCellTypeState__; +text: .text%__1cITypeFuncCeq6kMpknEType__i_; +text: .text%__1cMloadConLNodePoper_input_base6kM_I_; +text: .text%__1cMPhaseIterGVNHmakecon6MpknEType__pnHConNode__; +text: .text%__1cWShouldNotReachHereNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHoopDescGverify6M_v_; +text: .text%__1cQconstMethodKlassSoop_is_constMethod6kM_i_; +text: .text%__1cRconstantPoolKlassToop_is_constantPool6kM_i_; +text: .text%__1cOcompP_rRegNodePoper_input_base6kM_I_; +text: .text%__1cHTypePtrHget_con6kM_l_; +text: .text%__1cHMatcherWis_short_branch_offset6Mi_i_; +text: .text%__1cMloadConLNodeHtwo_adr6kM_I_; +text: .text%__1cMTypeKlassPtr2t6MnHTypePtrDPTR_pnHciKlass_i_v_; +text: .text%__1cYCallStaticJavaDirectNodeKmethod_set6Ml_v_; +text: .text%__1cWMachCallStaticJavaNodePret_addr_offset6M_i_; +text: .text%__1cYCallStaticJavaDirectNodePcompute_padding6kMi_i_; +text: .text%__1cJVectorSet2t6MpnFArena__v_; +text: .text%__1cFStateM_sub_Op_RegI6MpknENode__v_; +text: .text%__1cICmpPNodeDsub6kMpknEType_3_3_; +text: .text%__1cNloadConP0NodePoper_input_base6kM_I_; +text: .text%__1cOAbstractICachePinvalidate_word6FpC_v_; +text: .text%__1cRNativeInstructionFwrote6Mi_v_; +text: .text%__1cMURShiftLNodeGOpcode6kM_i_; +text: .text%__1cOrFlagsRegUOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIciMethodLscale_count6Mi_i_; +text: .text%__1cLjmpConUNodeJnum_opnds6kM_I_; +text: .text%__1cQciBytecodeStreamQget_method_index6M_i_; +text: .text%__1cFciEnvbTget_instance_klass_for_declared_method_holder6FpnHciKlass__pnPciInstanceKlass__; +text: .text%__1cPSignatureStream2t6MnMsymbolHandle_i_v_; +text: .text%JVM_GetMethodIxLocalsCount; +text: .text%__1cJlabelOperFlabel6kM_pnFLabel__; +text: .text%__1cGOopMapJheap_size6kM_i_; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cMloadConLNodeErule6kM_I_; +text: .text%__1cOMacroAssemblerZneeds_explicit_null_check6Fi_i_; +text: .text%__1cNSafePointNode2t6MIpnIJVMState__v_; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__; +text: .text%__1cNloadKlassNodeMideal_Opcode6kM_i_; +text: .text%__1cWConstantPoolCacheEntryIas_flags6MnITosState_iiiii_i_; +text: .text%__1cNloadConP0NodeHtwo_adr6kM_I_; +text: .text%__1cWThreadLocalAllocBufferVinitialize_statistics6M_v_; +text: .text%__1cWThreadLocalAllocBufferFclear6M_v_; +text: .text%__1cWThreadLocalAllocBufferVaccumulate_statistics6MLi_v_; +text: .text%__1cWThreadLocalAllocBufferImax_size6F_L_; +text: .text%__1cWThreadLocalAllocBufferGresize6M_v_; +text: .text%__1cJloadINodePoper_input_base6kM_I_; +text: .text%__1cIRootNodeLbottom_type6kM_pknEType__; +text: .text%__1cWConstantPoolCacheEntryGverify6kMpnMoutputStream__v_; +text: .text%__1cHCompileYout_preserve_stack_slots6F_I_; +text: .text%__1cFParsePload_state_from6Mpn0AFBlock__v_; +text: .text%__1cFParseMmerge_common6Mpn0AFBlock_i_v_; +text: .text%__1cNloadRangeNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cIGraphKitQkill_dead_locals6M_v_; +text: .text%__1cKMemBarNodeLbottom_type6kM_pknEType__; +text: .text%__1cKimmL32OperJconstantL6kM_x_; +text: .text%__1cIciObject2t6MnGHandle__v_; +text: .text%__1cITypeFuncEmake6FpknJTypeTuple_3_pk0_; +text: .text%__1cLConvI2LNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPciObjectFactoryGinsert6MipnIciObject_pnNGrowableArray4C2___v_; +text: .text%__1cRaddP_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cLBoxLockNodeJideal_reg6kM_I_; +text: .text%__1cPClassFileParserYverify_legal_method_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cICHeapObj2n6FL_pv_; +text: .text%__1cFframeUentry_frame_is_first6kM_i_; +text: .text%__1cKInlineTreeJcallee_at6kMipnIciMethod__p0_; +text: .text%__1cOFastUnlockNodeGOpcode6kM_i_; +text: .text%__1cFStateM_sub_Op_ConL6MpknENode__v_; +text: .text%__1cIHaltNodeGpinned6kM_i_; +text: .text%__1cMTypeKlassPtrEmake6FnHTypePtrDPTR_pnHciKlass_i_pk0_; +text: .text%__1cOoop_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cOCallRelocationFvalue6M_pC_; +text: .text%__1cKRelocationXpd_set_call_destination6MpCl_v_; +text: .text%__1cOCallRelocationWfix_relocation_at_move6Ml_v_; +text: .text%__1cKRelocationTpd_call_destination6M_pC_; +text: .text%__1cOCallRelocationPset_destination6MpCl_v_; +text: .text%__1cHcommute6FpnENode_ii_i_: addnode.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__; +text: .text%__1cENodeQlatency_from_use6kMrnLBlock_Array_rnNGrowableArray4CI__pk0p0_i_; +text: .text%__1cHAddNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNinstanceKlassQfind_local_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__i_; +text: .text%__1cMmerge_region6FpnKRegionNode_pnIPhaseGVN__pnENode__: cfgnode.o; +text: .text%__1cNloadConP0NodeErule6kM_I_; +text: .text%__1cIGraphKitMreset_memory6M_pnENode__; +text: .text%__1cPciObjectFactoryRcreate_new_object6MpnHoopDesc__pnIciObject__; +text: .text%__1cNloadRangeNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNloadConI0NodeMideal_Opcode6kM_i_; +text: .text%__1cJloadPNodeHtwo_adr6kM_I_; +text: .text%__1cNSignatureInfoGdo_int6M_v_; +text: .text%__1cKjmpDirNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPThreadRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cPThreadRootsTaskEname6M_pc_; +text: .text%__1cUThreadSafepointStateMroll_forward6Mn0AMsuspend_type__v_; +text: .text%__1cUThreadSafepointStateHrestart6M_v_; +text: .text%__1cLciSignatureHtype_at6kMi_pnGciType__; +text: .text%__1cJrRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIAddLNodeGOpcode6kM_i_; +text: .text%__1cJLoadPNodeJideal_reg6kM_I_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cOkill_dead_code6FpnENode_pnMPhaseIterGVN__i_: node.o; +text: .text%__1cKjmpDirNodeFclone6kM_pnENode__; +text: .text%__1cOcompI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cFParseFBlockRsuccessor_for_bci6Mi_p1_; +text: .text%__1cNGrowableArray4CpnOMethodLivenessKBasicBlock__2t6MpnFArena_iirk2_v_; +text: .text%__1cQSystemDictionaryEfind6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNtestP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNincI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cICallInfoDset6MnLKlassHandle_1nMmethodHandle_2ipnGThread__v_; +text: .text%__1cHOrINodeGOpcode6kM_i_; +text: .text%__1cOcompI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMPhaseChaitinJsplit_DEF6MpnENode_pnFBlock_iIp25nNGrowableArray4CI__i_I_; +text: .text%__1cENodeHis_Type6M_pnITypeNode__; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_; +text: .text%__1cICHeapObj2k6Fpv_v_; +text: .text%__1cRaddP_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cICmpINodeDsub6kMpknEType_3_3_; +text: .text%__1cENode2n6FLi_pv_; +text: .text%__1cJTraceTime2t6MpkcpnMelapsedTimer_iipnMoutputStream__v_; +text: .text%__1cIAddINodeGadd_id6kM_pknEType__; +text: .text%__1cKStoreCNodeGOpcode6kM_i_; +text: .text%__1cTconvI2L_reg_regNodeMcisc_operand6kM_i_; +text: .text%__1cHMemNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cSvframeStreamCommonPfill_from_frame6M_i_; +text: .text%__1cILoadNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cJStoreNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNFingerprinterLfingerprint6M_L_; +text: .text%__1cLConvI2LNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOGenerateOopMapGppload6MpnNCellTypeState_i_v_; +text: .text%__1cIGraphKitRnull_check_common6MpnENode_nJBasicType_i_2_; +text: .text%__1cKBlock_ListGremove6MI_v_; +text: .text%__1cKMemoryPoolYrecord_peak_memory_usage6M_v_; +text: .text%__1cKciTypeFlowGJsrSetJcopy_into6Mp1_v_; +text: .text%__1cICmpLNodeGOpcode6kM_i_; +text: .text%__1cLklassVtableTupdate_super_vtable6MpnNinstanceKlass_pnNmethodOopDesc_i_i_; +text: .text%__1cLcastP2LNodePoper_input_base6kM_I_; +text: .text%__1cQStackFrameStream2t6MpnKJavaThread_i_v_; +text: .text%__1cTciConstantPoolCacheEfind6Mi_i_; +text: .text%__1cOPhaseIdealLoopGspinup6MpnENode_2222pnLsmall_cache__2_; +text: .text%__1cMVirtualSpaceOcommitted_size6kM_L_; +text: .text%__1cNGrowableArray4CpnIciObject__Praw_at_put_grow6Mirk14_v_; +text: .text%__1cIRootNodeNis_block_proj6kM_pknENode__; +text: .text%__1cHOopFlowEmake6FpnFArena_i_p0_; +text: .text%__1cJloadLNodeErule6kM_I_; +text: .text%__1cNloadConI0NodeLbottom_type6kM_pknEType__; +text: .text%__1cJimmI0OperIconstant6kM_l_; +text: .text%__1cScompI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cLBoxLockNodeLbottom_type6kM_pknEType__; +text: .text%__1cNaddI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cOPhaseIdealLoopKhandle_use6MpnENode_2pnLsmall_cache_22222_v_; +text: .text%__1cOPhaseIdealLoopOfind_use_block6MpnENode_22222_2_; +text: .text%__1cNmethodOopDescbGupdate_compiled_code_entry_point6Mi_v_; +text: .text%__1cJStoreNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNSignatureInfoHdo_void6M_v_; +text: .text%__1cLAdapterInfoKhash_value6kM_l_; +text: .text%JVM_GetCPMethodClassNameUTF; +text: .text%__1cHOopFlowFclone6Mp0i_v_; +text: .text%__1cRSignatureIteratorSiterate_parameters6ML_v_; +text: .text%__1cILoopNodeHis_Loop6M_p0_; +text: .text%__1cPindOffset32OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cPindOffset32OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMCallLeafNodeGOpcode6kM_i_; +text: .text%__1cNmethodOopDescPis_final_method6kM_i_; +text: .text%__1cSComputeAdapterInfoHcompute6Mplii_v_; +text: .text%__1cLAdapterInfoHcompute6MnMmethodHandle_i_v_; +text: .text%__1cLAdapterInfo2T6M_v_; +text: .text%__1cSComputeAdapterInfoLreturn_type6MnJBasicType__i_; +text: .text%__1cSComputeAdapterInfoMsize_in_bits6FnMmethodHandle__i_; +text: .text%__1cMAdapterCacheGlookup6MpnLAdapterInfo__pnMBasicAdapter__; +text: .text%__1cJloadINodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cPadd_derived_oop6FppnHoopDesc_2_v_: oopMap.o; +text: .text%__1cTDerivedPointerTableDadd6FppnHoopDesc_3_v_; +text: .text%__1cFParseFBlockJinit_node6Mp0i_v_; +text: .text%__1cFParseFBlockKinit_graph6Mp0_v_; +text: .text%__1cOcompP_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cSObjectSynchronizerKslow_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cKjmpDirNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMLinkResolverbFlinktime_resolve_virtual_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_; +text: .text%__1cKCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJTraceTime2T6M_v_; +text: .text%__1cKciTypeFlowNmake_range_at6Mi_pn0AFRange__; +text: .text%__1cNmethodOopDescbAcompute_size_of_parameters6MpnGThread__v_; +text: .text%__1cKoopFactoryKnew_method6FinLAccessFlags_iiipnGThread__pnNmethodOopDesc__; +text: .text%__1cNmethodOopDescLobject_size6Fi_i_; +text: .text%__1cSconstMethodOopDescLobject_size6Fiiii_i_; +text: .text%__1cLklassVtableWneeds_new_vtable_entry6FpnNmethodOopDesc_pnMklassOopDesc_pnHoopDesc_pnNsymbolOopDesc_nLAccessFlags__i_; +text: .text%__1cSconstMethodOopDescZset_inlined_tables_length6Miii_v_; +text: .text%__1cLmethodKlassIallocate6MnRconstMethodHandle_nLAccessFlags_pnGThread__pnNmethodOopDesc__; +text: .text%__1cQconstMethodKlassIallocate6MiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cNmethodOopDescJinit_code6M_v_; +text: .text%__1cKoopFactoryPnew_constMethod6FiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cPClassFileParserMparse_method6MnSconstantPoolHandle_ipnLAccessFlags_pnPtypeArrayHandle_55pnGThread__nMmethodHandle__; +text: .text%__1cFBlockUhoist_LCA_above_defs6Mp01IrnLBlock_Array__1_; +text: .text%__1cScompI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cPciInstanceKlassLfield_cache6M_pnTciConstantPoolCache__; +text: .text%__1cHciFieldJwill_link6MpnPciInstanceKlass_nJBytecodesECode__i_; +text: .text%__1cFciEnvXget_field_by_index_impl6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cQciBytecodeStreamJget_field6Mri_pnHciField__; +text: .text%__1cFciEnvSget_field_by_index6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cKTypeOopPtrFempty6kM_i_; +text: .text%__1cWConstantPoolCacheEntryOset_bytecode_16MnJBytecodesECode__v_; +text: .text%__1cKCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMMergeMemNodeNgrow_to_match6Mpk0_v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_; +text: .text%__1cTCreateExceptionNodePoper_input_base6kM_I_; +text: .text%__1cPciInstanceKlassYunique_concrete_subklass6M_p0_; +text: .text%__1cLStringTableGlookup6MipHiI_pnHoopDesc__; +text: .text%__1cLBoxLockNodeHsize_of6kM_I_; +text: .text%__1cIciObjectIencoding6M_pnI_jobject__; +text: .text%__1cPClassFileParserbDverify_legal_method_modifiers6MiinMsymbolHandle_pnGThread__v_; +text: .text%__1cIPhaseGVNUtransform_no_reclaim6MpnENode__2_; +text: .text%__1cIRewriterOrewrite_method6FnMmethodHandle_rnIintArray_pnGThread__1_; +text: .text%__1cNmethodOopDescLlink_method6FnMmethodHandle__v_; +text: .text%__1cNloadKlassNodePoper_input_base6kM_I_; +text: .text%__1cNObjectMonitorEexit6MpnGThread__v_; +text: .text%__1cPClassFileParserZskip_over_field_signature6MpciIpnGThread__1_; +text: .text%__1cMMergeMemNode2t6MpnENode__v_; +text: .text%__1cMMergeMemNodeRmake_empty_memory6F_pnENode__; +text: .text%__1cNinstanceKlassVshould_be_initialized6kM_i_; +text: .text%__1cNtestP_regNodeJnum_opnds6kM_I_; +text: .text%__1cJStartNodeGis_CFG6kM_i_; +text: .text%__1cRaddI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_; +text: .text%__1cMindIndexOperJnum_edges6kM_I_; +text: .text%__1cRInterpretedRFrameKtop_method6kM_nMmethodHandle__; +text: .text%__1cKGCStatInfoMset_gc_usage6MinLMemoryUsage_i_v_; +text: .text%__1cXmembar_acquire_lockNodeLbottom_type6kM_pknEType__; +text: .text%__1cQPreserveJVMState2T6M_v_; +text: .text%__1cLRuntimeStubIis_alive6kM_i_; +text: .text%__1cMWarmCallInfoHis_cold6kM_i_; +text: .text%__1cNObjectMonitorFenter6MpnGThread__v_; +text: .text%__1cKjmpConNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIemit_d646FrnKCodeBuffer_l_v_; +text: .text%__1cFParseFmerge6Mi_v_; +text: .text%__1cOPhaseIdealLoopIset_idom6MpnENode_2I_v_; +text: .text%__1cIAddINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFParseMdo_one_block6M_v_; +text: .text%__1cFParseFBlockMrecord_state6Mp0_v_; +text: .text%__1cNtestP_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cJloadLNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cIPhaseIFGFUnion6MII_v_; +text: .text%__1cNloadRangeNodeJnum_opnds6kM_I_; +text: .text%__1cWConstantPoolCacheEntryOset_bytecode_26MnJBytecodesECode__v_; +text: .text%__1cOcompP_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cIBoolNodeJideal_reg6kM_I_; +text: .text%__1cHCmpNodeJideal_reg6kM_I_; +text: .text%__1cFStateM_sub_Op_Bool6MpknENode__v_; +text: .text%__1cJCatchNodeIis_Catch6kM_pk0_; +text: .text%__1cJLoadBNodeGOpcode6kM_i_; +text: .text%__1cENodeHlatency6MI_I_; +text: .text%__1cIGraphKit2t6MpnIJVMState__v_; +text: .text%__1cKTypeAryPtrFklass6kM_pnHciKlass__; +text: .text%__1cTciConstantPoolCacheDget6Mi_pv_; +text: .text%__1cNmethodOopDescIbcp_from6kMi_pC_; +text: .text%__1cKTypeOopPtrHget_con6kM_l_; +text: .text%__1cMPhaseChaitinKprompt_use6MpnFBlock_I_i_; +text: .text%__1cIJVMStateLdebug_depth6kM_I_; +text: .text%__1cIGraphKitTadd_safepoint_edges6MpnNSafePointNode_i_v_; +text: .text%__1cENodeNadd_req_batch6Mp0I_v_; +text: .text%__1cIJVMStateKclone_deep6kM_p0_; +text: .text%__1cFStateK_sub_Op_If6MpknENode__v_; +text: .text%__1cXindIndexScaleOffsetOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cXindIndexScaleOffsetOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cGBitMapVset_union_with_result6M0_i_; +text: .text%__1cNSafePointNodeEhash6kM_I_; +text: .text%__1cMelapsedTimerFstart6M_v_; +text: .text%__1cJStartNodeGOpcode6kM_i_; +text: .text%__1cMelapsedTimerEstop6M_v_; +text: .text%__1cOPhaseIdealLoopQconditional_move6MpnENode__2_; +text: .text%__1cJloadLNodeMideal_Opcode6kM_i_; +text: .text%__1cOMethodLivenessKBasicBlockJstore_one6Mi_v_; +text: .text%__1cTC2IAdapterGeneratorXlazy_std_verified_entry6FnMmethodHandle__pC_; +text: .text%__1cPindOffset32OperJnum_edges6kM_I_; +text: .text%__1cPFieldAccessInfoDset6MnLKlassHandle_nMsymbolHandle_iinJBasicType_nLAccessFlags__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_iipnGThread__v_; +text: .text%__1cMLinkResolverZcheck_field_accessability6FnLKlassHandle_11rnPfieldDescriptor_pnGThread__v_; +text: .text%__1cNsubI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cTCreateExceptionNodeHtwo_adr6kM_I_; +text: .text%__1cPindOffset32OperFscale6kM_i_; +text: .text%__1cHAddNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cICmpPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cbFCompressedLineNumberWriteStream2t6Mi_v_; +text: .text%__1cPClassFileParserWparse_linenumber_table6MIIpipnGThread__pC_; +text: .text%__1cSconstMethodOopDescbBcompressed_linenumber_table6kM_pC_; +text: .text%__1cHTypePtrLdual_offset6kM_i_; +text: .text%__1cNMachIdealNodePoper_input_base6kM_I_; +text: .text%__1cSObjectSynchronizerOinflate_helper6FpnHoopDesc__pnNObjectMonitor__; +text: .text%__1cKciTypeFlowIblock_at6Mipn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cKciTypeFlowFRangeNget_block_for6Mpn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cSvframeStreamCommonbBfill_from_interpreter_frame6M_v_; +text: .text%__1cHMonitorEwait6Mil_i_; +text: .text%__1cLcastP2LNodeMideal_Opcode6kM_i_; +text: .text%__1cNloadKlassNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cSObjectSynchronizerJslow_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cPJavaCallWrapperHoops_do6MpnKOopClosure__v_; +text: .text%__1cFframeNoops_entry_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cXindIndexScaleOffsetOperFscale6kM_i_; +text: .text%__1cQciBytecodeStreamKget_method6Mri_pnIciMethod__; +text: .text%__1cFciEnvTget_method_by_index6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvYget_method_by_index_impl6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvNlookup_method6MpnNinstanceKlass_2pnNsymbolOopDesc_4nJBytecodesECode__pnNmethodOopDesc__; +text: .text%__1cYinternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cNstoreImmBNodePoper_input_base6kM_I_; +text: .text%__1cNLoadRangeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cITypeFuncEmake6FpnIciMethod__pk0_; +text: .text%__1cMindirectOperJnum_edges6kM_I_; +text: .text%__1cKCompiledICOic_destination6kM_pC_; +text: .text%__1cTconvI2L_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cICmpUNodeDsub6kMpknEType_3_3_; +text: .text%__1cPSignatureStreamJas_symbol6MpnGThread__pnNsymbolOopDesc__; +text: .text%__1cRshrL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__; +text: .text%__1cPClassFileParserbFparse_constant_pool_class_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cOMethodLivenessKBasicBlockMmerge_normal6MnGBitMap__i_; +text: .text%__1cTleaPIdxScaleOffNodeHtwo_adr6kM_I_; +text: .text%__1cETypeFwiden6kMpk0_2_; +text: .text%__1cKciTypeFlowLStateVector2t6Mp0_v_; +text: .text%__1cNCatchProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cOcompU_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCatchProjNodeHsize_of6kM_I_; +text: .text%__1cNCatchProjNodeEhash6kM_I_; +text: .text%__1cSvframeStreamCommonEnext6M_v_; +text: .text%__1cFKlassIsubklass6kM_p0_; +text: .text%__1cKciTypeFlowFBlockPis_simpler_than6Mp1_i_; +text: .text%__1cJimmI8OperIconstant6kM_l_; +text: .text%__1cIAddPNodeQmach_bottom_type6FpknIMachNode__pknEType__; +text: .text%__1cILoadNodeHsize_of6kM_I_; +text: .text%__1cHMatcherVReduceInst_Chain_Rule6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cURethrowExceptionNodeNis_block_proj6kM_pknENode__; +text: .text%__1cNincI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cLjmpConUNodeHtwo_adr6kM_I_; +text: .text%__1cHMatcherScalling_convention6FpnLOptoRegPair_Ii_v_; +text: .text%__1cKPerfStringKset_string6Mpkc_v_; +text: .text%__1cENodeLnonnull_req6kM_p0_; +text: .text%__1cICmpINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHTypeAryCeq6kMpknEType__i_; +text: .text%__1cQSystemDictionaryKfind_class6FiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cKDictionaryKfind_class6MiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cQUnique_Node_ListEpush6MpnENode__v_; +text: .text%__1cILoopNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitTadd_exception_state6MpnNSafePointNode__v_; +text: .text%__1cJloadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__; +text: .text%__1cOPhaseIdealLoopRregister_new_node6MpnENode_2_v_; +text: .text%__1cQPSGenerationPoolImax_size6kM_L_; +text: .text%__1cQPSGenerationPoolNused_in_bytes6M_L_; +text: .text%__1cQPSGenerationPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cOMethodLivenessNwork_list_get6M_pn0AKBasicBlock__; +text: .text%__1cNinstanceKlassKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cICallNodeOis_CallRuntime6kM_pknPCallRuntimeNode__; +text: .text%__1cHTypeAryFxmeet6kMpknEType__3_; +text: .text%__1cNstoreImmBNodeMideal_Opcode6kM_i_; +text: .text%__1cKciTypeFlowLStateVectorEmeet6Mpk1_i_; +text: .text%__1cIMachNodeTmay_be_short_branch6kM_i_; +text: .text%__1cJloadINodeJnum_opnds6kM_I_; +text: .text%__1cNaddI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cRMachSafePointNode2t6M_v_; +text: .text%__1cHMatcherKmatch_sfpt6MpnNSafePointNode__pnIMachNode__; +text: .text%__1cOcompP_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cMPhaseChaitinKFind_const6kMpknENode__I_; +text: .text%__1cMPhaseChaitinKFind_const6kMI_I_; +text: .text%__1cNinstanceKlassKinitialize6MpnGThread__v_; +text: .text%__1cRInterpretedRFrameEinit6M_v_; +text: .text%__1cHemit_cc6FrnKCodeBuffer_ii_v_; +text: .text%__1cNtestI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframeMpd_gc_epilog6M_v_; +text: .text%__1cFframeLgc_epilogue6M_v_; +text: .text%__1cRframe_gc_prologue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cRframe_gc_epilogue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeLgc_prologue6M_v_; +text: .text%__1cOGenerateOopMapUreachable_basicblock6Fp0ipi_v_; +text: .text%__1cXvirtual_call_RelocationJfirst_oop6M_pC_; +text: .text%__1cXvirtual_call_RelocationJoop_limit6M_pC_; +text: .text%__1cMciMethodDataLhas_trap_at6MpnLProfileData_i_i_; +text: .text%__1cKciTypeFlowLStateVectorOpush_translate6MpnGciType__v_; +text: .text%__1cOGenerateOopMapJdo_method6Miiii_v_; +text: .text%__1cMloadConPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIimmPOperIconstant6kM_l_; +text: .text%__1cIimmPOperPconstant_is_oop6kM_i_; +text: .text%__1cOleaPIdxOffNodeHtwo_adr6kM_I_; +text: .text%__1cHGCCauseJto_string6Fn0AFCause__pkc_; +text: .text%__1cTleaPIdxScaleOffNodeErule6kM_I_; +text: .text%JVM_IsNaN; +text: .text%__1cXinsert_anti_dependences6FrpnFBlock_pnENode_rnLBlock_Array__i_: gcm.o; +text: .text%__1cLOptoRuntimebCcomplete_monitor_unlocking_C6FpnHoopDesc_pnJBasicLock__v_; +text: .text%__1cJloadINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOptoRuntimebAcomplete_monitor_locking_C6FpnHoopDesc_pnJBasicLock_pnKJavaThread__v_; +text: .text%__1cHCompileKTracePhase2t6MpkcpnMelapsedTimer_i_v_; +text: .text%__1cHCompileKTracePhase2T6M_v_; +text: .text%__1cOGenerateOopMapTmerge_state_into_bb6MpnKBasicBlock__v_; +text: .text%__1cHMatcherPc_frame_pointer6kM_i_; +text: .text%__1cFBlockKsched_call6MrnHMatcher_rnLBlock_Array_IrnJNode_List_pipnMMachCallNode_rnJVectorSet__I_; +text: .text%__1cMMachCallNode2t6M_v_; +text: .text%__1cICallNodeJideal_reg6kM_I_; +text: .text%__1cOleaPIdxOffNodeErule6kM_I_; +text: .text%__1cOGenerateOopMapEppop6MpnNCellTypeState__v_; +text: .text%__1cSCallLeafDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cOcompP_rRegNodeErule6kM_I_; +text: .text%__1cMany_RegPOperJnum_edges6kM_I_; +text: .text%__1cIGraphKitbLset_predefined_input_for_runtime_call6MpnNSafePointNode__v_; +text: .text%__1cMany_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cScompI_rReg_immNodeErule6kM_I_; +text: .text%__1cLBoxLockNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cIGraphKitOset_all_memory6MpnENode__v_; +text: .text%__1cLRegisterMap2t6Mpk0_v_; +text: .text%__1cGvframe2t6MpknFframe_pknLRegisterMap_pnKJavaThread__v_; +text: .text%__1cNmethodOopDescWwas_executed_more_than6kMi_i_; +text: .text%__1cKstoreCNodePoper_input_base6kM_I_; +text: .text%__1cVjava_lang_ClassLoaderbBnon_reflection_class_loader6FpnHoopDesc__2_; +text: .text%__1cHi2sNodeErule6kM_I_; +text: .text%__1cIMulLNodeGOpcode6kM_i_; +text: .text%__1cOPhaseIdealLoopHdom_lca6kMpnENode_2_2_; +text: .text%__1cMPrefetchNodeGOpcode6kM_i_; +text: .text%__1cSReferenceProcessorSdiscover_reference6MpnHoopDesc_nNReferenceType__i_; +text: .text%__1cSReferenceProcessorTget_discovered_list6MnNReferenceType__ppnHoopDesc__; +text: .text%__1cXjava_lang_ref_ReferenceIset_next6FpnHoopDesc_2_v_; +text: .text%__1cKciTypeFlowGJsrSet2t6MpnFArena_i_v_; +text: .text%__1cNtestI_regNodeJnum_opnds6kM_I_; +text: .text%__1cIAddINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIMachOperNconstant_disp6kM_i_; +text: .text%__1cIMachOperFscale6kM_i_; +text: .text%__1cFframeNis_java_frame6kM_i_; +text: .text%__1cOMethodLivenessKBasicBlockJpropagate6Mp0_v_; +text: .text%__1cQSystemDictionaryStry_get_next_class6F_pnMklassOopDesc__; +text: .text%__1cKDictionaryStry_get_next_class6M_pnMklassOopDesc__; +text: .text%__1cNloadKlassNodeErule6kM_I_; +text: .text%__1cIciMethodRhas_compiled_code6M_i_; +text: .text%__1cOoop_RelocationJpack_data6M_i_; +text: .text%__1cKCompiledICKcached_oop6kM_pnHoopDesc__; +text: .text%__1cGOopMap2t6Mii_v_; +text: .text%__1cJOopMapSetKadd_gc_map6MipnGOopMap__v_; +text: .text%__1cNincI_rRegNodeErule6kM_I_; +text: .text%__1cRMachSafePointNodePis_MachCallLeaf6M_pnQMachCallLeafNode__; +text: .text%__1cRMachSafePointNodeLset_oop_map6MpnGOopMap__v_; +text: .text%__1cYDebugInformationRecorderNadd_safepoint6MipnGOopMap__v_; +text: .text%__1cHCompileTProcess_OopMap_Node6MpnIMachNode_i_v_; +text: .text%__1cYDebugInformationRecorderKadd_oopmap6MipnGOopMap__v_; +text: .text%__1cHOopFlowNbuild_oop_map6MpnENode_ipnNPhaseRegAlloc_pi_pnGOopMap__; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNsubI_rRegNodeMcisc_operand6kM_i_; +text: .text%JVM_GetCPFieldClassNameUTF; +text: .text%__1cRaddI_rReg_immNodeErule6kM_I_; +text: .text%__1cRMachNullCheckNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cHRetNodeMideal_Opcode6kM_i_; +text: .text%__1cGvframeKnew_vframe6FpknFframe_pknLRegisterMap_pnKJavaThread__p0_; +text: .text%__1cNsubI_rRegNodeErule6kM_I_; +text: .text%__1cRaddP_rReg_immNodeErule6kM_I_; +text: .text%__1cPClassFileParserbGparse_constant_pool_string_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cJloadLNodePoper_input_base6kM_I_; +text: .text%__1cRshrL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cITypeLongFxdual6kM_pknEType__; +text: .text%__1cRMachSafePointNodeSis_MachCallRuntime6M_pnTMachCallRuntimeNode__; +text: .text%__1cNaddI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cENodeJset_req_X6MIp0pnMPhaseIterGVN__v_; +text: .text%__1cOcompP_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cSInterpreterRuntimePresolve_get_put6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_ipnGThread__v_; +text: .text%__1cWConstantPoolCacheEntryJset_field6MnJBytecodesECode_2nLKlassHandle_iinITosState_ii_v_; +text: .text%__1cIAndLNodeGOpcode6kM_i_; +text: .text%__1cMindIndexOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOtypeArrayKlassSallocate_permanent6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cOGenerateOopMapCpp6MpnNCellTypeState_2_v_; +text: .text%__1cMCallJavaNodeLis_CallJava6kM_pk0_; +text: .text%__1cICallNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cHCompileSflatten_alias_type6kMpknHTypePtr__3_; +text: .text%__1cRcmpFastUnlockNodePoper_input_base6kM_I_; +text: .text%__1cYCallStaticJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcompP_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cFStateW_sub_Op_CallStaticJava6MpknENode__v_; +text: .text%__1cWMachCallStaticJavaNodeVis_MachCallStaticJava6M_p0_; +text: .text%__1cRaddP_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cICallInfoDset6MnLKlassHandle_nMmethodHandle_pnGThread__v_; +text: .text%__1cSComputeAdapterInfoJdo_object6Mii_v_; +text: .text%__1cRMachSafePointNodeWis_MachCallInterpreter6M_pnXMachCallInterpreterNode__; +text: .text%__1cIGraphKitbDtransfer_exceptions_into_jvms6M_pnIJVMState__; +text: .text%__1cLConvL2INodeGOpcode6kM_i_; +text: .text%__1cOcompI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionarybEresolve_instance_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNaddI_rRegNodeErule6kM_I_; +text: .text%__1cHConNodeEmake6FpknEType__p0_; +text: .text%__1cScompI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cJLoadCNodeGOpcode6kM_i_; +text: .text%__1cYCallStaticJavaDirectNodeFreloc6kM_i_; +text: .text%__1cRcmpFastUnlockNodeMideal_Opcode6kM_i_; +text: .text%__1cJTraceTime2t6MpkciipnMoutputStream__v_; +text: .text%__1cWstatic_stub_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cIGraphKitQset_saved_ex_oop6FpnNSafePointNode_pnENode__v_; +text: .text%__1cIGraphKitUmake_exception_state6MpnENode__pnNSafePointNode__; +text: .text%__1cJloadBNodeMideal_Opcode6kM_i_; +text: .text%__1cMLinkResolverOresolve_invoke6FrnICallInfo_nGHandle_nSconstantPoolHandle_inJBytecodesECode_pnGThread__v_; +text: .text%__1cMLinkResolverMresolve_pool6FrnLKlassHandle_rnMsymbolHandle_42nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cGOopMapHcopy_to6MpC_v_; +text: .text%__1cNstoreImmBNodeJnum_opnds6kM_I_; +text: .text%__1cVLoaderConstraintTableWfind_loader_constraint6MnMsymbolHandle_nGHandle__ppnVLoaderConstraintEntry__; +text: .text%__1cJTimeStampJupdate_to6Mx_v_; +text: .text%__1cJTimeStampGupdate6M_v_; +text: .text%__1cFframeZinterpreter_frame_set_bcx6Ml_v_; +text: .text%__1cCosFsleep6FpnGThread_xi_i_; +text: .text%__1cNgetTimeMillis6F_x_; +text: .text%__1cRaddP_rReg_immNodeLbottom_type6kM_pknEType__; +text: .text%__1cIos_sleep6Fxi_i_: os_solaris.o; +text: .text%__1cLPhaseValuesHzerocon6MnJBasicType__pnHConNode__; +text: .text%__1cMCreateExNodeKmatch_edge6kMI_I_; +text: .text%__1cTconvI2L_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cIGraphKitNuncommon_trap6MipnHciKlass_pkci_v_; +text: .text%__1cILoadNodeEmake6FpnENode_22pknHTypePtr_pknEType_nJBasicType__p0_; +text: .text%__1cIGraphKitJmake_load6MpnENode_2pknEType_nJBasicType_i_2_; +text: .text%__1cTconvI2L_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cTno_rax_rbx_RegPOperJnum_edges6kM_I_; +text: .text%__1cLPCTableNodeLbottom_type6kM_pknEType__; +text: .text%__1cLOptoRuntimeSuncommon_trap_Type6F_pknITypeFunc__; +text: .text%__1cIHaltNode2t6MpnENode_2_v_; +text: .text%__1cNSafePointNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNaddI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cGPcDesc2t6Mii_v_; +text: .text%__1cHnmethodKcopy_pc_at6MipnGPcDesc__v_; +text: .text%__1cKciTypeFlowLStateVectorJcopy_into6kMp1_v_; +text: .text%__1cXmembar_release_lockNodeMideal_Opcode6kM_i_; +text: .text%__1cOcompL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cMoutputStreamPupdate_position6MpkcL_v_; +text: .text%__1cMstringStreamFwrite6MpkcL_v_; +text: .text%__1cKciTypeFlowQadd_to_work_list6Mpn0AFBlock__v_; +text: .text%__1cKciTypeFlowKflow_block6Mpn0AFBlock_pn0ALStateVector_pn0AGJsrSet__v_; +text: .text%__1cKciTypeFlowFBlockKsuccessors6MpnQciBytecodeStream_pn0ALStateVector_pn0AGJsrSet__pnNGrowableArray4Cp1___; +text: .text%__1cKciTypeFlowOwork_list_next6M_pn0AFBlock__; +text: .text%__1cIPipelineXfunctional_unit_latency6kMIpk0_I_; +text: .text%__1cMPhaseIterGVNJtransform6MpnENode__2_; +text: .text%__1cQSystemDictionarybCfind_instance_or_array_klass6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cSCompareAndSwapNodeGis_CFG6kM_i_; +text: .text%__1cNGrowableArray4CpnOMethodLivenessKBasicBlock__Icontains6kMrk2_i_; +text: .text%__1cKciTypeFlowFBlock2t6Mp0pn0AFRange_pn0AGJsrSet__v_; +text: .text%__1cNGrowableArray4CpnKciTypeFlowJJsrRecord__2t6MpnFArena_iirk2_v_; +text: .text%__1cKstoreCNodeJnum_opnds6kM_I_; +text: .text%__1cNmodI_rRegNodeErule6kM_I_; +text: .text%__1cKInlineTreeWfind_subtree_from_root6Fp0pnIJVMState_pnIciMethod_i_1_; +text: .text%__1cNGrowableArray4CpnPciInstanceKlass__2t6MpnFArena_iirk1_v_; +text: .text%__1cKciTypeFlowFBlockScompute_exceptions6M_v_; +text: .text%__1cYciExceptionHandlerStreamFcount6M_i_; +text: .text%__1cINodeHashJhash_find6MpknENode__p1_; +text: .text%__1cFParsePdo_field_access6Mii_v_; +text: .text%__1cPThreadLocalNodeLbottom_type6kM_pknEType__; +text: .text%__1cOMethodLivenessNmake_block_at6Mipn0AKBasicBlock__2_; +text: .text%__1cKstorePNodeHtwo_adr6kM_I_; +text: .text%__1cKciTypeFlowPflow_successors6MpnNGrowableArray4Cpn0AFBlock___pn0ALStateVector__v_; +text: .text%__1cGciTypeMis_classless6kM_i_; +text: .text%__1cRsalI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cJloadFNodeErule6kM_I_; +text: .text%__1cKBranchDataNis_BranchData6M_i_; +text: .text%__1cIJumpDataLis_JumpData6M_i_; +text: .text%__1cSMemBarCPUOrderNodeGOpcode6kM_i_; +text: .text%__1cLklassVtableNput_method_at6MpnNmethodOopDesc_i_v_; +text: .text%__1cHi2sNodeMideal_Opcode6kM_i_; +text: .text%__1cKstoreCNodeMideal_Opcode6kM_i_; +text: .text%__1cRshrI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNloadConI0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNloadKlassNodeJnum_opnds6kM_I_; +text: .text%__1cHCompileKalias_type6MpnHciField__pn0AJAliasType__; +text: .text%__1cLStringTableGintern6FnGHandle_pHipnGThread__pnHoopDesc__; +text: .text%__1cLStringTableLhash_string6FpHi_i_; +text: .text%__1cMCreateExNodeGpinned6kM_i_; +text: .text%__1cFciEnvWget_klass_by_name_impl6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cNloadKlassNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRScavengeRootsTaskEname6M_pc_; +text: .text%__1cRScavengeRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cNtestP_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cKstoreINodeHtwo_adr6kM_I_; +text: .text%__1cSInterpreterRuntimeOresolve_invoke6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cPCountedLoopNodeGOpcode6kM_i_; +text: .text%__1cHTypeIntEmake6Fii_pk0_; +text: .text%__1cRcmpFastUnlockNodeHtwo_adr6kM_I_; +text: .text%__1cJloadSNodeMideal_Opcode6kM_i_; +text: .text%__1cPDictionaryEntrybAcontains_protection_domain6kMpnHoopDesc__i_; +text: .text%__1cIregFOperEtype6kM_pknEType__; +text: .text%__1cLLShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%JVM_GetCPMethodNameUTF; +text: .text%__1cMLinkResolverbNlinktime_resolve_virtual_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cNsubI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cLTypeInstPtrFxdual6kM_pknEType__; +text: .text%__1cNsubI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cIGraphKitOreplace_in_map6MpnENode_2_v_; +text: .text%__1cMPhaseChaitinLclone_projs6MpnFBlock_IpnENode_4rI_i_; +text: .text%__1cTconstantPoolOopDescbCverify_constant_pool_resolve6FnSconstantPoolHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cLcastP2LNodeJnum_opnds6kM_I_; +text: .text%__1cOMethodLivenessNwork_list_add6Mpn0AKBasicBlock__v_; +text: .text%__1cFParseFBlockNlocal_type_at6kMi_pknEType__; +text: .text%__1cWConstantPoolCacheEntryKset_method6MnJBytecodesECode_nMmethodHandle_i_v_; +text: .text%__1cHTypeIntFxdual6kM_pknEType__; +text: .text%__1cEUTF8Hstrrchr6FpWiW_1_; +text: .text%__1cQConstantIntValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_nGHandle_2ipnGThread__pnMklassOopDesc__; +text: .text%__1cNLoadKlassNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHCompileZintrinsic_insertion_index6MpnIciMethod_i_i_; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_; +text: .text%__1cJVectorSetFClear6M_v_; +text: .text%__1cMMergeMemNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKMemoryPoolHoops_do6MpnKOopClosure__v_; +text: .text%__1cICodeHeapSallocated_capacity6kM_L_; +text: .text%__1cICodeBlobRis_at_poll_return6MpC_i_; +text: .text%__1cIMachOperEtype6kM_pknEType__; +text: .text%__1cLjmpConUNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQSystemDictionarybOfind_constrained_instance_or_array_klass6FnMsymbolHandle_nGHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cNCallGenerator2t6MpnIciMethod__v_; +text: .text%__1cSHighResTimeSamplerLtake_sample6M_x_; +text: .text%__1cRis_error_reported6F_i_; +text: .text%__1cLStatSamplerLsample_data6FpnMPerfDataList__v_; +text: .text%__1cPStatSamplerTaskEtask6M_v_; +text: .text%__1cMPeriodicTaskMtime_to_wait6F_L_; +text: .text%__1cMPeriodicTaskOreal_time_tick6FL_v_; +text: .text%__1cNWatcherThreadRis_Watcher_thread6kM_i_; +text: .text%__1cLStatSamplerOcollect_sample6F_v_; +text: .text%__1cJloadBNodePoper_input_base6kM_I_; +text: .text%__1cMVM_OperationPevaluation_mode6kM_n0AEMode__; +text: .text%__1cIGraphKit2t6M_v_; +text: .text%__1cOemit_d64_reloc6FrnKCodeBuffer_lnJrelocInfoJrelocType_i_v_; +text: .text%__1cNinstanceKlassKlink_class6MpnGThread__v_; +text: .text%__1cIGraphKitNset_map_clone6MpnNSafePointNode__v_; +text: .text%__1cRRawBytecodeStream2t6MnMmethodHandle__v_; +text: .text%__1cENodeHget_ptr6kM_l_; +text: .text%__1cFStateM_sub_Op_ConP6MpknENode__v_; +text: .text%__1cJloadPNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKI2CAdapterOis_i2c_adapter6kM_i_; +text: .text%__1cOcompU_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cNinstanceKlassPinitialize_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cQciBytecodeStreamWget_field_holder_index6M_i_; +text: .text%__1cQciBytecodeStreamZget_declared_field_holder6M_pnPciInstanceKlass__; +text: .text%__1cRinterpretedVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cMorI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cIciSymbol2t6MnMsymbolHandle__v_; +text: .text%__1cFParseRensure_memory_phi6Mii_pnHPhiNode__; +text: .text%__1cNdecI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cJLoadINodeJideal_reg6kM_I_; +text: .text%__1cKRelocationWfix_relocation_at_move6Ml_v_; +text: .text%__1cPindOffset32OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cHAddNodePadd_of_identity6kMpknEType_3_3_; +text: .text%__1cMFastLockNodeGOpcode6kM_i_; +text: .text%__1cScompU_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cHCompilebAallow_range_check_smearing6kM_i_; +text: .text%__1cLBuildCutout2T6M_v_; +text: .text%__1cLBuildCutout2t6MpnIGraphKit_pnENode_ff_v_; +text: .text%__1cTconstantPoolOopDescOstring_at_impl6FnSconstantPoolHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cQjava_lang_StringQbasic_create_oop6FpnQtypeArrayOopDesc_ipnGThread__pnHoopDesc__; +text: .text%__1cICodeHeapIcapacity6kM_L_; +text: .text%__1cKMemoryPoolImax_size6kM_L_; +text: .text%__1cMCodeHeapPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cMCodeHeapPoolNused_in_bytes6M_L_; +text: .text%__1cMPhaseChaitinTsplit_Rematerialize6MpnENode_pnFBlock_IrInNGrowableArray4CI__ipIp2i_2_; +text: .text%__1cJcmpOpOperFccode6kM_i_; +text: .text%__1cKjmpDirNodeTmay_be_short_branch6kM_i_; +text: .text%__1cKjmpDirNodeOis_pc_relative6kM_i_; +text: .text%__1cPClassFileParserXverify_legal_field_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_field_signature6MnMsymbolHandle_1pnGThread__v_; +text: .text%__1cEUTF8Enext6FpkcpH_pc_; +text: .text%__1cOcompL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cbAPSEvacuateFollowersClosureHdo_void6M_v_; +text: .text%__1cFParseKdo_get_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_; +text: .text%__1cHMulNodeEhash6kM_I_; +text: .text%__1cGRFrame2t6MnFframe_pnKJavaThread_p0_v_; +text: .text%__1cTconvI2L_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cOGenerateOopMapLbb_mark_fct6Fp0ipi_v_; +text: .text%__1cScompU_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNFingerprinterJdo_object6Mii_v_; +text: .text%__1cMloadConFNodePoper_input_base6kM_I_; +text: .text%__1cOGenerateOopMapTmerge_state_vectors6MpnNCellTypeState_2_i_; +text: .text%__1cMloadConFNodeHtwo_adr6kM_I_; +text: .text%__1cICallNodeSis_CallDynamicJava6kM_pknTCallDynamicJavaNode__; +text: .text%__1cRcmpFastUnlockNodeJnum_opnds6kM_I_; +text: .text%__1cEUTF8Sconvert_to_unicode6FpkcpHi_v_; +text: .text%__1cUParallelScavengeHeapMmem_allocate6MLii_pnIHeapWord__; +text: .text%__1cNinstanceKlassbBallocate_permanent_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cKjmpConNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMethodLivenessKBasicBlockQcompute_gen_kill6MpnIciMethod__v_; +text: .text%__1cOMethodLivenessKBasicBlock2t6Mp0ii_v_; +text: .text%__1cMloadConFNodeErule6kM_I_; +text: .text%__1cLcastP2LNodeHtwo_adr6kM_I_; +text: .text%__1cIMachOperIconstant6kM_l_; +text: .text%__1cJloadSNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cPcheckCastPPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMTypeKlassPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cUEdenMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolImax_size6kM_L_; +text: .text%__1cUEdenMutableSpacePoolNused_in_bytes6M_L_; +text: .text%__1cUEdenMutableSpacePoolImax_size6kM_L_; +text: .text%__1cYSurvivorMutableSpacePoolNused_in_bytes6M_L_; +text: .text%__1cKjmpConNodeTmay_be_short_branch6kM_i_; +text: .text%__1cKjmpConNodeOis_pc_relative6kM_i_; +text: .text%__1cHConNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKCodeBufferKend_a_stub6M_v_; +text: .text%__1cTemit_java_to_interp6FrnKCodeBuffer__v_; +text: .text%__1cKCodeBufferMstart_a_stub6M_v_; +text: .text%__1cFParseUprofile_taken_branch6Mi_v_; +text: .text%__1cKciTypeFlowGJsrSetNapply_control6Mp0pnQciBytecodeStream_pn0ALStateVector__v_; +text: .text%__1cKReturnNodeGis_CFG6kM_i_; +text: .text%__1cRSignatureIteratorSskip_optional_size6M_v_; +text: .text%__1cRaddI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHTypeIntFwiden6kMpknEType__3_; +text: .text%__1cTCompareAndSwapLNodeGOpcode6kM_i_; +text: .text%__1cNmethodOopDescbHhas_unloaded_classes_in_signature6FnMmethodHandle_pnGThread__i_; +text: .text%__1cIciObjectRis_instance_klass6M_i_; +text: .text%__1cOGenerateOopMapRsigchar_to_effect6McipnNCellTypeState__2_; +text: .text%__1cOGenerateOopMapIdo_field6Miiii_v_; +text: .text%__1cPloadConUL32NodeMideal_Opcode6kM_i_; +text: .text%__1cEUTF8Ounicode_length6Fpkci_i_; +text: .text%__1cIciMethodRget_flow_analysis6M_pnKciTypeFlow__; +text: .text%__1cICodeBlobLoop_addr_at6kMi_ppnHoopDesc__; +text: .text%__1cNloadRangeNodeHtwo_adr6kM_I_; +text: .text%__1cJloadLNodeJnum_opnds6kM_I_; +text: .text%__1cSmembar_acquireNodeMideal_Opcode6kM_i_; +text: .text%__1cSObjectSynchronizerXidentity_hash_value_for6FnGHandle__l_; +text: .text%__1cHoopDescSslow_identity_hash6M_l_; +text: .text%__1cKMemBarNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOGenerateOopMapLmerge_state6Fp0ipi_v_; +text: .text%__1cIMachNodeOpipeline_class6F_pknIPipeline__; +text: .text%__1cIMachNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadCNodeErule6kM_I_; +text: .text%__1cKOSRAdapterIis_alive6kM_i_; +text: .text%__1cQjava_lang_StringMbasic_create6FpnQtypeArrayOopDesc_ipnGThread__nGHandle__; +text: .text%__1cRMachNullCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGThreadXclear_pending_exception6M_v_; +text: .text%__1cXindIndexScaleOffsetOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOcompL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cTconstantPoolOopDescbBbasic_type_for_signature_at6Mi_nJBasicType__; +text: .text%__1cETypeRget_typeflow_type6FpnGciType__pk0_; +text: .text%__1cLStringTableGintern6FpnNsymbolOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cNsymbolOopDescKas_unicode6kMri_pH_; +text: .text%__1cKciTypeFlowLStateVectorJdo_invoke6MpnQciBytecodeStream_i_v_; +text: .text%__1cKstorePNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKReturnNodeKmatch_edge6kMI_I_; +text: .text%__1cKRegionNodeJideal_reg6kM_I_; +text: .text%__1cJloadINodeHtwo_adr6kM_I_; +text: .text%__1cQmark_inner_loops6FpnIPhaseCFG_pnFBlock__v_: block.o; +text: .text%__1cIHaltNodeJideal_reg6kM_I_; +text: .text%__1cFStateM_sub_Op_Halt6MpknENode__v_; +text: .text%__1cWShouldNotReachHereNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReturnNodeGOpcode6kM_i_; +text: .text%__1cJTypeTupleKmake_range6FpnLciSignature__pk0_; +text: .text%__1cKStoreLNodeGOpcode6kM_i_; +text: .text%__1cPCountedLoopNodeOis_CountedLoop6M_p0_; +text: .text%__1cJTypeTupleLmake_domain6FpnPciInstanceKlass_pnLciSignature__pk0_; +text: .text%__1cPClassFileParserWparse_field_attributes6MnSconstantPoolHandle_iHpHpi2pnPtypeArrayHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_field_modifiers6MiipnGThread__v_; +text: .text%__1cMindirectOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMindirectOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMLinkResolverUresolve_virtual_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_virtual_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cFKlassXcan_be_statically_bound6FpnNmethodOopDesc__i_; +text: .text%__1cLProfileDataOtranslate_from6Mp0_v_; +text: .text%__1cKstorePNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKstoreINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSPSKeepAliveClosureGdo_oop6MppnHoopDesc__v_; +text: .text%__1cNloadConI0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvI2L_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMWarmCallInfoKalways_hot6F_p0_; +text: .text%__1cMWarmCallInfoGis_hot6kM_i_; +text: .text%__1cNprefetchwNodeMideal_Opcode6kM_i_; +text: .text%__1cIAddINodeJideal_reg6kM_I_; +text: .text%__1cNCatchProjNode2t6MpnENode_Ii_v_; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__; +text: .text%__1cLBoxLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmulL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cKciTypeFlowIcan_trap6MrnQciBytecodeStream__i_; +text: .text%__1cQVMOperationQdDueueLqueue_empty6Mi_i_; +text: .text%__1cIProjNodeDcmp6kMrknENode__I_; +text: .text%__1cSComputeAdapterInfoGdo_int6M_v_; +text: .text%__1cNCatchProjNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%JVM_GetCPFieldSignatureUTF; +text: .text%__1cIGraphKitTtoo_many_recompiles6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cHCompileFstart6kM_pnJStartNode__; +text: .text%__1cNmulL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cLPCTableNodeEhash6kM_I_; +text: .text%__1cIGraphKitZadd_exception_states_from6MpnIJVMState__v_; +text: .text%__1cJStartNodeOis_block_start6kM_i_; +text: .text%__1cQComputeCallStackHdo_void6M_v_; +text: .text%__1cNaddI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitOtoo_many_traps6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cNciCallProfileRapply_prof_factor6Mf_v_; +text: .text%__1cIciMethodTcall_profile_at_bci6Mi_nNciCallProfile__; +text: .text%__1cHCompileOcall_generator6MpnIciMethod_ipnIJVMState_if_pnNCallGenerator__; +text: .text%__1cHCompileOfind_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cMindIndexOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMindIndexOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%jni_GetPrimitiveArrayCritical: jni.o; +text: .text%jni_ReleasePrimitiveArrayCritical: jni.o; +text: .text%__1cSInterpreterRuntimeDldc6FpnKJavaThread_i_v_; +text: .text%__1cPClassFileParserbIparse_constant_pool_fieldref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNdecI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cFKlassOis_subclass_of6kMpnMklassOopDesc__i_; +text: .text%__1cNGrowableArray4Cl_2t6Mii_v_; +text: .text%__1cNinstanceKlassSlookup_osr_nmethod6kMpnNmethodOopDesc_i_pnHnmethod__; +text: .text%__1cIAddINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cLPCTableNodeKis_PCTable6kM_pk0_; +text: .text%__1cLPCTableNodeHsize_of6kM_I_; +text: .text%__1cNincI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKJNIHandlesKmake_local6FpnGThread_pnHoopDesc__pnI_jobject__; +text: .text%__1cQciBytecodeStreamXget_method_holder_index6M_i_; +text: .text%__1cMorI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cLimmUL32OperJconstantL6kM_x_; +text: .text%__1cIGraphKitWround_double_arguments6MpnIciMethod__v_; +text: .text%__1cFParseHdo_call6M_v_; +text: .text%__1cIGraphKitTround_double_result6MpnIciMethod__v_; +text: .text%__1cFParseZcan_not_compile_call_site6MpnIciMethod_pnPciInstanceKlass__i_; +text: .text%__1cQciBytecodeStreambAget_declared_method_holder6M_pnHciKlass__; +text: .text%__1cFParseMprofile_call6MpnENode__v_; +text: .text%__1cScompP_mem_rRegNodePoper_input_base6kM_I_; +text: .text%__1cICodeHeapLheader_size6F_L_; +text: .text%__1cJloadBNodeJnum_opnds6kM_I_; +text: .text%__1cENodeLbottom_type6kM_pknEType__; +text: .text%__1cXindIndexScaleOffsetOperNconstant_disp6kM_i_; +text: .text%__1cSindIndexOffsetOperJnum_edges6kM_I_; +text: .text%__1cFKlassMnext_sibling6kM_p0_; +text: .text%__1cOGenerateOopMapNrestore_state6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapQnext_bb_start_pc6MpnKBasicBlock__i_; +text: .text%__1cOGenerateOopMapJinterp_bb6MpnKBasicBlock__v_; +text: .text%__1cKciTypeFlowLStateVectorStype_meet_internal6FpnGciType_3p0_3_; +text: .text%__1cRcmpFastUnlockNodeErule6kM_I_; +text: .text%__1cIBoolNodeHsize_of6kM_I_; +text: .text%__1cLLShiftINodeLbottom_type6kM_pknEType__; +text: .text%jni_DeleteLocalRef: jni.o; +text: .text%__1cJloadSNodePoper_input_base6kM_I_; +text: .text%__1cPno_rax_RegPOperJnum_edges6kM_I_; +text: .text%__1cOcompI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%jni_SetIntField: jni.o; +text: .text%__1cMURShiftLNodeLbottom_type6kM_pknEType__; +text: .text%__1cMMutableSpaceFclear6M_v_; +text: .text%__1cNtestI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQjava_lang_StringbBcreate_tenured_from_unicode6FpHipnGThread__nGHandle__; +text: .text%__1cKoopFactoryXnew_permanent_charArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cLStringTableJbasic_add6MinGHandle_pHiIpnGThread__pnHoopDesc__; +text: .text%__1cNprefetchwNodePoper_input_base6kM_I_; +text: .text%__1cTCreateExceptionNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cHMulNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cVExceptionHandlerTableJadd_entry6MnRHandlerTableEntry__v_; +text: .text%__1cPsalI_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cRaddP_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMLinkResolverbFlinktime_resolve_special_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverUresolve_special_call6FrnICallInfo_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_special_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_4ipnGThread__v_; +text: .text%__1cQComputeCallStackJdo_object6Mii_v_; +text: .text%__1cIGraphKitNcast_not_null6MpnENode__2_; +text: .text%__1cTconvL2I_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cKPSYoungGenNused_in_bytes6kM_L_; +text: .text%__1cOCompilerOracleOshould_exclude6FnMmethodHandle__i_; +text: .text%__1cOGenerateOopMapHset_var6MinNCellTypeState__v_; +text: .text%__1cPcheckCastPPNodeJnum_opnds6kM_I_; +text: .text%__1cLLShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cGIfNodeHsize_of6kM_I_; +text: .text%__1cPciInstanceKlassFsuper6M_p0_; +text: .text%__1cOcompL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cLLShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cScompI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMMergeMemNodeJideal_reg6kM_I_; +text: .text%__1cNandL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cIciMethodWwas_executed_more_than6Mi_i_; +text: .text%__1cSReferenceProcessorbAprocess_discovered_reflist6MppnHoopDesc_pnPReferencePolicy_i_v_; +text: .text%__1cSReferenceProcessorOprocess_phase36MppnHoopDesc_ipnRBoolObjectClosure_pnKOopClosure_pnLVoidClosure__v_; +text: .text%__1cSReferenceProcessorOprocess_phase26MppnHoopDesc_pnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSReferenceProcessorbAenqueue_discovered_reflist6MpnHoopDesc_p2_v_; +text: .text%__1cTleaPIdxScaleOffNodeMideal_Opcode6kM_i_; +text: .text%__1cTleaPIdxScaleOffNodePoper_input_base6kM_I_; +text: .text%__1cFLabelJadd_patch6Mi_v_; +text: .text%__1cKMemBarNodeEhash6kM_I_; +text: .text%__1cOcompP_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNloadConL0NodePoper_input_base6kM_I_; +text: .text%__1cNsubI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJMarkSweepSMarkAndPushClosureLdo_nmethods6kM_ki_; +text: .text%__1cIXorINodeGOpcode6kM_i_; +text: .text%__1cTStackWalkCompPolicyRcompilation_level6MnMmethodHandle_i_i_; +text: .text%__1cMoutputStreamDput6Mc_v_; +text: .text%__1cPindOffset32OperNbase_position6kM_i_; +text: .text%__1cPindOffset32OperNconstant_disp6kM_i_; +text: .text%__1cOcompU_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeHtwo_adr6kM_I_; +text: .text%__1cPBytecode_invokeFindex6kM_i_; +text: .text%__1cFframeNis_glue_frame6kM_i_; +text: .text%__1cLRShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cZPhaseConservativeCoalesceKupdate_ifg6MIIpnIIndexSet_2_v_; +text: .text%__1cZPhaseConservativeCoalesceMunion_helper6MpnENode_2II222pnFBlock_I_v_; +text: .text%__1cIIndexSetEswap6Mp0_v_; +text: .text%__1cOPhaseIdealLoopIsink_use6MpnENode_2_v_; +text: .text%__1cRshrL_rReg_immNodeErule6kM_I_; +text: .text%__1cKInlineTreeMok_to_inline6MpnIciMethod_pnIJVMState_rnNciCallProfile_pnMWarmCallInfo__8_; +text: .text%__1cTpass_initial_checks6FpnIciMethod_i1_i_; +text: .text%__1cKInlineTreeMshouldInline6kMpnIciMethod_irnNciCallProfile_pnMWarmCallInfo__pkc_; +text: .text%__1cOCompilerOracleNshould_inline6FnMmethodHandle__i_; +text: .text%__1cKInlineTreeNtry_to_inline6MpnIciMethod_irnNciCallProfile_pnMWarmCallInfo__pkc_; +text: .text%__1cIciMethodbAinterpreter_throwout_count6kM_i_; +text: .text%__1cIciMethodNshould_inline6M_i_; +text: .text%__1cIciMethodOshould_exclude6M_i_; +text: .text%__1cScompU_rReg_immNodeErule6kM_I_; +text: .text%__1cKjmpDirNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKjmpDirNodeJis_Branch6kM_I_; +text: .text%__1cKjmpDirNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMindirectOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cILoadNodeDcmp6kMrknENode__I_; +text: .text%__1cLTypeInstPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cIGraphKitHjava_bc6kM_nJBytecodesECode__; +text: .text%__1cFLabelSpatch_instructions6MpnRAbstractAssembler__v_; +text: .text%__1cRAbstractAssemblerHbind_to6MrnFLabel_i_v_; +text: .text%__1cRAbstractAssemblerEbind6MrnFLabel__v_; +text: .text%__1cNloadConL0NodeErule6kM_I_; +text: .text%__1cJStoreNodeSIdeal_masked_input6MpnIPhaseGVN_I_pnENode__; +text: .text%__1cIGraphKitNbuiltin_throw6MnODeoptimizationLDeoptReason_pnENode__v_; +text: .text%__1cbBopt_virtual_call_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cJrRegIOperFclone6kM_pnIMachOper__; +text: .text%__1cMindIndexOperFscale6kM_i_; +text: .text%__1cScompP_mem_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cFKlassPoop_is_instance6kM_i_; +text: .text%__1cHciFieldPinitialize_from6MpnPfieldDescriptor__v_; +text: .text%__1cRandI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cMMachProjNodeHsize_of6kM_I_; +text: .text%__1cJStoreNodeZIdeal_sign_extended_input6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cScompP_mem_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cTconvF2D_reg_memNodeErule6kM_I_; +text: .text%__1cPindOffset32OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFStateM_sub_Op_CmpP6MpknENode__v_; +text: .text%__1cPciInstanceKlassUget_canonical_holder6Mi_p0_; +text: .text%__1cMloadConLNodeMideal_Opcode6kM_i_; +text: .text%__1cSCompiledStaticCallJfind_stub6M_pC_; +text: .text%__1cIciSymbolEmake6Fpkc_p0_; +text: .text%__1cIciSymbolJmake_impl6Fpkc_p0_; +text: .text%__1cScompU_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cKimmL32OperIconstant6kM_l_; +text: .text%__1cHi2sNodePoper_input_base6kM_I_; +text: .text%__1cKimmL32OperJnum_edges6kM_I_; +text: .text%__1cMMonitorValue2t6MpnKScopeValue_nILocation__v_; +text: .text%__1cMMonitorValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cLBoxLockNodeKstack_slot6FpnENode__i_; +text: .text%__1cLBoxLockNodeKis_BoxLock6kM_pk0_; +text: .text%__1cIBoolNodeDcmp6kMrknENode__I_; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_; +text: .text%__1cKDataLayoutPneeds_array_len6FC_i_; +text: .text%__1cKDataLayoutKinitialize6MCHi_v_; +text: .text%__1cJloadLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframebHnext_monitor_in_interpreter_frame6kMpnPBasicObjectLock__2_; +text: .text%__1cLRuntimeStubHoops_do6MpnKOopClosure__v_; +text: .text%__1cFParseRbranch_prediction6Mrf_f_; +text: .text%__1cPshrI_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cHciKlassNis_subtype_of6Mp0_i_; +text: .text%__1cOcompL_rRegNodeErule6kM_I_; +text: .text%__1cNGrowableArray4Cpv_Praw_at_put_grow6Mirk03_v_; +text: .text%__1cNGrowableArray4Cl_Praw_at_put_grow6Mirkl2_v_; +text: .text%__1cISubINodeLbottom_type6kM_pknEType__; +text: .text%__1cIGraphKitZset_all_rewritable_memory6MpnENode__v_; +text: .text%__1cIGraphKitTset_all_memory_call6MpnENode__v_; +text: .text%__1cMtlsLoadPNodeLbottom_type6kM_pknEType__; +text: .text%__1cJAssemblerEmovq6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cRsalI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cNloadRangeNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_; +text: .text%__1cGRFrameMset_distance6Mi_v_; +text: .text%__1cICodeBlobOis_c2i_adapter6kM_i_; +text: .text%__1cFframeTis_first_java_frame6kM_i_; +text: .text%__1cFframeLreal_sender6kMpnLRegisterMap__0_; +text: .text%__1cGRFrameGcaller6M_p0_; +text: .text%__1cTStackWalkCompPolicyIsenderOf6MpnGRFrame_pnNGrowableArray4C2___2_; +text: .text%__1cGRFrameKnew_RFrame6FnFframe_pnKJavaThread_p0_4_; +text: .text%__1cKstoreLNodePoper_input_base6kM_I_; +text: .text%__1cTconstantPoolOopDescMklass_at_put6MipnMklassOopDesc__v_; +text: .text%__1cNFingerprinterGdo_int6M_v_; +text: .text%__1cNSafepointBlobbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cRaddI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cRshrL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHPhiNodeEmake6FpnENode_2_p0_; +text: .text%__1cScompI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__; +text: .text%__1cWstatic_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cLRethrowNodeGis_CFG6kM_i_; +text: .text%__1cIciObjectFklass6M_pnHciKlass__; +text: .text%__1cNloadConP0NodeMideal_Opcode6kM_i_; +text: .text%__1cOPhaseIdealLoopOsplit_thru_phi6MpnENode_2i_2_; +text: .text%__1cNGCTaskManagerRset_resource_flag6MIi_v_; +text: .text%__1cRshrI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cUmembar_cpu_orderNodeMideal_Opcode6kM_i_; +text: .text%__1cLklassVtableIindex_of6kMpnNmethodOopDesc_i_i_; +text: .text%__1cKEntryPointFentry6kMnITosState__pC_; +text: .text%__1cJloadCNodeMideal_Opcode6kM_i_; +text: .text%__1cKJavaThreadJframes_do6MpFpnFframe_pknLRegisterMap__v_v_; +text: .text%__1cNMemoryManagerHoops_do6MpnKOopClosure__v_; +text: .text%__1cRInvocationCounterJset_carry6M_v_; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cFStateM_sub_Op_RegL6MpknENode__v_; +text: .text%__1cNdecI_rRegNodeErule6kM_I_; +text: .text%__1cKjmpConNodeJis_Branch6kM_I_; +text: .text%__1cKjmpConNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmpFastLockNodeMideal_Opcode6kM_i_; +text: .text%__1cKjmpConNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cJttyLockerbCbreak_tty_lock_for_safepoint6Fl_v_; +text: .text%__1cNCallGeneratorJis_inline6kM_i_; +text: .text%__1cUSafepointSynchronizeFblock6FpnKJavaThread__v_; +text: .text%__1cCosRcurrent_thread_id6F_l_; +text: .text%__1cKciTypeFlowLStateVectorMdo_getstatic6MpnQciBytecodeStream__v_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_; +text: .text%__1cLLShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKstoreLNodeMideal_Opcode6kM_i_; +text: .text%__1cNloadKlassNodeHtwo_adr6kM_I_; +text: .text%__1cFParseYprofile_not_taken_branch6M_v_; +text: .text%__1cHPhiNodeMslice_memory6kMpknHTypePtr__p0_; +text: .text%__1cOcompL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cLRuntimeStubMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cLSymbolTableFprobe6Fpkci_pnNsymbolOopDesc__; +text: .text%__1cOcompL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cPcmpFastLockNodePoper_input_base6kM_I_; +text: .text%__1cNCallGeneratorKis_virtual6kM_i_; +text: .text%__1cKInlineTreePshouldNotInline6kMpnIciMethod_pnMWarmCallInfo__pkc_; +text: .text%__1cLcastP2LNodeErule6kM_I_; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cNPhaseRegAllocKoffset2reg6kMi_i_; +text: .text%__1cQjmpCon_shortNodeMideal_Opcode6kM_i_; +text: .text%__1cQjmpCon_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_pnGRFrame__v_; +text: .text%__1cTconvI2L_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cENodeRlatency_from_uses6kMrnLBlock_Array_rnNGrowableArray4CI___i_; +text: .text%__1cNGrowableArray4CI_Praw_at_put_grow6MirkI2_v_; +text: .text%__1cFParseFdo_if6MpnENode_2nIBoolTestEmask_2_v_; +text: .text%__1cXmembar_acquire_lockNodeMideal_Opcode6kM_i_; +text: .text%__1cKklassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cXindIndexScaleOffsetOperOindex_position6kM_i_; +text: .text%__1cXindIndexScaleOffsetOperNbase_position6kM_i_; +text: .text%__1cPsalI_rReg_1NodePoper_input_base6kM_I_; +text: .text%__1cIGraphKitJpush_node6MnJBasicType_pnENode__v_; +text: .text%__1cISubINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSconstMethodOopDescbEchecked_exceptions_length_addr6kM_pH_; +text: .text%__1cPThreadLocalNodeGOpcode6kM_i_; +text: .text%__1cRsubI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cJloadCNodePoper_input_base6kM_I_; +text: .text%__1cSThreadLocalStoragePget_thread_slow6F_pnGThread__; +text: .text%__1cCosXthread_local_storage_at6Fi_pv_; +text: .text%__1cLRuntimeStubYcaller_must_gc_arguments6kMpnKJavaThread__i_; +text: .text%__1cIAddPNodeJideal_reg6kM_I_; +text: .text%__1cTleaPIdxScaleOffNodeJnum_opnds6kM_I_; +text: .text%__1cRaddI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cFParseNthrow_to_exit6MpnNSafePointNode__v_; +text: .text%__1cQCallLeafNoFPNodeGOpcode6kM_i_; +text: .text%__1cKTypeRawPtrHget_con6kM_l_; +text: .text%__1cOClearArrayNodeGOpcode6kM_i_; +text: .text%__1cOoop_RelocationHoops_do6MpFppnHoopDesc__v_v_; +text: .text%__1cIciMethodbHhas_unloaded_classes_in_signature6M_i_; +text: .text%__1cScompP_mem_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cFStateM_sub_Op_CmpI6MpknENode__v_; +text: .text%__1cNincI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cJimmP0OperEtype6kM_pknEType__; +text: .text%__1cNloadConP0NodeLbottom_type6kM_pknEType__; +text: .text%__1cPloadConUL32NodeLbottom_type6kM_pknEType__; +text: .text%__1cNloadConI0NodeHsize_of6kM_I_; +text: .text%__1cRaddI_rReg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cPshrI_rReg_1NodePoper_input_base6kM_I_; +text: .text%JVM_handle_solaris_signal; +text: .text%signalHandler; +text: .text%__1cQJNI_FastGetFieldQfind_slowcase_pc6FpC_1_; +text: .text%__1cMLinkResolverbElinktime_resolve_static_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cRresolve_and_patch6FppnHoopDesc__v_; +text: .text%__1cFStateN_sub_Op_LoadP6MpknENode__v_; +text: .text%__1cISubINodeDsub6kMpknEType_3_3_; +text: .text%__1cPClassFileParserbDverify_legal_method_signature6MnMsymbolHandle_1pnGThread__i_; +text: .text%__1cMLinkResolverVresolve_invokevirtual6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cRInterpretedRFrameOis_interpreted6kM_i_; +text: .text%__1cGRFrameLis_compiled6kM_i_; +text: .text%__1cUPSGenerationCountersKupdate_all6M_v_; +text: .text%__1cTStackWalkCompPolicyMshouldInline6FnMmethodHandle_fi_pkc_; +text: .text%__1cFArenaRdestruct_contents6M_v_; +text: .text%__1cIGraphKitPstore_to_memory6MpnENode_22nJBasicType_i_2_; +text: .text%__1cJStoreNodeEmake6FpnENode_22pknHTypePtr_2nJBasicType__p0_; +text: .text%__1cXPhaseAggressiveCoalesceYinsert_copy_with_overlap6MpnFBlock_pnENode_II_v_; +text: .text%__1cULinearLeastSquareFitGupdate6Mdd_v_; +text: .text%__1cKciTypeFlowGJsrSetSis_compatible_with6Mp1_i_; +text: .text%__1cENodeIadd_prec6Mp0_v_; +text: .text%__1cKOSRAdapterOis_osr_adapter6kM_i_; +text: .text%__1cIMulINodeGOpcode6kM_i_; +text: .text%__1cLciSignature2t6MpnHciKlass_pnIciSymbol__v_; +text: .text%__1cNGrowableArray4CpnGciType__2t6MpnFArena_iirk1_v_; +text: .text%__1cKTypeAryPtrFempty6kM_i_; +text: .text%__1cHTypeAryFempty6kM_i_; +text: .text%__1cJloadCNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cIciMethod2t6MnMmethodHandle__v_; +text: .text%__1cRandI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cJAssemblerDjcc6Mn0AJCondition_rnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cCosRelapsed_frequency6F_x_; +text: .text%__1cNinstanceKlassVis_same_class_package6FpnHoopDesc_pnNsymbolOopDesc_24_i_; +text: .text%__1cMelapsedTimerHseconds6kM_d_; +text: .text%__1cOJNIHandleBlockOallocate_block6FpnGThread__p0_; +text: .text%__1cJAssemblerDnop6M_v_; +text: .text%__1cDCHANprocess_class6FnLKlassHandle_pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___nMsymbolHandle_6_v_; +text: .text%__1cEUTF8Fequal6FpWi1i_i_; +text: .text%__1cKstoreLNodeJnum_opnds6kM_I_; +text: .text%__1cIjniIdMapHoops_do6MpnKOopClosure__v_; +text: .text%__1cMjniIdMapBaseHoops_do6MpnKOopClosure__v_; +text: .text%__1cJArrayDataKcell_count6M_i_; +text: .text%__1cGBitMapIset_from6M0_v_; +text: .text%__1cPBytecode_invokeJsignature6kM_pnNsymbolOopDesc__; +text: .text%__1cKType_ArrayEgrow6MI_v_; +text: .text%JVM_Write; +text: .text%__1cDhpiFwrite6FipkvI_L_; +text: .text%__1cMStartC2INodeGOpcode6kM_i_; +text: .text%__1cSindIndexOffsetOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFframebGinterpreter_callee_receiver_addr6MnMsymbolHandle__ppnHoopDesc__; +text: .text%__1cIAddLNodeLbottom_type6kM_pknEType__; +text: .text%__1cKMemBarNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cIParmNodeJideal_reg6kM_I_; +text: .text%__1cVAdaptivePaddedAverageGsample6Mf_v_; +text: .text%__1cMtlsLoadPNodeMideal_Opcode6kM_i_; +text: .text%__1cRshrI_rReg_immNodeErule6kM_I_; +text: .text%__1cJcmpOpOperGnegate6M_v_; +text: .text%__1cHTypeAryEmake6FpknEType_pknHTypeInt__pk0_; +text: .text%__1cFParseRoptimize_inlining6MpnIciMethod_ipnPciInstanceKlass_24irnKInlineTreeLInlineStyle_r2_v_; +text: .text%__1cQimprove_receiver6FpnPciInstanceKlass_pknLTypeInstPtr_ri_1_; +text: .text%__1cPcmpFastLockNodeHtwo_adr6kM_I_; +text: .text%__1cMLinkResolverTresolve_static_call6FrnICallInfo_rnLKlassHandle_nMsymbolHandle_53iipnGThread__v_; +text: .text%__1cPCheckCastPPNodeJideal_reg6kM_I_; +text: .text%__1cKJavaThreadUin_stack_yellow_zone6MpC_i_; +text: .text%__1cFParseSmerge_memory_edges6MpnMMergeMemNode_ii_v_; +text: .text%__1cJAssemblerEmovq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cUSafepointSynchronizebDhandle_polling_page_exception6FpnKJavaThread__v_; +text: .text%__1cUThreadSafepointStatebDhandle_polling_page_exception6M_v_; +text: .text%__1cLjmpConUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNandL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cKcmpOpUOperFccode6kM_i_; +text: .text%__1cLjmpConUNodeTmay_be_short_branch6kM_i_; +text: .text%__1cLjmpConUNodeOis_pc_relative6kM_i_; +text: .text%__1cIPipelinePoperand_latency6kMIpk0_I_; +text: .text%__1cWCallLeafNoFPDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cQPlaceholderTableKfind_entry6MiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cJTimeStampSticks_since_update6kM_x_; +text: .text%__1cURethrowExceptionNodeMideal_Opcode6kM_i_; +text: .text%__1cJloadPNodeFreloc6kM_i_; +text: .text%__1cTno_rax_rbx_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNprefetchwNodeJnum_opnds6kM_I_; +text: .text%__1cKjmpConNodeGnegate6M_v_; +text: .text%__1cMindirectOperFscale6kM_i_; +text: .text%__1cQSystemDictionaryVadd_loader_constraint6FnMsymbolHandle_nGHandle_2pnGThread__v_; +text: .text%__1cVLoaderConstraintTableJadd_entry6MnMsymbolHandle_pnMklassOopDesc_nGHandle_34pnGThread__i_; +text: .text%__1cRsubI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cTOopMapForCacheEntryZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cQComputeCallStackGdo_int6M_v_; +text: .text%__1cNloadRangeNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNtestP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadRangeNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPCheckCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFParseMvisit_blocks6M_v_; +text: .text%__1cQjmpDir_shortNodeMideal_Opcode6kM_i_; +text: .text%__1cQjmpDir_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKciTypeFlowLStateVectorLdo_getfield6MpnQciBytecodeStream__v_; +text: .text%__1cNaddI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cScompP_mem_rRegNodeErule6kM_I_; +text: .text%__1cPSignatureStreamRas_symbol_or_null6M_pnNsymbolOopDesc__; +text: .text%__1cNSafePointNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadSNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKMemBarNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOjmpLoopEndNodeMideal_Opcode6kM_i_; +text: .text%__1cFBlockTimplicit_null_check6MrnLBlock_Array_rnNGrowableArray4CI__pnENode_6_v_; +text: .text%__1cKCastPPNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cOGenerateOopMapKcopy_state6MpnNCellTypeState_2_v_; +text: .text%__1cGThreadSis_Compiler_thread6kM_i_; +text: .text%__1cRCardTableModRefBSPdirty_MemRegion6MnJMemRegion__v_; +text: .text%__1cJloadBNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cQVMOperationQdDueueSqueue_remove_front6Mi_pnMVM_Operation__; +text: .text%__1cOcompI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVCompressedWriteStreamEgrow6M_v_; +text: .text%__1cNCompileBrokerYcheck_compilation_result6FnMmethodHandle_iippnHnmethod__i_; +text: .text%__1cKReflectionVis_same_class_package6FpnMklassOopDesc_2_i_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnMklassOopDesc__i_; +text: .text%JVM_RawMonitorEnter; +text: .text%__1cFMutexMjvm_raw_lock6M_v_; +text: .text%JVM_RawMonitorExit; +text: .text%__1cFMutexOjvm_raw_unlock6M_v_; +text: .text%__1cHUNICODELutf8_length6FpHi_i_; +text: .text%__1cRaddP_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cIciMethodLis_accessor6kM_i_; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__; +text: .text%__1cLBoxLockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSmembar_releaseNodeMideal_Opcode6kM_i_; +text: .text%__1cQciBytecodeStreamSget_constant_index6kM_i_; +text: .text%__1cOGenerateOopMapOset_bbmark_bit6Mi_v_; +text: .text%__1cFParseOreturn_current6MpnENode__v_; +text: .text%__1cLConvI2LNodeJideal_reg6kM_I_; +text: .text%__1cJStartNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cMorI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cMloadConPNodeFreloc6kM_i_; +text: .text%__1cGThreadMis_VM_thread6kM_i_; +text: .text%__1cSPSPromotionManagerFreset6M_v_; +text: .text%__1cNPrefetchQdDueueFclear6M_v_; +text: .text%__1cSPSPromotionManagerKflush_labs6M_v_; +text: .text%__1cFciEnvVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cOJNIHandleBlockNrelease_block6Fp0pnGThread__v_; +text: .text%__1cNincI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cTJvmtiEventCollectorYunset_jvmti_thread_state6M_v_; +text: .text%__1cLRShiftINodeLbottom_type6kM_pknEType__; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2t6M_v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2T6M_v_; +text: .text%__1cJTypeTupleFxdual6kM_pknEType__; +text: .text%__1cOcompP_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cHi2sNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_2_v_; +text: .text%__1cLcastP2LNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRciVirtualCallDataOtranslate_from6MpnLProfileData__v_; +text: .text%__1cXinitialize_static_field6FpnPfieldDescriptor_pnGThread__v_: classFileParser.o; +text: .text%__1cKstoreCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNCompileBrokerXcompilation_is_in_queue6FnMmethodHandle_i_i_; +text: .text%__1cRsubI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cETypeCeq6kMpk0_i_; +text: .text%__1cHMatcherPstack_alignment6F_I_; +text: .text%__1cNloadKlassNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadSNodeJnum_opnds6kM_I_; +text: .text%__1cJMultiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRshrL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cTconvI2L_reg_memNodeErule6kM_I_; +text: .text%__1cMURShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRcmpFastUnlockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSInterpreterRuntimebAfrequency_counter_overflow6FpnKJavaThread_pC_n0AJIcoResult__; +text: .text%__1cHSubNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQjava_lang_StringGlength6FpnHoopDesc__i_; +text: .text%__1cLRethrowNodeGOpcode6kM_i_; +text: .text%__1cPcmpFastLockNodeJnum_opnds6kM_I_; +text: .text%__1cIciMethodPcan_be_compiled6M_i_; +text: .text%__1cFParseQcreate_entry_map6M_pnNSafePointNode__; +text: .text%__1cFParseIdo_exits6M_v_; +text: .text%__1cFParseLbuild_exits6M_v_; +text: .text%__1cFParseLinit_blocks6M_v_; +text: .text%__1cFParse2t6MpnIJVMState_pnIciMethod_f_v_; +text: .text%__1cIciMethodVhas_balanced_monitors6M_i_; +text: .text%__1cFParseNdo_all_blocks6M_v_; +text: .text%__1cOParseGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cOParseGeneratorJcan_parse6FpnIciMethod_i_i_; +text: .text%__1cFArenaEused6kM_L_; +text: .text%__1cRandI_rReg_immNodeErule6kM_I_; +text: .text%jni_GetSuperclass: jni.o; +text: .text%__1cPno_rax_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFStateM_sub_Op_AddI6MpknENode__v_; +text: .text%__1cPClassFileParserUskip_over_field_name6MpciI_1_; +text: .text%__1cFParsePdo_method_entry6M_v_; +text: .text%__1cNCallGeneratorKfor_inline6FpnIciMethod_f_p0_; +text: .text%__1cKciTypeFlowFBlockPclone_loop_head6Mp0ip1pn0AGJsrSet__3_; +text: .text%__1cLOpaque1NodeGOpcode6kM_i_; +text: .text%__1cHciKlassOis_subclass_of6Mp0_i_; +text: .text%__1cbBjava_lang_ref_SoftReferenceFclock6F_x_; +text: .text%__1cTAbstractInterpreterbFsize_top_interpreter_activation6FpnNmethodOopDesc__i_; +text: .text%__1cRruntime_type_from6FpnJJavaValue__nJBasicType__: javaCalls.o; +text: .text%__1cCosbCstack_shadow_pages_available6FpnGThread_nMmethodHandle__i_; +text: .text%__1cPJavaCallWrapper2t6MnMmethodHandle_nGHandle_pnJJavaValue_pnGThread__v_; +text: .text%__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_; +text: .text%__1cRJavaCallArgumentsKparameters6M_pl_; +text: .text%__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cLCastP2LNodeLbottom_type6kM_pknEType__; +text: .text%__1cPJavaCallWrapper2T6M_v_; +text: .text%__1cVPreserveExceptionMark2T6M_v_; +text: .text%__1cVPreserveExceptionMark2t6MrpnGThread__v_; +text: .text%__1cMrax_RegPOperJnum_edges6kM_I_; +text: .text%__1cMrax_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cITypeFuncFxdual6kM_pknEType__; +text: .text%__1cIimmLOperJconstantL6kM_x_; +text: .text%__1cIMulLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLRShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNCompileBrokerTcompile_method_base6FnMmethodHandle_ii1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNCompileBrokerOcompile_method6FnMmethodHandle_i1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNmethodOopDescWload_signature_classes6FnMmethodHandle_pnGThread__i_; +text: .text%__1cTconstantPoolOopDescbDresolve_string_constants_impl6FnSconstantPoolHandle_pnGThread__v_; +text: .text%__1cYDebugInformationRecorderNadd_dependent6MpnPciInstanceKlass_pnIciMethod__v_; +text: .text%__1cHciField2t6MpnPciInstanceKlass_i_v_; +text: .text%__1cTconstantPoolOopDescbCklass_ref_at_if_loaded_check6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cMLinkResolverXresolve_klass_no_update6FrnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNaddL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cISubINodeGadd_id6kM_pknEType__; +text: .text%__1cNsubI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cGciType2t6MnLKlassHandle__v_; +text: .text%__1cMMutableSpaceKinitialize6MnJMemRegion_i_v_; +text: .text%__1cHciKlass2t6MnLKlassHandle__v_; +text: .text%__1cKInlineTree2t6MpnHCompile_pk0pnIciMethod_pnIJVMState_if_v_; +text: .text%__1cJEventMark2t6MpkcE_v_; +text: .text%__1cJloadCNodeJnum_opnds6kM_I_; +text: .text%__1cNaddI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cQComputeCallStackHdo_long6M_v_; +text: .text%__1cMindirectOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cRaddI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRLowMemoryDetectorWdetect_after_gc_memory6FpnKMemoryPool__v_; +text: .text%__1cQVMOperationQdDueueNqueue_oops_do6MipnKOopClosure__v_; +text: .text%__1cMCreateExNodeJideal_reg6kM_I_; +text: .text%__1cMorI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMorI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cRmethodDataOopDescLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cNSCMemProjNodeGOpcode6kM_i_; +text: .text%__1cNSignatureInfoHdo_long6M_v_; +text: .text%__1cLPCTableNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMCreateExNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRinterpretedVFrameDbci6kM_i_; +text: .text%__1cKInlineTreeYcompute_callee_frequency6kMi_f_; +text: .text%__1cKInlineTreebCbuild_inline_tree_for_callee6MpnIciMethod_pnIJVMState_i_p0_; +text: .text%__1cRinterpretedVFrameDbcp6kM_pC_; +text: .text%__1cRInterpretedRFrameKtop_vframe6kM_pnKjavaVFrame__; +text: .text%__1cIciMethodbBinterpreter_call_site_count6Mi_i_; +text: .text%__1cLRShiftLNodeGOpcode6kM_i_; +text: .text%__1cPsarI_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cNsubI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cOMethodLivenessKBasicBlockIload_two6Mi_v_; +text: .text%__1cFKlassTarray_klass_or_null6Mi_pnMklassOopDesc__; +text: .text%__1cNmulL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cNobjArrayKlassPoop_is_objArray6kM_i_; +text: .text%__1cLklassVtableXvtable_accessibility_at6Mi_n0AKAccessType__; +text: .text%__1cNrFlagsRegOperFclone6kM_pnIMachOper__; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_; +text: .text%__1cNCatchProjNodeDcmp6kMrknENode__I_; +text: .text%__1cIGraphKitRmake_slow_call_ex6MpnENode_pnPciInstanceKlass__v_; +text: .text%__1cTcompareAndSwapLNodePoper_input_base6kM_I_; +text: .text%__1cMloadConINodeHsize_of6kM_I_; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_i_v_; +text: .text%__1cRMachSafePointNodeLis_MachCall6M_pnMMachCallNode__; +text: .text%__1cNstoreImmINodeMideal_Opcode6kM_i_; +text: .text%__1cJScopeDescGis_top6kM_i_; +text: .text%__1cHOrINodeLbottom_type6kM_pknEType__; +text: .text%__1cPstoreImmI16NodeMideal_Opcode6kM_i_; +text: .text%__1cMindIndexOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cFStateQ_sub_Op_CreateEx6MpknENode__v_; +text: .text%__1cRshrL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cLjmpConUNodeJis_Branch6kM_I_; +text: .text%__1cLjmpConUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLjmpConUNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cRaddI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cPciInstanceKlass2t6MnLKlassHandle__v_; +text: .text%__1cHCompileXin_preserve_stack_slots6M_I_; +text: .text%__1cMMachCallNodeHis_Call6M_pnICallNode__; +text: .text%__1cNdecI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKStoreCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLLShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cTtypeArrayKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLklassVtableKis_miranda6FpnNmethodOopDesc_pnPobjArrayOopDesc_pnMklassOopDesc__i_; +text: .text%__1cTconvL2I_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cRalign_code_offset6Fi_I_; +text: .text%__1cMURShiftINodeLbottom_type6kM_pknEType__; +text: .text%__1cMorI_rRegNodeErule6kM_I_; +text: .text%__1cMLinkResolverVresolve_invokespecial6FrnICallInfo_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cHMulNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cTconvL2I_reg_regNodeErule6kM_I_; +text: .text%__1cRmethodDataOopDescJbci_to_dp6Mi_pC_; +text: .text%__1cIAddLNodeGadd_id6kM_pknEType__; +text: .text%__1cRaddL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cLRShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFTypeFEhash6kM_i_; +text: .text%__1cIGraphKitMarray_length6MpnENode__2_; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_; +text: .text%__1cPsalI_rReg_1NodeErule6kM_I_; +text: .text%__1cIJVMState2t6Mi_v_; +text: .text%__1cNstoreImmBNodeHtwo_adr6kM_I_; +text: .text%__1cLLShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cScompU_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cNGrowableArray4Cl_Icontains6kMrkl_i_; +text: .text%__1cScompU_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_StringGequals6FpnHoopDesc_pHi_i_; +text: .text%__1cOcompP_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTCreateExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMURShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKRegionNodeOhas_unique_phi6kM_pnHPhiNode__; +text: .text%__1cIMulINodeLbottom_type6kM_pknEType__; +text: .text%__1cKTypeAryPtrFxdual6kM_pknEType__; +text: .text%__1cVExceptionHandlerTableMadd_subtable6MipnNGrowableArray4Cl__22_v_; +text: .text%__1cNandL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cNObjectMonitorHis_busy6kM_l_; +text: .text%__1cEDict2t6MpFpkv2_ipF2_i_v_; +text: .text%__1cJAssemblerElock6M_v_; +text: .text%__1cJAssemblerIcmpxchgq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cSsafePoint_pollNodePoper_input_base6kM_I_; +text: .text%__1cLRuntimeStubbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cPshrI_rReg_1NodeErule6kM_I_; +text: .text%__1cRmethodDataOopDescKmileage_of6FpnNmethodOopDesc__i_; +text: .text%__1cKManagementJtimestamp6F_x_; +text: .text%__1cbDReferenceProcessorInitializerIis_clean6kM_v_; +text: .text%__1cIPSOldGenPupdate_counters6M_v_; +text: .text%__1cNSingletonBlobIis_alive6kM_i_; +text: .text%__1cKTypeRawPtrCeq6kMpknEType__i_; +text: .text%__1cIregDOperEtype6kM_pknEType__; +text: .text%__1cQleaPIdxScaleNodeHtwo_adr6kM_I_; +text: .text%__1cTStackWalkCompPolicyPshouldNotInline6FnMmethodHandle__pkc_; +text: .text%__1cMPrefetchNodeLbottom_type6kM_pknEType__; +text: .text%__1cPcmpFastLockNodeErule6kM_I_; +text: .text%__1cFArena2t6M_v_; +text: .text%__1cSCallLeafDirectNodePoper_input_base6kM_I_; +text: .text%__1cMCallLeafNodeLis_CallLeaf6kM_pk0_; +text: .text%__1cQleaPIdxScaleNodeMideal_Opcode6kM_i_; +text: .text%__1cJcmpOpOperFequal6kM_i_; +text: .text%__1cScompI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_IsSameObject: jni.o; +text: .text%__1cNmulL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNmulL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cIGraphKitYcombine_exception_states6MpnNSafePointNode_2_v_; +text: .text%__1cJloadBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMrcx_RegIOperJnum_edges6kM_I_; +text: .text%__1cFKlassNoop_is_method6kM_i_; +text: .text%__1cIMulLNodeLbottom_type6kM_pknEType__; +text: .text%__1cHnmethodPis_locked_by_vm6kM_i_; +text: .text%__1cONMethodSweeperPprocess_nmethod6FpnHnmethod__v_; +text: .text%__1cRaddP_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXjava_lang_ref_ReferenceWpending_list_lock_addr6F_ppnHoopDesc__; +text: .text%__1cJloadLNodeHtwo_adr6kM_I_; +text: .text%__1cHMulNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMrep_stosNodePoper_input_base6kM_I_; +text: .text%__1cRsalI_rReg_immNodeErule6kM_I_; +text: .text%__1cJFieldTypeSskip_optional_size6FpnNsymbolOopDesc_pi_v_; +text: .text%__1cMloadConPNodeHsize_of6kM_I_; +text: .text%__1cSCallLeafDirectNodeHtwo_adr6kM_I_; +text: .text%__1cHnmethodVcleanup_inline_caches6M_v_; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__; +text: .text%__1cQsolaris_mprotect6FpcLi_i_: os_solaris.o; +text: .text%__1cRaddI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cHnmethodLis_unloaded6kM_i_; +text: .text%__1cOGenerateOopMapHppstore6MpnNCellTypeState_i_v_; +text: .text%__1cIGraphKitXset_edges_for_java_call6MpnMCallJavaNode_i_v_; +text: .text%__1cTconvI2L_reg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cHi2sNodeJnum_opnds6kM_I_; +text: .text%__1cSconstMethodOopDescZchecked_exceptions_length6kM_i_; +text: .text%__1cHMatcherXadjust_incoming_stk_arg6Mi_i_; +text: .text%__1cNIdealLoopTreeIset_nest6MI_i_; +text: .text%__1cNIdealLoopTreeMcounted_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cRsubI_rReg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cIGraphKitZset_results_for_java_call6MpnMCallJavaNode__pnENode__; +text: .text%__1cTconvI2L_reg_memNodePoper_input_base6kM_I_; +text: .text%__1cFStateM_sub_Op_CmpU6MpknENode__v_; +text: .text%__1cLRethrowNodeKmatch_edge6kMI_I_; +text: .text%__1cKcopy_table6FppC1i_v_: interpreter.o; +text: .text%__1cUBytecode_tableswitchOdest_offset_at6kMi_i_; +text: .text%__1cNobjArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cKTypeRawPtrFxmeet6kMpknEType__3_; +text: .text%__1cMVM_OperationVevaluate_at_safepoint6kM_i_; +text: .text%__1cMVM_OperationVevaluate_concurrently6kM_i_; +text: .text%__1cMVM_OperationSis_cheap_allocated6kM_i_; +text: .text%__1cXmembar_release_lockNodePoper_input_base6kM_I_; +text: .text%__1cRaddL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cScompP_mem_rRegNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cJFieldTypeOget_array_info6FpnNsymbolOopDesc_pip2pnGThread__nJBasicType__; +text: .text%__1cJFieldTypeYis_valid_array_signature6FpnNsymbolOopDesc__i_; +text: .text%__1cNincI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObject2t6M_v_; +text: .text%__1cPstoreImmI16NodePoper_input_base6kM_I_; +text: .text%__1cNinstanceKlassbDcheck_valid_for_instantiation6MipnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_class_modifiers6MipnGThread__v_; +text: .text%__1cQLibraryIntrinsicKis_virtual6kM_i_; +text: .text%__1cPciObjectFactoryUget_empty_methodData6M_pnMciMethodData__; +text: .text%__1cMciMethodData2t6M_v_; +text: .text%__1cPsarI_rReg_1NodePoper_input_base6kM_I_; +text: .text%__1cNstoreImmBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFTypeDEhash6kM_i_; +text: .text%__1cMPrefetchNodeKmatch_edge6kMI_I_; +text: .text%__1cHCompileQcan_generate_C2I6MpnIciMethod_i_i_; +text: .text%__1cPloadConUL32NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPciObjArrayKlassSis_obj_array_klass6M_i_; +text: .text%__1cLOpaque1NodeEhash6kM_I_; +text: .text%__1cXmembar_release_lockNodeHtwo_adr6kM_I_; +text: .text%JVM_GetMethodIxModifiers; +text: .text%__1cJloadBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNandL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cNandL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cFKlassMset_subklass6MpnMklassOopDesc__v_; +text: .text%__1cTCallDynamicJavaNodeGOpcode6kM_i_; +text: .text%__1cJloadINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKklassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cUCompressedReadStreamMraw_read_int6FrpC_i_; +text: .text%__1cIHaltNodeEhash6kM_I_; +text: .text%__1cNstoreImmINodePoper_input_base6kM_I_; +text: .text%__1cLAccessFlagsPatomic_set_bits6Mi_v_; +text: .text%__1cNinstanceKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cNinstanceKlassScopy_static_fields6MpnSPSPromotionManager__v_; +text: .text%__1cSinstanceKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cOcompL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQVMOperationQdDueueLremove_next6M_pnMVM_Operation__; +text: .text%__1cQciTypeArrayKlassTis_type_array_klass6M_i_; +text: .text%__1cRsubI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFStateP_sub_Op_LShiftL6MpknENode__v_; +text: .text%__1cLjmpConUNodeGnegate6M_v_; +text: .text%__1cKcmpOpUOperGnegate6M_v_; +text: .text%__1cMrax_RegLOperJnum_edges6kM_I_; +text: .text%__1cLGCTaskQdDueueKinitialize6M_v_; +text: .text%__1cJStealTask2t6Mi_v_; +text: .text%__1cJStealTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cTOldToYoungRootsTaskEname6M_pc_; +text: .text%__1cTOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cNGCTaskManagerMnote_release6MI_v_; +text: .text%__1cJStealTaskEname6M_pc_; +text: .text%__1cSCardTableExtensionbAscavenge_contents_parallel6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager_I_v_; +text: .text%__1cFciEnvbAget_constant_by_index_impl6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cQciBytecodeStreamMget_constant6M_nKciConstant__; +text: .text%__1cFciEnvVget_constant_by_index6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cJcmpOpOperFclone6kM_pnIMachOper__; +text: .text%__1cMrep_stosNodeMideal_Opcode6kM_i_; +text: .text%__1cEhash6Fpkc1_I_; +text: .text%__1cQput_after_lookup6FnMsymbolHandle_0ppnLNameSigHash__i_; +text: .text%__1cKJavaThreadLgc_epilogue6M_v_; +text: .text%__1cKJavaThreadLgc_prologue6M_v_; +text: .text%__1cTsize_java_to_interp6F_I_; +text: .text%__1cUreloc_java_to_interp6F_I_; +text: .text%__1cQinit_input_masks6FIrnHRegMask_1_p0_: matcher.o; +text: .text%__1cKOSRAdapterHoops_do6MpnKOopClosure__v_; +text: .text%__1cRCompilationPolicybIreset_counter_for_invocation_event6MnMmethodHandle__v_; +text: .text%__1cRitableMethodEntryKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cTcompareAndSwapLNodeMideal_Opcode6kM_i_; +text: .text%__1cNinstanceKlassPlink_class_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIGraphKitbBset_arguments_for_java_call6MpnMCallJavaNode__v_; +text: .text%__1cNCallGeneratorCtf6kM_pknITypeFunc__; +text: .text%__1cMloadConLNodeLbottom_type6kM_pknEType__; +text: .text%__1cKStoreBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNaddL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cTcompareAndSwapLNodeJnum_opnds6kM_I_; +text: .text%__1cFStateO_sub_Op_StoreI6MpknENode__v_; +text: .text%__1cQleaPIdxScaleNodePoper_input_base6kM_I_; +text: .text%__1cNGrowableArray4CpnNmethodOopDesc__2t6Mii_v_; +text: .text%__1cLklassVtableMget_mirandas6FpnNGrowableArray4CpnNmethodOopDesc___pnMklassOopDesc_pnPobjArrayOopDesc_8_v_; +text: .text%__1cXJNI_ArgumentPusherVaArgKget_object6M_v_; +text: .text%__1cNloadConP0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFKlassKsuperklass6kM_pnNinstanceKlass__; +text: .text%__1cWstatic_stub_RelocationJpack_data6M_i_; +text: .text%__1cNsubL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cbAjni_check_async_exceptions6FpnKJavaThread__v_: jni.o; +text: .text%__1cKJavaThreadbHcheck_and_handle_async_exceptions6Mi_v_; +text: .text%__1cRsalI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMindIndexOperNbase_position6kM_i_; +text: .text%__1cMindIndexOperOindex_position6kM_i_; +text: .text%__1cMindIndexOperNconstant_disp6kM_i_; +text: .text%__1cJLoadSNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNGrowableArray4CpnOMethodLivenessKBasicBlock__2t6Mii_v_; +text: .text%__1cKstoreINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCallGeneratorPfor_direct_call6FpnIciMethod__p0_; +text: .text%__1cTDirectCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cMWarmCallInfoLalways_cold6F_p0_; +text: .text%__1cFframeIpatch_pc6MpnGThread_pC_v_; +text: .text%JVM_IsInterface; +text: .text%__1cFKlassQset_next_sibling6MpnMklassOopDesc__v_; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_; +text: .text%__1cRshrL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQjmpCon_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQjmpCon_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKjmpConNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cQjmpCon_shortNodeJis_Branch6kM_I_; +text: .text%__1cKJavaThreadNpd_last_frame6M_nFframe__; +text: .text%__1cTStackWalkCompPolicyVfindTopInlinableFrame6MpnNGrowableArray4CpnGRFrame____2_; +text: .text%__1cKJavaThreadQlast_java_vframe6MpnLRegisterMap__pnKjavaVFrame__; +text: .text%__1cTStackWalkCompPolicyXmethod_invocation_event6MnMmethodHandle_pnGThread__v_; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_nMmethodHandle__v_; +text: .text%__1cNGrowableArray4CpnGRFrame__2t6Mii_v_; +text: .text%__1cKjavaVFrameNis_java_frame6kM_i_; +text: .text%__1cIVerifierRshould_verify_for6FpnHoopDesc__i_; +text: .text%__1cQciBytecodeStreamPget_klass_index6M_i_; +text: .text%__1cRMachNullCheckNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRMachNullCheckNode2t6MpnENode_2I_v_; +text: .text%__1cRsarI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNMachIdealNodeJnum_opnds6kM_I_; +text: .text%__1cRMachSafePointNodePis_MachCallJava6M_pnQMachCallJavaNode__; +text: .text%__1cKstorePNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWImplicitExceptionTableGappend6MII_v_; +text: .text%__1cHUNICODEHas_utf86FpHi_pc_; +text: .text%__1cMStartI2CNodeGOpcode6kM_i_; +text: .text%__1cKOSRAdapterMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cSvframeStreamCommonbHskip_method_invoke_and_aux_frames6M_v_; +text: .text%__1cNdecI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cIMinINodeGOpcode6kM_i_; +text: .text%__1cNinstanceKlassbCfind_local_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cNinstanceKlassWfind_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cPciInstanceKlassTget_field_by_offset6Mii_pnHciField__; +text: .text%__1cFArena2T6M_v_; +text: .text%__1cKmethodOperJnum_edges6kM_I_; +text: .text%__1cSconstMethodOopDescYchecked_exceptions_start6kM_pnXCheckedExceptionElement__; +text: .text%__1cISubLNodeGOpcode6kM_i_; +text: .text%__1cFStateO_sub_Op_StoreP6MpknENode__v_; +text: .text%__1cRshrI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRsarL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNstoreImmBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstorePNodeFreloc6kM_i_; +text: .text%__1cYCallStaticJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cYCallStaticJavaDirectNodeJnum_opnds6kM_I_; +text: .text%__1cQleaPIdxScaleNodeErule6kM_I_; +text: .text%__1cTciConstantPoolCacheGinsert6Mipv_v_; +text: .text%__1cNinstanceKlassXmark_dependent_nmethods6MpnMklassOopDesc__i_; +text: .text%__1cMvframeStream2t6MpnKJavaThread_i_v_; +text: .text%__1cIGraphKitTuse_exception_state6MpnNSafePointNode__pnENode__; +text: .text%__1cIGraphKitSclear_saved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cNloadConI0NodeFclone6kM_pnENode__; +text: .text%__1cJimmI0OperFclone6kM_pnIMachOper__; +text: .text%__1cLCastP2LNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMLinkResolverbBresolve_static_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cKcmpOpUOperNgreater_equal6kM_i_; +text: .text%__1cPClassFileParserYparse_checked_exceptions6MpHInSconstantPoolHandle_pnGThread__1_; +text: .text%__1cXcopy_u2_with_conversion6FpH0i_v_: classFileParser.o; +text: .text%__1cENodeGis_Sub6M_pnHSubNode__; +text: .text%__1cJAssemblerFtestq6MpnMRegisterImpl_2_v_; +text: .text%__1cJCMoveNodeLis_cmove_id6FpnOPhaseTransform_pnENode_44pnIBoolNode__4_; +text: .text%__1cZresource_reallocate_bytes6FpcLL_0_; +text: .text%__1cKstoreINodeFreloc6kM_i_; +text: .text%__1cLsymbolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQciBytecodeStreamJget_klass6Mri_pnHciKlass__; +text: .text%__1cKMemBarNode2t6M_v_; +text: .text%__1cIDivINodeGOpcode6kM_i_; +text: .text%__1cFframeRis_compiled_frame6kMpi_i_; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_; +text: .text%__1cPshrI_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMorI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFParseQarray_addressing6MnJBasicType_ippknEType__pnENode__; +text: .text%__1cPsalI_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHciField2t6MpnPfieldDescriptor__v_; +text: .text%__1cIAddLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIModINodeGOpcode6kM_i_; +text: .text%__1cNmulL_rRegNodeErule6kM_I_; +text: .text%__1cSsafePoint_pollNodeHtwo_adr6kM_I_; +text: .text%__1cDCHAManalyze_call6FnLKlassHandle_11nMsymbolHandle_2_pnJCHAResult__; +text: .text%__1cJCHAResult2t6MnLKlassHandle_nMsymbolHandle_2pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___n0E_i_v_; +text: .text%__1cMLinkResolverbCresolve_virtual_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cJCHAResultOis_monomorphic6kM_i_; +text: .text%__1cIciMethodXfind_monomorphic_target6MpnHciKlass_22_p0_; +text: .text%__1cQconstMethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cKMemBarNodeJis_MemBar6kM_pk0_; +text: .text%__1cIGraphKitOinsert_mem_bar6MpnKMemBarNode__v_; +text: .text%__1cHi2sNodeHtwo_adr6kM_I_; +text: .text%__1cJCodeCacheMfind_nmethod6Fpv_pnHnmethod__; +text: .text%__1cLConvL2INodeLbottom_type6kM_pknEType__; +text: .text%__1cMLinkResolverbCresolve_special_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cNIdealLoopTreeObeautify_loops6MpnOPhaseIdealLoop__i_; +text: .text%__1cScompP_mem_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreBNodePoper_input_base6kM_I_; +text: .text%__1cRandI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSCallLeafDirectNodeRis_safepoint_node6kM_i_; +text: .text%__1cJloadLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCallLeafDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKMemBarNodeJideal_reg6kM_I_; +text: .text%__1cJloadSNodeHtwo_adr6kM_I_; +text: .text%__1cVConstantOopWriteValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cUDebugInfoWriteStreamMwrite_handle6MpnI_jobject__v_; +text: .text%__1cLmethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNaddI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJlog2_long6Fx_i_; +text: .text%__1cTconvL2I_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitPpush_pair_local6Mi_v_; +text: .text%__1cOjmpLoopEndNodePoper_input_base6kM_I_; +text: .text%__1cMURShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHCompileSregister_intrinsic6MpnNCallGenerator__v_; +text: .text%__1cIAddLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNloadKlassNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQjava_lang_StringRas_unicode_string6FpnHoopDesc_ri_pH_; +text: .text%__1cHCmpNodeGadd_id6kM_pknEType__; +text: .text%JVM_InternString; +text: .text%__1cLStringTableGintern6FpnHoopDesc_pnGThread__2_; +text: .text%__1cFKlassQup_cast_abstract6M_p0_; +text: .text%__1cNGrowableArray4CpnENode__2t6Mii_v_; +text: .text%__1cPCheckCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSInterpreterRuntimeMmonitorenter6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cSCountedLoopEndNodeKstride_con6kM_i_; +text: .text%__1cTconvI2L_reg_memNodeJnum_opnds6kM_I_; +text: .text%__1cPCheckCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPClassFileStreamHskip_u26MipnGThread__v_; +text: .text%__1cLOpaque1NodeLbottom_type6kM_pknEType__; +text: .text%__1cOPhaseIdealLoopRsplit_thru_region6MpnENode_2_2_; +text: .text%__1cFTypeFCeq6kMpknEType__i_; +text: .text%__1cNmethodOopDescOis_initializer6kM_i_; +text: .text%__1cFciEnvRfind_system_klass6MpnIciSymbol__pnHciKlass__; +text: .text%__1cNandL_rRegNodeErule6kM_I_; +text: .text%__1cQjmpDir_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cQjmpDir_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKjmpDirNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cQjmpDir_shortNodeJis_Branch6kM_I_; +text: .text%__1cLBlock_ArrayEgrow6MI_v_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cSCompareAndSwapNodeLbottom_type6kM_pknEType__; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6FnUtypeArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cMPhaseChaitinVfind_base_for_derived6MppnENode_2rI_2_; +text: .text%__1cSindIndexOffsetOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cSindIndexOffsetOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cNGrowableArray4CI_Egrow6Mi_v_; +text: .text%__1cHMatcherMreturn_value6Fii_nLOptoRegPair__; +text: .text%__1cFStateP_sub_Op_ConvI2L6MpknENode__v_; +text: .text%__1cOjmpLoopEndNodeGpinned6kM_i_; +text: .text%__1cNxorI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cJCHAResultSmonomorphic_target6kM_nMmethodHandle__; +text: .text%__1cNsubI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_; +text: .text%__1cRcmpFastUnlockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJtimestamp6FpnHoopDesc__x_; +text: .text%__1cQLRUMaxHeapPolicyWshould_clear_reference6MpnHoopDesc__i_; +text: .text%__1cLcastP2LNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcheckCastPPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPciInstanceKlassLfind_method6MpnIciSymbol_2_pnIciMethod__; +text: .text%__1cZCallInterpreterDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cILoopNodeHsize_of6kM_I_; +text: .text%__1cSindIndexOffsetOperFscale6kM_i_; +text: .text%__1cMjniIdSupportNto_method_oop6FpnK_jmethodID__pnNmethodOopDesc__; +text: .text%__1cLBoxLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRSignatureIterator2t6MpnGThread_pnNsymbolOopDesc__v_; +text: .text%__1cRaddI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_; +text: .text%__1cGOopMapPset_derived_oop6Miiii_v_; +text: .text%__1cKstoreBNodeMideal_Opcode6kM_i_; +text: .text%__1cHi2bNodeErule6kM_I_; +text: .text%__1cFStateN_sub_Op_LoadI6MpknENode__v_; +text: .text%__1cMloadConDNodePoper_input_base6kM_I_; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__; +text: .text%__1cICmpLNodeDsub6kMpknEType_3_3_; +text: .text%__1cRjmpConU_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRjmpConU_shortNodeMideal_Opcode6kM_i_; +text: .text%__1cMloadConDNodeHtwo_adr6kM_I_; +text: .text%__1cHnmethodKpc_desc_at6MpC_pnGPcDesc__; +text: .text%__1cJrRegPOperFclone6kM_pnIMachOper__; +text: .text%__1cFParseNpush_constant6MnKciConstant__i_; +text: .text%__1cMrep_stosNodeJnum_opnds6kM_I_; +text: .text%__1cOClearArrayNodeKmatch_edge6kMI_I_; +text: .text%__1cUvisit_all_interfaces6FpnPobjArrayOopDesc_pnXInterfaceVisiterClosure__v_; +text: .text%__1cXmembar_release_lockNodeLbottom_type6kM_pknEType__; +text: .text%__1cPThreadLocalNodeJideal_reg6kM_I_; +text: .text%__1cPstoreImmI16NodeJnum_opnds6kM_I_; +text: .text%__1cTleaPIdxScaleOffNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitbMset_predefined_output_for_runtime_call6MpnENode_pnMMergeMemNode__v_; +text: .text%__1cFKlassXsearch_secondary_supers6kMpnMklassOopDesc__i_; +text: .text%__1cPsarI_rReg_1NodeErule6kM_I_; +text: .text%__1cOPhaseIdealLoopPis_counted_loop6MpnENode_pnNIdealLoopTree__2_; +text: .text%__1cIGraphKitOhas_ex_handler6M_i_; +text: .text%__1cMloadConDNodeErule6kM_I_; +text: .text%__1cHCompileQsync_stack_slots6kM_i_; +text: .text%__1cNMemoryServiceXtrack_memory_pool_usage6FpnKMemoryPool__v_; +text: .text%__1cMURShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKoopFactoryTnew_system_objArray6FipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNdecI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cPClassFileParserbHparse_constant_pool_integer_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cTDebugInfoReadStream2t6MpknHnmethod_i_v_; +text: .text%__1cRsalI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cJScopeDescJstream_at6kMi_pnTDebugInfoReadStream__; +text: .text%__1cVjava_lang_ClassLoaderGparent6FpnHoopDesc__2_; +text: .text%__1cIPhaseIFGEinit6MI_v_; +text: .text%__1cMPhaseChaitinQgather_lrg_masks6Mi_v_; +text: .text%__1cJPhaseLiveHcompute6MI_v_; +text: .text%JVM_GetCPClassNameUTF; +text: .text%__1cMLinkResolverUresolve_invokestatic6FrnICallInfo_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNstoreImmINodeJnum_opnds6kM_I_; +text: .text%__1cITypeNodeHis_Type6M_p0_; +text: .text%__1cHRetNodePoper_input_base6kM_I_; +text: .text%__1cLCastP2LNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%jni_GetStringLength: jni.o; +text: .text%__1cPloadConUL32NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOFastUnlockNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPciObjArrayKlassNelement_klass6M_pnHciKlass__; +text: .text%__1cNprefetchwNodeHtwo_adr6kM_I_; +text: .text%__1cNnmethodLocker2T6M_v_; +text: .text%__1cKoopFactoryYnew_permanent_shortArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cKstoreCNodeHtwo_adr6kM_I_; +text: .text%__1cQleaPIdxScaleNodeJnum_opnds6kM_I_; +text: .text%__1cNaddL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cOcompL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cTDebugInfoReadStreamLread_handle6M_nGHandle__; +text: .text%__1cJScopeDesc2t6MpknHnmethod_i_v_; +text: .text%__1cFStateR_sub_Op_LoadRange6MpknENode__v_; +text: .text%__1cOcompiledVFrame2t6MpknFframe_pknLRegisterMap_pnKJavaThread_pnJScopeDesc__v_; +text: .text%__1cOcompU_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmovI_reg_gNodePoper_input_base6kM_I_; +text: .text%__1cLProfileDataSis_VirtualCallData6M_i_; +text: .text%__1cSmembar_acquireNodePoper_input_base6kM_I_; +text: .text%__1cNsubL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cJMarkSweepMfollow_stack6F_v_; +text: .text%__1cNnmethodLocker2t6MpnHnmethod__v_; +text: .text%__1cNloadRangeNodeFreloc6kM_i_; +text: .text%__1cNGrowableArray4CpnKciTypeFlowJJsrRecord__2t6Miirk2i_v_; +text: .text%__1cTcompareAndSwapLNodeErule6kM_I_; +text: .text%__1cZCallDynamicJavaDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cMURShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOcompiledVFrameGis_top6kM_i_; +text: .text%__1cSInterpreterRuntimeLmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cNxorI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cRshrI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cKciTypeFlow2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cKciTypeFlowXmark_known_range_starts6M_v_; +text: .text%__1cKciTypeFlowLfind_ranges6M_v_; +text: .text%__1cSFixupMirrorClosureJdo_object6MpnHoopDesc__v_; +text: .text%__1cKciTypeFlowKmap_blocks6M_v_; +text: .text%__1cKciTypeFlowHdo_flow6M_v_; +text: .text%__1cKciTypeFlowPget_start_state6M_pkn0ALStateVector__; +text: .text%__1cKciTypeFlowKflow_types6M_v_; +text: .text%__1cIAndINodeGadd_id6kM_pknEType__; +text: .text%__1cMURShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadBNodeHtwo_adr6kM_I_; +text: .text%__1cKPSYoungGenRcapacity_in_bytes6kM_L_; +text: .text%__1cHMonitorGnotify6M_i_; +text: .text%__1cYciExceptionHandlerStreamPcount_remaining6M_i_; +text: .text%__1cFParseXcatch_inline_exceptions6MpnNSafePointNode__v_; +text: .text%__1cHMatcherNfind_receiver6Fi_i_; +text: .text%__1cMciMethodDataJload_data6M_v_; +text: .text%__1cIciMethodJload_code6M_v_; +text: .text%__1cJCmpL3NodeGOpcode6kM_i_; +text: .text%__1cIAndINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cWstatic_call_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cUParallelScavengeHeapEused6kM_L_; +text: .text%__1cOGenerateOopMapGdo_ldc6Mii_v_; +text: .text%__1cMTypeKlassPtrFxdual6kM_pknEType__; +text: .text%__1cIMaxINodeGOpcode6kM_i_; +text: .text%__1cOPhaseTransform2t6MnFPhaseLPhaseNumber__v_; +text: .text%__1cPsalI_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cQSystemDictionarybAcompute_loader_lock_object6FnGHandle_pnGThread__1_; +text: .text%__1cHciKlassMis_interface6M_i_; +text: .text%__1cPmethodDataKlassRoop_is_methodData6kM_i_; +text: .text%__1cIMulLNodeGadd_id6kM_pknEType__; +text: .text%__1cJloadCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionaryRpreloaded_oops_do6FpnKOopClosure__v_; +text: .text%__1cIVMThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cLJvmtiExportHoops_do6FpnKOopClosure__v_; +text: .text%__1cMFlatProfilerHoops_do6FpnKOopClosure__v_; +text: .text%__1cQVMOperationQdDueueHoops_do6MpnKOopClosure__v_; +text: .text%__1cNMemoryServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cOJNIHandleBlockMweak_oops_do6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cKJNIHandlesMweak_oops_do6FpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSObjectSynchronizerHoops_do6FpnKOopClosure__v_; +text: .text%__1cKManagementHoops_do6FpnKOopClosure__v_; +text: .text%__1cQPlaceholderTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cNThreadServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cKJNIHandlesHoops_do6FpnKOopClosure__v_; +text: .text%__1cXJvmtiCurrentBreakpointsHoops_do6FpnKOopClosure__v_; +text: .text%__1cIUniverseHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollectorXoops_do_for_all_threads6FpnKOopClosure__v_; +text: .text%__1cRindIndexScaleOperJnum_edges6kM_I_; +text: .text%__1cRindIndexScaleOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKstoreBNodeJnum_opnds6kM_I_; +text: .text%__1cNSignatureInfoJdo_double6M_v_; +text: .text%__1cJAssemblerEmovl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cRsalI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cMrdx_RegIOperEtype6kM_pknEType__; +text: .text%__1cMciMethodData2t6MnQmethodDataHandle__v_; +text: .text%__1cSmembar_acquireNodeHtwo_adr6kM_I_; +text: .text%__1cRshrI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cKJNIHandlesLmake_global6FnGHandle_i_pnI_jobject__; +text: .text%jni_ExceptionOccurred: jni.o; +text: .text%jni_SetObjectArrayElement: jni.o; +text: .text%__1cSCompareAndSwapNodeKmatch_edge6kMI_I_; +text: .text%__1cISubINodeJideal_reg6kM_I_; +text: .text%__1cRMachSafePointNodeGpinned6kM_i_; +text: .text%__1cIimmIOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConINodeFclone6kM_pnENode__; +text: .text%__1cICodeHeapIallocate6ML_pv_; +text: .text%__1cICodeHeapPsearch_freelist6ML_pnJFreeBlock__; +text: .text%__1cbACallCompiledJavaDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cPcmpFastLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLCastP2LNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNmulL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJLoadBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cVmerge_point_too_heavy6FpnHCompile_pnENode__i_: loopopts.o; +text: .text%jni_GetByteArrayRegion: jni.o; +text: .text%__1cFParseKdo_put_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cHnmethodOis_java_method6kM_i_; +text: .text%__1cQjava_lang_StringLutf8_length6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc_ii_pc_; +text: .text%jni_GetStringUTFRegion: jni.o; +text: .text%jni_GetStringUTFLength: jni.o; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_; +text: .text%__1cRsarL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cScompU_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFciEnvZcheck_klass_accessibility6MpnHciKlass_pnMklassOopDesc__i_; +text: .text%__1cIciObjectMis_obj_array6M_i_; +text: .text%__1cOLibraryCallKitOgenerate_guard6MpnENode_pnKRegionNode_f_v_; +text: .text%__1cMoutputStream2t6Mi_v_; +text: .text%__1cMstringStreamJas_string6M_pc_; +text: .text%__1cMstringStream2t6ML_v_; +text: .text%__1cJloadINodeFreloc6kM_i_; +text: .text%__1cMstringStream2T6M_v_; +text: .text%__1cOMethodLivenessKBasicBlockJstore_two6Mi_v_; +text: .text%__1cJloadINodeIpeephole6MpnFBlock_ipnNPhaseRegAlloc_ri_pnIMachNode__; +text: .text%__1cTconvL2I_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cPClassFileParserXverify_legal_class_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cRandI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cOAbstractICacheQinvalidate_range6FpCi_v_; +text: .text%__1cOAbstractICachePcall_flush_stub6FpCi_v_; +text: .text%__1cICodeBlobMset_oop_maps6MpnJOopMapSet__v_; +text: .text%__1cRClassPathZipEntryLopen_stream6Mpkc_pnPClassFileStream__; +text: .text%__1cJCodeCacheIallocate6Fi_pnICodeBlob__; +text: .text%__1cIGraphKitOmake_slow_call6MpknITypeFunc_pCpkcpnENode_88_8_; +text: .text%__1cICodeHeapPfollowing_block6MpnJFreeBlock__2_; +text: .text%__1cOClearArrayNodeLbottom_type6kM_pknEType__; +text: .text%__1cPshrI_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cEDictIdoubhash6M_v_; +text: .text%__1cTleaPIdxScaleOffNodeLbottom_type6kM_pknEType__; +text: .text%__1cIProjNodeJideal_reg6kM_I_; +text: .text%__1cHi2sNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLimmI_16OperJnum_edges6kM_I_; +text: .text%__1cUmembar_cpu_orderNodePoper_input_base6kM_I_; +text: .text%__1cPfieldDescriptorRint_initial_value6kM_i_; +text: .text%__1cTCallInterpreterNodeGOpcode6kM_i_; +text: .text%__1cMloadConLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRaddL_rReg_immNodeErule6kM_I_; +text: .text%__1cJLoadLNodeJideal_reg6kM_I_; +text: .text%__1cTleaPIdxScaleOffNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cHCompileTset_cached_top_node6MpnENode__v_; +text: .text%__1cENodeMsetup_is_top6M_v_; +text: .text%__1cIGotoNodeGOpcode6kM_i_; +text: .text%__1cOMachPrologNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHCompilePneed_stack_bang6kMi_i_; +text: .text%__1cKBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cNFingerprinterIdo_array6Mii_v_; +text: .text%jni_GetArrayLength: jni.o; +text: .text%__1cJloadSNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKReturnNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSInterpreterRuntimeE_new6FpnKJavaThread_pnTconstantPoolOopDesc_i_v_; +text: .text%__1cMorI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cKTypeRawPtrFempty6kM_i_; +text: .text%__1cHRetNodeGpinned6kM_i_; +text: .text%__1cHRetNodeHtwo_adr6kM_I_; +text: .text%__1cPsalI_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cNinstanceKlassVadd_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cHRetNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPGlobalTLABStatsKinitialize6M_v_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJset_clock6Fx_v_; +text: .text%__1cUParallelScavengeHeapTensure_parseability6M_v_; +text: .text%__1cTDerivedPointerTableFclear6F_v_; +text: .text%__1cNMemoryServiceGgc_end6Fi_v_; +text: .text%__1cSReferenceProcessorQprocess_phaseJNI6M_v_; +text: .text%__1cRLowMemoryDetectorRdetect_low_memory6F_v_; +text: .text%__1cSReferenceProcessorbDenqueue_discovered_references6M_i_; +text: .text%__1cSReferenceProcessorbDprocess_discovered_references6M_v_; +text: .text%__1cNCollectedHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cTDerivedPointerTablePupdate_pointers6F_v_; +text: .text%__1cNCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapOfill_all_tlabs6M_v_; +text: .text%__1cKDictionaryHoops_do6MpnKOopClosure__v_; +text: .text%__1cSReferenceProcessorbBenqueue_discovered_reflists6MppnHoopDesc__v_; +text: .text%__1cWThreadLocalAllocBufferbFaccumulate_statistics_before_gc6F_v_; +text: .text%__1cSReferenceProcessorOprocess_phase16MppnHoopDesc_pnPReferencePolicy_pnRBoolObjectClosure_pnKOopClosure_pnLVoidClosure__v_; +text: .text%__1cQLRUMaxHeapPolicy2t6M_v_; +text: .text%__1cPGCMemoryManagerIgc_begin6M_v_; +text: .text%__1cPGCMemoryManagerGgc_end6M_v_; +text: .text%__1cVLoaderConstraintTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cUParallelScavengeHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cKPSYoungGenPupdate_counters6M_v_; +text: .text%__1cXjava_lang_ref_ReferenceRpending_list_addr6F_ppnHoopDesc__; +text: .text%__1cNMemoryServiceStrack_memory_usage6F_v_; +text: .text%__1cQSystemDictionaryHoops_do6FpnKOopClosure__v_; +text: .text%__1cNMemoryServiceIgc_begin6Fi_v_; +text: .text%__1cUParallelScavengeHeapOfill_all_tlabs6M_v_; +text: .text%__1cXTraceMemoryManagerStats2T6M_v_; +text: .text%__1cXTraceMemoryManagerStats2t6Mi_v_; +text: .text%__1cUParallelScavengeHeapPupdate_counters6M_v_; +text: .text%__1cQPlaceholderTableJnew_entry6MipnNsymbolOopDesc_pnHoopDesc__pnQPlaceholderEntry__; +text: .text%__1cQPlaceholderTableMremove_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cQPlaceholderTableJadd_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cNCollectedHeapQresize_all_tlabs6M_v_; +text: .text%__1cUParallelScavengeHeapQresize_all_tlabs6M_v_; +text: .text%__1cWThreadLocalAllocBufferQresize_all_tlabs6F_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersPupdate_counters6M_v_; +text: .text%__1cYGCAdaptivePolicyCountersbBupdate_counters_from_policy6M_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersbBupdate_counters_from_policy6M_v_; +text: .text%__1cNmethodOopDescbEfast_exception_handler_bci_for6MnLKlassHandle_ipnGThread__i_; +text: .text%__1cSInterpreterRuntimebFexception_handler_for_exception6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cNaddL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cNaddL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cKstoreLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cUPSAdaptiveSizePolicyZdecay_supplemental_growth6Mi_v_; +text: .text%__1cUPSAdaptiveSizePolicybPeden_increment_with_supplement_aligned_up6ML_L_; +text: .text%__1cUPSAdaptiveSizePolicyQdecaying_gc_cost6kM_d_; +text: .text%__1cUPSAdaptiveSizePolicybDcompute_generation_free_space6MLLLLLLLi_v_; +text: .text%__1cIPSOldGenMmax_gen_size6M_L_; +text: .text%__1cUPSAdaptiveSizePolicybHclear_generation_free_space_flags6M_v_; +text: .text%__1cUPSAdaptiveSizePolicyOeden_increment6MLI_L_; +text: .text%__1cUPSAdaptiveSizePolicyVadjust_for_throughput6MipL1_v_; +text: .text%__1cQSystemDictionaryTload_instance_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cUmembar_cpu_orderNodeHtwo_adr6kM_I_; +text: .text%__1cPjava_lang_ClassNcreate_mirror6FnLKlassHandle_pnGThread__pnHoopDesc__; +text: .text%__1cLklassVtableRinitialize_vtable6MpnGThread__v_; +text: .text%__1cJAssemblerDjmp6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cPshrI_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cRmulI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNandI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cOMachEpilogNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLklassVtableVinitialize_from_super6MnLKlassHandle__i_; +text: .text%__1cIVMThreadHexecute6FpnMVM_Operation__v_; +text: .text%__1cLklassVtableOcopy_vtable_to6MpnLvtableEntry__v_; +text: .text%__1cQinstanceRefKlassZacquire_pending_list_lock6FpnJBasicLock__v_; +text: .text%__1cQinstanceRefKlassbKrelease_and_notify_pending_list_lock6FipnJBasicLock__v_; +text: .text%__1cPVM_GC_OperationbKrelease_and_notify_pending_list_lock6M_v_; +text: .text%__1cPVM_GC_OperationOskip_operation6kM_i_; +text: .text%__1cPVM_GC_OperationNdoit_prologue6M_i_; +text: .text%__1cPVM_GC_OperationZacquire_pending_list_lock6M_v_; +text: .text%__1cMTypeKlassPtrFxmeet6kMpknEType__3_; +text: .text%__1cKReturnNodeEhash6kM_I_; +text: .text%__1cHnmethodVis_dependent_on_entry6MpnMklassOopDesc_2pnNmethodOopDesc__i_; +text: .text%__1cbDVM_ParallelGCFailedAllocation2t6MLiiI_v_; +text: .text%__1cLlog2_intptr6Fl_i_; +text: .text%__1cKKlass_vtbl2n6FLrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cFKlassVbase_create_klass_oop6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassRinitialize_supers6MpnMklassOopDesc_pnGThread__v_; +text: .text%__1cMloadConPNodeFclone6kM_pnENode__; +text: .text%__1cIimmPOperFclone6kM_pnIMachOper__; +text: .text%__1cFKlassRbase_create_klass6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__1_; +text: .text%__1cSCallLeafDirectNodeKmethod_set6Ml_v_; +text: .text%__1cJcmpOpOperJnot_equal6kM_i_; +text: .text%__1cJloadLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRAbstractAssemblerbDgenerate_stack_overflow_check6Mi_v_; +text: .text%__1cCosOunguard_memory6FpcL_i_; +text: .text%__1cNandL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLLShiftINodeJideal_reg6kM_I_; +text: .text%__1cRsarI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_; +text: .text%__1cJLoadSNodeJideal_reg6kM_I_; +text: .text%__1cTconvL2I_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cIPhaseIFGISquareUp6M_v_; +text: .text%__1cPciObjectFactoryMvm_symbol_at6Fi_pnIciSymbol__; +text: .text%__1cCosJyield_all6Fi_v_; +text: .text%__1cKciTypeFlowLStateVectorOmeet_exception6MpnPciInstanceKlass_pk1_i_; +text: .text%__1cCosbCmake_polling_page_unreadable6F_v_; +text: .text%__1cONMethodSweeperFsweep6F_v_; +text: .text%__1cSObjectSynchronizerVdeflate_idle_monitors6F_v_; +text: .text%__1cMCounterDecayFdecay6F_v_; +text: .text%__1cCosOprotect_memory6FpcL_i_; +text: .text%__1cORuntimeServiceWrecord_safepoint_begin6F_v_; +text: .text%__1cORuntimeServicebDrecord_safepoint_synchronized6F_v_; +text: .text%__1cCosXserialize_thread_states6F_v_; +text: .text%__1cUSafepointSynchronizeFbegin6F_v_; +text: .text%__1cUSafepointSynchronizeRis_cleanup_needed6F_i_; +text: .text%__1cUSafepointSynchronizeQdo_cleanup_tasks6F_v_; +text: .text%__1cRInlineCacheBufferIis_empty6F_i_; +text: .text%__1cRInlineCacheBufferUupdate_inline_caches6F_v_; +text: .text%__1cTAbstractInterpreterRnotice_safepoints6F_v_; +text: .text%__1cNloadConP0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosbAmake_polling_page_readable6F_v_; +text: .text%__1cORuntimeServiceUrecord_safepoint_end6F_v_; +text: .text%__1cUSafepointSynchronizeDend6F_v_; +text: .text%__1cTAbstractInterpreterRignore_safepoints6F_v_; +text: .text%__1cQVMOperationQdDueueGinsert6MpnMVM_Operation_2_v_; +text: .text%__1cQVMOperationQdDueueGunlink6MpnMVM_Operation__v_; +text: .text%__1cMVM_OperationIevaluate6M_v_; +text: .text%__1cMVM_OperationSset_calling_thread6MpnGThread_nOThreadPriority__v_; +text: .text%__1cCosMget_priority6FpknGThread_rnOThreadPriority__nIOSReturn__; +text: .text%__1cQVMOperationQdDueueOqueue_add_back6MipnMVM_Operation__v_; +text: .text%__1cGThreadMget_priority6Fpk0_nOThreadPriority__; +text: .text%__1cCosTget_native_priority6FpknGThread_pi_nIOSReturn__; +text: .text%__1cIVMThreadSevaluate_operation6MpnMVM_Operation__v_; +text: .text%__1cQVMOperationQdDueueDadd6MpnMVM_Operation__i_; +text: .text%__1cSmembar_releaseNodeLbottom_type6kM_pknEType__; +text: .text%__1cCosGrandom6F_l_; +text: .text%__1cNget_next_hash6F_l_: synchronizer.o; +text: .text%__1cNJvmtiGCMarker2t6Mi_v_; +text: .text%__1cPVM_GC_OperationNdoit_epilogue6M_v_; +text: .text%__1cKPSScavengeXshould_attempt_scavenge6F_i_; +text: .text%__1cKPSScavengeQinvoke_no_policy6Fpi_i_; +text: .text%__1cPGlobalTLABStatsHpublish6M_v_; +text: .text%__1cUinitialize_hashtable6FppnLNameSigHash__v_; +text: .text%__1cPclear_hashtable6FppnLNameSigHash__v_; +text: .text%__1cQciBytecodeStreamUis_unresolved_string6kM_i_; +text: .text%__1cFciEnvUis_unresolved_string6kMpnPciInstanceKlass_i_i_; +text: .text%__1cFciEnvZis_unresolved_string_impl6kMpnNinstanceKlass_i_i_; +text: .text%__1cNtestP_regNodeFreloc6kM_i_; +text: .text%__1cNSCMemProjNodeGis_CFG6kM_i_; +text: .text%__1cKPSScavengeGinvoke6Fpi_v_; +text: .text%__1cUParallelScavengeHeapTfailed_mem_allocate6MpiLii_pnIHeapWord__; +text: .text%__1cbDVM_ParallelGCFailedAllocationEname6kM_pkc_; +text: .text%__1cbDVM_ParallelGCFailedAllocationEdoit6M_v_; +text: .text%__1cKDictionaryJnew_entry6MIpnMklassOopDesc_pnHoopDesc__pnPDictionaryEntry__; +text: .text%__1cKDictionaryJadd_klass6MnMsymbolHandle_nGHandle_nLKlassHandle__v_; +text: .text%__1cQSystemDictionaryRupdate_dictionary6FiIiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cQSystemDictionaryRcheck_constraints6FiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cQSystemDictionaryQfind_placeholder6FiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cVLoaderConstraintTablePcheck_or_update6MnTinstanceKlassHandle_nGHandle_nMsymbolHandle__pkc_; +text: .text%__1cKoopFactoryXnew_permanent_byteArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNIdealLoopTreeTcheck_inner_safepts6MpnOPhaseIdealLoop__v_; +text: .text%__1cPsarI_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKciTypeFlowPflow_exceptions6MpnNGrowableArray4Cpn0AFBlock___pnNGrowableArray4CpnPciInstanceKlass___pn0ALStateVector__v_; +text: .text%__1cIAndINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciObjectOis_null_object6kM_i_; +text: .text%__1cNIdealLoopTreeNDCE_loop_body6M_v_; +text: .text%__1cNprefetchwNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXmembar_release_lockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFStateP_sub_Op_LShiftI6MpknENode__v_; +text: .text%__1cNIdealLoopTreeVadjust_loop_exit_prob6MpnOPhaseIdealLoop__v_; +text: .text%__1cKstoreCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMPhaseChaitinMreset_uf_map6MI_v_; +text: .text%__1cMPhaseChaitinSbuild_ifg_physical6MpnMResourceArea__I_; +text: .text%__1cNPhaseCoalescePcoalesce_driver6M_v_; +text: .text%__1cNdecI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSComputeAdapterInfoHdo_long6M_v_; +text: .text%__1cOGenerateOopMapJdo_astore6Mi_v_; +text: .text%__1cSTailCalljmpIndNodeNis_block_proj6kM_pknENode__; +text: .text%__1cIciObjectMhas_encoding6M_i_; +text: .text%__1cMrcx_RegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIMulLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cHPhiNodeKmake_blank6FpnENode_2_p0_; +text: .text%JVM_GetMethodIxSignatureUTF; +text: .text%JVM_GetMethodIxMaxStack; +text: .text%JVM_GetMethodIxArgsSize; +text: .text%JVM_GetMethodIxByteCodeLength; +text: .text%JVM_GetMethodIxByteCode; +text: .text%JVM_GetMethodIxExceptionIndexes; +text: .text%JVM_GetMethodIxExceptionsCount; +text: .text%__1cENodeUdepends_only_on_test6kM_i_; +text: .text%__1cXmembar_acquire_lockNodePoper_input_base6kM_I_; +text: .text%__1cOPhaseIdealLoopMdominated_by6MpnENode_2_v_; +text: .text%__1cNGrowableArray4nLKlassHandle__Icontains6kMrkn0A__i_; +text: .text%__1cLGCTaskQdDueue2t6Mi_v_; +text: .text%__1cNaddL_rRegNodeErule6kM_I_; +text: .text%__1cGGCTask2t6Mn0AEKindEkind__v_; +text: .text%__1cZSerialOldToYoungRootsTaskEname6M_pc_; +text: .text%__1cZSerialOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cSPSPromotionManagerNpost_scavenge6F_v_; +text: .text%__1cSPSPromotionManagerMpre_scavenge6F_v_; +text: .text%__1cUWaitForBarrierGCTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cSPSPromotionManagerbBvm_thread_promotion_manager6F_p0_; +text: .text%__1cSAdaptiveSizePolicyWminor_collection_begin6M_v_; +text: .text%__1cSAdaptiveSizePolicyUminor_collection_end6MnHGCCauseFCause__v_; +text: .text%__1cUWaitForBarrierGCTaskEname6M_pc_; +text: .text%__1cNMonitorSupplyHrelease6FpnHMonitor__v_; +text: .text%__1cNMonitorSupplyHreserve6F_pnHMonitor__; +text: .text%__1cUWaitForBarrierGCTaskIwait_for6M_v_; +text: .text%__1cUWaitForBarrierGCTaskIdestruct6M_v_; +text: .text%__1cUWaitForBarrierGCTaskHdestroy6Fp0_v_; +text: .text%__1cUWaitForBarrierGCTask2t6Mi_v_; +text: .text%__1cUWaitForBarrierGCTaskGcreate6F_p0_; +text: .text%__1cNBarrierGCTaskIdestruct6M_v_; +text: .text%__1cNBarrierGCTaskOdo_it_internal6MpnNGCTaskManager_I_v_; +text: .text%__1cNGCTaskManagerVrelease_all_resources6M_v_; +text: .text%__1cNGCTaskManagerIadd_list6MpnLGCTaskQdDueue__v_; +text: .text%__1cLGCTaskQdDueueHenqueue6Mp0_v_; +text: .text%__1cLGCTaskQdDueueGcreate6F_p0_; +text: .text%__1cGGCTaskIdestruct6M_v_; +text: .text%__1cSCardTableExtensionRscavenge_contents6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager__v_; +text: .text%__1cHThreadsZcreate_thread_roots_tasks6FpnLGCTaskQdDueue__v_; +text: .text%__1cKPSYoungGenLswap_spaces6M_v_; +text: .text%__1cUPSAdaptiveSizePolicybPcompute_survivor_space_size_and_threshold6MiiL_i_; +text: .text%__1cUParallelScavengeHeapQresize_young_gen6MLL_v_; +text: .text%__1cUPSAdaptiveSizePolicyPupdate_averages6MiLL_v_; +text: .text%__1cKPSYoungGenRresize_generation6MLL_i_; +text: .text%__1cKPSYoungGenGresize6MLL_v_; +text: .text%__1cKPSYoungGenNresize_spaces6MLL_v_; +text: .text%__1cHMatcherKcan_be_arg6Fi_i_; +text: .text%__1cHMatcherQis_spillable_arg6Fi_i_; +text: .text%__1cUPSAdaptiveSizePolicyOshould_full_GC6ML_i_; +text: .text%__1cSAdaptiveSizePolicybIupdate_minor_pause_young_estimator6Md_v_; +text: .text%__1cUPSAdaptiveSizePolicybGupdate_minor_pause_old_estimator6Md_v_; +text: .text%__1cNsubL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cMStartOSRNodeGOpcode6kM_i_; +text: .text%__1cRsubI_rReg_memNodeErule6kM_I_; +text: .text%__1cQinstanceRefKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cXmembar_acquire_lockNodeHtwo_adr6kM_I_; +text: .text%__1cNandI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cNcmovI_regNodePoper_input_base6kM_I_; +text: .text%__1cMURShiftINodeJideal_reg6kM_I_; +text: .text%__1cMorI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cLRShiftINodeJideal_reg6kM_I_; +text: .text%__1cLklassItableRinitialize_itable6M_v_; +text: .text%__1cLklassVtableQfill_in_mirandas6Mri_v_; +text: .text%__1cRandI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cSmembar_releaseNodePoper_input_base6kM_I_; +text: .text%__1cFKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cJrRegLOperFclone6kM_pnIMachOper__; +text: .text%__1cFStateR_sub_Op_LoadKlass6MpknENode__v_; +text: .text%__1cRmethodDataOopDescJis_mature6kM_i_; +text: .text%__1cJcmpOpOperEless6kM_i_; +text: .text%__1cFKlassWappend_to_sibling_list6M_v_; +text: .text%__1cOcompL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQSystemDictionarySjava_system_loader6F_pnHoopDesc__; +text: .text%__1cNloadKlassNodeFreloc6kM_i_; +text: .text%__1cRshrI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAndINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cTjava_lang_ThrowableNset_backtrace6FpnHoopDesc_2_v_; +text: .text%__1cPcmovI_reg_gNodeMideal_Opcode6kM_i_; +text: .text%__1cIAndINodeGmul_id6kM_pknEType__; +text: .text%__1cTClassLoadingServiceScompute_class_size6FpnNinstanceKlass__L_; +text: .text%__1cLklassVtableQget_num_mirandas6FpnMklassOopDesc_pnPobjArrayOopDesc_4_i_; +text: .text%__1cIVerifierQrelax_verify_for6FpnHoopDesc__i_; +text: .text%__1cLklassVtablebKcompute_vtable_size_and_num_mirandas6Fri1pnMklassOopDesc_pnPobjArrayOopDesc_nLAccessFlags_pnHoopDesc_pnNsymbolOopDesc_5_v_; +text: .text%__1cPClassFileParserbAparse_classfile_attributes6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cRaddI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cVjava_lang_ClassLoaderRis_trusted_loader6FpnHoopDesc__i_; +text: .text%__1cNmethodOopDescMsort_methods6FpnPobjArrayOopDesc_222_v_; +text: .text%__1cQSystemDictionaryQadd_to_hierarchy6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserUcompute_oop_map_size6MnTinstanceKlassHandle_ii_i_; +text: .text%__1cPClassFileParserOparseClassFile6MnMsymbolHandle_nGHandle_2r1pnGThread__nTinstanceKlassHandle__; +text: .text%__1cPClassFileParserVset_precomputed_flags6MnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserNfill_oop_maps6MnTinstanceKlassHandle_ii_v_; +text: .text%__1cPClassFileParserbDcompute_transitive_interfaces6MnTinstanceKlassHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cQSystemDictionaryVdefine_instance_class6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCcheck_super_interface_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserYcheck_super_class_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbBcheck_final_method_override6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cSinstanceKlassKlassXallocate_instance_klass6MiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cPClassFileParserQparse_interfaces6MnSconstantPoolHandle_nGHandle_2pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserTparse_constant_pool6MpnGThread__nSconstantPoolHandle__; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_; +text: .text%__1cTClassLoadingServiceTnotify_class_loaded6FpnNinstanceKlass_i_v_; +text: .text%__1cLklassItableZsetup_itable_offset_table6FnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserMparse_fields6MnSconstantPoolHandle_ipnUFieldAllocationCount_pnOobjArrayHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserNparse_methods6MnSconstantPoolHandle_ipnLAccessFlags_ppnPobjArrayOopDesc_66pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserMsort_methods6MnOobjArrayHandle_111pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserbBparse_constant_pool_entries6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cIUniverseTflush_dependents_on6FnTinstanceKlassHandle__v_; +text: .text%__1cKoopFactoryQnew_constantPool6FipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cRconstantPoolKlassIallocate6MipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cPClassFileStream2t6MpCipc_v_; +text: .text%__1cNinstanceKlassbBdo_local_static_fields_impl6FnTinstanceKlassHandle_pFpnPfieldDescriptor_pnGThread__v5_v_; +text: .text%__1cJCodeCachebKnumber_of_nmethods_with_dependencies6F_i_; +text: .text%__1cNinstanceKlassWdo_local_static_fields6MpFpnPfieldDescriptor_pnGThread__v4_v_; +text: .text%__1cNinstanceKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassSprocess_interfaces6MpnGThread__v_; +text: .text%__1cNinstanceKlassQinit_implementor6M_v_; +text: .text%__1cNinstanceKlassQeager_initialize6MpnGThread__v_; +text: .text%__1cKoopFactoryRnew_instanceKlass6FiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cQSystemDictionaryVresolve_super_or_fail6FnMsymbolHandle_1nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cKTypeRawPtrEmake6FpC_pk0_; +text: .text%__1cScompI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cScompI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cOMethodLiveness2t6MpnFArena_pnIciMethod__v_; +text: .text%__1cOMethodLivenessSpropagate_liveness6M_v_; +text: .text%__1cOMethodLivenessRinit_basic_blocks6M_v_; +text: .text%__1cOMethodLivenessNinit_gen_kill6M_v_; +text: .text%__1cOMethodLivenessQcompute_liveness6M_v_; +text: .text%__1cFKlassRoop_is_methodData6kM_i_; +text: .text%__1cFVTuneQstart_class_load6F_v_; +text: .text%__1cSThreadProfilerMark2t6Mn0AGRegion__v_; +text: .text%__1cLClassLoaderOload_classfile6FnMsymbolHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cFVTuneOend_class_load6F_v_; +text: .text%__1cQSystemDictionaryRload_shared_class6FnTinstanceKlassHandle_nGHandle_pnGThread__1_; +text: .text%__1cQSystemDictionaryRload_shared_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cQSystemDictionaryRfind_shared_class6FnMsymbolHandle__pnMklassOopDesc__; +text: .text%__1cSThreadProfilerMark2T6M_v_; +text: .text%__1cNinstanceKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cSMemBarVolatileNodeGOpcode6kM_i_; +text: .text%__1cRaddL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPClassFileParserbKparse_classfile_sourcefile_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cKciTypeFlowLStateVectorGdo_ldc6MpnQciBytecodeStream__v_; +text: .text%__1cMPhaseIterGVNIoptimize6M_v_; +text: .text%__1cOrFlagsRegUOperFclone6kM_pnIMachOper__; +text: .text%__1cNmulL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cMrdi_RegPOperJnum_edges6kM_I_; +text: .text%__1cRsalI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLklassVtableYadd_new_mirandas_to_list6FpnNGrowableArray4CpnNmethodOopDesc___pnPobjArrayOopDesc_6pnMklassOopDesc__v_; +text: .text%__1cQPackageHashtableMcompute_hash6Mpkci_I_; +text: .text%__1cWconstantPoolCacheKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cRsalL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cIConINodeHget_int6kMpi_i_; +text: .text%__1cJMarkSweepSFollowStackClosureHdo_void6M_v_; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__; +text: .text%__1cLOpaque2NodeGOpcode6kM_i_; +text: .text%__1cOGenerateOopMapJppdupswap6Mipkc_v_; +text: .text%__1cILoopNode2t6MpnENode_2_v_; +text: .text%__1cJloadBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPClassFileStreamGget_u86MpnGThread__X_; +text: .text%__1cKstoreINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTconvI2L_reg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cScompP_mem_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIVerifierRverify_byte_codes6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIRewriterHrewrite6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIRewriterScompute_index_maps6FnSconstantPoolHandle_rpnIintArray_rpnIintStack__v_; +text: .text%__1cIRewriterXnew_constant_pool_cache6FrnIintArray_pnGThread__nXconstantPoolCacheHandle__; +text: .text%__1cIintArray2t6Mii_v_; +text: .text%__1cNinstanceKlassNrewrite_class6MpnGThread__v_; +text: .text%__1cKoopFactoryVnew_constantPoolCache6FipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cNinstanceKlassWadd_loader_constraints6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassLverify_code6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cWconstantPoolCacheKlassIallocate6MipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cYconstantPoolCacheOopDescKinitialize6MrnIintArray__v_; +text: .text%__1cFframeWsender_for_entry_frame6kMpnLRegisterMap__0_; +text: .text%__1cHPhiNodeDcmp6kMrknENode__I_; +text: .text%__1cSmembar_releaseNodeHtwo_adr6kM_I_; +text: .text%__1cSObjectSynchronizerJnotifyall6FnGHandle_pnGThread__v_; +text: .text%__1cKoopFactoryWnew_permanent_intArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cPClassFileParserVparse_exception_table6MIInSconstantPoolHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserbSparse_constant_pool_interfacemethodref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cWCountInterfacesClosureEdoit6MpnMklassOopDesc_i_v_; +text: .text%__1cMtlsLoadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmodI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cNtestL_regNodeMideal_Opcode6kM_i_; +text: .text%__1cRaddI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIConFNodeGOpcode6kM_i_; +text: .text%__1cLOpaque1NodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cTconvI2L_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNSharedRuntimebOraw_exception_handler_for_return_address6FpC_1_; +text: .text%__1cNSharedRuntimebKexception_handler_for_return_address6FpC_1_; +text: .text%__1cOMethodLivenessKBasicBlockPmerge_exception6MnGBitMap__i_; +text: .text%__1cTconvI2L_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAndINodeKmul_opcode6kM_i_; +text: .text%__1cIAndINodeKadd_opcode6kM_i_; +text: .text%__1cPcmovI_reg_gNodeJnum_opnds6kM_I_; +text: .text%__1cKCMoveINodeGOpcode6kM_i_; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_; +text: .text%__1cIRootNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPloadConUL32NodeHsize_of6kM_I_; +text: .text%__1cJAssemblerEandq6MpnMRegisterImpl_i_v_; +text: .text%__1cLClassLoaderOlookup_package6Fpkc_pnLPackageInfo__; +text: .text%__1cQPackageHashtableJget_entry6MiIpkcL_pnLPackageInfo__; +text: .text%__1cIGraphKitRmerge_fast_memory6MpnENode_2i_v_; +text: .text%JVM_Clone; +text: .text%__1cLklassItableTcompute_itable_size6FnOobjArrayHandle__i_; +text: .text%__1cUCallCompiledJavaNodeGOpcode6kM_i_; +text: .text%__1cPsalI_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKadd_n_reqs6FpnENode_1_v_: graphKit.o; +text: .text%__1cSTailCalljmpIndNodeMideal_Opcode6kM_i_; +text: .text%__1cQComputeCallStackJdo_double6M_v_; +text: .text%__1cKciTypeFlowLStateVectorMdo_putstatic6MpnQciBytecodeStream__v_; +text: .text%__1cLClassLoaderLadd_package6Fpkci_i_; +text: .text%__1cIGraphKitHopt_iff6MpnENode_2_2_; +text: .text%__1cIGraphKitOmake_merge_mem6MpnENode_22_v_; +text: .text%__1cGEventsDlog6FpkcE_v_; +text: .text%__1cMLinkResolverbHlookup_instance_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cSsafePoint_pollNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNSharedRuntimebWnative_method_throw_unsatisfied_link_error_entry6F_pC_; +text: .text%__1cPshrI_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFParseWensure_phis_everywhere6M_v_; +text: .text%__1cNsubL_rRegNodeErule6kM_I_; +text: .text%__1cNIdealLoopTreeUiteration_split_impl6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cNIdealLoopTreebBpolicy_do_remove_empty_loop6MpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeOpolicy_peeling6kMpnOPhaseIdealLoop__i_; +text: .text%__1cIBoolNodeZis_counted_loop_exit_test6M_i_; +text: .text%__1cJloadCNodeHtwo_adr6kM_I_; +text: .text%__1cUPSMarkSweepDecoratorVdestination_decorator6F_p0_; +text: .text%__1cTGeneratePairingInfoRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cENode2n6FL_pv_; +text: .text%__1cSvframeStreamCommonZsecurity_get_caller_frame6Mi_v_; +text: .text%__1cKBufferBlob2n6FLI_pv_; +text: .text%__1cFParseKarray_load6MnJBasicType__v_; +text: .text%__1cICodeBlob2t6Mpkcii_v_; +text: .text%__1cKBufferBlob2t6Mpkci_v_; +text: .text%__1cKBufferBlobGcreate6Fpkci_p0_; +text: .text%__1cKciTypeFlowLStateVectorLdo_putfield6MpnQciBytecodeStream__v_; +text: .text%__1cHnmethodNscope_desc_at6MpC_pnJScopeDesc__; +text: .text%__1cHnmethodJcode_size6kM_i_; +text: .text%__1cRtestP_reg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cRtestP_reg_memNodePoper_input_base6kM_I_; +text: .text%__1cOPhaseIdealLoopQset_subtree_ctrl6MpnENode__v_; +text: .text%__1cOjmpLoopEndNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_i_v_; +text: .text%__1cRconstantPoolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNinstanceKlassbBcall_class_initializer_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassRclass_initializer6M_pnNmethodOopDesc__; +text: .text%__1cNinstanceKlassWcall_class_initializer6MpnGThread__v_; +text: .text%__1cNinstanceKlassbOset_initialization_state_and_notify_impl6FnTinstanceKlassHandle_n0AKClassState_pnGThread__v_; +text: .text%__1cNinstanceKlassbJset_initialization_state_and_notify6Mn0AKClassState_pnGThread__v_; +text: .text%__1cNRelocIteratorTlocs_and_index_size6Fii_i_; +text: .text%__1cMrdi_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cTStackWalkCompPolicyYmethod_back_branch_event6MnMmethodHandle_iipnGThread__v_; +text: .text%__1cFTypeDCeq6kMpknEType__i_; +text: .text%__1cJLoadCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNtestL_regNodeHtwo_adr6kM_I_; +text: .text%__1cTconvL2I_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRCompilationPolicybJreset_counter_for_back_branch_event6MnMmethodHandle__v_; +text: .text%__1cMrax_RegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNmodI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cNSignatureInfoIdo_short6M_v_; +text: .text%JVM_GetFieldIxModifiers; +text: .text%__1cNsubL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNandL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cNsubL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cTMachCallRuntimeNodePret_addr_offset6M_i_; +text: .text%__1cOcompiledVFrameEcode6kM_pnHnmethod__; +text: .text%__1cICodeHeapTmark_segmap_as_used6MLL_v_; +text: .text%__1cMorI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAddLNodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cHOrINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%JVM_IsConstructorIx; +text: .text%__1cEDict2t6MpFpkv2_ipF2_ipnFArena_i_v_; +text: .text%__1cHMatcherLfind_shared6MpnENode__v_; +text: .text%__1cHMatcherFxform6MpnENode_i_2_; +text: .text%__1cJStartNodeHsize_of6kM_I_; +text: .text%__1cILRG_List2t6MI_v_; +text: .text%__1cHMatcherLreturn_addr6kM_i_; +text: .text%__1cSindIndexOffsetOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cGBundlePinitialize_nops6FppnIMachNode__v_; +text: .text%__1cOMachPrologNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cHMemNodeHsize_of6kM_I_; +text: .text%__1cNSignatureInfoIdo_float6M_v_; +text: .text%__1cRaddI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRmulI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cFParseNadd_safepoint6M_v_; +text: .text%__1cFStateT_sub_Op_CheckCastPP6MpknENode__v_; +text: .text%__1cRaddI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOCompiledRFrameEinit6M_v_; +text: .text%__1cGvframeDtop6kM_p0_; +text: .text%__1cPsarI_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cRmethodDataOopDescYcompute_extra_data_count6Fii_i_; +text: .text%__1cPcheckCastPPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObjectIis_klass6M_i_; +text: .text%__1cFStateM_sub_Op_SubI6MpknENode__v_; +text: .text%__1cRxorI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNloadConP0NodeHsize_of6kM_I_; +text: .text%__1cJAssemblerEaddq6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerEsubq6MpnMRegisterImpl_2_v_; +text: .text%__1cXJNI_ArgumentPusherVaArgHiterate6ML_v_; +text: .text%__1cTresource_free_bytes6FpcL_v_; +text: .text%__1cNSingletonBlobMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cUPSMarkSweepDecoratorPadjust_pointers6M_v_; +text: .text%__1cUPSMarkSweepDecoratorHcompact6Mi_v_; +text: .text%__1cUPSMarkSweepDecoratorKprecompact6M_v_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeMideal_Opcode6kM_i_; +text: .text%__1cRindIndexScaleOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cRindIndexScaleOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cPCountedLoopNodeHsize_of6kM_I_; +text: .text%__1cENodeHrm_prec6MI_v_; +text: .text%__1cHAddNodeGis_Add6kM_pk0_; +text: .text%__1cHCompilebAvarargs_C_out_slots_killed6kM_I_; +text: .text%__1cTMachCallRuntimeNodeSis_MachCallRuntime6M_p0_; +text: .text%__1cMrax_RegIOperJnum_edges6kM_I_; +text: .text%__1cICodeHeapLmerge_right6MpnJFreeBlock__v_; +text: .text%__1cNaddI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNmulL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cWandI_rReg_imm65535NodeMideal_Opcode6kM_i_; +text: .text%__1cKReturnNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRjmpConU_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLjmpConUNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cRjmpConU_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cRjmpConU_shortNodeJis_Branch6kM_I_; +text: .text%__1cKcmpOpUOperFclone6kM_pnIMachOper__; +text: .text%__1cRtestP_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFMutexbLwait_for_lock_blocking_implementation6MpnKJavaThread__v_; +text: .text%__1cIregDOperJnum_edges6kM_I_; +text: .text%__1cPciInstanceKlassTis_java_lang_Object6M_i_; +text: .text%__1cSciExceptionHandlerLcatch_klass6M_pnPciInstanceKlass__; +text: .text%__1cSindIndexOffsetOperNconstant_disp6kM_i_; +text: .text%__1cIAndLNodeGadd_id6kM_pknEType__; +text: .text%__1cLConvL2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQleaPIdxScaleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIAndLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cRaddP_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMloadConLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHMatcherQpost_fast_unlock6FpknENode__i_; +text: .text%__1cFStateV_sub_Op_MemBarRelease6MpknENode__v_; +text: .text%__1cOleaPIdxOffNodeMideal_Opcode6kM_i_; +text: .text%__1cILoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cScompI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cScompI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerDorq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cScompI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKJNIHandlesOdestroy_global6FpnI_jobject_i_v_; +text: .text%__1cPcmpFastLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXmembar_release_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTcompareAndSwapLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescTset_native_function6MpC_v_; +text: .text%__1cKciTypeFlowLStateVectorJhalf_type6FpnGciType__3_; +text: .text%__1cQmerge_point_safe6FpnENode__i_: loopopts.o; +text: .text%__1cRaddL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cHMatcherUc_calling_convention6FpnLOptoRegPair_I_v_; +text: .text%__1cPCallRuntimeNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cUjni_invoke_nonstatic6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cQSystemDictionaryRnumber_of_classes6F_i_; +text: .text%__1cNaddL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNxorI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cWCallLeafNoFPDirectNodePoper_input_base6kM_I_; +text: .text%__1cENodeHget_int6kMpi_i_; +text: .text%__1cPCountedLoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJLoadFNodeGOpcode6kM_i_; +text: .text%__1cQSystemDictionarybBresolve_array_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNincI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPClassFileParserbEparse_constant_pool_long_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPcmovI_reg_lNodePoper_input_base6kM_I_; +text: .text%__1cJAssemblerEleaq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cNinstanceKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cJloadINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHTypePtrFxmeet6kMpknEType__3_; +text: .text%__1cNprefetchwNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cFKlassTarray_klass_or_null6M_pnMklassOopDesc__; +text: .text%__1cIPhaseIFGYCompute_Effective_Degree6M_v_; +text: .text%__1cbCfind_class_from_class_loader6FpnHJNIEnv__nMsymbolHandle_CnGHandle_3CpnGThread__pnH_jclass__; +text: .text%__1cMPhaseChaitinOcache_lrg_info6M_v_; +text: .text%__1cMPhaseChaitinISimplify6M_v_; +text: .text%__1cMPhaseChaitinGSelect6M_I_; +text: .text%__1cRsarL_rReg_immNodeErule6kM_I_; +text: .text%__1cKcmpOpUOperJnot_equal6kM_i_; +text: .text%__1cScompU_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cScompU_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cRNativeGeneralJumpQjump_destination6kM_pC_; +text: .text%__1cLRethrowNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cWCallLeafNoFPDirectNodeHtwo_adr6kM_I_; +text: .text%__1cNSafepointBlobHoops_do6MpnKOopClosure__v_; +text: .text%__1cSvframeStreamCommonYfill_from_compiled_frame6MpnHnmethod_i_v_; +text: .text%__1cNandL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cHnmethodQis_native_method6kM_i_; +text: .text%__1cTleaPIdxScaleOffNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNmulL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cODataRelocationGoffset6M_i_; +text: .text%__1cODataRelocationJset_value6MpC_v_; +text: .text%__1cKRelocationRpd_set_data_value6MpCl_v_; +text: .text%__1cLOptoRuntimeJstub_name6FpC_pkc_; +text: .text%__1cIMulINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFStateO_sub_Op_StoreB6MpknENode__v_; +text: .text%__1cRaddL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cIregFOperJnum_edges6kM_I_; +text: .text%__1cRandI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIRootNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIRootNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOleaPIdxOffNodePoper_input_base6kM_I_; +text: .text%__1cJcmpOpOperKless_equal6kM_i_; +text: .text%__1cNmethodOopDescVset_signature_handler6MpC_v_; +text: .text%__1cIMulLNodeGmul_id6kM_pknEType__; +text: .text%__1cMrep_stosNodeHtwo_adr6kM_I_; +text: .text%__1cHMemNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cNsubI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cHMemNodeScalculate_adr_type6FpknEType_pknHTypePtr__6_; +text: .text%__1cRmulI_rReg_immNodeErule6kM_I_; +text: .text%__1cURethrowExceptionNodePoper_input_base6kM_I_; +text: .text%__1cNaddP_rRegNodePoper_input_base6kM_I_; +text: .text%__1cFStateP_sub_Op_RShiftI6MpknENode__v_; +text: .text%__1cKstoreLNodeHtwo_adr6kM_I_; +text: .text%__1cNnegI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodePoper_input_base6kM_I_; +text: .text%__1cbFloadConL_0x6666666666666667NodeErule6kM_I_; +text: .text%__1cNSharedRuntimeXfind_callee_info_helper6FpnKJavaThread_rnMvframeStream_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cIGraphKitNstore_barrier6MpnENode_22_v_; +text: .text%__1cNmethodOopDescTverified_code_entry6M_pC_; +text: .text%__1cNloadKlassNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmembar_acquireNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMoutputStreamMdo_vsnprintf6FpcLpkcpnR__va_list_element_irL_3_; +text: .text%__1cMoutputStreamFprint6MpkcE_v_; +text: .text%__1cMURShiftLNodeJideal_reg6kM_I_; +text: .text%__1cZPhaseConservativeCoalesce2t6MrnMPhaseChaitin__v_; +text: .text%__1cMPhaseChaitinFSplit6MI_I_; +text: .text%__1cMPhaseChaitinHcompact6M_v_; +text: .text%__1cMPhaseChaitinZcompress_uf_map_for_nodes6M_v_; +text: .text%__1cZPhaseConservativeCoalesceGverify6M_v_; +text: .text%__1cQComputeCallStackIdo_short6M_v_; +text: .text%__1cNFingerprinterHdo_long6M_v_; +text: .text%__1cIciMethodRinstructions_size6M_i_; +text: .text%__1cSsafePoint_pollNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNloadConL0NodeLbottom_type6kM_pknEType__; +text: .text%__1cJimmL0OperJconstantL6kM_x_; +text: .text%__1cWandI_rReg_imm65535NodePoper_input_base6kM_I_; +text: .text%__1cIAndINodeJideal_reg6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerKverify_oop6MpnMRegisterImpl_nITosState__v_; +text: .text%__1cYexternal_word_RelocationJpack_data6M_i_; +text: .text%__1cJimmP0OperFclone6kM_pnIMachOper__; +text: .text%__1cKRelocationYruntime_address_to_index6FpC_l_; +text: .text%__1cOemit_d32_reloc6FrnKCodeBuffer_inJrelocInfoJrelocType_i_v_; +text: .text%__1cYexternal_word_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cRsalL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cLPhaseValues2T5B6M_v_; +text: .text%__1cNstoreImmBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateQ_sub_Op_URShiftL6MpknENode__v_; +text: .text%__1cJNode_ListEyank6MpnENode__v_; +text: .text%__1cNxorI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNxorI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerEmovq6MpnMRegisterImpl_l_v_; +text: .text%jni_ExceptionCheck: jni.o; +text: .text%__1cMFastLockNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTCallDynamicJavaNodeEhash6kM_I_; +text: .text%__1cMalloc_object6FpnH_jclass_pnGThread__pnPinstanceOopDesc__: jni.o; +text: .text%__1cRshrL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIXorINodeLbottom_type6kM_pknEType__; +text: .text%__1cJAssemblerEsubq6MpnMRegisterImpl_i_v_; +text: .text%__1cNloadConL0NodeMideal_Opcode6kM_i_; +text: .text%__1cLPcDescCacheKpc_desc_at6kMpnHnmethod_pC_pnGPcDesc__; +text: .text%__1cKBlock_ListGinsert6MIpnFBlock__v_; +text: .text%__1cKtype2basic6FpknEType__nJBasicType__; +text: .text%__1cQleaPIdxScaleNodeLbottom_type6kM_pknEType__; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_; +text: .text%__1cIGraphKitOnull_check_oop6MpnKRegionNode_pnENode_i_4_; +text: .text%__1cJloadCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRxorI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cKTypeAryPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cKTypeRawPtrEmake6FnHTypePtrDPTR__pk0_; +text: .text%__1cJCodeCacheEfree6FpnICodeBlob__v_; +text: .text%__1cICodeHeapPadd_to_freelist6MpnJHeapBlock__v_; +text: .text%__1cICodeHeapKdeallocate6Mpv_v_; +text: .text%__1cFframeLnmethods_do6M_v_; +text: .text%__1cJVectorSetGslamin6Mrk0_v_; +text: .text%__1cFStateQ_sub_Op_URShiftI6MpknENode__v_; +text: .text%__1cScompI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRaddI_rReg_memNodeErule6kM_I_; +text: .text%__1cYexternal_word_RelocationWfix_relocation_at_move6Ml_v_; +text: .text%__1cKRelocationYpd_get_address_from_code6M_pC_; +text: .text%__1cRxorI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cXJNI_ArgumentPusherVaArgIget_long6M_v_; +text: .text%__1cNandL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOCompilerOracleMshould_print6FnMmethodHandle__i_; +text: .text%__1cNstoreImmBNodeFreloc6kM_i_; +text: .text%__1cJcmpOpOperNgreater_equal6kM_i_; +text: .text%__1cKBufferBlobEfree6Fp0_v_; +text: .text%__1cZInterpreterMacroAssemblerKverify_FPU6MinITosState__v_; +text: .text%__1cKNativeCallXset_destination_mt_safe6MpC_v_; +text: .text%__1cOGenerateOopMapIppop_any6Mi_v_; +text: .text%__1cKNode_ArrayFclear6M_v_; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc__pc_; +text: .text%__1cJAssemblerFpushq6MpnMRegisterImpl__v_; +text: .text%__1cIRootNodeHis_Root6M_p0_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cIJumpDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cRsalL_rReg_immNodeErule6kM_I_; +text: .text%__1cPstoreImmI16NodeHtwo_adr6kM_I_; +text: .text%__1cQjava_lang_StringOchar_converter6FnGHandle_HHpnGThread__1_; +text: .text%jni_NewObject: jni.o; +text: .text%__1cNaddP_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cPsarI_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cFciEnvKcompile_id6M_I_; +text: .text%__1cOPhaseIdealLoopNreorg_offsets6MpnNIdealLoopTree__v_; +text: .text%__1cNtestL_regNodeErule6kM_I_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_exit_Type6F_pknITypeFunc__; +text: .text%__1cNstoreImmINodeHtwo_adr6kM_I_; +text: .text%__1cIGraphKitNshared_unlock6MpnENode_2_v_; +text: .text%__1cNSafePointNodeLpop_monitor6M_v_; +text: .text%__1cRsarI_rReg_immNodeErule6kM_I_; +text: .text%__1cNtestL_regNodePoper_input_base6kM_I_; +text: .text%__1cRsarL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMindirectOperNbase_position6kM_i_; +text: .text%__1cMindirectOperNconstant_disp6kM_i_; +text: .text%__1cMTailCallNodeKmatch_edge6kMI_I_; +text: .text%__1cKciTypeFlowLStateVectorGdo_new6MpnQciBytecodeStream__v_; +text: .text%__1cHMatcherPprior_fast_lock6FpknENode__i_; +text: .text%__1cGIfNodeMdominated_by6MpnENode_pnMPhaseIterGVN__v_; +text: .text%__1cFStateV_sub_Op_MemBarAcquire6MpknENode__v_; +text: .text%__1cNSharedRuntimeQfind_callee_info6FpnKJavaThread_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cFKlassDLCA6Mp0_1_; +text: .text%__1cRtestP_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRtestP_reg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cRtestP_reg_memNodeJnum_opnds6kM_I_; +text: .text%__1cHciKlassVleast_common_ancestor6Mp0_1_; +text: .text%__1cUmembar_cpu_orderNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUmembar_cpu_orderNodeLbottom_type6kM_pknEType__; +text: .text%__1cTcompareAndSwapLNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cNSCMemProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cTcompareAndSwapLNodeHtwo_adr6kM_I_; +text: .text%__1cJScopeDescGsender6kM_p0_; +text: .text%__1cSindIndexOffsetOperOindex_position6kM_i_; +text: .text%__1cSindIndexOffsetOperNbase_position6kM_i_; +text: .text%__1cNSharedRuntimeOresolve_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cNSafePointNodeMpush_monitor6MpknMFastLockNode__v_; +text: .text%__1cNSharedRuntimeSresolve_sub_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cOcompiledVFrameGsender6kM_pnGvframe__; +text: .text%__1cNtestU_regNodeHtwo_adr6kM_I_; +text: .text%__1cTciConstantPoolCache2t6MpnFArena_i_v_; +text: .text%__1cNGrowableArray4Cpv_2t6MpnFArena_iirk0_v_; +text: .text%__1cKstoreFNodePoper_input_base6kM_I_; +text: .text%__1cNGrowableArray4Cl_2t6MpnFArena_iirkl_v_; +text: .text%__1cNstoreImmINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOMachEpilogNodeFreloc6kM_i_; +text: .text%__1cOMachEpilogNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovI_regNodeMideal_Opcode6kM_i_; +text: .text%__1cKCompiledIC2t6MpnKNativeCall__v_; +text: .text%__1cFStateN_sub_Op_LoadL6MpknENode__v_; +text: .text%__1cNmodI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cSCallLeafDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSignatureInfoHdo_char6M_v_; +text: .text%__1cNtestU_regNodeMideal_Opcode6kM_i_; +text: .text%__1cFStateQ_sub_Op_CallLeaf6MpknENode__v_; +text: .text%__1cRAbstractAssemblerFflush6M_v_; +text: .text%__1cJloadLNodeIpeephole6MpnFBlock_ipnNPhaseRegAlloc_ri_pnIMachNode__; +text: .text%__1cJloadLNodeFreloc6kM_i_; +text: .text%__1cSCallLeafDirectNodeFreloc6kM_i_; +text: .text%__1cIGraphKitNgen_checkcast6MpnENode_2p2_2_; +text: .text%__1cIGraphKitRgen_subtype_check6MpnENode_2_2_; +text: .text%__1cNsubL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXmembar_release_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cJAssemblerEmovq6MpnMRegisterImpl_2_v_; +text: .text%__1cRmulL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cRsubI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cFframeRretrieve_receiver6MpnLRegisterMap__pnHoopDesc__; +text: .text%__1cPBytecode_invokeNstatic_target6MpnGThread__nMmethodHandle__; +text: .text%jni_NewGlobalRef: jni.o; +text: .text%__1cKciTypeFlowFRangeSprivate_copy_count6kMpn0AGJsrSet__i_; +text: .text%__1cOleaPIdxOffNodeJnum_opnds6kM_I_; +text: .text%__1cOPhaseIdealLoopLdo_split_if6MpnENode__v_; +text: .text%__1cNandI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cHOrINodeGadd_id6kM_pknEType__; +text: .text%__1cIPhaseCFGOinsert_goto_at6MII_v_; +text: .text%__1cOPhaseIdealLoop2t6MrnMPhaseIterGVN_pk0i_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_tree6M_v_; +text: .text%__1cRsubI_rReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMinINodeLbottom_type6kM_pknEType__; +text: .text%__1cOjmpLoopEndNodeHtwo_adr6kM_I_; +text: .text%__1cJLoadBNodeJideal_reg6kM_I_; +text: .text%__1cNnegI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cFStateS_sub_Op_FastUnlock6MpknENode__v_; +text: .text%__1cXmembar_release_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRcmpFastUnlockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMVirtualSpaceNreserved_size6kM_L_; +text: .text%__1cScompU_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsarI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKStoreFNodeGOpcode6kM_i_; +text: .text%__1cLCastP2LNodeJideal_reg6kM_I_; +text: .text%__1cPcmovI_reg_gNodeErule6kM_I_; +text: .text%__1cFStateP_sub_Op_CastP2L6MpknENode__v_; +text: .text%__1cScompU_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cScompU_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cScompU_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cWCallLeafNoFPDirectNodeRis_safepoint_node6kM_i_; +text: .text%__1cQjava_lang_ThreadRset_thread_status6FpnHoopDesc_n0AMThreadStatus__v_; +text: .text%__1cWCallLeafNoFPDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerDjcc6Mn0AJCondition_pCnJrelocInfoJrelocType__v_; +text: .text%__1cKstoreFNodeMideal_Opcode6kM_i_; +text: .text%__1cIimmFOperJconstantF6kM_f_; +text: .text%__1cNcmovI_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cKTypeOopPtrSmake_from_constant6FpnIciObject__pk0_; +text: .text%__1cNcmovI_regNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerEmovq6MnHAddress_i_v_; +text: .text%__1cIciObjectJis_method6M_i_; +text: .text%__1cIciObjectOis_method_data6M_i_; +text: .text%__1cIDivINodeLbottom_type6kM_pknEType__; +text: .text%__1cHOrINodeJideal_reg6kM_I_; +text: .text%__1cNcmovI_regNodeMcisc_operand6kM_i_; +text: .text%__1cIAndLNodeGmul_id6kM_pknEType__; +text: .text%__1cIAndLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNinstanceKlassUfind_interface_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cJloadFNodeMideal_Opcode6kM_i_; +text: .text%__1cbFunnecessary_membar_volatileNodeMideal_Opcode6kM_i_; +text: .text%__1cSmembar_acquireNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cIAndLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIAndLNodeKadd_opcode6kM_i_; +text: .text%__1cFStateO_sub_Op_StoreC6MpknENode__v_; +text: .text%__1cIAndLNodeKmul_opcode6kM_i_; +text: .text%__1cRaddL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMrep_stosNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXmembar_acquire_lockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFParseJdo_ifnull6MnIBoolTestEmask__v_; +text: .text%__1cMtlsLoadPNodeHtwo_adr6kM_I_; +text: .text%__1cIGraphKitOset_pair_local6MipnENode__v_; +text: .text%__1cJLoadCNodeJideal_reg6kM_I_; +text: .text%__1cPcmovI_reg_lNodeMideal_Opcode6kM_i_; +text: .text%__1cJCodeCacheXmark_for_deoptimization6FpnMklassOopDesc__i_; +text: .text%__1cMrcx_RegIOperEtype6kM_pknEType__; +text: .text%__1cLConvL2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOPhaseIdealLoopKDominators6M_v_; +text: .text%__1cHNTarjanDDFS6Fp0rnJVectorSet_pnOPhaseIdealLoop_pI_i_; +text: .text%__1cHNTarjanIsetdepth6MIpI_v_; +text: .text%__1cIMulLNodeKmul_opcode6kM_i_; +text: .text%__1cIMulLNodeKadd_opcode6kM_i_; +text: .text%jni_SetLongField: jni.o; +text: .text%__1cOPhaseIdealLoopQbuild_loop_early6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_late6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopRinit_dom_lca_tags6M_v_; +text: .text%__1cKstoreLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLPcDescCacheLadd_pc_desc6MpnGPcDesc__v_; +text: .text%__1cScheck_phi_clipping6FpnHPhiNode_rpnHConNode_rI45rpnENode_5_i_: cfgnode.o; +text: .text%__1cJloadSNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTconvI2L_reg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cRsubI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMloadConFNodeMideal_Opcode6kM_i_; +text: .text%__1cTC2IAdapterGeneratorUgenerate_c2i_adapter6FnMmethodHandle__pnKC2IAdapter__; +text: .text%__1cKCompiledICIis_clean6kM_i_; +text: .text%__1cNaddP_rRegNodeErule6kM_I_; +text: .text%__1cRmulL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cNmethodOopDescVclear_native_function6M_v_; +text: .text%__1cICodeBlobOis_java_method6kM_i_; +text: .text%__1cKVtableStubSpd_code_size_limit6Fi_i_; +text: .text%__1cIUniverseWis_out_of_memory_error6FnGHandle__i_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle_pnGThread__v_; +text: .text%__1cTjava_lang_ThrowableQclear_stacktrace6FpnHoopDesc__v_; +text: .text%__1cKJavaThreadGactive6F_p0_; +text: .text%JVM_FillInStackTrace; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle__v_; +text: .text%__1cSInterpreterRuntimePset_bcp_and_mdp6FpCpnKJavaThread__v_; +text: .text%__1cKJavaThreadNreguard_stack6MpC_i_; +text: .text%__1cFframeZinterpreter_frame_set_bcp6MpC_v_; +text: .text%jni_DeleteGlobalRef: jni.o; +text: .text%__1cKCompiledICZcompute_monomorphic_entry6FnMmethodHandle_nLKlassHandle_iirnOCompiledICInfo_pnGThread__v_; +text: .text%__1cNGrowableArray4nMmethodHandle__Icontains6kMrkn0A__i_; +text: .text%__1cLOpaque2NodeEhash6kM_I_; +text: .text%__1cICodeHeapLfirst_block6kM_pnJHeapBlock__; +text: .text%__1cJCodeCacheFfirst6F_pnICodeBlob__; +text: .text%__1cJBytecodesRspecial_length_at6FpC_i_; +text: .text%__1cFParseGdo_new6M_v_; +text: .text%__1cFParseFBlockMadd_new_path6M_i_; +text: .text%__1cLklassItablebFinitialize_itable_for_interface6MpnMklassOopDesc_pnRitableMethodEntry__v_; +text: .text%__1cJimmI0OperJnum_edges6kM_I_; +text: .text%__1cRmulI_rReg_immNodeMcisc_operand6kM_i_; +text: .text%__1cICodeHeapMmax_capacity6kM_L_; +text: .text%__1cMciMethodDataStrap_recompiled_at6MpnLProfileData__i_; +text: .text%__1cRindIndexScaleOperFscale6kM_i_; +text: .text%__1cNxorI_rRegNodeErule6kM_I_; +text: .text%__1cFParseFBlockNstack_type_at6kMi_pknEType__; +text: .text%__1cLConvL2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFTypeFEmake6Ff_pk0_; +text: .text%__1cIModINodeLbottom_type6kM_pknEType__; +text: .text%__1cJcmpOpOperHgreater6kM_i_; +text: .text%__1cQComputeCallStackHdo_bool6M_v_; +text: .text%__1cJMemRegionMintersection6kM0_0_; +text: .text%__1cNobjArrayKlassIallocate6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cRmulI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHciKlassGloader6M_pnHoopDesc__; +text: .text%__1cIConDNodeGOpcode6kM_i_; +text: .text%__1cNandI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cLRethrowNodeEhash6kM_I_; +text: .text%__1cTC2IAdapterGeneratorSstd_verified_entry6FnMmethodHandle__pC_; +text: .text%__1cIDivLNodeGOpcode6kM_i_; +text: .text%__1cNandI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cGThreadOis_Java_thread6kM_i_; +text: .text%__1cSmembar_releaseNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cHMatcherQinline_cache_reg6F_i_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeJnum_opnds6kM_I_; +text: .text%__1cbBInterpreterCodeletInterfaceRcode_size_to_size6kMi_i_; +text: .text%__1cbBInterpreterCodeletInterfaceKinitialize6MpnEStub_i_v_; +text: .text%jni_NewLocalRef: jni.o; +text: .text%__1cSSetupItableClosureEdoit6MpnMklassOopDesc_i_v_; +text: .text%__1cLOptoRuntimebAresolve_opt_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cPstoreImmI16NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLRShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIemit_d166FrnKCodeBuffer_i_v_; +text: .text%__1cKimmI16OperIconstant6kM_l_; +text: .text%__1cPClassFileParserbNparse_classfile_inner_classes_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__H_; +text: .text%__1cMloadConFNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__; +text: .text%__1cJCodeCacheNalive_nmethod6FpnICodeBlob__pnHnmethod__; +text: .text%__1cJAssemblerGmovzbl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cIVMThreadMis_VM_thread6kM_i_; +text: .text%__1cPcmovI_reg_lNodeJnum_opnds6kM_I_; +text: .text%__1cMloadConLNodeHsize_of6kM_I_; +text: .text%__1cOMacroAssemblerSload_unsigned_byte6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cTconvI2L_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNaddL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cKstoreFNodeJnum_opnds6kM_I_; +text: .text%__1cNaddL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cSComputeAdapterInfoJdo_double6M_v_; +text: .text%__1cLimmUL32OperFclone6kM_pnIMachOper__; +text: .text%__1cPloadConUL32NodeFclone6kM_pnENode__; +text: .text%__1cLLShiftLNodeJideal_reg6kM_I_; +text: .text%__1cMtlsLoadPNodePoper_input_base6kM_I_; +text: .text%__1cPlocal_vsnprintf6FpcLpkcpnR__va_list_element__i_; +text: .text%__1cSComputeAdapterInfoHdo_bool6M_v_; +text: .text%jio_vsnprintf; +text: .text%__1cURethrowExceptionNodeGpinned6kM_i_; +text: .text%__1cNstoreImmINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAndLNodeJideal_reg6kM_I_; +text: .text%__1cURethrowExceptionNodeHtwo_adr6kM_I_; +text: .text%__1cNDispatchTableJset_entry6MirnKEntryPoint__v_; +text: .text%jio_snprintf; +text: .text%__1cNSafePointNodeKgrow_stack6MpnIJVMState_I_v_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeErule6kM_I_; +text: .text%__1cURethrowExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsarL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cTcompareAndSwapLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_GetCPMethodModifiers; +text: .text%__1cFStateR_sub_Op_SafePoint6MpknENode__v_; +text: .text%__1cSsafePoint_pollNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOCompilerOraclePshould_break_at6FnMmethodHandle__i_; +text: .text%__1cJloadCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQorI_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cPsarI_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLRShiftLNodeLbottom_type6kM_pknEType__; +text: .text%__1cKReturnNode2t6MpnENode_2222_v_; +text: .text%__1cKReturnNodeJideal_reg6kM_I_; +text: .text%__1cNinstanceKlassPadd_implementor6MpnMklassOopDesc__v_; +text: .text%__1cRPrivilegedElementKinitialize6MpnMvframeStream_pnHoopDesc_p0pnGThread__v_; +text: .text%JVM_DoPrivileged; +text: .text%__1cOGenerateOopMapXreplace_all_CTS_matches6MnNCellTypeState_1_v_; +text: .text%__1cNIdealLoopTreeMis_loop_exit6kMpnENode_pnOPhaseIdealLoop__2_; +text: .text%__1cPpoll_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cSsafePoint_pollNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSsafePoint_pollNodeFreloc6kM_i_; +text: .text%__1cLStrCompNodeGOpcode6kM_i_; +text: .text%__1cJOopMapSet2t6M_v_; +text: .text%__1cKloadUBNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cNobjArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cKstoreCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeGOpcode6kM_i_; +text: .text%__1cOLibraryCallKitNtry_to_inline6M_i_; +text: .text%__1cNFingerprinterHdo_bool6M_v_; +text: .text%__1cOPhaseIdealLoopUsplit_if_with_blocks6MrnJVectorSet_rnKNode_Stack__v_; +text: .text%__1cNmethodOopDescbDbuild_interpreter_method_data6FnMmethodHandle_pnGThread__v_; +text: .text%__1cQLibraryIntrinsicIgenerate6MpnIJVMState__2_; +text: .text%__1cLOopRecorderIoop_size6M_i_; +text: .text%__1cHnmethodOexception_size6kM_i_; +text: .text%__1cHnmethodPscopes_pcs_size6kM_i_; +text: .text%__1cYDebugInformationRecorderIpcs_size6M_i_; +text: .text%__1cYDebugInformationRecorderJdata_size6M_i_; +text: .text%__1cHnmethodQscopes_data_size6kM_i_; +text: .text%__1cHnmethodJstub_size6kM_i_; +text: .text%__1cHnmethodKtotal_size6kM_i_; +text: .text%__1cNtestU_regNodeErule6kM_I_; +text: .text%__1cJOopMapSetJheap_size6kM_i_; +text: .text%__1cICodeBlobWfix_relocation_at_move6Ml_v_; +text: .text%__1cKCodeBufferJcopy_code6MpnICodeBlob__v_; +text: .text%__1cNRelocIteratorMcreate_index6FpnKCodeBuffer_pnJrelocInfo_4_4_; +text: .text%__1cICodeBlobPallocation_size6FpnKCodeBuffer_ii_I_; +text: .text%__1cRAbstractAssemblerOcode_fill_byte6F_i_; +text: .text%__1cICodeBlob2t6MpkcpnKCodeBuffer_iiipnJOopMapSet_i_v_; +text: .text%__1cMrdx_RegLOperEtype6kM_pknEType__; +text: .text%__1cKCodeBufferPcopy_relocation6MpnICodeBlob__v_; +text: .text%__1cVPatchingRelocIteratorHprepass6M_v_; +text: .text%__1cVPatchingRelocIteratorIpostpass6M_v_; +text: .text%__1cJOopMapSetHcopy_to6MpC_v_; +text: .text%__1cLPhaseValues2t6Mp0_v_; +text: .text%__1cINodeHash2t6Mp0_v_; +text: .text%__1cOPhaseTransform2t6Mp0nFPhaseLPhaseNumber__v_; +text: .text%__1cJAssemblerDjmp6MnHAddress__v_; +text: .text%__1cOJNIHandleBlockRrebuild_free_list6M_v_; +text: .text%__1cSstring_compareNodeZcheck_for_anti_dependence6kM_i_; +text: .text%jni_GetObjectArrayElement: jni.o; +text: .text%__1cKCompiledICSset_to_monomorphic6MrknOCompiledICInfo__v_; +text: .text%__1cIDivINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cVCallRuntimeDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cICmpDNodeGOpcode6kM_i_; +text: .text%__1cPcmovI_reg_gNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_AndI6MpknENode__v_; +text: .text%__1cHCompilebBregister_library_intrinsics6M_v_; +text: .text%__1cNGrowableArray4CpnNCallGenerator__2t6Mii_v_; +text: .text%__1cETypeKInitialize6FpnHCompile__v_; +text: .text%__1cYDebugInformationRecorder2t6MpnLOopRecorder__v_; +text: .text%__1cOCompileWrapper2t6MpnHCompile__v_; +text: .text%__1cHCompileEInit6Mi_v_; +text: .text%__1cVExceptionHandlerTable2t6Mi_v_; +text: .text%__1cFDictIFreset6MpknEDict__v_; +text: .text%__1cUDebugInfoWriteStream2t6MpnYDebugInformationRecorder_i_v_; +text: .text%JVM_FindLoadedClass; +text: .text%__1cHMatcherZnumber_of_saved_registers6F_i_; +text: .text%__1cXPhaseAggressiveCoalesceNinsert_copies6MrnHMatcher__v_; +text: .text%__1cIPhaseCFGDDFS6MpnGTarjan__I_; +text: .text%__1cFArenaNmove_contents6Mp0_1_; +text: .text%__1cIPhaseIFG2t6MpnFArena__v_; +text: .text%__1cHMatcherUvalidate_null_checks6M_v_; +text: .text%__1cHCompileOcompute_old_SP6M_i_; +text: .text%__1cJPhaseLive2t6MrknIPhaseCFG_rnILRG_List_pnFArena__v_; +text: .text%__1cMPhaseChaitinRbuild_ifg_virtual6M_v_; +text: .text%__1cXPhaseAggressiveCoalesceGverify6M_v_; +text: .text%__1cJStartNodeJideal_reg6kM_I_; +text: .text%__1cHMatcher2t6MrnJNode_List__v_; +text: .text%__1cFArena2t6ML_v_; +text: .text%__1cIPhaseCFGOschedule_early6MrnJVectorSet_rnJNode_List_rnLBlock_Array__i_; +text: .text%__1cWNode_Backward_Iterator2t6MpnENode_rnJVectorSet_rnJNode_List_rnLBlock_Array__v_; +text: .text%__1cHMatcherFmatch6M_v_; +text: .text%__1cFStateM_sub_Op_Goto6MpknENode__v_; +text: .text%__1cIPhaseCFGNschedule_late6MrnJVectorSet_rnJNode_List_rnNGrowableArray4CI___v_; +text: .text%__1cIPhaseCFGQFind_Inner_Loops6M_v_; +text: .text%__1cIPhaseCFGQGlobalCodeMotion6MrnHMatcher_IrnJNode_List__v_; +text: .text%__1cIPhaseCFGYEstimate_Block_Frequency6M_v_; +text: .text%__1cIPhaseCFGJbuild_cfg6M_I_; +text: .text%__1cHCompileICode_Gen6M_v_; +text: .text%__1cMPhaseChaitin2t6MIrnIPhaseCFG_rnHMatcher__v_; +text: .text%__1cMPhaseChaitinRRegister_Allocate6M_v_; +text: .text%__1cMPhaseChaitinGde_ssa6M_v_; +text: .text%__1cMPhaseChaitinbGstretch_base_pointer_live_ranges6MpnMResourceArea__i_; +text: .text%__1cNPhaseRegAllocTpd_preallocate_hook6M_v_; +text: .text%__1cHMatcherPinit_spill_mask6MpnENode__v_; +text: .text%__1cHMatcherTFixup_Save_On_Entry6M_v_; +text: .text%__1cHMatcherVinit_first_stack_mask6M_v_; +text: .text%__1cIPhaseCFG2t6MpnFArena_pnIRootNode_rnHMatcher__v_; +text: .text%__1cGTarjanIsetdepth6MI_v_; +text: .text%__1cIPhaseCFGKDominators6M_v_; +text: .text%__1cNPhaseRegAlloc2t6MIrnIPhaseCFG_rnHMatcher_pF_v_v_; +text: .text%__1cIPhaseCFGVschedule_pinned_nodes6MrnJVectorSet__v_; +text: .text%__1cHCompileTframe_size_in_words6kM_i_; +text: .text%__1cOCompileWrapper2T6M_v_; +text: .text%__1cHCompileYinit_scratch_buffer_blob6M_v_; +text: .text%__1cHCompileYinit_scratch_locs_memory6M_v_; +text: .text%__1cNPhasePeephole2t6MpnNPhaseRegAlloc_rnIPhaseCFG__v_; +text: .text%__1cJPhaseLive2T6M_v_; +text: .text%__1cNPhasePeephole2T6M_v_; +text: .text%__1cHCompileGOutput6M_v_; +text: .text%__1cHCompileQShorten_branches6MpnFLabel_ri333_v_; +text: .text%__1cHCompileLFill_buffer6M_v_; +text: .text%__1cHCompileTFillExceptionTables6MIpI1pnFLabel__v_; +text: .text%__1cHCompileRScheduleAndBundle6M_v_; +text: .text%__1cOMachPrologNodeFreloc6kM_i_; +text: .text%__1cNtestU_regNodePoper_input_base6kM_I_; +text: .text%__1cWemit_exception_handler6FrnKCodeBuffer__v_; +text: .text%__1cWsize_exception_handler6F_I_; +text: .text%__1cWImplicitExceptionTableIset_size6MI_v_; +text: .text%__1cNPhasePeepholeMdo_transform6M_v_; +text: .text%__1cMPhaseChaitinMfixup_spills6M_v_; +text: .text%__1cMPhaseChaitin2T6M_v_; +text: .text%__1cNPhaseRegAllocPalloc_node_regs6Mi_v_; +text: .text%__1cKCodeBufferOrelocate_stubs6M_v_; +text: .text%__1cIPhaseCFGLRemoveEmpty6M_v_; +text: .text%__1cLdo_liveness6FpnNPhaseRegAlloc_pnIPhaseCFG_pnKBlock_List_ipnFArena_pnEDict__v_: buildOopMap.o; +text: .text%__1cHCompileMBuildOopMaps6M_v_; +text: .text%__1cMPhaseChaitinbApost_allocate_copy_removal6M_v_; +text: .text%__1cNGrowableArray4CpnJNode_List__2t6Mii_v_; +text: .text%__1cRsarL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cOGenerateOopMapIcopy_cts6MpnNCellTypeState_2_i_; +text: .text%__1cFStateM_sub_Op_CmpL6MpknENode__v_; +text: .text%__1cJloadSNodeFreloc6kM_i_; +text: .text%__1cFStateN_sub_Op_LoadS6MpknENode__v_; +text: .text%__1cSInterpreterRuntimeOprofile_method6FpnKJavaThread_pC_i_; +text: .text%__1cOCompiledRFrame2t6MnFframe_pnKJavaThread_pnGRFrame__v_; +text: .text%__1cKC2IAdapterOis_c2i_adapter6kM_i_; +text: .text%__1cOCompiledRFrameKtop_method6kM_nMmethodHandle__; +text: .text%__1cOCompiledRFrameLis_compiled6kM_i_; +text: .text%__1cRmethodDataOopDescKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cKoopFactoryOnew_methodData6FnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_bytes6FpnNmethodOopDesc__i_; +text: .text%__1cPmethodDataKlassIallocate6MnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cNxorI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_words6FpnNmethodOopDesc__i_; +text: .text%__1cRmethodDataOopDescPpost_initialize6MpnOBytecodeStream__v_; +text: .text%__1cHRetNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateO_sub_Op_Return6MpknENode__v_; +text: .text%__1cHRetNodeFreloc6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_base6MnITosState_ppCi_v_; +text: .text%__1cZCallInterpreterDirectNodeHtwo_adr6kM_I_; +text: .text%__1cNloadConP0NodeFclone6kM_pnENode__; +text: .text%__1cOClearArrayNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKScopeValueJread_from6FpnTDebugInfoReadStream__p0_; +text: .text%__1cOcompiledVFrameScreate_stack_value6kMpnKScopeValue__pnKStackValue__; +text: .text%__1cQleaPIdxScaleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRindIndexScaleOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cIGraphKitNallocate_heap6MpnENode_222pknITypeFunc_pC22ipknKTypeOopPtr__2_; +text: .text%__1cPciInstanceKlassbBcompute_shared_has_subklass6M_i_; +text: .text%__1cNSignatureInfoHdo_byte6M_v_; +text: .text%__1cQorI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cKCompiledICSset_ic_destination6MpC_v_; +text: .text%__1cNIdealLoopTreePiteration_split6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cNandI_rRegNodeErule6kM_I_; +text: .text%__1cRsarI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cIMulINodeGadd_id6kM_pknEType__; +text: .text%__1cVcompiledICHolderKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNmodI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cKloadUBNodeMideal_Opcode6kM_i_; +text: .text%__1cHBitDataKis_BitData6M_i_; +text: .text%__1cQsalI_rReg_CLNodeMideal_Opcode6kM_i_; +text: .text%__1cNaddP_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerEcmpq6MnHAddress_i_v_; +text: .text%__1cNloadConP0NodeFreloc6kM_i_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cSmembar_acquireNodeLbottom_type6kM_pknEType__; +text: .text%__1cOMacroAssemblerKincrementq6MpnMRegisterImpl_i_v_; +text: .text%__1cRsarI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_next6MnITosState_i_v_; +text: .text%__1cNGrowableArray4nMmethodHandle__2t6Mii_v_; +text: .text%__1cLConvL2INodeJideal_reg6kM_I_; +text: .text%__1cNGrowableArray4nLKlassHandle__2t6Mii_v_; +text: .text%__1cNmethodOopDescThas_native_function6kM_i_; +text: .text%JVM_GetClassNameUTF; +text: .text%__1cMPrefetchNodeJideal_reg6kM_I_; +text: .text%__1cKCodeBuffer2t6MpCi_v_; +text: .text%__1cNprefetchwNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateQ_sub_Op_Prefetch6MpknENode__v_; +text: .text%__1cOjmpLoopEndNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNprefetchwNodeFreloc6kM_i_; +text: .text%__1cIAddLNodeJideal_reg6kM_I_; +text: .text%__1cILocation2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cKstoreCNodeFreloc6kM_i_; +text: .text%__1cNdecI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNmethodOopDescWis_vanilla_constructor6kM_i_; +text: .text%__1cWCallLeafNoFPDirectNodeKmethod_set6Ml_v_; +text: .text%__1cOPhaseIdealLoopOplace_near_use6kMpnENode__2_; +text: .text%__1cHi2bNodeMideal_Opcode6kM_i_; +text: .text%__1cNLocationValueLis_location6kM_i_; +text: .text%__1cNLocationValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cIMulLNodeJideal_reg6kM_I_; +text: .text%__1cNsubL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cFStateN_sub_Op_LoadB6MpknENode__v_; +text: .text%__1cNnegI_rRegNodeErule6kM_I_; +text: .text%__1cNFingerprinterJdo_double6M_v_; +text: .text%JVM_FindClassFromClass; +text: .text%__1cKcmpOpUOperEless6kM_i_; +text: .text%__1cVcompiledICHolderKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cVcompiledICHolderKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cKReflectionGinvoke6FnTinstanceKlassHandle_nMmethodHandle_nGHandle_inOobjArrayHandle_nJBasicType_4ipnGThread__pnHoopDesc__; +text: .text%__1cITypeLongFwiden6kMpknEType__3_; +text: .text%__1cQsalI_rReg_CLNodePoper_input_base6kM_I_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectIoverride6FpnHoopDesc__C_; +text: .text%__1cKReflectionDbox6FpnGjvalue_nJBasicType_pnGThread__pnHoopDesc__; +text: .text%__1cMLinkResolverbHlinktime_resolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cLBoxLockNodeEhash6kM_I_; +text: .text%__1cJOopMapSetMgrow_om_data6M_v_; +text: .text%__1cRxorI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cKciTypeFlowFBlockQset_private_copy6Mi_v_; +text: .text%__1cWandI_rReg_imm65535NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWandI_rReg_imm65535NodeErule6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerGpush_i6MpnMRegisterImpl__v_; +text: .text%__1cNcmovI_regNodeErule6kM_I_; +text: .text%__1cRsalL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNGrowableArray4CpnKInlineTree__Egrow6Mi_v_; +text: .text%__1cSComputeAdapterInfoIdo_short6M_v_; +text: .text%__1cNtestL_regNodeJnum_opnds6kM_I_; +text: .text%__1cLConvF2DNodeGOpcode6kM_i_; +text: .text%__1cISubLNodeLbottom_type6kM_pknEType__; +text: .text%__1cSmembar_acquireNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmembar_acquireNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNaddP_rRegNodeLbottom_type6kM_pknEType__; +text: .text%__1cNmodL_rRegNodeErule6kM_I_; +text: .text%__1cRsalI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerDret6Mi_v_; +text: .text%__1cRshrI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbDjava_lang_reflect_ConstructorPparameter_types6FpnHoopDesc__2_; +text: .text%__1cKReflectionSinvoke_constructor6FpnHoopDesc_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cbDjava_lang_reflect_ConstructorFclazz6FpnHoopDesc__2_; +text: .text%JVM_NewInstanceFromConstructor; +text: .text%__1cbDjava_lang_reflect_ConstructorEslot6FpnHoopDesc__i_; +text: .text%__1cFStateL_sub_Op_OrI6MpknENode__v_; +text: .text%JVM_IHashCode; +text: .text%__1cMtlsLoadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cEUTF8Ounicode_length6Fpkc_i_; +text: .text%__1cHTypePtrEmake6FnETypeFTYPES_n0ADPTR_i_pk0_; +text: .text%__1cPfieldDescriptorUstring_initial_value6kMpnGThread__pnHoopDesc__; +text: .text%__1cNxorI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cIregFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLMachUEPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLOpaque2NodeLbottom_type6kM_pknEType__; +text: .text%__1cNtestL_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadFNodePoper_input_base6kM_I_; +text: .text%__1cHRetDataKcell_count6M_i_; +text: .text%__1cSinstanceKlassKlassOklass_oop_size6kM_i_; +text: .text%__1cSobjArrayKlassKlassOklass_oop_size6kM_i_; +text: .text%__1cPCallRuntimeNodeGOpcode6kM_i_; +text: .text%__1cLsymbolKlassOklass_oop_size6kM_i_; +text: .text%__1cLmethodKlassOklass_oop_size6kM_i_; +text: .text%__1cMdecI_memNodePoper_input_base6kM_I_; +text: .text%__1cRconstantPoolKlassOklass_oop_size6kM_i_; +text: .text%__1cVcompiledICHolderKlassOklass_oop_size6kM_i_; +text: .text%__1cTtypeArrayKlassKlassOklass_oop_size6kM_i_; +text: .text%__1cWconstantPoolCacheKlassOklass_oop_size6kM_i_; +text: .text%__1cQconstMethodKlassOklass_oop_size6kM_i_; +text: .text%__1cPmethodDataKlassOklass_oop_size6kM_i_; +text: .text%__1cGThreadOis_interrupted6Fp0i_i_; +text: .text%__1cHThreadsHoops_do6FpnKOopClosure__v_; +text: .text%__1cJHashtableHoops_do6MpnKOopClosure__v_; +text: .text%__1cSsafePoint_pollNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIPSOldGenHcompact6M_v_; +text: .text%__1cSsafePoint_pollNodeJnum_opnds6kM_I_; +text: .text%__1cFJNIidHoops_do6MpnKOopClosure__v_; +text: .text%__1cJvmSymbolsHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cJHashtableGunlink6MpnRBoolObjectClosure__v_; +text: .text%__1cSReferenceProcessorHoops_do6MpnKOopClosure__v_; +text: .text%__1cQObjectStartArrayFreset6M_v_; +text: .text%__1cIPSOldGenPadjust_pointers6M_v_; +text: .text%__1cScompP_mem_rRegNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJloadBNodeFreloc6kM_i_; +text: .text%__1cUandI_rReg_imm255NodeMideal_Opcode6kM_i_; +text: .text%__1cNGrowableArray4CpnKciTypeFlowFBlock__Icontains6kMrk2_i_; +text: .text%__1cScompP_mem_rRegNodeFreloc6kM_i_; +text: .text%__1cNcmovP_regNodePoper_input_base6kM_I_; +text: .text%__1cTno_rax_rdx_RegIOperJnum_edges6kM_I_; +text: .text%__1cKciTypeFlowLStateVectorJdo_aaload6MpnQciBytecodeStream__v_; +text: .text%__1cJAssemblerMemit_operand6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerMemit_operand6MpnRFloatRegisterImpl_pnMRegisterImpl_4nHAddressLScaleFactor_ipCrknQRelocationHolder__v_; +text: .text%__1cNaddL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLRethrowNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRaddI_rReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRsubI_rReg_memNodeHtwo_adr6kM_I_; +text: .text%__1cIModLNodeGOpcode6kM_i_; +text: .text%__1cIMaxINodeLbottom_type6kM_pknEType__; +text: .text%__1cFParseMdo_checkcast6M_v_; +text: .text%__1cIMulINodeGmul_id6kM_pknEType__; +text: .text%__1cMloadConINodeGis_Con6kM_I_; +text: .text%__1cIregDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIMulDNodeGOpcode6kM_i_; +text: .text%__1cRsarL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNsubL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cHnmethodUnumber_of_dependents6kM_i_; +text: .text%__1cTconvI2L_reg_memNodeFreloc6kM_i_; +text: .text%__1cSComputeAdapterInfoIdo_float6M_v_; +text: .text%__1cFParseLarray_store6MnJBasicType__v_; +text: .text%__1cOmangle_name_on6FpnMoutputStream_pnNsymbolOopDesc_ii_v_: nativeLookup.o; +text: .text%JVM_FindClassFromClassLoader; +text: .text%__1cZCallInterpreterDirectNodeSalignment_required6kM_i_; +text: .text%__1cZCallInterpreterDirectNodePoper_input_base6kM_I_; +text: .text%__1cZCallInterpreterDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRmulL_rReg_immNodeMcisc_operand6kM_i_; +text: .text%__1cNloadConI0NodeGis_Con6kM_I_; +text: .text%__1cKstoreBNodeHtwo_adr6kM_I_; +text: .text%__1cOmangle_name_on6FpnMoutputStream_pnNsymbolOopDesc__v_: nativeLookup.o; +text: .text%__1cKRegionNodeUdepends_only_on_test6kM_i_; +text: .text%__1cMMergeMemNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cFciEnvZcall_has_multiple_targets6FpnNinstanceKlass_nMsymbolHandle_3ri_i_; +text: .text%__1cMadjust_check6FpnENode_11iipnMPhaseIterGVN__v_: ifnode.o; +text: .text%__1cPsalI_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTconvI2L_reg_memNodeHtwo_adr6kM_I_; +text: .text%__1cLPhaseValuesKis_IterGVN6M_pnMPhaseIterGVN__; +text: .text%__1cQciTypeArrayKlassJmake_impl6FnJBasicType__p0_; +text: .text%__1cFStateM_sub_Op_AddL6MpknENode__v_; +text: .text%__1cQciTypeArrayKlassEmake6FnJBasicType__p0_; +text: .text%__1cUmembar_cpu_orderNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUmembar_cpu_orderNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSCMemProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSCompareAndSwapNodeJideal_reg6kM_I_; +text: .text%__1cFStateW_sub_Op_MemBarCPUOrder6MpknENode__v_; +text: .text%__1cKciTypeFlowLStateVectorMdo_checkcast6MpnQciBytecodeStream__v_; +text: .text%__1cMorI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMrax_RegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%lwp_mutex_init: os_solaris.o; +text: .text%__1cJStubQdDueueGcommit6Mi_v_; +text: .text%__1cJStubQdDueueHrequest6Mi_pnEStub__; +text: .text%__1cNmodL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cQjava_lang_ThreadRget_thread_status6FpnHoopDesc__n0AMThreadStatus__; +text: .text%__1cMincI_memNodePoper_input_base6kM_I_; +text: .text%__1cOGenerateOopMapOdo_monitorexit6Mi_v_; +text: .text%__1cOGenerateOopMapLmonitor_pop6M_nNCellTypeState__; +text: .text%__1cJAssemblerEmovl6MnHAddress_i_v_; +text: .text%__1cKoopFactoryNnew_charArray6FpkcpnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cPshrI_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cQjava_lang_StringTcreate_oop_from_str6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cRmulI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cNandI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cbACallCompiledJavaDirectNodeHtwo_adr6kM_I_; +text: .text%__1cIModINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnHoopDesc_pnNsymbolOopDesc__i_; +text: .text%__1cbLtransform_int_divide_to_long_multiply6FpnIPhaseGVN_pnENode_i_3_: divnode.o; +text: .text%__1cTno_rax_rdx_RegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cJAssemblerGmovzwl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cRmulL_rReg_immNodeErule6kM_I_; +text: .text%__1cZCallDynamicJavaDirectNodePoper_input_base6kM_I_; +text: .text%__1cHTypePtrFempty6kM_i_; +text: .text%__1cOMacroAssemblerSload_unsigned_word6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cOGenerateOopMapXdo_return_monitor_check6M_v_; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cFStateP_sub_Op_ConvL2I6MpknENode__v_; +text: .text%__1cLOptoRuntimebBcomplete_monitor_enter_Type6F_pknITypeFunc__; +text: .text%__1cIGraphKitMnext_monitor6M_i_; +text: .text%__1cLBoxLockNode2t6Mi_v_; +text: .text%__1cRmulI_rReg_immNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cJloadFNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cIplus_adr6FpnENode_l_1_: generateOptoStub.o; +text: .text%__1cIGraphKitLshared_lock6MpnENode__pnMFastLockNode__; +text: .text%__1cHConNode2t6MpknEType__v_; +text: .text%__1cMloadConDNodeMideal_Opcode6kM_i_; +text: .text%__1cNCompileBrokerTcreate_compile_task6FpnMCompileQdDueue_inMmethodHandle_i3ipkcii_pnLCompileTask__; +text: .text%__1cLCompileTaskKinitialize6MinMmethodHandle_i1ipkcii_v_; +text: .text%__1cNCompileBrokerNallocate_task6F_pnLCompileTask__; +text: .text%__1cMCompileQdDueueDadd6MpnLCompileTask__v_; +text: .text%__1cRxorI_rReg_memNodeErule6kM_I_; +text: .text%__1cMCompileQdDueueDget6M_pnLCompileTask__; +text: .text%__1cRsarI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQleaPIdxScaleNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cSCompileTaskWrapper2t6MpnLCompileTask__v_; +text: .text%__1cQjava_lang_ThreadGthread6FpnHoopDesc__pnKJavaThread__; +text: .text%__1cXmembar_acquire_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_MulL6MpknENode__v_; +text: .text%__1cCosPhint_no_preempt6F_v_; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_i_v_; +text: .text%__1cObox_handleNodePoper_input_base6kM_I_; +text: .text%__1cNCompileBrokerJfree_task6FpnLCompileTask__v_; +text: .text%__1cSCompileTaskWrapper2T6M_v_; +text: .text%__1cLCompileTaskEfree6M_v_; +text: .text%__1cNnegI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKDictionarybAis_valid_protection_domain6MiInMsymbolHandle_nGHandle_2_i_; +text: .text%__1cMincI_memNodeMideal_Opcode6kM_i_; +text: .text%__1cRandL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cRaddI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cQjava_lang_StringXcreate_oop_from_unicode6FpHipnGThread__pnHoopDesc__; +text: .text%jni_NewString: jni.o; +text: .text%__1cRxorI_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cFStateM_sub_Op_AndL6MpknENode__v_; +text: .text%__1cKloadUBNodePoper_input_base6kM_I_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVcompiledICHolderKlassXoop_is_compiledICHolder6kM_i_; +text: .text%__1cJStoreNodeUdepends_only_on_test6kM_i_; +text: .text%__1cPcmovI_reg_lNodeErule6kM_I_; +text: .text%__1cOloadConL32NodePoper_input_base6kM_I_; +text: .text%__1cNSharedRuntimebJcontinuation_for_implicit_exception6FpnKJavaThread_pCn0AVImplicitExceptionKind__3_; +text: .text%__1cRtestI_reg_immNodeHtwo_adr6kM_I_; +text: .text%__1cIimmDOperJconstantD6kM_d_; +text: .text%__1cFParsePmerge_exception6Mi_v_; +text: .text%__1cXmembar_acquire_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cNGrowableArray4CpnIciObject__2t6MpnFArena_iirk1_v_; +text: .text%__1cICallInfoDset6MnLKlassHandle_1nMmethodHandle_2pnGThread__v_; +text: .text%__1cZCallDynamicJavaDirectNodeHtwo_adr6kM_I_; +text: .text%__1cMLinkResolverbGruntime_resolve_interface_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cMLinkResolverWresolve_interface_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cNGrowableArray4CpnIciObject__JappendAll6Mpk2_v_; +text: .text%__1cFciEnv2t6MpnHJNIEnv__iii_v_; +text: .text%__1cRtestP_reg_memNodeFreloc6kM_i_; +text: .text%__1cNtestP_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNGrowableArray4CpnIciMethod__2t6MpnFArena_iirk1_v_; +text: .text%__1cNGrowableArray4CpnHciKlass__2t6MpnFArena_iirk1_v_; +text: .text%__1cPciObjectFactory2t6MpnFArena_i_v_; +text: .text%__1cNGrowableArray4CpnPciReturnAddress__2t6MpnFArena_iirk1_v_; +text: .text%__1cFciEnvWget_method_from_handle6MpnI_jobject__pnIciMethod__; +text: .text%__1cNCompileBrokerVpush_jni_handle_block6F_v_; +text: .text%__1cIJVMStateNmonitor_depth6kM_i_; +text: .text%__1cHciKlassSsuper_check_offset6M_I_; +text: .text%__1cFciEnv2T6M_v_; +text: .text%__1cKstoreBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNCompileBrokerUpop_jni_handle_block6F_v_; +text: .text%__1cbBopt_virtual_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cNinstanceKlassbFlookup_method_in_all_interfaces6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cPcmpFastLockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateQ_sub_Op_FastLock6MpknENode__v_; +text: .text%__1cXmembar_acquire_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvD2I_reg_regNodeErule6kM_I_; +text: .text%__1cITemplateIbytecode6kM_nJBytecodesECode__; +text: .text%__1cLOptoRuntimeRmultianewarray1_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cWImplicitExceptionTableCat6kMI_I_; +text: .text%__1cWImplicitExceptionTable2t6MpknHnmethod__v_; +text: .text%__1cLVtableStubsPstub_containing6FpC_pnKVtableStub__; +text: .text%__1cLVtableStubsIcontains6FpC_i_; +text: .text%__1cNFingerprinterIdo_float6M_v_; +text: .text%__1cHnmethodbJcontinuation_for_implicit_exception6MpC_1_; +text: .text%__1cLRShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cUjmpLoopEnd_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNmodI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cUjmpLoopEnd_shortNodeMideal_Opcode6kM_i_; +text: .text%__1cKEntryPoint2t6MpC11111111_v_; +text: .text%jni_GetObjectClass: jni.o; +text: .text%__1cRappend_interfaces6FnOobjArrayHandle_ripnPobjArrayOopDesc__v_; +text: .text%__1cRandI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cISubLNodeDsub6kMpknEType_3_3_; +text: .text%__1cJloadSNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRtestI_reg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cOloadConL32NodeHtwo_adr6kM_I_; +text: .text%__1cQshrI_rReg_CLNodePoper_input_base6kM_I_; +text: .text%__1cSstring_compareNodePoper_input_base6kM_I_; +text: .text%__1cNcmovI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMdecI_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMrax_RegLOperEtype6kM_pknEType__; +text: .text%__1cRmulI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cIXorINodeGadd_id6kM_pknEType__; +text: .text%__1cNtestP_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmovI_reg_gNodeHtwo_adr6kM_I_; +text: .text%__1cOPhaseIdealLoopKclone_loop6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cHi2bNodePoper_input_base6kM_I_; +text: .text%__1cRsalL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cKBinaryNodeGOpcode6kM_i_; +text: .text%__1cNxorI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cFStateO_sub_Op_Binary6MpknENode__v_; +text: .text%JVM_GetClassLoader; +text: .text%__1cMstoreSSPNodeMideal_Opcode6kM_i_; +text: .text%__1cNmulL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRxorI_rReg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cINodeHashIround_up6FI_I_; +text: .text%__1cHCompileKinit_start6MpnJStartNode__v_; +text: .text%__1cOPhaseTransform2t6MpnFArena_nFPhaseLPhaseNumber__v_; +text: .text%__1cLPhaseValues2t6MpnFArena_I_v_; +text: .text%__1cRaddP_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cINodeHash2t6MpnFArena_I_v_; +text: .text%__1cRaddI_rReg_memNodeHtwo_adr6kM_I_; +text: .text%__1cIJVMState2n6FL_pv_; +text: .text%__1cOMacroAssemblerFalign6Mi_v_; +text: .text%__1cOleaPIdxOffNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cFVTuneNregister_stub6FpkcpC3_v_; +text: .text%__1cFForteNregister_stub6FpkcpC3_v_; +text: .text%__1cMdecI_memNodeJnum_opnds6kM_I_; +text: .text%__1cIModINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMLinkResolverXresolve_invokeinterface6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%lwp_cond_init: os_solaris.o; +text: .text%__1cSmembar_releaseNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmembar_releaseNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOPhaseIdealLoopVclone_up_backedge_goo6MpnENode_22_2_; +text: .text%__1cSInterpreterCodeletKinitialize6MpkcnJBytecodesECode__v_; +text: .text%__1cTconvI2L_reg_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNxorI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNaddP_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerZcheck_and_handle_popframe6MpnMRegisterImpl__v_; +text: .text%__1cOloadConL32NodeErule6kM_I_; +text: .text%__1cOMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cFframeVnmethods_code_blob_do6M_v_; +text: .text%__1cHi2bNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKcmpOpUOperKless_equal6kM_i_; +text: .text%__1cWandI_rReg_imm65535NodeJnum_opnds6kM_I_; +text: .text%__1cFParseTprofile_switch_case6Mi_v_; +text: .text%__1cKNativeJumpbEcheck_verified_entry_alignment6FpC1_v_; +text: .text%__1cFParseSjump_switch_ranges6MpnENode_pnLSwitchRange_4i_v_; +text: .text%__1cFParseOmerge_new_path6Mi_v_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_; +text: .text%__1cNandI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cNmodL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cMloadConLNodeFclone6kM_pnENode__; +text: .text%__1cNtestU_regNodeJnum_opnds6kM_I_; +text: .text%__1cIimmLOperFclone6kM_pnIMachOper__; +text: .text%__1cRandL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cOleaPIdxOffNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_pkcp4_i_: arguments.o; +text: .text%__1cNCompileBrokerTis_not_compile_only6FnMmethodHandle__i_; +text: .text%__1cNCompileBrokerRassign_compile_id6FnMmethodHandle_i_I_; +text: .text%__1cNCompileBrokerTis_compile_blocking6FnMmethodHandle_i_i_; +text: .text%__1cIMulFNodeGOpcode6kM_i_; +text: .text%__1cNIdealLoopTreeQpolicy_peel_only6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeSpolicy_range_check6kMpnOPhaseIdealLoop__i_; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cNIdealLoopTreeMpolicy_align6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeNpolicy_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNtestU_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCompileBrokerZcompilation_is_prohibited6FnMmethodHandle_i_i_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFi_vi_v_; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_and_dispatch6MpnITemplate_nITosState__v_; +text: .text%__1cITemplateIgenerate6MpnZInterpreterMacroAssembler__v_; +text: .text%__1cKC2CompilerOneeds_adapters6M_i_; +text: .text%__1cLServiceUtilLvisible_oop6FpnHoopDesc__i_; +text: .text%__1cITemplateKinitialize6MinITosState_1pFi_vi_v_; +text: .text%__1cNObjectMonitorGEnterI6MpnGThread__v_; +text: .text%__1cIciMethodJhas_loops6kM_i_; +text: .text%__1cIciMethodVshould_print_assembly6M_i_; +text: .text%__1cOMacroAssemblerOcall_VM_helper6MpnMRegisterImpl_pCii_v_; +text: .text%__1cNloadConL0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cMincI_memNodeJnum_opnds6kM_I_; +text: .text%__1cNCompileBrokerOcheck_break_at6FnMmethodHandle_iii_i_; +text: .text%__1cJAssemblerEcall6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cNCompileBrokerQset_last_compile6FpnOCompilerThread_nMmethodHandle_ii_v_; +text: .text%__1cNCompileBrokerbAeager_compile_c2i_adapters6FpnFciEnv_pnIciMethod__v_; +text: .text%__1cNCompileBrokerbAeager_compile_i2c_adapters6FpnFciEnv_pnIciMethod__v_; +text: .text%__1cNCompileBrokerZinvoke_compiler_on_method6FpnLCompileTask__v_; +text: .text%__1cKC2CompilerOcompile_method6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cMstoreSSPNodeHis_Copy6kM_I_; +text: .text%__1cQshrI_rReg_CLNodeMideal_Opcode6kM_i_; +text: .text%__1cFciEnvPregister_method6MpnIciMethod_iiiiiipnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnQAbstractCompiler_ii_v_; +text: .text%__1cLAccessFlagsRatomic_clear_bits6Mi_v_; +text: .text%__1cIciMethodQbreak_at_execute6M_i_; +text: .text%__1cFciEnvbOcheck_for_system_dictionary_modification6MpnIciMethod__v_; +text: .text%__1cFciEnvbUsystem_dictionary_modification_counter_changed6M_i_; +text: .text%__1cMelapsedTimerDadd6M0_v_; +text: .text%__1cNCompileBrokerScollect_statistics6FpnOCompilerThread_nMelapsedTimer_pnLCompileTask__v_; +text: .text%__1cJStartNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cICodeHeapMinsert_after6MpnJFreeBlock_2_v_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_3pnRJavaCallArguments_nGHandle_6_6_; +text: .text%__1cFMutex2t6Mipkci_v_; +text: .text%__1cKloadUBNodeErule6kM_I_; +text: .text%__1cQsalL_rReg_CLNodeMideal_Opcode6kM_i_; +text: .text%__1cbACallCompiledJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbACallCompiledJavaDirectNodePoper_input_base6kM_I_; +text: .text%__1cTbasictype2arraycopy6FnJBasicType_i_pC_; +text: .text%__1cOLibraryCallKitQinline_arraycopy6M_i_; +text: .text%__1cPstoreImmI16NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLOptoRuntimeOarraycopy_Type6F_pknITypeFunc__; +text: .text%__1cFciEnvbFpost_compiled_method_load_event6MpnHnmethod__v_; +text: .text%__1cJCodeCacheGcommit6FpnICodeBlob__v_; +text: .text%__1cLPcDescCache2t6M_v_; +text: .text%__1cFciEnvVnum_inlined_bytecodes6kM_i_; +text: .text%__1cHnmethodSresolve_JNIHandles6M_v_; +text: .text%__1cRmulL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVExceptionHandlerTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cWImplicitExceptionTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cFVTuneOcreate_nmethod6FpnHnmethod__v_; +text: .text%__1cYDebugInformationRecorderHcopy_to6MpnHnmethod__v_; +text: .text%__1cHnmethodLnew_nmethod6FnMmethodHandle_iiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnQAbstractCompiler__p0_; +text: .text%__1cPcmovI_reg_lNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHnmFlagsFclear6M_v_; +text: .text%__1cHnmethod2n6FLi_pv_; +text: .text%__1cHnmethod2t6MpnNmethodOopDesc_iiiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnQAbstractCompiler__v_; +text: .text%__1cNaddI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cKTypeRawPtrFxdual6kM_pknEType__; +text: .text%__1cHnmethodQcopy_scopes_data6MpCi_v_; +text: .text%__1cLOopRecorderHcopy_to6MpnICodeBlob__v_; +text: .text%__1cICodeBlobJcopy_oops6MppnI_jobject_i_v_; +text: .text%__1cFStateN_sub_Op_LoadC6MpknENode__v_; +text: .text%__1cJloadCNodeFreloc6kM_i_; +text: .text%__1cFParseQjump_if_fork_int6MpnENode_2nIBoolTestEmask__pnGIfNode__; +text: .text%__1cWandI_rReg_imm65535NodeHtwo_adr6kM_I_; +text: .text%__1cNdivL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cNmethodOopDescIset_code6MpnHnmethod__v_; +text: .text%__1cINodeHashUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cQUnique_Node_ListUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cKInlineTreeWbuild_inline_tree_root6F_p0_; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod_ii_v_; +text: .text%__1cHCompileWprint_compile_messages6M_v_; +text: .text%__1cPClassFileParserbGparse_constant_pool_double_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cQsalI_rReg_CLNodeErule6kM_I_; +text: .text%__1cHCompileRbuild_start_state6MpnJStartNode_pknITypeFunc__pnIJVMState__; +text: .text%__1cHCompileVidentify_useful_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cIciMethodRbuild_method_data6M_v_; +text: .text%__1cHCompileIOptimize6M_v_; +text: .text%__1cHCompileUremove_useless_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cSPhaseRemoveUseless2t6MpnIPhaseGVN_pnQUnique_Node_List__v_; +text: .text%__1cHCompileLFinish_Warm6M_v_; +text: .text%__1cHCompileLInline_Warm6M_i_; +text: .text%__1cPno_rax_RegLOperJnum_edges6kM_I_; +text: .text%__1cMPhaseIterGVN2t6MpnIPhaseGVN__v_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii1_v_; +text: .text%__1cIciMethodRbuild_method_data6MnMmethodHandle__v_; +text: .text%__1cSstring_compareNodeErule6kM_I_; +text: .text%__1cbAfinal_graph_reshaping_walk6FrnKNode_Stack_pnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cHCompileVfinal_graph_reshaping6M_i_; +text: .text%__1cOcompI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cScompI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cJStartNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTableKtransition6FnITosState_1_v_; +text: .text%__1cIPhaseCCPJtransform6MpnENode__2_; +text: .text%__1cHCompileNreturn_values6MpnIJVMState__v_; +text: .text%__1cbCAbstractInterpreterGeneratorQset_entry_points6MnJBytecodesECode__v_; +text: .text%__1cbCAbstractInterpreterGeneratorWset_short_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cMPhaseIterGVN2t6Mp0_v_; +text: .text%__1cIPhaseCCP2t6MpnMPhaseIterGVN__v_; +text: .text%__1cIPhaseCCP2T6M_v_; +text: .text%__1cIPhaseCCPHanalyze6M_v_; +text: .text%__1cIPhaseCCPMdo_transform6M_v_; +text: .text%__1cOcompI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNsubL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConPcNodeMideal_Opcode6kM_i_; +text: .text%__1cKExceptionsG_throw6FpnGThread_pkcinGHandle__v_; +text: .text%__1cKExceptionsRspecial_exception6FpnGThread_pkcinGHandle__i_; +text: .text%__1cGThreadVset_pending_exception6MpnHoopDesc_pkci_v_; +text: .text%__1cNandL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLklassItableUcompute_itable_index6FpnNmethodOopDesc__i_; +text: .text%__1cWpoll_return_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cWConstantPoolCacheEntrySset_interface_call6MnMmethodHandle_i_v_; +text: .text%__1cJloadFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPClassFileParserUverify_constantvalue6MiinSconstantPoolHandle_pnGThread__v_; +text: .text%__1cQsalI_rReg_CLNodeJnum_opnds6kM_I_; +text: .text%__1cIMachNodeJis_MachIf6kM_pknKMachIfNode__; +text: .text%__1cPsalL_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cKExceptionsRspecial_exception6FpnGThread_pkcinMsymbolHandle_4_i_; +text: .text%__1cQjava_lang_StringScreate_from_symbol6FnMsymbolHandle_pnGThread__nGHandle__; +text: .text%__1cOClearArrayNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHMatcherbDinterpreter_frame_pointer_reg6F_i_; +text: .text%__1cQorI_rReg_immNodeErule6kM_I_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_53pnGThread__v_; +text: .text%__1cSInterpreterRuntimeXthrow_pending_exception6FpnKJavaThread__v_; +text: .text%__1cKcmpOpUOperHgreater6kM_i_; +text: .text%__1cNCompileBrokerUcheck_adapter_result6FnMmethodHandle_ippnMBasicAdapter__i_; +text: .text%__1cJloadFNodeJnum_opnds6kM_I_; +text: .text%__1cYinternal_word_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cSMachC2IEntriesNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cbFunnecessary_membar_volatileNodePoper_input_base6kM_I_; +text: .text%__1cRmulI_rReg_immNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cQPSIsAliveClosureLdo_object_b6MpnHoopDesc__i_; +text: .text%__1cTCallInterpreterNodeSis_CallInterpreter6kM_pk0_; +text: .text%__1cZCallInterpreterDirectNodePcompute_padding6kMi_i_; +text: .text%__1cSMachC2IcheckICNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_epilog6MnITosState_i_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_prolog6MnITosState_i_v_; +text: .text%__1cZCallInterpreterDirectNodeKmethod_set6Ml_v_; +text: .text%__1cXMachCallInterpreterNodePret_addr_offset6M_i_; +text: .text%__1cQjava_lang_StringPcreate_from_str6FpkcpnGThread__nGHandle__; +text: .text%__1cLOptoRuntimeInew_Type6F_pknITypeFunc__; +text: .text%__1cLBoxLockNodeDcmp6kMrknENode__I_; +text: .text%__1cMTailCallNodeGOpcode6kM_i_; +text: .text%__1cJChunkPoolMfree_all_but6ML_v_; +text: .text%__1cIGraphKitMnew_instance6MpnPciInstanceKlass__pnENode__; +text: .text%__1cPcmpD_cc_regNodePoper_input_base6kM_I_; +text: .text%__1cRsalL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cLOptoRuntimeSnew_typeArray_Type6F_pknITypeFunc__; +text: .text%__1cObox_handleNodeMideal_Opcode6kM_i_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cIGraphKitJnew_array6MpnENode_nJBasicType_pknEType_pknMTypeKlassPtr__2_; +text: .text%__1cNdecL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cKJavaThreadZsecurity_get_caller_class6Mi_pnMklassOopDesc__; +text: .text%__1cZCallDynamicJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOemit_d64_reloc6FrnKCodeBuffer_lrknQRelocationHolder_i_v_; +text: .text%__1cRtestI_reg_immNodeErule6kM_I_; +text: .text%__1cIAddFNodeGOpcode6kM_i_; +text: .text%__1cSstring_compareNodeMideal_Opcode6kM_i_; +text: .text%__1cNmethodOopDescWcompute_has_loops_flag6M_i_; +text: .text%__1cKloadUBNodeJnum_opnds6kM_I_; +text: .text%__1cNGrowableArray4CpnHoopDesc__2t6Mii_v_; +text: .text%__1cZCallDynamicJavaDirectNodeSalignment_required6kM_i_; +text: .text%__1cXvirtual_call_RelocationJpack_data6M_i_; +text: .text%__1cXvirtual_call_RelocationEtype6M_nJrelocInfoJrelocType__; +text: .text%__1cQorI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUParallelScavengeHeapIcapacity6kM_L_; +text: .text%__1cJAssemblerEcmpq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cNnegI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cYinternal_word_RelocationJpack_data6M_i_; +text: .text%__1cKsplit_once6FpnMPhaseIterGVN_pnENode_333_v_: cfgnode.o; +text: .text%__1cQSystemDictionarybAvalidate_protection_domain6FnTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cPDictionaryEntryVadd_protection_domain6MpnHoopDesc__v_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectMset_override6FpnHoopDesc_C_v_; +text: .text%__1cRCardTableModRefBSPclear_MemRegion6MnJMemRegion__v_; +text: .text%__1cOleaPIdxOffNodeLbottom_type6kM_pknEType__; +text: .text%__1cNdivL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cFParsebLincrement_and_test_invocation_counter6Mi_v_; +text: .text%__1cKDictionaryVadd_protection_domain6MiInTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%JVM_IsInterrupted; +text: .text%__1cFParseRarray_store_check6M_v_; +text: .text%__1cNobjArrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cScompL_rReg_immNodeMideal_Opcode6kM_i_; +text: .text%__1cTmembar_volatileNodeMideal_Opcode6kM_i_; +text: .text%__1cTCallDynamicJavaNodeSis_CallDynamicJava6kM_pk0_; +text: .text%__1cCosHSolarisFEventEpark6M_v_; +text: .text%__1cIMinINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cODeoptimizationYtrap_state_is_recompiled6Fi_i_; +text: .text%__1cXSignatureHandlerLibraryKinitialize6F_v_; +text: .text%__1cXSignatureHandlerLibraryDadd6FnMmethodHandle__v_; +text: .text%__1cSInterpreterRuntimeTprepare_native_call6FpnKJavaThread_pnNmethodOopDesc__v_; +text: .text%__1cNGrowableArray4CL_Efind6kMrkL_i_; +text: .text%__1cUandI_rReg_imm255NodePoper_input_base6kM_I_; +text: .text%__1cSReferenceProcessorZadd_to_discovered_list_mt6MppnHoopDesc_23_v_; +text: .text%__1cSInterpreterRuntimeNquicken_io_cc6FpnKJavaThread__v_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii_v_; +text: .text%__1cHOrINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cNObjectMonitorbAEntryQdDueue_SelectSuccessor6M_pnMObjectWaiter__; +text: .text%__1cNObjectMonitorREntryQdDueue_insert6MpnMObjectWaiter_i_v_; +text: .text%__1cSobjArrayKlassKlassXallocate_objArray_klass6MinLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cJAssemblerEpopq6MpnMRegisterImpl__v_; +text: .text%__1cSobjArrayKlassKlassbCallocate_objArray_klass_impl6FnYobjArrayKlassKlassHandle_inLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cISubLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMTypeKlassPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cNloadConI0NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSComputeAdapterInfoHdo_char6M_v_; +text: .text%__1cKPerfMemoryFalloc6FL_pc_; +text: .text%__1cIPerfData2T6M_v_; +text: .text%__1cIPerfDataMcreate_entry6MnJBasicType_LL_v_; +text: .text%__1cKPerfMemoryMmark_updated6F_v_; +text: .text%__1cPcmovI_reg_gNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEfrom6F_pnMRegisterImpl__; +text: .text%__1cXJNI_ArgumentPusherVaArgHget_int6M_v_; +text: .text%__1cIPerfData2t6MnJCounterNS_pkcn0AFUnits_n0ALVariability__v_; +text: .text%__1cPPerfDataManagerIadd_item6FpnIPerfData_i_v_; +text: .text%__1cLStrCompNodeKmatch_edge6kMI_I_; +text: .text%__1cOjmpLoopEndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOjmpLoopEndNodeOis_pc_relative6kM_i_; +text: .text%__1cOjmpLoopEndNodeTmay_be_short_branch6kM_i_; +text: .text%jni_ReleaseStringUTFChars: jni.o; +text: .text%jni_GetStringUTFChars: jni.o; +text: .text%__1cFStateW_sub_Op_CountedLoopEnd6MpknENode__v_; +text: .text%__1cNFingerprinterIdo_short6M_v_; +text: .text%__1cOcompU_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cJAssemblerEincq6MpnMRegisterImpl__v_; +text: .text%__1cFTypeDEmake6Fd_pk0_; +text: .text%__1cScompU_rReg_memNodeFreloc6kM_i_; +text: .text%__1cNtestL_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFParseLdo_newarray6MnJBasicType__v_; +text: .text%__1cWCallLeafNoFPDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMNativeLookupGlookup6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cWCallLeafNoFPDirectNodeFreloc6kM_i_; +text: .text%__1cSstring_compareNodeJnum_opnds6kM_I_; +text: .text%__1cFStateU_sub_Op_CallLeafNoFP6MpknENode__v_; +text: .text%JVM_FindLibraryEntry; +text: .text%__1cSObjectSynchronizerHinflate6FpnHoopDesc__pnNObjectMonitor__; +text: .text%JVM_GetMethodIxExceptionTableEntry; +text: .text%__1cNObjectMonitorHRecycle6M_v_; +text: .text%__1cISubLNodeGadd_id6kM_pknEType__; +text: .text%__1cNmodI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHMatcherOc_return_value6Fii_nLOptoRegPair__; +text: .text%__1cRxorI_rReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMachNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQsarL_rReg_63NodeMideal_Opcode6kM_i_; +text: .text%__1cRmulI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMachOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cIMachOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cZInterpreterMacroAssemblerDpop6MnITosState__v_; +text: .text%__1cNandI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cIDivINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNnegI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cFStateS_sub_Op_ClearArray6MpknENode__v_; +text: .text%__1cRaddL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFciEnvWis_dependence_violated6FpnMklassOopDesc_pnNmethodOopDesc__i_; +text: .text%__1cHCompile2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cIXorINodeJideal_reg6kM_I_; +text: .text%__1cMrep_stosNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRtestI_reg_immNodePoper_input_base6kM_I_; +text: .text%__1cKC2CompilerPcompile_adapter6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cMrep_stosNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMAdapterCacheGinsert6MpnLAdapterInfo_pnMBasicAdapter__v_; +text: .text%__1cFStateO_sub_Op_StoreL6MpknENode__v_; +text: .text%__1cLAdapterInfoHcopy_to6Mp0_v_; +text: .text%__1cQjava_lang_StringFvalue6FpnHoopDesc__pnQtypeArrayOopDesc__; +text: .text%__1cPciObjArrayKlassGloader6M_pnHoopDesc__; +text: .text%__1cIMinINodeGadd_id6kM_pknEType__; +text: .text%__1cNdecL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cQjava_lang_StringGoffset6FpnHoopDesc__i_; +text: .text%__1cKstoreLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGThreadLnmethods_do6M_v_; +text: .text%__1cKmul_hiNodeMideal_Opcode6kM_i_; +text: .text%__1cKstoreLNodeFreloc6kM_i_; +text: .text%__1cMstoreSSPNodeLbottom_type6kM_pknEType__; +text: .text%__1cRsubI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cPsarL_rReg_2NodeMideal_Opcode6kM_i_; +text: .text%__1cTconvF2D_reg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cRmulL_rReg_immNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNmodL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cRmulL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeHtwo_adr6kM_I_; +text: .text%__1cSCompiledStaticCallSset_to_interpreted6MnMmethodHandle_pC_v_; +text: .text%__1cScompU_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRInterpreterOopMapIis_empty6M_i_; +text: .text%__1cNFingerprinterHdo_char6M_v_; +text: .text%__1cOrepush_if_args6FpnFParse_pnENode_3_v_: parse2.o; +text: .text%__1cMloadConDNodeLbottom_type6kM_pknEType__; +text: .text%__1cNGrowableArray4CpnHoopDesc__Uclear_and_deallocate6M_v_; +text: .text%__1cMrdx_RegLOperJnum_edges6kM_I_; +text: .text%__1cLMachUEPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZget_mirror_from_signature6FnMmethodHandle_pnPSignatureStream_pnGThread__pnHoopDesc__; +text: .text%__1cNGrowableArray4CpnJNode_List__Egrow6Mi_v_; +text: .text%__1cMciArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cNObjectMonitorGenter26MpnGThread__v_; +text: .text%__1cKarrayKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cIUniverseWupdate_heap_info_at_gc6F_v_; +text: .text%__1cJCodeCacheLgc_prologue6F_v_; +text: .text%__1cJCodeCacheLgc_epilogue6F_v_; +text: .text%__1cVLoaderConstraintTableYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cJMarkSweepXfollow_weak_klass_links6F_v_; +text: .text%__1cJMarkSweepMadjust_marks6F_v_; +text: .text%__1cJMarkSweepNrestore_marks6F_v_; +text: .text%__1cHThreadsLgc_epilogue6F_v_; +text: .text%__1cKDictionaryMdo_unloading6MpnRBoolObjectClosure__i_; +text: .text%__1cHThreadsLgc_prologue6F_v_; +text: .text%__1cNGrowableArray4CpnFKlass__2t6Mii_v_; +text: .text%__1cQSystemDictionaryMdo_unloading6FpnRBoolObjectClosure__i_; +text: .text%__1cQSystemDictionaryPplaceholders_do6FpnKOopClosure__v_; +text: .text%__1cSReferenceProcessorPoops_do_statics6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryYalways_strong_classes_do6FpnKOopClosure__v_; +text: .text%__1cUPSAdaptiveSizePolicyWmajor_collection_begin6M_v_; +text: .text%__1cUPSAdaptiveSizePolicyUmajor_collection_end6MLnHGCCauseFCause__v_; +text: .text%__1cQSystemDictionaryValways_strong_oops_do6FpnKOopClosure__v_; +text: .text%__1cNGrowableArray4CpnFKlass__Uclear_and_deallocate6M_v_; +text: .text%__1cKPSYoungGenHcompact6M_v_; +text: .text%__1cKPSYoungGenPadjust_pointers6M_v_; +text: .text%__1cKPSYoungGenKprecompact6M_v_; +text: .text%__1cLPSMarkSweepQinvoke_no_policy6Fpii_v_; +text: .text%__1cLPSMarkSweepPallocate_stacks6F_v_; +text: .text%__1cLPSMarkSweepRdeallocate_stacks6F_v_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase16Fi_v_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase26F_v_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase36F_v_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase46F_v_; +text: .text%__1cLPSMarkSweepbAreset_millis_since_last_gc6F_v_; +text: .text%__1cUPSMarkSweepDecoratorbHset_destination_decorator_tenured6F_v_; +text: .text%__1cUPSMarkSweepDecoratorbIset_destination_decorator_perm_gen6F_v_; +text: .text%__1cNExceptionBlobHoops_do6MpnKOopClosure__v_; +text: .text%__1cKDictionaryYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cQUncommonTrapBlobHoops_do6MpnKOopClosure__v_; +text: .text%__1cSDeoptimizationBlobHoops_do6MpnKOopClosure__v_; +text: .text%__1cIPSOldGenKprecompact6M_v_; +text: .text%__1cVLoaderConstraintTableYpurge_loader_constraints6MpnRBoolObjectClosure__v_; +text: .text%__1cRCardTableModRefBSEis_a6MnKBarrierSetEName__i_; +text: .text%__1cJPSPermGenQcompute_new_size6ML_v_; +text: .text%__1cJPSPermGenKprecompact6M_v_; +text: .text%__1cJCodeCacheMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure_i_v_; +text: .text%__1cJCodeCacheHoops_do6FpnKOopClosure__v_; +text: .text%__1cPcmpD_cc_regNodeHtwo_adr6kM_I_; +text: .text%__1cbFunnecessary_membar_volatileNodeHtwo_adr6kM_I_; +text: .text%__1cIDivINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIciSymbolHas_utf86M_pkc_; +text: .text%__1cQorI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cPfieldDescriptorSlong_initial_value6kM_x_; +text: .text%__1cKJavaThreadLnmethods_do6M_v_; +text: .text%__1cCosTnative_java_library6F_pv_; +text: .text%__1cSTailCalljmpIndNodePoper_input_base6kM_I_; +text: .text%__1cOstackSlotPOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOstackSlotPOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cScompL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cNandI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2ipnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cPciObjArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cPstoreImmI16NodeFreloc6kM_i_; +text: .text%__1cPstoreImmI16NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosYprint_jni_name_prefix_on6FpnMoutputStream_i_v_; +text: .text%__1cVlookup_special_native6Fpc_pC_: nativeLookup.o; +text: .text%jni_ReleaseStringCritical: jni.o; +text: .text%__1cMNativeLookupMlookup_style6FnMmethodHandle_pcpkciiripnGThread__pC_; +text: .text%__1cCosYprint_jni_name_suffix_on6FpnMoutputStream_i_v_; +text: .text%jni_GetStringCritical: jni.o; +text: .text%__1cSInterpreterRuntimeTnmethod_entry_point6FpnKJavaThread_pnNmethodOopDesc_pnHnmethod__pC_; +text: .text%__1cIPSOldGenOgen_size_limit6M_L_; +text: .text%__1cTI2CAdapterGeneratorSstd_verified_entry6FnMmethodHandle__pC_; +text: .text%__1cTI2CAdapterGeneratorUgenerate_i2c_adapter6FnMmethodHandle__pnKI2CAdapter__; +text: .text%__1cUPSAdaptiveSizePolicybQpromo_increment_with_supplement_aligned_up6ML_L_; +text: .text%__1cHnmethodXinterpreter_entry_point6M_pC_; +text: .text%__1cUParallelScavengeHeapOresize_old_gen6ML_v_; +text: .text%__1cUPSAdaptiveSizePolicyPpromo_increment6MLI_L_; +text: .text%__1cWandI_rReg_imm65535NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIPSOldGenGresize6ML_v_; +text: .text%__1cKConv2BNodeGOpcode6kM_i_; +text: .text%__1cObox_handleNodeHtwo_adr6kM_I_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_; +text: .text%__1cKarrayKlassXbase_create_array_klass6FrknKKlass_vtbl_inLKlassHandle_pnGThread__nQarrayKlassHandle__; +text: .text%__1cNstoreImmINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKarrayKlassbBcomplete_create_array_klass6FnQarrayKlassHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cLConvI2FNodeGOpcode6kM_i_; +text: .text%__1cNmethodOopDescVparameter_annotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cNmethodOopDescLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cNcmovI_regNodeHtwo_adr6kM_I_; +text: .text%__1cNmethodOopDescbGresolved_checked_exceptions_impl6Fp0pnGThread__nOobjArrayHandle__; +text: .text%__1cIGraphKitbKcombine_and_pop_all_exception_states6M_pnNSafePointNode__; +text: .text%__1cLRethrowNode2t6MpnENode_22222_v_; +text: .text%__1cHCompileSrethrow_exceptions6MpnIJVMState__v_; +text: .text%__1cKReflectionTget_parameter_types6FnMmethodHandle_ippnHoopDesc_pnGThread__nOobjArrayHandle__; +text: .text%__1cKReflectionTget_exception_types6FnMmethodHandle_pnGThread__nOobjArrayHandle__; +text: .text%__1cNinstanceKlassQmethod_index_for6kMpnNmethodOopDesc_pnGThread__i_; +text: .text%__1cJAssemblerEandl6MpnMRegisterImpl_i_v_; +text: .text%__1cNmethodOopDescKklass_name6kM_pnNsymbolOopDesc__; +text: .text%__1cFStateP_sub_Op_Rethrow6MpknENode__v_; +text: .text%__1cQComputeCallStackIdo_array6Mii_v_; +text: .text%__1cQsalL_rReg_CLNodePoper_input_base6kM_I_; +text: .text%__1cNdecL_rRegNodeErule6kM_I_; +text: .text%__1cLRethrowNodeJideal_reg6kM_I_; +text: .text%__1cMNativeLookupLlookup_base6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cNstoreImmINodeFreloc6kM_i_; +text: .text%__1cURethrowExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMNativeLookupNpure_jni_name6FnMmethodHandle__pc_; +text: .text%__1cIPerfLong2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability__v_; +text: .text%__1cURethrowExceptionNodeFreloc6kM_i_; +text: .text%__1cTCompareAndSwapLNode2t6MpnENode_2222_v_; +text: .text%__1cQshrI_rReg_CLNodeJnum_opnds6kM_I_; +text: .text%__1cSCompareAndSwapNode2t6MpnENode_2222_v_; +text: .text%__1cTcompareAndSwapLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTcompareAndSwapLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOLibraryCallKitRinline_unsafe_CAS6MnJBasicType__i_; +text: .text%__1cIProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateX_sub_Op_CompareAndSwapL6MpknENode__v_; +text: .text%__1cNSCMemProjNodeJideal_reg6kM_I_; +text: .text%__1cTcompareAndSwapLNodeFreloc6kM_i_; +text: .text%__1cOGenerateOopMapMmonitor_push6MnNCellTypeState__v_; +text: .text%__1cOcompP_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPsarI_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOGenerateOopMapPdo_monitorenter6Mi_v_; +text: .text%__1cMmulD_immNodeMideal_Opcode6kM_i_; +text: .text%__1cPmethodDataKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cMaddF_regNodePoper_input_base6kM_I_; +text: .text%__1cPcmpD_cc_regNodeMideal_Opcode6kM_i_; +text: .text%__1cJLoadPNodeMstore_Opcode6kM_i_; +text: .text%__1cbACallCompiledJavaDirectNodeKmethod_set6Ml_v_; +text: .text%__1cYMachCallCompiledJavaNodePret_addr_offset6M_i_; +text: .text%__1cJCMoveNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKciTypeFlowLStateVectorEtrap6MpnQciBytecodeStream_pnHciKlass_i_v_; +text: .text%__1cIDivLNodeLbottom_type6kM_pknEType__; +text: .text%__1cNobjArrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cJloadFNodeHtwo_adr6kM_I_; +text: .text%__1cNinstanceKlassYremove_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cOGenerateOopMapTmark_reachable_code6M_v_; +text: .text%__1cNobjArrayKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cOGenerateOopMapKinterp_all6M_v_; +text: .text%__1cOGenerateOopMapYrewrite_refval_conflicts6M_v_; +text: .text%__1cOGenerateOopMapRinit_basic_blocks6M_v_; +text: .text%__1cOGenerateOopMapYsetup_method_entry_state6M_v_; +text: .text%__1cFframeZinterpreter_frame_set_mdx6Ml_v_; +text: .text%__1cOGenerateOopMapbAmake_context_uninitialized6M_v_; +text: .text%__1cQorI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cOGenerateOopMapTmethodsig_to_effect6MpnNsymbolOopDesc_ipnNCellTypeState__i_; +text: .text%__1cOGenerateOopMapPinitialize_vars6M_v_; +text: .text%__1cNobjArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cOGenerateOopMapKinit_state6M_v_; +text: .text%__1cPClassFileParserbFparse_constant_pool_float_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cRmulL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cOGenerateOopMapNinitialize_bb6M_v_; +text: .text%__1cOGenerateOopMapRdo_interpretation6M_v_; +text: .text%__1cQciBytecodeStreamFtable6MnJBytecodesECode__2_; +text: .text%__1cOGenerateOopMap2t6MnMmethodHandle__v_; +text: .text%__1cOGenerateOopMapbImark_bbheaders_and_count_gc_points6M_v_; +text: .text%__1cOGenerateOopMapLcompute_map6MpnGThread__v_; +text: .text%__1cIRetTableRcompute_ret_table6MnMmethodHandle__v_; +text: .text%__1cRxorI_rReg_memNodeHtwo_adr6kM_I_; +text: .text%__1cNmulI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cFStateM_sub_Op_XorI6MpknENode__v_; +text: .text%__1cISubLNodeJideal_reg6kM_I_; +text: .text%__1cLStrCompNodeLbottom_type6kM_pknEType__; +text: .text%__1cQOopMapCacheEntryFflush6M_v_; +text: .text%__1cQOopMapCacheEntryRallocate_bit_mask6M_v_; +text: .text%__1cQOopMapCacheEntryTdeallocate_bit_mask6M_v_; +text: .text%__1cQOopMapCacheEntryEfill6MnMmethodHandle_i_v_; +text: .text%__1cRsalL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPsalL_rReg_1NodePoper_input_base6kM_I_; +text: .text%__1cOstackSlotPOperJnum_edges6kM_I_; +text: .text%__1cOPhaseIdealLoopOadd_constraint6MiipnENode_22p23_v_; +text: .text%jni_IsAssignableFrom: jni.o; +text: .text%__1cOstackSlotPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFParseWcheck_interpreter_type6MpnENode_pknEType_rpnNSafePointNode__2_; +text: .text%__1cFParseXfetch_interpreter_state6MipknEType_pnENode__5_; +text: .text%__1cObox_handleNodeJnum_opnds6kM_I_; +text: .text%__1cNaddP_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cSComputeAdapterInfoHdo_byte6M_v_; +text: .text%__1cPPerfDataManagerMcounter_name6Fpkc2_pc_; +text: .text%__1cOGenerateOopMapKpp_new_ref6MpnNCellTypeState_i_v_; +text: .text%__1cNcmovI_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cQOopMapCacheEntryIset_mask6MpnNCellTypeState_2i_v_; +text: .text%__1cOGenerateOopMapVresult_for_basicblock6Mi_v_; +text: .text%__1cTOopMapForCacheEntry2t6MnMmethodHandle_ipnQOopMapCacheEntry__v_; +text: .text%__1cPcmpD_cc_immNodeMideal_Opcode6kM_i_; +text: .text%__1cTOopMapForCacheEntryLcompute_map6MpnGThread__v_; +text: .text%__1cTOopMapForCacheEntryOreport_results6kM_i_; +text: .text%__1cTconvF2D_reg_memNodePoper_input_base6kM_I_; +text: .text%__1cNdivL_rRegNodeErule6kM_I_; +text: .text%__1cRmulL_rReg_immNodeQuse_cisc_RegMask6M_v_; +text: .text%JVM_GetCallerClass; +text: .text%__1cQsalL_rReg_CLNodeErule6kM_I_; +text: .text%__1cJloadLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNloadConP0NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXMachCallDynamicJavaNodePret_addr_offset6M_i_; +text: .text%__1cRxorI_rReg_immNodeErule6kM_I_; +text: .text%__1cZCallDynamicJavaDirectNodePcompute_padding6kMi_i_; +text: .text%__1cFParseScreate_jump_tables6MpnENode_pnLSwitchRange_4_i_; +text: .text%__1cZCallDynamicJavaDirectNodeKmethod_set6Ml_v_; +text: .text%__1cPcmovI_reg_lNodeHtwo_adr6kM_I_; +text: .text%__1cLConvD2INodeGOpcode6kM_i_; +text: .text%__1cNcmovP_regNodeMideal_Opcode6kM_i_; +text: .text%__1cTconvI2F_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cKstorePNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWPredictedCallGeneratorJis_inline6kM_i_; +text: .text%__1cUjmpLoopEnd_shortNodeJis_Branch6kM_I_; +text: .text%__1cQorI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cSInterpreterRuntimeOmultianewarray6FpnKJavaThread_pi_v_; +text: .text%__1cUjmpLoopEnd_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cFParseSjump_if_false_fork6MpnGIfNode_ii_v_; +text: .text%__1cbFloadConL_0x6666666666666667NodeMideal_Opcode6kM_i_; +text: .text%__1cJAssemblerEshll6MpnMRegisterImpl_i_v_; +text: .text%__1cOjmpLoopEndNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cUjmpLoopEnd_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPCountedLoopNodeGstride6kM_pnENode__; +text: .text%__1cHi2bNodeJnum_opnds6kM_I_; +text: .text%__1cHTypeAryFxdual6kM_pknEType__; +text: .text%__1cOMacroAssemblerKverify_oop6MpnMRegisterImpl_pkc_v_; +text: .text%__1cKstoreFNodeHtwo_adr6kM_I_; +text: .text%__1cNnegI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLencode_copy6FrnKCodeBuffer_ii_v_; +text: .text%__1cbBconvI2L_reg_reg_reg_zexNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvI2F_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cUCallNativeDirectNodeMideal_Opcode6kM_i_; +text: .text%__1cKmul_hiNodePoper_input_base6kM_I_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_pkcnGHandle_6_6_; +text: .text%__1cPcmpD_cc_regNodeMcisc_operand6kM_i_; +text: .text%__1cPICStubInterfaceKinitialize6MpnEStub_i_v_; +text: .text%__1cPICStubInterfaceRcode_size_to_size6kMi_i_; +text: .text%__1cPcmpD_cc_regNodeErule6kM_I_; +text: .text%__1cNtestU_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPSPromotionLABRunallocate_object6MpnHoopDesc__i_; +text: .text%__1cPcmpD_cc_immNodeHtwo_adr6kM_I_; +text: .text%__1cOPhaseIdealLoopJdo_unroll6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cRandL_rReg_immNodeErule6kM_I_; +text: .text%__1cNloadConP0NodeGis_Con6kM_I_; +text: .text%__1cIMulINodeKmul_opcode6kM_i_; +text: .text%__1cNdivL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cIMulINodeKadd_opcode6kM_i_; +text: .text%__1cRxorI_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPregister_native6FnLKlassHandle_nMsymbolHandle_1pCpnGThread__i_: jni.o; +text: .text%__1cTno_rax_rdx_RegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOtypeArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cTno_rax_rdx_RegLOperJnum_edges6kM_I_; +text: .text%__1cOCallNativeNodeGOpcode6kM_i_; +text: .text%__1cQsalI_rReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSobjArrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cJAssemblerDjmp6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cLRShiftLNodeJideal_reg6kM_I_; +text: .text%jni_SetBooleanField: jni.o; +text: .text%__1cVLoaderConstraintTableWfind_constrained_klass6MnMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cIModLNodeLbottom_type6kM_pknEType__; +text: .text%__1cRxorI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHMonitor2t6Mipkci_v_; +text: .text%__1cGHandle2t6MpnGThread_pnHoopDesc__v_; +text: .text%__1cQorI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cODeoptimizationVtrap_state_has_reason6Fii_i_; +text: .text%__1cOloadConL32NodeMideal_Opcode6kM_i_; +text: .text%__1cNGrowableArray4Cpv_Egrow6Mi_v_; +text: .text%jni_GetFieldID: jni.o; +text: .text%__1cNGrowableArray4Cl_Egrow6Mi_v_; +text: .text%__1cXjava_lang_ref_ReferenceOset_discovered6FpnHoopDesc_2_v_; +text: .text%__1cLResourceObj2n6FLn0APallocation_type__pv_; +text: .text%__1cFframeZinterpreter_frame_set_mdp6MpC_v_; +text: .text%__1cJLoadPNodeUdepends_only_on_test6kM_i_; +text: .text%__1cFBlockNset_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cIciObject2t6MpnHciKlass__v_; +text: .text%__1cScompL_rReg_immNodeErule6kM_I_; +text: .text%__1cQshrI_rReg_CLNodeErule6kM_I_; +text: .text%__1cNaddL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateT_sub_Op_ThreadLocal6MpknENode__v_; +text: .text%__1cVCallRuntimeDirectNodeHtwo_adr6kM_I_; +text: .text%__1cKciTypeFlowOsplit_range_at6Mi_pn0AFRange__; +text: .text%__1cQSystemDictionaryTresolve_from_stream6FnMsymbolHandle_nGHandle_2pnPClassFileStream_pnGThread__pnMklassOopDesc__; +text: .text%__1cXjvm_define_class_common6FpnHJNIEnv__pkcpnI_jobject_pkWi53pnGThread__pnH_jclass__: jvm.o; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcipnNsymbolOopDesc_4_v_; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcinMsymbolHandle_4nGHandle_6_v_; +text: .text%__1cMmulD_immNodePoper_input_base6kM_I_; +text: .text%__1cRInlineCacheBufferRic_stub_code_size6F_i_; +text: .text%__1cMmulF_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNGrowableArray4CpnKStackValue__2t6Mii_v_; +text: .text%__1cZInterpreterMacroAssemblerbAget_cache_and_index_at_bcp6MpnMRegisterImpl_2i_v_; +text: .text%__1cRandL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pCi_v_; +text: .text%__1cUandI_rReg_imm255NodeErule6kM_I_; +text: .text%__1cRmulL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPsarL_rReg_2NodePoper_input_base6kM_I_; +text: .text%__1cJAssemblerGpushaq6M_v_; +text: .text%__1cOMethodLivenessKBasicBlockFsplit6Mi_p1_; +text: .text%__1cFStateP_sub_Op_RShiftL6MpknENode__v_; +text: .text%__1cMrsi_RegPOperJnum_edges6kM_I_; +text: .text%__1cMstoreSSPNodePoper_input_base6kM_I_; +text: .text%__1cScompL_rReg_immNodePoper_input_base6kM_I_; +text: .text%__1cKCodeBufferWinsert_double_constant6Md_pC_; +text: .text%__1cPClassFileParserbJparse_classfile_signature_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNaddP_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFParseHdo_irem6M_v_; +text: .text%__1cRsarL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_2_v_; +text: .text%__1cHi2bNodeHtwo_adr6kM_I_; +text: .text%__1cPClassFileParserbBcheck_illegal_static_method6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cJAssemblerFmovsd6MnHAddress_pnRFloatRegisterImpl__v_; +text: .text%__1cTCallInterpreterNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cXMachCallInterpreterNodeWis_MachCallInterpreter6M_p0_; +text: .text%__1cFStateX_sub_Op_CallInterpreter6MpknENode__v_; +text: .text%__1cZCallInterpreterDirectNodeFreloc6kM_i_; +text: .text%__1cMStartC2INodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cZInterpreterMacroAssemblerYtest_method_data_pointer6MpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cMStartC2INodeKc2i_domain6FpknJTypeTuple__3_; +text: .text%__1cHCompilebMGenerate_Compiled_To_Interpreter_Graph6MpknITypeFunc_pC_v_; +text: .text%__1cZCallInterpreterDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFciEnvUregister_c2i_adapter6MpnIciMethod_pnJOopMapSet_pnKCodeBuffer_ii_v_; +text: .text%__1cSMachC2IcheckICNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSMachC2IEntriesNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRtestI_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHMatcherbAinterpreter_method_oop_reg6F_i_; +text: .text%__1cHMatcherXcompiler_method_oop_reg6F_i_; +text: .text%__1cIciMethodRinterpreter_entry6M_pC_; +text: .text%__1cSTailCalljmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmpD_cc_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cSTailCalljmpIndNodeGpinned6kM_i_; +text: .text%__1cSTailCalljmpIndNodeHtwo_adr6kM_I_; +text: .text%jni_SetByteArrayRegion: jni.o; +text: .text%__1cHBoxNodeGOpcode6kM_i_; +text: .text%__1cPcmpD_cc_regNodeJnum_opnds6kM_I_; +text: .text%__1cKC2IAdapter2t6MpnKCodeBuffer_iIpnJOopMapSet_i_v_; +text: .text%__1cKC2IAdapterPnew_c2i_adapter6FpnKCodeBuffer_IpnJOopMapSet_i_p0_; +text: .text%__1cKC2IAdapter2n6FLI_pv_; +text: .text%__1cJAssemblerFmovss6MnHAddress_pnRFloatRegisterImpl__v_; +text: .text%__1cIMulINodeJideal_reg6kM_I_; +text: .text%__1cKCMovePNodeGOpcode6kM_i_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pF_vc_v_; +text: .text%__1cGciType2t6MpnHciKlass__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_object6M_v_; +text: .text%__1cHciKlass2t6MpnIciSymbol_p0_v_; +text: .text%__1cFParseTjump_if_always_fork6Mii_v_; +text: .text%__1cKloadUBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMmulF_immNodePoper_input_base6kM_I_; +text: .text%__1cPcmpD_cc_immNodePoper_input_base6kM_I_; +text: .text%__1cUciInstanceKlassKlassEmake6F_p0_; +text: .text%__1cJAssemblerDhlt6M_v_; +text: .text%__1cOMacroAssemblerEstop6Mpkc_v_; +text: .text%__1cQComputeCallStackIdo_float6M_v_; +text: .text%__1cIciObjectUis_array_klass_klass6M_i_; +text: .text%__1cKciTypeFlowLStateVectorLdo_newarray6MpnQciBytecodeStream__v_; +text: .text%__1cWResolveOopMapConflictsRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cJloadFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmovI_reg_lNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciSymbolHbyte_at6Mi_i_; +text: .text%__1cNFingerprinterHdo_byte6M_v_; +text: .text%__1cENode2t6Mp0111111_v_; +text: .text%__1cIMaxINodeGadd_id6kM_pknEType__; +text: .text%__1cRCardTableModRefBSFclear6MnJMemRegion__v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cKExceptionsL_throw_args6FpnGThread_pkcinMsymbolHandle_5pnRJavaCallArguments__v_; +text: .text%__1cMmulD_immNodeErule6kM_I_; +text: .text%__1cMnegD_regNodePoper_input_base6kM_I_; +text: .text%__1cFframeVshould_be_deoptimized6kM_i_; +text: .text%__1cMaddF_regNodeMideal_Opcode6kM_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorThas_signature_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorVhas_annotations_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorbFhas_parameter_annotations_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cKReflectionPnew_constructor6FnMmethodHandle_pnGThread__pnHoopDesc__; +text: .text%__1cbDjava_lang_reflect_ConstructorPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorNset_modifiers6FpnHoopDesc_i_v_; +text: .text%JVM_DefineClassWithSource; +text: .text%__1cbDjava_lang_reflect_ConstructorZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cPciInstanceKlassNloader_handle6M_pnI_jobject__; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cPciInstanceKlassYprotection_domain_handle6M_pnI_jobject__; +text: .text%__1cbDjava_lang_reflect_ConstructorJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorGcreate6FpnGThread__nGHandle__; +text: .text%__1cKmul_hiNodeJnum_opnds6kM_I_; +text: .text%__1cKstoreFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstoreBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRxorI_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cNsubI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cJAssemblerExorl6MpnMRegisterImpl_2_v_; +text: .text%__1cHMatcherXinterpreter_arg_ptr_reg6F_i_; +text: .text%__1cINegDNodeGOpcode6kM_i_; +text: .text%__1cNdecL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPjava_lang_ClassQprimitive_mirror6FnJBasicType__pnHoopDesc__; +text: .text%__1cRsarI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cNcmovP_regNodeJnum_opnds6kM_I_; +text: .text%__1cSCompiledStaticCallNcompute_entry6FnMmethodHandle_rnOStaticCallInfo__v_; +text: .text%__1cMloadConDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOptoRuntimeVresolve_static_call_C6FpnKJavaThread__pC_; +text: .text%__1cSCompiledStaticCallIis_clean6kM_i_; +text: .text%__1cMrsi_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cTAbstractInterpreterLdeopt_entry6FnITosState_i_pC_; +text: .text%__1cRindIndexScaleOperNconstant_disp6kM_i_; +text: .text%__1cQorI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cRtestI_reg_immNodeJnum_opnds6kM_I_; +text: .text%jni_NewStringUTF: jni.o; +text: .text%__1cTAbstractInterpreterSBasicType_as_index6FnJBasicType__i_; +text: .text%__1cNtestI_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerGpush_l6MpnMRegisterImpl__v_; +text: .text%__1cRxorI_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerFpop_i6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cObox_handleNodeErule6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerIpush_ptr6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cOleaPIdxOffNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsarL_rReg_63NodePoper_input_base6kM_I_; +text: .text%__1cSCompiledStaticCallDset6MrknOStaticCallInfo__v_; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_i_v_; +text: .text%__1cHCompileRmake_vm_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cNmulI_rRegNodeErule6kM_I_; +text: .text%__1cNGrowableArray4Ci_2t6Mii_v_; +text: .text%__1cQsalL_rReg_CLNodeJnum_opnds6kM_I_; +text: .text%__1cNGrowableArray4Ci_Uclear_and_deallocate6M_v_; +text: .text%__1cPCountedLoopNode2t6MpnENode_2_v_; +text: .text%__1cSCountedLoopEndNode2t6MpnENode_2ff_v_; +text: .text%__1cJloadDNodeMideal_Opcode6kM_i_; +text: .text%__1cENodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIDivLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMmulD_regNodePoper_input_base6kM_I_; +text: .text%__1cTconvF2D_reg_memNodeJnum_opnds6kM_I_; +text: .text%__1cIModINodeJideal_reg6kM_I_; +text: .text%__1cYinternal_word_RelocationGtarget6M_pC_; +text: .text%__1cObox_handleNodeLbottom_type6kM_pknEType__; +text: .text%__1cbDreorder_based_on_method_index6FpnPobjArrayOopDesc_1ppnHoopDesc__v_: methodOop.o; +text: .text%__1cPshrL_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cUverify_byte_codes_fn6F_pv_: verifier.o; +text: .text%__1cMLinkResolverbPlinktime_resolve_interface_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%JVM_GetClassCPTypes; +text: .text%__1cQComputeCallStackHdo_byte6M_v_; +text: .text%JVM_GetClassCPEntriesCount; +text: .text%JVM_GetClassFieldsCount; +text: .text%JVM_GetClassMethodsCount; +text: .text%__1cINodeHashEgrow6M_v_; +text: .text%__1cJloadBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKmul_hiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerEnegq6MpnMRegisterImpl__v_; +text: .text%__1cNmodL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKloadUBNodeHtwo_adr6kM_I_; +text: .text%__1cRxorI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cVCallRuntimeDirectNodePoper_input_base6kM_I_; +text: .text%__1cSstring_compareNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongVariable__; +text: .text%__1cNsubL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOjmpLoopEndNodeGnegate6M_v_; +text: .text%__1cQorI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVCallRuntimeDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPsalL_rReg_1NodeErule6kM_I_; +text: .text%__1cPcmpD_cc_immNodeErule6kM_I_; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_2_v_; +text: .text%__1cbCAbstractInterpreterGeneratorVset_vtos_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cHCompileQgrow_alias_types6M_v_; +text: .text%__1cUandI_rReg_imm255NodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_CallIntMethod: jni.o; +text: .text%__1cNSharedRuntimeSfind_callee_method6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cPno_rax_RegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMrdx_RegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNmulI_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cNxorI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTconvF2D_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIUniverseWreinitialize_vtable_of6FpnFKlass_pnGThread__v_; +text: .text%__1cJLoadINodeMstore_Opcode6kM_i_; +text: .text%__1cNSharedRuntimeTreresolve_call_site6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cbFunnecessary_membar_volatileNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cENodeEgetd6kM_d_; +text: .text%__1cICmpFNodeGOpcode6kM_i_; +text: .text%__1cLOptoRuntimeThandle_wrong_method6FpnKJavaThread__pC_; +text: .text%__1cNGrowableArray4CpnOMethodLivenessKBasicBlock__Egrow6Mi_v_; +text: .text%__1cKstoreFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%JVM_SetClassSigners; +text: .text%__1cNdivL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompI_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZCallDynamicJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRandL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cOstackSlotPOperFscale6kM_i_; +text: .text%__1cHnmethodPis_dependent_on6MpnMklassOopDesc__i_; +text: .text%__1cMdecI_memNodeHtwo_adr6kM_I_; +text: .text%__1cSalign_to_page_size6FL_L_: heap.o; +text: .text%__1cNmulI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cOstackSlotPOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cPsarL_rReg_2NodeErule6kM_I_; +text: .text%__1cOPhaseIdealLoopUpeeled_dom_test_elim6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%jni_NewByteArray: jni.o; +text: .text%__1cYinternal_word_RelocationFvalue6M_pC_; +text: .text%__1cYinternal_word_RelocationWfix_relocation_at_move6Ml_v_; +text: .text%__1cFStateM_sub_Op_SubL6MpknENode__v_; +text: .text%__1cJAssemblerSemit_arith_operand6MipnMRegisterImpl_nHAddress_i_v_; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cMaddF_regNodeMcisc_operand6kM_i_; +text: .text%__1cRsubI_rReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNloadConPcNodeHtwo_adr6kM_I_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_deopt_entry_for6MnITosState_i_pC_; +text: .text%__1cKLoadPCNodeGOpcode6kM_i_; +text: .text%__1cTconvI2F_reg_regNodeMcisc_operand6kM_i_; +text: .text%__1cOstackSlotPOperEtype6kM_pknEType__; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_return_entry_for6MnITosState_i_pC_; +text: .text%__1cTconvD2I_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cMstoreSSPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNGrowableArray4Ci_Icontains6kMrki_i_; +text: .text%__1cKstoreBNodeFreloc6kM_i_; +text: .text%__1cObox_handleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMjniIdPrivateGid_for6FnTinstanceKlassHandle_i_l_; +text: .text%__1cNget_method_id6FpnHJNIEnv__pnH_jclass_pkc5ipnGThread__pnK_jmethodID__: jni.o; +text: .text%__1cQshrI_rReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_SystemTout_offset_in_bytes6F_i_; +text: .text%__1cMjniIdSupportNto_jmethod_id6FpnNmethodOopDesc__pnK_jmethodID__; +text: .text%__1cNmethodOopDescKjmethod_id6M_pnK_jmethodID__; +text: .text%__1cQjava_lang_SystemSin_offset_in_bytes6F_i_; +text: .text%__1cRandL_rReg_immNodeHtwo_adr6kM_I_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_2_v_; +text: .text%__1cbACallCompiledJavaDirectNodeFreloc6kM_i_; +text: .text%__1cIAddFNodeLbottom_type6kM_pknEType__; +text: .text%__1cUCallCompiledJavaNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cFStateY_sub_Op_CallCompiledJava6MpknENode__v_; +text: .text%__1cFciEnvUregister_i2c_adapter6MpnIciMethod_pnJOopMapSet_pnKCodeBuffer_i_v_; +text: .text%__1cbACallCompiledJavaDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMStartI2CNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cHCompilebMGenerate_Interpreter_To_Compiled_Graph6MpknITypeFunc__v_; +text: .text%__1cPciObjectFactoryPinsert_non_perm6Mrpn0ANNonPermObject_pnHoopDesc_pnIciObject__v_; +text: .text%__1cKI2CAdapter2n6FLI_pv_; +text: .text%__1cKCodeBufferVinsert_float_constant6Mf_pC_; +text: .text%__1cbACallCompiledJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKI2CAdapterPnew_i2c_adapter6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cKmul_hiNodeErule6kM_I_; +text: .text%__1cKI2CAdapter2t6MpnKCodeBuffer_pnJOopMapSet_ii_v_; +text: .text%__1cbBinitialize_itable_for_klass6FpnMklassOopDesc__v_; +text: .text%__1cFJNIidEfind6Mi_p0_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_5pnGThread__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIgenerate6ML_v_; +text: .text%__1cXSignatureHandlerLibraryOpd_set_handler6FpC_v_; +text: .text%__1cILogDNodeGOpcode6kM_i_; +text: .text%__1cXSignatureHandlerLibraryLset_handler6FpnKCodeBuffer__pC_; +text: .text%JVM_IsPrimitiveClass; +text: .text%__1cIDivDNodeGOpcode6kM_i_; +text: .text%__1cMnegD_regNodeMideal_Opcode6kM_i_; +text: .text%__1cJCMoveNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPciObjArrayKlassJmake_impl6FpnHciKlass__p0_; +text: .text%__1cQjava_lang_ThreadJis_daemon6FpnHoopDesc__i_; +text: .text%__1cJAssemblerEcmpl6MnHAddress_i_v_; +text: .text%__1cHi2bNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLimmI_24OperJnum_edges6kM_I_; +text: .text%__1cRxorI_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFTypeDJsingleton6kM_i_; +text: .text%__1cPsalI_rReg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIModLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cUPipeline_Use_Element2t6MIIIinXPipeline_Use_Cycle_Mask__v_; +text: .text%__1cTmembar_volatileNodePoper_input_base6kM_I_; +text: .text%__1cNloadConPcNodePoper_input_base6kM_I_; +text: .text%__1cXPipeline_Use_Cycle_Mask2t6MI_v_; +text: .text%__1cKCompiledICMset_to_clean6M_v_; +text: .text%__1cNdecL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cIciMethodOresolve_invoke6MpnHciKlass_2_p0_; +text: .text%__1cQChunkPoolCleanerEtask6M_v_; +text: .text%__1cICmpDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPsalL_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_MulI6MpknENode__v_; +text: .text%__1cZCallDynamicJavaDirectNodeFreloc6kM_i_; +text: .text%__1cQMachCallJavaNodeVis_MachCallStaticJava6M_pnWMachCallStaticJavaNode__; +text: .text%__1cUandI_rReg_imm255NodeJnum_opnds6kM_I_; +text: .text%__1cFStateX_sub_Op_CallDynamicJava6MpknENode__v_; +text: .text%__1cNObjectMonitorEwait6MxipnGThread__v_; +text: .text%jni_FindClass: jni.o; +text: .text%__1cKarrayKlassTallocate_arrayArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cIMinINodeJideal_reg6kM_I_; +text: .text%__1cJCMoveNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSObjectSynchronizerEwait6FnGHandle_xpnGThread__v_; +text: .text%__1cNCallGeneratorSfor_predicted_call6FpnHciKlass_p03_3_; +text: .text%__1cLTypeInstPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cTconvI2F_reg_regNodeErule6kM_I_; +text: .text%__1cWPredictedCallGeneratorKis_virtual6kM_i_; +text: .text%__1cTconvF2D_reg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cNcmovP_regNodeErule6kM_I_; +text: .text%__1cMaddF_regNodeJnum_opnds6kM_I_; +text: .text%__1cWPredictedCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%JVM_MonitorWait; +text: .text%__1cPshrL_rReg_1NodePoper_input_base6kM_I_; +text: .text%__1cMaddF_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNCallGeneratorQfor_virtual_call6FpnIciMethod__p0_; +text: .text%__1cUVirtualCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cLPSMarkSweepbAabsorb_live_data_from_eden6FpnUPSAdaptiveSizePolicy_pnKPSYoungGen_pnIPSOldGen__i_; +text: .text%__1cUPSMarkSweepDecoratorbDadvance_destination_decorator6F_v_; +text: .text%__1cNmulI_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNmulI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cSOnStackReplacementPget_osr_adapter6FnFframe_nMmethodHandle__pnKOSRAdapter__; +text: .text%__1cNGrowableArray4CpnKOSRAdapter__Hat_grow6Mirk1_1_; +text: .text%__1cPciObjArrayKlassEmake6FpnHciKlass__p0_; +text: .text%JVM_GetClassDeclaredConstructors; +text: .text%__1cRCardTableModRefBSKinvalidate6MnJMemRegion__v_; +text: .text%__1cJLoadFNodeJideal_reg6kM_I_; +text: .text%__1cJAssemblerEaddq6MpnMRegisterImpl_2_v_; +text: .text%__1cFTypeFJsingleton6kM_i_; +text: .text%__1cTconvF2D_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cMstoreSSPNodeErule6kM_I_; +text: .text%__1cOloadConL32NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMstoreSSPNodeHtwo_adr6kM_I_; +text: .text%__1cMincI_memNodeHtwo_adr6kM_I_; +text: .text%__1cKcmpOpUOperFequal6kM_i_; +text: .text%__1cTconvF2D_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cHRegMask2t6M_v_; +text: .text%__1cIGraphKitPdstore_rounding6MpnENode__2_; +text: .text%__1cNGrowableArray4Ci_2t6MpnFArena_iirki_v_; +text: .text%__1cNloadConL0NodeHsize_of6kM_I_; +text: .text%__1cQset_lwp_priority6Fiii_i_; +text: .text%__1cJCmpD3NodeGOpcode6kM_i_; +text: .text%__1cKloadUBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmpD_cc_immNodeJnum_opnds6kM_I_; +text: .text%__1cLConvL2DNodeGOpcode6kM_i_; +text: .text%__1cRmulI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerFcmovq6Mn0AJCondition_pnMRegisterImpl_3_v_; +text: .text%__1cNminI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMmulD_regNodeMideal_Opcode6kM_i_; +text: .text%__1cNminI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cFStateM_sub_Op_MinI6MpknENode__v_; +text: .text%__1cCosTset_native_priority6FpnGThread_i_nIOSReturn__; +text: .text%__1cJStubQdDueueRrequest_committed6Mi_pnEStub__; +text: .text%__1cScompL_rReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGICStubIfinalize6M_v_; +text: .text%__1cNCallGeneratorRfor_uncommon_trap6FpnIciMethod_nODeoptimizationLDeoptReason_n0CLDeoptAction__p0_; +text: .text%__1cUandI_rReg_imm255NodeHtwo_adr6kM_I_; +text: .text%__1cJStubQdDueueMremove_first6M_v_; +text: .text%__1cOPhaseIdealLoopOdo_range_check6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cUVirtualCallGeneratorKis_virtual6kM_i_; +text: .text%__1cPICStubInterfaceIfinalize6MpnEStub__v_; +text: .text%__1cZUncommonTrapCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cPICStubInterfaceEsize6kMpnEStub__i_; +text: .text%__1cJAssemblerEcmpq6MpnMRegisterImpl_2_v_; +text: .text%__1cNGrowableArray4CpnIciObject__Egrow6Mi_v_; +text: .text%__1cFStateM_sub_Op_ModI6MpknENode__v_; +text: .text%__1cNObjectMonitor2t6M_v_; +text: .text%__1cNmodI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFParseNdo_instanceof6M_v_; +text: .text%__1cPcmpD_cc_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNIdealLoopTreeXpolicy_maximally_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cTmembar_volatileNodeHtwo_adr6kM_I_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cIGraphKitOgen_instanceof6MpnENode_2_2_; +text: .text%__1cHciKlassOsuper_of_depth6MI_p0_; +text: .text%__1cJLoadDNodeGOpcode6kM_i_; +text: .text%__1cNcmovL_regNodePoper_input_base6kM_I_; +text: .text%__1cMdecI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRaddI_rReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_; +text: .text%__1cQsalL_rReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNandI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbFunnecessary_membar_volatileNodeLbottom_type6kM_pknEType__; +text: .text%__1cHMatcherXpost_store_load_barrier6FpknENode__i_; +text: .text%__1cYjava_lang_reflect_MethodThas_signature_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodbFhas_parameter_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodbChas_annotation_default_field6F_i_; +text: .text%__1cJloadDNodePoper_input_base6kM_I_; +text: .text%__1cENodeEgetf6kM_f_; +text: .text%__1cYjava_lang_reflect_MethodIset_name6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodPset_return_type6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodGcreate6FpnGThread__nGHandle__; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_; +text: .text%__1cKReflectionKnew_method6FnMmethodHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cYjava_lang_reflect_MethodVhas_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodWset_annotation_default6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cNmethodOopDescSannotation_default6kM_pnQtypeArrayOopDesc__; +text: .text%__1cYjava_lang_reflect_MethodTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cVCallRuntimeDirectNodeKmethod_set6Ml_v_; +text: .text%__1cTconvD2I_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cSTailCalljmpIndNodeJnum_opnds6kM_I_; +text: .text%__1cTconvI2D_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cQorI_rReg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cKstoreFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRaddI_mem_rRegNodePoper_input_base6kM_I_; +text: .text%__1cMmulF_immNodeErule6kM_I_; +text: .text%__1cJAssemblerGmovlpd6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cPcmpF_cc_regNodePoper_input_base6kM_I_; +text: .text%__1cNCompileBrokerTcompile_adapter_for6FnMmethodHandle_ii_pnMBasicAdapter__; +text: .text%__1cCosbBthread_local_storage_at_put6Fipv_v_; +text: .text%__1cNCompileBrokerbBwait_for_adapter_completion6FpnLCompileTask__pnMBasicAdapter__; +text: .text%__1cOjmpLoopEndNodeJis_Branch6kM_I_; +text: .text%__1cOjmpLoopEndNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cNinstanceKlassSregister_finalizer6FpnPinstanceOopDesc_pnGThread__2_; +text: .text%__1cSThreadLocalStorageNpd_set_thread6FpnGThread__v_; +text: .text%__1cKCMoveINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSThreadLocalStoragebBget_thread_via_cache_slowly6FLi_pnGThread__; +text: .text%__1cMrax_RegIOperEtype6kM_pknEType__; +text: .text%__1cOjmpLoopEndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateW_sub_Op_MemBarVolatile6MpknENode__v_; +text: .text%jni_GetMethodID: jni.o; +text: .text%__1cSThreadLocalStorageSset_thread_in_slot6FpnGThread__v_; +text: .text%get_thread; +text: .text%__1cMincI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIGraphKitXinsert_mem_bar_volatile6MpnKMemBarNode_i_v_; +text: .text%__1cSThreadLocalStorageKset_thread6FpnGThread__v_; +text: .text%__1cCosHSolarisKmmap_chunk6FpcLii_2_; +text: .text%__1cbFloadConL_0x6666666666666667NodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_MonitorNotifyAll; +text: .text%__1cOloadConL32NodeLbottom_type6kM_pknEType__; +text: .text%__1cRxorI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cMmulD_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNcmovP_regNodeLbottom_type6kM_pknEType__; +text: .text%__1cJScopeDescTdecode_scope_values6Mi_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cTAbstractInterpreterWlayout_activation_impl6FpnNmethodOopDesc_iiiipnFframe_4i_i_; +text: .text%__1cLconvI2BNodeMideal_Opcode6kM_i_; +text: .text%__1cIDivLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQsalI_rReg_CLNodeHtwo_adr6kM_I_; +text: .text%__1cPsarL_rReg_2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmodL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cScompL_rReg_immNodeJnum_opnds6kM_I_; +text: .text%__1cQshrL_rReg_CLNodePoper_input_base6kM_I_; +text: .text%__1cJCMoveNode2t6MpnENode_22pknEType__v_; +text: .text%__1cJCMoveNodeEmake6FpnENode_222pknEType__p0_; +text: .text%__1cVLoaderConstraintTableYextend_loader_constraint6MpnVLoaderConstraintEntry_nGHandle_pnMklassOopDesc__v_; +text: .text%__1cIimmIOperJnum_edges6kM_I_; +text: .text%__1cJAssemblerFmovss6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cRtestI_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRandL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNGrowableArray4CpnKciTypeFlowFBlock__Gremove6Mrk2_v_; +text: .text%__1cVLoaderConstraintTablebHensure_loader_constraint_capacity6MpnVLoaderConstraintEntry_i_v_; +text: .text%__1cPsalL_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cMsubD_regNodePoper_input_base6kM_I_; +text: .text%__1cMstoreSSPNodeJnum_opnds6kM_I_; +text: .text%__1cMnegD_regNodeHtwo_adr6kM_I_; +text: .text%__1cIBytecodeIset_code6MnJBytecodesECode__v_; +text: .text%__1cPClassFileParserXverify_unqualified_name6MpcIi_i_; +text: .text%__1cMdivD_immNodeMideal_Opcode6kM_i_; +text: .text%__1cTconvI2F_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_ThreadMis_stillborn6FpnHoopDesc__i_; +text: .text%__1cQsarL_rReg_63NodeErule6kM_I_; +text: .text%__1cRsubL_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMVirtualSpaceQuncommitted_size6kM_L_; +text: .text%__1cRsubL_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cMVirtualSpaceJexpand_by6ML_i_; +text: .text%__1cNstoreImmPNodeMideal_Opcode6kM_i_; +text: .text%__1cQjava_lang_ThreadKset_thread6FpnHoopDesc_pnKJavaThread__v_; +text: .text%__1cLOopMapCache2t6M_v_; +text: .text%__1cJloadDNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cQComputeCallStackHdo_char6M_v_; +text: .text%__1cNdivI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cOcmovI_regUNodePoper_input_base6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerEpush6MnITosState__v_; +text: .text%__1cSvframeArrayElementDbci6kM_i_; +text: .text%__1cMaddF_regNodeErule6kM_I_; +text: .text%__1cTconvF2D_reg_memNodeHtwo_adr6kM_I_; +text: .text%__1cNdecL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cMdecI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFStateN_sub_Op_LoadF6MpknENode__v_; +text: .text%__1cIMulDNodeLbottom_type6kM_pknEType__; +text: .text%__1cNaddI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerEdecl6MpnMRegisterImpl__v_; +text: .text%__1cOPhaseIdealLoopVinsert_pre_post_loops6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cJAssemblerGbswapl6MpnMRegisterImpl__v_; +text: .text%__1cRInlineCacheBufferLnew_ic_stub6F_pnGICStub__; +text: .text%__1cRInlineCacheBufferWcreate_transition_stub6FpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cIAddDNodeGOpcode6kM_i_; +text: .text%__1cMincI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIpass_int6M_v_; +text: .text%__1cNloadConPcNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGICStubIset_stub6MpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cRInlineCacheBufferXassemble_ic_buffer_code6FpCpnHoopDesc_1_v_; +text: .text%__1cTconvD2I_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerHpop_ptr6MpnMRegisterImpl__v_; +text: .text%__1cGThreadbCis_hidden_from_external_view6kM_i_; +text: .text%__1cMelapsedTimer2t6M_v_; +text: .text%__1cGThreadVis_jvmti_agent_thread6kM_i_; +text: .text%__1cMdivD_immNodeErule6kM_I_; +text: .text%__1cTconvI2D_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cFTypeFFxmeet6kMpknEType__3_; +text: .text%__1cJAssemblerEshrl6MpnMRegisterImpl_i_v_; +text: .text%__1cGICStubLdestination6kM_pC_; +text: .text%__1cRsalL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRInlineCacheBufferVic_buffer_entry_point6FpC_1_; +text: .text%__1cPcmpD_cc_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMaddD_immNodeMideal_Opcode6kM_i_; +text: .text%__1cNdivI_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cNobjArrayKlassKinitialize6MpnGThread__v_; +text: .text%jni_NewObjectArray: jni.o; +text: .text%__1cIMulFNodeLbottom_type6kM_pknEType__; +text: .text%__1cTmembar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUandI_rReg_imm255NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNdivL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cKcastPPNodePoper_input_base6kM_I_; +text: .text%__1cMaddD_immNodePoper_input_base6kM_I_; +text: .text%__1cFTypeDFxmeet6kMpknEType__3_; +text: .text%__1cKloadUBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIDivLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJloadDNodeErule6kM_I_; +text: .text%__1cRaddI_mem_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cPsarL_rReg_1NodeMideal_Opcode6kM_i_; +text: .text%__1cKCompiledICMstub_address6kM_pC_; +text: .text%__1cMmulD_regNodeMcisc_operand6kM_i_; +text: .text%__1cMmulF_memNodePoper_input_base6kM_I_; +text: .text%lwp_cond_destroy: os_solaris.o; +text: .text%__1cHnmethodNis_osr_method6kM_i_; +text: .text%lwp_mutex_destroy: os_solaris.o; +text: .text%__1cFParseScan_rerun_bytecode6M_i_; +text: .text%__1cISubFNodeGOpcode6kM_i_; +text: .text%__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cFTypeDGis_nan6kM_i_; +text: .text%__1cTconvI2F_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cOJavaAssertionsNmatch_package6Fpkc_pn0AKOptionList__; +text: .text%__1cOJavaAssertionsHenabled6Fpkci_i_; +text: .text%__1cOJavaAssertionsLmatch_class6Fpkc_pn0AKOptionList__; +text: .text%JVM_DesiredAssertionStatus; +text: .text%__1cHTypePtrFxdual6kM_pknEType__; +text: .text%__1cTconvI2F_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cQjava_lang_ThreadLthreadGroup6FpnHoopDesc__2_; +text: .text%__1cLConvI2FNodeLbottom_type6kM_pknEType__; +text: .text%__1cXjava_lang_reflect_FieldNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cMaddF_immNodeMideal_Opcode6kM_i_; +text: .text%__1cQshrL_rReg_CLNodeMideal_Opcode6kM_i_; +text: .text%__1cKReflectionInew_type6FnMsymbolHandle_nLKlassHandle_pnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_name6FpnHoopDesc_2_v_; +text: .text%__1cJvmSymbolsOsignature_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cMloadConFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNnegI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPcmpF_cc_regNodeHtwo_adr6kM_I_; +text: .text%__1cKReflectionJnew_field6FpnPfieldDescriptor_ipnGThread__pnHoopDesc__; +text: .text%__1cXjava_lang_reflect_FieldIset_type6FpnHoopDesc_2_v_; +text: .text%__1cKJavaThreadbScheck_safepoint_and_suspend_for_native_trans6Fp0_v_; +text: .text%__1cXjava_lang_reflect_FieldPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldGcreate6FpnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldVhas_annotations_field6F_i_; +text: .text%__1cISubDNodeGOpcode6kM_i_; +text: .text%__1cJloadFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSstring_compareNodeHtwo_adr6kM_I_; +text: .text%__1cPfieldDescriptorLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cRaddI_mem_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cXjava_lang_reflect_FieldIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cKScopeValueLis_location6kM_i_; +text: .text%__1cXjava_lang_reflect_FieldThas_signature_field6F_i_; +text: .text%__1cMmulF_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSObjectSynchronizerGnotify6FnGHandle_pnGThread__v_; +text: .text%JVM_MonitorNotify; +text: .text%__1cQsarL_rReg_63NodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_GetStaticFieldID: jni.o; +text: .text%__1cIModLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPjava_lang_ClassOprimitive_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cIjniIdMapGcreate6FnTinstanceKlassHandle__p0_; +text: .text%__1cPsarL_rReg_2NodeJnum_opnds6kM_I_; +text: .text%__1cKReflectionbFbasic_type_mirror_to_basic_type6FpnHoopDesc_pnGThread__nJBasicType__; +text: .text%__1cPcmpF_cc_regNodeMideal_Opcode6kM_i_; +text: .text%__1cQSystemDictionaryQjava_mirror_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cIjniIdMap2t6MpnMklassOopDesc_i_v_; +text: .text%__1cIjniIdMapRcompute_index_cnt6FnTinstanceKlassHandle__i_; +text: .text%__1cLjniIdBucket2t6MpnIjniIdMap_p0_v_; +text: .text%__1cNinstanceKlassKjni_id_for6Mi_pnFJNIid__; +text: .text%__1cJLoadSNodeMstore_Opcode6kM_i_; +text: .text%__1cLTypeInstPtrLmirror_type6kM_pnGciType__; +text: .text%__1cMsubF_regNodePoper_input_base6kM_I_; +text: .text%__1cPcmpD_cc_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMlogD_regNodeMideal_Opcode6kM_i_; +text: .text%__1cbFunnecessary_membar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvL2FNodeGOpcode6kM_i_; +text: .text%__1cFKlassNexternal_name6kM_pkc_; +text: .text%__1cMPipeline_Use2t6MIIIpnUPipeline_Use_Element__v_; +text: .text%__1cKstorePNodeErule6kM_I_; +text: .text%__1cNsymbolOopDescWas_klass_external_name6kM_pkc_; +text: .text%__1cbFunnecessary_membar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConPcNodeErule6kM_I_; +text: .text%__1cIPipeline2t6MIIiIIiiiipnSmachPipelineStages_2pInMPipeline_Use__v_; +text: .text%__1cRComputeEntryStackGdo_int6M_v_; +text: .text%__1cMstoreSSPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSMachBreakpointNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRsubL_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNtestU_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPsalL_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cNmodL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvF2D_reg_regNodeErule6kM_I_; +text: .text%__1cJAssemblerDjmp6MpnMRegisterImpl_nJrelocInfoJrelocType__v_; +text: .text%__1cObox_handleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsalI_rReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQorI_rReg_memNodeErule6kM_I_; +text: .text%__1cLloadSSDNodePoper_input_base6kM_I_; +text: .text%__1cNCompileBrokerbAinvoke_compiler_on_adapter6FpnLCompileTask__v_; +text: .text%__1cLConvF2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMaddF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRxorI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cMaddF_immNodePoper_input_base6kM_I_; +text: .text%__1cKCMoveLNodeGOpcode6kM_i_; +text: .text%__1cICodeHeapTmark_segmap_as_free6MLL_v_; +text: .text%__1cRaddL_rReg_memNodePoper_input_base6kM_I_; +text: .text%JVM_IsArrayClass; +text: .text%__1cJAssemblerEsbbq6MnHAddress_i_v_; +text: .text%__1cZInterpreterMacroAssemblerFpop_l6MpnMRegisterImpl__v_; +text: .text%__1cMmulD_regNodeJnum_opnds6kM_I_; +text: .text%__1cODeoptimizationYquery_update_method_data6FnQmethodDataHandle_in0ALDeoptReason_rIri4_pnLProfileData__; +text: .text%__1cICodeHeapJexpand_by6ML_i_; +text: .text%__1cMmulD_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cLConvF2DNodeLbottom_type6kM_pknEType__; +text: .text%__1cJAssemblerEaddq6MnHAddress_i_v_; +text: .text%JVM_GetClassName; +text: .text%__1cTconvF2D_reg_regNodeMcisc_operand6kM_i_; +text: .text%__1cLStringTableGintern6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cMmulD_immNodeJnum_opnds6kM_I_; +text: .text%__1cNmulI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQorI_rReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnPPerfLongCounter__; +text: .text%__1cNObjectMonitorGnotify6MpnGThread__v_; +text: .text%__1cQjava_lang_ThreadMset_priority6FpnHoopDesc_nOThreadPriority__v_; +text: .text%__1cRsubL_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRaddL_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cRsubL_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cRsubL_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cPshrL_rReg_1NodeErule6kM_I_; +text: .text%__1cQshrI_rReg_CLNodeHtwo_adr6kM_I_; +text: .text%__1cFStateO_sub_Op_CMoveI6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_RegD6MpknENode__v_; +text: .text%__1cQorI_rReg_memNodeHtwo_adr6kM_I_; +text: .text%__1cUCallNativeDirectNodeHtwo_adr6kM_I_; +text: .text%__1cTconvI2D_reg_regNodeMcisc_operand6kM_i_; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_; +text: .text%__1cIMaxINodeJideal_reg6kM_I_; +text: .text%__1cFJNIid2t6MpnMklassOopDesc_ip0_v_; +text: .text%__1cNinstanceKlassPjni_id_for_impl6FnTinstanceKlassHandle_i_pnFJNIid__; +text: .text%__1cJAssemblerEaddq6MpnMRegisterImpl_nHAddress__v_; +text: .text%JVM_Open; +text: .text%__1cHRegMask2t6Miiiiiii_v_; +text: .text%__1cbFloadConL_0x6666666666666667NodeHtwo_adr6kM_I_; +text: .text%__1cNsubI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMmulF_regNodePoper_input_base6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_i_v_; +text: .text%__1cQConstantIntValuePis_constant_int6kM_i_; +text: .text%__1cRmulL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPsarL_rReg_2NodeHtwo_adr6kM_I_; +text: .text%__1cKmul_hiNodeHtwo_adr6kM_I_; +text: .text%__1cQConstantIntValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cRxorI_rReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateM_sub_Op_ConD6MpknENode__v_; +text: .text%__1cLConvI2DNodeGOpcode6kM_i_; +text: .text%__1cVLoaderConstraintTableJnew_entry6MIpnNsymbolOopDesc_pnMklassOopDesc_ii_pnVLoaderConstraintEntry__; +text: .text%__1cNaddP_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPcmpF_cc_regNodeMcisc_operand6kM_i_; +text: .text%__1cJAssemblerFcmovl6Mn0AJCondition_pnMRegisterImpl_3_v_; +text: .text%__1cVscale_to_lwp_priority6Fiii_i_: os_solaris.o; +text: .text%__1cLOptoRuntimeWresolve_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cNPerfByteArray2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_i_v_; +text: .text%__1cLStrCompNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMmulF_memNodeMideal_Opcode6kM_i_; +text: .text%__1cKConv2BNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJloadDNodeJnum_opnds6kM_I_; +text: .text%__1cFStateM_sub_Op_RegF6MpknENode__v_; +text: .text%__1cMmulF_immNodeJnum_opnds6kM_I_; +text: .text%__1cOMacroAssemblerKnull_check6MpnMRegisterImpl_i_v_; +text: .text%__1cNcmovP_regNodeHtwo_adr6kM_I_; +text: .text%jni_GetStaticObjectField: jni.o; +text: .text%__1cIGraphKitSprecision_rounding6MpnENode__2_; +text: .text%__1cScompL_rReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSTailCalljmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvD2F_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cLCastP2LNodeUdepends_only_on_test6kM_i_; +text: .text%__1cTconvF2D_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulD_immNodeHtwo_adr6kM_I_; +text: .text%__1cOMacroAssemblerFleave6M_v_; +text: .text%__1cMloadConDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMTailCallNode2t6MpnENode_222222_v_; +text: .text%__1cICmpDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cXPartialSubtypeCheckNodeGOpcode6kM_i_; +text: .text%__1cSTailCalljmpIndNodeFreloc6kM_i_; +text: .text%__1cObox_handleNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cOloadConL32NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMlogD_regNodePoper_input_base6kM_I_; +text: .text%__1cTconvI2F_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cMnegD_regNodeErule6kM_I_; +text: .text%__1cLvframeArrayRregister_location6kMi_pC_; +text: .text%__1cQorI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateQ_sub_Op_TailCall6MpknENode__v_; +text: .text%__1cIOSThreadNpd_initialize6M_v_; +text: .text%__1cCosScurrent_process_id6F_i_; +text: .text%__1cMaddD_immNodeErule6kM_I_; +text: .text%__1cNmaxI_rRegNodePoper_input_base6kM_I_; +text: .text%__1cPshrL_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvI2F_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cNmaxI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIMaxINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cCosRinitialize_thread6F_v_; +text: .text%__1cCosHSolarisKvm_signals6F_pnIsigset_t__; +text: .text%__1cCosHSolarisRunblocked_signals6F_pnIsigset_t__; +text: .text%__1cMaddF_immNodeErule6kM_I_; +text: .text%__1cCosHSolarisPhotspot_sigmask6FpnGThread__v_; +text: .text%__1cFStateM_sub_Op_MaxI6MpknENode__v_; +text: .text%__1cGThread2t6M_v_; +text: .text%__1cGThreadbFinitialize_thread_local_storage6M_v_; +text: .text%__1cGThreadbArecord_stack_base_and_size6M_v_; +text: .text%__1cCosHSolarisVinit_thread_fpu_state6F_v_; +text: .text%__1cIOSThread2t6MpFpv_i1_v_; +text: .text%jni_CallStaticVoidMethod: jni.o; +text: .text%__1cCosScurrent_stack_size6F_L_; +text: .text%__1cNPhaseRegAllocHset_oop6MpknENode_i_v_; +text: .text%__1cCosScurrent_stack_base6F_pC_; +text: .text%__1cJloadFNodeFreloc6kM_i_; +text: .text%__1cCosMstart_thread6FpnGThread__v_; +text: .text%__1cKstoreFNodeFreloc6kM_i_; +text: .text%__1cKstoreLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_; +text: .text%__1cNcmovL_memNodeErule6kM_I_; +text: .text%__1cFStateO_sub_Op_StoreF6MpknENode__v_; +text: .text%__1cHnmethodbAmake_not_entrant_or_zombie6Mi_v_; +text: .text%__1cCosPpd_start_thread6FpnGThread__v_; +text: .text%__1cNcmovL_regNodeMcisc_operand6kM_i_; +text: .text%__1cCosNcreate_thread6FpnGThread_n0AKThreadType_L_i_; +text: .text%__1cLconvI2BNodePoper_input_base6kM_I_; +text: .text%__1cOGenerateOopMapMdo_checkcast6M_v_; +text: .text%JVM_SetThreadPriority; +text: .text%__1cG_start6Fpv_0_: os_solaris.o; +text: .text%__1cLOptoRuntimeMrethrow_Type6F_pknITypeFunc__; +text: .text%JVM_GetStackAccessControlContext; +text: .text%JVM_IsThreadAlive; +text: .text%__1cTconvL2D_reg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cSstring_compareNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNdivI_rRegNodeErule6kM_I_; +text: .text%__1cNdecL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOLibraryCallKitYinline_native_time_funcs6Mi_i_; +text: .text%__1cNGrowableArray4CpknEType__2t6MpnFArena_iirk2_v_; +text: .text%__1cFParseVcatch_call_exceptions6MrnYciExceptionHandlerStream__v_; +text: .text%__1cTconvL2F_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cQjava_lang_ThreadIis_alive6FpnHoopDesc__i_; +text: .text%jni_CallObjectMethod: jni.o; +text: .text%__1cJAssemblerExorq6MpnMRegisterImpl_2_v_; +text: .text%__1cNcmovL_regNodeJnum_opnds6kM_I_; +text: .text%__1cLOptoRuntimeYcurrent_time_millis_Type6F_pknITypeFunc__; +text: .text%__1cOcmovI_regUNodeMideal_Opcode6kM_i_; +text: .text%__1cNcmovL_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNObjectMonitorJnotifyAll6MpnGThread__v_; +text: .text%__1cMsubD_regNodeMideal_Opcode6kM_i_; +text: .text%__1cNloadConL0NodeFclone6kM_pnENode__; +text: .text%__1cPcmpF_cc_regNodeErule6kM_I_; +text: .text%__1cJimmL0OperFclone6kM_pnIMachOper__; +text: .text%__1cNmodI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmpF_cc_regNodeJnum_opnds6kM_I_; +text: .text%__1cPcmpF_cc_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cbFloadConL_0x6666666666666667NodePoper_input_base6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerFpop_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cTconvL2D_reg_memNodePoper_input_base6kM_I_; +text: .text%__1cLConvD2FNodeGOpcode6kM_i_; +text: .text%__1cWThreadLocalAllocBufferKinitialize6M_v_; +text: .text%__1cJloadDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEtemp6F_pnMRegisterImpl__; +text: .text%__1cMmulF_immNodeHtwo_adr6kM_I_; +text: .text%__1cQsarL_rReg_63NodeHtwo_adr6kM_I_; +text: .text%__1cQsarL_rReg_63NodeJnum_opnds6kM_I_; +text: .text%__1cQjava_lang_StringbHcreate_from_platform_depended_str6FpkcpnGThread__nGHandle__; +text: .text%__1cMsubF_regNodeMideal_Opcode6kM_i_; +text: .text%__1cTconvI2L_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWThreadLocalAllocBufferMinitial_size6F_L_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorJpass_long6M_v_; +text: .text%__1cSInterpreterRuntimeMat_safepoint6FpnKJavaThread__v_; +text: .text%__1cTconvF2I_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cRandI_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cRandI_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cCosNcommit_memory6FpcL_i_; +text: .text%__1cNdivI_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cENodeJis_MemBar6kM_pknKMemBarNode__; +text: .text%__1cNjni_functions6F_pknTJNINativeInterface___; +text: .text%__1cNThreadServiceKadd_thread6FpnKJavaThread_i_v_; +text: .text%JVM_NativePath; +text: .text%__1cKJavaThreadKinitialize6M_v_; +text: .text%__1cHThreadsDadd6FpnKJavaThread_i_v_; +text: .text%__1cGParker2t6M_v_; +text: .text%__1cOPhaseIdealLoopKdo_peeling6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cUThreadSafepointStateGcreate6FpnKJavaThread__v_; +text: .text%__1cPciInstanceKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cKJavaThreadYcreate_stack_guard_pages6M_v_; +text: .text%__1cUThreadSafepointState2t6MpnKJavaThread__v_; +text: .text%__1cCosMguard_memory6FpcL_i_; +text: .text%__1cMnegD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUCallNativeDirectNodePoper_input_base6kM_I_; +text: .text%__1cHnmethodTinc_decompile_count6M_v_; +text: .text%__1cIMinINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cMResourceMarkNreset_to_mark6M_v_; +text: .text%__1cQThreadStatistics2t6M_v_; +text: .text%__1cMFlatProfilerJis_active6F_i_; +text: .text%__1cNloadConPcNodeLbottom_type6kM_pknEType__; +text: .text%__1cMmulD_regNodeErule6kM_I_; +text: .text%__1cMdivD_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKJavaThread2t6MpFp0pnGThread__vL_v_; +text: .text%__1cPcmpD_cc_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvI2D_reg_regNodeErule6kM_I_; +text: .text%__1cQshrL_rReg_CLNodeJnum_opnds6kM_I_; +text: .text%__1cNcmovL_memNodePoper_input_base6kM_I_; +text: .text%__1cNdivL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmpD_cc_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateT_sub_Op_CallRuntime6MpknENode__v_; +text: .text%__1cXJNI_ArgumentPusherVaArgJget_float6M_v_; +text: .text%__1cKcastPPNodeHtwo_adr6kM_I_; +text: .text%__1cKarrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cMsubD_regNodeMcisc_operand6kM_i_; +text: .text%__1cGThreadFstart6Fp0_v_; +text: .text%__1cVCallRuntimeDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsalL_rReg_CLNodeHtwo_adr6kM_I_; +text: .text%__1cVCallRuntimeDirectNodeFreloc6kM_i_; +text: .text%__1cIGraphKitIset_jvms6MpnIJVMState__v_; +text: .text%__1cKJavaThreadDrun6M_v_; +text: .text%__1cTconvD2I_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cOsalI_mem_1NodePoper_input_base6kM_I_; +text: .text%__1cSMachCallNativeNodePret_addr_offset6M_i_; +text: .text%__1cMLinkResolverbEresolve_interface_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cZInterpreterMacroAssemblerFpop_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cMrdi_RegIOperEtype6kM_pknEType__; +text: .text%__1cVThreadStateTransitionKtransition6FpnKJavaThread_nPJavaThreadState_3_v_; +text: .text%__1cUCallNativeDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKJavaThreadRthread_main_inner6M_v_; +text: .text%__1cQorI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIGraphKitbAgen_stub_or_native_wrapper6MpCpkcpnIciMethod_iiiii_v_; +text: .text%__1cPsalL_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMResourceMark2t6M_v_; +text: .text%__1cQshrI_rReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJScopeDescGlocals6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cJScopeDescVdecode_monitor_values6Mi_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cSvframeArrayElementPunpack_on_stack6MiipnFframe_ii_v_; +text: .text%__1cTAbstractInterpreterRlayout_activation6FpnNmethodOopDesc_iiiipnFframe_4i_v_; +text: .text%__1cOcompiledVFrameLexpressions6kM_pnUStackValueCollection__; +text: .text%__1cOcompiledVFrameImonitors6kM_pnNGrowableArray4CpnLMonitorInfo____; +text: .text%__1cOcompiledVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cSvframeArrayElementHfill_in6MpnOcompiledVFrame__v_; +text: .text%__1cOcompiledVFrameHraw_bci6kM_i_; +text: .text%__1cNGrowableArray4CpnLMonitorInfo__2t6Mii_v_; +text: .text%__1cPPerfDataManagerWcreate_string_constant6FnJCounterNS_pkc3pnGThread__pnSPerfStringConstant__; +text: .text%__1cOMacroAssemblerKincrementl6MpnMRegisterImpl_i_v_; +text: .text%__1cFframebCinterpreter_frame_set_locals6Mpl_v_; +text: .text%__1cFframebHinterpreter_frame_set_monitor_end6MpnPBasicObjectLock__v_; +text: .text%__1cTAbstractInterpreterPsize_activation6FpnNmethodOopDesc_iiiii_i_; +text: .text%__1cSPerfStringConstant2t6MnJCounterNS_pkc3_v_; +text: .text%__1cTAbstractInterpreterQcontinuation_for6FpnNmethodOopDesc_pCiiri_3_; +text: .text%__1cZInterpreterMacroAssemblerLcall_VM_Ico6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cFframebCinterpreter_frame_set_method6MpnNmethodOopDesc__v_; +text: .text%__1cMmulF_regNodeMideal_Opcode6kM_i_; +text: .text%__1cFframebBinterpreter_frame_sender_sp6kM_pl_; +text: .text%__1cMaddF_regNodeHtwo_adr6kM_I_; +text: .text%__1cKCompiledICOset_cached_oop6MpnHoopDesc__v_; +text: .text%__1cKstoreINodeErule6kM_I_; +text: .text%__1cJScopeDescImonitors6M_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_i_v_; +text: .text%__1cOcompiledVFrameGlocals6kM_pnUStackValueCollection__; +text: .text%__1cJScopeDescLexpressions6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cTconvF2D_reg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cSvframeArrayElementNon_stack_size6kMiiii_i_; +text: .text%__1cMaddD_regNodePoper_input_base6kM_I_; +text: .text%__1cXjava_lang_boxing_objectJget_value6FpnHoopDesc_pnGjvalue__nJBasicType__; +text: .text%__1cMorL_rRegNodePoper_input_base6kM_I_; +text: .text%__1cOcmovD_regUNodePoper_input_base6kM_I_; +text: .text%__1cPcmovI_reg_gNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMdivD_immNodePoper_input_base6kM_I_; +text: .text%__1cJloadDNodeHtwo_adr6kM_I_; +text: .text%__1cKReflectionTunbox_for_primitive6FpnHoopDesc_pnGjvalue_pnGThread__nJBasicType__; +text: .text%__1cCosMset_priority6FpnGThread_nOThreadPriority__nIOSReturn__; +text: .text%__1cMmulF_memNodeJnum_opnds6kM_I_; +text: .text%__1cIMulDNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cODeoptimizationVtrap_state_add_reason6Fii_i_; +text: .text%__1cDhpiFclose6Fi_i_; +text: .text%__1cJMemRegionFminus6kM0_0_; +text: .text%__1cMmulD_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_i_v_; +text: .text%__1cNcmovL_regNodeMideal_Opcode6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerWupdate_mdp_by_constant6MpnMRegisterImpl_i_v_; +text: .text%__1cOtailjmpIndNodeNis_block_proj6kM_pknENode__; +text: .text%__1cRaddL_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cTconvI2F_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvL2F_reg_regNodeMideal_Opcode6kM_i_; +text: .text%__1cGICStubKcached_oop6kM_pnHoopDesc__; +text: .text%__1cRInlineCacheBufferUic_buffer_cached_oop6FpC_pnHoopDesc__; +text: .text%__1cTconvD2F_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cJAssemblerFpushq6Mi_v_; +text: .text%JVM_Close; +text: .text%__1cMnegF_regNodePoper_input_base6kM_I_; +text: .text%__1cOcmovI_regUNodeJnum_opnds6kM_I_; +text: .text%__1cbCAbstractInterpreterGeneratorRset_unimplemented6Mi_v_; +text: .text%__1cRComputeEntryStackJdo_object6Mii_v_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cOcmovI_regUNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cGThreadMset_priority6Fp0nOThreadPriority__v_; +text: .text%jni_NewObjectV: jni.o; +text: .text%__1cKConv2BNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQshrL_rReg_CLNodeErule6kM_I_; +text: .text%__1cTconvF2D_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHnmethodbCcan_not_entrant_be_converted6M_i_; +text: .text%__1cNSafePointNodeQpeek_monitor_obj6kM_pnENode__; +text: .text%__1cOcmovI_regUNodeMcisc_operand6kM_i_; +text: .text%__1cNSafePointNodeQpeek_monitor_box6kM_pnENode__; +text: .text%__1cJLoadBNodeMstore_Opcode6kM_i_; +text: .text%__1cVCompressedWriteStreamKwrite_long6Mx_v_; +text: .text%__1cTconvF2I_reg_regNodePoper_input_base6kM_I_; +text: .text%__1cLConvF2INodeGOpcode6kM_i_; +text: .text%__1cPMultiBranchDataScompute_cell_count6FpnOBytecodeStream__i_; +text: .text%__1cFParsePdo_monitor_exit6M_v_; +text: .text%__1cMStubCodeMark2T6M_v_; +text: .text%__1cMStubCodeMark2t6MpnRStubCodeGenerator_pkc4_v_; +text: .text%__1cRStubCodeGeneratorLstub_epilog6MpnMStubCodeDesc__v_; +text: .text%__1cRStubCodeGeneratorLstub_prolog6MpnMStubCodeDesc__v_; +text: .text%__1cKcastPPNodeErule6kM_I_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pF3_v3_v_; +text: .text%__1cOsalI_mem_1NodeJnum_opnds6kM_I_; +text: .text%__1cKJavaThreadbFdeoptimized_wrt_marked_nmethods6M_v_; +text: .text%__1cKJavaThreadHprepare6MpnI_jobject_nOThreadPriority__v_; +text: .text%__1cPshrL_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cRandI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cRandI_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cQorI_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cJAssemblerGmovslq6MpnMRegisterImpl_2_v_; +text: .text%__1cRandI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQjava_lang_ThreadJstackSize6FpnHoopDesc__x_; +text: .text%__1cRConstantLongValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%JVM_StartThread; +text: .text%__1cMthread_entry6FpnKJavaThread_pnGThread__v_: jvm.o; +text: .text%__1cTconvF2D_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMsubD_regNodeErule6kM_I_; +text: .text%__1cQjava_lang_ThreadIpriority6FpnHoopDesc__nOThreadPriority__; +text: .text%__1cNmulI_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cIMulFNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cLRuntimeStub2n6FLI_pv_; +text: .text%__1cLRuntimeStubQnew_runtime_stub6FpkcpnKCodeBuffer_ipnJOopMapSet_i_p0_; +text: .text%__1cLRuntimeStub2t6MpkcpnKCodeBuffer_iipnJOopMapSet_i_v_; +text: .text%__1cTconvF2D_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cRxorI_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMmulF_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOLibraryCallKitbDis_method_invoke_or_aux_frame6MpnIJVMState__i_; +text: .text%__1cbFloadConL_0x6666666666666667NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAddFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOloadConL32NodeHsize_of6kM_I_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFn0AJOperation__v4_v_; +text: .text%__1cRaddL_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivLNodeJideal_reg6kM_I_; +text: .text%__1cGICStubFclear6M_v_; +text: .text%__1cTconvI2D_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMsubD_regNodeJnum_opnds6kM_I_; +text: .text%__1cMsubD_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cHCompileWget_MethodAccessorImpl6M_pnPciInstanceKlass__; +text: .text%__1cHCompileRget_Method_invoke6M_pnIciMethod__; +text: .text%__1cNdecI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFTypeFFxdual6kM_pknEType__; +text: .text%__1cTconvL2D_reg_memNodeJnum_opnds6kM_I_; +text: .text%__1cTconvI2D_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cTunsafe_intrinsic_id6FpnNsymbolOopDesc_1_nMvmIntrinsicsCID__; +text: .text%__1cPPerfDataManagerUcreate_long_constant6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongConstant__; +text: .text%__1cFStateM_sub_Op_ConF6MpknENode__v_; +text: .text%__1cMloadConFNodeHsize_of6kM_I_; +text: .text%__1cPsarL_rReg_2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsarL_rReg_63NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPoldgetTimeNanos6F_x_; +text: .text%__1cPno_rax_RegLOperFclone6kM_pnIMachOper__; +text: .text%__1cTAbstractInterpreterMreturn_entry6FnITosState_i_pC_; +text: .text%__1cPsarL_rReg_1NodePoper_input_base6kM_I_; +text: .text%__1cMnegD_regNodeJnum_opnds6kM_I_; +text: .text%__1cKmul_hiNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerEjccb6Mn0AJCondition_rnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cNcmovP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHMonitor2T6M_v_; +text: .text%__1cINegDNodeLbottom_type6kM_pknEType__; +text: .text%__1cLvframeArrayIallocate6FpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pnLRegisterMap_nFframe_9A9A9A_p0_; +text: .text%__1cNThreadServiceNremove_thread6FpnKJavaThread_i_v_; +text: .text%__1cQjava_lang_ThreadbGinherited_access_control_context6FpnHoopDesc__2_; +text: .text%__1cODeoptimizationScreate_vframeArray6FpnKJavaThread_nFframe_pnLRegisterMap__pnLvframeArray__; +text: .text%__1cHThreadsGremove6FpnKJavaThread__v_; +text: .text%__1cODeoptimizationTuncommon_trap_inner6FpnKJavaThread_i_v_; +text: .text%__1cODeoptimizationPget_method_data6FpnKJavaThread_nMmethodHandle_i_pnRmethodDataOopDesc__; +text: .text%__1cLensure_join6FpnKJavaThread__v_: thread.o; +text: .text%__1cIOSThread2T6M_v_; +text: .text%__1cODeoptimizationNuncommon_trap6FpnKJavaThread_i_pn0ALUnrollBlock__; +text: .text%__1cTconvI2D_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationRgather_statistics6Fn0ALDeoptReason_n0ALDeoptAction_nJBytecodesECode__v_; +text: .text%__1cUThreadSafepointStateHdestroy6FpnKJavaThread__v_; +text: .text%__1cIGraphKitTdprecision_rounding6MpnENode__2_; +text: .text%__1cNGrowableArray4CpnOcompiledVFrame__2t6Mii_v_; +text: .text%__1cOcmovI_regUNodeErule6kM_I_; +text: .text%__1cKJavaThreadEexit6Mi_v_; +text: .text%__1cGParker2T6M_v_; +text: .text%__1cCosLfree_thread6FpnIOSThread__v_; +text: .text%__1cXpartialSubtypeCheckNodeMideal_Opcode6kM_i_; +text: .text%JVM_GetInheritedAccessControlContext; +text: .text%__1cNThreadServiceWcurrent_thread_exiting6FpnKJavaThread__v_; +text: .text%__1cLvframeArrayZdeallocate_monitor_chunks6M_v_; +text: .text%__1cGThread2T5B6M_v_; +text: .text%__1cIAddDNodeLbottom_type6kM_pknEType__; +text: .text%__1cKJavaThread2T6M_v_; +text: .text%__1cQjava_lang_ThreadNset_stillborn6FpnHoopDesc__v_; +text: .text%__1cMmulD_memNodePoper_input_base6kM_I_; +text: .text%__1cRInlineCacheBufferSic_destination_for6FpnKCompiledIC__pC_; +text: .text%__1cLvframeArrayPunpack_to_stack6MrnFframe_i_v_; +text: .text%__1cOcompL_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cODeoptimizationLUnrollBlock2t6MiiiiiplppCnJBasicType__v_; +text: .text%__1cLvframeArrayHfill_in6MpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pknLRegisterMap_i_v_; +text: .text%__SLIP.DELETER__A: thread.o; +text: .text%__1cbIjava_security_AccessControlContextGcreate6FnOobjArrayHandle_inGHandle_pnGThread__pnHoopDesc__; +text: .text%__1cIOSThreadKpd_destroy6M_v_; +text: .text%__1cMaddF_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationYfetch_unroll_info_helper6FpnKJavaThread__pn0ALUnrollBlock__; +text: .text%__1cODeoptimizationRlast_frame_adjust6Fii_i_; +text: .text%__1cKJavaThreadYremove_stack_guard_pages6M_v_; +text: .text%__1cODeoptimizationNunpack_frames6FpnKJavaThread_i_nJBasicType__; +text: .text%__1cNnmethodLocker2t6MpC_v_; +text: .text%__1cTconvD2I_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cOtailjmpIndNodeMideal_Opcode6kM_i_; +text: .text%__1cLconvI2BNodeErule6kM_I_; +text: .text%__1cTconvF2I_reg_regNodeErule6kM_I_; +text: .text%__1cIciMethodVget_osr_flow_analysis6Mi_pnKciTypeFlow__; +text: .text%__1cSCallLeafDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQAbstractCompilerMsupports_osr6M_i_; +text: .text%__1cRaddL_mem_rRegNodePoper_input_base6kM_I_; +text: .text%__1cSCallLeafDirectNodeJnum_opnds6kM_I_; +text: .text%__1cXjava_lang_reflect_FieldJmodifiers6FpnHoopDesc__i_; +text: .text%__1cMmulL_memNodePoper_input_base6kM_I_; +text: .text%__1cODeoptimizationLUnrollBlock2T6M_v_; +text: .text%__1cHciKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cMaddF_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cSCompiledStaticCallMset_to_clean6M_v_; +text: .text%__1cMincL_memNodePoper_input_base6kM_I_; +text: .text%jni_NewDirectByteBuffer; +text: .text%__1cMmulL_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMaddD_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHJNIEnv_JNewObject6MpnH_jclass_pnK_jmethodID_E_pnI_jobject__; +text: .text%__1cJAssemblerEmovb6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cJSubFPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_only6MnITosState__v_; +text: .text%__1cRcmpFastUnlockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKloadUBNodeFreloc6kM_i_; +text: .text%__1cMStartOSRNodeScalling_convention6kMpnLOptoRegPair_I_v_; +text: .text%__1cMStartOSRNodeKosr_domain6F_pknJTypeTuple__; +text: .text%__1cMloadConPNodeGis_Con6kM_I_; +text: .text%__1cMmulD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXjava_lang_reflect_FieldFclazz6FpnHoopDesc__2_; +text: .text%__1cOPSVirtualSpaceJexpand_by6ML_i_; +text: .text%__1cNCallGeneratorHfor_osr6FpnIciMethod_i_p0_; +text: .text%__1cFParseWload_interpreter_state6MpnENode_2_v_; +text: .text%__1cKstoreINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOstackSlotDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%jni_GetFloatArrayRegion: jni.o; +text: .text%__1cPfieldDescriptorUdouble_initial_value6kM_d_; +text: .text%__1cKEntryPoint2t6M_v_; +text: .text%__1cJAssemblerEsarl6MpnMRegisterImpl_i_v_; +text: .text%__1cSCardTableExtensionbEresize_covered_region_by_start6MnJMemRegion__v_; +text: .text%__1cQshrL_rReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRaddL_mem_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cIAddFNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMlogD_regNodeErule6kM_I_; +text: .text%__1cXpartialSubtypeCheckNodePoper_input_base6kM_I_; +text: .text%__1cNmulI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cMdecI_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsalL_rReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKemit_break6FrnKCodeBuffer__v_; +text: .text%jni_GetStaticMethodID: jni.o; +text: .text%__1cOstackSlotDOperJnum_edges6kM_I_; +text: .text%__1cMsubF_regNodeMcisc_operand6kM_i_; +text: .text%__1cMdecI_memNodeFreloc6kM_i_; +text: .text%__1cMdecI_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRCardTableModRefBSbCfind_covering_region_by_base6MpnIHeapWord__i_; +text: .text%__1cRCardTableModRefBSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cINegFNodeGOpcode6kM_i_; +text: .text%__1cRCardTableModRefBSbAlargest_prev_committed_end6kMi_pnIHeapWord__; +text: .text%__1cLloadSSDNodeJnum_opnds6kM_I_; +text: .text%__1cSMachBreakpointNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCardTableExtensionVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cLconvI2BNodeJnum_opnds6kM_I_; +text: .text%__1cNstoreImmPNodePoper_input_base6kM_I_; +text: .text%__1cKReflectionUarray_component_type6FpnHoopDesc_pnGThread__2_; +text: .text%__1cKoopFactoryUnew_compiledICHolder6FnMmethodHandle_nLKlassHandle_pnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cHCompile2t6MpnFciEnv_pF_pknITypeFunc_pCpkciiii_v_; +text: .text%__1cTconvL2F_reg_regNodeMcisc_operand6kM_i_; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_; +text: .text%__1cNloadConPcNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKarrayKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cFStateM_sub_Op_CmpD6MpknENode__v_; +text: .text%__1cNloadConL0NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cUCallNativeDirectNodeKmethod_set6Ml_v_; +text: .text%__1cKcastPPNodeMideal_Opcode6kM_i_; +text: .text%__1cNcmovL_memNodeJnum_opnds6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_taken_branch6MpnMRegisterImpl_2_v_; +text: .text%__1cPshrL_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cIGraphKitIgen_stub6MpCpkciii_v_; +text: .text%__1cbDcatch_cleanup_find_cloned_def6FpnFBlock_pnENode_1rnLBlock_Array_i_3_: lcm.o; +text: .text%__1cVcompiledICHolderKlassIallocate6MpnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cTC2IAdapterGeneratorUmkh_unverified_entry6FnMmethodHandle__pC_; +text: .text%__1cRaddL_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cLOptoRuntimeNgenerate_stub6FpnFciEnv_pF_pknITypeFunc_pCpkciiii_8_; +text: .text%__1cISubDNodeLbottom_type6kM_pknEType__; +text: .text%__1cbCcatch_cleanup_fix_all_inputs6FpnENode_11_v_: lcm.o; +text: .text%__1cISubFNodeLbottom_type6kM_pknEType__; +text: .text%__1cNdivI_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableGbranch6Fii_v_; +text: .text%__1cNstoreImmPNodeHtwo_adr6kM_I_; +text: .text%__1cLOptoRuntimeRnew_objArray_Type6F_pknITypeFunc__; +text: .text%JVM_GetComponentType; +text: .text%__1cIMulDNodeJideal_reg6kM_I_; +text: .text%__1cTconvF2D_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cJAssemblerEsbbq6MpnMRegisterImpl_i_v_; +text: .text%__1cNcmovL_memNodeMideal_Opcode6kM_i_; +text: .text%jni_GetStringRegion: jni.o; +text: .text%jni_EnsureLocalCapacity: jni.o; +text: .text%__1cLloadSSDNodeHtwo_adr6kM_I_; +text: .text%__1cMaddF_memNodePoper_input_base6kM_I_; +text: .text%__1cFParseMdo_anewarray6M_v_; +text: .text%__1cLConvI2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLconvI2BNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC22i_v_; +text: .text%__1cHThreadsYis_supported_jni_version6Fi_C_; +text: .text%__1cMincL_memNodeJnum_opnds6kM_I_; +text: .text%__1cRandL_rReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKarrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cRaddL_mem_rRegNodeJnum_opnds6kM_I_; +text: .text%JVM_NewArray; +text: .text%JVM_FreeMemory; +text: .text%JVM_TotalMemory; +text: .text%__1cMaddD_immNodeJnum_opnds6kM_I_; +text: .text%__1cMsubF_regNodeJnum_opnds6kM_I_; +text: .text%__1cLloadSSINodePoper_input_base6kM_I_; +text: .text%__1cNinstanceKlassPadd_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cMincI_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReflectionRreflect_new_array6FpnHoopDesc_ipnGThread__pnMarrayOopDesc__; +text: .text%__1cMsubF_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMmulF_memNodeErule6kM_I_; +text: .text%__1cODeoptimizationbJupdate_method_data_from_interpreter6FnQmethodDataHandle_ii_v_; +text: .text%__1cLClassLoaderSget_system_package6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cMTailJumpNodeKmatch_edge6kMI_I_; +text: .text%__1cFStateL_sub_Op_Box6MpknENode__v_; +text: .text%__1cRaddL_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTconvL2F_reg_regNodeErule6kM_I_; +text: .text%__1cKPSYoungGenLpost_resize6M_v_; +text: .text%__1cNcmovL_regNodeErule6kM_I_; +text: .text%__1cOcmovD_regUNodeJnum_opnds6kM_I_; +text: .text%__1cRandI_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeHtwo_adr6kM_I_; +text: .text%__1cTAbstractInterpreterRTosState_as_index6FnITosState__i_; +text: .text%__1cHThreadsbMis_supported_jni_version_including_1_16Fi_C_; +text: .text%__1cKstoreBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPBytecode_invokeLresult_type6kMpnGThread__nJBasicType__; +text: .text%__1cMincL_memNodeMideal_Opcode6kM_i_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJloadCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosPuncommit_memory6FpcL_i_; +text: .text%__1cSInterpreterRuntimeJnote_trap6FpnKJavaThread_ipnGThread__v_; +text: .text%__1cRSignatureIteratorHiterate6M_v_; +text: .text%__1cIModLNodeJideal_reg6kM_I_; +text: .text%__1cNTemplateTableOpatch_bytecode6FnJBytecodesECode_pnMRegisterImpl_4i_v_; +text: .text%__1cLConvD2INodeLbottom_type6kM_pknEType__; +text: .text%__1cMaddF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHBoxNodeLbottom_type6kM_pknEType__; +text: .text%__1cFStateM_sub_Op_DivL6MpknENode__v_; +text: .text%__1cTconvL2D_reg_memNodeErule6kM_I_; +text: .text%JVM_GetSystemPackage; +text: .text%__1cCosNcommit_memory6FpcLL_i_; +text: .text%__1cOMacroAssemblerFenter6M_v_; +text: .text%__1cLConvF2DNodeJideal_reg6kM_I_; +text: .text%__1cNTemplateTableLindex_check6FpnMRegisterImpl_2_v_; +text: .text%__1cKNativeJumpUpatch_verified_entry6FpC11_v_; +text: .text%__1cLMoveL2DNodeGOpcode6kM_i_; +text: .text%__1cMincI_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateP_sub_Op_ConvF2D6MpknENode__v_; +text: .text%__1cMmulL_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%Unsafe_DefineClass1; +text: .text%__1cSUnsafe_DefineClass6FpnHJNIEnv__pnI_jstring_pnL_jbyteArray_iipnI_jobject_7_pnH_jclass__: unsafe.o; +text: .text%__1cFTypeDFxdual6kM_pknEType__; +text: .text%__1cMincI_memNodeFreloc6kM_i_; +text: .text%__1cPcmpF_cc_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMsubF_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMsubF_memNodePoper_input_base6kM_I_; +text: .text%__1cTconvF2D_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%JVM_DefineClass; +text: .text%__1cMaddF_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMmulL_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cMmulL_memNodeJnum_opnds6kM_I_; +text: .text%__1cJAssemblerEshrq6MpnMRegisterImpl_i_v_; +text: .text%__1cTC2IAdapterGeneratorLadapter_for6FnMmethodHandle__pnKC2IAdapter__; +text: .text%__1cPcmpFastLockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerQtest_mdp_data_at6MpnMRegisterImpl_i2rnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerYprofile_not_taken_branch6MpnMRegisterImpl__v_; +text: .text%__1cTleaPIdxScaleOffNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cYjava_lang_reflect_MethodFclazz6FpnHoopDesc__2_; +text: .text%__1cYjava_lang_reflect_MethodEslot6FpnHoopDesc__i_; +text: .text%__1cYjava_lang_reflect_MethodLreturn_type6FpnHoopDesc__2_; +text: .text%__1cYjava_lang_reflect_MethodPparameter_types6FpnHoopDesc__2_; +text: .text%__1cNloadConL0NodeGis_Con6kM_I_; +text: .text%__1cMset_property6FnGHandle_pkc2pnGThread__v_: jvm.o; +text: .text%JVM_GetCPFieldModifiers; +text: .text%JVM_InvokeMethod; +text: .text%__1cFKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cZcatch_cleanup_inter_block6FpnENode_pnFBlock_13rnLBlock_Array_i_v_: lcm.o; +text: .text%__1cOsalI_mem_1NodeMideal_Opcode6kM_i_; +text: .text%__1cMaddF_immNodeJnum_opnds6kM_I_; +text: .text%__1cMsubD_immNodePoper_input_base6kM_I_; +text: .text%__1cMmulF_regNodeMcisc_operand6kM_i_; +text: .text%__1cMmulF_regNodeJnum_opnds6kM_I_; +text: .text%__1cMmulF_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cMmulD_regNodeHtwo_adr6kM_I_; +text: .text%__1cTconvD2F_reg_regNodeMcisc_operand6kM_i_; +text: .text%jni_AllocObject: jni.o; +text: .text%__1cCosHSolarisOset_mpss_range6FpcLL_i_; +text: .text%__1cTconvF2I_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFParseOdo_tableswitch6M_v_; +text: .text%__1cTmembar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReflectionNinvoke_method6FpnHoopDesc_nGHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cMrdx_RegLOperFclone6kM_pnIMachOper__; +text: .text%__1cICmpFNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFn0AJCondition__v4_v_; +text: .text%__1cFj_not6FnNTemplateTableJCondition__nJAssemblerJCondition__: templateTable_amd64.o; +text: .text%__1cNTemplateTableMlocals_index6FpnMRegisterImpl_i_v_; +text: .text%__1cTconvF2D_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMmulF_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%Unsafe_AllocateInstance; +text: .text%__1cZInterpreterMacroAssemblerNunlock_object6MpnMRegisterImpl__v_; +text: .text%__1cOcmovD_regUNodeMideal_Opcode6kM_i_; +text: .text%__1cIciObjectMis_classless6kM_i_; +text: .text%__1cMsubD_immNodeMideal_Opcode6kM_i_; +text: .text%__1cRInlineCacheBufferOinit_next_stub6F_v_; +text: .text%__1cPshrL_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_immNodeErule6kM_I_; +text: .text%__1cHTypePtrKadd_offset6kMi_pk0_; +text: .text%__1cNTemplateTableHconvert6F_v_; +text: .text%__1cMnegD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQorI_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLMoveF2INodeGOpcode6kM_i_; +text: .text%__1cNcmovL_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_2_v_; +text: .text%__1cOcmovD_regUNodeErule6kM_I_; +text: .text%__1cKConv2BNodeLbottom_type6kM_pknEType__; +text: .text%__1cHnmethodSflush_dependencies6MpnRBoolObjectClosure__v_; +text: .text%__1cMorL_rRegNodeMideal_Opcode6kM_i_; +text: .text%__1cTconvD2I_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cScompL_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cXpartialSubtypeCheckNodeErule6kM_I_; +text: .text%__1cOstackSlotDOperEtype6kM_pknEType__; +text: .text%__1cHThreadsLnmethods_do6F_v_; +text: .text%__1cLloadSSDNodeErule6kM_I_; +text: .text%__1cMsubD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRComputeEntryStackIdo_short6M_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorCto6F_pnMRegisterImpl__; +text: .text%__1cTconvF2D_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulL_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPciObjectFactoryTget_unloaded_method6MpnPciInstanceKlass_pnIciSymbol_4_pnIciMethod__; +text: .text%__1cLTypeInstPtrOxmeet_unloaded6kMpk0_2_; +text: .text%__1cMloadConFNodeKconst_size6kM_i_; +text: .text%__1cMorL_rRegNodeMcisc_operand6kM_i_; +text: .text%__1cMmulD_memNodeMideal_Opcode6kM_i_; +text: .text%__1cMaddD_regNodeMideal_Opcode6kM_i_; +text: .text%__1cTconvI2D_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMnegF_regNodeMideal_Opcode6kM_i_; +text: .text%__1cMloadConFNodeFreloc6kM_i_; +text: .text%__1cILogDNodeLbottom_type6kM_pknEType__; +text: .text%__1cLConvI2DNodeLbottom_type6kM_pknEType__; +text: .text%__1cNstoreImmPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLStrCompNodeJideal_reg6kM_I_; +text: .text%__1cMlogD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFVTuneOdelete_nmethod6FpnHnmethod__v_; +text: .text%__1cMaddD_regNodeMcisc_operand6kM_i_; +text: .text%__1cMaddD_regNodeErule6kM_I_; +text: .text%__1cScompL_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cIAddFNodeJideal_reg6kM_I_; +text: .text%__1cJimmP0OperPconstant_is_oop6kM_i_; +text: .text%__1cJimmP0OperIconstant6kM_l_; +text: .text%__1cNciMethodKlassEmake6F_p0_; +text: .text%__1cETypeJis_finite6kM_i_; +text: .text%__1cHnmethodFflush6M_v_; +text: .text%JVM_GetClassContext; +text: .text%__1cIciObjectTis_type_array_klass6M_i_; +text: .text%__1cNsubL_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cIregFOperFclone6kM_pnIMachOper__; +text: .text%__1cRfind_field_offset6FpnI_jobject_ipnGThread__i_; +text: .text%__1cHBoxNodeJideal_reg6kM_I_; +text: .text%__1cXPartialSubtypeCheckNodeLbottom_type6kM_pknEType__; +text: .text%__1cIciMethod2t6MpnPciInstanceKlass_pnIciSymbol_4_v_; +text: .text%__1cPfieldDescriptorTfloat_initial_value6kM_f_; +text: .text%__1cLloadSSDNodeMideal_Opcode6kM_i_; +text: .text%__1cMsubF_regNodeErule6kM_I_; +text: .text%__1cRsubL_rReg_memNodeFreloc6kM_i_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_pnNsymbolOopDesc_pkc_nGHandle__; +text: .text%__1cTconvL2F_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMmulF_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStubQdDueueKremove_all6M_v_; +text: .text%__1cIDivDNodeLbottom_type6kM_pknEType__; +text: .text%__1cLStatSamplerTget_system_property6FpkcpnGThread__2_; +text: .text%__1cRmethodDataOopDescRbci_to_extra_data6Mii_pnLProfileData__; +text: .text%__1cICodeBlobFflush6M_v_; +text: .text%__1cVMoveF2I_reg_stackNodeMideal_Opcode6kM_i_; +text: .text%__1cNmodL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJArgumentsQPropertyList_add6FppnOSystemProperty_2_v_; +text: .text%__1cKstoreCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmembar_releaseNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cQsalI_rReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSInterpreterRuntimeQcreate_exception6FpnKJavaThread_pc3_v_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_2_v_; +text: .text%__1cJStubQdDueueMremove_first6Mi_v_; +text: .text%__1cQinitialize_class6FnMsymbolHandle_pnGThread__v_: thread.o; +text: .text%__1cJAssemblerFcmovq6Mn0AJCondition_pnMRegisterImpl_nHAddress__v_; +text: .text%__1cXpartialSubtypeCheckNodeJnum_opnds6kM_I_; +text: .text%__1cMmulD_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMaddF_immNodeHtwo_adr6kM_I_; +text: .text%__1cIMulDNodeGmul_id6kM_pknEType__; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_method_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cTconvL2D_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvD2F_reg_regNodeErule6kM_I_; +text: .text%__1cVMoveL2D_reg_stackNodeMideal_Opcode6kM_i_; +text: .text%__1cFStateM_sub_Op_MulD6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_ModL6MpknENode__v_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_pnGThread__v_; +text: .text%__1cXjava_lang_reflect_FieldEslot6FpnHoopDesc__i_; +text: .text%__1cPloadConUL32NodeGis_Con6kM_I_; +text: .text%__1cQshrL_rReg_CLNodeHtwo_adr6kM_I_; +text: .text%__1cKJavaThreadbOcheck_special_condition_for_native_trans6Fp0_v_; +text: .text%__1cODeoptimizationYreset_invocation_counter6FpnJScopeDesc_i_v_; +text: .text%__1cZCallDynamicJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTconvF2I_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cMmulD_memNodeJnum_opnds6kM_I_; +text: .text%__1cHOrLNodeGOpcode6kM_i_; +text: .text%__1cIMulFNodeGmul_id6kM_pknEType__; +text: .text%__1cMnegF_regNodeErule6kM_I_; +text: .text%__1cMsubF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvD2F_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_RawMonitorCreate; +text: .text%__1cOresolve_symbol6Fpkc_pC_: os_solaris.o; +text: .text%__1cMMutableSpaceOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cKCMoveDNodeGOpcode6kM_i_; +text: .text%__1cFParseQdo_monitor_enter6M_v_; +text: .text%__1cPMultiBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cXpartialSubtypeCheckNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvD2INodeJideal_reg6kM_I_; +text: .text%__1cKcastPPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZCallDynamicJavaDirectNodeJnum_opnds6kM_I_; +text: .text%__1cMlogD_regNodeJnum_opnds6kM_I_; +text: .text%Unsafe_CompareAndSwapInt; +text: .text%__1cOstackSlotIOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOstackSlotIOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_ppkc5i_i_: arguments.o; +text: .text%__1cMmulD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNmulI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKimmL32OperFclone6kM_pnIMachOper__; +text: .text%__1cIimmFOperFclone6kM_pnIMachOper__; +text: .text%__1cOMacroAssemblerTset_last_Java_frame6MpnMRegisterImpl_22pC_v_; +text: .text%__1cOindOffset8OperFclone6kM_pnIMachOper__; +text: .text%__1cOMacroAssemblerVreset_last_Java_frame6MpnMRegisterImpl_i_v_; +text: .text%__1cOloadConL32NodeFclone6kM_pnENode__; +text: .text%__1cMloadConFNodeFclone6kM_pnENode__; +text: .text%__1cScompL_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQinstanceRefKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cNTemplateTableRlocals_index_wide6FpnMRegisterImpl__v_; +text: .text%__1cVMoveL2D_reg_stackNodePoper_input_base6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_data_at6MpnMRegisterImpl_i2_v_; +text: .text%__1cKOSRAdapter2n6FLI_pv_; +text: .text%__1cKOSRAdapterPnew_osr_adapter6FpnKCodeBuffer_pnJOopMapSet_ii_p0_; +text: .text%__1cJAssemblerEincl6MnHAddress__v_; +text: .text%__1cKOSRAdapter2t6MpnKCodeBuffer_pnJOopMapSet_iii_v_; +text: .text%__1cTconvI2D_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cNSharedRuntimeRgenerate_osr_blob6Fi_pnKOSRAdapter__; +text: .text%__1cMaddD_regNodeJnum_opnds6kM_I_; +text: .text%__1cbCAbstractInterpreterGeneratorUset_wide_entry_point6MpnITemplate_rpC_v_; +text: .text%__1cMmulF_regNodeErule6kM_I_; +text: .text%__1cIMulFNodeJideal_reg6kM_I_; +text: .text%__1cFStateM_sub_Op_MulF6MpknENode__v_; +text: .text%__1cJOopMapSetQsingular_oop_map6M_pnGOopMap__; +text: .text%__1cHnmethodVmark_as_seen_on_stack6M_v_; +text: .text%__1cMloadConDNodeHsize_of6kM_I_; +text: .text%__1cOcmovI_regUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLconvI2BNodeHtwo_adr6kM_I_; +text: .text%__1cMorL_rRegNodeJnum_opnds6kM_I_; +text: .text%__1cQorI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cMaddD_immNodeHtwo_adr6kM_I_; +text: .text%__1cMloadConDNodeKconst_size6kM_i_; +text: .text%__1cLConvL2FNodeLbottom_type6kM_pknEType__; +text: .text%__1cLConvL2DNodeLbottom_type6kM_pknEType__; +text: .text%__1cLloadSSINodeMideal_Opcode6kM_i_; +text: .text%__1cOstackSlotDOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cTconvF2D_reg_memNodeFreloc6kM_i_; +text: .text%__1cLConvD2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTconvL2D_reg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cMloadConDNodeFreloc6kM_i_; +text: .text%JVM_Lseek; +text: .text%__1cPsarL_rReg_1NodeErule6kM_I_; +text: .text%__1cPsarL_rReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVcompiledICHolderKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cMaddD_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cOstackSlotDOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMorL_rRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMmulF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMlogD_regNodeHtwo_adr6kM_I_; +text: .text%__1cRaddI_mem_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cFStateM_sub_Op_AddF6MpknENode__v_; +text: .text%__1cIXorINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cTconvL2F_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cSstring_compareNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNGrowableArray4CpnKOSRAdapter__Praw_at_put_grow6Mirk14_v_; +text: .text%__1cFStateP_sub_Op_StrComp6MpknENode__v_; +text: .text%__1cTconvL2F_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cUInterpreterGeneratorUgenerate_fixed_frame6Mi_v_; +text: .text%__1cGciType2t6MnJBasicType__v_; +text: .text%__1cMaddF_memNodeJnum_opnds6kM_I_; +text: .text%__1cUInterpreterGeneratorbAgenerate_run_compiled_code6M_v_; +text: .text%__1cScompL_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cUInterpreterGeneratorZgenerate_counter_overflow6MpC_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_counter_incr6MpnFLabel_22_v_; +text: .text%__1cPcmpF_cc_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_; +text: .text%__1cQmulI_mem_immNodePoper_input_base6kM_I_; +text: .text%__1cbCAbstractInterpreterGeneratorXbang_stack_shadow_pages6Mi_v_; +text: .text%__1cZInterpreterMacroAssemblerTnotify_method_entry6M_v_; +text: .text%__1cNdecL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOLibraryCallKitXinline_string_compareTo6M_i_; +text: .text%__1cScompL_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cJAssemblerEsubq6MpnMRegisterImpl_nHAddress__v_; +text: .text%jni_GetEnv; +text: .text%JVM_NanoTime; +text: .text%__1cCosNjavaTimeNanos6F_x_; +text: .text%__1cZInterpreterMacroAssemblerNsuper_call_VM6MpnMRegisterImpl_2pC22_v_; +text: .text%__1cFTypeFJis_finite6kM_i_; +text: .text%__1cRmulI_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOMacroAssemblerRcall_VM_leaf_base6MpCi_v_; +text: .text%__1cScompL_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cHMulNodeGis_Mul6kM_pk0_; +text: .text%__1cETypeEmake6Fn0AFTYPES__pk0_; +text: .text%__1cQmulI_mem_immNodeMideal_Opcode6kM_i_; +text: .text%__1cParrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cJAssemblerLemit_data646MxnJrelocInfoJrelocType_i_v_; +text: .text%__1cJAssemblerFpushq6MnHAddress__v_; +text: .text%__1cIGraphKitSgen_native_wrapper6MpnIciMethod__v_; +text: .text%__1cRComputeEntryStackIdo_array6Mii_v_; +text: .text%__1cPcmpD_cc_immNodeKconst_size6kM_i_; +text: .text%__1cKLoadPCNodeJideal_reg6kM_I_; +text: .text%__1cMorL_rRegNodeErule6kM_I_; +text: .text%__1cUCallNativeDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompP_rReg_memNodePoper_input_base6kM_I_; +text: .text%__1cScompP_rReg_memNodeMideal_Opcode6kM_i_; +text: .text%__1cSvframeStreamCommonbFfill_in_compiled_inlined_sender6M_i_; +text: .text%__1cNdivI_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cKcastPPNodeJnum_opnds6kM_I_; +text: .text%__1cTconvL2D_reg_memNodeHtwo_adr6kM_I_; +text: .text%__1cOLibraryCallKitbNinline_native_Reflection_getCallerClass6M_i_; +text: .text%__1cOLibraryCallKitZinline_native_Class_query6MnMvmIntrinsicsCID__i_; +text: .text%__1cMnegF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod__v_; +text: .text%__1cKciTypeFlowLStateVectorOdo_null_assert6MpnHciKlass__v_; +text: .text%__1cMsubD_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cJLoadLNodeMstore_Opcode6kM_i_; +text: .text%__1cNGrowableArray4CpnGciType__Egrow6Mi_v_; +text: .text%__1cMdivD_immNodeJnum_opnds6kM_I_; +text: .text%__1cNstoreImmPNodeJnum_opnds6kM_I_; +text: .text%__1cMdivD_immNodeHtwo_adr6kM_I_; +text: .text%__1cLloadSSINodeHtwo_adr6kM_I_; +text: .text%__1cLConvI2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIciMethodMnative_entry6M_pC_; +text: .text%__1cPcmpD_cc_immNodeFreloc6kM_i_; +text: .text%__1cUCallNativeDirectNodeFreloc6kM_i_; +text: .text%__1cNloadConPcNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulD_memNodeErule6kM_I_; +text: .text%jni_CallVoidMethod: jni.o; +text: .text%__1cFStateS_sub_Op_CallNative6MpknENode__v_; +text: .text%__1cFStateO_sub_Op_LoadPC6MpknENode__v_; +text: .text%__1cQAbstractCompilerPsupports_native6M_i_; +text: .text%__1cQorI_rReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbCAbstractInterpreterGeneratorbBgenerate_result_handler_for6MnJBasicType__pC_; +text: .text%__1cMmulF_regNodeHtwo_adr6kM_I_; +text: .text%__1cPsalL_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cQshrI_rReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableQvolatile_barrier6FnJAssemblerQMembar_mask_bits__v_; +text: .text%__1cNdivL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cVMoveL2D_reg_stackNodeErule6kM_I_; +text: .text%__1cRsalI_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNRelocIterator2t6MpnKCodeBuffer_pC3_v_; +text: .text%__1cJBasicLockHmove_to6MpnHoopDesc_p0_v_; +text: .text%__1cYinternal_word_RelocationMforce_target6MpC_v_; +text: .text%__1cOstackSlotIOperEtype6kM_pknEType__; +text: .text%__1cLloadSSINodeJnum_opnds6kM_I_; +text: .text%__1cKPSYoungGenRavailable_to_live6M_L_; +text: .text%__1cOstackSlotIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNcmovL_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cSstore_to_stackslot6FrnKCodeBuffer_iii_v_; +text: .text%__1cFTypeFGis_nan6kM_i_; +text: .text%__1cQshrL_rReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvD2F_reg_regNodeJnum_opnds6kM_I_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_safept_entry_for6MnITosState_pC_2_; +text: .text%__1cUInterpreterGeneratorbDgenerate_stack_overflow_check6M_v_; +text: .text%__1cRComputeEntryStackHdo_bool6M_v_; +text: .text%__1cMmulD_immNodeFreloc6kM_i_; +text: .text%__1cQmulI_mem_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_continuation_for6MnITosState__pC_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%JVM_FindPrimitiveClass; +text: .text%__1cOstackSlotIOperJnum_edges6kM_I_; +text: .text%JVM_IsSameClassPackage; +text: .text%__1cUInterpreterGeneratorXcheck_for_compiled_code6MrnFLabel__v_; +text: .text%__1cRaddI_mem_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMaddF_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPjava_lang_ClassYcreate_basic_type_mirror6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cLconvI2BNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMaddD_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMlogD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPSVirtualSpaceJshrink_by6ML_i_; +text: .text%__1cTconvD2F_reg_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cRCardTableModRefBSYcommitted_unique_to_self6kMinJMemRegion__1_; +text: .text%__1cNTemplateTableEiop26Fn0AJOperation__v_; +text: .text%__1cFStateN_sub_Op_LoadD6MpknENode__v_; +text: .text%__1cTconvL2F_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cZInterpreterMacroAssemblerRremove_activation6MnITosState_pnMRegisterImpl_iii_v_; +text: .text%__1cZInterpreterMacroAssemblerMdispatch_via6MnITosState_ppC_v_; +text: .text%__1cUInterpreterGeneratorbEgenerate_asm_interpreter_entry6Mi_pC_; +text: .text%__1cPcmpF_cc_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubF_memNodeJnum_opnds6kM_I_; +text: .text%__1cKPSYoungGenUavailable_to_min_gen6M_L_; +text: .text%__1cJAssemblerKrepne_scan6M_v_; +text: .text%__1cJname2type6Fpkc_nJBasicType__; +text: .text%__1cKPSYoungGenbCreset_survivors_after_shrink6M_v_; +text: .text%__1cKPSYoungGenQlimit_gen_shrink6ML_L_; +text: .text%__1cTconvI2D_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateP_sub_Op_ConvI2F6MpknENode__v_; +text: .text%__1cMmulD_immNodeKconst_size6kM_i_; +text: .text%__1cMmulD_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cMmulF_immNodeFreloc6kM_i_; +text: .text%__1cJloadBNodeHsize_of6kM_I_; +text: .text%__1cOcompI_rRegNodeHsize_of6kM_I_; +text: .text%__1cPPerfDataManagerWcreate_string_variable6FnJCounterNS_pkci3pnGThread__pnSPerfStringVariable__; +text: .text%__1cJloadPNodeHsize_of6kM_I_; +text: .text%__1cOtypeArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cOtypeArrayKlassNexternal_name6FnJBasicType__pkc_; +text: .text%Unsafe_StaticFieldOffset; +text: .text%__1cFTypeFFempty6kM_i_; +text: .text%__1cNcmovL_regNodeHtwo_adr6kM_I_; +text: .text%__1cLloadSSDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateO_sub_Op_CMoveP6MpknENode__v_; +text: .text%__1cVVM_ParallelGCSystemGCEdoit6M_v_; +text: .text%__1cETypeFxdual6kM_pk0_; +text: .text%__1cVVM_ParallelGCSystemGC2t6MIInHGCCauseFCause__v_; +text: .text%__1cJCmpF3NodeGOpcode6kM_i_; +text: .text%Unsafe_GetObjectVolatile; +text: .text%__1cMsubD_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLdivL_10NodePoper_input_base6kM_I_; +text: .text%__1cVVM_ParallelGCSystemGCEname6kM_pkc_; +text: .text%__1cOtypeArrayKlassMcreate_klass6FnJBasicType_ipnGThread__pnMklassOopDesc__; +text: .text%Unsafe_EnsureClassInitialized; +text: .text%__1cJAssemblerEjmpb6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cOcmovI_regUNodeHtwo_adr6kM_I_; +text: .text%__1cMaddD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerEmovw6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerGmovsbl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cMrax_RegLOperFclone6kM_pnIMachOper__; +text: .text%__1cMorL_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvD2F_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cUParallelScavengeHeapHcollect6MnHGCCauseFCause__v_; +text: .text%__1cJLoadDNodeJideal_reg6kM_I_; +text: .text%__1cQciTypeArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cQmulI_mem_immNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCi_v_; +text: .text%__1cPcheckCastPPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerFpopaq6M_v_; +text: .text%__1cSmembar_acquireNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKimmL10OperJnum_edges6kM_I_; +text: .text%Unsafe_StaticFieldBaseFromField; +text: .text%__1cLcastP2LNodeHsize_of6kM_I_; +text: .text%__1cQmulI_mem_immNodeRis_cisc_alternate6kM_i_; +text: .text%__1cMsubD_regNodeHtwo_adr6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerSnotify_method_exit6MnITosState__v_; +text: .text%__1cRsubI_rReg_memNodeHsize_of6kM_I_; +text: .text%__1cTconvL2F_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cNReservedSpace2t6MpcL_v_; +text: .text%__1cKmul_hiNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSmembar_acquireNodeJnum_opnds6kM_I_; +text: .text%__1cQsarL_rReg_63NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerMemit_arith_b6MiipnMRegisterImpl_i_v_; +text: .text%__1cPsarL_rReg_2NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLdivL_10NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableXresolve_cache_and_index6FipnMRegisterImpl_2_v_; +text: .text%__1cNTemplateTableZjvmti_post_fast_field_mod6F_v_; +text: .text%JVM_GC; +text: .text%__1cNTemplateTablePfast_storefield6FnITosState__v_; +text: .text%__1cNTemplateTableQfast_accessfield6FnITosState__v_; +text: .text%__1cScompP_rReg_memNodeJnum_opnds6kM_I_; +text: .text%__1cIPSOldGenSexpand_to_reserved6M_i_; +text: .text%__1cQmulI_mem_immNodeJnum_opnds6kM_I_; +text: .text%__1cIPSOldGenJexpand_by6ML_i_; +text: .text%__1cIPSOldGenGexpand6ML_v_; +text: .text%__1cIPSOldGenXexpand_and_cas_allocate6ML_pnIHeapWord__; +text: .text%__1cPsarL_rReg_1NodeHtwo_adr6kM_I_; +text: .text%__1cJAssemblerFtestb6MpnMRegisterImpl_i_v_; +text: .text%__1cXpartialSubtypeCheckNodeHtwo_adr6kM_I_; +text: .text%__1cMsubF_regNodeHtwo_adr6kM_I_; +text: .text%__1cZInterpreterMacroAssemblerRget_constant_pool6MpnMRegisterImpl__v_; +text: .text%__1cRaddL_rReg_memNodeFreloc6kM_i_; +text: .text%__1cVMoveL2D_reg_stackNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompP_rReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPsarL_rReg_1NodeJnum_opnds6kM_I_; +text: .text%__1cOGenerateOopMapGdo_jsr6Mi_v_; +text: .text%__1cMmulF_memNodeHtwo_adr6kM_I_; +text: .text%__1cScompP_rReg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cLPSMarkSweepGinvoke6Fpii_v_; +text: .text%__1cOcmovD_regUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNcmovL_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvF2I_reg_regNodeHtwo_adr6kM_I_; +text: .text%__1cMmulF_immNodeKconst_size6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerbGget_unsigned_2_byte_index_at_bcp6MpnMRegisterImpl_i_v_; +text: .text%__1cZInterpreterMacroAssemblerRcall_VM_leaf_base6MpCi_v_; +text: .text%__1cNTemplateTableGiconst6Fi_v_; +text: .text%__1cMdecI_memNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosMuser_handler6F_pv_; +text: .text%__1cJloadDNodeFreloc6kM_i_; +text: .text%__1cMincL_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNaddL_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cCosHSolarisSset_signal_handler6Fiii_v_; +text: .text%__1cNinstanceKlassSremove_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cTconvD2F_reg_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cKMemoryPoolLadd_manager6MpnNMemoryManager__v_; +text: .text%__1cMmulD_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLClassLoaderbCupdate_class_path_entry_list6Fpkc_v_; +text: .text%__1cMsubF_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcmovI_regUNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cTconvL2D_reg_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cLOptoRuntimeRresolve_call_Type6F_pknITypeFunc__; +text: .text%__1cHciKlassIis_klass6M_i_; +text: .text%__1cPPerfDataManagerKname_space6Fpkci_pc_; +text: .text%__1cKScopeValuePis_constant_int6kM_i_; +text: .text%jni_RegisterNatives: jni.o; +text: .text%Unsafe_GetNativeFloat; +text: .text%__1cMsubF_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%JVM_GetClassDeclaredFields; +text: .text%__1cMsubF_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cJMemRegion2t6M_v_; +text: .text%jni_SetStaticObjectField: jni.o; +text: .text%__1cQsalL_rReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cCosEstat6FpkcpnEstat__i_; +text: .text%__1cJArgumentsRverify_percentage6FLpkc_i_; +text: .text%__1cLOptoRuntimeTmultianewarray_Type6Fi_pknITypeFunc__; +text: .text%__1cRComputeEntryStackHdo_long6M_v_; +text: .text%__1cHnmethodVinvalidate_osr_method6M_v_; +text: .text%__1cMaddF_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cNMemoryManagerIadd_pool6MpnKMemoryPool__v_; +text: .text%jni_SetObjectField: jni.o; +text: .text%__1cLConvD2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJAssemblerEcall6MpnMRegisterImpl_nJrelocInfoJrelocType__v_; +text: .text%__1cJloadDNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLConvD2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOBasicHashtable2t6Mii_v_; +text: .text%__1cCosHSolarisOis_sig_ignored6Fi_i_; +text: .text%__1cNandI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNcmovL_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cOPhaseIdealLoopJclone_iff6MpnHPhiNode_pnNIdealLoopTree__pnIBoolNode__; +text: .text%__1cMTailJumpNodeGOpcode6kM_i_; +text: .text%__1cCosHSolarisVcleanup_interruptible6FpnKJavaThread__v_; +text: .text%__1cCosHSolarisTsetup_interruptible6F_pnKJavaThread__; +text: .text%__1cCosHSolarisTsetup_interruptible6FpnKJavaThread__v_; +text: .text%__1cMdivD_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_IsSupportedJNIVersion; +text: .text%JVM_LoadLibrary; +text: .text%JVM_Sleep; +text: .text%__1cNReservedSpaceKinitialize6MLLipc_v_; +text: .text%__1cHOrLNodeLbottom_type6kM_pknEType__; +text: .text%__1cOstackSlotIOperFscale6kM_i_; +text: .text%__1cLConvD2FNodeLbottom_type6kM_pknEType__; +text: .text%__1cLConvF2INodeLbottom_type6kM_pknEType__; +text: .text%jint_cmp: parse2.o; +text: .text%__1cOstackSlotIOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cLloadSSINodeErule6kM_I_; +text: .text%__1cLConvI2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cVMoveF2I_reg_stackNodePoper_input_base6kM_I_; +text: .text%__1cLConvL2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIDivDNodeJideal_reg6kM_I_; +text: .text%__1cRandI_rReg_memNodeFreloc6kM_i_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_int_field06FnJBasicType__pC_; +text: .text%jni_GetJavaVM: jni.o; +text: .text%__1cPPerfLongVariant2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_pnUPerfLongSampleHelper__v_; +text: .text%jni_MonitorExit: jni.o; +text: .text%jni_MonitorEnter: jni.o; +text: .text%__1cHciKlass2t6MnLKlassHandle_pnIciSymbol__v_; +text: .text%__1cPciInstanceKlassbDcompute_shared_is_initialized6M_i_; +text: .text%__1cNGrowableArray4CpnIPerfData__Praw_at_put_grow6Mirk14_v_; +text: .text%__1cFciEnvOrecord_failure6Mpkc_v_; +text: .text%__1cMciArrayKlassRbase_element_type6M_pnGciType__; +text: .text%__1cLConvL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOstackSlotDOperFscale6kM_i_; +text: .text%__1cOstackSlotDOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cOcmovI_regUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKReturnNodeUdepends_only_on_test6kM_i_; +text: .text%__1cNcmovL_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvD2F_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTconvF2I_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvL2F_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_immNodeJnum_opnds6kM_I_; +text: .text%__1cVMoveL2D_reg_stackNodeJnum_opnds6kM_I_; +text: .text%__1cRaddI_mem_rRegNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNTemplateTableH_return6FnITosState__v_; +text: .text%__1cNTemplateTableHif_icmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_0cmp6Fn0AJCondition__v_; +text: .text%__1cTconvL2D_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXpartialSubtypeCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSharedRuntimeEdrem6Fdd_d_; +text: .text%__1cRaddI_rReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMloadConDNodeFclone6kM_pnENode__; +text: .text%__1cScompP_rReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKC2IAdapterXreturn_from_interpreter6M_pC_; +text: .text%__1cKC2IAdapterRsetup_stack_frame6MnFframe_pnLvframeArray__v_; +text: .text%__1cIregDOperFclone6kM_pnIMachOper__; +text: .text%__1cJAssemblerGmovswl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cMsubF_memNodeErule6kM_I_; +text: .text%__1cIimmDOperFclone6kM_pnIMachOper__; +text: .text%__1cOMacroAssemblerQload_signed_byte6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cKC2IAdapterSunpack_c2i_adapter6MnFframe_1pnLvframeArray__v_; +text: .text%__1cNdivI_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRMachSpillCopyNodeHsize_of6kM_I_; +text: .text%__1cFframebFset_interpreter_frame_sender_sp6Mpl_v_; +text: .text%__1cPsarL_rReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cULinearLeastSquareFit2t6MI_v_; +text: .text%__1cSObjectSynchronizerIjni_exit6FpnHoopDesc_pnGThread__v_; +text: .text%__1cSObjectSynchronizerJjni_enter6FnGHandle_pnGThread__v_; +text: .text%__1cMaddF_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulF_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosZvm_allocation_granularity6F_i_; +text: .text%__1cJAssemblerFpopfq6M_v_; +text: .text%__1cCosOreserve_memory6FLpc_1_; +text: .text%Unsafe_ObjectFieldOffset; +text: .text%__1cUParallelScavengeHeapEkind6M_nNCollectedHeapEName__; +text: .text%__1cKMemoryPool2t6Mpkcn0AIPoolType_LLii_v_; +text: .text%__1cNSpaceCounters2t6MpkciLpnMMutableSpace_pnSGenerationCounters__v_; +text: .text%__1cMNativeLookupTbase_library_lookup6Fpkc22_pC_; +text: .text%__1cMaddF_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cOcompiledVFrameUresolve_monitor_lock6kMnILocation__pnJBasicLock__; +text: .text%__1cTconvD2I_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cQjava_lang_ThreadKset_daemon6FpnHoopDesc__v_; +text: .text%__1cKCompiledICSset_to_megamorphic6MpnICallInfo_nJBytecodesECode_pnGThread__v_; +text: .text%__1cNReservedSpaceKfirst_part6MLii_0_; +text: .text%__1cNCellTypeStateImake_any6Fi_0_; +text: .text%__1cMorL_rRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cNTemplateTableFaload6Fi_v_; +text: .text%__1cISubFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMnegF_regNodeJnum_opnds6kM_I_; +text: .text%__1cINegDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKCompiledICOis_megamorphic6kM_i_; +text: .text%__1cXNativeSignatureIteratorJdo_double6M_v_; +text: .text%__1cISubDNodeGadd_id6kM_pknEType__; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_double6M_v_; +text: .text%__1cNTemplateTableElop26Fn0AJOperation__v_; +text: .text%__1cNTemplateTableEfop26Fn0AJOperation__v_; +text: .text%__1cNTemplateTableEdop26Fn0AJOperation__v_; +text: .text%__1cMaddD_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMMonitorChunk2t6Mi_v_; +text: .text%__1cLMoveL2DNodeLbottom_type6kM_pknEType__; +text: .text%__1cZCompiledArgumentOopFinderDset6MinJBasicType__v_; +text: .text%__1cNstoreImmPNodeFreloc6kM_i_; +text: .text%__1cNSharedRuntimeVhandle_ic_miss_helper6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cLOptoRuntimebBhandle_wrong_method_ic_miss6FpnKJavaThread__pC_; +text: .text%__1cKJavaThreadUremove_monitor_chunk6MpnMMonitorChunk__v_; +text: .text%__1cKJavaThreadRadd_monitor_chunk6MpnMMonitorChunk__v_; +text: .text%__1cNReservedSpace2t6ML_v_; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnQPerfLongVariable__; +text: .text%__1cNmulL_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNmulI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTGeneratePairingInfoOreport_results6kM_i_; +text: .text%Unsafe_GetNativeByte; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2T6M_v_; +text: .text%__1cFframebLprevious_monitor_in_interpreter_frame6kMpnPBasicObjectLock__2_; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2t6M_v_; +text: .text%__1cOMacroAssemblerQload_signed_word6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cQOopMapCacheEntryPfill_for_native6M_v_; +text: .text%__1cFStateP_sub_Op_ConvD2I6MpknENode__v_; +text: .text%__1cJAssemblerGpushfq6M_v_; +text: .text%__1cKVtableStubRpd_code_alignment6F_i_; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_2_v_; +text: .text%__1cIDivFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cINegDNodeJideal_reg6kM_I_; +text: .text%__1cODeoptimizationZtrap_state_set_recompiled6Fii_i_; +text: .text%__1cPshrL_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTconvF2D_reg_memNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_; +text: .text%__1cNandI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTMaskFillerForNative2t6MnMmethodHandle_pLi_v_; +text: .text%__1cMsubF_memNodeHtwo_adr6kM_I_; +text: .text%__1cINegFNodeLbottom_type6kM_pknEType__; +text: .text%__1cRaddL_rReg_memNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cLVtableStubsLcreate_stub6FiipnNmethodOopDesc__pC_; +text: .text%__1cMmulL_memNodeFreloc6kM_i_; +text: .text%__1cLVtableStubsGlookup6Fiii_pnKVtableStub__; +text: .text%__1cMMonitorValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cFStateM_sub_Op_NegD6MpknENode__v_; +text: .text%__1cOtailjmpIndNodePoper_input_base6kM_I_; +text: .text%__1cNStubGeneratorYgenerate_throw_exception6MpkcpCi_3_; +text: .text%__1cISubDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPPerfDataManagerKname_space6Fpkc2i_pc_; +text: .text%__1cLVtableStubsOis_entry_point6FpC_i_; +text: .text%__1cNstoreImmPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHRetNodeJnum_opnds6kM_I_; +text: .text%__1cIDivINodeJideal_reg6kM_I_; +text: .text%__1cRInvocationCounterDdef6Fn0AFState_ipFnMmethodHandle_pnGThread__pC_v_; +text: .text%__1cMNativeLookupNlong_jni_name6FnMmethodHandle__pc_; +text: .text%__1cMaddF_memNodeErule6kM_I_; +text: .text%__1cOcmovD_regUNodeHtwo_adr6kM_I_; +text: .text%__1cMaddF_memNodeHtwo_adr6kM_I_; +text: .text%__1cOGenerateOopMapTret_jump_targets_do6MpnOBytecodeStream_pFp0ipi_vi4_v_; +text: .text%__1cOClassPathEntry2t6M_v_; +text: .text%__1cMorL_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cOMacroAssemblerNpop_CPU_state6M_v_; +text: .text%__1cMmulF_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cOMacroAssemblerOpush_CPU_state6M_v_; +text: .text%__1cOMacroAssemblerNpop_FPU_state6M_v_; +text: .text%__1cOMacroAssemblerOpush_FPU_state6M_v_; +text: .text%__1cOMacroAssemblerMpop_IU_state6M_v_; +text: .text%__1cOMacroAssemblerNpush_IU_state6M_v_; +text: .text%__1cOMacroAssemblerSstore_check_part_26MpnMRegisterImpl__v_; +text: .text%__1cTconvL2D_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOMacroAssemblerSstore_check_part_16MpnMRegisterImpl__v_; +text: .text%__1cRaddL_rReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMaddF_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRClassPathZipEntry2t6Mppvpc_v_; +text: .text%__1cNTemplateTableOprepare_invoke6FpnMRegisterImpl_2inJBytecodesECode__v_; +text: .text%__1cVMoveF2I_reg_stackNodeErule6kM_I_; +text: .text%__1cJAssemblerEandq6MpnMRegisterImpl_2_v_; +text: .text%__1cFParsePdo_lookupswitch6M_v_; +text: .text%__1cLConvF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFParseRjump_if_true_fork6MpnGIfNode_ii_v_; +text: .text%__1cIAddDNodeJideal_reg6kM_I_; +text: .text%__1cJloadFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRComputeEntryStackJdo_double6M_v_; +text: .text%__1cMaddD_regNodeHtwo_adr6kM_I_; +text: .text%__1cLConvD2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTablebAload_invoke_cp_cache_entry6FipnMRegisterImpl_22ii_v_; +text: .text%__1cNTemplateTableZload_field_cp_cache_entry6FipnMRegisterImpl_22i_v_; +text: .text%__1cJAssemblerEcmpb6MnHAddress_i_v_; +text: .text%__1cCosGsignal6Fipv_1_; +text: .text%__1cLClassLoaderSget_canonical_path6Fpc1i_i_; +text: .text%__1cLClassLoaderXcreate_class_path_entry6FpcnEstat_ppnOClassPathEntry__v_; +text: .text%__1cMsubD_immNodeHtwo_adr6kM_I_; +text: .text%__1cLklassVtableTis_miranda_entry_at6Mi_i_; +text: .text%__1cKPSScavengeZclean_up_failed_promotion6F_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fii_v_; +text: .text%JVM_Available; +text: .text%__1cJAssemblerHucomiss6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerRprofile_checkcast6MipnMRegisterImpl__v_; +text: .text%__1cIAddDNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJAssemblerFimulq6MpnMRegisterImpl_2_v_; +text: .text%__1cIRetTableUfind_jsrs_for_target6Mi_pnNRetTableEntry__; +text: .text%__1cNRegisterSaverWrestore_live_registers6FpnOMacroAssembler__v_; +text: .text%__1cLClassLoaderLadd_to_list6FpnOClassPathEntry__v_; +text: .text%__1cNTemplateTableGastore6Fi_v_; +text: .text%__1cNTemplateTableGdstore6Fi_v_; +text: .text%__1cNTemplateTableGfstore6Fi_v_; +text: .text%__1cNTemplateTableGlstore6Fi_v_; +text: .text%__1cNRegisterSaverTsave_live_registers6FpnOMacroAssembler_ipi_pnGOopMap__; +text: .text%__1cNTemplateTableGistore6Fi_v_; +text: .text%__1cIRetTableHadd_jsr6Mii_v_; +text: .text%__1cMincL_memNodeHtwo_adr6kM_I_; +text: .text%__1cKPSYoungGenOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cNGrowableArray4CpnLmarkOopDesc__2t6Mii_v_; +text: .text%__1cUCompressedReadStreamJread_long6M_x_; +text: .text%__1cISubDNodeJideal_reg6kM_I_; +text: .text%__1cWNonPrintingResourceObj2n6FLnLResourceObjPallocation_type__pv_; +text: .text%__1cNTemplateTableFdload6Fi_v_; +text: .text%__1cNTemplateTableFfload6Fi_v_; +text: .text%__1cPaddress_of_flag6FnXCommandLineFlagWithType__pnEFlag__: globals.o; +text: .text%__1cNTemplateTableFlload6Fi_v_; +text: .text%__1cNTemplateTableFiload6Fi_v_; +text: .text%__1cMmulI_memNodePoper_input_base6kM_I_; +text: .text%__1cOcompL_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNGrowableArray4CpnLmarkOopDesc__Uclear_and_deallocate6M_v_; +text: .text%__1cMLinkResolverbBlookup_method_in_interfaces6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cOcompI_rRegNodeFclone6kM_pnENode__; +text: .text%__1cRsubI_rReg_memNodeFclone6kM_pnENode__; +text: .text%__1cLcastP2LNodeFclone6kM_pnENode__; +text: .text%__1cKExceptionsK_throw_oop6FpnGThread_pkcipnHoopDesc__v_; +text: .text%__1cRaddL_rReg_memNodeErule6kM_I_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl__v_; +text: .text%__1cOsalI_mem_1NodeHtwo_adr6kM_I_; +text: .text%__1cHRetNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVMoveL2D_reg_stackNodeHtwo_adr6kM_I_; +text: .text%__1cRaddL_mem_rRegNodeHtwo_adr6kM_I_; +text: .text%__1cJloadPNodeFclone6kM_pnENode__; +text: .text%__1cJloadBNodeFclone6kM_pnENode__; +text: .text%__1cRaddL_rReg_memNodeHtwo_adr6kM_I_; +text: .text%__1cMmulF_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cMaddF_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerEmovb6MnHAddress_i_v_; +text: .text%__1cIAddDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%Unsafe_AllocateMemory; +text: .text%__1cVMoveF2I_reg_stackNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerHfxrstor6MnHAddress__v_; +text: .text%__1cJAssemblerGfxsave6MnHAddress__v_; +text: .text%__1cHCompilePget_invoke_name6M_pnIciSymbol__; +text: .text%__1cJAssemblerEsetb6Mn0AJCondition_pnMRegisterImpl__v_; +text: .text%__1cNxorI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cMVM_OperationNdoit_prologue6M_i_; +text: .text%__1cNGCTaskManagerGthread6MI_pnMGCTaskThread__; +text: .text%__1cRConstantLongValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cRConstantLongValueQis_constant_long6kM_i_; +text: .text%__1cKScopeValuePis_constant_oop6kM_i_; +text: .text%__1cKScopeValueSis_constant_double6kM_i_; +text: .text%__1cMmulD_memNodeHtwo_adr6kM_I_; +text: .text%__1cVMoveF2I_reg_stackNodeHtwo_adr6kM_I_; +text: .text%jni_CallStaticObjectMethod: jni.o; +text: .text%__1cNcmovL_memNodeHtwo_adr6kM_I_; +text: .text%__1cFStateM_sub_Op_AddD6MpknENode__v_; +text: .text%__1cMmulI_memNodeMideal_Opcode6kM_i_; +text: .text%__1cScompL_rReg_memNodeFreloc6kM_i_; +text: .text%__1cLloadSSINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNGrowableArray4CpnIPerfData__2t6Mii_v_; +text: .text%__1cOCompilerThreadSis_Compiler_thread6kM_i_; +text: .text%__1cTjava_lang_ThrowableLset_message6FpnHoopDesc_2_v_; +text: .text%__1cMPerfDataList2T6M_v_; +text: .text%__1cLVtableStubsFenter6FiiipnKVtableStub__v_; +text: .text%__1cMmulI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOcmovD_regUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNCompileBrokerUmake_compiler_thread6FpkcpnMCompileQdDueue_pnQCompilerCounters_pnGThread__pnOCompilerThread__; +text: .text%__1cNnegI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cbCAbstractInterpreterGeneratorbHgenerate_exception_handler_common6Mpkc2i_pC_; +text: .text%__1cSCommandLineFlagsExKis_default6FnPCommandLineFlag__i_; +text: .text%__1cJAssemblerEnegl6MpnMRegisterImpl__v_; +text: .text%__1cUConstantOopReadValuePis_constant_oop6kM_i_; +text: .text%__1cHMatcherNlogDSupported6F_ki_; +text: .text%__1cOGenerateOopMapRdo_multianewarray6Mii_v_; +text: .text%__1cLconvI2BNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cUPSGenerationCounters2t6MpkciipnOPSVirtualSpace__v_; +text: .text%__1cMPerfDataList2t6Mi_v_; +text: .text%__1cFStateP_sub_Op_ConvI2D6MpknENode__v_; +text: .text%__1cQCompilerCounters2t6MpkcipnGThread__v_; +text: .text%__1cJCodeCachebCmake_marked_nmethods_zombies6F_v_; +text: .text%__1cNVM_DeoptimizeEname6kM_pkc_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC2_v_; +text: .text%__1cHThreadsbFdeoptimized_wrt_marked_nmethods6F_v_; +text: .text%__1cSCommandLineFlagsExJboolAtPut6FnXCommandLineFlagWithType_i_v_; +text: .text%__1cMmulI_memNodeJnum_opnds6kM_I_; +text: .text%__1cFStateM_sub_Op_CmpF6MpknENode__v_; +text: .text%__1cODeoptimizationVdeoptimize_dependents6F_i_; +text: .text%__1cOtailjmpIndNodeGpinned6kM_i_; +text: .text%__1cQshrL_rReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerGmovzbl6MpnMRegisterImpl_2_v_; +text: .text%__1cILogDNodeJideal_reg6kM_I_; +text: .text%__1cMmulI_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cRaddL_mem_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMincL_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateO_sub_Op_Conv2B6MpknENode__v_; +text: .text%__1cNcmovL_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_CallStaticObjectMethodV: jni.o; +text: .text%__1cMOopTaskQdDueueKinitialize6M_v_; +text: .text%__1cMmulD_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMOopTaskQdDueue2t6M_v_; +text: .text%__1cOLibraryCallKitbBinline_native_currentThread6M_i_; +text: .text%__1cNMemoryManager2t6M_v_; +text: .text%__1cMaddF_immNodeKconst_size6kM_i_; +text: .text%__1cVMoveL2D_reg_stackNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOLibraryCallKitSinline_math_native6MnMvmIntrinsicsCID__i_; +text: .text%__1cFciEnvbNArrayIndexOutOfBoundsException_instance6M_pnKciInstance__; +text: .text%__1cMsubD_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMaddF_immNodeFreloc6kM_i_; +text: .text%__1cMaddD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOCompilerThreadbCis_hidden_from_external_view6kM_i_; +text: .text%__1cNReservedSpaceJlast_part6ML_0_; +text: .text%__1cMnegF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMaddD_immNodeFreloc6kM_i_; +text: .text%jni_IsInstanceOf: jni.o; +text: .text%__1cMaddD_immNodeKconst_size6kM_i_; +text: .text%jni_Throw: jni.o; +text: .text%__1cRmulI_rReg_immNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cOsalI_mem_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSPSPromotionManager2t6M_v_; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cOLibraryCallKitXgenerate_current_thread6MrpnENode__2_; +text: .text%__1cMsubF_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerFmovss6MpnRFloatRegisterImpl_2_v_; +text: .text%JVM_GetLastErrorString; +text: .text%__1cJAssemblerFmovsd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerTprofile_switch_case6MpnMRegisterImpl_22_v_; +text: .text%__1cZInterpreterMacroAssemblerWprofile_switch_default6MpnMRegisterImpl__v_; +text: .text%__1cFStateM_sub_Op_SubF6MpknENode__v_; +text: .text%__1cLVtableStubsScreate_vtable_stub6Fii_pnKVtableStub__; +text: .text%JVM_GetInterfaceVersion; +text: .text%__1cKstoreBNodeErule6kM_I_; +text: .text%__1cKVtableStub2n6FLi_pv_; +text: .text%__1cJAssemblerEdecq6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_virtual_call6MpnMRegisterImpl_22_v_; +text: .text%__1cOtailjmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosFyield6F_v_; +text: .text%__1cRaddI_mem_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOPSVirtualSpace2t6MnNReservedSpace_L_v_; +text: .text%__1cOLibraryCallKitMinline_trans6MnMvmIntrinsicsCID__i_; +text: .text%Unsafe_SetMemory; +text: .text%__1cCosIjvm_path6Fpci_v_; +text: .text%__1cJTimeStamp2t6M_v_; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_2i_v_; +text: .text%__1cLconvI2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorKpass_float6M_v_; +text: .text%__1cISubFNodeJideal_reg6kM_I_; +text: .text%__1cNGrowableArray4CpnIPerfData__Egrow6Mi_v_; +text: .text%__1cMSysClassPathNreset_item_at6Mi_v_; +text: .text%__1cFStateM_sub_Op_LogD6MpknENode__v_; +text: .text%__1cFTypeDFempty6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_2i_v_; +text: .text%__1cJlookupOne6FpnHJNIEnv__pkcpnGThread__pnH_jclass__: jni.o; +text: .text%__1cLloadSSINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosGgetenv6Fpkcpci_i_; +text: .text%__1cZInterpreterMacroAssemblerLlock_object6MpnMRegisterImpl__v_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cISubFNodeGadd_id6kM_pknEType__; +text: .text%__1cJArgumentsMbuild_string6Fppcpkc_v_; +text: .text%__1cFStateM_sub_Op_SubD6MpknENode__v_; +text: .text%JVM_RegisterSignal; +text: .text%JVM_FindSignal; +text: .text%__1cVcompiler_thread_entry6FpnKJavaThread_pnGThread__v_: thread.o; +text: .text%__1cMorL_rRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableMfast_xaccess6FnITosState__v_; +text: .text%__1cKConv2BNodeJideal_reg6kM_I_; +text: .text%__1cXNativeSignatureIteratorIdo_float6M_v_; +text: .text%jni_GetDoubleArrayRegion: jni.o; +text: .text%__1cLloadSSDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveF2I_reg_stackNodeJnum_opnds6kM_I_; +text: .text%__1cJArgumentsObuild_jvm_args6Fpkc_v_; +text: .text%__1cOLibraryCallKitMpop_math_arg6M_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerRgen_subtype_check6MpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cFStateO_sub_Op_CMoveL6MpknENode__v_; +text: .text%__1cJArgumentsMadd_property6Fpkc_i_; +text: .text%__1cMVM_OperationNdoit_epilogue6M_v_; +text: .text%__1cOCompilerThread2t6MpnMCompileQdDueue_pnQCompilerCounters__v_; +text: .text%__1cRaddI_mem_rRegNodeFreloc6kM_i_; +text: .text%__1cSInterpreterRuntimebKthrow_ArrayIndexOutOfBoundsException6FpnKJavaThread_pci_v_; +text: .text%__1cVMoveF2I_reg_stackNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableGfconst6Fi_v_; +text: .text%__1cNCompileBrokerUcompiler_thread_loop6F_v_; +text: .text%__1cOtailjmpIndNodeHtwo_adr6kM_I_; +text: .text%__1cQmulI_mem_immNodeFreloc6kM_i_; +text: .text%__1cNincI_rRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNVM_DeoptimizeEdoit6M_v_; +text: .text%__1cLMoveF2INodeLbottom_type6kM_pknEType__; +text: .text%__1cUConstantOopReadValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cRaddI_mem_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMdivD_immNodeKconst_size6kM_i_; +text: .text%__1cMmulD_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cQObjectStartArrayKinitialize6MnJMemRegion__v_; +text: .text%__1cQObjectStartArraySset_covered_region6MnJMemRegion__v_; +text: .text%__1cMsubF_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cUGcThreadCountClosureJdo_thread6MpnGThread__v_; +text: .text%__1cNGrowableArray4CpnTDerivedPointerEntry__Egrow6Mi_v_; +text: .text%__1cOtailjmpIndNodeJnum_opnds6kM_I_; +text: .text%__1cPGCMemoryManagerXinitialize_gc_stat_info6M_v_; +text: .text%__1cPGCMemoryManager2t6M_v_; +text: .text%__1cKGCStatInfo2t6Mi_v_; +text: .text%__1cTMaskFillerForNativeLpass_object6M_v_; +text: .text%__1cTMaskFillerForNativeJpass_long6M_v_; +text: .text%__1cJMarkSweepUAdjustPointerClosure2t6Mi_v_; +text: .text%__1cCosHrealloc6FpvL_1_; +text: .text%__1cCosWactive_processor_count6F_i_; +text: .text%__1cSestimate_path_freq6FpnENode__f_: loopnode.o; +text: .text%__1cCosNsigexitnum_pd6F_i_; +text: .text%__1cMLinkResolverbEvtable_index_of_miranda_method6FnLKlassHandle_nMsymbolHandle_2pnGThread__i_; +text: .text%__1cOLibraryCallKitVinline_fp_conversions6MnMvmIntrinsicsCID__i_; +text: .text%__1cZcatch_cleanup_intra_block6FpnENode_1pnFBlock_ii_v_: lcm.o; +text: .text%__1cCosbCis_thread_cpu_time_supported6F_i_; +text: .text%__1cLklassVtableQindex_of_miranda6MpnNsymbolOopDesc_2_i_; +text: .text%__1cNdefaultStreamMhas_log_file6M_i_; +text: .text%__1cNcmovL_memNodeRis_cisc_alternate6kM_i_; +text: .text%__1cRalign_object_size6Fl_l_; +text: .text%__1cMarrayOopDescLheader_size6FnJBasicType__i_; +text: .text%__1cNstoreImmBNodeErule6kM_I_; +text: .text%__1cNstoreImmINodeErule6kM_I_; +text: .text%__1cLloadSSDNodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cFParseMjump_if_join6MpnENode_2_2_; +text: .text%__1cKJNIHandlesQmake_weak_global6FnGHandle__pnI_jobject__; +text: .text%__1cLloadSSINodeZcheck_for_anti_dependence6kM_i_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_float_field06FnJBasicType__pC_; +text: .text%__1cFParseRdo_multianewarray6M_v_; +text: .text%__1cMloadConDNodeGis_Con6kM_I_; +text: .text%jni_NewWeakGlobalRef: jni.o; +text: .text%__1cPfilename_to_pid6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cTis_directory_secure6Fpkc_i_: perfMemory_solaris.o; +text: .text%jni_CallStaticVoidMethodV: jni.o; +text: .text%jni_CallStaticBooleanMethod: jni.o; +text: .text%__1cNGrowableArray4CpnNmethodOopDesc__Egrow6Mi_v_; +text: .text%__1cRInvocationCounterMreinitialize6Fi_v_; +text: .text%__1cXpartialSubtypeCheckNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTconvF2I_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cUInterpreterGeneratorVgenerate_native_entry6Mi_pC_; +text: .text%__1cUInterpreterGeneratorLlock_method6M_v_; +text: .text%__1cNGrowableArray4CpC_Egrow6Mi_v_; +text: .text%__1cNGrowableArray4CL_Egrow6Mi_v_; +text: .text%__1cObox_handleNodeHsize_of6kM_I_; +text: .text%__1cPsarL_rReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIPSOldGenKinitialize6MnNReservedSpace_Lpkci_v_; +text: .text%__1cIPSOldGenYinitialize_virtual_space6MnNReservedSpace_L_v_; +text: .text%__1cIPSOldGenPinitialize_work6Mpkci_v_; +text: .text%__1cNdivI_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbCAbstractInterpreterGeneratorTgenerate_error_exit6Mpkc_pC_; +text: .text%__1cTAbstractInterpreterKinitialize6F_v_; +text: .text%__1cZInterpreterMacroAssemblerLprofile_ret6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerSprofile_final_call6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerMprofile_call6MpnMRegisterImpl__v_; +text: .text%__1cOPSVirtualSpace2t6M_v_; +text: .text%__1cOPSVirtualSpaceKinitialize6MnNReservedSpace_L_i_; +text: .text%__1cZInterpreterMacroAssemblerSupdate_mdp_for_ret6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_flag_at6MpnMRegisterImpl_i_v_; +text: .text%__1cZInterpreterMacroAssemblerWdispatch_only_noverify6MnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpCpnMRegisterImpl__v_; +text: .text%__1cKReflectionbFbasic_type_arrayklass_to_mirror6FpnMklassOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cMAdapterCache2t6M_v_; +text: .text%__1cSComputeAdapterInfoIdo_array6Mii_v_; +text: .text%__1cGatomll6Fpkcpx_i_: arguments.o; +text: .text%__1cJArgumentsRcheck_memory_size6Fxx_n0AJArgsRange__; +text: .text%__1cJArgumentsVset_parallel_gc_flags6F_v_; +text: .text%__1cYalign_to_allocation_size6FL_L_: heap.o; +text: .text%__1cJArgumentsRparse_memory_size6Fpkcpxx_n0AJArgsRange__; +text: .text%__1cJArgumentsXPropertyList_unique_add6FppnOSystemProperty_pkcpc_v_; +text: .text%__1cQAgentLibraryList2t6M_v_; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_; +text: .text%__1cMmulF_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJAssemblerGmovsbl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerGmovswl6MpnMRegisterImpl_2_v_; +text: .text%__1cLOptoRuntimebDlazy_c2i_adapter_generation_C6FpnKJavaThread__pC_; +text: .text%__1cLOptoRuntimeVgenerate_handler_blob6FpCi_pnNSafepointBlob__; +text: .text%__1cRaddL_mem_rRegNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJAssemblerGmovzwl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerFmovdq6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cRComputeEntryStackIdo_float6M_v_; +text: .text%__1cJAssemblerFcmovl6Mn0AJCondition_pnMRegisterImpl_nHAddress__v_; +text: .text%__1cSReferenceProcessor2t6MnJMemRegion_iii_v_; +text: .text%__1cJAssemblerEaddl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cOGenerateOopMapTadd_to_ref_init_set6Mi_v_; +text: .text%__1cJAssemblerEcmpq6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerHucomisd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cJAssemblerFidivl6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerFidivq6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEcdql6M_v_; +text: .text%__1cJAssemblerEcdqq6M_v_; +text: .text%__1cJAssemblerEleal6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerDorq6MnHAddress_i_v_; +text: .text%__1cWStubGenerator_generate6FpnKCodeBuffer_i_v_; +text: .text%__1cJStubQdDueue2t6MpnNStubInterface_ipnFMutex_pkc_v_; +text: .text%__1cMGCTaskThreadDrun6M_v_; +text: .text%__1cMGCTaskThreadFstart6M_v_; +text: .text%__1cMGCTaskThread2t6MpnNGCTaskManager_II_v_; +text: .text%__1cJStubQdDueueOregister_queue6Fp0_v_; +text: .text%__1cISubFNodeDsub6kMpknEType_3_3_; +text: .text%__1cJAssemblerFxaddl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cNGCTaskManagerKset_thread6MIpnMGCTaskThread__v_; +text: .text%__1cJAssemblerHldmxcsr6MnHAddress__v_; +text: .text%__1cJAssemblerFxorps6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cKcastPPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMPeriodicTask2t6ML_v_; +text: .text%__1cMPeriodicTaskGenroll6M_v_; +text: .text%__1cPOopTaskQdDueueSetOregister_queue6MipnMOopTaskQdDueue__v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC222i_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC22_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fi_v_; +text: .text%__1cNTemplateTableKdouble_cmp6Fi_v_; +text: .text%__1cNTemplateTableKinitialize6F_v_; +text: .text%__1cNTemplateTableGlconst6Fi_v_; +text: .text%__1cNTemplateTableGdconst6Fi_v_; +text: .text%__1cNTemplateTableDldc6Fi_v_; +text: .text%__1cNTemplateTableHcastore6F_v_; +text: .text%__1cPdouble_quadword6Fpxxx_0_: templateTable_amd64.o; +text: .text%__1cNTemplateTableKif_nullcmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_acmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableSgetfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableUjvmti_post_field_mod6Fii_v_; +text: .text%__1cNTemplateTableSputfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableUinvokevirtual_helper6FpnMRegisterImpl_22_v_; +text: .text%__1cEMIN24CL_6FTA0_0_; +text: .text%__1cRCardTableModRefBSbCpar_chunk_heapword_alignment6F_L_; +text: .text%__1cOMacroAssemblerPcorrected_idivl6MpnMRegisterImpl__i_; +text: .text%__1cOMacroAssemblerPcorrected_idivq6MpnMRegisterImpl__i_; +text: .text%__1cLNamedThread2t6M_v_; +text: .text%__1cLNamedThreadIset_name6MpkcE_v_; +text: .text%__1cOMacroAssemblerQserialize_memory6MpnMRegisterImpl_22_v_; +text: .text%__1cIDivDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIDivDNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFStatebB_sub_Op_PartialSubtypeCheck6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_DivI6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_DivD6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_ConvL2F6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_ConvL2D6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_ConvF2I6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_ConvD2F6MpknENode__v_; +text: .text%__1cRcheck_if_clipping6FpknKRegionNode_rpnGIfNode_5_i_: cfgnode.o; +text: .text%__1cWcheck_compare_clipping6FipnGIfNode_pnHConNode_rpnENode__i_: cfgnode.o; +text: .text%__1cIciObjectOis_array_klass6M_i_; +text: .text%__1cScompP_rReg_memNodeFreloc6kM_i_; +text: .text%__1cKCastPPNodeJideal_reg6kM_I_; +text: .text%__1cLMoveF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFTypeDJis_finite6kM_i_; +text: .text%__1cLMoveL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTconvL2D_reg_memNodeFreloc6kM_i_; +text: .text%__1cMdivD_immNodeFreloc6kM_i_; +text: .text%__1cMmulF_memNodeFreloc6kM_i_; +text: .text%__1cMaddF_memNodeFreloc6kM_i_; +text: .text%__1cLConvF2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLConvF2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLConvD2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOcompP_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cKciTypeFlowLStateVectorRdo_multianewarray6MpnQciBytecodeStream__v_; +text: .text%__1cMorI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cRCollectorCounters2t6Mpkci_v_; +text: .text%Unsafe_CompareAndSwapObject; +text: .text%__1cNSafepointBlob2n6FLI_pv_; +text: .text%__1cNSafepointBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNSafepointBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cINegFNodeJideal_reg6kM_I_; +text: .text%__1cMVirtualSpace2t6M_v_; +text: .text%__1cMVirtualSpaceKinitialize6MnNReservedSpace_L_i_; +text: .text%__1cHMatcherQconvL2FSupported6F_ki_; +text: .text%__1cLConvD2FNodeJideal_reg6kM_I_; +text: .text%__1cLConvF2INodeJideal_reg6kM_I_; +text: .text%__1cLConvL2DNodeJideal_reg6kM_I_; +text: .text%__1cLConvL2FNodeJideal_reg6kM_I_; +text: .text%__1cXPartialSubtypeCheckNodeJideal_reg6kM_I_; +text: .text%__1cTAbstract_VM_VersionOvm_info_string6F_pkc_; +text: .text%__1cTAbstract_VM_VersionKvm_release6F_pkc_; +text: .text%__1cTAbstract_VM_VersionXinternal_vm_info_string6F_pkc_; +text: .text%__1cJAssemblerEshrq6MpnMRegisterImpl__v_; +text: .text%__1cMsubF_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cParrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerEsubq6MnHAddress_i_v_; +text: .text%__1cLOptoRuntimeIgenerate6FpnFciEnv__v_; +text: .text%__1cMmulD_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cYSurvivorMutableSpacePool2t6MpnKPSYoungGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cParrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassUoop_is_instanceKlass6kM_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: arguments.o; +text: .text%__1cJArgumentsWPropertyList_get_value6FpnOSystemProperty_pkc_4_; +text: .text%__1cJArgumentsFparse6FpknOJavaVMInitArgs__i_; +text: .text%__1cKNoopGCTaskQcreate_on_c_heap6F_p0_; +text: .text%__1cJArgumentsbSparse_java_tool_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cWResolveOopMapConflictsOreport_results6kM_i_; +text: .text%__1cMCodeHeapPool2t6MpnICodeHeap_pkci_v_; +text: .text%__1cJAssemblerFxchgl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJArgumentsbNparse_java_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJAssemblerFxchgq6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJArgumentsVfinalize_vm_init_args6FpnMSysClassPath_i_i_; +text: .text%__1cJArgumentsWparse_each_vm_init_arg6FpknOJavaVMInitArgs_pnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsSparse_vm_init_args6FpknOJavaVMInitArgs__i_; +text: .text%__1cJAssemblerIcmpxchgl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cINegFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJArgumentsZcheck_vm_args_consistency6F_i_; +text: .text%__1cICodeHeap2t6M_v_; +text: .text%__1cJArgumentsbOparse_java_compiler_environment_variable6F_v_; +text: .text%__1cHVM_ExitNset_vm_exited6F_i_; +text: .text%__1cICodeHeapHreserve6MLLL_i_; +text: .text%__1cQRelocationHolder2t6M_v_; +text: .text%__1cICodeHeapFclear6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: relocInfo.o; +text: .text%__1cLsymbolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cJArgumentsSset_bytecode_flags6F_v_; +text: .text%__1cJArgumentsUset_ergonomics_flags6F_v_; +text: .text%__1cJArgumentsbBset_cms_and_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsTset_parnew_gc_flags6F_v_; +text: .text%__1cQno_shared_spaces6F_v_: arguments.o; +text: .text%__1cJArgumentsMget_property6Fpkc_2_; +text: .text%__1cLsymbolKlassOset_alloc_size6MI_v_; +text: .text%__1cLsymbolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cJArgumentsVprocess_settings_file6Fpkcii_i_; +text: .text%__1cNGCTaskManagerKthreads_do6MpnNThreadClosure__v_; +text: .text%__1cNGCTaskManagerKinitialize6M_v_; +text: .text%__1cNGCTaskManager2t6MI_v_; +text: .text%__1cXSynchronizedGCTaskQdDueue2t6MpnLGCTaskQdDueue_pnFMutex__v_; +text: .text%__1cDhpiKinitialize6F_i_; +text: .text%__1cDhpiZinitialize_socket_library6F_i_; +text: .text%__1cDhpiYinitialize_get_interface6FpnIvm_calls__v_; +text: .text%__1cRInlineCacheBufferKinitialize6F_v_; +text: .text%__1cWInlineCacheBuffer_init6F_v_; +text: .text%__1cJAssemblerHclflush6MnHAddress__v_; +text: .text%__1cOAbstractICacheKinitialize6F_v_; +text: .text%__1cLGCTaskQdDueueQcreate_on_c_heap6F_p0_; +text: .text%__1cMSysClassPathQadd_jars_to_path6Fpcpkc_1_; +text: .text%__1cHGCStats2t6M_v_; +text: .text%__1cQGCPolicyCounters2t6Mpkcii_v_; +text: .text%__1cMSysClassPathPexpand_endorsed6M_v_; +text: .text%__1cMSysClassPath2T6M_v_; +text: .text%__1cLicache_init6F_v_; +text: .text%__1cYGCAdaptivePolicyCounters2t6MpkciipnSAdaptiveSizePolicy__v_; +text: .text%__1cHVM_ExitbJwait_for_threads_in_native_to_block6F_i_; +text: .text%__1cJAssemblerHstmxcsr6MnHAddress__v_; +text: .text%__1cJAssemblerFaddss6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerFsubss6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cTICacheStubGeneratorVgenerate_icache_flush6MppFpCii_i_v_; +text: .text%__1cMSysClassPath2t6Mpkc_v_; +text: .text%__1cJArgumentsWinit_system_properties6F_v_; +text: .text%__1cJAssemblerFmulss6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerFdivss6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cJAssemblerFaddsd6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cFChunkbDstart_chunk_pool_cleaner_task6F_v_; +text: .text%__1cJAssemblerFsubsd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cOchunkpool_init6F_v_; +text: .text%__1cJAssemblerFmulsd6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerFdivsd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cQSystemDictionarybAcompute_java_system_loader6FpnGThread__v_; +text: .text%__1cJAssemblerGsqrtsd6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cHVM_ExitEdoit6M_v_; +text: .text%__1cRArgumentOopFinderDset6MinJBasicType__v_; +text: .text%__1cWAdjoiningVirtualSpaces2t6MnNReservedSpace_LLL_v_; +text: .text%__1cUAdjoiningGenerations2t6MnNReservedSpace_LLLLLLL_v_; +text: .text%__1cHOrLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cZCompiledArgumentOopFinderRhandle_oop_offset6M_v_; +text: .text%__1cJAssemblerFxorps6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerFxorpd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cJAssemblerFxorpd6MpnRFloatRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerJcvtsi2ssl6MpnRFloatRegisterImpl_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerJcvtsi2ssq6MpnRFloatRegisterImpl_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerJcvtsi2sdl6MpnRFloatRegisterImpl_pnMRegisterImpl__v_; +text: .text%__1cFframebAoops_compiled_arguments_do6MnMsymbolHandle_ipknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cJAssemblerJcvtsi2sdq6MpnRFloatRegisterImpl_pnMRegisterImpl__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: adaptiveSizePolicy.o; +text: .text%__1cSAdaptiveSizePolicy2t6ML_v_; +text: .text%__1cFframebDoops_interpreted_arguments_do6MnMsymbolHandle_ipnKOopClosure__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: regmask.o; +text: .text%__1cJAssemblerKcvttss2sil6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cJAssemblerKcvttss2siq6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: indexSet.o; +text: .text%__1cJAssemblerKcvttsd2sil6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cFframeVinterpreter_frame_mdp6kM_pC_; +text: .text%__1cPvm_init_globals6F_v_; +text: .text%__1cJAssemblerKcvttsd2siq6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cQSystemDictionaryKclasses_do6FpFpnMklassOopDesc__v_v_; +text: .text%__1cQSystemDictionaryKmethods_do6FpFpnNmethodOopDesc__v_v_; +text: .text%__1cQSystemDictionaryKinitialize6FpnGThread__v_; +text: .text%__1cQSystemDictionarybCinitialize_preloaded_classes6FpnGThread__v_; +text: .text%__1cQSystemDictionarybDinitialize_basic_type_mirrors6FpnGThread__v_; +text: .text%__1cJAssemblerIcvtss2sd6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cJAssemblerIcvtsd2ss6MpnRFloatRegisterImpl_2_v_; +text: .text%__1cMinit_globals6F_i_; +text: .text%__1cMexit_globals6F_v_; +text: .text%__1cOMacroAssemblerKdecrementl6MpnMRegisterImpl_i_v_; +text: .text%__1cHVM_ExitEname6kM_pkc_; +text: .text%__1cKcastPPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: fprofiler.o; +text: .text%__1cNMemoryServiceRset_universe_heap6FpnNCollectedHeap__v_; +text: .text%__1cMPeriodicTask2T5B6M_v_; +text: .text%__1cMPeriodicTaskLis_enrolled6kM_i_; +text: .text%__1cNMemoryServicebFadd_parallel_scavenge_heap_info6FpnUParallelScavengeHeap__v_; +text: .text%__1cMPeriodicTaskJdisenroll6M_v_; +text: .text%__1cSset_init_completed6F_v_; +text: .text%__1cMadapter_init6F_v_; +text: .text%__1cTI2CAdapterGeneratorKinitialize6F_v_; +text: .text%__1cNMemoryServiceXadd_psYoung_memory_pool6FpnKPSYoungGen_pnNMemoryManager_4_v_; +text: .text%__1cTC2IAdapterGeneratorKinitialize6F_v_; +text: .text%__1cOstackSlotPOperFclone6kM_pnIMachOper__; +text: .text%__1cObox_handleNodeFclone6kM_pnENode__; +text: .text%__1cTAbstract_VM_VersionHvm_name6F_pkc_; +text: .text%__1cTAbstract_VM_VersionJvm_vendor6F_pkc_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_amd64_pipeline.o; +text: .text%__1cSobjArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC222_v_; +text: .text%__1cKmutex_init6F_v_; +text: .text%__1cHnmethodbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cNTemplateTableF_goto6F_v_; +text: .text%__1cNTemplateTableGgoto_w6F_v_; +text: .text%__1cNTemplateTableFjsr_w6F_v_; +text: .text%__1cNTemplateTableDjsr6F_v_; +text: .text%__1cNinstanceKlassZrelease_C_heap_structures6M_v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCpnMRegisterImpl_3_v_; +text: .text%__1cFJNIidKdeallocate6Fp0_v_; +text: .text%__1cSinstanceKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vm_version.o; +text: .text%__1cStemplateTable_init6F_v_; +text: .text%__1cNTemplateTableNpd_initialize6F_v_; +text: .text%__1cSinstanceKlassKlassUoop_is_instanceKlass6kM_i_; +text: .text%__1cNTemplateTableDnop6F_v_; +text: .text%__1cNTemplateTableSshouldnotreachhere6F_v_; +text: .text%__1cNTemplateTableLaconst_null6F_v_; +text: .text%__1cSinstanceKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cKVM_VersionKinitialize6F_v_; +text: .text%__1cQinstanceRefKlassZupdate_nonstatic_oop_maps6FpnMklassOopDesc__v_; +text: .text%__1cPVM_Version_init6F_v_; +text: .text%__1cNTemplateTableGbipush6F_v_; +text: .text%__1cNTemplateTableGsipush6F_v_; +text: .text%__1cYVM_Version_StubGeneratorTgenerate_getPsrInfo6M_pC_; +text: .text%__1cNTemplateTableGldc2_w6F_v_; +text: .text%__1cNTemplateTableFiload6F_v_; +text: .text%__1cNTemplateTableLfast_iload26F_v_; +text: .text%__1cNTemplateTableKfast_iload6F_v_; +text: .text%__1cNTemplateTableFlload6F_v_; +text: .text%__1cNTemplateTableFfload6F_v_; +text: .text%__1cNTemplateTableFdload6F_v_; +text: .text%__1cNTemplateTableFaload6F_v_; +text: .text%__1cVInterfaceSupport_init6F_v_; +text: .text%__1cNTemplateTableKwide_iload6F_v_; +text: .text%__1cNTemplateTableKwide_lload6F_v_; +text: .text%__1cNTemplateTableKwide_fload6F_v_; +text: .text%__1cNTemplateTableKwide_dload6F_v_; +text: .text%__1cNTemplateTableKwide_aload6F_v_; +text: .text%__1cScheck_ThreadShadow6F_v_; +text: .text%__1cNTemplateTableGiaload6F_v_; +text: .text%__1cNTemplateTableGlaload6F_v_; +text: .text%__1cNTemplateTableGfaload6F_v_; +text: .text%__1cNTemplateTableGdaload6F_v_; +text: .text%__1cNTemplateTableGaaload6F_v_; +text: .text%__1cNTemplateTableGbaload6F_v_; +text: .text%__1cNTemplateTableGcaload6F_v_; +text: .text%__1cNTemplateTableMfast_icaload6F_v_; +text: .text%__1cNTemplateTableGsaload6F_v_; +text: .text%__1cSReferenceProcessorMinit_statics6F_v_; +text: .text%__1cXreferenceProcessor_init6F_v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpCpnMRegisterImpl_33_v_; +text: .text%__1cURecompilationMonitorbGstart_recompilation_monitor_task6F_v_; +text: .text%__1cZInterpreterMacroAssemblerUdispatch_only_normal6MnITosState__v_; +text: .text%__1cNTemplateTableHaload_06F_v_; +text: .text%__1cNTemplateTableGistore6F_v_; +text: .text%__1cNTemplateTableGlstore6F_v_; +text: .text%__1cNTemplateTableGfstore6F_v_; +text: .text%__1cNTemplateTableGdstore6F_v_; +text: .text%__1cNTemplateTableGastore6F_v_; +text: .text%__1cNTemplateTableLwide_istore6F_v_; +text: .text%__1cNTemplateTableLwide_lstore6F_v_; +text: .text%__1cNTemplateTableLwide_fstore6F_v_; +text: .text%__1cNTemplateTableLwide_dstore6F_v_; +text: .text%__1cNTemplateTableLwide_astore6F_v_; +text: .text%__1cNTemplateTableHiastore6F_v_; +text: .text%__1cNTemplateTableHlastore6F_v_; +text: .text%__1cNTemplateTableHfastore6F_v_; +text: .text%__1cNTemplateTableHdastore6F_v_; +text: .text%__1cNTemplateTableHaastore6F_v_; +text: .text%__1cNTemplateTableHbastore6F_v_; +text: .text%__1cNMemoryServiceVadd_psOld_memory_pool6FpnIPSOldGen_pnNMemoryManager__v_; +text: .text%__1cNTemplateTableHsastore6F_v_; +text: .text%__1cZInterpreterMacroAssemblerbFset_method_data_pointer_for_bcp6M_v_; +text: .text%__1cKPSYoungGenUset_space_boundaries6MLL_v_; +text: .text%__1cKPSYoungGenbGcompute_initial_space_boundaries6M_v_; +text: .text%__1cKPSYoungGenPinitialize_work6M_v_; +text: .text%__1cKPSYoungGenKinitialize6MnNReservedSpace_L_v_; +text: .text%__1cNTemplateTableDpop6F_v_; +text: .text%__1cNTemplateTableEpop26F_v_; +text: .text%__1cNTemplateTableDdup6F_v_; +text: .text%__1cNTemplateTableGdup_x16F_v_; +text: .text%__1cNTemplateTableGdup_x26F_v_; +text: .text%__1cNTemplateTableEdup26F_v_; +text: .text%__1cNTemplateTableHdup2_x16F_v_; +text: .text%__1cNTemplateTableHdup2_x26F_v_; +text: .text%__1cNTemplateTableEswap6F_v_; +text: .text%__1cKPSYoungGenYinitialize_virtual_space6MnNReservedSpace_L_v_; +text: .text%__1cKPSYoungGen2t6MLLL_v_; +text: .text%__1cNTemplateTableEidiv6F_v_; +text: .text%__1cNTemplateTableEirem6F_v_; +text: .text%__1cNTemplateTableElmul6F_v_; +text: .text%__1cNTemplateTableEldiv6F_v_; +text: .text%__1cNTemplateTableElrem6F_v_; +text: .text%__1cNTemplateTableElshl6F_v_; +text: .text%__1cNTemplateTableElshr6F_v_; +text: .text%__1cNTemplateTableFlushr6F_v_; +text: .text%__1cRaddL_rReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRaddL_mem_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableEineg6F_v_; +text: .text%__1cNTemplateTableElneg6F_v_; +text: .text%__1cLVtableStubsKinitialize6F_v_; +text: .text%__1cNTemplateTableEfneg6F_v_; +text: .text%__1cNTemplateTableEdneg6F_v_; +text: .text%__1cNTemplateTableEiinc6F_v_; +text: .text%__1cNTemplateTableJwide_iinc6F_v_; +text: .text%__1cMincL_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableElcmp6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psScavenge.o; +text: .text%__1cKPSScavengeKinitialize6F_v_; +text: .text%__1cQinterpreter_init6F_v_; +text: .text%__1cbCAbstractInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cNMemoryServiceWadd_psPerm_memory_pool6FpnJPSPermGen_pnNMemoryManager__v_; +text: .text%__1cNMemoryServiceZadd_code_heap_memory_pool6FpnICodeHeap__v_; +text: .text%__1cNTemplateTableDret6F_v_; +text: .text%__1cNTemplateTableIwide_ret6F_v_; +text: .text%__1cNTemplateTableLtableswitch6F_v_; +text: .text%__1cNTemplateTableMlookupswitch6F_v_; +text: .text%__1cNTemplateTableRfast_linearswitch6F_v_; +text: .text%__1cNTemplateTableRfast_binaryswitch6F_v_; +text: .text%__1cbCAbstractInterpreterGeneratorMgenerate_all6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbEset_entry_points_for_all_bytes6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbCset_safepoints_for_all_bytes6M_v_; +text: .text%__1cOsalI_mem_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSPSPromotionManagerKinitialize6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: memoryService.o; +text: .text%__1cNTemplateTableIgetfield6Fi_v_; +text: .text%__1cNTemplateTableJgetstatic6Fi_v_; +text: .text%__1cNGrowableArray4CpnNMemoryManager__2t6Mii_v_; +text: .text%__1cNGrowableArray4CpnKMemoryPool__2t6Mii_v_; +text: .text%__1cNTemplateTableIputfield6Fi_v_; +text: .text%__1cNTemplateTableJputstatic6Fi_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psPromotionLAB.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter.o; +text: .text%__1cJPSPermGen2t6MnNReservedSpace_LLLLpkci_v_; +text: .text%__1cXNativeSignatureIteratorHdo_byte6M_v_; +text: .text%__1cIPSOldGen2t6MLLLpkci_v_; +text: .text%__1cPmethodDataKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cNTemplateTableNinvokevirtual6Fi_v_; +text: .text%__1cNTemplateTableNinvokespecial6Fi_v_; +text: .text%__1cNTemplateTableMinvokestatic6Fi_v_; +text: .text%__1cNTemplateTableRfast_invokevfinal6Fi_v_; +text: .text%__1cNTemplateTablePinvokeinterface6Fi_v_; +text: .text%__1cNTemplateTableE_new6F_v_; +text: .text%__1cNTemplateTableInewarray6F_v_; +text: .text%__1cNTemplateTableJanewarray6F_v_; +text: .text%__1cNTemplateTableLarraylength6F_v_; +text: .text%__1cNTemplateTableJcheckcast6F_v_; +text: .text%__1cNTemplateTableKinstanceof6F_v_; +text: .text%__1cNTemplateTableL_breakpoint6F_v_; +text: .text%__1cNTemplateTableGathrow6F_v_; +text: .text%__1cNTemplateTableMmonitorenter6F_v_; +text: .text%__1cNTemplateTableLmonitorexit6F_v_; +text: .text%__1cNTemplateTableEwide6F_v_; +text: .text%__1cNTemplateTableOmultianewarray6F_v_; +text: .text%__1cIPSOldGen2t6MnNReservedSpace_LLLLpkci_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: tenuredGeneration.o; +text: .text%__1cQvtableStubs_init6F_v_; +text: .text%__1cQaccessFlags_init6F_v_; +text: .text%__1cSInterpreterRuntimeYthrow_ClassCastException6FpnKJavaThread_pnHoopDesc__v_; +text: .text%__1cSInterpreterRuntimeSupdate_mdp_for_ret6FpnKJavaThread_i_v_; +text: .text%__1cNeventlog_init6F_v_; +text: .text%__1cOMacroAssemblerGc2bool6MpnMRegisterImpl__v_; +text: .text%__1cPmethodDataKlassOset_alloc_size6MI_v_; +text: .text%__1cFVTuneEexit6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psMarkSweep.o; +text: .text%__1cTPSAlwaysTrueClosure2t6M_v_: psMarkSweep.o; +text: .text%__1cXSignatureHandlerLibraryQset_handler_blob6F_pC_; +text: .text%__1cNGrowableArray4CpC_2t6Mii_v_; +text: .text%__1cNGrowableArray4CL_2t6Mii_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_StackOverflowError_handler6M_pC_; +text: .text%__1cOMacroAssemblerRsign_extend_short6MpnMRegisterImpl__v_; +text: .text%__1cbCAbstractInterpreterGeneratorbMgenerate_ArrayIndexOutOfBounds_handler6Mpkc_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_ClassCastException_handler6M_pC_; +text: .text%__1cGThreadWset_as_starting_thread6M_i_; +text: .text%__1cLPSMarkSweepKinitialize6F_v_; +text: .text%__1cbBcreate_initial_thread_group6FpnGThread__nGHandle__: thread.o; +text: .text%__1cVcreate_initial_thread6FnGHandle_pnKJavaThread_pnGThread__pnHoopDesc__: thread.o; +text: .text%__1cbAcall_initializeSystemClass6FpnGThread__v_: thread.o; +text: .text%__1cWreset_vm_info_property6FpnGThread__v_: thread.o; +text: .text%__1cPmethodDataKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cHRetDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cNWatcherThread2t6M_v_; +text: .text%__1cNWatcherThreadDrun6M_v_; +text: .text%__1cNWatcherThreadFstart6F_v_; +text: .text%__1cNWatcherThreadEstop6F_v_; +text: .text%__1cOMacroAssemblerQsign_extend_byte6MpnMRegisterImpl__v_; +text: .text%__1cKJavaThread2t6M_v_; +text: .text%__1cHRetDataJfixup_ret6MinQmethodDataHandle__pC_; +text: .text%__1cKvtune_init6F_v_; +text: .text%__1cLmethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cbAPSGCAdaptivePolicyCounters2t6MpkciipnUPSAdaptiveSizePolicy__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psAdaptiveSizePolicy.o; +text: .text%__1cKDictionaryKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cKDictionaryKclasses_do6MpFpnMklassOopDesc__v_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbFgenerate_slow_signature_handler6M_pC_; +text: .text%__1cUInterpreterGeneratorTgenerate_math_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cUInterpreterGeneratorXgenerate_abstract_entry6M_pC_; +text: .text%__1cUInterpreterGeneratorUgenerate_empty_entry6M_pC_; +text: .text%__1cKDictionaryKfree_entry6MpnPDictionaryEntry__v_; +text: .text%__1cUInterpreterGeneratorXgenerate_accessor_entry6M_pC_; +text: .text%__1cKDictionary2t6Mi_v_; +text: .text%__1cJBytecodesKinitialize6F_v_; +text: .text%__1cObytecodes_init6F_v_; +text: .text%__1cUPSAdaptiveSizePolicy2t6MLLLLLddI_v_; +text: .text%__1cJBytecodesNpd_initialize6F_v_; +text: .text%__1cHCompileRpd_compiler2_init6F_v_; +text: .text%__1cKC2CompilerKinitialize6M_v_; +text: .text%__1cFStateQ_sub_Op_TailJump6MpknENode__v_; +text: .text%__1cMorL_rRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_throw_exception6M_v_; +text: .text%__1cUInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cWinvocationCounter_init6F_v_; +text: .text%__1cQPlaceholderTable2t6Mi_v_; +text: .text%__1cHThreadsJcreate_vm6FpnOJavaVMInitArgs_pi_i_; +text: .text%__1cFStateL_sub_Op_OrL6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_NegF6MpknENode__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_amd64_expand.o; +text: .text%__1cQprint_statistics6F_v_; +text: .text%__1cLbefore_exit6FpnKJavaThread__v_; +text: .text%__1cFStateP_sub_Op_MoveL2D6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_MoveF2I6MpknENode__v_; +text: .text%__1cHvm_exit6Fi_v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cQjava_lang_ThreadPcompute_offsets6F_v_; +text: .text%__1cVjava_lang_ThreadGroupPcompute_offsets6F_v_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectPcompute_offsets6F_v_; +text: .text%__1cYjava_lang_reflect_MethodPcompute_offsets6F_v_; +text: .text%__1cOThreadCriticalKinitialize6F_v_; +text: .text%__1cRAllocateTLSOffset6F_v_: threadLS_solaris_amd64.o; +text: .text%__1cSThreadLocalStoragebCgenerate_code_for_get_thread6F_v_; +text: .text%__1cYjava_lang_reflect_MethodNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cYsun_reflect_ConstantPoolPcompute_offsets6F_v_; +text: .text%__1cbIjava_security_AccessControlContextPcompute_offsets6F_v_; +text: .text%__1cQjava_lang_SystemPcompute_offsets6F_v_; +text: .text%__1cPjava_nio_BufferPcompute_offsets6F_v_; +text: .text%__1cFStateO_sub_Op_CMoveD6MpknENode__v_; +text: .text%__1cZsun_misc_AtomicLongCSImplPcompute_offsets6F_v_; +text: .text%__1cFStateO_sub_Op_CastPP6MpknENode__v_; +text: .text%__1cWThreadLocalAllocBufferWstartup_initialization6F_v_; +text: .text%__1cLJavaClassesbAcompute_hard_coded_offsets6F_v_; +text: .text%__1cLJavaClassesPcompute_offsets6F_v_; +text: .text%__1cPGlobalTLABStats2t6M_v_; +text: .text%__1cQjavaClasses_init6F_v_; +text: .text%__1cMTailJumpNode2t6MpnENode_22222_v_; +text: .text%jni_ToReflectedMethod: jni.o; +text: .text%__1cSThreadLocalStorageEinit6F_v_; +text: .text%__1cNThreadServiceEinit6F_v_; +text: .text%__1cYjni_GetBooleanField_addr6F_pC_; +text: .text%__1cVjni_GetByteField_addr6F_pC_; +text: .text%__1cVjni_GetCharField_addr6F_pC_; +text: .text%__1cWjni_GetShortField_addr6F_pC_; +text: .text%__1cUjni_GetIntField_addr6F_pC_; +text: .text%__1cVjni_GetLongField_addr6F_pC_; +text: .text%__1cWjni_GetFloatField_addr6F_pC_; +text: .text%__1cRCardTableModRefBS2t6MnJMemRegion_i_v_; +text: .text%__1cXjni_GetDoubleField_addr6F_pC_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: phase.o; +text: .text%__1cJTimeStampMmilliseconds6kM_x_; +text: .text%__1cTConstantDoubleValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cKPerfMemoryUdelete_memory_region6F_v_; +text: .text%__1cKPerfMemoryUcreate_memory_region6FL_v_; +text: .text%__1cRCardTableModRefBSbBct_max_alignment_constraint6F_L_; +text: .text%__1cUdelete_shared_memory6FpcL_v_: perfMemory_solaris.o; +text: .text%__1cUcreate_shared_memory6FL_pc_: perfMemory_solaris.o; +text: .text%__1cOtailjmpIndNodeFreloc6kM_i_; +text: .text%__1cSmmap_create_shared6FL_pc_: perfMemory_solaris.o; +text: .text%__1cETypeRInitialize_shared6FpnHCompile__v_; +text: .text%__1cWconstantPoolCacheKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cbAcreate_sharedmem_resources6Fpkc1L_i_: perfMemory_solaris.o; +text: .text%__1cWconstantPoolCacheKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cRmake_user_tmp_dir6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cbBcleanup_sharedmem_resources6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cMciArrayKlass2t6MpnIciSymbol_ipnHciKlass__v_; +text: .text%__1cLremove_file6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cWget_sharedmem_filename6Fpkci_pc_: perfMemory_solaris.o; +text: .text%__1cNget_user_name6Fi_pc_: perfMemory_solaris.o; +text: .text%__1cQget_user_tmp_dir6Fpkc_pc_: perfMemory_solaris.o; +text: .text%__1cRconstantPoolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cFciEnvXget_or_create_exception6MrpnI_jobject_nMsymbolHandle__pnKciInstance__; +text: .text%__1cMloadConFNodeGis_Con6kM_I_; +text: .text%__1cRconstantPoolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cKPerfMemoryHdestroy6F_v_; +text: .text%__1cQconstMethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cKPerfMemoryKinitialize6F_v_; +text: .text%__1cPperfMemory_exit6F_v_; +text: .text%__1cQconstMethodKlassOset_alloc_size6MI_v_; +text: .text%__1cPperfMemory_init6F_v_; +text: .text%__1cNGrowableArray4CpnIPerfData__JappendAll6Mpk2_v_; +text: .text%__1cMPerfDataListFclone6M_p0_; +text: .text%__1cQconstMethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cMciKlassKlassEmake6F_p0_; +text: .text%__1cMPerfDataList2t6Mp0_v_; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnPPerfLongCounter__; +text: .text%__1cPPerfDataManagerHsampled6F_pnMPerfDataList__; +text: .text%__1cMmulD_memNodeFreloc6kM_i_; +text: .text%__1cPPerfDataManagerHdestroy6F_v_; +text: .text%__1cMsubD_immNodeFreloc6kM_i_; +text: .text%__1cMsubF_memNodeFreloc6kM_i_; +text: .text%lookupDirectBufferClasses: jni.o; +text: .text%__1cbDinitializeDirectBufferSupport6FpnHJNIEnv___i_: jni.o; +text: .text%__1cVquicken_jni_functions6F_v_; +text: .text%JNI_CreateJavaVM; +text: .text%__1cFParseWprofile_null_checkcast6M_v_; +text: .text%__1cQJNI_FastGetFieldbFgenerate_fast_get_boolean_field6F_pC_; +text: .text%__1cOsalI_mem_1NodeFreloc6kM_i_; +text: .text%__1cIciMethodMvtable_index6M_i_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_byte_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_char_field6F_pC_; +text: .text%__1cMmulI_memNodeFreloc6kM_i_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_short_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbBgenerate_fast_get_int_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_long_field6F_pC_; +text: .text%__1cMincL_memNodeFreloc6kM_i_; +text: .text%__1cRaddL_mem_rRegNodeFreloc6kM_i_; +text: .text%__1cRaddL_mem_rRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMincL_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_float_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_double_field6F_pC_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jniFastGetField_amd64.o; +text: .text%__1cNcmovL_memNodeFreloc6kM_i_; +text: .text%__1cKJNIHandlesKinitialize6F_v_; +text: .text%__1cQjni_handles_init6F_v_; +text: .text%JVM_InitProperties; +text: .text%JVM_Halt; +text: .text%JVM_MaxMemory; +text: .text%JVM_GetClassDeclaredMethods; +text: .text%__1cKCMoveDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOsalI_mem_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHRetDataKis_RetData6M_i_; +text: .text%JVM_InitializeSocketLibrary; +text: .text%JVM_Socket; +text: .text%__1cPciObjArrayKlass2t6MpnIciSymbol_pnHciKlass_i_v_; +text: .text%__1cPOopTaskQdDueueSet2t6Mi_v_; +text: .text%__1cbEinitialize_converter_functions6F_v_; +text: .text%JVM_SupportsCX8; +text: .text%__1cUciObjArrayKlassKlassEmake6F_p0_; +text: .text%__1cTcompilerOracle_init6F_v_; +text: .text%__1cOCompilerOracleRparse_from_string6Fpkc_v_; +text: .text%__1cOCompilerOraclePparse_from_file6F_v_; +text: .text%__1cHcc_file6F_pkc_: compilerOracle.o; +text: .text%__1cKTypeOopPtrEmake6FnHTypePtrDPTR_i_pk0_; +text: .text%__1cKTypeOopPtrFxdual6kM_pknEType__; +text: .text%__1cOCompilerOracleOread_from_line6Fpc_v_; +text: .text%__1cPciObjectFactoryTinit_shared_objects6M_v_; +text: .text%__1cVcompiledICHolderKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cVcompiledICHolderKlassOset_alloc_size6MI_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEnvBase.o; +text: .text%__1cVcompiledICHolderKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cNGrowableArray4CpnMJvmtiEnvBase__2t6Mii_v_; +text: .text%__1cRJvmtiEventEnabled2t6M_v_; +text: .text%__1cRciArrayKlassKlassUis_array_klass_klass6M_i_; +text: .text%__1cRJvmtiEventEnabledFclear6M_v_; +text: .text%__1cNGrowableArray4CpnOCompilerThread__2t6Mii_v_; +text: .text%__1cFParseNfetch_monitor6MipnENode_2_2_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compileBroker.o; +text: .text%__1cNGrowableArray4CpnIciMethod__Egrow6Mi_v_; +text: .text%__1cNCompileBrokerQset_should_block6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_start6F_v_; +text: .text%__1cPGenerationSizerQinitialize_flags6M_v_; +text: .text%__1cUJvmtiEventControllerHvm_init6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_death6F_v_; +text: .text%__1cNCompileBrokerVinit_compiler_threads6Fi_v_; +text: .text%__1cUParallelScavengeHeapbCsupports_inline_contig_alloc6kM_i_; +text: .text%__1cUParallelScavengeHeapItop_addr6kM_ppnIHeapWord__; +text: .text%__1cNCompileBrokerQcompilation_init6FpnQAbstractCompiler__v_; +text: .text%__1cUParallelScavengeHeapIend_addr6kM_ppnIHeapWord__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEventController.o; +text: .text%__1cLJvmtiExportRenter_start_phase6F_v_; +text: .text%__1cUParallelScavengeHeapEheap6F_p0_; +text: .text%__1cUParallelScavengeHeapNgc_threads_do6kMpnNThreadClosure__v_; +text: .text%__1cUParallelScavengeHeapYpermanent_object_iterate6MpnNObjectClosure__v_; +text: .text%__1cLJvmtiExportQenter_live_phase6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_start6F_v_; +text: .text%__1cOcompiler2_init6F_v_; +text: .text%__1cLJvmtiExportTpost_vm_initialized6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_death6F_v_; +text: .text%__1cLJvmtiExportbMtransition_pending_onload_raw_monitors6F_v_; +text: .text%__1cUParallelScavengeHeapMmax_capacity6kM_L_; +text: .text%__1cUJvmtiPendingMonitorsXtransition_raw_monitors6F_v_; +text: .text%__1cMaddF_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cUParallelScavengeHeapPpost_initialize6M_v_; +text: .text%__1cUParallelScavengeHeapKinitialize6M_i_; +text: .text%__1cHoopDescLheader_size6F_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compilationPolicy.o; +text: .text%__1cPClassFileParserbFjava_lang_ref_Reference_fix_pre6MpnPtypeArrayHandle_nSconstantPoolHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cPClassFileParserXjava_lang_Class_fix_pre6MpnOobjArrayHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cPClassFileParserYjava_lang_Class_fix_post6Mpi_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: parGCAllocBuffer.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiImpl.o; +text: .text%__1cNGrowableArray4CpnPJvmtiRawMonitor__2t6Mii_v_; +text: .text%__1cNGrowableArray4CpnPJvmtiRawMonitor__Uclear_and_deallocate6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiTagMap.o; +text: .text%__1cRCompilationPolicyUcompleted_vm_startup6F_v_; +text: .text%__1cOtypeArrayKlassKinitialize6MpnGThread__v_; +text: .text%__SLIP.DELETER__C: ostream.o; +text: .text%__1cWcompilationPolicy_init6F_v_; +text: .text%__1cMostream_exit6F_v_; +text: .text%__1cTtypeArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cTtypeArrayKlassKlassVoop_is_typeArrayKlass6kM_i_; +text: .text%__1cbCTwoGenerationCollectorPolicyMrem_set_name6M_nJGenRemSetEName__; +text: .text%__1cTtypeArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cQostream_init_log6F_v_; +text: .text%__1cIUniversePcheck_alignment6FLLpkc_v_; +text: .text%__1cIUniverseHgenesis6FpnGThread__v_; +text: .text%__1cIUniverseNfixup_mirrors6FpnGThread__v_; +text: .text%__1cMostream_init6F_v_; +text: .text%__1cNdefaultStreamEinit6M_v_; +text: .text%__1cIUniverseUreinitialize_itables6F_v_; +text: .text%__1cKklassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cKklassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cNuniverse_init6F_i_; +text: .text%__1cIUniversePinitialize_heap6F_i_; +text: .text%__1cLClassLoaderbBsetup_bootstrap_search_path6F_v_; +text: .text%__1cOuniverse2_init6F_v_; +text: .text%__1cSuniverse_post_init6F_v_; +text: .text%__1cIUniverseYcompute_base_vtable_size6F_v_; +text: .text%__1cCosXnon_memory_address_word6F_pc_; +text: .text%__1cCosGinit_26F_i_; +text: .text%__1cbCTwoGenerationCollectorPolicyUinitialize_size_info6M_v_; +text: .text%__1cCosEinit6F_v_; +text: .text%__1cCosHSolarisUsynchronization_init6F_v_; +text: .text%Unsafe_SetNativeLong; +text: .text%__1cCosHSolarisOlibthread_init6F_v_; +text: .text%__1cJLoadFNodeMstore_Opcode6kM_i_; +text: .text%__1cOisT2_libthread6F_i_; +text: .text%Unsafe_FreeMemory; +text: .text%__1cCosHSolarisXinstall_signal_handlers6F_v_; +text: .text%Unsafe_PageSize; +text: .text%__1cRlwp_priocntl_init6F_i_: os_solaris.o; +text: .text%__1cNpriocntl_stub6FinGidtype_iipc_l_: os_solaris.o; +text: .text%__1cCosHSolarisRmpss_sanity_check6F_v_; +text: .text%__1cOLibraryCallKitWinline_native_hashcode6Mii_i_; +text: .text%__1cbCTwoGenerationCollectorPolicyQinitialize_flags6M_v_; +text: .text%__1cCosOrelease_memory6FpcL_i_; +text: .text%__1cCosLsignal_wait6F_i_; +text: .text%JVM_RegisterUnsafeMethods; +text: .text%__1cVcheck_pending_signals6Fi_i_: os_solaris.o; +text: .text%__1cCosNsignal_notify6Fi_v_; +text: .text%__1cCosOsignal_init_pd6F_v_; +text: .text%__1cLClassLoaderQload_zip_library6F_v_; +text: .text%__1cLClassLoaderZcreate_package_info_table6F_v_; +text: .text%__1cNmulI_rRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cLClassLoaderKinitialize6F_v_; +text: .text%__1cLClassLoaderVcompute_Object_vtable6F_i_; +text: .text%__1cCosHSolarisPinit_signal_mem6F_v_; +text: .text%__1cNCollectedHeap2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeCache.o; +text: .text%__1cQclassLoader_init6F_v_; +text: .text%__1cCosSget_temp_directory6F_pkc_; +text: .text%__1cVLoaderConstraintTable2t6Mi_v_; +text: .text%__1cCosbDallocate_thread_local_storage6F_i_; +text: .text%__1cOcodeCache_init6F_v_; +text: .text%__1cVverificationType_init6F_v_; +text: .text%__1cVverificationType_exit6F_v_; +text: .text%__1cQVerificationTypeKinitialize6F_v_; +text: .text%__1cQVerificationTypeIfinalize6F_v_; +text: .text%__1cJCodeCacheKinitialize6F_v_; +text: .text%__1cNIdealLoopTreeQsplit_outer_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cKfix_parent6FpnNIdealLoopTree_1_v_: loopnode.o; +text: .text%__1cCosHSolarisQsignal_sets_init6F_v_; +text: .text%__1cTClassLoadingServiceEinit6F_v_; +text: .text%__1cTClassLoadingServiceVnotify_class_unloaded6FpnNinstanceKlass_i_v_; +text: .text%__1cCosScreate_main_thread6FpnGThread__i_; +text: .text%__1cNIdealLoopTreeUmerge_many_backedges6MpnOPhaseIdealLoop__v_; +text: .text%__1cQcreate_os_thread6FpnGThread_I_pnIOSThread__: os_solaris.o; +text: .text%__1cRLowMemoryDetectorKinitialize6F_v_; +text: .text%__1cLmethodKlassOset_alloc_size6MI_v_; +text: .text%__1cNExceptionBlob2n6FLI_pv_; +text: .text%__1cNExceptionBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNExceptionBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cQUncommonTrapBlob2n6FLI_pv_; +text: .text%__1cQUncommonTrapBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cQUncommonTrapBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cSDeoptimizationBlob2n6FLI_pv_; +text: .text%__1cSDeoptimizationBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_iiii_p0_; +text: .text%__1cSDeoptimizationBlob2t6MpnKCodeBuffer_ipnJOopMapSet_iiii_v_; +text: .text%__1cRLowMemoryDetectorUhas_pending_requests6F_i_; +text: .text%__1cCosbDinit_system_properties_values6F_v_; +text: .text%__1cCosHSolarisWinitialize_system_info6F_v_; +text: .text%__1cCosPphysical_memory6F_X_; +text: .text%__1cMFastLockNodeLis_FastLock6kM_pk0_; +text: .text%__1cRLowMemoryDetectorbGlow_memory_detector_thread_entry6FpnKJavaThread_pnGThread__v_; +text: .text%__1cXLowMemoryDetectorThreadbCis_hidden_from_external_view6kM_i_; +text: .text%__1cSThreadLocalStorageHpd_init6F_v_; +text: .text%__1cLmethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: machnode.o; +text: .text%__1cPmanagement_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: cmsAdaptiveSizePolicy.o; +text: .text%__1cKManagementEinit6F_v_; +text: .text%__1cKManagementKinitialize6FpnGThread__v_; +text: .text%__1cKManagementWrecord_vm_startup_time6Fxx_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: management.o; +text: .text%__1cOmarksweep_init6F_v_; +text: .text%__1cCosXis_server_class_machine6F_i_; +text: .text%__1cJMarkSweepQKeepAliveClosure2t6M_v_: markSweep.o; +text: .text%__1cNReservedSpace2t6MLLipc_v_; +text: .text%__1cJMarkSweepOIsAliveClosure2t6M_v_: markSweep.o; +text: .text%__1cCosZset_memory_serialize_page6FpC_v_; +text: .text%__1cCosNset_boot_path6Fcc_i_; +text: .text%__1cJMarkSweepSFollowStackClosure2t6M_v_: markSweep.o; +text: .text%__1cNReservedSpaceUpage_align_size_down6FL_L_; +text: .text%__1cNReservedSpaceYallocation_align_size_up6FL_L_; +text: .text%__1cNGrowableArray4CpnKOSRAdapter__2t6Mii_v_; +text: .text%__1cXonStackReplacement_init6F_v_; +text: .text%__1cSOnStackReplacementKinitialize6F_v_; +text: .text%__1cJMarkSweepRFollowRootClosure2t6M_v_: markSweep.o; +text: .text%__1cCosLinit_random6Fl_v_; +text: .text%__1cHOrLNodeJideal_reg6kM_I_; +text: .text%__1cOvmStructs_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vmStructs.o; +text: .text%__1cJvmSymbolsKinitialize6FpnGThread__v_; +text: .text%__1cJMarkSweepSMarkAndPushClosure2t6M_v_: markSweep.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: markSweep.o; +text: .text%__1cQVMOperationQdDueue2t6M_v_; +text: .text%__1cCosGstrdup6Fpkc_pc_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: methodLiveness.o; +text: .text%__1cCosXterminate_signal_thread6F_v_; +text: .text%__1cCosLsignal_init6F_v_; +text: .text%__1cMsubD_immNodeKconst_size6kM_i_; +text: .text%__1cTsignal_thread_entry6FpnKJavaThread_pnGThread__v_: os.o; +text: .text%__1cOtailjmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSobjArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cHMatcherVfind_callee_arguments6FpnNsymbolOopDesc_ipi_pnLOptoRegPair__; +text: .text%__1cIVMThreadGcreate6F_v_; +text: .text%__1cIVMThread2t6M_v_; +text: .text%__1cNSharedRuntimeUlookup_function_DD_D6FrpFpnHJNIEnv__pnH_jclass_dd_dpkc_v_; +text: .text%__1cNSharedRuntimebIinitialize_StrictMath_entry_points6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedHeap.o; +text: .text%__1cTAssertIsPermClosure2t6M_v_: sharedHeap.o; +text: .text%__1cIVMThreadDrun6M_v_; +text: .text%__1cWResolveOopMapConflictsUdo_potential_rewrite6MpnGThread__nMmethodHandle__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: generateOopMap.o; +text: .text%__1cNCellTypeStateImake_top6F_0_; +text: .text%__1cJAssemblerEandl6MpnMRegisterImpl_2_v_; +text: .text%__1cNCellTypeStateLmake_bottom6F_0_; +text: .text%__1cNcmovL_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRcheck_basic_types6F_v_; +text: .text%__1cLOptoRuntimeYgenerate_arraycopy_stubs6F_v_; +text: .text%__1cSobjArrayKlassKlassUoop_is_objArrayKlass6kM_i_; +text: .text%__1cNSharedRuntimebBgenerate_class_cast_message6FpnKJavaThread_pkc_pc_; +text: .text%__1cNSharedRuntimebBgenerate_class_cast_message6Fpkc2_pc_; +text: .text%__1cLOptoRuntimebPgenerate_polling_page_return_handler_blob6F_v_; +text: .text%__1cIVMThreadEloop6M_v_; +text: .text%__1cLOptoRuntimebSgenerate_polling_page_safepoint_handler_blob6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: matcher.o; +text: .text%__1cLOptoRuntimeUsetup_exception_blob6F_v_; +text: .text%__1cLOptoRuntimeWfill_in_exception_blob6F_v_; +text: .text%__1cNMemoryManagerbDget_code_cache_memory_manager6F_p0_; +text: .text%__1cNMemoryManagerbDget_psScavenge_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cNMemoryManagerbEget_psMarkSweep_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cQPSGenerationPool2t6MpnIPSOldGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cJAssemblerFimull6MpnMRegisterImpl_2_v_; +text: .text%__1cLOptoRuntimebBgenerate_uncommon_trap_blob6F_v_; +text: .text%__1cNSharedRuntimeTgenerate_deopt_blob6F_v_; +text: .text%__1cQPSGenerationPool2t6MpnJPSPermGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cNRegisterSaverYrestore_result_registers6FpnOMacroAssembler__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: runtimeService.o; +text: .text%__1cORuntimeServiceYrecord_application_start6F_v_; +text: .text%__1cJGenRemSetYmax_alignment_constraint6Fn0AEName__L_; +text: .text%__1cICarSpaceEinit6F_v_; +text: .text%__1cNcarSpace_init6F_v_; +text: .text%__1cORuntimeServiceEinit6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: genCollectedHeap.o; +text: .text%__1cTAssertIsPermClosure2t6M_v_: genCollectedHeap.o; +text: .text%__1cRAlwaysTrueClosure2t6M_v_: genCollectedHeap.o; +text: .text%__1cLOptoRuntimeVhandle_exception_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeSfetch_monitor_Type6F_pknITypeFunc__; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_i_v_; +text: .text%__1cLStatSamplerKinitialize6F_v_; +text: .text%__1cLStatSamplerGengage6F_v_; +text: .text%__1cLStatSamplerJdisengage6F_v_; +text: .text%__1cLStatSamplerHdestroy6F_v_; +text: .text%__1cSCommandLineFlagsExKuintxAtPut6FnXCommandLineFlagWithType_L_v_; +text: .text%__1cSobjArrayKlassKlassbEallocate_system_objArray_klass6MpnGThread__pnMklassOopDesc__; +text: .text%__1cLOptoRuntimeUmultianewarray5_Type6F_pknITypeFunc__; +text: .text%__1cLStatSamplerbMcreate_system_property_instrumentation6FpnGThread__v_; +text: .text%__1cLStatSamplerUcreate_misc_perfdata6F_v_; +text: .text%__1cLStatSamplerXcreate_sampled_perfdata6F_v_; +text: .text%__1cJAssemblerDorq6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEsarl6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEsarq6MpnMRegisterImpl__v_; +text: .text%__1cLOptoRuntimeUmultianewarray4_Type6F_pknITypeFunc__; +text: .text%__1cJAssemblerEshll6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEshlq6MpnMRegisterImpl__v_; +text: .text%__1cUEdenMutableSpacePool2t6MpnKPSYoungGen_pnMMutableSpace_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cNStubGeneratorQgenerate_initial6M_v_; +text: .text%__1cNStubGeneratorXgenerate_atomic_add_ptr6M_pC_; +text: .text%__1cNStubGeneratorTgenerate_atomic_add6M_pC_; +text: .text%__1cNStubGeneratorbCgenerate_atomic_cmpxchg_long6M_pC_; +text: .text%__1cNStubGeneratorXgenerate_atomic_cmpxchg6M_pC_; +text: .text%__1cNStubGeneratorYgenerate_atomic_xchg_ptr6M_pC_; +text: .text%__1cNStubGeneratorUgenerate_atomic_xchg6M_pC_; +text: .text%__1cNStubGeneratorYgenerate_catch_exception6M_pC_; +text: .text%__1cNStubGeneratorSgenerate_call_stub6MrpC_1_; +text: .text%__1cNStubGeneratorbAgenerate_forward_exception6M_pC_; +text: .text%__1cNStubGeneratorMgenerate_all6M_v_; +text: .text%__1cNStubGeneratorSgenerate_d2l_fixup6M_pC_; +text: .text%__1cNStubGeneratorSgenerate_d2i_fixup6M_pC_; +text: .text%__1cNStubGeneratorSgenerate_f2l_fixup6M_pC_; +text: .text%__1cNStubGeneratorSgenerate_f2i_fixup6M_pC_; +text: .text%__1cLOptoRuntimeUmultianewarray3_Type6F_pknITypeFunc__; +text: .text%__1cNStubGeneratorTgenerate_verify_oop6M_pC_; +text: .text%__1cNStubGeneratorVgenerate_verify_mxcsr6M_pC_; +text: .text%__1cNStubGeneratorYgenerate_get_previous_fp6M_pC_; +text: .text%__1cNStubGeneratorbIgenerate_handler_for_unsafe_access6M_pC_; +text: .text%__1cMStubRoutinesLinitialize16F_v_; +text: .text%__1cMStubRoutinesLinitialize26F_v_; +text: .text%__1cSstubRoutines_init16F_v_; +text: .text%__1cSstubRoutines_init26F_v_; +text: .text%__1cLMoveL2DNodeJideal_reg6kM_I_; +text: .text%__1cNGrowableArray4CpnTDerivedPointerEntry__2t6Mii_v_; +text: .text%__1cLMoveF2INodeJideal_reg6kM_I_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: oopMap.o; +text: .text%__1cNGrowableArray4CpnHMonitor__2t6Mii_v_; +text: .text%__1cLOptoRuntimeUmultianewarray2_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray1_Type6F_pknITypeFunc__; +text: .text%__1cQDoNothingClosure2t6M_v_: oopMap.o; +text: .text%__1cJAssemblerEshrl6MpnMRegisterImpl__v_; diff --git a/hotspot/build/solaris/makefiles/reorder_TIERED_i486 b/hotspot/build/solaris/makefiles/reorder_TIERED_i486 new file mode 100644 index 00000000000..4113159bf8d --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_TIERED_i486 @@ -0,0 +1,8394 @@ +data = R0x2000; +text = LOAD ?RXO; + + +text: .text%__1cQIndexSetIteratorEnext6M_I_: ifg.o; +text: .text%__1cSPSPromotionManagerWcopy_to_survivor_space6MpnHoopDesc__2_; +text: .text%__1cNinstanceKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQIndexSetIteratorQadvance_and_next6M_I_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: indexSet.o; +text: .text%__1cNSharedRuntimeElrem6Fxx_x_; +text: .text%__1cCosOjavaTimeMillis6F_x_; +text: .text%__1cJMarkSweepO_mark_and_push6FppnHoopDesc__v_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: chaitin.o; +text: .text%__1cNSharedRuntimeEldiv6Fxx_x_; +text: .text%__1cIPhaseIFGIadd_edge6MII_i_; +text: .text%__1cQIndexSetIterator2t6MpnIIndexSet__v_; +text: .text%__1cIMachNodeNrematerialize6kM_i_; +text: .text%__1cNinstanceKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMOopTaskQdDueueKpop_global6MrpnHoopDesc__i_; +text: .text%__1cPOopTaskQdDueueSetPsteal_best_of_26MipirpnHoopDesc__i_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: live.o; +text: .text%__1cOtypeArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cOtypeArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cIIndexSetLalloc_block6M_pn0AIBitBlock__; +text: .text%__1cHRegMaskFis_UP6kM_i_; +text: .text%__1cIMachNodeHis_Mach6M_p0_: ad_i486_misc.o; +text: .text%__1cDLRGOcompute_degree6kMr0_i_; +text: .text%__1cIIndexSetWalloc_block_containing6MI_pn0AIBitBlock__; +text: .text%__1cRMachSpillCopyNodeMis_SpillCopy6M_p0_: ad_i486.o; +text: .text%__1cENodeEjvms6kM_pnIJVMState__; +text: .text%__1cIMachNodeJideal_reg6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: classes.o; +text: .text%__1cJMarkSweepPmark_and_follow6FppnHoopDesc__v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: ad_i486_misc.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: classes.o; +text: .text%__1cHRegMaskJis_bound16kM_i_; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__: classes.o; +text: .text%__1cENodeHis_Copy6kM_I_: classes.o; +text: .text%__1cQObjectStartArrayMobject_start6MpnIHeapWord__2_: cardTableExtension.o; +text: .text%__1cRMachSpillCopyNodeHis_Copy6kM_I_: ad_i486.o; +text: .text%__1cNobjArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cENodeHis_Copy6kM_I_: ad_i486_misc.o; +text: .text%__1cETypeDcmp6Fkpk03_i_; +text: .text%__1cOPhaseIdealLoopOidom_no_update6kMpnENode__2_: loopnode.o; +text: .text%__1cHRegMaskJis_bound26kM_i_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: ad_i486_misc.o; +text: .text%__1cHRegMaskESize6kM_I_; +text: .text%__1cNRelocIteratorEnext6M_i_: relocInfo.o; +text: .text%__1cJeRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cUGenericGrowableArrayLraw_at_grow6MipknEGrET__pv_; +text: .text%__1cJVectorSet2R6MI_rnDSet__; +text: .text%__1cIMachNodeHis_Mach6M_p0_: ad_i486.o; +text: .text%__1cXresource_allocate_bytes6FI_pc_; +text: .text%__1cDff16FI_i_; +text: .text%__1cJeRegPOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cIProjNodeHis_Proj6M_p0_; +text: .text%__1cENodeGis_CFG6kM_i_: classes.o; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopopts.o; +text: .text%__1cIIndexSetKinitialize6MI_v_; +text: .text%__1cMloadConINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPOopTaskQdDueueSetFsteal6MipirpnHoopDesc__i_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopnode.o; +text: .text%__1cMloadConINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cHPhiNodeGis_Phi6M_p0_: cfgnode.o; +text: .text%__1cENodeGpinned6kM_i_: classes.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: ad_i486.o; +text: .text%__1cIIndexSetKfree_block6MI_v_; +text: .text%__1cIMachNodeGOpcode6kM_i_; +text: .text%__1cWPSScavengeRootsClosureGdo_oop6MppnHoopDesc__v_: psTasks.o; +text: .text%__1cITypeNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: ad_i486_misc.o; +text: .text%__1cENodeIout_grow6MI_v_; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_: callnode.o; +text: .text%__1cENodeHadd_req6Mp0_v_; +text: .text%__1cKTypeOopPtrFklass6kM_pnHciKlass__: type.o; +text: .text%__1cMPhaseChaitinTinterfere_with_live6MIpnIIndexSet__v_; +text: .text%__1cNRelocIteratorEnext6M_i_: nmethod.o; +text: .text%__1cETypeFuhash6Fkpk0_i_; +text: .text%__1cJCProjNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeNrematerialize6kM_i_: classes.o; +text: .text%__1cJloadPNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIrc_class6FnHOptoRegEName__nCRC__: ad_i486.o; +text: .text%__1cNMachIdealNodeErule6kM_I_: ad_i486.o; +text: .text%__1cKjmpDirNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cRMachSpillCopyNodeJideal_reg6kM_I_: ad_i486.o; +text: .text%__1cOlower_pressure6FpnDLRG_IpnFBlock_pI4_v_: ifg.o; +text: .text%__1cMget_live_bit6Fpii_i_: buildOopMap.o; +text: .text%__1cMPhaseChaitinLskip_copies6MpnENode__2_; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__: ad_i486_misc.o; +text: .text%__1cINodeHashLhash_delete6MpknENode__i_; +text: .text%__1cEDictGInsert6Mpv1i_1_; +text: .text%__1cICallNodeKmatch_edge6kMI_I_; +text: .text%__1cJMultiNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cHTypeIntCeq6kMpknEType__i_; +text: .text%__1cENodeGis_CFG6kM_i_: cfgnode.o; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_: ad_i486.o; +text: .text%__1cETypeJtype_dict6F_pnEDict__; +text: .text%__1cHPhiNodeGOpcode6kM_i_; +text: .text%__1cJMarkSweepUAdjustPointerClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cENodeHdel_out6Mp0_v_: matcher.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: classes.o; +text: .text%__1cFArenaIcontains6kMpkv_i_; +text: .text%__1cINodeHashQhash_find_insert6MpnENode__2_; +text: .text%__1cKRegionNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cIPhaseIFGQeffective_degree6kMI_i_; +text: .text%__1cIProjNodeGis_CFG6kM_i_; +text: .text%__1cIUniverseMnon_oop_word6F_pv_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: classes.o; +text: .text%__1cIPhaseIFGLremove_node6MI_pnIIndexSet__; +text: .text%__1cIPhaseIFGJre_insert6MI_v_; +text: .text%__1cJraw_score6Fdd_d_: chaitin.o; +text: .text%__1cDLRGFscore6kM_d_; +text: .text%__1cJMarkSweepOIsAliveClosureLdo_object_b6MpnHoopDesc__i_: markSweep.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: classes.o; +text: .text%__1cETypeIhashcons6M_pk0_; +text: .text%__1cPClassFileStreamGget_u26MpnGThread__H_; +text: .text%__1cENodeEhash6kM_I_; +text: .text%__1cIProjNodeGpinned6kM_i_; +text: .text%__1cMloadConPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMPhaseChaitinKelide_copy6MpnENode_ipnFBlock_rnJNode_List_6i_i_; +text: .text%__1cHNTarjanEEVAL6M_p0_; +text: .text%__1cMloadConPNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: classes.o; +text: .text%__1cQIndexSetIteratorEnext6M_I_: coalesce.o; +text: .text%__1cWShouldNotReachHereNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cMMachCallNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cMset_live_bit6Fpii_v_: buildOopMap.o; +text: .text%__1cHCompileRvalid_bundle_info6MpknENode__i_; +text: .text%__1cOPhaseIdealLoopUbuild_loop_late_post6MpnENode_pk0_v_; +text: .text%__1cIProjNodeGOpcode6kM_i_; +text: .text%__1cENodeHdel_out6Mp0_v_: phaseX.o; +text: .text%__1cNobjArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNobjArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJPhaseLiveLadd_liveout6MpnFBlock_IrnJVectorSet__v_; +text: .text%__1cDfh16FI_i_; +text: .text%__1cRMachSpillCopyNodeLbottom_type6kM_pknEType__: ad_i486.o; +text: .text%__1cMPhaseChaitinMchoose_color6MrnDLRG_i_nHOptoRegEName__; +text: .text%__1cNIdealLoopTreeJis_member6kMpk0_i_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: oopMap.o; +text: .text%__1cENodeHis_Copy6kM_I_: cfgnode.o; +text: .text%__1cIMachNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cIConINodeGOpcode6kM_i_; +text: .text%__1cGIfNodeGOpcode6kM_i_; +text: .text%__1cOtypeArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cHTypePtrEhash6kM_i_; +text: .text%__1cMPhaseChaitinQis_high_pressure6MpnFBlock_pnDLRG_I_i_; +text: .text%__1cENode2t6MI_v_; +text: .text%__1cMPhaseChaitinKbias_color6MrnDLRG_i_nHOptoRegEName__; +text: .text%__1cENodeMcisc_operand6kM_i_: classes.o; +text: .text%__1cMOopTaskQdDueueOpop_local_slow6MInOTaskQdDueueSuperDAge__i_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: typeArrayKlass.o; +text: .text%JVM_ArrayCopy; +text: .text%__1cOtypeArrayKlassQoop_is_typeArray6kM_i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cOPhaseIdealLoopYsplit_if_with_blocks_pre6MpnENode__2_; +text: .text%__1cOPhaseIdealLoopZsplit_if_with_blocks_post6MpnENode__v_; +text: .text%__1cETypeEmeet6kMpk0_2_; +text: .text%__1cETypeLisa_oop_ptr6kM_i_; +text: .text%__1cFArenaIArealloc6MpvII_1_; +text: .text%__1cRMachSpillCopyNodeKin_RegMask6kMI_rknHRegMask__: ad_i486.o; +text: .text%__1cKTypeOopPtrEhash6kM_i_; +text: .text%__1cRMachSpillCopyNodeLout_RegMask6kM_rknHRegMask__: ad_i486.o; +text: .text%__1cIMachNodeMcisc_operand6kM_i_: ad_i486.o; +text: .text%__1cIMachNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: ad_i486_misc.o; +text: .text%__1cKIfTrueNodeGOpcode6kM_i_; +text: .text%__1cIAddPNodeGOpcode6kM_i_; +text: .text%__1cENodeHdel_out6Mp0_v_: graphKit.o; +text: .text%__1cPDictionaryEntrybDprotection_domain_set_oops_do6MpnKOopClosure__v_: dictionary.o; +text: .text%__1cHTypeIntEhash6kM_i_; +text: .text%__1cSPSPromotionManagerUflush_prefetch_queue6M_v_: psPromotionManager.o; +text: .text%__1cMMachProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cMPhaseIterGVNNtransform_old6MpnENode__2_; +text: .text%__1cMMachProjNodeGOpcode6kM_i_; +text: .text%__1cETypeJsingleton6kM_i_; +text: .text%__1cRMachSpillCopyNodePoper_input_base6kM_I_: ad_i486.o; +text: .text%__1cJleaP8NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJleaP8NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMclr_live_bit6Fpii_v_: buildOopMap.o; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__: cfgnode.o; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__: ad_i486.o; +text: .text%__1cIMachNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cJPhaseLiveGgetset6MpnFBlock__pnIIndexSet__; +text: .text%__1cHConNodeGOpcode6kM_i_; +text: .text%__1cJMarkSweepSMarkAndPushClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cJloadINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSis_single_register6FI_i_: postaloc.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: relocInfo.o; +text: .text%__1cILRG_ListGextend6MII_v_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: classes.o; +text: .text%__1cLIfFalseNodeGOpcode6kM_i_; +text: .text%__1cECopyYconjoint_words_to_higher6FpnIHeapWord_2I_v_: node.o; +text: .text%__1cHTypeIntJsingleton6kM_i_; +text: .text%__1cSCallStaticJavaNodeGOpcode6kM_i_; +text: .text%__1cMPhaseIterGVNWadd_users_to_worklist06MpnENode__v_; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopEsort6MpnNIdealLoopTree_2_2_; +text: .text%__1cJPhaseLiveLadd_liveout6MpnFBlock_pnIIndexSet_rnJVectorSet__v_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: cfgnode.o; +text: .text%__1cMMutableSpaceMcas_allocate6MI_pnIHeapWord__; +text: .text%__1cHNTarjanICOMPRESS6M_v_; +text: .text%__1cLsymbolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cIciObjectGequals6Mp0_i_; +text: .text%__1cKNode_ArrayGinsert6MIpnENode__v_; +text: .text%__1cKTypeOopPtrCeq6kMpknEType__i_; +text: .text%__1cIBoolNodeGOpcode6kM_i_; +text: .text%__1cNRelocIteratorFreloc6M_pnKRelocation__; +text: .text%__1cENodeEgrow6MI_v_; +text: .text%__1cHTypePtrCeq6kMpknEType__i_; +text: .text%__1cYCallStaticJavaDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNRelocIteratorEnext6M_i_: codeBlob.o; +text: .text%__1cWConstantPoolCacheEntryPfollow_contents6M_v_; +text: .text%__1cWConstantPoolCacheEntryPadjust_pointers6M_v_; +text: .text%__1cJeRegIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cLemit_opcode6FrnKCodeBuffer_i_v_; +text: .text%__1cMMachProjNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cENodeNrematerialize6kM_i_: cfgnode.o; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: ad_i486_misc.o; +text: .text%__1cKup_one_dom6FpnENode__1_: ifnode.o; +text: .text%__1cJMultiNodeIis_Multi6M_p0_; +text: .text%__1cLsymbolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLsymbolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cIIndexSetKinitialize6MIpnFArena__v_; +text: .text%__1cJPhaseLiveKgetfreeset6M_pnIIndexSet__; +text: .text%__1cHnmethodbHfollow_root_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_ppnHoopDesc_iri_v_; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: classes.o; +text: .text%__1cMMachProjNodeLout_RegMask6kM_rknHRegMask__: classes.o; +text: .text%__1cOPhaseIdealLoopUbuild_loop_tree_impl6MpnENode_i_i_; +text: .text%__1cFState2T6M_v_; +text: .text%__1cIParmNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cHTypeInt2t6Miii_v_; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: ad_i486_misc.o; +text: .text%__1cECopyXconjoint_words_to_lower6FpnIHeapWord_2I_v_: node.o; +text: .text%__1cEUTF8Enext6FpkcpH_pc_; +text: .text%__1cHemit_rm6FrnKCodeBuffer_iii_v_; +text: .text%__1cIciObjectEhash6M_i_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: typeArrayKlass.o; +text: .text%__1cKRegionNodeGOpcode6kM_i_; +text: .text%__1cWNode_Backward_IteratorEnext6M_pnENode__; +text: .text%__1cIMachNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopOget_early_ctrl6MpnENode__2_; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: typeArrayKlass.o; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassIallocate6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cUParallelScavengeHeapVlarge_typearray_limit6M_I_: parallelScavengeHeap.o; +text: .text%__1cOPhaseIdealLoopOset_early_ctrl6MpnENode__v_; +text: .text%__1cHPhiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: cfgnode.o; +text: .text%__1cKoopFactoryNnew_typeArray6FnJBasicType_ipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cHNTarjanELINK6Mp01_v_; +text: .text%__1cENodeHdel_out6Mp0_v_: node.o; +text: .text%__1cLuse_dom_lca6FpnFBlock_pnENode_3rnLBlock_Array__1_: gcm.o; +text: .text%__1cIPhaseGVNJtransform6MpnENode__2_; +text: .text%__1cIIndexSetFclear6M_v_: live.o; +text: .text%__1cOPSPromotionLABKinitialize6MnJMemRegion__v_; +text: .text%__1cOPSPromotionLABFflush6M_v_; +text: .text%__1cITypeNodeEhash6kM_I_; +text: .text%__1cJVectorSet2F6kMI_i_; +text: .text%__1cJPhaseLiveHfreeset6MpknFBlock__v_; +text: .text%__1cENodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOPhaseIdealLoopThas_local_phi_input6MpnENode__2_; +text: .text%__1cIBoolNodeHis_Bool6M_p0_: subnode.o; +text: .text%__1cTleaPIdxScaleOffNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNPhaseRegAllocUreg2offset_unchecked6kMnHOptoRegEName__i_; +text: .text%__1cNPhaseRegAllocKreg2offset6kMnHOptoRegEName__i_; +text: .text%__1cTleaPIdxScaleOffNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachNode2t6M_v_; +text: .text%__1cITypeNodeJideal_reg6kM_I_; +text: .text%__1cLTypeInstPtrEhash6kM_i_; +text: .text%__1cFStateRMachOperGenerator6MipnIMachNode_pnHCompile__pnIMachOper__; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: classes.o; +text: .text%__1cHdom_lca6FpnFBlock_1_1_: gcm.o; +text: .text%__1cENodeNis_block_proj6kM_pk0_; +text: .text%__1cSInterpreterRuntimeInewarray6FpnKJavaThread_nJBasicType_i_v_; +text: .text%__1cPClassFileParserOcheck_property6MipkcipnGThread__v_; +text: .text%__1cKRegionNodeGpinned6kM_i_: classes.o; +text: .text%__1cFMutexGunlock6M_v_; +text: .text%__1cHPhiNodeGpinned6kM_i_: cfgnode.o; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: ad_i486.o; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: ad_i486.o; +text: .text%__1cPjava_lang_ClassLas_klassOop6FpnHoopDesc__pnMklassOopDesc__; +text: .text%__1cFStateDDFA6MipknENode__i_; +text: .text%__1cFState2t6M_v_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: classes.o; +text: .text%__1cKRelocationLunpack_data6M_v_: ad_i486.o; +text: .text%__1cHRegMaskMSmearToPairs6M_v_; +text: .text%__1cKjmpDirNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIIndexSet2t6Mp0_v_; +text: .text%__1cENodeFclone6kM_p0_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: cfgnode.o; +text: .text%__1cRmethodDataOopDescHdata_at6Mi_pnLProfileData__; +text: .text%__1cETypeFxmeet6kMpk0_2_; +text: .text%__1cOPhaseIdealLoopZremix_address_expressions6MpnENode__2_; +text: .text%__1cENodeKmatch_edge6kMI_I_; +text: .text%__1cIPhaseCCPOtransform_once6MpnENode__2_; +text: .text%__1cICallNodeLbottom_type6kM_pknEType__; +text: .text%__1cKTypeAryPtrEhash6kM_i_; +text: .text%__1cENodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cbAfinal_graph_reshaping_impl6FpnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cOPhaseIdealLoopNget_late_ctrl6MpnENode_2_2_; +text: .text%__1cLmethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cOMethodLivenessKBasicBlockXcompute_gen_kill_single6MpnQciByteCodeStream__v_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: callnode.o; +text: .text%__1cICallNodeHis_Call6M_p0_: callnode.o; +text: .text%__1cRMachSpillCopyNodeOimplementation6kMpnKCodeBuffer_pnNPhaseRegAlloc_i_I_; +text: .text%__1cIProjNodeEhash6kM_I_; +text: .text%__1cHemit_d86FrnKCodeBuffer_i_v_; +text: .text%__1cENodeIIdentity6MpnOPhaseTransform__p0_; +text: .text%__1cRMachSafePointNodeEjvms6kM_pnIJVMState__: ad_i486_misc.o; +text: .text%__1cENodeFIdeal6MpnIPhaseGVN_i_p0_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: debugInfo.o; +text: .text%__1cGIfNodeGpinned6kM_i_: classes.o; +text: .text%__1cRSignatureIteratorGexpect6Mc_v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: memnode.o; +text: .text%__1cENodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: instanceKlass.o; +text: .text%__1cNeFlagsRegOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cQUnique_Node_ListGremove6MpnENode__v_; +text: .text%__1cICmpPNodeGOpcode6kM_i_; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: location.o; +text: .text%__1cILocationIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cHPhiNodeEhash6kM_I_; +text: .text%__1cRmethodDataOopDescJnext_data6MpnLProfileData__2_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: callnode.o; +text: .text%__1cQconstMethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cTCreateExceptionNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cGciTypeEmake6FnJBasicType__p0_; +text: .text%__1cNLocationValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: cfgnode.o; +text: .text%__1cOis_diamond_phi6FpnENode__i_: cfgnode.o; +text: .text%__1cHCompileMFillLocArray6MpnENode_pnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: ad_i486.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: ad_i486.o; +text: .text%__1cENodeQIdeal_DU_postCCP6MpnIPhaseCCP__p0_; +text: .text%__1cLTypeInstPtrCeq6kMpknEType__i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: memnode.o; +text: .text%__1cMPhaseChaitinSuse_prior_register6MpnENode_I2pnFBlock_rnJNode_List_6_i_; +text: .text%__1cLimpl_helper6FpnKCodeBuffer_iiiiipkci_i_: ad_i486.o; +text: .text%__1cKTypeOopPtrJsingleton6kM_i_; +text: .text%__1cENodeHsize_of6kM_I_; +text: .text%__1cMOopMapStreamJfind_next6M_v_; +text: .text%__1cKJavaThreadPcook_last_frame6MnFframe__1_; +text: .text%__1cIAddINodeGOpcode6kM_i_; +text: .text%__1cIGraphKitHstopped6M_i_; +text: .text%__1cJStartNodeLbottom_type6kM_pknEType__; +text: .text%__1cGIfNodeFis_If6M_p0_: classes.o; +text: .text%__1cMPhaseChaitinHnew_lrg6MpknENode_I_v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: callnode.o; +text: .text%__1cFMutexElock6MpnGThread__v_; +text: .text%__1cTconstantPoolOopDescbAname_and_type_ref_index_at6Mi_i_; +text: .text%__1cENodeSremove_dead_region6MpnIPhaseGVN_i_i_; +text: .text%__1cKTypeOopPtrLxadd_offset6kMi_i_; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_: coalesce.o; +text: .text%__1cHMatcherKLabel_Root6MpknENode_pnFState_p16_6_; +text: .text%__1cJAssemblerOlocate_operand6FpCn0AMWhichOperand__1_; +text: .text%__1cPClassFileStreamGget_u16MpnGThread__C_; +text: .text%__1cHTypeIntFxmeet6kMpknEType__3_; +text: .text%__1cKRelocationSpd_address_in_code6M_ppC_; +text: .text%__1cNSafePointNodeGpinned6kM_i_: callnode.o; +text: .text%__1cOoop_RelocationIoop_addr6M_ppnHoopDesc__; +text: .text%__1cQconstMethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLmethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cQconstMethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cICmpINodeGOpcode6kM_i_; +text: .text%__1cFBlockLis_uncommon6kMrnLBlock_Array__i_; +text: .text%__1cKRelocationNunpack_2_ints6Mri1_v_: relocInfo.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: ad_i486_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: classes.o; +text: .text%JVM_CurrentTimeMillis; +text: .text%__1cSCallStaticJavaNodeEhash6kM_I_: callnode.o; +text: .text%__1cMPhaseIterGVNVadd_users_to_worklist6MpnENode__v_; +text: .text%__1cIMachNodeSalignment_required6kM_i_: ad_i486_misc.o; +text: .text%__1cOoop_RelocationLunpack_data6M_v_; +text: .text%__1cIHaltNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: classes.o; +text: .text%__1cNSharedRuntimeDd2i6Fd_i_; +text: .text%__1cGcmpkey6Fpkv1_i_; +text: .text%__1cMMergeMemNodeGOpcode6kM_i_; +text: .text%__1cIMachNodeIpeephole6MpnFBlock_ipnNPhaseRegAlloc_ri_p0_; +text: .text%__1cJCProjNodeGis_CFG6kM_i_: cfgnode.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: cfgnode.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: classes.o; +text: .text%__1cPciObjectFactoryEfind6MpnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cOmatch_into_reg6FpnENode_iii1_i_: matcher.o; +text: .text%__1cENodeHdel_out6Mp0_v_: reg_split.o; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: debugInfoRec.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: ad_i486.o; +text: .text%__1cIMachNodeSalignment_required6kM_i_: ad_i486.o; +text: .text%__1cINodeHashLhash_insert6MpnENode__v_; +text: .text%__1cKTypeAryPtrCeq6kMpknEType__i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: callnode.o; +text: .text%__1cOindOffset8OperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cPciObjectFactoryLis_found_at6MipnHoopDesc_pnNGrowableArray4CpnIciObject____i_; +text: .text%__1cLTypeInstPtr2t6MnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_v_; +text: .text%__1cENode2t6Mp0_v_; +text: .text%__1cIimmIOperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cLSymbolTableGlookup6MipkciI_pnNsymbolOopDesc__; +text: .text%__1cENodeFis_If6M_pnGIfNode__: classes.o; +text: .text%__1cIProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cETypeKhas_memory6kM_i_; +text: .text%__1cNloadRangeNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLTypeInstPtrEmake6FnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_pk0_; +text: .text%__1cKjmpConNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIJVMStateIof_depth6kMi_p0_; +text: .text%__1cKNode_ArrayEgrow6MI_v_; +text: .text%__1cJStartNodeGpinned6kM_i_: callnode.o; +text: .text%__1cRPSOldPromotionLABFflush6M_v_; +text: .text%__1cNCatchProjNodeGOpcode6kM_i_; +text: .text%__1cENodeGis_CFG6kM_i_: connode.o; +text: .text%__1cHMatcherKReduceOper6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cWShouldNotReachHereNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIPhaseIFGMtest_edge_sq6kMII_i_; +text: .text%__1cGIfNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIConPNodeGOpcode6kM_i_; +text: .text%__1cHTypeIntEmake6Fiii_pk0_; +text: .text%__1cRMachSpillCopyNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFframeVoopmapreg_to_location6kMnFVMRegEName_pknLRegisterMap__ppnHoopDesc__; +text: .text%__1cHPhiNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cICallNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: instanceKlass.o; +text: .text%__1cJTypeTupleJsingleton6kM_i_; +text: .text%__1cJLoadPNodeGOpcode6kM_i_; +text: .text%__1cKCodeBuffer2t6MiiiiiipnKBufferBlob_pnJrelocInfo_pnORelocateBuffer_ipnLOopRecorder_pkcii_v_; +text: .text%__1cLOopRecorder2t6MpnFArena__v_; +text: .text%__1cKCodeBuffer2T6M_v_; +text: .text%__1cKCodeBufferQalloc_relocation6MI_v_; +text: .text%__1cIAddPNodeKmatch_edge6kMI_I_; +text: .text%__1cIMachNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIMachNodeJemit_size6kMpnNPhaseRegAlloc__I_; +text: .text%__1cZPhaseConservativeCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cHCmpNodeGis_Cmp6kM_pk0_: classes.o; +text: .text%__1cFMutexElock6M_v_; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: instanceKlass.o; +text: .text%__1cHTypeIntEmake6Fi_pk0_; +text: .text%__1cPciObjectFactoryDget6MpnHoopDesc__pnIciObject__; +text: .text%__1cENodeRis_cisc_alternate6kM_i_: ad_i486.o; +text: .text%__1cENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNencode_RegMem6FrnKCodeBuffer_iiiiii_v_; +text: .text%__1cMloadConINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cILoadNodeEhash6kM_I_; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pnIciObject_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cENodeMcisc_operand6kM_i_: cfgnode.o; +text: .text%__1cJeRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cICmpUNodeGOpcode6kM_i_; +text: .text%__1cJHashtableLhash_symbol6Fpkci_I_: symbolTable.o; +text: .text%__1cFframeUis_interpreted_frame6kM_i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: cfgnode.o; +text: .text%__1cJCProjNodeEhash6kM_I_: classes.o; +text: .text%__1cHPhiNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFframeYinterpreter_frame_method6kM_pnNmethodOopDesc__; +text: .text%__1cJMultiNodeEhash6kM_I_: classes.o; +text: .text%__1cENodeHdel_req6MI_v_; +text: .text%__1cHCompileJcan_alias6MpknHTypePtr_i_i_; +text: .text%__1cSPSPromotionManagerMdrain_stacks6M_v_; +text: .text%__1cETypeEhash6kM_i_; +text: .text%__1cLOptoRuntimeXdeoptimize_caller_frame6FpnKJavaThread_i_v_; +text: .text%__1cLSymbolTableGlookup6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cKoopFactoryKnew_symbol6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cIHaltNodeGOpcode6kM_i_; +text: .text%__1cZPhaseConservativeCoalesceJcopy_copy6MpnENode_2pnFBlock_I_i_; +text: .text%__1cENodeGis_CFG6kM_i_: subnode.o; +text: .text%__1cMPhaseIterGVNZremove_globally_dead_node6MpnENode__v_; +text: .text%__1cIParmNodeGOpcode6kM_i_; +text: .text%__1cIJVMStateLdebug_start6kM_I_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: classes.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: multnode.o; +text: .text%__1cGTarjanEEVAL6M_p0_; +text: .text%__1cOThreadCritical2T6M_v_; +text: .text%__1cOThreadCritical2t6M_v_; +text: .text%__1cRSignatureIteratorKparse_type6M_i_; +text: .text%__1cFframeVinterpreter_frame_bcp6kM_pC_; +text: .text%__1cNRelocIteratorTadvance_over_prefix6M_v_; +text: .text%__1cOPhaseIdealLoopbIdom_lca_for_get_late_ctrl_internal6MpnENode_22_2_; +text: .text%__1cTconstantPoolOopDescSklass_ref_index_at6Mi_i_; +text: .text%__1cOeFlagsRegUOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_: classes.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: cfgnode.o; +text: .text%__1cFBlockGselect6MrnJNode_List_rnLBlock_Array_pirnJVectorSet_IrnNGrowableArray4CI___pnENode__; +text: .text%__1cKMachIfNodeJis_MachIf6kM_pk0_: ad_i486_misc.o; +text: .text%__1cEDict2F6kMpkv_pv_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nmethod.o; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: multnode.o; +text: .text%__1cICodeHeapKfind_start6kMpv_1_; +text: .text%__1cKTypeAryPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cIMachNodeHtwo_adr6kM_I_: ad_i486.o; +text: .text%__1cHhashptr6Fpkv_i_; +text: .text%__1cIAddPNodeLbottom_type6kM_pknEType__; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cENodeHget_int6kM_i_; +text: .text%__1cMMachTypeNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cJCatchNodeGOpcode6kM_i_; +text: .text%__1cPClassFileStreamHskip_u16MipnGThread__v_; +text: .text%__1cOoop_RelocationJoop_value6M_pnHoopDesc__; +text: .text%__1cHConNodeGis_Con6kM_I_: classes.o; +text: .text%__1cNinstanceKlassLfind_method6FpnPobjArrayOopDesc_pnNsymbolOopDesc_4_pnNmethodOopDesc__; +text: .text%__1cFBlockIis_Empty6kM_i_; +text: .text%__1cWThreadLocalAllocBufferFreset6M_v_; +text: .text%__1cENodeGis_Con6kM_I_: classes.o; +text: .text%__1cGBitMapUclear_range_of_words6MII_v_: bitMap.o; +text: .text%__1cGBitMapFclear6M_v_; +text: .text%__1cbFCompressedLineNumberWriteStreamKwrite_pair6Mii_v_; +text: .text%__1cOPhaseIdealLoopOidom_no_update6kMpnENode__2_: split_if.o; +text: .text%__1cFframeOis_entry_frame6kM_i_; +text: .text%__1cYDebugInformationRecorderLcheck_phase6Mn0AFPhase__v_; +text: .text%__1cNinstanceKlassLfind_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cMMergeMemNodeLbottom_type6kM_pknEType__: memnode.o; +text: .text%__1cLLShiftINodeGOpcode6kM_i_; +text: .text%__1cRCardTableModRefBSEkind6M_nKBarrierSetEName__: cardTableExtension.o; +text: .text%__1cFBlockOcode_alignment6M_I_; +text: .text%__1cMPhaseChaitinLinsert_proj6MpnFBlock_IpnENode_I_v_; +text: .text%__1cKCastPPNodeGOpcode6kM_i_; +text: .text%__1cMMachCallNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cFStateRMachNodeGenerator6MipnHCompile__pnIMachNode__; +text: .text%__1cHMatcherKReduceInst6MpnFState_irpnENode__pnIMachNode__; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: oopMap.o; +text: .text%__1cNCollectedHeapXallocate_from_tlab_slow6FpnGThread_I_pnIHeapWord__; +text: .text%__1cENodeHis_Copy6kM_I_: memnode.o; +text: .text%__1cNsymbolOopDescGequals6kMpkci_i_; +text: .text%__1cUParallelScavengeHeapVunsafe_max_tlab_alloc6kM_I_; +text: .text%__1cJMultiNodeIproj_out6kMI_pnIProjNode__; +text: .text%__1cILoadNodeLbottom_type6kM_pknEType__; +text: .text%__1cIMachNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__: classes.o; +text: .text%__1cWThreadLocalAllocBufferXclear_before_allocation6M_v_; +text: .text%__1cENodeGpinned6kM_i_: connode.o; +text: .text%__1cWThreadLocalAllocBufferKinitialize6MpnIHeapWord_22_v_; +text: .text%__1cUParallelScavengeHeapRallocate_new_tlab6MI_pnIHeapWord__; +text: .text%__1cWThreadLocalAllocBufferEfill6MpnIHeapWord_2I_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: collectedHeap.o; +text: .text%__1cYNoJvmtiVMObjectAllocMark2T6M_v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2t6M_v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: sharedHeap.o; +text: .text%__1cKSharedHeapXfill_region_with_object6FnJMemRegion__v_; +text: .text%__1cIBoolNodeEhash6kM_I_; +text: .text%__1cQciByteCodeStreamEjava6MnJBytecodesECode__2_; +text: .text%__1cSinstanceKlassKlassMoop_is_klass6kM_i_: instanceKlassKlass.o; +text: .text%__1cOindOffset8OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKjmpDirNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKCodeBufferIrelocate6MpCrknQRelocationHolder_i_v_; +text: .text%__1cGIfNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: subnode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: cfgnode.o; +text: .text%__1cPClassFileParserRverify_legal_utf86MpkCipnGThread__v_; +text: .text%__1cPClassFileParserbEparse_constant_pool_utf8_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMMachHaltNodeEjvms6kM_pnIJVMState__; +text: .text%__1cIMachNodeHis_Mach6M_p0_: machnode.o; +text: .text%__1cKjmpConNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cUGenericGrowableArray2t6Mii_v_; +text: .text%__1cOPhaseIdealLoopOidom_no_update6kMpnENode__2_: loopopts.o; +text: .text%__1cNsymbolOopDescLas_C_string6kMpci_1_; +text: .text%__1cNsymbolOopDescLas_C_string6kM_pc_; +text: .text%__1cHMatcherTReduceInst_Interior6MpnFState_ipnIMachNode_IrpnENode__I_; +text: .text%__1cENodeJis_Branch6kM_I_: ad_i486.o; +text: .text%__1cIAddPNodeHis_AddP6M_p0_: classes.o; +text: .text%__1cIMachNodeNis_MachEpilog6M_pnOMachEpilogNode__: ad_i486.o; +text: .text%__1cPSignatureStreamEnext6M_v_; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode__i_; +text: .text%__1cJloadSNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJPSPermGenSallocate_permanent6MI_pnIHeapWord__; +text: .text%__1cMMutableSpaceIallocate6MI_pnIHeapWord__; +text: .text%__1cUParallelScavengeHeapWpermanent_mem_allocate6MI_pnIHeapWord__; +text: .text%__1cHCompileRprobe_alias_cache6MpknHTypePtr__pn0APAliasCacheEntry__; +text: .text%__1cENodeIdestruct6M_v_; +text: .text%__1cMloadConINodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cIMachNodeNoperand_index6kMI_i_; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cKRegionNodeEhash6kM_I_: classes.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: multnode.o; +text: .text%__1cOMachReturnNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cGBitMapJset_union6M0_v_; +text: .text%__1cIMachNodeGExpand6MpnFState_rnJNode_List__p0_: ad_i486_misc.o; +text: .text%__1cIAddPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNCollectedHeapMobj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cNinstanceKlassRallocate_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cMPhaseIterGVNKis_IterGVN6M_p0_: phaseX.o; +text: .text%__1cENodeOis_block_start6kM_i_; +text: .text%__1cPciInstanceKlassMis_interface6M_i_: ciInstanceKlass.o; +text: .text%__1cJeRegLOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cHPhiNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJTypeTupleEhash6kM_i_; +text: .text%__1cNCellTypeStateFmerge6kM0i_0_; +text: .text%__1cHPhiNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKjmpConNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFParseNdo_exceptions6M_v_; +text: .text%__1cFParsePdo_one_bytecode6M_v_; +text: .text%__1cFBlockJfind_node6kMpknENode__I_; +text: .text%__1cRSignatureIterator2t6MnMsymbolHandle__v_; +text: .text%__1cLjmpConUNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cGBitMap2t6MpII_v_; +text: .text%__1cLOptoRuntimeFnew_C6FpnMklassOopDesc_pnKJavaThread__v_; +text: .text%method_compare: methodOop.o; +text: .text%__1cIHaltNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: classes.o; +text: .text%__1cKis_x2logic6FpnIPhaseGVN_pnENode__3_: cfgnode.o; +text: .text%__1cHAbsNodeLis_absolute6FpnIPhaseGVN_pnENode__4_; +text: .text%__1cJloadPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNLoadRangeNodeGOpcode6kM_i_; +text: .text%__1cLPhaseValuesGintcon6Mi_pnIConINode__; +text: .text%__1cHCompilePfind_alias_type6MpknHTypePtr_i_pn0AJAliasType__; +text: .text%__1cKHandleMarkKinitialize6MpnGThread__v_; +text: .text%__1cKHandleMark2T6M_v_; +text: .text%__1cJloadLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOMachReturnNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cGTarjanICOMPRESS6M_v_; +text: .text%__1cTconstantPoolOopDescQsignature_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cHRegMaskMClearToPairs6M_v_; +text: .text%__1cENodeHdel_out6Mp0_v_: block.o; +text: .text%__1cENodeHdel_out6Mp0_v_: parse1.o; +text: .text%__1cMMachCallNodeLis_MachCall6M_p0_: ad_i486_misc.o; +text: .text%__1cNSafePointNodebBneeds_polling_address_input6F_i_; +text: .text%__1cIJVMStateJdebug_end6kM_I_; +text: .text%__1cIMachNodeKconst_size6kM_i_: ad_i486.o; +text: .text%__1cHSubNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJLoadINodeGOpcode6kM_i_; +text: .text%__1cPjava_lang_ClassMis_primitive6FpnHoopDesc__i_; +text: .text%__1cIMachNodeFreloc6kM_i_: ad_i486.o; +text: .text%__1cIProjNodeHsize_of6kM_I_; +text: .text%__1cHMatcherQis_save_on_entry6Mi_i_; +text: .text%__1cLBoxLockNodeNrematerialize6kM_i_: classes.o; +text: .text%__1cRMachSpillCopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPVirtualCallDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cHMatcherKmatch_tree6MpknENode__pnIMachNode__; +text: .text%__1cOindOffset8OperFscale6kM_i_: ad_i486.o; +text: .text%__1cNloadConI0NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIIndexSetSpopulate_free_list6F_v_; +text: .text%__1cLPCTableNodeGpinned6kM_i_: classes.o; +text: .text%__1cNnew_loc_value6FpnNPhaseRegAlloc_nHOptoRegEName_nILocationEType__pnNLocationValue__: output.o; +text: .text%__1cMloadConINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUGenericGrowableArray2t6MpnFArena_iipnEGrET__v_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: cfgnode.o; +text: .text%__1cENodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%JVM_ReleaseUTF; +text: .text%__1cKutf8_write6FpCH_0_: utf8.o; +text: .text%__1cKNode_ArrayGremove6MI_v_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOis_range_check6FpnENode_r12ri_i_: ifnode.o; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: methodDataOop.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: multnode.o; +text: .text%__1cMPhaseIterGVNMsubsume_node6MpnENode_2_v_; +text: .text%__1cJrelocInfo2t6Mn0AJrelocType_ii_v_; +text: .text%__1cMciMethodDataHdata_at6Mi_pnLProfileData__; +text: .text%__1cENodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachNodeNis_MachEpilog6M_pnOMachEpilogNode__: ad_i486_misc.o; +text: .text%__1cJrelocInfoNfinish_prefix6Mph_p0_; +text: .text%__1cMCreateExNodeGOpcode6kM_i_; +text: .text%__1cSloadL_volatileNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cGOopMapbEmap_compiler_reg_to_oopmap_reg6MnHOptoRegEName_ii_nFVMRegEName__; +text: .text%__1cHhashkey6Fpkv_i_; +text: .text%__1cLTypeInstPtrFxmeet6kMpknEType__3_; +text: .text%__1cIJVMState2t6MpnIciMethod_p0_v_; +text: .text%__1cGOopMapHset_xxx6MnHOptoRegEName_nLOopMapValueJoop_types_ii2_v_; +text: .text%__1cLTypeInstPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cENodeHdel_out6Mp0_v_: coalesce.o; +text: .text%__1cGBitMapGat_put6MIi_v_; +text: .text%__1cJloadBNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHdel_out6Mp0_v_: parse2.o; +text: .text%__1cHMatcherTcollect_null_checks6MpnENode__v_; +text: .text%__1cNloadConI0NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENode2t6Mp011_v_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: generateOopMap.o; +text: .text%__1cTconstantPoolOopDescNklass_at_impl6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cHMemNodeMIdeal_common6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cITypeLongCeq6kMpknEType__i_; +text: .text%__1cHCmpNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMMachCallNodeLbottom_type6kM_pknEType__; +text: .text%__1cRMachSafePointNodeQis_MachSafePoint6M_p0_: ad_i486_misc.o; +text: .text%__1cOMethodLivenessKBasicBlockIload_one6Mi_v_; +text: .text%__1cNSafePointNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: ad_i486_misc.o; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: classes.o; +text: .text%__1cIemit_d326FrnKCodeBuffer_i_v_; +text: .text%__1cFDictI2i6M_v_; +text: .text%__1cIJVMStateNclone_shallow6kM_p0_; +text: .text%__1cNloadConI0NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: subnode.o; +text: .text%__1cIMachNodeFreloc6kM_i_: ad_i486_misc.o; +text: .text%__1cFciEnvIis_in_vm6F_i_; +text: .text%__1cNSafePointNodeHsize_of6kM_I_; +text: .text%__1cPCheckCastPPNodeGOpcode6kM_i_; +text: .text%__1cJCodeCacheQfind_blob_unsafe6Fpv_pnICodeBlob__; +text: .text%__1cHTypePtrLmeet_offset6kMi_i_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: connode.o; +text: .text%__1cIHaltNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFChunk2t6MI_v_; +text: .text%__1cFChunk2n6FII_pv_; +text: .text%__1cKciTypeFlowLStateVectorSapply_one_bytecode6MpnQciByteCodeStream__i_; +text: .text%__1cGOopMapJset_value6MnHOptoRegEName_ii_v_; +text: .text%__1cIMachOperLdisp_is_oop6kM_i_; +text: .text%__1cJloadPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFChunk2k6Fpv_v_; +text: .text%__1cOcompU_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeRdisconnect_inputs6Mp0_i_; +text: .text%__1cIIndexSetFclear6M_v_: indexSet.o; +text: .text%__1cIIndexSetJlrg_union6MIIkIpknIPhaseIFG_rknHRegMask__I_; +text: .text%JVM_GetMethodIxExceptionTableLength; +text: .text%__1cETypeFempty6kM_i_; +text: .text%__1cPClassFileParserUassemble_annotations6MpCi1ipnGThread__nPtypeArrayHandle__; +text: .text%__1cJTypeTupleCeq6kMpknEType__i_; +text: .text%__1cRSignatureIteratorSiterate_parameters6M_v_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: split_if.o; +text: .text%__1cENodeHdel_out6Mp0_v_: memnode.o; +text: .text%__1cNPhaseCoalesceRcombine_these_two6MpnENode_2_v_; +text: .text%__1cFMutexbClock_without_safepoint_check6M_v_; +text: .text%__1cPSignatureStreamHis_done6kM_i_; +text: .text%__1cJStoreNodeKmatch_edge6kMI_I_; +text: .text%__1cIBoolNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIBoolTestKcc2logical6kMpknEType__3_; +text: .text%__1cIAddPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFArenaEgrow6MI_pv_; +text: .text%__1cQCompressedStream2t6MpCi_v_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: multnode.o; +text: .text%__1cJeRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cUParallelScavengeHeapPis_in_permanent6kMpkv_i_: parallelScavengeHeap.o; +text: .text%__1cOJNIHandleBlockPallocate_handle6MpnHoopDesc__pnI_jobject__; +text: .text%__1cXruntime_call_RelocationEtype6M_nJrelocInfoJrelocType__: ad_i486.o; +text: .text%__1cENodeHdel_out6Mp0_v_: loopopts.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: cfgnode.o; +text: .text%__1cKBranchDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cENodeGis_CFG6kM_i_: memnode.o; +text: .text%__1cKjmpDirNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cPClassFileStreamGget_u46MpnGThread__I_; +text: .text%__1cFBlockUneeded_for_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cTconstantPoolOopDescWsignature_ref_index_at6Mi_i_; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlass.o; +text: .text%__1cILoadNodeKmatch_edge6kMI_I_; +text: .text%__1cYCallStaticJavaDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHMemNodeGis_Mem6M_p0_: classes.o; +text: .text%__1cMPhaseIterGVNbGregister_new_node_with_optimizer6MpnENode__2_; +text: .text%__1cENodeKreplace_by6Mp0_v_; +text: .text%__1cNPhaseRegAllocGis_oop6kMpknENode__i_; +text: .text%__1cGIfNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNinstanceKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: classes.o; +text: .text%__1cSCountedLoopEndNodeGOpcode6kM_i_; +text: .text%__1cKjmpDirNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHPhiNodeIadr_type6kM_pknHTypePtr__: cfgnode.o; +text: .text%__1cHPhiNodeHsize_of6kM_I_: cfgnode.o; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6FnTobjArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cJTypeTupleGfields6FI_ppknEType__; +text: .text%__1cMPhaseChaitinFUnion6MpknENode_3_v_; +text: .text%__1cJdo_method6FpnNmethodOopDesc__v_: recompilationMonitor.o; +text: .text%__1cZload_can_see_stored_value6FpnILoadNode_pnENode_pnOPhaseTransform__3_: memnode.o; +text: .text%__1cFframeGsender6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cNtestP_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKRegionNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLrecord_bias6FpknIPhaseIFG_ii_v_: coalesce.o; +text: .text%__1cMPhaseChaitinSget_spillcopy_wide6MpnENode_2I_2_; +text: .text%__1cKNativeCallLdestination6kM_pC_; +text: .text%__1cNinstanceKlassQarray_klass_impl6FnTinstanceKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: memnode.o; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__: callnode.o; +text: .text%__1cSCallStaticJavaNodeRis_CallStaticJava6kM_pk0_: callnode.o; +text: .text%__1cIAddPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMMergeMemNodeEhash6kM_I_; +text: .text%__1cJcmpOpOperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cUPSMarkSweepDecoratorQinsert_deadspace6MripnIHeapWord_I_i_; +text: .text%__1cMMergeMemNodeLis_MergeMem6M_p0_: memnode.o; +text: .text%__1cKoopFactoryMnew_objArray6FpnMklassOopDesc_ipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cOoop_RelocationSfix_oop_relocation6M_v_; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cNinstanceKlassRallocate_objArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cPciInstanceKlassRis_instance_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cILoadNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIBoolNodeLbottom_type6kM_pknEType__: subnode.o; +text: .text%__1cMPhaseChaitinNFind_compress6MI_I_; +text: .text%__1cKStorePNodeGOpcode6kM_i_; +text: .text%__1cTconstantPoolOopDescLname_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: methodOop.o; +text: .text%__1cNmethodOopDescPis_empty_method6kM_i_; +text: .text%__1cLjmpConUNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPciInstanceKlassGloader6M_pnHoopDesc__; +text: .text%__1cOno_flip_branch6FpnFBlock__i_: block.o; +text: .text%__1cICodeHeapKnext_block6kMpnJHeapBlock__2_; +text: .text%__1cNmethodOopDescRis_not_compilable6kMi_i_; +text: .text%__1cICodeHeapJnext_free6kMpnJHeapBlock__pv_; +text: .text%__1cKRelocationJpack_data6M_i_: ad_i486.o; +text: .text%__1cICodeHeapLblock_start6kMpv_pnJHeapBlock__; +text: .text%__1cJCodeCacheEnext6FpnICodeBlob__2_; +text: .text%__1cNmethodOopDescLis_accessor6kM_i_; +text: .text%__1cILoadNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cISubINodeGOpcode6kM_i_; +text: .text%__1cKStoreINodeGOpcode6kM_i_; +text: .text%__1cNeFlagsRegOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cQciByteCodeStreamMreset_to_bci6Mi_v_; +text: .text%__1cHRetNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: lcm.o; +text: .text%__1cJStoreNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: machnode.o; +text: .text%__1cRSignatureIteratorTcheck_signature_end6M_v_; +text: .text%__1cRSignatureIteratorSiterate_returntype6M_v_; +text: .text%__1cENodeHis_Goto6kM_I_: classes.o; +text: .text%__1cILoadNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPciInstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceKlass.o; +text: .text%__1cITypeLongEhash6kM_i_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: block.o; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: relocInfo.o; +text: .text%__1cJloadPNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cMPhaseIterGVNFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cLis_cond_add6FpnIPhaseGVN_pnHPhiNode__pnENode__; +text: .text%__1cPsplit_flow_path6FpnIPhaseGVN_pnHPhiNode__pnENode__: cfgnode.o; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: classes.o; +text: .text%__1cKRegionNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKRelocationLunpack_data6M_v_: relocInfo.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: block.o; +text: .text%__1cITypeNodeHsize_of6kM_I_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: memnode.o; +text: .text%__1cFMutexNowned_by_self6kM_i_; +text: .text%__1cIMachNodeRget_base_and_disp6kMrirpknHTypePtr__pknENode__; +text: .text%__1cIAddINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIIndexSetFclear6M_v_: chaitin.o; +text: .text%__1cXPhaseAggressiveCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cFBlockOschedule_local6MrnHMatcher_rnLBlock_Array_pirnJVectorSet_rnNGrowableArray4CI___i_; +text: .text%__1cFBlockScall_catch_cleanup6MrnLBlock_Array__v_; +text: .text%__1cLBlock_StackXmost_frequent_successor6MpnFBlock__I_; +text: .text%__1cNidealize_test6FpnIPhaseGVN_pnGIfNode__3_: ifnode.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: symbolKlass.o; +text: .text%__1cENodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQSystemDictionaryXcheck_signature_loaders6FnMsymbolHandle_nGHandle_2ipnGThread__v_; +text: .text%__1cENodeGpinned6kM_i_: subnode.o; +text: .text%__1cLLShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLCounterDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cKRegionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGTarjanELINK6Mp01_v_; +text: .text%__1cMPhaseChaitinMyank_if_dead6MpnENode_pnFBlock_pnJNode_List_6_i_; +text: .text%__1cYDebugInformationRecorderWserialize_scope_values6MpnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cYDebugInformationRecorderTcreate_scope_values6MpnNGrowableArray4CpnKScopeValue____pnKDebugToken__; +text: .text%__1cIGraphKitJsync_jvms6kM_pnIJVMState__; +text: .text%__1cENodeGis_Con6kM_I_: cfgnode.o; +text: .text%__1cMciMethodDataJnext_data6MpnLProfileData__2_; +text: .text%__1cENodeGpinned6kM_i_: memnode.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: subnode.o; +text: .text%__1cIBoolNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNmethodOopDescIbci_from6kMpC_i_; +text: .text%__1cNSafePointNodeSset_next_exception6Mp0_v_; +text: .text%__1cQResultTypeFinderDset6MinJBasicType__v_: bytecode.o; +text: .text%__1cOcompU_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJCodeCacheJfind_blob6Fpv_pnICodeBlob__; +text: .text%__1cMPhaseChaitinNFind_compress6MpknENode__I_; +text: .text%__1cNmethodOopDescLresult_type6kM_nJBasicType__; +text: .text%__1cFStateM_sub_Op_ConI6MpknENode__v_; +text: .text%__1cNRelocIteratorKset_limits6MpC1_v_; +text: .text%__1cNRelocIteratorKinitialize6MipnICodeBlob_pC3_v_; +text: .text%__1cLsymbolKlassNoop_is_symbol6kM_i_: symbolKlass.o; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: parse1.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: ad_i486.o; +text: .text%__1cIsplit_if6FpnGIfNode_pnMPhaseIterGVN__pnENode__: ifnode.o; +text: .text%__1cIGraphKitEstop6M_v_; +text: .text%__1cTremove_useless_bool6FpnGIfNode_pnIPhaseGVN__pnENode__: ifnode.o; +text: .text%__1cPciObjectFactorySget_unloaded_klass6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cJeRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: subnode.o; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_: systemDictionary.o; +text: .text%__1cMloadConLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cILoadNodeHis_Load6M_p0_: classes.o; +text: .text%__1cSInterpreterRuntimeJanewarray6FpnKJavaThread_pnTconstantPoolOopDesc_ii_v_; +text: .text%__1cKjmpConNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHAddNodeEhash6kM_I_; +text: .text%__1cNSafePointNodeLbottom_type6kM_pknEType__: callnode.o; +text: .text%__1cOemit_d32_reloc6FrnKCodeBuffer_irknQRelocationHolder_i_v_; +text: .text%__1cNSafePointNodeKmatch_edge6kMI_I_; +text: .text%__1cMURShiftINodeGOpcode6kM_i_; +text: .text%__1cOcompI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLjmpConUNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cIRootNodeGOpcode6kM_i_; +text: .text%__1cFChunkEchop6M_v_; +text: .text%__1cIMachOperOindex_position6kM_i_; +text: .text%__1cMloadConPNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cIimmPOperEtype6kM_pknEType__: ad_i486_clone.o; +text: .text%__1cPcheckCastPPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOindOffset8OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOindOffset8OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOindOffset8OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cHRegMaskQis_aligned_Pairs6kM_i_; +text: .text%__1cMloadConLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompU_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cHSubNodeGis_Sub6M_p0_: classes.o; +text: .text%__1cJTypeTupleEmake6FIppknEType__pk0_; +text: .text%__1cYCallStaticJavaDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNeFlagsRegOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKReflectionTverify_field_access6FpnMklassOopDesc_22nLAccessFlags_ii_i_; +text: .text%__1cIregDOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cSInterpreterRuntimeLcache_entry6FpnKJavaThread__pnWConstantPoolCacheEntry__: interpreterRuntime.o; +text: .text%__1cJStoreNodeIis_Store6kM_pk0_: classes.o; +text: .text%__1cKDictionaryJget_entry6MiInMsymbolHandle_nGHandle__pnPDictionaryEntry__; +text: .text%__1cHCmpNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRSignatureIterator2t6MpnNsymbolOopDesc__v_; +text: .text%__1cOBasicHashtableJnew_entry6MI_pnTBasicHashtableEntry__; +text: .text%__1cHi2sNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNtestI_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJHashtableJnew_entry6MIpnHoopDesc__pnOHashtableEntry__; +text: .text%__1cJloadPNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPimpl_mov_helper6FpnKCodeBuffer_iiii_i_: ad_i486.o; +text: .text%__1cHConNodeEhash6kM_I_; +text: .text%__1cKjmpDirNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cKstoreINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%Unsafe_CompareAndSwapLong; +text: .text%__1cJStoreNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPciObjectFactoryNfind_non_perm6MpnHoopDesc__rpn0ANNonPermObject__; +text: .text%__1cFBlockLfind_remove6MpknENode__v_; +text: .text%__1cNinstanceKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cKTypeRawPtrJsingleton6kM_i_; +text: .text%__1cMloadConDNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConDNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJStoreNodeEhash6kM_I_; +text: .text%__1cMPhaseChaitinJsplit_USE6MpnENode_pnFBlock_2IIiinNGrowableArray4CI__i_I_; +text: .text%__1cMloadConDNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENode2t6Mp0111_v_; +text: .text%__1cXindIndexScaleOffsetOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cWShouldNotReachHereNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKTypeRawPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cTAbstractInterpreterLmethod_kind6FnMmethodHandle__n0AKMethodKind__; +text: .text%__1cGOopMapHset_oop6MnHOptoRegEName_ii_v_; +text: .text%__1cHMatcherXadjust_outgoing_stk_arg6MinHOptoRegEName_r2_2_; +text: .text%__1cMMergeMemNodePiteration_setup6Mpk0_v_; +text: .text%__1cJeRegLOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cJMultiNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cJCProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstorePNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cGPcDescHreal_pc6kMpknHnmethod__pC_; +text: .text%__1cLRegisterMapFclear6Mpi_v_; +text: .text%__1cNtestI_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFframeOis_first_frame6kM_i_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: cfgnode.o; +text: .text%__1cENodeMcisc_operand6kM_i_: memnode.o; +text: .text%__1cNtestP_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFChunkJnext_chop6M_v_; +text: .text%__1cHTypeAryRary_must_be_exact6kM_i_; +text: .text%__1cRMachNullCheckNodeQis_MachNullCheck6M_p0_: machnode.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: callnode.o; +text: .text%__1cKIfTrueNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJCodeCacheFalive6FpnICodeBlob__2_; +text: .text%__1cLRShiftINodeGOpcode6kM_i_; +text: .text%__1cOcompU_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_: reg_split.o; +text: .text%__1cLRegisterMap2t6MpnKJavaThread_i_v_; +text: .text%__1cHTypeIntFempty6kM_i_; +text: .text%__1cKJNIHandlesKmake_local6FpnHJNIEnv__pnHoopDesc__pnI_jobject__; +text: .text%__1cJloadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJchar2type6Fc_nJBasicType__: fieldType.o; +text: .text%__1cJFieldTypeKbasic_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cSinstanceKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNmethodOopDescMintrinsic_id6kM_n0ALIntrinsicId__; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceKlass.o; +text: .text%__1cMMergeMemNodeJmemory_at6kMI_pnENode__; +text: .text%__1cNloadRangeNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: subnode.o; +text: .text%__1cITypeNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cTconstantPoolOopDescSklass_at_if_loaded6FnSconstantPoolHandle_i_pnMklassOopDesc__; +text: .text%__1cFciEnvXget_klass_by_index_impl6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cNExceptionMark2T6M_v_; +text: .text%__1cNExceptionMark2t6MrpnGThread__v_; +text: .text%__1cMgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cMloadConPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHOopFlowNcompute_reach6MpnNPhaseRegAlloc_ipnEDict__v_; +text: .text%__1cRInvocationCounterFreset6M_v_; +text: .text%__1cRInvocationCounterJset_state6Mn0AFState__v_; +text: .text%__1cLOptoRuntimePnew_typeArray_C6FnJBasicType_ipnKJavaThread__v_; +text: .text%__1cRInvocationCounterEinit6M_v_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: multnode.o; +text: .text%__1cRMachSafePointNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cUGenericGrowableArrayPraw_at_put_grow6MipknEGrET_3_v_; +text: .text%__1cMMergeMemNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%JVM_GetClassModifiers; +text: .text%__1cTconstantPoolOopDescNklass_name_at6Mi_pnNsymbolOopDesc__; +text: .text%JVM_GetClassAccessFlags; +text: .text%__1cOcompU_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTCreateExceptionNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJCatchNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLIfFalseNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: codeBlob.o; +text: .text%__1cKstoreINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKjmpDirNodeHis_Goto6kM_I_: ad_i486_misc.o; +text: .text%__1cSObjectSynchronizerJfast_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cSObjectSynchronizerKfast_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cNSafePointNodeOnext_exception6kM_p0_; +text: .text%__1cTconstantPoolOopDescRname_ref_index_at6Mi_i_; +text: .text%__1cMMergeMemNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cENodeHdel_out6Mp0_v_: split_if.o; +text: .text%__1cLMachNopNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKRegionNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLPhaseValuesFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: connode.o; +text: .text%__1cOcompU_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNloadKlassNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cICallNodeLis_CallLeaf6kM_pknMCallLeafNode__: callnode.o; +text: .text%__1cMObjectLocker2t6MnGHandle_pnGThread__v_; +text: .text%__1cMObjectLocker2T6M_v_; +text: .text%__1cKmethodOperGmethod6kM_i_: ad_i486.o; +text: .text%__1cWConstantPoolCacheEntryRset_initial_state6Mi_v_; +text: .text%__1cPSignatureStreamJis_object6kM_i_; +text: .text%__1cJloadINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopOidom_no_update6kMpnENode__2_: loopTransform.o; +text: .text%__1cLProfileDataPfollow_contents6M_v_: methodDataOop.o; +text: .text%__1cLProfileDataPadjust_pointers6M_v_: methodDataOop.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: classes.o; +text: .text%__1cOindOffset8OperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cOcompI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNaddI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRmethodDataOopDescPinitialize_data6MpnOBytecodeStream_i_i_; +text: .text%__1cRmethodDataOopDescTbytecode_cell_count6FnJBytecodesECode__i_; +text: .text%__1cRmethodDataOopDescRcompute_data_size6FpnOBytecodeStream__i_; +text: .text%__1cMMergeMemNodeQclone_all_memory6FpnENode__p0_; +text: .text%__1cIGraphKitJclone_map6M_pnNSafePointNode__; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: multnode.o; +text: .text%__1cGOopMapQset_callee_saved6MnHOptoRegEName_ii2_v_; +text: .text%__1cOPhaseIdealLoopIsplit_up6MpnENode_22_i_; +text: .text%__1cKTypeOopPtrWmake_from_klass_common6FpnHciKlass_ii_pk0_; +text: .text%__1cWstatic_stub_RelocationLunpack_data6M_v_; +text: .text%__1cENodeHdel_out6Mp0_v_: postaloc.o; +text: .text%__1cOcompU_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cMOopMapStream2t6MpnGOopMap_i_v_; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: subnode.o; +text: .text%__1cNtestI_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIBoolNodeKmatch_edge6kMI_I_: subnode.o; +text: .text%__1cLklassItable2t6MnTinstanceKlassHandle__v_; +text: .text%__1cNinstanceKlassGitable6kM_pnLklassItable__; +text: .text%__1cNCompileBrokerLmaybe_block6F_v_; +text: .text%__1cFStateM_sub_Op_RegP6MpknENode__v_; +text: .text%__1cFPhase2t6Mn0ALPhaseNumber__v_; +text: .text%__1cLciSignatureLreturn_type6kM_pnGciType__; +text: .text%__1cIJumpDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cKstorePNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: symbolKlass.o; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: symbolKlass.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: symbolKlass.o; +text: .text%__1cLsymbolKlassPallocate_symbol6MpCipnGThread__pnNsymbolOopDesc__; +text: .text%__1cLSymbolTableJbasic_add6MipCiIpnGThread__pnNsymbolOopDesc__; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__: callnode.o; +text: .text%__1cKRelocationLspec_simple6FnJrelocInfoJrelocType__nQRelocationHolder__; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_: machnode.o; +text: .text%__1cHRegMaskPfind_first_pair6kM_nHOptoRegEName__; +text: .text%__1cIHaltNodeLbottom_type6kM_pknEType__; +text: .text%__1cRMachSafePointNodeRis_safepoint_node6kM_i_: ad_i486_misc.o; +text: .text%__1cKRegionNodeOis_block_start6kM_i_: classes.o; +text: .text%__1cKTypeAryPtrFxmeet6kMpknEType__3_; +text: .text%__1cMMergeMemNodeNset_memory_at6MIpnENode__v_; +text: .text%__1cRMemBarReleaseNodeGOpcode6kM_i_; +text: .text%__1cOeFlagsRegUOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cKcmpOpUOperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cENodeHdel_out6Mp0_v_: cfgnode.o; +text: .text%__1cCosPelapsed_counter6F_x_; +text: .text%__1cMtlsLoadPNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPClassFileParserbLparse_constant_pool_nameandtype_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cGBitMapOset_difference6M0_v_; +text: .text%__1cPcheckCastPPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKMemBarNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cYCallStaticJavaDirectNodeSalignment_required6kM_i_: ad_i486_misc.o; +text: .text%__1cYCallStaticJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNtestP_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: memnode.o; +text: .text%__1cKstorePNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOindOffset8OperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cXindIndexScaleOffsetOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cRaddI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKstoreINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: memnode.o; +text: .text%__1cOcompI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cKRegionNodeGis_CFG6kM_i_: loopnode.o; +text: .text%__1cNloadRangeNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQciByteCodeStreamFEOBCs6M_nJBytecodesECode__; +text: .text%__1cITypeNodeDcmp6kMrknENode__I_; +text: .text%__1cXInterpreterFrameClosureJoffset_do6Mi_v_: frame.o; +text: .text%__1cMPhaseChaitinPset_was_spilled6MpnENode__v_; +text: .text%__1cHTypeAryEhash6kM_i_; +text: .text%__1cKDictionaryEfind6MiInMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cPfieldDescriptorKinitialize6MpnMklassOopDesc_i_v_; +text: .text%__1cNsubI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverYlookup_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cPVirtualCallDataPadjust_pointers6M_v_; +text: .text%__1cPVirtualCallDataPfollow_contents6M_v_; +text: .text%__1cMMergeMemNodePset_base_memory6MpnENode__v_; +text: .text%__1cSsafePoint_pollNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNLoadKlassNodeGOpcode6kM_i_; +text: .text%__1cLOopRecorderOallocate_index6MpnI_jobject__i_; +text: .text%__1cYDebugInformationRecorderNappend_handle6MpnI_jobject__i_; +text: .text%__1cKTypeRawPtrEhash6kM_i_; +text: .text%__1cIciObjectIencoding6M_pnI_jobject__; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: machnode.o; +text: .text%__1cPPerfLongVariantGsample6M_v_; +text: .text%__1cENodeHis_Copy6kM_I_: machnode.o; +text: .text%__1cIAndINodeGOpcode6kM_i_; +text: .text%__1cRCompilationPolicyNcanBeCompiled6FnMmethodHandle__i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: multnode.o; +text: .text%__1cVCompressedWriteStream2t6Mi_v_; +text: .text%__1cHAddNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJlabelOperFclone6kM_pnIMachOper__; +text: .text%__1cMLinkResolverbAcheck_method_accessability6FnLKlassHandle_11nMmethodHandle_pnGThread__v_; +text: .text%__1cNCatchProjNodeMis_CatchProj6kM_pk0_: cfgnode.o; +text: .text%__1cENode2t6Mp01_v_; +text: .text%__1cRAbstractAssembler2t6MpnKCodeBuffer__v_; +text: .text%__1cNtestI_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: subnode.o; +text: .text%__1cKTypeOopPtrHget_con6kM_i_; +text: .text%__1cMloadConINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMUniverseOperFclone6kM_pnIMachOper__; +text: .text%__1cKRelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: multnode.o; +text: .text%__1cJloadINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMTypeKlassPtrEhash6kM_i_; +text: .text%__1cKRegionNodeHhas_phi6kM_pnHPhiNode__; +text: .text%__1cIMachNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cENodeHins_req6MIp0_v_; +text: .text%__1cPconvI2L_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIciMethodbCinterpreter_invocation_count6M_i_; +text: .text%__1cMciMethodDataLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cKStoreCNodeGOpcode6kM_i_; +text: .text%JVM_GetCPMethodSignatureUTF; +text: .text%__1cOcompI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cRshrI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerJemit_data6MirknQRelocationHolder_i_v_; +text: .text%__1cFframeQoops_do_internal6MpnKOopClosure_pnLRegisterMap_i_v_; +text: .text%__1cRInterpreterOopMapLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cOeFlagsRegUOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLjmpConUNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNSafePointNodeGOpcode6kM_i_; +text: .text%__1cJVectorSet2L6MI_rnDSet__; +text: .text%__1cJVectorSetEgrow6MI_v_; +text: .text%__1cOMethodLivenessKBasicBlockWcompute_gen_kill_range6MpnQciByteCodeStream__v_; +text: .text%__1cHnmethodJis_zombie6kM_i_: nmethod.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: gcm.o; +text: .text%__1cKStoreBNodeGOpcode6kM_i_; +text: .text%__1cPClassFileParserbJparse_constant_pool_methodref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: connode.o; +text: .text%__1cOFastUnlockNodeGOpcode6kM_i_; +text: .text%__1cHPhiNodeEmake6FpnENode_2pknEType_pknHTypePtr__p0_; +text: .text%__1cLeAXRegPOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cUArgumentSizeComputerDset6MinJBasicType__v_: frame.o; +text: .text%__1cENodeHdel_out6Mp0_v_: gcm.o; +text: .text%__1cETypeOget_const_type6FpnGciType__pk0_; +text: .text%__1cPcheckCastPPNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJVectorSet2t6MpnFArena__v_; +text: .text%__1cIGraphKitGmemory6MI_pnENode__; +text: .text%__1cITypeLong2t6Mxxi_v_; +text: .text%__1cKCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQciByteCodeStreamPget_field_index6M_i_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: memnode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: memnode.o; +text: .text%__1cCosMvm_page_size6F_i_; +text: .text%__1cENodeDcmp6kMrk0_I_; +text: .text%__1cKReflectionTverify_class_access6FpnMklassOopDesc_2i_i_; +text: .text%__1cNloadRangeNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_RegI6MpknENode__v_; +text: .text%__1cJloadINodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: methodOop.o; +text: .text%__1cFKlassNlookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cKjmpConNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_AddP6MpknENode__v_; +text: .text%__1cMPhaseChaitinVmay_be_copy_of_callee6kMpnENode__i_; +text: .text%__1cXroundDouble_mem_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitMsaved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cIPhaseCCPFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cFParseKensure_phi6Mii_pnHPhiNode__; +text: .text%__1cJloadFNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: postaloc.o; +text: .text%__1cOMachReturnNodeNis_MachReturn6M_p0_: ad_i486_misc.o; +text: .text%__1cIMachOperMdisp_as_type6kM_pknHTypePtr__: ad_i486.o; +text: .text%__1cPcheckCastPPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIciObjectJset_ident6MI_v_; +text: .text%__1cPciObjectFactoryNinit_ident_of6MpnIciObject__v_; +text: .text%__1cQPreserveJVMState2T6M_v_; +text: .text%__1cQPreserveJVMState2t6MpnIGraphKit_i_v_; +text: .text%__1cNtestP_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKJNIHandlesKmake_local6FpnHoopDesc__pnI_jobject__; +text: .text%__1cIGraphKitLclean_stack6Mi_v_; +text: .text%__1cYDebugInformationRecorderYserialize_monitor_values6MpnNGrowableArray4CpnMMonitorValue____i_; +text: .text%__1cYDebugInformationRecorderVcreate_monitor_values6MpnNGrowableArray4CpnMMonitorValue____pnKDebugToken__; +text: .text%__1cYDebugInformationRecorderOdescribe_scope6MpnIciMethod_ipnKDebugToken_44_v_; +text: .text%__1cWMutableSpaceUsedHelperLtake_sample6M_x_: spaceCounters.o; +text: .text%__1cICallNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cFciEnvSget_klass_by_index6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cLcastP2INodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRCompilationPolicyOmustBeCompiled6FnMmethodHandle__i_; +text: .text%__1cLBoxLockNodeGOpcode6kM_i_; +text: .text%__1cHTypePtrHget_con6kM_i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: callnode.o; +text: .text%__1cITypeFuncEhash6kM_i_; +text: .text%__1cJAssemblerJemit_data6MinJrelocInfoJrelocType_i_v_; +text: .text%__1cJAssemblerMemit_operand6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerMemit_operand6MpnMRegisterImpl_22nHAddressLScaleFactor_irknQRelocationHolder__v_; +text: .text%__1cLBoxLockNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMLinkResolverNresolve_klass6FrnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cTconstantPoolOopDescMklass_ref_at6MipnGThread__pnMklassOopDesc__; +text: .text%__1cNloadRangeNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cLPhaseValuesHmakecon6MpknEType__pnHConNode__; +text: .text%__1cOcompI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIciMethodPliveness_at_bci6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessPget_liveness_at6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessKBasicBlockPget_liveness_at6MpnIciMethod_i_nGBitMap__; +text: .text%__1cPSignatureStream2t6MnMsymbolHandle_i_v_; +text: .text%__1cITypeLongJsingleton6kM_i_; +text: .text%__1cOcompI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cRMemBarAcquireNodeGOpcode6kM_i_; +text: .text%__1cJOopMapSetSfind_map_at_offset6kMii_pnGOopMap__; +text: .text%__1cICodeBlobbAoop_map_for_return_address6MpCi_pnGOopMap__; +text: .text%__1cLCounterDataOis_CounterData6M_i_: ciMethodData.o; +text: .text%__1cHnmethodKis_nmethod6kM_i_: nmethod.o; +text: .text%__1cOGenerateOopMapHinterp16MpnOBytecodeStream__v_; +text: .text%__1cLCastP2INodeGOpcode6kM_i_; +text: .text%__1cKCodeBufferOadd_stub_reloc6MpCrknQRelocationHolder_i_v_; +text: .text%__1cKCodeBufferOalloc_relocate6M_pnORelocateBuffer__; +text: .text%__1cMCallLeafNodeGOpcode6kM_i_; +text: .text%__1cJLoadSNodeGOpcode6kM_i_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: subnode.o; +text: .text%__1cKjmpDirNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHTypePtrJsingleton6kM_i_; +text: .text%__1cJAssemblerEcall6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cMLinkResolverZcheck_klass_accessability6FnLKlassHandle_1pnGThread__v_; +text: .text%__1cIGraphKitObasic_plus_adr6MpnENode_2i_2_; +text: .text%__1cKMemBarNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIimmPOperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cMmerge_region6FpnKRegionNode_pnIPhaseGVN__pnENode__: cfgnode.o; +text: .text%__1cRconstantPoolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cTCreateExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRshrI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMPhaseIterGVNHmakecon6MpknEType__pnHConNode__; +text: .text%__1cMTypeKlassPtrCeq6kMpknEType__i_; +text: .text%__1cRaddI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%JVM_GetMethodIxLocalsCount; +text: .text%__1cRNativeInstructionFwrote6Mi_v_; +text: .text%__1cWShouldNotReachHereNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cICmpPNodeDsub6kMpknEType_3_3_; +text: .text%__1cWConstantPoolCacheEntryPbytecode_number6FnJBytecodesECode__i_: interpreterRuntime.o; +text: .text%__1cWConstantPoolCacheEntryLis_resolved6kMnJBytecodesECode__i_: interpreterRuntime.o; +text: .text%__1cWConstantPoolCacheEntryIas_flags6MnITosState_iiiii_i_; +text: .text%__1cPClassFileParserYverify_legal_method_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cQMachCallJavaNodePis_MachCallJava6M_p0_: ad_i486_misc.o; +text: .text%__1cWShouldNotReachHereNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJimmI0OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cNloadConI0NodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cHMatcherWis_short_branch_offset6Mi_i_; +text: .text%__1cWConstantPoolCacheEntryGverify6kMpnMoutputStream__v_; +text: .text%__1cNloadKlassNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cITypeFuncCeq6kMpknEType__i_; +text: .text%__1cNSafePointNode2t6MIpnIJVMState__v_; +text: .text%__1cOGenerateOopMapPjump_targets_do6MpnOBytecodeStream_pFp0ipi_v4_i_; +text: .text%__1cHcommute6FpnENode_ii_i_: addnode.o; +text: .text%__1cJeRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: cfgnode.o; +text: .text%__1cGOopMapJheap_size6kM_i_; +text: .text%__1cHAddNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLMachNopNodeMideal_Opcode6kM_i_: ad_i486.o; +text: .text%__1cLMachNopNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHCompileYout_preserve_stack_slots6F_I_; +text: .text%__1cIAddINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKjmpDirNodeFclone6kM_pnENode__; +text: .text%__1cIciObject2t6MnGHandle__v_; +text: .text%__1cJTraceTime2T6M_v_; +text: .text%__1cPciObjectFactoryGinsert6MipnIciObject_pnNGrowableArray4C2___v_; +text: .text%__1cWShouldNotReachHereNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKRelocationTpd_call_destination6M_pC_; +text: .text%__1cOCallRelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cKRelocationXpd_set_call_destination6MpCi_v_; +text: .text%__1cOCallRelocationPset_destination6MpCi_v_; +text: .text%__1cJlabelOperFlabel6kM_pnFLabel__: ad_i486.o; +text: .text%__1cIHaltNodeGpinned6kM_i_: classes.o; +text: .text%__1cITypeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIRootNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cWMachCallStaticJavaNodePret_addr_offset6M_i_; +text: .text%__1cYCallStaticJavaDirectNodePcompute_padding6kMi_i_; +text: .text%__1cYCallStaticJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cJStartNodeGpinned6kM_i_: classes.o; +text: .text%__1cMTypeKlassPtr2t6MnHTypePtrDPTR_pnHciKlass_i_v_; +text: .text%__1cJloadPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%JVM_IsNaN; +text: .text%__1cJStoreNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLBoxLockNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQciByteCodeStreamQget_method_index6M_i_; +text: .text%__1cFciEnvbTget_instance_klass_for_declared_method_holder6FpnHciKlass__pnPciInstanceKlass__; +text: .text%__1cRInterpretedRFrameKtop_method6kM_nMmethodHandle__: rframe.o; +text: .text%__1cITypeFuncEmake6FpknJTypeTuple_3_pk0_; +text: .text%__1cPciObjectFactoryRcreate_new_object6MpnHoopDesc__pnIciObject__; +text: .text%__1cIGraphKitQkill_dead_locals6M_v_; +text: .text%__1cCosVcurrent_stack_pointer6F_pC_; +text: .text%__1cICallNodeHis_Call6M_p0_: classes.o; +text: .text%__1cJTraceTime2t6MpkcpnMelapsedTimer_iipnMoutputStream__v_; +text: .text%__1cNaddI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cXmembar_acquire_lockNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cJLoadPNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIGraphKitMreset_memory6M_pnENode__; +text: .text%__1cLklassVtableTupdate_super_vtable6MpnNinstanceKlass_pnNmethodOopDesc_i_i_; +text: .text%__1cNinstanceKlassQfind_local_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__i_; +text: .text%__1cNaddI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeQlatency_from_use6kMrnLBlock_Array_rnNGrowableArray4CI__pk0p0_i_; +text: .text%__1cMPhaseChaitinKprompt_use6MpnFBlock_I_i_; +text: .text%__1cICallInfoDset6MnLKlassHandle_1nMmethodHandle_2ipnGThread__v_; +text: .text%__1cOkill_dead_code6FpnENode_pnMPhaseIterGVN__i_: node.o; +text: .text%__1cFParsePload_state_from6Mpn0AFBlock__v_; +text: .text%__1cFParseMmerge_common6Mpn0AFBlock_i_v_; +text: .text%__1cNloadConI0NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJStartNodeIis_Start6M_p0_: callnode.o; +text: .text%__1cICmpINodeDsub6kMpknEType_3_3_; +text: .text%__1cHMemNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cIAddINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLProfileDataPfollow_contents6M_v_: ciMethodData.o; +text: .text%__1cLProfileDataPadjust_pointers6M_v_: ciMethodData.o; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cNsubI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cICodeBlobJis_zombie6kM_i_: codeBlob.o; +text: .text%__1cJStoreNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNmethodOopDescbGupdate_compiled_code_entry_point6Mi_v_; +text: .text%__1cSvframeStreamCommonPfill_from_frame6M_i_; +text: .text%__1cNloadRangeNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFBlockUhoist_LCA_above_defs6Mp01IrnLBlock_Array__1_; +text: .text%__1cLciSignatureHtype_at6kMi_pnGciType__; +text: .text%__1cIciMethodLscale_count6Mi_i_; +text: .text%__1cOGenerateOopMapEpush6MnNCellTypeState__v_; +text: .text%__1cHMonitorKnotify_all6M_i_; +text: .text%__1cKklassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cKklassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cJMarkSweepXrevisit_weak_klass_link6FpnFKlass__v_; +text: .text%__1cFKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cOCallRelocationFvalue6M_pC_: ad_i486.o; +text: .text%__1cNtestP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMTypeKlassPtrEmake6FnHTypePtrDPTR_pnHciKlass_i_pk0_; +text: .text%__1cOoop_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cScompP_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRSignatureIteratorSiterate_parameters6MX_v_; +text: .text%__1cOPhaseIdealLoopIset_idom6MpnENode_2I_v_; +text: .text%__1cNSafePointNodeGpinned6kM_i_: classes.o; +text: .text%__1cOGenerateOopMapDpop6M_nNCellTypeState__; +text: .text%__1cWconstantPoolCacheKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLklassVtableToop_follow_contents6M_v_; +text: .text%__1cLklassVtableToop_adjust_pointers6M_v_; +text: .text%__1cIRootNodeNis_block_proj6kM_pknENode__: classes.o; +text: .text%__1cENodeHis_Type6M_pnITypeNode__: classes.o; +text: .text%__1cNmethodOopDescPis_final_method6kM_i_; +text: .text%__1cOPhaseIdealLoopQconditional_move6MpnENode__2_; +text: .text%__1cNSignatureInfoHdo_void6M_v_: bytecode.o; +text: .text%__1cNloadKlassNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitRnull_check_common6MpnENode_nJBasicType_i_2_; +text: .text%__1cICmpLNodeGOpcode6kM_i_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: constMethodKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: constMethodKlass.o; +text: .text%__1cSconstMethodOopDescLobject_size6Fiiii_i_; +text: .text%__1cKoopFactoryPnew_constMethod6FiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cPClassFileParserMparse_method6MnSconstantPoolHandle_ipnLAccessFlags_pnPtypeArrayHandle_55pnGThread__nMmethodHandle__; +text: .text%__1cKoopFactoryKnew_method6FinLAccessFlags_iiipnGThread__pnNmethodOopDesc__; +text: .text%__1cLklassVtableWneeds_new_vtable_entry6FpnNmethodOopDesc_pnMklassOopDesc_pnHoopDesc_pnNsymbolOopDesc_nLAccessFlags__i_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: constMethodKlass.o; +text: .text%__1cQconstMethodKlassIallocate6MiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cSconstMethodOopDescZset_inlined_tables_length6Miii_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: methodKlass.o; +text: .text%__1cNmethodOopDescLobject_size6Fi_i_; +text: .text%__1cNmethodOopDescJinit_code6M_v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: methodKlass.o; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: methodKlass.o; +text: .text%__1cLmethodKlassIallocate6MnRconstMethodHandle_nLAccessFlags_pnGThread__pnNmethodOopDesc__; +text: .text%__1cNmethodOopDescbAcompute_size_of_parameters6MpnGThread__v_; +text: .text%__1cOGenerateOopMapRdo_exception_edge6MpnOBytecodeStream__v_; +text: .text%__1cOcompU_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTciConstantPoolCacheEfind6Mi_i_; +text: .text%__1cKciTypeFlowGJsrSetJcopy_into6Mp1_v_; +text: .text%__1cJLoadLNodeGOpcode6kM_i_; +text: .text%__1cHOrINodeGOpcode6kM_i_; +text: .text%__1cILoadNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cJMarkSweepNpreserve_mark6FpnHoopDesc_pnLmarkOopDesc__v_; +text: .text%__1cIRewriterOrewrite_method6FnMmethodHandle_rnIintArray_pnGThread__1_; +text: .text%__1cNmethodOopDescLlink_method6FnMmethodHandle__v_; +text: .text%__1cPClassFileParserbDverify_legal_method_modifiers6MiinMsymbolHandle_pnGThread__v_; +text: .text%__1cJloadINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQSystemDictionaryEfind6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cFParseFBlockRsuccessor_for_bci6Mi_p1_; +text: .text%__1cQleaPIdxScaleNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKTypeOopPtrFempty6kM_i_; +text: .text%__1cJleaP8NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%JVM_CurrentThread; +text: .text%__1cPindOffset32OperFscale6kM_i_: ad_i486.o; +text: .text%__1cKBranchDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cLOopMapCacheIentry_at6kMi_pnQOopMapCacheEntry__; +text: .text%__1cRInlineCacheBufferIcontains6FpC_i_; +text: .text%__1cKCompiledICWis_in_transition_state6kM_i_; +text: .text%__1cHOopFlowEmake6FpnFArena_i_p0_; +text: .text%__1cOGenerateOopMapKcheck_type6MnNCellTypeState_1_v_; +text: .text%__1cMMergeMemNodeNgrow_to_match6Mpk0_v_; +text: .text%__1cVeADXRegL_low_onlyOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cVloadConL_low_onlyNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQleaPIdxScaleNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHTypeIntFxdual6kM_pknEType__; +text: .text%__1cVloadConL_low_onlyNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNMachIdealNodePoper_input_base6kM_I_: machnode.o; +text: .text%__1cNSharedRuntimeDf2i6Ff_i_; +text: .text%__1cLLShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cVloadConL_low_onlyNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKTypeAryPtrFklass6kM_pnHciKlass__; +text: .text%__1cRaddI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cRRawBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cRInterpretedRFrameEinit6M_v_; +text: .text%__1cMMergeMemNodeRmake_empty_memory6F_pnENode__; +text: .text%__1cMMergeMemNode2t6MpnENode__v_; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_: callnode.o; +text: .text%__1cHciFieldJwill_link6MpnPciInstanceKlass_nJBytecodesECode__i_; +text: .text%__1cFciEnvXget_field_by_index_impl6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cPciInstanceKlassLfield_cache6M_pnTciConstantPoolCache__; +text: .text%__1cFciEnvSget_field_by_index6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cQciByteCodeStreamJget_field6Mri_pnHciField__; +text: .text%__1cKBlock_ListGremove6MI_v_; +text: .text%__1cECopyXconjoint_words_to_lower6FpnIHeapWord_2I_v_: block.o; +text: .text%__1cXvirtual_call_RelocationIparse_ic6FrpnICodeBlob_rpC5rppnHoopDesc_pi_nNRelocIterator__; +text: .text%__1cWConstantPoolCacheEntryOset_bytecode_16MnJBytecodesECode__v_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: connode.o; +text: .text%__1cIPhaseGVNUtransform_no_reclaim6MpnENode__2_; +text: .text%__1cMLinkResolverbFlinktime_resolve_virtual_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%JVM_GetCPMethodClassNameUTF; +text: .text%__1cKCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNstoreImmBNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHOopFlowFclone6Mp0i_v_; +text: .text%__1cSCallLeafDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKCompiledICOic_destination6kM_pC_; +text: .text%__1cMPhaseChaitinJsplit_DEF6MpnENode_pnFBlock_iIp25nNGrowableArray4CI__i_I_; +text: .text%__1cNGCTaskManagerNresource_flag6MI_i_; +text: .text%__1cNGCTaskManagerYshould_release_resources6MI_i_; +text: .text%__1cNtestP_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLStringTableGlookup6MipHiI_pnHoopDesc__; +text: .text%__1cIBoolNodeJideal_reg6kM_I_: subnode.o; +text: .text%__1cFStateM_sub_Op_Bool6MpknENode__v_; +text: .text%__1cHCmpNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cPSignatureStreamJas_symbol6MpnGThread__pnNsymbolOopDesc__; +text: .text%__1cPciInstanceKlassYunique_concrete_subklass6M_p0_; +text: .text%__1cRcmpFastUnlockNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHAddNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cENodeFis_If6M_pnGIfNode__: subnode.o; +text: .text%__1cKciTypeFlowNmake_range_at6Mi_pn0AFRange__; +text: .text%__1cTCreateExceptionNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKjmpDirNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNaddI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNinstanceKlassVshould_be_initialized6kM_i_; +text: .text%__1cGThreadLis_in_stack6kMpC_i_; +text: .text%__1cKJavaThreadNis_lock_owned6kMpC_i_; +text: .text%__1cJStartNodeGOpcode6kM_i_; +text: .text%__1cPconvF2D_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: subnode.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: subnode.o; +text: .text%__1cOMethodLivenessKBasicBlockJstore_one6Mi_v_; +text: .text%__1cbFCompressedLineNumberWriteStream2t6Mi_v_; +text: .text%__1cSconstMethodOopDescbBcompressed_linenumber_table6kM_pC_; +text: .text%__1cPClassFileParserWparse_linenumber_table6MIIpipnGThread__pC_; +text: .text%__1cNsubI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cMWarmCallInfoHis_cold6kM_i_; +text: .text%__1cFStateK_sub_Op_If6MpknENode__v_; +text: .text%__1cJleaP8NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFParseFBlockJinit_node6Mp0i_v_; +text: .text%__1cFParseFBlockKinit_graph6Mp0_v_; +text: .text%__1cGBitMapVset_union_with_result6M0_i_; +text: .text%__1cOPhaseIdealLoopRregister_new_node6MpnENode_2_v_; +text: .text%__1cNtestI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIPhaseIFGFUnion6MII_v_; +text: .text%__1cIGraphKit2t6MpnIJVMState__v_; +text: .text%__1cKCompiledIC2t6MpnKRelocation__v_; +text: .text%__1cLcastP2INodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadKlassNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cWConstantPoolCacheEntryOset_bytecode_26MnJBytecodesECode__v_; +text: .text%__1cJStartNodeGis_CFG6kM_i_: callnode.o; +text: .text%__1cLBoxLockNodeHsize_of6kM_I_; +text: .text%__1cNinstanceKlassUfollow_static_fields6M_v_; +text: .text%__1cNinstanceKlassUadjust_static_fields6M_v_; +text: .text%__1cRconstantPoolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLklassItableToop_adjust_pointers6M_v_; +text: .text%__1cSinstanceKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLklassItableToop_follow_contents6M_v_; +text: .text%__1cSinstanceKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cRconstantPoolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cICmpUNodeDsub6kMpknEType_3_3_; +text: .text%__1cNincI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKjmpConNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: graphKit.o; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cTciConstantPoolCacheDget6Mi_pv_; +text: .text%__1cScompI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeHis_Goto6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: memnode.o; +text: .text%__1cOGenerateOopMapFppop16MnNCellTypeState__v_; +text: .text%__1cNLoadRangeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cScompI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: subnode.o; +text: .text%__1cHTypePtrLdual_offset6kM_i_; +text: .text%__1cNGCTaskManagerIget_task6MI_pnGGCTask__; +text: .text%__1cGGCTaskKinitialize6M_v_; +text: .text%__1cNGCTaskManagerWdecrement_busy_workers6M_I_; +text: .text%__1cLGCTaskQdDueueGremove6M_pnGGCTask__; +text: .text%__1cNGCTaskManagerWincrement_busy_workers6M_I_; +text: .text%__1cLGCTaskQdDueueHdequeue6M_pnGGCTask__; +text: .text%__1cLGCTaskQdDueueHenqueue6MpnGGCTask__v_; +text: .text%__1cNGCTaskManagerPnote_completion6MI_v_; +text: .text%__1cENodeHlatency6MI_I_; +text: .text%__1cNsubI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNtestI_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFframeZsender_for_compiled_frame6kMpnLRegisterMap_pnICodeBlob_i_0_; +text: .text%__1cPClassFileParserbFparse_constant_pool_class_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNstoreImmPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cICodeBlobLlink_offset6M_i_; +text: .text%__1cFParseFBlockMrecord_state6Mp0_v_; +text: .text%__1cFParseMdo_one_block6M_v_; +text: .text%__1cJCatchNodeIis_Catch6kM_pk0_: classes.o; +text: .text%__1cENodeHdel_out6Mp0_v_: addnode.o; +text: .text%__1cOMethodLivenessKBasicBlockMmerge_normal6MnGBitMap__i_; +text: .text%__1cLConvI2LNodeGOpcode6kM_i_; +text: .text%__1cPClassFileParserZskip_over_field_signature6MpciIpnGThread__1_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: callnode.o; +text: .text%__1cIGraphKitTadd_safepoint_edges6MpnNSafePointNode_i_v_; +text: .text%__1cIJVMStateKclone_deep6kM_p0_; +text: .text%__1cENodeNadd_req_batch6Mp0I_v_; +text: .text%__1cIJVMStateLdebug_depth6kM_I_; +text: .text%__1cSvframeStreamCommonbBfill_from_interpreter_frame6M_v_; +text: .text%__1cJloadINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFParseFmerge6Mi_v_; +text: .text%__1cMLinkResolverZcheck_field_accessability6FnLKlassHandle_11rnPfieldDescriptor_pnGThread__v_; +text: .text%__1cPFieldAccessInfoDset6MnLKlassHandle_nMsymbolHandle_iinJBasicType_nLAccessFlags__v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_iipnGThread__v_; +text: .text%__1cNaddI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cICmpPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cWconstantPoolCacheKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWconstantPoolCacheKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cKBufferBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cFframebFinterpreter_frame_monitor_begin6kM_pnPBasicObjectLock__; +text: .text%__1cGGCTask2t6M_v_; +text: .text%__1cOMacroAssemblerZneeds_explicit_null_check6Fi_i_; +text: .text%__1cNstoreImmBNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cETypeFwiden6kMpk0_2_: type.o; +text: .text%__1cIAddPNodeQmach_bottom_type6FpknIMachNode__pknEType__; +text: .text%__1cJLoadBNodeGOpcode6kM_i_; +text: .text%__1cRsalI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQleaPIdxScaleNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cFframeNis_java_frame6kM_i_; +text: .text%__1cOPhaseIdealLoopGspinup6MpnENode_2222pnLsmall_cache__2_; +text: .text%__1cRcmpFastUnlockNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: callnode.o; +text: .text%__1cJloadCNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNSafePointNodeEhash6kM_I_: callnode.o; +text: .text%__1cMnabxRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cPCountedLoopNodeGOpcode6kM_i_; +text: .text%__1cKciTypeFlowIblock_at6Mipn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cKciTypeFlowFRangeNget_block_for6Mpn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cHTypeAryFxmeet6kMpknEType__3_; +text: .text%__1cLjmpConUNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNaddI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTCreateExceptionNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cGvframe2t6MpknFframe_pknLRegisterMap_pnKJavaThread__v_; +text: .text%__1cLRegisterMap2t6Mpk0_v_; +text: .text%__1cUGenericGrowableArrayMraw_contains6kMpknEGrET__i_; +text: .text%__1cSvframeStreamCommonEnext6M_v_; +text: .text%__1cIMachNodeMcisc_operand6kM_i_: machnode.o; +text: .text%__1cITypeFuncEmake6FpnIciMethod__pk0_; +text: .text%__1cENodeLnonnull_req6kM_p0_; +text: .text%__1cSPSPromotionManagerbBgc_thread_promotion_manager6Fi_p0_; +text: .text%__1cITypeLongFxmeet6kMpknEType__3_; +text: .text%__1cRcmpFastUnlockNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIAddINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKstoreCNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowLStateVector2t6Mp0_v_; +text: .text%__1cOMethodLivenessNwork_list_get6M_pn0AKBasicBlock__; +text: .text%__1cIIndexSetFclear6M_v_: coalesce.o; +text: .text%__1cOPhaseIdealLoopKhandle_use6MpnENode_2pnLsmall_cache_22222_v_; +text: .text%__1cOPhaseIdealLoopOfind_use_block6MpnENode_22222_2_; +text: .text%__1cXmembar_release_lockNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cGvframeKnew_vframe6FpknFframe_pknLRegisterMap_pnKJavaThread__p0_; +text: .text%__1cCosGmalloc6FI_pv_; +text: .text%__1cNmethodOopDescWwas_executed_more_than6kMi_i_; +text: .text%__1cRInterpreterOopMapKinitialize6M_v_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: machnode.o; +text: .text%__1cRMachNullCheckNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cILoopNodeGOpcode6kM_i_; +text: .text%__1cICmpINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: bytecode.o; +text: .text%__1cHCompileKTracePhase2t6MpkcpnMelapsedTimer_i_v_; +text: .text%__1cHCompileKTracePhase2T6M_v_; +text: .text%__1cILoadNodeHsize_of6kM_I_; +text: .text%__1cKciTypeFlowFBlockPis_simpler_than6Mp1_i_; +text: .text%__1cRshrI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cVjava_lang_ClassLoaderbBnon_reflection_class_loader6FpnHoopDesc__2_; +text: .text%__1cFciEnvYget_method_by_index_impl6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cFciEnvTget_method_by_index6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cQciByteCodeStreamKget_method6Mri_pnIciMethod__; +text: .text%__1cFciEnvNlookup_method6MpnNinstanceKlass_2pnNsymbolOopDesc_4nJBytecodesECode__pnNmethodOopDesc__; +text: .text%__1cQleaPIdxScaleNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNCatchProjNodeLbottom_type6kM_pknEType__: cfgnode.o; +text: .text%__1cXindIndexScaleOffsetOperFscale6kM_i_: ad_i486.o; +text: .text%__1cNCatchProjNodeHsize_of6kM_I_: cfgnode.o; +text: .text%__1cKciTypeFlowLStateVectorEmeet6Mpk1_i_; +text: .text%__1cTOopMapForCacheEntryRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cRMachSafePointNode2t6M_v_; +text: .text%__1cHMatcherKmatch_sfpt6MpnNSafePointNode__pnIMachNode__; +text: .text%__1cFframeVinterpreter_frame_bci6kM_i_; +text: .text%__1cQleaPIdxScaleNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOcompI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHemit_cc6FrnKCodeBuffer_ii_v_; +text: .text%__1cJloadLNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cNCatchProjNodeEhash6kM_I_; +text: .text%__1cNincI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cURethrowExceptionNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopHdom_lca6kMpnENode_2_2_; +text: .text%__1cHMatcherScalling_convention6FpnLRegPair_Ii_v_; +text: .text%__1cIGraphKitTadd_exception_state6MpnNSafePointNode__v_; +text: .text%__1cQindOffset32XOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cICallNodeOis_CallRuntime6kM_pknPCallRuntimeNode__: callnode.o; +text: .text%__1cXinsert_anti_dependences6FrpnFBlock_pnENode_rnLBlock_Array__i_: gcm.o; +text: .text%__1cIimmLOperJconstantL6kM_x_: ad_i486_clone.o; +text: .text%__1cWflagsReg_long_LTGEOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cENodeHdel_out6Mp0_v_: lcm.o; +text: .text%__1cXAdaptiveWeightedAverageYcompute_adaptive_average6Mff_f_; +text: .text%__1cIGraphKitOset_all_memory6MpnENode__v_; +text: .text%__1cFKlassIsubklass6kM_p0_; +text: .text%__1cFframebDinterpreter_frame_monitor_end6kM_pnPBasicObjectLock__; +text: .text%__1cHMatcherVReduceInst_Chain_Rule6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cMciMethodDataLhas_trap_at6MpnLProfileData_i_i_; +text: .text%__1cICodeBlobTfix_oop_relocations6MpC1_v_; +text: .text%__1cICodeBlobTfix_oop_relocations6M_v_; +text: .text%__1cNmethodOopDescImask_for6MipnRInterpreterOopMap__v_; +text: .text%__1cLOopMapCacheGlookup6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cRInterpreterOopMap2t6M_v_; +text: .text%__1cRInterpreterOopMap2T6M_v_; +text: .text%__1cRInterpreterOopMapLiterate_oop6MpnNOffsetClosure__v_; +text: .text%__1cRInterpreterOopMapNresource_copy6MpnQOopMapCacheEntry__v_; +text: .text%__1cNinstanceKlassImask_for6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cFframeToops_interpreted_do6MpnKOopClosure_pknLRegisterMap_i_v_; +text: .text%__1cNinstanceKlassKinitialize6MpnGThread__v_; +text: .text%__1cHMatcherPc_frame_pointer6kM_nHOptoRegEName__; +text: .text%__1cMMachCallNode2t6M_v_; +text: .text%__1cFBlockKsched_call6MrnHMatcher_rnLBlock_Array_IrnJNode_List_pipnMMachCallNode_rnJVectorSet__I_; +text: .text%__1cOJNIHandleBlockHoops_do6MpnKOopClosure__v_; +text: .text%__1cHTypeIntEmake6Fii_pk0_; +text: .text%__1cOMethodLivenessKBasicBlockJpropagate6Mp0_v_; +text: .text%__1cNsubI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJOopMapSetTupdate_register_map6FpknFframe_pnICodeBlob_pnLRegisterMap__v_; +text: .text%__1cNsubI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cQSystemDictionaryKfind_class6FiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cKDictionaryKfind_class6MiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cIGraphKitbLset_predefined_input_for_runtime_call6MpnNSafePointNode__v_; +text: .text%JVM_GetCPFieldClassNameUTF; +text: .text%__1cLcastP2INodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: ciStreams.o; +text: .text%__1cHRetNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHBitDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cScompP_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowLStateVectorOpush_translate6MpnGciType__v_; +text: .text%__1cQSystemDictionarybEresolve_instance_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cKciTypeFlowGJsrSet2t6MpnFArena_i_v_; +text: .text%__1cENodeJset_req_X6MIp0pnMPhaseIterGVN__v_; +text: .text%__1cOcompU_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassSallocate_permanent6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNstoreImmPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRcmpFastUnlockNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cScompI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: connode.o; +text: .text%__1cPindOffset32OperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: frame.o; +text: .text%__1cLBoxLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHnmethodIis_alive6kM_i_: nmethod.o; +text: .text%__1cPClassFileParserbGparse_constant_pool_string_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNloadConI0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: connode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: connode.o; +text: .text%__1cHCompileSflatten_alias_type6kMpknHTypePtr__3_; +text: .text%__1cNincI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: callnode.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: memnode.o; +text: .text%__1cLBoxLockNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cMorI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cGOopMap2t6Mii_v_; +text: .text%__1cJOopMapSetKadd_gc_map6MiipnGOopMap__v_; +text: .text%__1cYDebugInformationRecorderKadd_oopmap6MiipnGOopMap__v_; +text: .text%__1cMoutputStreamPupdate_position6MpkcI_v_; +text: .text%__1cMstringStreamFwrite6MpkcI_v_; +text: .text%__1cMFastLockNodeGOpcode6kM_i_; +text: .text%__1cIciMethodRhas_compiled_code6M_i_; +text: .text%__1cMLinkResolverMresolve_pool6FrnLKlassHandle_rnMsymbolHandle_42nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverOresolve_invoke6FrnICallInfo_nGHandle_nSconstantPoolHandle_inJBytecodesECode_pnGThread__v_; +text: .text%__1cNmethodOopDescIbcp_from6kMi_pC_; +text: .text%__1cICallInfoDset6MnLKlassHandle_nMmethodHandle_pnGThread__v_; +text: .text%__1cKstoreCNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNloadKlassNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvL2INodeGOpcode6kM_i_; +text: .text%__1cRMachSafePointNodeSis_MachCallRuntime6M_pnTMachCallRuntimeNode__: ad_i486_misc.o; +text: .text%__1cHnmethodOis_not_entrant6kM_i_: nmethod.o; +text: .text%__1cIGraphKitbDtransfer_exceptions_into_jvms6M_pnIJVMState__; +text: .text%__1cITypeLongEmake6Fx_pk0_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: ciTypeFlow.o; +text: .text%__1cJloadSNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cICallNodeJideal_reg6kM_I_: callnode.o; +text: .text%__1cRMachSafePointNodePis_MachCallLeaf6M_pnQMachCallLeafNode__: ad_i486_misc.o; +text: .text%__1cRMachSafePointNodeLset_oop_map6MpnGOopMap__v_: ad_i486_misc.o; +text: .text%__1cYDebugInformationRecorderNadd_safepoint6MiipnGOopMap__v_; +text: .text%__1cHOopFlowNbuild_oop_map6MpnENode_ipnNPhaseRegAlloc_pi_pnGOopMap__; +text: .text%__1cHCompileTProcess_OopMap_Node6MpnIMachNode_i_v_; +text: .text%__1cENodeGis_Con6kM_I_: callnode.o; +text: .text%__1cILoopNodeHis_Loop6M_p0_: classes.o; +text: .text%__1cOoop_RelocationJpack_data6M_i_; +text: .text%__1cJVectorSetFClear6M_v_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: cfgnode.o; +text: .text%__1cMCallJavaNodeLis_CallJava6kM_pk0_: callnode.o; +text: .text%__1cICallNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_ipnGThread__v_; +text: .text%__1cWConstantPoolCacheEntryJset_field6MnJBytecodesECode_2nLKlassHandle_iinITosState_ii_v_; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: interpreterRuntime.o; +text: .text%__1cSInterpreterRuntimePresolve_get_put6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cYCallStaticJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMachOperIconstant6kM_i_; +text: .text%__1cRaddI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXAdaptiveWeightedAverageGsample6Mf_v_; +text: .text%__1cWMachCallStaticJavaNodeVis_MachCallStaticJava6M_p0_: ad_i486_misc.o; +text: .text%__1cFStateW_sub_Op_CallStaticJava6MpknENode__v_; +text: .text%__1cRinterpretedVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cLklassVtableNput_method_at6MpnNmethodOopDesc_i_v_; +text: .text%__1cMloadConPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNdecI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPVirtualCallDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cENodeHdel_out6Mp0_v_: parse3.o; +text: .text%__1cMloadConLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadKlassNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJleaP8NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKRelocationLunpack_data6M_v_: codeBlob.o; +text: .text%__1cJimmI8OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cKstoreCNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cGOopMapHcopy_to6MpC_v_; +text: .text%__1cRMachSafePointNodeWis_MachCallInterpreter6M_pnXMachCallInterpreterNode__: ad_i486_misc.o; +text: .text%__1cLPhaseValuesHzerocon6MnJBasicType__pnHConNode__; +text: .text%__1cScompI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: classes.o; +text: .text%__1cOGenerateOopMapGppush16MnNCellTypeState__v_; +text: .text%__1cSInterpreterRuntimeOresolve_invoke6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: cpCacheOop.o; +text: .text%__1cMPhaseChaitinLclone_projs6MpnFBlock_IpnENode_4rI_i_; +text: .text%__1cCosEfree6Fpv_v_; +text: .text%__1cKInlineTreeJcallee_at6kMipnIciMethod__p0_; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: thread.o; +text: .text%__1cRcmpFastUnlockNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOleaPIdxOffNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJCodeCacheIcontains6Fpv_i_; +text: .text%__1cQSystemDictionarybCfind_instance_or_array_klass6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cMVirtualSpaceOcommitted_size6kM_I_; +text: .text%__1cNinstanceKlassSlookup_osr_nmethod6kMkpnNmethodOopDesc_i_pnHnmethod__; +text: .text%__1cGRFrame2t6MnFframe_pnKJavaThread_kp0_v_; +text: .text%__1cYCallStaticJavaDirectNodeFreloc6kM_i_; +text: .text%__1cKciTypeFlowLStateVectorJcopy_into6kMp1_v_; +text: .text%__1cPThreadLocalNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJLoadCNodeGOpcode6kM_i_; +text: .text%__1cKciTypeFlowFBlockKsuccessors6MpnQciByteCodeStream_pn0ALStateVector_pn0AGJsrSet__pnNGrowableArray4Cp1___; +text: .text%__1cKciTypeFlowQadd_to_work_list6Mpn0AFBlock__v_; +text: .text%__1cKciTypeFlowOwork_list_next6M_pn0AFBlock__; +text: .text%__1cKciTypeFlowKflow_block6Mpn0AFBlock_pn0ALStateVector_pn0AGJsrSet__v_; +text: .text%__1cWConstantPoolCacheEntryKset_method6MnJBytecodesECode_nMmethodHandle_i_v_; +text: .text%__1cJOopMapSetGall_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure_pFppnHoopDesc_9E_v9B9B_v_; +text: .text%__1cFframeRoops_code_blob_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cIGraphKitNuncommon_trap6MipnHciKlass_pkci_v_; +text: .text%__1cJOopMapSetHoops_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cIGraphKitJmake_load6MpnENode_2pknEType_nJBasicType_i_2_; +text: .text%__1cILoadNodeEmake6FpnENode_22pknHTypePtr_pknEType_nJBasicType__p0_; +text: .text%__1cOleaPIdxOffNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLOptoRuntimeSuncommon_trap_Type6F_pknITypeFunc__; +text: .text%__1cIHaltNode2t6MpnENode_2_v_; +text: .text%__1cMindirectOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cKciTypeFlowFBlock2t6Mp0pn0AFRange_pn0AGJsrSet__v_; +text: .text%__1cJloadLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLPCTableNodeLbottom_type6kM_pknEType__; +text: .text%__1cMCreateExNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cYciExceptionHandlerStreamFcount6M_i_; +text: .text%__1cKciTypeFlowFBlockScompute_exceptions6M_v_; +text: .text%__1cScompU_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFciEnvWget_klass_by_name_impl6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: psTasks.o; +text: .text%__1cWstatic_stub_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cIimmPOperPconstant_is_oop6kM_i_: ad_i486_clone.o; +text: .text%__1cLanyRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cLanyRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOMethodLivenessNwork_list_add6Mpn0AKBasicBlock__v_; +text: .text%__1cMURShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFParsePdo_field_access6Mii_v_; +text: .text%__1cPCountedLoopNodeOis_CountedLoop6M_p0_: classes.o; +text: .text%__1cIAndLNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitQset_saved_ex_oop6FpnNSafePointNode_pnENode__v_; +text: .text%__1cKciTypeFlowPflow_successors6MpnNGrowableArray4Cpn0AFBlock___pn0ALStateVector__v_; +text: .text%__1cIGraphKitUmake_exception_state6MpnENode__pnNSafePointNode__; +text: .text%__1cLcastP2INodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRshrI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGPcDesc2t6Miii_v_; +text: .text%__1cHnmethodKcopy_pc_at6MipnGPcDesc__v_; +text: .text%__1cIPipelineXfunctional_unit_latency6kMIpk0_I_; +text: .text%__1cLTypeInstPtrFxdual6kM_pknEType__; +text: .text%__1cIJumpDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cNdecI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFTypeDCeq6kMpknEType__i_; +text: .text%__1cSindIndexOffsetOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cIMulLNodeGOpcode6kM_i_; +text: .text%__1cOMethodLivenessNmake_block_at6Mipn0AKBasicBlock__2_; +text: .text%__1cENodeHis_Goto6kM_I_: cfgnode.o; +text: .text%__1cHCompileKalias_type6MpnHciField__pn0AJAliasType__; +text: .text%__1cINodeHashJhash_find6MpknENode__p1_; +text: .text%__1cNloadConL0NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_nGHandle_2ipnGThread__pnMklassOopDesc__; +text: .text%__1cLLShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRsalI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJLoadINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLnaxRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cFParseFBlockNlocal_type_at6kMi_pknEType__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodKlass.o; +text: .text%__1cXjava_lang_ref_ReferenceNreferent_addr6FpnHoopDesc__p2_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: connode.o; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__: loopnode.o; +text: .text%__1cMelapsedTimerFstart6M_v_; +text: .text%__1cMelapsedTimerEstop6M_v_; +text: .text%__1cNaddI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: methodLiveness.o; +text: .text%__1cNloadConL0NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cLStringTableGintern6FnGHandle_pHipnGThread__pnHoopDesc__; +text: .text%__1cLStringTableLhash_string6FpHi_i_; +text: .text%__1cMMergeMemNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cEUTF8Hstrrchr6FpWiW_1_; +text: .text%__1cPshlI_eReg_1NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMCreateExNodeGpinned6kM_i_: classes.o; +text: .text%__1cIXorINodeGOpcode6kM_i_; +text: .text%__1cRindIndexScaleOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cNloadConL0NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cVLoaderConstraintTableWfind_loader_constraint6MnMsymbolHandle_nGHandle__ppnVLoaderConstraintEntry__; +text: .text%__1cMPhaseIterGVNJtransform6MpnENode__2_; +text: .text%__1cHi2sNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLLShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRindIndexScaleOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMPhaseChaitinTsplit_Rematerialize6MpnENode_pnFBlock_IrInNGrowableArray4CI__ipIp2i_2_; +text: .text%__1cOGenerateOopMapFppush6MpnNCellTypeState__v_; +text: .text%__1cENodeHis_Call6M_pnICallNode__: ad_i486_misc.o; +text: .text%__1cIConLNodeGOpcode6kM_i_; +text: .text%__1cHCompileZintrinsic_insertion_index6MpnIciMethod_i_i_; +text: .text%__1cNstoreImmBNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQSystemDictionarybOfind_constrained_instance_or_array_klass6FnMsymbolHandle_nGHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cILoopNodeHis_Loop6M_p0_: loopnode.o; +text: .text%__1cRandI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKstoreINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cGciTypeMis_classless6kM_i_: ciType.o; +text: .text%__1cTleaPIdxScaleOffNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHTypeIntFwiden6kMpknEType__3_; +text: .text%__1cNCallGenerator2t6MpnIciMethod__v_; +text: .text%__1cIGraphKit2t6M_v_; +text: .text%__1cHMulNodeEhash6kM_I_; +text: .text%__1cFStateM_sub_Op_ConP6MpknENode__v_; +text: .text%__1cIciSymbol2t6MnMsymbolHandle__v_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: codeBlob.o; +text: .text%__1cIAddLNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitNset_map_clone6MpnNSafePointNode__v_; +text: .text%__1cIGraphKitOreplace_in_map6MpnENode_2_v_; +text: .text%__1cPClassFileParserbCverify_legal_field_signature6MnMsymbolHandle_1pnGThread__v_; +text: .text%__1cPClassFileParserXverify_legal_field_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cScompU_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHCompilebAallow_range_check_smearing6kM_i_; +text: .text%__1cITypeLongEmake6Fxxi_pk0_; +text: .text%JVM_GetCPMethodNameUTF; +text: .text%__1cPmethodDataKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNmodI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLjmpConUNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNLoadKlassNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJeRegIOperFclone6kM_pnIMachOper__; +text: .text%__1cQciByteCodeStreamZget_declared_field_holder6M_pnPciInstanceKlass__; +text: .text%__1cQciByteCodeStreamWget_field_holder_index6M_i_; +text: .text%__1cNSafePointNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: classes.o; +text: .text%__1cTconstantPoolOopDescOstring_at_impl6FnSconstantPoolHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cTconstantPoolOopDescbCverify_constant_pool_resolve6FnSconstantPoolHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cNdecI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLOptoRuntimeOnew_objArray_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cQjava_lang_StringQbasic_create_oop6FpnQtypeArrayOopDesc_ipnGThread__pnHoopDesc__; +text: .text%__1cPcheckCastPPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadSNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKReturnNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cMLinkResolverbNlinktime_resolve_virtual_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cNmethodOopDescbHhas_unloaded_classes_in_signature6FnMmethodHandle_pnGThread__i_; +text: .text%__1cPindOffset32OperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cPindOffset32OperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cNinstanceKlassbBallocate_permanent_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cQmark_inner_loops6FpnIPhaseCFG_pnFBlock__v_: block.o; +text: .text%__1cPindOffset32OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cPindOffset32OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cPindOffset32OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOemit_d32_reloc6FrnKCodeBuffer_inJrelocInfoJrelocType_i_v_; +text: .text%__1cJloadLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadSNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cEUTF8Sconvert_to_unicode6FpkcpHi_v_; +text: .text%__1cKjmpConNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeFis_If6M_pnGIfNode__: memnode.o; +text: .text%__1cLBuildCutout2t6MpnIGraphKit_pnENode_ff_v_; +text: .text%__1cLBuildCutout2T6M_v_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: graphKit.o; +text: .text%__1cPDictionaryEntrybAcontains_protection_domain6kMpnHoopDesc__i_; +text: .text%__1cNPhaseRegAllocKoffset2reg6kMi_nHOptoRegEName__; +text: .text%__1cFParseRensure_memory_phi6Mii_pnHPhiNode__; +text: .text%__1cHTypeAryCeq6kMpknEType__i_; +text: .text%__1cMorI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadRangeNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIAddINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOPhaseIdealLoopIsink_use6MpnENode_2_v_; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: memnode.o; +text: .text%__1cKMemoryPoolYrecord_peak_memory_usage6M_v_; +text: .text%__1cNinstanceKlassKlink_class6MpnGThread__v_; +text: .text%__1cTStackWalkCompPolicyRcompilation_level6MnMmethodHandle_i_i_; +text: .text%__1cIMachNodeTmay_be_short_branch6kM_i_: ad_i486_misc.o; +text: .text%__1cKRegionNodeGpinned6kM_i_: loopnode.o; +text: .text%__1cJloadINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKBranchDataNis_BranchData6M_i_: ciMethodData.o; +text: .text%__1cJloadBNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKMemBarNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIJumpDataLis_JumpData6M_i_: ciMethodData.o; +text: .text%__1cNinstanceKlassPinitialize_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: frame.o; +text: .text%__1cJxRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cTconstantPoolOopDescbBbasic_type_for_signature_at6Mi_nJBasicType__; +text: .text%__1cUSafepointSynchronizeOsafepoint_safe6FpnKJavaThread_nPJavaThreadState__i_; +text: .text%__1cUThreadSafepointStateXexamine_state_of_thread6Mi_v_; +text: .text%__1cNsubI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMethodLivenessKBasicBlockQcompute_gen_kill6MpnIciMethod__v_; +text: .text%__1cOMethodLivenessKBasicBlock2t6Mp0ii_v_; +text: .text%__1cOcompI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cJcmpOpOperFccode6kM_i_: ad_i486_clone.o; +text: .text%__1cOGenerateOopMapbAget_basic_block_containing6kMi_pnKBasicBlock__; +text: .text%__1cIciMethodRget_flow_analysis6M_pnKciTypeFlow__; +text: .text%__1cENodeHdel_out6Mp0_v_: ifnode.o; +text: .text%__1cEUTF8Ounicode_length6Fpkci_i_; +text: .text%__1cRSignatureIteratorSskip_optional_size6M_v_; +text: .text%__1cKjmpDirNodeOis_pc_relative6kM_i_: ad_i486_misc.o; +text: .text%__1cKjmpDirNodeTmay_be_short_branch6kM_i_: ad_i486_misc.o; +text: .text%__1cKRegionNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFParseKdo_get_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cScompP_mem_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapSget_basic_block_at6kMi_pnKBasicBlock__; +text: .text%__1cJleaP8NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cISubINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cRaddI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKReturnNodeKmatch_edge6kMI_I_; +text: .text%__1cRshrI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPcmpFastLockNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowGJsrSetNapply_control6Mp0pnQciByteCodeStream_pn0ALStateVector__v_; +text: .text%__1cPClassFileParserWparse_field_attributes6MnSconstantPoolHandle_iHpHpi2pnPtypeArrayHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbCverify_legal_field_modifiers6MiipnGThread__v_; +text: .text%__1cKReturnNodeGOpcode6kM_i_; +text: .text%__1cKHandleAreaHoops_do6MpnKOopClosure__v_; +text: .text%__1cNchunk_oops_do6FpnKOopClosure_pnFChunk_pc_I_: handles.o; +text: .text%__1cGThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_virtual_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cMLinkResolverUresolve_virtual_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cKstorePNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLStringTableGintern6FpnNsymbolOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cNsymbolOopDescKas_unicode6kMri_pH_; +text: .text%__1cFKlassXcan_be_statically_bound6FpnNmethodOopDesc__i_; +text: .text%__1cQjava_lang_StringMbasic_create6FpnQtypeArrayOopDesc_ipnGThread__nGHandle__; +text: .text%__1cIMachNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIMachNodeOpipeline_class6F_pknIPipeline__; +text: .text%__1cUParallelScavengeHeapNtlab_capacity6kM_I_; +text: .text%__1cKjmpConNodeOis_pc_relative6kM_i_: ad_i486_misc.o; +text: .text%__1cKjmpConNodeTmay_be_short_branch6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: connode.o; +text: .text%__1cPshlI_eReg_1NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachNodeHtwo_adr6kM_I_: machnode.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: machnode.o; +text: .text%__1cHi2sNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: memnode.o; +text: .text%__1cENodeRis_cisc_alternate6kM_i_: machnode.o; +text: .text%__1cICallNodeSis_CallDynamicJava6kM_pknTCallDynamicJavaNode__: callnode.o; +text: .text%__1cKTypeRawPtrHget_con6kM_i_; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: machnode.o; +text: .text%__1cKStoreLNodeGOpcode6kM_i_; +text: .text%__1cPClassFileParserbIparse_constant_pool_fieldref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cScompU_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cHConNodeLout_RegMask6kM_rknHRegMask__: classes.o; +text: .text%__1cETypeRget_typeflow_type6FpnGciType__pk0_; +text: .text%__1cHPhiNodeEmake6FpnENode_2_p0_; +text: .text%__1cHAddNodePadd_of_identity6kMpknEType_3_3_; +text: .text%__1cQindOffset32XOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cRandI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMWarmCallInfoKalways_hot6F_p0_; +text: .text%__1cMWarmCallInfoGis_hot6kM_i_; +text: .text%__1cMTypeKlassPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cMPhaseChaitinKFind_const6kMI_I_; +text: .text%__1cMPhaseChaitinKFind_const6kMpknENode__I_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopTransform.o; +text: .text%__1cHCompileFstart6kM_pnJStartNode__; +text: .text%JVM_GetCPFieldSignatureUTF; +text: .text%__1cTemit_java_to_interp6FrnKCodeBuffer__v_; +text: .text%__1cKRelocationJpack_data6M_i_: relocInfo.o; +text: .text%__1cKCodeBufferMstart_a_stub6M_v_; +text: .text%__1cKCodeBufferKend_a_stub6M_v_; +text: .text%__1cPThreadRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cPThreadRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cIBoolNodeHsize_of6kM_I_; +text: .text%__1cFParseUprofile_taken_branch6Mi_v_; +text: .text%__1cPmethodDataKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cPmethodDataKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMloadConLNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cTleaPIdxScaleOffNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFframeNis_glue_frame6kM_i_; +text: .text%__1cPcmpFastLockNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cXjava_lang_ref_ReferenceJnext_addr6FpnHoopDesc__p2_; +text: .text%__1cKstoreLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRMachNullCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: machnode.o; +text: .text%__1cICodeHeapLheader_size6F_I_; +text: .text%__1cScompU_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_: methodDataOop.o; +text: .text%__1cXjava_lang_ref_ReferencePdiscovered_addr6FpnHoopDesc__p2_; +text: .text%__1cKJavaThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cTleaPIdxScaleOffNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJTypeTupleKmake_range6FpnLciSignature__pk0_; +text: .text%__1cPcheckCastPPNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJStoreNodeSIdeal_masked_input6MpnIPhaseGVN_I_pnENode__; +text: .text%__1cIAddINodeIadd_ring6kMpknEType_3_3_; +text: .text%jni_DeleteLocalRef: jni.o; +text: .text%__1cJTypeTupleLmake_domain6FpnPciInstanceKlass_pnLciSignature__pk0_; +text: .text%__1cSMemBarCPUOrderNodeGOpcode6kM_i_; +text: .text%__1cWShouldNotReachHereNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_Halt6MpknENode__v_; +text: .text%__1cIHaltNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cWflagsReg_long_EQdDNEOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cMoutputStreamDput6Mc_v_; +text: .text%__1cJStoreNodeZIdeal_sign_extended_input6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cWThreadLocalAllocBufferVinitialize_statistics6M_v_; +text: .text%__1cWThreadLocalAllocBufferImax_size6F_I_; +text: .text%__1cWThreadLocalAllocBufferFclear6M_v_; +text: .text%__1cSInterpreterRuntimeDldc6FpnKJavaThread_i_v_; +text: .text%__1cLProfileDataOtranslate_from6Mp0_v_: ciMethodData.o; +text: .text%__1cFframeLreal_sender6kMpnLRegisterMap__0_; +text: .text%__1cTStackWalkCompPolicyIsenderOf6MpnGRFrame_pnNGrowableArray4C2___2_; +text: .text%__1cENodeLbottom_type6kM_pknEType__; +text: .text%__1cFframeTis_first_java_frame6kM_i_; +text: .text%__1cGRFrameGcaller6M_p0_; +text: .text%__1cGRFrameMset_distance6Mi_v_; +text: .text%__1cGRFrameKnew_RFrame6FnFframe_pnKJavaThread_kp0_4_; +text: .text%__1cHConNodeEmake6FpknEType__p0_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlassKlass.o; +text: .text%__1cWThreadLocalAllocBufferVaccumulate_statistics6MIi_v_; +text: .text%__1cWThreadLocalAllocBufferGresize6M_v_; +text: .text%__1cJloadPNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIGraphKitOtoo_many_traps6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cWflagsReg_long_LEGTOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cKJNIHandlesKmake_local6FpnGThread_pnHoopDesc__pnI_jobject__; +text: .text%__1cSObjectSynchronizerKslow_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cQindOffset32XOperFscale6kM_i_: ad_i486.o; +text: .text%__1cUThreadSafepointStateMroll_forward6Mn0AMsuspend_type_pnHnmethod_i_v_; +text: .text%__1cGThreadQunboost_priority6Fp0_v_; +text: .text%__1cUThreadSafepointStateHrestart6M_v_; +text: .text%__1cIGraphKitTtoo_many_recompiles6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cGIfNodeHsize_of6kM_I_: classes.o; +text: .text%__1cZPhaseConservativeCoalesceKupdate_ifg6MIIpnIIndexSet_2_v_; +text: .text%__1cIIndexSetEswap6Mp0_v_; +text: .text%__1cZPhaseConservativeCoalesceMunion_helper6MpnENode_2II222pnFBlock_I_v_; +text: .text%__1cLcastP2INodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitZadd_exception_states_from6MpnIJVMState__v_; +text: .text%__1cScompP_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKoopFactoryXnew_permanent_charArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cQjava_lang_StringbBcreate_tenured_from_unicode6FpHipnGThread__nGHandle__; +text: .text%__1cLStringTableJbasic_add6MinGHandle_pHiIpnGThread__pnHoopDesc__; +text: .text%__1cPcmpFastLockNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cScompP_mem_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapHget_var6Mi_nNCellTypeState__; +text: .text%__1cXcmpL_reg_flags_LTGENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: relocInfo.o; +text: .text%__1cQCallLeafNoFPNodeGOpcode6kM_i_; +text: .text%__1cHCompileOcall_generator6MpnIciMethod_ipnIJVMState_if_pnNCallGenerator__; +text: .text%__1cNciCallProfileRapply_prof_factor6Mf_v_; +text: .text%__1cIciMethodTcall_profile_at_bci6Mi_nNciCallProfile__; +text: .text%__1cHCompileOfind_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cXmembar_acquire_lockNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: multnode.o; +text: .text%__1cENodeHdel_out6Mp0_v_: compile.o; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_: codeBlob.o; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_kpnGRFrame__v_; +text: .text%__1cMLinkResolverbEruntime_resolve_special_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_4ipnGThread__v_; +text: .text%__1cMLinkResolverbFlinktime_resolve_special_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cMLinkResolverUresolve_special_call6FrnICallInfo_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cXindIndexScaleOffsetOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cXindIndexScaleOffsetOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cXindIndexScaleOffsetOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cPBytecode_invokeFindex6kM_i_; +text: .text%__1cNCatchProjNode2t6MpnENode_Ii_v_; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_: ciMethodData.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: cfgnode.o; +text: .text%__1cQciByteCodeStreamXget_method_holder_index6M_i_; +text: .text%__1cFParseHdo_call6M_v_; +text: .text%__1cIGraphKitWround_double_arguments6MpnIciMethod__v_; +text: .text%__1cIGraphKitTround_double_result6MpnIciMethod__v_; +text: .text%__1cFParseZcan_not_compile_call_site6MpnIciMethod_pnPciInstanceKlass__i_; +text: .text%__1cQciByteCodeStreambAget_declared_method_holder6M_pnHciKlass__; +text: .text%__1cFParseMprofile_call6MpnENode__v_; +text: .text%__1cKstorePNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMMachProjNodeHsize_of6kM_I_: classes.o; +text: .text%__1cPciInstanceKlassFsuper6M_p0_; +text: .text%__1cNCatchProjNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFKlassOis_subclass_of6kMpnMklassOopDesc__i_; +text: .text%__1cSindIndexOffsetOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMeADXRegLOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cKciTypeFlowLStateVectorJdo_invoke6MpnQciByteCodeStream_i_v_; +text: .text%__1cHMulNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMtlsLoadPNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cNstoreImmPNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLPCTableNodeHsize_of6kM_I_: classes.o; +text: .text%__1cLPCTableNodeKis_PCTable6kM_pk0_: classes.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: cfgnode.o; +text: .text%__1cJStartNodeIis_Start6M_p0_: classes.o; +text: .text%__1cLPCTableNodeEhash6kM_I_; +text: .text%__1cMURShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNObjectMonitorEexit6MpnGThread__v_; +text: .text%__1cIProjNodeDcmp6kMrknENode__I_; +text: .text%__1cXindIndexScaleOffsetOperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cSmembar_acquireNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKDataLayoutKinitialize6MCHi_v_; +text: .text%__1cKDataLayoutPneeds_array_len6FC_i_; +text: .text%__1cScompP_mem_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNincI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMindirectOperKin_RegMask6kMi_pknHRegMask__; +text: .text%jni_GetObjectField: jni.o; +text: .text%__1cSCompareAndSwapNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cRInvocationCounterJset_carry6M_v_; +text: .text%__1cJloadBNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRAbstractAssemblerHbind_to6MrnFLabel_i_v_; +text: .text%__1cRAbstractAssemblerEbind6MrnFLabel__v_; +text: .text%__1cOCompilerOracleOshould_exclude6FnMmethodHandle__i_; +text: .text%__1cMnabxRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNObjectMonitorFenter6MpnGThread__v_; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: ad_i486_misc.o; +text: .text%__1cFframeUentry_frame_is_first6kM_i_; +text: .text%__1cHciFieldPinitialize_from6MpnPfieldDescriptor__v_; +text: .text%__1cHPhiNodeMslice_memory6kMpknHTypePtr__p0_; +text: .text%__1cKMemBarNodeEhash6kM_I_; +text: .text%__1cPstoreImmI16NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIciSymbolJmake_impl6Fpkc_p0_; +text: .text%__1cIciSymbolEmake6Fpkc_p0_; +text: .text%__1cXcmpL_reg_flags_LTGENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNloadConI0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNtestI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitNcast_not_null6MpnENode__2_; +text: .text%__1cKEntryPointFentry6kMnITosState__pC_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: nmethod.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: loopnode.o; +text: .text%__1cJleaP8NodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cKGCStatInfoMset_gc_usage6MinLMemoryUsage_i_v_; +text: .text%__1cWCallLeafNoFPDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cGRFrameLis_compiled6kM_i_: rframe.o; +text: .text%__1cRInterpretedRFrameOis_interpreted6kM_i_: rframe.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: callnode.o; +text: .text%__1cRsalI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPciInstanceKlassUget_canonical_holder6Mi_p0_; +text: .text%__1cENodeHdel_out6Mp0_v_: callnode.o; +text: .text%__1cISubINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cILoadNodeDcmp6kMrknENode__I_; +text: .text%__1cMDisplacementEbind6MrnFLabel_ipnRAbstractAssembler__v_; +text: .text%__1cTStackWalkCompPolicyMshouldInline6FnMmethodHandle_fi_pkc_; +text: .text%__1cTconstantPoolOopDescMklass_at_put6MipnMklassOopDesc__v_: constantPoolOop.o; +text: .text%__1cFTypeDEhash6kM_i_; +text: .text%__1cIGraphKitHjava_bc6kM_nJBytecodesECode__; +text: .text%__1cKarrayKlassLobject_size6kMi_i_; +text: .text%__1cIGraphKitNbuiltin_throw6MnODeoptimizationLDeoptReason_pnENode__v_; +text: .text%__1cIciMethodOshould_exclude6M_i_; +text: .text%__1cKInlineTreeWfind_subtree_from_root6Fp0pnIJVMState_pnIciMethod_i_1_; +text: .text%__1cIciMethodNshould_inline6M_i_; +text: .text%__1cKInlineTreeMshouldInline6kMpnIciMethod_irnNciCallProfile_pnMWarmCallInfo__pkc_; +text: .text%__1cTpass_initial_checks6FpnIciMethod_i1_i_; +text: .text%__1cOCompilerOracleNshould_inline6FnMmethodHandle__i_; +text: .text%__1cIciMethodbAinterpreter_throwout_count6kM_i_; +text: .text%__1cKInlineTreeMok_to_inline6MpnIciMethod_pnIJVMState_rnNciCallProfile_pnMWarmCallInfo__8_; +text: .text%__1cKInlineTreeNtry_to_inline6MpnIciMethod_irnNciCallProfile_pnMWarmCallInfo__pkc_; +text: .text%__1cVExceptionHandlerTableJadd_entry6MnRHandlerTableEntry__v_; +text: .text%__1cKstoreINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLRShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRaddI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTCreateExceptionNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOjmpLoopEndNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKstoreLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cGThreadXclear_pending_exception6M_v_; +text: .text%__1cLRethrowNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cLklassVtableIindex_of6kMpnNmethodOopDesc_i_i_; +text: .text%__1cFStateM_sub_Op_CmpP6MpknENode__v_; +text: .text%__1cMorI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cLTypeInstPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cIciMethodWwas_executed_more_than6Mi_i_; +text: .text%__1cRshrI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConINodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cKstoreLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHMonitorEwait6Mil_i_; +text: .text%__1cOGenerateOopMapGppload6MpnNCellTypeState_i_v_; +text: .text%__1cTCompareAndSwapLNodeGOpcode6kM_i_; +text: .text%__1cScompP_mem_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKarrayKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cJAssemblerEmovl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cLOpaque1NodeGOpcode6kM_i_; +text: .text%__1cSObjectSynchronizerJslow_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cbBopt_virtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cPcmpFastLockNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLnaxRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNCompileBrokerYcheck_compilation_result6FnMmethodHandle_iippnHnmethod__i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlass.o; +text: .text%__1cJloadCNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKJavaThreadbHcheck_and_handle_async_exceptions6Mi_v_; +text: .text%__1cbAjni_check_async_exceptions6FpnKJavaThread__v_: jni.o; +text: .text%__1cKciTypeFlowLStateVectorStype_meet_internal6FpnGciType_3p0_3_; +text: .text%__1cLeDXRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cJFieldTypeSskip_optional_size6FpnNsymbolOopDesc_pi_v_; +text: .text%__1cQjmpCon_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQjmpCon_shortNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSobjArrayKlassKlassIoop_size6kMpnHoopDesc__i_: objArrayKlassKlass.o; +text: .text%__1cIGraphKitZset_all_rewritable_memory6MpnENode__v_; +text: .text%__1cIGraphKitTset_all_memory_call6MpnENode__v_; +text: .text%__1cJloadSNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKjmpDirNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cKjmpDirNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIBoolNodeDcmp6kMrknENode__I_; +text: .text%__1cKjmpDirNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cScompI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cICodeBlobLoop_addr_at6kMi_ppnHoopDesc__; +text: .text%__1cRaddI_eReg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadRangeNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJFieldTypeYis_valid_array_signature6FpnNsymbolOopDesc__i_; +text: .text%__1cJFieldTypeOget_array_info6FpnNsymbolOopDesc_pip2pnGThread__nJBasicType__; +text: .text%__1cFKlassMnext_sibling6kM_p0_; +text: .text%__1cPCheckCastPPNodeJideal_reg6kM_I_: connode.o; +text: .text%__1cJloadCNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cOClearArrayNodeGOpcode6kM_i_; +text: .text%__1cPThreadLocalNodeGOpcode6kM_i_; +text: .text%__1cRcmpFastUnlockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMeADXRegLOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cFParseRbranch_prediction6Mrf_f_; +text: .text%__1cRandI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLeAXRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLeAXRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cPshlI_eReg_1NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowLStateVectorMdo_getstatic6MpnQciByteCodeStream__v_; +text: .text%__1cISubINodeDsub6kMpknEType_3_3_; +text: .text%__1cNCompileBrokerXcompilation_is_in_queue6FnMmethodHandle_i_i_; +text: .text%__1cSInterpreterRuntimebAfrequency_counter_overflow6FpnKJavaThread_pC_x_; +text: .text%__1cRindIndexScaleOperFscale6kM_i_: ad_i486.o; +text: .text%__1cNaddI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cLmethodKlassNoop_is_method6kM_i_: methodKlass.o; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceKlass.o; +text: .text%__1cENodeHdel_out6Mp0_v_: loopnode.o; +text: .text%__1cJxRegIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cMLinkResolverVresolve_invokevirtual6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_: loopnode.o; +text: .text%__1cIParmNodeJideal_reg6kM_I_; +text: .text%__1cICodeHeapSallocated_capacity6kM_I_; +text: .text%__1cHciKlassNis_subtype_of6Mp0_i_; +text: .text%__1cNtestP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICHeapObj2n6FI_pv_; +text: .text%__1cENodeHdel_out6Mp0_v_: loopTransform.o; +text: .text%__1cQleaPIdxScaleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cNloadConI0NodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cHTypeAryFempty6kM_i_; +text: .text%__1cKTypeAryPtrFempty6kM_i_; +text: .text%__1cCosFsleep6FpnGThread_xi_i_; +text: .text%__1cNgetTimeMillis6F_x_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: scopeDesc.o; +text: .text%__1cIos_sleep6Fxi_i_: os_solaris.o; +text: .text%__1cOPhaseIdealLoopOsplit_thru_phi6MpnENode_2i_2_; +text: .text%__1cENodeRlatency_from_uses6kMrnLBlock_Array_rnNGrowableArray4CI___i_; +text: .text%__1cXPhaseAggressiveCoalesceYinsert_copy_with_overlap6MpnFBlock_pnENode_II_v_; +text: .text%__1cCosOis_interrupted6FpnGThread_i_i_; +text: .text%__1cNaddI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_i_v_; +text: .text%__1cNloadKlassNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateN_sub_Op_LoadP6MpknENode__v_; +text: .text%__1cNmethodOopDescWload_signature_classes6FnMmethodHandle_pnGThread__i_; +text: .text%__1cTconstantPoolOopDescbDresolve_string_constants_impl6FnSconstantPoolHandle_pnGThread__v_; +text: .text%__1cNCompileBrokerOcompile_method6FnMmethodHandle_i1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNCompileBrokerTcompile_method_base6FnMmethodHandle_ii1ipkcpnGThread__pnHnmethod__; +text: .text%__1cITypeLongFempty6kM_i_; +text: .text%__1cJloadINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFArenaRdestruct_contents6M_v_; +text: .text%__1cJloadSNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_CmpI6MpknENode__v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodKlass.o; +text: .text%__1cIAddPNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJAssemblerDjcc6Mn0AJCondition_rnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cPcmpFastLockNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNmulL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJStoreNodeEmake6FpnENode_22pknHTypePtr_2nJBasicType__p0_; +text: .text%__1cIGraphKitPstore_to_memory6MpnENode_22nJBasicType_i_2_; +text: .text%__1cHi2sNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMapJdo_method6Miiii_v_; +text: .text%__1cLSymbolTableFprobe6Fpkci_pnNsymbolOopDesc__; +text: .text%__1cLcastP2INodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompP_mem_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNincI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNdecI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIciObjectFklass6M_pnHciKlass__; +text: .text%__1cQPSGenerationPoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cQPSGenerationPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cQPSGenerationPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cFParseYprofile_not_taken_branch6M_v_; +text: .text%__1cLRShiftLNodeGOpcode6kM_i_; +text: .text%__1cKMemBarNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cMURShiftLNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: parse2.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: cfgnode.o; +text: .text%__1cIMulINodeGOpcode6kM_i_; +text: .text%__1cPBytecode_invokeJsignature6kM_pnNsymbolOopDesc__; +text: .text%__1cIConDNodeGOpcode6kM_i_; +text: .text%__1cKInlineTreePshouldNotInline6kMpnIciMethod_pnMWarmCallInfo__pkc_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: bytecode.o; +text: .text%__1cFframebGinterpreter_callee_receiver_addr6MnMsymbolHandle__ppnHoopDesc__; +text: .text%__1cNandL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6FnUtypeArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cKciTypeFlowIcan_trap6MrnQciByteCodeStream__i_; +text: .text%__1cNxorI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cZload_long_indOffset32OperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cFParseNthrow_to_exit6MpnNSafePointNode__v_; +text: .text%__1cJloadLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKjmpConNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKjmpConNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKjmpConNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cMLinkResolverbElinktime_resolve_static_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cLencode_Copy6FrnKCodeBuffer_ii_v_; +text: .text%__1cQjmpDir_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQjmpDir_shortNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOJNIHandleBlockOallocate_block6FpnGThread__p0_; +text: .text%__1cENodeIadd_prec6Mp0_v_; +text: .text%__1cLjmpConUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMachNodeSalignment_required6kM_i_: machnode.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: machnode.o; +text: .text%__1cKType_ArrayEgrow6MI_v_; +text: .text%__1cMorI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMorI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cUmembar_cpu_orderNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSconstMethodOopDescbEchecked_exceptions_length_addr6kM_pH_; +text: .text%__1cFParseFdo_if6MpnENode_2nIBoolTestEmask_2_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: ciMethodData.o; +text: .text%__1cLBoxLockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJxRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLciSignature2t6MpnHciKlass_pnIciSymbol__v_; +text: .text%__1cIciMethod2t6MnMmethodHandle__v_; +text: .text%__1cIConINodeHget_int6kMpi_i_: classes.o; +text: .text%__1cIciMethodbHhas_unloaded_classes_in_signature6M_i_; +text: .text%__1cNinstanceKlassVis_same_class_package6FpnHoopDesc_pnNsymbolOopDesc_24_i_; +text: .text%__1cNloadRangeNodeIpipeline6kM_pknIPipeline__; +text: .text%jni_ExceptionOccurred: jni.o; +text: .text%__1cQConstantIntValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cHTypeAryEmake6FpknEType_pknHTypeInt__pk0_; +text: .text%__1cJloadCNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNsubI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cEUTF8Fequal6FpWi1i_i_; +text: .text%__1cSHighResTimeSamplerLtake_sample6M_x_: statSampler.o; +text: .text%__1cLStatSamplerLsample_data6FpnMPerfDataList__v_; +text: .text%__1cRis_error_reported6F_i_; +text: .text%__1cMPeriodicTaskOreal_time_tick6FI_v_; +text: .text%__1cLStatSamplerOcollect_sample6F_v_; +text: .text%__1cPStatSamplerTaskEtask6M_v_: statSampler.o; +text: .text%__1cNWatcherThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cMPeriodicTaskMtime_to_wait6F_I_: thread.o; +text: .text%jni_GetByteArrayRegion: jni.o; +text: .text%__1cSCallLeafDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2I_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQPlaceholderTableKfind_entry6MiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cOoop_RelocationHoops_do6MpFppnHoopDesc__v_v_; +text: .text%__1cKBufferBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cKBufferBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cSdivD_reg_roundNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQleaPIdxScaleNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cKstorePNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateP_sub_Op_LShiftI6MpknENode__v_; +text: .text%jni_GetArrayLength: jni.o; +text: .text%__1cICodeHeapIcapacity6kM_I_; +text: .text%__1cMCodeHeapPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cKMemoryPoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cMCodeHeapPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cRresolve_and_patch6FppnHoopDesc__v_; +text: .text%__1cMorI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIPipelinePoperand_latency6kMIpk0_I_; +text: .text%__1cIMachOperEtype6kM_pknEType__; +text: .text%__1cVCompressedWriteStreamEgrow6M_v_; +text: .text%__1cPClassFileParserbDverify_legal_method_signature6MnMsymbolHandle_1pnGThread__i_; +text: .text%__1cIregFOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cNloadRangeNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJAssemblerFpushl6MpnMRegisterImpl__v_; +text: .text%JVM_Write; +text: .text%__1cDhpiFwrite6FipkvI_I_: jvm.o; +text: .text%__1cPstoreImmI16NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPCheckCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJStartNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cKciTypeFlowLStateVectorLdo_getfield6MpnQciByteCodeStream__v_; +text: .text%__1cFParseMvisit_blocks6M_v_; +text: .text%__1cNsubI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMLinkResolverTresolve_static_call6FrnICallInfo_rnLKlassHandle_nMsymbolHandle_53iipnGThread__v_; +text: .text%__1cRsalI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTconvD2I_reg_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowGJsrSetSis_compatible_with6Mp1_i_; +text: .text%__1cOcompU_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadBNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLOpaque1NodeEhash6kM_I_; +text: .text%__1cXcmpL_reg_flags_LTGENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_: classes.o; +text: .text%__1cJcmpOpOperGnegate6M_v_: ad_i486_clone.o; +text: .text%__1cJAssemblerKemit_arith6MiipnMRegisterImpl_2_v_; +text: .text%__1cGBitMapIset_from6M0_v_; +text: .text%__1cGThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%__1cKMemBarNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKjmpConNodeGnegate6M_v_: ad_i486_misc.o; +text: .text%__1cFMutexMjvm_raw_lock6M_v_; +text: .text%__1cFMutexOjvm_raw_unlock6M_v_; +text: .text%JVM_RawMonitorEnter; +text: .text%JVM_RawMonitorExit; +text: .text%__1cXmembar_release_lockNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cITypeLongEmake6Fxx_pk0_; +text: .text%__1cNaddL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKcmpOpUOperFccode6kM_i_: ad_i486_clone.o; +text: .text%__1cPClassFileParserUskip_over_field_name6MpciI_1_; +text: .text%__1cLjmpConUNodeOis_pc_relative6kM_i_: ad_i486_misc.o; +text: .text%__1cLjmpConUNodeTmay_be_short_branch6kM_i_: ad_i486_misc.o; +text: .text%__1cNstoreImmPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cURethrowExceptionNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFParseOreturn_current6MpnENode__v_; +text: .text%__1cETypeCeq6kMpk0_i_; +text: .text%__1cXinitialize_static_field6FpnPfieldDescriptor_pnGThread__v_: classFileParser.o; +text: .text%__1cHSubNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKReflectionVis_same_class_package6FpnMklassOopDesc_2_i_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnMklassOopDesc__i_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: bytecode.o; +text: .text%__1cOJNIHandleBlockNrelease_block6Fp0pnGThread__v_; +text: .text%__1cOGenerateOopMapTmerge_state_into_bb6MpnKBasicBlock__v_; +text: .text%__1cRaddI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLeCXRegIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cMMonitorValue2t6MpnKScopeValue_nILocation__v_; +text: .text%__1cMMonitorValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cLBoxLockNodeKis_BoxLock6kM_pk0_: classes.o; +text: .text%__1cLBoxLockNodeKstack_slot6FpnENode__nHOptoRegEName__; +text: .text%__1cKCastPPNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cFParseRoptimize_inlining6MpnIciMethod_ipnPciInstanceKlass_24irnKInlineTreeLInlineStyle_r2_v_; +text: .text%__1cQimprove_receiver6FpnPciInstanceKlass_pknLTypeInstPtr_ri_1_; +text: .text%__1cJloadPNodeFreloc6kM_i_; +text: .text%__1cQStackFrameStream2t6MpnKJavaThread_i_v_; +text: .text%__1cQSystemDictionaryStry_get_next_class6F_pnMklassOopDesc__; +text: .text%__1cNinstanceKlassKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cFciEnvVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cKDictionaryStry_get_next_class6M_pnMklassOopDesc__; +text: .text%__1cLRShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFParseSmerge_memory_edges6MpnMMergeMemNode_ii_v_; +text: .text%__1cJloadBNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cFBlockTimplicit_null_check6MrnLBlock_Array_rnNGrowableArray4CI__pnENode_6_v_; +text: .text%__1cJTypeTupleFxdual6kM_pknEType__; +text: .text%__1cJTimeStampJupdate_to6Mx_v_; +text: .text%__1cJTimeStampGupdate6M_v_; +text: .text%__1cMloadConLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmulL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSmembar_releaseNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRaddI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cISubINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: loopnode.o; +text: .text%__1cRsarI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_AddI6MpknENode__v_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__: loopnode.o; +text: .text%__1cOGenerateOopMapEppop6MpnNCellTypeState__v_; +text: .text%__1cLCastP2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIciMethodLis_accessor6kM_i_; +text: .text%__1cRScavengeRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cRScavengeRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cOGenerateOopMapUreachable_basicblock6Fp0ipi_v_; +text: .text%__1cPsarI_eReg_1NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cXindIndexScaleOffsetOperOindex_position6kM_i_: ad_i486.o; +text: .text%__1cXindIndexScaleOffsetOperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cNSafePointNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRmethodDataOopDescLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cZload_long_indOffset32OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMloadConPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cScompU_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHUNICODELutf8_length6FpHi_i_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: memnode.o; +text: .text%__1cRInterpretedRFrameKtop_vframe6kM_pnKjavaVFrame__: rframe.o; +text: .text%__1cRinterpretedVFrameDbcp6kM_pC_; +text: .text%__1cRinterpretedVFrameDbci6kM_i_; +text: .text%__1cMCallLeafNodeLis_CallLeaf6kM_pk0_: classes.o; +text: .text%__1cJAssemblerEpopl6MpnMRegisterImpl__v_; +text: .text%__1cKStoreCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLRethrowNodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: loopnode.o; +text: .text%__1cPsarI_eReg_1NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cZload_long_indOffset32OperFscale6kM_i_: ad_i486.o; +text: .text%__1cIciMethodVhas_balanced_monitors6M_i_; +text: .text%__1cFArenaEused6kM_I_; +text: .text%__1cFParseNdo_all_blocks6M_v_; +text: .text%__1cOParseGeneratorJcan_parse6FpnIciMethod_i_i_; +text: .text%__1cFParseLbuild_exits6M_v_; +text: .text%__1cFParseIdo_exits6M_v_; +text: .text%__1cFParse2t6MpnIJVMState_pnIciMethod_f_v_; +text: .text%__1cFParseQcreate_entry_map6M_pnNSafePointNode__; +text: .text%__1cOParseGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cFParseLinit_blocks6M_v_; +text: .text%__1cIciMethodPcan_be_compiled6M_i_; +text: .text%__1cFframeNoops_entry_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cPJavaCallWrapperHoops_do6MpnKOopClosure__v_; +text: .text%__1cRRawBytecodeStream2t6MnMmethodHandle__v_; +text: .text%__1cNCallGeneratorKfor_inline6FpnIciMethod_f_p0_; +text: .text%__1cFParsePdo_method_entry6M_v_; +text: .text%__1cKstoreCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQComputeCallStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cTStackWalkCompPolicyPshouldNotInline6FnMmethodHandle__pkc_; +text: .text%__1cRaddI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTcompareAndSwapLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPSignatureStreamRas_symbol_or_null6M_pnNsymbolOopDesc__; +text: .text%__1cYDebugInformationRecorderNadd_dependent6MpnPciInstanceKlass_pnIciMethod__v_; +text: .text%__1cHi2sNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIget_long6kM_x_; +text: .text%__1cQciByteCodeStreamSget_constant_index6kM_i_; +text: .text%__1cICHeapObj2k6Fpv_v_; +text: .text%__1cFArena2T6M_v_; +text: .text%jni_GetSuperclass: jni.o; +text: .text%__1cLklassVtableXvtable_accessibility_at6Mi_n0AKAccessType__; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmBNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: cfgnode.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: cfgnode.o; +text: .text%__1cNdecI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cScompU_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNstoreImmINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cPJavaCallWrapper2t6MnMmethodHandle_nGHandle_pnJJavaValue_pnGThread__v_; +text: .text%__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_; +text: .text%__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cRJavaCallArgumentsKparameters6M_pi_; +text: .text%__1cCosbCstack_shadow_pages_available6FpnGThread_nMmethodHandle__i_; +text: .text%__1cRruntime_type_from6FpnJJavaValue__nJBasicType__: javaCalls.o; +text: .text%__1cTAbstractInterpreterbFsize_top_interpreter_activation6FpnNmethodOopDesc__i_; +text: .text%__1cJMultiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPJavaCallWrapper2T6M_v_; +text: .text%__1cUGenericGrowableArrayEgrow6Mi_v_; +text: .text%__1cSCallLeafDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowFBlockPclone_loop_head6Mp0ip1pn0AGJsrSet__3_; +text: .text%__1cITypeFuncFxdual6kM_pknEType__; +text: .text%__1cNxorI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHMatcherPstack_alignment6F_I_; +text: .text%__1cKInlineTree2t6MpnHCompile_pk0pnIciMethod_pnIJVMState_if_v_; +text: .text%__1cMLinkResolverXresolve_klass_no_update6FrnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cHciField2t6MpnPciInstanceKlass_i_v_; +text: .text%__1cTconstantPoolOopDescbCklass_ref_at_if_loaded_check6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cMURShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLeSIRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cQSystemDictionaryVadd_loader_constraint6FnMsymbolHandle_nGHandle_2pnGThread__v_; +text: .text%__1cVLoaderConstraintTableJadd_entry6MnMsymbolHandle_pnMklassOopDesc_nGHandle_34pnGThread__i_; +text: .text%__1cNloadKlassNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSloadL_volatileNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapCpp6MpnNCellTypeState_2_v_; +text: .text%__1cDCHANprocess_class6FnLKlassHandle_pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___nMsymbolHandle_6_v_; +text: .text%__1cSObjectSynchronizerXidentity_hash_value_for6FnGHandle__i_; +text: .text%__1cQjava_lang_StringGlength6FpnHoopDesc__i_; +text: .text%__1cHoopDescSslow_identity_hash6M_i_; +text: .text%__1cQindOffset32XOperLdisp_is_oop6kM_i_: ad_i486.o; +text: .text%__1cQindOffset32XOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cQindOffset32XOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cQindOffset32XOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cIMulINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cGciType2t6MnLKlassHandle__v_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodDataKlass.o; +text: .text%__1cHciKlass2t6MnLKlassHandle__v_; +text: .text%__1cLRShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKInlineTreeYcompute_callee_frequency6kMi_f_; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cKInlineTreebCbuild_inline_tree_for_callee6MpnIciMethod_pnIJVMState_i_p0_; +text: .text%__1cIciMethodbBinterpreter_call_site_count6Mi_i_; +text: .text%__1cHciKlassOis_subclass_of6Mp0_i_; +text: .text%__1cMtlsLoadPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHnmethodHoops_do6MpnKOopClosure__v_; +text: .text%__1cJGC_lockerNlock_critical6FpnKJavaThread__v_: jni.o; +text: .text%__1cJAssemblerElock6M_v_; +text: .text%__1cRindIndexScaleOperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cKRegionNodeEhash6kM_I_: loopnode.o; +text: .text%__1cFKlassTarray_klass_or_null6Mi_pnMklassOopDesc__; +text: .text%__1cXmembar_release_lockNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHnmethodbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cLklassVtableKis_miranda6FpnNmethodOopDesc_pnPobjArrayOopDesc_pnMklassOopDesc__i_; +text: .text%__1cENodeHdel_out6Mp0_v_: library_call.o; +text: .text%__1cRandI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNIdealLoopTreeIset_nest6MI_i_; +text: .text%__1cNIdealLoopTreeMcounted_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cPshlI_eReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlass.o; +text: .text%__1cRalign_code_offset6Fi_I_; +text: .text%__1cRciVirtualCallDataOtranslate_from6MpnLProfileData__v_; +text: .text%__1cMLinkResolverVresolve_invokespecial6FrnICallInfo_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cJAssemblerHcmpxchg6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cHnmethodPis_locked_by_vm6kM_i_: nmethod.o; +text: .text%__1cONMethodSweeperPprocess_nmethod6FpnHnmethod__v_; +text: .text%__1cKTypeAryPtrFxdual6kM_pknEType__; +text: .text%__1cRmethodDataOopDescJbci_to_dp6Mi_pC_; +text: .text%__1cOjmpLoopEndNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: ad_i486.o; +text: .text%__1cMVM_OperationPevaluation_mode6kM_n0AEMode__: vm_operations.o; +text: .text%__1cRsalI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cbAPSEvacuateFollowersClosureHdo_void6M_v_: psScavenge.o; +text: .text%__1cHnmethodVcleanup_inline_caches6M_v_; +text: .text%__1cIDivINodeGOpcode6kM_i_; +text: .text%__1cMURShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cHnmethodLis_unloaded6kM_i_: nmethod.o; +text: .text%__1cIimmDOperJconstantD6kM_d_: ad_i486_clone.o; +text: .text%__1cQSystemDictionarybBresolve_array_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cSInterpreterRuntimeMmonitorenter6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2I_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cScompU_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cScompU_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPciInstanceKlass2t6MnLKlassHandle__v_; +text: .text%jni_GetPrimitiveArrayCritical: jni.o; +text: .text%jni_ReleasePrimitiveArrayCritical: jni.o; +text: .text%__1cXcmpL_reg_flags_LEGTNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMrep_stosNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJStartNodeOis_block_start6kM_i_: callnode.o; +text: .text%__1cSsafePoint_pollNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cWConstantPoolCacheEntryPbytecode_number6FnJBytecodesECode__i_: cpCacheOop.o; +text: .text%__1cWConstantPoolCacheEntryLis_resolved6kMnJBytecodesECode__i_: cpCacheOop.o; +text: .text%__1cOMethodLivenessKBasicBlockIload_two6Mi_v_; +text: .text%__1cIGraphKitMarray_length6MpnENode__2_; +text: .text%__1cJloadCNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXmembar_release_lockNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cMCreateExNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNincI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFArena2t6M_v_; +text: .text%__1cMindirectOperFscale6kM_i_: ad_i486.o; +text: .text%__1cHMulNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLsymbolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cLPCTableNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMCreateExNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSInterpreterRuntimeLmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cLRShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJcmpOpOperFclone6kM_pnIMachOper__; +text: .text%__1cIJVMState2t6Mi_v_; +text: .text%__1cKPerfStringKset_string6Mpkc_v_; +text: .text%__1cJStartNodeOis_block_start6kM_i_: classes.o; +text: .text%__1cPciObjArrayKlassSis_obj_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cNeFlagsRegOperFclone6kM_pnIMachOper__; +text: .text%__1cMMachCallNodeHis_Call6M_pnICallNode__: ad_i486_misc.o; +text: .text%__1cKTypeRawPtrCeq6kMpknEType__i_; +text: .text%__1cFStateQ_sub_Op_CreateEx6MpknENode__v_; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjectFactory.o; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciObjectFactory.o; +text: .text%__1cLjmpConUNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cLjmpConUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLjmpConUNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cLOpaque1NodeLbottom_type6kM_pknEType__: connode.o; +text: .text%__1cEDict2t6MpFpkv2_ipF2_i_v_; +text: .text%__1cEDict2T6M_v_; +text: .text%__1cNCatchProjNodeDcmp6kMrknENode__I_; +text: .text%__1cIGraphKitRmake_slow_call_ex6MpnENode_pnPciInstanceKlass__v_; +text: .text%__1cSCountedLoopEndNodeKstride_con6kM_i_; +text: .text%__1cRmethodDataOopDescKmileage_of6FpnNmethodOopDesc__i_; +text: .text%__1cQconstMethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cHCompileXin_preserve_stack_slots6M_I_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: symbolKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: symbolKlass.o; +text: .text%__1cPconvL2I_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLmethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cKTypeRawPtrFxmeet6kMpknEType__3_; +text: .text%__1cTJvmtiEventCollectorYunset_jvmti_thread_state6M_v_; +text: .text%__1cZload_long_indOffset32OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cZload_long_indOffset32OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cZload_long_indOffset32OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2T6M_v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2t6M_v_; +text: .text%__1cIMinINodeGOpcode6kM_i_; +text: .text%__1cHMemNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cHMemNodeScalculate_adr_type6FpknEType_pknHTypePtr__6_; +text: .text%__1cHMatcherXadjust_incoming_stk_arg6MnHOptoRegEName__2_; +text: .text%__1cFStateM_sub_Op_CmpU6MpknENode__v_; +text: .text%__1cSPSKeepAliveClosureGdo_oop6MppnHoopDesc__v_: psScavenge.o; +text: .text%__1cFTypeDEmake6Fd_pk0_; +text: .text%jni_IsSameObject: jni.o; +text: .text%__1cRCompilationPolicybIreset_counter_for_invocation_event6MnMmethodHandle__v_; +text: .text%__1cKMemoryPoolHoops_do6MpnKOopClosure__v_; +text: .text%__1cQindOffset32XOperMdisp_as_type6kM_pknHTypePtr__: ad_i486.o; +text: .text%__1cQindOffset32XOperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cQindOffset32XOperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cNstoreImmBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTcompareAndSwapLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLlog2_intptr6Fi_i_: mulnode.o; +text: .text%__1cHOrINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConDNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cScompP_mem_eRegNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cHMulNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRaddI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LEGTNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKJavaThreadQlast_java_vframe6MpnLRegisterMap__pnKjavaVFrame__; +text: .text%__1cKjavaVFrameNis_java_frame6kM_i_: vframe.o; +text: .text%__1cTStackWalkCompPolicyXmethod_invocation_event6MnMmethodHandle_pnGThread__v_; +text: .text%__1cTStackWalkCompPolicyVfindTopInlinableFrame6MpnNGrowableArray4CpnGRFrame____2_; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_nMmethodHandle__v_; +text: .text%__1cRMachSafePointNodeLis_MachCall6M_pnMMachCallNode__: ad_i486_misc.o; +text: .text%__1cKStoreBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNandL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLCounterDataOis_CounterData6M_i_: methodDataOop.o; +text: .text%__1cPClassFileParserbCverify_legal_class_modifiers6MipnGThread__v_; +text: .text%__1cENodeHget_int6kMpi_i_; +text: .text%__1cOcompI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNinstanceKlassbDcheck_valid_for_instantiation6MipnGThread__v_; +text: .text%__1cYSurvivorMutableSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cTleaPIdxScaleOffNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJcmpOpOperFequal6kM_i_: ad_i486_clone.o; +text: .text%__1cMorI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFKlassMset_subklass6MpnMklassOopDesc__v_; +text: .text%__1cKRegionNodeOhas_unique_phi6kM_pnHPhiNode__; +text: .text%__1cNnegI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRsarI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPsarI_eReg_1NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLRethrowNodeKmatch_edge6kMI_I_; +text: .text%__1cNinstanceKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cPshrI_eReg_1NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLAccessFlagsPatomic_set_bits6Mi_v_; +text: .text%__1cQVMOperationQdDueueLqueue_empty6Mi_i_; +text: .text%__1cTCreateExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFTypeFEhash6kM_i_; +text: .text%__1cVExceptionHandlerTableMadd_subtable6MipnNGrowableArray4Ci__2_v_; +text: .text%__1cQLibraryIntrinsicKis_virtual6kM_i_: library_call.o; +text: .text%__1cScompP_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNxorI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cScompI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cScompI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cUCompressedReadStreamMraw_read_int6FrpC_i_: vframe.o; +text: .text%__1cRMachSafePointNodePis_MachCallJava6M_pnQMachCallJavaNode__: ad_i486_misc.o; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_: classes.o; +text: .text%__1cIimmIOperFclone6kM_pnIMachOper__; +text: .text%__1cJleaP8NodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cIciObject2t6M_v_; +text: .text%__1cMloadConINodeFclone6kM_pnENode__; +text: .text%__1cIMulLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIGraphKitYcombine_exception_states6MpnNSafePointNode_2_v_; +text: .text%__1cNinstanceKlassPlink_class_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cMciMethodData2t6M_v_; +text: .text%__1cPciObjectFactoryUget_empty_methodData6M_pnMciMethodData__; +text: .text%__1cRitableMethodEntryKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cRaddL_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQinstanceRefKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQinstanceRefKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cJloadBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIHaltNodeEhash6kM_I_: classes.o; +text: .text%__1cNmulL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQinit_input_masks6FIrnHRegMask_1_p0_: matcher.o; +text: .text%__1cXvirtual_call_RelocationLunpack_data6M_v_; +text: .text%__1cSconstMethodOopDescZchecked_exceptions_length6kM_i_; +text: .text%__1cNIdealLoopTreeObeautify_loops6MpnOPhaseIdealLoop__i_; +text: .text%__1cOjmpLoopEndNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cLklassVtableMget_mirandas6FpnNGrowableArray4CpnNmethodOopDesc___pnMklassOopDesc_pnPobjArrayOopDesc_8_v_; +text: .text%__1cMrep_stosNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapLbb_mark_fct6Fp0ipi_v_; +text: .text%__1cFStateO_sub_Op_StoreP6MpknENode__v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: phaseX.o; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: callnode.o; +text: .text%__1cPshlI_eReg_1NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFKlassKsuperklass6kM_pnNinstanceKlass__; +text: .text%__1cSindIndexOffsetOperFscale6kM_i_: ad_i486.o; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: cfgnode.o; +text: .text%__1cNaddL_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cXJNI_ArgumentPusherVaArgKget_object6M_v_: jni.o; +text: .text%__1cMMutableSpaceFclear6M_v_; +text: .text%__1cQjmpCon_shortNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cQjmpCon_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cQjmpCon_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKjmpConNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cPshrI_eReg_1NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMindirectOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cMindirectOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cMindirectOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cYmulI_imm_RShift_highNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPstoreImmI16NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cParrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cParrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cFKlassQset_next_sibling6MpnMklassOopDesc__v_; +text: .text%__1cUParallelScavengeHeapMmem_allocate6MIii_pnIHeapWord__; +text: .text%__1cNstoreImmBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cOGenerateOopMapIdo_field6Miiii_v_; +text: .text%__1cOGenerateOopMapRsigchar_to_effect6McipnNCellTypeState__2_; +text: .text%__1cIGraphKitXset_edges_for_java_call6MpnMCallJavaNode_i_v_; +text: .text%__1cQciTypeArrayKlassTis_type_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cIVerifierRshould_verify_for6FpnHoopDesc__i_; +text: .text%__1cKcmpOpUOperGnegate6M_v_: ad_i486_clone.o; +text: .text%__1cLjmpConUNodeGnegate6M_v_: ad_i486_misc.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: loopnode.o; +text: .text%__1cRandI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKstoreBNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapTmerge_state_vectors6MpnNCellTypeState_2_i_; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_2_v_; +text: .text%__1cNnegI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitZset_results_for_java_call6MpnMCallJavaNode__pnENode__; +text: .text%__1cOstackSlotDOperEtype6kM_pknEType__: ad_i486.o; +text: .text%JVM_GetMethodIxModifiers; +text: .text%__1cNSCMemProjNodeGOpcode6kM_i_; +text: .text%__1cNandL_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cJleaP8NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQjava_lang_StringGequals6FpnHoopDesc_pHi_i_; +text: .text%__1cENodeGis_Sub6M_pnHSubNode__: classes.o; +text: .text%__1cTcompareAndSwapLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cScompU_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cScompU_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cScompU_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cYmulI_imm_RShift_highNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPcmpFastLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNinstanceKlassbCfind_local_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cFStateO_sub_Op_StoreI6MpknENode__v_; +text: .text%__1cJLoadSNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%JVM_IsInterface; +text: .text%__1cNinstanceKlassWfind_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cPciInstanceKlassTget_field_by_offset6Mii_pnHciField__; +text: .text%__1cFStateM_sub_Op_RegL6MpknENode__v_; +text: .text%__1cRMachNullCheckNodeLout_RegMask6kM_rknHRegMask__: machnode.o; +text: .text%__1cRshrI_eReg_immNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cScompP_mem_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadSNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cXconvI2L_reg_reg_zexNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMvframeStream2t6MpnKJavaThread_i_v_; +text: .text%__1cRsalI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJimmI0OperFclone6kM_pnIMachOper__; +text: .text%__1cNloadConI0NodeFclone6kM_pnENode__; +text: .text%__1cWflagsReg_long_LTGEOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cQciByteCodeStreamMget_constant6M_nKciConstant__; +text: .text%__1cFciEnvbAget_constant_by_index_impl6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cFciEnvVget_constant_by_index6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cRMachNullCheckNode2t6MpnENode_2I_v_; +text: .text%__1cNMachIdealNodeJnum_opnds6kM_I_: machnode.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: machnode.o; +text: .text%__1cXconvI2L_reg_reg_zexNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLLShiftLNodeGOpcode6kM_i_; +text: .text%__1cTciConstantPoolCacheGinsert6Mipv_v_; +text: .text%__1cSobjArrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSobjArrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cEhash6Fpkc1_I_; +text: .text%__1cQput_after_lookup6FnMsymbolHandle_0ppnLNameSigHash__i_; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cZresource_reallocate_bytes6FpcII_0_; +text: .text%__1cLeCXRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cScompI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLCastP2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cUreloc_java_to_interp6F_I_; +text: .text%__1cTsize_java_to_interp6F_I_; +text: .text%__1cKstoreINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cScompU_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOstackSlotLOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: graphKit.o; +text: .text%__1cSCallLeafDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCallLeafDirectNodeRis_safepoint_node6kM_i_: ad_i486_misc.o; +text: .text%__1cQciByteCodeStreamPget_klass_index6M_i_; +text: .text%__1cQComputeCallStackHdo_void6M_v_: generateOopMap.o; +text: .text%__1cMnadxRegIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cJCHAResultOis_monomorphic6kM_i_; +text: .text%__1cJCHAResult2t6MnLKlassHandle_nMsymbolHandle_2pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___n0E_i_v_; +text: .text%__1cIciMethodXfind_monomorphic_target6MpnHciKlass_22_p0_; +text: .text%__1cDCHAManalyze_call6FnLKlassHandle_11nMsymbolHandle_2_pnJCHAResult__; +text: .text%__1cRaddL_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKMemBarNode2t6M_v_; +text: .text%__1cHciField2t6MpnPfieldDescriptor__v_; +text: .text%__1cKMemBarNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMloadConFNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKPSYoungGenNused_in_bytes6kM_I_; +text: .text%__1cNSafepointBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_; +text: .text%__1cbCCompiledCodeSafepointHandlerYcaller_must_gc_arguments6kM_i_: safepoint.o; +text: .text%__1cUThreadSafepointStateYcaller_must_gc_arguments6kM_i_; +text: .text%__1cWstatic_stub_RelocationJpack_data6M_i_; +text: .text%__1cMloadConFNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSReferenceProcessorOprocess_phase36MppnHoopDesc_ipnRBoolObjectClosure_pnKOopClosure_pnLVoidClosure__v_; +text: .text%__1cSReferenceProcessorOprocess_phase26MppnHoopDesc_pnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSReferenceProcessorbAprocess_discovered_reflist6MppnHoopDesc_pnPReferencePolicy_i_v_; +text: .text%__1cSReferenceProcessorbAenqueue_discovered_reflist6MpnHoopDesc_p2_v_; +text: .text%__1cNandL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLLShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOFastUnlockNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cOGenerateOopMapLmerge_state6Fp0ipi_v_; +text: .text%__1cMnegF_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIModINodeGOpcode6kM_i_; +text: .text%__1cHUNICODEHas_utf86FpHi_pc_; +text: .text%__1cKcmpOpUOperNgreater_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cMLinkResolverbCresolve_virtual_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cIMaxINodeGOpcode6kM_i_; +text: .text%__1cQjmpDir_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKjmpDirNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cQjmpDir_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cQjmpDir_shortNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConFNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKMemBarNodeJis_MemBar6kM_pk0_: classes.o; +text: .text%__1cIMachNodeKconst_size6kM_i_: machnode.o; +text: .text%__1cIMachNodeFreloc6kM_i_: machnode.o; +text: .text%__1cIMachNodeTmay_be_short_branch6kM_i_: machnode.o; +text: .text%__1cWImplicitExceptionTableGappend6MII_v_; +text: .text%__1cIGraphKitOinsert_mem_bar6MpnKMemBarNode__v_; +text: .text%__1cIGraphKitbBset_arguments_for_java_call6MpnMCallJavaNode__v_; +text: .text%__1cIGraphKitJpush_node6MnJBasicType_pnENode__v_: callGenerator.o; +text: .text%__1cNCallGeneratorCtf6kM_pknITypeFunc__; +text: .text%__1cNCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cLOptoRuntimebCcomplete_monitor_unlocking_C6FpnHoopDesc_pnJBasicLock__v_; +text: .text%__1cITypeNodeHis_Type6M_p0_: classes.o; +text: .text%__1cNdecI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHdel_out6Mp0_v_: chaitin.o; +text: .text%__1cYmulI_imm_RShift_highNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cICodeBlobRis_at_poll_return6MpC_i_; +text: .text%__1cHGCCauseJto_string6Fn0AFCause__pkc_; +text: .text%__1cSconstMethodOopDescYchecked_exceptions_start6kM_pnXCheckedExceptionElement__; +text: .text%__1cLBoxLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTleaPIdxScaleOffNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNGCTaskManagerRset_resource_flag6MIi_v_; +text: .text%__1cScompI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cScompI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cScompI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFParseQarray_addressing6MnJBasicType_ippknEType__pnENode__; +text: .text%__1cHCompileSregister_intrinsic6MpnNCallGenerator__v_; +text: .text%__1cNmulL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMeADXRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cRcmpFastUnlockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQleaPIdxScaleNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cXcopy_u2_with_conversion6FpH0i_v_: classFileParser.o; +text: .text%__1cPClassFileParserYparse_checked_exceptions6MpHInSconstantPoolHandle_pnGThread__1_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_locking_C6FpnHoopDesc_pnJBasicLock_pnKJavaThread__v_; +text: .text%__1cSvframeStreamCommonbHskip_method_invoke_and_aux_frames6M_v_; +text: .text%__1cIGraphKitbMset_predefined_output_for_runtime_call6MpnENode_pnMMergeMemNode__v_; +text: .text%__1cNminI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMLinkResolverbCresolve_special_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cPciInstanceKlassLfind_method6MpnIciSymbol_2_pnIciMethod__; +text: .text%__1cJloadLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitJpush_node6MnJBasicType_pnENode__v_: parse1.o; +text: .text%__1cJCHAResultSmonomorphic_target6kM_nMmethodHandle__; +text: .text%__1cNCallGeneratorPfor_direct_call6FpnIciMethod__p0_; +text: .text%__1cTDirectCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cMWarmCallInfoLalways_cold6F_p0_; +text: .text%__1cNaddL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMrep_stosNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFciEnvRfind_system_klass6MpnIciSymbol__pnHciKlass__; +text: .text%__1cPClassFileStreamHskip_u26MipnGThread__v_; +text: .text%__1cZCallInterpreterDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOClearArrayNodeKmatch_edge6kMI_I_; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_: methodDataOop.o; +text: .text%__1cLConvI2LNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPThreadLocalNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLBlock_ArrayEgrow6MI_v_; +text: .text%__1cLConvL2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQciByteCodeStreamJget_klass6Mri_pnHciKlass__; +text: .text%__1cNandL_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNandL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHi2sNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMjniIdSupportNto_method_oop6FpnK_jmethodID__pnNmethodOopDesc__; +text: .text%__1cRSignatureIterator2t6MpnGThread_pnNsymbolOopDesc__v_; +text: .text%__1cIAndINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cOGenerateOopMapQnext_bb_start_pc6MpnKBasicBlock__i_; +text: .text%__1cMLinkResolverbBresolve_static_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: callnode.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: callnode.o; +text: .text%__1cOGenerateOopMapNrestore_state6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapJinterp_bb6MpnKBasicBlock__v_; +text: .text%__1cPCheckCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cISubINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIAndINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cTCallDynamicJavaNodeGOpcode6kM_i_; +text: .text%__1cXcmpL_reg_flags_LEGTNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPCheckCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKRegionNodeOis_block_start6kM_i_: loopnode.o; +text: .text%__1cSloadL_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUvisit_all_interfaces6FpnPobjArrayOopDesc_pnXInterfaceVisiterClosure__v_; +text: .text%__1cJPhaseLiveHcompute6MI_v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: live.o; +text: .text%__1cIPhaseIFGEinit6MI_v_; +text: .text%__1cMPhaseChaitinQgather_lrg_masks6Mi_v_; +text: .text%__1cRjmpConU_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRjmpConU_shortNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cVmerge_point_too_heavy6FpnHCompile_pnENode__i_: loopopts.o; +text: .text%__1cMloadConPNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHRetNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: loopnode.o; +text: .text%__1cNandI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIAddLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFStateN_sub_Op_LoadI6MpknENode__v_; +text: .text%__1cPsarI_eReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXmembar_acquire_lockNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHCmpNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKstoreBNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXconvI2L_reg_reg_zexNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: connode.o; +text: .text%__1cNinstanceKlassXmark_dependent_nmethods6MpnMklassOopDesc__i_; +text: .text%__1cOPhaseIdealLoopPis_counted_loop6MpnENode_pnNIdealLoopTree__2_; +text: .text%__1cKoopFactoryTnew_system_objArray6FipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: oopFactory.o; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: oopFactory.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: oopFactory.o; +text: .text%__1cOGenerateOopMapHset_var6MinNCellTypeState__v_; +text: .text%__1cOleaPIdxOffNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescOis_initializer6kM_i_; +text: .text%__1cPciObjArrayKlassNelement_klass6M_pnHciKlass__; +text: .text%__1cSFixupMirrorClosureJdo_object6MpnHoopDesc__v_: universe.o; +text: .text%__1cNloadKlassNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIGraphKitPpush_pair_local6Mi_v_: parse2.o; +text: .text%__1cNMemoryServiceXtrack_memory_pool_usage6FpnKMemoryPool__v_; +text: .text%__1cPshlI_eReg_1NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPClassFileParserbHparse_constant_pool_integer_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKstoreBNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerFpushl6Mi_v_; +text: .text%__1cKmethodOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: reg_split.o; +text: .text%__1cMLinkResolverUresolve_invokestatic6FrnICallInfo_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKklassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNxorI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNxorI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHMatcherMreturn_value6Fii_nLRegPair__; +text: .text%__1cNinstanceKlassScopy_static_fields6MpnSPSPromotionManager__v_; +text: .text%__1cSinstanceKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cbACallCompiledJavaDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRandI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSsafePoint_pollNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSloadL_volatileNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKoopFactoryYnew_permanent_shortArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cLcastP2INodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: frame.o; +text: .text%__1cFStateR_sub_Op_LoadRange6MpknENode__v_; +text: .text%__1cOcompU_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICmpLNodeDsub6kMpknEType_3_3_; +text: .text%__1cHMemNodeHsize_of6kM_I_; +text: .text%__1cYCallStaticJavaDirectNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cYCallStaticJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNRelocIteratorJset_limit6MpC_v_; +text: .text%__1cIMachNodeTmay_be_short_branch6kM_i_: ad_i486.o; +text: .text%__1cRsarI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cHPhiNodeDcmp6kMrknENode__I_; +text: .text%jni_SetObjectArrayElement: jni.o; +text: .text%__1cNnmethodLocker2T6M_v_; +text: .text%__1cVjava_lang_ClassLoaderGparent6FpnHoopDesc__2_; +text: .text%__1cLOpaque2NodeGOpcode6kM_i_; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: bytecode.o; +text: .text%__1cMloadConLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLCastP2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIGraphKitSclear_saved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cIGraphKitTuse_exception_state6MpnNSafePointNode__pnENode__; +text: .text%__1cKstoreINodeFreloc6kM_i_; +text: .text%__1cRCardTableModRefBSPdirty_MemRegion6MnJMemRegion__v_; +text: .text%__1cNnmethodLocker2t6MpnHnmethod__v_; +text: .text%__1cNaddL_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNaddL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitOmake_slow_call6MpknITypeFunc_pCpkcpnENode_88_8_; +text: .text%__1cPcheckCastPPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUGenericGrowableArray2t6MiipnEGrET_i_v_; +text: .text%__1cSReferenceProcessorSdiscover_reference6MpnHoopDesc_nNReferenceType__i_; +text: .text%__1cTStackWalkCompPolicyYmethod_back_branch_event6MnMmethodHandle_iipnGThread__v_; +text: .text%__1cRCompilationPolicybJreset_counter_for_back_branch_event6MnMmethodHandle__v_; +text: .text%__1cNnegI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJStartNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cKciTypeFlowHdo_flow6M_v_; +text: .text%__1cKciTypeFlowKmap_blocks6M_v_; +text: .text%__1cKciTypeFlowKflow_types6M_v_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_i_v_; +text: .text%__1cKciTypeFlowLfind_ranges6M_v_; +text: .text%__1cKciTypeFlowXmark_known_range_starts6M_v_; +text: .text%__1cKciTypeFlow2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cKciTypeFlowPget_start_state6M_pkn0ALStateVector__; +text: .text%__1cXjava_lang_ref_ReferenceIset_next6FpnHoopDesc_2_v_; +text: .text%__1cOeFlagsRegUOperFclone6kM_pnIMachOper__; +text: .text%__1cSReferenceProcessorTget_discovered_list6MnNReferenceType__ppnHoopDesc__; +text: .text%__1cOPhaseIdealLoopRsplit_thru_region6MpnENode_2_2_; +text: .text%__1cOPhaseTransform2t6MnFPhaseLPhaseNumber__v_; +text: .text%__1cFKlassQup_cast_abstract6M_p0_; +text: .text%__1cFframebHnext_monitor_in_interpreter_frame6kMpnPBasicObjectLock__2_; +text: .text%__1cNsubL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMorI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cVPreserveExceptionMark2t6MrpnGThread__v_; +text: .text%__1cVPreserveExceptionMark2T6M_v_; +text: .text%__1cIciMethodJload_code6M_v_; +text: .text%__1cMciMethodDataJload_data6M_v_; +text: .text%__1cPconvL2I_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJimmI0OperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: multnode.o; +text: .text%__1cOCallRelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cPconvI2L_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJCMoveNodeLis_cmove_id6FpnOPhaseTransform_pnENode_44pnIBoolNode__4_; +text: .text%__1cNMemoryManagerHoops_do6MpnKOopClosure__v_; +text: .text%JVM_GetCPClassNameUTF; +text: .text%__1cLConvI2LNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNIdealLoopTreeNDCE_loop_body6M_v_; +text: .text%__1cNIdealLoopTreeVadjust_loop_exit_prob6MpnOPhaseIdealLoop__v_; +text: .text%__1cJAssemblerEleal6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cKstoreCNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%jni_GetStringLength: jni.o; +text: .text%__1cICodeHeapPsearch_freelist6MI_pnJFreeBlock__; +text: .text%__1cICodeHeapIallocate6MI_pv_; +text: .text%__1cKstorePNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionarybAcompute_loader_lock_object6FnGHandle_pnGThread__1_; +text: .text%__1cLeAXRegIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cFParseKdo_put_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cPmethodDataKlassRoop_is_methodData6kM_i_: methodDataKlass.o; +text: .text%__1cMciMethodData2t6MnQmethodDataHandle__v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodDataKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: methodDataKlass.o; +text: .text%__1cTcompareAndSwapLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOLibraryCallKitOgenerate_guard6MpnENode_pnKRegionNode_f_v_; +text: .text%__1cLOpaque1NodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKJNIHandlesLmake_global6FnGHandle_i_pnI_jobject__; +text: .text%__1cOClearArrayNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: jvm.o; +text: .text%__1cUPSGenerationCountersKupdate_all6M_v_: psGenerationCounters.o; +text: .text%__1cMoutputStream2t6Mi_v_; +text: .text%__1cWflagsReg_long_LTGEOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNloadRangeNodeFreloc6kM_i_; +text: .text%__1cFParseNpush_constant6MnKciConstant__i_; +text: .text%__1cMstringStream2t6MI_v_; +text: .text%__1cMstringStreamJas_string6M_pc_; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_: classes.o; +text: .text%__1cMstringStream2T6M_v_; +text: .text%__1cNaddL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMURShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMloadConDNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJrelocInfoKset_format6Mi_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: objArrayKlass.o; +text: .text%__1cPfieldDescriptorRint_initial_value6kM_i_; +text: .text%__1cJTimeStampSticks_since_update6kM_x_; +text: .text%__1cISubLNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse2.o; +text: .text%__1cNminI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOcompP_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRconstantPoolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cICodeBlobMset_oop_maps6MpnJOopMapSet__v_; +text: .text%__1cJCodeCacheIallocate6Fi_pnICodeBlob__; +text: .text%__1cQVMOperationQdDueueSqueue_remove_front6Mi_pnMVM_Operation__; +text: .text%__1cLProfileDataSis_VirtualCallData6M_i_: ciMethodData.o; +text: .text%__1cJScopeDescGis_top6kM_i_; +text: .text%__1cMorI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cOjmpLoopEndNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKTypeRawPtrFempty6kM_i_; +text: .text%__1cTCallInterpreterNodeGOpcode6kM_i_; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cScompI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRxorI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cVAdaptivePaddedAverageGsample6Mf_v_; +text: .text%__1cXmembar_acquire_lockNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNPrefetchQdDueueFclear6M_v_: psPromotionManager.o; +text: .text%__1cSPSPromotionManagerFreset6M_v_; +text: .text%__1cXconvI2L_reg_reg_zexNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cSPSPromotionManagerKflush_labs6M_v_; +text: .text%__1cRaddI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cIAndINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJloadSNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIregDOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cKBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cRClassPathZipEntryLopen_stream6Mpkc_pnPClassFileStream__; +text: .text%__1cSCompareAndSwapNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cICodeHeapPfollowing_block6MpnJFreeBlock__2_; +text: .text%__1cLRuntimeStubIis_alive6kM_i_: codeBlob.o; +text: .text%__1cFStateQ_sub_Op_URShiftI6MpknENode__v_; +text: .text%__1cUSafepointSynchronizeFblock6FpnKJavaThread__v_; +text: .text%__1cJttyLockerbCbreak_tty_lock_for_safepoint6Fi_v_; +text: .text%__1cIciObjectMis_obj_array6M_i_: ciInstanceKlass.o; +text: .text%__1cFciEnvZcheck_klass_accessibility6MpnHciKlass_pnMklassOopDesc__i_; +text: .text%__1cCosRcurrent_thread_id6F_i_; +text: .text%__1cMTypeKlassPtrFxdual6kM_pknEType__; +text: .text%__1cHciKlassMis_interface6M_i_: ciObjArrayKlass.o; +text: .text%__1cNtestP_regNodeFreloc6kM_i_; +text: .text%__1cHCompileTset_cached_top_node6MpnENode__v_; +text: .text%__1cIGotoNodeGOpcode6kM_i_; +text: .text%__1cENodeMsetup_is_top6M_v_; +text: .text%__1cTOopMapForCacheEntryZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cOleaPIdxOffNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPconvL2I_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHCompilePneed_stack_bang6kMi_i_; +text: .text%__1cOMachPrologNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cULinearLeastSquareFitGupdate6Mdd_v_; +text: .text%__1cSInterpreterRuntimeE_new6FpnKJavaThread_pnTconstantPoolOopDesc_i_v_; +text: .text%__1cNsubI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSmembar_acquireNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHRetNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cNobjArrayKlassPoop_is_objArray6kM_i_: objArrayKlass.o; +text: .text%__1cHRetNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKimmL32OperJconstantL6kM_x_: ad_i486_clone.o; +text: .text%__1cRindIndexScaleOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cRindIndexScaleOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cHRetNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRindIndexScaleOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cQleaPIdxScaleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLklassVtableRinitialize_vtable6MpnGThread__v_; +text: .text%__1cOPhaseIdealLoopQset_subtree_ctrl6MpnENode__v_; +text: .text%__1cRxorI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOMethodLivenessKBasicBlockJstore_two6Mi_v_; +text: .text%__1cPClassFileParserXverify_legal_class_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cJloadINodeIpeephole6MpnFBlock_ipnNPhaseRegAlloc_ri_pnIMachNode__; +text: .text%__1cJloadINodeFreloc6kM_i_; +text: .text%__1cNinstanceKlassVadd_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cKReturnNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOleaPIdxOffNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPjava_lang_ClassNcreate_mirror6FnLKlassHandle_pnGThread__pnHoopDesc__; +text: .text%__1cLklassVtableVinitialize_from_super6MnLKlassHandle__i_; +text: .text%__1cLklassVtableOcopy_vtable_to6MpnLvtableEntry__v_; +text: .text%__1cJloadBNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSCallLeafDirectNodeKmethod_set6Mi_v_; +text: .text%__1cKReturnNodeEhash6kM_I_: classes.o; +text: .text%__1cTleaPIdxScaleOffNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescbEfast_exception_handler_bci_for6MnLKlassHandle_ipnGThread__i_; +text: .text%__1cSInterpreterRuntimebFexception_handler_for_exception6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cIPhaseIFGISquareUp6M_v_; +text: .text%__1cYmulI_imm_RShift_highNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LEGTNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNxorI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKKlass_vtbl2n6FIrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cFKlassVbase_create_klass_oop6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__pnMklassOopDesc__; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: klass.o; +text: .text%__1cFKlassRinitialize_supers6MpnMklassOopDesc_pnGThread__v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: klass.o; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: klass.o; +text: .text%jni_GetStringUTFLength: jni.o; +text: .text%__1cWstatic_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cQjava_lang_StringLutf8_length6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc_ii_pc_; +text: .text%__1cFKlassRbase_create_klass6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__1_; +text: .text%jni_GetStringUTFRegion: jni.o; +text: .text%__1cIAndINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__: classes.o; +text: .text%__1cQPlaceholderTableJnew_entry6MipnNsymbolOopDesc_pnHoopDesc__pnQPlaceholderEntry__; +text: .text%__1cQPlaceholderTableMremove_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cQPlaceholderTableJadd_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cJcmpOpOperJnot_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cIAndINodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cKCMoveINodeGOpcode6kM_i_; +text: .text%__1cOMachEpilogNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: loopnode.o; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopnode.o; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: loopnode.o; +text: .text%__1cMPhaseChaitinSbuild_ifg_physical6MpnMResourceArea__I_; +text: .text%__1cMPhaseChaitinMreset_uf_map6MI_v_; +text: .text%__1cPsarI_eReg_1NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNPhaseCoalescePcoalesce_driver6M_v_; +text: .text%__1cHnmethodKpc_desc_at6MpCi_pnGPcDesc__; +text: .text%__1cHCompileQsync_stack_slots6kM_i_; +text: .text%__1cPindOffset32OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cJloadLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQSystemDictionaryTload_instance_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cUDebugInfoWriteStreamMwrite_handle6MpnI_jobject__v_; +text: .text%__1cVConstantOopWriteValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cPconvI2D_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNIdealLoopTreeTcheck_inner_safepts6MpnOPhaseIdealLoop__v_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: assembler_i486.o; +text: .text%__1cRAbstractAssemblerbDgenerate_stack_overflow_check6Mi_v_; +text: .text%__1cRaddI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNIdealLoopTreeUiteration_split_impl6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cNIdealLoopTreeOpolicy_peeling6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreebBpolicy_do_remove_empty_loop6MpnOPhaseIdealLoop__i_; +text: .text%__1cbBjava_lang_ref_SoftReferenceFclock6F_x_; +text: .text%__1cLStringTableGintern6FpnHoopDesc_pnGThread__2_; +text: .text%JVM_InternString; +text: .text%__1cXcmpL_reg_flags_LEGTNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_StringRas_unicode_string6FpnHoopDesc_ri_pH_; +text: .text%__1cCosGrandom6F_l_; +text: .text%__1cNget_next_hash6F_i_: synchronizer.o; +text: .text%__1cPshrI_eReg_1NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cUimpl_fp_store_helper6FpnKCodeBuffer_iiiiiii_i_: ad_i486.o; +text: .text%__1cVloadConL_low_onlyNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cXmembar_release_lockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOMacroAssemblerJincrement6MpnMRegisterImpl_i_v_; +text: .text%__1cKoopFactoryXnew_permanent_byteArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cENodeHdel_out6Mp0_v_: callGenerator.o; +text: .text%__1cJCmpL3NodeGOpcode6kM_i_; +text: .text%__1cOGenerateOopMapKcopy_state6MpnNCellTypeState_2_v_; +text: .text%__1cPclear_hashtable6FppnLNameSigHash__v_; +text: .text%__1cWflagsReg_long_LEGTOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cEDictIdoubhash6M_v_; +text: .text%__1cUinitialize_hashtable6FppnLNameSigHash__v_; +text: .text%__1cRcmpOp_commuteOperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cScompU_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSshrL_eReg_1_31NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPCountedLoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRMachSafePointNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cUmembar_cpu_orderNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPciObjectFactoryMvm_symbol_at6Fi_pnIciSymbol__; +text: .text%__1cNmodI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLRShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMMutableSpaceKinitialize6MnJMemRegion_i_v_; +text: .text%__1cKstoreLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%jni_SetIntField: jni.o; +text: .text%__1cENodeHis_Copy6kM_I_: ad_i486.o; +text: .text%__1cKstorePNodeFreloc6kM_i_; +text: .text%__1cNincI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPCountedLoopNodeHsize_of6kM_I_: classes.o; +text: .text%__1cNcmovI_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cTresource_free_bytes6FpcI_v_; +text: .text%__1cVLoaderConstraintTablePcheck_or_update6MnTinstanceKlassHandle_nGHandle_nMsymbolHandle__pkc_; +text: .text%__1cQSystemDictionaryRupdate_dictionary6FiIiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_: dictionary.o; +text: .text%__1cQSystemDictionaryRcheck_constraints6FiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cQSystemDictionaryQfind_placeholder6FiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cIProjNodeJideal_reg6kM_I_; +text: .text%__1cKDictionaryJadd_klass6MnMsymbolHandle_nGHandle_nLKlassHandle__v_; +text: .text%__1cKDictionaryJnew_entry6MIpnMklassOopDesc_pnHoopDesc__pnPDictionaryEntry__; +text: .text%__1cVloadConL_low_onlyNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPstoreImmI16NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: instanceKlass.o; +text: .text%__1cWflagsReg_long_EQdDNEOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cIAndINodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIAndINodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cRandI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLRuntimeStubYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cTGeneratePairingInfoRpossible_gc_point6MpnOBytecodeStream__i_: ciMethod.o; +text: .text%__1cJLoadBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFKlassXsearch_secondary_supers6kMpnMklassOopDesc__i_; +text: .text%__1cNmodI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRshrI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJLoadSNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOGenerateOopMapOset_bbmark_bit6Mi_v_; +text: .text%__1cIGraphKitOhas_ex_handler6M_i_; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_: ciMethodData.o; +text: .text%__1cRaddI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRsubI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopMdominated_by6MpnENode_2_v_; +text: .text%__1cPRoundDoubleNodeGOpcode6kM_i_; +text: .text%__1cRsarI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cICallNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJAssemblerEandl6MpnMRegisterImpl_i_v_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: doCall.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: connode.o; +text: .text%__1cLklassVtableQfill_in_mirandas6Mri_v_; +text: .text%__1cLklassItableRinitialize_itable6M_v_; +text: .text%__1cZCallDynamicJavaDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSshlL_eReg_1_31NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cCosXthread_local_storage_at6Fi_pv_; +text: .text%__1cSThreadLocalStoragePget_thread_slow6F_pnGThread__; +text: .text%__1cLregFPR1OperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cQinstanceRefKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cSmembar_releaseNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cLeDIRegPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cLregFPR1OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cSmembar_acquireNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitRmerge_fast_memory6MpnENode_2i_v_; +text: .text%__1cFKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cSCompareAndSwapNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cSindIndexOffsetOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cSindIndexOffsetOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cSindIndexOffsetOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cNstoreImmBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIGraphKitOmake_merge_mem6MpnENode_22_v_; +text: .text%__1cFKlassWappend_to_sibling_list6M_v_; +text: .text%__1cIGraphKitHopt_iff6MpnENode_2_2_; +text: .text%__1cOcompP_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQSystemDictionarySjava_system_loader6F_pnHoopDesc__; +text: .text%__1cSmembar_releaseNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: classes.o; +text: .text%__1cJLoadFNodeGOpcode6kM_i_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_2_v_; +text: .text%__1cFframeRis_compiled_frame6kMpi_i_; +text: .text%__1cTjava_lang_ThrowableNset_backtrace6FpnHoopDesc_2_v_; +text: .text%__1cKstoreCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMStartOSRNodeGOpcode6kM_i_; +text: .text%__1cHPhiNodeKmake_blank6FpnENode_2_p0_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: thread.o; +text: .text%__1cTClassLoadingServiceScompute_class_size6FpnNinstanceKlass__I_; +text: .text%__1cTClassLoadingServiceTnotify_class_loaded6FpnNinstanceKlass_i_v_; +text: .text%__1cNinstanceKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNmethodOopDescMsort_methods6FpnPobjArrayOopDesc_222_v_; +text: .text%__1cQSystemDictionaryVdefine_instance_class6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIVerifierQrelax_verify_for6FpnHoopDesc__i_; +text: .text%__1cNinstanceKlassQeager_initialize6MpnGThread__v_; +text: .text%__1cPClassFileStream2t6MpCipc_v_; +text: .text%__1cNinstanceKlassSprocess_interfaces6MpnGThread__v_; +text: .text%__1cNinstanceKlassQinit_implementor6M_v_; +text: .text%__1cNinstanceKlassbBdo_local_static_fields_impl6FnTinstanceKlassHandle_pFpnPfieldDescriptor_pnGThread__v5_v_; +text: .text%__1cRconstantPoolKlassIallocate6MipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cSinstanceKlassKlassXallocate_instance_klass6MiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cKoopFactoryRnew_instanceKlass6FiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cLklassVtableQget_num_mirandas6FpnMklassOopDesc_pnPobjArrayOopDesc_4_i_; +text: .text%__1cKoopFactoryQnew_constantPool6FipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cLklassItableZsetup_itable_offset_table6FnTinstanceKlassHandle__v_; +text: .text%__1cIUniverseTflush_dependents_on6FnTinstanceKlassHandle__v_; +text: .text%__1cLklassVtablebKcompute_vtable_size_and_num_mirandas6Fri1pnMklassOopDesc_pnPobjArrayOopDesc_nLAccessFlags_pnHoopDesc_pnNsymbolOopDesc_5_v_; +text: .text%__1cJCodeCachebKnumber_of_nmethods_with_dependencies6F_i_; +text: .text%__1cPClassFileParserbBparse_constant_pool_entries6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: constantPoolKlass.o; +text: .text%__1cVjava_lang_ClassLoaderRis_trusted_loader6FpnHoopDesc__i_; +text: .text%__1cQSystemDictionaryQadd_to_hierarchy6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserNparse_methods6MnSconstantPoolHandle_ipnLAccessFlags_ppnPobjArrayOopDesc_66pnGThread__nOobjArrayHandle__; +text: .text%__1cNinstanceKlassWdo_local_static_fields6MpFpnPfieldDescriptor_pnGThread__v4_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: constantPoolKlass.o; +text: .text%__1cPClassFileParserUcompute_oop_map_size6MnTinstanceKlassHandle_ii_i_; +text: .text%__1cPClassFileParserNfill_oop_maps6MnTinstanceKlassHandle_ii_v_; +text: .text%__1cPClassFileParserVset_precomputed_flags6MnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserTparse_constant_pool6MpnGThread__nSconstantPoolHandle__; +text: .text%__1cPClassFileParserbDcompute_transitive_interfaces6MnTinstanceKlassHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cPClassFileParserYcheck_super_class_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserMparse_fields6MnSconstantPoolHandle_ipnUFieldAllocationCount_pnOobjArrayHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserbCcheck_super_interface_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbBcheck_final_method_override6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cPClassFileParserbAparse_classfile_attributes6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: constantPoolKlass.o; +text: .text%__1cPClassFileParserMsort_methods6MnOobjArrayHandle_111pnGThread__nPtypeArrayHandle__; +text: .text%__1cPClassFileParserQparse_interfaces6MnSconstantPoolHandle_nGHandle_2pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserOparseClassFile6MnMsymbolHandle_nGHandle_2r1pnGThread__nTinstanceKlassHandle__; +text: .text%__1cQSystemDictionaryVresolve_super_or_fail6FnMsymbolHandle_1nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cLloadSSFNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNObjectMonitorHis_busy6kM_i_; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceKlass.o; +text: .text%__1cNinstanceKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlass.o; +text: .text%__1cSindIndexOffsetOperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cSThreadProfilerMark2T6M_v_; +text: .text%__1cJEventMark2t6MpkcE_v_: classLoader.o; +text: .text%__1cSThreadProfilerMark2t6Mn0AGRegion__v_; +text: .text%__1cFVTuneOend_class_load6F_v_; +text: .text%__1cFVTuneQstart_class_load6F_v_; +text: .text%__1cLClassLoaderOload_classfile6FnMsymbolHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cENodeHis_Call6M_pnICallNode__: loopnode.o; +text: .text%__1cQSystemDictionaryRfind_shared_class6FnMsymbolHandle__pnMklassOopDesc__; +text: .text%__1cQSystemDictionaryRload_shared_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cQSystemDictionaryRload_shared_class6FnTinstanceKlassHandle_nGHandle_pnGThread__1_; +text: .text%__1cHMatcherNfind_receiver6Fi_nFVMRegEName__; +text: .text%__1cNnegI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframeZinterpreter_frame_set_bcx6Mi_v_; +text: .text%__1cPClassFileParserbKparse_classfile_sourcefile_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cENodeHdel_out6Mp0_v_: generateOptoStub.o; +text: .text%__1cRsubI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNandI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cILoopNode2t6MpnENode_2_v_; +text: .text%__1cVeADXRegL_low_onlyOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cENodeGis_Con6kM_I_: memnode.o; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: debugInfo.o; +text: .text%__1cMPhaseIterGVNIoptimize6M_v_; +text: .text%__1cJloadFNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQPackageHashtableMcompute_hash6Mpkci_I_: classLoader.o; +text: .text%__1cOMethodLivenessRinit_basic_blocks6M_v_; +text: .text%__1cOMethodLivenessNinit_gen_kill6M_v_; +text: .text%__1cWCallLeafNoFPDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOMethodLivenessSpropagate_liveness6M_v_; +text: .text%__1cOMethodLiveness2t6MpnFArena_pnIciMethod__v_; +text: .text%__1cOMethodLivenessQcompute_liveness6M_v_; +text: .text%__1cHCompilebAvarargs_C_out_slots_killed6kM_I_; +text: .text%__1cSMemBarVolatileNodeGOpcode6kM_i_; +text: .text%__1cTMachCallRuntimeNodeSis_MachCallRuntime6M_p0_: ad_i486_misc.o; +text: .text%__1cMloadConPNodeFreloc6kM_i_; +text: .text%__1cSTailCalljmpIndNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cYconstantPoolCacheOopDescKinitialize6MrnIintArray__v_; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: cpCacheKlass.o; +text: .text%__1cNinstanceKlassLverify_code6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cIVerifierRverify_byte_codes6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassNrewrite_class6MpnGThread__v_; +text: .text%__1cNinstanceKlassWadd_loader_constraints6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cLklassVtableYadd_new_mirandas_to_list6FpnNGrowableArray4CpnNmethodOopDesc___pnPobjArrayOopDesc_6pnMklassOopDesc__v_; +text: .text%__1cIRewriterScompute_index_maps6FnSconstantPoolHandle_rpnIintArray_rpnIintStack__v_; +text: .text%__1cKoopFactoryVnew_constantPoolCache6FipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cIintArray2t6Mki1_v_: rewriter.o; +text: .text%__1cWconstantPoolCacheKlassIallocate6MipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cIRewriterHrewrite6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: cpCacheKlass.o; +text: .text%__1cIRewriterXnew_constant_pool_cache6FrnIintArray_pnGThread__nXconstantPoolCacheHandle__; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: cpCacheKlass.o; +text: .text%__1cRmethodDataOopDescJis_mature6kM_i_; +text: .text%__1cJCodeCacheMfind_nmethod6Fpv_pnHnmethod__; +text: .text%__1cQciByteCodeStreamUis_unresolved_string6kM_i_; +text: .text%__1cFciEnvUis_unresolved_string6kMpnPciInstanceKlass_i_i_; +text: .text%JVM_GetMethodIxExceptionIndexes; +text: .text%JVM_GetMethodIxSignatureUTF; +text: .text%JVM_GetMethodIxExceptionsCount; +text: .text%JVM_GetMethodIxMaxStack; +text: .text%JVM_GetMethodIxByteCode; +text: .text%JVM_GetMethodIxArgsSize; +text: .text%JVM_GetMethodIxByteCodeLength; +text: .text%__1cFciEnvZis_unresolved_string_impl6kMpnNinstanceKlass_i_i_; +text: .text%__1cKoopFactoryWnew_permanent_intArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cWCountInterfacesClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cKTypeRawPtrEmake6FpC_pk0_; +text: .text%__1cPClassFileParserVparse_exception_table6MIInSconstantPoolHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cFParseXcatch_inline_exceptions6MpnNSafePointNode__v_; +text: .text%__1cNmulL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNminI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__: classes.o; +text: .text%__1cYciExceptionHandlerStreamPcount_remaining6M_i_; +text: .text%__1cPconvI2L_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSshrL_eReg_1_31NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJLoadLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cPshrI_eReg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWconstantPoolCacheKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cYmulI_imm_RShift_highNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHMatcherUc_calling_convention6FpnLRegPair_I_v_; +text: .text%__1cPCallRuntimeNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__: classes.o; +text: .text%__1cHAddNodeGis_Add6kM_pk0_: classes.o; +text: .text%__1cUBytecode_tableswitchOdest_offset_at6kMi_i_; +text: .text%__1cSObjectSynchronizerJnotifyall6FnGHandle_pnGThread__v_; +text: .text%__1cCosRelapsed_frequency6F_x_; +text: .text%__1cRshrI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMelapsedTimerHseconds6kM_d_; +text: .text%__1cRaddL_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNaddI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIciObjectMhas_encoding6M_i_; +text: .text%__1cSloadL_volatileNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNandI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cQPackageHashtableJget_entry6MiIpkcI_pnLPackageInfo__: classLoader.o; +text: .text%__1cLClassLoaderOlookup_package6Fpkc_pnLPackageInfo__; +text: .text%__1cLeDIRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cENodeUdepends_only_on_test6kM_i_: connode.o; +text: .text%__1cFStateR_sub_Op_LoadKlass6MpknENode__v_; +text: .text%__1cQVMOperationQdDueueNqueue_oops_do6MipnKOopClosure__v_; +text: .text%__1cUCallCompiledJavaNodeGOpcode6kM_i_; +text: .text%__1cRLowMemoryDetectorWdetect_after_gc_memory6FpnKMemoryPool__v_; +text: .text%__1cUmembar_cpu_orderNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPClassFileParserbSparse_constant_pool_interfacemethodref_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cRsalI_eReg_immNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLClassLoaderLadd_package6Fpkci_i_; +text: .text%__1cNloadKlassNodeFreloc6kM_i_; +text: .text%__1cFStateV_sub_Op_MemBarRelease6MpknENode__v_; +text: .text%__1cMloadConLNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cHMatcherQpost_fast_unlock6FpknENode__i_; +text: .text%__1cZInterpreterMacroAssemblerKverify_oop6MpnMRegisterImpl_nITosState__v_; +text: .text%__1cFStateM_sub_Op_SubI6MpknENode__v_; +text: .text%__1cLklassItableTcompute_itable_size6FnOobjArrayHandle__i_; +text: .text%__1cLeSIRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKciTypeFlowLStateVectorMdo_putstatic6MpnQciByteCodeStream__v_; +text: .text%__1cIRootNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cIXorINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNaddP_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNSafepointBlobbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cLregFPR1OperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: instanceKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlass.o; +text: .text%__1cKciTypeFlowLStateVectorGdo_ldc6MpnQciByteCodeStream__v_; +text: .text%__1cbDReferenceProcessorInitializerIis_clean6kM_v_: concurrentMarkSweepGeneration.o; +text: .text%__1cKManagementJtimestamp6F_x_; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: cfgnode.o; +text: .text%__1cIPSOldGenPupdate_counters6M_v_; +text: .text%__1cNSharedRuntimebOraw_exception_handler_for_return_address6FpC_1_; +text: .text%__1cNSharedRuntimebKexception_handler_for_return_address6FpC_1_; +text: .text%__1cNaddI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cHOrINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cMnegF_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMnegF_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompP_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKciTypeFlowLStateVectorLdo_putfield6MpnQciByteCodeStream__v_; +text: .text%__1cXjava_lang_ref_ReferenceWpending_list_lock_addr6F_ppnHoopDesc__; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstance.o; +text: .text%__1cSshlL_eReg_1_31NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%signalHandler; +text: .text%JVM_handle_solaris_signal; +text: .text%__1cMnegF_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNinstanceKlassbBcall_class_initializer_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassRclass_initializer6M_pnNmethodOopDesc__; +text: .text%__1cNinstanceKlassbOset_initialization_state_and_notify_impl6FnTinstanceKlassHandle_n0AKClassState_pnGThread__v_; +text: .text%__1cNinstanceKlassbJset_initialization_state_and_notify6Mn0AKClassState_pnGThread__v_; +text: .text%__1cNinstanceKlassWcall_class_initializer6MpnGThread__v_; +text: .text%__1cKcopy_table6FppC1i_v_: interpreter.o; +text: .text%__1cMtlsLoadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQVMOperationQdDueueLremove_next6M_pnMVM_Operation__; +text: .text%__1cOPhaseIdealLoopNreorg_offsets6MpnNIdealLoopTree__v_; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopopts.o; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_i_v_; +text: .text%__1cPcmpFastLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKBufferBlob2t6Mpkci_v_; +text: .text%__1cKBufferBlobGcreate6Fpkci_p0_; +text: .text%__1cNaddL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKBufferBlob2n6FII_pv_; +text: .text%__1cICodeBlob2t6Mpkcii_v_; +text: .text%__1cMeBCXRegLOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cNSharedRuntimebWnative_method_throw_unsatisfied_link_error_entry6F_pC_; +text: .text%__1cNRelocIteratorTlocs_and_index_size6Fii_i_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: compile.o; +text: .text%__1cScompP_mem_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCCompiledCodeSafepointHandlerbDhandle_polling_page_exception6M_pC_; +text: .text%__1cKJavaThreadUin_stack_yellow_zone6MpC_i_: os_solaris_i486.o; +text: .text%__1cFframebDsender_for_raw_compiled_frame6kMpnLRegisterMap__0_; +text: .text%__1cMVM_OperationSis_cheap_allocated6kM_i_: vm_operations.o; +text: .text%__1cUSafepointSynchronizebDhandle_polling_page_exception6FpnKJavaThread__pC_; +text: .text%__1cKloadUBNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cLGCTaskQdDueueKinitialize6M_v_; +text: .text%__1cSCardTableExtensionbAscavenge_contents_parallel6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager_I_v_; +text: .text%__1cTOldToYoungRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cJStealTaskEname6M_pc_: psTasks.o; +text: .text%__1cTOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cJStealTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cJStealTask2t6Mi_v_; +text: .text%__1cNGCTaskManagerMnote_release6MI_v_; +text: .text%__1cHnmethodJcode_size6kM_i_: nmethod.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: memnode.o; +text: .text%__1cJcmpOpOperEless6kM_i_: ad_i486_clone.o; +text: .text%__1cSaddD_reg_roundNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSmembar_releaseNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cICodeHeapTmark_segmap_as_used6MII_v_; +text: .text%__1cJloadCNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cNsubL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJimmL0OperJconstantL6kM_x_: ad_i486_clone.o; +text: .text%__1cFParseWensure_phis_everywhere6M_v_; +text: .text%__1cLConvL2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNxorI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNandL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStartNodeHsize_of6kM_I_; +text: .text%__1cHMatcherFxform6MpnENode_i_2_; +text: .text%__1cHMatcherLfind_shared6MpnENode__v_; +text: .text%__1cTMachCallRuntimeNodePret_addr_offset6M_i_; +text: .text%__1cILRG_List2t6MI_v_; +text: .text%__1cILoopNodeHsize_of6kM_I_: classes.o; +text: .text%__1cEDict2t6MpFpkv2_ipF2_ipnFArena_i_v_; +text: .text%__1cGBundlePinitialize_nops6FppnIMachNode__v_; +text: .text%__1cOMachPrologNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNstoreImmPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLeAXRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cNsubL_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cHi2bNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cRmethodDataOopDescYcompute_extra_data_count6Fii_i_; +text: .text%__1cWCallLeafNoFPDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse3.o; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMLinkResolverbHlookup_instance_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cXJNI_ArgumentPusherVaArgHiterate6MX_v_: jni.o; +text: .text%__1cNFingerprinterLfingerprint6M_X_: jni.o; +text: .text%__1cKimmI16OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cSshrL_eReg_1_31NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPstoreImmI16NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIemit_d166FrnKCodeBuffer_i_v_; +text: .text%__1cRxorI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRsubI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJeRegPOperFclone6kM_pnIMachOper__; +text: .text%__1cJAssemblerEmovl6MnHAddress_i_v_; +text: .text%__1cNobjArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cFStateT_sub_Op_CheckCastPP6MpknENode__v_; +text: .text%__1cKciTypeFlowLStateVectorJhalf_type6FpnGciType__3_: ciTypeFlow.o; +text: .text%__1cJAssemblerDnop6M_v_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: connode.o; +text: .text%__1cMFastLockNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%JVM_GetFieldIxModifiers; +text: .text%__1cNmodI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKReturnNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRsarI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFParseKarray_load6MnJBasicType__v_; +text: .text%__1cRjmpConU_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cRjmpConU_shortNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cLjmpConUNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cRjmpConU_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKcmpOpUOperFclone6kM_pnIMachOper__; +text: .text%__1cFStateO_sub_Op_StoreB6MpknENode__v_; +text: .text%__1cFTypeFCeq6kMpknEType__i_; +text: .text%__1cFParseNadd_safepoint6M_v_; +text: .text%__1cLOpaque2NodeEhash6kM_I_; +text: .text%JVM_IsConstructorIx; +text: .text%__1cIimmPOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConPNodeFclone6kM_pnENode__; +text: .text%__1cSshrL_eReg_1_31NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: connode.o; +text: .text%__1cRxorI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXmembar_release_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNinstanceKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassTarray_klass_or_null6M_pnMklassOopDesc__; +text: .text%__1cKadd_n_reqs6FpnENode_1_v_: graphKit.o; +text: .text%__1cNdecI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cILoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cICodeHeapLmerge_right6MpnJFreeBlock__v_; +text: .text%__1cIregDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNsubI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXconvI2L_reg_reg_zexNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescTset_native_function6MpC_v_; +text: .text%__1cIPhaseIFGYCompute_Effective_Degree6M_v_; +text: .text%__1cMPhaseChaitinISimplify6M_v_; +text: .text%__1cMPhaseChaitinGSelect6M_I_; +text: .text%__1cMPhaseChaitinOcache_lrg_info6M_v_; +text: .text%__1cNtestU_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJScopeDescJstream_at6kMi_pnTDebugInfoReadStream__; +text: .text%__1cTDebugInfoReadStream2t6MpknHnmethod_i_v_; +text: .text%__1cLOptoRuntimeJstub_name6FpC_pkc_; +text: .text%__1cNSignatureInfoHdo_long6M_v_: frame.o; +text: .text%__1cSvframeStreamCommonZsecurity_get_caller_frame6Mi_v_; +text: .text%__1cENodeHrm_prec6MI_v_; +text: .text%__1cUjni_invoke_nonstatic6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cKciTypeFlowLStateVectorOmeet_exception6MpnPciInstanceKlass_pk1_i_; +text: .text%__1cKloadUBNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cWflagsReg_long_EQdDNEOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKciTypeFlowPflow_exceptions6MpnNGrowableArray4Cpn0AFBlock___pnNGrowableArray4CpnPciInstanceKlass___pn0ALStateVector__v_; +text: .text%__1cIBoolNodeZis_counted_loop_exit_test6M_i_; +text: .text%__1cPciInstanceKlassTis_java_lang_Object6M_i_; +text: .text%__1cRaddL_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXmembar_release_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cQsalI_eReg_CLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNnegI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompP_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cILoopNodeHsize_of6kM_I_: loopnode.o; +text: .text%__1cMPhaseChaitinFSplit6MI_I_; +text: .text%__1cZPhaseConservativeCoalesce2t6MrnMPhaseChaitin__v_; +text: .text%__1cMPhaseChaitinHcompact6M_v_; +text: .text%__1cMPhaseChaitinZcompress_uf_map_for_nodes6M_v_; +text: .text%__1cZPhaseConservativeCoalesceGverify6M_v_; +text: .text%__1cHTypePtrFxmeet6kMpknEType__3_; +text: .text%__1cOcompP_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cHMatcherQis_spillable_arg6Fi_i_; +text: .text%__1cSsafePoint_pollNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cHMatcherKcan_be_arg6Fi_i_; +text: .text%__1cSTailCalljmpIndNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIregFOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cTDebugInfoReadStreamLread_handle6M_nGHandle__; +text: .text%__1cJScopeDesc2t6MpknHnmethod_i_v_; +text: .text%__1cYcmpL_zero_flags_LEGTNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOcompiledVFrame2t6MpknFframe_pknLRegisterMap_pnKJavaThread_pnJScopeDesc__v_; +text: .text%__1cNSignatureInfoHdo_long6M_v_: bytecode.o; +text: .text%__1cXvirtual_call_RelocationJfirst_oop6M_pC_; +text: .text%__1cXvirtual_call_RelocationJoop_limit6M_pC_; +text: .text%__1cQComputeCallStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cHnmethodOis_java_method6kM_i_: nmethod.o; +text: .text%__1cNminI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSCallLeafDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYcmpL_zero_flags_LEGTNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompiledVFrameGis_top6kM_i_; +text: .text%__1cFStateQ_sub_Op_CallLeaf6MpknENode__v_; +text: .text%__1cKJNIHandlesOdestroy_global6FpnI_jobject_i_v_; +text: .text%__1cHi2sNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRNativeGeneralJumpQjump_destination6kM_pC_; +text: .text%__1cMoutputStreamFprint6MpkcE_v_; +text: .text%__1cIciObjectIis_klass6M_i_: ciInstance.o; +text: .text%__1cMoutputStreamMdo_vsnprintf6FpcIpkcpvirI_3_; +text: .text%__1cOGenerateOopMapGdo_ldc6Mii_v_; +text: .text%__1cRaddI_eReg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cITypeLongFxdual6kM_pknEType__; +text: .text%__1cSloadL_volatileNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_immNodeFreloc6kM_i_; +text: .text%__1cPcheckCastPPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSvframeStreamCommonYfill_from_compiled_frame6MpnHnmethod_i_v_; +text: .text%__1cHnmethodQis_native_method6kM_i_: nmethod.o; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: cfgnode.o; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: cfgnode.o; +text: .text%__1cTshrL_eReg_32_63NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: objArrayKlass.o; +text: .text%__1cPClassFileStreamGget_u86MpnGThread__X_; +text: .text%__1cPshrI_eReg_1NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cbBjava_lang_ref_SoftReferenceJtimestamp6FpnHoopDesc__x_; +text: .text%__1cJNode_ListEyank6MpnENode__v_; +text: .text%__1cQLRUMaxHeapPolicyWshould_clear_reference6MpnHoopDesc__i_; +text: .text%__1cIGraphKitNstore_barrier6MpnENode_22_v_; +text: .text%__1cMrep_stosNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cWflagsReg_long_LEGTOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cSThreadLocalStorageTpd_getTlsAccessMode6F_n0AQpd_tlsAccessMode__; +text: .text%__1cOMacroAssemblerKget_thread6MpnMRegisterImpl__v_; +text: .text%__1cKloadUBNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLRethrowNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHMonitorGnotify6M_i_; +text: .text%__1cIRootNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRcmpFastUnlockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIGraphKitNshared_unlock6MpnENode_2_v_; +text: .text%__1cNSafePointNodeLpop_monitor6M_v_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_exit_Type6F_pknITypeFunc__; +text: .text%__1cIRootNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cFStateS_sub_Op_FastUnlock6MpknENode__v_; +text: .text%__1cJloadFNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOstackSlotFOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cSstore_to_stackslot6FrnKCodeBuffer_iii_v_; +text: .text%__1cNmethodOopDescVset_signature_handler6MpC_v_; +text: .text%__1cMnadxRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_: callnode.o; +text: .text%__1cIAndINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cUmembar_cpu_orderNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMloadConINodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: cfgnode.o; +text: .text%__1cPconvI2L_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescTverified_code_entry6M_pC_; +text: .text%__1cNSharedRuntimeXfind_callee_info_helper6FpnKJavaThread_rnMvframeStream_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cOjmpLoopEndNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRmulI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPsarI_eReg_1NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%jni_ExceptionCheck: jni.o; +text: .text%__1cURethrowExceptionNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cKstoreLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateP_sub_Op_RShiftI6MpknENode__v_; +text: .text%__1cOGenerateOopMapHppstore6MpnNCellTypeState_i_v_; +text: .text%__1cIMulLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cOcompP_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMvalue_of_loc6FppnHoopDesc__i_; +text: .text%__1cNSafePointNodeMpush_monitor6MpknMFastLockNode__v_; +text: .text%__1cTcompareAndSwapLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNandI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cTshrL_eReg_32_63NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSshlL_eReg_1_31NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKcmpOpUOperJnot_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cOcompP_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNSCMemProjNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cNandI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNaddP_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNstoreImmPNodeFreloc6kM_i_; +text: .text%__1cKNativeCallXset_destination_mt_safe6MpC_v_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKtype2basic6FpknEType__nJBasicType__; +text: .text%__1cIAndLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cIAndLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cNstoreImmBNodeFreloc6kM_i_; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: jvm.o; +text: .text%JVM_Clone; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: jvm.o; +text: .text%__1cNcmovI_regNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadKlassNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVeADXRegL_low_onlyOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: objArrayKlass.o; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: objArrayKlass.o; +text: .text%__1cNobjArrayKlassIallocate6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: objArrayKlass.o; +text: .text%__1cMeBCXRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNimmI_1_31OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cLPhaseValues2T5B6M_v_; +text: .text%__1cNtestU_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJLoadCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRsubI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIJumpDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cMalloc_object6FpnH_jclass_pnGThread__pnPinstanceOopDesc__: jni.o; +text: .text%__1cIGraphKitOnull_check_oop6MpnKRegionNode_pnENode_i_4_; +text: .text%__1cRmulI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHOrINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKTypeRawPtrEmake6FnHTypePtrDPTR__pk0_; +text: .text%__1cKTypeAryPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cFKlassDLCA6Mp0_1_; +text: .text%__1cHciKlassVleast_common_ancestor6Mp0_1_; +text: .text%__1cPconvL2I_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJeRegLOperFclone6kM_pnIMachOper__; +text: .text%__1cbCfind_class_from_class_loader6FpnHJNIEnv__nMsymbolHandle_CnGHandle_3CpnGThread__pnH_jclass__; +text: .text%__1cNstoreImmINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPshlI_eReg_1NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNmaxI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIMinINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cHnmethodVis_dependent_on_entry6MpnMklassOopDesc_2pnNmethodOopDesc__i_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlass.o; +text: .text%__1cKloadUBNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMTypeKlassPtrFxmeet6kMpknEType__3_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cICodeHeapPadd_to_freelist6MpnJHeapBlock__v_; +text: .text%__1cICodeHeapKdeallocate6Mpv_v_; +text: .text%__1cJCodeCacheEfree6FpnICodeBlob__v_; +text: .text%__1cJVectorSetGslamin6Mrk0_v_; +text: .text%__1cQsalI_eReg_CLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKNode_ArrayFclear6M_v_; +text: .text%__1cKBufferBlobEfree6Fp0_v_; +text: .text%__1cIRootNodeHis_Root6M_p0_: classes.o; +text: .text%__1cXJNI_ArgumentPusherVaArgIget_long6M_v_: jni.o; +text: .text%__1cOMethodLivenessKBasicBlockPmerge_exception6MnGBitMap__i_; +text: .text%__1cNminI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNsubI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYinternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cMorI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNtestU_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%jni_NewObject: jni.o; +text: .text%__1cMTailCallNodeKmatch_edge6kMI_I_; +text: .text%__1cIciMethodRinstructions_size6M_i_; +text: .text%__1cRsarI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPshrI_eReg_1NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKStoreFNodeGOpcode6kM_i_; +text: .text%__1cFStateO_sub_Op_StoreC6MpknENode__v_; +text: .text%__1cNobjArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cFciEnvKcompile_id6M_I_; +text: .text%__1cRAbstractAssemblerFflush6M_v_; +text: .text%__1cJloadINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRaddL_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNloadConL0NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNsubL_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNsubL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompU_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cScompU_eReg_memNodeFreloc6kM_i_; +text: .text%__1cTleaPIdxScaleOffNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMURShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNSharedRuntimeQfind_callee_info6FpnKJavaThread_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cNsubL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSCallLeafDirectNodeFreloc6kM_i_; +text: .text%__1cNSharedRuntimeOresolve_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cNSharedRuntimeSresolve_sub_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cKCompiledIC2t6MpnKNativeCall__v_; +text: .text%__1cNcmovI_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cNcmovI_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cGIfNodeMdominated_by6MpnENode_pnMPhaseIterGVN__v_; +text: .text%__1cIMulDNodeGOpcode6kM_i_; +text: .text%__1cHMatcherPprior_fast_lock6FpknENode__i_; +text: .text%__1cFStateV_sub_Op_MemBarAcquire6MpknENode__v_; +text: .text%__1cLConvL2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMachEpilogNodeFreloc6kM_i_; +text: .text%__1cOMachEpilogNodeNis_MachEpilog6M_p0_: ad_i486.o; +text: .text%__1cOMachEpilogNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivLNodeGOpcode6kM_i_; +text: .text%__1cSstring_compareNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cSsafePoint_pollNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitNgen_checkcast6MpnENode_2p2_2_; +text: .text%__1cSindIndexOffsetOperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cLStrCompNodeGOpcode6kM_i_; +text: .text%__1cSindIndexOffsetOperOindex_position6kM_i_: ad_i486.o; +text: .text%__1cKRelocationJpack_data6M_i_: codeBlob.o; +text: .text%__1cGciTypeMis_classless6kM_i_: ciInstanceKlass.o; +text: .text%__1cLPcDescCacheKpc_desc_at6kMpnHnmethod_pCi_pnGPcDesc__; +text: .text%__1cXmembar_acquire_lockNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLloadSSFNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNaddP_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cYcmpL_zero_flags_LEGTNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEtemp6F_pnMRegisterImpl__; +text: .text%__1cPstoreImmI16NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframeRretrieve_receiver6MpnLRegisterMap__pnHoopDesc__; +text: .text%__1cJcmpOpOperNgreater_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cPBytecode_invokeNstatic_target6MpnGThread__nMmethodHandle__; +text: .text%__1cNmodL_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmulL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIDivINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJAssemblerDjmp6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cSloadL_volatileNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitRgen_subtype_check6MpnENode_2_2_; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc__pc_; +text: .text%__1cENodeHdel_out6Mp0_v_: parseHelper.o; +text: .text%__1cNandI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJTraceTime2t6MpkciipnMoutputStream__v_; +text: .text%__1cTciConstantPoolCache2t6MpnFArena_i_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_tree6M_v_; +text: .text%__1cLConvD2INodeGOpcode6kM_i_; +text: .text%__1cFframeIpatch_pc6MpnGThread_pC_v_; +text: .text%__1cOPhaseIdealLoop2t6MrnMPhaseIterGVN_pk0i_v_; +text: .text%__1cKciTypeFlowFRangeSprivate_copy_count6kMpn0AGJsrSet__i_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cXmembar_release_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXcmpL_reg_flags_LTGENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: frame.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: codeBlob.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: callnode.o; +text: .text%__1cNIdealLoopTreeMis_loop_exit6kMpnENode_pnOPhaseIdealLoop__2_; +text: .text%__1cJloadBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cZload_long_indOffset32OperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cZload_long_indOffset32OperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cNincI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJloadLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKstoreFNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cYcmpL_zero_flags_LEGTNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNxorI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNandL_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cNtestU_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: onStackReplacement.o; +text: .text%__1cSciExceptionHandlerLcatch_klass6M_pnPciInstanceKlass__; +text: .text%__1cXcmpL_reg_flags_LEGTNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNincI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: loopnode.o; +text: .text%__1cNSafePointNodeKgrow_stack6MpnIJVMState_I_v_; +text: .text%__1cNxorI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cLRShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRsubI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cKBlock_ListGinsert6MIpnFBlock__v_; +text: .text%__1cECopyYconjoint_words_to_higher6FpnIHeapWord_2I_v_: block.o; +text: .text%__1cLRShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: loopnode.o; +text: .text%__1cFStateP_sub_Op_CastP2I6MpknENode__v_; +text: .text%__1cLCastP2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNaddL_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cKstoreCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_NewGlobalRef: jni.o; +text: .text%__1cKPSYoungGenRcapacity_in_bytes6kM_I_; +text: .text%__1cMtlsLoadPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNmaxI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNandL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMVirtualSpaceNreserved_size6kM_I_; +text: .text%__1cHNTarjanIsetdepth6MIpI_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_late6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopQbuild_loop_early6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopKDominators6M_v_; +text: .text%__1cHNTarjanDDFS6Fp0rnJVectorSet_pnOPhaseIdealLoop_pI_i_; +text: .text%__1cOPhaseIdealLoopRinit_dom_lca_tags6M_v_; +text: .text%__1cNaddP_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvI2L_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLeCXRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cJcmpOpOperHgreater6kM_i_: ad_i486_clone.o; +text: .text%__1cSmembar_acquireNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLklassItablebFinitialize_itable_for_interface6MpnMklassOopDesc_pnRitableMethodEntry__v_; +text: .text%__1cScheck_phi_clipping6FpnHPhiNode_rpnHConNode_rI45rpnENode_5_i_: cfgnode.o; +text: .text%__1cQjava_lang_ThreadRset_thread_status6FpnHoopDesc_n0AMThreadStatus__v_; +text: .text%__1cbFunnecessary_membar_volatileNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKCompiledICIis_clean6kM_i_; +text: .text%__1cMPhaseChaitinVfind_base_for_derived6MppnENode_2rI_2_; +text: .text%__1cKciTypeFlowLStateVectorGdo_new6MpnQciByteCodeStream__v_; +text: .text%__1cNinstanceKlassUfind_interface_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cENodeGOpcode6kM_i_; +text: .text%__1cKJavaThreadGactive6F_p0_; +text: .text%__1cFframeZinterpreter_frame_set_bcp6MpC_v_; +text: .text%__1cKstoreLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKJavaThreadNreguard_stack6MpC_i_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle__v_; +text: .text%JVM_FillInStackTrace; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle_pnGThread__v_; +text: .text%__1cSInterpreterRuntimePset_bcp_and_mdp6FpCpnKJavaThread__v_; +text: .text%__1cTjava_lang_ThrowableQclear_stacktrace6FpnHoopDesc__v_; +text: .text%__1cIUniverseWis_out_of_memory_error6FnGHandle__i_; +text: .text%__1cJloadSNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSshlL_eReg_1_31NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_StringOchar_converter6FnGHandle_HHpnGThread__1_; +text: .text%__1cLPcDescCacheLadd_pc_desc6MpnGPcDesc__v_; +text: .text%__1cNcmovI_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOstackSlotFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cOstackSlotFOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cLloadSSFNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateN_sub_Op_LoadL6MpknENode__v_; +text: .text%__1cNnegI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEcmpl6MnHAddress_i_v_; +text: .text%__1cJloadFNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cIModINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOGenerateOopMapJdo_astore6Mi_v_; +text: .text%__1cKCompiledICZcompute_monomorphic_entry6FnMmethodHandle_nLKlassHandle_iirnOCompiledICInfo_pnGThread__v_; +text: .text%__1cObox_handleNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cbBInterpreterCodeletInterfaceRcode_size_to_size6kMi_i_: interpreter.o; +text: .text%__1cbBInterpreterCodeletInterfaceKinitialize6MpnEStub_i_v_: interpreter.o; +text: .text%__1cIPhaseCFGOinsert_goto_at6MII_v_; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: memnode.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: memnode.o; +text: .text%__1cUParallelScavengeHeapEused6kM_I_; +text: .text%__1cOmulIS_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cLOpaque2NodeLbottom_type6kM_pknEType__: connode.o; +text: .text%__1cTshrL_eReg_32_63NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerGmovzxb6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cHciKlassSsuper_check_offset6M_I_; +text: .text%__1cOMacroAssemblerSload_unsigned_byte6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cICmpDNodeGOpcode6kM_i_; +text: .text%__1cLRuntimeStubbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: parse2.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: loopnode.o; +text: .text%__1cNDispatchTableJset_entry6MirnKEntryPoint__v_; +text: .text%__1cFParseJdo_ifnull6MnIBoolTestEmask__v_; +text: .text%__1cNmethodOopDescVclear_native_function6M_v_; +text: .text%__1cFframeLnmethods_do6M_v_; +text: .text%__1cJLoadBNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSSetupItableClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cLeAXRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%jni_NewLocalRef: jni.o; +text: .text%__1cKstoreFNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHMatcherQinline_cache_reg6F_nHOptoRegEName__; +text: .text%__1cLloadSSFNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapJppdupswap6Mipkc_v_; +text: .text%__1cGOopMapPset_derived_oop6MnHOptoRegEName_ii2_v_; +text: .text%__1cOimmI_32_63OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cLOptoRuntimebAresolve_opt_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cPClassFileParserbEparse_constant_pool_long_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cLConvF2DNodeGOpcode6kM_i_; +text: .text%__1cIMulINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cPconvL2I_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitOset_pair_local6MipnENode__v_: parse2.o; +text: .text%__1cFStateM_sub_Op_AndI6MpknENode__v_; +text: .text%__1cPClassFileParserbNparse_classfile_inner_classes_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__H_; +text: .text%__1cICodeHeapMmax_capacity6kM_I_; +text: .text%__1cHciKlassGloader6M_pnHoopDesc__: ciTypeArrayKlass.o; +text: .text%__1cKloadUBNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%jni_DeleteGlobalRef: jni.o; +text: .text%__1cJLoadCNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOPhaseIdealLoopLdo_split_if6MpnENode__v_; +text: .text%__1cRsubI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJcmpOpOperKless_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cNaddI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKTypeOopPtrSmake_from_constant6FpnIciObject__pk0_; +text: .text%__1cJArrayDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cMURShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%jni_GetObjectArrayElement: jni.o; +text: .text%__1cRmulI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQSystemDictionaryRpreloaded_oops_do6FpnKOopClosure__v_; +text: .text%__1cNloadConI0NodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cNThreadServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cKCompiledICSset_to_monomorphic6MrknOCompiledICInfo__v_; +text: .text%__1cMFlatProfilerHoops_do6FpnKOopClosure__v_; +text: .text%__1cNMemoryServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cIUniverseHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cOJNIHandleBlockMweak_oops_do6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cQVMOperationQdDueueHoops_do6MpnKOopClosure__v_; +text: .text%__1cKManagementHoops_do6FpnKOopClosure__v_; +text: .text%__1cSObjectSynchronizerHoops_do6FpnKOopClosure__v_; +text: .text%__1cKJNIHandlesHoops_do6FpnKOopClosure__v_; +text: .text%__1cIVMThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollectorXoops_do_for_all_threads6FpnKOopClosure__v_; +text: .text%__1cQPlaceholderTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cKJNIHandlesMweak_oops_do6FpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cXJvmtiCurrentBreakpointsHoops_do6FpnKOopClosure__v_; +text: .text%__1cLJvmtiExportHoops_do6FpnKOopClosure__v_; +text: .text%__1cNstoreImmINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPlocal_vsnprintf6FpcIpkcpv_i_; +text: .text%JVM_GetCPMethodModifiers; +text: .text%__1cIModLNodeGOpcode6kM_i_; +text: .text%__1cNSCMemProjNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cHnmethodNscope_desc_at6MpCi_pnJScopeDesc__; +text: .text%__1cTcompareAndSwapLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIAndLNodeGmul_id6kM_pknEType__: classes.o; +text: .text%jni_SetLongField: jni.o; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: multnode.o; +text: .text%__1cIAndLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%jio_vsnprintf; +text: .text%__1cLRethrowNodeEhash6kM_I_: classes.o; +text: .text%__1cWCallLeafNoFPDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsalI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRsalI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWCallLeafNoFPDirectNodeRis_safepoint_node6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerDjmp6MnHAddress__v_; +text: .text%__1cIimmLOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConLNodeFclone6kM_pnENode__; +text: .text%jio_snprintf; +text: .text%__1cTCallDynamicJavaNodeEhash6kM_I_: callnode.o; +text: .text%__1cNSignatureInfoJdo_double6M_v_: frame.o; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: subnode.o; +text: .text%__1cMstoreSSINodeHis_Copy6kM_I_: ad_i486_misc.o; +text: .text%__1cYmulI_imm_RShift_highNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQsalL_eReg_CLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLConvL2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIMulLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOjmpLoopEndNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOmulIS_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNinstanceKlassPadd_implementor6MpnMklassOopDesc__v_; +text: .text%__1cMdecI_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompiledVFrameEcode6kM_pnHnmethod__; +text: .text%__1cOleaPIdxOffNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReturnNode2t6MpnENode_2222_v_; +text: .text%__1cKReturnNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQComputeCallStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cPGlobalTLABStatsKinitialize6M_v_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJset_clock6Fx_v_; +text: .text%__1cRLowMemoryDetectorRdetect_low_memory6F_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersbBupdate_counters_from_policy6M_v_; +text: .text%__1cSReferenceProcessorOprocess_phase16MppnHoopDesc_pnPReferencePolicy_pnRBoolObjectClosure_pnKOopClosure_pnLVoidClosure__v_; +text: .text%__1cSReferenceProcessorbBenqueue_discovered_reflists6MppnHoopDesc__v_; +text: .text%__1cSReferenceProcessorbDenqueue_discovered_references6M_i_; +text: .text%__1cWThreadLocalAllocBufferQresize_all_tlabs6F_v_; +text: .text%__1cUParallelScavengeHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cPGCMemoryManagerIgc_begin6M_v_; +text: .text%__1cPGCMemoryManagerGgc_end6M_v_; +text: .text%__1cUParallelScavengeHeapTensure_parseability6M_v_; +text: .text%__1cUParallelScavengeHeapOfill_all_tlabs6M_v_; +text: .text%__1cNMemoryServiceStrack_memory_usage6F_v_; +text: .text%__1cNMemoryServiceIgc_begin6Fi_v_; +text: .text%__1cNMemoryServiceGgc_end6Fi_v_; +text: .text%__1cXTraceMemoryManagerStats2t6Mi_v_; +text: .text%__1cXTraceMemoryManagerStats2T6M_v_; +text: .text%__1cQSystemDictionaryHoops_do6FpnKOopClosure__v_; +text: .text%__1cRaddL_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKDictionaryHoops_do6MpnKOopClosure__v_; +text: .text%__1cXjava_lang_ref_ReferenceRpending_list_addr6F_ppnHoopDesc__; +text: .text%__1cUParallelScavengeHeapQresize_all_tlabs6M_v_; +text: .text%__1cVLoaderConstraintTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cUParallelScavengeHeapPupdate_counters6M_v_; +text: .text%__1cNCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersPupdate_counters6M_v_; +text: .text%__1cKPSYoungGenPupdate_counters6M_v_; +text: .text%__1cNCollectedHeapQresize_all_tlabs6M_v_; +text: .text%__1cTDerivedPointerTableFclear6F_v_; +text: .text%__1cNCollectedHeapOfill_all_tlabs6M_v_; +text: .text%__1cQLRUMaxHeapPolicy2t6M_v_; +text: .text%__1cTDerivedPointerTablePupdate_pointers6F_v_; +text: .text%__1cWThreadLocalAllocBufferbFaccumulate_statistics_before_gc6F_v_; +text: .text%__1cSReferenceProcessorQprocess_phaseJNI6M_v_; +text: .text%__1cSReferenceProcessorbDprocess_discovered_references6M_v_; +text: .text%__1cJOopMapSet2t6M_v_; +text: .text%__1cIVMThreadHexecute6FpnMVM_Operation__v_; +text: .text%__1cRInlineCacheBufferIis_empty6F_i_; +text: .text%__1cUSafepointSynchronizeRis_cleanup_needed6F_i_; +text: .text%__1cOPhaseIdealLoopUsplit_if_with_blocks6MrnJVectorSet_rnKNode_Stack__v_; +text: .text%__1cIMachOperFscale6kM_i_; +text: .text%__1cMtlsLoadPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachOperNconstant_disp6kM_i_; +text: .text%__1cPVM_GC_OperationQgc_count_changed6kM_i_; +text: .text%__1cPVM_GC_OperationZacquire_pending_list_lock6M_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constantPoolKlass.o; +text: .text%__1cQinstanceRefKlassZacquire_pending_list_lock6FpnJBasicLock__v_; +text: .text%__1cQinstanceRefKlassbKrelease_and_notify_pending_list_lock6FipnJBasicLock__v_; +text: .text%__1cPVM_GC_OperationNdoit_prologue6M_i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constantPoolKlass.o; +text: .text%__1cPVM_GC_OperationbKrelease_and_notify_pending_list_lock6M_v_; +text: .text%__1cIAndLNodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIAndLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cURethrowExceptionNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cIAndLNodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cJCodeCacheXmark_for_deoptimization6FpnMklassOopDesc__i_; +text: .text%__1cRmulI_eReg_immNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cNmodI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUSafepointSynchronizeFbegin6F_v_; +text: .text%__1cORuntimeServicebDrecord_safepoint_synchronized6F_v_; +text: .text%__1cCosbCmake_polling_page_unreadable6F_v_; +text: .text%__1cUSafepointSynchronizeQdo_cleanup_tasks6F_v_; +text: .text%__1cORuntimeServiceWrecord_safepoint_begin6F_v_; +text: .text%__1cSObjectSynchronizerVdeflate_idle_monitors6F_v_; +text: .text%__1cJloadFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMCounterDecayFdecay6F_v_; +text: .text%__1cTAbstractInterpreterRnotice_safepoints6F_v_; +text: .text%__1cONMethodSweeperFsweep6F_v_; +text: .text%__1cRInlineCacheBufferUupdate_inline_caches6F_v_; +text: .text%__1cUSafepointSynchronizeDend6F_v_; +text: .text%__1cORuntimeServiceUrecord_safepoint_end6F_v_; +text: .text%__1cTAbstractInterpreterRignore_safepoints6F_v_; +text: .text%__1cCosbAmake_polling_page_readable6F_v_; +text: .text%__1cURethrowExceptionNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOCompiledRFrameEinit6M_v_; +text: .text%__1cGvframeDtop6kM_p0_; +text: .text%__1cOCompilerOraclePshould_break_at6FnMmethodHandle__i_; +text: .text%__1cJOopMapSetJheap_size6kM_i_; +text: .text%__1cJOopMapSetHcopy_to6MpC_v_; +text: .text%__1cRAbstractAssemblerOcode_fill_byte6F_i_; +text: .text%__1cKCodeBufferJcopy_code6MpnICodeBlob__v_; +text: .text%__1cNSignatureInfoJdo_double6M_v_: bytecode.o; +text: .text%__1cICodeBlobWfix_relocation_at_move6Mi_v_; +text: .text%JVM_DoPrivileged; +text: .text%__1cVPatchingRelocIteratorIpostpass6M_v_; +text: .text%__1cVPatchingRelocIteratorHprepass6M_v_; +text: .text%__1cICodeBlobPallocation_size6FpnKCodeBuffer_ii_I_; +text: .text%__1cNRelocIteratorMcreate_index6FpnKCodeBuffer_pnJrelocInfo_4_4_; +text: .text%__1cICodeBlob2t6MpkcpnKCodeBuffer_iiipnJOopMapSet_i_v_; +text: .text%__1cKCodeBufferPcopy_relocation6MpnICodeBlob__v_; +text: .text%__1cIAddLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cRPrivilegedElementKinitialize6MpnMvframeStream_pnHoopDesc_p0pnGThread__v_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_base6MnITosState_ppCi_v_; +text: .text%__1cZInterpreterMacroAssemblerKverify_FPU6MinITosState__v_; +text: .text%__1cNmodL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cURethrowExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstoreFNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQVMOperationQdDueueDadd6MpnMVM_Operation__i_; +text: .text%__1cJEventMark2t6MpkcE_v_: vmThread.o; +text: .text%__1cGThreadMget_priority6Fkpk0_nOThreadPriority__; +text: .text%__1cIVMThreadSevaluate_operation6MpnMVM_Operation__v_; +text: .text%__1cQVMOperationQdDueueGunlink6MpnMVM_Operation__v_; +text: .text%__1cQVMOperationQdDueueOqueue_add_back6MipnMVM_Operation__v_; +text: .text%__1cMVM_OperationIevaluate6M_v_; +text: .text%__1cCosTget_native_priority6FkpknGThread_pi_nIOSReturn__; +text: .text%__1cMVM_OperationSset_calling_thread6MpnGThread_nOThreadPriority__v_; +text: .text%__1cQVMOperationQdDueueGinsert6MpnMVM_Operation_2_v_; +text: .text%__1cCosMget_priority6FkpknGThread_rnOThreadPriority__nIOSReturn__; +text: .text%__1cScompI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: jvm.o; +text: .text%__1cOcompI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cGGCTask2t6Mn0AEKindEkind__v_; +text: .text%__1cFParseGdo_new6M_v_; +text: .text%__1cLGCTaskQdDueue2t6Mi_v_; +text: .text%__1cJloadFNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNJvmtiGCMarker2t6Mi_v_; +text: .text%__1cNJvmtiGCMarker2T6M_v_; +text: .text%__1cKPSScavengeXshould_attempt_scavenge6F_i_; +text: .text%__1cUWaitForBarrierGCTask2t6Mi_v_; +text: .text%__1cUPSAdaptiveSizePolicyPupdate_averages6MiII_v_; +text: .text%__1cNBarrierGCTaskIdestruct6M_v_; +text: .text%__1cNBarrierGCTaskOdo_it_internal6MpnNGCTaskManager_I_v_; +text: .text%__1cGGCTaskIdestruct6M_v_; +text: .text%__1cLGCTaskQdDueueGcreate6F_p0_; +text: .text%__1cKPSYoungGenLswap_spaces6M_v_; +text: .text%__1cKPSYoungGenNresize_spaces6MII_v_; +text: .text%__1cKPSScavengeQinvoke_no_policy6Fpi_i_; +text: .text%__1cLGCTaskQdDueueHenqueue6Mp0_v_; +text: .text%__1cKPSYoungGenRresize_generation6MII_i_; +text: .text%__1cKPSYoungGenGresize6MII_v_; +text: .text%__1cZSerialOldToYoungRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cUParallelScavengeHeapQresize_young_gen6MII_v_; +text: .text%__1cZSerialOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cNGCTaskManagerIadd_list6MpnLGCTaskQdDueue__v_; +text: .text%__1cNGCTaskManagerVrelease_all_resources6M_v_; +text: .text%__1cUWaitForBarrierGCTaskEname6M_pc_: gcTaskManager.o; +text: .text%__1cUWaitForBarrierGCTaskGcreate6F_p0_; +text: .text%__1cSPSPromotionManagerMpre_scavenge6F_v_; +text: .text%__1cSPSPromotionManagerNpost_scavenge6F_v_; +text: .text%__1cPVM_GC_OperationNdoit_epilogue6M_v_; +text: .text%__1cSPSPromotionManagerbBvm_thread_promotion_manager6F_p0_; +text: .text%__1cUPSAdaptiveSizePolicyUminor_collection_end6MnHGCCauseFCause__v_; +text: .text%__1cSCardTableExtensionRscavenge_contents6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager__v_; +text: .text%__1cUPSAdaptiveSizePolicyWminor_collection_begin6M_v_; +text: .text%__1cNMonitorSupplyHrelease6FpnHMonitor__v_; +text: .text%__1cNMonitorSupplyHreserve6F_pnHMonitor__; +text: .text%__1cUWaitForBarrierGCTaskIwait_for6M_v_; +text: .text%__1cUWaitForBarrierGCTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cUWaitForBarrierGCTaskIdestruct6M_v_; +text: .text%__1cUWaitForBarrierGCTaskHdestroy6Fp0_v_; +text: .text%__1cUPSAdaptiveSizePolicybPcompute_survivor_space_size_and_threshold6MiiI_i_; +text: .text%__1cHThreadsZcreate_thread_roots_tasks6FpnLGCTaskQdDueue__v_; +text: .text%__1cPGlobalTLABStatsHpublish6M_v_; +text: .text%__1cMindirectOperNconstant_disp6kM_i_: ad_i486.o; +text: .text%__1cMindirectOperNbase_position6kM_i_: ad_i486.o; +text: .text%__1cbDVM_ParallelGCFailedAllocation2t6MIiiI_v_; +text: .text%__1cJloadSNodeFreloc6kM_i_; +text: .text%__1cMrep_stosNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateN_sub_Op_LoadS6MpknENode__v_; +text: .text%__1cQorI_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQshrI_eReg_CLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cYDebugInformationRecorderIpcs_size6M_i_; +text: .text%__1cNIdealLoopTreePiteration_split6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cHnmethodPscopes_pcs_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodQscopes_data_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodJstub_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodOexception_size6kM_i_: nmethod.o; +text: .text%__1cIMulLNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cLOopRecorderIoop_size6M_i_; +text: .text%__1cYDebugInformationRecorderJdata_size6M_i_; +text: .text%__1cHnmethodKtotal_size6kM_i_; +text: .text%__1cVCallRuntimeDirectNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNstoreImmINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cINodeHash2t6Mp0_v_; +text: .text%__1cOPhaseTransform2t6Mp0nFPhaseLPhaseNumber__v_; +text: .text%__1cLPhaseValues2t6Mp0_v_; +text: .text%__1cZCallInterpreterDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescbDbuild_interpreter_method_data6FnMmethodHandle_pnGThread__v_; +text: .text%__1cOJNIHandleBlockRrebuild_free_list6M_v_; +text: .text%__1cFStateM_sub_Op_Goto6MpknENode__v_; +text: .text%__1cFDictIFreset6MpknEDict__v_; +text: .text%__1cIPhaseCFGQFind_Inner_Loops6M_v_; +text: .text%__1cMPhaseChaitinRRegister_Allocate6M_v_; +text: .text%__1cIPhaseCFGJbuild_cfg6M_I_; +text: .text%__1cIPhaseCFG2t6MpnFArena_pnIRootNode_rnHMatcher__v_; +text: .text%__1cHMatcherZnumber_of_saved_registers6F_i_; +text: .text%__1cMPhaseChaitin2t6MIrnIPhaseCFG_rnHMatcher__v_; +text: .text%__1cOCompileWrapper2t6MpnHCompile__v_; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: cfgnode.o; +text: .text%__1cETypeKInitialize6FpnHCompile__v_; +text: .text%__1cIPhaseCFGKDominators6M_v_; +text: .text%__1cHCompileEInit6Mi_v_; +text: .text%__1cIPhaseCFGDDFS6MpnGTarjan__I_; +text: .text%__1cIPhaseCFGVschedule_pinned_nodes6MrnJVectorSet__v_; +text: .text%__1cIPhaseCFGOschedule_early6MrnJVectorSet_rnJNode_List_rnLBlock_Array__i_; +text: .text%__1cXPhaseAggressiveCoalesceGverify6M_v_: coalesce.o; +text: .text%__1cWNode_Backward_Iterator2t6MpnENode_rnJVectorSet_rnJNode_List_rnLBlock_Array__v_; +text: .text%__1cIPhaseCFGNschedule_late6MrnJVectorSet_rnJNode_List_rnNGrowableArray4CI___v_; +text: .text%__1cIPhaseCFGQGlobalCodeMotion6MrnHMatcher_IrnJNode_List__v_; +text: .text%__1cVExceptionHandlerTable2t6Mi_v_; +text: .text%__1cGTarjanIsetdepth6MI_v_; +text: .text%__1cHCompileICode_Gen6M_v_; +text: .text%__1cIPhaseCFGYEstimate_Block_Frequency6M_v_; +text: .text%__1cXPhaseAggressiveCoalesceNinsert_copies6MrnHMatcher__v_; +text: .text%__1cMPhaseChaitinbGstretch_base_pointer_live_ranges6MpnMResourceArea__i_; +text: .text%__1cNPhaseRegAllocTpd_preallocate_hook6M_v_; +text: .text%__1cQorI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cYDebugInformationRecorder2t6MpnLOopRecorder__v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: matcher.o; +text: .text%__1cHMatcherUvalidate_null_checks6M_v_; +text: .text%__1cHMatcherPinit_spill_mask6MpnENode__v_; +text: .text%__1cHMatcherTFixup_Save_On_Entry6M_v_; +text: .text%__1cHMatcherVinit_first_stack_mask6M_v_; +text: .text%__1cHMatcherFmatch6M_v_; +text: .text%__1cHCompileOcompute_old_SP6M_nHOptoRegEName__; +text: .text%__1cHMatcher2t6MrnJNode_List__v_; +text: .text%__1cUDebugInfoWriteStream2t6MpnYDebugInformationRecorder_i_v_; +text: .text%__1cOleaPIdxOffNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cHMatcherLreturn_addr6kM_nHOptoRegEName__; +text: .text%__1cHCompilebBregister_library_intrinsics6M_v_; +text: .text%__1cMURShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMPhaseChaitinRbuild_ifg_virtual6M_v_; +text: .text%__1cIAndLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJPhaseLive2t6MrknIPhaseCFG_rnILRG_List_pnFArena__v_; +text: .text%__1cIPhaseIFG2t6MpnFArena__v_; +text: .text%__1cMPhaseChaitinGde_ssa6M_v_; +text: .text%__1cFArenaNmove_contents6Mp0_1_; +text: .text%__1cNPhaseRegAlloc2t6MIrnIPhaseCFG_rnHMatcher_pF_v_v_; +text: .text%__1cFArena2t6MI_v_; +text: .text%__1cWsize_exception_handler6F_I_; +text: .text%__1cHCompileYinit_scratch_buffer_blob6M_v_; +text: .text%__1cOCompileWrapper2T6M_v_; +text: .text%__1cUPSAdaptiveSizePolicyZdecay_supplemental_growth6Mi_v_; +text: .text%__1cNPhasePeepholeMdo_transform6M_v_; +text: .text%__1cHCompileTframe_size_in_words6kM_i_; +text: .text%__1cNPhasePeephole2T6M_v_; +text: .text%__1cNPhasePeephole2t6MpnNPhaseRegAlloc_rnIPhaseCFG__v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: buildOopMap.o; +text: .text%__1cHCompileMBuildOopMaps6M_v_; +text: .text%__1cWemit_exception_handler6FrnKCodeBuffer__v_; +text: .text%__1cLdo_liveness6FpnNPhaseRegAlloc_pnIPhaseCFG_pnKBlock_List_ipnFArena_pnEDict__v_: buildOopMap.o; +text: .text%__1cMPhaseChaitin2T6M_v_; +text: .text%__1cMPhaseChaitinbApost_allocate_copy_removal6M_v_; +text: .text%__1cIPhaseCFGLRemoveEmpty6M_v_; +text: .text%__1cNPhaseRegAllocPalloc_node_regs6Mi_v_; +text: .text%__1cMPhaseChaitinMfixup_spills6M_v_; +text: .text%__1cHCompileGOutput6M_v_; +text: .text%__1cHCompileQShorten_branches6MpnFLabel_ri333_v_; +text: .text%__1cHCompileLFill_buffer6M_v_; +text: .text%__1cHCompileTFillExceptionTables6MIpI1pnFLabel__v_; +text: .text%__1cHCompileRScheduleAndBundle6M_v_; +text: .text%__1cKCodeBufferOrelocate_stubs6M_v_; +text: .text%__1cUPSAdaptiveSizePolicybPeden_increment_with_supplement_aligned_up6MI_I_; +text: .text%__1cUPSAdaptiveSizePolicyOeden_increment6MII_I_; +text: .text%__1cOMachPrologNodeFreloc6kM_i_; +text: .text%__1cUPSAdaptiveSizePolicyVadjust_for_throughput6MipI1_v_; +text: .text%__1cWImplicitExceptionTableIset_size6MI_v_; +text: .text%__1cJPhaseLive2T6M_v_; +text: .text%__1cUPSAdaptiveSizePolicybDcompute_generation_free_space6MIIIIIIIi_v_; +text: .text%__1cIPSOldGenMmax_gen_size6M_I_: psOldGen.o; +text: .text%__1cUPSAdaptiveSizePolicybHclear_generation_free_space_flags6M_v_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: loopnode.o; +text: .text%__1cUPSAdaptiveSizePolicyQdecaying_gc_cost6kM_d_; +text: .text%__1cHCompileYinit_scratch_locs_memory6M_v_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: methodDataKlass.o; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_words6FpnNmethodOopDesc__i_; +text: .text%__1cRmethodDataOopDescPpost_initialize6MpnOBytecodeStream__v_; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_bytes6FpnNmethodOopDesc__i_; +text: .text%__1cRmethodDataOopDescKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: methodDataOop.o; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: methodDataKlass.o; +text: .text%__1cNmulL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbDVM_ParallelGCFailedAllocationEname6kM_pkc_: vm_operations.o; +text: .text%__1cbDVM_ParallelGCFailedAllocationEdoit6M_v_; +text: .text%__1cKoopFactoryOnew_methodData6FnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: methodDataKlass.o; +text: .text%__1cKPSScavengeGinvoke6Fpi_v_; +text: .text%__1cUParallelScavengeHeapTfailed_mem_allocate6MpiIii_pnIHeapWord__; +text: .text%__1cUPSAdaptiveSizePolicyOshould_full_GC6MI_i_; +text: .text%__1cPmethodDataKlassIallocate6MnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cOcmpD_cc_P6NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: loopTransform.o; +text: .text%__1cKklassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cHRetNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateO_sub_Op_Return6MpknENode__v_; +text: .text%__1cLlog2_intptr6Fi_i_: divnode.o; +text: .text%__1cbLtransform_int_divide_to_long_multiply6FpnIPhaseGVN_pnENode_i_3_: divnode.o; +text: .text%__1cFStateM_sub_Op_ConL6MpknENode__v_; +text: .text%__1cSmembar_acquireNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cNcmovI_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cHRetNodeFreloc6kM_i_; +text: .text%__1cOClearArrayNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciObjectOis_method_data6M_i_: ciInstance.o; +text: .text%__1cIciObjectJis_method6M_i_: ciInstance.o; +text: .text%__1cNaddP_eRegNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cIConFNodeGOpcode6kM_i_; +text: .text%__1cLRuntimeStubHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cJCodeCacheNalive_nmethod6FpnICodeBlob__pnHnmethod__; +text: .text%__1cIGraphKitNallocate_heap6MpnENode_222pknITypeFunc_pC22ipknKTypeOopPtr__2_; +text: .text%__1cPciInstanceKlassbBcompute_shared_has_subklass6M_i_; +text: .text%__1cOLibraryCallKitNtry_to_inline6M_i_; +text: .text%__1cQLibraryIntrinsicIgenerate6MpnIJVMState__2_; +text: .text%__1cLRuntimeStubbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cOcompP_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_CmpL6MpknENode__v_; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_: library_call.o; +text: .text%__1cJAssemblerDjcc6Mn0AJCondition_pCnJrelocInfoJrelocType__v_; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopTransform.o; +text: .text%__1cKCompiledICSset_ic_destination6MpC_v_; +text: .text%__1cLConvL2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cHBitDataKis_BitData6M_i_: ciMethodData.o; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_next6MnITosState_i_v_; +text: .text%__1cRxorI_eReg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNaddL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivL_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPshlI_eReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cGThreadOis_interrupted6Fp0i_i_; +text: .text%__1cSmembar_releaseNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNdecI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNandL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQshrI_eReg_CLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNdecI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJLoadDNodeGOpcode6kM_i_; +text: .text%__1cMstoreSSINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopOplace_near_use6kMpnENode__2_; +text: .text%__1cSInterpreterRuntimeOprofile_method6FpnKJavaThread_pC_i_; +text: .text%__1cOcmpD_cc_P6NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadLNodeFreloc6kM_i_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: multnode.o; +text: .text%__1cRxorI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLBoxLockNodeEhash6kM_I_: classes.o; +text: .text%__1cIAddLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRaddI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__: ad_i486_misc.o; +text: .text%__1cJlabelOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cNsubL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulLNodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cIMulLNodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cTconvF2I_reg_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: loopopts.o; +text: .text%__1cXmembar_acquire_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescThas_native_function6kM_i_; +text: .text%__1cRxorI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateR_sub_Op_SafePoint6MpknENode__v_; +text: .text%__1cMciMethodDataStrap_recompiled_at6MpnLProfileData__i_; +text: .text%__1cSsafePoint_pollNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSsafePoint_pollNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSsafePoint_pollNodeFreloc6kM_i_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlass.o; +text: .text%__1cPpoll_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cLOptoRuntimeRmultianewarray1_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cNmethodOopDescWis_vanilla_constructor6kM_i_; +text: .text%__1cKCodeBuffer2t6MpCi_v_; +text: .text%__1cNandI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSshlL_eReg_1_31NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: memnode.o; +text: .text%__1cOmulIS_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQsalI_eReg_CLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopKclone_loop6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cQsalL_eReg_CLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cXroundDouble_mem_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRandI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKciTypeFlowFBlockQset_private_copy6Mi_v_; +text: .text%__1cLimmI_16OperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cHi2sNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cHi2sNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRxorI_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvI2L_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateL_sub_Op_OrI6MpknENode__v_; +text: .text%JVM_GetClassNameUTF; +text: .text%__1cSshrL_eReg_1_31NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKStoreDNodeGOpcode6kM_i_; +text: .text%__1cNcmovP_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: loopTransform.o; +text: .text%__1cIXorINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cOcmpD_cc_P6NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNmaxI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFframeMpd_gc_epilog6M_v_; +text: .text%__1cRframe_gc_epilogue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeLgc_epilogue6M_v_; +text: .text%__1cRframe_gc_prologue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeLgc_prologue6M_v_; +text: .text%__1cHTypePtrEmake6FnETypeFTYPES_n0ADPTR_i_pk0_; +text: .text%__1cIAddLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%JVM_FindLoadedClass; +text: .text%__1cIMulFNodeGOpcode6kM_i_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: vmThread.o; +text: .text%__1cCosJyield_all6Fi_v_; +text: .text%__1cNstoreImmINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJBytecodesRspecial_length_at6FpC_i_; +text: .text%__1cOstackSlotLOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cRaddI_eReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvI2DNodeGOpcode6kM_i_; +text: .text%__1cTcompareAndSwapLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSloadL_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUjmpLoopEnd_shortNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cUjmpLoopEnd_shortNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMjniIdMapBaseHoops_do6MpnKOopClosure__v_; +text: .text%__1cNmodL_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEfrom6F_pnMRegisterImpl__; +text: .text%__1cIjniIdMapHoops_do6MpnKOopClosure__v_; +text: .text%__1cTDerivedPointerTableDadd6FppnHoopDesc_3_v_; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: classes.o; +text: .text%__1cPadd_derived_oop6FppnHoopDesc_2_v_: oopMap.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: loopnode.o; +text: .text%__1cKstoreCNodeFreloc6kM_i_; +text: .text%__1cKRegionNodeJideal_reg6kM_I_: loopnode.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: loopnode.o; +text: .text%__1cMincI_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Copy6kM_I_: loopnode.o; +text: .text%__1cXconvI2L_reg_reg_zexNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJOopMapSetMgrow_om_data6M_v_; +text: .text%__1cScompP_mem_eRegNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKReflectionGinvoke6FnTinstanceKlassHandle_nMmethodHandle_nGHandle_inOobjArrayHandle_nJBasicType_4ipnGThread__pnHoopDesc__; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectIoverride6FpnHoopDesc__C_; +text: .text%__1cKReflectionDbox6FpnGjvalue_nJBasicType_pnGThread__pnHoopDesc__; +text: .text%__1cJAssemblerDret6Mi_v_; +text: .text%__1cTcmovII_reg_LEGTNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJAssemblerEcall6MrnFLabel_nJrelocInfoJrelocType__v_; +text: .text%__1cLBoxLockNode2t6Mi_v_; +text: .text%__1cIGraphKitMnext_monitor6M_i_; +text: .text%__1cTshrL_eReg_32_63NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cLOptoRuntimebBcomplete_monitor_enter_Type6F_pknITypeFunc__; +text: .text%__1cMdecI_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIMaxINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFStateQ_sub_Op_FastLock6MpknENode__v_; +text: .text%__1cPcmpFastLockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIGraphKitLshared_lock6MpnENode__pnMFastLockNode__; +text: .text%__1cQjava_lang_ThreadGthread6FpnHoopDesc__pnKJavaThread__; +text: .text%__1cKciTypeFlowLStateVectorJdo_aaload6MpnQciByteCodeStream__v_; +text: .text%__1cNaddI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cEUTF8Ounicode_length6Fpkc_i_; +text: .text%__1cPCallRuntimeNodeGOpcode6kM_i_; +text: .text%__1cFParseFBlockMadd_new_path6M_i_; +text: .text%__1cYcmpL_zero_flags_LEGTNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%JVM_FindClassFromClass; +text: .text%__1cOmangle_name_on6FpnMoutputStream_pnNsymbolOopDesc_ii_v_: nativeLookup.o; +text: .text%__1cIMulINodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cMdecI_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPshlI_eReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbDjava_lang_reflect_ConstructorFclazz6FpnHoopDesc__2_; +text: .text%__1cNSignatureInfoIdo_float6M_v_: frame.o; +text: .text%JVM_NewInstanceFromConstructor; +text: .text%__1cbDjava_lang_reflect_ConstructorPparameter_types6FpnHoopDesc__2_; +text: .text%__1cbDjava_lang_reflect_ConstructorEslot6FpnHoopDesc__i_; +text: .text%__1cLConvI2LNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKReflectionSinvoke_constructor6FpnHoopDesc_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cNminI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cMorI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOmangle_name_on6FpnMoutputStream_pnNsymbolOopDesc__v_: nativeLookup.o; +text: .text%__1cNIdealLoopTreeMpolicy_align6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeNpolicy_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeSpolicy_range_check6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeQpolicy_peel_only6kMpnOPhaseIdealLoop__i_; +text: .text%__1cLloadSSFNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMtlsLoadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYmulI_imm_RShift_highNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFParseFBlockNstack_type_at6kMi_pknEType__; +text: .text%__1cJAssemblerGmovzxw6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cFStateN_sub_Op_LoadB6MpknENode__v_; +text: .text%__1cIMulLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOMacroAssemblerSload_unsigned_word6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cKstoreBNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cZCallInterpreterDirectNodeSalignment_required6kM_i_: ad_i486_misc.o; +text: .text%__1cZCallInterpreterDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNloadConL0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZCallInterpreterDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cTsarL_eReg_32_63NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cITemplateIbytecode6kM_nJBytecodesECode__; +text: .text%__1cLLShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMorI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLLShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: callnode.o; +text: .text%__1cNtestU_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMURShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLMachUEPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cWflagsReg_long_LTGEOperFclone6kM_pnIMachOper__; +text: .text%__1cIAddLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRsarI_eReg_immNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIJVMStateNmonitor_depth6kM_i_: graphKit.o; +text: .text%__1cWCallLeafNoFPDirectNodeKmethod_set6Mi_v_; +text: .text%__1cIregFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlassKlass.o; +text: .text%__1cHnmethodUnumber_of_dependents6kM_i_: nmethod.o; +text: .text%__1cNSingletonBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: node.o; +text: .text%lwp_mutex_init: os_solaris.o; +text: .text%__1cFParseLarray_store6MnJBasicType__v_; +text: .text%__1cLRShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLRethrowNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPsarI_eReg_1NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKcmpOpUOperEless6kM_i_: ad_i486_clone.o; +text: .text%__1cFParseMdo_checkcast6M_v_; +text: .text%__1cMloadConDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOPhaseIdealLoopVclone_up_backedge_goo6MpnENode_22_2_; +text: .text%__1cbACallCompiledJavaDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJStubQdDueueHrequest6Mi_pnEStub__; +text: .text%__1cJStubQdDueueGcommit6Mi_v_; +text: .text%__1cMLinkResolverbHlinktime_resolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnHoopDesc_pnNsymbolOopDesc__i_; +text: .text%__1cENodeHdel_out6Mp0_v_: doCall.o; +text: .text%__1cPfieldDescriptorUstring_initial_value6kMpnGThread__pnHoopDesc__; +text: .text%__1cKRegionNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%jni_NewString: jni.o; +text: .text%__1cQjava_lang_StringXcreate_oop_from_unicode6FpHipnGThread__pnHoopDesc__; +text: .text%__1cMincI_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cHTypePtrFempty6kM_i_; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_i_v_; +text: .text%__1cTshrL_eReg_32_63NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXmembar_acquire_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMMergeMemNodeIadr_type6kM_pknHTypePtr__: memnode.o; +text: .text%__1cITypeLongFwiden6kMpknEType__3_; +text: .text%__1cIplus_adr6FpnENode_i_1_: generateOptoStub.o; +text: .text%__1cIModINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQjava_lang_StringTcreate_oop_from_str6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cKoopFactoryNnew_charArray6FpkcpnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cScompP_mem_eRegNodeFreloc6kM_i_; +text: .text%__1cKciTypeFlowLStateVectorMdo_checkcast6MpnQciByteCodeStream__v_; +text: .text%__1cNSignatureInfoIdo_short6M_v_: frame.o; +text: .text%__1cMadjust_check6FpnENode_11iipnMPhaseIterGVN__v_: ifnode.o; +text: .text%__1cNmaxI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNtestU_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateP_sub_Op_ConvI2L6MpknENode__v_; +text: .text%__1cNdivL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJScopeDescGsender6kM_p0_; +text: .text%__1cOcompiledVFrameGsender6kM_pnGvframe__; +text: .text%__1cOstackSlotIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cQciTypeArrayKlassEmake6FnJBasicType__p0_; +text: .text%__1cLPhaseValuesKis_IterGVN6M_pnMPhaseIterGVN__: phaseX.o; +text: .text%__1cQsalI_eReg_CLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQciTypeArrayKlassJmake_impl6FnJBasicType__p0_; +text: .text%__1cJloadBNodeFreloc6kM_i_; +text: .text%__1cOcmpD_cc_P6NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOleaPIdxOffNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodKlass.o; +text: .text%__1cKJavaThreadJframes_do6MpFpnFframe_pknLRegisterMap__v_v_; +text: .text%__1cSmembar_acquireNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cKEntryPoint2t6MpC11111111_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: compiledIC.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constMethodKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constMethodKlass.o; +text: .text%__1cQjava_lang_ThreadRget_thread_status6FpnHoopDesc__n0AMThreadStatus__; +text: .text%__1cRmulI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTtypeArrayKlassKlassIoop_size6kMpnHoopDesc__i_: typeArrayKlassKlass.o; +text: .text%__1cPstoreImmI16NodeFreloc6kM_i_; +text: .text%__1cFStateP_sub_Op_ConvL2I6MpknENode__v_; +text: .text%__1cLloadSSDNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIXorINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cYcmpL_zero_flags_LEGTNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNcmovI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOmulIS_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cVloadConL_low_onlyNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRsubI_eReg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: typeArrayKlass.o; +text: .text%__1cOcmpD_cc_P6NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceRefKlass.o; +text: .text%__1cObox_handleNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadFNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSmembar_acquireNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCompileBrokerNallocate_task6F_pnLCompileTask__; +text: .text%__1cNCompileBrokerTcreate_compile_task6FpnMCompileQdDueue_inMmethodHandle_i3ipkcii_pnLCompileTask__; +text: .text%__1cMCompileQdDueueDadd6MpnLCompileTask__v_; +text: .text%__1cLCompileTaskKinitialize6MinMmethodHandle_i1ipkcii_v_; +text: .text%jni_GetObjectClass: jni.o; +text: .text%__1cMCompileQdDueueDget6M_pnLCompileTask__; +text: .text%__1cSmembar_acquireNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLBoxLockNodeDcmp6kMrknENode__I_; +text: .text%__1cXmembar_acquire_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cPconvF2D_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMincI_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cSCompileTaskWrapper2t6MpnLCompileTask__v_; +text: .text%__1cCosPhint_no_preempt6F_v_; +text: .text%__1cUGenericGrowableArrayNraw_appendAll6Mpk0_v_; +text: .text%__1cFStateM_sub_Op_MulL6MpknENode__v_; +text: .text%__1cOstackSlotFOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cLCompileTaskEfree6M_v_; +text: .text%__1cFciEnv2t6MpnHJNIEnv__iii_v_; +text: .text%__1cSCompileTaskWrapper2T6M_v_; +text: .text%__1cPciObjectFactory2t6MpnFArena_i_v_; +text: .text%__1cNCompileBrokerJfree_task6FpnLCompileTask__v_; +text: .text%__1cRappend_interfaces6FnOobjArrayHandle_ripnPobjArrayOopDesc__v_; +text: .text%__1cNCompileBrokerVpush_jni_handle_block6F_v_; +text: .text%__1cTmembar_volatileNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFciEnvWget_method_from_handle6MpnI_jobject__pnIciMethod__; +text: .text%__1cSshrL_eReg_1_31NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFciEnv2T6M_v_; +text: .text%__1cNmodI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cNinstanceKlassbFlookup_method_in_all_interfaces6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cNCompileBrokerUpop_jni_handle_block6F_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: arrayKlass.o; +text: .text%__1cNaddP_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionaryRnumber_of_classes6F_i_; +text: .text%__1cSstring_compareNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIimmFOperJconstantF6kM_f_: ad_i486_clone.o; +text: .text%__1cOcompP_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cOMacroAssemblerFalign6Mi_v_; +text: .text%__1cOstackSlotPOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cObox_handleNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOstackSlotPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLCodeletMark2t6MrpnZInterpreterMacroAssembler_pkcinJBytecodesECode__v_: interpreter.o; +text: .text%__1cSInterpreterCodeletKinitialize6MpkcinJBytecodesECode__v_; +text: .text%__1cNandI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cLloadSSFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmodL_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQmulD_reg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIMinINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cNandI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cITemplateIgenerate6MpnZInterpreterMacroAssembler__v_; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_and_dispatch6MpnITemplate_nITosState__v_; +text: .text%__1cITemplateKinitialize6MinITosState_1pFi_vi_v_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFi_vi_v_; +text: .text%JVM_FindClassFromClassLoader; +text: .text%__1cPshrI_eReg_1NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cHi2bNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMmatch_option6FpknMJavaVMOption_pkcp4_i_: arguments.o; +text: .text%__1cNnegI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSstring_compareNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOPhaseTransform2t6MpnFArena_nFPhaseLPhaseNumber__v_; +text: .text%__1cHCompileKinit_start6MpnJStartNode__v_; +text: .text%__1cMstoreSSINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLPhaseValues2t6MpnFArena_I_v_; +text: .text%__1cINodeHash2t6MpnFArena_I_v_; +text: .text%__1cINodeHashIround_up6FI_I_; +text: .text%__1cbACallCompiledJavaDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKScopeValueJread_from6FpnTDebugInfoReadStream__p0_; +text: .text%JVM_IHashCode; +text: .text%lwp_cond_init: os_solaris.o; +text: .text%__1cNCollectedHeapMobj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: jvm.o; +text: .text%__1cIVMThreadMis_VM_thread6kM_i_: vmThread.o; +text: .text%__1cOcompiledVFrameScreate_stack_value6kMpnKScopeValue__pnKStackValue__; +text: .text%__1cGThreadOis_Java_thread6kM_i_: vmThread.o; +text: .text%__1cKstoreBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorCto6F_pnMRegisterImpl__; +text: .text%__1cKDictionarybAis_valid_protection_domain6MiInMsymbolHandle_nGHandle_2_i_; +text: .text%__1cPconvI2L_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLStrCompNodeKmatch_edge6kMI_I_; +text: .text%__1cZInterpreterMacroAssemblerEpush6MnITosState__v_; +text: .text%__1cRtestI_reg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachNodeJis_MachIf6kM_pknKMachIfNode__: ad_i486_misc.o; +text: .text%__1cNmaxI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_AddL6MpknENode__v_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_i_v_; +text: .text%__1cFForteNregister_stub6FpkcpC3_v_; +text: .text%__1cXroundDouble_mem_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFVTuneNregister_stub6FpkcpC3_v_; +text: .text%__1cENodeEgetd6kM_d_; +text: .text%__1cNSignatureInfoHdo_char6M_v_: frame.o; +text: .text%__1cMstoreSSPNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: nmethod.o; +text: .text%__1cFStateM_sub_Op_AndL6MpknENode__v_; +text: .text%__1cKConv2BNodeGOpcode6kM_i_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii1i_v_; +text: .text%JVM_GetClassLoader; +text: .text%__1cRmulI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorQset_entry_points6MnJBytecodesECode__v_; +text: .text%__1cbCAbstractInterpreterGeneratorWset_short_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cOjmpLoopEndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateW_sub_Op_CountedLoopEnd6MpknENode__v_; +text: .text%__1cXconvI2L_reg_reg_zexNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMinINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSCompareAndSwapNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKBinaryNodeGOpcode6kM_i_; +text: .text%__1cUmembar_cpu_orderNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsalL_eReg_CLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNSCMemProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFStateW_sub_Op_MemBarCPUOrder6MpknENode__v_; +text: .text%__1cFStateO_sub_Op_Binary6MpknENode__v_; +text: .text%__1cNSignatureInfoIdo_float6M_v_: bytecode.o; +text: .text%__1cOMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cZInterpreterMacroAssemblerZcheck_and_handle_popframe6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableKtransition6FnITosState_1_v_; +text: .text%__1cPClassFileParserbGparse_constant_pool_double_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cRaddI_eReg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadDNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSleaP_eReg_immINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOjmpLoopEndNodeOis_pc_relative6kM_i_: ad_i486_misc.o; +text: .text%__1cOjmpLoopEndNodeTmay_be_short_branch6kM_i_: ad_i486_misc.o; +text: .text%__1cIModINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRmulI_eReg_immNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cKNativeJumpbEcheck_verified_entry_alignment6FpC1_v_; +text: .text%__1cTbasictype2arraycopy6FnJBasicType_i_pC_; +text: .text%__1cFStateU_sub_Op_CallLeafNoFP6MpknENode__v_; +text: .text%__1cZInterpreterMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cLOptoRuntimeOarraycopy_Type6F_pknITypeFunc__; +text: .text%__1cMMachCallNodeXreturns_float_or_double6kM_i_; +text: .text%__1cOLibraryCallKitQinline_arraycopy6M_i_; +text: .text%__1cOMacroAssemblerOcall_VM_helper6MpnMRegisterImpl_pCii_v_; +text: .text%__1cWCallLeafNoFPDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTcmovII_reg_LTGENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cbACallCompiledJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: compiledIC.o; +text: .text%__1cNLocationValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cILocation2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cUCompressedReadStreamIread_int6M_i_: location.o; +text: .text%__1cNLocationValueLis_location6kM_i_: debugInfo.o; +text: .text%__1cJAssemblerEmovl6MpnMRegisterImpl_2_v_; +text: .text%__1cFParseRarray_store_check6M_v_; +text: .text%__1cQshrI_eReg_CLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNCompileBrokerTis_compile_blocking6FnMmethodHandle_i_i_; +text: .text%__1cNCompileBrokerRassign_compile_id6FnMmethodHandle_i_I_; +text: .text%__1cNCompileBrokerZcompilation_is_prohibited6FnMmethodHandle_i_i_; +text: .text%__1cNCompileBrokerTis_not_compile_only6FnMmethodHandle__i_; +text: .text%__1cNsubL_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cQsalL_eReg_CLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_prolog6MnITosState_i_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_epilog6MnITosState_i_v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constMethodKlass.o; +text: .text%__1cFMutex2t6Mipkci_v_; +text: .text%__1cJAssemblerLemit_farith6Miii_v_; +text: .text%__1cIciMethodJhas_loops6kM_i_; +text: .text%__1cRtestI_reg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJStartNodeJideal_reg6kM_I_: callnode.o; +text: .text%__1cNCompileBrokerQset_last_compile6FpnOCompilerThread_nMmethodHandle_ii_v_; +text: .text%__1cNCompileBrokerOcheck_break_at6FnMmethodHandle_iii_i_; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: callnode.o; +text: .text%__1cJStartNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cKC2CompilerOcompile_method6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cNCompileBrokerZinvoke_compiler_on_method6FpnLCompileTask__v_; +text: .text%__1cNSignatureInfoIdo_short6M_v_: bytecode.o; +text: .text%__1cOCompiledRFrameKtop_method6kM_nMmethodHandle__: rframe.o; +text: .text%__1cOCompiledRFrameLis_compiled6kM_i_: rframe.o; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_: nmethod.o; +text: .text%__1cNminI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOCompiledRFrame2t6MnFframe_pnKJavaThread_kpnGRFrame__v_; +text: .text%__1cLAccessFlagsRatomic_clear_bits6Mi_v_; +text: .text%__1cMelapsedTimerDadd6M0_v_; +text: .text%__1cSmembar_releaseNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFciEnvPregister_method6MpnIciMethod_iiiiiipnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler_ii_v_; +text: .text%__1cFciEnvbOcheck_for_system_dictionary_modification6MpnIciMethod__v_; +text: .text%__1cNCompileBrokerScollect_statistics6FpnOCompilerThread_nMelapsedTimer_pnLCompileTask__v_; +text: .text%__1cFciEnvbUsystem_dictionary_modification_counter_changed6M_i_; +text: .text%__1cLConvI2FNodeGOpcode6kM_i_; +text: .text%__1cIciMethodQbreak_at_execute6M_i_; +text: .text%__1cQComputeCallStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cICodeHeapMinsert_after6MpnJFreeBlock_2_v_; +text: .text%__1cHnmFlagsFclear6M_v_; +text: .text%__1cHnmethodQcopy_scopes_data6MpCi_v_; +text: .text%__1cHnmethodSresolve_JNIHandles6M_v_; +text: .text%__1cVExceptionHandlerTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cHnmethod2t6MpnNmethodOopDesc_iiiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__v_; +text: .text%__1cHnmethod2n6FIi_pv_; +text: .text%__1cWImplicitExceptionTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cHnmethodLnew_nmethod6FnMmethodHandle_iiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__p0_; +text: .text%__1cFVTuneOcreate_nmethod6FpnHnmethod__v_; +text: .text%__1cLPcDescCache2t6M_v_; +text: .text%__1cFciEnvbFpost_compiled_method_load_event6MpnHnmethod__v_; +text: .text%__1cFciEnvVnum_inlined_bytecodes6kM_i_; +text: .text%__1cJCodeCacheGcommit6FpnICodeBlob__v_; +text: .text%__1cICodeBlobJcopy_oops6MppnI_jobject_i_v_; +text: .text%__1cYDebugInformationRecorderHcopy_to6MpnHnmethod__v_; +text: .text%__1cTcmovII_reg_LEGTNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cLOopRecorderHcopy_to6MpnICodeBlob__v_; +text: .text%__1cZCallDynamicJavaDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLOptoRuntimeInew_Type6F_pknITypeFunc__; +text: .text%__1cQorI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_3pnRJavaCallArguments_nGHandle_6_6_; +text: .text%__1cNsubL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cIGraphKitMnew_instance6MpnPciInstanceKlass__pnENode__; +text: .text%__1cHCompileUremove_useless_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cIciMethodRbuild_method_data6MnMmethodHandle__v_; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod_ii_v_; +text: .text%__1cHCompileWprint_compile_messages6M_v_; +text: .text%__1cQUnique_Node_ListUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cHCompileVidentify_useful_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cINodeHashUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cSPhaseRemoveUseless2t6MpnIPhaseGVN_pnQUnique_Node_List__v_; +text: .text%__1cIciMethodRbuild_method_data6M_v_; +text: .text%__1cHCompileLInline_Warm6M_i_; +text: .text%__1cMPhaseIterGVN2t6MpnIPhaseGVN__v_; +text: .text%__1cKloadUBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIPhaseCCP2t6MpnMPhaseIterGVN__v_; +text: .text%__1cHCompileRbuild_start_state6MpnJStartNode_pknITypeFunc__pnIJVMState__; +text: .text%__1cIPhaseCCP2T6M_v_; +text: .text%__1cIPhaseCCPHanalyze6M_v_; +text: .text%__1cIPhaseCCPMdo_transform6M_v_; +text: .text%__1cHCompileNreturn_values6MpnIJVMState__v_; +text: .text%__1cKInlineTreeWbuild_inline_tree_root6F_p0_; +text: .text%__1cHCompileVfinal_graph_reshaping6M_i_; +text: .text%__1cbAfinal_graph_reshaping_walk6FrnKNode_Stack_pnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cIPhaseCCPJtransform6MpnENode__2_; +text: .text%__1cHCompileLFinish_Warm6M_v_; +text: .text%__1cMPhaseIterGVN2t6Mp0_v_; +text: .text%__1cJStartNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHCompileIOptimize6M_v_; +text: .text%__1cXMachCallInterpreterNodePret_addr_offset6M_i_; +text: .text%__1cZCallInterpreterDirectNodePcompute_padding6kMi_i_; +text: .text%__1cQComputeCallStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cZCallInterpreterDirectNodeKmethod_set6Mi_v_; +text: .text%__1cTsarL_eReg_32_63NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOMachEpilogNodeQsafepoint_offset6kM_i_; +text: .text%__1cWpoll_return_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cOGenerateOopMapIcopy_cts6MpnNCellTypeState_2_i_; +text: .text%__1cQComputeCallStackJdo_double6M_v_: generateOopMap.o; +text: .text%__1cICallInfoDset6MnLKlassHandle_1nMmethodHandle_2pnGThread__v_; +text: .text%__1cMLinkResolverWresolve_interface_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cMLinkResolverXresolve_invokeinterface6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverbGruntime_resolve_interface_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cKcmpOpUOperKless_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cRaddL_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescIset_code6MpnHnmethod__v_; +text: .text%__1cJAssemblerEincl6MpnMRegisterImpl__v_; +text: .text%__1cIDivINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKTypeRawPtrFxdual6kM_pknEType__; +text: .text%__1cMTailCallNodeGOpcode6kM_i_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_iii_v_; +text: .text%__1cMstoreSSPNodeHis_Copy6kM_I_: ad_i486_misc.o; +text: .text%__1cZCallDynamicJavaDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cISubLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNobjArrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cPClassFileParserUverify_constantvalue6MiinSconstantPoolHandle_pnGThread__v_; +text: .text%__1cSInterpreterRuntimeTprepare_native_call6FpnKJavaThread_pnNmethodOopDesc__v_; +text: .text%__1cXSignatureHandlerLibraryKinitialize6F_v_; +text: .text%__1cXSignatureHandlerLibraryDadd6FnMmethodHandle__v_; +text: .text%__1cNGrowableArray4CX_Efind6kMkX_i_: interpreterRuntime.o; +text: .text%__1cNFingerprinterLfingerprint6M_X_: interpreterRuntime.o; +text: .text%__1cGEventsDlog6FpkcE_v_: exceptions.o; +text: .text%__1cIAddFNodeGOpcode6kM_i_; +text: .text%__1cRaddL_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerFffree6Mi_v_; +text: .text%__1cGThreadVset_pending_exception6MpnHoopDesc_pkci_v_; +text: .text%__1cKExceptionsRspecial_exception6FpnGThread_pkcinGHandle__i_; +text: .text%__1cKExceptionsG_throw6FpnGThread_pkcinGHandle__v_; +text: .text%__1cSstring_compareNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_StringPcreate_from_str6FpkcpnGThread__nGHandle__; +text: .text%__1cJloadDNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapIppop_any6Mi_v_; +text: .text%__1cXroundDouble_mem_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStartNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSshlL_eReg_1_31NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKPerfMemoryMmark_updated6F_v_; +text: .text%__1cKPerfMemoryFalloc6FI_pc_; +text: .text%__1cPPerfDataManagerIadd_item6FpnIPerfData_i_v_; +text: .text%__1cIPerfDataMcreate_entry6MnJBasicType_II_v_; +text: .text%__1cIPerfData2T6M_v_; +text: .text%__1cIPerfData2t6MnJCounterNS_pkcn0AFUnits_n0ALVariability__v_; +text: .text%__1cSobjArrayKlassKlassXallocate_objArray_klass6MinLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassbCallocate_objArray_klass_impl6FnYobjArrayKlassKlassHandle_inLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cFStateM_sub_Op_XorI6MpknENode__v_; +text: .text%__1cCosLelapsedTime6F_d_; +text: .text%__1cKJavaThreadZsecurity_get_caller_class6Mi_pnMklassOopDesc__; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse1.o; +text: .text%jni_GetStringUTFChars: jni.o; +text: .text%jni_ReleaseStringUTFChars; +text: .text%__1cTconvD2I_reg_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNmulI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cObox_handleNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescWcompute_has_loops_flag6M_i_; +text: .text%__1cNloadConI0NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSInterpreterRuntimeXthrow_pending_exception6FpnKJavaThread__v_; +text: .text%__1cXcmpL_reg_flags_LEGTNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOClearArrayNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%JVM_IsInterrupted; +text: .text%__1cJStoreNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cSmembar_releaseNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKExceptionsRspecial_exception6FpnGThread_pkcinMsymbolHandle_4_i_; +text: .text%__1cSInterpreterRuntimeNquicken_io_cc6FpnKJavaThread__v_; +text: .text%__1cNSignatureInfoHdo_char6M_v_: bytecode.o; +text: .text%__1cSCompiledStaticCallJfind_stub6M_pC_; +text: .text%__1cSCompiledStaticCallSset_to_interpreted6MnMmethodHandle_pC_v_; +text: .text%__1cLOptoRuntimeSnew_typeArray_Type6F_pknITypeFunc__; +text: .text%__1cNdivL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_FindLibraryEntry; +text: .text%__1cPCountedLoopNodeGstride6kM_pnENode__: loopTransform.o; +text: .text%__1cIGraphKitJnew_array6MpnENode_nJBasicType_pknEType_pknMTypeKlassPtr__2_; +text: .text%__1cSleaP_eReg_immINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cWConstantPoolCacheEntrySset_interface_call6MnMmethodHandle_i_v_; +text: .text%__1cLklassItableUcompute_itable_index6FpnNmethodOopDesc__i_; +text: .text%__1cLlog2_intptr6Fi_i_: graphKit.o; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cJloadCNodeFreloc6kM_i_; +text: .text%__1cFStateN_sub_Op_LoadC6MpknENode__v_; +text: .text%__1cLLShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadDNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cZInterpreterMacroAssemblerDpop6MnITosState__v_; +text: .text%__1cTcmovII_reg_LEGTNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQmulD_reg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cTcmovII_reg_LEGTNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cFParsebLincrement_and_test_invocation_counter6Mi_v_; +text: .text%__1cMTypeKlassPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cQsalI_eReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPconvI2F_SSFNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSshrL_eReg_1_31NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJleaP8NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cObox_handleNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNcmpL_LTGENodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbFunnecessary_membar_volatileNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNcmpL_LTGENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFParsePmerge_exception6Mi_v_; +text: .text%__1cSaddF24_reg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopJdo_unroll6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cFStateS_sub_Op_ClearArray6MpknENode__v_; +text: .text%__1cMrep_stosNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMrep_stosNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOcompI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFTypeFEmake6Ff_pk0_; +text: .text%__1cKstoreLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSInterpreterRuntimeOmultianewarray6FpnKJavaThread_pi_v_; +text: .text%__1cIDivINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPmethodDataKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cLStrCompNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cXJNI_ArgumentPusherVaArgHget_int6M_v_: jni.o; +text: .text%__1cNtestP_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectMset_override6FpnHoopDesc_C_v_; +text: .text%__1cSstring_compareNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHMatcherOc_return_value6Fii_nLRegPair__; +text: .text%__1cRandL_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIDivLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSleaP_eReg_immINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: cpCacheKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: cpCacheKlass.o; +text: .text%__1cHi2bNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHCompile2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cOPhaseIdealLoopOadd_constraint6MiipnENode_22p23_v_; +text: .text%__1cKcmpOpUOperHgreater6kM_i_: ad_i486_clone.o; +text: .text%__1cFciEnvZcall_has_multiple_targets6FpnNinstanceKlass_nMsymbolHandle_3ri_i_; +text: .text%__1cRtestI_reg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMmulD_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMNativeLookupGlookup6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cJCMoveNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%jni_SetByteArrayRegion: jni.o; +text: .text%__1cENodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cUjmpLoopEnd_shortNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cJloadFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOjmpLoopEndNodeUshort_branch_version6M_pnIMachNode__; +text: .text%__1cKarrayKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cUjmpLoopEnd_shortNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvF2D_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cUjmpLoopEnd_shortNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHi2bNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQPSIsAliveClosureLdo_object_b6MpnHoopDesc__i_: psScavenge.o; +text: .text%__1cFStateO_sub_Op_StoreL6MpknENode__v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cFParseLdo_newarray6MnJBasicType__v_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: ad_i486_misc.o; +text: .text%__1cSsafePoint_pollNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreLNodeFreloc6kM_i_; +text: .text%__1cENodeHdel_out6Mp0_v_: divnode.o; +text: .text%__1cGThreadLnmethods_do6M_v_; +text: .text%__1cSsafePoint_pollNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQorI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRsubI_eReg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSReferenceProcessorZadd_to_discovered_list_mt6MppnHoopDesc_23_v_; +text: .text%__1cPRoundDoubleNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cCosTnative_java_library6F_pv_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2ipnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cMloadConFNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cQjava_lang_StringFvalue6FpnHoopDesc__pnQtypeArrayOopDesc__; +text: .text%__1cQjava_lang_StringGoffset6FpnHoopDesc__i_; +text: .text%__1cIModLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cScompP_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQjava_lang_StringScreate_from_symbol6FnMsymbolHandle_pnGThread__nGHandle__; +text: .text%__1cWflagsReg_long_EQdDNEOperFclone6kM_pnIMachOper__; +text: .text%__1cWflagsReg_long_LEGTOperFclone6kM_pnIMachOper__; +text: .text%__1cTcmovII_reg_EQdDNENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNObjectMonitorGEnterI6MpnGThread__v_; +text: .text%__1cMNativeLookupMlookup_style6FnMmethodHandle_pcpkciiripnGThread__pC_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodKlass.o; +text: .text%__1cLServiceUtilLvisible_oop6FpnHoopDesc__i_: objectMonitor_solaris.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodKlass.o; +text: .text%__1cRmulI_eReg_immNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cCosYprint_jni_name_suffix_on6FpnMoutputStream_i_v_; +text: .text%__1cCosYprint_jni_name_prefix_on6FpnMoutputStream_i_v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_53pnGThread__v_; +text: .text%__1cRandI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cVlookup_special_native6Fpc_pC_: nativeLookup.o; +text: .text%__1cPciObjArrayKlassGloader6M_pnHoopDesc__: ciObjArrayKlass.o; +text: .text%__1cRandI_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPDictionaryEntryVadd_protection_domain6MpnHoopDesc__v_; +text: .text%__1cRmulI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTsarL_eReg_32_63NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQSystemDictionarybAvalidate_protection_domain6FnTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cKDictionaryVadd_protection_domain6MiInTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cPshrI_eReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKarrayKlassbBcomplete_create_array_klass6FnQarrayKlassHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cKarrayKlassXbase_create_array_klass6FrknKKlass_vtbl_inLKlassHandle_pnGThread__nQarrayKlassHandle__; +text: .text%__1cRxorI_eReg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIPerfLong2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability__v_; +text: .text%__1cJMarkSweepMfollow_stack6F_v_; +text: .text%__1cbBopt_virtual_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cMloadConFNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLregDPR1OperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cLregDPR1OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cMNativeLookupLlookup_base6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cNmethodOopDescKklass_name6kM_pnNsymbolOopDesc__; +text: .text%__1cJAssemblerSemit_arith_operand6MipnMRegisterImpl_nHAddress_i_v_; +text: .text%__1cISubLNodeDsub6kMpknEType_3_3_; +text: .text%__1cMNativeLookupNpure_jni_name6FnMmethodHandle__pc_; +text: .text%JVM_GetMethodIxExceptionTableEntry; +text: .text%__1cMciArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cMnegD_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMstoreSSINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSTailCalljmpIndNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRcmpOp_commuteOperFclone6kM_pnIMachOper__; +text: .text%__1cJLoadINodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cScompU_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerGfstp_d6MnHAddress__v_; +text: .text%__1cKciTypeFlowLStateVectorEtrap6MpnQciByteCodeStream_pnHciKlass_i_v_; +text: .text%__1cOmulF24_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQleaPIdxScaleNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRtestI_reg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPfieldDescriptorSlong_initial_value6kM_x_; +text: .text%__1cRsubI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cKstoreBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJLoadPNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cCosHSolarisFEventEpark6M_v_: objectMonitor_solaris.o; +text: .text%__1cKJavaThreadLnmethods_do6M_v_; +text: .text%__1cXvirtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cXvirtual_call_RelocationJpack_data6M_i_; +text: .text%__1cNcmovI_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cZCallDynamicJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZCallDynamicJavaDirectNodeSalignment_required6kM_i_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLMachUEPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNObjectMonitorREntryQdDueue_insert6MpnMObjectWaiter_i_v_; +text: .text%__1cQorI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapXreplace_all_CTS_matches6MnNCellTypeState_1_v_; +text: .text%__1cPClassFileParserbFparse_constant_pool_float_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cbFunnecessary_membar_volatileNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmINodeFreloc6kM_i_; +text: .text%__1cNobjArrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNobjArrayKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cNObjectMonitorbAEntryQdDueue_SelectSuccessor6M_pnMObjectWaiter__; +text: .text%__1cRsarI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsarI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: objArrayKlass.o; +text: .text%__1cKstoreDNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeFreloc6kM_i_; +text: .text%__1cLlog2_intptr6Fi_i_: objArrayKlassKlass.o; +text: .text%__1cNobjArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlass.o; +text: .text%jni_GetStringCritical: jni.o; +text: .text%jni_ReleaseStringCritical: jni.o; +text: .text%__1cPciObjArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cPsarI_eReg_1NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOaddF24_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvI2D_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPsarI_eReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__: callnode.o; +text: .text%__1cbACallCompiledJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cODeoptimizationYtrap_state_is_recompiled6Fi_i_; +text: .text%__1cPconvF2D_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYMachCallCompiledJavaNodePret_addr_offset6M_i_; +text: .text%__1cNdivI_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPPerfDataManagerMcounter_name6Fpkc2_pc_; +text: .text%__1cRaddL_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSInterpreterRuntimeTnmethod_entry_point6FpnKJavaThread_pnNmethodOopDesc_pnHnmethod__pC_; +text: .text%__1cRandL_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cHnmethodXinterpreter_entry_point6M_pC_; +text: .text%__1cFTypeDJsingleton6kM_i_; +text: .text%__1cHCompileSrethrow_exceptions6MpnIJVMState__v_; +text: .text%__1cLRethrowNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: frame.o; +text: .text%__1cIGraphKitbKcombine_and_pop_all_exception_states6M_pnNSafePointNode__: parse1.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cQshrL_eReg_CLNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cURethrowExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOstackSlotIOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cMmulD_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJloadDNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateP_sub_Op_Rethrow6MpknENode__v_; +text: .text%__1cOstackSlotIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLRethrowNode2t6MpnENode_22222_v_; +text: .text%__1cFTypeDFxmeet6kMpknEType__3_; +text: .text%__1cHOrINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cKloadUBNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTcmovII_reg_LEGTNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNsubI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cZget_mirror_from_signature6FnMmethodHandle_pnPSignatureStream_pnGThread__pnHoopDesc__; +text: .text%__1cNaddP_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cURethrowExceptionNodeFreloc6kM_i_; +text: .text%__1cKJavaThreadbScheck_safepoint_and_suspend_for_native_trans6Fp0_v_; +text: .text%__1cQorI_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cLRShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cHMonitor2t6Mipkci_v_; +text: .text%__1cQshrI_eReg_CLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEshll6MpnMRegisterImpl_i_v_; +text: .text%__1cKJavaThreadLgc_prologue6M_v_; +text: .text%__1cRaddL_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOMacroAssemblerKverify_oop6MpnMRegisterImpl_pkc_v_; +text: .text%__1cLloadSSDNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPconvI2F_SSFNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFParseTprofile_switch_case6Mi_v_; +text: .text%__1cKJavaThreadLgc_epilogue6M_v_; +text: .text%__1cFParseOmerge_new_path6Mi_v_; +text: .text%__1cFParseSjump_switch_ranges6MpnENode_pnLSwitchRange_4i_v_; +text: .text%__1cTCallDynamicJavaNodeSis_CallDynamicJava6kM_pk0_: callnode.o; +text: .text%__1cNxorI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%jni_NewByteArray: jni.o; +text: .text%__1cNmulL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: cfgnode.o; +text: .text%__1cMstoreSSINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConPNodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cNmethodOopDescLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cKReflectionTget_parameter_types6FnMmethodHandle_ippnHoopDesc_pnGThread__nOobjArrayHandle__; +text: .text%__1cNmethodOopDescbGresolved_checked_exceptions_impl6Fp0pnGThread__nOobjArrayHandle__; +text: .text%__1cNcmovP_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKReflectionTget_exception_types6FnMmethodHandle_pnGThread__nOobjArrayHandle__; +text: .text%__1cNmethodOopDescVparameter_annotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cNinstanceKlassQmethod_index_for6kMpnNmethodOopDesc_pnGThread__i_; +text: .text%__1cQorI_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOstackSlotDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cLloadSSDNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMstoreSSPNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cOstackSlotDOperJnum_edges6kM_I_: ad_i486.o; +text: .text%__1cMstoreSSINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFMutexbLwait_for_lock_blocking_implementation6MpnKJavaThread__v_; +text: .text%__1cHnmethodbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cRxorI_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOleaPIdxOffNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRaddL_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFframeZinterpreter_frame_set_mdx6Mi_v_; +text: .text%__1cSObjectSynchronizerHinflate6FpnHoopDesc__pnNObjectMonitor__; +text: .text%__1cPstoreImmI16NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOcmpD_cc_P6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTshrL_eReg_32_63NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRandI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cRandI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: loopnode.o; +text: .text%__1cNObjectMonitorHRecycle6M_v_; +text: .text%__1cRandI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNandL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFBlockNset_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cTcmovII_reg_LTGENodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cSleaP_eReg_immINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadSNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOtypeArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cPregister_native6FnLKlassHandle_nMsymbolHandle_1pCpnGThread__i_: jni.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorEmove6Mii_v_; +text: .text%__1cNcmovI_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENode2t6Mp0111111_v_; +text: .text%__1cFStateP_sub_Op_RShiftL6MpknENode__v_; +text: .text%__1cMloadConLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIAddDNodeGOpcode6kM_i_; +text: .text%__1cNmodL_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKarrayKlassTallocate_arrayArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cTconvD2I_reg_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: arrayKlass.o; +text: .text%__1cFParseQjump_if_fork_int6MpnENode_2nIBoolTestEmask__pnGIfNode__; +text: .text%__1cOcompP_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXis_positive_zero_double6Fd_i_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: arrayKlass.o; +text: .text%__1cNmaxI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: arrayKlass.o; +text: .text%__1cKstorePNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodDataKlass.o; +text: .text%__1cNmulI_eRegNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodDataKlass.o; +text: .text%__1cHTypeAryFxdual6kM_pknEType__; +text: .text%__1cOMethodLivenessKBasicBlockFsplit6Mi_p1_; +text: .text%__1cLloadSSFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVLoaderConstraintTableWfind_constrained_klass6MnMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cTcmovII_reg_LTGENodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cOrepush_if_args6FpnFParse_pnENode_3_v_: parse2.o; +text: .text%__1cNaddL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOstackSlotFOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cTcmovII_reg_LTGENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOstackSlotFOperFscale6kM_i_: ad_i486.o; +text: .text%__1cSshlL_eReg_1_31NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOstackSlotFOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cJAssemblerGpushad6M_v_; +text: .text%JVM_GetCallerClass; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: bytecode.o; +text: .text%__1cIMaxINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cQorI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapXdo_return_monitor_check6M_v_; +text: .text%__1cQsalL_eReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKciTypeFlowOsplit_range_at6Mi_pn0AFRange__; +text: .text%__1cPshrI_eReg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%jni_GetFieldID: jni.o; +text: .text%jni_IsAssignableFrom: jni.o; +text: .text%__1cISubLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNnegI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNnegI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cSobjArrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cFParseXfetch_interpreter_state6MipknEType_pnENode__5_; +text: .text%__1cFParseWcheck_interpreter_type6MpnENode_pknEType_rpnNSafePointNode__2_; +text: .text%__1cJAssemblerEaddl6MnHAddress_i_v_; +text: .text%__1cQmulD_reg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTsarL_eReg_32_63NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOstackSlotDOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cJAssemblerGfstp_s6MnHAddress__v_; +text: .text%__1cYinternal_word_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cXroundDouble_mem_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLloadSSDNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQsalI_eReg_CLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKCMovePNodeGOpcode6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerbAget_cache_and_index_at_bcp6MpnMRegisterImpl_2i_v_; +text: .text%__1cNmodI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRcmpOp_commuteOperFccode6kM_i_: ad_i486_clone.o; +text: .text%__1cJloadDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSCountedLoopEndNode2t6MpnENode_2ff_v_; +text: .text%__1cPCountedLoopNode2t6MpnENode_2_v_; +text: .text%__1cNcmovP_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pCi_v_; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_pkcnGHandle_6_6_; +text: .text%__1cNSCMemProjNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOmulF24_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFStateX_sub_Op_CompareAndSwapL6MpknENode__v_; +text: .text%__1cSloadL_volatileNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cNtestU_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTCompareAndSwapLNode2t6MpnENode_2222_v_; +text: .text%__1cSCompareAndSwapNode2t6MpnENode_2222_v_; +text: .text%__1cOLibraryCallKitRinline_unsafe_CAS6MnJBasicType__i_; +text: .text%__1cSloadL_volatileNodeFreloc6kM_i_; +text: .text%__1cTcompareAndSwapLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cObox_handleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cISubLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cFframeVnmethods_code_blob_do6M_v_; +text: .text%__1cYcmpL_zero_flags_LTGENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cVCallRuntimeDirectNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_ThreadJis_daemon6FpnHoopDesc__i_; +text: .text%__1cJloadDNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOPhaseIdealLoopUpeeled_dom_test_elim6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cJAssemblerDhlt6M_v_; +text: .text%__1cKstoreDNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cYcmpL_zero_flags_LEGTNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerEstop6Mpkc_v_; +text: .text%__1cTcmovII_reg_LTGENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPClassFileParserbJparse_classfile_signature_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cQshrI_eReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframeZinterpreter_frame_set_mdp6MpC_v_; +text: .text%__1cQciByteCodeStreamFtable6MnJBytecodesECode__2_; +text: .text%__1cYinternal_word_RelocationJpack_data6M_i_; +text: .text%__1cIDivLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%jni_SetBooleanField: jni.o; +text: .text%__1cSstoreD_roundedNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cTconvD2I_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreFNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: machnode.o; +text: .text%__1cHMatcherbDinterpreter_frame_pointer_reg6F_nHOptoRegEName__; +text: .text%__1cFStateT_sub_Op_ThreadLocal6MpknENode__v_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pF_vc_v_; +text: .text%__1cMtlsLoadPNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cWResolveOopMapConflictsRpossible_gc_point6MpnOBytecodeStream__i_: rewriter.o; +text: .text%__1cIciObject2t6MpnHciKlass__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_object6M_v_: interpreterRuntime.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorDbox6Mii_v_; +text: .text%__1cSsarL_eReg_1_31NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKsplit_once6FpnMPhaseIterGVN_pnENode_333_v_: cfgnode.o; +text: .text%__1cYmulI_imm_RShift_highNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLLShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPRoundDoubleNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKstoreDNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKloadUBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMURShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cPRoundDoubleNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIGraphKitPdstore_rounding6MpnENode__2_; +text: .text%__1cPClassFileParserbBcheck_illegal_static_method6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cTCallInterpreterNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cLPhaseValuesHlongcon6Mx_pnIConLNode__; +text: .text%__1cFStateX_sub_Op_CallInterpreter6MpknENode__v_; +text: .text%__1cZCallInterpreterDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZCallInterpreterDirectNodeFreloc6kM_i_; +text: .text%__1cTCallInterpreterNodeSis_CallInterpreter6kM_pk0_: classes.o; +text: .text%__1cHMatcherbAinterpreter_method_oop_reg6F_nHOptoRegEName__; +text: .text%__1cGciType2t6MpnHciKlass__v_; +text: .text%__1cHCompilebMGenerate_Compiled_To_Interpreter_Graph6MpknITypeFunc_pC_v_; +text: .text%__1cMdecI_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHMatcherXcompiler_method_oop_reg6F_nHOptoRegEName__; +text: .text%__1cXMachCallInterpreterNodeWis_MachCallInterpreter6M_p0_: ad_i486_misc.o; +text: .text%__1cHciKlass2t6MpnIciSymbol_p0_v_; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__: classes.o; +text: .text%__1cIciMethodRinterpreter_entry6M_pC_; +text: .text%__1cXjvm_define_class_common6FpnHJNIEnv__pkcpnI_jobject_pkWi53pnGThread__pnH_jclass__: jvm.o; +text: .text%__1cQSystemDictionaryTresolve_from_stream6FnMsymbolHandle_nGHandle_2pnPClassFileStream_pnGThread__pnMklassOopDesc__; +text: .text%__1cUciInstanceKlassKlassEmake6F_p0_; +text: .text%__1cIciObjectUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cLLShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSTailCalljmpIndNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOCallNativeNodeGOpcode6kM_i_; +text: .text%__1cOstackSlotIOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cIciSymbolHbyte_at6Mi_i_; +text: .text%__1cLregDPR1OperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cZCallDynamicJavaDirectNodePcompute_padding6kMi_i_; +text: .text%__1cZCallDynamicJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cSTailCalljmpIndNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%__1cIModINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSTailCalljmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQorI_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXMachCallDynamicJavaNodePret_addr_offset6M_i_; +text: .text%__1cJLoadPNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cNcmpL_EQdDNENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNcmpL_EQdDNENodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXroundDouble_mem_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapOdo_monitorexit6Mi_v_; +text: .text%__1cOGenerateOopMapLmonitor_pop6M_nNCellTypeState__; +text: .text%__1cOstoreF_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cOmulIS_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSalign_to_page_size6FI_I_: heap.o; +text: .text%__1cOaddF24_regNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cMstoreSSPNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvI2D_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcinMsymbolHandle_4nGHandle_6_v_; +text: .text%__1cNmulI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcipnNsymbolOopDesc_4_v_; +text: .text%__1cPciInstanceKlassYprotection_domain_handle6M_pnI_jobject__; +text: .text%__1cNmulI_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRandI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPciInstanceKlassNloader_handle6M_pnI_jobject__; +text: .text%__1cTcmovII_reg_EQdDNENodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cTcmovII_reg_LEGTNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJChunkPoolMfree_all_but6MI_v_: allocation.o; +text: .text%__1cRandL_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNxorI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOaddF24_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%JVM_MonitorWait; +text: .text%__1cNObjectMonitorEwait6MxipnGThread__v_; +text: .text%__1cSObjectSynchronizerEwait6FnGHandle_xpnGThread__v_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlassKlass.o; +text: .text%__1cZInterpreterMacroAssemblerYtest_method_data_pointer6MpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cMmulD_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQshrL_eReg_CLNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNdivI_eRegNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTAbstractInterpreterSBasicType_as_index6FnJBasicType__i_; +text: .text%__1cIMulDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPjava_lang_ClassQprimitive_mirror6FnJBasicType__pnHoopDesc__; +text: .text%__1cKExceptionsL_throw_args6FpnGThread_pkcinMsymbolHandle_5pnRJavaCallArguments__v_; +text: .text%__1cJMarkSweepSFollowStackClosureHdo_void6M_v_: markSweep.o; +text: .text%__1cNSingletonBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cPICStubInterfaceRcode_size_to_size6kMi_i_: icBuffer.o; +text: .text%__1cPICStubInterfaceKinitialize6MpnEStub_i_v_: icBuffer.o; +text: .text%__1cSleaP_eReg_immINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHciKlassMis_interface6M_i_: ciTypeArrayKlass.o; +text: .text%__1cNcmpL_LEGTNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateQ_sub_Op_URShiftL6MpknENode__v_; +text: .text%__1cNcmpL_LEGTNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cIModLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_i_v_; +text: .text%__1cHMatcherXinterpreter_arg_ptr_reg6F_nHOptoRegEName__; +text: .text%__1cFciEnvWis_dependence_violated6FpnMklassOopDesc_pnNmethodOopDesc__i_; +text: .text%__1cSCompiledStaticCallNcompute_entry6FnMmethodHandle_rnOStaticCallInfo__v_; +text: .text%__1cSCompiledStaticCallIis_clean6kM_i_; +text: .text%__1cJloadLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLOptoRuntimeVresolve_static_call_C6FpnKJavaThread__pC_; +text: .text%__1cJAssemblerEnegl6MpnMRegisterImpl__v_; +text: .text%__1cOmulF24_regNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cRsubL_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNloadConL0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsubL_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRmulI_imm_highNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSCompiledStaticCallDset6MrknOStaticCallInfo__v_; +text: .text%__1cFParseHdo_irem6M_v_; +text: .text%__1cHi2bNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHBoxNodeGOpcode6kM_i_; +text: .text%__1cQmulD_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXjava_lang_ref_ReferenceOset_discovered6FpnHoopDesc_2_v_; +text: .text%__1cTcmovII_reg_EQdDNENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cTcmovII_reg_EQdDNENodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cRtestI_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNtestI_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerFpopfd6M_v_; +text: .text%__1cUParallelScavengeHeapIcapacity6kM_I_; +text: .text%__1cCosTset_native_priority6FpnGThread_i_nIOSReturn__; +text: .text%__1cQset_lwp_priority6Fiii_i_; +text: .text%__1cbDreorder_based_on_method_index6FpnPobjArrayOopDesc_1ppnHoopDesc__v_: methodOop.o; +text: .text%__1cNmaxI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMLinkResolverbPlinktime_resolve_interface_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongVariable__; +text: .text%__1cPconvF2D_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNIdealLoopTreeXpolicy_maximally_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cbCAbstractInterpreterGeneratorVset_vtos_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cKciTypeFlowLStateVectorLdo_newarray6MpnQciByteCodeStream__v_; +text: .text%__1cTmembar_volatileNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cNxorI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cPconvI2F_SSFNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMjniIdSupportNto_jmethod_id6FpnNmethodOopDesc__pnK_jmethodID__; +text: .text%__1cMjniIdPrivateGid_for6FnTinstanceKlassHandle_i_i_: jniId.o; +text: .text%__1cNget_method_id6FpnHJNIEnv__pnH_jclass_pkc5ipnGThread__pnK_jmethodID__: jni.o; +text: .text%__1cICmpFNodeGOpcode6kM_i_; +text: .text%__1cNmethodOopDescKjmethod_id6M_pnK_jmethodID__; +text: .text%__1cRmulI_imm_highNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSstring_compareNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIAddLNodeIadd_ring6kMpknEType_3_3_; +text: .text%jni_NewStringUTF: jni.o; +text: .text%__1cQorI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVloadConL_low_onlyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIUniverseWreinitialize_vtable_of6FpnFKlass_pnGThread__v_; +text: .text%__1cPshlI_eReg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%JVM_SetClassSigners; +text: .text%JVM_DefineClassWithSource; +text: .text%__1cJAssemblerGpushfd6M_v_; +text: .text%__1cGEventsDlog6FpkcE_v_: sharedRuntime.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cNloadConL0NodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cNmulI_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulINodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIMulINodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cNmulI_eRegNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cHAddress2t6MinJrelocInfoJrelocType__v_; +text: .text%__1cTsarL_eReg_32_63NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cTconvF2I_reg_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceRefKlass.o; +text: .text%__1cNsubL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVCallRuntimeDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRaddI_mem_eRegNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cODeoptimizationVtrap_state_has_reason6Fii_i_; +text: .text%__1cNaddL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cVCallRuntimeDirectNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRmulI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLloadSSDNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cS__ieee754_rem_pio26Fdpd_i_: sharedRuntimeTrig.o; +text: .text%__1cQshrL_eReg_CLNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cObox_handleNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cTshlL_eReg_32_63NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cTmembar_volatileNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRtestI_reg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cSThreadLocalStorageNpd_set_thread6FpnGThread__v_; +text: .text%__1cMstoreSSINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSThreadLocalStorageKset_thread6FpnGThread__v_; +text: .text%__1cSThreadLocalStoragebBget_thread_via_cache_slowly6FIi_pnGThread__; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: connode.o; +text: .text%__1cCosbBthread_local_storage_at_put6Fipv_v_; +text: .text%__1cLConvF2INodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: connode.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: cfgnode.o; +text: .text%get_thread; +text: .text%__1cSThreadLocalStorageSset_thread_in_slot6FpnGThread__v_; +text: .text%jni_CallIntMethod: jni.o; +text: .text%__1cPconvF2D_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOjmpLoopEndNodeGnegate6M_v_: ad_i486_misc.o; +text: .text%__1cHCompileRmake_vm_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: node.o; +text: .text%__1cJCMoveNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRsubI_eReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLloadSSFNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: node.o; +text: .text%__1cMnegD_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHnmethodPis_dependent_on6MpnMklassOopDesc__i_; +text: .text%__1cNSharedRuntimeSfind_callee_method6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: node.o; +text: .text%__1cSsarL_eReg_1_31NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cTshrL_eReg_32_63NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPhaseIdealLoopOdo_range_check6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cNRelocIteratorEnext6M_i_: sharedRuntime.o; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_deopt_entry_for6MnITosState_i_pC_; +text: .text%__1cLOptoRuntimeThandle_wrong_method6FpnKJavaThread__pC_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_return_entry_for6MnITosState_i_pC_; +text: .text%__1cTmembar_volatileNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNSharedRuntimeTreresolve_call_site6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cKstoreFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUPSMarkSweepDecoratorHcompact6Mi_v_; +text: .text%__1cUPSMarkSweepDecoratorPadjust_pointers6M_v_; +text: .text%__1cTcmovII_reg_EQdDNENodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPconvI2F_SSFNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cUPSMarkSweepDecoratorVdestination_decorator6F_p0_; +text: .text%__1cRInlineCacheBufferRic_stub_code_size6F_i_; +text: .text%__1cUPSMarkSweepDecoratorKprecompact6M_v_; +text: .text%__1cQshrI_eReg_CLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadDNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cICmpDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOaddF24_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cIDivDNodeGOpcode6kM_i_; +text: .text%__1cOmulF24_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cOaddF24_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOmulF24_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHCompileQgrow_alias_types6M_v_; +text: .text%__1cFParseScreate_jump_tables6MpnENode_pnLSwitchRange_4_i_; +text: .text%__1cQjava_lang_ThreadMis_stillborn6FpnHoopDesc__i_; +text: .text%JVM_GetClassCPTypes; +text: .text%__1cUverify_byte_codes_fn6F_pv_: verifier.o; +text: .text%JVM_GetClassMethodsCount; +text: .text%JVM_GetClassCPEntriesCount; +text: .text%__1cOstoreF_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%JVM_GetClassFieldsCount; +text: .text%__1cQshrL_eReg_CLNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKstoreBNodeFreloc6kM_i_; +text: .text%__1cPconvI2F_SSFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_ThreadKset_thread6FpnHoopDesc_pnKJavaThread__v_; +text: .text%__1cMdecI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGEventsDlog6FpkcE_v_: thread.o; +text: .text%__1cPconvI2L_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOMacroAssemblerKverify_FPU6Mipkc_v_; +text: .text%__1cJLoadFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMloadConDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstoreBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddD_reg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerGmembar6M_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIgenerate6MX_v_; +text: .text%__1cXSignatureHandlerLibraryLset_handler6FpnKCodeBuffer__pC_; +text: .text%__1cNcmovP_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_5pnGThread__v_; +text: .text%__1cXSignatureHandlerLibraryOpd_set_handler6FpC_v_; +text: .text%__1cFJNIidEfind6Mi_p0_; +text: .text%__1cbBinitialize_itable_for_klass6FpnMklassOopDesc__v_; +text: .text%__1cJCmpD3NodeGOpcode6kM_i_; +text: .text%__1cXconvI2L_reg_reg_zexNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbFunnecessary_membar_volatileNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%JVM_IsPrimitiveClass; +text: .text%__1cIMinINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRsubL_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cNSharedRuntimeDd2l6Fd_x_; +text: .text%__1cRsubL_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPciObjArrayKlassJmake_impl6FpnHciKlass__p0_; +text: .text%__1cRsubL_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%jni_FindClass: jni.o; +text: .text%__1cPmovI_nocopyNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cbDjava_lang_reflect_ConstructorThas_signature_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorVhas_annotations_field6F_i_; +text: .text%__1cHCompilebMGenerate_Interpreter_To_Compiled_Graph6MpknITypeFunc__v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorbFhas_parameter_annotations_field6F_i_; +text: .text%__1cFStateM_sub_Op_RegD6MpknENode__v_; +text: .text%__1cbDjava_lang_reflect_ConstructorZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cUCallCompiledJavaNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cGThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cGThreadVis_jvmti_agent_thread6kM_i_: thread.o; +text: .text%__1cbACallCompiledJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cbACallCompiledJavaDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKCompiledICMset_to_clean6M_v_; +text: .text%__1cTcmovII_reg_LEGTNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerEcmpl6MpnMRegisterImpl_2_v_; +text: .text%__1cJloadFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKReflectionPnew_constructor6FnMmethodHandle_pnGThread__pnHoopDesc__; +text: .text%__1cbDjava_lang_reflect_ConstructorGcreate6FpnGThread__nGHandle__; +text: .text%__1cFParseSjump_if_false_fork6MpnGIfNode_ii_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cbACallCompiledJavaDirectNodeFreloc6kM_i_; +text: .text%__1cUPipeline_Use_Element2t6MIIIinXPipeline_Use_Cycle_Mask__v_: ad_i486_pipeline.o; +text: .text%__1cbDjava_lang_reflect_ConstructorNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cFStateY_sub_Op_CallCompiledJava6MpknENode__v_; +text: .text%__1cXPipeline_Use_Cycle_Mask2t6MI_v_: ad_i486_pipeline.o; +text: .text%__1cMsubD_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIDivLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSleaP_eReg_immINodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cKstoreFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTshlL_eReg_32_63NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRxorI_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_2_v_; +text: .text%__1cNcmovI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRandL_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSstoreD_roundedNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSstoreD_roundedNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateM_sub_Op_MinI6MpknENode__v_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cNminI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFMutex2T6M_v_; +text: .text%lwp_mutex_destroy: os_solaris.o; +text: .text%__1cQmulD_reg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cHi2bNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHMonitor2T6M_v_; +text: .text%__1cIimmIOperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cIMinINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cIAddFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%lwp_cond_destroy: os_solaris.o; +text: .text%__1cLConvD2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMmulD_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMstoreSSPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cISubLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cINodeHashEgrow6M_v_; +text: .text%__1cMnegD_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_ThreadLthreadGroup6FpnHoopDesc__2_; +text: .text%__1cIMulINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cICodeBlobOis_java_method6kM_i_: codeBlob.o; +text: .text%__1cLConvI2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOstackSlotPOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cJCMoveNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKLoadPCNodeGOpcode6kM_i_; +text: .text%__1cRaddI_mem_eRegNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKCMoveINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRcmpOp_commuteOperHgreater6kM_i_: ad_i486_clone.o; +text: .text%__1cNObjectMonitorGenter26MpnGThread__v_; +text: .text%__1cNmodL_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRcmpOp_commuteOperGnegate6M_v_: ad_i486_clone.o; +text: .text%__1cPciObjArrayKlassEmake6FpnHciKlass__p0_; +text: .text%__1cQaddD_reg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFTypeFFxmeet6kMpknEType__3_; +text: .text%__1cYcmpL_zero_flags_LTGENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRmulI_imm_highNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJCMoveNode2t6MpnENode_22pknEType__v_: connode.o; +text: .text%__1cJCMoveNodeEmake6FpnENode_222pknEType__p0_; +text: .text%__1cOPhaseIdealLoopVinsert_pre_post_loops6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cLloadSSDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKCodeBufferWinsert_double_constant6Md_pC_; +text: .text%__1cXroundDouble_mem_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_regNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cJCodeCacheFfirst6F_pnICodeBlob__; +text: .text%__1cICodeBlobJis_zombie6kM_i_: onStackReplacement.o; +text: .text%__1cWis_positive_one_double6Fd_i_; +text: .text%__1cNaddP_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cICodeHeapLfirst_block6kM_pnJHeapBlock__; +text: .text%__1cXcmpL_reg_flags_LEGTNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPciObjectFactoryPinsert_non_perm6Mrpn0ANNonPermObject_pnHoopDesc_pnIciObject__v_; +text: .text%__1cFframeVshould_be_deoptimized6kM_i_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlass.o; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_: callnode.o; +text: .text%__1cMincI_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlass.o; +text: .text%__1cSleaP_eReg_immINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJEventMark2t6MpkcE_v_: psMarkSweep.o; +text: .text%__1cKloadUBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJAssemblerDjmp6MpCnJrelocInfoJrelocType__v_; +text: .text%__1cISubDNodeGOpcode6kM_i_; +text: .text%__1cNObjectMonitor2t6M_v_; +text: .text%__1cTconvD2I_reg_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cSObjectSynchronizerGnotify6FnGHandle_pnGThread__v_; +text: .text%__1cOcmpD_cc_P6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvD2FNodeGOpcode6kM_i_; +text: .text%__1cTmembar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSshrL_eReg_1_31NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%JVM_MonitorNotify; +text: .text%__1cHMatcherXpost_store_load_barrier6FpknENode__i_; +text: .text%__1cFParseNdo_instanceof6M_v_; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_: interpreterRuntime.o; +text: .text%__1cSsarL_eReg_1_31NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEshrl6MpnMRegisterImpl_i_v_; +text: .text%__1cSmulF24_reg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIGraphKitOgen_instanceof6MpnENode_2_2_; +text: .text%__1cYjava_lang_reflect_MethodPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cLTypeInstPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cYjava_lang_reflect_MethodbFhas_parameter_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodbChas_annotation_default_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodVhas_annotations_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodThas_signature_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodPset_return_type6FpnHoopDesc_2_v_; +text: .text%__1cNCallGeneratorSfor_predicted_call6FpnHciKlass_p03_3_; +text: .text%__1cYjava_lang_reflect_MethodIset_name6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodWset_annotation_default6FpnHoopDesc_2_v_; +text: .text%__1cZCallDynamicJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFStateN_sub_Op_LoadF6MpknENode__v_; +text: .text%__1cNandI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKReflectionKnew_method6FnMmethodHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cTconvD2I_reg_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cWPredictedCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cWPredictedCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cWPredictedCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cNmethodOopDescSannotation_default6kM_pnQtypeArrayOopDesc__; +text: .text%__1cYjava_lang_reflect_MethodGcreate6FpnGThread__nGHandle__; +text: .text%__1cYjava_lang_reflect_MethodJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cMstoreSSPNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%jni_GetMethodID: jni.o; +text: .text%__1cOstackSlotPOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cOGenerateOopMapKpp_new_ref6MpnNCellTypeState_i_v_; +text: .text%__1cRmulI_imm_highNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFParseTjump_if_always_fork6Mii_v_; +text: .text%__1cMnegD_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFStateW_sub_Op_MemBarVolatile6MpknENode__v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: typeArrayKlass.o; +text: .text%__1cNObjectMonitorGnotify6MpnGThread__v_; +text: .text%__1cHRegMask2t6M_v_: matcher.o; +text: .text%__1cJLoadSNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cPconvI2D_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_GetClassDeclaredConstructors; +text: .text%__1cINegDNodeGOpcode6kM_i_; +text: .text%__1cIciObjectOis_method_data6M_i_: ciObjectFactory.o; +text: .text%__1cNCallGeneratorRfor_uncommon_trap6FpnIciMethod_nODeoptimizationLDeoptReason_n0CLDeoptAction__p0_; +text: .text%__1cIciObjectJis_method6M_i_: ciObjectFactory.o; +text: .text%__1cNmodI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateP_sub_Op_LShiftL6MpknENode__v_; +text: .text%__1cNmodI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLConvI2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: callGenerator.o; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceRefKlass.o; +text: .text%__1cKcmpOpUOperFequal6kM_i_: ad_i486_clone.o; +text: .text%__1cZUncommonTrapCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciObjectFactory.o; +text: .text%__1cFStateM_sub_Op_ModI6MpknENode__v_; +text: .text%__1cMloadConDNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cPmovI_nocopyNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIci2bNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMVirtualSpaceQuncommitted_size6kM_I_; +text: .text%__1cMVirtualSpaceJexpand_by6MI_i_; +text: .text%__1cJAssemblerFfld_d6MnHAddress__v_; +text: .text%__1cJloadFNodeFreloc6kM_i_; +text: .text%__1cOaddF24_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMincI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cVCallRuntimeDirectNodeKmethod_set6Mi_v_; +text: .text%__1cRaddI_eReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOGenerateOopMapKinit_state6M_v_; +text: .text%__1cTcmovII_reg_LTGENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNSafepointBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cOGenerateOopMapTmark_reachable_code6M_v_; +text: .text%__1cOGenerateOopMapPinitialize_vars6M_v_; +text: .text%__1cSTailCalljmpIndNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNinstanceKlassSregister_finalizer6FpnPinstanceOopDesc_pnGThread__2_; +text: .text%__1cOGenerateOopMapRinit_basic_blocks6M_v_; +text: .text%__1cOGenerateOopMapYsetup_method_entry_state6M_v_; +text: .text%__1cMstoreSSPNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapYrewrite_refval_conflicts6M_v_; +text: .text%__1cOGenerateOopMapbAmake_context_uninitialized6M_v_; +text: .text%__1cOmulF24_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapTmethodsig_to_effect6MpnNsymbolOopDesc_ipnNCellTypeState__i_; +text: .text%__1cQsalI_eReg_CLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNdivI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMapKinterp_all6M_v_; +text: .text%__1cNandI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPClassFileParserXverify_unqualified_name6MpcIi_i_; +text: .text%__1cUGenericGrowableArrayKraw_remove6MpknEGrET__v_; +text: .text%__1cOcmovI_regUNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMmulD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_SystemSin_offset_in_bytes6F_i_; +text: .text%__1cJAssemblerEaddl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cFTypeFJsingleton6kM_i_; +text: .text%__1cRandL_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_SystemTout_offset_in_bytes6F_i_; +text: .text%__1cIBytecodeIset_code6MnJBytecodesECode__v_; +text: .text%__1cPconvI2D_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: memnode.o; +text: .text%__1cNmulI_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIRetTableRcompute_ret_table6MnMmethodHandle__v_; +text: .text%__1cQsalL_eReg_CLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOGenerateOopMapLcompute_map6MpnGThread__v_; +text: .text%__1cNdivL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMaxINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cOGenerateOopMap2t6MnMmethodHandle__v_; +text: .text%__1cRsubL_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvF2D_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegD_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTAbstractInterpreterLdeopt_entry6FnITosState_i_pC_; +text: .text%__1cOGenerateOopMapRdo_interpretation6M_v_; +text: .text%__1cQshrL_eReg_CLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMapbImark_bbheaders_and_count_gc_points6M_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIpass_int6M_v_: interpreterRuntime.o; +text: .text%__1cOGenerateOopMapNinitialize_bb6M_v_; +text: .text%__1cNcmovP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQorI_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceRefKlass.o; +text: .text%__1cMdecI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSstring_compareNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: markSweep.o; +text: .text%__1cRInterpreterOopMapIis_empty6M_i_; +text: .text%__1cJMarkSweepSMarkAndPushClosureLdo_nmethods6kM_ki_: markSweep.o; +text: .text%__1cTsarL_eReg_32_63NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNcmovP_regNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cQjava_lang_ThreadMset_priority6FpnHoopDesc_nOThreadPriority__v_; +text: .text%__1cIMulDNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cOmulF24_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMsubD_regNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cOGenerateOopMapPdo_monitorenter6Mi_v_; +text: .text%__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cMsubD_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOjmpLoopEndNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cJAssemblerFbswap6MpnMRegisterImpl__v_; +text: .text%__1cTconvF2I_reg_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMstoreSSPNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cOjmpLoopEndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOGenerateOopMapMmonitor_push6MnNCellTypeState__v_; +text: .text%__1cOjmpLoopEndNodeJis_Branch6kM_I_: ad_i486_misc.o; +text: .text%__1cbFunnecessary_membar_volatileNodeLbottom_type6kM_pknEType__: ad_i486_misc.o; +text: .text%__1cIGraphKitXinsert_mem_bar_volatile6MpnKMemBarNode_i_v_; +text: .text%__1cRsubI_eReg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_ThreadIis_alive6FpnHoopDesc__i_; +text: .text%__1cFStateX_sub_Op_CallDynamicJava6MpknENode__v_; +text: .text%JVM_IsThreadAlive; +text: .text%__1cQMachCallJavaNodeVis_MachCallStaticJava6M_pnWMachCallStaticJavaNode__: ad_i486_misc.o; +text: .text%__1cZCallDynamicJavaDirectNodeFreloc6kM_i_; +text: .text%__1cTcmovII_reg_LTGENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYcmpL_zero_flags_LTGENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEdecl6MpnMRegisterImpl__v_; +text: .text%__1cVLoaderConstraintTableYextend_loader_constraint6MpnVLoaderConstraintEntry_nGHandle_pnMklassOopDesc__v_; +text: .text%__1cUVirtualCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cVcompiledICHolderKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cNCallGeneratorQfor_virtual_call6FpnIciMethod__p0_; +text: .text%__1cVLoaderConstraintTablebHensure_loader_constraint_capacity6MpnVLoaderConstraintEntry_i_v_; +text: .text%__1cIModLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQOopMapCacheEntryFflush6M_v_; +text: .text%__1cQOopMapCacheEntryEfill6MnMmethodHandle_i_v_; +text: .text%__1cQOopMapCacheEntryTdeallocate_bit_mask6M_v_; +text: .text%__1cQOopMapCacheEntryRallocate_bit_mask6M_v_; +text: .text%__1cOPSPromotionLABRunallocate_object6MpnHoopDesc__i_; +text: .text%__1cICodeHeapTmark_segmap_as_free6MII_v_; +text: .text%__1cNinstanceKlassYremove_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cNsubI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICodeHeapJexpand_by6MI_i_; +text: .text%__1cTcmovII_reg_LEGTNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cJAssemblerFfld_s6MnHAddress__v_; +text: .text%__1cOstoreF_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cVscale_to_lwp_priority6Fiii_i_: os_solaris.o; +text: .text%__1cMdivD_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddI_mem_eRegNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMdivD_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNxorI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQmulI_mem_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQmulI_mem_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQorI_eReg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cMsubD_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cUVirtualCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cPconvI2F_SSFNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRcmpOp_commuteOperKless_equal6kM_i_: ad_i486_clone.o; +text: .text%__1cIci2bNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJMemRegionMintersection6kMk0_0_; +text: .text%__1cIDivLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMincI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJStubQdDueueRrequest_committed6Mi_pnEStub__; +text: .text%__1cQmulD_reg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_ConD6MpknENode__v_; +text: .text%__1cSaddD_reg_roundNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNmodL_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNRelocIteratorEnext6M_i_: output.o; +text: .text%__1cYinternal_word_RelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cNaddI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosHSolarisKvm_signals6F_pnIsigset_t__; +text: .text%__1cODataRelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cODataRelocationJset_value6MpC_v_: relocInfo.o; +text: .text%__1cPICStubInterfaceEsize6kMpnEStub__i_: icBuffer.o; +text: .text%__1cFStateO_sub_Op_CMoveI6MpknENode__v_; +text: .text%__1cPICStubInterfaceIfinalize6MpnEStub__v_: icBuffer.o; +text: .text%__1cSdivD_reg_roundNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cTOopMapForCacheEntry2t6MnMmethodHandle_ipnQOopMapCacheEntry__v_; +text: .text%__1cCosHSolarisPhotspot_sigmask6FpnGThread__v_; +text: .text%__1cGICStubIfinalize6M_v_; +text: .text%__1cCosScurrent_stack_base6F_pC_; +text: .text%__1cOGenerateOopMapVresult_for_basicblock6Mi_v_; +text: .text%__1cGThread2t6M_v_; +text: .text%__1cCosScurrent_stack_size6F_I_; +text: .text%__1cCosHSolarisVinit_thread_fpu_state6F_v_; +text: .text%__1cJAssemblerExorl6MpnMRegisterImpl_2_v_; +text: .text%__1cCosHSolarisRunblocked_signals6F_pnIsigset_t__; +text: .text%__1cJStubQdDueueMremove_first6M_v_; +text: .text%__1cGThreadbArecord_stack_base_and_size6M_v_; +text: .text%__1cLMoveL2DNodeGOpcode6kM_i_; +text: .text%__1cIOSThreadNpd_initialize6M_v_; +text: .text%__1cQOopMapCacheEntryIset_mask6MpnNCellTypeState_2i_v_; +text: .text%__1cCosScurrent_process_id6F_i_; +text: .text%__1cIOSThread2t6MpFpv_i1_v_; +text: .text%__1cKRelocationRpd_set_data_value6MpCi_v_; +text: .text%__1cTshlL_eReg_32_63NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cKConv2BNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cGThreadbFinitialize_thread_local_storage6M_v_; +text: .text%__1cTOopMapForCacheEntryOreport_results6kM_i_: oopMapCache.o; +text: .text%__1cCosRinitialize_thread6F_v_; +text: .text%__1cFTypeDGis_nan6kM_i_; +text: .text%__1cTOopMapForCacheEntryLcompute_map6MpnGThread__v_; +text: .text%__1cCosMstart_thread6FpnGThread__v_; +text: .text%_start: os_solaris.o; +text: .text%__1cYcmpL_zero_flags_LTGENodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_SubL6MpknENode__v_; +text: .text%__1cLStrCompNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%JVM_SetThreadPriority; +text: .text%__1cNobjArrayKlassKinitialize6MpnGThread__v_; +text: .text%jni_NewObjectArray: jni.o; +text: .text%__1cKCompiledICKcached_oop6kM_pnHoopDesc__; +text: .text%__1cTcmovII_reg_EQdDNENodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_MulI6MpknENode__v_; +text: .text%__1cMPipeline_Use2t6MIIIpnUPipeline_Use_Element__v_: ad_i486_pipeline.o; +text: .text%__1cCosPpd_start_thread6FpnGThread__v_; +text: .text%__1cNsubL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cCosNcreate_thread6FpnGThread_n0AKThreadType_I_i_; +text: .text%__1cPRoundDoubleNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cXjava_lang_reflect_FieldIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cFParseScan_rerun_bytecode6M_i_; +text: .text%__1cXjava_lang_reflect_FieldThas_signature_field6F_i_; +text: .text%__1cKReflectionInew_type6FnMsymbolHandle_nLKlassHandle_pnGThread__nGHandle__; +text: .text%__1cDhpiEread6FipvI_I_: jvm.o; +text: .text%__1cXjava_lang_reflect_FieldPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cXjava_lang_reflect_FieldVhas_annotations_field6F_i_; +text: .text%__1cMstoreSSINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJvmSymbolsOsignature_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cXjava_lang_reflect_FieldJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cSmembar_releaseNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cXjava_lang_reflect_FieldGcreate6FpnGThread__nGHandle__; +text: .text%__1cPfieldDescriptorLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cXjava_lang_reflect_FieldIset_name6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_type6FpnHoopDesc_2_v_; +text: .text%__1cKReflectionJnew_field6FpnPfieldDescriptor_ipnGThread__pnHoopDesc__; +text: .text%__1cRtestI_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPhaseIdealLoopKdo_peeling6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%JVM_GetStackAccessControlContext; +text: .text%__1cIPipeline2t6MIIiIIiiiikpnSmachPipelineStages_3kpInMPipeline_Use__v_: ad_i486_pipeline.o; +text: .text%JVM_Read; +text: .text%__1cOJavaAssertionsNmatch_package6Fpkc_pn0AKOptionList__; +text: .text%__1cHciKlassOsuper_of_depth6MI_p0_; +text: .text%JVM_DesiredAssertionStatus; +text: .text%__1cWThreadLocalAllocBufferKinitialize6M_v_; +text: .text%__1cOcmpF_cc_P6NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOJavaAssertionsHenabled6Fpkci_i_; +text: .text%__1cOcmpF_cc_P6NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeFreloc6kM_i_; +text: .text%__1cWThreadLocalAllocBufferMinitial_size6F_I_; +text: .text%__1cQaddD_reg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%jni_GetStaticFieldID: jni.o; +text: .text%__1cOJavaAssertionsLmatch_class6Fpkc_pn0AKOptionList__: javaAssertions.o; +text: .text%__1cNinstanceKlassKjni_id_for6Mi_pnFJNIid__; +text: .text%__1cENodeEgetf6kM_f_; +text: .text%__1cLConvL2FNodeGOpcode6kM_i_; +text: .text%__1cMFlatProfilerJis_active6F_i_; +text: .text%__1cNThreadServiceKadd_thread6FpnKJavaThread_i_v_; +text: .text%__1cIjniIdMapGcreate6FnTinstanceKlassHandle__p0_; +text: .text%__1cQorI_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cHThreadsDadd6FpnKJavaThread_i_v_; +text: .text%__1cKJavaThreadKinitialize6M_v_; +text: .text%__1cLConvL2DNodeGOpcode6kM_i_; +text: .text%__1cNloadConL0NodeFclone6kM_pnENode__; +text: .text%__1cLConvF2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cGParker2t6M_v_; +text: .text%__1cQThreadStatistics2t6M_v_; +text: .text%__1cIAddDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJimmL0OperFclone6kM_pnIMachOper__; +text: .text%__1cENodeGis_Con6kM_I_: loopnode.o; +text: .text%__1cNjni_functions6F_pknTJNINativeInterface___; +text: .text%__1cLConvF2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cIjniIdMap2t6MpnMklassOopDesc_i_v_; +text: .text%__1cIjniIdMapRcompute_index_cnt6FnTinstanceKlassHandle__i_; +text: .text%__1cLjniIdBucket2t6MpnIjniIdMap_p0_v_; +text: .text%__1cUThreadSafepointStateGcreate6FpnKJavaThread__v_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: loopnode.o; +text: .text%__1cUThreadSafepointState2t6MpnKJavaThread__v_; +text: .text%__1cIGraphKitSprecision_rounding6MpnENode__2_; +text: .text%__1cCosMguard_memory6FpcI_i_; +text: .text%__1cKJavaThreadYcreate_stack_guard_pages6M_v_; +text: .text%__1cNaddL_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cOaddF24_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKJavaThread2t6MpFp0pnGThread__vI_v_; +text: .text%__1cSmulF24_reg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQorl_eReg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cPjava_lang_ClassOprimitive_type6FpnHoopDesc__nJBasicType__; +text: .text%jni_GetFloatArrayRegion: jni.o; +text: .text%__1cKJavaThreadDrun6M_v_; +text: .text%__1cKJavaThreadRthread_main_inner6M_v_; +text: .text%__1cQSystemDictionaryQjava_mirror_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cKReflectionbFbasic_type_mirror_to_basic_type6FpnHoopDesc_pnGThread__nJBasicType__; +text: .text%__1cM__kernel_cos6Fdd_d_: sharedRuntimeTrig.o; +text: .text%__1cJAssemblerFcmovl6Mn0AJCondition_pnMRegisterImpl_3_v_; +text: .text%__1cM__kernel_sin6Fddi_d_: sharedRuntimeTrig.o; +text: .text%__1cSleaP_eReg_immINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPhaseIdealLoopJclone_iff6MpnHPhiNode_pnNIdealLoopTree__pnIBoolNode__; +text: .text%__1cGThreadFstart6Fp0_v_; +text: .text%__1cNSharedRuntimeEdsin6Fd_d_; +text: .text%__1cNSharedRuntimeEdcos6Fd_d_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: multnode.o; +text: .text%jni_CallStaticVoidMethod: jni.o; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnPPerfLongCounter__; +text: .text%__1cSshlL_eReg_1_31NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNsymbolOopDescWas_klass_external_name6kM_pkc_; +text: .text%__1cNdecI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTconvF2I_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNcmovP_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEsbbl6MnHAddress_i_v_; +text: .text%__1cFKlassNexternal_name6kM_pkc_; +text: .text%__1cHRegMask2t6Miiiii_v_: ad_i486_expand.o; +text: .text%__1cLConvD2INodeJideal_reg6kM_I_: classes.o; +text: .text%JVM_IsArrayClass; +text: .text%__1cVloadConL_low_onlyNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cHTypePtrFxdual6kM_pknEType__; +text: .text%__1cSMachBreakpointNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlass.o; +text: .text%__1cICmpDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cVMoveL2D_reg_stackNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQshrI_eReg_CLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRmulI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: output.o; +text: .text%JVM_GetClassName; +text: .text%__1cSsarL_eReg_1_31NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLStringTableGintern6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cQorl_eReg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2D_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cCosMset_priority6FpnGThread_nOThreadPriority__nIOSReturn__; +text: .text%__1cFJNIid2t6MpnMklassOopDesc_ip0_v_; +text: .text%__1cENodeHdel_out6Mp0_v_: mulnode.o; +text: .text%__1cSstring_compareNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateM_sub_Op_RegF6MpknENode__v_; +text: .text%__1cPmovI_nocopyNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulFNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cNinstanceKlassPjni_id_for_impl6FnTinstanceKlassHandle_i_pnFJNIid__; +text: .text%__1cOcmpF_cc_P6NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKRelocationYpd_get_address_from_code6M_pC_; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_: interpreterRuntime.o; +text: .text%__1cSmulF24_reg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cbFunnecessary_membar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmulF24_reg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNtestU_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cbFunnecessary_membar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGThreadMset_priority6Fp0nOThreadPriority__v_; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerDjmp6MpnMRegisterImpl_nJrelocInfoJrelocType__v_; +text: .text%__1cJArrayDataKcell_count6M_i_: ciMethodData.o; +text: .text%JVM_Open; +text: .text%__1cOmulIS_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQjava_lang_ThreadIpriority6FpnHoopDesc__nOThreadPriority__; +text: .text%__1cQjava_lang_ThreadJstackSize6FpnHoopDesc__x_; +text: .text%__1cYcmpL_zero_flags_EQdDNENodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNObjectMonitorJnotifyAll6MpnGThread__v_; +text: .text%__1cNPerfByteArray2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_i_v_; +text: .text%JVM_StartThread; +text: .text%__1cQComputeCallStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cMthread_entry6FpnKJavaThread_pnGThread__v_: jvm.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorJpass_long6M_v_: interpreterRuntime.o; +text: .text%__1cOMacroAssemblerKnull_check6MpnMRegisterImpl_i_v_; +text: .text%__1cGICStubLdestination6kM_pC_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cYcmpL_zero_flags_LEGTNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLConvD2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRxorI_eReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRInlineCacheBufferVic_buffer_entry_point6FpC_1_; +text: .text%__1cKJavaThreadHprepare6MpnI_jobject_nOThreadPriority__v_; +text: .text%jni_GetStaticObjectField: jni.o; +text: .text%__1cQmulI_mem_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cScompI_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOstackSlotDOperFscale6kM_i_: ad_i486.o; +text: .text%__1cOstackSlotDOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cMmulD_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOstackSlotDOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cXPartialSubtypeCheckNodeGOpcode6kM_i_; +text: .text%__1cQmulD_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOstackSlotPOperFscale6kM_i_: ad_i486.o; +text: .text%__1cOstackSlotPOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOstackSlotPOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOGenerateOopMapMdo_checkcast6M_v_; +text: .text%__1cQmulI_mem_immNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPconvI2D_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRandL_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTAbstractInterpreterWlayout_activation_impl6FpnNmethodOopDesc_iiiipnFframe_4i_i_; +text: .text%JVM_TotalMemory; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: generateOptoStub.o; +text: .text%JVM_FreeMemory; +text: .text%__1cObox_handleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMaxINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cTshlL_eReg_32_63NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQmulI_mem_immNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cQmulI_mem_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvI2F_SSFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJScopeDescTdecode_scope_values6Mi_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cUThreadSafepointStateHdestroy6FpnKJavaThread__v_; +text: .text%__1cKJavaThread2T6M_v_; +text: .text%__1cKJavaThreadYremove_stack_guard_pages6M_v_; +text: .text%__1cIOSThreadKpd_destroy6M_v_; +text: .text%__1cIOSThread2T6M_v_; +text: .text%__1cCosLfree_thread6FpnIOSThread__v_; +text: .text%__1cCosOunguard_memory6FpcI_i_; +text: .text%__1cHRetDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cGParker2T6M_v_; +text: .text%__1cQorI_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRmulI_imm_highNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsGremove6FpnKJavaThread__v_; +text: .text%__1cKJavaThreadEexit6Mi_v_; +text: .text%__1cKloadUBNodeFreloc6kM_i_; +text: .text%__1cLensure_join6FpnKJavaThread__v_: thread.o; +text: .text%__1cGThread2T5B6M_v_; +text: .text%__1cNThreadServiceWcurrent_thread_exiting6FpnKJavaThread__v_; +text: .text%__1cNThreadServiceNremove_thread6FpnKJavaThread_i_v_; +text: .text%__SLIP.DELETER__A: thread.o; +text: .text%__1cUThreadSafepointState2T6M_v_; +text: .text%__1cFStateT_sub_Op_RoundDouble6MpknENode__v_; +text: .text%__1cQjava_lang_ThreadNset_stillborn6FpnHoopDesc__v_; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: klassKlass.o; +text: .text%__1cIGraphKitTdprecision_rounding6MpnENode__2_; +text: .text%__1cHOrLNodeGOpcode6kM_i_; +text: .text%__1cWconstantPoolCacheKlassOklass_oop_size6kM_i_: cpCacheKlass.o; +text: .text%__1cRconstantPoolKlassOklass_oop_size6kM_i_: constantPoolKlass.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: library_call.o; +text: .text%__1cSobjArrayKlassKlassOklass_oop_size6kM_i_: objArrayKlassKlass.o; +text: .text%__1cLmethodKlassOklass_oop_size6kM_i_: methodKlass.o; +text: .text%__1cPmethodDataKlassOklass_oop_size6kM_i_: methodDataKlass.o; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: generateOopMap.o; +text: .text%__1cTcmovII_reg_EQdDNENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTtypeArrayKlassKlassOklass_oop_size6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cSTailCalljmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: arrayKlassKlass.o; +text: .text%__1cSinstanceKlassKlassOklass_oop_size6kM_i_: instanceKlassKlass.o; +text: .text%__1cNcmovL_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQconstMethodKlassOklass_oop_size6kM_i_: constMethodKlass.o; +text: .text%__1cLsymbolKlassOklass_oop_size6kM_i_: symbolKlass.o; +text: .text%__1cMTailCallNode2t6MpnENode_222222_v_; +text: .text%__1cKstoreLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWstatic_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cFStateQ_sub_Op_TailCall6MpknENode__v_; +text: .text%__1cVcompiledICHolderKlassOklass_oop_size6kM_i_: compiledICHolderKlass.o; +text: .text%__1cLeDIRegIOperEtype6kM_pknEType__: ad_i486.o; +text: .text%__1cPmovI_nocopyNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSTailCalljmpIndNodeFreloc6kM_i_; +text: .text%__1cQChunkPoolCleanerEtask6M_v_: allocation.o; +text: .text%__1cNandI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cFStateO_sub_Op_StoreF6MpknENode__v_; +text: .text%__1cQaddD_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTsarL_eReg_32_63NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_CallObjectMethod: jni.o; +text: .text%__1cNaddP_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNaddP_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovI_regUNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLOptoRuntimeMrethrow_Type6F_pknITypeFunc__; +text: .text%__1cENodeJis_MemBar6kM_pknKMemBarNode__: classes.o; +text: .text%__1cMstoreSSPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUGenericGrowableArrayUclear_and_deallocate6M_v_; +text: .text%__1cRandI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cSsarL_eReg_1_31NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cRCardTableModRefBSPclear_MemRegion6MnJMemRegion__v_; +text: .text%__1cHThreadsHoops_do6FpnKOopClosure__v_; +text: .text%__1cRInlineCacheBufferLnew_ic_stub6F_pnGICStub__; +text: .text%__1cRInlineCacheBufferWcreate_transition_stub6FpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cVMoveL2D_reg_stackNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cObox_handleNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cRInlineCacheBufferXassemble_ic_buffer_code6FpCpnHoopDesc_1_v_; +text: .text%__1cGICStubIset_stub6MpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cTcmovII_reg_LEGTNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFJNIidHoops_do6MpnKOopClosure__v_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: onStackReplacement.o; +text: .text%__1cObox_handleNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: interpreterRuntime.o; +text: .text%__1cSaddD_reg_roundNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cQorI_eReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmulF24_reg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSaddD_reg_roundNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOmulF24_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cHi2bNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQjava_lang_StringbHcreate_from_platform_depended_str6FpkcpnGThread__nGHandle__; +text: .text%__1cOLibraryCallKitYinline_native_time_funcs6Mi_i_; +text: .text%__1cJHashtableHoops_do6MpnKOopClosure__v_; +text: .text%__1cLOptoRuntimeYcurrent_time_millis_Type6F_pknITypeFunc__; +text: .text%__1cIPSOldGenHcompact6M_v_; +text: .text%__1cJHashtableGunlink6MpnRBoolObjectClosure__v_; +text: .text%__1cOcmpF_cc_P6NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIPSOldGenPadjust_pointers6M_v_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: ciTypeFlow.o; +text: .text%__1cNmulI_eRegNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cKdirectOperFscale6kM_i_: ad_i486_clone.o; +text: .text%__1cQorI_eReg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQorI_eReg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQObjectStartArrayFreset6M_v_; +text: .text%__1cFParseVcatch_call_exceptions6MrnYciExceptionHandlerStream__v_; +text: .text%__1cMsubD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJvmSymbolsHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cSReferenceProcessorHoops_do6MpnKOopClosure__v_; +text: .text%__1cIci2bNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIregDOperFclone6kM_pnIMachOper__; +text: .text%JVM_MonitorNotifyAll; +text: .text%__1cLConvI2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKConv2BNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNmaxI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMelapsedTimer2t6M_v_: phase.o; +text: .text%__1cSvframeArrayElementDbci6kM_i_; +text: .text%__1cIDivDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKarrayKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cODeoptimizationYquery_update_method_data6FnQmethodDataHandle_in0ALDeoptReason_rIri4_pnLProfileData__; +text: .text%__1cKstoreDNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNminI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMnegD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNPhaseRegAllocHset_oop6MpknENode_i_v_; +text: .text%__1cKarrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cFStateM_sub_Op_MaxI6MpknENode__v_; +text: .text%__1cXJNI_ArgumentPusherVaArgJget_float6M_v_: jni.o; +text: .text%__1cWis_positive_zero_float6Ff_i_; +text: .text%__1cTcmovII_reg_LTGENodeQuse_cisc_RegMask6M_v_; +text: .text%__1cSPerfStringConstant2t6MnJCounterNS_pkc3_v_; +text: .text%__1cIMulDNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cQorI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPPerfDataManagerWcreate_string_constant6FnJCounterNS_pkc3pnGThread__pnSPerfStringConstant__; +text: .text%__1cOMacroAssemblerFleave6M_v_; +text: .text%__1cMloadConDNodeFclone6kM_pnENode__; +text: .text%JVM_GetInheritedAccessControlContext; +text: .text%JVM_NativePath; +text: .text%__1cQjava_lang_ThreadbGinherited_access_control_context6FpnHoopDesc__2_; +text: .text%__1cVLoaderConstraintTableJnew_entry6MIpnNsymbolOopDesc_pnMklassOopDesc_ii_pnVLoaderConstraintEntry__; +text: .text%__1cVloadConL_low_onlyNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIimmDOperFclone6kM_pnIMachOper__; +text: .text%__1cIMachNodeOmemory_operand6kM_pknIMachOper__: ad_i486_misc.o; +text: .text%__1cbIjava_security_AccessControlContextGcreate6FnOobjArrayHandle_inGHandle_pnGThread__pnHoopDesc__; +text: .text%__1cHnmethodNis_osr_method6kM_i_: nmethod.o; +text: .text%__1cNmulI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQorl_eReg_immNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cQsalI_eReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOaddF24_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_: callnode.o; +text: .text%__1cFStateT_sub_Op_CallRuntime6MpknENode__v_; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__: callnode.o; +text: .text%__1cOcmovI_regUNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cCosXnon_memory_address_word6F_pc_; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodDataKlass.o; +text: .text%__1cQsalI_eReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodDataKlass.o; +text: .text%__1cVCallRuntimeDirectNodeFreloc6kM_i_; +text: .text%__1cSdivD_reg_roundNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cISubFNodeGOpcode6kM_i_; +text: .text%__1cNandI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveL2D_reg_stackNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cVCallRuntimeDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOmulF24_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cOcmovI_regUNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJloadCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRandL_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTcmovII_reg_LTGENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTshrL_eReg_32_63NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOcmpF_cc_P6NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOaddF24_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cSaddF24_reg_immNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNcmovP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovI_regUNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cSmulF24_reg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cKCompiledICOset_cached_oop6MpnHoopDesc__v_; +text: .text%__1cCosHSolarisKmmap_chunk6FpcIii_2_; +text: .text%__1cKstoreFNodeFreloc6kM_i_; +text: .text%jni_GetStaticMethodID: jni.o; +text: .text%__1cZInterpreterMacroAssemblerWupdate_mdp_by_constant6MpnMRegisterImpl_i_v_; +text: .text%__1cKCompiledICMstub_address6kM_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorRset_unimplemented6Mi_v_; +text: .text%__1cSstoreD_roundedNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_i_v_; +text: .text%__1cKstoreFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKCodeBufferVinsert_float_constant6Mf_pC_; +text: .text%__1cWroundFloat_mem_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOsubF24_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cSaddF24_reg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cSaddD_reg_roundNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMdecI_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTconvF2I_reg_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPciInstanceKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cISubDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKReflectionTunbox_for_primitive6FpnHoopDesc_pnGjvalue_pnGThread__nJBasicType__; +text: .text%__1cMloadConDNodeFreloc6kM_i_; +text: .text%__1cPMultiBranchDataScompute_cell_count6FpnOBytecodeStream__i_; +text: .text%__1cJloadDNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQaddD_reg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMdecI_memNodeFreloc6kM_i_; +text: .text%__1cTshlL_eReg_32_63NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cFTypeDFxdual6kM_pknEType__; +text: .text%__1cXjava_lang_boxing_objectJget_value6FpnHoopDesc_pnGjvalue__nJBasicType__; +text: .text%__1cWCallLeafNoFPDirectNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConDNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cOtailjmpIndNodeNis_block_proj6kM_pknENode__: ad_i486_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMdecI_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOptoRuntimeWresolve_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cLTypeInstPtrLmirror_type6kM_pnGciType__; +text: .text%__1cSmulF24_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationVtrap_state_add_reason6Fii_i_; +text: .text%__1cKstoreINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMdivD_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJLoadBNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pF3_v3_v_; +text: .text%__1cOCompilerThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%__1cYcmpL_zero_flags_LTGENodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVcompiledICHolderKlassSoop_being_unloaded6MpnRBoolObjectClosure_pnHoopDesc__i_; +text: .text%__1cLRuntimeStub2n6FII_pv_; +text: .text%__1cYcmpL_zero_flags_LTGENodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLRuntimeStubQnew_runtime_stub6FpkcpnKCodeBuffer_ipnJOopMapSet_i_p0_; +text: .text%__1cLRuntimeStub2t6MpkcpnKCodeBuffer_iipnJOopMapSet_i_v_; +text: .text%__1cOstoreF_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2F_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLloadSSDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvI2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQshrL_eReg_CLNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQsalL_eReg_CLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNSafePointNodeQpeek_monitor_obj6kM_pnENode__; +text: .text%__1cVcompiledICHolderKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cHnmethodbAmake_not_entrant_or_zombie6Mi_v_; +text: .text%__1cFTypeDFempty6kM_i_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: methodDataOop.o; +text: .text%__1cPconvL2D_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKdirectOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486_clone.o; +text: .text%__1cKdirectOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486_clone.o; +text: .text%__1cPoldgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cKdirectOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486_clone.o; +text: .text%__1cKdirectOperLdisp_is_oop6kM_i_: ad_i486_clone.o; +text: .text%__1cLConvD2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cVcompiledICHolderKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cPconvI2F_SSFNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cRmulI_imm_highNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIDivLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cXpartialSubtypeCheckNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFn0AJOperation__v4_v_; +text: .text%__1cYinternal_word_RelocationGtarget6M_pC_; +text: .text%__1cRxorI_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXpartialSubtypeCheckNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerEsarl6MpnMRegisterImpl_i_v_; +text: .text%__1cIModLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cDhpiFclose6Fi_i_: jvm.o; +text: .text%__1cFParsePdo_monitor_exit6M_v_; +text: .text%__1cFStateN_sub_Op_LoadD6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_MulD6MpknENode__v_; +text: .text%__1cSMachCallNativeNodePret_addr_offset6M_i_; +text: .text%__1cPPerfDataManagerUcreate_long_constant6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongConstant__; +text: .text%__1cOsubF24_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: deoptimization.o; +text: .text%__1cTAbstractInterpreterMreturn_entry6FnITosState_i_pC_; +text: .text%JVM_Close; +text: .text%__1cLOptoRuntimeRnew_objArray_Type6F_pknITypeFunc__; +text: .text%__1cLConvD2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNSafePointNodeQpeek_monitor_box6kM_pnENode__; +text: .text%__1cIMulDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOmulIS_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovL_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerFtestl6MpnMRegisterImpl_i_v_; +text: .text%__1cNdivI_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulFNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cTunsafe_intrinsic_id6FpnNsymbolOopDesc_1_nNmethodOopDescLIntrinsicId__; +text: .text%__1cSaddF24_reg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cQComputeCallStackIdo_float6M_v_: generateOopMap.o; +text: .text%__1cQComputeCallStackHdo_byte6M_v_: generateOopMap.o; +text: .text%__1cFParseMdo_anewarray6M_v_; +text: .text%__1cIAddFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJLoadDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cTinc_decompile_count6FpnHnmethod__v_: nmethod.o; +text: .text%__1cXjava_lang_reflect_FieldFclazz6FpnHoopDesc__2_; +text: .text%__1cMsubD_regNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cOcmpD_cc_P6NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTcmovII_reg_EQdDNENodeQuse_cisc_RegMask6M_v_; +text: .text%__1cFStateM_sub_Op_CmpD6MpknENode__v_; +text: .text%__1cXjava_lang_reflect_FieldJmodifiers6FpnHoopDesc__i_; +text: .text%__1cJAssemblerEmovb6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_only6MnITosState__v_; +text: .text%__1cSCallLeafDirectNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPmovI_nocopyNodeErule6kM_I_: ad_i486_misc.o; +text: .text%jni_NewObjectV: jni.o; +text: .text%__1cSCallLeafDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIci2bNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cSsarL_eReg_1_31NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRcmpFastUnlockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovI_regUNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOtailjmpIndNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cLOopMapCache2t6M_v_; +text: .text%__1cGICStubKcached_oop6kM_pnHoopDesc__; +text: .text%__1cJSubFPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRInlineCacheBufferUic_buffer_cached_oop6FpC_pnHoopDesc__; +text: .text%__1cMdivD_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTconvF2I_reg_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSmulF24_reg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cTmembar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC2_v_; +text: .text%jni_EnsureLocalCapacity; +text: .text%__1cTcmovII_reg_LEGTNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPfieldDescriptorUdouble_initial_value6kM_d_; +text: .text%__1cQAbstractCompilerMsupports_osr6M_i_: c2compiler.o; +text: .text%__1cKEntryPoint2t6M_v_; +text: .text%__1cKstoreINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNTemplateTableGbranch6Fii_v_; +text: .text%__1cXcmpL_reg_flags_LTGENodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSMachBreakpointNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQshrI_eReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cHCompile2t6MpnFciEnv_pF_pknITypeFunc_pCpkciiii_v_; +text: .text%__1cKemit_break6FrnKCodeBuffer__v_; +text: .text%__1cQshrI_eReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvI2F_SSF_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIGraphKitIgen_stub6MpCpkciii_v_; +text: .text%__1cIciMethodVget_osr_flow_analysis6Mi_pnKciTypeFlow__; +text: .text%__1cLloadSSINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQmulD_reg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLResourceObj2n6FIn0APallocation_type__pv_; +text: .text%__1cMloadConFNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cQmulD_reg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cIAddFNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cINegDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSmulF24_reg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJlog2_long6Fx_i_: mulnode.o; +text: .text%__1cJAssemblerEsbbl6MpnMRegisterImpl_i_v_; +text: .text%__1cHnmethodbCcan_not_entrant_be_converted6M_i_; +text: .text%__1cMStartOSRNodeKosr_domain6F_pknJTypeTuple__; +text: .text%__1cNCallGeneratorHfor_osr6FpnIciMethod_i_p0_; +text: .text%__1cINegFNodeGOpcode6kM_i_; +text: .text%__1cQorl_eReg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVeADXRegL_low_onlyOperFclone6kM_pnIMachOper__; +text: .text%__1cZInterpreterMacroAssemblerUprofile_taken_branch6MpnMRegisterImpl_2_v_; +text: .text%__1cKimmL32OperFclone6kM_pnIMachOper__; +text: .text%__1cLOptoRuntimeNgenerate_stub6FpnFciEnv_pF_pknITypeFunc_pCpkciiii_8_; +text: .text%__1cVloadConL_low_onlyNodeFclone6kM_pnENode__; +text: .text%__1cHciKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cFParseWload_interpreter_state6MpnENode_2_v_; +text: .text%__1cVMoveL2D_reg_stackNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMStartOSRNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cPconvI2D_regNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cTshlL_eReg_32_63NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateO_sub_Op_StoreD6MpknENode__v_; +text: .text%__1cPmovI_nocopyNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsYis_supported_jni_version6Fi_C_; +text: .text%__1cFStateM_sub_Op_ConF6MpknENode__v_; +text: .text%__1cNcmovI_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cKConv2BNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJLoadLNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC22i_v_; +text: .text%__1cNcmovI_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cKarrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cIci2bNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSdivD_reg_roundNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cWroundFloat_mem_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOmulF24_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcompiledVFrameHraw_bci6kM_i_; +text: .text%__1cFTypeFFxdual6kM_pknEType__; +text: .text%__1cODeoptimizationbJupdate_method_data_from_interpreter6FnQmethodDataHandle_ii_v_; +text: .text%__1cOcompiledVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cSvframeArrayElementHfill_in6MpnOcompiledVFrame__v_; +text: .text%__1cRComputeEntryStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cTcmovII_reg_LTGENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQorI_eReg_memNodeFreloc6kM_i_; +text: .text%__1cFframebHinterpreter_frame_set_monitor_end6MpnPBasicObjectLock__v_; +text: .text%__1cOstoreF_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvF2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cPmovP_nocopyNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFframebBinterpreter_frame_sender_sp6kM_pi_; +text: .text%__1cNcmovL_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cTAbstractInterpreterPsize_activation6FpnNmethodOopDesc_iiiii_i_; +text: .text%__1cJScopeDescImonitors6M_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cJScopeDescLexpressions6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cJScopeDescGlocals6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cJScopeDescVdecode_monitor_values6Mi_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cOcompiledVFrameGlocals6kM_pnUStackValueCollection__; +text: .text%__1cOcompiledVFrameLexpressions6kM_pnUStackValueCollection__; +text: .text%__1cTcmovII_reg_EQdDNENodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSvframeArrayElementPunpack_on_stack6MiipnFframe_ii_v_; +text: .text%__1cTconvD2I_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFframebCinterpreter_frame_set_locals6Mpi_v_; +text: .text%__1cFframebCinterpreter_frame_set_method6MpnNmethodOopDesc__v_; +text: .text%__1cTAbstractInterpreterQcontinuation_for6FpnNmethodOopDesc_pCiiri_3_; +text: .text%__1cTAbstractInterpreterRTosState_as_index6FnITosState__i_; +text: .text%__1cTAbstractInterpreterRlayout_activation6FpnNmethodOopDesc_iiiipnFframe_4i_v_; +text: .text%__1cSvframeArrayElementNon_stack_size6kMiiii_i_; +text: .text%__1cSInterpreterRuntimeJnote_trap6FpnKJavaThread_ipnGThread__v_; +text: .text%__1cICmpFNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRmulI_imm_highNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcompiledVFrameImonitors6kM_pnNGrowableArray4CpnLMonitorInfo____; +text: .text%__1cRsubL_eReg_memNodeFreloc6kM_i_; +text: .text%__1cMTailJumpNodeKmatch_edge6kMI_I_; +text: .text%__1cFStateP_sub_Op_ConvF2D6MpknENode__v_; +text: .text%__1cCosNcommit_memory6FpcI_i_; +text: .text%__1cScompP_eReg_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_memNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cXpartialSubtypeCheckNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNdivL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNinstanceKlassPadd_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cHThreadsbMis_supported_jni_version_including_1_16Fi_C_; +text: .text%__1cFStateM_sub_Op_DivL6MpknENode__v_; +text: .text%__1cNsubL_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cLimmI_32OperIconstant6kM_i_: ad_i486_clone.o; +text: .text%__1cNTemplateTableLindex_check6FpnMRegisterImpl_2_v_; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: klassKlass.o; +text: .text%__1cSCompiledStaticCallMset_to_clean6M_v_; +text: .text%__1cIUniverseWupdate_heap_info_at_gc6F_v_; +text: .text%Unsafe_DefineClass1; +text: .text%JVM_GetComponentType; +text: .text%JVM_DefineClass; +text: .text%__1cKJavaThreadbFdeoptimized_wrt_marked_nmethods6M_v_; +text: .text%__1cNmodI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLOptoRuntimeNfetch_monitor6FipnJBasicLock_pC_pnHoopDesc__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cLStrCompNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRaddD_reg_imm1NodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cNTemplateTableOpatch_bytecode6FnJBytecodesECode_pnMRegisterImpl_4i_v_; +text: .text%__1cJAssemblerGfrstor6MnHAddress__v_; +text: .text%JVM_GetCPFieldModifiers; +text: .text%__1cSUnsafe_DefineClass6FpnHJNIEnv__pnI_jstring_pnL_jbyteArray_iipnI_jobject_7_pnH_jclass__: unsafe.o; +text: .text%__1cJBasicLockHmove_to6MpnHoopDesc_p0_v_; +text: .text%__1cSObjectSynchronizerOinflate_helper6FpnHoopDesc__pnNObjectMonitor__: synchronizer.o; +text: .text%__1cSdivD_reg_roundNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvI2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNSharedRuntimebJcontinuation_for_implicit_exception6FpnKJavaThread_pCn0AVImplicitExceptionKind__3_; +text: .text%__1cKReflectionUarray_component_type6FpnHoopDesc_pnGThread__2_; +text: .text%__1cOsubF24_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2D_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLimmI_24OperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%__1cYjava_lang_reflect_MethodEslot6FpnHoopDesc__i_; +text: .text%__1cLloadSSDNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cKPSYoungGenKprecompact6M_v_; +text: .text%__1cYjava_lang_reflect_MethodFclazz6FpnHoopDesc__2_; +text: .text%__1cKPSYoungGenPadjust_pointers6M_v_; +text: .text%__1cRCardTableModRefBSFclear6MnJMemRegion__v_; +text: .text%__1cOstackSlotPOperFclone6kM_pnIMachOper__; +text: .text%__1cLPSMarkSweepRmark_sweep_phase26F_v_; +text: .text%__1cKPSYoungGenHcompact6M_v_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase16Frii_v_; +text: .text%Unsafe_AllocateInstance; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceRefKlass.o; +text: .text%__1cYjava_lang_reflect_MethodLreturn_type6FpnHoopDesc__2_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase36F_v_; +text: .text%__1cRInlineCacheBufferSic_destination_for6FpnKCompiledIC__pC_; +text: .text%__1cLPSMarkSweepRmark_sweep_phase46F_v_; +text: .text%__1cPconvL2F_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cYjava_lang_reflect_MethodPparameter_types6FpnHoopDesc__2_; +text: .text%__1cLPSMarkSweepbAreset_millis_since_last_gc6F_v_; +text: .text%__1cUPSAdaptiveSizePolicyUmajor_collection_end6MInHGCCauseFCause__v_; +text: .text%__1cSDeoptimizationBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cMsubD_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJCodeCacheLgc_epilogue6F_v_; +text: .text%__1cJMarkSweepNrestore_marks6F_v_; +text: .text%__1cQUncommonTrapBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cNExceptionBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cJCodeCacheMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cYinternal_word_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cJCodeCacheHoops_do6FpnKOopClosure__v_; +text: .text%__1cRCardTableModRefBSEis_a6MnKBarrierSetEName__i_: cardTableExtension.o; +text: .text%__1cMset_property6FnGHandle_pkc2pnGThread__v_: jvm.o; +text: .text%__1cJCodeCacheLgc_prologue6F_v_; +text: .text%__1cHBoxNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSReferenceProcessorPoops_do_statics6FpnKOopClosure__v_; +text: .text%__1cQorl_eReg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLPSMarkSweepQinvoke_no_policy6Fpii_v_; +text: .text%__1cHJNIEnv_JNewObject6MpnH_jclass_pnK_jmethodID_E_pnI_jobject__: jni.o; +text: .text%__1cObox_handleNodeFclone6kM_pnENode__; +text: .text%__1cLPSMarkSweepPallocate_stacks6F_v_; +text: .text%jni_NewDirectByteBuffer; +text: .text%__1cMdivD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLPSMarkSweepRdeallocate_stacks6F_v_; +text: .text%__1cRInlineCacheBufferOinit_next_stub6F_v_; +text: .text%__1cKReflectionNinvoke_method6FpnHoopDesc_nGHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cMincI_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUPSAdaptiveSizePolicyWmajor_collection_begin6M_v_; +text: .text%__1cJMarkSweepMadjust_marks6F_v_; +text: .text%__1cJMarkSweepXfollow_weak_klass_links6F_v_; +text: .text%__1cQSystemDictionaryMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: symbolKlass.o; +text: .text%__1cNmodL_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%jni_AllocObject: jni.o; +text: .text%__1cZInterpreterMacroAssemblerQtest_mdp_data_at6MpnMRegisterImpl_i2rnFLabel__v_; +text: .text%__1cPcmpFastLockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQSystemDictionaryYalways_strong_classes_do6FpnKOopClosure__v_; +text: .text%__1cVLoaderConstraintTableYpurge_loader_constraints6MpnRBoolObjectClosure__v_; +text: .text%__1cVLoaderConstraintTableYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cUPSMarkSweepDecoratorbIset_destination_decorator_perm_gen6F_v_; +text: .text%__1cFStateL_sub_Op_Box6MpknENode__v_; +text: .text%__1cUPSMarkSweepDecoratorbHset_destination_decorator_tenured6F_v_; +text: .text%JVM_InvokeMethod; +text: .text%__1cQSystemDictionaryValways_strong_oops_do6FpnKOopClosure__v_; +text: .text%__1cSmulF24_reg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cFKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: symbolKlass.o; +text: .text%__1cKDictionaryMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cHThreadsLnmethods_do6F_v_; +text: .text%__1cIPSOldGenKprecompact6M_v_; +text: .text%__1cHThreadsLgc_epilogue6F_v_; +text: .text%__1cHThreadsLgc_prologue6F_v_; +text: .text%__1cJPSPermGenQcompute_new_size6MI_v_; +text: .text%__1cXpartialSubtypeCheckNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cZInterpreterMacroAssemblerYprofile_not_taken_branch6MpnMRegisterImpl__v_; +text: .text%__1cHi2bNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQSystemDictionaryPplaceholders_do6FpnKOopClosure__v_; +text: .text%__1cFStateM_sub_Op_ModL6MpknENode__v_; +text: .text%__1cSaddF24_reg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJPSPermGenKprecompact6M_v_; +text: .text%__1cHi2bNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTleaPIdxScaleOffNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFj_not6FnNTemplateTableJCondition__nJAssemblerJCondition__: templateTable_i486.o; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cVMoveF2I_reg_stackNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cMincI_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cKDictionaryYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cNTemplateTableMlocals_index6FpnMRegisterImpl_i_v_; +text: .text%__1cQaddD_reg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFn0AJCondition__v4_v_; +text: .text%__1cVMoveL2D_reg_stackNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMincI_memNodeFreloc6kM_i_; +text: .text%__1cOaddF24_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovI_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_pnNsymbolOopDesc_pkc_nGHandle__; +text: .text%__1cMnegF_regNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cJStubQdDueueMremove_first6Mi_v_; +text: .text%__1cSsarL_eReg_1_31NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%JVM_NewArray; +text: .text%__1cLConvF2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOcmpF_cc_P6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReflectionRreflect_new_array6FpnHoopDesc_ipnGThread__pnMarrayOopDesc__; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlass.o; +text: .text%__1cFStateM_sub_Op_MulF6MpknENode__v_; +text: .text%__1cTconvF2I_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTshlL_eReg_32_63NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerFpopad6M_v_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cNTemplateTableHconvert6F_v_; +text: .text%__1cZInterpreterMacroAssemblerNunlock_object6MpnMRegisterImpl__v_; +text: .text%__1cSmulF24_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNcmovI_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cRmethodDataOopDescRbci_to_extra_data6Mii_pnLProfileData__; +text: .text%__1cIDivDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cTconvD2I_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNcmovI_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIMulFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFStateP_sub_Op_ConvD2I6MpknENode__v_; +text: .text%__1cLMoveF2INodeGOpcode6kM_i_; +text: .text%__1cPconvI2D_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJStubQdDueueKremove_all6M_v_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: compiledICHolderKlass.o; +text: .text%__1cVVM_ParallelGCSystemGC2t6MI_v_; +text: .text%__1cOcmovI_regUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIregFOperFclone6kM_pnIMachOper__; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cLCastP2INodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cPconvL2D_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPmovP_nocopyNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPfieldDescriptorTfloat_initial_value6kM_f_; +text: .text%__1cJArgumentsQPropertyList_add6FppnOSystemProperty_2_v_; +text: .text%__1cSaddF24_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKoopFactoryUnew_compiledICHolder6FnMmethodHandle_nLKlassHandle_pnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cMciArrayKlassRbase_element_type6M_pnGciType__; +text: .text%__1cHCompileWget_MethodAccessorImpl6M_pnPciInstanceKlass__; +text: .text%__1cSInterpreterRuntimeQcreate_exception6FpnKJavaThread_pc3_v_; +text: .text%__1cXjava_lang_reflect_FieldEslot6FpnHoopDesc__i_; +text: .text%__1cSmulF24_reg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cVcompiledICHolderKlassIallocate6MpnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cLTypeInstPtrOxmeet_unloaded6kMpk0_2_; +text: .text%__1cScompP_eReg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cScompP_eReg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cHCompileRget_Method_invoke6M_pnIciMethod__; +text: .text%__1cLPSMarkSweepGinvoke6Fpii_v_; +text: .text%JVM_GC; +text: .text%__1cVVM_ParallelGCSystemGCEdoit6M_v_; +text: .text%__1cXpartialSubtypeCheckNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLClassLoaderSget_system_package6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cWroundFloat_mem_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cQinitialize_class6FnMsymbolHandle_pnGThread__v_: thread.o; +text: .text%__1cKScopeValueLis_location6kM_i_: debugInfo.o; +text: .text%__1cVVM_ParallelGCSystemGCEname6kM_pkc_: vm_operations.o; +text: .text%__1cRfind_field_offset6FpnI_jobject_ipnGThread__i_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: compiledICHolderKlass.o; +text: .text%__1cOLibraryCallKitbDis_method_invoke_or_aux_frame6MpnIJVMState__i_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_pnGThread__v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: objArrayKlass.o; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cUParallelScavengeHeapHcollect6MnHGCCauseFCause__v_; +text: .text%JVM_GetSystemPackage; +text: .text%__1cLStatSamplerTget_system_property6FpkcpnGThread__2_; +text: .text%__1cPconvL2D_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cETypeJis_finite6kM_i_; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: compiledICHolderKlass.o; +text: .text%__1cXPartialSubtypeCheckNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSaddF24_reg_immNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cCosNcommit_memory6FpcII_i_; +text: .text%__1cOresolve_symbol6Fpkc_pC_: os_solaris.o; +text: .text%__1cLloadSSINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: klassKlass.o; +text: .text%__1cNloadConL0NodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cPMultiBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%JVM_RawMonitorCreate; +text: .text%__1cImulINodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: interpreter.o; +text: .text%__1cQConstantIntValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cOLibraryCallKitXinline_string_compareTo6M_i_; +text: .text%__1cImulINodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cOMacroAssemblerVreset_last_Java_frame6MpnMRegisterImpl_i_v_; +text: .text%__1cOMacroAssemblerTset_last_Java_frame6MpnMRegisterImpl_22pC_v_; +text: .text%__1cOMacroAssemblerRcall_VM_leaf_base6MpCi_v_; +text: .text%__1cSstring_compareNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cISubFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFStateP_sub_Op_StrComp6MpknENode__v_; +text: .text%__1cIcp2bNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cFStateP_sub_Op_ConvI2F6MpknENode__v_; +text: .text%__1cOMacroAssemblerPempty_FPU_stack6M_v_; +text: .text%jni_GetStringRegion: jni.o; +text: .text%__1cQConstantIntValuePis_constant_int6kM_i_: debugInfo.o; +text: .text%__1cICodeBlobZis_at_poll_or_poll_return6MpC_i_; +text: .text%__1cSmulF24_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQaddD_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceRefKlass.o; +text: .text%__1cIAddFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cHOrLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cScompP_eReg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHBoxNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvL2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLConvL2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cTcmovII_reg_EQdDNENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQinstanceRefKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceRefKlass.o; +text: .text%__1cLvframeArrayPunpack_to_stack6MrnFframe_i_v_; +text: .text%__1cLvframeArrayZdeallocate_monitor_chunks6M_v_; +text: .text%__1cLvframeArrayHfill_in6MpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pknLRegisterMap_i_v_; +text: .text%__1cLvframeArrayIallocate6FpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pnLRegisterMap_nFframe_9A9A9A_p0_; +text: .text%__1cOstoreF_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVcompiledICHolderKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNcmovI_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cImulINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsalL_eReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cbCAbstractInterpreterGeneratorUset_wide_entry_point6MpnITemplate_rpC_v_; +text: .text%Unsafe_CompareAndSwapInt; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_data_at6MpnMRegisterImpl_i2_v_; +text: .text%__1cRsubI_eReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cQsalL_eReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMsubD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYcmpL_zero_flags_LTGENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cODeoptimizationLUnrollBlock2t6MiiiiipippCnJBasicType__v_; +text: .text%__1cNTemplateTableRlocals_index_wide6FpnMRegisterImpl__v_; +text: .text%__1cKstorePNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2F_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConDNodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LEGTNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cSmulF24_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosHSolarisVcleanup_interruptible6FpnKJavaThread__v_; +text: .text%__1cCosHSolarisTsetup_interruptible6F_pnKJavaThread__; +text: .text%__1cQmulI_mem_immNodeFreloc6kM_i_; +text: .text%__1cJloadFNodeIpipeline6kM_pknIPipeline__; +text: .text%JVM_Sleep; +text: .text%JVM_Lseek; +text: .text%__1cNnmethodLocker2t6MpC_v_; +text: .text%__1cRmulI_eReg_immNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNTemplateTableQvolatile_barrier6F_v_; +text: .text%__1cHnmethodVmark_as_seen_on_stack6M_v_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_ppkc5i_i_: arguments.o; +text: .text%__1cQmulD_reg_immNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cKVtableStubSpd_code_size_limit6Fi_i_; +text: .text%__1cMdivD_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cKNativeJumpUpatch_verified_entry6FpC11_v_; +text: .text%__1cQmulD_reg_immNodeFreloc6kM_i_; +text: .text%__1cODeoptimizationRlast_frame_adjust6Fii_i_; +text: .text%__1cFParseQdo_monitor_enter6M_v_; +text: .text%__1cCosHSolarisTsetup_interruptible6FpnKJavaThread__v_; +text: .text%__1cODeoptimizationScreate_vframeArray6FpnKJavaThread_nFframe_pnLRegisterMap__pnLvframeArray__; +text: .text%__1cLConvF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRaddI_mem_eRegNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cODeoptimizationNuncommon_trap6FpnKJavaThread_i_pn0ALUnrollBlock__; +text: .text%__1cODeoptimizationTuncommon_trap_inner6FpnKJavaThread_i_v_; +text: .text%__1cNcmovL_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: deoptimization.o; +text: .text%__1cODeoptimizationRgather_statistics6Fn0ALDeoptReason_n0ALDeoptAction_nJBytecodesECode__v_; +text: .text%__1cIcp2bNodeMideal_Opcode6kM_i_: ad_i486_misc.o; +text: .text%__1cODeoptimizationPget_method_data6FpnKJavaThread_nMmethodHandle_i_pnRmethodDataOopDesc__; +text: .text%__1cODeoptimizationNunpack_frames6FpnKJavaThread_i_nJBasicType__; +text: .text%__1cIci2bNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cODeoptimizationYfetch_unroll_info_helper6FpnKJavaThread__pn0ALUnrollBlock__; +text: .text%__1cOMacroAssemblerFenter6M_v_; +text: .text%Unsafe_GetNativeByte; +text: .text%__1cOMacroAssemblerNpop_FPU_state6M_v_; +text: .text%__1cTsarL_eReg_32_63NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPconvL2F_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQmulD_reg_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_: interpreterRuntime.o; +text: .text%__1cFTypeFGis_nan6kM_i_; +text: .text%__1cSaddF24_reg_memNodeMcisc_operand6kM_i_: ad_i486_misc.o; +text: .text%__1cKCMoveLNodeGOpcode6kM_i_; +text: .text%JVM_NanoTime; +text: .text%__1cIXorINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cFParseOdo_tableswitch6M_v_; +text: .text%__1cCosNjavaTimeNanos6F_x_; +text: .text%__1cETypeEmake6Fn0AFTYPES__pk0_; +text: .text%__1cVMoveF2I_reg_stackNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerFpushl6MnHAddress__v_; +text: .text%jni_GetEnv; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: methodLiveness.o; +text: .text%__1cKstoreCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cGICStubFclear6M_v_; +text: .text%__1cODeoptimizationLUnrollBlock2T6M_v_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: interp_masm_i486.o; +text: .text%__1cGciType2t6MnJBasicType__v_; +text: .text%__1cKstoreDNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRComputeEntryStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cMloadConFNodeFreloc6kM_i_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlassKlass.o; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCi_v_; +text: .text%__1cHTypePtrKadd_offset6kMi_pk0_; +text: .text%__1cMorI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cFStateP_sub_Op_ConvI2D6MpknENode__v_; +text: .text%__1cJAssemblerEmovw6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cQshrL_eReg_CLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIimmFOperFclone6kM_pnIMachOper__; +text: .text%__1cFKlassMoop_is_array6kM_i_: objArrayKlassKlass.o; +text: .text%__1cFStateM_sub_Op_AddF6MpknENode__v_; +text: .text%__1cOcompI_eRegNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cMloadConFNodeFclone6kM_pnENode__; +text: .text%__1cTconvI2F_SSF_memNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cParrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%jint_cmp: parse2.o; +text: .text%__1cJAssemblerExorl6MpnMRegisterImpl_i_v_; +text: .text%__1cPBytecode_invokeLresult_type6kMpnGThread__nJBasicType__; +text: .text%jni_CallVoidMethod: jni.o; +text: .text%__1cOsubF24_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOstoreF_immNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cENodeHis_Call6M_pnICallNode__: machnode.o; +text: .text%__1cLloadSSINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cLloadSSINodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRStubCodeGeneratorLstub_epilog6MpnMStubCodeDesc__v_; +text: .text%__1cRStubCodeGeneratorLstub_prolog6MpnMStubCodeDesc__v_; +text: .text%__1cMnegF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConFNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%Unsafe_GetNativeFloat; +text: .text%__1cKstoreDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSaddD_reg_roundNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddI_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWroundFloat_mem_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMStubCodeMark2t6MpnRStubCodeGenerator_pkc4_v_; +text: .text%__1cPmovP_nocopyNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_method_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: callnode.o; +text: .text%__1cOsubF24_regNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLMoveL2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNstoreImmPNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cMStubCodeMark2T6M_v_; +text: .text%__1cRSignatureIteratorHiterate6M_v_; +text: .text%__1cNTemplateTableEiop26Fn0AJOperation__v_; +text: .text%__1cFStateO_sub_Op_CMoveP6MpknENode__v_; +text: .text%__1cOcmovI_regUNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddD_reg_imm1NodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cPmovI_nocopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJAssemblerFfwait6M_v_; +text: .text%__1cJAssemblerKrepne_scan6M_v_; +text: .text%__1cPjava_lang_ClassYcreate_basic_type_mirror6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cPmovP_nocopyNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cNRegisterSaverWrestore_live_registers6FpnOMacroAssembler__v_; +text: .text%__1cRandL_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNcmovL_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerMdispatch_via6MnITosState_ppC_v_; +text: .text%__1cZInterpreterMacroAssemblerRremove_activation6MnITosState_pnMRegisterImpl_iii_v_; +text: .text%__1cImulINodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cImulINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSaddF24_reg_memNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cImulINodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cJname2type6Fpkc_nJBasicType__; +text: .text%__1cOcmpF_cc_P6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_continuation_for6MnITosState__pC_; +text: .text%__1cLvframeArrayRregister_location6kMi_pC_; +text: .text%JVM_FindPrimitiveClass; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constantPoolKlass.o; +text: .text%__1cVis_positive_one_float6Ff_i_; +text: .text%__1cRaddI_mem_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmulF24_reg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cSdivD_reg_roundNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cSaddF24_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_safept_entry_for6MnITosState_pC_2_; +text: .text%__1cSaddF24_reg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cIDivINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSstoreD_roundedNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMdivD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMmulD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIAddDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNRelocIterator2t6MpnKCodeBuffer_pC3_v_; +text: .text%__1cFStateO_sub_Op_LoadPC6MpknENode__v_; +text: .text%__1cJAssemblerGmovsxb6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cSmulF24_reg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cJCmpF3NodeGOpcode6kM_i_; +text: .text%__1cIAddDNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cHMulNodeGis_Mul6kM_pk0_: classes.o; +text: .text%__1cJAssemblerFtestb6MpnMRegisterImpl_i_v_; +text: .text%__1cIGraphKitSgen_native_wrapper6MpnIciMethod__v_; +text: .text%__1cNTemplateTableQfast_accessfield6FnITosState__v_; +text: .text%__1cOmulIS_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNtestI_regNodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%Unsafe_StaticFieldBaseFromField; +text: .text%__1cSaddF24_reg_memNodeRis_cisc_alternate6kM_i_: ad_i486_misc.o; +text: .text%__1cOsubF24_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNTemplateTableZjvmti_post_fast_field_mod6F_v_; +text: .text%__1cKLoadPCNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTablePfast_storefield6FnITosState__v_; +text: .text%__1cXpartialSubtypeCheckNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%Unsafe_StaticFieldOffset; +text: .text%__1cVMoveL2D_reg_stackNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cNTemplateTableXresolve_cache_and_index6FipnMRegisterImpl_2_v_; +text: .text%__1cKdirectOperJnum_edges6kM_I_: ad_i486_clone.o; +text: .text%Unsafe_GetObjectVolatile; +text: .text%__1cHnmethodFflush6M_v_; +text: .text%__1cHnmethodSflush_dependencies6MpnRBoolObjectClosure__v_; +text: .text%__1cTconvI2F_SSF_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cICodeBlobFflush6M_v_; +text: .text%__1cJAssemblerMemit_arith_b6MiipnMRegisterImpl_i_v_; +text: .text%__1cFTypeFFempty6kM_i_; +text: .text%__1cKdirectOperNconstant_disp6kM_i_: ad_i486_clone.o; +text: .text%__1cQciTypeArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cNReservedSpace2t6MpcI_v_; +text: .text%__1cNaddP_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJEventMark2t6MpkcE_v_: nmethod.o; +text: .text%__1cIMachOperNbase_position6kM_i_; +text: .text%__1cQorl_eReg_immNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2F_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cPconvL2F_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cWroundFloat_mem_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cYinternal_word_RelocationMforce_target6MpC_v_: relocInfo.o; +text: .text%__1cQmulD_reg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cZInterpreterMacroAssemblerGf2ieee6M_v_; +text: .text%__1cQmulD_reg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cFVTuneOdelete_nmethod6FpnHnmethod__v_; +text: .text%__1cPconvL2D_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIMachOperMdisp_as_type6kM_pknHTypePtr__: ad_i486_clone.o; +text: .text%Unsafe_EnsureClassInitialized; +text: .text%__1cFStateS_sub_Op_CallNative6MpknENode__v_; +text: .text%__1cIciSymbolHas_utf86M_pkc_; +text: .text%__1cOtypeArrayKlassNexternal_name6FnJBasicType__pkc_; +text: .text%__1cENodeHdel_out6Mp0_v_: connode.o; +text: .text%JVM_GetClassContext; +text: .text%__1cPPerfDataManagerWcreate_string_variable6FnJCounterNS_pkci3pnGThread__pnSPerfStringVariable__; +text: .text%__1cOtypeArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlass.o; +text: .text%__1cIciMethodMnative_entry6M_pC_; +text: .text%__1cNdivI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJAssemblerHfincstp6M_v_; +text: .text%__1cZInterpreterMacroAssemblerSnotify_method_exit6MnITosState__v_; +text: .text%__1cETypeFxdual6kM_pk0_; +text: .text%__1cQorI_eReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOMacroAssemblerEfpop6M_v_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassMcreate_klass6FnJBasicType_ipnGThread__pnMklassOopDesc__; +text: .text%__1cLlog2_intptr6Fi_i_: typeArrayKlass.o; +text: .text%__1cLOptoRuntimeTmultianewarray_Type6Fi_pknITypeFunc__; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod__v_; +text: .text%__1cQAbstractCompilerPsupports_native6M_i_: c2compiler.o; +text: .text%__1cOGenerateOopMapGdo_jsr6Mi_v_; +text: .text%__1cZInterpreterMacroAssemblerbGget_unsigned_2_byte_index_at_bcp6MpnMRegisterImpl_i_v_; +text: .text%__1cSaddF24_reg_immNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cIAddDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cbCAbstractInterpreterGeneratorbBgenerate_result_handler_for6MnJBasicType__pC_; +text: .text%__1cOMacroAssemblerOpush_FPU_state6M_v_; +text: .text%__1cZInterpreterMacroAssemblerRcall_VM_leaf_base6MpCi_v_; +text: .text%__1cZInterpreterMacroAssemblerGd2ieee6M_v_; +text: .text%jni_RegisterNatives: jni.o; +text: .text%__1cSmulF24_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSaddF24_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%JVM_GetClassDeclaredFields; +text: .text%stat: os_solaris.o; +text: .text%JVM_IsSameClassPackage; +text: .text%__1cWroundFloat_mem_regNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cCosMuser_handler6F_pv_; +text: .text%__1cLConvF2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cLClassLoaderbCupdate_class_path_entry_list6Fpkc_v_; +text: .text%__1cOcmovI_regUNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cJArgumentsRverify_percentage6FIpkc_i_; +text: .text%jni_SetStaticObjectField: jni.o; +text: .text%__1cNTemplateTableGiconst6Fi_v_; +text: .text%__1cJAssemblerEsubl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cFStateM_sub_Op_AddD6MpknENode__v_; +text: .text%__1cLConvD2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJAssemblerEcall6MpnMRegisterImpl_nJrelocInfoJrelocType__v_; +text: .text%__1cRComputeEntryStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cFTypeFJis_finite6kM_i_; +text: .text%__1cCosEstat6FpkcpnEstat__i_; +text: .text%__1cLOptoRuntimeRresolve_call_Type6F_pknITypeFunc__; +text: .text%__1cCosHSolarisSset_signal_handler6Fiii_v_; +text: .text%__1cOPhaseIdealLoopTdo_maximally_unroll6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkci_pc_; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_2_v_; +text: .text%__1cNMemoryManagerIadd_pool6MpnKMemoryPool__v_; +text: .text%__1cSaddF24_reg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cJAssemblerGfnsave6MnHAddress__v_; +text: .text%__1cVMoveF2I_reg_stackNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMdecI_memNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKMemoryPoolLadd_manager6MpnNMemoryManager__v_; +text: .text%__1cJloadDNodeFreloc6kM_i_; +text: .text%__1cSObjectSynchronizerIjni_exit6FpnHoopDesc_pnGThread__v_; +text: .text%__1cMloadConLNodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cSObjectSynchronizerJjni_enter6FnGHandle_pnGThread__v_; +text: .text%__1cSaddF24_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMloadConFNodeGis_Con6kM_I_: ad_i486_misc.o; +text: .text%__1cISubDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLloadSSINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cISubDNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cQorl_eReg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_MonitorExit: jni.o; +text: .text%__1cNTemplateTableHif_0cmp6Fn0AJCondition__v_; +text: .text%jni_MonitorEnter: jni.o; +text: .text%__1cSaddD_reg_roundNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvL2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTableHif_icmp6Fn0AJCondition__v_; +text: .text%__1cKReturnNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cPcheckCastPPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFframebFset_interpreter_frame_sender_sp6Mpi_v_; +text: .text%__1cRmulI_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovI_regUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cULinearLeastSquareFit2t6MI_v_; +text: .text%__1cNTemplateTableH_return6FnITosState__v_; +text: .text%__1cOMacroAssemblerEfcmp6MpnMRegisterImpl__v_; +text: .text%__1cMTailJumpNodeGOpcode6kM_i_; +text: .text%__1cOsubF24_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWroundFloat_mem_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHciKlass2t6MnLKlassHandle_pnIciSymbol__v_; +text: .text%jni_GetJavaVM; +text: .text%__1cHciKlassIis_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cLConvL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJOopMapSetQsingular_oop_map6M_pnGOopMap__; +text: .text%__1cSaddF24_reg_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLConvF2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cTconvI2F_SSF_memNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cTconvI2F_SSF_memNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cCosZvm_allocation_granularity6F_i_; +text: .text%__1cNReservedSpaceKinitialize6MIIipc_v_; +text: .text%JVM_LoadLibrary; +text: .text%__1cCosOreserve_memory6FIpc_1_; +text: .text%JVM_IsSupportedJNIVersion; +text: .text%__1cOstoreF_immNodeFreloc6kM_i_; +text: .text%__1cVMoveF2I_reg_stackNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cPPerfLongVariant2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_pnUPerfLongSampleHelper__v_; +text: .text%__1cNmulI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerGfild_d6MnHAddress__v_; +text: .text%__1cRmulI_imm_highNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerGmovsxw6MpnMRegisterImpl_nHAddress__v_; +text: .text%Unsafe_ObjectFieldOffset; +text: .text%__1cIcp2bNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIcp2bNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeHsize_of6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerEincl6MnHAddress__v_; +text: .text%__1cXpartialSubtypeCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPmovP_nocopyNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cVMoveL2D_reg_stackNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosHSolarisOis_sig_ignored6Fi_i_; +text: .text%__1cRaddI_mem_eRegNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOLibraryCallKitbNinline_native_Reflection_getCallerClass6M_i_; +text: .text%__1cOMacroAssemblerQload_signed_byte6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cOLibraryCallKitZinline_native_Class_query6MnIciMethodLIntrinsicId__i_; +text: .text%__1cOstoreF_immNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cPconvL2D_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKciTypeFlowLStateVectorOdo_null_assert6MpnHciKlass__v_; +text: .text%__1cPconvL2F_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIci2bNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cXcmpL_reg_flags_LEGTNodeFclone6kM_pnENode__; +text: .text%__1cJMemRegion2t6M_v_: cardTableModRefBS.o; +text: .text%__1cQmulD_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJAssemblerHfucomip6Mi_v_; +text: .text%__1cTGeneratePairingInfoOreport_results6kM_i_: ciMethod.o; +text: .text%__1cFciEnvbNArrayIndexOutOfBoundsException_instance6M_pnKciInstance__; +text: .text%__1cRsalI_eReg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%jni_SetObjectField: jni.o; +text: .text%Unsafe_AllocateMemory; +text: .text%__1cNSharedRuntimeVhandle_ic_miss_helper6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: callnode.o; +text: .text%__1cTconvI2F_SSF_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMelapsedTimer2t6M_v_: methodLiveness.o; +text: .text%__1cISubDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvI2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvF2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKMemoryPool2t6Mpkcn0AIPoolType_IIii_v_; +text: .text%__1cRaddD_reg_imm1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosHSolarisOset_mpss_range6FpcII_i_; +text: .text%__1cNTemplateTableEfop26Fn0AJOperation__v_; +text: .text%__1cPciInstanceKlassbDcompute_shared_is_initialized6M_i_; +text: .text%jni_Throw: jni.o; +text: .text%__1cNSpaceCounters2t6MpkciIpnMMutableSpace_pnSGenerationCounters__v_; +text: .text%__1cOPSVirtualSpaceJexpand_by6MI_i_; +text: .text%__1cSaddF24_reg_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableFaload6Fi_v_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_int_field06FnJBasicType__pC_; +text: .text%__1cNTemplateTableElop26Fn0AJOperation__v_; +text: .text%__1cQjava_lang_ThreadKset_daemon6FpnHoopDesc__v_; +text: .text%__1cPmovP_nocopyNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableEdop26Fn0AJOperation__v_; +text: .text%__1cIcp2bNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLklassVtableTis_miranda_entry_at6Mi_i_; +text: .text%__1cHnmethodbJcontinuation_for_implicit_exception6MpC_1_; +text: .text%__1cLVtableStubsIcontains6FpC_i_; +text: .text%__1cWImplicitExceptionTable2t6MpknHnmethod__v_; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnQPerfLongVariable__; +text: .text%__1cKExceptionsK_throw_oop6FpnGThread_pkcipnHoopDesc__v_; +text: .text%__1cOMacroAssemblerQload_signed_word6MpnMRegisterImpl_nHAddress__i_; +text: .text%__1cNdivI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXNativeSignatureIteratorLpass_double6M_v_: interpreterRuntime.o; +text: .text%__1cLVtableStubsPstub_containing6FpC_pnKVtableStub__; +text: .text%__1cOtailjmpIndNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cMNativeLookupTbase_library_lookup6Fpkc22_pC_; +text: .text%__1cFStateP_sub_Op_ConvF2I6MpknENode__v_; +text: .text%__1cODeoptimizationYreset_invocation_counter6FpnJScopeDesc_i_v_; +text: .text%__1cQshrL_eReg_CLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvF2I_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXNativeSignatureIteratorJdo_double6M_v_: interpreterRuntime.o; +text: .text%__1cOcompI_eRegNodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerNsuper_call_VM6MpnMRegisterImpl_2pC22_v_; +text: .text%__1cLVtableStubsLcreate_stub6FiipnNmethodOopDesc__pC_; +text: .text%__1cbCAbstractInterpreterGeneratorXbang_stack_shadow_pages6Mi_v_; +text: .text%__1cNCellTypeStateImake_any6Fi_0_: generateOopMap.o; +text: .text%__1cJAssemblerEmovb6MnHAddress_i_v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkc2i_pc_; +text: .text%__1cNRegisterSaverTsave_live_registers6FpnOMacroAssembler_ipi_pnGOopMap__; +text: .text%__1cTshlL_eReg_32_63NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cWImplicitExceptionTableCat6kMI_I_; +text: .text%__1cQshrL_eReg_CLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLVtableStubsGlookup6Fiii_pnKVtableStub__; +text: .text%__1cSsarL_eReg_1_31NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNReservedSpaceKfirst_part6MIii_0_; +text: .text%__1cJAssemblerFfinit6M_v_; +text: .text%__1cLVtableStubsOis_entry_point6FpC_i_; +text: .text%__1cFParseRjump_if_true_fork6MpnGIfNode_ii_v_; +text: .text%__1cKVtableStubRpd_code_alignment6F_i_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: subnode.o; +text: .text%__1cUInterpreterGeneratorUgenerate_fixed_frame6Mi_v_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerSstore_check_part_26MpnMRegisterImpl__v_; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: frame.o; +text: .text%__1cNStubGeneratorYgenerate_throw_exception6MpkcpCi_3_: stubGenerator_i486.o; +text: .text%__1cGEventsDlog6FpkcE_v_: compiledIC.o; +text: .text%__1cLOptoRuntimebBhandle_wrong_method_ic_miss6FpnKJavaThread__pC_; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2t6M_v_; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2T6M_v_; +text: .text%__1cNReservedSpace2t6MI_v_; +text: .text%__1cVMoveF2I_reg_stackNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationZtrap_state_set_recompiled6Fii_i_; +text: .text%__1cINegFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSmulF24_reg_immNodeFreloc6kM_i_; +text: .text%__1cLloadSSINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerSstore_check_part_16MpnMRegisterImpl__v_; +text: .text%__1cKCompiledICOis_megamorphic6kM_i_; +text: .text%__1cUInterpreterGeneratorbAgenerate_run_compiled_code6M_v_; +text: .text%__1cUInterpreterGeneratorZgenerate_counter_overflow6MpC_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_counter_incr6MpnFLabel_22_v_; +text: .text%__1cIDivDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKCompiledICSset_to_megamorphic6MpnICallInfo_nJBytecodesECode_pnGThread__v_; +text: .text%JVM_GetLastErrorString; +text: .text%__1cIDivDNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cZInterpreterMacroAssemblerTnotify_method_entry6M_v_; +text: .text%__1cSmulF24_reg_immNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cFStateM_sub_Op_DivD6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_DivI6MpknENode__v_; +text: .text%__1cRComputeEntryStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cINegDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNTemplateTableGdstore6Fi_v_; +text: .text%__1cNTemplateTableFdload6Fi_v_; +text: .text%__1cNTemplateTableOprepare_invoke6FpnMRegisterImpl_2inJBytecodesECode__v_; +text: .text%__1cLConvD2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTableGlstore6Fi_v_; +text: .text%__1cOcompP_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNTemplateTableGfstore6Fi_v_; +text: .text%__1cNTemplateTableGastore6Fi_v_; +text: .text%__1cKConv2BNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNGCTaskManagerGthread6MI_pnMGCTaskThread__; +text: .text%__1cPdouble_quadword6Fpxxx_0_: ad_i486.o; +text: .text%__1cFStateM_sub_Op_SubD6MpknENode__v_; +text: .text%__1cNTemplateTableZload_field_cp_cache_entry6FipnMRegisterImpl_22i_v_; +text: .text%__1cSmulF24_reg_memNodeFreloc6kM_i_; +text: .text%__1cScompP_eReg_memNodeFreloc6kM_i_; +text: .text%__1cLMoveL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNTemplateTablebAload_invoke_cp_cache_entry6FipnMRegisterImpl_22ii_v_; +text: .text%__1cFStateM_sub_Op_NegD6MpknENode__v_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: gcTaskThread.o; +text: .text%__1cJlog2_long6Fx_i_: divnode.o; +text: .text%__1cMelapsedTimer2t6M_v_: compileBroker.o; +text: .text%__1cNcmovI_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cQaddD_reg_immNodeFreloc6kM_i_; +text: .text%__1cIRetTableHadd_jsr6Mii_v_; +text: .text%__1cNTemplateTableFfload6Fi_v_; +text: .text%__1cNTemplateTableFlload6Fi_v_; +text: .text%__1cNcmovI_memNodeFreloc6kM_i_; +text: .text%__1cECopyQpd_fill_to_words6FpnIHeapWord_II_v_: unsafe.o; +text: .text%__1cPaddress_of_flag6FnXCommandLineFlagWithType__pnEFlag__: globals.o; +text: .text%__1cNTemplateTableFiload6Fi_v_; +text: .text%__1cOGenerateOopMapTret_jump_targets_do6MpnOBytecodeStream_pFp0ipi_vi4_v_; +text: .text%__1cNdivI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cImulINodeFreloc6kM_i_; +text: .text%__1cNmulI_eRegNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFStateO_sub_Op_Conv2B6MpknENode__v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fii_v_; +text: .text%__1cIRetTableUfind_jsrs_for_target6Mi_pnNRetTableEntry__; +text: .text%__1cNmulI_eRegNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNTemplateTableGistore6Fi_v_; +text: .text%__1cRClassPathZipEntry2t6Mppvpc_v_; +text: .text%__1cLClassLoaderXcreate_class_path_entry6FpcnEstat_ppnOClassPathEntry__v_; +text: .text%__1cLClassLoaderLadd_to_list6FpnOClassPathEntry__v_; +text: .text%__1cOLibraryCallKitbBinline_native_currentThread6M_i_; +text: .text%__1cOLibraryCallKitXgenerate_current_thread6MrpnENode__2_; +text: .text%__1cTconvI2F_SSF_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cQaddD_reg_immNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cLClassLoaderSget_canonical_path6Fpc1i_i_; +text: .text%__1cSmembar_acquireNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMstoreSSINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRaddD_reg_imm1NodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerEcmpb6MnHAddress_i_v_; +text: .text%__1cINegDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJAssemblerEdecl6MnHAddress__v_; +text: .text%__1cOstackSlotFOperFclone6kM_pnIMachOper__; +text: .text%__1cSaddF24_reg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cCosGsignal6Fipv_1_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%jni_CallStaticObjectMethod: jni.o; +text: .text%__1cMNativeLookupNlong_jni_name6FnMmethodHandle__pc_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceRefKlass.o; +text: .text%__1cZInterpreterMacroAssemblerRprofile_checkcast6MipnMRegisterImpl__v_; +text: .text%__1cHRetNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQmulD_reg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cOcompP_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMPerfDataList2T6M_v_; +text: .text%__1cHRetNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_LTGENodeFclone6kM_pnENode__; +text: .text%__1cNtestI_regNodeFclone6kM_pnENode__; +text: .text%__1cOClassPathEntry2t6M_v_; +text: .text%__1cVMoveF2I_reg_stackNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTjava_lang_ThrowableLset_message6FpnHoopDesc_2_v_; +text: .text%Unsafe_SetMemory; +text: .text%__1cRaddL_eReg_memNodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cRaddL_eReg_memNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIcp2bNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cFciEnvOrecord_failure6Mpkc_v_; +text: .text%__1cNcmovL_regNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorbHgenerate_exception_handler_common6Mpkc2i_pC_; +text: .text%__1cENodeHdel_out6Mp0_v_: ifg.o; +text: .text%__1cJAssemblerEfld16M_v_; +text: .text%__1cSmembar_acquireNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cJAssemblerFfld_x6MnHAddress__v_; +text: .text%__1cLConvD2FNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJAssemblerHfistp_d6MnHAddress__v_; +text: .text%__1cKstoreFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJAssemblerFimull6MpnMRegisterImpl_2_v_; +text: .text%__1cRInvocationCounterDdef6Fn0AFState_ipFnMmethodHandle_pnGThread__pC_v_; +text: .text%__1cOMacroAssemblerIfcmp2int6MpnMRegisterImpl_i_v_; +text: .text%__1cFciEnvXget_or_create_exception6MrpnI_jobject_nMsymbolHandle__pnKciInstance__; +text: .text%__1cMPerfDataList2t6Mi_v_; +text: .text%__1cLloadSSINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLVtableStubsScreate_vtable_stub6Fii_pnKVtableStub__; +text: .text%__1cRaddI_mem_eRegNodeFreloc6kM_i_; +text: .text%__1cUInterpreterGeneratorTgenerate_math_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cMLinkResolverbBlookup_method_in_interfaces6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cCosFyield6F_v_; +text: .text%__1cOCompilerThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cRComputeEntryStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cIDivFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSCardTableExtensionVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cSCardTableExtensionbEresize_covered_region_by_start6MnJMemRegion__v_; +text: .text%__1cJArgumentsMbuild_string6Fppcpkc_v_; +text: .text%__1cJArgumentsObuild_jvm_args6Fpkc_v_; +text: .text%__1cJArgumentsMadd_property6Fpkc_i_; +text: .text%__1cJAssemblerFfmulp6Mi_v_; +text: .text%__1cUInterpreterGeneratorbEgenerate_asm_interpreter_entry6Mi_pC_; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstanceKlass.o; +text: .text%__1cZInterpreterMacroAssemblerUprofile_virtual_call6MpnMRegisterImpl_22_v_; +text: .text%__1cNcmovL_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerWprofile_switch_default6MpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableMfast_xaccess6FnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerTprofile_switch_case6MpnMRegisterImpl_22_v_; +text: .text%__1cRaddI_mem_eRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_FindSignal; +text: .text%__1cKVtableStub2n6FIi_pv_; +text: .text%__1cSInterpreterRuntimebKthrow_ArrayIndexOutOfBoundsException6FpnKJavaThread_pci_v_; +text: .text%__1cOGenerateOopMapRdo_multianewarray6Mii_v_; +text: .text%__1cXNativeSignatureIteratorIdo_float6M_v_: interpreterRuntime.o; +text: .text%__1cLOptoRuntimeVgenerate_handler_blob6FpCi_pnNSafepointBlob__; +text: .text%JVM_RegisterSignal; +text: .text%__1cFParsePdo_lookupswitch6M_v_; +text: .text%__1cSaddF24_reg_memNodeQuse_cisc_RegMask6M_v_; +text: .text%__1cQCompilerCounters2t6MpkcipnGThread__v_; +text: .text%__1cJAssemblerEandl6MpnMRegisterImpl_2_v_; +text: .text%__1cNCompileBrokerUmake_compiler_thread6FpkcpnMCompileQdDueue_pnQCompilerCounters_pnGThread__pnOCompilerThread__; +text: .text%__1cNCompileBrokerUcompiler_thread_loop6F_v_; +text: .text%__1cOcmpF_cc_P6NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJAssemblerEcdql6M_v_; +text: .text%__1cMVM_OperationNdoit_prologue6M_i_: vm_operations.o; +text: .text%__1cUInterpreterGeneratorbDgenerate_stack_overflow_check6M_v_; +text: .text%__1cUInterpreterGeneratorXcheck_for_compiled_code6MrnFLabel__v_; +text: .text%__1cRCardTableModRefBSbCfind_covering_region_by_base6MpnIHeapWord__i_; +text: .text%__1cIci2bNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRCardTableModRefBSbAlargest_prev_committed_end6kMi_pnIHeapWord__; +text: .text%__1cMSysClassPathNreset_item_at6Mi_v_: arguments.o; +text: .text%__1cLconvI2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cFStateM_sub_Op_CmpF6MpknENode__v_; +text: .text%__1cRCardTableModRefBSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cOLibraryCallKitVinline_fp_conversions6MnIciMethodLIntrinsicId__i_; +text: .text%__1cJAssemblerDorl6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cHCompilePget_invoke_name6M_pnIciSymbol__; +text: .text%__1cFParseRdo_multianewarray6M_v_; +text: .text%__1cJAssemblerGfmul_d6MnHAddress__v_; +text: .text%__1cLVtableStubsFenter6FiiipnKVtableStub__v_; +text: .text%__1cZInterpreterMacroAssemblerLlock_object6MpnMRegisterImpl__v_; +text: .text%__1cLMoveF2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cRaddD_reg_imm1NodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cTMaskFillerForNative2t6MnMmethodHandle_pIi_v_: oopMapCache.o; +text: .text%__1cNTemplateTableLwide_istore6F_v_; +text: .text%__1cRaddD_reg_imm1NodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cISubFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNandI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOtailjmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVcompiler_thread_entry6FpnKJavaThread_pnGThread__v_: thread.o; +text: .text%__1cOCompilerThread2t6MpnMCompileQdDueue_pnQCompilerCounters__v_; +text: .text%__1cNReservedSpaceJlast_part6MI_0_; +text: .text%__1cNSafepointBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cJlookupOne6FpnHJNIEnv__pkcpnGThread__pnH_jclass__: jni.o; +text: .text%__1cUPSGenerationCounters2t6MpkciipnOPSVirtualSpace__v_; +text: .text%__1cNSafepointBlob2n6FII_pv_; +text: .text%__1cQmulD_reg_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTconvI2F_SSF_memNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPSVirtualSpace2t6MnNReservedSpace_I_v_; +text: .text%__1cFTypeDJis_finite6kM_i_; +text: .text%JVM_GetInterfaceVersion; +text: .text%__1cQOopMapCacheEntryPfill_for_native6M_v_; +text: .text%__1cNMemoryManager2t6M_v_; +text: .text%__1cSPSPromotionManager2t6M_v_; +text: .text%__1cOstackSlotIOperFscale6kM_i_: ad_i486.o; +text: .text%__1cOstackSlotIOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cOstackSlotIOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_i486.o; +text: .text%__1cNFingerprinterLfingerprint6M_X_: oopMapCache.o; +text: .text%__1cMnegF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveF2I_reg_stackNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSaddF24_reg_memNodeFreloc6kM_i_; +text: .text%__1cNTemplateTableGfconst6Fi_v_; +text: .text%__1cSdivD_reg_roundNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSafepointBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cPmovP_nocopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_CallStaticObjectMethodV: jni.o; +text: .text%__1cIci2bNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMOopTaskQdDueue2t6M_v_; +text: .text%__1cSCommandLineFlagsExJboolAtPut6FnXCommandLineFlagWithType_i_v_; +text: .text%__1cSCommandLineFlagsExKis_default6FnPCommandLineFlag__i_; +text: .text%__1cZInterpreterMacroAssemblerRgen_subtype_check6MpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cRaddI_mem_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%JVM_Available; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_2i_v_; +text: .text%__1cCosIjvm_path6Fpci_v_; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_2i_v_; +text: .text%__1cMOopTaskQdDueueKinitialize6M_v_; +text: .text%__1cPmovI_nocopyNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOtailjmpIndNodeHtwo_adr6kM_I_: ad_i486_misc.o; +text: .text%__1cIci2bNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cCosGgetenv6Fpkcpci_i_; +text: .text%__1cOtailjmpIndNodeGpinned6kM_i_: ad_i486_misc.o; +text: .text%jni_GetDoubleArrayRegion: jni.o; +text: .text%__1cIcp2bNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLconvI2BNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cXcmpL_reg_flags_EQdDNENodeFclone6kM_pnENode__; +text: .text%__1cKciTypeFlowLStateVectorRdo_multianewarray6MpnQciByteCodeStream__v_; +text: .text%__1cJAssemblerGfild_s6MnHAddress__v_; +text: .text%__1cJAssemblerGfstp_d6Mi_v_; +text: .text%__1cTMaskFillerForNativeLpass_object6M_v_: oopMapCache.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cJStubQdDueue2t6MpnNStubInterface_ipnFMutex_pkc_v_; +text: .text%__1cLMoveF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNStubGeneratorUgenerate_d2i_wrapper6MpC_1_: stubGenerator_i486.o; +text: .text%__1cTMaskFillerForNativeJpass_long6M_v_: oopMapCache.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cODeoptimizationVdeoptimize_dependents6F_i_; +text: .text%__1cNTemplateTableUinvokevirtual_helper6FpnMRegisterImpl_22_v_; +text: .text%__1cJAssemblerEfxch6Mi_v_; +text: .text%__1cJAssemblerFfprem6M_v_; +text: .text%__1cSvframeStreamCommonbFfill_in_compiled_inlined_sender6M_i_; +text: .text%__1cJAssemblerJfnstsw_ax6M_v_; +text: .text%__1cJAssemblerEsahf6M_v_; +text: .text%__1cQObjectStartArraySset_covered_region6MnJMemRegion__v_; +text: .text%__1cQObjectStartArrayKinitialize6MnJMemRegion__v_; +text: .text%__1cNVM_DeoptimizeEname6kM_pkc_: vm_operations.o; +text: .text%__1cOtailjmpIndNodeJnum_opnds6kM_I_: ad_i486_misc.o; +text: .text%__1cMVM_OperationNdoit_epilogue6M_v_: vm_operations.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC222i_v_; +text: .text%__1cJAssemblerEfchs6M_v_; +text: .text%__1cNVM_DeoptimizeEdoit6M_v_; +text: .text%__1cTAbstract_VM_VersionOvm_info_string6F_pkc_; +text: .text%__1cSaddF24_reg_immNodeFreloc6kM_i_; +text: .text%__1cJAssemblerEfabs6M_v_; +text: .text%__1cJStubQdDueueOregister_queue6Fp0_v_; +text: .text%__1cOMacroAssemblerPcorrected_idivl6MpnMRegisterImpl__i_; +text: .text%__1cMLinkResolverbEvtable_index_of_miranda_method6FnLKlassHandle_nMsymbolHandle_2pnGThread__i_; +text: .text%__1cRsubI_eReg_memNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFStatebB_sub_Op_PartialSubtypeCheck6MpknENode__v_; +text: .text%__1cNTemplateTableKdouble_cmp6Fi_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fi_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC22_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC_v_; +text: .text%__1cFStateL_sub_Op_OrL6MpknENode__v_; +text: .text%__1cPconvF2D_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFStateM_sub_Op_SubF6MpknENode__v_; +text: .text%__1cPOopTaskQdDueueSetOregister_queue6MipnMOopTaskQdDueue__v_; +text: .text%__1cMPeriodicTaskGenroll6M_v_; +text: .text%__1cNTemplateTableSgetfield_or_static6Fii_v_; +text: .text%__1cMPeriodicTask2t6MI_v_; +text: .text%__1cSestimate_path_freq6FpnENode__f_: loopnode.o; +text: .text%__1cFStateP_sub_Op_MoveL2D6MpknENode__v_; +text: .text%__1cNincI_eRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNTemplateTableHcastore6F_v_; +text: .text%__1cUGcThreadCountClosureJdo_thread6MpnGThread__v_; +text: .text%__1cWNonPrintingResourceObj2n6FInLResourceObjPallocation_type__pv_: memoryService.o; +text: .text%__1cPGCMemoryManagerXinitialize_gc_stat_info6M_v_; +text: .text%__1cNTemplateTableLwide_lstore6F_v_; +text: .text%__1cPGCMemoryManager2t6M_v_; +text: .text%__1cKGCStatInfo2t6Mi_v_; +text: .text%__1cJMarkSweepUAdjustPointerClosure2t6Mi_v_: markSweep.o; +text: .text%__1cNstoreImmBNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLloadSSINodeZcheck_for_anti_dependence6kM_i_: ad_i486_misc.o; +text: .text%__1cNTemplateTableDldc6Fi_v_; +text: .text%__1cNTemplateTableKif_nullcmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_acmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableGdconst6Fi_v_; +text: .text%__1cNTemplateTableGlconst6Fi_v_; +text: .text%__1cNTemplateTableKinitialize6F_v_; +text: .text%__1cNTemplateTableUjvmti_post_field_mod6Fii_v_; +text: .text%__1cJAssemblerEaddl6MnHAddress_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerFidivl6MpnMRegisterImpl__v_; +text: .text%__1cOmulF24_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cJAssemblerEmull6MnHAddress__v_; +text: .text%__1cJAssemblerDorl6MnHAddress_i_v_; +text: .text%__1cJAssemblerEsarl6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEshll6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerFshrdl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEshrl6MpnMRegisterImpl__v_; +text: .text%__1cTAbstract_VM_VersionXinternal_vm_info_string6F_pkc_; +text: .text%__1cTAbstract_VM_VersionKvm_release6F_pkc_; +text: .text%__1cJAssemblerFcpuid6M_v_; +text: .text%__1cJAssemblerEfldz6M_v_; +text: .text%__1cJAssemblerFfld_s6Mi_v_; +text: .text%__1cJAssemblerFfst_s6MnHAddress__v_; +text: .text%__1cJAssemblerFfst_d6MnHAddress__v_; +text: .text%__1cOaddF24_regNodeMcisc_version6Mi_pnIMachNode__; +text: .text%__1cNTemplateTableSputfield_or_static6Fii_v_; +text: .text%__1cSaddF24_reg_immNodeKconst_size6kM_i_: ad_i486_misc.o; +text: .text%__1cHMatcherQconvL2FSupported6F_ki_; +text: .text%__1cFStateP_sub_Op_ConvL2F6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_ConvL2D6MpknENode__v_; +text: .text%__1cGatomll6Fpkcpx_i_: arguments.o; +text: .text%__1cJArgumentsRcheck_memory_size6Fxx_n0AJArgsRange__; +text: .text%__1cJArgumentsVset_parallel_gc_flags6F_v_; +text: .text%__1cPconvD2F_regNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cJArgumentsRparse_memory_size6Fpkcpxx_n0AJArgsRange__; +text: .text%__1cFStateP_sub_Op_ConvD2F6MpknENode__v_; +text: .text%__1cHnmethodVinvalidate_osr_method6M_v_; +text: .text%__1cJArgumentsXPropertyList_unique_add6FppnOSystemProperty_pkcpc_v_; +text: .text%__1cQAgentLibraryList2t6M_v_: arguments.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: arrayKlassKlass.o; +text: .text%__1cJAssemblerEmovb6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cLklassVtableQindex_of_miranda6MpnNsymbolOopDesc_2_i_; +text: .text%__1cWStubGenerator_generate6FpnKCodeBuffer_i_v_; +text: .text%__1cPconvL2D_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: parse1.o; +text: .text%__1cPconvD2F_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNmulI_eRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cINegFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMFastLockNodeLis_FastLock6kM_pk0_: classes.o; +text: .text%__1cSInterpreterRuntimeMat_safepoint6FpnKJavaThread__v_; +text: .text%__1cOGenerateOopMapTadd_to_ref_init_set6Mi_v_; +text: .text%__1cIPSOldGenOgen_size_limit6M_I_; +text: .text%__1cIciObjectMis_classless6kM_i_: ciMethod.o; +text: .text%__1cIPSOldGenGresize6MI_v_; +text: .text%__1cMVirtualSpaceKinitialize6MnNReservedSpace_I_i_; +text: .text%__1cQorl_eReg_immNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNciMethodKlassEmake6F_p0_; +text: .text%__1cPconvL2F_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cUParallelScavengeHeapItop_addr6kM_ppnIHeapWord__: parallelScavengeHeap.o; +text: .text%__1cRInvocationCounterMreinitialize6Fi_v_; +text: .text%__1cIciMethod2t6MpnPciInstanceKlass_pnIciSymbol_4_v_; +text: .text%__1cFParseNfetch_monitor6MipnENode_2_2_; +text: .text%__1cMGCTaskThread2t6MpnNGCTaskManager_II_v_; +text: .text%__1cMGCTaskThreadFstart6M_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_native_entry6Mi_pC_; +text: .text%__1cMGCTaskThreadDrun6M_v_; +text: .text%__1cJLoadFNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cGThreadOis_Java_thread6kM_i_: gcTaskThread.o; +text: .text%__1cUInterpreterGeneratorLlock_method6M_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: universe.o; +text: .text%__1cXpartialSubtypeCheckNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cISubFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cISubFNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cIPSOldGenPinitialize_work6Mpkci_v_; +text: .text%lstat: perfMemory_solaris.o; +text: .text%__1cRComputeEntryStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cPfilename_to_pid6Fpkc_l_: perfMemory_solaris.o; +text: .text%Unsafe_CompareAndSwapObject; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cJCodeCachebGmake_marked_nmethods_not_entrant6F_v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpCpnMRegisterImpl__v_; +text: .text%__1cTis_directory_secure6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cZInterpreterMacroAssemblerSprofile_final_call6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerMprofile_call6MpnMRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerWdispatch_only_noverify6MnITosState__v_; +text: .text%__1cRCollectorCounters2t6Mpkci_v_; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_flag_at6MpnMRegisterImpl_i_v_; +text: .text%__1cZInterpreterMacroAssemblerSupdate_mdp_for_ret6MpnMRegisterImpl__v_; +text: .text%__1cHThreadsbFdeoptimized_wrt_marked_nmethods6F_v_; +text: .text%__1cZInterpreterMacroAssemblerLprofile_ret6MpnMRegisterImpl_2_v_; +text: .text%__1cJTimeStamp2t6M_v_: runtimeService.o; +text: .text%__1cIPSOldGenYinitialize_virtual_space6MnNReservedSpace_I_v_; +text: .text%__1cIPSOldGenKinitialize6MnNReservedSpace_Ipkci_v_; +text: .text%__1cMVirtualSpace2t6M_v_; +text: .text%__1cLOptoRuntimeSfetch_monitor_Type6F_pknITypeFunc__; +text: .text%__1cbCAbstractInterpreterGeneratorTgenerate_error_exit6Mpkc_pC_; +text: .text%__1cPciObjectFactoryTget_unloaded_method6MpnPciInstanceKlass_pnIciSymbol_4_pnIciMethod__; +text: .text%__1cNinstanceKlassSremove_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cTAbstractInterpreterKinitialize6F_v_; +text: .text%__1cLNamedThread2t6M_v_; +text: .text%__1cRMachSpillCopyNodeHsize_of6kM_I_: ad_i486.o; +text: .text%__1cLNamedThreadIset_name6MpkcE_v_; +text: .text%__1cMelapsedTimer2t6M_v_: psAdaptiveSizePolicy.o; +text: .text%__1cUPSAdaptiveSizePolicybQpromo_increment_with_supplement_aligned_up6MI_I_; +text: .text%__1cUPSAdaptiveSizePolicyPpromo_increment6MII_I_; +text: .text%__1cOMacroAssemblerElshr6MpnMRegisterImpl_2i_v_; +text: .text%__1cCosbCis_thread_cpu_time_supported6F_i_; +text: .text%__1cLConvD2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOMacroAssemblerIsave_eax6MpnMRegisterImpl__v_; +text: .text%jni_IsInstanceOf: jni.o; +text: .text%jni_CallStaticBooleanMethod: jni.o; +text: .text%__1cRCardTableModRefBSbCpar_chunk_heapword_alignment6F_I_: tenuredGeneration.o; +text: .text%jni_CallStaticVoidMethodV: jni.o; +text: .text%__1cSReferenceProcessor2t6MnJMemRegion_iii_v_; +text: .text%__1cCosWactive_processor_count6F_i_; +text: .text%jni_NewWeakGlobalRef: jni.o; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_float_field06FnJBasicType__pC_; +text: .text%__1cOMacroAssemblerLrestore_eax6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerFfremr6MpnMRegisterImpl__v_; +text: .text%__1cLMoveL2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKJNIHandlesQmake_weak_global6FnGHandle__pnI_jobject__; +text: .text%__1cHOrLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKReflectionbFbasic_type_arrayklass_to_mirror6FpnMklassOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cCosHrealloc6FpvI_1_; +text: .text%__1cOMacroAssemblerGsincos6Miii_v_; +text: .text%__1cEMIN24CI_6FTA0_0_: tenuredGeneration.o; +text: .text%__1cXPartialSubtypeCheckNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cCosNsigexitnum_pd6F_i_; +text: .text%__1cNGCTaskManagerKset_thread6MIpnMGCTaskThread__v_; +text: .text%__1cLConvL2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOPSVirtualSpace2t6M_v_; +text: .text%__1cNdefaultStreamMhas_log_file6M_i_; +text: .text%__1cUGenericGrowableArrayGgrow646Mi_v_; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: oopMapCache.o; +text: .text%__1cOPSVirtualSpaceKinitialize6MnNReservedSpace_I_i_; +text: .text%__1cYalign_to_allocation_size6FI_I_: heap.o; +text: .text%__1cWcheck_compare_clipping6FipnGIfNode_pnHConNode_rpnENode__i_: cfgnode.o; +text: .text%__1cLConvL2FNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cCosVatomic_xchg_bootstrap6Fipoi_i_; +text: .text%__1cUParallelScavengeHeapOresize_old_gen6MI_v_; +text: .text%__1cIciObjectOis_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cRcheck_if_clipping6FpknKRegionNode_rpnGIfNode_5_i_: cfgnode.o; +text: .text%__1cHOrLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cNTemplateTableEdup26F_v_; +text: .text%__1cHThreadsJcreate_vm6FpnOJavaVMInitArgs_pi_i_; +text: .text%__1cOThreadCriticalKinitialize6F_v_; +text: .text%__1cIUniverseHgenesis6FpnGThread__v_; +text: .text%__1cKJavaThread2t6M_v_; +text: .text%__1cNTemplateTableHdup2_x16F_v_; +text: .text%__1cNTemplateTableHdup2_x26F_v_; +text: .text%Unsafe_SetNativeLong; +text: .text%__1cNTemplateTableEswap6F_v_; +text: .text%__cplus_fini_at_exit: CCrti.o; +text: .text%__1cNTemplateTableGdup_x16F_v_; +text: .text%__1cNTemplateTableDpop6F_v_; +text: .text%__1cIUniverseUreinitialize_itables6F_v_; +text: .text%__1cNReservedSpace2t6MIIipc_v_; +text: .text%__1cNuniverse_init6F_i_; +text: .text%__1cIUniversePinitialize_heap6F_i_; +text: .text%__1cOuniverse2_init6F_v_; +text: .text%__1cNTemplateTableEpop26F_v_; +text: .text%__1cNTemplateTableIgetfield6Fi_v_; +text: .text%__1cNTemplateTableDdup6F_v_; +text: .text%__1cSuniverse_post_init6F_v_; +text: .text%__1cIUniverseNfixup_mirrors6FpnGThread__v_; +text: .text%__1cNTemplateTableGdup_x26F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vmStructs.o; +text: .text%__1cNTemplateTableElmul6F_v_; +text: .text%__1cNTemplateTableEldiv6F_v_; +text: .text%__1cNTemplateTableElrem6F_v_; +text: .text%__1cNTemplateTableElshl6F_v_; +text: .text%__1cNTemplateTableElshr6F_v_; +text: .text%__1cNTemplateTableFlushr6F_v_; +text: .text%__1cNTemplateTableElcmp6F_v_; +text: .text%__1cNTemplateTableJwide_iinc6F_v_; +text: .text%__1cNTemplateTableEiinc6F_v_; +text: .text%__1cNTemplateTableEineg6F_v_; +text: .text%__1cNTemplateTableElneg6F_v_; +text: .text%__1cNTemplateTableEdneg6F_v_; +text: .text%__1cNTemplateTableEfneg6F_v_; +text: .text%__1cNTemplateTableEirem6F_v_; +text: .text%__1cIUniverseYcompute_base_vtable_size6F_v_; +text: .text%__1cNTemplateTableRfast_binaryswitch6F_v_; +text: .text%__1cNReservedSpaceUpage_align_size_down6FI_I_; +text: .text%__1cQVMOperationQdDueue2t6M_v_; +text: .text%__1cJvmSymbolsKinitialize6FpnGThread__v_; +text: .text%__1cNReservedSpaceYallocation_align_size_up6FI_I_; +text: .text%__1cNTemplateTableRfast_linearswitch6F_v_; +text: .text%__1cNTemplateTableMlookupswitch6F_v_; +text: .text%__1cOvmStructs_init6F_v_; +text: .text%__1cNTemplateTableLtableswitch6F_v_; +text: .text%__1cNTemplateTableIwide_ret6F_v_; +text: .text%Unsafe_PageSize; +text: .text%__1cNTemplateTableDret6F_v_; +text: .text%Unsafe_FreeMemory; +text: .text%__1cNTemplateTableEidiv6F_v_; +text: .text%__1cJTimeStampMmilliseconds6kM_x_; +text: .text%__1cOtypeArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cHVM_ExitEdoit6M_v_; +text: .text%__1cHVM_ExitEname6kM_pkc_: vm_operations.o; +text: .text%__1cNThreadServiceEinit6F_v_; +text: .text%__1cSThreadLocalStorageEinit6F_v_; +text: .text%__1cNCollectedHeapYlarge_typearray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cTAbstract_VM_VersionHvm_name6F_pkc_; +text: .text%__1cTAbstract_VM_VersionJvm_vendor6F_pkc_; +text: .text%__1cIVMThreadEloop6M_v_; +text: .text%__1cPGlobalTLABStats2t6M_v_; +text: .text%__1cWThreadLocalAllocBufferWstartup_initialization6F_v_; +text: .text%__1cIVMThreadDrun6M_v_; +text: .text%__1cSThreadLocalStoragebCgenerate_code_for_get_thread6F_v_; +text: .text%__1cTtypeArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cTtypeArrayKlassKlassVoop_is_typeArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cKTypeOopPtrFxdual6kM_pknEType__; +text: .text%__1cKTypeOopPtrEmake6FnHTypePtrDPTR_i_pk0_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vm_version.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cKVM_VersionWget_processor_features6F_v_; +text: .text%__1cKVM_VersionKinitialize6F_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: tenuredGeneration.o; +text: .text%__1cNTemplateTableKinstanceof6F_v_; +text: .text%__1cNTemplateTableJcheckcast6F_v_; +text: .text%__1cNTemplateTableLarraylength6F_v_; +text: .text%__1cNTemplateTableJanewarray6F_v_; +text: .text%__1cNTemplateTableInewarray6F_v_; +text: .text%__1cNTemplateTableE_new6F_v_; +text: .text%__1cNTemplateTablePinvokeinterface6Fi_v_; +text: .text%__1cNTemplateTableRfast_invokevfinal6Fi_v_; +text: .text%__1cNTemplateTableMinvokestatic6Fi_v_; +text: .text%__1cNTemplateTableNinvokespecial6Fi_v_; +text: .text%__1cNTemplateTableNinvokevirtual6Fi_v_; +text: .text%__1cNTemplateTableL_breakpoint6F_v_; +text: .text%__1cNTemplateTableGathrow6F_v_; +text: .text%__1cNTemplateTableMmonitorenter6F_v_; +text: .text%__1cNTemplateTableLmonitorexit6F_v_; +text: .text%__1cNTemplateTableEwide6F_v_; +text: .text%__1cNTemplateTableOmultianewarray6F_v_; +text: .text%__1cETypeRInitialize_shared6FpnHCompile__v_; +text: .text%__1cHVM_ExitNset_vm_exited6F_i_; +text: .text%__1cHVM_ExitbJwait_for_threads_in_native_to_block6F_i_; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cNTemplateTableJputstatic6Fi_v_; +text: .text%__1cNWatcherThread2t6M_v_; +text: .text%__1cNWatcherThreadDrun6M_v_; +text: .text%__1cNWatcherThreadFstart6F_v_; +text: .text%__1cVverificationType_exit6F_v_; +text: .text%__1cQVerificationTypeKinitialize6F_v_; +text: .text%__1cQVerificationTypeIfinalize6F_v_; +text: .text%__1cNTemplateTableIputfield6Fi_v_; +text: .text%__1cNWatcherThreadEstop6F_v_; +text: .text%__1cNTemplateTableJgetstatic6Fi_v_; +text: .text%__1cPVM_Version_init6F_v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cRAllocateTLSOffset6F_v_: threadLS_solaris_i486.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cYVM_Version_StubGeneratorTgenerate_getPsrInfo6M_pC_: vm_version_i486.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cLVtableStubsKinitialize6F_v_; +text: .text%__1cVverificationType_init6F_v_; +text: .text%__1cIVMThread2t6M_v_; +text: .text%__1cGThreadWset_as_starting_thread6M_i_; +text: .text%__1cIVMThreadGcreate6F_v_; +text: .text%__1cbBcreate_initial_thread_group6FpnGThread__nGHandle__: thread.o; +text: .text%__1cVcreate_initial_thread6FnGHandle_pnKJavaThread_pnGThread__pnHoopDesc__: thread.o; +text: .text%__1cbAcall_initializeSystemClass6FpnGThread__v_: thread.o; +text: .text%__1cWreset_vm_info_property6FpnGThread__v_: thread.o; +text: .text%JVM_RegisterUnsafeMethods; +text: .text%__1cQvtableStubs_init6F_v_; +text: .text%__1cFVTuneEexit6F_v_; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: typeArrayKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlassKlass.o; +text: .text%__1cKvtune_init6F_v_; +text: .text%__1cIUniversePcheck_alignment6FIIpkc_v_; +text: .text%__1cFKlassPoop_is_instance6kM_i_: constantPoolKlass.o; +text: .text%__1cSinstanceKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFJNIidKdeallocate6Fp0_v_; +text: .text%__1cNinstanceKlassZrelease_C_heap_structures6M_v_; +text: .text%__1cSset_init_completed6F_v_; +text: .text%__1cMexit_globals6F_v_; +text: .text%__1cMinit_globals6F_i_; +text: .text%__1cPvm_init_globals6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: indexSet.o; +text: .text%__1cLicache_init6F_v_; +text: .text%__1cWInlineCacheBuffer_init6F_v_; +text: .text%__1cRInlineCacheBufferKinitialize6F_v_; +text: .text%__1cDhpiYinitialize_get_interface6FpnIvm_calls__v_; +text: .text%__1cDhpiZinitialize_socket_library6F_i_; +text: .text%__1cDhpiKinitialize6F_i_; +text: .text%__1cLlog2_intptr6Fi_i_: heap.o; +text: .text%__1cICodeHeapFclear6M_v_; +text: .text%__1cICodeHeapHreserve6MIII_i_; +text: .text%__1cICodeHeap2t6M_v_; +text: .text%__1cSinstanceKlassKlassUoop_is_instanceKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cSInterpreterRuntimeSupdate_mdp_for_ret6FpnKJavaThread_i_v_; +text: .text%__1cSInterpreterRuntimeWcreate_klass_exception6FpnKJavaThread_pcpnHoopDesc__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter.o; +text: .text%__1cbCAbstractInterpreterGeneratorbCset_safepoints_for_all_bytes6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbEset_entry_points_for_all_bytes6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorMgenerate_all6M_v_; +text: .text%__1cbCAbstractInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cQinterpreter_init6F_v_; +text: .text%__1cZInterpreterMacroAssemblerbFset_method_data_pointer_for_bcp6M_v_; +text: .text%__1cZInterpreterMacroAssemblerUdispatch_only_normal6MnITosState__v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpCpnMRegisterImpl_33_v_; +text: .text%__1cVInterfaceSupport_init6F_v_; +text: .text%__1cQinstanceRefKlassZupdate_nonstatic_oop_maps6FpnMklassOopDesc__v_; +text: .text%__1cSinstanceKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: instanceKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cSCommandLineFlagsExKuintxAtPut6FnXCommandLineFlagWithType_I_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: fprofiler.o; +text: .text%__1cMelapsedTimer2t6M_v_: fprofiler.o; +text: .text%__1cScheck_ThreadShadow6F_v_; +text: .text%__1cNeventlog_init6F_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: dictionary.o; +text: .text%__1cKDictionaryKclasses_do6MpFpnMklassOopDesc__v_v_; +text: .text%__1cKDictionaryKfree_entry6MpnPDictionaryEntry__v_; +text: .text%__1cKDictionary2t6Mi_v_; +text: .text%__1cFStateQ_sub_Op_TailJump6MpknENode__v_; +text: .text%__1cFStateM_sub_Op_NegF6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_MoveF2I6MpknENode__v_; +text: .text%__1cFStateO_sub_Op_CMoveL6MpknENode__v_; +text: .text%__1cODeoptimizationTload_class_by_index6FnSconstantPoolHandle_i_v_; +text: .text%__1cODeoptimizationTload_class_by_index6FnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKScopeValuePis_constant_int6kM_i_: debugInfo.o; +text: .text%__1cUConstantOopReadValuePis_constant_oop6kM_i_: debugInfo.o; +text: .text%__1cUConstantOopReadValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cWconstantPoolCacheKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: cpCacheKlass.o; +text: .text%__1cFframeVinterpreter_frame_mdp6kM_pC_; +text: .text%__1cRcheck_basic_types6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: generateOopMap.o; +text: .text%__1cRComputeEntryStackIdo_float6M_v_: generateOopMap.o; +text: .text%__1cNCellTypeStateLmake_bottom6F_0_: generateOopMap.o; +text: .text%__1cNCellTypeStateImake_top6F_0_: generateOopMap.o; +text: .text%__1cMelapsedTimer2t6M_v_: generateOopMap.o; +text: .text%__1cWResolveOopMapConflictsUdo_potential_rewrite6MpnGThread__nMmethodHandle__; +text: .text%__1cJGenRemSetYmax_alignment_constraint6Fn0AEName__I_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: genCollectedHeap.o; +text: .text%__1cTAssertIsPermClosure2t6M_v_: genCollectedHeap.o; +text: .text%__1cRAlwaysTrueClosure2t6M_v_: genCollectedHeap.o; +text: .text%__1cKNoopGCTaskQcreate_on_c_heap6F_p0_; +text: .text%__1cNGCTaskManagerKthreads_do6MpnNThreadClosure__v_; +text: .text%__1cNGCTaskManagerKinitialize6M_v_; +text: .text%__1cNGCTaskManager2t6MI_v_; +text: .text%__1cXSynchronizedGCTaskQdDueue2t6MpnLGCTaskQdDueue_pnFMutex__v_; +text: .text%__1cLGCTaskQdDueueQcreate_on_c_heap6F_p0_; +text: .text%__1cHGCStats2t6M_v_; +text: .text%__1cQGCPolicyCounters2t6Mpkcii_v_; +text: .text%__1cXSignatureHandlerLibraryQset_handler_blob6F_pC_; +text: .text%JVM_MaxMemory; +text: .text%JVM_Halt; +text: .text%JVM_InitProperties; +text: .text%__1cQjni_handles_init6F_v_; +text: .text%__1cKJNIHandlesKinitialize6F_v_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_double_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_float_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_long_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbBgenerate_fast_get_int_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_short_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_char_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_byte_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbFgenerate_fast_get_boolean_field6F_pC_; +text: .text%JNI_CreateJavaVM; +text: .text%__1cVquicken_jni_functions6F_v_; +text: .text%__1cbDinitializeDirectBufferSupport6FpnHJNIEnv___i_: jni.o; +text: .text%lookupDirectBufferClasses: jni.o; +text: .text%__1cXjni_GetDoubleField_addr6F_pC_; +text: .text%JVM_GetClassDeclaredMethods; +text: .text%__1cLJvmtiExportNpost_vm_start6F_v_; +text: .text%__1cLJvmtiExportQenter_live_phase6F_v_; +text: .text%__1cLJvmtiExportRenter_start_phase6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEventController.o; +text: .text%__1cUJvmtiEventControllerIvm_death6F_v_; +text: .text%__1cUJvmtiEventControllerHvm_init6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_start6F_v_; +text: .text%__1cRJvmtiEventEnabledFclear6M_v_; +text: .text%__1cRJvmtiEventEnabled2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEnvBase.o; +text: .text%__1cWNonPrintingResourceObj2n6FInLResourceObjPallocation_type__pv_: jvmtiEnvBase.o; +text: .text%__1cNGrowableArray4CpnMJvmtiEnvBase__2t6Mii_v_: jvmtiEnvBase.o; +text: .text%JVM_SupportsCX8; +text: .text%__1cbEinitialize_converter_functions6F_v_; +text: .text%JVM_Socket; +text: .text%JVM_InitializeSocketLibrary; +text: .text%__1cWjni_GetFloatField_addr6F_pC_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectPcompute_offsets6F_v_; +text: .text%__1cVjava_lang_ThreadGroupPcompute_offsets6F_v_; +text: .text%__1cQjava_lang_ThreadPcompute_offsets6F_v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cHvm_exit6Fi_v_; +text: .text%__1cLbefore_exit6FpnKJavaThread__v_; +text: .text%__1cQprint_statistics6F_v_; +text: .text%__1cWinvocationCounter_init6F_v_; +text: .text%__1cLlog2_intptr6Fi_i_: interpreter_i486.o; +text: .text%__1cUInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_throw_exception6M_v_; +text: .text%__1cUInterpreterGeneratorXgenerate_accessor_entry6M_pC_; +text: .text%__1cUInterpreterGeneratorUgenerate_empty_entry6M_pC_; +text: .text%__1cUInterpreterGeneratorXgenerate_abstract_entry6M_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbFgenerate_slow_signature_handler6M_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbMgenerate_ArrayIndexOutOfBounds_handler6Mpkc_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_StackOverflowError_handler6M_pC_; +text: .text%__1cXNativeSignatureIteratorHdo_byte6M_v_: interpreterRuntime.o; +text: .text%__1cYjava_lang_reflect_MethodPcompute_offsets6F_v_; +text: .text%__1cVjni_GetLongField_addr6F_pC_; +text: .text%__1cUjni_GetIntField_addr6F_pC_; +text: .text%__1cWjni_GetShortField_addr6F_pC_; +text: .text%__1cVjni_GetCharField_addr6F_pC_; +text: .text%__1cVjni_GetByteField_addr6F_pC_; +text: .text%__1cYjni_GetBooleanField_addr6F_pC_; +text: .text%jni_ToReflectedMethod: jni.o; +text: .text%__1cQjavaClasses_init6F_v_; +text: .text%__1cLJavaClassesPcompute_offsets6F_v_; +text: .text%__1cLJavaClassesbAcompute_hard_coded_offsets6F_v_; +text: .text%__1cZsun_misc_AtomicLongCSImplPcompute_offsets6F_v_; +text: .text%__1cPjava_nio_BufferPcompute_offsets6F_v_; +text: .text%__1cQjava_lang_SystemPcompute_offsets6F_v_; +text: .text%__1cbIjava_security_AccessControlContextPcompute_offsets6F_v_; +text: .text%__1cYsun_reflect_ConstantPoolPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldPcompute_offsets6F_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPcompute_offsets6F_v_; +text: .text%__1cYjava_lang_reflect_MethodNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: cpCacheKlass.o; +text: .text%__1cJAssemblerEmull6MpnMRegisterImpl__v_; +text: .text%__1cJAssemblerEadcl6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerEadcl6MpnMRegisterImpl_i_v_; +text: .text%__1cJAssemblerGmovsxw6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerGmovsxb6MpnMRegisterImpl_2_v_; +text: .text%__1cParrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: arrayKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: arrayKlassKlass.o; +text: .text%__1cParrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: arguments.o; +text: .text%__1cJArgumentsWPropertyList_get_value6FpnOSystemProperty_pkc_4_; +text: .text%__1cJArgumentsFparse6FpknOJavaVMInitArgs__i_; +text: .text%__1cJArgumentsbSparse_java_tool_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsbNparse_java_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsVfinalize_vm_init_args6FpnMSysClassPath_i_i_; +text: .text%__1cJArgumentsWparse_each_vm_init_arg6FpknOJavaVMInitArgs_pnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsSparse_vm_init_args6FpknOJavaVMInitArgs__i_; +text: .text%__1cJArgumentsZcheck_vm_args_consistency6F_i_; +text: .text%__1cJArgumentsbOparse_java_compiler_environment_variable6F_v_; +text: .text%__1cJAssemblerEsbbl6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerJdecrement6MpnMRegisterImpl_i_v_; +text: .text%__1cOMacroAssemblerLextend_sign6MpnMRegisterImpl_2_v_; +text: .text%__1cJAssemblerFfaddp6Mi_v_; +text: .text%__1cJAssemblerGfdivrp6Mi_v_; +text: .text%__1cJAssemblerHfdivr_d6MnHAddress__v_; +text: .text%__1cJAssemblerHfdivr_s6MnHAddress__v_; +text: .text%__1cJAssemblerGfmul_s6MnHAddress__v_; +text: .text%__1cJAssemblerHfsubr_d6MnHAddress__v_; +text: .text%__1cJAssemblerHfsubr_s6MnHAddress__v_; +text: .text%__1cJAssemblerGfadd_d6MnHAddress__v_; +text: .text%__1cJAssemblerGfadd_s6MnHAddress__v_; +text: .text%__1cJAssemblerFfsqrt6M_v_; +text: .text%__1cJAssemblerEfcos6M_v_; +text: .text%__1cJAssemblerEfsin6M_v_; +text: .text%__1cJAssemblerEsetb6Mn0AJCondition_pnMRegisterImpl__v_; +text: .text%__1cJAssemblerExchg6MpnMRegisterImpl_nHAddress__v_; +text: .text%__1cJAssemblerEsubl6MnHAddress_i_v_; +text: .text%__1cJAssemblerFshldl6MpnMRegisterImpl_2_v_; +text: .text%__1cJArgumentsUset_ergonomics_flags6F_v_; +text: .text%__1cHi2sNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHi2bNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreBNodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cNstoreImmINodeErule6kM_I_: ad_i486_misc.o; +text: .text%__1cLconvP2BNodePoper_input_base6kM_I_: ad_i486_misc.o; +text: .text%__1cOtailjmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_i486_expand.o; +text: .text%__1cRaddL_eReg_memNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLconvP2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIcp2bNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_i486.o; +text: .text%__1cTconvI2F_SSF_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQmulD_reg_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOtailjmpIndNodeFreloc6kM_i_; +text: .text%__1cTconvI2F_SSF_memNodeFreloc6kM_i_; +text: .text%__1cQmulD_reg_memNodeFreloc6kM_i_; +text: .text%__1cQaccessFlags_init6F_v_; +text: .text%__1cPconvI2L_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJArgumentsbBset_cms_and_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsTset_parnew_gc_flags6F_v_; +text: .text%__1cQno_shared_spaces6F_v_: arguments.o; +text: .text%__1cJArgumentsMget_property6Fpkc_2_; +text: .text%__1cJArgumentsVprocess_settings_file6Fpkcii_i_; +text: .text%__1cMSysClassPathQadd_jars_to_path6Fpcpkc_1_; +text: .text%__1cMSysClassPathPexpand_endorsed6M_v_; +text: .text%__1cMSysClassPath2T6M_v_; +text: .text%__1cMSysClassPath2t6Mpkc_v_; +text: .text%__1cJArgumentsWinit_system_properties6F_v_; +text: .text%__1cFChunkbDstart_chunk_pool_cleaner_task6F_v_; +text: .text%__1cOchunkpool_init6F_v_; +text: .text%__1cWAdjoiningVirtualSpaces2t6MnNReservedSpace_III_v_; +text: .text%__1cUAdjoiningGenerations2t6MnNReservedSpace_IIIIIII_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_i486_pipeline.o; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCpnMRegisterImpl__v_; +text: .text%__1cOcompiler2_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compilationPolicy.o; +text: .text%__1cMelapsedTimer2t6M_v_: compilationPolicy.o; +text: .text%__1cRCompilationPolicyUcompleted_vm_startup6F_v_; +text: .text%__1cWcompilationPolicy_init6F_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyUinitialize_size_info6M_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyQinitialize_flags6M_v_; +text: .text%__1cNCollectedHeap2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeCache.o; +text: .text%__1cOcodeCache_init6F_v_; +text: .text%__1cJCodeCacheKinitialize6F_v_; +text: .text%__1cNExceptionBlob2n6FII_pv_; +text: .text%__1cNExceptionBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNExceptionBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cQUncommonTrapBlob2n6FII_pv_; +text: .text%__1cQUncommonTrapBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cQUncommonTrapBlob2t6MpnKCodeBuffer_ipnJOopMapSet_i_v_; +text: .text%__1cSDeoptimizationBlob2n6FII_pv_; +text: .text%__1cNCompileBrokerQcompilation_init6FpnQAbstractCompiler__v_; +text: .text%__1cWconstantPoolCacheKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cRconstantPoolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constantPoolKlass.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: constantPoolKlass.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: constantPoolKlass.o; +text: .text%__1cRconstantPoolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cQconstMethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassOset_alloc_size6MI_v_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cTcompilerOracle_init6F_v_; +text: .text%__1cOCompilerOracleRparse_from_string6Fpkc_v_; +text: .text%__1cOCompilerOraclePparse_from_file6F_v_; +text: .text%__1cHcc_file6F_pkc_: compilerOracle.o; +text: .text%__1cOCompilerOracleOread_from_line6Fpc_v_; +text: .text%__1cVcompiledICHolderKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: compiledICHolderKlass.o; +text: .text%__1cVcompiledICHolderKlassOset_alloc_size6MI_v_: compiledICHolderKlass.o; +text: .text%__1cVcompiledICHolderKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compileBroker.o; +text: .text%__1cNCompileBrokerQset_should_block6F_v_; +text: .text%__1cNCompileBrokerVinit_compiler_threads6Fi_v_; +text: .text%__1cSDeoptimizationBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_iiii_p0_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: cfgnode.o; +text: .text%__1cRCardTableModRefBSbBct_max_alignment_constraint6F_I_; +text: .text%__1cRCardTableModRefBS2t6MnJMemRegion_i_v_; +text: .text%__1cMTailJumpNode2t6MpnENode_22222_v_; +text: .text%__1cKC2CompilerKinitialize6M_v_; +text: .text%__1cHCompileRpd_compiler2_init6F_v_; +text: .text%__1cJBytecodesNpd_initialize6F_v_; +text: .text%__1cObytecodes_init6F_v_; +text: .text%__1cJBytecodesKinitialize6F_v_; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_; +text: .text%__1cOMacroAssemblerQsign_extend_byte6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerRsign_extend_short6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerIlcmp2int6MpnMRegisterImpl_222_v_; +text: .text%__1cOMacroAssemblerElshl6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerElmul6Mii_v_; +text: .text%__1cOMacroAssemblerElneg6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerGc2bool6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpCpnMRegisterImpl_3_v_; +text: .text%__1cMciArrayKlass2t6MpnIciSymbol_ipnHciKlass__v_; +text: .text%__1cSDeoptimizationBlob2t6MpnKCodeBuffer_ipnJOopMapSet_iiii_v_; +text: .text%__1cLMoveF2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cTClassLoadingServiceVnotify_class_unloaded6FpnNinstanceKlass_i_v_; +text: .text%__1cTClassLoadingServiceEinit6F_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: classLoader.o; +text: .text%__1cQclassLoader_init6F_v_; +text: .text%__1cLClassLoaderVcompute_Object_vtable6F_i_; +text: .text%__1cLClassLoaderKinitialize6F_v_; +text: .text%__1cLClassLoaderZcreate_package_info_table6F_v_; +text: .text%__1cLClassLoaderQload_zip_library6F_v_; +text: .text%__1cLClassLoaderbBsetup_bootstrap_search_path6F_v_; +text: .text%__1cPClassFileParserYjava_lang_Class_fix_post6Mpi_v_; +text: .text%__1cPClassFileParserXjava_lang_Class_fix_pre6MpnOobjArrayHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cPClassFileParserbFjava_lang_ref_Reference_fix_pre6MpnPtypeArrayHandle_nSconstantPoolHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cRciArrayKlassKlassUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cPciObjectFactoryTinit_shared_objects6M_v_; +text: .text%__1cUciObjArrayKlassKlassEmake6F_p0_; +text: .text%__1cPciObjArrayKlass2t6MpnIciSymbol_pnHciKlass_i_v_; +text: .text%__1cMciKlassKlassEmake6F_p0_; +text: .text%__1cLJvmtiExportTpost_vm_initialized6F_v_; +text: .text%__1cNRegisterSaverYrestore_result_registers6FpnOMacroAssembler__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: runtimeService.o; +text: .text%__1cORuntimeServiceYrecord_application_start6F_v_; +text: .text%__1cORuntimeServiceEinit6F_v_; +text: .text%__1cLOptoRuntimeVhandle_exception_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray5_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray4_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray3_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray2_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray1_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeIgenerate6FpnFciEnv__v_; +text: .text%__1cWResolveOopMapConflictsOreport_results6kM_i_: rewriter.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: relocInfo.o; +text: .text%__1cQRelocationHolder2t6M_v_: relocInfo.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: regmask.o; +text: .text%__1cNSharedRuntimeTgenerate_deopt_blob6F_v_; +text: .text%__1cLStatSamplerbMcreate_system_property_instrumentation6FpnGThread__v_; +text: .text%__1cLStatSamplerHdestroy6F_v_; +text: .text%__1cLStatSamplerJdisengage6F_v_; +text: .text%__1cLStatSamplerGengage6F_v_; +text: .text%__1cLStatSamplerKinitialize6F_v_; +text: .text%__1cNcarSpace_init6F_v_; +text: .text%__1cICarSpaceEinit6F_v_; +text: .text%__1cNSharedRuntimebIinitialize_StrictMath_entry_points6F_v_; +text: .text%__1cNSharedRuntimeUlookup_function_DD_D6FrpFpnHJNIEnv__pnH_jclass_dd_dpkc_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedHeap.o; +text: .text%__1cTAssertIsPermClosure2t6M_v_: sharedHeap.o; +text: .text%__1cLOptoRuntimeYgenerate_arraycopy_stubs6F_v_; +text: .text%__1cLOptoRuntimebPgenerate_polling_page_return_handler_blob6F_v_; +text: .text%__1cLOptoRuntimebSgenerate_polling_page_safepoint_handler_blob6F_v_; +text: .text%__1cLOptoRuntimebPgenerate_illegal_instruction_handler_blob6F_v_; +text: .text%__1cLOptoRuntimeUsetup_exception_blob6F_v_; +text: .text%__1cLOptoRuntimeWfill_in_exception_blob6F_v_; +text: .text%__1cLOptoRuntimebBgenerate_uncommon_trap_blob6F_v_; +text: .text%__1cHRegMask2t6Miiiii_v_: regmask.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psMarkSweep.o; +text: .text%__1cMelapsedTimer2t6M_v_: psMarkSweep.o; +text: .text%__1cTPSAlwaysTrueClosure2t6M_v_: psMarkSweep.o; +text: .text%__1cLPSMarkSweepKinitialize6F_v_; +text: .text%__1cbAPSGCAdaptivePolicyCounters2t6MpkciipnUPSAdaptiveSizePolicy__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psAdaptiveSizePolicy.o; +text: .text%__1cUPSAdaptiveSizePolicy2t6MIIIIIddI_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: placeholders.o; +text: .text%__1cQPlaceholderTable2t6Mi_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: phase.o; +text: .text%__1cKPerfMemoryUdelete_memory_region6F_v_; +text: .text%__1cKPerfMemoryUcreate_memory_region6FI_v_; +text: .text%__1cUdelete_shared_memory6FpcI_v_: perfMemory_solaris.o; +text: .text%__1cUcreate_shared_memory6FI_pc_: perfMemory_solaris.o; +text: .text%__1cSmmap_create_shared6FI_pc_: perfMemory_solaris.o; +text: .text%__1cbAcreate_sharedmem_resources6Fpkc1I_i_: perfMemory_solaris.o; +text: .text%__1cRmake_user_tmp_dir6Fpkc_i_: perfMemory_solaris.o; +text: .text%__1cbBcleanup_sharedmem_resources6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cIPSOldGen2t6MnNReservedSpace_IIIIpkci_v_; +text: .text%__1cSReferenceProcessorMinit_statics6F_v_; +text: .text%__1cXreferenceProcessor_init6F_v_; +text: .text%__1cURecompilationMonitorbGstart_recompilation_monitor_task6F_v_; +text: .text%__1cKPSYoungGenUset_space_boundaries6MII_v_; +text: .text%__1cKPSYoungGenbGcompute_initial_space_boundaries6M_v_; +text: .text%__1cKPSYoungGenPinitialize_work6M_v_; +text: .text%__1cKPSYoungGenKinitialize6MnNReservedSpace_I_v_; +text: .text%__1cKPSYoungGenYinitialize_virtual_space6MnNReservedSpace_I_v_; +text: .text%__1cKPSYoungGen2t6MIII_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psScavenge.o; +text: .text%__1cMelapsedTimer2t6M_v_: psScavenge.o; +text: .text%__1cKPSScavengeKinitialize6F_v_; +text: .text%__1cPOopTaskQdDueueSet2t6Mi_v_: psPromotionManager.o; +text: .text%__1cSPSPromotionManagerKinitialize6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psPromotionLAB.o; +text: .text%__1cMarrayOopDescLheader_size6FnJBasicType__i_: psPromotionLAB.o; +text: .text%__1cRalign_object_size6Fi_i_: psPromotionLAB.o; +text: .text%__1cJPSPermGen2t6MnNReservedSpace_IIIIpkci_v_; +text: .text%__1cIPSOldGen2t6MIIIpkci_v_; +text: .text%__1cLStatSamplerUcreate_misc_perfdata6F_v_; +text: .text%__1cNTemplateTableGbaload6F_v_; +text: .text%__1cNTemplateTableGaaload6F_v_; +text: .text%__1cNTemplateTableGdaload6F_v_; +text: .text%__1cNTemplateTableGfaload6F_v_; +text: .text%__1cNTemplateTableGlaload6F_v_; +text: .text%__1cNTemplateTableGiaload6F_v_; +text: .text%__1cNTemplateTableKwide_aload6F_v_; +text: .text%__1cNTemplateTableKwide_dload6F_v_; +text: .text%__1cNTemplateTableKwide_fload6F_v_; +text: .text%__1cNTemplateTableKwide_lload6F_v_; +text: .text%__1cNTemplateTableKwide_iload6F_v_; +text: .text%__1cNTemplateTableFaload6F_v_; +text: .text%__1cNTemplateTableFdload6F_v_; +text: .text%__1cNTemplateTableFfload6F_v_; +text: .text%__1cNTemplateTableFlload6F_v_; +text: .text%__1cNTemplateTableKfast_iload6F_v_; +text: .text%__1cNTemplateTableLfast_iload26F_v_; +text: .text%__1cNTemplateTableFiload6F_v_; +text: .text%__1cNTemplateTableGcaload6F_v_; +text: .text%__1cNTemplateTableHsastore6F_v_; +text: .text%__1cNTemplateTableHbastore6F_v_; +text: .text%__1cNTemplateTableHaastore6F_v_; +text: .text%__1cNTemplateTableHdastore6F_v_; +text: .text%__1cNTemplateTableHfastore6F_v_; +text: .text%__1cNTemplateTableHlastore6F_v_; +text: .text%__1cNTemplateTableHiastore6F_v_; +text: .text%__1cNTemplateTableLwide_astore6F_v_; +text: .text%__1cNTemplateTableLwide_dstore6F_v_; +text: .text%__1cNTemplateTableLwide_fstore6F_v_; +text: .text%__1cNTemplateTableGastore6F_v_; +text: .text%__1cNTemplateTableGdstore6F_v_; +text: .text%__1cNTemplateTableGfstore6F_v_; +text: .text%__1cNTemplateTableGlstore6F_v_; +text: .text%__1cNTemplateTableGistore6F_v_; +text: .text%__1cNTemplateTableHaload_06F_v_; +text: .text%__1cNTemplateTableGsaload6F_v_; +text: .text%__1cNTemplateTableMfast_icaload6F_v_; +text: .text%__1cNTemplateTableGldc2_w6F_v_; +text: .text%__1cLsymbolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: symbolKlass.o; +text: .text%__1cLsymbolKlassOset_alloc_size6MI_v_: symbolKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: symbolKlass.o; +text: .text%__1cLsymbolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cINegFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cISubFNodeDsub6kMpknEType_3_3_; +text: .text%__1cSstubRoutines_init26F_v_; +text: .text%__1cSstubRoutines_init16F_v_; +text: .text%__1cMStubRoutinesLinitialize26F_v_; +text: .text%__1cMStubRoutinesLinitialize16F_v_; +text: .text%__1cNStubGeneratorTgenerate_verify_oop6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorbAgenerate_forward_exception6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorSgenerate_call_stub6MrpC_1_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorYgenerate_catch_exception6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorUgenerate_atomic_xchg6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorYgenerate_get_previous_fp6M_pC_: stubGenerator_i486.o; +text: .text%__1cNStubGeneratorUcreate_control_words6M_v_: stubGenerator_i486.o; +text: .text%__1cLStatSamplerXcreate_sampled_perfdata6F_v_; +text: .text%__1cQSystemDictionarybAcompute_java_system_loader6FpnGThread__v_; +text: .text%__1cNTemplateTableGsipush6F_v_; +text: .text%__1cNTemplateTableGbipush6F_v_; +text: .text%__1cNTemplateTableLaconst_null6F_v_; +text: .text%__1cNTemplateTableSshouldnotreachhere6F_v_; +text: .text%__1cNTemplateTableDnop6F_v_; +text: .text%__1cNTemplateTableNpd_initialize6F_v_; +text: .text%__1cStemplateTable_init6F_v_; +text: .text%__1cNTemplateTableDjsr6F_v_; +text: .text%__1cNTemplateTableFjsr_w6F_v_; +text: .text%__1cNTemplateTableGgoto_w6F_v_; +text: .text%__1cNTemplateTableF_goto6F_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC222_v_; +text: .text%__1cMPeriodicTaskJdisenroll6M_v_; +text: .text%__1cMPeriodicTaskLis_enrolled6kM_i_; +text: .text%__1cMPeriodicTask2T5B6M_v_; +text: .text%__1cQSystemDictionarybDinitialize_basic_type_mirrors6FpnGThread__v_; +text: .text%__1cQSystemDictionarybCinitialize_preloaded_classes6FpnGThread__v_; +text: .text%__1cQSystemDictionaryKinitialize6FpnGThread__v_; +text: .text%__1cQSystemDictionaryKclasses_do6FpFpnMklassOopDesc__v_v_; +text: .text%__1cLremove_file6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cNMemoryServicebFadd_parallel_scavenge_heap_info6FpnUParallelScavengeHeap__v_; +text: .text%__1cNMemoryServiceRset_universe_heap6FpnNCollectedHeap__v_; +text: .text%__1cMCodeHeapPool2t6MpnICodeHeap_pkci_v_; +text: .text%__1cYSurvivorMutableSpacePool2t6MpnKPSYoungGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cUEdenMutableSpacePool2t6MpnKPSYoungGen_pnMMutableSpace_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cQPSGenerationPool2t6MpnJPSPermGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cQPSGenerationPool2t6MpnIPSOldGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cNMemoryManagerbEget_psMarkSweep_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cNMemoryManagerbDget_psScavenge_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cNMemoryManagerbDget_code_cache_memory_manager6F_p0_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: matcher.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: markSweep.o; +text: .text%__1cJMarkSweepSMarkAndPushClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepRFollowRootClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepSFollowStackClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepOIsAliveClosure2t6M_v_: markSweep.o; +text: .text%__1cJMarkSweepQKeepAliveClosure2t6M_v_: markSweep.o; +text: .text%__1cOmarksweep_init6F_v_; +text: .text%__1cNMemoryServiceXadd_psYoung_memory_pool6FpnKPSYoungGen_pnNMemoryManager_4_v_; +text: .text%__1cKmutex_init6F_v_; +text: .text%__1cMMutableSpaceOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: methodLiveness.o; +text: .text%__1cLmethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodKlass.o; +text: .text%__1cLmethodKlassOset_alloc_size6MI_v_: methodKlass.o; +text: .text%__1cLmethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cHRetDataKis_RetData6M_i_: methodDataOop.o; +text: .text%__1cHRetDataJfixup_ret6MinQmethodDataHandle__pC_; +text: .text%__1cHRetDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cPmethodDataKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodDataKlass.o; +text: .text%__1cPmethodDataKlassOset_alloc_size6MI_v_: methodDataKlass.o; +text: .text%__1cPmethodDataKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: memoryService.o; +text: .text%__1cNGrowableArray4CpnKMemoryPool__2t6Mii_v_: memoryService.o; +text: .text%__1cNGrowableArray4CpnNMemoryManager__2t6Mii_v_: memoryService.o; +text: .text%__1cNMemoryServiceZadd_code_heap_memory_pool6FpnICodeHeap__v_; +text: .text%__1cNMemoryServiceWadd_psPerm_memory_pool6FpnJPSPermGen_pnNMemoryManager__v_; +text: .text%__1cNMemoryServiceVadd_psOld_memory_pool6FpnIPSOldGen_pnNMemoryManager__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: management.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: klassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: klassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: klassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassUoop_is_instanceKlass6kM_i_: klassKlass.o; +text: .text%__1cKklassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiTagMap.o; +text: .text%__1cJMemRegion2t6M_v_: jvmtiTagMap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiImpl.o; +text: .text%__1cWNonPrintingResourceObj2n6FInLResourceObjPallocation_type__pv_: jvmtiImpl.o; +text: .text%__1cNGrowableArray4CpnPJvmtiRawMonitor__2t6Mii_v_: jvmtiImpl.o; +text: .text%__1cUJvmtiPendingMonitorsXtransition_raw_monitors6F_v_; +text: .text%__1cLJvmtiExportbMtransition_pending_onload_raw_monitors6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_death6F_v_; +text: .text%__1cKklassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: klassKlass.o; +text: .text%__1cJTimeStamp2t6M_v_: management.o; +text: .text%__1cKManagementWrecord_vm_startup_time6Fxx_v_; +text: .text%__1cKManagementKinitialize6FpnGThread__v_; +text: .text%__1cKManagementEinit6F_v_; +text: .text%__1cPmanagement_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: machnode.o; +text: .text%__1cGThreadMis_VM_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cXLowMemoryDetectorThreadbCis_hidden_from_external_view6kM_i_: lowMemoryDetector.o; +text: .text%__1cRLowMemoryDetectorbGlow_memory_detector_thread_entry6FpnKJavaThread_pnGThread__v_; +text: .text%__1cRLowMemoryDetectorUhas_pending_requests6F_i_; +text: .text%__1cRLowMemoryDetectorKinitialize6F_v_; +text: .text%__1cKRegionNodeUdepends_only_on_test6kM_i_: loopnode.o; +text: .text%__1cNIdealLoopTreeUmerge_many_backedges6MpnOPhaseIdealLoop__v_; +text: .text%__1cKfix_parent6FpnNIdealLoopTree_1_v_: loopnode.o; +text: .text%__1cNIdealLoopTreeQsplit_outer_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: loaderConstraints.o; +text: .text%__1cVLoaderConstraintTable2t6Mi_v_; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: library_call.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: objArrayKlass.o; +text: .text%__1cUParallelScavengeHeapMmax_capacity6kM_I_; +text: .text%__1cUParallelScavengeHeapPpost_initialize6M_v_; +text: .text%__1cUParallelScavengeHeapKinitialize6M_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: parGCAllocBuffer.o; +text: .text%__1cMarrayOopDescLheader_size6FnJBasicType__i_: parGCAllocBuffer.o; +text: .text%__1cRalign_object_size6Fi_i_: parGCAllocBuffer.o; +text: .text%__1cHoopDescLheader_size6F_i_: parGCAllocBuffer.o; +text: .text%__SLIP.DELETER__C: ostream.o; +text: .text%__1cMostream_exit6F_v_; +text: .text%__1cQostream_init_log6F_v_; +text: .text%__1cMostream_init6F_v_; +text: .text%__1cNdefaultStreamEinit6M_v_; +text: .text%__1cCosMsupports_sse6F_i_; +text: .text%__1cVcheck_for_sse_support6F_v_: os_solaris_i486.o; +text: .text%__1cCosGinit_26F_i_; +text: .text%__1cCosEinit6F_v_; +text: .text%__1cCosHSolarisUsynchronization_init6F_v_; +text: .text%__1cCosHSolarisOlibthread_init6F_v_; +text: .text%__1cUParallelScavengeHeapYpermanent_object_iterate6MpnNObjectClosure__v_; +text: .text%__1cWget_sharedmem_filename6Fpkci_pc_: perfMemory_solaris.o; +text: .text%__1cNget_user_name6Fl_pc_: perfMemory_solaris.o; +text: .text%__1cQget_user_tmp_dir6Fpkc_pc_: perfMemory_solaris.o; +text: .text%__1cKPerfMemoryHdestroy6F_v_; +text: .text%__1cKPerfMemoryKinitialize6F_v_; +text: .text%__1cPperfMemory_exit6F_v_; +text: .text%__1cPperfMemory_init6F_v_; +text: .text%__1cMPerfDataListFclone6M_p0_; +text: .text%__1cMPerfDataList2t6Mp0_v_; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnPPerfLongCounter__; +text: .text%__1cPPerfDataManagerHsampled6F_pnMPerfDataList__; +text: .text%__1cPPerfDataManagerHdestroy6F_v_; +text: .text%__1cFParseMjump_if_join6MpnENode_2_2_; +text: .text%__1cPGenerationSizerQinitialize_flags6M_v_: parallelScavengeHeap.o; +text: .text%__1cbCTwoGenerationCollectorPolicyMrem_set_name6M_nJGenRemSetEName__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapEkind6M_nNCollectedHeapEName__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapIend_addr6kM_ppnIHeapWord__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapEheap6F_p0_; +text: .text%__1cUParallelScavengeHeapNgc_threads_do6kMpnNThreadClosure__v_; +text: .text%__1cOisT2_libthread6F_i_; +text: .text%__1cCosNset_boot_path6Fcc_i_; +text: .text%__1cCosLinit_random6Fl_v_; +text: .text%__1cCosGstrdup6Fpkc_pc_; +text: .text%__1cCosXterminate_signal_thread6F_v_; +text: .text%__1cCosLsignal_init6F_v_; +text: .text%__1cTsignal_thread_entry6FpnKJavaThread_pnGThread__v_: os.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: oopMap.o; +text: .text%__1cQDoNothingClosure2t6M_v_: oopMap.o; +text: .text%__1cXonStackReplacement_init6F_v_; +text: .text%__1cSOnStackReplacementKinitialize6F_v_; +text: .text%__1cNObjectMonitorREntryQdDueue_unlink6MpnMObjectWaiter__v_; +text: .text%__1cSobjArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: objArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassUoop_is_objArrayKlass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cSobjArrayKlassKlassbEallocate_system_objArray_klass6MpnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlass.o; +text: .text%__1cCosXis_server_class_machine6F_i_; +text: .text%__1cCosHSolarisXinstall_signal_handlers6F_v_; +text: .text%__1cRlwp_priocntl_init6F_i_: os_solaris.o; +text: .text%__1cNpriocntl_stub6FinGidtype_lipc_l_: os_solaris.o; +text: .text%__1cCosHSolarisRmpss_sanity_check6F_v_; +text: .text%__1cCosPuncommit_memory6FpcI_i_; +text: .text%__1cCosLsignal_wait6F_i_; +text: .text%__1cVcheck_pending_signals6Fi_i_: os_solaris.o; +text: .text%__1cCosNsignal_notify6Fi_v_; +text: .text%__1cCosOsignal_init_pd6F_v_; +text: .text%__1cCosHSolarisPinit_signal_mem6F_v_; +text: .text%__1cCosSget_temp_directory6F_pkc_; +text: .text%__1cCosbDallocate_thread_local_storage6F_i_; +text: .text%__1cCosHSolarisQsignal_sets_init6F_v_; +text: .text%__1cCosScreate_main_thread6FpnGThread__i_; +text: .text%__1cQcreate_os_thread6FpnGThread_I_pnIOSThread__: os_solaris.o; +text: .text%__1cCosbDinit_system_properties_values6F_v_; +text: .text%__1cCosHSolarisWinitialize_system_info6F_v_; +text: .text%__1cCosPphysical_memory6F_X_; +text: .text%__1cSThreadLocalStorageHpd_init6F_v_; diff --git a/hotspot/build/solaris/makefiles/reorder_TIERED_sparc b/hotspot/build/solaris/makefiles/reorder_TIERED_sparc new file mode 100644 index 00000000000..4eadf379ca5 --- /dev/null +++ b/hotspot/build/solaris/makefiles/reorder_TIERED_sparc @@ -0,0 +1,7112 @@ +data = R0x2000; +text = LOAD ?RXO; + + +text: .text%__1cLOptoRuntimeLjshort_copy6Fph1I_v_; +text: .text%__1cLOptoRuntimeTarrayof_jshort_copy6FpnIHeapWord_2I_v_; +text: .text%__1cSPSPromotionManagerWcopy_to_survivor_space6MpnHoopDesc__2_; +text: .text%__1cCosOjavaTimeMillis6F_x_; +text: .text%__1cQIndexSetIteratorQadvance_and_next6M_I_; +text: .text%__1cNinstanceKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cJMarkSweepO_mark_and_push6FppnHoopDesc__v_; +text: .text%__1cNinstanceKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cOtypeArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cOtypeArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMOopTaskQdDueueKpop_global6MrpnHoopDesc__i_; +text: .text%__1cIMachNodeHis_Mach6M_p0_: ad_sparc_misc.o; +text: .text%__1cIPhaseIFGIadd_edge6MII_i_; +text: .text%__1cQIndexSetIterator2t6MpnIIndexSet__v_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: ifg.o; +text: .text%__1cJMarkSweepPmark_and_follow6FppnHoopDesc__v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: ad_sparc_misc.o; +text: .text%__1cENodeEjvms6kM_pnIJVMState__; +text: .text%__1cHRegMaskFis_UP6kM_i_; +text: .text%__1cUGenericGrowableArrayLraw_at_grow6MipknEGrET__pv_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: ad_sparc_misc.o; +text: .text%__1cIMachNodeNrematerialize6kM_i_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: classes.o; +text: .text%__1cIProjNodeHis_Proj6M_p0_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: classes.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: classes.o; +text: .text%__1cIIndexSetWalloc_block_containing6MI_pn0AIBitBlock__; +text: .text%__1cENodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cETypeDcmp6Fkpk03_i_; +text: .text%__1cENodeHis_Copy6kM_I_: classes.o; +text: .text%__1cENodeHlatency6MI_I_; +text: .text%__1cHRegMaskJis_bound16kM_i_; +text: .text%__1cDff16FI_i_; +text: .text%__1cHRegMaskESize6kM_I_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: ad_sparc_misc.o; +text: .text%__1cXresource_allocate_bytes6FI_pc_; +text: .text%__1cIMachNodeJideal_reg6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__: classes.o; +text: .text%__1cRMachSpillCopyNodeMis_SpillCopy6M_p0_: ad_sparc.o; +text: .text%__1cENodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJVectorSet2R6MI_rnDSet__; +text: .text%__1cHRegMaskJis_bound26kM_i_; +text: .text%__1cNSharedRuntimeElmul6Fxx_x_; +text: .text%__1cPOopTaskQdDueueSetFsteal6MipirpnHoopDesc__i_; +text: .text%__1cIMachNodeGOpcode6kM_i_; +text: .text%__1cENodeGpinned6kM_i_: classes.o; +text: .text%__1cJiRegIOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cIIndexSetKinitialize6MI_v_; +text: .text%__1cITypeNodeLbottom_type6kM_pknEType__; +text: .text%__1cNRelocIteratorEnext6M_i_: relocInfo.o; +text: .text%__1cRMachSpillCopyNodeHis_Copy6kM_I_: ad_sparc.o; +text: .text%__1cPClassFileStreamGget_u26MpnGThread__H_; +text: .text%__1cKTypeOopPtrFklass6kM_pnHciKlass__: type.o; +text: .text%__1cHPhiNodeGis_Phi6M_p0_: cfgnode.o; +text: .text%__1cETypeFuhash6Fkpk0_i_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: chaitin.o; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_: callnode.o; +text: .text%__1cENodeIout_grow6MI_v_; +text: .text%__1cIMachNodeHis_Mach6M_p0_: ad_sparc.o; +text: .text%__1cJCProjNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cOloadConI13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNobjArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cENodeHadd_req6Mp0_v_; +text: .text%__1cEDictGInsert6Mpv1i_1_; +text: .text%__1cJMarkSweepUAdjustPointerClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cJMultiNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeGis_CFG6kM_i_: cfgnode.o; +text: .text%__1cOloadConI13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOloadConI13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMarkSweepOIsAliveClosureLdo_object_b6MpnHoopDesc__i_: markSweep.o; +text: .text%__1cKRegionNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cIProjNodeGis_CFG6kM_i_; +text: .text%__1cNobjArrayKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNobjArrayKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cINodeHashLhash_delete6MpknENode__i_; +text: .text%__1cFArenaIcontains6kMpkv_i_; +text: .text%__1cOloadConI13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cICallNodeKmatch_edge6kMI_I_; +text: .text%__1cINodeHashQhash_find_insert6MpnENode__2_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: classes.o; +text: .text%__1cHPhiNodeGOpcode6kM_i_; +text: .text%__1cHTypeIntCeq6kMpknEType__i_; +text: .text%__1cKbranchNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cIProjNodeGpinned6kM_i_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: classes.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: classes.o; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__: ad_sparc_misc.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: cfgnode.o; +text: .text%__1cIProjNodeGOpcode6kM_i_; +text: .text%__1cETypeIhashcons6M_pk0_; +text: .text%__1cOPhaseIdealLoopUbuild_loop_late_post6MpnENode_pk0_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: nmethod.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: ad_sparc.o; +text: .text%__1cMPhaseChaitinTinterfere_with_live6MIpnIIndexSet__v_; +text: .text%__1cWNode_Backward_IteratorEnext6M_pnENode__; +text: .text%__1cNIdealLoopTreeJis_member6kMpk0_i_; +text: .text%__1cMPhaseChaitinKelide_copy6MpnENode_ipnFBlock_rnJNode_List_6i_i_; +text: .text%__1cQObjectStartArrayMobject_start6MpnIHeapWord__2_: cardTableExtension.o; +text: .text%__1cHCompileRvalid_bundle_info6MpknENode__i_; +text: .text%__1cENodeNrematerialize6kM_i_: classes.o; +text: .text%__1cMMachCallNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cHCompileNnode_bundling6MpknENode__pnGBundle__; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopopts.o; +text: .text%__1cOlower_pressure6FpnDLRG_IpnFBlock_pI4_v_: ifg.o; +text: .text%__1cGIfNodeGOpcode6kM_i_; +text: .text%__1cOPhaseIdealLoopYsplit_if_with_blocks_pre6MpnENode__2_; +text: .text%__1cOPhaseIdealLoopZsplit_if_with_blocks_post6MpnENode__v_; +text: .text%__1cIUniverseMnon_oop_word6F_pv_; +text: .text%__1cDLRGOcompute_degree6kMr0_i_; +text: .text%__1cFArenaIArealloc6MpvII_1_; +text: .text%__1cIConINodeGOpcode6kM_i_; +text: .text%__1cETypeEmeet6kMpk0_2_; +text: .text%__1cENode2t6MI_v_; +text: .text%__1cRMachSpillCopyNodeJideal_reg6kM_I_: ad_sparc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cIPipelineXfunctional_unit_latency6kMIpk0_I_; +text: .text%__1cWPSScavengeRootsClosureGdo_oop6MppnHoopDesc__v_: psTasks.o; +text: .text%__1cENodeHis_Copy6kM_I_: cfgnode.o; +text: .text%__1cKSchedulingLanti_do_def6MpnFBlock_pnENode_nHOptoRegEName_i_v_; +text: .text%__1cLsymbolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cJCProjNodeNis_block_proj6kM_pknENode__: cfgnode.o; +text: .text%__1cKIfTrueNodeGOpcode6kM_i_; +text: .text%__1cNRelocIteratorTadvance_over_prefix6M_v_; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_: ad_sparc.o; +text: .text%__1cIMachNodePin_oper_RegMask6kMIII_pknHRegMask__; +text: .text%__1cETypeJsingleton6kM_i_; +text: .text%__1cQIndexSetIteratorEnext6M_I_: coalesce.o; +text: .text%__1cIMachNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cJloadPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIPhaseIFGQeffective_degree6kMI_i_; +text: .text%__1cWConstantPoolCacheEntryPfollow_contents6M_v_; +text: .text%__1cWConstantPoolCacheEntryPadjust_pointers6M_v_; +text: .text%__1cIAddPNodeGOpcode6kM_i_; +text: .text%__1cIPhaseIFGJre_insert6MI_v_; +text: .text%__1cIPhaseIFGLremove_node6MI_pnIIndexSet__; +text: .text%__1cKNode_ArrayGinsert6MIpnENode__v_; +text: .text%__1cHTypeIntEhash6kM_i_; +text: .text%__1cETypeLisa_oop_ptr6kM_i_; +text: .text%__1cLsymbolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cLsymbolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cMPhaseIterGVNNtransform_old6MpnENode__2_; +text: .text%__1cDfh16FI_i_; +text: .text%__1cNMachIdealNodeErule6kM_I_: ad_sparc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: ad_sparc_misc.o; +text: .text%__1cIciObjectGequals6Mp0_i_; +text: .text%__1cIIndexSetKfree_block6MI_v_; +text: .text%__1cWShouldNotReachHereNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cHTypeIntJsingleton6kM_i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: classes.o; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: cfgnode.o; +text: .text%__1cLIfFalseNodeGOpcode6kM_i_; +text: .text%__1cSCallStaticJavaNodeGOpcode6kM_i_; +text: .text%__1cENodeEhash6kM_I_; +text: .text%__1cOPhaseIdealLoopEsort6MpnNIdealLoopTree_2_2_; +text: .text%__1cMMachProjNodeLbottom_type6kM_pknEType__; +text: .text%JVM_ArrayCopy; +text: .text%__1cOtypeArrayKlassQoop_is_typeArray6kM_i_: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cNSharedRuntimeDl2f6Fx_f_; +text: .text%__1cPjava_lang_ClassLas_klassOop6FpnHoopDesc__pnMklassOopDesc__; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: classes.o; +text: .text%__1cIParmNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cMPhaseChaitinKbias_color6MrnDLRG_i_nHOptoRegEName__; +text: .text%__1cHConNodeGOpcode6kM_i_; +text: .text%__1cMPhaseIterGVNWadd_users_to_worklist06MpnENode__v_; +text: .text%__1cMMachProjNodeGOpcode6kM_i_; +text: .text%__1cJiRegPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: classes.o; +text: .text%__1cJiRegIOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cENodeXpartial_latency_of_defs6MrnLBlock_Array_rnNGrowableArray4CI___v_; +text: .text%__1cXPipeline_Use_Cycle_Mask2L6Mi_r0_: ad_sparc_pipeline.o; +text: .text%__1cIBoolNodeGOpcode6kM_i_; +text: .text%__1cJMultiNodeIis_Multi6M_p0_; +text: .text%__1cYCallStaticJavaDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeEgrow6MI_v_; +text: .text%__1cIciObjectEhash6M_i_; +text: .text%__1cKRegionNodeGOpcode6kM_i_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: relocInfo.o; +text: .text%__1cOPhaseIdealLoopUbuild_loop_tree_impl6MpnENode_i_i_; +text: .text%__1cJMarkSweepSMarkAndPushClosureGdo_oop6MppnHoopDesc__v_: markSweep.o; +text: .text%__1cRMachSpillCopyNodeLbottom_type6kM_pknEType__: ad_sparc.o; +text: .text%__1cOPhaseIdealLoopOget_early_ctrl6MpnENode__2_; +text: .text%__1cIIndexSetKinitialize6MIpnFArena__v_; +text: .text%__1cLmethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cFMutexGunlock6M_v_; +text: .text%__1cIPhaseGVNJtransform6MpnENode__2_; +text: .text%__1cFStateRMachOperGenerator6MipnIMachNode_pnHCompile__pnIMachOper__; +text: .text%__1cKRelocationNunpack_2_ints6Mri1_v_: relocInfo.o; +text: .text%__1cOoop_RelocationLunpack_data6M_v_; +text: .text%__1cRmethodDataOopDescHdata_at6Mi_pnLProfileData__; +text: .text%__1cNRelocIteratorEnext6M_i_: codeBlob.o; +text: .text%__1cPJavaFrameAnchorNmake_walkable6MpnKJavaThread__v_; +text: .text%__1cENodeNis_block_proj6kM_pk0_; +text: .text%__1cNRelocIteratorFreloc6M_pnKRelocation__; +text: .text%__1cKJavaThreadPcook_last_frame6MnFframe__1_; +text: .text%__1cIProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQconstMethodKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cUGenericGrowableArrayPraw_at_put_grow6MipknEGrET_3_v_; +text: .text%__1cPClassFileStreamGget_u16MpnGThread__C_; +text: .text%__1cKRegionNodeGpinned6kM_i_: classes.o; +text: .text%__1cLTypeInstPtrEhash6kM_i_; +text: .text%__1cYCallStaticJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOPhaseIdealLoopThas_local_phi_input6MpnENode__2_; +text: .text%__1cJloadINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRMachSpillCopyNodeLout_RegMask6kM_rknHRegMask__: ad_sparc.o; +text: .text%__1cKbranchNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFStateDDFA6MipknENode__i_; +text: .text%__1cMMachProjNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cENodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cMMachProjNodeLout_RegMask6kM_rknHRegMask__: classes.o; +text: .text%__1cOMethodLivenessKBasicBlockXcompute_gen_kill_single6MpnQciByteCodeStream__v_; +text: .text%__1cRMachSpillCopyNodeKin_RegMask6kMI_rknHRegMask__: ad_sparc.o; +text: .text%__1cbAfinal_graph_reshaping_impl6FpnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cOtypeArrayKlassIallocate6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: typeArrayKlass.o; +text: .text%__1cNCollectedHeapOarray_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cUParallelScavengeHeapVlarge_typearray_limit6M_I_: parallelScavengeHeap.o; +text: .text%__1cIPhaseCCPOtransform_once6MpnENode__2_; +text: .text%__1cGciTypeEmake6FnJBasicType__p0_; +text: .text%__1cKoopFactoryNnew_typeArray6FnJBasicType_ipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cENodeFclone6kM_p0_; +text: .text%__1cITypeNodeEhash6kM_I_; +text: .text%__1cIBoolNodeHis_Bool6M_p0_: subnode.o; +text: .text%__1cMPipeline_UseMfull_latency6kMIrk0_I_; +text: .text%__1cRMachSpillCopyNodePoper_input_base6kM_I_: ad_sparc.o; +text: .text%__1cENodeKmatch_edge6kMI_I_; +text: .text%__1cQconstMethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLmethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQconstMethodKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cHPhiNodeGpinned6kM_i_: cfgnode.o; +text: .text%__1cOPhaseIdealLoopZremix_address_expressions6MpnENode__2_; +text: .text%__1cSInterpreterRuntimeInewarray6FpnKJavaThread_nJBasicType_i_v_; +text: .text%__1cICallNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: ad_sparc_misc.o; +text: .text%__1cRMachSafePointNodeEjvms6kM_pnIJVMState__: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopNget_late_ctrl6MpnENode_2_2_; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: loopnode.o; +text: .text%JVM_CurrentTimeMillis; +text: .text%__1cIMachNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cENodeIIdentity6MpnOPhaseTransform__p0_; +text: .text%__1cIPipelinePoperand_latency6kMIpk0_I_; +text: .text%__1cENodeFIdeal6MpnIPhaseGVN_i_p0_; +text: .text%__1cKTypeAryPtrEhash6kM_i_; +text: .text%__1cICallNodeHis_Call6M_p0_: callnode.o; +text: .text%__1cETypeFxmeet6kMpk0_2_; +text: .text%__1cILRG_ListGextend6MII_v_; +text: .text%__1cJVectorSet2F6kMI_i_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: ad_sparc.o; +text: .text%__1cENodeQIdeal_DU_postCCP6MpnIPhaseCCP__p0_; +text: .text%__1cOtypeArrayKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cIProjNodeEhash6kM_I_; +text: .text%__1cGIfNodeGpinned6kM_i_: classes.o; +text: .text%__1cIAddINodeGOpcode6kM_i_; +text: .text%__1cIIndexSet2t6Mp0_v_; +text: .text%__1cRmethodDataOopDescJnext_data6MpnLProfileData__2_; +text: .text%__1cITypeNodeJideal_reg6kM_I_; +text: .text%__1cYCallStaticJavaDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLTypeInstPtrCeq6kMpknEType__i_; +text: .text%__1cMloadConPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: classes.o; +text: .text%__1cHPhiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMachNode2t6M_v_; +text: .text%__1cIMachNodeQis_MachNullCheck6M_pnRMachNullCheckNode__: ad_sparc.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: callnode.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: callnode.o; +text: .text%__1cOis_diamond_phi6FpnENode__i_: cfgnode.o; +text: .text%__1cHMatcherKLabel_Root6MpknENode_pnFState_p16_6_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: memnode.o; +text: .text%__1cENodeHsize_of6kM_I_; +text: .text%__1cICmpPNodeGOpcode6kM_i_; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: classes.o; +text: .text%__1cKNode_ArrayGremove6MI_v_; +text: .text%__1cNSafePointNodeGpinned6kM_i_: callnode.o; +text: .text%__1cMOopTaskQdDueueOpop_local_slow6MInOTaskQdDueueSuperDAge__i_; +text: .text%__1cHPhiNodeEhash6kM_I_; +text: .text%__1cKTypeOopPtrJsingleton6kM_i_; +text: .text%__1cPindOffset13OperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cFMutexElock6M_v_; +text: .text%__1cLSymbolTableGlookup6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cKoopFactoryKnew_symbol6FpkcipnGThread__pnNsymbolOopDesc__; +text: .text%__1cJCProjNodeGis_CFG6kM_i_: cfgnode.o; +text: .text%__1cKmethodOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cGIfNodeFis_If6M_p0_: classes.o; +text: .text%__1cENodeNrematerialize6kM_i_: cfgnode.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nmethod.o; +text: .text%__1cJStartNodeLbottom_type6kM_pknEType__; +text: .text%__1cHTypeIntFxmeet6kMpknEType__3_; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: ad_sparc_misc.o; +text: .text%__1cOmatch_into_reg6FpnENode_iii1_i_: matcher.o; +text: .text%__1cENodeSremove_dead_region6MpnIPhaseGVN_i_i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: classes.o; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: memnode.o; +text: .text%__1cIProjNodeLbottom_type6kM_pknEType__; +text: .text%__1cPciObjectFactoryDget6MpnHoopDesc__pnIciObject__; +text: .text%__1cKTypeAryPtrCeq6kMpknEType__i_; +text: .text%__1cILocationIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cICmpINodeGOpcode6kM_i_; +text: .text%Unsafe_CompareAndSwapLong; +text: .text%__1cNCatchProjNodeGOpcode6kM_i_; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: cfgnode.o; +text: .text%__1cSinstanceKlassKlassMoop_is_klass6kM_i_: instanceKlassKlass.o; +text: .text%__1cQUnique_Node_ListGremove6MpnENode__v_; +text: .text%__1cENode2t6Mp0_v_; +text: .text%__1cNLocationValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: classes.o; +text: .text%__1cMloadConPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitHstopped6M_i_; +text: .text%__1cETypeKhas_memory6kM_i_; +text: .text%__1cFframeVinterpreter_frame_bcp6kM_pC_; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: cfgnode.o; +text: .text%__1cMloadConPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJStartNodeGpinned6kM_i_: callnode.o; +text: .text%__1cTCreateExceptionNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompileMFillLocArray6MpnENode_pnNGrowableArray4CpnKScopeValue____i_; +text: .text%__1cIHaltNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: ad_sparc_misc.o; +text: .text%__1cLTypeInstPtrEmake6FnHTypePtrDPTR_pnHciKlass_ipnIciObject_i_pk0_; +text: .text%__1cPClassFileStreamHskip_u16MipnGThread__v_; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: cfgnode.o; +text: .text%__1cHRegMaskMSmearToPairs6M_v_; +text: .text%__1cYCallStaticJavaDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMPhaseIterGVNVadd_users_to_worklist6MpnENode__v_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: callnode.o; +text: .text%__1cKMachIfNodeJis_MachIf6kM_pk0_: ad_sparc_misc.o; +text: .text%__1cMloadConPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: classes.o; +text: .text%__1cbFCompressedLineNumberWriteStreamKwrite_pair6Mii_v_; +text: .text%__1cNinstanceKlassLfind_method6FpnPobjArrayOopDesc_pnNsymbolOopDesc_4_pnNmethodOopDesc__; +text: .text%__1cSCallStaticJavaNodeEhash6kM_I_: callnode.o; +text: .text%__1cHMatcherKReduceOper6MpnFState_ipnIMachNode_rpnENode__v_; +text: .text%__1cMPipeline_UseJadd_usage6Mrk0_v_; +text: .text%__1cRCardTableModRefBSEkind6M_nKBarrierSetEName__: cardTableExtension.o; +text: .text%__1cIAddPNodeKmatch_edge6kMI_I_; +text: .text%__1cJiRegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cGIfNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cGcmpkey6Fpkv1_i_; +text: .text%__1cNsymbolOopDescGequals6kMpkci_i_; +text: .text%__1cMPhaseChaitinMchoose_color6MrnDLRG_i_nHOptoRegEName__; +text: .text%__1cMMergeMemNodeGOpcode6kM_i_; +text: .text%__1cFframeYinterpreter_frame_method6kM_pnNmethodOopDesc__; +text: .text%__1cJTypeTupleJsingleton6kM_i_; +text: .text%__1cIParmNodeGOpcode6kM_i_; +text: .text%__1cJiRegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cPClassFileParserRverify_legal_utf86MpkCipnGThread__v_; +text: .text%__1cPClassFileParserbEparse_constant_pool_utf8_entry6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cHTypeIntEmake6Fiii_pk0_; +text: .text%__1cENodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNsymbolOopDescLas_C_string6kM_pc_; +text: .text%__1cKSchedulingWAddNodeToAvailableList6MpnENode__v_; +text: .text%__1cKSchedulingSChooseNodeToBundle6M_pnENode__; +text: .text%__1cKSchedulingPAddNodeToBundle6MpnENode_pknFBlock__v_; +text: .text%__1cKRelocationLunpack_data6M_v_: codeBlob.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: memnode.o; +text: .text%__1cICallNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: classes.o; +text: .text%__1cTconstantPoolOopDescNklass_at_impl6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cJLoadPNodeGOpcode6kM_i_; +text: .text%__1cMMutableSpaceIallocate6MI_pnIHeapWord__; +text: .text%__1cJPSPermGenSallocate_permanent6MI_pnIHeapWord__; +text: .text%__1cUParallelScavengeHeapWpermanent_mem_allocate6MI_pnIHeapWord__; +text: .text%__1cENodeGis_CFG6kM_i_: connode.o; +text: .text%__1cIMachNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMMutableSpaceMcas_allocate6MI_pnIHeapWord__; +text: .text%__1cNflagsRegPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cHPhiNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMMachTypeNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cJCatchNodeGOpcode6kM_i_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: cfgnode.o; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pnIciObject_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cHCmpNodeGis_Cmp6kM_pk0_: classes.o; +text: .text%__1cIJVMStateLdebug_start6kM_I_; +text: .text%__1cTconstantPoolOopDescSklass_ref_index_at6Mi_i_; +text: .text%__1cENodeHdel_req6MI_v_; +text: .text%__1cRSignatureIterator2t6MnMsymbolHandle__v_; +text: .text%__1cOAbstractICachePinvalidate_word6FpC_v_; +text: .text%__1cFBlockIis_Empty6kM_i_; +text: .text%__1cOThreadCritical2T6M_v_; +text: .text%__1cOThreadCritical2t6M_v_; +text: .text%method_compare: methodOop.o; +text: .text%__1cENodeGis_CFG6kM_i_: subnode.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: multnode.o; +text: .text%__1cICodeHeapKfind_start6kMpv_1_; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_: classes.o; +text: .text%__1cETypeEhash6kM_i_; +text: .text%__1cRNativeInstructionLset_long_at6Mii_v_; +text: .text%__1cJMultiNodeEhash6kM_I_: classes.o; +text: .text%__1cIAddPNodeLbottom_type6kM_pknEType__; +text: .text%__1cQciByteCodeStreamEjava6MnJBytecodesECode__2_; +text: .text%__1cJCProjNodeEhash6kM_I_: classes.o; +text: .text%__1cIHaltNodeGOpcode6kM_i_; +text: .text%__1cMMachCallNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cFBlockGselect6MrnJNode_List_rnLBlock_Array_pirnJVectorSet_IrnNGrowableArray4CI___pnENode__; +text: .text%__1cFStateRMachNodeGenerator6MipnHCompile__pnIMachNode__; +text: .text%__1cHMatcherKReduceInst6MpnFState_irpnENode__pnIMachNode__; +text: .text%__1cICmpUNodeGOpcode6kM_i_; +text: .text%__1cOPhaseIdealLoopbIdom_lca_for_get_late_ctrl_internal6MpnENode_22_2_; +text: .text%__1cXPipeline_Use_Cycle_MaskCOr6Mrk0_v_; +text: .text%__1cTconstantPoolOopDescQsignature_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cILoadNodeEhash6kM_I_; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: ad_sparc_misc.o; +text: .text%__1cKTypeAryPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cIMachNodeGExpand6MpnFState_rnJNode_List__p0_: ad_sparc_misc.o; +text: .text%__1cGBitMapFclear6M_v_; +text: .text%__1cHConNodeGis_Con6kM_I_: classes.o; +text: .text%__1cKHandleMarkKinitialize6MpnGThread__v_; +text: .text%__1cKHandleMark2T6M_v_; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlass.o; +text: .text%__1cFBlockLis_uncommon6kMrnLBlock_Array__i_; +text: .text%__1cZPhaseConservativeCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cMPhaseIterGVNZremove_globally_dead_node6MpnENode__v_; +text: .text%__1cWShouldNotReachHereNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHPhiNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cILoadNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: cfgnode.o; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: multnode.o; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNobjArrayKlassQarray_klass_impl6FnTobjArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%JVM_ReleaseUTF; +text: .text%__1cJloadPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJTypeTupleEhash6kM_i_; +text: .text%__1cFframeVoopmapreg_to_location6kMnFVMRegEName_pknLRegisterMap__ppnHoopDesc__; +text: .text%__1cENodeHget_int6kM_i_; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: methodDataOop.o; +text: .text%__1cHMatcherTReduceInst_Interior6MpnFState_ipnIMachNode_IrpnENode__I_; +text: .text%__1cNinstanceKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassQarray_klass_impl6FnTinstanceKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cMflagsRegOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: codeBlob.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: cfgnode.o; +text: .text%__1cICodeBlobLoop_addr_at6kMi_ppnHoopDesc__; +text: .text%__1cObranchConPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKoopFactoryMnew_objArray6FpnMklassOopDesc_ipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNinstanceKlassRallocate_objArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cMOopMapStreamJfind_next6M_v_; +text: .text%__1cFDictI2i6M_v_; +text: .text%__1cIMachNodeSalignment_required6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__: classes.o; +text: .text%__1cKNode_ArrayEgrow6MI_v_; +text: .text%__1cHTypeIntEmake6Fi_pk0_; +text: .text%__1cRAbstractAssembler2t6MpnKCodeBuffer__v_; +text: .text%__1cJloadPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMMergeMemNodeLbottom_type6kM_pknEType__: memnode.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: multnode.o; +text: .text%__1cSInterpreterRuntimeJanewarray6FpnKJavaThread_pnTconstantPoolOopDesc_ii_v_; +text: .text%__1cOPSPromotionLABKinitialize6MnJMemRegion__v_; +text: .text%__1cPciInstanceKlassMis_interface6M_i_: ciInstanceKlass.o; +text: .text%__1cJMultiNodeIproj_out6kMI_pnIProjNode__; +text: .text%__1cPindOffset13OperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_: coalesce.o; +text: .text%__1cUcompI_iReg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cODataRelocationJset_value6MpC_v_: relocInfo.o; +text: .text%__1cKRelocationRpd_set_data_value6MpCi_v_; +text: .text%__1cKCastPPNodeGOpcode6kM_i_; +text: .text%__1cOoop_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cOoop_RelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cPSignatureStreamEnext6M_v_; +text: .text%__1cLLShiftINodeGOpcode6kM_i_; +text: .text%__1cENodeOis_block_start6kM_i_; +text: .text%__1cMPhaseChaitinSuse_prior_register6MpnENode_I2pnFBlock_rnJNode_List_6_i_; +text: .text%__1cGIfNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKCodeBufferIrelocate6MpCrknQRelocationHolder_i_v_; +text: .text%__1cKbranchNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPjava_lang_ClassMis_primitive6FpnHoopDesc__i_; +text: .text%__1cGBitMapJset_union6M0_v_; +text: .text%__1cPVirtualCallDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cIAddPNodeHis_AddP6M_p0_: classes.o; +text: .text%__1cIConPNodeGOpcode6kM_i_; +text: .text%__1cJLoadINodeGOpcode6kM_i_; +text: .text%__1cUGenericGrowableArray2t6Mii_v_; +text: .text%JVM_GetMethodIxExceptionTableLength; +text: .text%__1cOJNIHandleBlockPallocate_handle6MpnHoopDesc__pnI_jobject__; +text: .text%__1cPClassFileParserUassemble_annotations6MpCi1ipnGThread__nPtypeArrayHandle__; +text: .text%__1cNSharedRuntimeDd2i6Fd_i_; +text: .text%__1cVcompP_iRegP_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKRegionNodeEhash6kM_I_: classes.o; +text: .text%__1cNbranchConNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNSafePointNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: subnode.o; +text: .text%__1cENodeHis_Copy6kM_I_: memnode.o; +text: .text%__1cOoop_RelocationSfix_oop_relocation6M_v_; +text: .text%__1cRSignatureIteratorSiterate_parameters6M_v_; +text: .text%__1cIAddPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cGBitMap2t6MpII_v_; +text: .text%__1cUGenericGrowableArray2t6MpnFArena_iipnEGrET__v_; +text: .text%__1cPClassFileStreamGget_u46MpnGThread__I_; +text: .text%__1cMMachCallNodeLbottom_type6kM_pknEType__; +text: .text%__1cFParsePdo_one_bytecode6M_v_; +text: .text%__1cFParseNdo_exceptions6M_v_; +text: .text%__1cITypeLongCeq6kMpknEType__i_; +text: .text%__1cLPCTableNodeGpinned6kM_i_: classes.o; +text: .text%__1cTconstantPoolOopDescbAname_and_type_ref_index_at6Mi_i_; +text: .text%__1cHPhiNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cENodeGpinned6kM_i_: connode.o; +text: .text%__1cHPhiNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cENodeGis_Con6kM_I_: classes.o; +text: .text%__1cHMatcherKmatch_tree6MpknENode__pnIMachNode__; +text: .text%__1cUParallelScavengeHeapPis_in_permanent6kMpkv_i_: parallelScavengeHeap.o; +text: .text%__1cMPhaseIterGVNKis_IterGVN6M_p0_: phaseX.o; +text: .text%__1cHCompileJcan_alias6MpknHTypePtr_i_i_; +text: .text%__1cKimmI13OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cCosVcurrent_stack_pointer6F_pC_; +text: .text%__1cENodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cEDict2F6kMpkv_pv_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cENodeIdestruct6M_v_; +text: .text%__1cIMachNodeNis_MachEpilog6M_pnOMachEpilogNode__: ad_sparc_misc.o; +text: .text%__1cMCreateExNodeGOpcode6kM_i_; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: cfgnode.o; +text: .text%__1cIBoolNodeEhash6kM_I_; +text: .text%__1cNinstanceKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cLTypeInstPtrFxmeet6kMpknEType__3_; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: classes.o; +text: .text%__1cKNode_ArrayFclear6M_v_; +text: .text%__1cObranchConPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIProjNodeHsize_of6kM_I_; +text: .text%__1cKis_x2logic6FpnIPhaseGVN_pnENode__3_: cfgnode.o; +text: .text%__1cHAbsNodeLis_absolute6FpnIPhaseGVN_pnENode__4_; +text: .text%__1cTconstantPoolOopDescWsignature_ref_index_at6Mi_i_; +text: .text%__1cMloadConINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cGBitMapGat_put6MIi_v_; +text: .text%__1cIHaltNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cLPhaseValuesGintcon6Mi_pnIConINode__; +text: .text%__1cJloadBNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: multnode.o; +text: .text%__1cGThreadLis_in_stack6kMpC_i_; +text: .text%__1cKJavaThreadNis_lock_owned6kMpC_i_; +text: .text%__1cFMutexElock6MpnGThread__v_; +text: .text%__1cKciTypeFlowLStateVectorSapply_one_bytecode6MpnQciByteCodeStream__i_; +text: .text%__1cHhashptr6Fpkv_i_; +text: .text%__1cMMachHaltNodeEjvms6kM_pnIJVMState__; +text: .text%__1cENodeLis_MergeMem6M_pnMMergeMemNode__: classes.o; +text: .text%__1cGOopMapJset_value6MnHOptoRegEName_ii_v_; +text: .text%__1cHhashkey6Fpkv_i_; +text: .text%__1cMPhaseChaitinHnew_lrg6MpknENode_I_v_; +text: .text%__1cIJVMStateJdebug_end6kM_I_; +text: .text%__1cIPhaseIFGMtest_edge_sq6kMII_i_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: classes.o; +text: .text%__1cTconstantPoolOopDescLname_ref_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cLBoxLockNodeNrematerialize6kM_i_: classes.o; +text: .text%__1cKBranchDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cJloadPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHSubNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: callnode.o; +text: .text%__1cJTypeTupleCeq6kMpknEType__i_; +text: .text%__1cRSignatureIteratorSiterate_returntype6M_v_; +text: .text%__1cSaddP_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMachNodeHtwo_adr6kM_I_: ad_sparc.o; +text: .text%__1cNSafePointNodeHsize_of6kM_I_; +text: .text%__1cObranchConPNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cLTypeInstPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cHCmpNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPcheckCastPPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNLoadRangeNodeGOpcode6kM_i_; +text: .text%__1cNbranchConNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENode2t6Mp011_v_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: symbolKlass.o; +text: .text%__1cHCompilePfind_alias_type6MpknHTypePtr_i_pn0AJAliasType__; +text: .text%__1cJStoreNodeKmatch_edge6kMI_I_; +text: .text%__1cKbranchNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cOPSPromotionLABFflush6M_v_; +text: .text%__1cQResultTypeFinderDset6MinJBasicType__v_: bytecode.o; +text: .text%__1cOBytecodeStreamEnext6M_nJBytecodesECode__: generateOopMap.o; +text: .text%__1cOcompU_iRegNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescLresult_type6kM_nJBasicType__; +text: .text%__1cICodeHeapJnext_free6kMpnJHeapBlock__pv_; +text: .text%__1cICodeHeapLblock_start6kMpv_pnJHeapBlock__; +text: .text%__1cICodeHeapKnext_block6kMpnJHeapBlock__2_; +text: .text%__1cQSystemDictionaryXcheck_signature_loaders6FnMsymbolHandle_nGHandle_2ipnGThread__v_; +text: .text%__1cKbranchNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: ad_sparc_misc.o; +text: .text%__1cJloadPNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cSCountedLoopEndNodeGOpcode6kM_i_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: cfgnode.o; +text: .text%__1cPciInstanceKlassGloader6M_pnHoopDesc__; +text: .text%__1cHMemNodeMIdeal_common6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPcheckCastPPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNCellTypeStateFmerge6kM0i_0_; +text: .text%__1cMPhaseIterGVNMsubsume_node6MpnENode_2_v_; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceKlass.o; +text: .text%__1cILoadNodeKmatch_edge6kMI_I_; +text: .text%__1cENodeGis_Con6kM_I_: subnode.o; +text: .text%__1cFframeUis_interpreted_frame6kM_i_; +text: .text%__1cLsymbolKlassNoop_is_symbol6kM_i_: symbolKlass.o; +text: .text%__1cJloadINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cETypeFempty6kM_i_; +text: .text%__1cNExceptionMark2T6M_v_; +text: .text%__1cNExceptionMark2t6MrpnGThread__v_; +text: .text%__1cMMachCallNodeLis_MachCall6M_p0_: ad_sparc_misc.o; +text: .text%__1cIMachNodeHis_Mach6M_p0_: machnode.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: connode.o; +text: .text%__1cITypeLongEhash6kM_i_; +text: .text%__1cNSafePointNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJHashtableJnew_entry6MIpnHoopDesc__pnOHashtableEntry__; +text: .text%__1cJiRegLOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: multnode.o; +text: .text%__1cKJNIHandlesKmake_local6FpnHJNIEnv__pnHoopDesc__pnI_jobject__; +text: .text%__1cMPhaseIterGVNbGregister_new_node_with_optimizer6MpnENode__2_; +text: .text%__1cPciInstanceKlassRis_instance_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cPciInstanceKlassRprotection_domain6M_pnHoopDesc__; +text: .text%__1cOloadConI13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOloadConI13NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cObranchConPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKStoreINodeGOpcode6kM_i_; +text: .text%__1cJcmpOpOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cRSignatureIterator2t6MpnNsymbolOopDesc__v_; +text: .text%__1cOno_flip_branch6FpnFBlock__i_: block.o; +text: .text%__1cMloadConINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJiRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKRegionNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKstorePNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHPhiNodeHsize_of6kM_I_: cfgnode.o; +text: .text%__1cJrelocInfo2t6Mn0AJrelocType_ii_v_; +text: .text%__1cPSignatureStreamHis_done6kM_i_; +text: .text%__1cJrelocInfoNfinish_prefix6Mph_p0_; +text: .text%__1cIAddPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQaddP_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescPis_empty_method6kM_i_; +text: .text%__1cSaddI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKReflectionTverify_field_access6FpnMklassOopDesc_22nLAccessFlags_ii_i_; +text: .text%__1cTAbstractInterpreterLmethod_kind6FnMmethodHandle__n0AKMethodKind__; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode_i_i_; +text: .text%__1cMflagsRegOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cIBoolNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLCounterDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cHRegMaskMClearToPairs6M_v_; +text: .text%__1cJiRegLOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cRshlI_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZPhaseConservativeCoalesceJcopy_copy6MpnENode_2pnFBlock_I_i_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: lcm.o; +text: .text%__1cIMachOperDreg6kMpnNPhaseRegAlloc_pknENode__i_; +text: .text%__1cKRelocationSfix_oop_relocation6M_v_: relocInfo.o; +text: .text%__1cNPhaseCoalesceRcombine_these_two6MpnENode_2_v_; +text: .text%__1cNflagsRegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKcmpOpPOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cKTypeRawPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cMloadConINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescRis_not_compilable6kMi_i_; +text: .text%__1cNmethodOopDescLis_accessor6kM_i_; +text: .text%__1cFArenaEgrow6MI_pv_; +text: .text%__1cMPhaseChaitinLinsert_proj6MpnFBlock_IpnENode_I_v_; +text: .text%__1cILoadNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cJStoreNodeLbottom_type6kM_pknEType__; +text: .text%__1cKTypeRawPtrJsingleton6kM_i_; +text: .text%__1cGIfNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHMemNodeGis_Mem6M_p0_: classes.o; +text: .text%__1cSCallStaticJavaNodeRis_CallStaticJava6kM_pk0_: callnode.o; +text: .text%__1cIBoolNodeLbottom_type6kM_pknEType__: subnode.o; +text: .text%__1cENodeHis_Goto6kM_I_: classes.o; +text: .text%__1cPciObjectFactorySget_unloaded_klass6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cNSafePointNodeSset_next_exception6Mp0_v_; +text: .text%__1cNmethodOopDescMintrinsic_id6kM_n0ALIntrinsicId__; +text: .text%__1cQaddP_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIHaltNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPCheckCastPPNodeGOpcode6kM_i_; +text: .text%__1cKStorePNodeGOpcode6kM_i_; +text: .text%__1cKRelocationLunpack_data6M_v_: relocInfo.o; +text: .text%__1cNflagsRegUOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cNinstanceKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cQciByteCodeStreamMreset_to_bci6Mi_v_; +text: .text%__1cPcheckCastPPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAddPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRInvocationCounterEinit6M_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: block.o; +text: .text%__1cTconstantPoolOopDescNklass_name_at6Mi_pnNsymbolOopDesc__; +text: .text%__1cMMergeMemNodeLis_MergeMem6M_p0_: memnode.o; +text: .text%__1cFBlockOschedule_local6MrnHMatcher_rnLBlock_Array_pirnJVectorSet_rnNGrowableArray4CI___i_; +text: .text%__1cXPhaseAggressiveCoalesceIcoalesce6MpnFBlock__v_; +text: .text%__1cFBlockScall_catch_cleanup6MrnLBlock_Array__v_; +text: .text%__1cObranchConUNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQCompressedStream2t6MpCi_v_; +text: .text%__1cTconstantPoolOopDescRname_ref_index_at6Mi_i_; +text: .text%__1cIAddINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cHRetNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cKRegionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstorePNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMObjectLocker2t6MnGHandle_pnGThread__v_; +text: .text%__1cMObjectLocker2T6M_v_; +text: .text%__1cNSafePointNodebBneeds_polling_address_input6F_i_; +text: .text%__1cKRegionNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOcompI_iRegNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__: callnode.o; +text: .text%__1cMURShiftINodeGOpcode6kM_i_; +text: .text%__1cRmethodDataOopDescPinitialize_data6MpnOBytecodeStream_i_i_; +text: .text%__1cENodeGis_CFG6kM_i_: memnode.o; +text: .text%__1cNRelocIteratorKset_limits6MpC1_v_; +text: .text%__1cIRootNodeGOpcode6kM_i_; +text: .text%__1cOloadConI13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cILoadNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cTCreateExceptionNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFStateM_sub_Op_ConI6MpknENode__v_; +text: .text%__1cRMachSafePointNodeQis_MachSafePoint6M_p0_: ad_sparc_misc.o; +text: .text%__1cENodeHdel_out6Mp0_v_: parse2.o; +text: .text%__1cPcheckCastPPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: classes.o; +text: .text%__1cISubINodeGOpcode6kM_i_; +text: .text%__1cNbranchConNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFframeZsender_with_pc_adjustment6kMpnLRegisterMap_pnICodeBlob_i_0_; +text: .text%__1cJTypeTupleEmake6FIppknEType__pk0_; +text: .text%__1cJTypeTupleGfields6FI_ppknEType__; +text: .text%__1cFframeGsender6kMpnLRegisterMap_pnICodeBlob__0_; +text: .text%__1cENodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLSymbolTableJbasic_add6MipCiIpnGThread__pnNsymbolOopDesc__; +text: .text%__1cLsymbolKlassPallocate_symbol6MpCipnGThread__pnNsymbolOopDesc__; +text: .text%__1cSinstanceKlassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cRAbstractAssemblerEbind6MrnFLabel__v_; +text: .text%__1cILoadNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cENodeGpinned6kM_i_: subnode.o; +text: .text%__1cKbranchNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cHPhiNodeIadr_type6kM_pknHTypePtr__: cfgnode.o; +text: .text%__1cHAddNodeEhash6kM_I_; +text: .text%__1cMPhaseIterGVNFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cNbranchConNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSPSPromotionManagerMdrain_stacks6M_v_; +text: .text%__1cENodeRdisconnect_inputs6Mp0_i_; +text: .text%__1cLis_cond_add6FpnIPhaseGVN_pnHPhiNode__pnENode__; +text: .text%__1cPsplit_flow_path6FpnIPhaseGVN_pnHPhiNode__pnENode__: cfgnode.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: cfgnode.o; +text: .text%__1cNbranchConNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cSaddI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcompU_iRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJFieldTypeKbasic_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cHConNodeEhash6kM_I_; +text: .text%__1cLLShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNmethodOopDescIbci_from6kMpC_i_; +text: .text%__1cOMachReturnNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cJdo_method6FpnNmethodOopDesc__v_: recompilationMonitor.o; +text: .text%__1cNidealize_test6FpnIPhaseGVN_pnGIfNode__3_: ifnode.o; +text: .text%__1cILoadNodeHis_Load6M_p0_: classes.o; +text: .text%__1cYCallStaticJavaDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: subnode.o; +text: .text%__1cOPhaseIdealLoopSget_ctrl_no_update6kMpnENode__2_: split_if.o; +text: .text%__1cITypeNodeHsize_of6kM_I_; +text: .text%__1cVcompP_iRegP_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: parse1.o; +text: .text%__1cENodeGpinned6kM_i_: memnode.o; +text: .text%__1cNSafePointNodeLbottom_type6kM_pknEType__: callnode.o; +text: .text%__1cFciEnvXget_klass_by_index_impl6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cTconstantPoolOopDescSklass_at_if_loaded6FnSconstantPoolHandle_i_pnMklassOopDesc__; +text: .text%__1cLProfileDataPadjust_pointers6M_v_: methodDataOop.o; +text: .text%__1cLProfileDataPfollow_contents6M_v_: methodDataOop.o; +text: .text%__1cQaddP_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJStoreNodeIis_Store6kM_pk0_: classes.o; +text: .text%__1cJloadINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstorePNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassModifiers; +text: .text%__1cJCodeCacheJfind_blob6Fpv_pnICodeBlob__; +text: .text%__1cNSafePointNodeOnext_exception6kM_p0_; +text: .text%JVM_GetClassAccessFlags; +text: .text%__1cKbranchNodeHis_Goto6kM_I_: ad_sparc_misc.o; +text: .text%__1cLklassItable2t6MnTinstanceKlassHandle__v_; +text: .text%__1cIsplit_if6FpnGIfNode_pnMPhaseIterGVN__pnENode__: ifnode.o; +text: .text%__1cHTypeAryEhash6kM_i_; +text: .text%__1cTremove_useless_bool6FpnGIfNode_pnIPhaseGVN__pnENode__: ifnode.o; +text: .text%__1cGOopMapHset_xxx6MnHOptoRegEName_nLOopMapValueJoop_types_ii2_v_; +text: .text%__1cPfieldDescriptorKinitialize6MpnMklassOopDesc_i_v_; +text: .text%__1cJMultiNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cJCProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPPerfLongVariantGsample6M_v_; +text: .text%__1cJStoreNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMPhaseChaitinMyank_if_dead6MpnENode_pnFBlock_pnJNode_List_6_i_; +text: .text%__1cJloadINodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cPSignatureStreamJis_object6kM_i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: callnode.o; +text: .text%__1cIBoolNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJCatchNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: multnode.o; +text: .text%__1cIMachOperNconstant_disp6kM_i_; +text: .text%__1cIMachOperFscale6kM_i_; +text: .text%__1cENode2t6Mp0111_v_; +text: .text%__1cFPhase2t6Mn0ALPhaseNumber__v_; +text: .text%__1cNCompileBrokerLmaybe_block6F_v_; +text: .text%__1cFBlockOcode_alignment6M_I_; +text: .text%__1cMgetTimeNanos6F_x_: os_solaris.o; +text: .text%__1cNinstanceKlassGitable6kM_pnLklassItable__; +text: .text%__1cLciSignatureLreturn_type6kM_pnGciType__; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: subnode.o; +text: .text%__1cFStateM_sub_Op_RegP6MpknENode__v_; +text: .text%JVM_GetCPMethodSignatureUTF; +text: .text%__1cFChunkJnext_chop6M_v_; +text: .text%__1cMMergeMemNodeEhash6kM_I_; +text: .text%__1cGOopMapHset_oop6MnHOptoRegEName_ii_v_; +text: .text%__1cKSchedulingbFComputeRegisterAntidependencies6MpnFBlock__v_; +text: .text%__1cKSchedulingPComputeUseCount6MpknFBlock__v_; +text: .text%__1cITypeNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cHTypePtrHget_con6kM_i_; +text: .text%__1cUcompI_iReg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceKlass.o; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: ad_sparc.o; +text: .text%__1cIJumpDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: ad_sparc.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: ad_sparc.o; +text: .text%__1cIMachNodeSalignment_required6kM_i_: ad_sparc.o; +text: .text%__1cENodeJis_Branch6kM_I_: ad_sparc.o; +text: .text%__1cMPhaseChaitinSget_spillcopy_wide6MpnENode_2I_2_; +text: .text%__1cYDebugInformationRecorderTcreate_scope_values6MpnNGrowableArray4CpnKScopeValue____pnKDebugToken__; +text: .text%__1cVcompP_iRegP_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cHSubNodeGis_Sub6M_p0_: classes.o; +text: .text%__1cNPhaseRegAllocGis_oop6kMpknENode__i_; +text: .text%__1cWstatic_stub_RelocationLunpack_data6M_v_; +text: .text%__1cQaddI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConUNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: multnode.o; +text: .text%__1cUParallelScavengeHeapVunsafe_max_tlab_alloc6kM_I_; +text: .text%__1cFBlockJfind_node6kMpknENode__I_; +text: .text%__1cUArgumentSizeComputerDset6MinJBasicType__v_: frame.o; +text: .text%__1cFMutexbClock_without_safepoint_check6M_v_; +text: .text%__1cHCmpNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNCollectedHeapXallocate_from_tlab_slow6FpnGThread_I_pnIHeapWord__; +text: .text%__1cWThreadLocalAllocBufferXclear_before_allocation6M_v_; +text: .text%__1cHTypePtrEhash6kM_i_; +text: .text%__1cIMachNodeNis_MachEpilog6M_pnOMachEpilogNode__: ad_sparc.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: codeBlob.o; +text: .text%__1cNinstanceKlassRallocate_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cSObjectSynchronizerKslow_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cWThreadLocalAllocBufferEfill6MpnIHeapWord_2I_v_; +text: .text%__1cUParallelScavengeHeapRallocate_new_tlab6MI_pnIHeapWord__; +text: .text%__1cYNoJvmtiVMObjectAllocMark2t6M_v_; +text: .text%__1cYNoJvmtiVMObjectAllocMark2T6M_v_; +text: .text%__1cKSharedHeapXfill_region_with_object6FnJMemRegion__v_; +text: .text%__1cFBlockLfind_remove6MpknENode__v_; +text: .text%__1cIIndexSetJlrg_union6MIIkIpknIPhaseIFG_rknHRegMask__I_; +text: .text%__1cKMemBarNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cUcompI_iReg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverbAcheck_method_accessability6FnLKlassHandle_11nMmethodHandle_pnGThread__v_; +text: .text%__1cNObjectMonitorEexit6MpnGThread__v_; +text: .text%__1cIimmPOperEtype6kM_pknEType__: ad_sparc_clone.o; +text: .text%__1cMloadConPNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cLMachNopNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadRangeNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cVCompressedWriteStream2t6Mi_v_; +text: .text%__1cNObjectMonitorFenter6MpnGThread__v_; +text: .text%__1cRlock_ptr_RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cPVirtualCallDataPadjust_pointers6M_v_; +text: .text%__1cPVirtualCallDataPfollow_contents6M_v_; +text: .text%__1cIJVMStateNclone_shallow6kM_p0_; +text: .text%__1cENodeKreplace_by6Mp0_v_; +text: .text%__1cSObjectSynchronizerJslow_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cMMergeMemNodePiteration_setup6Mpk0_v_; +text: .text%__1cFKlassNlookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cKDictionaryEfind6MiInMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cKRegionNodeOis_block_start6kM_i_: classes.o; +text: .text%__1cRMachSpillCopyNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRMachSpillCopyNodeOimplementation6kMpnKCodeBuffer_pnNPhaseRegAlloc_i_I_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: classes.o; +text: .text%__1cKRegionNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJStoreNodeEhash6kM_I_; +text: .text%__1cHMatcherQis_save_on_entry6Mi_i_; +text: .text%__1cSaddP_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQaddI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKTypeOopPtrWmake_from_klass_common6FpnHciKlass_ii_pk0_; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: machnode.o; +text: .text%__1cIGraphKitJclone_map6M_pnNSafePointNode__; +text: .text%__1cMMergeMemNodeQclone_all_memory6FpnENode__p0_; +text: .text%__1cOcompU_iRegNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKIfTrueNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRMemBarReleaseNodeGOpcode6kM_i_; +text: .text%__1cHTypeIntFempty6kM_i_; +text: .text%__1cKbranchNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLPhaseValuesFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cIMachOperIconstant6kM_i_; +text: .text%__1cNCatchProjNodeMis_CatchProj6kM_pk0_: cfgnode.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: subnode.o; +text: .text%__1cWMutableSpaceUsedHelperLtake_sample6M_x_: spaceCounters.o; +text: .text%__1cQaddI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cGPcDescHreal_pc6kMpknHnmethod__pC_; +text: .text%__1cOcompU_iRegNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNRelocIteratorKinitialize6MipnICodeBlob_pC3_v_; +text: .text%__1cRPSOldPromotionLABFflush6M_v_; +text: .text%__1cTconstantPoolOopDescMklass_ref_at6MipnGThread__pnMklassOopDesc__; +text: .text%__1cPcompP_iRegPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCodeBufferRtransform_address6kMrk0pC_3_; +text: .text%__1cLBoxLockNodeGOpcode6kM_i_; +text: .text%__1cIciObjectJset_ident6MI_v_; +text: .text%__1cKJNIHandlesKmake_local6FpnHoopDesc__pnI_jobject__; +text: .text%__1cKTypeRawPtrEhash6kM_i_; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: subnode.o; +text: .text%__1cIBoolNodeKmatch_edge6kMI_I_: subnode.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: memnode.o; +text: .text%__1cMMergeMemNodePset_base_memory6MpnENode__v_; +text: .text%__1cOcompI_iRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLIfFalseNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cCosPelapsed_counter6F_x_; +text: .text%__1cNPhaseRegAllocKreg2offset6kMnHOptoRegEName__i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: multnode.o; +text: .text%__1cKReflectionTverify_class_access6FpnMklassOopDesc_2i_i_; +text: .text%__1cICallNodeLis_CallLeaf6kM_pknMCallLeafNode__: callnode.o; +text: .text%__1cRCompilationPolicyOmustBeCompiled6FnMmethodHandle__i_; +text: .text%__1cMMergeMemNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cGBitMapOset_difference6M0_v_; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__: callnode.o; +text: .text%__1cMPhaseChaitinJsplit_USE6MpnENode_pnFBlock_2IIiinNGrowableArray4CI__i_I_; +text: .text%__1cENodeGis_Con6kM_I_: cfgnode.o; +text: .text%__1cOoop_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cRshlI_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMMergeMemNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%JVM_GetMethodIxLocalsCount; +text: .text%__1cNloadRangeNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: multnode.o; +text: .text%JVM_CurrentThread; +text: .text%__1cENodeHget_ptr6kM_i_; +text: .text%__1cQciByteCodeStreamFEOBCs6M_nJBytecodesECode__; +text: .text%__1cRcmpFastUnlockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIAndINodeGOpcode6kM_i_; +text: .text%__1cPClassFileParserYverify_legal_method_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cENodeHins_req6MIp0_v_; +text: .text%__1cNRelocIteratorEnext6M_i_: codeBuffer.o; +text: .text%__1cSaddI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMPhaseChaitinFUnion6MpknENode_3_v_; +text: .text%__1cMloadConLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHAddNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKRelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: codeBuffer.o; +text: .text%__1cPBoundRelocationMupdate_addrs6MpCrknKCodeBuffer_4_1_; +text: .text%__1cKstoreINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOFastUnlockNodeGOpcode6kM_i_; +text: .text%__1cLOptoRuntimeFnew_C6FpnMklassOopDesc_pnKJavaThread__v_; +text: .text%__1cITypeNodeDcmp6kMrknENode__I_; +text: .text%__1cIHaltNodeLbottom_type6kM_pknEType__; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: connode.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: gcm.o; +text: .text%__1cKstorePNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNflagsRegUOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKcmpOpUOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cLstoreI0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIciObject2t6MnGHandle__v_; +text: .text%__1cNSafePointNodeKmatch_edge6kMI_I_; +text: .text%__1cFframeOis_entry_frame6kM_i_; +text: .text%__1cIMachOperOindex_position6kM_i_; +text: .text%__1cLklassVtableTupdate_super_vtable6MpnNinstanceKlass_pnNmethodOopDesc_i_i_; +text: .text%__1cXmembar_release_lockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJVectorSet2L6MI_rnDSet__; +text: .text%__1cLOopRecorderOallocate_index6MpnI_jobject__i_; +text: .text%__1cOcompU_iRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMMergeMemNodeJmemory_at6kMI_pnENode__; +text: .text%__1cSaddP_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPindOffset13OperNconstant_disp6kM_i_: ad_sparc.o; +text: .text%__1cPindOffset13OperFscale6kM_i_: ad_sparc.o; +text: .text%__1cPindOffset13OperNbase_position6kM_i_: ad_sparc.o; +text: .text%__1cWShouldNotReachHereNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPciObjectFactoryRcreate_new_object6MpnHoopDesc__pnIciObject__; +text: .text%__1cUcompI_iReg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVcompP_iRegP_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQaddP_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQaddP_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSvframeStreamCommonPfill_from_frame6M_i_; +text: .text%__1cMLinkResolverZcheck_klass_accessability6FnLKlassHandle_1pnGThread__v_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: classes.o; +text: .text%__1cIJVMStateIof_depth6kMi_p0_; +text: .text%__1cNSharedRuntimeElrem6Fxx_x_; +text: .text%__1cRconstantPoolKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cMciMethodDataLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cRMemBarAcquireNodeGOpcode6kM_i_; +text: .text%__1cRcmpFastUnlockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKo0RegPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cSaddI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cObranchConUNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJVectorSet2t6MpnFArena__v_; +text: .text%__1cKRegionNodeGis_CFG6kM_i_: loopnode.o; +text: .text%__1cKTypeAryPtrFxmeet6kMpknEType__3_; +text: .text%__1cVcompP_iRegP_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRSignatureIteratorSiterate_parameters6MX_v_; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: relocInfo.o; +text: .text%__1cICallNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cKRelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: postaloc.o; +text: .text%__1cOMethodLivenessKBasicBlockWcompute_gen_kill_range6MpnQciByteCodeStream__v_; +text: .text%__1cJTraceTime2T6M_v_; +text: .text%__1cITypeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cObranchConUNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cPcheckCastPPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKMemBarNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJloadPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cITypeLongJsingleton6kM_i_; +text: .text%__1cJTraceTime2t6MpkcpnMelapsedTimer_iipnMoutputStream__v_; +text: .text%__1cKoopFactoryKnew_method6FinLAccessFlags_iiipnGThread__pnNmethodOopDesc__; +text: .text%__1cSconstMethodOopDescZset_inlined_tables_length6Miii_v_; +text: .text%__1cNmethodOopDescLobject_size6Fi_i_; +text: .text%__1cNmethodOopDescbAcompute_size_of_parameters6MpnGThread__v_; +text: .text%__1cSconstMethodOopDescLobject_size6Fiiii_i_; +text: .text%__1cLmethodKlassIallocate6MnRconstMethodHandle_nLAccessFlags_pnGThread__pnNmethodOopDesc__; +text: .text%__1cLklassVtableWneeds_new_vtable_entry6FpnNmethodOopDesc_pnMklassOopDesc_pnHoopDesc_pnNsymbolOopDesc_nLAccessFlags__i_; +text: .text%__1cNmethodOopDescJinit_code6M_v_; +text: .text%__1cQconstMethodKlassIallocate6MiiiipnGThread__pnSconstMethodOopDesc__; +text: .text%__1cPClassFileParserMparse_method6MnSconstantPoolHandle_ipnLAccessFlags_pnPtypeArrayHandle_55pnGThread__nMmethodHandle__; +text: .text%__1cObranchConUNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMMergeMemNodeNset_memory_at6MIpnENode__v_; +text: .text%__1cLstoreI0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFciEnvSget_klass_by_index6MpnPciInstanceKlass_iri_pnHciKlass__; +text: .text%__1cNSignatureInfoHdo_void6M_v_: bytecode.o; +text: .text%__1cQaddI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENode2t6Mp01_v_; +text: .text%__1cNmethodOopDescPis_final_method6kM_i_; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cKstoreINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIRewriterOrewrite_method6FnMmethodHandle_rnIintArray_pnGThread__1_; +text: .text%__1cNmethodOopDescLlink_method6FnMmethodHandle__v_; +text: .text%__1cPClassFileParserbDverify_legal_method_modifiers6MiinMsymbolHandle_pnGThread__v_; +text: .text%__1cHTypeAryRary_must_be_exact6kM_i_; +text: .text%__1cRshrI_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFStateM_sub_Op_AddP6MpknENode__v_; +text: .text%__1cTCreateExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJCatchNodeIis_Catch6kM_pk0_: classes.o; +text: .text%__1cIGraphKitEstop6M_v_; +text: .text%__1cOcompI_iRegNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIPhaseCCPFwiden6kMpknEType_3_3_: phaseX.o; +text: .text%__1cKCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPcompP_iRegPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRMachNullCheckNodeQis_MachNullCheck6M_p0_: machnode.o; +text: .text%__1cITypeFuncEhash6kM_i_; +text: .text%__1cLBoxLockNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNMachIdealNodeMideal_Opcode6kM_i_: machnode.o; +text: .text%__1cMTypeKlassPtrEhash6kM_i_; +text: .text%__1cMCallLeafNodeGOpcode6kM_i_; +text: .text%__1cENodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHnmethodKis_nmethod6kM_i_: nmethod.o; +text: .text%__1cOcompI_iRegNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHPhiNodeEmake6FpnENode_2pknEType_pknHTypePtr__p0_; +text: .text%__1cIAddPNodeQmach_bottom_type6FpknIMachNode__pknEType__; +text: .text%__1cOcompU_iRegNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: connode.o; +text: .text%__1cOCallRelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cKRelocationXpd_set_call_destination6MpCi_v_; +text: .text%__1cKRelocationTpd_call_destination6M_pC_; +text: .text%__1cJiRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNflagsRegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cJStartNodeGis_CFG6kM_i_: callnode.o; +text: .text%__1cHOrINodeGOpcode6kM_i_; +text: .text%__1cXmembar_acquire_lockNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%JVM_GetCPMethodClassNameUTF; +text: .text%__1cMloadConDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMflagsRegOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cHnmethodJis_zombie6kM_i_: nmethod.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: callnode.o; +text: .text%__1cKSchedulingQNodeFitsInBundle6MpnENode__i_; +text: .text%__1cLProfileDataPfollow_contents6M_v_: ciMethodData.o; +text: .text%__1cLProfileDataPadjust_pointers6M_v_: ciMethodData.o; +text: .text%__1cFStateM_sub_Op_RegI6MpknENode__v_; +text: .text%__1cOMachReturnNodeNis_MachReturn6M_p0_: ad_sparc_misc.o; +text: .text%__1cKklassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cFKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cJMarkSweepXrevisit_weak_klass_link6FpnFKlass__v_; +text: .text%__1cKklassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNinstanceKlassVshould_be_initialized6kM_i_; +text: .text%__1cWconstantPoolCacheKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cHCompileYout_preserve_stack_slots6F_I_; +text: .text%__1cPSignatureStream2t6MnMsymbolHandle_i_v_; +text: .text%__1cIGraphKitLclean_stack6Mi_v_; +text: .text%__1cKStoreBNodeGOpcode6kM_i_; +text: .text%__1cLklassVtableToop_adjust_pointers6M_v_; +text: .text%__1cLklassVtableToop_follow_contents6M_v_; +text: .text%__1cVcompP_iRegP_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconstMethodOopDescbBcompressed_linenumber_table6kM_pC_; +text: .text%__1cPClassFileParserWparse_linenumber_table6MIIpipnGThread__pC_; +text: .text%__1cbFCompressedLineNumberWriteStream2t6Mi_v_; +text: .text%__1cITypeFuncCeq6kMpknEType__i_; +text: .text%__1cUcompI_iReg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKbranchNodeFclone6kM_pnENode__; +text: .text%__1cMUniverseOperFclone6kM_pnIMachOper__; +text: .text%__1cJlabelOperFclone6kM_pnIMachOper__; +text: .text%__1cJlabelOperFlabel6kM_pnFLabel__: ad_sparc.o; +text: .text%__1cICallNodeHis_Call6M_p0_: classes.o; +text: .text%__1cLciSignatureHtype_at6kMi_pnGciType__; +text: .text%__1cIMachNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cIMachOperMdisp_as_type6kM_pknHTypePtr__: ad_sparc.o; +text: .text%__1cRshlI_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRelocationJpack_data6M_i_: codeBlob.o; +text: .text%__1cOPhaseIdealLoopIsplit_up6MpnENode_22_i_; +text: .text%__1cLCounterDataOis_CounterData6M_i_: ciMethodData.o; +text: .text%__1cJStartNodeGpinned6kM_i_: classes.o; +text: .text%__1cHAddNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOis_range_check6FpnENode_r12ri_i_: ifnode.o; +text: .text%JVM_IsNaN; +text: .text%__1cNloadRangeNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKbranchNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStartNodeGOpcode6kM_i_; +text: .text%__1cOMethodLivenessKBasicBlockPget_liveness_at6MpnIciMethod_i_nGBitMap__; +text: .text%__1cIciMethodPliveness_at_bci6Mi_nGBitMap__; +text: .text%__1cOMethodLivenessPget_liveness_at6Mi_nGBitMap__; +text: .text%__1cQregF_to_stkINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: memnode.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: subnode.o; +text: .text%__1cENodeDcmp6kMrk0_I_; +text: .text%__1cFParseKensure_phi6Mii_pnHPhiNode__; +text: .text%__1cOoop_RelocationJpack_data6M_i_; +text: .text%__1cHTypeIntFxdual6kM_pknEType__; +text: .text%__1cIciObjectIencoding6M_pnI_jobject__; +text: .text%__1cNSafePointNode2t6MIpnIJVMState__v_; +text: .text%__1cHTypePtrJsingleton6kM_i_; +text: .text%__1cMmerge_region6FpnKRegionNode_pnIPhaseGVN__pnENode__: cfgnode.o; +text: .text%__1cIGraphKitObasic_plus_adr6MpnENode_2i_2_; +text: .text%__1cJAssemblerOpatched_branch6Fiii_i_; +text: .text%__1cJAssemblerSbranch_destination6Fii_i_; +text: .text%__1cRshlI_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPClassFileParserZskip_over_field_signature6MpciIpnGThread__1_; +text: .text%__1cENodeIadd_prec6Mp0_v_; +text: .text%__1cLBoxLockNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPSignatureStreamJas_symbol6MpnGThread__pnNsymbolOopDesc__; +text: .text%__1cSaddP_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWMachCallStaticJavaNodePret_addr_offset6M_i_; +text: .text%__1cICodeBlobJis_zombie6kM_i_: codeBlob.o; +text: .text%__1cITypeFuncEmake6FpknJTypeTuple_3_pk0_; +text: .text%__1cMloadConDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLRegisterMap2t6MpnKJavaThread_i_v_; +text: .text%__1cKTypeOopPtrHget_con6kM_i_; +text: .text%__1cQsubI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIRootNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLPhaseValuesHmakecon6MpknEType__pnHConNode__; +text: .text%__1cJloadLNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_iipnGThread__v_; +text: .text%__1cMLinkResolverZcheck_field_accessability6FnLKlassHandle_11rnPfieldDescriptor_pnGThread__v_; +text: .text%__1cJLoadBNodeGOpcode6kM_i_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: subnode.o; +text: .text%__1cLOptoRuntimebCcomplete_monitor_unlocking_C6FpnHoopDesc_pnJBasicLock__v_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_locking_C6FpnHoopDesc_pnJBasicLock_pnKJavaThread__v_; +text: .text%__1cOGenerateOopMapHinterp16MpnOBytecodeStream__v_; +text: .text%__1cLRegisterMapLpd_location6kMnFVMRegEName__pC_; +text: .text%__1cSvframeStreamCommonEnext6M_v_; +text: .text%__1cIAddINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cIRootNodeNis_block_proj6kM_pknENode__: classes.o; +text: .text%__1cMMergeMemNode2t6MpnENode__v_; +text: .text%__1cOcompI_iRegNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRMachSafePointNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cJloadINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXruntime_call_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cQsubI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPindOffset13OperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cPindOffset13OperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: machnode.o; +text: .text%__1cPindOffset13OperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cICmpPNodeDsub6kMpknEType_3_3_; +text: .text%__1cKBufferBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cHMemNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cIAddINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIGraphKitQkill_dead_locals6M_v_; +text: .text%__1cCosMvm_page_size6F_i_; +text: .text%__1cHRegMaskPfind_first_pair6kM_nHOptoRegEName__; +text: .text%__1cMloadConLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cWShouldNotReachHereNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cRlock_ptr_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cVcompP_iRegP_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUcompI_iReg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: bytecode.o; +text: .text%__1cNloadRangeNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cRconstantPoolKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNinstanceKlassUadjust_static_fields6M_v_; +text: .text%__1cRconstantPoolKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cLklassItableToop_adjust_pointers6M_v_; +text: .text%__1cNinstanceKlassUfollow_static_fields6M_v_; +text: .text%__1cLklassItableToop_follow_contents6M_v_; +text: .text%__1cSinstanceKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cNinstanceKlassXfollow_weak_klass_links6MpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSinstanceKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cNSafePointNodeGOpcode6kM_i_; +text: .text%__1cWShouldNotReachHereNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: ad_sparc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: memnode.o; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: memnode.o; +text: .text%__1cOGenerateOopMapPjump_targets_do6MpnOBytecodeStream_pFp0ipi_v4_i_; +text: .text%__1cPcompP_iRegPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshlI_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadPNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMPhaseChaitinPset_was_spilled6MpnENode__v_; +text: .text%__1cYDebugInformationRecorderOdescribe_scope6MpnIciMethod_ipnKDebugToken_44_v_; +text: .text%__1cYDebugInformationRecorderVcreate_monitor_values6MpnNGrowableArray4CpnMMonitorValue____pnKDebugToken__; +text: .text%__1cVCompressedWriteStreamJwrite_int6Mi_v_: debugInfoRec.o; +text: .text%__1cIHaltNodeGpinned6kM_i_: classes.o; +text: .text%__1cMloadConPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNMachIdealNodeJnum_opnds6kM_I_: ad_sparc.o; +text: .text%__1cIGraphKit2t6MpnIJVMState__v_; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: cfgnode.o; +text: .text%__1cPsp_ptr_RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cPconvI2L_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKbranchNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cQPreserveJVMState2T6M_v_; +text: .text%__1cQPreserveJVMState2t6MpnIGraphKit_i_v_; +text: .text%__1cIGraphKitRnull_check_common6MpnENode_nJBasicType_i_2_; +text: .text%__1cPcompP_iRegPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMWarmCallInfoHis_cold6kM_i_; +text: .text%__1cLCastP2INodeGOpcode6kM_i_; +text: .text%__1cRshrI_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cWconstantPoolCacheKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cWconstantPoolCacheKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cTCreateExceptionNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cXmembar_release_lockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMloadConLNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Copy6kM_I_: machnode.o; +text: .text%__1cFMutexNowned_by_self6kM_i_; +text: .text%__1cLConvI2LNodeGOpcode6kM_i_; +text: .text%__1cITypeLongFxmeet6kMpknEType__3_; +text: .text%__1cNinstanceKlassKinitialize6MpnGThread__v_; +text: .text%__1cSaddI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowNmake_range_at6Mi_pn0AFRange__; +text: .text%__1cFParseMmerge_common6Mpn0AFBlock_i_v_; +text: .text%__1cOCallRelocationFvalue6M_pC_: codeBlob.o; +text: .text%__1cENodeHis_Type6M_pnITypeNode__: classes.o; +text: .text%__1cPciInstanceKlassYunique_concrete_subklass6M_p0_; +text: .text%__1cENodeQlatency_from_use6kMrnLBlock_Array_rnNGrowableArray4CI__pk0p0_i_; +text: .text%__1cLBoxLockNodeHsize_of6kM_I_; +text: .text%__1cOPhaseIdealLoopIset_idom6MpnENode_2I_v_; +text: .text%__1cJStoreNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHTypeAryCeq6kMpknEType__i_; +text: .text%__1cJStartNodeIis_Start6M_p0_: callnode.o; +text: .text%JVM_GetCPFieldClassNameUTF; +text: .text%__1cHCompileKTracePhase2t6MpkcpnMelapsedTimer_i_v_; +text: .text%__1cMPhaseIterGVNHmakecon6MpknEType__pnHConNode__; +text: .text%__1cSaddI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNLoadKlassNodeGOpcode6kM_i_; +text: .text%__1cRcmpFastUnlockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJLoadCNodeGOpcode6kM_i_; +text: .text%__1cMTypeKlassPtrCeq6kMpknEType__i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: memnode.o; +text: .text%__1cHciFieldJwill_link6MpnPciInstanceKlass_nJBytecodesECode__i_; +text: .text%__1cPciInstanceKlassLfield_cache6M_pnTciConstantPoolCache__; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: node.o; +text: .text%__1cQciByteCodeStreamJget_field6Mri_pnHciField__; +text: .text%__1cFciEnvSget_field_by_index6MpnPciInstanceKlass_i_pnHciField__; +text: .text%__1cOcompI_iRegNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMergeMemStreamOnext_non_empty6Mi_i_: graphKit.o; +text: .text%__1cRshlI_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNmethodOopDescIbcp_from6kMi_pC_; +text: .text%__1cTCreateExceptionNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cGBitMapVset_union_with_result6M0_i_; +text: .text%__1cICmpINodeDsub6kMpknEType_3_3_; +text: .text%__1cLRShiftINodeGOpcode6kM_i_; +text: .text%__1cNCollectedHeapYpermanent_array_allocate6FnLKlassHandle_iipnGThread__pnHoopDesc__: typeArrayKlass.o; +text: .text%__1cOtypeArrayKlassSallocate_permanent6MipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: memnode.o; +text: .text%__1cSCallLeafDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSignatureInfoJdo_object6Mii_v_: frame.o; +text: .text%__1cPcheckCastPPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOPhaseIdealLoopQconditional_move6MpnENode__2_; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_: callnode.o; +text: .text%__1cFframeOis_first_frame6kM_i_; +text: .text%__1cJStoreNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cITypeFuncEmake6FpnIciMethod__pk0_; +text: .text%__1cICodeBlobTfix_oop_relocations6M_v_; +text: .text%__1cOGenerateOopMapEpush6MnNCellTypeState__v_; +text: .text%__1cJloadSNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKStoreCNodeGOpcode6kM_i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: subnode.o; +text: .text%__1cOGenerateOopMapRdo_exception_edge6MpnOBytecodeStream__v_; +text: .text%__1cMstringStreamFwrite6MpkcI_v_; +text: .text%__1cOGenerateOopMapDpop6M_nNCellTypeState__; +text: .text%__1cHRetNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPcmpFastLockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverMresolve_pool6FrnLKlassHandle_rnMsymbolHandle_42nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverOresolve_invoke6FrnICallInfo_nGHandle_nSconstantPoolHandle_inJBytecodesECode_pnGThread__v_; +text: .text%__1cIBoolNodeJideal_reg6kM_I_: subnode.o; +text: .text%__1cHCmpNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cRcmpFastUnlockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRloadConP_pollNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cETypeFwiden6kMpk0_2_: type.o; +text: .text%__1cRcmpFastUnlockNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cILoadNodeRraise_bottom_type6MpknEType__v_; +text: .text%__1cLstoreI0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFciEnvTget_method_by_index6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cQciByteCodeStreamKget_method6Mri_pnIciMethod__; +text: .text%__1cFciEnvYget_method_by_index_impl6MpnPciInstanceKlass_inJBytecodesECode__pnIciMethod__; +text: .text%__1cMloadConPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFciEnvNlookup_method6MpnNinstanceKlass_2pnNsymbolOopDesc_4nJBytecodesECode__pnNmethodOopDesc__; +text: .text%__1cKDictionaryKfind_class6MiInMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cNSafePointNodeGpinned6kM_i_: classes.o; +text: .text%__1cPcompP_iRegPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cObranchConPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCodeCacheQfind_blob_unsafe6Fpv_pnICodeBlob__; +text: .text%__1cNloadRangeNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNCatchProjNodeLbottom_type6kM_pknEType__: cfgnode.o; +text: .text%__1cNCatchProjNodeHsize_of6kM_I_: cfgnode.o; +text: .text%__1cFStateK_sub_Op_If6MpknENode__v_; +text: .text%__1cIciMethodbCinterpreter_invocation_count6M_i_; +text: .text%__1cTciConstantPoolCacheDget6Mi_pv_; +text: .text%__1cSInterpreterRuntimeMmonitorenter6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: subnode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: subnode.o; +text: .text%__1cSInterpreterRuntimePresolve_get_put6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cWConstantPoolCacheEntryJset_field6MnJBytecodesECode_2nLKlassHandle_iinITosState_ii_v_; +text: .text%__1cKCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMLinkResolverNresolve_field6FrnPFieldAccessInfo_nSconstantPoolHandle_inJBytecodesECode_ipnGThread__v_; +text: .text%__1cKciTypeFlowFRangeNget_block_for6Mpn0AGJsrSet_n0AMCreateOption__pn0AFBlock__; +text: .text%__1cQsubI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cXmembar_acquire_lockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQaddP_reg_regNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cKCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPCountedLoopNodeGOpcode6kM_i_; +text: .text%__1cUGenericGrowableArrayMraw_contains6kMpknEGrET__i_; +text: .text%__1cPTwoOopHashtableMcompute_hash6MnMsymbolHandle_nGHandle__I_: systemDictionary.o; +text: .text%__1cSInterpreterRuntimeLmonitorexit6FpnKJavaThread_pnPBasicObjectLock__v_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: subnode.o; +text: .text%__1cIAndLNodeGOpcode6kM_i_; +text: .text%__1cIGraphKitOset_all_memory6MpnENode__v_; +text: .text%__1cENodeHis_Goto6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionarybEresolve_instance_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cVjava_lang_ClassLoaderbBnon_reflection_class_loader6FpnHoopDesc__2_; +text: .text%__1cHnmethodIis_alive6kM_i_: nmethod.o; +text: .text%__1cFParseFBlockKinit_graph6Mp0_v_; +text: .text%__1cMTypeKlassPtrEmake6FnHTypePtrDPTR_pnHciKlass_i_pk0_; +text: .text%__1cKRelocationLspec_simple6FnJrelocInfoJrelocType__nQRelocationHolder__; +text: .text%__1cRshrI_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOkill_dead_code6FpnENode_pnMPhaseIterGVN__i_: node.o; +text: .text%__1cMPrefetchNodeGOpcode6kM_i_; +text: .text%__1cCosGmalloc6FI_pv_; +text: .text%__1cSInterpreterRuntimeOresolve_invoke6FpnKJavaThread_nJBytecodesECode__v_; +text: .text%__1cIGraphKitTadd_exception_state6MpnNSafePointNode__v_; +text: .text%__1cIimmPOperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: cfgnode.o; +text: .text%__1cRRawBytecodeStreamMset_interval6Mii_v_; +text: .text%__1cIregDOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cKstoreINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICodeHeapLheader_size6F_I_; +text: .text%__1cQsubI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: connode.o; +text: .text%__1cFciEnvIis_in_vm6F_i_; +text: .text%__1cIMachNodeJis_MachIf6kM_pknKMachIfNode__: ad_sparc_misc.o; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: thread.o; +text: .text%__1cWConstantPoolCacheEntryKset_method6MnJBytecodesECode_nMmethodHandle_i_v_; +text: .text%__1cKciTypeFlowLStateVectorEmeet6Mpk1_i_; +text: .text%__1cNbranchConNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadRangeNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParseMdo_one_block6M_v_; +text: .text%__1cOPhaseIdealLoopRregister_new_node6MpnENode_2_v_; +text: .text%__1cLstoreB0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: callnode.o; +text: .text%__1cIAddINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIJVMStateKclone_deep6kM_p0_; +text: .text%__1cIJVMStateLdebug_depth6kM_I_; +text: .text%__1cENodeNadd_req_batch6Mp0I_v_; +text: .text%__1cIGraphKitTadd_safepoint_edges6MpnNSafePointNode_i_v_; +text: .text%__1cKciTypeFlowLStateVectorOpush_translate6MpnGciType__v_; +text: .text%__1cJloadFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: node.o; +text: .text%__1cMMachCallNodeHis_Call6M_pnICallNode__: ad_sparc_misc.o; +text: .text%__1cPVirtualCallDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cIMachNodeOpipeline_class6F_pknIPipeline__; +text: .text%__1cNloadRangeNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMindirectOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cHMatcherScalling_convention6FpnLRegPair_Ii_v_; +text: .text%__1cQSystemDictionarybCfind_instance_or_array_klass6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cIPhaseGVNUtransform_no_reclaim6MpnENode__2_; +text: .text%__1cIAddLNodeGOpcode6kM_i_; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_nGHandle_2ipnGThread__pnMklassOopDesc__; +text: .text%__1cLLShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOMethodLivenessKBasicBlockJpropagate6Mp0_v_; +text: .text%__1cFciEnvWget_klass_by_name_impl6MpnHciKlass_pnIciSymbol_i_2_; +text: .text%__1cKciTypeFlowGJsrSet2t6MpnFArena_i_v_; +text: .text%__1cRMachSafePointNode2t6M_v_; +text: .text%__1cHMatcherKmatch_sfpt6MpnNSafePointNode__pnIMachNode__; +text: .text%__1cMFastLockNodeGOpcode6kM_i_; +text: .text%__1cRInlineCacheBufferIcontains6FpC_i_; +text: .text%__1cLConvL2INodeGOpcode6kM_i_; +text: .text%__1cIXorINodeGOpcode6kM_i_; +text: .text%__1cICallNodeOis_CallRuntime6kM_pknPCallRuntimeNode__: callnode.o; +text: .text%__1cMVirtualSpaceOcommitted_size6kM_I_; +text: .text%__1cJloadCNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cXinsert_anti_dependences6FrpnFBlock_pnENode_rnLBlock_Array__i_: gcm.o; +text: .text%__1cPorI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcompU_iRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPorI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKTypeAryPtrFklass6kM_pnHciKlass__; +text: .text%__1cIGraphKitbDtransfer_exceptions_into_jvms6M_pnIJVMState__; +text: .text%__1cOPhaseIdealLoopHdom_lca6kMpnENode_2_2_; +text: .text%__1cLTypeInstPtrFxdual6kM_pknEType__; +text: .text%__1cNLoadRangeNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHMatcherPc_frame_pointer6kM_nHOptoRegEName__; +text: .text%__1cFBlockKsched_call6MrnHMatcher_rnLBlock_Array_IrnJNode_List_pipnMMachCallNode_rnJVectorSet__I_; +text: .text%__1cSsafePoint_pollNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMMachCallNode2t6M_v_; +text: .text%__1cILoadNodeHsize_of6kM_I_; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: methodLiveness.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodKlass.o; +text: .text%__1cRInterpretedRFrameKtop_method6kM_nMmethodHandle__: rframe.o; +text: .text%__1cICmpPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIGraphKitJsync_jvms6kM_pnIJVMState__; +text: .text%__1cICmpUNodeDsub6kMpknEType_3_3_; +text: .text%__1cHnmethodOis_not_entrant6kM_i_: nmethod.o; +text: .text%__1cNprefetch2NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cEUTF8Hstrrchr6FpWiW_1_; +text: .text%__1cIciMethodRhas_compiled_code6M_i_; +text: .text%__1cPcompP_iRegPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPsp_ptr_RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cPClassFileParserbCverify_legal_field_signature6MnMsymbolHandle_1pnGThread__v_; +text: .text%__1cPClassFileParserXverify_legal_field_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cLOptoRuntimePnew_typeArray_C6FnJBasicType_ipnKJavaThread__v_; +text: .text%__1cRshrP_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXvirtual_call_RelocationIparse_ic6FrpnICodeBlob_rpC5rppnHoopDesc_pi_nNRelocIterator__; +text: .text%__1cITypeLongEmake6Fxxi_pk0_; +text: .text%__1cRloadConP_pollNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%JVM_GetCPMethodNameUTF; +text: .text%__1cMtlsLoadPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreB0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRloadConP_pollNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIimmIOperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cLstoreI0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: cfgnode.o; +text: .text%__1cNSharedRuntimeEldiv6Fxx_x_; +text: .text%__1cHBitDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cURethrowExceptionNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cQSystemDictionarybOfind_constrained_instance_or_array_klass6FnMsymbolHandle_nGHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cQsubI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cYciExceptionHandlerStreamEnext6M_v_: ciTypeFlow.o; +text: .text%__1cKReturnNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cJloadBNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIciSymbol2t6MnMsymbolHandle__v_; +text: .text%__1cQaddP_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKmethodOperGmethod6kM_i_: ad_sparc.o; +text: .text%__1cQjava_lang_StringQbasic_create_oop6FpnQtypeArrayOopDesc_ipnGThread__pnHoopDesc__; +text: .text%__1cFKlassIsubklass6kM_p0_; +text: .text%__1cNinstanceKlassbBallocate_permanent_instance6MpnGThread__pnPinstanceOopDesc__; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: instanceKlass.o; +text: .text%__1cXInterpreterFrameClosureJoffset_do6Mi_v_: frame.o; +text: .text%__1cENodeHis_Goto6kM_I_: cfgnode.o; +text: .text%__1cICmpINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPemit_call_reloc6FrnKCodeBuffer_inJrelocInfoJrelocType_iii_v_; +text: .text%__1cTconstantPoolOopDescOstring_at_impl6FnSconstantPoolHandle_ipnGThread__pnHoopDesc__; +text: .text%__1cEUTF8Sconvert_to_unicode6FpkcpHi_v_; +text: .text%__1cIMulLNodeGOpcode6kM_i_; +text: .text%__1cKReturnNodeKmatch_edge6kMI_I_; +text: .text%__1cENodeHis_Call6M_pnICallNode__: callnode.o; +text: .text%__1cILoopNodeHis_Loop6M_p0_: classes.o; +text: .text%__1cGOopMap2t6Mii_v_; +text: .text%__1cJOopMapSetKadd_gc_map6MiipnGOopMap__v_; +text: .text%__1cNloadConP0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJLoadSNodeGOpcode6kM_i_; +text: .text%__1cLPCTableNodeLbottom_type6kM_pknEType__; +text: .text%__1cKBranchDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cMCreateExNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cRloadConP_pollNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCodeCacheEnext6FpnICodeBlob__2_; +text: .text%__1cENodeJis_Region6kM_pknKRegionNode__: connode.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: connode.o; +text: .text%__1cRcmpFastUnlockNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadLNodeGOpcode6kM_i_; +text: .text%__1cMciMethodDataLhas_trap_at6MpnLProfileData_i_i_; +text: .text%__1cPThreadLocalNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOMacroAssemblerZneeds_explicit_null_check6Fi_i_; +text: .text%__1cSaddI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKReturnNodeGOpcode6kM_i_; +text: .text%__1cNinstanceKlassPinitialize_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: frame.o; +text: .text%__1cTconstantPoolOopDescbBbasic_type_for_signature_at6Mi_nJBasicType__; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: callnode.o; +text: .text%__1cNflagsRegUOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIGraphKitbLset_predefined_input_for_runtime_call6MpnNSafePointNode__v_; +text: .text%__1cRshlI_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConINodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cNCatchProjNodeEhash6kM_I_; +text: .text%__1cEUTF8Ounicode_length6Fpkci_i_; +text: .text%__1cHCompileZintrinsic_insertion_index6MpnIciMethod_i_i_; +text: .text%__1cTOopMapForCacheEntryRpossible_gc_point6MpnOBytecodeStream__i_; +text: .text%__1cYDebugInformationRecorderNadd_safepoint6MiipnGOopMap__v_; +text: .text%__1cHCompileTProcess_OopMap_Node6MpnIMachNode_i_v_; +text: .text%__1cRMachSafePointNodePis_MachCallLeaf6M_pnQMachCallLeafNode__: ad_sparc_misc.o; +text: .text%__1cRMachSafePointNodeLset_oop_map6MpnGOopMap__v_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_ref_ReferenceNreferent_addr6FpnHoopDesc__p2_; +text: .text%__1cNCallGenerator2t6MpnIciMethod__v_; +text: .text%__1cRMachSafePointNodeSis_MachCallRuntime6M_pnTMachCallRuntimeNode__: ad_sparc_misc.o; +text: .text%__1cKCompiledIC2t6MpnKRelocation__v_; +text: .text%__1cNMachIdealNodePoper_input_base6kM_I_: machnode.o; +text: .text%__1cSCallLeafDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHRegMaskQis_aligned_Pairs6kM_i_; +text: .text%__1cKCompiledICOic_destination6kM_pC_; +text: .text%__1cHTypeAryFxmeet6kMpknEType__3_; +text: .text%__1cPClassFileParserbCverify_legal_field_modifiers6MiipnGThread__v_; +text: .text%__1cPClassFileParserWparse_field_attributes6MnSconstantPoolHandle_iHpHpi2pnPtypeArrayHandle_pnGThread__v_; +text: .text%__1cICallNodeJideal_reg6kM_I_: callnode.o; +text: .text%__1cLStringTableGintern6FpnNsymbolOopDesc_pnGThread__pnHoopDesc__; +text: .text%__1cNsymbolOopDescKas_unicode6kMri_pH_; +text: .text%__1cPmethodDataKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cKstorePNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowQadd_to_work_list6Mpn0AFBlock__v_; +text: .text%__1cKciTypeFlowKflow_block6Mpn0AFBlock_pn0ALStateVector_pn0AGJsrSet__v_; +text: .text%__1cKciTypeFlowFBlockKsuccessors6MpnQciByteCodeStream_pn0ALStateVector_pn0AGJsrSet__pnNGrowableArray4Cp1___; +text: .text%__1cRMachSafePointNodeRis_safepoint_node6kM_i_: ad_sparc_misc.o; +text: .text%__1cEUTF8Enext6FpkcpH_pc_; +text: .text%__1cJVectorSetFClear6M_v_; +text: .text%__1cHCompileSflatten_alias_type6kMpknHTypePtr__3_; +text: .text%__1cMCallJavaNodeLis_CallJava6kM_pk0_: callnode.o; +text: .text%__1cQMachCallJavaNodePis_MachCallJava6M_p0_: ad_sparc_misc.o; +text: .text%__1cCosEfree6Fpv_v_; +text: .text%__1cICallNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cKTypeOopPtrFempty6kM_i_; +text: .text%__1cKciTypeFlowFBlock2t6Mp0pn0AFRange_pn0AGJsrSet__v_; +text: .text%__1cRshrI_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmpFastLockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cYciExceptionHandlerStreamFcount6M_i_; +text: .text%__1cKciTypeFlowFBlockScompute_exceptions6M_v_; +text: .text%__1cIPhaseIFGFUnion6MII_v_; +text: .text%__1cLstoreB0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cYCallStaticJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWMachCallStaticJavaNodeVis_MachCallStaticJava6M_p0_: ad_sparc_misc.o; +text: .text%__1cILoopNodeGOpcode6kM_i_; +text: .text%__1cRMachSafePointNodeWis_MachCallInterpreter6M_pnXMachCallInterpreterNode__: ad_sparc_misc.o; +text: .text%__1cICmpLNodeGOpcode6kM_i_; +text: .text%__1cOPhaseIdealLoopGspinup6MpnENode_2222pnLsmall_cache__2_; +text: .text%__1cQaddI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMindIndexOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cIConLNodeGOpcode6kM_i_; +text: .text%__1cMLinkResolverbNlinktime_resolve_virtual_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cJloadCNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%JVM_GetCPFieldSignatureUTF; +text: .text%__1cFframeQoops_do_internal6MpnKOopClosure_pnLRegisterMap_i_v_; +text: .text%__1cMLinkResolverbEruntime_resolve_virtual_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cPCountedLoopNodeOis_CountedLoop6M_p0_: classes.o; +text: .text%__1cENodeLnonnull_req6kM_p0_; +text: .text%__1cGciTypeMis_classless6kM_i_: ciType.o; +text: .text%__1cFKlassXcan_be_statically_bound6FpnNmethodOopDesc__i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: connode.o; +text: .text%__1cHnmethodZsize_of_exception_handler6F_i_; +text: .text%__1cYCallStaticJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYCallStaticJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cMelapsedTimerFstart6M_v_; +text: .text%__1cMelapsedTimerEstop6M_v_; +text: .text%__1cQandL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMURShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSaddP_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cObranchConPNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopOfind_use_block6MpnENode_22222_2_; +text: .text%__1cOPhaseIdealLoopKhandle_use6MpnENode_2pnLsmall_cache_22222_v_; +text: .text%__1cOMethodLivenessNmake_block_at6Mipn0AKBasicBlock__2_; +text: .text%__1cPorI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%jni_DeleteLocalRef: jni.o; +text: .text%__1cIGraphKit2t6M_v_; +text: .text%__1cMoutputStreamDput6Mc_v_; +text: .text%__1cIGraphKitNset_map_clone6MpnNSafePointNode__v_; +text: .text%__1cRInterpretedRFrameEinit6M_v_; +text: .text%__1cHMulNodeEhash6kM_I_; +text: .text%__1cENodeJset_req_X6MIp0pnMPhaseIterGVN__v_; +text: .text%__1cSInterpreterRuntimeDldc6FpnKJavaThread_i_v_; +text: .text%__1cJLoadINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cINodeHashLhash_insert6MpnENode__v_; +text: .text%__1cHTypeIntEmake6Fii_pk0_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: ad_sparc.o; +text: .text%__1cKstoreCNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlassKlass.o; +text: .text%__1cNSafePointNodeEhash6kM_I_: callnode.o; +text: .text%__1cENodeLbottom_type6kM_pknEType__; +text: .text%__1cKJNIHandlesKmake_local6FpnGThread_pnHoopDesc__pnI_jobject__; +text: .text%__1cKstoreCNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMCreateExNodeGpinned6kM_i_: classes.o; +text: .text%__1cIAddPNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQjava_lang_StringbBcreate_tenured_from_unicode6FpHipnGThread__nGHandle__; +text: .text%__1cKoopFactoryXnew_permanent_charArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cFParsePdo_field_access6Mii_v_; +text: .text%__1cKMemBarNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cGvframe2t6MpknFframe_pknLRegisterMap_pnKJavaThread__v_; +text: .text%__1cLRegisterMap2t6Mpk0_v_; +text: .text%__1cXmembar_acquire_lockNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cQmark_inner_loops6FpnIPhaseCFG_pnFBlock__v_: block.o; +text: .text%__1cILoadNodeEmake6FpnENode_22pknHTypePtr_pknEType_nJBasicType__p0_; +text: .text%__1cICallNodeSis_CallDynamicJava6kM_pknTCallDynamicJavaNode__: callnode.o; +text: .text%__1cJOopMapSetSfind_map_at_offset6kMii_pnGOopMap__; +text: .text%__1cICodeBlobbAoop_map_for_return_address6MpCi_pnGOopMap__; +text: .text%__1cNmethodOopDescWwas_executed_more_than6kMi_i_; +text: .text%__1cRshrI_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcompI_iRegNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIciSymbolEmake6Fpkc_p0_; +text: .text%__1cPorI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLPhaseValuesHzerocon6MnJBasicType__pnHConNode__; +text: .text%__1cGPcDesc2t6Miii_v_; +text: .text%__1cHnmethodKcopy_pc_at6MipnGPcDesc__v_; +text: .text%__1cHCompileKalias_type6MpnHciField__pn0AJAliasType__; +text: .text%__1cGvframeKnew_vframe6FpknFframe_pknLRegisterMap_pnKJavaThread__p0_; +text: .text%__1cPconvI2L_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMtlsLoadPNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cYcompareAndSwapL_boolNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAddINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIciMethodRget_flow_analysis6M_pnKciTypeFlow__; +text: .text%__1cWCallLeafNoFPDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmembar_acquireNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKbranchNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cKbranchNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXjava_lang_ref_ReferencePdiscovered_addr6FpnHoopDesc__p2_; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: loopnode.o; +text: .text%__1cOloadConI13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSaddL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetObjectField: jni.o; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_: methodDataOop.o; +text: .text%__1cSMemBarCPUOrderNodeGOpcode6kM_i_; +text: .text%__1cJFieldTypeOget_array_info6FpnNsymbolOopDesc_pip2pnGThread__nJBasicType__; +text: .text%__1cJFieldTypeYis_valid_array_signature6FpnNsymbolOopDesc__i_; +text: .text%__1cQandL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cWstatic_stub_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cQaddL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPmethodDataKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cPmethodDataKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cJloadBNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNbranchConNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRegionNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: ad_sparc_misc.o; +text: .text%__1cYcompareAndSwapL_boolNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: machnode.o; +text: .text%__1cRMachNullCheckNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cYinlineCallClearArrayNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConP0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitJmake_load6MpnENode_2pknEType_nJBasicType_i_2_; +text: .text%__1cOPhaseIdealLoopIsink_use6MpnENode_2_v_; +text: .text%__1cIGraphKitOreplace_in_map6MpnENode_2_v_; +text: .text%__1cENodeGis_Con6kM_I_: callnode.o; +text: .text%__1cNinstanceKlassLfind_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cIGraphKitNuncommon_trap6MipnHciKlass_pkci_v_; +text: .text%__1cHCompileKTracePhase2T6M_v_; +text: .text%__1cMPhaseChaitinLclone_projs6MpnFBlock_IpnENode_4rI_i_; +text: .text%__1cNinstanceKlassSlookup_osr_nmethod6kMkpnNmethodOopDesc_i_pnHnmethod__; +text: .text%__1cIJVMState2t6MpnIciMethod_p0_v_; +text: .text%__1cMLinkResolverbEruntime_resolve_special_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_4ipnGThread__v_; +text: .text%__1cMLinkResolverbFlinktime_resolve_special_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cIHaltNode2t6MpnENode_2_v_; +text: .text%__1cLOptoRuntimeSuncommon_trap_Type6F_pknITypeFunc__; +text: .text%__1cJloadLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadConP0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowGJsrSetNapply_control6Mp0pnQciByteCodeStream_pn0ALStateVector__v_; +text: .text%__1cSsafePoint_pollNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cINodeHashJhash_find6MpknENode__p1_; +text: .text%__1cQmulL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cOMethodLivenessKBasicBlock2t6Mp0ii_v_; +text: .text%__1cOMethodLivenessKBasicBlockQcompute_gen_kill6MpnIciMethod__v_; +text: .text%__1cQciByteCodeStreamZget_declared_field_holder6M_pnPciInstanceKlass__; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: classes.o; +text: .text%__1cOGenerateOopMapFppush6MpnNCellTypeState__v_; +text: .text%__1cJTypeTupleKmake_range6FpnLciSignature__pk0_; +text: .text%__1cJTypeTupleLmake_domain6FpnPciInstanceKlass_pnLciSignature__pk0_; +text: .text%__1cSmembar_acquireNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConUNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_ref_ReferenceJnext_addr6FpnHoopDesc__p2_; +text: .text%__1cMWarmCallInfoGis_hot6kM_i_; +text: .text%__1cMWarmCallInfoKalways_hot6F_p0_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6FnUtypeArrayKlassHandle_iipnGThread__pnMklassOopDesc__; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MiipnGThread__pnMklassOopDesc__; +text: .text%__1cSCompareAndSwapNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cLRethrowNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cTmembar_CPUOrderNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPcmpFastLockNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTCreateExceptionNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKciTypeFlowLStateVectorJdo_invoke6MpnQciByteCodeStream_i_v_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: connode.o; +text: .text%__1cQmulL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPcmpFastLockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreB0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadBNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cQaddP_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMtlsLoadPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cFTypeDCeq6kMpknEType__i_; +text: .text%__1cITypeLongEmake6Fx_pk0_; +text: .text%__1cKJavaThreadbHcheck_and_handle_async_exceptions6Mi_v_; +text: .text%__1cHciFieldPinitialize_from6MpnPfieldDescriptor__v_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: memnode.o; +text: .text%__1cMPhaseChaitinTsplit_Rematerialize6MpnENode_pnFBlock_IrInNGrowableArray4CI__ipIp2i_2_; +text: .text%__1cKimmI13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cJloadBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIGraphKitZadd_exception_states_from6MpnIJVMState__v_; +text: .text%__1cQandL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseKdo_get_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cMPhaseChaitinNFind_compress6MpknENode__I_; +text: .text%__1cQSystemDictionaryEfind6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNCatchProjNode2t6MpnENode_Ii_v_; +text: .text%__1cPVirtualCallDataSis_VirtualCallData6M_i_: ciMethodData.o; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: cfgnode.o; +text: .text%__1cHPhiNodeEmake6FpnENode_2_p0_; +text: .text%__1cNCatchProjNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cWCallLeafNoFPDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassOis_subclass_of6kMpnMklassOopDesc__i_; +text: .text%__1cLPCTableNodeHsize_of6kM_I_: classes.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: cfgnode.o; +text: .text%__1cLPCTableNodeKis_PCTable6kM_pk0_: classes.o; +text: .text%__1cNciCallProfileRapply_prof_factor6Mf_v_; +text: .text%__1cIciMethodTcall_profile_at_bci6Mi_nNciCallProfile__; +text: .text%__1cHCompileOcall_generator6MpnIciMethod_ipnIJVMState_if_pnNCallGenerator__; +text: .text%__1cHCompileOfind_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cIProjNodeDcmp6kMrknENode__I_; +text: .text%__1cLklassVtableIindex_of6kMpnNmethodOopDesc_i_i_; +text: .text%__1cLLShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFParseMprofile_call6MpnENode__v_; +text: .text%__1cFciEnvbTget_instance_klass_for_declared_method_holder6FpnHciKlass__pnPciInstanceKlass__; +text: .text%__1cIGraphKitWround_double_arguments6MpnIciMethod__v_; +text: .text%__1cQciByteCodeStreambAget_declared_method_holder6M_pnHciKlass__; +text: .text%__1cIGraphKitTround_double_result6MpnIciMethod__v_; +text: .text%__1cFParseHdo_call6M_v_; +text: .text%__1cNloadConP0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIregFOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cHMulNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMPhaseIterGVNJtransform6MpnENode__2_; +text: .text%__1cHTypeIntFwiden6kMpknEType__3_; +text: .text%__1cQxorI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsafePoint_pollNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadSNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKarrayKlassLobject_size6kMi_i_; +text: .text%__1cIciMethodLscale_count6Mi_i_; +text: .text%__1cKMemBarNodeEhash6kM_I_; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlass.o; +text: .text%__1cMLinkResolverVresolve_invokevirtual6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKMemoryPoolYrecord_peak_memory_usage6M_v_; +text: .text%__1cMURShiftLNodeGOpcode6kM_i_; +text: .text%__1cOCompilerOracleOshould_exclude6FnMmethodHandle__i_; +text: .text%__1cIGraphKitUmake_exception_state6MpnENode__pnNSafePointNode__; +text: .text%__1cLProfileDataOtranslate_from6Mp0_v_: ciMethodData.o; +text: .text%__1cLstoreI0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarI_reg_imm5NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKBranchDataNis_BranchData6M_i_: ciMethodData.o; +text: .text%__1cKRegionNodeGpinned6kM_i_: loopnode.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: memnode.o; +text: .text%__1cLBuildCutout2t6MpnIGraphKit_pnENode_ff_v_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: graphKit.o; +text: .text%__1cTCompareAndSwapLNodeGOpcode6kM_i_; +text: .text%__1cNloadRangeNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQxorI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIJumpDataLis_JumpData6M_i_: ciMethodData.o; +text: .text%__1cMMergeMemNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLLShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNflagsRegLOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cCosOis_interrupted6FpnGThread_i_i_; +text: .text%__1cLmethodKlassNoop_is_method6kM_i_: methodKlass.o; +text: .text%__1cQsubI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXmembar_release_lockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: memnode.o; +text: .text%__1cKarrayKlassGvtable6kM_pnLklassVtable__; +text: .text%__1cRshrI_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeOis_CountedLoop6M_pnPCountedLoopNode__: loopnode.o; +text: .text%__1cTcan_branch_register6FpnENode_1_i_; +text: .text%__1cQCallLeafNoFPNodeGOpcode6kM_i_; +text: .text%__1cMURShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKstoreCNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHConNodeEmake6FpknEType__p0_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: memnode.o; +text: .text%__1cFStateM_sub_Op_ConP6MpknENode__v_; +text: .text%__1cRshrP_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitMsaved_ex_oop6FpnNSafePointNode__pnENode__; +text: .text%__1cISubINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPciInstanceKlassFsuper6M_p0_; +text: .text%__1cIBoolNodeHsize_of6kM_I_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: callnode.o; +text: .text%__1cSobjArrayKlassKlassIoop_size6kMpnHoopDesc__i_: objArrayKlassKlass.o; +text: .text%__1cPcompP_iRegPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvI2D_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotLOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cJloadPNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSsafePoint_pollNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPBytecode_invokeJsignature6kM_pnNsymbolOopDesc__; +text: .text%__1cFframebGinterpreter_callee_receiver_addr6MnMsymbolHandle__ppnHoopDesc__; +text: .text%__1cHMonitorKnotify_all6M_i_; +text: .text%__1cNSignatureInfoGdo_int6M_v_: bytecode.o; +text: .text%__1cOstackSlotLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIciMethodbAinterpreter_throwout_count6kM_i_; +text: .text%__1cOCompilerOracleNshould_inline6FnMmethodHandle__i_; +text: .text%__1cIciMethodOshould_exclude6M_i_; +text: .text%__1cKInlineTreeMok_to_inline6MpnIciMethod_pnIJVMState_rnNciCallProfile_pnMWarmCallInfo__8_; +text: .text%__1cKInlineTreeWfind_subtree_from_root6Fp0pnIJVMState_pnIciMethod_i_1_; +text: .text%__1cIciMethodNshould_inline6M_i_; +text: .text%__1cOGenerateOopMapbAget_basic_block_containing6kMi_pnKBasicBlock__; +text: .text%__1cGThreadXclear_pending_exception6M_v_; +text: .text%__1cICodeHeapSallocated_capacity6kM_I_; +text: .text%__1cSstkL_to_regD_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescbHhas_unloaded_classes_in_signature6FnMmethodHandle_pnGThread__i_; +text: .text%__1cTmembar_CPUOrderNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: connode.o; +text: .text%__1cNprefetch2NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cICHeapObj2n6FI_pv_; +text: .text%__1cQaddI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJStartNodeIis_Start6M_p0_: classes.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodKlass.o; +text: .text%__1cWCallLeafNoFPDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cCosFsleep6FpnGThread_xi_i_; +text: .text%__1cIos_sleep6Fxi_i_: os_solaris.o; +text: .text%__1cSstkL_to_regD_2NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFTypeDEhash6kM_i_; +text: .text%__1cKTypeRawPtrHget_con6kM_i_; +text: .text%__1cJStartNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%jni_ExceptionOccurred: jni.o; +text: .text%__1cPconvI2L_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowLStateVectorStype_meet_internal6FpnGciType_3p0_3_; +text: .text%__1cMloadConINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGIfNodeHsize_of6kM_I_: classes.o; +text: .text%__1cPconvL2I_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIimmLOperJconstantL6kM_x_: ad_sparc_clone.o; +text: .text%__1cTStackWalkCompPolicyRcompilation_level6MnMmethodHandle_i_i_; +text: .text%jni_GetByteArrayRegion: jni.o; +text: .text%__1cIGraphKitTset_all_memory_call6MpnENode__v_; +text: .text%__1cSHighResTimeSamplerLtake_sample6M_x_: statSampler.o; +text: .text%__1cHCompileFstart6kM_pnJStartNode__; +text: .text%__1cRis_error_reported6F_i_; +text: .text%__1cNWatcherThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cPStatSamplerTaskEtask6M_v_: statSampler.o; +text: .text%__1cMPeriodicTaskOreal_time_tick6FI_v_; +text: .text%__1cQPlaceholderTableKfind_entry6MiInMsymbolHandle_nGHandle__pnNsymbolOopDesc__; +text: .text%__1cIParmNodeJideal_reg6kM_I_; +text: .text%__1cQandL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIMachNodeRget_base_and_disp6kMrirpknHTypePtr__pknENode__; +text: .text%__1cQSystemDictionarybBresolve_array_class_or_null6FnMsymbolHandle_nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cIregFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cHCompilebAallow_range_check_smearing6kM_i_; +text: .text%__1cRbranchLoopEndNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cGRFrame2t6MnFframe_pnKJavaThread_kp0_v_; +text: .text%__1cIciMethodWwas_executed_more_than6Mi_i_; +text: .text%jni_GetArrayLength: jni.o; +text: .text%__1cIMachNodeHtwo_adr6kM_I_: machnode.o; +text: .text%__1cENodeHis_Proj6M_pnIProjNode__: machnode.o; +text: .text%__1cPciInstanceKlassUget_canonical_holder6Mi_p0_; +text: .text%__1cJloadLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOClearArrayNodeGOpcode6kM_i_; +text: .text%__1cWCallLeafNoFPDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserbDverify_legal_method_signature6MnMsymbolHandle_1pnGThread__i_; +text: .text%__1cVCompressedWriteStreamEgrow6M_v_; +text: .text%__1cPcheckCastPPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_Write; +text: .text%__1cLciSignature2t6MpnHciKlass_pnIciSymbol__v_; +text: .text%__1cIciMethod2t6MnMmethodHandle__v_; +text: .text%__1cIHaltNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cWShouldNotReachHereNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKRelocationJpack_data6M_i_: relocInfo.o; +text: .text%__1cNinstanceKlassVis_same_class_package6FpnHoopDesc_pnNsymbolOopDesc_24_i_; +text: .text%__1cLOpaque1NodeGOpcode6kM_i_; +text: .text%__1cQmulL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cEUTF8Fequal6FpWi1i_i_; +text: .text%__1cSbranchCon_longNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKstoreCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHAddNodePadd_of_identity6kMpknEType_3_3_; +text: .text%__1cUcompU_iReg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cGThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%JVM_RawMonitorEnter; +text: .text%__1cFMutexMjvm_raw_lock6M_v_; +text: .text%JVM_RawMonitorExit; +text: .text%__1cFMutexOjvm_raw_unlock6M_v_; +text: .text%__1cKg1RegIOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: loopnode.o; +text: .text%__1cOMachReturnNodeKin_RegMask6kMI_rknHRegMask__; +text: .text%__1cPClassFileParserUskip_over_field_name6MpciI_1_; +text: .text%__1cMTypeKlassPtrKadd_offset6kMi_pknHTypePtr__; +text: .text%__1cIGraphKitNcast_not_null6MpnENode__2_; +text: .text%__1cWShouldNotReachHereNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitTtoo_many_recompiles6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cPcmpFastLockNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cETypeRget_typeflow_type6FpnGciType__pk0_; +text: .text%__1cOJNIHandleBlockNrelease_block6Fp0pnGThread__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlass.o; +text: .text%__1cRcmpFastUnlockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXinitialize_static_field6FpnPfieldDescriptor_pnGThread__v_: classFileParser.o; +text: .text%__1cURethrowExceptionNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOJNIHandleBlockOallocate_block6FpnGThread__p0_; +text: .text%__1cIGraphKitOtoo_many_traps6MnODeoptimizationLDeoptReason__i_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: bytecode.o; +text: .text%__1cKBufferBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cKBufferBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cSandI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIAddINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cLTypeInstPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cFParseRensure_memory_phi6Mii_pnHPhiNode__; +text: .text%__1cMloadConLNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cFParseFmerge6Mi_v_; +text: .text%__1cFParseUprofile_taken_branch6Mi_v_; +text: .text%__1cNSafePointNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cYcompareAndSwapL_boolNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cWstatic_stub_RelocationJpack_data6M_i_; +text: .text%__1cILoopNodeHis_Loop6M_p0_: loopnode.o; +text: .text%__1cNCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cQxorI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJTypeTupleFxdual6kM_pknEType__; +text: .text%__1cNLoadKlassNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPorI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRethrowNodeGOpcode6kM_i_; +text: .text%__1cYinlineCallClearArrayNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadSNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cYinlineCallClearArrayNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeHeapIcapacity6kM_I_; +text: .text%__1cKMemoryPoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cMCodeHeapPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cPcmpFastLockNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMCodeHeapPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cNloadKlassNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFArena2T6M_v_; +text: .text%__1cMLinkResolverTresolve_static_call6FrnICallInfo_rnLKlassHandle_nMsymbolHandle_53iipnGThread__v_; +text: .text%__1cKMemBarNodeFmatch6MpknIProjNode_pknHMatcher__pnENode__; +text: .text%__1cOCallRelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cLRuntimeStubYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cHoopDescSslow_identity_hash6M_i_; +text: .text%__1cSObjectSynchronizerXidentity_hash_value_for6FnGHandle__i_; +text: .text%__1cJloadCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIimmPOperPconstant_is_oop6kM_i_: ad_sparc_clone.o; +text: .text%__1cLPCTableNodeEhash6kM_I_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnMklassOopDesc__i_; +text: .text%__1cHConNodeLout_RegMask6kM_rknHRegMask__: classes.o; +text: .text%__1cXPhaseAggressiveCoalesceYinsert_copy_with_overlap6MpnFBlock_pnENode_II_v_; +text: .text%__1cOloadConI13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMtlsLoadPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeGis_Con6kM_I_: multnode.o; +text: .text%__1cQandI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMPhaseChaitinNFind_compress6MI_I_; +text: .text%__1cITypeLongEmake6Fxx_pk0_; +text: .text%__1cMindIndexOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFStateN_sub_Op_LoadP6MpknENode__v_; +text: .text%__1cFframeVinterpreter_frame_bci6kM_i_; +text: .text%__1cJOopMapSetTupdate_register_map6FpknFframe_pnICodeBlob_pnLRegisterMap__v_; +text: .text%__1cNGCTaskManagerIget_task6MI_pnGGCTask__; +text: .text%__1cLGCTaskQdDueueGremove6M_pnGGCTask__; +text: .text%__1cNGCTaskManagerYshould_release_resources6MI_i_; +text: .text%__1cLGCTaskQdDueueHenqueue6MpnGGCTask__v_; +text: .text%__1cNGCTaskManagerPnote_completion6MI_v_; +text: .text%__1cITypeLongFempty6kM_i_; +text: .text%__1cJloadBNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQandI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLklassVtableXvtable_accessibility_at6Mi_n0AKAccessType__; +text: .text%__1cIGraphKitHjava_bc6kM_nJBytecodesECode__; +text: .text%__1cIGraphKitNbuiltin_throw6MnODeoptimizationLDeoptReason_pnENode__v_; +text: .text%__1cOGenerateOopMapHget_var6Mi_nNCellTypeState__; +text: .text%__1cRinterpretedVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cSmembar_acquireNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetSuperclass: jni.o; +text: .text%__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_; +text: .text%__1cTAbstractInterpreterbFsize_top_interpreter_activation6FpnNmethodOopDesc__i_; +text: .text%__1cCosbCstack_shadow_pages_available6FpnGThread_nMmethodHandle__i_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: ad_sparc.o; +text: .text%__1cIMulINodeGOpcode6kM_i_; +text: .text%__1cKInlineTreePshouldNotInline6kMpnIciMethod_pnMWarmCallInfo__pkc_; +text: .text%__1cRcompL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadCNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciMethodbHhas_unloaded_classes_in_signature6M_i_; +text: .text%__1cJloadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGGCTask2t6M_v_; +text: .text%__1cJloadSNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_: classes.o; +text: .text%__1cIJumpDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cObranchConUNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cObranchConPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cITypeFuncFxdual6kM_pknEType__; +text: .text%__1cQjava_lang_StringGlength6FpnHoopDesc__i_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: nmethod.o; +text: .text%__1cKstoreINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKMemBarNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cFStateM_sub_Op_CmpI6MpknENode__v_; +text: .text%__1cJcmpOpOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cKTypeRawPtrCeq6kMpknEType__i_; +text: .text%__1cGciType2t6MnLKlassHandle__v_; +text: .text%__1cHciKlass2t6MnLKlassHandle__v_; +text: .text%__1cKTypeAryPtrEmake6FnHTypePtrDPTR_pknHTypeAry_pnHciKlass_ii_pk0_; +text: .text%__1cMindirectOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFParseRoptimize_inlining6MpnIciMethod_ipnPciInstanceKlass_24irnKInlineTreeLInlineStyle_r2_v_; +text: .text%__1cSPSPromotionManagerbBgc_thread_promotion_manager6Fi_p0_; +text: .text%__1cQxorI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJloadLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIregFOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cKcmpOpPOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cNloadKlassNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHPhiNodeMslice_memory6kMpknHTypePtr__p0_; +text: .text%__1cPCheckCastPPNodeJideal_reg6kM_I_: connode.o; +text: .text%__1cObranchConPNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cObranchConPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICHeapObj2k6Fpv_v_; +text: .text%__1cSaddL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRmethodDataOopDescJbci_to_dp6Mi_pC_; +text: .text%__1cMloadConFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRInvocationCounterJset_carry6M_v_; +text: .text%__1cFArena2t6M_v_; +text: .text%__1cRInterpreterOopMapLiterate_oop6MpnNOffsetClosure__v_; +text: .text%__1cRInterpreterOopMap2T6M_v_; +text: .text%__1cFframeToops_interpreted_do6MpnKOopClosure_pknLRegisterMap_i_v_; +text: .text%__1cLOopMapCacheGlookup6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cNinstanceKlassImask_for6MnMmethodHandle_ipnRInterpreterOopMap__v_; +text: .text%__1cNmethodOopDescImask_for6MipnRInterpreterOopMap__v_; +text: .text%__1cRInterpreterOopMap2t6M_v_; +text: .text%__1cJloadCNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadSNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cISubINodeDsub6kMpknEType_3_3_; +text: .text%__1cFParseOreturn_current6MpnENode__v_; +text: .text%__1cRsarI_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIGraphKitPstore_to_memory6MpnENode_22nJBasicType_i_2_; +text: .text%__1cJStoreNodeEmake6FpnENode_22pknHTypePtr_2nJBasicType__p0_; +text: .text%__1cMMonitorValue2t6MpnKScopeValue_nILocation__v_; +text: .text%__1cLBoxLockNodeKis_BoxLock6kM_pk0_: classes.o; +text: .text%__1cMMonitorValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cLBoxLockNodeKstack_slot6FpnENode__nHOptoRegEName__; +text: .text%__1cMloadConLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJStoreNodeSIdeal_masked_input6MpnIPhaseGVN_I_pnENode__; +text: .text%__1cHMatcherPstack_alignment6F_I_; +text: .text%jni_GetPrimitiveArrayCritical: jni.o; +text: .text%jni_ReleasePrimitiveArrayCritical: jni.o; +text: .text%__1cPconvI2L_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeIget_long6kM_x_; +text: .text%__1cNMemoryServiceXtrack_memory_pool_usage6FpnKMemoryPool__v_; +text: .text%__1cSmembar_releaseNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJimmU5OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cPciInstanceKlass2t6MnLKlassHandle__v_; +text: .text%__1cLOpaque1NodeEhash6kM_I_; +text: .text%__1cJStoreNodeZIdeal_sign_extended_input6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSbranchCon_longNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapGppload6MpnNCellTypeState_i_v_; +text: .text%__1cSmembar_releaseNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNbranchConNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRshrI_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFciEnvVnotice_inlined_method6MpnIciMethod__v_; +text: .text%__1cKciTypeFlowGJsrSetSis_compatible_with6Mp1_i_; +text: .text%__1cFKlassTarray_klass_or_null6Mi_pnMklassOopDesc__; +text: .text%__1cZCallDynamicJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJMultiNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKStoreLNodeGOpcode6kM_i_; +text: .text%__1cbBopt_virtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cTconstantPoolOopDescbCklass_ref_at_if_loaded_check6FnSconstantPoolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cHciField2t6MpnPciInstanceKlass_i_v_; +text: .text%__1cNloadKlassNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cETypeCeq6kMpk0_i_; +text: .text%__1cOJNIHandleBlockHoops_do6MpnKOopClosure__v_; +text: .text%__1cOGenerateOopMapJdo_method6Miiii_v_; +text: .text%__1cFParseRbranch_prediction6Mrf_f_; +text: .text%__1cRsarI_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cHTypeAryFempty6kM_i_; +text: .text%__1cKTypeAryPtrFempty6kM_i_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: methodDataKlass.o; +text: .text%__1cOMacroAssemblerFjumpl6MrnHAddress_pnMRegisterImpl_ipkci_v_; +text: .text%__1cOMacroAssemblerEjump6MrnHAddress_ipkci_v_; +text: .text%__1cIciMethodLis_accessor6kM_i_; +text: .text%__1cRbranchLoopEndNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceKlass.o; +text: .text%__1cQmulL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFframeUentry_frame_is_first6kM_i_; +text: .text%__1cJiRegIOperFclone6kM_pnIMachOper__; +text: .text%__1cLstoreP0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRethrowNodeKmatch_edge6kMI_I_; +text: .text%__1cFTypeFEhash6kM_i_; +text: .text%__1cHnmethodbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cHnmethodHoops_do6MpnKOopClosure__v_; +text: .text%__1cFStateM_sub_Op_AddI6MpknENode__v_; +text: .text%__1cIciMethodPcan_be_compiled6M_i_; +text: .text%__1cOParseGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cFParseQcreate_entry_map6M_pnNSafePointNode__; +text: .text%__1cFArenaEused6kM_I_; +text: .text%__1cFParseLbuild_exits6M_v_; +text: .text%__1cFParseIdo_exits6M_v_; +text: .text%__1cIciMethodVhas_balanced_monitors6M_i_; +text: .text%__1cFParse2t6MpnIJVMState_pnIciMethod_f_v_; +text: .text%__1cIBoolNodeDcmp6kMrknENode__I_; +text: .text%__1cQsubI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParsePdo_method_entry6M_v_; +text: .text%__1cNCallGeneratorKfor_inline6FpnIciMethod_f_p0_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2t6M_v_; +text: .text%__1cbGJvmtiVMObjectAllocEventCollector2T6M_v_; +text: .text%__1cQconstMethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQSystemDictionaryVadd_loader_constraint6FnMsymbolHandle_nGHandle_2pnGThread__v_; +text: .text%__1cVLoaderConstraintTableJadd_entry6MnMsymbolHandle_pnMklassOopDesc_nGHandle_34pnGThread__i_; +text: .text%__1cRciVirtualCallDataOtranslate_from6MpnLProfileData__v_; +text: .text%jni_IsSameObject: jni.o; +text: .text%__1cMloadConINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNbranchConNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cNbranchConNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQandL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLmethodKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cLsymbolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQaddL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassbDcheck_valid_for_instantiation6MipnGThread__v_; +text: .text%__1cJStartNodeOis_block_start6kM_i_: callnode.o; +text: .text%__1cRsarI_reg_imm5NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciObjectFklass6M_pnHciKlass__; +text: .text%__1cLSymbolTableFprobe6Fpkci_pnNsymbolOopDesc__; +text: .text%__1cSInterpreterRuntimebAfrequency_counter_overflow6FpnKJavaThread_pC_x_; +text: .text%__1cPThreadLocalNodeGOpcode6kM_i_; +text: .text%__1cENodeRlatency_from_uses6kMrnLBlock_Array_rnNGrowableArray4CI___i_; +text: .text%__1cPconvL2I_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cZPhaseConservativeCoalesceKupdate_ifg6MIIpnIIndexSet_2_v_; +text: .text%__1cZPhaseConservativeCoalesceMunion_helper6MpnENode_2II222pnFBlock_I_v_; +text: .text%__1cOMethodLivenessKBasicBlockJstore_one6Mi_v_; +text: .text%__1cIIndexSetEswap6Mp0_v_; +text: .text%__1cHTypeAryEmake6FpknEType_pknHTypeInt__pk0_; +text: .text%__1cXmembar_release_lockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserbCverify_legal_class_modifiers6MipnGThread__v_; +text: .text%__1cJloadLNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cRshrP_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKTypeAryPtrFxdual6kM_pknEType__; +text: .text%__1cFBlockTimplicit_null_check6MrnLBlock_Array_rnNGrowableArray4CI__pnENode_6_v_; +text: .text%__1cQandI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFframeNis_glue_frame6kM_i_; +text: .text%__1cLAccessFlagsPatomic_set_bits6Mi_v_; +text: .text%__1cFParseYprofile_not_taken_branch6M_v_; +text: .text%__1cQComputeCallStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cHciKlassNis_subtype_of6Mp0_i_; +text: .text%__1cbACallCompiledJavaDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: cfgnode.o; +text: .text%__1cRRawBytecodeStream2t6MnMmethodHandle__v_; +text: .text%__1cNinstanceKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cKstoreBNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQLibraryIntrinsicKis_virtual6kM_i_: library_call.o; +text: .text%__1cMPrefetchNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKCastPPNodeQIdeal_DU_postCCP6MpnIPhaseCCP__pnENode__; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: callnode.o; +text: .text%__1cKstorePNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOPhaseIdealLoopOsplit_thru_phi6MpnENode_2i_2_; +text: .text%__1cYDebugInformationRecorderNadd_dependent6MpnPciInstanceKlass_pnIciMethod__v_; +text: .text%__1cENodeGOpcode6kM_i_; +text: .text%__1cRshrP_reg_imm5NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQandI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMURShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciMethodbBinterpreter_call_site_count6Mi_i_; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cGBitMapIset_from6M0_v_; +text: .text%__1cNmethodOopDescWload_signature_classes6FnMmethodHandle_pnGThread__i_; +text: .text%__1cNCompileBrokerOcompile_method6FnMmethodHandle_i1ipkcpnGThread__pnHnmethod__; +text: .text%__1cNCompileBrokerTcompile_method_base6FnMmethodHandle_ii1ipkcpnGThread__pnHnmethod__; +text: .text%__1cTconstantPoolOopDescbDresolve_string_constants_impl6FnSconstantPoolHandle_pnGThread__v_; +text: .text%__1cHSubNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cNinstanceKlassPlink_class_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cMloadConFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFChunk2n6FII_pv_; +text: .text%__1cbACallCompiledJavaDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2L_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTCallDynamicJavaNodeGOpcode6kM_i_; +text: .text%__1cKstoreBNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cILoadNodeDcmp6kMrknENode__I_; +text: .text%__1cIciObject2t6M_v_; +text: .text%__1cENodeHdel_out6Mp0_v_: library_call.o; +text: .text%__1cSconstMethodOopDescZchecked_exceptions_length6kM_i_; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: cfgnode.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: cfgnode.o; +text: .text%__1cRcompL_reg_conNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHCompileXin_preserve_stack_slots6M_I_; +text: .text%__1cPciObjectFactoryUget_empty_methodData6M_pnMciMethodData__; +text: .text%__1cMciMethodData2t6M_v_; +text: .text%__1cLOopRecorderKfind_index6MpnI_jobject__i_; +text: .text%__1cJStartNodeOis_block_start6kM_i_: classes.o; +text: .text%__1cHOrINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFframeLreal_sender6kMpnLRegisterMap__0_; +text: .text%__1cFframeTis_first_java_frame6kM_i_; +text: .text%__1cGRFrameGcaller6M_p0_; +text: .text%__1cFframeNis_java_frame6kM_i_; +text: .text%__1cNprefetch2NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowFBlockPclone_loop_head6Mp0ip1pn0AGJsrSet__3_; +text: .text%__1cPCheckCastPPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cRshrP_reg_imm5NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cKRegionNodeJis_Region6kM_pk0_: loopnode.o; +text: .text%__1cFParseFdo_if6MpnENode_2nIBoolTestEmask_2_v_; +text: .text%__1cLCastP2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cXmembar_release_lockNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cJloadINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSandI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: callnode.o; +text: .text%__1cMPrefetchNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cXJNI_ArgumentPusherVaArgKget_object6M_v_: jni.o; +text: .text%__1cMloadConFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMCreateExNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJCodeCacheIcontains6Fpv_i_; +text: .text%__1cQaddL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMCallLeafNodeLis_CallLeaf6kM_pk0_: classes.o; +text: .text%__1cLPCTableNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMCreateExNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIMachNodeJis_MachIf6kM_pknKMachIfNode__: ad_sparc.o; +text: .text%__1cISubINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cFKlassQset_next_sibling6MpnMklassOopDesc__v_; +text: .text%__1cQdivD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNCatchProjNodeDcmp6kMrknENode__I_; +text: .text%__1cIGraphKitRmake_slow_call_ex6MpnENode_pnPciInstanceKlass__v_; +text: .text%__1cKTypeOopPtrEhash6kM_i_; +text: .text%__1cIMinINodeGOpcode6kM_i_; +text: .text%__1cYinlineCallClearArrayNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNflagsRegLOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cMURShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIConINodeHget_int6kMpi_i_: classes.o; +text: .text%__1cKOopClosureLdo_nmethods6kM_ki_: psTasks.o; +text: .text%__1cJOopMapSetGall_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure_pFppnHoopDesc_9E_v9B9B_v_; +text: .text%__1cFframeRoops_code_blob_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cJOopMapSetHoops_do6FpknFframe_pnICodeBlob_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cKTypeRawPtrFxmeet6kMpknEType__3_; +text: .text%__1cFBlockUhoist_LCA_above_defs6Mp01IrnLBlock_Array__1_; +text: .text%JVM_GetMethodIxModifiers; +text: .text%__1cMvframeStream2t6MpnKJavaThread_i_v_; +text: .text%__1cQjava_lang_StringGequals6FpnHoopDesc_pHi_i_; +text: .text%__1cIMulLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_: codeBlob.o; +text: .text%__1cVExceptionHandlerTableMadd_subtable6MipnNGrowableArray4Ci__2_v_; +text: .text%__1cPconvI2L_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLLShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cHMulNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cTCreateExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_IsInterface; +text: .text%__1cHciKlassOis_subclass_of6Mp0_i_; +text: .text%__1cJloadCNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRInterpretedRFrameOis_interpreted6kM_i_: rframe.o; +text: .text%__1cGRFrameLis_compiled6kM_i_: rframe.o; +text: .text%__1cPorI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIDivINodeGOpcode6kM_i_; +text: .text%__1cbACallCompiledJavaDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapTmerge_state_into_bb6MpnKBasicBlock__v_; +text: .text%__1cICodeHeapIallocate6MI_pv_; +text: .text%__1cICodeHeapPsearch_freelist6MI_pnJFreeBlock__; +text: .text%__1cRcompL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLOpaque1NodeLbottom_type6kM_pknEType__: connode.o; +text: .text%__1cNloadRangeNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQput_after_lookup6FnMsymbolHandle_0ppnLNameSigHash__i_; +text: .text%__1cSconvI2D_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHMatcherQis_spillable_arg6Fi_i_; +text: .text%__1cLRegisterMapFclear6Mpi_v_; +text: .text%__1cLRShiftLNodeGOpcode6kM_i_; +text: .text%__1cJCodeCacheIallocate6Fi_pnICodeBlob__; +text: .text%__1cSCountedLoopEndNodeKstride_con6kM_i_; +text: .text%__1cUPipeline_Use_Element2t6M_v_: output.o; +text: .text%__1cRshrL_reg_imm6NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHCompileSregister_intrinsic6MpnNCallGenerator__v_; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_: classes.o; +text: .text%__1cFParseSmerge_memory_edges6MpnMMergeMemNode_ii_v_; +text: .text%__1cNSCMemProjNodeGOpcode6kM_i_; +text: .text%__1cNimmP_pollOperEtype6kM_pknEType__: ad_sparc_clone.o; +text: .text%__1cRloadConP_pollNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cQinstanceRefKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cQinstanceRefKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSconvI2D_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapUreachable_basicblock6Fp0ipi_v_; +text: .text%__1cIMulINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPciInstanceKlassLfind_method6MpnIciSymbol_2_pnIciMethod__; +text: .text%__1cXvirtual_call_RelocationLunpack_data6M_v_; +text: .text%__1cFciEnvRfind_system_klass6MpnIciSymbol__pnHciKlass__; +text: .text%__1cLRegisterMapIpd_clear6M_v_; +text: .text%__1cSstkL_to_regD_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHUNICODEHas_utf86FpHi_pc_; +text: .text%__1cLstoreP0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_2NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cParrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cParrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cMnegF_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKo0RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cSaddL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCodeBuffer2t6MiiiiiipnKBufferBlob_pnJrelocInfo_pnORelocateBuffer_ipnLOopRecorder_pkcii_v_; +text: .text%__1cIGraphKitYcombine_exception_states6MpnNSafePointNode_2_v_; +text: .text%__1cSstkL_to_regD_2NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_ltNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassWfind_field_from_offset6kMiipnPfieldDescriptor__i_; +text: .text%__1cPciInstanceKlassTget_field_by_offset6Mii_pnHciField__; +text: .text%__1cRshrP_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconstMethodOopDescYchecked_exceptions_start6kM_pnXCheckedExceptionElement__; +text: .text%__1cSstkL_to_regD_2NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cWstatic_stub_RelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cSstkL_to_regD_2NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRelocationWfix_relocation_at_move6Mi_v_: codeBlob.o; +text: .text%__1cPClassFileParserYparse_checked_exceptions6MpHInSconstantPoolHandle_pnGThread__1_; +text: .text%__1cKstoreLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSvframeStreamCommonbHskip_method_invoke_and_aux_frames6M_v_; +text: .text%__1cRbranchLoopEndNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSconvI2D_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassXmark_dependent_nmethods6MpnMklassOopDesc__i_; +text: .text%__1cDCHANprocess_class6FnLKlassHandle_pnNGrowableArray4n0B___pnNGrowableArray4nMmethodHandle___nMsymbolHandle_6_v_; +text: .text%__1cQConstantIntValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cSconvI2D_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileStreamHskip_u26MipnGThread__v_; +text: .text%__1cHnmethodPis_locked_by_vm6kM_i_: nmethod.o; +text: .text%__1cUcompU_iReg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitXset_edges_for_java_call6MpnMCallJavaNode_i_v_; +text: .text%__1cOMacroAssemblerNverify_thread6M_v_; +text: .text%__1cJloadLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitZset_results_for_java_call6MpnMCallJavaNode__pnENode__; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_: methodDataOop.o; +text: .text%__1cSbranchCon_longNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHnmethodVcleanup_inline_caches6M_v_; +text: .text%__1cTciConstantPoolCacheGinsert6Mipv_v_; +text: .text%__1cIAddLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNloadKlassNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFStateO_sub_Op_StoreI6MpknENode__v_; +text: .text%__1cKHandleAreaHoops_do6MpnKOopClosure__v_; +text: .text%__1cRbranchLoopEndNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHciField2t6MpnPfieldDescriptor__v_; +text: .text%__1cMjniIdSupportNto_method_oop6FpnK_jmethodID__pnNmethodOopDesc__; +text: .text%__1cRSignatureIterator2t6MpnGThread_pnNsymbolOopDesc__v_; +text: .text%__1cMloadConLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZCallInterpreterDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadSNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodLis_unloaded6kM_i_: nmethod.o; +text: .text%__1cYcompareAndSwapL_boolNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFKlassMnext_sibling6kM_p0_; +text: .text%__1cSbranchCon_longNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKDictionaryStry_get_next_class6M_pnMklassOopDesc__; +text: .text%__1cNinstanceKlassKmethods_do6MpFpnNmethodOopDesc__v_v_; +text: .text%__1cQSystemDictionaryStry_get_next_class6F_pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cSobjArrayKlassKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%__1cJimmU5OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cRbranchLoopEndNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: ad_sparc.o; +text: .text%__1cLBlock_ArrayEgrow6MI_v_; +text: .text%__1cKRegionNodeLbottom_type6kM_pknEType__: loopnode.o; +text: .text%__1cbCcatch_cleanup_fix_all_inputs6FpnENode_11_v_: lcm.o; +text: .text%__1cYinternal_word_RelocationLunpack_data6M_v_; +text: .text%__1cOMacroAssemblerUallocate_oop_address6MpnI_jobject_pnMRegisterImpl__nHAddress__; +text: .text%__1cUThreadSafepointStateXexamine_state_of_thread6Mi_v_; +text: .text%__1cUSafepointSynchronizeOsafepoint_safe6FpnKJavaThread_nPJavaThreadState__i_; +text: .text%__1cUParallelScavengeHeapNtlab_capacity6kM_I_; +text: .text%__1cKcmpOpPOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cObranchConPNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cKstoreBNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cUvisit_all_interfaces6FpnPobjArrayOopDesc_pnXInterfaceVisiterClosure__v_; +text: .text%__1cLBoxLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPcmpFastLockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHRetNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRegionNodeEhash6kM_I_: loopnode.o; +text: .text%__1cPconvL2I_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNnmethodLocker2T6M_v_; +text: .text%__1cICallNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cKoopFactoryTnew_system_objArray6FipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNnmethodLocker2t6MpnHnmethod__v_; +text: .text%__1cLstoreB0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescOis_initializer6kM_i_; +text: .text%__1cbDcatch_cleanup_find_cloned_def6FpnFBlock_pnENode_1rnLBlock_Array_i_3_: lcm.o; +text: .text%__1cQxorI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCodeBufferMstart_a_stub6M_v_; +text: .text%__1cKstoreLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCodeBufferKend_a_stub6M_v_; +text: .text%__1cFTypeFCeq6kMpknEType__i_; +text: .text%__1cFciEnvVget_constant_by_index6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cQciByteCodeStreamMget_constant6M_nKciConstant__; +text: .text%__1cFciEnvbAget_constant_by_index_impl6MpnPciInstanceKlass_i_nKciConstant__; +text: .text%__1cOClearArrayNodeKmatch_edge6kMI_I_; +text: .text%__1cPconvL2I_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadSNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSbranchCon_longNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cOFastUnlockNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cKJavaThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cSFixupMirrorClosureJdo_object6MpnHoopDesc__v_: universe.o; +text: .text%__1cFStateP_sub_Op_LShiftI6MpknENode__v_; +text: .text%__1cQandL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJiRegPOperFclone6kM_pnIMachOper__; +text: .text%__1cPSignatureStreamRas_symbol_or_null6M_pnNsymbolOopDesc__; +text: .text%__1cIregDOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cKMemBarNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKoopFactoryYnew_permanent_shortArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cKMemBarNode2t6M_v_; +text: .text%__1cNIdealLoopTreeObeautify_loops6MpnOPhaseIdealLoop__i_; +text: .text%__1cRsarI_reg_imm5NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: frame.o; +text: .text%__1cKimmI13OperFclone6kM_pnIMachOper__; +text: .text%__1cIGraphKitbBset_arguments_for_java_call6MpnMCallJavaNode__v_; +text: .text%__1cIGraphKitJpush_node6MnJBasicType_pnENode__v_: callGenerator.o; +text: .text%__1cNSignatureInfoIdo_array6Mii_v_: bytecode.o; +text: .text%__1cNCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cJcmpOpOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cNprefetch2NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRcompL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_SetObjectArrayElement: jni.o; +text: .text%__1cSandI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPThreadLocalNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNSafePointNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cKMemBarNodeJis_MemBar6kM_pk0_: classes.o; +text: .text%__1cIciMethodXfind_monomorphic_target6MpnHciKlass_22_p0_; +text: .text%__1cJCHAResultOis_monomorphic6kM_i_; +text: .text%__1cDCHAManalyze_call6FnLKlassHandle_11nMsymbolHandle_2_pnJCHAResult__; +text: .text%__1cOloadConI13NodeFclone6kM_pnENode__; +text: .text%__1cObranchConUNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitOinsert_mem_bar6MpnKMemBarNode__v_; +text: .text%__1cLOptoRuntimeOnew_objArray_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cRshlL_reg_imm6NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cbACallCompiledJavaDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQandI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRCardTableModRefBSPdirty_MemRegion6MnJMemRegion__v_; +text: .text%__1cZresource_reallocate_bytes6FpcII_0_; +text: .text%__1cLConvL2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOAbstractICacheQinvalidate_range6FpCi_v_; +text: .text%__1cKstorePNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMaxINodeGOpcode6kM_i_; +text: .text%__1cIGraphKitMarray_length6MpnENode__2_; +text: .text%__1cIGraphKitbMset_predefined_output_for_runtime_call6MpnENode_pnMMergeMemNode__v_; +text: .text%__1cWThreadLocalAllocBufferFclear6M_v_; +text: .text%__1cTDirectCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cNCallGeneratorPfor_direct_call6FpnIciMethod__p0_; +text: .text%__1cMWarmCallInfoLalways_cold6F_p0_; +text: .text%__1cMPhaseChaitinQgather_lrg_masks6Mi_v_; +text: .text%__1cIimmDOperJconstantD6kM_d_: ad_sparc_clone.o; +text: .text%__1cIPhaseIFGEinit6MI_v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: live.o; +text: .text%__1cJPhaseLiveHcompute6MI_v_; +text: .text%__1cMLinkResolverbCresolve_virtual_call_or_null6FnLKlassHandle_1nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cSaddI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRcompL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cZnoG3_iRegI_64bit_safeOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cFTypeDEmake6Fd_pk0_; +text: .text%__1cFKlassMoop_is_array6kM_i_: symbolKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: symbolKlass.o; +text: .text%__1cPThreadRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cPThreadRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cRshlI_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvL2I_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQciTypeArrayKlassTis_type_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cQaddL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHMatcherMreturn_value6Fii_nLRegPair__; +text: .text%__1cGThreadQunboost_priority6Fp0_v_; +text: .text%__1cMloadConDNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cFStateN_sub_Op_LoadI6MpknENode__v_; +text: .text%__1cIMachOperEtype6kM_pknEType__; +text: .text%JVM_GetCPClassNameUTF; +text: .text%__1cUcompU_iReg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cITypeNodeHis_Type6M_p0_: classes.o; +text: .text%__1cICodeBlob2t6Mpkcii_v_; +text: .text%__1cKBufferBlobGcreate6Fpkci_p0_; +text: .text%__1cTmembar_CPUOrderNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadBNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKcmpOpUOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cObranchConUNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cObranchConUNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmembar_acquireNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJNIHandlesLmake_global6FnGHandle_i_pnI_jobject__; +text: .text%__1cTCallInterpreterNodeGOpcode6kM_i_; +text: .text%jni_GetStringLength: jni.o; +text: .text%__1cMLinkResolverbBresolve_static_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cRshrP_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCHAResultSmonomorphic_target6kM_nMmethodHandle__; +text: .text%__1cLConvI2LNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cHMonitorEwait6Mil_i_; +text: .text%__1cJloadPNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cMoutputStream2t6Mi_v_; +text: .text%__1cMstringStreamJas_string6M_pc_; +text: .text%__1cMstringStream2T6M_v_; +text: .text%__1cMstringStream2t6MI_v_; +text: .text%__1cIGraphKitMreset_memory6M_pnENode__; +text: .text%__1cZCallDynamicJavaDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKstorePNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstoreLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNprefetch2NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeMsetup_is_top6M_v_; +text: .text%__1cIGotoNodeGOpcode6kM_i_; +text: .text%__1cPorI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPfieldDescriptorRint_initial_value6kM_i_; +text: .text%__1cSReferenceProcessorSdiscover_reference6MpnHoopDesc_nNReferenceType__i_; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: callnode.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: callnode.o; +text: .text%__1cHRetNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cNbranchConNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cHRetNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapLbb_mark_fct6Fp0ipi_v_; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjectFactory.o; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciObjectFactory.o; +text: .text%__1cKcmpOpPOperFequal6kM_i_: ad_sparc_clone.o; +text: .text%__1cSInterpreterRuntimeE_new6FpnKJavaThread_pnTconstantPoolOopDesc_i_v_; +text: .text%__1cKReturnNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSandI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_ref_ReferenceIset_next6FpnHoopDesc_2_v_; +text: .text%__1cSReferenceProcessorTget_discovered_list6MnNReferenceType__ppnHoopDesc__; +text: .text%__1cOGenerateOopMapRsigchar_to_effect6McipnNCellTypeState__2_; +text: .text%__1cXmembar_acquire_lockNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapIdo_field6Miiii_v_; +text: .text%__1cKStoreBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJloadINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmembar_releaseNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cSaddL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLRShiftINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cEDict2t6MpFpkv2_ipF2_i_v_; +text: .text%__1cEDict2T6M_v_; +text: .text%__1cKBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cLOopRecorder2t6MpnFArena__v_; +text: .text%__1cRClassPathZipEntryLopen_stream6Mpkc_pnPClassFileStream__; +text: .text%__1cMLinkResolverbCresolve_special_call_or_null6FnLKlassHandle_nMsymbolHandle_21_nMmethodHandle__; +text: .text%__1cIModINodeGOpcode6kM_i_; +text: .text%__1cUGenericGrowableArray2t6MiipnEGrET_i_v_; +text: .text%__1cJloadCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeHget_int6kMpi_i_; +text: .text%__1cRInterpretedRFrame2t6MnFframe_pnKJavaThread_nMmethodHandle__v_; +text: .text%__1cKJavaThreadQlast_java_vframe6MpnLRegisterMap__pnKjavaVFrame__; +text: .text%__1cTStackWalkCompPolicyVfindTopInlinableFrame6MpnNGrowableArray4CpnGRFrame____2_; +text: .text%__1cTStackWalkCompPolicyXmethod_invocation_event6MnMmethodHandle_pnGThread__v_; +text: .text%__1cKjavaVFrameNis_java_frame6kM_i_: vframe.o; +text: .text%__1cISubLNodeGOpcode6kM_i_; +text: .text%__1cKciTypeFlowXmark_known_range_starts6M_v_; +text: .text%__1cKciTypeFlow2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cKciTypeFlowLfind_ranges6M_v_; +text: .text%__1cKciTypeFlowPget_start_state6M_pkn0ALStateVector__; +text: .text%__1cKciTypeFlowHdo_flow6M_v_; +text: .text%__1cKciTypeFlowKflow_types6M_v_; +text: .text%__1cKciTypeFlowKmap_blocks6M_v_; +text: .text%__1cMloadConPNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_ltNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTconstantPoolOopDescbCverify_constant_pool_resolve6FnSconstantPoolHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cKStoreCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciMethodJload_code6M_v_; +text: .text%__1cMciMethodDataJload_data6M_v_; +text: .text%__1cIGraphKitTuse_exception_state6MpnNSafePointNode__pnENode__; +text: .text%__1cOcompU_iRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitGmemory6MI_pnENode__; +text: .text%__1cIHaltNodeEhash6kM_I_: classes.o; +text: .text%__1cFKlassQup_cast_abstract6M_p0_; +text: .text%__1cKReturnNodeEhash6kM_I_: classes.o; +text: .text%__1cLklassVtableRinitialize_vtable6MpnGThread__v_; +text: .text%__1cPClassFileParserXverify_legal_class_name6MnMsymbolHandle_pnGThread__v_; +text: .text%__1cNobjArrayKlassPoop_is_objArray6kM_i_: objArrayKlass.o; +text: .text%__1cPjava_lang_ClassNcreate_mirror6FnLKlassHandle_pnGThread__pnHoopDesc__; +text: .text%__1cIAndINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: methodDataKlass.o; +text: .text%__1cPmethodDataKlassRoop_is_methodData6kM_i_: methodDataKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: methodDataKlass.o; +text: .text%__1cMciMethodData2t6MnQmethodDataHandle__v_; +text: .text%__1cIGraphKitOmake_slow_call6MpknITypeFunc_pCpkcpnENode_88_8_; +text: .text%__1cUGenericGrowableArrayEgrow6Mi_v_; +text: .text%__1cIAndINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cLOpaque2NodeGOpcode6kM_i_; +text: .text%__1cOClearArrayNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNmethodOopDescbEfast_exception_handler_bci_for6MnLKlassHandle_ipnGThread__i_; +text: .text%__1cSInterpreterRuntimebFexception_handler_for_exception6FpnKJavaThread_pnHoopDesc__pC_; +text: .text%__1cOPhaseIdealLoopPis_counted_loop6MpnENode_pnNIdealLoopTree__2_; +text: .text%__1cKstoreCNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackHdo_void6M_v_: generateOopMap.o; +text: .text%__1cIMachNodeQis_MachSafePoint6M_pnRMachSafePointNode__: machnode.o; +text: .text%__1cNCollectedHeapWpermanent_obj_allocate6FnLKlassHandle_ipnGThread__pnHoopDesc__: klass.o; +text: .text%__1cFKlassRinitialize_supers6MpnMklassOopDesc_pnGThread__v_; +text: .text%__1cKKlass_vtbl2n6FIrnLKlassHandle_ipnGThread__pv_; +text: .text%__1cRLowMemoryDetectorbLdetect_low_memory_for_collected_pools6F_v_: klass.o; +text: .text%__1cFKlassVbase_create_klass_oop6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__pnMklassOopDesc__; +text: .text%__1cQjava_lang_StringLutf8_length6FpnHoopDesc__i_; +text: .text%jni_GetStringUTFLength: jni.o; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc_ii_pc_; +text: .text%jni_GetStringUTFRegion: jni.o; +text: .text%__1cFKlassRbase_create_klass6FrnLKlassHandle_irknKKlass_vtbl_pnGThread__1_; +text: .text%__1cHUNICODELutf8_length6FpHi_i_; +text: .text%__1cENodeHis_Mach6M_pnIMachNode__: loopnode.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: loopnode.o; +text: .text%__1cQPlaceholderTableJadd_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cQPlaceholderTableMremove_entry6MiInMsymbolHandle_nGHandle__v_; +text: .text%__1cENodeHis_Copy6kM_I_: ad_sparc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: memnode.o; +text: .text%__1cKstoreBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreINodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQSystemDictionaryTload_instance_class6FnMsymbolHandle_nGHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cRsarI_reg_imm5NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUcompU_iReg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHAddNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cUcompU_iReg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKPerfStringKset_string6Mpkc_v_; +text: .text%__1cQjava_lang_StringRas_unicode_string6FpnHoopDesc_ri_pH_; +text: .text%JVM_InternString; +text: .text%__1cLStringTableGintern6FpnHoopDesc_pnGThread__2_; +text: .text%__1cOGenerateOopMapTmerge_state_vectors6MpnNCellTypeState_2_i_; +text: .text%__1cRcompL_reg_conNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshrL_reg_imm6NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosGrandom6F_l_; +text: .text%__1cXmembar_acquire_lockNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKimmP13OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cVcompP_iRegP_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKoopFactoryXnew_permanent_byteArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cENodeGis_Con6kM_I_: ad_sparc.o; +text: .text%__1cSaddL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRcompL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: machnode.o; +text: .text%__1cNMachIdealNodeJnum_opnds6kM_I_: machnode.o; +text: .text%__1cRMachNullCheckNodeLout_RegMask6kM_rknHRegMask__: machnode.o; +text: .text%__1cRMachNullCheckNode2t6MpnENode_2I_v_; +text: .text%__1cSTailCalljmpIndNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cIGraphKitPpush_pair_local6Mi_v_: parse2.o; +text: .text%__1cICodeHeapKdeallocate6Mpv_v_; +text: .text%__1cJCodeCacheEfree6FpnICodeBlob__v_; +text: .text%__1cNSafePointNodeMis_SafePoint6M_p0_: classes.o; +text: .text%__1cSCompareAndSwapNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFParseNpush_constant6MnKciConstant__i_; +text: .text%__1cKTypeRawPtrEmake6FpC_pk0_; +text: .text%jni_SetIntField: jni.o; +text: .text%__1cENodeGis_Sub6M_pnHSubNode__: classes.o; +text: .text%__1cNIdealLoopTreeMcounted_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cNIdealLoopTreeTcheck_inner_safepts6MpnOPhaseIdealLoop__v_; +text: .text%__1cIciObjectMis_obj_array6M_i_: ciInstanceKlass.o; +text: .text%__1cKBufferBlobEfree6Fp0_v_; +text: .text%__1cIRootNodeHis_Root6M_p0_: classes.o; +text: .text%__1cPconvL2I_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLRuntimeStubIis_alive6kM_i_: codeBlob.o; +text: .text%__1cLCastP2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPciObjectFactoryMvm_symbol_at6Fi_pnIciSymbol__; +text: .text%__1cHnmethodKpc_desc_at6MpCi_pnGPcDesc__; +text: .text%__1cKDictionaryJadd_klass6MnMsymbolHandle_nGHandle_nLKlassHandle__v_; +text: .text%__1cQSystemDictionaryRcheck_constraints6FiInTinstanceKlassHandle_nGHandle_pnGThread__v_; +text: .text%__1cVLoaderConstraintTablePcheck_or_update6MnTinstanceKlassHandle_nGHandle_nMsymbolHandle__pkc_; +text: .text%__1cVshrL_reg_imm6_L2INodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQmodI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassXsearch_secondary_supers6kMpnMklassOopDesc__i_; +text: .text%__1cZCallDynamicJavaDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: graphKit.o; +text: .text%__1cKTypeRawPtrFempty6kM_i_; +text: .text%__1cLConvI2LNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIMachNodeSalignment_required6kM_i_: machnode.o; +text: .text%__1cIMachNodePcompute_padding6kMi_i_: machnode.o; +text: .text%__1cWImplicitExceptionTableGappend6MII_v_; +text: .text%__1cIMachNodeLis_MachCall6M_pnMMachCallNode__: machnode.o; +text: .text%__1cRMachNullCheckNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLProfileDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_: ciMethodData.o; +text: .text%__1cQxorI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNIdealLoopTreeVadjust_loop_exit_prob6MpnOPhaseIdealLoop__v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: assembler_sparc.o; +text: .text%__1cNinstanceKlassVadd_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cOLibraryCallKitOgenerate_guard6MpnENode_pnKRegionNode_f_v_; +text: .text%__1cSandI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLstoreP0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIPhaseIFGISquareUp6M_v_; +text: .text%__1cLklassVtableMget_mirandas6FpnNGrowableArray4CpnNmethodOopDesc___pnMklassOopDesc_pnPobjArrayOopDesc_8_v_; +text: .text%__1cKCodeBuffer2T6M_v_; +text: .text%__1cLklassItableRinitialize_itable6M_v_; +text: .text%__1cQPSGenerationPoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cQPSGenerationPoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cQPSGenerationPoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cKNode_Array2t6MpnFArena__v_: reg_split.o; +text: .text%__1cENodeHis_Load6M_pnILoadNode__: multnode.o; +text: .text%__1cLOpaque1NodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cPconvI2L_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMURShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJLoadBNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRcompL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cXAdaptiveWeightedAverageGsample6Mf_v_; +text: .text%__1cFKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cQciByteCodeStreamJget_klass6Mri_pnHciKlass__; +text: .text%__1cFKlassMoop_is_klass6kM_i_: instanceKlass.o; +text: .text%__1cFKlassWappend_to_sibling_list6M_v_; +text: .text%__1cNSafePointNodeEjvms6kM_pnIJVMState__: classes.o; +text: .text%__1cQcmovI_reg_ltNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionarySjava_system_loader6F_pnHoopDesc__; +text: .text%__1cFKlassMset_subklass6MpnMklassOopDesc__v_; +text: .text%__1cOGenerateOopMapLmerge_state6Fp0ipi_v_; +text: .text%__1cUCallCompiledJavaNodeGOpcode6kM_i_; +text: .text%__1cMTypeKlassPtrFxdual6kM_pknEType__; +text: .text%__1cNprefetch2NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryVdefine_instance_class6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cSinstanceKlassKlassXallocate_instance_klass6MiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cPClassFileParserbBcheck_final_method_override6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cJCodeCachebKnumber_of_nmethods_with_dependencies6F_i_; +text: .text%__1cNinstanceKlassQinit_implementor6M_v_; +text: .text%__1cPClassFileParserNfill_oop_maps6MnTinstanceKlassHandle_ii_v_; +text: .text%__1cPClassFileStream2t6MpCipc_v_; +text: .text%__1cRconstantPoolKlassIallocate6MipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cNinstanceKlassSprocess_interfaces6MpnGThread__v_; +text: .text%__1cNmethodOopDescMsort_methods6FpnPobjArrayOopDesc_222_v_; +text: .text%__1cNinstanceKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cKoopFactoryQnew_constantPool6FipnGThread__pnTconstantPoolOopDesc__; +text: .text%__1cKoopFactoryRnew_instanceKlass6FiiiinNReferenceType_pnGThread__pnMklassOopDesc__; +text: .text%__1cVjava_lang_ClassLoaderRis_trusted_loader6FpnHoopDesc__i_; +text: .text%__1cPClassFileParserOparseClassFile6MnMsymbolHandle_nGHandle_2r1pnGThread__nTinstanceKlassHandle__; +text: .text%__1cNinstanceKlassWdo_local_static_fields6MpFpnPfieldDescriptor_pnGThread__v4_v_; +text: .text%__1cPClassFileParserMsort_methods6MnOobjArrayHandle_111pnGThread__nPtypeArrayHandle__; +text: .text%__1cFKlassKsuperklass6kM_pnNinstanceKlass__; +text: .text%__1cPClassFileParserbBparse_constant_pool_entries6MnSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cPClassFileParserTparse_constant_pool6MpnGThread__nSconstantPoolHandle__; +text: .text%__1cTClassLoadingServiceTnotify_class_loaded6FpnNinstanceKlass_i_v_; +text: .text%__1cPClassFileParserQparse_interfaces6MnSconstantPoolHandle_nGHandle_2pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserbDcompute_transitive_interfaces6MnTinstanceKlassHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cIVerifierRshould_verify_for6FpnHoopDesc__i_; +text: .text%__1cIUniverseTflush_dependents_on6FnTinstanceKlassHandle__v_; +text: .text%__1cIVerifierQrelax_verify_for6FpnHoopDesc__i_; +text: .text%__1cPClassFileParserMparse_fields6MnSconstantPoolHandle_ipnUFieldAllocationCount_pnOobjArrayHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cLklassItableZsetup_itable_offset_table6FnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserbCcheck_super_interface_access6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassQeager_initialize6MpnGThread__v_; +text: .text%__1cLklassVtablebKcompute_vtable_size_and_num_mirandas6Fri1pnMklassOopDesc_pnPobjArrayOopDesc_nLAccessFlags_pnHoopDesc_pnNsymbolOopDesc_5_v_; +text: .text%__1cPClassFileParserVset_precomputed_flags6MnTinstanceKlassHandle__v_; +text: .text%__1cPClassFileParserNparse_methods6MnSconstantPoolHandle_ipnLAccessFlags_ppnPobjArrayOopDesc_66pnGThread__nOobjArrayHandle__; +text: .text%__1cPClassFileParserbAparse_classfile_attributes6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cQSystemDictionaryVresolve_super_or_fail6FnMsymbolHandle_1nGHandle_2pnGThread__pnMklassOopDesc__; +text: .text%__1cKcmpOpPOperJnot_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cRMachSafePointNodePis_MachCallJava6M_pnQMachCallJavaNode__: ad_sparc_misc.o; +text: .text%__1cMPhaseIterGVNIoptimize6M_v_; +text: .text%__1cOPhaseTransform2t6MnFPhaseLPhaseNumber__v_; +text: .text%__1cISubINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceKlass.o; +text: .text%__1cNinstanceKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlass.o; +text: .text%__1cHMemNodeHsize_of6kM_I_; +text: .text%__1cFVTuneQstart_class_load6F_v_; +text: .text%__1cSThreadProfilerMark2T6M_v_; +text: .text%__1cFVTuneOend_class_load6F_v_; +text: .text%__1cLClassLoaderOload_classfile6FnMsymbolHandle_pnGThread__nTinstanceKlassHandle__; +text: .text%__1cJEventMark2t6MpkcE_v_: classLoader.o; +text: .text%__1cSThreadProfilerMark2t6Mn0AGRegion__v_; +text: .text%__1cQSystemDictionaryRload_shared_class6FnTinstanceKlassHandle_nGHandle_pnGThread__1_; +text: .text%__1cKklassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cPClassFileParserbKparse_classfile_sourcefile_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cQmodI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLRShiftINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKCMoveINodeGOpcode6kM_i_; +text: .text%__1cLLShiftLNodeGOpcode6kM_i_; +text: .text%__1cYcompareAndSwapL_boolNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSinstanceKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNinstanceKlassScopy_static_fields6MpnSPSPromotionManager__v_; +text: .text%__1cMtlsLoadPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFStateQ_sub_Op_URShiftI6MpknENode__v_; +text: .text%__1cKcmpOpUOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cObranchConUNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cQaddP_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOGenerateOopMapJinterp_bb6MpnKBasicBlock__v_; +text: .text%__1cOGenerateOopMapQnext_bb_start_pc6MpnKBasicBlock__i_; +text: .text%__1cLklassVtableYadd_new_mirandas_to_list6FpnNGrowableArray4CpnNmethodOopDesc___pnPobjArrayOopDesc_6pnMklassOopDesc__v_; +text: .text%__1cIRewriterHrewrite6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassNrewrite_class6MpnGThread__v_; +text: .text%__1cKoopFactoryVnew_constantPoolCache6FipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cYconstantPoolCacheOopDescKinitialize6MrnIintArray__v_; +text: .text%__1cWconstantPoolCacheKlassIallocate6MipnGThread__pnYconstantPoolCacheOopDesc__; +text: .text%__1cIVerifierRverify_byte_codes6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNinstanceKlassWadd_loader_constraints6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%JVM_GetMethodIxSignatureUTF; +text: .text%JVM_GetMethodIxMaxStack; +text: .text%JVM_GetMethodIxArgsSize; +text: .text%JVM_GetMethodIxByteCodeLength; +text: .text%JVM_GetMethodIxExceptionIndexes; +text: .text%JVM_GetMethodIxByteCode; +text: .text%JVM_GetMethodIxExceptionsCount; +text: .text%__1cPCheckCastPPNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLstoreP0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHCmpNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cMPhaseChaitinSbuild_ifg_physical6MpnMResourceArea__I_; +text: .text%__1cWCountInterfacesClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cQmulD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKoopFactoryWnew_permanent_intArray6FipnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cPClassFileParserVparse_exception_table6MIInSconstantPoolHandle_pnGThread__nPtypeArrayHandle__; +text: .text%__1cNPhaseCoalescePcoalesce_driver6M_v_; +text: .text%__1cLProfileDataSis_VirtualCallData6M_i_: ciMethodData.o; +text: .text%__1cLBuildCutout2T6M_v_; +text: .text%__1cNloadConL0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cITypeFuncMreturns_long6kM_i_; +text: .text%__1cJloadSNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse2.o; +text: .text%__1cENodeGis_Con6kM_I_: connode.o; +text: .text%__1cNloadConP0NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cJimmP0OperEtype6kM_pknEType__: ad_sparc_clone.o; +text: .text%__1cLstoreI0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPCheckCastPPNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSObjectSynchronizerJnotifyall6FnGHandle_pnGThread__v_; +text: .text%__1cIAndINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIciObjectSis_obj_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cTGeneratePairingInfoRpossible_gc_point6MpnOBytecodeStream__i_: ciMethod.o; +text: .text%__1cQandL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKRegionNodeOis_block_start6kM_i_: loopnode.o; +text: .text%__1cHNTarjanICOMPRESS6M_v_; +text: .text%__1cNRelocIteratorTlocs_and_index_size6Fii_i_; +text: .text%__1cQsubL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQStackFrameStream2t6MpnKJavaThread_i_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: compile.o; +text: .text%__1cLClassLoaderLadd_package6Fpkci_i_; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: loopnode.o; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopnode.o; +text: .text%__1cOcompI_iRegNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: loopnode.o; +text: .text%__1cLklassItableTcompute_itable_size6FnOobjArrayHandle__i_; +text: .text%__1cHTypePtrCeq6kMpknEType__i_; +text: .text%__1cQandI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIXorINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cRmethodDataOopDescLbci_to_data6Mi_pnLProfileData__; +text: .text%__1cSCompareAndSwapNodeKmatch_edge6kMI_I_: classes.o; +text: .text%__1cRcompL_reg_conNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmembar_acquireNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPindOffset13OperFclone6kM_pnIMachOper__; +text: .text%__1cICallNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFframeZinterpreter_frame_set_bcx6Mi_v_; +text: .text%__1cJloadCNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodJcode_size6kM_i_: nmethod.o; +text: .text%__1cMnegF_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: loopnode.o; +text: .text%__1cLstoreI0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTOopMapForCacheEntryZfill_stackmap_for_opcodes6MpnOBytecodeStream_pnNCellTypeState_4i_v_; +text: .text%__1cPciObjArrayKlassSis_obj_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cSaddL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQshrL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNSharedRuntimebKexception_handler_for_return_address6FpC_1_; +text: .text%__1cILoopNodeHsize_of6kM_I_: classes.o; +text: .text%__1cHMatcherLfind_shared6MpnENode__v_; +text: .text%__1cJStartNodeHsize_of6kM_I_; +text: .text%__1cHMatcherFxform6MpnENode_i_2_; +text: .text%__1cEDict2t6MpFpkv2_ipF2_ipnFArena_i_v_; +text: .text%__1cRInterpretedRFrameKtop_vframe6kM_pnKjavaVFrame__: rframe.o; +text: .text%__1cQmodI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRinterpretedVFrameDbci6kM_i_; +text: .text%__1cRreturn_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cQmodI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarI_reg_imm5NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseKdo_put_xxx6MpknHTypePtr_pnENode_pnHciField_i_v_; +text: .text%__1cIAndINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cMnegF_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNIdealLoopTreeOpolicy_peeling6kMpnOPhaseIdealLoop__i_; +text: .text%__1cNIdealLoopTreeUiteration_split_impl6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cNIdealLoopTreebBpolicy_do_remove_empty_loop6MpnOPhaseIdealLoop__i_; +text: .text%__1cQsubL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAndINodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cNinstanceKlassbBcall_class_initializer_impl6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cNloadRangeNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitRmerge_fast_memory6MpnENode_2i_v_; +text: .text%__1cRcompL_reg_conNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverbHlookup_instance_method_in_klasses6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cMnegF_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: instanceKlass.o; +text: .text%__1cNSharedRuntimebWnative_method_throw_unsatisfied_link_error_entry6F_pC_; +text: .text%__1cTStackWalkCompPolicyYmethod_back_branch_event6MnMmethodHandle_iipnGThread__v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlass.o; +text: .text%__1cbCCompiledCodeSafepointHandlerYcaller_must_gc_arguments6kM_i_: safepoint.o; +text: .text%__1cUThreadSafepointStateYcaller_must_gc_arguments6kM_i_; +text: .text%__1cRCompilationPolicybJreset_counter_for_back_branch_event6MnMmethodHandle__v_; +text: .text%__1cNSafepointBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_; +text: .text%__1cOMethodLivenessQcompute_liveness6M_v_; +text: .text%__1cOMethodLiveness2t6MpnFArena_pnIciMethod__v_; +text: .text%__1cOMethodLivenessNinit_gen_kill6M_v_; +text: .text%__1cOMethodLivenessSpropagate_liveness6M_v_; +text: .text%__1cOMethodLivenessRinit_basic_blocks6M_v_; +text: .text%__1cIGraphKitHopt_iff6MpnENode_2_2_; +text: .text%__1cLCastP2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLRShiftINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cUcompU_iReg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJTimeStampGupdate6M_v_; +text: .text%__1cRmethodDataOopDescJis_mature6kM_i_; +text: .text%__1cRmethodDataOopDescKmileage_of6FpnNmethodOopDesc__i_; +text: .text%__1cWconstantPoolCacheKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cMMachCallNodeMreturns_long6kM_i_; +text: .text%__1cIGraphKitOhas_ex_handler6M_i_; +text: .text%__1cMloadConDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJStartNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cFParseQarray_addressing6MnJBasicType_ippknEType__pnENode__; +text: .text%__1cNloadConP0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKReturnNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPCountedLoopNodeHsize_of6kM_I_: classes.o; +text: .text%__1cIProjNodeJideal_reg6kM_I_; +text: .text%__1cQaddI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQcmovI_reg_ltNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQcmovI_reg_gtNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRMachSafePointNodeLis_MachCall6M_pnMMachCallNode__: ad_sparc_misc.o; +text: .text%__1cSsafePoint_pollNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJcmpOpOperFequal6kM_i_: ad_sparc_clone.o; +text: .text%__1cHCompilebAvarargs_C_out_slots_killed6kM_I_; +text: .text%__1cXJNI_ArgumentPusherVaArgHiterate6MX_v_: jni.o; +text: .text%__1cbBjava_lang_ref_SoftReferenceFclock6F_x_; +text: .text%__1cTMachCallRuntimeNodeSis_MachCallRuntime6M_p0_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopQset_subtree_ctrl6MpnENode__v_; +text: .text%__1cFciEnvUis_unresolved_string6kMpnPciInstanceKlass_i_i_; +text: .text%__1cQciByteCodeStreamUis_unresolved_string6kM_i_; +text: .text%__1cWstatic_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cNflagsRegLOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cZCallDynamicJavaDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerOcas_under_lock6MpnMRegisterImpl_22pCi_v_; +text: .text%__1cYciExceptionHandlerStreamPcount_remaining6M_i_; +text: .text%__1cFParseXcatch_inline_exceptions6MpnNSafePointNode__v_; +text: .text%__1cRconstantPoolKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cNobjArrayKlassKcopy_array6MpnMarrayOopDesc_i2iipnGThread__v_; +text: .text%__1cTmembar_CPUOrderNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKcmpOpUOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%JVM_GetFieldIxModifiers; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cENodeFis_If6M_pnGIfNode__: connode.o; +text: .text%__1cRScavengeRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cRScavengeRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cQmulL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cILoopNode2t6MpnENode_2_v_; +text: .text%JVM_IsConstructorIx; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__: classes.o; +text: .text%__1cPJavaCallWrapperHoops_do6MpnKOopClosure__v_; +text: .text%__1cFframeNoops_entry_do6MpnKOopClosure_pknLRegisterMap__v_; +text: .text%__1cSaddP_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHMatcherUc_calling_convention6FpnLRegPair_I_v_; +text: .text%__1cPCallRuntimeNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cFKlassTarray_klass_or_null6M_pnMklassOopDesc__; +text: .text%__1cHnmethodQis_native_method6kM_i_: nmethod.o; +text: .text%__1cPCountedLoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__: classes.o; +text: .text%__1cKNativeCallXset_destination_mt_safe6MpC_v_; +text: .text%__1cUBytecode_tableswitchOdest_offset_at6kMi_i_; +text: .text%__1cPciObjArrayKlassNelement_klass6M_pnHciKlass__; +text: .text%__1cLRShiftINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKg1RegIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNSignatureInfoHdo_long6M_v_: frame.o; +text: .text%__1cSvframeStreamCommonZsecurity_get_caller_frame6Mi_v_; +text: .text%__1cUjni_invoke_nonstatic6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_: jni.o; +text: .text%__1cIciObjectMhas_encoding6M_i_; +text: .text%__1cNinstanceKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cIAndINodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cIAndINodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cTMachCallRuntimeNodePret_addr_offset6M_i_; +text: .text%__1cLConvL2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKo0RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cIregDOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cVshrL_reg_imm6_L2INodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescbGupdate_compiled_code_entry_point6Mi_v_; +text: .text%__1cNmethodOopDescTverified_code_entry6M_pC_; +text: .text%__1cNSharedRuntimeXfind_callee_info_helper6FpnKJavaThread_rnMvframeStream_rnJBytecodesECode_rnICallInfo_pnGThread__nGHandle__; +text: .text%__1cPBytecode_invokeFindex6kM_i_; +text: .text%__1cLRethrowNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKJNIHandlesOdestroy_global6FpnI_jobject_i_v_; +text: .text%__1cSPSKeepAliveClosureGdo_oop6MppnHoopDesc__v_: psScavenge.o; +text: .text%__1cFParseFBlockRsuccessor_for_bci6Mi_p1_; +text: .text%__1cVPreserveExceptionMark2T6M_v_; +text: .text%__1cVPreserveExceptionMark2t6MrpnGThread__v_; +text: .text%__1cHRetNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHMatcherQpost_fast_unlock6FpknENode__i_; +text: .text%__1cIRootNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cMoutputStreamFprint6MpkcE_v_; +text: .text%__1cOGenerateOopMapKcopy_state6MpnNCellTypeState_2_v_; +text: .text%__1cHAddNodeGis_Add6kM_pk0_: classes.o; +text: .text%__1cHCompileQsync_stack_slots6kM_i_; +text: .text%__1cJLoadCNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHMulNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJLoadFNodeGOpcode6kM_i_; +text: .text%__1cNSignatureInfoHdo_long6M_v_: bytecode.o; +text: .text%__1cHPhiNodeDcmp6kMrknENode__I_; +text: .text%__1cKciTypeFlowLStateVectorMdo_putstatic6MpnQciByteCodeStream__v_; +text: .text%__1cHOrINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: objArrayKlass.o; +text: .text%__1cKciTypeFlowLStateVectorGdo_ldc6MpnQciByteCodeStream__v_; +text: .text%__1cSTailCalljmpIndNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKMemoryPoolHoops_do6MpnKOopClosure__v_; +text: .text%__1cKstoreINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRloadConP_pollNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPClassFileStreamGget_u86MpnGThread__X_; +text: .text%__1cQxorI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLMachNopNodeMideal_Opcode6kM_i_: ad_sparc.o; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstance.o; +text: .text%__1cLMachNopNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMFastLockNodeFValue6kMpnOPhaseTransform__pknEType__: classes.o; +text: .text%__1cPCountedLoopNodeDphi6kM_pnENode__: loopopts.o; +text: .text%__1cOPhaseIdealLoopNreorg_offsets6MpnNIdealLoopTree__v_; +text: .text%__1cSmembar_releaseNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMVM_OperationPevaluation_mode6kM_n0AEMode__: vm_operations.o; +text: .text%__1cRshrL_reg_imm6NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse3.o; +text: .text%__1cNmethodOopDescVset_signature_handler6MpC_v_; +text: .text%__1cQLRUMaxHeapPolicyWshould_clear_reference6MpnHoopDesc__i_; +text: .text%__1cbBjava_lang_ref_SoftReferenceJtimestamp6FpnHoopDesc__x_; +text: .text%__1cPcompP_iRegPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSxorI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopRsplit_thru_region6MpnENode_2_2_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: output.o; +text: .text%__1cIAndLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cbAPSEvacuateFollowersClosureHdo_void6M_v_: psScavenge.o; +text: .text%jni_ExceptionCheck: jni.o; +text: .text%__1cIAndLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cJCodeCacheMfind_nmethod6Fpv_pnHnmethod__; +text: .text%__1cOPhaseIdealLoopMdominated_by6MpnENode_2_v_; +text: .text%__1cQshlI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseNthrow_to_exit6MpnNSafePointNode__v_; +text: .text%__1cWCallLeafNoFPDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvL2I_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLCounterDataOis_CounterData6M_i_: methodDataOop.o; +text: .text%__1cJloadPNodeFclone6kM_pnENode__; +text: .text%__1cQinstanceRefKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cYDebugInformationRecorderNappend_handle6MpnI_jobject__i_; +text: .text%__1cVConstantOopWriteValueIwrite_on6MpnUDebugInfoWriteStream__v_; +text: .text%__1cJVectorSetGslamin6Mrk0_v_; +text: .text%JVM_Clone; +text: .text%__1cRAbstractAssemblerFflush6M_v_; +text: .text%__1cYinlineCallClearArrayNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPciInstanceKlassTis_java_lang_Object6M_i_; +text: .text%__1cITypeLongFxdual6kM_pknEType__; +text: .text%__1cSmembar_releaseNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIJumpDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cRshrL_reg_imm6NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledIC2t6MpnKNativeCall__v_; +text: .text%__1cOstackSlotLOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cURethrowExceptionNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshrL_reg_imm6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXmembar_release_lockNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLOpaque2NodeEhash6kM_I_; +text: .text%__1cJloadFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUcompU_iReg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYSurvivorMutableSpacePoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cNSharedRuntimeOresolve_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cUEdenMutableSpacePoolImax_size6kM_I_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cUEdenMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cNSharedRuntimeSresolve_sub_helper6FpnKJavaThread_iipnGThread__nMmethodHandle__; +text: .text%__1cYSurvivorMutableSpacePoolQget_memory_usage6M_nLMemoryUsage__; +text: .text%__1cYSurvivorMutableSpacePoolNused_in_bytes6M_I_: memoryPool.o; +text: .text%__1cLOptoRuntimeJstub_name6FpC_pkc_; +text: .text%__1cLstoreP0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHOrINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cICmpLNodeDsub6kMpknEType_3_3_; +text: .text%__1cHPhiNodeKmake_blank6FpnENode_2_p0_; +text: .text%__1cXJNI_ArgumentPusherVaArgIget_long6M_v_: jni.o; +text: .text%__1cHMatcherNfind_receiver6Fi_nFVMRegEName__; +text: .text%__1cIMulINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cQandI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMachEpilogNodeNis_MachEpilog6M_p0_: ad_sparc.o; +text: .text%__1cOMachEpilogNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIimmPOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConPNodeFclone6kM_pnENode__; +text: .text%__1cNSCMemProjNodeGis_CFG6kM_i_: classes.o; +text: .text%__1cFStateM_sub_Op_SubI6MpknENode__v_; +text: .text%__1cFframeRretrieve_receiver6MpnLRegisterMap__pnHoopDesc__; +text: .text%__1cPBytecode_invokeNstatic_target6MpnGThread__nMmethodHandle__; +text: .text%__1cNloadKlassNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRMachSafePointNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cMTailCallNodeKmatch_edge6kMI_I_; +text: .text%jni_NewObject: jni.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: assembler_sparc.o; +text: .text%__1cIPhaseIFGYCompute_Effective_Degree6M_v_; +text: .text%__1cHMemNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cXmembar_release_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cNobjArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cJNode_ListEyank6MpnENode__v_; +text: .text%__1cMPhaseChaitinISimplify6M_v_; +text: .text%__1cNIdealLoopTreeIset_nest6MI_i_; +text: .text%__1cQcmovI_reg_gtNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIMulLNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cMStartOSRNodeGOpcode6kM_i_; +text: .text%__1cSCallLeafDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cLcmpD_ccNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJLoadSNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cJcmpOpOperEless6kM_i_: ad_sparc_clone.o; +text: .text%__1cKciTypeFlowPflow_exceptions6MpnNGrowableArray4Cpn0AFBlock___pnNGrowableArray4CpnPciInstanceKlass___pn0ALStateVector__v_; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__: ad_sparc_misc.o; +text: .text%__1cKType_ArrayEgrow6MI_v_; +text: .text%__1cSaddL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConP0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKCompiledICWis_in_transition_state6kM_i_; +text: .text%__1cXmembar_release_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPconvF2D_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadCNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJScopeDescGis_top6kM_i_; +text: .text%__1cRshrL_reg_imm6NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKciTypeFlowLStateVectorOmeet_exception6MpnPciInstanceKlass_pk1_i_; +text: .text%__1cMURShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cMLinkResolverOresolve_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cNloadConL0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cVshrL_reg_imm6_L2INodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSMemBarVolatileNodeGOpcode6kM_i_; +text: .text%__1cLstoreB0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cRshrI_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGThreadMis_VM_thread6kM_i_: thread.o; +text: .text%__1cICodeBlobKis_nmethod6kM_i_: onStackReplacement.o; +text: .text%__1cQjava_lang_StringOas_utf8_string6FpnHoopDesc__pc_; +text: .text%__1cRcmpFastUnlockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNSafePointNodeLpop_monitor6M_v_; +text: .text%__1cMPhaseChaitinVfind_base_for_derived6MppnENode_2rI_2_; +text: .text%__1cLOptoRuntimebAcomplete_monitor_exit_Type6F_pknITypeFunc__; +text: .text%__1cOstackSlotIOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cIGraphKitNshared_unlock6MpnENode_2_v_; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: connode.o; +text: .text%__1cFStateT_sub_Op_CheckCastPP6MpknENode__v_; +text: .text%__1cTCallDynamicJavaNodeEhash6kM_I_: callnode.o; +text: .text%__1cQsubI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassDLCA6Mp0_1_; +text: .text%__1cKTypeRawPtrEmake6FnHTypePtrDPTR__pk0_; +text: .text%__1cHciKlassVleast_common_ancestor6Mp0_1_; +text: .text%__1cNSignatureInfoHdo_bool6M_v_: frame.o; +text: .text%__1cOPhaseIdealLoopPbuild_loop_tree6M_v_; +text: .text%__1cRcompL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeGis_Con6kM_I_: memnode.o; +text: .text%__1cRshlL_reg_imm6NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoop2t6MrnMPhaseIterGVN_pk0i_v_; +text: .text%__1cIciObjectIis_klass6M_i_: ciInstance.o; +text: .text%__1cRloadConP_pollNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQshlL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMindirectOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindirectOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindirectOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cNSafePointNodeMpush_monitor6MpknMFastLockNode__v_; +text: .text%__1cSCallLeafDirectNodeRis_safepoint_node6kM_i_: ad_sparc_misc.o; +text: .text%__1cSCallLeafDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCallLeafDirectNodeKmethod_set6Mi_v_; +text: .text%__1cIDivINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJLoadBNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSstring_compareNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cFBytesNget_native_u46FpC_I_: bytecodes.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlass.o; +text: .text%__1cRcompL_reg_conNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMLinkResolverUresolve_special_call6FrnICallInfo_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cJloadBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: cfgnode.o; +text: .text%__1cRcompL_reg_conNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: cfgnode.o; +text: .text%__1cPcheckCastPPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: connode.o; +text: .text%__1cOGenerateOopMapGdo_ldc6Mii_v_; +text: .text%__1cJCMoveNodeLis_cmove_id6FpnOPhaseTransform_pnENode_44pnIBoolNode__4_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interp_masm_sparc.o; +text: .text%__1cKTypeAryPtrQcast_to_ptr_type6kMnHTypePtrDPTR__pknEType__; +text: .text%__1cGOopMapPset_derived_oop6MnHOptoRegEName_ii2_v_; +text: .text%__1cLConvL2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOPhaseIdealLoopKDominators6M_v_; +text: .text%__1cOPhaseIdealLoopPbuild_loop_late6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cOPhaseIdealLoopQbuild_loop_early6MrnJVectorSet_rnJNode_List_rnKNode_Stack_pk0_v_; +text: .text%__1cKCompiledICIis_clean6kM_i_; +text: .text%jni_NewGlobalRef: jni.o; +text: .text%__1cTciConstantPoolCache2t6MpnFArena_i_v_; +text: .text%__1cIAndINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cGOopMapQset_callee_saved6MnHOptoRegEName_ii2_v_; +text: .text%__1cYcompareAndSwapL_boolNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRbranchLoopEndNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: loopnode.o; +text: .text%__1cKCompiledICZcompute_monomorphic_entry6FnMmethodHandle_nLKlassHandle_iirnOCompiledICInfo_pnGThread__v_; +text: .text%__1cKtype2basic6FpknEType__nJBasicType__; +text: .text%__1cMPhaseChaitinFSplit6MI_I_; +text: .text%__1cMPhaseChaitinHcompact6M_v_; +text: .text%__1cZPhaseConservativeCoalesce2t6MrnMPhaseChaitin__v_; +text: .text%__1cNIdealLoopTreeMis_loop_exit6kMpnENode_pnOPhaseIdealLoop__2_; +text: .text%__1cMPhaseChaitinZcompress_uf_map_for_nodes6M_v_; +text: .text%__1cZPhaseConservativeCoalesceGverify6M_v_; +text: .text%__1cRcmpFastUnlockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQshlI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerWcompiler_unlock_object6MpnMRegisterImpl_222rnFLabel__v_; +text: .text%__1cXmembar_release_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interpreter_sparc.o; +text: .text%__1cKPSYoungGenNused_in_bytes6kM_I_; +text: .text%__1cOMachEpilogNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKCompiledICSset_to_monomorphic6MrknOCompiledICInfo__v_; +text: .text%__1cLklassItablebFinitialize_itable_for_interface6MpnMklassOopDesc_pnRitableMethodEntry__v_; +text: .text%__1cJloadFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIRootNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cIRootNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHMatcherPprior_fast_lock6FpknENode__i_; +text: .text%__1cJLoadLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSReferenceProcessorbAenqueue_discovered_reflist6MpnHoopDesc_p2_v_; +text: .text%__1cSReferenceProcessorbAprocess_discovered_reflist6MppnHoopDesc_pnPReferencePolicy_i_v_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle__v_; +text: .text%__1cKJavaThreadNreguard_stack6MpC_i_; +text: .text%__1cTjava_lang_ThrowableTfill_in_stack_trace6FnGHandle_pnGThread__v_; +text: .text%__1cFframeZinterpreter_frame_set_bcp6MpC_v_; +text: .text%__1cIUniverseWis_out_of_memory_error6FnGHandle__i_; +text: .text%JVM_FillInStackTrace; +text: .text%__1cKJavaThreadGactive6F_p0_; +text: .text%__1cKstoreFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodOis_java_method6kM_i_: nmethod.o; +text: .text%__1cMtlsLoadPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_StringOchar_converter6FnGHandle_HHpnGThread__1_; +text: .text%__1cENodeUdepends_only_on_test6kM_i_: connode.o; +text: .text%__1cMVirtualSpaceNreserved_size6kM_I_; +text: .text%__1cICodeHeapMmax_capacity6kM_I_; +text: .text%__1cSbranchCon_longNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cHTypePtrFxmeet6kMpknEType__3_; +text: .text%__1cbBInterpreterCodeletInterfaceRcode_size_to_size6kMi_i_: interpreter.o; +text: .text%__1cbBInterpreterCodeletInterfaceKinitialize6MpnEStub_i_v_: interpreter.o; +text: .text%__1cNflagsRegFOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cIMinINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cFParseWensure_phis_everywhere6M_v_; +text: .text%__1cVshrL_reg_imm6_L2INodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadFNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cTDebugInfoReadStream2t6MpknHnmethod_i_v_; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: ad_sparc.o; +text: .text%__1cLRethrowNodeEhash6kM_I_: classes.o; +text: .text%__1cIDivLNodeGOpcode6kM_i_; +text: .text%__1cPlocal_vsnprintf6FpcIpkcpv_i_; +text: .text%__1cNDispatchTableJset_entry6MirnKEntryPoint__v_; +text: .text%__1cNmethodOopDescVclear_native_function6M_v_; +text: .text%__1cOloadConL13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerKverify_oop6MpnMRegisterImpl_pkc_v_; +text: .text%__1cNloadConL0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%jio_snprintf; +text: .text%__1cENodeGis_Phi6M_pnHPhiNode__: node.o; +text: .text%__1cMloadConINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSSetupItableClosureEdoit6MpnMklassOopDesc_i_v_: klassVtable.o; +text: .text%__1cSmulI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLOptoRuntimebAresolve_opt_virtual_call_C6FpnKJavaThread__pC_; +text: .text%jni_NewLocalRef: jni.o; +text: .text%__1cRsubI_zero_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulDNodeGOpcode6kM_i_; +text: .text%__1cLStrCompNodeGOpcode6kM_i_; +text: .text%__1cJCodeCacheXmark_for_deoptimization6FpnMklassOopDesc__i_; +text: .text%__1cQcmovI_reg_gtNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cURethrowExceptionNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: callnode.o; +text: .text%__1cKstoreBNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserbNparse_classfile_inner_classes_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__H_; +text: .text%__1cKStoreFNodeGOpcode6kM_i_; +text: .text%__1cLConvD2INodeGOpcode6kM_i_; +text: .text%__1cURethrowExceptionNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLPhaseValues2T5B6M_v_; +text: .text%__1cIAddLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKReturnNode2t6MpnENode_2222_v_; +text: .text%__1cMURShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKReturnNodeJideal_reg6kM_I_: classes.o; +text: .text%jni_DeleteGlobalRef: jni.o; +text: .text%__1cVPatchingRelocIteratorIpostpass6M_v_; +text: .text%__1cVPatchingRelocIteratorHprepass6M_v_; +text: .text%__1cKCodeBufferPcopy_relocation6MpnICodeBlob__v_; +text: .text%__1cKCodeBufferJcopy_code6MpnICodeBlob__v_; +text: .text%__1cZnoG3_iRegI_64bit_safeOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNRelocIteratorMcreate_index6FpnKCodeBuffer_pnJrelocInfo_4_4_; +text: .text%__1cRAbstractAssemblerOcode_fill_byte6F_i_; +text: .text%__1cQcmovI_reg_gtNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cICodeBlobWfix_relocation_at_move6Mi_v_; +text: .text%__1cICodeBlob2t6MpkcpnKCodeBuffer_iiipnJOopMapSet_i_v_; +text: .text%__1cIAndLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cIAndLNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cJOopMapSet2t6M_v_; +text: .text%__1cNSCMemProjNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%JVM_GetCPMethodModifiers; +text: .text%jni_GetObjectArrayElement: jni.o; +text: .text%__1cOCompilerOraclePshould_break_at6FnMmethodHandle__i_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: memnode.o; +text: .text%__1cFParseKarray_load6MnJBasicType__v_; +text: .text%jni_SetLongField: jni.o; +text: .text%__1cHGCCauseJto_string6Fn0AFCause__pkc_; +text: .text%__1cJOopMapSetHcopy_to6MpC_v_; +text: .text%__1cQjava_lang_ThreadRset_thread_status6FpnHoopDesc_n0AMThreadStatus__v_; +text: .text%__1cJOopMapSetJheap_size6kM_i_; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: classes.o; +text: .text%__1cNSafePointNodeKgrow_stack6MpnIJVMState_I_v_; +text: .text%__1cIJVMState2t6Mi_v_; +text: .text%__1cIAndLNodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interp_masm_sparc.o; +text: .text%__1cIAndLNodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cIAndLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cNSignatureInfoJdo_double6M_v_: frame.o; +text: .text%__1cJLoadSNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIciObjectRis_instance_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cNRelocIterator2t6MpnKCodeBuffer_pC3_v_; +text: .text%__1cMMachProjNodeHsize_of6kM_I_: classes.o; +text: .text%__1cTDebugInfoReadStreamLread_handle6M_nGHandle__; +text: .text%__1cOPhaseIdealLoopUsplit_if_with_blocks6MrnJVectorSet_rnKNode_Stack__v_; +text: .text%__1cMtlsLoadPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassPadd_implementor6MpnMklassOopDesc__v_; +text: .text%__1cLOopRecorderIoop_size6M_i_; +text: .text%__1cYDebugInformationRecorderJdata_size6M_i_; +text: .text%__1cHnmethodPscopes_pcs_size6kM_i_: nmethod.o; +text: .text%__1cYDebugInformationRecorderIpcs_size6M_i_; +text: .text%__1cHnmethodQscopes_data_size6kM_i_: nmethod.o; +text: .text%__1cHnmethodJstub_size6kM_i_: nmethod.o; +text: .text%__1cOPhaseIdealLoopOset_early_ctrl6MpnENode__v_; +text: .text%__1cHnmethodKtotal_size6kM_i_; +text: .text%__1cHnmethodOexception_size6kM_i_: nmethod.o; +text: .text%__1cbFunnecessary_membar_volatileNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMloadConLNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cJiRegLOperFclone6kM_pnIMachOper__; +text: .text%__1cFParseNadd_safepoint6M_v_; +text: .text%__1cOPhaseTransform2t6Mp0nFPhaseLPhaseNumber__v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: phaseX.o; +text: .text%__1cLPhaseValues2t6Mp0_v_; +text: .text%__1cQcmovI_reg_ltNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cXPhaseAggressiveCoalesceGverify6M_v_: coalesce.o; +text: .text%__1cHCompilebBregister_library_intrinsics6M_v_; +text: .text%__1cXPhaseAggressiveCoalesceNinsert_copies6MrnHMatcher__v_; +text: .text%__1cNPhaseRegAlloc2t6MIrnIPhaseCFG_rnHMatcher_pF_v_v_; +text: .text%__1cIPhaseCFGJbuild_cfg6M_I_; +text: .text%__1cHCompileEInit6Mi_v_; +text: .text%__1cVExceptionHandlerTable2t6Mi_v_; +text: .text%__1cHMatcherLreturn_addr6kM_nHOptoRegEName__; +text: .text%__1cMPhaseChaitin2t6MIrnIPhaseCFG_rnHMatcher__v_; +text: .text%__1cMPhaseChaitinRRegister_Allocate6M_v_; +text: .text%__1cHCompileTset_cached_top_node6MpnENode__v_; +text: .text%__1cIPhaseCFGQFind_Inner_Loops6M_v_; +text: .text%__1cHMatcherZnumber_of_saved_registers6F_i_; +text: .text%__1cNPhaseRegAllocTpd_preallocate_hook6M_v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: block.o; +text: .text%__1cMPhaseChaitinMreset_uf_map6MI_v_; +text: .text%__1cMPhaseChaitinRbuild_ifg_virtual6M_v_; +text: .text%__1cIPhaseCFGQGlobalCodeMotion6MrnHMatcher_IrnJNode_List__v_; +text: .text%__1cHMatcherTFixup_Save_On_Entry6M_v_; +text: .text%__1cHMatcherPinit_spill_mask6MpnENode__v_; +text: .text%__1cHCompileICode_Gen6M_v_; +text: .text%__1cKNode_Array2t6MpnFArena__v_: matcher.o; +text: .text%__1cFArena2t6MI_v_; +text: .text%__1cUDebugInfoWriteStream2t6MpnYDebugInformationRecorder_i_v_; +text: .text%__1cHMatcherVinit_first_stack_mask6M_v_; +text: .text%__1cFArenaNmove_contents6Mp0_1_; +text: .text%__1cKCodeBufferGresize6Miiii_v_; +text: .text%__1cFArenaRdestruct_contents6M_v_; +text: .text%__1cIPhaseIFG2t6MpnFArena__v_; +text: .text%__1cFDictIFreset6MpknEDict__v_; +text: .text%__1cHMatcherFmatch6M_v_; +text: .text%__1cHMatcher2t6MrnJNode_List__v_; +text: .text%__1cIPhaseCFGVschedule_pinned_nodes6MrnJVectorSet__v_; +text: .text%__1cIPhaseCFGOschedule_early6MrnJVectorSet_rnJNode_List_rnLBlock_Array__i_; +text: .text%__1cETypeKInitialize6FpnHCompile__v_; +text: .text%__1cIPhaseCFGNschedule_late6MrnJVectorSet_rnJNode_List_rnNGrowableArray4CI___v_; +text: .text%__1cIPhaseCFGYEstimate_Block_Frequency6M_v_; +text: .text%__1cYDebugInformationRecorder2t6MpnLOopRecorder__v_; +text: .text%__1cOCompileWrapper2t6MpnHCompile__v_; +text: .text%__1cIPhaseCFGKDominators6M_v_; +text: .text%__1cIPhaseCFG2t6MpnFArena_pnIRootNode_rnHMatcher__v_; +text: .text%__1cMPhaseChaitinbGstretch_base_pointer_live_ranges6MpnMResourceArea__i_; +text: .text%__1cJPhaseLive2t6MrknIPhaseCFG_rnILRG_List_pnFArena__v_; +text: .text%__1cJPhaseLive2T6M_v_; +text: .text%__1cWemit_exception_handler6FrnKCodeBuffer__v_; +text: .text%__1cHCompileYinit_scratch_buffer_blob6M_v_; +text: .text%__1cKCodeBufferOrelocate_stubs6M_v_; +text: .text%__1cOMachPrologNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMPhaseChaitin2T6M_v_; +text: .text%__1cHMatcherWis_short_branch_offset6Mi_i_; +text: .text%__1cHCompileTFillExceptionTables6MIpI1pnFLabel__v_; +text: .text%__1cMPhaseChaitinbApost_allocate_copy_removal6M_v_; +text: .text%__1cIPhaseCFGLRemoveEmpty6M_v_; +text: .text%__1cHCompileGOutput6M_v_; +text: .text%__1cWImplicitExceptionTableIset_size6MI_v_; +text: .text%__1cHCompileMBuildOopMaps6M_v_; +text: .text%__1cHCompilePneed_stack_bang6kMi_i_; +text: .text%__1cLdo_liveness6FpnNPhaseRegAlloc_pnIPhaseCFG_pnKBlock_List_ipnFArena_pnEDict__v_: buildOopMap.o; +text: .text%__1cGBundlePinitialize_nops6FppnIMachNode__v_; +text: .text%__1cMPhaseChaitinMfixup_spills6M_v_; +text: .text%__1cNPhaseRegAllocPalloc_node_regs6Mi_v_; +text: .text%__1cLBlock_Array2t6MpnFArena__v_: buildOopMap.o; +text: .text%__1cHCompileLFill_buffer6M_v_; +text: .text%__1cVCallRuntimeDirectNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZCallInterpreterDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNSignatureInfoJdo_double6M_v_: bytecode.o; +text: .text%__1cWpoll_return_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cENodeHrm_prec6MI_v_; +text: .text%__1cLcmpD_ccNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cGciTypeMis_classless6kM_i_: ciInstanceKlass.o; +text: .text%__1cIMulLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cHRetNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIGraphKitOset_pair_local6MipnENode__v_: parse2.o; +text: .text%__1cRPrivilegedElementKinitialize6MpnMvframeStream_pnHoopDesc_p0pnGThread__v_; +text: .text%JVM_DoPrivileged; +text: .text%__1cOcompiledVFrameGis_top6kM_i_; +text: .text%__1cRsubI_zero_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHRetNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICodeBlobPallocation_size6FpnKCodeBuffer_ii_I_; +text: .text%__1cQaddL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeBlobRis_at_poll_return6MpC_i_; +text: .text%__1cHciKlassMis_interface6M_i_: ciObjArrayKlass.o; +text: .text%__1cIConDNodeGOpcode6kM_i_; +text: .text%__1cObranchConFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cTresource_free_bytes6FpcI_v_; +text: .text%__1cNmethodOopDescbDbuild_interpreter_method_data6FnMmethodHandle_pnGThread__v_; +text: .text%__1cIAddLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRcompL_reg_conNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNMemoryManagerHoops_do6MpnKOopClosure__v_; +text: .text%__1cPconvL2I_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFciEnvKcompile_id6M_I_; +text: .text%__1cPpoll_RelocationEtype6M_nJrelocInfoJrelocType__: codeBlob.o; +text: .text%__1cRmethodDataOopDescKinitialize6MpnNmethodOopDesc__v_; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_bytes6FpnNmethodOopDesc__i_; +text: .text%__1cRmethodDataOopDescbGcompute_allocation_size_in_words6FpnNmethodOopDesc__i_; +text: .text%__1cIDivINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cILoopNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPmethodDataKlassIallocate6MnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cKoopFactoryOnew_methodData6FnMmethodHandle_pnGThread__pnRmethodDataOopDesc__; +text: .text%__1cNprefetch2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cILoopNodeHsize_of6kM_I_: loopnode.o; +text: .text%__1cIAndLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKCodeBuffer2t6MpCi_v_; +text: .text%__1cVshrL_reg_imm6_L2INodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: nativeInst_sparc.o; +text: .text%__1cIConPNodeEmake6FpC_p0_; +text: .text%__1cIGraphKitNstore_barrier6MpnENode_22_v_; +text: .text%__1cOcmovII_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvL2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: callnode.o; +text: .text%__1cIciMethodRinstructions_size6M_i_; +text: .text%__1cSmulI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovII_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: memnode.o; +text: .text%__1cCosXthread_local_storage_at6Fi_pv_; +text: .text%__1cMindIndexOperNconstant_disp6kM_i_: ad_sparc.o; +text: .text%__1cMindIndexOperOindex_position6kM_i_: ad_sparc.o; +text: .text%__1cMindIndexOperFscale6kM_i_: ad_sparc.o; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: assembler_sparc.o; +text: .text%__1cRAbstractAssemblerbDgenerate_stack_overflow_check6Mi_v_; +text: .text%__1cMindIndexOperNbase_position6kM_i_: ad_sparc.o; +text: .text%__1cNloadKlassNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFStateR_sub_Op_LoadKlass6MpknENode__v_; +text: .text%__1cGTarjanICOMPRESS6M_v_; +text: .text%__1cKstoreCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKloadUBNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cHciKlassGloader6M_pnHoopDesc__: ciTypeArrayKlass.o; +text: .text%__1cICmpDNodeGOpcode6kM_i_; +text: .text%__1cNloadConL0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulLNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cOPhaseIdealLoopOplace_near_use6kMpnENode__2_; +text: .text%__1cVCallRuntimeDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOloadConL13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cYcompareAndSwapL_boolNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cZCallInterpreterDirectNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLstoreB0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSInterpreterRuntimeOprofile_method6FpnKJavaThread_pC_i_; +text: .text%__1cHMonitorGnotify6M_i_; +text: .text%__1cNIdealLoopTreePiteration_split6MpnOPhaseIdealLoop_rnJNode_List__v_; +text: .text%__1cOloadConL13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cMURShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cJloadPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNRelocIteratorEnext6M_i_: compiledIC.o; +text: .text%__1cLOopMapCacheLoop_iterate6MpnKOopClosure__v_; +text: .text%__1cLRShiftINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLOptoRuntimeRmultianewarray1_C6FpnMklassOopDesc_ipnKJavaThread__v_; +text: .text%__1cIMachNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLOpaque2NodeLbottom_type6kM_pknEType__: connode.o; +text: .text%__1cGThreadOis_interrupted6Fp0i_i_; +text: .text%__1cSconvI2D_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: subnode.o; +text: .text%__1cUPSGenerationCountersKupdate_all6M_v_: psGenerationCounters.o; +text: .text%__1cQComputeCallStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cKTypeOopPtrSmake_from_constant6FpnIciObject__pk0_; +text: .text%__1cQregP_to_stkPNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapHppstore6MpnNCellTypeState_i_v_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_43ipnGThread__v_; +text: .text%__1cJTimeStampSticks_since_update6kM_x_; +text: .text%__1cJArrayDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cQmodI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlass.o; +text: .text%__1cNmethodOopDescThas_native_function6kM_i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constantPoolKlass.o; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: loopnode.o; +text: .text%__1cIMulINodeImul_ring6kMpknEType_3_3_; +text: .text%__1cURethrowExceptionNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNmethodOopDescWis_vanilla_constructor6kM_i_; +text: .text%__1cIAddLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cQcmovI_reg_ltNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLstoreB0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRshrL_reg_imm6NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIModINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKstoreFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKklassKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cJcmpOpOperHgreater6kM_i_: ad_sparc_clone.o; +text: .text%__1cQstkI_to_regFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJimmL0OperJconstantL6kM_x_: ad_sparc_clone.o; +text: .text%__1cFParseJdo_ifnull6MnIBoolTestEmask__v_; +text: .text%__1cQmulD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJimmI0OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cFStateM_sub_Op_ConL6MpknENode__v_; +text: .text%__1cOloadConL13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNObjectMonitorHis_busy6kM_i_; +text: .text%JVM_GetClassNameUTF; +text: .text%__1cKloadUBNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIXorINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cFStateM_sub_Op_AndI6MpknENode__v_; +text: .text%__1cVshrL_reg_imm6_L2INodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: multnode.o; +text: .text%__1cKcmpOpFOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cLRuntimeStubHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cLRuntimeStubbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cNflagsRegFOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cTmembar_volatileNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRshlL_reg_imm6NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMultiNodeUdepends_only_on_test6kM_i_: callnode.o; +text: .text%__1cJloadFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: memnode.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: memnode.o; +text: .text%__1cFStateL_sub_Op_OrI6MpknENode__v_; +text: .text%__1cJCmpL3NodeGOpcode6kM_i_; +text: .text%__1cIciObjectOis_method_data6M_i_: ciInstance.o; +text: .text%__1cIciObjectJis_method6M_i_: ciInstance.o; +text: .text%JVM_FindLoadedClass; +text: .text%__1cLCastP2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIMulLNodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIMulLNodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cRbranchLoopEndNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cVAdaptivePaddedAverageGsample6Mf_v_; +text: .text%__1cMMutableSpaceFclear6M_v_; +text: .text%__1cIConFNodeGOpcode6kM_i_; +text: .text%__1cOClearArrayNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cIGraphKitNallocate_heap6MpnENode_222pknITypeFunc_pC22ipknKTypeOopPtr__2_; +text: .text%__1cPciInstanceKlassbBcompute_shared_has_subklass6M_i_; +text: .text%__1cSmembar_acquireNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cMPrefetchNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQmulD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNprefetch2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIModLNodeGOpcode6kM_i_; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectIoverride6FpnHoopDesc__C_; +text: .text%__1cKReflectionGinvoke6FnTinstanceKlassHandle_nMmethodHandle_nGHandle_inOobjArrayHandle_nJBasicType_4ipnGThread__pnHoopDesc__; +text: .text%__1cSbranchCon_longNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cQLibraryIntrinsicIgenerate6MpnIJVMState__2_; +text: .text%__1cOLibraryCallKitNtry_to_inline6M_i_; +text: .text%__1cLRShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKTypeRawPtrFxdual6kM_pknEType__; +text: .text%__1cNloadConL0NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cTInlineCallGeneratorJis_inline6kM_i_: library_call.o; +text: .text%__1cFTypeFEmake6Ff_pk0_; +text: .text%__1cIimmFOperJconstantF6kM_f_: ad_sparc_clone.o; +text: .text%__1cEUTF8Ounicode_length6Fpkc_i_; +text: .text%__1cCosRcurrent_thread_id6F_i_; +text: .text%__1cUSafepointSynchronizeFblock6FpnKJavaThread__v_; +text: .text%__1cOGenerateOopMapJppdupswap6Mipkc_v_; +text: .text%__1cJttyLockerbCbreak_tty_lock_for_safepoint6Fi_v_; +text: .text%__1cbACallCompiledJavaDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmembar_acquireNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cPorI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIPhaseCFGOinsert_goto_at6MII_v_; +text: .text%__1cENodeHis_Copy6kM_I_: node.o; +text: .text%__1cZCallInterpreterDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPCountedLoopNodeNstride_is_con6kM_i_: loopTransform.o; +text: .text%__1cITypeLongFwiden6kMpknEType__3_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: loopnode.o; +text: .text%__1cSThreadLocalStoragePget_thread_slow6F_pnGThread__; +text: .text%__1cSxorI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPCallRuntimeNodeGOpcode6kM_i_; +text: .text%__1cJcmpOpOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cLcmpD_ccNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMindIndexOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindIndexOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cMindIndexOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%JVM_FindClassFromClass; +text: .text%__1cRshrP_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cObranchConFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: objArrayKlassKlass.o; +text: .text%__1cLRethrowNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMMutableSpaceKinitialize6MnJMemRegion_i_v_; +text: .text%__1cKstoreLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQshrI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: loopnode.o; +text: .text%__1cbDjava_lang_reflect_ConstructorFclazz6FpnHoopDesc__2_; +text: .text%__1cbDjava_lang_reflect_ConstructorEslot6FpnHoopDesc__i_; +text: .text%__1cbDjava_lang_reflect_ConstructorPparameter_types6FpnHoopDesc__2_; +text: .text%__1cKReflectionSinvoke_constructor6FpnHoopDesc_nOobjArrayHandle_pnGThread__2_; +text: .text%JVM_NewInstanceFromConstructor; +text: .text%__1cNSignatureInfoIdo_float6M_v_: frame.o; +text: .text%__1cObox_handleNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeEhash6kM_I_: classes.o; +text: .text%__1cFParseFBlockMadd_new_path6M_i_; +text: .text%__1cJBytecodesRspecial_length_at6FpC_i_; +text: .text%__1cIimmPOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cMloadConPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: loopnode.o; +text: .text%__1cQsubL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJloadBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvF2DNodeGOpcode6kM_i_; +text: .text%__1cOstackSlotIOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cLConvI2DNodeGOpcode6kM_i_; +text: .text%__1cSciExceptionHandlerLcatch_klass6M_pnPciInstanceKlass__; +text: .text%__1cMloadConFNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cKcmpOpPOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: interp_masm_sparc.o; +text: .text%__1cLRShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKimmL13OperJconstantL6kM_x_: ad_sparc_clone.o; +text: .text%__1cSTailCalljmpIndNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlassKlass.o; +text: .text%__1cGIfNodeMdominated_by6MpnENode_pnMPhaseIterGVN__v_; +text: .text%__1cNobjArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cMURShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cRsubI_zero_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcompiledVFrame2t6MpknFframe_pknLRegisterMap_pnKJavaThread_pnJScopeDesc__v_; +text: .text%__1cJScopeDesc2t6MpknHnmethod_i_v_; +text: .text%__1cQshlI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHnmethodNscope_desc_at6MpCi_pnJScopeDesc__; +text: .text%__1cOGenerateOopMapJdo_astore6Mi_v_; +text: .text%__1cbFunnecessary_membar_volatileNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHnmethodUnumber_of_dependents6kM_i_: nmethod.o; +text: .text%__1cSmulI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: multnode.o; +text: .text%__1cIGraphKitOnull_check_oop6MpnKRegionNode_pnENode_i_4_; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: compiledIC.o; +text: .text%__1cULinearLeastSquareFitGupdate6Mdd_v_; +text: .text%__1cOoop_RelocationIoop_addr6M_ppnHoopDesc__; +text: .text%__1cKstoreCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKstoreCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNinstanceKlassVis_same_class_package6MpnHoopDesc_pnNsymbolOopDesc__i_; +text: .text%__1cMjniIdMapBaseHoops_do6MpnKOopClosure__v_; +text: .text%__1cJcmpOpOperKless_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cOcmovII_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cXmembar_acquire_lockNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPfieldDescriptorUstring_initial_value6kMpnGThread__pnHoopDesc__; +text: .text%__1cOMacroAssemblerEsetx6MxpnMRegisterImpl_2nJrelocInfoJrelocType__v_; +text: .text%__1cMloadConLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIMaxINodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMindirectOperNconstant_disp6kM_i_: ad_sparc.o; +text: .text%__1cMindirectOperNbase_position6kM_i_: ad_sparc.o; +text: .text%__1cIAddLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMindirectOperFscale6kM_i_: ad_sparc.o; +text: .text%__1cYinternal_word_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cSsubL_reg_reg_2NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescTset_native_function6MpC_v_; +text: .text%jni_NewString: jni.o; +text: .text%__1cLConvL2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQjava_lang_StringXcreate_oop_from_unicode6FpHipnGThread__pnHoopDesc__; +text: .text%__1cQshlI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKoopFactoryNnew_charArray6FpkcpnGThread__pnQtypeArrayOopDesc__; +text: .text%__1cHMatcherQinline_cache_reg6F_nHOptoRegEName__; +text: .text%__1cOcompiledVFrameEcode6kM_pnHnmethod__; +text: .text%__1cIGraphKitMnext_monitor6M_i_; +text: .text%__1cOloadConI13NodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNode2t6Mi_v_; +text: .text%__1cPconvF2D_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLOptoRuntimebBcomplete_monitor_enter_Type6F_pknITypeFunc__; +text: .text%__1cIGraphKitLshared_lock6MpnENode__pnMFastLockNode__; +text: .text%__1cPcmpFastLockNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNloadConP0NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cLOptoRuntimeKjbyte_copy6FpW1I_v_; +text: .text%__1cRorI_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNSignatureInfoIdo_short6M_v_: frame.o; +text: .text%__1cKcmpOpUOperEless6kM_i_: ad_sparc_clone.o; +text: .text%__1cQaddF_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRLowMemoryDetectorWdetect_after_gc_memory6FpnKMemoryPool__v_; +text: .text%lwp_mutex_init: os_solaris.o; +text: .text%__1cObox_handleNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFframeLnmethods_do6M_v_; +text: .text%__1cQjava_lang_ThreadGthread6FpnHoopDesc__pnKJavaThread__; +text: .text%__1cQnotemp_iRegIOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cITemplateIbytecode6kM_nJBytecodesECode__; +text: .text%__1cODataRelocationGoffset6M_i_: relocInfo.o; +text: .text%__1cYinternal_word_RelocationFvalue6M_pC_: relocInfo.o; +text: .text%__1cCosPhint_no_preempt6F_v_; +text: .text%__1cYinternal_word_RelocationJpack_data6M_i_; +text: .text%__1cOcmovII_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUGenericGrowableArrayNraw_appendAll6Mpk0_v_; +text: .text%__1cIMulLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cYinternal_word_RelocationWfix_relocation_at_move6Mi_v_; +text: .text%__1cIMulINodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cPciObjectFactory2t6MpnFArena_i_v_; +text: .text%__1cFciEnv2t6MpnHJNIEnv__iii_v_; +text: .text%__1cRsarL_reg_imm6NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFciEnvWget_method_from_handle6MpnI_jobject__pnIciMethod__; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: cfgnode.o; +text: .text%__1cSstring_compareNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFciEnv2T6M_v_; +text: .text%__1cIGraphKitNgen_checkcast6MpnENode_2p2_2_; +text: .text%__1cMMergeMemNodeIadr_type6kM_pknHTypePtr__: memnode.o; +text: .text%__1cbLtransform_int_divide_to_long_multiply6FpnIPhaseGVN_pnENode_i_3_: divnode.o; +text: .text%__1cJcmpOpOperJnot_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cQmulD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cGvframeDtop6kM_p0_; +text: .text%__1cOCompiledRFrameEinit6M_v_; +text: .text%__1cXmembar_acquire_lockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadSNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cVCallRuntimeDirectNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPcmpFastLockNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerUcompiler_lock_object6MpnMRegisterImpl_222rnFLabel__v_; +text: .text%__1cQciTypeArrayKlassEmake6FnJBasicType__p0_; +text: .text%__1cIXorINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIGraphKitRgen_subtype_check6MpnENode_2_2_; +text: .text%__1cQregF_to_stkINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNSingletonBlobIis_alive6kM_i_: codeBlob.o; +text: .text%__1cOMacroAssemblerLsave_thread6MkpnMRegisterImpl__v_; +text: .text%__1cOcmovII_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: constMethodKlass.o; +text: .text%__1cLcmpD_ccNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKNode_Array2t6MpnFArena__v_: loopopts.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: constMethodKlass.o; +text: .text%__1cMloadConINodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cScheck_phi_clipping6FpnHPhiNode_rpnHConNode_rI45rpnENode_5_i_: cfgnode.o; +text: .text%__1cOcmovII_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeMis_SpillCopy6M_pnRMachSpillCopyNode__: ad_sparc.o; +text: .text%__1cRshlL_reg_imm6NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParseGdo_new6M_v_; +text: .text%__1cZCallDynamicJavaDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQshrI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIimmIOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cQmodI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvI2LNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOPhaseIdealLoopKclone_loop6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%jni_GetObjectClass: jni.o; +text: .text%__1cSxorI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOMacroAssemblerFalign6Mi_v_; +text: .text%__1cRappend_interfaces6FnOobjArrayHandle_ripnPobjArrayOopDesc__v_; +text: .text%__1cbDReferenceProcessorInitializerIis_clean6kM_v_: concurrentMarkSweepGeneration.o; +text: .text%__1cKManagementJtimestamp6F_x_; +text: .text%__1cIPSOldGenPupdate_counters6M_v_; +text: .text%__1cQshrI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNIdealLoopTreeNpolicy_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cFForteNregister_stub6FpkcpC3_v_; +text: .text%__1cNIdealLoopTreeSpolicy_range_check6kMpnOPhaseIdealLoop__i_; +text: .text%__1cFVTuneNregister_stub6FpkcpC3_v_; +text: .text%__1cPCountedLoopNodeJinit_trip6kM_pnENode__: loopTransform.o; +text: .text%__1cNinstanceKlassbFlookup_method_in_all_interfaces6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cTloadL_unalignedNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadLNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cOMacroAssemblerVreset_last_Java_frame6M_v_; +text: .text%__1cOMacroAssemblerTset_last_Java_frame6MpnMRegisterImpl_2_v_; +text: .text%__1cSstring_compareNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNinstanceKlassPoop_is_instance6kM_i_: instanceRefKlass.o; +text: .text%__1cJloadFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotIOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cQregF_to_stkINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cINodeHash2t6MpnFArena_I_v_; +text: .text%__1cPconvI2L_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPhaseTransform2t6MpnFArena_nFPhaseLPhaseNumber__v_; +text: .text%__1cLPhaseValues2t6MpnFArena_I_v_; +text: .text%__1cLCodeletMark2t6MrpnZInterpreterMacroAssembler_pkcinJBytecodesECode__v_: interpreter.o; +text: .text%__1cJStubQdDueueGcommit6Mi_v_; +text: .text%__1cJStubQdDueueHrequest6Mi_pnEStub__; +text: .text%__1cGEventsDlog6FpkcE_v_: nmethod.o; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: typeArrayKlass.o; +text: .text%__1cOcmovII_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOMacroAssemblerKsave_frame6Mi_v_; +text: .text%__1cVshrL_reg_imm6_L2INodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLstoreC0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_and_dispatch6MpnITemplate_nITosState__v_; +text: .text%__1cOPhaseIdealLoopVclone_up_backedge_goo6MpnENode_22_2_; +text: .text%__1cITemplateKinitialize6MinITosState_1pFi_vi_v_; +text: .text%__1cITemplateIgenerate6MpnZInterpreterMacroAssembler__v_; +text: .text%__1cQregI_to_stkINodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%JVM_FindClassFromClassLoader; +text: .text%signalHandler; +text: .text%__1cTtypeArrayKlassKlassIoop_size6kMpnHoopDesc__i_: typeArrayKlassKlass.o; +text: .text%JVM_handle_solaris_signal; +text: .text%__1cRInlineCacheBufferIis_empty6F_i_; +text: .text%__1cUSafepointSynchronizeRis_cleanup_needed6F_i_; +text: .text%__1cQjava_lang_ThreadRget_thread_status6FpnHoopDesc__n0AMThreadStatus__; +text: .text%__1cNSignatureInfoIdo_float6M_v_: bytecode.o; +text: .text%__1cFStateM_sub_Op_AndL6MpknENode__v_; +text: .text%__1cKConv2BNodeGOpcode6kM_i_; +text: .text%__1cSstring_compareNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFframeRis_compiled_frame6kMpi_i_; +text: .text%__1cZInterpreterMacroAssemblerZcheck_and_handle_popframe6MpnMRegisterImpl__v_; +text: .text%JVM_IHashCode; +text: .text%__1cSconvI2D_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJStartNodeJideal_reg6kM_I_: callnode.o; +text: .text%__1cJStartNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cOMacroAssemblerbBcheck_and_forward_exception6MpnMRegisterImpl__v_; +text: .text%__1cOMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cQshlI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_ltNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKDictionarybAis_valid_protection_domain6MiInMsymbolHandle_nGHandle_2_i_; +text: .text%__1cJloadCNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQandL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovPP_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMVM_OperationSis_cheap_allocated6kM_i_: vm_operations.o; +text: .text%__1cNSignatureInfoHdo_char6M_v_: frame.o; +text: .text%__1cNCompileBrokerZcompilation_is_prohibited6FnMmethodHandle_i_i_; +text: .text%__1cLPhaseValuesKis_IterGVN6M_pnMPhaseIterGVN__: phaseX.o; +text: .text%__1cMLinkResolverXresolve_invokeinterface6FrnICallInfo_nGHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cMLinkResolverbGruntime_resolve_interface_method6FrnICallInfo_nMmethodHandle_nLKlassHandle_nGHandle_4ipnGThread__v_; +text: .text%__1cKC2CompilerOcompile_method6MpnFciEnv_pnIciMethod_i_v_; +text: .text%JVM_GetClassLoader; +text: .text%__1cNCompileBrokerZinvoke_compiler_on_method6FpnLCompileTask__v_; +text: .text%__1cNCompileBrokerQset_last_compile6FpnOCompilerThread_nMmethodHandle_ii_v_; +text: .text%__1cbCAbstractInterpreterGeneratorQset_entry_points6MnJBytecodesECode__v_; +text: .text%__1cbCAbstractInterpreterGeneratorWset_short_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cJTraceTime2t6MpkciipnMoutputStream__v_; +text: .text%__1cIciMethodJhas_loops6kM_i_; +text: .text%__1cSconvD2I_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cCosRelapsed_frequency6F_x_; +text: .text%__1cFStateP_sub_Op_ConvL2I6MpknENode__v_; +text: .text%__1cIciMethodQbreak_at_execute6M_i_; +text: .text%__1cOPhaseIdealLoopLdo_split_if6MpnENode__v_; +text: .text%__1cLAccessFlagsRatomic_clear_bits6Mi_v_; +text: .text%__1cKScheduling2t6MpnFArena_rnHCompile__v_; +text: .text%__1cKSchedulingMDoScheduling6M_v_; +text: .text%__1cNCompileBrokerScollect_statistics6FpnOCompilerThread_nMelapsedTimer_pnLCompileTask__v_; +text: .text%__1cFciEnvPregister_method6MpnIciMethod_iiiiiipnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler_ii_v_; +text: .text%__1cFciEnvbOcheck_for_system_dictionary_modification6MpnIciMethod__v_; +text: .text%__1cNCompileBrokerYcheck_compilation_result6FnMmethodHandle_iippnHnmethod__i_; +text: .text%__1cZInterpreterMacroAssemblerMcall_VM_base6MpnMRegisterImpl_22pCii_v_; +text: .text%__1cSCardTableExtensionbAscavenge_contents_parallel6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager_I_v_; +text: .text%__1cRframe_gc_prologue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cFframeMpd_gc_epilog6M_v_; +text: .text%__1cMelapsedTimerHseconds6kM_d_; +text: .text%__1cJStealTaskEname6M_pc_: psTasks.o; +text: .text%__1cRframe_gc_epilogue6FpnFframe_pknLRegisterMap__v_: thread.o; +text: .text%__1cJStealTask2t6Mi_v_; +text: .text%__1cFframeLgc_epilogue6M_v_; +text: .text%__1cFframeLgc_prologue6M_v_; +text: .text%__1cTOldToYoungRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cJStealTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cTOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cNGCTaskManagerMnote_release6MI_v_; +text: .text%__1cMciMethodDataStrap_recompiled_at6MpnLProfileData__i_; +text: .text%__1cXjava_lang_ref_ReferenceWpending_list_lock_addr6F_ppnHoopDesc__; +text: .text%__1cNmethodOopDescIset_code6MpnHnmethod__v_; +text: .text%__1cQshrI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmembar_acquireNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmembar_acquireNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cICodeBlobJcopy_oops6MppnI_jobject_i_v_; +text: .text%__1cYDebugInformationRecorderHcopy_to6MpnHnmethod__v_; +text: .text%__1cVExceptionHandlerTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cHnmethodSresolve_JNIHandles6M_v_; +text: .text%__1cJCodeCacheGcommit6FpnICodeBlob__v_; +text: .text%__1cFVTuneOcreate_nmethod6FpnHnmethod__v_; +text: .text%__1cHnmethodQcopy_scopes_data6MpCi_v_; +text: .text%__1cHnmethod2t6MpnNmethodOopDesc_iiiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__v_; +text: .text%__1cFciEnvVnum_inlined_bytecodes6kM_i_; +text: .text%__1cWImplicitExceptionTableHcopy_to6MpnHnmethod__v_; +text: .text%__1cHnmethodLnew_nmethod6FnMmethodHandle_iiiiiipnYDebugInformationRecorder_pnKCodeBuffer_ipnJOopMapSet_pnVExceptionHandlerTable_pnWImplicitExceptionTable_pnTExceptionRangeTable_pnQAbstractCompiler__p0_; +text: .text%__1cLOopRecorderHcopy_to6MpnICodeBlob__v_; +text: .text%__1cIimmLOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConLNodeFclone6kM_pnENode__; +text: .text%__1cHCompileVfinal_graph_reshaping6M_i_; +text: .text%__1cIciMethodRbuild_method_data6M_v_; +text: .text%__1cHCompileIOptimize6M_v_; +text: .text%__1cHCompileLFinish_Warm6M_v_; +text: .text%__1cbAfinal_graph_reshaping_walk6FrnKNode_Stack_pnENode_rnUFinal_Reshape_Counts__v_: compile.o; +text: .text%__1cHCompileLInline_Warm6M_i_; +text: .text%__1cSPhaseRemoveUseless2t6MpnIPhaseGVN_pnQUnique_Node_List__v_; +text: .text%__1cJStartNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cKInlineTreeWbuild_inline_tree_root6F_p0_; +text: .text%__1cHCompileRbuild_start_state6MpnJStartNode_pknITypeFunc__pnIJVMState__; +text: .text%__1cIPhaseCCPHanalyze6M_v_; +text: .text%__1cIPhaseCCPMdo_transform6M_v_; +text: .text%__1cIPhaseCCPJtransform6MpnENode__2_; +text: .text%__1cIPhaseCCP2T6M_v_; +text: .text%__1cIPhaseCCP2t6MpnMPhaseIterGVN__v_; +text: .text%__1cHCompileVidentify_useful_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cHCompileUremove_useless_nodes6MrnQUnique_Node_List__v_; +text: .text%__1cQUnique_Node_ListUremove_useless_nodes6MrnJVectorSet__v_; +text: .text%__1cMPhaseIterGVN2t6MpnIPhaseGVN__v_; +text: .text%__1cMPhaseIterGVN2t6Mp0_v_; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod_ii_v_; +text: .text%__1cQmulI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableKtransition6FnITosState_1_v_; +text: .text%__1cHCompileNreturn_values6MpnIJVMState__v_; +text: .text%__1cOcmovII_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cXMachCallInterpreterNodePret_addr_offset6M_i_; +text: .text%__1cOMachEpilogNodeQsafepoint_offset6kM_i_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_epilog6MnITosState_i_v_; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_prolog6MnITosState_i_v_; +text: .text%__1cYDebugInformationRecorderKadd_oopmap6MiipnGOopMap__v_; +text: .text%__1cIModINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSxorI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFStateP_sub_Op_RShiftI6MpknENode__v_; +text: .text%__1cRsarI_reg_imm5NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%lwp_cond_init: os_solaris.o; +text: .text%__1cTmembar_volatileNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNloadConL0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cbFunnecessary_membar_volatileNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cENodeIis_Start6M_pnJStartNode__: callnode.o; +text: .text%__1cRshlL_reg_imm6NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXmembar_acquire_lockNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cKPSYoungGenRcapacity_in_bytes6kM_I_; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: interp_masm_sparc.o; +text: .text%__1cMloadConDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cbCCompiledCodeSafepointHandlerbDhandle_polling_page_exception6M_pC_; +text: .text%__1cZInterpreterMacroAssemblerTdispatch_Lbyte_code6MnITosState_ppCii_v_; +text: .text%__1cNSafepointBlobbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cFframebDsender_for_raw_compiled_frame6kMpnLRegisterMap__0_; +text: .text%__1cIJVMStateOis_monitor_use6kMI_i_: reg_split.o; +text: .text%__1cUSafepointSynchronizebDhandle_polling_page_exception6FpnKJavaThread__pC_; +text: .text%__1cOloadConI13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSconvI2F_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadSNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQaddF_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIAddFNodeGOpcode6kM_i_; +text: .text%__1cObranchConFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKciTypeFlowLStateVectorJdo_aaload6MpnQciByteCodeStream__v_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_; +text: .text%__1cFStateO_sub_Op_Binary6MpknENode__v_; +text: .text%__1cKBinaryNodeGOpcode6kM_i_; +text: .text%__1cNSignatureInfoIdo_short6M_v_: bytecode.o; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_3pnRJavaCallArguments_nGHandle_6_6_; +text: .text%__1cNmethodOopDescWcompute_has_loops_flag6M_i_; +text: .text%__1cQstkI_to_regFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLBoxLockNodeDcmp6kMrknENode__I_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constMethodKlass.o; +text: .text%__1cSCompiledStaticCallSset_to_interpreted6MnMmethodHandle_pC_v_; +text: .text%__1cSCompiledStaticCallJfind_stub6M_pC_; +text: .text%__1cRNativeMovConstRegIset_data6Mi_v_; +text: .text%__1cFParsebLincrement_and_test_invocation_counter6Mi_v_; +text: .text%__1cJStartNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSsafePoint_pollNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMTailCallNodeGOpcode6kM_i_; +text: .text%__1cJBytecodesDdef6Fn0AECode_pkc33nJBasicType_ii1i_v_; +text: .text%__1cQregP_to_stkPNodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeTprepare_native_call6FpnKJavaThread_pnNmethodOopDesc__v_; +text: .text%__1cHTypePtrFempty6kM_i_; +text: .text%__1cXSignatureHandlerLibraryDadd6FnMmethodHandle__v_; +text: .text%__1cSsafePoint_pollNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNobjArrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cPClassFileParserUverify_constantvalue6MiinSconstantPoolHandle_pnGThread__v_; +text: .text%__1cZInterpreterMacroAssemblerNdispatch_next6MnITosState_i_v_; +text: .text%__1cIMulFNodeGOpcode6kM_i_; +text: .text%__1cISubLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQmulD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPconvF2D_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOloadConI13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: exceptions.o; +text: .text%__1cTmembar_CPUOrderNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTmembar_CPUOrderNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodKlass.o; +text: .text%__1cSCompareAndSwapNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNSCMemProjNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSThreadLocalStorageGthread6F_pnGThread__: assembler_sparc.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodKlass.o; +text: .text%__1cGThreadVset_pending_exception6MpnHoopDesc_pkci_v_; +text: .text%jni_SetByteArrayRegion: jni.o; +text: .text%__1cFMutex2t6Mipkci_v_; +text: .text%__1cQregI_to_stkINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_StringPcreate_from_str6FpkcpnGThread__nGHandle__; +text: .text%__1cSdivL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cFStateM_sub_Op_XorI6MpknENode__v_; +text: .text%__1cHTypePtrEmake6FnETypeFTYPES_n0ADPTR_i_pk0_; +text: .text%__1cCosLelapsedTime6F_d_; +text: .text%__1cKScopeValueJread_from6FpnTDebugInfoReadStream__p0_; +text: .text%__1cKPerfMemoryMmark_updated6F_v_; +text: .text%__1cSobjArrayKlassKlassbCallocate_objArray_klass_impl6FnYobjArrayKlassKlassHandle_inLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cIPerfData2t6MnJCounterNS_pkcn0AFUnits_n0ALVariability__v_; +text: .text%__1cKPerfMemoryFalloc6FI_pc_; +text: .text%__1cLStrCompNodeKmatch_edge6kMI_I_; +text: .text%__1cQmulL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cILocation2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cKJavaThreadZsecurity_get_caller_class6Mi_pnMklassOopDesc__; +text: .text%jni_ReleaseStringUTFChars; +text: .text%jni_GetStringUTFChars: jni.o; +text: .text%__1cSobjArrayKlassKlassXallocate_objArray_klass6MinLKlassHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cFParseLarray_store6MnJBasicType__v_; +text: .text%__1cSInterpreterRuntimeNquicken_io_cc6FpnKJavaThread__v_; +text: .text%__1cSInterpreterRuntimeXthrow_pending_exception6FpnKJavaThread__v_; +text: .text%__1cSmulI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_IsInterrupted; +text: .text%__1cLLShiftLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNSignatureInfoHdo_char6M_v_: bytecode.o; +text: .text%JVM_FindLibraryEntry; +text: .text%__1cWConstantPoolCacheEntrySset_interface_call6MnMmethodHandle_i_v_; +text: .text%__1cLklassItableUcompute_itable_index6FpnNmethodOopDesc__i_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: assembler_sparc.o; +text: .text%__1cQSystemDictionaryPresolve_or_fail6FnMsymbolHandle_ipnGThread__pnMklassOopDesc__; +text: .text%__1cRshlL_reg_imm6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHCompile2t6MpnFciEnv_pnIciMethod_i_v_; +text: .text%__1cQshlL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: classes.o; +text: .text%__1cHBitDataKis_BitData6M_i_: ciMethodData.o; +text: .text%__1cNLocationValueLis_location6kM_i_: debugInfo.o; +text: .text%__1cPconvF2D_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLRShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSstring_compareNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIMinINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMacroAssemblerRload_ptr_contents6MrnHAddress_pnMRegisterImpl_i_v_: assembler_sparc.o; +text: .text%__1cOMacroAssemblerEstop6Mpkc_v_; +text: .text%__1cKRegionNodeJideal_reg6kM_I_: loopnode.o; +text: .text%__1cENodeKis_PCTable6kM_pknLPCTableNode__: loopnode.o; +text: .text%__1cENodeHis_Root6M_pnIRootNode__: loopnode.o; +text: .text%__1cSconvI2F_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cObranchConFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHMatcherOc_return_value6Fii_nLRegPair__; +text: .text%__1cENodeHis_Copy6kM_I_: loopnode.o; +text: .text%__1cKloadUBNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKg3RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cSsubL_reg_reg_2NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddP_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cTloadL_unalignedNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cLcmpD_ccNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cICallNodeRis_CallStaticJava6kM_pknSCallStaticJavaNode__: callnode.o; +text: .text%__1cMregD_lowOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cTCallDynamicJavaNodeSis_CallDynamicJava6kM_pk0_: callnode.o; +text: .text%__1cTloadL_unalignedNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLLShiftLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cbIjava_lang_reflect_AccessibleObjectMset_override6FpnHoopDesc_C_v_; +text: .text%__1cObranchConFNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cObox_handleNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cXJNI_ArgumentPusherVaArgHget_int6M_v_: jni.o; +text: .text%__1cQmodI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRbranchLoopEndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddF_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKcmpOpUOperHgreater6kM_i_: ad_sparc_clone.o; +text: .text%__1cUParallelScavengeHeapEused6kM_I_; +text: .text%__1cIDivINodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cVCallRuntimeDirectNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQxorI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLOptoRuntimeOarraycopy_Type6F_pknITypeFunc__; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodKlass.o; +text: .text%__1cWCallLeafNoFPDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTbasictype2arraycopy6FnJBasicType_i_pC_; +text: .text%__1cOLibraryCallKitQinline_arraycopy6M_i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodKlass.o; +text: .text%__1cLcmpD_ccNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cWCallLeafNoFPDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLOptoRuntimeSnew_typeArray_Type6F_pknITypeFunc__; +text: .text%__1cJloadINodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitJnew_array6MpnENode_nJBasicType_pknEType_pknMTypeKlassPtr__2_; +text: .text%__1cbBopt_virtual_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cNTemplateTableDdef6FnJBytecodesECode_inITosState_3pFi_vi_v_; +text: .text%__1cIMinINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cKarrayKlassKjava_super6kM_pnMklassOopDesc__; +text: .text%__1cTmembar_CPUOrderNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cOClearArrayNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cRbranchLoopEndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRbranchLoopEndNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cLMachUEPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTmembar_volatileNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKo1RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cCosTnative_java_library6F_pv_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cSInterpreterRuntimeOmultianewarray6FpnKJavaThread_pi_v_; +text: .text%__1cSxorI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cYinlineCallClearArrayNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cYinlineCallClearArrayNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMPhaseChaitinGSelect6M_I_; +text: .text%__1cLOptoRuntimeInew_Type6F_pknITypeFunc__; +text: .text%__1cFParseSjump_switch_ranges6MpnENode_pnLSwitchRange_4i_v_; +text: .text%__1cSbranchCon_longNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cSbranchCon_longNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSbranchCon_longNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosYprint_jni_name_suffix_on6FpnMoutputStream_i_v_; +text: .text%__1cIModINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cCosYprint_jni_name_prefix_on6FpnMoutputStream_i_v_; +text: .text%__1cIGraphKitMnew_instance6MpnPciInstanceKlass__pnENode__; +text: .text%__1cLstoreP0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cMloadConLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseTprofile_switch_case6Mi_v_; +text: .text%__1cOcmovIL_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMNativeLookupGlookup6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cSandI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIimmLOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cFParseOmerge_new_path6Mi_v_; +text: .text%__1cYMachCallCompiledJavaNodePret_addr_offset6M_i_; +text: .text%__1cQregP_to_stkPNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_StringGoffset6FpnHoopDesc__i_; +text: .text%__1cQjava_lang_StringFvalue6FpnHoopDesc__pnQtypeArrayOopDesc__; +text: .text%__1cNinstanceKlassKfind_field6kMpnNsymbolOopDesc_2ipnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cQjava_lang_StringScreate_from_symbol6FnMsymbolHandle_pnGThread__nGHandle__; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: templateTable_sparc.o; +text: .text%__1cHnmethodXinterpreter_entry_point6M_pC_; +text: .text%__1cSmembar_releaseNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQPSIsAliveClosureLdo_object_b6MpnHoopDesc__i_: psScavenge.o; +text: .text%jni_NewByteArray: jni.o; +text: .text%__1cQdivL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_2NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_53pnGThread__v_; +text: .text%__1cUdivL_reg_imm13_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeTnmethod_entry_point6FpnKJavaThread_pnNmethodOopDesc_pnHnmethod__pC_; +text: .text%__1cQaddF_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionarybAvalidate_protection_domain6FnTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cKDictionaryVadd_protection_domain6MiInTinstanceKlassHandle_nGHandle_2pnGThread__v_; +text: .text%__1cFParseLdo_newarray6MnJBasicType__v_; +text: .text%__1cPmethodDataKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cMNativeLookupLlookup_base6FnMmethodHandle_ripnGThread__pC_; +text: .text%__1cNmethodOopDescKklass_name6kM_pnNsymbolOopDesc__; +text: .text%__1cKRegionNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cSconvI2D_helperNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreP0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHRetNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMciArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cSmembar_releaseNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIPerfLong2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability__v_; +text: .text%__1cKarrayKlassXbase_create_array_klass6FrknKKlass_vtbl_inLKlassHandle_pnGThread__nQarrayKlassHandle__; +text: .text%__1cKarrayKlassbBcomplete_create_array_klass6FnQarrayKlassHandle_nLKlassHandle_pnGThread__v_; +text: .text%__1cSTailCalljmpIndNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQcmovI_reg_gtNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cObox_handleNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetMethodIxExceptionTableEntry; +text: .text%__1cSReferenceProcessorZadd_to_discovered_list_mt6MppnHoopDesc_23_v_; +text: .text%__1cIDivINodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLstoreP0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQstkI_to_regFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRethrowNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKloadUBNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompileSrethrow_exceptions6MpnIJVMState__v_; +text: .text%__1cURethrowExceptionNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLRethrowNode2t6MpnENode_22222_v_; +text: .text%__1cTLoadL_unalignedNodeGOpcode6kM_i_; +text: .text%__1cSmulI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerZget_2_byte_integer_at_bcp6MipnMRegisterImpl_2n0ALsignedOrNot_n0AKsetCCOrNot__v_; +text: .text%__1cQcmovI_reg_gtNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cURethrowExceptionNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPfieldDescriptorSlong_initial_value6kM_x_; +text: .text%__1cISubLNodeDsub6kMpknEType_3_3_; +text: .text%__1cPciObjArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cJLoadINodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cQandI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNobjArrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cQmulI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParsePmerge_exception6Mi_v_; +text: .text%__1cLStrCompNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cYinlineCallClearArrayNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNobjArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlass.o; +text: .text%__1cNloadConP0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_ReleaseStringCritical: jni.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: objArrayKlass.o; +text: .text%__1cNobjArrayKlassZcan_be_primary_super_slow6kM_i_; +text: .text%__1cJCMoveNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%jni_GetStringCritical: jni.o; +text: .text%__1cUmulL_reg_imm13_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHciKlassSsuper_check_offset6M_I_; +text: .text%__1cSTailCalljmpIndNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPciObjArrayKlassGloader6M_pnHoopDesc__: ciObjArrayKlass.o; +text: .text%__1cRorI_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregF_to_stkINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeKmethod_set6Mi_v_; +text: .text%__1cWCallLeafNoFPDirectNodeRis_safepoint_node6kM_i_: ad_sparc_misc.o; +text: .text%__1cWCallLeafNoFPDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: parse1.o; +text: .text%__1cIDivLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPICStubInterfaceRcode_size_to_size6kMi_i_: icBuffer.o; +text: .text%__1cPICStubInterfaceKinitialize6MpnEStub_i_v_: icBuffer.o; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: frame.o; +text: .text%__1cMloadConFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMadjust_check6FpnENode_11iipnMPhaseIterGVN__v_: ifnode.o; +text: .text%__1cJScopeDescGsender6kM_p0_; +text: .text%__1cNloadConP0NodeFclone6kM_pnENode__; +text: .text%__1cSxorI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJimmP0OperFclone6kM_pnIMachOper__; +text: .text%__1cOcompiledVFrameGsender6kM_pnGvframe__; +text: .text%__1cZInterpreterMacroAssemblerDpop6MnITosState__v_; +text: .text%__1cNloadConPCNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_2NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cGThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cQPlaceholderTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cXJvmtiCurrentBreakpointsHoops_do6FpnKOopClosure__v_; +text: .text%__1cKciTypeFlowLStateVectorEtrap6MpnQciByteCodeStream_pnHciKlass_i_v_; +text: .text%__1cNMemoryServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cNThreadServiceHoops_do6FpnKOopClosure__v_; +text: .text%__1cQregI_to_stkINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJNIHandlesHoops_do6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryRpreloaded_oops_do6FpnKOopClosure__v_; +text: .text%__1cIregDOperFclone6kM_pnIMachOper__; +text: .text%__1cLJvmtiExportHoops_do6FpnKOopClosure__v_; +text: .text%__1cIUniverseHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cIVMThreadHoops_do6MpnKOopClosure__v_; +text: .text%__1cKJNIHandlesMweak_oops_do6FpnRBoolObjectClosure_pnKOopClosure__v_; +text: .text%__1cSObjectSynchronizerHoops_do6FpnKOopClosure__v_; +text: .text%__1cMFlatProfilerHoops_do6FpnKOopClosure__v_; +text: .text%__1cOPhaseIdealLoopOadd_constraint6MiipnENode_22p23_v_; +text: .text%__1cKManagementHoops_do6FpnKOopClosure__v_; +text: .text%__1cYinternal_word_RelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cIregFOperFclone6kM_pnIMachOper__; +text: .text%__1cJloadDNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cKstoreBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQshlL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQSystemDictionaryRnumber_of_classes6F_i_; +text: .text%__1cOcmovPI_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cObox_handleNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreI0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQciByteCodeStreamPget_klass_index6M_i_; +text: .text%__1cFframeIpatch_pc6MpnGThread_pC_v_; +text: .text%__1cNinstanceKlassQmethod_index_for6kMpnNmethodOopDesc_pnGThread__i_; +text: .text%__1cNmethodOopDescVparameter_annotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cNmethodOopDescLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cRtestI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNmethodOopDescbGresolved_checked_exceptions_impl6Fp0pnGThread__nOobjArrayHandle__; +text: .text%__1cFParseMdo_checkcast6M_v_; +text: .text%__1cOCompiledRFrameLis_compiled6kM_i_: rframe.o; +text: .text%__1cOCompiledRFrameKtop_method6kM_nMmethodHandle__: rframe.o; +text: .text%__1cICodeBlobPis_runtime_stub6kM_i_: nmethod.o; +text: .text%__1cKReflectionTget_parameter_types6FnMmethodHandle_ippnHoopDesc_pnGThread__nOobjArrayHandle__; +text: .text%__1cRtestI_reg_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcmovIL_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJimmU6OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cHRegMask2t6M_v_: matcher.o; +text: .text%__1cOPhaseIdealLoopJdo_unroll6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cOGenerateOopMapIcopy_cts6MpnNCellTypeState_2_i_; +text: .text%__1cLServiceUtilLvisible_oop6FpnHoopDesc__i_: objectMonitor_solaris.o; +text: .text%__1cNObjectMonitorGEnterI6MpnGThread__v_; +text: .text%__1cIimmIOperFclone6kM_pnIMachOper__; +text: .text%__1cMloadConINodeFclone6kM_pnENode__; +text: .text%__1cSmulL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPstoreI_FregNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcmpD_ccNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXMachCallDynamicJavaNodePret_addr_offset6M_i_; +text: .text%__1cENodeFis_If6M_pnGIfNode__: ad_sparc_misc.o; +text: .text%__1cNflagsRegFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cXvirtual_call_RelocationEtype6M_nJrelocInfoJrelocType__: relocInfo.o; +text: .text%__1cXvirtual_call_RelocationJpack_data6M_i_; +text: .text%__1cPPerfDataManagerMcounter_name6Fpkc2_pc_; +text: .text%__1cSconvD2I_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHMonitor2t6Mipkci_v_; +text: .text%__1cIModLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMloadConFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOtypeArrayKlassOmulti_allocate6MipiipnGThread__pnHoopDesc__; +text: .text%__1cbBjava_lang_ref_SoftReferenceJset_clock6Fx_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersbBupdate_counters_from_policy6M_v_; +text: .text%__1cXTraceMemoryManagerStats2T6M_v_; +text: .text%__1cQSystemDictionaryHoops_do6FpnKOopClosure__v_; +text: .text%__1cQLRUMaxHeapPolicy2t6M_v_; +text: .text%__1cSReferenceProcessorbDprocess_discovered_references6M_v_; +text: .text%__1cSReferenceProcessorQprocess_phaseJNI6M_v_; +text: .text%__1cSReferenceProcessorbDenqueue_discovered_references6M_i_; +text: .text%__1cSReferenceProcessorbBenqueue_discovered_reflists6MppnHoopDesc__v_; +text: .text%__1cUParallelScavengeHeapQresize_all_tlabs6M_v_; +text: .text%__1cUParallelScavengeHeapPupdate_counters6M_v_; +text: .text%__1cUParallelScavengeHeapTensure_parseability6M_v_; +text: .text%__1cUParallelScavengeHeapOfill_all_tlabs6M_v_; +text: .text%__1cUParallelScavengeHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cVLoaderConstraintTableHoops_do6MpnKOopClosure__v_; +text: .text%__1cXTraceMemoryManagerStats2t6Mi_v_; +text: .text%__1cTDerivedPointerTablePupdate_pointers6F_v_; +text: .text%__1cNCollectedHeapOfill_all_tlabs6M_v_; +text: .text%__1cNCollectedHeapTensure_parseability6M_v_; +text: .text%__1cNCollectedHeapbFaccumulate_statistics_all_tlabs6M_v_; +text: .text%__1cNCollectedHeapQresize_all_tlabs6M_v_; +text: .text%__1cMTypeKlassPtrFxmeet6kMpknEType__3_; +text: .text%__1cXjava_lang_ref_ReferenceRpending_list_addr6F_ppnHoopDesc__; +text: .text%__1cKPSYoungGenPupdate_counters6M_v_; +text: .text%__1cWThreadLocalAllocBufferbFaccumulate_statistics_before_gc6F_v_; +text: .text%__1cWThreadLocalAllocBufferQresize_all_tlabs6F_v_; +text: .text%__1cPGCMemoryManagerIgc_begin6M_v_; +text: .text%__1cPGCMemoryManagerGgc_end6M_v_; +text: .text%__1cRLowMemoryDetectorRdetect_low_memory6F_v_; +text: .text%__1cNMemoryServiceStrack_memory_usage6F_v_; +text: .text%__1cbAPSGCAdaptivePolicyCountersPupdate_counters6M_v_; +text: .text%__1cTDerivedPointerTableFclear6F_v_; +text: .text%__1cKDictionaryHoops_do6MpnKOopClosure__v_; +text: .text%__1cORuntimeServiceWrecord_safepoint_begin6F_v_; +text: .text%__1cSObjectSynchronizerVdeflate_idle_monitors6F_v_; +text: .text%__1cMCounterDecayFdecay6F_v_; +text: .text%__1cCosbCmake_polling_page_unreadable6F_v_; +text: .text%__1cRInlineCacheBufferUupdate_inline_caches6F_v_; +text: .text%__1cLConvI2FNodeGOpcode6kM_i_; +text: .text%__1cORuntimeServicebDrecord_safepoint_synchronized6F_v_; +text: .text%__1cQaddF_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRshlL_reg_imm6NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUSafepointSynchronizeFbegin6F_v_; +text: .text%__1cKarrayKlassTallocate_arrayArray6MiipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cONMethodSweeperFsweep6F_v_; +text: .text%__1cNCollectedHeapYcommon_mem_allocate_init6FIipnGThread__pnIHeapWord__: arrayKlass.o; +text: .text%__1cTAbstractInterpreterRnotice_safepoints6F_v_; +text: .text%__1cCosbAmake_polling_page_readable6F_v_; +text: .text%__1cUSafepointSynchronizeDend6F_v_; +text: .text%__1cOcmovII_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cTAbstractInterpreterRignore_safepoints6F_v_; +text: .text%__1cORuntimeServiceUrecord_safepoint_end6F_v_; +text: .text%__1cJStoreNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cPconvF2D_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKimmU13OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cQshlL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cUcompU_iReg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_GetCallerClass; +text: .text%__1cNSignatureInfoHdo_byte6M_v_: bytecode.o; +text: .text%__1cOcmovPP_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKstoreBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSobjArrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cLstoreC0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadL_unalignedNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cICmpFNodeGOpcode6kM_i_; +text: .text%__1cSdivL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cOstackSlotPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cQregF_to_stkINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJLoadDNodeGOpcode6kM_i_; +text: .text%__1cQmulD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvI2F_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%jni_IsAssignableFrom: jni.o; +text: .text%jni_GetFieldID: jni.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pCii_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: cpCacheKlass.o; +text: .text%__1cJLoadPNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: cpCacheKlass.o; +text: .text%__1cLstoreB0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cZInterpreterMacroAssemblerbAget_cache_and_index_at_bcp6MpnMRegisterImpl_2i_v_; +text: .text%__1cHTypeAryFxdual6kM_pknEType__; +text: .text%__1cMtlsLoadPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJEventMark2t6MpkcE_v_: vmThread.o; +text: .text%__1cIVMThreadHexecute6FpnMVM_Operation__v_; +text: .text%__1cCosMget_priority6FkpknGThread_rnOThreadPriority__nIOSReturn__; +text: .text%__1cGThreadMget_priority6Fkpk0_nOThreadPriority__; +text: .text%__1cMVM_OperationIevaluate6M_v_; +text: .text%__1cMVM_OperationSset_calling_thread6MpnGThread_nOThreadPriority__v_; +text: .text%__1cCosTget_native_priority6FkpknGThread_pi_nIOSReturn__; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: interp_masm_sparc.o; +text: .text%__1cQdivL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cMnegD_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUParallelScavengeHeapMmem_allocate6MIii_pnIHeapWord__; +text: .text%__1cQregP_to_stkPNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQcmovI_reg_gtNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cWloadConI_x43300000NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cGGCTask2t6Mn0AEKindEkind__v_; +text: .text%__1cNGCTaskManagerVrelease_all_resources6M_v_; +text: .text%__1cUPSAdaptiveSizePolicyWminor_collection_begin6M_v_; +text: .text%__1cLGCTaskQdDueueHenqueue6Mp0_v_; +text: .text%__1cSCardTableExtensionRscavenge_contents6MpnQObjectStartArray_pnMMutableSpace_pnIHeapWord_pnSPSPromotionManager__v_; +text: .text%__1cUWaitForBarrierGCTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cNGCTaskManagerIadd_list6MpnLGCTaskQdDueue__v_; +text: .text%__1cHThreadsZcreate_thread_roots_tasks6FpnLGCTaskQdDueue__v_; +text: .text%__1cUWaitForBarrierGCTaskGcreate6F_p0_; +text: .text%__1cUWaitForBarrierGCTaskIdestruct6M_v_; +text: .text%__1cSObjectSynchronizerJfast_exit6FpnHoopDesc_pnJBasicLock_pnGThread__v_; +text: .text%__1cSObjectSynchronizerKfast_enter6FnGHandle_pnJBasicLock_pnGThread__v_; +text: .text%__1cUPSAdaptiveSizePolicyUminor_collection_end6MnHGCCauseFCause__v_; +text: .text%__1cSPSPromotionManagerNpost_scavenge6F_v_; +text: .text%__1cNBarrierGCTaskOdo_it_internal6MpnNGCTaskManager_I_v_; +text: .text%__1cNJvmtiGCMarker2T6M_v_; +text: .text%__1cUWaitForBarrierGCTaskHdestroy6Fp0_v_; +text: .text%__1cLGCTaskQdDueueGcreate6F_p0_; +text: .text%__1cSPSPromotionManagerMpre_scavenge6F_v_; +text: .text%__1cZSerialOldToYoungRootsTaskFdo_it6MpnNGCTaskManager_I_v_; +text: .text%__1cQinstanceRefKlassbKrelease_and_notify_pending_list_lock6FipnJBasicLock__v_; +text: .text%__1cQinstanceRefKlassZacquire_pending_list_lock6FpnJBasicLock__v_; +text: .text%__1cZSerialOldToYoungRootsTaskEname6M_pc_: psTasks.o; +text: .text%__1cKPSYoungGenLswap_spaces6M_v_; +text: .text%__1cUParallelScavengeHeapQresize_young_gen6MII_v_; +text: .text%__1cODeoptimizationYtrap_state_is_recompiled6Fi_i_; +text: .text%__1cKPSYoungGenGresize6MII_v_; +text: .text%__1cKPSYoungGenNresize_spaces6MII_v_; +text: .text%__1cUPSAdaptiveSizePolicyPupdate_averages6MiII_v_; +text: .text%__1cUPSAdaptiveSizePolicybPcompute_survivor_space_size_and_threshold6MiiI_i_; +text: .text%__1cSPSPromotionManagerbBvm_thread_promotion_manager6F_p0_; +text: .text%__1cUWaitForBarrierGCTaskIwait_for6M_v_; +text: .text%__1cKPSScavengeQinvoke_no_policy6Fpi_i_; +text: .text%__1cPVM_GC_OperationQgc_count_changed6kM_i_; +text: .text%__1cKPSYoungGenRresize_generation6MII_i_; +text: .text%__1cPVM_GC_OperationNdoit_prologue6M_i_; +text: .text%__1cPVM_GC_OperationNdoit_epilogue6M_v_; +text: .text%__1cNMonitorSupplyHreserve6F_pnHMonitor__; +text: .text%__1cNJvmtiGCMarker2t6Mi_v_; +text: .text%__1cNMonitorSupplyHrelease6FpnHMonitor__v_; +text: .text%__1cUWaitForBarrierGCTaskEname6M_pc_: gcTaskManager.o; +text: .text%__1cTmembar_volatileNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cVLoaderConstraintTableWfind_constrained_klass6MnMsymbolHandle_nGHandle__pnMklassOopDesc__; +text: .text%__1cQjava_lang_ThreadJis_daemon6FpnHoopDesc__i_; +text: .text%__1cVCallRuntimeDirectNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadL_unalignedNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovII_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQComputeCallStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cMLinkResolverbPlinktime_resolve_interface_method_or_null6FnLKlassHandle_nMsymbolHandle_21i_nMmethodHandle__; +text: .text%__1cMURShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSCompiledStaticCallIis_clean6kM_i_; +text: .text%__1cSCompiledStaticCallNcompute_entry6FnMmethodHandle_rnOStaticCallInfo__v_; +text: .text%__1cUmulL_reg_imm13_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserbJparse_classfile_signature_attribute6MnSconstantPoolHandle_nTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cKstoreBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCompiledStaticCallDset6MrknOStaticCallInfo__v_; +text: .text%__1cOGenerateOopMapXreplace_all_CTS_matches6MnNCellTypeState_1_v_; +text: .text%__1cFframeZinterpreter_frame_set_mdp6MpC_v_; +text: .text%__1cLOptoRuntimeVresolve_static_call_C6FpnKJavaThread__pC_; +text: .text%__1cZInterpreterMacroAssemblerIpush_ptr6MpnMRegisterImpl__v_; +text: .text%__1cHMatcherbAinterpreter_method_oop_reg6F_nHOptoRegEName__; +text: .text%__1cTCallInterpreterNodeSis_CallInterpreter6kM_pk0_: classes.o; +text: .text%__1cTCallInterpreterNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cHCompilebMGenerate_Compiled_To_Interpreter_Graph6MpknITypeFunc_pC_v_; +text: .text%__1cISubLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cZCallInterpreterDirectNodeKmethod_set6Mi_v_; +text: .text%__1cIciMethodRinterpreter_entry6M_pC_; +text: .text%__1cQmulF_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cXMachCallInterpreterNodeWis_MachCallInterpreter6M_p0_: ad_sparc_misc.o; +text: .text%__1cZCallInterpreterDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPconvF2D_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZCallInterpreterDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRcompL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWloadConI_x41f00000NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadBNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%jni_SetBooleanField: jni.o; +text: .text%__1cQciByteCodeStreamFtable6MnJBytecodesECode__2_; +text: .text%__1cKimmL13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cRsarL_reg_imm6NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: sharedRuntime.o; +text: .text%__1cLcmpF_ccNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKloadUBNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLRuntimeStubbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_: codeBlob.o; +text: .text%__1cRorI_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cWResolveOopMapConflictsRpossible_gc_point6MpnOBytecodeStream__i_: rewriter.o; +text: .text%__1cRsarL_reg_imm6NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQmulI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMnegD_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_object6M_v_; +text: .text%__1cZInterpreterMacroAssemblerGpush_i6MpnMRegisterImpl__v_; +text: .text%__1cUdivL_reg_imm13_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cPClassFileParserbBcheck_illegal_static_method6FnTinstanceKlassHandle_pnGThread__v_; +text: .text%__1cLLShiftLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cOcmovIL_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2D_memNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQComputeCallStackJdo_double6M_v_: generateOopMap.o; +text: .text%__1cJloadSNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cRloadConP_pollNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstkL_to_regD_0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNObjectMonitorHRecycle6M_v_; +text: .text%__1cNSharedRuntimeSfind_callee_method6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cQshlL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cMloadConLNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOMacroAssemblerWstore_unaligned_double6MpnRFloatRegisterImpl_pnMRegisterImpl_i_v_; +text: .text%__1cJloadDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryTresolve_from_stream6FnMsymbolHandle_nGHandle_2pnPClassFileStream_pnGThread__pnMklassOopDesc__; +text: .text%__1cQstkI_to_regFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQregP_to_stkPNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerFpop_i6MpnMRegisterImpl__v_; +text: .text%__1cSTailCalljmpIndNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSTailCalljmpIndNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cIMaxINodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cLOptoRuntimeThandle_wrong_method6FpnKJavaThread__pC_; +text: .text%__1cOMacroAssemblerUstore_unaligned_long6MpnMRegisterImpl_2i_v_; +text: .text%__1cSmulL_reg_reg_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNSharedRuntimeTreresolve_call_site6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cOLibraryCallKitRinline_unsafe_CAS6MnJBasicType__i_; +text: .text%__1cTCompareAndSwapLNode2t6MpnENode_2222_v_; +text: .text%__1cYcompareAndSwapL_boolNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSCompareAndSwapNode2t6MpnENode_2222_v_; +text: .text%__1cNSCMemProjNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cYcompareAndSwapL_boolNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cIProjNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cUPSAdaptiveSizePolicyOshould_full_GC6MI_i_; +text: .text%__1cIPSOldGenMmax_gen_size6M_I_: psOldGen.o; +text: .text%__1cUPSAdaptiveSizePolicyQdecaying_gc_cost6kM_d_; +text: .text%__1cUPSAdaptiveSizePolicybDcompute_generation_free_space6MIIIIIIIi_v_; +text: .text%__1cSmulL_reg_reg_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKPSScavengeGinvoke6Fpi_v_; +text: .text%__1cUPSAdaptiveSizePolicyVadjust_for_throughput6MipI1_v_; +text: .text%__1cKExceptionsK_throw_msg6FpnGThread_pkcipnNsymbolOopDesc_4_v_; +text: .text%__1cSsubL_reg_reg_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUPSAdaptiveSizePolicyZdecay_supplemental_growth6Mi_v_; +text: .text%__1cSdivL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cUParallelScavengeHeapTfailed_mem_allocate6MpiIii_pnIHeapWord__; +text: .text%__1cbDVM_ParallelGCFailedAllocation2t6MIiiI_v_; +text: .text%__1cbDVM_ParallelGCFailedAllocationEdoit6M_v_; +text: .text%__1cbDVM_ParallelGCFailedAllocationEname6kM_pkc_: vm_operations.o; +text: .text%__1cQaddL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPadd_derived_oop6FppnHoopDesc_2_v_: oopMap.o; +text: .text%__1cMregD_lowOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cHOrINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cOMethodLivenessKBasicBlockFsplit6Mi_p1_; +text: .text%__1cOcmovII_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseScreate_jump_tables6MpnENode_pnLSwitchRange_4_i_; +text: .text%__1cENodeEgetd6kM_d_; +text: .text%__1cZInterpreterMacroAssemblerKverify_oop6MpnMRegisterImpl_nITosState__v_; +text: .text%__1cOcmovIL_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTAbstractInterpreterSBasicType_as_index6FnJBasicType__i_; +text: .text%__1cZInterpreterMacroAssemblerGpush_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cIciObject2t6MpnHciKlass__v_; +text: .text%__1cRtestI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSxorI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPjava_lang_ClassQprimitive_mirror6FnJBasicType__pnHoopDesc__; +text: .text%__1cKExceptionsL_throw_args6FpnGThread_pkcinMsymbolHandle_5pnRJavaCallArguments__v_; +text: .text%__1cKstoreFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHMatcherXinterpreter_arg_ptr_reg6F_nHOptoRegEName__; +text: .text%__1cPstoreI_FregNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCMovePNodeGOpcode6kM_i_; +text: .text%__1cLstoreC0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKciTypeFlowOsplit_range_at6Mi_pn0AFRange__; +text: .text%__1cNObjectMonitorEwait6MxipnGThread__v_; +text: .text%__1cSmulI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%JVM_MonitorWait; +text: .text%__1cSObjectSynchronizerEwait6FnGHandle_xpnGThread__v_; +text: .text%__1cNloadConPCNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIAddLNodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cHciKlass2t6MpnIciSymbol_p0_v_; +text: .text%__1cGciType2t6MpnHciKlass__v_; +text: .text%__1cQshlI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQdivD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciObjectUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cFParseSjump_if_false_fork6MpnGIfNode_ii_v_; +text: .text%__1cNloadConL0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHBoxNodeGOpcode6kM_i_; +text: .text%__1cRshrL_reg_imm6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMflagsRegOperFclone6kM_pnIMachOper__; +text: .text%__1cSconvI2F_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregF_to_stkINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cbFunnecessary_membar_volatileNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUciInstanceKlassKlassEmake6F_p0_; +text: .text%__1cENode2t6Mp0111111_v_; +text: .text%__1cIDivLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cZInterpreterMacroAssemblerGpush_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cPstoreI_FregNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParseRarray_store_check6M_v_; +text: .text%__1cQsubF_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIciSymbolHbyte_at6Mi_i_; +text: .text%__1cKCompiledICSset_ic_destination6MpC_v_; +text: .text%__1cbDreorder_based_on_method_index6FpnPobjArrayOopDesc_1ppnHoopDesc__v_: methodOop.o; +text: .text%__1cQaddD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cCosTset_native_priority6FpnGThread_i_nIOSReturn__; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongVariable__; +text: .text%__1cQshlI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQset_lwp_priority6Fiii_i_; +text: .text%__1cSstkL_to_regD_0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_StringTcreate_oop_from_str6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cbCAbstractInterpreterGeneratorVset_vtos_entry_points6MpnITemplate_rpC44444444_v_; +text: .text%__1cQregI_to_stkINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%jni_NewStringUTF: jni.o; +text: .text%__1cZInterpreterMacroAssemblerGpush_l6MpnMRegisterImpl__v_; +text: .text%__1cQsubI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cZInterpreterMacroAssemblerXget_constant_pool_cache6MpnMRegisterImpl__v_; +text: .text%__1cSbranchCon_longNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cXjava_lang_ref_ReferenceOset_discovered6FpnHoopDesc_2_v_; +text: .text%__1cKcmpOpUOperKless_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cMLinkResolverUresolve_virtual_call6FrnICallInfo_nGHandle_nLKlassHandle_4nMsymbolHandle_54iipnGThread__v_; +text: .text%__1cPciInstanceKlassNloader_handle6M_pnI_jobject__; +text: .text%__1cPciInstanceKlassYprotection_domain_handle6M_pnI_jobject__; +text: .text%__1cUParallelScavengeHeapIcapacity6kM_I_; +text: .text%__1cNmethodOopDescKjmethod_id6M_pnK_jmethodID__; +text: .text%__1cSsubL_reg_reg_2NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQaddD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNget_method_id6FpnHJNIEnv__pnH_jclass_pkc5ipnGThread__pnK_jmethodID__: jni.o; +text: .text%__1cMjniIdSupportNto_jmethod_id6FpnNmethodOopDesc__pnK_jmethodID__; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceRefKlass.o; +text: .text%__1cENodeGis_Mem6M_pnHMemNode__: cfgnode.o; +text: .text%JVM_DefineClassWithSource; +text: .text%__1cLstoreF0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cJloadINodeFclone6kM_pnENode__; +text: .text%JVM_SetClassSigners; +text: .text%__1cQdivL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cXconvI2D_regDHi_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledICMset_to_clean6M_v_; +text: .text%__1cICodeBlobYcaller_must_gc_arguments6kMpnKJavaThread__i_: codeBlob.o; +text: .text%__1cSandL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRbranchLoopEndNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cLRShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNSingletonBlobbIfollow_roots_or_mark_for_unloading6MpnRBoolObjectClosure_pnKOopClosure_iri_v_: codeBlob.o; +text: .text%__1cJMarkSweepSFollowStackClosureHdo_void6M_v_: markSweep.o; +text: .text%__1cFParseXfetch_interpreter_state6MipknEType_pnENode__5_; +text: .text%__1cFParseWcheck_interpreter_type6MpnENode_pknEType_rpnNSafePointNode__2_; +text: .text%__1cOcmovPP_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSThreadLocalStorageSset_thread_in_slot6FpnGThread__v_; +text: .text%get_thread; +text: .text%__1cKstoreCNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSThreadLocalStoragebBget_thread_via_cache_slowly6FIi_pnGThread__; +text: .text%__1cSThreadLocalStorageKset_thread6FpnGThread__v_; +text: .text%jni_CallIntMethod: jni.o; +text: .text%__1cSThreadLocalStorageNpd_set_thread6FpnGThread__v_; +text: .text%__1cKloadUBNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregL_to_stkLNodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cUCallCompiledJavaNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cSconvD2I_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHCompilebMGenerate_Interpreter_To_Compiled_Graph6MpknITypeFunc__v_; +text: .text%__1cbACallCompiledJavaDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cbACallCompiledJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSconvD2I_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cbACallCompiledJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cIMulDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cbACallCompiledJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_return_entry_for6MnITosState_i_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_deopt_entry_for6MnITosState_i_pC_; +text: .text%__1cSaddP_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIAddDNodeGOpcode6kM_i_; +text: .text%__1cOcmovPP_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvI2D_helperNodeFclone6kM_pnENode__; +text: .text%__1cOloadI_fregNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cHCompileRmake_vm_intrinsic6MpnIciMethod_i_pnNCallGenerator__; +text: .text%__1cOloadI_fregNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCountedLoopEndNode2t6MpnENode_2ff_v_; +text: .text%__1cQmulD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosJyield_all6Fi_v_; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: vmThread.o; +text: .text%__1cKstoreLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPstoreI_FregNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFTypeDJsingleton6kM_i_; +text: .text%__1cLstoreC0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassMethodsCount; +text: .text%__1cKstoreINodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%JVM_GetClassFieldsCount; +text: .text%__1cLconvI2BNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_ThreadMis_stillborn6FpnHoopDesc__i_; +text: .text%__1cRorI_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassCPEntriesCount; +text: .text%JVM_GetClassCPTypes; +text: .text%__1cGEventsDlog6FpkcE_v_: thread.o; +text: .text%__1cHciKlassMis_interface6M_i_: ciTypeArrayKlass.o; +text: .text%__1cQmulI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOMacroAssemblerKverify_FPU6Mipkc_v_; +text: .text%__1cVinline_cache_regPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cbCfind_class_from_class_loader6FpnHJNIEnv__nMsymbolHandle_CnGHandle_3CpnGThread__pnH_jclass__; +text: .text%__1cQjava_lang_ThreadKset_thread6FpnHoopDesc_pnKJavaThread__v_; +text: .text%__1cIAddFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cPconvF2D_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregI_to_stkINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulF_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cbBinitialize_itable_for_klass6FpnMklassOopDesc__v_; +text: .text%__1cTloadL_unalignedNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_5pnGThread__v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interpreterRT_sparc.o; +text: .text%__1cXSignatureHandlerLibraryOpd_set_handler6FpC_v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIgenerate6MX_v_; +text: .text%JVM_IsPrimitiveClass; +text: .text%__1cJimmU6OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cOPhaseIdealLoopUpeeled_dom_test_elim6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cIDivDNodeGOpcode6kM_i_; +text: .text%__1cObox_handleNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRorI_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Loop6M_pnILoopNode__: cfgnode.o; +text: .text%__1cKloadUBNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cTmembar_volatileNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_reg_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLStringTableJbasic_add6MinGHandle_pHiIpnGThread__pnHoopDesc__; +text: .text%__1cIModLNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cXconvI2D_regDHi_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%jni_FindClass: jni.o; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cOMacroAssemblerOstore_argument6MpnMRegisterImpl_rnIArgument__v_: interpreterRT_sparc.o; +text: .text%__1cFParseHdo_irem6M_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorThas_signature_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorVhas_annotations_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorbFhas_parameter_annotations_field6F_i_; +text: .text%__1cbDjava_lang_reflect_ConstructorTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorGcreate6FpnGThread__nGHandle__; +text: .text%__1cGThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cGThreadVis_jvmti_agent_thread6kM_i_: thread.o; +text: .text%__1cKReflectionPnew_constructor6FnMmethodHandle_pnGThread__pnHoopDesc__; +text: .text%__1cENodeZcheck_for_anti_dependence6kM_i_: machnode.o; +text: .text%__1cNloadConP0NodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarL_reg_imm6NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovII_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSdivL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cTloadL_unalignedNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNSharedRuntimeDd2l6Fd_x_; +text: .text%__1cJStubQdDueueRrequest_committed6Mi_pnEStub__; +text: .text%__1cRInlineCacheBufferRic_stub_code_size6F_i_; +text: .text%__1cFStateP_sub_Op_RShiftL6MpknENode__v_; +text: .text%__1cPICStubInterfaceEsize6kMpnEStub__i_: icBuffer.o; +text: .text%__1cPICStubInterfaceIfinalize6MpnEStub__v_: icBuffer.o; +text: .text%__1cObox_handleNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cQshrI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapOdo_monitorexit6Mi_v_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cQregI_to_stkINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRorI_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOGenerateOopMapLmonitor_pop6M_nNCellTypeState__; +text: .text%__1cMregD_lowOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cLConvD2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSconvI2F_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHMonitor2T6M_v_; +text: .text%__1cFTypeDFxmeet6kMpknEType__3_; +text: .text%__1cFMutex2T6M_v_; +text: .text%__1cRtestI_reg_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%lwp_cond_destroy: os_solaris.o; +text: .text%lwp_mutex_destroy: os_solaris.o; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: multnode.o; +text: .text%__1cQdivI_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cVcompiledICHolderKlassIoop_size6kMpnHoopDesc__i_; +text: .text%__1cLPhaseValuesHlongcon6Mx_pnIConLNode__; +text: .text%__1cQregP_to_stkPNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQstkI_to_regFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQregI_to_stkINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQRelocationHolderEplus6kMi_0_; +text: .text%__1cUPSMarkSweepDecoratorHcompact6Mi_v_; +text: .text%__1cUPSMarkSweepDecoratorPadjust_pointers6M_v_; +text: .text%__1cUPSMarkSweepDecoratorKprecompact6M_v_; +text: .text%__1cTloadL_unalignedNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_ThreadLthreadGroup6FpnHoopDesc__2_; +text: .text%__1cQregF_to_stkINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHCompileQgrow_alias_types6M_v_; +text: .text%__1cLLShiftLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cbFunnecessary_membar_volatileNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOcmovII_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNiRegIsafeOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cNinstanceKlassKlink_class6MpnGThread__v_; +text: .text%__1cUmulL_reg_imm13_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKloadUBNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHnmethodPis_dependent_on6MpnMklassOopDesc__i_; +text: .text%__1cTloadD_unalignedNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cZInterpreterMacroAssemblerHpop_ptr6MpnMRegisterImpl__v_; +text: .text%__1cQdivD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHis_Bool6M_pnIBoolNode__: connode.o; +text: .text%__1cKLoadPCNodeGOpcode6kM_i_; +text: .text%__1cENodeIis_CMove6M_pnJCMoveNode__: connode.o; +text: .text%__1cOloadConL13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRcompL_reg_conNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQaddF_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFKlassMoop_is_array6kM_i_: methodDataKlass.o; +text: .text%__1cICmpDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFKlassPoop_is_instance6kM_i_: methodDataKlass.o; +text: .text%__1cLcmpF_ccNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKsplit_once6FpnMPhaseIterGVN_pnENode_333_v_: cfgnode.o; +text: .text%__1cLLShiftLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJloadFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cJCMoveNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFciEnvWis_dependence_violated6FpnMklassOopDesc_pnNmethodOopDesc__i_; +text: .text%__1cOPhaseIdealLoopOdo_range_check6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cQaddD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassRprotection_domain6M_pnHoopDesc__: instanceRefKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: instanceKlassKlass.o; +text: .text%__1cObox_handleNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSconvD2I_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitPdstore_rounding6MpnENode__2_; +text: .text%__1cJloadINodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSdivL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQregP_to_stkPNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRloadConP_pollNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIModINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cZCallDynamicJavaDirectNodeKmethod_set6Mi_v_; +text: .text%__1cFframeVshould_be_deoptimized6kM_i_; +text: .text%__1cZCallDynamicJavaDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXvirtual_call_RelocationMupdate_addrs6MrknKCodeBuffer_3_v_; +text: .text%__1cUdivL_reg_imm13_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvD2I_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCMoveINodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPCallRuntimeNodeOis_CallRuntime6kM_pk0_: callnode.o; +text: .text%__1cZCallDynamicJavaDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsubF_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeBlobOis_java_method6kM_i_: codeBlob.o; +text: .text%__1cQMachCallJavaNodeVis_MachCallStaticJava6M_pnWMachCallStaticJavaNode__: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC2i_v_; +text: .text%__1cUmulL_reg_imm13_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQdivL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cENodeMis_CatchProj6kM_pknNCatchProjNode__: classes.o; +text: .text%__1cOcmovPI_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cUdivL_reg_imm13_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cMTypeKlassPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cUmulL_reg_imm13_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNCallGeneratorQfor_virtual_call6FpnIciMethod__p0_; +text: .text%__1cUVirtualCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cNObjectMonitor2t6M_v_; +text: .text%__1cTmembar_volatileNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulINodeKadd_opcode6kM_i_: classes.o; +text: .text%__1cIMulINodeKmul_opcode6kM_i_: classes.o; +text: .text%__1cQdivD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cJCmpD3NodeGOpcode6kM_i_; +text: .text%__1cJloadDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMinINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOBasicHashtableJnew_entry6MI_pnTBasicHashtableEntry__; +text: .text%__1cUVirtualCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cQmulF_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_MonitorNotify; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: templateTable_sparc.o; +text: .text%__1cFBlockNset_next_call6MpnENode_rnJVectorSet_rnLBlock_Array__v_; +text: .text%__1cSObjectSynchronizerGnotify6FnGHandle_pnGThread__v_; +text: .text%__1cXNativeSignatureIteratorJdo_object6Mii_v_: interpreterRT_sparc.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: interpreterRT_sparc.o; +text: .text%__1cKstoreFNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cSstring_compareNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cYjava_lang_reflect_MethodVhas_annotations_field6F_i_; +text: .text%__1cVshrL_reg_imm6_L2INodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cYjava_lang_reflect_MethodIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cOloadConL13NodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cYjava_lang_reflect_MethodPset_return_type6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodGcreate6FpnGThread__nGHandle__; +text: .text%__1cYjava_lang_reflect_MethodbFhas_parameter_annotations_field6F_i_; +text: .text%__1cINegDNodeGOpcode6kM_i_; +text: .text%__1cYjava_lang_reflect_MethodJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodZset_parameter_annotations6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodWset_annotation_default6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_parameter_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodTset_exception_types6FpnHoopDesc_2_v_; +text: .text%__1cYjava_lang_reflect_MethodThas_signature_field6F_i_; +text: .text%__1cYjava_lang_reflect_MethodNset_modifiers6FpnHoopDesc_i_v_; +text: .text%__1cYjava_lang_reflect_MethodbChas_annotation_default_field6F_i_; +text: .text%__1cOimmI_32_63OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cYjava_lang_reflect_MethodIset_name6FpnHoopDesc_2_v_; +text: .text%__1cNmethodOopDescSannotation_default6kM_pnQtypeArrayOopDesc__; +text: .text%__1cKReflectionKnew_method6FnMmethodHandle_iipnGThread__pnHoopDesc__; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cNIdealLoopTreeXpolicy_maximally_unroll6kMpnOPhaseIdealLoop__i_; +text: .text%__1cSsubL_reg_reg_2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQregP_to_stkPNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNRelocIteratorEnext6M_i_: output.o; +text: .text%__1cOcmovII_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotPOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%jni_GetMethodID: jni.o; +text: .text%__1cTloadD_unalignedNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cQshlL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIMulINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceRefKlass.o; +text: .text%__1cObranchConFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNminI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRshlI_reg_imm5NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNminI_eRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOloadConL13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNObjectMonitorGnotify6MpnGThread__v_; +text: .text%__1cOMacroAssemblerDjmp6MpnMRegisterImpl_ipkci_v_; +text: .text%__1cIDivLNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSsubD_regD_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassDeclaredConstructors; +text: .text%__1cUdivL_reg_imm13_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmulD_regD_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJavaThreadbScheck_safepoint_and_suspend_for_native_trans6Fp0_v_; +text: .text%__1cRInlineCacheBufferVic_buffer_entry_point6FpC_1_; +text: .text%__1cOcmovIF_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUmulL_reg_imm13_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsubD_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubD_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cUdivL_reg_imm13_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSandL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConPCNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cMmatch_option6FpknMJavaVMOption_pkcp4_i_: arguments.o; +text: .text%__1cZregDHi_regDLo_to_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJEventMark2t6MpkcE_v_: psMarkSweep.o; +text: .text%__1cQregP_to_stkPNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCMoveNodeEmake6FpnENode_222pknEType__p0_; +text: .text%__1cJCMoveNode2t6MpnENode_22pknEType__v_: connode.o; +text: .text%__1cSconvI2F_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRInlineCacheBufferWcreate_transition_stub6FpnKCompiledIC_pnHoopDesc_pC_v_; +text: .text%__1cRInlineCacheBufferXassemble_ic_buffer_code6FpCpnHoopDesc_1_v_; +text: .text%__1cOcmovIF_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MipnMRegisterImpl__v_; +text: .text%__1cQcmovI_reg_ltNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNloadConL0NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cKo1RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cRtestI_reg_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivI_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubL_reg_reg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIBytecodeIset_code6MnJBytecodesECode__v_; +text: .text%__1cQshrL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRsarL_reg_imm6NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadBNodeFclone6kM_pnENode__; +text: .text%__1cJloadFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSaddD_regD_regDNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeHeapLfirst_block6kM_pnJHeapBlock__; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorIpass_int6M_v_: interpreterRT_sparc.o; +text: .text%__1cRorI_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassQfind_local_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__i_; +text: .text%__1cQshrL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQshrI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOimmI_32_63OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cSstkL_to_regD_0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cUGenericGrowableArrayKraw_remove6MpknEGrET__v_; +text: .text%__1cOloadI_fregNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMnegD_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTAbstractInterpreterLdeopt_entry6FnITosState_i_pC_; +text: .text%__1cLConvI2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cJloadSNodeFclone6kM_pnENode__; +text: .text%__1cQjava_lang_ThreadMset_priority6FpnHoopDesc_nOThreadPriority__v_; +text: .text%__1cMnegD_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledICKcached_oop6kM_pnHoopDesc__; +text: .text%__1cPBoundRelocationLunpack_data6MnJrelocInfoJrelocType__v_: output.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cISubFNodeGOpcode6kM_i_; +text: .text%JVM_IsThreadAlive; +text: .text%__1cQstkI_to_regINodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQshrL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_ThreadIis_alive6FpnHoopDesc__i_; +text: .text%__1cFKlassMoop_is_klass6kM_i_: typeArrayKlass.o; +text: .text%__1cXPartialSubtypeCheckNodeGOpcode6kM_i_; +text: .text%__1cSsubD_regD_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSmulD_regD_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLconvI2BNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovIF_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cRsarL_reg_imm6NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQaddI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRtestI_reg_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRtestI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregI_to_stkINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFTypeFJsingleton6kM_i_; +text: .text%__1cLconvI2BNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_reg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSconvD2I_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubF_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_immNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopVinsert_pre_post_loops6MpnNIdealLoopTree_rnJNode_List_i_v_; +text: .text%__1cQaddD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovPI_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKConv2BNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSstring_compareNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQregL_to_stkLNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_SystemTout_offset_in_bytes6F_i_; +text: .text%__1cQjava_lang_SystemSin_offset_in_bytes6F_i_; +text: .text%__1cLTypeInstPtrRcast_to_exactness6kMi_pknEType__; +text: .text%__1cWPredictedCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cWPredictedCallGeneratorJis_inline6kM_i_: callGenerator.o; +text: .text%__1cLcmpF_ccNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cWPredictedCallGeneratorKis_virtual6kM_i_: callGenerator.o; +text: .text%__1cNCallGeneratorSfor_predicted_call6FpnHciKlass_p03_3_; +text: .text%__1cSconvI2F_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cXconvI2D_regDHi_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cObox_handleNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cNCallGeneratorRfor_uncommon_trap6FpnIciMethod_nODeoptimizationLDeoptReason_n0CLDeoptAction__p0_; +text: .text%__1cOcmovPP_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZUncommonTrapCallGeneratorIgenerate6MpnIJVMState__2_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSaddD_regD_regDNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIVMThreadMis_VM_thread6kM_i_: vmThread.o; +text: .text%__1cIMulFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQmulF_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcmpF_ccNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregL_to_stkLNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cGThread2t6M_v_; +text: .text%__1cOcmovLI_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regINodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosHSolarisPhotspot_sigmask6FpnGThread__v_; +text: .text%__1cCosHSolarisVinit_thread_fpu_state6F_v_; +text: .text%__1cFTypeFFxmeet6kMpknEType__3_; +text: .text%__1cCosScurrent_stack_size6F_I_; +text: .text%__1cOcmovLL_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cIOSThreadNpd_initialize6M_v_; +text: .text%__1cCosScurrent_stack_base6F_pC_; +text: .text%__1cIOSThread2t6MpFpv_i1_v_; +text: .text%__1cIMulDNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cCosRinitialize_thread6F_v_; +text: .text%__1cSdivL_reg_reg_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cGThreadOis_Java_thread6kM_i_: vmThread.o; +text: .text%__1cCosPpd_start_thread6FpnGThread__v_; +text: .text%__1cLConvI2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cNobjArrayKlassIallocate6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cNobjArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlass.o; +text: .text%__1cFTypeDGis_nan6kM_i_; +text: .text%jni_NewObjectArray: jni.o; +text: .text%__1cSsubL_reg_reg_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_immNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%JVM_SetThreadPriority; +text: .text%__1cQaddF_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cCosMstart_thread6FpnGThread__v_; +text: .text%__1cCosNcreate_thread6FpnGThread_n0AKThreadType_I_i_; +text: .text%_start: os_solaris.o; +text: .text%__1cXjava_lang_reflect_FieldNset_modifiers6FpnHoopDesc_i_v_; +text: .text%JVM_GetStackAccessControlContext; +text: .text%__1cXjava_lang_reflect_FieldThas_signature_field6F_i_; +text: .text%__1cXjava_lang_reflect_FieldVhas_annotations_field6F_i_; +text: .text%__1cXjava_lang_reflect_FieldPset_annotations6FpnHoopDesc_2_v_; +text: .text%__1cQsubD_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerTload_unaligned_long6MpnMRegisterImpl_i2_v_; +text: .text%__1cFStateM_sub_Op_ModI6MpknENode__v_; +text: .text%JVM_Read; +text: .text%__1cOcmovPI_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cKCompiledICOset_cached_oop6MpnHoopDesc__v_; +text: .text%__1cFStateM_sub_Op_SubL6MpknENode__v_; +text: .text%__1cKCompiledICMstub_address6kM_pC_; +text: .text%__1cFciEnvZcall_has_multiple_targets6FpnNinstanceKlass_nMsymbolHandle_3ri_i_; +text: .text%__1cKReflectionJnew_field6FpnPfieldDescriptor_ipnGThread__pnHoopDesc__; +text: .text%__1cJvmSymbolsOsignature_type6FpnNsymbolOopDesc__nJBasicType__; +text: .text%__1cQsubL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQmodI_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cISubDNodeGOpcode6kM_i_; +text: .text%__1cQmodI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPfieldDescriptorLannotations6kM_pnQtypeArrayOopDesc__; +text: .text%__1cRsarI_reg_imm5NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: callGenerator.o; +text: .text%__1cKReflectionInew_type6FnMsymbolHandle_nLKlassHandle_pnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldIset_slot6FpnHoopDesc_i_v_; +text: .text%__1cXjava_lang_reflect_FieldIset_type6FpnHoopDesc_2_v_; +text: .text%__1cXjava_lang_reflect_FieldGcreate6FpnGThread__nGHandle__; +text: .text%__1cXjava_lang_reflect_FieldJset_clazz6FpnHoopDesc_2_v_; +text: .text%__1cLOptoRuntimeWresolve_virtual_call_C6FpnKJavaThread__pC_; +text: .text%__1cXjava_lang_reflect_FieldIset_name6FpnHoopDesc_2_v_; +text: .text%__1cSmulD_regD_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassYremove_dependent_nmethod6MpnHnmethod__v_; +text: .text%__1cMloadConINodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetStaticFieldID: jni.o; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadKlassNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSstring_compareNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreF0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerIjumpl_to6MrnHAddress_pnMRegisterImpl_i_v_: interp_masm_sparc.o; +text: .text%__1cOJavaAssertionsHenabled6Fpkci_i_; +text: .text%__1cPciObjArrayKlassEmake6FpnHciKlass__p0_; +text: .text%__1cSsubD_regD_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassKjni_id_for6Mi_pnFJNIid__; +text: .text%__1cSstkL_to_regD_0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFStateO_sub_Op_CMoveI6MpknENode__v_; +text: .text%__1cRsarL_reg_imm6NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIModLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cENodeEgetf6kM_f_; +text: .text%JVM_DesiredAssertionStatus; +text: .text%__1cKJavaThreadKinitialize6M_v_; +text: .text%__1cENodeIis_Multi6M_pnJMultiNode__: node.o; +text: .text%__1cNThreadServiceKadd_thread6FpnKJavaThread_i_v_; +text: .text%__1cWThreadLocalAllocBufferKinitialize6M_v_; +text: .text%__1cUregI_to_stkLHi_1NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cLConvL2DNodeGOpcode6kM_i_; +text: .text%__1cNjni_functions6F_pknTJNINativeInterface___; +text: .text%__1cCosMguard_memory6FpcI_i_; +text: .text%__1cENodeIis_Store6kM_pknJStoreNode__: node.o; +text: .text%__1cENodeGis_Cmp6kM_pknHCmpNode__: node.o; +text: .text%__1cQThreadStatistics2t6M_v_; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC22i_v_; +text: .text%__1cUThreadSafepointStateGcreate6FpnKJavaThread__v_; +text: .text%__1cQshrL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cGParker2t6M_v_; +text: .text%__1cIDivLNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSdivL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubD_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: interp_masm_sparc.o; +text: .text%__1cMFlatProfilerJis_active6F_i_; +text: .text%__1cOMacroAssemblerNload_contents6MrnHAddress_pnMRegisterImpl_i_v_: interp_masm_sparc.o; +text: .text%__1cGThreadFstart6Fp0_v_; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_: interp_masm_sparc.o; +text: .text%__1cPconvI2D_memNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%jni_GetFloatArrayRegion: jni.o; +text: .text%__1cJMarkSweepMfollow_stack6F_v_; +text: .text%__1cNimmP_pollOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cRtestI_reg_immNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZregDHi_regDLo_to_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMemRegionMintersection6kMk0_0_; +text: .text%__1cMVirtualSpaceJexpand_by6MI_i_; +text: .text%__1cUregI_to_stkLHi_0NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQdivI_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKJavaThread2t6MpFp0pnGThread__vI_v_; +text: .text%__1cOcmovIF_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryQjava_mirror_type6FpnHoopDesc__nJBasicType__; +text: .text%__1cRloadConP_pollNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cPPerfDataManagerIadd_item6FpnIPerfData_i_v_; +text: .text%__1cKJavaThreadDrun6M_v_; +text: .text%__1cNSafepointBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cPjava_lang_ClassOprimitive_type6FpnHoopDesc__nJBasicType__; +text: .text%JVM_IsArrayClass; +text: .text%jni_CallStaticVoidMethod: jni.o; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnPPerfLongCounter__; +text: .text%__1cJloadDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNObjectMonitorGenter26MpnGThread__v_; +text: .text%__1cLConvF2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cNsymbolOopDescWas_klass_external_name6kM_pkc_; +text: .text%__1cHnmethodbDpreserve_callee_argument_oops6MnFframe_pknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cKstoreBNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFKlassNexternal_name6kM_pkc_; +text: .text%__1cOloadI_fregNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeHdel_out6Mp0_v_: generateOptoStub.o; +text: .text%__1cOGenerateOopMapYrewrite_refval_conflicts6M_v_; +text: .text%__1cKstoreLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNloadConPCNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLstoreC0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cICodeHeapJexpand_by6MI_i_; +text: .text%__1cOGenerateOopMapKinterp_all6M_v_; +text: .text%__1cOGenerateOopMapPinitialize_vars6M_v_; +text: .text%__1cObranchConFNodeJis_Branch6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadD_unalignedNodeIpipeline6kM_pknIPipeline__; +text: .text%JVM_GetClassName; +text: .text%__1cOGenerateOopMapTmethodsig_to_effect6MpnNsymbolOopDesc_ipnNCellTypeState__i_; +text: .text%__1cOloadI_fregNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: objArrayKlass.o; +text: .text%__1cOGenerateOopMapbAmake_context_uninitialized6M_v_; +text: .text%__1cOGenerateOopMapKinit_state6M_v_; +text: .text%__1cOGenerateOopMapYsetup_method_entry_state6M_v_; +text: .text%__1cOGenerateOopMapTmark_reachable_code6M_v_; +text: .text%__1cOGenerateOopMapRinit_basic_blocks6M_v_; +text: .text%__1cSaddD_regD_regDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLStringTableGintern6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cOcmovIF_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cCosMset_priority6FpnGThread_nOThreadPriority__nIOSReturn__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: loopnode.o; +text: .text%__1cQshrL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHMatcherXpost_store_load_barrier6FpknENode__i_; +text: .text%__1cLConvD2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQstkI_to_regFNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcmovIL_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVcompiledICHolderKlassSoop_being_unloaded6MpnRBoolObjectClosure_pnHoopDesc__i_; +text: .text%__1cXNativeSignatureIteratorGdo_int6M_v_: interpreterRT_sparc.o; +text: .text%__1cINodeHashEgrow6M_v_; +text: .text%__1cOGenerateOopMapPdo_monitorenter6Mi_v_; +text: .text%__1cOcmovPP_regNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cMloadConDNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLStrCompNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMacroAssemblerVload_unaligned_double6MpnMRegisterImpl_ipnRFloatRegisterImpl__v_; +text: .text%__1cIMaxINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cJloadSNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOGenerateOopMapLcompute_map6MpnGThread__v_; +text: .text%__1cHnmethodNis_osr_method6kM_i_: nmethod.o; +text: .text%__1cLConvF2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQsubI_reg_regNodeFclone6kM_pnENode__; +text: .text%JVM_Open; +text: .text%__1cRInvocationCounterFreset6M_v_; +text: .text%__1cRCompilationPolicybIreset_counter_for_invocation_event6MnMmethodHandle__v_; +text: .text%__1cOGenerateOopMap2t6MnMmethodHandle__v_; +text: .text%__1cSsubL_reg_reg_2NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapRdo_interpretation6M_v_; +text: .text%__1cIRetTableRcompute_ret_table6MnMmethodHandle__v_; +text: .text%__1cICodeBlobJis_zombie6kM_i_: onStackReplacement.o; +text: .text%__1cOGenerateOopMapMmonitor_push6MnNCellTypeState__v_; +text: .text%__1cOGenerateOopMapNinitialize_bb6M_v_; +text: .text%__1cOGenerateOopMapbImark_bbheaders_and_count_gc_points6M_v_; +text: .text%__1cSmulL_reg_reg_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerEpush6MnITosState__v_; +text: .text%JVM_StartThread; +text: .text%__1cMthread_entry6FpnKJavaThread_pnGThread__v_: jvm.o; +text: .text%jni_GetStaticObjectField: jni.o; +text: .text%__1cJArrayDataKcell_count6M_i_: ciMethodData.o; +text: .text%__1cIGraphKitSprecision_rounding6MpnENode__2_; +text: .text%__1cNPerfByteArray2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_i_v_; +text: .text%__1cHAddress2t6Mn0AJaddr_type_i_v_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: generateOptoStub.o; +text: .text%__1cQjava_lang_ThreadIpriority6FpnHoopDesc__nOThreadPriority__; +text: .text%__1cQjava_lang_ThreadJstackSize6FpnHoopDesc__x_; +text: .text%__1cMLinkResolverYresolve_interface_method6FrnMmethodHandle_rnLKlassHandle_nSconstantPoolHandle_ipnGThread__v_; +text: .text%__1cKJavaThreadHprepare6MpnI_jobject_nOThreadPriority__v_; +text: .text%__1cIciObjectJis_method6M_i_: ciObjectFactory.o; +text: .text%__1cNloadConPCNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cTLoadD_unalignedNodeGOpcode6kM_i_; +text: .text%__1cSstkL_to_regD_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQshrI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%JVM_FreeMemory; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlass.o; +text: .text%__1cVcompiledICHolderKlassToop_follow_contents6MpnHoopDesc__v_; +text: .text%JVM_TotalMemory; +text: .text%__1cVcompiledICHolderKlassToop_adjust_pointers6MpnHoopDesc__i_; +text: .text%__1cMloadConDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciObjectFactory.o; +text: .text%__1cUmulL_reg_imm13_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCodeBufferWinsert_double_constant6Md_pC_; +text: .text%__1cTAbstractInterpreterWlayout_activation_impl6FpnNmethodOopDesc_iiiipnFframe_4i_i_; +text: .text%__1cQdivL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIciObjectOis_method_data6M_i_: ciObjectFactory.o; +text: .text%__1cOcmovIL_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2D_memNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: interpreter_sparc.o; +text: .text%__1cUdivL_reg_imm13_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cHThreadsGremove6FpnKJavaThread__v_; +text: .text%__1cIOSThread2T6M_v_; +text: .text%__1cUThreadSafepointStateHdestroy6FpnKJavaThread__v_; +text: .text%__1cKJavaThreadYremove_stack_guard_pages6M_v_; +text: .text%__1cQandI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQjava_lang_ThreadNset_stillborn6FpnHoopDesc__v_; +text: .text%__1cGParker2T6M_v_; +text: .text%__1cSandL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRInterpreterOopMapNresource_copy6MpnQOopMapCacheEntry__v_; +text: .text%__SLIP.DELETER__A: thread.o; +text: .text%__1cCosOunguard_memory6FpcI_i_; +text: .text%__1cKJavaThreadEexit6Mi_v_; +text: .text%__1cLConvD2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cIOSThreadKpd_destroy6M_v_; +text: .text%__1cWstatic_call_RelocationLstatic_stub6M_pC_; +text: .text%__1cODeoptimizationYquery_update_method_data6FnQmethodDataHandle_in0ALDeoptReason_rIri4_pnLProfileData__; +text: .text%__1cKJavaThread2T6M_v_; +text: .text%__1cGThread2T5B6M_v_; +text: .text%__1cCosLfree_thread6FpnIOSThread__v_; +text: .text%__1cFStateM_sub_Op_MulI6MpknENode__v_; +text: .text%__1cNThreadServiceNremove_thread6FpnKJavaThread_i_v_; +text: .text%__1cNThreadServiceWcurrent_thread_exiting6FpnKJavaThread__v_; +text: .text%__1cLensure_join6FpnKJavaThread__v_: thread.o; +text: .text%__1cQOopMapCacheEntryEfill6MnMmethodHandle_i_v_; +text: .text%__1cUregI_to_stkLHi_0NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_0NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSTailCalljmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOGenerateOopMapEppop6MpnNCellTypeState__v_; +text: .text%__1cUregI_to_stkLHi_1NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSTailCalljmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQsubF_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cICmpDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLconvI2BNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cRNativeMovConstRegEdata6kM_i_; +text: .text%__1cbFunnecessary_membar_volatileNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cLcmpF_ccNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNObjectMonitorJnotifyAll6MpnGThread__v_; +text: .text%jni_CallObjectMethod: jni.o; +text: .text%__1cMTailCallNode2t6MpnENode_222222_v_; +text: .text%__1cQaddD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cGEventsDlog6FpkcE_v_: deoptimization.o; +text: .text%__1cPconvD2F_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cRInlineCacheBufferUic_buffer_cached_oop6FpC_pnHoopDesc__; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNimmP_pollOperFclone6kM_pnIMachOper__; +text: .text%__1cQdivD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRtestI_reg_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_immNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerbEset_method_data_pointer_offset6MpnMRegisterImpl__v_; +text: .text%__1cSconvF2I_helperNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIMaxINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cQChunkPoolCleanerEtask6M_v_: allocation.o; +text: .text%__1cJLoadPNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cRloadConP_pollNodeFclone6kM_pnENode__; +text: .text%__1cHTypeInt2t6Miii_v_; +text: .text%__1cTOopMapForCacheEntryLcompute_map6MpnGThread__v_; +text: .text%__1cNloadConPCNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConPCNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIL_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cTOopMapForCacheEntryOreport_results6kM_i_: oopMapCache.o; +text: .text%__1cKConv2BNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cXconvI2D_regDHi_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitJpush_pair6MpnENode__v_: library_call.o; +text: .text%__1cSandL_reg_imm13NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLI_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadRangeNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQjava_lang_StringbHcreate_from_platform_depended_str6FpkcpnGThread__nGHandle__; +text: .text%__1cRshlI_reg_imm5NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorJpass_long6M_v_; +text: .text%__1cQregL_to_stkLNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapVresult_for_basicblock6Mi_v_; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: interpreterRT_sparc.o; +text: .text%__1cQOopMapCacheEntryIset_mask6MpnNCellTypeState_2i_v_; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: klassKlass.o; +text: .text%__1cLOptoRuntimeYcurrent_time_millis_Type6F_pknITypeFunc__; +text: .text%__1cHTypePtrFxdual6kM_pknEType__; +text: .text%__1cURethrowExceptionNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcastP2INodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotIOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotIOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOcmovLL_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%JVM_MonitorNotifyAll; +text: .text%__1cJloadDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOstackSlotIOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cIGraphKitXinsert_mem_bar_volatile6MpnKMemBarNode_i_v_; +text: .text%__1cKCMoveLNodeGOpcode6kM_i_; +text: .text%__1cRshlL_reg_imm6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOLibraryCallKitYinline_native_time_funcs6Mi_i_; +text: .text%__1cMnegD_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationVtrap_state_has_reason6Fii_i_; +text: .text%__1cVMoveL2D_stack_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHRetDataKcell_count6M_i_: methodDataOop.o; +text: .text%__1cTloadD_unalignedNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cNiRegIsafeOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cNloadConP0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: ciTypeFlow.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: stubGenerator_sparc.o; +text: .text%__1cSinstanceKlassKlassOklass_oop_size6kM_i_: instanceKlassKlass.o; +text: .text%__1cZInterpreterMacroAssemblerFpop_f6MpnRFloatRegisterImpl__v_; +text: .text%__1cIAddDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cMnegD_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPmethodDataKlassOklass_oop_size6kM_i_: methodDataKlass.o; +text: .text%__1cKimmL13OperFclone6kM_pnIMachOper__; +text: .text%__1cKarrayKlassWuncached_lookup_method6kMpnNsymbolOopDesc_2_pnNmethodOopDesc__; +text: .text%__1cLmethodKlassOklass_oop_size6kM_i_: methodKlass.o; +text: .text%__1cLstoreF0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cKarrayKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cWconstantPoolCacheKlassOklass_oop_size6kM_i_: cpCacheKlass.o; +text: .text%__1cQconstMethodKlassOklass_oop_size6kM_i_: constMethodKlass.o; +text: .text%__1cXJNI_ArgumentPusherVaArgJget_float6M_v_: jni.o; +text: .text%__1cKklassKlassOklass_oop_size6kM_i_: arrayKlassKlass.o; +text: .text%__1cQshlL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSobjArrayKlassKlassOklass_oop_size6kM_i_: objArrayKlassKlass.o; +text: .text%__1cLsymbolKlassOklass_oop_size6kM_i_: symbolKlass.o; +text: .text%__1cVcompiledICHolderKlassOklass_oop_size6kM_i_: compiledICHolderKlass.o; +text: .text%__1cSsubL_reg_reg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSmulL_reg_reg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSdivL_reg_reg_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRconstantPoolKlassOklass_oop_size6kM_i_: constantPoolKlass.o; +text: .text%__1cTtypeArrayKlassKlassOklass_oop_size6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cOloadI_fregNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRtestI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQjava_lang_ThreadbGinherited_access_control_context6FpnHoopDesc__2_; +text: .text%__1cJLoadSNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cbIjava_security_AccessControlContextGcreate6FnOobjArrayHandle_inGHandle_pnGThread__pnHoopDesc__; +text: .text%__1cLstoreF0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cUGenericGrowableArrayUclear_and_deallocate6M_v_; +text: .text%__1cIMinINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cNmaxI_eRegNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetInheritedAccessControlContext; +text: .text%__1cPPerfDataManagerWcreate_string_constant6FnJCounterNS_pkc3pnGThread__pnSPerfStringConstant__; +text: .text%__1cNmaxI_eRegNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%JVM_NativePath; +text: .text%__1cOMacroAssemblerNflush_windows6M_v_; +text: .text%__1cNloadConL0NodeFclone6kM_pnENode__; +text: .text%__1cSsubD_regD_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cVCallRuntimeDirectNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cFJNIidHoops_do6MpnKOopClosure__v_; +text: .text%__1cJHashtableHoops_do6MpnKOopClosure__v_; +text: .text%__1cSReferenceProcessorHoops_do6MpnKOopClosure__v_; +text: .text%__1cHCompileKinit_start6MpnJStartNode__v_; +text: .text%__1cKg3RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cVinline_cache_regPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cWloadConI_x41f00000NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstorePNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIGraphKitbAgen_stub_or_native_wrapper6MpCpkcpnIciMethod_iiiii_v_; +text: .text%__1cQObjectStartArrayFreset6M_v_; +text: .text%__1cPconvI2D_memNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsHoops_do6FpnKOopClosure__v_; +text: .text%__1cQaddD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvF2INodeGOpcode6kM_i_; +text: .text%__1cJimmL0OperFclone6kM_pnIMachOper__; +text: .text%__1cVCallRuntimeDirectNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJHashtableGunlink6MpnRBoolObjectClosure__v_; +text: .text%__1cIPSOldGenPadjust_pointers6M_v_; +text: .text%__1cVCallRuntimeDirectNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cPCallRuntimeNodeEhash6kM_I_: callnode.o; +text: .text%__1cICallNodeSis_CallInterpreter6kM_pknTCallInterpreterNode__: callnode.o; +text: .text%__1cOcmovPI_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cIPSOldGenHcompact6M_v_; +text: .text%__1cMtlsLoadPNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJvmSymbolsHoops_do6FpnKOopClosure_i_v_; +text: .text%__1cLcmpF_ccNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregL_to_stkLNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cVCallRuntimeDirectNodeKmethod_set6Mi_v_; +text: .text%__1cKimmI11OperIconstant6kM_i_: ad_sparc_clone.o; +text: .text%__1cSstkL_to_regD_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cQcmovI_reg_gtNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQstkI_to_regINodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLstoreP0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovIF_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLL_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%jni_GetStaticMethodID: jni.o; +text: .text%__1cIUniverseWreinitialize_vtable_of6FpnFKlass_pnGThread__v_; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MipnMRegisterImpl__v_; +text: .text%__1cRtestI_reg_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHnmethodbAmake_not_entrant_or_zombie6Mi_v_; +text: .text%__1cPconvF2D_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: methodDataKlass.o; +text: .text%__1cOcmovDF_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOPhaseIdealLoopKdo_peeling6MpnNIdealLoopTree_rnJNode_List__v_; +text: .text%__1cOcmovLL_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%jint_cmp: parse2.o; +text: .text%__1cXjava_lang_boxing_objectJget_value6FpnHoopDesc_pnGjvalue__nJBasicType__; +text: .text%__1cNloadConL0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerKnull_check6MpnMRegisterImpl_i_v_; +text: .text%__1cVMoveL2D_stack_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cIMulDNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cIGraphKitTdprecision_rounding6MpnENode__2_; +text: .text%__1cSconvF2I_helperNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cHnmethodbCcan_not_entrant_be_converted6M_i_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: methodDataKlass.o; +text: .text%__1cJloadCNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOloadI_fregNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcmovLL_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLConvD2FNodeGOpcode6kM_i_; +text: .text%__1cIMulFNodeImul_ring6kMpknEType_3_3_; +text: .text%__1cWloadConI_x41f00000NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKcmpOpFOperFccode6kM_i_: ad_sparc_clone.o; +text: .text%__1cLstoreC0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQregL_to_stkLNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLRuntimeStubQnew_runtime_stub6FpkcpnKCodeBuffer_ipnJOopMapSet_i_p0_; +text: .text%__1cIAddFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cLcastP2INodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKo2RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cOcmovIF_immNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovDF_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQaddL_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cZregDHi_regDLo_to_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%JVM_Close; +text: .text%__1cSmulD_regD_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKstoreFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSsubD_regD_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddD_regD_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddP_reg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cXconvI2D_regDHi_regDNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadD_unalignedNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKstoreFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPPerfDataManagerUcreate_long_constant6FnJCounterNS_pkcnIPerfDataFUnits_xpnGThread__pnQPerfLongConstant__; +text: .text%__1cOMacroAssemblerNget_vm_result6MpnMRegisterImpl__v_; +text: .text%__1cQsubF_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerbIcompute_extra_locals_size_in_bytes6MpnMRegisterImpl_22_v_; +text: .text%__1cLcmpF_ccNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPMultiBranchDataScompute_cell_count6FpnOBytecodeStream__i_; +text: .text%__1cPorI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cUregI_to_stkLHi_1NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSxorI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvI2D_memNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: templateTable_sparc.o; +text: .text%__1cQdivI_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2D_memNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cLconvI2BNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_2NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cISubFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cWloadConI_x43300000NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cWloadConI_x41f00000NodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeGis_Con6kM_I_: loopnode.o; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: templateTable_sparc.o; +text: .text%__1cSmulI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOtailjmpIndNodeNis_block_proj6kM_pknENode__: ad_sparc_misc.o; +text: .text%__1cRInlineCacheBufferSic_destination_for6FpnKCompiledIC__pC_; +text: .text%__1cENodeRraise_bottom_type6MpknEType__v_: loopnode.o; +text: .text%__1cENodeJis_MemBar6kM_pknKMemBarNode__: classes.o; +text: .text%__1cbFunnecessary_membar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJSubFPNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFParseNdo_instanceof6M_v_; +text: .text%__1cLconvI2BNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cIGraphKitOgen_instanceof6MpnENode_2_2_; +text: .text%__1cbFunnecessary_membar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRshrL_reg_imm6NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJloadBNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJloadDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQdivI_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIDivLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvI2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cSmulD_regD_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOstackSlotLOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cUregI_to_stkLHi_1NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cKConv2BNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQshlI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXjava_lang_reflect_FieldFclazz6FpnHoopDesc__2_; +text: .text%__1cXjava_lang_reflect_FieldJmodifiers6FpnHoopDesc__i_; +text: .text%__1cPconvD2F_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJloadDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOcmovPP_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cQsubF_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_NewObjectV: jni.o; +text: .text%__1cOcmovLI_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPciInstanceKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cCosHSolarisKmmap_chunk6FpcIii_2_; +text: .text%__1cXPartialSubtypeCheckNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%jni_EnsureLocalCapacity; +text: .text%__1cLstoreI0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cIAddFNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cTunsafe_intrinsic_id6FpnNsymbolOopDesc_1_nNmethodOopDescLIntrinsicId__; +text: .text%__1cLConvD2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLConvD2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKoopFactoryUnew_compiledICHolder6FnMmethodHandle_nLKlassHandle_pnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cPorL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cSCompiledStaticCallMset_to_clean6M_v_; +text: .text%__1cIDivDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cUregI_to_stkLHi_0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cVcompiledICHolderKlassIallocate6MpnGThread__pnXcompiledICHolderOopDesc__; +text: .text%__1cKVtableStubSpd_code_size_limit6Fi_i_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: instanceKlassKlass.o; +text: .text%__1cSaddD_regD_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPfieldDescriptorUdouble_initial_value6kM_d_; +text: .text%__1cQsubD_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovPP_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableGbranch6Fii_v_; +text: .text%__1cSsubL_reg_reg_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: interpreter_sparc.o; +text: .text%__1cNSafePointNodeQpeek_monitor_obj6kM_pnENode__; +text: .text%__1cJloadFNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddI_reg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSandL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cFParsePdo_monitor_exit6M_v_; +text: .text%__1cSdivL_reg_reg_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cObranchConFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cObranchConFNodeJlabel_set6MrnFLabel_I_v_; +text: .text%__1cSconvF2I_helperNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSmembar_releaseNodeIadr_type6kM_pknHTypePtr__; +text: .text%__1cOloadI_fregNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cObranchConFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPstoreI_FregNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcmpD_ccNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cJloadLNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cISubDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: stubGenerator_sparc.o; +text: .text%__1cSmulL_reg_reg_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerUprofile_taken_branch6MpnMRegisterImpl_2_v_; +text: .text%__1cHCompile2t6MpnFciEnv_pF_pknITypeFunc_pCpkciiii_v_; +text: .text%__1cLResourceObj2n6FIn0APallocation_type__pv_; +text: .text%__1cOMacroAssemblerWbang_stack_with_offset6Mi_v_: interp_masm_sparc.o; +text: .text%__1cNSafePointNodeQpeek_monitor_box6kM_pnENode__; +text: .text%__1cUregI_to_stkLHi_1NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regINodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cIGraphKitIgen_stub6MpCpkciii_v_; +text: .text%__1cNTemplateTableOpatch_bytecode6FnJBytecodesECode_pnMRegisterImpl_4i_v_; +text: .text%__1cFTypeFFxdual6kM_pknEType__; +text: .text%__1cICmpFNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: typeArrayKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlass.o; +text: .text%__1cKVtableStubRpd_code_alignment6F_i_; +text: .text%__1cSstkL_to_regD_2NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSstkL_to_regD_0NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIMachNodeOmemory_operand6kM_pknIMachOper__: ad_sparc_misc.o; +text: .text%__1cKarrayKlassYcompute_secondary_supers6MipnGThread__pnPobjArrayOopDesc__; +text: .text%__1cKloadUBNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQaddD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadL_unalignedNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cINegDNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cVMoveF2I_stack_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLConvI2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOcmovLL_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRorI_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cTloadL_unalignedNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cTloadL_unalignedNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cKloadUBNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cFMutexbLwait_for_lock_blocking_implementation6MpnKJavaThread__v_; +text: .text%__1cXconvI2D_regDHi_regDNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSvframeArrayElementPunpack_on_stack6MiipnFframe_ii_v_; +text: .text%__1cSconvF2I_helperNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerbFtest_invocation_counter_for_mdp6MpnMRegisterImpl_22rnFLabel__v_; +text: .text%__1cXconvI2D_regDHi_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSvframeArrayElementHfill_in6MpnOcompiledVFrame__v_; +text: .text%__1cFTypeDFxdual6kM_pknEType__; +text: .text%__1cSaddD_regD_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZInterpreterMacroAssemblerbAincrement_backedge_counter6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerbBtest_backedge_count_for_osr6MpnMRegisterImpl_22_v_; +text: .text%__1cSmulL_reg_imm13NodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cOcmovPI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKEntryPoint2t6M_v_; +text: .text%__1cTloadD_unalignedNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cZregDHi_regDLo_to_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcompiledVFrameImonitors6kM_pnNGrowableArray4CpnLMonitorInfo____; +text: .text%__1cOcompiledVFrameLexpressions6kM_pnUStackValueCollection__; +text: .text%__1cHciKlassOsuper_of_depth6MI_p0_; +text: .text%__1cOcompiledVFrameGlocals6kM_pnUStackValueCollection__; +text: .text%__1cOcompiledVFrameGmethod6kM_pnNmethodOopDesc__; +text: .text%__1cJimmP0OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cOcompiledVFrameHraw_bci6kM_i_; +text: .text%__1cQshrI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveL2D_stack_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cWloadConI_x43300000NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsbMis_supported_jni_version_including_1_16Fi_C_; +text: .text%__1cMTailJumpNodeKmatch_edge6kMI_I_; +text: .text%__1cSvframeArrayElementNon_stack_size6kMiiii_i_; +text: .text%__1cWloadConI_x41f00000NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cODeoptimizationbJupdate_method_data_from_interpreter6FnQmethodDataHandle_ii_v_; +text: .text%__1cIimmDOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cFframeZinterpreter_frame_set_mdx6Mi_v_; +text: .text%__1cOstackSlotLOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotLOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cTloadD_unalignedNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cTloadD_unalignedNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIModLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJimmU5OperFclone6kM_pnIMachOper__; +text: .text%__1cTAbstractInterpreterPsize_activation6FpnNmethodOopDesc_iiiii_i_; +text: .text%__1cOtailjmpIndNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cSmulD_regD_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSstkL_to_regD_0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cMloadConDNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovDF_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cTAbstractInterpreterQcontinuation_for6FpnNmethodOopDesc_pCiiri_3_; +text: .text%__1cUregI_to_stkLHi_1NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cINegFNodeGOpcode6kM_i_; +text: .text%__1cSsubD_regD_regDNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cTAbstractInterpreterRlayout_activation6FpnNmethodOopDesc_iiiipnFframe_4i_v_; +text: .text%__1cJScopeDescImonitors6M_pnNGrowableArray4CpnMMonitorValue____; +text: .text%__1cUregI_to_stkLHi_0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cJScopeDescLexpressions6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cJScopeDescGlocals6M_pnNGrowableArray4CpnKScopeValue____; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: klassKlass.o; +text: .text%JVM_GetComponentType; +text: .text%__1cQdivI_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%Unsafe_DefineClass1; +text: .text%__1cOcmovII_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLvframeArrayPunpack_to_stack6MrnFframe_i_v_; +text: .text%__1cKReflectionUarray_component_type6FpnHoopDesc_pnGThread__2_; +text: .text%__1cLConvF2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSvframeArrayElementDbci6kM_i_; +text: .text%__1cVMoveF2I_stack_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cICodeBlobZis_at_poll_or_poll_return6MpC_i_; +text: .text%__1cLvframeArrayIallocate6FpnKJavaThread_ipnNGrowableArray4CpnOcompiledVFrame___pnLRegisterMap_nFframe_9A9A9A_p0_; +text: .text%JVM_GetCPFieldModifiers; +text: .text%__1cKJavaThreadbFdeoptimized_wrt_marked_nmethods6M_v_; +text: .text%__1cLcastP2INodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNnmethodLocker2t6MpC_v_; +text: .text%__1cNSharedRuntimebJcontinuation_for_implicit_exception6FpnKJavaThread_pCn0AVImplicitExceptionKind__3_; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: deoptimization.o; +text: .text%__1cODeoptimizationNuncommon_trap6FpnKJavaThread_i_pn0ALUnrollBlock__; +text: .text%__1cNloadConL0NodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cODeoptimizationTuncommon_trap_inner6FpnKJavaThread_i_v_; +text: .text%__1cODeoptimizationScreate_vframeArray6FpnKJavaThread_nFframe_pnLRegisterMap__pnLvframeArray__; +text: .text%__1cODeoptimizationNunpack_frames6FpnKJavaThread_i_nJBasicType__; +text: .text%__1cODeoptimizationYfetch_unroll_info_helper6FpnKJavaThread__pn0ALUnrollBlock__; +text: .text%__1cZInterpreterMacroAssemblerXindex_check_without_pop6MpnMRegisterImpl_2i22_v_; +text: .text%__1cRSignatureIteratorKparse_type6M_i_; +text: .text%__1cPconvD2F_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHciKlassLjava_mirror6M_pnKciInstance__; +text: .text%__1cODeoptimizationRlast_frame_adjust6Fii_i_; +text: .text%__1cQsubD_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%JVM_DefineClass; +text: .text%JVM_InvokeMethod; +text: .text%__1cOcmovPP_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_NewDirectByteBuffer; +text: .text%__1cHJNIEnv_JNewObject6MpnH_jclass_pnK_jmethodID_E_pnI_jobject__: jni.o; +text: .text%__1cHBoxNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%jni_AllocObject: jni.o; +text: .text%__1cNTemplateTableMlocals_index6FpnMRegisterImpl_i_v_; +text: .text%__1cFStateL_sub_Op_Box6MpknENode__v_; +text: .text%__1cTmembar_volatileNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKCodeBufferVinsert_float_constant6Mf_pC_; +text: .text%__1cOMacroAssemblerCbr6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: jniFastGetField_sparc.o; +text: .text%__1cMnegD_regNodeIpipeline6kM_pknIPipeline__; +text: .text%Unsafe_AllocateInstance; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: generateOopMap.o; +text: .text%__1cQComputeCallStackHdo_byte6M_v_: generateOopMap.o; +text: .text%__1cQstkI_to_regINodeIpipeline6kM_pknIPipeline__; +text: .text%__1cYjava_lang_reflect_MethodEslot6FpnHoopDesc__i_; +text: .text%__1cYjava_lang_reflect_MethodFclazz6FpnHoopDesc__2_; +text: .text%__1cYinternal_word_RelocationGtarget6M_pC_; +text: .text%__1cYinternal_word_RelocationMforce_target6MpC_v_: relocInfo.o; +text: .text%__1cJStubQdDueueKremove_all6M_v_; +text: .text%__1cMloadConFNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cETypeJis_finite6kM_i_; +text: .text%__1cPconvI2D_memNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLconvI2BNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPorL_reg_regNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cPorL_reg_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerGif_cmp6MnJAssemblerJCondition_i_v_; +text: .text%__1cZInterpreterMacroAssemblerLindex_check6MpnMRegisterImpl_2i22_v_; +text: .text%__1cOMacroAssemblerPcasx_under_lock6MpnMRegisterImpl_22pCi_v_; +text: .text%__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_533pnGThread__v_; +text: .text%__1cQsubF_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cRshlI_reg_imm5NodeFclone6kM_pnENode__; +text: .text%__1cNloadRangeNodeFclone6kM_pnENode__; +text: .text%__1cSaddL_reg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovPI_regNodeLbottom_type6kM_pknEType__: ad_sparc_misc.o; +text: .text%__1cKstfSSFNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cMloadConFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKReflectionNinvoke_method6FpnHoopDesc_nGHandle_nOobjArrayHandle_pnGThread__2_; +text: .text%__1cYjava_lang_reflect_MethodPparameter_types6FpnHoopDesc__2_; +text: .text%__1cTmembar_volatileNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvI2L_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovII_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cYjava_lang_reflect_MethodLreturn_type6FpnHoopDesc__2_; +text: .text%__1cJCmpF3NodeGOpcode6kM_i_; +text: .text%__1cNinstanceKlassKjava_super6kM_pnMklassOopDesc__: instanceRefKlass.o; +text: .text%__1cLMoveL2DNodeGOpcode6kM_i_; +text: .text%__1cFKlassWcompute_modifier_flags6kMpnGThread__i_; +text: .text%__1cLTypeInstPtrLmirror_type6kM_pnGciType__; +text: .text%__1cOstackSlotIOperFclone6kM_pnIMachOper__; +text: .text%__1cKReflectionRreflect_new_array6FpnHoopDesc_ipnGThread__pnMarrayOopDesc__; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_nMsymbolHandle_pkcnGHandle_6_6_; +text: .text%__1cOcmovII_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOMacroAssemblerHbr_null6MpnMRegisterImpl_inJAssemblerHPredict_rnFLabel__v_; +text: .text%__1cFParseScan_rerun_bytecode6M_i_; +text: .text%__1cQshrL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKExceptionsNnew_exception6FpnGThread_pnNsymbolOopDesc_pkc_nGHandle__; +text: .text%__1cIAddFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKstfSSFNodeHis_Copy6kM_I_: ad_sparc_misc.o; +text: .text%__1cHdom_lca6FpnFBlock_1_1_: gcm.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlass.o; +text: .text%JVM_NewArray; +text: .text%__1cHOrLNodeGOpcode6kM_i_; +text: .text%__1cLStrCompNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cGThreadRis_Watcher_thread6kM_i_: thread.o; +text: .text%__1cLOopMapCache2t6M_v_; +text: .text%__1cNTemplateTableHconvert6F_v_; +text: .text%__1cOcmovDF_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cZInterpreterMacroAssemblerFpop_l6MpnMRegisterImpl__v_; +text: .text%__1cOcmovLI_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSMachBreakpointNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeQcreate_exception6FpnKJavaThread_pc3_v_; +text: .text%__1cFParseWload_interpreter_state6MpnENode_2_v_; +text: .text%__1cQComputeCallStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cKPSYoungGenKprecompact6M_v_; +text: .text%__1cXjava_lang_reflect_FieldEslot6FpnHoopDesc__i_; +text: .text%__1cSconvD2I_helperNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cMnegF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHThreadsLgc_prologue6F_v_; +text: .text%__1cHThreadsLgc_epilogue6F_v_; +text: .text%__1cPconvI2L_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPconvD2I_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4nGHandle_pnGThread__v_; +text: .text%__1cUParallelScavengeHeapHcollect6MnHGCCauseFCause__v_; +text: .text%__1cJCodeCacheMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure_iri_v_; +text: .text%__1cMStartOSRNodeScalling_convention6kMpnLRegPair_I_v_; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: callnode.o; +text: .text%__1cRCardTableModRefBSEis_a6MnKBarrierSetEName__i_: cardTableExtension.o; +text: .text%__1cRCardTableModRefBSFclear6MnJMemRegion__v_; +text: .text%__1cVLoaderConstraintTableYpurge_loader_constraints6MpnRBoolObjectClosure__v_; +text: .text%__1cVLoaderConstraintTableYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cLconvP2BNodeMideal_Opcode6kM_i_: ad_sparc_misc.o; +text: .text%__1cQSystemDictionaryValways_strong_oops_do6FpnKOopClosure__v_; +text: .text%__1cQSystemDictionaryMdo_unloading6FpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cIciMethodVget_osr_flow_analysis6Mi_pnKciTypeFlow__; +text: .text%__1cPconvD2F_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvD2I_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLMoveF2INodeGOpcode6kM_i_; +text: .text%__1cKNativeJumpUpatch_verified_entry6FpC11_v_; +text: .text%__1cSReferenceProcessorPoops_do_statics6FpnKOopClosure__v_; +text: .text%__1cMStartOSRNodeKosr_domain6F_pknJTypeTuple__; +text: .text%__1cVVM_ParallelGCSystemGCEname6kM_pkc_: vm_operations.o; +text: .text%__1cVVM_ParallelGCSystemGCEdoit6M_v_; +text: .text%__1cVVM_ParallelGCSystemGC2t6MI_v_; +text: .text%__1cJArgumentsQPropertyList_add6FppnOSystemProperty_2_v_; +text: .text%__1cOMacroAssemblerPbreakpoint_trap6M_v_; +text: .text%__1cJBasicLockHmove_to6MpnHoopDesc_p0_v_; +text: .text%__1cJMarkSweepNrestore_marks6F_v_; +text: .text%__1cJMarkSweepMadjust_marks6F_v_; +text: .text%__1cJMarkSweepXfollow_weak_klass_links6F_v_; +text: .text%__1cRStubCodeGeneratorLstub_epilog6MpnMStubCodeDesc__v_; +text: .text%__1cMStubCodeMark2t6MpnRStubCodeGenerator_pkc4_v_; +text: .text%__1cMStubCodeMark2T6M_v_; +text: .text%__1cQAbstractCompilerMsupports_osr6M_i_: c2compiler.o; +text: .text%__1cNCallGeneratorHfor_osr6FpnIciMethod_i_p0_; +text: .text%__1cLClassLoaderSget_system_package6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cJPSPermGenKprecompact6M_v_; +text: .text%JVM_GC; +text: .text%__1cIPSOldGenKprecompact6M_v_; +text: .text%__1cUPSMarkSweepDecoratorbIset_destination_decorator_perm_gen6F_v_; +text: .text%__1cUPSMarkSweepDecoratorbHset_destination_decorator_tenured6F_v_; +text: .text%__1cKDictionaryYalways_strong_classes_do6MpnKOopClosure__v_; +text: .text%__1cKDictionaryMdo_unloading6MpnRBoolObjectClosure_pnKOopClosure__i_; +text: .text%__1cLPSMarkSweepQinvoke_no_policy6Fpii_v_; +text: .text%__1cLPSMarkSweepGinvoke6Fpii_v_; +text: .text%__1cQmulL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cWloadConI_x43300000NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cUPSAdaptiveSizePolicyUmajor_collection_end6MInHGCCauseFCause__v_; +text: .text%__1cUPSAdaptiveSizePolicyWmajor_collection_begin6M_v_; +text: .text%__1cWloadConI_x41f00000NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cIUniverseWupdate_heap_info_at_gc6F_v_; +text: .text%__1cJPSPermGenQcompute_new_size6MI_v_; +text: .text%__1cKPSYoungGenHcompact6M_v_; +text: .text%JVM_GetSystemPackage; +text: .text%__1cPfieldDescriptorTfloat_initial_value6kM_f_; +text: .text%__1cKPSYoungGenPadjust_pointers6M_v_; +text: .text%__1cQUncommonTrapBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cSDeoptimizationBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cNExceptionBlobHoops_do6MpnKOopClosure__v_: codeBlob.o; +text: .text%__1cFKlassMoop_is_klass6kM_i_: objArrayKlass.o; +text: .text%__1cJCodeCacheHoops_do6FpnKOopClosure__v_; +text: .text%__1cJCodeCacheLgc_prologue6F_v_; +text: .text%__1cJCodeCacheLgc_epilogue6F_v_; +text: .text%__1cIXorINodeIadd_ring6kMpknEType_3_3_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNStubGeneratorFalign6Mi_v_: stubGenerator_sparc.o; +text: .text%__1cQregL_to_stkLNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLcastP2INodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKcmpOpFOperKless_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cOcmovPI_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cJCMoveNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQdivD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSmulL_reg_imm13NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cOcmovIF_immNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cKCMoveDNodeGOpcode6kM_i_; +text: .text%__1cJLoadDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cIMulFNodeGmul_id6kM_pknEType__: classes.o; +text: .text%__1cNStubGeneratorLstub_prolog6MpnMStubCodeDesc__v_: stubGenerator_sparc.o; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: klassKlass.o; +text: .text%__1cQaddL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%jni_GetStringRegion: jni.o; +text: .text%JVM_RawMonitorCreate; +text: .text%__1cJloadLNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMloadConFNodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cIMulFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNinstanceKlassPadd_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cMmatch_option6FpknMJavaVMOption_ppkc5i_i_: arguments.o; +text: .text%__1cNloadConPCNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotPOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotPOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotPOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cZInterpreterMacroAssemblerNunlock_object6MpnMRegisterImpl__v_; +text: .text%__1cSaddP_reg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cOstackSlotFOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%JVM_Sleep; +text: .text%__1cHBoxNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cObox_handleNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLConvL2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cLstoreF0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNinstanceKlassOset_alloc_size6MI_v_: instanceRefKlass.o; +text: .text%__1cQstkI_to_regFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQinstanceRefKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceRefKlass.o; +text: .text%__1cRorI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cVMoveF2I_stack_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOstackSlotLOperFclone6kM_pnIMachOper__; +text: .text%Unsafe_CompareAndSwapInt; +text: .text%JVM_Lseek; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: templateTable_sparc.o; +text: .text%__1cNloadRangeNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cPconvD2F_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cRComputeEntryStackJdo_object6Mii_v_: generateOopMap.o; +text: .text%__1cPconvF2D_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubL_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cQmulI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQmulF_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cSconvF2I_helperNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRmethodDataOopDescRbci_to_extra_data6Mii_pnLProfileData__; +text: .text%__1cOcmovLI_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cPMultiBranchDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cQregL_to_stkLNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQsubD_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQregP_to_stkPNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPconvI2D_memNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQaddL_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerQtest_mdp_data_at6MipnMRegisterImpl_rnFLabel_2_v_; +text: .text%__1cVMoveL2D_stack_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQstkI_to_regINodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cENodeIis_Catch6kM_pknJCatchNode__: loopnode.o; +text: .text%__1cPconvD2F_regNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLI_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOLibraryCallKitXinline_string_compareTo6M_i_; +text: .text%__1cQdivI_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cGciType2t6MnJBasicType__v_; +text: .text%__1cOMacroAssemblerKbr_notnull6MpnMRegisterImpl_inJAssemblerHPredict_rnFLabel__v_; +text: .text%__1cJLoadBNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cENodeHis_Call6M_pnICallNode__: machnode.o; +text: .text%__1cLOptoRuntimeRnew_objArray_Type6F_pknITypeFunc__; +text: .text%__1cQaddF_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cETypeEmake6Fn0AFTYPES__pk0_; +text: .text%__1cSconvF2I_helperNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cRsarL_reg_imm6NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstring_compareNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_GetEnv; +text: .text%__1cJloadDNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cQstkI_to_regINodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cSstring_compareNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cXNativeSignatureIteratorHdo_bool6M_v_: interpreterRT_sparc.o; +text: .text%Unsafe_GetNativeByte; +text: .text%JVM_NanoTime; +text: .text%__1cCosNjavaTimeNanos6F_x_; +text: .text%__1cOMacroAssemblerOrestore_thread6MkpnMRegisterImpl__v_; +text: .text%__1cVcompiledICHolderKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cQandL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIimmFOperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cHThreadsLnmethods_do6F_v_; +text: .text%__1cKcmpOpFOperGnegate6M_v_: ad_sparc_clone.o; +text: .text%__1cICodeBlobFflush6M_v_; +text: .text%__1cZInterpreterMacroAssemblerFpop_d6MpnRFloatRegisterImpl__v_; +text: .text%__1cFParseMdo_anewarray6M_v_; +text: .text%__1cSdivL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_CallVoidMethod: jni.o; +text: .text%__1cJCodeCacheFfirst6F_pnICodeBlob__; +text: .text%__1cObranchConFNodeGnegate6M_v_: ad_sparc_misc.o; +text: .text%__1cFParseOdo_tableswitch6M_v_; +text: .text%__1cOcmovIF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLConvI2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cSaddL_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLstoreC0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%Unsafe_GetNativeFloat; +text: .text%__1cOMacroAssemblerGmembar6MnJAssemblerQMembar_mask_bits__v_: templateTable_sparc.o; +text: .text%__1cNTemplateTableXjvmti_post_field_access6Fiii_v_; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: methodLiveness.o; +text: .text%__1cbCAbstractInterpreterGeneratorbBgenerate_result_handler_for6MnJBasicType__pC_; +text: .text%__1cOstackSlotFOperEtype6kM_pknEType__: ad_sparc.o; +text: .text%__1cHnmethodFflush6M_v_; +text: .text%__1cHnmethodSflush_dependencies6MpnRBoolObjectClosure__v_; +text: .text%__1cKo2RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cQregI_to_stkINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMloadConFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cbCAbstractInterpreterGeneratorVgenerate_method_entry6MnTAbstractInterpreterKMethodKind__pC_; +text: .text%__1cMregD_lowOperFclone6kM_pnIMachOper__; +text: .text%__1cJloadFNodeFclone6kM_pnENode__; +text: .text%__1cJEventMark2t6MpkcE_v_: nmethod.o; +text: .text%__1cCosNcommit_memory6FpcII_i_; +text: .text%__1cJloadLNodeFclone6kM_pnENode__; +text: .text%__1cParrayKlassKlassRoop_copy_contents6MpnSPSPromotionManager_pnHoopDesc__v_; +text: .text%__1cSaddI_reg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cFVTuneOdelete_nmethod6FpnHnmethod__v_; +text: .text%__1cLOptoRuntimeNgenerate_stub6FpnFciEnv_pF_pknITypeFunc_pCpkciiii_8_; +text: .text%__1cWloadConI_x43300000NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFParseQdo_monitor_enter6M_v_; +text: .text%__1cPorL_reg_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cLstoreC0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOPSPromotionLABRunallocate_object6MpnHoopDesc__i_; +text: .text%JVM_FindPrimitiveClass; +text: .text%__1cVMoveL2D_stack_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cNTemplateTableEiop26Fn0AJOperation__v_; +text: .text%__1cPjava_lang_ClassYcreate_basic_type_mirror6FpkcpnGThread__pnHoopDesc__; +text: .text%__1cZInterpreterMacroAssemblerMdispatch_via6MnITosState_ppC_v_; +text: .text%__1cSmodL_reg_imm13NodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cRshrI_reg_imm5NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJJavaCallsLcall_static6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cSsubL_reg_reg_2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUmulL_reg_imm13_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cIDivDNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cPconvI2F_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNinstanceKlassUfind_interface_field6kMpnNsymbolOopDesc_2pnPfieldDescriptor__pnMklassOopDesc__; +text: .text%__1cOstackSlotFOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cUdivL_reg_imm13_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_continuation_for6MnITosState__pC_; +text: .text%__1cRSignatureIteratorHiterate6M_v_; +text: .text%__1cOcmovLL_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLcastP2INodeIpipeline6kM_pknIPipeline__; +text: .text%__1cFKlassMoop_is_klass6kM_i_: constantPoolKlass.o; +text: .text%__1cJname2type6Fpkc_nJBasicType__; +text: .text%__1cSmulL_reg_imm13NodeIpipeline6kM_pknIPipeline__; +text: .text%__1cbCAbstractInterpreterGeneratorZgenerate_safept_entry_for6MnITosState_pC_2_; +text: .text%__1cLcastP2INodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPBytecode_invokeLresult_type6kMpnGThread__nJBasicType__; +text: .text%__1cLCastP2INodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cOloadConL13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSmodL_reg_imm13NodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFTypeFJis_finite6kM_i_; +text: .text%__1cKcmpOpFOperHgreater6kM_i_: ad_sparc_clone.o; +text: .text%__1cIDivDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOMacroAssemblerKget_thread6M_v_; +text: .text%__1cPconvI2F_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovDF_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovIF_immNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSconvI2F_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSaddD_regD_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKVtableStub2n6FIi_pv_; +text: .text%__1cNloadConPCNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWloadConI_x41f00000NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKScopeValueLis_location6kM_i_: debugInfo.o; +text: .text%__1cLVtableStubsLcreate_stub6FiipnNmethodOopDesc__pC_; +text: .text%__1cLOptoRuntimebBhandle_wrong_method_ic_miss6FpnKJavaThread__pC_; +text: .text%__1cSmulD_regD_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2T6M_v_; +text: .text%__1cZregDHi_regDLo_to_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSsubD_regD_regDNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLVtableStubsOis_entry_point6FpC_i_; +text: .text%__1cOtypeArrayKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlass.o; +text: .text%__1cPconvD2F_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cIciMethodMnative_entry6M_pC_; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cLVtableStubsScreate_vtable_stub6Fii_pnKVtableStub__; +text: .text%__1cVMoveF2I_stack_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cPPerfDataManagerWcreate_string_variable6FnJCounterNS_pkci3pnGThread__pnSPerfStringVariable__; +text: .text%__1cQAbstractCompilerPsupports_native6M_i_: c2compiler.o; +text: .text%__1cPorL_reg_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvD2F_regNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cIciSymbolHas_utf86M_pkc_; +text: .text%__1cQandI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQciTypeArrayKlass2t6MnLKlassHandle__v_; +text: .text%__1cMnegD_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cFStateO_sub_Op_CMoveP6MpknENode__v_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: typeArrayKlass.o; +text: .text%__1cSconvF2I_helperNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulD_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_1NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerZtotal_frame_size_in_bytes6Mi_i_; +text: .text%__1cNTemplateTableQfast_accessfield6FnITosState__v_; +text: .text%__1cKCompiledICOis_megamorphic6kM_i_; +text: .text%__1cVMoveF2I_stack_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cKCompiledICSset_to_megamorphic6MpnICallInfo_nJBytecodesECode_pnGThread__v_; +text: .text%Unsafe_StaticFieldOffset; +text: .text%__1cQmulI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableXresolve_cache_and_index6FipnMRegisterImpl_2_v_; +text: .text%__1cQaddI_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLI_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%JVM_GetClassContext; +text: .text%__1cHCompile2t6MpnFciEnv_pnKC2Compiler_pnIciMethod__v_; +text: .text%Unsafe_StaticFieldBaseFromField; +text: .text%Unsafe_EnsureClassInitialized; +text: .text%__1cOcmovIF_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pCi_v_; +text: .text%__1cKCodeBufferQalloc_relocation6MI_v_; +text: .text%__1cNTemplateTableZjvmti_post_fast_field_mod6F_v_; +text: .text%__1cWloadConI_x43300000NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_0NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%Unsafe_GetObjectVolatile; +text: .text%__1cFKlassPoop_is_instance6kM_i_: objArrayKlassKlass.o; +text: .text%__1cbEJvmtiDynamicCodeEventCollector2t6M_v_; +text: .text%__1cKstoreFNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cFKlassMoop_is_array6kM_i_: objArrayKlassKlass.o; +text: .text%__1cVMoveL2D_stack_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cJLoadLNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cSmulL_reg_imm13NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHMulNodeGis_Mul6kM_pk0_: classes.o; +text: .text%__1cNSharedRuntimeVhandle_ic_miss_helper6FpnKJavaThread_pnGThread__nMmethodHandle__; +text: .text%__1cOloadConL13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cKLoadPCNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTablePfast_storefield6FnITosState__v_; +text: .text%__1cGEventsDlog6FpkcE_v_: compiledIC.o; +text: .text%__1cLstoreF0NodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cPconvI2D_memNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cZInterpreterMacroAssemblerNsuper_call_VM6MpnMRegisterImpl_22pC22i_v_; +text: .text%__1cNloadConPCNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cETypeFxdual6kM_pk0_; +text: .text%__1cJOopMapSetQsingular_oop_map6M_pnGOopMap__; +text: .text%__1cKimmU13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cbCAbstractInterpreterGeneratorXbang_stack_shadow_pages6Mi_v_; +text: .text%__1cZInterpreterMacroAssemblerSnotify_method_exit6MinITosState__v_; +text: .text%__1cSaddL_reg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerTnotify_method_entry6M_v_; +text: .text%__1cZInterpreterMacroAssemblerbCincrement_invocation_counter6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerQaccess_local_int6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerbDunlock_if_synchronized_method6MnITosState_ii_v_; +text: .text%__1cOtypeArrayKlassMcreate_klass6FnJBasicType_ipnGThread__pnMklassOopDesc__; +text: .text%__1cIGraphKitSgen_native_wrapper6MpnIciMethod__v_; +text: .text%__1cPconvI2L_regNodeFclone6kM_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerWempty_expression_stack6M_v_; +text: .text%__1cUInterpreterGeneratorVgenerate_counter_incr6MpnFLabel_22_v_; +text: .text%__1cUInterpreterGeneratorZgenerate_counter_overflow6MirnFLabel__v_; +text: .text%__1cOcmovIL_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUInterpreterGeneratorbAgenerate_run_compiled_code6M_v_; +text: .text%__1cOCompilerThreadSis_Compiler_thread6kM_i_: thread.o; +text: .text%__1cUInterpreterGeneratorUgenerate_fixed_frame6Mi_v_; +text: .text%__1cCosHSolarisSset_signal_handler6Fiii_v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkci_pc_; +text: .text%__1cLOptoRuntimeRresolve_call_Type6F_pknITypeFunc__; +text: .text%__1cOtailjmpIndNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNMemoryManagerIadd_pool6MpnKMemoryPool__v_; +text: .text%__1cCosEstat6FpkcpnEstat__i_; +text: .text%__1cQregF_to_stkINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRComputeEntryStackIdo_short6M_v_: generateOopMap.o; +text: .text%__1cRComputeEntryStackGdo_int6M_v_: generateOopMap.o; +text: .text%__1cMMonitorChunk2t6Mi_v_; +text: .text%__1cQSystemDictionaryPresolve_or_null6FnMsymbolHandle_pnGThread__pnMklassOopDesc__; +text: .text%__1cOPhaseIdealLoopJclone_iff6MpnHPhiNode_pnNIdealLoopTree__pnIBoolNode__; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: callnode.o; +text: .text%__1cKReflectionVis_same_class_package6FpnMklassOopDesc_2_i_; +text: .text%__1cQComputeCallStackIdo_float6M_v_: generateOopMap.o; +text: .text%__1cMMonitorValue2t6MpnTDebugInfoReadStream__v_; +text: .text%__1cPciObjArrayKlassJmake_impl6FpnHciKlass__p0_; +text: .text%__1cPorL_reg_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cLOptoRuntimeMrethrow_Type6F_pknITypeFunc__; +text: .text%__1cUBytecode_tableswitchGlength6M_i_: methodDataOop.o; +text: .text%jni_SetStaticObjectField: jni.o; +text: .text%jni_RegisterNatives: jni.o; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: templateTable_sparc.o; +text: .text%__1cFframebLprevious_monitor_in_interpreter_frame6kMpnPBasicObjectLock__2_; +text: .text%__1cQshlL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_GetClassDeclaredFields; +text: .text%__1cCosMuser_handler6F_pv_; +text: .text%JVM_IsSameClassPackage; +text: .text%__1cLas_TosState6FnJBasicType__nITosState__: interpreter.o; +text: .text%__1cKMemoryPoolLadd_manager6MpnNMemoryManager__v_; +text: .text%__1cKJavaThreadRadd_monitor_chunk6MpnMMonitorChunk__v_; +text: .text%__1cKJavaThreadUremove_monitor_chunk6MpnMMonitorChunk__v_; +text: .text%__1cLcastP2INodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVMoveL2D_stack_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableGiconst6Fi_v_; +text: .text%__1cLConvF2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%JVM_LoadLibrary; +text: .text%JVM_IsSupportedJNIVersion; +text: .text%Unsafe_ObjectFieldOffset; +text: .text%__1cZInterpreterMacroAssemblerYtest_method_data_pointer6MrnFLabel__v_; +text: .text%__1cNTemplateTableHif_0cmp6Fn0AJCondition__v_; +text: .text%__1cHCompileRget_Method_invoke6M_pnIciMethod__; +text: .text%__1cZInterpreterMacroAssemblerSget_cpool_and_tags6MpnMRegisterImpl_2_v_; +text: .text%__1cIAddDNodeIIdentity6MpnOPhaseTransform__pnENode__: classes.o; +text: .text%__1cHCompileWget_MethodAccessorImpl6M_pnPciInstanceKlass__; +text: .text%__1cNTemplateTableHif_icmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableH_return6FnITosState__v_; +text: .text%__1cOPSVirtualSpaceJexpand_by6MI_i_; +text: .text%__1cHOrLNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKimmP13OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cLConvD2FNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSObjectSynchronizerJjni_enter6FnGHandle_pnGThread__v_; +text: .text%__1cHnmethodbJcontinuation_for_implicit_exception6MpC_1_; +text: .text%__1cNSharedRuntimeEdrem6Fdd_d_; +text: .text%__1cQstkI_to_regINodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cUInterpreterGeneratorbEgenerate_asm_interpreter_entry6Mi_pC_; +text: .text%__1cSstkL_to_regD_2NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_1NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cIAddDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cSstkL_to_regD_0NodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cPstoreI_FregNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cTloadD_unalignedNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLVtableStubsIcontains6FpC_i_; +text: .text%__1cOloadI_fregNodeOmemory_operand6kM_pknIMachOper__; +text: .text%__1cLconvP2BNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cUInterpreterGeneratorbCgenerate_check_compiled_code6MrnFLabel__v_; +text: .text%__1cUInterpreterGeneratorbDgenerate_stack_overflow_check6MpnMRegisterImpl_22_v_; +text: .text%__1cCosZvm_allocation_granularity6F_i_; +text: .text%__1cMTailJumpNodeGOpcode6kM_i_; +text: .text%__1cPconvF2D_regNodeFclone6kM_pnENode__; +text: .text%__1cOLibraryCallKitbDis_method_invoke_or_aux_frame6MpnIJVMState__i_; +text: .text%__1cTloadD_unalignedNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHciKlass2t6MnLKlassHandle_pnIciSymbol__v_; +text: .text%__1cJMemRegion2t6M_v_: cardTableModRefBS.o; +text: .text%__1cSestimate_path_freq6FpnENode__f_: loopnode.o; +text: .text%__1cCosOreserve_memory6FIpc_1_; +text: .text%__1cSObjectSynchronizerIjni_exit6FpnHoopDesc_pnGThread__v_; +text: .text%__1cSmulL_reg_imm13NodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNRegisterSaverWrestore_live_registers6FpnOMacroAssembler__v_; +text: .text%__1cKstfSSFNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLTypeInstPtrOxmeet_unloaded6kMpk0_2_; +text: .text%__1cENodeHis_AddP6M_pnIAddPNode__: subnode.o; +text: .text%__1cRtestI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cCosNcommit_memory6FpcI_i_; +text: .text%__1cPPerfLongVariant2t6MnJCounterNS_pkcnIPerfDataFUnits_n0CLVariability_pnUPerfLongSampleHelper__v_; +text: .text%__1cWImplicitExceptionTable2t6MpknHnmethod__v_; +text: .text%__1cWImplicitExceptionTableCat6kMI_I_; +text: .text%__1cFParseVcatch_call_exceptions6MrnYciExceptionHandlerStream__v_; +text: .text%jni_GetJavaVM; +text: .text%__1cOcmovDF_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%jni_MonitorEnter: jni.o; +text: .text%__1cQConstantIntValuePis_constant_int6kM_i_: debugInfo.o; +text: .text%jni_MonitorExit: jni.o; +text: .text%__1cOMacroAssemblerDret6Mi_v_: templateTable_sparc.o; +text: .text%__1cLConvL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cULinearLeastSquareFit2t6MI_v_; +text: .text%__1cQdivL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cPciObjectFactoryTget_unloaded_method6MpnPciInstanceKlass_pnIciSymbol_4_pnIciMethod__; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciObjArrayKlass.o; +text: .text%__1cOLibraryCallKitbBinline_native_currentThread6M_i_; +text: .text%__1cIciObjectMis_classless6kM_i_: ciMethod.o; +text: .text%__1cTGeneratePairingInfoOreport_results6kM_i_: ciMethod.o; +text: .text%__1cNReservedSpaceKfirst_part6MIii_0_; +text: .text%__1cNReservedSpace2t6MI_v_; +text: .text%__1cSCardTableExtensionVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cOloadI_fregNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRCardTableModRefBSVresize_covered_region6MnJMemRegion__v_; +text: .text%__1cIAddDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cJloadFNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKConv2BNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvI2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cSconvD2I_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%jni_Throw: jni.o; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC_v_; +text: .text%__1cFTypeFGis_nan6kM_i_; +text: .text%__1cLMoveL2DNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cKReturnNodeUdepends_only_on_test6kM_i_: classes.o; +text: .text%__1cIDivINodeJideal_reg6kM_I_: classes.o; +text: .text%__1cISubDNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cPstoreI_FregNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cINegFNodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cQciByteCodeStreamXget_method_holder_index6M_i_; +text: .text%__1cOLibraryCallKitXgenerate_current_thread6MrpnENode__2_; +text: .text%__1cOMacroAssemblerEfneg6MnRFloatRegisterImplFWidth_p13_v_; +text: .text%__1cXNativeSignatureIteratorJdo_double6M_v_: interpreterRT_sparc.o; +text: .text%__1cRtestI_reg_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNSpaceCounters2t6MpkciIpnMMutableSpace_pnSGenerationCounters__v_; +text: .text%__1cLcmpF_ccNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMNativeLookupTbase_library_lookup6Fpkc22_pC_; +text: .text%jni_SetObjectField: jni.o; +text: .text%__1cISubDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cISubFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cPPerfDataManagerUcreate_long_variable6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnQPerfLongVariable__; +text: .text%__1cHThreadsDadd6FpnKJavaThread_i_v_; +text: .text%__1cPPerfDataManagerKname_space6Fpkc2i_pc_; +text: .text%__1cNStubGeneratorYgenerate_throw_exception6MpkcpCi_3_: stubGenerator_sparc.o; +text: .text%bootstrap_flush_windows; +text: .text%__1cMloadConPNodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cSdivL_reg_reg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerbCverify_oop_or_return_address6MpnMRegisterImpl_2_v_; +text: .text%__1cFStateO_sub_Op_Conv2B6MpknENode__v_; +text: .text%__1cQmodL_reg_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cNRegisterSaverTsave_live_registers6FpnOMacroAssembler_ipi_pnGOopMap__; +text: .text%__1cSmulL_reg_reg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: symbolKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: symbolKlass.o; +text: .text%__1cIDivFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cKMemoryPool2t6Mpkcn0AIPoolType_IIii_v_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_int_field06FnJBasicType__pC_; +text: .text%__1cKExceptionsK_throw_oop6FpnGThread_pkcipnHoopDesc__v_; +text: .text%__1cSsubL_reg_reg_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSInterpreterRuntimeZSignatureHandlerGeneratorLpass_double6M_v_; +text: .text%__1cPciInstanceKlassbDcompute_shared_is_initialized6M_i_; +text: .text%__1cQmulD_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%Unsafe_AllocateMemory; +text: .text%__1cSandL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_GetLastErrorString; +text: .text%__1cQmodL_reg_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableElop26Fn0AJOperation__v_; +text: .text%__1cQjava_lang_ThreadKset_daemon6FpnHoopDesc__v_; +text: .text%__1cNTemplateTableEfop26Fn0AJOperation__v_; +text: .text%__1cPstoreI_FregNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cNTemplateTableEdop26Fn0AJOperation__v_; +text: .text%__1cSandI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNTemplateTablebAload_invoke_cp_cache_entry6FipnMRegisterImpl_22ii_v_; +text: .text%__1cMnegD_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNciMethodKlassEmake6F_p0_; +text: .text%__1cOLibraryCallKitZinline_native_Class_query6MnIciMethodLIntrinsicId__i_; +text: .text%__1cNTemplateTableGlstore6Fi_v_; +text: .text%__1cOLibraryCallKitbNinline_native_Reflection_getCallerClass6M_i_; +text: .text%__1cLConvF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cQaddI_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cPBytecode_invokeIis_valid6kM_i_: frame.o; +text: .text%__1cIciMethod2t6MpnPciInstanceKlass_pnIciSymbol_4_v_; +text: .text%__1cRcompL_reg_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cLconvI2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cLConvD2FNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_2_v_; +text: .text%__1cSconvD2I_helperNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cPconvI2D_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsubI_zero_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cLClassLoaderXcreate_class_path_entry6FpcnEstat_ppnOClassPathEntry__v_; +text: .text%__1cKstfSSFNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cOClassPathEntry2t6M_v_; +text: .text%__1cZInterpreterMacroAssemblerRprofile_checkcast6MipnMRegisterImpl_2_v_; +text: .text%__1cINegDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cZInterpreterMacroAssemblerQaccess_local_ptr6MpnMRegisterImpl_2_v_; +text: .text%__1cOloadConL13NodeFclone6kM_pnENode__; +text: .text%__1cNTemplateTableGistore6Fi_v_; +text: .text%__1cIRetTableUfind_jsrs_for_target6Mi_pnNRetTableEntry__; +text: .text%__1cPconvL2I_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cUcompI_iReg_imm13NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cRsarI_reg_imm5NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cSstkL_to_regD_2NodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableGastore6Fi_v_; +text: .text%__1cNTemplateTableZload_field_cp_cache_entry6FipnMRegisterImpl_22i_v_; +text: .text%__1cOMacroAssemblerMload_address6MrnHAddress_i_v_: assembler_sparc.o; +text: .text%__1cIRetTableHadd_jsr6Mii_v_; +text: .text%__1cMnegF_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulI_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cQregF_to_stkINodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cRComputeEntryStackHdo_bool6M_v_: generateOopMap.o; +text: .text%__1cPorL_reg_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cQmulD_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cNTemplateTableGdstore6Fi_v_; +text: .text%__1cNTemplateTableGfstore6Fi_v_; +text: .text%jni_CallStaticObjectMethod: jni.o; +text: .text%__1cPconvD2F_regNodeFclone6kM_pnENode__; +text: .text%__1cbCAbstractInterpreterGeneratorbHgenerate_exception_handler_common6Mpkc2i_pC_; +text: .text%__1cLconvP2BNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cLconvP2BNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cOcmovLL_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cQandI_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cMnegD_regNodeFclone6kM_pnENode__; +text: .text%__1cOMacroAssemblerJfloat_cmp6MiipnRFloatRegisterImpl_2pnMRegisterImpl__v_; +text: .text%__1cLconvI2BNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cODeoptimizationLUnrollBlockOsize_of_frames6kM_i_; +text: .text%__1cFKlassNoop_is_symbol6kM_i_: instanceRefKlass.o; +text: .text%__1cCosGsignal6Fipv_1_; +text: .text%__1cQaddD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cISubDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cISubFNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cISubFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNflagsRegLOperFclone6kM_pnIMachOper__; +text: .text%__1cNTemplateTableFlload6Fi_v_; +text: .text%__1cNTemplateTableFiload6Fi_v_; +text: .text%__1cJcmpOpOperFclone6kM_pnIMachOper__; +text: .text%__1cMOopMapStream2t6MpnGOopMap_i_v_; +text: .text%__1cFTypeFFempty6kM_i_; +text: .text%__1cLconvP2BNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cVMoveF2I_stack_regNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC22_v_; +text: .text%__1cOtypeArrayKlassQarray_klass_impl6MipnGThread__pnMklassOopDesc__; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC2_v_; +text: .text%__1cTjava_lang_ThrowableLset_message6FpnHoopDesc_2_v_; +text: .text%__1cKstfSSFNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cOGenerateOopMapTret_jump_targets_do6MpnOBytecodeStream_pFp0ipi_vi4_v_; +text: .text%__1cPconvI2D_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%Unsafe_SetMemory; +text: .text%__1cSstkL_to_regD_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKstfSSFNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cZInterpreterMacroAssemblerOthrow_if_not_x6MnJAssemblerJCondition_pCpnMRegisterImpl__v_; +text: .text%__1cVMoveF2I_stack_regNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cHTypePtrKadd_offset6kMi_pk0_; +text: .text%__1cOcmovLI_regNodeHsize_of6kM_I_: ad_sparc_misc.o; +text: .text%__1cNloadConL0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cKg1RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cOcmovPI_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovDF_regNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cNTemplateTableJfloat_cmp6Fii_v_; +text: .text%__1cQsubF_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstkL_to_regD_1NodeFclone6kM_pnENode__; +text: .text%__1cFParseRjump_if_true_fork6MpnGIfNode_ii_v_; +text: .text%__1cZInterpreterMacroAssemblerQthrow_if_not_icc6MnJAssemblerJCondition_pCpnMRegisterImpl__v_; +text: .text%__1cNTemplateTableFfload6Fi_v_; +text: .text%__1cFParsePdo_lookupswitch6M_v_; +text: .text%__1cNTemplateTableFdload6Fi_v_; +text: .text%__1cNgen_new_frame6FpnOMacroAssembler_i_v_: runtime_sparc.o; +text: .text%__1cKstfSSFNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cINegDNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTableFaload6Fi_v_; +text: .text%__1cRMachSpillCopyNodeHsize_of6kM_I_: ad_sparc.o; +text: .text%__1cQCompilerCounters2t6MpkcipnGThread__v_; +text: .text%__1cOGenerateOopMapRdo_multianewarray6Mii_v_; +text: .text%__1cQOopMapCacheEntryPfill_for_native6M_v_; +text: .text%__1cNCompileBrokerUcompiler_thread_loop6F_v_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%jni_CallStaticObjectMethodV: jni.o; +text: .text%__1cNTemplateTableMfast_xaccess6FnITosState__v_; +text: .text%__1cIDivDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMloadConDNodeGis_Con6kM_I_: ad_sparc_misc.o; +text: .text%__1cJMemRegionFminus6kMk0_0_; +text: .text%__1cNCompileBrokerUmake_compiler_thread6FpkcpnMCompileQdDueue_pnQCompilerCounters_pnGThread__pnOCompilerThread__; +text: .text%__1cJArgumentsMbuild_string6Fppcpkc_v_; +text: .text%__1cSInterpreterRuntimebKthrow_ArrayIndexOutOfBoundsException6FpnKJavaThread_pci_v_; +text: .text%__1cOtailjmpIndNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cNMemoryManager2t6M_v_; +text: .text%__1cFStatebB_sub_Op_PartialSubtypeCheck6MpknENode__v_; +text: .text%__1cOMacroAssemblerLstore_check6MpnMRegisterImpl_22_v_; +text: .text%__1cVMoveL2D_stack_regNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cJArgumentsMadd_property6Fpkc_i_; +text: .text%__1cOtailjmpIndNodeHtwo_adr6kM_I_: ad_sparc_misc.o; +text: .text%__1cSObjectSynchronizerHinflate6FpnHoopDesc__pnNObjectMonitor__; +text: .text%__1cFStateM_sub_Op_DivI6MpknENode__v_; +text: .text%__1cCosHSolarisOis_sig_ignored6Fi_i_; +text: .text%__1cUPSGenerationCounters2t6MpkciipnOPSVirtualSpace__v_; +text: .text%__1cCosFyield6F_v_; +text: .text%__1cLOptoRuntimeVgenerate_handler_blob6FpCi_pnNSafepointBlob__; +text: .text%__1cKstfSSFNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cOMacroAssemblerDset6MipnMRegisterImpl_rknQRelocationHolder__v_: runtime_sparc.o; +text: .text%__1cQsubD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cXNativeSignatureIteratorIdo_float6M_v_: interpreterRT_sparc.o; +text: .text%__1cOtailjmpIndNodeGpinned6kM_i_: ad_sparc_misc.o; +text: .text%__1cIDivDNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cFParseRdo_multianewarray6M_v_; +text: .text%__1cLOptoRuntimeTmultianewarray_Type6Fi_pknITypeFunc__; +text: .text%__1cZInterpreterMacroAssemblerRget_constant_pool6MpnMRegisterImpl__v_; +text: .text%__1cXPartialSubtypeCheckNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cOcmovIF_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNFingerprinterLfingerprint6M_X_: oopMapCache.o; +text: .text%__1cLMoveF2INodeLbottom_type6kM_pknEType__: classes.o; +text: .text%__1cSconvI2D_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveF2I_stack_regNodeZcheck_for_anti_dependence6kM_i_: ad_sparc_misc.o; +text: .text%__1cLstoreF0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerLlock_object6MpnMRegisterImpl_2_v_; +text: .text%__1cPstoreI_FregNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cSCommandLineFlagsExJboolAtPut6FnXCommandLineFlagWithType_i_v_; +text: .text%__1cSCommandLineFlagsExKis_default6FnPCommandLineFlag__i_; +text: .text%__1cOcmovLL_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerUupdate_mdp_by_offset6MpnMRegisterImpl_i2_v_; +text: .text%__1cRNativeInstructionPis_ic_miss_trap6M_i_; +text: .text%__1cNSafepointBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cMciArrayKlassRbase_element_type6M_pnGciType__; +text: .text%JVM_GetInterfaceVersion; +text: .text%__1cZInterpreterMacroAssemblerRgen_subtype_check6MpnMRegisterImpl_2222rnFLabel__v_; +text: .text%__1cbFpartialSubtypeCheck_vs_zeroNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableGfconst6Fi_v_; +text: .text%__1cOMacroAssemblerDbrx6MnJAssemblerJCondition_in0BHPredict_rnFLabel__v_: stubGenerator_sparc.o; +text: .text%__1cOCompilerThreadbCis_hidden_from_external_view6kM_i_: thread.o; +text: .text%__1cGThreadbFinitialize_thread_local_storage6M_v_; +text: .text%__1cOcmovPI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cGThreadbArecord_stack_base_and_size6M_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC2_v_; +text: .text%JVM_RegisterSignal; +text: .text%JVM_FindSignal; +text: .text%__1cTMaskFillerForNative2t6MnMmethodHandle_pIi_v_: oopMapCache.o; +text: .text%jio_vsnprintf; +text: .text%__1cQshrL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cZInterpreterMacroAssemblerVincrement_mdp_data_at6MpnMRegisterImpl_i22_v_; +text: .text%__1cZInterpreterMacroAssemblerTprofile_switch_case6MpnMRegisterImpl_222_v_; +text: .text%__1cNReservedSpaceJlast_part6MI_0_; +text: .text%__1cOCompilerThread2t6MpnMCompileQdDueue_pnQCompilerCounters__v_; +text: .text%__1cOPSVirtualSpace2t6MnNReservedSpace_I_v_; +text: .text%__1cFTypeDFempty6kM_i_; +text: .text%__1cVcompiler_thread_entry6FpnKJavaThread_pnGThread__v_: thread.o; +text: .text%__1cNIdealLoopTreeUmerge_many_backedges6MpnOPhaseIdealLoop__v_; +text: .text%__1cODeoptimizationLUnrollBlock2T6M_v_; +text: .text%jni_GetDoubleArrayRegion: jni.o; +text: .text%__1cHciKlassIis_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cIciObjectTis_type_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cMLinkResolverbBlookup_method_in_interfaces6FrnMmethodHandle_nLKlassHandle_nMsymbolHandle_4pnGThread__v_; +text: .text%__1cLconvP2BNodeErule6kM_I_: ad_sparc_misc.o; +text: .text%__1cKfix_parent6FpnNIdealLoopTree_1_v_: loopnode.o; +text: .text%__1cZInterpreterMacroAssemblerUadd_monitor_to_stack6MipnMRegisterImpl_2_v_; +text: .text%JVM_Available; +text: .text%__1cZInterpreterMacroAssemblerSprofile_final_call6MpnMRegisterImpl__v_; +text: .text%__1cQshlL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMOopTaskQdDueueKinitialize6M_v_; +text: .text%__1cMOopTaskQdDueue2t6M_v_; +text: .text%__1cRNativeInstructionKis_illegal6M_i_; +text: .text%__1cZInterpreterMacroAssemblerQtop_most_monitor6M_nHAddress__; +text: .text%__1cLstoreF0NodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cCosGgetenv6Fpkcpci_i_; +text: .text%__1cMVM_OperationNdoit_prologue6M_i_: vm_operations.o; +text: .text%__1cZInterpreterMacroAssemblerWprofile_switch_default6MpnMRegisterImpl__v_; +text: .text%__1cKi0RegPOperJnum_edges6kM_I_: ad_sparc.o; +text: .text%__1cTAbstract_VM_VersionOvm_info_string6F_pkc_; +text: .text%__1cPOopTaskQdDueueSetOregister_queue6MipnMOopTaskQdDueue__v_; +text: .text%__1cMVM_OperationNdoit_epilogue6M_v_: vm_operations.o; +text: .text%__1cNVM_DeoptimizeEname6kM_pkc_: vm_operations.o; +text: .text%__1cJStubQdDueue2t6MpnNStubInterface_ipnFMutex_pkc_v_; +text: .text%__1cSconvF2I_helperNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNloadConP0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cHThreadsbFdeoptimized_wrt_marked_nmethods6F_v_; +text: .text%__1cbAconvL2D_reg_slow_fxtofNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cOstackSlotFOperEdisp6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cNTemplateTableSputfield_or_static6Fii_v_; +text: .text%__1cOstackSlotFOperEbase6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cOstackSlotFOperFindex6kMpnNPhaseRegAlloc_pknENode_i_i_: ad_sparc.o; +text: .text%__1cPconvF2I_regNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cNTemplateTableSgetfield_or_static6Fii_v_; +text: .text%__1cNTemplateTableUjvmti_post_field_mod6Fii_v_; +text: .text%__1cHMatcherQconvL2FSupported6F_ki_; +text: .text%__1cNTemplateTableGlconst6Fi_v_; +text: .text%__1cLstoreC0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cRcompL_reg_regNodeFclone6kM_pnENode__; +text: .text%__1cUcompI_iReg_imm13NodeFclone6kM_pnENode__; +text: .text%__1cMPeriodicTaskGenroll6M_v_; +text: .text%__1cMPeriodicTask2t6MI_v_; +text: .text%__1cPconvF2I_regNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cFTypeDJis_finite6kM_i_; +text: .text%__1cPconvL2I_regNodeFclone6kM_pnENode__; +text: .text%__1cNTemplateTableHcastore6F_v_; +text: .text%Unsafe_CompareAndSwapObject; +text: .text%__1cLNamedThread2t6M_v_; +text: .text%__1cSconvD2I_helperNodeFclone6kM_pnENode__; +text: .text%__1cLNamedThreadIset_name6MpkcE_v_; +text: .text%__1cJloadDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQdivD_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWloadConI_x43300000NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableKinitialize6F_v_; +text: .text%__1cKcmpOpFOperJnot_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cPconvD2F_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableKdouble_cmp6Fi_v_; +text: .text%__1cNTemplateTableJfloat_cmp6Fi_v_; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC22_v_; +text: .text%__1cNTemplateTableGdconst6Fi_v_; +text: .text%__1cNTemplateTableDldc6Fi_v_; +text: .text%__1cSconvF2I_helperNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovIF_immNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovIF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: universe.o; +text: .text%__1cJimmL0OperJnum_edges6kM_I_: ad_sparc_clone.o; +text: .text%__1cLcastP2INodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovLL_regNodeFclone6kM_pnENode__; +text: .text%__1cbAconvL2D_reg_slow_fxtofNodePoper_input_base6kM_I_: ad_sparc_misc.o; +text: .text%__1cLconvP2BNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cSaddD_regD_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSsubD_regD_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQregF_to_stkINodeFclone6kM_pnENode__; +text: .text%__1cSstkL_to_regD_2NodeFclone6kM_pnENode__; +text: .text%__1cQregF_to_stkINodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNTemplateTableTinvokevfinal_helper6FpnMRegisterImpl_2_v_; +text: .text%__1cSmulD_regD_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableUgenerate_vtable_call6FpnMRegisterImpl_22_v_; +text: .text%__1cSstkL_to_regD_2NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cSstkL_to_regD_0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cNTemplateTableKif_nullcmp6Fn0AJCondition__v_; +text: .text%__1cNTemplateTableHif_acmp6Fn0AJCondition__v_; +text: .text%__1cSmembar_releaseNodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cJimmI0OperFclone6kM_pnIMachOper__; +text: .text%__1cNVM_DeoptimizeEdoit6M_v_; +text: .text%__1cNloadConL0NodeJnum_opnds6kM_I_: ad_sparc_misc.o; +text: .text%__1cUregI_to_stkLHi_0NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMnegF_regNodeFclone6kM_pnENode__; +text: .text%__1cQsubL_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cMVirtualSpace2t6M_v_; +text: .text%__1cRsarI_reg_imm5NodeFclone6kM_pnENode__; +text: .text%__1cWloadConI_x41f00000NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQdivI_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cMVirtualSpaceKinitialize6MnNReservedSpace_I_i_; +text: .text%__1cZregDHi_regDLo_to_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cRsubI_zero_regNodeFclone6kM_pnENode__; +text: .text%__1cXconvI2D_regDHi_regDNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cKloadUBNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cNTemplateTableEidiv6F_v_; +text: .text%__1cOcmovLI_regNodeFclone6kM_pnENode__; +text: .text%__1cQstkI_to_regINodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUregI_to_stkLHi_1NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cLMoveL2DNodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLConvD2FNodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLConvF2INodeIIdentity6MpnOPhaseTransform__pnENode__; +text: .text%__1cLConvF2INodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_float_field06FnJBasicType__pC_; +text: .text%__1cLMoveF2INodeFValue6kMpnOPhaseTransform__pknEType__; +text: .text%__1cLOptoRuntimeIl2f_Type6F_pknITypeFunc__; +text: .text%__1cOMacroAssemblerUcalc_mem_param_words6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerLprofile_ret6MnITosState_pnMRegisterImpl_3_v_; +text: .text%__1cZInterpreterMacroAssemblerUprofile_virtual_call6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerMprofile_call6MpnMRegisterImpl__v_; +text: .text%__1cLklassVtableQindex_of_miranda6MpnNsymbolOopDesc_2_i_; +text: .text%__1cZInterpreterMacroAssemblerSupdate_mdp_for_ret6MnITosState_pnMRegisterImpl__v_; +text: .text%__1cOLibraryCallKitVinline_fp_conversions6MnIciMethodLIntrinsicId__i_; +text: .text%__1cZInterpreterMacroAssemblerPset_mdp_flag_at6MipnMRegisterImpl_2_v_; +text: .text%__1cMLinkResolverbEvtable_index_of_miranda_method6FnLKlassHandle_nMsymbolHandle_2pnGThread__i_; +text: .text%__1cUInterpreterGeneratorLlock_method6M_v_; +text: .text%__1cZInterpreterMacroAssemblerOthrow_if_not_26MpCpnMRegisterImpl_rnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerQthrow_if_not_1_x6MnJAssemblerJCondition_rnFLabel__v_; +text: .text%__1cZInterpreterMacroAssemblerZget_4_byte_integer_at_bcp6MipnMRegisterImpl_2n0AKsetCCOrNot__v_; +text: .text%__1cUInterpreterGeneratorVgenerate_native_entry6Mi_pC_; +text: .text%__1cCosHrealloc6FpvI_1_; +text: .text%__1cUConstantOopReadValuePis_constant_oop6kM_i_: debugInfo.o; +text: .text%__1cKScopeValuePis_constant_int6kM_i_: debugInfo.o; +text: .text%__1cODeoptimizationVdeoptimize_dependents6F_i_; +text: .text%__1cKklassKlassMoop_is_klass6kM_i_: arrayKlassKlass.o; +text: .text%__1cFStateO_sub_Op_CMoveL6MpknENode__v_; +text: .text%__1cZInterpreterMacroAssemblerRaccess_local_long6MpnMRegisterImpl_2_v_; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cIPSOldGenPinitialize_work6Mpkci_v_; +text: .text%__1cCosIjvm_path6Fpci_v_; +text: .text%__1cCosNsigexitnum_pd6F_i_; +text: .text%__1cCosScurrent_process_id6F_i_; +text: .text%__1cINegFNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cMciArrayKlassOis_array_klass6M_i_: ciTypeArrayKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: objArrayKlassKlass.o; +text: .text%__1cCosPuncommit_memory6FpcI_i_; +text: .text%__1cSInterpreterRuntimeMat_safepoint6FpnKJavaThread__v_; +text: .text%__1cLConvL2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvF2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLConvD2FNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cKJNIHandlesQmake_weak_global6FnGHandle__pnI_jobject__; +text: .text%__1cZInterpreterMacroAssemblerSaccess_local_float6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerTaccess_local_double6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerPstore_local_int6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerPstore_local_ptr6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerQstore_local_long6MpnMRegisterImpl_2_v_; +text: .text%__1cZInterpreterMacroAssemblerRstore_local_float6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cZInterpreterMacroAssemblerSstore_local_double6MpnMRegisterImpl_pnRFloatRegisterImpl__v_; +text: .text%__1cCosWactive_processor_count6F_i_; +text: .text%__1cSReferenceProcessor2t6MnJMemRegion_iii_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: jniFastGetField_sparc.o; +text: .text%__1cRcheck_if_clipping6FpknKRegionNode_rpnGIfNode_5_i_: cfgnode.o; +text: .text%__1cTAbstractInterpreterKinitialize6F_v_; +text: .text%__1cIciObjectOis_array_klass6M_i_: ciInstanceKlass.o; +text: .text%__1cIciObjectOis_null_object6kM_i_: ciInstanceKlass.o; +text: .text%__1cPmake_new_frames6FpnOMacroAssembler_i_v_: runtime_sparc.o; +text: .text%jni_NewWeakGlobalRef: jni.o; +text: .text%__1cGatomll6Fpkcpx_i_: arguments.o; +text: .text%__1cRComputeEntryStackIdo_array6Mii_v_: generateOopMap.o; +text: .text%__1cTMaskFillerForNativeLpass_object6M_v_: oopMapCache.o; +text: .text%__1cOGenerateOopMapTadd_to_ref_init_set6Mi_v_; +text: .text%__1cUGcThreadCountClosureJdo_thread6MpnGThread__v_; +text: .text%__1cNinstanceKlassSremove_osr_nmethod6MpnHnmethod__v_; +text: .text%__1cOPSVirtualSpace2t6M_v_; +text: .text%jni_IsInstanceOf: jni.o; +text: .text%__1cGThreadMis_VM_thread6kM_i_: gcTaskThread.o; +text: .text%__1cXNativeSignatureIteratorHdo_long6M_v_: oopMapCache.o; +text: .text%__1cMGCTaskThreadDrun6M_v_; +text: .text%__1cJCodeCachebGmake_marked_nmethods_not_entrant6F_v_; +text: .text%__1cTMaskFillerForNativeJpass_long6M_v_: oopMapCache.o; +text: .text%jni_CallStaticVoidMethodV: jni.o; +text: .text%__1cKCodeBufferGresize6M_v_; +text: .text%jni_CallStaticBooleanMethod: jni.o; +text: .text%__1cMGCTaskThread2t6MpnNGCTaskManager_II_v_; +text: .text%__1cOtailjmpIndNodeIpipeline6kM_pknIPipeline__; +text: .text%__1cMGCTaskThreadFstart6M_v_; +text: .text%__1cNStubGenerator2t6MpnKCodeBuffer_i_v_: stubGenerator_sparc.o; +text: .text%__1cWStubGenerator_generate6FpnKCodeBuffer_i_v_; +text: .text%__1cRFloatRegisterImplIencoding6kMn0AFWidth__i_: interpreter_sparc.o; +text: .text%__1cCosbCis_thread_cpu_time_supported6F_i_; +text: .text%__1cJArgumentsXPropertyList_unique_add6FppnOSystemProperty_pkcpc_v_; +text: .text%__1cQObjectStartArrayKinitialize6MnJMemRegion__v_; +text: .text%__1cQObjectStartArraySset_covered_region6MnJMemRegion__v_; +text: .text%__1cUGenericGrowableArrayGgrow646Mi_v_; +text: .text%__1cZInterpreterMacroAssemblerbAdispatch_next_noverify_oop6MnITosState_i_v_; +text: .text%__1cOMacroAssemblerDret6Mi_v_: stubGenerator_sparc.o; +text: .text%__1cRCollectorCounters2t6Mpkci_v_; +text: .text%__1cFParseDl2f6M_v_; +text: .text%__1cPGCMemoryManagerXinitialize_gc_stat_info6M_v_; +text: .text%__1cJArgumentsVset_parallel_gc_flags6F_v_; +text: .text%__1cPGCMemoryManager2t6M_v_; +text: .text%__1cRComputeEntryStackHdo_long6M_v_: generateOopMap.o; +text: .text%__1cOPSVirtualSpaceKinitialize6MnNReservedSpace_I_i_; +text: .text%__1cSInterpreterRuntimeWcreate_klass_exception6FpnKJavaThread_pcpnHoopDesc__v_; +text: .text%__1cQcreate_os_thread6FpnGThread_I_pnIOSThread__: os_solaris.o; +text: .text%__1cYjava_lang_reflect_MethodPcompute_offsets6F_v_; +text: .text%__1cSInterpreterRuntimeSupdate_mdp_for_ret6FpnKJavaThread_i_v_; +text: .text%__1cPorL_reg_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQjava_lang_ThreadPcompute_offsets6F_v_; +text: .text%__1cXNativeSignatureIteratorHdo_byte6M_v_: interpreterRT_sparc.o; +text: .text%__1cCosHSolarisQsignal_sets_init6F_v_; +text: .text%__1cCosScreate_main_thread6FpnGThread__i_; +text: .text%__1cCosbDallocate_thread_local_storage6F_i_; +text: .text%__1cUInterpreterGeneratorVrestore_native_result6M_v_; +text: .text%__1cVjava_lang_ThreadGroupPcompute_offsets6F_v_; +text: .text%__1cHMatcherbDinterpreter_frame_pointer_reg6F_nHOptoRegEName__; +text: .text%__1cLconvP2BNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVshrL_reg_imm6_L2INodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cJJavaCallsMcall_special6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_; +text: .text%__1cCosGstrdup6Fpkc_pc_; +text: .text%__1cbCAbstractInterpreterGeneratorYgenerate_throw_exception6M_v_; +text: .text%__1cCosLinit_random6Fl_v_; +text: .text%__1cCosNset_boot_path6Fcc_i_; +text: .text%__1cUInterpreterGeneratorXgenerate_accessor_entry6M_pC_; +text: .text%__1cCosXis_server_class_machine6F_i_; +text: .text%__1cCosXterminate_signal_thread6F_v_; +text: .text%__1cCosLsignal_init6F_v_; +text: .text%__1cKTypeOopPtrCeq6kMpknEType__i_; +text: .text%__1cTsignal_thread_entry6FpnKJavaThread_pnGThread__v_: os.o; +text: .text%__1cOtailjmpIndNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cUInterpreterGeneratorUgenerate_empty_entry6M_pC_; +text: .text%__1cUInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cbCAbstractInterpreterGeneratorbMgenerate_ArrayIndexOutOfBounds_handler6Mpkc_pC_; +text: .text%__1cbCAbstractInterpreterGeneratorbJgenerate_StackOverflowError_handler6M_pC_; +text: .text%__1cCosbDinit_system_properties_values6F_v_; +text: .text%__1cCosPphysical_memory6F_X_; +text: .text%__1cHvm_exit6Fi_v_; +text: .text%__1cLbefore_exit6FpnKJavaThread__v_; +text: .text%__1cbCAbstractInterpreterGeneratorbFgenerate_slow_signature_handler6M_pC_; +text: .text%__1cSThreadLocalStorageHpd_init6F_v_; +text: .text%__1cVMoveF2I_stack_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVMoveL2D_stack_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cWinvocationCounter_init6F_v_; +text: .text%__1cRInvocationCounterMreinitialize6Fi_v_; +text: .text%__1cKTypeOopPtrEmake6FnHTypePtrDPTR_i_pk0_; +text: .text%__1cKTypeOopPtrFxdual6kM_pknEType__; +text: .text%__1cFParseMjump_if_join6MpnENode_2_2_; +text: .text%__1cSinstanceKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cSinstanceKlassKlassUoop_is_instanceKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: instanceKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: instanceKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: instanceKlassKlass.o; +text: .text%__1cSinstanceKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: instanceKlassKlass.o; +text: .text%__1cLconvP2BNodeGExpand6MpnFState_rnJNode_List__pnIMachNode__; +text: .text%__1cETypeRInitialize_shared6FpnHCompile__v_; +text: .text%__1cQinstanceRefKlassZupdate_nonstatic_oop_maps6FpnMklassOopDesc__v_; +text: .text%__1cVInterfaceSupport_init6F_v_; +text: .text%__1cZInterpreterMacroAssemblerSsuper_call_VM_leaf6MpnMRegisterImpl_pC2_v_; +text: .text%__1cFParseNfetch_monitor6MipnENode_2_2_; +text: .text%__1cPGenerationSizerQinitialize_flags6M_v_: parallelScavengeHeap.o; +text: .text%__1cbCTwoGenerationCollectorPolicyMrem_set_name6M_nJGenRemSetEName__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapEkind6M_nNCollectedHeapEName__: parallelScavengeHeap.o; +text: .text%__1cZInterpreterMacroAssemblerPdispatch_normal6MnITosState__v_; +text: .text%__1cJTimeStampMmilliseconds6kM_x_; +text: .text%__1cDhpiZinitialize_socket_library6F_i_; +text: .text%__1cDhpiYinitialize_get_interface6FpnIvm_calls__v_; +text: .text%__1cWInlineCacheBuffer_init6F_v_; +text: .text%__1cWThreadLocalAllocBufferWstartup_initialization6F_v_; +text: .text%__1cPGlobalTLABStats2t6M_v_; +text: .text%__1cLicache_init6F_v_; +text: .text%__1cIGraphKitRcreate_and_map_if6MpnENode_2ff_pnGIfNode__: parse1.o; +text: .text%__1cSThreadLocalStorageEinit6F_v_; +text: .text%__1cNThreadServiceEinit6F_v_; +text: .text%__1cTICacheStubGeneratorVgenerate_icache_flush6MppFpCii_i_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: indexSet.o; +text: .text%__1cPvm_init_globals6F_v_; +text: .text%__1cMinit_globals6F_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_sparc_expand.o; +text: .text%__1cMexit_globals6F_v_; +text: .text%__1cSset_init_completed6F_v_; +text: .text%__1cNinstanceKlassZrelease_C_heap_structures6M_v_; +text: .text%__1cJTimeStampJupdate_to6Mx_v_; +text: .text%__1cUParallelScavengeHeapItop_addr6kM_ppnIHeapWord__: parallelScavengeHeap.o; +text: .text%__1cOisT2_libthread6F_i_; +text: .text%__1cCosHSolarisXinstall_signal_handlers6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interp_masm_sparc.o; +text: .text%__1cQinterpreter_init6F_v_; +text: .text%__1cbCAbstractInterpreterGenerator2t6MpnJStubQdDueue__v_; +text: .text%__1cRlwp_priocntl_init6F_i_: os_solaris.o; +text: .text%__1cNpriocntl_stub6FinGidtype_lipc_l_: os_solaris.o; +text: .text%__1cbCAbstractInterpreterGeneratorMgenerate_all6M_v_; +text: .text%__1cbCAbstractInterpreterGeneratorbEset_entry_points_for_all_bytes6M_v_; +text: .text%__1cCosHSolarisRmpss_sanity_check6F_v_; +text: .text%__1cCosLsignal_wait6F_i_; +text: .text%__1cVcheck_pending_signals6Fi_i_: os_solaris.o; +text: .text%__1cCosNsignal_notify6Fi_v_; +text: .text%__1cCosOsignal_init_pd6F_v_; +text: .text%__1cCosHSolarisPinit_signal_mem6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: interpreter.o; +text: .text%__1cCosSget_temp_directory6F_pkc_; +text: .text%__1cCosHSolarisOlibthread_init6F_v_; +text: .text%__1cUParallelScavengeHeapIend_addr6kM_ppnIHeapWord__: parallelScavengeHeap.o; +text: .text%__1cUParallelScavengeHeapEheap6F_p0_; +text: .text%__1cUParallelScavengeHeapNgc_threads_do6kMpnNThreadClosure__v_; +text: .text%__1cUParallelScavengeHeapYpermanent_object_iterate6MpnNObjectClosure__v_; +text: .text%__1cKcmpOpFOperNgreater_equal6kM_i_: ad_sparc_clone.o; +text: .text%__1cUParallelScavengeHeapMmax_capacity6kM_I_; +text: .text%__1cUParallelScavengeHeapPpost_initialize6M_v_; +text: .text%__1cUParallelScavengeHeapKinitialize6M_i_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: parGCAllocBuffer.o; +text: .text%__1cZInterpreterMacroAssemblerbFset_method_data_pointer_for_bcp6M_v_; +text: .text%__SLIP.DELETER__C: ostream.o; +text: .text%__1cMostream_exit6F_v_; +text: .text%__1cQostream_init_log6F_v_; +text: .text%__1cMostream_init6F_v_; +text: .text%__1cCosXnon_memory_address_word6F_pc_; +text: .text%__1cCosGinit_26F_i_; +text: .text%__1cCosEinit6F_v_; +text: .text%__1cCosHSolarisUsynchronization_init6F_v_; +text: .text%__1cVjni_GetLongField_addr6F_pC_; +text: .text%__1cNIdealLoopTreeQsplit_outer_loop6MpnOPhaseIdealLoop__v_; +text: .text%__1cRLowMemoryDetectorKinitialize6F_v_; +text: .text%__1cNReservedSpace2t6MIIipc_v_; +text: .text%__1cRLowMemoryDetectorbGlow_memory_detector_thread_entry6FpnKJavaThread_pnGThread__v_; +text: .text%__1cXLowMemoryDetectorThreadbCis_hidden_from_external_view6kM_i_: lowMemoryDetector.o; +text: .text%__1cNReservedSpaceUpage_align_size_down6FI_I_; +text: .text%__1cNReservedSpaceYallocation_align_size_up6FI_I_; +text: .text%__1cKJavaThreadOis_Java_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cGThreadMis_VM_thread6kM_i_: lowMemoryDetector.o; +text: .text%__1cTloadL_unalignedNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: machnode.o; +text: .text%__1cPmanagement_init6F_v_; +text: .text%__1cOvmStructs_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vmStructs.o; +text: .text%__1cJvmSymbolsKinitialize6FpnGThread__v_; +text: .text%__1cKManagementKinitialize6FpnGThread__v_; +text: .text%__1cKManagementWrecord_vm_startup_time6Fxx_v_; +text: .text%__1cIVMThreadGcreate6F_v_; +text: .text%__1cIVMThreadDrun6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: management.o; +text: .text%__1cOBasicHashtable2t6Mii_v_: loaderConstraints.o; +text: .text%__1cLJvmtiExportNpost_vm_start6F_v_; +text: .text%__1cLJvmtiExportTpost_vm_initialized6F_v_; +text: .text%__1cLJvmtiExportNpost_vm_death6F_v_; +text: .text%__1cLJvmtiExportbMtransition_pending_onload_raw_monitors6F_v_; +text: .text%__1cUJvmtiPendingMonitorsXtransition_raw_monitors6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiImpl.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiTagMap.o; +text: .text%__1cKklassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cFKlassUoop_is_instanceKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassVoop_is_typeArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: klassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: klassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: klassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: klassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: klassKlass.o; +text: .text%__1cKklassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: klassKlass.o; +text: .text%__1cOLibraryCallKitWinline_native_hashcode6Mii_i_; +text: .text%__1cIGraphKitTcreate_and_xform_if6MpnENode_2ff_pnGIfNode__: library_call.o; +text: .text%__1cVLoaderConstraintTable2t6Mi_v_; +text: .text%__1cQregL_to_stkLNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cHRetDataPpost_initialize6MpnOBytecodeStream_pnRmethodDataOopDesc__v_; +text: .text%__1cTAbstract_VM_VersionKvm_release6F_pkc_; +text: .text%__1cTAbstract_VM_VersionXinternal_vm_info_string6F_pkc_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: vm_version.o; +text: .text%__1cPVM_Version_init6F_v_; +text: .text%__1cKVM_VersionKinitialize6F_v_; +text: .text%__1cHRetDataJfixup_ret6MinQmethodDataHandle__pC_; +text: .text%__1cHRetDataKis_RetData6M_i_: methodDataOop.o; +text: .text%__1cLmethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cLmethodKlassOset_alloc_size6MI_v_: methodKlass.o; +text: .text%__1cQvtableStubs_init6F_v_; +text: .text%__1cKi0RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cKg1RegPOperKin_RegMask6kMi_pknHRegMask__; +text: .text%__1cFVTuneEexit6F_v_; +text: .text%__1cLmethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodKlass.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: methodLiveness.o; +text: .text%__1cMMutableSpaceOobject_iterate6MpnNObjectClosure__v_; +text: .text%__1cKvtune_init6F_v_; +text: .text%__1cKmutex_init6F_v_; +text: .text%__1cQaccessFlags_init6F_v_; +text: .text%__1cOMacroAssemblerMcall_VM_leaf6MpnMRegisterImpl_pC222_v_; +text: .text%__1cTAbstract_VM_VersionJvm_vendor6F_pkc_; +text: .text%__1cOmarksweep_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: markSweep.o; +text: .text%__1cHMatcherVfind_callee_arguments6FpnNsymbolOopDesc_ipi_pnLRegPair__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: matcher.o; +text: .text%__1cNMemoryManagerbDget_code_cache_memory_manager6F_p0_; +text: .text%__1cNMemoryManagerbDget_psScavenge_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cNMemoryManagerbEget_psMarkSweep_memory_manager6F_pnPGCMemoryManager__; +text: .text%__1cQPSGenerationPool2t6MpnIPSOldGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cQPSGenerationPool2t6MpnJPSPermGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cUEdenMutableSpacePool2t6MpnKPSYoungGen_pnMMutableSpace_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cYSurvivorMutableSpacePool2t6MpnKPSYoungGen_pkcnKMemoryPoolIPoolType_i_v_; +text: .text%__1cMCodeHeapPool2t6MpnICodeHeap_pkci_v_; +text: .text%__1cHVM_ExitEdoit6M_v_; +text: .text%__1cHVM_ExitEname6kM_pkc_: vm_operations.o; +text: .text%__1cNMemoryServiceRset_universe_heap6FpnNCollectedHeap__v_; +text: .text%__1cNMemoryServiceZadd_code_heap_memory_pool6FpnICodeHeap__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: memoryService.o; +text: .text%__1cPmethodDataKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cPmethodDataKlassOset_alloc_size6MI_v_: methodDataKlass.o; +text: .text%__1cPmethodDataKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: methodDataKlass.o; +text: .text%__1cTAbstract_VM_VersionHvm_name6F_pkc_; +text: .text%__1cLstoreF0NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%JNI_CreateJavaVM; +text: .text%__1cXonStackReplacement_init6F_v_; +text: .text%__1cQJNI_FastGetFieldbFgenerate_fast_get_boolean_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_byte_field6F_pC_; +text: .text%__1cTtypeArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cTtypeArrayKlassKlassVoop_is_typeArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassUoop_is_objArrayKlass6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassQoop_is_typeArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassNoop_is_method6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_char_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_short_field6F_pC_; +text: .text%__1cFKlassPoop_is_objArray6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassMoop_is_array6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: typeArrayKlassKlass.o; +text: .text%__1cQJNI_FastGetFieldbBgenerate_fast_get_int_field6F_pC_; +text: .text%__1cQJNI_FastGetFieldbCgenerate_fast_get_long_field6F_pC_; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: typeArrayKlassKlass.o; +text: .text%__1cTtypeArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: typeArrayKlassKlass.o; +text: .text%__1cIUniversePcheck_alignment6FIIpkc_v_; +text: .text%__1cIUniverseHgenesis6FpnGThread__v_; +text: .text%__1cVquicken_jni_functions6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: oopMap.o; +text: .text%__1cYjava_lang_reflect_MethodNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cbDjava_lang_reflect_ConstructorPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldPcompute_offsets6F_v_; +text: .text%__1cXjava_lang_reflect_FieldNset_signature6FpnHoopDesc_2_v_; +text: .text%__1cQdivD_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cLJavaClassesbAcompute_hard_coded_offsets6F_v_; +text: .text%__1cQjavaClasses_init6F_v_; +text: .text%jni_ToReflectedMethod: jni.o; +text: .text%__1cQsubD_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cYjni_GetBooleanField_addr6F_pC_; +text: .text%__1cVjni_GetByteField_addr6F_pC_; +text: .text%__1cQaddF_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cVjni_GetCharField_addr6F_pC_; +text: .text%__1cWjni_GetShortField_addr6F_pC_; +text: .text%__1cUjni_GetIntField_addr6F_pC_; +text: .text%__1cOtypeArrayKlassKinitialize6MpnGThread__v_; +text: .text%__1cWjni_GetFloatField_addr6F_pC_; +text: .text%__1cRsarL_reg_imm6NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cXjni_GetDoubleField_addr6F_pC_; +text: .text%__1cQshlI_reg_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cIUniverseNfixup_mirrors6FpnGThread__v_; +text: .text%__1cFKlassRoop_is_methodData6kM_i_: objArrayKlass.o; +text: .text%JVM_InitializeSocketLibrary; +text: .text%JVM_RegisterUnsafeMethods; +text: .text%__1cOcmovLI_regNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cOcmovLI_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cOcmovDF_regNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%JVM_Socket; +text: .text%__1cbEinitialize_converter_functions6F_v_; +text: .text%JVM_SupportsCX8; +text: .text%__1cOcmovIF_immNodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEnvBase.o; +text: .text%__1cUJvmtiEventControllerIvm_start6F_v_; +text: .text%__1cUJvmtiEventControllerHvm_init6F_v_; +text: .text%__1cUJvmtiEventControllerIvm_death6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: jvmtiEventController.o; +text: .text%__1cKstfSSFNodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cVverificationType_init6F_v_; +text: .text%__1cVverificationType_exit6F_v_; +text: .text%__1cLJvmtiExportRenter_start_phase6F_v_; +text: .text%__1cLJvmtiExportQenter_live_phase6F_v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlass.o; +text: .text%__1cSmulL_reg_imm13NodeEemit6kMrnKCodeBuffer_pnNPhaseRegAlloc__v_; +text: .text%__1cQJNI_FastGetFieldbDgenerate_fast_get_float_field6F_pC_; +text: .text%__1cSmulI_reg_imm13NodeEsize6kMpnNPhaseRegAlloc__I_; +text: .text%__1cQJNI_FastGetFieldbEgenerate_fast_get_double_field6F_pC_; +text: .text%__1cNuniverse_init6F_i_; +text: .text%__1cOuniverse2_init6F_v_; +text: .text%__1cSuniverse_post_init6F_v_; +text: .text%__1cQjni_handles_init6F_v_; +text: .text%__1cSobjArrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: objArrayKlassKlass.o; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: objArrayKlassKlass.o; +text: .text%Unsafe_SetNativeLong; +text: .text%JVM_InitProperties; +text: .text%JVM_Halt; +text: .text%__1cFKlassNoop_is_method6kM_i_: objArrayKlassKlass.o; +text: .text%Unsafe_FreeMemory; +text: .text%Unsafe_PageSize; +text: .text%__1cSobjArrayKlassKlassUoop_is_objArrayKlass6kM_i_: objArrayKlassKlass.o; +text: .text%JVM_MaxMemory; +text: .text%__1cSobjArrayKlassKlassbEallocate_system_objArray_klass6MpnGThread__pnMklassOopDesc__; +text: .text%__1cSobjArrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%JVM_GetClassDeclaredMethods; +text: .text%__1cPPerfDataManagerHsampled6F_pnMPerfDataList__; +text: .text%__1cQSystemDictionaryKclasses_do6FpFpnMklassOopDesc__v_v_; +text: .text%__1cQSystemDictionaryKinitialize6FpnGThread__v_; +text: .text%__1cQSystemDictionarybCinitialize_preloaded_classes6FpnGThread__v_; +text: .text%__1cQSystemDictionarybDinitialize_basic_type_mirrors6FpnGThread__v_; +text: .text%__1cPciObjectFactoryTinit_shared_objects6M_v_; +text: .text%__1cRciArrayKlassKlassUis_array_klass_klass6M_i_: ciObjectFactory.o; +text: .text%__1cPClassFileParserbFjava_lang_ref_Reference_fix_pre6MpnPtypeArrayHandle_nSconstantPoolHandle_pnUFieldAllocationCount_pnGThread__v_; +text: .text%__1cLClassLoaderbBsetup_bootstrap_search_path6F_v_; +text: .text%__1cLClassLoaderQload_zip_library6F_v_; +text: .text%__1cLClassLoaderZcreate_package_info_table6F_v_; +text: .text%__1cLClassLoaderKinitialize6F_v_; +text: .text%__1cLClassLoaderVcompute_Object_vtable6F_i_; +text: .text%__1cMPeriodicTask2T5B6M_v_; +text: .text%__1cQclassLoader_init6F_v_; +text: .text%__1cMPeriodicTaskJdisenroll6M_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: classLoader.o; +text: .text%__1cTClassLoadingServiceEinit6F_v_; +text: .text%__1cTClassLoadingServiceVnotify_class_unloaded6FpnNinstanceKlass_i_v_; +text: .text%__1cMFastLockNodeLis_FastLock6kM_pk0_: classes.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: regmask.o; +text: .text%__1cUciObjArrayKlassKlassEmake6F_p0_; +text: .text%__1cLOptoRuntimeUmultianewarray1_Type6F_pknITypeFunc__; +text: .text%__1cVRegistersForDebuggingRrestore_registers6FpnOMacroAssembler_pnMRegisterImpl__v_: assembler_sparc.o; +text: .text%__1cVRegistersForDebuggingOsave_registers6FpnOMacroAssembler__v_: assembler_sparc.o; +text: .text%__1cJBytecodesKinitialize6F_v_; +text: .text%__1cQSystemDictionarybAcompute_java_system_loader6FpnGThread__v_; +text: .text%__1cObytecodes_init6F_v_; +text: .text%__1cLOptoRuntimeIgenerate6FpnFciEnv__v_; +text: .text%__1cJBytecodesNpd_initialize6F_v_; +text: .text%__1cHCompileRpd_compiler2_init6F_v_; +text: .text%__1cKC2CompilerKinitialize6M_v_; +text: .text%__1cMTailJumpNode2t6MpnENode_22222_v_; +text: .text%__1cRCardTableModRefBS2t6MnJMemRegion_i_v_; +text: .text%__1cWResolveOopMapConflictsOreport_results6kM_i_: rewriter.o; +text: .text%__1cRCardTableModRefBSbBct_max_alignment_constraint6F_I_; +text: .text%__1cENodeMis_SafePoint6M_pnNSafePointNode__: cfgnode.o; +text: .text%__1cMciArrayKlass2t6MpnIciSymbol_ipnHciKlass__v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: relocInfo.o; +text: .text%__1cMciKlassKlassEmake6F_p0_; +text: .text%__1cIciMethodMvtable_index6M_i_; +text: .text%__1cPciObjArrayKlass2t6MpnIciSymbol_pnHciKlass_i_v_; +text: .text%__1cJLoadFNodeMstore_Opcode6kM_i_: classes.o; +text: .text%__1cNTemplateTableGsipush6F_v_; +text: .text%__1cQUncommonTrapBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNTemplateTableGldc2_w6F_v_; +text: .text%__1cNExceptionBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_i_p0_; +text: .text%__1cNTemplateTableFiload6F_v_; +text: .text%__1cNTemplateTableLfast_iload26F_v_; +text: .text%__1cNTemplateTableKfast_iload6F_v_; +text: .text%__1cNTemplateTableFlload6F_v_; +text: .text%__1cNTemplateTableFfload6F_v_; +text: .text%__1cNTemplateTableFdload6F_v_; +text: .text%__1cNTemplateTableFaload6F_v_; +text: .text%__1cNTemplateTableKwide_iload6F_v_; +text: .text%__1cNTemplateTableKwide_lload6F_v_; +text: .text%__1cNTemplateTableKwide_fload6F_v_; +text: .text%__1cNTemplateTableKwide_dload6F_v_; +text: .text%__1cNTemplateTableKwide_aload6F_v_; +text: .text%__1cNTemplateTableGiaload6F_v_; +text: .text%__1cNTemplateTableGlaload6F_v_; +text: .text%__1cNTemplateTableGfaload6F_v_; +text: .text%__1cNTemplateTableGdaload6F_v_; +text: .text%__1cNTemplateTableGbipush6F_v_; +text: .text%__1cLMoveF2INodeJideal_reg6kM_I_: classes.o; +text: .text%__1cLMoveL2DNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTableHcall_VM6FpnMRegisterImpl_pC222_v_; +text: .text%__1cHOrLNodeGadd_id6kM_pknEType__: classes.o; +text: .text%__1cHOrLNodeJideal_reg6kM_I_: classes.o; +text: .text%__1cNTemplateTableF_goto6F_v_; +text: .text%__1cNTemplateTableGgoto_w6F_v_; +text: .text%__1cNTemplateTableFjsr_w6F_v_; +text: .text%__1cNTemplateTableDjsr6F_v_; +text: .text%__1cXreferenceProcessor_init6F_v_; +text: .text%__1cICodeBlobMset_oop_maps6MpnJOopMapSet__v_; +text: .text%__1cStemplateTable_init6F_v_; +text: .text%__1cNTemplateTableNpd_initialize6F_v_; +text: .text%__1cURecompilationMonitorbGstart_recompilation_monitor_task6F_v_; +text: .text%__1cNTemplateTableDnop6F_v_; +text: .text%__1cNTemplateTableSshouldnotreachhere6F_v_; +text: .text%__1cNTemplateTableLaconst_null6F_v_; +text: .text%__1cKPSYoungGenbCreset_survivors_after_shrink6M_v_; +text: .text%__1cKPSYoungGenQlimit_gen_shrink6MI_I_; +text: .text%__1cKPSYoungGenRavailable_to_live6M_I_; +text: .text%__1cSDeoptimizationBlobGcreate6FpnKCodeBuffer_pnJOopMapSet_iiii_p0_; +text: .text%__1cLOptoRuntimeUmultianewarray2_Type6F_pknITypeFunc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: ad_sparc_pipeline.o; +text: .text%__1cNcarSpace_init6F_v_; +text: .text%__1cUAdjoiningGenerations2t6MnNReservedSpace_IIIIIII_v_; +text: .text%__1cWAdjoiningVirtualSpaces2t6MnNReservedSpace_III_v_; +text: .text%__1cOchunkpool_init6F_v_; +text: .text%__1cFChunkbDstart_chunk_pool_cleaner_task6F_v_; +text: .text%__1cJArgumentsWinit_system_properties6F_v_; +text: .text%__1cINegFNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cMSysClassPathPexpand_endorsed6M_v_; +text: .text%__1cMSysClassPathQadd_jars_to_path6Fpcpkc_1_; +text: .text%__1cJArgumentsVprocess_settings_file6Fpkcii_i_; +text: .text%__1cJArgumentsTset_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsbBset_cms_and_parnew_gc_flags6F_v_; +text: .text%__1cJArgumentsUset_ergonomics_flags6F_v_; +text: .text%__1cJArgumentsZcheck_vm_args_consistency6F_i_; +text: .text%__1cJArgumentsSparse_vm_init_args6FpknOJavaVMInitArgs__i_; +text: .text%__1cJArgumentsWparse_each_vm_init_arg6FpknOJavaVMInitArgs_pnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsVfinalize_vm_init_args6FpnMSysClassPath_i_i_; +text: .text%__1cLStatSamplerGengage6F_v_; +text: .text%__1cNStubGeneratorbNgenerate_flush_callers_register_windows6M_pC_: stubGenerator_sparc.o; +text: .text%__1cSstubRoutines_init16F_v_; +text: .text%__1cSstubRoutines_init26F_v_; +text: .text%__1cHAddressQrspec_from_rtype6MnJrelocInfoJrelocType_pC_nQRelocationHolder__: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerIjumpl_to6MrnHAddress_pnMRegisterImpl_i_v_: stubGenerator_sparc.o; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbIgenerate_handler_for_unsafe_access6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbAgenerate_forward_exception6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorSgenerate_call_stub6MrpC_1_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorYgenerate_catch_exception6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorSgenerate_test_stop6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbIgenerate_copy_words_aligned8_lower6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbJgenerate_copy_words_aligned8_higher6M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbBgenerate_set_words_aligned86M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbCgenerate_zero_words_aligned86M_pC_: stubGenerator_sparc.o; +text: .text%__1cNStubGeneratorbEgenerate_partial_subtype_check6M_pC_: stubGenerator_sparc.o; +text: .text%__1cISubFNodeDsub6kMpknEType_3_3_; +text: .text%__1cRStubCodeGeneratorLstub_prolog6MpnMStubCodeDesc__v_; +text: .text%__1cLStatSamplerbMcreate_system_property_instrumentation6FpnGThread__v_; +text: .text%__1cLStatSamplerHdestroy6F_v_; +text: .text%__1cLStatSamplerJdisengage6F_v_; +text: .text%__1cJArgumentsbNparse_java_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cNRegisterSaverYrestore_result_registers6FpnOMacroAssembler__v_; +text: .text%__1cLOptoRuntimeYgenerate_arraycopy_stubs6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: runtimeService.o; +text: .text%__1cORuntimeServiceYrecord_application_start6F_v_; +text: .text%__1cOMacroAssemblerNset_vm_result6MpnMRegisterImpl__v_; +text: .text%__1cORuntimeServiceEinit6F_v_; +text: .text%__1cLOptoRuntimeVhandle_exception_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeSfetch_monitor_Type6F_pknITypeFunc__; +text: .text%__1cOMacroAssemblerVverify_oop_subroutine6M_v_; +text: .text%__1cOMacroAssemblerPstop_subroutine6M_v_; +text: .text%__1cOMacroAssemblerElcmp6MpnMRegisterImpl_2222_v_; +text: .text%__1cOMacroAssemblerElneg6MpnMRegisterImpl_2_v_; +text: .text%__1cOMacroAssemblerElshl6MpnMRegisterImpl_22222_v_; +text: .text%__1cOMacroAssemblerElshr6MpnMRegisterImpl_22222_v_; +text: .text%__1cOMacroAssemblerFlushr6MpnMRegisterImpl_22222_v_; +text: .text%__1cLOptoRuntimeUmultianewarray5_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray4_Type6F_pknITypeFunc__; +text: .text%__1cLOptoRuntimeUmultianewarray3_Type6F_pknITypeFunc__; +text: .text%__1cLsymbolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: symbolKlass.o; +text: .text%__1cJArgumentsbSparse_java_tool_options_environment_variable6FpnMSysClassPath_pi_i_; +text: .text%__1cJArgumentsFparse6FpknOJavaVMInitArgs__i_; +text: .text%__1cJArgumentsWPropertyList_get_value6FpnOSystemProperty_pkc_4_; +text: .text%__1cNSharedRuntimebIinitialize_StrictMath_entry_points6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: sharedHeap.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: arguments.o; +text: .text%__1cParrayKlassKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cKklassKlassOset_alloc_size6MI_v_: arrayKlassKlass.o; +text: .text%__1cLsymbolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cParrayKlassKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: arrayKlassKlass.o; +text: .text%__1cOMacroAssemblerHcall_VM6MpnMRegisterImpl_pC222i_v_; +text: .text%__1cOMacroAssemblerRcall_VM_leaf_base6MpnMRegisterImpl_pCi_v_; +text: .text%__1cLOptoRuntimebPgenerate_polling_page_return_handler_blob6F_v_; +text: .text%__1cLOptoRuntimebSgenerate_polling_page_safepoint_handler_blob6F_v_; +text: .text%__1cFKlassNoop_is_method6kM_i_: symbolKlass.o; +text: .text%__1cLOptoRuntimebPgenerate_illegal_instruction_handler_blob6F_v_; +text: .text%__1cLOptoRuntimebBgenerate_uncommon_trap_blob6F_v_; +text: .text%__1cNSharedRuntimeTgenerate_deopt_blob6F_v_; +text: .text%__1cLOptoRuntimeWfill_in_exception_blob6F_v_; +text: .text%__1cLOptoRuntimeUsetup_exception_blob6F_v_; +text: .text%__1cLsymbolKlassOset_alloc_size6MI_v_: symbolKlass.o; +text: .text%__1cNTemplateTableGaaload6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psAdaptiveSizePolicy.o; +text: .text%__1cKCMoveDNodeFIdeal6MpnIPhaseGVN_i_pnENode__; +text: .text%__1cOMacroAssemblerCfb6MnJAssemblerJCondition_in0BHPredict_pCnJrelocInfoJrelocType__v_: templateTable_sparc.o; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: tenuredGeneration.o; +text: .text%__1cUPSAdaptiveSizePolicy2t6MIIIIIddI_v_; +text: .text%__1cQconstMethodKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cQconstMethodKlassOset_alloc_size6MI_v_: constMethodKlass.o; +text: .text%__1cQconstMethodKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constMethodKlass.o; +text: .text%__1cGThreadMset_priority6Fp0nOThreadPriority__v_; +text: .text%__1cRconstantPoolKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cKarrayKlassMoop_is_array6kM_i_: constantPoolKlass.o; +text: .text%__1cFKlassPoop_is_instance6kM_i_: constantPoolKlass.o; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: constantPoolKlass.o; +text: .text%__1cOBasicHashtable2t6Mii_v_: placeholders.o; +text: .text%__1cRconstantPoolKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: constantPoolKlass.o; +text: .text%__1cQPlaceholderTable2t6Mi_v_; +text: .text%__1cbBcreate_initial_thread_group6FpnGThread__nGHandle__: thread.o; +text: .text%__1cVcreate_initial_thread6FnGHandle_pnKJavaThread_pnGThread__pnHoopDesc__: thread.o; +text: .text%__1cbAcall_initializeSystemClass6FpnGThread__v_: thread.o; +text: .text%__1cWreset_vm_info_property6FpnGThread__v_: thread.o; +text: .text%__1cbAPSGCAdaptivePolicyCounters2t6MpkciipnUPSAdaptiveSizePolicy__v_; +text: .text%__1cNTemplateTableRfast_invokevfinal6Fi_v_; +text: .text%__1cVcompiledICHolderKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: compiledICHolderKlass.o; +text: .text%__1cNTemplateTableNinvokespecial6Fi_v_; +text: .text%__1cNTemplateTableMinvokestatic6Fi_v_; +text: .text%__1cNTemplateTablebDinvokeinterface_object_method6FpnMRegisterImpl_222_v_; +text: .text%__1cNTemplateTablePinvokeinterface6Fi_v_; +text: .text%__1cNTemplateTableE_new6F_v_; +text: .text%__1cNTemplateTableInewarray6F_v_; +text: .text%__1cNTemplateTableJanewarray6F_v_; +text: .text%__1cNTemplateTableLarraylength6F_v_; +text: .text%__1cNTemplateTableJcheckcast6F_v_; +text: .text%__1cNTemplateTableKinstanceof6F_v_; +text: .text%__1cNTemplateTableL_breakpoint6F_v_; +text: .text%__1cNTemplateTableGathrow6F_v_; +text: .text%__1cNTemplateTableMmonitorenter6F_v_; +text: .text%__1cNTemplateTableLmonitorexit6F_v_; +text: .text%__1cNTemplateTableEwide6F_v_; +text: .text%__1cNTemplateTableOmultianewarray6F_v_; +text: .text%__1cOMacroAssemblerIround_to6MpnMRegisterImpl_i_v_: templateTable_sparc.o; +text: .text%__1cOCompilerOracleOread_from_line6Fpc_v_; +text: .text%__1cTcompilerOracle_init6F_v_; +text: .text%__1cWconstantPoolCacheKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cPPerfDataManagerTcreate_long_counter6FnJCounterNS_pkcnIPerfDataFUnits_pnUPerfLongSampleHelper_pnGThread__pnPPerfLongCounter__; +text: .text%__1cHThreadsJcreate_vm6FpnOJavaVMInitArgs_pi_i_; +text: .text%__1cZCompiledArgumentOopFinderRhandle_oop_offset6M_v_: frame.o; +text: .text%__1cQGCPolicyCounters2t6Mpkcii_v_; +text: .text%__1cHGCStats2t6M_v_; +text: .text%__1cNGCTaskManager2t6MI_v_; +text: .text%__1cNGCTaskManagerKinitialize6M_v_; +text: .text%__1cNGCTaskManagerKthreads_do6MpnNThreadClosure__v_; +text: .text%__1cPPerfDataManagerHdestroy6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: genCollectedHeap.o; +text: .text%__1cJGenRemSetYmax_alignment_constraint6Fn0AEName__I_; +text: .text%__1cWResolveOopMapConflictsUdo_potential_rewrite6MpnGThread__nMmethodHandle__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: generateOopMap.o; +text: .text%__1cRcheck_basic_types6F_v_; +text: .text%__1cSCommandLineFlagsExKuintxAtPut6FnXCommandLineFlagWithType_I_v_; +text: .text%__1cOThreadCriticalKinitialize6F_v_; +text: .text%__1cSThreadLocalStoragebCgenerate_code_for_get_thread6F_v_; +text: .text%__1cICodeHeap2t6M_v_; +text: .text%__1cICodeHeapHreserve6MIII_i_; +text: .text%__1cDhpiKinitialize6F_i_; +text: .text%__1cMPerfDataList2T6M_v_; +text: .text%__1cKarrayKlassOset_alloc_size6MI_v_: cpCacheKlass.o; +text: .text%__1cNWatcherThreadDrun6M_v_; +text: .text%__1cNWatcherThreadEstop6F_v_; +text: .text%__1cWconstantPoolCacheKlassSallocate_permanent6kMrnLKlassHandle_ipnGThread__pv_: cpCacheKlass.o; +text: .text%__1cFStateO_sub_Op_CMoveD6MpknENode__v_; +text: .text%__1cFStateP_sub_Op_MoveF2I6MpknENode__v_; +text: .text%__1cKDictionary2t6Mi_v_; +text: .text%__1cKDictionaryKclasses_do6MpFpnMklassOopDesc__v_v_; +text: .text%__1cOBasicHashtable2t6Mii_v_: dictionary.o; +text: .text%__1cNeventlog_init6F_v_; +text: .text%__1cScheck_ThreadShadow6F_v_; +text: .text%__1cOtailjmpIndNodeLout_RegMask6kM_rknHRegMask__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: fprofiler.o; +text: .text%__1cFframeVinterpreter_frame_mdp6kM_pC_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: phase.o; +text: .text%__1cKPerfMemoryUdelete_memory_region6F_v_; +text: .text%__1cKPerfMemoryUcreate_memory_region6FI_v_; +text: .text%__1cbBcleanup_sharedmem_resources6Fpkc_v_: perfMemory_solaris.o; +text: .text%__1cFframebAoops_compiled_arguments_do6MnMsymbolHandle_ipknLRegisterMap_pnKOopClosure__v_; +text: .text%__1cPperfMemory_exit6F_v_; +text: .text%__1cPperfMemory_init6F_v_; +text: .text%__1cNTemplateTableNinvokevirtual6Fi_v_; +text: .text%__1cNTemplateTableHfastore6F_v_; +text: .text%__1cNTemplateTableHdastore6F_v_; +text: .text%__1cNTemplateTableHaastore6F_v_; +text: .text%__1cNTemplateTableHbastore6F_v_; +text: .text%__1cNTemplateTableHsastore6F_v_; +text: .text%__1cOcodeCache_init6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: codeCache.o; +text: .text%__1cNTemplateTableDpop6F_v_; +text: .text%__1cNTemplateTableEpop26F_v_; +text: .text%__1cNTemplateTableDdup6F_v_; +text: .text%__1cNTemplateTableGdup_x16F_v_; +text: .text%__1cNTemplateTableGdup_x26F_v_; +text: .text%__1cNTemplateTableEdup26F_v_; +text: .text%__1cNTemplateTableHdup2_x16F_v_; +text: .text%__1cNTemplateTableHdup2_x26F_v_; +text: .text%__1cNTemplateTableEswap6F_v_; +text: .text%__1cNCollectedHeap2t6M_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psScavenge.o; +text: .text%__1cNTemplateTableEirem6F_v_; +text: .text%__1cNTemplateTableElmul6F_v_; +text: .text%__1cNTemplateTableHlastore6F_v_; +text: .text%__1cNTemplateTableGbaload6F_v_; +text: .text%__1cNTemplateTableGcaload6F_v_; +text: .text%__1cNTemplateTableMfast_icaload6F_v_; +text: .text%__1cNTemplateTableGsaload6F_v_; +text: .text%__1cKPSYoungGenPinitialize_work6M_v_; +text: .text%__1cKPSYoungGenKinitialize6MnNReservedSpace_I_v_; +text: .text%__1cKPSYoungGenYinitialize_virtual_space6MnNReservedSpace_I_v_; +text: .text%__1cKPSYoungGen2t6MIII_v_; +text: .text%__1cOPSVirtualSpaceJshrink_by6MI_i_; +text: .text%__1cNTemplateTableHaload_06F_v_; +text: .text%__1cNTemplateTableGistore6F_v_; +text: .text%__1cNTemplateTableGlstore6F_v_; +text: .text%__1cNTemplateTableGfstore6F_v_; +text: .text%__1cNTemplateTableGdstore6F_v_; +text: .text%__1cNTemplateTableGastore6F_v_; +text: .text%__1cNTemplateTableLwide_istore6F_v_; +text: .text%__1cNTemplateTableLwide_lstore6F_v_; +text: .text%__1cNTemplateTableLwide_fstore6F_v_; +text: .text%__1cNTemplateTableLwide_dstore6F_v_; +text: .text%__1cNTemplateTableLwide_astore6F_v_; +text: .text%__1cNTemplateTableHiastore6F_v_; +text: .text%__1cNTemplateTableEldiv6F_v_; +text: .text%__1cNTemplateTableLtableswitch6F_v_; +text: .text%__1cNTemplateTableMlookupswitch6F_v_; +text: .text%__1cNTemplateTableRfast_linearswitch6F_v_; +text: .text%__1cNTemplateTableRfast_binaryswitch6F_v_; +text: .text%__1cNCompileBrokerQcompilation_init6FpnQAbstractCompiler__v_; +text: .text%__1cNCompileBrokerVinit_compiler_threads6Fi_v_; +text: .text%__1cJPSPermGen2t6MnNReservedSpace_IIIIpkci_v_; +text: .text%__1cNCompileBrokerQset_should_block6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compileBroker.o; +text: .text%__1cNTemplateTableIgetfield6Fi_v_; +text: .text%__1cNTemplateTableJgetstatic6Fi_v_; +text: .text%__1cIPSOldGenKinitialize6MnNReservedSpace_Ipkci_v_; +text: .text%__1cIPSOldGen2t6MIIIpkci_v_; +text: .text%__1cIPSOldGen2t6MnNReservedSpace_IIIIpkci_v_; +text: .text%__1cVcompiledICHolderKlassMcreate_klass6FpnGThread__pnMklassOopDesc__; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psMarkSweep.o; +text: .text%__1cNTemplateTableIputfield6Fi_v_; +text: .text%__1cNTemplateTableJputstatic6Fi_v_; +text: .text%__1cVcompiledICHolderKlassOset_alloc_size6MI_v_: compiledICHolderKlass.o; +text: .text%__1cLPSMarkSweepKinitialize6F_v_; +text: .text%__1cNTemplateTableIwide_ret6F_v_; +text: .text%__1cNTemplateTableElrem6F_v_; +text: .text%__1cNTemplateTableElshl6F_v_; +text: .text%__1cNTemplateTableElshr6F_v_; +text: .text%__1cNTemplateTableFlushr6F_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyQinitialize_flags6M_v_; +text: .text%__1cbCTwoGenerationCollectorPolicyUinitialize_size_info6M_v_; +text: .text%__1cNTemplateTableEineg6F_v_; +text: .text%__1cNTemplateTableElneg6F_v_; +text: .text%__1cNTemplateTableEfneg6F_v_; +text: .text%__1cNTemplateTableEdneg6F_v_; +text: .text%__1cNTemplateTableEiinc6F_v_; +text: .text%__1cNTemplateTableJwide_iinc6F_v_; +text: .text%__1cKPSScavengeKinitialize6F_v_; +text: .text%__1cNTemplateTableElcmp6F_v_; +text: .text%__1cWcompilationPolicy_init6F_v_; +text: .text%__1cRCompilationPolicyUcompleted_vm_startup6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: compilationPolicy.o; +text: .text%__1cOcompiler2_init6F_v_; +text: .text%__1cSPSPromotionManagerKinitialize6F_v_; +text: .text%__1cU__STATIC_CONSTRUCTOR6F_v_: psPromotionLAB.o; +text: .text%__1cNTemplateTableDret6F_v_; diff --git a/hotspot/build/solaris/makefiles/rules.make b/hotspot/build/solaris/makefiles/rules.make new file mode 100644 index 00000000000..1329d2b1c42 --- /dev/null +++ b/hotspot/build/solaris/makefiles/rules.make @@ -0,0 +1,207 @@ +# +# Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Common rules/macros for the vm, adlc. + +# Tell make that .cpp is important +.SUFFIXES: .cpp $(SUFFIXES) + +# For now. Other makefiles use CPP as the c++ compiler, but that should really +# name the preprocessor. +ifeq ($(CCC),) +CCC = $(CPP) +endif + +DEMANGLER = c++filt +DEMANGLE = $(DEMANGLER) < $@ > .$@ && mv -f .$@ $@ + +# $(CC) is the c compiler (cc/gcc), $(CCC) is the c++ compiler (CC/g++). +C_COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) +CC_COMPILE = $(CCC) $(CPPFLAGS) $(CFLAGS) + +AS.S = $(AS) $(ASFLAGS) + +COMPILE.c = $(C_COMPILE) -c +GENASM.c = $(C_COMPILE) -S +LINK.c = $(CC) $(LFLAGS) $(AOUT_FLAGS) $(PROF_AOUT_FLAGS) +LINK_LIB.c = $(CC) $(LFLAGS) $(SHARED_FLAG) +PREPROCESS.c = $(C_COMPILE) -E + +COMPILE.CC = $(CC_COMPILE) -c +GENASM.CC = $(CC_COMPILE) -S +LINK.CC = $(CCC) $(LFLAGS) $(AOUT_FLAGS) $(PROF_AOUT_FLAGS) +LINK_NOPROF.CC = $(CCC) $(LFLAGS) $(AOUT_FLAGS) +LINK_LIB.CC = $(CCC) $(LFLAGS) $(SHARED_FLAG) +PREPROCESS.CC = $(CC_COMPILE) -E + +# Effect of REMOVE_TARGET is to delete out-of-date files during "gnumake -k". +REMOVE_TARGET = rm -f $@ + +# Synonyms. +COMPILE.cpp = $(COMPILE.CC) +GENASM.cpp = $(GENASM.CC) +LINK.cpp = $(LINK.CC) +LINK_LIB.cpp = $(LINK_LIB.CC) +PREPROCESS.cpp = $(PREPROCESS.CC) + +# Note use of ALT_BOOTDIR to explicitly specify location of java and +# javac; this is the same environment variable used in the J2SE build +# process for overriding the default spec, which is BOOTDIR. +# Note also that we fall back to using JAVA_HOME if neither of these is +# specified. + +ifdef ALT_BOOTDIR + +RUN.JAVA = $(ALT_BOOTDIR)/bin/java +RUN.JAVAP = $(ALT_BOOTDIR)/bin/javap +RUN.JAVAH = $(ALT_BOOTDIR)/bin/javah +RUN.JAR = $(ALT_BOOTDIR)/bin/jar +COMPILE.JAVAC = $(ALT_BOOTDIR)/bin/javac +COMPILE.RMIC = $(ALT_BOOTDIR)/bin/rmic +BOOT_JAVA_HOME = $(ALT_BOOTDIR) + +else + +ifdef BOOTDIR + +RUN.JAVA = $(BOOTDIR)/bin/java +RUN.JAVAP = $(BOOTDIR)/bin/javap +RUN.JAVAH = $(BOOTDIR)/bin/javah +RUN.JAR = $(BOOTDIR)/bin/jar +COMPILE.JAVAC = $(BOOTDIR)/bin/javac +COMPILE.RMIC = $(BOOTDIR)/bin/rmic +BOOT_JAVA_HOME = $(BOOTDIR) + +else + +ifdef JAVA_HOME + +RUN.JAVA = $(JAVA_HOME)/bin/java +RUN.JAVAP = $(JAVA_HOME)/bin/javap +RUN.JAVAH = $(JAVA_HOME)/bin/javah +RUN.JAR = $(JAVA_HOME)/bin/jar +COMPILE.JAVAC = $(JAVA_HOME)/bin/javac +COMPILE.RMIC = $(JAVA_HOME)/bin/rmic +BOOT_JAVA_HOME = $(JAVA_HOME) + +else + +# take from the PATH, if ALT_BOOTDIR, BOOTDIR and JAVA_HOME are not defined +# note that this is to support hotspot build without SA. To build +# SA along with hotspot, you need to define ALT_BOOTDIR, BOOTDIR or JAVA_HOME + +RUN.JAVA = java +RUN.JAVAP = javap +RUN.JAVAH = javah +RUN.JAR = jar +COMPILE.JAVAC = javac +COMPILE.RMIC = rmic + +endif +endif +endif + +SUM = /usr/bin/sum + +# 'gmake MAKE_VERBOSE=y' gives all the gory details. +QUIETLY$(MAKE_VERBOSE) = @ +RUN.JAR$(MAKE_VERBOSE) += >/dev/null + +# With parallel makes, print a message at the end of compilation. +ifeq ($(findstring j,$(MFLAGS)),j) +COMPILE_DONE = && { echo Done with $<; } +endif + +# A list of directories under which all source code are built without -KPIC/-Kpic +# flag. Performance measurements show that compiling GC related code will +# dramatically reduce the gc pause time. See bug 6454213 for more details. + +include $(GAMMADIR)/make/scm.make + +NONPIC_DIRS = memory oops gc_implementation gc_interface +NONPIC_DIRS := $(foreach dir,$(NONPIC_DIRS), $(GAMMADIR)/src/share/vm/$(dir)) +# Look for source code under NONPIC_DIRS +NONPIC_FILES := $(foreach dir,$(NONPIC_DIRS),\ + $(shell find $(dir) \( $(SCM_DIRS) \) -prune -o \ + -name '*.cpp' -print)) +NONPIC_OBJ_FILES := $(notdir $(subst .cpp,.o,$(NONPIC_FILES))) + +# Sun compiler for 64 bit Solaris does not support building non-PIC object files. +ifdef LP64 +%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(COMPILE.CC) -o $@ $< $(COMPILE_DONE) +else +%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(if $(findstring $@, $(NONPIC_OBJ_FILES)), \ + $(subst $(VM_PICFLAG), ,$(COMPILE.CC)) -o $@ $< $(COMPILE_DONE), \ + $(COMPILE.CC) -o $@ $< $(COMPILE_DONE)) +endif + +%.o: %.s + @echo Assembling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(AS.S) -o $@ $< $(COMPILE_DONE) + +%.s: %.cpp + @echo Generating assembly for $< + $(QUIETLY) $(GENASM.CC) -o $@ $< + $(QUIETLY) $(DEMANGLE) $(COMPILE_DONE) + +# Intermediate files (for debugging macros) +%.i: %.cpp + @echo Preprocessing $< to $@ + $(QUIETLY) $(PREPROCESS.CC) $< > $@ $(COMPILE_DONE) + +# Override gnumake built-in rules which do sccs get operations badly. +# (They put the checked out code in the current directory, not in the +# directory of the original file.) Since this is a symptom of a teamware +# failure, and since not all problems can be detected by gnumake due +# to incomplete dependency checking... just complain and stop. +%:: s.% + @echo "=========================================================" + @echo File $@ + @echo is out of date with respect to its SCCS file. + @echo This file may be from an unresolved Teamware conflict. + @echo This is also a symptom of a Teamware bringover/putback failure + @echo in which SCCS files are updated but not checked out. + @echo Check for other out of date files in your workspace. + @echo "=========================================================" + @exit 666 + +%:: SCCS/s.% + @echo "=========================================================" + @echo File $@ + @echo is out of date with respect to its SCCS file. + @echo This file may be from an unresolved Teamware conflict. + @echo This is also a symptom of a Teamware bringover/putback failure + @echo in which SCCS files are updated but not checked out. + @echo Check for other out of date files in your workspace. + @echo "=========================================================" + @exit 666 + +.PHONY: default diff --git a/hotspot/build/solaris/makefiles/sa.make b/hotspot/build/solaris/makefiles/sa.make new file mode 100644 index 00000000000..dc1f159a093 --- /dev/null +++ b/hotspot/build/solaris/makefiles/sa.make @@ -0,0 +1,78 @@ +# +# Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This makefile (sa.make) is included from the sa.make in the +# build directories. + +# This makefile is used to build Serviceability Agent java code +# and generate JNI header file for native methods. + +include $(GAMMADIR)/build/solaris/makefiles/rules.make +AGENT_DIR = $(GAMMADIR)/agent +include $(GAMMADIR)/build/sa.files +GENERATED = ../generated + +# tools.jar is needed by the JDI - SA binding +SA_CLASSPATH = $(BOOT_JAVA_HOME)/lib/tools.jar + +# gnumake 3.78.1 does not accept the *s that +# are in AGENT_ALLFILES, so use the shell to expand them +AGENT_ALLFILES := $(shell /usr/bin/test -d $(AGENT_DIR) && /bin/ls $(AGENT_ALLFILES)) + +SA_CLASSDIR = $(GENERATED)/saclasses + +SA_BUILD_VERSION_PROP = "sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VERSION)" + +SA_PROPERTIES = $(SA_CLASSDIR)/sa.properties + +# if $(AGENT_DIR) does not exist, we don't build SA. +all: + $(QUIETLY) if [ -d $(AGENT_DIR) ] ; then \ + $(MAKE) -f sa.make $(GENERATED)/sa-jdi.jar; \ + fi + +$(GENERATED)/sa-jdi.jar: $(AGENT_ALLFILES) + $(QUIETLY) echo "Making $@"; + $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ + echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ + exit 1; \ + fi + $(QUIETLY) if [ ! -f $(SA_CLASSPATH) ] ; then \ + echo "Missing $(SA_CLASSPATH) file. Use 1.6.0 or later version of JDK";\ + echo ""; \ + exit 1; \ + fi + $(QUIETLY) if [ ! -d $(SA_CLASSDIR) ] ; then \ + mkdir -p $(SA_CLASSDIR); \ + fi + $(QUIETLY) $(COMPILE.JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -g -d $(SA_CLASSDIR) $(AGENT_ALLFILES) + $(QUIETLY) $(COMPILE.RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer + $(QUIETLY) echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) + $(QUIETLY) $(RUN.JAR) cf $@ -C $(SA_CLASSDIR)/ . + $(QUIETLY) $(RUN.JAR) uf $@ -C $(AGENT_SRC_DIR) META-INF/services/com.sun.jdi.connect.Connector + $(QUIETLY) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.proc.ProcDebuggerLocal + +clean: + rm -rf $(SA_CLASSDIR) + rm -rf $(GENERATED)/sa-jdi.jar diff --git a/hotspot/build/solaris/makefiles/saproc.make b/hotspot/build/solaris/makefiles/saproc.make new file mode 100644 index 00000000000..83d9fce0e73 --- /dev/null +++ b/hotspot/build/solaris/makefiles/saproc.make @@ -0,0 +1,79 @@ +# +# Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Rules to build serviceability agent library, used by vm.make + +# libsaproc[_g].so: serviceability agent +SAPROC = saproc$(G_SUFFIX) +LIBSAPROC = lib$(SAPROC).so + +AGENT_DIR = $(GAMMADIR)/agent + +SASRCDIR = $(AGENT_DIR)/src/os/$(Platform_os_family)/proc + +SASRCFILES = $(SASRCDIR)/saproc.cpp + +SAMAPFILE = $(SASRCDIR)/mapfile + +DEST_SAPROC = $(JDK_LIBDIR)/$(LIBSAPROC) + +# if $(AGENT_DIR) does not exist, we don't build SA + +checkAndBuildSA: + $(QUIETLY) if [ -d $(AGENT_DIR) ] ; then \ + $(MAKE) -f vm.make $(LIBSAPROC); \ + fi + +SA_LFLAGS = $(MAPFLAG:FILENAME=$(SAMAPFILE)) + +ifdef USE_GCC +SA_LFLAGS += -D_REENTRANT +else +SA_LFLAGS += -mt -xnolib -norunpath +endif + +$(LIBSAPROC): $(SASRCFILES) $(SAMAPFILE) + $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ + echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ + exit 1; \ + fi + @echo Making SA debugger back-end... + $(QUIETLY) $(CPP) \ + $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ + -I$(SASRCDIR) \ + -I$(GENERATED) \ + -I$(BOOT_JAVA_HOME)/include \ + -I$(BOOT_JAVA_HOME)/include/$(Platform_os_family) \ + $(SASRCFILES) \ + $(SA_LFLAGS) \ + -o $@ \ + -ldl -ldemangle -lthread -lc + +install_saproc: checkAndBuildSA + $(QUIETLY) if [ -f $(LIBSAPROC) ] ; then \ + echo "Copying $(LIBSAPROC) to $(DEST_SAPROC)"; \ + cp -f $(LIBSAPROC) $(DEST_SAPROC) && echo "Done"; \ + fi + +.PHONY: checkAndBuildSA install_saproc diff --git a/hotspot/build/solaris/makefiles/sparc.make b/hotspot/build/solaris/makefiles/sparc.make new file mode 100644 index 00000000000..b3a0e2682bb --- /dev/null +++ b/hotspot/build/solaris/makefiles/sparc.make @@ -0,0 +1,124 @@ +# +# Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +Obj_Files += solaris_sparc.o +ASFLAGS += $(ARCHFLAG) + +ifeq ("${Platform_compiler}", "sparcWorks") +ifeq ($(shell expr $(COMPILER_REV) \< 5.5), 1) +# For 5.2 ad_sparc file is compiled with -O2 %%%% remove when adlc is fixed +OPT_CFLAGS/ad_sparc.o = $(OPT_CFLAGS/SLOWER) +OPT_CFLAGS/dfa_sparc.o = $(OPT_CFLAGS/SLOWER) +# CC drops core on systemDictionary.o in -xO4 mode +OPT_CFLAGS/systemDictionary.o = $(OPT_CFLAGS/SLOWER) +# SC5.0 bug 4284168 +OPT_CFLAGS/carRememberedSet.o = $(OPT_CFLAGS/O2) +# Temporarily drop the optimization level for compiling +# jniHandles.cpp to O3 from O4; see bug 4309181 +OPT_CFLAGS/jniHandles.o = $(OPT_CFLAGS/O2) +# CC brings an US-II to its knees compiling the vmStructs asserts under -xO4 +OPT_CFLAGS/vmStructs.o = $(OPT_CFLAGS/O2) +endif +else +# Options for gcc +OPT_CFLAGS/ad_sparc.o = $(OPT_CFLAGS/SLOWER) +OPT_CFLAGS/dfa_sparc.o = $(OPT_CFLAGS/SLOWER) +OPT_CFLAGS/systemDictionary.o = $(OPT_CFLAGS/SLOWER) +OPT_CFLAGS/carRememberedSet.o = $(OPT_CFLAGS/O2) +OPT_CFLAGS/jniHandles.o = $(OPT_CFLAGS/O2) +OPT_CFLAGS/vmStructs.o = $(OPT_CFLAGS/O2) +endif + +# File-specific adjustments to the PICFLAG, applicable only to SPARC, +# which has a very tight limit on global constant references. + +# Old files which seemed hot at one point or another: +#PICFLAG/runtime.o = $(PICFLAG/BETTER) +#PICFLAG/generateOopMap.o = $(PICFLAG/BETTER) +#PICFLAG/thread.o = $(PICFLAG/BETTER) +#PICFLAG/parse2.o = $(PICFLAG/BETTER) +#PICFLAG/parse1.o = $(PICFLAG/BETTER) +#PICFLAG/universe.o = $(PICFLAG/BETTER) +#PICFLAG/safepoint.o = $(PICFLAG/BETTER) +#PICFLAG/parse3.o = $(PICFLAG/BETTER) +#PICFLAG/compile.o = $(PICFLAG/BETTER) +#PICFLAG/codeBlob.o = $(PICFLAG/BETTER) +#PICFLAG/mutexLocker.o = $(PICFLAG/BETTER) +#PICFLAG/nativeInst_sparc.o = $(PICFLAG/BETTER) +#PICFLAG/methodLiveness.o = $(PICFLAG/BETTER) +#PICFLAG/synchronizer.o = $(PICFLAG/BETTER) +#PICFLAG/methodOop.o = $(PICFLAG/BETTER) +#PICFLAG/space.o = $(PICFLAG/BETTER) +#PICFLAG/interpreterRT_sparc.o = $(PICFLAG/BETTER) +#PICFLAG/generation.o = $(PICFLAG/BETTER) +#PICFLAG/markSweep.o = $(PICFLAG/BETTER) +#PICFLAG/parseHelper.o = $(PICFLAG/BETTER) + +# Confirmed by function-level profiling: +PICFLAG/scavenge.o = $(PICFLAG/BETTER) +PICFLAG/instanceKlass.o = $(PICFLAG/BETTER) +PICFLAG/frame.o = $(PICFLAG/BETTER) +PICFLAG/phaseX.o = $(PICFLAG/BETTER) +PICFLAG/lookupCache.o = $(PICFLAG/BETTER) +PICFLAG/chaitin.o = $(PICFLAG/BETTER) +PICFLAG/type.o = $(PICFLAG/BETTER) +PICFLAG/jvm.o = $(PICFLAG/BETTER) +PICFLAG/jni.o = $(PICFLAG/BETTER) +PICFLAG/matcher.o = $(PICFLAG/BETTER) + +# New from module-level profiling (trustworthy?): +PICFLAG/rememberedSet.o = $(PICFLAG/BETTER) +PICFLAG/frame_sparc.o = $(PICFLAG/BETTER) +PICFLAG/live.o = $(PICFLAG/BETTER) +PICFLAG/vectset.o = $(PICFLAG/BETTER) +PICFLAG/objArrayKlass.o = $(PICFLAG/BETTER) +PICFLAG/do_call.o = $(PICFLAG/BETTER) +PICFLAG/loopnode.o = $(PICFLAG/BETTER) +PICFLAG/cfgnode.o = $(PICFLAG/BETTER) +PICFLAG/ifg.o = $(PICFLAG/BETTER) +PICFLAG/vframe.o = $(PICFLAG/BETTER) +PICFLAG/postaloc.o = $(PICFLAG/BETTER) +PICFLAG/carRememberedSet.o = $(PICFLAG/BETTER) +PICFLAG/gcm.o = $(PICFLAG/BETTER) +PICFLAG/coalesce.o = $(PICFLAG/BETTER) +PICFLAG/oop.o = $(PICFLAG/BETTER) +PICFLAG/oopMap.o = $(PICFLAG/BETTER) +PICFLAG/resourceArea.o = $(PICFLAG/BETTER) +PICFLAG/node.o = $(PICFLAG/BETTER) +PICFLAG/dict.o = $(PICFLAG/BETTER) +PICFLAG/domgraph.o = $(PICFLAG/BETTER) +PICFLAG/dfa_sparc.o = $(PICFLAG/BETTER) +PICFLAG/block.o = $(PICFLAG/BETTER) +PICFLAG/javaClasses.o = $(PICFLAG/BETTER) + +# New hot files: +PICFLAG/classes.o = $(PICFLAG/BETTER) +#PICFLAG/ad_sparc.o = $(PICFLAG/BETTER) +PICFLAG/nmethod.o = $(PICFLAG/BETTER) +PICFLAG/relocInfo.o = $(PICFLAG/BETTER) +PICFLAG/codeBuffer_sparc.o = $(PICFLAG/BETTER) +PICFLAG/callnode.o = $(PICFLAG/BETTER) +PICFLAG/multnode.o = $(PICFLAG/BETTER) +PICFLAG/os_solaris.o = $(PICFLAG/BETTER) +PICFLAG/typeArrayKlass.o = $(PICFLAG/BETTER) diff --git a/hotspot/build/solaris/makefiles/sparcWorks.make b/hotspot/build/solaris/makefiles/sparcWorks.make new file mode 100644 index 00000000000..50a8b2a6af5 --- /dev/null +++ b/hotspot/build/solaris/makefiles/sparcWorks.make @@ -0,0 +1,495 @@ +# +# Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Compiler-specific flags for sparcworks. + +# tell make which C and C++ compilers to use +CC = cc +CPP = CC + +AS = /usr/ccs/bin/as + +NM = /usr/ccs/bin/nm +NAWK = /bin/nawk + +REORDER_FLAG = -xF + +# Check for the versions of C++ and C compilers ($CPP and $CC) used. + +# Get the last thing on the line that looks like x.x+ (x is a digit). +COMPILER_REV := \ +$(shell $(CPP) -V 2>&1 | sed -e 's/^.*\([1-9]\.[0-9][0-9]*\).*/\1/') +C_COMPILER_REV := \ +$(shell $(CC) -V 2>&1 | grep -i "cc:" | sed -e 's/^.*\([1-9]\.[0-9][0-9]*\).*/\1/') + +VALIDATED_COMPILER_REV := 5.8 +VALIDATED_C_COMPILER_REV := 5.8 + +ENFORCE_COMPILER_REV${ENFORCE_COMPILER_REV} := ${VALIDATED_COMPILER_REV} +ifneq (${COMPILER_REV},${ENFORCE_COMPILER_REV}) +dummy_target_to_enforce_compiler_rev: + @echo "Wrong ${CPP} version: ${COMPILER_REV}. " \ + "Use version ${ENFORCE_COMPILER_REV}, or set" \ + "ENFORCE_COMPILER_REV=${COMPILER_REV}." + @exit 1 +endif + +ENFORCE_C_COMPILER_REV${ENFORCE_C_COMPILER_REV} := ${VALIDATED_C_COMPILER_REV} +ifneq (${C_COMPILER_REV},${ENFORCE_C_COMPILER_REV}) +dummy_target_to_enforce_c_compiler_rev: + @echo "Wrong ${CC} version: ${C_COMPILER_REV}. " \ + "Use version ${ENFORCE_C_COMPILER_REV}, or set" \ + "ENFORCE_C_COMPILER_REV=${C_COMPILER_REV}." + @exit 1 +endif + +# Fail the build if __fabsf is used. __fabsf exists only in Solaris 8 2/04 +# and newer; objects with a dependency on this symbol will not run on older +# Solaris 8. +JVM_FAIL_IF_UNDEFINED = __fabsf + +JVM_CHECK_SYMBOLS = $(NM) -u -p $(LIBJVM.o) | \ + $(NAWK) -v f="${JVM_FAIL_IF_UNDEFINED}" \ + 'BEGIN { c=split(f,s); rc=0; } \ + /:$$/ { file = $$1; } \ + /[^:]$$/ { for(n=1;n<=c;++n) { \ + if($$1==s[n]) { \ + printf("JVM_CHECK_SYMBOLS: %s contains illegal symbol %s\n", \ + file,$$1); \ + rc=1; \ + } \ + } \ + } \ + END { exit rc; }' + +LINK_LIB.CC/PRE_HOOK += $(JVM_CHECK_SYMBOLS) || exit 1; + +# Some interfaces (_lwp_create) changed with LP64 and Solaris 7 +SOLARIS_7_OR_LATER := \ +$(shell uname -r | awk -F. '{ if ($$2 >= 7) print "-DSOLARIS_7_OR_LATER"; }') +CFLAGS += ${SOLARIS_7_OR_LATER} + +ARCHFLAG = $(ARCHFLAG/$(BUILDARCH)) +# set ARCHFLAG/BUILDARCH which will ultimately be ARCHFLAG +ifeq ($(TYPE),COMPILER2) +ARCHFLAG/sparc = -xarch=v8plus +else +ifeq ($(TYPE),TIERED) +ARCHFLAG/sparc = -xarch=v8plus +else +ARCHFLAG/sparc = -xarch=v8 +endif +endif +ARCHFLAG/sparcv9 = -xarch=v9 +ARCHFLAG/i486 = +ARCHFLAG/amd64 = -xarch=amd64 + +# Optional sub-directory in /usr/lib where BUILDARCH libraries are kept. +ISA_DIR=$(ISA_DIR/$(BUILDARCH)) +ISA_DIR/sparcv9=/sparcv9 +ISA_DIR/amd64=/amd64 + +# Use these to work around compiler bugs: +OPT_CFLAGS/SLOWER=-xO3 +OPT_CFLAGS/O2=-xO2 +OPT_CFLAGS/NOOPT=-xO1 + +################################################# +# Begin current (>=5.6) Forte compiler options # +################################################# + +ifeq ($(shell expr $(COMPILER_REV) \>= 5.6), 1) + +ifeq ("${Platform_arch}", "sparc") + +# We MUST allow data alignment of 4 for sparc (sparcv9 is ok at 8s) +ifndef LP64 +CFLAGS += -xmemalign=4s +endif + +endif + +endif + +################################################# +# Begin current (>=5.5) Forte compiler options # +################################################# + +ifeq ($(shell expr $(COMPILER_REV) \>= 5.5), 1) + +CFLAGS += $(ARCHFLAG) +AOUT_FLAGS += $(ARCHFLAG) +LIB_FLAGS += $(ARCHFLAG) +LFLAGS += $(ARCHFLAG) + +ifeq ("${Platform_arch}", "sparc") + +# Flags for Optimization + +# [phh] Commented out pending verification that we do indeed want +# to potentially bias against u1 and u3 targets. +#CFLAGS += -xchip=ultra2 + +OPT_CFLAGS=-xO4 $(EXTRA_OPT_CFLAGS) + +endif # sparc + +ifeq ("${Platform_arch_model}", "x86_32") + +OPT_CFLAGS=-xtarget=pentium $(EXTRA_OPT_CFLAGS) + +# UBE (CC 5.5) has bug 4923569 with -xO4 +OPT_CFLAGS+=-xO3 + +endif # 32bit x86 + +ifeq ("${Platform_arch_model}", "x86_64") + +ASFLAGS += -xarch=amd64 +CFLAGS += -xarch=amd64 +# this one seemed useless +LFLAGS_VM += -xarch=amd64 +# this one worked +LFLAGS += -xarch=amd64 +AOUT_FLAGS += -xarch=amd64 + +# -xO3 is faster than -xO4 on specjbb with SS10 compiler +OPT_CFLAGS=-xO4 $(EXTRA_OPT_CFLAGS) + +endif # 64bit x86 + +# Inline functions +CFLAGS += $(GAMMADIR)/src/os_cpu/solaris_${Platform_arch}/vm/solaris_${Platform_arch_model}.il + +# no more exceptions +CFLAGS/NOEX=-features=no%except + +# Reduce code bloat by reverting back to 5.0 behavior for static initializers +CFLAGS += -features=no%split_init + +# Use -D_Crun_inline_placement so we don't get references to +# __1c2n6FIpv_0_ or void*operator new(unsigned,void*) +# This avoids the hard requirement of the newer Solaris C++ runtime patches. +# NOTE: This is an undocumented feature of the SS10 compiler. See 6306698. +CFLAGS += -D_Crun_inline_placement + +# PIC is safer for SPARC, and is considerably slower +# a file foo.o which wants to compile -pic can set "PICFLAG/foo.o = -PIC" +PICFLAG = -KPIC +PICFLAG/DEFAULT = $(PICFLAG) +# [RGV] Need to figure which files to remove to get link to work +#PICFLAG/BETTER = -pic +PICFLAG/BETTER = $(PICFLAG/DEFAULT) +PICFLAG/BYFILE = $(PICFLAG/$@)$(PICFLAG/DEFAULT$(PICFLAG/$@)) + +# Use $(MAPFLAG:FILENAME=real_file_name) to specify a map file. +MAPFLAG = -M FILENAME + +# Use $(SONAMEFLAG:SONAME=soname) to specify the intrinsic name of a shared obj +SONAMEFLAG = -h SONAME + +# Build shared library +SHARED_FLAG = -G + +# We don't need libCstd.so and librwtools7.so, only libCrun.so +CFLAGS += -library=%none +LFLAGS += -library=%none + +LFLAGS += -mt + +endif # COMPILER_REV >= VALIDATED_COMPILER_REV + +###################################### +# End 5.5 Forte compiler options # +###################################### + +###################################### +# Begin 5.2 Forte compiler options # +###################################### + +ifeq ($(COMPILER_REV), 5.2) + +CFLAGS += $(ARCHFLAG) +AOUT_FLAGS += $(ARCHFLAG) +LIB_FLAGS += $(ARCHFLAG) +LFLAGS += $(ARCHFLAG) + +ifeq ("${Platform_arch}", "sparc") + +# Flags for Optimization + +# [phh] Commented out pending verification that we do indeed want +# to potentially bias against u1 and u3 targets. +#CFLAGS += -xchip=ultra2 + +ifdef LP64 +# SC5.0 tools on v9 are flakey at -xO4 +# [phh] Is this still true for 6.1? +OPT_CFLAGS=-xO3 $(EXTRA_OPT_CFLAGS) +else +OPT_CFLAGS=-xO4 $(EXTRA_OPT_CFLAGS) +endif + +CFLAGS += $(GAMMADIR)/src/os_cpu/solaris_sparc/vm/solaris_sparc.il + +endif # sparc + +ifeq ("${Platform_arch_model}", "x86_32") + +OPT_CFLAGS=-xtarget=pentium $(EXTRA_OPT_CFLAGS) + +# SC5.0 tools on x86 are flakey at -xO4 +# [phh] Is this still true for 6.1? +OPT_CFLAGS+=-xO3 + +CFLAGS += $(GAMMADIR)/src/os_cpu/solaris_x86/vm/solaris_x86_32.il + +endif # 32bit x86 + +# no more exceptions +CFLAGS/NOEX=-noex + +# Reduce code bloat by reverting back to 5.0 behavior for static initializers +CFLAGS += -Qoption ccfe -one_static_init + +# PIC is safer for SPARC, and is considerably slower +# a file foo.o which wants to compile -pic can set "PICFLAG/foo.o = -PIC" +PICFLAG = -KPIC +PICFLAG/DEFAULT = $(PICFLAG) +# [RGV] Need to figure which files to remove to get link to work +#PICFLAG/BETTER = -pic +PICFLAG/BETTER = $(PICFLAG/DEFAULT) +PICFLAG/BYFILE = $(PICFLAG/$@)$(PICFLAG/DEFAULT$(PICFLAG/$@)) + +# Would be better if these weren't needed, since we link with CC, but +# at present removing them causes run-time errors +LFLAGS += -library=Crun +LIBS += -library=Crun -lCrun + +endif # COMPILER_REV >= VALIDATED_COMPILER_REV + +################################## +# End 5.2 Forte compiler options # +################################## + +################################## +# Begin old 5.1 compiler options # +################################## +ifeq ($(COMPILER_REV), 5.1) + +_JUNK_ := $(shell echo >&2 \ + "*** ERROR: sparkWorks.make incomplete for 5.1 compiler") + @exit 1 +endif +################################## +# End old 5.1 compiler options # +################################## + +################################## +# Begin old 5.0 compiler options # +################################## + +ifeq (${COMPILER_REV}, 5.0) + +# Had to hoist this higher apparently because of other changes. Must +# come before -xarch specification. +CFLAGS += -xtarget=native + +CFLAGS += $(ARCHFLAG) +AOUT_FLAGS += $(ARCHFLAG) +LIB_FLAGS += $(ARCHFLAG) +LFLAGS += $(ARCHFLAG) + +CFLAGS += -library=iostream +LFLAGS += -library=iostream -library=Crun +LIBS += -library=iostream -library=Crun -lCrun + +# Flags for Optimization +ifdef LP64 +# SC5.0 tools on v9 are flakey at -xO4 +OPT_CFLAGS=-xO3 $(EXTRA_OPT_CFLAGS) +else +OPT_CFLAGS=-xO4 $(EXTRA_OPT_CFLAGS) +endif + +ifeq ("${Platform_arch}", "sparc") + +CFLAGS += $(GAMMADIR)/src/os_cpu/solaris_sparc/vm/atomic_solaris_sparc.il + +endif # sparc + +ifeq ("${Platform_arch_model}", "x86_32") +OPT_CFLAGS=-xtarget=pentium $(EXTRA_OPT_CFLAGS) +ifeq ("${COMPILER_REV}", "5.0") +# SC5.0 tools on x86 are flakey at -xO4 +OPT_CFLAGS+=-xO3 +else +OPT_CFLAGS+=-xO4 +endif + +CFLAGS += $(GAMMADIR)/src/os_cpu/solaris_x86/vm/solaris_x86_32.il + +endif # 32bit x86 + +# The following options run into misaligned ldd problem (raj) +#OPT_CFLAGS = -fast -O4 -xarch=v8 -xchip=ultra + +# no more exceptions +CFLAGS/NOEX=-noex + +# PIC is safer for SPARC, and is considerably slower +# a file foo.o which wants to compile -pic can set "PICFLAG/foo.o = -PIC" +PICFLAG = -PIC +PICFLAG/DEFAULT = $(PICFLAG) +# [RGV] Need to figure which files to remove to get link to work +#PICFLAG/BETTER = -pic +PICFLAG/BETTER = $(PICFLAG/DEFAULT) +PICFLAG/BYFILE = $(PICFLAG/$@)$(PICFLAG/DEFAULT$(PICFLAG/$@)) + +endif # COMPILER_REV = 5.0 + +################################ +# End old 5.0 compiler options # +################################ + +ifeq ("${COMPILER_REV}", "4.2") +# 4.2 COMPILERS SHOULD NO LONGER BE USED +_JUNK_ := $(shell echo >&2 \ + "*** ERROR: SC4.2 compilers are not supported by this code base!") + @exit 1 +endif + +# do not include shared lib path in a.outs +AOUT_FLAGS += -norunpath +LFLAGS_VM = -norunpath -z noversion + +# need position-indep-code for shared libraries +# (ild appears to get errors on PIC code, so we'll try non-PIC for debug) +ifeq ($(PICFLAGS),DEFAULT) +VM_PICFLAG/LIBJVM = $(PICFLAG/DEFAULT) +else +VM_PICFLAG/LIBJVM = $(PICFLAG/BYFILE) +endif +VM_PICFLAG/AOUT = + +VM_PICFLAG = $(VM_PICFLAG/$(LINK_INTO)) +CFLAGS += $(VM_PICFLAG) + +# less dynamic linking (no PLTs, please) +#LIB_FLAGS += $(LINK_MODE) +# %%%%% despite -znodefs, -Bsymbolic gets link errors -- Rose + +LINK_MODE = $(LINK_MODE/$(VERSION)) +LINK_MODE/debug = +LINK_MODE/optimized = -Bsymbolic -znodefs + +# Have thread local errnos +ifeq ($(shell expr $(COMPILER_REV) \>= 5.5), 1) +CFLAGS += -mt +else +CFLAGS += -D_REENTRANT +endif + +ifdef CC_INTERP +# C++ Interpreter +CFLAGS += -DCC_INTERP +endif + +# Flags for Debugging +DEBUG_CFLAGS = -g +FASTDEBUG_CFLAGS = -g0 +# The -g0 setting allows the C++ frontend to inline, which is a big win. + +# Enable the following CFLAGS additions if you need to compare the +# built ELF objects. +# +# The -g option makes static data global and the "-Qoption ccfe +# -xglobalstatic" option tells the compiler to not globalize static +# data using a unique globalization prefix. Instead force the use of +# a static globalization prefix based on the source filepath so the +# objects from two identical compilations are the same. +#DEBUG_CFLAGS += -Qoption ccfe -xglobalstatic +#FASTDEBUG_CFLAGS += -Qoption ccfe -xglobalstatic + +ifeq (${COMPILER_REV}, 5.2) +COMPILER_DATE := $(shell $(CPP) -V 2>&1 | awk '{ print $$NF; }') +ifeq (${COMPILER_DATE}, 2001/01/31) +# disable -g0 in fastdebug since SC6.1 dated 2001/01/31 seems to be buggy +# use an innocuous value because it will get -g if it's empty +FASTDEBUG_CFLAGS = -c +endif +endif + +# Uncomment or 'gmake CFLAGS_BROWSE=-sbfast' to get source browser information. +# CFLAGS_BROWSE = -sbfast +CFLAGS += $(CFLAGS_BROWSE) + +# ILD is gone as of SS11 (5.8), not supportted in SS10 (5.7) +ifeq ($(shell expr $(COMPILER_REV) \< 5.7), 1) + # use ild when debugging (but when optimizing we want reproducible results) + ILDFLAG = $(ILDFLAG/$(VERSION)) + ILDFLAG/debug = -xildon + ILDFLAG/optimized = + AOUT_FLAGS += $(ILDFLAG) +endif + +# Where to put the *.o files (a.out, or shared library)? +LINK_INTO = $(LINK_INTO/$(VERSION)) +LINK_INTO/debug = LIBJVM +LINK_INTO/optimized = LIBJVM + +# We link the debug version into the a.out because: +# 1. ild works on a.out but not shared libraries, and using ild +# can cut rebuild times by 25% for small changes. (ILD is gone in SS11) +# 2. dbx cannot gracefully set breakpoints in shared libraries +# + +# apply this setting to link into the shared library even in the debug version: +ifdef LP64 +LINK_INTO = LIBJVM +else +#LINK_INTO = LIBJVM +endif + +MCS = /usr/ccs/bin/mcs +STRIP = /usr/ccs/bin/strip + +# Solaris platforms collect lots of redundant file-ident lines, +# to the point of wasting a significant percentage of file space. +# (The text is stored in ELF .comment sections, contributed by +# all "#pragma ident" directives in header and source files.) +# This command "compresses" the .comment sections simply by +# removing repeated lines. The data can be extracted from +# binaries in the field by using "mcs -p libjvm.so" or the older +# command "what libjvm.so". +LINK_LIB.CC/POST_HOOK += $(MCS) -c $@ || exit 1; +# (The exit 1 is necessary to cause a build failure if the command fails and +# multiple commands are strung together, and the final semicolon is necessary +# since the hook must terminate itself as a valid command.) + +# Also, strip debug and line number information (worth about 1.7Mb). +STRIP_LIB.CC/POST_HOOK = $(STRIP) -x $@ || exit 1; +# STRIP_LIB.CC/POST_HOOK is incorporated into LINK_LIB.CC/POST_HOOK +# in certain configurations, such as product.make. Other configurations, +# such as debug.make, do not include the strip operation. diff --git a/hotspot/build/solaris/makefiles/sparcv9.make b/hotspot/build/solaris/makefiles/sparcv9.make new file mode 100644 index 00000000000..985b7b47135 --- /dev/null +++ b/hotspot/build/solaris/makefiles/sparcv9.make @@ -0,0 +1,49 @@ +# +# Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +Obj_Files += solaris_sparc.o +ASFLAGS += $(ARCHFLAG) + +ifeq ("${Platform_compiler}", "sparcWorks") +ifeq ($(shell expr $(COMPILER_REV) \< 5.5), 1) +# When optimized fully, stubGenerator_sparc.cpp +# has bogus code for the routine +# StubGenerator::generate_flush_callers_register_windows() +OPT_CFLAGS/stubGenerator_sparc.o = $(OPT_CFLAGS/SLOWER) + +# For now ad_sparc file is compiled with -O2 %%%% remove when adlc is fixed +OPT_CFLAGS/ad_sparc.o = $(OPT_CFLAGS/SLOWER) +OPT_CFLAGS/dfa_sparc.o = $(OPT_CFLAGS/SLOWER) + +# CC brings an US-II to its knees compiling the vmStructs asserts under -xO4 +OPT_CFLAGS/vmStructs.o = $(OPT_CFLAGS/O2) +endif + +else +#Options for gcc +OPT_CFLAGS/stubGenerator_sparc.o = $(OPT_CFLAGS/SLOWER) +OPT_CFLAGS/ad_sparc.o = $(OPT_CFLAGS/SLOWER) +OPT_CFLAGS/dfa_sparc.o = $(OPT_CFLAGS/SLOWER) +OPT_CFLAGS/vmStructs.o = $(OPT_CFLAGS/O2) +endif diff --git a/hotspot/build/solaris/makefiles/tiered.make b/hotspot/build/solaris/makefiles/tiered.make new file mode 100644 index 00000000000..220124e50e1 --- /dev/null +++ b/hotspot/build/solaris/makefiles/tiered.make @@ -0,0 +1,31 @@ +# +# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Sets make macros for making tiered version of VM + +TYPE=TIERED + +VM_SUBDIR = server + +CFLAGS += -DCOMPILER2 -DCOMPILER1 diff --git a/hotspot/build/solaris/makefiles/top.make b/hotspot/build/solaris/makefiles/top.make new file mode 100644 index 00000000000..14c9acd38ac --- /dev/null +++ b/hotspot/build/solaris/makefiles/top.make @@ -0,0 +1,184 @@ +# +# Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# top.make is included in the Makefile in the build directories. +# It DOES NOT include the vm dependency info in order to be faster. +# It's main job is to implement the incremental form of make lists. +# It also: +# -builds and runs adlc via adlc.make +# -generates JVMTI source and docs via jvmti.make (JSR-163) +# -generate sa-jdi.jar (JDI binding to core files) + +# It assumes the following flags are set: +# CFLAGS Platform_file, Src_Dirs, SYSDEFS, AOUT, Jvm_Obj_Files + +# -- D. Ungar (5/97) from a file by Bill Bush + +# Don't override the built-in $(MAKE). +# Instead, use "gmake" (or "gnumake") from the command line. --Rose +#MAKE = gmake + +GENERATED = ../generated +VM = $(GAMMADIR)/src/share/vm +Plat_File = $(Platform_file) +CDG = cd $(GENERATED); + +# Pick up MakeDeps' sources and definitions +include $(GAMMADIR)/build/$(Platform_os_family)/makefiles/makedeps.make +MakeDepsClass = MakeDeps.class +MakeDeps = $(RUN.JAVA) -classpath . MakeDeps + +Include_DBs/GC = $(VM)/includeDB_gc \ + $(VM)/includeDB_gc_parallel \ + $(VM)/gc_implementation/includeDB_gc_parallelScavenge \ + $(VM)/gc_implementation/includeDB_gc_concurrentMarkSweep \ + $(VM)/gc_implementation/includeDB_gc_parNew \ + $(VM)/gc_implementation/includeDB_gc_serial \ + $(VM)/gc_implementation/includeDB_gc_shared + + +Include_DBs/KERNEL = $(VM)/includeDB_core $(VM)/includeDB_gc \ + $(VM)/gc_implementation/includeDB_gc_serial \ + $(VM)/includeDB_jvmti \ + $(VM)/includeDB_compiler1 + +Include_DBs/CORE = $(VM)/includeDB_core $(Include_DBs/GC) \ + $(VM)/includeDB_jvmti \ + $(VM)/includeDB_features +Include_DBs/COMPILER1 = $(Include_DBs/CORE) $(VM)/includeDB_compiler1 +Include_DBs/COMPILER2 = $(Include_DBs/CORE) $(VM)/includeDB_compiler2 +Include_DBs/TIERED = $(Include_DBs/CORE) $(VM)/includeDB_compiler1 \ + $(VM)/includeDB_compiler2 + +Include_DBs = $(Include_DBs/$(TYPE)) + +Cached_plat = platform.current +Cached_db = includeDB.current + +Incremental_Lists =$(GENERATED)/$(Cached_db) +# list generation also creates $(GENERATED)/$(Cached_plat) + + +AD_Dir = $(GENERATED)/adfiles +ADLC = $(AD_Dir)/adlc +AD_Spec = $(GAMMADIR)/src/cpu/$(Platform_arch)/vm/$(Platform_arch).ad +AD_Src = $(GAMMADIR)/src/share/vm/adlc +AD_Names = ad_$(Platform_arch).hpp ad_$(Platform_arch).cpp +AD_Files = $(AD_Names:%=$(AD_Dir)/%) + +# AD_Files_If_Required/COMPILER1 = ad_stuff +AD_Files_If_Required/COMPILER2 = ad_stuff +AD_Files_If_Required/TIERED = ad_stuff +AD_Files_If_Required = $(AD_Files_If_Required/$(TYPE)) + +# Wierd argument adjustment for "gnumake -j..." +adjust-mflags = $(GENERATED)/adjust-mflags +MFLAGS-adjusted = -r `$(adjust-mflags) "$(MFLAGS)" "$(HOTSPOT_BUILD_JOBS)"` + + +# default target: make makeDeps, update lists, make vm +# done in stages to force sequential order with parallel make +# + +default: vm_build_preliminaries the_vm + @echo All done. + +# This is an explicit dependency for the sake of parallel makes. +vm_build_preliminaries: checks $(Incremental_Lists) $(AD_Files_If_Required) jvmti_stuff sa_stuff + @# We need a null action here, so implicit rules don't get consulted. + +# make makeDeps: (and zap the cached db files to force a nonincremental run) + +$(GENERATED)/$(MakeDepsClass): $(MakeDepsSources) + @$(COMPILE.JAVAC) -classpath $(GAMMADIR)/src/share/tools/MakeDeps -g -d $(GENERATED) $(MakeDepsSources) + @echo Removing $(Incremental_Lists) to force regeneration. + @rm -f $(Incremental_Lists) + @$(CDG) echo >$(Cached_plat) + +# make incremental_lists, if cached files out of date, run makeDeps + +$(Incremental_Lists): $(Include_DBs) $(Plat_File) $(GENERATED)/$(MakeDepsClass) + $(CDG) cat $(Include_DBs) > includeDB + $(CDG) if [ ! -r incls ] ; then \ + mkdir incls ; \ + fi + $(CDG) $(MakeDeps) diffs UnixPlatform $(Cached_plat) $(Cached_db) $(Plat_File) includeDB $(MakeDepsOptions) + $(CDG) cp includeDB $(Cached_db) + $(CDG) cp $(Plat_File) $(Cached_plat) + +# symbolic target for command lines +lists: $(Incremental_Lists) + @: lists are now up to date + +# make AD files as necessary +ad_stuff: $(Incremental_Lists) $(adjust-mflags) + @$(MAKE) -f adlc.make $(MFLAGS-adjusted) + +# generate JVMTI files from the spec +jvmti_stuff: $(Incremental_Lists) $(adjust-mflags) + @$(MAKE) -f jvmti.make $(MFLAGS-adjusted) + +# generate SA jar files and native header +sa_stuff: + @$(MAKE) -f sa.make $(MFLAGS-adjusted) + +# and the VM: must use other makefile with dependencies included + +# We have to go to great lengths to get control over the -jN argument +# to the recursive invocation of vm.make. The problem is that gnumake +# resets -jN to -j1 for recursive runs. (How helpful.) +# Note that the user must specify the desired parallelism level via a +# command-line or environment variable name HOTSPOT_BUILD_JOBS. +$(adjust-mflags): $(GAMMADIR)/build/$(Platform_os_family)/makefiles/adjust-mflags.sh + @+rm -f $@ $@+ + @+cat $< > $@+ + @+chmod +x $@+ + @+mv $@+ $@ + +the_vm: vm_build_preliminaries $(adjust-mflags) + @$(MAKE) -f vm.make $(MFLAGS-adjusted) + +install: the_vm + @$(MAKE) -f vm.make install + +# next rules support "make foo.[oi]" + +%.o %.i %.s: + $(MAKE) -f vm.make $(MFLAGS) $@ + #$(MAKE) -f vm.make $@ + +# this should force everything to be rebuilt +clean: + rm -f $(GENERATED)/*.class + $(MAKE) $(MFLAGS) $(GENERATED)/$(MakeDepsClass) + $(MAKE) -f vm.make $(MFLAGS) clean + +# just in case it doesn't, this should do it +realclean: + $(MAKE) -f vm.make $(MFLAGS) clean + rm -fr $(GENERATED) + +.PHONY: default vm_build_preliminaries +.PHONY: lists ad_stuff jvmti_stuff sa_stuff the_vm clean realclean +.PHONY: checks check_os_version install diff --git a/hotspot/build/solaris/makefiles/vm.make b/hotspot/build/solaris/makefiles/vm.make new file mode 100644 index 00000000000..c1fa4643129 --- /dev/null +++ b/hotspot/build/solaris/makefiles/vm.make @@ -0,0 +1,206 @@ +# +# Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Rules to build JVM and related libraries, included from vm.make in the build +# directory. + +# Common build rules. +MAKEFILES_DIR=$(GAMMADIR)/build/$(Platform_os_family)/makefiles +include $(MAKEFILES_DIR)/rules.make + +default: build + +#---------------------------------------------------------------------- +# Defs + +GENERATED = ../generated + +# read a generated file defining the set of .o's and the .o .h dependencies +include $(GENERATED)/Dependencies + +# read machine-specific adjustments (%%% should do this via buildtree.make?) +include $(MAKEFILES_DIR)/$(BUILDARCH).make + +# set VPATH so make knows where to look for source files +# Src_Dirs is everything in src/share/vm/*, plus the right os/*/vm and cpu/*/vm +# The incls directory contains generated header file lists for inclusion. +# The adfiles directory contains ad_.[ch]pp. +# The jvmtifiles directory contains jvmti*.[ch]pp +Src_Dirs_V = $(GENERATED)/adfiles $(GENERATED)/jvmtifiles ${Src_Dirs} $(GENERATED)/incls +VPATH += $(Src_Dirs_V:%=%:) + +# set INCLUDES for C preprocessor +Src_Dirs_I = $(GENERATED)/adfiles $(GENERATED)/jvmtifiles ${Src_Dirs} $(GENERATED) +INCLUDES += $(Src_Dirs_I:%=-I%) + +ifeq (${VERSION}, debug) + SYMFLAG = -g +else + SYMFLAG = +endif + +# The following variables are defined in the generated flags.make file. +BUILD_VERSION = -DHOTSPOT_RELEASE_VERSION="\"$(HS_BUILD_VER)\"" +JRE_VERSION = -DJRE_RELEASE_VERSION="\"$(JRE_RELEASE_VER)\"" +BUILD_TARGET = -DHOTSPOT_BUILD_TARGET="\"$(TARGET)\"" +BUILD_USER = -DHOTSPOT_BUILD_USER="\"$(HOTSPOT_BUILD_USER)\"" +VM_DISTRO = -DHOTSPOT_VM_DISTRO="\"$(HOTSPOT_VM_DISTRO)\"" + +CPPFLAGS = \ + ${SYSDEFS} \ + ${INCLUDES} \ + ${BUILD_VERSION} \ + ${BUILD_TARGET} \ + ${BUILD_USER} \ + ${JRE_VERSION} \ + ${VM_DISTRO} + +# CFLAGS_WARN holds compiler options to suppress/enable warnings. +CFLAGS += $(CFLAGS_WARN) + +# Do not use C++ exception handling +CFLAGS += $(CFLAGS/NOEX) + +# Extra flags from gnumake's invocation or environment +CFLAGS += $(EXTRA_CFLAGS) + +# Math Library (libm.so), do not use -lm. +# There might be two versions of libm.so on the build system: +# libm.so.1 and libm.so.2, and we want libm.so.1. +# Depending on the Solaris release being used to build with, +# /usr/lib/libm.so could point at a libm.so.2, so we are +# explicit here so that the libjvm.so you have built will work on an +# older Solaris release that might not have libm.so.2. +# This is a critical factor in allowing builds on Solaris 10 or newer +# to run on Solaris 8 or 9. +# +LIBM=/usr/lib$(ISA_DIR)/libm.so.1 + +ifeq ("${Platform_compiler}", "sparcWorks") +# The whole megilla: +ifeq ($(shell expr $(COMPILER_REV) \>= 5.5), 1) +# Old Comment: List the libraries in the order the compiler was designed for +# Not sure what the 'designed for' comment is referring too above. +# The order may not be too significant anymore, but I have placed this +# older libm before libCrun, just to make sure it's found and used first. +LIBS += -lsocket -lsched -ldl $(LIBM) -lCrun -lthread -ldoor -lc +else +LIBS += -ldl -lthread -lsocket $(LIBM) -lsched -ldoor +endif +else +LIBS += -lsocket -lsched -ldl $(LIBM) -lthread -lc +endif + +# By default, link the *.o into the library, not the executable. +LINK_INTO$(LINK_INTO) = LIBJVM + +JDK_LIBDIR = $(JAVA_HOME)/jre/lib/$(LIBARCH) + +#---------------------------------------------------------------------- +# jvm_db & dtrace +include $(MAKEFILES_DIR)/dtrace.make + +#---------------------------------------------------------------------- +# JVM + +JVM = jvm$(G_SUFFIX) +LIBJVM = lib$(JVM).so + +JVM_OBJ_FILES = $(Obj_Files) $(DTRACE_OBJS) + +vm_version.o: $(filter-out vm_version.o,$(JVM_OBJ_FILES)) + +mapfile : $(MAPFILE) $(MAPFILE_DTRACE_OPT) + rm -f $@ + cat $^ > $@ + +mapfile_reorder : mapfile $(MAPFILE_DTRACE_OPT) $(REORDERFILE) + rm -f $@ + cat $^ > $@ + +ifeq ($(LINK_INTO),AOUT) + LIBJVM.o = + LIBJVM_MAPFILE = + LIBS_VM = $(LIBS) +else + LIBJVM.o = $(JVM_OBJ_FILES) + LIBJVM_MAPFILE$(LDNOMAP) = mapfile_reorder + LFLAGS_VM$(LDNOMAP) += $(MAPFLAG:FILENAME=$(LIBJVM_MAPFILE)) + LFLAGS_VM += $(SONAMEFLAG:SONAME=$(LIBJVM)) +ifndef USE_GCC + LIBS_VM = $(LIBS) +else + # JVM is statically linked with libgcc[_s] and libstdc++; this is needed to + # get around library dependency and compatibility issues. Must use gcc not + # g++ to link. + LFLAGS_VM += $(STATIC_LIBGCC) + LIBS_VM += $(STATIC_STDCXX) $(LIBS) +endif +endif + +ifdef USE_GCC +LINK_VM = $(LINK_LIB.c) +else +LINK_VM = $(LINK_LIB.CC) +endif +# making the library: +$(LIBJVM): $(LIBJVM.o) $(LIBJVM_MAPFILE) + $(QUIETLY) \ + case "$(CFLAGS_BROWSE)" in \ + -sbfast|-xsbfast) \ + ;; \ + *) \ + echo Linking vm...; \ + $(LINK_LIB.CC/PRE_HOOK) \ + $(LINK_VM) $(LFLAGS_VM) -o $@ $(LIBJVM.o) $(LIBS_VM); \ + $(LINK_LIB.CC/POST_HOOK) \ + rm -f $@.1; ln -s $@ $@.1; \ + ;; \ + esac + +DEST_JVM = $(JDK_LIBDIR)/$(VM_SUBDIR)/$(LIBJVM) + +install_jvm: $(LIBJVM) + @echo "Copying $(LIBJVM) to $(DEST_JVM)" + $(QUIETLY) cp -f $(LIBJVM) $(DEST_JVM) && echo "Done" + +#---------------------------------------------------------------------- +# Other files + +# Gamma launcher +include $(MAKEFILES_DIR)/launcher.make + +# Signal interposition library +include $(MAKEFILES_DIR)/jsig.make + +# Serviceability agent +include $(MAKEFILES_DIR)/saproc.make + +#---------------------------------------------------------------------- + +build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) $(LIBJVM_DTRACE) checkAndBuildSA dtraceCheck + +install: install_jvm install_jsig install_saproc + +.PHONY: default build install install_jvm diff --git a/hotspot/build/solaris/platform_amd64 b/hotspot/build/solaris/platform_amd64 new file mode 100644 index 00000000000..2cfe3d41681 --- /dev/null +++ b/hotspot/build/solaris/platform_amd64 @@ -0,0 +1,17 @@ +os_family = solaris + +arch = x86 + +arch_model = x86_64 + +os_arch = solaris_x86 + +os_arch_model = solaris_x86_64 + +lib_arch = amd64 + +compiler = sparcWorks + +gnu_dis_arch = amd64 + +sysdefs = -DSOLARIS -DSPARC_WORKS -DAMD64 diff --git a/hotspot/build/solaris/platform_amd64.gcc b/hotspot/build/solaris/platform_amd64.gcc new file mode 100644 index 00000000000..57d25fa6f44 --- /dev/null +++ b/hotspot/build/solaris/platform_amd64.gcc @@ -0,0 +1,17 @@ +os_family = solaris + +arch = x86 + +arch_model = x86_64 + +os_arch = solaris_x86 + +os_arch_model = solaris_x86_64 + +lib_arch = amd64 + +compiler = gcc + +gnu_dis_arch = amd64 + +sysdefs = -DSOLARIS -D_GNU_SOURCE -DAMD64 diff --git a/hotspot/build/solaris/platform_i486 b/hotspot/build/solaris/platform_i486 new file mode 100644 index 00000000000..c6902160b1f --- /dev/null +++ b/hotspot/build/solaris/platform_i486 @@ -0,0 +1,17 @@ +os_family = solaris + +arch = x86 + +arch_model = x86_32 + +os_arch = solaris_x86 + +os_arch_model = solaris_x86_32 + +lib_arch = i386 + +compiler = sparcWorks + +gnu_dis_arch = i386 + +sysdefs = -DSOLARIS -DSPARC_WORKS -DIA32 diff --git a/hotspot/build/solaris/platform_i486.gcc b/hotspot/build/solaris/platform_i486.gcc new file mode 100644 index 00000000000..8d1d57ff596 --- /dev/null +++ b/hotspot/build/solaris/platform_i486.gcc @@ -0,0 +1,17 @@ +os_family = solaris + +arch = x86 + +arch_model = x86_32 + +os_arch = solaris_x86 + +os_arch_model = solaris_x86_32 + +lib_arch = i386 + +compiler = gcc + +gnu_dis_arch = i386 + +sysdefs = -DSOLARIS -D_GNU_SOURCE -DIA32 diff --git a/hotspot/build/solaris/platform_sparc b/hotspot/build/solaris/platform_sparc new file mode 100644 index 00000000000..4ff94c3ae68 --- /dev/null +++ b/hotspot/build/solaris/platform_sparc @@ -0,0 +1,17 @@ +os_family = solaris + +arch = sparc + +arch_model = sparc + +os_arch = solaris_sparc + +os_arch_model = solaris_sparc + +lib_arch = sparc + +compiler = sparcWorks + +gnu_dis_arch = sparc + +sysdefs = -DSOLARIS -DSPARC_WORKS -DSPARC diff --git a/hotspot/build/solaris/platform_sparc.gcc b/hotspot/build/solaris/platform_sparc.gcc new file mode 100644 index 00000000000..87d42becfa3 --- /dev/null +++ b/hotspot/build/solaris/platform_sparc.gcc @@ -0,0 +1,17 @@ +os_family = solaris + +arch = sparc + +arch_model = sparc + +os_arch = solaris_sparc + +os_arch_model = solaris_sparc + +lib_arch = sparc + +compiler = gcc + +gnu_dis_arch = sparc + +sysdefs = -DSOLARIS -D_GNU_SOURCE -DSPARC diff --git a/hotspot/build/solaris/platform_sparcv9 b/hotspot/build/solaris/platform_sparcv9 new file mode 100644 index 00000000000..4ff94c3ae68 --- /dev/null +++ b/hotspot/build/solaris/platform_sparcv9 @@ -0,0 +1,17 @@ +os_family = solaris + +arch = sparc + +arch_model = sparc + +os_arch = solaris_sparc + +os_arch_model = solaris_sparc + +lib_arch = sparc + +compiler = sparcWorks + +gnu_dis_arch = sparc + +sysdefs = -DSOLARIS -DSPARC_WORKS -DSPARC diff --git a/hotspot/build/solaris/platform_sparcv9.gcc b/hotspot/build/solaris/platform_sparcv9.gcc new file mode 100644 index 00000000000..87d42becfa3 --- /dev/null +++ b/hotspot/build/solaris/platform_sparcv9.gcc @@ -0,0 +1,17 @@ +os_family = solaris + +arch = sparc + +arch_model = sparc + +os_arch = solaris_sparc + +os_arch_model = solaris_sparc + +lib_arch = sparc + +compiler = gcc + +gnu_dis_arch = sparc + +sysdefs = -DSOLARIS -D_GNU_SOURCE -DSPARC diff --git a/hotspot/build/solaris/reorder.sh b/hotspot/build/solaris/reorder.sh new file mode 100644 index 00000000000..598f3ebbc38 --- /dev/null +++ b/hotspot/build/solaris/reorder.sh @@ -0,0 +1,355 @@ +#!/bin/sh -x +# +# Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Generate the reorder data for hotspot. +# +# Usage: +# +# sh reorder.sh +# +# is a *built* SDK workspace which contains the +# reordering tools for the SDK. This script relies on lib_mcount.so +# from this workspace. +# +# is a working SDK which you can use to run the profiled +# JVMs in to collect data. You must be able to write to this SDK. +# +# is a directory containing JBB test jar files and properties +# which will be used to run the JBB test to provide reordering data +# for the server VM. +# +# Profiled builds of the VM are needed (before running this script), +# build with PROFILE_PRODUCT=1: +# +# gnumake profiled1 profiled PROFILE_PRODUCT=1 +# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test_setup() { + + # $1 = "client" or "server" + # $2 = name of reorder file to be generated. + + echo "" + echo "TEST_SETUP $1 $2" + echo "" + libreldir=${ALT_OUTPUTDIR:-../../../build/solaris-$arch5}/reorder + libabsdir=${ALT_OUTPUTDIR:-$sdk_ws/build/solaris-$arch5}/reorder + ( cd $sdk_ws/make/tools/reorder ; gnumake $libreldir/$arch5/libmcount.so ) + if [ "${arch3}" = "i386" ] ; then + # On Solaris/x86 we need to remove the symbol _mcount from the command + ( cd $sdk_ws/make/tools/reorder ; \ + gnumake $libreldir/$arch5/remove_mcount ) + echo Remove _mcount from java command. + $libabsdir/$arch5/remove_mcount $jre/bin/java + fi + ( cd $sdk_ws/make/tools/reorder ; gnumake tool_classes ) + ( cd $sdk_ws/make/tools/reorder ; gnumake test_classes ) + + tests="Null Exit Hello Sleep IntToString \ + LoadToolkit LoadFrame LoadJFrame JHello" + swingset=$sdk/demo/jfc/SwingSet2/SwingSet2.jar + java=$jre/bin/java + if [ "X$LP64" != "X" ] ; then + testjava="$jre/bin/${arch3}/java" + else + testjava="$jre/bin/java" + fi + mcount=$libabsdir/$arch5/libmcount.so + + if [ ! -x $mcount ] ; then + echo $mcount is missing! + exit 1 + fi + + if [ "X$1" = "client" ] ; then + if [ "X$NO_SHARING" = "X" ] ; then + echo "Dumping shared file." + LD_PRELOAD=$mcount \ + JDK_ALTERNATE_VM=jvm_profiled \ + $testjava -Xshare:dump -Xint -XX:PermSize=16m -version 2> /dev/null + shared_client="-Xshare:on" + echo "Shared file dump completed." + else + shared_client="-Xshare:off" + echo "NO_SHARING defined, not using sharing." + fi + else + echo "Server: no sharing" + shared_server="-Xshare:off" + fi + + testpath=$libabsdir/classes + + reorder_file=$2 + + rm -f ${reorder_file} + rm -f ${reorder_file}_tmp2 + rm -f ${reorder_file}_tmp1 + + echo "data = R0x2000;" > ${reorder_file} + echo "text = LOAD ?RXO;" >> ${reorder_file} + echo "" >> ${reorder_file} + echo "" >> ${reorder_file} +} + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test_client() { + + # Run each of a set of tests, extract the methods called, + # append the new functions to the reorder list. + # $1 = "client" or "server" + # $2 = name of reorder file to be generated. + + echo "TEST_CLIENT $1 $2." + test_setup $1 $2 + echo "TEST_CLIENT $1 $2." + + for f in $tests ; do + echo Running test $f. + rm -f ${reorder_file}_tmp1 + echo "# Test $f" >> ${reorder_file} + + echo "Using LD_PRELOAD=$mcount" + echo $testjava ${shared_client} -classpath $testpath $f + + LD_PRELOAD=$mcount \ + JDK_ALTERNATE_VM=jvm_profiled \ + $testjava ${shared_client} -classpath $testpath $f 2> ${reorder_file}_tmp1 + + echo "Done." + sed -n -e '/^text:/p' ${reorder_file}_tmp1 > ${reorder_file}_tmp2 + sed -e '/^text:/d' ${reorder_file}_tmp1 + LD_LIBRARY_PATH=$lib/server \ + $java -classpath $testpath Combine ${reorder_file} \ + ${reorder_file}_tmp2 \ + > ${reorder_file}_tmp3 + mv ${reorder_file}_tmp3 ${reorder_file} + rm -f ${reorder_file}_tmp2 + rm -f ${reorder_file}_tmp1 + done + + # Run SwingSet, extract the methods called, + # append the new functions to the reorder list. + + echo "# SwingSet" >> ${reorder_file} + + echo "" + echo "" + echo "When SwingSet has finished drawing, " \ + "you may terminate it (with your mouse)." + echo "Otherwise, it should be automatically terminated in 3 minutes." + echo "" + echo "" + + echo "Using LD_PRELOAD=$mcount, JDK_ALTERNATE=jvm_profiled." + echo $testjava ${shared_client} -classpath $testpath MaxTime $swingset 60 + LD_PRELOAD=$mcount \ + JDK_ALTERNATE_VM=jvm_profiled \ + $testjava ${shared_client} -classpath $testpath MaxTime \ + $swingset 60 2> ${reorder_file}_tmp1 + + sed -n -e '/^text:/p' ${reorder_file}_tmp1 > ${reorder_file}_tmp2 + + LD_LIBRARY_PATH=$lib/server \ + $java -server -classpath $testpath Combine ${reorder_file} ${reorder_file}_tmp2 \ + > ${reorder_file}_tmp3 + echo mv ${reorder_file}_tmp3 ${reorder_file} + mv ${reorder_file}_tmp3 ${reorder_file} + echo rm -f ${reorder_file}_tmp2 + rm -f ${reorder_file}_tmp2 + echo rm -f ${reorder_file}_tmp1 + rm -f ${reorder_file}_tmp1 +} + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test_server() { + + # Run the JBB script, collecting data on the way. + # $1 = "client" or "server" + # $2 = name of reorder file to be generated. + + echo "TEST_SERVER $1 $2." + test_setup $1 $2 + echo "TEST_SERVER $1 $2." + + echo Running JBB. + + rm -f ${reorder_file}_tmp1 + rm -f ${reorder_file}_tmp2 + heap=200m + + CLASSPATH=jbb.jar:jbb_no_precompile.jar:check.jar:reporter.jar + + ( cd $jbb_dir; LD_PRELOAD=$mcount MCOUNT_ORDER_BY_COUNT=1 \ + JDK_ALTERNATE_VM=jvm_profiled \ + $testjava ${shared_server} -classpath $CLASSPATH -Xms${heap} -Xmx${heap} \ + spec.jbb.JBBmain -propfile SPECjbb.props ) 2> ${reorder_file}_tmp1 + + sed -n -e '/^text:/p' ${reorder_file}_tmp1 > ${reorder_file}_tmp2 + sed -e '/^text:/d' ${reorder_file}_tmp1 + cat ${reorder_file}_tmp2 >> ${reorder_file} + rm -f ${reorder_file}_tmp2 + rm -f ${reorder_file}_tmp1 +} + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Rename the old VMs, copy the new in, run the test, and put the +# old one back. + +copy_and_test() { + + # $1 = "client" or "server" + # $2 = name of reorder file to be generated. + # $3 = profiled jvm to copy in + + echo "COPY_AND_TEST ($1, $2, $3)." + # $2 = name of reorder file to be generated. + # $3 = profiled jvm to copy in + + rm -rf $lib/jvm_profiled + mkdir $lib/jvm_profiled + cp $3 $lib/jvm_profiled + test_$1 $1 $2 + rm -rf $lib/jvm_profiled +} + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# Check arguments: + +if [ $# != 3 ] ; then + echo "" + echo "Usage:" + echo " sh reorder.sh " + echo "" + exit 1 +fi + +sdk_ws=$1 +if [ ! -r $sdk_ws/make/tools/reorder/Makefile ] ; then + echo "" + echo "test workspace "$sdk_ws" does not contain the reordering tools." + echo "" + exit 1 +fi + +sdk=$2 +jre=$sdk/jre + +# Set up architecture names as needed by various components. +# Why couldn't we just use x86 for everything? + +# Arch name as used in JRE runtime (eg. i386): +# .../jre/lib/${arch3}/server +arch3=`uname -p` + +# Arch name as used in Hotspot build: (eg. i486) +# /export/hotspot/build/solaris/solaris_${arch4}_compiler1 +arch4=$arch3 + +# Arch name as used in SDK build (eg. i586): +# /export/tiger/build/solaris-${arch3} +arch5=$arch3 + +# Tweak for 64-bit sparc builds. At least they all agree. +if [ $arch3 = sparc -a "X$LP64" != "X" ] ; then + arch3=sparcv9 + arch4=sparcv9 + arch5=sparcv9 +fi + +# Tweak for 64-bit i386 == amd64 builds. At least they all agree. +if [ $arch3 = i386 -a "X$LP64" != "X" ] ; then + arch3=amd64 + arch4=amd64 + arch5=amd64 +fi + +# Tweak for x86 builds. All different. +if [ $arch3 = i386 ] ; then + arch4=i486 + arch5=i586 +fi + +lib=$jre/lib/$arch3 +if [ ! -r $jre/lib/rt.jar ] ; then + echo "" + echo "test SDK "$sdk" is not a suitable SDK." + echo "" + exit 1 +fi + +jbb_dir=$3 +if [ ! -r $jbb_dir/jbb.jar ] ; then + echo "" + echo "jbb.jar not present in $jbb_dir" + echo "" + exit 1 +fi + + +# Were profiled VMs built? + +if [ "X$LP64" != "X" ] ; then + if [ ! -r solaris_${arch4}_compiler2/profiled/libjvm.so ] ; then + echo "" + echo "Profiled builds of compiler2 are needed first." + echo ' -- build with "make profiled PROFILE_PRODUCT=1" -- ' + echo "" + exit 1 + fi +else + if [ ! -r solaris_${arch4}_compiler1/profiled/libjvm.so \ + -o ! -r solaris_${arch4}_compiler2/profiled/libjvm.so ] ; then + echo "" + echo "Profiled builds of compiler1 and compiler2 are needed first." + echo ' -- build with "make profiled{,1} PROFILE_PRODUCT=1" -- ' + exit 1 + fi +fi + + +# Compiler1 - not supported in 64-bit (b69 java launcher rejects it). + +if [ "X$LP64" = "X" ] ; then + #gnumake profiled1 + echo Using profiled client VM. + echo + copy_and_test client \ + reorder_COMPILER1_$arch4 \ + solaris_${arch4}_compiler1/profiled/libjvm.so +fi + +#gnumake profiled +echo Using profiled server VM. +echo +copy_and_test server \ + reorder_COMPILER2_$arch4 \ + solaris_${arch4}_compiler2/profiled/libjvm.so diff --git a/hotspot/build/test/Queens.java b/hotspot/build/test/Queens.java new file mode 100644 index 00000000000..075ca9b93ad --- /dev/null +++ b/hotspot/build/test/Queens.java @@ -0,0 +1,86 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.util.*; + +// Copyright 1996, Animorphic Systems +// gri 28 Aug 92 / 15 Jan 93 / 8 Dec 95 + +class Queens { + + static void try_i(boolean a[], boolean b[], boolean c[], int x[], int i) { + int adj = 7; + + for (int j = 1; j <= 8; j++) { + if (b[j] && a[i+j] && c[adj+i-j]) { + x[i] = j; + b[j] = false; + a[i+j] = false; + c[adj+i-j] = false; + if (i < 8) try_i(a, b, c, x, i+1); + else print(x); + b[j] = true; + a[i+j] = true; + c[adj+i-j] = true; + } + } + } + + public static void main(String s[]) { + boolean a[] = new boolean[16+1]; + boolean b[] = new boolean[ 8+1]; + boolean c[] = new boolean[14+1]; + int x[] = new int[8+1]; + int adj = 7; + + for (int i = -7; i <= 16; i++) { + if (i >= 1 && i <= 8) b[i] = true; + if (i >= 2) a[i] = true; + if (i <= 7) c[adj+i] = true; + } + + x[0] = 0; // solution counter + + try_i(a, b, c, x, 1); + } + + static void print(int x[]) { + // first correct solution: A1 B5 C8 D6 E3 F7 G2 H4 + + char LF = (char)0xA; + char CR = (char)0xD; + + x[0]++; + if (x[0] < 10) + System.out.print(" "); + System.out.print(x[0] + ". "); + for (int i = 1; i <= 8; i++) { + char p = (char)('A' + i - 1); + System.out.print(p); + System.out.print (x[i] + " "); + } + System.out.println(); + } + +}; diff --git a/hotspot/build/windows/README b/hotspot/build/windows/README new file mode 100644 index 00000000000..c6c6c9cc59f --- /dev/null +++ b/hotspot/build/windows/README @@ -0,0 +1,212 @@ +Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + +This code is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 only, as +published by the Free Software Foundation. + +This code is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +version 2 for more details (a copy is included in the LICENSE file that +accompanied this code). + +You should have received a copy of the GNU General Public License version +2 along with this work; if not, write to the Free Software Foundation, +Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +CA 95054 USA or visit www.sun.com if you need additional information or +have any questions. + +________________________________________________________________________________ + +__Introduction__________________________________________________________________ + +This readme file should provide all the information needed to build +the HotSpot VM for Windows 95/Windows NT from its teamware workspace. +It is intended as a starting point for people who want to learn how +to work with the current HotSpot source workspace and who need to +build the VM locally. It is not intended as a tutorial for licensees. + +Last update: 03/28/05 + + +__Platform______________________________________________________________________ + +The VM builds under the following platforms: +- Windows NT 4.0 on Intel x486 or greater +- x486 PC (or greater), 32MByte or more + + +__Tools_________________________________________________________________________ + +For building/testing the following tools need to be available: +- Microsoft Visual C++ 6.0 (with nmake version 1.62.7022 or greater) +- MKS Toolkit 6.1 or greater + see: /net/reinstall/export/vol0/pc-archive/software/mks6.1 (NFS) + or: \\reinstall\pc-archive\software\mks6.1 (NT) + + +__JDK___________________________________________________________________________ + +The workspace works with the following version of the JDK: +(NOTE: these are out of date) +- JDK1.2FCS "V" build + see: /usr/local/java/jdk1.2/win32 + +and the following version(s) of HotJava: +- hjb1.1.4 +- hjb1.1.5 + see /usr/local/java/hjb1.1.x/win32 + + +__Environment variables_________________________________________________________ + +The following environment variables need to be set up for the IDE +build process. For batch builds these do not need to be set. + +HotSpotMksHome points to the (NFS or PC-local) directory where the MKS + executables (like sh.exe and grep.exe) are installed + +Optionally you may set the following variables in your environment and they +will be picked up by the create.bat script used to generate the vm.vcproj files. +See the section on building within MS Developer Studio for more details. + +HotSpotWorkSpace points to the (NFS) directory where the workspace is located +HotSpotBuildSpace points to the (PC-local) directory where the vm is built +HotSpotReleaseBinDest points to the (NFS or PC-local) directory where the product DLL is + written +HotSpotDebugBinDest points to the (NFS or PC-local) directory where the debug DLL is + written + +NOTE: For both batch and IDE builds, java and javac must be in your +PATH, and the versions found by default must work. (If this turns out +to be a problem, we can define HotSpotJava and HotSpotJavaC for +bootstrapping...) + +__Building the JVM from the command line________________________________________ + +1) choose a directory in which you want to build the vm + (the build process will create a subdirectory) + +2) To build the 'core' version (debug || optimized) + %HotSpotWorkSpace%\build\windows\build core %HotSpotWorkSpace% + To build the 'compiler2' version (debug || optimized) + %HotSpotWorkSpace%\build\windows\build compiler2 %HotSpotWorkSpace% + + where is a full path to a JDK in which bin/java and + bin/javac are present and working. + +3) If you have problems with building, first try: + vcvars32 (sets path for VC++) + +4) In addition to jvm.dll, the Serviceability Agent (SA) based JDI connector + and command line tools are built if dbgeng.h and dbgeng.lib + can be located, and BUILD_WIN_SA=1 is specified. We look for dbgeng.h here: + $(MSVCDIR)\PlatformSDK\Include + $(SYSTEMROOT)\..\Program Files\Microsoft SDK\include + + The first directory is part of Visual Studio VC .NET 2003. + The second is used on Windows-amd64. + + +__Building the JVM from within MS Developer Studio______________________________ + +0) Set environment variables as described above + +1) Run the following script: + %HotSpotWorkSpace%\build\windows\create { } + where type is one of core, compiler1, compiler2. If you leave off the + " " part, the script expects to find their + values in the HotSpotWorkSpace, HotSpotBuildSpace, HotSpotReleaseBinDest, and HotSpotDebugBinDest environment + variables. The resulting vm.vcproj does not depend on these values in the environment. + + This will populate the build space with the appropriate makefiles + and run nmake in it. This builds and runs makedeps, which now + generates the appropriate vm.vcproj into the build space. It also + builds and runs adlc. + + To regenerate the .incl and .dsp files after changing the include + databases, just run nmake in the build space. + + The build process now relies on java and javac. For the IDE builds, + the full path to a JDK (in which bin/java and bin/javac are present + and working) can be specified either explicitly with the + ALT_BOOTDIR environment variable (like the JDK build process), via + the JDK build's default BOOTDIR environment variable, via JAVA_HOME, + or implicitly via the PATH. + + (Note that there are now many more command line options to MakeDeps + on the Windows platform than before. These have been bundled into + makefiles/makedeps.make, but it is still necessary to keep this in + sync with the batch makefiles, in vm/generated.) + + If you have problems with building (i.e,. finding nmake), first try: + vcvars32 (sets path for VC++) + +2) Double-click the vm.vcproj file in the %HotSpotBuildSpace% directory + to open MS Developer Studio. + +3) build desired or all versions: + menu Build -> Batch Build... -> Build (or Rebuild All) + +4) jvm.dll is in the %HotSpotReleaseBinDest% or %HotSpotDebugBinDest% directory + depending on which configuration you built (release or debug). + +Note: do not edit any of the files (especially the vm.vcproj file) in the +build space, since they are all either autogenerated or copied from +the work space. If necessary, modify the original Makefiles in +%HotSpotWorkSpace%\build\windows\projectfiles, or the shared +makedeps arguments in +%HotSpotWorkSpace%\build\windows\makefiles\makedeps.make. + +Note that it appears that some options set in the IDE (for example, +the default executable) show up not in the .dsp file, but in the .opt +file, so the automatic regeneration of the .dsp file should not +destroy the project settings. However, makedeps.make should be edited +to supply per-file compiler options. + +To build adlc from within the IDE for debugging purposes: + +1) in MS Developer Studio, open ADLCompiler.dsw: + menu File -> Open Workspace... + select & double-click ADLCompiler.dsw + +2) rebuild all (debug mode is enough) + menu Build -> Rebuild All (make sure Win32 Debug version is selected) + + +__Testing the VM________________________________________________________________ + +To test the VM using the Tonga Testsuite, use testlook. testlook is a very +simple testing framework on top of Tonga which allows us to use one (Tonga) +test file, that can be extended with attributes. + +1) copy %HotSpotWorkSpace%\test\testlook.bat onto PC (preferably + %HotSpotBuildSpace%\bin, which should ideally be in the path) + +2) run testlook or testlook help for details + +3) to run testlook you need to have Tonga mounted: + net use T: \\tapas\export1\psqe + + +__HotJava under HotSpot_________________________________________________________ + +To run HotJava, use the .bat file %HotSpotWorkSpace%\test\h.bat. Copy +it into %HotSpotBuildSpace%/ (which ideally is in the path) and run +HotJava: h java (e.g., h java_g -Xint). + + +__Preferred directory setup under Windows NT____________________________________ + +Within the HotSpot group we are using the following directory setup: + +D:\jdk1.2 - where we install the JDK + +The following drives are mounted for testing/putbacks/etc.: + +net use T: \\tapas\export1\psqe +net use Y: \\rschmidt\GammaBase +net use Z: \\animorphic\animorphic diff --git a/hotspot/build/windows/build.bat b/hotspot/build/windows/build.bat new file mode 100644 index 00000000000..7b7329b2098 --- /dev/null +++ b/hotspot/build/windows/build.bat @@ -0,0 +1,107 @@ +@echo off +REM +REM Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + + +REM +REM Since we don't have uname and we could be cross-compiling, +REM Use the compiler to determine which ARCH we are building +REM +cl 2>&1 | grep "IA-64" >NUL +if %errorlevel% == 0 goto isia64 +cl 2>&1 | grep "AMD64" >NUL +if %errorlevel% == 0 goto amd64 +set ARCH=x86 +set BUILDARCH=i486 +set Platform_arch=x86 +set Platform_arch_model=x86_32 +goto end +:amd64 +set LP64=1 +set ARCH=x86 +set BUILDARCH=amd64 +set Platform_arch=x86 +set Platform_arch_model=x86_64 +goto end +:isia64 +set LP64=1 +set ARCH=ia64 +set Platform_arch=ia64 +set Platform_arch_model=ia64 +:end + +if "%4" == "" goto usage +if not "%7" == "" goto usage + +if "%1" == "product" goto test1 +if "%1" == "debug" goto test1 +if "%1" == "fastdebug" goto test1 +goto usage + +:test1 +if "%2" == "core" goto test2 +if "%2" == "kernel" goto test2 +if "%2" == "compiler1" goto test2 +if "%2" == "compiler2" goto test2 +if "%2" == "tiered" goto test2 +if "%2" == "adlc" goto build_adlc + +goto usage + +:test2 +REM check_j2se_version +REM jvmti.make requires J2SE 1.4.x or newer. +REM If not found then fail fast. +%4\bin\javap javax.xml.transform.TransformerFactory >NUL +if %errorlevel% == 0 goto build +echo. +echo J2SE version found at %4\bin\java: +%4\bin\java -version +echo. +echo An XSLT processor (J2SE 1.4.x or newer) is required to +echo bootstrap this build +echo. + +goto usage + +:build +nmake -f %3/build/windows/build.make Variant=%2 WorkSpace=%3 BootStrapDir=%4 BuildUser="%USERNAME%" HOTSPOT_BUILD_VERSION="%5" %1 +goto end + +:build_adlc +nmake -f %3/build/windows/build.make Variant=compiler2 WorkSpace=%3 BootStrapDir=%4 BuildUser="%USERNAME%" HOTSPOT_BUILD_VERSION=%5 ADLC_ONLY=1 %1 +goto end + +:usage +echo Usage: build flavor version workspace bootstrap_dir [build_id] [windbg_home] +echo. +echo where: +echo flavor is "product", "debug" or "fastdebug", +echo version is "core", "kernel", "compiler1", "compiler2", or "tiered", +echo workspace is source directory without trailing slash, +echo bootstrap_dir is a full path to echo a JDK in which bin/java +echo and bin/javac are present and working, and echo build_id is an +echo optional build identifier displayed by java -version + +:end diff --git a/hotspot/build/windows/build.make b/hotspot/build/windows/build.make new file mode 100644 index 00000000000..c5e48e1715d --- /dev/null +++ b/hotspot/build/windows/build.make @@ -0,0 +1,309 @@ +# +# Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Note: this makefile is invoked both from build.bat and from the J2SE +# control workspace in exactly the same manner; the required +# environment variables (Variant, WorkSpace, BootStrapDir, BuildUser, HOTSPOT_BUILD_VERSION) +# are passed in as command line arguments. + +# SA components are built if BUILD_WIN_SA=1 is specified. +# See notes in README. This produces files: +# 1. sa-jdi.jar - This is built before building jvm.dll +# 2. sawindbg[_g].dll - Native library for SA - This is built after jvm.dll +# - Also, .lib, .map, .pdb. +# +# Please refer to ./makefiles/sa.make + +# If we haven't set an ARCH yet use x86 +# create.bat and build.bat will set it, if used. +!ifndef ARCH +ARCH=x86 +!endif + + +# Must be one of these values (if value comes in from env, can't trust it) +!if "$(ARCH)" != "x86" +!if "$(ARCH)" != "ia64" +ARCH=x86 +!endif +!endif + +# At this point we should be certain that ARCH has a definition +# now determine the BUILDARCH +# + +# the default BUILDARCH +BUILDARCH=i486 + +# Allow control workspace to force Itanium or AMD64 builds with LP64 +ARCH_TEXT= +!ifdef LP64 +!if "$(LP64)" == "1" +ARCH_TEXT=64-Bit +!if "$(ARCH)" == "x86" +BUILDARCH=amd64 +!else +BUILDARCH=ia64 +!endif +!endif +!endif + +!if "$(BUILDARCH)" != "amd64" +!if "$(BUILDARCH)" != "ia64" +!ifndef CC_INTERP +FORCE_TIERED=1 +!endif +!endif +!endif + +!if "$(BUILDARCH)" == "amd64" +Platform_arch=x86 +Platform_arch_model=x86_64 +!endif +!if "$(BUILDARCH)" == "i486" +Platform_arch=x86 +Platform_arch_model=x86_32 +!endif + +# Supply these from the command line or the environment +# It doesn't make sense to default this one +Variant= +# It doesn't make sense to default this one +WorkSpace= + +variantDir = windows_$(BUILDARCH)_$(Variant) + +realVariant=$(Variant) +VARIANT_TEXT=Core +!if "$(Variant)" == "compiler1" +VARIANT_TEXT=Client +!elseif "$(Variant)" == "compiler2" +!ifdef FORCE_TIERED +VARIANT_TEXT=Server +realVariant=tiered +!else +VARIANT_TEXT=Server +!endif +!elseif "$(Variant)" == "tiered" +VARIANT_TEXT=Tiered +!elseif "$(Variant)" == "kernel" +VARIANT_TEXT=Kernel +!endif + +######################################################################### +# Parameters for VERSIONINFO resource for jvm[_g].dll. +# These can be overridden via the nmake.exe command line. +# They are overridden by RE during the control builds. +# +!include "$(WorkSpace)/make/hotspot_version" + +# Define HOTSPOT_VM_DISTRO based on settings in build/hotspot_distro +# or build/closed/hotspot_distro. +!ifndef HOTSPOT_VM_DISTRO +!if exists($(WorkSpace)\build\closed) +!include $(WorkSpace)\build\closed\hotspot_distro +!else +!include $(WorkSpace)\build\hotspot_distro +!endif +!endif + +# Following the Web Start / Plugin model here.... +# We can have update versions like "01a", but Windows requires +# we use only integers in the file version field. So: +# JDK_UPDATE_VER = JDK_UPDATE_VERSION * 10 + EXCEPTION_VERSION +# +JDK_UPDATE_VER=0 +JDK_BUILD_NUMBER=0 + +HS_FILEDESC=$(HOTSPOT_VM_DISTRO) $(ARCH_TEXT) $(VARIANT_TEXT) VM + +# JDK ProductVersion: +# 1.5.0_-b will have DLL version 5.0.wx*10.yz +# Thus, 1.5.0_10-b04 will be 5.0.100.4 +# 1.6.0-b01 will be 6.0.0.1 +# 1.6.0_01a-b02 will be 6.0.11.2 +# +# JDK_* variables are defined in make/hotspot_version or on command line +# +JDK_VER=$(JDK_MINOR_VER),$(JDK_MICRO_VER),$(JDK_UPDATE_VER),$(JDK_BUILD_NUMBER) +JDK_DOTVER=$(JDK_MINOR_VER).$(JDK_MICRO_VER).$(JDK_UPDATE_VER).$(JDK_BUILD_NUMBER) +!if "$(JRE_RELEASE_VERSION)" == "" +JRE_RELEASE_VER=$(JDK_MAJOR_VER).$(JDK_MINOR_VER).$(JDK_MICRO_VER) +!else +JRE_RELEASE_VER=$(JRE_RELEASE_VERSION) +!endif +!if "$(JDK_MKTG_VERSION)" == "" +JDK_MKTG_VERSION=$(JDK_MINOR_VER).$(JDK_MICRO_VER) +!endif + +# Hotspot Express VM FileVersion: +# 10.0-b will have DLL version 10.0.0.yz (need 4 numbers). +# +# HS_* variables are defined in make/hotspot_version +# +HS_VER=$(HS_MAJOR_VER),$(HS_MINOR_VER),0,$(HS_BUILD_NUMBER) +HS_DOTVER=$(HS_MAJOR_VER).$(HS_MINOR_VER).0.$(HS_BUILD_NUMBER) + +!if "$(HOTSPOT_RELEASE_VERSION)" == "" +HOTSPOT_RELEASE_VERSION=$(HS_MAJOR_VER).$(HS_MINOR_VER)-b$(HS_BUILD_NUMBER) +!endif + +!if "$(HOTSPOT_BUILD_VERSION)" == "" +HS_BUILD_VER=$(HOTSPOT_RELEASE_VERSION) +!else +HS_BUILD_VER=$(HOTSPOT_RELEASE_VERSION)-$(HOTSPOT_BUILD_VERSION) +!endif + +# End VERSIONINFO parameters + + +# We don't support SA on ia64, and we can't +# build it if we are using a version of Vis Studio +# older than .Net 2003. +# SA_INCLUDE and SA_LIB are hold-overs from a previous +# implementation in which we could build SA using +# Debugging Tools For Windows, in which the .h/.lib files +# and the .dlls are in different places than +# they are for Vis Studio .Net 2003. +# If that code ever needs to be resurrected, these vars +# can be set here. They are used in makefiles/sa.make. + +checkSA:: + +!if "$(BUILD_WIN_SA)" != "1" +checkSA:: + @echo Not building SA: BUILD_WIN_SA != 1 + +!elseif "$(ARCH)" == "ia64" +BUILD_WIN_SA = 0 +checkSA:: + @echo Not building SA: ARCH = ia64 + +!elseif exist("$(MSVCDIR)\PlatformSDK\Include\dbgeng.h") +# These don't have to be set because the default +# setting of INCLUDE and LIB already contain the needed dirs. +SA_INCLUDE = +SA_LIB = + +!elseif exist("$(SYSTEMROOT)\..\Program Files\Microsoft SDK\include\dbgeng.h") +# These don't have to be set because the default +# setting of INCLUDE and LIB already contain the needed dirs. +SA_INCLUDE = +SA_LIB = + +!else +checkSA:: + @echo . + @echo ERROR: Can't build SA because dbgeng.h does not exist here: + @echo $(MSVCDIR)\PlatformSDK\Include\dbgeng.h + @echo nor here: + @echo $(SYSTEMROOT)\..\Program Files\Microsoft SDK\include\dbgeng.h + @echo You must use Vis. Studio .Net 2003 on Win 32, and you must + @echo have the Microsoft SDK installed on Win amd64. + @echo You can disable building of SA by specifying BUILD_WIN_SA = 0 + @echo . && false +!endif # ! "$(BUILD_WIN_SA)" != "1" + +######################################################################### + +# With the jvm_g.dll now being named jvm.dll, we can't build both and place +# the dll's in the same directory, so we only build one at a time, +# re-directing the output to different output directories (done by user +# of this makefile). +# +defaultTarget: product + +# The product or release build is an optimized build, and is the default + +# note that since all the build targets depend on local.make that BUILDARCH +# and Platform_arch and Platform_arch_model will get set in local.make +# and there is no need to pass them thru here on the command line +# +product release optimized: checks $(variantDir) $(variantDir)\local.make sanity + cd $(variantDir) + nmake -nologo -f $(WorkSpace)\build\windows\makefiles\top.make BUILD_FLAVOR=product ARCH=$(ARCH) + +# The debug or jvmg (all the same thing) is an optional build +debug jvmg: checks $(variantDir) $(variantDir)\local.make sanity + cd $(variantDir) + nmake -nologo -f $(WorkSpace)\build\windows\makefiles\top.make BUILD_FLAVOR=debug ARCH=$(ARCH) +fastdebug: checks $(variantDir) $(variantDir)\local.make sanity + cd $(variantDir) + nmake -nologo -f $(WorkSpace)\build\windows\makefiles\top.make BUILD_FLAVOR=fastdebug ARCH=$(ARCH) + +develop: checks $(variantDir) $(variantDir)\local.make sanity + cd $(variantDir) + nmake -nologo -f $(WorkSpace)\build\windows\makefiles\top.make BUILD_FLAVOR=product DEVELOP=1 ARCH=$(ARCH) + +sanity: + @ echo; + @ cd $(variantDir) + @ nmake -nologo -f $(WorkSpace)\build\windows\makefiles\sanity.make + @ cd .. + @ echo; + +clean: checkVariant + - rm -r -f $(variantDir) + +$(variantDir): + mkdir $(variantDir) + +$(variantDir)\local.make: checks + @ echo # Generated file > $@ + @ echo Variant=$(realVariant) >> $@ + @ echo WorkSpace=$(WorkSpace) >> $@ + @ echo BootStrapDir=$(BootStrapDir) >> $@ + @ if "$(USERNAME)" NEQ "" echo BuildUser=$(USERNAME) >> $@ + @ echo HS_VER=$(HS_VER) >> $@ + @ echo HS_DOTVER=$(HS_DOTVER) >> $@ + @ echo HS_COMPANY=$(COMPANY_NAME) >> $@ + @ echo HS_FILEDESC=$(HS_FILEDESC) >> $@ + @ echo HOTSPOT_VM_DISTRO=$(HOTSPOT_VM_DISTRO) >> $@ + @ echo HS_COPYRIGHT=$(HOTSPOT_VM_COPYRIGHT) >> $@ + @ echo HS_NAME=$(PRODUCT_NAME) $(JDK_MKTG_VERSION) >> $@ + @ echo HS_BUILD_VER=$(HS_BUILD_VER) >> $@ + @ echo BUILD_WIN_SA=$(BUILD_WIN_SA) >> $@ + @ echo SA_BUILD_VERSION=$(HS_BUILD_VER) >> $@ + @ echo SA_INCLUDE=$(SA_INCLUDE) >> $@ + @ echo SA_LIB=$(SA_LIB) >> $@ + @ echo JDK_VER=$(JDK_VER) >> $@ + @ echo JDK_DOTVER=$(JDK_DOTVER) >> $@ + @ echo JRE_RELEASE_VER=$(JRE_RELEASE_VER) >> $@ + @ echo BUILDARCH=$(BUILDARCH) >> $@ + @ echo Platform_arch=$(Platform_arch) >> $@ + @ echo Platform_arch_model=$(Platform_arch_model) >> $@ + @ sh $(WorkSpace)/build/windows/get_msc_ver.sh >> $@ + +checks: checkVariant checkWorkSpace checkSA + +checkVariant: + @ if "$(Variant)"=="" echo Need to specify "Variant=[tiered|compiler2|compiler1|kernel|core]" && false + @ if "$(Variant)" NEQ "tiered" if "$(Variant)" NEQ "compiler2" if "$(Variant)" NEQ "compiler1" if "$(Variant)" NEQ "kernel" if "$(Variant)" NEQ "core" \ + echo Need to specify "Variant=[tiered|compiler2|compiler1|kernel|core]" && false + +checkWorkSpace: + @ if "$(WorkSpace)"=="" echo Need to specify "WorkSpace=..." && false + +checkBuildID: + @ if "$(BuildID)"=="" echo Need to specify "BuildID=..." && false diff --git a/hotspot/build/windows/build_vm_def.sh b/hotspot/build/windows/build_vm_def.sh new file mode 100644 index 00000000000..4ccdb67d281 --- /dev/null +++ b/hotspot/build/windows/build_vm_def.sh @@ -0,0 +1,57 @@ +# +# Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This shell script builds a vm.def file for the current VM variant. +# The .def file exports vtbl symbols which allow the Serviceability +# Agent to run on Windows. See build/windows/projectfiles/*/vm.def +# for more information. +# +# The script expects to be executed in the directory containing all of +# the object files. + +# Note that we currently do not have a way to set HotSpotMksHome in +# the batch build, but so far this has not seemed to be a problem. The +# reason this environment variable is necessary is that it seems that +# Windows truncates very long PATHs when executing shells like MKS's +# sh, and it has been found that sometimes `which sh` fails. +if [ "x$HOTSPOTMKSHOME" != "x" ]; then + MKS_HOME="$HOTSPOTMKSHOME" +else + SH=`which sh` + MKS_HOME=`dirname "$SH"` +fi + +echo "EXPORTS" > vm1.def + +AWK="$MKS_HOME/awk.exe" +GREP="$MKS_HOME/grep.exe" +SORT="$MKS_HOME/sort.exe" +UNIQ="$MKS_HOME/uniq.exe" +CAT="$MKS_HOME/cat.exe" +RM="$MKS_HOME/rm.exe" +DUMPBIN="link.exe /dump" + +$DUMPBIN /symbols *.obj | "$GREP" "??_7.*@@6B@" | "$AWK" '{print $7}' | "$SORT" | "$UNIQ" > vm2.def +"$CAT" vm1.def vm2.def > vm.def +"$RM" -f vm1.def vm2.def diff --git a/hotspot/build/windows/create.bat b/hotspot/build/windows/create.bat new file mode 100644 index 00000000000..71ad50d0bd6 --- /dev/null +++ b/hotspot/build/windows/create.bat @@ -0,0 +1,179 @@ +@echo off +REM +REM Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +REM This is the interactive build setup script (as opposed to the batch +REM build execution script). It creates $HotSpotBuildSpace if necessary, +REM copies the appropriate files out of $HotSpotWorkSpace into it, and +REM builds and runs MakeDeps in it. This has the side-effect of creating +REM the vm.vcproj file in the buildspace, which is then used in Visual C++. +REM +REM The generated project file depends upon the include databases. If +REM those are changed then MakeDeps is rerun. + +REM +REM Since we don't have uname and we could be cross-compiling, +REM Use the compiler to determine which ARCH we are building +REM +cl 2>&1 | grep "IA-64" >NUL +if %errorlevel% == 0 goto isia64 +cl 2>&1 | grep "AMD64" >NUL +if %errorlevel% == 0 goto amd64 +set ARCH=x86 +set BUILDARCH=i486 +set Platform_arch=x86 +set Platform_arch_model=x86_32 +goto end +:amd64 +set ARCH=x86 +set BUILDARCH=amd64 +set Platform_arch=x86 +set Platform_arch_model=x86_64 +goto end +:isia64 +set ARCH=ia64 +set BUILDARCH=ia64 +set Platform_arch=ia64 +set Platform_arch_model=ia64 +:end + +setlocal + +if "%1" == "" goto usage + +if not "%4" == "" goto usage + +set HotSpotWorkSpace=%1 +set HotSpotBuildSpace=%2 +set HotSpotJDKDist=%3 + +REM figure out MSC version +for /F %%i in ('sh %HotSpotWorkSpace%/build/windows/get_msc_ver.sh') do set %%i + +echo ************************************************************** +if "%MSC_VER%" == "1200" ( +set ProjectFile=vm.dsp +echo Will generate VC6 project {unsupported} +) else ( +set ProjectFile=vm.vcproj +echo Will generate VC7 project +) +echo %ProjectFile% +echo ************************************************************** + +REM Test all variables to see whether the directories they +REM reference exist + +if exist %HotSpotWorkSpace% goto test1 + +echo Error: directory pointed to by HotSpotWorkSpace +echo does not exist, or the variable is not set. +echo. +goto usage + +:test1 +if exist %HotSpotBuildSpace% goto test2 +if not "%HotSpotBuildSpace%" == "" mkdir %HotSpotBuildSpace% +if exist %HotSpotBuildSpace% goto test2 +echo Error: directory pointed to by HotSpotBuildSpace +echo does not exist, or the variable is not set. +echo. +goto usage + +:test2 +if exist %HotSpotJDKDist% goto test3 +echo Error: directory pointed to by %HotSpotJDKDist% +echo does not exist, or the variable is not set. +echo. +goto usage + +:test3 +if not "%HOTSPOTMKSHOME%" == "" goto makedir +echo Warning: please set variable HOTSPOTMKSHOME to place where +echo your MKS/Cygwin installation is +echo. +goto usage + +:makedir +echo NOTE: Using the following settings: +echo HotSpotWorkSpace=%HotSpotWorkSpace% +echo HotSpotBuildSpace=%HotSpotBuildSpace% +echo HotSpotJDKDist=%HotSpotJDKDist% + + +REM This is now safe to do. +:copyfiles +for /D %%i in (compiler1, compiler2, tiered, core, kernel) do ( +if NOT EXIST %HotSpotBuildSpace%\%%i mkdir %HotSpotBuildSpace%\%%i +copy %HotSpotWorkSpace%\build\windows\projectfiles\%%i\* %HotSpotBuildSpace%\%%i\ > NUL +) + +REM force regneration of ProjectFile +if exist %HotSpotBuildSpace%\%ProjectFile% del %HotSpotBuildSpace%\%ProjectFile% + +for /D %%i in (compiler1, compiler2, tiered, core, kernel) do ( + +echo # Generated file! > %HotSpotBuildSpace%\%%i\local.make +echo # Changing a variable below and then deleting %ProjectFile% will cause >> %HotSpotBuildSpace%\%%i\local.make +echo # %ProjectFile% to be regenerated with the new values. Changing the >> %HotSpotBuildSpace%\%%i\local.make +echo # version requires rerunning create.bat. >> %HotSpotBuildSpace%\%%i\local.make +echo. >> %HotSpotBuildSpace%\%%i\local.make +echo HOTSPOTWORKSPACE=%HotSpotWorkSpace% >> %HotSpotBuildSpace%\%%i\local.make +echo HOTSPOTBUILDSPACE=%HotSpotBuildSpace% >> %HotSpotBuildSpace%\%%i\local.make +echo HOTSPOTJDKDIST=%HotSpotJDKDist% >> %HotSpotBuildSpace%\%%i\local.make +echo ARCH=%ARCH% >> %HotSpotBuildSpace%\%%i\local.make +echo BUILDARCH=%BUILDARCH% >> %HotSpotBuildSpace%\%%i\local.make +echo Platform_arch=%Platform_arch% >> %HotSpotBuildSpace%\%%i\local.make +echo Platform_arch_model=%Platform_arch_model% >> %HotSpotBuildSpace%\%%i\local.make + +REM build config specific stuff + +pushd %HotSpotBuildSpace%\%%i +nmake /nologo +popd +) + +goto end + +:usage +echo Usage: create HotSpotWorkSpace HotSpotBuildSpace HotSpotJDKDist +echo. +echo This is the interactive build setup script (as opposed to the batch +echo build execution script). It creates HotSpotBuildSpace if necessary, +echo copies the appropriate files out of HotSpotWorkSpace into it, and +echo builds and runs MakeDeps in it. This has the side-effect of creating +echo the %ProjectFile% file in the build space, which is then used in Visual C++. +echo The HotSpotJDKDist defines place where JVM binaries should be placed. +echo Environment variable FORCE_MSC_VER allows to override MSVC version autodetection. +echo. +echo The generated project file depends upon the include databases. If +echo those are changed then MakeDeps is rerun. +echo. +echo NOTE that it is now NOT safe to modify any of the files in the build +echo space, since they may be overwritten whenever this script is run or +echo nmake is run in that directory. + +:end + +endlocal diff --git a/hotspot/build/windows/cross_build.bat b/hotspot/build/windows/cross_build.bat new file mode 100644 index 00000000000..ef362caa07e --- /dev/null +++ b/hotspot/build/windows/cross_build.bat @@ -0,0 +1,61 @@ +@echo off +REM +REM Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +REM DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +REM +REM This code is free software; you can redistribute it and/or modify it +REM under the terms of the GNU General Public License version 2 only, as +REM published by the Free Software Foundation. +REM +REM This code is distributed in the hope that it will be useful, but WITHOUT +REM ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +REM FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +REM version 2 for more details (a copy is included in the LICENSE file that +REM accompanied this code). +REM +REM You should have received a copy of the GNU General Public License version +REM 2 along with this work; if not, write to the Free Software Foundation, +REM Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +REM +REM Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +REM CA 95054 USA or visit www.sun.com if you need additional information or +REM have any questions. +REM +REM + +REM Cross compile IA64 compiler2 VM +REM Usage: +REM cross_compile flavor workspace bootstrap_dir [build_id] +REM %1 %2 %3 %4 +REM +REM Set current directory +for /F %%i in ('cd') do set CD=%%i +echo Setting up Visual C++ Compilation Environment +if "%MSVCDir%" == "" goto setdir1 +goto setenv1 +:setdir1 +SET MSVCDir=C:\Program Files\Microsoft Visual Studio\VC98 +:setenv1 +SET OLDINCLUDE=%INCLUDE% +SET OLDLIB=%LIB% +SET OLDPATH=%PATH% +call "%MSVCDir%\Bin\VCVARS32" +call %2\build\windows\build %1 adlc %2 %3 %4 +SET INCLUDE=%OLDINCLUDE% +SET LIB=%OLDLIB% +SET PATH=%OLDPATH% +echo Setting up 64-BIT Compilation Environment +if "%MSSdk%" == "" goto setdir2 +goto setenv2 +:setdir2 +SET MSSdk=C:\Program Files\Microsoft SDK +:setenv2 +call "%MSSdk%\SetEnv.bat" /XP64 +SET ALT_ADLC_PATH=%CD%\windows_i486_compiler2\generated +call %2\build\windows\build %1 compiler2 %2 %3 %4 +SET INCLUDE=%OLDINCLUDE% +SET LIB=%OLDLIB% +SET PATH=%OLDPATH% +SET OLDINCLUDE= +SET OLDLIB= +SET OLDPATH= diff --git a/hotspot/build/windows/get_msc_ver.sh b/hotspot/build/windows/get_msc_ver.sh new file mode 100644 index 00000000000..1e4f6a02489 --- /dev/null +++ b/hotspot/build/windows/get_msc_ver.sh @@ -0,0 +1,79 @@ +# +# Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This shell script echoes "MSC_VER=" +# It ignores the micro version component. +# Examples: +# cl version 12.00.8804 returns "MSC_VER=1200" +# cl version 13.10.3077 returns "MSC_VER=1310" +# cl version 14.00.30701 returns "MSC_VER=1399" (OLD_MSSDK version) +# cl version 14.00.40310.41 returns "MSC_VER=1400" + +# Note that we currently do not have a way to set HotSpotMksHome in +# the batch build, but so far this has not seemed to be a problem. The +# reason this environment variable is necessary is that it seems that +# Windows truncates very long PATHs when executing shells like MKS's +# sh, and it has been found that sometimes `which sh` fails. + +if [ "x$HotSpotMksHome" != "x" ]; then + MKS_HOME="$HotSpotMksHome" +else + SH=`which sh` + MKS_HOME=`dirname "$SH"` +fi + +HEAD="$MKS_HOME/head" +ECHO="$MKS_HOME/echo" +EXPR="$MKS_HOME/expr" +CUT="$MKS_HOME/cut" +SED="$MKS_HOME/sed" + +if [ "x$FORCE_MSC_VER" != "x" ]; then + echo "MSC_VER=$FORCE_MSC_VER" +else + MSC_VER_RAW=`cl 2>&1 | "$HEAD" -n 1 | "$SED" 's/.*Version[\ ]*\([0-9][0-9.]*\).*/\1/'` + MSC_VER_MAJOR=`"$ECHO" $MSC_VER_RAW | "$CUT" -d'.' -f1` + MSC_VER_MINOR=`"$ECHO" $MSC_VER_RAW | "$CUT" -d'.' -f2` + MSC_VER_MICRO=`"$ECHO" $MSC_VER_RAW | "$CUT" -d'.' -f3` + if [ "${MSC_VER_MAJOR}" -eq 14 -a "${MSC_VER_MINOR}" -eq 0 -a "${MSC_VER_MICRO}" -eq 30701 ] ; then + # This said 1400 but it was really more like VS2003 (VC7) in terms of options + MSC_VER=1399 + else + MSC_VER=`"$EXPR" $MSC_VER_MAJOR \* 100 + $MSC_VER_MINOR` + fi + echo "MSC_VER=$MSC_VER" + echo "MSC_VER_RAW=$MSC_VER_RAW" +fi + +if [ "x$FORCE_LINK_VER" != "x" ]; then + echo "LINK_VER=$FORCE_LINK_VER" +else + LINK_VER_RAW=`link 2>&1 | "$HEAD" -n 1 | "$SED" 's/.*Version[\ ]*\([0-9][0-9.]*\).*/\1/'` + LINK_VER_MAJOR=`"$ECHO" $LINK_VER_RAW | "$CUT" -d'.' -f1` + LINK_VER_MINOR=`"$ECHO" $LINK_VER_RAW | "$CUT" -d'.' -f2` + LINK_VER_MICRO=`"$ECHO" $LINK_VER_RAW | "$CUT" -d'.' -f3` + LINK_VER=`"$EXPR" $LINK_VER_MAJOR \* 100 + $LINK_VER_MINOR` + echo "LINK_VER=$LINK_VER" + echo "LINK_VER_RAW=$LINK_VER_RAW" +fi diff --git a/hotspot/build/windows/jvmexp.lcf b/hotspot/build/windows/jvmexp.lcf new file mode 100644 index 00000000000..6489d02e266 --- /dev/null +++ b/hotspot/build/windows/jvmexp.lcf @@ -0,0 +1,10 @@ +-export:JNI_GetDefaultJavaVMInitArgs +-export:JNI_CreateJavaVM +-export:JNI_GetCreatedJavaVMs + +-export:jio_snprintf +-export:jio_printf +-export:jio_fprintf +-export:jio_vfprintf +-export:jio_vsnprintf + diff --git a/hotspot/build/windows/jvmexp_g.lcf b/hotspot/build/windows/jvmexp_g.lcf new file mode 100644 index 00000000000..6489d02e266 --- /dev/null +++ b/hotspot/build/windows/jvmexp_g.lcf @@ -0,0 +1,10 @@ +-export:JNI_GetDefaultJavaVMInitArgs +-export:JNI_CreateJavaVM +-export:JNI_GetCreatedJavaVMs + +-export:jio_snprintf +-export:jio_printf +-export:jio_fprintf +-export:jio_vfprintf +-export:jio_vsnprintf + diff --git a/hotspot/build/windows/makefiles/adlc.make b/hotspot/build/windows/makefiles/adlc.make new file mode 100644 index 00000000000..8dee8c3e6dd --- /dev/null +++ b/hotspot/build/windows/makefiles/adlc.make @@ -0,0 +1,114 @@ +# +# Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +!include $(WorkSpace)/build/windows/makefiles/compile.make + +# Rules for building adlc.exe + +# Need exception handling support here +# $(MS_RUNTIME_OPTION) ( with /D_STATIC_CPPLIB) +# causes adlc.exe to link with the static +# multithread Standard C++ library (libcpmt.lib) instead of +# the dynamic version (msvcprt.lib), which is not included +# in any of the free tools. +EXH_FLAGS=$(GX_OPTION) $(MS_RUNTIME_OPTION) + +!ifdef ALT_ADLC_PATH +ADLC=$(ALT_ADLC_PATH)\adlc.exe +!else +ADLC=adlc +!endif + +!ifdef LP64 +ADLCFLAGS=-q -T -D_LP64 +!else +ADLCFLAGS=-q -T -U_LP64 +!endif + + +CPP_INCLUDE_DIRS=\ + /I "..\generated" \ + /I "$(WorkSpace)\src\share\vm\compiler" \ + /I "$(WorkSpace)\src\share\vm\code" \ + /I "$(WorkSpace)\src\share\vm\interpreter" \ + /I "$(WorkSpace)\src\share\vm\classfile" \ + /I "$(WorkSpace)\src\share\vm\asm" \ + /I "$(WorkSpace)\src\share\vm\memory" \ + /I "$(WorkSpace)\src\share\vm\oops" \ + /I "$(WorkSpace)\src\share\vm\prims" \ + /I "$(WorkSpace)\src\share\vm\runtime" \ + /I "$(WorkSpace)\src\share\vm\utilities" \ + /I "$(WorkSpace)\src\share\vm\libadt" \ + /I "$(WorkSpace)\src\share\vm\opto" \ + /I "$(WorkSpace)\src\os\windows\vm" \ + /I "$(WorkSpace)\src\cpu\$(Platform_arch)\vm" + +# NOTE! If you add any files here, you must also update GENERATED_NAMES_IN_INCL +# and MakeDepsIDEOptions in makedeps.make. +GENERATED_NAMES=\ + ad_$(Platform_arch_model).cpp \ + ad_$(Platform_arch_model).hpp \ + ad_$(Platform_arch_model)_clone.cpp \ + ad_$(Platform_arch_model)_expand.cpp \ + ad_$(Platform_arch_model)_format.cpp \ + ad_$(Platform_arch_model)_gen.cpp \ + ad_$(Platform_arch_model)_misc.cpp \ + ad_$(Platform_arch_model)_peephole.cpp \ + ad_$(Platform_arch_model)_pipeline.cpp \ + adGlobals_$(Platform_arch_model).hpp \ + dfa_$(Platform_arch_model).cpp + +# NOTE! This must be kept in sync with GENERATED_NAMES +GENERATED_NAMES_IN_INCL=\ + incls/ad_$(Platform_arch_model).cpp \ + incls/ad_$(Platform_arch_model).hpp \ + incls/ad_$(Platform_arch_model)_clone.cpp \ + incls/ad_$(Platform_arch_model)_expand.cpp \ + incls/ad_$(Platform_arch_model)_format.cpp \ + incls/ad_$(Platform_arch_model)_gen.cpp \ + incls/ad_$(Platform_arch_model)_misc.cpp \ + incls/ad_$(Platform_arch_model)_peephole.cpp \ + incls/ad_$(Platform_arch_model)_pipeline.cpp \ + incls/adGlobals_$(Platform_arch_model).hpp \ + incls/dfa_$(Platform_arch_model).cpp + +{$(WorkSpace)\src\share\vm\adlc}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(EXH_FLAGS) $(CPP_INCLUDE_DIRS) /c $< + +{$(WorkSpace)\src\share\vm\opto}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(EXH_FLAGS) $(CPP_INCLUDE_DIRS) /c $< + +adlc.exe: main.obj adlparse.obj archDesc.obj arena.obj dfa.obj dict2.obj filebuff.obj \ + forms.obj formsopt.obj formssel.obj opcodes.obj output_c.obj output_h.obj + $(LINK) $(LINK_FLAGS) /subsystem:console /out:$@ $** + +$(GENERATED_NAMES_IN_INCL): $(Platform_arch_model).ad adlc.exe includeDB.current + rm -f $(GENERATED_NAMES) + $(ADLC) $(ADLCFLAGS) $(Platform_arch_model).ad + mv $(GENERATED_NAMES) incls/ + +$(Platform_arch_model).ad: $(WorkSpace)/src/cpu/$(Platform_arch)/vm/$(Platform_arch_model).ad $(WorkSpace)/src/os_cpu/windows_$(Platform_arch)/vm/windows_$(Platform_arch_model).ad + rm -f $(Platform_arch_model).ad + cat $(WorkSpace)/src/cpu/$(Platform_arch)/vm/$(Platform_arch_model).ad \ + $(WorkSpace)/src/os_cpu/windows_$(Platform_arch)/vm/windows_$(Platform_arch_model).ad >$(Platform_arch_model).ad diff --git a/hotspot/build/windows/makefiles/compile.make b/hotspot/build/windows/makefiles/compile.make new file mode 100644 index 00000000000..6bc4ed1ddce --- /dev/null +++ b/hotspot/build/windows/makefiles/compile.make @@ -0,0 +1,204 @@ +# +# Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Generic compiler settings +CPP=cl.exe + +# CPP Flags: (these vary slightly from VC6->VS2003->VS2005 compilers) +# /nologo Supress copyright message at every cl.exe startup +# /W3 Warning level 3 +# /Zi Include debugging information +# /WX Treat any warning error as a fatal error +# /MD Use dynamic multi-threaded runtime (msvcrt.dll or msvc*71.dll) +# /MTd Use static multi-threaded runtime debug versions +# /O1 Optimize for size (/Os), skips /Oi +# /O2 Optimize for speed (/Ot), adds /Oi to /O1 +# /Ox Old "all optimizations flag" for VC6 (in /O1) +# /Oy Use frame pointer register as GP reg (in /Ox and /O1) +# /GF Merge string constants and put in read-only memory (in /O1) +# /Gy Func level link (in /O1, allows for link-time func ordering) +# /Gs Inserts stack probes (in /O1) +# /GS Inserts security stack checks in some functions (VS2005 default) +# /Oi Use intrinsics (in /O2) +# /Od Disable all optimizations +# +# NOTE: Normally following any of the above with a '-' will turn off that flag + +# These are always used in all compiles +CPP_FLAGS=/nologo /W3 /WX + +# Let's add debug information always too. +CPP_FLAGS=$(CPP_FLAGS) /Zi + +# Based on BUILDARCH we add some flags and select the default compiler name +!if "$(BUILDARCH)" == "ia64" +MACHINE=IA64 +DEFAULT_COMPILER_NAME=VS2003 +CPP_FLAGS=$(CPP_FLAGS) /D "CC_INTERP" /D "_LP64" /D "IA64" +!endif + +!if "$(BUILDARCH)" == "amd64" +MACHINE=AMD64 +DEFAULT_COMPILER_NAME=VS2005 +CPP_FLAGS=$(CPP_FLAGS) /D "_LP64" /D "AMD64" +LP64=1 +!endif + +!if "$(BUILDARCH)" == "i486" +MACHINE=I386 +DEFAULT_COMPILER_NAME=VS2003 +CPP_FLAGS=$(CPP_FLAGS) /D "IA32" +!endif + +# Sanity check, this is the default if not amd64, ia64, or i486 +!ifndef DEFAULT_COMPILER_NAME +CPP=ARCH_ERROR +!endif + +# MSC_VER is a 4 digit number that tells us what compiler is being used, it is +# generated when the local.make file is created by the script gen_msc_ver.sh. +# If MSC_VER is set, it overrides the above default setting. +# But it should be set. +# Possible values: +# 1200 is for VC6 +# 1300 and 1310 is VS2003 or VC7 +# 1399 is our fake number for the VS2005 compiler that really isn't 1400 +# 1400 is for VS2005 +# Do not confuse this MSC_VER with the predefined macro _MSC_VER that the +# compiler provides, when MSC_VER==1399, _MSC_VER will be 1400. +# Normally they are the same, but a pre-release of the VS2005 compilers +# in the Windows 64bit Platform SDK said it was 1400 when it was really +# closer to VS2003 in terms of option spellings, so we use 1399 for that +# 1400 version that really isn't 1400. +# See the file gen_msc_ver.sh for more info. +!if "x$(MSC_VER)" == "x" +COMPILER_NAME=$(DEFAULT_COMPILER_NAME) +!else +!if "$(MSC_VER)" == "1200" +COMPILER_NAME=VC6 +!endif +!if "$(MSC_VER)" == "1300" +COMPILER_NAME=VS2003 +!endif +!if "$(MSC_VER)" == "1310" +COMPILER_NAME=VS2003 +!endif +!if "$(MSC_VER)" == "1399" +# Compiler might say 1400, but if it's 14.00.30701, it isn't really VS2005 +COMPILER_NAME=VS2003 +!endif +!if "$(MSC_VER)" == "1400" +COMPILER_NAME=VS2005 +!endif +!endif + +# Add what version of the compiler we think this is to the compile line +CPP_FLAGS=$(CPP_FLAGS) /D "MSC_VER=$(MSC_VER)" + +# By default, we do not want to use the debug version of the msvcrt.dll file +# but if MFC_DEBUG is defined in the environment it will be used. +MS_RUNTIME_OPTION = /MD +!if "$(MFC_DEBUG)" == "true" +MS_RUNTIME_OPTION = /MTd /D "_DEBUG" +!endif + +# Always add the _STATIC_CPPLIB flag +STATIC_CPPLIB_OPTION = /D _STATIC_CPPLIB +MS_RUNTIME_OPTION = $(MS_RUNTIME_OPTION) $(STATIC_CPPLIB_OPTION) +CPP_FLAGS=$(CPP_FLAGS) $(MS_RUNTIME_OPTION) + +# How /GX option is spelled +GX_OPTION = /GX + +# Optimization settings for various versions of the compilers and types of +# builds. Three basic sets of settings: product, fastdebug, and debug. +# These get added into CPP_FLAGS as needed by other makefiles. +!if "$(COMPILER_NAME)" == "VC6" +PRODUCT_OPT_OPTION = /Ox /Os /Gy /GF +FASTDEBUG_OPT_OPTION = /Ox /Os /Gy /GF +DEBUG_OPT_OPTION = /Od +!endif + +!if "$(COMPILER_NAME)" == "VS2003" +PRODUCT_OPT_OPTION = /O2 +FASTDEBUG_OPT_OPTION = /O2 +DEBUG_OPT_OPTION = /Od +!endif + +!if "$(COMPILER_NAME)" == "VS2005" +PRODUCT_OPT_OPTION = /O2 +FASTDEBUG_OPT_OPTION = /O2 +DEBUG_OPT_OPTION = /Od +GX_OPTION = /EHsc +# This VS2005 compiler has /GS as a default and requires bufferoverflowU.lib +# on the link command line, otherwise we get missing __security_check_cookie +# externals at link time. Even with /GS-, you need bufferoverflowU.lib. +# NOTE: Currently we decided to not use /GS- +BUFFEROVERFLOWLIB = bufferoverflowU.lib +LINK_FLAGS = $(LINK_FLAGS) $(BUFFEROVERFLOWLIB) +!if "$(BUILDARCH)" == "i486" +# VS2005 on x86 restricts the use of certain libc functions without this +CPP_FLAGS=$(CPP_FLAGS) /D _CRT_SECURE_NO_DEPRECATE +!endif +!endif + +# Compile for space above time. +!if "$(Variant)" == "kernel" +PRODUCT_OPT_OPTION = /O1 +FASTDEBUG_OPT_OPTION = /O1 +DEBUG_OPT_OPTION = /Od +!endif + +# If NO_OPTIMIZATIONS is defined in the environment, turn everything off +!ifdef NO_OPTIMIZATIONS +PRODUCT_OPT_OPTION = $(DEBUG_OPT_OPTION) +FASTDEBUG_OPT_OPTION = $(DEBUG_OPT_OPTION) +!endif + +# Generic linker settings +LINK=link.exe +LINK_FLAGS= $(LINK_FLAGS) kernel32.lib user32.lib gdi32.lib winspool.lib \ + comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib \ + uuid.lib Wsock32.lib winmm.lib /nologo /machine:$(MACHINE) /opt:REF \ + /opt:ICF,8 /map /debug + +# Resource compiler settings +RC=rc.exe +RC_FLAGS=/D "HS_VER=$(HS_VER)" \ + /D "HS_DOTVER=$(HS_DOTVER)" \ + /D "HS_BUILD_ID=$(HS_BUILD_ID)" \ + /D "JDK_VER=$(JDK_VER)" \ + /D "JDK_DOTVER=$(JDK_DOTVER)" \ + /D "HS_COMPANY=$(HS_COMPANY)" \ + /D "HS_FILEDESC=$(HS_FILEDESC)" \ + /D "HS_COPYRIGHT=$(HS_COPYRIGHT)" \ + /D "HS_FNAME=$(HS_FNAME)" \ + /D "HS_INTERNAL_NAME=$(HS_INTERNAL_NAME)" \ + /D "HS_NAME=$(HS_NAME)" + +# Need this to match the CPP_FLAGS settings +!if "$(MFC_DEBUG)" == "true" +RC_FLAGS = $(RC_FLAGS) /D "_DEBUG" +!endif + diff --git a/hotspot/build/windows/makefiles/debug.make b/hotspot/build/windows/makefiles/debug.make new file mode 100644 index 00000000000..de7924173e5 --- /dev/null +++ b/hotspot/build/windows/makefiles/debug.make @@ -0,0 +1,55 @@ +# +# Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +HS_INTERNAL_NAME=jvm +HS_FNAME=$(HS_INTERNAL_NAME).dll +AOUT=$(HS_FNAME) +SAWINDBG=sawindbg.dll +GENERATED=../generated + +default:: _build_pch_file.obj $(AOUT) checkAndBuildSA + +!include ../local.make +!include compile.make + +CPP_FLAGS=$(CPP_FLAGS) $(DEBUG_OPT_OPTION) + +!include $(WorkSpace)/build/windows/makefiles/vm.make +!include local.make + +!include $(GENERATED)/Dependencies + +HS_BUILD_ID=$(HS_BUILD_VER)-debug + +# Force resources to be rebuilt every time +$(Res_Files): FORCE + +$(AOUT): $(Res_Files) $(Obj_Files) + sh $(WorkSpace)/build/windows/build_vm_def.sh + $(LINK) @<< + $(LINK_FLAGS) /out:$@ /implib:$*.lib /def:vm.def $(Obj_Files) $(Res_Files) +<< + +!include $(WorkSpace)/build/windows/makefiles/shared.make +!include $(WorkSpace)/build/windows/makefiles/sa.make diff --git a/hotspot/build/windows/makefiles/defs.make b/hotspot/build/windows/makefiles/defs.make new file mode 100644 index 00000000000..1e1ab9fba47 --- /dev/null +++ b/hotspot/build/windows/makefiles/defs.make @@ -0,0 +1,160 @@ +# +# Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# The common definitions for hotspot windows builds. +# Include the top level defs.make under make directory instead of this one. +# This file is included into make/defs.make. +# On windows it is only used to construct parameters for +# build/windows/build.make when make/Makefile is used to build VM. + +SLASH_JAVA ?= J: +PATH_SEP = ; + +# Need PLATFORM (os-arch combo names) for jdk and hotspot, plus libarch name +ifneq ($(shell $(ECHO) $(PROCESSOR_IDENTIFIER) | $(GREP) x86),) + ARCH_DATA_MODEL=32 + PLATFORM=windows-i586 + VM_PLATFORM=windows_i486 + HS_ARCH=x86 + MAKE_ARGS += ARCH=x86 + MAKE_ARGS += BUILDARCH=i486 + MAKE_ARGS += Platform_arch=x86 + MAKE_ARGS += Platform_arch_model=x86_32 +endif + +ifneq ($(shell $(ECHO) $(PROCESSOR_IDENTIFIER) | $(GREP) ia64),) + ARCH_DATA_MODEL=64 + PLATFORM=windows-ia64 + VM_PLATFORM=windows_ia64 + HS_ARCH=ia64 + MAKE_ARGS += LP64=1 + MAKE_ARGS += ARCH=ia64 + MAKE_ARGS += BUILDARCH=ia64 + MAKE_ARGS += Platform_arch=ia64 + MAKE_ARGS += Platform_arch_model=ia64 +endif + +ifneq ($(shell $(ECHO) $(PROCESSOR_IDENTIFIER) | $(GREP) AMD64),) + ARCH_DATA_MODEL=64 + PLATFORM=windows-amd64 + VM_PLATFORM=windows_amd64 + HS_ARCH=x86 + MAKE_ARGS += LP64=1 + MAKE_ARGS += ARCH=x86 + MAKE_ARGS += BUILDARCH=amd64 + MAKE_ARGS += Platform_arch=x86 + MAKE_ARGS += Platform_arch_model=x86_64 +endif + +JDK_INCLUDE_SUBDIR=win32 + +# HOTSPOT_RELEASE_VERSION and HOTSPOT_BUILD_VERSION are defined +# and added to MAKE_ARGS list in $(GAMMADIR)/make/defs.make. + +# next parameters are defined in $(GAMMADIR)/make/defs.make. +MAKE_ARGS += JDK_MKTG_VERSION=$(JDK_MKTG_VERSION) +MAKE_ARGS += JDK_MAJOR_VER=$(JDK_MAJOR_VERSION) +MAKE_ARGS += JDK_MINOR_VER=$(JDK_MINOR_VERSION) +MAKE_ARGS += JDK_MICRO_VER=$(JDK_MICRO_VERSION) + +ifdef COOKED_JDK_UPDATE_VERSION + MAKE_ARGS += JDK_UPDATE_VER=$(COOKED_JDK_UPDATE_VERSION) +endif + +# COOKED_BUILD_NUMBER should only be set if we have a numeric +# build number. It must not be zero padded. +ifdef COOKED_BUILD_NUMBER + MAKE_ARGS += JDK_BUILD_NUMBER=$(COOKED_BUILD_NUMBER) +endif + +NMAKE= MAKEFLAGS= MFLAGS= nmake /NOLOGO + +# Check for CYGWIN +ifneq (,$(findstring CYGWIN,$(shell uname))) + USING_CYGWIN=true +else + USING_CYGWIN=false +endif +# FIXUP: The subdirectory for a debug build is NOT the same on all platforms +VM_DEBUG=debug + +# Windows wants particular paths due to nmake (must be after macros defined) +# It is important that gnumake invokes nmake with C:\\...\\ formated +# strings so that nmake gets C:\...\ style strings. +# Check for CYGWIN +ifeq ($(USING_CYGWIN), true) + ABS_OUTPUTDIR := $(subst /,\\,$(shell /bin/cygpath -m -a "$(OUTPUTDIR)")) + ABS_BOOTDIR := $(subst /,\\,$(shell /bin/cygpath -m -a "$(BOOTDIR)")) + ABS_GAMMADIR := $(subst /,\\,$(shell /bin/cygpath -m -a "$(GAMMADIR)")) + ABS_OS_MAKEFILE := $(shell /bin/cygpath -m -a "$(HS_BUILD_DIR)/$(OSNAME)")/build.make +else + ABS_OUTPUTDIR := $(subst /,\\,$(shell $(CD) $(OUTPUTDIR);$(PWD))) + ABS_BOOTDIR := $(subst /,\\,$(shell $(CD) $(BOOTDIR);$(PWD))) + ABS_GAMMADIR := $(subst /,\\,$(shell $(CD) $(GAMMADIR);$(PWD))) + ABS_OS_MAKEFILE := $(subst /,\\,$(shell $(CD) $(HS_BUILD_DIR)/$(OSNAME);$(PWD))/build.make) +endif + +# Disable building SA on windows until we are sure +# we want to release it. If we build it here, +# the SDK makefiles will copy it over and put it into +# the created image. +BUILD_WIN_SA = 0 +ifneq ($(ALT_BUILD_WIN_SA),) + BUILD_WIN_SA = $(ALT_BUILD_WIN_SA) +endif + +ifeq ($(BUILD_WIN_SA), 1) + ifeq ($(ARCH),ia64) + BUILD_WIN_SA = 0 + endif +endif + +EXPORT_SERVER_DIR = $(EXPORT_JRE_BIN_DIR)/server +EXPORT_LIST += $(EXPORT_SERVER_DIR)/Xusage.txt +EXPORT_LIST += $(EXPORT_SERVER_DIR)/jvm.dll +EXPORT_LIST += $(EXPORT_SERVER_DIR)/jvm.pdb +EXPORT_LIST += $(EXPORT_SERVER_DIR)/jvm.map +EXPORT_LIST += $(EXPORT_LIB_DIR)/jvm.lib +ifeq ($(ARCH_DATA_MODEL), 32) + EXPORT_CLIENT_DIR = $(EXPORT_JRE_BIN_DIR)/client + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/Xusage.txt + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/jvm.dll + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/jvm.pdb + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/jvm.map + # kernel vm + EXPORT_KERNEL_DIR = $(EXPORT_JRE_BIN_DIR)/kernel + EXPORT_LIST += $(EXPORT_KERNEL_DIR)/Xusage.txt + EXPORT_LIST += $(EXPORT_KERNEL_DIR)/jvm.dll + EXPORT_LIST += $(EXPORT_KERNEL_DIR)/jvm.pdb + EXPORT_LIST += $(EXPORT_KERNEL_DIR)/jvm.map +endif + +ifeq ($(BUILD_WIN_SA), 1) + EXPORT_LIST += $(EXPORT_JRE_BIN_DIR)/sawindbg.dll + EXPORT_LIST += $(EXPORT_JRE_BIN_DIR)/sawindbg.pdb + EXPORT_LIST += $(EXPORT_JRE_BIN_DIR)/sawindbg.map + EXPORT_LIST += $(EXPORT_LIB_DIR)/sa-jdi.jar + # Must pass this down to nmake. + MAKE_ARGS += BUILD_WIN_SA=1 +endif diff --git a/hotspot/build/windows/makefiles/fastdebug.make b/hotspot/build/windows/makefiles/fastdebug.make new file mode 100644 index 00000000000..dcbacdc9449 --- /dev/null +++ b/hotspot/build/windows/makefiles/fastdebug.make @@ -0,0 +1,55 @@ +# +# Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +HS_INTERNAL_NAME=jvm +HS_FNAME=$(HS_INTERNAL_NAME).dll +AOUT=$(HS_FNAME) +SAWINDBG=sawindbg.dll +GENERATED=../generated + +default:: _build_pch_file.obj $(AOUT) checkAndBuildSA + +!include ../local.make +!include compile.make + +CPP_FLAGS=$(CPP_FLAGS) $(FASTDEBUG_OPT_OPTION) + +!include $(WorkSpace)/build/windows/makefiles/vm.make +!include local.make + +!include $(GENERATED)/Dependencies + +HS_BUILD_ID=$(HS_BUILD_VER)-fastdebug + +# Force resources to be rebuilt every time +$(Res_Files): FORCE + +$(AOUT): $(Res_Files) $(Obj_Files) + sh $(WorkSpace)/build/windows/build_vm_def.sh + $(LINK) @<< + $(LINK_FLAGS) /out:$@ /implib:$*.lib /def:vm.def $(Obj_Files) $(Res_Files) +<< + +!include $(WorkSpace)/build/windows/makefiles/shared.make +!include $(WorkSpace)/build/windows/makefiles/sa.make diff --git a/hotspot/build/windows/makefiles/generated.make b/hotspot/build/windows/makefiles/generated.make new file mode 100644 index 00000000000..8def591058d --- /dev/null +++ b/hotspot/build/windows/makefiles/generated.make @@ -0,0 +1,101 @@ +# +# Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +!include ../local.make +!include $(WorkSpace)/build/windows/makefiles/makedeps.make +!include local.make + +# Pick up rules for building JVMTI (JSR-163) +JvmtiOutDir=jvmtifiles +!include $(WorkSpace)/build/windows/makefiles/jvmti.make + +# Pick up rules for building SA +!include $(WorkSpace)/build/windows/makefiles/sa.make + +!if ("$(Variant)" == "compiler2") || ("$(Variant)" == "tiered") +default:: includeDB.current Dependencies incls/ad_$(Platform_arch_model).cpp incls/dfa_$(Platform_arch_model).cpp $(JvmtiGeneratedFiles) +!else +default:: includeDB.current Dependencies $(JvmtiGeneratedFiles) +!endif + +# core plus serial gc +IncludeDBs_base=$(WorkSpace)/src/share/vm/includeDB_core \ + $(WorkSpace)/src/share/vm/includeDB_jvmti \ + $(WorkSpace)/src/share/vm/includeDB_gc \ + $(WorkSpace)/src/share/vm/gc_implementation/includeDB_gc_serial + +# parallel gc +IncludeDBs_gc= $(WorkSpace)/src/share/vm/includeDB_gc_parallel \ + $(WorkSpace)/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge \ + $(WorkSpace)/src/share/vm/gc_implementation/includeDB_gc_shared \ + $(WorkSpace)/src/share/vm/gc_implementation/includeDB_gc_parNew \ + $(WorkSpace)/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep + +IncludeDBs_core=$(IncludeDBs_base) $(IncludeDBs_gc) \ + $(WorkSpace)/src/share/vm/includeDB_features + +!if "$(Variant)" == "core" +IncludeDBs=$(IncludeDBs_core) +!endif + +!if "$(Variant)" == "kernel" +IncludeDBs=$(IncludeDBs_base) $(WorkSpace)/src/share/vm/includeDB_compiler1 +!endif + +!if "$(Variant)" == "compiler1" +IncludeDBs=$(IncludeDBs_core) $(WorkSpace)/src/share/vm/includeDB_compiler1 +!endif + + +!if "$(Variant)" == "compiler2" +IncludeDBs=$(IncludeDBs_core) $(WorkSpace)/src/share/vm/includeDB_compiler2 +!endif + +!if "$(Variant)" == "tiered" +IncludeDBs=$(IncludeDBs_core) $(WorkSpace)/src/share/vm/includeDB_compiler1 \ + $(WorkSpace)/src/share/vm/includeDB_compiler2 +!endif + +# Note we don't generate a Visual C++ project file using MakeDeps for +# the batch build. +includeDB.current Dependencies: classes/MakeDeps.class $(IncludeDBs) + cat $(IncludeDBs) > includeDB + if exist incls rmdir /s /q incls + mkdir incls + $(RUN_JAVA) -Djava.class.path=classes MakeDeps WinGammaPlatform$(VcVersion) $(WorkSpace)/build/windows/platform_$(BUILDARCH) includeDB $(MakeDepsOptions) + rm -f includeDB.current + cp includeDB includeDB.current + +classes/MakeDeps.class: $(MakeDepsSources) + if exist classes rmdir /s /q classes + mkdir classes + $(COMPILE_JAVAC) -classpath $(WorkSpace)\src\share\tools\MakeDeps -g -d classes $(MakeDepsSources) + +!if ("$(Variant)" == "compiler2") || ("$(Variant)" == "tiered") + +!include $(WorkSpace)/build/windows/makefiles/adlc.make + +!endif + +!include $(WorkSpace)/build/windows/makefiles/shared.make diff --git a/hotspot/build/windows/makefiles/jvmti.make b/hotspot/build/windows/makefiles/jvmti.make new file mode 100644 index 00000000000..3bd5ce6e2f3 --- /dev/null +++ b/hotspot/build/windows/makefiles/jvmti.make @@ -0,0 +1,114 @@ +# +# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This makefile (jvmti.make) is included from the jvmti.make in the +# build directories. +# +# It knows how to build and run the tools to generate jvmti. + +!include $(WorkSpace)/build/windows/makefiles/rules.make + +# ######################################################################### + +JvmtiSrcDir = $(WorkSpace)/src/share/vm/prims +InterpreterSrcDir = $(WorkSpace)/src/share/vm/interpreter + +JvmtiGeneratedNames = \ + jvmtiEnv.hpp \ + jvmtiEnter.cpp \ + jvmtiEnterTrace.cpp \ + jvmtiEnvRecommended.cpp \ + bytecodeInterpreterWithChecks.cpp \ + jvmti.h \ + +JvmtiEnvFillSource = $(JvmtiSrcDir)/jvmtiEnvFill.java +JvmtiEnvFillClass = $(JvmtiOutDir)/jvmtiEnvFill.class + +JvmtiGenSource = $(JvmtiSrcDir)/jvmtiGen.java +JvmtiGenClass = $(JvmtiOutDir)/jvmtiGen.class + +#Note: JvmtiGeneratedFiles must be kept in sync with JvmtiGeneratedNames by hand. +#Should be equivalent #to "JvmtiGeneratedFiles = $(JvmtiGeneratedNames:%=$(JvmtiOutDir)/%)" +JvmtiGeneratedFiles = \ + $(JvmtiOutDir)/jvmtiEnv.hpp \ + $(JvmtiOutDir)/jvmtiEnter.cpp \ + $(JvmtiOutDir)/jvmtiEnterTrace.cpp \ + $(JvmtiOutDir)/jvmtiEnvRecommended.cpp\ + $(JvmtiOutDir)/bytecodeInterpreterWithChecks.cpp\ + $(JvmtiOutDir)/jvmti.h \ + +XSLT = $(RUN_JAVA) -classpath $(JvmtiOutDir) jvmtiGen + +# ######################################################################### + +both = $(JvmtiGenClass) $(JvmtiSrcDir)/jvmti.xml $(JvmtiSrcDir)/jvmtiLib.xsl + +default:: + @if not exist $(JvmtiOutDir) mkdir $(JvmtiOutDir) + +$(JvmtiGenClass): $(JvmtiGenSource) + $(COMPILE_JAVAC) -g -d $(JvmtiOutDir) $(JvmtiGenSource) + +$(JvmtiEnvFillClass): $(JvmtiEnvFillSource) + @$(COMPILE_JAVAC) -g -d $(JvmtiOutDir) $(JvmtiEnvFillSource) + +$(JvmtiOutDir)/jvmtiEnter.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl + @echo Generating $@ + @$(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnter.xsl -OUT $(JvmtiOutDir)/jvmtiEnter.cpp -PARAM interface jvmti + +$(JvmtiOutDir)/bytecodeInterpreterWithChecks.cpp: $(JvmtiGenClass) $(InterpreterSrcDir)/bytecodeInterpreter.cpp $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xml $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xsl + @echo Generating $@ + @$(XSLT) -IN $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xml -XSL $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xsl -OUT $(JvmtiOutDir)/bytecodeInterpreterWithChecks.cpp + +$(JvmtiOutDir)/jvmtiEnterTrace.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl + @echo Generating $@ + @$(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnter.xsl -OUT $(JvmtiOutDir)/jvmtiEnterTrace.cpp -PARAM interface jvmti -PARAM trace Trace + +$(JvmtiOutDir)/jvmtiEnvRecommended.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnv.xsl $(JvmtiSrcDir)/jvmtiEnv.cpp $(JvmtiEnvFillClass) + @echo Generating $@ + @$(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnv.xsl -OUT $(JvmtiOutDir)/jvmtiEnvStub.cpp + @$(RUN_JAVA) -classpath $(JvmtiOutDir) jvmtiEnvFill $(JvmtiSrcDir)/jvmtiEnv.cpp $(JvmtiOutDir)/jvmtiEnvStub.cpp $(JvmtiOutDir)/jvmtiEnvRecommended.cpp + +$(JvmtiOutDir)/jvmtiEnv.hpp: $(both) $(JvmtiSrcDir)/jvmtiHpp.xsl + @echo Generating $@ + @$(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiHpp.xsl -OUT $(JvmtiOutDir)/jvmtiEnv.hpp + +$(JvmtiOutDir)/jvmti.h: $(both) $(JvmtiSrcDir)/jvmtiH.xsl + @echo Generating $@ + @$(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiH.xsl -OUT $(JvmtiOutDir)/jvmti.h + +jvmtidocs: $(JvmtiOutDir)/jvmti.html + +$(JvmtiOutDir)/jvmti.html: $(both) $(JvmtiSrcDir)/jvmti.xsl + @echo Generating $@ + @$(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmti.xsl -OUT $(JvmtiOutDir)/jvmti.html + +# ######################################################################### + +cleanall : + rm $(JvmtiGenClass) $(JvmtiEnvFillClass) $(JvmtiGeneratedFiles) + +# ######################################################################### + +.PHONY: jvmtidocs cleanall diff --git a/hotspot/build/windows/makefiles/makedeps.make b/hotspot/build/windows/makefiles/makedeps.make new file mode 100644 index 00000000000..f82f4264858 --- /dev/null +++ b/hotspot/build/windows/makefiles/makedeps.make @@ -0,0 +1,173 @@ +# +# Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +!include $(WorkSpace)/build/windows/makefiles/rules.make + +# This is used externally by both batch and IDE builds, so can't +# reference any of the HOTSPOTWORKSPACE, HOTSPOTBUILDSPACE, +# HOTSPOTRELEASEBINDEST, or HOTSPOTDEBUGBINDEST environment variables. +# +# NOTE: unfortunately the MakeDepsSources list must be kept +# synchronized between this and the Solaris version +# (build/solaris/makefiles/makedeps.make). + +MakeDepsSources=\ + $(WorkSpace)\src\share\tools\MakeDeps\Database.java \ + $(WorkSpace)\src\share\tools\MakeDeps\DirectoryTree.java \ + $(WorkSpace)\src\share\tools\MakeDeps\DirectoryTreeNode.java \ + $(WorkSpace)\src\share\tools\MakeDeps\FileFormatException.java \ + $(WorkSpace)\src\share\tools\MakeDeps\FileList.java \ + $(WorkSpace)\src\share\tools\MakeDeps\FileName.java \ + $(WorkSpace)\src\share\tools\MakeDeps\Macro.java \ + $(WorkSpace)\src\share\tools\MakeDeps\MacroDefinitions.java \ + $(WorkSpace)\src\share\tools\MakeDeps\MakeDeps.java \ + $(WorkSpace)\src\share\tools\MakeDeps\MetroWerksMacPlatform.java \ + $(WorkSpace)\src\share\tools\MakeDeps\Platform.java \ + $(WorkSpace)\src\share\tools\MakeDeps\UnixPlatform.java \ + $(WorkSpace)\src\share\tools\MakeDeps\WinGammaPlatform.java \ + $(WorkSpace)\src\share\tools\MakeDeps\WinGammaPlatformVC6.java \ + $(WorkSpace)\src\share\tools\MakeDeps\WinGammaPlatformVC7.java \ + $(WorkSpace)\src\share\tools\MakeDeps\Util.java \ + $(WorkSpace)\src\share\tools\MakeDeps\BuildConfig.java \ + $(WorkSpace)\src\share\tools\MakeDeps\ArgsParser.java + +# This is only used internally +MakeDepsIncludesPRIVATE=\ + -relativeInclude src\share\vm\c1 \ + -relativeInclude src\share\vm\compiler \ + -relativeInclude src\share\vm\code \ + -relativeInclude src\share\vm\interpreter \ + -relativeInclude src\share\vm\ci \ + -relativeInclude src\share\vm\classfile \ + -relativeInclude src\share\vm\gc_implementation\parallelScavenge \ + -relativeInclude src\share\vm\gc_implementation\shared \ + -relativeInclude src\share\vm\gc_implementation\parNew \ + -relativeInclude src\share\vm\gc_implementation\concurrentMarkSweep \ + -relativeInclude src\share\vm\gc_interface \ + -relativeInclude src\share\vm\asm \ + -relativeInclude src\share\vm\memory \ + -relativeInclude src\share\vm\oops \ + -relativeInclude src\share\vm\prims \ + -relativeInclude src\share\vm\runtime \ + -relativeInclude src\share\vm\services \ + -relativeInclude src\share\vm\utilities \ + -relativeInclude src\share\vm\libadt \ + -relativeInclude src\share\vm\opto \ + -relativeInclude src\os\windows\vm \ + -relativeInclude src\os_cpu\windows_$(Platform_arch)\vm \ + -relativeInclude src\cpu\$(Platform_arch)\vm + +# This is referenced externally by both the IDE and batch builds +MakeDepsOptions= + +# This is used externally, but only by the IDE builds, so we can +# reference environment variables which aren't defined in the batch +# build process. + +MakeDepsIDEOptions = \ + -useToGeneratePch java.cpp \ + -disablePch os_windows.cpp \ + -disablePch os_windows_$(Platform_arch).cpp \ + -disablePch osThread_windows.cpp \ + -disablePch bytecodeInterpreter.cpp \ + -disablePch bytecodeInterpreterWithChecks.cpp \ + -disablePch getThread_windows_$(Platform_arch).cpp \ + -disablePch_compiler2 opcodes.cpp + +# Common options for the IDE builds for core, c1, and c2 +MakeDepsIDEOptions=\ + $(MakeDepsIDEOptions) \ + -sourceBase $(HOTSPOTWORKSPACE) \ + -buildBase $(HOTSPOTBUILDSPACE)\%f\%b \ + -startAt src \ + -compiler $(VcVersion) \ + -projectFileName $(HOTSPOTBUILDSPACE)\$(ProjectFile) \ + -jdkTargetRoot $(HOTSPOTJDKDIST) \ + -define ALIGN_STACK_FRAMES \ + -define VM_LITTLE_ENDIAN \ + -additionalFile includeDB_compiler1 \ + -additionalFile includeDB_compiler2 \ + -additionalFile includeDB_core \ + -additionalFile includeDB_features \ + -additionalFile includeDB_jvmti \ + -additionalFile includeDB_gc \ + -additionalFile includeDB_gc_parallel \ + -additionalFile includeDB_gc_parallelScavenge \ + -additionalFile includeDB_gc_concurrentMarkSweep \ + -additionalFile includeDB_gc_parNew \ + -additionalFile includeDB_gc_shared \ + -additionalFile includeDB_gc_serial \ + -additionalGeneratedFile $(HOTSPOTBUILDSPACE)\%f\%b vm.def \ + -prelink "" "Generating vm.def..." "cd $(HOTSPOTBUILDSPACE)\%f\%b $(HOTSPOTMKSHOME)\sh $(HOTSPOTWORKSPACE)\build\windows\build_vm_def.sh" \ + $(MakeDepsIncludesPRIVATE) + +# Add in build-specific options +!if "$(BUILDARCH)" == "i486" +MakeDepsIDEOptions=$(MakeDepsIDEOptions) -define IA32 +!endif + +################################################## +# JKERNEL specific options +################################################## +MakeDepsIDEOptions=$(MakeDepsIDEOptions) \ + -define_kernel KERNEL \ + +################################################## +# Client(C1) compiler specific options +################################################## +MakeDepsIDEOptions=$(MakeDepsIDEOptions) \ + -define_compiler1 COMPILER1 \ + +################################################## +# Server(C2) compiler specific options +################################################## +#NOTE! This list must be kept in sync with GENERATED_NAMES in adlc.make. +MakeDepsIDEOptions=$(MakeDepsIDEOptions) \ + -define_compiler2 COMPILER2 \ + -absoluteInclude_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls \ + -additionalFile_compiler2 $(Platform_arch_model).ad \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls ad_$(Platform_arch_model).cpp \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls ad_$(Platform_arch_model).hpp \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls ad_$(Platform_arch_model)_clone.cpp \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls ad_$(Platform_arch_model)_expand.cpp \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls ad_$(Platform_arch_model)_format.cpp \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls ad_$(Platform_arch_model)_gen.cpp \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls ad_$(Platform_arch_model)_misc.cpp \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls ad_$(Platform_arch_model)_peephole.cpp \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls ad_$(Platform_arch_model)_pipeline.cpp \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls adGlobals_$(Platform_arch_model).hpp \ + -additionalGeneratedFile_compiler2 $(HOTSPOTBUILDSPACE)/%f/incls dfa_$(Platform_arch_model).cpp + +# Add in the jvmti (JSR-163) options +# NOTE: do not pull in jvmtiEnvRecommended.cpp. This file is generated +# so the programmer can diff it with jvmtiEnv.cpp to be sure the +# code merge was done correctly (@see jvmti.make and jvmtiEnvFill.java). +# If so, they would then check it in as a new version of jvmtiEnv.cpp. +MakeDepsIDEOptions=$(MakeDepsIDEOptions) \ + -absoluteInclude $(HOTSPOTBUILDSPACE)/jvmtifiles \ + -additionalGeneratedFile $(HOTSPOTBUILDSPACE)/jvmtifiles jvmtiEnv.hpp \ + -additionalGeneratedFile $(HOTSPOTBUILDSPACE)/jvmtifiles jvmtiEnter.cpp \ + -additionalGeneratedFile $(HOTSPOTBUILDSPACE)/jvmtifiles jvmtiEnterTrace.cpp \ + -additionalGeneratedFile $(HOTSPOTBUILDSPACE)/jvmtifiles jvmti.h \ + -additionalGeneratedFile $(HOTSPOTBUILDSPACE)/jvmtifiles bytecodeInterpreterWithChecks.cpp diff --git a/hotspot/build/windows/makefiles/product.make b/hotspot/build/windows/makefiles/product.make new file mode 100644 index 00000000000..d6fe73d125b --- /dev/null +++ b/hotspot/build/windows/makefiles/product.make @@ -0,0 +1,66 @@ +# +# Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +HS_INTERNAL_NAME=jvm +HS_FNAME=$(HS_INTERNAL_NAME).dll +AOUT=$(HS_FNAME) +GENERATED=../generated + +default:: _build_pch_file.obj $(AOUT) checkAndBuildSA + +!include ../local.make +!include compile.make + +CPP_FLAGS=$(CPP_FLAGS) $(PRODUCT_OPT_OPTION) + +RELEASE= + +RC_FLAGS=$(RC_FLAGS) /D "NDEBUG" + +!include $(WorkSpace)/build/windows/makefiles/vm.make +!include local.make + +!include $(GENERATED)/Dependencies + +HS_BUILD_ID=$(HS_BUILD_VER) + +# Force resources to be rebuilt every time +$(Res_Files): FORCE + +# Kernel doesn't need exported vtbl symbols. +!if "$(Variant)" == "kernel" +$(AOUT): $(Res_Files) $(Obj_Files) + $(LINK) @<< + $(LINK_FLAGS) /out:$@ /implib:$*.lib $(Obj_Files) $(Res_Files) +<< +!else +$(AOUT): $(Res_Files) $(Obj_Files) + sh $(WorkSpace)/build/windows/build_vm_def.sh + $(LINK) @<< + $(LINK_FLAGS) /out:$@ /implib:$*.lib /def:vm.def $(Obj_Files) $(Res_Files) +<< +!endif + +!include $(WorkSpace)/build/windows/makefiles/shared.make +!include $(WorkSpace)/build/windows/makefiles/sa.make diff --git a/hotspot/build/windows/makefiles/rules.make b/hotspot/build/windows/makefiles/rules.make new file mode 100644 index 00000000000..04ee8e399c5 --- /dev/null +++ b/hotspot/build/windows/makefiles/rules.make @@ -0,0 +1,51 @@ +# +# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# These are the commands used externally to compile and run. + +!ifdef BootStrapDir +RUN_JAVA=$(BootStrapDir)\bin\java +RUN_JAVAP=$(BootStrapDir)\bin\javap +RUN_JAVAH=$(BootStrapDir)\bin\javah +RUN_JAR=$(BootStrapDir)\bin\jar +COMPILE_JAVAC=$(BootStrapDir)\bin\javac +COMPILE_RMIC=$(BootStrapDir)\bin\rmic +BOOT_JAVA_HOME=$(BootStrapDir) +!else +RUN_JAVA=java +RUN_JAVAP=javap +RUN_JAVAH=javah +RUN_JAR=jar +COMPILE_JAVAC=javac +COMPILE_RMIC=rmic +BOOT_JAVA_HOME= +!endif + +!if "$(MSC_VER)" == "1200" +VcVersion=VC6 +ProjectFile=vm.dsp +!else +VcVersion=VC7 +ProjectFile=vm.vcproj +!endif diff --git a/hotspot/build/windows/makefiles/sa.make b/hotspot/build/windows/makefiles/sa.make new file mode 100644 index 00000000000..0c649dcaf94 --- /dev/null +++ b/hotspot/build/windows/makefiles/sa.make @@ -0,0 +1,110 @@ +# +# Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# This makefile is used to build Serviceability Agent code +# and generate JNI header file for native methods. + +AGENT_DIR = $(WorkSpace)/agent +checkAndBuildSA:: + +!if "$(BUILD_WIN_SA)" != "1" +# Already warned about this in build.make +!else + +# This first part is used to build sa-jdi.jar +!include $(WorkSpace)/build/windows/makefiles/rules.make +!include $(WorkSpace)/build/sa.files + +GENERATED = ..\generated + +# tools.jar is needed by the JDI - SA binding +SA_CLASSPATH = $(BOOT_JAVA_HOME)\lib\tools.jar + +SA_CLASSDIR = $(GENERATED)\saclasses + +SA_BUILD_VERSION_PROP = sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VERSION) + +SA_PROPERTIES = $(SA_CLASSDIR)\sa.properties + +default:: $(GENERATED)\sa-jdi.jar + +$(GENERATED)\sa-jdi.jar: $(AGENT_ALLFILES:/=\) + @if not exist $(SA_CLASSDIR) mkdir $(SA_CLASSDIR) + @echo ...Building sa-jdi.jar + @echo ...$(COMPILE_JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -g -d $(SA_CLASSDIR) .... + @$(COMPILE_JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -g -d $(SA_CLASSDIR) $(AGENT_ALLFILES:/=\) + $(COMPILE_RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer + $(QUIETLY) echo $(SA_BUILD_VERSION_PROP) > $(SA_PROPERTIES) + $(RUN_JAR) cf $@ -C saclasses . + $(RUN_JAR) uf $@ -C $(AGENT_SRC_DIR:/=\) META-INF\services\com.sun.jdi.connect.Connector + $(RUN_JAVAH) -classpath $(SA_CLASSDIR) -jni sun.jvm.hotspot.debugger.windbg.WindbgDebuggerLocal + $(RUN_JAVAH) -classpath $(SA_CLASSDIR) -jni sun.jvm.hotspot.debugger.x86.X86ThreadContext + $(RUN_JAVAH) -classpath $(SA_CLASSDIR) -jni sun.jvm.hotspot.debugger.ia64.IA64ThreadContext + $(RUN_JAVAH) -classpath $(SA_CLASSDIR) -jni sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext + + + +# This second part is used to build sawindbg.dll +# We currently build it the same way for product, debug, and fastdebug. + +SAWINDBG=sawindbg.dll + +checkAndBuildSA:: $(SAWINDBG) + +# These do not need to be optimized (don't run a lot of code) and it +# will be useful to have the assertion checks in place + +!if "$(BUILDARCH)" == "ia64" +SA_CFLAGS = /nologo $(MS_RUNTIME_OPTION) /W3 $(GX_OPTION) /Od /D "WIN32" /D "WIN64" /D "_WINDOWS" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +!elseif "$(BUILDARCH)" == "amd64" +SA_CFLAGS = /nologo $(MS_RUNTIME_OPTION) /W3 $(GX_OPTION) /Od /D "WIN32" /D "WIN64" /D "_WINDOWS" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# On amd64, VS2005 compiler requires bufferoverflowU.lib on the link command line, +# otherwise we get missing __security_check_cookie externals at link time. +SA_LINK_FLAGS = bufferoverflowU.lib +!else +SA_CFLAGS = /nologo $(MS_RUNTIME_OPTION) /W3 /Gm $(GX_OPTION) /ZI /Od /D "WIN32" /D "_WINDOWS" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +!endif + +SASRCFILE = $(AGENT_DIR)/src/os/win32/windbg/sawindbg.cpp +SA_LFLAGS = $(SA_LINK_FLAGS) /nologo /subsystem:console /map /debug /machine:$(MACHINE) + +# Note that we do not keep sawindbj.obj around as it would then +# get included in the dumpbin command in build_vm_def.sh + +$(SAWINDBG): $(SASRCFILE) + set INCLUDE=$(SA_INCLUDE)$(INCLUDE) + $(CPP) @<< + /I"$(BootStrapDir)/include" /I"$(BootStrapDir)/include/win32" + /I"$(GENERATED)" $(SA_CFLAGS) + $(SASRCFILE) + /out:sawindbg.obj +<< + set LIB=$(SA_LIB)$(LIB) + $(LINK) /out:$@ /DLL sawindbg.obj dbgeng.lib $(SA_LFLAGS) + -@rm -f sawindbg.obj + +cleanall : + rm -rf $(GENERATED:\=/)/saclasses + rm -rf $(GENERATED:\=/)/sa-jdi.jar +!endif diff --git a/hotspot/build/windows/makefiles/sanity.make b/hotspot/build/windows/makefiles/sanity.make new file mode 100644 index 00000000000..9b7aa13ab69 --- /dev/null +++ b/hotspot/build/windows/makefiles/sanity.make @@ -0,0 +1,35 @@ +# +# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +!include local.make + +all: checkCL checkLink + +checkCL: + @ if "$(MSC_VER)" NEQ "1310" if "$(MSC_VER)" NEQ "1399" if "$(MSC_VER)" NEQ "1400" \ + echo *** WARNING *** unrecognized cl.exe version $(MSC_VER) ($(RAW_MSC_VER)). Use FORCE_MSC_VER to override automatic detection. + +checkLink: + @ if "$(LINK_VER)" NEQ "710" if "$(LINK_VER)" NEQ "800" \ + echo *** WARNING *** unrecognized link.exe version $(LINK_VER) ($(RAW_LINK_VER)). Use FORCE_LINK_VER to override automatic detection. diff --git a/hotspot/build/windows/makefiles/shared.make b/hotspot/build/windows/makefiles/shared.make new file mode 100644 index 00000000000..4e2b04d6f17 --- /dev/null +++ b/hotspot/build/windows/makefiles/shared.make @@ -0,0 +1,70 @@ +# +# Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +DEFAULTACTIONS=clean post_update create + +default:: $(SUBDIRS) + +!ifndef DIR +DIR=. +!endif + +!ifndef CPP +CPP=cl.exe +!endif + + +!ifdef SUBDIRS +$(SUBDIRS): FORCE + @if not exist $@ mkdir $@ + @if not exist $@\local.make echo # Empty > $@\local.make + @echo nmake $(ACTION) in $(DIR)\$@ + cd $@ && $(MAKE) /NOLOGO /f $(WorkSpace)\build\windows\makefiles\$@.make $(ACTION) DIR=$(DIR)\$@ BUILD_FLAVOR=$(BUILD_FLAVOR) +!endif + +# Creates the needed directory +create:: +!if "$(DIR)" != "." + @echo mkdir $(DIR) +!endif + +# Epilog to update for generating derived files +post_update:: + +# Removes scrap files +clean:: FORCE + -@rm -f *.OLD *.publish + +# Remove all scrap files and all generated files +pure:: clean + -@rm -f *.OLD *.publish + +$(DEFAULTACTIONS) $(ACTIONS):: +!ifdef SUBDIRS + @$(MAKE) -nologo ACTION=$@ DIR=$(DIR) +!endif + +FORCE: + + diff --git a/hotspot/build/windows/makefiles/top.make b/hotspot/build/windows/makefiles/top.make new file mode 100644 index 00000000000..1ff6e1f4eec --- /dev/null +++ b/hotspot/build/windows/makefiles/top.make @@ -0,0 +1,34 @@ +# +# Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +!include local.make + +!ifdef ADLC_ONLY +SUBDIRS=generated +!else +SUBDIRS=generated $(BUILD_FLAVOR) +!endif + +!include $(WorkSpace)/build/windows/makefiles/shared.make + diff --git a/hotspot/build/windows/makefiles/vm.make b/hotspot/build/windows/makefiles/vm.make new file mode 100644 index 00000000000..ddfc8729678 --- /dev/null +++ b/hotspot/build/windows/makefiles/vm.make @@ -0,0 +1,271 @@ +# +# Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Resource file containing VERSIONINFO +Res_Files=.\version.res + +!ifdef RELEASE +!ifdef DEVELOP +CPP_FLAGS=$(CPP_FLAGS) /D "DEBUG" +!else +CPP_FLAGS=$(CPP_FLAGS) /D "PRODUCT" +!endif +!else +CPP_FLAGS=$(CPP_FLAGS) /D "ASSERT" +!endif + +!if "$(Variant)" == "core" +# No need to define anything, CORE is defined as !COMPILER1 && !COMPILER2 +!endif + +!if "$(Variant)" == "kernel" +CPP_FLAGS=$(CPP_FLAGS) /D "KERNEL" +!endif + +!if "$(Variant)" == "compiler1" +CPP_FLAGS=$(CPP_FLAGS) /D "COMPILER1" +!endif + +!if "$(Variant)" == "compiler2" +CPP_FLAGS=$(CPP_FLAGS) /D "COMPILER2" +!endif + +!if "$(Variant)" == "tiered" +CPP_FLAGS=$(CPP_FLAGS) /D "COMPILER1" /D "COMPILER2" +!endif + +# The following variables are defined in the generated local.make file. +CPP_FLAGS=$(CPP_FLAGS) /D "HOTSPOT_RELEASE_VERSION=\"$(HS_BUILD_VER)\"" +CPP_FLAGS=$(CPP_FLAGS) /D "JRE_RELEASE_VERSION=\"$(JRE_RELEASE_VER)\"" +CPP_FLAGS=$(CPP_FLAGS) /D "HOTSPOT_BUILD_TARGET=\"$(BUILD_FLAVOR)\"" +CPP_FLAGS=$(CPP_FLAGS) /D "HOTSPOT_BUILD_USER=\"$(BuildUser)\"" +CPP_FLAGS=$(CPP_FLAGS) /D "HOTSPOT_VM_DISTRO=\"$(HOTSPOT_VM_DISTRO)\"" + +CPP_FLAGS=$(CPP_FLAGS) /D "WIN32" /D "_WINDOWS" $(CPP_INCLUDE_DIRS) + +# Must specify this for sharedRuntimeTrig.cpp +CPP_FLAGS=$(CPP_FLAGS) /D "VM_LITTLE_ENDIAN" + +# Define that so jni.h is on correct side +CPP_FLAGS=$(CPP_FLAGS) /D "_JNI_IMPLEMENTATION_" + +!if "$(BUILDARCH)" == "ia64" +STACK_SIZE="/STACK:1048576,262144" +!else +STACK_SIZE= +!endif + +!if "$(BUILDARCH)" == "ia64" +# AsyncGetCallTrace is not supported on IA64 yet +AGCT_EXPORT= +!else +!if "$(Variant)" == "kernel" +AGCT_EXPORT= +!else +AGCT_EXPORT=/export:AsyncGetCallTrace +!endif +!endif + +LINK_FLAGS=$(LINK_FLAGS) $(STACK_SIZE) /subsystem:windows /dll /base:0x8000000 \ + /export:JNI_GetDefaultJavaVMInitArgs /export:JNI_CreateJavaVM \ + /export:JNI_GetCreatedJavaVMs /export:jio_snprintf \ + /export:jio_printf /export:jio_fprintf \ + /export:jio_vfprintf /export:jio_vsnprintf $(AGCT_EXPORT) \ + /export:JVM_GetVersionInfo \ + /export:JVM_GetThreadStateNames /export:JVM_GetThreadStateValues \ + /export:JVM_InitAgentProperties + +CPP_INCLUDE_DIRS=\ + /I "..\generated" \ + /I "..\generated\jvmtifiles" \ + /I "$(WorkSpace)\src\share\vm\c1" \ + /I "$(WorkSpace)\src\share\vm\compiler" \ + /I "$(WorkSpace)\src\share\vm\code" \ + /I "$(WorkSpace)\src\share\vm\interpreter" \ + /I "$(WorkSpace)\src\share\vm\ci" \ + /I "$(WorkSpace)\src\share\vm\classfile" \ + /I "$(WorkSpace)\src\share\vm\gc_implementation\parallelScavenge"\ + /I "$(WorkSpace)\src\share\vm\gc_implementation\shared"\ + /I "$(WorkSpace)\src\share\vm\gc_implementation\parNew"\ + /I "$(WorkSpace)\src\share\vm\gc_implementation\concurrentMarkSweep"\ + /I "$(WorkSpace)\src\share\vm\gc_interface"\ + /I "$(WorkSpace)\src\share\vm\asm" \ + /I "$(WorkSpace)\src\share\vm\memory" \ + /I "$(WorkSpace)\src\share\vm\oops" \ + /I "$(WorkSpace)\src\share\vm\prims" \ + /I "$(WorkSpace)\src\share\vm\runtime" \ + /I "$(WorkSpace)\src\share\vm\services" \ + /I "$(WorkSpace)\src\share\vm\utilities" \ + /I "$(WorkSpace)\src\share\vm\libadt" \ + /I "$(WorkSpace)\src\share\vm\opto" \ + /I "$(WorkSpace)\src\os\windows\vm" \ + /I "$(WorkSpace)\src\os_cpu\windows_$(Platform_arch)\vm" \ + /I "$(WorkSpace)\src\cpu\$(Platform_arch)\vm" + +CPP_USE_PCH=/Fp"vm.pch" /Yu"incls/_precompiled.incl" + +# Where to find the source code for the virtual machine +VM_PATH=../generated/incls +VM_PATH=$(VM_PATH);../generated/jvmtifiles +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/c1 +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/compiler +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/code +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/interpreter +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/ci +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/classfile +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_implementation/parallelScavenge +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_implementation/shared +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_implementation/parNew +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_implementation/concurrentMarkSweep +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_interface +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/asm +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/memory +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/oops +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/prims +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/runtime +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/services +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/utilities +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/libadt +VM_PATH=$(VM_PATH);$(WorkSpace)/src/os/windows/vm +VM_PATH=$(VM_PATH);$(WorkSpace)/src/os_cpu/windows_$(Platform_arch)/vm +VM_PATH=$(VM_PATH);$(WorkSpace)/src/cpu/$(Platform_arch)/vm +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/opto + +VM_PATH={$(VM_PATH)} + +# Special case files not using precompiled header files. + +c1_RInfo_$(Platform_arch).obj: $(WorkSpace)\src\cpu\$(Platform_arch)\vm\c1_RInfo_$(Platform_arch).cpp + $(CPP) $(CPP_FLAGS) /c $(WorkSpace)\src\cpu\$(Platform_arch)\vm\c1_RInfo_$(Platform_arch).cpp + +os_windows.obj: $(WorkSpace)\src\os\windows\vm\os_windows.cpp + $(CPP) $(CPP_FLAGS) /c $(WorkSpace)\src\os\windows\vm\os_windows.cpp + +os_windows_$(Platform_arch).obj: $(WorkSpace)\src\os_cpu\windows_$(Platform_arch)\vm\os_windows_$(Platform_arch).cpp + $(CPP) $(CPP_FLAGS) /c $(WorkSpace)\src\os_cpu\windows_$(Platform_arch)\vm\os_windows_$(Platform_arch).cpp + +osThread_windows.obj: $(WorkSpace)\src\os\windows\vm\osThread_windows.cpp + $(CPP) $(CPP_FLAGS) /c $(WorkSpace)\src\os\windows\vm\osThread_windows.cpp + +conditionVar_windows.obj: $(WorkSpace)\src\os\windows\vm\conditionVar_windows.cpp + $(CPP) $(CPP_FLAGS) /c $(WorkSpace)\src\os\windows\vm\conditionVar_windows.cpp + +getThread_windows_$(Platform_arch).obj: $(WorkSpace)\src\os_cpu\windows_$(Platform_arch)\vm\getThread_windows_$(Platform_arch).cpp + $(CPP) $(CPP_FLAGS) /c $(WorkSpace)\src\os_cpu\windows_$(Platform_arch)\vm\getThread_windows_$(Platform_arch).cpp + +opcodes.obj: $(WorkSpace)\src\share\vm\opto\opcodes.cpp + $(CPP) $(CPP_FLAGS) /c $(WorkSpace)\src\share\vm\opto\opcodes.cpp + +bytecodeInterpreter.obj: $(WorkSpace)\src\share\vm\interpreter\bytecodeInterpreter.cpp + $(CPP) $(CPP_FLAGS) /c $(WorkSpace)\src\share\vm\interpreter\bytecodeInterpreter.cpp + +bytecodeInterpreterWithChecks.obj: ..\generated\jvmtifiles\bytecodeInterpreterWithChecks.cpp + $(CPP) $(CPP_FLAGS) /c ..\generated\jvmtifiles\bytecodeInterpreterWithChecks.cpp + +# Default rules for the Virtual Machine +{$(WorkSpace)\src\share\vm\c1}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\compiler}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\code}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\interpreter}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\ci}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\classfile}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\gc_implementation\parallelScavenge}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\gc_implementation\shared}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\gc_implementation\parNew}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\gc_implementation\concurrentMarkSweep}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\gc_interface}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\asm}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\memory}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\oops}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\prims}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\runtime}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\services}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\utilities}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\libadt}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\share\vm\opto}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\os\windows\vm}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +# This guy should remain a single colon rule because +# otherwise we can't specify the output filename. +{$(WorkSpace)\src\os\windows\vm}.rc.res: + @$(RC) $(RC_FLAGS) /fo"$@" $< + +{$(WorkSpace)\src\cpu\$(Platform_arch)\vm}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{$(WorkSpace)\src\os_cpu\windows_$(Platform_arch)\vm}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{..\generated\incls}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +{..\generated\jvmtifiles}.cpp.obj:: + $(CPP) $(CPP_FLAGS) $(CPP_USE_PCH) /c $< + +default:: + +_build_pch_file.obj: + @echo #include "incls/_precompiled.incl" > ../generated/_build_pch_file.cpp + $(CPP) $(CPP_FLAGS) /Fp"vm.pch" /Yc"incls/_precompiled.incl" /c ../generated/_build_pch_file.cpp diff --git a/hotspot/build/windows/platform_amd64 b/hotspot/build/windows/platform_amd64 new file mode 100644 index 00000000000..e19b4878163 --- /dev/null +++ b/hotspot/build/windows/platform_amd64 @@ -0,0 +1,15 @@ +// Platform file for win32 NT platform + +os_family = windows + +arch = x86 + +arch_model = x86_64 + +os_arch = windows_x86 + +os_arch_model = windows_x86_64 + +compiler = visCPP + +gnu_dis_arch = amd64 diff --git a/hotspot/build/windows/platform_i486 b/hotspot/build/windows/platform_i486 new file mode 100644 index 00000000000..a426305fb5d --- /dev/null +++ b/hotspot/build/windows/platform_i486 @@ -0,0 +1,16 @@ +// Platform file for windows platform + +os_family = windows + +arch = x86 + +arch_model = x86_32 + +os_arch = windows_x86 + +os_arch_model = windows_x86_32 + +compiler = visCPP + +gnu_dis_arch = i386 + diff --git a/hotspot/build/windows/projectfiles/common/Makefile b/hotspot/build/windows/projectfiles/common/Makefile new file mode 100644 index 00000000000..b016cba8bf9 --- /dev/null +++ b/hotspot/build/windows/projectfiles/common/Makefile @@ -0,0 +1,183 @@ +# +# Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +WorkSpace=$(HOTSPOTWORKSPACE) + +!ifdef ALT_BOOTDIR +BootStrapDir=$(ALT_BOOTDIR) +!else +!ifdef BOOTDIR +BootStrapDir=$(BOOTDIR) +!else +!ifdef JAVA_HOME +BootStrapDir=$(JAVA_HOME) +!endif +!endif +!endif + +!include $(HOTSPOTWORKSPACE)/build/windows/makefiles/makedeps.make + +# Pick up rules for building JVMTI (JSR-163) +JvmtiOutDir=$(HOTSPOTBUILDSPACE)\jvmtifiles +!include $(HOTSPOTWORKSPACE)/build/windows/makefiles/jvmti.make + +Platform=$(HOTSPOTWORKSPACE)/build/windows/platform_$(BUILDARCH) + +default:: $(AdditionalTargets) $(JvmtiGeneratedFiles) + +IncludeDBs_base=$(HOTSPOTWORKSPACE)/src/share/vm/includeDB_core \ + $(HOTSPOTWORKSPACE)/src/share/vm/includeDB_jvmti \ + $(HOTSPOTWORKSPACE)/src/share/vm/includeDB_gc \ + $(HOTSPOTWORKSPACE)/src/share/vm/gc_implementation/includeDB_gc_serial + +# Parallel gc files +IncludeDBs_gc=$(HOTSPOTWORKSPACE)/src/share/vm/includeDB_gc_parallel \ + $(HOTSPOTWORKSPACE)/src/share/vm/gc_implementation/includeDB_gc_shared \ + $(HOTSPOTWORKSPACE)/src/share/vm/gc_implementation/includeDB_gc_parNew \ + $(HOTSPOTWORKSPACE)/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge \ + $(HOTSPOTWORKSPACE)/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep + + +IncludeDBs_kernel =$(IncludeDBs_base) \ + $(HOTSPOTWORKSPACE)/src/share/vm/includeDB_compiler1 + +IncludeDBs_core =$(IncludeDBs_base) $(IncludeDBs_gc) \ + $(HOTSPOTWORKSPACE)/src/share/vm/includeDB_features + +IncludeDBs_compiler1=$(IncludeDBs_core) \ + $(HOTSPOTWORKSPACE)/src/share/vm/includeDB_compiler1 + +IncludeDBs_compiler2=$(IncludeDBs_core) \ + $(HOTSPOTWORKSPACE)/src/share/vm/includeDB_compiler2 + +IncludeDBs_tiered=$(IncludeDBs_core) \ + $(HOTSPOTWORKSPACE)/src/share/vm/includeDB_compiler1 \ + $(HOTSPOTWORKSPACE)/src/share/vm/includeDB_compiler2 + + +!if "$(Variant)" == "compiler1" +IncludeDBs = $(IncludeDBs_compiler1) +!endif + +!if "$(Variant)" == "compiler2" +IncludeDBs = $(IncludeDBs_compiler2) +# Pick up rules for building adlc +!include $(HOTSPOTWORKSPACE)/build/windows/makefiles/adlc.make +!endif + +!if "$(Variant)" == "tiered" +IncludeDBs = $(IncludeDBs_tiered) +# Pick up rules for building adlc +!include $(HOTSPOTWORKSPACE)/build/windows/makefiles/adlc.make +!endif + +!if "$(Variant)" == "core" +IncludeDBs = $(IncludeDBs_core) +!endif + +!if "$(Variant)" == "kernel" +IncludeDBs = $(IncludeDBs_kernel) +!endif + +!include $(HOTSPOTWORKSPACE)/make/hotspot_version + +!if "$(HOTSPOT_RELEASE_VERSION)" != "" +HOTSPOT_RELEASE_VERSION="$(HOTSPOT_RELEASE_VERSION)" +!else +HOTSPOT_RELEASE_VERSION="$(HS_MAJOR_VER).$(HS_MINOR_VER)-b$(HS_BUILD_NUMBER)" +!endif +HOTSPOT_BUILD_VERSION$(HOTSPOT_BUILD_VERSION) = internal +!if "$(HOTSPOT_BUILD_VERSION)" != "" +HOTSPOT_RELEASE_VERSION="$(HOTSPOT_RELEASE_VERSION)-$(HOTSPOT_BUILD_VERSION)" +!endif +!if "$(JRE_RELEASE_VERSION)" != "" +JRE_RELEASE_VERSION="$(JRE_RELEASE_VERSION)" +!else +JRE_RELEASE_VERSION="$(JDK_MAJOR_VER).$(JDK_MINOR_VER).$(JDK_MICRO_VER)" +!endif + +# Define HOTSPOT_VM_DISTRO if HOTSPOT_VM_DISTRO is set, +# and if it is not see if we have the src/closed directory +!if "$(HOTSPOT_VM_DISTRO)" != "" +HOTSPOT_VM_DISTRO="$(HOTSPOT_VM_DISTRO)" +!else +!if exists($(HOTSPOTWORKSPACE)\src\closed) +HOTSPOT_VM_DISTRO="Java HotSpot(TM)" +!else +HOTSPOT_VM_DISTRO="OpenJDK" +!endif +!endif + +MakeDepsIDEOptions = $(MakeDepsIDEOptions) \ + -includeDB_kernel $(HOTSPOTBUILDSPACE)\includeDB_kernel \ + -includeDB_core $(HOTSPOTBUILDSPACE)\includeDB_core \ + -includeDB_compiler1 $(HOTSPOTBUILDSPACE)\includeDB_compiler1 \ + -includeDB_compiler2 $(HOTSPOTBUILDSPACE)\includeDB_compiler2 \ + -includeDB_tiered $(HOTSPOTBUILDSPACE)\includeDB_tiered \ + -platform $(Platform) \ + -define HOTSPOT_RELEASE_VERSION=\\\"$(HOTSPOT_RELEASE_VERSION)\\\" \ + -define JRE_RELEASE_VERSION=\\\"$(JRE_RELEASE_VERSION)\\\" \ + -define HOTSPOT_VM_DISTRO=\\\"$(HOTSPOT_VM_DISTRO)\\\" + +incls: + @mkdir incls + +includeDB.current $(ProjectFile) Dependencies: local.make $(HOTSPOTBUILDSPACE)/classes/MakeDeps.class \ + $(IncludeDBs) incls + @rm -f includeDB $(HOTSPOTBUILDSPACE)\includeDB_kernel \ + $(HOTSPOTBUILDSPACE)\includeDB_core \ + $(HOTSPOTBUILDSPACE)\includeDB_compiler1 \ + $(HOTSPOTBUILDSPACE)\includeDB_compiler2 \ + $(HOTSPOTBUILDSPACE)\includeDB_tiered + @cat $(IncludeDBs_kernel) > $(HOTSPOTBUILDSPACE)\includeDB_kernel + @cat $(IncludeDBs_core) > $(HOTSPOTBUILDSPACE)\includeDB_core + @cat $(IncludeDBs_compiler1) > $(HOTSPOTBUILDSPACE)\includeDB_compiler1 + @cat $(IncludeDBs_compiler2) > $(HOTSPOTBUILDSPACE)\includeDB_compiler2 + @cat $(IncludeDBs_tiered) > $(HOTSPOTBUILDSPACE)\includeDB_tiered + @echo java.cpp jni.h > includeDB + @$(RUN_JAVA) -Djava.class.path=$(HOTSPOTBUILDSPACE)/classes MakeDeps diffs WinGammaPlatform$(VcVersion) \ + $(Platform) includeDB.current $(Platform) includeDB $(MakeDepsOptions) $(MakeDepsIDEOptions) + @rm -f includeDB.current + @cp includeDB includeDB.current + +lists: $(HOTSPOTBUILDSPACE)/classes/MakeDeps.class FORCE + @if exist incls rmdir /s /q incls + @rm -f includeDB + @cat $(IncludeDBs) > includeDB + @mkdir incls + @$(RUN_JAVA) -Djava.class.path=$(HOTSPOTBUILDSPACE)/classes MakeDeps WinGammaPlatform$(VcVersion) \ + $(Platform) includeDB $(MakeDepsOptions) $(MakeDepsIDEOptions) + @rm -f includeDB.current + @cp includeDB includeDB.current + +clean: + @rm -rf incls $(HOTSPOTBUILDSPACE)/classes + @rm -f includeDB includeDB.current $(ProjectFile) Dependencies + +$(HOTSPOTBUILDSPACE)/classes/MakeDeps.class: $(MakeDepsSources) + @if exist $(HOTSPOTBUILDSPACE)\classes rmdir /s /q $(HOTSPOTBUILDSPACE)\classes + @mkdir $(HOTSPOTBUILDSPACE)\classes + @$(COMPILE_JAVAC) -classpath $(HOTSPOTWORKSPACE)\src\share\tools\MakeDeps -g -d $(HOTSPOTBUILDSPACE)/classes $(MakeDepsSources) + +FORCE: diff --git a/hotspot/build/windows/projectfiles/compiler1/Makefile b/hotspot/build/windows/projectfiles/compiler1/Makefile new file mode 100644 index 00000000000..1cddb034fe2 --- /dev/null +++ b/hotspot/build/windows/projectfiles/compiler1/Makefile @@ -0,0 +1,28 @@ +# +# Copyright 1999-2002 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +Variant=compiler1 +!include local.make + +!include $(HOTSPOTWORKSPACE)/build/windows/projectfiles/common/Makefile diff --git a/hotspot/build/windows/projectfiles/compiler1/vm.def b/hotspot/build/windows/projectfiles/compiler1/vm.def new file mode 100644 index 00000000000..4475c606215 --- /dev/null +++ b/hotspot/build/windows/projectfiles/compiler1/vm.def @@ -0,0 +1,7 @@ +; +; This .DEF file is a placeholder for one which is automatically +; generated during the build process. See +; build\windows\build_vm_def.sh and +; build\windows\makefiles\makedeps.make (esp. the "-prelink" +; options). +; diff --git a/hotspot/build/windows/projectfiles/compiler1/vm.dsw b/hotspot/build/windows/projectfiles/compiler1/vm.dsw new file mode 100644 index 00000000000..934f51afdb3 --- /dev/null +++ b/hotspot/build/windows/projectfiles/compiler1/vm.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "vm"=.\vm.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/hotspot/build/windows/projectfiles/compiler2/ADLCompiler.dsp b/hotspot/build/windows/projectfiles/compiler2/ADLCompiler.dsp new file mode 100644 index 00000000000..8b524da4ce5 --- /dev/null +++ b/hotspot/build/windows/projectfiles/compiler2/ADLCompiler.dsp @@ -0,0 +1,142 @@ +# Microsoft Developer Studio Project File - Name="ADLCompiler" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=ADLCompiler - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ADLCompiler.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ADLCompiler.mak" CFG="ADLCompiler - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ADLCompiler - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "ADLCompiler - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ADLCompiler - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\adlc\Release" +# PROP Intermediate_Dir ".\adlc\Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "." /I "$(HotSpotWorkSpace)\src\share\vm\opto" /I "$(HotSpotWorkSpace)\src\share\vm\prims" /I "$(HotSpotWorkSpace)\src\share\vm\lookup" /I "$(HotSpotWorkSpace)\src\share\vm\interpreter" /I "$(HotSpotWorkSpace)\src\share\vm\asm" /I "$(HotSpotWorkSpace)\src\share\vm\compiler" /I "$(HotSpotWorkSpace)\src\share\vm\utilities" /I "$(HotSpotWorkSpace)\src\share\vm\code" /I "$(HotSpotWorkSpace)\src\share\vm\oops" /I "$(HotSpotWorkSpace)\src\share\vm\runtime" /I "$(HotSpotWorkSpace)\src\share\vm\memory" /I "$(HotSpotWorkSpace)\src\share\vm\libadt" /I "$(HotSpotWorkSpace)\src\cpu\i486\vm" /I "$(HotSpotWorkSpace)\src\os\win32\vm" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o".\adlc\Release\adlc.bsc" +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:".\bin\adlc.exe" + +!ELSEIF "$(CFG)" == "ADLCompiler - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\adlc\Debug" +# PROP Intermediate_Dir ".\adlc\Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /ML /W3 /WX /Gm /GX /Zi /Od /I "." /I "$(HotSpotWorkSpace)\src\share\vm\opto" /I "$(HotSpotWorkSpace)\src\share\vm\prims" /I "$(HotSpotWorkSpace)\src\share\vm\lookup" /I "$(HotSpotWorkSpace)\src\share\vm\interpreter" /I "$(HotSpotWorkSpace)\src\share\vm\asm" /I "$(HotSpotWorkSpace)\src\share\vm\compiler" /I "$(HotSpotWorkSpace)\src\share\vm\utilities" /I "$(HotSpotWorkSpace)\src\share\vm\code" /I "$(HotSpotWorkSpace)\src\share\vm\oops" /I "$(HotSpotWorkSpace)\src\share\vm\runtime" /I "$(HotSpotWorkSpace)\src\share\vm\memory" /I "$(HotSpotWorkSpace)\src\share\vm\libadt" /I "$(HotSpotWorkSpace)\src\cpu\i486\vm" /I "$(HotSpotWorkSpace)\src\os\win32\vm" /D "WIN32" /D "DEBUG" /D "_WINDOWS" /D "ASSERT" /Fr /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /o".\adlc\Debug\adlc_g.bsc" +# SUBTRACT BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib /nologo /subsystem:console /debug /machine:I386 /out:".\bin\adlc_g.exe" + +!ENDIF + +# Begin Target + +# Name "ADLCompiler - Win32 Release" +# Name "ADLCompiler - Win32 Debug" +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\adlparse.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\archDesc.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\arena.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\dfa.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\dict2.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\filebuff.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\forms.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\formsopt.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\formssel.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\main.cpp" +# SUBTRACT CPP /YX /Yc +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\opto\opcodes.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\output_c.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\output_h.cpp" +# End Source File +# End Target +# End Project + \ No newline at end of file diff --git a/hotspot/build/windows/projectfiles/compiler2/ADLCompiler.dsw b/hotspot/build/windows/projectfiles/compiler2/ADLCompiler.dsw new file mode 100644 index 00000000000..0ad5370ed81 --- /dev/null +++ b/hotspot/build/windows/projectfiles/compiler2/ADLCompiler.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "ADLCompiler"=".\ADLCompiler.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/hotspot/build/windows/projectfiles/compiler2/Makefile b/hotspot/build/windows/projectfiles/compiler2/Makefile new file mode 100644 index 00000000000..eca41f04d5d --- /dev/null +++ b/hotspot/build/windows/projectfiles/compiler2/Makefile @@ -0,0 +1,29 @@ +# +# Copyright 1998-2002 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +Variant=compiler2 +!include local.make +AdditionalTargets=incls/ad_$(Platform_arch_model).cpp incls/dfa_$(Platform_arch_model).cpp + +!include $(HOTSPOTWORKSPACE)/build/windows/projectfiles/common/Makefile diff --git a/hotspot/build/windows/projectfiles/compiler2/vm.def b/hotspot/build/windows/projectfiles/compiler2/vm.def new file mode 100644 index 00000000000..4475c606215 --- /dev/null +++ b/hotspot/build/windows/projectfiles/compiler2/vm.def @@ -0,0 +1,7 @@ +; +; This .DEF file is a placeholder for one which is automatically +; generated during the build process. See +; build\windows\build_vm_def.sh and +; build\windows\makefiles\makedeps.make (esp. the "-prelink" +; options). +; diff --git a/hotspot/build/windows/projectfiles/compiler2/vm.dsw b/hotspot/build/windows/projectfiles/compiler2/vm.dsw new file mode 100644 index 00000000000..26aab5c3b16 --- /dev/null +++ b/hotspot/build/windows/projectfiles/compiler2/vm.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 5.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "vm"=.\vm.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/hotspot/build/windows/projectfiles/core/Makefile b/hotspot/build/windows/projectfiles/core/Makefile new file mode 100644 index 00000000000..243188b20ad --- /dev/null +++ b/hotspot/build/windows/projectfiles/core/Makefile @@ -0,0 +1,28 @@ +# +# Copyright 1998-2002 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +Variant=core +!include local.make + +!include $(HOTSPOTWORKSPACE)/build/windows/projectfiles/common/Makefile diff --git a/hotspot/build/windows/projectfiles/core/vm.def b/hotspot/build/windows/projectfiles/core/vm.def new file mode 100644 index 00000000000..4475c606215 --- /dev/null +++ b/hotspot/build/windows/projectfiles/core/vm.def @@ -0,0 +1,7 @@ +; +; This .DEF file is a placeholder for one which is automatically +; generated during the build process. See +; build\windows\build_vm_def.sh and +; build\windows\makefiles\makedeps.make (esp. the "-prelink" +; options). +; diff --git a/hotspot/build/windows/projectfiles/core/vm.dsw b/hotspot/build/windows/projectfiles/core/vm.dsw new file mode 100644 index 00000000000..934f51afdb3 --- /dev/null +++ b/hotspot/build/windows/projectfiles/core/vm.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "vm"=.\vm.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/hotspot/build/windows/projectfiles/kernel/Makefile b/hotspot/build/windows/projectfiles/kernel/Makefile new file mode 100644 index 00000000000..2ba53d245dd --- /dev/null +++ b/hotspot/build/windows/projectfiles/kernel/Makefile @@ -0,0 +1,28 @@ +# +# Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +Variant=compiler1 +!include local.make + +!include $(HOTSPOTWORKSPACE)/build/windows/projectfiles/common/Makefile diff --git a/hotspot/build/windows/projectfiles/kernel/vm.def b/hotspot/build/windows/projectfiles/kernel/vm.def new file mode 100644 index 00000000000..4475c606215 --- /dev/null +++ b/hotspot/build/windows/projectfiles/kernel/vm.def @@ -0,0 +1,7 @@ +; +; This .DEF file is a placeholder for one which is automatically +; generated during the build process. See +; build\windows\build_vm_def.sh and +; build\windows\makefiles\makedeps.make (esp. the "-prelink" +; options). +; diff --git a/hotspot/build/windows/projectfiles/kernel/vm.dsw b/hotspot/build/windows/projectfiles/kernel/vm.dsw new file mode 100644 index 00000000000..934f51afdb3 --- /dev/null +++ b/hotspot/build/windows/projectfiles/kernel/vm.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "vm"=.\vm.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/hotspot/build/windows/projectfiles/tiered/ADLCompiler.dsp b/hotspot/build/windows/projectfiles/tiered/ADLCompiler.dsp new file mode 100644 index 00000000000..8b524da4ce5 --- /dev/null +++ b/hotspot/build/windows/projectfiles/tiered/ADLCompiler.dsp @@ -0,0 +1,142 @@ +# Microsoft Developer Studio Project File - Name="ADLCompiler" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=ADLCompiler - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ADLCompiler.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ADLCompiler.mak" CFG="ADLCompiler - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ADLCompiler - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "ADLCompiler - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ADLCompiler - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\adlc\Release" +# PROP Intermediate_Dir ".\adlc\Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "." /I "$(HotSpotWorkSpace)\src\share\vm\opto" /I "$(HotSpotWorkSpace)\src\share\vm\prims" /I "$(HotSpotWorkSpace)\src\share\vm\lookup" /I "$(HotSpotWorkSpace)\src\share\vm\interpreter" /I "$(HotSpotWorkSpace)\src\share\vm\asm" /I "$(HotSpotWorkSpace)\src\share\vm\compiler" /I "$(HotSpotWorkSpace)\src\share\vm\utilities" /I "$(HotSpotWorkSpace)\src\share\vm\code" /I "$(HotSpotWorkSpace)\src\share\vm\oops" /I "$(HotSpotWorkSpace)\src\share\vm\runtime" /I "$(HotSpotWorkSpace)\src\share\vm\memory" /I "$(HotSpotWorkSpace)\src\share\vm\libadt" /I "$(HotSpotWorkSpace)\src\cpu\i486\vm" /I "$(HotSpotWorkSpace)\src\os\win32\vm" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o".\adlc\Release\adlc.bsc" +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:".\bin\adlc.exe" + +!ELSEIF "$(CFG)" == "ADLCompiler - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\adlc\Debug" +# PROP Intermediate_Dir ".\adlc\Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /ML /W3 /WX /Gm /GX /Zi /Od /I "." /I "$(HotSpotWorkSpace)\src\share\vm\opto" /I "$(HotSpotWorkSpace)\src\share\vm\prims" /I "$(HotSpotWorkSpace)\src\share\vm\lookup" /I "$(HotSpotWorkSpace)\src\share\vm\interpreter" /I "$(HotSpotWorkSpace)\src\share\vm\asm" /I "$(HotSpotWorkSpace)\src\share\vm\compiler" /I "$(HotSpotWorkSpace)\src\share\vm\utilities" /I "$(HotSpotWorkSpace)\src\share\vm\code" /I "$(HotSpotWorkSpace)\src\share\vm\oops" /I "$(HotSpotWorkSpace)\src\share\vm\runtime" /I "$(HotSpotWorkSpace)\src\share\vm\memory" /I "$(HotSpotWorkSpace)\src\share\vm\libadt" /I "$(HotSpotWorkSpace)\src\cpu\i486\vm" /I "$(HotSpotWorkSpace)\src\os\win32\vm" /D "WIN32" /D "DEBUG" /D "_WINDOWS" /D "ASSERT" /Fr /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /o".\adlc\Debug\adlc_g.bsc" +# SUBTRACT BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib /nologo /subsystem:console /debug /machine:I386 /out:".\bin\adlc_g.exe" + +!ENDIF + +# Begin Target + +# Name "ADLCompiler - Win32 Release" +# Name "ADLCompiler - Win32 Debug" +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\adlparse.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\archDesc.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\arena.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\dfa.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\dict2.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\filebuff.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\forms.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\formsopt.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\formssel.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\main.cpp" +# SUBTRACT CPP /YX /Yc +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\opto\opcodes.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\output_c.cpp" +# End Source File +# Begin Source File + +SOURCE="$(HotSpotWorkSpace)\src\share\vm\adlc\output_h.cpp" +# End Source File +# End Target +# End Project + \ No newline at end of file diff --git a/hotspot/build/windows/projectfiles/tiered/ADLCompiler.dsw b/hotspot/build/windows/projectfiles/tiered/ADLCompiler.dsw new file mode 100644 index 00000000000..0ad5370ed81 --- /dev/null +++ b/hotspot/build/windows/projectfiles/tiered/ADLCompiler.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "ADLCompiler"=".\ADLCompiler.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/hotspot/build/windows/projectfiles/tiered/Makefile b/hotspot/build/windows/projectfiles/tiered/Makefile new file mode 100644 index 00000000000..f92d249109b --- /dev/null +++ b/hotspot/build/windows/projectfiles/tiered/Makefile @@ -0,0 +1,29 @@ +# +# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +Variant=tiered +!include local.make +AdditionalTargets=incls/ad_$(Platform_arch_model).cpp incls/dfa_$(Platform_arch_model).cpp + +!include $(HOTSPOTWORKSPACE)/build/windows/projectfiles/common/Makefile diff --git a/hotspot/build/windows/projectfiles/tiered/vm.def b/hotspot/build/windows/projectfiles/tiered/vm.def new file mode 100644 index 00000000000..4475c606215 --- /dev/null +++ b/hotspot/build/windows/projectfiles/tiered/vm.def @@ -0,0 +1,7 @@ +; +; This .DEF file is a placeholder for one which is automatically +; generated during the build process. See +; build\windows\build_vm_def.sh and +; build\windows\makefiles\makedeps.make (esp. the "-prelink" +; options). +; diff --git a/hotspot/build/windows/projectfiles/tiered/vm.dsw b/hotspot/build/windows/projectfiles/tiered/vm.dsw new file mode 100644 index 00000000000..26aab5c3b16 --- /dev/null +++ b/hotspot/build/windows/projectfiles/tiered/vm.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 5.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "vm"=.\vm.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/hotspot/make/Makefile b/hotspot/make/Makefile new file mode 100644 index 00000000000..b6924757324 --- /dev/null +++ b/hotspot/make/Makefile @@ -0,0 +1,506 @@ +# +# Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Top level gnumake file for hotspot builds +# +# Default is to build the both product images and construct an export dir. +# The default export directory name is `pwd`/export-$(PLATFORM). +# +# Use: 'gnumake help' for more information. +# +# This makefile uses the default settings for where to find compilers and +# tools, and obeys the ALT_* variable settings used by the other JDK +# workspaces. +# + +# Expected/optional make variables defined on make command line: +# LP64=1 or ARCH_DATA_MODEL=64 for 64bit build +# +# Expected/optional make variables or environment variables: +# ALT_SLASH_JAVA Location of /java or J: +# ALT_BOOTDIR Previous JDK home directory for javac compiler +# ALT_OUTPUTDIR Output directory to use for hotspot build +# ALT_EXPORT_PATH Directory to export hotspot build to +# ALT_JDK_IMPORT_PATH Current JDK build (only for create_jdk rules) +# ALT_BUILD_WIN_SA Building SA on Windows is disabled by default. +# Set ALT_BUILD_WIN_SA=1 to enable building SA on +# Windows. +# Version strings and numbers: +# JDK_VERSION Current JDK version (e.g. 1.6.0) +# PREVIOUS_JDK_VERSION Previous (bootdir) JDK version (e.g. 1.5.0) +# FULL_VERSION Full version string to use (e.g. "1.6.0-ea-b42") +# +# Version strings and numbers especially needed on Windows: +# COOKED_JDK_UPDATE_VERSION Just the update release number (e.g. 02) +# COOKED_BUILD_NUMBER Just the build number (e.g. 42) +# JDK_MKTG_VERSION Marketing JDK version (e.g. 6.0) +# JDK_MAJOR_VERSION Major number for version (e.g. 1) always 1? +# JDK_MINOR_VERSION Minor number for version (e.g. 6) +# JDK_MICRO_VERSION Micro number for version (e.g. 0) +# + +# Default is build both product fastdebug and create export area + +# Allow to build HotSpot in local directory from sources specified by GAMMADIR. +# After make/defs.make GAMMADIR is defined. +ifdef GAMMADIR + ifndef ALT_OUTPUTDIR + ALT_OUTPUTDIR := $(shell pwd) + endif + include $(GAMMADIR)/make/defs.make +else + include defs.make +endif + + +ifneq ($(ALT_OUTPUTDIR),) + ALT_OUT=ALT_OUTPUTDIR=$(ALT_OUTPUTDIR) +else + ALT_OUT= +endif + +# Typical C1/C2 targets made available with this Makefile +C1_VM_TARGETS=product1 fastdebug1 optimized1 jvmg1 +C2_VM_TARGETS=product fastdebug optimized jvmg +KERNEL_VM_TARGETS=productkernel fastdebugkernel optimizedkernel jvmgkernel + +all: all_product all_fastdebug +all_product: product product1 productkernel docs export_product +all_fastdebug: fastdebug fastdebug1 fastdebugkernel docs export_fastdebug +all_debug: jvmg jvmg1 jvmgkernel docs export_debug +all_optimized: optimized optimized1 optimizedkernel docs export_optimized + +# Do everything +world: all create_jdk + +# Build or export docs +docs: +ifeq ($(OSNAME),windows) + @$(ECHO) "No docs ($(VM_TARGET)) for windows" +else + $(CD) $(OUTPUTDIR); \ + $(MAKE) -f $(ABS_OS_MAKEFILE) \ + $(MAKE_ARGS) docs +endif + +# Build variation of hotspot +$(C1_VM_TARGETS): + $(CD) $(GAMMADIR)/make; \ + $(MAKE) VM_TARGET=$@ generic_build1 $(ALT_OUT) + +$(C2_VM_TARGETS): + $(CD) $(GAMMADIR)/make; \ + $(MAKE) VM_TARGET=$@ generic_build2 $(ALT_OUT) + +$(KERNEL_VM_TARGETS): + $(CD) $(GAMMADIR)/make; \ + $(MAKE) VM_TARGET=$@ generic_buildkernel $(ALT_OUT) + +# Build compiler1 (client) rule, different for platforms +generic_build1: + $(MKDIR) -p $(OUTPUTDIR) +ifeq ($(OSNAME),windows) + ifeq ($(ARCH_DATA_MODEL), 32) + $(CD) $(OUTPUTDIR); \ + $(NMAKE) -f $(ABS_OS_MAKEFILE) \ + Variant=compiler1 \ + WorkSpace=$(ABS_GAMMADIR) \ + BootStrapDir=$(ABS_BOOTDIR) \ + BuildUser=$(USERNAME) \ + $(MAKE_ARGS) $(VM_TARGET:%1=%) + else + @$(ECHO) "No compiler1 ($(VM_TARGET)) for ARCH_DATA_MODEL=$(ARCH_DATA_MODEL)" + endif +else + ifeq ($(ARCH_DATA_MODEL), 32) + $(CD) $(OUTPUTDIR); \ + $(MAKE) -f $(ABS_OS_MAKEFILE) \ + $(MAKE_ARGS) $(VM_TARGET) + else + @$(ECHO) "No compiler1 ($(VM_TARGET)) for ARCH_DATA_MODEL=$(ARCH_DATA_MODEL)" + endif +endif + +# Build compiler2 (server) rule, different for platforms +generic_build2: + $(MKDIR) -p $(OUTPUTDIR) +ifeq ($(OSNAME),windows) + $(CD) $(OUTPUTDIR); \ + $(NMAKE) -f $(ABS_OS_MAKEFILE) \ + Variant=compiler2 \ + WorkSpace=$(ABS_GAMMADIR) \ + BootStrapDir=$(ABS_BOOTDIR) \ + BuildUser=$(USERNAME) \ + $(MAKE_ARGS) $(VM_TARGET) +else + $(CD) $(OUTPUTDIR); \ + $(MAKE) -f $(ABS_OS_MAKEFILE) \ + $(MAKE_ARGS) $(VM_TARGET) +endif + +generic_buildkernel: + $(MKDIR) -p $(OUTPUTDIR) +ifeq ($(OSNAME),windows) + ifeq ($(ARCH_DATA_MODEL), 32) + $(CD) $(OUTPUTDIR); \ + $(NMAKE) -f $(ABS_OS_MAKEFILE) \ + Variant=kernel \ + WorkSpace=$(ABS_GAMMADIR) \ + BootStrapDir=$(ABS_BOOTDIR) \ + BuildUser=$(USERNAME) \ + $(MAKE_ARGS) $(VM_TARGET:%kernel=%) + else + @$(ECHO) "No kernel ($(VM_TARGET)) for ARCH_DATA_MODEL=$(ARCH_DATA_MODEL)" + endif +else + @$(ECHO) "No kernel ($(VM_TARGET)) for OS_NAME=$(OSNAME)" +endif + +# Export file rule +generic_export: $(EXPORT_LIST) +export_product: + $(MAKE) VM_SUBDIR=product generic_export +export_fastdebug: + $(MAKE) VM_SUBDIR=fastdebug EXPORT_SUBDIR=/fastdebug generic_export +export_debug: + $(MAKE) VM_SUBDIR=${VM_DEBUG} EXPORT_SUBDIR=/debug generic_export +export_optimized: + $(MAKE) VM_SUBDIR=optimized EXPORT_SUBDIR=/optimized generic_export +export_product_jdk: + $(MAKE) ALT_EXPORT_PATH=$(JDK_IMAGE_DIR) \ + VM_SUBDIR=product generic_export +export_optimized_jdk: + $(MAKE) ALT_EXPORT_PATH=$(JDK_IMAGE_DIR) \ + VM_SUBDIR=optimized generic_export +export_fastdebug_jdk: + $(MAKE) ALT_EXPORT_PATH=$(JDK_IMAGE_DIR)/fastdebug \ + VM_SUBDIR=fastdebug generic_export +export_debug_jdk: + $(MAKE) ALT_EXPORT_PATH=$(JDK_IMAGE_DIR)/debug \ + VM_SUBDIR=${VM_DEBUG} generic_export + + +# Export file copy rules +XUSAGE=$(HS_SRC_DIR)/share/vm/Xusage.txt +DOCS_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_docs +C1_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_compiler1 +C2_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_compiler2 +KERNEL_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_kernel +C1_DIR=$(C1_BASE_DIR)/$(VM_SUBDIR) +C2_DIR=$(C2_BASE_DIR)/$(VM_SUBDIR) +KERNEL_DIR=$(KERNEL_BASE_DIR)/$(VM_SUBDIR) + +# Misc files and generated files need to come from C1 or C2 area +ifeq ($(ARCH_DATA_MODEL), 32) + MISC_DIR=$(C1_DIR) + GEN_DIR=$(C1_BASE_DIR)/generated +else + MISC_DIR=$(C2_DIR) + GEN_DIR=$(C2_BASE_DIR)/generated +endif + +# Bin files (windows) +ifeq ($(OSNAME),windows) + +# Get jvm.lib +$(EXPORT_LIB_DIR)/%.lib: $(MISC_DIR)/%.lib + $(install-file) + +# Other libraries (like SA) +$(EXPORT_JRE_BIN_DIR)/%.dll: $(MISC_DIR)/%.dll + $(install-file) +$(EXPORT_JRE_BIN_DIR)/%.pdb: $(MISC_DIR)/%.pdb + $(install-file) +$(EXPORT_JRE_BIN_DIR)/%.map: $(MISC_DIR)/%.map + $(install-file) + +# Client files always come from C1 area +$(EXPORT_CLIENT_DIR)/%.dll: $(C1_DIR)/%.dll + $(install-file) +$(EXPORT_CLIENT_DIR)/%.pdb: $(C1_DIR)/%.pdb + $(install-file) +$(EXPORT_CLIENT_DIR)/%.map: $(C1_DIR)/%.map + $(install-file) + +# Server files always come from C2 area +$(EXPORT_SERVER_DIR)/%.dll: $(C2_DIR)/%.dll + $(install-file) +$(EXPORT_SERVER_DIR)/%.pdb: $(C2_DIR)/%.pdb + $(install-file) +$(EXPORT_SERVER_DIR)/%.map: $(C2_DIR)/%.map + $(install-file) + +# Kernel files always come from kernel area +$(EXPORT_KERNEL_DIR)/%.dll: $(KERNEL_DIR)/%.dll + $(install-file) +$(EXPORT_KERNEL_DIR)/%.pdb: $(KERNEL_DIR)/%.pdb + $(install-file) +$(EXPORT_KERNEL_DIR)/%.map: $(KERNEL_DIR)/%.map + $(install-file) +endif + +# Shared Library +ifneq ($(OSNAME),windows) +$(EXPORT_JRE_LIB_ARCH_DIR)/%.so: $(C2_DIR)/%.so + $(install-file) +$(EXPORT_CLIENT_DIR)/%.so: $(C1_DIR)/%.so + $(install-file) +$(EXPORT_CLIENT_DIR)/64/%.so: $(C1_DIR)/%.so + $(install-file) +$(EXPORT_SERVER_DIR)/%.so: $(C2_DIR)/%.so + $(install-file) +$(EXPORT_SERVER_DIR)/64/%.so: $(C2_DIR)/%.so + $(install-file) +endif + +# Jar file (sa-jdi.jar) +$(EXPORT_LIB_DIR)/%.jar: $(GEN_DIR)/%.jar + $(install-file) + +# Include files (jvmti.h, jni.h, $(JDK_INCLUDE_SUBDIR)/jni_md.h, jmm.h) +$(EXPORT_INCLUDE_DIR)/%: $(GEN_DIR)/jvmtifiles/% + $(install-file) + +$(EXPORT_INCLUDE_DIR)/%: $(HS_SRC_DIR)/share/vm/prims/% + $(install-file) + +$(EXPORT_INCLUDE_DIR)/$(JDK_INCLUDE_SUBDIR)/jni_md.h: $(HS_SRC_DIR)/cpu/$(HS_ARCH)/vm/jni_$(HS_ARCH).h + $(install-file) + +$(EXPORT_INCLUDE_DIR)/%: $(HS_SRC_DIR)/share/vm/services/% + $(install-file) + +# Doc files (jvmti.html) +$(EXPORT_DOCS_DIR)/platform/jvmti/%: $(DOCS_DIR)/% + $(install-file) + +# Xusage file +$(EXPORT_SERVER_DIR)/Xusage.txt $(EXPORT_CLIENT_DIR)/Xusage.txt $(EXPORT_KERNEL_DIR)/Xusage.txt: $(XUSAGE) + $(prep-target) + $(RM) $@.temp + $(SED) 's/\(separated by \)[;:]/\1$(PATH_SEP)/g' $< > $@.temp + $(MV) $@.temp $@ + +# +# Clean rules +# +clobber clean: clean_build clean_export clean_jdk +clean_build: + $(RM) -r $(C1_DIR) + $(RM) -r $(C2_DIR) + $(RM) -r $(KERNEL_DIR) +clean_export: + $(RM) -r $(EXPORT_PATH) +clean_jdk: + $(RM) -r $(JDK_IMAGE_DIR) + +# +# Create JDK and place this build into it +# +create_jdk: copy_jdk update_jdk + +update_jdk: export_product_jdk export_fastdebug_jdk test_jdk + +copy_jdk: $(JDK_IMAGE_DIR)/jre/lib/rt.jar + +$(JDK_IMAGE_DIR)/jre/lib/rt.jar: + $(RM) -r $(JDK_IMAGE_DIR) + $(MKDIR) -p $(JDK_IMAGE_DIR) + ($(CD) $(JDK_IMPORT_PATH) && \ + $(TAR) -cf - *) | \ + ($(CD) $(JDK_IMAGE_DIR) && $(TAR) -xf -) + +test_jdk: + ifeq ($(ARCH_DATA_MODEL), 32) + $(JDK_IMAGE_DIR)/bin/java -client -version + endif + $(JDK_IMAGE_DIR)/bin/java -server -version + +copy_product_jdk: + $(RM) -r $(JDK_IMAGE_DIR) + $(MKDIR) -p $(JDK_IMAGE_DIR) + ($(CD) $(JDK_IMPORT_PATH) && \ + $(TAR) -cf - bin include jre lib) | \ + ($(CD) $(JDK_IMAGE_DIR) && $(TAR) -xf -) + +copy_fastdebug_jdk: + $(RM) -r $(JDK_IMAGE_DIR)/fastdebug + $(MKDIR) -p $(JDK_IMAGE_DIR)/fastdebug + if [ -d $(JDK_IMPORT_PATH)/fastdebug ] ; then \ + ($(CD) $(JDK_IMPORT_PATH)/fastdebug && \ + $(TAR) -cf - bin include jre lib) | \ + ($(CD) $(JDK_IMAGE_DIR)/fastdebug && $(TAR) -xf -) ; \ + else \ + ($(CD) $(JDK_IMPORT_PATH) && \ + $(TAR) -cf - bin include jre lib) | \ + ($(CD) $(JDK_IMAGE_DIR)/fastdebug && $(TAR) -xf -) ; \ + fi + +copy_debug_jdk: + $(RM) -r $(JDK_IMAGE_DIR)/debug + $(MKDIR) -p $(JDK_IMAGE_DIR)/debug + if [ -d $(JDK_IMPORT_PATH)/debug ] ; then \ + ($(CD) $(JDK_IMPORT_PATH)/debug && \ + $(TAR) -cf - bin include jre lib) | \ + ($(CD) $(JDK_IMAGE_DIR)/debug && $(TAR) -xf -) ; \ + elif [ -d $(JDK_IMPORT_PATH)/fastdebug ] ; then \ + ($(CD) $(JDK_IMPORT_PATH)/fastdebug && \ + $(TAR) -cf - bin include jre lib) | \ + ($(CD) $(JDK_IMAGE_DIR)/debug && $(TAR) -xf -) ; \ + else \ + ($(CD) $(JDK_IMPORT_PATH) && \ + $(TAR) -cf - bin include jre lib) | \ + ($(CD) $(JDK_IMAGE_DIR)/debug && $(TAR) -xf -) ; \ + fi + +# +# Check target +# +check: variable_check + +# +# Help target +# +help: intro_help target_help variable_help notes_help examples_help + +# Intro help message +intro_help: + @$(ECHO) \ +"Makefile for the Hotspot workspace." + @$(ECHO) \ +"Default behavior is to build and create an export area for the j2se builds." + +# Target help +target_help: + @$(ECHO) "help: This help message" + @$(ECHO) "all: Same as: all_product all_fastdebug" + @$(ECHO) "world: Same as: all create_jdk" + @$(ECHO) "all_product: Same as: product product1 export_product" + @$(ECHO) "all_fastdebug: Same as: fastdebug fastdebug1 export_fastdebug" + @$(ECHO) "all_debug: Same as: jvmg jvmg1 export_debug" + @$(ECHO) "all_optimized: Same as: optimized optimized1 export_optimized" + @$(ECHO) "clean: Clean all areas" + @$(ECHO) "export_product: Export product files to EXPORT_PATH" + @$(ECHO) "export_fastdebug: Export fastdebug files to EXPORT_PATH" + @$(ECHO) "export_debug: Export debug files to EXPORT_PATH" + @$(ECHO) "export_optimized: Export optimized files to EXPORT_PATH" + @$(ECHO) "create_jdk: Create JDK image, export all files into it" + @$(ECHO) "update_jdk: Update JDK image with fresh exported files" + @$(ECHO) " " + @$(ECHO) "Others targets are:" + @$(ECHO) " $(C1_VM_TARGETS)" + @$(ECHO) " $(C2_VM_TARGETS)" + @$(ECHO) " $(KERNEL_VM_TARGETS)" + +# Variable help (only common ones used by this workspace) +variable_help: variable_help_intro variable_list variable_help_end +variable_help_intro: + @$(ECHO) "--- Common Variables ---" +variable_help_end: + @$(ECHO) " " + @$(ECHO) "--- Make Arguments ---" + @$(ECHO) "MAKE_ARGS=$(MAKE_ARGS)" + +# One line descriptions for the variables +SLASH_JAVA.desc = Root of all build tools, e.g. /java or J: +OUTPUTDIR.desc = Output directory, default is build/ +BOOTDIR.desc = JDK used to compile agent java source and test with +JDK_IMPORT_PATH.desc = Promoted JDK to copy for 'create_jdk' +EXPORT_PATH.desc = Directory to place files to export for JDK build + +# Make variables to print out (description and value) +VARIABLE_PRINTVAL_LIST += \ + SLASH_JAVA \ + OUTPUTDIR \ + BOOTDIR \ + JDK_IMPORT_PATH \ + EXPORT_PATH + +# Make variables that should refer to directories that exist +VARIABLE_CHECKDIR_LIST += \ + SLASH_JAVA \ + BOOTDIR \ + JDK_IMPORT_PATH + +# For pattern rules below, so all are treated the same +DO_PRINTVAL_LIST=$(VARIABLE_PRINTVAL_LIST:%=%.printval) +DO_CHECKDIR_LIST=$(VARIABLE_CHECKDIR_LIST:%=%.checkdir) + +# Complete variable check +variable_check: $(DO_CHECKDIR_LIST) +variable_list: $(DO_PRINTVAL_LIST) variable_check + +# Pattern rule for printing out a variable +%.printval: + @$(ECHO) " ALT_$* - $($*.desc)" + @$(ECHO) " $*=$($*)" + +# Pattern rule for checking to see if a variable with a directory exists +%.checkdir: + @if [ ! -d $($*) ] ; then \ + $(ECHO) "WARNING: $* does not exist, try $(MAKE) sanity"; \ + fi + +# Pattern rule for checking to see if a variable with a file exists +%.checkfil: + @if [ ! -f $($*) ] ; then \ + $(ECHO) "WARNING: $* does not exist, try $(MAKE) sanity"; \ + fi + +# Misc notes on help +notes_help: + @$(ECHO) \ +"--- Notes --- " + @$(ECHO) \ +"- JDK_IMPORT_PATH must refer to a compatible build, not all past promoted" + @$(ECHO) \ +" builds or previous release JDK builds will work." + @$(ECHO) \ +"- The fastest builds have been when the workspace and the BOOTDIR are on" + @$(ECHO) \ +" local disk." + +examples_help: + @$(ECHO) \ +"--- Examples --- " + @$(ECHO) \ +" $(MAKE) all" + @$(ECHO) \ +" $(MAKE) world" + @$(ECHO) \ +" $(MAKE) ALT_BOOTDIR=/opt/java/jdk$(PREVIOUS_JDK_VERSION)" + @$(ECHO) \ +" $(MAKE) ALT_JDK_IMPORT_PATH=/opt/java/jdk$(JDK_VERSION)" + +# JPRT rule to build this workspace +include $(GAMMADIR)/make/jprt.gmk + +.PHONY: all world clobber clean help $(C1_VM_TARGETS) $(C2_VM_TARGETS) \ + $(KERNEL_VM_TARGETS) \ + generic_build1 generic_build2 generic_buildkernel generic_export \ + export_product export_fastdebug export_debug export_optimized \ + export_jdk_product export_jdk_fastdebug export_jdk_debug \ + create_jdk copy_jdk update_jdk test_jdk \ + copy_product_jdk copy_fastdebug_jdk copy_debug_jdk + diff --git a/hotspot/make/README b/hotspot/make/README new file mode 100644 index 00000000000..19afb261fc3 --- /dev/null +++ b/hotspot/make/README @@ -0,0 +1,14 @@ +README: + This file should be located at the top of the hotspot Mercurial repository. + + See http://openjdk.java.net/ for more information about the OpenJDK. + + See ../README-builds.html for complete details on build machine requirements. + +Simple Build Instructions: + + cd make && gnumake + + The files that will be imported into the jdk build will be in the "build" + directory. + diff --git a/hotspot/make/defs.make b/hotspot/make/defs.make new file mode 100644 index 00000000000..f3e686ae54e --- /dev/null +++ b/hotspot/make/defs.make @@ -0,0 +1,262 @@ +# +# Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# The common definitions for hotspot builds. + +# Default to verbose build logs (show all compile lines): +MAKE_VERBOSE=y + +# Make macros for install files or preparing targets +CD=cd +CP=cp +ECHO=echo +GREP=grep +MKDIR=mkdir +MV=mv +PWD=pwd +RM=rm -f +SED=sed +TAR=tar +ZIPEXE=zip + +define install-file +@$(MKDIR) -p $(@D) +@$(RM) $@ +$(CP) $< $@ +endef +define prep-target +@$(MKDIR) -p $(@D) +@$(RM) $@ +endef + +# Directory paths and user name +# Unless GAMMADIR is set on the command line, search upward from +# the current directory for a parent directory containing "src/share/vm". +# If that fails, look for $GAMMADIR in the environment. +# When the tree of subdirs is built, this setting is stored in each flags.make. +GAMMADIR := $(shell until ([ -d dev ]&&echo $${GAMMADIR:-/GAMMADIR/}) || ([ -d src/share/vm ]&&pwd); do cd ..; done) +HS_SRC_DIR=$(GAMMADIR)/src +HS_BUILD_DIR=$(GAMMADIR)/build + +ifeq ($(USER),) + USER=$(USERNAME) +endif + +# hotspot version definitions +include $(GAMMADIR)/make/hotspot_version + +# Java versions needed +ifeq ($(PREVIOUS_JDK_VERSION),) + PREVIOUS_JDK_VERSION=$(JDK_PREVIOUS_VERSION) +endif +ifeq ($(JDK_MAJOR_VERSION),) + JDK_MAJOR_VERSION=$(JDK_MAJOR_VER) +endif +ifeq ($(JDK_MINOR_VERSION),) + JDK_MINOR_VERSION=$(JDK_MINOR_VER) +endif +ifeq ($(JDK_MICRO_VERSION),) + JDK_MICRO_VERSION=$(JDK_MICRO_VER) +endif +ifeq ($(JDK_MKTG_VERSION),) + JDK_MKTG_VERSION=$(JDK_MINOR_VERSION).$(JDK_MICRO_VERSION) +endif +ifeq ($(JDK_VERSION),) + JDK_VERSION=$(JDK_MAJOR_VERSION).$(JDK_MINOR_VERSION).$(JDK_MICRO_VERSION) +endif +ifeq ($(FULL_VERSION),) + FULL_VERSION="$(JDK_VERSION)" +endif + +# FULL_VERSION is only used to define JRE_RELEASE_VERSION which is used +# as JRE version in VM -Xinternalversion output. +ifndef JRE_RELEASE_VERSION + JRE_RELEASE_VERSION=$(FULL_VERSION) +endif + +ifndef HOTSPOT_RELEASE_VERSION + HOTSPOT_RELEASE_VERSION=$(HS_MAJOR_VER).$(HS_MINOR_VER)-b$(HS_BUILD_NUMBER) +endif + +ifdef HOTSPOT_BUILD_VERSION +# specified in command line (PRT build) +else + ifdef JPRT_BUILD_VERSION +# JPR build + HOTSPOT_BUILD_VERSION=$(JPRT_BUILD_VERSION) + else + ifdef COOKED_BUILD_NUMBER +# JRE build + HOTSPOT_BUILD_VERSION= + else + HOTSPOT_BUILD_VERSION=internal + endif + endif +endif + +# Windows should have OS predefined +ifeq ($(OS),) + OS := $(shell uname -s) + HOST := $(shell uname -n) +endif + +# If not SunOS and not Linux, assume Windows +ifneq ($(OS), Linux) + ifneq ($(OS), SunOS) + OSNAME=windows + else + OSNAME=solaris + endif +else + OSNAME=linux +endif + +# Determinations of default make arguments and platform specific settings +MAKE_ARGS= + +# ARCH_DATA_MODEL==64 is equivalent to LP64=1 +ifeq ($(ARCH_DATA_MODEL), 64) + ifndef LP64 + LP64 := 1 + endif +endif + +# Defaults set for product build +EXPORT_SUBDIR= + +# Change default /java path if requested +ifneq ($(ALT_SLASH_JAVA),) + SLASH_JAVA=$(ALT_SLASH_JAVA) +endif + +# Default OUTPUTDIR +OUTPUTDIR=$(HS_BUILD_DIR)/$(OSNAME) +ifneq ($(ALT_OUTPUTDIR),) + OUTPUTDIR=$(ALT_OUTPUTDIR) +endif + +# Find latest promoted JDK area +JDK_IMPORT_PATH=$(SLASH_JAVA)/re/j2se/$(JDK_VERSION)/promoted/latest/binaries/$(PLATFORM) +ifneq ($(ALT_JDK_IMPORT_PATH),) + JDK_IMPORT_PATH=$(ALT_JDK_IMPORT_PATH) +endif + +# Find JDK used for javac compiles +BOOTDIR=$(SLASH_JAVA)/re/j2se/$(PREVIOUS_JDK_VERSION)/latest/binaries/$(PLATFORM) +ifneq ($(ALT_BOOTDIR),) + BOOTDIR=$(ALT_BOOTDIR) +endif + +# The platform dependent defs.make defines platform specific variable such +# as ARCH, EXPORT_LIST etc. We must place the include here after BOOTDIR is defined. +include $(GAMMADIR)/build/$(OSNAME)/makefiles/defs.make + +# We are trying to put platform specific defintions +# files to build/$(OSNAME)/makefiles dictory. However +# some definitions are common for both linux and solaris, +# so we put them here. +ifneq ($(OSNAME),windows) + ABS_OUTPUTDIR := $(shell $(CD) $(OUTPUTDIR); $(PWD)) + ABS_BOOTDIR := $(shell $(CD) $(BOOTDIR); $(PWD)) + ABS_GAMMADIR := $(shell $(CD) $(GAMMADIR); $(PWD)) + ABS_OS_MAKEFILE := $(shell $(CD) $(HS_BUILD_DIR)/$(OSNAME); $(PWD))/Makefile + + # uname, HotSpot source directory, build directory and JDK use different names + # for CPU architectures. + # ARCH - uname output + # SRCARCH - where to find HotSpot cpu and os_cpu source files + # BUILDARCH - build directory + # LIBARCH - directory name in JDK/JRE + + # Use uname output for SRCARCH, but deal with platform differences. If ARCH + # is not explicitly listed below, it is treated as x86. + SRCARCH = $(ARCH/$(filter sparc sparc64 ia64 amd64 x86_64,$(ARCH))) + ARCH/ = x86 + ARCH/sparc = sparc + ARCH/sparc64= sparc + ARCH/ia64 = ia64 + ARCH/amd64 = x86 + ARCH/x86_64 = x86 + + # BUILDARCH is usually the same as SRCARCH, except for sparcv9 + BUILDARCH = $(SRCARCH) + ifeq ($(BUILDARCH), x86) + ifdef LP64 + BUILDARCH = amd64 + else + BUILDARCH = i486 + endif + endif + ifeq ($(BUILDARCH), sparc) + ifdef LP64 + BUILDARCH = sparcv9 + endif + endif + + # LIBARCH is 1:1 mapping from BUILDARCH + LIBARCH = $(LIBARCH/$(BUILDARCH)) + LIBARCH/i486 = i386 + LIBARCH/amd64 = amd64 + LIBARCH/sparc = sparc + LIBARCH/sparcv9 = sparcv9 + LIBARCH/ia64 = ia64 + + LP64_ARCH = sparcv9 amd64 ia64 +endif + +# Required make macro settings for all platforms +MAKE_ARGS += JAVA_HOME=$(ABS_BOOTDIR) +MAKE_ARGS += GAMMADIR=$(ABS_GAMMADIR) +MAKE_ARGS += MAKE_VERBOSE=$(MAKE_VERBOSE) +MAKE_ARGS += HOTSPOT_RELEASE_VERSION=$(HOTSPOT_RELEASE_VERSION) +MAKE_ARGS += JRE_RELEASE_VERSION=$(JRE_RELEASE_VERSION) + +# Pass HOTSPOT_BUILD_VERSION as argument to OS specific Makefile +# to overwrite the default definition since OS specific Makefile also +# includes this make/defs.make file. +MAKE_ARGS += HOTSPOT_BUILD_VERSION=$(HOTSPOT_BUILD_VERSION) + +# Select name of export directory +EXPORT_PATH=$(OUTPUTDIR)/export-$(PLATFORM)$(EXPORT_SUBDIR) +ifneq ($(ALT_EXPORT_PATH),) + EXPORT_PATH=$(ALT_EXPORT_PATH) +endif + +# Default jdk image if one is created for you with create_jdk +JDK_IMAGE_DIR=$(OUTPUTDIR)/jdk-$(PLATFORM) + +# Various export sub directories +EXPORT_INCLUDE_DIR = $(EXPORT_PATH)/include +EXPORT_DOCS_DIR = $(EXPORT_PATH)/docs +EXPORT_LIB_DIR = $(EXPORT_PATH)/lib +EXPORT_JRE_DIR = $(EXPORT_PATH)/jre +EXPORT_JRE_BIN_DIR = $(EXPORT_JRE_DIR)/bin +EXPORT_JRE_LIB_DIR = $(EXPORT_JRE_DIR)/lib +EXPORT_JRE_LIB_ARCH_DIR = $(EXPORT_JRE_LIB_DIR)/$(LIBARCH) + +# Common export list of files +EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jvmti.h +EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jni.h +EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/$(JDK_INCLUDE_SUBDIR)/jni_md.h +EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jmm.h diff --git a/hotspot/make/hotspot_version b/hotspot/make/hotspot_version new file mode 100644 index 00000000000..1eb1bb178af --- /dev/null +++ b/hotspot/make/hotspot_version @@ -0,0 +1,45 @@ +# +# Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# +# + +# +# Master Hotspot version file. These values may be overridden by a control +# workspace build. This file format must remain compatible with both +# GNU Makefile and Microsoft nmake formats. +# + +# Don't put quotes (fail windows build). +HOTSPOT_VM_COPYRIGHT=Copyright 2007 + +HS_MAJOR_VER=12 +HS_MINOR_VER=0 +HS_BUILD_NUMBER=01 + +JDK_MAJOR_VER=1 +JDK_MINOR_VER=7 +JDK_MICRO_VER=0 + +# Previous (bootdir) JDK version +JDK_PREVIOUS_VERSION=1.6.0 diff --git a/hotspot/make/jprt.config b/hotspot/make/jprt.config new file mode 100644 index 00000000000..2c1f0dce2f4 --- /dev/null +++ b/hotspot/make/jprt.config @@ -0,0 +1,261 @@ +#!echo "This is not a shell script" +# +# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +############################################################################# +# Error +error() # message +{ + echo "ERROR: $1" + exit 6 +} +# Directory must exist +dirMustExist() # dir name +{ + if [ ! -d "$1" ] ; then + error "Directory for $2 does not exist: $1" + fi +} +# File must exist +fileMustExist() # dir name +{ + if [ ! -f "$1" ] ; then + error "File for $2 does not exist: $1" + fi +} +############################################################################# + +# Should be set by JPRT as the 3 basic inputs +bootdir="${ALT_BOOTDIR}" +slashjava="${ALT_SLASH_JAVA}" +jdk_import="${ALT_JDK_IMPORT_PATH}" + +# Check input +dirMustExist "${bootdir}" ALT_BOOTDIR +dirMustExist "${slashjava}" ALT_SLASH_JAVA +dirMustExist "${jdk_import}" ALT_JDK_IMPORT_PATH + +# Uses 'uname -s', but only expect SunOS or Linux, assume Windows otherwise. +osname=`uname -s` +if [ "${osname}" = SunOS ] ; then + + # SOLARIS: Sparc or X86 + osarch=`uname -p` + if [ "${osarch}" = sparc ] ; then + solaris_arch=sparc + else + solaris_arch=i386 + fi + + # Get the SS11 compilers into path (make sure it matches ALT setting) + compiler_path=${slashjava}/devtools/${solaris_arch}/SUNWspro/SS11/bin + dirMustExist "${compiler_path}" COMPILER_PATH + path4sdk=${compiler_path} + + # Add basic solaris system paths + path4sdk=${path4sdk}:/usr/ccs/bin:/usr/ccs/lib:/usr/bin:/bin:/usr/sfw/bin + + # Get the previous JDK to be used to bootstrap the build + path4sdk=${bootdir}/bin:${path4sdk} + + # Find GNU make + make=/usr/sfw/bin/gmake + if [ ! -f ${make} ] ; then + make=/opt/sfw/bin/gmake + if [ ! -f ${make} ] ; then + make=${slashjava}/devtools/${solaris_arch}/bin/gnumake + fi + fi + fileMustExist "${make}" make + + # File creation mask + umask 002 + +elif [ "${osname}" = Linux ] ; then + + # LINUX: X86, AMD64 + osarch=`uname -m` + if [ "${osarch}" = i686 ] ; then + linux_arch=i586 + elif [ "${osarch}" = x86_64 ] ; then + linux_arch=amd64 + fi + + # Get the compilers into path (make sure it matches ALT setting) + compiler_path=/usr/bin + dirMustExist "${compiler_path}" COMPILER_PATH + path4sdk=${compiler_path} + + # Add basic paths + path4sdk=${path4sdk}:/usr/bin:/bin:/usr/sbin:/sbin + + # Get the previous JDK to be used to bootstrap the build + path4sdk=${bootdir}/bin:${path4sdk} + + # Find GNU make + make=/usr/bin/make + fileMustExist "${make}" make + + umask 002 + +else + + # Windows: Differs on CYGWIN vs. MKS, and the compiler available. + # Also, blanks in pathnames gives GNU make headaches, so anything placed + # in any ALT_* variable should be the short windows dosname. + + # WINDOWS: Install and use MKS or CYGWIN (should have already been done) + # Assumption here is that you are in a shell window via MKS or cygwin. + # MKS install should have defined the environment variable ROOTDIR. + # We also need to figure out which one we have: X86, AMD64 + if [ "`echo ${PROCESSOR_IDENTIFIER} | fgrep AMD64`" != "" ] ; then + windows_arch=amd64 + else + windows_arch=i586 + fi + + # We need to determine if we are running a CYGWIN shell or an MKS shell + # (if uname isn't available, then it will be unix_toolset=unknown) + unix_toolset=unknown + if [ "`uname -a | fgrep Cygwin`" = "" -a -d "${ROOTDIR}" ] ; then + # We kind of assume ROOTDIR is where MKS is and it's ok + unix_toolset=MKS + mkshome=`dosname -s "${ROOTDIR}"` + # Utility to convert to short pathnames without spaces + dosname="${mkshome}/mksnt/dosname -s" + # Most unix utilities are in the mksnt directory of ROOTDIR + unixcommand_path="${mkshome}/mksnt" + path4sdk="${unixcommand_path}" + dirMustExist "${unixcommand_path}" UNIXCOMMAND_PATH + devtools_path="${slashjava}/devtools/win32/bin" + path4sdk="${devtools_path};${path4sdk}" + dirMustExist "${devtools_path}" DEVTOOLS_PATH + # Find GNU make + make="${devtools_path}/gnumake.exe" + fileMustExist "${make}" make + elif [ "`uname -a | fgrep Cygwin`" != "" -a -f /bin/cygpath ] ; then + # For CYGWIN, uname will have "Cygwin" in it, and /bin/cygpath should exist + unix_toolset=CYGWIN + # Utility to convert to short pathnames without spaces + dosname="/usr/bin/cygpath -a -m -s" + # Most unix utilities are in the /usr/bin + unixcommand_path="/usr/bin" + path4sdk="${unixcommand_path}" + dirMustExist "${unixcommand_path}" UNIXCOMMAND_PATH + # Find GNU make + make="${unixcommand_path}/make.exe" + fileMustExist "${make}" make + else + echo "WARNING: Cannot figure out if this is MKS or CYGWIN" + fi + + # WINDOWS: Compiler setup (nasty part) + # NOTE: You can use vcvars32.bat to set PATH, LIB, and INCLUDE. + # NOTE: CYGWIN has a link.exe too, make sure the compilers are first + if [ "${windows_arch}" = i586 ] ; then + # 32bit Windows compiler settings + # VisualStudio .NET 2003 VC++ 7.1 (VS71COMNTOOLS should be defined) + vs_root=`${dosname} "${VS71COMNTOOLS}/../.."` + # Fill in PATH, LIB, and INCLUDE (unset all others to make sure) + vc7_root="${vs_root}/Vc7" + compiler_path="${vc7_root}/bin" + platform_sdk="${vc7_root}/PlatformSDK" + # LIB and INCLUDE must use ; as a separator + include4sdk="${vc7_root}/atlmfc/include" + include4sdk="${include4sdk};${vc7_root}/include" + include4sdk="${include4sdk};${platform_sdk}/include/prerelease" + include4sdk="${include4sdk};${platform_sdk}/include" + include4sdk="${include4sdk};${vs_root}/SDK/v1.1/include" + lib4sdk="${vc7_root}/atlmfc/lib" + lib4sdk="${lib4sdk};${vc7_root}/lib" + lib4sdk="${lib4sdk};${platform_sdk}/lib/prerelease" + lib4sdk="${lib4sdk};${platform_sdk}/lib" + lib4sdk="${lib4sdk};${vs_root}/SDK/v1.1/lib" + # Search path and DLL locating path + # WARNING: CYGWIN has a link.exe too, make sure compilers are first + path4sdk="${vs_root}/Common7/Tools/bin;${path4sdk}" + path4sdk="${vs_root}/SDK/v1.1/bin;${path4sdk}" + path4sdk="${vs_root}/Common7/Tools;${path4sdk}" + path4sdk="${vs_root}/Common7/Tools/bin/prerelease;${path4sdk}" + path4sdk="${vs_root}/Common7/IDE;${path4sdk}" + path4sdk="${compiler_path};${path4sdk}" + elif [ "${windows_arch}" = amd64 ] ; then + # AMD64 64bit Windows compiler settings + if [ "${MSSDK}" != "" ] ; then + platform_sdk="${MSSDK}" + else + platform_sdk=`${dosname} "C:/Program Files/Microsoft Platform SDK/"` + fi + compiler_path="${platform_sdk}/Bin/win64/x86/AMD64" + # LIB and INCLUDE must use ; as a separator + include4sdk="${platform_sdk}/Include" + include4sdk="${include4sdk};${platform_sdk}/Include/crt/sys" + include4sdk="${include4sdk};${platform_sdk}/Include/mfc" + include4sdk="${include4sdk};${platform_sdk}/Include/atl" + include4sdk="${include4sdk};${platform_sdk}/Include/crt" + lib4sdk="${platform_sdk}/Lib/AMD64" + lib4sdk="${lib4sdk};${platform_sdk}/Lib/AMD64/atlmfc" + # Search path and DLL locating path + # WARNING: CYGWIN has a link.exe too, make sure compilers are first + path4sdk="${platform_sdk}/bin;${path4sdk}" + path4sdk="${compiler_path};${path4sdk}" + fi + # Export LIB and INCLUDE + unset lib + unset Lib + LIB="${lib4sdk}" + export LIB + unset include + unset Include + INCLUDE="${include4sdk}" + export INCLUDE + # Set the ALT variable + dirMustExist "${compiler_path}" COMPILER_PATH + + # WINDOWS: Get the previous JDK to be used to bootstrap the build + path4sdk="${bootdir}/bin;${path4sdk}" + + # Turn all \\ into /, remove duplicates and trailing / + slash_path="`echo ${path4sdk} | sed -e 's@\\\\@/@g' -e 's@//@/@g' -e 's@/$@@' -e 's@/;@;@g'`" + + # For windows, it's hard to know where the system is, so we just add this + # to PATH. + path4sdk="${slash_path};${PATH}" + + # Convert path4sdk to cygwin style + if [ "${unix_toolset}" = CYGWIN ] ; then + path4sdk="`/usr/bin/cygpath -p ${path4sdk}`" + fi + +fi + +# Export PATH setting +PATH="${path4sdk}" +export PATH + +# Unset certain vars +unset LD_LIBRARY_PATH +unset LD_LIBRARY_PATH_32 +unset LD_LIBRARY_PATH_64 + diff --git a/hotspot/make/jprt.gmk b/hotspot/make/jprt.gmk new file mode 100644 index 00000000000..e43f7638c1e --- /dev/null +++ b/hotspot/make/jprt.gmk @@ -0,0 +1,45 @@ +# +# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# JPRT rule to build this workspace + +JPRT_ARCHIVE_BUNDLE=$(ABS_OUTPUTDIR)/$(JPRT_BUILD_FLAVOR)-bundle.zip +ifdef JPRT_BUILD_VERSION + MILESTONE=$(JPRT_BUILD_VERSION) +endif + +jprt_build_product: all_product copy_product_jdk export_product_jdk + ( $(CD) $(JDK_IMAGE_DIR) && \ + $(ZIPEXE) -q -r $(JPRT_ARCHIVE_BUNDLE) . ) + +jprt_build_fastdebug: all_fastdebug copy_fastdebug_jdk export_fastdebug_jdk + ( $(CD) $(JDK_IMAGE_DIR)/fastdebug && \ + $(ZIPEXE) -q -r $(JPRT_ARCHIVE_BUNDLE) . ) + +jprt_build_debug: all_debug copy_debug_jdk export_debug_jdk + ( $(CD) $(JDK_IMAGE_DIR)/debug && \ + $(ZIPEXE) -q -r $(JPRT_ARCHIVE_BUNDLE) . ) + +.PHONY: jprt_build_product jprt_build_fastdebug jprt_build_debug + diff --git a/hotspot/make/jprt.properties b/hotspot/make/jprt.properties new file mode 100644 index 00000000000..1e9f5fb6413 --- /dev/null +++ b/hotspot/make/jprt.properties @@ -0,0 +1,227 @@ +# +# Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Properties for jprt + +JPRT.tools.default.release=jdk1.7.0 + +# Build result bundles are not partial builds| but include everything +JPRT.need.sibling.build=false + +# Standard list of JPRT build targets for this workspace +JPRT.build.targets= \ + solaris_sparc_5.10-{product|fastdebug|debug}, \ + solaris_sparcv9_5.10-{product|fastdebug|debug}, \ + solaris_i586_5.10-{product|fastdebug|debug}, \ + solaris_x64_5.10-{product|fastdebug|debug}, \ + linux_i586-{product|fastdebug|debug}, \ + linux_x64-{product|fastdebug}, \ + windows_i586-{product|fastdebug|debug}, \ + windows_x64-{product|fastdebug|debug} + +# Standard list of JPRT test targets for this workspace +JPRT.test.targets = \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-jvm98, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-scimark, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-jvm98, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-scimark, \ + solaris_i586_5.10-{product|fastdebug}-{c1|c2}-jvm98, \ + solaris_i586_5.10-{product|fastdebug}-{c1|c2}-scimark, \ + solaris_x64_5.10-{product|fastdebug}-c2-jvm98, \ + solaris_x64_5.10-{product|fastdebug}-c2-scimark, \ + linux_i586-{product|fastdebug}-{c1|c2}-jvm98, \ + linux_i586-{product|fastdebug}-{c1|c2}-scimark, \ + linux_x64-{product|fastdebug}-c2-jvm98, \ + linux_x64-{product|fastdebug}-c2-scimark, \ + windows_i586-{product|fastdebug}-{c1|c2}-jvm98, \ + windows_i586-{product|fastdebug}-{c1|c2}-scimark, \ + windows_x64-{product|fastdebug}-c2-jvm98, \ + windows_x64-{product|fastdebug}-c2-scimark, \ + solaris_sparc_5.10-product-{c1|c2}-runThese, \ + solaris_sparc_5.10-product-{c1|c2}-runThese_Xcomp, \ + solaris_sparc_5.10-product-{c1|c2}-runThese_Xcomp_2, \ + solaris_sparc_5.10-product-{c1|c2}-runThese_Xcomp_3, \ + solaris_sparc_5.10-fastdebug-c1-runThese_Xshare, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCBasher_default, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCBasher_SerialGC, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCBasher_ParallelGC, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCBasher_ParNewGC, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCBasher_CMS, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCBasher_default_2, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCBasher_SerialGC_2, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCBasher_ParallelGC_2, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCBasher_ParNewGC_2, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCBasher_CMS_2, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCOld_default, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCOld_SerialGC, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCOld_ParallelGC, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCOld_ParNewGC, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-GCOld_CMS, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-jbb_default, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-jbb_SerialGC, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-jbb_ParallelGC, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-jbb_CMS, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-scimark_2, \ + solaris_sparc_5.10-{product|fastdebug}-{c1|c2}-scimark_3, \ + solaris_sparcv9_5.10-product-c2-runThese, \ + solaris_sparcv9_5.10-product-c2-runThese_Xcomp, \ + solaris_sparcv9_5.10-product-c2-runThese_Xcomp_2, \ + solaris_sparcv9_5.10-product-c2-runThese_Xcomp_3, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCBasher_default, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCBasher_SerialGC, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCBasher_ParallelGC, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCBasher_ParNewGC, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCBasher_CMS, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCBasher_default_2, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCBasher_SerialGC_2, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCBasher_ParallelGC_2, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCBasher_ParNewGC_2, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCBasher_CMS_2, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCOld_default, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCOld_SerialGC, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCOld_ParallelGC, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCOld_ParNewGC, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-GCOld_CMS, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-jbb_default, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-jbb_SerialGC, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-jbb_ParallelGC, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-jbb_CMS, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-scimark_2, \ + solaris_sparcv9_5.10-{product|fastdebug}-c2-scimark_3, \ + solaris_x64-product-c2-runThese, \ + solaris_x64-product-c2-runThese_Xcomp, \ + solaris_x64-{product|fastdebug}-c2-GCBasher_default, \ + solaris_x64-{product|fastdebug}-c2-GCBasher_SerialGC, \ + solaris_x64-{product|fastdebug}-c2-GCBasher_ParallelGC, \ + solaris_x64-{product|fastdebug}-c2-GCBasher_ParNewGC, \ + solaris_x64-{product|fastdebug}-c2-GCBasher_CMS, \ + solaris_x64-{product|fastdebug}-c2-GCBasher_default_2, \ + solaris_x64-{product|fastdebug}-c2-GCBasher_SerialGC_2, \ + solaris_x64-{product|fastdebug}-c2-GCBasher_ParallelGC_2, \ + solaris_x64-{product|fastdebug}-c2-GCBasher_ParNewGC_2, \ + solaris_x64-{product|fastdebug}-c2-GCBasher_CMS_2, \ + solaris_x64-{product|fastdebug}-c2-GCOld_default, \ + solaris_x64-{product|fastdebug}-c2-GCOld_SerialGC, \ + solaris_x64-{product|fastdebug}-c2-GCOld_ParallelGC, \ + solaris_x64-{product|fastdebug}-c2-GCOld_ParNewGC, \ + solaris_x64-{product|fastdebug}-c2-GCOld_CMS, \ + solaris_x64-{product|fastdebug}-c2-jbb_default, \ + solaris_x64-{product|fastdebug}-c2-jbb_SerialGC, \ + solaris_x64-{product|fastdebug}-c2-jbb_ParallelGC, \ + solaris_x64-{product|fastdebug}-c2-jbb_CMS, \ + solaris_i586_5.10-product-{c1|c2}-runThese_Xcomp, \ + solaris_i586_5.10-product-c2-runThese_Xcomp_2, \ + solaris_i586_5.10-fastdebug-c1-runThese_Xcomp_2, \ + solaris_i586_5.10-fastdebug-c1-runThese_Xshare, \ + solaris_i586_5.10-product-c1-GCBasher_default, \ + solaris_i586_5.10-product-c1-GCBasher_SerialGC, \ + solaris_i586_5.10-product-c1-GCBasher_ParallelGC, \ + solaris_i586_5.10-product-c1-GCBasher_ParNewGC, \ + solaris_i586_5.10-product-c1-GCBasher_CMS, \ + solaris_i586_5.10-fastdebug-c2-GCBasher_default, \ + solaris_i586_5.10-fastdebug-c2-GCBasher_SerialGC, \ + solaris_i586_5.10-fastdebug-c2-GCBasher_ParallelGC, \ + solaris_i586_5.10-fastdebug-c2-GCBasher_ParNewGC, \ + solaris_i586_5.10-fastdebug-c2-GCBasher_CMS, \ + solaris_i586_5.10-product-c1-GCOld_default, \ + solaris_i586_5.10-product-c1-GCOld_SerialGC, \ + solaris_i586_5.10-product-c1-GCOld_ParallelGC, \ + solaris_i586_5.10-product-c1-GCOld_ParNewGC, \ + solaris_i586_5.10-product-c1-GCOld_CMS, \ + solaris_i586_5.10-fastdebug-c2-jbb_default, \ + solaris_i586_5.10-fastdebug-c2-jbb_ParallelGC, \ + solaris_i586_5.10-fastdebug-c2-jbb_CMS, \ + solaris_i586_5.10-{product|fastdebug}-{c1|c2}-scimark_2, \ + solaris_i586_5.10-{product|fastdebug}-{c1|c2}-scimark_3, \ + linux_i586-product-c1-runThese_Xcomp, \ + linux_i586-product-c1-runThese_Xcomp_2, \ + linux_i586-product-c1-runThese_Xcomp_3, \ + linux_i586-fastdebug-c1-runThese_Xshare, \ + linux_i586-fastdebug-c2-runThese_Xcomp, \ + linux_i586-fastdebug-c2-runThese_Xcomp_2, \ + linux_i586-{product|fastdebug}-{c1|c2}-GCBasher_default, \ + linux_i586-{product|fastdebug}-{c1|c2}-GCBasher_SerialGC, \ + linux_i586-{product|fastdebug}-{c1|c2}-GCBasher_ParallelGC, \ + linux_i586-{product|fastdebug}-{c1|c2}-GCBasher_ParNewGC, \ + linux_i586-{product|fastdebug}-{c1|c2}-GCBasher_CMS, \ + linux_i586-product-{c1|c2}-GCOld_default, \ + linux_i586-product-{c1|c2}-GCOld_SerialGC, \ + linux_i586-product-{c1|c2}-GCOld_ParallelGC, \ + linux_i586-product-{c1|c2}-GCOld_ParNewGC, \ + linux_i586-product-{c1|c2}-GCOld_CMS, \ + linux_i586-{product|fastdebug}-c1-jbb_default, \ + linux_i586-{product|fastdebug}-c1-jbb_ParallelGC, \ + linux_i586-{product|fastdebug}-c1-jbb_CMS, \ + linux_i586-{product|fastdebug}-c2-scimark_2, \ + linux_i586-{product|fastdebug}-c2-scimark_3, \ + linux_x64-{product|fastdebug}-c2-GCBasher_default, \ + linux_x64-{product|fastdebug}-c2-GCBasher_SerialGC, \ + linux_x64-{product|fastdebug}-c2-GCBasher_ParallelGC, \ + linux_x64-{product|fastdebug}-c2-GCBasher_ParNewGC, \ + linux_x64-{product|fastdebug}-c2-GCBasher_CMS, \ + linux_x64-{product|fastdebug}-c2-GCOld_default, \ + linux_x64-{product|fastdebug}-c2-GCOld_SerialGC, \ + linux_x64-{product|fastdebug}-c2-GCOld_ParallelGC, \ + linux_x64-{product|fastdebug}-c2-GCOld_ParNewGC, \ + linux_x64-{product|fastdebug}-c2-GCOld_CMS, \ + linux_x64-{product|fastdebug}-c2-jbb_default, \ + linux_x64-{product|fastdebug}-c2-jbb_ParallelGC, \ + linux_x64-{product|fastdebug}-c2-scimark_2, \ + linux_x64-{product|fastdebug}-c2-scimark_3, \ + windows_i586-product-{c1|c2}-runThese, \ + windows_i586-product-{c1|c2}-runThese_Xcomp, \ + windows_i586-fastdebug-c1-runThese_Xshare, \ + windows_i586-{product|fastdebug}-{c1|c2}-GCBasher_default, \ + windows_i586-{product|fastdebug}-{c1|c2}-GCBasher_SerialGC, \ + windows_i586-{product|fastdebug}-{c1|c2}-GCBasher_ParallelGC, \ + windows_i586-{product|fastdebug}-{c1|c2}-GCBasher_ParNewGC, \ + windows_i586-{product|fastdebug}-{c1|c2}-GCBasher_CMS, \ + windows_i586-product-{c1|c2}-GCOld_default, \ + windows_i586-product-{c1|c2}-GCOld_SerialGC, \ + windows_i586-product-{c1|c2}-GCOld_ParallelGC, \ + windows_i586-product-{c1|c2}-GCOld_ParNewGC, \ + windows_i586-product-{c1|c2}-GCOld_CMS, \ + windows_i586-{product|fastdebug}-{c1|c2}-jbb_default, \ + windows_i586-product-{c1|c2}-jbb_ParallelGC, \ + windows_i586-product-{c1|c2}-jbb_CMS, \ + windows_i586-product-{c1|c2}-scimark_2, \ + windows_i586-product-{c1|c2}-scimark_3, \ + windows_x64-product-c2-runThese, \ + windows_x64-product-c2-runThese_Xcomp, \ + windows_x64-{product|fastdebug}-c2-GCBasher_default, \ + windows_x64-{product|fastdebug}-c2-GCBasher_SerialGC, \ + windows_x64-{product|fastdebug}-c2-GCBasher_ParallelGC, \ + windows_x64-{product|fastdebug}-c2-GCBasher_ParNewGC, \ + windows_x64-{product|fastdebug}-c2-GCBasher_CMS, \ + windows_x64-{product|fastdebug}-c2-GCOld_default, \ + windows_x64-{product|fastdebug}-c2-GCOld_SerialGC, \ + windows_x64-{product|fastdebug}-c2-GCOld_ParallelGC, \ + windows_x64-{product|fastdebug}-c2-GCOld_ParNewGC, \ + windows_x64-{product|fastdebug}-c2-GCOld_CMS, \ + windows_x64-{product|fastdebug}-c2-jbb_default, \ + windows_x64-product-c2-jbb_CMS, \ + windows_x64-product-c2-jbb_ParallelGC, \ + windows_x64-{product|fastdebug}-c2-scimark_2, \ + windows_x64-{product|fastdebug}-c2-scimark_3 + diff --git a/hotspot/make/scm.make b/hotspot/make/scm.make new file mode 100644 index 00000000000..792cdfdf6cb --- /dev/null +++ b/hotspot/make/scm.make @@ -0,0 +1,28 @@ +# +# Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# Prune out all known SCM (Source Code Management) directories +# so they will not appear on -I PATHs, when copying directory trees, +# packaging up .jar files, etc. This applies to all workspaces. +# +SCM_DIRS = -name .hg -o -name .svn -o -name CVS -o -name RCS -o -name SCCS -o -name Codemgr_wsdata -o -name deleted_files diff --git a/hotspot/make/templates/bsd-header b/hotspot/make/templates/bsd-header new file mode 100644 index 00000000000..12bc44a72bc --- /dev/null +++ b/hotspot/make/templates/bsd-header @@ -0,0 +1,28 @@ +Copyright %YEARS% Sun Microsystems, Inc. All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Sun Microsystems nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/hotspot/make/templates/gpl-cp-header b/hotspot/make/templates/gpl-cp-header new file mode 100644 index 00000000000..8645e291a6e --- /dev/null +++ b/hotspot/make/templates/gpl-cp-header @@ -0,0 +1,22 @@ +Copyright %YEARS% Sun Microsystems, Inc. All Rights Reserved. +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + +This code is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 only, as +published by the Free Software Foundation. Sun designates this +particular file as subject to the "Classpath" exception as provided +by Sun in the LICENSE file that accompanied this code. + +This code is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +version 2 for more details (a copy is included in the LICENSE file that +accompanied this code). + +You should have received a copy of the GNU General Public License version +2 along with this work; if not, write to the Free Software Foundation, +Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +CA 95054 USA or visit www.sun.com if you need additional information or +have any questions. diff --git a/hotspot/make/templates/gpl-header b/hotspot/make/templates/gpl-header new file mode 100644 index 00000000000..07dbc05ecb7 --- /dev/null +++ b/hotspot/make/templates/gpl-header @@ -0,0 +1,20 @@ +Copyright %YEARS% Sun Microsystems, Inc. All Rights Reserved. +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + +This code is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 only, as +published by the Free Software Foundation. + +This code is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +version 2 for more details (a copy is included in the LICENSE file that +accompanied this code). + +You should have received a copy of the GNU General Public License version +2 along with this work; if not, write to the Free Software Foundation, +Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +CA 95054 USA or visit www.sun.com if you need additional information or +have any questions. diff --git a/hotspot/src/cpu/sparc/vm/args.cc b/hotspot/src/cpu/sparc/vm/args.cc new file mode 100644 index 00000000000..e15926f8bae --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/args.cc @@ -0,0 +1,309 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include + +static const int R_O0_num = 1000; +static const int R_I0_num = 2000; +static const int R_F0_num = 3000; +static const int R_F1_num = R_F0_num + 1; +static const int R_F2_num = R_F0_num + 2; +static const int STACK_num= 4000; + +static bool LP64 = false; +static bool LONGS_IN_ONE_ENTRY = false; + +static const int Op_RegI = 'I'; +static const int Op_RegP = 'P'; +static const int Op_RegF = 'F'; +static const int Op_RegD = 'D'; +static const int Op_RegL = 'L'; +static const int SPARC_ARGS_IN_REGS_NUM=6; + +static void print_reg( int reg ) { + if( reg == 0 ) + printf("__"); // halve's + else if( reg >= STACK_num && reg < STACK_num+100 ) + printf("S%d_",reg - STACK_num); + else if( reg >= R_F0_num && reg < R_F0_num+100 ) + printf("F%d_",reg - R_F0_num); + else if( reg >= R_O0_num && reg < R_O0_num+100 ) { + if( LONGS_IN_ONE_ENTRY ) { + reg -= R_O0_num; + printf("O%d",reg>>1); + printf(reg&1 ? "H" : "L"); + } else + printf("O%d_",reg - R_O0_num); + } else + printf("Wretched: %d\n", reg); +} + +static void print_convention( int *sig, const char *s, int length ) { + // Print it out + for( int i = 0; i < length; i++) { + if( sig[i] == 0 ) continue; // do not print 'halves' + print_reg( sig[i] & 0xFFFF ); + int reg = sig[i] >> 16; + if( reg ) { + printf(":"); + print_reg( reg ); + } else { + printf(" "); + } + printf(" "); + } + printf("\n"); +} + +static int INT_SCALE( int x ) { + return LONGS_IN_ONE_ENTRY ? (x<<1) : x; +} + +static void java_convention( int *sig, const char *s, int length ) { + if( LP64 && !LONGS_IN_ONE_ENTRY ) { + printf("LP64 and 2-reg longs not supported\n"); + return; + } + for( int i = 0; i < length; i++ ) + sig[i] = s[i]; // Reset sig array + bool is_outgoing = true; + + int int_base = (is_outgoing ? R_O0_num : R_I0_num); + + // Convention is to pack the first 6 int/oop args into the first 6 + // registers (I0-I5), extras spill to the stack. Then pack the first + // 32 float args into F0-F31, extras spill to the stack. Then pad + // all register sets to align. Then put longs and doubles into the + // same registers as they fit, else spill to the stack. + int int_reg_max = SPARC_ARGS_IN_REGS_NUM; + int flt_reg_max = 32; + + // Count int/oop and float args. See how many stack slots we'll need + // and where the longs & doubles will go. + int int_reg_cnt = 0; + int flt_reg_cnt = 0; + int stk_reg_pairs = 0; + for( int i = 0; i < length; i++) { + switch( sig[i] ) { + case Op_RegL: // Longs-in-1-reg compete with int args + if( LONGS_IN_ONE_ENTRY ) { + if( int_reg_cnt < int_reg_max ) int_reg_cnt++; + } + break; + case Op_RegP: + if( int_reg_cnt < int_reg_max ) int_reg_cnt++; + else if( !LP64 ) stk_reg_pairs++; + break; + case Op_RegI: + if( int_reg_cnt < int_reg_max ) int_reg_cnt++; + else stk_reg_pairs++; + break; + case Op_RegF: + if( flt_reg_cnt < flt_reg_max ) flt_reg_cnt++; + else stk_reg_pairs++; + break; + } + } + + // This is where the longs/doubles start on the stack. + stk_reg_pairs = (stk_reg_pairs+1) & ~1; // Round + + int int_reg_pairs = (int_reg_cnt+1) & ~1; // 32-bit 2-reg longs only + int flt_reg_pairs = (flt_reg_cnt+1) & ~1; + + int stk_reg = 0; + int int_reg = 0; + int flt_reg = 0; + + // Now do the signature layout + for( int i = 0; i < length; i++) { + int tmp = sig[i]; + if( tmp == Op_RegP ) + tmp = LP64 ? Op_RegL : Op_RegI; // Treat ptrs and ints or long accordingly + switch( tmp ) { + case Op_RegI: +// case Op_RegP: + if( int_reg < int_reg_max) tmp = INT_SCALE(int_reg++) + int_base; + else tmp = STACK_num + stk_reg++; + sig[i] = tmp; + break; + + case Op_RegL: + if( sig[i] != Op_RegP && sig[i+1] != 'h' ) { printf("expecting (h)alf, found %c\n", sig[i+1]); return; } +// case Op_RegP: + if( LONGS_IN_ONE_ENTRY ) { + if( int_reg < int_reg_max ) { + tmp = INT_SCALE(int_reg++) + int_base; + } else { + tmp = STACK_num + stk_reg_pairs; + stk_reg_pairs += 2; + } + } else { + if( int_reg_pairs < int_reg_max ) { + tmp = int_reg_pairs + int_base; + int_reg_pairs += 2; + } else { + tmp = STACK_num + stk_reg_pairs; + stk_reg_pairs += 2; + } + } + sig[i] = tmp | (tmp+1)<<16; // Smear to pair + break; + + case Op_RegF: + sig[i] = (flt_reg < flt_reg_max) ? (R_F0_num + flt_reg++) : STACK_num + stk_reg++; + break; + case Op_RegD: + if( sig[i+1] != 'h' ) { printf("expecting (h)alf, found %c\n", sig[i+1]); return; } + if( flt_reg_pairs < flt_reg_max ) { + tmp = R_F0_num + flt_reg_pairs; + flt_reg_pairs += 2; + } else { + tmp = STACK_num + stk_reg_pairs; + stk_reg_pairs += 2; + } + sig[i] = tmp | (tmp+1)<<16; // Smear to pair + break; + case 'h': sig[i] = 0; break; + default: + printf("Bad character: %c\n", sig[i] ); + return; + } + } + + printf("java "); + printf(LP64 ? "LP64 " : "LP32 "); + printf(LONGS_IN_ONE_ENTRY ? "long1: " : "long2: "); + print_convention(sig,s,length); +} + +static int int_stk_helper( int i ) { + if( i < 6 ) return R_O0_num + (LONGS_IN_ONE_ENTRY ? i<<1 : i); + else return STACK_num + (LP64 ? i<<1 : i); +} + +static void native_convention( int *sig, const char *s, int length ) { + if( LP64 && !LONGS_IN_ONE_ENTRY ) { + printf("LP64 and 2-reg longs not supported\n"); + return; + } + for( int i = 0; i < length; i++ ) + sig[i] = s[i]; // Reset sig array + + // The native convention is V8 if !LP64, which means the V8 convention is + // used both with and without LONGS_IN_ONE_ENTRY, an unfortunate split. The + // same actual machine registers are used, but they are named differently in + // the LONGS_IN_ONE_ENTRY mode. The LP64 convention is the V9 convention + // which is slightly more sane. + + if( LP64 ) { + // V9 convention: All things "as-if" on double-wide stack slots. + // Hoist any int/ptr/long's in the first 6 to int regs. + // Hoist any flt/dbl's in the first 16 dbl regs. + int j = 0; // Count of actual args, not HALVES + for( int i=0; i9)","P(n>9)"} +}; + +const char* Argument::name() const { + int nofArgs = sizeof argumentNames / sizeof argumentNames[0]; + int num = number(); + if (num >= nofArgs) num = nofArgs - 1; + return argumentNames[num][is_in() ? 1 : 0]; +} + +void Assembler::print_instruction(int inst) { + const char* s; + switch (inv_op(inst)) { + default: s = "????"; break; + case call_op: s = "call"; break; + case branch_op: + switch (inv_op2(inst)) { + case bpr_op2: s = "bpr"; break; + case fb_op2: s = "fb"; break; + case fbp_op2: s = "fbp"; break; + case br_op2: s = "br"; break; + case bp_op2: s = "bp"; break; + case cb_op2: s = "cb"; break; + default: s = "????"; break; + } + } + ::tty->print("%s", s); +} + + +// Patch instruction inst at offset inst_pos to refer to dest_pos +// and return the resulting instruction. +// We should have pcs, not offsets, but since all is relative, it will work out +// OK. +int Assembler::patched_branch(int dest_pos, int inst, int inst_pos) { + + int m; // mask for displacement field + int v; // new value for displacement field + const int word_aligned_ones = -4; + switch (inv_op(inst)) { + default: ShouldNotReachHere(); + case call_op: m = wdisp(word_aligned_ones, 0, 30); v = wdisp(dest_pos, inst_pos, 30); break; + case branch_op: + switch (inv_op2(inst)) { + case bpr_op2: m = wdisp16(word_aligned_ones, 0); v = wdisp16(dest_pos, inst_pos); break; + case fbp_op2: m = wdisp( word_aligned_ones, 0, 19); v = wdisp( dest_pos, inst_pos, 19); break; + case bp_op2: m = wdisp( word_aligned_ones, 0, 19); v = wdisp( dest_pos, inst_pos, 19); break; + case fb_op2: m = wdisp( word_aligned_ones, 0, 22); v = wdisp( dest_pos, inst_pos, 22); break; + case br_op2: m = wdisp( word_aligned_ones, 0, 22); v = wdisp( dest_pos, inst_pos, 22); break; + case cb_op2: m = wdisp( word_aligned_ones, 0, 22); v = wdisp( dest_pos, inst_pos, 22); break; + default: ShouldNotReachHere(); + } + } + return inst & ~m | v; +} + +// Return the offset of the branch destionation of instruction inst +// at offset pos. +// Should have pcs, but since all is relative, it works out. +int Assembler::branch_destination(int inst, int pos) { + int r; + switch (inv_op(inst)) { + default: ShouldNotReachHere(); + case call_op: r = inv_wdisp(inst, pos, 30); break; + case branch_op: + switch (inv_op2(inst)) { + case bpr_op2: r = inv_wdisp16(inst, pos); break; + case fbp_op2: r = inv_wdisp( inst, pos, 19); break; + case bp_op2: r = inv_wdisp( inst, pos, 19); break; + case fb_op2: r = inv_wdisp( inst, pos, 22); break; + case br_op2: r = inv_wdisp( inst, pos, 22); break; + case cb_op2: r = inv_wdisp( inst, pos, 22); break; + default: ShouldNotReachHere(); + } + } + return r; +} + +int AbstractAssembler::code_fill_byte() { + return 0x00; // illegal instruction 0x00000000 +} + +// Generate a bunch 'o stuff (including v9's +#ifndef PRODUCT +void Assembler::test_v9() { + add( G0, G1, G2 ); + add( G3, 0, G4 ); + + addcc( G5, G6, G7 ); + addcc( I0, 1, I1 ); + addc( I2, I3, I4 ); + addc( I5, -1, I6 ); + addccc( I7, L0, L1 ); + addccc( L2, (1 << 12) - 2, L3 ); + + Label lbl1, lbl2, lbl3; + + bind(lbl1); + + bpr( rc_z, true, pn, L4, pc(), relocInfo::oop_type ); + delayed()->nop(); + bpr( rc_lez, false, pt, L5, lbl1); + delayed()->nop(); + + fb( f_never, true, pc() + 4, relocInfo::none); + delayed()->nop(); + fb( f_notEqual, false, lbl2 ); + delayed()->nop(); + + fbp( f_notZero, true, fcc0, pn, pc() - 4, relocInfo::none); + delayed()->nop(); + fbp( f_lessOrGreater, false, fcc1, pt, lbl3 ); + delayed()->nop(); + + br( equal, true, pc() + 1024, relocInfo::none); + delayed()->nop(); + br( lessEqual, false, lbl1 ); + delayed()->nop(); + br( never, false, lbl1 ); + delayed()->nop(); + + bp( less, true, icc, pn, pc(), relocInfo::none); + delayed()->nop(); + bp( lessEqualUnsigned, false, xcc, pt, lbl2 ); + delayed()->nop(); + + call( pc(), relocInfo::none); + delayed()->nop(); + call( lbl3 ); + delayed()->nop(); + + + casa( L6, L7, O0 ); + casxa( O1, O2, O3, 0 ); + + udiv( O4, O5, O7 ); + udiv( G0, (1 << 12) - 1, G1 ); + sdiv( G1, G2, G3 ); + sdiv( G4, -((1 << 12) - 1), G5 ); + udivcc( G6, G7, I0 ); + udivcc( I1, -((1 << 12) - 2), I2 ); + sdivcc( I3, I4, I5 ); + sdivcc( I6, -((1 << 12) - 0), I7 ); + + done(); + retry(); + + fadd( FloatRegisterImpl::S, F0, F1, F2 ); + fsub( FloatRegisterImpl::D, F34, F0, F62 ); + + fcmp( FloatRegisterImpl::Q, fcc0, F0, F60); + fcmpe( FloatRegisterImpl::S, fcc1, F31, F30); + + ftox( FloatRegisterImpl::D, F2, F4 ); + ftoi( FloatRegisterImpl::Q, F4, F8 ); + + ftof( FloatRegisterImpl::S, FloatRegisterImpl::Q, F3, F12 ); + + fxtof( FloatRegisterImpl::S, F4, F5 ); + fitof( FloatRegisterImpl::D, F6, F8 ); + + fmov( FloatRegisterImpl::Q, F16, F20 ); + fneg( FloatRegisterImpl::S, F6, F7 ); + fabs( FloatRegisterImpl::D, F10, F12 ); + + fmul( FloatRegisterImpl::Q, F24, F28, F32 ); + fmul( FloatRegisterImpl::S, FloatRegisterImpl::D, F8, F9, F14 ); + fdiv( FloatRegisterImpl::S, F10, F11, F12 ); + + fsqrt( FloatRegisterImpl::S, F13, F14 ); + + flush( L0, L1 ); + flush( L2, -1 ); + + flushw(); + + illtrap( (1 << 22) - 2); + + impdep1( 17, (1 << 19) - 1 ); + impdep2( 3, 0 ); + + jmpl( L3, L4, L5 ); + delayed()->nop(); + jmpl( L6, -1, L7, Relocation::spec_simple(relocInfo::none)); + delayed()->nop(); + + + ldf( FloatRegisterImpl::S, O0, O1, F15 ); + ldf( FloatRegisterImpl::D, O2, -1, F14 ); + + + ldfsr( O3, O4 ); + ldfsr( O5, -1 ); + ldxfsr( O6, O7 ); + ldxfsr( I0, -1 ); + + ldfa( FloatRegisterImpl::D, I1, I2, 1, F16 ); + ldfa( FloatRegisterImpl::Q, I3, -1, F36 ); + + ldsb( I4, I5, I6 ); + ldsb( I7, -1, G0 ); + ldsh( G1, G3, G4 ); + ldsh( G5, -1, G6 ); + ldsw( G7, L0, L1 ); + ldsw( L2, -1, L3 ); + ldub( L4, L5, L6 ); + ldub( L7, -1, O0 ); + lduh( O1, O2, O3 ); + lduh( O4, -1, O5 ); + lduw( O6, O7, G0 ); + lduw( G1, -1, G2 ); + ldx( G3, G4, G5 ); + ldx( G6, -1, G7 ); + ldd( I0, I1, I2 ); + ldd( I3, -1, I4 ); + + ldsba( I5, I6, 2, I7 ); + ldsba( L0, -1, L1 ); + ldsha( L2, L3, 3, L4 ); + ldsha( L5, -1, L6 ); + ldswa( L7, O0, (1 << 8) - 1, O1 ); + ldswa( O2, -1, O3 ); + lduba( O4, O5, 0, O6 ); + lduba( O7, -1, I0 ); + lduha( I1, I2, 1, I3 ); + lduha( I4, -1, I5 ); + lduwa( I6, I7, 2, L0 ); + lduwa( L1, -1, L2 ); + ldxa( L3, L4, 3, L5 ); + ldxa( L6, -1, L7 ); + ldda( G0, G1, 4, G2 ); + ldda( G3, -1, G4 ); + + ldstub( G5, G6, G7 ); + ldstub( O0, -1, O1 ); + + ldstuba( O2, O3, 5, O4 ); + ldstuba( O5, -1, O6 ); + + and3( I0, L0, O0 ); + and3( G7, -1, O7 ); + andcc( L2, I2, G2 ); + andcc( L4, -1, G4 ); + andn( I5, I6, I7 ); + andn( I6, -1, I7 ); + andncc( I5, I6, I7 ); + andncc( I7, -1, I6 ); + or3( I5, I6, I7 ); + or3( I7, -1, I6 ); + orcc( I5, I6, I7 ); + orcc( I7, -1, I6 ); + orn( I5, I6, I7 ); + orn( I7, -1, I6 ); + orncc( I5, I6, I7 ); + orncc( I7, -1, I6 ); + xor3( I5, I6, I7 ); + xor3( I7, -1, I6 ); + xorcc( I5, I6, I7 ); + xorcc( I7, -1, I6 ); + xnor( I5, I6, I7 ); + xnor( I7, -1, I6 ); + xnorcc( I5, I6, I7 ); + xnorcc( I7, -1, I6 ); + + membar( Membar_mask_bits(StoreStore | LoadStore | StoreLoad | LoadLoad | Sync | MemIssue | Lookaside ) ); + membar( StoreStore ); + membar( LoadStore ); + membar( StoreLoad ); + membar( LoadLoad ); + membar( Sync ); + membar( MemIssue ); + membar( Lookaside ); + + fmov( FloatRegisterImpl::S, f_ordered, true, fcc2, F16, F17 ); + fmov( FloatRegisterImpl::D, rc_lz, L5, F18, F20 ); + + movcc( overflowClear, false, icc, I6, L4 ); + movcc( f_unorderedOrEqual, true, fcc2, (1 << 10) - 1, O0 ); + + movr( rc_nz, I5, I6, I7 ); + movr( rc_gz, L1, -1, L2 ); + + mulx( I5, I6, I7 ); + mulx( I7, -1, I6 ); + sdivx( I5, I6, I7 ); + sdivx( I7, -1, I6 ); + udivx( I5, I6, I7 ); + udivx( I7, -1, I6 ); + + umul( I5, I6, I7 ); + umul( I7, -1, I6 ); + smul( I5, I6, I7 ); + smul( I7, -1, I6 ); + umulcc( I5, I6, I7 ); + umulcc( I7, -1, I6 ); + smulcc( I5, I6, I7 ); + smulcc( I7, -1, I6 ); + + mulscc( I5, I6, I7 ); + mulscc( I7, -1, I6 ); + + nop(); + + + popc( G0, G1); + popc( -1, G2); + + prefetch( L1, L2, severalReads ); + prefetch( L3, -1, oneRead ); + prefetcha( O3, O2, 6, severalWritesAndPossiblyReads ); + prefetcha( G2, -1, oneWrite ); + + rett( I7, I7); + delayed()->nop(); + rett( G0, -1, relocInfo::none); + delayed()->nop(); + + save( I5, I6, I7 ); + save( I7, -1, I6 ); + restore( I5, I6, I7 ); + restore( I7, -1, I6 ); + + saved(); + restored(); + + sethi( 0xaaaaaaaa, I3, Relocation::spec_simple(relocInfo::none)); + + sll( I5, I6, I7 ); + sll( I7, 31, I6 ); + srl( I5, I6, I7 ); + srl( I7, 0, I6 ); + sra( I5, I6, I7 ); + sra( I7, 30, I6 ); + sllx( I5, I6, I7 ); + sllx( I7, 63, I6 ); + srlx( I5, I6, I7 ); + srlx( I7, 0, I6 ); + srax( I5, I6, I7 ); + srax( I7, 62, I6 ); + + sir( -1 ); + + stbar(); + + stf( FloatRegisterImpl::Q, F40, G0, I7 ); + stf( FloatRegisterImpl::S, F18, I3, -1 ); + + stfsr( L1, L2 ); + stfsr( I7, -1 ); + stxfsr( I6, I5 ); + stxfsr( L4, -1 ); + + stfa( FloatRegisterImpl::D, F22, I6, I7, 7 ); + stfa( FloatRegisterImpl::Q, F44, G0, -1 ); + + stb( L5, O2, I7 ); + stb( I7, I6, -1 ); + sth( L5, O2, I7 ); + sth( I7, I6, -1 ); + stw( L5, O2, I7 ); + stw( I7, I6, -1 ); + stx( L5, O2, I7 ); + stx( I7, I6, -1 ); + std( L5, O2, I7 ); + std( I7, I6, -1 ); + + stba( L5, O2, I7, 8 ); + stba( I7, I6, -1 ); + stha( L5, O2, I7, 9 ); + stha( I7, I6, -1 ); + stwa( L5, O2, I7, 0 ); + stwa( I7, I6, -1 ); + stxa( L5, O2, I7, 11 ); + stxa( I7, I6, -1 ); + stda( L5, O2, I7, 12 ); + stda( I7, I6, -1 ); + + sub( I5, I6, I7 ); + sub( I7, -1, I6 ); + subcc( I5, I6, I7 ); + subcc( I7, -1, I6 ); + subc( I5, I6, I7 ); + subc( I7, -1, I6 ); + subccc( I5, I6, I7 ); + subccc( I7, -1, I6 ); + + swap( I5, I6, I7 ); + swap( I7, -1, I6 ); + + swapa( G0, G1, 13, G2 ); + swapa( I7, -1, I6 ); + + taddcc( I5, I6, I7 ); + taddcc( I7, -1, I6 ); + taddcctv( I5, I6, I7 ); + taddcctv( I7, -1, I6 ); + + tsubcc( I5, I6, I7 ); + tsubcc( I7, -1, I6 ); + tsubcctv( I5, I6, I7 ); + tsubcctv( I7, -1, I6 ); + + trap( overflowClear, xcc, G0, G1 ); + trap( lessEqual, icc, I7, 17 ); + + bind(lbl2); + bind(lbl3); + + code()->decode(); +} + +// Generate a bunch 'o stuff unique to V8 +void Assembler::test_v8_onlys() { + Label lbl1; + + cb( cp_0or1or2, false, pc() - 4, relocInfo::none); + delayed()->nop(); + cb( cp_never, true, lbl1); + delayed()->nop(); + + cpop1(1, 2, 3, 4); + cpop2(5, 6, 7, 8); + + ldc( I0, I1, 31); + ldc( I2, -1, 0); + + lddc( I4, I4, 30); + lddc( I6, 0, 1 ); + + ldcsr( L0, L1, 0); + ldcsr( L1, (1 << 12) - 1, 17 ); + + stc( 31, L4, L5); + stc( 30, L6, -(1 << 12) ); + + stdc( 0, L7, G0); + stdc( 1, G1, 0 ); + + stcsr( 16, G2, G3); + stcsr( 17, G4, 1 ); + + stdcq( 4, G5, G6); + stdcq( 5, G7, -1 ); + + bind(lbl1); + + code()->decode(); +} +#endif + +// Implementation of MacroAssembler + +void MacroAssembler::null_check(Register reg, int offset) { + if (needs_explicit_null_check((intptr_t)offset)) { + // provoke OS NULL exception if reg = NULL by + // accessing M[reg] w/o changing any registers + ld_ptr(reg, 0, G0); + } + else { + // nothing to do, (later) access of M[reg + offset] + // will provoke OS NULL exception if reg = NULL + } +} + +// Ring buffer jumps + +#ifndef PRODUCT +void MacroAssembler::ret( bool trace ) { if (trace) { + mov(I7, O7); // traceable register + JMP(O7, 2 * BytesPerInstWord); + } else { + jmpl( I7, 2 * BytesPerInstWord, G0 ); + } + } + +void MacroAssembler::retl( bool trace ) { if (trace) JMP(O7, 2 * BytesPerInstWord); + else jmpl( O7, 2 * BytesPerInstWord, G0 ); } +#endif /* PRODUCT */ + + +void MacroAssembler::jmp2(Register r1, Register r2, const char* file, int line ) { + assert_not_delayed(); + // This can only be traceable if r1 & r2 are visible after a window save + if (TraceJumps) { +#ifndef PRODUCT + save_frame(0); + verify_thread(); + ld(G2_thread, in_bytes(JavaThread::jmp_ring_index_offset()), O0); + add(G2_thread, in_bytes(JavaThread::jmp_ring_offset()), O1); + sll(O0, exact_log2(4*sizeof(intptr_t)), O2); + add(O2, O1, O1); + + add(r1->after_save(), r2->after_save(), O2); + set((intptr_t)file, O3); + set(line, O4); + Label L; + // get nearby pc, store jmp target + call(L, relocInfo::none); // No relocation for call to pc+0x8 + delayed()->st(O2, O1, 0); + bind(L); + + // store nearby pc + st(O7, O1, sizeof(intptr_t)); + // store file + st(O3, O1, 2*sizeof(intptr_t)); + // store line + st(O4, O1, 3*sizeof(intptr_t)); + add(O0, 1, O0); + and3(O0, JavaThread::jump_ring_buffer_size - 1, O0); + st(O0, G2_thread, in_bytes(JavaThread::jmp_ring_index_offset())); + restore(); +#endif /* PRODUCT */ + } + jmpl(r1, r2, G0); +} +void MacroAssembler::jmp(Register r1, int offset, const char* file, int line ) { + assert_not_delayed(); + // This can only be traceable if r1 is visible after a window save + if (TraceJumps) { +#ifndef PRODUCT + save_frame(0); + verify_thread(); + ld(G2_thread, in_bytes(JavaThread::jmp_ring_index_offset()), O0); + add(G2_thread, in_bytes(JavaThread::jmp_ring_offset()), O1); + sll(O0, exact_log2(4*sizeof(intptr_t)), O2); + add(O2, O1, O1); + + add(r1->after_save(), offset, O2); + set((intptr_t)file, O3); + set(line, O4); + Label L; + // get nearby pc, store jmp target + call(L, relocInfo::none); // No relocation for call to pc+0x8 + delayed()->st(O2, O1, 0); + bind(L); + + // store nearby pc + st(O7, O1, sizeof(intptr_t)); + // store file + st(O3, O1, 2*sizeof(intptr_t)); + // store line + st(O4, O1, 3*sizeof(intptr_t)); + add(O0, 1, O0); + and3(O0, JavaThread::jump_ring_buffer_size - 1, O0); + st(O0, G2_thread, in_bytes(JavaThread::jmp_ring_index_offset())); + restore(); +#endif /* PRODUCT */ + } + jmp(r1, offset); +} + +// This code sequence is relocatable to any address, even on LP64. +void MacroAssembler::jumpl( Address& a, Register d, int offset, const char* file, int line ) { + assert_not_delayed(); + // Force fixed length sethi because NativeJump and NativeFarCall don't handle + // variable length instruction streams. + sethi(a, /*ForceRelocatable=*/ true); + if (TraceJumps) { +#ifndef PRODUCT + // Must do the add here so relocation can find the remainder of the + // value to be relocated. + add(a.base(), a.disp() + offset, a.base(), a.rspec(offset)); + save_frame(0); + verify_thread(); + ld(G2_thread, in_bytes(JavaThread::jmp_ring_index_offset()), O0); + add(G2_thread, in_bytes(JavaThread::jmp_ring_offset()), O1); + sll(O0, exact_log2(4*sizeof(intptr_t)), O2); + add(O2, O1, O1); + + set((intptr_t)file, O3); + set(line, O4); + Label L; + + // get nearby pc, store jmp target + call(L, relocInfo::none); // No relocation for call to pc+0x8 + delayed()->st(a.base()->after_save(), O1, 0); + bind(L); + + // store nearby pc + st(O7, O1, sizeof(intptr_t)); + // store file + st(O3, O1, 2*sizeof(intptr_t)); + // store line + st(O4, O1, 3*sizeof(intptr_t)); + add(O0, 1, O0); + and3(O0, JavaThread::jump_ring_buffer_size - 1, O0); + st(O0, G2_thread, in_bytes(JavaThread::jmp_ring_index_offset())); + restore(); + jmpl(a.base(), G0, d); +#else + jmpl(a, d, offset); +#endif /* PRODUCT */ + } else { + jmpl(a, d, offset); + } +} + +void MacroAssembler::jump( Address& a, int offset, const char* file, int line ) { + jumpl( a, G0, offset, file, line ); +} + + +// Convert to C varargs format +void MacroAssembler::set_varargs( Argument inArg, Register d ) { + // spill register-resident args to their memory slots + // (SPARC calling convention requires callers to have already preallocated these) + // Note that the inArg might in fact be an outgoing argument, + // if a leaf routine or stub does some tricky argument shuffling. + // This routine must work even though one of the saved arguments + // is in the d register (e.g., set_varargs(Argument(0, false), O0)). + for (Argument savePtr = inArg; + savePtr.is_register(); + savePtr = savePtr.successor()) { + st_ptr(savePtr.as_register(), savePtr.address_in_frame()); + } + // return the address of the first memory slot + add(inArg.address_in_frame(), d); +} + +// Conditional breakpoint (for assertion checks in assembly code) +void MacroAssembler::breakpoint_trap(Condition c, CC cc) { + trap(c, cc, G0, ST_RESERVED_FOR_USER_0); +} + +// We want to use ST_BREAKPOINT here, but the debugger is confused by it. +void MacroAssembler::breakpoint_trap() { + trap(ST_RESERVED_FOR_USER_0); +} + +// flush windows (except current) using flushw instruction if avail. +void MacroAssembler::flush_windows() { + if (VM_Version::v9_instructions_work()) flushw(); + else flush_windows_trap(); +} + +// Write serialization page so VM thread can do a pseudo remote membar +// We use the current thread pointer to calculate a thread specific +// offset to write to within the page. This minimizes bus traffic +// due to cache line collision. +void MacroAssembler::serialize_memory(Register thread, Register tmp1, Register tmp2) { + Address mem_serialize_page(tmp1, os::get_memory_serialize_page()); + srl(thread, os::get_serialize_page_shift_count(), tmp2); + if (Assembler::is_simm13(os::vm_page_size())) { + and3(tmp2, (os::vm_page_size() - sizeof(int)), tmp2); + } + else { + set((os::vm_page_size() - sizeof(int)), tmp1); + and3(tmp2, tmp1, tmp2); + } + load_address(mem_serialize_page); + st(G0, tmp1, tmp2); +} + + + +void MacroAssembler::enter() { + Unimplemented(); +} + +void MacroAssembler::leave() { + Unimplemented(); +} + +void MacroAssembler::mult(Register s1, Register s2, Register d) { + if(VM_Version::v9_instructions_work()) { + mulx (s1, s2, d); + } else { + smul (s1, s2, d); + } +} + +void MacroAssembler::mult(Register s1, int simm13a, Register d) { + if(VM_Version::v9_instructions_work()) { + mulx (s1, simm13a, d); + } else { + smul (s1, simm13a, d); + } +} + + +#ifdef ASSERT +void MacroAssembler::read_ccr_v8_assert(Register ccr_save) { + const Register s1 = G3_scratch; + const Register s2 = G4_scratch; + Label get_psr_test; + // Get the condition codes the V8 way. + read_ccr_trap(s1); + mov(ccr_save, s2); + // This is a test of V8 which has icc but not xcc + // so mask off the xcc bits + and3(s2, 0xf, s2); + // Compare condition codes from the V8 and V9 ways. + subcc(s2, s1, G0); + br(Assembler::notEqual, true, Assembler::pt, get_psr_test); + delayed()->breakpoint_trap(); + bind(get_psr_test); +} + +void MacroAssembler::write_ccr_v8_assert(Register ccr_save) { + const Register s1 = G3_scratch; + const Register s2 = G4_scratch; + Label set_psr_test; + // Write out the saved condition codes the V8 way + write_ccr_trap(ccr_save, s1, s2); + // Read back the condition codes using the V9 instruction + rdccr(s1); + mov(ccr_save, s2); + // This is a test of V8 which has icc but not xcc + // so mask off the xcc bits + and3(s2, 0xf, s2); + and3(s1, 0xf, s1); + // Compare the V8 way with the V9 way. + subcc(s2, s1, G0); + br(Assembler::notEqual, true, Assembler::pt, set_psr_test); + delayed()->breakpoint_trap(); + bind(set_psr_test); +} +#else +#define read_ccr_v8_assert(x) +#define write_ccr_v8_assert(x) +#endif // ASSERT + +void MacroAssembler::read_ccr(Register ccr_save) { + if (VM_Version::v9_instructions_work()) { + rdccr(ccr_save); + // Test code sequence used on V8. Do not move above rdccr. + read_ccr_v8_assert(ccr_save); + } else { + read_ccr_trap(ccr_save); + } +} + +void MacroAssembler::write_ccr(Register ccr_save) { + if (VM_Version::v9_instructions_work()) { + // Test code sequence used on V8. Do not move below wrccr. + write_ccr_v8_assert(ccr_save); + wrccr(ccr_save); + } else { + const Register temp_reg1 = G3_scratch; + const Register temp_reg2 = G4_scratch; + write_ccr_trap(ccr_save, temp_reg1, temp_reg2); + } +} + + +// Calls to C land + +#ifdef ASSERT +// a hook for debugging +static Thread* reinitialize_thread() { + return ThreadLocalStorage::thread(); +} +#else +#define reinitialize_thread ThreadLocalStorage::thread +#endif + +#ifdef ASSERT +address last_get_thread = NULL; +#endif + +// call this when G2_thread is not known to be valid +void MacroAssembler::get_thread() { + save_frame(0); // to avoid clobbering O0 + mov(G1, L0); // avoid clobbering G1 + mov(G5_method, L1); // avoid clobbering G5 + mov(G3, L2); // avoid clobbering G3 also + mov(G4, L5); // avoid clobbering G4 +#ifdef ASSERT + Address last_get_thread_addr(L3, (address)&last_get_thread); + sethi(last_get_thread_addr); + inc(L4, get_pc(L4) + 2 * BytesPerInstWord); // skip getpc() code + inc + st_ptr to point L4 at call + st_ptr(L4, last_get_thread_addr); +#endif + call(CAST_FROM_FN_PTR(address, reinitialize_thread), relocInfo::runtime_call_type); + delayed()->nop(); + mov(L0, G1); + mov(L1, G5_method); + mov(L2, G3); + mov(L5, G4); + restore(O0, 0, G2_thread); +} + +static Thread* verify_thread_subroutine(Thread* gthread_value) { + Thread* correct_value = ThreadLocalStorage::thread(); + guarantee(gthread_value == correct_value, "G2_thread value must be the thread"); + return correct_value; +} + +void MacroAssembler::verify_thread() { + if (VerifyThread) { + // NOTE: this chops off the heads of the 64-bit O registers. +#ifdef CC_INTERP + save_frame(0); +#else + // make sure G2_thread contains the right value + save_frame_and_mov(0, Lmethod, Lmethod); // to avoid clobbering O0 (and propagate Lmethod for -Xprof) + mov(G1, L1); // avoid clobbering G1 + // G2 saved below + mov(G3, L3); // avoid clobbering G3 + mov(G4, L4); // avoid clobbering G4 + mov(G5_method, L5); // avoid clobbering G5_method +#endif /* CC_INTERP */ +#if defined(COMPILER2) && !defined(_LP64) + // Save & restore possible 64-bit Long arguments in G-regs + srlx(G1,32,L0); + srlx(G4,32,L6); +#endif + call(CAST_FROM_FN_PTR(address,verify_thread_subroutine), relocInfo::runtime_call_type); + delayed()->mov(G2_thread, O0); + + mov(L1, G1); // Restore G1 + // G2 restored below + mov(L3, G3); // restore G3 + mov(L4, G4); // restore G4 + mov(L5, G5_method); // restore G5_method +#if defined(COMPILER2) && !defined(_LP64) + // Save & restore possible 64-bit Long arguments in G-regs + sllx(L0,32,G2); // Move old high G1 bits high in G2 + sllx(G1, 0,G1); // Clear current high G1 bits + or3 (G1,G2,G1); // Recover 64-bit G1 + sllx(L6,32,G2); // Move old high G4 bits high in G2 + sllx(G4, 0,G4); // Clear current high G4 bits + or3 (G4,G2,G4); // Recover 64-bit G4 +#endif + restore(O0, 0, G2_thread); + } +} + + +void MacroAssembler::save_thread(const Register thread_cache) { + verify_thread(); + if (thread_cache->is_valid()) { + assert(thread_cache->is_local() || thread_cache->is_in(), "bad volatile"); + mov(G2_thread, thread_cache); + } + if (VerifyThread) { + // smash G2_thread, as if the VM were about to anyway + set(0x67676767, G2_thread); + } +} + + +void MacroAssembler::restore_thread(const Register thread_cache) { + if (thread_cache->is_valid()) { + assert(thread_cache->is_local() || thread_cache->is_in(), "bad volatile"); + mov(thread_cache, G2_thread); + verify_thread(); + } else { + // do it the slow way + get_thread(); + } +} + + +// %%% maybe get rid of [re]set_last_Java_frame +void MacroAssembler::set_last_Java_frame(Register last_java_sp, Register last_Java_pc) { + assert_not_delayed(); + Address flags(G2_thread, + 0, + in_bytes(JavaThread::frame_anchor_offset()) + + in_bytes(JavaFrameAnchor::flags_offset())); + Address pc_addr(G2_thread, + 0, + in_bytes(JavaThread::last_Java_pc_offset())); + + // Always set last_Java_pc and flags first because once last_Java_sp is visible + // has_last_Java_frame is true and users will look at the rest of the fields. + // (Note: flags should always be zero before we get here so doesn't need to be set.) + +#ifdef ASSERT + // Verify that flags was zeroed on return to Java + Label PcOk; + save_frame(0); // to avoid clobbering O0 + ld_ptr(pc_addr, L0); + tst(L0); +#ifdef _LP64 + brx(Assembler::zero, false, Assembler::pt, PcOk); +#else + br(Assembler::zero, false, Assembler::pt, PcOk); +#endif // _LP64 + delayed() -> nop(); + stop("last_Java_pc not zeroed before leaving Java"); + bind(PcOk); + + // Verify that flags was zeroed on return to Java + Label FlagsOk; + ld(flags, L0); + tst(L0); + br(Assembler::zero, false, Assembler::pt, FlagsOk); + delayed() -> restore(); + stop("flags not zeroed before leaving Java"); + bind(FlagsOk); +#endif /* ASSERT */ + // + // When returning from calling out from Java mode the frame anchor's last_Java_pc + // will always be set to NULL. It is set here so that if we are doing a call to + // native (not VM) that we capture the known pc and don't have to rely on the + // native call having a standard frame linkage where we can find the pc. + + if (last_Java_pc->is_valid()) { + st_ptr(last_Java_pc, pc_addr); + } + +#ifdef _LP64 +#ifdef ASSERT + // Make sure that we have an odd stack + Label StackOk; + andcc(last_java_sp, 0x01, G0); + br(Assembler::notZero, false, Assembler::pt, StackOk); + delayed() -> nop(); + stop("Stack Not Biased in set_last_Java_frame"); + bind(StackOk); +#endif // ASSERT + assert( last_java_sp != G4_scratch, "bad register usage in set_last_Java_frame"); + add( last_java_sp, STACK_BIAS, G4_scratch ); + st_ptr(G4_scratch, Address(G2_thread, 0, in_bytes(JavaThread::last_Java_sp_offset()))); +#else + st_ptr(last_java_sp, Address(G2_thread, 0, in_bytes(JavaThread::last_Java_sp_offset()))); +#endif // _LP64 +} + +void MacroAssembler::reset_last_Java_frame(void) { + assert_not_delayed(); + + Address sp_addr(G2_thread, 0, in_bytes(JavaThread::last_Java_sp_offset())); + Address pc_addr(G2_thread, + 0, + in_bytes(JavaThread::frame_anchor_offset()) + in_bytes(JavaFrameAnchor::last_Java_pc_offset())); + Address flags(G2_thread, + 0, + in_bytes(JavaThread::frame_anchor_offset()) + in_bytes(JavaFrameAnchor::flags_offset())); + +#ifdef ASSERT + // check that it WAS previously set +#ifdef CC_INTERP + save_frame(0); +#else + save_frame_and_mov(0, Lmethod, Lmethod); // Propagate Lmethod to helper frame for -Xprof +#endif /* CC_INTERP */ + ld_ptr(sp_addr, L0); + tst(L0); + breakpoint_trap(Assembler::zero, Assembler::ptr_cc); + restore(); +#endif // ASSERT + + st_ptr(G0, sp_addr); + // Always return last_Java_pc to zero + st_ptr(G0, pc_addr); + // Always null flags after return to Java + st(G0, flags); +} + + +void MacroAssembler::call_VM_base( + Register oop_result, + Register thread_cache, + Register last_java_sp, + address entry_point, + int number_of_arguments, + bool check_exceptions) +{ + assert_not_delayed(); + + // determine last_java_sp register + if (!last_java_sp->is_valid()) { + last_java_sp = SP; + } + // debugging support + assert(number_of_arguments >= 0 , "cannot have negative number of arguments"); + + // 64-bit last_java_sp is biased! + set_last_Java_frame(last_java_sp, noreg); + if (VerifyThread) mov(G2_thread, O0); // about to be smashed; pass early + save_thread(thread_cache); + // do the call + call(entry_point, relocInfo::runtime_call_type); + if (!VerifyThread) + delayed()->mov(G2_thread, O0); // pass thread as first argument + else + delayed()->nop(); // (thread already passed) + restore_thread(thread_cache); + reset_last_Java_frame(); + + // check for pending exceptions. use Gtemp as scratch register. + if (check_exceptions) { + check_and_forward_exception(Gtemp); + } + + // get oop result if there is one and reset the value in the thread + if (oop_result->is_valid()) { + get_vm_result(oop_result); + } +} + +void MacroAssembler::check_and_forward_exception(Register scratch_reg) +{ + Label L; + + check_and_handle_popframe(scratch_reg); + check_and_handle_earlyret(scratch_reg); + + Address exception_addr(G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + ld_ptr(exception_addr, scratch_reg); + br_null(scratch_reg,false,pt,L); + delayed()->nop(); + // we use O7 linkage so that forward_exception_entry has the issuing PC + call(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); + delayed()->nop(); + bind(L); +} + + +void MacroAssembler::check_and_handle_popframe(Register scratch_reg) { +} + + +void MacroAssembler::check_and_handle_earlyret(Register scratch_reg) { +} + + +void MacroAssembler::call_VM(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions) { + call_VM_base(oop_result, noreg, noreg, entry_point, number_of_arguments, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions) { + // O0 is reserved for the thread + mov(arg_1, O1); + call_VM(oop_result, entry_point, 1, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions) { + // O0 is reserved for the thread + mov(arg_1, O1); + mov(arg_2, O2); assert(arg_2 != O1, "smashed argument"); + call_VM(oop_result, entry_point, 2, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions) { + // O0 is reserved for the thread + mov(arg_1, O1); + mov(arg_2, O2); assert(arg_2 != O1, "smashed argument"); + mov(arg_3, O3); assert(arg_3 != O1 && arg_3 != O2, "smashed argument"); + call_VM(oop_result, entry_point, 3, check_exceptions); +} + + + +// Note: The following call_VM overloadings are useful when a "save" +// has already been performed by a stub, and the last Java frame is +// the previous one. In that case, last_java_sp must be passed as FP +// instead of SP. + + +void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, int number_of_arguments, bool check_exceptions) { + call_VM_base(oop_result, noreg, last_java_sp, entry_point, number_of_arguments, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, bool check_exceptions) { + // O0 is reserved for the thread + mov(arg_1, O1); + call_VM(oop_result, last_java_sp, entry_point, 1, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, bool check_exceptions) { + // O0 is reserved for the thread + mov(arg_1, O1); + mov(arg_2, O2); assert(arg_2 != O1, "smashed argument"); + call_VM(oop_result, last_java_sp, entry_point, 2, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions) { + // O0 is reserved for the thread + mov(arg_1, O1); + mov(arg_2, O2); assert(arg_2 != O1, "smashed argument"); + mov(arg_3, O3); assert(arg_3 != O1 && arg_3 != O2, "smashed argument"); + call_VM(oop_result, last_java_sp, entry_point, 3, check_exceptions); +} + + + +void MacroAssembler::call_VM_leaf_base(Register thread_cache, address entry_point, int number_of_arguments) { + assert_not_delayed(); + save_thread(thread_cache); + // do the call + call(entry_point, relocInfo::runtime_call_type); + delayed()->nop(); + restore_thread(thread_cache); +} + + +void MacroAssembler::call_VM_leaf(Register thread_cache, address entry_point, int number_of_arguments) { + call_VM_leaf_base(thread_cache, entry_point, number_of_arguments); +} + + +void MacroAssembler::call_VM_leaf(Register thread_cache, address entry_point, Register arg_1) { + mov(arg_1, O0); + call_VM_leaf(thread_cache, entry_point, 1); +} + + +void MacroAssembler::call_VM_leaf(Register thread_cache, address entry_point, Register arg_1, Register arg_2) { + mov(arg_1, O0); + mov(arg_2, O1); assert(arg_2 != O0, "smashed argument"); + call_VM_leaf(thread_cache, entry_point, 2); +} + + +void MacroAssembler::call_VM_leaf(Register thread_cache, address entry_point, Register arg_1, Register arg_2, Register arg_3) { + mov(arg_1, O0); + mov(arg_2, O1); assert(arg_2 != O0, "smashed argument"); + mov(arg_3, O2); assert(arg_3 != O0 && arg_3 != O1, "smashed argument"); + call_VM_leaf(thread_cache, entry_point, 3); +} + + +void MacroAssembler::get_vm_result(Register oop_result) { + verify_thread(); + Address vm_result_addr(G2_thread, 0, in_bytes(JavaThread::vm_result_offset())); + ld_ptr( vm_result_addr, oop_result); + st_ptr(G0, vm_result_addr); + verify_oop(oop_result); +} + + +void MacroAssembler::get_vm_result_2(Register oop_result) { + verify_thread(); + Address vm_result_addr_2(G2_thread, 0, in_bytes(JavaThread::vm_result_2_offset())); + ld_ptr(vm_result_addr_2, oop_result); + st_ptr(G0, vm_result_addr_2); + verify_oop(oop_result); +} + + +// We require that C code which does not return a value in vm_result will +// leave it undisturbed. +void MacroAssembler::set_vm_result(Register oop_result) { + verify_thread(); + Address vm_result_addr(G2_thread, 0, in_bytes(JavaThread::vm_result_offset())); + verify_oop(oop_result); + +# ifdef ASSERT + // Check that we are not overwriting any other oop. +#ifdef CC_INTERP + save_frame(0); +#else + save_frame_and_mov(0, Lmethod, Lmethod); // Propagate Lmethod for -Xprof +#endif /* CC_INTERP */ + ld_ptr(vm_result_addr, L0); + tst(L0); + restore(); + breakpoint_trap(notZero, Assembler::ptr_cc); + // } +# endif + + st_ptr(oop_result, vm_result_addr); +} + + +void MacroAssembler::store_check(Register tmp, Register obj) { + // Use two shifts to clear out those low order two bits! (Cannot opt. into 1.) + + /* $$$ This stuff needs to go into one of the BarrierSet generator + functions. (The particular barrier sets will have to be friends of + MacroAssembler, I guess.) */ + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); + CardTableModRefBS* ct = (CardTableModRefBS*)bs; + assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); +#ifdef _LP64 + srlx(obj, CardTableModRefBS::card_shift, obj); +#else + srl(obj, CardTableModRefBS::card_shift, obj); +#endif + assert( tmp != obj, "need separate temp reg"); + Address rs(tmp, (address)ct->byte_map_base); + load_address(rs); + stb(G0, rs.base(), obj); +} + +void MacroAssembler::store_check(Register tmp, Register obj, Register offset) { + store_check(tmp, obj); +} + +// %%% Note: The following six instructions have been moved, +// unchanged, from assembler_sparc.inline.hpp. +// They will be refactored at a later date. + +void MacroAssembler::sethi(intptr_t imm22a, + Register d, + bool ForceRelocatable, + RelocationHolder const& rspec) { + Address adr( d, (address)imm22a, rspec ); + MacroAssembler::sethi( adr, ForceRelocatable ); +} + + +void MacroAssembler::sethi(Address& a, bool ForceRelocatable) { + address save_pc; + int shiftcnt; + // if addr of local, do not need to load it + assert(a.base() != FP && a.base() != SP, "just use ld or st for locals"); +#ifdef _LP64 +# ifdef CHECK_DELAY + assert_not_delayed( (char *)"cannot put two instructions in delay slot" ); +# endif + v9_dep(); +// ForceRelocatable = 1; + save_pc = pc(); + if (a.hi32() == 0 && a.low32() >= 0) { + Assembler::sethi(a.low32(), a.base(), a.rspec()); + } + else if (a.hi32() == -1) { + Assembler::sethi(~a.low32(), a.base(), a.rspec()); + xor3(a.base(), ~low10(~0), a.base()); + } + else { + Assembler::sethi(a.hi32(), a.base(), a.rspec() ); // 22 + if ( a.hi32() & 0x3ff ) // Any bits? + or3( a.base(), a.hi32() & 0x3ff ,a.base() ); // High 32 bits are now in low 32 + if ( a.low32() & 0xFFFFFC00 ) { // done? + if( (a.low32() >> 20) & 0xfff ) { // Any bits set? + sllx(a.base(), 12, a.base()); // Make room for next 12 bits + or3( a.base(), (a.low32() >> 20) & 0xfff,a.base() ); // Or in next 12 + shiftcnt = 0; // We already shifted + } + else + shiftcnt = 12; + if( (a.low32() >> 10) & 0x3ff ) { + sllx(a.base(), shiftcnt+10, a.base());// Make room for last 10 bits + or3( a.base(), (a.low32() >> 10) & 0x3ff,a.base() ); // Or in next 10 + shiftcnt = 0; + } + else + shiftcnt = 10; + sllx(a.base(), shiftcnt+10 , a.base()); // Shift leaving disp field 0'd + } + else + sllx( a.base(), 32, a.base() ); + } + // Pad out the instruction sequence so it can be + // patched later. + if ( ForceRelocatable || (a.rtype() != relocInfo::none && + a.rtype() != relocInfo::runtime_call_type) ) { + while ( pc() < (save_pc + (7 * BytesPerInstWord )) ) + nop(); + } +#else + Assembler::sethi(a.hi(), a.base(), a.rspec()); +#endif + +} + +int MacroAssembler::size_of_sethi(address a, bool worst_case) { +#ifdef _LP64 + if (worst_case) return 7; + intptr_t iaddr = (intptr_t)a; + int hi32 = (int)(iaddr >> 32); + int lo32 = (int)(iaddr); + int inst_count; + if (hi32 == 0 && lo32 >= 0) + inst_count = 1; + else if (hi32 == -1) + inst_count = 2; + else { + inst_count = 2; + if ( hi32 & 0x3ff ) + inst_count++; + if ( lo32 & 0xFFFFFC00 ) { + if( (lo32 >> 20) & 0xfff ) inst_count += 2; + if( (lo32 >> 10) & 0x3ff ) inst_count += 2; + } + } + return BytesPerInstWord * inst_count; +#else + return BytesPerInstWord; +#endif +} + +int MacroAssembler::worst_case_size_of_set() { + return size_of_sethi(NULL, true) + 1; +} + +void MacroAssembler::set(intptr_t value, Register d, + RelocationHolder const& rspec) { + Address val( d, (address)value, rspec); + + if ( rspec.type() == relocInfo::none ) { + // can optimize + if (-4096 <= value && value <= 4095) { + or3(G0, value, d); // setsw (this leaves upper 32 bits sign-extended) + return; + } + if (inv_hi22(hi22(value)) == value) { + sethi(val); + return; + } + } + assert_not_delayed( (char *)"cannot put two instructions in delay slot" ); + sethi( val ); + if (rspec.type() != relocInfo::none || (value & 0x3ff) != 0) { + add( d, value & 0x3ff, d, rspec); + } +} + +void MacroAssembler::setsw(int value, Register d, + RelocationHolder const& rspec) { + Address val( d, (address)value, rspec); + if ( rspec.type() == relocInfo::none ) { + // can optimize + if (-4096 <= value && value <= 4095) { + or3(G0, value, d); + return; + } + if (inv_hi22(hi22(value)) == value) { + sethi( val ); +#ifndef _LP64 + if ( value < 0 ) { + assert_not_delayed(); + sra (d, G0, d); + } +#endif + return; + } + } + assert_not_delayed(); + sethi( val ); + add( d, value & 0x3ff, d, rspec); + + // (A negative value could be loaded in 2 insns with sethi/xor, + // but it would take a more complex relocation.) +#ifndef _LP64 + if ( value < 0) + sra(d, G0, d); +#endif +} + +// %%% End of moved six set instructions. + + +void MacroAssembler::set64(jlong value, Register d, Register tmp) { + assert_not_delayed(); + v9_dep(); + + int hi = (int)(value >> 32); + int lo = (int)(value & ~0); + // (Matcher::isSimpleConstant64 knows about the following optimizations.) + if (Assembler::is_simm13(lo) && value == lo) { + or3(G0, lo, d); + } else if (hi == 0) { + Assembler::sethi(lo, d); // hardware version zero-extends to upper 32 + if (low10(lo) != 0) + or3(d, low10(lo), d); + } + else if (hi == -1) { + Assembler::sethi(~lo, d); // hardware version zero-extends to upper 32 + xor3(d, low10(lo) ^ ~low10(~0), d); + } + else if (lo == 0) { + if (Assembler::is_simm13(hi)) { + or3(G0, hi, d); + } else { + Assembler::sethi(hi, d); // hardware version zero-extends to upper 32 + if (low10(hi) != 0) + or3(d, low10(hi), d); + } + sllx(d, 32, d); + } + else { + Assembler::sethi(hi, tmp); + Assembler::sethi(lo, d); // macro assembler version sign-extends + if (low10(hi) != 0) + or3 (tmp, low10(hi), tmp); + if (low10(lo) != 0) + or3 ( d, low10(lo), d); + sllx(tmp, 32, tmp); + or3 (d, tmp, d); + } +} + +// compute size in bytes of sparc frame, given +// number of extraWords +int MacroAssembler::total_frame_size_in_bytes(int extraWords) { + + int nWords = frame::memory_parameter_word_sp_offset; + + nWords += extraWords; + + if (nWords & 1) ++nWords; // round up to double-word + + return nWords * BytesPerWord; +} + + +// save_frame: given number of "extra" words in frame, +// issue approp. save instruction (p 200, v8 manual) + +void MacroAssembler::save_frame(int extraWords = 0) { + int delta = -total_frame_size_in_bytes(extraWords); + if (is_simm13(delta)) { + save(SP, delta, SP); + } else { + set(delta, G3_scratch); + save(SP, G3_scratch, SP); + } +} + + +void MacroAssembler::save_frame_c1(int size_in_bytes) { + if (is_simm13(-size_in_bytes)) { + save(SP, -size_in_bytes, SP); + } else { + set(-size_in_bytes, G3_scratch); + save(SP, G3_scratch, SP); + } +} + + +void MacroAssembler::save_frame_and_mov(int extraWords, + Register s1, Register d1, + Register s2, Register d2) { + assert_not_delayed(); + + // The trick here is to use precisely the same memory word + // that trap handlers also use to save the register. + // This word cannot be used for any other purpose, but + // it works fine to save the register's value, whether or not + // an interrupt flushes register windows at any given moment! + Address s1_addr; + if (s1->is_valid() && (s1->is_in() || s1->is_local())) { + s1_addr = s1->address_in_saved_window(); + st_ptr(s1, s1_addr); + } + + Address s2_addr; + if (s2->is_valid() && (s2->is_in() || s2->is_local())) { + s2_addr = s2->address_in_saved_window(); + st_ptr(s2, s2_addr); + } + + save_frame(extraWords); + + if (s1_addr.base() == SP) { + ld_ptr(s1_addr.after_save(), d1); + } else if (s1->is_valid()) { + mov(s1->after_save(), d1); + } + + if (s2_addr.base() == SP) { + ld_ptr(s2_addr.after_save(), d2); + } else if (s2->is_valid()) { + mov(s2->after_save(), d2); + } +} + + +Address MacroAssembler::allocate_oop_address(jobject obj, Register d) { + assert(oop_recorder() != NULL, "this assembler needs an OopRecorder"); + int oop_index = oop_recorder()->allocate_index(obj); + return Address(d, address(obj), oop_Relocation::spec(oop_index)); +} + + +Address MacroAssembler::constant_oop_address(jobject obj, Register d) { + assert(oop_recorder() != NULL, "this assembler needs an OopRecorder"); + int oop_index = oop_recorder()->find_index(obj); + return Address(d, address(obj), oop_Relocation::spec(oop_index)); +} + + +void MacroAssembler::align(int modulus) { + while (offset() % modulus != 0) nop(); +} + + +void MacroAssembler::safepoint() { + relocate(breakpoint_Relocation::spec(breakpoint_Relocation::safepoint)); +} + + +void RegistersForDebugging::print(outputStream* s) { + int j; + for ( j = 0; j < 8; ++j ) + if ( j != 6 ) s->print_cr("i%d = 0x%.16lx", j, i[j]); + else s->print_cr( "fp = 0x%.16lx", i[j]); + s->cr(); + + for ( j = 0; j < 8; ++j ) + s->print_cr("l%d = 0x%.16lx", j, l[j]); + s->cr(); + + for ( j = 0; j < 8; ++j ) + if ( j != 6 ) s->print_cr("o%d = 0x%.16lx", j, o[j]); + else s->print_cr( "sp = 0x%.16lx", o[j]); + s->cr(); + + for ( j = 0; j < 8; ++j ) + s->print_cr("g%d = 0x%.16lx", j, g[j]); + s->cr(); + + // print out floats with compression + for (j = 0; j < 32; ) { + jfloat val = f[j]; + int last = j; + for ( ; last+1 < 32; ++last ) { + char b1[1024], b2[1024]; + sprintf(b1, "%f", val); + sprintf(b2, "%f", f[last+1]); + if (strcmp(b1, b2)) + break; + } + s->print("f%d", j); + if ( j != last ) s->print(" - f%d", last); + s->print(" = %f", val); + s->fill_to(25); + s->print_cr(" (0x%x)", val); + j = last + 1; + } + s->cr(); + + // and doubles (evens only) + for (j = 0; j < 32; ) { + jdouble val = d[j]; + int last = j; + for ( ; last+1 < 32; ++last ) { + char b1[1024], b2[1024]; + sprintf(b1, "%f", val); + sprintf(b2, "%f", d[last+1]); + if (strcmp(b1, b2)) + break; + } + s->print("d%d", 2 * j); + if ( j != last ) s->print(" - d%d", last); + s->print(" = %f", val); + s->fill_to(30); + s->print("(0x%x)", *(int*)&val); + s->fill_to(42); + s->print_cr("(0x%x)", *(1 + (int*)&val)); + j = last + 1; + } + s->cr(); +} + +void RegistersForDebugging::save_registers(MacroAssembler* a) { + a->sub(FP, round_to(sizeof(RegistersForDebugging), sizeof(jdouble)) - STACK_BIAS, O0); + a->flush_windows(); + int i; + for (i = 0; i < 8; ++i) { + a->ld_ptr(as_iRegister(i)->address_in_saved_window().after_save(), L1); a->st_ptr( L1, O0, i_offset(i)); + a->ld_ptr(as_lRegister(i)->address_in_saved_window().after_save(), L1); a->st_ptr( L1, O0, l_offset(i)); + a->st_ptr(as_oRegister(i)->after_save(), O0, o_offset(i)); + a->st_ptr(as_gRegister(i)->after_save(), O0, g_offset(i)); + } + for (i = 0; i < 32; ++i) { + a->stf(FloatRegisterImpl::S, as_FloatRegister(i), O0, f_offset(i)); + } + for (i = 0; i < (VM_Version::v9_instructions_work() ? 64 : 32); i += 2) { + a->stf(FloatRegisterImpl::D, as_FloatRegister(i), O0, d_offset(i)); + } +} + +void RegistersForDebugging::restore_registers(MacroAssembler* a, Register r) { + for (int i = 1; i < 8; ++i) { + a->ld_ptr(r, g_offset(i), as_gRegister(i)); + } + for (int j = 0; j < 32; ++j) { + a->ldf(FloatRegisterImpl::S, O0, f_offset(j), as_FloatRegister(j)); + } + for (int k = 0; k < (VM_Version::v9_instructions_work() ? 64 : 32); k += 2) { + a->ldf(FloatRegisterImpl::D, O0, d_offset(k), as_FloatRegister(k)); + } +} + + +// pushes double TOS element of FPU stack on CPU stack; pops from FPU stack +void MacroAssembler::push_fTOS() { + // %%%%%% need to implement this +} + +// pops double TOS element from CPU stack and pushes on FPU stack +void MacroAssembler::pop_fTOS() { + // %%%%%% need to implement this +} + +void MacroAssembler::empty_FPU_stack() { + // %%%%%% need to implement this +} + +void MacroAssembler::_verify_oop(Register reg, const char* msg, const char * file, int line) { + // plausibility check for oops + if (!VerifyOops) return; + + if (reg == G0) return; // always NULL, which is always an oop + + char buffer[16]; + sprintf(buffer, "%d", line); + int len = strlen(file) + strlen(msg) + 1 + 4 + strlen(buffer); + char * real_msg = new char[len]; + sprintf(real_msg, "%s (%s:%d)", msg, file, line); + + // Call indirectly to solve generation ordering problem + Address a(O7, (address)StubRoutines::verify_oop_subroutine_entry_address()); + + // Make some space on stack above the current register window. + // Enough to hold 8 64-bit registers. + add(SP,-8*8,SP); + + // Save some 64-bit registers; a normal 'save' chops the heads off + // of 64-bit longs in the 32-bit build. + stx(O0,SP,frame::register_save_words*wordSize+STACK_BIAS+0*8); + stx(O1,SP,frame::register_save_words*wordSize+STACK_BIAS+1*8); + mov(reg,O0); // Move arg into O0; arg might be in O7 which is about to be crushed + stx(O7,SP,frame::register_save_words*wordSize+STACK_BIAS+7*8); + + set((intptr_t)real_msg, O1); + // Load address to call to into O7 + load_ptr_contents(a, O7); + // Register call to verify_oop_subroutine + callr(O7, G0); + delayed()->nop(); + // recover frame size + add(SP, 8*8,SP); +} + +void MacroAssembler::_verify_oop_addr(Address addr, const char* msg, const char * file, int line) { + // plausibility check for oops + if (!VerifyOops) return; + + char buffer[64]; + sprintf(buffer, "%d", line); + int len = strlen(file) + strlen(msg) + 1 + 4 + strlen(buffer); + sprintf(buffer, " at SP+%d ", addr.disp()); + len += strlen(buffer); + char * real_msg = new char[len]; + sprintf(real_msg, "%s at SP+%d (%s:%d)", msg, addr.disp(), file, line); + + // Call indirectly to solve generation ordering problem + Address a(O7, (address)StubRoutines::verify_oop_subroutine_entry_address()); + + // Make some space on stack above the current register window. + // Enough to hold 8 64-bit registers. + add(SP,-8*8,SP); + + // Save some 64-bit registers; a normal 'save' chops the heads off + // of 64-bit longs in the 32-bit build. + stx(O0,SP,frame::register_save_words*wordSize+STACK_BIAS+0*8); + stx(O1,SP,frame::register_save_words*wordSize+STACK_BIAS+1*8); + ld_ptr(addr.base(), addr.disp() + 8*8, O0); // Load arg into O0; arg might be in O7 which is about to be crushed + stx(O7,SP,frame::register_save_words*wordSize+STACK_BIAS+7*8); + + set((intptr_t)real_msg, O1); + // Load address to call to into O7 + load_ptr_contents(a, O7); + // Register call to verify_oop_subroutine + callr(O7, G0); + delayed()->nop(); + // recover frame size + add(SP, 8*8,SP); +} + +// side-door communication with signalHandler in os_solaris.cpp +address MacroAssembler::_verify_oop_implicit_branch[3] = { NULL }; + +// This macro is expanded just once; it creates shared code. Contract: +// receives an oop in O0. Must restore O0 & O7 from TLS. Must not smash ANY +// registers, including flags. May not use a register 'save', as this blows +// the high bits of the O-regs if they contain Long values. Acts as a 'leaf' +// call. +void MacroAssembler::verify_oop_subroutine() { + assert( VM_Version::v9_instructions_work(), "VerifyOops not supported for V8" ); + + // Leaf call; no frame. + Label succeed, fail, null_or_fail; + + // O0 and O7 were saved already (O0 in O0's TLS home, O7 in O5's TLS home). + // O0 is now the oop to be checked. O7 is the return address. + Register O0_obj = O0; + + // Save some more registers for temps. + stx(O2,SP,frame::register_save_words*wordSize+STACK_BIAS+2*8); + stx(O3,SP,frame::register_save_words*wordSize+STACK_BIAS+3*8); + stx(O4,SP,frame::register_save_words*wordSize+STACK_BIAS+4*8); + stx(O5,SP,frame::register_save_words*wordSize+STACK_BIAS+5*8); + + // Save flags + Register O5_save_flags = O5; + rdccr( O5_save_flags ); + + { // count number of verifies + Register O2_adr = O2; + Register O3_accum = O3; + Address count_addr( O2_adr, (address) StubRoutines::verify_oop_count_addr() ); + sethi(count_addr); + ld(count_addr, O3_accum); + inc(O3_accum); + st(O3_accum, count_addr); + } + + Register O2_mask = O2; + Register O3_bits = O3; + Register O4_temp = O4; + + // mark lower end of faulting range + assert(_verify_oop_implicit_branch[0] == NULL, "set once"); + _verify_oop_implicit_branch[0] = pc(); + + // We can't check the mark oop because it could be in the process of + // locking or unlocking while this is running. + set(Universe::verify_oop_mask (), O2_mask); + set(Universe::verify_oop_bits (), O3_bits); + + // assert((obj & oop_mask) == oop_bits); + and3(O0_obj, O2_mask, O4_temp); + cmp(O4_temp, O3_bits); + brx(notEqual, false, pn, null_or_fail); + delayed()->nop(); + + if ((NULL_WORD & Universe::verify_oop_mask()) == Universe::verify_oop_bits()) { + // the null_or_fail case is useless; must test for null separately + br_null(O0_obj, false, pn, succeed); + delayed()->nop(); + } + + // Check the klassOop of this object for being in the right area of memory. + // Cannot do the load in the delay above slot in case O0 is null + ld_ptr(Address(O0_obj, 0, oopDesc::klass_offset_in_bytes()), O0_obj); + // assert((klass & klass_mask) == klass_bits); + if( Universe::verify_klass_mask() != Universe::verify_oop_mask() ) + set(Universe::verify_klass_mask(), O2_mask); + if( Universe::verify_klass_bits() != Universe::verify_oop_bits() ) + set(Universe::verify_klass_bits(), O3_bits); + and3(O0_obj, O2_mask, O4_temp); + cmp(O4_temp, O3_bits); + brx(notEqual, false, pn, fail); + // Check the klass's klass + delayed()->ld_ptr(Address(O0_obj, 0, oopDesc::klass_offset_in_bytes()), O0_obj); + and3(O0_obj, O2_mask, O4_temp); + cmp(O4_temp, O3_bits); + brx(notEqual, false, pn, fail); + delayed()->wrccr( O5_save_flags ); // Restore CCR's + + // mark upper end of faulting range + _verify_oop_implicit_branch[1] = pc(); + + //----------------------- + // all tests pass + bind(succeed); + + // Restore prior 64-bit registers + ldx(SP,frame::register_save_words*wordSize+STACK_BIAS+0*8,O0); + ldx(SP,frame::register_save_words*wordSize+STACK_BIAS+1*8,O1); + ldx(SP,frame::register_save_words*wordSize+STACK_BIAS+2*8,O2); + ldx(SP,frame::register_save_words*wordSize+STACK_BIAS+3*8,O3); + ldx(SP,frame::register_save_words*wordSize+STACK_BIAS+4*8,O4); + ldx(SP,frame::register_save_words*wordSize+STACK_BIAS+5*8,O5); + + retl(); // Leaf return; restore prior O7 in delay slot + delayed()->ldx(SP,frame::register_save_words*wordSize+STACK_BIAS+7*8,O7); + + //----------------------- + bind(null_or_fail); // nulls are less common but OK + br_null(O0_obj, false, pt, succeed); + delayed()->wrccr( O5_save_flags ); // Restore CCR's + + //----------------------- + // report failure: + bind(fail); + _verify_oop_implicit_branch[2] = pc(); + + wrccr( O5_save_flags ); // Restore CCR's + + save_frame(::round_to(sizeof(RegistersForDebugging) / BytesPerWord, 2)); + + // stop_subroutine expects message pointer in I1. + mov(I1, O1); + + // Restore prior 64-bit registers + ldx(FP,frame::register_save_words*wordSize+STACK_BIAS+0*8,I0); + ldx(FP,frame::register_save_words*wordSize+STACK_BIAS+1*8,I1); + ldx(FP,frame::register_save_words*wordSize+STACK_BIAS+2*8,I2); + ldx(FP,frame::register_save_words*wordSize+STACK_BIAS+3*8,I3); + ldx(FP,frame::register_save_words*wordSize+STACK_BIAS+4*8,I4); + ldx(FP,frame::register_save_words*wordSize+STACK_BIAS+5*8,I5); + + // factor long stop-sequence into subroutine to save space + assert(StubRoutines::Sparc::stop_subroutine_entry_address(), "hasn't been generated yet"); + + // call indirectly to solve generation ordering problem + Address a(O5, (address)StubRoutines::Sparc::stop_subroutine_entry_address()); + load_ptr_contents(a, O5); + jmpl(O5, 0, O7); + delayed()->nop(); +} + + +void MacroAssembler::stop(const char* msg) { + // save frame first to get O7 for return address + // add one word to size in case struct is odd number of words long + // It must be doubleword-aligned for storing doubles into it. + + save_frame(::round_to(sizeof(RegistersForDebugging) / BytesPerWord, 2)); + + // stop_subroutine expects message pointer in I1. + set((intptr_t)msg, O1); + + // factor long stop-sequence into subroutine to save space + assert(StubRoutines::Sparc::stop_subroutine_entry_address(), "hasn't been generated yet"); + + // call indirectly to solve generation ordering problem + Address a(O5, (address)StubRoutines::Sparc::stop_subroutine_entry_address()); + load_ptr_contents(a, O5); + jmpl(O5, 0, O7); + delayed()->nop(); + + breakpoint_trap(); // make stop actually stop rather than writing + // unnoticeable results in the output files. + + // restore(); done in callee to save space! +} + + +void MacroAssembler::warn(const char* msg) { + save_frame(::round_to(sizeof(RegistersForDebugging) / BytesPerWord, 2)); + RegistersForDebugging::save_registers(this); + mov(O0, L0); + set((intptr_t)msg, O0); + call( CAST_FROM_FN_PTR(address, warning) ); + delayed()->nop(); +// ret(); +// delayed()->restore(); + RegistersForDebugging::restore_registers(this, L0); + restore(); +} + + +void MacroAssembler::untested(const char* what) { + // We must be able to turn interactive prompting off + // in order to run automated test scripts on the VM + // Use the flag ShowMessageBoxOnError + + char* b = new char[1024]; + sprintf(b, "untested: %s", what); + + if ( ShowMessageBoxOnError ) stop(b); + else warn(b); +} + + +void MacroAssembler::stop_subroutine() { + RegistersForDebugging::save_registers(this); + + // for the sake of the debugger, stick a PC on the current frame + // (this assumes that the caller has performed an extra "save") + mov(I7, L7); + add(O7, -7 * BytesPerInt, I7); + + save_frame(); // one more save to free up another O7 register + mov(I0, O1); // addr of reg save area + + // We expect pointer to message in I1. Caller must set it up in O1 + mov(I1, O0); // get msg + call (CAST_FROM_FN_PTR(address, MacroAssembler::debug), relocInfo::runtime_call_type); + delayed()->nop(); + + restore(); + + RegistersForDebugging::restore_registers(this, O0); + + save_frame(0); + call(CAST_FROM_FN_PTR(address,breakpoint)); + delayed()->nop(); + restore(); + + mov(L7, I7); + retl(); + delayed()->restore(); // see stop above +} + + +void MacroAssembler::debug(char* msg, RegistersForDebugging* regs) { + if ( ShowMessageBoxOnError ) { + JavaThreadState saved_state = JavaThread::current()->thread_state(); + JavaThread::current()->set_thread_state(_thread_in_vm); + { + // In order to get locks work, we need to fake a in_VM state + ttyLocker ttyl; + ::tty->print_cr("EXECUTION STOPPED: %s\n", msg); + if (CountBytecodes || TraceBytecodes || StopInterpreterAt) { + ::tty->print_cr("Interpreter::bytecode_counter = %d", BytecodeCounter::counter_value()); + } + if (os::message_box(msg, "Execution stopped, print registers?")) + regs->print(::tty); + } + ThreadStateTransition::transition(JavaThread::current(), _thread_in_vm, saved_state); + } + else + ::tty->print_cr("=============== DEBUG MESSAGE: %s ================\n", msg); + assert(false, "error"); +} + + +#ifndef PRODUCT +void MacroAssembler::test() { + ResourceMark rm; + + CodeBuffer cb("test", 10000, 10000); + MacroAssembler* a = new MacroAssembler(&cb); + VM_Version::allow_all(); + a->test_v9(); + a->test_v8_onlys(); + VM_Version::revert(); + + StubRoutines::Sparc::test_stop_entry()(); +} +#endif + + +void MacroAssembler::calc_mem_param_words(Register Rparam_words, Register Rresult) { + subcc( Rparam_words, Argument::n_register_parameters, Rresult); // how many mem words? + Label no_extras; + br( negative, true, pt, no_extras ); // if neg, clear reg + delayed()->set( 0, Rresult); // annuled, so only if taken + bind( no_extras ); +} + + +void MacroAssembler::calc_frame_size(Register Rextra_words, Register Rresult) { +#ifdef _LP64 + add(Rextra_words, frame::memory_parameter_word_sp_offset, Rresult); +#else + add(Rextra_words, frame::memory_parameter_word_sp_offset + 1, Rresult); +#endif + bclr(1, Rresult); + sll(Rresult, LogBytesPerWord, Rresult); // Rresult has total frame bytes +} + + +void MacroAssembler::calc_frame_size_and_save(Register Rextra_words, Register Rresult) { + calc_frame_size(Rextra_words, Rresult); + neg(Rresult); + save(SP, Rresult, SP); +} + + +// --------------------------------------------------------- +Assembler::RCondition cond2rcond(Assembler::Condition c) { + switch (c) { + /*case zero: */ + case Assembler::equal: return Assembler::rc_z; + case Assembler::lessEqual: return Assembler::rc_lez; + case Assembler::less: return Assembler::rc_lz; + /*case notZero:*/ + case Assembler::notEqual: return Assembler::rc_nz; + case Assembler::greater: return Assembler::rc_gz; + case Assembler::greaterEqual: return Assembler::rc_gez; + } + ShouldNotReachHere(); + return Assembler::rc_z; +} + +// compares register with zero and branches. NOT FOR USE WITH 64-bit POINTERS +void MacroAssembler::br_zero( Condition c, bool a, Predict p, Register s1, Label& L) { + tst(s1); + br (c, a, p, L); +} + + +// Compares a pointer register with zero and branches on null. +// Does a test & branch on 32-bit systems and a register-branch on 64-bit. +void MacroAssembler::br_null( Register s1, bool a, Predict p, Label& L ) { + assert_not_delayed(); +#ifdef _LP64 + bpr( rc_z, a, p, s1, L ); +#else + tst(s1); + br ( zero, a, p, L ); +#endif +} + +void MacroAssembler::br_notnull( Register s1, bool a, Predict p, Label& L ) { + assert_not_delayed(); +#ifdef _LP64 + bpr( rc_nz, a, p, s1, L ); +#else + tst(s1); + br ( notZero, a, p, L ); +#endif +} + + +// instruction sequences factored across compiler & interpreter + + +void MacroAssembler::lcmp( Register Ra_hi, Register Ra_low, + Register Rb_hi, Register Rb_low, + Register Rresult) { + + Label check_low_parts, done; + + cmp(Ra_hi, Rb_hi ); // compare hi parts + br(equal, true, pt, check_low_parts); + delayed()->cmp(Ra_low, Rb_low); // test low parts + + // And, with an unsigned comparison, it does not matter if the numbers + // are negative or not. + // E.g., -2 cmp -1: the low parts are 0xfffffffe and 0xffffffff. + // The second one is bigger (unsignedly). + + // Other notes: The first move in each triplet can be unconditional + // (and therefore probably prefetchable). + // And the equals case for the high part does not need testing, + // since that triplet is reached only after finding the high halves differ. + + if (VM_Version::v9_instructions_work()) { + + mov ( -1, Rresult); + ba( false, done ); delayed()-> movcc(greater, false, icc, 1, Rresult); + } + else { + br(less, true, pt, done); delayed()-> set(-1, Rresult); + br(greater, true, pt, done); delayed()-> set( 1, Rresult); + } + + bind( check_low_parts ); + + if (VM_Version::v9_instructions_work()) { + mov( -1, Rresult); + movcc(equal, false, icc, 0, Rresult); + movcc(greaterUnsigned, false, icc, 1, Rresult); + } + else { + set(-1, Rresult); + br(equal, true, pt, done); delayed()->set( 0, Rresult); + br(greaterUnsigned, true, pt, done); delayed()->set( 1, Rresult); + } + bind( done ); +} + +void MacroAssembler::lneg( Register Rhi, Register Rlow ) { + subcc( G0, Rlow, Rlow ); + subc( G0, Rhi, Rhi ); +} + +void MacroAssembler::lshl( Register Rin_high, Register Rin_low, + Register Rcount, + Register Rout_high, Register Rout_low, + Register Rtemp ) { + + + Register Ralt_count = Rtemp; + Register Rxfer_bits = Rtemp; + + assert( Ralt_count != Rin_high + && Ralt_count != Rin_low + && Ralt_count != Rcount + && Rxfer_bits != Rin_low + && Rxfer_bits != Rin_high + && Rxfer_bits != Rcount + && Rxfer_bits != Rout_low + && Rout_low != Rin_high, + "register alias checks"); + + Label big_shift, done; + + // This code can be optimized to use the 64 bit shifts in V9. + // Here we use the 32 bit shifts. + + and3( Rcount, 0x3f, Rcount); // take least significant 6 bits + subcc(Rcount, 31, Ralt_count); + br(greater, true, pn, big_shift); + delayed()-> + dec(Ralt_count); + + // shift < 32 bits, Ralt_count = Rcount-31 + + // We get the transfer bits by shifting right by 32-count the low + // register. This is done by shifting right by 31-count and then by one + // more to take care of the special (rare) case where count is zero + // (shifting by 32 would not work). + + neg( Ralt_count ); + + // The order of the next two instructions is critical in the case where + // Rin and Rout are the same and should not be reversed. + + srl( Rin_low, Ralt_count, Rxfer_bits ); // shift right by 31-count + if (Rcount != Rout_low) { + sll( Rin_low, Rcount, Rout_low ); // low half + } + sll( Rin_high, Rcount, Rout_high ); + if (Rcount == Rout_low) { + sll( Rin_low, Rcount, Rout_low ); // low half + } + srl( Rxfer_bits, 1, Rxfer_bits ); // shift right by one more + ba (false, done); + delayed()-> + or3( Rout_high, Rxfer_bits, Rout_high); // new hi value: or in shifted old hi part and xfer from low + + // shift >= 32 bits, Ralt_count = Rcount-32 + bind(big_shift); + sll( Rin_low, Ralt_count, Rout_high ); + clr( Rout_low ); + + bind(done); +} + + +void MacroAssembler::lshr( Register Rin_high, Register Rin_low, + Register Rcount, + Register Rout_high, Register Rout_low, + Register Rtemp ) { + + Register Ralt_count = Rtemp; + Register Rxfer_bits = Rtemp; + + assert( Ralt_count != Rin_high + && Ralt_count != Rin_low + && Ralt_count != Rcount + && Rxfer_bits != Rin_low + && Rxfer_bits != Rin_high + && Rxfer_bits != Rcount + && Rxfer_bits != Rout_high + && Rout_high != Rin_low, + "register alias checks"); + + Label big_shift, done; + + // This code can be optimized to use the 64 bit shifts in V9. + // Here we use the 32 bit shifts. + + and3( Rcount, 0x3f, Rcount); // take least significant 6 bits + subcc(Rcount, 31, Ralt_count); + br(greater, true, pn, big_shift); + delayed()->dec(Ralt_count); + + // shift < 32 bits, Ralt_count = Rcount-31 + + // We get the transfer bits by shifting left by 32-count the high + // register. This is done by shifting left by 31-count and then by one + // more to take care of the special (rare) case where count is zero + // (shifting by 32 would not work). + + neg( Ralt_count ); + if (Rcount != Rout_low) { + srl( Rin_low, Rcount, Rout_low ); + } + + // The order of the next two instructions is critical in the case where + // Rin and Rout are the same and should not be reversed. + + sll( Rin_high, Ralt_count, Rxfer_bits ); // shift left by 31-count + sra( Rin_high, Rcount, Rout_high ); // high half + sll( Rxfer_bits, 1, Rxfer_bits ); // shift left by one more + if (Rcount == Rout_low) { + srl( Rin_low, Rcount, Rout_low ); + } + ba (false, done); + delayed()-> + or3( Rout_low, Rxfer_bits, Rout_low ); // new low value: or shifted old low part and xfer from high + + // shift >= 32 bits, Ralt_count = Rcount-32 + bind(big_shift); + + sra( Rin_high, Ralt_count, Rout_low ); + sra( Rin_high, 31, Rout_high ); // sign into hi + + bind( done ); +} + + + +void MacroAssembler::lushr( Register Rin_high, Register Rin_low, + Register Rcount, + Register Rout_high, Register Rout_low, + Register Rtemp ) { + + Register Ralt_count = Rtemp; + Register Rxfer_bits = Rtemp; + + assert( Ralt_count != Rin_high + && Ralt_count != Rin_low + && Ralt_count != Rcount + && Rxfer_bits != Rin_low + && Rxfer_bits != Rin_high + && Rxfer_bits != Rcount + && Rxfer_bits != Rout_high + && Rout_high != Rin_low, + "register alias checks"); + + Label big_shift, done; + + // This code can be optimized to use the 64 bit shifts in V9. + // Here we use the 32 bit shifts. + + and3( Rcount, 0x3f, Rcount); // take least significant 6 bits + subcc(Rcount, 31, Ralt_count); + br(greater, true, pn, big_shift); + delayed()->dec(Ralt_count); + + // shift < 32 bits, Ralt_count = Rcount-31 + + // We get the transfer bits by shifting left by 32-count the high + // register. This is done by shifting left by 31-count and then by one + // more to take care of the special (rare) case where count is zero + // (shifting by 32 would not work). + + neg( Ralt_count ); + if (Rcount != Rout_low) { + srl( Rin_low, Rcount, Rout_low ); + } + + // The order of the next two instructions is critical in the case where + // Rin and Rout are the same and should not be reversed. + + sll( Rin_high, Ralt_count, Rxfer_bits ); // shift left by 31-count + srl( Rin_high, Rcount, Rout_high ); // high half + sll( Rxfer_bits, 1, Rxfer_bits ); // shift left by one more + if (Rcount == Rout_low) { + srl( Rin_low, Rcount, Rout_low ); + } + ba (false, done); + delayed()-> + or3( Rout_low, Rxfer_bits, Rout_low ); // new low value: or shifted old low part and xfer from high + + // shift >= 32 bits, Ralt_count = Rcount-32 + bind(big_shift); + + srl( Rin_high, Ralt_count, Rout_low ); + clr( Rout_high ); + + bind( done ); +} + +#ifdef _LP64 +void MacroAssembler::lcmp( Register Ra, Register Rb, Register Rresult) { + cmp(Ra, Rb); + mov( -1, Rresult); + movcc(equal, false, xcc, 0, Rresult); + movcc(greater, false, xcc, 1, Rresult); +} +#endif + + +void MacroAssembler::float_cmp( bool is_float, int unordered_result, + FloatRegister Fa, FloatRegister Fb, + Register Rresult) { + + fcmp(is_float ? FloatRegisterImpl::S : FloatRegisterImpl::D, fcc0, Fa, Fb); + + Condition lt = unordered_result == -1 ? f_unorderedOrLess : f_less; + Condition eq = f_equal; + Condition gt = unordered_result == 1 ? f_unorderedOrGreater : f_greater; + + if (VM_Version::v9_instructions_work()) { + + mov( -1, Rresult ); + movcc( eq, true, fcc0, 0, Rresult ); + movcc( gt, true, fcc0, 1, Rresult ); + + } else { + Label done; + + set( -1, Rresult ); + //fb(lt, true, pn, done); delayed()->set( -1, Rresult ); + fb( eq, true, pn, done); delayed()->set( 0, Rresult ); + fb( gt, true, pn, done); delayed()->set( 1, Rresult ); + + bind (done); + } +} + + +void MacroAssembler::fneg( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d) +{ + if (VM_Version::v9_instructions_work()) { + Assembler::fneg(w, s, d); + } else { + if (w == FloatRegisterImpl::S) { + Assembler::fneg(w, s, d); + } else if (w == FloatRegisterImpl::D) { + // number() does a sanity check on the alignment. + assert(((s->encoding(FloatRegisterImpl::D) & 1) == 0) && + ((d->encoding(FloatRegisterImpl::D) & 1) == 0), "float register alignment check"); + + Assembler::fneg(FloatRegisterImpl::S, s, d); + Assembler::fmov(FloatRegisterImpl::S, s->successor(), d->successor()); + } else { + assert(w == FloatRegisterImpl::Q, "Invalid float register width"); + + // number() does a sanity check on the alignment. + assert(((s->encoding(FloatRegisterImpl::D) & 3) == 0) && + ((d->encoding(FloatRegisterImpl::D) & 3) == 0), "float register alignment check"); + + Assembler::fneg(FloatRegisterImpl::S, s, d); + Assembler::fmov(FloatRegisterImpl::S, s->successor(), d->successor()); + Assembler::fmov(FloatRegisterImpl::S, s->successor()->successor(), d->successor()->successor()); + Assembler::fmov(FloatRegisterImpl::S, s->successor()->successor()->successor(), d->successor()->successor()->successor()); + } + } +} + +void MacroAssembler::fmov( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d) +{ + if (VM_Version::v9_instructions_work()) { + Assembler::fmov(w, s, d); + } else { + if (w == FloatRegisterImpl::S) { + Assembler::fmov(w, s, d); + } else if (w == FloatRegisterImpl::D) { + // number() does a sanity check on the alignment. + assert(((s->encoding(FloatRegisterImpl::D) & 1) == 0) && + ((d->encoding(FloatRegisterImpl::D) & 1) == 0), "float register alignment check"); + + Assembler::fmov(FloatRegisterImpl::S, s, d); + Assembler::fmov(FloatRegisterImpl::S, s->successor(), d->successor()); + } else { + assert(w == FloatRegisterImpl::Q, "Invalid float register width"); + + // number() does a sanity check on the alignment. + assert(((s->encoding(FloatRegisterImpl::D) & 3) == 0) && + ((d->encoding(FloatRegisterImpl::D) & 3) == 0), "float register alignment check"); + + Assembler::fmov(FloatRegisterImpl::S, s, d); + Assembler::fmov(FloatRegisterImpl::S, s->successor(), d->successor()); + Assembler::fmov(FloatRegisterImpl::S, s->successor()->successor(), d->successor()->successor()); + Assembler::fmov(FloatRegisterImpl::S, s->successor()->successor()->successor(), d->successor()->successor()->successor()); + } + } +} + +void MacroAssembler::fabs( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d) +{ + if (VM_Version::v9_instructions_work()) { + Assembler::fabs(w, s, d); + } else { + if (w == FloatRegisterImpl::S) { + Assembler::fabs(w, s, d); + } else if (w == FloatRegisterImpl::D) { + // number() does a sanity check on the alignment. + assert(((s->encoding(FloatRegisterImpl::D) & 1) == 0) && + ((d->encoding(FloatRegisterImpl::D) & 1) == 0), "float register alignment check"); + + Assembler::fabs(FloatRegisterImpl::S, s, d); + Assembler::fmov(FloatRegisterImpl::S, s->successor(), d->successor()); + } else { + assert(w == FloatRegisterImpl::Q, "Invalid float register width"); + + // number() does a sanity check on the alignment. + assert(((s->encoding(FloatRegisterImpl::D) & 3) == 0) && + ((d->encoding(FloatRegisterImpl::D) & 3) == 0), "float register alignment check"); + + Assembler::fabs(FloatRegisterImpl::S, s, d); + Assembler::fmov(FloatRegisterImpl::S, s->successor(), d->successor()); + Assembler::fmov(FloatRegisterImpl::S, s->successor()->successor(), d->successor()->successor()); + Assembler::fmov(FloatRegisterImpl::S, s->successor()->successor()->successor(), d->successor()->successor()->successor()); + } + } +} + +void MacroAssembler::save_all_globals_into_locals() { + mov(G1,L1); + mov(G2,L2); + mov(G3,L3); + mov(G4,L4); + mov(G5,L5); + mov(G6,L6); + mov(G7,L7); +} + +void MacroAssembler::restore_globals_from_locals() { + mov(L1,G1); + mov(L2,G2); + mov(L3,G3); + mov(L4,G4); + mov(L5,G5); + mov(L6,G6); + mov(L7,G7); +} + +// Use for 64 bit operation. +void MacroAssembler::casx_under_lock(Register top_ptr_reg, Register top_reg, Register ptr_reg, address lock_addr, bool use_call_vm) +{ + // store ptr_reg as the new top value +#ifdef _LP64 + casx(top_ptr_reg, top_reg, ptr_reg); +#else + cas_under_lock(top_ptr_reg, top_reg, ptr_reg, lock_addr, use_call_vm); +#endif // _LP64 +} + +// [RGV] This routine does not handle 64 bit operations. +// use casx_under_lock() or casx directly!!! +void MacroAssembler::cas_under_lock(Register top_ptr_reg, Register top_reg, Register ptr_reg, address lock_addr, bool use_call_vm) +{ + // store ptr_reg as the new top value + if (VM_Version::v9_instructions_work()) { + cas(top_ptr_reg, top_reg, ptr_reg); + } else { + + // If the register is not an out nor global, it is not visible + // after the save. Allocate a register for it, save its + // value in the register save area (the save may not flush + // registers to the save area). + + Register top_ptr_reg_after_save; + Register top_reg_after_save; + Register ptr_reg_after_save; + + if (top_ptr_reg->is_out() || top_ptr_reg->is_global()) { + top_ptr_reg_after_save = top_ptr_reg->after_save(); + } else { + Address reg_save_addr = top_ptr_reg->address_in_saved_window(); + top_ptr_reg_after_save = L0; + st(top_ptr_reg, reg_save_addr); + } + + if (top_reg->is_out() || top_reg->is_global()) { + top_reg_after_save = top_reg->after_save(); + } else { + Address reg_save_addr = top_reg->address_in_saved_window(); + top_reg_after_save = L1; + st(top_reg, reg_save_addr); + } + + if (ptr_reg->is_out() || ptr_reg->is_global()) { + ptr_reg_after_save = ptr_reg->after_save(); + } else { + Address reg_save_addr = ptr_reg->address_in_saved_window(); + ptr_reg_after_save = L2; + st(ptr_reg, reg_save_addr); + } + + const Register& lock_reg = L3; + const Register& lock_ptr_reg = L4; + const Register& value_reg = L5; + const Register& yield_reg = L6; + const Register& yieldall_reg = L7; + + save_frame(); + + if (top_ptr_reg_after_save == L0) { + ld(top_ptr_reg->address_in_saved_window().after_save(), top_ptr_reg_after_save); + } + + if (top_reg_after_save == L1) { + ld(top_reg->address_in_saved_window().after_save(), top_reg_after_save); + } + + if (ptr_reg_after_save == L2) { + ld(ptr_reg->address_in_saved_window().after_save(), ptr_reg_after_save); + } + + Label(retry_get_lock); + Label(not_same); + Label(dont_yield); + + assert(lock_addr, "lock_address should be non null for v8"); + set((intptr_t)lock_addr, lock_ptr_reg); + // Initialize yield counter + mov(G0,yield_reg); + mov(G0, yieldall_reg); + set(StubRoutines::Sparc::locked, lock_reg); + + bind(retry_get_lock); + cmp(yield_reg, V8AtomicOperationUnderLockSpinCount); + br(Assembler::less, false, Assembler::pt, dont_yield); + delayed()->nop(); + + if(use_call_vm) { + Untested("Need to verify global reg consistancy"); + call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::yield_all), yieldall_reg); + } else { + // Save the regs and make space for a C call + save(SP, -96, SP); + save_all_globals_into_locals(); + call(CAST_FROM_FN_PTR(address,os::yield_all)); + delayed()->mov(yieldall_reg, O0); + restore_globals_from_locals(); + restore(); + } + + // reset the counter + mov(G0,yield_reg); + add(yieldall_reg, 1, yieldall_reg); + + bind(dont_yield); + // try to get lock + swap(lock_ptr_reg, 0, lock_reg); + + // did we get the lock? + cmp(lock_reg, StubRoutines::Sparc::unlocked); + br(Assembler::notEqual, true, Assembler::pn, retry_get_lock); + delayed()->add(yield_reg,1,yield_reg); + + // yes, got lock. do we have the same top? + ld(top_ptr_reg_after_save, 0, value_reg); + cmp(value_reg, top_reg_after_save); + br(Assembler::notEqual, false, Assembler::pn, not_same); + delayed()->nop(); + + // yes, same top. + st(ptr_reg_after_save, top_ptr_reg_after_save, 0); + membar(Assembler::StoreStore); + + bind(not_same); + mov(value_reg, ptr_reg_after_save); + st(lock_reg, lock_ptr_reg, 0); // unlock + + restore(); + } +} + +void MacroAssembler::biased_locking_enter(Register obj_reg, Register mark_reg, Register temp_reg, + Label& done, Label* slow_case, + BiasedLockingCounters* counters) { + assert(UseBiasedLocking, "why call this otherwise?"); + + if (PrintBiasedLockingStatistics) { + assert_different_registers(obj_reg, mark_reg, temp_reg, O7); + if (counters == NULL) + counters = BiasedLocking::counters(); + } + + Label cas_label; + + // Biased locking + // See whether the lock is currently biased toward our thread and + // whether the epoch is still valid + // Note that the runtime guarantees sufficient alignment of JavaThread + // pointers to allow age to be placed into low bits + assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, "biased locking makes assumptions about bit layout"); + and3(mark_reg, markOopDesc::biased_lock_mask_in_place, temp_reg); + cmp(temp_reg, markOopDesc::biased_lock_pattern); + brx(Assembler::notEqual, false, Assembler::pn, cas_label); + + delayed()->ld_ptr(Address(obj_reg, 0, oopDesc::klass_offset_in_bytes()), temp_reg); + ld_ptr(Address(temp_reg, 0, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes()), temp_reg); + or3(G2_thread, temp_reg, temp_reg); + xor3(mark_reg, temp_reg, temp_reg); + andcc(temp_reg, ~((int) markOopDesc::age_mask_in_place), temp_reg); + if (counters != NULL) { + cond_inc(Assembler::equal, (address) counters->biased_lock_entry_count_addr(), mark_reg, temp_reg); + // Reload mark_reg as we may need it later + ld_ptr(Address(obj_reg, 0, oopDesc::mark_offset_in_bytes()), mark_reg); + } + brx(Assembler::equal, true, Assembler::pt, done); + delayed()->nop(); + + Label try_revoke_bias; + Label try_rebias; + Address mark_addr = Address(obj_reg, 0, oopDesc::mark_offset_in_bytes()); + assert(mark_addr.disp() == 0, "cas must take a zero displacement"); + + // At this point we know that the header has the bias pattern and + // that we are not the bias owner in the current epoch. We need to + // figure out more details about the state of the header in order to + // know what operations can be legally performed on the object's + // header. + + // If the low three bits in the xor result aren't clear, that means + // the prototype header is no longer biased and we have to revoke + // the bias on this object. + btst(markOopDesc::biased_lock_mask_in_place, temp_reg); + brx(Assembler::notZero, false, Assembler::pn, try_revoke_bias); + + // Biasing is still enabled for this data type. See whether the + // epoch of the current bias is still valid, meaning that the epoch + // bits of the mark word are equal to the epoch bits of the + // prototype header. (Note that the prototype header's epoch bits + // only change at a safepoint.) If not, attempt to rebias the object + // toward the current thread. Note that we must be absolutely sure + // that the current epoch is invalid in order to do this because + // otherwise the manipulations it performs on the mark word are + // illegal. + delayed()->btst(markOopDesc::epoch_mask_in_place, temp_reg); + brx(Assembler::notZero, false, Assembler::pn, try_rebias); + + // The epoch of the current bias is still valid but we know nothing + // about the owner; it might be set or it might be clear. Try to + // acquire the bias of the object using an atomic operation. If this + // fails we will go in to the runtime to revoke the object's bias. + // Note that we first construct the presumed unbiased header so we + // don't accidentally blow away another thread's valid bias. + delayed()->and3(mark_reg, + markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place, + mark_reg); + or3(G2_thread, mark_reg, temp_reg); + casx_under_lock(mark_addr.base(), mark_reg, temp_reg, + (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + // If the biasing toward our thread failed, this means that + // another thread succeeded in biasing it toward itself and we + // need to revoke that bias. The revocation will occur in the + // interpreter runtime in the slow case. + cmp(mark_reg, temp_reg); + if (counters != NULL) { + cond_inc(Assembler::zero, (address) counters->anonymously_biased_lock_entry_count_addr(), mark_reg, temp_reg); + } + if (slow_case != NULL) { + brx(Assembler::notEqual, true, Assembler::pn, *slow_case); + delayed()->nop(); + } + br(Assembler::always, false, Assembler::pt, done); + delayed()->nop(); + + bind(try_rebias); + // At this point we know the epoch has expired, meaning that the + // current "bias owner", if any, is actually invalid. Under these + // circumstances _only_, we are allowed to use the current header's + // value as the comparison value when doing the cas to acquire the + // bias in the current epoch. In other words, we allow transfer of + // the bias from one thread to another directly in this situation. + // + // FIXME: due to a lack of registers we currently blow away the age + // bits in this situation. Should attempt to preserve them. + ld_ptr(Address(obj_reg, 0, oopDesc::klass_offset_in_bytes()), temp_reg); + ld_ptr(Address(temp_reg, 0, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes()), temp_reg); + or3(G2_thread, temp_reg, temp_reg); + casx_under_lock(mark_addr.base(), mark_reg, temp_reg, + (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + // If the biasing toward our thread failed, this means that + // another thread succeeded in biasing it toward itself and we + // need to revoke that bias. The revocation will occur in the + // interpreter runtime in the slow case. + cmp(mark_reg, temp_reg); + if (counters != NULL) { + cond_inc(Assembler::zero, (address) counters->rebiased_lock_entry_count_addr(), mark_reg, temp_reg); + } + if (slow_case != NULL) { + brx(Assembler::notEqual, true, Assembler::pn, *slow_case); + delayed()->nop(); + } + br(Assembler::always, false, Assembler::pt, done); + delayed()->nop(); + + bind(try_revoke_bias); + // The prototype mark in the klass doesn't have the bias bit set any + // more, indicating that objects of this data type are not supposed + // to be biased any more. We are going to try to reset the mark of + // this object to the prototype value and fall through to the + // CAS-based locking scheme. Note that if our CAS fails, it means + // that another thread raced us for the privilege of revoking the + // bias of this particular object, so it's okay to continue in the + // normal locking code. + // + // FIXME: due to a lack of registers we currently blow away the age + // bits in this situation. Should attempt to preserve them. + ld_ptr(Address(obj_reg, 0, oopDesc::klass_offset_in_bytes()), temp_reg); + ld_ptr(Address(temp_reg, 0, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes()), temp_reg); + casx_under_lock(mark_addr.base(), mark_reg, temp_reg, + (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + // Fall through to the normal CAS-based lock, because no matter what + // the result of the above CAS, some thread must have succeeded in + // removing the bias bit from the object's header. + if (counters != NULL) { + cmp(mark_reg, temp_reg); + cond_inc(Assembler::zero, (address) counters->revoked_lock_entry_count_addr(), mark_reg, temp_reg); + } + + bind(cas_label); +} + +void MacroAssembler::biased_locking_exit (Address mark_addr, Register temp_reg, Label& done, + bool allow_delay_slot_filling) { + // Check for biased locking unlock case, which is a no-op + // Note: we do not have to check the thread ID for two reasons. + // First, the interpreter checks for IllegalMonitorStateException at + // a higher level. Second, if the bias was revoked while we held the + // lock, the object could not be rebiased toward another thread, so + // the bias bit would be clear. + ld_ptr(mark_addr, temp_reg); + and3(temp_reg, markOopDesc::biased_lock_mask_in_place, temp_reg); + cmp(temp_reg, markOopDesc::biased_lock_pattern); + brx(Assembler::equal, allow_delay_slot_filling, Assembler::pt, done); + delayed(); + if (!allow_delay_slot_filling) { + nop(); + } +} + + +// CASN -- 32-64 bit switch hitter similar to the synthetic CASN provided by +// Solaris/SPARC's "as". Another apt name would be cas_ptr() + +void MacroAssembler::casn (Register addr_reg, Register cmp_reg, Register set_reg ) { + casx_under_lock (addr_reg, cmp_reg, set_reg, (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()) ; +} + + + +// compiler_lock_object() and compiler_unlock_object() are direct transliterations +// of i486.ad fast_lock() and fast_unlock(). See those methods for detailed comments. +// The code could be tightened up considerably. +// +// box->dhw disposition - post-conditions at DONE_LABEL. +// - Successful inflated lock: box->dhw != 0. +// Any non-zero value suffices. +// Consider G2_thread, rsp, boxReg, or unused_mark() +// - Successful Stack-lock: box->dhw == mark. +// box->dhw must contain the displaced mark word value +// - Failure -- icc.ZFlag == 0 and box->dhw is undefined. +// The slow-path fast_enter() and slow_enter() operators +// are responsible for setting box->dhw = NonZero (typically ::unused_mark). +// - Biased: box->dhw is undefined +// +// SPARC refworkload performance - specifically jetstream and scimark - are +// extremely sensitive to the size of the code emitted by compiler_lock_object +// and compiler_unlock_object. Critically, the key factor is code size, not path +// length. (Simply experiments to pad CLO with unexecuted NOPs demonstrte the +// effect). + + +void MacroAssembler::compiler_lock_object(Register Roop, Register Rmark, Register Rbox, Register Rscratch, + BiasedLockingCounters* counters) { + Address mark_addr(Roop, 0, oopDesc::mark_offset_in_bytes()); + + verify_oop(Roop); + Label done ; + + if (counters != NULL) { + inc_counter((address) counters->total_entry_count_addr(), Rmark, Rscratch); + } + + if (EmitSync & 1) { + mov (3, Rscratch) ; + st_ptr (Rscratch, Rbox, BasicLock::displaced_header_offset_in_bytes()); + cmp (SP, G0) ; + return ; + } + + if (EmitSync & 2) { + + // Fetch object's markword + ld_ptr(mark_addr, Rmark); + + if (UseBiasedLocking) { + biased_locking_enter(Roop, Rmark, Rscratch, done, NULL, counters); + } + + // Save Rbox in Rscratch to be used for the cas operation + mov(Rbox, Rscratch); + + // set Rmark to markOop | markOopDesc::unlocked_value + or3(Rmark, markOopDesc::unlocked_value, Rmark); + + // Initialize the box. (Must happen before we update the object mark!) + st_ptr(Rmark, Rbox, BasicLock::displaced_header_offset_in_bytes()); + + // compare object markOop with Rmark and if equal exchange Rscratch with object markOop + assert(mark_addr.disp() == 0, "cas must take a zero displacement"); + casx_under_lock(mark_addr.base(), Rmark, Rscratch, + (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + + // if compare/exchange succeeded we found an unlocked object and we now have locked it + // hence we are done + cmp(Rmark, Rscratch); +#ifdef _LP64 + sub(Rscratch, STACK_BIAS, Rscratch); +#endif + brx(Assembler::equal, false, Assembler::pt, done); + delayed()->sub(Rscratch, SP, Rscratch); //pull next instruction into delay slot + + // we did not find an unlocked object so see if this is a recursive case + // sub(Rscratch, SP, Rscratch); + assert(os::vm_page_size() > 0xfff, "page size too small - change the constant"); + andcc(Rscratch, 0xfffff003, Rscratch); + st_ptr(Rscratch, Rbox, BasicLock::displaced_header_offset_in_bytes()); + bind (done) ; + return ; + } + + Label Egress ; + + if (EmitSync & 256) { + Label IsInflated ; + + ld_ptr (mark_addr, Rmark); // fetch obj->mark + // Triage: biased, stack-locked, neutral, inflated + if (UseBiasedLocking) { + biased_locking_enter(Roop, Rmark, Rscratch, done, NULL, counters); + // Invariant: if control reaches this point in the emitted stream + // then Rmark has not been modified. + } + + // Store mark into displaced mark field in the on-stack basic-lock "box" + // Critically, this must happen before the CAS + // Maximize the ST-CAS distance to minimize the ST-before-CAS penalty. + st_ptr (Rmark, Rbox, BasicLock::displaced_header_offset_in_bytes()); + andcc (Rmark, 2, G0) ; + brx (Assembler::notZero, false, Assembler::pn, IsInflated) ; + delayed() -> + + // Try stack-lock acquisition. + // Beware: the 1st instruction is in a delay slot + mov (Rbox, Rscratch); + or3 (Rmark, markOopDesc::unlocked_value, Rmark); + assert (mark_addr.disp() == 0, "cas must take a zero displacement"); + casn (mark_addr.base(), Rmark, Rscratch) ; + cmp (Rmark, Rscratch); + brx (Assembler::equal, false, Assembler::pt, done); + delayed()->sub(Rscratch, SP, Rscratch); + + // Stack-lock attempt failed - check for recursive stack-lock. + // See the comments below about how we might remove this case. +#ifdef _LP64 + sub (Rscratch, STACK_BIAS, Rscratch); +#endif + assert(os::vm_page_size() > 0xfff, "page size too small - change the constant"); + andcc (Rscratch, 0xfffff003, Rscratch); + br (Assembler::always, false, Assembler::pt, done) ; + delayed()-> st_ptr (Rscratch, Rbox, BasicLock::displaced_header_offset_in_bytes()); + + bind (IsInflated) ; + if (EmitSync & 64) { + // If m->owner != null goto IsLocked + // Pessimistic form: Test-and-CAS vs CAS + // The optimistic form avoids RTS->RTO cache line upgrades. + ld_ptr (Address (Rmark, 0, ObjectMonitor::owner_offset_in_bytes()-2), Rscratch) ; + andcc (Rscratch, Rscratch, G0) ; + brx (Assembler::notZero, false, Assembler::pn, done) ; + delayed()->nop() ; + // m->owner == null : it's unlocked. + } + + // Try to CAS m->owner from null to Self + // Invariant: if we acquire the lock then _recursions should be 0. + add (Rmark, ObjectMonitor::owner_offset_in_bytes()-2, Rmark) ; + mov (G2_thread, Rscratch) ; + casn (Rmark, G0, Rscratch) ; + cmp (Rscratch, G0) ; + // Intentional fall-through into done + } else { + // Aggressively avoid the Store-before-CAS penalty + // Defer the store into box->dhw until after the CAS + Label IsInflated, Recursive ; + +// Anticipate CAS -- Avoid RTS->RTO upgrade +// prefetch (mark_addr, Assembler::severalWritesAndPossiblyReads) ; + + ld_ptr (mark_addr, Rmark); // fetch obj->mark + // Triage: biased, stack-locked, neutral, inflated + + if (UseBiasedLocking) { + biased_locking_enter(Roop, Rmark, Rscratch, done, NULL, counters); + // Invariant: if control reaches this point in the emitted stream + // then Rmark has not been modified. + } + andcc (Rmark, 2, G0) ; + brx (Assembler::notZero, false, Assembler::pn, IsInflated) ; + delayed()-> // Beware - dangling delay-slot + + // Try stack-lock acquisition. + // Transiently install BUSY (0) encoding in the mark word. + // if the CAS of 0 into the mark was successful then we execute: + // ST box->dhw = mark -- save fetched mark in on-stack basiclock box + // ST obj->mark = box -- overwrite transient 0 value + // This presumes TSO, of course. + + mov (0, Rscratch) ; + or3 (Rmark, markOopDesc::unlocked_value, Rmark); + assert (mark_addr.disp() == 0, "cas must take a zero displacement"); + casn (mark_addr.base(), Rmark, Rscratch) ; +// prefetch (mark_addr, Assembler::severalWritesAndPossiblyReads) ; + cmp (Rscratch, Rmark) ; + brx (Assembler::notZero, false, Assembler::pn, Recursive) ; + delayed() -> + st_ptr (Rmark, Rbox, BasicLock::displaced_header_offset_in_bytes()); + if (counters != NULL) { + cond_inc(Assembler::equal, (address) counters->fast_path_entry_count_addr(), Rmark, Rscratch); + } + br (Assembler::always, false, Assembler::pt, done); + delayed() -> + st_ptr (Rbox, mark_addr) ; + + bind (Recursive) ; + // Stack-lock attempt failed - check for recursive stack-lock. + // Tests show that we can remove the recursive case with no impact + // on refworkload 0.83. If we need to reduce the size of the code + // emitted by compiler_lock_object() the recursive case is perfect + // candidate. + // + // A more extreme idea is to always inflate on stack-lock recursion. + // This lets us eliminate the recursive checks in compiler_lock_object + // and compiler_unlock_object and the (box->dhw == 0) encoding. + // A brief experiment - requiring changes to synchronizer.cpp, interpreter, + // and showed a performance *increase*. In the same experiment I eliminated + // the fast-path stack-lock code from the interpreter and always passed + // control to the "slow" operators in synchronizer.cpp. + + // RScratch contains the fetched obj->mark value from the failed CASN. +#ifdef _LP64 + sub (Rscratch, STACK_BIAS, Rscratch); +#endif + sub(Rscratch, SP, Rscratch); + assert(os::vm_page_size() > 0xfff, "page size too small - change the constant"); + andcc (Rscratch, 0xfffff003, Rscratch); + if (counters != NULL) { + // Accounting needs the Rscratch register + st_ptr (Rscratch, Rbox, BasicLock::displaced_header_offset_in_bytes()); + cond_inc(Assembler::equal, (address) counters->fast_path_entry_count_addr(), Rmark, Rscratch); + br (Assembler::always, false, Assembler::pt, done) ; + delayed()->nop() ; + } else { + br (Assembler::always, false, Assembler::pt, done) ; + delayed()-> st_ptr (Rscratch, Rbox, BasicLock::displaced_header_offset_in_bytes()); + } + + bind (IsInflated) ; + if (EmitSync & 64) { + // If m->owner != null goto IsLocked + // Test-and-CAS vs CAS + // Pessimistic form avoids futile (doomed) CAS attempts + // The optimistic form avoids RTS->RTO cache line upgrades. + ld_ptr (Address (Rmark, 0, ObjectMonitor::owner_offset_in_bytes()-2), Rscratch) ; + andcc (Rscratch, Rscratch, G0) ; + brx (Assembler::notZero, false, Assembler::pn, done) ; + delayed()->nop() ; + // m->owner == null : it's unlocked. + } + + // Try to CAS m->owner from null to Self + // Invariant: if we acquire the lock then _recursions should be 0. + add (Rmark, ObjectMonitor::owner_offset_in_bytes()-2, Rmark) ; + mov (G2_thread, Rscratch) ; + casn (Rmark, G0, Rscratch) ; + cmp (Rscratch, G0) ; + // ST box->displaced_header = NonZero. + // Any non-zero value suffices: + // unused_mark(), G2_thread, RBox, RScratch, rsp, etc. + st_ptr (Rbox, Rbox, BasicLock::displaced_header_offset_in_bytes()); + // Intentional fall-through into done + } + + bind (done) ; +} + +void MacroAssembler::compiler_unlock_object(Register Roop, Register Rmark, Register Rbox, Register Rscratch) { + Address mark_addr(Roop, 0, oopDesc::mark_offset_in_bytes()); + + Label done ; + + if (EmitSync & 4) { + cmp (SP, G0) ; + return ; + } + + if (EmitSync & 8) { + if (UseBiasedLocking) { + biased_locking_exit(mark_addr, Rscratch, done); + } + + // Test first if it is a fast recursive unlock + ld_ptr(Rbox, BasicLock::displaced_header_offset_in_bytes(), Rmark); + cmp(Rmark, G0); + brx(Assembler::equal, false, Assembler::pt, done); + delayed()->nop(); + + // Check if it is still a light weight lock, this is is true if we see + // the stack address of the basicLock in the markOop of the object + assert(mark_addr.disp() == 0, "cas must take a zero displacement"); + casx_under_lock(mark_addr.base(), Rbox, Rmark, + (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + br (Assembler::always, false, Assembler::pt, done); + delayed()->cmp(Rbox, Rmark); + bind (done) ; + return ; + } + + // Beware ... If the aggregate size of the code emitted by CLO and CUO is + // is too large performance rolls abruptly off a cliff. + // This could be related to inlining policies, code cache management, or + // I$ effects. + Label LStacked ; + + if (UseBiasedLocking) { + // TODO: eliminate redundant LDs of obj->mark + biased_locking_exit(mark_addr, Rscratch, done); + } + + ld_ptr (Roop, oopDesc::mark_offset_in_bytes(), Rmark) ; + ld_ptr (Rbox, BasicLock::displaced_header_offset_in_bytes(), Rscratch); + andcc (Rscratch, Rscratch, G0); + brx (Assembler::zero, false, Assembler::pn, done); + delayed()-> nop() ; // consider: relocate fetch of mark, above, into this DS + andcc (Rmark, 2, G0) ; + brx (Assembler::zero, false, Assembler::pt, LStacked) ; + delayed()-> nop() ; + + // It's inflated + // Conceptually we need a #loadstore|#storestore "release" MEMBAR before + // the ST of 0 into _owner which releases the lock. This prevents loads + // and stores within the critical section from reordering (floating) + // past the store that releases the lock. But TSO is a strong memory model + // and that particular flavor of barrier is a noop, so we can safely elide it. + // Note that we use 1-0 locking by default for the inflated case. We + // close the resultant (and rare) race by having contented threads in + // monitorenter periodically poll _owner. + ld_ptr (Address(Rmark, 0, ObjectMonitor::owner_offset_in_bytes()-2), Rscratch) ; + ld_ptr (Address(Rmark, 0, ObjectMonitor::recursions_offset_in_bytes()-2), Rbox) ; + xor3 (Rscratch, G2_thread, Rscratch) ; + orcc (Rbox, Rscratch, Rbox) ; + brx (Assembler::notZero, false, Assembler::pn, done) ; + delayed()-> + ld_ptr (Address (Rmark, 0, ObjectMonitor::EntryList_offset_in_bytes()-2), Rscratch) ; + ld_ptr (Address (Rmark, 0, ObjectMonitor::cxq_offset_in_bytes()-2), Rbox) ; + orcc (Rbox, Rscratch, G0) ; + if (EmitSync & 65536) { + Label LSucc ; + brx (Assembler::notZero, false, Assembler::pn, LSucc) ; + delayed()->nop() ; + br (Assembler::always, false, Assembler::pt, done) ; + delayed()-> + st_ptr (G0, Address (Rmark, 0, ObjectMonitor::owner_offset_in_bytes()-2)) ; + + bind (LSucc) ; + st_ptr (G0, Address (Rmark, 0, ObjectMonitor::owner_offset_in_bytes()-2)) ; + if (os::is_MP()) { membar (StoreLoad) ; } + ld_ptr (Address (Rmark, 0, ObjectMonitor::succ_offset_in_bytes()-2), Rscratch) ; + andcc (Rscratch, Rscratch, G0) ; + brx (Assembler::notZero, false, Assembler::pt, done) ; + delayed()-> andcc (G0, G0, G0) ; + add (Rmark, ObjectMonitor::owner_offset_in_bytes()-2, Rmark) ; + mov (G2_thread, Rscratch) ; + casn (Rmark, G0, Rscratch) ; + cmp (Rscratch, G0) ; + // invert icc.zf and goto done + brx (Assembler::notZero, false, Assembler::pt, done) ; + delayed() -> cmp (G0, G0) ; + br (Assembler::always, false, Assembler::pt, done); + delayed() -> cmp (G0, 1) ; + } else { + brx (Assembler::notZero, false, Assembler::pn, done) ; + delayed()->nop() ; + br (Assembler::always, false, Assembler::pt, done) ; + delayed()-> + st_ptr (G0, Address (Rmark, 0, ObjectMonitor::owner_offset_in_bytes()-2)) ; + } + + bind (LStacked) ; + // Consider: we could replace the expensive CAS in the exit + // path with a simple ST of the displaced mark value fetched from + // the on-stack basiclock box. That admits a race where a thread T2 + // in the slow lock path -- inflating with monitor M -- could race a + // thread T1 in the fast unlock path, resulting in a missed wakeup for T2. + // More precisely T1 in the stack-lock unlock path could "stomp" the + // inflated mark value M installed by T2, resulting in an orphan + // object monitor M and T2 becoming stranded. We can remedy that situation + // by having T2 periodically poll the object's mark word using timed wait + // operations. If T2 discovers that a stomp has occurred it vacates + // the monitor M and wakes any other threads stranded on the now-orphan M. + // In addition the monitor scavenger, which performs deflation, + // would also need to check for orpan monitors and stranded threads. + // + // Finally, inflation is also used when T2 needs to assign a hashCode + // to O and O is stack-locked by T1. The "stomp" race could cause + // an assigned hashCode value to be lost. We can avoid that condition + // and provide the necessary hashCode stability invariants by ensuring + // that hashCode generation is idempotent between copying GCs. + // For example we could compute the hashCode of an object O as + // O's heap address XOR some high quality RNG value that is refreshed + // at GC-time. The monitor scavenger would install the hashCode + // found in any orphan monitors. Again, the mechanism admits a + // lost-update "stomp" WAW race but detects and recovers as needed. + // + // A prototype implementation showed excellent results, although + // the scavenger and timeout code was rather involved. + + casn (mark_addr.base(), Rbox, Rscratch) ; + cmp (Rbox, Rscratch); + // Intentional fall through into done ... + + bind (done) ; +} + + + +void MacroAssembler::print_CPU_state() { + // %%%%% need to implement this +} + +void MacroAssembler::verify_FPU(int stack_depth, const char* s) { + // %%%%% need to implement this +} + +void MacroAssembler::push_IU_state() { + // %%%%% need to implement this +} + + +void MacroAssembler::pop_IU_state() { + // %%%%% need to implement this +} + + +void MacroAssembler::push_FPU_state() { + // %%%%% need to implement this +} + + +void MacroAssembler::pop_FPU_state() { + // %%%%% need to implement this +} + + +void MacroAssembler::push_CPU_state() { + // %%%%% need to implement this +} + + +void MacroAssembler::pop_CPU_state() { + // %%%%% need to implement this +} + + + +void MacroAssembler::verify_tlab() { +#ifdef ASSERT + if (UseTLAB && VerifyOops) { + Label next, next2, ok; + Register t1 = L0; + Register t2 = L1; + Register t3 = L2; + + save_frame(0); + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_top_offset()), t1); + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_start_offset()), t2); + or3(t1, t2, t3); + cmp(t1, t2); + br(Assembler::greaterEqual, false, Assembler::pn, next); + delayed()->nop(); + stop("assert(top >= start)"); + should_not_reach_here(); + + bind(next); + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_top_offset()), t1); + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_end_offset()), t2); + or3(t3, t2, t3); + cmp(t1, t2); + br(Assembler::lessEqual, false, Assembler::pn, next2); + delayed()->nop(); + stop("assert(top <= end)"); + should_not_reach_here(); + + bind(next2); + and3(t3, MinObjAlignmentInBytesMask, t3); + cmp(t3, 0); + br(Assembler::lessEqual, false, Assembler::pn, ok); + delayed()->nop(); + stop("assert(aligned)"); + should_not_reach_here(); + + bind(ok); + restore(); + } +#endif +} + + +void MacroAssembler::eden_allocate( + Register obj, // result: pointer to object after successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Register t2, // temp register + Label& slow_case // continuation point if fast allocation fails +){ + // make sure arguments make sense + assert_different_registers(obj, var_size_in_bytes, t1, t2); + assert(0 <= con_size_in_bytes && Assembler::is_simm13(con_size_in_bytes), "illegal object size"); + assert((con_size_in_bytes & MinObjAlignmentInBytesMask) == 0, "object size is not multiple of alignment"); + + // get eden boundaries + // note: we need both top & top_addr! + const Register top_addr = t1; + const Register end = t2; + + CollectedHeap* ch = Universe::heap(); + set((intx)ch->top_addr(), top_addr); + intx delta = (intx)ch->end_addr() - (intx)ch->top_addr(); + ld_ptr(top_addr, delta, end); + ld_ptr(top_addr, 0, obj); + + // try to allocate + Label retry; + bind(retry); +#ifdef ASSERT + // make sure eden top is properly aligned + { + Label L; + btst(MinObjAlignmentInBytesMask, obj); + br(Assembler::zero, false, Assembler::pt, L); + delayed()->nop(); + stop("eden top is not properly aligned"); + bind(L); + } +#endif // ASSERT + const Register free = end; + sub(end, obj, free); // compute amount of free space + if (var_size_in_bytes->is_valid()) { + // size is unknown at compile time + cmp(free, var_size_in_bytes); + br(Assembler::lessUnsigned, false, Assembler::pn, slow_case); // if there is not enough space go the slow case + delayed()->add(obj, var_size_in_bytes, end); + } else { + // size is known at compile time + cmp(free, con_size_in_bytes); + br(Assembler::lessUnsigned, false, Assembler::pn, slow_case); // if there is not enough space go the slow case + delayed()->add(obj, con_size_in_bytes, end); + } + // Compare obj with the value at top_addr; if still equal, swap the value of + // end with the value at top_addr. If not equal, read the value at top_addr + // into end. + casx_under_lock(top_addr, obj, end, (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + // if someone beat us on the allocation, try again, otherwise continue + cmp(obj, end); + brx(Assembler::notEqual, false, Assembler::pn, retry); + delayed()->mov(end, obj); // nop if successfull since obj == end + +#ifdef ASSERT + // make sure eden top is properly aligned + { + Label L; + const Register top_addr = t1; + + set((intx)ch->top_addr(), top_addr); + ld_ptr(top_addr, 0, top_addr); + btst(MinObjAlignmentInBytesMask, top_addr); + br(Assembler::zero, false, Assembler::pt, L); + delayed()->nop(); + stop("eden top is not properly aligned"); + bind(L); + } +#endif // ASSERT +} + + +void MacroAssembler::tlab_allocate( + Register obj, // result: pointer to object after successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Label& slow_case // continuation point if fast allocation fails +){ + // make sure arguments make sense + assert_different_registers(obj, var_size_in_bytes, t1); + assert(0 <= con_size_in_bytes && is_simm13(con_size_in_bytes), "illegal object size"); + assert((con_size_in_bytes & MinObjAlignmentInBytesMask) == 0, "object size is not multiple of alignment"); + + const Register free = t1; + + verify_tlab(); + + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_top_offset()), obj); + + // calculate amount of free space + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_end_offset()), free); + sub(free, obj, free); + + Label done; + if (var_size_in_bytes == noreg) { + cmp(free, con_size_in_bytes); + } else { + cmp(free, var_size_in_bytes); + } + br(Assembler::less, false, Assembler::pn, slow_case); + // calculate the new top pointer + if (var_size_in_bytes == noreg) { + delayed()->add(obj, con_size_in_bytes, free); + } else { + delayed()->add(obj, var_size_in_bytes, free); + } + + bind(done); + +#ifdef ASSERT + // make sure new free pointer is properly aligned + { + Label L; + btst(MinObjAlignmentInBytesMask, free); + br(Assembler::zero, false, Assembler::pt, L); + delayed()->nop(); + stop("updated TLAB free is not properly aligned"); + bind(L); + } +#endif // ASSERT + + // update the tlab top pointer + st_ptr(free, G2_thread, in_bytes(JavaThread::tlab_top_offset())); + verify_tlab(); +} + + +void MacroAssembler::tlab_refill(Label& retry, Label& try_eden, Label& slow_case) { + Register top = O0; + Register t1 = G1; + Register t2 = G3; + Register t3 = O1; + assert_different_registers(top, t1, t2, t3, G4, G5 /* preserve G4 and G5 */); + Label do_refill, discard_tlab; + + if (CMSIncrementalMode || !Universe::heap()->supports_inline_contig_alloc()) { + // No allocation in the shared eden. + br(Assembler::always, false, Assembler::pt, slow_case); + delayed()->nop(); + } + + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_top_offset()), top); + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_end_offset()), t1); + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_refill_waste_limit_offset()), t2); + + // calculate amount of free space + sub(t1, top, t1); + srl_ptr(t1, LogHeapWordSize, t1); + + // Retain tlab and allocate object in shared space if + // the amount free in the tlab is too large to discard. + cmp(t1, t2); + brx(Assembler::lessEqual, false, Assembler::pt, discard_tlab); + + // increment waste limit to prevent getting stuck on this slow path + delayed()->add(t2, ThreadLocalAllocBuffer::refill_waste_limit_increment(), t2); + st_ptr(t2, G2_thread, in_bytes(JavaThread::tlab_refill_waste_limit_offset())); + if (TLABStats) { + // increment number of slow_allocations + ld(G2_thread, in_bytes(JavaThread::tlab_slow_allocations_offset()), t2); + add(t2, 1, t2); + stw(t2, G2_thread, in_bytes(JavaThread::tlab_slow_allocations_offset())); + } + br(Assembler::always, false, Assembler::pt, try_eden); + delayed()->nop(); + + bind(discard_tlab); + if (TLABStats) { + // increment number of refills + ld(G2_thread, in_bytes(JavaThread::tlab_number_of_refills_offset()), t2); + add(t2, 1, t2); + stw(t2, G2_thread, in_bytes(JavaThread::tlab_number_of_refills_offset())); + // accumulate wastage + ld(G2_thread, in_bytes(JavaThread::tlab_fast_refill_waste_offset()), t2); + add(t2, t1, t2); + stw(t2, G2_thread, in_bytes(JavaThread::tlab_fast_refill_waste_offset())); + } + + // if tlab is currently allocated (top or end != null) then + // fill [top, end + alignment_reserve) with array object + br_null(top, false, Assembler::pn, do_refill); + delayed()->nop(); + + set((intptr_t)markOopDesc::prototype()->copy_set_hash(0x2), t2); + st_ptr(t2, top, oopDesc::mark_offset_in_bytes()); // set up the mark word + // set klass to intArrayKlass + set((intptr_t)Universe::intArrayKlassObj_addr(), t2); + ld_ptr(t2, 0, t2); + st_ptr(t2, top, oopDesc::klass_offset_in_bytes()); + sub(t1, typeArrayOopDesc::header_size(T_INT), t1); + add(t1, ThreadLocalAllocBuffer::alignment_reserve(), t1); + sll_ptr(t1, log2_intptr(HeapWordSize/sizeof(jint)), t1); + st(t1, top, arrayOopDesc::length_offset_in_bytes()); + verify_oop(top); + + // refill the tlab with an eden allocation + bind(do_refill); + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_size_offset()), t1); + sll_ptr(t1, LogHeapWordSize, t1); + // add object_size ?? + eden_allocate(top, t1, 0, t2, t3, slow_case); + + st_ptr(top, G2_thread, in_bytes(JavaThread::tlab_start_offset())); + st_ptr(top, G2_thread, in_bytes(JavaThread::tlab_top_offset())); +#ifdef ASSERT + // check that tlab_size (t1) is still valid + { + Label ok; + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_size_offset()), t2); + sll_ptr(t2, LogHeapWordSize, t2); + cmp(t1, t2); + br(Assembler::equal, false, Assembler::pt, ok); + delayed()->nop(); + stop("assert(t1 == tlab_size)"); + should_not_reach_here(); + + bind(ok); + } +#endif // ASSERT + add(top, t1, top); // t1 is tlab_size + sub(top, ThreadLocalAllocBuffer::alignment_reserve_in_bytes(), top); + st_ptr(top, G2_thread, in_bytes(JavaThread::tlab_end_offset())); + verify_tlab(); + br(Assembler::always, false, Assembler::pt, retry); + delayed()->nop(); +} + +Assembler::Condition MacroAssembler::negate_condition(Assembler::Condition cond) { + switch (cond) { + // Note some conditions are synonyms for others + case Assembler::never: return Assembler::always; + case Assembler::zero: return Assembler::notZero; + case Assembler::lessEqual: return Assembler::greater; + case Assembler::less: return Assembler::greaterEqual; + case Assembler::lessEqualUnsigned: return Assembler::greaterUnsigned; + case Assembler::lessUnsigned: return Assembler::greaterEqualUnsigned; + case Assembler::negative: return Assembler::positive; + case Assembler::overflowSet: return Assembler::overflowClear; + case Assembler::always: return Assembler::never; + case Assembler::notZero: return Assembler::zero; + case Assembler::greater: return Assembler::lessEqual; + case Assembler::greaterEqual: return Assembler::less; + case Assembler::greaterUnsigned: return Assembler::lessEqualUnsigned; + case Assembler::greaterEqualUnsigned: return Assembler::lessUnsigned; + case Assembler::positive: return Assembler::negative; + case Assembler::overflowClear: return Assembler::overflowSet; + } + + ShouldNotReachHere(); return Assembler::overflowClear; +} + +void MacroAssembler::cond_inc(Assembler::Condition cond, address counter_ptr, + Register Rtmp1, Register Rtmp2 /*, Register Rtmp3, Register Rtmp4 */) { + Condition negated_cond = negate_condition(cond); + Label L; + brx(negated_cond, false, Assembler::pt, L); + delayed()->nop(); + inc_counter(counter_ptr, Rtmp1, Rtmp2); + bind(L); +} + +void MacroAssembler::inc_counter(address counter_ptr, Register Rtmp1, Register Rtmp2) { + Address counter_addr(Rtmp1, counter_ptr); + load_contents(counter_addr, Rtmp2); + inc(Rtmp2); + store_contents(Rtmp2, counter_addr); +} + +SkipIfEqual::SkipIfEqual( + MacroAssembler* masm, Register temp, const bool* flag_addr, + Assembler::Condition condition) { + _masm = masm; + Address flag(temp, (address)flag_addr, relocInfo::none); + _masm->sethi(flag); + _masm->ldub(flag, temp); + _masm->tst(temp); + _masm->br(condition, false, Assembler::pt, _label); + _masm->delayed()->nop(); +} + +SkipIfEqual::~SkipIfEqual() { + _masm->bind(_label); +} + + +// Writes to stack successive pages until offset reached to check for +// stack overflow + shadow pages. This clobbers tsp and scratch. +void MacroAssembler::bang_stack_size(Register Rsize, Register Rtsp, + Register Rscratch) { + // Use stack pointer in temp stack pointer + mov(SP, Rtsp); + + // Bang stack for total size given plus stack shadow page size. + // Bang one page at a time because a large size can overflow yellow and + // red zones (the bang will fail but stack overflow handling can't tell that + // it was a stack overflow bang vs a regular segv). + int offset = os::vm_page_size(); + Register Roffset = Rscratch; + + Label loop; + bind(loop); + set((-offset)+STACK_BIAS, Rscratch); + st(G0, Rtsp, Rscratch); + set(offset, Roffset); + sub(Rsize, Roffset, Rsize); + cmp(Rsize, G0); + br(Assembler::greater, false, Assembler::pn, loop); + delayed()->sub(Rtsp, Roffset, Rtsp); + + // Bang down shadow pages too. + // The -1 because we already subtracted 1 page. + for (int i = 0; i< StackShadowPages-1; i++) { + set((-i*offset)+STACK_BIAS, Rscratch); + st(G0, Rtsp, Rscratch); + } +} diff --git a/hotspot/src/cpu/sparc/vm/assembler_sparc.hpp b/hotspot/src/cpu/sparc/vm/assembler_sparc.hpp new file mode 100644 index 00000000000..15ec5f5a199 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/assembler_sparc.hpp @@ -0,0 +1,2254 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class BiasedLockingCounters; + +// promises that the system will not use traps 16-31 +#define ST_RESERVED_FOR_USER_0 0x10 + +/* Written: David Ungar 4/19/97 */ + +// Contains all the definitions needed for sparc assembly code generation. + +// Register aliases for parts of the system: + +// 64 bit values can be kept in g1-g5, o1-o5 and o7 and all 64 bits are safe +// across context switches in V8+ ABI. Of course, there are no 64 bit regs +// in V8 ABI. All 64 bits are preserved in V9 ABI for all registers. + +// g2-g4 are scratch registers called "application globals". Their +// meaning is reserved to the "compilation system"--which means us! +// They are are not supposed to be touched by ordinary C code, although +// highly-optimized C code might steal them for temps. They are safe +// across thread switches, and the ABI requires that they be safe +// across function calls. +// +// g1 and g3 are touched by more modules. V8 allows g1 to be clobbered +// across func calls, and V8+ also allows g5 to be clobbered across +// func calls. Also, g1 and g5 can get touched while doing shared +// library loading. +// +// We must not touch g7 (it is the thread-self register) and g6 is +// reserved for certain tools. g0, of course, is always zero. +// +// (Sources: SunSoft Compilers Group, thread library engineers.) + +// %%%% The interpreter should be revisited to reduce global scratch regs. + +// This global always holds the current JavaThread pointer: + +REGISTER_DECLARATION(Register, G2_thread , G2); + +// The following globals are part of the Java calling convention: + +REGISTER_DECLARATION(Register, G5_method , G5); +REGISTER_DECLARATION(Register, G5_megamorphic_method , G5_method); +REGISTER_DECLARATION(Register, G5_inline_cache_reg , G5_method); + +// The following globals are used for the new C1 & interpreter calling convention: +REGISTER_DECLARATION(Register, Gargs , G4); // pointing to the last argument + +// This local is used to preserve G2_thread in the interpreter and in stubs: +REGISTER_DECLARATION(Register, L7_thread_cache , L7); + +// These globals are used as scratch registers in the interpreter: + +REGISTER_DECLARATION(Register, Gframe_size , G1); // SAME REG as G1_scratch +REGISTER_DECLARATION(Register, G1_scratch , G1); // also SAME +REGISTER_DECLARATION(Register, G3_scratch , G3); +REGISTER_DECLARATION(Register, G4_scratch , G4); + +// These globals are used as short-lived scratch registers in the compiler: + +REGISTER_DECLARATION(Register, Gtemp , G5); + +// The compiler requires that G5_megamorphic_method is G5_inline_cache_klass, +// because a single patchable "set" instruction (NativeMovConstReg, +// or NativeMovConstPatching for compiler1) instruction +// serves to set up either quantity, depending on whether the compiled +// call site is an inline cache or is megamorphic. See the function +// CompiledIC::set_to_megamorphic. +// +// On the other hand, G5_inline_cache_klass must differ from G5_method, +// because both registers are needed for an inline cache that calls +// an interpreted method. +// +// Note that G5_method is only the method-self for the interpreter, +// and is logically unrelated to G5_megamorphic_method. +// +// Invariants on G2_thread (the JavaThread pointer): +// - it should not be used for any other purpose anywhere +// - it must be re-initialized by StubRoutines::call_stub() +// - it must be preserved around every use of call_VM + +// We can consider using g2/g3/g4 to cache more values than the +// JavaThread, such as the card-marking base or perhaps pointers into +// Eden. It's something of a waste to use them as scratch temporaries, +// since they are not supposed to be volatile. (Of course, if we find +// that Java doesn't benefit from application globals, then we can just +// use them as ordinary temporaries.) +// +// Since g1 and g5 (and/or g6) are the volatile (caller-save) registers, +// it makes sense to use them routinely for procedure linkage, +// whenever the On registers are not applicable. Examples: G5_method, +// G5_inline_cache_klass, and a double handful of miscellaneous compiler +// stubs. This means that compiler stubs, etc., should be kept to a +// maximum of two or three G-register arguments. + + +// stub frames + +REGISTER_DECLARATION(Register, Lentry_args , L0); // pointer to args passed to callee (interpreter) not stub itself + +// Interpreter frames + +#ifdef CC_INTERP +REGISTER_DECLARATION(Register, Lstate , L0); // interpreter state object pointer +REGISTER_DECLARATION(Register, L1_scratch , L1); // scratch +REGISTER_DECLARATION(Register, Lmirror , L1); // mirror (for native methods only) +REGISTER_DECLARATION(Register, L2_scratch , L2); +REGISTER_DECLARATION(Register, L3_scratch , L3); +REGISTER_DECLARATION(Register, L4_scratch , L4); +REGISTER_DECLARATION(Register, Lscratch , L5); // C1 uses +REGISTER_DECLARATION(Register, Lscratch2 , L6); // C1 uses +REGISTER_DECLARATION(Register, L7_scratch , L7); // constant pool cache +REGISTER_DECLARATION(Register, O5_savedSP , O5); +REGISTER_DECLARATION(Register, I5_savedSP , I5); // Saved SP before bumping for locals. This is simply + // a copy SP, so in 64-bit it's a biased value. The bias + // is added and removed as needed in the frame code. +// Interface to signature handler +REGISTER_DECLARATION(Register, Llocals , L7); // pointer to locals for signature handler +REGISTER_DECLARATION(Register, Lmethod , L6); // methodOop when calling signature handler + +#else +REGISTER_DECLARATION(Register, Lesp , L0); // expression stack pointer +REGISTER_DECLARATION(Register, Lbcp , L1); // pointer to next bytecode +REGISTER_DECLARATION(Register, Lmethod , L2); +REGISTER_DECLARATION(Register, Llocals , L3); +REGISTER_DECLARATION(Register, Largs , L3); // pointer to locals for signature handler + // must match Llocals in asm interpreter +REGISTER_DECLARATION(Register, Lmonitors , L4); +REGISTER_DECLARATION(Register, Lbyte_code , L5); +// When calling out from the interpreter we record SP so that we can remove any extra stack +// space allocated during adapter transitions. This register is only live from the point +// of the call until we return. +REGISTER_DECLARATION(Register, Llast_SP , L5); +REGISTER_DECLARATION(Register, Lscratch , L5); +REGISTER_DECLARATION(Register, Lscratch2 , L6); +REGISTER_DECLARATION(Register, LcpoolCache , L6); // constant pool cache + +REGISTER_DECLARATION(Register, O5_savedSP , O5); +REGISTER_DECLARATION(Register, I5_savedSP , I5); // Saved SP before bumping for locals. This is simply + // a copy SP, so in 64-bit it's a biased value. The bias + // is added and removed as needed in the frame code. +REGISTER_DECLARATION(Register, IdispatchTables , I4); // Base address of the bytecode dispatch tables +REGISTER_DECLARATION(Register, IdispatchAddress , I3); // Register which saves the dispatch address for each bytecode +REGISTER_DECLARATION(Register, ImethodDataPtr , I2); // Pointer to the current method data +#endif /* CC_INTERP */ + +// NOTE: Lscratch2 and LcpoolCache point to the same registers in +// the interpreter code. If Lscratch2 needs to be used for some +// purpose than LcpoolCache should be restore after that for +// the interpreter to work right +// (These assignments must be compatible with L7_thread_cache; see above.) + +// Since Lbcp points into the middle of the method object, +// it is temporarily converted into a "bcx" during GC. + +// Exception processing +// These registers are passed into exception handlers. +// All exception handlers require the exception object being thrown. +// In addition, an nmethod's exception handler must be passed +// the address of the call site within the nmethod, to allow +// proper selection of the applicable catch block. +// (Interpreter frames use their own bcp() for this purpose.) +// +// The Oissuing_pc value is not always needed. When jumping to a +// handler that is known to be interpreted, the Oissuing_pc value can be +// omitted. An actual catch block in compiled code receives (from its +// nmethod's exception handler) the thrown exception in the Oexception, +// but it doesn't need the Oissuing_pc. +// +// If an exception handler (either interpreted or compiled) +// discovers there is no applicable catch block, it updates +// the Oissuing_pc to the continuation PC of its own caller, +// pops back to that caller's stack frame, and executes that +// caller's exception handler. Obviously, this process will +// iterate until the control stack is popped back to a method +// containing an applicable catch block. A key invariant is +// that the Oissuing_pc value is always a value local to +// the method whose exception handler is currently executing. +// +// Note: The issuing PC value is __not__ a raw return address (I7 value). +// It is a "return pc", the address __following__ the call. +// Raw return addresses are converted to issuing PCs by frame::pc(), +// or by stubs. Issuing PCs can be used directly with PC range tables. +// +REGISTER_DECLARATION(Register, Oexception , O0); // exception being thrown +REGISTER_DECLARATION(Register, Oissuing_pc , O1); // where the exception is coming from + + +// These must occur after the declarations above +#ifndef DONT_USE_REGISTER_DEFINES + +#define Gthread AS_REGISTER(Register, Gthread) +#define Gmethod AS_REGISTER(Register, Gmethod) +#define Gmegamorphic_method AS_REGISTER(Register, Gmegamorphic_method) +#define Ginline_cache_reg AS_REGISTER(Register, Ginline_cache_reg) +#define Gargs AS_REGISTER(Register, Gargs) +#define Lthread_cache AS_REGISTER(Register, Lthread_cache) +#define Gframe_size AS_REGISTER(Register, Gframe_size) +#define Gtemp AS_REGISTER(Register, Gtemp) + +#ifdef CC_INTERP +#define Lstate AS_REGISTER(Register, Lstate) +#define Lesp AS_REGISTER(Register, Lesp) +#define L1_scratch AS_REGISTER(Register, L1_scratch) +#define Lmirror AS_REGISTER(Register, Lmirror) +#define L2_scratch AS_REGISTER(Register, L2_scratch) +#define L3_scratch AS_REGISTER(Register, L3_scratch) +#define L4_scratch AS_REGISTER(Register, L4_scratch) +#define Lscratch AS_REGISTER(Register, Lscratch) +#define Lscratch2 AS_REGISTER(Register, Lscratch2) +#define L7_scratch AS_REGISTER(Register, L7_scratch) +#define Ostate AS_REGISTER(Register, Ostate) +#else +#define Lesp AS_REGISTER(Register, Lesp) +#define Lbcp AS_REGISTER(Register, Lbcp) +#define Lmethod AS_REGISTER(Register, Lmethod) +#define Llocals AS_REGISTER(Register, Llocals) +#define Lmonitors AS_REGISTER(Register, Lmonitors) +#define Lbyte_code AS_REGISTER(Register, Lbyte_code) +#define Lscratch AS_REGISTER(Register, Lscratch) +#define Lscratch2 AS_REGISTER(Register, Lscratch2) +#define LcpoolCache AS_REGISTER(Register, LcpoolCache) +#endif /* ! CC_INTERP */ + +#define Lentry_args AS_REGISTER(Register, Lentry_args) +#define I5_savedSP AS_REGISTER(Register, I5_savedSP) +#define O5_savedSP AS_REGISTER(Register, O5_savedSP) +#define IdispatchAddress AS_REGISTER(Register, IdispatchAddress) +#define ImethodDataPtr AS_REGISTER(Register, ImethodDataPtr) +#define IdispatchTables AS_REGISTER(Register, IdispatchTables) + +#define Oexception AS_REGISTER(Register, Oexception) +#define Oissuing_pc AS_REGISTER(Register, Oissuing_pc) + + +#endif + +// Address is an abstraction used to represent a memory location. +// +// Note: A register location is represented via a Register, not +// via an address for efficiency & simplicity reasons. + +class Address VALUE_OBJ_CLASS_SPEC { + private: + Register _base; +#ifdef _LP64 + int _hi32; // bits 63::32 + int _low32; // bits 31::0 +#endif + int _hi; + int _disp; + RelocationHolder _rspec; + + RelocationHolder rspec_from_rtype(relocInfo::relocType rt, address a = NULL) { + switch (rt) { + case relocInfo::external_word_type: + return external_word_Relocation::spec(a); + case relocInfo::internal_word_type: + return internal_word_Relocation::spec(a); +#ifdef _LP64 + case relocInfo::opt_virtual_call_type: + return opt_virtual_call_Relocation::spec(); + case relocInfo::static_call_type: + return static_call_Relocation::spec(); + case relocInfo::runtime_call_type: + return runtime_call_Relocation::spec(); +#endif + case relocInfo::none: + return RelocationHolder(); + default: + ShouldNotReachHere(); + return RelocationHolder(); + } + } + + public: + Address(Register b, address a, relocInfo::relocType rt = relocInfo::none) + : _rspec(rspec_from_rtype(rt, a)) + { + _base = b; +#ifdef _LP64 + _hi32 = (intptr_t)a >> 32; // top 32 bits in 64 bit word + _low32 = (intptr_t)a & ~0; // low 32 bits in 64 bit word +#endif + _hi = (intptr_t)a & ~0x3ff; // top 22 bits in low word + _disp = (intptr_t)a & 0x3ff; // bottom 10 bits + } + + Address(Register b, address a, RelocationHolder const& rspec) + : _rspec(rspec) + { + _base = b; +#ifdef _LP64 + _hi32 = (intptr_t)a >> 32; // top 32 bits in 64 bit word + _low32 = (intptr_t)a & ~0; // low 32 bits in 64 bit word +#endif + _hi = (intptr_t)a & ~0x3ff; // top 22 bits + _disp = (intptr_t)a & 0x3ff; // bottom 10 bits + } + + Address(Register b, intptr_t h, intptr_t d, RelocationHolder const& rspec = RelocationHolder()) + : _rspec(rspec) + { + _base = b; +#ifdef _LP64 +// [RGV] Put in Assert to force me to check usage of this constructor + assert( h == 0, "Check usage of this constructor" ); + _hi32 = h; + _low32 = d; + _hi = h; + _disp = d; +#else + _hi = h; + _disp = d; +#endif + } + + Address() + : _rspec(RelocationHolder()) + { + _base = G0; +#ifdef _LP64 + _hi32 = 0; + _low32 = 0; +#endif + _hi = 0; + _disp = 0; + } + + // fancier constructors + + enum addr_type { + extra_in_argument, // in the In registers + extra_out_argument // in the Outs + }; + + Address( addr_type, int ); + + // accessors + + Register base() const { return _base; } +#ifdef _LP64 + int hi32() const { return _hi32; } + int low32() const { return _low32; } +#endif + int hi() const { return _hi; } + int disp() const { return _disp; } +#ifdef _LP64 + intptr_t value() const { return ((intptr_t)_hi32 << 32) | + (intptr_t)(uint32_t)_low32; } +#else + int value() const { return _hi | _disp; } +#endif + const relocInfo::relocType rtype() { return _rspec.type(); } + const RelocationHolder& rspec() { return _rspec; } + + RelocationHolder rspec(int offset) const { + return offset == 0 ? _rspec : _rspec.plus(offset); + } + + inline bool is_simm13(int offset = 0); // check disp+offset for overflow + + Address split_disp() const { // deal with disp overflow + Address a = (*this); + int hi_disp = _disp & ~0x3ff; + if (hi_disp != 0) { + a._disp -= hi_disp; + a._hi += hi_disp; + } + return a; + } + + Address after_save() const { + Address a = (*this); + a._base = a._base->after_save(); + return a; + } + + Address after_restore() const { + Address a = (*this); + a._base = a._base->after_restore(); + return a; + } + + friend class Assembler; +}; + + +inline Address RegisterImpl::address_in_saved_window() const { + return (Address(SP, 0, (sp_offset_in_saved_window() * wordSize) + STACK_BIAS)); +} + + + +// Argument is an abstraction used to represent an outgoing +// actual argument or an incoming formal parameter, whether +// it resides in memory or in a register, in a manner consistent +// with the SPARC Application Binary Interface, or ABI. This is +// often referred to as the native or C calling convention. + +class Argument VALUE_OBJ_CLASS_SPEC { + private: + int _number; + bool _is_in; + + public: +#ifdef _LP64 + enum { + n_register_parameters = 6, // only 6 registers may contain integer parameters + n_float_register_parameters = 16 // Can have up to 16 floating registers + }; +#else + enum { + n_register_parameters = 6 // only 6 registers may contain integer parameters + }; +#endif + + // creation + Argument(int number, bool is_in) : _number(number), _is_in(is_in) {} + + int number() const { return _number; } + bool is_in() const { return _is_in; } + bool is_out() const { return !is_in(); } + + Argument successor() const { return Argument(number() + 1, is_in()); } + Argument as_in() const { return Argument(number(), true ); } + Argument as_out() const { return Argument(number(), false); } + + // locating register-based arguments: + bool is_register() const { return _number < n_register_parameters; } + +#ifdef _LP64 + // locating Floating Point register-based arguments: + bool is_float_register() const { return _number < n_float_register_parameters; } + + FloatRegister as_float_register() const { + assert(is_float_register(), "must be a register argument"); + return as_FloatRegister(( number() *2 ) + 1); + } + FloatRegister as_double_register() const { + assert(is_float_register(), "must be a register argument"); + return as_FloatRegister(( number() *2 )); + } +#endif + + Register as_register() const { + assert(is_register(), "must be a register argument"); + return is_in() ? as_iRegister(number()) : as_oRegister(number()); + } + + // locating memory-based arguments + Address as_address() const { + assert(!is_register(), "must be a memory argument"); + return address_in_frame(); + } + + // When applied to a register-based argument, give the corresponding address + // into the 6-word area "into which callee may store register arguments" + // (This is a different place than the corresponding register-save area location.) + Address address_in_frame() const { + return Address( is_in() ? Address::extra_in_argument + : Address::extra_out_argument, + _number ); + } + + // debugging + const char* name() const; + + friend class Assembler; +}; + + +// The SPARC Assembler: Pure assembler doing NO optimizations on the instruction +// level; i.e., what you write +// is what you get. The Assembler is generating code into a CodeBuffer. + +class Assembler : public AbstractAssembler { + protected: + + static void print_instruction(int inst); + static int patched_branch(int dest_pos, int inst, int inst_pos); + static int branch_destination(int inst, int pos); + + + friend class AbstractAssembler; + + // code patchers need various routines like inv_wdisp() + friend class NativeInstruction; + friend class NativeGeneralJump; + friend class Relocation; + friend class Label; + + public: + // op carries format info; see page 62 & 267 + + enum ops { + call_op = 1, // fmt 1 + branch_op = 0, // also sethi (fmt2) + arith_op = 2, // fmt 3, arith & misc + ldst_op = 3 // fmt 3, load/store + }; + + enum op2s { + bpr_op2 = 3, + fb_op2 = 6, + fbp_op2 = 5, + br_op2 = 2, + bp_op2 = 1, + cb_op2 = 7, // V8 + sethi_op2 = 4 + }; + + enum op3s { + // selected op3s + add_op3 = 0x00, + and_op3 = 0x01, + or_op3 = 0x02, + xor_op3 = 0x03, + sub_op3 = 0x04, + andn_op3 = 0x05, + orn_op3 = 0x06, + xnor_op3 = 0x07, + addc_op3 = 0x08, + mulx_op3 = 0x09, + umul_op3 = 0x0a, + smul_op3 = 0x0b, + subc_op3 = 0x0c, + udivx_op3 = 0x0d, + udiv_op3 = 0x0e, + sdiv_op3 = 0x0f, + + addcc_op3 = 0x10, + andcc_op3 = 0x11, + orcc_op3 = 0x12, + xorcc_op3 = 0x13, + subcc_op3 = 0x14, + andncc_op3 = 0x15, + orncc_op3 = 0x16, + xnorcc_op3 = 0x17, + addccc_op3 = 0x18, + umulcc_op3 = 0x1a, + smulcc_op3 = 0x1b, + subccc_op3 = 0x1c, + udivcc_op3 = 0x1e, + sdivcc_op3 = 0x1f, + + taddcc_op3 = 0x20, + tsubcc_op3 = 0x21, + taddcctv_op3 = 0x22, + tsubcctv_op3 = 0x23, + mulscc_op3 = 0x24, + sll_op3 = 0x25, + sllx_op3 = 0x25, + srl_op3 = 0x26, + srlx_op3 = 0x26, + sra_op3 = 0x27, + srax_op3 = 0x27, + rdreg_op3 = 0x28, + membar_op3 = 0x28, + + flushw_op3 = 0x2b, + movcc_op3 = 0x2c, + sdivx_op3 = 0x2d, + popc_op3 = 0x2e, + movr_op3 = 0x2f, + + sir_op3 = 0x30, + wrreg_op3 = 0x30, + saved_op3 = 0x31, + + fpop1_op3 = 0x34, + fpop2_op3 = 0x35, + impdep1_op3 = 0x36, + impdep2_op3 = 0x37, + jmpl_op3 = 0x38, + rett_op3 = 0x39, + trap_op3 = 0x3a, + flush_op3 = 0x3b, + save_op3 = 0x3c, + restore_op3 = 0x3d, + done_op3 = 0x3e, + retry_op3 = 0x3e, + + lduw_op3 = 0x00, + ldub_op3 = 0x01, + lduh_op3 = 0x02, + ldd_op3 = 0x03, + stw_op3 = 0x04, + stb_op3 = 0x05, + sth_op3 = 0x06, + std_op3 = 0x07, + ldsw_op3 = 0x08, + ldsb_op3 = 0x09, + ldsh_op3 = 0x0a, + ldx_op3 = 0x0b, + + ldstub_op3 = 0x0d, + stx_op3 = 0x0e, + swap_op3 = 0x0f, + + lduwa_op3 = 0x10, + ldxa_op3 = 0x1b, + + stwa_op3 = 0x14, + stxa_op3 = 0x1e, + + ldf_op3 = 0x20, + ldfsr_op3 = 0x21, + ldqf_op3 = 0x22, + lddf_op3 = 0x23, + stf_op3 = 0x24, + stfsr_op3 = 0x25, + stqf_op3 = 0x26, + stdf_op3 = 0x27, + + prefetch_op3 = 0x2d, + + + ldc_op3 = 0x30, + ldcsr_op3 = 0x31, + lddc_op3 = 0x33, + stc_op3 = 0x34, + stcsr_op3 = 0x35, + stdcq_op3 = 0x36, + stdc_op3 = 0x37, + + casa_op3 = 0x3c, + casxa_op3 = 0x3e, + + alt_bit_op3 = 0x10, + cc_bit_op3 = 0x10 + }; + + enum opfs { + // selected opfs + fmovs_opf = 0x01, + fmovd_opf = 0x02, + + fnegs_opf = 0x05, + fnegd_opf = 0x06, + + fadds_opf = 0x41, + faddd_opf = 0x42, + fsubs_opf = 0x45, + fsubd_opf = 0x46, + + fmuls_opf = 0x49, + fmuld_opf = 0x4a, + fdivs_opf = 0x4d, + fdivd_opf = 0x4e, + + fcmps_opf = 0x51, + fcmpd_opf = 0x52, + + fstox_opf = 0x81, + fdtox_opf = 0x82, + fxtos_opf = 0x84, + fxtod_opf = 0x88, + fitos_opf = 0xc4, + fdtos_opf = 0xc6, + fitod_opf = 0xc8, + fstod_opf = 0xc9, + fstoi_opf = 0xd1, + fdtoi_opf = 0xd2 + }; + + enum RCondition { rc_z = 1, rc_lez = 2, rc_lz = 3, rc_nz = 5, rc_gz = 6, rc_gez = 7 }; + + enum Condition { + // for FBfcc & FBPfcc instruction + f_never = 0, + f_notEqual = 1, + f_notZero = 1, + f_lessOrGreater = 2, + f_unorderedOrLess = 3, + f_less = 4, + f_unorderedOrGreater = 5, + f_greater = 6, + f_unordered = 7, + f_always = 8, + f_equal = 9, + f_zero = 9, + f_unorderedOrEqual = 10, + f_greaterOrEqual = 11, + f_unorderedOrGreaterOrEqual = 12, + f_lessOrEqual = 13, + f_unorderedOrLessOrEqual = 14, + f_ordered = 15, + + // V8 coproc, pp 123 v8 manual + + cp_always = 8, + cp_never = 0, + cp_3 = 7, + cp_2 = 6, + cp_2or3 = 5, + cp_1 = 4, + cp_1or3 = 3, + cp_1or2 = 2, + cp_1or2or3 = 1, + cp_0 = 9, + cp_0or3 = 10, + cp_0or2 = 11, + cp_0or2or3 = 12, + cp_0or1 = 13, + cp_0or1or3 = 14, + cp_0or1or2 = 15, + + + // for integers + + never = 0, + equal = 1, + zero = 1, + lessEqual = 2, + less = 3, + lessEqualUnsigned = 4, + lessUnsigned = 5, + carrySet = 5, + negative = 6, + overflowSet = 7, + always = 8, + notEqual = 9, + notZero = 9, + greater = 10, + greaterEqual = 11, + greaterUnsigned = 12, + greaterEqualUnsigned = 13, + carryClear = 13, + positive = 14, + overflowClear = 15 + }; + + enum CC { + icc = 0, xcc = 2, + // ptr_cc is the correct condition code for a pointer or intptr_t: + ptr_cc = NOT_LP64(icc) LP64_ONLY(xcc), + fcc0 = 0, fcc1 = 1, fcc2 = 2, fcc3 = 3 + }; + + enum PrefetchFcn { + severalReads = 0, oneRead = 1, severalWritesAndPossiblyReads = 2, oneWrite = 3, page = 4 + }; + + public: + // Helper functions for groups of instructions + + enum Predict { pt = 1, pn = 0 }; // pt = predict taken + + enum Membar_mask_bits { // page 184, v9 + StoreStore = 1 << 3, + LoadStore = 1 << 2, + StoreLoad = 1 << 1, + LoadLoad = 1 << 0, + + Sync = 1 << 6, + MemIssue = 1 << 5, + Lookaside = 1 << 4 + }; + + // test if x is within signed immediate range for nbits + static bool is_simm(int x, int nbits) { return -( 1 << nbits-1 ) <= x && x < ( 1 << nbits-1 ); } + + // test if -4096 <= x <= 4095 + static bool is_simm13(int x) { return is_simm(x, 13); } + + enum ASIs { // page 72, v9 + ASI_PRIMARY = 0x80, + ASI_PRIMARY_LITTLE = 0x88 + // add more from book as needed + }; + + protected: + // helpers + + // x is supposed to fit in a field "nbits" wide + // and be sign-extended. Check the range. + + static void assert_signed_range(intptr_t x, int nbits) { + assert( nbits == 32 + || -(1 << nbits-1) <= x && x < ( 1 << nbits-1), + "value out of range"); + } + + static void assert_signed_word_disp_range(intptr_t x, int nbits) { + assert( (x & 3) == 0, "not word aligned"); + assert_signed_range(x, nbits + 2); + } + + static void assert_unsigned_const(int x, int nbits) { + assert( juint(x) < juint(1 << nbits), "unsigned constant out of range"); + } + + // fields: note bits numbered from LSB = 0, + // fields known by inclusive bit range + + static int fmask(juint hi_bit, juint lo_bit) { + assert( hi_bit >= lo_bit && 0 <= lo_bit && hi_bit < 32, "bad bits"); + return (1 << ( hi_bit-lo_bit + 1 )) - 1; + } + + // inverse of u_field + + static int inv_u_field(int x, int hi_bit, int lo_bit) { + juint r = juint(x) >> lo_bit; + r &= fmask( hi_bit, lo_bit); + return int(r); + } + + + // signed version: extract from field and sign-extend + + static int inv_s_field(int x, int hi_bit, int lo_bit) { + int sign_shift = 31 - hi_bit; + return inv_u_field( ((x << sign_shift) >> sign_shift), hi_bit, lo_bit); + } + + // given a field that ranges from hi_bit to lo_bit (inclusive, + // LSB = 0), and an unsigned value for the field, + // shift it into the field + +#ifdef ASSERT + static int u_field(int x, int hi_bit, int lo_bit) { + assert( ( x & ~fmask(hi_bit, lo_bit)) == 0, + "value out of range"); + int r = x << lo_bit; + assert( inv_u_field(r, hi_bit, lo_bit) == x, "just checking"); + return r; + } +#else + // make sure this is inlined as it will reduce code size significantly + #define u_field(x, hi_bit, lo_bit) ((x) << (lo_bit)) +#endif + + static int inv_op( int x ) { return inv_u_field(x, 31, 30); } + static int inv_op2( int x ) { return inv_u_field(x, 24, 22); } + static int inv_op3( int x ) { return inv_u_field(x, 24, 19); } + static int inv_cond( int x ){ return inv_u_field(x, 28, 25); } + + static bool inv_immed( int x ) { return (x & Assembler::immed(true)) != 0; } + + static Register inv_rd( int x ) { return as_Register(inv_u_field(x, 29, 25)); } + static Register inv_rs1( int x ) { return as_Register(inv_u_field(x, 18, 14)); } + static Register inv_rs2( int x ) { return as_Register(inv_u_field(x, 4, 0)); } + + static int op( int x) { return u_field(x, 31, 30); } + static int rd( Register r) { return u_field(r->encoding(), 29, 25); } + static int fcn( int x) { return u_field(x, 29, 25); } + static int op3( int x) { return u_field(x, 24, 19); } + static int rs1( Register r) { return u_field(r->encoding(), 18, 14); } + static int rs2( Register r) { return u_field(r->encoding(), 4, 0); } + static int annul( bool a) { return u_field(a ? 1 : 0, 29, 29); } + static int cond( int x) { return u_field(x, 28, 25); } + static int cond_mov( int x) { return u_field(x, 17, 14); } + static int rcond( RCondition x) { return u_field(x, 12, 10); } + static int op2( int x) { return u_field(x, 24, 22); } + static int predict( bool p) { return u_field(p ? 1 : 0, 19, 19); } + static int branchcc( CC fcca) { return u_field(fcca, 21, 20); } + static int cmpcc( CC fcca) { return u_field(fcca, 26, 25); } + static int imm_asi( int x) { return u_field(x, 12, 5); } + static int immed( bool i) { return u_field(i ? 1 : 0, 13, 13); } + static int opf_low6( int w) { return u_field(w, 10, 5); } + static int opf_low5( int w) { return u_field(w, 9, 5); } + static int trapcc( CC cc) { return u_field(cc, 12, 11); } + static int sx( int i) { return u_field(i, 12, 12); } // shift x=1 means 64-bit + static int opf( int x) { return u_field(x, 13, 5); } + + static int opf_cc( CC c, bool useFloat ) { return u_field((useFloat ? 0 : 4) + c, 13, 11); } + static int mov_cc( CC c, bool useFloat ) { return u_field(useFloat ? 0 : 1, 18, 18) | u_field(c, 12, 11); } + + static int fd( FloatRegister r, FloatRegisterImpl::Width fwa) { return u_field(r->encoding(fwa), 29, 25); }; + static int fs1(FloatRegister r, FloatRegisterImpl::Width fwa) { return u_field(r->encoding(fwa), 18, 14); }; + static int fs2(FloatRegister r, FloatRegisterImpl::Width fwa) { return u_field(r->encoding(fwa), 4, 0); }; + + // some float instructions use this encoding on the op3 field + static int alt_op3(int op, FloatRegisterImpl::Width w) { + int r; + switch(w) { + case FloatRegisterImpl::S: r = op + 0; break; + case FloatRegisterImpl::D: r = op + 3; break; + case FloatRegisterImpl::Q: r = op + 2; break; + default: ShouldNotReachHere(); break; + } + return op3(r); + } + + + // compute inverse of simm + static int inv_simm(int x, int nbits) { + return (int)(x << (32 - nbits)) >> (32 - nbits); + } + + static int inv_simm13( int x ) { return inv_simm(x, 13); } + + // signed immediate, in low bits, nbits long + static int simm(int x, int nbits) { + assert_signed_range(x, nbits); + return x & (( 1 << nbits ) - 1); + } + + // compute inverse of wdisp16 + static intptr_t inv_wdisp16(int x, intptr_t pos) { + int lo = x & (( 1 << 14 ) - 1); + int hi = (x >> 20) & 3; + if (hi >= 2) hi |= ~1; + return (((hi << 14) | lo) << 2) + pos; + } + + // word offset, 14 bits at LSend, 2 bits at B21, B20 + static int wdisp16(intptr_t x, intptr_t off) { + intptr_t xx = x - off; + assert_signed_word_disp_range(xx, 16); + int r = (xx >> 2) & ((1 << 14) - 1) + | ( ( (xx>>(2+14)) & 3 ) << 20 ); + assert( inv_wdisp16(r, off) == x, "inverse is not inverse"); + return r; + } + + + // word displacement in low-order nbits bits + + static intptr_t inv_wdisp( int x, intptr_t pos, int nbits ) { + int pre_sign_extend = x & (( 1 << nbits ) - 1); + int r = pre_sign_extend >= ( 1 << (nbits-1) ) + ? pre_sign_extend | ~(( 1 << nbits ) - 1) + : pre_sign_extend; + return (r << 2) + pos; + } + + static int wdisp( intptr_t x, intptr_t off, int nbits ) { + intptr_t xx = x - off; + assert_signed_word_disp_range(xx, nbits); + int r = (xx >> 2) & (( 1 << nbits ) - 1); + assert( inv_wdisp( r, off, nbits ) == x, "inverse not inverse"); + return r; + } + + + // Extract the top 32 bits in a 64 bit word + static int32_t hi32( int64_t x ) { + int32_t r = int32_t( (uint64_t)x >> 32 ); + return r; + } + + // given a sethi instruction, extract the constant, left-justified + static int inv_hi22( int x ) { + return x << 10; + } + + // create an imm22 field, given a 32-bit left-justified constant + static int hi22( int x ) { + int r = int( juint(x) >> 10 ); + assert( (r & ~((1 << 22) - 1)) == 0, "just checkin'"); + return r; + } + + // create a low10 __value__ (not a field) for a given a 32-bit constant + static int low10( int x ) { + return x & ((1 << 10) - 1); + } + + // instruction only in v9 + static void v9_only() { assert( VM_Version::v9_instructions_work(), "This instruction only works on SPARC V9"); } + + // instruction only in v8 + static void v8_only() { assert( VM_Version::v8_instructions_work(), "This instruction only works on SPARC V8"); } + + // instruction deprecated in v9 + static void v9_dep() { } // do nothing for now + + // some float instructions only exist for single prec. on v8 + static void v8_s_only(FloatRegisterImpl::Width w) { if (w != FloatRegisterImpl::S) v9_only(); } + + // v8 has no CC field + static void v8_no_cc(CC cc) { if (cc) v9_only(); } + + protected: + // Simple delay-slot scheme: + // In order to check the programmer, the assembler keeps track of deley slots. + // It forbids CTIs in delay slots (conservative, but should be OK). + // Also, when putting an instruction into a delay slot, you must say + // asm->delayed()->add(...), in order to check that you don't omit + // delay-slot instructions. + // To implement this, we use a simple FSA + +#ifdef ASSERT + #define CHECK_DELAY +#endif +#ifdef CHECK_DELAY + enum Delay_state { no_delay, at_delay_slot, filling_delay_slot } delay_state; +#endif + + public: + // Tells assembler next instruction must NOT be in delay slot. + // Use at start of multinstruction macros. + void assert_not_delayed() { + // This is a separate overloading to avoid creation of string constants + // in non-asserted code--with some compilers this pollutes the object code. +#ifdef CHECK_DELAY + assert_not_delayed("next instruction should not be a delay slot"); +#endif + } + void assert_not_delayed(const char* msg) { +#ifdef CHECK_DELAY + assert_msg ( delay_state == no_delay, msg); +#endif + } + + protected: + // Delay slot helpers + // cti is called when emitting control-transfer instruction, + // BEFORE doing the emitting. + // Only effective when assertion-checking is enabled. + void cti() { +#ifdef CHECK_DELAY + assert_not_delayed("cti should not be in delay slot"); +#endif + } + + // called when emitting cti with a delay slot, AFTER emitting + void has_delay_slot() { +#ifdef CHECK_DELAY + assert_not_delayed("just checking"); + delay_state = at_delay_slot; +#endif + } + +public: + // Tells assembler you know that next instruction is delayed + Assembler* delayed() { +#ifdef CHECK_DELAY + assert ( delay_state == at_delay_slot, "delayed instruction is not in delay slot"); + delay_state = filling_delay_slot; +#endif + return this; + } + + void flush() { +#ifdef CHECK_DELAY + assert ( delay_state == no_delay, "ending code with a delay slot"); +#endif + AbstractAssembler::flush(); + } + + inline void emit_long(int); // shadows AbstractAssembler::emit_long + inline void emit_data(int x) { emit_long(x); } + inline void emit_data(int, RelocationHolder const&); + inline void emit_data(int, relocInfo::relocType rtype); + // helper for above fcns + inline void check_delay(); + + + public: + // instructions, refer to page numbers in the SPARC Architecture Manual, V9 + + // pp 135 (addc was addx in v8) + + inline void add( Register s1, Register s2, Register d ); + inline void add( Register s1, int simm13a, Register d, relocInfo::relocType rtype = relocInfo::none); + inline void add( Register s1, int simm13a, Register d, RelocationHolder const& rspec); + inline void add( const Address& a, Register d, int offset = 0); + + void addcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(add_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void addcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(add_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void addc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(addc_op3 ) | rs1(s1) | rs2(s2) ); } + void addc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(addc_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void addccc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(addc_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void addccc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(addc_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 136 + + inline void bpr( RCondition c, bool a, Predict p, Register s1, address d, relocInfo::relocType rt = relocInfo::none ); + inline void bpr( RCondition c, bool a, Predict p, Register s1, Label& L); + + protected: // use MacroAssembler::br instead + + // pp 138 + + inline void fb( Condition c, bool a, address d, relocInfo::relocType rt = relocInfo::none ); + inline void fb( Condition c, bool a, Label& L ); + + // pp 141 + + inline void fbp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt = relocInfo::none ); + inline void fbp( Condition c, bool a, CC cc, Predict p, Label& L ); + + public: + + // pp 144 + + inline void br( Condition c, bool a, address d, relocInfo::relocType rt = relocInfo::none ); + inline void br( Condition c, bool a, Label& L ); + + // pp 146 + + inline void bp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt = relocInfo::none ); + inline void bp( Condition c, bool a, CC cc, Predict p, Label& L ); + + // pp 121 (V8) + + inline void cb( Condition c, bool a, address d, relocInfo::relocType rt = relocInfo::none ); + inline void cb( Condition c, bool a, Label& L ); + + // pp 149 + + inline void call( address d, relocInfo::relocType rt = relocInfo::runtime_call_type ); + inline void call( Label& L, relocInfo::relocType rt = relocInfo::runtime_call_type ); + + // pp 150 + + // These instructions compare the contents of s2 with the contents of + // memory at address in s1. If the values are equal, the contents of memory + // at address s1 is swapped with the data in d. If the values are not equal, + // the the contents of memory at s1 is loaded into d, without the swap. + + void casa( Register s1, Register s2, Register d, int ia = -1 ) { v9_only(); emit_long( op(ldst_op) | rd(d) | op3(casa_op3 ) | rs1(s1) | (ia == -1 ? immed(true) : imm_asi(ia)) | rs2(s2)); } + void casxa( Register s1, Register s2, Register d, int ia = -1 ) { v9_only(); emit_long( op(ldst_op) | rd(d) | op3(casxa_op3) | rs1(s1) | (ia == -1 ? immed(true) : imm_asi(ia)) | rs2(s2)); } + + // pp 152 + + void udiv( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(udiv_op3 ) | rs1(s1) | rs2(s2)); } + void udiv( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(udiv_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void sdiv( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sdiv_op3 ) | rs1(s1) | rs2(s2)); } + void sdiv( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sdiv_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void udivcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(udiv_op3 | cc_bit_op3) | rs1(s1) | rs2(s2)); } + void udivcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(udiv_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void sdivcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sdiv_op3 | cc_bit_op3) | rs1(s1) | rs2(s2)); } + void sdivcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sdiv_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 155 + + void done() { v9_only(); cti(); emit_long( op(arith_op) | fcn(0) | op3(done_op3) ); } + void retry() { v9_only(); cti(); emit_long( op(arith_op) | fcn(1) | op3(retry_op3) ); } + + // pp 156 + + void fadd( FloatRegisterImpl::Width w, FloatRegister s1, FloatRegister s2, FloatRegister d ) { emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | fs1(s1, w) | opf(0x40 + w) | fs2(s2, w)); } + void fsub( FloatRegisterImpl::Width w, FloatRegister s1, FloatRegister s2, FloatRegister d ) { emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | fs1(s1, w) | opf(0x44 + w) | fs2(s2, w)); } + + // pp 157 + + void fcmp( FloatRegisterImpl::Width w, CC cc, FloatRegister s1, FloatRegister s2) { v8_no_cc(cc); emit_long( op(arith_op) | cmpcc(cc) | op3(fpop2_op3) | fs1(s1, w) | opf(0x50 + w) | fs2(s2, w)); } + void fcmpe( FloatRegisterImpl::Width w, CC cc, FloatRegister s1, FloatRegister s2) { v8_no_cc(cc); emit_long( op(arith_op) | cmpcc(cc) | op3(fpop2_op3) | fs1(s1, w) | opf(0x54 + w) | fs2(s2, w)); } + + // pp 159 + + void ftox( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d ) { v9_only(); emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | opf(0x80 + w) | fs2(s, w)); } + void ftoi( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d ) { emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | opf(0xd0 + w) | fs2(s, w)); } + + // pp 160 + + void ftof( FloatRegisterImpl::Width sw, FloatRegisterImpl::Width dw, FloatRegister s, FloatRegister d ) { emit_long( op(arith_op) | fd(d, dw) | op3(fpop1_op3) | opf(0xc0 + sw + dw*4) | fs2(s, sw)); } + + // pp 161 + + void fxtof( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d ) { v9_only(); emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | opf(0x80 + w*4) | fs2(s, w)); } + void fitof( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d ) { emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | opf(0xc0 + w*4) | fs2(s, w)); } + + // pp 162 + + void fmov( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d ) { v8_s_only(w); emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | opf(0x00 + w) | fs2(s, w)); } + + void fneg( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d ) { v8_s_only(w); emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | opf(0x04 + w) | fs2(s, w)); } + + // page 144 sparc v8 architecture (double prec works on v8 if the source and destination registers are the same). fnegs is the only instruction available + // on v8 to do negation of single, double and quad precision floats. + + void fneg( FloatRegisterImpl::Width w, FloatRegister sd ) { if (VM_Version::v9_instructions_work()) emit_long( op(arith_op) | fd(sd, w) | op3(fpop1_op3) | opf(0x04 + w) | fs2(sd, w)); else emit_long( op(arith_op) | fd(sd, w) | op3(fpop1_op3) | opf(0x05) | fs2(sd, w)); } + + void fabs( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d ) { v8_s_only(w); emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | opf(0x08 + w) | fs2(s, w)); } + + // page 144 sparc v8 architecture (double prec works on v8 if the source and destination registers are the same). fabss is the only instruction available + // on v8 to do abs operation on single/double/quad precision floats. + + void fabs( FloatRegisterImpl::Width w, FloatRegister sd ) { if (VM_Version::v9_instructions_work()) emit_long( op(arith_op) | fd(sd, w) | op3(fpop1_op3) | opf(0x08 + w) | fs2(sd, w)); else emit_long( op(arith_op) | fd(sd, w) | op3(fpop1_op3) | opf(0x09) | fs2(sd, w)); } + + // pp 163 + + void fmul( FloatRegisterImpl::Width w, FloatRegister s1, FloatRegister s2, FloatRegister d ) { emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | fs1(s1, w) | opf(0x48 + w) | fs2(s2, w)); } + void fmul( FloatRegisterImpl::Width sw, FloatRegisterImpl::Width dw, FloatRegister s1, FloatRegister s2, FloatRegister d ) { emit_long( op(arith_op) | fd(d, dw) | op3(fpop1_op3) | fs1(s1, sw) | opf(0x60 + sw + dw*4) | fs2(s2, sw)); } + void fdiv( FloatRegisterImpl::Width w, FloatRegister s1, FloatRegister s2, FloatRegister d ) { emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | fs1(s1, w) | opf(0x4c + w) | fs2(s2, w)); } + + // pp 164 + + void fsqrt( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d ) { emit_long( op(arith_op) | fd(d, w) | op3(fpop1_op3) | opf(0x28 + w) | fs2(s, w)); } + + // pp 165 + + inline void flush( Register s1, Register s2 ); + inline void flush( Register s1, int simm13a); + + // pp 167 + + void flushw() { v9_only(); emit_long( op(arith_op) | op3(flushw_op3) ); } + + // pp 168 + + void illtrap( int const22a) { if (const22a != 0) v9_only(); emit_long( op(branch_op) | u_field(const22a, 21, 0) ); } + // v8 unimp == illtrap(0) + + // pp 169 + + void impdep1( int id1, int const19a ) { v9_only(); emit_long( op(arith_op) | fcn(id1) | op3(impdep1_op3) | u_field(const19a, 18, 0)); } + void impdep2( int id1, int const19a ) { v9_only(); emit_long( op(arith_op) | fcn(id1) | op3(impdep2_op3) | u_field(const19a, 18, 0)); } + + // pp 149 (v8) + + void cpop1( int opc, int cr1, int cr2, int crd ) { v8_only(); emit_long( op(arith_op) | fcn(crd) | op3(impdep1_op3) | u_field(cr1, 18, 14) | opf(opc) | u_field(cr2, 4, 0)); } + void cpop2( int opc, int cr1, int cr2, int crd ) { v8_only(); emit_long( op(arith_op) | fcn(crd) | op3(impdep2_op3) | u_field(cr1, 18, 14) | opf(opc) | u_field(cr2, 4, 0)); } + + // pp 170 + + void jmpl( Register s1, Register s2, Register d ); + void jmpl( Register s1, int simm13a, Register d, RelocationHolder const& rspec = RelocationHolder() ); + + inline void jmpl( Address& a, Register d, int offset = 0); + + // 171 + + inline void ldf( FloatRegisterImpl::Width w, Register s1, Register s2, FloatRegister d ); + inline void ldf( FloatRegisterImpl::Width w, Register s1, int simm13a, FloatRegister d ); + + inline void ldf( FloatRegisterImpl::Width w, const Address& a, FloatRegister d, int offset = 0); + + + inline void ldfsr( Register s1, Register s2 ); + inline void ldfsr( Register s1, int simm13a); + inline void ldxfsr( Register s1, Register s2 ); + inline void ldxfsr( Register s1, int simm13a); + + // pp 94 (v8) + + inline void ldc( Register s1, Register s2, int crd ); + inline void ldc( Register s1, int simm13a, int crd); + inline void lddc( Register s1, Register s2, int crd ); + inline void lddc( Register s1, int simm13a, int crd); + inline void ldcsr( Register s1, Register s2, int crd ); + inline void ldcsr( Register s1, int simm13a, int crd); + + + // 173 + + void ldfa( FloatRegisterImpl::Width w, Register s1, Register s2, int ia, FloatRegister d ) { v9_only(); emit_long( op(ldst_op) | fd(d, w) | alt_op3(ldf_op3 | alt_bit_op3, w) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void ldfa( FloatRegisterImpl::Width w, Register s1, int simm13a, FloatRegister d ) { v9_only(); emit_long( op(ldst_op) | fd(d, w) | alt_op3(ldf_op3 | alt_bit_op3, w) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 175, lduw is ld on v8 + + inline void ldsb( Register s1, Register s2, Register d ); + inline void ldsb( Register s1, int simm13a, Register d); + inline void ldsh( Register s1, Register s2, Register d ); + inline void ldsh( Register s1, int simm13a, Register d); + inline void ldsw( Register s1, Register s2, Register d ); + inline void ldsw( Register s1, int simm13a, Register d); + inline void ldub( Register s1, Register s2, Register d ); + inline void ldub( Register s1, int simm13a, Register d); + inline void lduh( Register s1, Register s2, Register d ); + inline void lduh( Register s1, int simm13a, Register d); + inline void lduw( Register s1, Register s2, Register d ); + inline void lduw( Register s1, int simm13a, Register d); + inline void ldx( Register s1, Register s2, Register d ); + inline void ldx( Register s1, int simm13a, Register d); + inline void ld( Register s1, Register s2, Register d ); + inline void ld( Register s1, int simm13a, Register d); + inline void ldd( Register s1, Register s2, Register d ); + inline void ldd( Register s1, int simm13a, Register d); + + inline void ldsb( const Address& a, Register d, int offset = 0 ); + inline void ldsh( const Address& a, Register d, int offset = 0 ); + inline void ldsw( const Address& a, Register d, int offset = 0 ); + inline void ldub( const Address& a, Register d, int offset = 0 ); + inline void lduh( const Address& a, Register d, int offset = 0 ); + inline void lduw( const Address& a, Register d, int offset = 0 ); + inline void ldx( const Address& a, Register d, int offset = 0 ); + inline void ld( const Address& a, Register d, int offset = 0 ); + inline void ldd( const Address& a, Register d, int offset = 0 ); + + // pp 177 + + void ldsba( Register s1, Register s2, int ia, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(ldsb_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void ldsba( Register s1, int simm13a, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(ldsb_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void ldsha( Register s1, Register s2, int ia, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(ldsh_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void ldsha( Register s1, int simm13a, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(ldsh_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void ldswa( Register s1, Register s2, int ia, Register d ) { v9_only(); emit_long( op(ldst_op) | rd(d) | op3(ldsw_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void ldswa( Register s1, int simm13a, Register d ) { v9_only(); emit_long( op(ldst_op) | rd(d) | op3(ldsw_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void lduba( Register s1, Register s2, int ia, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(ldub_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void lduba( Register s1, int simm13a, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(ldub_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void lduha( Register s1, Register s2, int ia, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(lduh_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void lduha( Register s1, int simm13a, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(lduh_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void lduwa( Register s1, Register s2, int ia, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(lduw_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void lduwa( Register s1, int simm13a, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(lduw_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void ldxa( Register s1, Register s2, int ia, Register d ) { v9_only(); emit_long( op(ldst_op) | rd(d) | op3(ldx_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void ldxa( Register s1, int simm13a, Register d ) { v9_only(); emit_long( op(ldst_op) | rd(d) | op3(ldx_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void ldda( Register s1, Register s2, int ia, Register d ) { v9_dep(); emit_long( op(ldst_op) | rd(d) | op3(ldd_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void ldda( Register s1, int simm13a, Register d ) { v9_dep(); emit_long( op(ldst_op) | rd(d) | op3(ldd_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 179 + + inline void ldstub( Register s1, Register s2, Register d ); + inline void ldstub( Register s1, int simm13a, Register d); + + // pp 180 + + void ldstuba( Register s1, Register s2, int ia, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(ldstub_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void ldstuba( Register s1, int simm13a, Register d ) { emit_long( op(ldst_op) | rd(d) | op3(ldstub_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 181 + + void and3( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(and_op3 ) | rs1(s1) | rs2(s2) ); } + void and3( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(and_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void andcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(and_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void andcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(and_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void andn( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(andn_op3 ) | rs1(s1) | rs2(s2) ); } + void andn( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(andn_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void andncc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(andn_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void andncc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(andn_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void or3( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(or_op3 ) | rs1(s1) | rs2(s2) ); } + void or3( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(or_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void orcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(or_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void orcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(or_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void orn( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(orn_op3) | rs1(s1) | rs2(s2) ); } + void orn( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(orn_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void orncc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(orn_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void orncc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(orn_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void xor3( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(xor_op3 ) | rs1(s1) | rs2(s2) ); } + void xor3( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(xor_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void xorcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(xor_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void xorcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(xor_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void xnor( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(xnor_op3 ) | rs1(s1) | rs2(s2) ); } + void xnor( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(xnor_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void xnorcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(xnor_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void xnorcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(xnor_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 183 + + void membar( Membar_mask_bits const7a ) { v9_only(); emit_long( op(arith_op) | op3(membar_op3) | rs1(O7) | immed(true) | u_field( int(const7a), 6, 0)); } + + // pp 185 + + void fmov( FloatRegisterImpl::Width w, Condition c, bool floatCC, CC cca, FloatRegister s2, FloatRegister d ) { v9_only(); emit_long( op(arith_op) | fd(d, w) | op3(fpop2_op3) | cond_mov(c) | opf_cc(cca, floatCC) | opf_low6(w) | fs2(s2, w)); } + + // pp 189 + + void fmov( FloatRegisterImpl::Width w, RCondition c, Register s1, FloatRegister s2, FloatRegister d ) { v9_only(); emit_long( op(arith_op) | fd(d, w) | op3(fpop2_op3) | rs1(s1) | rcond(c) | opf_low5(4 + w) | fs2(s2, w)); } + + // pp 191 + + void movcc( Condition c, bool floatCC, CC cca, Register s2, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(movcc_op3) | mov_cc(cca, floatCC) | cond_mov(c) | rs2(s2) ); } + void movcc( Condition c, bool floatCC, CC cca, int simm11a, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(movcc_op3) | mov_cc(cca, floatCC) | cond_mov(c) | immed(true) | simm(simm11a, 11) ); } + + // pp 195 + + void movr( RCondition c, Register s1, Register s2, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(movr_op3) | rs1(s1) | rcond(c) | rs2(s2) ); } + void movr( RCondition c, Register s1, int simm10a, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(movr_op3) | rs1(s1) | rcond(c) | immed(true) | simm(simm10a, 10) ); } + + // pp 196 + + void mulx( Register s1, Register s2, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(mulx_op3 ) | rs1(s1) | rs2(s2) ); } + void mulx( Register s1, int simm13a, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(mulx_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void sdivx( Register s1, Register s2, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(sdivx_op3) | rs1(s1) | rs2(s2) ); } + void sdivx( Register s1, int simm13a, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(sdivx_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void udivx( Register s1, Register s2, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(udivx_op3) | rs1(s1) | rs2(s2) ); } + void udivx( Register s1, int simm13a, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(udivx_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 197 + + void umul( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(umul_op3 ) | rs1(s1) | rs2(s2) ); } + void umul( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(umul_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void smul( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(smul_op3 ) | rs1(s1) | rs2(s2) ); } + void smul( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(smul_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void umulcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(umul_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void umulcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(umul_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void smulcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(smul_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void smulcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(smul_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 199 + + void mulscc( Register s1, Register s2, Register d ) { v9_dep(); emit_long( op(arith_op) | rd(d) | op3(mulscc_op3) | rs1(s1) | rs2(s2) ); } + void mulscc( Register s1, int simm13a, Register d ) { v9_dep(); emit_long( op(arith_op) | rd(d) | op3(mulscc_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 201 + + void nop() { emit_long( op(branch_op) | op2(sethi_op2) ); } + + + // pp 202 + + void popc( Register s, Register d) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(popc_op3) | rs2(s)); } + void popc( int simm13a, Register d) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(popc_op3) | immed(true) | simm(simm13a, 13)); } + + // pp 203 + + void prefetch( Register s1, Register s2, PrefetchFcn f); + void prefetch( Register s1, int simm13a, PrefetchFcn f); + void prefetcha( Register s1, Register s2, int ia, PrefetchFcn f ) { v9_only(); emit_long( op(ldst_op) | fcn(f) | op3(prefetch_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void prefetcha( Register s1, int simm13a, PrefetchFcn f ) { v9_only(); emit_long( op(ldst_op) | fcn(f) | op3(prefetch_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + inline void prefetch(const Address& a, PrefetchFcn F, int offset = 0); + + // pp 208 + + // not implementing read privileged register + + inline void rdy( Register d) { v9_dep(); emit_long( op(arith_op) | rd(d) | op3(rdreg_op3) | u_field(0, 18, 14)); } + inline void rdccr( Register d) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(rdreg_op3) | u_field(2, 18, 14)); } + inline void rdasi( Register d) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(rdreg_op3) | u_field(3, 18, 14)); } + inline void rdtick( Register d) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(rdreg_op3) | u_field(4, 18, 14)); } // Spoon! + inline void rdpc( Register d) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(rdreg_op3) | u_field(5, 18, 14)); } + inline void rdfprs( Register d) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(rdreg_op3) | u_field(6, 18, 14)); } + + // pp 213 + + inline void rett( Register s1, Register s2); + inline void rett( Register s1, int simm13a, relocInfo::relocType rt = relocInfo::none); + + // pp 214 + + void save( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(save_op3) | rs1(s1) | rs2(s2) ); } + void save( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(save_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + void restore( Register s1 = G0, Register s2 = G0, Register d = G0 ) { emit_long( op(arith_op) | rd(d) | op3(restore_op3) | rs1(s1) | rs2(s2) ); } + void restore( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(restore_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 216 + + void saved() { v9_only(); emit_long( op(arith_op) | fcn(0) | op3(saved_op3)); } + void restored() { v9_only(); emit_long( op(arith_op) | fcn(1) | op3(saved_op3)); } + + // pp 217 + + inline void sethi( int imm22a, Register d, RelocationHolder const& rspec = RelocationHolder() ); + // pp 218 + + void sll( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sll_op3) | rs1(s1) | sx(0) | rs2(s2) ); } + void sll( Register s1, int imm5a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sll_op3) | rs1(s1) | sx(0) | immed(true) | u_field(imm5a, 4, 0) ); } + void srl( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(srl_op3) | rs1(s1) | sx(0) | rs2(s2) ); } + void srl( Register s1, int imm5a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(srl_op3) | rs1(s1) | sx(0) | immed(true) | u_field(imm5a, 4, 0) ); } + void sra( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sra_op3) | rs1(s1) | sx(0) | rs2(s2) ); } + void sra( Register s1, int imm5a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sra_op3) | rs1(s1) | sx(0) | immed(true) | u_field(imm5a, 4, 0) ); } + + void sllx( Register s1, Register s2, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(sll_op3) | rs1(s1) | sx(1) | rs2(s2) ); } + void sllx( Register s1, int imm6a, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(sll_op3) | rs1(s1) | sx(1) | immed(true) | u_field(imm6a, 5, 0) ); } + void srlx( Register s1, Register s2, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(srl_op3) | rs1(s1) | sx(1) | rs2(s2) ); } + void srlx( Register s1, int imm6a, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(srl_op3) | rs1(s1) | sx(1) | immed(true) | u_field(imm6a, 5, 0) ); } + void srax( Register s1, Register s2, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(sra_op3) | rs1(s1) | sx(1) | rs2(s2) ); } + void srax( Register s1, int imm6a, Register d ) { v9_only(); emit_long( op(arith_op) | rd(d) | op3(sra_op3) | rs1(s1) | sx(1) | immed(true) | u_field(imm6a, 5, 0) ); } + + // pp 220 + + void sir( int simm13a ) { emit_long( op(arith_op) | fcn(15) | op3(sir_op3) | immed(true) | simm(simm13a, 13)); } + + // pp 221 + + void stbar() { emit_long( op(arith_op) | op3(membar_op3) | u_field(15, 18, 14)); } + + // pp 222 + + inline void stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, Register s2 ); + inline void stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, int simm13a); + inline void stf( FloatRegisterImpl::Width w, FloatRegister d, const Address& a, int offset = 0); + + inline void stfsr( Register s1, Register s2 ); + inline void stfsr( Register s1, int simm13a); + inline void stxfsr( Register s1, Register s2 ); + inline void stxfsr( Register s1, int simm13a); + + // pp 224 + + void stfa( FloatRegisterImpl::Width w, FloatRegister d, Register s1, Register s2, int ia ) { v9_only(); emit_long( op(ldst_op) | fd(d, w) | alt_op3(stf_op3 | alt_bit_op3, w) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void stfa( FloatRegisterImpl::Width w, FloatRegister d, Register s1, int simm13a ) { v9_only(); emit_long( op(ldst_op) | fd(d, w) | alt_op3(stf_op3 | alt_bit_op3, w) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // p 226 + + inline void stb( Register d, Register s1, Register s2 ); + inline void stb( Register d, Register s1, int simm13a); + inline void sth( Register d, Register s1, Register s2 ); + inline void sth( Register d, Register s1, int simm13a); + inline void stw( Register d, Register s1, Register s2 ); + inline void stw( Register d, Register s1, int simm13a); + inline void st( Register d, Register s1, Register s2 ); + inline void st( Register d, Register s1, int simm13a); + inline void stx( Register d, Register s1, Register s2 ); + inline void stx( Register d, Register s1, int simm13a); + inline void std( Register d, Register s1, Register s2 ); + inline void std( Register d, Register s1, int simm13a); + + inline void stb( Register d, const Address& a, int offset = 0 ); + inline void sth( Register d, const Address& a, int offset = 0 ); + inline void stw( Register d, const Address& a, int offset = 0 ); + inline void stx( Register d, const Address& a, int offset = 0 ); + inline void st( Register d, const Address& a, int offset = 0 ); + inline void std( Register d, const Address& a, int offset = 0 ); + + // pp 177 + + void stba( Register d, Register s1, Register s2, int ia ) { emit_long( op(ldst_op) | rd(d) | op3(stb_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void stba( Register d, Register s1, int simm13a ) { emit_long( op(ldst_op) | rd(d) | op3(stb_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void stha( Register d, Register s1, Register s2, int ia ) { emit_long( op(ldst_op) | rd(d) | op3(sth_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void stha( Register d, Register s1, int simm13a ) { emit_long( op(ldst_op) | rd(d) | op3(sth_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void stwa( Register d, Register s1, Register s2, int ia ) { emit_long( op(ldst_op) | rd(d) | op3(stw_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void stwa( Register d, Register s1, int simm13a ) { emit_long( op(ldst_op) | rd(d) | op3(stw_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void stxa( Register d, Register s1, Register s2, int ia ) { v9_only(); emit_long( op(ldst_op) | rd(d) | op3(stx_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void stxa( Register d, Register s1, int simm13a ) { v9_only(); emit_long( op(ldst_op) | rd(d) | op3(stx_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void stda( Register d, Register s1, Register s2, int ia ) { emit_long( op(ldst_op) | rd(d) | op3(std_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void stda( Register d, Register s1, int simm13a ) { emit_long( op(ldst_op) | rd(d) | op3(std_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 97 (v8) + + inline void stc( int crd, Register s1, Register s2 ); + inline void stc( int crd, Register s1, int simm13a); + inline void stdc( int crd, Register s1, Register s2 ); + inline void stdc( int crd, Register s1, int simm13a); + inline void stcsr( int crd, Register s1, Register s2 ); + inline void stcsr( int crd, Register s1, int simm13a); + inline void stdcq( int crd, Register s1, Register s2 ); + inline void stdcq( int crd, Register s1, int simm13a); + + // pp 230 + + void sub( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sub_op3 ) | rs1(s1) | rs2(s2) ); } + void sub( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sub_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void subcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sub_op3 | cc_bit_op3 ) | rs1(s1) | rs2(s2) ); } + void subcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(sub_op3 | cc_bit_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void subc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(subc_op3 ) | rs1(s1) | rs2(s2) ); } + void subc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(subc_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void subccc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(subc_op3 | cc_bit_op3) | rs1(s1) | rs2(s2) ); } + void subccc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(subc_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 231 + + inline void swap( Register s1, Register s2, Register d ); + inline void swap( Register s1, int simm13a, Register d); + inline void swap( Address& a, Register d, int offset = 0 ); + + // pp 232 + + void swapa( Register s1, Register s2, int ia, Register d ) { v9_dep(); emit_long( op(ldst_op) | rd(d) | op3(swap_op3 | alt_bit_op3) | rs1(s1) | imm_asi(ia) | rs2(s2) ); } + void swapa( Register s1, int simm13a, Register d ) { v9_dep(); emit_long( op(ldst_op) | rd(d) | op3(swap_op3 | alt_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 234, note op in book is wrong, see pp 268 + + void taddcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(taddcc_op3 ) | rs1(s1) | rs2(s2) ); } + void taddcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(taddcc_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void taddcctv( Register s1, Register s2, Register d ) { v9_dep(); emit_long( op(arith_op) | rd(d) | op3(taddcctv_op3) | rs1(s1) | rs2(s2) ); } + void taddcctv( Register s1, int simm13a, Register d ) { v9_dep(); emit_long( op(arith_op) | rd(d) | op3(taddcctv_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 235 + + void tsubcc( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(tsubcc_op3 ) | rs1(s1) | rs2(s2) ); } + void tsubcc( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(tsubcc_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + void tsubcctv( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(tsubcctv_op3) | rs1(s1) | rs2(s2) ); } + void tsubcctv( Register s1, int simm13a, Register d ) { emit_long( op(arith_op) | rd(d) | op3(tsubcctv_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + + // pp 237 + + void trap( Condition c, CC cc, Register s1, Register s2 ) { v8_no_cc(cc); emit_long( op(arith_op) | cond(c) | op3(trap_op3) | rs1(s1) | trapcc(cc) | rs2(s2)); } + void trap( Condition c, CC cc, Register s1, int trapa ) { v8_no_cc(cc); emit_long( op(arith_op) | cond(c) | op3(trap_op3) | rs1(s1) | trapcc(cc) | immed(true) | u_field(trapa, 6, 0)); } + // simple uncond. trap + void trap( int trapa ) { trap( always, icc, G0, trapa ); } + + // pp 239 omit write priv register for now + + inline void wry( Register d) { v9_dep(); emit_long( op(arith_op) | rs1(d) | op3(wrreg_op3) | u_field(0, 29, 25)); } + inline void wrccr(Register s) { v9_only(); emit_long( op(arith_op) | rs1(s) | op3(wrreg_op3) | u_field(2, 29, 25)); } + inline void wrccr(Register s, int simm13a) { v9_only(); emit_long( op(arith_op) | + rs1(s) | + op3(wrreg_op3) | + u_field(2, 29, 25) | + u_field(1, 13, 13) | + simm(simm13a, 13)); } + inline void wrasi( Register d) { v9_only(); emit_long( op(arith_op) | rs1(d) | op3(wrreg_op3) | u_field(3, 29, 25)); } + inline void wrfprs( Register d) { v9_only(); emit_long( op(arith_op) | rs1(d) | op3(wrreg_op3) | u_field(6, 29, 25)); } + + + // Creation + Assembler(CodeBuffer* code) : AbstractAssembler(code) { +#ifdef CHECK_DELAY + delay_state = no_delay; +#endif + } + + // Testing +#ifndef PRODUCT + void test_v9(); + void test_v8_onlys(); +#endif +}; + + +class RegistersForDebugging : public StackObj { + public: + intptr_t i[8], l[8], o[8], g[8]; + float f[32]; + double d[32]; + + void print(outputStream* s); + + static int i_offset(int j) { return offset_of(RegistersForDebugging, i[j]); } + static int l_offset(int j) { return offset_of(RegistersForDebugging, l[j]); } + static int o_offset(int j) { return offset_of(RegistersForDebugging, o[j]); } + static int g_offset(int j) { return offset_of(RegistersForDebugging, g[j]); } + static int f_offset(int j) { return offset_of(RegistersForDebugging, f[j]); } + static int d_offset(int j) { return offset_of(RegistersForDebugging, d[j / 2]); } + + // gen asm code to save regs + static void save_registers(MacroAssembler* a); + + // restore global registers in case C code disturbed them + static void restore_registers(MacroAssembler* a, Register r); +}; + + +// MacroAssembler extends Assembler by a few frequently used macros. +// +// Most of the standard SPARC synthetic ops are defined here. +// Instructions for which a 'better' code sequence exists depending +// on arguments should also go in here. + +#define JMP2(r1, r2) jmp(r1, r2, __FILE__, __LINE__) +#define JMP(r1, off) jmp(r1, off, __FILE__, __LINE__) +#define JUMP(a, off) jump(a, off, __FILE__, __LINE__) +#define JUMPL(a, d, off) jumpl(a, d, off, __FILE__, __LINE__) + + +class MacroAssembler: public Assembler { + protected: + // Support for VM calls + // This is the base routine called by the different versions of call_VM_leaf. The interpreter + // may customize this version by overriding it for its purposes (e.g., to save/restore + // additional registers when doing a VM call). +#ifdef CC_INTERP + #define VIRTUAL +#else + #define VIRTUAL virtual +#endif + + VIRTUAL void call_VM_leaf_base(Register thread_cache, address entry_point, int number_of_arguments); + + // + // It is imperative that all calls into the VM are handled via the call_VM macros. + // They make sure that the stack linkage is setup correctly. call_VM's correspond + // to ENTRY/ENTRY_X entry points while call_VM_leaf's correspond to LEAF entry points. + // + // This is the base routine called by the different versions of call_VM. The interpreter + // may customize this version by overriding it for its purposes (e.g., to save/restore + // additional registers when doing a VM call). + // + // A non-volatile java_thread_cache register should be specified so + // that the G2_thread value can be preserved across the call. + // (If java_thread_cache is noreg, then a slow get_thread call + // will re-initialize the G2_thread.) call_VM_base returns the register that contains the + // thread. + // + // If no last_java_sp is specified (noreg) than SP will be used instead. + + virtual void call_VM_base( + Register oop_result, // where an oop-result ends up if any; use noreg otherwise + Register java_thread_cache, // the thread if computed before ; use noreg otherwise + Register last_java_sp, // to set up last_Java_frame in stubs; use noreg otherwise + address entry_point, // the entry point + int number_of_arguments, // the number of arguments (w/o thread) to pop after call + bool check_exception=true // flag which indicates if exception should be checked + ); + + // This routine should emit JVMTI PopFrame and ForceEarlyReturn handling code. + // The implementation is only non-empty for the InterpreterMacroAssembler, + // as only the interpreter handles and ForceEarlyReturn PopFrame requests. + virtual void check_and_handle_popframe(Register scratch_reg); + virtual void check_and_handle_earlyret(Register scratch_reg); + + public: + MacroAssembler(CodeBuffer* code) : Assembler(code) {} + + // Support for NULL-checks + // + // Generates code that causes a NULL OS exception if the content of reg is NULL. + // If the accessed location is M[reg + offset] and the offset is known, provide the + // offset. No explicit code generation is needed if the offset is within a certain + // range (0 <= offset <= page_size). + // + // %%%%%% Currently not done for SPARC + + void null_check(Register reg, int offset = -1); + static bool needs_explicit_null_check(intptr_t offset); + + // support for delayed instructions + MacroAssembler* delayed() { Assembler::delayed(); return this; } + + // branches that use right instruction for v8 vs. v9 + inline void br( Condition c, bool a, Predict p, address d, relocInfo::relocType rt = relocInfo::none ); + inline void br( Condition c, bool a, Predict p, Label& L ); + inline void fb( Condition c, bool a, Predict p, address d, relocInfo::relocType rt = relocInfo::none ); + inline void fb( Condition c, bool a, Predict p, Label& L ); + + // compares register with zero and branches (V9 and V8 instructions) + void br_zero( Condition c, bool a, Predict p, Register s1, Label& L); + // Compares a pointer register with zero and branches on (not)null. + // Does a test & branch on 32-bit systems and a register-branch on 64-bit. + void br_null ( Register s1, bool a, Predict p, Label& L ); + void br_notnull( Register s1, bool a, Predict p, Label& L ); + + inline void bp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt = relocInfo::none ); + inline void bp( Condition c, bool a, CC cc, Predict p, Label& L ); + + // Branch that tests xcc in LP64 and icc in !LP64 + inline void brx( Condition c, bool a, Predict p, address d, relocInfo::relocType rt = relocInfo::none ); + inline void brx( Condition c, bool a, Predict p, Label& L ); + + // unconditional short branch + inline void ba( bool a, Label& L ); + + // Branch that tests fp condition codes + inline void fbp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt = relocInfo::none ); + inline void fbp( Condition c, bool a, CC cc, Predict p, Label& L ); + + // get PC the best way + inline int get_pc( Register d ); + + // Sparc shorthands(pp 85, V8 manual, pp 289 V9 manual) + inline void cmp( Register s1, Register s2 ) { subcc( s1, s2, G0 ); } + inline void cmp( Register s1, int simm13a ) { subcc( s1, simm13a, G0 ); } + + inline void jmp( Register s1, Register s2 ); + inline void jmp( Register s1, int simm13a, RelocationHolder const& rspec = RelocationHolder() ); + + inline void call( address d, relocInfo::relocType rt = relocInfo::runtime_call_type ); + inline void call( Label& L, relocInfo::relocType rt = relocInfo::runtime_call_type ); + inline void callr( Register s1, Register s2 ); + inline void callr( Register s1, int simm13a, RelocationHolder const& rspec = RelocationHolder() ); + + // Emits nothing on V8 + inline void iprefetch( address d, relocInfo::relocType rt = relocInfo::none ); + inline void iprefetch( Label& L); + + inline void tst( Register s ) { orcc( G0, s, G0 ); } + +#ifdef PRODUCT + inline void ret( bool trace = TraceJumps ) { if (trace) { + mov(I7, O7); // traceable register + JMP(O7, 2 * BytesPerInstWord); + } else { + jmpl( I7, 2 * BytesPerInstWord, G0 ); + } + } + + inline void retl( bool trace = TraceJumps ) { if (trace) JMP(O7, 2 * BytesPerInstWord); + else jmpl( O7, 2 * BytesPerInstWord, G0 ); } +#else + void ret( bool trace = TraceJumps ); + void retl( bool trace = TraceJumps ); +#endif /* PRODUCT */ + + // Required platform-specific helpers for Label::patch_instructions. + // They _shadow_ the declarations in AbstractAssembler, which are undefined. + void pd_patch_instruction(address branch, address target); +#ifndef PRODUCT + static void pd_print_patched_instruction(address branch); +#endif + + // sethi Macro handles optimizations and relocations + void sethi( Address& a, bool ForceRelocatable = false ); + void sethi( intptr_t imm22a, Register d, bool ForceRelocatable = false, RelocationHolder const& rspec = RelocationHolder()); + + // compute the size of a sethi/set + static int size_of_sethi( address a, bool worst_case = false ); + static int worst_case_size_of_set(); + + // set may be either setsw or setuw (high 32 bits may be zero or sign) + void set( intptr_t value, Register d, RelocationHolder const& rspec = RelocationHolder() ); + void setsw( int value, Register d, RelocationHolder const& rspec = RelocationHolder() ); + void set64( jlong value, Register d, Register tmp); + + // sign-extend 32 to 64 + inline void signx( Register s, Register d ) { sra( s, G0, d); } + inline void signx( Register d ) { sra( d, G0, d); } + + inline void not1( Register s, Register d ) { xnor( s, G0, d ); } + inline void not1( Register d ) { xnor( d, G0, d ); } + + inline void neg( Register s, Register d ) { sub( G0, s, d ); } + inline void neg( Register d ) { sub( G0, d, d ); } + + inline void cas( Register s1, Register s2, Register d) { casa( s1, s2, d, ASI_PRIMARY); } + inline void casx( Register s1, Register s2, Register d) { casxa(s1, s2, d, ASI_PRIMARY); } + // Functions for isolating 64 bit atomic swaps for LP64 + // cas_ptr will perform cas for 32 bit VM's and casx for 64 bit VM's + inline void cas_ptr( Register s1, Register s2, Register d) { +#ifdef _LP64 + casx( s1, s2, d ); +#else + cas( s1, s2, d ); +#endif + } + + // Functions for isolating 64 bit shifts for LP64 + inline void sll_ptr( Register s1, Register s2, Register d ); + inline void sll_ptr( Register s1, int imm6a, Register d ); + inline void srl_ptr( Register s1, Register s2, Register d ); + inline void srl_ptr( Register s1, int imm6a, Register d ); + + // little-endian + inline void casl( Register s1, Register s2, Register d) { casa( s1, s2, d, ASI_PRIMARY_LITTLE); } + inline void casxl( Register s1, Register s2, Register d) { casxa(s1, s2, d, ASI_PRIMARY_LITTLE); } + + inline void inc( Register d, int const13 = 1 ) { add( d, const13, d); } + inline void inccc( Register d, int const13 = 1 ) { addcc( d, const13, d); } + + inline void dec( Register d, int const13 = 1 ) { sub( d, const13, d); } + inline void deccc( Register d, int const13 = 1 ) { subcc( d, const13, d); } + + inline void btst( Register s1, Register s2 ) { andcc( s1, s2, G0 ); } + inline void btst( int simm13a, Register s ) { andcc( s, simm13a, G0 ); } + + inline void bset( Register s1, Register s2 ) { or3( s1, s2, s2 ); } + inline void bset( int simm13a, Register s ) { or3( s, simm13a, s ); } + + inline void bclr( Register s1, Register s2 ) { andn( s1, s2, s2 ); } + inline void bclr( int simm13a, Register s ) { andn( s, simm13a, s ); } + + inline void btog( Register s1, Register s2 ) { xor3( s1, s2, s2 ); } + inline void btog( int simm13a, Register s ) { xor3( s, simm13a, s ); } + + inline void clr( Register d ) { or3( G0, G0, d ); } + + inline void clrb( Register s1, Register s2); + inline void clrh( Register s1, Register s2); + inline void clr( Register s1, Register s2); + inline void clrx( Register s1, Register s2); + + inline void clrb( Register s1, int simm13a); + inline void clrh( Register s1, int simm13a); + inline void clr( Register s1, int simm13a); + inline void clrx( Register s1, int simm13a); + + // copy & clear upper word + inline void clruw( Register s, Register d ) { srl( s, G0, d); } + // clear upper word + inline void clruwu( Register d ) { srl( d, G0, d); } + + // membar psuedo instruction. takes into account target memory model. + inline void membar( Assembler::Membar_mask_bits const7a ); + + // returns if membar generates anything. + inline bool membar_has_effect( Assembler::Membar_mask_bits const7a ); + + // mov pseudo instructions + inline void mov( Register s, Register d) { + if ( s != d ) or3( G0, s, d); + else assert_not_delayed(); // Put something useful in the delay slot! + } + + inline void mov_or_nop( Register s, Register d) { + if ( s != d ) or3( G0, s, d); + else nop(); + } + + inline void mov( int simm13a, Register d) { or3( G0, simm13a, d); } + + // address pseudos: make these names unlike instruction names to avoid confusion + inline void split_disp( Address& a, Register temp ); + inline intptr_t load_pc_address( Register reg, int bytes_to_skip ); + inline void load_address( Address& a, int offset = 0 ); + inline void load_contents( Address& a, Register d, int offset = 0 ); + inline void load_ptr_contents( Address& a, Register d, int offset = 0 ); + inline void store_contents( Register s, Address& a, int offset = 0 ); + inline void store_ptr_contents( Register s, Address& a, int offset = 0 ); + inline void jumpl_to( Address& a, Register d, int offset = 0 ); + inline void jump_to( Address& a, int offset = 0 ); + + // ring buffer traceable jumps + + void jmp2( Register r1, Register r2, const char* file, int line ); + void jmp ( Register r1, int offset, const char* file, int line ); + + void jumpl( Address& a, Register d, int offset, const char* file, int line ); + void jump ( Address& a, int offset, const char* file, int line ); + + + // argument pseudos: + + inline void load_argument( Argument& a, Register d ); + inline void store_argument( Register s, Argument& a ); + inline void store_ptr_argument( Register s, Argument& a ); + inline void store_float_argument( FloatRegister s, Argument& a ); + inline void store_double_argument( FloatRegister s, Argument& a ); + inline void store_long_argument( Register s, Argument& a ); + + // handy macros: + + inline void round_to( Register r, int modulus ) { + assert_not_delayed(); + inc( r, modulus - 1 ); + and3( r, -modulus, r ); + } + + // -------------------------------------------------- + + // Functions for isolating 64 bit loads for LP64 + // ld_ptr will perform ld for 32 bit VM's and ldx for 64 bit VM's + // st_ptr will perform st for 32 bit VM's and stx for 64 bit VM's + inline void ld_ptr( Register s1, Register s2, Register d ); + inline void ld_ptr( Register s1, int simm13a, Register d); + inline void ld_ptr( const Address& a, Register d, int offset = 0 ); + inline void st_ptr( Register d, Register s1, Register s2 ); + inline void st_ptr( Register d, Register s1, int simm13a); + inline void st_ptr( Register d, const Address& a, int offset = 0 ); + + // ld_long will perform ld for 32 bit VM's and ldx for 64 bit VM's + // st_long will perform st for 32 bit VM's and stx for 64 bit VM's + inline void ld_long( Register s1, Register s2, Register d ); + inline void ld_long( Register s1, int simm13a, Register d ); + inline void ld_long( const Address& a, Register d, int offset = 0 ); + inline void st_long( Register d, Register s1, Register s2 ); + inline void st_long( Register d, Register s1, int simm13a ); + inline void st_long( Register d, const Address& a, int offset = 0 ); + + // -------------------------------------------------- + + public: + // traps as per trap.h (SPARC ABI?) + + void breakpoint_trap(); + void breakpoint_trap(Condition c, CC cc = icc); + void flush_windows_trap(); + void clean_windows_trap(); + void get_psr_trap(); + void set_psr_trap(); + + // V8/V9 flush_windows + void flush_windows(); + + // Support for serializing memory accesses between threads + void serialize_memory(Register thread, Register tmp1, Register tmp2); + + // Stack frame creation/removal + void enter(); + void leave(); + + // V8/V9 integer multiply + void mult(Register s1, Register s2, Register d); + void mult(Register s1, int simm13a, Register d); + + // V8/V9 read and write of condition codes. + void read_ccr(Register d); + void write_ccr(Register s); + + // Manipulation of C++ bools + // These are idioms to flag the need for care with accessing bools but on + // this platform we assume byte size + + inline void stbool( Register d, const Address& a, int offset = 0 ) { stb(d, a, offset); } + inline void ldbool( const Address& a, Register d, int offset = 0 ) { ldsb( a, d, offset ); } + inline void tstbool( Register s ) { tst(s); } + inline void movbool( bool boolconst, Register d) { mov( (int) boolconst, d); } + + // Support for managing the JavaThread pointer (i.e.; the reference to + // thread-local information). + void get_thread(); // load G2_thread + void verify_thread(); // verify G2_thread contents + void save_thread (const Register threache); // save to cache + void restore_thread(const Register thread_cache); // restore from cache + + // Support for last Java frame (but use call_VM instead where possible) + void set_last_Java_frame(Register last_java_sp, Register last_Java_pc); + void reset_last_Java_frame(void); + + // Call into the VM. + // Passes the thread pointer (in O0) as a prepended argument. + // Makes sure oop return values are visible to the GC. + void call_VM(Register oop_result, address entry_point, int number_of_arguments = 0, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions = true); + + // these overloadings are not presently used on SPARC: + void call_VM(Register oop_result, Register last_java_sp, address entry_point, int number_of_arguments = 0, bool check_exceptions = true); + void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, bool check_exceptions = true); + void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); + void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions = true); + + void call_VM_leaf(Register thread_cache, address entry_point, int number_of_arguments = 0); + void call_VM_leaf(Register thread_cache, address entry_point, Register arg_1); + void call_VM_leaf(Register thread_cache, address entry_point, Register arg_1, Register arg_2); + void call_VM_leaf(Register thread_cache, address entry_point, Register arg_1, Register arg_2, Register arg_3); + + void get_vm_result (Register oop_result); + void get_vm_result_2(Register oop_result); + + // vm result is currently getting hijacked to for oop preservation + void set_vm_result(Register oop_result); + + // if call_VM_base was called with check_exceptions=false, then call + // check_and_forward_exception to handle exceptions when it is safe + void check_and_forward_exception(Register scratch_reg); + + private: + // For V8 + void read_ccr_trap(Register ccr_save); + void write_ccr_trap(Register ccr_save1, Register scratch1, Register scratch2); + +#ifdef ASSERT + // For V8 debugging. Uses V8 instruction sequence and checks + // result with V9 insturctions rdccr and wrccr. + // Uses Gscatch and Gscatch2 + void read_ccr_v8_assert(Register ccr_save); + void write_ccr_v8_assert(Register ccr_save); +#endif // ASSERT + + public: + // Stores + void store_check(Register tmp, Register obj); // store check for obj - register is destroyed afterwards + void store_check(Register tmp, Register obj, Register offset); // store check for obj - register is destroyed afterwards + + // pushes double TOS element of FPU stack on CPU stack; pops from FPU stack + void push_fTOS(); + + // pops double TOS element from CPU stack and pushes on FPU stack + void pop_fTOS(); + + void empty_FPU_stack(); + + void push_IU_state(); + void pop_IU_state(); + + void push_FPU_state(); + void pop_FPU_state(); + + void push_CPU_state(); + void pop_CPU_state(); + + // Debugging + void _verify_oop(Register reg, const char * msg, const char * file, int line); + void _verify_oop_addr(Address addr, const char * msg, const char * file, int line); + +#define verify_oop(reg) _verify_oop(reg, "broken oop " #reg, __FILE__, __LINE__) +#define verify_oop_addr(addr) _verify_oop_addr(addr, "broken oop addr ", __FILE__, __LINE__) + + // only if +VerifyOops + void verify_FPU(int stack_depth, const char* s = "illegal FPU state"); + // only if +VerifyFPU + void stop(const char* msg); // prints msg, dumps registers and stops execution + void warn(const char* msg); // prints msg, but don't stop + void untested(const char* what = ""); + void unimplemented(const char* what = "") { char* b = new char[1024]; sprintf(b, "unimplemented: %s", what); stop(b); } + void should_not_reach_here() { stop("should not reach here"); } + void print_CPU_state(); + + // oops in code + Address allocate_oop_address( jobject obj, Register d ); // allocate_index + Address constant_oop_address( jobject obj, Register d ); // find_index + inline void set_oop ( jobject obj, Register d ); // uses allocate_oop_address + inline void set_oop_constant( jobject obj, Register d ); // uses constant_oop_address + inline void set_oop ( Address obj_addr ); // same as load_address + + // nop padding + void align(int modulus); + + // declare a safepoint + void safepoint(); + + // factor out part of stop into subroutine to save space + void stop_subroutine(); + // factor out part of verify_oop into subroutine to save space + void verify_oop_subroutine(); + + // side-door communication with signalHandler in os_solaris.cpp + static address _verify_oop_implicit_branch[3]; + +#ifndef PRODUCT + static void test(); +#endif + + // convert an incoming arglist to varargs format; put the pointer in d + void set_varargs( Argument a, Register d ); + + int total_frame_size_in_bytes(int extraWords); + + // used when extraWords known statically + void save_frame(int extraWords); + void save_frame_c1(int size_in_bytes); + // make a frame, and simultaneously pass up one or two register value + // into the new register window + void save_frame_and_mov(int extraWords, Register s1, Register d1, Register s2 = Register(), Register d2 = Register()); + + // give no. (outgoing) params, calc # of words will need on frame + void calc_mem_param_words(Register Rparam_words, Register Rresult); + + // used to calculate frame size dynamically + // result is in bytes and must be negated for save inst + void calc_frame_size(Register extraWords, Register resultReg); + + // calc and also save + void calc_frame_size_and_save(Register extraWords, Register resultReg); + + static void debug(char* msg, RegistersForDebugging* outWindow); + + // implementations of bytecodes used by both interpreter and compiler + + void lcmp( Register Ra_hi, Register Ra_low, + Register Rb_hi, Register Rb_low, + Register Rresult); + + void lneg( Register Rhi, Register Rlow ); + + void lshl( Register Rin_high, Register Rin_low, Register Rcount, + Register Rout_high, Register Rout_low, Register Rtemp ); + + void lshr( Register Rin_high, Register Rin_low, Register Rcount, + Register Rout_high, Register Rout_low, Register Rtemp ); + + void lushr( Register Rin_high, Register Rin_low, Register Rcount, + Register Rout_high, Register Rout_low, Register Rtemp ); + +#ifdef _LP64 + void lcmp( Register Ra, Register Rb, Register Rresult); +#endif + + void float_cmp( bool is_float, int unordered_result, + FloatRegister Fa, FloatRegister Fb, + Register Rresult); + + void fneg( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d); + void fneg( FloatRegisterImpl::Width w, FloatRegister sd ) { Assembler::fneg(w, sd); } + void fmov( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d); + void fabs( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d); + + void save_all_globals_into_locals(); + void restore_globals_from_locals(); + + void casx_under_lock(Register top_ptr_reg, Register top_reg, Register ptr_reg, + address lock_addr=0, bool use_call_vm=false); + void cas_under_lock(Register top_ptr_reg, Register top_reg, Register ptr_reg, + address lock_addr=0, bool use_call_vm=false); + void casn (Register addr_reg, Register cmp_reg, Register set_reg) ; + + // These set the icc condition code to equal if the lock succeeded + // and notEqual if it failed and requires a slow case + void compiler_lock_object(Register Roop, Register Rmark, Register Rbox, Register Rscratch, + BiasedLockingCounters* counters = NULL); + void compiler_unlock_object(Register Roop, Register Rmark, Register Rbox, Register Rscratch); + + // Biased locking support + // Upon entry, lock_reg must point to the lock record on the stack, + // obj_reg must contain the target object, and mark_reg must contain + // the target object's header. + // Destroys mark_reg if an attempt is made to bias an anonymously + // biased lock. In this case a failure will go either to the slow + // case or fall through with the notEqual condition code set with + // the expectation that the slow case in the runtime will be called. + // In the fall-through case where the CAS-based lock is done, + // mark_reg is not destroyed. + void biased_locking_enter(Register obj_reg, Register mark_reg, Register temp_reg, + Label& done, Label* slow_case = NULL, + BiasedLockingCounters* counters = NULL); + // Upon entry, the base register of mark_addr must contain the oop. + // Destroys temp_reg. + + // If allow_delay_slot_filling is set to true, the next instruction + // emitted after this one will go in an annulled delay slot if the + // biased locking exit case failed. + void biased_locking_exit(Address mark_addr, Register temp_reg, Label& done, bool allow_delay_slot_filling = false); + + // allocation + void eden_allocate( + Register obj, // result: pointer to object after successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Register t2, // temp register + Label& slow_case // continuation point if fast allocation fails + ); + void tlab_allocate( + Register obj, // result: pointer to object after successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Label& slow_case // continuation point if fast allocation fails + ); + void tlab_refill(Label& retry_tlab, Label& try_eden, Label& slow_case); + + // Stack overflow checking + + // Note: this clobbers G3_scratch + void bang_stack_with_offset(int offset) { + // stack grows down, caller passes positive offset + assert(offset > 0, "must bang with negative offset"); + set((-offset)+STACK_BIAS, G3_scratch); + st(G0, SP, G3_scratch); + } + + // Writes to stack successive pages until offset reached to check for + // stack overflow + shadow pages. Clobbers tsp and scratch registers. + void bang_stack_size(Register Rsize, Register Rtsp, Register Rscratch); + + void verify_tlab(); + + Condition negate_condition(Condition cond); + + // Helper functions for statistics gathering. + // Conditionally (non-atomically) increments passed counter address, preserving condition codes. + void cond_inc(Condition cond, address counter_addr, Register Rtemp1, Register Rtemp2); + // Unconditional increment. + void inc_counter(address counter_addr, Register Rtemp1, Register Rtemp2); + +#undef VIRTUAL + +}; + +/** + * class SkipIfEqual: + * + * Instantiating this class will result in assembly code being output that will + * jump around any code emitted between the creation of the instance and it's + * automatic destruction at the end of a scope block, depending on the value of + * the flag passed to the constructor, which will be checked at run-time. + */ +class SkipIfEqual : public StackObj { + private: + MacroAssembler* _masm; + Label _label; + + public: + // 'temp' is a temp register that this object can use (and trash) + SkipIfEqual(MacroAssembler*, Register temp, + const bool* flag_addr, Assembler::Condition condition); + ~SkipIfEqual(); +}; + +#ifdef ASSERT +// On RISC, there's no benefit to verifying instruction boundaries. +inline bool AbstractAssembler::pd_check_instruction_mark() { return false; } +#endif diff --git a/hotspot/src/cpu/sparc/vm/assembler_sparc.inline.hpp b/hotspot/src/cpu/sparc/vm/assembler_sparc.inline.hpp new file mode 100644 index 00000000000..0efaa846b1d --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/assembler_sparc.inline.hpp @@ -0,0 +1,687 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void MacroAssembler::pd_patch_instruction(address branch, address target) { + jint& stub_inst = *(jint*) branch; + stub_inst = patched_branch(target - branch, stub_inst, 0); +} + +#ifndef PRODUCT +inline void MacroAssembler::pd_print_patched_instruction(address branch) { + jint stub_inst = *(jint*) branch; + print_instruction(stub_inst); + ::tty->print("%s", " (unresolved)"); +} +#endif // PRODUCT + +inline bool Address::is_simm13(int offset) { return Assembler::is_simm13(disp() + offset); } + + +// inlines for SPARC assembler -- dmu 5/97 + +inline void Assembler::check_delay() { +# ifdef CHECK_DELAY + guarantee( delay_state != at_delay_slot, "must say delayed() when filling delay slot"); + delay_state = no_delay; +# endif +} + +inline void Assembler::emit_long(int x) { + check_delay(); + AbstractAssembler::emit_long(x); +} + +inline void Assembler::emit_data(int x, relocInfo::relocType rtype) { + relocate(rtype); + emit_long(x); +} + +inline void Assembler::emit_data(int x, RelocationHolder const& rspec) { + relocate(rspec); + emit_long(x); +} + + +inline void Assembler::add( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(add_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::add( Register s1, int simm13a, Register d, relocInfo::relocType rtype ) { emit_data( op(arith_op) | rd(d) | op3(add_op3) | rs1(s1) | immed(true) | simm(simm13a, 13), rtype ); } +inline void Assembler::add( Register s1, int simm13a, Register d, RelocationHolder const& rspec ) { emit_data( op(arith_op) | rd(d) | op3(add_op3) | rs1(s1) | immed(true) | simm(simm13a, 13), rspec ); } +inline void Assembler::add( const Address& a, Register d, int offset) { add( a.base(), a.disp() + offset, d, a.rspec(offset)); } + +inline void Assembler::bpr( RCondition c, bool a, Predict p, Register s1, address d, relocInfo::relocType rt ) { v9_only(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(bpr_op2) | wdisp16(intptr_t(d), intptr_t(pc())) | predict(p) | rs1(s1), rt); has_delay_slot(); } +inline void Assembler::bpr( RCondition c, bool a, Predict p, Register s1, Label& L) { bpr( c, a, p, s1, target(L)); } + +inline void Assembler::fb( Condition c, bool a, address d, relocInfo::relocType rt ) { v9_dep(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(fb_op2) | wdisp(intptr_t(d), intptr_t(pc()), 22), rt); has_delay_slot(); } +inline void Assembler::fb( Condition c, bool a, Label& L ) { fb(c, a, target(L)); } + +inline void Assembler::fbp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt ) { v9_only(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(fbp_op2) | branchcc(cc) | predict(p) | wdisp(intptr_t(d), intptr_t(pc()), 19), rt); has_delay_slot(); } +inline void Assembler::fbp( Condition c, bool a, CC cc, Predict p, Label& L ) { fbp(c, a, cc, p, target(L)); } + +inline void Assembler::cb( Condition c, bool a, address d, relocInfo::relocType rt ) { v8_only(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(cb_op2) | wdisp(intptr_t(d), intptr_t(pc()), 22), rt); has_delay_slot(); } +inline void Assembler::cb( Condition c, bool a, Label& L ) { cb(c, a, target(L)); } + +inline void Assembler::br( Condition c, bool a, address d, relocInfo::relocType rt ) { v9_dep(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(br_op2) | wdisp(intptr_t(d), intptr_t(pc()), 22), rt); has_delay_slot(); } +inline void Assembler::br( Condition c, bool a, Label& L ) { br(c, a, target(L)); } + +inline void Assembler::bp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt ) { v9_only(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(bp_op2) | branchcc(cc) | predict(p) | wdisp(intptr_t(d), intptr_t(pc()), 19), rt); has_delay_slot(); } +inline void Assembler::bp( Condition c, bool a, CC cc, Predict p, Label& L ) { bp(c, a, cc, p, target(L)); } + +inline void Assembler::call( address d, relocInfo::relocType rt ) { emit_data( op(call_op) | wdisp(intptr_t(d), intptr_t(pc()), 30), rt); has_delay_slot(); assert(rt != relocInfo::virtual_call_type, "must use virtual_call_Relocation::spec"); } +inline void Assembler::call( Label& L, relocInfo::relocType rt ) { call( target(L), rt); } + +inline void Assembler::flush( Register s1, Register s2) { emit_long( op(arith_op) | op3(flush_op3) | rs1(s1) | rs2(s2)); } +inline void Assembler::flush( Register s1, int simm13a) { emit_data( op(arith_op) | op3(flush_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +inline void Assembler::jmpl( Register s1, Register s2, Register d ) { emit_long( op(arith_op) | rd(d) | op3(jmpl_op3) | rs1(s1) | rs2(s2)); has_delay_slot(); } +inline void Assembler::jmpl( Register s1, int simm13a, Register d, RelocationHolder const& rspec ) { emit_data( op(arith_op) | rd(d) | op3(jmpl_op3) | rs1(s1) | immed(true) | simm(simm13a, 13), rspec); has_delay_slot(); } + +inline void Assembler::jmpl( Address& a, Register d, int offset) { jmpl( a.base(), a.disp() + offset, d, a.rspec(offset)); } + + +inline void Assembler::ldf( FloatRegisterImpl::Width w, Register s1, Register s2, FloatRegister d) { emit_long( op(ldst_op) | fd(d, w) | alt_op3(ldf_op3, w) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldf( FloatRegisterImpl::Width w, Register s1, int simm13a, FloatRegister d) { emit_data( op(ldst_op) | fd(d, w) | alt_op3(ldf_op3, w) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +inline void Assembler::ldf( FloatRegisterImpl::Width w, const Address& a, FloatRegister d, int offset) { relocate(a.rspec(offset)); ldf( w, a.base(), a.disp() + offset, d); } + +inline void Assembler::ldfsr( Register s1, Register s2) { v9_dep(); emit_long( op(ldst_op) | op3(ldfsr_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldfsr( Register s1, int simm13a) { v9_dep(); emit_data( op(ldst_op) | op3(ldfsr_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::ldxfsr( Register s1, Register s2) { v9_only(); emit_long( op(ldst_op) | rd(G1) | op3(ldfsr_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldxfsr( Register s1, int simm13a) { v9_only(); emit_data( op(ldst_op) | rd(G1) | op3(ldfsr_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +inline void Assembler::ldc( Register s1, Register s2, int crd) { v8_only(); emit_long( op(ldst_op) | fcn(crd) | op3(ldc_op3 ) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldc( Register s1, int simm13a, int crd) { v8_only(); emit_data( op(ldst_op) | fcn(crd) | op3(ldc_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::lddc( Register s1, Register s2, int crd) { v8_only(); emit_long( op(ldst_op) | fcn(crd) | op3(lddc_op3 ) | rs1(s1) | rs2(s2) ); } +inline void Assembler::lddc( Register s1, int simm13a, int crd) { v8_only(); emit_data( op(ldst_op) | fcn(crd) | op3(lddc_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::ldcsr( Register s1, Register s2, int crd) { v8_only(); emit_long( op(ldst_op) | fcn(crd) | op3(ldcsr_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldcsr( Register s1, int simm13a, int crd) { v8_only(); emit_data( op(ldst_op) | fcn(crd) | op3(ldcsr_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +inline void Assembler::ldsb( Register s1, Register s2, Register d) { emit_long( op(ldst_op) | rd(d) | op3(ldsb_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldsb( Register s1, int simm13a, Register d) { emit_data( op(ldst_op) | rd(d) | op3(ldsb_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +inline void Assembler::ldsh( Register s1, Register s2, Register d) { emit_long( op(ldst_op) | rd(d) | op3(ldsh_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldsh( Register s1, int simm13a, Register d) { emit_data( op(ldst_op) | rd(d) | op3(ldsh_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::ldsw( Register s1, Register s2, Register d) { emit_long( op(ldst_op) | rd(d) | op3(ldsw_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldsw( Register s1, int simm13a, Register d) { emit_data( op(ldst_op) | rd(d) | op3(ldsw_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::ldub( Register s1, Register s2, Register d) { emit_long( op(ldst_op) | rd(d) | op3(ldub_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldub( Register s1, int simm13a, Register d) { emit_data( op(ldst_op) | rd(d) | op3(ldub_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::lduh( Register s1, Register s2, Register d) { emit_long( op(ldst_op) | rd(d) | op3(lduh_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::lduh( Register s1, int simm13a, Register d) { emit_data( op(ldst_op) | rd(d) | op3(lduh_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::lduw( Register s1, Register s2, Register d) { emit_long( op(ldst_op) | rd(d) | op3(lduw_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::lduw( Register s1, int simm13a, Register d) { emit_data( op(ldst_op) | rd(d) | op3(lduw_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +inline void Assembler::ldx( Register s1, Register s2, Register d) { v9_only(); emit_long( op(ldst_op) | rd(d) | op3(ldx_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldx( Register s1, int simm13a, Register d) { v9_only(); emit_data( op(ldst_op) | rd(d) | op3(ldx_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::ldd( Register s1, Register s2, Register d) { v9_dep(); assert(d->is_even(), "not even"); emit_long( op(ldst_op) | rd(d) | op3(ldd_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldd( Register s1, int simm13a, Register d) { v9_dep(); assert(d->is_even(), "not even"); emit_data( op(ldst_op) | rd(d) | op3(ldd_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +#ifdef _LP64 +// Make all 32 bit loads signed so 64 bit registers maintain proper sign +inline void Assembler::ld( Register s1, Register s2, Register d) { ldsw( s1, s2, d); } +inline void Assembler::ld( Register s1, int simm13a, Register d) { ldsw( s1, simm13a, d); } +#else +inline void Assembler::ld( Register s1, Register s2, Register d) { lduw( s1, s2, d); } +inline void Assembler::ld( Register s1, int simm13a, Register d) { lduw( s1, simm13a, d); } +#endif + + +inline void Assembler::ld( const Address& a, Register d, int offset ) { relocate(a.rspec(offset)); ld( a.base(), a.disp() + offset, d ); } +inline void Assembler::ldsb( const Address& a, Register d, int offset ) { relocate(a.rspec(offset)); ldsb( a.base(), a.disp() + offset, d ); } +inline void Assembler::ldsh( const Address& a, Register d, int offset ) { relocate(a.rspec(offset)); ldsh( a.base(), a.disp() + offset, d ); } +inline void Assembler::ldsw( const Address& a, Register d, int offset ) { relocate(a.rspec(offset)); ldsw( a.base(), a.disp() + offset, d ); } +inline void Assembler::ldub( const Address& a, Register d, int offset ) { relocate(a.rspec(offset)); ldub( a.base(), a.disp() + offset, d ); } +inline void Assembler::lduh( const Address& a, Register d, int offset ) { relocate(a.rspec(offset)); lduh( a.base(), a.disp() + offset, d ); } +inline void Assembler::lduw( const Address& a, Register d, int offset ) { relocate(a.rspec(offset)); lduw( a.base(), a.disp() + offset, d ); } +inline void Assembler::ldd( const Address& a, Register d, int offset ) { relocate(a.rspec(offset)); ldd( a.base(), a.disp() + offset, d ); } +inline void Assembler::ldx( const Address& a, Register d, int offset ) { relocate(a.rspec(offset)); ldx( a.base(), a.disp() + offset, d ); } + + +inline void Assembler::ldstub( Register s1, Register s2, Register d) { emit_long( op(ldst_op) | rd(d) | op3(ldstub_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::ldstub( Register s1, int simm13a, Register d) { emit_data( op(ldst_op) | rd(d) | op3(ldstub_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + + +inline void Assembler::prefetch(Register s1, Register s2, PrefetchFcn f) { v9_only(); emit_long( op(ldst_op) | fcn(f) | op3(prefetch_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::prefetch(Register s1, int simm13a, PrefetchFcn f) { v9_only(); emit_data( op(ldst_op) | fcn(f) | op3(prefetch_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +inline void Assembler::prefetch(const Address& a, PrefetchFcn f, int offset) { v9_only(); relocate(a.rspec(offset)); prefetch(a.base(), a.disp() + offset, f); } + + +inline void Assembler::rett( Register s1, Register s2 ) { emit_long( op(arith_op) | op3(rett_op3) | rs1(s1) | rs2(s2)); has_delay_slot(); } +inline void Assembler::rett( Register s1, int simm13a, relocInfo::relocType rt) { emit_data( op(arith_op) | op3(rett_op3) | rs1(s1) | immed(true) | simm(simm13a, 13), rt); has_delay_slot(); } + +inline void Assembler::sethi( int imm22a, Register d, RelocationHolder const& rspec ) { emit_data( op(branch_op) | rd(d) | op2(sethi_op2) | hi22(imm22a), rspec); } + + // pp 222 + +inline void Assembler::stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, Register s2) { emit_long( op(ldst_op) | fd(d, w) | alt_op3(stf_op3, w) | rs1(s1) | rs2(s2) ); } +inline void Assembler::stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, int simm13a) { emit_data( op(ldst_op) | fd(d, w) | alt_op3(stf_op3, w) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +inline void Assembler::stf( FloatRegisterImpl::Width w, FloatRegister d, const Address& a, int offset) { relocate(a.rspec(offset)); stf(w, d, a.base(), a.disp() + offset); } + +inline void Assembler::stfsr( Register s1, Register s2) { v9_dep(); emit_long( op(ldst_op) | op3(stfsr_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::stfsr( Register s1, int simm13a) { v9_dep(); emit_data( op(ldst_op) | op3(stfsr_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::stxfsr( Register s1, Register s2) { v9_only(); emit_long( op(ldst_op) | rd(G1) | op3(stfsr_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::stxfsr( Register s1, int simm13a) { v9_only(); emit_data( op(ldst_op) | rd(G1) | op3(stfsr_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + + // p 226 + +inline void Assembler::stb( Register d, Register s1, Register s2) { emit_long( op(ldst_op) | rd(d) | op3(stb_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::stb( Register d, Register s1, int simm13a) { emit_data( op(ldst_op) | rd(d) | op3(stb_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::sth( Register d, Register s1, Register s2) { emit_long( op(ldst_op) | rd(d) | op3(sth_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::sth( Register d, Register s1, int simm13a) { emit_data( op(ldst_op) | rd(d) | op3(sth_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::stw( Register d, Register s1, Register s2) { emit_long( op(ldst_op) | rd(d) | op3(stw_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::stw( Register d, Register s1, int simm13a) { emit_data( op(ldst_op) | rd(d) | op3(stw_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + + +inline void Assembler::stx( Register d, Register s1, Register s2) { v9_only(); emit_long( op(ldst_op) | rd(d) | op3(stx_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::stx( Register d, Register s1, int simm13a) { v9_only(); emit_data( op(ldst_op) | rd(d) | op3(stx_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::std( Register d, Register s1, Register s2) { v9_dep(); assert(d->is_even(), "not even"); emit_long( op(ldst_op) | rd(d) | op3(std_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::std( Register d, Register s1, int simm13a) { v9_dep(); assert(d->is_even(), "not even"); emit_data( op(ldst_op) | rd(d) | op3(std_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +inline void Assembler::st( Register d, Register s1, Register s2) { stw(d, s1, s2); } +inline void Assembler::st( Register d, Register s1, int simm13a) { stw(d, s1, simm13a); } + +inline void Assembler::stb( Register d, const Address& a, int offset) { relocate(a.rspec(offset)); stb( d, a.base(), a.disp() + offset); } +inline void Assembler::sth( Register d, const Address& a, int offset) { relocate(a.rspec(offset)); sth( d, a.base(), a.disp() + offset); } +inline void Assembler::stw( Register d, const Address& a, int offset) { relocate(a.rspec(offset)); stw( d, a.base(), a.disp() + offset); } +inline void Assembler::st( Register d, const Address& a, int offset) { relocate(a.rspec(offset)); st( d, a.base(), a.disp() + offset); } +inline void Assembler::std( Register d, const Address& a, int offset) { relocate(a.rspec(offset)); std( d, a.base(), a.disp() + offset); } +inline void Assembler::stx( Register d, const Address& a, int offset) { relocate(a.rspec(offset)); stx( d, a.base(), a.disp() + offset); } + +// v8 p 99 + +inline void Assembler::stc( int crd, Register s1, Register s2) { v8_only(); emit_long( op(ldst_op) | fcn(crd) | op3(stc_op3 ) | rs1(s1) | rs2(s2) ); } +inline void Assembler::stc( int crd, Register s1, int simm13a) { v8_only(); emit_data( op(ldst_op) | fcn(crd) | op3(stc_op3 ) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::stdc( int crd, Register s1, Register s2) { v8_only(); emit_long( op(ldst_op) | fcn(crd) | op3(stdc_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::stdc( int crd, Register s1, int simm13a) { v8_only(); emit_data( op(ldst_op) | fcn(crd) | op3(stdc_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::stcsr( int crd, Register s1, Register s2) { v8_only(); emit_long( op(ldst_op) | fcn(crd) | op3(stcsr_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::stcsr( int crd, Register s1, int simm13a) { v8_only(); emit_data( op(ldst_op) | fcn(crd) | op3(stcsr_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } +inline void Assembler::stdcq( int crd, Register s1, Register s2) { v8_only(); emit_long( op(ldst_op) | fcn(crd) | op3(stdcq_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::stdcq( int crd, Register s1, int simm13a) { v8_only(); emit_data( op(ldst_op) | fcn(crd) | op3(stdcq_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + + +// pp 231 + +inline void Assembler::swap( Register s1, Register s2, Register d) { v9_dep(); emit_long( op(ldst_op) | rd(d) | op3(swap_op3) | rs1(s1) | rs2(s2) ); } +inline void Assembler::swap( Register s1, int simm13a, Register d) { v9_dep(); emit_data( op(ldst_op) | rd(d) | op3(swap_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } + +inline void Assembler::swap( Address& a, Register d, int offset ) { relocate(a.rspec(offset)); swap( a.base(), a.disp() + offset, d ); } + + +// Use the right loads/stores for the platform +inline void MacroAssembler::ld_ptr( Register s1, Register s2, Register d ) { +#ifdef _LP64 + Assembler::ldx( s1, s2, d); +#else + Assembler::ld( s1, s2, d); +#endif +} + +inline void MacroAssembler::ld_ptr( Register s1, int simm13a, Register d ) { +#ifdef _LP64 + Assembler::ldx( s1, simm13a, d); +#else + Assembler::ld( s1, simm13a, d); +#endif +} + +inline void MacroAssembler::ld_ptr( const Address& a, Register d, int offset ) { +#ifdef _LP64 + Assembler::ldx( a, d, offset ); +#else + Assembler::ld( a, d, offset ); +#endif +} + +inline void MacroAssembler::st_ptr( Register d, Register s1, Register s2 ) { +#ifdef _LP64 + Assembler::stx( d, s1, s2); +#else + Assembler::st( d, s1, s2); +#endif +} + +inline void MacroAssembler::st_ptr( Register d, Register s1, int simm13a ) { +#ifdef _LP64 + Assembler::stx( d, s1, simm13a); +#else + Assembler::st( d, s1, simm13a); +#endif +} + +inline void MacroAssembler::st_ptr( Register d, const Address& a, int offset) { +#ifdef _LP64 + Assembler::stx( d, a, offset); +#else + Assembler::st( d, a, offset); +#endif +} + +// Use the right loads/stores for the platform +inline void MacroAssembler::ld_long( Register s1, Register s2, Register d ) { +#ifdef _LP64 + Assembler::ldx(s1, s2, d); +#else + Assembler::ldd(s1, s2, d); +#endif +} + +inline void MacroAssembler::ld_long( Register s1, int simm13a, Register d ) { +#ifdef _LP64 + Assembler::ldx(s1, simm13a, d); +#else + Assembler::ldd(s1, simm13a, d); +#endif +} + +inline void MacroAssembler::ld_long( const Address& a, Register d, int offset ) { +#ifdef _LP64 + Assembler::ldx(a, d, offset ); +#else + Assembler::ldd(a, d, offset ); +#endif +} + +inline void MacroAssembler::st_long( Register d, Register s1, Register s2 ) { +#ifdef _LP64 + Assembler::stx(d, s1, s2); +#else + Assembler::std(d, s1, s2); +#endif +} + +inline void MacroAssembler::st_long( Register d, Register s1, int simm13a ) { +#ifdef _LP64 + Assembler::stx(d, s1, simm13a); +#else + Assembler::std(d, s1, simm13a); +#endif +} + +inline void MacroAssembler::st_long( Register d, const Address& a, int offset ) { +#ifdef _LP64 + Assembler::stx(d, a, offset); +#else + Assembler::std(d, a, offset); +#endif +} + +// Functions for isolating 64 bit shifts for LP64 + +inline void MacroAssembler::sll_ptr( Register s1, Register s2, Register d ) { +#ifdef _LP64 + Assembler::sllx(s1, s2, d); +#else + Assembler::sll(s1, s2, d); +#endif +} + +inline void MacroAssembler::sll_ptr( Register s1, int imm6a, Register d ) { +#ifdef _LP64 + Assembler::sllx(s1, imm6a, d); +#else + Assembler::sll(s1, imm6a, d); +#endif +} + +inline void MacroAssembler::srl_ptr( Register s1, Register s2, Register d ) { +#ifdef _LP64 + Assembler::srlx(s1, s2, d); +#else + Assembler::srl(s1, s2, d); +#endif +} + +inline void MacroAssembler::srl_ptr( Register s1, int imm6a, Register d ) { +#ifdef _LP64 + Assembler::srlx(s1, imm6a, d); +#else + Assembler::srl(s1, imm6a, d); +#endif +} + +// Use the right branch for the platform + +inline void MacroAssembler::br( Condition c, bool a, Predict p, address d, relocInfo::relocType rt ) { + if (VM_Version::v9_instructions_work()) + Assembler::bp(c, a, icc, p, d, rt); + else + Assembler::br(c, a, d, rt); +} + +inline void MacroAssembler::br( Condition c, bool a, Predict p, Label& L ) { + br(c, a, p, target(L)); +} + + +// Branch that tests either xcc or icc depending on the +// architecture compiled (LP64 or not) +inline void MacroAssembler::brx( Condition c, bool a, Predict p, address d, relocInfo::relocType rt ) { +#ifdef _LP64 + Assembler::bp(c, a, xcc, p, d, rt); +#else + MacroAssembler::br(c, a, p, d, rt); +#endif +} + +inline void MacroAssembler::brx( Condition c, bool a, Predict p, Label& L ) { + brx(c, a, p, target(L)); +} + +inline void MacroAssembler::ba( bool a, Label& L ) { + br(always, a, pt, L); +} + +// Warning: V9 only functions +inline void MacroAssembler::bp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt ) { + Assembler::bp(c, a, cc, p, d, rt); +} + +inline void MacroAssembler::bp( Condition c, bool a, CC cc, Predict p, Label& L ) { + Assembler::bp(c, a, cc, p, L); +} + +inline void MacroAssembler::fb( Condition c, bool a, Predict p, address d, relocInfo::relocType rt ) { + if (VM_Version::v9_instructions_work()) + fbp(c, a, fcc0, p, d, rt); + else + Assembler::fb(c, a, d, rt); +} + +inline void MacroAssembler::fb( Condition c, bool a, Predict p, Label& L ) { + fb(c, a, p, target(L)); +} + +inline void MacroAssembler::fbp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt ) { + Assembler::fbp(c, a, cc, p, d, rt); +} + +inline void MacroAssembler::fbp( Condition c, bool a, CC cc, Predict p, Label& L ) { + Assembler::fbp(c, a, cc, p, L); +} + +inline void MacroAssembler::jmp( Register s1, Register s2 ) { jmpl( s1, s2, G0 ); } +inline void MacroAssembler::jmp( Register s1, int simm13a, RelocationHolder const& rspec ) { jmpl( s1, simm13a, G0, rspec); } + +// Call with a check to see if we need to deal with the added +// expense of relocation and if we overflow the displacement +// of the quick call instruction./ +// Check to see if we have to deal with relocations +inline void MacroAssembler::call( address d, relocInfo::relocType rt ) { +#ifdef _LP64 + intptr_t disp; + // NULL is ok because it will be relocated later. + // Must change NULL to a reachable address in order to + // pass asserts here and in wdisp. + if ( d == NULL ) + d = pc(); + + // Is this address within range of the call instruction? + // If not, use the expensive instruction sequence + disp = (intptr_t)d - (intptr_t)pc(); + if ( disp != (intptr_t)(int32_t)disp ) { + relocate(rt); + Address dest(O7, (address)d); + sethi(dest, /*ForceRelocatable=*/ true); + jmpl(dest, O7); + } + else { + Assembler::call( d, rt ); + } +#else + Assembler::call( d, rt ); +#endif +} + +inline void MacroAssembler::call( Label& L, relocInfo::relocType rt ) { + MacroAssembler::call( target(L), rt); +} + + + +inline void MacroAssembler::callr( Register s1, Register s2 ) { jmpl( s1, s2, O7 ); } +inline void MacroAssembler::callr( Register s1, int simm13a, RelocationHolder const& rspec ) { jmpl( s1, simm13a, O7, rspec); } + +// prefetch instruction +inline void MacroAssembler::iprefetch( address d, relocInfo::relocType rt ) { + if (VM_Version::v9_instructions_work()) + Assembler::bp( never, true, xcc, pt, d, rt ); +} +inline void MacroAssembler::iprefetch( Label& L) { iprefetch( target(L) ); } + + +// clobbers o7 on V8!! +// returns delta from gotten pc to addr after +inline int MacroAssembler::get_pc( Register d ) { + int x = offset(); + if (VM_Version::v9_instructions_work()) + rdpc(d); + else { + Label lbl; + Assembler::call(lbl, relocInfo::none); // No relocation as this is call to pc+0x8 + if (d == O7) delayed()->nop(); + else delayed()->mov(O7, d); + bind(lbl); + } + return offset() - x; +} + + +// Note: All MacroAssembler::set_foo functions are defined out-of-line. + + +// Loads the current PC of the following instruction as an immediate value in +// 2 instructions. All PCs in the CodeCache are within 2 Gig of each other. +inline intptr_t MacroAssembler::load_pc_address( Register reg, int bytes_to_skip ) { + intptr_t thepc = (intptr_t)pc() + 2*BytesPerInstWord + bytes_to_skip; +#ifdef _LP64 + Unimplemented(); +#else + Assembler::sethi( thepc & ~0x3ff, reg, internal_word_Relocation::spec((address)thepc)); + Assembler::add(reg,thepc & 0x3ff, reg, internal_word_Relocation::spec((address)thepc)); +#endif + return thepc; +} + +inline void MacroAssembler::load_address( Address& a, int offset ) { + assert_not_delayed(); +#ifdef _LP64 + sethi(a); + add(a, a.base(), offset); +#else + if (a.hi() == 0 && a.rtype() == relocInfo::none) { + set(a.disp() + offset, a.base()); + } + else { + sethi(a); + add(a, a.base(), offset); + } +#endif +} + + +inline void MacroAssembler::split_disp( Address& a, Register temp ) { + assert_not_delayed(); + a = a.split_disp(); + Assembler::sethi(a.hi(), temp, a.rspec()); + add(a.base(), temp, a.base()); +} + + +inline void MacroAssembler::load_contents( Address& a, Register d, int offset ) { + assert_not_delayed(); + sethi(a); + ld(a, d, offset); +} + + +inline void MacroAssembler::load_ptr_contents( Address& a, Register d, int offset ) { + assert_not_delayed(); + sethi(a); + ld_ptr(a, d, offset); +} + + +inline void MacroAssembler::store_contents( Register s, Address& a, int offset ) { + assert_not_delayed(); + sethi(a); + st(s, a, offset); +} + + +inline void MacroAssembler::store_ptr_contents( Register s, Address& a, int offset ) { + assert_not_delayed(); + sethi(a); + st_ptr(s, a, offset); +} + + +// This code sequence is relocatable to any address, even on LP64. +inline void MacroAssembler::jumpl_to( Address& a, Register d, int offset ) { + assert_not_delayed(); + // Force fixed length sethi because NativeJump and NativeFarCall don't handle + // variable length instruction streams. + sethi(a, /*ForceRelocatable=*/ true); + jmpl(a, d, offset); +} + + +inline void MacroAssembler::jump_to( Address& a, int offset ) { + jumpl_to( a, G0, offset ); +} + + +inline void MacroAssembler::set_oop( jobject obj, Register d ) { + set_oop(allocate_oop_address(obj, d)); +} + + +inline void MacroAssembler::set_oop_constant( jobject obj, Register d ) { + set_oop(constant_oop_address(obj, d)); +} + + +inline void MacroAssembler::set_oop( Address obj_addr ) { + assert(obj_addr.rspec().type()==relocInfo::oop_type, "must be an oop reloc"); + load_address(obj_addr); +} + + +inline void MacroAssembler::load_argument( Argument& a, Register d ) { + if (a.is_register()) + mov(a.as_register(), d); + else + ld (a.as_address(), d); +} + +inline void MacroAssembler::store_argument( Register s, Argument& a ) { + if (a.is_register()) + mov(s, a.as_register()); + else + st_ptr (s, a.as_address()); // ABI says everything is right justified. +} + +inline void MacroAssembler::store_ptr_argument( Register s, Argument& a ) { + if (a.is_register()) + mov(s, a.as_register()); + else + st_ptr (s, a.as_address()); +} + + +#ifdef _LP64 +inline void MacroAssembler::store_float_argument( FloatRegister s, Argument& a ) { + if (a.is_float_register()) +// V9 ABI has F1, F3, F5 are used to pass instead of O0, O1, O2 + fmov(FloatRegisterImpl::S, s, a.as_float_register() ); + else + // Floats are stored in the high half of the stack entry + // The low half is undefined per the ABI. + stf(FloatRegisterImpl::S, s, a.as_address(), sizeof(jfloat)); +} + +inline void MacroAssembler::store_double_argument( FloatRegister s, Argument& a ) { + if (a.is_float_register()) +// V9 ABI has D0, D2, D4 are used to pass instead of O0, O1, O2 + fmov(FloatRegisterImpl::D, s, a.as_double_register() ); + else + stf(FloatRegisterImpl::D, s, a.as_address()); +} + +inline void MacroAssembler::store_long_argument( Register s, Argument& a ) { + if (a.is_register()) + mov(s, a.as_register()); + else + stx(s, a.as_address()); +} +#endif + +inline void MacroAssembler::clrb( Register s1, Register s2) { stb( G0, s1, s2 ); } +inline void MacroAssembler::clrh( Register s1, Register s2) { sth( G0, s1, s2 ); } +inline void MacroAssembler::clr( Register s1, Register s2) { stw( G0, s1, s2 ); } +inline void MacroAssembler::clrx( Register s1, Register s2) { stx( G0, s1, s2 ); } + +inline void MacroAssembler::clrb( Register s1, int simm13a) { stb( G0, s1, simm13a); } +inline void MacroAssembler::clrh( Register s1, int simm13a) { sth( G0, s1, simm13a); } +inline void MacroAssembler::clr( Register s1, int simm13a) { stw( G0, s1, simm13a); } +inline void MacroAssembler::clrx( Register s1, int simm13a) { stx( G0, s1, simm13a); } + +// returns if membar generates anything, obviously this code should mirror +// membar below. +inline bool MacroAssembler::membar_has_effect( Membar_mask_bits const7a ) { + if( !os::is_MP() ) return false; // Not needed on single CPU + if( VM_Version::v9_instructions_work() ) { + const Membar_mask_bits effective_mask = + Membar_mask_bits(const7a & ~(LoadLoad | LoadStore | StoreStore)); + return (effective_mask != 0); + } else { + return true; + } +} + +inline void MacroAssembler::membar( Membar_mask_bits const7a ) { + // Uniprocessors do not need memory barriers + if (!os::is_MP()) return; + // Weakened for current Sparcs and TSO. See the v9 manual, sections 8.4.3, + // 8.4.4.3, a.31 and a.50. + if( VM_Version::v9_instructions_work() ) { + // Under TSO, setting bit 3, 2, or 0 is redundant, so the only value + // of the mmask subfield of const7a that does anything that isn't done + // implicitly is StoreLoad. + const Membar_mask_bits effective_mask = + Membar_mask_bits(const7a & ~(LoadLoad | LoadStore | StoreStore)); + if ( effective_mask != 0 ) { + Assembler::membar( effective_mask ); + } + } else { + // stbar is the closest there is on v8. Equivalent to membar(StoreStore). We + // do not issue the stbar because to my knowledge all v8 machines implement TSO, + // which guarantees that all stores behave as if an stbar were issued just after + // each one of them. On these machines, stbar ought to be a nop. There doesn't + // appear to be an equivalent of membar(StoreLoad) on v8: TSO doesn't require it, + // it can't be specified by stbar, nor have I come up with a way to simulate it. + // + // Addendum. Dave says that ldstub guarantees a write buffer flush to coherent + // space. Put one here to be on the safe side. + Assembler::ldstub(SP, 0, G0); + } +} diff --git a/hotspot/src/cpu/sparc/vm/bytecodeInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/bytecodeInterpreter_sparc.cpp new file mode 100644 index 00000000000..e52d7193581 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/bytecodeInterpreter_sparc.cpp @@ -0,0 +1,25 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// KILL THIS FILE diff --git a/hotspot/src/cpu/sparc/vm/bytecodeInterpreter_sparc.hpp b/hotspot/src/cpu/sparc/vm/bytecodeInterpreter_sparc.hpp new file mode 100644 index 00000000000..3cf99c6d4ec --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/bytecodeInterpreter_sparc.hpp @@ -0,0 +1,99 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Platform specific for C++ based Interpreter +#define LOTS_OF_REGS /* Lets interpreter use plenty of registers */ + +private: + + // save the bottom of the stack after frame manager setup. For ease of restoration after return + // from recursive interpreter call + intptr_t* _frame_bottom; /* saved bottom of frame manager frame */ + intptr_t* _last_Java_pc; /* pc to return to in frame manager */ + interpreterState _self_link; /* Previous interpreter state */ /* sometimes points to self??? */ + double _native_fresult; /* save result of native calls that might return floats */ + intptr_t _native_lresult; /* save result of native calls that might return handle/longs */ +public: + + static void pd_layout_interpreterState(interpreterState istate, address last_Java_pc, intptr_t* last_Java_fp); + + +#define SET_LAST_JAVA_FRAME() + +#define RESET_LAST_JAVA_FRAME() THREAD->frame_anchor()->set_flags(0); + +/* + * Macros for accessing the stack. + */ +#undef STACK_INT +#undef STACK_FLOAT +#undef STACK_ADDR +#undef STACK_OBJECT +#undef STACK_DOUBLE +#undef STACK_LONG +// JavaStack Implementation + + +#define GET_STACK_SLOT(offset) (*((intptr_t*) &topOfStack[-(offset)])) +#define STACK_SLOT(offset) ((address) &topOfStack[-(offset)]) +#define STACK_ADDR(offset) (*((address *) &topOfStack[-(offset)])) +#define STACK_INT(offset) (*((jint*) &topOfStack[-(offset)])) +#define STACK_FLOAT(offset) (*((jfloat *) &topOfStack[-(offset)])) +#define STACK_OBJECT(offset) (*((oop *) &topOfStack [-(offset)])) +#define STACK_DOUBLE(offset) (((VMJavaVal64*) &topOfStack[-(offset)])->d) +#define STACK_LONG(offset) (((VMJavaVal64 *) &topOfStack[-(offset)])->l) + +#define SET_STACK_SLOT(value, offset) (*(intptr_t*)&topOfStack[-(offset)] = *(intptr_t*)(value)) +#define SET_STACK_ADDR(value, offset) (*((address *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_INT(value, offset) (*((jint *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_FLOAT(value, offset) (*((jfloat *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_OBJECT(value, offset) (*((oop *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_DOUBLE(value, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->d = (value)) +#define SET_STACK_DOUBLE_FROM_ADDR(addr, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->d = \ + ((VMJavaVal64*)(addr))->d) +#define SET_STACK_LONG(value, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->l = (value)) +#define SET_STACK_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->l = \ + ((VMJavaVal64*)(addr))->l) + +#define LOCALS_SLOT(offset) ((intptr_t*)&locals[-(offset)]) +#define LOCALS_ADDR(offset) ((address)locals[-(offset)]) +#define LOCALS_INT(offset) ((jint)(locals[-(offset)])) +#define LOCALS_FLOAT(offset) (*((jfloat*)&locals[-(offset)])) +#define LOCALS_OBJECT(offset) ((oop)locals[-(offset)]) +#define LOCALS_DOUBLE(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->d) +#define LOCALS_LONG(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->l) +#define LOCALS_LONG_AT(offset) (((address)&locals[-((offset) + 1)])) +#define LOCALS_DOUBLE_AT(offset) (((address)&locals[-((offset) + 1)])) + +#define SET_LOCALS_SLOT(value, offset) (*(intptr_t*)&locals[-(offset)] = *(intptr_t *)(value)) +#define SET_LOCALS_ADDR(value, offset) (*((address *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_INT(value, offset) (*((jint *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_FLOAT(value, offset) (*((jfloat *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_OBJECT(value, offset) (*((oop *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_DOUBLE(value, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->d = (value)) +#define SET_LOCALS_LONG(value, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = (value)) +#define SET_LOCALS_DOUBLE_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->d = \ + ((VMJavaVal64*)(addr))->d) +#define SET_LOCALS_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = \ + ((VMJavaVal64*)(addr))->l) diff --git a/hotspot/src/cpu/sparc/vm/bytecodeInterpreter_sparc.inline.hpp b/hotspot/src/cpu/sparc/vm/bytecodeInterpreter_sparc.inline.hpp new file mode 100644 index 00000000000..35d2545f0ea --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/bytecodeInterpreter_sparc.inline.hpp @@ -0,0 +1,333 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline interpreter functions for sparc + +inline jfloat BytecodeInterpreter::VMfloatAdd(jfloat op1, jfloat op2) { return op1 + op2; } +inline jfloat BytecodeInterpreter::VMfloatSub(jfloat op1, jfloat op2) { return op1 - op2; } +inline jfloat BytecodeInterpreter::VMfloatMul(jfloat op1, jfloat op2) { return op1 * op2; } +inline jfloat BytecodeInterpreter::VMfloatDiv(jfloat op1, jfloat op2) { return op1 / op2; } +inline jfloat BytecodeInterpreter::VMfloatRem(jfloat op1, jfloat op2) { return fmod(op1, op2); } + +inline jfloat BytecodeInterpreter::VMfloatNeg(jfloat op) { return -op; } + +inline int32_t BytecodeInterpreter::VMfloatCompare(jfloat op1, jfloat op2, int32_t direction) { + return ( op1 < op2 ? -1 : + op1 > op2 ? 1 : + op1 == op2 ? 0 : + (direction == -1 || direction == 1) ? direction : 0); + +} + +inline void BytecodeInterpreter::VMmemCopy64(uint32_t to[2], const uint32_t from[2]) { + // x86 can do unaligned copies but not 64bits at a time + to[0] = from[0]; to[1] = from[1]; +} + +// The long operations depend on compiler support for "long long" on x86 + +inline jlong BytecodeInterpreter::VMlongAdd(jlong op1, jlong op2) { + return op1 + op2; +} + +inline jlong BytecodeInterpreter::VMlongAnd(jlong op1, jlong op2) { + return op1 & op2; +} + +inline jlong BytecodeInterpreter::VMlongDiv(jlong op1, jlong op2) { + // QQQ what about check and throw... + return op1 / op2; +} + +inline jlong BytecodeInterpreter::VMlongMul(jlong op1, jlong op2) { + return op1 * op2; +} + +inline jlong BytecodeInterpreter::VMlongOr(jlong op1, jlong op2) { + return op1 | op2; +} + +inline jlong BytecodeInterpreter::VMlongSub(jlong op1, jlong op2) { + return op1 - op2; +} + +inline jlong BytecodeInterpreter::VMlongXor(jlong op1, jlong op2) { + return op1 ^ op2; +} + +inline jlong BytecodeInterpreter::VMlongRem(jlong op1, jlong op2) { + return op1 % op2; +} + +inline jlong BytecodeInterpreter::VMlongUshr(jlong op1, jint op2) { + // CVM did this 0x3f mask, is the really needed??? QQQ + return ((unsigned long long) op1) >> (op2 & 0x3F); +} + +inline jlong BytecodeInterpreter::VMlongShr(jlong op1, jint op2) { + return op1 >> (op2 & 0x3F); +} + +inline jlong BytecodeInterpreter::VMlongShl(jlong op1, jint op2) { + return op1 << (op2 & 0x3F); +} + +inline jlong BytecodeInterpreter::VMlongNeg(jlong op) { + return -op; +} + +inline jlong BytecodeInterpreter::VMlongNot(jlong op) { + return ~op; +} + +inline int32_t BytecodeInterpreter::VMlongLtz(jlong op) { + return (op <= 0); +} + +inline int32_t BytecodeInterpreter::VMlongGez(jlong op) { + return (op >= 0); +} + +inline int32_t BytecodeInterpreter::VMlongEqz(jlong op) { + return (op == 0); +} + +inline int32_t BytecodeInterpreter::VMlongEq(jlong op1, jlong op2) { + return (op1 == op2); +} + +inline int32_t BytecodeInterpreter::VMlongNe(jlong op1, jlong op2) { + return (op1 != op2); +} + +inline int32_t BytecodeInterpreter::VMlongGe(jlong op1, jlong op2) { + return (op1 >= op2); +} + +inline int32_t BytecodeInterpreter::VMlongLe(jlong op1, jlong op2) { + return (op1 <= op2); +} + +inline int32_t BytecodeInterpreter::VMlongLt(jlong op1, jlong op2) { + return (op1 < op2); +} + +inline int32_t BytecodeInterpreter::VMlongGt(jlong op1, jlong op2) { + return (op1 > op2); +} + +inline int32_t BytecodeInterpreter::VMlongCompare(jlong op1, jlong op2) { + return (VMlongLt(op1, op2) ? -1 : VMlongGt(op1, op2) ? 1 : 0); +} + +// Long conversions + +inline jdouble BytecodeInterpreter::VMlong2Double(jlong val) { + return (jdouble) val; +} + +inline jfloat BytecodeInterpreter::VMlong2Float(jlong val) { + return (jfloat) val; +} + +inline jint BytecodeInterpreter::VMlong2Int(jlong val) { + return (jint) val; +} + +// Double Arithmetic + +inline jdouble BytecodeInterpreter::VMdoubleAdd(jdouble op1, jdouble op2) { + return op1 + op2; +} + +inline jdouble BytecodeInterpreter::VMdoubleDiv(jdouble op1, jdouble op2) { + // Divide by zero... QQQ + return op1 / op2; +} + +inline jdouble BytecodeInterpreter::VMdoubleMul(jdouble op1, jdouble op2) { + return op1 * op2; +} + +inline jdouble BytecodeInterpreter::VMdoubleNeg(jdouble op) { + return -op; +} + +inline jdouble BytecodeInterpreter::VMdoubleRem(jdouble op1, jdouble op2) { + return fmod(op1, op2); +} + +inline jdouble BytecodeInterpreter::VMdoubleSub(jdouble op1, jdouble op2) { + return op1 - op2; +} + +inline int32_t BytecodeInterpreter::VMdoubleCompare(jdouble op1, jdouble op2, int32_t direction) { + return ( op1 < op2 ? -1 : + op1 > op2 ? 1 : + op1 == op2 ? 0 : + (direction == -1 || direction == 1) ? direction : 0); +} + +// Double Conversions + +inline jfloat BytecodeInterpreter::VMdouble2Float(jdouble val) { + return (jfloat) val; +} + +// Float Conversions + +inline jdouble BytecodeInterpreter::VMfloat2Double(jfloat op) { + return (jdouble) op; +} + +// Integer Arithmetic + +inline jint BytecodeInterpreter::VMintAdd(jint op1, jint op2) { + return op1 + op2; +} + +inline jint BytecodeInterpreter::VMintAnd(jint op1, jint op2) { + return op1 & op2; +} + +inline jint BytecodeInterpreter::VMintDiv(jint op1, jint op2) { + /* it's possible we could catch this special case implicitly */ + if (op1 == 0x80000000 && op2 == -1) return op1; + else return op1 / op2; +} + +inline jint BytecodeInterpreter::VMintMul(jint op1, jint op2) { + return op1 * op2; +} + +inline jint BytecodeInterpreter::VMintNeg(jint op) { + return -op; +} + +inline jint BytecodeInterpreter::VMintOr(jint op1, jint op2) { + return op1 | op2; +} + +inline jint BytecodeInterpreter::VMintRem(jint op1, jint op2) { + /* it's possible we could catch this special case implicitly */ + if (op1 == 0x80000000 && op2 == -1) return 0; + else return op1 % op2; +} + +inline jint BytecodeInterpreter::VMintShl(jint op1, jint op2) { + return op1 << op2; +} + +inline jint BytecodeInterpreter::VMintShr(jint op1, jint op2) { + return op1 >> op2; // QQ op2 & 0x1f?? +} + +inline jint BytecodeInterpreter::VMintSub(jint op1, jint op2) { + return op1 - op2; +} + +inline jint BytecodeInterpreter::VMintUshr(jint op1, jint op2) { + return ((juint) op1) >> op2; // QQ op2 & 0x1f?? +} + +inline jint BytecodeInterpreter::VMintXor(jint op1, jint op2) { + return op1 ^ op2; +} + +inline jdouble BytecodeInterpreter::VMint2Double(jint val) { + return (jdouble) val; +} + +inline jfloat BytecodeInterpreter::VMint2Float(jint val) { + return (jfloat) val; +} + +inline jlong BytecodeInterpreter::VMint2Long(jint val) { + return (jlong) val; +} + +inline jchar BytecodeInterpreter::VMint2Char(jint val) { + return (jchar) val; +} + +inline jshort BytecodeInterpreter::VMint2Short(jint val) { + return (jshort) val; +} + +inline jbyte BytecodeInterpreter::VMint2Byte(jint val) { + return (jbyte) val; +} + +// The implementations are platform dependent. We have to worry about alignment +// issues on some machines which can change on the same platform depending on +// whether it is an LP64 machine also. + +// We know that on LP32 mode that longs/doubles are the only thing that gives +// us alignment headaches. We also know that the worst we have is 32bit alignment +// so thing are not really too bad. +// (Also sparcworks compiler does the right thing for free if we don't use -arch.. +// switches. Only gcc gives us a hard time. In LP64 mode I think we have no issue +// with alignment. + +#ifdef _GNU_SOURCE + #define ALIGN_CONVERTER /* Needs alignment converter */ +#else + #undef ALIGN_CONVERTER /* No alignment converter */ +#endif /* _GNU_SOURCE */ + +#ifdef ALIGN_CONVERTER +class u8_converter { + + private: + + public: + static jdouble get_jdouble(address p) { + VMJavaVal64 tmp; + tmp.v[0] = ((uint32_t*)p)[0]; + tmp.v[1] = ((uint32_t*)p)[1]; + return tmp.d; + } + + static void put_jdouble(address p, jdouble d) { + VMJavaVal64 tmp; + tmp.d = d; + ((uint32_t*)p)[0] = tmp.v[0]; + ((uint32_t*)p)[1] = tmp.v[1]; + } + + static jlong get_jlong(address p) { + VMJavaVal64 tmp; + tmp.v[0] = ((uint32_t*)p)[0]; + tmp.v[1] = ((uint32_t*)p)[1]; + return tmp.l; + } + + static void put_jlong(address p, jlong l) { + VMJavaVal64 tmp; + tmp.l = l; + ((uint32_t*)p)[0] = tmp.v[0]; + ((uint32_t*)p)[1] = tmp.v[1]; + } +}; +#endif /* ALIGN_CONVERTER */ diff --git a/hotspot/src/cpu/sparc/vm/bytecodes_sparc.cpp b/hotspot/src/cpu/sparc/vm/bytecodes_sparc.cpp new file mode 100644 index 00000000000..d43edf0b014 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/bytecodes_sparc.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_bytecodes_sparc.cpp.incl" + + +void Bytecodes::pd_initialize() { + // (nothing) +} + +Bytecodes::Code Bytecodes::pd_base_code_for(Code code) { + return code; +} diff --git a/hotspot/src/cpu/sparc/vm/bytecodes_sparc.hpp b/hotspot/src/cpu/sparc/vm/bytecodes_sparc.hpp new file mode 100644 index 00000000000..6d5e8b9304c --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/bytecodes_sparc.hpp @@ -0,0 +1,32 @@ +/* + * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifdef SPARC +#define NLOCALS_IN_REGS 6 +#endif + + +// Sparc specific bytecodes + +// (none) diff --git a/hotspot/src/cpu/sparc/vm/bytes_sparc.hpp b/hotspot/src/cpu/sparc/vm/bytes_sparc.hpp new file mode 100644 index 00000000000..3e0b646b035 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/bytes_sparc.hpp @@ -0,0 +1,157 @@ +/* + * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Bytes: AllStatic { + public: + // Efficient reading and writing of unaligned unsigned data in platform-specific byte ordering + // Sparc needs to check for alignment. + + // can I count on address always being a pointer to an unsigned char? Yes + + // Returns true, if the byte ordering used by Java is different from the nativ byte ordering + // of the underlying machine. For example, true for Intel x86, False, for Solaris on Sparc. + static inline bool is_Java_byte_ordering_different() { return false; } + + // Thus, a swap between native and Java ordering is always a no-op: + static inline u2 swap_u2(u2 x) { return x; } + static inline u4 swap_u4(u4 x) { return x; } + static inline u8 swap_u8(u8 x) { return x; } + + static inline u2 get_native_u2(address p){ + return (intptr_t(p) & 1) == 0 + ? *(u2*)p + : ( u2(p[0]) << 8 ) + | ( u2(p[1]) ); + } + + static inline u4 get_native_u4(address p) { + switch (intptr_t(p) & 3) { + case 0: return *(u4*)p; + + case 2: return ( u4( ((u2*)p)[0] ) << 16 ) + | ( u4( ((u2*)p)[1] ) ); + + default: return ( u4(p[0]) << 24 ) + | ( u4(p[1]) << 16 ) + | ( u4(p[2]) << 8 ) + | u4(p[3]); + } + } + + static inline u8 get_native_u8(address p) { + switch (intptr_t(p) & 7) { + case 0: return *(u8*)p; + + case 4: return ( u8( ((u4*)p)[0] ) << 32 ) + | ( u8( ((u4*)p)[1] ) ); + + case 2: return ( u8( ((u2*)p)[0] ) << 48 ) + | ( u8( ((u2*)p)[1] ) << 32 ) + | ( u8( ((u2*)p)[2] ) << 16 ) + | ( u8( ((u2*)p)[3] ) ); + + default: return ( u8(p[0]) << 56 ) + | ( u8(p[1]) << 48 ) + | ( u8(p[2]) << 40 ) + | ( u8(p[3]) << 32 ) + | ( u8(p[4]) << 24 ) + | ( u8(p[5]) << 16 ) + | ( u8(p[6]) << 8 ) + | u8(p[7]); + } + } + + + + static inline void put_native_u2(address p, u2 x) { + if ( (intptr_t(p) & 1) == 0 ) *(u2*)p = x; + else { + p[0] = x >> 8; + p[1] = x; + } + } + + static inline void put_native_u4(address p, u4 x) { + switch ( intptr_t(p) & 3 ) { + case 0: *(u4*)p = x; + break; + + case 2: ((u2*)p)[0] = x >> 16; + ((u2*)p)[1] = x; + break; + + default: ((u1*)p)[0] = x >> 24; + ((u1*)p)[1] = x >> 16; + ((u1*)p)[2] = x >> 8; + ((u1*)p)[3] = x; + break; + } + } + + static inline void put_native_u8(address p, u8 x) { + switch ( intptr_t(p) & 7 ) { + case 0: *(u8*)p = x; + break; + + case 4: ((u4*)p)[0] = x >> 32; + ((u4*)p)[1] = x; + break; + + case 2: ((u2*)p)[0] = x >> 48; + ((u2*)p)[1] = x >> 32; + ((u2*)p)[2] = x >> 16; + ((u2*)p)[3] = x; + break; + + default: ((u1*)p)[0] = x >> 56; + ((u1*)p)[1] = x >> 48; + ((u1*)p)[2] = x >> 40; + ((u1*)p)[3] = x >> 32; + ((u1*)p)[4] = x >> 24; + ((u1*)p)[5] = x >> 16; + ((u1*)p)[6] = x >> 8; + ((u1*)p)[7] = x; + } + } + + + // Efficient reading and writing of unaligned unsigned data in Java byte ordering (i.e. big-endian ordering) + // (no byte-order reversal is needed since SPARC CPUs are big-endian oriented) + static inline u2 get_Java_u2(address p) { return get_native_u2(p); } + static inline u4 get_Java_u4(address p) { return get_native_u4(p); } + static inline u8 get_Java_u8(address p) { return get_native_u8(p); } + + static inline void put_Java_u2(address p, u2 x) { put_native_u2(p, x); } + static inline void put_Java_u4(address p, u4 x) { put_native_u4(p, x); } + static inline void put_Java_u8(address p, u8 x) { put_native_u8(p, x); } +}; + +//Reconciliation History +// 1.7 98/02/24 10:18:41 bytes_i486.hpp +// 1.10 98/04/08 18:47:57 bytes_i486.hpp +// 1.13 98/07/15 17:10:03 bytes_i486.hpp +// 1.14 98/08/13 10:38:23 bytes_i486.hpp +// 1.15 98/10/05 16:30:21 bytes_i486.hpp +// 1.17 99/06/22 16:37:35 bytes_i486.hpp +//End diff --git a/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp new file mode 100644 index 00000000000..b0caddae967 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp @@ -0,0 +1,407 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_CodeStubs_sparc.cpp.incl" + +#define __ ce->masm()-> + +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, + bool throw_index_out_of_bounds_exception) + : _throw_index_out_of_bounds_exception(throw_index_out_of_bounds_exception) + , _index(index) +{ + _info = new CodeEmitInfo(info); +} + + +void RangeCheckStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + + if (_index->is_register()) { + __ mov(_index->as_register(), G4); + } else { + __ set(_index->as_jint(), G4); + } + if (_throw_index_out_of_bounds_exception) { + __ call(Runtime1::entry_for(Runtime1::throw_index_exception_id), relocInfo::runtime_call_type); + } else { + __ call(Runtime1::entry_for(Runtime1::throw_range_check_failed_id), relocInfo::runtime_call_type); + } + __ delayed()->nop(); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); +#ifdef ASSERT + __ should_not_reach_here(); +#endif +} + +#ifdef TIERED + +void CounterOverflowStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + __ set(_bci, G4); + __ call(Runtime1::entry_for(Runtime1::counter_overflow_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); + + __ br(Assembler::always, true, Assembler::pt, _continuation); + __ delayed()->nop(); +} + +#endif // TIERED + +void DivByZeroStub::emit_code(LIR_Assembler* ce) { + if (_offset != -1) { + ce->compilation()->implicit_exception_table()->append(_offset, __ offset()); + } + __ bind(_entry); + __ call(Runtime1::entry_for(Runtime1::throw_div0_exception_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); +#ifdef ASSERT + __ should_not_reach_here(); +#endif +} + + +void ImplicitNullCheckStub::emit_code(LIR_Assembler* ce) { + ce->compilation()->implicit_exception_table()->append(_offset, __ offset()); + __ bind(_entry); + __ call(Runtime1::entry_for(Runtime1::throw_null_pointer_exception_id), + relocInfo::runtime_call_type); + __ delayed()->nop(); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); +#ifdef ASSERT + __ should_not_reach_here(); +#endif +} + + +// Implementation of SimpleExceptionStub +// Note: %g1 and %g3 are already in use +void SimpleExceptionStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + __ call(Runtime1::entry_for(_stub), relocInfo::runtime_call_type); + + if (_obj->is_valid()) { + __ delayed()->mov(_obj->as_register(), G4); // _obj contains the optional argument to the stub + } else { + __ delayed()->mov(G0, G4); + } + ce->add_call_info_here(_info); +#ifdef ASSERT + __ should_not_reach_here(); +#endif +} + + +// Implementation of ArrayStoreExceptionStub + +ArrayStoreExceptionStub::ArrayStoreExceptionStub(CodeEmitInfo* info): + _info(info) { +} + + +void ArrayStoreExceptionStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + __ call(Runtime1::entry_for(Runtime1::throw_array_store_exception_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); +#ifdef ASSERT + __ should_not_reach_here(); +#endif +} + + + + +// Implementation of NewInstanceStub + +NewInstanceStub::NewInstanceStub(LIR_Opr klass_reg, LIR_Opr result, ciInstanceKlass* klass, CodeEmitInfo* info, Runtime1::StubID stub_id) { + _result = result; + _klass = klass; + _klass_reg = klass_reg; + _info = new CodeEmitInfo(info); + assert(stub_id == Runtime1::new_instance_id || + stub_id == Runtime1::fast_new_instance_id || + stub_id == Runtime1::fast_new_instance_init_check_id, + "need new_instance id"); + _stub_id = stub_id; +} + + +void NewInstanceStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + __ call(Runtime1::entry_for(_stub_id), relocInfo::runtime_call_type); + __ delayed()->mov_or_nop(_klass_reg->as_register(), G5); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); + __ br(Assembler::always, false, Assembler::pt, _continuation); + __ delayed()->mov_or_nop(O0, _result->as_register()); +} + + +// Implementation of NewTypeArrayStub +NewTypeArrayStub::NewTypeArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, CodeEmitInfo* info) { + _klass_reg = klass_reg; + _length = length; + _result = result; + _info = new CodeEmitInfo(info); +} + + +void NewTypeArrayStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + + __ mov(_length->as_register(), G4); + __ call(Runtime1::entry_for(Runtime1::new_type_array_id), relocInfo::runtime_call_type); + __ delayed()->mov_or_nop(_klass_reg->as_register(), G5); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); + __ br(Assembler::always, false, Assembler::pt, _continuation); + __ delayed()->mov_or_nop(O0, _result->as_register()); +} + + +// Implementation of NewObjectArrayStub + +NewObjectArrayStub::NewObjectArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, CodeEmitInfo* info) { + _klass_reg = klass_reg; + _length = length; + _result = result; + _info = new CodeEmitInfo(info); +} + + +void NewObjectArrayStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + + __ mov(_length->as_register(), G4); + __ call(Runtime1::entry_for(Runtime1::new_object_array_id), relocInfo::runtime_call_type); + __ delayed()->mov_or_nop(_klass_reg->as_register(), G5); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); + __ br(Assembler::always, false, Assembler::pt, _continuation); + __ delayed()->mov_or_nop(O0, _result->as_register()); +} + + +// Implementation of MonitorAccessStubs +MonitorEnterStub::MonitorEnterStub(LIR_Opr obj_reg, LIR_Opr lock_reg, CodeEmitInfo* info) + : MonitorAccessStub(obj_reg, lock_reg) { + _info = new CodeEmitInfo(info); +} + + +void MonitorEnterStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + __ mov(_obj_reg->as_register(), G4); + if (ce->compilation()->has_fpu_code()) { + __ call(Runtime1::entry_for(Runtime1::monitorenter_id), relocInfo::runtime_call_type); + } else { + __ call(Runtime1::entry_for(Runtime1::monitorenter_nofpu_id), relocInfo::runtime_call_type); + } + __ delayed()->mov_or_nop(_lock_reg->as_register(), G5); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); + __ br(Assembler::always, true, Assembler::pt, _continuation); + __ delayed()->nop(); +} + + +void MonitorExitStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + if (_compute_lock) { + ce->monitor_address(_monitor_ix, _lock_reg); + } + if (ce->compilation()->has_fpu_code()) { + __ call(Runtime1::entry_for(Runtime1::monitorexit_id), relocInfo::runtime_call_type); + } else { + __ call(Runtime1::entry_for(Runtime1::monitorexit_nofpu_id), relocInfo::runtime_call_type); + } + + __ delayed()->mov_or_nop(_lock_reg->as_register(), G4); + __ br(Assembler::always, true, Assembler::pt, _continuation); + __ delayed()->nop(); +} + +// Implementation of patching: +// - Copy the code at given offset to an inlined buffer (first the bytes, then the number of bytes) +// - Replace original code with a call to the stub +// At Runtime: +// - call to stub, jump to runtime +// - in runtime: preserve all registers (especially objects, i.e., source and destination object) +// - in runtime: after initializing class, restore original code, reexecute instruction + +int PatchingStub::_patch_info_offset = -NativeGeneralJump::instruction_size; + +void PatchingStub::align_patch_site(MacroAssembler* ) { + // patch sites on sparc are always properly aligned. +} + +void PatchingStub::emit_code(LIR_Assembler* ce) { + // copy original code here + assert(NativeCall::instruction_size <= _bytes_to_copy && _bytes_to_copy <= 0xFF, + "not enough room for call"); + assert((_bytes_to_copy & 0x3) == 0, "must copy a multiple of four bytes"); + + Label call_patch; + + int being_initialized_entry = __ offset(); + + if (_id == load_klass_id) { + // produce a copy of the load klass instruction for use by the being initialized case + address start = __ pc(); + Address addr = Address(_obj, address(NULL), oop_Relocation::spec(_oop_index)); + __ sethi(addr, true); + __ add(addr, _obj, 0); + +#ifdef ASSERT + for (int i = 0; i < _bytes_to_copy; i++) { + address ptr = (address)(_pc_start + i); + int a_byte = (*ptr) & 0xFF; + assert(a_byte == *start++, "should be the same code"); + } +#endif + } else { + // make a copy the code which is going to be patched. + for (int i = 0; i < _bytes_to_copy; i++) { + address ptr = (address)(_pc_start + i); + int a_byte = (*ptr) & 0xFF; + __ a_byte (a_byte); + } + } + + address end_of_patch = __ pc(); + int bytes_to_skip = 0; + if (_id == load_klass_id) { + int offset = __ offset(); + if (CommentedAssembly) { + __ block_comment(" being_initialized check"); + } + + // static field accesses have special semantics while the class + // initializer is being run so we emit a test which can be used to + // check that this code is being executed by the initializing + // thread. + assert(_obj != noreg, "must be a valid register"); + assert(_oop_index >= 0, "must have oop index"); + __ ld_ptr(_obj, instanceKlass::init_thread_offset_in_bytes() + sizeof(klassOopDesc), G3); + __ cmp(G2_thread, G3); + __ br(Assembler::notEqual, false, Assembler::pn, call_patch); + __ delayed()->nop(); + + // load_klass patches may execute the patched code before it's + // copied back into place so we need to jump back into the main + // code of the nmethod to continue execution. + __ br(Assembler::always, false, Assembler::pt, _patch_site_continuation); + __ delayed()->nop(); + + // make sure this extra code gets skipped + bytes_to_skip += __ offset() - offset; + } + + // Now emit the patch record telling the runtime how to find the + // pieces of the patch. We only need 3 bytes but it has to be + // aligned as an instruction so emit 4 bytes. + int sizeof_patch_record = 4; + bytes_to_skip += sizeof_patch_record; + + // emit the offsets needed to find the code to patch + int being_initialized_entry_offset = __ offset() - being_initialized_entry + sizeof_patch_record; + + // Emit the patch record. We need to emit a full word, so emit an extra empty byte + __ a_byte(0); + __ a_byte(being_initialized_entry_offset); + __ a_byte(bytes_to_skip); + __ a_byte(_bytes_to_copy); + address patch_info_pc = __ pc(); + assert(patch_info_pc - end_of_patch == bytes_to_skip, "incorrect patch info"); + + address entry = __ pc(); + NativeGeneralJump::insert_unconditional((address)_pc_start, entry); + address target = NULL; + switch (_id) { + case access_field_id: target = Runtime1::entry_for(Runtime1::access_field_patching_id); break; + case load_klass_id: target = Runtime1::entry_for(Runtime1::load_klass_patching_id); break; + default: ShouldNotReachHere(); + } + __ bind(call_patch); + + if (CommentedAssembly) { + __ block_comment("patch entry point"); + } + __ call(target, relocInfo::runtime_call_type); + __ delayed()->nop(); + assert(_patch_info_offset == (patch_info_pc - __ pc()), "must not change"); + ce->add_call_info_here(_info); + __ br(Assembler::always, false, Assembler::pt, _patch_site_entry); + __ delayed()->nop(); + if (_id == load_klass_id) { + CodeSection* cs = __ code_section(); + address pc = (address)_pc_start; + RelocIterator iter(cs, pc, pc + 1); + relocInfo::change_reloc_info_for_address(&iter, (address) pc, relocInfo::oop_type, relocInfo::none); + + pc = (address)(_pc_start + NativeMovConstReg::add_offset); + RelocIterator iter2(cs, pc, pc+1); + relocInfo::change_reloc_info_for_address(&iter2, (address) pc, relocInfo::oop_type, relocInfo::none); + } + +} + +void ArrayCopyStub::emit_code(LIR_Assembler* ce) { + //---------------slow case: call to native----------------- + __ bind(_entry); + __ mov(src()->as_register(), O0); + __ mov(src_pos()->as_register(), O1); + __ mov(dst()->as_register(), O2); + __ mov(dst_pos()->as_register(), O3); + __ mov(length()->as_register(), O4); + + ce->emit_static_call_stub(); + + __ call(SharedRuntime::get_resolve_static_call_stub(), relocInfo::static_call_type); + __ delayed()->nop(); + ce->add_call_info_here(info()); + ce->verify_oop_map(info()); + +#ifndef PRODUCT + __ set((intptr_t)&Runtime1::_arraycopy_slowcase_cnt, O0); + __ ld(O0, 0, O1); + __ inc(O1); + __ st(O1, 0, O0); +#endif + + __ br(Assembler::always, false, Assembler::pt, _continuation); + __ delayed()->nop(); +} + + +#undef __ diff --git a/hotspot/src/cpu/sparc/vm/c1_Defs_sparc.hpp b/hotspot/src/cpu/sparc/vm/c1_Defs_sparc.hpp new file mode 100644 index 00000000000..5d529c59507 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_Defs_sparc.hpp @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// native word offsets from memory address (big endian) +enum { + pd_lo_word_offset_in_bytes = BytesPerInt, + pd_hi_word_offset_in_bytes = 0 +}; + + +// explicit rounding operations are not required to implement the strictFP mode +enum { + pd_strict_fp_requires_explicit_rounding = false +}; + + +// registers +enum { + pd_nof_cpu_regs_frame_map = 32, // number of registers used during code emission + pd_nof_caller_save_cpu_regs_frame_map = 6, // number of cpu registers killed by calls + pd_nof_cpu_regs_reg_alloc = 20, // number of registers that are visible to register allocator + pd_nof_cpu_regs_linearscan = 32,// number of registers visible linear scan + pd_first_cpu_reg = 0, + pd_last_cpu_reg = 31, + pd_last_allocatable_cpu_reg = 19, + pd_first_callee_saved_reg = 0, + pd_last_callee_saved_reg = 13, + + pd_nof_fpu_regs_frame_map = 32, // number of registers used during code emission + pd_nof_caller_save_fpu_regs_frame_map = 32, // number of fpu registers killed by calls + pd_nof_fpu_regs_reg_alloc = 32, // number of registers that are visible to register allocator + pd_nof_fpu_regs_linearscan = 32, // number of registers visible to linear scan + pd_first_fpu_reg = pd_nof_cpu_regs_frame_map, + pd_last_fpu_reg = pd_nof_cpu_regs_frame_map + pd_nof_fpu_regs_frame_map - 1, + + pd_nof_xmm_regs_linearscan = 0, + pd_nof_caller_save_xmm_regs = 0, + pd_first_xmm_reg = -1, + pd_last_xmm_reg = -1 +}; + + +// for debug info: a float value in a register is saved in single precision by runtime stubs +enum { + pd_float_saved_as_double = false +}; diff --git a/hotspot/src/cpu/sparc/vm/c1_FpuStackSim_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_FpuStackSim_sparc.cpp new file mode 100644 index 00000000000..524941081ec --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_FpuStackSim_sparc.cpp @@ -0,0 +1,25 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// No FPU stack on SPARC diff --git a/hotspot/src/cpu/sparc/vm/c1_FpuStackSim_sparc.hpp b/hotspot/src/cpu/sparc/vm/c1_FpuStackSim_sparc.hpp new file mode 100644 index 00000000000..2f6c52ee713 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_FpuStackSim_sparc.hpp @@ -0,0 +1,26 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// No FPU stack on SPARC +class FpuStackSim; diff --git a/hotspot/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp new file mode 100644 index 00000000000..7bbf1078571 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp @@ -0,0 +1,355 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_FrameMap_sparc.cpp.incl" + + +const int FrameMap::pd_c_runtime_reserved_arg_size = 7; + + +LIR_Opr FrameMap::map_to_opr(BasicType type, VMRegPair* reg, bool outgoing) { + LIR_Opr opr = LIR_OprFact::illegalOpr; + VMReg r_1 = reg->first(); + VMReg r_2 = reg->second(); + if (r_1->is_stack()) { + // Convert stack slot to an SP offset + // The calling convention does not count the SharedRuntime::out_preserve_stack_slots() value + // so we must add it in here. + int st_off = (r_1->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size; + opr = LIR_OprFact::address(new LIR_Address(SP_opr, st_off + STACK_BIAS, type)); + } else if (r_1->is_Register()) { + Register reg = r_1->as_Register(); + if (outgoing) { + assert(!reg->is_in(), "should be using I regs"); + } else { + assert(!reg->is_out(), "should be using O regs"); + } + if (r_2->is_Register() && (type == T_LONG || type == T_DOUBLE)) { + opr = as_long_opr(reg); + } else if (type == T_OBJECT || type == T_ARRAY) { + opr = as_oop_opr(reg); + } else { + opr = as_opr(reg); + } + } else if (r_1->is_FloatRegister()) { + assert(type == T_DOUBLE || type == T_FLOAT, "wrong type"); + FloatRegister f = r_1->as_FloatRegister(); + if (type == T_DOUBLE) { + opr = as_double_opr(f); + } else { + opr = as_float_opr(f); + } + } + return opr; +} + +// FrameMap +//-------------------------------------------------------- + +FloatRegister FrameMap::_fpu_regs [FrameMap::nof_fpu_regs]; + +// some useful constant RInfo's: +LIR_Opr FrameMap::in_long_opr; +LIR_Opr FrameMap::out_long_opr; + +LIR_Opr FrameMap::F0_opr; +LIR_Opr FrameMap::F0_double_opr; + +LIR_Opr FrameMap::G0_opr; +LIR_Opr FrameMap::G1_opr; +LIR_Opr FrameMap::G2_opr; +LIR_Opr FrameMap::G3_opr; +LIR_Opr FrameMap::G4_opr; +LIR_Opr FrameMap::G5_opr; +LIR_Opr FrameMap::G6_opr; +LIR_Opr FrameMap::G7_opr; +LIR_Opr FrameMap::O0_opr; +LIR_Opr FrameMap::O1_opr; +LIR_Opr FrameMap::O2_opr; +LIR_Opr FrameMap::O3_opr; +LIR_Opr FrameMap::O4_opr; +LIR_Opr FrameMap::O5_opr; +LIR_Opr FrameMap::O6_opr; +LIR_Opr FrameMap::O7_opr; +LIR_Opr FrameMap::L0_opr; +LIR_Opr FrameMap::L1_opr; +LIR_Opr FrameMap::L2_opr; +LIR_Opr FrameMap::L3_opr; +LIR_Opr FrameMap::L4_opr; +LIR_Opr FrameMap::L5_opr; +LIR_Opr FrameMap::L6_opr; +LIR_Opr FrameMap::L7_opr; +LIR_Opr FrameMap::I0_opr; +LIR_Opr FrameMap::I1_opr; +LIR_Opr FrameMap::I2_opr; +LIR_Opr FrameMap::I3_opr; +LIR_Opr FrameMap::I4_opr; +LIR_Opr FrameMap::I5_opr; +LIR_Opr FrameMap::I6_opr; +LIR_Opr FrameMap::I7_opr; + +LIR_Opr FrameMap::G0_oop_opr; +LIR_Opr FrameMap::G1_oop_opr; +LIR_Opr FrameMap::G2_oop_opr; +LIR_Opr FrameMap::G3_oop_opr; +LIR_Opr FrameMap::G4_oop_opr; +LIR_Opr FrameMap::G5_oop_opr; +LIR_Opr FrameMap::G6_oop_opr; +LIR_Opr FrameMap::G7_oop_opr; +LIR_Opr FrameMap::O0_oop_opr; +LIR_Opr FrameMap::O1_oop_opr; +LIR_Opr FrameMap::O2_oop_opr; +LIR_Opr FrameMap::O3_oop_opr; +LIR_Opr FrameMap::O4_oop_opr; +LIR_Opr FrameMap::O5_oop_opr; +LIR_Opr FrameMap::O6_oop_opr; +LIR_Opr FrameMap::O7_oop_opr; +LIR_Opr FrameMap::L0_oop_opr; +LIR_Opr FrameMap::L1_oop_opr; +LIR_Opr FrameMap::L2_oop_opr; +LIR_Opr FrameMap::L3_oop_opr; +LIR_Opr FrameMap::L4_oop_opr; +LIR_Opr FrameMap::L5_oop_opr; +LIR_Opr FrameMap::L6_oop_opr; +LIR_Opr FrameMap::L7_oop_opr; +LIR_Opr FrameMap::I0_oop_opr; +LIR_Opr FrameMap::I1_oop_opr; +LIR_Opr FrameMap::I2_oop_opr; +LIR_Opr FrameMap::I3_oop_opr; +LIR_Opr FrameMap::I4_oop_opr; +LIR_Opr FrameMap::I5_oop_opr; +LIR_Opr FrameMap::I6_oop_opr; +LIR_Opr FrameMap::I7_oop_opr; + +LIR_Opr FrameMap::SP_opr; +LIR_Opr FrameMap::FP_opr; + +LIR_Opr FrameMap::Oexception_opr; +LIR_Opr FrameMap::Oissuing_pc_opr; + +LIR_Opr FrameMap::_caller_save_cpu_regs[] = { 0, }; +LIR_Opr FrameMap::_caller_save_fpu_regs[] = { 0, }; + + +FloatRegister FrameMap::nr2floatreg (int rnr) { + assert(_init_done, "tables not initialized"); + debug_only(fpu_range_check(rnr);) + return _fpu_regs[rnr]; +} + + +// returns true if reg could be smashed by a callee. +bool FrameMap::is_caller_save_register (LIR_Opr reg) { + if (reg->is_single_fpu() || reg->is_double_fpu()) { return true; } + if (reg->is_double_cpu()) { + return is_caller_save_register(reg->as_register_lo()) || + is_caller_save_register(reg->as_register_hi()); + } + return is_caller_save_register(reg->as_register()); +} + + +NEEDS_CLEANUP // once the new calling convention is enabled, we no + // longer need to treat I5, I4 and L0 specially +// Because the interpreter destroys caller's I5, I4 and L0, +// we must spill them before doing a Java call as we may land in +// interpreter. +bool FrameMap::is_caller_save_register (Register r) { + return (r->is_global() && (r != G0)) || r->is_out(); +} + + +void FrameMap::init () { + if (_init_done) return; + + int i=0; + // Register usage: + // O6: sp + // I6: fp + // I7: return address + // G0: zero + // G2: thread + // G7: not available + // G6: not available + /* 0 */ map_register(i++, L0); + /* 1 */ map_register(i++, L1); + /* 2 */ map_register(i++, L2); + /* 3 */ map_register(i++, L3); + /* 4 */ map_register(i++, L4); + /* 5 */ map_register(i++, L5); + /* 6 */ map_register(i++, L6); + /* 7 */ map_register(i++, L7); + + /* 8 */ map_register(i++, I0); + /* 9 */ map_register(i++, I1); + /* 10 */ map_register(i++, I2); + /* 11 */ map_register(i++, I3); + /* 12 */ map_register(i++, I4); + /* 13 */ map_register(i++, I5); + /* 14 */ map_register(i++, O0); + /* 15 */ map_register(i++, O1); + /* 16 */ map_register(i++, O2); + /* 17 */ map_register(i++, O3); + /* 18 */ map_register(i++, O4); + /* 19 */ map_register(i++, O5); // <- last register visible in RegAlloc (RegAlloc::nof+cpu_regs) + /* 20 */ map_register(i++, G1); + /* 21 */ map_register(i++, G3); + /* 22 */ map_register(i++, G4); + /* 23 */ map_register(i++, G5); + /* 24 */ map_register(i++, G0); + + // the following registers are not normally available + /* 25 */ map_register(i++, O7); + /* 26 */ map_register(i++, G2); + /* 27 */ map_register(i++, O6); + /* 28 */ map_register(i++, I6); + /* 29 */ map_register(i++, I7); + /* 30 */ map_register(i++, G6); + /* 31 */ map_register(i++, G7); + assert(i == nof_cpu_regs, "number of CPU registers"); + + for (i = 0; i < nof_fpu_regs; i++) { + _fpu_regs[i] = as_FloatRegister(i); + } + + _init_done = true; + + in_long_opr = as_long_opr(I0); + out_long_opr = as_long_opr(O0); + + G0_opr = as_opr(G0); + G1_opr = as_opr(G1); + G2_opr = as_opr(G2); + G3_opr = as_opr(G3); + G4_opr = as_opr(G4); + G5_opr = as_opr(G5); + G6_opr = as_opr(G6); + G7_opr = as_opr(G7); + O0_opr = as_opr(O0); + O1_opr = as_opr(O1); + O2_opr = as_opr(O2); + O3_opr = as_opr(O3); + O4_opr = as_opr(O4); + O5_opr = as_opr(O5); + O6_opr = as_opr(O6); + O7_opr = as_opr(O7); + L0_opr = as_opr(L0); + L1_opr = as_opr(L1); + L2_opr = as_opr(L2); + L3_opr = as_opr(L3); + L4_opr = as_opr(L4); + L5_opr = as_opr(L5); + L6_opr = as_opr(L6); + L7_opr = as_opr(L7); + I0_opr = as_opr(I0); + I1_opr = as_opr(I1); + I2_opr = as_opr(I2); + I3_opr = as_opr(I3); + I4_opr = as_opr(I4); + I5_opr = as_opr(I5); + I6_opr = as_opr(I6); + I7_opr = as_opr(I7); + + G0_oop_opr = as_oop_opr(G0); + G1_oop_opr = as_oop_opr(G1); + G2_oop_opr = as_oop_opr(G2); + G3_oop_opr = as_oop_opr(G3); + G4_oop_opr = as_oop_opr(G4); + G5_oop_opr = as_oop_opr(G5); + G6_oop_opr = as_oop_opr(G6); + G7_oop_opr = as_oop_opr(G7); + O0_oop_opr = as_oop_opr(O0); + O1_oop_opr = as_oop_opr(O1); + O2_oop_opr = as_oop_opr(O2); + O3_oop_opr = as_oop_opr(O3); + O4_oop_opr = as_oop_opr(O4); + O5_oop_opr = as_oop_opr(O5); + O6_oop_opr = as_oop_opr(O6); + O7_oop_opr = as_oop_opr(O7); + L0_oop_opr = as_oop_opr(L0); + L1_oop_opr = as_oop_opr(L1); + L2_oop_opr = as_oop_opr(L2); + L3_oop_opr = as_oop_opr(L3); + L4_oop_opr = as_oop_opr(L4); + L5_oop_opr = as_oop_opr(L5); + L6_oop_opr = as_oop_opr(L6); + L7_oop_opr = as_oop_opr(L7); + I0_oop_opr = as_oop_opr(I0); + I1_oop_opr = as_oop_opr(I1); + I2_oop_opr = as_oop_opr(I2); + I3_oop_opr = as_oop_opr(I3); + I4_oop_opr = as_oop_opr(I4); + I5_oop_opr = as_oop_opr(I5); + I6_oop_opr = as_oop_opr(I6); + I7_oop_opr = as_oop_opr(I7); + + FP_opr = as_pointer_opr(FP); + SP_opr = as_pointer_opr(SP); + + F0_opr = as_float_opr(F0); + F0_double_opr = as_double_opr(F0); + + Oexception_opr = as_oop_opr(Oexception); + Oissuing_pc_opr = as_opr(Oissuing_pc); + + _caller_save_cpu_regs[0] = FrameMap::O0_opr; + _caller_save_cpu_regs[1] = FrameMap::O1_opr; + _caller_save_cpu_regs[2] = FrameMap::O2_opr; + _caller_save_cpu_regs[3] = FrameMap::O3_opr; + _caller_save_cpu_regs[4] = FrameMap::O4_opr; + _caller_save_cpu_regs[5] = FrameMap::O5_opr; + for (int i = 0; i < nof_caller_save_fpu_regs; i++) { + _caller_save_fpu_regs[i] = LIR_OprFact::single_fpu(i); + } +} + + +Address FrameMap::make_new_address(ByteSize sp_offset) const { + return Address(SP, 0, STACK_BIAS + in_bytes(sp_offset)); +} + + +VMReg FrameMap::fpu_regname (int n) { + return as_FloatRegister(n)->as_VMReg(); +} + + +LIR_Opr FrameMap::stack_pointer() { + return SP_opr; +} + + +bool FrameMap::validate_frame() { + int max_offset = in_bytes(framesize_in_bytes()); + int java_index = 0; + for (int i = 0; i < _incoming_arguments->length(); i++) { + LIR_Opr opr = _incoming_arguments->at(i); + if (opr->is_stack()) { + max_offset = MAX2(_argument_locations->at(java_index), max_offset); + } + java_index += type2size[opr->type()]; + } + return Assembler::is_simm13(max_offset + STACK_BIAS); +} diff --git a/hotspot/src/cpu/sparc/vm/c1_FrameMap_sparc.hpp b/hotspot/src/cpu/sparc/vm/c1_FrameMap_sparc.hpp new file mode 100644 index 00000000000..2651784469a --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_FrameMap_sparc.hpp @@ -0,0 +1,145 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + public: + + enum { + nof_reg_args = 6, // registers o0-o5 are available for parameter passing + first_available_sp_in_frame = frame::memory_parameter_word_sp_offset * BytesPerWord, + frame_pad_in_bytes = 0 + }; + + static const int pd_c_runtime_reserved_arg_size; + + static LIR_Opr G0_opr; + static LIR_Opr G1_opr; + static LIR_Opr G2_opr; + static LIR_Opr G3_opr; + static LIR_Opr G4_opr; + static LIR_Opr G5_opr; + static LIR_Opr G6_opr; + static LIR_Opr G7_opr; + static LIR_Opr O0_opr; + static LIR_Opr O1_opr; + static LIR_Opr O2_opr; + static LIR_Opr O3_opr; + static LIR_Opr O4_opr; + static LIR_Opr O5_opr; + static LIR_Opr O6_opr; + static LIR_Opr O7_opr; + static LIR_Opr L0_opr; + static LIR_Opr L1_opr; + static LIR_Opr L2_opr; + static LIR_Opr L3_opr; + static LIR_Opr L4_opr; + static LIR_Opr L5_opr; + static LIR_Opr L6_opr; + static LIR_Opr L7_opr; + static LIR_Opr I0_opr; + static LIR_Opr I1_opr; + static LIR_Opr I2_opr; + static LIR_Opr I3_opr; + static LIR_Opr I4_opr; + static LIR_Opr I5_opr; + static LIR_Opr I6_opr; + static LIR_Opr I7_opr; + + static LIR_Opr SP_opr; + static LIR_Opr FP_opr; + + static LIR_Opr G0_oop_opr; + static LIR_Opr G1_oop_opr; + static LIR_Opr G2_oop_opr; + static LIR_Opr G3_oop_opr; + static LIR_Opr G4_oop_opr; + static LIR_Opr G5_oop_opr; + static LIR_Opr G6_oop_opr; + static LIR_Opr G7_oop_opr; + static LIR_Opr O0_oop_opr; + static LIR_Opr O1_oop_opr; + static LIR_Opr O2_oop_opr; + static LIR_Opr O3_oop_opr; + static LIR_Opr O4_oop_opr; + static LIR_Opr O5_oop_opr; + static LIR_Opr O6_oop_opr; + static LIR_Opr O7_oop_opr; + static LIR_Opr L0_oop_opr; + static LIR_Opr L1_oop_opr; + static LIR_Opr L2_oop_opr; + static LIR_Opr L3_oop_opr; + static LIR_Opr L4_oop_opr; + static LIR_Opr L5_oop_opr; + static LIR_Opr L6_oop_opr; + static LIR_Opr L7_oop_opr; + static LIR_Opr I0_oop_opr; + static LIR_Opr I1_oop_opr; + static LIR_Opr I2_oop_opr; + static LIR_Opr I3_oop_opr; + static LIR_Opr I4_oop_opr; + static LIR_Opr I5_oop_opr; + static LIR_Opr I6_oop_opr; + static LIR_Opr I7_oop_opr; + + static LIR_Opr in_long_opr; + static LIR_Opr out_long_opr; + + static LIR_Opr F0_opr; + static LIR_Opr F0_double_opr; + + static LIR_Opr Oexception_opr; + static LIR_Opr Oissuing_pc_opr; + + private: + static FloatRegister _fpu_regs [nof_fpu_regs]; + + public: + +#ifdef _LP64 + static LIR_Opr as_long_opr(Register r) { + return LIR_OprFact::double_cpu(cpu_reg2rnr(r), cpu_reg2rnr(r)); + } + static LIR_Opr as_pointer_opr(Register r) { + return LIR_OprFact::double_cpu(cpu_reg2rnr(r), cpu_reg2rnr(r)); + } +#else + static LIR_Opr as_long_opr(Register r) { + return LIR_OprFact::double_cpu(cpu_reg2rnr(r->successor()), cpu_reg2rnr(r)); + } + static LIR_Opr as_pointer_opr(Register r) { + return as_opr(r); + } +#endif + static LIR_Opr as_float_opr(FloatRegister r) { + return LIR_OprFact::single_fpu(r->encoding()); + } + static LIR_Opr as_double_opr(FloatRegister r) { + return LIR_OprFact::double_fpu(r->successor()->encoding(), r->encoding()); + } + + static FloatRegister nr2floatreg (int rnr); + + static VMReg fpu_regname (int n); + + static bool is_caller_save_register (LIR_Opr reg); + static bool is_caller_save_register (Register r); diff --git a/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp new file mode 100644 index 00000000000..e594085505b --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp @@ -0,0 +1,3210 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_LIRAssembler_sparc.cpp.incl" + +#define __ _masm-> + + +//------------------------------------------------------------ + + +bool LIR_Assembler::is_small_constant(LIR_Opr opr) { + if (opr->is_constant()) { + LIR_Const* constant = opr->as_constant_ptr(); + switch (constant->type()) { + case T_INT: { + jint value = constant->as_jint(); + return Assembler::is_simm13(value); + } + + default: + return false; + } + } + return false; +} + + +bool LIR_Assembler::is_single_instruction(LIR_Op* op) { + switch (op->code()) { + case lir_null_check: + return true; + + + case lir_add: + case lir_ushr: + case lir_shr: + case lir_shl: + // integer shifts and adds are always one instruction + return op->result_opr()->is_single_cpu(); + + + case lir_move: { + LIR_Op1* op1 = op->as_Op1(); + LIR_Opr src = op1->in_opr(); + LIR_Opr dst = op1->result_opr(); + + if (src == dst) { + NEEDS_CLEANUP; + // this works around a problem where moves with the same src and dst + // end up in the delay slot and then the assembler swallows the mov + // since it has no effect and then it complains because the delay slot + // is empty. returning false stops the optimizer from putting this in + // the delay slot + return false; + } + + // don't put moves involving oops into the delay slot since the VerifyOops code + // will make it much larger than a single instruction. + if (VerifyOops) { + return false; + } + + if (src->is_double_cpu() || dst->is_double_cpu() || op1->patch_code() != lir_patch_none || + ((src->is_double_fpu() || dst->is_double_fpu()) && op1->move_kind() != lir_move_normal)) { + return false; + } + + if (dst->is_register()) { + if (src->is_address() && Assembler::is_simm13(src->as_address_ptr()->disp())) { + return !PatchALot; + } else if (src->is_single_stack()) { + return true; + } + } + + if (src->is_register()) { + if (dst->is_address() && Assembler::is_simm13(dst->as_address_ptr()->disp())) { + return !PatchALot; + } else if (dst->is_single_stack()) { + return true; + } + } + + if (dst->is_register() && + ((src->is_register() && src->is_single_word() && src->is_same_type(dst)) || + (src->is_constant() && LIR_Assembler::is_small_constant(op->as_Op1()->in_opr())))) { + return true; + } + + return false; + } + + default: + return false; + } + ShouldNotReachHere(); +} + + +LIR_Opr LIR_Assembler::receiverOpr() { + return FrameMap::O0_oop_opr; +} + + +LIR_Opr LIR_Assembler::incomingReceiverOpr() { + return FrameMap::I0_oop_opr; +} + + +LIR_Opr LIR_Assembler::osrBufferPointer() { + return FrameMap::I0_opr; +} + + +int LIR_Assembler::initial_frame_size_in_bytes() { + return in_bytes(frame_map()->framesize_in_bytes()); +} + + +// inline cache check: the inline cached class is in G5_inline_cache_reg(G5); +// we fetch the class of the receiver (O0) and compare it with the cached class. +// If they do not match we jump to slow case. +int LIR_Assembler::check_icache() { + int offset = __ offset(); + __ inline_cache_check(O0, G5_inline_cache_reg); + return offset; +} + + +void LIR_Assembler::osr_entry() { + // On-stack-replacement entry sequence (interpreter frame layout described in interpreter_sparc.cpp): + // + // 1. Create a new compiled activation. + // 2. Initialize local variables in the compiled activation. The expression stack must be empty + // at the osr_bci; it is not initialized. + // 3. Jump to the continuation address in compiled code to resume execution. + + // OSR entry point + offsets()->set_value(CodeOffsets::OSR_Entry, code_offset()); + BlockBegin* osr_entry = compilation()->hir()->osr_entry(); + ValueStack* entry_state = osr_entry->end()->state(); + int number_of_locks = entry_state->locks_size(); + + // Create a frame for the compiled activation. + __ build_frame(initial_frame_size_in_bytes()); + + // OSR buffer is + // + // locals[nlocals-1..0] + // monitors[number_of_locks-1..0] + // + // locals is a direct copy of the interpreter frame so in the osr buffer + // so first slot in the local array is the last local from the interpreter + // and last slot is local[0] (receiver) from the interpreter + // + // Similarly with locks. The first lock slot in the osr buffer is the nth lock + // from the interpreter frame, the nth lock slot in the osr buffer is 0th lock + // in the interpreter frame (the method lock if a sync method) + + // Initialize monitors in the compiled activation. + // I0: pointer to osr buffer + // + // All other registers are dead at this point and the locals will be + // copied into place by code emitted in the IR. + + Register OSR_buf = osrBufferPointer()->as_register(); + { assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); + int monitor_offset = BytesPerWord * method()->max_locals() + + (BasicObjectLock::size() * BytesPerWord) * (number_of_locks - 1); + for (int i = 0; i < number_of_locks; i++) { + int slot_offset = monitor_offset - ((i * BasicObjectLock::size()) * BytesPerWord); +#ifdef ASSERT + // verify the interpreter's monitor has a non-null object + { + Label L; + __ ld_ptr(Address(OSR_buf, 0, slot_offset + BasicObjectLock::obj_offset_in_bytes()), O7); + __ cmp(G0, O7); + __ br(Assembler::notEqual, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("locked object is NULL"); + __ bind(L); + } +#endif // ASSERT + // Copy the lock field into the compiled activation. + __ ld_ptr(Address(OSR_buf, 0, slot_offset + BasicObjectLock::lock_offset_in_bytes()), O7); + __ st_ptr(O7, frame_map()->address_for_monitor_lock(i)); + __ ld_ptr(Address(OSR_buf, 0, slot_offset + BasicObjectLock::obj_offset_in_bytes()), O7); + __ st_ptr(O7, frame_map()->address_for_monitor_object(i)); + } + } +} + + +// Optimized Library calls +// This is the fast version of java.lang.String.compare; it has not +// OSR-entry and therefore, we generate a slow version for OSR's +void LIR_Assembler::emit_string_compare(LIR_Opr left, LIR_Opr right, LIR_Opr dst, CodeEmitInfo* info) { + Register str0 = left->as_register(); + Register str1 = right->as_register(); + + Label Ldone; + + Register result = dst->as_register(); + { + // Get a pointer to the first character of string0 in tmp0 and get string0.count in str0 + // Get a pointer to the first character of string1 in tmp1 and get string1.count in str1 + // Also, get string0.count-string1.count in o7 and get the condition code set + // Note: some instructions have been hoisted for better instruction scheduling + + Register tmp0 = L0; + Register tmp1 = L1; + Register tmp2 = L2; + + int value_offset = java_lang_String:: value_offset_in_bytes(); // char array + int offset_offset = java_lang_String::offset_offset_in_bytes(); // first character position + int count_offset = java_lang_String:: count_offset_in_bytes(); + + __ ld_ptr(Address(str0, 0, value_offset), tmp0); + __ ld(Address(str0, 0, offset_offset), tmp2); + __ add(tmp0, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp0); + __ ld(Address(str0, 0, count_offset), str0); + __ sll(tmp2, exact_log2(sizeof(jchar)), tmp2); + + // str1 may be null + add_debug_info_for_null_check_here(info); + + __ ld_ptr(Address(str1, 0, value_offset), tmp1); + __ add(tmp0, tmp2, tmp0); + + __ ld(Address(str1, 0, offset_offset), tmp2); + __ add(tmp1, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp1); + __ ld(Address(str1, 0, count_offset), str1); + __ sll(tmp2, exact_log2(sizeof(jchar)), tmp2); + __ subcc(str0, str1, O7); + __ add(tmp1, tmp2, tmp1); + } + + { + // Compute the minimum of the string lengths, scale it and store it in limit + Register count0 = I0; + Register count1 = I1; + Register limit = L3; + + Label Lskip; + __ sll(count0, exact_log2(sizeof(jchar)), limit); // string0 is shorter + __ br(Assembler::greater, true, Assembler::pt, Lskip); + __ delayed()->sll(count1, exact_log2(sizeof(jchar)), limit); // string1 is shorter + __ bind(Lskip); + + // If either string is empty (or both of them) the result is the difference in lengths + __ cmp(limit, 0); + __ br(Assembler::equal, true, Assembler::pn, Ldone); + __ delayed()->mov(O7, result); // result is difference in lengths + } + + { + // Neither string is empty + Label Lloop; + + Register base0 = L0; + Register base1 = L1; + Register chr0 = I0; + Register chr1 = I1; + Register limit = L3; + + // Shift base0 and base1 to the end of the arrays, negate limit + __ add(base0, limit, base0); + __ add(base1, limit, base1); + __ neg(limit); // limit = -min{string0.count, strin1.count} + + __ lduh(base0, limit, chr0); + __ bind(Lloop); + __ lduh(base1, limit, chr1); + __ subcc(chr0, chr1, chr0); + __ br(Assembler::notZero, false, Assembler::pn, Ldone); + assert(chr0 == result, "result must be pre-placed"); + __ delayed()->inccc(limit, sizeof(jchar)); + __ br(Assembler::notZero, true, Assembler::pt, Lloop); + __ delayed()->lduh(base0, limit, chr0); + } + + // If strings are equal up to min length, return the length difference. + __ mov(O7, result); + + // Otherwise, return the difference between the first mismatched chars. + __ bind(Ldone); +} + + +// -------------------------------------------------------------------------------------------- + +void LIR_Assembler::monitorexit(LIR_Opr obj_opr, LIR_Opr lock_opr, Register hdr, int monitor_no) { + if (!GenerateSynchronizationCode) return; + + Register obj_reg = obj_opr->as_register(); + Register lock_reg = lock_opr->as_register(); + + Address mon_addr = frame_map()->address_for_monitor_lock(monitor_no); + Register reg = mon_addr.base(); + int offset = mon_addr.disp(); + // compute pointer to BasicLock + if (mon_addr.is_simm13()) { + __ add(reg, offset, lock_reg); + } + else { + __ set(offset, lock_reg); + __ add(reg, lock_reg, lock_reg); + } + // unlock object + MonitorAccessStub* slow_case = new MonitorExitStub(lock_opr, UseFastLocking, monitor_no); + // _slow_case_stubs->append(slow_case); + // temporary fix: must be created after exceptionhandler, therefore as call stub + _slow_case_stubs->append(slow_case); + if (UseFastLocking) { + // try inlined fast unlocking first, revert to slow locking if it fails + // note: lock_reg points to the displaced header since the displaced header offset is 0! + assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); + __ unlock_object(hdr, obj_reg, lock_reg, *slow_case->entry()); + } else { + // always do slow unlocking + // note: the slow unlocking code could be inlined here, however if we use + // slow unlocking, speed doesn't matter anyway and this solution is + // simpler and requires less duplicated code - additionally, the + // slow unlocking code is the same in either case which simplifies + // debugging + __ br(Assembler::always, false, Assembler::pt, *slow_case->entry()); + __ delayed()->nop(); + } + // done + __ bind(*slow_case->continuation()); +} + + +void LIR_Assembler::emit_exception_handler() { + // if the last instruction is a call (typically to do a throw which + // is coming at the end after block reordering) the return address + // must still point into the code area in order to avoid assertion + // failures when searching for the corresponding bci => add a nop + // (was bug 5/14/1999 - gri) + __ nop(); + + // generate code for exception handler + ciMethod* method = compilation()->method(); + + address handler_base = __ start_a_stub(exception_handler_size); + + if (handler_base == NULL) { + // not enough space left for the handler + bailout("exception handler overflow"); + return; + } +#ifdef ASSERT + int offset = code_offset(); +#endif // ASSERT + compilation()->offsets()->set_value(CodeOffsets::Exceptions, code_offset()); + + + if (compilation()->has_exception_handlers() || JvmtiExport::can_post_exceptions()) { + __ call(Runtime1::entry_for(Runtime1::handle_exception_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + } + + __ call(Runtime1::entry_for(Runtime1::unwind_exception_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + debug_only(__ stop("should have gone to the caller");) + assert(code_offset() - offset <= exception_handler_size, "overflow"); + + __ end_a_stub(); +} + +void LIR_Assembler::emit_deopt_handler() { + // if the last instruction is a call (typically to do a throw which + // is coming at the end after block reordering) the return address + // must still point into the code area in order to avoid assertion + // failures when searching for the corresponding bci => add a nop + // (was bug 5/14/1999 - gri) + __ nop(); + + // generate code for deopt handler + ciMethod* method = compilation()->method(); + address handler_base = __ start_a_stub(deopt_handler_size); + if (handler_base == NULL) { + // not enough space left for the handler + bailout("deopt handler overflow"); + return; + } +#ifdef ASSERT + int offset = code_offset(); +#endif // ASSERT + compilation()->offsets()->set_value(CodeOffsets::Deopt, code_offset()); + + Address deopt_blob(G3_scratch, SharedRuntime::deopt_blob()->unpack()); + + __ JUMP(deopt_blob, 0); // sethi;jmp + __ delayed()->nop(); + + assert(code_offset() - offset <= deopt_handler_size, "overflow"); + + debug_only(__ stop("should have gone to the caller");) + + __ end_a_stub(); +} + + +void LIR_Assembler::jobject2reg(jobject o, Register reg) { + if (o == NULL) { + __ set(NULL_WORD, reg); + } else { + int oop_index = __ oop_recorder()->find_index(o); + RelocationHolder rspec = oop_Relocation::spec(oop_index); + __ set(NULL_WORD, reg, rspec); // Will be set when the nmethod is created + } +} + + +void LIR_Assembler::jobject2reg_with_patching(Register reg, CodeEmitInfo *info) { + // Allocate a new index in oop table to hold the oop once it's been patched + int oop_index = __ oop_recorder()->allocate_index((jobject)NULL); + PatchingStub* patch = new PatchingStub(_masm, PatchingStub::load_klass_id, oop_index); + + Address addr = Address(reg, address(NULL), oop_Relocation::spec(oop_index)); + assert(addr.rspec().type() == relocInfo::oop_type, "must be an oop reloc"); + // It may not seem necessary to use a sethi/add pair to load a NULL into dest, but the + // NULL will be dynamically patched later and the patched value may be large. We must + // therefore generate the sethi/add as a placeholders + __ sethi(addr, true); + __ add(addr, reg, 0); + + patching_epilog(patch, lir_patch_normal, reg, info); +} + + +void LIR_Assembler::emit_op3(LIR_Op3* op) { + Register Rdividend = op->in_opr1()->as_register(); + Register Rdivisor = noreg; + Register Rscratch = op->in_opr3()->as_register(); + Register Rresult = op->result_opr()->as_register(); + int divisor = -1; + + if (op->in_opr2()->is_register()) { + Rdivisor = op->in_opr2()->as_register(); + } else { + divisor = op->in_opr2()->as_constant_ptr()->as_jint(); + assert(Assembler::is_simm13(divisor), "can only handle simm13"); + } + + assert(Rdividend != Rscratch, ""); + assert(Rdivisor != Rscratch, ""); + assert(op->code() == lir_idiv || op->code() == lir_irem, "Must be irem or idiv"); + + if (Rdivisor == noreg && is_power_of_2(divisor)) { + // convert division by a power of two into some shifts and logical operations + if (op->code() == lir_idiv) { + if (divisor == 2) { + __ srl(Rdividend, 31, Rscratch); + } else { + __ sra(Rdividend, 31, Rscratch); + __ and3(Rscratch, divisor - 1, Rscratch); + } + __ add(Rdividend, Rscratch, Rscratch); + __ sra(Rscratch, log2_intptr(divisor), Rresult); + return; + } else { + if (divisor == 2) { + __ srl(Rdividend, 31, Rscratch); + } else { + __ sra(Rdividend, 31, Rscratch); + __ and3(Rscratch, divisor - 1,Rscratch); + } + __ add(Rdividend, Rscratch, Rscratch); + __ andn(Rscratch, divisor - 1,Rscratch); + __ sub(Rdividend, Rscratch, Rresult); + return; + } + } + + __ sra(Rdividend, 31, Rscratch); + __ wry(Rscratch); + if (!VM_Version::v9_instructions_work()) { + // v9 doesn't require these nops + __ nop(); + __ nop(); + __ nop(); + __ nop(); + } + + add_debug_info_for_div0_here(op->info()); + + if (Rdivisor != noreg) { + __ sdivcc(Rdividend, Rdivisor, (op->code() == lir_idiv ? Rresult : Rscratch)); + } else { + assert(Assembler::is_simm13(divisor), "can only handle simm13"); + __ sdivcc(Rdividend, divisor, (op->code() == lir_idiv ? Rresult : Rscratch)); + } + + Label skip; + __ br(Assembler::overflowSet, true, Assembler::pn, skip); + __ delayed()->Assembler::sethi(0x80000000, (op->code() == lir_idiv ? Rresult : Rscratch)); + __ bind(skip); + + if (op->code() == lir_irem) { + if (Rdivisor != noreg) { + __ smul(Rscratch, Rdivisor, Rscratch); + } else { + __ smul(Rscratch, divisor, Rscratch); + } + __ sub(Rdividend, Rscratch, Rresult); + } +} + + +void LIR_Assembler::emit_opBranch(LIR_OpBranch* op) { +#ifdef ASSERT + assert(op->block() == NULL || op->block()->label() == op->label(), "wrong label"); + if (op->block() != NULL) _branch_target_blocks.append(op->block()); + if (op->ublock() != NULL) _branch_target_blocks.append(op->ublock()); +#endif + assert(op->info() == NULL, "shouldn't have CodeEmitInfo"); + + if (op->cond() == lir_cond_always) { + __ br(Assembler::always, false, Assembler::pt, *(op->label())); + } else if (op->code() == lir_cond_float_branch) { + assert(op->ublock() != NULL, "must have unordered successor"); + bool is_unordered = (op->ublock() == op->block()); + Assembler::Condition acond; + switch (op->cond()) { + case lir_cond_equal: acond = Assembler::f_equal; break; + case lir_cond_notEqual: acond = Assembler::f_notEqual; break; + case lir_cond_less: acond = (is_unordered ? Assembler::f_unorderedOrLess : Assembler::f_less); break; + case lir_cond_greater: acond = (is_unordered ? Assembler::f_unorderedOrGreater : Assembler::f_greater); break; + case lir_cond_lessEqual: acond = (is_unordered ? Assembler::f_unorderedOrLessOrEqual : Assembler::f_lessOrEqual); break; + case lir_cond_greaterEqual: acond = (is_unordered ? Assembler::f_unorderedOrGreaterOrEqual: Assembler::f_greaterOrEqual); break; + default : ShouldNotReachHere(); + }; + + if (!VM_Version::v9_instructions_work()) { + __ nop(); + } + __ fb( acond, false, Assembler::pn, *(op->label())); + } else { + assert (op->code() == lir_branch, "just checking"); + + Assembler::Condition acond; + switch (op->cond()) { + case lir_cond_equal: acond = Assembler::equal; break; + case lir_cond_notEqual: acond = Assembler::notEqual; break; + case lir_cond_less: acond = Assembler::less; break; + case lir_cond_lessEqual: acond = Assembler::lessEqual; break; + case lir_cond_greaterEqual: acond = Assembler::greaterEqual; break; + case lir_cond_greater: acond = Assembler::greater; break; + case lir_cond_aboveEqual: acond = Assembler::greaterEqualUnsigned; break; + case lir_cond_belowEqual: acond = Assembler::lessEqualUnsigned; break; + default: ShouldNotReachHere(); + }; + + // sparc has different condition codes for testing 32-bit + // vs. 64-bit values. We could always test xcc is we could + // guarantee that 32-bit loads always sign extended but that isn't + // true and since sign extension isn't free, it would impose a + // slight cost. +#ifdef _LP64 + if (op->type() == T_INT) { + __ br(acond, false, Assembler::pn, *(op->label())); + } else +#endif + __ brx(acond, false, Assembler::pn, *(op->label())); + } + // The peephole pass fills the delay slot +} + + +void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { + Bytecodes::Code code = op->bytecode(); + LIR_Opr dst = op->result_opr(); + + switch(code) { + case Bytecodes::_i2l: { + Register rlo = dst->as_register_lo(); + Register rhi = dst->as_register_hi(); + Register rval = op->in_opr()->as_register(); +#ifdef _LP64 + __ sra(rval, 0, rlo); +#else + __ mov(rval, rlo); + __ sra(rval, BitsPerInt-1, rhi); +#endif + break; + } + case Bytecodes::_i2d: + case Bytecodes::_i2f: { + bool is_double = (code == Bytecodes::_i2d); + FloatRegister rdst = is_double ? dst->as_double_reg() : dst->as_float_reg(); + FloatRegisterImpl::Width w = is_double ? FloatRegisterImpl::D : FloatRegisterImpl::S; + FloatRegister rsrc = op->in_opr()->as_float_reg(); + if (rsrc != rdst) { + __ fmov(FloatRegisterImpl::S, rsrc, rdst); + } + __ fitof(w, rdst, rdst); + break; + } + case Bytecodes::_f2i:{ + FloatRegister rsrc = op->in_opr()->as_float_reg(); + Address addr = frame_map()->address_for_slot(dst->single_stack_ix()); + Label L; + // result must be 0 if value is NaN; test by comparing value to itself + __ fcmp(FloatRegisterImpl::S, Assembler::fcc0, rsrc, rsrc); + if (!VM_Version::v9_instructions_work()) { + __ nop(); + } + __ fb(Assembler::f_unordered, true, Assembler::pn, L); + __ delayed()->st(G0, addr); // annuled if contents of rsrc is not NaN + __ ftoi(FloatRegisterImpl::S, rsrc, rsrc); + // move integer result from float register to int register + __ stf(FloatRegisterImpl::S, rsrc, addr.base(), addr.disp()); + __ bind (L); + break; + } + case Bytecodes::_l2i: { + Register rlo = op->in_opr()->as_register_lo(); + Register rhi = op->in_opr()->as_register_hi(); + Register rdst = dst->as_register(); +#ifdef _LP64 + __ sra(rlo, 0, rdst); +#else + __ mov(rlo, rdst); +#endif + break; + } + case Bytecodes::_d2f: + case Bytecodes::_f2d: { + bool is_double = (code == Bytecodes::_f2d); + assert((!is_double && dst->is_single_fpu()) || (is_double && dst->is_double_fpu()), "check"); + LIR_Opr val = op->in_opr(); + FloatRegister rval = (code == Bytecodes::_d2f) ? val->as_double_reg() : val->as_float_reg(); + FloatRegister rdst = is_double ? dst->as_double_reg() : dst->as_float_reg(); + FloatRegisterImpl::Width vw = is_double ? FloatRegisterImpl::S : FloatRegisterImpl::D; + FloatRegisterImpl::Width dw = is_double ? FloatRegisterImpl::D : FloatRegisterImpl::S; + __ ftof(vw, dw, rval, rdst); + break; + } + case Bytecodes::_i2s: + case Bytecodes::_i2b: { + Register rval = op->in_opr()->as_register(); + Register rdst = dst->as_register(); + int shift = (code == Bytecodes::_i2b) ? (BitsPerInt - T_BYTE_aelem_bytes * BitsPerByte) : (BitsPerInt - BitsPerShort); + __ sll (rval, shift, rdst); + __ sra (rdst, shift, rdst); + break; + } + case Bytecodes::_i2c: { + Register rval = op->in_opr()->as_register(); + Register rdst = dst->as_register(); + int shift = BitsPerInt - T_CHAR_aelem_bytes * BitsPerByte; + __ sll (rval, shift, rdst); + __ srl (rdst, shift, rdst); + break; + } + + default: ShouldNotReachHere(); + } +} + + +void LIR_Assembler::align_call(LIR_Code) { + // do nothing since all instructions are word aligned on sparc +} + + +void LIR_Assembler::call(address entry, relocInfo::relocType rtype, CodeEmitInfo* info) { + __ call(entry, rtype); + // the peephole pass fills the delay slot +} + + +void LIR_Assembler::ic_call(address entry, CodeEmitInfo* info) { + RelocationHolder rspec = virtual_call_Relocation::spec(pc()); + __ set_oop((jobject)Universe::non_oop_word(), G5_inline_cache_reg); + __ relocate(rspec); + __ call(entry, relocInfo::none); + // the peephole pass fills the delay slot +} + + +void LIR_Assembler::vtable_call(int vtable_offset, CodeEmitInfo* info) { + add_debug_info_for_null_check_here(info); + __ ld_ptr(Address(O0, 0, oopDesc::klass_offset_in_bytes()), G3_scratch); + if (__ is_simm13(vtable_offset) ) { + __ ld_ptr(G3_scratch, vtable_offset, G5_method); + } else { + // This will generate 2 instructions + __ set(vtable_offset, G5_method); + // ld_ptr, set_hi, set + __ ld_ptr(G3_scratch, G5_method, G5_method); + } + __ ld_ptr(G5_method, in_bytes(methodOopDesc::from_compiled_offset()), G3_scratch); + __ callr(G3_scratch, G0); + // the peephole pass fills the delay slot +} + + +// load with 32-bit displacement +int LIR_Assembler::load(Register s, int disp, Register d, BasicType ld_type, CodeEmitInfo *info) { + int load_offset = code_offset(); + if (Assembler::is_simm13(disp)) { + if (info != NULL) add_debug_info_for_null_check_here(info); + switch(ld_type) { + case T_BOOLEAN: // fall through + case T_BYTE : __ ldsb(s, disp, d); break; + case T_CHAR : __ lduh(s, disp, d); break; + case T_SHORT : __ ldsh(s, disp, d); break; + case T_INT : __ ld(s, disp, d); break; + case T_ADDRESS:// fall through + case T_ARRAY : // fall through + case T_OBJECT: __ ld_ptr(s, disp, d); break; + default : ShouldNotReachHere(); + } + } else { + __ sethi(disp & ~0x3ff, O7, true); + __ add(O7, disp & 0x3ff, O7); + if (info != NULL) add_debug_info_for_null_check_here(info); + load_offset = code_offset(); + switch(ld_type) { + case T_BOOLEAN: // fall through + case T_BYTE : __ ldsb(s, O7, d); break; + case T_CHAR : __ lduh(s, O7, d); break; + case T_SHORT : __ ldsh(s, O7, d); break; + case T_INT : __ ld(s, O7, d); break; + case T_ADDRESS:// fall through + case T_ARRAY : // fall through + case T_OBJECT: __ ld_ptr(s, O7, d); break; + default : ShouldNotReachHere(); + } + } + if (ld_type == T_ARRAY || ld_type == T_OBJECT) __ verify_oop(d); + return load_offset; +} + + +// store with 32-bit displacement +void LIR_Assembler::store(Register value, Register base, int offset, BasicType type, CodeEmitInfo *info) { + if (Assembler::is_simm13(offset)) { + if (info != NULL) add_debug_info_for_null_check_here(info); + switch (type) { + case T_BOOLEAN: // fall through + case T_BYTE : __ stb(value, base, offset); break; + case T_CHAR : __ sth(value, base, offset); break; + case T_SHORT : __ sth(value, base, offset); break; + case T_INT : __ stw(value, base, offset); break; + case T_ADDRESS:// fall through + case T_ARRAY : // fall through + case T_OBJECT: __ st_ptr(value, base, offset); break; + default : ShouldNotReachHere(); + } + } else { + __ sethi(offset & ~0x3ff, O7, true); + __ add(O7, offset & 0x3ff, O7); + if (info != NULL) add_debug_info_for_null_check_here(info); + switch (type) { + case T_BOOLEAN: // fall through + case T_BYTE : __ stb(value, base, O7); break; + case T_CHAR : __ sth(value, base, O7); break; + case T_SHORT : __ sth(value, base, O7); break; + case T_INT : __ stw(value, base, O7); break; + case T_ADDRESS:// fall through + case T_ARRAY : //fall through + case T_OBJECT: __ st_ptr(value, base, O7); break; + default : ShouldNotReachHere(); + } + } + // Note: Do the store before verification as the code might be patched! + if (type == T_ARRAY || type == T_OBJECT) __ verify_oop(value); +} + + +// load float with 32-bit displacement +void LIR_Assembler::load(Register s, int disp, FloatRegister d, BasicType ld_type, CodeEmitInfo *info) { + FloatRegisterImpl::Width w; + switch(ld_type) { + case T_FLOAT : w = FloatRegisterImpl::S; break; + case T_DOUBLE: w = FloatRegisterImpl::D; break; + default : ShouldNotReachHere(); + } + + if (Assembler::is_simm13(disp)) { + if (info != NULL) add_debug_info_for_null_check_here(info); + if (disp % BytesPerLong != 0 && w == FloatRegisterImpl::D) { + __ ldf(FloatRegisterImpl::S, s, disp + BytesPerWord, d->successor()); + __ ldf(FloatRegisterImpl::S, s, disp , d); + } else { + __ ldf(w, s, disp, d); + } + } else { + __ sethi(disp & ~0x3ff, O7, true); + __ add(O7, disp & 0x3ff, O7); + if (info != NULL) add_debug_info_for_null_check_here(info); + __ ldf(w, s, O7, d); + } +} + + +// store float with 32-bit displacement +void LIR_Assembler::store(FloatRegister value, Register base, int offset, BasicType type, CodeEmitInfo *info) { + FloatRegisterImpl::Width w; + switch(type) { + case T_FLOAT : w = FloatRegisterImpl::S; break; + case T_DOUBLE: w = FloatRegisterImpl::D; break; + default : ShouldNotReachHere(); + } + + if (Assembler::is_simm13(offset)) { + if (info != NULL) add_debug_info_for_null_check_here(info); + if (w == FloatRegisterImpl::D && offset % BytesPerLong != 0) { + __ stf(FloatRegisterImpl::S, value->successor(), base, offset + BytesPerWord); + __ stf(FloatRegisterImpl::S, value , base, offset); + } else { + __ stf(w, value, base, offset); + } + } else { + __ sethi(offset & ~0x3ff, O7, true); + __ add(O7, offset & 0x3ff, O7); + if (info != NULL) add_debug_info_for_null_check_here(info); + __ stf(w, value, O7, base); + } +} + + +int LIR_Assembler::store(LIR_Opr from_reg, Register base, int offset, BasicType type, bool unaligned) { + int store_offset; + if (!Assembler::is_simm13(offset + (type == T_LONG) ? wordSize : 0)) { + assert(!unaligned, "can't handle this"); + // for offsets larger than a simm13 we setup the offset in O7 + __ sethi(offset & ~0x3ff, O7, true); + __ add(O7, offset & 0x3ff, O7); + store_offset = store(from_reg, base, O7, type); + } else { + if (type == T_ARRAY || type == T_OBJECT) __ verify_oop(from_reg->as_register()); + store_offset = code_offset(); + switch (type) { + case T_BOOLEAN: // fall through + case T_BYTE : __ stb(from_reg->as_register(), base, offset); break; + case T_CHAR : __ sth(from_reg->as_register(), base, offset); break; + case T_SHORT : __ sth(from_reg->as_register(), base, offset); break; + case T_INT : __ stw(from_reg->as_register(), base, offset); break; + case T_LONG : +#ifdef _LP64 + if (unaligned || PatchALot) { + __ srax(from_reg->as_register_lo(), 32, O7); + __ stw(from_reg->as_register_lo(), base, offset + lo_word_offset_in_bytes); + __ stw(O7, base, offset + hi_word_offset_in_bytes); + } else { + __ stx(from_reg->as_register_lo(), base, offset); + } +#else + assert(Assembler::is_simm13(offset + 4), "must be"); + __ stw(from_reg->as_register_lo(), base, offset + lo_word_offset_in_bytes); + __ stw(from_reg->as_register_hi(), base, offset + hi_word_offset_in_bytes); +#endif + break; + case T_ADDRESS:// fall through + case T_ARRAY : // fall through + case T_OBJECT: __ st_ptr(from_reg->as_register(), base, offset); break; + case T_FLOAT : __ stf(FloatRegisterImpl::S, from_reg->as_float_reg(), base, offset); break; + case T_DOUBLE: + { + FloatRegister reg = from_reg->as_double_reg(); + // split unaligned stores + if (unaligned || PatchALot) { + assert(Assembler::is_simm13(offset + 4), "must be"); + __ stf(FloatRegisterImpl::S, reg->successor(), base, offset + 4); + __ stf(FloatRegisterImpl::S, reg, base, offset); + } else { + __ stf(FloatRegisterImpl::D, reg, base, offset); + } + break; + } + default : ShouldNotReachHere(); + } + } + return store_offset; +} + + +int LIR_Assembler::store(LIR_Opr from_reg, Register base, Register disp, BasicType type) { + if (type == T_ARRAY || type == T_OBJECT) __ verify_oop(from_reg->as_register()); + int store_offset = code_offset(); + switch (type) { + case T_BOOLEAN: // fall through + case T_BYTE : __ stb(from_reg->as_register(), base, disp); break; + case T_CHAR : __ sth(from_reg->as_register(), base, disp); break; + case T_SHORT : __ sth(from_reg->as_register(), base, disp); break; + case T_INT : __ stw(from_reg->as_register(), base, disp); break; + case T_LONG : +#ifdef _LP64 + __ stx(from_reg->as_register_lo(), base, disp); +#else + assert(from_reg->as_register_hi()->successor() == from_reg->as_register_lo(), "must match"); + __ std(from_reg->as_register_hi(), base, disp); +#endif + break; + case T_ADDRESS:// fall through + case T_ARRAY : // fall through + case T_OBJECT: __ st_ptr(from_reg->as_register(), base, disp); break; + case T_FLOAT : __ stf(FloatRegisterImpl::S, from_reg->as_float_reg(), base, disp); break; + case T_DOUBLE: __ stf(FloatRegisterImpl::D, from_reg->as_double_reg(), base, disp); break; + default : ShouldNotReachHere(); + } + return store_offset; +} + + +int LIR_Assembler::load(Register base, int offset, LIR_Opr to_reg, BasicType type, bool unaligned) { + int load_offset; + if (!Assembler::is_simm13(offset + (type == T_LONG) ? wordSize : 0)) { + assert(base != O7, "destroying register"); + assert(!unaligned, "can't handle this"); + // for offsets larger than a simm13 we setup the offset in O7 + __ sethi(offset & ~0x3ff, O7, true); + __ add(O7, offset & 0x3ff, O7); + load_offset = load(base, O7, to_reg, type); + } else { + load_offset = code_offset(); + switch(type) { + case T_BOOLEAN: // fall through + case T_BYTE : __ ldsb(base, offset, to_reg->as_register()); break; + case T_CHAR : __ lduh(base, offset, to_reg->as_register()); break; + case T_SHORT : __ ldsh(base, offset, to_reg->as_register()); break; + case T_INT : __ ld(base, offset, to_reg->as_register()); break; + case T_LONG : + if (!unaligned) { +#ifdef _LP64 + __ ldx(base, offset, to_reg->as_register_lo()); +#else + assert(to_reg->as_register_hi()->successor() == to_reg->as_register_lo(), + "must be sequential"); + __ ldd(base, offset, to_reg->as_register_hi()); +#endif + } else { +#ifdef _LP64 + assert(base != to_reg->as_register_lo(), "can't handle this"); + __ ld(base, offset + hi_word_offset_in_bytes, to_reg->as_register_lo()); + __ sllx(to_reg->as_register_lo(), 32, to_reg->as_register_lo()); + __ ld(base, offset + lo_word_offset_in_bytes, to_reg->as_register_lo()); +#else + if (base == to_reg->as_register_lo()) { + __ ld(base, offset + hi_word_offset_in_bytes, to_reg->as_register_hi()); + __ ld(base, offset + lo_word_offset_in_bytes, to_reg->as_register_lo()); + } else { + __ ld(base, offset + lo_word_offset_in_bytes, to_reg->as_register_lo()); + __ ld(base, offset + hi_word_offset_in_bytes, to_reg->as_register_hi()); + } +#endif + } + break; + case T_ADDRESS:// fall through + case T_ARRAY : // fall through + case T_OBJECT: __ ld_ptr(base, offset, to_reg->as_register()); break; + case T_FLOAT: __ ldf(FloatRegisterImpl::S, base, offset, to_reg->as_float_reg()); break; + case T_DOUBLE: + { + FloatRegister reg = to_reg->as_double_reg(); + // split unaligned loads + if (unaligned || PatchALot) { + __ ldf(FloatRegisterImpl::S, base, offset + BytesPerWord, reg->successor()); + __ ldf(FloatRegisterImpl::S, base, offset, reg); + } else { + __ ldf(FloatRegisterImpl::D, base, offset, to_reg->as_double_reg()); + } + break; + } + default : ShouldNotReachHere(); + } + if (type == T_ARRAY || type == T_OBJECT) __ verify_oop(to_reg->as_register()); + } + return load_offset; +} + + +int LIR_Assembler::load(Register base, Register disp, LIR_Opr to_reg, BasicType type) { + int load_offset = code_offset(); + switch(type) { + case T_BOOLEAN: // fall through + case T_BYTE : __ ldsb(base, disp, to_reg->as_register()); break; + case T_CHAR : __ lduh(base, disp, to_reg->as_register()); break; + case T_SHORT : __ ldsh(base, disp, to_reg->as_register()); break; + case T_INT : __ ld(base, disp, to_reg->as_register()); break; + case T_ADDRESS:// fall through + case T_ARRAY : // fall through + case T_OBJECT: __ ld_ptr(base, disp, to_reg->as_register()); break; + case T_FLOAT: __ ldf(FloatRegisterImpl::S, base, disp, to_reg->as_float_reg()); break; + case T_DOUBLE: __ ldf(FloatRegisterImpl::D, base, disp, to_reg->as_double_reg()); break; + case T_LONG : +#ifdef _LP64 + __ ldx(base, disp, to_reg->as_register_lo()); +#else + assert(to_reg->as_register_hi()->successor() == to_reg->as_register_lo(), + "must be sequential"); + __ ldd(base, disp, to_reg->as_register_hi()); +#endif + break; + default : ShouldNotReachHere(); + } + if (type == T_ARRAY || type == T_OBJECT) __ verify_oop(to_reg->as_register()); + return load_offset; +} + + +// load/store with an Address +void LIR_Assembler::load(const Address& a, Register d, BasicType ld_type, CodeEmitInfo *info, int offset) { + load(a.base(), a.disp() + offset, d, ld_type, info); +} + + +void LIR_Assembler::store(Register value, const Address& dest, BasicType type, CodeEmitInfo *info, int offset) { + store(value, dest.base(), dest.disp() + offset, type, info); +} + + +// loadf/storef with an Address +void LIR_Assembler::load(const Address& a, FloatRegister d, BasicType ld_type, CodeEmitInfo *info, int offset) { + load(a.base(), a.disp() + offset, d, ld_type, info); +} + + +void LIR_Assembler::store(FloatRegister value, const Address& dest, BasicType type, CodeEmitInfo *info, int offset) { + store(value, dest.base(), dest.disp() + offset, type, info); +} + + +// load/store with an Address +void LIR_Assembler::load(LIR_Address* a, Register d, BasicType ld_type, CodeEmitInfo *info) { + load(as_Address(a), d, ld_type, info); +} + + +void LIR_Assembler::store(Register value, LIR_Address* dest, BasicType type, CodeEmitInfo *info) { + store(value, as_Address(dest), type, info); +} + + +// loadf/storef with an Address +void LIR_Assembler::load(LIR_Address* a, FloatRegister d, BasicType ld_type, CodeEmitInfo *info) { + load(as_Address(a), d, ld_type, info); +} + + +void LIR_Assembler::store(FloatRegister value, LIR_Address* dest, BasicType type, CodeEmitInfo *info) { + store(value, as_Address(dest), type, info); +} + + +void LIR_Assembler::const2stack(LIR_Opr src, LIR_Opr dest) { + LIR_Const* c = src->as_constant_ptr(); + switch (c->type()) { + case T_INT: + case T_FLOAT: { + Register src_reg = O7; + int value = c->as_jint_bits(); + if (value == 0) { + src_reg = G0; + } else { + __ set(value, O7); + } + Address addr = frame_map()->address_for_slot(dest->single_stack_ix()); + __ stw(src_reg, addr.base(), addr.disp()); + break; + } + case T_OBJECT: { + Register src_reg = O7; + jobject2reg(c->as_jobject(), src_reg); + Address addr = frame_map()->address_for_slot(dest->single_stack_ix()); + __ st_ptr(src_reg, addr.base(), addr.disp()); + break; + } + case T_LONG: + case T_DOUBLE: { + Address addr = frame_map()->address_for_double_slot(dest->double_stack_ix()); + + Register tmp = O7; + int value_lo = c->as_jint_lo_bits(); + if (value_lo == 0) { + tmp = G0; + } else { + __ set(value_lo, O7); + } + __ stw(tmp, addr.base(), addr.disp() + lo_word_offset_in_bytes); + int value_hi = c->as_jint_hi_bits(); + if (value_hi == 0) { + tmp = G0; + } else { + __ set(value_hi, O7); + } + __ stw(tmp, addr.base(), addr.disp() + hi_word_offset_in_bytes); + break; + } + default: + Unimplemented(); + } +} + + +void LIR_Assembler::const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info ) { + LIR_Const* c = src->as_constant_ptr(); + LIR_Address* addr = dest->as_address_ptr(); + Register base = addr->base()->as_pointer_register(); + + if (info != NULL) { + add_debug_info_for_null_check_here(info); + } + switch (c->type()) { + case T_INT: + case T_FLOAT: { + LIR_Opr tmp = FrameMap::O7_opr; + int value = c->as_jint_bits(); + if (value == 0) { + tmp = FrameMap::G0_opr; + } else if (Assembler::is_simm13(value)) { + __ set(value, O7); + } + if (addr->index()->is_valid()) { + assert(addr->disp() == 0, "must be zero"); + store(tmp, base, addr->index()->as_pointer_register(), type); + } else { + assert(Assembler::is_simm13(addr->disp()), "can't handle larger addresses"); + store(tmp, base, addr->disp(), type); + } + break; + } + case T_LONG: + case T_DOUBLE: { + assert(!addr->index()->is_valid(), "can't handle reg reg address here"); + assert(Assembler::is_simm13(addr->disp()) && + Assembler::is_simm13(addr->disp() + 4), "can't handle larger addresses"); + + Register tmp = O7; + int value_lo = c->as_jint_lo_bits(); + if (value_lo == 0) { + tmp = G0; + } else { + __ set(value_lo, O7); + } + store(tmp, base, addr->disp() + lo_word_offset_in_bytes, T_INT); + int value_hi = c->as_jint_hi_bits(); + if (value_hi == 0) { + tmp = G0; + } else { + __ set(value_hi, O7); + } + store(tmp, base, addr->disp() + hi_word_offset_in_bytes, T_INT); + break; + } + case T_OBJECT: { + jobject obj = c->as_jobject(); + LIR_Opr tmp; + if (obj == NULL) { + tmp = FrameMap::G0_opr; + } else { + tmp = FrameMap::O7_opr; + jobject2reg(c->as_jobject(), O7); + } + // handle either reg+reg or reg+disp address + if (addr->index()->is_valid()) { + assert(addr->disp() == 0, "must be zero"); + store(tmp, base, addr->index()->as_pointer_register(), type); + } else { + assert(Assembler::is_simm13(addr->disp()), "can't handle larger addresses"); + store(tmp, base, addr->disp(), type); + } + + break; + } + default: + Unimplemented(); + } +} + + +void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_code, CodeEmitInfo* info) { + LIR_Const* c = src->as_constant_ptr(); + LIR_Opr to_reg = dest; + + switch (c->type()) { + case T_INT: + { + jint con = c->as_jint(); + if (to_reg->is_single_cpu()) { + assert(patch_code == lir_patch_none, "no patching handled here"); + __ set(con, to_reg->as_register()); + } else { + ShouldNotReachHere(); + assert(to_reg->is_single_fpu(), "wrong register kind"); + + __ set(con, O7); + Address temp_slot(SP, 0, (frame::register_save_words * wordSize) + STACK_BIAS); + __ st(O7, temp_slot); + __ ldf(FloatRegisterImpl::S, temp_slot, to_reg->as_float_reg()); + } + } + break; + + case T_LONG: + { + jlong con = c->as_jlong(); + + if (to_reg->is_double_cpu()) { +#ifdef _LP64 + __ set(con, to_reg->as_register_lo()); +#else + __ set(low(con), to_reg->as_register_lo()); + __ set(high(con), to_reg->as_register_hi()); +#endif +#ifdef _LP64 + } else if (to_reg->is_single_cpu()) { + __ set(con, to_reg->as_register()); +#endif + } else { + ShouldNotReachHere(); + assert(to_reg->is_double_fpu(), "wrong register kind"); + Address temp_slot_lo(SP, 0, ((frame::register_save_words ) * wordSize) + STACK_BIAS); + Address temp_slot_hi(SP, 0, ((frame::register_save_words) * wordSize) + (longSize/2) + STACK_BIAS); + __ set(low(con), O7); + __ st(O7, temp_slot_lo); + __ set(high(con), O7); + __ st(O7, temp_slot_hi); + __ ldf(FloatRegisterImpl::D, temp_slot_lo, to_reg->as_double_reg()); + } + } + break; + + case T_OBJECT: + { + if (patch_code == lir_patch_none) { + jobject2reg(c->as_jobject(), to_reg->as_register()); + } else { + jobject2reg_with_patching(to_reg->as_register(), info); + } + } + break; + + case T_FLOAT: + { + address const_addr = __ float_constant(c->as_jfloat()); + if (const_addr == NULL) { + bailout("const section overflow"); + break; + } + RelocationHolder rspec = internal_word_Relocation::spec(const_addr); + if (to_reg->is_single_fpu()) { + __ sethi( (intx)const_addr & ~0x3ff, O7, true, rspec); + __ relocate(rspec); + + int offset = (intx)const_addr & 0x3ff; + __ ldf (FloatRegisterImpl::S, O7, offset, to_reg->as_float_reg()); + + } else { + assert(to_reg->is_single_cpu(), "Must be a cpu register."); + + __ set((intx)const_addr, O7, rspec); + load(O7, 0, to_reg->as_register(), T_INT); + } + } + break; + + case T_DOUBLE: + { + address const_addr = __ double_constant(c->as_jdouble()); + if (const_addr == NULL) { + bailout("const section overflow"); + break; + } + RelocationHolder rspec = internal_word_Relocation::spec(const_addr); + + if (to_reg->is_double_fpu()) { + __ sethi( (intx)const_addr & ~0x3ff, O7, true, rspec); + int offset = (intx)const_addr & 0x3ff; + __ relocate(rspec); + __ ldf (FloatRegisterImpl::D, O7, offset, to_reg->as_double_reg()); + } else { + assert(to_reg->is_double_cpu(), "Must be a long register."); +#ifdef _LP64 + __ set(jlong_cast(c->as_jdouble()), to_reg->as_register_lo()); +#else + __ set(low(jlong_cast(c->as_jdouble())), to_reg->as_register_lo()); + __ set(high(jlong_cast(c->as_jdouble())), to_reg->as_register_hi()); +#endif + } + + } + break; + + default: + ShouldNotReachHere(); + } +} + +Address LIR_Assembler::as_Address(LIR_Address* addr) { + Register reg = addr->base()->as_register(); + return Address(reg, 0, addr->disp()); +} + + +void LIR_Assembler::stack2stack(LIR_Opr src, LIR_Opr dest, BasicType type) { + switch (type) { + case T_INT: + case T_FLOAT: { + Register tmp = O7; + Address from = frame_map()->address_for_slot(src->single_stack_ix()); + Address to = frame_map()->address_for_slot(dest->single_stack_ix()); + __ lduw(from.base(), from.disp(), tmp); + __ stw(tmp, to.base(), to.disp()); + break; + } + case T_OBJECT: { + Register tmp = O7; + Address from = frame_map()->address_for_slot(src->single_stack_ix()); + Address to = frame_map()->address_for_slot(dest->single_stack_ix()); + __ ld_ptr(from.base(), from.disp(), tmp); + __ st_ptr(tmp, to.base(), to.disp()); + break; + } + case T_LONG: + case T_DOUBLE: { + Register tmp = O7; + Address from = frame_map()->address_for_double_slot(src->double_stack_ix()); + Address to = frame_map()->address_for_double_slot(dest->double_stack_ix()); + __ lduw(from.base(), from.disp(), tmp); + __ stw(tmp, to.base(), to.disp()); + __ lduw(from.base(), from.disp() + 4, tmp); + __ stw(tmp, to.base(), to.disp() + 4); + break; + } + + default: + ShouldNotReachHere(); + } +} + + +Address LIR_Assembler::as_Address_hi(LIR_Address* addr) { + Address base = as_Address(addr); + return Address(base.base(), 0, base.disp() + hi_word_offset_in_bytes); +} + + +Address LIR_Assembler::as_Address_lo(LIR_Address* addr) { + Address base = as_Address(addr); + return Address(base.base(), 0, base.disp() + lo_word_offset_in_bytes); +} + + +void LIR_Assembler::mem2reg(LIR_Opr src_opr, LIR_Opr dest, BasicType type, + LIR_PatchCode patch_code, CodeEmitInfo* info, bool unaligned) { + + LIR_Address* addr = src_opr->as_address_ptr(); + LIR_Opr to_reg = dest; + + Register src = addr->base()->as_pointer_register(); + Register disp_reg = noreg; + int disp_value = addr->disp(); + bool needs_patching = (patch_code != lir_patch_none); + + if (addr->base()->type() == T_OBJECT) { + __ verify_oop(src); + } + + PatchingStub* patch = NULL; + if (needs_patching) { + patch = new PatchingStub(_masm, PatchingStub::access_field_id); + assert(!to_reg->is_double_cpu() || + patch_code == lir_patch_none || + patch_code == lir_patch_normal, "patching doesn't match register"); + } + + if (addr->index()->is_illegal()) { + if (!Assembler::is_simm13(disp_value) && (!unaligned || Assembler::is_simm13(disp_value + 4))) { + if (needs_patching) { + __ sethi(0, O7, true); + __ add(O7, 0, O7); + } else { + __ set(disp_value, O7); + } + disp_reg = O7; + } + } else if (unaligned || PatchALot) { + __ add(src, addr->index()->as_register(), O7); + src = O7; + } else { + disp_reg = addr->index()->as_pointer_register(); + assert(disp_value == 0, "can't handle 3 operand addresses"); + } + + // remember the offset of the load. The patching_epilog must be done + // before the call to add_debug_info, otherwise the PcDescs don't get + // entered in increasing order. + int offset = code_offset(); + + assert(disp_reg != noreg || Assembler::is_simm13(disp_value), "should have set this up"); + if (disp_reg == noreg) { + offset = load(src, disp_value, to_reg, type, unaligned); + } else { + assert(!unaligned, "can't handle this"); + offset = load(src, disp_reg, to_reg, type); + } + + if (patch != NULL) { + patching_epilog(patch, patch_code, src, info); + } + + if (info != NULL) add_debug_info_for_null_check(offset, info); +} + + +void LIR_Assembler::prefetchr(LIR_Opr src) { + LIR_Address* addr = src->as_address_ptr(); + Address from_addr = as_Address(addr); + + if (VM_Version::has_v9()) { + __ prefetch(from_addr, Assembler::severalReads); + } +} + + +void LIR_Assembler::prefetchw(LIR_Opr src) { + LIR_Address* addr = src->as_address_ptr(); + Address from_addr = as_Address(addr); + + if (VM_Version::has_v9()) { + __ prefetch(from_addr, Assembler::severalWritesAndPossiblyReads); + } +} + + +void LIR_Assembler::stack2reg(LIR_Opr src, LIR_Opr dest, BasicType type) { + Address addr; + if (src->is_single_word()) { + addr = frame_map()->address_for_slot(src->single_stack_ix()); + } else if (src->is_double_word()) { + addr = frame_map()->address_for_double_slot(src->double_stack_ix()); + } + + bool unaligned = (addr.disp() - STACK_BIAS) % 8 != 0; + load(addr.base(), addr.disp(), dest, dest->type(), unaligned); +} + + +void LIR_Assembler::reg2stack(LIR_Opr from_reg, LIR_Opr dest, BasicType type, bool pop_fpu_stack) { + Address addr; + if (dest->is_single_word()) { + addr = frame_map()->address_for_slot(dest->single_stack_ix()); + } else if (dest->is_double_word()) { + addr = frame_map()->address_for_slot(dest->double_stack_ix()); + } + bool unaligned = (addr.disp() - STACK_BIAS) % 8 != 0; + store(from_reg, addr.base(), addr.disp(), from_reg->type(), unaligned); +} + + +void LIR_Assembler::reg2reg(LIR_Opr from_reg, LIR_Opr to_reg) { + if (from_reg->is_float_kind() && to_reg->is_float_kind()) { + if (from_reg->is_double_fpu()) { + // double to double moves + assert(to_reg->is_double_fpu(), "should match"); + __ fmov(FloatRegisterImpl::D, from_reg->as_double_reg(), to_reg->as_double_reg()); + } else { + // float to float moves + assert(to_reg->is_single_fpu(), "should match"); + __ fmov(FloatRegisterImpl::S, from_reg->as_float_reg(), to_reg->as_float_reg()); + } + } else if (!from_reg->is_float_kind() && !to_reg->is_float_kind()) { + if (from_reg->is_double_cpu()) { +#ifdef _LP64 + __ mov(from_reg->as_pointer_register(), to_reg->as_pointer_register()); +#else + assert(to_reg->is_double_cpu() && + from_reg->as_register_hi() != to_reg->as_register_lo() && + from_reg->as_register_lo() != to_reg->as_register_hi(), + "should both be long and not overlap"); + // long to long moves + __ mov(from_reg->as_register_hi(), to_reg->as_register_hi()); + __ mov(from_reg->as_register_lo(), to_reg->as_register_lo()); +#endif +#ifdef _LP64 + } else if (to_reg->is_double_cpu()) { + // int to int moves + __ mov(from_reg->as_register(), to_reg->as_register_lo()); +#endif + } else { + // int to int moves + __ mov(from_reg->as_register(), to_reg->as_register()); + } + } else { + ShouldNotReachHere(); + } + if (to_reg->type() == T_OBJECT || to_reg->type() == T_ARRAY) { + __ verify_oop(to_reg->as_register()); + } +} + + +void LIR_Assembler::reg2mem(LIR_Opr from_reg, LIR_Opr dest, BasicType type, + LIR_PatchCode patch_code, CodeEmitInfo* info, bool pop_fpu_stack, + bool unaligned) { + LIR_Address* addr = dest->as_address_ptr(); + + Register src = addr->base()->as_pointer_register(); + Register disp_reg = noreg; + int disp_value = addr->disp(); + bool needs_patching = (patch_code != lir_patch_none); + + if (addr->base()->is_oop_register()) { + __ verify_oop(src); + } + + PatchingStub* patch = NULL; + if (needs_patching) { + patch = new PatchingStub(_masm, PatchingStub::access_field_id); + assert(!from_reg->is_double_cpu() || + patch_code == lir_patch_none || + patch_code == lir_patch_normal, "patching doesn't match register"); + } + + if (addr->index()->is_illegal()) { + if (!Assembler::is_simm13(disp_value) && (!unaligned || Assembler::is_simm13(disp_value + 4))) { + if (needs_patching) { + __ sethi(0, O7, true); + __ add(O7, 0, O7); + } else { + __ set(disp_value, O7); + } + disp_reg = O7; + } + } else if (unaligned || PatchALot) { + __ add(src, addr->index()->as_register(), O7); + src = O7; + } else { + disp_reg = addr->index()->as_pointer_register(); + assert(disp_value == 0, "can't handle 3 operand addresses"); + } + + // remember the offset of the store. The patching_epilog must be done + // before the call to add_debug_info_for_null_check, otherwise the PcDescs don't get + // entered in increasing order. + int offset; + + assert(disp_reg != noreg || Assembler::is_simm13(disp_value), "should have set this up"); + if (disp_reg == noreg) { + offset = store(from_reg, src, disp_value, type, unaligned); + } else { + assert(!unaligned, "can't handle this"); + offset = store(from_reg, src, disp_reg, type); + } + + if (patch != NULL) { + patching_epilog(patch, patch_code, src, info); + } + + if (info != NULL) add_debug_info_for_null_check(offset, info); +} + + +void LIR_Assembler::return_op(LIR_Opr result) { + // the poll may need a register so just pick one that isn't the return register +#ifdef TIERED + if (result->type_field() == LIR_OprDesc::long_type) { + // Must move the result to G1 + // Must leave proper result in O0,O1 and G1 (TIERED only) + __ sllx(I0, 32, G1); // Shift bits into high G1 + __ srl (I1, 0, I1); // Zero extend O1 (harmless?) + __ or3 (I1, G1, G1); // OR 64 bits into G1 + } +#endif // TIERED + __ set((intptr_t)os::get_polling_page(), L0); + __ relocate(relocInfo::poll_return_type); + __ ld_ptr(L0, 0, G0); + __ ret(); + __ delayed()->restore(); +} + + +int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) { + __ set((intptr_t)os::get_polling_page(), tmp->as_register()); + if (info != NULL) { + add_debug_info_for_branch(info); + } else { + __ relocate(relocInfo::poll_type); + } + + int offset = __ offset(); + __ ld_ptr(tmp->as_register(), 0, G0); + + return offset; +} + + +void LIR_Assembler::emit_static_call_stub() { + address call_pc = __ pc(); + address stub = __ start_a_stub(call_stub_size); + if (stub == NULL) { + bailout("static call stub overflow"); + return; + } + + int start = __ offset(); + __ relocate(static_stub_Relocation::spec(call_pc)); + + __ set_oop(NULL, G5); + // must be set to -1 at code generation time + Address a(G3, (address)-1); + __ jump_to(a, 0); + __ delayed()->nop(); + + assert(__ offset() - start <= call_stub_size, "stub too big"); + __ end_a_stub(); +} + + +void LIR_Assembler::comp_op(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Op2* op) { + if (opr1->is_single_fpu()) { + __ fcmp(FloatRegisterImpl::S, Assembler::fcc0, opr1->as_float_reg(), opr2->as_float_reg()); + } else if (opr1->is_double_fpu()) { + __ fcmp(FloatRegisterImpl::D, Assembler::fcc0, opr1->as_double_reg(), opr2->as_double_reg()); + } else if (opr1->is_single_cpu()) { + if (opr2->is_constant()) { + switch (opr2->as_constant_ptr()->type()) { + case T_INT: + { jint con = opr2->as_constant_ptr()->as_jint(); + if (Assembler::is_simm13(con)) { + __ cmp(opr1->as_register(), con); + } else { + __ set(con, O7); + __ cmp(opr1->as_register(), O7); + } + } + break; + + case T_OBJECT: + // there are only equal/notequal comparisions on objects + { jobject con = opr2->as_constant_ptr()->as_jobject(); + if (con == NULL) { + __ cmp(opr1->as_register(), 0); + } else { + jobject2reg(con, O7); + __ cmp(opr1->as_register(), O7); + } + } + break; + + default: + ShouldNotReachHere(); + break; + } + } else { + if (opr2->is_address()) { + LIR_Address * addr = opr2->as_address_ptr(); + BasicType type = addr->type(); + if ( type == T_OBJECT ) __ ld_ptr(as_Address(addr), O7); + else __ ld(as_Address(addr), O7); + __ cmp(opr1->as_register(), O7); + } else { + __ cmp(opr1->as_register(), opr2->as_register()); + } + } + } else if (opr1->is_double_cpu()) { + Register xlo = opr1->as_register_lo(); + Register xhi = opr1->as_register_hi(); + if (opr2->is_constant() && opr2->as_jlong() == 0) { + assert(condition == lir_cond_equal || condition == lir_cond_notEqual, "only handles these cases"); +#ifdef _LP64 + __ orcc(xhi, G0, G0); +#else + __ orcc(xhi, xlo, G0); +#endif + } else if (opr2->is_register()) { + Register ylo = opr2->as_register_lo(); + Register yhi = opr2->as_register_hi(); +#ifdef _LP64 + __ cmp(xlo, ylo); +#else + __ subcc(xlo, ylo, xlo); + __ subccc(xhi, yhi, xhi); + if (condition == lir_cond_equal || condition == lir_cond_notEqual) { + __ orcc(xhi, xlo, G0); + } +#endif + } else { + ShouldNotReachHere(); + } + } else if (opr1->is_address()) { + LIR_Address * addr = opr1->as_address_ptr(); + BasicType type = addr->type(); + assert (opr2->is_constant(), "Checking"); + if ( type == T_OBJECT ) __ ld_ptr(as_Address(addr), O7); + else __ ld(as_Address(addr), O7); + __ cmp(O7, opr2->as_constant_ptr()->as_jint()); + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::comp_fl2i(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dst, LIR_Op2* op){ + if (code == lir_cmp_fd2i || code == lir_ucmp_fd2i) { + bool is_unordered_less = (code == lir_ucmp_fd2i); + if (left->is_single_fpu()) { + __ float_cmp(true, is_unordered_less ? -1 : 1, left->as_float_reg(), right->as_float_reg(), dst->as_register()); + } else if (left->is_double_fpu()) { + __ float_cmp(false, is_unordered_less ? -1 : 1, left->as_double_reg(), right->as_double_reg(), dst->as_register()); + } else { + ShouldNotReachHere(); + } + } else if (code == lir_cmp_l2i) { + __ lcmp(left->as_register_hi(), left->as_register_lo(), + right->as_register_hi(), right->as_register_lo(), + dst->as_register()); + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::cmove(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr result) { + + Assembler::Condition acond; + switch (condition) { + case lir_cond_equal: acond = Assembler::equal; break; + case lir_cond_notEqual: acond = Assembler::notEqual; break; + case lir_cond_less: acond = Assembler::less; break; + case lir_cond_lessEqual: acond = Assembler::lessEqual; break; + case lir_cond_greaterEqual: acond = Assembler::greaterEqual; break; + case lir_cond_greater: acond = Assembler::greater; break; + case lir_cond_aboveEqual: acond = Assembler::greaterEqualUnsigned; break; + case lir_cond_belowEqual: acond = Assembler::lessEqualUnsigned; break; + default: ShouldNotReachHere(); + }; + + if (opr1->is_constant() && opr1->type() == T_INT) { + Register dest = result->as_register(); + // load up first part of constant before branch + // and do the rest in the delay slot. + if (!Assembler::is_simm13(opr1->as_jint())) { + __ sethi(opr1->as_jint(), dest); + } + } else if (opr1->is_constant()) { + const2reg(opr1, result, lir_patch_none, NULL); + } else if (opr1->is_register()) { + reg2reg(opr1, result); + } else if (opr1->is_stack()) { + stack2reg(opr1, result, result->type()); + } else { + ShouldNotReachHere(); + } + Label skip; + __ br(acond, false, Assembler::pt, skip); + if (opr1->is_constant() && opr1->type() == T_INT) { + Register dest = result->as_register(); + if (Assembler::is_simm13(opr1->as_jint())) { + __ delayed()->or3(G0, opr1->as_jint(), dest); + } else { + // the sethi has been done above, so just put in the low 10 bits + __ delayed()->or3(dest, opr1->as_jint() & 0x3ff, dest); + } + } else { + // can't do anything useful in the delay slot + __ delayed()->nop(); + } + if (opr2->is_constant()) { + const2reg(opr2, result, lir_patch_none, NULL); + } else if (opr2->is_register()) { + reg2reg(opr2, result); + } else if (opr2->is_stack()) { + stack2reg(opr2, result, result->type()); + } else { + ShouldNotReachHere(); + } + __ bind(skip); +} + + +void LIR_Assembler::arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest, CodeEmitInfo* info, bool pop_fpu_stack) { + assert(info == NULL, "unused on this code path"); + assert(left->is_register(), "wrong items state"); + assert(dest->is_register(), "wrong items state"); + + if (right->is_register()) { + if (dest->is_float_kind()) { + + FloatRegister lreg, rreg, res; + FloatRegisterImpl::Width w; + if (right->is_single_fpu()) { + w = FloatRegisterImpl::S; + lreg = left->as_float_reg(); + rreg = right->as_float_reg(); + res = dest->as_float_reg(); + } else { + w = FloatRegisterImpl::D; + lreg = left->as_double_reg(); + rreg = right->as_double_reg(); + res = dest->as_double_reg(); + } + + switch (code) { + case lir_add: __ fadd(w, lreg, rreg, res); break; + case lir_sub: __ fsub(w, lreg, rreg, res); break; + case lir_mul: // fall through + case lir_mul_strictfp: __ fmul(w, lreg, rreg, res); break; + case lir_div: // fall through + case lir_div_strictfp: __ fdiv(w, lreg, rreg, res); break; + default: ShouldNotReachHere(); + } + + } else if (dest->is_double_cpu()) { +#ifdef _LP64 + Register dst_lo = dest->as_register_lo(); + Register op1_lo = left->as_pointer_register(); + Register op2_lo = right->as_pointer_register(); + + switch (code) { + case lir_add: + __ add(op1_lo, op2_lo, dst_lo); + break; + + case lir_sub: + __ sub(op1_lo, op2_lo, dst_lo); + break; + + default: ShouldNotReachHere(); + } +#else + Register op1_lo = left->as_register_lo(); + Register op1_hi = left->as_register_hi(); + Register op2_lo = right->as_register_lo(); + Register op2_hi = right->as_register_hi(); + Register dst_lo = dest->as_register_lo(); + Register dst_hi = dest->as_register_hi(); + + switch (code) { + case lir_add: + __ addcc(op1_lo, op2_lo, dst_lo); + __ addc (op1_hi, op2_hi, dst_hi); + break; + + case lir_sub: + __ subcc(op1_lo, op2_lo, dst_lo); + __ subc (op1_hi, op2_hi, dst_hi); + break; + + default: ShouldNotReachHere(); + } +#endif + } else { + assert (right->is_single_cpu(), "Just Checking"); + + Register lreg = left->as_register(); + Register res = dest->as_register(); + Register rreg = right->as_register(); + switch (code) { + case lir_add: __ add (lreg, rreg, res); break; + case lir_sub: __ sub (lreg, rreg, res); break; + case lir_mul: __ mult (lreg, rreg, res); break; + default: ShouldNotReachHere(); + } + } + } else { + assert (right->is_constant(), "must be constant"); + + if (dest->is_single_cpu()) { + Register lreg = left->as_register(); + Register res = dest->as_register(); + int simm13 = right->as_constant_ptr()->as_jint(); + + switch (code) { + case lir_add: __ add (lreg, simm13, res); break; + case lir_sub: __ sub (lreg, simm13, res); break; + case lir_mul: __ mult (lreg, simm13, res); break; + default: ShouldNotReachHere(); + } + } else { + Register lreg = left->as_pointer_register(); + Register res = dest->as_register_lo(); + long con = right->as_constant_ptr()->as_jlong(); + assert(Assembler::is_simm13(con), "must be simm13"); + + switch (code) { + case lir_add: __ add (lreg, (int)con, res); break; + case lir_sub: __ sub (lreg, (int)con, res); break; + case lir_mul: __ mult (lreg, (int)con, res); break; + default: ShouldNotReachHere(); + } + } + } +} + + +void LIR_Assembler::fpop() { + // do nothing +} + + +void LIR_Assembler::intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr thread, LIR_Opr dest, LIR_Op* op) { + switch (code) { + case lir_sin: + case lir_tan: + case lir_cos: { + assert(thread->is_valid(), "preserve the thread object for performance reasons"); + assert(dest->as_double_reg() == F0, "the result will be in f0/f1"); + break; + } + case lir_sqrt: { + assert(!thread->is_valid(), "there is no need for a thread_reg for dsqrt"); + FloatRegister src_reg = value->as_double_reg(); + FloatRegister dst_reg = dest->as_double_reg(); + __ fsqrt(FloatRegisterImpl::D, src_reg, dst_reg); + break; + } + case lir_abs: { + assert(!thread->is_valid(), "there is no need for a thread_reg for fabs"); + FloatRegister src_reg = value->as_double_reg(); + FloatRegister dst_reg = dest->as_double_reg(); + __ fabs(FloatRegisterImpl::D, src_reg, dst_reg); + break; + } + default: { + ShouldNotReachHere(); + break; + } + } +} + + +void LIR_Assembler::logic_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest) { + if (right->is_constant()) { + if (dest->is_single_cpu()) { + int simm13 = right->as_constant_ptr()->as_jint(); + switch (code) { + case lir_logic_and: __ and3 (left->as_register(), simm13, dest->as_register()); break; + case lir_logic_or: __ or3 (left->as_register(), simm13, dest->as_register()); break; + case lir_logic_xor: __ xor3 (left->as_register(), simm13, dest->as_register()); break; + default: ShouldNotReachHere(); + } + } else { + long c = right->as_constant_ptr()->as_jlong(); + assert(c == (int)c && Assembler::is_simm13(c), "out of range"); + int simm13 = (int)c; + switch (code) { + case lir_logic_and: +#ifndef _LP64 + __ and3 (left->as_register_hi(), 0, dest->as_register_hi()); +#endif + __ and3 (left->as_register_lo(), simm13, dest->as_register_lo()); + break; + + case lir_logic_or: +#ifndef _LP64 + __ or3 (left->as_register_hi(), 0, dest->as_register_hi()); +#endif + __ or3 (left->as_register_lo(), simm13, dest->as_register_lo()); + break; + + case lir_logic_xor: +#ifndef _LP64 + __ xor3 (left->as_register_hi(), 0, dest->as_register_hi()); +#endif + __ xor3 (left->as_register_lo(), simm13, dest->as_register_lo()); + break; + + default: ShouldNotReachHere(); + } + } + } else { + assert(right->is_register(), "right should be in register"); + + if (dest->is_single_cpu()) { + switch (code) { + case lir_logic_and: __ and3 (left->as_register(), right->as_register(), dest->as_register()); break; + case lir_logic_or: __ or3 (left->as_register(), right->as_register(), dest->as_register()); break; + case lir_logic_xor: __ xor3 (left->as_register(), right->as_register(), dest->as_register()); break; + default: ShouldNotReachHere(); + } + } else { +#ifdef _LP64 + Register l = (left->is_single_cpu() && left->is_oop_register()) ? left->as_register() : + left->as_register_lo(); + Register r = (right->is_single_cpu() && right->is_oop_register()) ? right->as_register() : + right->as_register_lo(); + + switch (code) { + case lir_logic_and: __ and3 (l, r, dest->as_register_lo()); break; + case lir_logic_or: __ or3 (l, r, dest->as_register_lo()); break; + case lir_logic_xor: __ xor3 (l, r, dest->as_register_lo()); break; + default: ShouldNotReachHere(); + } +#else + switch (code) { + case lir_logic_and: + __ and3 (left->as_register_hi(), right->as_register_hi(), dest->as_register_hi()); + __ and3 (left->as_register_lo(), right->as_register_lo(), dest->as_register_lo()); + break; + + case lir_logic_or: + __ or3 (left->as_register_hi(), right->as_register_hi(), dest->as_register_hi()); + __ or3 (left->as_register_lo(), right->as_register_lo(), dest->as_register_lo()); + break; + + case lir_logic_xor: + __ xor3 (left->as_register_hi(), right->as_register_hi(), dest->as_register_hi()); + __ xor3 (left->as_register_lo(), right->as_register_lo(), dest->as_register_lo()); + break; + + default: ShouldNotReachHere(); + } +#endif + } + } +} + + +int LIR_Assembler::shift_amount(BasicType t) { + int elem_size = type2aelembytes[t]; + switch (elem_size) { + case 1 : return 0; + case 2 : return 1; + case 4 : return 2; + case 8 : return 3; + } + ShouldNotReachHere(); + return -1; +} + + +void LIR_Assembler::throw_op(LIR_Opr exceptionPC, LIR_Opr exceptionOop, CodeEmitInfo* info, bool unwind) { + assert(exceptionOop->as_register() == Oexception, "should match"); + assert(unwind || exceptionPC->as_register() == Oissuing_pc, "should match"); + + info->add_register_oop(exceptionOop); + + if (unwind) { + __ call(Runtime1::entry_for(Runtime1::unwind_exception_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + } else { + // reuse the debug info from the safepoint poll for the throw op itself + address pc_for_athrow = __ pc(); + int pc_for_athrow_offset = __ offset(); + RelocationHolder rspec = internal_word_Relocation::spec(pc_for_athrow); + __ set((intptr_t)pc_for_athrow, Oissuing_pc, rspec); + add_call_info(pc_for_athrow_offset, info); // for exception handler + + __ call(Runtime1::entry_for(Runtime1::handle_exception_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + } +} + + +void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { + Register src = op->src()->as_register(); + Register dst = op->dst()->as_register(); + Register src_pos = op->src_pos()->as_register(); + Register dst_pos = op->dst_pos()->as_register(); + Register length = op->length()->as_register(); + Register tmp = op->tmp()->as_register(); + Register tmp2 = O7; + + int flags = op->flags(); + ciArrayKlass* default_type = op->expected_type(); + BasicType basic_type = default_type != NULL ? default_type->element_type()->basic_type() : T_ILLEGAL; + if (basic_type == T_ARRAY) basic_type = T_OBJECT; + + // set up the arraycopy stub information + ArrayCopyStub* stub = op->stub(); + + // always do stub if no type information is available. it's ok if + // the known type isn't loaded since the code sanity checks + // in debug mode and the type isn't required when we know the exact type + // also check that the type is an array type. + if (op->expected_type() == NULL) { + __ mov(src, O0); + __ mov(src_pos, O1); + __ mov(dst, O2); + __ mov(dst_pos, O3); + __ mov(length, O4); + __ call_VM_leaf(tmp, CAST_FROM_FN_PTR(address, Runtime1::arraycopy)); + + __ br_zero(Assembler::less, false, Assembler::pn, O0, *stub->entry()); + __ delayed()->nop(); + __ bind(*stub->continuation()); + return; + } + + assert(default_type != NULL && default_type->is_array_klass(), "must be true at this point"); + + // make sure src and dst are non-null and load array length + if (flags & LIR_OpArrayCopy::src_null_check) { + __ tst(src); + __ br(Assembler::equal, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } + + if (flags & LIR_OpArrayCopy::dst_null_check) { + __ tst(dst); + __ br(Assembler::equal, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } + + if (flags & LIR_OpArrayCopy::src_pos_positive_check) { + // test src_pos register + __ tst(src_pos); + __ br(Assembler::less, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } + + if (flags & LIR_OpArrayCopy::dst_pos_positive_check) { + // test dst_pos register + __ tst(dst_pos); + __ br(Assembler::less, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } + + if (flags & LIR_OpArrayCopy::length_positive_check) { + // make sure length isn't negative + __ tst(length); + __ br(Assembler::less, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } + + if (flags & LIR_OpArrayCopy::src_range_check) { + __ ld(src, arrayOopDesc::length_offset_in_bytes(), tmp2); + __ add(length, src_pos, tmp); + __ cmp(tmp2, tmp); + __ br(Assembler::carrySet, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } + + if (flags & LIR_OpArrayCopy::dst_range_check) { + __ ld(dst, arrayOopDesc::length_offset_in_bytes(), tmp2); + __ add(length, dst_pos, tmp); + __ cmp(tmp2, tmp); + __ br(Assembler::carrySet, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } + + if (flags & LIR_OpArrayCopy::type_check) { + __ ld_ptr(src, oopDesc::klass_offset_in_bytes(), tmp); + __ ld_ptr(dst, oopDesc::klass_offset_in_bytes(), tmp2); + __ cmp(tmp, tmp2); + __ br(Assembler::notEqual, false, Assembler::pt, *stub->entry()); + __ delayed()->nop(); + } + +#ifdef ASSERT + if (basic_type != T_OBJECT || !(flags & LIR_OpArrayCopy::type_check)) { + // Sanity check the known type with the incoming class. For the + // primitive case the types must match exactly with src.klass and + // dst.klass each exactly matching the default type. For the + // object array case, if no type check is needed then either the + // dst type is exactly the expected type and the src type is a + // subtype which we can't check or src is the same array as dst + // but not necessarily exactly of type default_type. + Label known_ok, halt; + jobject2reg(op->expected_type()->encoding(), tmp); + __ ld_ptr(dst, oopDesc::klass_offset_in_bytes(), tmp2); + if (basic_type != T_OBJECT) { + __ cmp(tmp, tmp2); + __ br(Assembler::notEqual, false, Assembler::pn, halt); + __ delayed()->ld_ptr(src, oopDesc::klass_offset_in_bytes(), tmp2); + __ cmp(tmp, tmp2); + __ br(Assembler::equal, false, Assembler::pn, known_ok); + __ delayed()->nop(); + } else { + __ cmp(tmp, tmp2); + __ br(Assembler::equal, false, Assembler::pn, known_ok); + __ delayed()->cmp(src, dst); + __ br(Assembler::equal, false, Assembler::pn, known_ok); + __ delayed()->nop(); + } + __ bind(halt); + __ stop("incorrect type information in arraycopy"); + __ bind(known_ok); + } +#endif + + int shift = shift_amount(basic_type); + + Register src_ptr = O0; + Register dst_ptr = O1; + Register len = O2; + + __ add(src, arrayOopDesc::base_offset_in_bytes(basic_type), src_ptr); + if (shift == 0) { + __ add(src_ptr, src_pos, src_ptr); + } else { + __ sll(src_pos, shift, tmp); + __ add(src_ptr, tmp, src_ptr); + } + + __ add(dst, arrayOopDesc::base_offset_in_bytes(basic_type), dst_ptr); + if (shift == 0) { + __ add(dst_ptr, dst_pos, dst_ptr); + } else { + __ sll(dst_pos, shift, tmp); + __ add(dst_ptr, tmp, dst_ptr); + } + + if (basic_type != T_OBJECT) { + if (shift == 0) { + __ mov(length, len); + } else { + __ sll(length, shift, len); + } + __ call_VM_leaf(tmp, CAST_FROM_FN_PTR(address, Runtime1::primitive_arraycopy)); + } else { + // oop_arraycopy takes a length in number of elements, so don't scale it. + __ mov(length, len); + __ call_VM_leaf(tmp, CAST_FROM_FN_PTR(address, Runtime1::oop_arraycopy)); + } + + __ bind(*stub->continuation()); +} + + +void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, LIR_Opr count, LIR_Opr dest, LIR_Opr tmp) { + if (dest->is_single_cpu()) { +#ifdef _LP64 + if (left->type() == T_OBJECT) { + switch (code) { + case lir_shl: __ sllx (left->as_register(), count->as_register(), dest->as_register()); break; + case lir_shr: __ srax (left->as_register(), count->as_register(), dest->as_register()); break; + case lir_ushr: __ srl (left->as_register(), count->as_register(), dest->as_register()); break; + default: ShouldNotReachHere(); + } + } else +#endif + switch (code) { + case lir_shl: __ sll (left->as_register(), count->as_register(), dest->as_register()); break; + case lir_shr: __ sra (left->as_register(), count->as_register(), dest->as_register()); break; + case lir_ushr: __ srl (left->as_register(), count->as_register(), dest->as_register()); break; + default: ShouldNotReachHere(); + } + } else { +#ifdef _LP64 + switch (code) { + case lir_shl: __ sllx (left->as_register_lo(), count->as_register(), dest->as_register_lo()); break; + case lir_shr: __ srax (left->as_register_lo(), count->as_register(), dest->as_register_lo()); break; + case lir_ushr: __ srlx (left->as_register_lo(), count->as_register(), dest->as_register_lo()); break; + default: ShouldNotReachHere(); + } +#else + switch (code) { + case lir_shl: __ lshl (left->as_register_hi(), left->as_register_lo(), count->as_register(), dest->as_register_hi(), dest->as_register_lo(), G3_scratch); break; + case lir_shr: __ lshr (left->as_register_hi(), left->as_register_lo(), count->as_register(), dest->as_register_hi(), dest->as_register_lo(), G3_scratch); break; + case lir_ushr: __ lushr (left->as_register_hi(), left->as_register_lo(), count->as_register(), dest->as_register_hi(), dest->as_register_lo(), G3_scratch); break; + default: ShouldNotReachHere(); + } +#endif + } +} + + +void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr dest) { +#ifdef _LP64 + if (left->type() == T_OBJECT) { + count = count & 63; // shouldn't shift by more than sizeof(intptr_t) + Register l = left->as_register(); + Register d = dest->as_register_lo(); + switch (code) { + case lir_shl: __ sllx (l, count, d); break; + case lir_shr: __ srax (l, count, d); break; + case lir_ushr: __ srlx (l, count, d); break; + default: ShouldNotReachHere(); + } + return; + } +#endif + + if (dest->is_single_cpu()) { + count = count & 0x1F; // Java spec + switch (code) { + case lir_shl: __ sll (left->as_register(), count, dest->as_register()); break; + case lir_shr: __ sra (left->as_register(), count, dest->as_register()); break; + case lir_ushr: __ srl (left->as_register(), count, dest->as_register()); break; + default: ShouldNotReachHere(); + } + } else if (dest->is_double_cpu()) { + count = count & 63; // Java spec + switch (code) { + case lir_shl: __ sllx (left->as_pointer_register(), count, dest->as_pointer_register()); break; + case lir_shr: __ srax (left->as_pointer_register(), count, dest->as_pointer_register()); break; + case lir_ushr: __ srlx (left->as_pointer_register(), count, dest->as_pointer_register()); break; + default: ShouldNotReachHere(); + } + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { + assert(op->tmp1()->as_register() == G1 && + op->tmp2()->as_register() == G3 && + op->tmp3()->as_register() == G4 && + op->obj()->as_register() == O0 && + op->klass()->as_register() == G5, "must be"); + if (op->init_check()) { + __ ld(op->klass()->as_register(), + instanceKlass::init_state_offset_in_bytes() + sizeof(oopDesc), + op->tmp1()->as_register()); + add_debug_info_for_null_check_here(op->stub()->info()); + __ cmp(op->tmp1()->as_register(), instanceKlass::fully_initialized); + __ br(Assembler::notEqual, false, Assembler::pn, *op->stub()->entry()); + __ delayed()->nop(); + } + __ allocate_object(op->obj()->as_register(), + op->tmp1()->as_register(), + op->tmp2()->as_register(), + op->tmp3()->as_register(), + op->header_size(), + op->object_size(), + op->klass()->as_register(), + *op->stub()->entry()); + __ bind(*op->stub()->continuation()); + __ verify_oop(op->obj()->as_register()); +} + + +void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) { + assert(op->tmp1()->as_register() == G1 && + op->tmp2()->as_register() == G3 && + op->tmp3()->as_register() == G4 && + op->tmp4()->as_register() == O1 && + op->klass()->as_register() == G5, "must be"); + if (UseSlowPath || + (!UseFastNewObjectArray && (op->type() == T_OBJECT || op->type() == T_ARRAY)) || + (!UseFastNewTypeArray && (op->type() != T_OBJECT && op->type() != T_ARRAY))) { + __ br(Assembler::always, false, Assembler::pn, *op->stub()->entry()); + __ delayed()->nop(); + } else { + __ allocate_array(op->obj()->as_register(), + op->len()->as_register(), + op->tmp1()->as_register(), + op->tmp2()->as_register(), + op->tmp3()->as_register(), + arrayOopDesc::header_size(op->type()), + type2aelembytes[op->type()], + op->klass()->as_register(), + *op->stub()->entry()); + } + __ bind(*op->stub()->continuation()); +} + + +void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { + LIR_Code code = op->code(); + if (code == lir_store_check) { + Register value = op->object()->as_register(); + Register array = op->array()->as_register(); + Register k_RInfo = op->tmp1()->as_register(); + Register klass_RInfo = op->tmp2()->as_register(); + Register Rtmp1 = op->tmp3()->as_register(); + + __ verify_oop(value); + + CodeStub* stub = op->stub(); + Label done; + __ cmp(value, 0); + __ br(Assembler::equal, false, Assembler::pn, done); + __ delayed()->nop(); + load(array, oopDesc::klass_offset_in_bytes(), k_RInfo, T_OBJECT, op->info_for_exception()); + load(value, oopDesc::klass_offset_in_bytes(), klass_RInfo, T_OBJECT, NULL); + + // get instance klass + load(k_RInfo, objArrayKlass::element_klass_offset_in_bytes() + sizeof(oopDesc), k_RInfo, T_OBJECT, NULL); + // get super_check_offset + load(k_RInfo, sizeof(oopDesc) + Klass::super_check_offset_offset_in_bytes(), Rtmp1, T_INT, NULL); + // See if we get an immediate positive hit + __ ld_ptr(klass_RInfo, Rtmp1, FrameMap::O7_oop_opr->as_register()); + __ cmp(k_RInfo, O7); + __ br(Assembler::equal, false, Assembler::pn, done); + __ delayed()->nop(); + // check for immediate negative hit + __ cmp(Rtmp1, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes()); + __ br(Assembler::notEqual, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + // check for self + __ cmp(klass_RInfo, k_RInfo); + __ br(Assembler::equal, false, Assembler::pn, done); + __ delayed()->nop(); + + // assert(sub.is_same(FrameMap::G3_RInfo) && super.is_same(FrameMap::G1_RInfo), "incorrect call setup"); + __ call(Runtime1::entry_for(Runtime1::slow_subtype_check_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + __ cmp(G3, 0); + __ br(Assembler::equal, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + __ bind(done); + } else if (op->code() == lir_checkcast) { + // we always need a stub for the failure case. + CodeStub* stub = op->stub(); + Register obj = op->object()->as_register(); + Register k_RInfo = op->tmp1()->as_register(); + Register klass_RInfo = op->tmp2()->as_register(); + Register dst = op->result_opr()->as_register(); + Register Rtmp1 = op->tmp3()->as_register(); + ciKlass* k = op->klass(); + + if (obj == k_RInfo) { + k_RInfo = klass_RInfo; + klass_RInfo = obj; + } + if (op->profiled_method() != NULL) { + ciMethod* method = op->profiled_method(); + int bci = op->profiled_bci(); + + // We need two temporaries to perform this operation on SPARC, + // so to keep things simple we perform a redundant test here + Label profile_done; + __ cmp(obj, 0); + __ br(Assembler::notEqual, false, Assembler::pn, profile_done); + __ delayed()->nop(); + // Object is null; update methodDataOop + ciMethodData* md = method->method_data(); + if (md == NULL) { + bailout("out of memory building methodDataOop"); + return; + } + ciProfileData* data = md->bci_to_data(bci); + assert(data != NULL, "need data for checkcast"); + assert(data->is_BitData(), "need BitData for checkcast"); + Register mdo = k_RInfo; + Register data_val = Rtmp1; + jobject2reg(md->encoding(), mdo); + + int mdo_offset_bias = 0; + if (!Assembler::is_simm13(md->byte_offset_of_slot(data, DataLayout::header_offset()) + data->size_in_bytes())) { + // The offset is large so bias the mdo by the base of the slot so + // that the ld can use simm13s to reference the slots of the data + mdo_offset_bias = md->byte_offset_of_slot(data, DataLayout::header_offset()); + __ set(mdo_offset_bias, data_val); + __ add(mdo, data_val, mdo); + } + + + Address flags_addr(mdo, 0, md->byte_offset_of_slot(data, DataLayout::flags_offset()) - mdo_offset_bias); + __ ldub(flags_addr, data_val); + __ or3(data_val, BitData::null_seen_byte_constant(), data_val); + __ stb(data_val, flags_addr); + __ bind(profile_done); + } + + Label done; + // patching may screw with our temporaries on sparc, + // so let's do it before loading the class + if (k->is_loaded()) { + jobject2reg(k->encoding(), k_RInfo); + } else { + jobject2reg_with_patching(k_RInfo, op->info_for_patch()); + } + assert(obj != k_RInfo, "must be different"); + __ cmp(obj, 0); + __ br(Assembler::equal, false, Assembler::pn, done); + __ delayed()->nop(); + + // get object class + // not a safepoint as obj null check happens earlier + load(obj, oopDesc::klass_offset_in_bytes(), klass_RInfo, T_OBJECT, NULL); + if (op->fast_check()) { + assert_different_registers(klass_RInfo, k_RInfo); + __ cmp(k_RInfo, klass_RInfo); + __ br(Assembler::notEqual, false, Assembler::pt, *stub->entry()); + __ delayed()->nop(); + __ bind(done); + } else { + if (k->is_loaded()) { + load(klass_RInfo, k->super_check_offset(), Rtmp1, T_OBJECT, NULL); + + if (sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() != k->super_check_offset()) { + // See if we get an immediate positive hit + __ cmp(Rtmp1, k_RInfo ); + __ br(Assembler::notEqual, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } else { + // See if we get an immediate positive hit + assert_different_registers(Rtmp1, k_RInfo, klass_RInfo); + __ cmp(Rtmp1, k_RInfo ); + __ br(Assembler::equal, false, Assembler::pn, done); + // check for self + __ delayed()->cmp(klass_RInfo, k_RInfo); + __ br(Assembler::equal, false, Assembler::pn, done); + __ delayed()->nop(); + + // assert(sub.is_same(FrameMap::G3_RInfo) && super.is_same(FrameMap::G1_RInfo), "incorrect call setup"); + __ call(Runtime1::entry_for(Runtime1::slow_subtype_check_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + __ cmp(G3, 0); + __ br(Assembler::equal, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } + __ bind(done); + } else { + assert_different_registers(Rtmp1, klass_RInfo, k_RInfo); + + load(k_RInfo, sizeof(oopDesc) + Klass::super_check_offset_offset_in_bytes(), Rtmp1, T_INT, NULL); + // See if we get an immediate positive hit + load(klass_RInfo, Rtmp1, FrameMap::O7_oop_opr, T_OBJECT); + __ cmp(k_RInfo, O7); + __ br(Assembler::equal, false, Assembler::pn, done); + __ delayed()->nop(); + // check for immediate negative hit + __ cmp(Rtmp1, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes()); + __ br(Assembler::notEqual, false, Assembler::pn, *stub->entry()); + // check for self + __ delayed()->cmp(klass_RInfo, k_RInfo); + __ br(Assembler::equal, false, Assembler::pn, done); + __ delayed()->nop(); + + // assert(sub.is_same(FrameMap::G3_RInfo) && super.is_same(FrameMap::G1_RInfo), "incorrect call setup"); + __ call(Runtime1::entry_for(Runtime1::slow_subtype_check_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + __ cmp(G3, 0); + __ br(Assembler::equal, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + __ bind(done); + } + + } + __ mov(obj, dst); + } else if (code == lir_instanceof) { + Register obj = op->object()->as_register(); + Register k_RInfo = op->tmp1()->as_register(); + Register klass_RInfo = op->tmp2()->as_register(); + Register dst = op->result_opr()->as_register(); + Register Rtmp1 = op->tmp3()->as_register(); + ciKlass* k = op->klass(); + + Label done; + if (obj == k_RInfo) { + k_RInfo = klass_RInfo; + klass_RInfo = obj; + } + // patching may screw with our temporaries on sparc, + // so let's do it before loading the class + if (k->is_loaded()) { + jobject2reg(k->encoding(), k_RInfo); + } else { + jobject2reg_with_patching(k_RInfo, op->info_for_patch()); + } + assert(obj != k_RInfo, "must be different"); + __ cmp(obj, 0); + __ br(Assembler::equal, true, Assembler::pn, done); + __ delayed()->set(0, dst); + + // get object class + // not a safepoint as obj null check happens earlier + load(obj, oopDesc::klass_offset_in_bytes(), klass_RInfo, T_OBJECT, NULL); + if (op->fast_check()) { + __ cmp(k_RInfo, klass_RInfo); + __ br(Assembler::equal, true, Assembler::pt, done); + __ delayed()->set(1, dst); + __ set(0, dst); + __ bind(done); + } else { + if (k->is_loaded()) { + assert_different_registers(Rtmp1, klass_RInfo, k_RInfo); + load(klass_RInfo, k->super_check_offset(), Rtmp1, T_OBJECT, NULL); + + if (sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() != k->super_check_offset()) { + // See if we get an immediate positive hit + __ cmp(Rtmp1, k_RInfo ); + __ br(Assembler::equal, true, Assembler::pt, done); + __ delayed()->set(1, dst); + __ set(0, dst); + __ bind(done); + } else { + // See if we get an immediate positive hit + assert_different_registers(Rtmp1, k_RInfo, klass_RInfo); + __ cmp(Rtmp1, k_RInfo ); + __ br(Assembler::equal, true, Assembler::pt, done); + __ delayed()->set(1, dst); + // check for self + __ cmp(klass_RInfo, k_RInfo); + __ br(Assembler::equal, true, Assembler::pt, done); + __ delayed()->set(1, dst); + + // assert(sub.is_same(FrameMap::G3_RInfo) && super.is_same(FrameMap::G1_RInfo), "incorrect call setup"); + __ call(Runtime1::entry_for(Runtime1::slow_subtype_check_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + __ mov(G3, dst); + __ bind(done); + } + } else { + assert(dst != klass_RInfo && dst != k_RInfo, "need 3 registers"); + + load(k_RInfo, sizeof(oopDesc) + Klass::super_check_offset_offset_in_bytes(), dst, T_INT, NULL); + // See if we get an immediate positive hit + load(klass_RInfo, dst, FrameMap::O7_oop_opr, T_OBJECT); + __ cmp(k_RInfo, O7); + __ br(Assembler::equal, true, Assembler::pt, done); + __ delayed()->set(1, dst); + // check for immediate negative hit + __ cmp(dst, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes()); + __ br(Assembler::notEqual, true, Assembler::pt, done); + __ delayed()->set(0, dst); + // check for self + __ cmp(klass_RInfo, k_RInfo); + __ br(Assembler::equal, true, Assembler::pt, done); + __ delayed()->set(1, dst); + + // assert(sub.is_same(FrameMap::G3_RInfo) && super.is_same(FrameMap::G1_RInfo), "incorrect call setup"); + __ call(Runtime1::entry_for(Runtime1::slow_subtype_check_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + __ mov(G3, dst); + __ bind(done); + } + } + } else { + ShouldNotReachHere(); + } + +} + + +void LIR_Assembler::emit_compare_and_swap(LIR_OpCompareAndSwap* op) { + if (op->code() == lir_cas_long) { + assert(VM_Version::supports_cx8(), "wrong machine"); + Register addr = op->addr()->as_pointer_register(); + Register cmp_value_lo = op->cmp_value()->as_register_lo(); + Register cmp_value_hi = op->cmp_value()->as_register_hi(); + Register new_value_lo = op->new_value()->as_register_lo(); + Register new_value_hi = op->new_value()->as_register_hi(); + Register t1 = op->tmp1()->as_register(); + Register t2 = op->tmp2()->as_register(); +#ifdef _LP64 + __ mov(cmp_value_lo, t1); + __ mov(new_value_lo, t2); +#else + // move high and low halves of long values into single registers + __ sllx(cmp_value_hi, 32, t1); // shift high half into temp reg + __ srl(cmp_value_lo, 0, cmp_value_lo); // clear upper 32 bits of low half + __ or3(t1, cmp_value_lo, t1); // t1 holds 64-bit compare value + __ sllx(new_value_hi, 32, t2); + __ srl(new_value_lo, 0, new_value_lo); + __ or3(t2, new_value_lo, t2); // t2 holds 64-bit value to swap +#endif + // perform the compare and swap operation + __ casx(addr, t1, t2); + // generate condition code - if the swap succeeded, t2 ("new value" reg) was + // overwritten with the original value in "addr" and will be equal to t1. + __ cmp(t1, t2); + + } else if (op->code() == lir_cas_int || op->code() == lir_cas_obj) { + Register addr = op->addr()->as_pointer_register(); + Register cmp_value = op->cmp_value()->as_register(); + Register new_value = op->new_value()->as_register(); + Register t1 = op->tmp1()->as_register(); + Register t2 = op->tmp2()->as_register(); + __ mov(cmp_value, t1); + __ mov(new_value, t2); +#ifdef _LP64 + if (op->code() == lir_cas_obj) { + __ casx(addr, t1, t2); + } else +#endif + { + __ cas(addr, t1, t2); + } + __ cmp(t1, t2); + } else { + Unimplemented(); + } +} + +void LIR_Assembler::set_24bit_FPU() { + Unimplemented(); +} + + +void LIR_Assembler::reset_FPU() { + Unimplemented(); +} + + +void LIR_Assembler::breakpoint() { + __ breakpoint_trap(); +} + + +void LIR_Assembler::push(LIR_Opr opr) { + Unimplemented(); +} + + +void LIR_Assembler::pop(LIR_Opr opr) { + Unimplemented(); +} + + +void LIR_Assembler::monitor_address(int monitor_no, LIR_Opr dst_opr) { + Address mon_addr = frame_map()->address_for_monitor_lock(monitor_no); + Register dst = dst_opr->as_register(); + Register reg = mon_addr.base(); + int offset = mon_addr.disp(); + // compute pointer to BasicLock + if (mon_addr.is_simm13()) { + __ add(reg, offset, dst); + } else { + __ set(offset, dst); + __ add(dst, reg, dst); + } +} + + +void LIR_Assembler::emit_lock(LIR_OpLock* op) { + Register obj = op->obj_opr()->as_register(); + Register hdr = op->hdr_opr()->as_register(); + Register lock = op->lock_opr()->as_register(); + + // obj may not be an oop + if (op->code() == lir_lock) { + MonitorEnterStub* stub = (MonitorEnterStub*)op->stub(); + if (UseFastLocking) { + assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); + // add debug info for NullPointerException only if one is possible + if (op->info() != NULL) { + add_debug_info_for_null_check_here(op->info()); + } + __ lock_object(hdr, obj, lock, op->scratch_opr()->as_register(), *op->stub()->entry()); + } else { + // always do slow locking + // note: the slow locking code could be inlined here, however if we use + // slow locking, speed doesn't matter anyway and this solution is + // simpler and requires less duplicated code - additionally, the + // slow locking code is the same in either case which simplifies + // debugging + __ br(Assembler::always, false, Assembler::pt, *op->stub()->entry()); + __ delayed()->nop(); + } + } else { + assert (op->code() == lir_unlock, "Invalid code, expected lir_unlock"); + if (UseFastLocking) { + assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); + __ unlock_object(hdr, obj, lock, *op->stub()->entry()); + } else { + // always do slow unlocking + // note: the slow unlocking code could be inlined here, however if we use + // slow unlocking, speed doesn't matter anyway and this solution is + // simpler and requires less duplicated code - additionally, the + // slow unlocking code is the same in either case which simplifies + // debugging + __ br(Assembler::always, false, Assembler::pt, *op->stub()->entry()); + __ delayed()->nop(); + } + } + __ bind(*op->stub()->continuation()); +} + + +void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { + ciMethod* method = op->profiled_method(); + int bci = op->profiled_bci(); + + // Update counter for all call types + ciMethodData* md = method->method_data(); + if (md == NULL) { + bailout("out of memory building methodDataOop"); + return; + } + ciProfileData* data = md->bci_to_data(bci); + assert(data->is_CounterData(), "need CounterData for calls"); + assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); + assert(op->tmp1()->is_single_cpu(), "tmp1 must be allocated"); + Register mdo = op->mdo()->as_register(); + Register tmp1 = op->tmp1()->as_register(); + jobject2reg(md->encoding(), mdo); + int mdo_offset_bias = 0; + if (!Assembler::is_simm13(md->byte_offset_of_slot(data, CounterData::count_offset()) + + data->size_in_bytes())) { + // The offset is large so bias the mdo by the base of the slot so + // that the ld can use simm13s to reference the slots of the data + mdo_offset_bias = md->byte_offset_of_slot(data, CounterData::count_offset()); + __ set(mdo_offset_bias, O7); + __ add(mdo, O7, mdo); + } + + Address counter_addr(mdo, 0, md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias); + __ lduw(counter_addr, tmp1); + __ add(tmp1, DataLayout::counter_increment, tmp1); + __ stw(tmp1, counter_addr); + Bytecodes::Code bc = method->java_code_at_bci(bci); + // Perform additional virtual call profiling for invokevirtual and + // invokeinterface bytecodes + if ((bc == Bytecodes::_invokevirtual || bc == Bytecodes::_invokeinterface) && + Tier1ProfileVirtualCalls) { + assert(op->recv()->is_single_cpu(), "recv must be allocated"); + Register recv = op->recv()->as_register(); + assert_different_registers(mdo, tmp1, recv); + assert(data->is_VirtualCallData(), "need VirtualCallData for virtual calls"); + ciKlass* known_klass = op->known_holder(); + if (Tier1OptimizeVirtualCallProfiling && known_klass != NULL) { + // We know the type that will be seen at this call site; we can + // statically update the methodDataOop rather than needing to do + // dynamic tests on the receiver type + + // NOTE: we should probably put a lock around this search to + // avoid collisions by concurrent compilations + ciVirtualCallData* vc_data = (ciVirtualCallData*) data; + uint i; + for (i = 0; i < VirtualCallData::row_limit(); i++) { + ciKlass* receiver = vc_data->receiver(i); + if (known_klass->equals(receiver)) { + Address data_addr(mdo, 0, md->byte_offset_of_slot(data, + VirtualCallData::receiver_count_offset(i)) - + mdo_offset_bias); + __ lduw(data_addr, tmp1); + __ add(tmp1, DataLayout::counter_increment, tmp1); + __ stw(tmp1, data_addr); + return; + } + } + + // Receiver type not found in profile data; select an empty slot + + // Note that this is less efficient than it should be because it + // always does a write to the receiver part of the + // VirtualCallData rather than just the first time + for (i = 0; i < VirtualCallData::row_limit(); i++) { + ciKlass* receiver = vc_data->receiver(i); + if (receiver == NULL) { + Address recv_addr(mdo, 0, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)) - + mdo_offset_bias); + jobject2reg(known_klass->encoding(), tmp1); + __ st_ptr(tmp1, recv_addr); + Address data_addr(mdo, 0, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - + mdo_offset_bias); + __ lduw(data_addr, tmp1); + __ add(tmp1, DataLayout::counter_increment, tmp1); + __ stw(tmp1, data_addr); + return; + } + } + } else { + load(Address(recv, 0, oopDesc::klass_offset_in_bytes()), recv, T_OBJECT); + Label update_done; + uint i; + for (i = 0; i < VirtualCallData::row_limit(); i++) { + Label next_test; + // See if the receiver is receiver[n]. + Address receiver_addr(mdo, 0, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)) - + mdo_offset_bias); + __ ld_ptr(receiver_addr, tmp1); + __ verify_oop(tmp1); + __ cmp(recv, tmp1); + __ brx(Assembler::notEqual, false, Assembler::pt, next_test); + __ delayed()->nop(); + Address data_addr(mdo, 0, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - + mdo_offset_bias); + __ lduw(data_addr, tmp1); + __ add(tmp1, DataLayout::counter_increment, tmp1); + __ stw(tmp1, data_addr); + __ br(Assembler::always, false, Assembler::pt, update_done); + __ delayed()->nop(); + __ bind(next_test); + } + + // Didn't find receiver; find next empty slot and fill it in + for (i = 0; i < VirtualCallData::row_limit(); i++) { + Label next_test; + Address recv_addr(mdo, 0, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)) - + mdo_offset_bias); + load(recv_addr, tmp1, T_OBJECT); + __ tst(tmp1); + __ brx(Assembler::notEqual, false, Assembler::pt, next_test); + __ delayed()->nop(); + __ st_ptr(recv, recv_addr); + __ set(DataLayout::counter_increment, tmp1); + __ st_ptr(tmp1, Address(mdo, 0, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - + mdo_offset_bias)); + if (i < (VirtualCallData::row_limit() - 1)) { + __ br(Assembler::always, false, Assembler::pt, update_done); + __ delayed()->nop(); + } + __ bind(next_test); + } + + __ bind(update_done); + } + } +} + + +void LIR_Assembler::align_backward_branch_target() { + __ align(16); +} + + +void LIR_Assembler::emit_delay(LIR_OpDelay* op) { + // make sure we are expecting a delay + // this has the side effect of clearing the delay state + // so we can use _masm instead of _masm->delayed() to do the + // code generation. + __ delayed(); + + // make sure we only emit one instruction + int offset = code_offset(); + op->delay_op()->emit_code(this); +#ifdef ASSERT + if (code_offset() - offset != NativeInstruction::nop_instruction_size) { + op->delay_op()->print(); + } + assert(code_offset() - offset == NativeInstruction::nop_instruction_size, + "only one instruction can go in a delay slot"); +#endif + + // we may also be emitting the call info for the instruction + // which we are the delay slot of. + CodeEmitInfo * call_info = op->call_info(); + if (call_info) { + add_call_info(code_offset(), call_info); + } + + if (VerifyStackAtCalls) { + _masm->sub(FP, SP, O7); + _masm->cmp(O7, initial_frame_size_in_bytes()); + _masm->trap(Assembler::notEqual, Assembler::ptr_cc, G0, ST_RESERVED_FOR_USER_0+2 ); + } +} + + +void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) { + assert(left->is_register(), "can only handle registers"); + + if (left->is_single_cpu()) { + __ neg(left->as_register(), dest->as_register()); + } else if (left->is_single_fpu()) { + __ fneg(FloatRegisterImpl::S, left->as_float_reg(), dest->as_float_reg()); + } else if (left->is_double_fpu()) { + __ fneg(FloatRegisterImpl::D, left->as_double_reg(), dest->as_double_reg()); + } else { + assert (left->is_double_cpu(), "Must be a long"); + Register Rlow = left->as_register_lo(); + Register Rhi = left->as_register_hi(); +#ifdef _LP64 + __ sub(G0, Rlow, dest->as_register_lo()); +#else + __ subcc(G0, Rlow, dest->as_register_lo()); + __ subc (G0, Rhi, dest->as_register_hi()); +#endif + } +} + + +void LIR_Assembler::fxch(int i) { + Unimplemented(); +} + +void LIR_Assembler::fld(int i) { + Unimplemented(); +} + +void LIR_Assembler::ffree(int i) { + Unimplemented(); +} + +void LIR_Assembler::rt_call(LIR_Opr result, address dest, + const LIR_OprList* args, LIR_Opr tmp, CodeEmitInfo* info) { + + // if tmp is invalid, then the function being called doesn't destroy the thread + if (tmp->is_valid()) { + __ save_thread(tmp->as_register()); + } + __ call(dest, relocInfo::runtime_call_type); + __ delayed()->nop(); + if (info != NULL) { + add_call_info_here(info); + } + if (tmp->is_valid()) { + __ restore_thread(tmp->as_register()); + } + +#ifdef ASSERT + __ verify_thread(); +#endif // ASSERT +} + + +void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info) { +#ifdef _LP64 + ShouldNotReachHere(); +#endif + + NEEDS_CLEANUP; + if (type == T_LONG) { + LIR_Address* mem_addr = dest->is_address() ? dest->as_address_ptr() : src->as_address_ptr(); + + // (extended to allow indexed as well as constant displaced for JSR-166) + Register idx = noreg; // contains either constant offset or index + + int disp = mem_addr->disp(); + if (mem_addr->index() == LIR_OprFact::illegalOpr) { + if (!Assembler::is_simm13(disp)) { + idx = O7; + __ set(disp, idx); + } + } else { + assert(disp == 0, "not both indexed and disp"); + idx = mem_addr->index()->as_register(); + } + + int null_check_offset = -1; + + Register base = mem_addr->base()->as_register(); + if (src->is_register() && dest->is_address()) { + // G4 is high half, G5 is low half + if (VM_Version::v9_instructions_work()) { + // clear the top bits of G5, and scale up G4 + __ srl (src->as_register_lo(), 0, G5); + __ sllx(src->as_register_hi(), 32, G4); + // combine the two halves into the 64 bits of G4 + __ or3(G4, G5, G4); + null_check_offset = __ offset(); + if (idx == noreg) { + __ stx(G4, base, disp); + } else { + __ stx(G4, base, idx); + } + } else { + __ mov (src->as_register_hi(), G4); + __ mov (src->as_register_lo(), G5); + null_check_offset = __ offset(); + if (idx == noreg) { + __ std(G4, base, disp); + } else { + __ std(G4, base, idx); + } + } + } else if (src->is_address() && dest->is_register()) { + null_check_offset = __ offset(); + if (VM_Version::v9_instructions_work()) { + if (idx == noreg) { + __ ldx(base, disp, G5); + } else { + __ ldx(base, idx, G5); + } + __ srax(G5, 32, dest->as_register_hi()); // fetch the high half into hi + __ mov (G5, dest->as_register_lo()); // copy low half into lo + } else { + if (idx == noreg) { + __ ldd(base, disp, G4); + } else { + __ ldd(base, idx, G4); + } + // G4 is high half, G5 is low half + __ mov (G4, dest->as_register_hi()); + __ mov (G5, dest->as_register_lo()); + } + } else { + Unimplemented(); + } + if (info != NULL) { + add_debug_info_for_null_check(null_check_offset, info); + } + + } else { + // use normal move for all other volatiles since they don't need + // special handling to remain atomic. + move_op(src, dest, type, lir_patch_none, info, false, false); + } +} + +void LIR_Assembler::membar() { + // only StoreLoad membars are ever explicitly needed on sparcs in TSO mode + __ membar( Assembler::Membar_mask_bits(Assembler::StoreLoad) ); +} + +void LIR_Assembler::membar_acquire() { + // no-op on TSO +} + +void LIR_Assembler::membar_release() { + // no-op on TSO +} + +// Macro to Pack two sequential registers containing 32 bit values +// into a single 64 bit register. +// rs and rs->successor() are packed into rd +// rd and rs may be the same register. +// Note: rs and rs->successor() are destroyed. +void LIR_Assembler::pack64( Register rs, Register rd ) { + __ sllx(rs, 32, rs); + __ srl(rs->successor(), 0, rs->successor()); + __ or3(rs, rs->successor(), rd); +} + +// Macro to unpack a 64 bit value in a register into +// two sequential registers. +// rd is unpacked into rd and rd->successor() +void LIR_Assembler::unpack64( Register rd ) { + __ mov(rd, rd->successor()); + __ srax(rd, 32, rd); + __ sra(rd->successor(), 0, rd->successor()); +} + + +void LIR_Assembler::leal(LIR_Opr addr_opr, LIR_Opr dest) { + LIR_Address* addr = addr_opr->as_address_ptr(); + assert(addr->index()->is_illegal() && addr->scale() == LIR_Address::times_1 && Assembler::is_simm13(addr->disp()), "can't handle complex addresses yet"); + __ add(addr->base()->as_register(), addr->disp(), dest->as_register()); +} + + +void LIR_Assembler::get_thread(LIR_Opr result_reg) { + assert(result_reg->is_register(), "check"); + __ mov(G2_thread, result_reg->as_register()); +} + + +void LIR_Assembler::peephole(LIR_List* lir) { + LIR_OpList* inst = lir->instructions_list(); + for (int i = 0; i < inst->length(); i++) { + LIR_Op* op = inst->at(i); + switch (op->code()) { + case lir_cond_float_branch: + case lir_branch: { + LIR_OpBranch* branch = op->as_OpBranch(); + assert(branch->info() == NULL, "shouldn't be state on branches anymore"); + LIR_Op* delay_op = NULL; + // we'd like to be able to pull following instructions into + // this slot but we don't know enough to do it safely yet so + // only optimize block to block control flow. + if (LIRFillDelaySlots && branch->block()) { + LIR_Op* prev = inst->at(i - 1); + if (prev && LIR_Assembler::is_single_instruction(prev) && prev->info() == NULL) { + // swap previous instruction into delay slot + inst->at_put(i - 1, op); + inst->at_put(i, new LIR_OpDelay(prev, op->info())); +#ifndef PRODUCT + if (LIRTracePeephole) { + tty->print_cr("delayed"); + inst->at(i - 1)->print(); + inst->at(i)->print(); + } +#endif + continue; + } + } + + if (!delay_op) { + delay_op = new LIR_OpDelay(new LIR_Op0(lir_nop), NULL); + } + inst->insert_before(i + 1, delay_op); + break; + } + case lir_static_call: + case lir_virtual_call: + case lir_icvirtual_call: + case lir_optvirtual_call: { + LIR_Op* delay_op = NULL; + LIR_Op* prev = inst->at(i - 1); + if (LIRFillDelaySlots && prev && prev->code() == lir_move && prev->info() == NULL && + (op->code() != lir_virtual_call || + !prev->result_opr()->is_single_cpu() || + prev->result_opr()->as_register() != O0) && + LIR_Assembler::is_single_instruction(prev)) { + // Only moves without info can be put into the delay slot. + // Also don't allow the setup of the receiver in the delay + // slot for vtable calls. + inst->at_put(i - 1, op); + inst->at_put(i, new LIR_OpDelay(prev, op->info())); +#ifndef PRODUCT + if (LIRTracePeephole) { + tty->print_cr("delayed"); + inst->at(i - 1)->print(); + inst->at(i)->print(); + } +#endif + continue; + } + + if (!delay_op) { + delay_op = new LIR_OpDelay(new LIR_Op0(lir_nop), op->as_OpJavaCall()->info()); + inst->insert_before(i + 1, delay_op); + } + break; + } + } + } +} + + + + +#undef __ diff --git a/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.hpp b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.hpp new file mode 100644 index 00000000000..c97085c3b69 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.hpp @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Sparc load/store emission + // + // The sparc ld/st instructions cannot accomodate displacements > 13 bits long. + // The following "pseudo" sparc instructions (load/store) make it easier to use the indexed addressing mode + // by allowing 32 bit displacements: + // + // When disp <= 13 bits long, a single load or store instruction is emitted with (disp + [d]). + // When disp > 13 bits long, code is emitted to set the displacement into the O7 register, + // and then a load or store is emitted with ([O7] + [d]). + // + + // some load/store variants return the code_offset for proper positioning of debug info for null checks + + // load/store with 32 bit displacement + int load(Register s, int disp, Register d, BasicType ld_type, CodeEmitInfo* info = NULL); + void store(Register value, Register base, int offset, BasicType type, CodeEmitInfo *info = NULL); + + // loadf/storef with 32 bit displacement + void load(Register s, int disp, FloatRegister d, BasicType ld_type, CodeEmitInfo* info = NULL); + void store(FloatRegister d, Register s1, int disp, BasicType st_type, CodeEmitInfo* info = NULL); + + // convienence methods for calling load/store with an Address + void load(const Address& a, Register d, BasicType ld_type, CodeEmitInfo* info = NULL, int offset = 0); + void store(Register d, const Address& a, BasicType st_type, CodeEmitInfo* info = NULL, int offset = 0); + void load(const Address& a, FloatRegister d, BasicType ld_type, CodeEmitInfo* info = NULL, int offset = 0); + void store(FloatRegister d, const Address& a, BasicType st_type, CodeEmitInfo* info = NULL, int offset = 0); + + // convienence methods for calling load/store with an LIR_Address + void load(LIR_Address* a, Register d, BasicType ld_type, CodeEmitInfo* info = NULL); + void store(Register d, LIR_Address* a, BasicType st_type, CodeEmitInfo* info = NULL); + void load(LIR_Address* a, FloatRegister d, BasicType ld_type, CodeEmitInfo* info = NULL); + void store(FloatRegister d, LIR_Address* a, BasicType st_type, CodeEmitInfo* info = NULL); + + int store(LIR_Opr from_reg, Register base, int offset, BasicType type, bool unaligned = false); + int store(LIR_Opr from_reg, Register base, Register disp, BasicType type); + + int load(Register base, int offset, LIR_Opr to_reg, BasicType type, bool unaligned = false); + int load(Register base, Register disp, LIR_Opr to_reg, BasicType type); + + void monitorexit(LIR_Opr obj_opr, LIR_Opr lock_opr, Register hdr, int monitor_no); + + int shift_amount(BasicType t); + + static bool is_single_instruction(LIR_Op* op); + + public: + void pack64( Register rs, Register rd ); + void unpack64( Register rd ); + +enum { +#ifdef _LP64 + call_stub_size = 68, +#else + call_stub_size = 20, +#endif // _LP64 + exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(10*4), + deopt_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(10*4) }; diff --git a/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp new file mode 100644 index 00000000000..443551871fe --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp @@ -0,0 +1,1176 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_LIRGenerator_sparc.cpp.incl" + +#ifdef ASSERT +#define __ gen()->lir(__FILE__, __LINE__)-> +#else +#define __ gen()->lir()-> +#endif + +void LIRItem::load_byte_item() { + // byte loads use same registers as other loads + load_item(); +} + + +void LIRItem::load_nonconstant() { + LIR_Opr r = value()->operand(); + if (_gen->can_inline_as_constant(value())) { + if (!r->is_constant()) { + r = LIR_OprFact::value_type(value()->type()); + } + _result = r; + } else { + load_item(); + } +} + + +//-------------------------------------------------------------- +// LIRGenerator +//-------------------------------------------------------------- + +LIR_Opr LIRGenerator::exceptionOopOpr() { return FrameMap::Oexception_opr; } +LIR_Opr LIRGenerator::exceptionPcOpr() { return FrameMap::Oissuing_pc_opr; } +LIR_Opr LIRGenerator::syncTempOpr() { return new_register(T_OBJECT); } +LIR_Opr LIRGenerator::getThreadTemp() { return rlock_callee_saved(T_INT); } + +LIR_Opr LIRGenerator::result_register_for(ValueType* type, bool callee) { + LIR_Opr opr; + switch (type->tag()) { + case intTag: opr = callee ? FrameMap::I0_opr : FrameMap::O0_opr; break; + case objectTag: opr = callee ? FrameMap::I0_oop_opr : FrameMap::O0_oop_opr; break; + case longTag: opr = callee ? FrameMap::in_long_opr : FrameMap::out_long_opr; break; + case floatTag: opr = FrameMap::F0_opr; break; + case doubleTag: opr = FrameMap::F0_double_opr; break; + + case addressTag: + default: ShouldNotReachHere(); return LIR_OprFact::illegalOpr; + } + + assert(opr->type_field() == as_OprType(as_BasicType(type)), "type mismatch"); + return opr; +} + +LIR_Opr LIRGenerator::rlock_callee_saved(BasicType type) { + LIR_Opr reg = new_register(type); + set_vreg_flag(reg, callee_saved); + return reg; +} + + +LIR_Opr LIRGenerator::rlock_byte(BasicType type) { + return new_register(T_INT); +} + + + + + +//--------- loading items into registers -------------------------------- + +// SPARC cannot inline all constants +bool LIRGenerator::can_store_as_constant(Value v, BasicType type) const { + if (v->type()->as_IntConstant() != NULL) { + return v->type()->as_IntConstant()->value() == 0; + } else if (v->type()->as_LongConstant() != NULL) { + return v->type()->as_LongConstant()->value() == 0L; + } else if (v->type()->as_ObjectConstant() != NULL) { + return v->type()->as_ObjectConstant()->value()->is_null_object(); + } else { + return false; + } +} + + +// only simm13 constants can be inlined +bool LIRGenerator:: can_inline_as_constant(Value i) const { + if (i->type()->as_IntConstant() != NULL) { + return Assembler::is_simm13(i->type()->as_IntConstant()->value()); + } else { + return can_store_as_constant(i, as_BasicType(i->type())); + } +} + + +bool LIRGenerator:: can_inline_as_constant(LIR_Const* c) const { + if (c->type() == T_INT) { + return Assembler::is_simm13(c->as_jint()); + } + return false; +} + + +LIR_Opr LIRGenerator::safepoint_poll_register() { + return new_register(T_INT); +} + + + +LIR_Address* LIRGenerator::generate_address(LIR_Opr base, LIR_Opr index, + int shift, int disp, BasicType type) { + assert(base->is_register(), "must be"); + + // accumulate fixed displacements + if (index->is_constant()) { + disp += index->as_constant_ptr()->as_jint() << shift; + index = LIR_OprFact::illegalOpr; + } + + if (index->is_register()) { + // apply the shift and accumulate the displacement + if (shift > 0) { + LIR_Opr tmp = new_register(T_INT); + __ shift_left(index, shift, tmp); + index = tmp; + } + if (disp != 0) { + LIR_Opr tmp = new_register(T_INT); + if (Assembler::is_simm13(disp)) { + __ add(tmp, LIR_OprFact::intConst(disp), tmp); + index = tmp; + } else { + __ move(LIR_OprFact::intConst(disp), tmp); + __ add(tmp, index, tmp); + index = tmp; + } + disp = 0; + } + } else if (disp != 0 && !Assembler::is_simm13(disp)) { + // index is illegal so replace it with the displacement loaded into a register + index = new_register(T_INT); + __ move(LIR_OprFact::intConst(disp), index); + disp = 0; + } + + // at this point we either have base + index or base + displacement + if (disp == 0) { + return new LIR_Address(base, index, type); + } else { + assert(Assembler::is_simm13(disp), "must be"); + return new LIR_Address(base, disp, type); + } +} + + +LIR_Address* LIRGenerator::emit_array_address(LIR_Opr array_opr, LIR_Opr index_opr, + BasicType type, bool needs_card_mark) { + int elem_size = type2aelembytes[type]; + int shift = exact_log2(elem_size); + + LIR_Opr base_opr; + int offset = arrayOopDesc::base_offset_in_bytes(type); + + if (index_opr->is_constant()) { + int i = index_opr->as_constant_ptr()->as_jint(); + int array_offset = i * elem_size; + if (Assembler::is_simm13(array_offset + offset)) { + base_opr = array_opr; + offset = array_offset + offset; + } else { + base_opr = new_pointer_register(); + if (Assembler::is_simm13(array_offset)) { + __ add(array_opr, LIR_OprFact::intptrConst(array_offset), base_opr); + } else { + __ move(LIR_OprFact::intptrConst(array_offset), base_opr); + __ add(base_opr, array_opr, base_opr); + } + } + } else { +#ifdef _LP64 + if (index_opr->type() == T_INT) { + LIR_Opr tmp = new_register(T_LONG); + __ convert(Bytecodes::_i2l, index_opr, tmp); + index_opr = tmp; + } +#endif + + base_opr = new_pointer_register(); + assert (index_opr->is_register(), "Must be register"); + if (shift > 0) { + __ shift_left(index_opr, shift, base_opr); + __ add(base_opr, array_opr, base_opr); + } else { + __ add(index_opr, array_opr, base_opr); + } + } + if (needs_card_mark) { + LIR_Opr ptr = new_pointer_register(); + __ add(base_opr, LIR_OprFact::intptrConst(offset), ptr); + return new LIR_Address(ptr, 0, type); + } else { + return new LIR_Address(base_opr, offset, type); + } +} + + +void LIRGenerator::increment_counter(address counter, int step) { + LIR_Opr pointer = new_pointer_register(); + __ move(LIR_OprFact::intptrConst(counter), pointer); + LIR_Address* addr = new LIR_Address(pointer, 0, T_INT); + increment_counter(addr, step); +} + +void LIRGenerator::increment_counter(LIR_Address* addr, int step) { + LIR_Opr temp = new_register(T_INT); + __ move(addr, temp); + LIR_Opr c = LIR_OprFact::intConst(step); + if (Assembler::is_simm13(step)) { + __ add(temp, c, temp); + } else { + LIR_Opr temp2 = new_register(T_INT); + __ move(c, temp2); + __ add(temp, temp2, temp); + } + __ move(temp, addr); +} + + +void LIRGenerator::cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int c, CodeEmitInfo* info) { + LIR_Opr o7opr = FrameMap::O7_opr; + __ load(new LIR_Address(base, disp, T_INT), o7opr, info); + __ cmp(condition, o7opr, c); +} + + +void LIRGenerator::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, int disp, BasicType type, CodeEmitInfo* info) { + LIR_Opr o7opr = FrameMap::O7_opr; + __ load(new LIR_Address(base, disp, type), o7opr, info); + __ cmp(condition, reg, o7opr); +} + + +void LIRGenerator::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, LIR_Opr disp, BasicType type, CodeEmitInfo* info) { + LIR_Opr o7opr = FrameMap::O7_opr; + __ load(new LIR_Address(base, disp, type), o7opr, info); + __ cmp(condition, reg, o7opr); +} + + +bool LIRGenerator::strength_reduce_multiply(LIR_Opr left, int c, LIR_Opr result, LIR_Opr tmp) { + assert(left != result, "should be different registers"); + if (is_power_of_2(c + 1)) { + __ shift_left(left, log2_intptr(c + 1), result); + __ sub(result, left, result); + return true; + } else if (is_power_of_2(c - 1)) { + __ shift_left(left, log2_intptr(c - 1), result); + __ add(result, left, result); + return true; + } + return false; +} + + +void LIRGenerator::store_stack_parameter (LIR_Opr item, ByteSize offset_from_sp) { + BasicType t = item->type(); + LIR_Opr sp_opr = FrameMap::SP_opr; + if ((t == T_LONG || t == T_DOUBLE) && + ((in_bytes(offset_from_sp) - STACK_BIAS) % 8 != 0)) { + __ unaligned_move(item, new LIR_Address(sp_opr, in_bytes(offset_from_sp), t)); + } else { + __ move(item, new LIR_Address(sp_opr, in_bytes(offset_from_sp), t)); + } +} + +//---------------------------------------------------------------------- +// visitor functions +//---------------------------------------------------------------------- + + +void LIRGenerator::do_StoreIndexed(StoreIndexed* x) { + assert(x->is_root(),""); + bool needs_range_check = true; + bool use_length = x->length() != NULL; + bool obj_store = x->elt_type() == T_ARRAY || x->elt_type() == T_OBJECT; + bool needs_store_check = obj_store && (x->value()->as_Constant() == NULL || + !get_jobject_constant(x->value())->is_null_object()); + + LIRItem array(x->array(), this); + LIRItem index(x->index(), this); + LIRItem value(x->value(), this); + LIRItem length(this); + + array.load_item(); + index.load_nonconstant(); + + if (use_length) { + needs_range_check = x->compute_needs_range_check(); + if (needs_range_check) { + length.set_instruction(x->length()); + length.load_item(); + } + } + if (needs_store_check) { + value.load_item(); + } else { + value.load_for_store(x->elt_type()); + } + + set_no_result(x); + + // the CodeEmitInfo must be duplicated for each different + // LIR-instruction because spilling can occur anywhere between two + // instructions and so the debug information must be different + CodeEmitInfo* range_check_info = state_for(x); + CodeEmitInfo* null_check_info = NULL; + if (x->needs_null_check()) { + null_check_info = new CodeEmitInfo(range_check_info); + } + + // emit array address setup early so it schedules better + LIR_Address* array_addr = emit_array_address(array.result(), index.result(), x->elt_type(), obj_store); + + if (GenerateRangeChecks && needs_range_check) { + if (use_length) { + __ cmp(lir_cond_belowEqual, length.result(), index.result()); + __ branch(lir_cond_belowEqual, T_INT, new RangeCheckStub(range_check_info, index.result())); + } else { + array_range_check(array.result(), index.result(), null_check_info, range_check_info); + // range_check also does the null check + null_check_info = NULL; + } + } + + if (GenerateArrayStoreCheck && needs_store_check) { + LIR_Opr tmp1 = FrameMap::G1_opr; + LIR_Opr tmp2 = FrameMap::G3_opr; + LIR_Opr tmp3 = FrameMap::G5_opr; + + CodeEmitInfo* store_check_info = new CodeEmitInfo(range_check_info); + __ store_check(value.result(), array.result(), tmp1, tmp2, tmp3, store_check_info); + } + + __ move(value.result(), array_addr, null_check_info); + if (obj_store) { + // Is this precise? + post_barrier(LIR_OprFact::address(array_addr), value.result()); + } +} + + +void LIRGenerator::do_MonitorEnter(MonitorEnter* x) { + assert(x->is_root(),""); + LIRItem obj(x->obj(), this); + obj.load_item(); + + set_no_result(x); + + LIR_Opr lock = FrameMap::G1_opr; + LIR_Opr scratch = FrameMap::G3_opr; + LIR_Opr hdr = FrameMap::G4_opr; + + CodeEmitInfo* info_for_exception = NULL; + if (x->needs_null_check()) { + info_for_exception = state_for(x, x->lock_stack_before()); + } + + // this CodeEmitInfo must not have the xhandlers because here the + // object is already locked (xhandlers expects object to be unlocked) + CodeEmitInfo* info = state_for(x, x->state(), true); + monitor_enter(obj.result(), lock, hdr, scratch, x->monitor_no(), info_for_exception, info); +} + + +void LIRGenerator::do_MonitorExit(MonitorExit* x) { + assert(x->is_root(),""); + LIRItem obj(x->obj(), this); + obj.dont_load_item(); + + set_no_result(x); + LIR_Opr lock = FrameMap::G1_opr; + LIR_Opr hdr = FrameMap::G3_opr; + LIR_Opr obj_temp = FrameMap::G4_opr; + monitor_exit(obj_temp, lock, hdr, x->monitor_no()); +} + + +// _ineg, _lneg, _fneg, _dneg +void LIRGenerator::do_NegateOp(NegateOp* x) { + LIRItem value(x->x(), this); + value.load_item(); + LIR_Opr reg = rlock_result(x); + __ negate(value.result(), reg); +} + + + +// for _fadd, _fmul, _fsub, _fdiv, _frem +// _dadd, _dmul, _dsub, _ddiv, _drem +void LIRGenerator::do_ArithmeticOp_FPU(ArithmeticOp* x) { + switch (x->op()) { + case Bytecodes::_fadd: + case Bytecodes::_fmul: + case Bytecodes::_fsub: + case Bytecodes::_fdiv: + case Bytecodes::_dadd: + case Bytecodes::_dmul: + case Bytecodes::_dsub: + case Bytecodes::_ddiv: { + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + left.load_item(); + right.load_item(); + rlock_result(x); + arithmetic_op_fpu(x->op(), x->operand(), left.result(), right.result(), x->is_strictfp()); + } + break; + + case Bytecodes::_frem: + case Bytecodes::_drem: { + address entry; + switch (x->op()) { + case Bytecodes::_frem: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::frem); + break; + case Bytecodes::_drem: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::drem); + break; + default: + ShouldNotReachHere(); + } + LIR_Opr result = call_runtime(x->x(), x->y(), entry, x->type(), NULL); + set_result(x, result); + } + break; + + default: ShouldNotReachHere(); + } +} + + +// for _ladd, _lmul, _lsub, _ldiv, _lrem +void LIRGenerator::do_ArithmeticOp_Long(ArithmeticOp* x) { + switch (x->op()) { + case Bytecodes::_lrem: + case Bytecodes::_lmul: + case Bytecodes::_ldiv: { + + if (x->op() == Bytecodes::_ldiv || x->op() == Bytecodes::_lrem) { + LIRItem right(x->y(), this); + right.load_item(); + + CodeEmitInfo* info = state_for(x); + LIR_Opr item = right.result(); + assert(item->is_register(), "must be"); + __ cmp(lir_cond_equal, item, LIR_OprFact::longConst(0)); + __ branch(lir_cond_equal, T_LONG, new DivByZeroStub(info)); + } + + address entry; + switch (x->op()) { + case Bytecodes::_lrem: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::lrem); + break; // check if dividend is 0 is done elsewhere + case Bytecodes::_ldiv: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::ldiv); + break; // check if dividend is 0 is done elsewhere + case Bytecodes::_lmul: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::lmul); + break; + default: + ShouldNotReachHere(); + } + + // order of arguments to runtime call is reversed. + LIR_Opr result = call_runtime(x->y(), x->x(), entry, x->type(), NULL); + set_result(x, result); + break; + } + case Bytecodes::_ladd: + case Bytecodes::_lsub: { + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + left.load_item(); + right.load_item(); + rlock_result(x); + + arithmetic_op_long(x->op(), x->operand(), left.result(), right.result(), NULL); + break; + } + default: ShouldNotReachHere(); + } +} + + +// Returns if item is an int constant that can be represented by a simm13 +static bool is_simm13(LIR_Opr item) { + if (item->is_constant() && item->type() == T_INT) { + return Assembler::is_simm13(item->as_constant_ptr()->as_jint()); + } else { + return false; + } +} + + +// for: _iadd, _imul, _isub, _idiv, _irem +void LIRGenerator::do_ArithmeticOp_Int(ArithmeticOp* x) { + bool is_div_rem = x->op() == Bytecodes::_idiv || x->op() == Bytecodes::_irem; + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + // missing test if instr is commutative and if we should swap + right.load_nonconstant(); + assert(right.is_constant() || right.is_register(), "wrong state of right"); + left.load_item(); + rlock_result(x); + if (is_div_rem) { + CodeEmitInfo* info = state_for(x); + LIR_Opr tmp = FrameMap::G1_opr; + if (x->op() == Bytecodes::_irem) { + __ irem(left.result(), right.result(), x->operand(), tmp, info); + } else if (x->op() == Bytecodes::_idiv) { + __ idiv(left.result(), right.result(), x->operand(), tmp, info); + } + } else { + arithmetic_op_int(x->op(), x->operand(), left.result(), right.result(), FrameMap::G1_opr); + } +} + + +void LIRGenerator::do_ArithmeticOp(ArithmeticOp* x) { + ValueTag tag = x->type()->tag(); + assert(x->x()->type()->tag() == tag && x->y()->type()->tag() == tag, "wrong parameters"); + switch (tag) { + case floatTag: + case doubleTag: do_ArithmeticOp_FPU(x); return; + case longTag: do_ArithmeticOp_Long(x); return; + case intTag: do_ArithmeticOp_Int(x); return; + } + ShouldNotReachHere(); +} + + +// _ishl, _lshl, _ishr, _lshr, _iushr, _lushr +void LIRGenerator::do_ShiftOp(ShiftOp* x) { + LIRItem value(x->x(), this); + LIRItem count(x->y(), this); + // Long shift destroys count register + if (value.type()->is_long()) { + count.set_destroys_register(); + } + value.load_item(); + // the old backend doesn't support this + if (count.is_constant() && count.type()->as_IntConstant() != NULL && value.type()->is_int()) { + jint c = count.get_jint_constant() & 0x1f; + assert(c >= 0 && c < 32, "should be small"); + count.dont_load_item(); + } else { + count.load_item(); + } + LIR_Opr reg = rlock_result(x); + shift_op(x->op(), reg, value.result(), count.result(), LIR_OprFact::illegalOpr); +} + + +// _iand, _land, _ior, _lor, _ixor, _lxor +void LIRGenerator::do_LogicOp(LogicOp* x) { + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + + left.load_item(); + right.load_nonconstant(); + LIR_Opr reg = rlock_result(x); + + logic_op(x->op(), reg, left.result(), right.result()); +} + + + +// _lcmp, _fcmpl, _fcmpg, _dcmpl, _dcmpg +void LIRGenerator::do_CompareOp(CompareOp* x) { + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + left.load_item(); + right.load_item(); + LIR_Opr reg = rlock_result(x); + + if (x->x()->type()->is_float_kind()) { + Bytecodes::Code code = x->op(); + __ fcmp2int(left.result(), right.result(), reg, (code == Bytecodes::_fcmpl || code == Bytecodes::_dcmpl)); + } else if (x->x()->type()->tag() == longTag) { + __ lcmp2int(left.result(), right.result(), reg); + } else { + Unimplemented(); + } +} + + +void LIRGenerator::do_AttemptUpdate(Intrinsic* x) { + assert(x->number_of_arguments() == 3, "wrong type"); + LIRItem obj (x->argument_at(0), this); // AtomicLong object + LIRItem cmp_value (x->argument_at(1), this); // value to compare with field + LIRItem new_value (x->argument_at(2), this); // replace field with new_value if it matches cmp_value + + obj.load_item(); + cmp_value.load_item(); + new_value.load_item(); + + // generate compare-and-swap and produce zero condition if swap occurs + int value_offset = sun_misc_AtomicLongCSImpl::value_offset(); + LIR_Opr addr = FrameMap::O7_opr; + __ add(obj.result(), LIR_OprFact::intConst(value_offset), addr); + LIR_Opr t1 = FrameMap::G1_opr; // temp for 64-bit value + LIR_Opr t2 = FrameMap::G3_opr; // temp for 64-bit value + __ cas_long(addr, cmp_value.result(), new_value.result(), t1, t2); + + // generate conditional move of boolean result + LIR_Opr result = rlock_result(x); + __ cmove(lir_cond_equal, LIR_OprFact::intConst(1), LIR_OprFact::intConst(0), result); +} + + +void LIRGenerator::do_CompareAndSwap(Intrinsic* x, ValueType* type) { + assert(x->number_of_arguments() == 4, "wrong type"); + LIRItem obj (x->argument_at(0), this); // object + LIRItem offset(x->argument_at(1), this); // offset of field + LIRItem cmp (x->argument_at(2), this); // value to compare with field + LIRItem val (x->argument_at(3), this); // replace field with val if matches cmp + + // Use temps to avoid kills + LIR_Opr t1 = FrameMap::G1_opr; + LIR_Opr t2 = FrameMap::G3_opr; + LIR_Opr addr = new_pointer_register(); + + // get address of field + obj.load_item(); + offset.load_item(); + cmp.load_item(); + val.load_item(); + + __ add(obj.result(), offset.result(), addr); + + if (type == objectType) + __ cas_obj(addr, cmp.result(), val.result(), t1, t2); + else if (type == intType) + __ cas_int(addr, cmp.result(), val.result(), t1, t2); + else if (type == longType) + __ cas_long(addr, cmp.result(), val.result(), t1, t2); + else { + ShouldNotReachHere(); + } + + // generate conditional move of boolean result + LIR_Opr result = rlock_result(x); + __ cmove(lir_cond_equal, LIR_OprFact::intConst(1), LIR_OprFact::intConst(0), result); + if (type == objectType) { // Write-barrier needed for Object fields. + post_barrier(obj.result(), val.result()); + } +} + + +void LIRGenerator::do_MathIntrinsic(Intrinsic* x) { + switch (x->id()) { + case vmIntrinsics::_dabs: + case vmIntrinsics::_dsqrt: { + assert(x->number_of_arguments() == 1, "wrong type"); + LIRItem value(x->argument_at(0), this); + value.load_item(); + LIR_Opr dst = rlock_result(x); + + switch (x->id()) { + case vmIntrinsics::_dsqrt: { + __ sqrt(value.result(), dst, LIR_OprFact::illegalOpr); + break; + } + case vmIntrinsics::_dabs: { + __ abs(value.result(), dst, LIR_OprFact::illegalOpr); + break; + } + } + break; + } + case vmIntrinsics::_dlog10: // fall through + case vmIntrinsics::_dlog: // fall through + case vmIntrinsics::_dsin: // fall through + case vmIntrinsics::_dtan: // fall through + case vmIntrinsics::_dcos: { + assert(x->number_of_arguments() == 1, "wrong type"); + + address runtime_entry = NULL; + switch (x->id()) { + case vmIntrinsics::_dsin: + runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dsin); + break; + case vmIntrinsics::_dcos: + runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dcos); + break; + case vmIntrinsics::_dtan: + runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dtan); + break; + case vmIntrinsics::_dlog: + runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dlog); + break; + case vmIntrinsics::_dlog10: + runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dlog10); + break; + default: + ShouldNotReachHere(); + } + + LIR_Opr result = call_runtime(x->argument_at(0), runtime_entry, x->type(), NULL); + set_result(x, result); + } + } +} + + +void LIRGenerator::do_ArrayCopy(Intrinsic* x) { + assert(x->number_of_arguments() == 5, "wrong type"); + // Note: spill caller save before setting the item + LIRItem src (x->argument_at(0), this); + LIRItem src_pos (x->argument_at(1), this); + LIRItem dst (x->argument_at(2), this); + LIRItem dst_pos (x->argument_at(3), this); + LIRItem length (x->argument_at(4), this); + // load all values in callee_save_registers, as this makes the + // parameter passing to the fast case simpler + src.load_item_force (rlock_callee_saved(T_OBJECT)); + src_pos.load_item_force (rlock_callee_saved(T_INT)); + dst.load_item_force (rlock_callee_saved(T_OBJECT)); + dst_pos.load_item_force (rlock_callee_saved(T_INT)); + length.load_item_force (rlock_callee_saved(T_INT)); + + int flags; + ciArrayKlass* expected_type; + arraycopy_helper(x, &flags, &expected_type); + + CodeEmitInfo* info = state_for(x, x->state()); + __ arraycopy(src.result(), src_pos.result(), dst.result(), dst_pos.result(), + length.result(), rlock_callee_saved(T_INT), + expected_type, flags, info); + set_no_result(x); +} + +// _i2l, _i2f, _i2d, _l2i, _l2f, _l2d, _f2i, _f2l, _f2d, _d2i, _d2l, _d2f +// _i2b, _i2c, _i2s +void LIRGenerator::do_Convert(Convert* x) { + + switch (x->op()) { + case Bytecodes::_f2l: + case Bytecodes::_d2l: + case Bytecodes::_d2i: + case Bytecodes::_l2f: + case Bytecodes::_l2d: { + + address entry; + switch (x->op()) { + case Bytecodes::_l2f: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::l2f); + break; + case Bytecodes::_l2d: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::l2d); + break; + case Bytecodes::_f2l: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::f2l); + break; + case Bytecodes::_d2l: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::d2l); + break; + case Bytecodes::_d2i: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::d2i); + break; + default: + ShouldNotReachHere(); + } + LIR_Opr result = call_runtime(x->value(), entry, x->type(), NULL); + set_result(x, result); + break; + } + + case Bytecodes::_i2f: + case Bytecodes::_i2d: { + LIRItem value(x->value(), this); + + LIR_Opr reg = rlock_result(x); + // To convert an int to double, we need to load the 32-bit int + // from memory into a single precision floating point register + // (even numbered). Then the sparc fitod instruction takes care + // of the conversion. This is a bit ugly, but is the best way to + // get the int value in a single precision floating point register + value.load_item(); + LIR_Opr tmp = force_to_spill(value.result(), T_FLOAT); + __ convert(x->op(), tmp, reg); + break; + } + break; + + case Bytecodes::_i2l: + case Bytecodes::_i2b: + case Bytecodes::_i2c: + case Bytecodes::_i2s: + case Bytecodes::_l2i: + case Bytecodes::_f2d: + case Bytecodes::_d2f: { // inline code + LIRItem value(x->value(), this); + + value.load_item(); + LIR_Opr reg = rlock_result(x); + __ convert(x->op(), value.result(), reg, false); + } + break; + + case Bytecodes::_f2i: { + LIRItem value (x->value(), this); + value.set_destroys_register(); + value.load_item(); + LIR_Opr reg = rlock_result(x); + set_vreg_flag(reg, must_start_in_memory); + __ convert(x->op(), value.result(), reg, false); + } + break; + + default: ShouldNotReachHere(); + } +} + + +void LIRGenerator::do_NewInstance(NewInstance* x) { + // This instruction can be deoptimized in the slow path : use + // O0 as result register. + const LIR_Opr reg = result_register_for(x->type()); + + if (PrintNotLoaded && !x->klass()->is_loaded()) { + tty->print_cr(" ###class not loaded at new bci %d", x->bci()); + } + CodeEmitInfo* info = state_for(x, x->state()); + LIR_Opr tmp1 = FrameMap::G1_oop_opr; + LIR_Opr tmp2 = FrameMap::G3_oop_opr; + LIR_Opr tmp3 = FrameMap::G4_oop_opr; + LIR_Opr tmp4 = FrameMap::O1_oop_opr; + LIR_Opr klass_reg = FrameMap::G5_oop_opr; + new_instance(reg, x->klass(), tmp1, tmp2, tmp3, tmp4, klass_reg, info); + LIR_Opr result = rlock_result(x); + __ move(reg, result); +} + + +void LIRGenerator::do_NewTypeArray(NewTypeArray* x) { + LIRItem length(x->length(), this); + length.load_item(); + + LIR_Opr reg = result_register_for(x->type()); + LIR_Opr tmp1 = FrameMap::G1_oop_opr; + LIR_Opr tmp2 = FrameMap::G3_oop_opr; + LIR_Opr tmp3 = FrameMap::G4_oop_opr; + LIR_Opr tmp4 = FrameMap::O1_oop_opr; + LIR_Opr klass_reg = FrameMap::G5_oop_opr; + LIR_Opr len = length.result(); + BasicType elem_type = x->elt_type(); + + __ oop2reg(ciTypeArrayKlass::make(elem_type)->encoding(), klass_reg); + + CodeEmitInfo* info = state_for(x, x->state()); + CodeStub* slow_path = new NewTypeArrayStub(klass_reg, len, reg, info); + __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, elem_type, klass_reg, slow_path); + + LIR_Opr result = rlock_result(x); + __ move(reg, result); +} + + +void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { + LIRItem length(x->length(), this); + // in case of patching (i.e., object class is not yet loaded), we need to reexecute the instruction + // and therefore provide the state before the parameters have been consumed + CodeEmitInfo* patching_info = NULL; + if (!x->klass()->is_loaded() || PatchALot) { + patching_info = state_for(x, x->state_before()); + } + + length.load_item(); + + const LIR_Opr reg = result_register_for(x->type()); + LIR_Opr tmp1 = FrameMap::G1_oop_opr; + LIR_Opr tmp2 = FrameMap::G3_oop_opr; + LIR_Opr tmp3 = FrameMap::G4_oop_opr; + LIR_Opr tmp4 = FrameMap::O1_oop_opr; + LIR_Opr klass_reg = FrameMap::G5_oop_opr; + LIR_Opr len = length.result(); + CodeEmitInfo* info = state_for(x, x->state()); + + CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info); + ciObject* obj = (ciObject*) ciObjArrayKlass::make(x->klass()); + if (obj == ciEnv::unloaded_ciobjarrayklass()) { + BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error"); + } + jobject2reg_with_patching(klass_reg, obj, patching_info); + __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, T_OBJECT, klass_reg, slow_path); + + LIR_Opr result = rlock_result(x); + __ move(reg, result); +} + + +void LIRGenerator::do_NewMultiArray(NewMultiArray* x) { + Values* dims = x->dims(); + int i = dims->length(); + LIRItemList* items = new LIRItemList(dims->length(), NULL); + while (i-- > 0) { + LIRItem* size = new LIRItem(dims->at(i), this); + items->at_put(i, size); + } + + // need to get the info before, as the items may become invalid through item_free + CodeEmitInfo* patching_info = NULL; + if (!x->klass()->is_loaded() || PatchALot) { + patching_info = state_for(x, x->state_before()); + + // cannot re-use same xhandlers for multiple CodeEmitInfos, so + // clone all handlers + x->set_exception_handlers(new XHandlers(x->exception_handlers())); + } + + i = dims->length(); + while (i-- > 0) { + LIRItem* size = items->at(i); + // if a patching_info was generated above then debug information for the state before + // the call is going to be emitted. The LIRGenerator calls above may have left some values + // in registers and that's been recorded in the CodeEmitInfo. In that case the items + // for those values can't simply be freed if they are registers because the values + // might be destroyed by store_stack_parameter. So in the case of patching, delay the + // freeing of the items that already were in registers + size->load_item(); + store_stack_parameter (size->result(), + in_ByteSize(STACK_BIAS + + (i + frame::memory_parameter_word_sp_offset) * wordSize)); + } + + // This instruction can be deoptimized in the slow path : use + // O0 as result register. + const LIR_Opr reg = result_register_for(x->type()); + CodeEmitInfo* info = state_for(x, x->state()); + + jobject2reg_with_patching(reg, x->klass(), patching_info); + LIR_Opr rank = FrameMap::O1_opr; + __ move(LIR_OprFact::intConst(x->rank()), rank); + LIR_Opr varargs = FrameMap::as_pointer_opr(O2); + int offset_from_sp = (frame::memory_parameter_word_sp_offset * wordSize) + STACK_BIAS; + __ add(FrameMap::SP_opr, + LIR_OprFact::intptrConst(offset_from_sp), + varargs); + LIR_OprList* args = new LIR_OprList(3); + args->append(reg); + args->append(rank); + args->append(varargs); + __ call_runtime(Runtime1::entry_for(Runtime1::new_multi_array_id), + LIR_OprFact::illegalOpr, + reg, args, info); + + LIR_Opr result = rlock_result(x); + __ move(reg, result); +} + + +void LIRGenerator::do_BlockBegin(BlockBegin* x) { +} + + +void LIRGenerator::do_CheckCast(CheckCast* x) { + LIRItem obj(x->obj(), this); + CodeEmitInfo* patching_info = NULL; + if (!x->klass()->is_loaded() || (PatchALot && !x->is_incompatible_class_change_check())) { + // must do this before locking the destination register as an oop register, + // and before the obj is loaded (so x->obj()->item() is valid for creating a debug info location) + patching_info = state_for(x, x->state_before()); + } + obj.load_item(); + LIR_Opr out_reg = rlock_result(x); + CodeStub* stub; + CodeEmitInfo* info_for_exception = state_for(x, x->state()->copy_locks()); + + if (x->is_incompatible_class_change_check()) { + assert(patching_info == NULL, "can't patch this"); + stub = new SimpleExceptionStub(Runtime1::throw_incompatible_class_change_error_id, LIR_OprFact::illegalOpr, info_for_exception); + } else { + stub = new SimpleExceptionStub(Runtime1::throw_class_cast_exception_id, obj.result(), info_for_exception); + } + LIR_Opr tmp1 = FrameMap::G1_oop_opr; + LIR_Opr tmp2 = FrameMap::G3_oop_opr; + LIR_Opr tmp3 = FrameMap::G4_oop_opr; + __ checkcast(out_reg, obj.result(), x->klass(), tmp1, tmp2, tmp3, + x->direct_compare(), info_for_exception, patching_info, stub, + x->profiled_method(), x->profiled_bci()); +} + + +void LIRGenerator::do_InstanceOf(InstanceOf* x) { + LIRItem obj(x->obj(), this); + CodeEmitInfo* patching_info = NULL; + if (!x->klass()->is_loaded() || PatchALot) { + patching_info = state_for(x, x->state_before()); + } + // ensure the result register is not the input register because the result is initialized before the patching safepoint + obj.load_item(); + LIR_Opr out_reg = rlock_result(x); + LIR_Opr tmp1 = FrameMap::G1_oop_opr; + LIR_Opr tmp2 = FrameMap::G3_oop_opr; + LIR_Opr tmp3 = FrameMap::G4_oop_opr; + __ instanceof(out_reg, obj.result(), x->klass(), tmp1, tmp2, tmp3, x->direct_compare(), patching_info); +} + + +void LIRGenerator::do_If(If* x) { + assert(x->number_of_sux() == 2, "inconsistency"); + ValueTag tag = x->x()->type()->tag(); + LIRItem xitem(x->x(), this); + LIRItem yitem(x->y(), this); + LIRItem* xin = &xitem; + LIRItem* yin = &yitem; + If::Condition cond = x->cond(); + + if (tag == longTag) { + // for longs, only conditions "eql", "neq", "lss", "geq" are valid; + // mirror for other conditions + if (cond == If::gtr || cond == If::leq) { + // swap inputs + cond = Instruction::mirror(cond); + xin = &yitem; + yin = &xitem; + } + xin->set_destroys_register(); + } + + LIR_Opr left = LIR_OprFact::illegalOpr; + LIR_Opr right = LIR_OprFact::illegalOpr; + + xin->load_item(); + left = xin->result(); + + if (is_simm13(yin->result())) { + // inline int constants which are small enough to be immediate operands + right = LIR_OprFact::value_type(yin->value()->type()); + } else if (tag == longTag && yin->is_constant() && yin->get_jlong_constant() == 0 && + (cond == If::eql || cond == If::neq)) { + // inline long zero + right = LIR_OprFact::value_type(yin->value()->type()); + } else if (tag == objectTag && yin->is_constant() && (yin->get_jobject_constant()->is_null_object())) { + right = LIR_OprFact::value_type(yin->value()->type()); + } else { + yin->load_item(); + right = yin->result(); + } + set_no_result(x); + + // add safepoint before generating condition code so it can be recomputed + if (x->is_safepoint()) { + // increment backedge counter if needed + increment_backedge_counter(state_for(x, x->state_before())); + + __ safepoint(new_register(T_INT), state_for(x, x->state_before())); + } + + __ cmp(lir_cond(cond), left, right); + profile_branch(x, cond); + move_to_phi(x->state()); + if (x->x()->type()->is_float_kind()) { + __ branch(lir_cond(cond), right->type(), x->tsux(), x->usux()); + } else { + __ branch(lir_cond(cond), right->type(), x->tsux()); + } + assert(x->default_sux() == x->fsux(), "wrong destination above"); + __ jump(x->default_sux()); +} + + +LIR_Opr LIRGenerator::getThreadPointer() { + return FrameMap::as_pointer_opr(G2); +} + + +void LIRGenerator::trace_block_entry(BlockBegin* block) { + __ move(LIR_OprFact::intConst(block->block_id()), FrameMap::O0_opr); + LIR_OprList* args = new LIR_OprList(1); + args->append(FrameMap::O0_opr); + address func = CAST_FROM_FN_PTR(address, Runtime1::trace_block_entry); + __ call_runtime_leaf(func, rlock_callee_saved(T_INT), LIR_OprFact::illegalOpr, args); +} + + +void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, + CodeEmitInfo* info) { +#ifdef _LP64 + __ store(value, address, info); +#else + __ volatile_store_mem_reg(value, address, info); +#endif +} + +void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, + CodeEmitInfo* info) { +#ifdef _LP64 + __ load(address, result, info); +#else + __ volatile_load_mem_reg(address, result, info); +#endif +} + + +void LIRGenerator::put_Object_unsafe(LIR_Opr src, LIR_Opr offset, LIR_Opr data, + BasicType type, bool is_volatile) { + LIR_Opr base_op = src; + LIR_Opr index_op = offset; + + bool is_obj = (type == T_ARRAY || type == T_OBJECT); +#ifndef _LP64 + if (is_volatile && type == T_LONG) { + __ volatile_store_unsafe_reg(data, src, offset, type, NULL, lir_patch_none); + } else +#endif + { + if (type == T_BOOLEAN) { + type = T_BYTE; + } + LIR_Address* addr; + if (type == T_ARRAY || type == T_OBJECT) { + LIR_Opr tmp = new_pointer_register(); + __ add(base_op, index_op, tmp); + addr = new LIR_Address(tmp, 0, type); + } else { + addr = new LIR_Address(base_op, index_op, type); + } + + __ move(data, addr); + if (is_obj) { + // This address is precise + post_barrier(LIR_OprFact::address(addr), data); + } + } +} + + +void LIRGenerator::get_Object_unsafe(LIR_Opr dst, LIR_Opr src, LIR_Opr offset, + BasicType type, bool is_volatile) { +#ifndef _LP64 + if (is_volatile && type == T_LONG) { + __ volatile_load_unsafe_reg(src, offset, dst, type, NULL, lir_patch_none); + } else +#endif + { + LIR_Address* addr = new LIR_Address(src, offset, type); + __ load(addr, dst); + } +} diff --git a/hotspot/src/cpu/sparc/vm/c1_LinearScan_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_LinearScan_sparc.cpp new file mode 100644 index 00000000000..1ec904dd812 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_LinearScan_sparc.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_LinearScan_sparc.cpp.incl" + +void LinearScan::allocate_fpu_stack() { + // No FPU stack on SPARC +} diff --git a/hotspot/src/cpu/sparc/vm/c1_LinearScan_sparc.hpp b/hotspot/src/cpu/sparc/vm/c1_LinearScan_sparc.hpp new file mode 100644 index 00000000000..5eb70f52950 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_LinearScan_sparc.hpp @@ -0,0 +1,73 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline bool LinearScan::is_processed_reg_num(int reg_num) { + return reg_num < 26 || reg_num > 31; +} + +inline int LinearScan::num_physical_regs(BasicType type) { + // Sparc requires two cpu registers for long + // and two cpu registers for double +#ifdef _LP64 + if (type == T_DOUBLE) { +#else + if (type == T_DOUBLE || type == T_LONG) { +#endif + return 2; + } + return 1; +} + + +inline bool LinearScan::requires_adjacent_regs(BasicType type) { +#ifdef _LP64 + return type == T_DOUBLE; +#else + return type == T_DOUBLE || type == T_LONG; +#endif +} + +inline bool LinearScan::is_caller_save(int assigned_reg) { + return assigned_reg > pd_last_callee_saved_reg && assigned_reg <= pd_last_fpu_reg; +} + + +inline void LinearScan::pd_add_temps(LIR_Op* op) { + // No special case behaviours yet +} + + +inline bool LinearScanWalker::pd_init_regs_for_alloc(Interval* cur) { + if (allocator()->gen()->is_vreg_flag_set(cur->reg_num(), LIRGenerator::callee_saved)) { + assert(cur->type() != T_FLOAT && cur->type() != T_DOUBLE, "cpu regs only"); + _first_reg = pd_first_callee_saved_reg; + _last_reg = pd_last_callee_saved_reg; + return true; + } else if (cur->type() == T_INT || cur->type() == T_LONG || cur->type() == T_OBJECT) { + _first_reg = pd_first_cpu_reg; + _last_reg = pd_last_allocatable_cpu_reg; + return true; + } + return false; +} diff --git a/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.cpp new file mode 100644 index 00000000000..3e07437ce4f --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.cpp @@ -0,0 +1,409 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_MacroAssembler_sparc.cpp.incl" + +void C1_MacroAssembler::inline_cache_check(Register receiver, Register iCache) { + Label L; + const Register temp_reg = G3_scratch; + // Note: needs more testing of out-of-line vs. inline slow case + Address ic_miss(temp_reg, SharedRuntime::get_ic_miss_stub()); + verify_oop(receiver); + ld_ptr(receiver, oopDesc::klass_offset_in_bytes(), temp_reg); + cmp(temp_reg, iCache); + brx(Assembler::equal, true, Assembler::pt, L); + delayed()->nop(); + jump_to(ic_miss, 0); + delayed()->nop(); + align(CodeEntryAlignment); + bind(L); +} + + +void C1_MacroAssembler::method_exit(bool restore_frame) { + // this code must be structured this way so that the return + // instruction can be a safepoint. + if (restore_frame) { + restore(); + } + retl(); + delayed()->nop(); +} + + +void C1_MacroAssembler::explicit_null_check(Register base) { + Unimplemented(); +} + + +void C1_MacroAssembler::build_frame(int frame_size_in_bytes) { + + generate_stack_overflow_check(frame_size_in_bytes); + // Create the frame. + save_frame_c1(frame_size_in_bytes); +} + + +void C1_MacroAssembler::unverified_entry(Register receiver, Register ic_klass) { + if (C1Breakpoint) breakpoint_trap(); + inline_cache_check(receiver, ic_klass); +} + + +void C1_MacroAssembler::verified_entry() { + if (C1Breakpoint) breakpoint_trap(); + // build frame + verify_FPU(0, "method_entry"); +} + + +void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox, Register Rscratch, Label& slow_case) { + assert_different_registers(Rmark, Roop, Rbox, Rscratch); + + Label done; + + Address mark_addr(Roop, 0, oopDesc::mark_offset_in_bytes()); + + // The following move must be the first instruction of emitted since debug + // information may be generated for it. + // Load object header + ld_ptr(mark_addr, Rmark); + + verify_oop(Roop); + + // save object being locked into the BasicObjectLock + st_ptr(Roop, Rbox, BasicObjectLock::obj_offset_in_bytes()); + + if (UseBiasedLocking) { + biased_locking_enter(Roop, Rmark, Rscratch, done, &slow_case); + } + + // Save Rbox in Rscratch to be used for the cas operation + mov(Rbox, Rscratch); + + // and mark it unlocked + or3(Rmark, markOopDesc::unlocked_value, Rmark); + + // save unlocked object header into the displaced header location on the stack + st_ptr(Rmark, Rbox, BasicLock::displaced_header_offset_in_bytes()); + + // compare object markOop with Rmark and if equal exchange Rscratch with object markOop + assert(mark_addr.disp() == 0, "cas must take a zero displacement"); + casx_under_lock(mark_addr.base(), Rmark, Rscratch, (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + // if compare/exchange succeeded we found an unlocked object and we now have locked it + // hence we are done + cmp(Rmark, Rscratch); + brx(Assembler::equal, false, Assembler::pt, done); + delayed()->sub(Rscratch, SP, Rscratch); //pull next instruction into delay slot + // we did not find an unlocked object so see if this is a recursive case + // sub(Rscratch, SP, Rscratch); + assert(os::vm_page_size() > 0xfff, "page size too small - change the constant"); + andcc(Rscratch, 0xfffff003, Rscratch); + brx(Assembler::notZero, false, Assembler::pn, slow_case); + delayed()->st_ptr(Rscratch, Rbox, BasicLock::displaced_header_offset_in_bytes()); + bind(done); +} + + +void C1_MacroAssembler::unlock_object(Register Rmark, Register Roop, Register Rbox, Label& slow_case) { + assert_different_registers(Rmark, Roop, Rbox); + + Label done; + + Address mark_addr(Roop, 0, oopDesc::mark_offset_in_bytes()); + assert(mark_addr.disp() == 0, "cas must take a zero displacement"); + + if (UseBiasedLocking) { + // load the object out of the BasicObjectLock + ld_ptr(Rbox, BasicObjectLock::obj_offset_in_bytes(), Roop); + verify_oop(Roop); + biased_locking_exit(mark_addr, Rmark, done); + } + // Test first it it is a fast recursive unlock + ld_ptr(Rbox, BasicLock::displaced_header_offset_in_bytes(), Rmark); + br_null(Rmark, false, Assembler::pt, done); + delayed()->nop(); + if (!UseBiasedLocking) { + // load object + ld_ptr(Rbox, BasicObjectLock::obj_offset_in_bytes(), Roop); + verify_oop(Roop); + } + + // Check if it is still a light weight lock, this is is true if we see + // the stack address of the basicLock in the markOop of the object + casx_under_lock(mark_addr.base(), Rbox, Rmark, (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + cmp(Rbox, Rmark); + + brx(Assembler::notEqual, false, Assembler::pn, slow_case); + delayed()->nop(); + // Done + bind(done); +} + + +void C1_MacroAssembler::try_allocate( + Register obj, // result: pointer to object after successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Register t2, // temp register + Label& slow_case // continuation point if fast allocation fails +) { + if (UseTLAB) { + tlab_allocate(obj, var_size_in_bytes, con_size_in_bytes, t1, slow_case); + } else { + eden_allocate(obj, var_size_in_bytes, con_size_in_bytes, t1, t2, slow_case); + } +} + + +void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register len, Register t1, Register t2) { + assert_different_registers(obj, klass, len, t1, t2); + if (UseBiasedLocking && !len->is_valid()) { + ld_ptr(klass, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes(), t1); + } else { + set((intx)markOopDesc::prototype(), t1); + } + st_ptr(t1 , obj, oopDesc::mark_offset_in_bytes ()); + st_ptr(klass, obj, oopDesc::klass_offset_in_bytes ()); + if (len->is_valid()) st(len , obj, arrayOopDesc::length_offset_in_bytes()); +} + + +void C1_MacroAssembler::initialize_body(Register base, Register index) { + assert_different_registers(base, index); + Label loop; + bind(loop); + subcc(index, HeapWordSize, index); + brx(Assembler::greaterEqual, true, Assembler::pt, loop); + delayed()->st_ptr(G0, base, index); +} + + +void C1_MacroAssembler::allocate_object( + Register obj, // result: pointer to object after successful allocation + Register t1, // temp register + Register t2, // temp register + Register t3, // temp register + int hdr_size, // object header size in words + int obj_size, // object size in words + Register klass, // object klass + Label& slow_case // continuation point if fast allocation fails +) { + assert_different_registers(obj, t1, t2, t3, klass); + assert(klass == G5, "must be G5"); + + // allocate space & initialize header + if (!is_simm13(obj_size * wordSize)) { + // would need to use extra register to load + // object size => go the slow case for now + br(Assembler::always, false, Assembler::pt, slow_case); + delayed()->nop(); + return; + } + try_allocate(obj, noreg, obj_size * wordSize, t2, t3, slow_case); + + initialize_object(obj, klass, noreg, obj_size * HeapWordSize, t1, t2); +} + +void C1_MacroAssembler::initialize_object( + Register obj, // result: pointer to object after successful allocation + Register klass, // object klass + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Register t2 // temp register + ) { + const int hdr_size_in_bytes = oopDesc::header_size_in_bytes(); + + initialize_header(obj, klass, noreg, t1, t2); + +#ifdef ASSERT + { + Label ok; + ld(klass, klassOopDesc::header_size() * HeapWordSize + Klass::layout_helper_offset_in_bytes(), t1); + if (var_size_in_bytes != noreg) { + cmp(t1, var_size_in_bytes); + } else { + cmp(t1, con_size_in_bytes); + } + brx(Assembler::equal, false, Assembler::pt, ok); + delayed()->nop(); + stop("bad size in initialize_object"); + should_not_reach_here(); + + bind(ok); + } + +#endif + + // initialize body + const int threshold = 5 * HeapWordSize; // approximate break even point for code size + if (var_size_in_bytes != noreg) { + // use a loop + add(obj, hdr_size_in_bytes, t1); // compute address of first element + sub(var_size_in_bytes, hdr_size_in_bytes, t2); // compute size of body + initialize_body(t1, t2); +#ifndef _LP64 + } else if (VM_Version::v9_instructions_work() && con_size_in_bytes < threshold * 2) { + // on v9 we can do double word stores to fill twice as much space. + assert(hdr_size_in_bytes % 8 == 0, "double word aligned"); + assert(con_size_in_bytes % 8 == 0, "double word aligned"); + for (int i = hdr_size_in_bytes; i < con_size_in_bytes; i += 2 * HeapWordSize) stx(G0, obj, i); +#endif + } else if (con_size_in_bytes <= threshold) { + // use explicit NULL stores + for (int i = hdr_size_in_bytes; i < con_size_in_bytes; i += HeapWordSize) st_ptr(G0, obj, i); + } else if (con_size_in_bytes > hdr_size_in_bytes) { + // use a loop + const Register base = t1; + const Register index = t2; + add(obj, hdr_size_in_bytes, base); // compute address of first element + // compute index = number of words to clear + set(con_size_in_bytes - hdr_size_in_bytes, index); + initialize_body(base, index); + } + + if (DTraceAllocProbes) { + assert(obj == O0, "must be"); + call(CAST_FROM_FN_PTR(address, Runtime1::entry_for(Runtime1::dtrace_object_alloc_id)), + relocInfo::runtime_call_type); + delayed()->nop(); + } + + verify_oop(obj); +} + + +void C1_MacroAssembler::allocate_array( + Register obj, // result: pointer to array after successful allocation + Register len, // array length + Register t1, // temp register + Register t2, // temp register + Register t3, // temp register + int hdr_size, // object header size in words + int elt_size, // element size in bytes + Register klass, // object klass + Label& slow_case // continuation point if fast allocation fails +) { + assert_different_registers(obj, len, t1, t2, t3, klass); + assert(klass == G5, "must be G5"); + assert(t1 == G1, "must be G1"); + + // determine alignment mask + assert(!(BytesPerWord & 1), "must be a multiple of 2 for masking code to work"); + + // check for negative or excessive length + // note: the maximum length allowed is chosen so that arrays of any + // element size with this length are always smaller or equal + // to the largest integer (i.e., array size computation will + // not overflow) + set(max_array_allocation_length, t1); + cmp(len, t1); + br(Assembler::greaterUnsigned, false, Assembler::pn, slow_case); + + // compute array size + // note: if 0 <= len <= max_length, len*elt_size + header + alignment is + // smaller or equal to the largest integer; also, since top is always + // aligned, we can do the alignment here instead of at the end address + // computation + const Register arr_size = t1; + switch (elt_size) { + case 1: delayed()->mov(len, arr_size); break; + case 2: delayed()->sll(len, 1, arr_size); break; + case 4: delayed()->sll(len, 2, arr_size); break; + case 8: delayed()->sll(len, 3, arr_size); break; + default: ShouldNotReachHere(); + } + add(arr_size, hdr_size * wordSize + MinObjAlignmentInBytesMask, arr_size); // add space for header & alignment + and3(arr_size, ~MinObjAlignmentInBytesMask, arr_size); // align array size + + // allocate space & initialize header + if (UseTLAB) { + tlab_allocate(obj, arr_size, 0, t2, slow_case); + } else { + eden_allocate(obj, arr_size, 0, t2, t3, slow_case); + } + initialize_header(obj, klass, len, t2, t3); + + // initialize body + const Register base = t2; + const Register index = t3; + add(obj, hdr_size * wordSize, base); // compute address of first element + sub(arr_size, hdr_size * wordSize, index); // compute index = number of words to clear + initialize_body(base, index); + + if (DTraceAllocProbes) { + assert(obj == O0, "must be"); + call(CAST_FROM_FN_PTR(address, Runtime1::entry_for(Runtime1::dtrace_object_alloc_id)), + relocInfo::runtime_call_type); + delayed()->nop(); + } + + verify_oop(obj); +} + + +#ifndef PRODUCT + +void C1_MacroAssembler::verify_stack_oop(int stack_offset) { + if (!VerifyOops) return; + verify_oop_addr(Address(SP, 0, stack_offset + STACK_BIAS)); +} + +void C1_MacroAssembler::verify_not_null_oop(Register r) { + Label not_null; + br_zero(Assembler::notEqual, false, Assembler::pt, r, not_null); + delayed()->nop(); + stop("non-null oop required"); + bind(not_null); + if (!VerifyOops) return; + verify_oop(r); +} + +void C1_MacroAssembler::invalidate_registers(bool iregisters, bool lregisters, bool oregisters, + Register preserve1, Register preserve2) { + if (iregisters) { + for (int i = 0; i < 6; i++) { + Register r = as_iRegister(i); + if (r != preserve1 && r != preserve2) set(0xdead, r); + } + } + if (oregisters) { + for (int i = 0; i < 6; i++) { + Register r = as_oRegister(i); + if (r != preserve1 && r != preserve2) set(0xdead, r); + } + } + if (lregisters) { + for (int i = 0; i < 8; i++) { + Register r = as_lRegister(i); + if (r != preserve1 && r != preserve2) set(0xdead, r); + } + } +} + + +#endif diff --git a/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp b/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp new file mode 100644 index 00000000000..eaa981bca6e --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp @@ -0,0 +1,86 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + void pd_init() { /* nothing to do */ } + + public: + void try_allocate( + Register obj, // result: pointer to object after successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Register t2, // temp register + Label& slow_case // continuation point if fast allocation fails + ); + + void initialize_header(Register obj, Register klass, Register len, Register t1, Register t2); + void initialize_body(Register base, Register index); + + // locking/unlocking + void lock_object (Register Rmark, Register Roop, Register Rbox, Register Rscratch, Label& slow_case); + void unlock_object(Register Rmark, Register Roop, Register Rbox, Label& slow_case); + + void initialize_object( + Register obj, // result: pointer to object after successful allocation + Register klass, // object klass + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Register t2 // temp register + ); + + // allocation of fixed-size objects + // (can also be used to allocate fixed-size arrays, by setting + // hdr_size correctly and storing the array length afterwards) + void allocate_object( + Register obj, // result: pointer to object after successful allocation + Register t1, // temp register + Register t2, // temp register + Register t3, // temp register + int hdr_size, // object header size in words + int obj_size, // object size in words + Register klass, // object klass + Label& slow_case // continuation point if fast allocation fails + ); + + enum { + max_array_allocation_length = 0x01000000 // sparc friendly value, requires sethi only + }; + + // allocation of arrays + void allocate_array( + Register obj, // result: pointer to array after successful allocation + Register len, // array length + Register t1, // temp register + Register t2, // temp register + Register t3, // temp register + int hdr_size, // object header size in words + int elt_size, // element size in bytes + Register klass, // object klass + Label& slow_case // continuation point if fast allocation fails + ); + + // invalidates registers in this window + void invalidate_registers(bool iregisters, bool lregisters, bool oregisters, + Register preserve1 = noreg, Register preserve2 = noreg); diff --git a/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp new file mode 100644 index 00000000000..45c63228c64 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp @@ -0,0 +1,898 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_Runtime1_sparc.cpp.incl" + +// Implementation of StubAssembler + +int StubAssembler::call_RT(Register oop_result1, Register oop_result2, address entry_point, int number_of_arguments) { + // for sparc changing the number of arguments doesn't change + // anything about the frame size so we'll always lie and claim that + // we are only passing 1 argument. + set_num_rt_args(1); + + assert_not_delayed(); + // bang stack before going to runtime + set(-os::vm_page_size() + STACK_BIAS, G3_scratch); + st(G0, SP, G3_scratch); + + // debugging support + assert(number_of_arguments >= 0 , "cannot have negative number of arguments"); + + set_last_Java_frame(SP, noreg); + if (VerifyThread) mov(G2_thread, O0); // about to be smashed; pass early + save_thread(L7_thread_cache); + // do the call + call(entry_point, relocInfo::runtime_call_type); + if (!VerifyThread) { + delayed()->mov(G2_thread, O0); // pass thread as first argument + } else { + delayed()->nop(); // (thread already passed) + } + int call_offset = offset(); // offset of return address + restore_thread(L7_thread_cache); + reset_last_Java_frame(); + + // check for pending exceptions + { Label L; + Address exception_addr(G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + ld_ptr(exception_addr, Gtemp); + br_null(Gtemp, false, pt, L); + delayed()->nop(); + Address vm_result_addr(G2_thread, 0, in_bytes(JavaThread::vm_result_offset())); + st_ptr(G0, vm_result_addr); + Address vm_result_addr_2(G2_thread, 0, in_bytes(JavaThread::vm_result_2_offset())); + st_ptr(G0, vm_result_addr_2); + + if (frame_size() == no_frame_size) { + // we use O7 linkage so that forward_exception_entry has the issuing PC + call(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); + delayed()->restore(); + } else if (_stub_id == Runtime1::forward_exception_id) { + should_not_reach_here(); + } else { + Address exc(G4, Runtime1::entry_for(Runtime1::forward_exception_id)); + jump_to(exc, 0); + delayed()->nop(); + } + bind(L); + } + + // get oop result if there is one and reset the value in the thread + if (oop_result1->is_valid()) { // get oop result if there is one and reset it in the thread + get_vm_result (oop_result1); + } else { + // be a little paranoid and clear the result + Address vm_result_addr(G2_thread, 0, in_bytes(JavaThread::vm_result_offset())); + st_ptr(G0, vm_result_addr); + } + + if (oop_result2->is_valid()) { + get_vm_result_2(oop_result2); + } else { + // be a little paranoid and clear the result + Address vm_result_addr_2(G2_thread, 0, in_bytes(JavaThread::vm_result_2_offset())); + st_ptr(G0, vm_result_addr_2); + } + + return call_offset; +} + + +int StubAssembler::call_RT(Register oop_result1, Register oop_result2, address entry, Register arg1) { + // O0 is reserved for the thread + mov(arg1, O1); + return call_RT(oop_result1, oop_result2, entry, 1); +} + + +int StubAssembler::call_RT(Register oop_result1, Register oop_result2, address entry, Register arg1, Register arg2) { + // O0 is reserved for the thread + mov(arg1, O1); + mov(arg2, O2); assert(arg2 != O1, "smashed argument"); + return call_RT(oop_result1, oop_result2, entry, 2); +} + + +int StubAssembler::call_RT(Register oop_result1, Register oop_result2, address entry, Register arg1, Register arg2, Register arg3) { + // O0 is reserved for the thread + mov(arg1, O1); + mov(arg2, O2); assert(arg2 != O1, "smashed argument"); + mov(arg3, O3); assert(arg3 != O1 && arg3 != O2, "smashed argument"); + return call_RT(oop_result1, oop_result2, entry, 3); +} + + +// Implementation of Runtime1 + +#define __ sasm-> + +static int cpu_reg_save_offsets[FrameMap::nof_cpu_regs]; +static int fpu_reg_save_offsets[FrameMap::nof_fpu_regs]; +static int reg_save_size_in_words; +static int frame_size_in_bytes = -1; + +static OopMap* generate_oop_map(StubAssembler* sasm, bool save_fpu_registers) { + assert(frame_size_in_bytes == __ total_frame_size_in_bytes(reg_save_size_in_words), + " mismatch in calculation"); + sasm->set_frame_size(frame_size_in_bytes / BytesPerWord); + int frame_size_in_slots = frame_size_in_bytes / sizeof(jint); + OopMap* oop_map = new OopMap(frame_size_in_slots, 0); + + int i; + for (i = 0; i < FrameMap::nof_cpu_regs; i++) { + Register r = as_Register(i); + if (r == G1 || r == G3 || r == G4 || r == G5) { + int sp_offset = cpu_reg_save_offsets[i]; + oop_map->set_callee_saved(VMRegImpl::stack2reg(sp_offset), + r->as_VMReg()); + } + } + + if (save_fpu_registers) { + for (i = 0; i < FrameMap::nof_fpu_regs; i++) { + FloatRegister r = as_FloatRegister(i); + int sp_offset = fpu_reg_save_offsets[i]; + oop_map->set_callee_saved(VMRegImpl::stack2reg(sp_offset), + r->as_VMReg()); + } + } + return oop_map; +} + +static OopMap* save_live_registers(StubAssembler* sasm, bool save_fpu_registers = true) { + assert(frame_size_in_bytes == __ total_frame_size_in_bytes(reg_save_size_in_words), + " mismatch in calculation"); + __ save_frame_c1(frame_size_in_bytes); + sasm->set_frame_size(frame_size_in_bytes / BytesPerWord); + + // Record volatile registers as callee-save values in an OopMap so their save locations will be + // propagated to the caller frame's RegisterMap during StackFrameStream construction (needed for + // deoptimization; see compiledVFrame::create_stack_value). The caller's I, L and O registers + // are saved in register windows - I's and L's in the caller's frame and O's in the stub frame + // (as the stub's I's) when the runtime routine called by the stub creates its frame. + // OopMap frame sizes are in c2 stack slot sizes (sizeof(jint)) + + int i; + for (i = 0; i < FrameMap::nof_cpu_regs; i++) { + Register r = as_Register(i); + if (r == G1 || r == G3 || r == G4 || r == G5) { + int sp_offset = cpu_reg_save_offsets[i]; + __ st_ptr(r, SP, (sp_offset * BytesPerWord) + STACK_BIAS); + } + } + + if (save_fpu_registers) { + for (i = 0; i < FrameMap::nof_fpu_regs; i++) { + FloatRegister r = as_FloatRegister(i); + int sp_offset = fpu_reg_save_offsets[i]; + __ stf(FloatRegisterImpl::S, r, SP, (sp_offset * BytesPerWord) + STACK_BIAS); + } + } + + return generate_oop_map(sasm, save_fpu_registers); +} + +static void restore_live_registers(StubAssembler* sasm, bool restore_fpu_registers = true) { + for (int i = 0; i < FrameMap::nof_cpu_regs; i++) { + Register r = as_Register(i); + if (r == G1 || r == G3 || r == G4 || r == G5) { + __ ld_ptr(SP, (cpu_reg_save_offsets[i] * BytesPerWord) + STACK_BIAS, r); + } + } + + if (restore_fpu_registers) { + for (int i = 0; i < FrameMap::nof_fpu_regs; i++) { + FloatRegister r = as_FloatRegister(i); + __ ldf(FloatRegisterImpl::S, SP, (fpu_reg_save_offsets[i] * BytesPerWord) + STACK_BIAS, r); + } + } +} + + +void Runtime1::initialize_pd() { + // compute word offsets from SP at which live (non-windowed) registers are captured by stub routines + // + // A stub routine will have a frame that is at least large enough to hold + // a register window save area (obviously) and the volatile g registers + // and floating registers. A user of save_live_registers can have a frame + // that has more scratch area in it (although typically they will use L-regs). + // in that case the frame will look like this (stack growing down) + // + // FP -> | | + // | scratch mem | + // | " " | + // -------------- + // | float regs | + // | " " | + // --------------- + // | G regs | + // | " " | + // --------------- + // | abi reg. | + // | window save | + // | area | + // SP -> --------------- + // + int i; + int sp_offset = round_to(frame::register_save_words, 2); // start doubleword aligned + + // only G int registers are saved explicitly; others are found in register windows + for (i = 0; i < FrameMap::nof_cpu_regs; i++) { + Register r = as_Register(i); + if (r == G1 || r == G3 || r == G4 || r == G5) { + cpu_reg_save_offsets[i] = sp_offset; + sp_offset++; + } + } + + // all float registers are saved explicitly + assert(FrameMap::nof_fpu_regs == 32, "double registers not handled here"); + for (i = 0; i < FrameMap::nof_fpu_regs; i++) { + fpu_reg_save_offsets[i] = sp_offset; + sp_offset++; + } + reg_save_size_in_words = sp_offset - frame::memory_parameter_word_sp_offset; + // this should match assembler::total_frame_size_in_bytes, which + // isn't callable from this context. It's checked by an assert when + // it's used though. + frame_size_in_bytes = align_size_up(sp_offset * wordSize, 8); +} + + +OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, bool has_argument) { + // make a frame and preserve the caller's caller-save registers + OopMap* oop_map = save_live_registers(sasm); + int call_offset; + if (!has_argument) { + call_offset = __ call_RT(noreg, noreg, target); + } else { + call_offset = __ call_RT(noreg, noreg, target, G4); + } + OopMapSet* oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, oop_map); + + __ should_not_reach_here(); + return oop_maps; +} + + +OopMapSet* Runtime1::generate_stub_call(StubAssembler* sasm, Register result, address target, + Register arg1, Register arg2, Register arg3) { + // make a frame and preserve the caller's caller-save registers + OopMap* oop_map = save_live_registers(sasm); + + int call_offset; + if (arg1 == noreg) { + call_offset = __ call_RT(result, noreg, target); + } else if (arg2 == noreg) { + call_offset = __ call_RT(result, noreg, target, arg1); + } else if (arg3 == noreg) { + call_offset = __ call_RT(result, noreg, target, arg1, arg2); + } else { + call_offset = __ call_RT(result, noreg, target, arg1, arg2, arg3); + } + OopMapSet* oop_maps = NULL; + + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, oop_map); + restore_live_registers(sasm); + + __ ret(); + __ delayed()->restore(); + + return oop_maps; +} + + +OopMapSet* Runtime1::generate_patching(StubAssembler* sasm, address target) { + // make a frame and preserve the caller's caller-save registers + OopMap* oop_map = save_live_registers(sasm); + + // call the runtime patching routine, returns non-zero if nmethod got deopted. + int call_offset = __ call_RT(noreg, noreg, target); + OopMapSet* oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, oop_map); + + // re-execute the patched instruction or, if the nmethod was deoptmized, return to the + // deoptimization handler entry that will cause re-execution of the current bytecode + DeoptimizationBlob* deopt_blob = SharedRuntime::deopt_blob(); + assert(deopt_blob != NULL, "deoptimization blob must have been created"); + + Label no_deopt; + __ tst(O0); + __ brx(Assembler::equal, false, Assembler::pt, no_deopt); + __ delayed()->nop(); + + // return to the deoptimization handler entry for unpacking and rexecute + // if we simply returned the we'd deopt as if any call we patched had just + // returned. + + restore_live_registers(sasm); + __ restore(); + __ br(Assembler::always, false, Assembler::pt, deopt_blob->unpack_with_reexecution(), relocInfo::runtime_call_type); + __ delayed()->nop(); + + __ bind(no_deopt); + restore_live_registers(sasm); + __ ret(); + __ delayed()->restore(); + + return oop_maps; +} + +OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { + + OopMapSet* oop_maps = NULL; + // for better readability + const bool must_gc_arguments = true; + const bool dont_gc_arguments = false; + + // stub code & info for the different stubs + switch (id) { + case forward_exception_id: + { + // we're handling an exception in the context of a compiled + // frame. The registers have been saved in the standard + // places. Perform an exception lookup in the caller and + // dispatch to the handler if found. Otherwise unwind and + // dispatch to the callers exception handler. + + oop_maps = new OopMapSet(); + OopMap* oop_map = generate_oop_map(sasm, true); + + // transfer the pending exception to the exception_oop + __ ld_ptr(G2_thread, in_bytes(JavaThread::pending_exception_offset()), Oexception); + __ ld_ptr(Oexception, 0, G0); + __ st_ptr(G0, G2_thread, in_bytes(JavaThread::pending_exception_offset())); + __ add(I7, frame::pc_return_offset, Oissuing_pc); + + generate_handle_exception(sasm, oop_maps, oop_map); + __ should_not_reach_here(); + } + break; + + case new_instance_id: + case fast_new_instance_id: + case fast_new_instance_init_check_id: + { + Register G5_klass = G5; // Incoming + Register O0_obj = O0; // Outgoing + + if (id == new_instance_id) { + __ set_info("new_instance", dont_gc_arguments); + } else if (id == fast_new_instance_id) { + __ set_info("fast new_instance", dont_gc_arguments); + } else { + assert(id == fast_new_instance_init_check_id, "bad StubID"); + __ set_info("fast new_instance init check", dont_gc_arguments); + } + + if ((id == fast_new_instance_id || id == fast_new_instance_init_check_id) && + UseTLAB && FastTLABRefill) { + Label slow_path; + Register G1_obj_size = G1; + Register G3_t1 = G3; + Register G4_t2 = G4; + assert_different_registers(G5_klass, G1_obj_size, G3_t1, G4_t2); + + // Push a frame since we may do dtrace notification for the + // allocation which requires calling out and we don't want + // to stomp the real return address. + __ save_frame(0); + + if (id == fast_new_instance_init_check_id) { + // make sure the klass is initialized + __ ld(G5_klass, instanceKlass::init_state_offset_in_bytes() + sizeof(oopDesc), G3_t1); + __ cmp(G3_t1, instanceKlass::fully_initialized); + __ br(Assembler::notEqual, false, Assembler::pn, slow_path); + __ delayed()->nop(); + } +#ifdef ASSERT + // assert object can be fast path allocated + { + Label ok, not_ok; + __ ld(G5_klass, Klass::layout_helper_offset_in_bytes() + sizeof(oopDesc), G1_obj_size); + __ cmp(G1_obj_size, 0); // make sure it's an instance (LH > 0) + __ br(Assembler::lessEqual, false, Assembler::pn, not_ok); + __ delayed()->nop(); + __ btst(Klass::_lh_instance_slow_path_bit, G1_obj_size); + __ br(Assembler::zero, false, Assembler::pn, ok); + __ delayed()->nop(); + __ bind(not_ok); + __ stop("assert(can be fast path allocated)"); + __ should_not_reach_here(); + __ bind(ok); + } +#endif // ASSERT + // if we got here then the TLAB allocation failed, so try + // refilling the TLAB or allocating directly from eden. + Label retry_tlab, try_eden; + __ tlab_refill(retry_tlab, try_eden, slow_path); // preserves G5_klass + + __ bind(retry_tlab); + + // get the instance size + __ ld(G5_klass, klassOopDesc::header_size() * HeapWordSize + Klass::layout_helper_offset_in_bytes(), G1_obj_size); + __ tlab_allocate(O0_obj, G1_obj_size, 0, G3_t1, slow_path); + __ initialize_object(O0_obj, G5_klass, G1_obj_size, 0, G3_t1, G4_t2); + __ verify_oop(O0_obj); + __ mov(O0, I0); + __ ret(); + __ delayed()->restore(); + + __ bind(try_eden); + // get the instance size + __ ld(G5_klass, klassOopDesc::header_size() * HeapWordSize + Klass::layout_helper_offset_in_bytes(), G1_obj_size); + __ eden_allocate(O0_obj, G1_obj_size, 0, G3_t1, G4_t2, slow_path); + __ initialize_object(O0_obj, G5_klass, G1_obj_size, 0, G3_t1, G4_t2); + __ verify_oop(O0_obj); + __ mov(O0, I0); + __ ret(); + __ delayed()->restore(); + + __ bind(slow_path); + + // pop this frame so generate_stub_call can push it's own + __ restore(); + } + + oop_maps = generate_stub_call(sasm, I0, CAST_FROM_FN_PTR(address, new_instance), G5_klass); + // I0->O0: new instance + } + + break; + +#ifdef TIERED + case counter_overflow_id: + // G4 contains bci + oop_maps = generate_stub_call(sasm, noreg, CAST_FROM_FN_PTR(address, counter_overflow), G4); + break; +#endif // TIERED + + case new_type_array_id: + case new_object_array_id: + { + Register G5_klass = G5; // Incoming + Register G4_length = G4; // Incoming + Register O0_obj = O0; // Outgoing + + Address klass_lh(G5_klass, 0, ((klassOopDesc::header_size() * HeapWordSize) + + Klass::layout_helper_offset_in_bytes())); + assert(Klass::_lh_header_size_shift % BitsPerByte == 0, "bytewise"); + assert(Klass::_lh_header_size_mask == 0xFF, "bytewise"); + // Use this offset to pick out an individual byte of the layout_helper: + const int klass_lh_header_size_offset = ((BytesPerInt - 1) // 3 - 2 selects byte {0,1,0,0} + - Klass::_lh_header_size_shift / BitsPerByte); + + if (id == new_type_array_id) { + __ set_info("new_type_array", dont_gc_arguments); + } else { + __ set_info("new_object_array", dont_gc_arguments); + } + +#ifdef ASSERT + // assert object type is really an array of the proper kind + { + Label ok; + Register G3_t1 = G3; + __ ld(klass_lh, G3_t1); + __ sra(G3_t1, Klass::_lh_array_tag_shift, G3_t1); + int tag = ((id == new_type_array_id) + ? Klass::_lh_array_tag_type_value + : Klass::_lh_array_tag_obj_value); + __ cmp(G3_t1, tag); + __ brx(Assembler::equal, false, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("assert(is an array klass)"); + __ should_not_reach_here(); + __ bind(ok); + } +#endif // ASSERT + + if (UseTLAB && FastTLABRefill) { + Label slow_path; + Register G1_arr_size = G1; + Register G3_t1 = G3; + Register O1_t2 = O1; + assert_different_registers(G5_klass, G4_length, G1_arr_size, G3_t1, O1_t2); + + // check that array length is small enough for fast path + __ set(C1_MacroAssembler::max_array_allocation_length, G3_t1); + __ cmp(G4_length, G3_t1); + __ br(Assembler::greaterUnsigned, false, Assembler::pn, slow_path); + __ delayed()->nop(); + + // if we got here then the TLAB allocation failed, so try + // refilling the TLAB or allocating directly from eden. + Label retry_tlab, try_eden; + __ tlab_refill(retry_tlab, try_eden, slow_path); // preserves G4_length and G5_klass + + __ bind(retry_tlab); + + // get the allocation size: (length << (layout_helper & 0x1F)) + header_size + __ ld(klass_lh, G3_t1); + __ sll(G4_length, G3_t1, G1_arr_size); + __ srl(G3_t1, Klass::_lh_header_size_shift, G3_t1); + __ and3(G3_t1, Klass::_lh_header_size_mask, G3_t1); + __ add(G1_arr_size, G3_t1, G1_arr_size); + __ add(G1_arr_size, MinObjAlignmentInBytesMask, G1_arr_size); // align up + __ and3(G1_arr_size, ~MinObjAlignmentInBytesMask, G1_arr_size); + + __ tlab_allocate(O0_obj, G1_arr_size, 0, G3_t1, slow_path); // preserves G1_arr_size + + __ initialize_header(O0_obj, G5_klass, G4_length, G3_t1, O1_t2); + __ ldub(klass_lh, G3_t1, klass_lh_header_size_offset); + __ sub(G1_arr_size, G3_t1, O1_t2); // body length + __ add(O0_obj, G3_t1, G3_t1); // body start + __ initialize_body(G3_t1, O1_t2); + __ verify_oop(O0_obj); + __ retl(); + __ delayed()->nop(); + + __ bind(try_eden); + // get the allocation size: (length << (layout_helper & 0x1F)) + header_size + __ ld(klass_lh, G3_t1); + __ sll(G4_length, G3_t1, G1_arr_size); + __ srl(G3_t1, Klass::_lh_header_size_shift, G3_t1); + __ and3(G3_t1, Klass::_lh_header_size_mask, G3_t1); + __ add(G1_arr_size, G3_t1, G1_arr_size); + __ add(G1_arr_size, MinObjAlignmentInBytesMask, G1_arr_size); + __ and3(G1_arr_size, ~MinObjAlignmentInBytesMask, G1_arr_size); + + __ eden_allocate(O0_obj, G1_arr_size, 0, G3_t1, O1_t2, slow_path); // preserves G1_arr_size + + __ initialize_header(O0_obj, G5_klass, G4_length, G3_t1, O1_t2); + __ ldub(klass_lh, G3_t1, klass_lh_header_size_offset); + __ sub(G1_arr_size, G3_t1, O1_t2); // body length + __ add(O0_obj, G3_t1, G3_t1); // body start + __ initialize_body(G3_t1, O1_t2); + __ verify_oop(O0_obj); + __ retl(); + __ delayed()->nop(); + + __ bind(slow_path); + } + + if (id == new_type_array_id) { + oop_maps = generate_stub_call(sasm, I0, CAST_FROM_FN_PTR(address, new_type_array), G5_klass, G4_length); + } else { + oop_maps = generate_stub_call(sasm, I0, CAST_FROM_FN_PTR(address, new_object_array), G5_klass, G4_length); + } + // I0 -> O0: new array + } + break; + + case new_multi_array_id: + { // O0: klass + // O1: rank + // O2: address of 1st dimension + __ set_info("new_multi_array", dont_gc_arguments); + oop_maps = generate_stub_call(sasm, I0, CAST_FROM_FN_PTR(address, new_multi_array), I0, I1, I2); + // I0 -> O0: new multi array + } + break; + + case register_finalizer_id: + { + __ set_info("register_finalizer", dont_gc_arguments); + + // load the klass and check the has finalizer flag + Label register_finalizer; + Register t = O1; + __ ld_ptr(O0, oopDesc::klass_offset_in_bytes(), t); + __ ld(t, Klass::access_flags_offset_in_bytes() + sizeof(oopDesc), t); + __ set(JVM_ACC_HAS_FINALIZER, G3); + __ andcc(G3, t, G0); + __ br(Assembler::notZero, false, Assembler::pt, register_finalizer); + __ delayed()->nop(); + + // do a leaf return + __ retl(); + __ delayed()->nop(); + + __ bind(register_finalizer); + OopMap* oop_map = save_live_registers(sasm); + int call_offset = __ call_RT(noreg, noreg, + CAST_FROM_FN_PTR(address, SharedRuntime::register_finalizer), I0); + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, oop_map); + + // Now restore all the live registers + restore_live_registers(sasm); + + __ ret(); + __ delayed()->restore(); + } + break; + + case throw_range_check_failed_id: + { __ set_info("range_check_failed", dont_gc_arguments); // arguments will be discarded + // G4: index + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_range_check_exception), true); + } + break; + + case throw_index_exception_id: + { __ set_info("index_range_check_failed", dont_gc_arguments); // arguments will be discarded + // G4: index + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_index_exception), true); + } + break; + + case throw_div0_exception_id: + { __ set_info("throw_div0_exception", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_div0_exception), false); + } + break; + + case throw_null_pointer_exception_id: + { __ set_info("throw_null_pointer_exception", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_null_pointer_exception), false); + } + break; + + case handle_exception_id: + { + __ set_info("handle_exception", dont_gc_arguments); + // make a frame and preserve the caller's caller-save registers + + oop_maps = new OopMapSet(); + OopMap* oop_map = save_live_registers(sasm); + __ mov(Oexception->after_save(), Oexception); + __ mov(Oissuing_pc->after_save(), Oissuing_pc); + generate_handle_exception(sasm, oop_maps, oop_map); + } + break; + + case unwind_exception_id: + { + // O0: exception + // I7: address of call to this method + + __ set_info("unwind_exception", dont_gc_arguments); + __ mov(Oexception, Oexception->after_save()); + __ add(I7, frame::pc_return_offset, Oissuing_pc->after_save()); + + __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), + Oissuing_pc->after_save()); + __ verify_not_null_oop(Oexception->after_save()); + __ jmp(O0, 0); + __ delayed()->restore(); + } + break; + + case throw_array_store_exception_id: + { + __ set_info("throw_array_store_exception", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_array_store_exception), false); + } + break; + + case throw_class_cast_exception_id: + { + // G4: object + __ set_info("throw_class_cast_exception", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_class_cast_exception), true); + } + break; + + case throw_incompatible_class_change_error_id: + { + __ set_info("throw_incompatible_class_cast_exception", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_incompatible_class_change_error), false); + } + break; + + case slow_subtype_check_id: + { // Support for uint StubRoutine::partial_subtype_check( Klass sub, Klass super ); + // Arguments : + // + // ret : G3 + // sub : G3, argument, destroyed + // super: G1, argument, not changed + // raddr: O7, blown by call + Label loop, miss; + + __ save_frame(0); // Blow no registers! + + __ ld_ptr( G3, sizeof(oopDesc) + Klass::secondary_supers_offset_in_bytes(), L3 ); + __ lduw(L3,arrayOopDesc::length_offset_in_bytes(),L0); // length in l0 + __ add(L3,arrayOopDesc::base_offset_in_bytes(T_OBJECT),L1); // ptr into array + __ clr(L4); // Index + // Load a little early; will load 1 off the end of the array. + // Ok for now; revisit if we have other uses of this routine. + __ ld_ptr(L1,0,L2); // Will load a little early + + // The scan loop + __ bind(loop); + __ add(L1,wordSize,L1); // Bump by OOP size + __ cmp(L4,L0); + __ br(Assembler::equal,false,Assembler::pn,miss); + __ delayed()->inc(L4); // Bump index + __ subcc(L2,G1,L3); // Check for match; zero in L3 for a hit + __ brx( Assembler::notEqual, false, Assembler::pt, loop ); + __ delayed()->ld_ptr(L1,0,L2); // Will load a little early + + // Got a hit; report success; set cache + __ st_ptr( G1, G3, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() ); + + __ mov(1, G3); + __ ret(); // Result in G5 is ok; flags set + __ delayed()->restore(); // free copy or add can go here + + __ bind(miss); + __ mov(0, G3); + __ ret(); // Result in G5 is ok; flags set + __ delayed()->restore(); // free copy or add can go here + } + + case monitorenter_nofpu_id: + case monitorenter_id: + { // G4: object + // G5: lock address + __ set_info("monitorenter", dont_gc_arguments); + + int save_fpu_registers = (id == monitorenter_id); + // make a frame and preserve the caller's caller-save registers + OopMap* oop_map = save_live_registers(sasm, save_fpu_registers); + + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, monitorenter), G4, G5); + + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, oop_map); + restore_live_registers(sasm, save_fpu_registers); + + __ ret(); + __ delayed()->restore(); + } + break; + + case monitorexit_nofpu_id: + case monitorexit_id: + { // G4: lock address + // note: really a leaf routine but must setup last java sp + // => use call_RT for now (speed can be improved by + // doing last java sp setup manually) + __ set_info("monitorexit", dont_gc_arguments); + + int save_fpu_registers = (id == monitorexit_id); + // make a frame and preserve the caller's caller-save registers + OopMap* oop_map = save_live_registers(sasm, save_fpu_registers); + + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, monitorexit), G4); + + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, oop_map); + restore_live_registers(sasm, save_fpu_registers); + + __ ret(); + __ delayed()->restore(); + + } + break; + + case access_field_patching_id: + { __ set_info("access_field_patching", dont_gc_arguments); + oop_maps = generate_patching(sasm, CAST_FROM_FN_PTR(address, access_field_patching)); + } + break; + + case load_klass_patching_id: + { __ set_info("load_klass_patching", dont_gc_arguments); + oop_maps = generate_patching(sasm, CAST_FROM_FN_PTR(address, move_klass_patching)); + } + break; + + case jvmti_exception_throw_id: + { // Oexception : exception + __ set_info("jvmti_exception_throw", dont_gc_arguments); + oop_maps = generate_stub_call(sasm, noreg, CAST_FROM_FN_PTR(address, Runtime1::post_jvmti_exception_throw), I0); + } + break; + + case dtrace_object_alloc_id: + { // O0: object + __ set_info("dtrace_object_alloc", dont_gc_arguments); + // we can't gc here so skip the oopmap but make sure that all + // the live registers get saved. + save_live_registers(sasm); + + __ save_thread(L7_thread_cache); + __ call(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), + relocInfo::runtime_call_type); + __ delayed()->mov(I0, O0); + __ restore_thread(L7_thread_cache); + + restore_live_registers(sasm); + __ ret(); + __ delayed()->restore(); + } + break; + + default: + { __ set_info("unimplemented entry", dont_gc_arguments); + __ save_frame(0); + __ set((int)id, O1); + __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, unimplemented_entry), O1); + __ should_not_reach_here(); + } + break; + } + return oop_maps; +} + + +void Runtime1::generate_handle_exception(StubAssembler* sasm, OopMapSet* oop_maps, OopMap* oop_map, bool) { + Label no_deopt; + Label no_handler; + + __ verify_not_null_oop(Oexception); + + // save the exception and issuing pc in the thread + __ st_ptr(Oexception, G2_thread, in_bytes(JavaThread::exception_oop_offset())); + __ st_ptr(Oissuing_pc, G2_thread, in_bytes(JavaThread::exception_pc_offset())); + + // save the real return address and use the throwing pc as the return address to lookup (has bci & oop map) + __ mov(I7, L0); + __ mov(Oissuing_pc, I7); + __ sub(I7, frame::pc_return_offset, I7); + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, exception_handler_for_pc)); + + // Note: if nmethod has been deoptimized then regardless of + // whether it had a handler or not we will deoptimize + // by entering the deopt blob with a pending exception. + + __ tst(O0); + __ br(Assembler::zero, false, Assembler::pn, no_handler); + __ delayed()->nop(); + + // restore the registers that were saved at the beginning and jump to the exception handler. + restore_live_registers(sasm); + + __ jmp(O0, 0); + __ delayed()->restore(); + + __ bind(no_handler); + __ mov(L0, I7); // restore return address + + // restore exception oop + __ ld_ptr(G2_thread, in_bytes(JavaThread::exception_oop_offset()), Oexception->after_save()); + __ st_ptr(G0, G2_thread, in_bytes(JavaThread::exception_oop_offset())); + + __ restore(); + + Address exc(G4, Runtime1::entry_for(Runtime1::unwind_exception_id)); + __ jump_to(exc, 0); + __ delayed()->nop(); + + + oop_maps->add_gc_map(call_offset, oop_map); +} + + +#undef __ + +#define __ masm-> diff --git a/hotspot/src/cpu/sparc/vm/c1_globals_sparc.hpp b/hotspot/src/cpu/sparc/vm/c1_globals_sparc.hpp new file mode 100644 index 00000000000..cb2bab6ea93 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c1_globals_sparc.hpp @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for platform dependent flags used by the client compiler. +// (see c1_globals.hpp) +// +#ifndef TIERED +define_pd_global(bool, BackgroundCompilation, true ); +define_pd_global(bool, CICompileOSR, true ); +define_pd_global(bool, InlineIntrinsics, true ); +define_pd_global(bool, PreferInterpreterNativeStubs, false); +define_pd_global(bool, ProfileTraps, false); +define_pd_global(bool, UseOnStackReplacement, true ); +define_pd_global(bool, TieredCompilation, false); +define_pd_global(intx, CompileThreshold, 1000 ); // Design center runs on 1.3.1 +define_pd_global(intx, Tier2CompileThreshold, 1500 ); +define_pd_global(intx, Tier3CompileThreshold, 2000 ); +define_pd_global(intx, Tier4CompileThreshold, 2500 ); + +define_pd_global(intx, BackEdgeThreshold, 100000); +define_pd_global(intx, Tier2BackEdgeThreshold, 100000); +define_pd_global(intx, Tier3BackEdgeThreshold, 100000); +define_pd_global(intx, Tier4BackEdgeThreshold, 100000); + +define_pd_global(intx, OnStackReplacePercentage, 1400 ); +define_pd_global(bool, UseTLAB, true ); +define_pd_global(bool, ProfileInterpreter, false); +define_pd_global(intx, FreqInlineSize, 325 ); +define_pd_global(intx, NewRatio, 8 ); // Design center runs on 1.3.1 +define_pd_global(bool, ResizeTLAB, true ); +define_pd_global(intx, ReservedCodeCacheSize, 32*M ); +define_pd_global(intx, CodeCacheExpansionSize, 32*K ); +define_pd_global(uintx,CodeCacheMinBlockLength, 1); +define_pd_global(uintx, PermSize, 12*M ); +define_pd_global(uintx, MaxPermSize, 64*M ); +define_pd_global(bool, NeverActAsServerClassMachine, true); +define_pd_global(intx, NewSizeThreadIncrease, 16*K ); +define_pd_global(uintx, DefaultMaxRAM, 1*G); +define_pd_global(intx, InitialCodeCacheSize, 160*K); +#endif // TIERED + +define_pd_global(bool, UseTypeProfile, false); +define_pd_global(bool, RoundFPResults, false); + + +define_pd_global(bool, LIRFillDelaySlots, true); +define_pd_global(bool, OptimizeSinglePrecision, false); +define_pd_global(bool, CSEArrayLength, true); +define_pd_global(bool, TwoOperandLIRForm, false); + + +define_pd_global(intx, SafepointPollOffset, 0); diff --git a/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp b/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp new file mode 100644 index 00000000000..92f69be26bb --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp @@ -0,0 +1,107 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Sets the default values for platform dependent flags used by the server compiler. +// (see c2_globals.hpp). Alpha-sorted. + +define_pd_global(bool, BackgroundCompilation, true); +define_pd_global(bool, CICompileOSR, true); +define_pd_global(bool, InlineIntrinsics, false); +define_pd_global(bool, PreferInterpreterNativeStubs, false); +define_pd_global(bool, ProfileTraps, true); +define_pd_global(bool, UseOnStackReplacement, true); +#ifdef CC_INTERP +define_pd_global(bool, ProfileInterpreter, false); +#else +define_pd_global(bool, ProfileInterpreter, true); +#endif // CC_INTERP +define_pd_global(bool, TieredCompilation, false); +#ifdef TIERED +define_pd_global(intx, CompileThreshold, 1000); +define_pd_global(intx, BackEdgeThreshold, 14000); +#else +define_pd_global(intx, CompileThreshold, 10000); +define_pd_global(intx, BackEdgeThreshold, 140000); +#endif // TIERED + +define_pd_global(intx, Tier2CompileThreshold, 10000); // unused level +define_pd_global(intx, Tier3CompileThreshold, 10000); +define_pd_global(intx, Tier4CompileThreshold, 40000); + +define_pd_global(intx, Tier2BackEdgeThreshold, 100000); +define_pd_global(intx, Tier3BackEdgeThreshold, 100000); +define_pd_global(intx, Tier4BackEdgeThreshold, 100000); + +define_pd_global(intx, OnStackReplacePercentage, 140); +define_pd_global(intx, ConditionalMoveLimit, 4); +define_pd_global(intx, FLOATPRESSURE, 52); // C2 on V9 gets to use all the float/double registers +define_pd_global(intx, FreqInlineSize, 175); +define_pd_global(intx, INTPRESSURE, 48); // large register set +define_pd_global(intx, InteriorEntryAlignment, 16); // = CodeEntryAlignment +define_pd_global(intx, NewRatio, 2); +define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K)); +// The default setting 16/16 seems to work best. +// (For _228_jack 16/16 is 2% better than 4/4, 16/4, 32/32, 32/16, or 16/32.) +define_pd_global(intx, OptoLoopAlignment, 16); // = 4*wordSize +define_pd_global(intx, RegisterCostAreaRatio, 12000); +define_pd_global(bool, UseTLAB, true); +define_pd_global(bool, ResizeTLAB, true); +define_pd_global(intx, LoopUnrollLimit, 60); // Design center runs on 1.3.1 + +// Peephole and CISC spilling both break the graph, and so makes the +// scheduler sick. +define_pd_global(bool, OptoPeephole, false); +define_pd_global(bool, UseCISCSpill, false); +define_pd_global(bool, OptoBundling, false); +define_pd_global(bool, OptoScheduling, true); + +#ifdef _LP64 +// We need to make sure that all generated code is within +// 2 gigs of the libjvm.so runtime routines so we can use +// the faster "call" instruction rather than the expensive +// sequence of instructions to load a 64 bit pointer. +// +// InitialCodeCacheSize derived from specjbb2000 run. +define_pd_global(intx, InitialCodeCacheSize, 2048*K); // Integral multiple of CodeCacheExpansionSize +define_pd_global(intx, ReservedCodeCacheSize, 48*M); +define_pd_global(intx, CodeCacheExpansionSize, 64*K); + +// Ergonomics related flags +define_pd_global(uintx, DefaultMaxRAM, 32*G); +#else +// InitialCodeCacheSize derived from specjbb2000 run. +define_pd_global(intx, InitialCodeCacheSize, 1536*K); // Integral multiple of CodeCacheExpansionSize +define_pd_global(intx, ReservedCodeCacheSize, 32*M); +define_pd_global(intx, CodeCacheExpansionSize, 32*K); +// Ergonomics related flags +define_pd_global(uintx, DefaultMaxRAM, 1*G); +#endif +define_pd_global(uintx,CodeCacheMinBlockLength, 4); + +// Heap related flags +define_pd_global(uintx, PermSize, ScaleForWordSize(16*M)); +define_pd_global(uintx, MaxPermSize, ScaleForWordSize(64*M)); + +// Ergonomics related flags +define_pd_global(bool, NeverActAsServerClassMachine, false); diff --git a/hotspot/src/cpu/sparc/vm/c2_init_sparc.cpp b/hotspot/src/cpu/sparc/vm/c2_init_sparc.cpp new file mode 100644 index 00000000000..ed0b08dc7ef --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/c2_init_sparc.cpp @@ -0,0 +1,33 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c2_init_sparc.cpp.incl" + +// processor dependent initialization for sparc + +void Compile::pd_compiler2_init() { + guarantee(CodeEntryAlignment >= InteriorEntryAlignment, "" ); + guarantee( VM_Version::v9_instructions_work(), "Server compiler does not run on V8 systems" ); +} diff --git a/hotspot/src/cpu/sparc/vm/codeBuffer_sparc.hpp b/hotspot/src/cpu/sparc/vm/codeBuffer_sparc.hpp new file mode 100644 index 00000000000..1bfb00d1304 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/codeBuffer_sparc.hpp @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +private: + void pd_initialize() {} + +public: + void flush_bundle(bool start_new_bundle) {} + + // Heuristic for pre-packing the pt/pn bit of a predicted branch. + bool is_backward_branch(Label& L) { + return L.is_bound() && code_end() <= locator_address(L.loc()); + } diff --git a/hotspot/src/cpu/sparc/vm/copy_sparc.hpp b/hotspot/src/cpu/sparc/vm/copy_sparc.hpp new file mode 100644 index 00000000000..4b8d122a799 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/copy_sparc.hpp @@ -0,0 +1,192 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline functions for memory copy and fill. + +static void pd_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + (void)memmove(to, from, count * HeapWordSize); +} + +static void pd_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { + switch (count) { + case 8: to[7] = from[7]; + case 7: to[6] = from[6]; + case 6: to[5] = from[5]; + case 5: to[4] = from[4]; + case 4: to[3] = from[3]; + case 3: to[2] = from[2]; + case 2: to[1] = from[1]; + case 1: to[0] = from[0]; + case 0: break; + default: (void)memcpy(to, from, count * HeapWordSize); + break; + } +} + +static void pd_disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count) { + switch (count) { + case 8: to[7] = from[7]; + case 7: to[6] = from[6]; + case 6: to[5] = from[5]; + case 5: to[4] = from[4]; + case 4: to[3] = from[3]; + case 3: to[2] = from[2]; + case 2: to[1] = from[1]; + case 1: to[0] = from[0]; + case 0: break; + default: while (count-- > 0) { + *to++ = *from++; + } + break; + } +} + +static void pd_aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + (void)memmove(to, from, count * HeapWordSize); +} + +static void pd_aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { + pd_disjoint_words(from, to, count); +} + +static void pd_conjoint_bytes(void* from, void* to, size_t count) { + (void)memmove(to, from, count); +} + +static void pd_conjoint_bytes_atomic(void* from, void* to, size_t count) { + (void)memmove(to, from, count); +} + +static void pd_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { + // FIXME + (void)memmove(to, from, count << LogBytesPerShort); +} + +static void pd_conjoint_jints_atomic(jint* from, jint* to, size_t count) { + // FIXME + (void)memmove(to, from, count << LogBytesPerInt); +} + +static void pd_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { +#ifdef _LP64 + assert(BytesPerLong == BytesPerOop, "jlongs and oops must be the same size"); + pd_conjoint_oops_atomic((oop*)from, (oop*)to, count); +#else + // Guarantee use of ldd/std via some asm code, because compiler won't. + // See solaris_sparc.il. + _Copy_conjoint_jlongs_atomic(from, to, count); +#endif +} + +static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) { + // Do better than this: inline memmove body NEEDS CLEANUP + if (from > to) { + while (count-- > 0) { + // Copy forwards + *to++ = *from++; + } + } else { + from += count - 1; + to += count - 1; + while (count-- > 0) { + // Copy backwards + *to-- = *from--; + } + } +} + +static void pd_arrayof_conjoint_bytes(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_bytes_atomic(from, to, count); +} + +static void pd_arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_jshorts_atomic((jshort*)from, (jshort*)to, count); +} + +static void pd_arrayof_conjoint_jints(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_jints_atomic((jint*)from, (jint*)to, count); +} + +static void pd_arrayof_conjoint_jlongs(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_jlongs_atomic((jlong*)from, (jlong*)to, count); +} + +static void pd_arrayof_conjoint_oops(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_oops_atomic((oop*)from, (oop*)to, count); +} + +static void pd_fill_to_words(HeapWord* tohw, size_t count, juint value) { +#if 0 + if (HeapWordsPerLong == 1 || + (HeapWordsPerLong == 2 && + mask_bits((uintptr_t)tohw, right_n_bits(LogBytesPerLong)) == 0 && + ((count & 1) ? false : count >>= 1))) { + julong* to = (julong*)tohw; + julong v = ((julong)value << 32) | value; + while (count-- > 0) { + *to++ = v; + } + } else { +#endif + juint* to = (juint*)tohw; + count *= HeapWordSize / BytesPerInt; + while (count-- > 0) { + *to++ = value; + } + // } +} + +static void pd_fill_to_aligned_words(HeapWord* tohw, size_t count, juint value) { + assert(MinObjAlignmentInBytes == BytesPerLong, "need alternate implementation"); + + julong* to = (julong*)tohw; + julong v = ((julong)value << 32) | value; + // If count is odd, odd will be equal to 1 on 32-bit platform + // and be equal to 0 on 64-bit platform. + size_t odd = count % (BytesPerLong / HeapWordSize) ; + + size_t aligned_count = align_object_size(count - odd) / HeapWordsPerLong; + julong* end = ((julong*)tohw) + aligned_count - 1; + while (to <= end) { + DEBUG_ONLY(count -= BytesPerLong / HeapWordSize ;) + *to++ = v; + } + assert(count == odd, "bad bounds on loop filling to aligned words"); + if (odd) { + *((juint*)to) = value; + + } +} + +static void pd_fill_to_bytes(void* to, size_t count, jubyte value) { + (void)memset(to, value, count); +} + +static void pd_zero_to_words(HeapWord* tohw, size_t count) { + pd_fill_to_words(tohw, count, 0); +} + +static void pd_zero_to_bytes(void* to, size_t count) { + (void)memset(to, 0, count); +} diff --git a/hotspot/src/cpu/sparc/vm/cppInterpreterGenerator_sparc.hpp b/hotspot/src/cpu/sparc/vm/cppInterpreterGenerator_sparc.hpp new file mode 100644 index 00000000000..65e38818131 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/cppInterpreterGenerator_sparc.hpp @@ -0,0 +1,34 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + static address frame_manager_return; + static address frame_manager_sync_return; + + + void generate_more_monitors(); + void generate_deopt_handling(); + void adjust_callers_stack(Register args); + void generate_compute_interpreter_state(const Register state, + const Register prev_state, + bool native); diff --git a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp new file mode 100644 index 00000000000..b94b82f59a3 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp @@ -0,0 +1,2243 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_cppInterpreter_sparc.cpp.incl" + +#ifdef CC_INTERP + +// Routine exists to make tracebacks look decent in debugger +// while "shadow" interpreter frames are on stack. It is also +// used to distinguish interpreter frames. + +extern "C" void RecursiveInterpreterActivation(interpreterState istate) { + ShouldNotReachHere(); +} + +bool CppInterpreter::contains(address pc) { + return ( _code->contains(pc) || + ( pc == (CAST_FROM_FN_PTR(address, RecursiveInterpreterActivation) + frame::pc_return_offset))); +} + +#define STATE(field_name) Lstate, in_bytes(byte_offset_of(BytecodeInterpreter, field_name)) +#define __ _masm-> + +Label frame_manager_entry; +Label fast_accessor_slow_entry_path; // fast accessor methods need to be able to jmp to unsynchronized + // c++ interpreter entry point this holds that entry point label. + +static address unctrap_frame_manager_entry = NULL; + +static address interpreter_return_address = NULL; +static address deopt_frame_manager_return_atos = NULL; +static address deopt_frame_manager_return_btos = NULL; +static address deopt_frame_manager_return_itos = NULL; +static address deopt_frame_manager_return_ltos = NULL; +static address deopt_frame_manager_return_ftos = NULL; +static address deopt_frame_manager_return_dtos = NULL; +static address deopt_frame_manager_return_vtos = NULL; + +const Register prevState = G1_scratch; + +void InterpreterGenerator::save_native_result(void) { + // result potentially in O0/O1: save it across calls + __ stf(FloatRegisterImpl::D, F0, STATE(_native_fresult)); +#ifdef _LP64 + __ stx(O0, STATE(_native_lresult)); +#else + __ std(O0, STATE(_native_lresult)); +#endif +} + +void InterpreterGenerator::restore_native_result(void) { + + // Restore any method result value + __ ldf(FloatRegisterImpl::D, STATE(_native_fresult), F0); +#ifdef _LP64 + __ ldx(STATE(_native_lresult), O0); +#else + __ ldd(STATE(_native_lresult), O0); +#endif +} + +// A result handler converts/unboxes a native call result into +// a java interpreter/compiler result. The current frame is an +// interpreter frame. The activation frame unwind code must be +// consistent with that of TemplateTable::_return(...). In the +// case of native methods, the caller's SP was not modified. +address CppInterpreterGenerator::generate_result_handler_for(BasicType type) { + address entry = __ pc(); + Register Itos_i = Otos_i ->after_save(); + Register Itos_l = Otos_l ->after_save(); + Register Itos_l1 = Otos_l1->after_save(); + Register Itos_l2 = Otos_l2->after_save(); + switch (type) { + case T_BOOLEAN: __ subcc(G0, O0, G0); __ addc(G0, 0, Itos_i); break; // !0 => true; 0 => false + case T_CHAR : __ sll(O0, 16, O0); __ srl(O0, 16, Itos_i); break; // cannot use and3, 0xFFFF too big as immediate value! + case T_BYTE : __ sll(O0, 24, O0); __ sra(O0, 24, Itos_i); break; + case T_SHORT : __ sll(O0, 16, O0); __ sra(O0, 16, Itos_i); break; + case T_LONG : +#ifndef _LP64 + __ mov(O1, Itos_l2); // move other half of long +#endif // ifdef or no ifdef, fall through to the T_INT case + case T_INT : __ mov(O0, Itos_i); break; + case T_VOID : /* nothing to do */ break; + case T_FLOAT : assert(F0 == Ftos_f, "fix this code" ); break; + case T_DOUBLE : assert(F0 == Ftos_d, "fix this code" ); break; + case T_OBJECT : + __ ld_ptr(STATE(_oop_temp), Itos_i); + __ verify_oop(Itos_i); + break; + default : ShouldNotReachHere(); + } + __ ret(); // return from interpreter activation + __ delayed()->restore(I5_savedSP, G0, SP); // remove interpreter frame + NOT_PRODUCT(__ emit_long(0);) // marker for disassembly + return entry; +} + +// tosca based result to c++ interpreter stack based result. +// Result goes to address in L1_scratch + +address CppInterpreterGenerator::generate_tosca_to_stack_converter(BasicType type) { + // A result is in the native abi result register from a native method call. + // We need to return this result to the interpreter by pushing the result on the interpreter's + // stack. This is relatively simple the destination is in L1_scratch + // i.e. L1_scratch is the first free element on the stack. If we "push" a return value we must + // adjust L1_scratch + address entry = __ pc(); + switch (type) { + case T_BOOLEAN: + // !0 => true; 0 => false + __ subcc(G0, O0, G0); + __ addc(G0, 0, O0); + __ st(O0, L1_scratch, 0); + __ sub(L1_scratch, wordSize, L1_scratch); + break; + + // cannot use and3, 0xFFFF too big as immediate value! + case T_CHAR : + __ sll(O0, 16, O0); + __ srl(O0, 16, O0); + __ st(O0, L1_scratch, 0); + __ sub(L1_scratch, wordSize, L1_scratch); + break; + + case T_BYTE : + __ sll(O0, 24, O0); + __ sra(O0, 24, O0); + __ st(O0, L1_scratch, 0); + __ sub(L1_scratch, wordSize, L1_scratch); + break; + + case T_SHORT : + __ sll(O0, 16, O0); + __ sra(O0, 16, O0); + __ st(O0, L1_scratch, 0); + __ sub(L1_scratch, wordSize, L1_scratch); + break; + case T_LONG : +#ifndef _LP64 +#if !defined(_LP64) && defined(COMPILER2) + // All return values are where we want them, except for Longs. C2 returns + // longs in G1 in the 32-bit build whereas the interpreter wants them in O0/O1. + // Since the interpreter will return longs in G1 and O0/O1 in the 32bit + // build even if we are returning from interpreted we just do a little + // stupid shuffing. + // Note: I tried to make c2 return longs in O0/O1 and G1 so we wouldn't have to + // do this here. Unfortunately if we did a rethrow we'd see an machepilog node + // first which would move g1 -> O0/O1 and destroy the exception we were throwing. + __ stx(G1, L1_scratch, -wordSize); +#else + // native result is in O0, O1 + __ st(O1, L1_scratch, 0); // Low order + __ st(O0, L1_scratch, -wordSize); // High order +#endif /* !_LP64 && COMPILER2 */ +#else + __ stx(O0, L1_scratch, 0); +__ breakpoint_trap(); +#endif + __ sub(L1_scratch, 2*wordSize, L1_scratch); + break; + + case T_INT : + __ st(O0, L1_scratch, 0); + __ sub(L1_scratch, wordSize, L1_scratch); + break; + + case T_VOID : /* nothing to do */ + break; + + case T_FLOAT : + __ stf(FloatRegisterImpl::S, F0, L1_scratch, 0); + __ sub(L1_scratch, wordSize, L1_scratch); + break; + + case T_DOUBLE : + // Every stack slot is aligned on 64 bit, However is this + // the correct stack slot on 64bit?? QQQ + __ stf(FloatRegisterImpl::D, F0, L1_scratch, -wordSize); + __ sub(L1_scratch, 2*wordSize, L1_scratch); + break; + case T_OBJECT : + __ verify_oop(O0); + __ st_ptr(O0, L1_scratch, 0); + __ sub(L1_scratch, wordSize, L1_scratch); + break; + default : ShouldNotReachHere(); + } + __ retl(); // return from interpreter activation + __ delayed()->nop(); // schedule this better + NOT_PRODUCT(__ emit_long(0);) // marker for disassembly + return entry; +} + +address CppInterpreterGenerator::generate_stack_to_stack_converter(BasicType type) { + // A result is in the java expression stack of the interpreted method that has just + // returned. Place this result on the java expression stack of the caller. + // + // The current interpreter activation in Lstate is for the method just returning its + // result. So we know that the result of this method is on the top of the current + // execution stack (which is pre-pushed) and will be return to the top of the caller + // stack. The top of the callers stack is the bottom of the locals of the current + // activation. + // Because of the way activation are managed by the frame manager the value of esp is + // below both the stack top of the current activation and naturally the stack top + // of the calling activation. This enable this routine to leave the return address + // to the frame manager on the stack and do a vanilla return. + // + // On entry: O0 - points to source (callee stack top) + // O1 - points to destination (caller stack top [i.e. free location]) + // destroys O2, O3 + // + + address entry = __ pc(); + switch (type) { + case T_VOID: break; + break; + case T_FLOAT : + __ breakpoint_trap(Assembler::zero); + case T_BOOLEAN: + case T_CHAR : + case T_BYTE : + case T_SHORT : + case T_INT : + // 1 word result + __ ld(O0, 0, O2); + __ st(O2, O1, 0); + __ sub(O1, wordSize, O1); + break; + case T_DOUBLE : + case T_LONG : + // return top two words on current expression stack to caller's expression stack + // The caller's expression stack is adjacent to the current frame manager's intepretState + // except we allocated one extra word for this intepretState so we won't overwrite it + // when we return a two word result. +#ifdef _LP64 +__ breakpoint_trap(); + // Hmm now that longs are in one entry should "_ptr" really be "x"? + __ ld_ptr(O0, 0, O2); + __ ld_ptr(O0, wordSize, O3); + __ st_ptr(O3, O1, 0); + __ st_ptr(O2, O1, -wordSize); +#else + __ ld(O0, 0, O2); + __ ld(O0, wordSize, O3); + __ st(O3, O1, 0); + __ st(O2, O1, -wordSize); +#endif + __ sub(O1, 2*wordSize, O1); + break; + case T_OBJECT : + __ ld_ptr(O0, 0, O2); + __ verify_oop(O2); // verify it + __ st_ptr(O2, O1, 0); + __ sub(O1, wordSize, O1); + break; + default : ShouldNotReachHere(); + } + __ retl(); + __ delayed()->nop(); // QQ schedule this better + return entry; +} + +address CppInterpreterGenerator::generate_stack_to_native_abi_converter(BasicType type) { + // A result is in the java expression stack of the interpreted method that has just + // returned. Place this result in the native abi that the caller expects. + // We are in a new frame registers we set must be in caller (i.e. callstub) frame. + // + // Similar to generate_stack_to_stack_converter above. Called at a similar time from the + // frame manager execept in this situation the caller is native code (c1/c2/call_stub) + // and so rather than return result onto caller's java expression stack we return the + // result in the expected location based on the native abi. + // On entry: O0 - source (stack top) + // On exit result in expected output register + // QQQ schedule this better + + address entry = __ pc(); + switch (type) { + case T_VOID: break; + break; + case T_FLOAT : + __ ldf(FloatRegisterImpl::S, O0, 0, F0); + break; + case T_BOOLEAN: + case T_CHAR : + case T_BYTE : + case T_SHORT : + case T_INT : + // 1 word result + __ ld(O0, 0, O0->after_save()); + break; + case T_DOUBLE : + __ ldf(FloatRegisterImpl::D, O0, 0, F0); + break; + case T_LONG : + // return top two words on current expression stack to caller's expression stack + // The caller's expression stack is adjacent to the current frame manager's interpretState + // except we allocated one extra word for this intepretState so we won't overwrite it + // when we return a two word result. +#ifdef _LP64 +__ breakpoint_trap(); + // Hmm now that longs are in one entry should "_ptr" really be "x"? + __ ld_ptr(O0, 0, O0->after_save()); + __ ld_ptr(O0, wordSize, O1->after_save()); +#else + __ ld(O0, wordSize, O1->after_save()); + __ ld(O0, 0, O0->after_save()); +#endif +#if defined(COMPILER2) && !defined(_LP64) + // C2 expects long results in G1 we can't tell if we're returning to interpreted + // or compiled so just be safe use G1 and O0/O1 + + // Shift bits into high (msb) of G1 + __ sllx(Otos_l1->after_save(), 32, G1); + // Zero extend low bits + __ srl (Otos_l2->after_save(), 0, Otos_l2->after_save()); + __ or3 (Otos_l2->after_save(), G1, G1); +#endif /* COMPILER2 */ + break; + case T_OBJECT : + __ ld_ptr(O0, 0, O0->after_save()); + __ verify_oop(O0->after_save()); // verify it + break; + default : ShouldNotReachHere(); + } + __ retl(); + __ delayed()->nop(); + return entry; +} + +address CppInterpreter::return_entry(TosState state, int length) { + // make it look good in the debugger + return CAST_FROM_FN_PTR(address, RecursiveInterpreterActivation) + frame::pc_return_offset; +} + +address CppInterpreter::deopt_entry(TosState state, int length) { + address ret = NULL; + if (length != 0) { + switch (state) { + case atos: ret = deopt_frame_manager_return_atos; break; + case btos: ret = deopt_frame_manager_return_btos; break; + case ctos: + case stos: + case itos: ret = deopt_frame_manager_return_itos; break; + case ltos: ret = deopt_frame_manager_return_ltos; break; + case ftos: ret = deopt_frame_manager_return_ftos; break; + case dtos: ret = deopt_frame_manager_return_dtos; break; + case vtos: ret = deopt_frame_manager_return_vtos; break; + } + } else { + ret = unctrap_frame_manager_entry; // re-execute the bytecode ( e.g. uncommon trap) + } + assert(ret != NULL, "Not initialized"); + return ret; +} + +// +// Helpers for commoning out cases in the various type of method entries. +// + +// increment invocation count & check for overflow +// +// Note: checking for negative value instead of overflow +// so we have a 'sticky' overflow test +// +// Lmethod: method +// ??: invocation counter +// +void InterpreterGenerator::generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue) { + // Update standard invocation counters + __ increment_invocation_counter(O0, G3_scratch); + if (ProfileInterpreter) { // %%% Merge this into methodDataOop + __ ld_ptr(STATE(_method), G3_scratch); + Address interpreter_invocation_counter(G3_scratch, 0, in_bytes(methodOopDesc::interpreter_invocation_counter_offset())); + __ ld(interpreter_invocation_counter, G3_scratch); + __ inc(G3_scratch); + __ st(G3_scratch, interpreter_invocation_counter); + } + + Address invocation_limit(G3_scratch, (address)&InvocationCounter::InterpreterInvocationLimit); + __ sethi(invocation_limit); + __ ld(invocation_limit, G3_scratch); + __ cmp(O0, G3_scratch); + __ br(Assembler::greaterEqualUnsigned, false, Assembler::pn, *overflow); + __ delayed()->nop(); + +} + +address InterpreterGenerator::generate_empty_entry(void) { + + // A method that does nothing but return... + + address entry = __ pc(); + Label slow_path; + + __ verify_oop(G5_method); + + // do nothing for empty methods (do not even increment invocation counter) + if ( UseFastEmptyMethods) { + // If we need a safepoint check, generate full interpreter entry. + Address sync_state(G3_scratch, SafepointSynchronize::address_of_state()); + __ load_contents(sync_state, G3_scratch); + __ cmp(G3_scratch, SafepointSynchronize::_not_synchronized); + __ br(Assembler::notEqual, false, Assembler::pn, frame_manager_entry); + __ delayed()->nop(); + + // Code: _return + __ retl(); + __ delayed()->mov(O5_savedSP, SP); + return entry; + } + return NULL; +} + +// Call an accessor method (assuming it is resolved, otherwise drop into +// vanilla (slow path) entry + +// Generates code to elide accessor methods +// Uses G3_scratch and G1_scratch as scratch +address InterpreterGenerator::generate_accessor_entry(void) { + + // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites thereof; + // parameter size = 1 + // Note: We can only use this code if the getfield has been resolved + // and if we don't have a null-pointer exception => check for + // these conditions first and use slow path if necessary. + address entry = __ pc(); + Label slow_path; + + if ( UseFastAccessorMethods) { + // Check if we need to reach a safepoint and generate full interpreter + // frame if so. + Address sync_state(G3_scratch, SafepointSynchronize::address_of_state()); + __ load_contents(sync_state, G3_scratch); + __ cmp(G3_scratch, SafepointSynchronize::_not_synchronized); + __ br(Assembler::notEqual, false, Assembler::pn, slow_path); + __ delayed()->nop(); + + // Check if local 0 != NULL + __ ld_ptr(Gargs, G0, Otos_i ); // get local 0 + __ tst(Otos_i); // check if local 0 == NULL and go the slow path + __ brx(Assembler::zero, false, Assembler::pn, slow_path); + __ delayed()->nop(); + + + // read first instruction word and extract bytecode @ 1 and index @ 2 + // get first 4 bytes of the bytecodes (big endian!) + __ ld_ptr(Address(G5_method, 0, in_bytes(methodOopDesc::const_offset())), G1_scratch); + __ ld(Address(G1_scratch, 0, in_bytes(constMethodOopDesc::codes_offset())), G1_scratch); + + // move index @ 2 far left then to the right most two bytes. + __ sll(G1_scratch, 2*BitsPerByte, G1_scratch); + __ srl(G1_scratch, 2*BitsPerByte - exact_log2(in_words( + ConstantPoolCacheEntry::size()) * BytesPerWord), G1_scratch); + + // get constant pool cache + __ ld_ptr(G5_method, in_bytes(methodOopDesc::constants_offset()), G3_scratch); + __ ld_ptr(G3_scratch, constantPoolOopDesc::cache_offset_in_bytes(), G3_scratch); + + // get specific constant pool cache entry + __ add(G3_scratch, G1_scratch, G3_scratch); + + // Check the constant Pool cache entry to see if it has been resolved. + // If not, need the slow path. + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + __ ld_ptr(G3_scratch, in_bytes(cp_base_offset + ConstantPoolCacheEntry::indices_offset()), G1_scratch); + __ srl(G1_scratch, 2*BitsPerByte, G1_scratch); + __ and3(G1_scratch, 0xFF, G1_scratch); + __ cmp(G1_scratch, Bytecodes::_getfield); + __ br(Assembler::notEqual, false, Assembler::pn, slow_path); + __ delayed()->nop(); + + // Get the type and return field offset from the constant pool cache + __ ld_ptr(G3_scratch, in_bytes(cp_base_offset + ConstantPoolCacheEntry::flags_offset()), G1_scratch); + __ ld_ptr(G3_scratch, in_bytes(cp_base_offset + ConstantPoolCacheEntry::f2_offset()), G3_scratch); + + Label xreturn_path; + // Need to differentiate between igetfield, agetfield, bgetfield etc. + // because they are different sizes. + // Get the type from the constant pool cache + __ srl(G1_scratch, ConstantPoolCacheEntry::tosBits, G1_scratch); + // Make sure we don't need to mask G1_scratch for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ cmp(G1_scratch, atos ); + __ br(Assembler::equal, true, Assembler::pt, xreturn_path); + __ delayed()->ld_ptr(Otos_i, G3_scratch, Otos_i); + __ cmp(G1_scratch, itos); + __ br(Assembler::equal, true, Assembler::pt, xreturn_path); + __ delayed()->ld(Otos_i, G3_scratch, Otos_i); + __ cmp(G1_scratch, stos); + __ br(Assembler::equal, true, Assembler::pt, xreturn_path); + __ delayed()->ldsh(Otos_i, G3_scratch, Otos_i); + __ cmp(G1_scratch, ctos); + __ br(Assembler::equal, true, Assembler::pt, xreturn_path); + __ delayed()->lduh(Otos_i, G3_scratch, Otos_i); +#ifdef ASSERT + __ cmp(G1_scratch, btos); + __ br(Assembler::equal, true, Assembler::pt, xreturn_path); + __ delayed()->ldsb(Otos_i, G3_scratch, Otos_i); + __ should_not_reach_here(); +#endif + __ ldsb(Otos_i, G3_scratch, Otos_i); + __ bind(xreturn_path); + + // _ireturn/_areturn + __ retl(); // return from leaf routine + __ delayed()->mov(O5_savedSP, SP); + + // Generate regular method entry + __ bind(slow_path); + __ ba(false, fast_accessor_slow_entry_path); + __ delayed()->nop(); + return entry; + } + return NULL; +} + +// +// Interpreter stub for calling a native method. (C++ interpreter) +// This sets up a somewhat different looking stack for calling the native method +// than the typical interpreter frame setup. +// + +address InterpreterGenerator::generate_native_entry(bool synchronized) { + address entry = __ pc(); + + // the following temporary registers are used during frame creation + const Register Gtmp1 = G3_scratch ; + const Register Gtmp2 = G1_scratch; + const Address size_of_parameters(G5_method, 0, in_bytes(methodOopDesc::size_of_parameters_offset())); + + bool inc_counter = UseCompiler || CountCompiledCalls; + + // make sure registers are different! + assert_different_registers(G2_thread, G5_method, Gargs, Gtmp1, Gtmp2); + + const Address access_flags (G5_method, 0, in_bytes(methodOopDesc::access_flags_offset())); + + Label Lentry; + __ bind(Lentry); + + __ verify_oop(G5_method); + + const Register Glocals_size = G3; + assert_different_registers(Glocals_size, G4_scratch, Gframe_size); + + // make sure method is native & not abstract + // rethink these assertions - they can be simplified and shared (gri 2/25/2000) +#ifdef ASSERT + __ ld(access_flags, Gtmp1); + { + Label L; + __ btst(JVM_ACC_NATIVE, Gtmp1); + __ br(Assembler::notZero, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("tried to execute non-native method as native"); + __ bind(L); + } + { Label L; + __ btst(JVM_ACC_ABSTRACT, Gtmp1); + __ br(Assembler::zero, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("tried to execute abstract method as non-abstract"); + __ bind(L); + } +#endif // ASSERT + + __ lduh(size_of_parameters, Gtmp1); + __ sll(Gtmp1, LogBytesPerWord, Gtmp2); // parameter size in bytes + __ add(Gargs, Gtmp2, Gargs); // points to first local + BytesPerWord + // NEW + __ add(Gargs, -wordSize, Gargs); // points to first local[0] + // generate the code to allocate the interpreter stack frame + // NEW FRAME ALLOCATED HERE + // save callers original sp + // __ mov(SP, I5_savedSP->after_restore()); + + generate_compute_interpreter_state(Lstate, G0, true); + + // At this point Lstate points to new interpreter state + // + + const Address do_not_unlock_if_synchronized(G2_thread, 0, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + // Since at this point in the method invocation the exception handler + // would try to exit the monitor of synchronized methods which hasn't + // been entered yet, we set the thread local variable + // _do_not_unlock_if_synchronized to true. If any exception was thrown by + // runtime, exception handling i.e. unlock_if_synchronized_method will + // check this thread local flag. + // This flag has two effects, one is to force an unwind in the topmost + // interpreter frame and not perform an unlock while doing so. + + __ movbool(true, G3_scratch); + __ stbool(G3_scratch, do_not_unlock_if_synchronized); + + + // increment invocation counter and check for overflow + // + // Note: checking for negative value instead of overflow + // so we have a 'sticky' overflow test (may be of + // importance as soon as we have true MT/MP) + Label invocation_counter_overflow; + if (inc_counter) { + generate_counter_incr(&invocation_counter_overflow, NULL, NULL); + } + Label Lcontinue; + __ bind(Lcontinue); + + bang_stack_shadow_pages(true); + // reset the _do_not_unlock_if_synchronized flag + __ stbool(G0, do_not_unlock_if_synchronized); + + // check for synchronized methods + // Must happen AFTER invocation_counter check, so method is not locked + // if counter overflows. + + if (synchronized) { + lock_method(); + // Don't see how G2_thread is preserved here... + // __ verify_thread(); QQQ destroys L0,L1 can't use + } else { +#ifdef ASSERT + { Label ok; + __ ld_ptr(STATE(_method), G5_method); + __ ld(access_flags, O0); + __ btst(JVM_ACC_SYNCHRONIZED, O0); + __ br( Assembler::zero, false, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("method needs synchronization"); + __ bind(ok); + } +#endif // ASSERT + } + + // start execution + +// __ verify_thread(); kills L1,L2 can't use at the moment + + // jvmti/jvmpi support + __ notify_method_entry(); + + // native call + + // (note that O0 is never an oop--at most it is a handle) + // It is important not to smash any handles created by this call, + // until any oop handle in O0 is dereferenced. + + // (note that the space for outgoing params is preallocated) + + // get signature handler + + Label pending_exception_present; + + { Label L; + __ ld_ptr(STATE(_method), G5_method); + __ ld_ptr(Address(G5_method, 0, in_bytes(methodOopDesc::signature_handler_offset())), G3_scratch); + __ tst(G3_scratch); + __ brx(Assembler::notZero, false, Assembler::pt, L); + __ delayed()->nop(); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), G5_method, false); + __ ld_ptr(STATE(_method), G5_method); + + Address exception_addr(G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + __ ld_ptr(exception_addr, G3_scratch); + __ br_notnull(G3_scratch, false, Assembler::pn, pending_exception_present); + __ delayed()->nop(); + __ ld_ptr(Address(G5_method, 0, in_bytes(methodOopDesc::signature_handler_offset())), G3_scratch); + __ bind(L); + } + + // Push a new frame so that the args will really be stored in + // Copy a few locals across so the new frame has the variables + // we need but these values will be dead at the jni call and + // therefore not gc volatile like the values in the current + // frame (Lstate in particular) + + // Flush the state pointer to the register save area + // Which is the only register we need for a stack walk. + __ st_ptr(Lstate, SP, (Lstate->sp_offset_in_saved_window() * wordSize) + STACK_BIAS); + + __ mov(Lstate, O1); // Need to pass the state pointer across the frame + + // Calculate current frame size + __ sub(SP, FP, O3); // Calculate negative of current frame size + __ save(SP, O3, SP); // Allocate an identical sized frame + + __ mov(I1, Lstate); // In the "natural" register. + + // Note I7 has leftover trash. Slow signature handler will fill it in + // should we get there. Normal jni call will set reasonable last_Java_pc + // below (and fix I7 so the stack trace doesn't have a meaningless frame + // in it). + + + // call signature handler + __ ld_ptr(STATE(_method), Lmethod); + __ ld_ptr(STATE(_locals), Llocals); + + __ callr(G3_scratch, 0); + __ delayed()->nop(); + __ ld_ptr(STATE(_thread), G2_thread); // restore thread (shouldn't be needed) + + { Label not_static; + + __ ld_ptr(STATE(_method), G5_method); + __ ld(access_flags, O0); + __ btst(JVM_ACC_STATIC, O0); + __ br( Assembler::zero, false, Assembler::pt, not_static); + __ delayed()-> + // get native function entry point(O0 is a good temp until the very end) + ld_ptr(Address(G5_method, 0, in_bytes(methodOopDesc::native_function_offset())), O0); + // for static methods insert the mirror argument + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + Klass::java_mirror_offset_in_bytes(); + + __ ld_ptr(Address(G5_method, 0, in_bytes(methodOopDesc:: constants_offset())), O1); + __ ld_ptr(Address(O1, 0, constantPoolOopDesc::pool_holder_offset_in_bytes()), O1); + __ ld_ptr(O1, mirror_offset, O1); + // where the mirror handle body is allocated: +#ifdef ASSERT + if (!PrintSignatureHandlers) // do not dirty the output with this + { Label L; + __ tst(O1); + __ brx(Assembler::notZero, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("mirror is missing"); + __ bind(L); + } +#endif // ASSERT + __ st_ptr(O1, STATE(_oop_temp)); + __ add(STATE(_oop_temp), O1); // this is really an LEA not an add + __ bind(not_static); + } + + // At this point, arguments have been copied off of stack into + // their JNI positions, which are O1..O5 and SP[68..]. + // Oops are boxed in-place on the stack, with handles copied to arguments. + // The result handler is in Lscratch. O0 will shortly hold the JNIEnv*. + +#ifdef ASSERT + { Label L; + __ tst(O0); + __ brx(Assembler::notZero, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("native entry point is missing"); + __ bind(L); + } +#endif // ASSERT + + // + // setup the java frame anchor + // + // The scavenge function only needs to know that the PC of this frame is + // in the interpreter method entry code, it doesn't need to know the exact + // PC and hence we can use O7 which points to the return address from the + // previous call in the code stream (signature handler function) + // + // The other trick is we set last_Java_sp to FP instead of the usual SP because + // we have pushed the extra frame in order to protect the volatile register(s) + // in that frame when we return from the jni call + // + + + __ set_last_Java_frame(FP, O7); + __ mov(O7, I7); // make dummy interpreter frame look like one above, + // not meaningless information that'll confuse me. + + // flush the windows now. We don't care about the current (protection) frame + // only the outer frames + + __ flush_windows(); + + // mark windows as flushed + Address flags(G2_thread, + 0, + in_bytes(JavaThread::frame_anchor_offset()) + in_bytes(JavaFrameAnchor::flags_offset())); + __ set(JavaFrameAnchor::flushed, G3_scratch); + __ st(G3_scratch, flags); + + // Transition from _thread_in_Java to _thread_in_native. We are already safepoint ready. + + Address thread_state(G2_thread, 0, in_bytes(JavaThread::thread_state_offset())); +#ifdef ASSERT + { Label L; + __ ld(thread_state, G3_scratch); + __ cmp(G3_scratch, _thread_in_Java); + __ br(Assembler::equal, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("Wrong thread state in native stub"); + __ bind(L); + } +#endif // ASSERT + __ set(_thread_in_native, G3_scratch); + __ st(G3_scratch, thread_state); + + // Call the jni method, using the delay slot to set the JNIEnv* argument. + __ callr(O0, 0); + __ delayed()-> + add(G2_thread, in_bytes(JavaThread::jni_environment_offset()), O0); + __ ld_ptr(STATE(_thread), G2_thread); // restore thread + + // must we block? + + // Block, if necessary, before resuming in _thread_in_Java state. + // In order for GC to work, don't clear the last_Java_sp until after blocking. + { Label no_block; + Address sync_state(G3_scratch, SafepointSynchronize::address_of_state()); + + // Switch thread to "native transition" state before reading the synchronization state. + // This additional state is necessary because reading and testing the synchronization + // state is not atomic w.r.t. GC, as this scenario demonstrates: + // Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. + // VM thread changes sync state to synchronizing and suspends threads for GC. + // Thread A is resumed to finish this native method, but doesn't block here since it + // didn't see any synchronization is progress, and escapes. + __ set(_thread_in_native_trans, G3_scratch); + __ st(G3_scratch, thread_state); + if(os::is_MP()) { + // Write serialization page so VM thread can do a pseudo remote membar. + // We use the current thread pointer to calculate a thread specific + // offset to write to within the page. This minimizes bus traffic + // due to cache line collision. + __ serialize_memory(G2_thread, G1_scratch, G3_scratch); + } + __ load_contents(sync_state, G3_scratch); + __ cmp(G3_scratch, SafepointSynchronize::_not_synchronized); + + + Label L; + Address suspend_state(G2_thread, 0, in_bytes(JavaThread::suspend_flags_offset())); + __ br(Assembler::notEqual, false, Assembler::pn, L); + __ delayed()-> + ld(suspend_state, G3_scratch); + __ cmp(G3_scratch, 0); + __ br(Assembler::equal, false, Assembler::pt, no_block); + __ delayed()->nop(); + __ bind(L); + + // Block. Save any potential method result value before the operation and + // use a leaf call to leave the last_Java_frame setup undisturbed. + save_native_result(); + __ call_VM_leaf(noreg, + CAST_FROM_FN_PTR(address, JavaThread::check_safepoint_and_suspend_for_native_trans), + G2_thread); + __ ld_ptr(STATE(_thread), G2_thread); // restore thread + // Restore any method result value + restore_native_result(); + __ bind(no_block); + } + + // Clear the frame anchor now + + __ reset_last_Java_frame(); + + // Move the result handler address + __ mov(Lscratch, G3_scratch); + // return possible result to the outer frame +#ifndef __LP64 + __ mov(O0, I0); + __ restore(O1, G0, O1); +#else + __ restore(O0, G0, O0); +#endif /* __LP64 */ + + // Move result handler to expected register + __ mov(G3_scratch, Lscratch); + + + // thread state is thread_in_native_trans. Any safepoint blocking has + // happened in the trampoline we are ready to switch to thread_in_Java. + + __ set(_thread_in_Java, G3_scratch); + __ st(G3_scratch, thread_state); + + // If we have an oop result store it where it will be safe for any further gc + // until we return now that we've released the handle it might be protected by + + { + Label no_oop, store_result; + + __ set((intptr_t)AbstractInterpreter::result_handler(T_OBJECT), G3_scratch); + __ cmp(G3_scratch, Lscratch); + __ brx(Assembler::notEqual, false, Assembler::pt, no_oop); + __ delayed()->nop(); + __ addcc(G0, O0, O0); + __ brx(Assembler::notZero, true, Assembler::pt, store_result); // if result is not NULL: + __ delayed()->ld_ptr(O0, 0, O0); // unbox it + __ mov(G0, O0); + + __ bind(store_result); + // Store it where gc will look for it and result handler expects it. + __ st_ptr(O0, STATE(_oop_temp)); + + __ bind(no_oop); + + } + + // reset handle block + __ ld_ptr(G2_thread, in_bytes(JavaThread::active_handles_offset()), G3_scratch); + __ st_ptr(G0, G3_scratch, JNIHandleBlock::top_offset_in_bytes()); + + + // handle exceptions (exception handling will handle unlocking!) + { Label L; + Address exception_addr (G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + + __ ld_ptr(exception_addr, Gtemp); + __ tst(Gtemp); + __ brx(Assembler::equal, false, Assembler::pt, L); + __ delayed()->nop(); + __ bind(pending_exception_present); + // With c++ interpreter we just leave it pending caller will do the correct thing. However... + // Like x86 we ignore the result of the native call and leave the method locked. This + // seems wrong to leave things locked. + + __ br(Assembler::always, false, Assembler::pt, StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); + __ delayed()->restore(I5_savedSP, G0, SP); // remove interpreter frame + + __ bind(L); + } + + // jvmdi/jvmpi support (preserves thread register) + __ notify_method_exit(true, ilgl, InterpreterMacroAssembler::NotifyJVMTI); + + if (synchronized) { + // save and restore any potential method result value around the unlocking operation + save_native_result(); + + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + // Get the initial monitor we allocated + __ sub(Lstate, entry_size, O1); // initial monitor + __ unlock_object(O1); + restore_native_result(); + } + +#if defined(COMPILER2) && !defined(_LP64) + + // C2 expects long results in G1 we can't tell if we're returning to interpreted + // or compiled so just be safe. + + __ sllx(O0, 32, G1); // Shift bits into high G1 + __ srl (O1, 0, O1); // Zero extend O1 + __ or3 (O1, G1, G1); // OR 64 bits into G1 + +#endif /* COMPILER2 && !_LP64 */ + +#ifdef ASSERT + { + Label ok; + __ cmp(I5_savedSP, FP); + __ brx(Assembler::greaterEqualUnsigned, false, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("bad I5_savedSP value"); + __ should_not_reach_here(); + __ bind(ok); + } +#endif + // Calls result handler which POPS FRAME + if (TraceJumps) { + // Move target to register that is recordable + __ mov(Lscratch, G3_scratch); + __ JMP(G3_scratch, 0); + } else { + __ jmp(Lscratch, 0); + } + __ delayed()->nop(); + + if (inc_counter) { + // handle invocation counter overflow + __ bind(invocation_counter_overflow); + generate_counter_overflow(Lcontinue); + } + + + return entry; +} + +void CppInterpreterGenerator::generate_compute_interpreter_state(const Register state, + const Register prev_state, + bool native) { + + // On entry + // G5_method - caller's method + // Gargs - points to initial parameters (i.e. locals[0]) + // G2_thread - valid? (C1 only??) + // "prev_state" - contains any previous frame manager state which we must save a link + // + // On return + // "state" is a pointer to the newly allocated state object. We must allocate and initialize + // a new interpretState object and the method expression stack. + + assert_different_registers(state, prev_state); + assert_different_registers(prev_state, G3_scratch); + const Register Gtmp = G3_scratch; + const Address constants (G5_method, 0, in_bytes(methodOopDesc::constants_offset())); + const Address access_flags (G5_method, 0, in_bytes(methodOopDesc::access_flags_offset())); + const Address size_of_parameters(G5_method, 0, in_bytes(methodOopDesc::size_of_parameters_offset())); + const Address max_stack (G5_method, 0, in_bytes(methodOopDesc::max_stack_offset())); + const Address size_of_locals (G5_method, 0, in_bytes(methodOopDesc::size_of_locals_offset())); + + // slop factor is two extra slots on the expression stack so that + // we always have room to store a result when returning from a call without parameters + // that returns a result. + + const int slop_factor = 2*wordSize; + + const int fixed_size = ((sizeof(BytecodeInterpreter) + slop_factor) >> LogBytesPerWord) + // what is the slop factor? + frame::memory_parameter_word_sp_offset + // register save area + param window + (native ? frame::interpreter_frame_extra_outgoing_argument_words : 0); // JNI, class + + // XXX G5_method valid + + // Now compute new frame size + + if (native) { + __ lduh( size_of_parameters, Gtmp ); + __ calc_mem_param_words(Gtmp, Gtmp); // space for native call parameters passed on the stack in words + } else { + __ lduh(max_stack, Gtmp); // Full size expression stack + } + __ add(Gtmp, fixed_size, Gtmp); // plus the fixed portion + + __ neg(Gtmp); // negative space for stack/parameters in words + __ and3(Gtmp, -WordsPerLong, Gtmp); // make multiple of 2 (SP must be 2-word aligned) + __ sll(Gtmp, LogBytesPerWord, Gtmp); // negative space for frame in bytes + + // Need to do stack size check here before we fault on large frames + + Label stack_ok; + + const int max_pages = StackShadowPages > (StackRedPages+StackYellowPages) ? StackShadowPages : + (StackRedPages+StackYellowPages); + + + __ ld_ptr(G2_thread, in_bytes(Thread::stack_base_offset()), O0); + __ ld_ptr(G2_thread, in_bytes(Thread::stack_size_offset()), O1); + // compute stack bottom + __ sub(O0, O1, O0); + + // Avoid touching the guard pages + // Also a fudge for frame size of BytecodeInterpreter::run + // It varies from 1k->4k depending on build type + const int fudge = 6 * K; + + __ set(fudge + (max_pages * os::vm_page_size()), O1); + + __ add(O0, O1, O0); + __ sub(O0, Gtmp, O0); + __ cmp(SP, O0); + __ brx(Assembler::greaterUnsigned, false, Assembler::pt, stack_ok); + __ delayed()->nop(); + + // throw exception return address becomes throwing pc + + __ call_VM(Oexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_StackOverflowError)); + __ stop("never reached"); + + __ bind(stack_ok); + + __ save(SP, Gtmp, SP); // setup new frame and register window + + // New window I7 call_stub or previous activation + // O6 - register save area, BytecodeInterpreter just below it, args/locals just above that + // + __ sub(FP, sizeof(BytecodeInterpreter), state); // Point to new Interpreter state + __ add(state, STACK_BIAS, state ); // Account for 64bit bias + +#define XXX_STATE(field_name) state, in_bytes(byte_offset_of(BytecodeInterpreter, field_name)) + + // Initialize a new Interpreter state + // orig_sp - caller's original sp + // G2_thread - thread + // Gargs - &locals[0] (unbiased?) + // G5_method - method + // SP (biased) - accounts for full size java stack, BytecodeInterpreter object, register save area, and register parameter save window + + + __ set(0xdead0004, O1); + + + __ st_ptr(Gargs, XXX_STATE(_locals)); + __ st_ptr(G0, XXX_STATE(_oop_temp)); + + __ st_ptr(state, XXX_STATE(_self_link)); // point to self + __ st_ptr(prev_state->after_save(), XXX_STATE(_prev_link)); // Chain interpreter states + __ st_ptr(G2_thread, XXX_STATE(_thread)); // Store javathread + + if (native) { + __ st_ptr(G0, XXX_STATE(_bcp)); + } else { + __ ld_ptr(G5_method, in_bytes(methodOopDesc::const_offset()), O2); // get constMethodOop + __ add(O2, in_bytes(constMethodOopDesc::codes_offset()), O2); // get bcp + __ st_ptr(O2, XXX_STATE(_bcp)); + } + + __ st_ptr(G0, XXX_STATE(_mdx)); + __ st_ptr(G5_method, XXX_STATE(_method)); + + __ set((int) BytecodeInterpreter::method_entry, O1); + __ st(O1, XXX_STATE(_msg)); + + __ ld_ptr(constants, O3); + __ ld_ptr(O3, constantPoolOopDesc::cache_offset_in_bytes(), O2); + __ st_ptr(O2, XXX_STATE(_constants)); + + __ st_ptr(G0, XXX_STATE(_result._to_call._callee)); + + // Monitor base is just start of BytecodeInterpreter object; + __ mov(state, O2); + __ st_ptr(O2, XXX_STATE(_monitor_base)); + + // Do we need a monitor for synchonized method? + { + __ ld(access_flags, O1); + Label done; + Label got_obj; + __ btst(JVM_ACC_SYNCHRONIZED, O1); + __ br( Assembler::zero, false, Assembler::pt, done); + + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + Klass::java_mirror_offset_in_bytes(); + __ delayed()->btst(JVM_ACC_STATIC, O1); + __ ld_ptr(XXX_STATE(_locals), O1); + __ br( Assembler::zero, true, Assembler::pt, got_obj); + __ delayed()->ld_ptr(O1, 0, O1); // get receiver for not-static case + __ ld_ptr(constants, O1); + __ ld_ptr( O1, constantPoolOopDesc::pool_holder_offset_in_bytes(), O1); + // lock the mirror, not the klassOop + __ ld_ptr( O1, mirror_offset, O1); + + __ bind(got_obj); + + #ifdef ASSERT + __ tst(O1); + __ breakpoint_trap(Assembler::zero); + #endif // ASSERT + + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + __ sub(SP, entry_size, SP); // account for initial monitor + __ sub(O2, entry_size, O2); // initial monitor + __ st_ptr(O1, O2, BasicObjectLock::obj_offset_in_bytes()); // and allocate it for interpreter use + __ bind(done); + } + + // Remember initial frame bottom + + __ st_ptr(SP, XXX_STATE(_frame_bottom)); + + __ st_ptr(O2, XXX_STATE(_stack_base)); + + __ sub(O2, wordSize, O2); // prepush + __ st_ptr(O2, XXX_STATE(_stack)); // PREPUSH + + __ lduh(max_stack, O3); // Full size expression stack + __ sll(O3, LogBytesPerWord, O3); + __ sub(O2, O3, O3); +// __ sub(O3, wordSize, O3); // so prepush doesn't look out of bounds + __ st_ptr(O3, XXX_STATE(_stack_limit)); + + if (!native) { + // + // Code to initialize locals + // + Register init_value = noreg; // will be G0 if we must clear locals + // Now zero locals + if (true /* zerolocals */ || ClearInterpreterLocals) { + // explicitly initialize locals + init_value = G0; + } else { + #ifdef ASSERT + // initialize locals to a garbage pattern for better debugging + init_value = O3; + __ set( 0x0F0F0F0F, init_value ); + #endif // ASSERT + } + if (init_value != noreg) { + Label clear_loop; + + // NOTE: If you change the frame layout, this code will need to + // be updated! + __ lduh( size_of_locals, O2 ); + __ lduh( size_of_parameters, O1 ); + __ sll( O2, LogBytesPerWord, O2); + __ sll( O1, LogBytesPerWord, O1 ); + __ ld_ptr(XXX_STATE(_locals), L2_scratch); + __ sub( L2_scratch, O2, O2 ); + __ sub( L2_scratch, O1, O1 ); + + __ bind( clear_loop ); + __ inc( O2, wordSize ); + + __ cmp( O2, O1 ); + __ br( Assembler::lessEqualUnsigned, true, Assembler::pt, clear_loop ); + __ delayed()->st_ptr( init_value, O2, 0 ); + } + } +} +// Find preallocated monitor and lock method (C++ interpreter) +// +void InterpreterGenerator::lock_method(void) { +// Lock the current method. +// Destroys registers L2_scratch, L3_scratch, O0 +// +// Find everything relative to Lstate + +#ifdef ASSERT + __ ld_ptr(STATE(_method), L2_scratch); + __ ld(L2_scratch, in_bytes(methodOopDesc::access_flags_offset()), O0); + + { Label ok; + __ btst(JVM_ACC_SYNCHRONIZED, O0); + __ br( Assembler::notZero, false, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("method doesn't need synchronization"); + __ bind(ok); + } +#endif // ASSERT + + // monitor is already allocated at stack base + // and the lockee is already present + __ ld_ptr(STATE(_stack_base), L2_scratch); + __ ld_ptr(L2_scratch, BasicObjectLock::obj_offset_in_bytes(), O0); // get object + __ lock_object(L2_scratch, O0); + +} + +// Generate code for handling resuming a deopted method +void CppInterpreterGenerator::generate_deopt_handling() { + + Label return_from_deopt_common; + + // deopt needs to jump to here to enter the interpreter (return a result) + deopt_frame_manager_return_atos = __ pc(); + + // O0/O1 live + __ ba(false, return_from_deopt_common); + __ delayed()->set(AbstractInterpreter::BasicType_as_index(T_OBJECT), L3_scratch); // Result stub address array index + + + // deopt needs to jump to here to enter the interpreter (return a result) + deopt_frame_manager_return_btos = __ pc(); + + // O0/O1 live + __ ba(false, return_from_deopt_common); + __ delayed()->set(AbstractInterpreter::BasicType_as_index(T_BOOLEAN), L3_scratch); // Result stub address array index + + // deopt needs to jump to here to enter the interpreter (return a result) + deopt_frame_manager_return_itos = __ pc(); + + // O0/O1 live + __ ba(false, return_from_deopt_common); + __ delayed()->set(AbstractInterpreter::BasicType_as_index(T_INT), L3_scratch); // Result stub address array index + + // deopt needs to jump to here to enter the interpreter (return a result) + + deopt_frame_manager_return_ltos = __ pc(); +#if !defined(_LP64) && defined(COMPILER2) + // All return values are where we want them, except for Longs. C2 returns + // longs in G1 in the 32-bit build whereas the interpreter wants them in O0/O1. + // Since the interpreter will return longs in G1 and O0/O1 in the 32bit + // build even if we are returning from interpreted we just do a little + // stupid shuffing. + // Note: I tried to make c2 return longs in O0/O1 and G1 so we wouldn't have to + // do this here. Unfortunately if we did a rethrow we'd see an machepilog node + // first which would move g1 -> O0/O1 and destroy the exception we were throwing. + + __ srl (G1, 0,O1); + __ srlx(G1,32,O0); +#endif /* !_LP64 && COMPILER2 */ + // O0/O1 live + __ ba(false, return_from_deopt_common); + __ delayed()->set(AbstractInterpreter::BasicType_as_index(T_LONG), L3_scratch); // Result stub address array index + + // deopt needs to jump to here to enter the interpreter (return a result) + + deopt_frame_manager_return_ftos = __ pc(); + // O0/O1 live + __ ba(false, return_from_deopt_common); + __ delayed()->set(AbstractInterpreter::BasicType_as_index(T_FLOAT), L3_scratch); // Result stub address array index + + // deopt needs to jump to here to enter the interpreter (return a result) + deopt_frame_manager_return_dtos = __ pc(); + + // O0/O1 live + __ ba(false, return_from_deopt_common); + __ delayed()->set(AbstractInterpreter::BasicType_as_index(T_DOUBLE), L3_scratch); // Result stub address array index + + // deopt needs to jump to here to enter the interpreter (return a result) + deopt_frame_manager_return_vtos = __ pc(); + + // O0/O1 live + __ set(AbstractInterpreter::BasicType_as_index(T_VOID), L3_scratch); + + // Deopt return common + // an index is present that lets us move any possible result being + // return to the interpreter's stack + // + __ bind(return_from_deopt_common); + + // Result if any is in native abi result (O0..O1/F0..F1). The java expression + // stack is in the state that the calling convention left it. + // Copy the result from native abi result and place it on java expression stack. + + // Current interpreter state is present in Lstate + + // Get current pre-pushed top of interpreter stack + // Any result (if any) is in native abi + // result type index is in L3_scratch + + __ ld_ptr(STATE(_stack), L1_scratch); // get top of java expr stack + + __ set((intptr_t)CppInterpreter::_tosca_to_stack, L4_scratch); + __ sll(L3_scratch, LogBytesPerWord, L3_scratch); + __ ld_ptr(L4_scratch, L3_scratch, Lscratch); // get typed result converter address + __ jmpl(Lscratch, G0, O7); // and convert it + __ delayed()->nop(); + + // L1_scratch points to top of stack (prepushed) + __ st_ptr(L1_scratch, STATE(_stack)); +} + +// Generate the code to handle a more_monitors message from the c++ interpreter +void CppInterpreterGenerator::generate_more_monitors() { + + Label entry, loop; + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + // 1. compute new pointers // esp: old expression stack top + __ delayed()->ld_ptr(STATE(_stack_base), L4_scratch); // current expression stack bottom + __ sub(L4_scratch, entry_size, L4_scratch); + __ st_ptr(L4_scratch, STATE(_stack_base)); + + __ sub(SP, entry_size, SP); // Grow stack + __ st_ptr(SP, STATE(_frame_bottom)); + + __ ld_ptr(STATE(_stack_limit), L2_scratch); + __ sub(L2_scratch, entry_size, L2_scratch); + __ st_ptr(L2_scratch, STATE(_stack_limit)); + + __ ld_ptr(STATE(_stack), L1_scratch); // Get current stack top + __ sub(L1_scratch, entry_size, L1_scratch); + __ st_ptr(L1_scratch, STATE(_stack)); + __ ba(false, entry); + __ delayed()->add(L1_scratch, wordSize, L1_scratch); // first real entry (undo prepush) + + // 2. move expression stack + + __ bind(loop); + __ st_ptr(L3_scratch, Address(L1_scratch, 0)); + __ add(L1_scratch, wordSize, L1_scratch); + __ bind(entry); + __ cmp(L1_scratch, L4_scratch); + __ br(Assembler::notEqual, false, Assembler::pt, loop); + __ delayed()->ld_ptr(L1_scratch, entry_size, L3_scratch); + + // now zero the slot so we can find it. + __ st(G0, L4_scratch, BasicObjectLock::obj_offset_in_bytes()); + +} + +// Initial entry to C++ interpreter from the call_stub. +// This entry point is called the frame manager since it handles the generation +// of interpreter activation frames via requests directly from the vm (via call_stub) +// and via requests from the interpreter. The requests from the call_stub happen +// directly thru the entry point. Requests from the interpreter happen via returning +// from the interpreter and examining the message the interpreter has returned to +// the frame manager. The frame manager can take the following requests: + +// NO_REQUEST - error, should never happen. +// MORE_MONITORS - need a new monitor. Shuffle the expression stack on down and +// allocate a new monitor. +// CALL_METHOD - setup a new activation to call a new method. Very similar to what +// happens during entry during the entry via the call stub. +// RETURN_FROM_METHOD - remove an activation. Return to interpreter or call stub. +// +// Arguments: +// +// ebx: methodOop +// ecx: receiver - unused (retrieved from stack as needed) +// esi: previous frame manager state (NULL from the call_stub/c1/c2) +// +// +// Stack layout at entry +// +// [ return address ] <--- esp +// [ parameter n ] +// ... +// [ parameter 1 ] +// [ expression stack ] +// +// +// We are free to blow any registers we like because the call_stub which brought us here +// initially has preserved the callee save registers already. +// +// + +static address interpreter_frame_manager = NULL; + +#ifdef ASSERT + #define VALIDATE_STATE(scratch, marker) \ + { \ + Label skip; \ + __ ld_ptr(STATE(_self_link), scratch); \ + __ cmp(Lstate, scratch); \ + __ brx(Assembler::equal, false, Assembler::pt, skip); \ + __ delayed()->nop(); \ + __ breakpoint_trap(); \ + __ emit_long(marker); \ + __ bind(skip); \ + } +#else + #define VALIDATE_STATE(scratch, marker) +#endif /* ASSERT */ + +void CppInterpreterGenerator::adjust_callers_stack(Register args) { +// +// Adjust caller's stack so that all the locals can be contiguous with +// the parameters. +// Worries about stack overflow make this a pain. +// +// Destroys args, G3_scratch, G3_scratch +// In/Out O5_savedSP (sender's original SP) +// +// assert_different_registers(state, prev_state); + const Register Gtmp = G3_scratch; + const Register tmp = O2; + const Address size_of_parameters(G5_method, 0, in_bytes(methodOopDesc::size_of_parameters_offset())); + const Address size_of_locals (G5_method, 0, in_bytes(methodOopDesc::size_of_locals_offset())); + + __ lduh(size_of_parameters, tmp); + __ sll(tmp, LogBytesPerWord, Gtmp); // parameter size in bytes + __ add(args, Gtmp, Gargs); // points to first local + BytesPerWord + // NEW + __ add(Gargs, -wordSize, Gargs); // points to first local[0] + // determine extra space for non-argument locals & adjust caller's SP + // Gtmp1: parameter size in words + __ lduh(size_of_locals, Gtmp); + __ compute_extra_locals_size_in_bytes(tmp, Gtmp, Gtmp); + +#if 1 + // c2i adapters place the final interpreter argument in the register save area for O0/I0 + // the call_stub will place the final interpreter argument at + // frame::memory_parameter_word_sp_offset. This is mostly not noticable for either asm + // or c++ interpreter. However with the c++ interpreter when we do a recursive call + // and try to make it look good in the debugger we will store the argument to + // RecursiveInterpreterActivation in the register argument save area. Without allocating + // extra space for the compiler this will overwrite locals in the local array of the + // interpreter. + // QQQ still needed with frameless adapters??? + + const int c2i_adjust_words = frame::memory_parameter_word_sp_offset - frame::callee_register_argument_save_area_sp_offset; + + __ add(Gtmp, c2i_adjust_words*wordSize, Gtmp); +#endif // 1 + + + __ sub(SP, Gtmp, SP); // just caller's frame for the additional space we need. +} + +address InterpreterGenerator::generate_normal_entry(bool synchronized) { + + // G5_method: methodOop + // G2_thread: thread (unused) + // Gargs: bottom of args (sender_sp) + // O5: sender's sp + + // A single frame manager is plenty as we don't specialize for synchronized. We could and + // the code is pretty much ready. Would need to change the test below and for good measure + // modify generate_interpreter_state to only do the (pre) sync stuff stuff for synchronized + // routines. Not clear this is worth it yet. + + if (interpreter_frame_manager) { + return interpreter_frame_manager; + } + + __ bind(frame_manager_entry); + + // the following temporary registers are used during frame creation + const Register Gtmp1 = G3_scratch; + // const Register Lmirror = L1; // native mirror (native calls only) + + const Address constants (G5_method, 0, in_bytes(methodOopDesc::constants_offset())); + const Address access_flags (G5_method, 0, in_bytes(methodOopDesc::access_flags_offset())); + const Address size_of_parameters(G5_method, 0, in_bytes(methodOopDesc::size_of_parameters_offset())); + const Address max_stack (G5_method, 0, in_bytes(methodOopDesc::max_stack_offset())); + const Address size_of_locals (G5_method, 0, in_bytes(methodOopDesc::size_of_locals_offset())); + + address entry_point = __ pc(); + __ mov(G0, prevState); // no current activation + + + Label re_dispatch; + + __ bind(re_dispatch); + + // Interpreter needs to have locals completely contiguous. In order to do that + // We must adjust the caller's stack pointer for any locals beyond just the + // parameters + adjust_callers_stack(Gargs); + + // O5_savedSP still contains sender's sp + + // NEW FRAME + + generate_compute_interpreter_state(Lstate, prevState, false); + + // At this point a new interpreter frame and state object are created and initialized + // Lstate has the pointer to the new activation + // Any stack banging or limit check should already be done. + + Label call_interpreter; + + __ bind(call_interpreter); + + +#if 1 + __ set(0xdead002, Lmirror); + __ set(0xdead002, L2_scratch); + __ set(0xdead003, L3_scratch); + __ set(0xdead004, L4_scratch); + __ set(0xdead005, Lscratch); + __ set(0xdead006, Lscratch2); + __ set(0xdead007, L7_scratch); + + __ set(0xdeaf002, O2); + __ set(0xdeaf003, O3); + __ set(0xdeaf004, O4); + __ set(0xdeaf005, O5); +#endif + + // Call interpreter (stack bang complete) enter here if message is + // set and we know stack size is valid + + Label call_interpreter_2; + + __ bind(call_interpreter_2); + +#ifdef ASSERT + { + Label skip; + __ ld_ptr(STATE(_frame_bottom), G3_scratch); + __ cmp(G3_scratch, SP); + __ brx(Assembler::equal, false, Assembler::pt, skip); + __ delayed()->nop(); + __ stop("SP not restored to frame bottom"); + __ bind(skip); + } +#endif + + VALIDATE_STATE(G3_scratch, 4); + __ set_last_Java_frame(SP, noreg); + __ mov(Lstate, O0); // (arg) pointer to current state + + __ call(CAST_FROM_FN_PTR(address, + JvmtiExport::can_post_interpreter_events() ? + BytecodeInterpreter::runWithChecks + : BytecodeInterpreter::run), + relocInfo::runtime_call_type); + + __ delayed()->nop(); + + __ ld_ptr(STATE(_thread), G2_thread); + __ reset_last_Java_frame(); + + // examine msg from interpreter to determine next action + __ ld_ptr(STATE(_thread), G2_thread); // restore G2_thread + + __ ld(STATE(_msg), L1_scratch); // Get new message + + Label call_method; + Label return_from_interpreted_method; + Label throw_exception; + Label do_OSR; + Label bad_msg; + Label resume_interpreter; + + __ cmp(L1_scratch, (int)BytecodeInterpreter::call_method); + __ br(Assembler::equal, false, Assembler::pt, call_method); + __ delayed()->cmp(L1_scratch, (int)BytecodeInterpreter::return_from_method); + __ br(Assembler::equal, false, Assembler::pt, return_from_interpreted_method); + __ delayed()->cmp(L1_scratch, (int)BytecodeInterpreter::throwing_exception); + __ br(Assembler::equal, false, Assembler::pt, throw_exception); + __ delayed()->cmp(L1_scratch, (int)BytecodeInterpreter::do_osr); + __ br(Assembler::equal, false, Assembler::pt, do_OSR); + __ delayed()->cmp(L1_scratch, (int)BytecodeInterpreter::more_monitors); + __ br(Assembler::notEqual, false, Assembler::pt, bad_msg); + + // Allocate more monitor space, shuffle expression stack.... + + generate_more_monitors(); + + // new monitor slot allocated, resume the interpreter. + + __ set((int)BytecodeInterpreter::got_monitors, L1_scratch); + VALIDATE_STATE(G3_scratch, 5); + __ ba(false, call_interpreter); + __ delayed()->st(L1_scratch, STATE(_msg)); + + // uncommon trap needs to jump to here to enter the interpreter (re-execute current bytecode) + unctrap_frame_manager_entry = __ pc(); + + // QQQ what message do we send + + __ ba(false, call_interpreter); + __ delayed()->ld_ptr(STATE(_frame_bottom), SP); // restore to full stack frame + + //============================================================================= + // Returning from a compiled method into a deopted method. The bytecode at the + // bcp has completed. The result of the bytecode is in the native abi (the tosca + // for the template based interpreter). Any stack space that was used by the + // bytecode that has completed has been removed (e.g. parameters for an invoke) + // so all that we have to do is place any pending result on the expression stack + // and resume execution on the next bytecode. + + generate_deopt_handling(); + + // ready to resume the interpreter + + __ set((int)BytecodeInterpreter::deopt_resume, L1_scratch); + __ ba(false, call_interpreter); + __ delayed()->st(L1_scratch, STATE(_msg)); + + // Current frame has caught an exception we need to dispatch to the + // handler. We can get here because a native interpreter frame caught + // an exception in which case there is no handler and we must rethrow + // If it is a vanilla interpreted frame the we simply drop into the + // interpreter and let it do the lookup. + + Interpreter::_rethrow_exception_entry = __ pc(); + + Label return_with_exception; + Label unwind_and_forward; + + // O0: exception + // O7: throwing pc + + // We want exception in the thread no matter what we ultimately decide about frame type. + + Address exception_addr (G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + __ verify_thread(); + __ st_ptr(O0, exception_addr); + + // get the methodOop + __ ld_ptr(STATE(_method), G5_method); + + // if this current frame vanilla or native? + + __ ld(access_flags, Gtmp1); + __ btst(JVM_ACC_NATIVE, Gtmp1); + __ br(Assembler::zero, false, Assembler::pt, return_with_exception); // vanilla interpreted frame handle directly + __ delayed()->nop(); + + // We drop thru to unwind a native interpreted frame with a pending exception + // We jump here for the initial interpreter frame with exception pending + // We unwind the current acivation and forward it to our caller. + + __ bind(unwind_and_forward); + + // Unwind frame and jump to forward exception. unwinding will place throwing pc in O7 + // as expected by forward_exception. + + __ restore(FP, G0, SP); // unwind interpreter state frame + __ br(Assembler::always, false, Assembler::pt, StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); + __ delayed()->mov(I5_savedSP->after_restore(), SP); + + // Return point from a call which returns a result in the native abi + // (c1/c2/jni-native). This result must be processed onto the java + // expression stack. + // + // A pending exception may be present in which case there is no result present + + address return_from_native_method = __ pc(); + + VALIDATE_STATE(G3_scratch, 6); + + // Result if any is in native abi result (O0..O1/F0..F1). The java expression + // stack is in the state that the calling convention left it. + // Copy the result from native abi result and place it on java expression stack. + + // Current interpreter state is present in Lstate + + // Exception pending? + + __ ld_ptr(STATE(_frame_bottom), SP); // restore to full stack frame + __ ld_ptr(exception_addr, Lscratch); // get any pending exception + __ tst(Lscratch); // exception pending? + __ brx(Assembler::notZero, false, Assembler::pt, return_with_exception); + __ delayed()->nop(); + + // Process the native abi result to java expression stack + + __ ld_ptr(STATE(_result._to_call._callee), L4_scratch); // called method + __ ld_ptr(STATE(_stack), L1_scratch); // get top of java expr stack + __ lduh(L4_scratch, in_bytes(methodOopDesc::size_of_parameters_offset()), L2_scratch); // get parameter size + __ sll(L2_scratch, LogBytesPerWord, L2_scratch ); // parameter size in bytes + __ add(L1_scratch, L2_scratch, L1_scratch); // stack destination for result + __ ld_ptr(L4_scratch, in_bytes(methodOopDesc::result_index_offset()), L3_scratch); // called method result type index + + // tosca is really just native abi + __ set((intptr_t)CppInterpreter::_tosca_to_stack, L4_scratch); + __ sll(L3_scratch, LogBytesPerWord, L3_scratch); + __ ld_ptr(L4_scratch, L3_scratch, Lscratch); // get typed result converter address + __ jmpl(Lscratch, G0, O7); // and convert it + __ delayed()->nop(); + + // L1_scratch points to top of stack (prepushed) + + __ ba(false, resume_interpreter); + __ delayed()->mov(L1_scratch, O1); + + // An exception is being caught on return to a vanilla interpreter frame. + // Empty the stack and resume interpreter + + __ bind(return_with_exception); + + __ ld_ptr(STATE(_frame_bottom), SP); // restore to full stack frame + __ ld_ptr(STATE(_stack_base), O1); // empty java expression stack + __ ba(false, resume_interpreter); + __ delayed()->sub(O1, wordSize, O1); // account for prepush + + // Return from interpreted method we return result appropriate to the caller (i.e. "recursive" + // interpreter call, or native) and unwind this interpreter activation. + // All monitors should be unlocked. + + __ bind(return_from_interpreted_method); + + VALIDATE_STATE(G3_scratch, 7); + + Label return_to_initial_caller; + + // Interpreted result is on the top of the completed activation expression stack. + // We must return it to the top of the callers stack if caller was interpreted + // otherwise we convert to native abi result and return to call_stub/c1/c2 + // The caller's expression stack was truncated by the call however the current activation + // has enough stuff on the stack that we have usable space there no matter what. The + // other thing that makes it easy is that the top of the caller's stack is stored in STATE(_locals) + // for the current activation + + __ ld_ptr(STATE(_prev_link), L1_scratch); + __ ld_ptr(STATE(_method), L2_scratch); // get method just executed + __ ld_ptr(L2_scratch, in_bytes(methodOopDesc::result_index_offset()), L2_scratch); + __ tst(L1_scratch); + __ brx(Assembler::zero, false, Assembler::pt, return_to_initial_caller); + __ delayed()->sll(L2_scratch, LogBytesPerWord, L2_scratch); + + // Copy result to callers java stack + + __ set((intptr_t)CppInterpreter::_stack_to_stack, L4_scratch); + __ ld_ptr(L4_scratch, L2_scratch, Lscratch); // get typed result converter address + __ ld_ptr(STATE(_stack), O0); // current top (prepushed) + __ ld_ptr(STATE(_locals), O1); // stack destination + + // O0 - will be source, O1 - will be destination (preserved) + __ jmpl(Lscratch, G0, O7); // and convert it + __ delayed()->add(O0, wordSize, O0); // get source (top of current expr stack) + + // O1 == &locals[0] + + // Result is now on caller's stack. Just unwind current activation and resume + + Label unwind_recursive_activation; + + + __ bind(unwind_recursive_activation); + + // O1 == &locals[0] (really callers stacktop) for activation now returning + // returning to interpreter method from "recursive" interpreter call + // result converter left O1 pointing to top of the( prepushed) java stack for method we are returning + // to. Now all we must do is unwind the state from the completed call + + // Must restore stack + VALIDATE_STATE(G3_scratch, 8); + + // Return to interpreter method after a method call (interpreted/native/c1/c2) has completed. + // Result if any is already on the caller's stack. All we must do now is remove the now dead + // frame and tell interpreter to resume. + + + __ mov(O1, I1); // pass back new stack top across activation + // POP FRAME HERE ================================== + __ restore(FP, G0, SP); // unwind interpreter state frame + __ ld_ptr(STATE(_frame_bottom), SP); // restore to full stack frame + + + // Resume the interpreter. The current frame contains the current interpreter + // state object. + // + // O1 == new java stack pointer + + __ bind(resume_interpreter); + VALIDATE_STATE(G3_scratch, 10); + + // A frame we have already used before so no need to bang stack so use call_interpreter_2 entry + + __ set((int)BytecodeInterpreter::method_resume, L1_scratch); + __ st(L1_scratch, STATE(_msg)); + __ ba(false, call_interpreter_2); + __ delayed()->st_ptr(O1, STATE(_stack)); + + + // Fast accessor methods share this entry point. + // This works because frame manager is in the same codelet + // This can either be an entry via call_stub/c1/c2 or a recursive interpreter call + // we need to do a little register fixup here once we distinguish the two of them + if (UseFastAccessorMethods && !synchronized) { + // Call stub_return address still in O7 + __ bind(fast_accessor_slow_entry_path); + __ set((intptr_t)return_from_native_method - 8, Gtmp1); + __ cmp(Gtmp1, O7); // returning to interpreter? + __ brx(Assembler::equal, true, Assembler::pt, re_dispatch); // yep + __ delayed()->nop(); + __ ba(false, re_dispatch); + __ delayed()->mov(G0, prevState); // initial entry + + } + + // interpreter returning to native code (call_stub/c1/c2) + // convert result and unwind initial activation + // L2_scratch - scaled result type index + + __ bind(return_to_initial_caller); + + __ set((intptr_t)CppInterpreter::_stack_to_native_abi, L4_scratch); + __ ld_ptr(L4_scratch, L2_scratch, Lscratch); // get typed result converter address + __ ld_ptr(STATE(_stack), O0); // current top (prepushed) + __ jmpl(Lscratch, G0, O7); // and convert it + __ delayed()->add(O0, wordSize, O0); // get source (top of current expr stack) + + Label unwind_initial_activation; + __ bind(unwind_initial_activation); + + // RETURN TO CALL_STUB/C1/C2 code (result if any in I0..I1/(F0/..F1) + // we can return here with an exception that wasn't handled by interpreted code + // how does c1/c2 see it on return? + + // compute resulting sp before/after args popped depending upon calling convention + // __ ld_ptr(STATE(_saved_sp), Gtmp1); + // + // POP FRAME HERE ================================== + __ restore(FP, G0, SP); + __ retl(); + __ delayed()->mov(I5_savedSP->after_restore(), SP); + + // OSR request, unwind the current frame and transfer to the OSR entry + // and enter OSR nmethod + + __ bind(do_OSR); + Label remove_initial_frame; + __ ld_ptr(STATE(_prev_link), L1_scratch); + __ ld_ptr(STATE(_result._osr._osr_buf), G1_scratch); + + // We are going to pop this frame. Is there another interpreter frame underneath + // it or is it callstub/compiled? + + __ tst(L1_scratch); + __ brx(Assembler::zero, false, Assembler::pt, remove_initial_frame); + __ delayed()->ld_ptr(STATE(_result._osr._osr_entry), G3_scratch); + + // Frame underneath is an interpreter frame simply unwind + // POP FRAME HERE ================================== + __ restore(FP, G0, SP); // unwind interpreter state frame + __ mov(I5_savedSP->after_restore(), SP); + + // Since we are now calling native need to change our "return address" from the + // dummy RecursiveInterpreterActivation to a return from native + + __ set((intptr_t)return_from_native_method - 8, O7); + + __ jmpl(G3_scratch, G0, G0); + __ delayed()->mov(G1_scratch, O0); + + __ bind(remove_initial_frame); + + // POP FRAME HERE ================================== + __ restore(FP, G0, SP); + __ mov(I5_savedSP->after_restore(), SP); + __ jmpl(G3_scratch, G0, G0); + __ delayed()->mov(G1_scratch, O0); + + // Call a new method. All we do is (temporarily) trim the expression stack + // push a return address to bring us back to here and leap to the new entry. + // At this point we have a topmost frame that was allocated by the frame manager + // which contains the current method interpreted state. We trim this frame + // of excess java expression stack entries and then recurse. + + __ bind(call_method); + + // stack points to next free location and not top element on expression stack + // method expects sp to be pointing to topmost element + + __ ld_ptr(STATE(_thread), G2_thread); + __ ld_ptr(STATE(_result._to_call._callee), G5_method); + + + // SP already takes in to account the 2 extra words we use for slop + // when we call a "static long no_params()" method. So if + // we trim back sp by the amount of unused java expression stack + // there will be automagically the 2 extra words we need. + // We also have to worry about keeping SP aligned. + + __ ld_ptr(STATE(_stack), Gargs); + __ ld_ptr(STATE(_stack_limit), L1_scratch); + + // compute the unused java stack size + __ sub(Gargs, L1_scratch, L2_scratch); // compute unused space + + // Round down the unused space to that stack is always aligned + // by making the unused space a multiple of the size of a long. + + __ and3(L2_scratch, -BytesPerLong, L2_scratch); + + // Now trim the stack + __ add(SP, L2_scratch, SP); + + + // Now point to the final argument (account for prepush) + __ add(Gargs, wordSize, Gargs); +#ifdef ASSERT + // Make sure we have space for the window + __ sub(Gargs, SP, L1_scratch); + __ cmp(L1_scratch, 16*wordSize); + { + Label skip; + __ brx(Assembler::greaterEqual, false, Assembler::pt, skip); + __ delayed()->nop(); + __ stop("killed stack"); + __ bind(skip); + } +#endif // ASSERT + + // Create a new frame where we can store values that make it look like the interpreter + // really recursed. + + // prepare to recurse or call specialized entry + + // First link the registers we need + + // make the pc look good in debugger + __ set(CAST_FROM_FN_PTR(intptr_t, RecursiveInterpreterActivation), O7); + // argument too + __ mov(Lstate, I0); + + // Record our sending SP + __ mov(SP, O5_savedSP); + + __ ld_ptr(STATE(_result._to_call._callee_entry_point), L2_scratch); + __ set((intptr_t) entry_point, L1_scratch); + __ cmp(L1_scratch, L2_scratch); + __ brx(Assembler::equal, false, Assembler::pt, re_dispatch); + __ delayed()->mov(Lstate, prevState); // link activations + + // method uses specialized entry, push a return so we look like call stub setup + // this path will handle fact that result is returned in registers and not + // on the java stack. + + __ set((intptr_t)return_from_native_method - 8, O7); + __ jmpl(L2_scratch, G0, G0); // Do specialized entry + __ delayed()->nop(); + + // + // Bad Message from interpreter + // + __ bind(bad_msg); + __ stop("Bad message from interpreter"); + + // Interpreted method "returned" with an exception pass it on... + // Pass result, unwind activation and continue/return to interpreter/call_stub + // We handle result (if any) differently based on return to interpreter or call_stub + + __ bind(throw_exception); + __ ld_ptr(STATE(_prev_link), L1_scratch); + __ tst(L1_scratch); + __ brx(Assembler::zero, false, Assembler::pt, unwind_and_forward); + __ delayed()->nop(); + + __ ld_ptr(STATE(_locals), O1); // get result of popping callee's args + __ ba(false, unwind_recursive_activation); + __ delayed()->nop(); + + interpreter_frame_manager = entry_point; + return entry_point; +} + +InterpreterGenerator::InterpreterGenerator(StubQueue* code) + : CppInterpreterGenerator(code) { + generate_all(); // down here so it can be "virtual" +} + + +static int size_activation_helper(int callee_extra_locals, int max_stack, int monitor_size) { + + // Figure out the size of an interpreter frame (in words) given that we have a fully allocated + // expression stack, the callee will have callee_extra_locals (so we can account for + // frame extension) and monitor_size for monitors. Basically we need to calculate + // this exactly like generate_fixed_frame/generate_compute_interpreter_state. + // + // + // The big complicating thing here is that we must ensure that the stack stays properly + // aligned. This would be even uglier if monitor size wasn't modulo what the stack + // needs to be aligned for). We are given that the sp (fp) is already aligned by + // the caller so we must ensure that it is properly aligned for our callee. + // + // Ths c++ interpreter always makes sure that we have a enough extra space on the + // stack at all times to deal with the "stack long no_params()" method issue. This + // is "slop_factor" here. + const int slop_factor = 2; + + const int fixed_size = sizeof(BytecodeInterpreter)/wordSize + // interpreter state object + frame::memory_parameter_word_sp_offset; // register save area + param window + return (round_to(max_stack + + slop_factor + + fixed_size + + monitor_size + + (callee_extra_locals * Interpreter::stackElementWords()), WordsPerLong)); + +} + +int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { + + // See call_stub code + int call_stub_size = round_to(7 + frame::memory_parameter_word_sp_offset, + WordsPerLong); // 7 + register save area + + // Save space for one monitor to get into the interpreted method in case + // the method is synchronized + int monitor_size = method->is_synchronized() ? + 1*frame::interpreter_frame_monitor_size() : 0; + return size_activation_helper(method->max_locals(), method->max_stack(), + monitor_size) + call_stub_size; +} + +void BytecodeInterpreter::layout_interpreterState(interpreterState to_fill, + frame* caller, + frame* current, + methodOop method, + intptr_t* locals, + intptr_t* stack, + intptr_t* stack_base, + intptr_t* monitor_base, + intptr_t* frame_bottom, + bool is_top_frame + ) +{ + // What about any vtable? + // + to_fill->_thread = JavaThread::current(); + // This gets filled in later but make it something recognizable for now + to_fill->_bcp = method->code_base(); + to_fill->_locals = locals; + to_fill->_constants = method->constants()->cache(); + to_fill->_method = method; + to_fill->_mdx = NULL; + to_fill->_stack = stack; + if (is_top_frame && JavaThread::current()->popframe_forcing_deopt_reexecution() ) { + to_fill->_msg = deopt_resume2; + } else { + to_fill->_msg = method_resume; + } + to_fill->_result._to_call._bcp_advance = 0; + to_fill->_result._to_call._callee_entry_point = NULL; // doesn't matter to anyone + to_fill->_result._to_call._callee = NULL; // doesn't matter to anyone + to_fill->_prev_link = NULL; + + // Fill in the registers for the frame + + // Need to install _sender_sp. Actually not too hard in C++! + // When the skeletal frames are layed out we fill in a value + // for _sender_sp. That value is only correct for the oldest + // skeletal frame constructed (because there is only a single + // entry for "caller_adjustment". While the skeletal frames + // exist that is good enough. We correct that calculation + // here and get all the frames correct. + + // to_fill->_sender_sp = locals - (method->size_of_parameters() - 1); + + *current->register_addr(Lstate) = (intptr_t) to_fill; + // skeletal already places a useful value here and this doesn't account + // for alignment so don't bother. + // *current->register_addr(I5_savedSP) = (intptr_t) locals - (method->size_of_parameters() - 1); + + if (caller->is_interpreted_frame()) { + interpreterState prev = caller->get_interpreterState(); + to_fill->_prev_link = prev; + // Make the prev callee look proper + prev->_result._to_call._callee = method; + if (*prev->_bcp == Bytecodes::_invokeinterface) { + prev->_result._to_call._bcp_advance = 5; + } else { + prev->_result._to_call._bcp_advance = 3; + } + } + to_fill->_oop_temp = NULL; + to_fill->_stack_base = stack_base; + // Need +1 here because stack_base points to the word just above the first expr stack entry + // and stack_limit is supposed to point to the word just below the last expr stack entry. + // See generate_compute_interpreter_state. + to_fill->_stack_limit = stack_base - (method->max_stack() + 1); + to_fill->_monitor_base = (BasicObjectLock*) monitor_base; + + // sparc specific + to_fill->_frame_bottom = frame_bottom; + to_fill->_self_link = to_fill; +#ifdef ASSERT + to_fill->_native_fresult = 123456.789; + to_fill->_native_lresult = CONST64(0xdeadcafedeafcafe); +#endif +} + +void BytecodeInterpreter::pd_layout_interpreterState(interpreterState istate, address last_Java_pc, intptr_t* last_Java_fp) { + istate->_last_Java_pc = (intptr_t*) last_Java_pc; +} + + +int AbstractInterpreter::layout_activation(methodOop method, + int tempcount, // Number of slots on java expression stack in use + int popframe_extra_args, + int moncount, // Number of active monitors + int callee_param_size, + int callee_locals_size, + frame* caller, + frame* interpreter_frame, + bool is_top_frame) { + + assert(popframe_extra_args == 0, "NEED TO FIX"); + // NOTE this code must exactly mimic what InterpreterGenerator::generate_compute_interpreter_state() + // does as far as allocating an interpreter frame. + // If interpreter_frame!=NULL, set up the method, locals, and monitors. + // The frame interpreter_frame, if not NULL, is guaranteed to be the right size, + // as determined by a previous call to this method. + // It is also guaranteed to be walkable even though it is in a skeletal state + // NOTE: return size is in words not bytes + // NOTE: tempcount is the current size of the java expression stack. For top most + // frames we will allocate a full sized expression stack and not the curback + // version that non-top frames have. + + // Calculate the amount our frame will be adjust by the callee. For top frame + // this is zero. + + // NOTE: ia64 seems to do this wrong (or at least backwards) in that it + // calculates the extra locals based on itself. Not what the callee does + // to it. So it ignores last_frame_adjust value. Seems suspicious as far + // as getting sender_sp correct. + + int extra_locals_size = callee_locals_size - callee_param_size; + int monitor_size = (sizeof(BasicObjectLock) * moncount) / wordSize; + int full_frame_words = size_activation_helper(extra_locals_size, method->max_stack(), monitor_size); + int short_frame_words = size_activation_helper(extra_locals_size, method->max_stack(), monitor_size); + int frame_words = is_top_frame ? full_frame_words : short_frame_words; + + + /* + if we actually have a frame to layout we must now fill in all the pieces. This means both + the interpreterState and the registers. + */ + if (interpreter_frame != NULL) { + + // MUCHO HACK + + intptr_t* frame_bottom = interpreter_frame->sp() - (full_frame_words - frame_words); + + /* Now fillin the interpreterState object */ + + interpreterState cur_state = (interpreterState) ((intptr_t)interpreter_frame->fp() - sizeof(BytecodeInterpreter)); + + + intptr_t* locals; + + // Calculate the postion of locals[0]. This is painful because of + // stack alignment (same as ia64). The problem is that we can + // not compute the location of locals from fp(). fp() will account + // for the extra locals but it also accounts for aligning the stack + // and we can't determine if the locals[0] was misaligned but max_locals + // was enough to have the + // calculate postion of locals. fp already accounts for extra locals. + // +2 for the static long no_params() issue. + + if (caller->is_interpreted_frame()) { + // locals must agree with the caller because it will be used to set the + // caller's tos when we return. + interpreterState prev = caller->get_interpreterState(); + // stack() is prepushed. + locals = prev->stack() + method->size_of_parameters(); + } else { + // Lay out locals block in the caller adjacent to the register window save area. + // + // Compiled frames do not allocate a varargs area which is why this if + // statement is needed. + // + intptr_t* fp = interpreter_frame->fp(); + int local_words = method->max_locals() * Interpreter::stackElementWords(); + + if (caller->is_compiled_frame()) { + locals = fp + frame::register_save_words + local_words - 1; + } else { + locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1; + } + + } + // END MUCHO HACK + + intptr_t* monitor_base = (intptr_t*) cur_state; + intptr_t* stack_base = monitor_base - monitor_size; + /* +1 because stack is always prepushed */ + intptr_t* stack = stack_base - (tempcount + 1); + + + BytecodeInterpreter::layout_interpreterState(cur_state, + caller, + interpreter_frame, + method, + locals, + stack, + stack_base, + monitor_base, + frame_bottom, + is_top_frame); + + BytecodeInterpreter::pd_layout_interpreterState(cur_state, interpreter_return_address, interpreter_frame->fp()); + + } + return frame_words; +} + +#endif // CC_INTERP diff --git a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.hpp b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.hpp new file mode 100644 index 00000000000..c76d1e409c1 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.hpp @@ -0,0 +1,39 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // Size of interpreter code. Increase if too small. Interpreter will + // fail with a guarantee ("not enough space for interpreter generation"); + // if too small. + // Run with +PrintInterpreter to get the VM to print out the size. + // Max size with JVMTI and TaggedStackInterpreter + + // QQQ this is proably way too large for c++ interpreter + +#ifdef _LP64 + // The sethi() instruction generates lots more instructions when shell + // stack limit is unlimited, so that's why this is much bigger. + const static int InterpreterCodeSize = 210 * K; +#else + const static int InterpreterCodeSize = 180 * K; +#endif diff --git a/hotspot/src/cpu/sparc/vm/debug_sparc.cpp b/hotspot/src/cpu/sparc/vm/debug_sparc.cpp new file mode 100644 index 00000000000..c106a0a3723 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/debug_sparc.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_debug_sparc.cpp.incl" + +#ifndef PRODUCT + +extern "C" void findpc(int x); + + +void pd_ps(frame f) { + intptr_t* sp = f.sp(); + intptr_t* prev_sp = sp - 1; + intptr_t *pc = NULL; + intptr_t *next_pc = NULL; + int count = 0; + tty->print("register window backtrace from %#x:\n", sp); + while (sp != NULL && ((intptr_t)sp & 7) == 0 && sp > prev_sp && sp < prev_sp+1000) { + pc = next_pc; + next_pc = (intptr_t*) sp[I7->sp_offset_in_saved_window()]; + tty->print("[%d] sp=%#x pc=", count, sp); + findpc((intptr_t)pc); + if (WizardMode && Verbose) { + // print register window contents also + tty->print_cr(" L0..L7: {%#x %#x %#x %#x %#x %#x %#x %#x}", + sp[0+0],sp[0+1],sp[0+2],sp[0+3], + sp[0+4],sp[0+5],sp[0+6],sp[0+7]); + tty->print_cr(" I0..I7: {%#x %#x %#x %#x %#x %#x %#x %#x}", + sp[8+0],sp[8+1],sp[8+2],sp[8+3], + sp[8+4],sp[8+5],sp[8+6],sp[8+7]); + // (and print stack frame contents too??) + + CodeBlob *b = CodeCache::find_blob((address) pc); + if (b != NULL) { + if (b->is_nmethod()) { + methodOop m = ((nmethod*)b)->method(); + int nlocals = m->max_locals(); + int nparams = m->size_of_parameters(); + tty->print_cr("compiled java method (locals = %d, params = %d)", nlocals, nparams); + } + } + } + prev_sp = sp; + sp = (intptr_t *)sp[FP->sp_offset_in_saved_window()]; + sp = (intptr_t *)((intptr_t)sp + STACK_BIAS); + count += 1; + } + if (sp != NULL) + tty->print("[%d] sp=%#x [bogus sp!]", count, sp); +} + +#endif // PRODUCT diff --git a/hotspot/src/cpu/sparc/vm/depChecker_sparc.cpp b/hotspot/src/cpu/sparc/vm/depChecker_sparc.cpp new file mode 100644 index 00000000000..82bb5bf6076 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/depChecker_sparc.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_depChecker_sparc.cpp.incl" + +// Nothing to do on Sparc diff --git a/hotspot/src/cpu/sparc/vm/depChecker_sparc.hpp b/hotspot/src/cpu/sparc/vm/depChecker_sparc.hpp new file mode 100644 index 00000000000..de5d21e2569 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/depChecker_sparc.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Nothing to do on Sparc diff --git a/hotspot/src/cpu/sparc/vm/disassembler_sparc.cpp b/hotspot/src/cpu/sparc/vm/disassembler_sparc.cpp new file mode 100644 index 00000000000..fc7cc059798 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/disassembler_sparc.cpp @@ -0,0 +1,230 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_disassembler_sparc.cpp.incl" + +#ifndef PRODUCT + +#define SPARC_VERSION (VM_Version::v9_instructions_work()? \ + (VM_Version::v8_instructions_work()? "" : "9") : "8") + +// This routine is in the shared library: +typedef unsigned char* print_insn_sparc_t(unsigned char* start, DisassemblerEnv* env, + const char* sparc_version); + +void* Disassembler::_library = NULL; +dll_func Disassembler::_print_insn_sparc = NULL; + +bool Disassembler::load_library() { + if (_library == NULL) { + char buf[1024]; + char ebuf[1024]; + sprintf(buf, "disassembler%s", os::dll_file_extension()); + _library = hpi::dll_load(buf, ebuf, sizeof ebuf); + if (_library != NULL) { + tty->print_cr("Loaded disassembler"); + _print_insn_sparc = CAST_TO_FN_PTR(dll_func, hpi::dll_lookup(_library, "print_insn_sparc")); + } + } + return (_library != NULL) && (_print_insn_sparc != NULL); +} + + +class sparc_env : public DisassemblerEnv { + private: + nmethod* code; + outputStream* output; + const char* version; + + static void print_address(address value, outputStream* st); + + public: + sparc_env(nmethod* rcode, outputStream* routput) { + code = rcode; + output = routput; + version = SPARC_VERSION; + } + const char* sparc_version() { return version; } + void print_label(intptr_t value); + void print_raw(char* str) { output->print_raw(str); } + void print(char* format, ...); + char* string_for_offset(intptr_t value); + char* string_for_constant(unsigned char* pc, intptr_t value, int is_decimal); +}; + + +void sparc_env::print_address(address adr, outputStream* st) { + if (!Universe::is_fully_initialized()) { + st->print(INTPTR_FORMAT, (intptr_t)adr); + return; + } + if (StubRoutines::contains(adr)) { + StubCodeDesc *desc = StubCodeDesc::desc_for(adr); + if (desc == NULL) + desc = StubCodeDesc::desc_for(adr + frame::pc_return_offset); + if (desc == NULL) + st->print("Unknown stub at " INTPTR_FORMAT, adr); + else { + st->print("Stub::%s", desc->name()); + if (desc->begin() != adr) + st->print("%+d 0x%p",adr - desc->begin(), adr); + else if (WizardMode) st->print(" " INTPTR_FORMAT, adr); + } + } else { + BarrierSet* bs = Universe::heap()->barrier_set(); + if (bs->kind() == BarrierSet::CardTableModRef && + adr == (address)((CardTableModRefBS*)(bs))->byte_map_base) { + st->print("word_map_base"); + if (WizardMode) st->print(" " INTPTR_FORMAT, (intptr_t)adr); + } else { + st->print(INTPTR_FORMAT, (intptr_t)adr); + } + } +} + + +// called by the disassembler to print out jump addresses +void sparc_env::print_label(intptr_t value) { + print_address((address) value, output); +} + +void sparc_env::print(char* format, ...) { + va_list ap; + va_start(ap, format); + output->vprint(format, ap); + va_end(ap); +} + +char* sparc_env::string_for_offset(intptr_t value) { + stringStream st; + print_address((address) value, &st); + return st.as_string(); +} + +char* sparc_env::string_for_constant(unsigned char* pc, intptr_t value, int is_decimal) { + stringStream st; + oop obj; + if (code && (obj = code->embeddedOop_at(pc)) != NULL) { + obj->print_value_on(&st); + } else + { + print_address((address) value, &st); + } + return st.as_string(); +} + + +address Disassembler::decode_instruction(address start, DisassemblerEnv* env) { + const char* version = ((sparc_env*)env)->sparc_version(); + return ((print_insn_sparc_t*) _print_insn_sparc)(start, env, version); +} + + +const int show_bytes = false; // for disassembler debugging + + +void Disassembler::decode(CodeBlob* cb, outputStream* st) { + st = st ? st : tty; + st->print_cr("Decoding CodeBlob " INTPTR_FORMAT, cb); + decode(cb->instructions_begin(), cb->instructions_end(), st); +} + + +void Disassembler::decode(u_char* begin, u_char* end, outputStream* st) { + assert ((((intptr_t)begin | (intptr_t)end) % sizeof(int) == 0), "misaligned insn addr"); + st = st ? st : tty; + if (!load_library()) { + st->print_cr("Could not load disassembler"); + return; + } + sparc_env env(NULL, st); + unsigned char* p = (unsigned char*) begin; + CodeBlob* cb = CodeCache::find_blob_unsafe(begin); + while (p < (unsigned char*) end && p) { + if (cb != NULL) { + cb->print_block_comment(st, (intptr_t)(p - cb->instructions_begin())); + } + + unsigned char* p0 = p; + st->print(INTPTR_FORMAT ": ", p); + p = decode_instruction(p, &env); + if (show_bytes && p) { + st->print("\t\t\t"); + while (p0 < p) { st->print("%08lx ", *(int*)p0); p0 += sizeof(int); } + } + st->cr(); + } +} + + +void Disassembler::decode(nmethod* nm, outputStream* st) { + st = st ? st : tty; + + st->print_cr("Decoding compiled method " INTPTR_FORMAT ":", nm); + st->print("Code:"); + st->cr(); + + if (!load_library()) { + st->print_cr("Could not load disassembler"); + return; + } + sparc_env env(nm, st); + unsigned char* p = nm->instructions_begin(); + unsigned char* end = nm->instructions_end(); + assert ((((intptr_t)p | (intptr_t)end) % sizeof(int) == 0), "misaligned insn addr"); + + unsigned char *p1 = p; + int total_bucket_count = 0; + while (p1 < end && p1) { + unsigned char *p0 = p1; + ++p1; + address bucket_pc = FlatProfiler::bucket_start_for(p1); + if (bucket_pc != NULL && bucket_pc > p0 && bucket_pc <= p1) + total_bucket_count += FlatProfiler::bucket_count_for(p0); + } + + while (p < end && p) { + if (p == nm->entry_point()) st->print_cr("[Entry Point]"); + if (p == nm->verified_entry_point()) st->print_cr("[Verified Entry Point]"); + if (p == nm->exception_begin()) st->print_cr("[Exception Handler]"); + if (p == nm->stub_begin()) st->print_cr("[Stub Code]"); + if (p == nm->consts_begin()) st->print_cr("[Constants]"); + nm->print_block_comment(st, (intptr_t)(p - nm->instructions_begin())); + unsigned char* p0 = p; + st->print(" " INTPTR_FORMAT ": ", p); + p = decode_instruction(p, &env); + nm->print_code_comment_on(st, 40, p0, p); + st->cr(); + // Output pc bucket ticks if we have any + address bucket_pc = FlatProfiler::bucket_start_for(p); + if (bucket_pc != NULL && bucket_pc > p0 && bucket_pc <= p) { + int bucket_count = FlatProfiler::bucket_count_for(p0); + tty->print_cr("%3.1f%% [%d]", bucket_count*100.0/total_bucket_count, bucket_count); + tty->cr(); + } + } +} + +#endif // PRODUCT diff --git a/hotspot/src/cpu/sparc/vm/disassembler_sparc.hpp b/hotspot/src/cpu/sparc/vm/disassembler_sparc.hpp new file mode 100644 index 00000000000..827488b8e20 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/disassembler_sparc.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The disassembler prints out sparc code annotated +// with Java specific information. + +class Disassembler { +#ifndef PRODUCT + private: + // points to the library. + static void* _library; + // points to the print_insn_sparc function. + static dll_func _print_insn_sparc; + // tries to load library and return whether it succedded. + static bool load_library(); + // decodes one instruction and return the start of the next instruction. + static address decode_instruction(address start, DisassemblerEnv* env); +#endif + public: + static void decode(CodeBlob *cb, outputStream* st = NULL) PRODUCT_RETURN; + static void decode(nmethod* nm, outputStream* st = NULL) PRODUCT_RETURN; + static void decode(u_char* begin, u_char* end, outputStream* st = NULL) PRODUCT_RETURN; +}; + +//Reconciliation History +// 1.9 98/04/29 10:45:51 disassembler_i486.hpp +// 1.10 98/05/11 16:47:20 disassembler_i486.hpp +// 1.12 99/06/22 16:37:37 disassembler_i486.hpp +// 1.13 99/08/06 10:09:04 disassembler_i486.hpp +//End diff --git a/hotspot/src/cpu/sparc/vm/dump_sparc.cpp b/hotspot/src/cpu/sparc/vm/dump_sparc.cpp new file mode 100644 index 00000000000..38ec7aae949 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/dump_sparc.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_dump_sparc.cpp.incl" + + + +// Generate the self-patching vtable method: +// +// This method will be called (as any other Klass virtual method) with +// the Klass itself as the first argument. Example: +// +// oop obj; +// int size = obj->klass()->klass_part()->oop_size(this); +// +// for which the virtual method call is Klass::oop_size(); +// +// The dummy method is called with the Klass object as the first +// operand, and an object as the second argument. +// + +//===================================================================== + +// All of the dummy methods in the vtable are essentially identical, +// differing only by an ordinal constant, and they bear no releationship +// to the original method which the caller intended. Also, there needs +// to be 'vtbl_list_size' instances of the vtable in order to +// differentiate between the 'vtable_list_size' original Klass objects. + +#define __ masm-> + +void CompactingPermGenGen::generate_vtable_methods(void** vtbl_list, + void** vtable, + char** md_top, + char* md_end, + char** mc_top, + char* mc_end) { + + intptr_t vtable_bytes = (num_virtuals * vtbl_list_size) * sizeof(void*); + *(intptr_t *)(*md_top) = vtable_bytes; + *md_top += sizeof(intptr_t); + void** dummy_vtable = (void**)*md_top; + *vtable = dummy_vtable; + *md_top += vtable_bytes; + + guarantee(*md_top <= md_end, "Insufficient space for vtables."); + + // Get ready to generate dummy methods. + + CodeBuffer cb((unsigned char*)*mc_top, mc_end - *mc_top); + MacroAssembler* masm = new MacroAssembler(&cb); + + Label common_code; + for (int i = 0; i < vtbl_list_size; ++i) { + for (int j = 0; j < num_virtuals; ++j) { + dummy_vtable[num_virtuals * i + j] = (void*)masm->pc(); + __ save(SP, -256, SP); + __ brx(Assembler::always, false, Assembler::pt, common_code); + + // Load L0 with a value indicating vtable/offset pair. + // -- bits[ 7..0] (8 bits) which virtual method in table? + // -- bits[12..8] (5 bits) which virtual method table? + // -- must fit in 13-bit instruction immediate field. + __ delayed()->set((i << 8) + j, L0); + } + } + + __ bind(common_code); + + // Expecting to be called with the "this" pointer in O0/I0 (where + // "this" is a Klass object). In addition, L0 was set (above) to + // identify the method and table. + + // Look up the correct vtable pointer. + + __ set((intptr_t)vtbl_list, L2); // L2 = address of new vtable list. + __ srl(L0, 8, L3); // Isolate L3 = vtable identifier. + __ sll(L3, LogBytesPerWord, L3); + __ ld_ptr(L2, L3, L3); // L3 = new (correct) vtable pointer. + __ st_ptr(L3, Address(I0, 0)); // Save correct vtable ptr in entry. + + // Restore registers and jump to the correct method; + + __ and3(L0, 255, L4); // Isolate L3 = method offset;. + __ sll(L4, LogBytesPerWord, L4); + __ ld_ptr(L3, L4, L4); // Get address of correct virtual method + Address method(L4, 0); + __ jmpl(method, G0); // Jump to correct method. + __ delayed()->restore(); // Restore registers. + + __ flush(); + *mc_top = (char*)__ pc(); + + guarantee(*mc_top <= mc_end, "Insufficient space for method wrappers."); +} diff --git a/hotspot/src/cpu/sparc/vm/frame_sparc.cpp b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp new file mode 100644 index 00000000000..e51cd9c70df --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp @@ -0,0 +1,606 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_frame_sparc.cpp.incl" + +void RegisterMap::pd_clear() { + if (_thread->has_last_Java_frame()) { + frame fr = _thread->last_frame(); + _window = fr.sp(); + } else { + _window = NULL; + } + _younger_window = NULL; +} + + +// Unified register numbering scheme: each 32-bits counts as a register +// number, so all the V9 registers take 2 slots. +const static int R_L_nums[] = {0+040,2+040,4+040,6+040,8+040,10+040,12+040,14+040}; +const static int R_I_nums[] = {0+060,2+060,4+060,6+060,8+060,10+060,12+060,14+060}; +const static int R_O_nums[] = {0+020,2+020,4+020,6+020,8+020,10+020,12+020,14+020}; +const static int R_G_nums[] = {0+000,2+000,4+000,6+000,8+000,10+000,12+000,14+000}; +static RegisterMap::LocationValidType bad_mask = 0; +static RegisterMap::LocationValidType R_LIO_mask = 0; +static bool register_map_inited = false; + +static void register_map_init() { + if (!register_map_inited) { + register_map_inited = true; + int i; + for (i = 0; i < 8; i++) { + assert(R_L_nums[i] < RegisterMap::location_valid_type_size, "in first chunk"); + assert(R_I_nums[i] < RegisterMap::location_valid_type_size, "in first chunk"); + assert(R_O_nums[i] < RegisterMap::location_valid_type_size, "in first chunk"); + assert(R_G_nums[i] < RegisterMap::location_valid_type_size, "in first chunk"); + } + + bad_mask |= (1LL << R_O_nums[6]); // SP + bad_mask |= (1LL << R_O_nums[7]); // cPC + bad_mask |= (1LL << R_I_nums[6]); // FP + bad_mask |= (1LL << R_I_nums[7]); // rPC + bad_mask |= (1LL << R_G_nums[2]); // TLS + bad_mask |= (1LL << R_G_nums[7]); // reserved by libthread + + for (i = 0; i < 8; i++) { + R_LIO_mask |= (1LL << R_L_nums[i]); + R_LIO_mask |= (1LL << R_I_nums[i]); + R_LIO_mask |= (1LL << R_O_nums[i]); + } + } +} + + +address RegisterMap::pd_location(VMReg regname) const { + register_map_init(); + + assert(regname->is_reg(), "sanity check"); + // Only the GPRs get handled this way + if( !regname->is_Register()) + return NULL; + + // don't talk about bad registers + if ((bad_mask & ((LocationValidType)1 << regname->value())) != 0) { + return NULL; + } + + // Convert to a GPR + Register reg; + int second_word = 0; + // 32-bit registers for in, out and local + if (!regname->is_concrete()) { + // HMM ought to return NULL for any non-concrete (odd) vmreg + // this all tied up in the fact we put out double oopMaps for + // register locations. When that is fixed we'd will return NULL + // (or assert here). + reg = regname->prev()->as_Register(); +#ifdef _LP64 + second_word = sizeof(jint); +#else + return NULL; +#endif // _LP64 + } else { + reg = regname->as_Register(); + } + if (reg->is_out()) { + assert(_younger_window != NULL, "Younger window should be available"); + return second_word + (address)&_younger_window[reg->after_save()->sp_offset_in_saved_window()]; + } + if (reg->is_local() || reg->is_in()) { + assert(_window != NULL, "Window should be available"); + return second_word + (address)&_window[reg->sp_offset_in_saved_window()]; + } + // Only the window'd GPRs get handled this way; not the globals. + return NULL; +} + + +#ifdef ASSERT +void RegisterMap::check_location_valid() { + register_map_init(); + assert((_location_valid[0] & bad_mask) == 0, "cannot have special locations for SP,FP,TLS,etc."); +} +#endif + +// We are shifting windows. That means we are moving all %i to %o, +// getting rid of all current %l, and keeping all %g. This is only +// complicated if any of the location pointers for these are valid. +// The normal case is that everything is in its standard register window +// home, and _location_valid[0] is zero. In that case, this routine +// does exactly nothing. +void RegisterMap::shift_individual_registers() { + if (!update_map()) return; // this only applies to maps with locations + register_map_init(); + check_location_valid(); + + LocationValidType lv = _location_valid[0]; + LocationValidType lv0 = lv; + + lv &= ~R_LIO_mask; // clear %l, %o, %i regs + + // if we cleared some non-%g locations, we may have to do some shifting + if (lv != lv0) { + // copy %i0-%i5 to %o0-%o5, if they have special locations + // This can happen in within stubs which spill argument registers + // around a dynamic link operation, such as resolve_opt_virtual_call. + for (int i = 0; i < 8; i++) { + if (lv0 & (1LL << R_I_nums[i])) { + _location[R_O_nums[i]] = _location[R_I_nums[i]]; + lv |= (1LL << R_O_nums[i]); + } + } + } + + _location_valid[0] = lv; + check_location_valid(); +} + + +bool frame::safe_for_sender(JavaThread *thread) { + address sp = (address)_sp; + if (sp != NULL && + (sp <= thread->stack_base() && sp >= thread->stack_base() - thread->stack_size())) { + // Unfortunately we can only check frame complete for runtime stubs and nmethod + // other generic buffer blobs are more problematic so we just assume they are + // ok. adapter blobs never have a frame complete and are never ok. + if (_cb != NULL && !_cb->is_frame_complete_at(_pc)) { + if (_cb->is_nmethod() || _cb->is_adapter_blob() || _cb->is_runtime_stub()) { + return false; + } + } + return true; + } + return false; +} + +// constructors + +// Construct an unpatchable, deficient frame +frame::frame(intptr_t* sp, unpatchable_t, address pc, CodeBlob* cb) { +#ifdef _LP64 + assert( (((intptr_t)sp & (wordSize-1)) == 0), "frame constructor passed an invalid sp"); +#endif + _sp = sp; + _younger_sp = NULL; + _pc = pc; + _cb = cb; + _sp_adjustment_by_callee = 0; + assert(pc == NULL && cb == NULL || pc != NULL, "can't have a cb and no pc!"); + if (_cb == NULL && _pc != NULL ) { + _cb = CodeCache::find_blob(_pc); + } + _deopt_state = unknown; +#ifdef ASSERT + if ( _cb != NULL && _cb->is_nmethod()) { + // Without a valid unextended_sp() we can't convert the pc to "original" + assert(!((nmethod*)_cb)->is_deopt_pc(_pc), "invariant broken"); + } +#endif // ASSERT +} + +frame::frame(intptr_t* sp, intptr_t* younger_sp, bool younger_frame_adjusted_stack) { + _sp = sp; + _younger_sp = younger_sp; + if (younger_sp == NULL) { + // make a deficient frame which doesn't know where its PC is + _pc = NULL; + _cb = NULL; + } else { + _pc = (address)younger_sp[I7->sp_offset_in_saved_window()] + pc_return_offset; + assert( (intptr_t*)younger_sp[FP->sp_offset_in_saved_window()] == (intptr_t*)((intptr_t)sp - STACK_BIAS), "younger_sp must be valid"); + // Any frame we ever build should always "safe" therefore we should not have to call + // find_blob_unsafe + // In case of native stubs, the pc retrieved here might be + // wrong. (the _last_native_pc will have the right value) + // So do not put add any asserts on the _pc here. + } + if (younger_frame_adjusted_stack) { + // compute adjustment to this frame's SP made by its interpreted callee + _sp_adjustment_by_callee = (intptr_t*)((intptr_t)younger_sp[I5_savedSP->sp_offset_in_saved_window()] + + STACK_BIAS) - sp; + } else { + _sp_adjustment_by_callee = 0; + } + + _deopt_state = unknown; + + // It is important that frame be fully construct when we do this lookup + // as get_original_pc() needs correct value for unextended_sp() + if (_pc != NULL) { + _cb = CodeCache::find_blob(_pc); + if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { + _pc = ((nmethod*)_cb)->get_original_pc(this); + _deopt_state = is_deoptimized; + } else { + _deopt_state = not_deoptimized; + } + } +} + +bool frame::is_interpreted_frame() const { + return Interpreter::contains(pc()); +} + +// sender_sp + +intptr_t* frame::interpreter_frame_sender_sp() const { + assert(is_interpreted_frame(), "interpreted frame expected"); + return fp(); +} + +#ifndef CC_INTERP +void frame::set_interpreter_frame_sender_sp(intptr_t* sender_sp) { + assert(is_interpreted_frame(), "interpreted frame expected"); + Unimplemented(); +} +#endif // CC_INTERP + + +#ifdef ASSERT +// Debugging aid +static frame nth_sender(int n) { + frame f = JavaThread::current()->last_frame(); + + for(int i = 0; i < n; ++i) + f = f.sender((RegisterMap*)NULL); + + printf("first frame %d\n", f.is_first_frame() ? 1 : 0); + printf("interpreted frame %d\n", f.is_interpreted_frame() ? 1 : 0); + printf("java frame %d\n", f.is_java_frame() ? 1 : 0); + printf("entry frame %d\n", f.is_entry_frame() ? 1 : 0); + printf("native frame %d\n", f.is_native_frame() ? 1 : 0); + if (f.is_compiled_frame()) { + if (f.is_deoptimized_frame()) + printf("deoptimized frame 1\n"); + else + printf("compiled frame 1\n"); + } + + return f; +} +#endif + + +frame frame::sender_for_entry_frame(RegisterMap *map) const { + assert(map != NULL, "map must be set"); + // Java frame called from C; skip all C frames and return top C + // frame of that chunk as the sender + JavaFrameAnchor* jfa = entry_frame_call_wrapper()->anchor(); + assert(!entry_frame_is_first(), "next Java fp must be non zero"); + assert(jfa->last_Java_sp() > _sp, "must be above this frame on stack"); + intptr_t* last_Java_sp = jfa->last_Java_sp(); + // Since we are walking the stack now this nested anchor is obviously walkable + // even if it wasn't when it was stacked. + if (!jfa->walkable()) { + // Capture _last_Java_pc (if needed) and mark anchor walkable. + jfa->capture_last_Java_pc(_sp); + } + assert(jfa->last_Java_pc() != NULL, "No captured pc!"); + map->clear(); + map->make_integer_regs_unsaved(); + map->shift_window(last_Java_sp, NULL); + assert(map->include_argument_oops(), "should be set by clear"); + return frame(last_Java_sp, frame::unpatchable, jfa->last_Java_pc()); +} + +frame frame::sender_for_interpreter_frame(RegisterMap *map) const { + ShouldNotCallThis(); + return sender(map); +} + +frame frame::sender_for_compiled_frame(RegisterMap *map) const { + ShouldNotCallThis(); + return sender(map); +} + +frame frame::sender(RegisterMap* map) const { + assert(map != NULL, "map must be set"); + + assert(CodeCache::find_blob_unsafe(_pc) == _cb, "inconsistent"); + + // Default is not to follow arguments; update it accordingly below + map->set_include_argument_oops(false); + + if (is_entry_frame()) return sender_for_entry_frame(map); + + intptr_t* younger_sp = sp(); + intptr_t* sp = sender_sp(); + bool adjusted_stack = false; + + // Note: The version of this operation on any platform with callee-save + // registers must update the register map (if not null). + // In order to do this correctly, the various subtypes of + // of frame (interpreted, compiled, glue, native), + // must be distinguished. There is no need on SPARC for + // such distinctions, because all callee-save registers are + // preserved for all frames via SPARC-specific mechanisms. + // + // *** HOWEVER, *** if and when we make any floating-point + // registers callee-saved, then we will have to copy over + // the RegisterMap update logic from the Intel code. + + // The constructor of the sender must know whether this frame is interpreted so it can set the + // sender's _sp_adjustment_by_callee field. An osr adapter frame was originally + // interpreted but its pc is in the code cache (for c1 -> osr_frame_return_id stub), so it must be + // explicitly recognized. + + adjusted_stack = is_interpreted_frame(); + if (adjusted_stack) { + map->make_integer_regs_unsaved(); + map->shift_window(sp, younger_sp); + } else if (_cb != NULL) { + // Update the locations of implicitly saved registers to be their + // addresses in the register save area. + // For %o registers, the addresses of %i registers in the next younger + // frame are used. + map->shift_window(sp, younger_sp); + if (map->update_map()) { + // Tell GC to use argument oopmaps for some runtime stubs that need it. + // For C1, the runtime stub might not have oop maps, so set this flag + // outside of update_register_map. + map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); + if (_cb->oop_maps() != NULL) { + OopMapSet::update_register_map(this, map); + } + } + } + return frame(sp, younger_sp, adjusted_stack); +} + + +void frame::patch_pc(Thread* thread, address pc) { + if(thread == Thread::current()) { + StubRoutines::Sparc::flush_callers_register_windows_func()(); + } + if (TracePcPatching) { + // QQQ this assert is invalid (or too strong anyway) sice _pc could + // be original pc and frame could have the deopt pc. + // assert(_pc == *O7_addr() + pc_return_offset, "frame has wrong pc"); + tty->print_cr("patch_pc at address 0x%x [0x%x -> 0x%x] ", O7_addr(), _pc, pc); + } + _cb = CodeCache::find_blob(pc); + *O7_addr() = pc - pc_return_offset; + _cb = CodeCache::find_blob(_pc); + if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { + address orig = ((nmethod*)_cb)->get_original_pc(this); + assert(orig == _pc, "expected original to be stored before patching"); + _deopt_state = is_deoptimized; + } else { + _deopt_state = not_deoptimized; + } +} + + +static bool sp_is_valid(intptr_t* old_sp, intptr_t* young_sp, intptr_t* sp) { + return (((intptr_t)sp & (2*wordSize-1)) == 0 && + sp <= old_sp && + sp >= young_sp); +} + + +/* + Find the (biased) sp that is just younger than old_sp starting at sp. + If not found return NULL. Register windows are assumed to be flushed. +*/ +intptr_t* frame::next_younger_sp_or_null(intptr_t* old_sp, intptr_t* sp) { + + intptr_t* previous_sp = NULL; + intptr_t* orig_sp = sp; + + int max_frames = (old_sp - sp) / 16; // Minimum frame size is 16 + int max_frame2 = max_frames; + while(sp != old_sp && sp_is_valid(old_sp, orig_sp, sp)) { + if (max_frames-- <= 0) + // too many frames have gone by; invalid parameters given to this function + break; + previous_sp = sp; + sp = (intptr_t*)sp[FP->sp_offset_in_saved_window()]; + sp = (intptr_t*)((intptr_t)sp + STACK_BIAS); + } + + return (sp == old_sp ? previous_sp : NULL); +} + +/* + Determine if "sp" is a valid stack pointer. "sp" is assumed to be younger than + "valid_sp". So if "sp" is valid itself then it should be possible to walk frames + from "sp" to "valid_sp". The assumption is that the registers windows for the + thread stack in question are flushed. +*/ +bool frame::is_valid_stack_pointer(intptr_t* valid_sp, intptr_t* sp) { + return next_younger_sp_or_null(valid_sp, sp) != NULL; +} + + +bool frame::interpreter_frame_equals_unpacked_fp(intptr_t* fp) { + assert(is_interpreted_frame(), "must be interpreter frame"); + return this->fp() == fp; +} + + +void frame::pd_gc_epilog() { + if (is_interpreted_frame()) { + // set constant pool cache entry for interpreter + methodOop m = interpreter_frame_method(); + + *interpreter_frame_cpoolcache_addr() = m->constants()->cache(); + } +} + + +bool frame::is_interpreted_frame_valid() const { +#ifdef CC_INTERP + // Is there anything to do? +#else + assert(is_interpreted_frame(), "Not an interpreted frame"); + // These are reasonable sanity checks + if (fp() == 0 || (intptr_t(fp()) & (2*wordSize-1)) != 0) { + return false; + } + if (sp() == 0 || (intptr_t(sp()) & (2*wordSize-1)) != 0) { + return false; + } + const intptr_t interpreter_frame_initial_sp_offset = interpreter_frame_vm_local_words; + if (fp() + interpreter_frame_initial_sp_offset < sp()) { + return false; + } + // These are hacks to keep us out of trouble. + // The problem with these is that they mask other problems + if (fp() <= sp()) { // this attempts to deal with unsigned comparison above + return false; + } + if (fp() - sp() > 4096) { // stack frames shouldn't be large. + return false; + } +#endif /* CC_INTERP */ + return true; +} + + +// Windows have been flushed on entry (but not marked). Capture the pc that +// is the return address to the frame that contains "sp" as its stack pointer. +// This pc resides in the called of the frame corresponding to "sp". +// As a side effect we mark this JavaFrameAnchor as having flushed the windows. +// This side effect lets us mark stacked JavaFrameAnchors (stacked in the +// call_helper) as flushed when we have flushed the windows for the most +// recent (i.e. current) JavaFrameAnchor. This saves useless flushing calls +// and lets us find the pc just once rather than multiple times as it did +// in the bad old _post_Java_state days. +// +void JavaFrameAnchor::capture_last_Java_pc(intptr_t* sp) { + if (last_Java_sp() != NULL && last_Java_pc() == NULL) { + // try and find the sp just younger than _last_Java_sp + intptr_t* _post_Java_sp = frame::next_younger_sp_or_null(last_Java_sp(), sp); + // Really this should never fail otherwise VM call must have non-standard + // frame linkage (bad) or stack is not properly flushed (worse). + guarantee(_post_Java_sp != NULL, "bad stack!"); + _last_Java_pc = (address) _post_Java_sp[ I7->sp_offset_in_saved_window()] + frame::pc_return_offset; + + } + set_window_flushed(); +} + +void JavaFrameAnchor::make_walkable(JavaThread* thread) { + if (walkable()) return; + // Eventually make an assert + guarantee(Thread::current() == (Thread*)thread, "only current thread can flush its registers"); + // We always flush in case the profiler wants it but we won't mark + // the windows as flushed unless we have a last_Java_frame + intptr_t* sp = StubRoutines::Sparc::flush_callers_register_windows_func()(); + if (last_Java_sp() != NULL ) { + capture_last_Java_pc(sp); + } +} + +intptr_t* frame::entry_frame_argument_at(int offset) const { + // convert offset to index to deal with tsi + int index = (Interpreter::expr_offset_in_bytes(offset)/wordSize); + + intptr_t* LSP = (intptr_t*) sp()[Lentry_args->sp_offset_in_saved_window()]; + return &LSP[index+1]; +} + + +BasicType frame::interpreter_frame_result(oop* oop_result, jvalue* value_result) { + assert(is_interpreted_frame(), "interpreted frame expected"); + methodOop method = interpreter_frame_method(); + BasicType type = method->result_type(); + + if (method->is_native()) { + // Prior to notifying the runtime of the method_exit the possible result + // value is saved to l_scratch and d_scratch. + +#ifdef CC_INTERP + interpreterState istate = get_interpreterState(); + intptr_t* l_scratch = (intptr_t*) &istate->_native_lresult; + intptr_t* d_scratch = (intptr_t*) &istate->_native_fresult; +#else /* CC_INTERP */ + intptr_t* l_scratch = fp() + interpreter_frame_l_scratch_fp_offset; + intptr_t* d_scratch = fp() + interpreter_frame_d_scratch_fp_offset; +#endif /* CC_INTERP */ + + address l_addr = (address)l_scratch; +#ifdef _LP64 + // On 64-bit the result for 1/8/16/32-bit result types is in the other + // word half + l_addr += wordSize/2; +#endif + + switch (type) { + case T_OBJECT: + case T_ARRAY: { +#ifdef CC_INTERP + *oop_result = istate->_oop_temp; +#else + oop obj = (oop) at(interpreter_frame_oop_temp_offset); + assert(obj == NULL || Universe::heap()->is_in(obj), "sanity check"); + *oop_result = obj; +#endif // CC_INTERP + break; + } + + case T_BOOLEAN : { jint* p = (jint*)l_addr; value_result->z = (jboolean)((*p) & 0x1); break; } + case T_BYTE : { jint* p = (jint*)l_addr; value_result->b = (jbyte)((*p) & 0xff); break; } + case T_CHAR : { jint* p = (jint*)l_addr; value_result->c = (jchar)((*p) & 0xffff); break; } + case T_SHORT : { jint* p = (jint*)l_addr; value_result->s = (jshort)((*p) & 0xffff); break; } + case T_INT : value_result->i = *(jint*)l_addr; break; + case T_LONG : value_result->j = *(jlong*)l_scratch; break; + case T_FLOAT : value_result->f = *(jfloat*)d_scratch; break; + case T_DOUBLE : value_result->d = *(jdouble*)d_scratch; break; + case T_VOID : /* Nothing to do */ break; + default : ShouldNotReachHere(); + } + } else { + intptr_t* tos_addr = interpreter_frame_tos_address(); + + switch(type) { + case T_OBJECT: + case T_ARRAY: { + oop obj = (oop)*tos_addr; + assert(obj == NULL || Universe::heap()->is_in(obj), "sanity check"); + *oop_result = obj; + break; + } + case T_BOOLEAN : { jint* p = (jint*)tos_addr; value_result->z = (jboolean)((*p) & 0x1); break; } + case T_BYTE : { jint* p = (jint*)tos_addr; value_result->b = (jbyte)((*p) & 0xff); break; } + case T_CHAR : { jint* p = (jint*)tos_addr; value_result->c = (jchar)((*p) & 0xffff); break; } + case T_SHORT : { jint* p = (jint*)tos_addr; value_result->s = (jshort)((*p) & 0xffff); break; } + case T_INT : value_result->i = *(jint*)tos_addr; break; + case T_LONG : value_result->j = *(jlong*)tos_addr; break; + case T_FLOAT : value_result->f = *(jfloat*)tos_addr; break; + case T_DOUBLE : value_result->d = *(jdouble*)tos_addr; break; + case T_VOID : /* Nothing to do */ break; + default : ShouldNotReachHere(); + } + }; + + return type; +} + +// Lesp pointer is one word lower than the top item on the stack. +intptr_t* frame::interpreter_frame_tos_at(jint offset) const { + int index = (Interpreter::expr_offset_in_bytes(offset)/wordSize) - 1; + return &interpreter_frame_tos_address()[index]; +} diff --git a/hotspot/src/cpu/sparc/vm/frame_sparc.hpp b/hotspot/src/cpu/sparc/vm/frame_sparc.hpp new file mode 100644 index 00000000000..11baf5e73a5 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/frame_sparc.hpp @@ -0,0 +1,312 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A frame represents a physical stack frame (an activation). Frames can be +// C or Java frames, and the Java frames can be interpreted or compiled. +// In contrast, vframes represent source-level activations, so that one physical frame +// can correspond to multiple source level frames because of inlining. +// A frame is comprised of {pc, sp, younger_sp} + + +// Layout of asm interpreter frame: +// +// 0xfffffff +// ...... +// [last extra incoming arg, (local # Nargs > 6 ? Nargs-1 : undef)] +// .. Note: incoming args are copied to local frame area upon entry +// [first extra incoming arg, (local # Nargs > 6 ? 6 : undef)] +// [6 words for C-arg storage (unused)] Are this and next one really needed? +// [C-aggregate-word (unused)] Yes, if want extra params to be in same place as C convention +// [16 words for register saving] <--- FP +// [interpreter_frame_vm_locals ] (see below) + +// Note: Llocals is always double-word aligned +// [first local i.e. local # 0] <-- Llocals +// ... +// [last local, i.e. local # Nlocals-1] + +// [monitors ] +// .... +// [monitors ] <-- Lmonitors (same as Llocals + 6*4 if none) +// (must be double-word aligned because +// monitor element size is constrained to +// doubleword) +// +// <-- Lesp (points 1 past TOS) +// [bottom word used for stack ] +// ... +// [top word used for stack] (first word of stack is double-word aligned) + +// [space for outgoing args (conservatively allocated as max_stack - 6 + interpreter_frame_extra_outgoing_argument_words)] +// [6 words for C-arg storage] +// [C-aggregate-word (unused)] +// [16 words for register saving] <--- SP +// ... +// 0x0000000 +// +// The in registers and local registers are preserved in a block at SP. +// +// The first six in registers (I0..I5) hold the first six locals. +// The locals are used as follows: +// Lesp first free element of expression stack +// (which grows towards __higher__ addresses) +// Lbcp is set to address of bytecode to execute +// It is accessed in the frame under the name "bcx". +// It may at times (during GC) be an index instead. +// Lmethod the method being interpreted +// Llocals the base pointer for accessing the locals array +// (lower-numbered locals have lower addresses) +// Lmonitors the base pointer for accessing active monitors +// Lcache a saved pointer to the method's constant pool cache +// +// +// When calling out to another method, +// G5_method is set to method to call, G5_inline_cache_klass may be set, +// parameters are put in O registers, and also extra parameters +// must be cleverly copied from the top of stack to the outgoing param area in the frame, +// ------------------------------ C++ interpreter ---------------------------------------- +// Layout of C++ interpreter frame: +// + + + +// All frames: + + public: + + enum { + // normal return address is 2 words past PC + pc_return_offset = 2 * BytesPerInstWord, + + // size of each block, in order of increasing address: + register_save_words = 16, +#ifdef _LP64 + callee_aggregate_return_pointer_words = 0, +#else + callee_aggregate_return_pointer_words = 1, +#endif + callee_register_argument_save_area_words = 6, + // memory_parameter_words = , + + // offset of each block, in order of increasing address: + // (note: callee_register_argument_save_area_words == Assembler::n_register_parameters) + register_save_words_sp_offset = 0, + callee_aggregate_return_pointer_sp_offset = register_save_words_sp_offset + register_save_words, + callee_register_argument_save_area_sp_offset = callee_aggregate_return_pointer_sp_offset + callee_aggregate_return_pointer_words, + memory_parameter_word_sp_offset = callee_register_argument_save_area_sp_offset + callee_register_argument_save_area_words, + varargs_offset = memory_parameter_word_sp_offset + }; + + private: + intptr_t* _younger_sp; // optional SP of callee (used to locate O7) + int _sp_adjustment_by_callee; // adjustment in words to SP by callee for making locals contiguous + + // Note: On SPARC, unlike Intel, the saved PC for a stack frame + // is stored at a __variable__ distance from that frame's SP. + // (In fact, it may be in the register save area of the callee frame, + // but that fact need not bother us.) Thus, we must store the + // address of that saved PC explicitly. On the other hand, SPARC + // stores the FP for a frame at a fixed offset from the frame's SP, + // so there is no need for a separate "frame::_fp" field. + + public: + // Accessors + + intptr_t* younger_sp() const { + assert(_younger_sp != NULL, "frame must possess a younger_sp"); + return _younger_sp; + } + + int callee_sp_adjustment() const { return _sp_adjustment_by_callee; } + void set_sp_adjustment_by_callee(int number_of_words) { _sp_adjustment_by_callee = number_of_words; } + + // Constructors + + // This constructor relies on the fact that the creator of a frame + // has flushed register windows which the frame will refer to, and + // that those register windows will not be reloaded until the frame is + // done reading and writing the stack. Moreover, if the "younger_sp" + // argument points into the register save area of the next younger + // frame (though it need not), the register window for that next + // younger frame must also stay flushed. (The caller is responsible + // for ensuring this.) + + frame(intptr_t* sp, intptr_t* younger_sp, bool younger_frame_adjusted_stack = false); + + // make a deficient frame which doesn't know where its PC is: + enum unpatchable_t { unpatchable }; + frame(intptr_t* sp, unpatchable_t, address pc = NULL, CodeBlob* cb = NULL); + + // Walk from sp outward looking for old_sp, and return old_sp's predecessor + // (i.e. return the sp from the frame where old_sp is the fp). + // Register windows are assumed to be flushed for the stack in question. + + static intptr_t* next_younger_sp_or_null(intptr_t* old_sp, intptr_t* sp); + + // Return true if sp is a younger sp in the stack described by valid_sp. + static bool is_valid_stack_pointer(intptr_t* valid_sp, intptr_t* sp); + + public: + // accessors for the instance variables + intptr_t* fp() const { return (intptr_t*) ((intptr_t)(sp()[FP->sp_offset_in_saved_window()]) + STACK_BIAS ); } + + // All frames + + intptr_t* fp_addr_at(int index) const { return &fp()[index]; } + intptr_t* sp_addr_at(int index) const { return &sp()[index]; } + intptr_t fp_at( int index) const { return *fp_addr_at(index); } + intptr_t sp_at( int index) const { return *sp_addr_at(index); } + + private: + inline address* I7_addr() const; + inline address* O7_addr() const; + + inline address* I0_addr() const; + inline address* O0_addr() const; + intptr_t* younger_sp_addr_at(int index) const { return &younger_sp()[index]; } + + public: + // access to SPARC arguments and argument registers + + // Assumes reg is an in/local register + intptr_t* register_addr(Register reg) const { + return sp_addr_at(reg->sp_offset_in_saved_window()); + } + + // Assumes reg is an out register + intptr_t* out_register_addr(Register reg) const { + return younger_sp_addr_at(reg->after_save()->sp_offset_in_saved_window()); + } + intptr_t* memory_param_addr(int param_ix, bool is_in) const { + int offset = callee_register_argument_save_area_sp_offset + param_ix; + if (is_in) + return fp_addr_at(offset); + else + return sp_addr_at(offset); + } + intptr_t* param_addr(int param_ix, bool is_in) const { + if (param_ix >= callee_register_argument_save_area_words) + return memory_param_addr(param_ix, is_in); + else if (is_in) + return register_addr(Argument(param_ix, true).as_register()); + else { + // the registers are stored in the next younger frame + // %%% is this really necessary? + ShouldNotReachHere(); + return NULL; + } + } + + + // Interpreter frames + + public: + // Asm interpreter +#ifndef CC_INTERP + enum interpreter_frame_vm_locals { + // 2 words, also used to save float regs across calls to C + interpreter_frame_d_scratch_fp_offset = -2, + interpreter_frame_l_scratch_fp_offset = -4, + interpreter_frame_padding_offset = -5, // for native calls only + interpreter_frame_oop_temp_offset = -6, // for native calls only + interpreter_frame_vm_locals_fp_offset = -6, // should be same as above, and should be zero mod 8 + + interpreter_frame_vm_local_words = -interpreter_frame_vm_locals_fp_offset, + + + // interpreter frame set-up needs to save 2 extra words in outgoing param area + // for class and jnienv arguments for native stubs (see nativeStubGen_sparc.cpp_ + + interpreter_frame_extra_outgoing_argument_words = 2 + }; +#else + enum interpreter_frame_vm_locals { + // 2 words, also used to save float regs across calls to C + interpreter_state_ptr_offset = 0, // Is in L0 (Lstate) in save area + interpreter_frame_mirror_offset = 1, // Is in L1 (Lmirror) in save area (for native calls only) + + // interpreter frame set-up needs to save 2 extra words in outgoing param area + // for class and jnienv arguments for native stubs (see nativeStubGen_sparc.cpp_ + + interpreter_frame_extra_outgoing_argument_words = 2 + }; +#endif /* CC_INTERP */ + + // the compiler frame has many of the same fields as the interpreter frame + // %%%%% factor out declarations of the shared fields + enum compiler_frame_fixed_locals { + compiler_frame_d_scratch_fp_offset = -2, + compiler_frame_vm_locals_fp_offset = -2, // should be same as above + + compiler_frame_vm_local_words = -compiler_frame_vm_locals_fp_offset + }; + + private: + + constantPoolCacheOop* frame::interpreter_frame_cpoolcache_addr() const; + +#ifndef CC_INTERP + + // where Lmonitors is saved: + BasicObjectLock** interpreter_frame_monitors_addr() const { + return (BasicObjectLock**) sp_addr_at(Lmonitors->sp_offset_in_saved_window()); + } + intptr_t** interpreter_frame_esp_addr() const { + return (intptr_t**)sp_addr_at(Lesp->sp_offset_in_saved_window()); + } + + inline void interpreter_frame_set_tos_address(intptr_t* x); + + + // %%%%% Another idea: instead of defining 3 fns per item, just define one returning a ref + + // monitors: + + // next two fns read and write Lmonitors value, + private: + BasicObjectLock* interpreter_frame_monitors() const { return *interpreter_frame_monitors_addr(); } + void interpreter_frame_set_monitors(BasicObjectLock* monitors) { *interpreter_frame_monitors_addr() = monitors; } +#else + public: + inline interpreterState get_interpreterState() const { + return ((interpreterState)sp_at(interpreter_state_ptr_offset)); + } + + +#endif /* CC_INTERP */ + + + + // Compiled frames + + public: + // Tells if this register can hold 64 bits on V9 (really, V8+). + static bool holds_a_doubleword(Register reg) { +#ifdef _LP64 + // return true; + return reg->is_out() || reg->is_global(); +#else + return reg->is_out() || reg->is_global(); +#endif + } diff --git a/hotspot/src/cpu/sparc/vm/frame_sparc.inline.hpp b/hotspot/src/cpu/sparc/vm/frame_sparc.inline.hpp new file mode 100644 index 00000000000..bc97771ff1b --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/frame_sparc.inline.hpp @@ -0,0 +1,297 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline functions for SPARC frames: + +// Constructors + +inline frame::frame() { + _pc = NULL; + _sp = NULL; + _younger_sp = NULL; + _cb = NULL; + _deopt_state = unknown; + _sp_adjustment_by_callee = 0; +} + +// Accessors: + +inline bool frame::equal(frame other) const { + bool ret = sp() == other.sp() + && fp() == other.fp() + && pc() == other.pc(); + assert(!ret || ret && cb() == other.cb() && _deopt_state == other._deopt_state, "inconsistent construction"); + return ret; +} + +// Return unique id for this frame. The id must have a value where we can distinguish +// identity and younger/older relationship. NULL represents an invalid (incomparable) +// frame. +inline intptr_t* frame::id(void) const { return unextended_sp(); } + +// Relationals on frames based +// Return true if the frame is younger (more recent activation) than the frame represented by id +inline bool frame::is_younger(intptr_t* id) const { assert(this->id() != NULL && id != NULL, "NULL frame id"); + return this->id() < id ; } + +// Return true if the frame is older (less recent activation) than the frame represented by id +inline bool frame::is_older(intptr_t* id) const { assert(this->id() != NULL && id != NULL, "NULL frame id"); + return this->id() > id ; } + +inline int frame::frame_size() const { return sender_sp() - sp(); } + +inline intptr_t* frame::link() const { return (intptr_t *)(fp()[FP->sp_offset_in_saved_window()] + STACK_BIAS); } + +inline void frame::set_link(intptr_t* addr) { assert(link()==addr, "frame nesting is controlled by hardware"); } + +inline intptr_t* frame::unextended_sp() const { return sp() + _sp_adjustment_by_callee; } + +// return address: + +inline address frame::sender_pc() const { return *I7_addr() + pc_return_offset; } + +inline address* frame::I7_addr() const { return (address*) &sp()[ I7->sp_offset_in_saved_window()]; } +inline address* frame::I0_addr() const { return (address*) &sp()[ I0->sp_offset_in_saved_window()]; } + +inline address* frame::O7_addr() const { return (address*) &younger_sp()[ I7->sp_offset_in_saved_window()]; } +inline address* frame::O0_addr() const { return (address*) &younger_sp()[ I0->sp_offset_in_saved_window()]; } + +inline intptr_t* frame::sender_sp() const { return fp(); } + +// Used only in frame::oopmapreg_to_location +// This return a value in VMRegImpl::slot_size +inline int frame::pd_oop_map_offset_adjustment() const { + return _sp_adjustment_by_callee * VMRegImpl::slots_per_word; +} + +#ifdef CC_INTERP +inline intptr_t** frame::interpreter_frame_locals_addr() const { + interpreterState istate = get_interpreterState(); + return (intptr_t**) &istate->_locals; +} + +inline intptr_t* frame::interpreter_frame_bcx_addr() const { + interpreterState istate = get_interpreterState(); + return (intptr_t*) &istate->_bcp; +} + +inline intptr_t* frame::interpreter_frame_mdx_addr() const { + interpreterState istate = get_interpreterState(); + return (intptr_t*) &istate->_mdx; +} + +inline jint frame::interpreter_frame_expression_stack_direction() { return -1; } + +// bottom(base) of the expression stack (highest address) +inline intptr_t* frame::interpreter_frame_expression_stack() const { + return (intptr_t*)interpreter_frame_monitor_end() - 1; +} + +// top of expression stack (lowest address) +inline intptr_t* frame::interpreter_frame_tos_address() const { + interpreterState istate = get_interpreterState(); + return istate->_stack + 1; // Is this off by one? QQQ +} + +// monitor elements + +// in keeping with Intel side: end is lower in memory than begin; +// and beginning element is oldest element +// Also begin is one past last monitor. + +inline BasicObjectLock* frame::interpreter_frame_monitor_begin() const { + return get_interpreterState()->monitor_base(); +} + +inline BasicObjectLock* frame::interpreter_frame_monitor_end() const { + return (BasicObjectLock*) get_interpreterState()->stack_base(); +} + + +inline int frame::interpreter_frame_monitor_size() { + return round_to(BasicObjectLock::size(), WordsPerLong); +} + +inline methodOop* frame::interpreter_frame_method_addr() const { + interpreterState istate = get_interpreterState(); + return &istate->_method; +} + + +// Constant pool cache + +// where LcpoolCache is saved: +inline constantPoolCacheOop* frame::interpreter_frame_cpoolcache_addr() const { + interpreterState istate = get_interpreterState(); + return &istate->_constants; // should really use accessor + } + +inline constantPoolCacheOop* frame::interpreter_frame_cache_addr() const { + interpreterState istate = get_interpreterState(); + return &istate->_constants; +} + +#else // !CC_INTERP + +inline intptr_t** frame::interpreter_frame_locals_addr() const { + return (intptr_t**) sp_addr_at( Llocals->sp_offset_in_saved_window()); +} + +inline intptr_t* frame::interpreter_frame_bcx_addr() const { + // %%%%% reinterpreting Lbcp as a bcx + return (intptr_t*) sp_addr_at( Lbcp->sp_offset_in_saved_window()); +} + +inline intptr_t* frame::interpreter_frame_mdx_addr() const { + // %%%%% reinterpreting ImethodDataPtr as a mdx + return (intptr_t*) sp_addr_at( ImethodDataPtr->sp_offset_in_saved_window()); +} + +inline jint frame::interpreter_frame_expression_stack_direction() { return -1; } + +// bottom(base) of the expression stack (highest address) +inline intptr_t* frame::interpreter_frame_expression_stack() const { + return (intptr_t*)interpreter_frame_monitors() - 1; +} + +// top of expression stack (lowest address) +inline intptr_t* frame::interpreter_frame_tos_address() const { + return *interpreter_frame_esp_addr() + 1; +} + +inline void frame::interpreter_frame_set_tos_address( intptr_t* x ) { + *interpreter_frame_esp_addr() = x - 1; +} + +// monitor elements + +// in keeping with Intel side: end is lower in memory than begin; +// and beginning element is oldest element +// Also begin is one past last monitor. + +inline BasicObjectLock* frame::interpreter_frame_monitor_begin() const { + int rounded_vm_local_words = round_to(frame::interpreter_frame_vm_local_words, WordsPerLong); + return (BasicObjectLock *)fp_addr_at(-rounded_vm_local_words); +} + +inline BasicObjectLock* frame::interpreter_frame_monitor_end() const { + return interpreter_frame_monitors(); +} + + +inline void frame::interpreter_frame_set_monitor_end(BasicObjectLock* value) { + interpreter_frame_set_monitors(value); +} + +inline int frame::interpreter_frame_monitor_size() { + return round_to(BasicObjectLock::size(), WordsPerLong); +} + +inline methodOop* frame::interpreter_frame_method_addr() const { + return (methodOop*)sp_addr_at( Lmethod->sp_offset_in_saved_window()); +} + + +// Constant pool cache + +// where LcpoolCache is saved: +inline constantPoolCacheOop* frame::interpreter_frame_cpoolcache_addr() const { + return (constantPoolCacheOop*)sp_addr_at(LcpoolCache->sp_offset_in_saved_window()); + } + +inline constantPoolCacheOop* frame::interpreter_frame_cache_addr() const { + return (constantPoolCacheOop*)sp_addr_at( LcpoolCache->sp_offset_in_saved_window()); +} +#endif // CC_INTERP + + +inline JavaCallWrapper* frame::entry_frame_call_wrapper() const { + // note: adjust this code if the link argument in StubGenerator::call_stub() changes! + const Argument link = Argument(0, false); + return (JavaCallWrapper*)sp()[link.as_in().as_register()->sp_offset_in_saved_window()]; +} + + +inline int frame::local_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors) { + // always allocate non-argument locals 0..5 as if they were arguments: + int allocated_above_frame = nof_args; + if (allocated_above_frame < callee_register_argument_save_area_words) + allocated_above_frame = callee_register_argument_save_area_words; + if (allocated_above_frame > max_nof_locals) + allocated_above_frame = max_nof_locals; + + // Note: monitors (BasicLock blocks) are never allocated in argument slots + //assert(local_index >= 0 && local_index < max_nof_locals, "bad local index"); + if (local_index < allocated_above_frame) + return local_index + callee_register_argument_save_area_sp_offset; + else + return local_index - (max_nof_locals + max_nof_monitors*2) + compiler_frame_vm_locals_fp_offset; +} + +inline int frame::monitor_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors) { + assert(local_index >= max_nof_locals && ((local_index - max_nof_locals) & 1) && (local_index - max_nof_locals) < max_nof_monitors*2, "bad monitor index"); + + // The compiler uses the __higher__ of two indexes allocated to the monitor. + // Increasing local indexes are mapped to increasing memory locations, + // so the start of the BasicLock is associated with the __lower__ index. + + int offset = (local_index-1) - (max_nof_locals + max_nof_monitors*2) + compiler_frame_vm_locals_fp_offset; + + // We allocate monitors aligned zero mod 8: + assert((offset & 1) == 0, "monitor must be an an even address."); + // This works because all monitors are allocated after + // all locals, and because the highest address corresponding to any + // monitor index is always even. + assert((compiler_frame_vm_locals_fp_offset & 1) == 0, "end of monitors must be even address"); + + return offset; +} + +inline int frame::min_local_offset_for_compiler(int nof_args, int max_nof_locals, int max_nof_monitors) { + // always allocate non-argument locals 0..5 as if they were arguments: + int allocated_above_frame = nof_args; + if (allocated_above_frame < callee_register_argument_save_area_words) + allocated_above_frame = callee_register_argument_save_area_words; + if (allocated_above_frame > max_nof_locals) + allocated_above_frame = max_nof_locals; + + int allocated_in_frame = (max_nof_locals + max_nof_monitors*2) - allocated_above_frame; + + return compiler_frame_vm_locals_fp_offset - allocated_in_frame; +} + +// On SPARC, the %lN and %iN registers are non-volatile. +inline bool frame::volatile_across_calls(Register reg) { + // This predicate is (presently) applied only to temporary registers, + // and so it need not recognize non-volatile globals. + return reg->is_out() || reg->is_global(); +} + +inline oop frame::saved_oop_result(RegisterMap* map) const { + return *((oop*) map->location(O0->as_VMReg())); +} + +inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { + *((oop*) map->location(O0->as_VMReg())) = obj; +} diff --git a/hotspot/src/cpu/sparc/vm/globalDefinitions_sparc.hpp b/hotspot/src/cpu/sparc/vm/globalDefinitions_sparc.hpp new file mode 100644 index 00000000000..c9b46c1e6a7 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/globalDefinitions_sparc.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Size of Sparc Instructions +const int BytesPerInstWord = 4; + +const int StackAlignmentInBytes = (2*wordSize); diff --git a/hotspot/src/cpu/sparc/vm/globals_sparc.hpp b/hotspot/src/cpu/sparc/vm/globals_sparc.hpp new file mode 100644 index 00000000000..9d1bd7ac26f --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/globals_sparc.hpp @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for platform dependent flags used by the runtime system. +// (see globals.hpp) +// + +// For sparc we do not do call backs when a thread is in the interpreter, because the +// interpreter dispatch needs at least two instructions - first to load the dispatch address +// in a register, and second to jmp. The swapping of the dispatch table may occur _after_ +// the load of the dispatch address and hence the jmp would still go to the location +// according to the prior table. So, we let the thread continue and let it block by itself. +define_pd_global(bool, DontYieldALot, true); // yield no more than 100 times per second +define_pd_global(bool, ConvertSleepToYield, false); // do not convert sleep(0) to yield. Helps GUI +define_pd_global(bool, ShareVtableStubs, false); // improves performance markedly for mtrt and compress +define_pd_global(bool, CountInterpCalls, false); // not implemented in the interpreter +define_pd_global(bool, NeedsDeoptSuspend, true); // register window machines need this + +define_pd_global(bool, ImplicitNullChecks, true); // Generate code for implicit null checks +define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs past to check cast + +define_pd_global(intx, CodeEntryAlignment, 32); +define_pd_global(uintx, TLABSize, 0); +define_pd_global(uintx, NewSize, ScaleForWordSize((2048 * K) + (2 * (64 * K)))); +define_pd_global(intx, SurvivorRatio, 8); +define_pd_global(intx, InlineFrequencyCount, 50); // we can use more inlining on the SPARC +#ifdef _LP64 +// Stack slots are 2X larger in LP64 than in the 32 bit VM. +define_pd_global(intx, ThreadStackSize, 1024); +define_pd_global(intx, VMThreadStackSize, 1024); +#else +define_pd_global(intx, ThreadStackSize, 512); +define_pd_global(intx, VMThreadStackSize, 512); +#endif + +define_pd_global(intx, StackYellowPages, 2); +define_pd_global(intx, StackRedPages, 1); +define_pd_global(intx, StackShadowPages, 3 DEBUG_ONLY(+1)); + +define_pd_global(intx, PreInflateSpin, 40); // Determined by running design center + +define_pd_global(bool, RewriteBytecodes, true); +define_pd_global(bool, RewriteFrequentPairs, true); diff --git a/hotspot/src/cpu/sparc/vm/icBuffer_sparc.cpp b/hotspot/src/cpu/sparc/vm/icBuffer_sparc.cpp new file mode 100644 index 00000000000..a95dca542b6 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/icBuffer_sparc.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_icBuffer_sparc.cpp.incl" + +int InlineCacheBuffer::ic_stub_code_size() { +#ifdef _LP64 + if (TraceJumps) return 600 * wordSize; + return (NativeMovConstReg::instruction_size + // sethi;add + NativeJump::instruction_size + // sethi; jmp; delay slot + (1*BytesPerInstWord) + 1); // flush + 1 extra byte +#else + if (TraceJumps) return 300 * wordSize; + return (2+2+ 1) * wordSize + 1; // set/jump_to/nop + 1 byte so that code_end can be set in CodeBuffer +#endif +} + +void InlineCacheBuffer::assemble_ic_buffer_code(address code_begin, oop cached_oop, address entry_point) { + ResourceMark rm; + CodeBuffer code(code_begin, ic_stub_code_size()); + MacroAssembler* masm = new MacroAssembler(&code); + // note: even though the code contains an embedded oop, we do not need reloc info + // because + // (1) the oop is old (i.e., doesn't matter for scavenges) + // (2) these ICStubs are removed *before* a GC happens, so the roots disappear + assert(cached_oop == NULL || cached_oop->is_perm(), "must be old oop"); + Address cached_oop_addr(G5_inline_cache_reg, address(cached_oop)); + // Force the sethi to generate the fixed sequence so next_instruction_address works + masm->sethi(cached_oop_addr, true /* ForceRelocatable */ ); + masm->add(cached_oop_addr, G5_inline_cache_reg); + assert(G3_scratch != G5_method, "Do not clobber the method oop in the transition stub"); + assert(G3_scratch != G5_inline_cache_reg, "Do not clobber the inline cache register in the transition stub"); + Address entry(G3_scratch, entry_point); + masm->JUMP(entry, 0); + masm->delayed()->nop(); + masm->flush(); +} + + +address InlineCacheBuffer::ic_buffer_entry_point(address code_begin) { + NativeMovConstReg* move = nativeMovConstReg_at(code_begin); // creation also verifies the object + NativeJump* jump = nativeJump_at(move->next_instruction_address()); + return jump->jump_destination(); +} + + +oop InlineCacheBuffer::ic_buffer_cached_oop(address code_begin) { + NativeMovConstReg* move = nativeMovConstReg_at(code_begin); // creation also verifies the object + NativeJump* jump = nativeJump_at(move->next_instruction_address()); + return (oop)move->data(); +} diff --git a/hotspot/src/cpu/sparc/vm/icache_sparc.cpp b/hotspot/src/cpu/sparc/vm/icache_sparc.cpp new file mode 100644 index 00000000000..68f263efa01 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/icache_sparc.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_icache_sparc.cpp.incl" + +#define __ _masm-> + +void ICacheStubGenerator::generate_icache_flush( + ICache::flush_icache_stub_t* flush_icache_stub +) { + StubCodeMark mark(this, "ICache", "flush_icache_stub"); + address start = __ pc(); + + Label L; + __ bind(L); + __ flush( O0, G0 ); + __ deccc( O1 ); + __ br(Assembler::positive, false, Assembler::pn, L); + __ delayed()->inc( O0, 8 ); + __ retl(false); + __ delayed()->mov( O2, O0 ); // handshake with caller to make sure it happened! + + // Must be set here so StubCodeMark destructor can call the flush stub. + *flush_icache_stub = (ICache::flush_icache_stub_t)start; +}; + +#undef __ diff --git a/hotspot/src/cpu/sparc/vm/icache_sparc.hpp b/hotspot/src/cpu/sparc/vm/icache_sparc.hpp new file mode 100644 index 00000000000..f759ec945f1 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/icache_sparc.hpp @@ -0,0 +1,38 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Interface for updating the instruction cache. Whenever the VM modifies +// code, part of the processor instruction cache potentially has to be flushed. + + +class ICache : public AbstractICache { + public: + enum { + stub_size = 160, // Size of the icache flush stub in bytes + line_size = 8, // flush instruction affects a dword + log2_line_size = 3 // log2(line_size) + }; + + // Use default implementation +}; diff --git a/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp b/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp new file mode 100644 index 00000000000..5a868c82e03 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp @@ -0,0 +1,2595 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interp_masm_sparc.cpp.incl" + +#ifndef CC_INTERP +#ifndef FAST_DISPATCH +#define FAST_DISPATCH 1 +#endif +#undef FAST_DISPATCH + +// Implementation of InterpreterMacroAssembler + +// This file specializes the assember with interpreter-specific macros + +const Address InterpreterMacroAssembler::l_tmp( FP, 0, (frame::interpreter_frame_l_scratch_fp_offset * wordSize ) + STACK_BIAS); +const Address InterpreterMacroAssembler::d_tmp( FP, 0, (frame::interpreter_frame_d_scratch_fp_offset * wordSize) + STACK_BIAS); + +#else // CC_INTERP +#ifndef STATE +#define STATE(field_name) Lstate, in_bytes(byte_offset_of(BytecodeInterpreter, field_name)) +#endif // STATE + +#endif // CC_INTERP + +void InterpreterMacroAssembler::compute_extra_locals_size_in_bytes(Register args_size, Register locals_size, Register delta) { + // Note: this algorithm is also used by C1's OSR entry sequence. + // Any changes should also be applied to CodeEmitter::emit_osr_entry(). + assert_different_registers(args_size, locals_size); + // max_locals*2 for TAGS. Assumes that args_size has already been adjusted. + if (TaggedStackInterpreter) sll(locals_size, 1, locals_size); + subcc(locals_size, args_size, delta);// extra space for non-arguments locals in words + // Use br/mov combination because it works on both V8 and V9 and is + // faster. + Label skip_move; + br(Assembler::negative, true, Assembler::pt, skip_move); + delayed()->mov(G0, delta); + bind(skip_move); + round_to(delta, WordsPerLong); // make multiple of 2 (SP must be 2-word aligned) + sll(delta, LogBytesPerWord, delta); // extra space for locals in bytes +} + +#ifndef CC_INTERP + +// Dispatch code executed in the prolog of a bytecode which does not do it's +// own dispatch. The dispatch address is computed and placed in IdispatchAddress +void InterpreterMacroAssembler::dispatch_prolog(TosState state, int bcp_incr) { + assert_not_delayed(); +#ifdef FAST_DISPATCH + // FAST_DISPATCH and ProfileInterpreter are mutually exclusive since + // they both use I2. + assert(!ProfileInterpreter, "FAST_DISPATCH and +ProfileInterpreter are mutually exclusive"); + ldub(Lbcp, bcp_incr, Lbyte_code); // load next bytecode + add(Lbyte_code, Interpreter::distance_from_dispatch_table(state), Lbyte_code); + // add offset to correct dispatch table + sll(Lbyte_code, LogBytesPerWord, Lbyte_code); // multiply by wordSize + ld_ptr(IdispatchTables, Lbyte_code, IdispatchAddress);// get entry addr +#else + ldub( Lbcp, bcp_incr, Lbyte_code); // load next bytecode + // dispatch table to use + Address tbl(G3_scratch, (address)Interpreter::dispatch_table(state)); + + sethi(tbl); + sll(Lbyte_code, LogBytesPerWord, Lbyte_code); // multiply by wordSize + add(tbl, tbl.base(), 0); + ld_ptr( G3_scratch, Lbyte_code, IdispatchAddress); // get entry addr +#endif +} + + +// Dispatch code executed in the epilog of a bytecode which does not do it's +// own dispatch. The dispatch address in IdispatchAddress is used for the +// dispatch. +void InterpreterMacroAssembler::dispatch_epilog(TosState state, int bcp_incr) { + assert_not_delayed(); + verify_FPU(1, state); + interp_verify_oop(Otos_i, state, __FILE__, __LINE__); + jmp( IdispatchAddress, 0 ); + if (bcp_incr != 0) delayed()->inc(Lbcp, bcp_incr); + else delayed()->nop(); +} + + +void InterpreterMacroAssembler::dispatch_next(TosState state, int bcp_incr) { + // %%%% consider branching to a single shared dispatch stub (for each bcp_incr) + assert_not_delayed(); + ldub( Lbcp, bcp_incr, Lbyte_code); // load next bytecode + dispatch_Lbyte_code(state, Interpreter::dispatch_table(state), bcp_incr); +} + + +void InterpreterMacroAssembler::dispatch_next_noverify_oop(TosState state, int bcp_incr) { + // %%%% consider branching to a single shared dispatch stub (for each bcp_incr) + assert_not_delayed(); + ldub( Lbcp, bcp_incr, Lbyte_code); // load next bytecode + dispatch_Lbyte_code(state, Interpreter::dispatch_table(state), bcp_incr, false); +} + + +void InterpreterMacroAssembler::dispatch_via(TosState state, address* table) { + // load current bytecode + assert_not_delayed(); + ldub( Lbcp, 0, Lbyte_code); // load next bytecode + dispatch_base(state, table); +} + + +void InterpreterMacroAssembler::call_VM_leaf_base( + Register java_thread, + address entry_point, + int number_of_arguments +) { + if (!java_thread->is_valid()) + java_thread = L7_thread_cache; + // super call + MacroAssembler::call_VM_leaf_base(java_thread, entry_point, number_of_arguments); +} + + +void InterpreterMacroAssembler::call_VM_base( + Register oop_result, + Register java_thread, + Register last_java_sp, + address entry_point, + int number_of_arguments, + bool check_exception +) { + if (!java_thread->is_valid()) + java_thread = L7_thread_cache; + // See class ThreadInVMfromInterpreter, which assumes that the interpreter + // takes responsibility for setting its own thread-state on call-out. + // However, ThreadInVMfromInterpreter resets the state to "in_Java". + + //save_bcp(); // save bcp + MacroAssembler::call_VM_base(oop_result, java_thread, last_java_sp, entry_point, number_of_arguments, check_exception); + //restore_bcp(); // restore bcp + //restore_locals(); // restore locals pointer +} + + +void InterpreterMacroAssembler::check_and_handle_popframe(Register scratch_reg) { + if (JvmtiExport::can_pop_frame()) { + Label L; + + // Check the "pending popframe condition" flag in the current thread + Address popframe_condition_addr(G2_thread, 0, in_bytes(JavaThread::popframe_condition_offset())); + ld(popframe_condition_addr, scratch_reg); + + // Initiate popframe handling only if it is not already being processed. If the flag + // has the popframe_processing bit set, it means that this code is called *during* popframe + // handling - we don't want to reenter. + btst(JavaThread::popframe_pending_bit, scratch_reg); + br(zero, false, pt, L); + delayed()->nop(); + btst(JavaThread::popframe_processing_bit, scratch_reg); + br(notZero, false, pt, L); + delayed()->nop(); + + // Call Interpreter::remove_activation_preserving_args_entry() to get the + // address of the same-named entrypoint in the generated interpreter code. + call_VM_leaf(noreg, CAST_FROM_FN_PTR(address, Interpreter::remove_activation_preserving_args_entry)); + + // Jump to Interpreter::_remove_activation_preserving_args_entry + jmpl(O0, G0, G0); + delayed()->nop(); + bind(L); + } +} + + +void InterpreterMacroAssembler::load_earlyret_value(TosState state) { + Register thr_state = G4_scratch; + ld_ptr(Address(G2_thread, 0, in_bytes(JavaThread::jvmti_thread_state_offset())), + thr_state); + const Address tos_addr(thr_state, 0, in_bytes(JvmtiThreadState::earlyret_tos_offset())); + const Address oop_addr(thr_state, 0, in_bytes(JvmtiThreadState::earlyret_oop_offset())); + const Address val_addr(thr_state, 0, in_bytes(JvmtiThreadState::earlyret_value_offset())); + switch (state) { + case ltos: ld_long(val_addr, Otos_l); break; + case atos: ld_ptr(oop_addr, Otos_l); + st_ptr(G0, oop_addr); break; + case btos: // fall through + case ctos: // fall through + case stos: // fall through + case itos: ld(val_addr, Otos_l1); break; + case ftos: ldf(FloatRegisterImpl::S, val_addr, Ftos_f); break; + case dtos: ldf(FloatRegisterImpl::D, val_addr, Ftos_d); break; + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } + // Clean up tos value in the jvmti thread state + or3(G0, ilgl, G3_scratch); + stw(G3_scratch, tos_addr); + st_long(G0, val_addr); + interp_verify_oop(Otos_i, state, __FILE__, __LINE__); +} + + +void InterpreterMacroAssembler::check_and_handle_earlyret(Register scratch_reg) { + if (JvmtiExport::can_force_early_return()) { + Label L; + Register thr_state = G3_scratch; + ld_ptr(Address(G2_thread, 0, in_bytes(JavaThread::jvmti_thread_state_offset())), + thr_state); + tst(thr_state); + br(zero, false, pt, L); // if (thread->jvmti_thread_state() == NULL) exit; + delayed()->nop(); + + // Initiate earlyret handling only if it is not already being processed. + // If the flag has the earlyret_processing bit set, it means that this code + // is called *during* earlyret handling - we don't want to reenter. + ld(Address(thr_state, 0, in_bytes(JvmtiThreadState::earlyret_state_offset())), + G4_scratch); + cmp(G4_scratch, JvmtiThreadState::earlyret_pending); + br(Assembler::notEqual, false, pt, L); + delayed()->nop(); + + // Call Interpreter::remove_activation_early_entry() to get the address of the + // same-named entrypoint in the generated interpreter code + Address tos_addr(thr_state, 0, in_bytes(JvmtiThreadState::earlyret_tos_offset())); + ld(tos_addr, Otos_l1); + call_VM_leaf(noreg, CAST_FROM_FN_PTR(address, Interpreter::remove_activation_early_entry), Otos_l1); + + // Jump to Interpreter::_remove_activation_early_entry + jmpl(O0, G0, G0); + delayed()->nop(); + bind(L); + } +} + + +void InterpreterMacroAssembler::super_call_VM_leaf(Register thread_cache, address entry_point, Register arg_1) { + mov(arg_1, O0); + MacroAssembler::call_VM_leaf_base(thread_cache, entry_point, 1); +} +#endif /* CC_INTERP */ + + +#ifndef CC_INTERP + +void InterpreterMacroAssembler::dispatch_base(TosState state, address* table) { + assert_not_delayed(); + dispatch_Lbyte_code(state, table); +} + + +void InterpreterMacroAssembler::dispatch_normal(TosState state) { + dispatch_base(state, Interpreter::normal_table(state)); +} + + +void InterpreterMacroAssembler::dispatch_only(TosState state) { + dispatch_base(state, Interpreter::dispatch_table(state)); +} + + +// common code to dispatch and dispatch_only +// dispatch value in Lbyte_code and increment Lbcp + +void InterpreterMacroAssembler::dispatch_Lbyte_code(TosState state, address* table, int bcp_incr, bool verify) { + verify_FPU(1, state); + // %%%%% maybe implement +VerifyActivationFrameSize here + //verify_thread(); //too slow; we will just verify on method entry & exit + if (verify) interp_verify_oop(Otos_i, state, __FILE__, __LINE__); +#ifdef FAST_DISPATCH + if (table == Interpreter::dispatch_table(state)) { + // use IdispatchTables + add(Lbyte_code, Interpreter::distance_from_dispatch_table(state), Lbyte_code); + // add offset to correct dispatch table + sll(Lbyte_code, LogBytesPerWord, Lbyte_code); // multiply by wordSize + ld_ptr(IdispatchTables, Lbyte_code, G3_scratch); // get entry addr + } else { +#endif + // dispatch table to use + Address tbl(G3_scratch, (address)table); + + sll(Lbyte_code, LogBytesPerWord, Lbyte_code); // multiply by wordSize + load_address(tbl); // compute addr of table + ld_ptr(G3_scratch, Lbyte_code, G3_scratch); // get entry addr +#ifdef FAST_DISPATCH + } +#endif + jmp( G3_scratch, 0 ); + if (bcp_incr != 0) delayed()->inc(Lbcp, bcp_incr); + else delayed()->nop(); +} + + +// Helpers for expression stack + +// Longs and doubles are Category 2 computational types in the +// JVM specification (section 3.11.1) and take 2 expression stack or +// local slots. +// Aligning them on 32 bit with tagged stacks is hard because the code generated +// for the dup* bytecodes depends on what types are already on the stack. +// If the types are split into the two stack/local slots, that is much easier +// (and we can use 0 for non-reference tags). + +// Known good alignment in _LP64 but unknown otherwise +void InterpreterMacroAssembler::load_unaligned_double(Register r1, int offset, FloatRegister d) { + assert_not_delayed(); + +#ifdef _LP64 + ldf(FloatRegisterImpl::D, r1, offset, d); +#else + ldf(FloatRegisterImpl::S, r1, offset, d); + ldf(FloatRegisterImpl::S, r1, offset + Interpreter::stackElementSize(), d->successor()); +#endif +} + +// Known good alignment in _LP64 but unknown otherwise +void InterpreterMacroAssembler::store_unaligned_double(FloatRegister d, Register r1, int offset) { + assert_not_delayed(); + +#ifdef _LP64 + stf(FloatRegisterImpl::D, d, r1, offset); + // store something more useful here + debug_only(stx(G0, r1, offset+Interpreter::stackElementSize());) +#else + stf(FloatRegisterImpl::S, d, r1, offset); + stf(FloatRegisterImpl::S, d->successor(), r1, offset + Interpreter::stackElementSize()); +#endif +} + + +// Known good alignment in _LP64 but unknown otherwise +void InterpreterMacroAssembler::load_unaligned_long(Register r1, int offset, Register rd) { + assert_not_delayed(); +#ifdef _LP64 + ldx(r1, offset, rd); +#else + ld(r1, offset, rd); + ld(r1, offset + Interpreter::stackElementSize(), rd->successor()); +#endif +} + +// Known good alignment in _LP64 but unknown otherwise +void InterpreterMacroAssembler::store_unaligned_long(Register l, Register r1, int offset) { + assert_not_delayed(); + +#ifdef _LP64 + stx(l, r1, offset); + // store something more useful here + debug_only(stx(G0, r1, offset+Interpreter::stackElementSize());) +#else + st(l, r1, offset); + st(l->successor(), r1, offset + Interpreter::stackElementSize()); +#endif +} + +#ifdef ASSERT +void InterpreterMacroAssembler::verify_stack_tag(frame::Tag t, + Register r, + Register scratch) { + if (TaggedStackInterpreter) { + Label ok, long_ok; + ld_ptr(Lesp, Interpreter::expr_tag_offset_in_bytes(0), r); + if (t == frame::TagCategory2) { + cmp(r, G0); + brx(Assembler::equal, false, Assembler::pt, long_ok); + delayed()->ld_ptr(Lesp, Interpreter::expr_tag_offset_in_bytes(1), r); + stop("stack long/double tag value bad"); + bind(long_ok); + cmp(r, G0); + } else if (t == frame::TagValue) { + cmp(r, G0); + } else { + assert_different_registers(r, scratch); + mov(t, scratch); + cmp(r, scratch); + } + brx(Assembler::equal, false, Assembler::pt, ok); + delayed()->nop(); + // Also compare if the stack value is zero, then the tag might + // not have been set coming from deopt. + ld_ptr(Lesp, Interpreter::expr_offset_in_bytes(0), r); + cmp(r, G0); + brx(Assembler::equal, false, Assembler::pt, ok); + delayed()->nop(); + stop("Stack tag value is bad"); + bind(ok); + } +} +#endif // ASSERT + +void InterpreterMacroAssembler::pop_i(Register r) { + assert_not_delayed(); + // Uses destination register r for scratch + debug_only(verify_stack_tag(frame::TagValue, r)); + ld(Lesp, Interpreter::expr_offset_in_bytes(0), r); + inc(Lesp, Interpreter::stackElementSize()); + debug_only(verify_esp(Lesp)); +} + +void InterpreterMacroAssembler::pop_ptr(Register r, Register scratch) { + assert_not_delayed(); + // Uses destination register r for scratch + debug_only(verify_stack_tag(frame::TagReference, r, scratch)); + ld_ptr(Lesp, Interpreter::expr_offset_in_bytes(0), r); + inc(Lesp, Interpreter::stackElementSize()); + debug_only(verify_esp(Lesp)); +} + +void InterpreterMacroAssembler::pop_l(Register r) { + assert_not_delayed(); + // Uses destination register r for scratch + debug_only(verify_stack_tag(frame::TagCategory2, r)); + load_unaligned_long(Lesp, Interpreter::expr_offset_in_bytes(0), r); + inc(Lesp, 2*Interpreter::stackElementSize()); + debug_only(verify_esp(Lesp)); +} + + +void InterpreterMacroAssembler::pop_f(FloatRegister f, Register scratch) { + assert_not_delayed(); + debug_only(verify_stack_tag(frame::TagValue, scratch)); + ldf(FloatRegisterImpl::S, Lesp, Interpreter::expr_offset_in_bytes(0), f); + inc(Lesp, Interpreter::stackElementSize()); + debug_only(verify_esp(Lesp)); +} + + +void InterpreterMacroAssembler::pop_d(FloatRegister f, Register scratch) { + assert_not_delayed(); + debug_only(verify_stack_tag(frame::TagCategory2, scratch)); + load_unaligned_double(Lesp, Interpreter::expr_offset_in_bytes(0), f); + inc(Lesp, 2*Interpreter::stackElementSize()); + debug_only(verify_esp(Lesp)); +} + + +// (Note use register first, then decrement so dec can be done during store stall) +void InterpreterMacroAssembler::tag_stack(Register r) { + if (TaggedStackInterpreter) { + st_ptr(r, Lesp, Interpreter::tag_offset_in_bytes()); + } +} + +void InterpreterMacroAssembler::tag_stack(frame::Tag t, Register r) { + if (TaggedStackInterpreter) { + assert (frame::TagValue == 0, "TagValue must be zero"); + if (t == frame::TagValue) { + st_ptr(G0, Lesp, Interpreter::tag_offset_in_bytes()); + } else if (t == frame::TagCategory2) { + st_ptr(G0, Lesp, Interpreter::tag_offset_in_bytes()); + // Tag next slot down too + st_ptr(G0, Lesp, -Interpreter::stackElementSize() + Interpreter::tag_offset_in_bytes()); + } else { + assert_different_registers(r, O3); + mov(t, O3); + st_ptr(O3, Lesp, Interpreter::tag_offset_in_bytes()); + } + } +} + +void InterpreterMacroAssembler::push_i(Register r) { + assert_not_delayed(); + debug_only(verify_esp(Lesp)); + tag_stack(frame::TagValue, r); + st( r, Lesp, Interpreter::value_offset_in_bytes()); + dec( Lesp, Interpreter::stackElementSize()); +} + +void InterpreterMacroAssembler::push_ptr(Register r) { + assert_not_delayed(); + tag_stack(frame::TagReference, r); + st_ptr( r, Lesp, Interpreter::value_offset_in_bytes()); + dec( Lesp, Interpreter::stackElementSize()); +} + +void InterpreterMacroAssembler::push_ptr(Register r, Register tag) { + assert_not_delayed(); + tag_stack(tag); + st_ptr(r, Lesp, Interpreter::value_offset_in_bytes()); + dec( Lesp, Interpreter::stackElementSize()); +} + +// remember: our convention for longs in SPARC is: +// O0 (Otos_l1) has high-order part in first word, +// O1 (Otos_l2) has low-order part in second word + +void InterpreterMacroAssembler::push_l(Register r) { + assert_not_delayed(); + debug_only(verify_esp(Lesp)); + tag_stack(frame::TagCategory2, r); + // Longs are in stored in memory-correct order, even if unaligned. + // and may be separated by stack tags. + int offset = -Interpreter::stackElementSize() + Interpreter::value_offset_in_bytes(); + store_unaligned_long(r, Lesp, offset); + dec(Lesp, 2 * Interpreter::stackElementSize()); +} + + +void InterpreterMacroAssembler::push_f(FloatRegister f) { + assert_not_delayed(); + debug_only(verify_esp(Lesp)); + tag_stack(frame::TagValue, Otos_i); + stf(FloatRegisterImpl::S, f, Lesp, Interpreter::value_offset_in_bytes()); + dec(Lesp, Interpreter::stackElementSize()); +} + + +void InterpreterMacroAssembler::push_d(FloatRegister d) { + assert_not_delayed(); + debug_only(verify_esp(Lesp)); + tag_stack(frame::TagCategory2, Otos_i); + // Longs are in stored in memory-correct order, even if unaligned. + // and may be separated by stack tags. + int offset = -Interpreter::stackElementSize() + Interpreter::value_offset_in_bytes(); + store_unaligned_double(d, Lesp, offset); + dec(Lesp, 2 * Interpreter::stackElementSize()); +} + + +void InterpreterMacroAssembler::push(TosState state) { + interp_verify_oop(Otos_i, state, __FILE__, __LINE__); + switch (state) { + case atos: push_ptr(); break; + case btos: push_i(); break; + case ctos: + case stos: push_i(); break; + case itos: push_i(); break; + case ltos: push_l(); break; + case ftos: push_f(); break; + case dtos: push_d(); break; + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } +} + + +void InterpreterMacroAssembler::pop(TosState state) { + switch (state) { + case atos: pop_ptr(); break; + case btos: pop_i(); break; + case ctos: + case stos: pop_i(); break; + case itos: pop_i(); break; + case ltos: pop_l(); break; + case ftos: pop_f(); break; + case dtos: pop_d(); break; + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } + interp_verify_oop(Otos_i, state, __FILE__, __LINE__); +} + + +// Tagged stack helpers for swap and dup +void InterpreterMacroAssembler::load_ptr_and_tag(int n, Register val, + Register tag) { + ld_ptr(Lesp, Interpreter::expr_offset_in_bytes(n), val); + if (TaggedStackInterpreter) { + ld_ptr(Lesp, Interpreter::expr_tag_offset_in_bytes(n), tag); + } +} +void InterpreterMacroAssembler::store_ptr_and_tag(int n, Register val, + Register tag) { + st_ptr(val, Lesp, Interpreter::expr_offset_in_bytes(n)); + if (TaggedStackInterpreter) { + st_ptr(tag, Lesp, Interpreter::expr_tag_offset_in_bytes(n)); + } +} + + +void InterpreterMacroAssembler::load_receiver(Register param_count, + Register recv) { + + sll(param_count, Interpreter::logStackElementSize(), param_count); + if (TaggedStackInterpreter) { + add(param_count, Interpreter::value_offset_in_bytes(), param_count); // get obj address + } + ld_ptr(Lesp, param_count, recv); // gets receiver Oop +} + +void InterpreterMacroAssembler::empty_expression_stack() { + // Reset Lesp. + sub( Lmonitors, wordSize, Lesp ); + + // Reset SP by subtracting more space from Lesp. + Label done; + + const Address max_stack (Lmethod, 0, in_bytes(methodOopDesc::max_stack_offset())); + const Address access_flags(Lmethod, 0, in_bytes(methodOopDesc::access_flags_offset())); + + verify_oop(Lmethod); + + + assert( G4_scratch != Gframe_size, + "Only you can prevent register aliasing!"); + + // A native does not need to do this, since its callee does not change SP. + ld(access_flags, Gframe_size); + btst(JVM_ACC_NATIVE, Gframe_size); + br(Assembler::notZero, false, Assembler::pt, done); + delayed()->nop(); + + // + // Compute max expression stack+register save area + // + lduh( max_stack, Gframe_size ); + if (TaggedStackInterpreter) sll ( Gframe_size, 1, Gframe_size); // max_stack * 2 for TAGS + add( Gframe_size, frame::memory_parameter_word_sp_offset, Gframe_size ); + + // + // now set up a stack frame with the size computed above + // + //round_to( Gframe_size, WordsPerLong ); // -- moved down to the "and" below + sll( Gframe_size, LogBytesPerWord, Gframe_size ); + sub( Lesp, Gframe_size, Gframe_size ); + and3( Gframe_size, -(2 * wordSize), Gframe_size ); // align SP (downwards) to an 8/16-byte boundary + debug_only(verify_sp(Gframe_size, G4_scratch)); +#ifdef _LP64 + sub(Gframe_size, STACK_BIAS, Gframe_size ); +#endif + mov(Gframe_size, SP); + + bind(done); +} + + +#ifdef ASSERT +void InterpreterMacroAssembler::verify_sp(Register Rsp, Register Rtemp) { + Label Bad, OK; + + // Saved SP must be aligned. +#ifdef _LP64 + btst(2*BytesPerWord-1, Rsp); +#else + btst(LongAlignmentMask, Rsp); +#endif + br(Assembler::notZero, false, Assembler::pn, Bad); + delayed()->nop(); + + // Saved SP, plus register window size, must not be above FP. + add(Rsp, frame::register_save_words * wordSize, Rtemp); +#ifdef _LP64 + sub(Rtemp, STACK_BIAS, Rtemp); // Bias Rtemp before cmp to FP +#endif + cmp(Rtemp, FP); + brx(Assembler::greaterUnsigned, false, Assembler::pn, Bad); + delayed()->nop(); + + // Saved SP must not be ridiculously below current SP. + size_t maxstack = MAX2(JavaThread::stack_size_at_create(), (size_t) 4*K*K); + set(maxstack, Rtemp); + sub(SP, Rtemp, Rtemp); +#ifdef _LP64 + add(Rtemp, STACK_BIAS, Rtemp); // Unbias Rtemp before cmp to Rsp +#endif + cmp(Rsp, Rtemp); + brx(Assembler::lessUnsigned, false, Assembler::pn, Bad); + delayed()->nop(); + + br(Assembler::always, false, Assembler::pn, OK); + delayed()->nop(); + + bind(Bad); + stop("on return to interpreted call, restored SP is corrupted"); + + bind(OK); +} + + +void InterpreterMacroAssembler::verify_esp(Register Resp) { + // about to read or write Resp[0] + // make sure it is not in the monitors or the register save area + Label OK1, OK2; + + cmp(Resp, Lmonitors); + brx(Assembler::lessUnsigned, true, Assembler::pt, OK1); + delayed()->sub(Resp, frame::memory_parameter_word_sp_offset * wordSize, Resp); + stop("too many pops: Lesp points into monitor area"); + bind(OK1); +#ifdef _LP64 + sub(Resp, STACK_BIAS, Resp); +#endif + cmp(Resp, SP); + brx(Assembler::greaterEqualUnsigned, false, Assembler::pt, OK2); + delayed()->add(Resp, STACK_BIAS + frame::memory_parameter_word_sp_offset * wordSize, Resp); + stop("too many pushes: Lesp points into register window"); + bind(OK2); +} +#endif // ASSERT + +// Load compiled (i2c) or interpreter entry when calling from interpreted and +// do the call. Centralized so that all interpreter calls will do the same actions. +// If jvmti single stepping is on for a thread we must not call compiled code. +void InterpreterMacroAssembler::call_from_interpreter(Register target, Register scratch, Register Rret) { + + // Assume we want to go compiled if available + + ld_ptr(G5_method, in_bytes(methodOopDesc::from_interpreted_offset()), target); + + if (JvmtiExport::can_post_interpreter_events()) { + // JVMTI events, such as single-stepping, are implemented partly by avoiding running + // compiled code in threads for which the event is enabled. Check here for + // interp_only_mode if these events CAN be enabled. + verify_thread(); + Label skip_compiled_code; + + const Address interp_only (G2_thread, 0, in_bytes(JavaThread::interp_only_mode_offset())); + + ld(interp_only, scratch); + tst(scratch); + br(Assembler::notZero, true, Assembler::pn, skip_compiled_code); + delayed()->ld_ptr(G5_method, in_bytes(methodOopDesc::interpreter_entry_offset()), target); + bind(skip_compiled_code); + } + + // the i2c_adapters need methodOop in G5_method (right? %%%) + // do the call +#ifdef ASSERT + { + Label ok; + br_notnull(target, false, Assembler::pt, ok); + delayed()->nop(); + stop("null entry point"); + bind(ok); + } +#endif // ASSERT + + // Adjust Rret first so Llast_SP can be same as Rret + add(Rret, -frame::pc_return_offset, O7); + add(Lesp, BytesPerWord, Gargs); // setup parameter pointer + // Record SP so we can remove any stack space allocated by adapter transition + jmp(target, 0); + delayed()->mov(SP, Llast_SP); +} + +void InterpreterMacroAssembler::if_cmp(Condition cc, bool ptr_compare) { + assert_not_delayed(); + + Label not_taken; + if (ptr_compare) brx(cc, false, Assembler::pn, not_taken); + else br (cc, false, Assembler::pn, not_taken); + delayed()->nop(); + + TemplateTable::branch(false,false); + + bind(not_taken); + + profile_not_taken_branch(G3_scratch); +} + + +void InterpreterMacroAssembler::get_2_byte_integer_at_bcp( + int bcp_offset, + Register Rtmp, + Register Rdst, + signedOrNot is_signed, + setCCOrNot should_set_CC ) { + assert(Rtmp != Rdst, "need separate temp register"); + assert_not_delayed(); + switch (is_signed) { + default: ShouldNotReachHere(); + + case Signed: ldsb( Lbcp, bcp_offset, Rdst ); break; // high byte + case Unsigned: ldub( Lbcp, bcp_offset, Rdst ); break; // high byte + } + ldub( Lbcp, bcp_offset + 1, Rtmp ); // low byte + sll( Rdst, BitsPerByte, Rdst); + switch (should_set_CC ) { + default: ShouldNotReachHere(); + + case set_CC: orcc( Rdst, Rtmp, Rdst ); break; + case dont_set_CC: or3( Rdst, Rtmp, Rdst ); break; + } +} + + +void InterpreterMacroAssembler::get_4_byte_integer_at_bcp( + int bcp_offset, + Register Rtmp, + Register Rdst, + setCCOrNot should_set_CC ) { + assert(Rtmp != Rdst, "need separate temp register"); + assert_not_delayed(); + add( Lbcp, bcp_offset, Rtmp); + andcc( Rtmp, 3, G0); + Label aligned; + switch (should_set_CC ) { + default: ShouldNotReachHere(); + + case set_CC: break; + case dont_set_CC: break; + } + + br(Assembler::zero, true, Assembler::pn, aligned); +#ifdef _LP64 + delayed()->ldsw(Rtmp, 0, Rdst); +#else + delayed()->ld(Rtmp, 0, Rdst); +#endif + + ldub(Lbcp, bcp_offset + 3, Rdst); + ldub(Lbcp, bcp_offset + 2, Rtmp); sll(Rtmp, 8, Rtmp); or3(Rtmp, Rdst, Rdst); + ldub(Lbcp, bcp_offset + 1, Rtmp); sll(Rtmp, 16, Rtmp); or3(Rtmp, Rdst, Rdst); +#ifdef _LP64 + ldsb(Lbcp, bcp_offset + 0, Rtmp); sll(Rtmp, 24, Rtmp); +#else + // Unsigned load is faster than signed on some implementations + ldub(Lbcp, bcp_offset + 0, Rtmp); sll(Rtmp, 24, Rtmp); +#endif + or3(Rtmp, Rdst, Rdst ); + + bind(aligned); + if (should_set_CC == set_CC) tst(Rdst); +} + + +void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, Register tmp, int bcp_offset) { + assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + assert_different_registers(cache, tmp); + assert_not_delayed(); + get_2_byte_integer_at_bcp(bcp_offset, cache, tmp, Unsigned); + // convert from field index to ConstantPoolCacheEntry index + // and from word index to byte offset + sll(tmp, exact_log2(in_words(ConstantPoolCacheEntry::size()) * BytesPerWord), tmp); + add(LcpoolCache, tmp, cache); +} + + +void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset) { + assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + assert_different_registers(cache, tmp); + assert_not_delayed(); + get_2_byte_integer_at_bcp(bcp_offset, cache, tmp, Unsigned); + // convert from field index to ConstantPoolCacheEntry index + // and from word index to byte offset + sll(tmp, exact_log2(in_words(ConstantPoolCacheEntry::size()) * BytesPerWord), tmp); + // skip past the header + add(tmp, in_bytes(constantPoolCacheOopDesc::base_offset()), tmp); + // construct pointer to cache entry + add(LcpoolCache, tmp, cache); +} + + +// Generate a subtype check: branch to ok_is_subtype if sub_klass is +// a subtype of super_klass. Blows registers Rsub_klass, tmp1, tmp2. +void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass, + Register Rsuper_klass, + Register Rtmp1, + Register Rtmp2, + Register Rtmp3, + Label &ok_is_subtype ) { + Label not_subtype, loop; + + // Profile the not-null value's klass. + profile_typecheck(Rsub_klass, Rtmp1); + + // Load the super-klass's check offset into Rtmp1 + ld( Rsuper_klass, sizeof(oopDesc) + Klass::super_check_offset_offset_in_bytes(), Rtmp1 ); + // Load from the sub-klass's super-class display list, or a 1-word cache of + // the secondary superclass list, or a failing value with a sentinel offset + // if the super-klass is an interface or exceptionally deep in the Java + // hierarchy and we have to scan the secondary superclass list the hard way. + ld_ptr( Rsub_klass, Rtmp1, Rtmp2 ); + // See if we get an immediate positive hit + cmp( Rtmp2, Rsuper_klass ); + brx( Assembler::equal, false, Assembler::pt, ok_is_subtype ); + // In the delay slot, check for immediate negative hit + delayed()->cmp( Rtmp1, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() ); + br( Assembler::notEqual, false, Assembler::pt, not_subtype ); + // In the delay slot, check for self + delayed()->cmp( Rsub_klass, Rsuper_klass ); + brx( Assembler::equal, false, Assembler::pt, ok_is_subtype ); + + // Now do a linear scan of the secondary super-klass chain. + delayed()->ld_ptr( Rsub_klass, sizeof(oopDesc) + Klass::secondary_supers_offset_in_bytes(), Rtmp2 ); + + // Rtmp2 holds the objArrayOop of secondary supers. + ld( Rtmp2, arrayOopDesc::length_offset_in_bytes(), Rtmp1 );// Load the array length + // Check for empty secondary super list + tst(Rtmp1); + + // Top of search loop + bind( loop ); + br( Assembler::equal, false, Assembler::pn, not_subtype ); + delayed()->nop(); + // load next super to check + ld_ptr( Rtmp2, arrayOopDesc::base_offset_in_bytes(T_OBJECT), Rtmp3 ); + + // Bump array pointer forward one oop + add( Rtmp2, wordSize, Rtmp2 ); + // Look for Rsuper_klass on Rsub_klass's secondary super-class-overflow list + cmp( Rtmp3, Rsuper_klass ); + // A miss means we are NOT a subtype and need to keep looping + brx( Assembler::notEqual, false, Assembler::pt, loop ); + delayed()->deccc( Rtmp1 ); // dec trip counter in delay slot + // Falling out the bottom means we found a hit; we ARE a subtype + br( Assembler::always, false, Assembler::pt, ok_is_subtype ); + // Update the cache + delayed()->st_ptr( Rsuper_klass, Rsub_klass, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() ); + + bind(not_subtype); + profile_typecheck_failed(Rtmp1); +} + +// Separate these two to allow for delay slot in middle +// These are used to do a test and full jump to exception-throwing code. + +// %%%%% Could possibly reoptimize this by testing to see if could use +// a single conditional branch (i.e. if span is small enough. +// If you go that route, than get rid of the split and give up +// on the delay-slot hack. + +void InterpreterMacroAssembler::throw_if_not_1_icc( Condition ok_condition, + Label& ok ) { + assert_not_delayed(); + br(ok_condition, true, pt, ok); + // DELAY SLOT +} + +void InterpreterMacroAssembler::throw_if_not_1_xcc( Condition ok_condition, + Label& ok ) { + assert_not_delayed(); + bp( ok_condition, true, Assembler::xcc, pt, ok); + // DELAY SLOT +} + +void InterpreterMacroAssembler::throw_if_not_1_x( Condition ok_condition, + Label& ok ) { + assert_not_delayed(); + brx(ok_condition, true, pt, ok); + // DELAY SLOT +} + +void InterpreterMacroAssembler::throw_if_not_2( address throw_entry_point, + Register Rscratch, + Label& ok ) { + assert(throw_entry_point != NULL, "entry point must be generated by now"); + Address dest(Rscratch, throw_entry_point); + jump_to(dest); + delayed()->nop(); + bind(ok); +} + + +// And if you cannot use the delay slot, here is a shorthand: + +void InterpreterMacroAssembler::throw_if_not_icc( Condition ok_condition, + address throw_entry_point, + Register Rscratch ) { + Label ok; + if (ok_condition != never) { + throw_if_not_1_icc( ok_condition, ok); + delayed()->nop(); + } + throw_if_not_2( throw_entry_point, Rscratch, ok); +} +void InterpreterMacroAssembler::throw_if_not_xcc( Condition ok_condition, + address throw_entry_point, + Register Rscratch ) { + Label ok; + if (ok_condition != never) { + throw_if_not_1_xcc( ok_condition, ok); + delayed()->nop(); + } + throw_if_not_2( throw_entry_point, Rscratch, ok); +} +void InterpreterMacroAssembler::throw_if_not_x( Condition ok_condition, + address throw_entry_point, + Register Rscratch ) { + Label ok; + if (ok_condition != never) { + throw_if_not_1_x( ok_condition, ok); + delayed()->nop(); + } + throw_if_not_2( throw_entry_point, Rscratch, ok); +} + +// Check that index is in range for array, then shift index by index_shift, and put arrayOop + shifted_index into res +// Note: res is still shy of address by array offset into object. + +void InterpreterMacroAssembler::index_check_without_pop(Register array, Register index, int index_shift, Register tmp, Register res) { + assert_not_delayed(); + + verify_oop(array); +#ifdef _LP64 + // sign extend since tos (index) can be a 32bit value + sra(index, G0, index); +#endif // _LP64 + + // check array + Label ptr_ok; + tst(array); + throw_if_not_1_x( notZero, ptr_ok ); + delayed()->ld( array, arrayOopDesc::length_offset_in_bytes(), tmp ); // check index + throw_if_not_2( Interpreter::_throw_NullPointerException_entry, G3_scratch, ptr_ok); + + Label index_ok; + cmp(index, tmp); + throw_if_not_1_icc( lessUnsigned, index_ok ); + if (index_shift > 0) delayed()->sll(index, index_shift, index); + else delayed()->add(array, index, res); // addr - const offset in index + // convention: move aberrant index into G3_scratch for exception message + mov(index, G3_scratch); + throw_if_not_2( Interpreter::_throw_ArrayIndexOutOfBoundsException_entry, G4_scratch, index_ok); + + // add offset if didn't do it in delay slot + if (index_shift > 0) add(array, index, res); // addr - const offset in index +} + + +void InterpreterMacroAssembler::index_check(Register array, Register index, int index_shift, Register tmp, Register res) { + assert_not_delayed(); + + // pop array + pop_ptr(array); + + // check array + index_check_without_pop(array, index, index_shift, tmp, res); +} + + +void InterpreterMacroAssembler::get_constant_pool(Register Rdst) { + ld_ptr(Lmethod, in_bytes(methodOopDesc::constants_offset()), Rdst); +} + + +void InterpreterMacroAssembler::get_constant_pool_cache(Register Rdst) { + get_constant_pool(Rdst); + ld_ptr(Rdst, constantPoolOopDesc::cache_offset_in_bytes(), Rdst); +} + + +void InterpreterMacroAssembler::get_cpool_and_tags(Register Rcpool, Register Rtags) { + get_constant_pool(Rcpool); + ld_ptr(Rcpool, constantPoolOopDesc::tags_offset_in_bytes(), Rtags); +} + + +// unlock if synchronized method +// +// Unlock the receiver if this is a synchronized method. +// Unlock any Java monitors from syncronized blocks. +// +// If there are locked Java monitors +// If throw_monitor_exception +// throws IllegalMonitorStateException +// Else if install_monitor_exception +// installs IllegalMonitorStateException +// Else +// no error processing +void InterpreterMacroAssembler::unlock_if_synchronized_method(TosState state, + bool throw_monitor_exception, + bool install_monitor_exception) { + Label unlocked, unlock, no_unlock; + + // get the value of _do_not_unlock_if_synchronized into G1_scratch + const Address do_not_unlock_if_synchronized(G2_thread, 0, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + ldbool(do_not_unlock_if_synchronized, G1_scratch); + stbool(G0, do_not_unlock_if_synchronized); // reset the flag + + // check if synchronized method + const Address access_flags(Lmethod, 0, in_bytes(methodOopDesc::access_flags_offset())); + interp_verify_oop(Otos_i, state, __FILE__, __LINE__); + push(state); // save tos + ld(access_flags, G3_scratch); + btst(JVM_ACC_SYNCHRONIZED, G3_scratch); + br( zero, false, pt, unlocked); + delayed()->nop(); + + // Don't unlock anything if the _do_not_unlock_if_synchronized flag + // is set. + tstbool(G1_scratch); + br(Assembler::notZero, false, pn, no_unlock); + delayed()->nop(); + + // BasicObjectLock will be first in list, since this is a synchronized method. However, need + // to check that the object has not been unlocked by an explicit monitorexit bytecode. + + //Intel: if (throw_monitor_exception) ... else ... + // Entry already unlocked, need to throw exception + //... + + // pass top-most monitor elem + add( top_most_monitor(), O1 ); + + ld_ptr(O1, BasicObjectLock::obj_offset_in_bytes(), G3_scratch); + br_notnull(G3_scratch, false, pt, unlock); + delayed()->nop(); + + if (throw_monitor_exception) { + // Entry already unlocked need to throw an exception + MacroAssembler::call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + should_not_reach_here(); + } else { + // Monitor already unlocked during a stack unroll. + // If requested, install an illegal_monitor_state_exception. + // Continue with stack unrolling. + if (install_monitor_exception) { + MacroAssembler::call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::new_illegal_monitor_state_exception)); + } + ba(false, unlocked); + delayed()->nop(); + } + + bind(unlock); + + unlock_object(O1); + + bind(unlocked); + + // I0, I1: Might contain return value + + // Check that all monitors are unlocked + { Label loop, exception, entry, restart; + + Register Rmptr = O0; + Register Rtemp = O1; + Register Rlimit = Lmonitors; + const jint delta = frame::interpreter_frame_monitor_size() * wordSize; + assert( (delta & LongAlignmentMask) == 0, + "sizeof BasicObjectLock must be even number of doublewords"); + + #ifdef ASSERT + add(top_most_monitor(), Rmptr, delta); + { Label L; + // ensure that Rmptr starts out above (or at) Rlimit + cmp(Rmptr, Rlimit); + brx(Assembler::greaterEqualUnsigned, false, pn, L); + delayed()->nop(); + stop("monitor stack has negative size"); + bind(L); + } + #endif + bind(restart); + ba(false, entry); + delayed()-> + add(top_most_monitor(), Rmptr, delta); // points to current entry, starting with bottom-most entry + + // Entry is still locked, need to throw exception + bind(exception); + if (throw_monitor_exception) { + MacroAssembler::call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + should_not_reach_here(); + } else { + // Stack unrolling. Unlock object and if requested, install illegal_monitor_exception. + // Unlock does not block, so don't have to worry about the frame + unlock_object(Rmptr); + if (install_monitor_exception) { + MacroAssembler::call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::new_illegal_monitor_state_exception)); + } + ba(false, restart); + delayed()->nop(); + } + + bind(loop); + cmp(Rtemp, G0); // check if current entry is used + brx(Assembler::notEqual, false, pn, exception); + delayed()-> + dec(Rmptr, delta); // otherwise advance to next entry + #ifdef ASSERT + { Label L; + // ensure that Rmptr has not somehow stepped below Rlimit + cmp(Rmptr, Rlimit); + brx(Assembler::greaterEqualUnsigned, false, pn, L); + delayed()->nop(); + stop("ran off the end of the monitor stack"); + bind(L); + } + #endif + bind(entry); + cmp(Rmptr, Rlimit); // check if bottom reached + brx(Assembler::notEqual, true, pn, loop); // if not at bottom then check this entry + delayed()-> + ld_ptr(Rmptr, BasicObjectLock::obj_offset_in_bytes() - delta, Rtemp); + } + + bind(no_unlock); + pop(state); + interp_verify_oop(Otos_i, state, __FILE__, __LINE__); +} + + +// remove activation +// +// Unlock the receiver if this is a synchronized method. +// Unlock any Java monitors from syncronized blocks. +// Remove the activation from the stack. +// +// If there are locked Java monitors +// If throw_monitor_exception +// throws IllegalMonitorStateException +// Else if install_monitor_exception +// installs IllegalMonitorStateException +// Else +// no error processing +void InterpreterMacroAssembler::remove_activation(TosState state, + bool throw_monitor_exception, + bool install_monitor_exception) { + + unlock_if_synchronized_method(state, throw_monitor_exception, install_monitor_exception); + + // save result (push state before jvmti call and pop it afterwards) and notify jvmti + notify_method_exit(false, state, NotifyJVMTI); + + interp_verify_oop(Otos_i, state, __FILE__, __LINE__); + verify_oop(Lmethod); + verify_thread(); + + // return tos + assert(Otos_l1 == Otos_i, "adjust code below"); + switch (state) { +#ifdef _LP64 + case ltos: mov(Otos_l, Otos_l->after_save()); break; // O0 -> I0 +#else + case ltos: mov(Otos_l2, Otos_l2->after_save()); // fall through // O1 -> I1 +#endif + case btos: // fall through + case ctos: + case stos: // fall through + case atos: // fall through + case itos: mov(Otos_l1, Otos_l1->after_save()); break; // O0 -> I0 + case ftos: // fall through + case dtos: // fall through + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } + +#if defined(COMPILER2) && !defined(_LP64) + if (state == ltos) { + // C2 expects long results in G1 we can't tell if we're returning to interpreted + // or compiled so just be safe use G1 and O0/O1 + + // Shift bits into high (msb) of G1 + sllx(Otos_l1->after_save(), 32, G1); + // Zero extend low bits + srl (Otos_l2->after_save(), 0, Otos_l2->after_save()); + or3 (Otos_l2->after_save(), G1, G1); + } +#endif /* COMPILER2 */ + +} +#endif /* CC_INTERP */ + + +// Lock object +// +// Argument - lock_reg points to the BasicObjectLock to be used for locking, +// it must be initialized with the object to lock +void InterpreterMacroAssembler::lock_object(Register lock_reg, Register Object) { + if (UseHeavyMonitors) { + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); + } + else { + Register obj_reg = Object; + Register mark_reg = G4_scratch; + Register temp_reg = G1_scratch; + Address lock_addr = Address(lock_reg, 0, BasicObjectLock::lock_offset_in_bytes()); + Address mark_addr = Address(obj_reg, 0, oopDesc::mark_offset_in_bytes()); + Label done; + + Label slow_case; + + assert_different_registers(lock_reg, obj_reg, mark_reg, temp_reg); + + // load markOop from object into mark_reg + ld_ptr(mark_addr, mark_reg); + + if (UseBiasedLocking) { + biased_locking_enter(obj_reg, mark_reg, temp_reg, done, &slow_case); + } + + // get the address of basicLock on stack that will be stored in the object + // we need a temporary register here as we do not want to clobber lock_reg + // (cas clobbers the destination register) + mov(lock_reg, temp_reg); + // set mark reg to be (markOop of object | UNLOCK_VALUE) + or3(mark_reg, markOopDesc::unlocked_value, mark_reg); + // initialize the box (Must happen before we update the object mark!) + st_ptr(mark_reg, lock_addr, BasicLock::displaced_header_offset_in_bytes()); + // compare and exchange object_addr, markOop | 1, stack address of basicLock + assert(mark_addr.disp() == 0, "cas must take a zero displacement"); + casx_under_lock(mark_addr.base(), mark_reg, temp_reg, + (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + + // if the compare and exchange succeeded we are done (we saw an unlocked object) + cmp(mark_reg, temp_reg); + brx(Assembler::equal, true, Assembler::pt, done); + delayed()->nop(); + + // We did not see an unlocked object so try the fast recursive case + + // Check if owner is self by comparing the value in the markOop of object + // with the stack pointer + sub(temp_reg, SP, temp_reg); +#ifdef _LP64 + sub(temp_reg, STACK_BIAS, temp_reg); +#endif + assert(os::vm_page_size() > 0xfff, "page size too small - change the constant"); + + // Composite "andcc" test: + // (a) %sp -vs- markword proximity check, and, + // (b) verify mark word LSBs == 0 (Stack-locked). + // + // FFFFF003/FFFFFFFFFFFF003 is (markOopDesc::lock_mask_in_place | -os::vm_page_size()) + // Note that the page size used for %sp proximity testing is arbitrary and is + // unrelated to the actual MMU page size. We use a 'logical' page size of + // 4096 bytes. F..FFF003 is designed to fit conveniently in the SIMM13 immediate + // field of the andcc instruction. + andcc (temp_reg, 0xFFFFF003, G0) ; + + // if condition is true we are done and hence we can store 0 in the displaced + // header indicating it is a recursive lock and be done + brx(Assembler::zero, true, Assembler::pt, done); + delayed()->st_ptr(G0, lock_addr, BasicLock::displaced_header_offset_in_bytes()); + + // none of the above fast optimizations worked so we have to get into the + // slow case of monitor enter + bind(slow_case); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); + + bind(done); + } +} + +// Unlocks an object. Used in monitorexit bytecode and remove_activation. +// +// Argument - lock_reg points to the BasicObjectLock for lock +// Throw IllegalMonitorException if object is not locked by current thread +void InterpreterMacroAssembler::unlock_object(Register lock_reg) { + if (UseHeavyMonitors) { + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); + } else { + Register obj_reg = G3_scratch; + Register mark_reg = G4_scratch; + Register displaced_header_reg = G1_scratch; + Address lock_addr = Address(lock_reg, 0, BasicObjectLock::lock_offset_in_bytes()); + Address lockobj_addr = Address(lock_reg, 0, BasicObjectLock::obj_offset_in_bytes()); + Address mark_addr = Address(obj_reg, 0, oopDesc::mark_offset_in_bytes()); + Label done; + + if (UseBiasedLocking) { + // load the object out of the BasicObjectLock + ld_ptr(lockobj_addr, obj_reg); + biased_locking_exit(mark_addr, mark_reg, done, true); + st_ptr(G0, lockobj_addr); // free entry + } + + // Test first if we are in the fast recursive case + ld_ptr(lock_addr, displaced_header_reg, BasicLock::displaced_header_offset_in_bytes()); + br_null(displaced_header_reg, true, Assembler::pn, done); + delayed()->st_ptr(G0, lockobj_addr); // free entry + + // See if it is still a light weight lock, if so we just unlock + // the object and we are done + + if (!UseBiasedLocking) { + // load the object out of the BasicObjectLock + ld_ptr(lockobj_addr, obj_reg); + } + + // we have the displaced header in displaced_header_reg + // we expect to see the stack address of the basicLock in case the + // lock is still a light weight lock (lock_reg) + assert(mark_addr.disp() == 0, "cas must take a zero displacement"); + casx_under_lock(mark_addr.base(), lock_reg, displaced_header_reg, + (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + cmp(lock_reg, displaced_header_reg); + brx(Assembler::equal, true, Assembler::pn, done); + delayed()->st_ptr(G0, lockobj_addr); // free entry + + // The lock has been converted into a heavy lock and hence + // we need to get into the slow case + + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); + + bind(done); + } +} + +#ifndef CC_INTERP + +// Get the method data pointer from the methodOop and set the +// specified register to its value. + +void InterpreterMacroAssembler::set_method_data_pointer_offset(Register Roff) { + assert(ProfileInterpreter, "must be profiling interpreter"); + Label get_continue; + + ld_ptr(Lmethod, in_bytes(methodOopDesc::method_data_offset()), ImethodDataPtr); + test_method_data_pointer(get_continue); + add(ImethodDataPtr, in_bytes(methodDataOopDesc::data_offset()), ImethodDataPtr); + if (Roff != noreg) + // Roff contains a method data index ("mdi"). It defaults to zero. + add(ImethodDataPtr, Roff, ImethodDataPtr); + bind(get_continue); +} + +// Set the method data pointer for the current bcp. + +void InterpreterMacroAssembler::set_method_data_pointer_for_bcp() { + assert(ProfileInterpreter, "must be profiling interpreter"); + Label zero_continue; + + // Test MDO to avoid the call if it is NULL. + ld_ptr(Lmethod, in_bytes(methodOopDesc::method_data_offset()), ImethodDataPtr); + test_method_data_pointer(zero_continue); + call_VM_leaf(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::bcp_to_di), Lmethod, Lbcp); + set_method_data_pointer_offset(O0); + bind(zero_continue); +} + +// Test ImethodDataPtr. If it is null, continue at the specified label + +void InterpreterMacroAssembler::test_method_data_pointer(Label& zero_continue) { + assert(ProfileInterpreter, "must be profiling interpreter"); +#ifdef _LP64 + bpr(Assembler::rc_z, false, Assembler::pn, ImethodDataPtr, zero_continue); +#else + tst(ImethodDataPtr); + br(Assembler::zero, false, Assembler::pn, zero_continue); +#endif + delayed()->nop(); +} + +void InterpreterMacroAssembler::verify_method_data_pointer() { + assert(ProfileInterpreter, "must be profiling interpreter"); +#ifdef ASSERT + Label verify_continue; + test_method_data_pointer(verify_continue); + + // If the mdp is valid, it will point to a DataLayout header which is + // consistent with the bcp. The converse is highly probable also. + lduh(ImethodDataPtr, in_bytes(DataLayout::bci_offset()), G3_scratch); + ld_ptr(Address(Lmethod, 0, in_bytes(methodOopDesc::const_offset())), O5); + add(G3_scratch, in_bytes(constMethodOopDesc::codes_offset()), G3_scratch); + add(G3_scratch, O5, G3_scratch); + cmp(Lbcp, G3_scratch); + brx(Assembler::equal, false, Assembler::pt, verify_continue); + + Register temp_reg = O5; + delayed()->mov(ImethodDataPtr, temp_reg); + // %%% should use call_VM_leaf here? + //call_VM_leaf(noreg, ..., Lmethod, Lbcp, ImethodDataPtr); + save_frame_and_mov(sizeof(jdouble) / wordSize, Lmethod, O0, Lbcp, O1); + Address d_save(FP, 0, -sizeof(jdouble) + STACK_BIAS); + stf(FloatRegisterImpl::D, Ftos_d, d_save); + mov(temp_reg->after_save(), O2); + save_thread(L7_thread_cache); + call(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp), relocInfo::none); + delayed()->nop(); + restore_thread(L7_thread_cache); + ldf(FloatRegisterImpl::D, d_save, Ftos_d); + restore(); + bind(verify_continue); +#endif // ASSERT +} + +void InterpreterMacroAssembler::test_invocation_counter_for_mdp(Register invocation_count, + Register cur_bcp, + Register Rtmp, + Label &profile_continue) { + assert(ProfileInterpreter, "must be profiling interpreter"); + // Control will flow to "profile_continue" if the counter is less than the + // limit or if we call profile_method() + + Label done; + + // if no method data exists, and the counter is high enough, make one +#ifdef _LP64 + bpr(Assembler::rc_nz, false, Assembler::pn, ImethodDataPtr, done); +#else + tst(ImethodDataPtr); + br(Assembler::notZero, false, Assembler::pn, done); +#endif + + // Test to see if we should create a method data oop + Address profile_limit(Rtmp, (address)&InvocationCounter::InterpreterProfileLimit); +#ifdef _LP64 + delayed()->nop(); + sethi(profile_limit); +#else + delayed()->sethi(profile_limit); +#endif + ld(profile_limit, Rtmp); + cmp(invocation_count, Rtmp); + br(Assembler::lessUnsigned, false, Assembler::pn, profile_continue); + delayed()->nop(); + + // Build it now. + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method), cur_bcp); + set_method_data_pointer_offset(O0); + ba(false, profile_continue); + delayed()->nop(); + bind(done); +} + +// Store a value at some constant offset from the method data pointer. + +void InterpreterMacroAssembler::set_mdp_data_at(int constant, Register value) { + assert(ProfileInterpreter, "must be profiling interpreter"); + st_ptr(value, ImethodDataPtr, constant); +} + +void InterpreterMacroAssembler::increment_mdp_data_at(Address counter, + Register bumped_count, + bool decrement) { + assert(ProfileInterpreter, "must be profiling interpreter"); + + // Load the counter. + ld_ptr(counter, bumped_count); + + if (decrement) { + // Decrement the register. Set condition codes. + subcc(bumped_count, DataLayout::counter_increment, bumped_count); + + // If the decrement causes the counter to overflow, stay negative + Label L; + brx(Assembler::negative, true, Assembler::pn, L); + + // Store the decremented counter, if it is still negative. + delayed()->st_ptr(bumped_count, counter); + bind(L); + } else { + // Increment the register. Set carry flag. + addcc(bumped_count, DataLayout::counter_increment, bumped_count); + + // If the increment causes the counter to overflow, pull back by 1. + assert(DataLayout::counter_increment == 1, "subc works"); + subc(bumped_count, G0, bumped_count); + + // Store the incremented counter. + st_ptr(bumped_count, counter); + } +} + +// Increment the value at some constant offset from the method data pointer. + +void InterpreterMacroAssembler::increment_mdp_data_at(int constant, + Register bumped_count, + bool decrement) { + // Locate the counter at a fixed offset from the mdp: + Address counter(ImethodDataPtr, 0, constant); + increment_mdp_data_at(counter, bumped_count, decrement); +} + +// Increment the value at some non-fixed (reg + constant) offset from +// the method data pointer. + +void InterpreterMacroAssembler::increment_mdp_data_at(Register reg, + int constant, + Register bumped_count, + Register scratch2, + bool decrement) { + // Add the constant to reg to get the offset. + add(ImethodDataPtr, reg, scratch2); + Address counter(scratch2, 0, constant); + increment_mdp_data_at(counter, bumped_count, decrement); +} + +// Set a flag value at the current method data pointer position. +// Updates a single byte of the header, to avoid races with other header bits. + +void InterpreterMacroAssembler::set_mdp_flag_at(int flag_constant, + Register scratch) { + assert(ProfileInterpreter, "must be profiling interpreter"); + // Load the data header + ldub(ImethodDataPtr, in_bytes(DataLayout::flags_offset()), scratch); + + // Set the flag + or3(scratch, flag_constant, scratch); + + // Store the modified header. + stb(scratch, ImethodDataPtr, in_bytes(DataLayout::flags_offset())); +} + +// Test the location at some offset from the method data pointer. +// If it is not equal to value, branch to the not_equal_continue Label. +// Set condition codes to match the nullness of the loaded value. + +void InterpreterMacroAssembler::test_mdp_data_at(int offset, + Register value, + Label& not_equal_continue, + Register scratch) { + assert(ProfileInterpreter, "must be profiling interpreter"); + ld_ptr(ImethodDataPtr, offset, scratch); + cmp(value, scratch); + brx(Assembler::notEqual, false, Assembler::pn, not_equal_continue); + delayed()->tst(scratch); +} + +// Update the method data pointer by the displacement located at some fixed +// offset from the method data pointer. + +void InterpreterMacroAssembler::update_mdp_by_offset(int offset_of_disp, + Register scratch) { + assert(ProfileInterpreter, "must be profiling interpreter"); + ld_ptr(ImethodDataPtr, offset_of_disp, scratch); + add(ImethodDataPtr, scratch, ImethodDataPtr); +} + +// Update the method data pointer by the displacement located at the +// offset (reg + offset_of_disp). + +void InterpreterMacroAssembler::update_mdp_by_offset(Register reg, + int offset_of_disp, + Register scratch) { + assert(ProfileInterpreter, "must be profiling interpreter"); + add(reg, offset_of_disp, scratch); + ld_ptr(ImethodDataPtr, scratch, scratch); + add(ImethodDataPtr, scratch, ImethodDataPtr); +} + +// Update the method data pointer by a simple constant displacement. + +void InterpreterMacroAssembler::update_mdp_by_constant(int constant) { + assert(ProfileInterpreter, "must be profiling interpreter"); + add(ImethodDataPtr, constant, ImethodDataPtr); +} + +// Update the method data pointer for a _ret bytecode whose target +// was not among our cached targets. + +void InterpreterMacroAssembler::update_mdp_for_ret(TosState state, + Register return_bci) { + assert(ProfileInterpreter, "must be profiling interpreter"); + push(state); + st_ptr(return_bci, l_tmp); // protect return_bci, in case it is volatile + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::update_mdp_for_ret), return_bci); + ld_ptr(l_tmp, return_bci); + pop(state); +} + +// Count a taken branch in the bytecodes. + +void InterpreterMacroAssembler::profile_taken_branch(Register scratch, Register bumped_count) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // We are taking a branch. Increment the taken count. + increment_mdp_data_at(in_bytes(JumpData::taken_offset()), bumped_count); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_offset(in_bytes(JumpData::displacement_offset()), scratch); + bind (profile_continue); + } +} + + +// Count a not-taken branch in the bytecodes. + +void InterpreterMacroAssembler::profile_not_taken_branch(Register scratch) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // We are taking a branch. Increment the not taken count. + increment_mdp_data_at(in_bytes(BranchData::not_taken_offset()), scratch); + + // The method data pointer needs to be updated to correspond to the + // next bytecode. + update_mdp_by_constant(in_bytes(BranchData::branch_data_size())); + bind (profile_continue); + } +} + + +// Count a non-virtual call in the bytecodes. + +void InterpreterMacroAssembler::profile_call(Register scratch) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(in_bytes(CounterData::counter_data_size())); + bind (profile_continue); + } +} + + +// Count a final call in the bytecodes. + +void InterpreterMacroAssembler::profile_final_call(Register scratch) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size())); + bind (profile_continue); + } +} + + +// Count a virtual call in the bytecodes. + +void InterpreterMacroAssembler::profile_virtual_call(Register receiver, + Register scratch) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch); + + // Record the receiver type. + record_klass_in_profile(receiver, scratch); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size())); + bind (profile_continue); + } +} + +void InterpreterMacroAssembler::record_klass_in_profile_helper( + Register receiver, Register scratch, + int start_row, Label& done) { + int last_row = VirtualCallData::row_limit() - 1; + assert(start_row <= last_row, "must be work left to do"); + // Test this row for both the receiver and for null. + // Take any of three different outcomes: + // 1. found receiver => increment count and goto done + // 2. found null => keep looking for case 1, maybe allocate this cell + // 3. found something else => keep looking for cases 1 and 2 + // Case 3 is handled by a recursive call. + for (int row = start_row; row <= last_row; row++) { + Label next_test; + bool test_for_null_also = (row == start_row); + + // See if the receiver is receiver[n]. + int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row)); + test_mdp_data_at(recvr_offset, receiver, next_test, scratch); + + // The receiver is receiver[n]. Increment count[n]. + int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row)); + increment_mdp_data_at(count_offset, scratch); + ba(false, done); + delayed()->nop(); + bind(next_test); + + if (test_for_null_also) { + // Failed the equality check on receiver[n]... Test for null. + if (start_row == last_row) { + // The only thing left to do is handle the null case. + brx(Assembler::notZero, false, Assembler::pt, done); + delayed()->nop(); + break; + } + // Since null is rare, make it be the branch-taken case. + Label found_null; + brx(Assembler::zero, false, Assembler::pn, found_null); + delayed()->nop(); + + // Put all the "Case 3" tests here. + record_klass_in_profile_helper(receiver, scratch, start_row + 1, done); + + // Found a null. Keep searching for a matching receiver, + // but remember that this is an empty (unused) slot. + bind(found_null); + } + } + + // In the fall-through case, we found no matching receiver, but we + // observed the receiver[start_row] is NULL. + + // Fill in the receiver field and increment the count. + int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row)); + set_mdp_data_at(recvr_offset, receiver); + int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row)); + mov(DataLayout::counter_increment, scratch); + set_mdp_data_at(count_offset, scratch); + ba(false, done); + delayed()->nop(); +} + +void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, + Register scratch) { + assert(ProfileInterpreter, "must be profiling"); + Label done; + + record_klass_in_profile_helper(receiver, scratch, 0, done); + + bind (done); +} + + +// Count a ret in the bytecodes. + +void InterpreterMacroAssembler::profile_ret(TosState state, + Register return_bci, + Register scratch) { + if (ProfileInterpreter) { + Label profile_continue; + uint row; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // Update the total ret count. + increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch); + + for (row = 0; row < RetData::row_limit(); row++) { + Label next_test; + + // See if return_bci is equal to bci[n]: + test_mdp_data_at(in_bytes(RetData::bci_offset(row)), + return_bci, next_test, scratch); + + // return_bci is equal to bci[n]. Increment the count. + increment_mdp_data_at(in_bytes(RetData::bci_count_offset(row)), scratch); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_offset(in_bytes(RetData::bci_displacement_offset(row)), scratch); + ba(false, profile_continue); + delayed()->nop(); + bind(next_test); + } + + update_mdp_for_ret(state, return_bci); + + bind (profile_continue); + } +} + +// Profile an unexpected null in the bytecodes. +void InterpreterMacroAssembler::profile_null_seen(Register scratch) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + set_mdp_flag_at(BitData::null_seen_byte_constant(), scratch); + + // The method data pointer needs to be updated. + int mdp_delta = in_bytes(BitData::bit_data_size()); + if (TypeProfileCasts) { + mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); + } + update_mdp_by_constant(mdp_delta); + + bind (profile_continue); + } +} + +void InterpreterMacroAssembler::profile_typecheck(Register klass, + Register scratch) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + int mdp_delta = in_bytes(BitData::bit_data_size()); + if (TypeProfileCasts) { + mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); + + // Record the object type. + record_klass_in_profile(klass, scratch); + } + + // The method data pointer needs to be updated. + update_mdp_by_constant(mdp_delta); + + bind (profile_continue); + } +} + +void InterpreterMacroAssembler::profile_typecheck_failed(Register scratch) { + if (ProfileInterpreter && TypeProfileCasts) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + int count_offset = in_bytes(CounterData::count_offset()); + // Back up the address, since we have already bumped the mdp. + count_offset -= in_bytes(VirtualCallData::virtual_call_data_size()); + + // *Decrement* the counter. We expect to see zero or small negatives. + increment_mdp_data_at(count_offset, scratch, true); + + bind (profile_continue); + } +} + +// Count the default case of a switch construct. + +void InterpreterMacroAssembler::profile_switch_default(Register scratch) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // Update the default case count + increment_mdp_data_at(in_bytes(MultiBranchData::default_count_offset()), + scratch); + + // The method data pointer needs to be updated. + update_mdp_by_offset( + in_bytes(MultiBranchData::default_displacement_offset()), + scratch); + + bind (profile_continue); + } +} + +// Count the index'th case of a switch construct. + +void InterpreterMacroAssembler::profile_switch_case(Register index, + Register scratch, + Register scratch2, + Register scratch3) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // Build the base (index * per_case_size_in_bytes()) + case_array_offset_in_bytes() + set(in_bytes(MultiBranchData::per_case_size()), scratch); + smul(index, scratch, scratch); + add(scratch, in_bytes(MultiBranchData::case_array_offset()), scratch); + + // Update the case count + increment_mdp_data_at(scratch, + in_bytes(MultiBranchData::relative_count_offset()), + scratch2, + scratch3); + + // The method data pointer needs to be updated. + update_mdp_by_offset(scratch, + in_bytes(MultiBranchData::relative_displacement_offset()), + scratch2); + + bind (profile_continue); + } +} + +// add a InterpMonitorElem to stack (see frame_sparc.hpp) + +void InterpreterMacroAssembler::add_monitor_to_stack( bool stack_is_empty, + Register Rtemp, + Register Rtemp2 ) { + + Register Rlimit = Lmonitors; + const jint delta = frame::interpreter_frame_monitor_size() * wordSize; + assert( (delta & LongAlignmentMask) == 0, + "sizeof BasicObjectLock must be even number of doublewords"); + + sub( SP, delta, SP); + sub( Lesp, delta, Lesp); + sub( Lmonitors, delta, Lmonitors); + + if (!stack_is_empty) { + + // must copy stack contents down + + Label start_copying, next; + + // untested("monitor stack expansion"); + compute_stack_base(Rtemp); + ba( false, start_copying ); + delayed()->cmp( Rtemp, Rlimit); // done? duplicated below + + // note: must copy from low memory upwards + // On entry to loop, + // Rtemp points to new base of stack, Lesp points to new end of stack (1 past TOS) + // Loop mutates Rtemp + + bind( next); + + st_ptr(Rtemp2, Rtemp, 0); + inc(Rtemp, wordSize); + cmp(Rtemp, Rlimit); // are we done? (duplicated above) + + bind( start_copying ); + + brx( notEqual, true, pn, next ); + delayed()->ld_ptr( Rtemp, delta, Rtemp2 ); + + // done copying stack + } +} + +// Locals +#ifdef ASSERT +void InterpreterMacroAssembler::verify_local_tag(frame::Tag t, + Register base, + Register scratch, + int n) { + if (TaggedStackInterpreter) { + Label ok, long_ok; + // Use dst for scratch + assert_different_registers(base, scratch); + ld_ptr(base, Interpreter::local_tag_offset_in_bytes(n), scratch); + if (t == frame::TagCategory2) { + cmp(scratch, G0); + brx(Assembler::equal, false, Assembler::pt, long_ok); + delayed()->ld_ptr(base, Interpreter::local_tag_offset_in_bytes(n+1), scratch); + stop("local long/double tag value bad"); + bind(long_ok); + // compare second half tag + cmp(scratch, G0); + } else if (t == frame::TagValue) { + cmp(scratch, G0); + } else { + assert_different_registers(O3, base, scratch); + mov(t, O3); + cmp(scratch, O3); + } + brx(Assembler::equal, false, Assembler::pt, ok); + delayed()->nop(); + // Also compare if the local value is zero, then the tag might + // not have been set coming from deopt. + ld_ptr(base, Interpreter::local_offset_in_bytes(n), scratch); + cmp(scratch, G0); + brx(Assembler::equal, false, Assembler::pt, ok); + delayed()->nop(); + stop("Local tag value is bad"); + bind(ok); + } +} +#endif // ASSERT + +void InterpreterMacroAssembler::access_local_ptr( Register index, Register dst ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + debug_only(verify_local_tag(frame::TagReference, index, dst)); + ld_ptr(index, Interpreter::value_offset_in_bytes(), dst); + // Note: index must hold the effective address--the iinc template uses it +} + +// Just like access_local_ptr but the tag is a returnAddress +void InterpreterMacroAssembler::access_local_returnAddress(Register index, + Register dst ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + debug_only(verify_local_tag(frame::TagValue, index, dst)); + ld_ptr(index, Interpreter::value_offset_in_bytes(), dst); +} + +void InterpreterMacroAssembler::access_local_int( Register index, Register dst ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + debug_only(verify_local_tag(frame::TagValue, index, dst)); + ld(index, Interpreter::value_offset_in_bytes(), dst); + // Note: index must hold the effective address--the iinc template uses it +} + + +void InterpreterMacroAssembler::access_local_long( Register index, Register dst ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + debug_only(verify_local_tag(frame::TagCategory2, index, dst)); + // First half stored at index n+1 (which grows down from Llocals[n]) + load_unaligned_long(index, Interpreter::local_offset_in_bytes(1), dst); +} + + +void InterpreterMacroAssembler::access_local_float( Register index, FloatRegister dst ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + debug_only(verify_local_tag(frame::TagValue, index, G1_scratch)); + ldf(FloatRegisterImpl::S, index, Interpreter::value_offset_in_bytes(), dst); +} + + +void InterpreterMacroAssembler::access_local_double( Register index, FloatRegister dst ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + debug_only(verify_local_tag(frame::TagCategory2, index, G1_scratch)); + load_unaligned_double(index, Interpreter::local_offset_in_bytes(1), dst); +} + + +#ifdef ASSERT +void InterpreterMacroAssembler::check_for_regarea_stomp(Register Rindex, int offset, Register Rlimit, Register Rscratch, Register Rscratch1) { + Label L; + + assert(Rindex != Rscratch, "Registers cannot be same"); + assert(Rindex != Rscratch1, "Registers cannot be same"); + assert(Rlimit != Rscratch, "Registers cannot be same"); + assert(Rlimit != Rscratch1, "Registers cannot be same"); + assert(Rscratch1 != Rscratch, "Registers cannot be same"); + + // untested("reg area corruption"); + add(Rindex, offset, Rscratch); + add(Rlimit, 64 + STACK_BIAS, Rscratch1); + cmp(Rscratch, Rscratch1); + brx(Assembler::greaterEqualUnsigned, false, pn, L); + delayed()->nop(); + stop("regsave area is being clobbered"); + bind(L); +} +#endif // ASSERT + +void InterpreterMacroAssembler::tag_local(frame::Tag t, + Register base, + Register src, + int n) { + if (TaggedStackInterpreter) { + // have to store zero because local slots can be reused (rats!) + if (t == frame::TagValue) { + st_ptr(G0, base, Interpreter::local_tag_offset_in_bytes(n)); + } else if (t == frame::TagCategory2) { + st_ptr(G0, base, Interpreter::local_tag_offset_in_bytes(n)); + st_ptr(G0, base, Interpreter::local_tag_offset_in_bytes(n+1)); + } else { + // assert that we don't stomp the value in 'src' + // O3 is arbitrary because it's not used. + assert_different_registers(src, base, O3); + mov( t, O3); + st_ptr(O3, base, Interpreter::local_tag_offset_in_bytes(n)); + } + } +} + + +void InterpreterMacroAssembler::store_local_int( Register index, Register src ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + debug_only(check_for_regarea_stomp(index, Interpreter::value_offset_in_bytes(), FP, G1_scratch, G4_scratch);) + tag_local(frame::TagValue, index, src); + st(src, index, Interpreter::value_offset_in_bytes()); +} + +void InterpreterMacroAssembler::store_local_ptr( Register index, Register src, + Register tag ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + #ifdef ASSERT + check_for_regarea_stomp(index, Interpreter::value_offset_in_bytes(), FP, G1_scratch, G4_scratch); + #endif + st_ptr(src, index, Interpreter::value_offset_in_bytes()); + // Store tag register directly + if (TaggedStackInterpreter) { + st_ptr(tag, index, Interpreter::tag_offset_in_bytes()); + } +} + + + +void InterpreterMacroAssembler::store_local_ptr( int n, Register src, + Register tag ) { + st_ptr(src, Llocals, Interpreter::local_offset_in_bytes(n)); + if (TaggedStackInterpreter) { + st_ptr(tag, Llocals, Interpreter::local_tag_offset_in_bytes(n)); + } +} + +void InterpreterMacroAssembler::store_local_long( Register index, Register src ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + #ifdef ASSERT + check_for_regarea_stomp(index, Interpreter::local_offset_in_bytes(1), FP, G1_scratch, G4_scratch); + #endif + tag_local(frame::TagCategory2, index, src); + store_unaligned_long(src, index, Interpreter::local_offset_in_bytes(1)); // which is n+1 +} + + +void InterpreterMacroAssembler::store_local_float( Register index, FloatRegister src ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + #ifdef ASSERT + check_for_regarea_stomp(index, Interpreter::value_offset_in_bytes(), FP, G1_scratch, G4_scratch); + #endif + tag_local(frame::TagValue, index, G1_scratch); + stf(FloatRegisterImpl::S, src, index, Interpreter::value_offset_in_bytes()); +} + + +void InterpreterMacroAssembler::store_local_double( Register index, FloatRegister src ) { + assert_not_delayed(); + sll(index, Interpreter::logStackElementSize(), index); + sub(Llocals, index, index); + #ifdef ASSERT + check_for_regarea_stomp(index, Interpreter::local_offset_in_bytes(1), FP, G1_scratch, G4_scratch); + #endif + tag_local(frame::TagCategory2, index, G1_scratch); + store_unaligned_double(src, index, Interpreter::local_offset_in_bytes(1)); +} + + +int InterpreterMacroAssembler::top_most_monitor_byte_offset() { + const jint delta = frame::interpreter_frame_monitor_size() * wordSize; + int rounded_vm_local_words = ::round_to(frame::interpreter_frame_vm_local_words, WordsPerLong); + return ((-rounded_vm_local_words * wordSize) - delta ) + STACK_BIAS; +} + + +Address InterpreterMacroAssembler::top_most_monitor() { + return Address(FP, 0, top_most_monitor_byte_offset()); +} + + +void InterpreterMacroAssembler::compute_stack_base( Register Rdest ) { + add( Lesp, wordSize, Rdest ); +} + +#endif /* CC_INTERP */ + +void InterpreterMacroAssembler::increment_invocation_counter( Register Rtmp, Register Rtmp2 ) { + assert(UseCompiler, "incrementing must be useful"); +#ifdef CC_INTERP + Address inv_counter(G5_method, 0, in_bytes(methodOopDesc::invocation_counter_offset() + + InvocationCounter::counter_offset())); + Address be_counter(G5_method, 0, in_bytes(methodOopDesc::backedge_counter_offset() + + InvocationCounter::counter_offset())); +#else + Address inv_counter(Lmethod, 0, in_bytes(methodOopDesc::invocation_counter_offset() + + InvocationCounter::counter_offset())); + Address be_counter(Lmethod, 0, in_bytes(methodOopDesc::backedge_counter_offset() + + InvocationCounter::counter_offset())); +#endif /* CC_INTERP */ + int delta = InvocationCounter::count_increment; + + // Load each counter in a register + ld( inv_counter, Rtmp ); + ld( be_counter, Rtmp2 ); + + assert( is_simm13( delta ), " delta too large."); + + // Add the delta to the invocation counter and store the result + add( Rtmp, delta, Rtmp ); + + // Mask the backedge counter + and3( Rtmp2, InvocationCounter::count_mask_value, Rtmp2 ); + + // Store value + st( Rtmp, inv_counter); + + // Add invocation counter + backedge counter + add( Rtmp, Rtmp2, Rtmp); + + // Note that this macro must leave the backedge_count + invocation_count in Rtmp! +} + +void InterpreterMacroAssembler::increment_backedge_counter( Register Rtmp, Register Rtmp2 ) { + assert(UseCompiler, "incrementing must be useful"); +#ifdef CC_INTERP + Address be_counter(G5_method, 0, in_bytes(methodOopDesc::backedge_counter_offset() + + InvocationCounter::counter_offset())); + Address inv_counter(G5_method, 0, in_bytes(methodOopDesc::invocation_counter_offset() + + InvocationCounter::counter_offset())); +#else + Address be_counter(Lmethod, 0, in_bytes(methodOopDesc::backedge_counter_offset() + + InvocationCounter::counter_offset())); + Address inv_counter(Lmethod, 0, in_bytes(methodOopDesc::invocation_counter_offset() + + InvocationCounter::counter_offset())); +#endif /* CC_INTERP */ + int delta = InvocationCounter::count_increment; + // Load each counter in a register + ld( be_counter, Rtmp ); + ld( inv_counter, Rtmp2 ); + + // Add the delta to the backedge counter + add( Rtmp, delta, Rtmp ); + + // Mask the invocation counter, add to backedge counter + and3( Rtmp2, InvocationCounter::count_mask_value, Rtmp2 ); + + // and store the result to memory + st( Rtmp, be_counter ); + + // Add backedge + invocation counter + add( Rtmp, Rtmp2, Rtmp ); + + // Note that this macro must leave backedge_count + invocation_count in Rtmp! +} + +#ifndef CC_INTERP +void InterpreterMacroAssembler::test_backedge_count_for_osr( Register backedge_count, + Register branch_bcp, + Register Rtmp ) { + Label did_not_overflow; + Label overflow_with_error; + assert_different_registers(backedge_count, Rtmp, branch_bcp); + assert(UseOnStackReplacement,"Must UseOnStackReplacement to test_backedge_count_for_osr"); + + Address limit(Rtmp, address(&InvocationCounter::InterpreterBackwardBranchLimit)); + load_contents(limit, Rtmp); + cmp(backedge_count, Rtmp); + br(Assembler::lessUnsigned, false, Assembler::pt, did_not_overflow); + delayed()->nop(); + + // When ProfileInterpreter is on, the backedge_count comes from the + // methodDataOop, which value does not get reset on the call to + // frequency_counter_overflow(). To avoid excessive calls to the overflow + // routine while the method is being compiled, add a second test to make sure + // the overflow function is called only once every overflow_frequency. + if (ProfileInterpreter) { + const int overflow_frequency = 1024; + andcc(backedge_count, overflow_frequency-1, Rtmp); + brx(Assembler::notZero, false, Assembler::pt, did_not_overflow); + delayed()->nop(); + } + + // overflow in loop, pass branch bytecode + set(6,Rtmp); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), branch_bcp, Rtmp); + + // Was an OSR adapter generated? + // O0 = osr nmethod + tst(O0); + brx(Assembler::zero, false, Assembler::pn, overflow_with_error); + delayed()->nop(); + + // Has the nmethod been invalidated already? + ld(O0, nmethod::entry_bci_offset(), O2); + cmp(O2, InvalidOSREntryBci); + br(Assembler::equal, false, Assembler::pn, overflow_with_error); + delayed()->nop(); + + // migrate the interpreter frame off of the stack + + mov(G2_thread, L7); + // save nmethod + mov(O0, L6); + set_last_Java_frame(SP, noreg); + call_VM_leaf(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin), L7); + reset_last_Java_frame(); + mov(L7, G2_thread); + + // move OSR nmethod to I1 + mov(L6, I1); + + // OSR buffer to I0 + mov(O0, I0); + + // remove the interpreter frame + restore(I5_savedSP, 0, SP); + + // Jump to the osr code. + ld_ptr(O1, nmethod::osr_entry_point_offset(), O2); + jmp(O2, G0); + delayed()->nop(); + + bind(overflow_with_error); + + bind(did_not_overflow); +} + + + +void InterpreterMacroAssembler::interp_verify_oop(Register reg, TosState state, const char * file, int line) { + if (state == atos) { MacroAssembler::_verify_oop(reg, "broken oop ", file, line); } +} + + +// local helper function for the verify_oop_or_return_address macro +static bool verify_return_address(methodOopDesc* m, int bci) { +#ifndef PRODUCT + address pc = (address)(m->constMethod()) + + in_bytes(constMethodOopDesc::codes_offset()) + bci; + // assume it is a valid return address if it is inside m and is preceded by a jsr + if (!m->contains(pc)) return false; + address jsr_pc; + jsr_pc = pc - Bytecodes::length_for(Bytecodes::_jsr); + if (*jsr_pc == Bytecodes::_jsr && jsr_pc >= m->code_base()) return true; + jsr_pc = pc - Bytecodes::length_for(Bytecodes::_jsr_w); + if (*jsr_pc == Bytecodes::_jsr_w && jsr_pc >= m->code_base()) return true; +#endif // PRODUCT + return false; +} + + +void InterpreterMacroAssembler::verify_oop_or_return_address(Register reg, Register Rtmp) { + if (!VerifyOops) return; + // the VM documentation for the astore[_wide] bytecode allows + // the TOS to be not only an oop but also a return address + Label test; + Label skip; + // See if it is an address (in the current method): + + mov(reg, Rtmp); + const int log2_bytecode_size_limit = 16; + srl(Rtmp, log2_bytecode_size_limit, Rtmp); + br_notnull( Rtmp, false, pt, test ); + delayed()->nop(); + + // %%% should use call_VM_leaf here? + save_frame_and_mov(0, Lmethod, O0, reg, O1); + save_thread(L7_thread_cache); + call(CAST_FROM_FN_PTR(address,verify_return_address), relocInfo::none); + delayed()->nop(); + restore_thread(L7_thread_cache); + br_notnull( O0, false, pt, skip ); + delayed()->restore(); + + // Perform a more elaborate out-of-line call + // Not an address; verify it: + bind(test); + verify_oop(reg); + bind(skip); +} + + +void InterpreterMacroAssembler::verify_FPU(int stack_depth, TosState state) { + if (state == ftos || state == dtos) MacroAssembler::verify_FPU(stack_depth); +} +#endif /* CC_INTERP */ + +// Inline assembly for: +// +// if (thread is in interp_only_mode) { +// InterpreterRuntime::post_method_entry(); +// } +// if (DTraceMethodProbes) { +// SharedRuntime::dtrace_method_entry(method, reciever); +// } + +void InterpreterMacroAssembler::notify_method_entry() { + + // C++ interpreter only uses this for native methods. + + // Whenever JVMTI puts a thread in interp_only_mode, method + // entry/exit events are sent for that thread to track stack + // depth. If it is possible to enter interp_only_mode we add + // the code to check if the event should be sent. + if (JvmtiExport::can_post_interpreter_events()) { + Label L; + Register temp_reg = O5; + + const Address interp_only (G2_thread, 0, in_bytes(JavaThread::interp_only_mode_offset())); + + ld(interp_only, temp_reg); + tst(temp_reg); + br(zero, false, pt, L); + delayed()->nop(); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_entry)); + bind(L); + } + + { + Register temp_reg = O5; + SkipIfEqual skip_if(this, temp_reg, &DTraceMethodProbes, zero); + call_VM_leaf(noreg, + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), + G2_thread, Lmethod); + } +} + + +// Inline assembly for: +// +// if (thread is in interp_only_mode) { +// // save result +// InterpreterRuntime::post_method_exit(); +// // restore result +// } +// if (DTraceMethodProbes) { +// SharedRuntime::dtrace_method_exit(thread, method); +// } +// +// Native methods have their result stored in d_tmp and l_tmp +// Java methods have their result stored in the expression stack + +void InterpreterMacroAssembler::notify_method_exit(bool is_native_method, + TosState state, + NotifyMethodExitMode mode) { + // C++ interpreter only uses this for native methods. + + // Whenever JVMTI puts a thread in interp_only_mode, method + // entry/exit events are sent for that thread to track stack + // depth. If it is possible to enter interp_only_mode we add + // the code to check if the event should be sent. + if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) { + Label L; + Register temp_reg = O5; + + const Address interp_only (G2_thread, 0, in_bytes(JavaThread::interp_only_mode_offset())); + + ld(interp_only, temp_reg); + tst(temp_reg); + br(zero, false, pt, L); + delayed()->nop(); + + // Note: frame::interpreter_frame_result has a dependency on how the + // method result is saved across the call to post_method_exit. For + // native methods it assumes the result registers are saved to + // l_scratch and d_scratch. If this changes then the interpreter_frame_result + // implementation will need to be updated too. + + save_return_value(state, is_native_method); + call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit)); + restore_return_value(state, is_native_method); + bind(L); + } + + { + Register temp_reg = O5; + // Dtrace notification + SkipIfEqual skip_if(this, temp_reg, &DTraceMethodProbes, zero); + save_return_value(state, is_native_method); + call_VM_leaf( + noreg, + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), + G2_thread, Lmethod); + restore_return_value(state, is_native_method); + } +} + +void InterpreterMacroAssembler::save_return_value(TosState state, bool is_native_call) { +#ifdef CC_INTERP + // result potentially in O0/O1: save it across calls + stf(FloatRegisterImpl::D, F0, STATE(_native_fresult)); +#ifdef _LP64 + stx(O0, STATE(_native_lresult)); +#else + std(O0, STATE(_native_lresult)); +#endif +#else // CC_INTERP + if (is_native_call) { + stf(FloatRegisterImpl::D, F0, d_tmp); +#ifdef _LP64 + stx(O0, l_tmp); +#else + std(O0, l_tmp); +#endif + } else { + push(state); + } +#endif // CC_INTERP +} + +void InterpreterMacroAssembler::restore_return_value( TosState state, bool is_native_call) { +#ifdef CC_INTERP + ldf(FloatRegisterImpl::D, STATE(_native_fresult), F0); +#ifdef _LP64 + ldx(STATE(_native_lresult), O0); +#else + ldd(STATE(_native_lresult), O0); +#endif +#else // CC_INTERP + if (is_native_call) { + ldf(FloatRegisterImpl::D, d_tmp, F0); +#ifdef _LP64 + ldx(l_tmp, O0); +#else + ldd(l_tmp, O0); +#endif + } else { + pop(state); + } +#endif // CC_INTERP +} diff --git a/hotspot/src/cpu/sparc/vm/interp_masm_sparc.hpp b/hotspot/src/cpu/sparc/vm/interp_masm_sparc.hpp new file mode 100644 index 00000000000..674611dad60 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/interp_masm_sparc.hpp @@ -0,0 +1,332 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file specializes the assember with interpreter-specific macros + +REGISTER_DECLARATION( Register, Otos_i , O0); // tos for ints, etc +REGISTER_DECLARATION( Register, Otos_l , O0); // for longs +REGISTER_DECLARATION( Register, Otos_l1, O0); // for 1st part of longs +REGISTER_DECLARATION( Register, Otos_l2, O1); // for 2nd part of longs +REGISTER_DECLARATION(FloatRegister, Ftos_f , F0); // for floats +REGISTER_DECLARATION(FloatRegister, Ftos_d , F0); // for doubles +REGISTER_DECLARATION(FloatRegister, Ftos_d1, F0); // for 1st part of double +REGISTER_DECLARATION(FloatRegister, Ftos_d2, F1); // for 2nd part of double + +#ifndef DONT_USE_REGISTER_DEFINES +#define Otos_i O0 +#define Otos_l O0 +#define Otos_l1 O0 +#define Otos_l2 O1 +#define Ftos_f F0 +#define Ftos_d F0 +#define Ftos_d1 F0 +#define Ftos_d2 F1 +#endif // DONT_USE_REGISTER_DEFINES + +class InterpreterMacroAssembler: public MacroAssembler { + protected: +#ifndef CC_INTERP + // Interpreter specific version of call_VM_base + virtual void call_VM_leaf_base( + Register java_thread, + address entry_point, + int number_of_arguments + ); + + virtual void call_VM_base( + Register oop_result, + Register java_thread, + Register last_java_sp, + address entry_point, + int number_of_arguments, + bool check_exception=true + ); + + virtual void check_and_handle_popframe(Register java_thread); + virtual void check_and_handle_earlyret(Register java_thread); + + // base routine for all dispatches + void dispatch_base(TosState state, address* table); +#endif /* CC_INTERP */ + + public: + InterpreterMacroAssembler(CodeBuffer* c) + : MacroAssembler(c) {} + +#ifndef CC_INTERP + virtual void load_earlyret_value(TosState state); + + static const Address l_tmp ; + static const Address d_tmp ; +#endif /* CC_INTERP */ + + // helper routine for frame allocation/deallocation + // compute the delta by which the caller's SP has to + // be adjusted to accomodate for the non-argument + // locals + void compute_extra_locals_size_in_bytes(Register args_size, Register locals_size, Register delta); + +#ifndef CC_INTERP + + // dispatch routines + void dispatch_prolog(TosState state, int step = 0); + void dispatch_epilog(TosState state, int step = 0); + void dispatch_only(TosState state); + void dispatch_normal(TosState state); + void dispatch_next(TosState state, int step = 0); + void dispatch_next_noverify_oop(TosState state, int step = 0); + void dispatch_via (TosState state, address* table); + + + // Removes the current activation (incl. unlocking of monitors). + // Additionally this code is used for earlyReturn in which case we + // want to skip throwing an exception and installing an exception. + void remove_activation(TosState state, + bool throw_monitor_exception = true, + bool install_monitor_exception = true); + + protected: + void dispatch_Lbyte_code(TosState state, address* table, int bcp_incr = 0, bool verify = true); +#endif /* CC_INTERP */ + + public: + // Super call_VM calls - correspond to MacroAssembler::call_VM(_leaf) calls + void super_call_VM(Register thread_cache, + Register oop_result, + Register last_java_sp, + address entry_point, + Register arg_1, + Register arg_2, + bool check_exception = true); + +#ifndef CC_INTERP + void super_call_VM_leaf(Register thread_cache, address entry_point, Register arg_1); + + // Generate a subtype check: branch to ok_is_subtype if sub_klass is + // a subtype of super_klass. Blows registers tmp1, tmp2 and tmp3. + void gen_subtype_check( Register sub_klass, Register super_klass, Register tmp1, Register tmp2, Register tmp3, Label &ok_is_subtype ); + + // helpers for tossing exceptions + void throw_if_not_1_icc( Condition ok_condition, Label& ok ); + void throw_if_not_1_xcc( Condition ok_condition, Label& ok ); + void throw_if_not_1_x ( Condition ok_condition, Label& ok ); // chooses icc or xcc based on _LP64 + + void throw_if_not_2( address throw_entry_point, Register Rscratch, Label& ok); + + void throw_if_not_icc( Condition ok_condition, address throw_entry_point, Register Rscratch ); + void throw_if_not_xcc( Condition ok_condition, address throw_entry_point, Register Rscratch ); + void throw_if_not_x ( Condition ok_condition, address throw_entry_point, Register Rscratch ); + + // helpers for expression stack + + void pop_i( Register r = Otos_i); + void pop_ptr( Register r = Otos_i, Register scratch = O4); + void pop_l( Register r = Otos_l1); + // G4_scratch and Lscratch are used at call sites!! + void pop_f(FloatRegister f = Ftos_f, Register scratch = G1_scratch); + void pop_d(FloatRegister f = Ftos_d1, Register scratch = G1_scratch); + + void push_i( Register r = Otos_i); + void push_ptr( Register r = Otos_i); + void push_ptr( Register r, Register tag); + void push_l( Register r = Otos_l1); + void push_f(FloatRegister f = Ftos_f); + void push_d(FloatRegister f = Ftos_d1); + + + void pop (TosState state); // transition vtos -> state + void push(TosState state); // transition state -> vtos + void empty_expression_stack(); // resets both Lesp and SP + + // Support for Tagged Stacks + void tag_stack(frame::Tag t, Register r); + void tag_stack(Register tag); + void tag_local(frame::Tag t, Register src, Register base, int n = 0); + +#ifdef ASSERT + void verify_sp(Register Rsp, Register Rtemp); + void verify_esp(Register Resp); // verify that Lesp points to a word in the temp stack + + void verify_stack_tag(frame::Tag t, Register r, Register scratch = G0); + void verify_local_tag(frame::Tag t, Register base, Register scr, int n = 0); +#endif // ASSERT + + public: + void if_cmp(Condition cc, bool ptr_compare); + + // Load values from bytecode stream: + + enum signedOrNot { Signed, Unsigned }; + enum setCCOrNot { set_CC, dont_set_CC }; + + void get_2_byte_integer_at_bcp( int bcp_offset, + Register Rtmp, + Register Rdst, + signedOrNot is_signed, + setCCOrNot should_set_CC = dont_set_CC ); + + void get_4_byte_integer_at_bcp( int bcp_offset, + Register Rtmp, + Register Rdst, + setCCOrNot should_set_CC = dont_set_CC ); + + void get_cache_and_index_at_bcp(Register cache, Register tmp, int bcp_offset); + void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset); + + + // common code + + void field_offset_at(int n, Register tmp, Register dest, Register base); + int field_offset_at(Register object, address bcp, int offset); + void fast_iaaccess(int n, address bcp); + void fast_iagetfield(address bcp); + void fast_iaputfield(address bcp, bool do_store_check ); + + void index_check(Register array, Register index, int index_shift, Register tmp, Register res); + void index_check_without_pop(Register array, Register index, int index_shift, Register tmp, Register res); + + void get_constant_pool(Register Rdst); + void get_constant_pool_cache(Register Rdst); + void get_cpool_and_tags(Register Rcpool, Register Rtags); + void is_a(Label& L); + + // Load compiled (i2c) or interpreter entry and call from interpreted + void call_from_interpreter(Register target, Register scratch, Register Rret); + + // -------------------------------------------------- + + void unlock_if_synchronized_method(TosState state, bool throw_monitor_exception = true, bool install_monitor_exception = true); + + void add_monitor_to_stack( bool stack_is_empty, + Register Rtemp, + Register Rtemp2 ); + + // Load/store aligned in _LP64 but unaligned otherwise + // These only apply to the Interpreter expression stack and locals! + void load_unaligned_double(Register r1, int offset, FloatRegister d); + void store_unaligned_double(FloatRegister d, Register r1, int offset ); + + // Load/store aligned in _LP64 but unaligned otherwise + void load_unaligned_long(Register r1, int offset, Register d); + void store_unaligned_long(Register d, Register r1, int offset ); + + void access_local_int( Register index, Register dst ); + void access_local_ptr( Register index, Register dst ); + void access_local_returnAddress( Register index, Register dst ); + void access_local_long( Register index, Register dst ); + void access_local_float( Register index, FloatRegister dst ); + void access_local_double( Register index, FloatRegister dst ); +#ifdef ASSERT + void check_for_regarea_stomp( Register Rindex, int offset, Register Rlimit, Register Rscratch, Register Rscratch1); +#endif // ASSERT + void store_local_int( Register index, Register src ); + void store_local_ptr( Register index, Register src, Register tag = Otos_l2 ); + void store_local_ptr( int n, Register src, Register tag = Otos_l2 ); + void store_local_long( Register index, Register src ); + void store_local_float( Register index, FloatRegister src ); + void store_local_double( Register index, FloatRegister src ); + + // Tagged stack helpers for swap and dup + void load_ptr_and_tag(int n, Register val, Register tag); + void store_ptr_and_tag(int n, Register val, Register tag); + + // Tagged stack helper for getting receiver in register. + void load_receiver(Register param_count, Register recv); + + static int top_most_monitor_byte_offset(); // offset in bytes to top of monitor block + Address top_most_monitor(); + void compute_stack_base( Register Rdest ); + +#endif /* CC_INTERP */ + void increment_invocation_counter( Register Rtmp, Register Rtmp2 ); + void increment_backedge_counter( Register Rtmp, Register Rtmp2 ); +#ifndef CC_INTERP + void test_backedge_count_for_osr( Register backedge_count, Register branch_bcp, Register Rtmp ); + +#endif /* CC_INTERP */ + // Object locking + void lock_object (Register lock_reg, Register obj_reg); + void unlock_object(Register lock_reg); + +#ifndef CC_INTERP + // Interpreter profiling operations + void set_method_data_pointer() { set_method_data_pointer_offset(noreg); } + void set_method_data_pointer_for_bcp(); + void set_method_data_pointer_offset(Register mdi_reg); + void test_method_data_pointer(Label& zero_continue); + void verify_method_data_pointer(); + void test_invocation_counter_for_mdp(Register invocation_count, Register cur_bcp, Register Rtmp, Label &profile_continue); + + void set_mdp_data_at(int constant, Register value); + void increment_mdp_data_at(Address counter, Register bumped_count, + bool decrement = false); + void increment_mdp_data_at(int constant, Register bumped_count, + bool decrement = false); + void increment_mdp_data_at(Register reg, int constant, + Register bumped_count, Register scratch2, + bool decrement = false); + void set_mdp_flag_at(int flag_constant, Register scratch); + void test_mdp_data_at(int offset, Register value, Label& not_equal_continue, + Register scratch); + + void record_klass_in_profile(Register receiver, Register scratch); + void record_klass_in_profile_helper(Register receiver, Register scratch, + int start_row, Label& done); + + void update_mdp_by_offset(int offset_of_disp, Register scratch); + void update_mdp_by_offset(Register reg, int offset_of_disp, + Register scratch); + void update_mdp_by_constant(int constant); + void update_mdp_for_ret(TosState state, Register return_bci); + + void profile_taken_branch(Register scratch, Register bumped_count); + void profile_not_taken_branch(Register scratch); + void profile_call(Register scratch); + void profile_final_call(Register scratch); + void profile_virtual_call(Register receiver, Register scratch); + void profile_ret(TosState state, Register return_bci, Register scratch); + void profile_null_seen(Register scratch); + void profile_typecheck(Register klass, Register scratch); + void profile_typecheck_failed(Register scratch); + void profile_switch_default(Register scratch); + void profile_switch_case(Register index, + Register scratch1, + Register scratch2, + Register scratch3); + + // Debugging + void interp_verify_oop(Register reg, TosState state, const char * file, int line); // only if +VerifyOops && state == atos + void verify_oop_or_return_address(Register reg, Register rtmp); // for astore + void verify_FPU(int stack_depth, TosState state = ftos); // only if +VerifyFPU && (state == ftos || state == dtos) + +#endif /* CC_INTERP */ + // support for JVMTI/Dtrace + typedef enum { NotifyJVMTI, SkipNotifyJVMTI } NotifyMethodExitMode; + void notify_method_entry(); + void notify_method_exit( + bool save_result, TosState state, NotifyMethodExitMode mode); + + void save_return_value(TosState state, bool is_native_call); + void restore_return_value(TosState state, bool is_native_call); +}; diff --git a/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp b/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp new file mode 100644 index 00000000000..3352d2abd17 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp @@ -0,0 +1,40 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + friend class AbstractInterpreterGenerator; + + private: + + address generate_normal_entry(bool synchronized); + address generate_native_entry(bool synchronized); + address generate_abstract_entry(void); + address generate_math_entry(AbstractInterpreter::MethodKind kind); + address generate_empty_entry(void); + address generate_accessor_entry(void); + void lock_method(void); + void save_native_result(void); + void restore_native_result(void); + + void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); + void generate_counter_overflow(Label& Lcontinue); diff --git a/hotspot/src/cpu/sparc/vm/interpreterRT_sparc.cpp b/hotspot/src/cpu/sparc/vm/interpreterRT_sparc.cpp new file mode 100644 index 00000000000..f6cb8e72a20 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/interpreterRT_sparc.cpp @@ -0,0 +1,260 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreterRT_sparc.cpp.incl" + + +#define __ _masm-> + + +// Implementation of SignatureHandlerGenerator + +void InterpreterRuntime::SignatureHandlerGenerator::pass_word(int size_of_arg, int offset_in_arg) { + Argument jni_arg(jni_offset() + offset_in_arg, false); + Register Rtmp = O0; + __ ld(Llocals, Interpreter::local_offset_in_bytes(offset()), Rtmp); + + __ store_argument(Rtmp, jni_arg); +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_long() { + Argument jni_arg(jni_offset(), false); + Register Rtmp = O0; + +#ifdef ASSERT + if (TaggedStackInterpreter) { + // check at least one tag is okay + Label ok; + __ ld_ptr(Llocals, Interpreter::local_tag_offset_in_bytes(offset() + 1), Rtmp); + __ cmp(Rtmp, G0); + __ brx(Assembler::equal, false, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("Native object has bad tag value"); + __ bind(ok); + } +#endif // ASSERT + +#ifdef _LP64 + __ ldx(Llocals, Interpreter::local_offset_in_bytes(offset() + 1), Rtmp); + __ store_long_argument(Rtmp, jni_arg); +#else + __ ld(Llocals, Interpreter::local_offset_in_bytes(offset() + 1), Rtmp); + __ store_argument(Rtmp, jni_arg); + __ ld(Llocals, Interpreter::local_offset_in_bytes(offset() + 0), Rtmp); + Argument successor(jni_arg.successor()); + __ store_argument(Rtmp, successor); +#endif +} + + +#ifdef _LP64 +void InterpreterRuntime::SignatureHandlerGenerator::pass_float() { + Argument jni_arg(jni_offset(), false); + FloatRegister Rtmp = F0; + __ ldf(FloatRegisterImpl::S, Llocals, Interpreter::local_offset_in_bytes(offset()), Rtmp); + __ store_float_argument(Rtmp, jni_arg); +} +#endif + + +void InterpreterRuntime::SignatureHandlerGenerator::pass_double() { + Argument jni_arg(jni_offset(), false); +#ifdef _LP64 + FloatRegister Rtmp = F0; + __ ldf(FloatRegisterImpl::D, Llocals, Interpreter::local_offset_in_bytes(offset() + 1), Rtmp); + __ store_double_argument(Rtmp, jni_arg); +#else + Register Rtmp = O0; + __ ld(Llocals, Interpreter::local_offset_in_bytes(offset() + 1), Rtmp); + __ store_argument(Rtmp, jni_arg); + __ ld(Llocals, Interpreter::local_offset_in_bytes(offset()), Rtmp); + Argument successor(jni_arg.successor()); + __ store_argument(Rtmp, successor); +#endif +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_object() { + Argument jni_arg(jni_offset(), false); + Argument java_arg( offset(), true); + Register Rtmp1 = O0; + Register Rtmp2 = jni_arg.is_register() ? jni_arg.as_register() : O0; + Register Rtmp3 = G3_scratch; + + // the handle for a receiver will never be null + bool do_NULL_check = offset() != 0 || is_static(); + + Address h_arg = Address(Llocals, 0, Interpreter::local_offset_in_bytes(offset())); + __ ld_ptr(h_arg, Rtmp1); +#ifdef ASSERT + if (TaggedStackInterpreter) { + // check we have the obj and not the tag + Label ok; + __ mov(frame::TagReference, Rtmp3); + __ cmp(Rtmp1, Rtmp3); + __ brx(Assembler::notEqual, true, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("Native object passed tag by mistake"); + __ bind(ok); + } +#endif // ASSERT + if (!do_NULL_check) { + __ add(h_arg, Rtmp2); + } else { + if (Rtmp1 == Rtmp2) + __ tst(Rtmp1); + else __ addcc(G0, Rtmp1, Rtmp2); // optimize mov/test pair + Label L; + __ brx(Assembler::notZero, true, Assembler::pt, L); + __ delayed()->add(h_arg, Rtmp2); + __ bind(L); + } + __ store_ptr_argument(Rtmp2, jni_arg); // this is often a no-op +} + + +void InterpreterRuntime::SignatureHandlerGenerator::generate(uint64_t fingerprint) { + + // generate code to handle arguments + iterate(fingerprint); + + // return result handler + Address result_handler(Lscratch, Interpreter::result_handler(method()->result_type())); + __ sethi(result_handler); + __ retl(); + __ delayed()->add(result_handler, result_handler.base()); + + __ flush(); +} + + +// Implementation of SignatureHandlerLibrary + +void SignatureHandlerLibrary::pd_set_handler(address handler) {} + + +class SlowSignatureHandler: public NativeSignatureIterator { + private: + address _from; + intptr_t* _to; + intptr_t* _RegArgSignature; // Signature of first Arguments to be passed in Registers + uint _argcount; + + enum { // We need to differenciate float from non floats in reg args + non_float = 0, + float_sig = 1, + double_sig = 2, + long_sig = 3 + }; + +#ifdef ASSERT + void verify_tag(frame::Tag t) { + assert(!TaggedStackInterpreter || + *(intptr_t*)(_from+Interpreter::local_tag_offset_in_bytes(0)) == t, "wrong tag"); + } +#endif // ASSERT + + virtual void pass_int() { + *_to++ = *(jint *)(_from+Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagValue)); + _from -= Interpreter::stackElementSize(); + add_signature( non_float ); + } + + virtual void pass_object() { + // pass address of from + intptr_t *from_addr = (intptr_t*)(_from + Interpreter::local_offset_in_bytes(0)); + *_to++ = (*from_addr == 0) ? NULL : (intptr_t) from_addr; + debug_only(verify_tag(frame::TagReference)); + _from -= Interpreter::stackElementSize(); + add_signature( non_float ); + } + +#ifdef _LP64 + virtual void pass_float() { + *_to++ = *(jint *)(_from+Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagValue)); + _from -= Interpreter::stackElementSize(); + add_signature( float_sig ); + } + + virtual void pass_double() { + *_to++ = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); + debug_only(verify_tag(frame::TagValue)); + _from -= 2*Interpreter::stackElementSize(); + add_signature( double_sig ); + } + + virtual void pass_long() { + _to[0] = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); + debug_only(verify_tag(frame::TagValue)); + _to += 1; + _from -= 2*Interpreter::stackElementSize(); + add_signature( long_sig ); + } +#else + // pass_double() is pass_long() and pass_float() only _LP64 + virtual void pass_long() { + _to[0] = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); + _to[1] = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagValue)); + _to += 2; + _from -= 2*Interpreter::stackElementSize(); + add_signature( non_float ); + } +#endif // _LP64 + + virtual void add_signature( intptr_t sig_type ) { + if ( _argcount < (sizeof (intptr_t))*4 ) { + *_RegArgSignature |= (sig_type << (_argcount*2) ); + _argcount++; + } + } + + + public: + SlowSignatureHandler(methodHandle method, address from, intptr_t* to, intptr_t *RegArgSig) : NativeSignatureIterator(method) { + _from = from; + _to = to; + _RegArgSignature = RegArgSig; + *_RegArgSignature = 0; + _argcount = method->is_static() ? 2 : 1; + } +}; + + +IRT_ENTRY(address, InterpreterRuntime::slow_signature_handler( + JavaThread* thread, + methodOopDesc* method, + intptr_t* from, + intptr_t* to )) + methodHandle m(thread, method); + assert(m->is_native(), "sanity check"); + // handle arguments + // Warning: We use reg arg slot 00 temporarily to return the RegArgSignature + // back to the code that pops the arguments into the CPU registers + SlowSignatureHandler(m, (address)from, m->is_static() ? to+2 : to+1, to).iterate(UCONST64(-1)); + // return result handler + return Interpreter::result_handler(m->result_type()); +IRT_END diff --git a/hotspot/src/cpu/sparc/vm/interpreterRT_sparc.hpp b/hotspot/src/cpu/sparc/vm/interpreterRT_sparc.hpp new file mode 100644 index 00000000000..788f69db7b5 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/interpreterRT_sparc.hpp @@ -0,0 +1,56 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +static int binary_search(int key, LookupswitchPair* array, int n); + +static address iload (JavaThread* thread); +static address aload (JavaThread* thread); +static address istore(JavaThread* thread); +static address astore(JavaThread* thread); +static address iinc (JavaThread* thread); + + + +// native method calls + +class SignatureHandlerGenerator: public NativeSignatureIterator { + private: + MacroAssembler* _masm; + + void pass_word(int size_of_arg, int offset_in_arg); + void pass_int() { pass_word(1, 0); } + void pass_long(); + void pass_double(); + void pass_float(); + void pass_object(); + + public: + // Creation + SignatureHandlerGenerator(methodHandle method, CodeBuffer* buffer) : NativeSignatureIterator(method) { + _masm = new MacroAssembler(buffer); + } + + // Code generation + void generate( uint64_t fingerprint ); +}; diff --git a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp new file mode 100644 index 00000000000..898d3706062 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp @@ -0,0 +1,420 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreter_sparc.cpp.incl" + + + +// Generation of Interpreter +// +// The InterpreterGenerator generates the interpreter into Interpreter::_code. + + +#define __ _masm-> + + +//---------------------------------------------------------------------------------------------------- + + + + +int AbstractInterpreter::BasicType_as_index(BasicType type) { + int i = 0; + switch (type) { + case T_BOOLEAN: i = 0; break; + case T_CHAR : i = 1; break; + case T_BYTE : i = 2; break; + case T_SHORT : i = 3; break; + case T_INT : i = 4; break; + case T_LONG : i = 5; break; + case T_VOID : i = 6; break; + case T_FLOAT : i = 7; break; + case T_DOUBLE : i = 8; break; + case T_OBJECT : i = 9; break; + case T_ARRAY : i = 9; break; + default : ShouldNotReachHere(); + } + assert(0 <= i && i < AbstractInterpreter::number_of_result_handlers, "index out of bounds"); + return i; +} + + +#ifndef _LP64 +address AbstractInterpreterGenerator::generate_slow_signature_handler() { + address entry = __ pc(); + Argument argv(0, true); + + // We are in the jni transition frame. Save the last_java_frame corresponding to the + // outer interpreter frame + // + __ set_last_Java_frame(FP, noreg); + // make sure the interpreter frame we've pushed has a valid return pc + __ mov(O7, I7); + __ mov(Lmethod, G3_scratch); + __ mov(Llocals, G4_scratch); + __ save_frame(0); + __ mov(G2_thread, L7_thread_cache); + __ add(argv.address_in_frame(), O3); + __ mov(G2_thread, O0); + __ mov(G3_scratch, O1); + __ call(CAST_FROM_FN_PTR(address, InterpreterRuntime::slow_signature_handler), relocInfo::runtime_call_type); + __ delayed()->mov(G4_scratch, O2); + __ mov(L7_thread_cache, G2_thread); + __ reset_last_Java_frame(); + + // load the register arguments (the C code packed them as varargs) + for (Argument ldarg = argv.successor(); ldarg.is_register(); ldarg = ldarg.successor()) { + __ ld_ptr(ldarg.address_in_frame(), ldarg.as_register()); + } + __ ret(); + __ delayed()-> + restore(O0, 0, Lscratch); // caller's Lscratch gets the result handler + return entry; +} + + +#else +// LP64 passes floating point arguments in F1, F3, F5, etc. instead of +// O0, O1, O2 etc.. +// Doubles are passed in D0, D2, D4 +// We store the signature of the first 16 arguments in the first argument +// slot because it will be overwritten prior to calling the native +// function, with the pointer to the JNIEnv. +// If LP64 there can be up to 16 floating point arguments in registers +// or 6 integer registers. +address AbstractInterpreterGenerator::generate_slow_signature_handler() { + + enum { + non_float = 0, + float_sig = 1, + double_sig = 2, + sig_mask = 3 + }; + + address entry = __ pc(); + Argument argv(0, true); + + // We are in the jni transition frame. Save the last_java_frame corresponding to the + // outer interpreter frame + // + __ set_last_Java_frame(FP, noreg); + // make sure the interpreter frame we've pushed has a valid return pc + __ mov(O7, I7); + __ mov(Lmethod, G3_scratch); + __ mov(Llocals, G4_scratch); + __ save_frame(0); + __ mov(G2_thread, L7_thread_cache); + __ add(argv.address_in_frame(), O3); + __ mov(G2_thread, O0); + __ mov(G3_scratch, O1); + __ call(CAST_FROM_FN_PTR(address, InterpreterRuntime::slow_signature_handler), relocInfo::runtime_call_type); + __ delayed()->mov(G4_scratch, O2); + __ mov(L7_thread_cache, G2_thread); + __ reset_last_Java_frame(); + + + // load the register arguments (the C code packed them as varargs) + Address Sig = argv.address_in_frame(); // Argument 0 holds the signature + __ ld_ptr( Sig, G3_scratch ); // Get register argument signature word into G3_scratch + __ mov( G3_scratch, G4_scratch); + __ srl( G4_scratch, 2, G4_scratch); // Skip Arg 0 + Label done; + for (Argument ldarg = argv.successor(); ldarg.is_float_register(); ldarg = ldarg.successor()) { + Label NonFloatArg; + Label LoadFloatArg; + Label LoadDoubleArg; + Label NextArg; + Address a = ldarg.address_in_frame(); + __ andcc(G4_scratch, sig_mask, G3_scratch); + __ br(Assembler::zero, false, Assembler::pt, NonFloatArg); + __ delayed()->nop(); + + __ cmp(G3_scratch, float_sig ); + __ br(Assembler::equal, false, Assembler::pt, LoadFloatArg); + __ delayed()->nop(); + + __ cmp(G3_scratch, double_sig ); + __ br(Assembler::equal, false, Assembler::pt, LoadDoubleArg); + __ delayed()->nop(); + + __ bind(NonFloatArg); + // There are only 6 integer register arguments! + if ( ldarg.is_register() ) + __ ld_ptr(ldarg.address_in_frame(), ldarg.as_register()); + else { + // Optimization, see if there are any more args and get out prior to checking + // all 16 float registers. My guess is that this is rare. + // If is_register is false, then we are done the first six integer args. + __ tst(G4_scratch); + __ brx(Assembler::zero, false, Assembler::pt, done); + __ delayed()->nop(); + + } + __ ba(false, NextArg); + __ delayed()->srl( G4_scratch, 2, G4_scratch ); + + __ bind(LoadFloatArg); + __ ldf( FloatRegisterImpl::S, a, ldarg.as_float_register(), 4); + __ ba(false, NextArg); + __ delayed()->srl( G4_scratch, 2, G4_scratch ); + + __ bind(LoadDoubleArg); + __ ldf( FloatRegisterImpl::D, a, ldarg.as_double_register() ); + __ ba(false, NextArg); + __ delayed()->srl( G4_scratch, 2, G4_scratch ); + + __ bind(NextArg); + + } + + __ bind(done); + __ ret(); + __ delayed()-> + restore(O0, 0, Lscratch); // caller's Lscratch gets the result handler + return entry; +} +#endif + +void InterpreterGenerator::generate_counter_overflow(Label& Lcontinue) { + + // Generate code to initiate compilation on the counter overflow. + + // InterpreterRuntime::frequency_counter_overflow takes two arguments, + // the first indicates if the counter overflow occurs at a backwards branch (NULL bcp) + // and the second is only used when the first is true. We pass zero for both. + // The call returns the address of the verified entry point for the method or NULL + // if the compilation did not complete (either went background or bailed out). + __ set((int)false, O2); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), O2, O2, true); + // returns verified_entry_point or NULL + // we ignore it in any case + __ ba(false, Lcontinue); + __ delayed()->nop(); + +} + + +// End of helpers + +// Various method entries + +// Abstract method entry +// Attempt to execute abstract method. Throw exception +// +address InterpreterGenerator::generate_abstract_entry(void) { + address entry = __ pc(); + // abstract method entry + // throw exception + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); + // the call_VM checks for exception, so we should never return here. + __ should_not_reach_here(); + return entry; + +} + + +//---------------------------------------------------------------------------------------------------- +// Entry points & stack frame layout +// +// Here we generate the various kind of entries into the interpreter. +// The two main entry type are generic bytecode methods and native call method. +// These both come in synchronized and non-synchronized versions but the +// frame layout they create is very similar. The other method entry +// types are really just special purpose entries that are really entry +// and interpretation all in one. These are for trivial methods like +// accessor, empty, or special math methods. +// +// When control flow reaches any of the entry types for the interpreter +// the following holds -> +// +// C2 Calling Conventions: +// +// The entry code below assumes that the following registers are set +// when coming in: +// G5_method: holds the methodOop of the method to call +// Lesp: points to the TOS of the callers expression stack +// after having pushed all the parameters +// +// The entry code does the following to setup an interpreter frame +// pop parameters from the callers stack by adjusting Lesp +// set O0 to Lesp +// compute X = (max_locals - num_parameters) +// bump SP up by X to accomadate the extra locals +// compute X = max_expression_stack +// + vm_local_words +// + 16 words of register save area +// save frame doing a save sp, -X, sp growing towards lower addresses +// set Lbcp, Lmethod, LcpoolCache +// set Llocals to i0 +// set Lmonitors to FP - rounded_vm_local_words +// set Lesp to Lmonitors - 4 +// +// The frame has now been setup to do the rest of the entry code + +// Try this optimization: Most method entries could live in a +// "one size fits all" stack frame without all the dynamic size +// calculations. It might be profitable to do all this calculation +// statically and approximately for "small enough" methods. + +//----------------------------------------------------------------------------------------------- + +// C1 Calling conventions +// +// Upon method entry, the following registers are setup: +// +// g2 G2_thread: current thread +// g5 G5_method: method to activate +// g4 Gargs : pointer to last argument +// +// +// Stack: +// +// +---------------+ <--- sp +// | | +// : reg save area : +// | | +// +---------------+ <--- sp + 0x40 +// | | +// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) +// | | +// +---------------+ <--- sp + 0x5c +// | | +// : free : +// | | +// +---------------+ <--- Gargs +// | | +// : arguments : +// | | +// +---------------+ +// | | +// +// +// +// AFTER FRAME HAS BEEN SETUP for method interpretation the stack looks like: +// +// +---------------+ <--- sp +// | | +// : reg save area : +// | | +// +---------------+ <--- sp + 0x40 +// | | +// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) +// | | +// +---------------+ <--- sp + 0x5c +// | | +// : : +// | | <--- Lesp +// +---------------+ <--- Lmonitors (fp - 0x18) +// | VM locals | +// +---------------+ <--- fp +// | | +// : reg save area : +// | | +// +---------------+ <--- fp + 0x40 +// | | +// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) +// | | +// +---------------+ <--- fp + 0x5c +// | | +// : free : +// | | +// +---------------+ +// | | +// : nonarg locals : +// | | +// +---------------+ +// | | +// : arguments : +// | | <--- Llocals +// +---------------+ <--- Gargs +// | | + +address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) { + // determine code generation flags + bool synchronized = false; + address entry_point = NULL; + + switch (kind) { + case Interpreter::zerolocals : break; + case Interpreter::zerolocals_synchronized: synchronized = true; break; + case Interpreter::native : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(false); break; + case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(true); break; + case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break; + case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break; + case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break; + case Interpreter::java_lang_math_sin : break; + case Interpreter::java_lang_math_cos : break; + case Interpreter::java_lang_math_tan : break; + case Interpreter::java_lang_math_sqrt : break; + case Interpreter::java_lang_math_abs : break; + case Interpreter::java_lang_math_log : break; + case Interpreter::java_lang_math_log10 : break; + default : ShouldNotReachHere(); break; + } + + if (entry_point) return entry_point; + + return ((InterpreterGenerator*)this)->generate_normal_entry(synchronized); +} + + +// This method tells the deoptimizer how big an interpreted frame must be: +int AbstractInterpreter::size_activation(methodOop method, + int tempcount, + int popframe_extra_args, + int moncount, + int callee_param_count, + int callee_locals, + bool is_top_frame) { + return layout_activation(method, + tempcount, + popframe_extra_args, + moncount, + callee_param_count, + callee_locals, + (frame*)NULL, + (frame*)NULL, + is_top_frame); +} + +void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { + + // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in + // the days we had adapter frames. When we deoptimize a situation where a + // compiled caller calls a compiled caller will have registers it expects + // to survive the call to the callee. If we deoptimize the callee the only + // way we can restore these registers is to have the oldest interpreter + // frame that we create restore these values. That is what this routine + // will accomplish. + + // At the moment we have modified c2 to not have any callee save registers + // so this problem does not exist and this routine is just a place holder. + + assert(f->is_interpreted_frame(), "must be interpreted"); +} + + +//---------------------------------------------------------------------------------------------------- +// Exceptions diff --git a/hotspot/src/cpu/sparc/vm/interpreter_sparc.hpp b/hotspot/src/cpu/sparc/vm/interpreter_sparc.hpp new file mode 100644 index 00000000000..711eedead67 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/interpreter_sparc.hpp @@ -0,0 +1,56 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + public: + + // Support for Tagged Stacks + + // Stack index relative to tos (which points at value) + static int expr_index_at(int i) { + return stackElementWords() * i; + } + + static int expr_tag_index_at(int i) { + assert(TaggedStackInterpreter, "should not call this"); + // tag is one word above java stack element + return stackElementWords() * i + 1; + } + + static int expr_offset_in_bytes(int i) { return stackElementSize()*i + wordSize; } + static int expr_tag_offset_in_bytes (int i) { + assert(TaggedStackInterpreter, "should not call this"); + return expr_offset_in_bytes(i) + wordSize; + } + + // Already negated by c++ interpreter + static int local_index_at(int i) { + assert(i<=0, "local direction already negated"); + return stackElementWords() * i + (value_offset_in_bytes()/wordSize); + } + + static int local_tag_index_at(int i) { + assert(i<=0, "local direction already negated"); + assert(TaggedStackInterpreter, "should not call this"); + return stackElementWords() * i + (tag_offset_in_bytes()/wordSize); + } diff --git a/hotspot/src/cpu/sparc/vm/javaFrameAnchor_sparc.hpp b/hotspot/src/cpu/sparc/vm/javaFrameAnchor_sparc.hpp new file mode 100644 index 00000000000..ce18fe90c63 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/javaFrameAnchor_sparc.hpp @@ -0,0 +1,97 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +private: + volatile int _flags; + +public: + + enum pd_Constants { + flushed = 1 // winodows have flushed + }; + + int flags(void) { return _flags; } + void set_flags(int flags) { _flags = flags; } + + static ByteSize flags_offset() { return byte_offset_of(JavaFrameAnchor, _flags); } + + // Each arch must define clear, copy + // These are used by objects that only care about: + // 1 - initializing a new state (thread creation, javaCalls) + // 2 - saving a current state (javaCalls) + // 3 - restoring an old state (javaCalls) + + void clear(void) { + // clearing _last_Java_sp must be first + _last_Java_sp = NULL; + // fence? + _flags = 0; + _last_Java_pc = NULL; + } + + void copy(JavaFrameAnchor* src) { + // In order to make sure the transition state is valid for "this" + // We must clear _last_Java_sp before copying the rest of the new data + // + // Hack Alert: Temporary bugfix for 4717480/4721647 + // To act like previous version (pd_cache_state) don't NULL _last_Java_sp + // unless the value is changing + // + if (_last_Java_sp != src->_last_Java_sp) + _last_Java_sp = NULL; + + _flags = src->_flags; + _last_Java_pc = src->_last_Java_pc; + // Must be last so profiler will always see valid frame if has_last_frame() is true + _last_Java_sp = src->_last_Java_sp; + } + + // Is stack walkable + inline bool walkable( void) { + return _flags & flushed; + } + + void make_walkable(JavaThread* thread); + + void set_last_Java_sp(intptr_t* sp) { _last_Java_sp = sp; } + + // These are only used by friends +private: + + intptr_t* last_Java_sp() const { + // _last_Java_sp will always be a an unbiased stack pointer + // if is is biased then some setter screwed up. This is + // deadly. +#ifdef _LP64 + assert(((intptr_t)_last_Java_sp & 0xF) == 0, "Biased last_Java_sp"); +#endif + return _last_Java_sp; + } + + void capture_last_Java_pc(intptr_t* sp); + + void set_window_flushed( void) { + _flags |= flushed; + OrderAccess::fence(); + } diff --git a/hotspot/src/cpu/sparc/vm/jniFastGetField_sparc.cpp b/hotspot/src/cpu/sparc/vm/jniFastGetField_sparc.cpp new file mode 100644 index 00000000000..31253baca2c --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/jniFastGetField_sparc.cpp @@ -0,0 +1,261 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jniFastGetField_sparc.cpp.incl" + +// TSO ensures that loads are blocking and ordered with respect to +// to earlier loads, so we don't need LoadLoad membars. + +#define __ masm-> + +#define BUFFER_SIZE 30*sizeof(jint) + +// Common register usage: +// O0: env +// O1: obj +// O2: jfieldID +// O4: offset (O2 >> 2) +// G4: old safepoint counter + +address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { + const char *name; + switch (type) { + case T_BOOLEAN: name = "jni_fast_GetBooleanField"; break; + case T_BYTE: name = "jni_fast_GetByteField"; break; + case T_CHAR: name = "jni_fast_GetCharField"; break; + case T_SHORT: name = "jni_fast_GetShortField"; break; + case T_INT: name = "jni_fast_GetIntField"; break; + default: ShouldNotReachHere(); + } + ResourceMark rm; + BufferBlob* b = BufferBlob::create(name, BUFFER_SIZE*wordSize); + address fast_entry = b->instructions_begin(); + CodeBuffer cbuf(fast_entry, b->instructions_size()); + MacroAssembler* masm = new MacroAssembler(&cbuf); + + Label label1, label2; + + address cnt_addr = SafepointSynchronize::safepoint_counter_addr(); + Address ca(O3, cnt_addr); + __ sethi (ca); + __ ld (ca, G4); + __ andcc (G4, 1, G0); + __ br (Assembler::notZero, false, Assembler::pn, label1); + __ delayed()->srl (O2, 2, O4); + __ ld_ptr (O1, 0, O5); + + assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); + speculative_load_pclist[count] = __ pc(); + switch (type) { + case T_BOOLEAN: __ ldub (O5, O4, G3); break; + case T_BYTE: __ ldsb (O5, O4, G3); break; + case T_CHAR: __ lduh (O5, O4, G3); break; + case T_SHORT: __ ldsh (O5, O4, G3); break; + case T_INT: __ ld (O5, O4, G3); break; + default: ShouldNotReachHere(); + } + + __ ld (ca, O5); + __ cmp (O5, G4); + __ br (Assembler::notEqual, false, Assembler::pn, label2); + __ delayed()->mov (O7, G1); + __ retl (); + __ delayed()->mov (G3, O0); + + slowcase_entry_pclist[count++] = __ pc(); + __ bind (label1); + __ mov (O7, G1); + + address slow_case_addr; + switch (type) { + case T_BOOLEAN: slow_case_addr = jni_GetBooleanField_addr(); break; + case T_BYTE: slow_case_addr = jni_GetByteField_addr(); break; + case T_CHAR: slow_case_addr = jni_GetCharField_addr(); break; + case T_SHORT: slow_case_addr = jni_GetShortField_addr(); break; + case T_INT: slow_case_addr = jni_GetIntField_addr(); break; + default: ShouldNotReachHere(); + } + __ bind (label2); + __ call (slow_case_addr, relocInfo::none); + __ delayed()->mov (G1, O7); + + __ flush (); + + return fast_entry; +} + +address JNI_FastGetField::generate_fast_get_boolean_field() { + return generate_fast_get_int_field0(T_BOOLEAN); +} + +address JNI_FastGetField::generate_fast_get_byte_field() { + return generate_fast_get_int_field0(T_BYTE); +} + +address JNI_FastGetField::generate_fast_get_char_field() { + return generate_fast_get_int_field0(T_CHAR); +} + +address JNI_FastGetField::generate_fast_get_short_field() { + return generate_fast_get_int_field0(T_SHORT); +} + +address JNI_FastGetField::generate_fast_get_int_field() { + return generate_fast_get_int_field0(T_INT); +} + +address JNI_FastGetField::generate_fast_get_long_field() { + const char *name = "jni_fast_GetLongField"; + ResourceMark rm; + BufferBlob* b = BufferBlob::create(name, BUFFER_SIZE*wordSize); + address fast_entry = b->instructions_begin(); + CodeBuffer cbuf(fast_entry, b->instructions_size()); + MacroAssembler* masm = new MacroAssembler(&cbuf); + + Label label1, label2; + + address cnt_addr = SafepointSynchronize::safepoint_counter_addr(); + Address ca(G3, cnt_addr); + __ sethi (ca); + __ ld (ca, G4); + __ andcc (G4, 1, G0); + __ br (Assembler::notZero, false, Assembler::pn, label1); + __ delayed()->srl (O2, 2, O4); + __ ld_ptr (O1, 0, O5); + __ add (O5, O4, O5); + +#ifndef _LP64 + assert(count < LIST_CAPACITY-1, "LIST_CAPACITY too small"); + speculative_load_pclist[count++] = __ pc(); + __ ld (O5, 0, G2); + + speculative_load_pclist[count] = __ pc(); + __ ld (O5, 4, O3); +#else + assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); + speculative_load_pclist[count] = __ pc(); + __ ldx (O5, 0, O3); +#endif + + __ ld (ca, G1); + __ cmp (G1, G4); + __ br (Assembler::notEqual, false, Assembler::pn, label2); + __ delayed()->mov (O7, G1); + +#ifndef _LP64 + __ mov (G2, O0); + __ retl (); + __ delayed()->mov (O3, O1); +#else + __ retl (); + __ delayed()->mov (O3, O0); +#endif + +#ifndef _LP64 + slowcase_entry_pclist[count-1] = __ pc(); + slowcase_entry_pclist[count++] = __ pc() ; +#else + slowcase_entry_pclist[count++] = __ pc(); +#endif + + __ bind (label1); + __ mov (O7, G1); + + address slow_case_addr = jni_GetLongField_addr(); + __ bind (label2); + __ call (slow_case_addr, relocInfo::none); + __ delayed()->mov (G1, O7); + + __ flush (); + + return fast_entry; +} + +address JNI_FastGetField::generate_fast_get_float_field0(BasicType type) { + const char *name; + switch (type) { + case T_FLOAT: name = "jni_fast_GetFloatField"; break; + case T_DOUBLE: name = "jni_fast_GetDoubleField"; break; + default: ShouldNotReachHere(); + } + ResourceMark rm; + BufferBlob* b = BufferBlob::create(name, BUFFER_SIZE*wordSize); + address fast_entry = b->instructions_begin(); + CodeBuffer cbuf(fast_entry, b->instructions_size()); + MacroAssembler* masm = new MacroAssembler(&cbuf); + + Label label1, label2; + + address cnt_addr = SafepointSynchronize::safepoint_counter_addr(); + Address ca(O3, cnt_addr); + __ sethi (ca); + __ ld (ca, G4); + __ andcc (G4, 1, G0); + __ br (Assembler::notZero, false, Assembler::pn, label1); + __ delayed()->srl (O2, 2, O4); + __ ld_ptr (O1, 0, O5); + + assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); + speculative_load_pclist[count] = __ pc(); + switch (type) { + case T_FLOAT: __ ldf (FloatRegisterImpl::S, O5, O4, F0); break; + case T_DOUBLE: __ ldf (FloatRegisterImpl::D, O5, O4, F0); break; + default: ShouldNotReachHere(); + } + + __ ld (ca, O5); + __ cmp (O5, G4); + __ br (Assembler::notEqual, false, Assembler::pn, label2); + __ delayed()->mov (O7, G1); + + __ retl (); + __ delayed()-> nop (); + + slowcase_entry_pclist[count++] = __ pc(); + __ bind (label1); + __ mov (O7, G1); + + address slow_case_addr; + switch (type) { + case T_FLOAT: slow_case_addr = jni_GetFloatField_addr(); break; + case T_DOUBLE: slow_case_addr = jni_GetDoubleField_addr(); break; + default: ShouldNotReachHere(); + } + __ bind (label2); + __ call (slow_case_addr, relocInfo::none); + __ delayed()->mov (G1, O7); + + __ flush (); + + return fast_entry; +} + +address JNI_FastGetField::generate_fast_get_float_field() { + return generate_fast_get_float_field0(T_FLOAT); +} + +address JNI_FastGetField::generate_fast_get_double_field() { + return generate_fast_get_float_field0(T_DOUBLE); +} diff --git a/hotspot/src/cpu/sparc/vm/jniTypes_sparc.hpp b/hotspot/src/cpu/sparc/vm/jniTypes_sparc.hpp new file mode 100644 index 00000000000..bc38c813e2a --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/jniTypes_sparc.hpp @@ -0,0 +1,108 @@ +/* + * Copyright 1998-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds platform-dependent routines used to write primitive jni +// types to the array of arguments passed into JavaCalls::call + +class JNITypes : AllStatic { + // These functions write a java primitive type (in native format) + // to a java stack slot array to be passed as an argument to JavaCalls:calls. + // I.e., they are functionally 'push' operations if they have a 'pos' + // formal parameter. Note that jlong's and jdouble's are written + // _in reverse_ of the order in which they appear in the interpreter + // stack. This is because call stubs (see stubGenerator_sparc.cpp) + // reverse the argument list constructed by JavaCallArguments (see + // javaCalls.hpp). + +private: + // Helper routines. + static inline void put_int2 (jint *from, jint *to) { to[0] = from[0]; to[1] = from[1]; } + static inline void put_int2 (jint *from, jint *to, int& pos) { put_int2 (from, (jint *)((intptr_t *)to + pos)); pos += 2; } + static inline void put_int2r(jint *from, jint *to) { to[0] = from[1]; to[1] = from[0]; } + static inline void put_int2r(jint *from, jint *to, int& pos) { put_int2r(from, (jint *)((intptr_t *)to + pos)); pos += 2; } + +public: + // Ints are stored in native format in one JavaCallArgument slot at *to. + static inline void put_int(jint from, intptr_t *to) { *(jint *)(to + 0 ) = from; } + static inline void put_int(jint from, intptr_t *to, int& pos) { *(jint *)(to + pos++) = from; } + static inline void put_int(jint *from, intptr_t *to, int& pos) { *(jint *)(to + pos++) = *from; } + +#ifdef _LP64 + // Longs are stored in native format in one JavaCallArgument slot at *(to+1). + static inline void put_long(jlong from, intptr_t *to) { *(jlong *)(to + 1 + 0) = from; } + static inline void put_long(jlong from, intptr_t *to, int& pos) { *(jlong *)(to + 1 + pos) = from; pos += 2; } + static inline void put_long(jlong *from, intptr_t *to, int& pos) { *(jlong *)(to + 1 + pos) = *from; pos += 2; } +#else + // Longs are stored in reversed native word format in two JavaCallArgument slots at *to. + // The high half is in *(to+1) and the low half in *to. + static inline void put_long(jlong from, intptr_t *to) { put_int2r((jint *)&from, (jint *)to); } + static inline void put_long(jlong from, intptr_t *to, int& pos) { put_int2r((jint *)&from, (jint *)to, pos); } + static inline void put_long(jlong *from, intptr_t *to, int& pos) { put_int2r((jint *) from, (jint *)to, pos); } +#endif + + // Oops are stored in native format in one JavaCallArgument slot at *to. + static inline void put_obj(oop from, intptr_t *to) { *(oop *)(to + 0 ) = from; } + static inline void put_obj(oop from, intptr_t *to, int& pos) { *(oop *)(to + pos++) = from; } + static inline void put_obj(oop *from, intptr_t *to, int& pos) { *(oop *)(to + pos++) = *from; } + + // Floats are stored in native format in one JavaCallArgument slot at *to. + static inline void put_float(jfloat from, intptr_t *to) { *(jfloat *)(to + 0 ) = from; } + static inline void put_float(jfloat from, intptr_t *to, int& pos) { *(jfloat *)(to + pos++) = from; } + static inline void put_float(jfloat *from, intptr_t *to, int& pos) { *(jfloat *)(to + pos++) = *from; } + +#ifdef _LP64 + // Doubles are stored in native word format in one JavaCallArgument slot at *(to+1). + static inline void put_double(jdouble from, intptr_t *to) { *(jdouble *)(to + 1 + 0) = from; } + static inline void put_double(jdouble from, intptr_t *to, int& pos) { *(jdouble *)(to + 1 + pos) = from; pos += 2; } + static inline void put_double(jdouble *from, intptr_t *to, int& pos) { *(jdouble *)(to + 1 + pos) = *from; pos += 2; } +#else + // Doubles are stored in reversed native word format in two JavaCallArgument slots at *to. + static inline void put_double(jdouble from, intptr_t *to) { put_int2r((jint *)&from, (jint *)to); } + static inline void put_double(jdouble from, intptr_t *to, int& pos) { put_int2r((jint *)&from, (jint *)to, pos); } + static inline void put_double(jdouble *from, intptr_t *to, int& pos) { put_int2r((jint *) from, (jint *)to, pos); } +#endif + + // The get_xxx routines, on the other hand, actually _do_ fetch + // java primitive types from the interpreter stack. + static inline jint get_int(intptr_t *from) { return *(jint *)from; } + +#ifdef _LP64 + static inline jlong get_long(intptr_t *from) { return *(jlong *)from; } +#else + static inline jlong get_long(intptr_t *from) { return ((jlong)(*( signed int *)((jint *)from )) << 32) | + ((jlong)(*(unsigned int *)((jint *)from + 1)) << 0); } +#endif + + static inline oop get_obj(intptr_t *from) { return *(oop *)from; } + static inline jfloat get_float(intptr_t *from) { return *(jfloat *)from; } + +#ifdef _LP64 + static inline jdouble get_double(intptr_t *from) { return *(jdouble *)from; } +#else + static inline jdouble get_double(intptr_t *from) { jlong jl = ((jlong)(*( signed int *)((jint *)from )) << 32) | + ((jlong)(*(unsigned int *)((jint *)from + 1)) << 0); + return *(jdouble *)&jl; } +#endif + +}; diff --git a/hotspot/src/cpu/sparc/vm/jni_sparc.h b/hotspot/src/cpu/sparc/vm/jni_sparc.h new file mode 100644 index 00000000000..7d2845e0162 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/jni_sparc.h @@ -0,0 +1,32 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#define JNIEXPORT +#define JNIIMPORT +#define JNICALL + +typedef int jint; +typedef long long jlong; +typedef signed char jbyte; diff --git a/hotspot/src/cpu/sparc/vm/nativeInst_sparc.cpp b/hotspot/src/cpu/sparc/vm/nativeInst_sparc.cpp new file mode 100644 index 00000000000..331675102a8 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/nativeInst_sparc.cpp @@ -0,0 +1,989 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_nativeInst_sparc.cpp.incl" + + +void NativeInstruction::set_data64_sethi(address instaddr, intptr_t x) { + ResourceMark rm; + CodeBuffer buf(instaddr, 10 * BytesPerInstWord ); + MacroAssembler* _masm = new MacroAssembler(&buf); + Register destreg; + + destreg = inv_rd(*(unsigned int *)instaddr); + // Generate a the new sequence + Address dest( destreg, (address)x ); + _masm->sethi( dest, true ); + ICache::invalidate_range(instaddr, 7 * BytesPerInstWord); +} + +void NativeInstruction::verify() { + // make sure code pattern is actually an instruction address + address addr = addr_at(0); + if (addr == 0 || ((intptr_t)addr & 3) != 0) { + fatal("not an instruction address"); + } +} + +void NativeInstruction::print() { + tty->print_cr(INTPTR_FORMAT ": 0x%x", addr_at(0), long_at(0)); +} + +void NativeInstruction::set_long_at(int offset, int i) { + address addr = addr_at(offset); + *(int*)addr = i; + ICache::invalidate_word(addr); +} + +void NativeInstruction::set_jlong_at(int offset, jlong i) { + address addr = addr_at(offset); + *(jlong*)addr = i; + // Don't need to invalidate 2 words here, because + // the flush instruction operates on doublewords. + ICache::invalidate_word(addr); +} + +void NativeInstruction::set_addr_at(int offset, address x) { + address addr = addr_at(offset); + assert( ((intptr_t)addr & (wordSize-1)) == 0, "set_addr_at bad address alignment"); + *(uintptr_t*)addr = (uintptr_t)x; + // Don't need to invalidate 2 words here in the 64-bit case, + // because the flush instruction operates on doublewords. + ICache::invalidate_word(addr); + // The Intel code has this assertion for NativeCall::set_destination, + // NativeMovConstReg::set_data, NativeMovRegMem::set_offset, + // NativeJump::set_jump_destination, and NativePushImm32::set_data + //assert (Patching_lock->owned_by_self(), "must hold lock to patch instruction") +} + +bool NativeInstruction::is_zero_test(Register ®) { + int x = long_at(0); + Assembler::op3s temp = (Assembler::op3s) (Assembler::sub_op3 | Assembler::cc_bit_op3); + if (is_op3(x, temp, Assembler::arith_op) && + inv_immed(x) && inv_rd(x) == G0) { + if (inv_rs1(x) == G0) { + reg = inv_rs2(x); + return true; + } else if (inv_rs2(x) == G0) { + reg = inv_rs1(x); + return true; + } + } + return false; +} + +bool NativeInstruction::is_load_store_with_small_offset(Register reg) { + int x = long_at(0); + if (is_op(x, Assembler::ldst_op) && + inv_rs1(x) == reg && inv_immed(x)) { + return true; + } + return false; +} + +void NativeCall::verify() { + NativeInstruction::verify(); + // make sure code pattern is actually a call instruction + if (!is_op(long_at(0), Assembler::call_op)) { + fatal("not a call"); + } +} + +void NativeCall::print() { + tty->print_cr(INTPTR_FORMAT ": call " INTPTR_FORMAT, instruction_address(), destination()); +} + + +// MT-safe patching of a call instruction (and following word). +// First patches the second word, and then atomicly replaces +// the first word with the first new instruction word. +// Other processors might briefly see the old first word +// followed by the new second word. This is OK if the old +// second word is harmless, and the new second word may be +// harmlessly executed in the delay slot of the call. +void NativeCall::replace_mt_safe(address instr_addr, address code_buffer) { + assert(Patching_lock->is_locked() || + SafepointSynchronize::is_at_safepoint(), "concurrent code patching"); + assert (instr_addr != NULL, "illegal address for code patching"); + NativeCall* n_call = nativeCall_at (instr_addr); // checking that it is a call + assert(NativeCall::instruction_size == 8, "wrong instruction size; must be 8"); + int i0 = ((int*)code_buffer)[0]; + int i1 = ((int*)code_buffer)[1]; + int* contention_addr = (int*) n_call->addr_at(1*BytesPerInstWord); + assert(inv_op(*contention_addr) == Assembler::arith_op || + *contention_addr == nop_instruction() || !VM_Version::v9_instructions_work(), + "must not interfere with original call"); + // The set_long_at calls do the ICacheInvalidate so we just need to do them in reverse order + n_call->set_long_at(1*BytesPerInstWord, i1); + n_call->set_long_at(0*BytesPerInstWord, i0); + // NOTE: It is possible that another thread T will execute + // only the second patched word. + // In other words, since the original instruction is this + // call patching_stub; nop (NativeCall) + // and the new sequence from the buffer is this: + // sethi %hi(K), %r; add %r, %lo(K), %r (NativeMovConstReg) + // what T will execute is this: + // call patching_stub; add %r, %lo(K), %r + // thereby putting garbage into %r before calling the patching stub. + // This is OK, because the patching stub ignores the value of %r. + + // Make sure the first-patched instruction, which may co-exist + // briefly with the call, will do something harmless. + assert(inv_op(*contention_addr) == Assembler::arith_op || + *contention_addr == nop_instruction() || !VM_Version::v9_instructions_work(), + "must not interfere with original call"); +} + +// Similar to replace_mt_safe, but just changes the destination. The +// important thing is that free-running threads are able to execute this +// call instruction at all times. Thus, the displacement field must be +// instruction-word-aligned. This is always true on SPARC. +// +// Used in the runtime linkage of calls; see class CompiledIC. +void NativeCall::set_destination_mt_safe(address dest) { + assert(Patching_lock->is_locked() || + SafepointSynchronize::is_at_safepoint(), "concurrent code patching"); + // set_destination uses set_long_at which does the ICache::invalidate + set_destination(dest); +} + +// Code for unit testing implementation of NativeCall class +void NativeCall::test() { +#ifdef ASSERT + ResourceMark rm; + CodeBuffer cb("test", 100, 100); + MacroAssembler* a = new MacroAssembler(&cb); + NativeCall *nc; + uint idx; + int offsets[] = { + 0x0, + 0xfffffff0, + 0x7ffffff0, + 0x80000000, + 0x20, + 0x4000, + }; + + VM_Version::allow_all(); + + a->call( a->pc(), relocInfo::none ); + a->delayed()->nop(); + nc = nativeCall_at( cb.code_begin() ); + nc->print(); + + nc = nativeCall_overwriting_at( nc->next_instruction_address() ); + for (idx = 0; idx < ARRAY_SIZE(offsets); idx++) { + nc->set_destination( cb.code_begin() + offsets[idx] ); + assert(nc->destination() == (cb.code_begin() + offsets[idx]), "check unit test"); + nc->print(); + } + + nc = nativeCall_before( cb.code_begin() + 8 ); + nc->print(); + + VM_Version::revert(); +#endif +} +// End code for unit testing implementation of NativeCall class + +//------------------------------------------------------------------- + +#ifdef _LP64 + +void NativeFarCall::set_destination(address dest) { + // Address materialized in the instruction stream, so nothing to do. + return; +#if 0 // What we'd do if we really did want to change the destination + if (destination() == dest) { + return; + } + ResourceMark rm; + CodeBuffer buf(addr_at(0), instruction_size + 1); + MacroAssembler* _masm = new MacroAssembler(&buf); + // Generate the new sequence + Address(O7, dest); + _masm->jumpl_to(dest, O7); + ICache::invalidate_range(addr_at(0), instruction_size ); +#endif +} + +void NativeFarCall::verify() { + // make sure code pattern is actually a jumpl_to instruction + assert((int)instruction_size == (int)NativeJump::instruction_size, "same as jump_to"); + assert((int)jmpl_offset == (int)NativeMovConstReg::add_offset, "sethi size ok"); + nativeJump_at(addr_at(0))->verify(); +} + +bool NativeFarCall::is_call_at(address instr) { + return nativeInstruction_at(instr)->is_sethi(); +} + +void NativeFarCall::print() { + tty->print_cr(INTPTR_FORMAT ": call " INTPTR_FORMAT, instruction_address(), destination()); +} + +bool NativeFarCall::destination_is_compiled_verified_entry_point() { + nmethod* callee = CodeCache::find_nmethod(destination()); + if (callee == NULL) { + return false; + } else { + return destination() == callee->verified_entry_point(); + } +} + +// MT-safe patching of a far call. +void NativeFarCall::replace_mt_safe(address instr_addr, address code_buffer) { + Unimplemented(); +} + +// Code for unit testing implementation of NativeFarCall class +void NativeFarCall::test() { + Unimplemented(); +} +// End code for unit testing implementation of NativeFarCall class + +#endif // _LP64 + +//------------------------------------------------------------------- + + +void NativeMovConstReg::verify() { + NativeInstruction::verify(); + // make sure code pattern is actually a "set_oop" synthetic instruction + // see MacroAssembler::set_oop() + int i0 = long_at(sethi_offset); + int i1 = long_at(add_offset); + + // verify the pattern "sethi %hi22(imm), reg ; add reg, %lo10(imm), reg" + Register rd = inv_rd(i0); +#ifndef _LP64 + if (!(is_op2(i0, Assembler::sethi_op2) && rd != G0 && + is_op3(i1, Assembler::add_op3, Assembler::arith_op) && + inv_immed(i1) && (unsigned)get_simm13(i1) < (1 << 10) && + rd == inv_rs1(i1) && rd == inv_rd(i1))) { + fatal("not a set_oop"); + } +#else + if (!is_op2(i0, Assembler::sethi_op2) && rd != G0 ) { + fatal("not a set_oop"); + } +#endif +} + + +void NativeMovConstReg::print() { + tty->print_cr(INTPTR_FORMAT ": mov reg, " INTPTR_FORMAT, instruction_address(), data()); +} + + +#ifdef _LP64 +intptr_t NativeMovConstReg::data() const { + return data64(addr_at(sethi_offset), long_at(add_offset)); +} +#else +intptr_t NativeMovConstReg::data() const { + return data32(long_at(sethi_offset), long_at(add_offset)); +} +#endif + + +void NativeMovConstReg::set_data(intptr_t x) { +#ifdef _LP64 + set_data64_sethi(addr_at(sethi_offset), x); +#else + set_long_at(sethi_offset, set_data32_sethi( long_at(sethi_offset), x)); +#endif + set_long_at(add_offset, set_data32_simm13( long_at(add_offset), x)); + + // also store the value into an oop_Relocation cell, if any + CodeBlob* nm = CodeCache::find_blob(instruction_address()); + if (nm != NULL) { + RelocIterator iter(nm, instruction_address(), next_instruction_address()); + oop* oop_addr = NULL; + while (iter.next()) { + if (iter.type() == relocInfo::oop_type) { + oop_Relocation *r = iter.oop_reloc(); + if (oop_addr == NULL) { + oop_addr = r->oop_addr(); + *oop_addr = (oop)x; + } else { + assert(oop_addr == r->oop_addr(), "must be only one set-oop here"); + } + } + } + } +} + + +// Code for unit testing implementation of NativeMovConstReg class +void NativeMovConstReg::test() { +#ifdef ASSERT + ResourceMark rm; + CodeBuffer cb("test", 100, 100); + MacroAssembler* a = new MacroAssembler(&cb); + NativeMovConstReg* nm; + uint idx; + int offsets[] = { + 0x0, + 0x7fffffff, + 0x80000000, + 0xffffffff, + 0x20, + 4096, + 4097, + }; + + VM_Version::allow_all(); + + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); + a->add(I3, low10(0xaaaabbbb), I3); + a->sethi(0xccccdddd, O2, true, RelocationHolder::none); + a->add(O2, low10(0xccccdddd), O2); + + nm = nativeMovConstReg_at( cb.code_begin() ); + nm->print(); + + nm = nativeMovConstReg_at( nm->next_instruction_address() ); + for (idx = 0; idx < ARRAY_SIZE(offsets); idx++) { + nm->set_data( offsets[idx] ); + assert(nm->data() == offsets[idx], "check unit test"); + } + nm->print(); + + VM_Version::revert(); +#endif +} +// End code for unit testing implementation of NativeMovConstReg class + +//------------------------------------------------------------------- + +void NativeMovConstRegPatching::verify() { + NativeInstruction::verify(); + // Make sure code pattern is sethi/nop/add. + int i0 = long_at(sethi_offset); + int i1 = long_at(nop_offset); + int i2 = long_at(add_offset); + assert((int)nop_offset == (int)NativeMovConstReg::add_offset, "sethi size ok"); + + // Verify the pattern "sethi %hi22(imm), reg; nop; add reg, %lo10(imm), reg" + // The casual reader should note that on Sparc a nop is a special case if sethi + // in which the destination register is %g0. + Register rd0 = inv_rd(i0); + Register rd1 = inv_rd(i1); + if (!(is_op2(i0, Assembler::sethi_op2) && rd0 != G0 && + is_op2(i1, Assembler::sethi_op2) && rd1 == G0 && // nop is a special case of sethi + is_op3(i2, Assembler::add_op3, Assembler::arith_op) && + inv_immed(i2) && (unsigned)get_simm13(i2) < (1 << 10) && + rd0 == inv_rs1(i2) && rd0 == inv_rd(i2))) { + fatal("not a set_oop"); + } +} + + +void NativeMovConstRegPatching::print() { + tty->print_cr(INTPTR_FORMAT ": mov reg, " INTPTR_FORMAT, instruction_address(), data()); +} + + +int NativeMovConstRegPatching::data() const { +#ifdef _LP64 + return data64(addr_at(sethi_offset), long_at(add_offset)); +#else + return data32(long_at(sethi_offset), long_at(add_offset)); +#endif +} + + +void NativeMovConstRegPatching::set_data(int x) { +#ifdef _LP64 + set_data64_sethi(addr_at(sethi_offset), x); +#else + set_long_at(sethi_offset, set_data32_sethi(long_at(sethi_offset), x)); +#endif + set_long_at(add_offset, set_data32_simm13(long_at(add_offset), x)); + + // also store the value into an oop_Relocation cell, if any + CodeBlob* nm = CodeCache::find_blob(instruction_address()); + if (nm != NULL) { + RelocIterator iter(nm, instruction_address(), next_instruction_address()); + oop* oop_addr = NULL; + while (iter.next()) { + if (iter.type() == relocInfo::oop_type) { + oop_Relocation *r = iter.oop_reloc(); + if (oop_addr == NULL) { + oop_addr = r->oop_addr(); + *oop_addr = (oop)x; + } else { + assert(oop_addr == r->oop_addr(), "must be only one set-oop here"); + } + } + } + } +} + + +// Code for unit testing implementation of NativeMovConstRegPatching class +void NativeMovConstRegPatching::test() { +#ifdef ASSERT + ResourceMark rm; + CodeBuffer cb("test", 100, 100); + MacroAssembler* a = new MacroAssembler(&cb); + NativeMovConstRegPatching* nm; + uint idx; + int offsets[] = { + 0x0, + 0x7fffffff, + 0x80000000, + 0xffffffff, + 0x20, + 4096, + 4097, + }; + + VM_Version::allow_all(); + + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); + a->nop(); + a->add(I3, low10(0xaaaabbbb), I3); + a->sethi(0xccccdddd, O2, true, RelocationHolder::none); + a->nop(); + a->add(O2, low10(0xccccdddd), O2); + + nm = nativeMovConstRegPatching_at( cb.code_begin() ); + nm->print(); + + nm = nativeMovConstRegPatching_at( nm->next_instruction_address() ); + for (idx = 0; idx < ARRAY_SIZE(offsets); idx++) { + nm->set_data( offsets[idx] ); + assert(nm->data() == offsets[idx], "check unit test"); + } + nm->print(); + + VM_Version::revert(); +#endif // ASSERT +} +// End code for unit testing implementation of NativeMovConstRegPatching class + + +//------------------------------------------------------------------- + + +void NativeMovRegMem::copy_instruction_to(address new_instruction_address) { + Untested("copy_instruction_to"); + int instruction_size = next_instruction_address() - instruction_address(); + for (int i = 0; i < instruction_size; i += BytesPerInstWord) { + *(int*)(new_instruction_address + i) = *(int*)(address(this) + i); + } +} + + +void NativeMovRegMem::verify() { + NativeInstruction::verify(); + // make sure code pattern is actually a "ld" or "st" of some sort. + int i0 = long_at(0); + int op3 = inv_op3(i0); + + assert((int)add_offset == NativeMovConstReg::add_offset, "sethi size ok"); + + if (!(is_op(i0, Assembler::ldst_op) && + inv_immed(i0) && + 0 != (op3 < op3_ldst_int_limit + ? (1 << op3 ) & (op3_mask_ld | op3_mask_st) + : (1 << (op3 - op3_ldst_int_limit)) & (op3_mask_ldf | op3_mask_stf)))) + { + int i1 = long_at(ldst_offset); + Register rd = inv_rd(i0); + + op3 = inv_op3(i1); + if (!is_op(i1, Assembler::ldst_op) && rd == inv_rs2(i1) && + 0 != (op3 < op3_ldst_int_limit + ? (1 << op3 ) & (op3_mask_ld | op3_mask_st) + : (1 << (op3 - op3_ldst_int_limit)) & (op3_mask_ldf | op3_mask_stf))) { + fatal("not a ld* or st* op"); + } + } +} + + +void NativeMovRegMem::print() { + if (is_immediate()) { + tty->print_cr(INTPTR_FORMAT ": mov reg, [reg + %x]", instruction_address(), offset()); + } else { + tty->print_cr(INTPTR_FORMAT ": mov reg, [reg + reg]", instruction_address()); + } +} + + +// Code for unit testing implementation of NativeMovRegMem class +void NativeMovRegMem::test() { +#ifdef ASSERT + ResourceMark rm; + CodeBuffer cb("test", 1000, 1000); + MacroAssembler* a = new MacroAssembler(&cb); + NativeMovRegMem* nm; + uint idx = 0; + uint idx1; + int offsets[] = { + 0x0, + 0xffffffff, + 0x7fffffff, + 0x80000000, + 4096, + 4097, + 0x20, + 0x4000, + }; + + VM_Version::allow_all(); + + a->ldsw( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->ldsw( G5, I3, G4 ); idx++; + a->ldsb( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->ldsb( G5, I3, G4 ); idx++; + a->ldsh( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->ldsh( G5, I3, G4 ); idx++; + a->lduw( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->lduw( G5, I3, G4 ); idx++; + a->ldub( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->ldub( G5, I3, G4 ); idx++; + a->lduh( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->lduh( G5, I3, G4 ); idx++; + a->ldx( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->ldx( G5, I3, G4 ); idx++; + a->ldd( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->ldd( G5, I3, G4 ); idx++; + a->ldf( FloatRegisterImpl::D, O2, -1, F14 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->ldf( FloatRegisterImpl::S, O0, I3, F15 ); idx++; + + a->stw( G5, G4, low10(0xffffffff) ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->stw( G5, G4, I3 ); idx++; + a->stb( G5, G4, low10(0xffffffff) ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->stb( G5, G4, I3 ); idx++; + a->sth( G5, G4, low10(0xffffffff) ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->sth( G5, G4, I3 ); idx++; + a->stx( G5, G4, low10(0xffffffff) ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->stx( G5, G4, I3 ); idx++; + a->std( G5, G4, low10(0xffffffff) ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->std( G5, G4, I3 ); idx++; + a->stf( FloatRegisterImpl::S, F18, O2, -1 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->add(I3, low10(0xaaaabbbb), I3); + a->stf( FloatRegisterImpl::S, F15, O0, I3 ); idx++; + + nm = nativeMovRegMem_at( cb.code_begin() ); + nm->print(); + nm->set_offset( low10(0) ); + nm->print(); + nm->add_offset_in_bytes( low10(0xbb) * wordSize ); + nm->print(); + + while (--idx) { + nm = nativeMovRegMem_at( nm->next_instruction_address() ); + nm->print(); + for (idx1 = 0; idx1 < ARRAY_SIZE(offsets); idx1++) { + nm->set_offset( nm->is_immediate() ? low10(offsets[idx1]) : offsets[idx1] ); + assert(nm->offset() == (nm->is_immediate() ? low10(offsets[idx1]) : offsets[idx1]), + "check unit test"); + nm->print(); + } + nm->add_offset_in_bytes( low10(0xbb) * wordSize ); + nm->print(); + } + + VM_Version::revert(); +#endif // ASSERT +} + +// End code for unit testing implementation of NativeMovRegMem class + +//-------------------------------------------------------------------------------- + + +void NativeMovRegMemPatching::copy_instruction_to(address new_instruction_address) { + Untested("copy_instruction_to"); + int instruction_size = next_instruction_address() - instruction_address(); + for (int i = 0; i < instruction_size; i += wordSize) { + *(long*)(new_instruction_address + i) = *(long*)(address(this) + i); + } +} + + +void NativeMovRegMemPatching::verify() { + NativeInstruction::verify(); + // make sure code pattern is actually a "ld" or "st" of some sort. + int i0 = long_at(0); + int op3 = inv_op3(i0); + + assert((int)nop_offset == (int)NativeMovConstReg::add_offset, "sethi size ok"); + + if (!(is_op(i0, Assembler::ldst_op) && + inv_immed(i0) && + 0 != (op3 < op3_ldst_int_limit + ? (1 << op3 ) & (op3_mask_ld | op3_mask_st) + : (1 << (op3 - op3_ldst_int_limit)) & (op3_mask_ldf | op3_mask_stf)))) { + int i1 = long_at(ldst_offset); + Register rd = inv_rd(i0); + + op3 = inv_op3(i1); + if (!is_op(i1, Assembler::ldst_op) && rd == inv_rs2(i1) && + 0 != (op3 < op3_ldst_int_limit + ? (1 << op3 ) & (op3_mask_ld | op3_mask_st) + : (1 << (op3 - op3_ldst_int_limit)) & (op3_mask_ldf | op3_mask_stf))) { + fatal("not a ld* or st* op"); + } + } +} + + +void NativeMovRegMemPatching::print() { + if (is_immediate()) { + tty->print_cr(INTPTR_FORMAT ": mov reg, [reg + %x]", instruction_address(), offset()); + } else { + tty->print_cr(INTPTR_FORMAT ": mov reg, [reg + reg]", instruction_address()); + } +} + + +// Code for unit testing implementation of NativeMovRegMemPatching class +void NativeMovRegMemPatching::test() { +#ifdef ASSERT + ResourceMark rm; + CodeBuffer cb("test", 1000, 1000); + MacroAssembler* a = new MacroAssembler(&cb); + NativeMovRegMemPatching* nm; + uint idx = 0; + uint idx1; + int offsets[] = { + 0x0, + 0xffffffff, + 0x7fffffff, + 0x80000000, + 4096, + 4097, + 0x20, + 0x4000, + }; + + VM_Version::allow_all(); + + a->ldsw( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->ldsw( G5, I3, G4 ); idx++; + a->ldsb( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->ldsb( G5, I3, G4 ); idx++; + a->ldsh( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->ldsh( G5, I3, G4 ); idx++; + a->lduw( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->lduw( G5, I3, G4 ); idx++; + a->ldub( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->ldub( G5, I3, G4 ); idx++; + a->lduh( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->lduh( G5, I3, G4 ); idx++; + a->ldx( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->ldx( G5, I3, G4 ); idx++; + a->ldd( G5, low10(0xffffffff), G4 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->ldd( G5, I3, G4 ); idx++; + a->ldf( FloatRegisterImpl::D, O2, -1, F14 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->ldf( FloatRegisterImpl::S, O0, I3, F15 ); idx++; + + a->stw( G5, G4, low10(0xffffffff) ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->stw( G5, G4, I3 ); idx++; + a->stb( G5, G4, low10(0xffffffff) ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->stb( G5, G4, I3 ); idx++; + a->sth( G5, G4, low10(0xffffffff) ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->sth( G5, G4, I3 ); idx++; + a->stx( G5, G4, low10(0xffffffff) ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->stx( G5, G4, I3 ); idx++; + a->std( G5, G4, low10(0xffffffff) ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->std( G5, G4, I3 ); idx++; + a->stf( FloatRegisterImpl::S, F18, O2, -1 ); idx++; + a->sethi(0xaaaabbbb, I3, true, RelocationHolder::none); a->nop(); a->add(I3, low10(0xaaaabbbb), I3); + a->stf( FloatRegisterImpl::S, F15, O0, I3 ); idx++; + + nm = nativeMovRegMemPatching_at( cb.code_begin() ); + nm->print(); + nm->set_offset( low10(0) ); + nm->print(); + nm->add_offset_in_bytes( low10(0xbb) * wordSize ); + nm->print(); + + while (--idx) { + nm = nativeMovRegMemPatching_at( nm->next_instruction_address() ); + nm->print(); + for (idx1 = 0; idx1 < ARRAY_SIZE(offsets); idx1++) { + nm->set_offset( nm->is_immediate() ? low10(offsets[idx1]) : offsets[idx1] ); + assert(nm->offset() == (nm->is_immediate() ? low10(offsets[idx1]) : offsets[idx1]), + "check unit test"); + nm->print(); + } + nm->add_offset_in_bytes( low10(0xbb) * wordSize ); + nm->print(); + } + + VM_Version::revert(); +#endif // ASSERT +} +// End code for unit testing implementation of NativeMovRegMemPatching class + + +//-------------------------------------------------------------------------------- + + +void NativeJump::verify() { + NativeInstruction::verify(); + int i0 = long_at(sethi_offset); + int i1 = long_at(jmpl_offset); + assert((int)jmpl_offset == (int)NativeMovConstReg::add_offset, "sethi size ok"); + // verify the pattern "sethi %hi22(imm), treg ; jmpl treg, %lo10(imm), lreg" + Register rd = inv_rd(i0); +#ifndef _LP64 + if (!(is_op2(i0, Assembler::sethi_op2) && rd != G0 && + (is_op3(i1, Assembler::jmpl_op3, Assembler::arith_op) || + (TraceJumps && is_op3(i1, Assembler::add_op3, Assembler::arith_op))) && + inv_immed(i1) && (unsigned)get_simm13(i1) < (1 << 10) && + rd == inv_rs1(i1))) { + fatal("not a jump_to instruction"); + } +#else + // In LP64, the jump instruction location varies for non relocatable + // jumps, for example is could be sethi, xor, jmp instead of the + // 7 instructions for sethi. So let's check sethi only. + if (!is_op2(i0, Assembler::sethi_op2) && rd != G0 ) { + fatal("not a jump_to instruction"); + } +#endif +} + + +void NativeJump::print() { + tty->print_cr(INTPTR_FORMAT ": jmpl reg, " INTPTR_FORMAT, instruction_address(), jump_destination()); +} + + +// Code for unit testing implementation of NativeJump class +void NativeJump::test() { +#ifdef ASSERT + ResourceMark rm; + CodeBuffer cb("test", 100, 100); + MacroAssembler* a = new MacroAssembler(&cb); + NativeJump* nj; + uint idx; + int offsets[] = { + 0x0, + 0xffffffff, + 0x7fffffff, + 0x80000000, + 4096, + 4097, + 0x20, + 0x4000, + }; + + VM_Version::allow_all(); + + a->sethi(0x7fffbbbb, I3, true, RelocationHolder::none); + a->jmpl(I3, low10(0x7fffbbbb), G0, RelocationHolder::none); + a->delayed()->nop(); + a->sethi(0x7fffbbbb, I3, true, RelocationHolder::none); + a->jmpl(I3, low10(0x7fffbbbb), L3, RelocationHolder::none); + a->delayed()->nop(); + + nj = nativeJump_at( cb.code_begin() ); + nj->print(); + + nj = nativeJump_at( nj->next_instruction_address() ); + for (idx = 0; idx < ARRAY_SIZE(offsets); idx++) { + nj->set_jump_destination( nj->instruction_address() + offsets[idx] ); + assert(nj->jump_destination() == (nj->instruction_address() + offsets[idx]), "check unit test"); + nj->print(); + } + + VM_Version::revert(); +#endif // ASSERT +} +// End code for unit testing implementation of NativeJump class + + +void NativeJump::insert(address code_pos, address entry) { + Unimplemented(); +} + +// MT safe inserting of a jump over an unknown instruction sequence (used by nmethod::makeZombie) +// The problem: jump_to is a 3-word instruction (including its delay slot). +// Atomic write can be only with 1 word. +void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) { + // Here's one way to do it: Pre-allocate a three-word jump sequence somewhere + // in the header of the nmethod, within a short branch's span of the patch point. + // Set up the jump sequence using NativeJump::insert, and then use an annulled + // unconditional branch at the target site (an atomic 1-word update). + // Limitations: You can only patch nmethods, with any given nmethod patched at + // most once, and the patch must be in the nmethod's header. + // It's messy, but you can ask the CodeCache for the nmethod containing the + // target address. + + // %%%%% For now, do something MT-stupid: + ResourceMark rm; + int code_size = 1 * BytesPerInstWord; + CodeBuffer cb(verified_entry, code_size + 1); + MacroAssembler* a = new MacroAssembler(&cb); + if (VM_Version::v9_instructions_work()) { + a->ldsw(G0, 0, O7); // "ld" must agree with code in the signal handler + } else { + a->lduw(G0, 0, O7); // "ld" must agree with code in the signal handler + } + ICache::invalidate_range(verified_entry, code_size); +} + + +void NativeIllegalInstruction::insert(address code_pos) { + NativeIllegalInstruction* nii = (NativeIllegalInstruction*) nativeInstruction_at(code_pos); + nii->set_long_at(0, illegal_instruction()); +} + +static int illegal_instruction_bits = 0; + +int NativeInstruction::illegal_instruction() { + if (illegal_instruction_bits == 0) { + ResourceMark rm; + char buf[40]; + CodeBuffer cbuf((address)&buf[0], 20); + MacroAssembler* a = new MacroAssembler(&cbuf); + address ia = a->pc(); + a->trap(ST_RESERVED_FOR_USER_0 + 1); + int bits = *(int*)ia; + assert(is_op3(bits, Assembler::trap_op3, Assembler::arith_op), "bad instruction"); + illegal_instruction_bits = bits; + assert(illegal_instruction_bits != 0, "oops"); + } + return illegal_instruction_bits; +} + +static int ic_miss_trap_bits = 0; + +bool NativeInstruction::is_ic_miss_trap() { + if (ic_miss_trap_bits == 0) { + ResourceMark rm; + char buf[40]; + CodeBuffer cbuf((address)&buf[0], 20); + MacroAssembler* a = new MacroAssembler(&cbuf); + address ia = a->pc(); + a->trap(Assembler::notEqual, Assembler::ptr_cc, G0, ST_RESERVED_FOR_USER_0 + 2); + int bits = *(int*)ia; + assert(is_op3(bits, Assembler::trap_op3, Assembler::arith_op), "bad instruction"); + ic_miss_trap_bits = bits; + assert(ic_miss_trap_bits != 0, "oops"); + } + return long_at(0) == ic_miss_trap_bits; +} + + +bool NativeInstruction::is_illegal() { + if (illegal_instruction_bits == 0) { + return false; + } + return long_at(0) == illegal_instruction_bits; +} + + +void NativeGeneralJump::verify() { + assert(((NativeInstruction *)this)->is_jump() || + ((NativeInstruction *)this)->is_cond_jump(), "not a general jump instruction"); +} + + +void NativeGeneralJump::insert_unconditional(address code_pos, address entry) { + Assembler::Condition condition = Assembler::always; + int x = Assembler::op2(Assembler::br_op2) | Assembler::annul(false) | + Assembler::cond(condition) | Assembler::wdisp((intptr_t)entry, (intptr_t)code_pos, 22); + NativeGeneralJump* ni = (NativeGeneralJump*) nativeInstruction_at(code_pos); + ni->set_long_at(0, x); +} + + +// MT-safe patching of a jmp instruction (and following word). +// First patches the second word, and then atomicly replaces +// the first word with the first new instruction word. +// Other processors might briefly see the old first word +// followed by the new second word. This is OK if the old +// second word is harmless, and the new second word may be +// harmlessly executed in the delay slot of the call. +void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) { + assert(Patching_lock->is_locked() || + SafepointSynchronize::is_at_safepoint(), "concurrent code patching"); + assert (instr_addr != NULL, "illegal address for code patching"); + NativeGeneralJump* h_jump = nativeGeneralJump_at (instr_addr); // checking that it is a call + assert(NativeGeneralJump::instruction_size == 8, "wrong instruction size; must be 8"); + int i0 = ((int*)code_buffer)[0]; + int i1 = ((int*)code_buffer)[1]; + int* contention_addr = (int*) h_jump->addr_at(1*BytesPerInstWord); + assert(inv_op(*contention_addr) == Assembler::arith_op || + *contention_addr == nop_instruction() || !VM_Version::v9_instructions_work(), + "must not interfere with original call"); + // The set_long_at calls do the ICacheInvalidate so we just need to do them in reverse order + h_jump->set_long_at(1*BytesPerInstWord, i1); + h_jump->set_long_at(0*BytesPerInstWord, i0); + // NOTE: It is possible that another thread T will execute + // only the second patched word. + // In other words, since the original instruction is this + // jmp patching_stub; nop (NativeGeneralJump) + // and the new sequence from the buffer is this: + // sethi %hi(K), %r; add %r, %lo(K), %r (NativeMovConstReg) + // what T will execute is this: + // jmp patching_stub; add %r, %lo(K), %r + // thereby putting garbage into %r before calling the patching stub. + // This is OK, because the patching stub ignores the value of %r. + + // Make sure the first-patched instruction, which may co-exist + // briefly with the call, will do something harmless. + assert(inv_op(*contention_addr) == Assembler::arith_op || + *contention_addr == nop_instruction() || !VM_Version::v9_instructions_work(), + "must not interfere with original call"); +} diff --git a/hotspot/src/cpu/sparc/vm/nativeInst_sparc.hpp b/hotspot/src/cpu/sparc/vm/nativeInst_sparc.hpp new file mode 100644 index 00000000000..ff0913515c7 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/nativeInst_sparc.hpp @@ -0,0 +1,914 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// We have interface for the following instructions: +// - NativeInstruction +// - - NativeCall +// - - NativeFarCall +// - - NativeMovConstReg +// - - NativeMovConstRegPatching +// - - NativeMovRegMem +// - - NativeMovRegMemPatching +// - - NativeJump +// - - NativeGeneralJump +// - - NativeIllegalInstruction +// The base class for different kinds of native instruction abstractions. +// Provides the primitive operations to manipulate code relative to this. +class NativeInstruction VALUE_OBJ_CLASS_SPEC { + friend class Relocation; + + public: + enum Sparc_specific_constants { + nop_instruction_size = 4 + }; + + bool is_nop() { return long_at(0) == nop_instruction(); } + bool is_call() { return is_op(long_at(0), Assembler::call_op); } + bool is_sethi() { return (is_op2(long_at(0), Assembler::sethi_op2) + && inv_rd(long_at(0)) != G0); } + + bool sets_cc() { + // conservative (returns true for some instructions that do not set the + // the condition code, such as, "save". + // Does not return true for the deprecated tagged instructions, such as, TADDcc + int x = long_at(0); + return (is_op(x, Assembler::arith_op) && + (inv_op3(x) & Assembler::cc_bit_op3) == Assembler::cc_bit_op3); + } + bool is_illegal(); + bool is_zombie() { + int x = long_at(0); + return is_op3(x, + VM_Version::v9_instructions_work() ? + Assembler::ldsw_op3 : Assembler::lduw_op3, + Assembler::ldst_op) + && Assembler::inv_rs1(x) == G0 + && Assembler::inv_rd(x) == O7; + } + bool is_ic_miss_trap(); // Inline-cache uses a trap to detect a miss + bool is_return() { + // is it the output of MacroAssembler::ret or MacroAssembler::retl? + int x = long_at(0); + const int pc_return_offset = 8; // see frame_sparc.hpp + return is_op3(x, Assembler::jmpl_op3, Assembler::arith_op) + && (inv_rs1(x) == I7 || inv_rs1(x) == O7) + && inv_immed(x) && inv_simm(x, 13) == pc_return_offset + && inv_rd(x) == G0; + } + bool is_int_jump() { + // is it the output of MacroAssembler::b? + int x = long_at(0); + return is_op2(x, Assembler::bp_op2) || is_op2(x, Assembler::br_op2); + } + bool is_float_jump() { + // is it the output of MacroAssembler::fb? + int x = long_at(0); + return is_op2(x, Assembler::fbp_op2) || is_op2(x, Assembler::fb_op2); + } + bool is_jump() { + return is_int_jump() || is_float_jump(); + } + bool is_cond_jump() { + int x = long_at(0); + return (is_int_jump() && Assembler::inv_cond(x) != Assembler::always) || + (is_float_jump() && Assembler::inv_cond(x) != Assembler::f_always); + } + + bool is_stack_bang() { + int x = long_at(0); + return is_op3(x, Assembler::stw_op3, Assembler::ldst_op) && + (inv_rd(x) == G0) && (inv_rs1(x) == SP) && (inv_rs2(x) == G3_scratch); + } + + bool is_prefetch() { + int x = long_at(0); + return is_op3(x, Assembler::prefetch_op3, Assembler::ldst_op); + } + + bool is_membar() { + int x = long_at(0); + return is_op3(x, Assembler::membar_op3, Assembler::arith_op) && + (inv_rd(x) == G0) && (inv_rs1(x) == O7); + } + + bool is_safepoint_poll() { + int x = long_at(0); +#ifdef _LP64 + return is_op3(x, Assembler::ldx_op3, Assembler::ldst_op) && +#else + return is_op3(x, Assembler::lduw_op3, Assembler::ldst_op) && +#endif + (inv_rd(x) == G0) && (inv_immed(x) ? Assembler::inv_simm13(x) == 0 : inv_rs2(x) == G0); + } + + bool is_zero_test(Register ®); + bool is_load_store_with_small_offset(Register reg); + + public: +#ifdef ASSERT + static int rdpc_instruction() { return Assembler::op(Assembler::arith_op ) | Assembler::op3(Assembler::rdreg_op3) | Assembler::u_field(5, 18, 14) | Assembler::rd(O7); } +#else + // Temporary fix: in optimized mode, u_field is a macro for efficiency reasons (see Assembler::u_field) - needs to be fixed + static int rdpc_instruction() { return Assembler::op(Assembler::arith_op ) | Assembler::op3(Assembler::rdreg_op3) | u_field(5, 18, 14) | Assembler::rd(O7); } +#endif + static int nop_instruction() { return Assembler::op(Assembler::branch_op) | Assembler::op2(Assembler::sethi_op2); } + static int illegal_instruction(); // the output of __ breakpoint_trap() + static int call_instruction(address destination, address pc) { return Assembler::op(Assembler::call_op) | Assembler::wdisp((intptr_t)destination, (intptr_t)pc, 30); } + + static int branch_instruction(Assembler::op2s op2val, Assembler::Condition c, bool a) { + return Assembler::op(Assembler::branch_op) | Assembler::op2(op2val) | Assembler::annul(a) | Assembler::cond(c); + } + + static int op3_instruction(Assembler::ops opval, Register rd, Assembler::op3s op3val, Register rs1, int simm13a) { + return Assembler::op(opval) | Assembler::rd(rd) | Assembler::op3(op3val) | Assembler::rs1(rs1) | Assembler::immed(true) | Assembler::simm(simm13a, 13); + } + + static int sethi_instruction(Register rd, int imm22a) { + return Assembler::op(Assembler::branch_op) | Assembler::rd(rd) | Assembler::op2(Assembler::sethi_op2) | Assembler::hi22(imm22a); + } + + protected: + address addr_at(int offset) const { return address(this) + offset; } + int long_at(int offset) const { return *(int*)addr_at(offset); } + void set_long_at(int offset, int i); /* deals with I-cache */ + void set_jlong_at(int offset, jlong i); /* deals with I-cache */ + void set_addr_at(int offset, address x); /* deals with I-cache */ + + address instruction_address() const { return addr_at(0); } + address next_instruction_address() const { return addr_at(BytesPerInstWord); } + + static bool is_op( int x, Assembler::ops opval) { + return Assembler::inv_op(x) == opval; + } + static bool is_op2(int x, Assembler::op2s op2val) { + return Assembler::inv_op(x) == Assembler::branch_op && Assembler::inv_op2(x) == op2val; + } + static bool is_op3(int x, Assembler::op3s op3val, Assembler::ops opval) { + return Assembler::inv_op(x) == opval && Assembler::inv_op3(x) == op3val; + } + + // utilities to help subclasses decode: + static Register inv_rd( int x ) { return Assembler::inv_rd( x); } + static Register inv_rs1( int x ) { return Assembler::inv_rs1(x); } + static Register inv_rs2( int x ) { return Assembler::inv_rs2(x); } + + static bool inv_immed( int x ) { return Assembler::inv_immed(x); } + static bool inv_annul( int x ) { return (Assembler::annul(true) & x) != 0; } + static int inv_cond( int x ) { return Assembler::inv_cond(x); } + + static int inv_op( int x ) { return Assembler::inv_op( x); } + static int inv_op2( int x ) { return Assembler::inv_op2(x); } + static int inv_op3( int x ) { return Assembler::inv_op3(x); } + + static int inv_simm( int x, int nbits ) { return Assembler::inv_simm(x, nbits); } + static intptr_t inv_wdisp( int x, int nbits ) { return Assembler::inv_wdisp( x, 0, nbits); } + static intptr_t inv_wdisp16( int x ) { return Assembler::inv_wdisp16(x, 0); } + static int branch_destination_offset(int x) { return Assembler::branch_destination(x, 0); } + static int patch_branch_destination_offset(int dest_offset, int x) { + return Assembler::patched_branch(dest_offset, x, 0); + } + void set_annul_bit() { set_long_at(0, long_at(0) | Assembler::annul(true)); } + + // utility for checking if x is either of 2 small constants + static bool is_either(int x, int k1, int k2) { + // return x == k1 || x == k2; + return (1 << x) & (1 << k1 | 1 << k2); + } + + // utility for checking overflow of signed instruction fields + static bool fits_in_simm(int x, int nbits) { + // cf. Assembler::assert_signed_range() + // return -(1 << nbits-1) <= x && x < ( 1 << nbits-1), + return (unsigned)(x + (1 << nbits-1)) < (unsigned)(1 << nbits); + } + + // set a signed immediate field + static int set_simm(int insn, int imm, int nbits) { + return (insn &~ Assembler::simm(-1, nbits)) | Assembler::simm(imm, nbits); + } + + // set a wdisp field (disp should be the difference of two addresses) + static int set_wdisp(int insn, intptr_t disp, int nbits) { + return (insn &~ Assembler::wdisp((intptr_t)-4, (intptr_t)0, nbits)) | Assembler::wdisp(disp, 0, nbits); + } + + static int set_wdisp16(int insn, intptr_t disp) { + return (insn &~ Assembler::wdisp16((intptr_t)-4, 0)) | Assembler::wdisp16(disp, 0); + } + + // get a simm13 field from an arithmetic or memory instruction + static int get_simm13(int insn) { + assert(is_either(Assembler::inv_op(insn), + Assembler::arith_op, Assembler::ldst_op) && + (insn & Assembler::immed(true)), "must have a simm13 field"); + return Assembler::inv_simm(insn, 13); + } + + // set the simm13 field of an arithmetic or memory instruction + static bool set_simm13(int insn, int imm) { + get_simm13(insn); // tickle the assertion check + return set_simm(insn, imm, 13); + } + + // combine the fields of a sethi stream (7 instructions ) and an add, jmp or ld/st + static intptr_t data64( address pc, int arith_insn ) { + assert(is_op2(*(unsigned int *)pc, Assembler::sethi_op2), "must be sethi"); + intptr_t hi = (intptr_t)gethi( (unsigned int *)pc ); + intptr_t lo = (intptr_t)get_simm13(arith_insn); + assert((unsigned)lo < (1 << 10), "offset field of set_oop must be 10 bits"); + return hi | lo; + } + + // Regenerate the instruction sequence that performs the 64 bit + // sethi. This only does the sethi. The disp field (bottom 10 bits) + // must be handled seperately. + static void set_data64_sethi(address instaddr, intptr_t x); + + // combine the fields of a sethi/simm13 pair (simm13 = or, add, jmpl, ld/st) + static int data32(int sethi_insn, int arith_insn) { + assert(is_op2(sethi_insn, Assembler::sethi_op2), "must be sethi"); + int hi = Assembler::inv_hi22(sethi_insn); + int lo = get_simm13(arith_insn); + assert((unsigned)lo < (1 << 10), "offset field of set_oop must be 10 bits"); + return hi | lo; + } + + static int set_data32_sethi(int sethi_insn, int imm) { + // note that Assembler::hi22 clips the low 10 bits for us + assert(is_op2(sethi_insn, Assembler::sethi_op2), "must be sethi"); + return (sethi_insn &~ Assembler::hi22(-1)) | Assembler::hi22(imm); + } + + static int set_data32_simm13(int arith_insn, int imm) { + get_simm13(arith_insn); // tickle the assertion check + int imm10 = Assembler::low10(imm); + return (arith_insn &~ Assembler::simm(-1, 13)) | Assembler::simm(imm10, 13); + } + + static int low10(int imm) { + return Assembler::low10(imm); + } + + // Perform the inverse of the LP64 Macroassembler::sethi + // routine. Extracts the 54 bits of address from the instruction + // stream. This routine must agree with the sethi routine in + // assembler_inline_sparc.hpp + static address gethi( unsigned int *pc ) { + int i = 0; + uintptr_t adr; + // We first start out with the real sethi instruction + assert(is_op2(*pc, Assembler::sethi_op2), "in gethi - must be sethi"); + adr = (unsigned int)Assembler::inv_hi22( *(pc++) ); + i++; + while ( i < 7 ) { + // We're done if we hit a nop + if ( (int)*pc == nop_instruction() ) break; + assert ( Assembler::inv_op(*pc) == Assembler::arith_op, "in gethi - must be arith_op" ); + switch ( Assembler::inv_op3(*pc) ) { + case Assembler::xor_op3: + adr ^= (intptr_t)get_simm13( *pc ); + return ( (address)adr ); + break; + case Assembler::sll_op3: + adr <<= ( *pc & 0x3f ); + break; + case Assembler::or_op3: + adr |= (intptr_t)get_simm13( *pc ); + break; + default: + assert ( 0, "in gethi - Should not reach here" ); + break; + } + pc++; + i++; + } + return ( (address)adr ); + } + + public: + void verify(); + void print(); + + // unit test stuff + static void test() {} // override for testing + + inline friend NativeInstruction* nativeInstruction_at(address address); +}; + +inline NativeInstruction* nativeInstruction_at(address address) { + NativeInstruction* inst = (NativeInstruction*)address; +#ifdef ASSERT + inst->verify(); +#endif + return inst; +} + + + +//----------------------------------------------------------------------------- + +// The NativeCall is an abstraction for accessing/manipulating native call imm32 instructions. +// (used to manipulate inline caches, primitive & dll calls, etc.) +inline NativeCall* nativeCall_at(address instr); +inline NativeCall* nativeCall_overwriting_at(address instr, + address destination); +inline NativeCall* nativeCall_before(address return_address); +class NativeCall: public NativeInstruction { + public: + enum Sparc_specific_constants { + instruction_size = 8, + return_address_offset = 8, + call_displacement_width = 30, + displacement_offset = 0, + instruction_offset = 0 + }; + address instruction_address() const { return addr_at(0); } + address next_instruction_address() const { return addr_at(instruction_size); } + address return_address() const { return addr_at(return_address_offset); } + + address destination() const { return inv_wdisp(long_at(0), call_displacement_width) + instruction_address(); } + address displacement_address() const { return addr_at(displacement_offset); } + void set_destination(address dest) { set_long_at(0, set_wdisp(long_at(0), dest - instruction_address(), call_displacement_width)); } + void set_destination_mt_safe(address dest); + + void verify_alignment() {} // do nothing on sparc + void verify(); + void print(); + + // unit test stuff + static void test(); + + // Creation + friend inline NativeCall* nativeCall_at(address instr); + friend NativeCall* nativeCall_overwriting_at(address instr, address destination = NULL) { + // insert a "blank" call: + NativeCall* call = (NativeCall*)instr; + call->set_long_at(0 * BytesPerInstWord, call_instruction(destination, instr)); + call->set_long_at(1 * BytesPerInstWord, nop_instruction()); + assert(call->addr_at(2 * BytesPerInstWord) - instr == instruction_size, "instruction size"); + // check its structure now: + assert(nativeCall_at(instr)->destination() == destination, "correct call destination"); + return call; + } + + friend inline NativeCall* nativeCall_before(address return_address) { + NativeCall* call = (NativeCall*)(return_address - return_address_offset); + #ifdef ASSERT + call->verify(); + #endif + return call; + } + + static bool is_call_at(address instr) { + return nativeInstruction_at(instr)->is_call(); + } + + static bool is_call_before(address instr) { + return nativeInstruction_at(instr - return_address_offset)->is_call(); + } + + static bool is_call_to(address instr, address target) { + return nativeInstruction_at(instr)->is_call() && + nativeCall_at(instr)->destination() == target; + } + + // MT-safe patching of a call instruction. + static void insert(address code_pos, address entry) { + (void)nativeCall_overwriting_at(code_pos, entry); + } + + static void replace_mt_safe(address instr_addr, address code_buffer); +}; +inline NativeCall* nativeCall_at(address instr) { + NativeCall* call = (NativeCall*)instr; +#ifdef ASSERT + call->verify(); +#endif + return call; +} + +// The NativeFarCall is an abstraction for accessing/manipulating native call-anywhere +// instructions in the sparcv9 vm. Used to call native methods which may be loaded +// anywhere in the address space, possibly out of reach of a call instruction. + +#ifndef _LP64 + +// On 32-bit systems, a far call is the same as a near one. +class NativeFarCall; +inline NativeFarCall* nativeFarCall_at(address instr); +class NativeFarCall : public NativeCall { +public: + friend inline NativeFarCall* nativeFarCall_at(address instr) { return (NativeFarCall*)nativeCall_at(instr); } + friend NativeFarCall* nativeFarCall_overwriting_at(address instr, address destination = NULL) + { return (NativeFarCall*)nativeCall_overwriting_at(instr, destination); } + friend NativeFarCall* nativeFarCall_before(address return_address) + { return (NativeFarCall*)nativeCall_before(return_address); } +}; + +#else + +// The format of this extended-range call is: +// jumpl_to addr, lreg +// == sethi %hi54(addr), O7 ; jumpl O7, %lo10(addr), O7 ; +// That is, it is essentially the same as a NativeJump. +class NativeFarCall; +inline NativeFarCall* nativeFarCall_overwriting_at(address instr, address destination); +inline NativeFarCall* nativeFarCall_at(address instr); +class NativeFarCall: public NativeInstruction { + public: + enum Sparc_specific_constants { + // instruction_size includes the delay slot instruction. + instruction_size = 9 * BytesPerInstWord, + return_address_offset = 9 * BytesPerInstWord, + jmpl_offset = 7 * BytesPerInstWord, + displacement_offset = 0, + instruction_offset = 0 + }; + address instruction_address() const { return addr_at(0); } + address next_instruction_address() const { return addr_at(instruction_size); } + address return_address() const { return addr_at(return_address_offset); } + + address destination() const { + return (address) data64(addr_at(0), long_at(jmpl_offset)); + } + address displacement_address() const { return addr_at(displacement_offset); } + void set_destination(address dest); + + bool destination_is_compiled_verified_entry_point(); + + void verify(); + void print(); + + // unit test stuff + static void test(); + + // Creation + friend inline NativeFarCall* nativeFarCall_at(address instr) { + NativeFarCall* call = (NativeFarCall*)instr; + #ifdef ASSERT + call->verify(); + #endif + return call; + } + + friend inline NativeFarCall* nativeFarCall_overwriting_at(address instr, address destination = NULL) { + Unimplemented(); + NativeFarCall* call = (NativeFarCall*)instr; + return call; + } + + friend NativeFarCall* nativeFarCall_before(address return_address) { + NativeFarCall* call = (NativeFarCall*)(return_address - return_address_offset); + #ifdef ASSERT + call->verify(); + #endif + return call; + } + + static bool is_call_at(address instr); + + // MT-safe patching of a call instruction. + static void insert(address code_pos, address entry) { + (void)nativeFarCall_overwriting_at(code_pos, entry); + } + static void replace_mt_safe(address instr_addr, address code_buffer); +}; + +#endif // _LP64 + +// An interface for accessing/manipulating native set_oop imm, reg instructions. +// (used to manipulate inlined data references, etc.) +// set_oop imm, reg +// == sethi %hi22(imm), reg ; add reg, %lo10(imm), reg +class NativeMovConstReg; +inline NativeMovConstReg* nativeMovConstReg_at(address address); +class NativeMovConstReg: public NativeInstruction { + public: + enum Sparc_specific_constants { + sethi_offset = 0, +#ifdef _LP64 + add_offset = 7 * BytesPerInstWord, + instruction_size = 8 * BytesPerInstWord +#else + add_offset = 4, + instruction_size = 8 +#endif + }; + + address instruction_address() const { return addr_at(0); } + address next_instruction_address() const { return addr_at(instruction_size); } + + // (The [set_]data accessor respects oop_type relocs also.) + intptr_t data() const; + void set_data(intptr_t x); + + // report the destination register + Register destination() { return inv_rd(long_at(sethi_offset)); } + + void verify(); + void print(); + + // unit test stuff + static void test(); + + // Creation + friend inline NativeMovConstReg* nativeMovConstReg_at(address address) { + NativeMovConstReg* test = (NativeMovConstReg*)address; + #ifdef ASSERT + test->verify(); + #endif + return test; + } + + + friend NativeMovConstReg* nativeMovConstReg_before(address address) { + NativeMovConstReg* test = (NativeMovConstReg*)(address - instruction_size); + #ifdef ASSERT + test->verify(); + #endif + return test; + } + +}; + + +// An interface for accessing/manipulating native set_oop imm, reg instructions. +// (used to manipulate inlined data references, etc.) +// set_oop imm, reg +// == sethi %hi22(imm), reg; nop; add reg, %lo10(imm), reg +// +// Note that it is identical to NativeMovConstReg with the exception of a nop between the +// sethi and the add. The nop is required to be in the delay slot of the call instruction +// which overwrites the sethi during patching. +class NativeMovConstRegPatching; +inline NativeMovConstRegPatching* nativeMovConstRegPatching_at(address address);class NativeMovConstRegPatching: public NativeInstruction { + public: + enum Sparc_specific_constants { + sethi_offset = 0, +#ifdef _LP64 + nop_offset = 7 * BytesPerInstWord, +#else + nop_offset = sethi_offset + BytesPerInstWord, +#endif + add_offset = nop_offset + BytesPerInstWord, + instruction_size = add_offset + BytesPerInstWord + }; + + address instruction_address() const { return addr_at(0); } + address next_instruction_address() const { return addr_at(instruction_size); } + + // (The [set_]data accessor respects oop_type relocs also.) + int data() const; + void set_data(int x); + + // report the destination register + Register destination() { return inv_rd(long_at(sethi_offset)); } + + void verify(); + void print(); + + // unit test stuff + static void test(); + + // Creation + friend inline NativeMovConstRegPatching* nativeMovConstRegPatching_at(address address) { + NativeMovConstRegPatching* test = (NativeMovConstRegPatching*)address; + #ifdef ASSERT + test->verify(); + #endif + return test; + } + + + friend NativeMovConstRegPatching* nativeMovConstRegPatching_before(address address) { + NativeMovConstRegPatching* test = (NativeMovConstRegPatching*)(address - instruction_size); + #ifdef ASSERT + test->verify(); + #endif + return test; + } + +}; + + +// An interface for accessing/manipulating native memory ops +// ld* [reg + offset], reg +// st* reg, [reg + offset] +// sethi %hi(imm), reg; add reg, %lo(imm), reg; ld* [reg1 + reg], reg2 +// sethi %hi(imm), reg; add reg, %lo(imm), reg; st* reg2, [reg1 + reg] +// Ops covered: {lds,ldu,st}{w,b,h}, {ld,st}{d,x} +// +class NativeMovRegMem; +inline NativeMovRegMem* nativeMovRegMem_at (address address); +class NativeMovRegMem: public NativeInstruction { + public: + enum Sparc_specific_constants { + op3_mask_ld = 1 << Assembler::lduw_op3 | + 1 << Assembler::ldub_op3 | + 1 << Assembler::lduh_op3 | + 1 << Assembler::ldd_op3 | + 1 << Assembler::ldsw_op3 | + 1 << Assembler::ldsb_op3 | + 1 << Assembler::ldsh_op3 | + 1 << Assembler::ldx_op3, + op3_mask_st = 1 << Assembler::stw_op3 | + 1 << Assembler::stb_op3 | + 1 << Assembler::sth_op3 | + 1 << Assembler::std_op3 | + 1 << Assembler::stx_op3, + op3_ldst_int_limit = Assembler::ldf_op3, + op3_mask_ldf = 1 << (Assembler::ldf_op3 - op3_ldst_int_limit) | + 1 << (Assembler::lddf_op3 - op3_ldst_int_limit), + op3_mask_stf = 1 << (Assembler::stf_op3 - op3_ldst_int_limit) | + 1 << (Assembler::stdf_op3 - op3_ldst_int_limit), + + offset_width = 13, + sethi_offset = 0, +#ifdef _LP64 + add_offset = 7 * BytesPerInstWord, +#else + add_offset = 4, +#endif + ldst_offset = add_offset + BytesPerInstWord + }; + bool is_immediate() const { + // check if instruction is ld* [reg + offset], reg or st* reg, [reg + offset] + int i0 = long_at(0); + return (is_op(i0, Assembler::ldst_op)); + } + + address instruction_address() const { return addr_at(0); } + address next_instruction_address() const { +#ifdef _LP64 + return addr_at(is_immediate() ? 4 : (7 * BytesPerInstWord)); +#else + return addr_at(is_immediate() ? 4 : 12); +#endif + } + intptr_t offset() const { + return is_immediate()? inv_simm(long_at(0), offset_width) : + nativeMovConstReg_at(addr_at(0))->data(); + } + void set_offset(intptr_t x) { + if (is_immediate()) { + guarantee(fits_in_simm(x, offset_width), "data block offset overflow"); + set_long_at(0, set_simm(long_at(0), x, offset_width)); + } else + nativeMovConstReg_at(addr_at(0))->set_data(x); + } + + void add_offset_in_bytes(intptr_t radd_offset) { + set_offset (offset() + radd_offset); + } + + void copy_instruction_to(address new_instruction_address); + + void verify(); + void print (); + + // unit test stuff + static void test(); + + private: + friend inline NativeMovRegMem* nativeMovRegMem_at (address address) { + NativeMovRegMem* test = (NativeMovRegMem*)address; + #ifdef ASSERT + test->verify(); + #endif + return test; + } +}; + + +// An interface for accessing/manipulating native memory ops +// ld* [reg + offset], reg +// st* reg, [reg + offset] +// sethi %hi(imm), reg; nop; add reg, %lo(imm), reg; ld* [reg1 + reg], reg2 +// sethi %hi(imm), reg; nop; add reg, %lo(imm), reg; st* reg2, [reg1 + reg] +// Ops covered: {lds,ldu,st}{w,b,h}, {ld,st}{d,x} +// +// Note that it is identical to NativeMovRegMem with the exception of a nop between the +// sethi and the add. The nop is required to be in the delay slot of the call instruction +// which overwrites the sethi during patching. +class NativeMovRegMemPatching; +inline NativeMovRegMemPatching* nativeMovRegMemPatching_at (address address); +class NativeMovRegMemPatching: public NativeInstruction { + public: + enum Sparc_specific_constants { + op3_mask_ld = 1 << Assembler::lduw_op3 | + 1 << Assembler::ldub_op3 | + 1 << Assembler::lduh_op3 | + 1 << Assembler::ldd_op3 | + 1 << Assembler::ldsw_op3 | + 1 << Assembler::ldsb_op3 | + 1 << Assembler::ldsh_op3 | + 1 << Assembler::ldx_op3, + op3_mask_st = 1 << Assembler::stw_op3 | + 1 << Assembler::stb_op3 | + 1 << Assembler::sth_op3 | + 1 << Assembler::std_op3 | + 1 << Assembler::stx_op3, + op3_ldst_int_limit = Assembler::ldf_op3, + op3_mask_ldf = 1 << (Assembler::ldf_op3 - op3_ldst_int_limit) | + 1 << (Assembler::lddf_op3 - op3_ldst_int_limit), + op3_mask_stf = 1 << (Assembler::stf_op3 - op3_ldst_int_limit) | + 1 << (Assembler::stdf_op3 - op3_ldst_int_limit), + + offset_width = 13, + sethi_offset = 0, +#ifdef _LP64 + nop_offset = 7 * BytesPerInstWord, +#else + nop_offset = 4, +#endif + add_offset = nop_offset + BytesPerInstWord, + ldst_offset = add_offset + BytesPerInstWord + }; + bool is_immediate() const { + // check if instruction is ld* [reg + offset], reg or st* reg, [reg + offset] + int i0 = long_at(0); + return (is_op(i0, Assembler::ldst_op)); + } + + address instruction_address() const { return addr_at(0); } + address next_instruction_address() const { + return addr_at(is_immediate()? 4 : 16); + } + int offset() const { + return is_immediate()? inv_simm(long_at(0), offset_width) : + nativeMovConstRegPatching_at(addr_at(0))->data(); + } + void set_offset(int x) { + if (is_immediate()) { + guarantee(fits_in_simm(x, offset_width), "data block offset overflow"); + set_long_at(0, set_simm(long_at(0), x, offset_width)); + } + else + nativeMovConstRegPatching_at(addr_at(0))->set_data(x); + } + + void add_offset_in_bytes(intptr_t radd_offset) { + set_offset (offset() + radd_offset); + } + + void copy_instruction_to(address new_instruction_address); + + void verify(); + void print (); + + // unit test stuff + static void test(); + + private: + friend inline NativeMovRegMemPatching* nativeMovRegMemPatching_at (address address) { + NativeMovRegMemPatching* test = (NativeMovRegMemPatching*)address; + #ifdef ASSERT + test->verify(); + #endif + return test; + } +}; + + +// An interface for accessing/manipulating native jumps +// jump_to addr +// == sethi %hi22(addr), temp ; jumpl reg, %lo10(addr), G0 ; +// jumpl_to addr, lreg +// == sethi %hi22(addr), temp ; jumpl reg, %lo10(addr), lreg ; +class NativeJump; +inline NativeJump* nativeJump_at(address address); +class NativeJump: public NativeInstruction { + private: + void guarantee_displacement(int disp, int width) { + guarantee(fits_in_simm(disp, width + 2), "branch displacement overflow"); + } + + public: + enum Sparc_specific_constants { + sethi_offset = 0, +#ifdef _LP64 + jmpl_offset = 7 * BytesPerInstWord, + instruction_size = 9 * BytesPerInstWord // includes delay slot +#else + jmpl_offset = 1 * BytesPerInstWord, + instruction_size = 3 * BytesPerInstWord // includes delay slot +#endif + }; + + address instruction_address() const { return addr_at(0); } + address next_instruction_address() const { return addr_at(instruction_size); } + +#ifdef _LP64 + address jump_destination() const { + return (address) data64(instruction_address(), long_at(jmpl_offset)); + } + void set_jump_destination(address dest) { + set_data64_sethi( instruction_address(), (intptr_t)dest); + set_long_at(jmpl_offset, set_data32_simm13( long_at(jmpl_offset), (intptr_t)dest)); + } +#else + address jump_destination() const { + return (address) data32(long_at(sethi_offset), long_at(jmpl_offset)); + } + void set_jump_destination(address dest) { + set_long_at(sethi_offset, set_data32_sethi( long_at(sethi_offset), (intptr_t)dest)); + set_long_at(jmpl_offset, set_data32_simm13( long_at(jmpl_offset), (intptr_t)dest)); + } +#endif + + // Creation + friend inline NativeJump* nativeJump_at(address address) { + NativeJump* jump = (NativeJump*)address; + #ifdef ASSERT + jump->verify(); + #endif + return jump; + } + + void verify(); + void print(); + + // Unit testing stuff + static void test(); + + // Insertion of native jump instruction + static void insert(address code_pos, address entry); + // MT-safe insertion of native jump at verified method entry + static void check_verified_entry_alignment(address entry, address verified_entry) { + // nothing to do for sparc. + } + static void patch_verified_entry(address entry, address verified_entry, address dest); +}; + + + +// Despite the name, handles only simple branches. +class NativeGeneralJump; +inline NativeGeneralJump* nativeGeneralJump_at(address address); +class NativeGeneralJump: public NativeInstruction { + public: + enum Sparc_specific_constants { + instruction_size = 8 + }; + + address instruction_address() const { return addr_at(0); } + address jump_destination() const { return addr_at(0) + branch_destination_offset(long_at(0)); } + void set_jump_destination(address dest) { + int patched_instr = patch_branch_destination_offset(dest - addr_at(0), long_at(0)); + set_long_at(0, patched_instr); + } + void set_annul() { set_annul_bit(); } + NativeInstruction *delay_slot_instr() { return nativeInstruction_at(addr_at(4));} + void fill_delay_slot(int instr) { set_long_at(4, instr);} + Assembler::Condition condition() { + int x = long_at(0); + return (Assembler::Condition) Assembler::inv_cond(x); + } + + // Creation + friend inline NativeGeneralJump* nativeGeneralJump_at(address address) { + NativeGeneralJump* jump = (NativeGeneralJump*)(address); +#ifdef ASSERT + jump->verify(); +#endif + return jump; + } + + // Insertion of native general jump instruction + static void insert_unconditional(address code_pos, address entry); + static void replace_mt_safe(address instr_addr, address code_buffer); + + void verify(); +}; + + +class NativeIllegalInstruction: public NativeInstruction { + public: + enum Sparc_specific_constants { + instruction_size = 4 + }; + + // Insert illegal opcode as specific address + static void insert(address code_pos); +}; diff --git a/hotspot/src/cpu/sparc/vm/registerMap_sparc.hpp b/hotspot/src/cpu/sparc/vm/registerMap_sparc.hpp new file mode 100644 index 00000000000..193e35bb313 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/registerMap_sparc.hpp @@ -0,0 +1,53 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// machine-dependent implemention for register maps + friend class frame; + + private: + intptr_t* _window; // register window save area (for L and I regs) + intptr_t* _younger_window; // previous save area (for O regs, if needed) + + address pd_location(VMReg reg) const; + void pd_clear(); + void pd_initialize_from(const RegisterMap* map) { + _window = map->_window; + _younger_window = map->_younger_window; + _location_valid[0] = 0; // avoid the shift_individual_registers game + } + void pd_initialize() { + _window = NULL; + _younger_window = NULL; + _location_valid[0] = 0; // avoid the shift_individual_registers game + } + void shift_window(intptr_t* sp, intptr_t* younger_sp) { + _window = sp; + _younger_window = younger_sp; + // Throw away locations for %i, %o, and %l registers: + // But do not throw away %g register locs. + if (_location_valid[0] != 0) shift_individual_registers(); + } + void shift_individual_registers(); + // When popping out of compiled frames, we make all IRegs disappear. + void make_integer_regs_unsaved() { _location_valid[0] = 0; } diff --git a/hotspot/src/cpu/sparc/vm/register_definitions_sparc.cpp b/hotspot/src/cpu/sparc/vm/register_definitions_sparc.cpp new file mode 100644 index 00000000000..0a8ab2e5cd8 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/register_definitions_sparc.cpp @@ -0,0 +1,175 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// make sure the defines don't screw up the declarations later on in this file +#define DONT_USE_REGISTER_DEFINES + +#include "incls/_precompiled.incl" +#include "incls/_register_definitions_sparc.cpp.incl" + +REGISTER_DEFINITION(Register, noreg); + +REGISTER_DEFINITION(Register, G0); +REGISTER_DEFINITION(Register, G1); +REGISTER_DEFINITION(Register, G2); +REGISTER_DEFINITION(Register, G3); +REGISTER_DEFINITION(Register, G4); +REGISTER_DEFINITION(Register, G5); +REGISTER_DEFINITION(Register, G6); +REGISTER_DEFINITION(Register, G7); + +REGISTER_DEFINITION(Register, O0); +REGISTER_DEFINITION(Register, O1); +REGISTER_DEFINITION(Register, O2); +REGISTER_DEFINITION(Register, O3); +REGISTER_DEFINITION(Register, O4); +REGISTER_DEFINITION(Register, O5); +REGISTER_DEFINITION(Register, O6); +REGISTER_DEFINITION(Register, O7); + +REGISTER_DEFINITION(Register, L0); +REGISTER_DEFINITION(Register, L1); +REGISTER_DEFINITION(Register, L2); +REGISTER_DEFINITION(Register, L3); +REGISTER_DEFINITION(Register, L4); +REGISTER_DEFINITION(Register, L5); +REGISTER_DEFINITION(Register, L6); +REGISTER_DEFINITION(Register, L7); + +REGISTER_DEFINITION(Register, I0); +REGISTER_DEFINITION(Register, I1); +REGISTER_DEFINITION(Register, I2); +REGISTER_DEFINITION(Register, I3); +REGISTER_DEFINITION(Register, I4); +REGISTER_DEFINITION(Register, I5); +REGISTER_DEFINITION(Register, I6); +REGISTER_DEFINITION(Register, I7); + +REGISTER_DEFINITION(Register, FP); +REGISTER_DEFINITION(Register, SP); + +REGISTER_DEFINITION(FloatRegister, fnoreg); +REGISTER_DEFINITION(FloatRegister, F0); +REGISTER_DEFINITION(FloatRegister, F1); +REGISTER_DEFINITION(FloatRegister, F2); +REGISTER_DEFINITION(FloatRegister, F3); +REGISTER_DEFINITION(FloatRegister, F4); +REGISTER_DEFINITION(FloatRegister, F5); +REGISTER_DEFINITION(FloatRegister, F6); +REGISTER_DEFINITION(FloatRegister, F7); +REGISTER_DEFINITION(FloatRegister, F8); +REGISTER_DEFINITION(FloatRegister, F9); +REGISTER_DEFINITION(FloatRegister, F10); +REGISTER_DEFINITION(FloatRegister, F11); +REGISTER_DEFINITION(FloatRegister, F12); +REGISTER_DEFINITION(FloatRegister, F13); +REGISTER_DEFINITION(FloatRegister, F14); +REGISTER_DEFINITION(FloatRegister, F15); +REGISTER_DEFINITION(FloatRegister, F16); +REGISTER_DEFINITION(FloatRegister, F17); +REGISTER_DEFINITION(FloatRegister, F18); +REGISTER_DEFINITION(FloatRegister, F19); +REGISTER_DEFINITION(FloatRegister, F20); +REGISTER_DEFINITION(FloatRegister, F21); +REGISTER_DEFINITION(FloatRegister, F22); +REGISTER_DEFINITION(FloatRegister, F23); +REGISTER_DEFINITION(FloatRegister, F24); +REGISTER_DEFINITION(FloatRegister, F25); +REGISTER_DEFINITION(FloatRegister, F26); +REGISTER_DEFINITION(FloatRegister, F27); +REGISTER_DEFINITION(FloatRegister, F28); +REGISTER_DEFINITION(FloatRegister, F29); +REGISTER_DEFINITION(FloatRegister, F30); +REGISTER_DEFINITION(FloatRegister, F31); +REGISTER_DEFINITION(FloatRegister, F32); +REGISTER_DEFINITION(FloatRegister, F34); +REGISTER_DEFINITION(FloatRegister, F36); +REGISTER_DEFINITION(FloatRegister, F38); +REGISTER_DEFINITION(FloatRegister, F40); +REGISTER_DEFINITION(FloatRegister, F42); +REGISTER_DEFINITION(FloatRegister, F44); +REGISTER_DEFINITION(FloatRegister, F46); +REGISTER_DEFINITION(FloatRegister, F48); +REGISTER_DEFINITION(FloatRegister, F50); +REGISTER_DEFINITION(FloatRegister, F52); +REGISTER_DEFINITION(FloatRegister, F54); +REGISTER_DEFINITION(FloatRegister, F56); +REGISTER_DEFINITION(FloatRegister, F58); +REGISTER_DEFINITION(FloatRegister, F60); +REGISTER_DEFINITION(FloatRegister, F62); + + +REGISTER_DEFINITION( Register, Otos_i); +REGISTER_DEFINITION( Register, Otos_l); +REGISTER_DEFINITION( Register, Otos_l1); +REGISTER_DEFINITION( Register, Otos_l2); +REGISTER_DEFINITION(FloatRegister, Ftos_f); +REGISTER_DEFINITION(FloatRegister, Ftos_d); +REGISTER_DEFINITION(FloatRegister, Ftos_d1); +REGISTER_DEFINITION(FloatRegister, Ftos_d2); + + +REGISTER_DEFINITION(Register, G2_thread); +REGISTER_DEFINITION(Register, G5_method); +REGISTER_DEFINITION(Register, G5_megamorphic_method); +REGISTER_DEFINITION(Register, G5_inline_cache_reg); +REGISTER_DEFINITION(Register, Gargs); +REGISTER_DEFINITION(Register, L7_thread_cache); +REGISTER_DEFINITION(Register, Gframe_size); +REGISTER_DEFINITION(Register, G1_scratch); +REGISTER_DEFINITION(Register, G3_scratch); +REGISTER_DEFINITION(Register, G4_scratch); +REGISTER_DEFINITION(Register, Gtemp); +REGISTER_DEFINITION(Register, Lentry_args); + +#ifdef CC_INTERP +REGISTER_DEFINITION(Register, Lstate); +REGISTER_DEFINITION(Register, L1_scratch); +REGISTER_DEFINITION(Register, Lmirror); +REGISTER_DEFINITION(Register, L2_scratch); +REGISTER_DEFINITION(Register, L3_scratch); +REGISTER_DEFINITION(Register, L4_scratch); +REGISTER_DEFINITION(Register, Lscratch); +REGISTER_DEFINITION(Register, Lscratch2); +REGISTER_DEFINITION(Register, L7_scratch); +REGISTER_DEFINITION(Register, I5_savedSP); +#else // CC_INTERP +REGISTER_DEFINITION(Register, Lesp); +REGISTER_DEFINITION(Register, Lbcp); +REGISTER_DEFINITION(Register, Lmonitors); +REGISTER_DEFINITION(Register, Lbyte_code); +REGISTER_DEFINITION(Register, Llast_SP); +REGISTER_DEFINITION(Register, Lscratch); +REGISTER_DEFINITION(Register, Lscratch2); +REGISTER_DEFINITION(Register, LcpoolCache); +REGISTER_DEFINITION(Register, I5_savedSP); +REGISTER_DEFINITION(Register, O5_savedSP); +REGISTER_DEFINITION(Register, IdispatchAddress); +REGISTER_DEFINITION(Register, ImethodDataPtr); +REGISTER_DEFINITION(Register, IdispatchTables); +#endif // CC_INTERP +REGISTER_DEFINITION(Register, Lmethod); +REGISTER_DEFINITION(Register, Llocals); +REGISTER_DEFINITION(Register, Oexception); +REGISTER_DEFINITION(Register, Oissuing_pc); diff --git a/hotspot/src/cpu/sparc/vm/register_sparc.cpp b/hotspot/src/cpu/sparc/vm/register_sparc.cpp new file mode 100644 index 00000000000..125141a0c7c --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/register_sparc.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_register_sparc.cpp.incl" + +const int ConcreteRegisterImpl::max_gpr = RegisterImpl::number_of_registers << 1; +const int ConcreteRegisterImpl::max_fpr = ConcreteRegisterImpl::max_gpr + FloatRegisterImpl::number_of_registers; + +const char* RegisterImpl::name() const { + const char* names[number_of_registers] = { + "G0", "G1", "G2", "G3", "G4", "G5", "G6", "G7", + "O0", "O1", "O2", "O3", "O4", "O5", "SP", "O7", + "L0", "L1", "L2", "L3", "L4", "L5", "L6", "L7", + "I0", "I1", "I2", "I3", "I4", "I5", "FP", "I7" + }; + return is_valid() ? names[encoding()] : "noreg"; +} + + +const char* FloatRegisterImpl::name() const { + const char* names[number_of_registers] = { + "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", + "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "F19", + "F20", "F21", "F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", + "F30", "F31", "F32", "F33?", "F34", "F35?", "F36", "F37?", "F38", "F39?", + "F40", "F41?", "F42", "F43?", "F44", "F45?", "F46", "F47?", "F48", "F49?", + "F50", "F51?", "F52", "F53?", "F54", "F55?", "F56", "F57?", "F58", "F59?", + "F60", "F61?", "F62" + }; + return is_valid() ? names[encoding()] : "fnoreg"; +} diff --git a/hotspot/src/cpu/sparc/vm/register_sparc.hpp b/hotspot/src/cpu/sparc/vm/register_sparc.hpp new file mode 100644 index 00000000000..852ee7e87ec --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/register_sparc.hpp @@ -0,0 +1,442 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// forward declaration +class Address; +class VMRegImpl; +typedef VMRegImpl* VMReg; + + +// Use Register as shortcut +class RegisterImpl; +typedef RegisterImpl* Register; + + +inline Register as_Register(int encoding) { + return (Register)(intptr_t) encoding; +} + +// The implementation of integer registers for the SPARC architecture +class RegisterImpl: public AbstractRegisterImpl { + public: + enum { + log_set_size = 3, // the number of bits to encode the set register number + number_of_sets = 4, // the number of registers sets (in, local, out, global) + number_of_registers = number_of_sets << log_set_size, + + iset_no = 3, ibase = iset_no << log_set_size, // the in register set + lset_no = 2, lbase = lset_no << log_set_size, // the local register set + oset_no = 1, obase = oset_no << log_set_size, // the output register set + gset_no = 0, gbase = gset_no << log_set_size // the global register set + }; + + + friend Register as_Register(int encoding); + // set specific construction + friend Register as_iRegister(int number); + friend Register as_lRegister(int number); + friend Register as_oRegister(int number); + friend Register as_gRegister(int number); + + VMReg as_VMReg(); + + // accessors + int encoding() const { assert(is_valid(), "invalid register"); return value(); } + const char* name() const; + + // testers + bool is_valid() const { return (0 <= (value()&0x7F) && (value()&0x7F) < number_of_registers); } + bool is_even() const { return (encoding() & 1) == 0; } + bool is_in() const { return (encoding() >> log_set_size) == iset_no; } + bool is_local() const { return (encoding() >> log_set_size) == lset_no; } + bool is_out() const { return (encoding() >> log_set_size) == oset_no; } + bool is_global() const { return (encoding() >> log_set_size) == gset_no; } + + // derived registers, offsets, and addresses + Register successor() const { return as_Register(encoding() + 1); } + + int input_number() const { + assert(is_in(), "must be input register"); + return encoding() - ibase; + } + + Register after_save() const { + assert(is_out() || is_global(), "register not visible after save"); + return is_out() ? as_Register(encoding() + (ibase - obase)) : (const Register)this; + } + + Register after_restore() const { + assert(is_in() || is_global(), "register not visible after restore"); + return is_in() ? as_Register(encoding() + (obase - ibase)) : (const Register)this; + } + + int sp_offset_in_saved_window() const { + assert(is_in() || is_local(), "only i and l registers are saved in frame"); + return encoding() - lbase; + } + + inline Address address_in_saved_window() const; // implemented in assembler_sparc.hpp +}; + + +// set specific construction +inline Register as_iRegister(int number) { return as_Register(RegisterImpl::ibase + number); } +inline Register as_lRegister(int number) { return as_Register(RegisterImpl::lbase + number); } +inline Register as_oRegister(int number) { return as_Register(RegisterImpl::obase + number); } +inline Register as_gRegister(int number) { return as_Register(RegisterImpl::gbase + number); } + +// The integer registers of the SPARC architecture + +CONSTANT_REGISTER_DECLARATION(Register, noreg , (-1)); + +CONSTANT_REGISTER_DECLARATION(Register, G0 , (RegisterImpl::gbase + 0)); +CONSTANT_REGISTER_DECLARATION(Register, G1 , (RegisterImpl::gbase + 1)); +CONSTANT_REGISTER_DECLARATION(Register, G2 , (RegisterImpl::gbase + 2)); +CONSTANT_REGISTER_DECLARATION(Register, G3 , (RegisterImpl::gbase + 3)); +CONSTANT_REGISTER_DECLARATION(Register, G4 , (RegisterImpl::gbase + 4)); +CONSTANT_REGISTER_DECLARATION(Register, G5 , (RegisterImpl::gbase + 5)); +CONSTANT_REGISTER_DECLARATION(Register, G6 , (RegisterImpl::gbase + 6)); +CONSTANT_REGISTER_DECLARATION(Register, G7 , (RegisterImpl::gbase + 7)); + +CONSTANT_REGISTER_DECLARATION(Register, O0 , (RegisterImpl::obase + 0)); +CONSTANT_REGISTER_DECLARATION(Register, O1 , (RegisterImpl::obase + 1)); +CONSTANT_REGISTER_DECLARATION(Register, O2 , (RegisterImpl::obase + 2)); +CONSTANT_REGISTER_DECLARATION(Register, O3 , (RegisterImpl::obase + 3)); +CONSTANT_REGISTER_DECLARATION(Register, O4 , (RegisterImpl::obase + 4)); +CONSTANT_REGISTER_DECLARATION(Register, O5 , (RegisterImpl::obase + 5)); +CONSTANT_REGISTER_DECLARATION(Register, O6 , (RegisterImpl::obase + 6)); +CONSTANT_REGISTER_DECLARATION(Register, O7 , (RegisterImpl::obase + 7)); + +CONSTANT_REGISTER_DECLARATION(Register, L0 , (RegisterImpl::lbase + 0)); +CONSTANT_REGISTER_DECLARATION(Register, L1 , (RegisterImpl::lbase + 1)); +CONSTANT_REGISTER_DECLARATION(Register, L2 , (RegisterImpl::lbase + 2)); +CONSTANT_REGISTER_DECLARATION(Register, L3 , (RegisterImpl::lbase + 3)); +CONSTANT_REGISTER_DECLARATION(Register, L4 , (RegisterImpl::lbase + 4)); +CONSTANT_REGISTER_DECLARATION(Register, L5 , (RegisterImpl::lbase + 5)); +CONSTANT_REGISTER_DECLARATION(Register, L6 , (RegisterImpl::lbase + 6)); +CONSTANT_REGISTER_DECLARATION(Register, L7 , (RegisterImpl::lbase + 7)); + +CONSTANT_REGISTER_DECLARATION(Register, I0 , (RegisterImpl::ibase + 0)); +CONSTANT_REGISTER_DECLARATION(Register, I1 , (RegisterImpl::ibase + 1)); +CONSTANT_REGISTER_DECLARATION(Register, I2 , (RegisterImpl::ibase + 2)); +CONSTANT_REGISTER_DECLARATION(Register, I3 , (RegisterImpl::ibase + 3)); +CONSTANT_REGISTER_DECLARATION(Register, I4 , (RegisterImpl::ibase + 4)); +CONSTANT_REGISTER_DECLARATION(Register, I5 , (RegisterImpl::ibase + 5)); +CONSTANT_REGISTER_DECLARATION(Register, I6 , (RegisterImpl::ibase + 6)); +CONSTANT_REGISTER_DECLARATION(Register, I7 , (RegisterImpl::ibase + 7)); + +CONSTANT_REGISTER_DECLARATION(Register, FP , (RegisterImpl::ibase + 6)); +CONSTANT_REGISTER_DECLARATION(Register, SP , (RegisterImpl::obase + 6)); + +// +// Because sparc has so many registers, #define'ing values for the is +// beneficial in code size and the cost of some of the dangers of +// defines. We don't use them on Intel because win32 uses asm +// directives which use the same names for registers as Hotspot does, +// so #defines would screw up the inline assembly. If a particular +// file has a problem with these defines then it's possible to turn +// them off in that file by defining DONT_USE_REGISTER_DEFINES. +// register_definition_sparc.cpp does that so that it's able to +// provide real definitions of these registers for use in debuggers +// and such. +// + +#ifndef DONT_USE_REGISTER_DEFINES +#define noreg ((Register)(noreg_RegisterEnumValue)) + +#define G0 ((Register)(G0_RegisterEnumValue)) +#define G1 ((Register)(G1_RegisterEnumValue)) +#define G2 ((Register)(G2_RegisterEnumValue)) +#define G3 ((Register)(G3_RegisterEnumValue)) +#define G4 ((Register)(G4_RegisterEnumValue)) +#define G5 ((Register)(G5_RegisterEnumValue)) +#define G6 ((Register)(G6_RegisterEnumValue)) +#define G7 ((Register)(G7_RegisterEnumValue)) + +#define O0 ((Register)(O0_RegisterEnumValue)) +#define O1 ((Register)(O1_RegisterEnumValue)) +#define O2 ((Register)(O2_RegisterEnumValue)) +#define O3 ((Register)(O3_RegisterEnumValue)) +#define O4 ((Register)(O4_RegisterEnumValue)) +#define O5 ((Register)(O5_RegisterEnumValue)) +#define O6 ((Register)(O6_RegisterEnumValue)) +#define O7 ((Register)(O7_RegisterEnumValue)) + +#define L0 ((Register)(L0_RegisterEnumValue)) +#define L1 ((Register)(L1_RegisterEnumValue)) +#define L2 ((Register)(L2_RegisterEnumValue)) +#define L3 ((Register)(L3_RegisterEnumValue)) +#define L4 ((Register)(L4_RegisterEnumValue)) +#define L5 ((Register)(L5_RegisterEnumValue)) +#define L6 ((Register)(L6_RegisterEnumValue)) +#define L7 ((Register)(L7_RegisterEnumValue)) + +#define I0 ((Register)(I0_RegisterEnumValue)) +#define I1 ((Register)(I1_RegisterEnumValue)) +#define I2 ((Register)(I2_RegisterEnumValue)) +#define I3 ((Register)(I3_RegisterEnumValue)) +#define I4 ((Register)(I4_RegisterEnumValue)) +#define I5 ((Register)(I5_RegisterEnumValue)) +#define I6 ((Register)(I6_RegisterEnumValue)) +#define I7 ((Register)(I7_RegisterEnumValue)) + +#define FP ((Register)(FP_RegisterEnumValue)) +#define SP ((Register)(SP_RegisterEnumValue)) +#endif // DONT_USE_REGISTER_DEFINES + +// Use FloatRegister as shortcut +class FloatRegisterImpl; +typedef FloatRegisterImpl* FloatRegister; + + +// construction +inline FloatRegister as_FloatRegister(int encoding) { + return (FloatRegister)(intptr_t)encoding; +} + +// The implementation of float registers for the SPARC architecture + +class FloatRegisterImpl: public AbstractRegisterImpl { + public: + enum { + number_of_registers = 64 + }; + + enum Width { + S = 1, D = 2, Q = 3 + }; + + // construction + VMReg as_VMReg( ); + + // accessors + int encoding() const { assert(is_valid(), "invalid register"); return value(); } + + public: + int encoding(Width w) const { + const int c = encoding(); + switch (w) { + case S: + assert(c < 32, "bad single float register"); + return c; + + case D: + assert(c < 64 && (c & 1) == 0, "bad double float register"); + assert(c < 32 || VM_Version::v9_instructions_work(), "V9 float work only on V9 platform"); + return (c & 0x1e) | ((c & 0x20) >> 5); + + case Q: + assert(c < 64 && (c & 3) == 0, "bad quad float register"); + assert(c < 32 || VM_Version::v9_instructions_work(), "V9 float work only on V9 platform"); + return (c & 0x1c) | ((c & 0x20) >> 5); + } + ShouldNotReachHere(); + return -1; + } + + bool is_valid() const { return 0 <= value() && value() < number_of_registers; } + const char* name() const; + + FloatRegister successor() const { return as_FloatRegister(encoding() + 1); } +}; + + +// The float registers of the SPARC architecture + +CONSTANT_REGISTER_DECLARATION(FloatRegister, fnoreg , (-1)); + +CONSTANT_REGISTER_DECLARATION(FloatRegister, F0 , ( 0)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F1 , ( 1)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F2 , ( 2)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F3 , ( 3)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F4 , ( 4)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F5 , ( 5)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F6 , ( 6)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F7 , ( 7)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F8 , ( 8)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F9 , ( 9)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F10 , (10)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F11 , (11)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F12 , (12)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F13 , (13)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F14 , (14)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F15 , (15)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F16 , (16)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F17 , (17)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F18 , (18)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F19 , (19)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F20 , (20)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F21 , (21)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F22 , (22)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F23 , (23)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F24 , (24)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F25 , (25)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F26 , (26)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F27 , (27)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F28 , (28)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F29 , (29)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F30 , (30)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F31 , (31)); + +CONSTANT_REGISTER_DECLARATION(FloatRegister, F32 , (32)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F34 , (34)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F36 , (36)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F38 , (38)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F40 , (40)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F42 , (42)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F44 , (44)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F46 , (46)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F48 , (48)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F50 , (50)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F52 , (52)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F54 , (54)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F56 , (56)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F58 , (58)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F60 , (60)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F62 , (62)); + + +#ifndef DONT_USE_REGISTER_DEFINES +#define fnoreg ((FloatRegister)(fnoreg_FloatRegisterEnumValue)) +#define F0 ((FloatRegister)( F0_FloatRegisterEnumValue)) +#define F1 ((FloatRegister)( F1_FloatRegisterEnumValue)) +#define F2 ((FloatRegister)( F2_FloatRegisterEnumValue)) +#define F3 ((FloatRegister)( F3_FloatRegisterEnumValue)) +#define F4 ((FloatRegister)( F4_FloatRegisterEnumValue)) +#define F5 ((FloatRegister)( F5_FloatRegisterEnumValue)) +#define F6 ((FloatRegister)( F6_FloatRegisterEnumValue)) +#define F7 ((FloatRegister)( F7_FloatRegisterEnumValue)) +#define F8 ((FloatRegister)( F8_FloatRegisterEnumValue)) +#define F9 ((FloatRegister)( F9_FloatRegisterEnumValue)) +#define F10 ((FloatRegister)( F10_FloatRegisterEnumValue)) +#define F11 ((FloatRegister)( F11_FloatRegisterEnumValue)) +#define F12 ((FloatRegister)( F12_FloatRegisterEnumValue)) +#define F13 ((FloatRegister)( F13_FloatRegisterEnumValue)) +#define F14 ((FloatRegister)( F14_FloatRegisterEnumValue)) +#define F15 ((FloatRegister)( F15_FloatRegisterEnumValue)) +#define F16 ((FloatRegister)( F16_FloatRegisterEnumValue)) +#define F17 ((FloatRegister)( F17_FloatRegisterEnumValue)) +#define F18 ((FloatRegister)( F18_FloatRegisterEnumValue)) +#define F19 ((FloatRegister)( F19_FloatRegisterEnumValue)) +#define F20 ((FloatRegister)( F20_FloatRegisterEnumValue)) +#define F21 ((FloatRegister)( F21_FloatRegisterEnumValue)) +#define F22 ((FloatRegister)( F22_FloatRegisterEnumValue)) +#define F23 ((FloatRegister)( F23_FloatRegisterEnumValue)) +#define F24 ((FloatRegister)( F24_FloatRegisterEnumValue)) +#define F25 ((FloatRegister)( F25_FloatRegisterEnumValue)) +#define F26 ((FloatRegister)( F26_FloatRegisterEnumValue)) +#define F27 ((FloatRegister)( F27_FloatRegisterEnumValue)) +#define F28 ((FloatRegister)( F28_FloatRegisterEnumValue)) +#define F29 ((FloatRegister)( F29_FloatRegisterEnumValue)) +#define F30 ((FloatRegister)( F30_FloatRegisterEnumValue)) +#define F31 ((FloatRegister)( F31_FloatRegisterEnumValue)) +#define F32 ((FloatRegister)( F32_FloatRegisterEnumValue)) +#define F34 ((FloatRegister)( F34_FloatRegisterEnumValue)) +#define F36 ((FloatRegister)( F36_FloatRegisterEnumValue)) +#define F38 ((FloatRegister)( F38_FloatRegisterEnumValue)) +#define F40 ((FloatRegister)( F40_FloatRegisterEnumValue)) +#define F42 ((FloatRegister)( F42_FloatRegisterEnumValue)) +#define F44 ((FloatRegister)( F44_FloatRegisterEnumValue)) +#define F46 ((FloatRegister)( F46_FloatRegisterEnumValue)) +#define F48 ((FloatRegister)( F48_FloatRegisterEnumValue)) +#define F50 ((FloatRegister)( F50_FloatRegisterEnumValue)) +#define F52 ((FloatRegister)( F52_FloatRegisterEnumValue)) +#define F54 ((FloatRegister)( F54_FloatRegisterEnumValue)) +#define F56 ((FloatRegister)( F56_FloatRegisterEnumValue)) +#define F58 ((FloatRegister)( F58_FloatRegisterEnumValue)) +#define F60 ((FloatRegister)( F60_FloatRegisterEnumValue)) +#define F62 ((FloatRegister)( F62_FloatRegisterEnumValue)) +#endif // DONT_USE_REGISTER_DEFINES + +// Maximum number of incoming arguments that can be passed in i registers. +const int SPARC_ARGS_IN_REGS_NUM = 6; + +class ConcreteRegisterImpl : public AbstractRegisterImpl { + public: + enum { + // This number must be large enough to cover REG_COUNT (defined by c2) registers. + // There is no requirement that any ordering here matches any ordering c2 gives + // it's optoregs. + number_of_registers = 2*RegisterImpl::number_of_registers + + FloatRegisterImpl::number_of_registers + + 1 + // ccr + 4 // fcc + }; + static const int max_gpr; + static const int max_fpr; + +}; + +// Single, Double and Quad fp reg classes. These exist to map the ADLC +// encoding for a floating point register, to the FloatRegister number +// desired by the macroassembler. A FloatRegister is a number between +// 0 and 63 passed around as a pointer. For ADLC, an fp register encoding +// is the actual bit encoding used by the sparc hardware. When ADLC used +// the macroassembler to generate an instruction that references, e.g., a +// double fp reg, it passed the bit encoding to the macroassembler via +// as_FloatRegister, which, for double regs > 30, returns an illegal +// register number. +// +// Therefore we provide the following classes for use by ADLC. Their +// sole purpose is to convert from sparc register encodings to FloatRegisters. +// At some future time, we might replace FloatRegister with these classes, +// hence the definitions of as_xxxFloatRegister as class methods rather +// than as external inline routines. + +class SingleFloatRegisterImpl; +typedef SingleFloatRegisterImpl *SingleFloatRegister; + +inline FloatRegister as_SingleFloatRegister(int encoding); +class SingleFloatRegisterImpl { + public: + friend inline FloatRegister as_SingleFloatRegister(int encoding) { + assert(encoding < 32, "bad single float register encoding"); + return as_FloatRegister(encoding); + } +}; + + +class DoubleFloatRegisterImpl; +typedef DoubleFloatRegisterImpl *DoubleFloatRegister; + +inline FloatRegister as_DoubleFloatRegister(int encoding); +class DoubleFloatRegisterImpl { + public: + friend inline FloatRegister as_DoubleFloatRegister(int encoding) { + assert(encoding < 32, "bad double float register encoding"); + return as_FloatRegister( ((encoding & 1) << 5) | (encoding & 0x1e) ); + } +}; + + +class QuadFloatRegisterImpl; +typedef QuadFloatRegisterImpl *QuadFloatRegister; + +class QuadFloatRegisterImpl { + public: + friend FloatRegister as_QuadFloatRegister(int encoding) { + assert(encoding < 32 && ((encoding & 2) == 0), "bad quad float register encoding"); + return as_FloatRegister( ((encoding & 1) << 5) | (encoding & 0x1c) ); + } +}; diff --git a/hotspot/src/cpu/sparc/vm/relocInfo_sparc.cpp b/hotspot/src/cpu/sparc/vm/relocInfo_sparc.cpp new file mode 100644 index 00000000000..af1450c7440 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/relocInfo_sparc.cpp @@ -0,0 +1,195 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_relocInfo_sparc.cpp.incl" + +void Relocation::pd_set_data_value(address x, intptr_t o) { + NativeInstruction* ip = nativeInstruction_at(addr()); + jint inst = ip->long_at(0); + assert(inst != NativeInstruction::illegal_instruction(), "no breakpoint"); + switch (Assembler::inv_op(inst)) { + + case Assembler::ldst_op: + #ifdef ASSERT + switch (Assembler::inv_op3(inst)) { + case Assembler::lduw_op3: + case Assembler::ldub_op3: + case Assembler::lduh_op3: + case Assembler::ldd_op3: + case Assembler::ldsw_op3: + case Assembler::ldsb_op3: + case Assembler::ldsh_op3: + case Assembler::ldx_op3: + case Assembler::ldf_op3: + case Assembler::lddf_op3: + case Assembler::stw_op3: + case Assembler::stb_op3: + case Assembler::sth_op3: + case Assembler::std_op3: + case Assembler::stx_op3: + case Assembler::stf_op3: + case Assembler::stdf_op3: + case Assembler::casa_op3: + case Assembler::casxa_op3: + break; + default: + ShouldNotReachHere(); + } + goto do_non_sethi; + #endif + + case Assembler::arith_op: + #ifdef ASSERT + switch (Assembler::inv_op3(inst)) { + case Assembler::or_op3: + case Assembler::add_op3: + case Assembler::jmpl_op3: + break; + default: + ShouldNotReachHere(); + } + do_non_sethi:; + #endif + { + guarantee(Assembler::inv_immed(inst), "must have a simm13 field"); + int simm13 = Assembler::low10((intptr_t)x) + o; + guarantee(Assembler::is_simm13(simm13), "offset can't overflow simm13"); + inst &= ~Assembler::simm( -1, 13); + inst |= Assembler::simm(simm13, 13); + ip->set_long_at(0, inst); + } + break; + + case Assembler::branch_op: + { +#ifdef _LP64 + jint inst2; + guarantee(Assembler::inv_op2(inst)==Assembler::sethi_op2, "must be sethi"); + ip->set_data64_sethi( ip->addr_at(0), (intptr_t)x ); +#ifdef COMPILER2 + // [RGV] Someone must have missed putting in a reloc entry for the + // add in compiler2. + inst2 = ip->long_at( NativeMovConstReg::add_offset ); + guarantee(Assembler::inv_op(inst2)==Assembler::arith_op, "arith op"); + ip->set_long_at(NativeMovConstReg::add_offset,ip->set_data32_simm13( inst2, (intptr_t)x+o)); +#endif +#else + guarantee(Assembler::inv_op2(inst)==Assembler::sethi_op2, "must be sethi"); + inst &= ~Assembler::hi22( -1); + inst |= Assembler::hi22((intptr_t)x); + // (ignore offset; it doesn't play into the sethi) + ip->set_long_at(0, inst); +#endif + } + break; + + default: + guarantee(false, "instruction must perform arithmetic or memory access"); + } +} + + +address Relocation::pd_call_destination(address orig_addr) { + intptr_t adj = 0; + if (orig_addr != NULL) { + // We just moved this call instruction from orig_addr to addr(). + // This means its target will appear to have grown by addr() - orig_addr. + adj = -( addr() - orig_addr ); + } + if (NativeCall::is_call_at(addr())) { + NativeCall* call = nativeCall_at(addr()); + return call->destination() + adj; + } + if (NativeFarCall::is_call_at(addr())) { + NativeFarCall* call = nativeFarCall_at(addr()); + return call->destination() + adj; + } + // Special case: Patchable branch local to the code cache. + // This will break badly if the code cache grows larger than a few Mb. + NativeGeneralJump* br = nativeGeneralJump_at(addr()); + return br->jump_destination() + adj; +} + + +void Relocation::pd_set_call_destination(address x) { + if (NativeCall::is_call_at(addr())) { + NativeCall* call = nativeCall_at(addr()); + call->set_destination(x); + return; + } + if (NativeFarCall::is_call_at(addr())) { + NativeFarCall* call = nativeFarCall_at(addr()); + call->set_destination(x); + return; + } + // Special case: Patchable branch local to the code cache. + // This will break badly if the code cache grows larger than a few Mb. + NativeGeneralJump* br = nativeGeneralJump_at(addr()); + br->set_jump_destination(x); +} + + +address* Relocation::pd_address_in_code() { + // SPARC never embeds addresses in code, at present. + //assert(type() == relocInfo::oop_type, "only oops are inlined at present"); + return (address*)addr(); +} + + +address Relocation::pd_get_address_from_code() { + // SPARC never embeds addresses in code, at present. + //assert(type() == relocInfo::oop_type, "only oops are inlined at present"); + return *(address*)addr(); +} + + +int Relocation::pd_breakpoint_size() { + // minimum breakpoint size, in short words + return NativeIllegalInstruction::instruction_size / sizeof(short); +} + +void Relocation::pd_swap_in_breakpoint(address x, short* instrs, int instrlen) { + Untested("pd_swap_in_breakpoint"); + // %%% probably do not need a general instrlen; just use the trap size + if (instrs != NULL) { + assert(instrlen * sizeof(short) == NativeIllegalInstruction::instruction_size, "enough instrlen in reloc. data"); + for (int i = 0; i < instrlen; i++) { + instrs[i] = ((short*)x)[i]; + } + } + NativeIllegalInstruction::insert(x); +} + + +void Relocation::pd_swap_out_breakpoint(address x, short* instrs, int instrlen) { + Untested("pd_swap_out_breakpoint"); + assert(instrlen * sizeof(short) == sizeof(int), "enough buf"); + union { int l; short s[1]; } u; + for (int i = 0; i < instrlen; i++) { + u.s[i] = instrs[i]; + } + NativeInstruction* ni = nativeInstruction_at(x); + ni->set_long_at(0, u.l); +} diff --git a/hotspot/src/cpu/sparc/vm/relocInfo_sparc.hpp b/hotspot/src/cpu/sparc/vm/relocInfo_sparc.hpp new file mode 100644 index 00000000000..ed38e6e50cb --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/relocInfo_sparc.hpp @@ -0,0 +1,46 @@ +/* + * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // machine-dependent parts of class relocInfo + private: + enum { + // Since SPARC instructions are whole words, + // the two low-order offset bits can always be discarded. + offset_unit = 4, + + // There is no need for format bits; the instructions are + // sufficiently self-identifying. + format_width = 0 + }; + + +//Reconciliation History +// 1.3 97/10/15 15:38:36 relocInfo_i486.hpp +// 1.4 97/12/08 16:01:06 relocInfo_i486.hpp +// 1.5 98/01/23 01:34:55 relocInfo_i486.hpp +// 1.6 98/02/27 15:44:53 relocInfo_i486.hpp +// 1.6 98/03/12 14:47:13 relocInfo_i486.hpp +// 1.8 99/06/22 16:37:50 relocInfo_i486.hpp +// 1.9 99/07/16 11:12:11 relocInfo_i486.hpp +//End diff --git a/hotspot/src/cpu/sparc/vm/runtime_sparc.cpp b/hotspot/src/cpu/sparc/vm/runtime_sparc.cpp new file mode 100644 index 00000000000..181990edb8c --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/runtime_sparc.cpp @@ -0,0 +1,142 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_runtime_sparc.cpp.incl" + + +#define __ masm-> + +ExceptionBlob *OptoRuntime::_exception_blob; + +//------------------------------ generate_exception_blob --------------------------- +// creates exception blob at the end +// Using exception blob, this code is jumped from a compiled method. +// (see emit_exception_handler in sparc.ad file) +// +// Given an exception pc at a call we call into the runtime for the +// handler in this method. This handler might merely restore state +// (i.e. callee save registers) unwind the frame and jump to the +// exception handler for the nmethod if there is no Java level handler +// for the nmethod. +// +// This code is entered with a jmp. +// +// Arguments: +// O0: exception oop +// O1: exception pc +// +// Results: +// O0: exception oop +// O1: exception pc in caller or ??? +// destination: exception handler of caller +// +// Note: the exception pc MUST be at a call (precise debug information) +// +void OptoRuntime::generate_exception_blob() { + // allocate space for code + ResourceMark rm; + int pad = VerifyThread ? 256 : 0;// Extra slop space for more verify code + + // setup code generation tools + // Measured 8/7/03 at 256 in 32bit debug build (no VerifyThread) + // Measured 8/7/03 at 528 in 32bit debug build (VerifyThread) + CodeBuffer buffer("exception_blob", 600+pad, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + + int framesize_in_bytes = __ total_frame_size_in_bytes(0); + int framesize_in_words = framesize_in_bytes / wordSize; + int framesize_in_slots = framesize_in_bytes / sizeof(jint); + + Label L; + + int start = __ offset(); + + __ verify_thread(); + __ st_ptr(Oexception, Address(G2_thread, 0, in_bytes(JavaThread::exception_oop_offset()))); + __ st_ptr(Oissuing_pc, Address(G2_thread, 0, in_bytes(JavaThread::exception_pc_offset()))); + + // This call does all the hard work. It checks if an exception catch + // exists in the method. + // If so, it returns the handler address. + // If the nmethod has been deoptimized and it had a handler the handler + // address is the deopt blob unpack_with_exception entry. + // + // If no handler exists it prepares for stack-unwinding, restoring the callee-save + // registers of the frame being removed. + // + __ save_frame(0); + + __ mov(G2_thread, O0); + __ set_last_Java_frame(SP, noreg); + __ save_thread(L7_thread_cache); + + // This call can block at exit and nmethod can be deoptimized at that + // point. If the nmethod had a catch point we would jump to the + // now deoptimized catch point and fall thru the vanilla deopt + // path and lose the exception + // Sure would be simpler if this call didn't block! + __ call(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C), relocInfo::runtime_call_type); + __ delayed()->mov(L7_thread_cache, O0); + + // Set an oopmap for the call site. This oopmap will only be used if we + // are unwinding the stack. Hence, all locations will be dead. + // Callee-saved registers will be the same as the frame above (i.e., + // handle_exception_stub), since they were restored when we got the + // exception. + + OopMapSet *oop_maps = new OopMapSet(); + oop_maps->add_gc_map( __ offset()-start, new OopMap(framesize_in_slots, 0)); + + __ bind(L); + __ restore_thread(L7_thread_cache); + __ reset_last_Java_frame(); + + __ mov(O0, G3_scratch); // Move handler address to temp + __ restore(); + + // G3_scratch contains handler address + // Since this may be the deopt blob we must set O7 to look like we returned + // from the original pc that threw the exception + + __ ld_ptr(Address(G2_thread, 0, in_bytes(JavaThread::exception_pc_offset())), O7); + __ sub(O7, frame::pc_return_offset, O7); + + + assert(Assembler::is_simm13(in_bytes(JavaThread::exception_oop_offset())), "exception offset overflows simm13, following ld instruction cannot be in delay slot"); + __ ld_ptr(Address(G2_thread, 0, in_bytes(JavaThread::exception_oop_offset())), Oexception); // O0 +#ifdef ASSERT + __ st_ptr(G0, Address(G2_thread, 0, in_bytes(JavaThread::exception_handler_pc_offset()))); + __ st_ptr(G0, Address(G2_thread, 0, in_bytes(JavaThread::exception_pc_offset()))); +#endif + __ JMP(G3_scratch, 0); + // Clear the exception oop so GC no longer processes it as a root. + __ delayed()->st_ptr(G0, Address(G2_thread, 0, in_bytes(JavaThread::exception_oop_offset()))); + + // ------------- + // make sure all code is generated + masm->flush(); + + _exception_blob = ExceptionBlob::create(&buffer, oop_maps, framesize_in_words); +} diff --git a/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp b/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp new file mode 100644 index 00000000000..f58d87820e2 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp @@ -0,0 +1,3199 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_sharedRuntime_sparc.cpp.incl" + +#define __ masm-> + +#ifdef COMPILER2 +UncommonTrapBlob* SharedRuntime::_uncommon_trap_blob; +#endif // COMPILER2 + +DeoptimizationBlob* SharedRuntime::_deopt_blob; +SafepointBlob* SharedRuntime::_polling_page_safepoint_handler_blob; +SafepointBlob* SharedRuntime::_polling_page_return_handler_blob; +RuntimeStub* SharedRuntime::_wrong_method_blob; +RuntimeStub* SharedRuntime::_ic_miss_blob; +RuntimeStub* SharedRuntime::_resolve_opt_virtual_call_blob; +RuntimeStub* SharedRuntime::_resolve_virtual_call_blob; +RuntimeStub* SharedRuntime::_resolve_static_call_blob; + +class RegisterSaver { + + // Used for saving volatile registers. This is Gregs, Fregs, I/L/O. + // The Oregs are problematic. In the 32bit build the compiler can + // have O registers live with 64 bit quantities. A window save will + // cut the heads off of the registers. We have to do a very extensive + // stack dance to save and restore these properly. + + // Note that the Oregs problem only exists if we block at either a polling + // page exception a compiled code safepoint that was not originally a call + // or deoptimize following one of these kinds of safepoints. + + // Lots of registers to save. For all builds, a window save will preserve + // the %i and %l registers. For the 32-bit longs-in-two entries and 64-bit + // builds a window-save will preserve the %o registers. In the LION build + // we need to save the 64-bit %o registers which requires we save them + // before the window-save (as then they become %i registers and get their + // heads chopped off on interrupt). We have to save some %g registers here + // as well. + enum { + // This frame's save area. Includes extra space for the native call: + // vararg's layout space and the like. Briefly holds the caller's + // register save area. + call_args_area = frame::register_save_words_sp_offset + + frame::memory_parameter_word_sp_offset*wordSize, + // Make sure save locations are always 8 byte aligned. + // can't use round_to because it doesn't produce compile time constant + start_of_extra_save_area = ((call_args_area + 7) & ~7), + g1_offset = start_of_extra_save_area, // g-regs needing saving + g3_offset = g1_offset+8, + g4_offset = g3_offset+8, + g5_offset = g4_offset+8, + o0_offset = g5_offset+8, + o1_offset = o0_offset+8, + o2_offset = o1_offset+8, + o3_offset = o2_offset+8, + o4_offset = o3_offset+8, + o5_offset = o4_offset+8, + start_of_flags_save_area = o5_offset+8, + ccr_offset = start_of_flags_save_area, + fsr_offset = ccr_offset + 8, + d00_offset = fsr_offset+8, // Start of float save area + register_save_size = d00_offset+8*32 + }; + + + public: + + static int Oexception_offset() { return o0_offset; }; + static int G3_offset() { return g3_offset; }; + static int G5_offset() { return g5_offset; }; + static OopMap* save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words); + static void restore_live_registers(MacroAssembler* masm); + + // During deoptimization only the result register need to be restored + // all the other values have already been extracted. + + static void restore_result_registers(MacroAssembler* masm); +}; + +OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words) { + // Record volatile registers as callee-save values in an OopMap so their save locations will be + // propagated to the caller frame's RegisterMap during StackFrameStream construction (needed for + // deoptimization; see compiledVFrame::create_stack_value). The caller's I, L and O registers + // are saved in register windows - I's and L's in the caller's frame and O's in the stub frame + // (as the stub's I's) when the runtime routine called by the stub creates its frame. + int i; + // Always make the frame size 16 bytr aligned. + int frame_size = round_to(additional_frame_words + register_save_size, 16); + // OopMap frame size is in c2 stack slots (sizeof(jint)) not bytes or words + int frame_size_in_slots = frame_size / sizeof(jint); + // CodeBlob frame size is in words. + *total_frame_words = frame_size / wordSize; + // OopMap* map = new OopMap(*total_frame_words, 0); + OopMap* map = new OopMap(frame_size_in_slots, 0); + +#if !defined(_LP64) + + // Save 64-bit O registers; they will get their heads chopped off on a 'save'. + __ stx(O0, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+0*8); + __ stx(O1, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+1*8); + __ stx(O2, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+2*8); + __ stx(O3, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+3*8); + __ stx(O4, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+4*8); + __ stx(O5, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+5*8); +#endif /* _LP64 */ + + __ save(SP, -frame_size, SP); + +#ifndef _LP64 + // Reload the 64 bit Oregs. Although they are now Iregs we load them + // to Oregs here to avoid interrupts cutting off their heads + + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+0*8, O0); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+1*8, O1); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+2*8, O2); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+3*8, O3); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+4*8, O4); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+5*8, O5); + + __ stx(O0, SP, o0_offset+STACK_BIAS); + map->set_callee_saved(VMRegImpl::stack2reg((o0_offset + 4)>>2), O0->as_VMReg()); + + __ stx(O1, SP, o1_offset+STACK_BIAS); + + map->set_callee_saved(VMRegImpl::stack2reg((o1_offset + 4)>>2), O1->as_VMReg()); + + __ stx(O2, SP, o2_offset+STACK_BIAS); + map->set_callee_saved(VMRegImpl::stack2reg((o2_offset + 4)>>2), O2->as_VMReg()); + + __ stx(O3, SP, o3_offset+STACK_BIAS); + map->set_callee_saved(VMRegImpl::stack2reg((o3_offset + 4)>>2), O3->as_VMReg()); + + __ stx(O4, SP, o4_offset+STACK_BIAS); + map->set_callee_saved(VMRegImpl::stack2reg((o4_offset + 4)>>2), O4->as_VMReg()); + + __ stx(O5, SP, o5_offset+STACK_BIAS); + map->set_callee_saved(VMRegImpl::stack2reg((o5_offset + 4)>>2), O5->as_VMReg()); +#endif /* _LP64 */ + + // Save the G's + __ stx(G1, SP, g1_offset+STACK_BIAS); + map->set_callee_saved(VMRegImpl::stack2reg((g1_offset + 4)>>2), G1->as_VMReg()); + + __ stx(G3, SP, g3_offset+STACK_BIAS); + map->set_callee_saved(VMRegImpl::stack2reg((g3_offset + 4)>>2), G3->as_VMReg()); + + __ stx(G4, SP, g4_offset+STACK_BIAS); + map->set_callee_saved(VMRegImpl::stack2reg((g4_offset + 4)>>2), G4->as_VMReg()); + + __ stx(G5, SP, g5_offset+STACK_BIAS); + map->set_callee_saved(VMRegImpl::stack2reg((g5_offset + 4)>>2), G5->as_VMReg()); + + // This is really a waste but we'll keep things as they were for now + if (true) { +#ifndef _LP64 + map->set_callee_saved(VMRegImpl::stack2reg((o0_offset)>>2), O0->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg((o1_offset)>>2), O1->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg((o2_offset)>>2), O2->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg((o3_offset)>>2), O3->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg((o4_offset)>>2), O4->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg((o5_offset)>>2), O5->as_VMReg()->next()); +#endif /* _LP64 */ + map->set_callee_saved(VMRegImpl::stack2reg((g1_offset)>>2), G1->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg((g3_offset)>>2), G3->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg((g4_offset)>>2), G4->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg((g5_offset)>>2), G5->as_VMReg()->next()); + } + + + // Save the flags + __ rdccr( G5 ); + __ stx(G5, SP, ccr_offset+STACK_BIAS); + __ stxfsr(SP, fsr_offset+STACK_BIAS); + + // Save all the FP registers + int offset = d00_offset; + for( int i=0; i<64; i+=2 ) { + FloatRegister f = as_FloatRegister(i); + __ stf(FloatRegisterImpl::D, f, SP, offset+STACK_BIAS); + map->set_callee_saved(VMRegImpl::stack2reg(offset>>2), f->as_VMReg()); + if (true) { + map->set_callee_saved(VMRegImpl::stack2reg((offset + sizeof(float))>>2), f->as_VMReg()->next()); + } + offset += sizeof(double); + } + + // And we're done. + + return map; +} + + +// Pop the current frame and restore all the registers that we +// saved. +void RegisterSaver::restore_live_registers(MacroAssembler* masm) { + + // Restore all the FP registers + for( int i=0; i<64; i+=2 ) { + __ ldf(FloatRegisterImpl::D, SP, d00_offset+i*sizeof(float)+STACK_BIAS, as_FloatRegister(i)); + } + + __ ldx(SP, ccr_offset+STACK_BIAS, G1); + __ wrccr (G1) ; + + // Restore the G's + // Note that G2 (AKA GThread) must be saved and restored separately. + // TODO-FIXME: save and restore some of the other ASRs, viz., %asi and %gsr. + + __ ldx(SP, g1_offset+STACK_BIAS, G1); + __ ldx(SP, g3_offset+STACK_BIAS, G3); + __ ldx(SP, g4_offset+STACK_BIAS, G4); + __ ldx(SP, g5_offset+STACK_BIAS, G5); + + +#if !defined(_LP64) + // Restore the 64-bit O's. + __ ldx(SP, o0_offset+STACK_BIAS, O0); + __ ldx(SP, o1_offset+STACK_BIAS, O1); + __ ldx(SP, o2_offset+STACK_BIAS, O2); + __ ldx(SP, o3_offset+STACK_BIAS, O3); + __ ldx(SP, o4_offset+STACK_BIAS, O4); + __ ldx(SP, o5_offset+STACK_BIAS, O5); + + // And temporarily place them in TLS + + __ stx(O0, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+0*8); + __ stx(O1, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+1*8); + __ stx(O2, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+2*8); + __ stx(O3, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+3*8); + __ stx(O4, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+4*8); + __ stx(O5, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+5*8); +#endif /* _LP64 */ + + // Restore flags + + __ ldxfsr(SP, fsr_offset+STACK_BIAS); + + __ restore(); + +#if !defined(_LP64) + // Now reload the 64bit Oregs after we've restore the window. + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+0*8, O0); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+1*8, O1); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+2*8, O2); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+3*8, O3); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+4*8, O4); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+5*8, O5); +#endif /* _LP64 */ + +} + +// Pop the current frame and restore the registers that might be holding +// a result. +void RegisterSaver::restore_result_registers(MacroAssembler* masm) { + +#if !defined(_LP64) + // 32bit build returns longs in G1 + __ ldx(SP, g1_offset+STACK_BIAS, G1); + + // Retrieve the 64-bit O's. + __ ldx(SP, o0_offset+STACK_BIAS, O0); + __ ldx(SP, o1_offset+STACK_BIAS, O1); + // and save to TLS + __ stx(O0, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+0*8); + __ stx(O1, G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+1*8); +#endif /* _LP64 */ + + __ ldf(FloatRegisterImpl::D, SP, d00_offset+STACK_BIAS, as_FloatRegister(0)); + + __ restore(); + +#if !defined(_LP64) + // Now reload the 64bit Oregs after we've restore the window. + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+0*8, O0); + __ ldx(G2_thread, JavaThread::o_reg_temps_offset_in_bytes()+1*8, O1); +#endif /* _LP64 */ + +} + +// The java_calling_convention describes stack locations as ideal slots on +// a frame with no abi restrictions. Since we must observe abi restrictions +// (like the placement of the register window) the slots must be biased by +// the following value. +static int reg2offset(VMReg r) { + return (r->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size; +} + +// --------------------------------------------------------------------------- +// Read the array of BasicTypes from a signature, and compute where the +// arguments should go. Values in the VMRegPair regs array refer to 4-byte (VMRegImpl::stack_slot_size) +// quantities. Values less than VMRegImpl::stack0 are registers, those above +// refer to 4-byte stack slots. All stack slots are based off of the window +// top. VMRegImpl::stack0 refers to the first slot past the 16-word window, +// and VMRegImpl::stack0+1 refers to the memory word 4-byes higher. Register +// values 0-63 (up to RegisterImpl::number_of_registers) are the 64-bit +// integer registers. Values 64-95 are the (32-bit only) float registers. +// Each 32-bit quantity is given its own number, so the integer registers +// (in either 32- or 64-bit builds) use 2 numbers. For example, there is +// an O0-low and an O0-high. Essentially, all int register numbers are doubled. + +// Register results are passed in O0-O5, for outgoing call arguments. To +// convert to incoming arguments, convert all O's to I's. The regs array +// refer to the low and hi 32-bit words of 64-bit registers or stack slots. +// If the regs[].second() field is set to VMRegImpl::Bad(), it means it's unused (a +// 32-bit value was passed). If both are VMRegImpl::Bad(), it means no value was +// passed (used as a placeholder for the other half of longs and doubles in +// the 64-bit build). regs[].second() is either VMRegImpl::Bad() or regs[].second() is +// regs[].first()+1 (regs[].first() may be misaligned in the C calling convention). +// Sparc never passes a value in regs[].second() but not regs[].first() (regs[].first() +// == VMRegImpl::Bad() && regs[].second() != VMRegImpl::Bad()) nor unrelated values in the +// same VMRegPair. + +// Note: the INPUTS in sig_bt are in units of Java argument words, which are +// either 32-bit or 64-bit depending on the build. The OUTPUTS are in 32-bit +// units regardless of build. + + +// --------------------------------------------------------------------------- +// The compiled Java calling convention. The Java convention always passes +// 64-bit values in adjacent aligned locations (either registers or stack), +// floats in float registers and doubles in aligned float pairs. Values are +// packed in the registers. There is no backing varargs store for values in +// registers. In the 32-bit build, longs are passed in G1 and G4 (cannot be +// passed in I's, because longs in I's get their heads chopped off at +// interrupt). +int SharedRuntime::java_calling_convention(const BasicType *sig_bt, + VMRegPair *regs, + int total_args_passed, + int is_outgoing) { + assert(F31->as_VMReg()->is_reg(), "overlapping stack/register numbers"); + + // Convention is to pack the first 6 int/oop args into the first 6 registers + // (I0-I5), extras spill to the stack. Then pack the first 8 float args + // into F0-F7, extras spill to the stack. Then pad all register sets to + // align. Then put longs and doubles into the same registers as they fit, + // else spill to the stack. + const int int_reg_max = SPARC_ARGS_IN_REGS_NUM; + const int flt_reg_max = 8; + // + // Where 32-bit 1-reg longs start being passed + // In tiered we must pass on stack because c1 can't use a "pair" in a single reg. + // So make it look like we've filled all the G regs that c2 wants to use. + Register g_reg = TieredCompilation ? noreg : G1; + + // Count int/oop and float args. See how many stack slots we'll need and + // where the longs & doubles will go. + int int_reg_cnt = 0; + int flt_reg_cnt = 0; + // int stk_reg_pairs = frame::register_save_words*(wordSize>>2); + // int stk_reg_pairs = SharedRuntime::out_preserve_stack_slots(); + int stk_reg_pairs = 0; + for (int i = 0; i < total_args_passed; i++) { + switch (sig_bt[i]) { + case T_LONG: // LP64, longs compete with int args + assert(sig_bt[i+1] == T_VOID, ""); +#ifdef _LP64 + if (int_reg_cnt < int_reg_max) int_reg_cnt++; +#endif + break; + case T_OBJECT: + case T_ARRAY: + case T_ADDRESS: // Used, e.g., in slow-path locking for the lock's stack address + if (int_reg_cnt < int_reg_max) int_reg_cnt++; +#ifndef _LP64 + else stk_reg_pairs++; +#endif + break; + case T_INT: + case T_SHORT: + case T_CHAR: + case T_BYTE: + case T_BOOLEAN: + if (int_reg_cnt < int_reg_max) int_reg_cnt++; + else stk_reg_pairs++; + break; + case T_FLOAT: + if (flt_reg_cnt < flt_reg_max) flt_reg_cnt++; + else stk_reg_pairs++; + break; + case T_DOUBLE: + assert(sig_bt[i+1] == T_VOID, ""); + break; + case T_VOID: + break; + default: + ShouldNotReachHere(); + } + } + + // This is where the longs/doubles start on the stack. + stk_reg_pairs = (stk_reg_pairs+1) & ~1; // Round + + int int_reg_pairs = (int_reg_cnt+1) & ~1; // 32-bit 2-reg longs only + int flt_reg_pairs = (flt_reg_cnt+1) & ~1; + + // int stk_reg = frame::register_save_words*(wordSize>>2); + // int stk_reg = SharedRuntime::out_preserve_stack_slots(); + int stk_reg = 0; + int int_reg = 0; + int flt_reg = 0; + + // Now do the signature layout + for (int i = 0; i < total_args_passed; i++) { + switch (sig_bt[i]) { + case T_INT: + case T_SHORT: + case T_CHAR: + case T_BYTE: + case T_BOOLEAN: +#ifndef _LP64 + case T_OBJECT: + case T_ARRAY: + case T_ADDRESS: // Used, e.g., in slow-path locking for the lock's stack address +#endif // _LP64 + if (int_reg < int_reg_max) { + Register r = is_outgoing ? as_oRegister(int_reg++) : as_iRegister(int_reg++); + regs[i].set1(r->as_VMReg()); + } else { + regs[i].set1(VMRegImpl::stack2reg(stk_reg++)); + } + break; + +#ifdef _LP64 + case T_OBJECT: + case T_ARRAY: + case T_ADDRESS: // Used, e.g., in slow-path locking for the lock's stack address + if (int_reg < int_reg_max) { + Register r = is_outgoing ? as_oRegister(int_reg++) : as_iRegister(int_reg++); + regs[i].set2(r->as_VMReg()); + } else { + regs[i].set2(VMRegImpl::stack2reg(stk_reg_pairs)); + stk_reg_pairs += 2; + } + break; +#endif // _LP64 + + case T_LONG: + assert(sig_bt[i+1] == T_VOID, "expecting VOID in other half"); +#ifdef COMPILER2 +#ifdef _LP64 + // Can't be tiered (yet) + if (int_reg < int_reg_max) { + Register r = is_outgoing ? as_oRegister(int_reg++) : as_iRegister(int_reg++); + regs[i].set2(r->as_VMReg()); + } else { + regs[i].set2(VMRegImpl::stack2reg(stk_reg_pairs)); + stk_reg_pairs += 2; + } +#else + // For 32-bit build, can't pass longs in O-regs because they become + // I-regs and get trashed. Use G-regs instead. G1 and G4 are almost + // spare and available. This convention isn't used by the Sparc ABI or + // anywhere else. If we're tiered then we don't use G-regs because c1 + // can't deal with them as a "pair". + // G0: zero + // G1: 1st Long arg + // G2: global allocated to TLS + // G3: used in inline cache check + // G4: 2nd Long arg + // G5: used in inline cache check + // G6: used by OS + // G7: used by OS + + if (g_reg == G1) { + regs[i].set2(G1->as_VMReg()); // This long arg in G1 + g_reg = G4; // Where the next arg goes + } else if (g_reg == G4) { + regs[i].set2(G4->as_VMReg()); // The 2nd long arg in G4 + g_reg = noreg; // No more longs in registers + } else { + regs[i].set2(VMRegImpl::stack2reg(stk_reg_pairs)); + stk_reg_pairs += 2; + } +#endif // _LP64 +#else // COMPILER2 + if (int_reg_pairs + 1 < int_reg_max) { + if (is_outgoing) { + regs[i].set_pair(as_oRegister(int_reg_pairs + 1)->as_VMReg(), as_oRegister(int_reg_pairs)->as_VMReg()); + } else { + regs[i].set_pair(as_iRegister(int_reg_pairs + 1)->as_VMReg(), as_iRegister(int_reg_pairs)->as_VMReg()); + } + int_reg_pairs += 2; + } else { + regs[i].set2(VMRegImpl::stack2reg(stk_reg_pairs)); + stk_reg_pairs += 2; + } +#endif // COMPILER2 + break; + + case T_FLOAT: + if (flt_reg < flt_reg_max) regs[i].set1(as_FloatRegister(flt_reg++)->as_VMReg()); + else regs[i].set1( VMRegImpl::stack2reg(stk_reg++)); + break; + case T_DOUBLE: + assert(sig_bt[i+1] == T_VOID, "expecting half"); + if (flt_reg_pairs + 1 < flt_reg_max) { + regs[i].set2(as_FloatRegister(flt_reg_pairs)->as_VMReg()); + flt_reg_pairs += 2; + } else { + regs[i].set2(VMRegImpl::stack2reg(stk_reg_pairs)); + stk_reg_pairs += 2; + } + break; + case T_VOID: regs[i].set_bad(); break; // Halves of longs & doubles + default: + ShouldNotReachHere(); + } + } + + // retun the amount of stack space these arguments will need. + return stk_reg_pairs; + +} + +// Helper class mostly to avoid passing masm everywhere, and handle store +// displacement overflow logic for LP64 +class AdapterGenerator { + MacroAssembler *masm; +#ifdef _LP64 + Register Rdisp; + void set_Rdisp(Register r) { Rdisp = r; } +#endif // _LP64 + + void patch_callers_callsite(); + void tag_c2i_arg(frame::Tag t, Register base, int st_off, Register scratch); + + // base+st_off points to top of argument + int arg_offset(const int st_off) { return st_off + Interpreter::value_offset_in_bytes(); } + int next_arg_offset(const int st_off) { + return st_off - Interpreter::stackElementSize() + Interpreter::value_offset_in_bytes(); + } + +#ifdef _LP64 + // On _LP64 argument slot values are loaded first into a register + // because they might not fit into displacement. + Register arg_slot(const int st_off); + Register next_arg_slot(const int st_off); +#else + int arg_slot(const int st_off) { return arg_offset(st_off); } + int next_arg_slot(const int st_off) { return next_arg_offset(st_off); } +#endif // _LP64 + + // Stores long into offset pointed to by base + void store_c2i_long(Register r, Register base, + const int st_off, bool is_stack); + void store_c2i_object(Register r, Register base, + const int st_off); + void store_c2i_int(Register r, Register base, + const int st_off); + void store_c2i_double(VMReg r_2, + VMReg r_1, Register base, const int st_off); + void store_c2i_float(FloatRegister f, Register base, + const int st_off); + + public: + void gen_c2i_adapter(int total_args_passed, + // VMReg max_arg, + int comp_args_on_stack, // VMRegStackSlots + const BasicType *sig_bt, + const VMRegPair *regs, + Label& skip_fixup); + void gen_i2c_adapter(int total_args_passed, + // VMReg max_arg, + int comp_args_on_stack, // VMRegStackSlots + const BasicType *sig_bt, + const VMRegPair *regs); + + AdapterGenerator(MacroAssembler *_masm) : masm(_masm) {} +}; + + +// Patch the callers callsite with entry to compiled code if it exists. +void AdapterGenerator::patch_callers_callsite() { + Label L; + __ ld_ptr(G5_method, in_bytes(methodOopDesc::code_offset()), G3_scratch); + __ br_null(G3_scratch, false, __ pt, L); + // Schedule the branch target address early. + __ delayed()->ld_ptr(G5_method, in_bytes(methodOopDesc::interpreter_entry_offset()), G3_scratch); + // Call into the VM to patch the caller, then jump to compiled callee + __ save_frame(4); // Args in compiled layout; do not blow them + + // Must save all the live Gregs the list is: + // G1: 1st Long arg (32bit build) + // G2: global allocated to TLS + // G3: used in inline cache check (scratch) + // G4: 2nd Long arg (32bit build); + // G5: used in inline cache check (methodOop) + + // The longs must go to the stack by hand since in the 32 bit build they can be trashed by window ops. + +#ifdef _LP64 + // mov(s,d) + __ mov(G1, L1); + __ mov(G4, L4); + __ mov(G5_method, L5); + __ mov(G5_method, O0); // VM needs target method + __ mov(I7, O1); // VM needs caller's callsite + // Must be a leaf call... + // can be very far once the blob has been relocated + Address dest(O7, CAST_FROM_FN_PTR(address, SharedRuntime::fixup_callers_callsite)); + __ relocate(relocInfo::runtime_call_type); + __ jumpl_to(dest, O7); + __ delayed()->mov(G2_thread, L7_thread_cache); + __ mov(L7_thread_cache, G2_thread); + __ mov(L1, G1); + __ mov(L4, G4); + __ mov(L5, G5_method); +#else + __ stx(G1, FP, -8 + STACK_BIAS); + __ stx(G4, FP, -16 + STACK_BIAS); + __ mov(G5_method, L5); + __ mov(G5_method, O0); // VM needs target method + __ mov(I7, O1); // VM needs caller's callsite + // Must be a leaf call... + __ call(CAST_FROM_FN_PTR(address, SharedRuntime::fixup_callers_callsite), relocInfo::runtime_call_type); + __ delayed()->mov(G2_thread, L7_thread_cache); + __ mov(L7_thread_cache, G2_thread); + __ ldx(FP, -8 + STACK_BIAS, G1); + __ ldx(FP, -16 + STACK_BIAS, G4); + __ mov(L5, G5_method); + __ ld_ptr(G5_method, in_bytes(methodOopDesc::interpreter_entry_offset()), G3_scratch); +#endif /* _LP64 */ + + __ restore(); // Restore args + __ bind(L); +} + +void AdapterGenerator::tag_c2i_arg(frame::Tag t, Register base, int st_off, + Register scratch) { + if (TaggedStackInterpreter) { + int tag_off = st_off + Interpreter::tag_offset_in_bytes(); +#ifdef _LP64 + Register tag_slot = Rdisp; + __ set(tag_off, tag_slot); +#else + int tag_slot = tag_off; +#endif // _LP64 + // have to store zero because local slots can be reused (rats!) + if (t == frame::TagValue) { + __ st_ptr(G0, base, tag_slot); + } else if (t == frame::TagCategory2) { + __ st_ptr(G0, base, tag_slot); + int next_tag_off = st_off - Interpreter::stackElementSize() + + Interpreter::tag_offset_in_bytes(); +#ifdef _LP64 + __ set(next_tag_off, tag_slot); +#else + tag_slot = next_tag_off; +#endif // _LP64 + __ st_ptr(G0, base, tag_slot); + } else { + __ mov(t, scratch); + __ st_ptr(scratch, base, tag_slot); + } + } +} + +#ifdef _LP64 +Register AdapterGenerator::arg_slot(const int st_off) { + __ set( arg_offset(st_off), Rdisp); + return Rdisp; +} + +Register AdapterGenerator::next_arg_slot(const int st_off){ + __ set( next_arg_offset(st_off), Rdisp); + return Rdisp; +} +#endif // _LP64 + +// Stores long into offset pointed to by base +void AdapterGenerator::store_c2i_long(Register r, Register base, + const int st_off, bool is_stack) { +#ifdef COMPILER2 +#ifdef _LP64 + // In V9, longs are given 2 64-bit slots in the interpreter, but the + // data is passed in only 1 slot. + __ stx(r, base, next_arg_slot(st_off)); +#else + // Misaligned store of 64-bit data + __ stw(r, base, arg_slot(st_off)); // lo bits + __ srlx(r, 32, r); + __ stw(r, base, next_arg_slot(st_off)); // hi bits +#endif // _LP64 +#else + if (is_stack) { + // Misaligned store of 64-bit data + __ stw(r, base, arg_slot(st_off)); // lo bits + __ srlx(r, 32, r); + __ stw(r, base, next_arg_slot(st_off)); // hi bits + } else { + __ stw(r->successor(), base, arg_slot(st_off) ); // lo bits + __ stw(r , base, next_arg_slot(st_off)); // hi bits + } +#endif // COMPILER2 + tag_c2i_arg(frame::TagCategory2, base, st_off, r); +} + +void AdapterGenerator::store_c2i_object(Register r, Register base, + const int st_off) { + __ st_ptr (r, base, arg_slot(st_off)); + tag_c2i_arg(frame::TagReference, base, st_off, r); +} + +void AdapterGenerator::store_c2i_int(Register r, Register base, + const int st_off) { + __ st (r, base, arg_slot(st_off)); + tag_c2i_arg(frame::TagValue, base, st_off, r); +} + +// Stores into offset pointed to by base +void AdapterGenerator::store_c2i_double(VMReg r_2, + VMReg r_1, Register base, const int st_off) { +#ifdef _LP64 + // In V9, doubles are given 2 64-bit slots in the interpreter, but the + // data is passed in only 1 slot. + __ stf(FloatRegisterImpl::D, r_1->as_FloatRegister(), base, next_arg_slot(st_off)); +#else + // Need to marshal 64-bit value from misaligned Lesp loads + __ stf(FloatRegisterImpl::S, r_1->as_FloatRegister(), base, next_arg_slot(st_off)); + __ stf(FloatRegisterImpl::S, r_2->as_FloatRegister(), base, arg_slot(st_off) ); +#endif + tag_c2i_arg(frame::TagCategory2, base, st_off, G1_scratch); +} + +void AdapterGenerator::store_c2i_float(FloatRegister f, Register base, + const int st_off) { + __ stf(FloatRegisterImpl::S, f, base, arg_slot(st_off)); + tag_c2i_arg(frame::TagValue, base, st_off, G1_scratch); +} + +void AdapterGenerator::gen_c2i_adapter( + int total_args_passed, + // VMReg max_arg, + int comp_args_on_stack, // VMRegStackSlots + const BasicType *sig_bt, + const VMRegPair *regs, + Label& skip_fixup) { + + // Before we get into the guts of the C2I adapter, see if we should be here + // at all. We've come from compiled code and are attempting to jump to the + // interpreter, which means the caller made a static call to get here + // (vcalls always get a compiled target if there is one). Check for a + // compiled target. If there is one, we need to patch the caller's call. + // However we will run interpreted if we come thru here. The next pass + // thru the call site will run compiled. If we ran compiled here then + // we can (theorectically) do endless i2c->c2i->i2c transitions during + // deopt/uncommon trap cycles. If we always go interpreted here then + // we can have at most one and don't need to play any tricks to keep + // from endlessly growing the stack. + // + // Actually if we detected that we had an i2c->c2i transition here we + // ought to be able to reset the world back to the state of the interpreted + // call and not bother building another interpreter arg area. We don't + // do that at this point. + + patch_callers_callsite(); + + __ bind(skip_fixup); + + // Since all args are passed on the stack, total_args_passed*wordSize is the + // space we need. Add in varargs area needed by the interpreter. Round up + // to stack alignment. + const int arg_size = total_args_passed * Interpreter::stackElementSize(); + const int varargs_area = + (frame::varargs_offset - frame::register_save_words)*wordSize; + const int extraspace = round_to(arg_size + varargs_area, 2*wordSize); + + int bias = STACK_BIAS; + const int interp_arg_offset = frame::varargs_offset*wordSize + + (total_args_passed-1)*Interpreter::stackElementSize(); + + Register base = SP; + +#ifdef _LP64 + // In the 64bit build because of wider slots and STACKBIAS we can run + // out of bits in the displacement to do loads and stores. Use g3 as + // temporary displacement. + if (! __ is_simm13(extraspace)) { + __ set(extraspace, G3_scratch); + __ sub(SP, G3_scratch, SP); + } else { + __ sub(SP, extraspace, SP); + } + set_Rdisp(G3_scratch); +#else + __ sub(SP, extraspace, SP); +#endif // _LP64 + + // First write G1 (if used) to where ever it must go + for (int i=0; ias_VMReg()) { + if (sig_bt[i] == T_OBJECT || sig_bt[i] == T_ARRAY) { + store_c2i_object(G1_scratch, base, st_off); + } else if (sig_bt[i] == T_LONG) { + assert(!TieredCompilation, "should not use register args for longs"); + store_c2i_long(G1_scratch, base, st_off, false); + } else { + store_c2i_int(G1_scratch, base, st_off); + } + } + } + + // Now write the args into the outgoing interpreter space + for (int i=0; iis_valid()) { + assert(!r_2->is_valid(), ""); + continue; + } + // Skip G1 if found as we did it first in order to free it up + if (r_1 == G1_scratch->as_VMReg()) { + continue; + } +#ifdef ASSERT + bool G1_forced = false; +#endif // ASSERT + if (r_1->is_stack()) { // Pretend stack targets are loaded into G1 +#ifdef _LP64 + Register ld_off = Rdisp; + __ set(reg2offset(r_1) + extraspace + bias, ld_off); +#else + int ld_off = reg2offset(r_1) + extraspace + bias; +#ifdef ASSERT + G1_forced = true; +#endif // ASSERT +#endif // _LP64 + r_1 = G1_scratch->as_VMReg();// as part of the load/store shuffle + if (!r_2->is_valid()) __ ld (base, ld_off, G1_scratch); + else __ ldx(base, ld_off, G1_scratch); + } + + if (r_1->is_Register()) { + Register r = r_1->as_Register()->after_restore(); + if (sig_bt[i] == T_OBJECT || sig_bt[i] == T_ARRAY) { + store_c2i_object(r, base, st_off); + } else if (sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) { + if (TieredCompilation) { + assert(G1_forced || sig_bt[i] != T_LONG, "should not use register args for longs"); + } + store_c2i_long(r, base, st_off, r_2->is_stack()); + } else { + store_c2i_int(r, base, st_off); + } + } else { + assert(r_1->is_FloatRegister(), ""); + if (sig_bt[i] == T_FLOAT) { + store_c2i_float(r_1->as_FloatRegister(), base, st_off); + } else { + assert(sig_bt[i] == T_DOUBLE, "wrong type"); + store_c2i_double(r_2, r_1, base, st_off); + } + } + } + +#ifdef _LP64 + // Need to reload G3_scratch, used for temporary displacements. + __ ld_ptr(G5_method, in_bytes(methodOopDesc::interpreter_entry_offset()), G3_scratch); + + // Pass O5_savedSP as an argument to the interpreter. + // The interpreter will restore SP to this value before returning. + __ set(extraspace, G1); + __ add(SP, G1, O5_savedSP); +#else + // Pass O5_savedSP as an argument to the interpreter. + // The interpreter will restore SP to this value before returning. + __ add(SP, extraspace, O5_savedSP); +#endif // _LP64 + + __ mov((frame::varargs_offset)*wordSize - + 1*Interpreter::stackElementSize()+bias+BytesPerWord, G1); + // Jump to the interpreter just as if interpreter was doing it. + __ jmpl(G3_scratch, 0, G0); + // Setup Lesp for the call. Cannot actually set Lesp as the current Lesp + // (really L0) is in use by the compiled frame as a generic temp. However, + // the interpreter does not know where its args are without some kind of + // arg pointer being passed in. Pass it in Gargs. + __ delayed()->add(SP, G1, Gargs); +} + +void AdapterGenerator::gen_i2c_adapter( + int total_args_passed, + // VMReg max_arg, + int comp_args_on_stack, // VMRegStackSlots + const BasicType *sig_bt, + const VMRegPair *regs) { + + // Generate an I2C adapter: adjust the I-frame to make space for the C-frame + // layout. Lesp was saved by the calling I-frame and will be restored on + // return. Meanwhile, outgoing arg space is all owned by the callee + // C-frame, so we can mangle it at will. After adjusting the frame size, + // hoist register arguments and repack other args according to the compiled + // code convention. Finally, end in a jump to the compiled code. The entry + // point address is the start of the buffer. + + // We will only enter here from an interpreted frame and never from after + // passing thru a c2i. Azul allowed this but we do not. If we lose the + // race and use a c2i we will remain interpreted for the race loser(s). + // This removes all sorts of headaches on the x86 side and also eliminates + // the possibility of having c2i -> i2c -> c2i -> ... endless transitions. + + // As you can see from the list of inputs & outputs there are not a lot + // of temp registers to work with: mostly G1, G3 & G4. + + // Inputs: + // G2_thread - TLS + // G5_method - Method oop + // O0 - Flag telling us to restore SP from O5 + // O4_args - Pointer to interpreter's args + // O5 - Caller's saved SP, to be restored if needed + // O6 - Current SP! + // O7 - Valid return address + // L0-L7, I0-I7 - Caller's temps (no frame pushed yet) + + // Outputs: + // G2_thread - TLS + // G1, G4 - Outgoing long args in 32-bit build + // O0-O5 - Outgoing args in compiled layout + // O6 - Adjusted or restored SP + // O7 - Valid return address + // L0-L7, I0-I7 - Caller's temps (no frame pushed yet) + // F0-F7 - more outgoing args + + + // O4 is about to get loaded up with compiled callee's args + __ sub(Gargs, BytesPerWord, Gargs); + +#ifdef ASSERT + { + // on entry OsavedSP and SP should be equal + Label ok; + __ cmp(O5_savedSP, SP); + __ br(Assembler::equal, false, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("I5_savedSP not set"); + __ should_not_reach_here(); + __ bind(ok); + } +#endif + + // ON ENTRY TO THE CODE WE ARE MAKING, WE HAVE AN INTERPRETED FRAME + // WITH O7 HOLDING A VALID RETURN PC + // + // | | + // : java stack : + // | | + // +--------------+ <--- start of outgoing args + // | receiver | | + // : rest of args : |---size is java-arg-words + // | | | + // +--------------+ <--- O4_args (misaligned) and Lesp if prior is not C2I + // | | | + // : unused : |---Space for max Java stack, plus stack alignment + // | | | + // +--------------+ <--- SP + 16*wordsize + // | | + // : window : + // | | + // +--------------+ <--- SP + + // WE REPACK THE STACK. We use the common calling convention layout as + // discovered by calling SharedRuntime::calling_convention. We assume it + // causes an arbitrary shuffle of memory, which may require some register + // temps to do the shuffle. We hope for (and optimize for) the case where + // temps are not needed. We may have to resize the stack slightly, in case + // we need alignment padding (32-bit interpreter can pass longs & doubles + // misaligned, but the compilers expect them aligned). + // + // | | + // : java stack : + // | | + // +--------------+ <--- start of outgoing args + // | pad, align | | + // +--------------+ | + // | ints, floats | |---Outgoing stack args, packed low. + // +--------------+ | First few args in registers. + // : doubles : | + // | longs | | + // +--------------+ <--- SP' + 16*wordsize + // | | + // : window : + // | | + // +--------------+ <--- SP' + + // ON EXIT FROM THE CODE WE ARE MAKING, WE STILL HAVE AN INTERPRETED FRAME + // WITH O7 HOLDING A VALID RETURN PC - ITS JUST THAT THE ARGS ARE NOW SETUP + // FOR COMPILED CODE AND THE FRAME SLIGHTLY GROWN. + + // Cut-out for having no stack args. Since up to 6 args are passed + // in registers, we will commonly have no stack args. + if (comp_args_on_stack > 0) { + + // Convert VMReg stack slots to words. + int comp_words_on_stack = round_to(comp_args_on_stack*VMRegImpl::stack_slot_size, wordSize)>>LogBytesPerWord; + // Round up to miminum stack alignment, in wordSize + comp_words_on_stack = round_to(comp_words_on_stack, 2); + // Now compute the distance from Lesp to SP. This calculation does not + // include the space for total_args_passed because Lesp has not yet popped + // the arguments. + __ sub(SP, (comp_words_on_stack)*wordSize, SP); + } + + // Will jump to the compiled code just as if compiled code was doing it. + // Pre-load the register-jump target early, to schedule it better. + __ ld_ptr(G5_method, in_bytes(methodOopDesc::from_compiled_offset()), G3); + + // Now generate the shuffle code. Pick up all register args and move the + // rest through G1_scratch. + for (int i=0; i 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half"); + continue; + } + + // Pick up 0, 1 or 2 words from Lesp+offset. Assume mis-aligned in the + // 32-bit build and aligned in the 64-bit build. Look for the obvious + // ldx/lddf optimizations. + + // Load in argument order going down. + const int ld_off = (total_args_passed-i)*Interpreter::stackElementSize(); +#ifdef _LP64 + set_Rdisp(G1_scratch); +#endif // _LP64 + + VMReg r_1 = regs[i].first(); + VMReg r_2 = regs[i].second(); + if (!r_1->is_valid()) { + assert(!r_2->is_valid(), ""); + continue; + } + if (r_1->is_stack()) { // Pretend stack targets are loaded into F8/F9 + r_1 = F8->as_VMReg(); // as part of the load/store shuffle + if (r_2->is_valid()) r_2 = r_1->next(); + } + if (r_1->is_Register()) { // Register argument + Register r = r_1->as_Register()->after_restore(); + if (!r_2->is_valid()) { + __ ld(Gargs, arg_slot(ld_off), r); + } else { +#ifdef _LP64 + // In V9, longs are given 2 64-bit slots in the interpreter, but the + // data is passed in only 1 slot. + Register slot = (sig_bt[i]==T_LONG) ? + next_arg_slot(ld_off) : arg_slot(ld_off); + __ ldx(Gargs, slot, r); +#else + // Need to load a 64-bit value into G1/G4, but G1/G4 is being used in the + // stack shuffle. Load the first 2 longs into G1/G4 later. +#endif + } + } else { + assert(r_1->is_FloatRegister(), ""); + if (!r_2->is_valid()) { + __ ldf(FloatRegisterImpl::S, Gargs, arg_slot(ld_off), r_1->as_FloatRegister()); + } else { +#ifdef _LP64 + // In V9, doubles are given 2 64-bit slots in the interpreter, but the + // data is passed in only 1 slot. This code also handles longs that + // are passed on the stack, but need a stack-to-stack move through a + // spare float register. + Register slot = (sig_bt[i]==T_LONG || sig_bt[i] == T_DOUBLE) ? + next_arg_slot(ld_off) : arg_slot(ld_off); + __ ldf(FloatRegisterImpl::D, Gargs, slot, r_1->as_FloatRegister()); +#else + // Need to marshal 64-bit value from misaligned Lesp loads + __ ldf(FloatRegisterImpl::S, Gargs, next_arg_slot(ld_off), r_1->as_FloatRegister()); + __ ldf(FloatRegisterImpl::S, Gargs, arg_slot(ld_off), r_2->as_FloatRegister()); +#endif + } + } + // Was the argument really intended to be on the stack, but was loaded + // into F8/F9? + if (regs[i].first()->is_stack()) { + assert(r_1->as_FloatRegister() == F8, "fix this code"); + // Convert stack slot to an SP offset + int st_off = reg2offset(regs[i].first()) + STACK_BIAS; + // Store down the shuffled stack word. Target address _is_ aligned. + if (!r_2->is_valid()) __ stf(FloatRegisterImpl::S, r_1->as_FloatRegister(), SP, st_off); + else __ stf(FloatRegisterImpl::D, r_1->as_FloatRegister(), SP, st_off); + } + } + bool made_space = false; +#ifndef _LP64 + // May need to pick up a few long args in G1/G4 + bool g4_crushed = false; + bool g3_crushed = false; + for (int i=0; iis_Register() && regs[i].second()->is_valid()) { + // Load in argument order going down + int ld_off = (total_args_passed-i)*Interpreter::stackElementSize(); + // Need to marshal 64-bit value from misaligned Lesp loads + Register r = regs[i].first()->as_Register()->after_restore(); + if (r == G1 || r == G4) { + assert(!g4_crushed, "ordering problem"); + if (r == G4){ + g4_crushed = true; + __ lduw(Gargs, arg_slot(ld_off) , G3_scratch); // Load lo bits + __ ld (Gargs, next_arg_slot(ld_off), r); // Load hi bits + } else { + // better schedule this way + __ ld (Gargs, next_arg_slot(ld_off), r); // Load hi bits + __ lduw(Gargs, arg_slot(ld_off) , G3_scratch); // Load lo bits + } + g3_crushed = true; + __ sllx(r, 32, r); + __ or3(G3_scratch, r, r); + } else { + assert(r->is_out(), "longs passed in two O registers"); + __ ld (Gargs, arg_slot(ld_off) , r->successor()); // Load lo bits + __ ld (Gargs, next_arg_slot(ld_off), r); // Load hi bits + } + } + } +#endif + + // Jump to the compiled code just as if compiled code was doing it. + // +#ifndef _LP64 + if (g3_crushed) { + // Rats load was wasted, at least it is in cache... + __ ld_ptr(G5_method, in_bytes(methodOopDesc::from_compiled_offset()), G3); + } +#endif /* _LP64 */ + + // 6243940 We might end up in handle_wrong_method if + // the callee is deoptimized as we race thru here. If that + // happens we don't want to take a safepoint because the + // caller frame will look interpreted and arguments are now + // "compiled" so it is much better to make this transition + // invisible to the stack walking code. Unfortunately if + // we try and find the callee by normal means a safepoint + // is possible. So we stash the desired callee in the thread + // and the vm will find there should this case occur. + Address callee_target_addr(G2_thread, 0, in_bytes(JavaThread::callee_target_offset())); + __ st_ptr(G5_method, callee_target_addr); + + if (StressNonEntrant) { + // Open a big window for deopt failure + __ save_frame(0); + __ mov(G0, L0); + Label loop; + __ bind(loop); + __ sub(L0, 1, L0); + __ br_null(L0, false, Assembler::pt, loop); + __ delayed()->nop(); + + __ restore(); + } + + + __ jmpl(G3, 0, G0); + __ delayed()->nop(); +} + +// --------------------------------------------------------------- +AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + // VMReg max_arg, + int comp_args_on_stack, // VMRegStackSlots + const BasicType *sig_bt, + const VMRegPair *regs) { + address i2c_entry = __ pc(); + + AdapterGenerator agen(masm); + + agen.gen_i2c_adapter(total_args_passed, comp_args_on_stack, sig_bt, regs); + + + // ------------------------------------------------------------------------- + // Generate a C2I adapter. On entry we know G5 holds the methodOop. The + // args start out packed in the compiled layout. They need to be unpacked + // into the interpreter layout. This will almost always require some stack + // space. We grow the current (compiled) stack, then repack the args. We + // finally end in a jump to the generic interpreter entry point. On exit + // from the interpreter, the interpreter will restore our SP (lest the + // compiled code, which relys solely on SP and not FP, get sick). + + address c2i_unverified_entry = __ pc(); + Label skip_fixup; + { +#if !defined(_LP64) && defined(COMPILER2) + Register R_temp = L0; // another scratch register +#else + Register R_temp = G1; // another scratch register +#endif + + Address ic_miss(G3_scratch, SharedRuntime::get_ic_miss_stub()); + + __ verify_oop(O0); + __ verify_oop(G5_method); + __ ld_ptr(O0, oopDesc::klass_offset_in_bytes(), G3_scratch); + __ verify_oop(G3_scratch); + +#if !defined(_LP64) && defined(COMPILER2) + __ save(SP, -frame::register_save_words*wordSize, SP); + __ ld_ptr(G5_method, compiledICHolderOopDesc::holder_klass_offset(), R_temp); + __ verify_oop(R_temp); + __ cmp(G3_scratch, R_temp); + __ restore(); +#else + __ ld_ptr(G5_method, compiledICHolderOopDesc::holder_klass_offset(), R_temp); + __ verify_oop(R_temp); + __ cmp(G3_scratch, R_temp); +#endif + + Label ok, ok2; + __ brx(Assembler::equal, false, Assembler::pt, ok); + __ delayed()->ld_ptr(G5_method, compiledICHolderOopDesc::holder_method_offset(), G5_method); + __ jump_to(ic_miss); + __ delayed()->nop(); + + __ bind(ok); + // Method might have been compiled since the call site was patched to + // interpreted if that is the case treat it as a miss so we can get + // the call site corrected. + __ ld_ptr(G5_method, in_bytes(methodOopDesc::code_offset()), G3_scratch); + __ bind(ok2); + __ br_null(G3_scratch, false, __ pt, skip_fixup); + __ delayed()->ld_ptr(G5_method, in_bytes(methodOopDesc::interpreter_entry_offset()), G3_scratch); + __ jump_to(ic_miss); + __ delayed()->nop(); + + } + + address c2i_entry = __ pc(); + + agen.gen_c2i_adapter(total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); + + __ flush(); + return new AdapterHandlerEntry(i2c_entry, c2i_entry, c2i_unverified_entry); + +} + +// Helper function for native calling conventions +static VMReg int_stk_helper( int i ) { + // Bias any stack based VMReg we get by ignoring the window area + // but not the register parameter save area. + // + // This is strange for the following reasons. We'd normally expect + // the calling convention to return an VMReg for a stack slot + // completely ignoring any abi reserved area. C2 thinks of that + // abi area as only out_preserve_stack_slots. This does not include + // the area allocated by the C abi to store down integer arguments + // because the java calling convention does not use it. So + // since c2 assumes that there are only out_preserve_stack_slots + // to bias the optoregs (which impacts VMRegs) when actually referencing any actual stack + // location the c calling convention must add in this bias amount + // to make up for the fact that the out_preserve_stack_slots is + // insufficient for C calls. What a mess. I sure hope those 6 + // stack words were worth it on every java call! + + // Another way of cleaning this up would be for out_preserve_stack_slots + // to take a parameter to say whether it was C or java calling conventions. + // Then things might look a little better (but not much). + + int mem_parm_offset = i - SPARC_ARGS_IN_REGS_NUM; + if( mem_parm_offset < 0 ) { + return as_oRegister(i)->as_VMReg(); + } else { + int actual_offset = (mem_parm_offset + frame::memory_parameter_word_sp_offset) * VMRegImpl::slots_per_word; + // Now return a biased offset that will be correct when out_preserve_slots is added back in + return VMRegImpl::stack2reg(actual_offset - SharedRuntime::out_preserve_stack_slots()); + } +} + + +int SharedRuntime::c_calling_convention(const BasicType *sig_bt, + VMRegPair *regs, + int total_args_passed) { + + // Return the number of VMReg stack_slots needed for the args. + // This value does not include an abi space (like register window + // save area). + + // The native convention is V8 if !LP64 + // The LP64 convention is the V9 convention which is slightly more sane. + + // We return the amount of VMReg stack slots we need to reserve for all + // the arguments NOT counting out_preserve_stack_slots. Since we always + // have space for storing at least 6 registers to memory we start with that. + // See int_stk_helper for a further discussion. + int max_stack_slots = (frame::varargs_offset * VMRegImpl::slots_per_word) - SharedRuntime::out_preserve_stack_slots(); + +#ifdef _LP64 + // V9 convention: All things "as-if" on double-wide stack slots. + // Hoist any int/ptr/long's in the first 6 to int regs. + // Hoist any flt/dbl's in the first 16 dbl regs. + int j = 0; // Count of actual args, not HALVES + for( int i=0; ias_VMReg()); + } else { + // V9ism: floats go in ODD stack slot + regs[i].set1(VMRegImpl::stack2reg(1 + (j<<1))); + } + break; + case T_DOUBLE: + assert( sig_bt[i+1] == T_VOID, "expecting half" ); + if ( j < 16 ) { + // V9ism: doubles go in EVEN/ODD regs + regs[i].set2(as_FloatRegister(j<<1)->as_VMReg()); + } else { + // V9ism: doubles go in EVEN/ODD stack slots + regs[i].set2(VMRegImpl::stack2reg(j<<1)); + } + break; + case T_VOID: regs[i].set_bad(); j--; break; // Do not count HALVES + default: + ShouldNotReachHere(); + } + if (regs[i].first()->is_stack()) { + int off = regs[i].first()->reg2stack(); + if (off > max_stack_slots) max_stack_slots = off; + } + if (regs[i].second()->is_stack()) { + int off = regs[i].second()->reg2stack(); + if (off > max_stack_slots) max_stack_slots = off; + } + } + +#else // _LP64 + // V8 convention: first 6 things in O-regs, rest on stack. + // Alignment is willy-nilly. + for( int i=0; iis_stack()) { + int off = regs[i].first()->reg2stack(); + if (off > max_stack_slots) max_stack_slots = off; + } + if (regs[i].second()->is_stack()) { + int off = regs[i].second()->reg2stack(); + if (off > max_stack_slots) max_stack_slots = off; + } + } +#endif // _LP64 + + return round_to(max_stack_slots + 1, 2); + +} + + +// --------------------------------------------------------------------------- +void SharedRuntime::save_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { + switch (ret_type) { + case T_FLOAT: + __ stf(FloatRegisterImpl::S, F0, SP, frame_slots*VMRegImpl::stack_slot_size - 4+STACK_BIAS); + break; + case T_DOUBLE: + __ stf(FloatRegisterImpl::D, F0, SP, frame_slots*VMRegImpl::stack_slot_size - 8+STACK_BIAS); + break; + } +} + +void SharedRuntime::restore_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { + switch (ret_type) { + case T_FLOAT: + __ ldf(FloatRegisterImpl::S, SP, frame_slots*VMRegImpl::stack_slot_size - 4+STACK_BIAS, F0); + break; + case T_DOUBLE: + __ ldf(FloatRegisterImpl::D, SP, frame_slots*VMRegImpl::stack_slot_size - 8+STACK_BIAS, F0); + break; + } +} + +// Check and forward and pending exception. Thread is stored in +// L7_thread_cache and possibly NOT in G2_thread. Since this is a native call, there +// is no exception handler. We merely pop this frame off and throw the +// exception in the caller's frame. +static void check_forward_pending_exception(MacroAssembler *masm, Register Rex_oop) { + Label L; + __ br_null(Rex_oop, false, Assembler::pt, L); + __ delayed()->mov(L7_thread_cache, G2_thread); // restore in case we have exception + // Since this is a native call, we *know* the proper exception handler + // without calling into the VM: it's the empty function. Just pop this + // frame and then jump to forward_exception_entry; O7 will contain the + // native caller's return PC. + Address exception_entry(G3_scratch, StubRoutines::forward_exception_entry()); + __ jump_to(exception_entry); + __ delayed()->restore(); // Pop this frame off. + __ bind(L); +} + +// A simple move of integer like type +static void simple_move32(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack + __ ld(FP, reg2offset(src.first()) + STACK_BIAS, L5); + __ st(L5, SP, reg2offset(dst.first()) + STACK_BIAS); + } else { + // stack to reg + __ ld(FP, reg2offset(src.first()) + STACK_BIAS, dst.first()->as_Register()); + } + } else if (dst.first()->is_stack()) { + // reg to stack + __ st(src.first()->as_Register(), SP, reg2offset(dst.first()) + STACK_BIAS); + } else { + __ mov(src.first()->as_Register(), dst.first()->as_Register()); + } +} + +// On 64 bit we will store integer like items to the stack as +// 64 bits items (sparc abi) even though java would only store +// 32bits for a parameter. On 32bit it will simply be 32 bits +// So this routine will do 32->32 on 32bit and 32->64 on 64bit +static void move32_64(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack + __ ld(FP, reg2offset(src.first()) + STACK_BIAS, L5); + __ st_ptr(L5, SP, reg2offset(dst.first()) + STACK_BIAS); + } else { + // stack to reg + __ ld(FP, reg2offset(src.first()) + STACK_BIAS, dst.first()->as_Register()); + } + } else if (dst.first()->is_stack()) { + // reg to stack + __ st_ptr(src.first()->as_Register(), SP, reg2offset(dst.first()) + STACK_BIAS); + } else { + __ mov(src.first()->as_Register(), dst.first()->as_Register()); + } +} + + +// An oop arg. Must pass a handle not the oop itself +static void object_move(MacroAssembler* masm, + OopMap* map, + int oop_handle_offset, + int framesize_in_slots, + VMRegPair src, + VMRegPair dst, + bool is_receiver, + int* receiver_offset) { + + // must pass a handle. First figure out the location we use as a handle + + if (src.first()->is_stack()) { + // Oop is already on the stack + Register rHandle = dst.first()->is_stack() ? L5 : dst.first()->as_Register(); + __ add(FP, reg2offset(src.first()) + STACK_BIAS, rHandle); + __ ld_ptr(rHandle, 0, L4); +#ifdef _LP64 + __ movr( Assembler::rc_z, L4, G0, rHandle ); +#else + __ tst( L4 ); + __ movcc( Assembler::zero, false, Assembler::icc, G0, rHandle ); +#endif + if (dst.first()->is_stack()) { + __ st_ptr(rHandle, SP, reg2offset(dst.first()) + STACK_BIAS); + } + int offset_in_older_frame = src.first()->reg2stack() + SharedRuntime::out_preserve_stack_slots(); + if (is_receiver) { + *receiver_offset = (offset_in_older_frame + framesize_in_slots) * VMRegImpl::stack_slot_size; + } + map->set_oop(VMRegImpl::stack2reg(offset_in_older_frame + framesize_in_slots)); + } else { + // Oop is in an input register pass we must flush it to the stack + const Register rOop = src.first()->as_Register(); + const Register rHandle = L5; + int oop_slot = rOop->input_number() * VMRegImpl::slots_per_word + oop_handle_offset; + int offset = oop_slot*VMRegImpl::stack_slot_size; + Label skip; + __ st_ptr(rOop, SP, offset + STACK_BIAS); + if (is_receiver) { + *receiver_offset = oop_slot * VMRegImpl::stack_slot_size; + } + map->set_oop(VMRegImpl::stack2reg(oop_slot)); + __ add(SP, offset + STACK_BIAS, rHandle); +#ifdef _LP64 + __ movr( Assembler::rc_z, rOop, G0, rHandle ); +#else + __ tst( rOop ); + __ movcc( Assembler::zero, false, Assembler::icc, G0, rHandle ); +#endif + + if (dst.first()->is_stack()) { + __ st_ptr(rHandle, SP, reg2offset(dst.first()) + STACK_BIAS); + } else { + __ mov(rHandle, dst.first()->as_Register()); + } + } +} + +// A float arg may have to do float reg int reg conversion +static void float_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + assert(!src.second()->is_valid() && !dst.second()->is_valid(), "bad float_move"); + + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack the easiest of the bunch + __ ld(FP, reg2offset(src.first()) + STACK_BIAS, L5); + __ st(L5, SP, reg2offset(dst.first()) + STACK_BIAS); + } else { + // stack to reg + if (dst.first()->is_Register()) { + __ ld(FP, reg2offset(src.first()) + STACK_BIAS, dst.first()->as_Register()); + } else { + __ ldf(FloatRegisterImpl::S, FP, reg2offset(src.first()) + STACK_BIAS, dst.first()->as_FloatRegister()); + } + } + } else if (dst.first()->is_stack()) { + // reg to stack + if (src.first()->is_Register()) { + __ st(src.first()->as_Register(), SP, reg2offset(dst.first()) + STACK_BIAS); + } else { + __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), SP, reg2offset(dst.first()) + STACK_BIAS); + } + } else { + // reg to reg + if (src.first()->is_Register()) { + if (dst.first()->is_Register()) { + // gpr -> gpr + __ mov(src.first()->as_Register(), dst.first()->as_Register()); + } else { + // gpr -> fpr + __ st(src.first()->as_Register(), FP, -4 + STACK_BIAS); + __ ldf(FloatRegisterImpl::S, FP, -4 + STACK_BIAS, dst.first()->as_FloatRegister()); + } + } else if (dst.first()->is_Register()) { + // fpr -> gpr + __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), FP, -4 + STACK_BIAS); + __ ld(FP, -4 + STACK_BIAS, dst.first()->as_Register()); + } else { + // fpr -> fpr + // In theory these overlap but the ordering is such that this is likely a nop + if ( src.first() != dst.first()) { + __ fmov(FloatRegisterImpl::S, src.first()->as_FloatRegister(), dst.first()->as_FloatRegister()); + } + } + } +} + +static void split_long_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + VMRegPair src_lo(src.first()); + VMRegPair src_hi(src.second()); + VMRegPair dst_lo(dst.first()); + VMRegPair dst_hi(dst.second()); + simple_move32(masm, src_lo, dst_lo); + simple_move32(masm, src_hi, dst_hi); +} + +// A long move +static void long_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + + // Do the simple ones here else do two int moves + if (src.is_single_phys_reg() ) { + if (dst.is_single_phys_reg()) { + __ mov(src.first()->as_Register(), dst.first()->as_Register()); + } else { + // split src into two separate registers + // Remember hi means hi address or lsw on sparc + // Move msw to lsw + if (dst.second()->is_reg()) { + // MSW -> MSW + __ srax(src.first()->as_Register(), 32, dst.first()->as_Register()); + // Now LSW -> LSW + // this will only move lo -> lo and ignore hi + VMRegPair split(dst.second()); + simple_move32(masm, src, split); + } else { + VMRegPair split(src.first(), L4->as_VMReg()); + // MSW -> MSW (lo ie. first word) + __ srax(src.first()->as_Register(), 32, L4); + split_long_move(masm, split, dst); + } + } + } else if (dst.is_single_phys_reg()) { + if (src.is_adjacent_aligned_on_stack(2)) { + __ ldd(FP, reg2offset(src.first()) + STACK_BIAS, dst.first()->as_Register()); + } else { + // dst is a single reg. + // Remember lo is low address not msb for stack slots + // and lo is the "real" register for registers + // src is + + VMRegPair split; + + if (src.first()->is_reg()) { + // src.lo (msw) is a reg, src.hi is stk/reg + // we will move: src.hi (LSW) -> dst.lo, src.lo (MSW) -> src.lo [the MSW is in the LSW of the reg] + split.set_pair(dst.first(), src.first()); + } else { + // msw is stack move to L5 + // lsw is stack move to dst.lo (real reg) + // we will move: src.hi (LSW) -> dst.lo, src.lo (MSW) -> L5 + split.set_pair(dst.first(), L5->as_VMReg()); + } + + // src.lo -> src.lo/L5, src.hi -> dst.lo (the real reg) + // msw -> src.lo/L5, lsw -> dst.lo + split_long_move(masm, src, split); + + // So dst now has the low order correct position the + // msw half + __ sllx(split.first()->as_Register(), 32, L5); + + const Register d = dst.first()->as_Register(); + __ or3(L5, d, d); + } + } else { + // For LP64 we can probably do better. + split_long_move(masm, src, dst); + } +} + +// A double move +static void double_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + + // The painful thing here is that like long_move a VMRegPair might be + // 1: a single physical register + // 2: two physical registers (v8) + // 3: a physical reg [lo] and a stack slot [hi] (v8) + // 4: two stack slots + + // Since src is always a java calling convention we know that the src pair + // is always either all registers or all stack (and aligned?) + + // in a register [lo] and a stack slot [hi] + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack the easiest of the bunch + // ought to be a way to do this where if alignment is ok we use ldd/std when possible + __ ld(FP, reg2offset(src.first()) + STACK_BIAS, L5); + __ ld(FP, reg2offset(src.second()) + STACK_BIAS, L4); + __ st(L5, SP, reg2offset(dst.first()) + STACK_BIAS); + __ st(L4, SP, reg2offset(dst.second()) + STACK_BIAS); + } else { + // stack to reg + if (dst.second()->is_stack()) { + // stack -> reg, stack -> stack + __ ld(FP, reg2offset(src.second()) + STACK_BIAS, L4); + if (dst.first()->is_Register()) { + __ ld(FP, reg2offset(src.first()) + STACK_BIAS, dst.first()->as_Register()); + } else { + __ ldf(FloatRegisterImpl::S, FP, reg2offset(src.first()) + STACK_BIAS, dst.first()->as_FloatRegister()); + } + // This was missing. (very rare case) + __ st(L4, SP, reg2offset(dst.second()) + STACK_BIAS); + } else { + // stack -> reg + // Eventually optimize for alignment QQQ + if (dst.first()->is_Register()) { + __ ld(FP, reg2offset(src.first()) + STACK_BIAS, dst.first()->as_Register()); + __ ld(FP, reg2offset(src.second()) + STACK_BIAS, dst.second()->as_Register()); + } else { + __ ldf(FloatRegisterImpl::S, FP, reg2offset(src.first()) + STACK_BIAS, dst.first()->as_FloatRegister()); + __ ldf(FloatRegisterImpl::S, FP, reg2offset(src.second()) + STACK_BIAS, dst.second()->as_FloatRegister()); + } + } + } + } else if (dst.first()->is_stack()) { + // reg to stack + if (src.first()->is_Register()) { + // Eventually optimize for alignment QQQ + __ st(src.first()->as_Register(), SP, reg2offset(dst.first()) + STACK_BIAS); + if (src.second()->is_stack()) { + __ ld(FP, reg2offset(src.second()) + STACK_BIAS, L4); + __ st(L4, SP, reg2offset(dst.second()) + STACK_BIAS); + } else { + __ st(src.second()->as_Register(), SP, reg2offset(dst.second()) + STACK_BIAS); + } + } else { + // fpr to stack + if (src.second()->is_stack()) { + ShouldNotReachHere(); + } else { + // Is the stack aligned? + if (reg2offset(dst.first()) & 0x7) { + // No do as pairs + __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), SP, reg2offset(dst.first()) + STACK_BIAS); + __ stf(FloatRegisterImpl::S, src.second()->as_FloatRegister(), SP, reg2offset(dst.second()) + STACK_BIAS); + } else { + __ stf(FloatRegisterImpl::D, src.first()->as_FloatRegister(), SP, reg2offset(dst.first()) + STACK_BIAS); + } + } + } + } else { + // reg to reg + if (src.first()->is_Register()) { + if (dst.first()->is_Register()) { + // gpr -> gpr + __ mov(src.first()->as_Register(), dst.first()->as_Register()); + __ mov(src.second()->as_Register(), dst.second()->as_Register()); + } else { + // gpr -> fpr + // ought to be able to do a single store + __ stx(src.first()->as_Register(), FP, -8 + STACK_BIAS); + __ stx(src.second()->as_Register(), FP, -4 + STACK_BIAS); + // ought to be able to do a single load + __ ldf(FloatRegisterImpl::S, FP, -8 + STACK_BIAS, dst.first()->as_FloatRegister()); + __ ldf(FloatRegisterImpl::S, FP, -4 + STACK_BIAS, dst.second()->as_FloatRegister()); + } + } else if (dst.first()->is_Register()) { + // fpr -> gpr + // ought to be able to do a single store + __ stf(FloatRegisterImpl::D, src.first()->as_FloatRegister(), FP, -8 + STACK_BIAS); + // ought to be able to do a single load + // REMEMBER first() is low address not LSB + __ ld(FP, -8 + STACK_BIAS, dst.first()->as_Register()); + if (dst.second()->is_Register()) { + __ ld(FP, -4 + STACK_BIAS, dst.second()->as_Register()); + } else { + __ ld(FP, -4 + STACK_BIAS, L4); + __ st(L4, SP, reg2offset(dst.second()) + STACK_BIAS); + } + } else { + // fpr -> fpr + // In theory these overlap but the ordering is such that this is likely a nop + if ( src.first() != dst.first()) { + __ fmov(FloatRegisterImpl::D, src.first()->as_FloatRegister(), dst.first()->as_FloatRegister()); + } + } + } +} + +// Creates an inner frame if one hasn't already been created, and +// saves a copy of the thread in L7_thread_cache +static void create_inner_frame(MacroAssembler* masm, bool* already_created) { + if (!*already_created) { + __ save_frame(0); + // Save thread in L7 (INNER FRAME); it crosses a bunch of VM calls below + // Don't use save_thread because it smashes G2 and we merely want to save a + // copy + __ mov(G2_thread, L7_thread_cache); + *already_created = true; + } +} + +// --------------------------------------------------------------------------- +// Generate a native wrapper for a given method. The method takes arguments +// in the Java compiled code convention, marshals them to the native +// convention (handlizes oops, etc), transitions to native, makes the call, +// returns to java state (possibly blocking), unhandlizes any result and +// returns. +nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler* masm, + methodHandle method, + int total_in_args, + int comp_args_on_stack, // in VMRegStackSlots + BasicType *in_sig_bt, + VMRegPair *in_regs, + BasicType ret_type) { + + + // Native nmethod wrappers never take possesion of the oop arguments. + // So the caller will gc the arguments. The only thing we need an + // oopMap for is if the call is static + // + // An OopMap for lock (and class if static), and one for the VM call itself + OopMapSet *oop_maps = new OopMapSet(); + intptr_t start = (intptr_t)__ pc(); + + // First thing make an ic check to see if we should even be here + { + Label L; + const Register temp_reg = G3_scratch; + Address ic_miss(temp_reg, SharedRuntime::get_ic_miss_stub()); + __ verify_oop(O0); + __ ld_ptr(O0, oopDesc::klass_offset_in_bytes(), temp_reg); + __ cmp(temp_reg, G5_inline_cache_reg); + __ brx(Assembler::equal, true, Assembler::pt, L); + __ delayed()->nop(); + + __ jump_to(ic_miss, 0); + __ delayed()->nop(); + __ align(CodeEntryAlignment); + __ bind(L); + } + + int vep_offset = ((intptr_t)__ pc()) - start; + +#ifdef COMPILER1 + if (InlineObjectHash && method->intrinsic_id() == vmIntrinsics::_hashCode) { + // Object.hashCode can pull the hashCode from the header word + // instead of doing a full VM transition once it's been computed. + // Since hashCode is usually polymorphic at call sites we can't do + // this optimization at the call site without a lot of work. + Label slowCase; + Register receiver = O0; + Register result = O0; + Register header = G3_scratch; + Register hash = G3_scratch; // overwrite header value with hash value + Register mask = G1; // to get hash field from header + + // Read the header and build a mask to get its hash field. Give up if the object is not unlocked. + // We depend on hash_mask being at most 32 bits and avoid the use of + // hash_mask_in_place because it could be larger than 32 bits in a 64-bit + // vm: see markOop.hpp. + __ ld_ptr(receiver, oopDesc::mark_offset_in_bytes(), header); + __ sethi(markOopDesc::hash_mask, mask); + __ btst(markOopDesc::unlocked_value, header); + __ br(Assembler::zero, false, Assembler::pn, slowCase); + if (UseBiasedLocking) { + // Check if biased and fall through to runtime if so + __ delayed()->nop(); + __ btst(markOopDesc::biased_lock_bit_in_place, header); + __ br(Assembler::notZero, false, Assembler::pn, slowCase); + } + __ delayed()->or3(mask, markOopDesc::hash_mask & 0x3ff, mask); + + // Check for a valid (non-zero) hash code and get its value. +#ifdef _LP64 + __ srlx(header, markOopDesc::hash_shift, hash); +#else + __ srl(header, markOopDesc::hash_shift, hash); +#endif + __ andcc(hash, mask, hash); + __ br(Assembler::equal, false, Assembler::pn, slowCase); + __ delayed()->nop(); + + // leaf return. + __ retl(); + __ delayed()->mov(hash, result); + __ bind(slowCase); + } +#endif // COMPILER1 + + + // We have received a description of where all the java arg are located + // on entry to the wrapper. We need to convert these args to where + // the jni function will expect them. To figure out where they go + // we convert the java signature to a C signature by inserting + // the hidden arguments as arg[0] and possibly arg[1] (static method) + + int total_c_args = total_in_args + 1; + if (method->is_static()) { + total_c_args++; + } + + BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_c_args); + VMRegPair * out_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_c_args); + + int argc = 0; + out_sig_bt[argc++] = T_ADDRESS; + if (method->is_static()) { + out_sig_bt[argc++] = T_OBJECT; + } + + for (int i = 0; i < total_in_args ; i++ ) { + out_sig_bt[argc++] = in_sig_bt[i]; + } + + // Now figure out where the args must be stored and how much stack space + // they require (neglecting out_preserve_stack_slots but space for storing + // the 1st six register arguments). It's weird see int_stk_helper. + // + int out_arg_slots; + out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args); + + // Compute framesize for the wrapper. We need to handlize all oops in + // registers. We must create space for them here that is disjoint from + // the windowed save area because we have no control over when we might + // flush the window again and overwrite values that gc has since modified. + // (The live window race) + // + // We always just allocate 6 word for storing down these object. This allow + // us to simply record the base and use the Ireg number to decide which + // slot to use. (Note that the reg number is the inbound number not the + // outbound number). + // We must shuffle args to match the native convention, and include var-args space. + + // Calculate the total number of stack slots we will need. + + // First count the abi requirement plus all of the outgoing args + int stack_slots = SharedRuntime::out_preserve_stack_slots() + out_arg_slots; + + // Now the space for the inbound oop handle area + + int oop_handle_offset = stack_slots; + stack_slots += 6*VMRegImpl::slots_per_word; + + // Now any space we need for handlizing a klass if static method + + int oop_temp_slot_offset = 0; + int klass_slot_offset = 0; + int klass_offset = -1; + int lock_slot_offset = 0; + bool is_static = false; + + if (method->is_static()) { + klass_slot_offset = stack_slots; + stack_slots += VMRegImpl::slots_per_word; + klass_offset = klass_slot_offset * VMRegImpl::stack_slot_size; + is_static = true; + } + + // Plus a lock if needed + + if (method->is_synchronized()) { + lock_slot_offset = stack_slots; + stack_slots += VMRegImpl::slots_per_word; + } + + // Now a place to save return value or as a temporary for any gpr -> fpr moves + stack_slots += 2; + + // Ok The space we have allocated will look like: + // + // + // FP-> | | + // |---------------------| + // | 2 slots for moves | + // |---------------------| + // | lock box (if sync) | + // |---------------------| <- lock_slot_offset + // | klass (if static) | + // |---------------------| <- klass_slot_offset + // | oopHandle area | + // |---------------------| <- oop_handle_offset + // | outbound memory | + // | based arguments | + // | | + // |---------------------| + // | vararg area | + // |---------------------| + // | | + // SP-> | out_preserved_slots | + // + // + + + // Now compute actual number of stack words we need rounding to make + // stack properly aligned. + stack_slots = round_to(stack_slots, 2 * VMRegImpl::slots_per_word); + + int stack_size = stack_slots * VMRegImpl::stack_slot_size; + + // Generate stack overflow check before creating frame + __ generate_stack_overflow_check(stack_size); + + // Generate a new frame for the wrapper. + __ save(SP, -stack_size, SP); + + int frame_complete = ((intptr_t)__ pc()) - start; + + __ verify_thread(); + + + // + // We immediately shuffle the arguments so that any vm call we have to + // make from here on out (sync slow path, jvmti, etc.) we will have + // captured the oops from our caller and have a valid oopMap for + // them. + + // ----------------- + // The Grand Shuffle + // + // Natives require 1 or 2 extra arguments over the normal ones: the JNIEnv* + // (derived from JavaThread* which is in L7_thread_cache) and, if static, + // the class mirror instead of a receiver. This pretty much guarantees that + // register layout will not match. We ignore these extra arguments during + // the shuffle. The shuffle is described by the two calling convention + // vectors we have in our possession. We simply walk the java vector to + // get the source locations and the c vector to get the destinations. + // Because we have a new window and the argument registers are completely + // disjoint ( I0 -> O1, I1 -> O2, ...) we have nothing to worry about + // here. + + // This is a trick. We double the stack slots so we can claim + // the oops in the caller's frame. Since we are sure to have + // more args than the caller doubling is enough to make + // sure we can capture all the incoming oop args from the + // caller. + // + OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); + int c_arg = total_c_args - 1; + // Record sp-based slot for receiver on stack for non-static methods + int receiver_offset = -1; + + // We move the arguments backward because the floating point registers + // destination will always be to a register with a greater or equal register + // number or the stack. + +#ifdef ASSERT + bool reg_destroyed[RegisterImpl::number_of_registers]; + bool freg_destroyed[FloatRegisterImpl::number_of_registers]; + for ( int r = 0 ; r < RegisterImpl::number_of_registers ; r++ ) { + reg_destroyed[r] = false; + } + for ( int f = 0 ; f < FloatRegisterImpl::number_of_registers ; f++ ) { + freg_destroyed[f] = false; + } + +#endif /* ASSERT */ + + for ( int i = total_in_args - 1; i >= 0 ; i--, c_arg-- ) { + +#ifdef ASSERT + if (in_regs[i].first()->is_Register()) { + assert(!reg_destroyed[in_regs[i].first()->as_Register()->encoding()], "ack!"); + } else if (in_regs[i].first()->is_FloatRegister()) { + assert(!freg_destroyed[in_regs[i].first()->as_FloatRegister()->encoding(FloatRegisterImpl::S)], "ack!"); + } + if (out_regs[c_arg].first()->is_Register()) { + reg_destroyed[out_regs[c_arg].first()->as_Register()->encoding()] = true; + } else if (out_regs[c_arg].first()->is_FloatRegister()) { + freg_destroyed[out_regs[c_arg].first()->as_FloatRegister()->encoding(FloatRegisterImpl::S)] = true; + } +#endif /* ASSERT */ + + switch (in_sig_bt[i]) { + case T_ARRAY: + case T_OBJECT: + object_move(masm, map, oop_handle_offset, stack_slots, in_regs[i], out_regs[c_arg], + ((i == 0) && (!is_static)), + &receiver_offset); + break; + case T_VOID: + break; + + case T_FLOAT: + float_move(masm, in_regs[i], out_regs[c_arg]); + break; + + case T_DOUBLE: + assert( i + 1 < total_in_args && + in_sig_bt[i + 1] == T_VOID && + out_sig_bt[c_arg+1] == T_VOID, "bad arg list"); + double_move(masm, in_regs[i], out_regs[c_arg]); + break; + + case T_LONG : + long_move(masm, in_regs[i], out_regs[c_arg]); + break; + + case T_ADDRESS: assert(false, "found T_ADDRESS in java args"); + + default: + move32_64(masm, in_regs[i], out_regs[c_arg]); + } + } + + // Pre-load a static method's oop into O1. Used both by locking code and + // the normal JNI call code. + if (method->is_static()) { + __ set_oop_constant(JNIHandles::make_local(Klass::cast(method->method_holder())->java_mirror()), O1); + + // Now handlize the static class mirror in O1. It's known not-null. + __ st_ptr(O1, SP, klass_offset + STACK_BIAS); + map->set_oop(VMRegImpl::stack2reg(klass_slot_offset)); + __ add(SP, klass_offset + STACK_BIAS, O1); + } + + + const Register L6_handle = L6; + + if (method->is_synchronized()) { + __ mov(O1, L6_handle); + } + + // We have all of the arguments setup at this point. We MUST NOT touch any Oregs + // except O6/O7. So if we must call out we must push a new frame. We immediately + // push a new frame and flush the windows. + +#ifdef _LP64 + intptr_t thepc = (intptr_t) __ pc(); + { + address here = __ pc(); + // Call the next instruction + __ call(here + 8, relocInfo::none); + __ delayed()->nop(); + } +#else + intptr_t thepc = __ load_pc_address(O7, 0); +#endif /* _LP64 */ + + // We use the same pc/oopMap repeatedly when we call out + oop_maps->add_gc_map(thepc - start, map); + + // O7 now has the pc loaded that we will use when we finally call to native. + + // Save thread in L7; it crosses a bunch of VM calls below + // Don't use save_thread because it smashes G2 and we merely + // want to save a copy + __ mov(G2_thread, L7_thread_cache); + + + // If we create an inner frame once is plenty + // when we create it we must also save G2_thread + bool inner_frame_created = false; + + // dtrace method entry support + { + SkipIfEqual skip_if( + masm, G3_scratch, &DTraceMethodProbes, Assembler::zero); + // create inner frame + __ save_frame(0); + __ mov(G2_thread, L7_thread_cache); + __ set_oop_constant(JNIHandles::make_local(method()), O1); + __ call_VM_leaf(L7_thread_cache, + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), + G2_thread, O1); + __ restore(); + } + + // We are in the jni frame unless saved_frame is true in which case + // we are in one frame deeper (the "inner" frame). If we are in the + // "inner" frames the args are in the Iregs and if the jni frame then + // they are in the Oregs. + // If we ever need to go to the VM (for locking, jvmti) then + // we will always be in the "inner" frame. + + // Lock a synchronized method + int lock_offset = -1; // Set if locked + if (method->is_synchronized()) { + Register Roop = O1; + const Register L3_box = L3; + + create_inner_frame(masm, &inner_frame_created); + + __ ld_ptr(I1, 0, O1); + Label done; + + lock_offset = (lock_slot_offset * VMRegImpl::stack_slot_size); + __ add(FP, lock_offset+STACK_BIAS, L3_box); +#ifdef ASSERT + if (UseBiasedLocking) { + // making the box point to itself will make it clear it went unused + // but also be obviously invalid + __ st_ptr(L3_box, L3_box, 0); + } +#endif // ASSERT + // + // Compiler_lock_object (Roop, Rmark, Rbox, Rscratch) -- kills Rmark, Rbox, Rscratch + // + __ compiler_lock_object(Roop, L1, L3_box, L2); + __ br(Assembler::equal, false, Assembler::pt, done); + __ delayed() -> add(FP, lock_offset+STACK_BIAS, L3_box); + + + // None of the above fast optimizations worked so we have to get into the + // slow case of monitor enter. Inline a special case of call_VM that + // disallows any pending_exception. + __ mov(Roop, O0); // Need oop in O0 + __ mov(L3_box, O1); + + // Record last_Java_sp, in case the VM code releases the JVM lock. + + __ set_last_Java_frame(FP, I7); + + // do the call + __ call(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C), relocInfo::runtime_call_type); + __ delayed()->mov(L7_thread_cache, O2); + + __ restore_thread(L7_thread_cache); // restore G2_thread + __ reset_last_Java_frame(); + +#ifdef ASSERT + { Label L; + __ ld_ptr(G2_thread, in_bytes(Thread::pending_exception_offset()), O0); + __ br_null(O0, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("no pending exception allowed on exit from IR::monitorenter"); + __ bind(L); + } +#endif + __ bind(done); + } + + + // Finally just about ready to make the JNI call + + __ flush_windows(); + if (inner_frame_created) { + __ restore(); + } else { + // Store only what we need from this frame + // QQQ I think that non-v9 (like we care) we don't need these saves + // either as the flush traps and the current window goes too. + __ st_ptr(FP, SP, FP->sp_offset_in_saved_window()*wordSize + STACK_BIAS); + __ st_ptr(I7, SP, I7->sp_offset_in_saved_window()*wordSize + STACK_BIAS); + } + + // get JNIEnv* which is first argument to native + + __ add(G2_thread, in_bytes(JavaThread::jni_environment_offset()), O0); + + // Use that pc we placed in O7 a while back as the current frame anchor + + __ set_last_Java_frame(SP, O7); + + // Transition from _thread_in_Java to _thread_in_native. + __ set(_thread_in_native, G3_scratch); + __ st(G3_scratch, G2_thread, in_bytes(JavaThread::thread_state_offset())); + + // We flushed the windows ages ago now mark them as flushed + + // mark windows as flushed + __ set(JavaFrameAnchor::flushed, G3_scratch); + + Address flags(G2_thread, + 0, + in_bytes(JavaThread::frame_anchor_offset()) + in_bytes(JavaFrameAnchor::flags_offset())); + +#ifdef _LP64 + Address dest(O7, method->native_function()); + __ relocate(relocInfo::runtime_call_type); + __ jumpl_to(dest, O7); +#else + __ call(method->native_function(), relocInfo::runtime_call_type); +#endif + __ delayed()->st(G3_scratch, flags); + + __ restore_thread(L7_thread_cache); // restore G2_thread + + // Unpack native results. For int-types, we do any needed sign-extension + // and move things into I0. The return value there will survive any VM + // calls for blocking or unlocking. An FP or OOP result (handle) is done + // specially in the slow-path code. + switch (ret_type) { + case T_VOID: break; // Nothing to do! + case T_FLOAT: break; // Got it where we want it (unless slow-path) + case T_DOUBLE: break; // Got it where we want it (unless slow-path) + // In 64 bits build result is in O0, in O0, O1 in 32bit build + case T_LONG: +#ifndef _LP64 + __ mov(O1, I1); +#endif + // Fall thru + case T_OBJECT: // Really a handle + case T_ARRAY: + case T_INT: + __ mov(O0, I0); + break; + case T_BOOLEAN: __ subcc(G0, O0, G0); __ addc(G0, 0, I0); break; // !0 => true; 0 => false + case T_BYTE : __ sll(O0, 24, O0); __ sra(O0, 24, I0); break; + case T_CHAR : __ sll(O0, 16, O0); __ srl(O0, 16, I0); break; // cannot use and3, 0xFFFF too big as immediate value! + case T_SHORT : __ sll(O0, 16, O0); __ sra(O0, 16, I0); break; + break; // Cannot de-handlize until after reclaiming jvm_lock + default: + ShouldNotReachHere(); + } + + // must we block? + + // Block, if necessary, before resuming in _thread_in_Java state. + // In order for GC to work, don't clear the last_Java_sp until after blocking. + { Label no_block; + Address sync_state(G3_scratch, SafepointSynchronize::address_of_state()); + + // Switch thread to "native transition" state before reading the synchronization state. + // This additional state is necessary because reading and testing the synchronization + // state is not atomic w.r.t. GC, as this scenario demonstrates: + // Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. + // VM thread changes sync state to synchronizing and suspends threads for GC. + // Thread A is resumed to finish this native method, but doesn't block here since it + // didn't see any synchronization is progress, and escapes. + __ set(_thread_in_native_trans, G3_scratch); + __ st(G3_scratch, G2_thread, in_bytes(JavaThread::thread_state_offset())); + if(os::is_MP()) { + if (UseMembar) { + // Force this write out before the read below + __ membar(Assembler::StoreLoad); + } else { + // Write serialization page so VM thread can do a pseudo remote membar. + // We use the current thread pointer to calculate a thread specific + // offset to write to within the page. This minimizes bus traffic + // due to cache line collision. + __ serialize_memory(G2_thread, G1_scratch, G3_scratch); + } + } + __ load_contents(sync_state, G3_scratch); + __ cmp(G3_scratch, SafepointSynchronize::_not_synchronized); + + Label L; + Address suspend_state(G2_thread, 0, in_bytes(JavaThread::suspend_flags_offset())); + __ br(Assembler::notEqual, false, Assembler::pn, L); + __ delayed()-> + ld(suspend_state, G3_scratch); + __ cmp(G3_scratch, 0); + __ br(Assembler::equal, false, Assembler::pt, no_block); + __ delayed()->nop(); + __ bind(L); + + // Block. Save any potential method result value before the operation and + // use a leaf call to leave the last_Java_frame setup undisturbed. Doing this + // lets us share the oopMap we used when we went native rather the create + // a distinct one for this pc + // + save_native_result(masm, ret_type, stack_slots); + __ call_VM_leaf(L7_thread_cache, + CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans), + G2_thread); + + // Restore any method result value + restore_native_result(masm, ret_type, stack_slots); + __ bind(no_block); + } + + // thread state is thread_in_native_trans. Any safepoint blocking has already + // happened so we can now change state to _thread_in_Java. + + + __ set(_thread_in_Java, G3_scratch); + __ st(G3_scratch, G2_thread, in_bytes(JavaThread::thread_state_offset())); + + + Label no_reguard; + __ ld(G2_thread, in_bytes(JavaThread::stack_guard_state_offset()), G3_scratch); + __ cmp(G3_scratch, JavaThread::stack_guard_yellow_disabled); + __ br(Assembler::notEqual, false, Assembler::pt, no_reguard); + __ delayed()->nop(); + + save_native_result(masm, ret_type, stack_slots); + __ call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); + __ delayed()->nop(); + + __ restore_thread(L7_thread_cache); // restore G2_thread + restore_native_result(masm, ret_type, stack_slots); + + __ bind(no_reguard); + + // Handle possible exception (will unlock if necessary) + + // native result if any is live in freg or I0 (and I1 if long and 32bit vm) + + // Unlock + if (method->is_synchronized()) { + Label done; + Register I2_ex_oop = I2; + const Register L3_box = L3; + // Get locked oop from the handle we passed to jni + __ ld_ptr(L6_handle, 0, L4); + __ add(SP, lock_offset+STACK_BIAS, L3_box); + // Must save pending exception around the slow-path VM call. Since it's a + // leaf call, the pending exception (if any) can be kept in a register. + __ ld_ptr(G2_thread, in_bytes(Thread::pending_exception_offset()), I2_ex_oop); + // Now unlock + // (Roop, Rmark, Rbox, Rscratch) + __ compiler_unlock_object(L4, L1, L3_box, L2); + __ br(Assembler::equal, false, Assembler::pt, done); + __ delayed()-> add(SP, lock_offset+STACK_BIAS, L3_box); + + // save and restore any potential method result value around the unlocking + // operation. Will save in I0 (or stack for FP returns). + save_native_result(masm, ret_type, stack_slots); + + // Must clear pending-exception before re-entering the VM. Since this is + // a leaf call, pending-exception-oop can be safely kept in a register. + __ st_ptr(G0, G2_thread, in_bytes(Thread::pending_exception_offset())); + + // slow case of monitor enter. Inline a special case of call_VM that + // disallows any pending_exception. + __ mov(L3_box, O1); + + __ call(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_unlocking_C), relocInfo::runtime_call_type); + __ delayed()->mov(L4, O0); // Need oop in O0 + + __ restore_thread(L7_thread_cache); // restore G2_thread + +#ifdef ASSERT + { Label L; + __ ld_ptr(G2_thread, in_bytes(Thread::pending_exception_offset()), O0); + __ br_null(O0, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("no pending exception allowed on exit from IR::monitorexit"); + __ bind(L); + } +#endif + restore_native_result(masm, ret_type, stack_slots); + // check_forward_pending_exception jump to forward_exception if any pending + // exception is set. The forward_exception routine expects to see the + // exception in pending_exception and not in a register. Kind of clumsy, + // since all folks who branch to forward_exception must have tested + // pending_exception first and hence have it in a register already. + __ st_ptr(I2_ex_oop, G2_thread, in_bytes(Thread::pending_exception_offset())); + __ bind(done); + } + + // Tell dtrace about this method exit + { + SkipIfEqual skip_if( + masm, G3_scratch, &DTraceMethodProbes, Assembler::zero); + save_native_result(masm, ret_type, stack_slots); + __ set_oop_constant(JNIHandles::make_local(method()), O1); + __ call_VM_leaf(L7_thread_cache, + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), + G2_thread, O1); + restore_native_result(masm, ret_type, stack_slots); + } + + // Clear "last Java frame" SP and PC. + __ verify_thread(); // G2_thread must be correct + __ reset_last_Java_frame(); + + // Unpack oop result + if (ret_type == T_OBJECT || ret_type == T_ARRAY) { + Label L; + __ addcc(G0, I0, G0); + __ brx(Assembler::notZero, true, Assembler::pt, L); + __ delayed()->ld_ptr(I0, 0, I0); + __ mov(G0, I0); + __ bind(L); + __ verify_oop(I0); + } + + // reset handle block + __ ld_ptr(G2_thread, in_bytes(JavaThread::active_handles_offset()), L5); + __ st_ptr(G0, L5, JNIHandleBlock::top_offset_in_bytes()); + + __ ld_ptr(G2_thread, in_bytes(Thread::pending_exception_offset()), G3_scratch); + check_forward_pending_exception(masm, G3_scratch); + + + // Return + +#ifndef _LP64 + if (ret_type == T_LONG) { + + // Must leave proper result in O0,O1 and G1 (c2/tiered only) + __ sllx(I0, 32, G1); // Shift bits into high G1 + __ srl (I1, 0, I1); // Zero extend O1 (harmless?) + __ or3 (I1, G1, G1); // OR 64 bits into G1 + } +#endif + + __ ret(); + __ delayed()->restore(); + + __ flush(); + + nmethod *nm = nmethod::new_native_nmethod(method, + masm->code(), + vep_offset, + frame_complete, + stack_slots / VMRegImpl::slots_per_word, + (is_static ? in_ByteSize(klass_offset) : in_ByteSize(receiver_offset)), + in_ByteSize(lock_offset), + oop_maps); + return nm; + +} + +// this function returns the adjust size (in number of words) to a c2i adapter +// activation for use during deoptimization +int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals) { + assert(callee_locals >= callee_parameters, + "test and remove; got more parms than locals"); + if (callee_locals < callee_parameters) + return 0; // No adjustment for negative locals + int diff = (callee_locals - callee_parameters) * Interpreter::stackElementWords(); + return round_to(diff, WordsPerLong); +} + +// "Top of Stack" slots that may be unused by the calling convention but must +// otherwise be preserved. +// On Intel these are not necessary and the value can be zero. +// On Sparc this describes the words reserved for storing a register window +// when an interrupt occurs. +uint SharedRuntime::out_preserve_stack_slots() { + return frame::register_save_words * VMRegImpl::slots_per_word; +} + +static void gen_new_frame(MacroAssembler* masm, bool deopt) { +// +// Common out the new frame generation for deopt and uncommon trap +// + Register G3pcs = G3_scratch; // Array of new pcs (input) + Register Oreturn0 = O0; + Register Oreturn1 = O1; + Register O2UnrollBlock = O2; + Register O3array = O3; // Array of frame sizes (input) + Register O4array_size = O4; // number of frames (input) + Register O7frame_size = O7; // number of frames (input) + + __ ld_ptr(O3array, 0, O7frame_size); + __ sub(G0, O7frame_size, O7frame_size); + __ save(SP, O7frame_size, SP); + __ ld_ptr(G3pcs, 0, I7); // load frame's new pc + + #ifdef ASSERT + // make sure that the frames are aligned properly +#ifndef _LP64 + __ btst(wordSize*2-1, SP); + __ breakpoint_trap(Assembler::notZero); +#endif + #endif + + // Deopt needs to pass some extra live values from frame to frame + + if (deopt) { + __ mov(Oreturn0->after_save(), Oreturn0); + __ mov(Oreturn1->after_save(), Oreturn1); + } + + __ mov(O4array_size->after_save(), O4array_size); + __ sub(O4array_size, 1, O4array_size); + __ mov(O3array->after_save(), O3array); + __ mov(O2UnrollBlock->after_save(), O2UnrollBlock); + __ add(G3pcs, wordSize, G3pcs); // point to next pc value + + #ifdef ASSERT + // trash registers to show a clear pattern in backtraces + __ set(0xDEAD0000, I0); + __ add(I0, 2, I1); + __ add(I0, 4, I2); + __ add(I0, 6, I3); + __ add(I0, 8, I4); + // Don't touch I5 could have valuable savedSP + __ set(0xDEADBEEF, L0); + __ mov(L0, L1); + __ mov(L0, L2); + __ mov(L0, L3); + __ mov(L0, L4); + __ mov(L0, L5); + + // trash the return value as there is nothing to return yet + __ set(0xDEAD0001, O7); + #endif + + __ mov(SP, O5_savedSP); +} + + +static void make_new_frames(MacroAssembler* masm, bool deopt) { + // + // loop through the UnrollBlock info and create new frames + // + Register G3pcs = G3_scratch; + Register Oreturn0 = O0; + Register Oreturn1 = O1; + Register O2UnrollBlock = O2; + Register O3array = O3; + Register O4array_size = O4; + Label loop; + + // Before we make new frames, check to see if stack is available. + // Do this after the caller's return address is on top of stack + if (UseStackBanging) { + // Get total frame size for interpreted frames + __ ld(Address(O2UnrollBlock, 0, + Deoptimization::UnrollBlock::total_frame_sizes_offset_in_bytes()), O4); + __ bang_stack_size(O4, O3, G3_scratch); + } + + __ ld(Address(O2UnrollBlock, 0, Deoptimization::UnrollBlock::number_of_frames_offset_in_bytes()), O4array_size); + __ ld_ptr(Address(O2UnrollBlock, 0, Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes()), G3pcs); + + __ ld_ptr(Address(O2UnrollBlock, 0, Deoptimization::UnrollBlock::frame_sizes_offset_in_bytes()), O3array); + + // Adjust old interpreter frame to make space for new frame's extra java locals + // + // We capture the original sp for the transition frame only because it is needed in + // order to properly calculate interpreter_sp_adjustment. Even though in real life + // every interpreter frame captures a savedSP it is only needed at the transition + // (fortunately). If we had to have it correct everywhere then we would need to + // be told the sp_adjustment for each frame we create. If the frame size array + // were to have twice the frame count entries then we could have pairs [sp_adjustment, frame_size] + // for each frame we create and keep up the illusion every where. + // + + __ ld(Address(O2UnrollBlock, 0, Deoptimization::UnrollBlock::caller_adjustment_offset_in_bytes()), O7); + __ mov(SP, O5_savedSP); // remember initial sender's original sp before adjustment + __ sub(SP, O7, SP); + +#ifdef ASSERT + // make sure that there is at least one entry in the array + __ tst(O4array_size); + __ breakpoint_trap(Assembler::zero); +#endif + + // Now push the new interpreter frames + __ bind(loop); + + // allocate a new frame, filling the registers + + gen_new_frame(masm, deopt); // allocate an interpreter frame + + __ tst(O4array_size); + __ br(Assembler::notZero, false, Assembler::pn, loop); + __ delayed()->add(O3array, wordSize, O3array); + __ ld_ptr(G3pcs, 0, O7); // load final frame new pc + +} + +//------------------------------generate_deopt_blob---------------------------- +// Ought to generate an ideal graph & compile, but here's some SPARC ASM +// instead. +void SharedRuntime::generate_deopt_blob() { + // allocate space for the code + ResourceMark rm; + // setup code generation tools + int pad = VerifyThread ? 512 : 0;// Extra slop space for more verify code +#ifdef _LP64 + CodeBuffer buffer("deopt_blob", 2100+pad, 512); +#else + // Measured 8/7/03 at 1212 in 32bit debug build (no VerifyThread) + // Measured 8/7/03 at 1396 in 32bit debug build (VerifyThread) + CodeBuffer buffer("deopt_blob", 1600+pad, 512); +#endif /* _LP64 */ + MacroAssembler* masm = new MacroAssembler(&buffer); + FloatRegister Freturn0 = F0; + Register Greturn1 = G1; + Register Oreturn0 = O0; + Register Oreturn1 = O1; + Register O2UnrollBlock = O2; + Register O3tmp = O3; + Register I5exception_tmp = I5; + Register G4exception_tmp = G4_scratch; + int frame_size_words; + Address saved_Freturn0_addr(FP, 0, -sizeof(double) + STACK_BIAS); +#if !defined(_LP64) && defined(COMPILER2) + Address saved_Greturn1_addr(FP, 0, -sizeof(double) -sizeof(jlong) + STACK_BIAS); +#endif + Label cont; + + OopMapSet *oop_maps = new OopMapSet(); + + // + // This is the entry point for code which is returning to a de-optimized + // frame. + // The steps taken by this frame are as follows: + // - push a dummy "register_save" and save the return values (O0, O1, F0/F1, G1) + // and all potentially live registers (at a pollpoint many registers can be live). + // + // - call the C routine: Deoptimization::fetch_unroll_info (this function + // returns information about the number and size of interpreter frames + // which are equivalent to the frame which is being deoptimized) + // - deallocate the unpack frame, restoring only results values. Other + // volatile registers will now be captured in the vframeArray as needed. + // - deallocate the deoptimization frame + // - in a loop using the information returned in the previous step + // push new interpreter frames (take care to propagate the return + // values through each new frame pushed) + // - create a dummy "unpack_frame" and save the return values (O0, O1, F0) + // - call the C routine: Deoptimization::unpack_frames (this function + // lays out values on the interpreter frame which was just created) + // - deallocate the dummy unpack_frame + // - ensure that all the return values are correctly set and then do + // a return to the interpreter entry point + // + // Refer to the following methods for more information: + // - Deoptimization::fetch_unroll_info + // - Deoptimization::unpack_frames + + OopMap* map = NULL; + + int start = __ offset(); + + // restore G2, the trampoline destroyed it + __ get_thread(); + + // On entry we have been called by the deoptimized nmethod with a call that + // replaced the original call (or safepoint polling location) so the deoptimizing + // pc is now in O7. Return values are still in the expected places + + map = RegisterSaver::save_live_registers(masm, 0, &frame_size_words); + __ ba(false, cont); + __ delayed()->mov(Deoptimization::Unpack_deopt, I5exception_tmp); + + int exception_offset = __ offset() - start; + + // restore G2, the trampoline destroyed it + __ get_thread(); + + // On entry we have been jumped to by the exception handler (or exception_blob + // for server). O0 contains the exception oop and O7 contains the original + // exception pc. So if we push a frame here it will look to the + // stack walking code (fetch_unroll_info) just like a normal call so + // state will be extracted normally. + + // save exception oop in JavaThread and fall through into the + // exception_in_tls case since they are handled in same way except + // for where the pending exception is kept. + __ st_ptr(Oexception, G2_thread, in_bytes(JavaThread::exception_oop_offset())); + + // + // Vanilla deoptimization with an exception pending in exception_oop + // + int exception_in_tls_offset = __ offset() - start; + + // No need to update oop_map as each call to save_live_registers will produce identical oopmap + (void) RegisterSaver::save_live_registers(masm, 0, &frame_size_words); + + // Restore G2_thread + __ get_thread(); + +#ifdef ASSERT + { + // verify that there is really an exception oop in exception_oop + Label has_exception; + __ ld_ptr(G2_thread, in_bytes(JavaThread::exception_oop_offset()), Oexception); + __ br_notnull(Oexception, false, Assembler::pt, has_exception); + __ delayed()-> nop(); + __ stop("no exception in thread"); + __ bind(has_exception); + + // verify that there is no pending exception + Label no_pending_exception; + Address exception_addr(G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + __ ld_ptr(exception_addr, Oexception); + __ br_null(Oexception, false, Assembler::pt, no_pending_exception); + __ delayed()->nop(); + __ stop("must not have pending exception here"); + __ bind(no_pending_exception); + } +#endif + + __ ba(false, cont); + __ delayed()->mov(Deoptimization::Unpack_exception, I5exception_tmp);; + + // + // Reexecute entry, similar to c2 uncommon trap + // + int reexecute_offset = __ offset() - start; + + // No need to update oop_map as each call to save_live_registers will produce identical oopmap + (void) RegisterSaver::save_live_registers(masm, 0, &frame_size_words); + + __ mov(Deoptimization::Unpack_reexecute, I5exception_tmp); + + __ bind(cont); + + __ set_last_Java_frame(SP, noreg); + + // do the call by hand so we can get the oopmap + + __ mov(G2_thread, L7_thread_cache); + __ call(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info), relocInfo::runtime_call_type); + __ delayed()->mov(G2_thread, O0); + + // Set an oopmap for the call site this describes all our saved volatile registers + + oop_maps->add_gc_map( __ offset()-start, map); + + __ mov(L7_thread_cache, G2_thread); + + __ reset_last_Java_frame(); + + // NOTE: we know that only O0/O1 will be reloaded by restore_result_registers + // so this move will survive + + __ mov(I5exception_tmp, G4exception_tmp); + + __ mov(O0, O2UnrollBlock->after_save()); + + RegisterSaver::restore_result_registers(masm); + + Label noException; + __ cmp(G4exception_tmp, Deoptimization::Unpack_exception); // Was exception pending? + __ br(Assembler::notEqual, false, Assembler::pt, noException); + __ delayed()->nop(); + + // Move the pending exception from exception_oop to Oexception so + // the pending exception will be picked up the interpreter. + __ ld_ptr(G2_thread, in_bytes(JavaThread::exception_oop_offset()), Oexception); + __ st_ptr(G0, G2_thread, in_bytes(JavaThread::exception_oop_offset())); + __ bind(noException); + + // deallocate the deoptimization frame taking care to preserve the return values + __ mov(Oreturn0, Oreturn0->after_save()); + __ mov(Oreturn1, Oreturn1->after_save()); + __ mov(O2UnrollBlock, O2UnrollBlock->after_save()); + __ restore(); + + // Allocate new interpreter frame(s) and possible c2i adapter frame + + make_new_frames(masm, true); + + // push a dummy "unpack_frame" taking care of float return values and + // call Deoptimization::unpack_frames to have the unpacker layout + // information in the interpreter frames just created and then return + // to the interpreter entry point + __ save(SP, -frame_size_words*wordSize, SP); + __ stf(FloatRegisterImpl::D, Freturn0, saved_Freturn0_addr); +#if !defined(_LP64) +#if defined(COMPILER2) + if (!TieredCompilation) { + // 32-bit 1-register longs return longs in G1 + __ stx(Greturn1, saved_Greturn1_addr); + } +#endif + __ set_last_Java_frame(SP, noreg); + __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames), G2_thread, G4exception_tmp); +#else + // LP64 uses g4 in set_last_Java_frame + __ mov(G4exception_tmp, O1); + __ set_last_Java_frame(SP, G0); + __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames), G2_thread, O1); +#endif + __ reset_last_Java_frame(); + __ ldf(FloatRegisterImpl::D, saved_Freturn0_addr, Freturn0); + + // In tiered we never use C2 to compile methods returning longs so + // the result is where we expect it already. + +#if !defined(_LP64) && defined(COMPILER2) + // In 32 bit, C2 returns longs in G1 so restore the saved G1 into + // I0/I1 if the return value is long. In the tiered world there is + // a mismatch between how C1 and C2 return longs compiles and so + // currently compilation of methods which return longs is disabled + // for C2 and so is this code. Eventually C1 and C2 will do the + // same thing for longs in the tiered world. + if (!TieredCompilation) { + Label not_long; + __ cmp(O0,T_LONG); + __ br(Assembler::notEqual, false, Assembler::pt, not_long); + __ delayed()->nop(); + __ ldd(saved_Greturn1_addr,I0); + __ bind(not_long); + } +#endif + __ ret(); + __ delayed()->restore(); + + masm->flush(); + _deopt_blob = DeoptimizationBlob::create(&buffer, oop_maps, 0, exception_offset, reexecute_offset, frame_size_words); + _deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset); +} + +#ifdef COMPILER2 + +//------------------------------generate_uncommon_trap_blob-------------------- +// Ought to generate an ideal graph & compile, but here's some SPARC ASM +// instead. +void SharedRuntime::generate_uncommon_trap_blob() { + // allocate space for the code + ResourceMark rm; + // setup code generation tools + int pad = VerifyThread ? 512 : 0; +#ifdef _LP64 + CodeBuffer buffer("uncommon_trap_blob", 2700+pad, 512); +#else + // Measured 8/7/03 at 660 in 32bit debug build (no VerifyThread) + // Measured 8/7/03 at 1028 in 32bit debug build (VerifyThread) + CodeBuffer buffer("uncommon_trap_blob", 2000+pad, 512); +#endif + MacroAssembler* masm = new MacroAssembler(&buffer); + Register O2UnrollBlock = O2; + Register O3tmp = O3; + Register O2klass_index = O2; + + // + // This is the entry point for all traps the compiler takes when it thinks + // it cannot handle further execution of compilation code. The frame is + // deoptimized in these cases and converted into interpreter frames for + // execution + // The steps taken by this frame are as follows: + // - push a fake "unpack_frame" + // - call the C routine Deoptimization::uncommon_trap (this function + // packs the current compiled frame into vframe arrays and returns + // information about the number and size of interpreter frames which + // are equivalent to the frame which is being deoptimized) + // - deallocate the "unpack_frame" + // - deallocate the deoptimization frame + // - in a loop using the information returned in the previous step + // push interpreter frames; + // - create a dummy "unpack_frame" + // - call the C routine: Deoptimization::unpack_frames (this function + // lays out values on the interpreter frame which was just created) + // - deallocate the dummy unpack_frame + // - return to the interpreter entry point + // + // Refer to the following methods for more information: + // - Deoptimization::uncommon_trap + // - Deoptimization::unpack_frame + + // the unloaded class index is in O0 (first parameter to this blob) + + // push a dummy "unpack_frame" + // and call Deoptimization::uncommon_trap to pack the compiled frame into + // vframe array and return the UnrollBlock information + __ save_frame(0); + __ set_last_Java_frame(SP, noreg); + __ mov(I0, O2klass_index); + __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap), G2_thread, O2klass_index); + __ reset_last_Java_frame(); + __ mov(O0, O2UnrollBlock->after_save()); + __ restore(); + + // deallocate the deoptimized frame taking care to preserve the return values + __ mov(O2UnrollBlock, O2UnrollBlock->after_save()); + __ restore(); + + // Allocate new interpreter frame(s) and possible c2i adapter frame + + make_new_frames(masm, false); + + // push a dummy "unpack_frame" taking care of float return values and + // call Deoptimization::unpack_frames to have the unpacker layout + // information in the interpreter frames just created and then return + // to the interpreter entry point + __ save_frame(0); + __ set_last_Java_frame(SP, noreg); + __ mov(Deoptimization::Unpack_uncommon_trap, O3); // indicate it is the uncommon trap case + __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames), G2_thread, O3); + __ reset_last_Java_frame(); + __ ret(); + __ delayed()->restore(); + + masm->flush(); + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, NULL, __ total_frame_size_in_bytes(0)/wordSize); +} + +#endif // COMPILER2 + +//------------------------------generate_handler_blob------------------- +// +// Generate a special Compile2Runtime blob that saves all registers, and sets +// up an OopMap. +// +// This blob is jumped to (via a breakpoint and the signal handler) from a +// safepoint in compiled code. On entry to this blob, O7 contains the +// address in the original nmethod at which we should resume normal execution. +// Thus, this blob looks like a subroutine which must preserve lots of +// registers and return normally. Note that O7 is never register-allocated, +// so it is guaranteed to be free here. +// + +// The hardest part of what this blob must do is to save the 64-bit %o +// registers in the 32-bit build. A simple 'save' turn the %o's to %i's and +// an interrupt will chop off their heads. Making space in the caller's frame +// first will let us save the 64-bit %o's before save'ing, but we cannot hand +// the adjusted FP off to the GC stack-crawler: this will modify the caller's +// SP and mess up HIS OopMaps. So we first adjust the caller's SP, then save +// the 64-bit %o's, then do a save, then fixup the caller's SP (our FP). +// Tricky, tricky, tricky... + +static SafepointBlob* generate_handler_blob(address call_ptr, bool cause_return) { + assert (StubRoutines::forward_exception_entry() != NULL, "must be generated before"); + + // allocate space for the code + ResourceMark rm; + // setup code generation tools + // Measured 8/7/03 at 896 in 32bit debug build (no VerifyThread) + // Measured 8/7/03 at 1080 in 32bit debug build (VerifyThread) + // even larger with TraceJumps + int pad = TraceJumps ? 512 : 0; + CodeBuffer buffer("handler_blob", 1600 + pad, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + int frame_size_words; + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map = NULL; + + int start = __ offset(); + + // If this causes a return before the processing, then do a "restore" + if (cause_return) { + __ restore(); + } else { + // Make it look like we were called via the poll + // so that frame constructor always sees a valid return address + __ ld_ptr(G2_thread, in_bytes(JavaThread::saved_exception_pc_offset()), O7); + __ sub(O7, frame::pc_return_offset, O7); + } + + map = RegisterSaver::save_live_registers(masm, 0, &frame_size_words); + + // setup last_Java_sp (blows G4) + __ set_last_Java_frame(SP, noreg); + + // call into the runtime to handle illegal instructions exception + // Do not use call_VM_leaf, because we need to make a GC map at this call site. + __ mov(G2_thread, O0); + __ save_thread(L7_thread_cache); + __ call(call_ptr); + __ delayed()->nop(); + + // Set an oopmap for the call site. + // We need this not only for callee-saved registers, but also for volatile + // registers that the compiler might be keeping live across a safepoint. + + oop_maps->add_gc_map( __ offset() - start, map); + + __ restore_thread(L7_thread_cache); + // clear last_Java_sp + __ reset_last_Java_frame(); + + // Check for exceptions + Label pending; + + __ ld_ptr(G2_thread, in_bytes(Thread::pending_exception_offset()), O1); + __ tst(O1); + __ brx(Assembler::notEqual, true, Assembler::pn, pending); + __ delayed()->nop(); + + RegisterSaver::restore_live_registers(masm); + + // We are back the the original state on entry and ready to go. + + __ retl(); + __ delayed()->nop(); + + // Pending exception after the safepoint + + __ bind(pending); + + RegisterSaver::restore_live_registers(masm); + + // We are back the the original state on entry. + + // Tail-call forward_exception_entry, with the issuing PC in O7, + // so it looks like the original nmethod called forward_exception_entry. + __ set((intptr_t)StubRoutines::forward_exception_entry(), O0); + __ JMP(O0, 0); + __ delayed()->nop(); + + // ------------- + // make sure all code is generated + masm->flush(); + + // return exception blob + return SafepointBlob::create(&buffer, oop_maps, frame_size_words); +} + +// +// generate_resolve_blob - call resolution (static/virtual/opt-virtual/ic-miss +// +// Generate a stub that calls into vm to find out the proper destination +// of a java call. All the argument registers are live at this point +// but since this is generic code we don't know what they are and the caller +// must do any gc of the args. +// +static RuntimeStub* generate_resolve_blob(address destination, const char* name) { + assert (StubRoutines::forward_exception_entry() != NULL, "must be generated before"); + + // allocate space for the code + ResourceMark rm; + // setup code generation tools + // Measured 8/7/03 at 896 in 32bit debug build (no VerifyThread) + // Measured 8/7/03 at 1080 in 32bit debug build (VerifyThread) + // even larger with TraceJumps + int pad = TraceJumps ? 512 : 0; + CodeBuffer buffer(name, 1600 + pad, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + int frame_size_words; + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map = NULL; + + int start = __ offset(); + + map = RegisterSaver::save_live_registers(masm, 0, &frame_size_words); + + int frame_complete = __ offset(); + + // setup last_Java_sp (blows G4) + __ set_last_Java_frame(SP, noreg); + + // call into the runtime to handle illegal instructions exception + // Do not use call_VM_leaf, because we need to make a GC map at this call site. + __ mov(G2_thread, O0); + __ save_thread(L7_thread_cache); + __ call(destination, relocInfo::runtime_call_type); + __ delayed()->nop(); + + // O0 contains the address we are going to jump to assuming no exception got installed + + // Set an oopmap for the call site. + // We need this not only for callee-saved registers, but also for volatile + // registers that the compiler might be keeping live across a safepoint. + + oop_maps->add_gc_map( __ offset() - start, map); + + __ restore_thread(L7_thread_cache); + // clear last_Java_sp + __ reset_last_Java_frame(); + + // Check for exceptions + Label pending; + + __ ld_ptr(G2_thread, in_bytes(Thread::pending_exception_offset()), O1); + __ tst(O1); + __ brx(Assembler::notEqual, true, Assembler::pn, pending); + __ delayed()->nop(); + + // get the returned methodOop + + __ get_vm_result(G5_method); + __ stx(G5_method, SP, RegisterSaver::G5_offset()+STACK_BIAS); + + // O0 is where we want to jump, overwrite G3 which is saved and scratch + + __ stx(O0, SP, RegisterSaver::G3_offset()+STACK_BIAS); + + RegisterSaver::restore_live_registers(masm); + + // We are back the the original state on entry and ready to go. + + __ JMP(G3, 0); + __ delayed()->nop(); + + // Pending exception after the safepoint + + __ bind(pending); + + RegisterSaver::restore_live_registers(masm); + + // We are back the the original state on entry. + + // Tail-call forward_exception_entry, with the issuing PC in O7, + // so it looks like the original nmethod called forward_exception_entry. + __ set((intptr_t)StubRoutines::forward_exception_entry(), O0); + __ JMP(O0, 0); + __ delayed()->nop(); + + // ------------- + // make sure all code is generated + masm->flush(); + + // return the blob + // frame_size_words or bytes?? + return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_words, oop_maps, true); +} + +void SharedRuntime::generate_stubs() { + + _wrong_method_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method), + "wrong_method_stub"); + + _ic_miss_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method_ic_miss), + "ic_miss_stub"); + + _resolve_opt_virtual_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_opt_virtual_call_C), + "resolve_opt_virtual_call"); + + _resolve_virtual_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_virtual_call_C), + "resolve_virtual_call"); + + _resolve_static_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_static_call_C), + "resolve_static_call"); + + _polling_page_safepoint_handler_blob = + generate_handler_blob(CAST_FROM_FN_PTR(address, + SafepointSynchronize::handle_polling_page_exception), false); + + _polling_page_return_handler_blob = + generate_handler_blob(CAST_FROM_FN_PTR(address, + SafepointSynchronize::handle_polling_page_exception), true); + + generate_deopt_blob(); + +#ifdef COMPILER2 + generate_uncommon_trap_blob(); +#endif // COMPILER2 +} diff --git a/hotspot/src/cpu/sparc/vm/sparc.ad b/hotspot/src/cpu/sparc/vm/sparc.ad new file mode 100644 index 00000000000..d940498efa5 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/sparc.ad @@ -0,0 +1,8824 @@ +// +// Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// SPARC Architecture Description File + +//----------REGISTER DEFINITION BLOCK------------------------------------------ +// This information is used by the matcher and the register allocator to +// describe individual registers and classes of registers within the target +// archtecture. +register %{ +//----------Architecture Description Register Definitions---------------------- +// General Registers +// "reg_def" name ( register save type, C convention save type, +// ideal register type, encoding, vm name ); +// Register Save Types: +// +// NS = No-Save: The register allocator assumes that these registers +// can be used without saving upon entry to the method, & +// that they do not need to be saved at call sites. +// +// SOC = Save-On-Call: The register allocator assumes that these registers +// can be used without saving upon entry to the method, +// but that they must be saved at call sites. +// +// SOE = Save-On-Entry: The register allocator assumes that these registers +// must be saved before using them upon entry to the +// method, but they do not need to be saved at call +// sites. +// +// AS = Always-Save: The register allocator assumes that these registers +// must be saved before using them upon entry to the +// method, & that they must be saved at call sites. +// +// Ideal Register Type is used to determine how to save & restore a +// register. Op_RegI will get spilled with LoadI/StoreI, Op_RegP will get +// spilled with LoadP/StoreP. If the register supports both, use Op_RegI. +// +// The encoding number is the actual bit-pattern placed into the opcodes. + + +// ---------------------------- +// Integer/Long Registers +// ---------------------------- + +// Need to expose the hi/lo aspect of 64-bit registers +// This register set is used for both the 64-bit build and +// the 32-bit build with 1-register longs. + +// Global Registers 0-7 +reg_def R_G0H( NS, NS, Op_RegI,128, G0->as_VMReg()->next()); +reg_def R_G0 ( NS, NS, Op_RegI, 0, G0->as_VMReg()); +reg_def R_G1H(SOC, SOC, Op_RegI,129, G1->as_VMReg()->next()); +reg_def R_G1 (SOC, SOC, Op_RegI, 1, G1->as_VMReg()); +reg_def R_G2H( NS, NS, Op_RegI,130, G2->as_VMReg()->next()); +reg_def R_G2 ( NS, NS, Op_RegI, 2, G2->as_VMReg()); +reg_def R_G3H(SOC, SOC, Op_RegI,131, G3->as_VMReg()->next()); +reg_def R_G3 (SOC, SOC, Op_RegI, 3, G3->as_VMReg()); +reg_def R_G4H(SOC, SOC, Op_RegI,132, G4->as_VMReg()->next()); +reg_def R_G4 (SOC, SOC, Op_RegI, 4, G4->as_VMReg()); +reg_def R_G5H(SOC, SOC, Op_RegI,133, G5->as_VMReg()->next()); +reg_def R_G5 (SOC, SOC, Op_RegI, 5, G5->as_VMReg()); +reg_def R_G6H( NS, NS, Op_RegI,134, G6->as_VMReg()->next()); +reg_def R_G6 ( NS, NS, Op_RegI, 6, G6->as_VMReg()); +reg_def R_G7H( NS, NS, Op_RegI,135, G7->as_VMReg()->next()); +reg_def R_G7 ( NS, NS, Op_RegI, 7, G7->as_VMReg()); + +// Output Registers 0-7 +reg_def R_O0H(SOC, SOC, Op_RegI,136, O0->as_VMReg()->next()); +reg_def R_O0 (SOC, SOC, Op_RegI, 8, O0->as_VMReg()); +reg_def R_O1H(SOC, SOC, Op_RegI,137, O1->as_VMReg()->next()); +reg_def R_O1 (SOC, SOC, Op_RegI, 9, O1->as_VMReg()); +reg_def R_O2H(SOC, SOC, Op_RegI,138, O2->as_VMReg()->next()); +reg_def R_O2 (SOC, SOC, Op_RegI, 10, O2->as_VMReg()); +reg_def R_O3H(SOC, SOC, Op_RegI,139, O3->as_VMReg()->next()); +reg_def R_O3 (SOC, SOC, Op_RegI, 11, O3->as_VMReg()); +reg_def R_O4H(SOC, SOC, Op_RegI,140, O4->as_VMReg()->next()); +reg_def R_O4 (SOC, SOC, Op_RegI, 12, O4->as_VMReg()); +reg_def R_O5H(SOC, SOC, Op_RegI,141, O5->as_VMReg()->next()); +reg_def R_O5 (SOC, SOC, Op_RegI, 13, O5->as_VMReg()); +reg_def R_SPH( NS, NS, Op_RegI,142, SP->as_VMReg()->next()); +reg_def R_SP ( NS, NS, Op_RegI, 14, SP->as_VMReg()); +reg_def R_O7H(SOC, SOC, Op_RegI,143, O7->as_VMReg()->next()); +reg_def R_O7 (SOC, SOC, Op_RegI, 15, O7->as_VMReg()); + +// Local Registers 0-7 +reg_def R_L0H( NS, NS, Op_RegI,144, L0->as_VMReg()->next()); +reg_def R_L0 ( NS, NS, Op_RegI, 16, L0->as_VMReg()); +reg_def R_L1H( NS, NS, Op_RegI,145, L1->as_VMReg()->next()); +reg_def R_L1 ( NS, NS, Op_RegI, 17, L1->as_VMReg()); +reg_def R_L2H( NS, NS, Op_RegI,146, L2->as_VMReg()->next()); +reg_def R_L2 ( NS, NS, Op_RegI, 18, L2->as_VMReg()); +reg_def R_L3H( NS, NS, Op_RegI,147, L3->as_VMReg()->next()); +reg_def R_L3 ( NS, NS, Op_RegI, 19, L3->as_VMReg()); +reg_def R_L4H( NS, NS, Op_RegI,148, L4->as_VMReg()->next()); +reg_def R_L4 ( NS, NS, Op_RegI, 20, L4->as_VMReg()); +reg_def R_L5H( NS, NS, Op_RegI,149, L5->as_VMReg()->next()); +reg_def R_L5 ( NS, NS, Op_RegI, 21, L5->as_VMReg()); +reg_def R_L6H( NS, NS, Op_RegI,150, L6->as_VMReg()->next()); +reg_def R_L6 ( NS, NS, Op_RegI, 22, L6->as_VMReg()); +reg_def R_L7H( NS, NS, Op_RegI,151, L7->as_VMReg()->next()); +reg_def R_L7 ( NS, NS, Op_RegI, 23, L7->as_VMReg()); + +// Input Registers 0-7 +reg_def R_I0H( NS, NS, Op_RegI,152, I0->as_VMReg()->next()); +reg_def R_I0 ( NS, NS, Op_RegI, 24, I0->as_VMReg()); +reg_def R_I1H( NS, NS, Op_RegI,153, I1->as_VMReg()->next()); +reg_def R_I1 ( NS, NS, Op_RegI, 25, I1->as_VMReg()); +reg_def R_I2H( NS, NS, Op_RegI,154, I2->as_VMReg()->next()); +reg_def R_I2 ( NS, NS, Op_RegI, 26, I2->as_VMReg()); +reg_def R_I3H( NS, NS, Op_RegI,155, I3->as_VMReg()->next()); +reg_def R_I3 ( NS, NS, Op_RegI, 27, I3->as_VMReg()); +reg_def R_I4H( NS, NS, Op_RegI,156, I4->as_VMReg()->next()); +reg_def R_I4 ( NS, NS, Op_RegI, 28, I4->as_VMReg()); +reg_def R_I5H( NS, NS, Op_RegI,157, I5->as_VMReg()->next()); +reg_def R_I5 ( NS, NS, Op_RegI, 29, I5->as_VMReg()); +reg_def R_FPH( NS, NS, Op_RegI,158, FP->as_VMReg()->next()); +reg_def R_FP ( NS, NS, Op_RegI, 30, FP->as_VMReg()); +reg_def R_I7H( NS, NS, Op_RegI,159, I7->as_VMReg()->next()); +reg_def R_I7 ( NS, NS, Op_RegI, 31, I7->as_VMReg()); + +// ---------------------------- +// Float/Double Registers +// ---------------------------- + +// Float Registers +reg_def R_F0 ( SOC, SOC, Op_RegF, 0, F0->as_VMReg()); +reg_def R_F1 ( SOC, SOC, Op_RegF, 1, F1->as_VMReg()); +reg_def R_F2 ( SOC, SOC, Op_RegF, 2, F2->as_VMReg()); +reg_def R_F3 ( SOC, SOC, Op_RegF, 3, F3->as_VMReg()); +reg_def R_F4 ( SOC, SOC, Op_RegF, 4, F4->as_VMReg()); +reg_def R_F5 ( SOC, SOC, Op_RegF, 5, F5->as_VMReg()); +reg_def R_F6 ( SOC, SOC, Op_RegF, 6, F6->as_VMReg()); +reg_def R_F7 ( SOC, SOC, Op_RegF, 7, F7->as_VMReg()); +reg_def R_F8 ( SOC, SOC, Op_RegF, 8, F8->as_VMReg()); +reg_def R_F9 ( SOC, SOC, Op_RegF, 9, F9->as_VMReg()); +reg_def R_F10( SOC, SOC, Op_RegF, 10, F10->as_VMReg()); +reg_def R_F11( SOC, SOC, Op_RegF, 11, F11->as_VMReg()); +reg_def R_F12( SOC, SOC, Op_RegF, 12, F12->as_VMReg()); +reg_def R_F13( SOC, SOC, Op_RegF, 13, F13->as_VMReg()); +reg_def R_F14( SOC, SOC, Op_RegF, 14, F14->as_VMReg()); +reg_def R_F15( SOC, SOC, Op_RegF, 15, F15->as_VMReg()); +reg_def R_F16( SOC, SOC, Op_RegF, 16, F16->as_VMReg()); +reg_def R_F17( SOC, SOC, Op_RegF, 17, F17->as_VMReg()); +reg_def R_F18( SOC, SOC, Op_RegF, 18, F18->as_VMReg()); +reg_def R_F19( SOC, SOC, Op_RegF, 19, F19->as_VMReg()); +reg_def R_F20( SOC, SOC, Op_RegF, 20, F20->as_VMReg()); +reg_def R_F21( SOC, SOC, Op_RegF, 21, F21->as_VMReg()); +reg_def R_F22( SOC, SOC, Op_RegF, 22, F22->as_VMReg()); +reg_def R_F23( SOC, SOC, Op_RegF, 23, F23->as_VMReg()); +reg_def R_F24( SOC, SOC, Op_RegF, 24, F24->as_VMReg()); +reg_def R_F25( SOC, SOC, Op_RegF, 25, F25->as_VMReg()); +reg_def R_F26( SOC, SOC, Op_RegF, 26, F26->as_VMReg()); +reg_def R_F27( SOC, SOC, Op_RegF, 27, F27->as_VMReg()); +reg_def R_F28( SOC, SOC, Op_RegF, 28, F28->as_VMReg()); +reg_def R_F29( SOC, SOC, Op_RegF, 29, F29->as_VMReg()); +reg_def R_F30( SOC, SOC, Op_RegF, 30, F30->as_VMReg()); +reg_def R_F31( SOC, SOC, Op_RegF, 31, F31->as_VMReg()); + +// Double Registers +// The rules of ADL require that double registers be defined in pairs. +// Each pair must be two 32-bit values, but not necessarily a pair of +// single float registers. In each pair, ADLC-assigned register numbers +// must be adjacent, with the lower number even. Finally, when the +// CPU stores such a register pair to memory, the word associated with +// the lower ADLC-assigned number must be stored to the lower address. + +// These definitions specify the actual bit encodings of the sparc +// double fp register numbers. FloatRegisterImpl in register_sparc.hpp +// wants 0-63, so we have to convert every time we want to use fp regs +// with the macroassembler, using reg_to_DoubleFloatRegister_object(). +// 255 is a flag meaning 'dont go here'. +// I believe we can't handle callee-save doubles D32 and up until +// the place in the sparc stack crawler that asserts on the 255 is +// fixed up. +reg_def R_D32x(SOC, SOC, Op_RegD,255, F32->as_VMReg()); +reg_def R_D32 (SOC, SOC, Op_RegD, 1, F32->as_VMReg()->next()); +reg_def R_D34x(SOC, SOC, Op_RegD,255, F34->as_VMReg()); +reg_def R_D34 (SOC, SOC, Op_RegD, 3, F34->as_VMReg()->next()); +reg_def R_D36x(SOC, SOC, Op_RegD,255, F36->as_VMReg()); +reg_def R_D36 (SOC, SOC, Op_RegD, 5, F36->as_VMReg()->next()); +reg_def R_D38x(SOC, SOC, Op_RegD,255, F38->as_VMReg()); +reg_def R_D38 (SOC, SOC, Op_RegD, 7, F38->as_VMReg()->next()); +reg_def R_D40x(SOC, SOC, Op_RegD,255, F40->as_VMReg()); +reg_def R_D40 (SOC, SOC, Op_RegD, 9, F40->as_VMReg()->next()); +reg_def R_D42x(SOC, SOC, Op_RegD,255, F42->as_VMReg()); +reg_def R_D42 (SOC, SOC, Op_RegD, 11, F42->as_VMReg()->next()); +reg_def R_D44x(SOC, SOC, Op_RegD,255, F44->as_VMReg()); +reg_def R_D44 (SOC, SOC, Op_RegD, 13, F44->as_VMReg()->next()); +reg_def R_D46x(SOC, SOC, Op_RegD,255, F46->as_VMReg()); +reg_def R_D46 (SOC, SOC, Op_RegD, 15, F46->as_VMReg()->next()); +reg_def R_D48x(SOC, SOC, Op_RegD,255, F48->as_VMReg()); +reg_def R_D48 (SOC, SOC, Op_RegD, 17, F48->as_VMReg()->next()); +reg_def R_D50x(SOC, SOC, Op_RegD,255, F50->as_VMReg()); +reg_def R_D50 (SOC, SOC, Op_RegD, 19, F50->as_VMReg()->next()); +reg_def R_D52x(SOC, SOC, Op_RegD,255, F52->as_VMReg()); +reg_def R_D52 (SOC, SOC, Op_RegD, 21, F52->as_VMReg()->next()); +reg_def R_D54x(SOC, SOC, Op_RegD,255, F54->as_VMReg()); +reg_def R_D54 (SOC, SOC, Op_RegD, 23, F54->as_VMReg()->next()); +reg_def R_D56x(SOC, SOC, Op_RegD,255, F56->as_VMReg()); +reg_def R_D56 (SOC, SOC, Op_RegD, 25, F56->as_VMReg()->next()); +reg_def R_D58x(SOC, SOC, Op_RegD,255, F58->as_VMReg()); +reg_def R_D58 (SOC, SOC, Op_RegD, 27, F58->as_VMReg()->next()); +reg_def R_D60x(SOC, SOC, Op_RegD,255, F60->as_VMReg()); +reg_def R_D60 (SOC, SOC, Op_RegD, 29, F60->as_VMReg()->next()); +reg_def R_D62x(SOC, SOC, Op_RegD,255, F62->as_VMReg()); +reg_def R_D62 (SOC, SOC, Op_RegD, 31, F62->as_VMReg()->next()); + + +// ---------------------------- +// Special Registers +// Condition Codes Flag Registers +// I tried to break out ICC and XCC but it's not very pretty. +// Every Sparc instruction which defs/kills one also kills the other. +// Hence every compare instruction which defs one kind of flags ends +// up needing a kill of the other. +reg_def CCR (SOC, SOC, Op_RegFlags, 0, VMRegImpl::Bad()); + +reg_def FCC0(SOC, SOC, Op_RegFlags, 0, VMRegImpl::Bad()); +reg_def FCC1(SOC, SOC, Op_RegFlags, 1, VMRegImpl::Bad()); +reg_def FCC2(SOC, SOC, Op_RegFlags, 2, VMRegImpl::Bad()); +reg_def FCC3(SOC, SOC, Op_RegFlags, 3, VMRegImpl::Bad()); + +// ---------------------------- +// Specify the enum values for the registers. These enums are only used by the +// OptoReg "class". We can convert these enum values at will to VMReg when needed +// for visibility to the rest of the vm. The order of this enum influences the +// register allocator so having the freedom to set this order and not be stuck +// with the order that is natural for the rest of the vm is worth it. +alloc_class chunk0( + R_L0,R_L0H, R_L1,R_L1H, R_L2,R_L2H, R_L3,R_L3H, R_L4,R_L4H, R_L5,R_L5H, R_L6,R_L6H, R_L7,R_L7H, + R_G0,R_G0H, R_G1,R_G1H, R_G2,R_G2H, R_G3,R_G3H, R_G4,R_G4H, R_G5,R_G5H, R_G6,R_G6H, R_G7,R_G7H, + R_O7,R_O7H, R_SP,R_SPH, R_O0,R_O0H, R_O1,R_O1H, R_O2,R_O2H, R_O3,R_O3H, R_O4,R_O4H, R_O5,R_O5H, + R_I0,R_I0H, R_I1,R_I1H, R_I2,R_I2H, R_I3,R_I3H, R_I4,R_I4H, R_I5,R_I5H, R_FP,R_FPH, R_I7,R_I7H); + +// Note that a register is not allocatable unless it is also mentioned +// in a widely-used reg_class below. Thus, R_G7 and R_G0 are outside i_reg. + +alloc_class chunk1( + // The first registers listed here are those most likely to be used + // as temporaries. We move F0..F7 away from the front of the list, + // to reduce the likelihood of interferences with parameters and + // return values. Likewise, we avoid using F0/F1 for parameters, + // since they are used for return values. + // This FPU fine-tuning is worth about 1% on the SPEC geomean. + R_F8 ,R_F9 ,R_F10,R_F11,R_F12,R_F13,R_F14,R_F15, + R_F16,R_F17,R_F18,R_F19,R_F20,R_F21,R_F22,R_F23, + R_F24,R_F25,R_F26,R_F27,R_F28,R_F29,R_F30,R_F31, + R_F0 ,R_F1 ,R_F2 ,R_F3 ,R_F4 ,R_F5 ,R_F6 ,R_F7 , // used for arguments and return values + R_D32,R_D32x,R_D34,R_D34x,R_D36,R_D36x,R_D38,R_D38x, + R_D40,R_D40x,R_D42,R_D42x,R_D44,R_D44x,R_D46,R_D46x, + R_D48,R_D48x,R_D50,R_D50x,R_D52,R_D52x,R_D54,R_D54x, + R_D56,R_D56x,R_D58,R_D58x,R_D60,R_D60x,R_D62,R_D62x); + +alloc_class chunk2(CCR, FCC0, FCC1, FCC2, FCC3); + +//----------Architecture Description Register Classes-------------------------- +// Several register classes are automatically defined based upon information in +// this architecture description. +// 1) reg_class inline_cache_reg ( as defined in frame section ) +// 2) reg_class interpreter_method_oop_reg ( as defined in frame section ) +// 3) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) +// + +// G0 is not included in integer class since it has special meaning. +reg_class g0_reg(R_G0); + +// ---------------------------- +// Integer Register Classes +// ---------------------------- +// Exclusions from i_reg: +// R_G0: hardwired zero +// R_G2: reserved by HotSpot to the TLS register (invariant within Java) +// R_G6: reserved by Solaris ABI to tools +// R_G7: reserved by Solaris ABI to libthread +// R_O7: Used as a temp in many encodings +reg_class int_reg(R_G1,R_G3,R_G4,R_G5,R_O0,R_O1,R_O2,R_O3,R_O4,R_O5,R_L0,R_L1,R_L2,R_L3,R_L4,R_L5,R_L6,R_L7,R_I0,R_I1,R_I2,R_I3,R_I4,R_I5); + +// Class for all integer registers, except the G registers. This is used for +// encodings which use G registers as temps. The regular inputs to such +// instructions use a "notemp_" prefix, as a hack to ensure that the allocator +// will not put an input into a temp register. +reg_class notemp_int_reg(R_O0,R_O1,R_O2,R_O3,R_O4,R_O5,R_L0,R_L1,R_L2,R_L3,R_L4,R_L5,R_L6,R_L7,R_I0,R_I1,R_I2,R_I3,R_I4,R_I5); + +reg_class g1_regI(R_G1); +reg_class g3_regI(R_G3); +reg_class g4_regI(R_G4); +reg_class o0_regI(R_O0); +reg_class o7_regI(R_O7); + +// ---------------------------- +// Pointer Register Classes +// ---------------------------- +#ifdef _LP64 +// 64-bit build means 64-bit pointers means hi/lo pairs +reg_class ptr_reg( R_G1H,R_G1, R_G3H,R_G3, R_G4H,R_G4, R_G5H,R_G5, + R_O0H,R_O0, R_O1H,R_O1, R_O2H,R_O2, R_O3H,R_O3, R_O4H,R_O4, R_O5H,R_O5, + R_L0H,R_L0, R_L1H,R_L1, R_L2H,R_L2, R_L3H,R_L3, R_L4H,R_L4, R_L5H,R_L5, R_L6H,R_L6, R_L7H,R_L7, + R_I0H,R_I0, R_I1H,R_I1, R_I2H,R_I2, R_I3H,R_I3, R_I4H,R_I4, R_I5H,R_I5 ); +// Lock encodings use G3 and G4 internally +reg_class lock_ptr_reg( R_G1H,R_G1, R_G5H,R_G5, + R_O0H,R_O0, R_O1H,R_O1, R_O2H,R_O2, R_O3H,R_O3, R_O4H,R_O4, R_O5H,R_O5, + R_L0H,R_L0, R_L1H,R_L1, R_L2H,R_L2, R_L3H,R_L3, R_L4H,R_L4, R_L5H,R_L5, R_L6H,R_L6, R_L7H,R_L7, + R_I0H,R_I0, R_I1H,R_I1, R_I2H,R_I2, R_I3H,R_I3, R_I4H,R_I4, R_I5H,R_I5 ); +// Special class for storeP instructions, which can store SP or RPC to TLS. +// It is also used for memory addressing, allowing direct TLS addressing. +reg_class sp_ptr_reg( R_G1H,R_G1, R_G2H,R_G2, R_G3H,R_G3, R_G4H,R_G4, R_G5H,R_G5, + R_O0H,R_O0, R_O1H,R_O1, R_O2H,R_O2, R_O3H,R_O3, R_O4H,R_O4, R_O5H,R_O5, R_SPH,R_SP, + R_L0H,R_L0, R_L1H,R_L1, R_L2H,R_L2, R_L3H,R_L3, R_L4H,R_L4, R_L5H,R_L5, R_L6H,R_L6, R_L7H,R_L7, + R_I0H,R_I0, R_I1H,R_I1, R_I2H,R_I2, R_I3H,R_I3, R_I4H,R_I4, R_I5H,R_I5, R_FPH,R_FP ); +// R_L7 is the lowest-priority callee-save (i.e., NS) register +// We use it to save R_G2 across calls out of Java. +reg_class l7_regP(R_L7H,R_L7); + +// Other special pointer regs +reg_class g1_regP(R_G1H,R_G1); +reg_class g2_regP(R_G2H,R_G2); +reg_class g3_regP(R_G3H,R_G3); +reg_class g4_regP(R_G4H,R_G4); +reg_class g5_regP(R_G5H,R_G5); +reg_class i0_regP(R_I0H,R_I0); +reg_class o0_regP(R_O0H,R_O0); +reg_class o1_regP(R_O1H,R_O1); +reg_class o2_regP(R_O2H,R_O2); +reg_class o7_regP(R_O7H,R_O7); + +#else // _LP64 +// 32-bit build means 32-bit pointers means 1 register. +reg_class ptr_reg( R_G1, R_G3,R_G4,R_G5, + R_O0,R_O1,R_O2,R_O3,R_O4,R_O5, + R_L0,R_L1,R_L2,R_L3,R_L4,R_L5,R_L6,R_L7, + R_I0,R_I1,R_I2,R_I3,R_I4,R_I5); +// Lock encodings use G3 and G4 internally +reg_class lock_ptr_reg(R_G1, R_G5, + R_O0,R_O1,R_O2,R_O3,R_O4,R_O5, + R_L0,R_L1,R_L2,R_L3,R_L4,R_L5,R_L6,R_L7, + R_I0,R_I1,R_I2,R_I3,R_I4,R_I5); +// Special class for storeP instructions, which can store SP or RPC to TLS. +// It is also used for memory addressing, allowing direct TLS addressing. +reg_class sp_ptr_reg( R_G1,R_G2,R_G3,R_G4,R_G5, + R_O0,R_O1,R_O2,R_O3,R_O4,R_O5,R_SP, + R_L0,R_L1,R_L2,R_L3,R_L4,R_L5,R_L6,R_L7, + R_I0,R_I1,R_I2,R_I3,R_I4,R_I5,R_FP); +// R_L7 is the lowest-priority callee-save (i.e., NS) register +// We use it to save R_G2 across calls out of Java. +reg_class l7_regP(R_L7); + +// Other special pointer regs +reg_class g1_regP(R_G1); +reg_class g2_regP(R_G2); +reg_class g3_regP(R_G3); +reg_class g4_regP(R_G4); +reg_class g5_regP(R_G5); +reg_class i0_regP(R_I0); +reg_class o0_regP(R_O0); +reg_class o1_regP(R_O1); +reg_class o2_regP(R_O2); +reg_class o7_regP(R_O7); +#endif // _LP64 + + +// ---------------------------- +// Long Register Classes +// ---------------------------- +// Longs in 1 register. Aligned adjacent hi/lo pairs. +// Note: O7 is never in this class; it is sometimes used as an encoding temp. +reg_class long_reg( R_G1H,R_G1, R_G3H,R_G3, R_G4H,R_G4, R_G5H,R_G5 + ,R_O0H,R_O0, R_O1H,R_O1, R_O2H,R_O2, R_O3H,R_O3, R_O4H,R_O4, R_O5H,R_O5 +#ifdef _LP64 +// 64-bit, longs in 1 register: use all 64-bit integer registers +// 32-bit, longs in 1 register: cannot use I's and L's. Restrict to O's and G's. + ,R_L0H,R_L0, R_L1H,R_L1, R_L2H,R_L2, R_L3H,R_L3, R_L4H,R_L4, R_L5H,R_L5, R_L6H,R_L6, R_L7H,R_L7 + ,R_I0H,R_I0, R_I1H,R_I1, R_I2H,R_I2, R_I3H,R_I3, R_I4H,R_I4, R_I5H,R_I5 +#endif // _LP64 + ); + +reg_class g1_regL(R_G1H,R_G1); +reg_class o2_regL(R_O2H,R_O2); +reg_class o7_regL(R_O7H,R_O7); + +// ---------------------------- +// Special Class for Condition Code Flags Register +reg_class int_flags(CCR); +reg_class float_flags(FCC0,FCC1,FCC2,FCC3); +reg_class float_flag0(FCC0); + + +// ---------------------------- +// Float Point Register Classes +// ---------------------------- +// Skip F30/F31, they are reserved for mem-mem copies +reg_class sflt_reg(R_F0,R_F1,R_F2,R_F3,R_F4,R_F5,R_F6,R_F7,R_F8,R_F9,R_F10,R_F11,R_F12,R_F13,R_F14,R_F15,R_F16,R_F17,R_F18,R_F19,R_F20,R_F21,R_F22,R_F23,R_F24,R_F25,R_F26,R_F27,R_F28,R_F29); + +// Paired floating point registers--they show up in the same order as the floats, +// but they are used with the "Op_RegD" type, and always occur in even/odd pairs. +reg_class dflt_reg(R_F0, R_F1, R_F2, R_F3, R_F4, R_F5, R_F6, R_F7, R_F8, R_F9, R_F10,R_F11,R_F12,R_F13,R_F14,R_F15, + R_F16,R_F17,R_F18,R_F19,R_F20,R_F21,R_F22,R_F23,R_F24,R_F25,R_F26,R_F27,R_F28,R_F29, + /* Use extra V9 double registers; this AD file does not support V8 */ + R_D32,R_D32x,R_D34,R_D34x,R_D36,R_D36x,R_D38,R_D38x,R_D40,R_D40x,R_D42,R_D42x,R_D44,R_D44x,R_D46,R_D46x, + R_D48,R_D48x,R_D50,R_D50x,R_D52,R_D52x,R_D54,R_D54x,R_D56,R_D56x,R_D58,R_D58x,R_D60,R_D60x,R_D62,R_D62x + ); + +// Paired floating point registers--they show up in the same order as the floats, +// but they are used with the "Op_RegD" type, and always occur in even/odd pairs. +// This class is usable for mis-aligned loads as happen in I2C adapters. +reg_class dflt_low_reg(R_F0, R_F1, R_F2, R_F3, R_F4, R_F5, R_F6, R_F7, R_F8, R_F9, R_F10,R_F11,R_F12,R_F13,R_F14,R_F15, + R_F16,R_F17,R_F18,R_F19,R_F20,R_F21,R_F22,R_F23,R_F24,R_F25,R_F26,R_F27,R_F28,R_F29,R_F30,R_F31 ); +%} + +//----------DEFINITION BLOCK--------------------------------------------------- +// Define name --> value mappings to inform the ADLC of an integer valued name +// Current support includes integer values in the range [0, 0x7FFFFFFF] +// Format: +// int_def ( , ); +// Generated Code in ad_.hpp +// #define () +// // value == +// Generated code in ad_.cpp adlc_verification() +// assert( == , "Expect () to equal "); +// +definitions %{ +// The default cost (of an ALU instruction). + int_def DEFAULT_COST ( 100, 100); + int_def HUGE_COST (1000000, 1000000); + +// Memory refs are twice as expensive as run-of-the-mill. + int_def MEMORY_REF_COST ( 200, DEFAULT_COST * 2); + +// Branches are even more expensive. + int_def BRANCH_COST ( 300, DEFAULT_COST * 3); + int_def CALL_COST ( 300, DEFAULT_COST * 3); +%} + + +//----------SOURCE BLOCK------------------------------------------------------- +// This is a block of C++ code which provides values, functions, and +// definitions necessary in the rest of the architecture description +source_hpp %{ +// Must be visible to the DFA in dfa_sparc.cpp +extern bool can_branch_register( Node *bol, Node *cmp ); + +// Macros to extract hi & lo halves from a long pair. +// G0 is not part of any long pair, so assert on that. +// Prevents accidently using G1 instead of G0. +#define LONG_HI_REG(x) (x) +#define LONG_LO_REG(x) (x) + +%} + +source %{ +#define __ _masm. + +// tertiary op of a LoadP or StoreP encoding +#define REGP_OP true + +static FloatRegister reg_to_SingleFloatRegister_object(int register_encoding); +static FloatRegister reg_to_DoubleFloatRegister_object(int register_encoding); +static Register reg_to_register_object(int register_encoding); + +// Used by the DFA in dfa_sparc.cpp. +// Check for being able to use a V9 branch-on-register. Requires a +// compare-vs-zero, equal/not-equal, of a value which was zero- or sign- +// extended. Doesn't work following an integer ADD, for example, because of +// overflow (-1 incremented yields 0 plus a carry in the high-order word). On +// 32-bit V9 systems, interrupts currently blow away the high-order 32 bits and +// replace them with zero, which could become sign-extension in a different OS +// release. There's no obvious reason why an interrupt will ever fill these +// bits with non-zero junk (the registers are reloaded with standard LD +// instructions which either zero-fill or sign-fill). +bool can_branch_register( Node *bol, Node *cmp ) { + if( !BranchOnRegister ) return false; +#ifdef _LP64 + if( cmp->Opcode() == Op_CmpP ) + return true; // No problems with pointer compares +#endif + if( cmp->Opcode() == Op_CmpL ) + return true; // No problems with long compares + + if( !SparcV9RegsHiBitsZero ) return false; + if( bol->as_Bool()->_test._test != BoolTest::ne && + bol->as_Bool()->_test._test != BoolTest::eq ) + return false; + + // Check for comparing against a 'safe' value. Any operation which + // clears out the high word is safe. Thus, loads and certain shifts + // are safe, as are non-negative constants. Any operation which + // preserves zero bits in the high word is safe as long as each of its + // inputs are safe. Thus, phis and bitwise booleans are safe if their + // inputs are safe. At present, the only important case to recognize + // seems to be loads. Constants should fold away, and shifts & + // logicals can use the 'cc' forms. + Node *x = cmp->in(1); + if( x->is_Load() ) return true; + if( x->is_Phi() ) { + for( uint i = 1; i < x->req(); i++ ) + if( !x->in(i)->is_Load() ) + return false; + return true; + } + return false; +} + +// **************************************************************************** + +// REQUIRED FUNCTIONALITY + +// !!!!! Special hack to get all type of calls to specify the byte offset +// from the start of the call to the point where the return address +// will point. +// The "return address" is the address of the call instruction, plus 8. + +int MachCallStaticJavaNode::ret_addr_offset() { + return NativeCall::instruction_size; // call; delay slot +} + +int MachCallDynamicJavaNode::ret_addr_offset() { + int vtable_index = this->_vtable_index; + if (vtable_index < 0) { + // must be invalid_vtable_index, not nonvirtual_vtable_index + assert(vtable_index == methodOopDesc::invalid_vtable_index, "correct sentinel value"); + return (NativeMovConstReg::instruction_size + + NativeCall::instruction_size); // sethi; setlo; call; delay slot + } else { + assert(!UseInlineCaches, "expect vtable calls only if not using ICs"); + int entry_offset = instanceKlass::vtable_start_offset() + vtable_index*vtableEntry::size(); + int v_off = entry_offset*wordSize + vtableEntry::method_offset_in_bytes(); + if( Assembler::is_simm13(v_off) ) { + return (3*BytesPerInstWord + // ld_ptr, ld_ptr, ld_ptr + NativeCall::instruction_size); // call; delay slot + } else { + return (5*BytesPerInstWord + // ld_ptr, set_hi, set, ld_ptr, ld_ptr + NativeCall::instruction_size); // call; delay slot + } + } +} + +int MachCallRuntimeNode::ret_addr_offset() { +#ifdef _LP64 + return NativeFarCall::instruction_size; // farcall; delay slot +#else + return NativeCall::instruction_size; // call; delay slot +#endif +} + +// Indicate if the safepoint node needs the polling page as an input. +// Since Sparc does not have absolute addressing, it does. +bool SafePointNode::needs_polling_address_input() { + return true; +} + +// emit an interrupt that is caught by the debugger (for debugging compiler) +void emit_break(CodeBuffer &cbuf) { + MacroAssembler _masm(&cbuf); + __ breakpoint_trap(); +} + +#ifndef PRODUCT +void MachBreakpointNode::format( PhaseRegAlloc *, outputStream *st ) const { + st->print("TA"); +} +#endif + +void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + emit_break(cbuf); +} + +uint MachBreakpointNode::size(PhaseRegAlloc *ra_) const { + return MachNode::size(ra_); +} + +// Traceable jump +void emit_jmpl(CodeBuffer &cbuf, int jump_target) { + MacroAssembler _masm(&cbuf); + Register rdest = reg_to_register_object(jump_target); + __ JMP(rdest, 0); + __ delayed()->nop(); +} + +// Traceable jump and set exception pc +void emit_jmpl_set_exception_pc(CodeBuffer &cbuf, int jump_target) { + MacroAssembler _masm(&cbuf); + Register rdest = reg_to_register_object(jump_target); + __ JMP(rdest, 0); + __ delayed()->add(O7, frame::pc_return_offset, Oissuing_pc ); +} + +void emit_nop(CodeBuffer &cbuf) { + MacroAssembler _masm(&cbuf); + __ nop(); +} + +void emit_illtrap(CodeBuffer &cbuf) { + MacroAssembler _masm(&cbuf); + __ illtrap(0); +} + + +intptr_t get_offset_from_base(const MachNode* n, const TypePtr* atype, int disp32) { + assert(n->rule() != loadUB_rule, ""); + + intptr_t offset = 0; + const TypePtr *adr_type = TYPE_PTR_SENTINAL; // Check for base==RegI, disp==immP + const Node* addr = n->get_base_and_disp(offset, adr_type); + assert(adr_type == (const TypePtr*)-1, "VerifyOops: no support for sparc operands with base==RegI, disp==immP"); + assert(addr != NULL && addr != (Node*)-1, "invalid addr"); + assert(addr->bottom_type()->isa_oopptr() == atype, ""); + atype = atype->add_offset(offset); + assert(disp32 == offset, "wrong disp32"); + return atype->_offset; +} + + +intptr_t get_offset_from_base_2(const MachNode* n, const TypePtr* atype, int disp32) { + assert(n->rule() != loadUB_rule, ""); + + intptr_t offset = 0; + Node* addr = n->in(2); + assert(addr->bottom_type()->isa_oopptr() == atype, ""); + if (addr->is_Mach() && addr->as_Mach()->ideal_Opcode() == Op_AddP) { + Node* a = addr->in(2/*AddPNode::Address*/); + Node* o = addr->in(3/*AddPNode::Offset*/); + offset = o->is_Con() ? o->bottom_type()->is_intptr_t()->get_con() : Type::OffsetBot; + atype = a->bottom_type()->is_ptr()->add_offset(offset); + assert(atype->isa_oop_ptr(), "still an oop"); + } + offset = atype->is_ptr()->_offset; + if (offset != Type::OffsetBot) offset += disp32; + return offset; +} + +// Standard Sparc opcode form2 field breakdown +static inline void emit2_19(CodeBuffer &cbuf, int f30, int f29, int f25, int f22, int f20, int f19, int f0 ) { + f0 &= (1<<19)-1; // Mask displacement to 19 bits + int op = (f30 << 30) | + (f29 << 29) | + (f25 << 25) | + (f22 << 22) | + (f20 << 20) | + (f19 << 19) | + (f0 << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); +} + +// Standard Sparc opcode form2 field breakdown +static inline void emit2_22(CodeBuffer &cbuf, int f30, int f25, int f22, int f0 ) { + f0 >>= 10; // Drop 10 bits + f0 &= (1<<22)-1; // Mask displacement to 22 bits + int op = (f30 << 30) | + (f25 << 25) | + (f22 << 22) | + (f0 << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); +} + +// Standard Sparc opcode form3 field breakdown +static inline void emit3(CodeBuffer &cbuf, int f30, int f25, int f19, int f14, int f5, int f0 ) { + int op = (f30 << 30) | + (f25 << 25) | + (f19 << 19) | + (f14 << 14) | + (f5 << 5) | + (f0 << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); +} + +// Standard Sparc opcode form3 field breakdown +static inline void emit3_simm13(CodeBuffer &cbuf, int f30, int f25, int f19, int f14, int simm13 ) { + simm13 &= (1<<13)-1; // Mask to 13 bits + int op = (f30 << 30) | + (f25 << 25) | + (f19 << 19) | + (f14 << 14) | + (1 << 13) | // bit to indicate immediate-mode + (simm13<<0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); +} + +static inline void emit3_simm10(CodeBuffer &cbuf, int f30, int f25, int f19, int f14, int simm10 ) { + simm10 &= (1<<10)-1; // Mask to 10 bits + emit3_simm13(cbuf,f30,f25,f19,f14,simm10); +} + +#ifdef ASSERT +// Helper function for VerifyOops in emit_form3_mem_reg +void verify_oops_warning(const MachNode *n, int ideal_op, int mem_op) { + warning("VerifyOops encountered unexpected instruction:"); + n->dump(2); + warning("Instruction has ideal_Opcode==Op_%s and op_ld==Op_%s \n", NodeClassNames[ideal_op], NodeClassNames[mem_op]); +} +#endif + + +void emit_form3_mem_reg(CodeBuffer &cbuf, const MachNode* n, int primary, int tertiary, + int src1_enc, int disp32, int src2_enc, int dst_enc) { + +#ifdef ASSERT + // The following code implements the +VerifyOops feature. + // It verifies oop values which are loaded into or stored out of + // the current method activation. +VerifyOops complements techniques + // like ScavengeALot, because it eagerly inspects oops in transit, + // as they enter or leave the stack, as opposed to ScavengeALot, + // which inspects oops "at rest", in the stack or heap, at safepoints. + // For this reason, +VerifyOops can sometimes detect bugs very close + // to their point of creation. It can also serve as a cross-check + // on the validity of oop maps, when used toegether with ScavengeALot. + + // It would be good to verify oops at other points, especially + // when an oop is used as a base pointer for a load or store. + // This is presently difficult, because it is hard to know when + // a base address is biased or not. (If we had such information, + // it would be easy and useful to make a two-argument version of + // verify_oop which unbiases the base, and performs verification.) + + assert((uint)tertiary == 0xFFFFFFFF || tertiary == REGP_OP, "valid tertiary"); + bool is_verified_oop_base = false; + bool is_verified_oop_load = false; + bool is_verified_oop_store = false; + int tmp_enc = -1; + if (VerifyOops && src1_enc != R_SP_enc) { + // classify the op, mainly for an assert check + int st_op = 0, ld_op = 0; + switch (primary) { + case Assembler::stb_op3: st_op = Op_StoreB; break; + case Assembler::sth_op3: st_op = Op_StoreC; break; + case Assembler::stx_op3: // may become StoreP or stay StoreI or StoreD0 + case Assembler::stw_op3: st_op = Op_StoreI; break; + case Assembler::std_op3: st_op = Op_StoreL; break; + case Assembler::stf_op3: st_op = Op_StoreF; break; + case Assembler::stdf_op3: st_op = Op_StoreD; break; + + case Assembler::ldsb_op3: ld_op = Op_LoadB; break; + case Assembler::lduh_op3: ld_op = Op_LoadC; break; + case Assembler::ldsh_op3: ld_op = Op_LoadS; break; + case Assembler::ldx_op3: // may become LoadP or stay LoadI + case Assembler::ldsw_op3: // may become LoadP or stay LoadI + case Assembler::lduw_op3: ld_op = Op_LoadI; break; + case Assembler::ldd_op3: ld_op = Op_LoadL; break; + case Assembler::ldf_op3: ld_op = Op_LoadF; break; + case Assembler::lddf_op3: ld_op = Op_LoadD; break; + case Assembler::ldub_op3: ld_op = Op_LoadB; break; + case Assembler::prefetch_op3: ld_op = Op_LoadI; break; + + default: ShouldNotReachHere(); + } + if (tertiary == REGP_OP) { + if (st_op == Op_StoreI) st_op = Op_StoreP; + else if (ld_op == Op_LoadI) ld_op = Op_LoadP; + else ShouldNotReachHere(); + if (st_op) { + // a store + // inputs are (0:control, 1:memory, 2:address, 3:value) + Node* n2 = n->in(3); + if (n2 != NULL) { + const Type* t = n2->bottom_type(); + is_verified_oop_store = t->isa_oop_ptr() ? (t->is_ptr()->_offset==0) : false; + } + } else { + // a load + const Type* t = n->bottom_type(); + is_verified_oop_load = t->isa_oop_ptr() ? (t->is_ptr()->_offset==0) : false; + } + } + + if (ld_op) { + // a Load + // inputs are (0:control, 1:memory, 2:address) + if (!(n->ideal_Opcode()==ld_op) && // Following are special cases + !(n->ideal_Opcode()==Op_LoadLLocked && ld_op==Op_LoadI) && + !(n->ideal_Opcode()==Op_LoadPLocked && ld_op==Op_LoadP) && + !(n->ideal_Opcode()==Op_LoadI && ld_op==Op_LoadF) && + !(n->ideal_Opcode()==Op_LoadF && ld_op==Op_LoadI) && + !(n->ideal_Opcode()==Op_LoadRange && ld_op==Op_LoadI) && + !(n->ideal_Opcode()==Op_LoadKlass && ld_op==Op_LoadP) && + !(n->ideal_Opcode()==Op_LoadL && ld_op==Op_LoadI) && + !(n->ideal_Opcode()==Op_LoadL_unaligned && ld_op==Op_LoadI) && + !(n->ideal_Opcode()==Op_LoadD_unaligned && ld_op==Op_LoadF) && + !(n->ideal_Opcode()==Op_ConvI2F && ld_op==Op_LoadF) && + !(n->ideal_Opcode()==Op_ConvI2D && ld_op==Op_LoadF) && + !(n->ideal_Opcode()==Op_PrefetchRead && ld_op==Op_LoadI) && + !(n->ideal_Opcode()==Op_PrefetchWrite && ld_op==Op_LoadI) && + !(n->rule() == loadUB_rule)) { + verify_oops_warning(n, n->ideal_Opcode(), ld_op); + } + } else if (st_op) { + // a Store + // inputs are (0:control, 1:memory, 2:address, 3:value) + if (!(n->ideal_Opcode()==st_op) && // Following are special cases + !(n->ideal_Opcode()==Op_StoreCM && st_op==Op_StoreB) && + !(n->ideal_Opcode()==Op_StoreI && st_op==Op_StoreF) && + !(n->ideal_Opcode()==Op_StoreF && st_op==Op_StoreI) && + !(n->ideal_Opcode()==Op_StoreL && st_op==Op_StoreI) && + !(n->ideal_Opcode()==Op_StoreD && st_op==Op_StoreI && n->rule() == storeD0_rule)) { + verify_oops_warning(n, n->ideal_Opcode(), st_op); + } + } + + if (src2_enc == R_G0_enc && n->rule() != loadUB_rule && n->ideal_Opcode() != Op_StoreCM ) { + Node* addr = n->in(2); + if (!(addr->is_Mach() && addr->as_Mach()->ideal_Opcode() == Op_AddP)) { + const TypeOopPtr* atype = addr->bottom_type()->isa_instptr(); // %%% oopptr? + if (atype != NULL) { + intptr_t offset = get_offset_from_base(n, atype, disp32); + intptr_t offset_2 = get_offset_from_base_2(n, atype, disp32); + if (offset != offset_2) { + get_offset_from_base(n, atype, disp32); + get_offset_from_base_2(n, atype, disp32); + } + assert(offset == offset_2, "different offsets"); + if (offset == disp32) { + // we now know that src1 is a true oop pointer + is_verified_oop_base = true; + if (ld_op && src1_enc == dst_enc && ld_op != Op_LoadF && ld_op != Op_LoadD) { + if( primary == Assembler::ldd_op3 ) { + is_verified_oop_base = false; // Cannot 'ldd' into O7 + } else { + tmp_enc = dst_enc; + dst_enc = R_O7_enc; // Load into O7; preserve source oop + assert(src1_enc != dst_enc, ""); + } + } + } + if (st_op && (( offset == oopDesc::klass_offset_in_bytes()) + || offset == oopDesc::mark_offset_in_bytes())) { + // loading the mark should not be allowed either, but + // we don't check this since it conflicts with InlineObjectHash + // usage of LoadINode to get the mark. We could keep the + // check if we create a new LoadMarkNode + // but do not verify the object before its header is initialized + ShouldNotReachHere(); + } + } + } + } + } +#endif + + uint instr; + instr = (Assembler::ldst_op << 30) + | (dst_enc << 25) + | (primary << 19) + | (src1_enc << 14); + + uint index = src2_enc; + int disp = disp32; + + if (src1_enc == R_SP_enc || src1_enc == R_FP_enc) + disp += STACK_BIAS; + + // We should have a compiler bailout here rather than a guarantee. + // Better yet would be some mechanism to handle variable-size matches correctly. + guarantee(Assembler::is_simm13(disp), "Do not match large constant offsets" ); + + if( disp == 0 ) { + // use reg-reg form + // bit 13 is already zero + instr |= index; + } else { + // use reg-imm form + instr |= 0x00002000; // set bit 13 to one + instr |= disp & 0x1FFF; + } + + uint *code = (uint*)cbuf.code_end(); + *code = instr; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + +#ifdef ASSERT + { + MacroAssembler _masm(&cbuf); + if (is_verified_oop_base) { + __ verify_oop(reg_to_register_object(src1_enc)); + } + if (is_verified_oop_store) { + __ verify_oop(reg_to_register_object(dst_enc)); + } + if (tmp_enc != -1) { + __ mov(O7, reg_to_register_object(tmp_enc)); + } + if (is_verified_oop_load) { + __ verify_oop(reg_to_register_object(dst_enc)); + } + } +#endif +} + +void emit_form3_mem_reg_asi(CodeBuffer &cbuf, const MachNode* n, int primary, int tertiary, + int src1_enc, int disp32, int src2_enc, int dst_enc, int asi) { + + uint instr; + instr = (Assembler::ldst_op << 30) + | (dst_enc << 25) + | (primary << 19) + | (src1_enc << 14); + + int disp = disp32; + int index = src2_enc; + + if (src1_enc == R_SP_enc || src1_enc == R_FP_enc) + disp += STACK_BIAS; + + // We should have a compiler bailout here rather than a guarantee. + // Better yet would be some mechanism to handle variable-size matches correctly. + guarantee(Assembler::is_simm13(disp), "Do not match large constant offsets" ); + + if( disp != 0 ) { + // use reg-reg form + // set src2=R_O7 contains offset + index = R_O7_enc; + emit3_simm13( cbuf, Assembler::arith_op, index, Assembler::or_op3, 0, disp); + } + instr |= (asi << 5); + instr |= index; + uint *code = (uint*)cbuf.code_end(); + *code = instr; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); +} + +void emit_call_reloc(CodeBuffer &cbuf, intptr_t entry_point, relocInfo::relocType rtype, bool preserve_g2 = false, bool force_far_call = false) { + // The method which records debug information at every safepoint + // expects the call to be the first instruction in the snippet as + // it creates a PcDesc structure which tracks the offset of a call + // from the start of the codeBlob. This offset is computed as + // code_end() - code_begin() of the code which has been emitted + // so far. + // In this particular case we have skirted around the problem by + // putting the "mov" instruction in the delay slot but the problem + // may bite us again at some other point and a cleaner/generic + // solution using relocations would be needed. + MacroAssembler _masm(&cbuf); + __ set_inst_mark(); + + // We flush the current window just so that there is a valid stack copy + // the fact that the current window becomes active again instantly is + // not a problem there is nothing live in it. + +#ifdef ASSERT + int startpos = __ offset(); +#endif /* ASSERT */ + +#ifdef _LP64 + // Calls to the runtime or native may not be reachable from compiled code, + // so we generate the far call sequence on 64 bit sparc. + // This code sequence is relocatable to any address, even on LP64. + if ( force_far_call ) { + __ relocate(rtype); + Address dest(O7, (address)entry_point); + __ jumpl_to(dest, O7); + } + else +#endif + { + __ call((address)entry_point, rtype); + } + + if (preserve_g2) __ delayed()->mov(G2, L7); + else __ delayed()->nop(); + + if (preserve_g2) __ mov(L7, G2); + +#ifdef ASSERT + if (preserve_g2 && (VerifyCompiledCode || VerifyOops)) { +#ifdef _LP64 + // Trash argument dump slots. + __ set(0xb0b8ac0db0b8ac0d, G1); + __ mov(G1, G5); + __ stx(G1, SP, STACK_BIAS + 0x80); + __ stx(G1, SP, STACK_BIAS + 0x88); + __ stx(G1, SP, STACK_BIAS + 0x90); + __ stx(G1, SP, STACK_BIAS + 0x98); + __ stx(G1, SP, STACK_BIAS + 0xA0); + __ stx(G1, SP, STACK_BIAS + 0xA8); +#else // _LP64 + // this is also a native call, so smash the first 7 stack locations, + // and the various registers + + // Note: [SP+0x40] is sp[callee_aggregate_return_pointer_sp_offset], + // while [SP+0x44..0x58] are the argument dump slots. + __ set((intptr_t)0xbaadf00d, G1); + __ mov(G1, G5); + __ sllx(G1, 32, G1); + __ or3(G1, G5, G1); + __ mov(G1, G5); + __ stx(G1, SP, 0x40); + __ stx(G1, SP, 0x48); + __ stx(G1, SP, 0x50); + __ stw(G1, SP, 0x58); // Do not trash [SP+0x5C] which is a usable spill slot +#endif // _LP64 + } +#endif /*ASSERT*/ +} + +//============================================================================= +// REQUIRED FUNCTIONALITY for encoding +void emit_lo(CodeBuffer &cbuf, int val) { } +void emit_hi(CodeBuffer &cbuf, int val) { } + +void emit_ptr(CodeBuffer &cbuf, intptr_t val, Register reg, bool ForceRelocatable) { + MacroAssembler _masm(&cbuf); + if (ForceRelocatable) { + Address addr(reg, (address)val); + __ sethi(addr, ForceRelocatable); + __ add(addr, reg); + } else { + __ set(val, reg); + } +} + + +//============================================================================= + +#ifndef PRODUCT +void MachPrologNode::format( PhaseRegAlloc *ra_, outputStream *st ) const { + Compile* C = ra_->C; + + for (int i = 0; i < OptoPrologueNops; i++) { + st->print_cr("NOP"); st->print("\t"); + } + + if( VerifyThread ) { + st->print_cr("Verify_Thread"); st->print("\t"); + } + + size_t framesize = C->frame_slots() << LogBytesPerInt; + + // Calls to C2R adapters often do not accept exceptional returns. + // We require that their callers must bang for them. But be careful, because + // some VM calls (such as call site linkage) can use several kilobytes of + // stack. But the stack safety zone should account for that. + // See bugs 4446381, 4468289, 4497237. + if (C->need_stack_bang(framesize)) { + st->print_cr("! stack bang"); st->print("\t"); + } + + if (Assembler::is_simm13(-framesize)) { + st->print ("SAVE R_SP,-%d,R_SP",framesize); + } else { + st->print_cr("SETHI R_SP,hi%%(-%d),R_G3",framesize); st->print("\t"); + st->print_cr("ADD R_G3,lo%%(-%d),R_G3",framesize); st->print("\t"); + st->print ("SAVE R_SP,R_G3,R_SP"); + } + +} +#endif + +void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + Compile* C = ra_->C; + MacroAssembler _masm(&cbuf); + + for (int i = 0; i < OptoPrologueNops; i++) { + __ nop(); + } + + __ verify_thread(); + + size_t framesize = C->frame_slots() << LogBytesPerInt; + assert(framesize >= 16*wordSize, "must have room for reg. save area"); + assert(framesize%(2*wordSize) == 0, "must preserve 2*wordSize alignment"); + + // Calls to C2R adapters often do not accept exceptional returns. + // We require that their callers must bang for them. But be careful, because + // some VM calls (such as call site linkage) can use several kilobytes of + // stack. But the stack safety zone should account for that. + // See bugs 4446381, 4468289, 4497237. + if (C->need_stack_bang(framesize)) { + __ generate_stack_overflow_check(framesize); + } + + if (Assembler::is_simm13(-framesize)) { + __ save(SP, -framesize, SP); + } else { + __ sethi(-framesize & ~0x3ff, G3); + __ add(G3, -framesize & 0x3ff, G3); + __ save(SP, G3, SP); + } + C->set_frame_complete( __ offset() ); +} + +uint MachPrologNode::size(PhaseRegAlloc *ra_) const { + return MachNode::size(ra_); +} + +int MachPrologNode::reloc() const { + return 10; // a large enough number +} + +//============================================================================= +#ifndef PRODUCT +void MachEpilogNode::format( PhaseRegAlloc *ra_, outputStream *st ) const { + Compile* C = ra_->C; + + if( do_polling() && ra_->C->is_method_compilation() ) { + st->print("SETHI #PollAddr,L0\t! Load Polling address\n\t"); +#ifdef _LP64 + st->print("LDX [L0],G0\t!Poll for Safepointing\n\t"); +#else + st->print("LDUW [L0],G0\t!Poll for Safepointing\n\t"); +#endif + } + + if( do_polling() ) + st->print("RET\n\t"); + + st->print("RESTORE"); +} +#endif + +void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + MacroAssembler _masm(&cbuf); + Compile* C = ra_->C; + + __ verify_thread(); + + // If this does safepoint polling, then do it here + if( do_polling() && ra_->C->is_method_compilation() ) { + Address polling_page(L0, (address)os::get_polling_page()); + __ sethi(polling_page, false); + __ relocate(relocInfo::poll_return_type); + __ ld_ptr( L0, 0, G0 ); + } + + // If this is a return, then stuff the restore in the delay slot + if( do_polling() ) { + __ ret(); + __ delayed()->restore(); + } else { + __ restore(); + } +} + +uint MachEpilogNode::size(PhaseRegAlloc *ra_) const { + return MachNode::size(ra_); +} + +int MachEpilogNode::reloc() const { + return 16; // a large enough number +} + +const Pipeline * MachEpilogNode::pipeline() const { + return MachNode::pipeline_class(); +} + +int MachEpilogNode::safepoint_offset() const { + assert( do_polling(), "no return for this epilog node"); + return MacroAssembler::size_of_sethi(os::get_polling_page()); +} + +//============================================================================= + +// Figure out which register class each belongs in: rc_int, rc_float, rc_stack +enum RC { rc_bad, rc_int, rc_float, rc_stack }; +static enum RC rc_class( OptoReg::Name reg ) { + if( !OptoReg::is_valid(reg) ) return rc_bad; + if (OptoReg::is_stack(reg)) return rc_stack; + VMReg r = OptoReg::as_VMReg(reg); + if (r->is_Register()) return rc_int; + assert(r->is_FloatRegister(), "must be"); + return rc_float; +} + +static int impl_helper( const MachNode *mach, CodeBuffer *cbuf, PhaseRegAlloc *ra_, bool do_size, bool is_load, int offset, int reg, int opcode, const char *op_str, int size, outputStream* st ) { + if( cbuf ) { + // Better yet would be some mechanism to handle variable-size matches correctly + if (!Assembler::is_simm13(offset + STACK_BIAS)) { + ra_->C->record_method_not_compilable("unable to handle large constant offsets"); + } else { + emit_form3_mem_reg(*cbuf, mach, opcode, -1, R_SP_enc, offset, 0, Matcher::_regEncode[reg]); + } + } +#ifndef PRODUCT + else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); + if( is_load ) st->print("%s [R_SP + #%d],R_%s\t! spill",op_str,offset,OptoReg::regname(reg)); + else st->print("%s R_%s,[R_SP + #%d]\t! spill",op_str,OptoReg::regname(reg),offset); + } +#endif + return size+4; +} + +static int impl_mov_helper( CodeBuffer *cbuf, bool do_size, int src, int dst, int op1, int op2, const char *op_str, int size, outputStream* st ) { + if( cbuf ) emit3( *cbuf, Assembler::arith_op, Matcher::_regEncode[dst], op1, 0, op2, Matcher::_regEncode[src] ); +#ifndef PRODUCT + else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); + st->print("%s R_%s,R_%s\t! spill",op_str,OptoReg::regname(src),OptoReg::regname(dst)); + } +#endif + return size+4; +} + +uint MachSpillCopyNode::implementation( CodeBuffer *cbuf, + PhaseRegAlloc *ra_, + bool do_size, + outputStream* st ) const { + // Get registers to move + OptoReg::Name src_second = ra_->get_reg_second(in(1)); + OptoReg::Name src_first = ra_->get_reg_first(in(1)); + OptoReg::Name dst_second = ra_->get_reg_second(this ); + OptoReg::Name dst_first = ra_->get_reg_first(this ); + + enum RC src_second_rc = rc_class(src_second); + enum RC src_first_rc = rc_class(src_first); + enum RC dst_second_rc = rc_class(dst_second); + enum RC dst_first_rc = rc_class(dst_first); + + assert( OptoReg::is_valid(src_first) && OptoReg::is_valid(dst_first), "must move at least 1 register" ); + + // Generate spill code! + int size = 0; + + if( src_first == dst_first && src_second == dst_second ) + return size; // Self copy, no move + + // -------------------------------------- + // Check for mem-mem move. Load into unused float registers and fall into + // the float-store case. + if( src_first_rc == rc_stack && dst_first_rc == rc_stack ) { + int offset = ra_->reg2offset(src_first); + // Further check for aligned-adjacent pair, so we can use a double load + if( (src_first&1)==0 && src_first+1 == src_second ) { + src_second = OptoReg::Name(R_F31_num); + src_second_rc = rc_float; + size = impl_helper(this,cbuf,ra_,do_size,true,offset,R_F30_num,Assembler::lddf_op3,"LDDF",size, st); + } else { + size = impl_helper(this,cbuf,ra_,do_size,true,offset,R_F30_num,Assembler::ldf_op3 ,"LDF ",size, st); + } + src_first = OptoReg::Name(R_F30_num); + src_first_rc = rc_float; + } + + if( src_second_rc == rc_stack && dst_second_rc == rc_stack ) { + int offset = ra_->reg2offset(src_second); + size = impl_helper(this,cbuf,ra_,do_size,true,offset,R_F31_num,Assembler::ldf_op3,"LDF ",size, st); + src_second = OptoReg::Name(R_F31_num); + src_second_rc = rc_float; + } + + // -------------------------------------- + // Check for float->int copy; requires a trip through memory + if( src_first_rc == rc_float && dst_first_rc == rc_int ) { + int offset = frame::register_save_words*wordSize; + if( cbuf ) { + emit3_simm13( *cbuf, Assembler::arith_op, R_SP_enc, Assembler::sub_op3, R_SP_enc, 16 ); + impl_helper(this,cbuf,ra_,do_size,false,offset,src_first,Assembler::stf_op3 ,"STF ",size, st); + impl_helper(this,cbuf,ra_,do_size,true ,offset,dst_first,Assembler::lduw_op3,"LDUW",size, st); + emit3_simm13( *cbuf, Assembler::arith_op, R_SP_enc, Assembler::add_op3, R_SP_enc, 16 ); + } +#ifndef PRODUCT + else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); + st->print( "SUB R_SP,16,R_SP\n"); + impl_helper(this,cbuf,ra_,do_size,false,offset,src_first,Assembler::stf_op3 ,"STF ",size, st); + impl_helper(this,cbuf,ra_,do_size,true ,offset,dst_first,Assembler::lduw_op3,"LDUW",size, st); + st->print("\tADD R_SP,16,R_SP\n"); + } +#endif + size += 16; + } + + // -------------------------------------- + // In the 32-bit 1-reg-longs build ONLY, I see mis-aligned long destinations. + // In such cases, I have to do the big-endian swap. For aligned targets, the + // hardware does the flop for me. Doubles are always aligned, so no problem + // there. Misaligned sources only come from native-long-returns (handled + // special below). +#ifndef _LP64 + if( src_first_rc == rc_int && // source is already big-endian + src_second_rc != rc_bad && // 64-bit move + ((dst_first&1)!=0 || dst_second != dst_first+1) ) { // misaligned dst + assert( (src_first&1)==0 && src_second == src_first+1, "source must be aligned" ); + // Do the big-endian flop. + OptoReg::Name tmp = dst_first ; dst_first = dst_second ; dst_second = tmp ; + enum RC tmp_rc = dst_first_rc; dst_first_rc = dst_second_rc; dst_second_rc = tmp_rc; + } +#endif + + // -------------------------------------- + // Check for integer reg-reg copy + if( src_first_rc == rc_int && dst_first_rc == rc_int ) { +#ifndef _LP64 + if( src_first == R_O0_num && src_second == R_O1_num ) { // Check for the evil O0/O1 native long-return case + // Note: The _first and _second suffixes refer to the addresses of the the 2 halves of the 64-bit value + // as stored in memory. On a big-endian machine like SPARC, this means that the _second + // operand contains the least significant word of the 64-bit value and vice versa. + OptoReg::Name tmp = OptoReg::Name(R_O7_num); + assert( (dst_first&1)==0 && dst_second == dst_first+1, "return a native O0/O1 long to an aligned-adjacent 64-bit reg" ); + // Shift O0 left in-place, zero-extend O1, then OR them into the dst + if( cbuf ) { + emit3_simm13( *cbuf, Assembler::arith_op, Matcher::_regEncode[tmp], Assembler::sllx_op3, Matcher::_regEncode[src_first], 0x1020 ); + emit3_simm13( *cbuf, Assembler::arith_op, Matcher::_regEncode[src_second], Assembler::srl_op3, Matcher::_regEncode[src_second], 0x0000 ); + emit3 ( *cbuf, Assembler::arith_op, Matcher::_regEncode[dst_first], Assembler:: or_op3, Matcher::_regEncode[tmp], 0, Matcher::_regEncode[src_second] ); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); + st->print("SLLX R_%s,32,R_%s\t! Move O0-first to O7-high\n\t", OptoReg::regname(src_first), OptoReg::regname(tmp)); + st->print("SRL R_%s, 0,R_%s\t! Zero-extend O1\n\t", OptoReg::regname(src_second), OptoReg::regname(src_second)); + st->print("OR R_%s,R_%s,R_%s\t! spill",OptoReg::regname(tmp), OptoReg::regname(src_second), OptoReg::regname(dst_first)); +#endif + } + return size+12; + } + else if( dst_first == R_I0_num && dst_second == R_I1_num ) { + // returning a long value in I0/I1 + // a SpillCopy must be able to target a return instruction's reg_class + // Note: The _first and _second suffixes refer to the addresses of the the 2 halves of the 64-bit value + // as stored in memory. On a big-endian machine like SPARC, this means that the _second + // operand contains the least significant word of the 64-bit value and vice versa. + OptoReg::Name tdest = dst_first; + + if (src_first == dst_first) { + tdest = OptoReg::Name(R_O7_num); + size += 4; + } + + if( cbuf ) { + assert( (src_first&1) == 0 && (src_first+1) == src_second, "return value was in an aligned-adjacent 64-bit reg"); + // Shift value in upper 32-bits of src to lower 32-bits of I0; move lower 32-bits to I1 + // ShrL_reg_imm6 + emit3_simm13( *cbuf, Assembler::arith_op, Matcher::_regEncode[tdest], Assembler::srlx_op3, Matcher::_regEncode[src_second], 32 | 0x1000 ); + // ShrR_reg_imm6 src, 0, dst + emit3_simm13( *cbuf, Assembler::arith_op, Matcher::_regEncode[dst_second], Assembler::srl_op3, Matcher::_regEncode[src_first], 0x0000 ); + if (tdest != dst_first) { + emit3 ( *cbuf, Assembler::arith_op, Matcher::_regEncode[dst_first], Assembler::or_op3, 0/*G0*/, 0/*op2*/, Matcher::_regEncode[tdest] ); + } + } +#ifndef PRODUCT + else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); // %%%%% !!!!! + st->print("SRLX R_%s,32,R_%s\t! Extract MSW\n\t",OptoReg::regname(src_second),OptoReg::regname(tdest)); + st->print("SRL R_%s, 0,R_%s\t! Extract LSW\n\t",OptoReg::regname(src_first),OptoReg::regname(dst_second)); + if (tdest != dst_first) { + st->print("MOV R_%s,R_%s\t! spill\n\t", OptoReg::regname(tdest), OptoReg::regname(dst_first)); + } + } +#endif // PRODUCT + return size+8; + } +#endif // !_LP64 + // Else normal reg-reg copy + assert( src_second != dst_first, "smashed second before evacuating it" ); + size = impl_mov_helper(cbuf,do_size,src_first,dst_first,Assembler::or_op3,0,"MOV ",size, st); + assert( (src_first&1) == 0 && (dst_first&1) == 0, "never move second-halves of int registers" ); + // This moves an aligned adjacent pair. + // See if we are done. + if( src_first+1 == src_second && dst_first+1 == dst_second ) + return size; + } + + // Check for integer store + if( src_first_rc == rc_int && dst_first_rc == rc_stack ) { + int offset = ra_->reg2offset(dst_first); + // Further check for aligned-adjacent pair, so we can use a double store + if( (src_first&1)==0 && src_first+1 == src_second && (dst_first&1)==0 && dst_first+1 == dst_second ) + return impl_helper(this,cbuf,ra_,do_size,false,offset,src_first,Assembler::stx_op3,"STX ",size, st); + size = impl_helper(this,cbuf,ra_,do_size,false,offset,src_first,Assembler::stw_op3,"STW ",size, st); + } + + // Check for integer load + if( dst_first_rc == rc_int && src_first_rc == rc_stack ) { + int offset = ra_->reg2offset(src_first); + // Further check for aligned-adjacent pair, so we can use a double load + if( (src_first&1)==0 && src_first+1 == src_second && (dst_first&1)==0 && dst_first+1 == dst_second ) + return impl_helper(this,cbuf,ra_,do_size,true,offset,dst_first,Assembler::ldx_op3 ,"LDX ",size, st); + size = impl_helper(this,cbuf,ra_,do_size,true,offset,dst_first,Assembler::lduw_op3,"LDUW",size, st); + } + + // Check for float reg-reg copy + if( src_first_rc == rc_float && dst_first_rc == rc_float ) { + // Further check for aligned-adjacent pair, so we can use a double move + if( (src_first&1)==0 && src_first+1 == src_second && (dst_first&1)==0 && dst_first+1 == dst_second ) + return impl_mov_helper(cbuf,do_size,src_first,dst_first,Assembler::fpop1_op3,Assembler::fmovd_opf,"FMOVD",size, st); + size = impl_mov_helper(cbuf,do_size,src_first,dst_first,Assembler::fpop1_op3,Assembler::fmovs_opf,"FMOVS",size, st); + } + + // Check for float store + if( src_first_rc == rc_float && dst_first_rc == rc_stack ) { + int offset = ra_->reg2offset(dst_first); + // Further check for aligned-adjacent pair, so we can use a double store + if( (src_first&1)==0 && src_first+1 == src_second && (dst_first&1)==0 && dst_first+1 == dst_second ) + return impl_helper(this,cbuf,ra_,do_size,false,offset,src_first,Assembler::stdf_op3,"STDF",size, st); + size = impl_helper(this,cbuf,ra_,do_size,false,offset,src_first,Assembler::stf_op3 ,"STF ",size, st); + } + + // Check for float load + if( dst_first_rc == rc_float && src_first_rc == rc_stack ) { + int offset = ra_->reg2offset(src_first); + // Further check for aligned-adjacent pair, so we can use a double load + if( (src_first&1)==0 && src_first+1 == src_second && (dst_first&1)==0 && dst_first+1 == dst_second ) + return impl_helper(this,cbuf,ra_,do_size,true,offset,dst_first,Assembler::lddf_op3,"LDDF",size, st); + size = impl_helper(this,cbuf,ra_,do_size,true,offset,dst_first,Assembler::ldf_op3 ,"LDF ",size, st); + } + + // -------------------------------------------------------------------- + // Check for hi bits still needing moving. Only happens for misaligned + // arguments to native calls. + if( src_second == dst_second ) + return size; // Self copy; no move + assert( src_second_rc != rc_bad && dst_second_rc != rc_bad, "src_second & dst_second cannot be Bad" ); + +#ifndef _LP64 + // In the LP64 build, all registers can be moved as aligned/adjacent + // pairs, so there's never any need to move the high bits seperately. + // The 32-bit builds have to deal with the 32-bit ABI which can force + // all sorts of silly alignment problems. + + // Check for integer reg-reg copy. Hi bits are stuck up in the top + // 32-bits of a 64-bit register, but are needed in low bits of another + // register (else it's a hi-bits-to-hi-bits copy which should have + // happened already as part of a 64-bit move) + if( src_second_rc == rc_int && dst_second_rc == rc_int ) { + assert( (src_second&1)==1, "its the evil O0/O1 native return case" ); + assert( (dst_second&1)==0, "should have moved with 1 64-bit move" ); + // Shift src_second down to dst_second's low bits. + if( cbuf ) { + emit3_simm13( *cbuf, Assembler::arith_op, Matcher::_regEncode[dst_second], Assembler::srlx_op3, Matcher::_regEncode[src_second-1], 0x1020 ); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); + st->print("SRLX R_%s,32,R_%s\t! spill: Move high bits down low",OptoReg::regname(src_second-1),OptoReg::regname(dst_second)); +#endif + } + return size+4; + } + + // Check for high word integer store. Must down-shift the hi bits + // into a temp register, then fall into the case of storing int bits. + if( src_second_rc == rc_int && dst_second_rc == rc_stack && (src_second&1)==1 ) { + // Shift src_second down to dst_second's low bits. + if( cbuf ) { + emit3_simm13( *cbuf, Assembler::arith_op, Matcher::_regEncode[R_O7_num], Assembler::srlx_op3, Matcher::_regEncode[src_second-1], 0x1020 ); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); + st->print("SRLX R_%s,32,R_%s\t! spill: Move high bits down low",OptoReg::regname(src_second-1),OptoReg::regname(R_O7_num)); +#endif + } + size+=4; + src_second = OptoReg::Name(R_O7_num); // Not R_O7H_num! + } + + // Check for high word integer load + if( dst_second_rc == rc_int && src_second_rc == rc_stack ) + return impl_helper(this,cbuf,ra_,do_size,true ,ra_->reg2offset(src_second),dst_second,Assembler::lduw_op3,"LDUW",size, st); + + // Check for high word integer store + if( src_second_rc == rc_int && dst_second_rc == rc_stack ) + return impl_helper(this,cbuf,ra_,do_size,false,ra_->reg2offset(dst_second),src_second,Assembler::stw_op3 ,"STW ",size, st); + + // Check for high word float store + if( src_second_rc == rc_float && dst_second_rc == rc_stack ) + return impl_helper(this,cbuf,ra_,do_size,false,ra_->reg2offset(dst_second),src_second,Assembler::stf_op3 ,"STF ",size, st); + +#endif // !_LP64 + + Unimplemented(); +} + +#ifndef PRODUCT +void MachSpillCopyNode::format( PhaseRegAlloc *ra_, outputStream *st ) const { + implementation( NULL, ra_, false, st ); +} +#endif + +void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + implementation( &cbuf, ra_, false, NULL ); +} + +uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const { + return implementation( NULL, ra_, true, NULL ); +} + +//============================================================================= +#ifndef PRODUCT +void MachNopNode::format( PhaseRegAlloc *, outputStream *st ) const { + st->print("NOP \t# %d bytes pad for loops and calls", 4 * _count); +} +#endif + +void MachNopNode::emit(CodeBuffer &cbuf, PhaseRegAlloc * ) const { + MacroAssembler _masm(&cbuf); + for(int i = 0; i < _count; i += 1) { + __ nop(); + } +} + +uint MachNopNode::size(PhaseRegAlloc *ra_) const { + return 4 * _count; +} + + +//============================================================================= +#ifndef PRODUCT +void BoxLockNode::format( PhaseRegAlloc *ra_, outputStream *st ) const { + int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); + int reg = ra_->get_reg_first(this); + st->print("LEA [R_SP+#%d+BIAS],%s",offset,Matcher::regName[reg]); +} +#endif + +void BoxLockNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + MacroAssembler _masm(&cbuf); + int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()) + STACK_BIAS; + int reg = ra_->get_encode(this); + + if (Assembler::is_simm13(offset)) { + __ add(SP, offset, reg_to_register_object(reg)); + } else { + __ set(offset, O7); + __ add(SP, O7, reg_to_register_object(reg)); + } +} + +uint BoxLockNode::size(PhaseRegAlloc *ra_) const { + // BoxLockNode is not a MachNode, so we can't just call MachNode::size(ra_) + assert(ra_ == ra_->C->regalloc(), "sanity"); + return ra_->C->scratch_emit_size(this); +} + +//============================================================================= + +// emit call stub, compiled java to interpretor +void emit_java_to_interp(CodeBuffer &cbuf ) { + + // Stub is fixed up when the corresponding call is converted from calling + // compiled code to calling interpreted code. + // set (empty), G5 + // jmp -1 + + address mark = cbuf.inst_mark(); // get mark within main instrs section + + MacroAssembler _masm(&cbuf); + + address base = + __ start_a_stub(Compile::MAX_stubs_size); + if (base == NULL) return; // CodeBuffer::expand failed + + // static stub relocation stores the instruction address of the call + __ relocate(static_stub_Relocation::spec(mark)); + + __ set_oop(NULL, reg_to_register_object(Matcher::inline_cache_reg_encode())); + + __ set_inst_mark(); + Address a(G3, (address)-1); + __ JUMP(a, 0); + + __ delayed()->nop(); + + // Update current stubs pointer and restore code_end. + __ end_a_stub(); +} + +// size of call stub, compiled java to interpretor +uint size_java_to_interp() { + // This doesn't need to be accurate but it must be larger or equal to + // the real size of the stub. + return (NativeMovConstReg::instruction_size + // sethi/setlo; + NativeJump::instruction_size + // sethi; jmp; nop + (TraceJumps ? 20 * BytesPerInstWord : 0) ); +} +// relocation entries for call stub, compiled java to interpretor +uint reloc_java_to_interp() { + return 10; // 4 in emit_java_to_interp + 1 in Java_Static_Call +} + + +//============================================================================= +#ifndef PRODUCT +void MachUEPNode::format( PhaseRegAlloc *ra_, outputStream *st ) const { + st->print_cr("\nUEP:"); +#ifdef _LP64 + st->print_cr("\tLDX [R_O0 + oopDesc::klass_offset_in_bytes],R_G5\t! Inline cache check"); + st->print_cr("\tCMP R_G5,R_G3" ); + st->print ("\tTne xcc,R_G0+ST_RESERVED_FOR_USER_0+2"); +#else // _LP64 + st->print_cr("\tLDUW [R_O0 + oopDesc::klass_offset_in_bytes],R_G5\t! Inline cache check"); + st->print_cr("\tCMP R_G5,R_G3" ); + st->print ("\tTne icc,R_G0+ST_RESERVED_FOR_USER_0+2"); +#endif // _LP64 +} +#endif + +void MachUEPNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + MacroAssembler _masm(&cbuf); + Label L; + Register G5_ic_reg = reg_to_register_object(Matcher::inline_cache_reg_encode()); + Register temp_reg = G3; + assert( G5_ic_reg != temp_reg, "conflicting registers" ); + + // Load klass from reciever + __ ld_ptr(O0, oopDesc::klass_offset_in_bytes(), temp_reg); + // Compare against expected klass + __ cmp(temp_reg, G5_ic_reg); + // Branch to miss code, checks xcc or icc depending + __ trap(Assembler::notEqual, Assembler::ptr_cc, G0, ST_RESERVED_FOR_USER_0+2); +} + +uint MachUEPNode::size(PhaseRegAlloc *ra_) const { + return MachNode::size(ra_); +} + + +//============================================================================= + +uint size_exception_handler() { + if (TraceJumps) { + return (400); // just a guess + } + return ( NativeJump::instruction_size ); // sethi;jmp;nop +} + +uint size_deopt_handler() { + if (TraceJumps) { + return (400); // just a guess + } + return ( 4+ NativeJump::instruction_size ); // save;sethi;jmp;restore +} + +// Emit exception handler code. +int emit_exception_handler(CodeBuffer& cbuf) { + Register temp_reg = G3; + Address exception_blob(temp_reg, OptoRuntime::exception_blob()->instructions_begin()); + MacroAssembler _masm(&cbuf); + + address base = + __ start_a_stub(size_exception_handler()); + if (base == NULL) return 0; // CodeBuffer::expand failed + + int offset = __ offset(); + + __ JUMP(exception_blob, 0); // sethi;jmp + __ delayed()->nop(); + + assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); + + __ end_a_stub(); + + return offset; +} + +int emit_deopt_handler(CodeBuffer& cbuf) { + // Can't use any of the current frame's registers as we may have deopted + // at a poll and everything (including G3) can be live. + Register temp_reg = L0; + Address deopt_blob(temp_reg, SharedRuntime::deopt_blob()->unpack()); + MacroAssembler _masm(&cbuf); + + address base = + __ start_a_stub(size_deopt_handler()); + if (base == NULL) return 0; // CodeBuffer::expand failed + + int offset = __ offset(); + __ save_frame(0); + __ JUMP(deopt_blob, 0); // sethi;jmp + __ delayed()->restore(); + + assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); + + __ end_a_stub(); + return offset; + +} + +// Given a register encoding, produce a Integer Register object +static Register reg_to_register_object(int register_encoding) { + assert(L5->encoding() == R_L5_enc && G1->encoding() == R_G1_enc, "right coding"); + return as_Register(register_encoding); +} + +// Given a register encoding, produce a single-precision Float Register object +static FloatRegister reg_to_SingleFloatRegister_object(int register_encoding) { + assert(F5->encoding(FloatRegisterImpl::S) == R_F5_enc && F12->encoding(FloatRegisterImpl::S) == R_F12_enc, "right coding"); + return as_SingleFloatRegister(register_encoding); +} + +// Given a register encoding, produce a double-precision Float Register object +static FloatRegister reg_to_DoubleFloatRegister_object(int register_encoding) { + assert(F4->encoding(FloatRegisterImpl::D) == R_F4_enc, "right coding"); + assert(F32->encoding(FloatRegisterImpl::D) == R_D32_enc, "right coding"); + return as_DoubleFloatRegister(register_encoding); +} + +int Matcher::regnum_to_fpu_offset(int regnum) { + return regnum - 32; // The FP registers are in the second chunk +} + +#ifdef ASSERT +address last_rethrow = NULL; // debugging aid for Rethrow encoding +#endif + +// Vector width in bytes +const uint Matcher::vector_width_in_bytes(void) { + return 8; +} + +// Vector ideal reg +const uint Matcher::vector_ideal_reg(void) { + return Op_RegD; +} + +// USII supports fxtof through the whole range of number, USIII doesn't +const bool Matcher::convL2FSupported(void) { + return VM_Version::has_fast_fxtof(); +} + +// Is this branch offset short enough that a short branch can be used? +// +// NOTE: If the platform does not provide any short branch variants, then +// this method should return false for offset 0. +bool Matcher::is_short_branch_offset(int offset) { + return false; +} + +const bool Matcher::isSimpleConstant64(jlong value) { + // Will one (StoreL ConL) be cheaper than two (StoreI ConI)?. + // Depends on optimizations in MacroAssembler::setx. + int hi = (int)(value >> 32); + int lo = (int)(value & ~0); + return (hi == 0) || (hi == -1) || (lo == 0); +} + +// No scaling for the parameter the ClearArray node. +const bool Matcher::init_array_count_is_in_bytes = true; + +// Threshold size for cleararray. +const int Matcher::init_array_short_size = 8 * BytesPerLong; + +// Should the Matcher clone shifts on addressing modes, expecting them to +// be subsumed into complex addressing expressions or compute them into +// registers? True for Intel but false for most RISCs +const bool Matcher::clone_shift_expressions = false; + +// Is it better to copy float constants, or load them directly from memory? +// Intel can load a float constant from a direct address, requiring no +// extra registers. Most RISCs will have to materialize an address into a +// register first, so they would do better to copy the constant from stack. +const bool Matcher::rematerialize_float_constants = false; + +// If CPU can load and store mis-aligned doubles directly then no fixup is +// needed. Else we split the double into 2 integer pieces and move it +// piece-by-piece. Only happens when passing doubles into C code as the +// Java calling convention forces doubles to be aligned. +#ifdef _LP64 +const bool Matcher::misaligned_doubles_ok = true; +#else +const bool Matcher::misaligned_doubles_ok = false; +#endif + +// No-op on SPARC. +void Matcher::pd_implicit_null_fixup(MachNode *node, uint idx) { +} + +// Advertise here if the CPU requires explicit rounding operations +// to implement the UseStrictFP mode. +const bool Matcher::strict_fp_requires_explicit_rounding = false; + +// Do floats take an entire double register or just half? +const bool Matcher::float_in_double = false; + +// Do ints take an entire long register or just half? +// Note that we if-def off of _LP64. +// The relevant question is how the int is callee-saved. In _LP64 +// the whole long is written but de-opt'ing will have to extract +// the relevant 32 bits, in not-_LP64 only the low 32 bits is written. +#ifdef _LP64 +const bool Matcher::int_in_long = true; +#else +const bool Matcher::int_in_long = false; +#endif + +// Return whether or not this register is ever used as an argument. This +// function is used on startup to build the trampoline stubs in generateOptoStub. +// Registers not mentioned will be killed by the VM call in the trampoline, and +// arguments in those registers not be available to the callee. +bool Matcher::can_be_java_arg( int reg ) { + // Standard sparc 6 args in registers + if( reg == R_I0_num || + reg == R_I1_num || + reg == R_I2_num || + reg == R_I3_num || + reg == R_I4_num || + reg == R_I5_num ) return true; +#ifdef _LP64 + // 64-bit builds can pass 64-bit pointers and longs in + // the high I registers + if( reg == R_I0H_num || + reg == R_I1H_num || + reg == R_I2H_num || + reg == R_I3H_num || + reg == R_I4H_num || + reg == R_I5H_num ) return true; +#else + // 32-bit builds with longs-in-one-entry pass longs in G1 & G4. + // Longs cannot be passed in O regs, because O regs become I regs + // after a 'save' and I regs get their high bits chopped off on + // interrupt. + if( reg == R_G1H_num || reg == R_G1_num ) return true; + if( reg == R_G4H_num || reg == R_G4_num ) return true; +#endif + // A few float args in registers + if( reg >= R_F0_num && reg <= R_F7_num ) return true; + + return false; +} + +bool Matcher::is_spillable_arg( int reg ) { + return can_be_java_arg(reg); +} + +// Register for DIVI projection of divmodI +RegMask Matcher::divI_proj_mask() { + ShouldNotReachHere(); + return RegMask(); +} + +// Register for MODI projection of divmodI +RegMask Matcher::modI_proj_mask() { + ShouldNotReachHere(); + return RegMask(); +} + +// Register for DIVL projection of divmodL +RegMask Matcher::divL_proj_mask() { + ShouldNotReachHere(); + return RegMask(); +} + +// Register for MODL projection of divmodL +RegMask Matcher::modL_proj_mask() { + ShouldNotReachHere(); + return RegMask(); +} + +%} + + +// The intptr_t operand types, defined by textual substitution. +// (Cf. opto/type.hpp. This lets us avoid many, many other ifdefs.) +#ifdef _LP64 +#define immX immL +#define immX13 immL13 +#define iRegX iRegL +#define g1RegX g1RegL +#else +#define immX immI +#define immX13 immI13 +#define iRegX iRegI +#define g1RegX g1RegI +#endif + +//----------ENCODING BLOCK----------------------------------------------------- +// This block specifies the encoding classes used by the compiler to output +// byte streams. Encoding classes are parameterized macros used by +// Machine Instruction Nodes in order to generate the bit encoding of the +// instruction. Operands specify their base encoding interface with the +// interface keyword. There are currently supported four interfaces, +// REG_INTER, CONST_INTER, MEMORY_INTER, & COND_INTER. REG_INTER causes an +// operand to generate a function which returns its register number when +// queried. CONST_INTER causes an operand to generate a function which +// returns the value of the constant when queried. MEMORY_INTER causes an +// operand to generate four functions which return the Base Register, the +// Index Register, the Scale Value, and the Offset Value of the operand when +// queried. COND_INTER causes an operand to generate six functions which +// return the encoding code (ie - encoding bits for the instruction) +// associated with each basic boolean condition for a conditional instruction. +// +// Instructions specify two basic values for encoding. Again, a function +// is available to check if the constant displacement is an oop. They use the +// ins_encode keyword to specify their encoding classes (which must be +// a sequence of enc_class names, and their parameters, specified in +// the encoding block), and they use the +// opcode keyword to specify, in order, their primary, secondary, and +// tertiary opcode. Only the opcode sections which a particular instruction +// needs for encoding need to be specified. +encode %{ + enc_class enc_untested %{ +#ifdef ASSERT + MacroAssembler _masm(&cbuf); + __ untested("encoding"); +#endif + %} + + enc_class form3_mem_reg( memory mem, iRegI dst ) %{ + emit_form3_mem_reg(cbuf, this, $primary, $tertiary, + $mem$$base, $mem$$disp, $mem$$index, $dst$$reg); + %} + + enc_class form3_mem_reg_little( memory mem, iRegI dst) %{ + emit_form3_mem_reg_asi(cbuf, this, $primary, $tertiary, + $mem$$base, $mem$$disp, $mem$$index, $dst$$reg, Assembler::ASI_PRIMARY_LITTLE); + %} + + enc_class form3_mem_prefetch_read( memory mem ) %{ + emit_form3_mem_reg(cbuf, this, $primary, $tertiary, + $mem$$base, $mem$$disp, $mem$$index, 0/*prefetch function many-reads*/); + %} + + enc_class form3_mem_prefetch_write( memory mem ) %{ + emit_form3_mem_reg(cbuf, this, $primary, $tertiary, + $mem$$base, $mem$$disp, $mem$$index, 2/*prefetch function many-writes*/); + %} + + enc_class form3_mem_reg_long_unaligned_marshal( memory mem, iRegL reg ) %{ + assert( Assembler::is_simm13($mem$$disp ), "need disp and disp+4" ); + assert( Assembler::is_simm13($mem$$disp+4), "need disp and disp+4" ); + guarantee($mem$$index == R_G0_enc, "double index?"); + emit_form3_mem_reg(cbuf, this, $primary, $tertiary, $mem$$base, $mem$$disp+4, R_G0_enc, R_O7_enc ); + emit_form3_mem_reg(cbuf, this, $primary, $tertiary, $mem$$base, $mem$$disp, R_G0_enc, $reg$$reg ); + emit3_simm13( cbuf, Assembler::arith_op, $reg$$reg, Assembler::sllx_op3, $reg$$reg, 0x1020 ); + emit3( cbuf, Assembler::arith_op, $reg$$reg, Assembler::or_op3, $reg$$reg, 0, R_O7_enc ); + %} + + enc_class form3_mem_reg_double_unaligned( memory mem, RegD_low reg ) %{ + assert( Assembler::is_simm13($mem$$disp ), "need disp and disp+4" ); + assert( Assembler::is_simm13($mem$$disp+4), "need disp and disp+4" ); + guarantee($mem$$index == R_G0_enc, "double index?"); + // Load long with 2 instructions + emit_form3_mem_reg(cbuf, this, $primary, $tertiary, $mem$$base, $mem$$disp, R_G0_enc, $reg$$reg+0 ); + emit_form3_mem_reg(cbuf, this, $primary, $tertiary, $mem$$base, $mem$$disp+4, R_G0_enc, $reg$$reg+1 ); + %} + + //%%% form3_mem_plus_4_reg is a hack--get rid of it + enc_class form3_mem_plus_4_reg( memory mem, iRegI dst ) %{ + guarantee($mem$$disp, "cannot offset a reg-reg operand by 4"); + emit_form3_mem_reg(cbuf, this, $primary, $tertiary, $mem$$base, $mem$$disp + 4, $mem$$index, $dst$$reg); + %} + + enc_class form3_g0_rs2_rd_move( iRegI rs2, iRegI rd ) %{ + // Encode a reg-reg copy. If it is useless, then empty encoding. + if( $rs2$$reg != $rd$$reg ) + emit3( cbuf, Assembler::arith_op, $rd$$reg, Assembler::or_op3, 0, 0, $rs2$$reg ); + %} + + // Target lo half of long + enc_class form3_g0_rs2_rd_move_lo( iRegI rs2, iRegL rd ) %{ + // Encode a reg-reg copy. If it is useless, then empty encoding. + if( $rs2$$reg != LONG_LO_REG($rd$$reg) ) + emit3( cbuf, Assembler::arith_op, LONG_LO_REG($rd$$reg), Assembler::or_op3, 0, 0, $rs2$$reg ); + %} + + // Source lo half of long + enc_class form3_g0_rs2_rd_move_lo2( iRegL rs2, iRegI rd ) %{ + // Encode a reg-reg copy. If it is useless, then empty encoding. + if( LONG_LO_REG($rs2$$reg) != $rd$$reg ) + emit3( cbuf, Assembler::arith_op, $rd$$reg, Assembler::or_op3, 0, 0, LONG_LO_REG($rs2$$reg) ); + %} + + // Target hi half of long + enc_class form3_rs1_rd_copysign_hi( iRegI rs1, iRegL rd ) %{ + emit3_simm13( cbuf, Assembler::arith_op, $rd$$reg, Assembler::sra_op3, $rs1$$reg, 31 ); + %} + + // Source lo half of long, and leave it sign extended. + enc_class form3_rs1_rd_signextend_lo1( iRegL rs1, iRegI rd ) %{ + // Sign extend low half + emit3( cbuf, Assembler::arith_op, $rd$$reg, Assembler::sra_op3, $rs1$$reg, 0, 0 ); + %} + + // Source hi half of long, and leave it sign extended. + enc_class form3_rs1_rd_copy_hi1( iRegL rs1, iRegI rd ) %{ + // Shift high half to low half + emit3_simm13( cbuf, Assembler::arith_op, $rd$$reg, Assembler::srlx_op3, $rs1$$reg, 32 ); + %} + + // Source hi half of long + enc_class form3_g0_rs2_rd_move_hi2( iRegL rs2, iRegI rd ) %{ + // Encode a reg-reg copy. If it is useless, then empty encoding. + if( LONG_HI_REG($rs2$$reg) != $rd$$reg ) + emit3( cbuf, Assembler::arith_op, $rd$$reg, Assembler::or_op3, 0, 0, LONG_HI_REG($rs2$$reg) ); + %} + + enc_class form3_rs1_rs2_rd( iRegI rs1, iRegI rs2, iRegI rd ) %{ + emit3( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, 0, $rs2$$reg ); + %} + + enc_class enc_to_bool( iRegI src, iRegI dst ) %{ + emit3 ( cbuf, Assembler::arith_op, 0, Assembler::subcc_op3, 0, 0, $src$$reg ); + emit3_simm13( cbuf, Assembler::arith_op, $dst$$reg, Assembler::addc_op3 , 0, 0 ); + %} + + enc_class enc_ltmask( iRegI p, iRegI q, iRegI dst ) %{ + emit3 ( cbuf, Assembler::arith_op, 0, Assembler::subcc_op3, $p$$reg, 0, $q$$reg ); + // clear if nothing else is happening + emit3_simm13( cbuf, Assembler::arith_op, $dst$$reg, Assembler::or_op3, 0, 0 ); + // blt,a,pn done + emit2_19 ( cbuf, Assembler::branch_op, 1/*annul*/, Assembler::less, Assembler::bp_op2, Assembler::icc, 0/*predict not taken*/, 2 ); + // mov dst,-1 in delay slot + emit3_simm13( cbuf, Assembler::arith_op, $dst$$reg, Assembler::or_op3, 0, -1 ); + %} + + enc_class form3_rs1_imm5_rd( iRegI rs1, immU5 imm5, iRegI rd ) %{ + emit3_simm13( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, $imm5$$constant & 0x1F ); + %} + + enc_class form3_sd_rs1_imm6_rd( iRegL rs1, immU6 imm6, iRegL rd ) %{ + emit3_simm13( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, ($imm6$$constant & 0x3F) | 0x1000 ); + %} + + enc_class form3_sd_rs1_rs2_rd( iRegL rs1, iRegI rs2, iRegL rd ) %{ + emit3( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, 0x80, $rs2$$reg ); + %} + + enc_class form3_rs1_simm13_rd( iRegI rs1, immI13 simm13, iRegI rd ) %{ + emit3_simm13( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, $simm13$$constant ); + %} + + enc_class move_return_pc_to_o1() %{ + emit3_simm13( cbuf, Assembler::arith_op, R_O1_enc, Assembler::add_op3, R_O7_enc, frame::pc_return_offset ); + %} + +#ifdef _LP64 + /* %%% merge with enc_to_bool */ + enc_class enc_convP2B( iRegI dst, iRegP src ) %{ + MacroAssembler _masm(&cbuf); + + Register src_reg = reg_to_register_object($src$$reg); + Register dst_reg = reg_to_register_object($dst$$reg); + __ movr(Assembler::rc_nz, src_reg, 1, dst_reg); + %} +#endif + + enc_class enc_cadd_cmpLTMask( iRegI p, iRegI q, iRegI y, iRegI tmp ) %{ + // (Set p (AddI (AndI (CmpLTMask p q) y) (SubI p q))) + MacroAssembler _masm(&cbuf); + + Register p_reg = reg_to_register_object($p$$reg); + Register q_reg = reg_to_register_object($q$$reg); + Register y_reg = reg_to_register_object($y$$reg); + Register tmp_reg = reg_to_register_object($tmp$$reg); + + __ subcc( p_reg, q_reg, p_reg ); + __ add ( p_reg, y_reg, tmp_reg ); + __ movcc( Assembler::less, false, Assembler::icc, tmp_reg, p_reg ); + %} + + enc_class form_d2i_helper(regD src, regF dst) %{ + // fcmp %fcc0,$src,$src + emit3( cbuf, Assembler::arith_op , Assembler::fcc0, Assembler::fpop2_op3, $src$$reg, Assembler::fcmpd_opf, $src$$reg ); + // branch %fcc0 not-nan, predict taken + emit2_19( cbuf, Assembler::branch_op, 0/*annul*/, Assembler::f_ordered, Assembler::fbp_op2, Assembler::fcc0, 1/*predict taken*/, 4 ); + // fdtoi $src,$dst + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fdtoi_opf, $src$$reg ); + // fitos $dst,$dst (if nan) + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fitos_opf, $dst$$reg ); + // clear $dst (if nan) + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, $dst$$reg, Assembler::fsubs_opf, $dst$$reg ); + // carry on here... + %} + + enc_class form_d2l_helper(regD src, regD dst) %{ + // fcmp %fcc0,$src,$src check for NAN + emit3( cbuf, Assembler::arith_op , Assembler::fcc0, Assembler::fpop2_op3, $src$$reg, Assembler::fcmpd_opf, $src$$reg ); + // branch %fcc0 not-nan, predict taken + emit2_19( cbuf, Assembler::branch_op, 0/*annul*/, Assembler::f_ordered, Assembler::fbp_op2, Assembler::fcc0, 1/*predict taken*/, 4 ); + // fdtox $src,$dst convert in delay slot + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fdtox_opf, $src$$reg ); + // fxtod $dst,$dst (if nan) + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fxtod_opf, $dst$$reg ); + // clear $dst (if nan) + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, $dst$$reg, Assembler::fsubd_opf, $dst$$reg ); + // carry on here... + %} + + enc_class form_f2i_helper(regF src, regF dst) %{ + // fcmps %fcc0,$src,$src + emit3( cbuf, Assembler::arith_op , Assembler::fcc0, Assembler::fpop2_op3, $src$$reg, Assembler::fcmps_opf, $src$$reg ); + // branch %fcc0 not-nan, predict taken + emit2_19( cbuf, Assembler::branch_op, 0/*annul*/, Assembler::f_ordered, Assembler::fbp_op2, Assembler::fcc0, 1/*predict taken*/, 4 ); + // fstoi $src,$dst + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fstoi_opf, $src$$reg ); + // fitos $dst,$dst (if nan) + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fitos_opf, $dst$$reg ); + // clear $dst (if nan) + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, $dst$$reg, Assembler::fsubs_opf, $dst$$reg ); + // carry on here... + %} + + enc_class form_f2l_helper(regF src, regD dst) %{ + // fcmps %fcc0,$src,$src + emit3( cbuf, Assembler::arith_op , Assembler::fcc0, Assembler::fpop2_op3, $src$$reg, Assembler::fcmps_opf, $src$$reg ); + // branch %fcc0 not-nan, predict taken + emit2_19( cbuf, Assembler::branch_op, 0/*annul*/, Assembler::f_ordered, Assembler::fbp_op2, Assembler::fcc0, 1/*predict taken*/, 4 ); + // fstox $src,$dst + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fstox_opf, $src$$reg ); + // fxtod $dst,$dst (if nan) + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fxtod_opf, $dst$$reg ); + // clear $dst (if nan) + emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, $dst$$reg, Assembler::fsubd_opf, $dst$$reg ); + // carry on here... + %} + + enc_class form3_opf_rs2F_rdF(regF rs2, regF rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg); %} + enc_class form3_opf_rs2F_rdD(regF rs2, regD rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg); %} + enc_class form3_opf_rs2D_rdF(regD rs2, regF rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg); %} + enc_class form3_opf_rs2D_rdD(regD rs2, regD rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg); %} + + enc_class form3_opf_rs2D_lo_rdF(regD rs2, regF rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg+1); %} + + enc_class form3_opf_rs2D_hi_rdD_hi(regD rs2, regD rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg); %} + enc_class form3_opf_rs2D_lo_rdD_lo(regD rs2, regD rd) %{ emit3(cbuf,$secondary,$rd$$reg+1,$primary,0,$tertiary,$rs2$$reg+1); %} + + enc_class form3_opf_rs1F_rs2F_rdF( regF rs1, regF rs2, regF rd ) %{ + emit3( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, $tertiary, $rs2$$reg ); + %} + + enc_class form3_opf_rs1D_rs2D_rdD( regD rs1, regD rs2, regD rd ) %{ + emit3( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, $tertiary, $rs2$$reg ); + %} + + enc_class form3_opf_rs1F_rs2F_fcc( regF rs1, regF rs2, flagsRegF fcc ) %{ + emit3( cbuf, $secondary, $fcc$$reg, $primary, $rs1$$reg, $tertiary, $rs2$$reg ); + %} + + enc_class form3_opf_rs1D_rs2D_fcc( regD rs1, regD rs2, flagsRegF fcc ) %{ + emit3( cbuf, $secondary, $fcc$$reg, $primary, $rs1$$reg, $tertiary, $rs2$$reg ); + %} + + enc_class form3_convI2F(regF rs2, regF rd) %{ + emit3(cbuf,Assembler::arith_op,$rd$$reg,Assembler::fpop1_op3,0,$secondary,$rs2$$reg); + %} + + // Encloding class for traceable jumps + enc_class form_jmpl(g3RegP dest) %{ + emit_jmpl(cbuf, $dest$$reg); + %} + + enc_class form_jmpl_set_exception_pc(g1RegP dest) %{ + emit_jmpl_set_exception_pc(cbuf, $dest$$reg); + %} + + enc_class form2_nop() %{ + emit_nop(cbuf); + %} + + enc_class form2_illtrap() %{ + emit_illtrap(cbuf); + %} + + + // Compare longs and convert into -1, 0, 1. + enc_class cmpl_flag( iRegL src1, iRegL src2, iRegI dst ) %{ + // CMP $src1,$src2 + emit3( cbuf, Assembler::arith_op, 0, Assembler::subcc_op3, $src1$$reg, 0, $src2$$reg ); + // blt,a,pn done + emit2_19( cbuf, Assembler::branch_op, 1/*annul*/, Assembler::less , Assembler::bp_op2, Assembler::xcc, 0/*predict not taken*/, 5 ); + // mov dst,-1 in delay slot + emit3_simm13( cbuf, Assembler::arith_op, $dst$$reg, Assembler::or_op3, 0, -1 ); + // bgt,a,pn done + emit2_19( cbuf, Assembler::branch_op, 1/*annul*/, Assembler::greater, Assembler::bp_op2, Assembler::xcc, 0/*predict not taken*/, 3 ); + // mov dst,1 in delay slot + emit3_simm13( cbuf, Assembler::arith_op, $dst$$reg, Assembler::or_op3, 0, 1 ); + // CLR $dst + emit3( cbuf, Assembler::arith_op, $dst$$reg, Assembler::or_op3 , 0, 0, 0 ); + %} + + enc_class enc_PartialSubtypeCheck() %{ + MacroAssembler _masm(&cbuf); + __ call(StubRoutines::Sparc::partial_subtype_check(), relocInfo::runtime_call_type); + __ delayed()->nop(); + %} + + enc_class enc_bp( Label labl, cmpOp cmp, flagsReg cc ) %{ + MacroAssembler _masm(&cbuf); + Label &L = *($labl$$label); + Assembler::Predict predict_taken = + cbuf.is_backward_branch(L) ? Assembler::pt : Assembler::pn; + + __ bp( (Assembler::Condition)($cmp$$cmpcode), false, Assembler::icc, predict_taken, L); + __ delayed()->nop(); + %} + + enc_class enc_bpl( Label labl, cmpOp cmp, flagsRegL cc ) %{ + MacroAssembler _masm(&cbuf); + Label &L = *($labl$$label); + Assembler::Predict predict_taken = + cbuf.is_backward_branch(L) ? Assembler::pt : Assembler::pn; + + __ bp( (Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, L); + __ delayed()->nop(); + %} + + enc_class enc_bpx( Label labl, cmpOp cmp, flagsRegP cc ) %{ + MacroAssembler _masm(&cbuf); + Label &L = *($labl$$label); + Assembler::Predict predict_taken = + cbuf.is_backward_branch(L) ? Assembler::pt : Assembler::pn; + + __ bp( (Assembler::Condition)($cmp$$cmpcode), false, Assembler::ptr_cc, predict_taken, L); + __ delayed()->nop(); + %} + + enc_class enc_fbp( Label labl, cmpOpF cmp, flagsRegF cc ) %{ + MacroAssembler _masm(&cbuf); + Label &L = *($labl$$label); + Assembler::Predict predict_taken = + cbuf.is_backward_branch(L) ? Assembler::pt : Assembler::pn; + + __ fbp( (Assembler::Condition)($cmp$$cmpcode), false, (Assembler::CC)($cc$$reg), predict_taken, L); + __ delayed()->nop(); + %} + + enc_class jump_enc( iRegX switch_val, o7RegI table) %{ + MacroAssembler _masm(&cbuf); + + Register switch_reg = as_Register($switch_val$$reg); + Register table_reg = O7; + + address table_base = __ address_table_constant(_index2label); + RelocationHolder rspec = internal_word_Relocation::spec(table_base); + + // Load table address + Address the_pc(table_reg, table_base, rspec); + __ load_address(the_pc); + + // Jump to base address + switch value + __ ld_ptr(table_reg, switch_reg, table_reg); + __ jmp(table_reg, G0); + __ delayed()->nop(); + + %} + + enc_class enc_ba( Label labl ) %{ + MacroAssembler _masm(&cbuf); + Label &L = *($labl$$label); + __ ba(false, L); + __ delayed()->nop(); + %} + + enc_class enc_bpr( Label labl, cmpOp_reg cmp, iRegI op1 ) %{ + MacroAssembler _masm(&cbuf); + Label &L = *$labl$$label; + Assembler::Predict predict_taken = + cbuf.is_backward_branch(L) ? Assembler::pt : Assembler::pn; + + __ bpr( (Assembler::RCondition)($cmp$$cmpcode), false, predict_taken, as_Register($op1$$reg), L); + __ delayed()->nop(); + %} + + enc_class enc_cmov_reg( cmpOp cmp, iRegI dst, iRegI src, immI pcc) %{ + int op = (Assembler::arith_op << 30) | + ($dst$$reg << 25) | + (Assembler::movcc_op3 << 19) | + (1 << 18) | // cc2 bit for 'icc' + ($cmp$$cmpcode << 14) | + (0 << 13) | // select register move + ($pcc$$constant << 11) | // cc1, cc0 bits for 'icc' or 'xcc' + ($src$$reg << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + enc_class enc_cmov_imm( cmpOp cmp, iRegI dst, immI11 src, immI pcc ) %{ + int simm11 = $src$$constant & ((1<<11)-1); // Mask to 11 bits + int op = (Assembler::arith_op << 30) | + ($dst$$reg << 25) | + (Assembler::movcc_op3 << 19) | + (1 << 18) | // cc2 bit for 'icc' + ($cmp$$cmpcode << 14) | + (1 << 13) | // select immediate move + ($pcc$$constant << 11) | // cc1, cc0 bits for 'icc' + (simm11 << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + enc_class enc_cmov_reg_f( cmpOpF cmp, iRegI dst, iRegI src, flagsRegF fcc ) %{ + int op = (Assembler::arith_op << 30) | + ($dst$$reg << 25) | + (Assembler::movcc_op3 << 19) | + (0 << 18) | // cc2 bit for 'fccX' + ($cmp$$cmpcode << 14) | + (0 << 13) | // select register move + ($fcc$$reg << 11) | // cc1, cc0 bits for fcc0-fcc3 + ($src$$reg << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + enc_class enc_cmov_imm_f( cmpOp cmp, iRegI dst, immI11 src, flagsRegF fcc ) %{ + int simm11 = $src$$constant & ((1<<11)-1); // Mask to 11 bits + int op = (Assembler::arith_op << 30) | + ($dst$$reg << 25) | + (Assembler::movcc_op3 << 19) | + (0 << 18) | // cc2 bit for 'fccX' + ($cmp$$cmpcode << 14) | + (1 << 13) | // select immediate move + ($fcc$$reg << 11) | // cc1, cc0 bits for fcc0-fcc3 + (simm11 << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + enc_class enc_cmovf_reg( cmpOp cmp, regD dst, regD src, immI pcc ) %{ + int op = (Assembler::arith_op << 30) | + ($dst$$reg << 25) | + (Assembler::fpop2_op3 << 19) | + (0 << 18) | + ($cmp$$cmpcode << 14) | + (1 << 13) | // select register move + ($pcc$$constant << 11) | // cc1-cc0 bits for 'icc' or 'xcc' + ($primary << 5) | // select single, double or quad + ($src$$reg << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + enc_class enc_cmovff_reg( cmpOpF cmp, flagsRegF fcc, regD dst, regD src ) %{ + int op = (Assembler::arith_op << 30) | + ($dst$$reg << 25) | + (Assembler::fpop2_op3 << 19) | + (0 << 18) | + ($cmp$$cmpcode << 14) | + ($fcc$$reg << 11) | // cc2-cc0 bits for 'fccX' + ($primary << 5) | // select single, double or quad + ($src$$reg << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + // Used by the MIN/MAX encodings. Same as a CMOV, but + // the condition comes from opcode-field instead of an argument. + enc_class enc_cmov_reg_minmax( iRegI dst, iRegI src ) %{ + int op = (Assembler::arith_op << 30) | + ($dst$$reg << 25) | + (Assembler::movcc_op3 << 19) | + (1 << 18) | // cc2 bit for 'icc' + ($primary << 14) | + (0 << 13) | // select register move + (0 << 11) | // cc1, cc0 bits for 'icc' + ($src$$reg << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + enc_class enc_cmov_reg_minmax_long( iRegL dst, iRegL src ) %{ + int op = (Assembler::arith_op << 30) | + ($dst$$reg << 25) | + (Assembler::movcc_op3 << 19) | + (6 << 16) | // cc2 bit for 'xcc' + ($primary << 14) | + (0 << 13) | // select register move + (0 << 11) | // cc1, cc0 bits for 'icc' + ($src$$reg << 0); + *((int*)(cbuf.code_end())) = op; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + // Utility encoding for loading a 64 bit Pointer into a register + // The 64 bit pointer is stored in the generated code stream + enc_class SetPtr( immP src, iRegP rd ) %{ + Register dest = reg_to_register_object($rd$$reg); + // [RGV] This next line should be generated from ADLC + if ( _opnds[1]->constant_is_oop() ) { + intptr_t val = $src$$constant; + MacroAssembler _masm(&cbuf); + __ set_oop_constant((jobject)val, dest); + } else { // non-oop pointers, e.g. card mark base, heap top + emit_ptr(cbuf, $src$$constant, dest, /*ForceRelocatable=*/ false); + } + %} + + enc_class Set13( immI13 src, iRegI rd ) %{ + emit3_simm13( cbuf, Assembler::arith_op, $rd$$reg, Assembler::or_op3, 0, $src$$constant ); + %} + + enc_class SetHi22( immI src, iRegI rd ) %{ + emit2_22( cbuf, Assembler::branch_op, $rd$$reg, Assembler::sethi_op2, $src$$constant ); + %} + + enc_class Set32( immI src, iRegI rd ) %{ + MacroAssembler _masm(&cbuf); + __ set($src$$constant, reg_to_register_object($rd$$reg)); + %} + + enc_class SetNull( iRegI rd ) %{ + emit3_simm13( cbuf, Assembler::arith_op, $rd$$reg, Assembler::or_op3, 0, 0 ); + %} + + enc_class call_epilog %{ + if( VerifyStackAtCalls ) { + MacroAssembler _masm(&cbuf); + int framesize = ra_->C->frame_slots() << LogBytesPerInt; + Register temp_reg = G3; + __ add(SP, framesize, temp_reg); + __ cmp(temp_reg, FP); + __ breakpoint_trap(Assembler::notEqual, Assembler::ptr_cc); + } + %} + + // Long values come back from native calls in O0:O1 in the 32-bit VM, copy the value + // to G1 so the register allocator will not have to deal with the misaligned register + // pair. + enc_class adjust_long_from_native_call %{ +#ifndef _LP64 + if (returns_long()) { + // sllx O0,32,O0 + emit3_simm13( cbuf, Assembler::arith_op, R_O0_enc, Assembler::sllx_op3, R_O0_enc, 0x1020 ); + // srl O1,0,O1 + emit3_simm13( cbuf, Assembler::arith_op, R_O1_enc, Assembler::srl_op3, R_O1_enc, 0x0000 ); + // or O0,O1,G1 + emit3 ( cbuf, Assembler::arith_op, R_G1_enc, Assembler:: or_op3, R_O0_enc, 0, R_O1_enc ); + } +#endif + %} + + enc_class Java_To_Runtime (method meth) %{ // CALL Java_To_Runtime + // CALL directly to the runtime + // The user of this is responsible for ensuring that R_L7 is empty (killed). + emit_call_reloc(cbuf, $meth$$method, relocInfo::runtime_call_type, + /*preserve_g2=*/true, /*force far call*/true); + %} + + enc_class Java_Static_Call (method meth) %{ // JAVA STATIC CALL + // CALL to fixup routine. Fixup routine uses ScopeDesc info to determine + // who we intended to call. + if ( !_method ) { + emit_call_reloc(cbuf, $meth$$method, relocInfo::runtime_call_type); + } else if (_optimized_virtual) { + emit_call_reloc(cbuf, $meth$$method, relocInfo::opt_virtual_call_type); + } else { + emit_call_reloc(cbuf, $meth$$method, relocInfo::static_call_type); + } + if( _method ) { // Emit stub for static call + emit_java_to_interp(cbuf); + } + %} + + enc_class Java_Dynamic_Call (method meth) %{ // JAVA DYNAMIC CALL + MacroAssembler _masm(&cbuf); + __ set_inst_mark(); + int vtable_index = this->_vtable_index; + // MachCallDynamicJavaNode::ret_addr_offset uses this same test + if (vtable_index < 0) { + // must be invalid_vtable_index, not nonvirtual_vtable_index + assert(vtable_index == methodOopDesc::invalid_vtable_index, "correct sentinel value"); + Register G5_ic_reg = reg_to_register_object(Matcher::inline_cache_reg_encode()); + assert(G5_ic_reg == G5_inline_cache_reg, "G5_inline_cache_reg used in assemble_ic_buffer_code()"); + assert(G5_ic_reg == G5_megamorphic_method, "G5_megamorphic_method used in megamorphic call stub"); + // !!!!! + // Generate "set 0x01, R_G5", placeholder instruction to load oop-info + // emit_call_dynamic_prologue( cbuf ); + __ set_oop((jobject)Universe::non_oop_word(), G5_ic_reg); + + address virtual_call_oop_addr = __ inst_mark(); + // CALL to fixup routine. Fixup routine uses ScopeDesc info to determine + // who we intended to call. + __ relocate(virtual_call_Relocation::spec(virtual_call_oop_addr)); + emit_call_reloc(cbuf, $meth$$method, relocInfo::none); + } else { + assert(!UseInlineCaches, "expect vtable calls only if not using ICs"); + // Just go thru the vtable + // get receiver klass (receiver already checked for non-null) + // If we end up going thru a c2i adapter interpreter expects method in G5 + int off = __ offset(); + __ ld_ptr(O0, oopDesc::klass_offset_in_bytes(), G3_scratch); + int entry_offset = instanceKlass::vtable_start_offset() + vtable_index*vtableEntry::size(); + int v_off = entry_offset*wordSize + vtableEntry::method_offset_in_bytes(); + if( __ is_simm13(v_off) ) { + __ ld_ptr(G3, v_off, G5_method); + } else { + // Generate 2 instructions + __ Assembler::sethi(v_off & ~0x3ff, G5_method); + __ or3(G5_method, v_off & 0x3ff, G5_method); + // ld_ptr, set_hi, set + assert(__ offset() - off == 3*BytesPerInstWord, "Unexpected instruction size(s)"); + __ ld_ptr(G3, G5_method, G5_method); + } + // NOTE: for vtable dispatches, the vtable entry will never be null. + // However it may very well end up in handle_wrong_method if the + // method is abstract for the particular class. + __ ld_ptr(G5_method, in_bytes(methodOopDesc::from_compiled_offset()), G3_scratch); + // jump to target (either compiled code or c2iadapter) + __ jmpl(G3_scratch, G0, O7); + __ delayed()->nop(); + } + %} + + enc_class Java_Compiled_Call (method meth) %{ // JAVA COMPILED CALL + MacroAssembler _masm(&cbuf); + + Register G5_ic_reg = reg_to_register_object(Matcher::inline_cache_reg_encode()); + Register temp_reg = G3; // caller must kill G3! We cannot reuse G5_ic_reg here because + // we might be calling a C2I adapter which needs it. + + assert(temp_reg != G5_ic_reg, "conflicting registers"); + // Load nmethod + __ ld_ptr(G5_ic_reg, in_bytes(methodOopDesc::from_compiled_offset()), temp_reg); + + // CALL to compiled java, indirect the contents of G3 + __ set_inst_mark(); + __ callr(temp_reg, G0); + __ delayed()->nop(); + %} + +enc_class idiv_reg(iRegIsafe src1, iRegIsafe src2, iRegIsafe dst) %{ + MacroAssembler _masm(&cbuf); + Register Rdividend = reg_to_register_object($src1$$reg); + Register Rdivisor = reg_to_register_object($src2$$reg); + Register Rresult = reg_to_register_object($dst$$reg); + + __ sra(Rdivisor, 0, Rdivisor); + __ sra(Rdividend, 0, Rdividend); + __ sdivx(Rdividend, Rdivisor, Rresult); +%} + +enc_class idiv_imm(iRegIsafe src1, immI13 imm, iRegIsafe dst) %{ + MacroAssembler _masm(&cbuf); + + Register Rdividend = reg_to_register_object($src1$$reg); + int divisor = $imm$$constant; + Register Rresult = reg_to_register_object($dst$$reg); + + __ sra(Rdividend, 0, Rdividend); + __ sdivx(Rdividend, divisor, Rresult); +%} + +enc_class enc_mul_hi(iRegIsafe dst, iRegIsafe src1, iRegIsafe src2) %{ + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + Register Rdst = reg_to_register_object($dst$$reg); + + __ sra( Rsrc1, 0, Rsrc1 ); + __ sra( Rsrc2, 0, Rsrc2 ); + __ mulx( Rsrc1, Rsrc2, Rdst ); + __ srlx( Rdst, 32, Rdst ); +%} + +enc_class irem_reg(iRegIsafe src1, iRegIsafe src2, iRegIsafe dst, o7RegL scratch) %{ + MacroAssembler _masm(&cbuf); + Register Rdividend = reg_to_register_object($src1$$reg); + Register Rdivisor = reg_to_register_object($src2$$reg); + Register Rresult = reg_to_register_object($dst$$reg); + Register Rscratch = reg_to_register_object($scratch$$reg); + + assert(Rdividend != Rscratch, ""); + assert(Rdivisor != Rscratch, ""); + + __ sra(Rdividend, 0, Rdividend); + __ sra(Rdivisor, 0, Rdivisor); + __ sdivx(Rdividend, Rdivisor, Rscratch); + __ mulx(Rscratch, Rdivisor, Rscratch); + __ sub(Rdividend, Rscratch, Rresult); +%} + +enc_class irem_imm(iRegIsafe src1, immI13 imm, iRegIsafe dst, o7RegL scratch) %{ + MacroAssembler _masm(&cbuf); + + Register Rdividend = reg_to_register_object($src1$$reg); + int divisor = $imm$$constant; + Register Rresult = reg_to_register_object($dst$$reg); + Register Rscratch = reg_to_register_object($scratch$$reg); + + assert(Rdividend != Rscratch, ""); + + __ sra(Rdividend, 0, Rdividend); + __ sdivx(Rdividend, divisor, Rscratch); + __ mulx(Rscratch, divisor, Rscratch); + __ sub(Rdividend, Rscratch, Rresult); +%} + +enc_class fabss (sflt_reg dst, sflt_reg src) %{ + MacroAssembler _masm(&cbuf); + + FloatRegister Fdst = reg_to_SingleFloatRegister_object($dst$$reg); + FloatRegister Fsrc = reg_to_SingleFloatRegister_object($src$$reg); + + __ fabs(FloatRegisterImpl::S, Fsrc, Fdst); +%} + +enc_class fabsd (dflt_reg dst, dflt_reg src) %{ + MacroAssembler _masm(&cbuf); + + FloatRegister Fdst = reg_to_DoubleFloatRegister_object($dst$$reg); + FloatRegister Fsrc = reg_to_DoubleFloatRegister_object($src$$reg); + + __ fabs(FloatRegisterImpl::D, Fsrc, Fdst); +%} + +enc_class fnegd (dflt_reg dst, dflt_reg src) %{ + MacroAssembler _masm(&cbuf); + + FloatRegister Fdst = reg_to_DoubleFloatRegister_object($dst$$reg); + FloatRegister Fsrc = reg_to_DoubleFloatRegister_object($src$$reg); + + __ fneg(FloatRegisterImpl::D, Fsrc, Fdst); +%} + +enc_class fsqrts (sflt_reg dst, sflt_reg src) %{ + MacroAssembler _masm(&cbuf); + + FloatRegister Fdst = reg_to_SingleFloatRegister_object($dst$$reg); + FloatRegister Fsrc = reg_to_SingleFloatRegister_object($src$$reg); + + __ fsqrt(FloatRegisterImpl::S, Fsrc, Fdst); +%} + +enc_class fsqrtd (dflt_reg dst, dflt_reg src) %{ + MacroAssembler _masm(&cbuf); + + FloatRegister Fdst = reg_to_DoubleFloatRegister_object($dst$$reg); + FloatRegister Fsrc = reg_to_DoubleFloatRegister_object($src$$reg); + + __ fsqrt(FloatRegisterImpl::D, Fsrc, Fdst); +%} + +enc_class fmovs (dflt_reg dst, dflt_reg src) %{ + MacroAssembler _masm(&cbuf); + + FloatRegister Fdst = reg_to_SingleFloatRegister_object($dst$$reg); + FloatRegister Fsrc = reg_to_SingleFloatRegister_object($src$$reg); + + __ fmov(FloatRegisterImpl::S, Fsrc, Fdst); +%} + +enc_class fmovd (dflt_reg dst, dflt_reg src) %{ + MacroAssembler _masm(&cbuf); + + FloatRegister Fdst = reg_to_DoubleFloatRegister_object($dst$$reg); + FloatRegister Fsrc = reg_to_DoubleFloatRegister_object($src$$reg); + + __ fmov(FloatRegisterImpl::D, Fsrc, Fdst); +%} + +enc_class Fast_Lock(iRegP oop, iRegP box, o7RegP scratch, iRegP scratch2) %{ + MacroAssembler _masm(&cbuf); + + Register Roop = reg_to_register_object($oop$$reg); + Register Rbox = reg_to_register_object($box$$reg); + Register Rscratch = reg_to_register_object($scratch$$reg); + Register Rmark = reg_to_register_object($scratch2$$reg); + + assert(Roop != Rscratch, ""); + assert(Roop != Rmark, ""); + assert(Rbox != Rscratch, ""); + assert(Rbox != Rmark, ""); + + __ compiler_lock_object(Roop, Rmark, Rbox, Rscratch, _counters); +%} + +enc_class Fast_Unlock(iRegP oop, iRegP box, o7RegP scratch, iRegP scratch2) %{ + MacroAssembler _masm(&cbuf); + + Register Roop = reg_to_register_object($oop$$reg); + Register Rbox = reg_to_register_object($box$$reg); + Register Rscratch = reg_to_register_object($scratch$$reg); + Register Rmark = reg_to_register_object($scratch2$$reg); + + assert(Roop != Rscratch, ""); + assert(Roop != Rmark, ""); + assert(Rbox != Rscratch, ""); + assert(Rbox != Rmark, ""); + + __ compiler_unlock_object(Roop, Rmark, Rbox, Rscratch); + %} + + enc_class enc_cas( iRegP mem, iRegP old, iRegP new ) %{ + MacroAssembler _masm(&cbuf); + Register Rmem = reg_to_register_object($mem$$reg); + Register Rold = reg_to_register_object($old$$reg); + Register Rnew = reg_to_register_object($new$$reg); + + // casx_under_lock picks 1 of 3 encodings: + // For 32-bit pointers you get a 32-bit CAS + // For 64-bit pointers you get a 64-bit CASX + __ casx_under_lock(Rmem, Rold, Rnew, // Swap(*Rmem,Rnew) if *Rmem == Rold + (address) StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + __ cmp( Rold, Rnew ); + %} + + enc_class enc_casx( iRegP mem, iRegL old, iRegL new) %{ + Register Rmem = reg_to_register_object($mem$$reg); + Register Rold = reg_to_register_object($old$$reg); + Register Rnew = reg_to_register_object($new$$reg); + + MacroAssembler _masm(&cbuf); + __ mov(Rnew, O7); + __ casx(Rmem, Rold, O7); + __ cmp( Rold, O7 ); + %} + + // raw int cas, used for compareAndSwap + enc_class enc_casi( iRegP mem, iRegL old, iRegL new) %{ + Register Rmem = reg_to_register_object($mem$$reg); + Register Rold = reg_to_register_object($old$$reg); + Register Rnew = reg_to_register_object($new$$reg); + + MacroAssembler _masm(&cbuf); + __ mov(Rnew, O7); + __ cas(Rmem, Rold, O7); + __ cmp( Rold, O7 ); + %} + + enc_class enc_lflags_ne_to_boolean( iRegI res ) %{ + Register Rres = reg_to_register_object($res$$reg); + + MacroAssembler _masm(&cbuf); + __ mov(1, Rres); + __ movcc( Assembler::notEqual, false, Assembler::xcc, G0, Rres ); + %} + + enc_class enc_iflags_ne_to_boolean( iRegI res ) %{ + Register Rres = reg_to_register_object($res$$reg); + + MacroAssembler _masm(&cbuf); + __ mov(1, Rres); + __ movcc( Assembler::notEqual, false, Assembler::icc, G0, Rres ); + %} + + enc_class floating_cmp ( iRegP dst, regF src1, regF src2 ) %{ + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + FloatRegister Fsrc1 = $primary ? reg_to_SingleFloatRegister_object($src1$$reg) + : reg_to_DoubleFloatRegister_object($src1$$reg); + FloatRegister Fsrc2 = $primary ? reg_to_SingleFloatRegister_object($src2$$reg) + : reg_to_DoubleFloatRegister_object($src2$$reg); + + // Convert condition code fcc0 into -1,0,1; unordered reports less-than (-1) + __ float_cmp( $primary, -1, Fsrc1, Fsrc2, Rdst); + %} + + enc_class LdImmL (immL src, iRegL dst, o7RegL tmp) %{ // Load Immediate + MacroAssembler _masm(&cbuf); + Register dest = reg_to_register_object($dst$$reg); + Register temp = reg_to_register_object($tmp$$reg); + __ set64( $src$$constant, dest, temp ); + %} + + enc_class LdImmF(immF src, regF dst, o7RegP tmp) %{ // Load Immediate + address float_address = MacroAssembler(&cbuf).float_constant($src$$constant); + RelocationHolder rspec = internal_word_Relocation::spec(float_address); +#ifdef _LP64 + Register tmp_reg = reg_to_register_object($tmp$$reg); + cbuf.relocate(cbuf.code_end(), rspec, 0); + emit_ptr(cbuf, (intptr_t)float_address, tmp_reg, /*ForceRelocatable=*/ true); + emit3_simm10( cbuf, Assembler::ldst_op, $dst$$reg, Assembler::ldf_op3, $tmp$$reg, 0 ); +#else // _LP64 + uint *code; + int tmp_reg = $tmp$$reg; + + cbuf.relocate(cbuf.code_end(), rspec, 0); + emit2_22( cbuf, Assembler::branch_op, tmp_reg, Assembler::sethi_op2, (intptr_t) float_address ); + + cbuf.relocate(cbuf.code_end(), rspec, 0); + emit3_simm10( cbuf, Assembler::ldst_op, $dst$$reg, Assembler::ldf_op3, tmp_reg, (intptr_t) float_address ); +#endif // _LP64 + %} + + enc_class LdImmD(immD src, regD dst, o7RegP tmp) %{ // Load Immediate + address double_address = MacroAssembler(&cbuf).double_constant($src$$constant); + RelocationHolder rspec = internal_word_Relocation::spec(double_address); +#ifdef _LP64 + Register tmp_reg = reg_to_register_object($tmp$$reg); + cbuf.relocate(cbuf.code_end(), rspec, 0); + emit_ptr(cbuf, (intptr_t)double_address, tmp_reg, /*ForceRelocatable=*/ true); + emit3_simm10( cbuf, Assembler::ldst_op, $dst$$reg, Assembler::lddf_op3, $tmp$$reg, 0 ); +#else // _LP64 + uint *code; + int tmp_reg = $tmp$$reg; + + cbuf.relocate(cbuf.code_end(), rspec, 0); + emit2_22( cbuf, Assembler::branch_op, tmp_reg, Assembler::sethi_op2, (intptr_t) double_address ); + + cbuf.relocate(cbuf.code_end(), rspec, 0); + emit3_simm10( cbuf, Assembler::ldst_op, $dst$$reg, Assembler::lddf_op3, tmp_reg, (intptr_t) double_address ); +#endif // _LP64 + %} + + enc_class LdReplImmI(immI src, regD dst, o7RegP tmp, int count, int width) %{ + // Load a constant replicated "count" times with width "width" + int bit_width = $width$$constant * 8; + jlong elt_val = $src$$constant; + elt_val &= (((jlong)1) << bit_width) - 1; // mask off sign bits + jlong val = elt_val; + for (int i = 0; i < $count$$constant - 1; i++) { + val <<= bit_width; + val |= elt_val; + } + jdouble dval = *(jdouble*)&val; // coerce to double type + address double_address = MacroAssembler(&cbuf).double_constant(dval); + RelocationHolder rspec = internal_word_Relocation::spec(double_address); +#ifdef _LP64 + Register tmp_reg = reg_to_register_object($tmp$$reg); + cbuf.relocate(cbuf.code_end(), rspec, 0); + emit_ptr(cbuf, (intptr_t)double_address, tmp_reg, /*ForceRelocatable=*/ true); + emit3_simm10( cbuf, Assembler::ldst_op, $dst$$reg, Assembler::lddf_op3, $tmp$$reg, 0 ); +#else // _LP64 + uint *code; + int tmp_reg = $tmp$$reg; + + cbuf.relocate(cbuf.code_end(), rspec, 0); + emit2_22( cbuf, Assembler::branch_op, tmp_reg, Assembler::sethi_op2, (intptr_t) double_address ); + + cbuf.relocate(cbuf.code_end(), rspec, 0); + emit3_simm10( cbuf, Assembler::ldst_op, $dst$$reg, Assembler::lddf_op3, tmp_reg, (intptr_t) double_address ); +#endif // _LP64 + %} + + + enc_class ShouldNotEncodeThis ( ) %{ + ShouldNotCallThis(); + %} + + // Compiler ensures base is doubleword aligned and cnt is count of doublewords + enc_class enc_Clear_Array(iRegX cnt, iRegP base, iRegX temp) %{ + MacroAssembler _masm(&cbuf); + Register nof_bytes_arg = reg_to_register_object($cnt$$reg); + Register nof_bytes_tmp = reg_to_register_object($temp$$reg); + Register base_pointer_arg = reg_to_register_object($base$$reg); + + Label loop; + __ mov(nof_bytes_arg, nof_bytes_tmp); + + // Loop and clear, walking backwards through the array. + // nof_bytes_tmp (if >0) is always the number of bytes to zero + __ bind(loop); + __ deccc(nof_bytes_tmp, 8); + __ br(Assembler::greaterEqual, true, Assembler::pt, loop); + __ delayed()-> stx(G0, base_pointer_arg, nof_bytes_tmp); + // %%%% this mini-loop must not cross a cache boundary! + %} + + + enc_class enc_String_Compare(o0RegP str1, o1RegP str2, g3RegP tmp1, g4RegP tmp2, notemp_iRegI result) %{ + Label Ldone, Lloop; + MacroAssembler _masm(&cbuf); + + Register str1_reg = reg_to_register_object($str1$$reg); + Register str2_reg = reg_to_register_object($str2$$reg); + Register tmp1_reg = reg_to_register_object($tmp1$$reg); + Register tmp2_reg = reg_to_register_object($tmp2$$reg); + Register result_reg = reg_to_register_object($result$$reg); + + // Get the first character position in both strings + // [8] char array, [12] offset, [16] count + int value_offset = java_lang_String:: value_offset_in_bytes(); + int offset_offset = java_lang_String::offset_offset_in_bytes(); + int count_offset = java_lang_String:: count_offset_in_bytes(); + + // load str1 (jchar*) base address into tmp1_reg + __ ld_ptr(Address(str1_reg, 0, value_offset), tmp1_reg); + __ ld(Address(str1_reg, 0, offset_offset), result_reg); + __ add(tmp1_reg, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp1_reg); + __ ld(Address(str1_reg, 0, count_offset), str1_reg); // hoisted + __ sll(result_reg, exact_log2(sizeof(jchar)), result_reg); + __ ld_ptr(Address(str2_reg, 0, value_offset), tmp2_reg); // hoisted + __ add(result_reg, tmp1_reg, tmp1_reg); + + // load str2 (jchar*) base address into tmp2_reg + // __ ld_ptr(Address(str2_reg, 0, value_offset), tmp2_reg); // hoisted + __ ld(Address(str2_reg, 0, offset_offset), result_reg); + __ add(tmp2_reg, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp2_reg); + __ ld(Address(str2_reg, 0, count_offset), str2_reg); // hoisted + __ sll(result_reg, exact_log2(sizeof(jchar)), result_reg); + __ subcc(str1_reg, str2_reg, O7); // hoisted + __ add(result_reg, tmp2_reg, tmp2_reg); + + // Compute the minimum of the string lengths(str1_reg) and the + // difference of the string lengths (stack) + + // discard string base pointers, after loading up the lengths + // __ ld(Address(str1_reg, 0, count_offset), str1_reg); // hoisted + // __ ld(Address(str2_reg, 0, count_offset), str2_reg); // hoisted + + // See if the lengths are different, and calculate min in str1_reg. + // Stash diff in O7 in case we need it for a tie-breaker. + Label Lskip; + // __ subcc(str1_reg, str2_reg, O7); // hoisted + __ sll(str1_reg, exact_log2(sizeof(jchar)), str1_reg); // scale the limit + __ br(Assembler::greater, true, Assembler::pt, Lskip); + // str2 is shorter, so use its count: + __ delayed()->sll(str2_reg, exact_log2(sizeof(jchar)), str1_reg); // scale the limit + __ bind(Lskip); + + // reallocate str1_reg, str2_reg, result_reg + // Note: limit_reg holds the string length pre-scaled by 2 + Register limit_reg = str1_reg; + Register chr2_reg = str2_reg; + Register chr1_reg = result_reg; + // tmp{12} are the base pointers + + // Is the minimum length zero? + __ cmp(limit_reg, (int)(0 * sizeof(jchar))); // use cast to resolve overloading ambiguity + __ br(Assembler::equal, true, Assembler::pn, Ldone); + __ delayed()->mov(O7, result_reg); // result is difference in lengths + + // Load first characters + __ lduh(tmp1_reg, 0, chr1_reg); + __ lduh(tmp2_reg, 0, chr2_reg); + + // Compare first characters + __ subcc(chr1_reg, chr2_reg, chr1_reg); + __ br(Assembler::notZero, false, Assembler::pt, Ldone); + assert(chr1_reg == result_reg, "result must be pre-placed"); + __ delayed()->nop(); + + { + // Check after comparing first character to see if strings are equivalent + Label LSkip2; + // Check if the strings start at same location + __ cmp(tmp1_reg, tmp2_reg); + __ brx(Assembler::notEqual, true, Assembler::pt, LSkip2); + __ delayed()->nop(); + + // Check if the length difference is zero (in O7) + __ cmp(G0, O7); + __ br(Assembler::equal, true, Assembler::pn, Ldone); + __ delayed()->mov(G0, result_reg); // result is zero + + // Strings might not be equal + __ bind(LSkip2); + } + + __ subcc(limit_reg, 1 * sizeof(jchar), chr1_reg); + __ br(Assembler::equal, true, Assembler::pn, Ldone); + __ delayed()->mov(O7, result_reg); // result is difference in lengths + + // Shift tmp1_reg and tmp2_reg to the end of the arrays, negate limit + __ add(tmp1_reg, limit_reg, tmp1_reg); + __ add(tmp2_reg, limit_reg, tmp2_reg); + __ neg(chr1_reg, limit_reg); // limit = -(limit-2) + + // Compare the rest of the characters + __ lduh(tmp1_reg, limit_reg, chr1_reg); + __ bind(Lloop); + // __ lduh(tmp1_reg, limit_reg, chr1_reg); // hoisted + __ lduh(tmp2_reg, limit_reg, chr2_reg); + __ subcc(chr1_reg, chr2_reg, chr1_reg); + __ br(Assembler::notZero, false, Assembler::pt, Ldone); + assert(chr1_reg == result_reg, "result must be pre-placed"); + __ delayed()->inccc(limit_reg, sizeof(jchar)); + // annul LDUH if branch is not taken to prevent access past end of string + __ br(Assembler::notZero, true, Assembler::pt, Lloop); + __ delayed()->lduh(tmp1_reg, limit_reg, chr1_reg); // hoisted + + // If strings are equal up to min length, return the length difference. + __ mov(O7, result_reg); + + // Otherwise, return the difference between the first mismatched chars. + __ bind(Ldone); + %} + + enc_class enc_rethrow() %{ + cbuf.set_inst_mark(); + Register temp_reg = G3; + Address rethrow_stub(temp_reg, OptoRuntime::rethrow_stub()); + assert(temp_reg != reg_to_register_object(R_I0_num), "temp must not break oop_reg"); + MacroAssembler _masm(&cbuf); +#ifdef ASSERT + __ save_frame(0); + Address last_rethrow_addr(L1, (address)&last_rethrow); + __ sethi(last_rethrow_addr); + __ get_pc(L2); + __ inc(L2, 3 * BytesPerInstWord); // skip this & 2 more insns to point at jump_to + __ st_ptr(L2, last_rethrow_addr); + __ restore(); +#endif + __ JUMP(rethrow_stub, 0); // sethi;jmp + __ delayed()->nop(); + %} + + enc_class emit_mem_nop() %{ + // Generates the instruction LDUXA [o6,g0],#0x82,g0 + unsigned int *code = (unsigned int*)cbuf.code_end(); + *code = (unsigned int)0xc0839040; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + enc_class emit_fadd_nop() %{ + // Generates the instruction FMOVS f31,f31 + unsigned int *code = (unsigned int*)cbuf.code_end(); + *code = (unsigned int)0xbfa0003f; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + enc_class emit_br_nop() %{ + // Generates the instruction BPN,PN . + unsigned int *code = (unsigned int*)cbuf.code_end(); + *code = (unsigned int)0x00400000; + cbuf.set_code_end(cbuf.code_end() + BytesPerInstWord); + %} + + enc_class enc_membar_acquire %{ + MacroAssembler _masm(&cbuf); + __ membar( Assembler::Membar_mask_bits(Assembler::LoadStore | Assembler::LoadLoad) ); + %} + + enc_class enc_membar_release %{ + MacroAssembler _masm(&cbuf); + __ membar( Assembler::Membar_mask_bits(Assembler::LoadStore | Assembler::StoreStore) ); + %} + + enc_class enc_membar_volatile %{ + MacroAssembler _masm(&cbuf); + __ membar( Assembler::Membar_mask_bits(Assembler::StoreLoad) ); + %} + enc_class enc_repl8b( iRegI src, iRegL dst ) %{ + MacroAssembler _masm(&cbuf); + Register src_reg = reg_to_register_object($src$$reg); + Register dst_reg = reg_to_register_object($dst$$reg); + __ sllx(src_reg, 56, dst_reg); + __ srlx(dst_reg, 8, O7); + __ or3 (dst_reg, O7, dst_reg); + __ srlx(dst_reg, 16, O7); + __ or3 (dst_reg, O7, dst_reg); + __ srlx(dst_reg, 32, O7); + __ or3 (dst_reg, O7, dst_reg); + %} + + enc_class enc_repl4b( iRegI src, iRegL dst ) %{ + MacroAssembler _masm(&cbuf); + Register src_reg = reg_to_register_object($src$$reg); + Register dst_reg = reg_to_register_object($dst$$reg); + __ sll(src_reg, 24, dst_reg); + __ srl(dst_reg, 8, O7); + __ or3(dst_reg, O7, dst_reg); + __ srl(dst_reg, 16, O7); + __ or3(dst_reg, O7, dst_reg); + %} + + enc_class enc_repl4s( iRegI src, iRegL dst ) %{ + MacroAssembler _masm(&cbuf); + Register src_reg = reg_to_register_object($src$$reg); + Register dst_reg = reg_to_register_object($dst$$reg); + __ sllx(src_reg, 48, dst_reg); + __ srlx(dst_reg, 16, O7); + __ or3 (dst_reg, O7, dst_reg); + __ srlx(dst_reg, 32, O7); + __ or3 (dst_reg, O7, dst_reg); + %} + + enc_class enc_repl2i( iRegI src, iRegL dst ) %{ + MacroAssembler _masm(&cbuf); + Register src_reg = reg_to_register_object($src$$reg); + Register dst_reg = reg_to_register_object($dst$$reg); + __ sllx(src_reg, 32, dst_reg); + __ srlx(dst_reg, 32, O7); + __ or3 (dst_reg, O7, dst_reg); + %} + +%} + +//----------FRAME-------------------------------------------------------------- +// Definition of frame structure and management information. +// +// S T A C K L A Y O U T Allocators stack-slot number +// | (to get allocators register number +// G Owned by | | v add VMRegImpl::stack0) +// r CALLER | | +// o | +--------+ pad to even-align allocators stack-slot +// w V | pad0 | numbers; owned by CALLER +// t -----------+--------+----> Matcher::_in_arg_limit, unaligned +// h ^ | in | 5 +// | | args | 4 Holes in incoming args owned by SELF +// | | | | 3 +// | | +--------+ +// V | | old out| Empty on Intel, window on Sparc +// | old |preserve| Must be even aligned. +// | SP-+--------+----> Matcher::_old_SP, 8 (or 16 in LP64)-byte aligned +// | | in | 3 area for Intel ret address +// Owned by |preserve| Empty on Sparc. +// SELF +--------+ +// | | pad2 | 2 pad to align old SP +// | +--------+ 1 +// | | locks | 0 +// | +--------+----> VMRegImpl::stack0, 8 (or 16 in LP64)-byte aligned +// | | pad1 | 11 pad to align new SP +// | +--------+ +// | | | 10 +// | | spills | 9 spills +// V | | 8 (pad0 slot for callee) +// -----------+--------+----> Matcher::_out_arg_limit, unaligned +// ^ | out | 7 +// | | args | 6 Holes in outgoing args owned by CALLEE +// Owned by +--------+ +// CALLEE | new out| 6 Empty on Intel, window on Sparc +// | new |preserve| Must be even-aligned. +// | SP-+--------+----> Matcher::_new_SP, even aligned +// | | | +// +// Note 1: Only region 8-11 is determined by the allocator. Region 0-5 is +// known from SELF's arguments and the Java calling convention. +// Region 6-7 is determined per call site. +// Note 2: If the calling convention leaves holes in the incoming argument +// area, those holes are owned by SELF. Holes in the outgoing area +// are owned by the CALLEE. Holes should not be nessecary in the +// incoming area, as the Java calling convention is completely under +// the control of the AD file. Doubles can be sorted and packed to +// avoid holes. Holes in the outgoing arguments may be nessecary for +// varargs C calling conventions. +// Note 3: Region 0-3 is even aligned, with pad2 as needed. Region 3-5 is +// even aligned with pad0 as needed. +// Region 6 is even aligned. Region 6-7 is NOT even aligned; +// region 6-11 is even aligned; it may be padded out more so that +// the region from SP to FP meets the minimum stack alignment. + +frame %{ + // What direction does stack grow in (assumed to be same for native & Java) + stack_direction(TOWARDS_LOW); + + // These two registers define part of the calling convention + // between compiled code and the interpreter. + inline_cache_reg(R_G5); // Inline Cache Register or methodOop for I2C + interpreter_method_oop_reg(R_G5); // Method Oop Register when calling interpreter + + // Optional: name the operand used by cisc-spilling to access [stack_pointer + offset] + cisc_spilling_operand_name(indOffset); + + // Number of stack slots consumed by a Monitor enter +#ifdef _LP64 + sync_stack_slots(2); +#else + sync_stack_slots(1); +#endif + + // Compiled code's Frame Pointer + frame_pointer(R_SP); + + // Stack alignment requirement + stack_alignment(StackAlignmentInBytes); + // LP64: Alignment size in bytes (128-bit -> 16 bytes) + // !LP64: Alignment size in bytes (64-bit -> 8 bytes) + + // Number of stack slots between incoming argument block and the start of + // a new frame. The PROLOG must add this many slots to the stack. The + // EPILOG must remove this many slots. + in_preserve_stack_slots(0); + + // Number of outgoing stack slots killed above the out_preserve_stack_slots + // for calls to C. Supports the var-args backing area for register parms. + // ADLC doesn't support parsing expressions, so I folded the math by hand. +#ifdef _LP64 + // (callee_register_argument_save_area_words (6) + callee_aggregate_return_pointer_words (0)) * 2-stack-slots-per-word + varargs_C_out_slots_killed(12); +#else + // (callee_register_argument_save_area_words (6) + callee_aggregate_return_pointer_words (1)) * 1-stack-slots-per-word + varargs_C_out_slots_killed( 7); +#endif + + // The after-PROLOG location of the return address. Location of + // return address specifies a type (REG or STACK) and a number + // representing the register number (i.e. - use a register name) or + // stack slot. + return_addr(REG R_I7); // Ret Addr is in register I7 + + // Body of function which returns an OptoRegs array locating + // arguments either in registers or in stack slots for calling + // java + calling_convention %{ + (void) SharedRuntime::java_calling_convention(sig_bt, regs, length, is_outgoing); + + %} + + // Body of function which returns an OptoRegs array locating + // arguments either in registers or in stack slots for callin + // C. + c_calling_convention %{ + // This is obviously always outgoing + (void) SharedRuntime::c_calling_convention(sig_bt, regs, length); + %} + + // Location of native (C/C++) and interpreter return values. This is specified to + // be the same as Java. In the 32-bit VM, long values are actually returned from + // native calls in O0:O1 and returned to the interpreter in I0:I1. The copying + // to and from the register pairs is done by the appropriate call and epilog + // opcodes. This simplifies the register allocator. + c_return_value %{ + assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" ); +#ifdef _LP64 + static int lo_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_O0_num, R_O0_num, R_F0_num, R_F0_num, R_O0_num }; + static int hi_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_O0H_num, OptoReg::Bad, R_F1_num, R_O0H_num}; + static int lo_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_I0_num, R_I0_num, R_F0_num, R_F0_num, R_I0_num }; + static int hi_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_I0H_num, OptoReg::Bad, R_F1_num, R_I0H_num}; +#else // !_LP64 + static int lo_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_O0_num, R_O0_num, R_F0_num, R_F0_num, R_G1_num }; + static int hi_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_F1_num, R_G1H_num }; + static int lo_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_I0_num, R_I0_num, R_F0_num, R_F0_num, R_G1_num }; + static int hi_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_F1_num, R_G1H_num }; +#endif + return OptoRegPair( (is_outgoing?hi_out:hi_in)[ideal_reg], + (is_outgoing?lo_out:lo_in)[ideal_reg] ); + %} + + // Location of compiled Java return values. Same as C + return_value %{ + assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" ); +#ifdef _LP64 + static int lo_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_O0_num, R_O0_num, R_F0_num, R_F0_num, R_O0_num }; + static int hi_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_O0H_num, OptoReg::Bad, R_F1_num, R_O0H_num}; + static int lo_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_I0_num, R_I0_num, R_F0_num, R_F0_num, R_I0_num }; + static int hi_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_I0H_num, OptoReg::Bad, R_F1_num, R_I0H_num}; +#else // !_LP64 + static int lo_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_O0_num, R_O0_num, R_F0_num, R_F0_num, R_G1_num }; + static int hi_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_F1_num, R_G1H_num}; + static int lo_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_I0_num, R_I0_num, R_F0_num, R_F0_num, R_G1_num }; + static int hi_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_F1_num, R_G1H_num}; +#endif + return OptoRegPair( (is_outgoing?hi_out:hi_in)[ideal_reg], + (is_outgoing?lo_out:lo_in)[ideal_reg] ); + %} + +%} + + +//----------ATTRIBUTES--------------------------------------------------------- +//----------Operand Attributes------------------------------------------------- +op_attrib op_cost(1); // Required cost attribute + +//----------Instruction Attributes--------------------------------------------- +ins_attrib ins_cost(DEFAULT_COST); // Required cost attribute +ins_attrib ins_size(32); // Required size attribute (in bits) +ins_attrib ins_pc_relative(0); // Required PC Relative flag +ins_attrib ins_short_branch(0); // Required flag: is this instruction a + // non-matching short branch variant of some + // long branch? + +//----------OPERANDS----------------------------------------------------------- +// Operand definitions must precede instruction definitions for correct parsing +// in the ADLC because operands constitute user defined types which are used in +// instruction definitions. + +//----------Simple Operands---------------------------------------------------- +// Immediate Operands +// Integer Immediate: 32-bit +operand immI() %{ + match(ConI); + + op_cost(0); + // formats are generated automatically for constants and base registers + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: 13-bit +operand immI13() %{ + predicate(Assembler::is_simm13(n->get_int())); + match(ConI); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Unsigned (positive) Integer Immediate: 13-bit +operand immU13() %{ + predicate((0 <= n->get_int()) && Assembler::is_simm13(n->get_int())); + match(ConI); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: 6-bit +operand immU6() %{ + predicate(n->get_int() >= 0 && n->get_int() <= 63); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: 11-bit +operand immI11() %{ + predicate(Assembler::is_simm(n->get_int(),11)); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: 0-bit +operand immI0() %{ + predicate(n->get_int() == 0); + match(ConI); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: the value 10 +operand immI10() %{ + predicate(n->get_int() == 10); + match(ConI); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: the values 0-31 +operand immU5() %{ + predicate(n->get_int() >= 0 && n->get_int() <= 31); + match(ConI); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: the values 1-31 +operand immI_1_31() %{ + predicate(n->get_int() >= 1 && n->get_int() <= 31); + match(ConI); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: the values 32-63 +operand immI_32_63() %{ + predicate(n->get_int() >= 32 && n->get_int() <= 63); + match(ConI); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: the value 255 +operand immI_255() %{ + predicate( n->get_int() == 255 ); + match(ConI); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: the value FF +operand immL_FF() %{ + predicate( n->get_long() == 0xFFL ); + match(ConL); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: the value FFFF +operand immL_FFFF() %{ + predicate( n->get_long() == 0xFFFFL ); + match(ConL); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Pointer Immediate: 32 or 64-bit +operand immP() %{ + match(ConP); + + op_cost(5); + // formats are generated automatically for constants and base registers + format %{ %} + interface(CONST_INTER); +%} + +operand immP13() %{ + predicate((-4096 < n->get_ptr()) && (n->get_ptr() <= 4095)); + match(ConP); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +operand immP0() %{ + predicate(n->get_ptr() == 0); + match(ConP); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +operand immP_poll() %{ + predicate(n->get_ptr() != 0 && n->get_ptr() == (intptr_t)os::get_polling_page()); + match(ConP); + + // formats are generated automatically for constants and base registers + format %{ %} + interface(CONST_INTER); +%} + +operand immL() %{ + match(ConL); + op_cost(40); + // formats are generated automatically for constants and base registers + format %{ %} + interface(CONST_INTER); +%} + +operand immL0() %{ + predicate(n->get_long() == 0L); + match(ConL); + op_cost(0); + // formats are generated automatically for constants and base registers + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: 13-bit +operand immL13() %{ + predicate((-4096L < n->get_long()) && (n->get_long() <= 4095L)); + match(ConL); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: low 32-bit mask +operand immL_32bits() %{ + predicate(n->get_long() == 0xFFFFFFFFL); + match(ConL); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Double Immediate +operand immD() %{ + match(ConD); + + op_cost(40); + format %{ %} + interface(CONST_INTER); +%} + +operand immD0() %{ +#ifdef _LP64 + // on 64-bit architectures this comparision is faster + predicate(jlong_cast(n->getd()) == 0); +#else + predicate((n->getd() == 0) && (fpclass(n->getd()) == FP_PZERO)); +#endif + match(ConD); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Float Immediate +operand immF() %{ + match(ConF); + + op_cost(20); + format %{ %} + interface(CONST_INTER); +%} + +// Float Immediate: 0 +operand immF0() %{ + predicate((n->getf() == 0) && (fpclass(n->getf()) == FP_PZERO)); + match(ConF); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Integer Register Operands +// Integer Register +operand iRegI() %{ + constraint(ALLOC_IN_RC(int_reg)); + match(RegI); + + match(notemp_iRegI); + match(g1RegI); + match(o0RegI); + match(iRegIsafe); + + format %{ %} + interface(REG_INTER); +%} + +operand notemp_iRegI() %{ + constraint(ALLOC_IN_RC(notemp_int_reg)); + match(RegI); + + match(o0RegI); + + format %{ %} + interface(REG_INTER); +%} + +operand o0RegI() %{ + constraint(ALLOC_IN_RC(o0_regI)); + match(iRegI); + + format %{ %} + interface(REG_INTER); +%} + +// Pointer Register +operand iRegP() %{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(RegP); + + match(lock_ptr_RegP); + match(g1RegP); + match(g2RegP); + match(g3RegP); + match(g4RegP); + match(i0RegP); + match(o0RegP); + match(o1RegP); + match(l7RegP); + + format %{ %} + interface(REG_INTER); +%} + +operand sp_ptr_RegP() %{ + constraint(ALLOC_IN_RC(sp_ptr_reg)); + match(RegP); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand lock_ptr_RegP() %{ + constraint(ALLOC_IN_RC(lock_ptr_reg)); + match(RegP); + match(i0RegP); + match(o0RegP); + match(o1RegP); + match(l7RegP); + + format %{ %} + interface(REG_INTER); +%} + +operand g1RegP() %{ + constraint(ALLOC_IN_RC(g1_regP)); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand g2RegP() %{ + constraint(ALLOC_IN_RC(g2_regP)); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand g3RegP() %{ + constraint(ALLOC_IN_RC(g3_regP)); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand g1RegI() %{ + constraint(ALLOC_IN_RC(g1_regI)); + match(iRegI); + + format %{ %} + interface(REG_INTER); +%} + +operand g3RegI() %{ + constraint(ALLOC_IN_RC(g3_regI)); + match(iRegI); + + format %{ %} + interface(REG_INTER); +%} + +operand g4RegI() %{ + constraint(ALLOC_IN_RC(g4_regI)); + match(iRegI); + + format %{ %} + interface(REG_INTER); +%} + +operand g4RegP() %{ + constraint(ALLOC_IN_RC(g4_regP)); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand i0RegP() %{ + constraint(ALLOC_IN_RC(i0_regP)); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand o0RegP() %{ + constraint(ALLOC_IN_RC(o0_regP)); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand o1RegP() %{ + constraint(ALLOC_IN_RC(o1_regP)); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand o2RegP() %{ + constraint(ALLOC_IN_RC(o2_regP)); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand o7RegP() %{ + constraint(ALLOC_IN_RC(o7_regP)); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand l7RegP() %{ + constraint(ALLOC_IN_RC(l7_regP)); + match(iRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand o7RegI() %{ + constraint(ALLOC_IN_RC(o7_regI)); + match(iRegI); + + format %{ %} + interface(REG_INTER); +%} + +// Long Register +operand iRegL() %{ + constraint(ALLOC_IN_RC(long_reg)); + match(RegL); + + format %{ %} + interface(REG_INTER); +%} + +operand o2RegL() %{ + constraint(ALLOC_IN_RC(o2_regL)); + match(iRegL); + + format %{ %} + interface(REG_INTER); +%} + +operand o7RegL() %{ + constraint(ALLOC_IN_RC(o7_regL)); + match(iRegL); + + format %{ %} + interface(REG_INTER); +%} + +operand g1RegL() %{ + constraint(ALLOC_IN_RC(g1_regL)); + match(iRegL); + + format %{ %} + interface(REG_INTER); +%} + +// Int Register safe +// This is 64bit safe +operand iRegIsafe() %{ + constraint(ALLOC_IN_RC(long_reg)); + + match(iRegI); + + format %{ %} + interface(REG_INTER); +%} + +// Condition Code Flag Register +operand flagsReg() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "ccr" %} // both ICC and XCC + interface(REG_INTER); +%} + +// Condition Code Register, unsigned comparisons. +operand flagsRegU() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "icc_U" %} + interface(REG_INTER); +%} + +// Condition Code Register, pointer comparisons. +operand flagsRegP() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + +#ifdef _LP64 + format %{ "xcc_P" %} +#else + format %{ "icc_P" %} +#endif + interface(REG_INTER); +%} + +// Condition Code Register, long comparisons. +operand flagsRegL() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "xcc_L" %} + interface(REG_INTER); +%} + +// Condition Code Register, floating comparisons, unordered same as "less". +operand flagsRegF() %{ + constraint(ALLOC_IN_RC(float_flags)); + match(RegFlags); + match(flagsRegF0); + + format %{ %} + interface(REG_INTER); +%} + +operand flagsRegF0() %{ + constraint(ALLOC_IN_RC(float_flag0)); + match(RegFlags); + + format %{ %} + interface(REG_INTER); +%} + + +// Condition Code Flag Register used by long compare +operand flagsReg_long_LTGE() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + format %{ "icc_LTGE" %} + interface(REG_INTER); +%} +operand flagsReg_long_EQNE() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + format %{ "icc_EQNE" %} + interface(REG_INTER); +%} +operand flagsReg_long_LEGT() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + format %{ "icc_LEGT" %} + interface(REG_INTER); +%} + + +operand regD() %{ + constraint(ALLOC_IN_RC(dflt_reg)); + match(RegD); + + format %{ %} + interface(REG_INTER); +%} + +operand regF() %{ + constraint(ALLOC_IN_RC(sflt_reg)); + match(RegF); + + format %{ %} + interface(REG_INTER); +%} + +operand regD_low() %{ + constraint(ALLOC_IN_RC(dflt_low_reg)); + match(RegD); + + format %{ %} + interface(REG_INTER); +%} + +// Special Registers + +// Method Register +operand inline_cache_regP(iRegP reg) %{ + constraint(ALLOC_IN_RC(g5_regP)); // G5=inline_cache_reg but uses 2 bits instead of 1 + match(reg); + format %{ %} + interface(REG_INTER); +%} + +operand interpreter_method_oop_regP(iRegP reg) %{ + constraint(ALLOC_IN_RC(g5_regP)); // G5=interpreter_method_oop_reg but uses 2 bits instead of 1 + match(reg); + format %{ %} + interface(REG_INTER); +%} + + +//----------Complex Operands--------------------------------------------------- +// Indirect Memory Reference +operand indirect(sp_ptr_RegP reg) %{ + constraint(ALLOC_IN_RC(sp_ptr_reg)); + match(reg); + + op_cost(100); + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x0); + scale(0x0); + disp(0x0); + %} +%} + +// Indirect with Offset +operand indOffset13(sp_ptr_RegP reg, immX13 offset) %{ + constraint(ALLOC_IN_RC(sp_ptr_reg)); + match(AddP reg offset); + + op_cost(100); + format %{ "[$reg + $offset]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x0); + scale(0x0); + disp($offset); + %} +%} + +// Note: Intel has a swapped version also, like this: +//operand indOffsetX(iRegI reg, immP offset) %{ +// constraint(ALLOC_IN_RC(int_reg)); +// match(AddP offset reg); +// +// op_cost(100); +// format %{ "[$reg + $offset]" %} +// interface(MEMORY_INTER) %{ +// base($reg); +// index(0x0); +// scale(0x0); +// disp($offset); +// %} +//%} +//// However, it doesn't make sense for SPARC, since +// we have no particularly good way to embed oops in +// single instructions. + +// Indirect with Register Index +operand indIndex(iRegP addr, iRegX index) %{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP addr index); + + op_cost(100); + format %{ "[$addr + $index]" %} + interface(MEMORY_INTER) %{ + base($addr); + index($index); + scale(0x0); + disp(0x0); + %} +%} + +//----------Special Memory Operands-------------------------------------------- +// Stack Slot Operand - This operand is used for loading and storing temporary +// values on the stack where a match requires a value to +// flow through memory. +operand stackSlotI(sRegI reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + op_cost(100); + //match(RegI); + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0xE); // R_SP + index(0x0); + scale(0x0); + disp($reg); // Stack Offset + %} +%} + +operand stackSlotP(sRegP reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + op_cost(100); + //match(RegP); + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0xE); // R_SP + index(0x0); + scale(0x0); + disp($reg); // Stack Offset + %} +%} + +operand stackSlotF(sRegF reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + op_cost(100); + //match(RegF); + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0xE); // R_SP + index(0x0); + scale(0x0); + disp($reg); // Stack Offset + %} +%} +operand stackSlotD(sRegD reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + op_cost(100); + //match(RegD); + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0xE); // R_SP + index(0x0); + scale(0x0); + disp($reg); // Stack Offset + %} +%} +operand stackSlotL(sRegL reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + op_cost(100); + //match(RegL); + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0xE); // R_SP + index(0x0); + scale(0x0); + disp($reg); // Stack Offset + %} +%} + +// Operands for expressing Control Flow +// NOTE: Label is a predefined operand which should not be redefined in +// the AD file. It is generically handled within the ADLC. + +//----------Conditional Branch Operands---------------------------------------- +// Comparison Op - This is the operation of the comparison, and is limited to +// the following set of codes: +// L (<), LE (<=), G (>), GE (>=), E (==), NE (!=) +// +// Other attributes of the comparison, such as unsignedness, are specified +// by the comparison instruction that sets a condition code flags register. +// That result is represented by a flags operand whose subtype is appropriate +// to the unsignedness (etc.) of the comparison. +// +// Later, the instruction which matches both the Comparison Op (a Bool) and +// the flags (produced by the Cmp) specifies the coding of the comparison op +// by matching a specific subtype of Bool operand below, such as cmpOpU. + +operand cmpOp() %{ + match(Bool); + + format %{ "" %} + interface(COND_INTER) %{ + equal(0x1); + not_equal(0x9); + less(0x3); + greater_equal(0xB); + less_equal(0x2); + greater(0xA); + %} +%} + +// Comparison Op, unsigned +operand cmpOpU() %{ + match(Bool); + + format %{ "u" %} + interface(COND_INTER) %{ + equal(0x1); + not_equal(0x9); + less(0x5); + greater_equal(0xD); + less_equal(0x4); + greater(0xC); + %} +%} + +// Comparison Op, pointer (same as unsigned) +operand cmpOpP() %{ + match(Bool); + + format %{ "p" %} + interface(COND_INTER) %{ + equal(0x1); + not_equal(0x9); + less(0x5); + greater_equal(0xD); + less_equal(0x4); + greater(0xC); + %} +%} + +// Comparison Op, branch-register encoding +operand cmpOp_reg() %{ + match(Bool); + + format %{ "" %} + interface(COND_INTER) %{ + equal (0x1); + not_equal (0x5); + less (0x3); + greater_equal(0x7); + less_equal (0x2); + greater (0x6); + %} +%} + +// Comparison Code, floating, unordered same as less +operand cmpOpF() %{ + match(Bool); + + format %{ "fl" %} + interface(COND_INTER) %{ + equal(0x9); + not_equal(0x1); + less(0x3); + greater_equal(0xB); + less_equal(0xE); + greater(0x6); + %} +%} + +// Used by long compare +operand cmpOp_commute() %{ + match(Bool); + + format %{ "" %} + interface(COND_INTER) %{ + equal(0x1); + not_equal(0x9); + less(0xA); + greater_equal(0x2); + less_equal(0xB); + greater(0x3); + %} +%} + +//----------OPERAND CLASSES---------------------------------------------------- +// Operand Classes are groups of operands that are used to simplify +// instruction definitions by not requiring the AD writer to specify seperate +// instructions for every form of operand when the instruction accepts +// multiple operand types with the same basic encoding and format. The classic +// case of this is memory operands. +// Indirect is not included since its use is limited to Compare & Swap +opclass memory( indirect, indOffset13, indIndex ); + +//----------PIPELINE----------------------------------------------------------- +pipeline %{ + +//----------ATTRIBUTES--------------------------------------------------------- +attributes %{ + fixed_size_instructions; // Fixed size instructions + branch_has_delay_slot; // Branch has delay slot following + max_instructions_per_bundle = 4; // Up to 4 instructions per bundle + instruction_unit_size = 4; // An instruction is 4 bytes long + instruction_fetch_unit_size = 16; // The processor fetches one line + instruction_fetch_units = 1; // of 16 bytes + + // List of nop instructions + nops( Nop_A0, Nop_A1, Nop_MS, Nop_FA, Nop_BR ); +%} + +//----------RESOURCES---------------------------------------------------------- +// Resources are the functional units available to the machine +resources(A0, A1, MS, BR, FA, FM, IDIV, FDIV, IALU = A0 | A1); + +//----------PIPELINE DESCRIPTION----------------------------------------------- +// Pipeline Description specifies the stages in the machine's pipeline + +pipe_desc(A, P, F, B, I, J, S, R, E, C, M, W, X, T, D); + +//----------PIPELINE CLASSES--------------------------------------------------- +// Pipeline Classes describe the stages in which input and output are +// referenced by the hardware pipeline. + +// Integer ALU reg-reg operation +pipe_class ialu_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + single_instruction; + dst : E(write); + src1 : R(read); + src2 : R(read); + IALU : R; +%} + +// Integer ALU reg-reg long operation +pipe_class ialu_reg_reg_2(iRegL dst, iRegL src1, iRegL src2) %{ + instruction_count(2); + dst : E(write); + src1 : R(read); + src2 : R(read); + IALU : R; + IALU : R; +%} + +// Integer ALU reg-reg long dependent operation +pipe_class ialu_reg_reg_2_dep(iRegL dst, iRegL src1, iRegL src2, flagsReg cr) %{ + instruction_count(1); multiple_bundles; + dst : E(write); + src1 : R(read); + src2 : R(read); + cr : E(write); + IALU : R(2); +%} + +// Integer ALU reg-imm operaion +pipe_class ialu_reg_imm(iRegI dst, iRegI src1, immI13 src2) %{ + single_instruction; + dst : E(write); + src1 : R(read); + IALU : R; +%} + +// Integer ALU reg-reg operation with condition code +pipe_class ialu_cc_reg_reg(iRegI dst, iRegI src1, iRegI src2, flagsReg cr) %{ + single_instruction; + dst : E(write); + cr : E(write); + src1 : R(read); + src2 : R(read); + IALU : R; +%} + +// Integer ALU reg-imm operation with condition code +pipe_class ialu_cc_reg_imm(iRegI dst, iRegI src1, immI13 src2, flagsReg cr) %{ + single_instruction; + dst : E(write); + cr : E(write); + src1 : R(read); + IALU : R; +%} + +// Integer ALU zero-reg operation +pipe_class ialu_zero_reg(iRegI dst, immI0 zero, iRegI src2) %{ + single_instruction; + dst : E(write); + src2 : R(read); + IALU : R; +%} + +// Integer ALU zero-reg operation with condition code only +pipe_class ialu_cconly_zero_reg(flagsReg cr, iRegI src) %{ + single_instruction; + cr : E(write); + src : R(read); + IALU : R; +%} + +// Integer ALU reg-reg operation with condition code only +pipe_class ialu_cconly_reg_reg(flagsReg cr, iRegI src1, iRegI src2) %{ + single_instruction; + cr : E(write); + src1 : R(read); + src2 : R(read); + IALU : R; +%} + +// Integer ALU reg-imm operation with condition code only +pipe_class ialu_cconly_reg_imm(flagsReg cr, iRegI src1, immI13 src2) %{ + single_instruction; + cr : E(write); + src1 : R(read); + IALU : R; +%} + +// Integer ALU reg-reg-zero operation with condition code only +pipe_class ialu_cconly_reg_reg_zero(flagsReg cr, iRegI src1, iRegI src2, immI0 zero) %{ + single_instruction; + cr : E(write); + src1 : R(read); + src2 : R(read); + IALU : R; +%} + +// Integer ALU reg-imm-zero operation with condition code only +pipe_class ialu_cconly_reg_imm_zero(flagsReg cr, iRegI src1, immI13 src2, immI0 zero) %{ + single_instruction; + cr : E(write); + src1 : R(read); + IALU : R; +%} + +// Integer ALU reg-reg operation with condition code, src1 modified +pipe_class ialu_cc_rwreg_reg(flagsReg cr, iRegI src1, iRegI src2) %{ + single_instruction; + cr : E(write); + src1 : E(write); + src1 : R(read); + src2 : R(read); + IALU : R; +%} + +// Integer ALU reg-imm operation with condition code, src1 modified +pipe_class ialu_cc_rwreg_imm(flagsReg cr, iRegI src1, immI13 src2) %{ + single_instruction; + cr : E(write); + src1 : E(write); + src1 : R(read); + IALU : R; +%} + +pipe_class cmpL_reg(iRegI dst, iRegL src1, iRegL src2, flagsReg cr ) %{ + multiple_bundles; + dst : E(write)+4; + cr : E(write); + src1 : R(read); + src2 : R(read); + IALU : R(3); + BR : R(2); +%} + +// Integer ALU operation +pipe_class ialu_none(iRegI dst) %{ + single_instruction; + dst : E(write); + IALU : R; +%} + +// Integer ALU reg operation +pipe_class ialu_reg(iRegI dst, iRegI src) %{ + single_instruction; may_have_no_code; + dst : E(write); + src : R(read); + IALU : R; +%} + +// Integer ALU reg conditional operation +// This instruction has a 1 cycle stall, and cannot execute +// in the same cycle as the instruction setting the condition +// code. We kludge this by pretending to read the condition code +// 1 cycle earlier, and by marking the functional units as busy +// for 2 cycles with the result available 1 cycle later than +// is really the case. +pipe_class ialu_reg_flags( iRegI op2_out, iRegI op2_in, iRegI op1, flagsReg cr ) %{ + single_instruction; + op2_out : C(write); + op1 : R(read); + cr : R(read); // This is really E, with a 1 cycle stall + BR : R(2); + MS : R(2); +%} + +#ifdef _LP64 +pipe_class ialu_clr_and_mover( iRegI dst, iRegP src ) %{ + instruction_count(1); multiple_bundles; + dst : C(write)+1; + src : R(read)+1; + IALU : R(1); + BR : E(2); + MS : E(2); +%} +#endif + +// Integer ALU reg operation +pipe_class ialu_move_reg_L_to_I(iRegI dst, iRegL src) %{ + single_instruction; may_have_no_code; + dst : E(write); + src : R(read); + IALU : R; +%} +pipe_class ialu_move_reg_I_to_L(iRegL dst, iRegI src) %{ + single_instruction; may_have_no_code; + dst : E(write); + src : R(read); + IALU : R; +%} + +// Two integer ALU reg operations +pipe_class ialu_reg_2(iRegL dst, iRegL src) %{ + instruction_count(2); + dst : E(write); + src : R(read); + A0 : R; + A1 : R; +%} + +// Two integer ALU reg operations +pipe_class ialu_move_reg_L_to_L(iRegL dst, iRegL src) %{ + instruction_count(2); may_have_no_code; + dst : E(write); + src : R(read); + A0 : R; + A1 : R; +%} + +// Integer ALU imm operation +pipe_class ialu_imm(iRegI dst, immI13 src) %{ + single_instruction; + dst : E(write); + IALU : R; +%} + +// Integer ALU reg-reg with carry operation +pipe_class ialu_reg_reg_cy(iRegI dst, iRegI src1, iRegI src2, iRegI cy) %{ + single_instruction; + dst : E(write); + src1 : R(read); + src2 : R(read); + IALU : R; +%} + +// Integer ALU cc operation +pipe_class ialu_cc(iRegI dst, flagsReg cc) %{ + single_instruction; + dst : E(write); + cc : R(read); + IALU : R; +%} + +// Integer ALU cc / second IALU operation +pipe_class ialu_reg_ialu( iRegI dst, iRegI src ) %{ + instruction_count(1); multiple_bundles; + dst : E(write)+1; + src : R(read); + IALU : R; +%} + +// Integer ALU cc / second IALU operation +pipe_class ialu_reg_reg_ialu( iRegI dst, iRegI p, iRegI q ) %{ + instruction_count(1); multiple_bundles; + dst : E(write)+1; + p : R(read); + q : R(read); + IALU : R; +%} + +// Integer ALU hi-lo-reg operation +pipe_class ialu_hi_lo_reg(iRegI dst, immI src) %{ + instruction_count(1); multiple_bundles; + dst : E(write)+1; + IALU : R(2); +%} + +// Float ALU hi-lo-reg operation (with temp) +pipe_class ialu_hi_lo_reg_temp(regF dst, immF src, g3RegP tmp) %{ + instruction_count(1); multiple_bundles; + dst : E(write)+1; + IALU : R(2); +%} + +// Long Constant +pipe_class loadConL( iRegL dst, immL src ) %{ + instruction_count(2); multiple_bundles; + dst : E(write)+1; + IALU : R(2); + IALU : R(2); +%} + +// Pointer Constant +pipe_class loadConP( iRegP dst, immP src ) %{ + instruction_count(0); multiple_bundles; + fixed_latency(6); +%} + +// Polling Address +pipe_class loadConP_poll( iRegP dst, immP_poll src ) %{ +#ifdef _LP64 + instruction_count(0); multiple_bundles; + fixed_latency(6); +#else + dst : E(write); + IALU : R; +#endif +%} + +// Long Constant small +pipe_class loadConLlo( iRegL dst, immL src ) %{ + instruction_count(2); + dst : E(write); + IALU : R; + IALU : R; +%} + +// [PHH] This is wrong for 64-bit. See LdImmF/D. +pipe_class loadConFD(regF dst, immF src, g3RegP tmp) %{ + instruction_count(1); multiple_bundles; + src : R(read); + dst : M(write)+1; + IALU : R; + MS : E; +%} + +// Integer ALU nop operation +pipe_class ialu_nop() %{ + single_instruction; + IALU : R; +%} + +// Integer ALU nop operation +pipe_class ialu_nop_A0() %{ + single_instruction; + A0 : R; +%} + +// Integer ALU nop operation +pipe_class ialu_nop_A1() %{ + single_instruction; + A1 : R; +%} + +// Integer Multiply reg-reg operation +pipe_class imul_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + single_instruction; + dst : E(write); + src1 : R(read); + src2 : R(read); + MS : R(5); +%} + +// Integer Multiply reg-imm operation +pipe_class imul_reg_imm(iRegI dst, iRegI src1, immI13 src2) %{ + single_instruction; + dst : E(write); + src1 : R(read); + MS : R(5); +%} + +pipe_class mulL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{ + single_instruction; + dst : E(write)+4; + src1 : R(read); + src2 : R(read); + MS : R(6); +%} + +pipe_class mulL_reg_imm(iRegL dst, iRegL src1, immL13 src2) %{ + single_instruction; + dst : E(write)+4; + src1 : R(read); + MS : R(6); +%} + +// Integer Divide reg-reg +pipe_class sdiv_reg_reg(iRegI dst, iRegI src1, iRegI src2, iRegI temp, flagsReg cr) %{ + instruction_count(1); multiple_bundles; + dst : E(write); + temp : E(write); + src1 : R(read); + src2 : R(read); + temp : R(read); + MS : R(38); +%} + +// Integer Divide reg-imm +pipe_class sdiv_reg_imm(iRegI dst, iRegI src1, immI13 src2, iRegI temp, flagsReg cr) %{ + instruction_count(1); multiple_bundles; + dst : E(write); + temp : E(write); + src1 : R(read); + temp : R(read); + MS : R(38); +%} + +// Long Divide +pipe_class divL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{ + dst : E(write)+71; + src1 : R(read); + src2 : R(read)+1; + MS : R(70); +%} + +pipe_class divL_reg_imm(iRegL dst, iRegL src1, immL13 src2) %{ + dst : E(write)+71; + src1 : R(read); + MS : R(70); +%} + +// Floating Point Add Float +pipe_class faddF_reg_reg(regF dst, regF src1, regF src2) %{ + single_instruction; + dst : X(write); + src1 : E(read); + src2 : E(read); + FA : R; +%} + +// Floating Point Add Double +pipe_class faddD_reg_reg(regD dst, regD src1, regD src2) %{ + single_instruction; + dst : X(write); + src1 : E(read); + src2 : E(read); + FA : R; +%} + +// Floating Point Conditional Move based on integer flags +pipe_class int_conditional_float_move (cmpOp cmp, flagsReg cr, regF dst, regF src) %{ + single_instruction; + dst : X(write); + src : E(read); + cr : R(read); + FA : R(2); + BR : R(2); +%} + +// Floating Point Conditional Move based on integer flags +pipe_class int_conditional_double_move (cmpOp cmp, flagsReg cr, regD dst, regD src) %{ + single_instruction; + dst : X(write); + src : E(read); + cr : R(read); + FA : R(2); + BR : R(2); +%} + +// Floating Point Multiply Float +pipe_class fmulF_reg_reg(regF dst, regF src1, regF src2) %{ + single_instruction; + dst : X(write); + src1 : E(read); + src2 : E(read); + FM : R; +%} + +// Floating Point Multiply Double +pipe_class fmulD_reg_reg(regD dst, regD src1, regD src2) %{ + single_instruction; + dst : X(write); + src1 : E(read); + src2 : E(read); + FM : R; +%} + +// Floating Point Divide Float +pipe_class fdivF_reg_reg(regF dst, regF src1, regF src2) %{ + single_instruction; + dst : X(write); + src1 : E(read); + src2 : E(read); + FM : R; + FDIV : C(14); +%} + +// Floating Point Divide Double +pipe_class fdivD_reg_reg(regD dst, regD src1, regD src2) %{ + single_instruction; + dst : X(write); + src1 : E(read); + src2 : E(read); + FM : R; + FDIV : C(17); +%} + +// Floating Point Move/Negate/Abs Float +pipe_class faddF_reg(regF dst, regF src) %{ + single_instruction; + dst : W(write); + src : E(read); + FA : R(1); +%} + +// Floating Point Move/Negate/Abs Double +pipe_class faddD_reg(regD dst, regD src) %{ + single_instruction; + dst : W(write); + src : E(read); + FA : R; +%} + +// Floating Point Convert F->D +pipe_class fcvtF2D(regD dst, regF src) %{ + single_instruction; + dst : X(write); + src : E(read); + FA : R; +%} + +// Floating Point Convert I->D +pipe_class fcvtI2D(regD dst, regF src) %{ + single_instruction; + dst : X(write); + src : E(read); + FA : R; +%} + +// Floating Point Convert LHi->D +pipe_class fcvtLHi2D(regD dst, regD src) %{ + single_instruction; + dst : X(write); + src : E(read); + FA : R; +%} + +// Floating Point Convert L->D +pipe_class fcvtL2D(regD dst, regF src) %{ + single_instruction; + dst : X(write); + src : E(read); + FA : R; +%} + +// Floating Point Convert L->F +pipe_class fcvtL2F(regD dst, regF src) %{ + single_instruction; + dst : X(write); + src : E(read); + FA : R; +%} + +// Floating Point Convert D->F +pipe_class fcvtD2F(regD dst, regF src) %{ + single_instruction; + dst : X(write); + src : E(read); + FA : R; +%} + +// Floating Point Convert I->L +pipe_class fcvtI2L(regD dst, regF src) %{ + single_instruction; + dst : X(write); + src : E(read); + FA : R; +%} + +// Floating Point Convert D->F +pipe_class fcvtD2I(regF dst, regD src, flagsReg cr) %{ + instruction_count(1); multiple_bundles; + dst : X(write)+6; + src : E(read); + FA : R; +%} + +// Floating Point Convert D->L +pipe_class fcvtD2L(regD dst, regD src, flagsReg cr) %{ + instruction_count(1); multiple_bundles; + dst : X(write)+6; + src : E(read); + FA : R; +%} + +// Floating Point Convert F->I +pipe_class fcvtF2I(regF dst, regF src, flagsReg cr) %{ + instruction_count(1); multiple_bundles; + dst : X(write)+6; + src : E(read); + FA : R; +%} + +// Floating Point Convert F->L +pipe_class fcvtF2L(regD dst, regF src, flagsReg cr) %{ + instruction_count(1); multiple_bundles; + dst : X(write)+6; + src : E(read); + FA : R; +%} + +// Floating Point Convert I->F +pipe_class fcvtI2F(regF dst, regF src) %{ + single_instruction; + dst : X(write); + src : E(read); + FA : R; +%} + +// Floating Point Compare +pipe_class faddF_fcc_reg_reg_zero(flagsRegF cr, regF src1, regF src2, immI0 zero) %{ + single_instruction; + cr : X(write); + src1 : E(read); + src2 : E(read); + FA : R; +%} + +// Floating Point Compare +pipe_class faddD_fcc_reg_reg_zero(flagsRegF cr, regD src1, regD src2, immI0 zero) %{ + single_instruction; + cr : X(write); + src1 : E(read); + src2 : E(read); + FA : R; +%} + +// Floating Add Nop +pipe_class fadd_nop() %{ + single_instruction; + FA : R; +%} + +// Integer Store to Memory +pipe_class istore_mem_reg(memory mem, iRegI src) %{ + single_instruction; + mem : R(read); + src : C(read); + MS : R; +%} + +// Integer Store to Memory +pipe_class istore_mem_spORreg(memory mem, sp_ptr_RegP src) %{ + single_instruction; + mem : R(read); + src : C(read); + MS : R; +%} + +// Integer Store Zero to Memory +pipe_class istore_mem_zero(memory mem, immI0 src) %{ + single_instruction; + mem : R(read); + MS : R; +%} + +// Special Stack Slot Store +pipe_class istore_stk_reg(stackSlotI stkSlot, iRegI src) %{ + single_instruction; + stkSlot : R(read); + src : C(read); + MS : R; +%} + +// Special Stack Slot Store +pipe_class lstoreI_stk_reg(stackSlotL stkSlot, iRegI src) %{ + instruction_count(2); multiple_bundles; + stkSlot : R(read); + src : C(read); + MS : R(2); +%} + +// Float Store +pipe_class fstoreF_mem_reg(memory mem, RegF src) %{ + single_instruction; + mem : R(read); + src : C(read); + MS : R; +%} + +// Float Store +pipe_class fstoreF_mem_zero(memory mem, immF0 src) %{ + single_instruction; + mem : R(read); + MS : R; +%} + +// Double Store +pipe_class fstoreD_mem_reg(memory mem, RegD src) %{ + instruction_count(1); + mem : R(read); + src : C(read); + MS : R; +%} + +// Double Store +pipe_class fstoreD_mem_zero(memory mem, immD0 src) %{ + single_instruction; + mem : R(read); + MS : R; +%} + +// Special Stack Slot Float Store +pipe_class fstoreF_stk_reg(stackSlotI stkSlot, RegF src) %{ + single_instruction; + stkSlot : R(read); + src : C(read); + MS : R; +%} + +// Special Stack Slot Double Store +pipe_class fstoreD_stk_reg(stackSlotI stkSlot, RegD src) %{ + single_instruction; + stkSlot : R(read); + src : C(read); + MS : R; +%} + +// Integer Load (when sign bit propagation not needed) +pipe_class iload_mem(iRegI dst, memory mem) %{ + single_instruction; + mem : R(read); + dst : C(write); + MS : R; +%} + +// Integer Load from stack operand +pipe_class iload_stkD(iRegI dst, stackSlotD mem ) %{ + single_instruction; + mem : R(read); + dst : C(write); + MS : R; +%} + +// Integer Load (when sign bit propagation or masking is needed) +pipe_class iload_mask_mem(iRegI dst, memory mem) %{ + single_instruction; + mem : R(read); + dst : M(write); + MS : R; +%} + +// Float Load +pipe_class floadF_mem(regF dst, memory mem) %{ + single_instruction; + mem : R(read); + dst : M(write); + MS : R; +%} + +// Float Load +pipe_class floadD_mem(regD dst, memory mem) %{ + instruction_count(1); multiple_bundles; // Again, unaligned argument is only multiple case + mem : R(read); + dst : M(write); + MS : R; +%} + +// Float Load +pipe_class floadF_stk(regF dst, stackSlotI stkSlot) %{ + single_instruction; + stkSlot : R(read); + dst : M(write); + MS : R; +%} + +// Float Load +pipe_class floadD_stk(regD dst, stackSlotI stkSlot) %{ + single_instruction; + stkSlot : R(read); + dst : M(write); + MS : R; +%} + +// Memory Nop +pipe_class mem_nop() %{ + single_instruction; + MS : R; +%} + +pipe_class sethi(iRegP dst, immI src) %{ + single_instruction; + dst : E(write); + IALU : R; +%} + +pipe_class loadPollP(iRegP poll) %{ + single_instruction; + poll : R(read); + MS : R; +%} + +pipe_class br(Universe br, label labl) %{ + single_instruction_with_delay_slot; + BR : R; +%} + +pipe_class br_cc(Universe br, cmpOp cmp, flagsReg cr, label labl) %{ + single_instruction_with_delay_slot; + cr : E(read); + BR : R; +%} + +pipe_class br_reg(Universe br, cmpOp cmp, iRegI op1, label labl) %{ + single_instruction_with_delay_slot; + op1 : E(read); + BR : R; + MS : R; +%} + +pipe_class br_fcc(Universe br, cmpOpF cc, flagsReg cr, label labl) %{ + single_instruction_with_delay_slot; + cr : E(read); + BR : R; +%} + +pipe_class br_nop() %{ + single_instruction; + BR : R; +%} + +pipe_class simple_call(method meth) %{ + instruction_count(2); multiple_bundles; force_serialization; + fixed_latency(100); + BR : R(1); + MS : R(1); + A0 : R(1); +%} + +pipe_class compiled_call(method meth) %{ + instruction_count(1); multiple_bundles; force_serialization; + fixed_latency(100); + MS : R(1); +%} + +pipe_class call(method meth) %{ + instruction_count(0); multiple_bundles; force_serialization; + fixed_latency(100); +%} + +pipe_class tail_call(Universe ignore, label labl) %{ + single_instruction; has_delay_slot; + fixed_latency(100); + BR : R(1); + MS : R(1); +%} + +pipe_class ret(Universe ignore) %{ + single_instruction; has_delay_slot; + BR : R(1); + MS : R(1); +%} + +pipe_class ret_poll(g3RegP poll) %{ + instruction_count(3); has_delay_slot; + poll : E(read); + MS : R; +%} + +// The real do-nothing guy +pipe_class empty( ) %{ + instruction_count(0); +%} + +pipe_class long_memory_op() %{ + instruction_count(0); multiple_bundles; force_serialization; + fixed_latency(25); + MS : R(1); +%} + +// Check-cast +pipe_class partial_subtype_check_pipe(Universe ignore, iRegP array, iRegP match ) %{ + array : R(read); + match : R(read); + IALU : R(2); + BR : R(2); + MS : R; +%} + +// Convert FPU flags into +1,0,-1 +pipe_class floating_cmp( iRegI dst, regF src1, regF src2 ) %{ + src1 : E(read); + src2 : E(read); + dst : E(write); + FA : R; + MS : R(2); + BR : R(2); +%} + +// Compare for p < q, and conditionally add y +pipe_class cadd_cmpltmask( iRegI p, iRegI q, iRegI y ) %{ + p : E(read); + q : E(read); + y : E(read); + IALU : R(3) +%} + +// Perform a compare, then move conditionally in a branch delay slot. +pipe_class min_max( iRegI src2, iRegI srcdst ) %{ + src2 : E(read); + srcdst : E(read); + IALU : R; + BR : R; +%} + +// Define the class for the Nop node +define %{ + MachNop = ialu_nop; +%} + +%} + +//----------INSTRUCTIONS------------------------------------------------------- + +//------------Special Stack Slot instructions - no match rules----------------- +instruct stkI_to_regF(regF dst, stackSlotI src) %{ + // No match rule to avoid chain rule match. + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LDF $src,$dst\t! stkI to regF" %} + opcode(Assembler::ldf_op3); + ins_encode(form3_mem_reg(src, dst)); + ins_pipe(floadF_stk); +%} + +instruct stkL_to_regD(regD dst, stackSlotL src) %{ + // No match rule to avoid chain rule match. + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LDDF $src,$dst\t! stkL to regD" %} + opcode(Assembler::lddf_op3); + ins_encode(form3_mem_reg(src, dst)); + ins_pipe(floadD_stk); +%} + +instruct regF_to_stkI(stackSlotI dst, regF src) %{ + // No match rule to avoid chain rule match. + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STF $src,$dst\t! regF to stkI" %} + opcode(Assembler::stf_op3); + ins_encode(form3_mem_reg(dst, src)); + ins_pipe(fstoreF_stk_reg); +%} + +instruct regD_to_stkL(stackSlotL dst, regD src) %{ + // No match rule to avoid chain rule match. + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STDF $src,$dst\t! regD to stkL" %} + opcode(Assembler::stdf_op3); + ins_encode(form3_mem_reg(dst, src)); + ins_pipe(fstoreD_stk_reg); +%} + +instruct regI_to_stkLHi(stackSlotL dst, iRegI src) %{ + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST*2); + size(8); + format %{ "STW $src,$dst.hi\t! long\n\t" + "STW R_G0,$dst.lo" %} + opcode(Assembler::stw_op3); + ins_encode(form3_mem_reg(dst, src), form3_mem_plus_4_reg(dst, R_G0)); + ins_pipe(lstoreI_stk_reg); +%} + +instruct regL_to_stkD(stackSlotD dst, iRegL src) %{ + // No match rule to avoid chain rule match. + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STX $src,$dst\t! regL to stkD" %} + opcode(Assembler::stx_op3); + ins_encode( form3_mem_reg( dst, src ) ); + ins_pipe(istore_stk_reg); +%} + +//---------- Chain stack slots between similar types -------- + +// Load integer from stack slot +instruct stkI_to_regI( iRegI dst, stackSlotI src ) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDUW $src,$dst\t!stk" %} + opcode(Assembler::lduw_op3); + ins_encode( form3_mem_reg( src, dst ) ); + ins_pipe(iload_mem); +%} + +// Store integer to stack slot +instruct regI_to_stkI( stackSlotI dst, iRegI src ) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STW $src,$dst\t!stk" %} + opcode(Assembler::stw_op3); + ins_encode( form3_mem_reg( dst, src ) ); + ins_pipe(istore_mem_reg); +%} + +// Load long from stack slot +instruct stkL_to_regL( iRegL dst, stackSlotL src ) %{ + match(Set dst src); + + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LDX $src,$dst\t! long" %} + opcode(Assembler::ldx_op3); + ins_encode( form3_mem_reg( src, dst ) ); + ins_pipe(iload_mem); +%} + +// Store long to stack slot +instruct regL_to_stkL(stackSlotL dst, iRegL src) %{ + match(Set dst src); + + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STX $src,$dst\t! long" %} + opcode(Assembler::stx_op3); + ins_encode( form3_mem_reg( dst, src ) ); + ins_pipe(istore_mem_reg); +%} + +#ifdef _LP64 +// Load pointer from stack slot, 64-bit encoding +instruct stkP_to_regP( iRegP dst, stackSlotP src ) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LDX $src,$dst\t!ptr" %} + opcode(Assembler::ldx_op3); + ins_encode( form3_mem_reg( src, dst ) ); + ins_pipe(iload_mem); +%} + +// Store pointer to stack slot +instruct regP_to_stkP(stackSlotP dst, iRegP src) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STX $src,$dst\t!ptr" %} + opcode(Assembler::stx_op3); + ins_encode( form3_mem_reg( dst, src ) ); + ins_pipe(istore_mem_reg); +%} +#else // _LP64 +// Load pointer from stack slot, 32-bit encoding +instruct stkP_to_regP( iRegP dst, stackSlotP src ) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + format %{ "LDUW $src,$dst\t!ptr" %} + opcode(Assembler::lduw_op3, Assembler::ldst_op); + ins_encode( form3_mem_reg( src, dst ) ); + ins_pipe(iload_mem); +%} + +// Store pointer to stack slot +instruct regP_to_stkP(stackSlotP dst, iRegP src) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + format %{ "STW $src,$dst\t!ptr" %} + opcode(Assembler::stw_op3, Assembler::ldst_op); + ins_encode( form3_mem_reg( dst, src ) ); + ins_pipe(istore_mem_reg); +%} +#endif // _LP64 + +//------------Special Nop instructions for bundling - no match rules----------- +// Nop using the A0 functional unit +instruct Nop_A0() %{ + ins_cost(0); + + format %{ "NOP ! Alu Pipeline" %} + opcode(Assembler::or_op3, Assembler::arith_op); + ins_encode( form2_nop() ); + ins_pipe(ialu_nop_A0); +%} + +// Nop using the A1 functional unit +instruct Nop_A1( ) %{ + ins_cost(0); + + format %{ "NOP ! Alu Pipeline" %} + opcode(Assembler::or_op3, Assembler::arith_op); + ins_encode( form2_nop() ); + ins_pipe(ialu_nop_A1); +%} + +// Nop using the memory functional unit +instruct Nop_MS( ) %{ + ins_cost(0); + + format %{ "NOP ! Memory Pipeline" %} + ins_encode( emit_mem_nop ); + ins_pipe(mem_nop); +%} + +// Nop using the floating add functional unit +instruct Nop_FA( ) %{ + ins_cost(0); + + format %{ "NOP ! Floating Add Pipeline" %} + ins_encode( emit_fadd_nop ); + ins_pipe(fadd_nop); +%} + +// Nop using the branch functional unit +instruct Nop_BR( ) %{ + ins_cost(0); + + format %{ "NOP ! Branch Pipeline" %} + ins_encode( emit_br_nop ); + ins_pipe(br_nop); +%} + +//----------Load/Store/Move Instructions--------------------------------------- +//----------Load Instructions-------------------------------------------------- +// Load Byte (8bit signed) +instruct loadB(iRegI dst, memory mem) %{ + match(Set dst (LoadB mem)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDSB $mem,$dst" %} + opcode(Assembler::ldsb_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mask_mem); +%} + +// Load Byte (8bit UNsigned) into an int reg +instruct loadUB(iRegI dst, memory mem, immI_255 bytemask) %{ + match(Set dst (AndI (LoadB mem) bytemask)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDUB $mem,$dst" %} + opcode(Assembler::ldub_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mask_mem); +%} + +// Load Byte (8bit UNsigned) into a Long Register +instruct loadUBL(iRegL dst, memory mem, immL_FF bytemask) %{ + match(Set dst (AndL (ConvI2L (LoadB mem)) bytemask)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDUB $mem,$dst" %} + opcode(Assembler::ldub_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mask_mem); +%} + +// Load Char (16bit UNsigned) into a Long Register +instruct loadUCL(iRegL dst, memory mem, immL_FFFF bytemask) %{ + match(Set dst (AndL (ConvI2L (LoadC mem)) bytemask)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDUH $mem,$dst" %} + opcode(Assembler::lduh_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mask_mem); +%} + +// Load Char (16bit unsigned) +instruct loadC(iRegI dst, memory mem) %{ + match(Set dst (LoadC mem)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDUH $mem,$dst" %} + opcode(Assembler::lduh_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mask_mem); +%} + +// Load Integer +instruct loadI(iRegI dst, memory mem) %{ + match(Set dst (LoadI mem)); + ins_cost(MEMORY_REF_COST); + size(4); + + format %{ "LDUW $mem,$dst" %} + opcode(Assembler::lduw_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mem); +%} + +// Load Long - aligned +instruct loadL(iRegL dst, memory mem ) %{ + match(Set dst (LoadL mem)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LDX $mem,$dst\t! long" %} + opcode(Assembler::ldx_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mem); +%} + +// Load Long - UNaligned +instruct loadL_unaligned(iRegL dst, memory mem, o7RegI tmp) %{ + match(Set dst (LoadL_unaligned mem)); + effect(KILL tmp); + ins_cost(MEMORY_REF_COST*2+DEFAULT_COST); + size(16); + format %{ "LDUW $mem+4,R_O7\t! misaligned long\n" + "\tLDUW $mem ,$dst\n" + "\tSLLX #32, $dst, $dst\n" + "\tOR $dst, R_O7, $dst" %} + opcode(Assembler::lduw_op3); + ins_encode( form3_mem_reg_long_unaligned_marshal( mem, dst )); + ins_pipe(iload_mem); +%} + +// Load Aligned Packed Byte into a Double Register +instruct loadA8B(regD dst, memory mem) %{ + match(Set dst (Load8B mem)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LDDF $mem,$dst\t! packed8B" %} + opcode(Assembler::lddf_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(floadD_mem); +%} + +// Load Aligned Packed Char into a Double Register +instruct loadA4C(regD dst, memory mem) %{ + match(Set dst (Load4C mem)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LDDF $mem,$dst\t! packed4C" %} + opcode(Assembler::lddf_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(floadD_mem); +%} + +// Load Aligned Packed Short into a Double Register +instruct loadA4S(regD dst, memory mem) %{ + match(Set dst (Load4S mem)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LDDF $mem,$dst\t! packed4S" %} + opcode(Assembler::lddf_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(floadD_mem); +%} + +// Load Aligned Packed Int into a Double Register +instruct loadA2I(regD dst, memory mem) %{ + match(Set dst (Load2I mem)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LDDF $mem,$dst\t! packed2I" %} + opcode(Assembler::lddf_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(floadD_mem); +%} + +// Load Range +instruct loadRange(iRegI dst, memory mem) %{ + match(Set dst (LoadRange mem)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDUW $mem,$dst\t! range" %} + opcode(Assembler::lduw_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mem); +%} + +// Load Integer into %f register (for fitos/fitod) +instruct loadI_freg(regF dst, memory mem) %{ + match(Set dst (LoadI mem)); + ins_cost(MEMORY_REF_COST); + size(4); + + format %{ "LDF $mem,$dst\t! for fitos/fitod" %} + opcode(Assembler::ldf_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(floadF_mem); +%} + +// Load Pointer +instruct loadP(iRegP dst, memory mem) %{ + match(Set dst (LoadP mem)); + ins_cost(MEMORY_REF_COST); + size(4); + +#ifndef _LP64 + format %{ "LDUW $mem,$dst\t! ptr" %} + opcode(Assembler::lduw_op3, 0, REGP_OP); +#else + format %{ "LDX $mem,$dst\t! ptr" %} + opcode(Assembler::ldx_op3, 0, REGP_OP); +#endif + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mem); +%} + +// Load Klass Pointer +instruct loadKlass(iRegP dst, memory mem) %{ + match(Set dst (LoadKlass mem)); + ins_cost(MEMORY_REF_COST); + size(4); + +#ifndef _LP64 + format %{ "LDUW $mem,$dst\t! klass ptr" %} + opcode(Assembler::lduw_op3, 0, REGP_OP); +#else + format %{ "LDX $mem,$dst\t! klass ptr" %} + opcode(Assembler::ldx_op3, 0, REGP_OP); +#endif + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mem); +%} + +// Load Short (16bit signed) +instruct loadS(iRegI dst, memory mem) %{ + match(Set dst (LoadS mem)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDSH $mem,$dst" %} + opcode(Assembler::ldsh_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mask_mem); +%} + +// Load Double +instruct loadD(regD dst, memory mem) %{ + match(Set dst (LoadD mem)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDDF $mem,$dst" %} + opcode(Assembler::lddf_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(floadD_mem); +%} + +// Load Double - UNaligned +instruct loadD_unaligned(regD_low dst, memory mem ) %{ + match(Set dst (LoadD_unaligned mem)); + ins_cost(MEMORY_REF_COST*2+DEFAULT_COST); + size(8); + format %{ "LDF $mem ,$dst.hi\t! misaligned double\n" + "\tLDF $mem+4,$dst.lo\t!" %} + opcode(Assembler::ldf_op3); + ins_encode( form3_mem_reg_double_unaligned( mem, dst )); + ins_pipe(iload_mem); +%} + +// Load Float +instruct loadF(regF dst, memory mem) %{ + match(Set dst (LoadF mem)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDF $mem,$dst" %} + opcode(Assembler::ldf_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(floadF_mem); +%} + +// Load Constant +instruct loadConI( iRegI dst, immI src ) %{ + match(Set dst src); + ins_cost(DEFAULT_COST * 3/2); + format %{ "SET $src,$dst" %} + ins_encode( Set32(src, dst) ); + ins_pipe(ialu_hi_lo_reg); +%} + +instruct loadConI13( iRegI dst, immI13 src ) %{ + match(Set dst src); + + size(4); + format %{ "MOV $src,$dst" %} + ins_encode( Set13( src, dst ) ); + ins_pipe(ialu_imm); +%} + +instruct loadConP(iRegP dst, immP src) %{ + match(Set dst src); + ins_cost(DEFAULT_COST * 3/2); + format %{ "SET $src,$dst\t!ptr" %} + // This rule does not use "expand" unlike loadConI because then + // the result type is not known to be an Oop. An ADLC + // enhancement will be needed to make that work - not worth it! + + ins_encode( SetPtr( src, dst ) ); + ins_pipe(loadConP); + +%} + +instruct loadConP0(iRegP dst, immP0 src) %{ + match(Set dst src); + + size(4); + format %{ "CLR $dst\t!ptr" %} + ins_encode( SetNull( dst ) ); + ins_pipe(ialu_imm); +%} + +instruct loadConP_poll(iRegP dst, immP_poll src) %{ + match(Set dst src); + ins_cost(DEFAULT_COST); + format %{ "SET $src,$dst\t!ptr" %} + ins_encode %{ + Address polling_page(reg_to_register_object($dst$$reg), (address)os::get_polling_page()); + __ sethi(polling_page, false ); + %} + ins_pipe(loadConP_poll); +%} + +instruct loadConL(iRegL dst, immL src, o7RegL tmp) %{ + // %%% maybe this should work like loadConD + match(Set dst src); + effect(KILL tmp); + ins_cost(DEFAULT_COST * 4); + format %{ "SET64 $src,$dst KILL $tmp\t! long" %} + ins_encode( LdImmL(src, dst, tmp) ); + ins_pipe(loadConL); +%} + +instruct loadConL0( iRegL dst, immL0 src ) %{ + match(Set dst src); + ins_cost(DEFAULT_COST); + size(4); + format %{ "CLR $dst\t! long" %} + ins_encode( Set13( src, dst ) ); + ins_pipe(ialu_imm); +%} + +instruct loadConL13( iRegL dst, immL13 src ) %{ + match(Set dst src); + ins_cost(DEFAULT_COST * 2); + + size(4); + format %{ "MOV $src,$dst\t! long" %} + ins_encode( Set13( src, dst ) ); + ins_pipe(ialu_imm); +%} + +instruct loadConF(regF dst, immF src, o7RegP tmp) %{ + match(Set dst src); + effect(KILL tmp); + +#ifdef _LP64 + size(36); +#else + size(8); +#endif + + format %{ "SETHI hi(&$src),$tmp\t!get float $src from table\n\t" + "LDF [$tmp+lo(&$src)],$dst" %} + ins_encode( LdImmF(src, dst, tmp) ); + ins_pipe(loadConFD); +%} + +instruct loadConD(regD dst, immD src, o7RegP tmp) %{ + match(Set dst src); + effect(KILL tmp); + +#ifdef _LP64 + size(36); +#else + size(8); +#endif + + format %{ "SETHI hi(&$src),$tmp\t!get double $src from table\n\t" + "LDDF [$tmp+lo(&$src)],$dst" %} + ins_encode( LdImmD(src, dst, tmp) ); + ins_pipe(loadConFD); +%} + +// Prefetch instructions. +// Must be safe to execute with invalid address (cannot fault). + +instruct prefetchr( memory mem ) %{ + match( PrefetchRead mem ); + ins_cost(MEMORY_REF_COST); + + format %{ "PREFETCH $mem,0\t! Prefetch read-many" %} + opcode(Assembler::prefetch_op3); + ins_encode( form3_mem_prefetch_read( mem ) ); + ins_pipe(iload_mem); +%} + +instruct prefetchw( memory mem ) %{ + match( PrefetchWrite mem ); + ins_cost(MEMORY_REF_COST); + + format %{ "PREFETCH $mem,2\t! Prefetch write-many (and read)" %} + opcode(Assembler::prefetch_op3); + ins_encode( form3_mem_prefetch_write( mem ) ); + ins_pipe(iload_mem); +%} + + +//----------Store Instructions------------------------------------------------- +// Store Byte +instruct storeB(memory mem, iRegI src) %{ + match(Set mem (StoreB mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STB $src,$mem\t! byte" %} + opcode(Assembler::stb_op3); + ins_encode( form3_mem_reg( mem, src ) ); + ins_pipe(istore_mem_reg); +%} + +instruct storeB0(memory mem, immI0 src) %{ + match(Set mem (StoreB mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STB $src,$mem\t! byte" %} + opcode(Assembler::stb_op3); + ins_encode( form3_mem_reg( mem, R_G0 ) ); + ins_pipe(istore_mem_zero); +%} + +instruct storeCM0(memory mem, immI0 src) %{ + match(Set mem (StoreCM mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STB $src,$mem\t! CMS card-mark byte 0" %} + opcode(Assembler::stb_op3); + ins_encode( form3_mem_reg( mem, R_G0 ) ); + ins_pipe(istore_mem_zero); +%} + +// Store Char/Short +instruct storeC(memory mem, iRegI src) %{ + match(Set mem (StoreC mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STH $src,$mem\t! short" %} + opcode(Assembler::sth_op3); + ins_encode( form3_mem_reg( mem, src ) ); + ins_pipe(istore_mem_reg); +%} + +instruct storeC0(memory mem, immI0 src) %{ + match(Set mem (StoreC mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STH $src,$mem\t! short" %} + opcode(Assembler::sth_op3); + ins_encode( form3_mem_reg( mem, R_G0 ) ); + ins_pipe(istore_mem_zero); +%} + +// Store Integer +instruct storeI(memory mem, iRegI src) %{ + match(Set mem (StoreI mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STW $src,$mem" %} + opcode(Assembler::stw_op3); + ins_encode( form3_mem_reg( mem, src ) ); + ins_pipe(istore_mem_reg); +%} + +// Store Long +instruct storeL(memory mem, iRegL src) %{ + match(Set mem (StoreL mem src)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STX $src,$mem\t! long" %} + opcode(Assembler::stx_op3); + ins_encode( form3_mem_reg( mem, src ) ); + ins_pipe(istore_mem_reg); +%} + +instruct storeI0(memory mem, immI0 src) %{ + match(Set mem (StoreI mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STW $src,$mem" %} + opcode(Assembler::stw_op3); + ins_encode( form3_mem_reg( mem, R_G0 ) ); + ins_pipe(istore_mem_zero); +%} + +instruct storeL0(memory mem, immL0 src) %{ + match(Set mem (StoreL mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STX $src,$mem" %} + opcode(Assembler::stx_op3); + ins_encode( form3_mem_reg( mem, R_G0 ) ); + ins_pipe(istore_mem_zero); +%} + +// Store Integer from float register (used after fstoi) +instruct storeI_Freg(memory mem, regF src) %{ + match(Set mem (StoreI mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STF $src,$mem\t! after fstoi/fdtoi" %} + opcode(Assembler::stf_op3); + ins_encode( form3_mem_reg( mem, src ) ); + ins_pipe(fstoreF_mem_reg); +%} + +// Store Pointer +instruct storeP(memory dst, sp_ptr_RegP src) %{ + match(Set dst (StoreP dst src)); + ins_cost(MEMORY_REF_COST); + size(4); + +#ifndef _LP64 + format %{ "STW $src,$dst\t! ptr" %} + opcode(Assembler::stw_op3, 0, REGP_OP); +#else + format %{ "STX $src,$dst\t! ptr" %} + opcode(Assembler::stx_op3, 0, REGP_OP); +#endif + ins_encode( form3_mem_reg( dst, src ) ); + ins_pipe(istore_mem_spORreg); +%} + +instruct storeP0(memory dst, immP0 src) %{ + match(Set dst (StoreP dst src)); + ins_cost(MEMORY_REF_COST); + size(4); + +#ifndef _LP64 + format %{ "STW $src,$dst\t! ptr" %} + opcode(Assembler::stw_op3, 0, REGP_OP); +#else + format %{ "STX $src,$dst\t! ptr" %} + opcode(Assembler::stx_op3, 0, REGP_OP); +#endif + ins_encode( form3_mem_reg( dst, R_G0 ) ); + ins_pipe(istore_mem_zero); +%} + +// Store Double +instruct storeD( memory mem, regD src) %{ + match(Set mem (StoreD mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STDF $src,$mem" %} + opcode(Assembler::stdf_op3); + ins_encode( form3_mem_reg( mem, src ) ); + ins_pipe(fstoreD_mem_reg); +%} + +instruct storeD0( memory mem, immD0 src) %{ + match(Set mem (StoreD mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STX $src,$mem" %} + opcode(Assembler::stx_op3); + ins_encode( form3_mem_reg( mem, R_G0 ) ); + ins_pipe(fstoreD_mem_zero); +%} + +// Store Float +instruct storeF( memory mem, regF src) %{ + match(Set mem (StoreF mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STF $src,$mem" %} + opcode(Assembler::stf_op3); + ins_encode( form3_mem_reg( mem, src ) ); + ins_pipe(fstoreF_mem_reg); +%} + +instruct storeF0( memory mem, immF0 src) %{ + match(Set mem (StoreF mem src)); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STW $src,$mem\t! storeF0" %} + opcode(Assembler::stw_op3); + ins_encode( form3_mem_reg( mem, R_G0 ) ); + ins_pipe(fstoreF_mem_zero); +%} + +// Store Aligned Packed Bytes in Double register to memory +instruct storeA8B(memory mem, regD src) %{ + match(Set mem (Store8B mem src)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STDF $src,$mem\t! packed8B" %} + opcode(Assembler::stdf_op3); + ins_encode( form3_mem_reg( mem, src ) ); + ins_pipe(fstoreD_mem_reg); +%} + +// Store Zero into Aligned Packed Bytes +instruct storeA8B0(memory mem, immI0 zero) %{ + match(Set mem (Store8B mem zero)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STX $zero,$mem\t! packed8B" %} + opcode(Assembler::stx_op3); + ins_encode( form3_mem_reg( mem, R_G0 ) ); + ins_pipe(fstoreD_mem_zero); +%} + +// Store Aligned Packed Chars/Shorts in Double register to memory +instruct storeA4C(memory mem, regD src) %{ + match(Set mem (Store4C mem src)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STDF $src,$mem\t! packed4C" %} + opcode(Assembler::stdf_op3); + ins_encode( form3_mem_reg( mem, src ) ); + ins_pipe(fstoreD_mem_reg); +%} + +// Store Zero into Aligned Packed Chars/Shorts +instruct storeA4C0(memory mem, immI0 zero) %{ + match(Set mem (Store4C mem (Replicate4C zero))); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STX $zero,$mem\t! packed4C" %} + opcode(Assembler::stx_op3); + ins_encode( form3_mem_reg( mem, R_G0 ) ); + ins_pipe(fstoreD_mem_zero); +%} + +// Store Aligned Packed Ints in Double register to memory +instruct storeA2I(memory mem, regD src) %{ + match(Set mem (Store2I mem src)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STDF $src,$mem\t! packed2I" %} + opcode(Assembler::stdf_op3); + ins_encode( form3_mem_reg( mem, src ) ); + ins_pipe(fstoreD_mem_reg); +%} + +// Store Zero into Aligned Packed Ints +instruct storeA2I0(memory mem, immI0 zero) %{ + match(Set mem (Store2I mem zero)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "STX $zero,$mem\t! packed2I" %} + opcode(Assembler::stx_op3); + ins_encode( form3_mem_reg( mem, R_G0 ) ); + ins_pipe(fstoreD_mem_zero); +%} + + +//----------MemBar Instructions----------------------------------------------- +// Memory barrier flavors + +instruct membar_acquire() %{ + match(MemBarAcquire); + ins_cost(4*MEMORY_REF_COST); + + size(0); + format %{ "MEMBAR-acquire" %} + ins_encode( enc_membar_acquire ); + ins_pipe(long_memory_op); +%} + +instruct membar_acquire_lock() %{ + match(MemBarAcquire); + predicate(Matcher::prior_fast_lock(n)); + ins_cost(0); + + size(0); + format %{ "!MEMBAR-acquire (CAS in prior FastLock so empty encoding)" %} + ins_encode( ); + ins_pipe(empty); +%} + +instruct membar_release() %{ + match(MemBarRelease); + ins_cost(4*MEMORY_REF_COST); + + size(0); + format %{ "MEMBAR-release" %} + ins_encode( enc_membar_release ); + ins_pipe(long_memory_op); +%} + +instruct membar_release_lock() %{ + match(MemBarRelease); + predicate(Matcher::post_fast_unlock(n)); + ins_cost(0); + + size(0); + format %{ "!MEMBAR-release (CAS in succeeding FastUnlock so empty encoding)" %} + ins_encode( ); + ins_pipe(empty); +%} + +instruct membar_volatile() %{ + match(MemBarVolatile); + ins_cost(4*MEMORY_REF_COST); + + size(4); + format %{ "MEMBAR-volatile" %} + ins_encode( enc_membar_volatile ); + ins_pipe(long_memory_op); +%} + +instruct unnecessary_membar_volatile() %{ + match(MemBarVolatile); + predicate(Matcher::post_store_load_barrier(n)); + ins_cost(0); + + size(0); + format %{ "!MEMBAR-volatile (unnecessary so empty encoding)" %} + ins_encode( ); + ins_pipe(empty); +%} + +//----------Register Move Instructions----------------------------------------- +instruct roundDouble_nop(regD dst) %{ + match(Set dst (RoundDouble dst)); + ins_cost(0); + // SPARC results are already "rounded" (i.e., normal-format IEEE) + ins_encode( ); + ins_pipe(empty); +%} + + +instruct roundFloat_nop(regF dst) %{ + match(Set dst (RoundFloat dst)); + ins_cost(0); + // SPARC results are already "rounded" (i.e., normal-format IEEE) + ins_encode( ); + ins_pipe(empty); +%} + + +// Cast Index to Pointer for unsafe natives +instruct castX2P(iRegX src, iRegP dst) %{ + match(Set dst (CastX2P src)); + + format %{ "MOV $src,$dst\t! IntX->Ptr" %} + ins_encode( form3_g0_rs2_rd_move( src, dst ) ); + ins_pipe(ialu_reg); +%} + +// Cast Pointer to Index for unsafe natives +instruct castP2X(iRegP src, iRegX dst) %{ + match(Set dst (CastP2X src)); + + format %{ "MOV $src,$dst\t! Ptr->IntX" %} + ins_encode( form3_g0_rs2_rd_move( src, dst ) ); + ins_pipe(ialu_reg); +%} + +instruct stfSSD(stackSlotD stkSlot, regD src) %{ + // %%%% TO DO: Tell the coalescer that this kind of node is a copy! + match(Set stkSlot src); // chain rule + ins_cost(MEMORY_REF_COST); + format %{ "STDF $src,$stkSlot\t!stk" %} + opcode(Assembler::stdf_op3); + ins_encode(form3_mem_reg(stkSlot, src)); + ins_pipe(fstoreD_stk_reg); +%} + +instruct ldfSSD(regD dst, stackSlotD stkSlot) %{ + // %%%% TO DO: Tell the coalescer that this kind of node is a copy! + match(Set dst stkSlot); // chain rule + ins_cost(MEMORY_REF_COST); + format %{ "LDDF $stkSlot,$dst\t!stk" %} + opcode(Assembler::lddf_op3); + ins_encode(form3_mem_reg(stkSlot, dst)); + ins_pipe(floadD_stk); +%} + +instruct stfSSF(stackSlotF stkSlot, regF src) %{ + // %%%% TO DO: Tell the coalescer that this kind of node is a copy! + match(Set stkSlot src); // chain rule + ins_cost(MEMORY_REF_COST); + format %{ "STF $src,$stkSlot\t!stk" %} + opcode(Assembler::stf_op3); + ins_encode(form3_mem_reg(stkSlot, src)); + ins_pipe(fstoreF_stk_reg); +%} + +//----------Conditional Move--------------------------------------------------- +// Conditional move +instruct cmovIP_reg(cmpOpP cmp, flagsRegP pcc, iRegI dst, iRegI src) %{ + match(Set dst (CMoveI (Binary cmp pcc) (Binary dst src))); + ins_cost(150); + format %{ "MOV$cmp $pcc,$src,$dst" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::ptr_cc)) ); + ins_pipe(ialu_reg); +%} + +instruct cmovIP_imm(cmpOpP cmp, flagsRegP pcc, iRegI dst, immI11 src) %{ + match(Set dst (CMoveI (Binary cmp pcc) (Binary dst src))); + ins_cost(140); + format %{ "MOV$cmp $pcc,$src,$dst" %} + ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::ptr_cc)) ); + ins_pipe(ialu_imm); +%} + +instruct cmovII_reg(cmpOp cmp, flagsReg icc, iRegI dst, iRegI src) %{ + match(Set dst (CMoveI (Binary cmp icc) (Binary dst src))); + ins_cost(150); + size(4); + format %{ "MOV$cmp $icc,$src,$dst" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_reg); +%} + +instruct cmovII_imm(cmpOp cmp, flagsReg icc, iRegI dst, immI11 src) %{ + match(Set dst (CMoveI (Binary cmp icc) (Binary dst src))); + ins_cost(140); + size(4); + format %{ "MOV$cmp $icc,$src,$dst" %} + ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_imm); +%} + +instruct cmovII_U_reg(cmpOp cmp, flagsRegU icc, iRegI dst, iRegI src) %{ + match(Set dst (CMoveI (Binary cmp icc) (Binary dst src))); + ins_cost(150); + size(4); + format %{ "MOV$cmp $icc,$src,$dst" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_reg); +%} + +instruct cmovII_U_imm(cmpOp cmp, flagsRegU icc, iRegI dst, immI11 src) %{ + match(Set dst (CMoveI (Binary cmp icc) (Binary dst src))); + ins_cost(140); + size(4); + format %{ "MOV$cmp $icc,$src,$dst" %} + ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_imm); +%} + +instruct cmovIF_reg(cmpOpF cmp, flagsRegF fcc, iRegI dst, iRegI src) %{ + match(Set dst (CMoveI (Binary cmp fcc) (Binary dst src))); + ins_cost(150); + size(4); + format %{ "MOV$cmp $fcc,$src,$dst" %} + ins_encode( enc_cmov_reg_f(cmp,dst,src, fcc) ); + ins_pipe(ialu_reg); +%} + +instruct cmovIF_imm(cmpOpF cmp, flagsRegF fcc, iRegI dst, immI11 src) %{ + match(Set dst (CMoveI (Binary cmp fcc) (Binary dst src))); + ins_cost(140); + size(4); + format %{ "MOV$cmp $fcc,$src,$dst" %} + ins_encode( enc_cmov_imm_f(cmp,dst,src, fcc) ); + ins_pipe(ialu_imm); +%} + +// Conditional move +instruct cmovPP_reg(cmpOpP cmp, flagsRegP pcc, iRegP dst, iRegP src) %{ + match(Set dst (CMoveP (Binary cmp pcc) (Binary dst src))); + ins_cost(150); + format %{ "MOV$cmp $pcc,$src,$dst\t! ptr" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::ptr_cc)) ); + ins_pipe(ialu_reg); +%} + +instruct cmovPP_imm(cmpOpP cmp, flagsRegP pcc, iRegP dst, immP0 src) %{ + match(Set dst (CMoveP (Binary cmp pcc) (Binary dst src))); + ins_cost(140); + format %{ "MOV$cmp $pcc,$src,$dst\t! ptr" %} + ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::ptr_cc)) ); + ins_pipe(ialu_imm); +%} + +instruct cmovPI_reg(cmpOp cmp, flagsReg icc, iRegP dst, iRegP src) %{ + match(Set dst (CMoveP (Binary cmp icc) (Binary dst src))); + ins_cost(150); + + size(4); + format %{ "MOV$cmp $icc,$src,$dst\t! ptr" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_reg); +%} + +instruct cmovPI_imm(cmpOp cmp, flagsReg icc, iRegP dst, immP0 src) %{ + match(Set dst (CMoveP (Binary cmp icc) (Binary dst src))); + ins_cost(140); + + size(4); + format %{ "MOV$cmp $icc,$src,$dst\t! ptr" %} + ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_imm); +%} + +instruct cmovPF_reg(cmpOpF cmp, flagsRegF fcc, iRegP dst, iRegP src) %{ + match(Set dst (CMoveP (Binary cmp fcc) (Binary dst src))); + ins_cost(150); + size(4); + format %{ "MOV$cmp $fcc,$src,$dst" %} + ins_encode( enc_cmov_reg_f(cmp,dst,src, fcc) ); + ins_pipe(ialu_imm); +%} + +instruct cmovPF_imm(cmpOpF cmp, flagsRegF fcc, iRegP dst, immP0 src) %{ + match(Set dst (CMoveP (Binary cmp fcc) (Binary dst src))); + ins_cost(140); + size(4); + format %{ "MOV$cmp $fcc,$src,$dst" %} + ins_encode( enc_cmov_imm_f(cmp,dst,src, fcc) ); + ins_pipe(ialu_imm); +%} + +// Conditional move +instruct cmovFP_reg(cmpOpP cmp, flagsRegP pcc, regF dst, regF src) %{ + match(Set dst (CMoveF (Binary cmp pcc) (Binary dst src))); + ins_cost(150); + opcode(0x101); + format %{ "FMOVD$cmp $pcc,$src,$dst" %} + ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::ptr_cc)) ); + ins_pipe(int_conditional_float_move); +%} + +instruct cmovFI_reg(cmpOp cmp, flagsReg icc, regF dst, regF src) %{ + match(Set dst (CMoveF (Binary cmp icc) (Binary dst src))); + ins_cost(150); + + size(4); + format %{ "FMOVS$cmp $icc,$src,$dst" %} + opcode(0x101); + ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(int_conditional_float_move); +%} + +// Conditional move, +instruct cmovFF_reg(cmpOpF cmp, flagsRegF fcc, regF dst, regF src) %{ + match(Set dst (CMoveF (Binary cmp fcc) (Binary dst src))); + ins_cost(150); + size(4); + format %{ "FMOVF$cmp $fcc,$src,$dst" %} + opcode(0x1); + ins_encode( enc_cmovff_reg(cmp,fcc,dst,src) ); + ins_pipe(int_conditional_double_move); +%} + +// Conditional move +instruct cmovDP_reg(cmpOpP cmp, flagsRegP pcc, regD dst, regD src) %{ + match(Set dst (CMoveD (Binary cmp pcc) (Binary dst src))); + ins_cost(150); + size(4); + opcode(0x102); + format %{ "FMOVD$cmp $pcc,$src,$dst" %} + ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::ptr_cc)) ); + ins_pipe(int_conditional_double_move); +%} + +instruct cmovDI_reg(cmpOp cmp, flagsReg icc, regD dst, regD src) %{ + match(Set dst (CMoveD (Binary cmp icc) (Binary dst src))); + ins_cost(150); + + size(4); + format %{ "FMOVD$cmp $icc,$src,$dst" %} + opcode(0x102); + ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(int_conditional_double_move); +%} + +// Conditional move, +instruct cmovDF_reg(cmpOpF cmp, flagsRegF fcc, regD dst, regD src) %{ + match(Set dst (CMoveD (Binary cmp fcc) (Binary dst src))); + ins_cost(150); + size(4); + format %{ "FMOVD$cmp $fcc,$src,$dst" %} + opcode(0x2); + ins_encode( enc_cmovff_reg(cmp,fcc,dst,src) ); + ins_pipe(int_conditional_double_move); +%} + +// Conditional move +instruct cmovLP_reg(cmpOpP cmp, flagsRegP pcc, iRegL dst, iRegL src) %{ + match(Set dst (CMoveL (Binary cmp pcc) (Binary dst src))); + ins_cost(150); + format %{ "MOV$cmp $pcc,$src,$dst\t! long" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::ptr_cc)) ); + ins_pipe(ialu_reg); +%} + +instruct cmovLP_imm(cmpOpP cmp, flagsRegP pcc, iRegL dst, immI11 src) %{ + match(Set dst (CMoveL (Binary cmp pcc) (Binary dst src))); + ins_cost(140); + format %{ "MOV$cmp $pcc,$src,$dst\t! long" %} + ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::ptr_cc)) ); + ins_pipe(ialu_imm); +%} + +instruct cmovLI_reg(cmpOp cmp, flagsReg icc, iRegL dst, iRegL src) %{ + match(Set dst (CMoveL (Binary cmp icc) (Binary dst src))); + ins_cost(150); + + size(4); + format %{ "MOV$cmp $icc,$src,$dst\t! long" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_reg); +%} + + +instruct cmovLF_reg(cmpOpF cmp, flagsRegF fcc, iRegL dst, iRegL src) %{ + match(Set dst (CMoveL (Binary cmp fcc) (Binary dst src))); + ins_cost(150); + + size(4); + format %{ "MOV$cmp $fcc,$src,$dst\t! long" %} + ins_encode( enc_cmov_reg_f(cmp,dst,src, fcc) ); + ins_pipe(ialu_reg); +%} + + + +//----------OS and Locking Instructions---------------------------------------- + +// This name is KNOWN by the ADLC and cannot be changed. +// The ADLC forces a 'TypeRawPtr::BOTTOM' output type +// for this guy. +instruct tlsLoadP(g2RegP dst) %{ + match(Set dst (ThreadLocal)); + + size(0); + ins_cost(0); + format %{ "# TLS is in G2" %} + ins_encode( /*empty encoding*/ ); + ins_pipe(ialu_none); +%} + +instruct checkCastPP( iRegP dst ) %{ + match(Set dst (CheckCastPP dst)); + + size(0); + format %{ "# checkcastPP of $dst" %} + ins_encode( /*empty encoding*/ ); + ins_pipe(empty); +%} + + +instruct castPP( iRegP dst ) %{ + match(Set dst (CastPP dst)); + format %{ "# castPP of $dst" %} + ins_encode( /*empty encoding*/ ); + ins_pipe(empty); +%} + +instruct castII( iRegI dst ) %{ + match(Set dst (CastII dst)); + format %{ "# castII of $dst" %} + ins_encode( /*empty encoding*/ ); + ins_cost(0); + ins_pipe(empty); +%} + +//----------Arithmetic Instructions-------------------------------------------- +// Addition Instructions +// Register Addition +instruct addI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + match(Set dst (AddI src1 src2)); + + size(4); + format %{ "ADD $src1,$src2,$dst" %} + ins_encode %{ + __ add($src1$$Register, $src2$$Register, $dst$$Register); + %} + ins_pipe(ialu_reg_reg); +%} + +// Immediate Addition +instruct addI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{ + match(Set dst (AddI src1 src2)); + + size(4); + format %{ "ADD $src1,$src2,$dst" %} + opcode(Assembler::add_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Pointer Register Addition +instruct addP_reg_reg(iRegP dst, iRegP src1, iRegX src2) %{ + match(Set dst (AddP src1 src2)); + + size(4); + format %{ "ADD $src1,$src2,$dst" %} + opcode(Assembler::add_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Pointer Immediate Addition +instruct addP_reg_imm13(iRegP dst, iRegP src1, immX13 src2) %{ + match(Set dst (AddP src1 src2)); + + size(4); + format %{ "ADD $src1,$src2,$dst" %} + opcode(Assembler::add_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Long Addition +instruct addL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{ + match(Set dst (AddL src1 src2)); + + size(4); + format %{ "ADD $src1,$src2,$dst\t! long" %} + opcode(Assembler::add_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +instruct addL_reg_imm13(iRegL dst, iRegL src1, immL13 con) %{ + match(Set dst (AddL src1 con)); + + size(4); + format %{ "ADD $src1,$con,$dst" %} + opcode(Assembler::add_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, con, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +//----------Conditional_store-------------------------------------------------- +// Conditional-store of the updated heap-top. +// Used during allocation of the shared heap. +// Sets flags (EQ) on success. Implemented with a CASA on Sparc. + +// LoadP-locked. Same as a regular pointer load when used with a compare-swap +instruct loadPLocked(iRegP dst, memory mem) %{ + match(Set dst (LoadPLocked mem)); + ins_cost(MEMORY_REF_COST); + +#ifndef _LP64 + size(4); + format %{ "LDUW $mem,$dst\t! ptr" %} + opcode(Assembler::lduw_op3, 0, REGP_OP); +#else + format %{ "LDX $mem,$dst\t! ptr" %} + opcode(Assembler::ldx_op3, 0, REGP_OP); +#endif + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mem); +%} + +// LoadL-locked. Same as a regular long load when used with a compare-swap +instruct loadLLocked(iRegL dst, memory mem) %{ + match(Set dst (LoadLLocked mem)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LDX $mem,$dst\t! long" %} + opcode(Assembler::ldx_op3); + ins_encode( form3_mem_reg( mem, dst ) ); + ins_pipe(iload_mem); +%} + +instruct storePConditional( iRegP heap_top_ptr, iRegP oldval, g3RegP newval, flagsRegP pcc ) %{ + match(Set pcc (StorePConditional heap_top_ptr (Binary oldval newval))); + effect( KILL newval ); + format %{ "CASA [$heap_top_ptr],$oldval,R_G3\t! If $oldval==[$heap_top_ptr] Then store R_G3 into [$heap_top_ptr], set R_G3=[$heap_top_ptr] in any case\n\t" + "CMP R_G3,$oldval\t\t! See if we made progress" %} + ins_encode( enc_cas(heap_top_ptr,oldval,newval) ); + ins_pipe( long_memory_op ); +%} + +instruct storeLConditional_bool(iRegP mem_ptr, iRegL oldval, iRegL newval, iRegI res, o7RegI tmp1, flagsReg ccr ) %{ + match(Set res (StoreLConditional mem_ptr (Binary oldval newval))); + effect( USE mem_ptr, KILL ccr, KILL tmp1); + // Marshal the register pairs into V9 64-bit registers, then do the compare-and-swap + format %{ + "MOV $newval,R_O7\n\t" + "CASXA [$mem_ptr],$oldval,R_O7\t! If $oldval==[$mem_ptr] Then store R_O7 into [$mem_ptr], set R_O7=[$mem_ptr] in any case\n\t" + "CMP $oldval,R_O7\t\t! See if we made progress\n\t" + "MOV 1,$res\n\t" + "MOVne xcc,R_G0,$res" + %} + ins_encode( enc_casx(mem_ptr, oldval, newval), + enc_lflags_ne_to_boolean(res) ); + ins_pipe( long_memory_op ); +%} + +instruct storeLConditional_flags(iRegP mem_ptr, iRegL oldval, iRegL newval, flagsRegL xcc, o7RegI tmp1, immI0 zero) %{ + match(Set xcc (CmpI (StoreLConditional mem_ptr (Binary oldval newval)) zero)); + effect( USE mem_ptr, KILL tmp1); + // Marshal the register pairs into V9 64-bit registers, then do the compare-and-swap + format %{ + "MOV $newval,R_O7\n\t" + "CASXA [$mem_ptr],$oldval,R_O7\t! If $oldval==[$mem_ptr] Then store R_O7 into [$mem_ptr], set R_O7=[$mem_ptr] in any case\n\t" + "CMP $oldval,R_O7\t\t! See if we made progress" + %} + ins_encode( enc_casx(mem_ptr, oldval, newval)); + ins_pipe( long_memory_op ); +%} + +// No flag versions for CompareAndSwap{P,I,L} because matcher can't match them + +instruct compareAndSwapL_bool(iRegP mem_ptr, iRegL oldval, iRegL newval, iRegI res, o7RegI tmp1, flagsReg ccr ) %{ + match(Set res (CompareAndSwapL mem_ptr (Binary oldval newval))); + effect( USE mem_ptr, KILL ccr, KILL tmp1); + format %{ + "MOV $newval,O7\n\t" + "CASXA [$mem_ptr],$oldval,O7\t! If $oldval==[$mem_ptr] Then store O7 into [$mem_ptr], set O7=[$mem_ptr] in any case\n\t" + "CMP $oldval,O7\t\t! See if we made progress\n\t" + "MOV 1,$res\n\t" + "MOVne xcc,R_G0,$res" + %} + ins_encode( enc_casx(mem_ptr, oldval, newval), + enc_lflags_ne_to_boolean(res) ); + ins_pipe( long_memory_op ); +%} + + +instruct compareAndSwapI_bool(iRegP mem_ptr, iRegI oldval, iRegI newval, iRegI res, o7RegI tmp1, flagsReg ccr ) %{ + match(Set res (CompareAndSwapI mem_ptr (Binary oldval newval))); + effect( USE mem_ptr, KILL ccr, KILL tmp1); + format %{ + "MOV $newval,O7\n\t" + "CASA [$mem_ptr],$oldval,O7\t! If $oldval==[$mem_ptr] Then store O7 into [$mem_ptr], set O7=[$mem_ptr] in any case\n\t" + "CMP $oldval,O7\t\t! See if we made progress\n\t" + "MOV 1,$res\n\t" + "MOVne icc,R_G0,$res" + %} + ins_encode( enc_casi(mem_ptr, oldval, newval), + enc_iflags_ne_to_boolean(res) ); + ins_pipe( long_memory_op ); +%} + +instruct compareAndSwapP_bool(iRegP mem_ptr, iRegP oldval, iRegP newval, iRegI res, o7RegI tmp1, flagsReg ccr ) %{ + match(Set res (CompareAndSwapP mem_ptr (Binary oldval newval))); + effect( USE mem_ptr, KILL ccr, KILL tmp1); +#ifdef _LP64 + format %{ + "MOV $newval,O7\n\t" + "CASXA [$mem_ptr],$oldval,O7\t! If $oldval==[$mem_ptr] Then store O7 into [$mem_ptr], set O7=[$mem_ptr] in any case\n\t" + "CMP $oldval,O7\t\t! See if we made progress\n\t" + "MOV 1,$res\n\t" + "MOVne xcc,R_G0,$res" + %} + ins_encode( enc_casx(mem_ptr, oldval, newval), + enc_lflags_ne_to_boolean(res) ); +#else + format %{ + "MOV $newval,O7\n\t" + "CASA [$mem_ptr],$oldval,O7\t! If $oldval==[$mem_ptr] Then store O7 into [$mem_ptr], set O7=[$mem_ptr] in any case\n\t" + "CMP $oldval,O7\t\t! See if we made progress\n\t" + "MOV 1,$res\n\t" + "MOVne icc,R_G0,$res" + %} + ins_encode( enc_casi(mem_ptr, oldval, newval), + enc_iflags_ne_to_boolean(res) ); +#endif + ins_pipe( long_memory_op ); +%} + +//--------------------- +// Subtraction Instructions +// Register Subtraction +instruct subI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + match(Set dst (SubI src1 src2)); + + size(4); + format %{ "SUB $src1,$src2,$dst" %} + opcode(Assembler::sub_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Immediate Subtraction +instruct subI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{ + match(Set dst (SubI src1 src2)); + + size(4); + format %{ "SUB $src1,$src2,$dst" %} + opcode(Assembler::sub_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +instruct subI_zero_reg(iRegI dst, immI0 zero, iRegI src2) %{ + match(Set dst (SubI zero src2)); + + size(4); + format %{ "NEG $src2,$dst" %} + opcode(Assembler::sub_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( R_G0, src2, dst ) ); + ins_pipe(ialu_zero_reg); +%} + +// Long subtraction +instruct subL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{ + match(Set dst (SubL src1 src2)); + + size(4); + format %{ "SUB $src1,$src2,$dst\t! long" %} + opcode(Assembler::sub_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Immediate Subtraction +instruct subL_reg_imm13(iRegL dst, iRegL src1, immL13 con) %{ + match(Set dst (SubL src1 con)); + + size(4); + format %{ "SUB $src1,$con,$dst\t! long" %} + opcode(Assembler::sub_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, con, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Long negation +instruct negL_reg_reg(iRegL dst, immL0 zero, iRegL src2) %{ + match(Set dst (SubL zero src2)); + + size(4); + format %{ "NEG $src2,$dst\t! long" %} + opcode(Assembler::sub_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( R_G0, src2, dst ) ); + ins_pipe(ialu_zero_reg); +%} + +// Multiplication Instructions +// Integer Multiplication +// Register Multiplication +instruct mulI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + match(Set dst (MulI src1 src2)); + + size(4); + format %{ "MULX $src1,$src2,$dst" %} + opcode(Assembler::mulx_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(imul_reg_reg); +%} + +// Immediate Multiplication +instruct mulI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{ + match(Set dst (MulI src1 src2)); + + size(4); + format %{ "MULX $src1,$src2,$dst" %} + opcode(Assembler::mulx_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(imul_reg_imm); +%} + +instruct mulL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{ + match(Set dst (MulL src1 src2)); + ins_cost(DEFAULT_COST * 5); + size(4); + format %{ "MULX $src1,$src2,$dst\t! long" %} + opcode(Assembler::mulx_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(mulL_reg_reg); +%} + +// Immediate Multiplication +instruct mulL_reg_imm13(iRegL dst, iRegL src1, immL13 src2) %{ + match(Set dst (MulL src1 src2)); + ins_cost(DEFAULT_COST * 5); + size(4); + format %{ "MULX $src1,$src2,$dst" %} + opcode(Assembler::mulx_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(mulL_reg_imm); +%} + +// Integer Division +// Register Division +instruct divI_reg_reg(iRegI dst, iRegIsafe src1, iRegIsafe src2) %{ + match(Set dst (DivI src1 src2)); + ins_cost((2+71)*DEFAULT_COST); + + format %{ "SRA $src2,0,$src2\n\t" + "SRA $src1,0,$src1\n\t" + "SDIVX $src1,$src2,$dst" %} + ins_encode( idiv_reg( src1, src2, dst ) ); + ins_pipe(sdiv_reg_reg); +%} + +// Immediate Division +instruct divI_reg_imm13(iRegI dst, iRegIsafe src1, immI13 src2) %{ + match(Set dst (DivI src1 src2)); + ins_cost((2+71)*DEFAULT_COST); + + format %{ "SRA $src1,0,$src1\n\t" + "SDIVX $src1,$src2,$dst" %} + ins_encode( idiv_imm( src1, src2, dst ) ); + ins_pipe(sdiv_reg_imm); +%} + +//----------Div-By-10-Expansion------------------------------------------------ +// Extract hi bits of a 32x32->64 bit multiply. +// Expand rule only, not matched +instruct mul_hi(iRegIsafe dst, iRegIsafe src1, iRegIsafe src2 ) %{ + effect( DEF dst, USE src1, USE src2 ); + format %{ "MULX $src1,$src2,$dst\t! Used in div-by-10\n\t" + "SRLX $dst,#32,$dst\t\t! Extract only hi word of result" %} + ins_encode( enc_mul_hi(dst,src1,src2)); + ins_pipe(sdiv_reg_reg); +%} + +// Magic constant, reciprical of 10 +instruct loadConI_x66666667(iRegIsafe dst) %{ + effect( DEF dst ); + + size(8); + format %{ "SET 0x66666667,$dst\t! Used in div-by-10" %} + ins_encode( Set32(0x66666667, dst) ); + ins_pipe(ialu_hi_lo_reg); +%} + +// Register Shift Right Arithmatic Long by 32-63 +instruct sra_31( iRegI dst, iRegI src ) %{ + effect( DEF dst, USE src ); + format %{ "SRA $src,31,$dst\t! Used in div-by-10" %} + ins_encode( form3_rs1_rd_copysign_hi(src,dst) ); + ins_pipe(ialu_reg_reg); +%} + +// Arithmetic Shift Right by 8-bit immediate +instruct sra_reg_2( iRegI dst, iRegI src ) %{ + effect( DEF dst, USE src ); + format %{ "SRA $src,2,$dst\t! Used in div-by-10" %} + opcode(Assembler::sra_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src, 0x2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Integer DIV with 10 +instruct divI_10( iRegI dst, iRegIsafe src, immI10 div ) %{ + match(Set dst (DivI src div)); + ins_cost((6+6)*DEFAULT_COST); + expand %{ + iRegIsafe tmp1; // Killed temps; + iRegIsafe tmp2; // Killed temps; + iRegI tmp3; // Killed temps; + iRegI tmp4; // Killed temps; + loadConI_x66666667( tmp1 ); // SET 0x66666667 -> tmp1 + mul_hi( tmp2, src, tmp1 ); // MUL hibits(src * tmp1) -> tmp2 + sra_31( tmp3, src ); // SRA src,31 -> tmp3 + sra_reg_2( tmp4, tmp2 ); // SRA tmp2,2 -> tmp4 + subI_reg_reg( dst,tmp4,tmp3); // SUB tmp4 - tmp3 -> dst + %} +%} + +// Register Long Division +instruct divL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{ + match(Set dst (DivL src1 src2)); + ins_cost(DEFAULT_COST*71); + size(4); + format %{ "SDIVX $src1,$src2,$dst\t! long" %} + opcode(Assembler::sdivx_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(divL_reg_reg); +%} + +// Register Long Division +instruct divL_reg_imm13(iRegL dst, iRegL src1, immL13 src2) %{ + match(Set dst (DivL src1 src2)); + ins_cost(DEFAULT_COST*71); + size(4); + format %{ "SDIVX $src1,$src2,$dst\t! long" %} + opcode(Assembler::sdivx_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(divL_reg_imm); +%} + +// Integer Remainder +// Register Remainder +instruct modI_reg_reg(iRegI dst, iRegIsafe src1, iRegIsafe src2, o7RegP temp, flagsReg ccr ) %{ + match(Set dst (ModI src1 src2)); + effect( KILL ccr, KILL temp); + + format %{ "SREM $src1,$src2,$dst" %} + ins_encode( irem_reg(src1, src2, dst, temp) ); + ins_pipe(sdiv_reg_reg); +%} + +// Immediate Remainder +instruct modI_reg_imm13(iRegI dst, iRegIsafe src1, immI13 src2, o7RegP temp, flagsReg ccr ) %{ + match(Set dst (ModI src1 src2)); + effect( KILL ccr, KILL temp); + + format %{ "SREM $src1,$src2,$dst" %} + ins_encode( irem_imm(src1, src2, dst, temp) ); + ins_pipe(sdiv_reg_imm); +%} + +// Register Long Remainder +instruct divL_reg_reg_1(iRegL dst, iRegL src1, iRegL src2) %{ + effect(DEF dst, USE src1, USE src2); + size(4); + format %{ "SDIVX $src1,$src2,$dst\t! long" %} + opcode(Assembler::sdivx_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(divL_reg_reg); +%} + +// Register Long Division +instruct divL_reg_imm13_1(iRegL dst, iRegL src1, immL13 src2) %{ + effect(DEF dst, USE src1, USE src2); + size(4); + format %{ "SDIVX $src1,$src2,$dst\t! long" %} + opcode(Assembler::sdivx_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(divL_reg_imm); +%} + +instruct mulL_reg_reg_1(iRegL dst, iRegL src1, iRegL src2) %{ + effect(DEF dst, USE src1, USE src2); + size(4); + format %{ "MULX $src1,$src2,$dst\t! long" %} + opcode(Assembler::mulx_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(mulL_reg_reg); +%} + +// Immediate Multiplication +instruct mulL_reg_imm13_1(iRegL dst, iRegL src1, immL13 src2) %{ + effect(DEF dst, USE src1, USE src2); + size(4); + format %{ "MULX $src1,$src2,$dst" %} + opcode(Assembler::mulx_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(mulL_reg_imm); +%} + +instruct subL_reg_reg_1(iRegL dst, iRegL src1, iRegL src2) %{ + effect(DEF dst, USE src1, USE src2); + size(4); + format %{ "SUB $src1,$src2,$dst\t! long" %} + opcode(Assembler::sub_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +instruct subL_reg_reg_2(iRegL dst, iRegL src1, iRegL src2) %{ + effect(DEF dst, USE src1, USE src2); + size(4); + format %{ "SUB $src1,$src2,$dst\t! long" %} + opcode(Assembler::sub_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Register Long Remainder +instruct modL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{ + match(Set dst (ModL src1 src2)); + ins_cost(DEFAULT_COST*(71 + 6 + 1)); + expand %{ + iRegL tmp1; + iRegL tmp2; + divL_reg_reg_1(tmp1, src1, src2); + mulL_reg_reg_1(tmp2, tmp1, src2); + subL_reg_reg_1(dst, src1, tmp2); + %} +%} + +// Register Long Remainder +instruct modL_reg_imm13(iRegL dst, iRegL src1, immL13 src2) %{ + match(Set dst (ModL src1 src2)); + ins_cost(DEFAULT_COST*(71 + 6 + 1)); + expand %{ + iRegL tmp1; + iRegL tmp2; + divL_reg_imm13_1(tmp1, src1, src2); + mulL_reg_imm13_1(tmp2, tmp1, src2); + subL_reg_reg_2 (dst, src1, tmp2); + %} +%} + +// Integer Shift Instructions +// Register Shift Left +instruct shlI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + match(Set dst (LShiftI src1 src2)); + + size(4); + format %{ "SLL $src1,$src2,$dst" %} + opcode(Assembler::sll_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Register Shift Left Immediate +instruct shlI_reg_imm5(iRegI dst, iRegI src1, immU5 src2) %{ + match(Set dst (LShiftI src1 src2)); + + size(4); + format %{ "SLL $src1,$src2,$dst" %} + opcode(Assembler::sll_op3, Assembler::arith_op); + ins_encode( form3_rs1_imm5_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Register Shift Left +instruct shlL_reg_reg(iRegL dst, iRegL src1, iRegI src2) %{ + match(Set dst (LShiftL src1 src2)); + + size(4); + format %{ "SLLX $src1,$src2,$dst" %} + opcode(Assembler::sllx_op3, Assembler::arith_op); + ins_encode( form3_sd_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Register Shift Left Immediate +instruct shlL_reg_imm6(iRegL dst, iRegL src1, immU6 src2) %{ + match(Set dst (LShiftL src1 src2)); + + size(4); + format %{ "SLLX $src1,$src2,$dst" %} + opcode(Assembler::sllx_op3, Assembler::arith_op); + ins_encode( form3_sd_rs1_imm6_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Register Arithmetic Shift Right +instruct sarI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + match(Set dst (RShiftI src1 src2)); + size(4); + format %{ "SRA $src1,$src2,$dst" %} + opcode(Assembler::sra_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Register Arithmetic Shift Right Immediate +instruct sarI_reg_imm5(iRegI dst, iRegI src1, immU5 src2) %{ + match(Set dst (RShiftI src1 src2)); + + size(4); + format %{ "SRA $src1,$src2,$dst" %} + opcode(Assembler::sra_op3, Assembler::arith_op); + ins_encode( form3_rs1_imm5_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Register Shift Right Arithmatic Long +instruct sarL_reg_reg(iRegL dst, iRegL src1, iRegI src2) %{ + match(Set dst (RShiftL src1 src2)); + + size(4); + format %{ "SRAX $src1,$src2,$dst" %} + opcode(Assembler::srax_op3, Assembler::arith_op); + ins_encode( form3_sd_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Register Shift Left Immediate +instruct sarL_reg_imm6(iRegL dst, iRegL src1, immU6 src2) %{ + match(Set dst (RShiftL src1 src2)); + + size(4); + format %{ "SRAX $src1,$src2,$dst" %} + opcode(Assembler::srax_op3, Assembler::arith_op); + ins_encode( form3_sd_rs1_imm6_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Register Shift Right +instruct shrI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + match(Set dst (URShiftI src1 src2)); + + size(4); + format %{ "SRL $src1,$src2,$dst" %} + opcode(Assembler::srl_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Register Shift Right Immediate +instruct shrI_reg_imm5(iRegI dst, iRegI src1, immU5 src2) %{ + match(Set dst (URShiftI src1 src2)); + + size(4); + format %{ "SRL $src1,$src2,$dst" %} + opcode(Assembler::srl_op3, Assembler::arith_op); + ins_encode( form3_rs1_imm5_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Register Shift Right +instruct shrL_reg_reg(iRegL dst, iRegL src1, iRegI src2) %{ + match(Set dst (URShiftL src1 src2)); + + size(4); + format %{ "SRLX $src1,$src2,$dst" %} + opcode(Assembler::srlx_op3, Assembler::arith_op); + ins_encode( form3_sd_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Register Shift Right Immediate +instruct shrL_reg_imm6(iRegL dst, iRegL src1, immU6 src2) %{ + match(Set dst (URShiftL src1 src2)); + + size(4); + format %{ "SRLX $src1,$src2,$dst" %} + opcode(Assembler::srlx_op3, Assembler::arith_op); + ins_encode( form3_sd_rs1_imm6_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Register Shift Right Immediate with a CastP2X +#ifdef _LP64 +instruct shrP_reg_imm6(iRegL dst, iRegP src1, immU6 src2) %{ + match(Set dst (URShiftL (CastP2X src1) src2)); + size(4); + format %{ "SRLX $src1,$src2,$dst\t! Cast ptr $src1 to long and shift" %} + opcode(Assembler::srlx_op3, Assembler::arith_op); + ins_encode( form3_sd_rs1_imm6_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} +#else +instruct shrP_reg_imm5(iRegI dst, iRegP src1, immU5 src2) %{ + match(Set dst (URShiftI (CastP2X src1) src2)); + size(4); + format %{ "SRL $src1,$src2,$dst\t! Cast ptr $src1 to int and shift" %} + opcode(Assembler::srl_op3, Assembler::arith_op); + ins_encode( form3_rs1_imm5_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} +#endif + + +//----------Floating Point Arithmetic Instructions----------------------------- + +// Add float single precision +instruct addF_reg_reg(regF dst, regF src1, regF src2) %{ + match(Set dst (AddF src1 src2)); + + size(4); + format %{ "FADDS $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fadds_opf); + ins_encode(form3_opf_rs1F_rs2F_rdF(src1, src2, dst)); + ins_pipe(faddF_reg_reg); +%} + +// Add float double precision +instruct addD_reg_reg(regD dst, regD src1, regD src2) %{ + match(Set dst (AddD src1 src2)); + + size(4); + format %{ "FADDD $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::faddd_opf); + ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst)); + ins_pipe(faddD_reg_reg); +%} + +// Sub float single precision +instruct subF_reg_reg(regF dst, regF src1, regF src2) %{ + match(Set dst (SubF src1 src2)); + + size(4); + format %{ "FSUBS $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fsubs_opf); + ins_encode(form3_opf_rs1F_rs2F_rdF(src1, src2, dst)); + ins_pipe(faddF_reg_reg); +%} + +// Sub float double precision +instruct subD_reg_reg(regD dst, regD src1, regD src2) %{ + match(Set dst (SubD src1 src2)); + + size(4); + format %{ "FSUBD $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fsubd_opf); + ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst)); + ins_pipe(faddD_reg_reg); +%} + +// Mul float single precision +instruct mulF_reg_reg(regF dst, regF src1, regF src2) %{ + match(Set dst (MulF src1 src2)); + + size(4); + format %{ "FMULS $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fmuls_opf); + ins_encode(form3_opf_rs1F_rs2F_rdF(src1, src2, dst)); + ins_pipe(fmulF_reg_reg); +%} + +// Mul float double precision +instruct mulD_reg_reg(regD dst, regD src1, regD src2) %{ + match(Set dst (MulD src1 src2)); + + size(4); + format %{ "FMULD $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fmuld_opf); + ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst)); + ins_pipe(fmulD_reg_reg); +%} + +// Div float single precision +instruct divF_reg_reg(regF dst, regF src1, regF src2) %{ + match(Set dst (DivF src1 src2)); + + size(4); + format %{ "FDIVS $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fdivs_opf); + ins_encode(form3_opf_rs1F_rs2F_rdF(src1, src2, dst)); + ins_pipe(fdivF_reg_reg); +%} + +// Div float double precision +instruct divD_reg_reg(regD dst, regD src1, regD src2) %{ + match(Set dst (DivD src1 src2)); + + size(4); + format %{ "FDIVD $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fdivd_opf); + ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst)); + ins_pipe(fdivD_reg_reg); +%} + +// Absolute float double precision +instruct absD_reg(regD dst, regD src) %{ + match(Set dst (AbsD src)); + + format %{ "FABSd $src,$dst" %} + ins_encode(fabsd(dst, src)); + ins_pipe(faddD_reg); +%} + +// Absolute float single precision +instruct absF_reg(regF dst, regF src) %{ + match(Set dst (AbsF src)); + + format %{ "FABSs $src,$dst" %} + ins_encode(fabss(dst, src)); + ins_pipe(faddF_reg); +%} + +instruct negF_reg(regF dst, regF src) %{ + match(Set dst (NegF src)); + + size(4); + format %{ "FNEGs $src,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fnegs_opf); + ins_encode(form3_opf_rs2F_rdF(src, dst)); + ins_pipe(faddF_reg); +%} + +instruct negD_reg(regD dst, regD src) %{ + match(Set dst (NegD src)); + + format %{ "FNEGd $src,$dst" %} + ins_encode(fnegd(dst, src)); + ins_pipe(faddD_reg); +%} + +// Sqrt float double precision +instruct sqrtF_reg_reg(regF dst, regF src) %{ + match(Set dst (ConvD2F (SqrtD (ConvF2D src)))); + + size(4); + format %{ "FSQRTS $src,$dst" %} + ins_encode(fsqrts(dst, src)); + ins_pipe(fdivF_reg_reg); +%} + +// Sqrt float double precision +instruct sqrtD_reg_reg(regD dst, regD src) %{ + match(Set dst (SqrtD src)); + + size(4); + format %{ "FSQRTD $src,$dst" %} + ins_encode(fsqrtd(dst, src)); + ins_pipe(fdivD_reg_reg); +%} + +//----------Logical Instructions----------------------------------------------- +// And Instructions +// Register And +instruct andI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + match(Set dst (AndI src1 src2)); + + size(4); + format %{ "AND $src1,$src2,$dst" %} + opcode(Assembler::and_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Immediate And +instruct andI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{ + match(Set dst (AndI src1 src2)); + + size(4); + format %{ "AND $src1,$src2,$dst" %} + opcode(Assembler::and_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Register And Long +instruct andL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{ + match(Set dst (AndL src1 src2)); + + ins_cost(DEFAULT_COST); + size(4); + format %{ "AND $src1,$src2,$dst\t! long" %} + opcode(Assembler::and_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +instruct andL_reg_imm13(iRegL dst, iRegL src1, immL13 con) %{ + match(Set dst (AndL src1 con)); + + ins_cost(DEFAULT_COST); + size(4); + format %{ "AND $src1,$con,$dst\t! long" %} + opcode(Assembler::and_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, con, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Or Instructions +// Register Or +instruct orI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + match(Set dst (OrI src1 src2)); + + size(4); + format %{ "OR $src1,$src2,$dst" %} + opcode(Assembler::or_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Immediate Or +instruct orI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{ + match(Set dst (OrI src1 src2)); + + size(4); + format %{ "OR $src1,$src2,$dst" %} + opcode(Assembler::or_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Register Or Long +instruct orL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{ + match(Set dst (OrL src1 src2)); + + ins_cost(DEFAULT_COST); + size(4); + format %{ "OR $src1,$src2,$dst\t! long" %} + opcode(Assembler::or_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +instruct orL_reg_imm13(iRegL dst, iRegL src1, immL13 con) %{ + match(Set dst (OrL src1 con)); + ins_cost(DEFAULT_COST*2); + + ins_cost(DEFAULT_COST); + size(4); + format %{ "OR $src1,$con,$dst\t! long" %} + opcode(Assembler::or_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, con, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Xor Instructions +// Register Xor +instruct xorI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{ + match(Set dst (XorI src1 src2)); + + size(4); + format %{ "XOR $src1,$src2,$dst" %} + opcode(Assembler::xor_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Immediate Xor +instruct xorI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{ + match(Set dst (XorI src1 src2)); + + size(4); + format %{ "XOR $src1,$src2,$dst" %} + opcode(Assembler::xor_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Register Xor Long +instruct xorL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{ + match(Set dst (XorL src1 src2)); + + ins_cost(DEFAULT_COST); + size(4); + format %{ "XOR $src1,$src2,$dst\t! long" %} + opcode(Assembler::xor_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +instruct xorL_reg_imm13(iRegL dst, iRegL src1, immL13 con) %{ + match(Set dst (XorL src1 con)); + + ins_cost(DEFAULT_COST); + size(4); + format %{ "XOR $src1,$con,$dst\t! long" %} + opcode(Assembler::xor_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( src1, con, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +//----------Convert to Boolean------------------------------------------------- +// Nice hack for 32-bit tests but doesn't work for +// 64-bit pointers. +instruct convI2B( iRegI dst, iRegI src, flagsReg ccr ) %{ + match(Set dst (Conv2B src)); + effect( KILL ccr ); + ins_cost(DEFAULT_COST*2); + format %{ "CMP R_G0,$src\n\t" + "ADDX R_G0,0,$dst" %} + ins_encode( enc_to_bool( src, dst ) ); + ins_pipe(ialu_reg_ialu); +%} + +#ifndef _LP64 +instruct convP2B( iRegI dst, iRegP src, flagsReg ccr ) %{ + match(Set dst (Conv2B src)); + effect( KILL ccr ); + ins_cost(DEFAULT_COST*2); + format %{ "CMP R_G0,$src\n\t" + "ADDX R_G0,0,$dst" %} + ins_encode( enc_to_bool( src, dst ) ); + ins_pipe(ialu_reg_ialu); +%} +#else +instruct convP2B( iRegI dst, iRegP src ) %{ + match(Set dst (Conv2B src)); + ins_cost(DEFAULT_COST*2); + format %{ "MOV $src,$dst\n\t" + "MOVRNZ $src,1,$dst" %} + ins_encode( form3_g0_rs2_rd_move( src, dst ), enc_convP2B( dst, src ) ); + ins_pipe(ialu_clr_and_mover); +%} +#endif + +instruct cmpLTMask_reg_reg( iRegI dst, iRegI p, iRegI q, flagsReg ccr ) %{ + match(Set dst (CmpLTMask p q)); + effect( KILL ccr ); + ins_cost(DEFAULT_COST*4); + format %{ "CMP $p,$q\n\t" + "MOV #0,$dst\n\t" + "BLT,a .+8\n\t" + "MOV #-1,$dst" %} + ins_encode( enc_ltmask(p,q,dst) ); + ins_pipe(ialu_reg_reg_ialu); +%} + +instruct cadd_cmpLTMask( iRegI p, iRegI q, iRegI y, iRegI tmp, flagsReg ccr ) %{ + match(Set p (AddI (AndI (CmpLTMask p q) y) (SubI p q))); + effect(KILL ccr, TEMP tmp); + ins_cost(DEFAULT_COST*3); + + format %{ "SUBcc $p,$q,$p\t! p' = p-q\n\t" + "ADD $p,$y,$tmp\t! g3=p-q+y\n\t" + "MOVl $tmp,$p\t! p' < 0 ? p'+y : p'" %} + ins_encode( enc_cadd_cmpLTMask(p, q, y, tmp) ); + ins_pipe( cadd_cmpltmask ); +%} + +instruct cadd_cmpLTMask2( iRegI p, iRegI q, iRegI y, iRegI tmp, flagsReg ccr ) %{ + match(Set p (AddI (SubI p q) (AndI (CmpLTMask p q) y))); + effect( KILL ccr, TEMP tmp); + ins_cost(DEFAULT_COST*3); + + format %{ "SUBcc $p,$q,$p\t! p' = p-q\n\t" + "ADD $p,$y,$tmp\t! g3=p-q+y\n\t" + "MOVl $tmp,$p\t! p' < 0 ? p'+y : p'" %} + ins_encode( enc_cadd_cmpLTMask(p, q, y, tmp) ); + ins_pipe( cadd_cmpltmask ); +%} + +//----------Arithmetic Conversion Instructions--------------------------------- +// The conversions operations are all Alpha sorted. Please keep it that way! + +instruct convD2F_reg(regF dst, regD src) %{ + match(Set dst (ConvD2F src)); + size(4); + format %{ "FDTOS $src,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fdtos_opf); + ins_encode(form3_opf_rs2D_rdF(src, dst)); + ins_pipe(fcvtD2F); +%} + + +// Convert a double to an int in a float register. +// If the double is a NAN, stuff a zero in instead. +instruct convD2I_helper(regF dst, regD src, flagsRegF0 fcc0) %{ + effect(DEF dst, USE src, KILL fcc0); + format %{ "FCMPd fcc0,$src,$src\t! check for NAN\n\t" + "FBO,pt fcc0,skip\t! branch on ordered, predict taken\n\t" + "FDTOI $src,$dst\t! convert in delay slot\n\t" + "FITOS $dst,$dst\t! change NaN/max-int to valid float\n\t" + "FSUBs $dst,$dst,$dst\t! cleared only if nan\n" + "skip:" %} + ins_encode(form_d2i_helper(src,dst)); + ins_pipe(fcvtD2I); +%} + +instruct convD2I_reg(stackSlotI dst, regD src) %{ + match(Set dst (ConvD2I src)); + ins_cost(DEFAULT_COST*2 + MEMORY_REF_COST*2 + BRANCH_COST); + expand %{ + regF tmp; + convD2I_helper(tmp, src); + regF_to_stkI(dst, tmp); + %} +%} + +// Convert a double to a long in a double register. +// If the double is a NAN, stuff a zero in instead. +instruct convD2L_helper(regD dst, regD src, flagsRegF0 fcc0) %{ + effect(DEF dst, USE src, KILL fcc0); + format %{ "FCMPd fcc0,$src,$src\t! check for NAN\n\t" + "FBO,pt fcc0,skip\t! branch on ordered, predict taken\n\t" + "FDTOX $src,$dst\t! convert in delay slot\n\t" + "FXTOD $dst,$dst\t! change NaN/max-long to valid double\n\t" + "FSUBd $dst,$dst,$dst\t! cleared only if nan\n" + "skip:" %} + ins_encode(form_d2l_helper(src,dst)); + ins_pipe(fcvtD2L); +%} + + +// Double to Long conversion +instruct convD2L_reg(stackSlotL dst, regD src) %{ + match(Set dst (ConvD2L src)); + ins_cost(DEFAULT_COST*2 + MEMORY_REF_COST*2 + BRANCH_COST); + expand %{ + regD tmp; + convD2L_helper(tmp, src); + regD_to_stkL(dst, tmp); + %} +%} + + +instruct convF2D_reg(regD dst, regF src) %{ + match(Set dst (ConvF2D src)); + format %{ "FSTOD $src,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fstod_opf); + ins_encode(form3_opf_rs2F_rdD(src, dst)); + ins_pipe(fcvtF2D); +%} + + +instruct convF2I_helper(regF dst, regF src, flagsRegF0 fcc0) %{ + effect(DEF dst, USE src, KILL fcc0); + format %{ "FCMPs fcc0,$src,$src\t! check for NAN\n\t" + "FBO,pt fcc0,skip\t! branch on ordered, predict taken\n\t" + "FSTOI $src,$dst\t! convert in delay slot\n\t" + "FITOS $dst,$dst\t! change NaN/max-int to valid float\n\t" + "FSUBs $dst,$dst,$dst\t! cleared only if nan\n" + "skip:" %} + ins_encode(form_f2i_helper(src,dst)); + ins_pipe(fcvtF2I); +%} + +instruct convF2I_reg(stackSlotI dst, regF src) %{ + match(Set dst (ConvF2I src)); + ins_cost(DEFAULT_COST*2 + MEMORY_REF_COST*2 + BRANCH_COST); + expand %{ + regF tmp; + convF2I_helper(tmp, src); + regF_to_stkI(dst, tmp); + %} +%} + + +instruct convF2L_helper(regD dst, regF src, flagsRegF0 fcc0) %{ + effect(DEF dst, USE src, KILL fcc0); + format %{ "FCMPs fcc0,$src,$src\t! check for NAN\n\t" + "FBO,pt fcc0,skip\t! branch on ordered, predict taken\n\t" + "FSTOX $src,$dst\t! convert in delay slot\n\t" + "FXTOD $dst,$dst\t! change NaN/max-long to valid double\n\t" + "FSUBd $dst,$dst,$dst\t! cleared only if nan\n" + "skip:" %} + ins_encode(form_f2l_helper(src,dst)); + ins_pipe(fcvtF2L); +%} + +// Float to Long conversion +instruct convF2L_reg(stackSlotL dst, regF src) %{ + match(Set dst (ConvF2L src)); + ins_cost(DEFAULT_COST*2 + MEMORY_REF_COST*2 + BRANCH_COST); + expand %{ + regD tmp; + convF2L_helper(tmp, src); + regD_to_stkL(dst, tmp); + %} +%} + + +instruct convI2D_helper(regD dst, regF tmp) %{ + effect(USE tmp, DEF dst); + format %{ "FITOD $tmp,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fitod_opf); + ins_encode(form3_opf_rs2F_rdD(tmp, dst)); + ins_pipe(fcvtI2D); +%} + +instruct convI2D_reg(stackSlotI src, regD dst) %{ + match(Set dst (ConvI2D src)); + ins_cost(DEFAULT_COST + MEMORY_REF_COST); + expand %{ + regF tmp; + stkI_to_regF( tmp, src); + convI2D_helper( dst, tmp); + %} +%} + +instruct convI2D_mem( regD_low dst, memory mem ) %{ + match(Set dst (ConvI2D (LoadI mem))); + ins_cost(DEFAULT_COST + MEMORY_REF_COST); + size(8); + format %{ "LDF $mem,$dst\n\t" + "FITOD $dst,$dst" %} + opcode(Assembler::ldf_op3, Assembler::fitod_opf); + ins_encode( form3_mem_reg( mem, dst ), form3_convI2F(dst, dst)); + ins_pipe(floadF_mem); +%} + + +instruct convI2F_helper(regF dst, regF tmp) %{ + effect(DEF dst, USE tmp); + format %{ "FITOS $tmp,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fitos_opf); + ins_encode(form3_opf_rs2F_rdF(tmp, dst)); + ins_pipe(fcvtI2F); +%} + +instruct convI2F_reg( regF dst, stackSlotI src ) %{ + match(Set dst (ConvI2F src)); + ins_cost(DEFAULT_COST + MEMORY_REF_COST); + expand %{ + regF tmp; + stkI_to_regF(tmp,src); + convI2F_helper(dst, tmp); + %} +%} + +instruct convI2F_mem( regF dst, memory mem ) %{ + match(Set dst (ConvI2F (LoadI mem))); + ins_cost(DEFAULT_COST + MEMORY_REF_COST); + size(8); + format %{ "LDF $mem,$dst\n\t" + "FITOS $dst,$dst" %} + opcode(Assembler::ldf_op3, Assembler::fitos_opf); + ins_encode( form3_mem_reg( mem, dst ), form3_convI2F(dst, dst)); + ins_pipe(floadF_mem); +%} + + +instruct convI2L_reg(iRegL dst, iRegI src) %{ + match(Set dst (ConvI2L src)); + size(4); + format %{ "SRA $src,0,$dst\t! int->long" %} + opcode(Assembler::sra_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src, R_G0, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Zero-extend convert int to long +instruct convI2L_reg_zex(iRegL dst, iRegI src, immL_32bits mask ) %{ + match(Set dst (AndL (ConvI2L src) mask) ); + size(4); + format %{ "SRL $src,0,$dst\t! zero-extend int to long" %} + opcode(Assembler::srl_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src, R_G0, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +// Zero-extend long +instruct zerox_long(iRegL dst, iRegL src, immL_32bits mask ) %{ + match(Set dst (AndL src mask) ); + size(4); + format %{ "SRL $src,0,$dst\t! zero-extend long" %} + opcode(Assembler::srl_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( src, R_G0, dst ) ); + ins_pipe(ialu_reg_reg); +%} + +instruct MoveF2I_stack_reg(iRegI dst, stackSlotF src) %{ + match(Set dst (MoveF2I src)); + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDUW $src,$dst\t! MoveF2I" %} + opcode(Assembler::lduw_op3); + ins_encode( form3_mem_reg( src, dst ) ); + ins_pipe(iload_mem); +%} + +instruct MoveI2F_stack_reg(regF dst, stackSlotI src) %{ + match(Set dst (MoveI2F src)); + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDF $src,$dst\t! MoveI2F" %} + opcode(Assembler::ldf_op3); + ins_encode(form3_mem_reg(src, dst)); + ins_pipe(floadF_stk); +%} + +instruct MoveD2L_stack_reg(iRegL dst, stackSlotD src) %{ + match(Set dst (MoveD2L src)); + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDX $src,$dst\t! MoveD2L" %} + opcode(Assembler::ldx_op3); + ins_encode( form3_mem_reg( src, dst ) ); + ins_pipe(iload_mem); +%} + +instruct MoveL2D_stack_reg(regD dst, stackSlotL src) %{ + match(Set dst (MoveL2D src)); + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "LDDF $src,$dst\t! MoveL2D" %} + opcode(Assembler::lddf_op3); + ins_encode(form3_mem_reg(src, dst)); + ins_pipe(floadD_stk); +%} + +instruct MoveF2I_reg_stack(stackSlotI dst, regF src) %{ + match(Set dst (MoveF2I src)); + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STF $src,$dst\t!MoveF2I" %} + opcode(Assembler::stf_op3); + ins_encode(form3_mem_reg(dst, src)); + ins_pipe(fstoreF_stk_reg); +%} + +instruct MoveI2F_reg_stack(stackSlotF dst, iRegI src) %{ + match(Set dst (MoveI2F src)); + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STW $src,$dst\t!MoveI2F" %} + opcode(Assembler::stw_op3); + ins_encode( form3_mem_reg( dst, src ) ); + ins_pipe(istore_mem_reg); +%} + +instruct MoveD2L_reg_stack(stackSlotL dst, regD src) %{ + match(Set dst (MoveD2L src)); + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STDF $src,$dst\t!MoveD2L" %} + opcode(Assembler::stdf_op3); + ins_encode(form3_mem_reg(dst, src)); + ins_pipe(fstoreD_stk_reg); +%} + +instruct MoveL2D_reg_stack(stackSlotD dst, iRegL src) %{ + match(Set dst (MoveL2D src)); + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + + size(4); + format %{ "STX $src,$dst\t!MoveL2D" %} + opcode(Assembler::stx_op3); + ins_encode( form3_mem_reg( dst, src ) ); + ins_pipe(istore_mem_reg); +%} + + +//----------- +// Long to Double conversion using V8 opcodes. +// Still useful because cheetah traps and becomes +// amazingly slow for some common numbers. + +// Magic constant, 0x43300000 +instruct loadConI_x43300000(iRegI dst) %{ + effect(DEF dst); + size(4); + format %{ "SETHI HI(0x43300000),$dst\t! 2^52" %} + ins_encode(SetHi22(0x43300000, dst)); + ins_pipe(ialu_none); +%} + +// Magic constant, 0x41f00000 +instruct loadConI_x41f00000(iRegI dst) %{ + effect(DEF dst); + size(4); + format %{ "SETHI HI(0x41f00000),$dst\t! 2^32" %} + ins_encode(SetHi22(0x41f00000, dst)); + ins_pipe(ialu_none); +%} + +// Construct a double from two float halves +instruct regDHi_regDLo_to_regD(regD_low dst, regD_low src1, regD_low src2) %{ + effect(DEF dst, USE src1, USE src2); + size(8); + format %{ "FMOVS $src1.hi,$dst.hi\n\t" + "FMOVS $src2.lo,$dst.lo" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fmovs_opf); + ins_encode(form3_opf_rs2D_hi_rdD_hi(src1, dst), form3_opf_rs2D_lo_rdD_lo(src2, dst)); + ins_pipe(faddD_reg_reg); +%} + +// Convert integer in high half of a double register (in the lower half of +// the double register file) to double +instruct convI2D_regDHi_regD(regD dst, regD_low src) %{ + effect(DEF dst, USE src); + size(4); + format %{ "FITOD $src,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fitod_opf); + ins_encode(form3_opf_rs2D_rdD(src, dst)); + ins_pipe(fcvtLHi2D); +%} + +// Add float double precision +instruct addD_regD_regD(regD dst, regD src1, regD src2) %{ + effect(DEF dst, USE src1, USE src2); + size(4); + format %{ "FADDD $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::faddd_opf); + ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst)); + ins_pipe(faddD_reg_reg); +%} + +// Sub float double precision +instruct subD_regD_regD(regD dst, regD src1, regD src2) %{ + effect(DEF dst, USE src1, USE src2); + size(4); + format %{ "FSUBD $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fsubd_opf); + ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst)); + ins_pipe(faddD_reg_reg); +%} + +// Mul float double precision +instruct mulD_regD_regD(regD dst, regD src1, regD src2) %{ + effect(DEF dst, USE src1, USE src2); + size(4); + format %{ "FMULD $src1,$src2,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fmuld_opf); + ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst)); + ins_pipe(fmulD_reg_reg); +%} + +instruct convL2D_reg_slow_fxtof(regD dst, stackSlotL src) %{ + match(Set dst (ConvL2D src)); + ins_cost(DEFAULT_COST*8 + MEMORY_REF_COST*6); + + expand %{ + regD_low tmpsrc; + iRegI ix43300000; + iRegI ix41f00000; + stackSlotL lx43300000; + stackSlotL lx41f00000; + regD_low dx43300000; + regD dx41f00000; + regD tmp1; + regD_low tmp2; + regD tmp3; + regD tmp4; + + stkL_to_regD(tmpsrc, src); + + loadConI_x43300000(ix43300000); + loadConI_x41f00000(ix41f00000); + regI_to_stkLHi(lx43300000, ix43300000); + regI_to_stkLHi(lx41f00000, ix41f00000); + stkL_to_regD(dx43300000, lx43300000); + stkL_to_regD(dx41f00000, lx41f00000); + + convI2D_regDHi_regD(tmp1, tmpsrc); + regDHi_regDLo_to_regD(tmp2, dx43300000, tmpsrc); + subD_regD_regD(tmp3, tmp2, dx43300000); + mulD_regD_regD(tmp4, tmp1, dx41f00000); + addD_regD_regD(dst, tmp3, tmp4); + %} +%} + +// Long to Double conversion using fast fxtof +instruct convL2D_helper(regD dst, regD tmp) %{ + effect(DEF dst, USE tmp); + size(4); + format %{ "FXTOD $tmp,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fxtod_opf); + ins_encode(form3_opf_rs2D_rdD(tmp, dst)); + ins_pipe(fcvtL2D); +%} + +instruct convL2D_reg_fast_fxtof(regD dst, stackSlotL src) %{ + predicate(VM_Version::has_fast_fxtof()); + match(Set dst (ConvL2D src)); + ins_cost(DEFAULT_COST + 3 * MEMORY_REF_COST); + expand %{ + regD tmp; + stkL_to_regD(tmp, src); + convL2D_helper(dst, tmp); + %} +%} + +//----------- +// Long to Float conversion using V8 opcodes. +// Still useful because cheetah traps and becomes +// amazingly slow for some common numbers. + +// Long to Float conversion using fast fxtof +instruct convL2F_helper(regF dst, regD tmp) %{ + effect(DEF dst, USE tmp); + size(4); + format %{ "FXTOS $tmp,$dst" %} + opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fxtos_opf); + ins_encode(form3_opf_rs2D_rdF(tmp, dst)); + ins_pipe(fcvtL2F); +%} + +instruct convL2F_reg_fast_fxtof(regF dst, stackSlotL src) %{ + match(Set dst (ConvL2F src)); + ins_cost(DEFAULT_COST + MEMORY_REF_COST); + expand %{ + regD tmp; + stkL_to_regD(tmp, src); + convL2F_helper(dst, tmp); + %} +%} +//----------- + +instruct convL2I_reg(iRegI dst, iRegL src) %{ + match(Set dst (ConvL2I src)); +#ifndef _LP64 + format %{ "MOV $src.lo,$dst\t! long->int" %} + ins_encode( form3_g0_rs2_rd_move_lo2( src, dst ) ); + ins_pipe(ialu_move_reg_I_to_L); +#else + size(4); + format %{ "SRA $src,R_G0,$dst\t! long->int" %} + ins_encode( form3_rs1_rd_signextend_lo1( src, dst ) ); + ins_pipe(ialu_reg); +#endif +%} + +// Register Shift Right Immediate +instruct shrL_reg_imm6_L2I(iRegI dst, iRegL src, immI_32_63 cnt) %{ + match(Set dst (ConvL2I (RShiftL src cnt))); + + size(4); + format %{ "SRAX $src,$cnt,$dst" %} + opcode(Assembler::srax_op3, Assembler::arith_op); + ins_encode( form3_sd_rs1_imm6_rd( src, cnt, dst ) ); + ins_pipe(ialu_reg_imm); +%} + +// Replicate scalar to packed byte values in Double register +instruct Repl8B_reg_helper(iRegL dst, iRegI src) %{ + effect(DEF dst, USE src); + format %{ "SLLX $src,56,$dst\n\t" + "SRLX $dst, 8,O7\n\t" + "OR $dst,O7,$dst\n\t" + "SRLX $dst,16,O7\n\t" + "OR $dst,O7,$dst\n\t" + "SRLX $dst,32,O7\n\t" + "OR $dst,O7,$dst\t! replicate8B" %} + ins_encode( enc_repl8b(src, dst)); + ins_pipe(ialu_reg); +%} + +// Replicate scalar to packed byte values in Double register +instruct Repl8B_reg(stackSlotD dst, iRegI src) %{ + match(Set dst (Replicate8B src)); + expand %{ + iRegL tmp; + Repl8B_reg_helper(tmp, src); + regL_to_stkD(dst, tmp); + %} +%} + +// Replicate scalar constant to packed byte values in Double register +instruct Repl8B_immI(regD dst, immI13 src, o7RegP tmp) %{ + match(Set dst (Replicate8B src)); +#ifdef _LP64 + size(36); +#else + size(8); +#endif + format %{ "SETHI hi(&Repl8($src)),$tmp\t!get Repl8B($src) from table\n\t" + "LDDF [$tmp+lo(&Repl8($src))],$dst" %} + ins_encode( LdReplImmI(src, dst, tmp, (8), (1)) ); + ins_pipe(loadConFD); +%} + +// Replicate scalar to packed char values into stack slot +instruct Repl4C_reg_helper(iRegL dst, iRegI src) %{ + effect(DEF dst, USE src); + format %{ "SLLX $src,48,$dst\n\t" + "SRLX $dst,16,O7\n\t" + "OR $dst,O7,$dst\n\t" + "SRLX $dst,32,O7\n\t" + "OR $dst,O7,$dst\t! replicate4C" %} + ins_encode( enc_repl4s(src, dst) ); + ins_pipe(ialu_reg); +%} + +// Replicate scalar to packed char values into stack slot +instruct Repl4C_reg(stackSlotD dst, iRegI src) %{ + match(Set dst (Replicate4C src)); + expand %{ + iRegL tmp; + Repl4C_reg_helper(tmp, src); + regL_to_stkD(dst, tmp); + %} +%} + +// Replicate scalar constant to packed char values in Double register +instruct Repl4C_immI(regD dst, immI src, o7RegP tmp) %{ + match(Set dst (Replicate4C src)); +#ifdef _LP64 + size(36); +#else + size(8); +#endif + format %{ "SETHI hi(&Repl4($src)),$tmp\t!get Repl4C($src) from table\n\t" + "LDDF [$tmp+lo(&Repl4($src))],$dst" %} + ins_encode( LdReplImmI(src, dst, tmp, (4), (2)) ); + ins_pipe(loadConFD); +%} + +// Replicate scalar to packed short values into stack slot +instruct Repl4S_reg_helper(iRegL dst, iRegI src) %{ + effect(DEF dst, USE src); + format %{ "SLLX $src,48,$dst\n\t" + "SRLX $dst,16,O7\n\t" + "OR $dst,O7,$dst\n\t" + "SRLX $dst,32,O7\n\t" + "OR $dst,O7,$dst\t! replicate4S" %} + ins_encode( enc_repl4s(src, dst) ); + ins_pipe(ialu_reg); +%} + +// Replicate scalar to packed short values into stack slot +instruct Repl4S_reg(stackSlotD dst, iRegI src) %{ + match(Set dst (Replicate4S src)); + expand %{ + iRegL tmp; + Repl4S_reg_helper(tmp, src); + regL_to_stkD(dst, tmp); + %} +%} + +// Replicate scalar constant to packed short values in Double register +instruct Repl4S_immI(regD dst, immI src, o7RegP tmp) %{ + match(Set dst (Replicate4S src)); +#ifdef _LP64 + size(36); +#else + size(8); +#endif + format %{ "SETHI hi(&Repl4($src)),$tmp\t!get Repl4S($src) from table\n\t" + "LDDF [$tmp+lo(&Repl4($src))],$dst" %} + ins_encode( LdReplImmI(src, dst, tmp, (4), (2)) ); + ins_pipe(loadConFD); +%} + +// Replicate scalar to packed int values in Double register +instruct Repl2I_reg_helper(iRegL dst, iRegI src) %{ + effect(DEF dst, USE src); + format %{ "SLLX $src,32,$dst\n\t" + "SRLX $dst,32,O7\n\t" + "OR $dst,O7,$dst\t! replicate2I" %} + ins_encode( enc_repl2i(src, dst)); + ins_pipe(ialu_reg); +%} + +// Replicate scalar to packed int values in Double register +instruct Repl2I_reg(stackSlotD dst, iRegI src) %{ + match(Set dst (Replicate2I src)); + expand %{ + iRegL tmp; + Repl2I_reg_helper(tmp, src); + regL_to_stkD(dst, tmp); + %} +%} + +// Replicate scalar zero constant to packed int values in Double register +instruct Repl2I_immI(regD dst, immI src, o7RegP tmp) %{ + match(Set dst (Replicate2I src)); +#ifdef _LP64 + size(36); +#else + size(8); +#endif + format %{ "SETHI hi(&Repl2($src)),$tmp\t!get Repl2I($src) from table\n\t" + "LDDF [$tmp+lo(&Repl2($src))],$dst" %} + ins_encode( LdReplImmI(src, dst, tmp, (2), (4)) ); + ins_pipe(loadConFD); +%} + +//----------Control Flow Instructions------------------------------------------ +// Compare Instructions +// Compare Integers +instruct compI_iReg(flagsReg icc, iRegI op1, iRegI op2) %{ + match(Set icc (CmpI op1 op2)); + effect( DEF icc, USE op1, USE op2 ); + + size(4); + format %{ "CMP $op1,$op2" %} + opcode(Assembler::subcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) ); + ins_pipe(ialu_cconly_reg_reg); +%} + +instruct compU_iReg(flagsRegU icc, iRegI op1, iRegI op2) %{ + match(Set icc (CmpU op1 op2)); + + size(4); + format %{ "CMP $op1,$op2\t! unsigned" %} + opcode(Assembler::subcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) ); + ins_pipe(ialu_cconly_reg_reg); +%} + +instruct compI_iReg_imm13(flagsReg icc, iRegI op1, immI13 op2) %{ + match(Set icc (CmpI op1 op2)); + effect( DEF icc, USE op1 ); + + size(4); + format %{ "CMP $op1,$op2" %} + opcode(Assembler::subcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( op1, op2, R_G0 ) ); + ins_pipe(ialu_cconly_reg_imm); +%} + +instruct testI_reg_reg( flagsReg icc, iRegI op1, iRegI op2, immI0 zero ) %{ + match(Set icc (CmpI (AndI op1 op2) zero)); + + size(4); + format %{ "BTST $op2,$op1" %} + opcode(Assembler::andcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) ); + ins_pipe(ialu_cconly_reg_reg_zero); +%} + +instruct testI_reg_imm( flagsReg icc, iRegI op1, immI13 op2, immI0 zero ) %{ + match(Set icc (CmpI (AndI op1 op2) zero)); + + size(4); + format %{ "BTST $op2,$op1" %} + opcode(Assembler::andcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( op1, op2, R_G0 ) ); + ins_pipe(ialu_cconly_reg_imm_zero); +%} + +instruct compL_reg_reg(flagsRegL xcc, iRegL op1, iRegL op2 ) %{ + match(Set xcc (CmpL op1 op2)); + effect( DEF xcc, USE op1, USE op2 ); + + size(4); + format %{ "CMP $op1,$op2\t\t! long" %} + opcode(Assembler::subcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) ); + ins_pipe(ialu_cconly_reg_reg); +%} + +instruct compL_reg_con(flagsRegL xcc, iRegL op1, immL13 con) %{ + match(Set xcc (CmpL op1 con)); + effect( DEF xcc, USE op1, USE con ); + + size(4); + format %{ "CMP $op1,$con\t\t! long" %} + opcode(Assembler::subcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( op1, con, R_G0 ) ); + ins_pipe(ialu_cconly_reg_reg); +%} + +instruct testL_reg_reg(flagsRegL xcc, iRegL op1, iRegL op2, immL0 zero) %{ + match(Set xcc (CmpL (AndL op1 op2) zero)); + effect( DEF xcc, USE op1, USE op2 ); + + size(4); + format %{ "BTST $op1,$op2\t\t! long" %} + opcode(Assembler::andcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) ); + ins_pipe(ialu_cconly_reg_reg); +%} + +// useful for checking the alignment of a pointer: +instruct testL_reg_con(flagsRegL xcc, iRegL op1, immL13 con, immL0 zero) %{ + match(Set xcc (CmpL (AndL op1 con) zero)); + effect( DEF xcc, USE op1, USE con ); + + size(4); + format %{ "BTST $op1,$con\t\t! long" %} + opcode(Assembler::andcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( op1, con, R_G0 ) ); + ins_pipe(ialu_cconly_reg_reg); +%} + +instruct compU_iReg_imm13(flagsRegU icc, iRegI op1, immU13 op2 ) %{ + match(Set icc (CmpU op1 op2)); + + size(4); + format %{ "CMP $op1,$op2\t! unsigned" %} + opcode(Assembler::subcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( op1, op2, R_G0 ) ); + ins_pipe(ialu_cconly_reg_imm); +%} + +// Compare Pointers +instruct compP_iRegP(flagsRegP pcc, iRegP op1, iRegP op2 ) %{ + match(Set pcc (CmpP op1 op2)); + + size(4); + format %{ "CMP $op1,$op2\t! ptr" %} + opcode(Assembler::subcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) ); + ins_pipe(ialu_cconly_reg_reg); +%} + +instruct compP_iRegP_imm13(flagsRegP pcc, iRegP op1, immP13 op2 ) %{ + match(Set pcc (CmpP op1 op2)); + + size(4); + format %{ "CMP $op1,$op2\t! ptr" %} + opcode(Assembler::subcc_op3, Assembler::arith_op); + ins_encode( form3_rs1_simm13_rd( op1, op2, R_G0 ) ); + ins_pipe(ialu_cconly_reg_imm); +%} + +//----------Max and Min-------------------------------------------------------- +// Min Instructions +// Conditional move for min +instruct cmovI_reg_lt( iRegI op2, iRegI op1, flagsReg icc ) %{ + effect( USE_DEF op2, USE op1, USE icc ); + + size(4); + format %{ "MOVlt icc,$op1,$op2\t! min" %} + opcode(Assembler::less); + ins_encode( enc_cmov_reg_minmax(op2,op1) ); + ins_pipe(ialu_reg_flags); +%} + +// Min Register with Register. +instruct minI_eReg(iRegI op1, iRegI op2) %{ + match(Set op2 (MinI op1 op2)); + ins_cost(DEFAULT_COST*2); + expand %{ + flagsReg icc; + compI_iReg(icc,op1,op2); + cmovI_reg_lt(op2,op1,icc); + %} +%} + +// Max Instructions +// Conditional move for max +instruct cmovI_reg_gt( iRegI op2, iRegI op1, flagsReg icc ) %{ + effect( USE_DEF op2, USE op1, USE icc ); + format %{ "MOVgt icc,$op1,$op2\t! max" %} + opcode(Assembler::greater); + ins_encode( enc_cmov_reg_minmax(op2,op1) ); + ins_pipe(ialu_reg_flags); +%} + +// Max Register with Register +instruct maxI_eReg(iRegI op1, iRegI op2) %{ + match(Set op2 (MaxI op1 op2)); + ins_cost(DEFAULT_COST*2); + expand %{ + flagsReg icc; + compI_iReg(icc,op1,op2); + cmovI_reg_gt(op2,op1,icc); + %} +%} + + +//----------Float Compares---------------------------------------------------- +// Compare floating, generate condition code +instruct cmpF_cc(flagsRegF fcc, regF src1, regF src2) %{ + match(Set fcc (CmpF src1 src2)); + + size(4); + format %{ "FCMPs $fcc,$src1,$src2" %} + opcode(Assembler::fpop2_op3, Assembler::arith_op, Assembler::fcmps_opf); + ins_encode( form3_opf_rs1F_rs2F_fcc( src1, src2, fcc ) ); + ins_pipe(faddF_fcc_reg_reg_zero); +%} + +instruct cmpD_cc(flagsRegF fcc, regD src1, regD src2) %{ + match(Set fcc (CmpD src1 src2)); + + size(4); + format %{ "FCMPd $fcc,$src1,$src2" %} + opcode(Assembler::fpop2_op3, Assembler::arith_op, Assembler::fcmpd_opf); + ins_encode( form3_opf_rs1D_rs2D_fcc( src1, src2, fcc ) ); + ins_pipe(faddD_fcc_reg_reg_zero); +%} + + +// Compare floating, generate -1,0,1 +instruct cmpF_reg(iRegI dst, regF src1, regF src2, flagsRegF0 fcc0) %{ + match(Set dst (CmpF3 src1 src2)); + effect(KILL fcc0); + ins_cost(DEFAULT_COST*3+BRANCH_COST*3); + format %{ "fcmpl $dst,$src1,$src2" %} + // Primary = float + opcode( true ); + ins_encode( floating_cmp( dst, src1, src2 ) ); + ins_pipe( floating_cmp ); +%} + +instruct cmpD_reg(iRegI dst, regD src1, regD src2, flagsRegF0 fcc0) %{ + match(Set dst (CmpD3 src1 src2)); + effect(KILL fcc0); + ins_cost(DEFAULT_COST*3+BRANCH_COST*3); + format %{ "dcmpl $dst,$src1,$src2" %} + // Primary = double (not float) + opcode( false ); + ins_encode( floating_cmp( dst, src1, src2 ) ); + ins_pipe( floating_cmp ); +%} + +//----------Branches--------------------------------------------------------- +// Jump +// (compare 'operand indIndex' and 'instruct addP_reg_reg' above) +instruct jumpXtnd(iRegX switch_val, o7RegI table) %{ + match(Jump switch_val); + + ins_cost(350); + + format %{ "SETHI [hi(table_base)],O7\n\t" + "ADD O7, lo(table_base), O7\n\t" + "LD [O7+$switch_val], O7\n\t" + "JUMP O7" + %} + ins_encode( jump_enc( switch_val, table) ); + ins_pc_relative(1); + ins_pipe(ialu_reg_reg); +%} + +// Direct Branch. Use V8 version with longer range. +instruct branch(label labl) %{ + match(Goto); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "BA $labl" %} + // Prim = bits 24-22, Secnd = bits 31-30, Tert = cond + opcode(Assembler::br_op2, Assembler::branch_op, Assembler::always); + ins_encode( enc_ba( labl ) ); + ins_pc_relative(1); + ins_pipe(br); +%} + +// Conditional Direct Branch +instruct branchCon(cmpOp cmp, flagsReg icc, label labl) %{ + match(If cmp icc); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "BP$cmp $icc,$labl" %} + // Prim = bits 24-22, Secnd = bits 31-30 + ins_encode( enc_bp( labl, cmp, icc ) ); + ins_pc_relative(1); + ins_pipe(br_cc); +%} + +// Branch-on-register tests all 64 bits. We assume that values +// in 64-bit registers always remains zero or sign extended +// unless our code munges the high bits. Interrupts can chop +// the high order bits to zero or sign at any time. +instruct branchCon_regI(cmpOp_reg cmp, iRegI op1, immI0 zero, label labl) %{ + match(If cmp (CmpI op1 zero)); + predicate(can_branch_register(_kids[0]->_leaf, _kids[1]->_leaf)); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "BR$cmp $op1,$labl" %} + ins_encode( enc_bpr( labl, cmp, op1 ) ); + ins_pc_relative(1); + ins_pipe(br_reg); +%} + +instruct branchCon_regP(cmpOp_reg cmp, iRegP op1, immP0 null, label labl) %{ + match(If cmp (CmpP op1 null)); + predicate(can_branch_register(_kids[0]->_leaf, _kids[1]->_leaf)); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "BR$cmp $op1,$labl" %} + ins_encode( enc_bpr( labl, cmp, op1 ) ); + ins_pc_relative(1); + ins_pipe(br_reg); +%} + +instruct branchCon_regL(cmpOp_reg cmp, iRegL op1, immL0 zero, label labl) %{ + match(If cmp (CmpL op1 zero)); + predicate(can_branch_register(_kids[0]->_leaf, _kids[1]->_leaf)); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "BR$cmp $op1,$labl" %} + ins_encode( enc_bpr( labl, cmp, op1 ) ); + ins_pc_relative(1); + ins_pipe(br_reg); +%} + +instruct branchConU(cmpOpU cmp, flagsRegU icc, label labl) %{ + match(If cmp icc); + effect(USE labl); + + format %{ "BP$cmp $icc,$labl" %} + // Prim = bits 24-22, Secnd = bits 31-30 + ins_encode( enc_bp( labl, cmp, icc ) ); + ins_pc_relative(1); + ins_pipe(br_cc); +%} + +instruct branchConP(cmpOpP cmp, flagsRegP pcc, label labl) %{ + match(If cmp pcc); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "BP$cmp $pcc,$labl" %} + // Prim = bits 24-22, Secnd = bits 31-30 + ins_encode( enc_bpx( labl, cmp, pcc ) ); + ins_pc_relative(1); + ins_pipe(br_cc); +%} + +instruct branchConF(cmpOpF cmp, flagsRegF fcc, label labl) %{ + match(If cmp fcc); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "FBP$cmp $fcc,$labl" %} + // Prim = bits 24-22, Secnd = bits 31-30 + ins_encode( enc_fbp( labl, cmp, fcc ) ); + ins_pc_relative(1); + ins_pipe(br_fcc); +%} + +instruct branchLoopEnd(cmpOp cmp, flagsReg icc, label labl) %{ + match(CountedLoopEnd cmp icc); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "BP$cmp $icc,$labl\t! Loop end" %} + // Prim = bits 24-22, Secnd = bits 31-30 + ins_encode( enc_bp( labl, cmp, icc ) ); + ins_pc_relative(1); + ins_pipe(br_cc); +%} + +instruct branchLoopEndU(cmpOpU cmp, flagsRegU icc, label labl) %{ + match(CountedLoopEnd cmp icc); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "BP$cmp $icc,$labl\t! Loop end" %} + // Prim = bits 24-22, Secnd = bits 31-30 + ins_encode( enc_bp( labl, cmp, icc ) ); + ins_pc_relative(1); + ins_pipe(br_cc); +%} + +// ============================================================================ +// Long Compare +// +// Currently we hold longs in 2 registers. Comparing such values efficiently +// is tricky. The flavor of compare used depends on whether we are testing +// for LT, LE, or EQ. For a simple LT test we can check just the sign bit. +// The GE test is the negated LT test. The LE test can be had by commuting +// the operands (yielding a GE test) and then negating; negate again for the +// GT test. The EQ test is done by ORcc'ing the high and low halves, and the +// NE test is negated from that. + +// Due to a shortcoming in the ADLC, it mixes up expressions like: +// (foo (CmpI (CmpL X Y) 0)) and (bar (CmpI (CmpL X 0L) 0)). Note the +// difference between 'Y' and '0L'. The tree-matches for the CmpI sections +// are collapsed internally in the ADLC's dfa-gen code. The match for +// (CmpI (CmpL X Y) 0) is silently replaced with (CmpI (CmpL X 0L) 0) and the +// foo match ends up with the wrong leaf. One fix is to not match both +// reg-reg and reg-zero forms of long-compare. This is unfortunate because +// both forms beat the trinary form of long-compare and both are very useful +// on Intel which has so few registers. + +instruct branchCon_long(cmpOp cmp, flagsRegL xcc, label labl) %{ + match(If cmp xcc); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "BP$cmp $xcc,$labl" %} + // Prim = bits 24-22, Secnd = bits 31-30 + ins_encode( enc_bpl( labl, cmp, xcc ) ); + ins_pc_relative(1); + ins_pipe(br_cc); +%} + +// Manifest a CmpL3 result in an integer register. Very painful. +// This is the test to avoid. +instruct cmpL3_reg_reg(iRegI dst, iRegL src1, iRegL src2, flagsReg ccr ) %{ + match(Set dst (CmpL3 src1 src2) ); + effect( KILL ccr ); + ins_cost(6*DEFAULT_COST); + size(24); + format %{ "CMP $src1,$src2\t\t! long\n" + "\tBLT,a,pn done\n" + "\tMOV -1,$dst\t! delay slot\n" + "\tBGT,a,pn done\n" + "\tMOV 1,$dst\t! delay slot\n" + "\tCLR $dst\n" + "done:" %} + ins_encode( cmpl_flag(src1,src2,dst) ); + ins_pipe(cmpL_reg); +%} + +// Conditional move +instruct cmovLL_reg(cmpOp cmp, flagsRegL xcc, iRegL dst, iRegL src) %{ + match(Set dst (CMoveL (Binary cmp xcc) (Binary dst src))); + ins_cost(150); + format %{ "MOV$cmp $xcc,$src,$dst\t! long" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::xcc)) ); + ins_pipe(ialu_reg); +%} + +instruct cmovLL_imm(cmpOp cmp, flagsRegL xcc, iRegL dst, immL0 src) %{ + match(Set dst (CMoveL (Binary cmp xcc) (Binary dst src))); + ins_cost(140); + format %{ "MOV$cmp $xcc,$src,$dst\t! long" %} + ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::xcc)) ); + ins_pipe(ialu_imm); +%} + +instruct cmovIL_reg(cmpOp cmp, flagsRegL xcc, iRegI dst, iRegI src) %{ + match(Set dst (CMoveI (Binary cmp xcc) (Binary dst src))); + ins_cost(150); + format %{ "MOV$cmp $xcc,$src,$dst" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::xcc)) ); + ins_pipe(ialu_reg); +%} + +instruct cmovIL_imm(cmpOp cmp, flagsRegL xcc, iRegI dst, immI11 src) %{ + match(Set dst (CMoveI (Binary cmp xcc) (Binary dst src))); + ins_cost(140); + format %{ "MOV$cmp $xcc,$src,$dst" %} + ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::xcc)) ); + ins_pipe(ialu_imm); +%} + +instruct cmovPL_reg(cmpOp cmp, flagsRegL xcc, iRegP dst, iRegP src) %{ + match(Set dst (CMoveP (Binary cmp xcc) (Binary dst src))); + ins_cost(150); + format %{ "MOV$cmp $xcc,$src,$dst" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::xcc)) ); + ins_pipe(ialu_reg); +%} + +instruct cmovPL_imm(cmpOp cmp, flagsRegL xcc, iRegP dst, immP0 src) %{ + match(Set dst (CMoveP (Binary cmp xcc) (Binary dst src))); + ins_cost(140); + format %{ "MOV$cmp $xcc,$src,$dst" %} + ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::xcc)) ); + ins_pipe(ialu_imm); +%} + +instruct cmovFL_reg(cmpOp cmp, flagsRegL xcc, regF dst, regF src) %{ + match(Set dst (CMoveF (Binary cmp xcc) (Binary dst src))); + ins_cost(150); + opcode(0x101); + format %{ "FMOVS$cmp $xcc,$src,$dst" %} + ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::xcc)) ); + ins_pipe(int_conditional_float_move); +%} + +instruct cmovDL_reg(cmpOp cmp, flagsRegL xcc, regD dst, regD src) %{ + match(Set dst (CMoveD (Binary cmp xcc) (Binary dst src))); + ins_cost(150); + opcode(0x102); + format %{ "FMOVD$cmp $xcc,$src,$dst" %} + ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::xcc)) ); + ins_pipe(int_conditional_float_move); +%} + +// ============================================================================ +// Safepoint Instruction +instruct safePoint_poll(iRegP poll) %{ + match(SafePoint poll); + effect(USE poll); + + size(4); +#ifdef _LP64 + format %{ "LDX [$poll],R_G0\t! Safepoint: poll for GC" %} +#else + format %{ "LDUW [$poll],R_G0\t! Safepoint: poll for GC" %} +#endif + ins_encode %{ + __ relocate(relocInfo::poll_type); + __ ld_ptr($poll$$Register, 0, G0); + %} + ins_pipe(loadPollP); +%} + +// ============================================================================ +// Call Instructions +// Call Java Static Instruction +instruct CallStaticJavaDirect( method meth ) %{ + match(CallStaticJava); + effect(USE meth); + + size(8); + ins_cost(CALL_COST); + format %{ "CALL,static ; NOP ==> " %} + ins_encode( Java_Static_Call( meth ), call_epilog ); + ins_pc_relative(1); + ins_pipe(simple_call); +%} + +// Call Java Dynamic Instruction +instruct CallDynamicJavaDirect( method meth ) %{ + match(CallDynamicJava); + effect(USE meth); + + ins_cost(CALL_COST); + format %{ "SET (empty),R_G5\n\t" + "CALL,dynamic ; NOP ==> " %} + ins_encode( Java_Dynamic_Call( meth ), call_epilog ); + ins_pc_relative(1); + ins_pipe(call); +%} + +// Call Runtime Instruction +instruct CallRuntimeDirect(method meth, l7RegP l7) %{ + match(CallRuntime); + effect(USE meth, KILL l7); + ins_cost(CALL_COST); + format %{ "CALL,runtime" %} + ins_encode( Java_To_Runtime( meth ), + call_epilog, adjust_long_from_native_call ); + ins_pc_relative(1); + ins_pipe(simple_call); +%} + +// Call runtime without safepoint - same as CallRuntime +instruct CallLeafDirect(method meth, l7RegP l7) %{ + match(CallLeaf); + effect(USE meth, KILL l7); + ins_cost(CALL_COST); + format %{ "CALL,runtime leaf" %} + ins_encode( Java_To_Runtime( meth ), + call_epilog, + adjust_long_from_native_call ); + ins_pc_relative(1); + ins_pipe(simple_call); +%} + +// Call runtime without safepoint - same as CallLeaf +instruct CallLeafNoFPDirect(method meth, l7RegP l7) %{ + match(CallLeafNoFP); + effect(USE meth, KILL l7); + ins_cost(CALL_COST); + format %{ "CALL,runtime leaf nofp" %} + ins_encode( Java_To_Runtime( meth ), + call_epilog, + adjust_long_from_native_call ); + ins_pc_relative(1); + ins_pipe(simple_call); +%} + +// Tail Call; Jump from runtime stub to Java code. +// Also known as an 'interprocedural jump'. +// Target of jump will eventually return to caller. +// TailJump below removes the return address. +instruct TailCalljmpInd(g3RegP jump_target, inline_cache_regP method_oop) %{ + match(TailCall jump_target method_oop ); + + ins_cost(CALL_COST); + format %{ "Jmp $jump_target ; NOP \t! $method_oop holds method oop" %} + ins_encode(form_jmpl(jump_target)); + ins_pipe(tail_call); +%} + + +// Return Instruction +instruct Ret() %{ + match(Return); + + // The epilogue node did the ret already. + size(0); + format %{ "! return" %} + ins_encode(); + ins_pipe(empty); +%} + + +// Tail Jump; remove the return address; jump to target. +// TailCall above leaves the return address around. +// TailJump is used in only one place, the rethrow_Java stub (fancy_jump=2). +// ex_oop (Exception Oop) is needed in %o0 at the jump. As there would be a +// "restore" before this instruction (in Epilogue), we need to materialize it +// in %i0. +instruct tailjmpInd(g1RegP jump_target, i0RegP ex_oop) %{ + match( TailJump jump_target ex_oop ); + ins_cost(CALL_COST); + format %{ "! discard R_O7\n\t" + "Jmp $jump_target ; ADD O7,8,O1 \t! $ex_oop holds exc. oop" %} + ins_encode(form_jmpl_set_exception_pc(jump_target)); + // opcode(Assembler::jmpl_op3, Assembler::arith_op); + // The hack duplicates the exception oop into G3, so that CreateEx can use it there. + // ins_encode( form3_rs1_simm13_rd( jump_target, 0x00, R_G0 ), move_return_pc_to_o1() ); + ins_pipe(tail_call); +%} + +// Create exception oop: created by stack-crawling runtime code. +// Created exception is now available to this handler, and is setup +// just prior to jumping to this handler. No code emitted. +instruct CreateException( o0RegP ex_oop ) +%{ + match(Set ex_oop (CreateEx)); + ins_cost(0); + + size(0); + // use the following format syntax + format %{ "! exception oop is in R_O0; no code emitted" %} + ins_encode(); + ins_pipe(empty); +%} + + +// Rethrow exception: +// The exception oop will come in the first argument position. +// Then JUMP (not call) to the rethrow stub code. +instruct RethrowException() +%{ + match(Rethrow); + ins_cost(CALL_COST); + + // use the following format syntax + format %{ "Jmp rethrow_stub" %} + ins_encode(enc_rethrow); + ins_pipe(tail_call); +%} + + +// Die now +instruct ShouldNotReachHere( ) +%{ + match(Halt); + ins_cost(CALL_COST); + + size(4); + // Use the following format syntax + format %{ "ILLTRAP ; ShouldNotReachHere" %} + ins_encode( form2_illtrap() ); + ins_pipe(tail_call); +%} + +// ============================================================================ +// The 2nd slow-half of a subtype check. Scan the subklass's 2ndary superklass +// array for an instance of the superklass. Set a hidden internal cache on a +// hit (cache is checked with exposed code in gen_subtype_check()). Return +// not zero for a miss or zero for a hit. The encoding ALSO sets flags. +instruct partialSubtypeCheck( o0RegP index, o1RegP sub, o2RegP super, flagsRegP pcc, o7RegP o7 ) %{ + match(Set index (PartialSubtypeCheck sub super)); + effect( KILL pcc, KILL o7 ); + ins_cost(DEFAULT_COST*10); + format %{ "CALL PartialSubtypeCheck\n\tNOP" %} + ins_encode( enc_PartialSubtypeCheck() ); + ins_pipe(partial_subtype_check_pipe); +%} + +instruct partialSubtypeCheck_vs_zero( flagsRegP pcc, o1RegP sub, o2RegP super, immP0 zero, o0RegP idx, o7RegP o7 ) %{ + match(Set pcc (CmpP (PartialSubtypeCheck sub super) zero)); + effect( KILL idx, KILL o7 ); + ins_cost(DEFAULT_COST*10); + format %{ "CALL PartialSubtypeCheck\n\tNOP\t# (sets condition codes)" %} + ins_encode( enc_PartialSubtypeCheck() ); + ins_pipe(partial_subtype_check_pipe); +%} + +// ============================================================================ +// inlined locking and unlocking + +instruct cmpFastLock(flagsRegP pcc, iRegP object, iRegP box, iRegP scratch2, o7RegP scratch ) %{ + match(Set pcc (FastLock object box)); + + effect(KILL scratch, TEMP scratch2); + ins_cost(100); + + size(4*112); // conservative overestimation ... + format %{ "FASTLOCK $object, $box; KILL $scratch, $scratch2, $box" %} + ins_encode( Fast_Lock(object, box, scratch, scratch2) ); + ins_pipe(long_memory_op); +%} + + +instruct cmpFastUnlock(flagsRegP pcc, iRegP object, iRegP box, iRegP scratch2, o7RegP scratch ) %{ + match(Set pcc (FastUnlock object box)); + effect(KILL scratch, TEMP scratch2); + ins_cost(100); + + size(4*120); // conservative overestimation ... + format %{ "FASTUNLOCK $object, $box; KILL $scratch, $scratch2, $box" %} + ins_encode( Fast_Unlock(object, box, scratch, scratch2) ); + ins_pipe(long_memory_op); +%} + +// Count and Base registers are fixed because the allocator cannot +// kill unknown registers. The encodings are generic. +instruct clear_array(iRegX cnt, iRegP base, iRegX temp, Universe dummy, flagsReg ccr) %{ + match(Set dummy (ClearArray cnt base)); + effect(TEMP temp, KILL ccr); + ins_cost(300); + format %{ "MOV $cnt,$temp\n" + "loop: SUBcc $temp,8,$temp\t! Count down a dword of bytes\n" + " BRge loop\t\t! Clearing loop\n" + " STX G0,[$base+$temp]\t! delay slot" %} + ins_encode( enc_Clear_Array(cnt, base, temp) ); + ins_pipe(long_memory_op); +%} + +instruct string_compare(o0RegP str1, o1RegP str2, g3RegP tmp1, g4RegP tmp2, notemp_iRegI result, flagsReg ccr) %{ + match(Set result (StrComp str1 str2)); + effect(USE_KILL str1, USE_KILL str2, KILL tmp1, KILL tmp2, KILL ccr); + ins_cost(300); + format %{ "String Compare $str1,$str2 -> $result" %} + ins_encode( enc_String_Compare(str1, str2, tmp1, tmp2, result) ); + ins_pipe(long_memory_op); +%} + +// ============================================================================ +//------------Bytes reverse-------------------------------------------------- + +instruct bytes_reverse_int(iRegI dst, stackSlotI src) %{ + match(Set dst (ReverseBytesI src)); + effect(DEF dst, USE src); + + // Op cost is artificially doubled to make sure that load or store + // instructions are preferred over this one which requires a spill + // onto a stack slot. + ins_cost(2*DEFAULT_COST + MEMORY_REF_COST); + size(8); + format %{ "LDUWA $src, $dst\t!asi=primary_little" %} + opcode(Assembler::lduwa_op3); + ins_encode( form3_mem_reg_little(src, dst) ); + ins_pipe( iload_mem ); +%} + +instruct bytes_reverse_long(iRegL dst, stackSlotL src) %{ + match(Set dst (ReverseBytesL src)); + effect(DEF dst, USE src); + + // Op cost is artificially doubled to make sure that load or store + // instructions are preferred over this one which requires a spill + // onto a stack slot. + ins_cost(2*DEFAULT_COST + MEMORY_REF_COST); + size(8); + format %{ "LDXA $src, $dst\t!asi=primary_little" %} + + opcode(Assembler::ldxa_op3); + ins_encode( form3_mem_reg_little(src, dst) ); + ins_pipe( iload_mem ); +%} + +// Load Integer reversed byte order +instruct loadI_reversed(iRegI dst, memory src) %{ + match(Set dst (ReverseBytesI (LoadI src))); + + ins_cost(DEFAULT_COST + MEMORY_REF_COST); + size(8); + format %{ "LDUWA $src, $dst\t!asi=primary_little" %} + + opcode(Assembler::lduwa_op3); + ins_encode( form3_mem_reg_little( src, dst) ); + ins_pipe(iload_mem); +%} + +// Load Long - aligned and reversed +instruct loadL_reversed(iRegL dst, memory src) %{ + match(Set dst (ReverseBytesL (LoadL src))); + + ins_cost(DEFAULT_COST + MEMORY_REF_COST); + size(8); + format %{ "LDXA $src, $dst\t!asi=primary_little" %} + + opcode(Assembler::ldxa_op3); + ins_encode( form3_mem_reg_little( src, dst ) ); + ins_pipe(iload_mem); +%} + +// Store Integer reversed byte order +instruct storeI_reversed(memory dst, iRegI src) %{ + match(Set dst (StoreI dst (ReverseBytesI src))); + + ins_cost(MEMORY_REF_COST); + size(8); + format %{ "STWA $src, $dst\t!asi=primary_little" %} + + opcode(Assembler::stwa_op3); + ins_encode( form3_mem_reg_little( dst, src) ); + ins_pipe(istore_mem_reg); +%} + +// Store Long reversed byte order +instruct storeL_reversed(memory dst, iRegL src) %{ + match(Set dst (StoreL dst (ReverseBytesL src))); + + ins_cost(MEMORY_REF_COST); + size(8); + format %{ "STXA $src, $dst\t!asi=primary_little" %} + + opcode(Assembler::stxa_op3); + ins_encode( form3_mem_reg_little( dst, src) ); + ins_pipe(istore_mem_reg); +%} + +//----------PEEPHOLE RULES----------------------------------------------------- +// These must follow all instruction definitions as they use the names +// defined in the instructions definitions. +// +// peepmatch ( root_instr_name [preceeding_instruction]* ); +// +// peepconstraint %{ +// (instruction_number.operand_name relational_op instruction_number.operand_name +// [, ...] ); +// // instruction numbers are zero-based using left to right order in peepmatch +// +// peepreplace ( instr_name ( [instruction_number.operand_name]* ) ); +// // provide an instruction_number.operand_name for each operand that appears +// // in the replacement instruction's match rule +// +// ---------VM FLAGS--------------------------------------------------------- +// +// All peephole optimizations can be turned off using -XX:-OptoPeephole +// +// Each peephole rule is given an identifying number starting with zero and +// increasing by one in the order seen by the parser. An individual peephole +// can be enabled, and all others disabled, by using -XX:OptoPeepholeAt=# +// on the command-line. +// +// ---------CURRENT LIMITATIONS---------------------------------------------- +// +// Only match adjacent instructions in same basic block +// Only equality constraints +// Only constraints between operands, not (0.dest_reg == EAX_enc) +// Only one replacement instruction +// +// ---------EXAMPLE---------------------------------------------------------- +// +// // pertinent parts of existing instructions in architecture description +// instruct movI(eRegI dst, eRegI src) %{ +// match(Set dst (CopyI src)); +// %} +// +// instruct incI_eReg(eRegI dst, immI1 src, eFlagsReg cr) %{ +// match(Set dst (AddI dst src)); +// effect(KILL cr); +// %} +// +// // Change (inc mov) to lea +// peephole %{ +// // increment preceeded by register-register move +// peepmatch ( incI_eReg movI ); +// // require that the destination register of the increment +// // match the destination register of the move +// peepconstraint ( 0.dst == 1.dst ); +// // construct a replacement instruction that sets +// // the destination to ( move's source register + one ) +// peepreplace ( incI_eReg_immI1( 0.dst 1.src 0.src ) ); +// %} +// + +// // Change load of spilled value to only a spill +// instruct storeI(memory mem, eRegI src) %{ +// match(Set mem (StoreI mem src)); +// %} +// +// instruct loadI(eRegI dst, memory mem) %{ +// match(Set dst (LoadI mem)); +// %} +// +// peephole %{ +// peepmatch ( loadI storeI ); +// peepconstraint ( 1.src == 0.dst, 1.mem == 0.mem ); +// peepreplace ( storeI( 1.mem 1.mem 1.src ) ); +// %} + +//----------SMARTSPILL RULES--------------------------------------------------- +// These must follow all instruction definitions as they use the names +// defined in the instructions definitions. +// +// SPARC will probably not have any of these rules due to RISC instruction set. + +//----------PIPELINE----------------------------------------------------------- +// Rules which define the behavior of the target architectures pipeline. diff --git a/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp b/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp new file mode 100644 index 00000000000..03c74085734 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp @@ -0,0 +1,3002 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubGenerator_sparc.cpp.incl" + +// Declaration and definition of StubGenerator (no .hpp file). +// For a more detailed description of the stub routine structure +// see the comment in stubRoutines.hpp. + +#define __ _masm-> + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) /* nothing */ +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") + +// Note: The register L7 is used as L7_thread_cache, and may not be used +// any other way within this module. + + +static const Register& Lstub_temp = L2; + +// ------------------------------------------------------------------------------------------------------------------------- +// Stub Code definitions + +static address handle_unsafe_access() { + JavaThread* thread = JavaThread::current(); + address pc = thread->saved_exception_pc(); + address npc = thread->saved_exception_npc(); + // pc is the instruction which we must emulate + // doing a no-op is fine: return garbage from the load + + // request an async exception + thread->set_pending_unsafe_access_error(); + + // return address of next instruction to execute + return npc; +} + +class StubGenerator: public StubCodeGenerator { + private: + +#ifdef PRODUCT +#define inc_counter_np(a,b,c) (0) +#else + void inc_counter_np_(int& counter, Register t1, Register t2) { + Address counter_addr(t2, (address) &counter); + __ sethi(counter_addr); + __ ld(counter_addr, t1); + __ inc(t1); + __ st(t1, counter_addr); + } +#define inc_counter_np(counter, t1, t2) \ + BLOCK_COMMENT("inc_counter " #counter); \ + inc_counter_np_(counter, t1, t2); +#endif + + //---------------------------------------------------------------------------------------------------- + // Call stubs are used to call Java from C + + address generate_call_stub(address& return_pc) { + StubCodeMark mark(this, "StubRoutines", "call_stub"); + address start = __ pc(); + + // Incoming arguments: + // + // o0 : call wrapper address + // o1 : result (address) + // o2 : result type + // o3 : method + // o4 : (interpreter) entry point + // o5 : parameters (address) + // [sp + 0x5c]: parameter size (in words) + // [sp + 0x60]: thread + // + // +---------------+ <--- sp + 0 + // | | + // . reg save area . + // | | + // +---------------+ <--- sp + 0x40 + // | | + // . extra 7 slots . + // | | + // +---------------+ <--- sp + 0x5c + // | param. size | + // +---------------+ <--- sp + 0x60 + // | thread | + // +---------------+ + // | | + + // note: if the link argument position changes, adjust + // the code in frame::entry_frame_call_wrapper() + + const Argument link = Argument(0, false); // used only for GC + const Argument result = Argument(1, false); + const Argument result_type = Argument(2, false); + const Argument method = Argument(3, false); + const Argument entry_point = Argument(4, false); + const Argument parameters = Argument(5, false); + const Argument parameter_size = Argument(6, false); + const Argument thread = Argument(7, false); + + // setup thread register + __ ld_ptr(thread.as_address(), G2_thread); + +#ifdef ASSERT + // make sure we have no pending exceptions + { const Register t = G3_scratch; + Label L; + __ ld_ptr(G2_thread, in_bytes(Thread::pending_exception_offset()), t); + __ br_null(t, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("StubRoutines::call_stub: entered with pending exception"); + __ bind(L); + } +#endif + + // create activation frame & allocate space for parameters + { const Register t = G3_scratch; + __ ld_ptr(parameter_size.as_address(), t); // get parameter size (in words) + __ add(t, frame::memory_parameter_word_sp_offset, t); // add space for save area (in words) + __ round_to(t, WordsPerLong); // make sure it is multiple of 2 (in words) + __ sll(t, Interpreter::logStackElementSize(), t); // compute number of bytes + __ neg(t); // negate so it can be used with save + __ save(SP, t, SP); // setup new frame + } + + // +---------------+ <--- sp + 0 + // | | + // . reg save area . + // | | + // +---------------+ <--- sp + 0x40 + // | | + // . extra 7 slots . + // | | + // +---------------+ <--- sp + 0x5c + // | empty slot | (only if parameter size is even) + // +---------------+ + // | | + // . parameters . + // | | + // +---------------+ <--- fp + 0 + // | | + // . reg save area . + // | | + // +---------------+ <--- fp + 0x40 + // | | + // . extra 7 slots . + // | | + // +---------------+ <--- fp + 0x5c + // | param. size | + // +---------------+ <--- fp + 0x60 + // | thread | + // +---------------+ + // | | + + // pass parameters if any + BLOCK_COMMENT("pass parameters if any"); + { const Register src = parameters.as_in().as_register(); + const Register dst = Lentry_args; + const Register tmp = G3_scratch; + const Register cnt = G4_scratch; + + // test if any parameters & setup of Lentry_args + Label exit; + __ ld_ptr(parameter_size.as_in().as_address(), cnt); // parameter counter + __ add( FP, STACK_BIAS, dst ); + __ tst(cnt); + __ br(Assembler::zero, false, Assembler::pn, exit); + __ delayed()->sub(dst, BytesPerWord, dst); // setup Lentry_args + + // copy parameters if any + Label loop; + __ BIND(loop); + // Store tag first. + if (TaggedStackInterpreter) { + __ ld_ptr(src, 0, tmp); + __ add(src, BytesPerWord, src); // get next + __ st_ptr(tmp, dst, Interpreter::tag_offset_in_bytes()); + } + // Store parameter value + __ ld_ptr(src, 0, tmp); + __ add(src, BytesPerWord, src); + __ st_ptr(tmp, dst, Interpreter::value_offset_in_bytes()); + __ deccc(cnt); + __ br(Assembler::greater, false, Assembler::pt, loop); + __ delayed()->sub(dst, Interpreter::stackElementSize(), dst); + + // done + __ BIND(exit); + } + + // setup parameters, method & call Java function +#ifdef ASSERT + // layout_activation_impl checks it's notion of saved SP against + // this register, so if this changes update it as well. + const Register saved_SP = Lscratch; + __ mov(SP, saved_SP); // keep track of SP before call +#endif + + // setup parameters + const Register t = G3_scratch; + __ ld_ptr(parameter_size.as_in().as_address(), t); // get parameter size (in words) + __ sll(t, Interpreter::logStackElementSize(), t); // compute number of bytes + __ sub(FP, t, Gargs); // setup parameter pointer +#ifdef _LP64 + __ add( Gargs, STACK_BIAS, Gargs ); // Account for LP64 stack bias +#endif + __ mov(SP, O5_savedSP); + + + // do the call + // + // the following register must be setup: + // + // G2_thread + // G5_method + // Gargs + BLOCK_COMMENT("call Java function"); + __ jmpl(entry_point.as_in().as_register(), G0, O7); + __ delayed()->mov(method.as_in().as_register(), G5_method); // setup method + + BLOCK_COMMENT("call_stub_return_address:"); + return_pc = __ pc(); + + // The callee, if it wasn't interpreted, can return with SP changed so + // we can no longer assert of change of SP. + + // store result depending on type + // (everything that is not T_OBJECT, T_LONG, T_FLOAT, or T_DOUBLE + // is treated as T_INT) + { const Register addr = result .as_in().as_register(); + const Register type = result_type.as_in().as_register(); + Label is_long, is_float, is_double, is_object, exit; + __ cmp(type, T_OBJECT); __ br(Assembler::equal, false, Assembler::pn, is_object); + __ delayed()->cmp(type, T_FLOAT); __ br(Assembler::equal, false, Assembler::pn, is_float); + __ delayed()->cmp(type, T_DOUBLE); __ br(Assembler::equal, false, Assembler::pn, is_double); + __ delayed()->cmp(type, T_LONG); __ br(Assembler::equal, false, Assembler::pn, is_long); + __ delayed()->nop(); + + // store int result + __ st(O0, addr, G0); + + __ BIND(exit); + __ ret(); + __ delayed()->restore(); + + __ BIND(is_object); + __ ba(false, exit); + __ delayed()->st_ptr(O0, addr, G0); + + __ BIND(is_float); + __ ba(false, exit); + __ delayed()->stf(FloatRegisterImpl::S, F0, addr, G0); + + __ BIND(is_double); + __ ba(false, exit); + __ delayed()->stf(FloatRegisterImpl::D, F0, addr, G0); + + __ BIND(is_long); +#ifdef _LP64 + __ ba(false, exit); + __ delayed()->st_long(O0, addr, G0); // store entire long +#else +#if defined(COMPILER2) + // All return values are where we want them, except for Longs. C2 returns + // longs in G1 in the 32-bit build whereas the interpreter wants them in O0/O1. + // Since the interpreter will return longs in G1 and O0/O1 in the 32bit + // build we simply always use G1. + // Note: I tried to make c2 return longs in O0/O1 and G1 so we wouldn't have to + // do this here. Unfortunately if we did a rethrow we'd see an machepilog node + // first which would move g1 -> O0/O1 and destroy the exception we were throwing. + + __ ba(false, exit); + __ delayed()->stx(G1, addr, G0); // store entire long +#else + __ st(O1, addr, BytesPerInt); + __ ba(false, exit); + __ delayed()->st(O0, addr, G0); +#endif /* COMPILER2 */ +#endif /* _LP64 */ + } + return start; + } + + + //---------------------------------------------------------------------------------------------------- + // Return point for a Java call if there's an exception thrown in Java code. + // The exception is caught and transformed into a pending exception stored in + // JavaThread that can be tested from within the VM. + // + // Oexception: exception oop + + address generate_catch_exception() { + StubCodeMark mark(this, "StubRoutines", "catch_exception"); + + address start = __ pc(); + // verify that thread corresponds + __ verify_thread(); + + const Register& temp_reg = Gtemp; + Address pending_exception_addr (G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + Address exception_file_offset_addr(G2_thread, 0, in_bytes(Thread::exception_file_offset ())); + Address exception_line_offset_addr(G2_thread, 0, in_bytes(Thread::exception_line_offset ())); + + // set pending exception + __ verify_oop(Oexception); + __ st_ptr(Oexception, pending_exception_addr); + __ set((intptr_t)__FILE__, temp_reg); + __ st_ptr(temp_reg, exception_file_offset_addr); + __ set((intptr_t)__LINE__, temp_reg); + __ st(temp_reg, exception_line_offset_addr); + + // complete return to VM + assert(StubRoutines::_call_stub_return_address != NULL, "must have been generated before"); + + Address stub_ret(temp_reg, StubRoutines::_call_stub_return_address); + __ jump_to(stub_ret); + __ delayed()->nop(); + + return start; + } + + + //---------------------------------------------------------------------------------------------------- + // Continuation point for runtime calls returning with a pending exception + // The pending exception check happened in the runtime or native call stub + // The pending exception in Thread is converted into a Java-level exception + // + // Contract with Java-level exception handler: O0 = exception + // O1 = throwing pc + + address generate_forward_exception() { + StubCodeMark mark(this, "StubRoutines", "forward_exception"); + address start = __ pc(); + + // Upon entry, O7 has the return address returning into Java + // (interpreted or compiled) code; i.e. the return address + // becomes the throwing pc. + + const Register& handler_reg = Gtemp; + + Address exception_addr (G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + +#ifdef ASSERT + // make sure that this code is only executed if there is a pending exception + { Label L; + __ ld_ptr(exception_addr, Gtemp); + __ br_notnull(Gtemp, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("StubRoutines::forward exception: no pending exception (1)"); + __ bind(L); + } +#endif + + // compute exception handler into handler_reg + __ get_thread(); + __ ld_ptr(exception_addr, Oexception); + __ verify_oop(Oexception); + __ save_frame(0); // compensates for compiler weakness + __ add(O7->after_save(), frame::pc_return_offset, Lscratch); // save the issuing PC + BLOCK_COMMENT("call exception_handler_for_return_address"); + __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), Lscratch); + __ mov(O0, handler_reg); + __ restore(); // compensates for compiler weakness + + __ ld_ptr(exception_addr, Oexception); + __ add(O7, frame::pc_return_offset, Oissuing_pc); // save the issuing PC + +#ifdef ASSERT + // make sure exception is set + { Label L; + __ br_notnull(Oexception, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("StubRoutines::forward exception: no pending exception (2)"); + __ bind(L); + } +#endif + // jump to exception handler + __ jmp(handler_reg, 0); + // clear pending exception + __ delayed()->st_ptr(G0, exception_addr); + + return start; + } + + + //------------------------------------------------------------------------------------------------------------------------ + // Continuation point for throwing of implicit exceptions that are not handled in + // the current activation. Fabricates an exception oop and initiates normal + // exception dispatching in this frame. Only callee-saved registers are preserved + // (through the normal register window / RegisterMap handling). + // If the compiler needs all registers to be preserved between the fault + // point and the exception handler then it must assume responsibility for that in + // AbstractCompiler::continuation_for_implicit_null_exception or + // continuation_for_implicit_division_by_zero_exception. All other implicit + // exceptions (e.g., NullPointerException or AbstractMethodError on entry) are + // either at call sites or otherwise assume that stack unwinding will be initiated, + // so caller saved registers were assumed volatile in the compiler. + + // Note that we generate only this stub into a RuntimeStub, because it needs to be + // properly traversed and ignored during GC, so we change the meaning of the "__" + // macro within this method. +#undef __ +#define __ masm-> + + address generate_throw_exception(const char* name, address runtime_entry, bool restore_saved_exception_pc) { +#ifdef ASSERT + int insts_size = VerifyThread ? 1 * K : 600; +#else + int insts_size = VerifyThread ? 1 * K : 256; +#endif /* ASSERT */ + int locs_size = 32; + + CodeBuffer code(name, insts_size, locs_size); + MacroAssembler* masm = new MacroAssembler(&code); + + __ verify_thread(); + + // This is an inlined and slightly modified version of call_VM + // which has the ability to fetch the return PC out of thread-local storage + __ assert_not_delayed(); + + // Note that we always push a frame because on the SPARC + // architecture, for all of our implicit exception kinds at call + // sites, the implicit exception is taken before the callee frame + // is pushed. + __ save_frame(0); + + int frame_complete = __ offset(); + + if (restore_saved_exception_pc) { + Address saved_exception_pc(G2_thread, 0, in_bytes(JavaThread::saved_exception_pc_offset())); + __ ld_ptr(saved_exception_pc, I7); + __ sub(I7, frame::pc_return_offset, I7); + } + + // Note that we always have a runtime stub frame on the top of stack by this point + Register last_java_sp = SP; + // 64-bit last_java_sp is biased! + __ set_last_Java_frame(last_java_sp, G0); + if (VerifyThread) __ mov(G2_thread, O0); // about to be smashed; pass early + __ save_thread(noreg); + // do the call + BLOCK_COMMENT("call runtime_entry"); + __ call(runtime_entry, relocInfo::runtime_call_type); + if (!VerifyThread) + __ delayed()->mov(G2_thread, O0); // pass thread as first argument + else + __ delayed()->nop(); // (thread already passed) + __ restore_thread(noreg); + __ reset_last_Java_frame(); + + // check for pending exceptions. use Gtemp as scratch register. +#ifdef ASSERT + Label L; + + Address exception_addr(G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + Register scratch_reg = Gtemp; + __ ld_ptr(exception_addr, scratch_reg); + __ br_notnull(scratch_reg, false, Assembler::pt, L); + __ delayed()->nop(); + __ should_not_reach_here(); + __ bind(L); +#endif // ASSERT + BLOCK_COMMENT("call forward_exception_entry"); + __ call(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); + // we use O7 linkage so that forward_exception_entry has the issuing PC + __ delayed()->restore(); + + RuntimeStub* stub = RuntimeStub::new_runtime_stub(name, &code, frame_complete, masm->total_frame_size_in_bytes(0), NULL, false); + return stub->entry_point(); + } + +#undef __ +#define __ _masm-> + + + // Generate a routine that sets all the registers so we + // can tell if the stop routine prints them correctly. + address generate_test_stop() { + StubCodeMark mark(this, "StubRoutines", "test_stop"); + address start = __ pc(); + + int i; + + __ save_frame(0); + + static jfloat zero = 0.0, one = 1.0; + + // put addr in L0, then load through L0 to F0 + __ set((intptr_t)&zero, L0); __ ldf( FloatRegisterImpl::S, L0, 0, F0); + __ set((intptr_t)&one, L0); __ ldf( FloatRegisterImpl::S, L0, 0, F1); // 1.0 to F1 + + // use add to put 2..18 in F2..F18 + for ( i = 2; i <= 18; ++i ) { + __ fadd( FloatRegisterImpl::S, F1, as_FloatRegister(i-1), as_FloatRegister(i)); + } + + // Now put double 2 in F16, double 18 in F18 + __ ftof( FloatRegisterImpl::S, FloatRegisterImpl::D, F2, F16 ); + __ ftof( FloatRegisterImpl::S, FloatRegisterImpl::D, F18, F18 ); + + // use add to put 20..32 in F20..F32 + for (i = 20; i < 32; i += 2) { + __ fadd( FloatRegisterImpl::D, F16, as_FloatRegister(i-2), as_FloatRegister(i)); + } + + // put 0..7 in i's, 8..15 in l's, 16..23 in o's, 24..31 in g's + for ( i = 0; i < 8; ++i ) { + if (i < 6) { + __ set( i, as_iRegister(i)); + __ set(16 + i, as_oRegister(i)); + __ set(24 + i, as_gRegister(i)); + } + __ set( 8 + i, as_lRegister(i)); + } + + __ stop("testing stop"); + + + __ ret(); + __ delayed()->restore(); + + return start; + } + + + address generate_stop_subroutine() { + StubCodeMark mark(this, "StubRoutines", "stop_subroutine"); + address start = __ pc(); + + __ stop_subroutine(); + + return start; + } + + address generate_flush_callers_register_windows() { + StubCodeMark mark(this, "StubRoutines", "flush_callers_register_windows"); + address start = __ pc(); + + __ flush_windows(); + __ retl(false); + __ delayed()->add( FP, STACK_BIAS, O0 ); + // The returned value must be a stack pointer whose register save area + // is flushed, and will stay flushed while the caller executes. + + return start; + } + + // Helper functions for v8 atomic operations. + // + void get_v8_oop_lock_ptr(Register lock_ptr_reg, Register mark_oop_reg, Register scratch_reg) { + if (mark_oop_reg == noreg) { + address lock_ptr = (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr(); + __ set((intptr_t)lock_ptr, lock_ptr_reg); + } else { + assert(scratch_reg != noreg, "just checking"); + address lock_ptr = (address)StubRoutines::Sparc::_v8_oop_lock_cache; + __ set((intptr_t)lock_ptr, lock_ptr_reg); + __ and3(mark_oop_reg, StubRoutines::Sparc::v8_oop_lock_mask_in_place, scratch_reg); + __ add(lock_ptr_reg, scratch_reg, lock_ptr_reg); + } + } + + void generate_v8_lock_prologue(Register lock_reg, Register lock_ptr_reg, Register yield_reg, Label& retry, Label& dontyield, Register mark_oop_reg = noreg, Register scratch_reg = noreg) { + + get_v8_oop_lock_ptr(lock_ptr_reg, mark_oop_reg, scratch_reg); + __ set(StubRoutines::Sparc::locked, lock_reg); + // Initialize yield counter + __ mov(G0,yield_reg); + + __ BIND(retry); + __ cmp(yield_reg, V8AtomicOperationUnderLockSpinCount); + __ br(Assembler::less, false, Assembler::pt, dontyield); + __ delayed()->nop(); + + // This code can only be called from inside the VM, this + // stub is only invoked from Atomic::add(). We do not + // want to use call_VM, because _last_java_sp and such + // must already be set. + // + // Save the regs and make space for a C call + __ save(SP, -96, SP); + __ save_all_globals_into_locals(); + BLOCK_COMMENT("call os::naked_sleep"); + __ call(CAST_FROM_FN_PTR(address, os::naked_sleep)); + __ delayed()->nop(); + __ restore_globals_from_locals(); + __ restore(); + // reset the counter + __ mov(G0,yield_reg); + + __ BIND(dontyield); + + // try to get lock + __ swap(lock_ptr_reg, 0, lock_reg); + + // did we get the lock? + __ cmp(lock_reg, StubRoutines::Sparc::unlocked); + __ br(Assembler::notEqual, true, Assembler::pn, retry); + __ delayed()->add(yield_reg,1,yield_reg); + + // yes, got lock. do the operation here. + } + + void generate_v8_lock_epilogue(Register lock_reg, Register lock_ptr_reg, Register yield_reg, Label& retry, Label& dontyield, Register mark_oop_reg = noreg, Register scratch_reg = noreg) { + __ st(lock_reg, lock_ptr_reg, 0); // unlock + } + + // Support for jint Atomic::xchg(jint exchange_value, volatile jint* dest). + // + // Arguments : + // + // exchange_value: O0 + // dest: O1 + // + // Results: + // + // O0: the value previously stored in dest + // + address generate_atomic_xchg() { + StubCodeMark mark(this, "StubRoutines", "atomic_xchg"); + address start = __ pc(); + + if (UseCASForSwap) { + // Use CAS instead of swap, just in case the MP hardware + // prefers to work with just one kind of synch. instruction. + Label retry; + __ BIND(retry); + __ mov(O0, O3); // scratch copy of exchange value + __ ld(O1, 0, O2); // observe the previous value + // try to replace O2 with O3 + __ cas_under_lock(O1, O2, O3, + (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr(),false); + __ cmp(O2, O3); + __ br(Assembler::notEqual, false, Assembler::pn, retry); + __ delayed()->nop(); + + __ retl(false); + __ delayed()->mov(O2, O0); // report previous value to caller + + } else { + if (VM_Version::v9_instructions_work()) { + __ retl(false); + __ delayed()->swap(O1, 0, O0); + } else { + const Register& lock_reg = O2; + const Register& lock_ptr_reg = O3; + const Register& yield_reg = O4; + + Label retry; + Label dontyield; + + generate_v8_lock_prologue(lock_reg, lock_ptr_reg, yield_reg, retry, dontyield); + // got the lock, do the swap + __ swap(O1, 0, O0); + + generate_v8_lock_epilogue(lock_reg, lock_ptr_reg, yield_reg, retry, dontyield); + __ retl(false); + __ delayed()->nop(); + } + } + + return start; + } + + + // Support for jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) + // + // Arguments : + // + // exchange_value: O0 + // dest: O1 + // compare_value: O2 + // + // Results: + // + // O0: the value previously stored in dest + // + // Overwrites (v8): O3,O4,O5 + // + address generate_atomic_cmpxchg() { + StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg"); + address start = __ pc(); + + // cmpxchg(dest, compare_value, exchange_value) + __ cas_under_lock(O1, O2, O0, + (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr(),false); + __ retl(false); + __ delayed()->nop(); + + return start; + } + + // Support for jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong *dest, jlong compare_value) + // + // Arguments : + // + // exchange_value: O1:O0 + // dest: O2 + // compare_value: O4:O3 + // + // Results: + // + // O1:O0: the value previously stored in dest + // + // This only works on V9, on V8 we don't generate any + // code and just return NULL. + // + // Overwrites: G1,G2,G3 + // + address generate_atomic_cmpxchg_long() { + StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg_long"); + address start = __ pc(); + + if (!VM_Version::supports_cx8()) + return NULL;; + __ sllx(O0, 32, O0); + __ srl(O1, 0, O1); + __ or3(O0,O1,O0); // O0 holds 64-bit value from compare_value + __ sllx(O3, 32, O3); + __ srl(O4, 0, O4); + __ or3(O3,O4,O3); // O3 holds 64-bit value from exchange_value + __ casx(O2, O3, O0); + __ srl(O0, 0, O1); // unpacked return value in O1:O0 + __ retl(false); + __ delayed()->srlx(O0, 32, O0); + + return start; + } + + + // Support for jint Atomic::add(jint add_value, volatile jint* dest). + // + // Arguments : + // + // add_value: O0 (e.g., +1 or -1) + // dest: O1 + // + // Results: + // + // O0: the new value stored in dest + // + // Overwrites (v9): O3 + // Overwrites (v8): O3,O4,O5 + // + address generate_atomic_add() { + StubCodeMark mark(this, "StubRoutines", "atomic_add"); + address start = __ pc(); + __ BIND(_atomic_add_stub); + + if (VM_Version::v9_instructions_work()) { + Label(retry); + __ BIND(retry); + + __ lduw(O1, 0, O2); + __ add(O0, O2, O3); + __ cas(O1, O2, O3); + __ cmp( O2, O3); + __ br(Assembler::notEqual, false, Assembler::pn, retry); + __ delayed()->nop(); + __ retl(false); + __ delayed()->add(O0, O2, O0); // note that cas made O2==O3 + } else { + const Register& lock_reg = O2; + const Register& lock_ptr_reg = O3; + const Register& value_reg = O4; + const Register& yield_reg = O5; + + Label(retry); + Label(dontyield); + + generate_v8_lock_prologue(lock_reg, lock_ptr_reg, yield_reg, retry, dontyield); + // got lock, do the increment + __ ld(O1, 0, value_reg); + __ add(O0, value_reg, value_reg); + __ st(value_reg, O1, 0); + + // %%% only for RMO and PSO + __ membar(Assembler::StoreStore); + + generate_v8_lock_epilogue(lock_reg, lock_ptr_reg, yield_reg, retry, dontyield); + + __ retl(false); + __ delayed()->mov(value_reg, O0); + } + + return start; + } + Label _atomic_add_stub; // called from other stubs + + + // Support for void OrderAccess::fence(). + // + address generate_fence() { + StubCodeMark mark(this, "StubRoutines", "fence"); + address start = __ pc(); + + __ membar(Assembler::Membar_mask_bits(Assembler::LoadLoad | Assembler::LoadStore | + Assembler::StoreLoad | Assembler::StoreStore)); + __ retl(false); + __ delayed()->nop(); + + return start; + } + + + //------------------------------------------------------------------------------------------------------------------------ + // The following routine generates a subroutine to throw an asynchronous + // UnknownError when an unsafe access gets a fault that could not be + // reasonably prevented by the programmer. (Example: SIGBUS/OBJERR.) + // + // Arguments : + // + // trapping PC: O7 + // + // Results: + // posts an asynchronous exception, skips the trapping instruction + // + + address generate_handler_for_unsafe_access() { + StubCodeMark mark(this, "StubRoutines", "handler_for_unsafe_access"); + address start = __ pc(); + + const int preserve_register_words = (64 * 2); + Address preserve_addr(FP, 0, (-preserve_register_words * wordSize) + STACK_BIAS); + + Register Lthread = L7_thread_cache; + int i; + + __ save_frame(0); + __ mov(G1, L1); + __ mov(G2, L2); + __ mov(G3, L3); + __ mov(G4, L4); + __ mov(G5, L5); + for (i = 0; i < (VM_Version::v9_instructions_work() ? 64 : 32); i += 2) { + __ stf(FloatRegisterImpl::D, as_FloatRegister(i), preserve_addr, i * wordSize); + } + + address entry_point = CAST_FROM_FN_PTR(address, handle_unsafe_access); + BLOCK_COMMENT("call handle_unsafe_access"); + __ call(entry_point, relocInfo::runtime_call_type); + __ delayed()->nop(); + + __ mov(L1, G1); + __ mov(L2, G2); + __ mov(L3, G3); + __ mov(L4, G4); + __ mov(L5, G5); + for (i = 0; i < (VM_Version::v9_instructions_work() ? 64 : 32); i += 2) { + __ ldf(FloatRegisterImpl::D, preserve_addr, as_FloatRegister(i), i * wordSize); + } + + __ verify_thread(); + + __ jmp(O0, 0); + __ delayed()->restore(); + + return start; + } + + + // Support for uint StubRoutine::Sparc::partial_subtype_check( Klass sub, Klass super ); + // Arguments : + // + // ret : O0, returned + // icc/xcc: set as O0 (depending on wordSize) + // sub : O1, argument, not changed + // super: O2, argument, not changed + // raddr: O7, blown by call + address generate_partial_subtype_check() { + StubCodeMark mark(this, "StubRoutines", "partial_subtype_check"); + address start = __ pc(); + Label loop, miss; + + // Compare super with sub directly, since super is not in its own SSA. + // The compiler used to emit this test, but we fold it in here, + // to increase overall code density, with no real loss of speed. + { Label L; + __ cmp(O1, O2); + __ brx(Assembler::notEqual, false, Assembler::pt, L); + __ delayed()->nop(); + __ retl(); + __ delayed()->addcc(G0,0,O0); // set Z flags, zero result + __ bind(L); + } + +#if defined(COMPILER2) && !defined(_LP64) + // Do not use a 'save' because it blows the 64-bit O registers. + __ add(SP,-4*wordSize,SP); // Make space for 4 temps + __ st_ptr(L0,SP,(frame::register_save_words+0)*wordSize); + __ st_ptr(L1,SP,(frame::register_save_words+1)*wordSize); + __ st_ptr(L2,SP,(frame::register_save_words+2)*wordSize); + __ st_ptr(L3,SP,(frame::register_save_words+3)*wordSize); + Register Rret = O0; + Register Rsub = O1; + Register Rsuper = O2; +#else + __ save_frame(0); + Register Rret = I0; + Register Rsub = I1; + Register Rsuper = I2; +#endif + + Register L0_ary_len = L0; + Register L1_ary_ptr = L1; + Register L2_super = L2; + Register L3_index = L3; + + inc_counter_np(SharedRuntime::_partial_subtype_ctr, L0, L1); + + __ ld_ptr( Rsub, sizeof(oopDesc) + Klass::secondary_supers_offset_in_bytes(), L3 ); + __ lduw(L3,arrayOopDesc::length_offset_in_bytes(),L0_ary_len); + __ add(L3,arrayOopDesc::base_offset_in_bytes(T_OBJECT),L1_ary_ptr); + __ clr(L3_index); // zero index + // Load a little early; will load 1 off the end of the array. + // Ok for now; revisit if we have other uses of this routine. + __ ld_ptr(L1_ary_ptr,0,L2_super);// Will load a little early + __ align(CodeEntryAlignment); + + // The scan loop + __ BIND(loop); + __ add(L1_ary_ptr,wordSize,L1_ary_ptr); // Bump by OOP size + __ cmp(L3_index,L0_ary_len); + __ br(Assembler::equal,false,Assembler::pn,miss); + __ delayed()->inc(L3_index); // Bump index + __ subcc(L2_super,Rsuper,Rret); // Check for match; zero in Rret for a hit + __ brx( Assembler::notEqual, false, Assembler::pt, loop ); + __ delayed()->ld_ptr(L1_ary_ptr,0,L2_super); // Will load a little early + + // Got a hit; report success; set cache. Cache load doesn't + // happen here; for speed it is directly emitted by the compiler. + __ st_ptr( Rsuper, Rsub, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() ); + +#if defined(COMPILER2) && !defined(_LP64) + __ ld_ptr(SP,(frame::register_save_words+0)*wordSize,L0); + __ ld_ptr(SP,(frame::register_save_words+1)*wordSize,L1); + __ ld_ptr(SP,(frame::register_save_words+2)*wordSize,L2); + __ ld_ptr(SP,(frame::register_save_words+3)*wordSize,L3); + __ retl(); // Result in Rret is zero; flags set to Z + __ delayed()->add(SP,4*wordSize,SP); +#else + __ ret(); // Result in Rret is zero; flags set to Z + __ delayed()->restore(); +#endif + + // Hit or miss falls through here + __ BIND(miss); + __ addcc(G0,1,Rret); // set NZ flags, NZ result + +#if defined(COMPILER2) && !defined(_LP64) + __ ld_ptr(SP,(frame::register_save_words+0)*wordSize,L0); + __ ld_ptr(SP,(frame::register_save_words+1)*wordSize,L1); + __ ld_ptr(SP,(frame::register_save_words+2)*wordSize,L2); + __ ld_ptr(SP,(frame::register_save_words+3)*wordSize,L3); + __ retl(); // Result in Rret is != 0; flags set to NZ + __ delayed()->add(SP,4*wordSize,SP); +#else + __ ret(); // Result in Rret is != 0; flags set to NZ + __ delayed()->restore(); +#endif + + return start; + } + + + // Called from MacroAssembler::verify_oop + // + address generate_verify_oop_subroutine() { + StubCodeMark mark(this, "StubRoutines", "verify_oop_stub"); + + address start = __ pc(); + + __ verify_oop_subroutine(); + + return start; + } + + static address disjoint_byte_copy_entry; + static address disjoint_short_copy_entry; + static address disjoint_int_copy_entry; + static address disjoint_long_copy_entry; + static address disjoint_oop_copy_entry; + + static address byte_copy_entry; + static address short_copy_entry; + static address int_copy_entry; + static address long_copy_entry; + static address oop_copy_entry; + + static address checkcast_copy_entry; + + // + // Verify that a register contains clean 32-bits positive value + // (high 32-bits are 0) so it could be used in 64-bits shifts (sllx, srax). + // + // Input: + // Rint - 32-bits value + // Rtmp - scratch + // + void assert_clean_int(Register Rint, Register Rtmp) { +#if defined(ASSERT) && defined(_LP64) + __ signx(Rint, Rtmp); + __ cmp(Rint, Rtmp); + __ breakpoint_trap(Assembler::notEqual, Assembler::xcc); +#endif + } + + // + // Generate overlap test for array copy stubs + // + // Input: + // O0 - array1 + // O1 - array2 + // O2 - element count + // + // Kills temps: O3, O4 + // + void array_overlap_test(address no_overlap_target, int log2_elem_size) { + assert(no_overlap_target != NULL, "must be generated"); + array_overlap_test(no_overlap_target, NULL, log2_elem_size); + } + void array_overlap_test(Label& L_no_overlap, int log2_elem_size) { + array_overlap_test(NULL, &L_no_overlap, log2_elem_size); + } + void array_overlap_test(address no_overlap_target, Label* NOLp, int log2_elem_size) { + const Register from = O0; + const Register to = O1; + const Register count = O2; + const Register to_from = O3; // to - from + const Register byte_count = O4; // count << log2_elem_size + + __ subcc(to, from, to_from); + __ sll_ptr(count, log2_elem_size, byte_count); + if (NOLp == NULL) + __ brx(Assembler::lessEqualUnsigned, false, Assembler::pt, no_overlap_target); + else + __ brx(Assembler::lessEqualUnsigned, false, Assembler::pt, (*NOLp)); + __ delayed()->cmp(to_from, byte_count); + if (NOLp == NULL) + __ brx(Assembler::greaterEqual, false, Assembler::pt, no_overlap_target); + else + __ brx(Assembler::greaterEqual, false, Assembler::pt, (*NOLp)); + __ delayed()->nop(); + } + + // + // Generate pre-write barrier for array. + // + // Input: + // addr - register containing starting address + // count - register containing element count + // tmp - scratch register + // + // The input registers are overwritten. + // + void gen_write_ref_array_pre_barrier(Register addr, Register count) { +#if 0 // G1 only + BarrierSet* bs = Universe::heap()->barrier_set(); + if (bs->has_write_ref_pre_barrier()) { + assert(bs->has_write_ref_array_pre_opt(), + "Else unsupported barrier set."); + + assert(addr->is_global() && count->is_global(), + "If not, then we have to fix this code to handle more " + "general cases."); + // Get some new fresh output registers. + __ save_frame(0); + // Save the necessary global regs... will be used after. + __ mov(addr, L0); + __ mov(count, L1); + + __ mov(addr, O0); + // Get the count into O1 + __ call(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_pre)); + __ delayed()->mov(count, O1); + __ mov(L0, addr); + __ mov(L1, count); + __ restore(); + } +#endif // 0 + } + + // + // Generate post-write barrier for array. + // + // Input: + // addr - register containing starting address + // count - register containing element count + // tmp - scratch register + // + // The input registers are overwritten. + // + void gen_write_ref_array_post_barrier(Register addr, Register count, + Register tmp) { + BarrierSet* bs = Universe::heap()->barrier_set(); + + switch (bs->kind()) { +#if 0 // G1 - only + case BarrierSet::G1SATBCT: + case BarrierSet::G1SATBCTLogging: + { + assert(addr->is_global() && count->is_global(), + "If not, then we have to fix this code to handle more " + "general cases."); + // Get some new fresh output registers. + __ save_frame(0); + __ mov(addr, O0); + __ call(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post)); + __ delayed()->mov(count, O1); + __ restore(); + } + break; +#endif // 0 G1 - only + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + { + CardTableModRefBS* ct = (CardTableModRefBS*)bs; + assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); + assert_different_registers(addr, count, tmp); + + Label L_loop; + + __ sll_ptr(count, LogBytesPerOop, count); + __ sub(count, BytesPerOop, count); + __ add(count, addr, count); + // Use two shifts to clear out those low order two bits! (Cannot opt. into 1.) + __ srl_ptr(addr, CardTableModRefBS::card_shift, addr); + __ srl_ptr(count, CardTableModRefBS::card_shift, count); + __ sub(count, addr, count); + Address rs(tmp, (address)ct->byte_map_base); + __ load_address(rs); + __ BIND(L_loop); + __ stb(G0, rs.base(), addr); + __ subcc(count, 1, count); + __ brx(Assembler::greaterEqual, false, Assembler::pt, L_loop); + __ delayed()->add(addr, 1, addr); + + } + break; + case BarrierSet::ModRef: + break; + default : + ShouldNotReachHere(); + + } + + } + + + // Copy big chunks forward with shift + // + // Inputs: + // from - source arrays + // to - destination array aligned to 8-bytes + // count - elements count to copy >= the count equivalent to 16 bytes + // count_dec - elements count's decrement equivalent to 16 bytes + // L_copy_bytes - copy exit label + // + void copy_16_bytes_forward_with_shift(Register from, Register to, + Register count, int count_dec, Label& L_copy_bytes) { + Label L_loop, L_aligned_copy, L_copy_last_bytes; + + // if both arrays have the same alignment mod 8, do 8 bytes aligned copy + __ andcc(from, 7, G1); // misaligned bytes + __ br(Assembler::zero, false, Assembler::pt, L_aligned_copy); + __ delayed()->nop(); + + const Register left_shift = G1; // left shift bit counter + const Register right_shift = G5; // right shift bit counter + + __ sll(G1, LogBitsPerByte, left_shift); + __ mov(64, right_shift); + __ sub(right_shift, left_shift, right_shift); + + // + // Load 2 aligned 8-bytes chunks and use one from previous iteration + // to form 2 aligned 8-bytes chunks to store. + // + __ deccc(count, count_dec); // Pre-decrement 'count' + __ andn(from, 7, from); // Align address + __ ldx(from, 0, O3); + __ inc(from, 8); + __ align(16); + __ BIND(L_loop); + __ ldx(from, 0, O4); + __ deccc(count, count_dec); // Can we do next iteration after this one? + __ ldx(from, 8, G4); + __ inc(to, 16); + __ inc(from, 16); + __ sllx(O3, left_shift, O3); + __ srlx(O4, right_shift, G3); + __ bset(G3, O3); + __ stx(O3, to, -16); + __ sllx(O4, left_shift, O4); + __ srlx(G4, right_shift, G3); + __ bset(G3, O4); + __ stx(O4, to, -8); + __ brx(Assembler::greaterEqual, false, Assembler::pt, L_loop); + __ delayed()->mov(G4, O3); + + __ inccc(count, count_dec>>1 ); // + 8 bytes + __ brx(Assembler::negative, true, Assembler::pn, L_copy_last_bytes); + __ delayed()->inc(count, count_dec>>1); // restore 'count' + + // copy 8 bytes, part of them already loaded in O3 + __ ldx(from, 0, O4); + __ inc(to, 8); + __ inc(from, 8); + __ sllx(O3, left_shift, O3); + __ srlx(O4, right_shift, G3); + __ bset(O3, G3); + __ stx(G3, to, -8); + + __ BIND(L_copy_last_bytes); + __ srl(right_shift, LogBitsPerByte, right_shift); // misaligned bytes + __ br(Assembler::always, false, Assembler::pt, L_copy_bytes); + __ delayed()->sub(from, right_shift, from); // restore address + + __ BIND(L_aligned_copy); + } + + // Copy big chunks backward with shift + // + // Inputs: + // end_from - source arrays end address + // end_to - destination array end address aligned to 8-bytes + // count - elements count to copy >= the count equivalent to 16 bytes + // count_dec - elements count's decrement equivalent to 16 bytes + // L_aligned_copy - aligned copy exit label + // L_copy_bytes - copy exit label + // + void copy_16_bytes_backward_with_shift(Register end_from, Register end_to, + Register count, int count_dec, + Label& L_aligned_copy, Label& L_copy_bytes) { + Label L_loop, L_copy_last_bytes; + + // if both arrays have the same alignment mod 8, do 8 bytes aligned copy + __ andcc(end_from, 7, G1); // misaligned bytes + __ br(Assembler::zero, false, Assembler::pt, L_aligned_copy); + __ delayed()->deccc(count, count_dec); // Pre-decrement 'count' + + const Register left_shift = G1; // left shift bit counter + const Register right_shift = G5; // right shift bit counter + + __ sll(G1, LogBitsPerByte, left_shift); + __ mov(64, right_shift); + __ sub(right_shift, left_shift, right_shift); + + // + // Load 2 aligned 8-bytes chunks and use one from previous iteration + // to form 2 aligned 8-bytes chunks to store. + // + __ andn(end_from, 7, end_from); // Align address + __ ldx(end_from, 0, O3); + __ align(16); + __ BIND(L_loop); + __ ldx(end_from, -8, O4); + __ deccc(count, count_dec); // Can we do next iteration after this one? + __ ldx(end_from, -16, G4); + __ dec(end_to, 16); + __ dec(end_from, 16); + __ srlx(O3, right_shift, O3); + __ sllx(O4, left_shift, G3); + __ bset(G3, O3); + __ stx(O3, end_to, 8); + __ srlx(O4, right_shift, O4); + __ sllx(G4, left_shift, G3); + __ bset(G3, O4); + __ stx(O4, end_to, 0); + __ brx(Assembler::greaterEqual, false, Assembler::pt, L_loop); + __ delayed()->mov(G4, O3); + + __ inccc(count, count_dec>>1 ); // + 8 bytes + __ brx(Assembler::negative, true, Assembler::pn, L_copy_last_bytes); + __ delayed()->inc(count, count_dec>>1); // restore 'count' + + // copy 8 bytes, part of them already loaded in O3 + __ ldx(end_from, -8, O4); + __ dec(end_to, 8); + __ dec(end_from, 8); + __ srlx(O3, right_shift, O3); + __ sllx(O4, left_shift, G3); + __ bset(O3, G3); + __ stx(G3, end_to, 0); + + __ BIND(L_copy_last_bytes); + __ srl(left_shift, LogBitsPerByte, left_shift); // misaligned bytes + __ br(Assembler::always, false, Assembler::pt, L_copy_bytes); + __ delayed()->add(end_from, left_shift, end_from); // restore address + } + + // + // Generate stub for disjoint byte copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + address generate_disjoint_byte_copy(bool aligned, const char * name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_skip_alignment, L_align; + Label L_copy_byte, L_copy_byte_loop, L_exit; + + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + const Register offset = O5; // offset from start of arrays + // O3, O4, G3, G4 are used as temp registers + + assert_clean_int(count, O3); // Make sure 'count' is clean int. + + if (!aligned) disjoint_byte_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + if (!aligned) BLOCK_COMMENT("Entry:"); + + // for short arrays, just do single element copy + __ cmp(count, 23); // 16 + 7 + __ brx(Assembler::less, false, Assembler::pn, L_copy_byte); + __ delayed()->mov(G0, offset); + + if (aligned) { + // 'aligned' == true when it is known statically during compilation + // of this arraycopy call site that both 'from' and 'to' addresses + // are HeapWordSize aligned (see LibraryCallKit::basictype2arraycopy()). + // + // Aligned arrays have 4 bytes alignment in 32-bits VM + // and 8 bytes - in 64-bits VM. So we do it only for 32-bits VM + // +#ifndef _LP64 + // copy a 4-bytes word if necessary to align 'to' to 8 bytes + __ andcc(to, 7, G0); + __ br(Assembler::zero, false, Assembler::pn, L_skip_alignment); + __ delayed()->ld(from, 0, O3); + __ inc(from, 4); + __ inc(to, 4); + __ dec(count, 4); + __ st(O3, to, -4); + __ BIND(L_skip_alignment); +#endif + } else { + // copy bytes to align 'to' on 8 byte boundary + __ andcc(to, 7, G1); // misaligned bytes + __ br(Assembler::zero, false, Assembler::pt, L_skip_alignment); + __ delayed()->neg(G1); + __ inc(G1, 8); // bytes need to copy to next 8-bytes alignment + __ sub(count, G1, count); + __ BIND(L_align); + __ ldub(from, 0, O3); + __ deccc(G1); + __ inc(from); + __ stb(O3, to, 0); + __ br(Assembler::notZero, false, Assembler::pt, L_align); + __ delayed()->inc(to); + __ BIND(L_skip_alignment); + } +#ifdef _LP64 + if (!aligned) +#endif + { + // Copy with shift 16 bytes per iteration if arrays do not have + // the same alignment mod 8, otherwise fall through to the next + // code for aligned copy. + // The compare above (count >= 23) guarantes 'count' >= 16 bytes. + // Also jump over aligned copy after the copy with shift completed. + + copy_16_bytes_forward_with_shift(from, to, count, 16, L_copy_byte); + } + + // Both array are 8 bytes aligned, copy 16 bytes at a time + __ and3(count, 7, G4); // Save count + __ srl(count, 3, count); + generate_disjoint_long_copy_core(aligned); + __ mov(G4, count); // Restore count + + // copy tailing bytes + __ BIND(L_copy_byte); + __ br_zero(Assembler::zero, false, Assembler::pt, count, L_exit); + __ delayed()->nop(); + __ align(16); + __ BIND(L_copy_byte_loop); + __ ldub(from, offset, O3); + __ deccc(count); + __ stb(O3, to, offset); + __ brx(Assembler::notZero, false, Assembler::pt, L_copy_byte_loop); + __ delayed()->inc(offset); + + __ BIND(L_exit); + // O3, O4 are used as temp registers + inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->mov(G0, O0); // return 0 + return start; + } + + // + // Generate stub for conjoint byte copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + address generate_conjoint_byte_copy(bool aligned, const char * name) { + // Do reverse copy. + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + address nooverlap_target = aligned ? + StubRoutines::arrayof_jbyte_disjoint_arraycopy() : + disjoint_byte_copy_entry; + + Label L_skip_alignment, L_align, L_aligned_copy; + Label L_copy_byte, L_copy_byte_loop, L_exit; + + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + const Register end_from = from; // source array end address + const Register end_to = to; // destination array end address + + assert_clean_int(count, O3); // Make sure 'count' is clean int. + + if (!aligned) byte_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + if (!aligned) BLOCK_COMMENT("Entry:"); + + array_overlap_test(nooverlap_target, 0); + + __ add(to, count, end_to); // offset after last copied element + + // for short arrays, just do single element copy + __ cmp(count, 23); // 16 + 7 + __ brx(Assembler::less, false, Assembler::pn, L_copy_byte); + __ delayed()->add(from, count, end_from); + + { + // Align end of arrays since they could be not aligned even + // when arrays itself are aligned. + + // copy bytes to align 'end_to' on 8 byte boundary + __ andcc(end_to, 7, G1); // misaligned bytes + __ br(Assembler::zero, false, Assembler::pt, L_skip_alignment); + __ delayed()->nop(); + __ sub(count, G1, count); + __ BIND(L_align); + __ dec(end_from); + __ dec(end_to); + __ ldub(end_from, 0, O3); + __ deccc(G1); + __ brx(Assembler::notZero, false, Assembler::pt, L_align); + __ delayed()->stb(O3, end_to, 0); + __ BIND(L_skip_alignment); + } +#ifdef _LP64 + if (aligned) { + // Both arrays are aligned to 8-bytes in 64-bits VM. + // The 'count' is decremented in copy_16_bytes_backward_with_shift() + // in unaligned case. + __ dec(count, 16); + } else +#endif + { + // Copy with shift 16 bytes per iteration if arrays do not have + // the same alignment mod 8, otherwise jump to the next + // code for aligned copy (and substracting 16 from 'count' before jump). + // The compare above (count >= 11) guarantes 'count' >= 16 bytes. + // Also jump over aligned copy after the copy with shift completed. + + copy_16_bytes_backward_with_shift(end_from, end_to, count, 16, + L_aligned_copy, L_copy_byte); + } + // copy 4 elements (16 bytes) at a time + __ align(16); + __ BIND(L_aligned_copy); + __ dec(end_from, 16); + __ ldx(end_from, 8, O3); + __ ldx(end_from, 0, O4); + __ dec(end_to, 16); + __ deccc(count, 16); + __ stx(O3, end_to, 8); + __ brx(Assembler::greaterEqual, false, Assembler::pt, L_aligned_copy); + __ delayed()->stx(O4, end_to, 0); + __ inc(count, 16); + + // copy 1 element (2 bytes) at a time + __ BIND(L_copy_byte); + __ br_zero(Assembler::zero, false, Assembler::pt, count, L_exit); + __ delayed()->nop(); + __ align(16); + __ BIND(L_copy_byte_loop); + __ dec(end_from); + __ dec(end_to); + __ ldub(end_from, 0, O4); + __ deccc(count); + __ brx(Assembler::greater, false, Assembler::pt, L_copy_byte_loop); + __ delayed()->stb(O4, end_to, 0); + + __ BIND(L_exit); + // O3, O4 are used as temp registers + inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->mov(G0, O0); // return 0 + return start; + } + + // + // Generate stub for disjoint short copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + address generate_disjoint_short_copy(bool aligned, const char * name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_skip_alignment, L_skip_alignment2; + Label L_copy_2_bytes, L_copy_2_bytes_loop, L_exit; + + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + const Register offset = O5; // offset from start of arrays + // O3, O4, G3, G4 are used as temp registers + + assert_clean_int(count, O3); // Make sure 'count' is clean int. + + if (!aligned) disjoint_short_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + if (!aligned) BLOCK_COMMENT("Entry:"); + + // for short arrays, just do single element copy + __ cmp(count, 11); // 8 + 3 (22 bytes) + __ brx(Assembler::less, false, Assembler::pn, L_copy_2_bytes); + __ delayed()->mov(G0, offset); + + if (aligned) { + // 'aligned' == true when it is known statically during compilation + // of this arraycopy call site that both 'from' and 'to' addresses + // are HeapWordSize aligned (see LibraryCallKit::basictype2arraycopy()). + // + // Aligned arrays have 4 bytes alignment in 32-bits VM + // and 8 bytes - in 64-bits VM. + // +#ifndef _LP64 + // copy a 2-elements word if necessary to align 'to' to 8 bytes + __ andcc(to, 7, G0); + __ br(Assembler::zero, false, Assembler::pt, L_skip_alignment); + __ delayed()->ld(from, 0, O3); + __ inc(from, 4); + __ inc(to, 4); + __ dec(count, 2); + __ st(O3, to, -4); + __ BIND(L_skip_alignment); +#endif + } else { + // copy 1 element if necessary to align 'to' on an 4 bytes + __ andcc(to, 3, G0); + __ br(Assembler::zero, false, Assembler::pt, L_skip_alignment); + __ delayed()->lduh(from, 0, O3); + __ inc(from, 2); + __ inc(to, 2); + __ dec(count); + __ sth(O3, to, -2); + __ BIND(L_skip_alignment); + + // copy 2 elements to align 'to' on an 8 byte boundary + __ andcc(to, 7, G0); + __ br(Assembler::zero, false, Assembler::pn, L_skip_alignment2); + __ delayed()->lduh(from, 0, O3); + __ dec(count, 2); + __ lduh(from, 2, O4); + __ inc(from, 4); + __ inc(to, 4); + __ sth(O3, to, -4); + __ sth(O4, to, -2); + __ BIND(L_skip_alignment2); + } +#ifdef _LP64 + if (!aligned) +#endif + { + // Copy with shift 16 bytes per iteration if arrays do not have + // the same alignment mod 8, otherwise fall through to the next + // code for aligned copy. + // The compare above (count >= 11) guarantes 'count' >= 16 bytes. + // Also jump over aligned copy after the copy with shift completed. + + copy_16_bytes_forward_with_shift(from, to, count, 8, L_copy_2_bytes); + } + + // Both array are 8 bytes aligned, copy 16 bytes at a time + __ and3(count, 3, G4); // Save + __ srl(count, 2, count); + generate_disjoint_long_copy_core(aligned); + __ mov(G4, count); // restore + + // copy 1 element at a time + __ BIND(L_copy_2_bytes); + __ br_zero(Assembler::zero, false, Assembler::pt, count, L_exit); + __ delayed()->nop(); + __ align(16); + __ BIND(L_copy_2_bytes_loop); + __ lduh(from, offset, O3); + __ deccc(count); + __ sth(O3, to, offset); + __ brx(Assembler::notZero, false, Assembler::pt, L_copy_2_bytes_loop); + __ delayed()->inc(offset, 2); + + __ BIND(L_exit); + // O3, O4 are used as temp registers + inc_counter_np(SharedRuntime::_jshort_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->mov(G0, O0); // return 0 + return start; + } + + // + // Generate stub for conjoint short copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + address generate_conjoint_short_copy(bool aligned, const char * name) { + // Do reverse copy. + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + address nooverlap_target = aligned ? + StubRoutines::arrayof_jshort_disjoint_arraycopy() : + disjoint_short_copy_entry; + + Label L_skip_alignment, L_skip_alignment2, L_aligned_copy; + Label L_copy_2_bytes, L_copy_2_bytes_loop, L_exit; + + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + const Register end_from = from; // source array end address + const Register end_to = to; // destination array end address + + const Register byte_count = O3; // bytes count to copy + + assert_clean_int(count, O3); // Make sure 'count' is clean int. + + if (!aligned) short_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + if (!aligned) BLOCK_COMMENT("Entry:"); + + array_overlap_test(nooverlap_target, 1); + + __ sllx(count, LogBytesPerShort, byte_count); + __ add(to, byte_count, end_to); // offset after last copied element + + // for short arrays, just do single element copy + __ cmp(count, 11); // 8 + 3 (22 bytes) + __ brx(Assembler::less, false, Assembler::pn, L_copy_2_bytes); + __ delayed()->add(from, byte_count, end_from); + + { + // Align end of arrays since they could be not aligned even + // when arrays itself are aligned. + + // copy 1 element if necessary to align 'end_to' on an 4 bytes + __ andcc(end_to, 3, G0); + __ br(Assembler::zero, false, Assembler::pt, L_skip_alignment); + __ delayed()->lduh(end_from, -2, O3); + __ dec(end_from, 2); + __ dec(end_to, 2); + __ dec(count); + __ sth(O3, end_to, 0); + __ BIND(L_skip_alignment); + + // copy 2 elements to align 'end_to' on an 8 byte boundary + __ andcc(end_to, 7, G0); + __ br(Assembler::zero, false, Assembler::pn, L_skip_alignment2); + __ delayed()->lduh(end_from, -2, O3); + __ dec(count, 2); + __ lduh(end_from, -4, O4); + __ dec(end_from, 4); + __ dec(end_to, 4); + __ sth(O3, end_to, 2); + __ sth(O4, end_to, 0); + __ BIND(L_skip_alignment2); + } +#ifdef _LP64 + if (aligned) { + // Both arrays are aligned to 8-bytes in 64-bits VM. + // The 'count' is decremented in copy_16_bytes_backward_with_shift() + // in unaligned case. + __ dec(count, 8); + } else +#endif + { + // Copy with shift 16 bytes per iteration if arrays do not have + // the same alignment mod 8, otherwise jump to the next + // code for aligned copy (and substracting 8 from 'count' before jump). + // The compare above (count >= 11) guarantes 'count' >= 16 bytes. + // Also jump over aligned copy after the copy with shift completed. + + copy_16_bytes_backward_with_shift(end_from, end_to, count, 8, + L_aligned_copy, L_copy_2_bytes); + } + // copy 4 elements (16 bytes) at a time + __ align(16); + __ BIND(L_aligned_copy); + __ dec(end_from, 16); + __ ldx(end_from, 8, O3); + __ ldx(end_from, 0, O4); + __ dec(end_to, 16); + __ deccc(count, 8); + __ stx(O3, end_to, 8); + __ brx(Assembler::greaterEqual, false, Assembler::pt, L_aligned_copy); + __ delayed()->stx(O4, end_to, 0); + __ inc(count, 8); + + // copy 1 element (2 bytes) at a time + __ BIND(L_copy_2_bytes); + __ br_zero(Assembler::zero, false, Assembler::pt, count, L_exit); + __ delayed()->nop(); + __ BIND(L_copy_2_bytes_loop); + __ dec(end_from, 2); + __ dec(end_to, 2); + __ lduh(end_from, 0, O4); + __ deccc(count); + __ brx(Assembler::greater, false, Assembler::pt, L_copy_2_bytes_loop); + __ delayed()->sth(O4, end_to, 0); + + __ BIND(L_exit); + // O3, O4 are used as temp registers + inc_counter_np(SharedRuntime::_jshort_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->mov(G0, O0); // return 0 + return start; + } + + // + // Generate core code for disjoint int copy (and oop copy on 32-bit). + // If "aligned" is true, the "from" and "to" addresses are assumed + // to be heapword aligned. + // + // Arguments: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + void generate_disjoint_int_copy_core(bool aligned) { + + Label L_skip_alignment, L_aligned_copy; + Label L_copy_16_bytes, L_copy_4_bytes, L_copy_4_bytes_loop, L_exit; + + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + const Register offset = O5; // offset from start of arrays + // O3, O4, G3, G4 are used as temp registers + + // 'aligned' == true when it is known statically during compilation + // of this arraycopy call site that both 'from' and 'to' addresses + // are HeapWordSize aligned (see LibraryCallKit::basictype2arraycopy()). + // + // Aligned arrays have 4 bytes alignment in 32-bits VM + // and 8 bytes - in 64-bits VM. + // +#ifdef _LP64 + if (!aligned) +#endif + { + // The next check could be put under 'ifndef' since the code in + // generate_disjoint_long_copy_core() has own checks and set 'offset'. + + // for short arrays, just do single element copy + __ cmp(count, 5); // 4 + 1 (20 bytes) + __ brx(Assembler::lessEqual, false, Assembler::pn, L_copy_4_bytes); + __ delayed()->mov(G0, offset); + + // copy 1 element to align 'to' on an 8 byte boundary + __ andcc(to, 7, G0); + __ br(Assembler::zero, false, Assembler::pt, L_skip_alignment); + __ delayed()->ld(from, 0, O3); + __ inc(from, 4); + __ inc(to, 4); + __ dec(count); + __ st(O3, to, -4); + __ BIND(L_skip_alignment); + + // if arrays have same alignment mod 8, do 4 elements copy + __ andcc(from, 7, G0); + __ br(Assembler::zero, false, Assembler::pt, L_aligned_copy); + __ delayed()->ld(from, 0, O3); + + // + // Load 2 aligned 8-bytes chunks and use one from previous iteration + // to form 2 aligned 8-bytes chunks to store. + // + // copy_16_bytes_forward_with_shift() is not used here since this + // code is more optimal. + + // copy with shift 4 elements (16 bytes) at a time + __ dec(count, 4); // The cmp at the beginning guaranty count >= 4 + + __ align(16); + __ BIND(L_copy_16_bytes); + __ ldx(from, 4, O4); + __ deccc(count, 4); // Can we do next iteration after this one? + __ ldx(from, 12, G4); + __ inc(to, 16); + __ inc(from, 16); + __ sllx(O3, 32, O3); + __ srlx(O4, 32, G3); + __ bset(G3, O3); + __ stx(O3, to, -16); + __ sllx(O4, 32, O4); + __ srlx(G4, 32, G3); + __ bset(G3, O4); + __ stx(O4, to, -8); + __ brx(Assembler::greaterEqual, false, Assembler::pt, L_copy_16_bytes); + __ delayed()->mov(G4, O3); + + __ br(Assembler::always, false, Assembler::pt, L_copy_4_bytes); + __ delayed()->inc(count, 4); // restore 'count' + + __ BIND(L_aligned_copy); + } + // copy 4 elements (16 bytes) at a time + __ and3(count, 1, G4); // Save + __ srl(count, 1, count); + generate_disjoint_long_copy_core(aligned); + __ mov(G4, count); // Restore + + // copy 1 element at a time + __ BIND(L_copy_4_bytes); + __ br_zero(Assembler::zero, false, Assembler::pt, count, L_exit); + __ delayed()->nop(); + __ BIND(L_copy_4_bytes_loop); + __ ld(from, offset, O3); + __ deccc(count); + __ st(O3, to, offset); + __ brx(Assembler::notZero, false, Assembler::pt, L_copy_4_bytes_loop); + __ delayed()->inc(offset, 4); + __ BIND(L_exit); + } + + // + // Generate stub for disjoint int copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + address generate_disjoint_int_copy(bool aligned, const char * name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + const Register count = O2; + assert_clean_int(count, O3); // Make sure 'count' is clean int. + + if (!aligned) disjoint_int_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + if (!aligned) BLOCK_COMMENT("Entry:"); + + generate_disjoint_int_copy_core(aligned); + + // O3, O4 are used as temp registers + inc_counter_np(SharedRuntime::_jint_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->mov(G0, O0); // return 0 + return start; + } + + // + // Generate core code for conjoint int copy (and oop copy on 32-bit). + // If "aligned" is true, the "from" and "to" addresses are assumed + // to be heapword aligned. + // + // Arguments: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + void generate_conjoint_int_copy_core(bool aligned) { + // Do reverse copy. + + Label L_skip_alignment, L_aligned_copy; + Label L_copy_16_bytes, L_copy_4_bytes, L_copy_4_bytes_loop, L_exit; + + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + const Register end_from = from; // source array end address + const Register end_to = to; // destination array end address + // O3, O4, O5, G3 are used as temp registers + + const Register byte_count = O3; // bytes count to copy + + __ sllx(count, LogBytesPerInt, byte_count); + __ add(to, byte_count, end_to); // offset after last copied element + + __ cmp(count, 5); // for short arrays, just do single element copy + __ brx(Assembler::lessEqual, false, Assembler::pn, L_copy_4_bytes); + __ delayed()->add(from, byte_count, end_from); + + // copy 1 element to align 'to' on an 8 byte boundary + __ andcc(end_to, 7, G0); + __ br(Assembler::zero, false, Assembler::pt, L_skip_alignment); + __ delayed()->nop(); + __ dec(count); + __ dec(end_from, 4); + __ dec(end_to, 4); + __ ld(end_from, 0, O4); + __ st(O4, end_to, 0); + __ BIND(L_skip_alignment); + + // Check if 'end_from' and 'end_to' has the same alignment. + __ andcc(end_from, 7, G0); + __ br(Assembler::zero, false, Assembler::pt, L_aligned_copy); + __ delayed()->dec(count, 4); // The cmp at the start guaranty cnt >= 4 + + // copy with shift 4 elements (16 bytes) at a time + // + // Load 2 aligned 8-bytes chunks and use one from previous iteration + // to form 2 aligned 8-bytes chunks to store. + // + __ ldx(end_from, -4, O3); + __ align(16); + __ BIND(L_copy_16_bytes); + __ ldx(end_from, -12, O4); + __ deccc(count, 4); + __ ldx(end_from, -20, O5); + __ dec(end_to, 16); + __ dec(end_from, 16); + __ srlx(O3, 32, O3); + __ sllx(O4, 32, G3); + __ bset(G3, O3); + __ stx(O3, end_to, 8); + __ srlx(O4, 32, O4); + __ sllx(O5, 32, G3); + __ bset(O4, G3); + __ stx(G3, end_to, 0); + __ brx(Assembler::greaterEqual, false, Assembler::pt, L_copy_16_bytes); + __ delayed()->mov(O5, O3); + + __ br(Assembler::always, false, Assembler::pt, L_copy_4_bytes); + __ delayed()->inc(count, 4); + + // copy 4 elements (16 bytes) at a time + __ align(16); + __ BIND(L_aligned_copy); + __ dec(end_from, 16); + __ ldx(end_from, 8, O3); + __ ldx(end_from, 0, O4); + __ dec(end_to, 16); + __ deccc(count, 4); + __ stx(O3, end_to, 8); + __ brx(Assembler::greaterEqual, false, Assembler::pt, L_aligned_copy); + __ delayed()->stx(O4, end_to, 0); + __ inc(count, 4); + + // copy 1 element (4 bytes) at a time + __ BIND(L_copy_4_bytes); + __ br_zero(Assembler::zero, false, Assembler::pt, count, L_exit); + __ delayed()->nop(); + __ BIND(L_copy_4_bytes_loop); + __ dec(end_from, 4); + __ dec(end_to, 4); + __ ld(end_from, 0, O4); + __ deccc(count); + __ brx(Assembler::greater, false, Assembler::pt, L_copy_4_bytes_loop); + __ delayed()->st(O4, end_to, 0); + __ BIND(L_exit); + } + + // + // Generate stub for conjoint int copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + address generate_conjoint_int_copy(bool aligned, const char * name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + address nooverlap_target = aligned ? + StubRoutines::arrayof_jint_disjoint_arraycopy() : + disjoint_int_copy_entry; + + assert_clean_int(O2, O3); // Make sure 'count' is clean int. + + if (!aligned) int_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + if (!aligned) BLOCK_COMMENT("Entry:"); + + array_overlap_test(nooverlap_target, 2); + + generate_conjoint_int_copy_core(aligned); + + // O3, O4 are used as temp registers + inc_counter_np(SharedRuntime::_jint_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->mov(G0, O0); // return 0 + return start; + } + + // + // Generate core code for disjoint long copy (and oop copy on 64-bit). + // "aligned" is ignored, because we must make the stronger + // assumption that both addresses are always 64-bit aligned. + // + // Arguments: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + void generate_disjoint_long_copy_core(bool aligned) { + Label L_copy_8_bytes, L_copy_16_bytes, L_exit; + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + const Register offset0 = O4; // element offset + const Register offset8 = O5; // next element offset + + __ deccc(count, 2); + __ mov(G0, offset0); // offset from start of arrays (0) + __ brx(Assembler::negative, false, Assembler::pn, L_copy_8_bytes ); + __ delayed()->add(offset0, 8, offset8); + __ align(16); + __ BIND(L_copy_16_bytes); + __ ldx(from, offset0, O3); + __ ldx(from, offset8, G3); + __ deccc(count, 2); + __ stx(O3, to, offset0); + __ inc(offset0, 16); + __ stx(G3, to, offset8); + __ brx(Assembler::greaterEqual, false, Assembler::pt, L_copy_16_bytes); + __ delayed()->inc(offset8, 16); + + __ BIND(L_copy_8_bytes); + __ inccc(count, 2); + __ brx(Assembler::zero, true, Assembler::pn, L_exit ); + __ delayed()->mov(offset0, offset8); // Set O5 used by other stubs + __ ldx(from, offset0, O3); + __ stx(O3, to, offset0); + __ BIND(L_exit); + } + + // + // Generate stub for disjoint long copy. + // "aligned" is ignored, because we must make the stronger + // assumption that both addresses are always 64-bit aligned. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + address generate_disjoint_long_copy(bool aligned, const char * name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + assert_clean_int(O2, O3); // Make sure 'count' is clean int. + + if (!aligned) disjoint_long_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + if (!aligned) BLOCK_COMMENT("Entry:"); + + generate_disjoint_long_copy_core(aligned); + + // O3, O4 are used as temp registers + inc_counter_np(SharedRuntime::_jlong_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->mov(G0, O0); // return 0 + return start; + } + + // + // Generate core code for conjoint long copy (and oop copy on 64-bit). + // "aligned" is ignored, because we must make the stronger + // assumption that both addresses are always 64-bit aligned. + // + // Arguments: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + void generate_conjoint_long_copy_core(bool aligned) { + // Do reverse copy. + Label L_copy_8_bytes, L_copy_16_bytes, L_exit; + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + const Register offset8 = O4; // element offset + const Register offset0 = O5; // previous element offset + + __ subcc(count, 1, count); + __ brx(Assembler::lessEqual, false, Assembler::pn, L_copy_8_bytes ); + __ delayed()->sllx(count, LogBytesPerLong, offset8); + __ sub(offset8, 8, offset0); + __ align(16); + __ BIND(L_copy_16_bytes); + __ ldx(from, offset8, O2); + __ ldx(from, offset0, O3); + __ stx(O2, to, offset8); + __ deccc(offset8, 16); // use offset8 as counter + __ stx(O3, to, offset0); + __ brx(Assembler::greater, false, Assembler::pt, L_copy_16_bytes); + __ delayed()->dec(offset0, 16); + + __ BIND(L_copy_8_bytes); + __ brx(Assembler::negative, false, Assembler::pn, L_exit ); + __ delayed()->nop(); + __ ldx(from, 0, O3); + __ stx(O3, to, 0); + __ BIND(L_exit); + } + + // Generate stub for conjoint long copy. + // "aligned" is ignored, because we must make the stronger + // assumption that both addresses are always 64-bit aligned. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + address generate_conjoint_long_copy(bool aligned, const char * name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + assert(!aligned, "usage"); + address nooverlap_target = disjoint_long_copy_entry; + + assert_clean_int(O2, O3); // Make sure 'count' is clean int. + + if (!aligned) long_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + if (!aligned) BLOCK_COMMENT("Entry:"); + + array_overlap_test(nooverlap_target, 3); + + generate_conjoint_long_copy_core(aligned); + + // O3, O4 are used as temp registers + inc_counter_np(SharedRuntime::_jlong_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->mov(G0, O0); // return 0 + return start; + } + + // Generate stub for disjoint oop copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + address generate_disjoint_oop_copy(bool aligned, const char * name) { + + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + assert_clean_int(count, O3); // Make sure 'count' is clean int. + + if (!aligned) disjoint_oop_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here + if (!aligned) BLOCK_COMMENT("Entry:"); + + // save arguments for barrier generation + __ mov(to, G1); + __ mov(count, G5); + gen_write_ref_array_pre_barrier(G1, G5); + #ifdef _LP64 + generate_disjoint_long_copy_core(aligned); + #else + generate_disjoint_int_copy_core(aligned); + #endif + // O0 is used as temp register + gen_write_ref_array_post_barrier(G1, G5, O0); + + // O3, O4 are used as temp registers + inc_counter_np(SharedRuntime::_oop_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->mov(G0, O0); // return 0 + return start; + } + + // Generate stub for conjoint oop copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + address generate_conjoint_oop_copy(bool aligned, const char * name) { + + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + assert_clean_int(count, O3); // Make sure 'count' is clean int. + + if (!aligned) oop_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here + if (!aligned) BLOCK_COMMENT("Entry:"); + + // save arguments for barrier generation + __ mov(to, G1); + __ mov(count, G5); + + gen_write_ref_array_pre_barrier(G1, G5); + + address nooverlap_target = aligned ? + StubRoutines::arrayof_oop_disjoint_arraycopy() : + disjoint_oop_copy_entry; + + array_overlap_test(nooverlap_target, LogBytesPerWord); + + #ifdef _LP64 + generate_conjoint_long_copy_core(aligned); + #else + generate_conjoint_int_copy_core(aligned); + #endif + + // O0 is used as temp register + gen_write_ref_array_post_barrier(G1, G5, O0); + + // O3, O4 are used as temp registers + inc_counter_np(SharedRuntime::_oop_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->mov(G0, O0); // return 0 + return start; + } + + + // Helper for generating a dynamic type check. + // Smashes only the given temp registers. + void generate_type_check(Register sub_klass, + Register super_check_offset, + Register super_klass, + Register temp, + Label& L_success, + Register deccc_hack = noreg) { + assert_different_registers(sub_klass, super_check_offset, super_klass, temp); + + BLOCK_COMMENT("type_check:"); + + Label L_miss; + + assert_clean_int(super_check_offset, temp); + + // maybe decrement caller's trip count: +#define DELAY_SLOT delayed(); \ + { if (deccc_hack == noreg) __ nop(); else __ deccc(deccc_hack); } + + // if the pointers are equal, we are done (e.g., String[] elements) + __ cmp(sub_klass, super_klass); + __ brx(Assembler::equal, true, Assembler::pt, L_success); + __ DELAY_SLOT; + + // check the supertype display: + __ ld_ptr(sub_klass, super_check_offset, temp); // query the super type + __ cmp(super_klass, temp); // test the super type + __ brx(Assembler::equal, true, Assembler::pt, L_success); + __ DELAY_SLOT; + + int sc_offset = (klassOopDesc::header_size() * HeapWordSize + + Klass::secondary_super_cache_offset_in_bytes()); + __ cmp(super_klass, sc_offset); + __ brx(Assembler::notEqual, true, Assembler::pt, L_miss); + __ delayed()->nop(); + + __ save_frame(0); + __ mov(sub_klass->after_save(), O1); + // mov(super_klass->after_save(), O2); //fill delay slot + assert(StubRoutines::Sparc::_partial_subtype_check != NULL, "order of generation"); + __ call(StubRoutines::Sparc::_partial_subtype_check); + __ delayed()->mov(super_klass->after_save(), O2); + __ restore(); + + // Upon return, the condition codes are already set. + __ brx(Assembler::equal, true, Assembler::pt, L_success); + __ DELAY_SLOT; + +#undef DELAY_SLOT + + // Fall through on failure! + __ BIND(L_miss); + } + + + // Generate stub for checked oop copy. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 treated as signed + // ckoff: O3 (super_check_offset) + // ckval: O4 (super_klass) + // ret: O0 zero for success; (-1^K) where K is partial transfer count + // + address generate_checkcast_copy(const char* name) { + + const Register O0_from = O0; // source array address + const Register O1_to = O1; // destination array address + const Register O2_count = O2; // elements count + const Register O3_ckoff = O3; // super_check_offset + const Register O4_ckval = O4; // super_klass + + const Register O5_offset = O5; // loop var, with stride wordSize + const Register G1_remain = G1; // loop var, with stride -1 + const Register G3_oop = G3; // actual oop copied + const Register G4_klass = G4; // oop._klass + const Register G5_super = G5; // oop._klass._primary_supers[ckval] + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + int klass_off = oopDesc::klass_offset_in_bytes(); + + gen_write_ref_array_pre_barrier(G1, G5); + + +#ifdef ASSERT + // We sometimes save a frame (see partial_subtype_check below). + // If this will cause trouble, let's fail now instead of later. + __ save_frame(0); + __ restore(); +#endif + +#ifdef ASSERT + // caller guarantees that the arrays really are different + // otherwise, we would have to make conjoint checks + { Label L; + __ mov(O3, G1); // spill: overlap test smashes O3 + __ mov(O4, G4); // spill: overlap test smashes O4 + array_overlap_test(L, LogBytesPerWord); + __ stop("checkcast_copy within a single array"); + __ bind(L); + __ mov(G1, O3); + __ mov(G4, O4); + } +#endif //ASSERT + + assert_clean_int(O2_count, G1); // Make sure 'count' is clean int. + + checkcast_copy_entry = __ pc(); + // caller can pass a 64-bit byte count here (from generic stub) + BLOCK_COMMENT("Entry:"); + + Label load_element, store_element, do_card_marks, fail, done; + __ addcc(O2_count, 0, G1_remain); // initialize loop index, and test it + __ brx(Assembler::notZero, false, Assembler::pt, load_element); + __ delayed()->mov(G0, O5_offset); // offset from start of arrays + + // Empty array: Nothing to do. + inc_counter_np(SharedRuntime::_checkcast_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->set(0, O0); // return 0 on (trivial) success + + // ======== begin loop ======== + // (Loop is rotated; its entry is load_element.) + // Loop variables: + // (O5 = 0; ; O5 += wordSize) --- offset from src, dest arrays + // (O2 = len; O2 != 0; O2--) --- number of oops *remaining* + // G3, G4, G5 --- current oop, oop.klass, oop.klass.super + __ align(16); + + __ bind(store_element); + // deccc(G1_remain); // decrement the count (hoisted) + __ st_ptr(G3_oop, O1_to, O5_offset); // store the oop + __ inc(O5_offset, wordSize); // step to next offset + __ brx(Assembler::zero, true, Assembler::pt, do_card_marks); + __ delayed()->set(0, O0); // return -1 on success + + // ======== loop entry is here ======== + __ bind(load_element); + __ ld_ptr(O0_from, O5_offset, G3_oop); // load the oop + __ br_null(G3_oop, true, Assembler::pt, store_element); + __ delayed()->deccc(G1_remain); // decrement the count + + __ ld_ptr(G3_oop, klass_off, G4_klass); // query the object klass + + generate_type_check(G4_klass, O3_ckoff, O4_ckval, G5_super, + // branch to this on success: + store_element, + // decrement this on success: + G1_remain); + // ======== end loop ======== + + // It was a real error; we must depend on the caller to finish the job. + // Register G1 has number of *remaining* oops, O2 number of *total* oops. + // Emit GC store barriers for the oops we have copied (O2 minus G1), + // and report their number to the caller. + __ bind(fail); + __ subcc(O2_count, G1_remain, O2_count); + __ brx(Assembler::zero, false, Assembler::pt, done); + __ delayed()->not1(O2_count, O0); // report (-1^K) to caller + + __ bind(do_card_marks); + gen_write_ref_array_post_barrier(O1_to, O2_count, O3); // store check on O1[0..O2] + + __ bind(done); + inc_counter_np(SharedRuntime::_checkcast_array_copy_ctr, O3, O4); + __ retl(); + __ delayed()->nop(); // return value in 00 + + return start; + } + + + // Generate 'unsafe' array copy stub + // Though just as safe as the other stubs, it takes an unscaled + // size_t argument instead of an element count. + // + // Arguments for generated stub: + // from: O0 + // to: O1 + // count: O2 byte count, treated as ssize_t, can be zero + // + // Examines the alignment of the operands and dispatches + // to a long, int, short, or byte copy loop. + // + address generate_unsafe_copy(const char* name) { + + const Register O0_from = O0; // source array address + const Register O1_to = O1; // destination array address + const Register O2_count = O2; // elements count + + const Register G1_bits = G1; // test copy of low bits + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + // bump this on entry, not on exit: + inc_counter_np(SharedRuntime::_unsafe_array_copy_ctr, G1, G3); + + __ or3(O0_from, O1_to, G1_bits); + __ or3(O2_count, G1_bits, G1_bits); + + __ btst(BytesPerLong-1, G1_bits); + __ br(Assembler::zero, true, Assembler::pt, + long_copy_entry, relocInfo::runtime_call_type); + // scale the count on the way out: + __ delayed()->srax(O2_count, LogBytesPerLong, O2_count); + + __ btst(BytesPerInt-1, G1_bits); + __ br(Assembler::zero, true, Assembler::pt, + int_copy_entry, relocInfo::runtime_call_type); + // scale the count on the way out: + __ delayed()->srax(O2_count, LogBytesPerInt, O2_count); + + __ btst(BytesPerShort-1, G1_bits); + __ br(Assembler::zero, true, Assembler::pt, + short_copy_entry, relocInfo::runtime_call_type); + // scale the count on the way out: + __ delayed()->srax(O2_count, LogBytesPerShort, O2_count); + + __ br(Assembler::always, false, Assembler::pt, + byte_copy_entry, relocInfo::runtime_call_type); + __ delayed()->nop(); + + return start; + } + + + // Perform range checks on the proposed arraycopy. + // Kills the two temps, but nothing else. + // Also, clean the sign bits of src_pos and dst_pos. + void arraycopy_range_checks(Register src, // source array oop (O0) + Register src_pos, // source position (O1) + Register dst, // destination array oo (O2) + Register dst_pos, // destination position (O3) + Register length, // length of copy (O4) + Register temp1, Register temp2, + Label& L_failed) { + BLOCK_COMMENT("arraycopy_range_checks:"); + + // if (src_pos + length > arrayOop(src)->length() ) FAIL; + + const Register array_length = temp1; // scratch + const Register end_pos = temp2; // scratch + + // Note: This next instruction may be in the delay slot of a branch: + __ add(length, src_pos, end_pos); // src_pos + length + __ lduw(src, arrayOopDesc::length_offset_in_bytes(), array_length); + __ cmp(end_pos, array_length); + __ br(Assembler::greater, false, Assembler::pn, L_failed); + + // if (dst_pos + length > arrayOop(dst)->length() ) FAIL; + __ delayed()->add(length, dst_pos, end_pos); // dst_pos + length + __ lduw(dst, arrayOopDesc::length_offset_in_bytes(), array_length); + __ cmp(end_pos, array_length); + __ br(Assembler::greater, false, Assembler::pn, L_failed); + + // Have to clean up high 32-bits of 'src_pos' and 'dst_pos'. + // Move with sign extension can be used since they are positive. + __ delayed()->signx(src_pos, src_pos); + __ signx(dst_pos, dst_pos); + + BLOCK_COMMENT("arraycopy_range_checks done"); + } + + + // + // Generate generic array copy stubs + // + // Input: + // O0 - src oop + // O1 - src_pos + // O2 - dst oop + // O3 - dst_pos + // O4 - element count + // + // Output: + // O0 == 0 - success + // O0 == -1 - need to call System.arraycopy + // + address generate_generic_copy(const char *name) { + + Label L_failed, L_objArray; + + // Input registers + const Register src = O0; // source array oop + const Register src_pos = O1; // source position + const Register dst = O2; // destination array oop + const Register dst_pos = O3; // destination position + const Register length = O4; // elements count + + // registers used as temp + const Register G3_src_klass = G3; // source array klass + const Register G4_dst_klass = G4; // destination array klass + const Register G5_lh = G5; // layout handler + const Register O5_temp = O5; + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + // bump this on entry, not on exit: + inc_counter_np(SharedRuntime::_generic_array_copy_ctr, G1, G3); + + // In principle, the int arguments could be dirty. + //assert_clean_int(src_pos, G1); + //assert_clean_int(dst_pos, G1); + //assert_clean_int(length, G1); + + //----------------------------------------------------------------------- + // Assembler stubs will be used for this call to arraycopy + // if the following conditions are met: + // + // (1) src and dst must not be null. + // (2) src_pos must not be negative. + // (3) dst_pos must not be negative. + // (4) length must not be negative. + // (5) src klass and dst klass should be the same and not NULL. + // (6) src and dst should be arrays. + // (7) src_pos + length must not exceed length of src. + // (8) dst_pos + length must not exceed length of dst. + BLOCK_COMMENT("arraycopy initial argument checks"); + + // if (src == NULL) return -1; + __ br_null(src, false, Assembler::pn, L_failed); + + // if (src_pos < 0) return -1; + __ delayed()->tst(src_pos); + __ br(Assembler::negative, false, Assembler::pn, L_failed); + __ delayed()->nop(); + + // if (dst == NULL) return -1; + __ br_null(dst, false, Assembler::pn, L_failed); + + // if (dst_pos < 0) return -1; + __ delayed()->tst(dst_pos); + __ br(Assembler::negative, false, Assembler::pn, L_failed); + + // if (length < 0) return -1; + __ delayed()->tst(length); + __ br(Assembler::negative, false, Assembler::pn, L_failed); + + BLOCK_COMMENT("arraycopy argument klass checks"); + // get src->klass() + __ delayed()->ld_ptr(src, oopDesc::klass_offset_in_bytes(), G3_src_klass); + +#ifdef ASSERT + // assert(src->klass() != NULL); + BLOCK_COMMENT("assert klasses not null"); + { Label L_a, L_b; + __ br_notnull(G3_src_klass, false, Assembler::pt, L_b); // it is broken if klass is NULL + __ delayed()->ld_ptr(dst, oopDesc::klass_offset_in_bytes(), G4_dst_klass); + __ bind(L_a); + __ stop("broken null klass"); + __ bind(L_b); + __ br_null(G4_dst_klass, false, Assembler::pn, L_a); // this would be broken also + __ delayed()->mov(G0, G4_dst_klass); // scribble the temp + BLOCK_COMMENT("assert done"); + } +#endif + + // Load layout helper + // + // |array_tag| | header_size | element_type | |log2_element_size| + // 32 30 24 16 8 2 0 + // + // array_tag: typeArray = 0x3, objArray = 0x2, non-array = 0x0 + // + + int lh_offset = klassOopDesc::header_size() * HeapWordSize + + Klass::layout_helper_offset_in_bytes(); + + // Load 32-bits signed value. Use br() instruction with it to check icc. + __ lduw(G3_src_klass, lh_offset, G5_lh); + + // Handle objArrays completely differently... + juint objArray_lh = Klass::array_layout_helper(T_OBJECT); + __ set(objArray_lh, O5_temp); + __ cmp(G5_lh, O5_temp); + __ br(Assembler::equal, false, Assembler::pt, L_objArray); + __ delayed()->ld_ptr(dst, oopDesc::klass_offset_in_bytes(), G4_dst_klass); + + // if (src->klass() != dst->klass()) return -1; + __ cmp(G3_src_klass, G4_dst_klass); + __ brx(Assembler::notEqual, false, Assembler::pn, L_failed); + __ delayed()->nop(); + + // if (!src->is_Array()) return -1; + __ cmp(G5_lh, Klass::_lh_neutral_value); // < 0 + __ br(Assembler::greaterEqual, false, Assembler::pn, L_failed); + + // At this point, it is known to be a typeArray (array_tag 0x3). +#ifdef ASSERT + __ delayed()->nop(); + { Label L; + jint lh_prim_tag_in_place = (Klass::_lh_array_tag_type_value << Klass::_lh_array_tag_shift); + __ set(lh_prim_tag_in_place, O5_temp); + __ cmp(G5_lh, O5_temp); + __ br(Assembler::greaterEqual, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("must be a primitive array"); + __ bind(L); + } +#else + __ delayed(); // match next insn to prev branch +#endif + + arraycopy_range_checks(src, src_pos, dst, dst_pos, length, + O5_temp, G4_dst_klass, L_failed); + + // typeArrayKlass + // + // src_addr = (src + array_header_in_bytes()) + (src_pos << log2elemsize); + // dst_addr = (dst + array_header_in_bytes()) + (dst_pos << log2elemsize); + // + + const Register G4_offset = G4_dst_klass; // array offset + const Register G3_elsize = G3_src_klass; // log2 element size + + __ srl(G5_lh, Klass::_lh_header_size_shift, G4_offset); + __ and3(G4_offset, Klass::_lh_header_size_mask, G4_offset); // array_offset + __ add(src, G4_offset, src); // src array offset + __ add(dst, G4_offset, dst); // dst array offset + __ and3(G5_lh, Klass::_lh_log2_element_size_mask, G3_elsize); // log2 element size + + // next registers should be set before the jump to corresponding stub + const Register from = O0; // source array address + const Register to = O1; // destination array address + const Register count = O2; // elements count + + // 'from', 'to', 'count' registers should be set in this order + // since they are the same as 'src', 'src_pos', 'dst'. + + BLOCK_COMMENT("scale indexes to element size"); + __ sll_ptr(src_pos, G3_elsize, src_pos); + __ sll_ptr(dst_pos, G3_elsize, dst_pos); + __ add(src, src_pos, from); // src_addr + __ add(dst, dst_pos, to); // dst_addr + + BLOCK_COMMENT("choose copy loop based on element size"); + __ cmp(G3_elsize, 0); + __ br(Assembler::equal,true,Assembler::pt,StubRoutines::_jbyte_arraycopy); + __ delayed()->signx(length, count); // length + + __ cmp(G3_elsize, LogBytesPerShort); + __ br(Assembler::equal,true,Assembler::pt,StubRoutines::_jshort_arraycopy); + __ delayed()->signx(length, count); // length + + __ cmp(G3_elsize, LogBytesPerInt); + __ br(Assembler::equal,true,Assembler::pt,StubRoutines::_jint_arraycopy); + __ delayed()->signx(length, count); // length +#ifdef ASSERT + { Label L; + __ cmp(G3_elsize, LogBytesPerLong); + __ br(Assembler::equal, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("must be long copy, but elsize is wrong"); + __ bind(L); + } +#endif + __ br(Assembler::always,false,Assembler::pt,StubRoutines::_jlong_arraycopy); + __ delayed()->signx(length, count); // length + + // objArrayKlass + __ BIND(L_objArray); + // live at this point: G3_src_klass, G4_dst_klass, src[_pos], dst[_pos], length + + Label L_plain_copy, L_checkcast_copy; + // test array classes for subtyping + __ cmp(G3_src_klass, G4_dst_klass); // usual case is exact equality + __ brx(Assembler::notEqual, true, Assembler::pn, L_checkcast_copy); + __ delayed()->lduw(G4_dst_klass, lh_offset, O5_temp); // hoisted from below + + // Identically typed arrays can be copied without element-wise checks. + arraycopy_range_checks(src, src_pos, dst, dst_pos, length, + O5_temp, G5_lh, L_failed); + + __ add(src, arrayOopDesc::base_offset_in_bytes(T_OBJECT), src); //src offset + __ add(dst, arrayOopDesc::base_offset_in_bytes(T_OBJECT), dst); //dst offset + __ sll_ptr(src_pos, LogBytesPerOop, src_pos); + __ sll_ptr(dst_pos, LogBytesPerOop, dst_pos); + __ add(src, src_pos, from); // src_addr + __ add(dst, dst_pos, to); // dst_addr + __ BIND(L_plain_copy); + __ br(Assembler::always, false, Assembler::pt,StubRoutines::_oop_arraycopy); + __ delayed()->signx(length, count); // length + + __ BIND(L_checkcast_copy); + // live at this point: G3_src_klass, G4_dst_klass + { + // Before looking at dst.length, make sure dst is also an objArray. + // lduw(G4_dst_klass, lh_offset, O5_temp); // hoisted to delay slot + __ cmp(G5_lh, O5_temp); + __ br(Assembler::notEqual, false, Assembler::pn, L_failed); + + // It is safe to examine both src.length and dst.length. + __ delayed(); // match next insn to prev branch + arraycopy_range_checks(src, src_pos, dst, dst_pos, length, + O5_temp, G5_lh, L_failed); + + // Marshal the base address arguments now, freeing registers. + __ add(src, arrayOopDesc::base_offset_in_bytes(T_OBJECT), src); //src offset + __ add(dst, arrayOopDesc::base_offset_in_bytes(T_OBJECT), dst); //dst offset + __ sll_ptr(src_pos, LogBytesPerOop, src_pos); + __ sll_ptr(dst_pos, LogBytesPerOop, dst_pos); + __ add(src, src_pos, from); // src_addr + __ add(dst, dst_pos, to); // dst_addr + __ signx(length, count); // length (reloaded) + + Register sco_temp = O3; // this register is free now + assert_different_registers(from, to, count, sco_temp, + G4_dst_klass, G3_src_klass); + + // Generate the type check. + int sco_offset = (klassOopDesc::header_size() * HeapWordSize + + Klass::super_check_offset_offset_in_bytes()); + __ lduw(G4_dst_klass, sco_offset, sco_temp); + generate_type_check(G3_src_klass, sco_temp, G4_dst_klass, + O5_temp, L_plain_copy); + + // Fetch destination element klass from the objArrayKlass header. + int ek_offset = (klassOopDesc::header_size() * HeapWordSize + + objArrayKlass::element_klass_offset_in_bytes()); + + // the checkcast_copy loop needs two extra arguments: + __ ld_ptr(G4_dst_klass, ek_offset, O4); // dest elem klass + // lduw(O4, sco_offset, O3); // sco of elem klass + + __ br(Assembler::always, false, Assembler::pt, checkcast_copy_entry); + __ delayed()->lduw(O4, sco_offset, O3); + } + + __ BIND(L_failed); + __ retl(); + __ delayed()->sub(G0, 1, O0); // return -1 + return start; + } + + void generate_arraycopy_stubs() { + + // Note: the disjoint stubs must be generated first, some of + // the conjoint stubs use them. + StubRoutines::_jbyte_disjoint_arraycopy = generate_disjoint_byte_copy(false, "jbyte_disjoint_arraycopy"); + StubRoutines::_jshort_disjoint_arraycopy = generate_disjoint_short_copy(false, "jshort_disjoint_arraycopy"); + StubRoutines::_jint_disjoint_arraycopy = generate_disjoint_int_copy(false, "jint_disjoint_arraycopy"); + StubRoutines::_jlong_disjoint_arraycopy = generate_disjoint_long_copy(false, "jlong_disjoint_arraycopy"); + StubRoutines::_oop_disjoint_arraycopy = generate_disjoint_oop_copy(false, "oop_disjoint_arraycopy"); + StubRoutines::_arrayof_jbyte_disjoint_arraycopy = generate_disjoint_byte_copy(true, "arrayof_jbyte_disjoint_arraycopy"); + StubRoutines::_arrayof_jshort_disjoint_arraycopy = generate_disjoint_short_copy(true, "arrayof_jshort_disjoint_arraycopy"); + StubRoutines::_arrayof_jint_disjoint_arraycopy = generate_disjoint_int_copy(true, "arrayof_jint_disjoint_arraycopy"); + StubRoutines::_arrayof_jlong_disjoint_arraycopy = generate_disjoint_long_copy(true, "arrayof_jlong_disjoint_arraycopy"); + StubRoutines::_arrayof_oop_disjoint_arraycopy = generate_disjoint_oop_copy(true, "arrayof_oop_disjoint_arraycopy"); + + StubRoutines::_jbyte_arraycopy = generate_conjoint_byte_copy(false, "jbyte_arraycopy"); + StubRoutines::_jshort_arraycopy = generate_conjoint_short_copy(false, "jshort_arraycopy"); + StubRoutines::_jint_arraycopy = generate_conjoint_int_copy(false, "jint_arraycopy"); + StubRoutines::_jlong_arraycopy = generate_conjoint_long_copy(false, "jlong_arraycopy"); + StubRoutines::_oop_arraycopy = generate_conjoint_oop_copy(false, "oop_arraycopy"); + StubRoutines::_arrayof_jbyte_arraycopy = generate_conjoint_byte_copy(true, "arrayof_jbyte_arraycopy"); + StubRoutines::_arrayof_jshort_arraycopy = generate_conjoint_short_copy(true, "arrayof_jshort_arraycopy"); +#ifdef _LP64 + // since sizeof(jint) < sizeof(HeapWord), there's a different flavor: + StubRoutines::_arrayof_jint_arraycopy = generate_conjoint_int_copy(true, "arrayof_jint_arraycopy"); + #else + StubRoutines::_arrayof_jint_arraycopy = StubRoutines::_jint_arraycopy; +#endif + StubRoutines::_arrayof_jlong_arraycopy = StubRoutines::_jlong_arraycopy; + StubRoutines::_arrayof_oop_arraycopy = StubRoutines::_oop_arraycopy; + + StubRoutines::_checkcast_arraycopy = generate_checkcast_copy("checkcast_arraycopy"); + StubRoutines::_unsafe_arraycopy = generate_unsafe_copy("unsafe_arraycopy"); + StubRoutines::_generic_arraycopy = generate_generic_copy("generic_arraycopy"); + } + + void generate_initial() { + // Generates all stubs and initializes the entry points + + //------------------------------------------------------------------------------------------------------------------------ + // entry points that exist in all platforms + // Note: This is code that could be shared among different platforms - however the benefit seems to be smaller than + // the disadvantage of having a much more complicated generator structure. See also comment in stubRoutines.hpp. + StubRoutines::_forward_exception_entry = generate_forward_exception(); + + StubRoutines::_call_stub_entry = generate_call_stub(StubRoutines::_call_stub_return_address); + StubRoutines::_catch_exception_entry = generate_catch_exception(); + + //------------------------------------------------------------------------------------------------------------------------ + // entry points that are platform specific + StubRoutines::Sparc::_test_stop_entry = generate_test_stop(); + + StubRoutines::Sparc::_stop_subroutine_entry = generate_stop_subroutine(); + StubRoutines::Sparc::_flush_callers_register_windows_entry = generate_flush_callers_register_windows(); + +#if !defined(COMPILER2) && !defined(_LP64) + StubRoutines::_atomic_xchg_entry = generate_atomic_xchg(); + StubRoutines::_atomic_cmpxchg_entry = generate_atomic_cmpxchg(); + StubRoutines::_atomic_add_entry = generate_atomic_add(); + StubRoutines::_atomic_xchg_ptr_entry = StubRoutines::_atomic_xchg_entry; + StubRoutines::_atomic_cmpxchg_ptr_entry = StubRoutines::_atomic_cmpxchg_entry; + StubRoutines::_atomic_cmpxchg_long_entry = generate_atomic_cmpxchg_long(); + StubRoutines::_atomic_add_ptr_entry = StubRoutines::_atomic_add_entry; + StubRoutines::_fence_entry = generate_fence(); +#endif // COMPILER2 !=> _LP64 + + StubRoutines::Sparc::_partial_subtype_check = generate_partial_subtype_check(); + } + + + void generate_all() { + // Generates all stubs and initializes the entry points + + // These entry points require SharedInfo::stack0 to be set up in non-core builds + StubRoutines::_throw_AbstractMethodError_entry = generate_throw_exception("AbstractMethodError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_AbstractMethodError), false); + StubRoutines::_throw_ArithmeticException_entry = generate_throw_exception("ArithmeticException throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_ArithmeticException), true); + StubRoutines::_throw_NullPointerException_entry = generate_throw_exception("NullPointerException throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException), true); + StubRoutines::_throw_NullPointerException_at_call_entry= generate_throw_exception("NullPointerException at call throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException_at_call), false); + StubRoutines::_throw_StackOverflowError_entry = generate_throw_exception("StackOverflowError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_StackOverflowError), false); + + StubRoutines::_handler_for_unsafe_access_entry = + generate_handler_for_unsafe_access(); + + // support for verify_oop (must happen after universe_init) + StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop_subroutine(); + + // arraycopy stubs used by compilers + generate_arraycopy_stubs(); + } + + + public: + StubGenerator(CodeBuffer* code, bool all) : StubCodeGenerator(code) { + // replace the standard masm with a special one: + _masm = new MacroAssembler(code); + + _stub_count = !all ? 0x100 : 0x200; + if (all) { + generate_all(); + } else { + generate_initial(); + } + + // make sure this stub is available for all local calls + if (_atomic_add_stub.is_unbound()) { + // generate a second time, if necessary + (void) generate_atomic_add(); + } + } + + + private: + int _stub_count; + void stub_prolog(StubCodeDesc* cdesc) { + # ifdef ASSERT + // put extra information in the stub code, to make it more readable +#ifdef _LP64 +// Write the high part of the address +// [RGV] Check if there is a dependency on the size of this prolog + __ emit_data((intptr_t)cdesc >> 32, relocInfo::none); +#endif + __ emit_data((intptr_t)cdesc, relocInfo::none); + __ emit_data(++_stub_count, relocInfo::none); + # endif + align(true); + } + + void align(bool at_header = false) { + // %%%%% move this constant somewhere else + // UltraSPARC cache line size is 8 instructions: + const unsigned int icache_line_size = 32; + const unsigned int icache_half_line_size = 16; + + if (at_header) { + while ((intptr_t)(__ pc()) % icache_line_size != 0) { + __ emit_data(0, relocInfo::none); + } + } else { + while ((intptr_t)(__ pc()) % icache_half_line_size != 0) { + __ nop(); + } + } + } + +}; // end class declaration + + +address StubGenerator::disjoint_byte_copy_entry = NULL; +address StubGenerator::disjoint_short_copy_entry = NULL; +address StubGenerator::disjoint_int_copy_entry = NULL; +address StubGenerator::disjoint_long_copy_entry = NULL; +address StubGenerator::disjoint_oop_copy_entry = NULL; + +address StubGenerator::byte_copy_entry = NULL; +address StubGenerator::short_copy_entry = NULL; +address StubGenerator::int_copy_entry = NULL; +address StubGenerator::long_copy_entry = NULL; +address StubGenerator::oop_copy_entry = NULL; + +address StubGenerator::checkcast_copy_entry = NULL; + +void StubGenerator_generate(CodeBuffer* code, bool all) { + StubGenerator g(code, all); +} diff --git a/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.cpp b/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.cpp new file mode 100644 index 00000000000..dea384daf80 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubRoutines_sparc.cpp.incl" + +// Implementation of the platform-specific part of StubRoutines - for +// a description of how to extend it, see the stubRoutines.hpp file. + + +extern "C" { + address _flush_reg_windows(); // in .s file. + // Flush registers to stack. In case of error we will need to stack walk. + address bootstrap_flush_windows(void) { + Thread* thread = ThreadLocalStorage::get_thread_slow(); + // Very early in process there is no thread. + if (thread != NULL) { + guarantee(thread->is_Java_thread(), "Not a Java thread."); + JavaThread* jt = (JavaThread*)thread; + guarantee(!jt->has_last_Java_frame(), "Must be able to flush registers!"); + } + return (address)_flush_reg_windows(); + }; +}; + +address StubRoutines::Sparc::_test_stop_entry = NULL; +address StubRoutines::Sparc::_stop_subroutine_entry = NULL; +address StubRoutines::Sparc::_flush_callers_register_windows_entry = CAST_FROM_FN_PTR(address, bootstrap_flush_windows); + +address StubRoutines::Sparc::_partial_subtype_check = NULL; + +int StubRoutines::Sparc::_atomic_memory_operation_lock = StubRoutines::Sparc::unlocked; + +int StubRoutines::Sparc::_v8_oop_lock_cache[StubRoutines::Sparc::nof_v8_oop_lock_cache_entries]; diff --git a/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.hpp b/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.hpp new file mode 100644 index 00000000000..6b23258ffa2 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.hpp @@ -0,0 +1,97 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds the platform specific parts of the StubRoutines +// definition. See stubRoutines.hpp for a description on how to +// extend it. + + +// So unfortunately c2 will call with a pc from a frame object +// (already adjusted) and a raw pc (unadjusted), so we need to check both. +// It didn't use to be like this before adapter removal. +static bool returns_to_call_stub(address return_pc) { + return ((return_pc + frame::pc_return_offset) == _call_stub_return_address) || + (return_pc == _call_stub_return_address ); +} + +enum /* platform_dependent_constants */ { + // %%%%%%%% May be able to shrink this a lot + code_size1 = 20000, // simply increase if too small (assembler will crash if too small) + code_size2 = 20000 // simply increase if too small (assembler will crash if too small) +}; + +class Sparc { + friend class StubGenerator; + + public: + enum { nof_instance_allocators = 10 }; + + // allocator lock values + enum { + unlocked = 0, + locked = 1 + }; + + enum { + v8_oop_lock_ignore_bits = 2, + v8_oop_lock_bits = 4, + nof_v8_oop_lock_cache_entries = 1 << (v8_oop_lock_bits+v8_oop_lock_ignore_bits), + v8_oop_lock_mask = right_n_bits(v8_oop_lock_bits), + v8_oop_lock_mask_in_place = v8_oop_lock_mask << v8_oop_lock_ignore_bits + }; + + static int _v8_oop_lock_cache[nof_v8_oop_lock_cache_entries]; + + private: + static address _test_stop_entry; + static address _stop_subroutine_entry; + static address _flush_callers_register_windows_entry; + + static int _atomic_memory_operation_lock; + + static address _partial_subtype_check; + + public: + // %%% global lock for everyone who needs to use atomic_compare_and_exchange + // %%% or atomic_increment -- should probably use more locks for more + // %%% scalability-- for instance one for each eden space or group of + + // address of the lock for atomic_compare_and_exchange + static int* atomic_memory_operation_lock_addr() { return &_atomic_memory_operation_lock; } + + // accessor and mutator for _atomic_memory_operation_lock + static int atomic_memory_operation_lock() { return _atomic_memory_operation_lock; } + static void set_atomic_memory_operation_lock(int value) { _atomic_memory_operation_lock = value; } + + // test assembler stop routine by setting registers + static void (*test_stop_entry()) () { return CAST_TO_FN_PTR(void (*)(void), _test_stop_entry); } + + // a subroutine for debugging assembler code + static address stop_subroutine_entry_address() { return (address)&_stop_subroutine_entry; } + + // flushes (all but current) register window + static intptr_t* (*flush_callers_register_windows_func())() { return CAST_TO_FN_PTR(intptr_t* (*)(void), _flush_callers_register_windows_entry); } + + static address partial_subtype_check() { return _partial_subtype_check; } +}; diff --git a/hotspot/src/cpu/sparc/vm/templateInterpreterGenerator_sparc.hpp b/hotspot/src/cpu/sparc/vm/templateInterpreterGenerator_sparc.hpp new file mode 100644 index 00000000000..d6d72f4eb7c --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/templateInterpreterGenerator_sparc.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + protected: + + void generate_fixed_frame(bool native_call); // template interpreter only + void generate_stack_overflow_check(Register Rframe_size, Register Rscratch, + Register Rscratch2); diff --git a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp new file mode 100644 index 00000000000..a840f3e6807 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp @@ -0,0 +1,1965 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_templateInterpreter_sparc.cpp.incl" + +#ifndef CC_INTERP +#ifndef FAST_DISPATCH +#define FAST_DISPATCH 1 +#endif +#undef FAST_DISPATCH + + +// Generation of Interpreter +// +// The InterpreterGenerator generates the interpreter into Interpreter::_code. + + +#define __ _masm-> + + +//---------------------------------------------------------------------------------------------------- + + +void InterpreterGenerator::save_native_result(void) { + // result potentially in O0/O1: save it across calls + const Address& l_tmp = InterpreterMacroAssembler::l_tmp; + + // result potentially in F0/F1: save it across calls + const Address& d_tmp = InterpreterMacroAssembler::d_tmp; + + // save and restore any potential method result value around the unlocking operation + __ stf(FloatRegisterImpl::D, F0, d_tmp); +#ifdef _LP64 + __ stx(O0, l_tmp); +#else + __ std(O0, l_tmp); +#endif +} + +void InterpreterGenerator::restore_native_result(void) { + const Address& l_tmp = InterpreterMacroAssembler::l_tmp; + const Address& d_tmp = InterpreterMacroAssembler::d_tmp; + + // Restore any method result value + __ ldf(FloatRegisterImpl::D, d_tmp, F0); +#ifdef _LP64 + __ ldx(l_tmp, O0); +#else + __ ldd(l_tmp, O0); +#endif +} + +address TemplateInterpreterGenerator::generate_exception_handler_common(const char* name, const char* message, bool pass_oop) { + assert(!pass_oop || message == NULL, "either oop or message but not both"); + address entry = __ pc(); + // expression stack must be empty before entering the VM if an exception happened + __ empty_expression_stack(); + // load exception object + __ set((intptr_t)name, G3_scratch); + if (pass_oop) { + __ call_VM(Oexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::create_klass_exception), G3_scratch, Otos_i); + } else { + __ set((intptr_t)message, G4_scratch); + __ call_VM(Oexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::create_exception), G3_scratch, G4_scratch); + } + // throw exception + assert(Interpreter::throw_exception_entry() != NULL, "generate it first"); + Address thrower(G3_scratch, Interpreter::throw_exception_entry()); + __ jump_to (thrower); + __ delayed()->nop(); + return entry; +} + +address TemplateInterpreterGenerator::generate_ClassCastException_handler() { + address entry = __ pc(); + // expression stack must be empty before entering the VM if an exception + // happened + __ empty_expression_stack(); + // load exception object + __ call_VM(Oexception, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_ClassCastException), + Otos_i); + __ should_not_reach_here(); + return entry; +} + + +address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler(const char* name) { + address entry = __ pc(); + // expression stack must be empty before entering the VM if an exception happened + __ empty_expression_stack(); + // convention: expect aberrant index in register G3_scratch, then shuffle the + // index to G4_scratch for the VM call + __ mov(G3_scratch, G4_scratch); + __ set((intptr_t)name, G3_scratch); + __ call_VM(Oexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException), G3_scratch, G4_scratch); + __ should_not_reach_here(); + return entry; +} + + +address TemplateInterpreterGenerator::generate_StackOverflowError_handler() { + address entry = __ pc(); + // expression stack must be empty before entering the VM if an exception happened + __ empty_expression_stack(); + __ call_VM(Oexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_StackOverflowError)); + __ should_not_reach_here(); + return entry; +} + + +address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step) { + address compiled_entry = __ pc(); + Label cont; + + address entry = __ pc(); +#if !defined(_LP64) && defined(COMPILER2) + // All return values are where we want them, except for Longs. C2 returns + // longs in G1 in the 32-bit build whereas the interpreter wants them in O0/O1. + // Since the interpreter will return longs in G1 and O0/O1 in the 32bit + // build even if we are returning from interpreted we just do a little + // stupid shuffing. + // Note: I tried to make c2 return longs in O0/O1 and G1 so we wouldn't have to + // do this here. Unfortunately if we did a rethrow we'd see an machepilog node + // first which would move g1 -> O0/O1 and destroy the exception we were throwing. + + if( state == ltos ) { + __ srl (G1, 0,O1); + __ srlx(G1,32,O0); + } +#endif /* !_LP64 && COMPILER2 */ + + + __ bind(cont); + + // The callee returns with the stack possibly adjusted by adapter transition + // We remove that possible adjustment here. + // All interpreter local registers are untouched. Any result is passed back + // in the O0/O1 or float registers. Before continuing, the arguments must be + // popped from the java expression stack; i.e., Lesp must be adjusted. + + __ mov(Llast_SP, SP); // Remove any adapter added stack space. + + + const Register cache = G3_scratch; + const Register size = G1_scratch; + __ get_cache_and_index_at_bcp(cache, G1_scratch, 1); + __ ld_ptr(Address(cache, 0, in_bytes(constantPoolCacheOopDesc::base_offset()) + + in_bytes(ConstantPoolCacheEntry::flags_offset())), size); + __ and3(size, 0xFF, size); // argument size in words + __ sll(size, Interpreter::logStackElementSize(), size); // each argument size in bytes + __ add(Lesp, size, Lesp); // pop arguments + __ dispatch_next(state, step); + + return entry; +} + + +address TemplateInterpreterGenerator::generate_deopt_entry_for(TosState state, int step) { + address entry = __ pc(); + __ get_constant_pool_cache(LcpoolCache); // load LcpoolCache + { Label L; + Address exception_addr (G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + + __ ld_ptr(exception_addr, Gtemp); + __ tst(Gtemp); + __ brx(Assembler::equal, false, Assembler::pt, L); + __ delayed()->nop(); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_pending_exception)); + __ should_not_reach_here(); + __ bind(L); + } + __ dispatch_next(state, step); + return entry; +} + +// A result handler converts/unboxes a native call result into +// a java interpreter/compiler result. The current frame is an +// interpreter frame. The activation frame unwind code must be +// consistent with that of TemplateTable::_return(...). In the +// case of native methods, the caller's SP was not modified. +address TemplateInterpreterGenerator::generate_result_handler_for(BasicType type) { + address entry = __ pc(); + Register Itos_i = Otos_i ->after_save(); + Register Itos_l = Otos_l ->after_save(); + Register Itos_l1 = Otos_l1->after_save(); + Register Itos_l2 = Otos_l2->after_save(); + switch (type) { + case T_BOOLEAN: __ subcc(G0, O0, G0); __ addc(G0, 0, Itos_i); break; // !0 => true; 0 => false + case T_CHAR : __ sll(O0, 16, O0); __ srl(O0, 16, Itos_i); break; // cannot use and3, 0xFFFF too big as immediate value! + case T_BYTE : __ sll(O0, 24, O0); __ sra(O0, 24, Itos_i); break; + case T_SHORT : __ sll(O0, 16, O0); __ sra(O0, 16, Itos_i); break; + case T_LONG : +#ifndef _LP64 + __ mov(O1, Itos_l2); // move other half of long +#endif // ifdef or no ifdef, fall through to the T_INT case + case T_INT : __ mov(O0, Itos_i); break; + case T_VOID : /* nothing to do */ break; + case T_FLOAT : assert(F0 == Ftos_f, "fix this code" ); break; + case T_DOUBLE : assert(F0 == Ftos_d, "fix this code" ); break; + case T_OBJECT : + __ ld_ptr(FP, (frame::interpreter_frame_oop_temp_offset*wordSize) + STACK_BIAS, Itos_i); + __ verify_oop(Itos_i); + break; + default : ShouldNotReachHere(); + } + __ ret(); // return from interpreter activation + __ delayed()->restore(I5_savedSP, G0, SP); // remove interpreter frame + NOT_PRODUCT(__ emit_long(0);) // marker for disassembly + return entry; +} + +address TemplateInterpreterGenerator::generate_safept_entry_for(TosState state, address runtime_entry) { + address entry = __ pc(); + __ push(state); + __ call_VM(noreg, runtime_entry); + __ dispatch_via(vtos, Interpreter::normal_table(vtos)); + return entry; +} + + +address TemplateInterpreterGenerator::generate_continuation_for(TosState state) { + address entry = __ pc(); + __ dispatch_next(state); + return entry; +} + +// +// Helpers for commoning out cases in the various type of method entries. +// + +// increment invocation count & check for overflow +// +// Note: checking for negative value instead of overflow +// so we have a 'sticky' overflow test +// +// Lmethod: method +// ??: invocation counter +// +void InterpreterGenerator::generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue) { + // Update standard invocation counters + __ increment_invocation_counter(O0, G3_scratch); + if (ProfileInterpreter) { // %%% Merge this into methodDataOop + Address interpreter_invocation_counter(Lmethod, 0, in_bytes(methodOopDesc::interpreter_invocation_counter_offset())); + __ ld(interpreter_invocation_counter, G3_scratch); + __ inc(G3_scratch); + __ st(G3_scratch, interpreter_invocation_counter); + } + + if (ProfileInterpreter && profile_method != NULL) { + // Test to see if we should create a method data oop + Address profile_limit(G3_scratch, (address)&InvocationCounter::InterpreterProfileLimit); + __ sethi(profile_limit); + __ ld(profile_limit, G3_scratch); + __ cmp(O0, G3_scratch); + __ br(Assembler::lessUnsigned, false, Assembler::pn, *profile_method_continue); + __ delayed()->nop(); + + // if no method data exists, go to profile_method + __ test_method_data_pointer(*profile_method); + } + + Address invocation_limit(G3_scratch, (address)&InvocationCounter::InterpreterInvocationLimit); + __ sethi(invocation_limit); + __ ld(invocation_limit, G3_scratch); + __ cmp(O0, G3_scratch); + __ br(Assembler::greaterEqualUnsigned, false, Assembler::pn, *overflow); + __ delayed()->nop(); + +} + +// Allocate monitor and lock method (asm interpreter) +// ebx - methodOop +// +void InterpreterGenerator::lock_method(void) { + const Address access_flags (Lmethod, 0, in_bytes(methodOopDesc::access_flags_offset())); + __ ld(access_flags, O0); + +#ifdef ASSERT + { Label ok; + __ btst(JVM_ACC_SYNCHRONIZED, O0); + __ br( Assembler::notZero, false, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("method doesn't need synchronization"); + __ bind(ok); + } +#endif // ASSERT + + // get synchronization object to O0 + { Label done; + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + Klass::java_mirror_offset_in_bytes(); + __ btst(JVM_ACC_STATIC, O0); + __ br( Assembler::zero, true, Assembler::pt, done); + __ delayed()->ld_ptr(Llocals, Interpreter::local_offset_in_bytes(0), O0); // get receiver for not-static case + + __ ld_ptr( Lmethod, in_bytes(methodOopDesc::constants_offset()), O0); + __ ld_ptr( O0, constantPoolOopDesc::pool_holder_offset_in_bytes(), O0); + + // lock the mirror, not the klassOop + __ ld_ptr( O0, mirror_offset, O0); + +#ifdef ASSERT + __ tst(O0); + __ breakpoint_trap(Assembler::zero); +#endif // ASSERT + + __ bind(done); + } + + __ add_monitor_to_stack(true, noreg, noreg); // allocate monitor elem + __ st_ptr( O0, Lmonitors, BasicObjectLock::obj_offset_in_bytes()); // store object + // __ untested("lock_object from method entry"); + __ lock_object(Lmonitors, O0); +} + + +void TemplateInterpreterGenerator::generate_stack_overflow_check(Register Rframe_size, + Register Rscratch, + Register Rscratch2) { + const int page_size = os::vm_page_size(); + Address saved_exception_pc(G2_thread, 0, + in_bytes(JavaThread::saved_exception_pc_offset())); + Label after_frame_check; + + assert_different_registers(Rframe_size, Rscratch, Rscratch2); + + __ set( page_size, Rscratch ); + __ cmp( Rframe_size, Rscratch ); + + __ br( Assembler::lessEqual, false, Assembler::pt, after_frame_check ); + __ delayed()->nop(); + + // get the stack base, and in debug, verify it is non-zero + __ ld_ptr( G2_thread, in_bytes(Thread::stack_base_offset()), Rscratch ); +#ifdef ASSERT + Label base_not_zero; + __ cmp( Rscratch, G0 ); + __ brx( Assembler::notEqual, false, Assembler::pn, base_not_zero ); + __ delayed()->nop(); + __ stop("stack base is zero in generate_stack_overflow_check"); + __ bind(base_not_zero); +#endif + + // get the stack size, and in debug, verify it is non-zero + assert( sizeof(size_t) == sizeof(intptr_t), "wrong load size" ); + __ ld_ptr( G2_thread, in_bytes(Thread::stack_size_offset()), Rscratch2 ); +#ifdef ASSERT + Label size_not_zero; + __ cmp( Rscratch2, G0 ); + __ brx( Assembler::notEqual, false, Assembler::pn, size_not_zero ); + __ delayed()->nop(); + __ stop("stack size is zero in generate_stack_overflow_check"); + __ bind(size_not_zero); +#endif + + // compute the beginning of the protected zone minus the requested frame size + __ sub( Rscratch, Rscratch2, Rscratch ); + __ set( (StackRedPages+StackYellowPages) * page_size, Rscratch2 ); + __ add( Rscratch, Rscratch2, Rscratch ); + + // Add in the size of the frame (which is the same as subtracting it from the + // SP, which would take another register + __ add( Rscratch, Rframe_size, Rscratch ); + + // the frame is greater than one page in size, so check against + // the bottom of the stack + __ cmp( SP, Rscratch ); + __ brx( Assembler::greater, false, Assembler::pt, after_frame_check ); + __ delayed()->nop(); + + // Save the return address as the exception pc + __ st_ptr(O7, saved_exception_pc); + + // the stack will overflow, throw an exception + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_StackOverflowError)); + + // if you get to here, then there is enough stack space + __ bind( after_frame_check ); +} + + +// +// Generate a fixed interpreter frame. This is identical setup for interpreted +// methods and for native methods hence the shared code. + +void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) { + // + // + // The entry code sets up a new interpreter frame in 4 steps: + // + // 1) Increase caller's SP by for the extra local space needed: + // (check for overflow) + // Efficient implementation of xload/xstore bytecodes requires + // that arguments and non-argument locals are in a contigously + // addressable memory block => non-argument locals must be + // allocated in the caller's frame. + // + // 2) Create a new stack frame and register window: + // The new stack frame must provide space for the standard + // register save area, the maximum java expression stack size, + // the monitor slots (0 slots initially), and some frame local + // scratch locations. + // + // 3) The following interpreter activation registers must be setup: + // Lesp : expression stack pointer + // Lbcp : bytecode pointer + // Lmethod : method + // Llocals : locals pointer + // Lmonitors : monitor pointer + // LcpoolCache: constant pool cache + // + // 4) Initialize the non-argument locals if necessary: + // Non-argument locals may need to be initialized to NULL + // for GC to work. If the oop-map information is accurate + // (in the absence of the JSR problem), no initialization + // is necessary. + // + // (gri - 2/25/2000) + + + const Address size_of_parameters(G5_method, 0, in_bytes(methodOopDesc::size_of_parameters_offset())); + const Address size_of_locals (G5_method, 0, in_bytes(methodOopDesc::size_of_locals_offset())); + const Address max_stack (G5_method, 0, in_bytes(methodOopDesc::max_stack_offset())); + int rounded_vm_local_words = round_to( frame::interpreter_frame_vm_local_words, WordsPerLong ); + + const int extra_space = + rounded_vm_local_words + // frame local scratch space + frame::memory_parameter_word_sp_offset + // register save area + (native_call ? frame::interpreter_frame_extra_outgoing_argument_words : 0); + + const Register Glocals_size = G3; + const Register Otmp1 = O3; + const Register Otmp2 = O4; + // Lscratch can't be used as a temporary because the call_stub uses + // it to assert that the stack frame was setup correctly. + + __ lduh( size_of_parameters, Glocals_size); + + // Gargs points to first local + BytesPerWord + // Set the saved SP after the register window save + // + assert_different_registers(Gargs, Glocals_size, Gframe_size, O5_savedSP); + __ sll(Glocals_size, Interpreter::logStackElementSize(), Otmp1); + __ add(Gargs, Otmp1, Gargs); + + if (native_call) { + __ calc_mem_param_words( Glocals_size, Gframe_size ); + __ add( Gframe_size, extra_space, Gframe_size); + __ round_to( Gframe_size, WordsPerLong ); + __ sll( Gframe_size, LogBytesPerWord, Gframe_size ); + } else { + + // + // Compute number of locals in method apart from incoming parameters + // + __ lduh( size_of_locals, Otmp1 ); + __ sub( Otmp1, Glocals_size, Glocals_size ); + __ round_to( Glocals_size, WordsPerLong ); + __ sll( Glocals_size, Interpreter::logStackElementSize(), Glocals_size ); + + // see if the frame is greater than one page in size. If so, + // then we need to verify there is enough stack space remaining + // Frame_size = (max_stack + extra_space) * BytesPerWord; + __ lduh( max_stack, Gframe_size ); + __ add( Gframe_size, extra_space, Gframe_size ); + __ round_to( Gframe_size, WordsPerLong ); + __ sll( Gframe_size, Interpreter::logStackElementSize(), Gframe_size); + + // Add in java locals size for stack overflow check only + __ add( Gframe_size, Glocals_size, Gframe_size ); + + const Register Otmp2 = O4; + assert_different_registers(Otmp1, Otmp2, O5_savedSP); + generate_stack_overflow_check(Gframe_size, Otmp1, Otmp2); + + __ sub( Gframe_size, Glocals_size, Gframe_size); + + // + // bump SP to accomodate the extra locals + // + __ sub( SP, Glocals_size, SP ); + } + + // + // now set up a stack frame with the size computed above + // + __ neg( Gframe_size ); + __ save( SP, Gframe_size, SP ); + + // + // now set up all the local cache registers + // + // NOTE: At this point, Lbyte_code/Lscratch has been modified. Note + // that all present references to Lbyte_code initialize the register + // immediately before use + if (native_call) { + __ mov(G0, Lbcp); + } else { + __ ld_ptr(Address(G5_method, 0, in_bytes(methodOopDesc::const_offset())), Lbcp ); + __ add(Address(Lbcp, 0, in_bytes(constMethodOopDesc::codes_offset())), Lbcp ); + } + __ mov( G5_method, Lmethod); // set Lmethod + __ get_constant_pool_cache( LcpoolCache ); // set LcpoolCache + __ sub(FP, rounded_vm_local_words * BytesPerWord, Lmonitors ); // set Lmonitors +#ifdef _LP64 + __ add( Lmonitors, STACK_BIAS, Lmonitors ); // Account for 64 bit stack bias +#endif + __ sub(Lmonitors, BytesPerWord, Lesp); // set Lesp + + // setup interpreter activation registers + __ sub(Gargs, BytesPerWord, Llocals); // set Llocals + + if (ProfileInterpreter) { +#ifdef FAST_DISPATCH + // FAST_DISPATCH and ProfileInterpreter are mutually exclusive since + // they both use I2. + assert(0, "FAST_DISPATCH and +ProfileInterpreter are mutually exclusive"); +#endif // FAST_DISPATCH + __ set_method_data_pointer(); + } + +} + +// Empty method, generate a very fast return. + +address InterpreterGenerator::generate_empty_entry(void) { + + // A method that does nother but return... + + address entry = __ pc(); + Label slow_path; + + __ verify_oop(G5_method); + + // do nothing for empty methods (do not even increment invocation counter) + if ( UseFastEmptyMethods) { + // If we need a safepoint check, generate full interpreter entry. + Address sync_state(G3_scratch, SafepointSynchronize::address_of_state()); + __ load_contents(sync_state, G3_scratch); + __ cmp(G3_scratch, SafepointSynchronize::_not_synchronized); + __ br(Assembler::notEqual, false, Assembler::pn, slow_path); + __ delayed()->nop(); + + // Code: _return + __ retl(); + __ delayed()->mov(O5_savedSP, SP); + + __ bind(slow_path); + (void) generate_normal_entry(false); + + return entry; + } + return NULL; +} + +// Call an accessor method (assuming it is resolved, otherwise drop into +// vanilla (slow path) entry + +// Generates code to elide accessor methods +// Uses G3_scratch and G1_scratch as scratch +address InterpreterGenerator::generate_accessor_entry(void) { + + // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites thereof; + // parameter size = 1 + // Note: We can only use this code if the getfield has been resolved + // and if we don't have a null-pointer exception => check for + // these conditions first and use slow path if necessary. + address entry = __ pc(); + Label slow_path; + + if ( UseFastAccessorMethods) { + // Check if we need to reach a safepoint and generate full interpreter + // frame if so. + Address sync_state(G3_scratch, SafepointSynchronize::address_of_state()); + __ load_contents(sync_state, G3_scratch); + __ cmp(G3_scratch, SafepointSynchronize::_not_synchronized); + __ br(Assembler::notEqual, false, Assembler::pn, slow_path); + __ delayed()->nop(); + + // Check if local 0 != NULL + __ ld_ptr(Gargs, G0, Otos_i ); // get local 0 + __ tst(Otos_i); // check if local 0 == NULL and go the slow path + __ brx(Assembler::zero, false, Assembler::pn, slow_path); + __ delayed()->nop(); + + + // read first instruction word and extract bytecode @ 1 and index @ 2 + // get first 4 bytes of the bytecodes (big endian!) + __ ld_ptr(Address(G5_method, 0, in_bytes(methodOopDesc::const_offset())), G1_scratch); + __ ld(Address(G1_scratch, 0, in_bytes(constMethodOopDesc::codes_offset())), G1_scratch); + + // move index @ 2 far left then to the right most two bytes. + __ sll(G1_scratch, 2*BitsPerByte, G1_scratch); + __ srl(G1_scratch, 2*BitsPerByte - exact_log2(in_words( + ConstantPoolCacheEntry::size()) * BytesPerWord), G1_scratch); + + // get constant pool cache + __ ld_ptr(G5_method, in_bytes(methodOopDesc::constants_offset()), G3_scratch); + __ ld_ptr(G3_scratch, constantPoolOopDesc::cache_offset_in_bytes(), G3_scratch); + + // get specific constant pool cache entry + __ add(G3_scratch, G1_scratch, G3_scratch); + + // Check the constant Pool cache entry to see if it has been resolved. + // If not, need the slow path. + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + __ ld_ptr(G3_scratch, in_bytes(cp_base_offset + ConstantPoolCacheEntry::indices_offset()), G1_scratch); + __ srl(G1_scratch, 2*BitsPerByte, G1_scratch); + __ and3(G1_scratch, 0xFF, G1_scratch); + __ cmp(G1_scratch, Bytecodes::_getfield); + __ br(Assembler::notEqual, false, Assembler::pn, slow_path); + __ delayed()->nop(); + + // Get the type and return field offset from the constant pool cache + __ ld_ptr(G3_scratch, in_bytes(cp_base_offset + ConstantPoolCacheEntry::flags_offset()), G1_scratch); + __ ld_ptr(G3_scratch, in_bytes(cp_base_offset + ConstantPoolCacheEntry::f2_offset()), G3_scratch); + + Label xreturn_path; + // Need to differentiate between igetfield, agetfield, bgetfield etc. + // because they are different sizes. + // Get the type from the constant pool cache + __ srl(G1_scratch, ConstantPoolCacheEntry::tosBits, G1_scratch); + // Make sure we don't need to mask G1_scratch for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ cmp(G1_scratch, atos ); + __ br(Assembler::equal, true, Assembler::pt, xreturn_path); + __ delayed()->ld_ptr(Otos_i, G3_scratch, Otos_i); + __ cmp(G1_scratch, itos); + __ br(Assembler::equal, true, Assembler::pt, xreturn_path); + __ delayed()->ld(Otos_i, G3_scratch, Otos_i); + __ cmp(G1_scratch, stos); + __ br(Assembler::equal, true, Assembler::pt, xreturn_path); + __ delayed()->ldsh(Otos_i, G3_scratch, Otos_i); + __ cmp(G1_scratch, ctos); + __ br(Assembler::equal, true, Assembler::pt, xreturn_path); + __ delayed()->lduh(Otos_i, G3_scratch, Otos_i); +#ifdef ASSERT + __ cmp(G1_scratch, btos); + __ br(Assembler::equal, true, Assembler::pt, xreturn_path); + __ delayed()->ldsb(Otos_i, G3_scratch, Otos_i); + __ should_not_reach_here(); +#endif + __ ldsb(Otos_i, G3_scratch, Otos_i); + __ bind(xreturn_path); + + // _ireturn/_areturn + __ retl(); // return from leaf routine + __ delayed()->mov(O5_savedSP, SP); + + // Generate regular method entry + __ bind(slow_path); + (void) generate_normal_entry(false); + return entry; + } + return NULL; +} + +// +// Interpreter stub for calling a native method. (asm interpreter) +// This sets up a somewhat different looking stack for calling the native method +// than the typical interpreter frame setup. +// + +address InterpreterGenerator::generate_native_entry(bool synchronized) { + address entry = __ pc(); + + // the following temporary registers are used during frame creation + const Register Gtmp1 = G3_scratch ; + const Register Gtmp2 = G1_scratch; + bool inc_counter = UseCompiler || CountCompiledCalls; + + // make sure registers are different! + assert_different_registers(G2_thread, G5_method, Gargs, Gtmp1, Gtmp2); + + const Address Laccess_flags (Lmethod, 0, in_bytes(methodOopDesc::access_flags_offset())); + + __ verify_oop(G5_method); + + const Register Glocals_size = G3; + assert_different_registers(Glocals_size, G4_scratch, Gframe_size); + + // make sure method is native & not abstract + // rethink these assertions - they can be simplified and shared (gri 2/25/2000) +#ifdef ASSERT + __ ld(G5_method, in_bytes(methodOopDesc::access_flags_offset()), Gtmp1); + { + Label L; + __ btst(JVM_ACC_NATIVE, Gtmp1); + __ br(Assembler::notZero, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("tried to execute non-native method as native"); + __ bind(L); + } + { Label L; + __ btst(JVM_ACC_ABSTRACT, Gtmp1); + __ br(Assembler::zero, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("tried to execute abstract method as non-abstract"); + __ bind(L); + } +#endif // ASSERT + + // generate the code to allocate the interpreter stack frame + generate_fixed_frame(true); + + // + // No locals to initialize for native method + // + + // this slot will be set later, we initialize it to null here just in + // case we get a GC before the actual value is stored later + __ st_ptr(G0, Address(FP, 0, (frame::interpreter_frame_oop_temp_offset*wordSize) + STACK_BIAS)); + + const Address do_not_unlock_if_synchronized(G2_thread, 0, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + // Since at this point in the method invocation the exception handler + // would try to exit the monitor of synchronized methods which hasn't + // been entered yet, we set the thread local variable + // _do_not_unlock_if_synchronized to true. If any exception was thrown by + // runtime, exception handling i.e. unlock_if_synchronized_method will + // check this thread local flag. + // This flag has two effects, one is to force an unwind in the topmost + // interpreter frame and not perform an unlock while doing so. + + __ movbool(true, G3_scratch); + __ stbool(G3_scratch, do_not_unlock_if_synchronized); + + // increment invocation counter and check for overflow + // + // Note: checking for negative value instead of overflow + // so we have a 'sticky' overflow test (may be of + // importance as soon as we have true MT/MP) + Label invocation_counter_overflow; + Label Lcontinue; + if (inc_counter) { + generate_counter_incr(&invocation_counter_overflow, NULL, NULL); + + } + __ bind(Lcontinue); + + bang_stack_shadow_pages(true); + + // reset the _do_not_unlock_if_synchronized flag + __ stbool(G0, do_not_unlock_if_synchronized); + + // check for synchronized methods + // Must happen AFTER invocation_counter check and stack overflow check, + // so method is not locked if overflows. + + if (synchronized) { + lock_method(); + } else { +#ifdef ASSERT + { Label ok; + __ ld(Laccess_flags, O0); + __ btst(JVM_ACC_SYNCHRONIZED, O0); + __ br( Assembler::zero, false, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("method needs synchronization"); + __ bind(ok); + } +#endif // ASSERT + } + + + // start execution + __ verify_thread(); + + // JVMTI support + __ notify_method_entry(); + + // native call + + // (note that O0 is never an oop--at most it is a handle) + // It is important not to smash any handles created by this call, + // until any oop handle in O0 is dereferenced. + + // (note that the space for outgoing params is preallocated) + + // get signature handler + { Label L; + __ ld_ptr(Address(Lmethod, 0, in_bytes(methodOopDesc::signature_handler_offset())), G3_scratch); + __ tst(G3_scratch); + __ brx(Assembler::notZero, false, Assembler::pt, L); + __ delayed()->nop(); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), Lmethod); + __ ld_ptr(Address(Lmethod, 0, in_bytes(methodOopDesc::signature_handler_offset())), G3_scratch); + __ bind(L); + } + + // Push a new frame so that the args will really be stored in + // Copy a few locals across so the new frame has the variables + // we need but these values will be dead at the jni call and + // therefore not gc volatile like the values in the current + // frame (Lmethod in particular) + + // Flush the method pointer to the register save area + __ st_ptr(Lmethod, SP, (Lmethod->sp_offset_in_saved_window() * wordSize) + STACK_BIAS); + __ mov(Llocals, O1); + // calculate where the mirror handle body is allocated in the interpreter frame: + + Address mirror(FP, 0, (frame::interpreter_frame_oop_temp_offset*wordSize) + STACK_BIAS); + __ add(mirror, O2); + + // Calculate current frame size + __ sub(SP, FP, O3); // Calculate negative of current frame size + __ save(SP, O3, SP); // Allocate an identical sized frame + + // Note I7 has leftover trash. Slow signature handler will fill it in + // should we get there. Normal jni call will set reasonable last_Java_pc + // below (and fix I7 so the stack trace doesn't have a meaningless frame + // in it). + + // Load interpreter frame's Lmethod into same register here + + __ ld_ptr(FP, (Lmethod->sp_offset_in_saved_window() * wordSize) + STACK_BIAS, Lmethod); + + __ mov(I1, Llocals); + __ mov(I2, Lscratch2); // save the address of the mirror + + + // ONLY Lmethod and Llocals are valid here! + + // call signature handler, It will move the arg properly since Llocals in current frame + // matches that in outer frame + + __ callr(G3_scratch, 0); + __ delayed()->nop(); + + // Result handler is in Lscratch + + // Reload interpreter frame's Lmethod since slow signature handler may block + __ ld_ptr(FP, (Lmethod->sp_offset_in_saved_window() * wordSize) + STACK_BIAS, Lmethod); + + { Label not_static; + + __ ld(Laccess_flags, O0); + __ btst(JVM_ACC_STATIC, O0); + __ br( Assembler::zero, false, Assembler::pt, not_static); + __ delayed()-> + // get native function entry point(O0 is a good temp until the very end) + ld_ptr(Address(Lmethod, 0, in_bytes(methodOopDesc::native_function_offset())), O0); + // for static methods insert the mirror argument + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + Klass::java_mirror_offset_in_bytes(); + + __ ld_ptr(Address(Lmethod, 0, in_bytes(methodOopDesc:: constants_offset())), O1); + __ ld_ptr(Address(O1, 0, constantPoolOopDesc::pool_holder_offset_in_bytes()), O1); + __ ld_ptr(O1, mirror_offset, O1); +#ifdef ASSERT + if (!PrintSignatureHandlers) // do not dirty the output with this + { Label L; + __ tst(O1); + __ brx(Assembler::notZero, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("mirror is missing"); + __ bind(L); + } +#endif // ASSERT + __ st_ptr(O1, Lscratch2, 0); + __ mov(Lscratch2, O1); + __ bind(not_static); + } + + // At this point, arguments have been copied off of stack into + // their JNI positions, which are O1..O5 and SP[68..]. + // Oops are boxed in-place on the stack, with handles copied to arguments. + // The result handler is in Lscratch. O0 will shortly hold the JNIEnv*. + +#ifdef ASSERT + { Label L; + __ tst(O0); + __ brx(Assembler::notZero, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("native entry point is missing"); + __ bind(L); + } +#endif // ASSERT + + // + // setup the frame anchor + // + // The scavenge function only needs to know that the PC of this frame is + // in the interpreter method entry code, it doesn't need to know the exact + // PC and hence we can use O7 which points to the return address from the + // previous call in the code stream (signature handler function) + // + // The other trick is we set last_Java_sp to FP instead of the usual SP because + // we have pushed the extra frame in order to protect the volatile register(s) + // in that frame when we return from the jni call + // + + __ set_last_Java_frame(FP, O7); + __ mov(O7, I7); // make dummy interpreter frame look like one above, + // not meaningless information that'll confuse me. + + // flush the windows now. We don't care about the current (protection) frame + // only the outer frames + + __ flush_windows(); + + // mark windows as flushed + Address flags(G2_thread, + 0, + in_bytes(JavaThread::frame_anchor_offset()) + in_bytes(JavaFrameAnchor::flags_offset())); + __ set(JavaFrameAnchor::flushed, G3_scratch); + __ st(G3_scratch, flags); + + // Transition from _thread_in_Java to _thread_in_native. We are already safepoint ready. + + Address thread_state(G2_thread, 0, in_bytes(JavaThread::thread_state_offset())); +#ifdef ASSERT + { Label L; + __ ld(thread_state, G3_scratch); + __ cmp(G3_scratch, _thread_in_Java); + __ br(Assembler::equal, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("Wrong thread state in native stub"); + __ bind(L); + } +#endif // ASSERT + __ set(_thread_in_native, G3_scratch); + __ st(G3_scratch, thread_state); + + // Call the jni method, using the delay slot to set the JNIEnv* argument. + __ save_thread(L7_thread_cache); // save Gthread + __ callr(O0, 0); + __ delayed()-> + add(L7_thread_cache, in_bytes(JavaThread::jni_environment_offset()), O0); + + // Back from jni method Lmethod in this frame is DEAD, DEAD, DEAD + + __ restore_thread(L7_thread_cache); // restore G2_thread + + // must we block? + + // Block, if necessary, before resuming in _thread_in_Java state. + // In order for GC to work, don't clear the last_Java_sp until after blocking. + { Label no_block; + Address sync_state(G3_scratch, SafepointSynchronize::address_of_state()); + + // Switch thread to "native transition" state before reading the synchronization state. + // This additional state is necessary because reading and testing the synchronization + // state is not atomic w.r.t. GC, as this scenario demonstrates: + // Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. + // VM thread changes sync state to synchronizing and suspends threads for GC. + // Thread A is resumed to finish this native method, but doesn't block here since it + // didn't see any synchronization is progress, and escapes. + __ set(_thread_in_native_trans, G3_scratch); + __ st(G3_scratch, thread_state); + if(os::is_MP()) { + if (UseMembar) { + // Force this write out before the read below + __ membar(Assembler::StoreLoad); + } else { + // Write serialization page so VM thread can do a pseudo remote membar. + // We use the current thread pointer to calculate a thread specific + // offset to write to within the page. This minimizes bus traffic + // due to cache line collision. + __ serialize_memory(G2_thread, G1_scratch, G3_scratch); + } + } + __ load_contents(sync_state, G3_scratch); + __ cmp(G3_scratch, SafepointSynchronize::_not_synchronized); + + Label L; + Address suspend_state(G2_thread, 0, in_bytes(JavaThread::suspend_flags_offset())); + __ br(Assembler::notEqual, false, Assembler::pn, L); + __ delayed()-> + ld(suspend_state, G3_scratch); + __ cmp(G3_scratch, 0); + __ br(Assembler::equal, false, Assembler::pt, no_block); + __ delayed()->nop(); + __ bind(L); + + // Block. Save any potential method result value before the operation and + // use a leaf call to leave the last_Java_frame setup undisturbed. + save_native_result(); + __ call_VM_leaf(L7_thread_cache, + CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans), + G2_thread); + + // Restore any method result value + restore_native_result(); + __ bind(no_block); + } + + // Clear the frame anchor now + + __ reset_last_Java_frame(); + + // Move the result handler address + __ mov(Lscratch, G3_scratch); + // return possible result to the outer frame +#ifndef __LP64 + __ mov(O0, I0); + __ restore(O1, G0, O1); +#else + __ restore(O0, G0, O0); +#endif /* __LP64 */ + + // Move result handler to expected register + __ mov(G3_scratch, Lscratch); + + // Back in normal (native) interpreter frame. State is thread_in_native_trans + // switch to thread_in_Java. + + __ set(_thread_in_Java, G3_scratch); + __ st(G3_scratch, thread_state); + + // reset handle block + __ ld_ptr(G2_thread, in_bytes(JavaThread::active_handles_offset()), G3_scratch); + __ st_ptr(G0, G3_scratch, JNIHandleBlock::top_offset_in_bytes()); + + // If we have an oop result store it where it will be safe for any further gc + // until we return now that we've released the handle it might be protected by + + { + Label no_oop, store_result; + + __ set((intptr_t)AbstractInterpreter::result_handler(T_OBJECT), G3_scratch); + __ cmp(G3_scratch, Lscratch); + __ brx(Assembler::notEqual, false, Assembler::pt, no_oop); + __ delayed()->nop(); + __ addcc(G0, O0, O0); + __ brx(Assembler::notZero, true, Assembler::pt, store_result); // if result is not NULL: + __ delayed()->ld_ptr(O0, 0, O0); // unbox it + __ mov(G0, O0); + + __ bind(store_result); + // Store it where gc will look for it and result handler expects it. + __ st_ptr(O0, FP, (frame::interpreter_frame_oop_temp_offset*wordSize) + STACK_BIAS); + + __ bind(no_oop); + + } + + + // handle exceptions (exception handling will handle unlocking!) + { Label L; + Address exception_addr (G2_thread, 0, in_bytes(Thread::pending_exception_offset())); + + __ ld_ptr(exception_addr, Gtemp); + __ tst(Gtemp); + __ brx(Assembler::equal, false, Assembler::pt, L); + __ delayed()->nop(); + // Note: This could be handled more efficiently since we know that the native + // method doesn't have an exception handler. We could directly return + // to the exception handler for the caller. + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_pending_exception)); + __ should_not_reach_here(); + __ bind(L); + } + + // JVMTI support (preserves thread register) + __ notify_method_exit(true, ilgl, InterpreterMacroAssembler::NotifyJVMTI); + + if (synchronized) { + // save and restore any potential method result value around the unlocking operation + save_native_result(); + + __ add( __ top_most_monitor(), O1); + __ unlock_object(O1); + + restore_native_result(); + } + +#if defined(COMPILER2) && !defined(_LP64) + + // C2 expects long results in G1 we can't tell if we're returning to interpreted + // or compiled so just be safe. + + __ sllx(O0, 32, G1); // Shift bits into high G1 + __ srl (O1, 0, O1); // Zero extend O1 + __ or3 (O1, G1, G1); // OR 64 bits into G1 + +#endif /* COMPILER2 && !_LP64 */ + + // dispose of return address and remove activation +#ifdef ASSERT + { + Label ok; + __ cmp(I5_savedSP, FP); + __ brx(Assembler::greaterEqualUnsigned, false, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("bad I5_savedSP value"); + __ should_not_reach_here(); + __ bind(ok); + } +#endif + if (TraceJumps) { + // Move target to register that is recordable + __ mov(Lscratch, G3_scratch); + __ JMP(G3_scratch, 0); + } else { + __ jmp(Lscratch, 0); + } + __ delayed()->nop(); + + + if (inc_counter) { + // handle invocation counter overflow + __ bind(invocation_counter_overflow); + generate_counter_overflow(Lcontinue); + } + + + + return entry; +} + + +// Generic method entry to (asm) interpreter +//------------------------------------------------------------------------------------------------------------------------ +// +address InterpreterGenerator::generate_normal_entry(bool synchronized) { + address entry = __ pc(); + + bool inc_counter = UseCompiler || CountCompiledCalls; + + // the following temporary registers are used during frame creation + const Register Gtmp1 = G3_scratch ; + const Register Gtmp2 = G1_scratch; + + // make sure registers are different! + assert_different_registers(G2_thread, G5_method, Gargs, Gtmp1, Gtmp2); + + const Address size_of_parameters(G5_method, 0, in_bytes(methodOopDesc::size_of_parameters_offset())); + const Address size_of_locals (G5_method, 0, in_bytes(methodOopDesc::size_of_locals_offset())); + // Seems like G5_method is live at the point this is used. So we could make this look consistent + // and use in the asserts. + const Address access_flags (Lmethod, 0, in_bytes(methodOopDesc::access_flags_offset())); + + __ verify_oop(G5_method); + + const Register Glocals_size = G3; + assert_different_registers(Glocals_size, G4_scratch, Gframe_size); + + // make sure method is not native & not abstract + // rethink these assertions - they can be simplified and shared (gri 2/25/2000) +#ifdef ASSERT + __ ld(G5_method, in_bytes(methodOopDesc::access_flags_offset()), Gtmp1); + { + Label L; + __ btst(JVM_ACC_NATIVE, Gtmp1); + __ br(Assembler::zero, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("tried to execute native method as non-native"); + __ bind(L); + } + { Label L; + __ btst(JVM_ACC_ABSTRACT, Gtmp1); + __ br(Assembler::zero, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("tried to execute abstract method as non-abstract"); + __ bind(L); + } +#endif // ASSERT + + // generate the code to allocate the interpreter stack frame + + generate_fixed_frame(false); + +#ifdef FAST_DISPATCH + __ set((intptr_t)Interpreter::dispatch_table(), IdispatchTables); + // set bytecode dispatch table base +#endif + + // + // Code to initialize the extra (i.e. non-parm) locals + // + Register init_value = noreg; // will be G0 if we must clear locals + // The way the code was setup before zerolocals was always true for vanilla java entries. + // It could only be false for the specialized entries like accessor or empty which have + // no extra locals so the testing was a waste of time and the extra locals were always + // initialized. We removed this extra complication to already over complicated code. + + init_value = G0; + Label clear_loop; + + // NOTE: If you change the frame layout, this code will need to + // be updated! + __ lduh( size_of_locals, O2 ); + __ lduh( size_of_parameters, O1 ); + __ sll( O2, Interpreter::logStackElementSize(), O2); + __ sll( O1, Interpreter::logStackElementSize(), O1 ); + __ sub( Llocals, O2, O2 ); + __ sub( Llocals, O1, O1 ); + + __ bind( clear_loop ); + __ inc( O2, wordSize ); + + __ cmp( O2, O1 ); + __ brx( Assembler::lessEqualUnsigned, true, Assembler::pt, clear_loop ); + __ delayed()->st_ptr( init_value, O2, 0 ); + + const Address do_not_unlock_if_synchronized(G2_thread, 0, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + // Since at this point in the method invocation the exception handler + // would try to exit the monitor of synchronized methods which hasn't + // been entered yet, we set the thread local variable + // _do_not_unlock_if_synchronized to true. If any exception was thrown by + // runtime, exception handling i.e. unlock_if_synchronized_method will + // check this thread local flag. + __ movbool(true, G3_scratch); + __ stbool(G3_scratch, do_not_unlock_if_synchronized); + + // increment invocation counter and check for overflow + // + // Note: checking for negative value instead of overflow + // so we have a 'sticky' overflow test (may be of + // importance as soon as we have true MT/MP) + Label invocation_counter_overflow; + Label profile_method; + Label profile_method_continue; + Label Lcontinue; + if (inc_counter) { + generate_counter_incr(&invocation_counter_overflow, &profile_method, &profile_method_continue); + if (ProfileInterpreter) { + __ bind(profile_method_continue); + } + } + __ bind(Lcontinue); + + bang_stack_shadow_pages(false); + + // reset the _do_not_unlock_if_synchronized flag + __ stbool(G0, do_not_unlock_if_synchronized); + + // check for synchronized methods + // Must happen AFTER invocation_counter check and stack overflow check, + // so method is not locked if overflows. + + if (synchronized) { + lock_method(); + } else { +#ifdef ASSERT + { Label ok; + __ ld(access_flags, O0); + __ btst(JVM_ACC_SYNCHRONIZED, O0); + __ br( Assembler::zero, false, Assembler::pt, ok); + __ delayed()->nop(); + __ stop("method needs synchronization"); + __ bind(ok); + } +#endif // ASSERT + } + + // start execution + + __ verify_thread(); + + // jvmti support + __ notify_method_entry(); + + // start executing instructions + __ dispatch_next(vtos); + + + if (inc_counter) { + if (ProfileInterpreter) { + // We have decided to profile this method in the interpreter + __ bind(profile_method); + + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method), Lbcp, true); + +#ifdef ASSERT + __ tst(O0); + __ breakpoint_trap(Assembler::notEqual); +#endif + + __ set_method_data_pointer(); + + __ ba(false, profile_method_continue); + __ delayed()->nop(); + } + + // handle invocation counter overflow + __ bind(invocation_counter_overflow); + generate_counter_overflow(Lcontinue); + } + + + return entry; +} + + +//---------------------------------------------------------------------------------------------------- +// Entry points & stack frame layout +// +// Here we generate the various kind of entries into the interpreter. +// The two main entry type are generic bytecode methods and native call method. +// These both come in synchronized and non-synchronized versions but the +// frame layout they create is very similar. The other method entry +// types are really just special purpose entries that are really entry +// and interpretation all in one. These are for trivial methods like +// accessor, empty, or special math methods. +// +// When control flow reaches any of the entry types for the interpreter +// the following holds -> +// +// C2 Calling Conventions: +// +// The entry code below assumes that the following registers are set +// when coming in: +// G5_method: holds the methodOop of the method to call +// Lesp: points to the TOS of the callers expression stack +// after having pushed all the parameters +// +// The entry code does the following to setup an interpreter frame +// pop parameters from the callers stack by adjusting Lesp +// set O0 to Lesp +// compute X = (max_locals - num_parameters) +// bump SP up by X to accomadate the extra locals +// compute X = max_expression_stack +// + vm_local_words +// + 16 words of register save area +// save frame doing a save sp, -X, sp growing towards lower addresses +// set Lbcp, Lmethod, LcpoolCache +// set Llocals to i0 +// set Lmonitors to FP - rounded_vm_local_words +// set Lesp to Lmonitors - 4 +// +// The frame has now been setup to do the rest of the entry code + +// Try this optimization: Most method entries could live in a +// "one size fits all" stack frame without all the dynamic size +// calculations. It might be profitable to do all this calculation +// statically and approximately for "small enough" methods. + +//----------------------------------------------------------------------------------------------- + +// C1 Calling conventions +// +// Upon method entry, the following registers are setup: +// +// g2 G2_thread: current thread +// g5 G5_method: method to activate +// g4 Gargs : pointer to last argument +// +// +// Stack: +// +// +---------------+ <--- sp +// | | +// : reg save area : +// | | +// +---------------+ <--- sp + 0x40 +// | | +// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) +// | | +// +---------------+ <--- sp + 0x5c +// | | +// : free : +// | | +// +---------------+ <--- Gargs +// | | +// : arguments : +// | | +// +---------------+ +// | | +// +// +// +// AFTER FRAME HAS BEEN SETUP for method interpretation the stack looks like: +// +// +---------------+ <--- sp +// | | +// : reg save area : +// | | +// +---------------+ <--- sp + 0x40 +// | | +// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) +// | | +// +---------------+ <--- sp + 0x5c +// | | +// : : +// | | <--- Lesp +// +---------------+ <--- Lmonitors (fp - 0x18) +// | VM locals | +// +---------------+ <--- fp +// | | +// : reg save area : +// | | +// +---------------+ <--- fp + 0x40 +// | | +// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) +// | | +// +---------------+ <--- fp + 0x5c +// | | +// : free : +// | | +// +---------------+ +// | | +// : nonarg locals : +// | | +// +---------------+ +// | | +// : arguments : +// | | <--- Llocals +// +---------------+ <--- Gargs +// | | + +static int size_activation_helper(int callee_extra_locals, int max_stack, int monitor_size) { + + // Figure out the size of an interpreter frame (in words) given that we have a fully allocated + // expression stack, the callee will have callee_extra_locals (so we can account for + // frame extension) and monitor_size for monitors. Basically we need to calculate + // this exactly like generate_fixed_frame/generate_compute_interpreter_state. + // + // + // The big complicating thing here is that we must ensure that the stack stays properly + // aligned. This would be even uglier if monitor size wasn't modulo what the stack + // needs to be aligned for). We are given that the sp (fp) is already aligned by + // the caller so we must ensure that it is properly aligned for our callee. + // + const int rounded_vm_local_words = + round_to(frame::interpreter_frame_vm_local_words,WordsPerLong); + // callee_locals and max_stack are counts, not the size in frame. + const int locals_size = + round_to(callee_extra_locals * Interpreter::stackElementWords(), WordsPerLong); + const int max_stack_words = max_stack * Interpreter::stackElementWords(); + return (round_to((max_stack_words + + rounded_vm_local_words + + frame::memory_parameter_word_sp_offset), WordsPerLong) + // already rounded + + locals_size + monitor_size); +} + +// How much stack a method top interpreter activation needs in words. +int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { + + // See call_stub code + int call_stub_size = round_to(7 + frame::memory_parameter_word_sp_offset, + WordsPerLong); // 7 + register save area + + // Save space for one monitor to get into the interpreted method in case + // the method is synchronized + int monitor_size = method->is_synchronized() ? + 1*frame::interpreter_frame_monitor_size() : 0; + return size_activation_helper(method->max_locals(), method->max_stack(), + monitor_size) + call_stub_size; +} + +int AbstractInterpreter::layout_activation(methodOop method, + int tempcount, + int popframe_extra_args, + int moncount, + int callee_param_count, + int callee_local_count, + frame* caller, + frame* interpreter_frame, + bool is_top_frame) { + // Note: This calculation must exactly parallel the frame setup + // in InterpreterGenerator::generate_fixed_frame. + // If f!=NULL, set up the following variables: + // - Lmethod + // - Llocals + // - Lmonitors (to the indicated number of monitors) + // - Lesp (to the indicated number of temps) + // The frame f (if not NULL) on entry is a description of the caller of the frame + // we are about to layout. We are guaranteed that we will be able to fill in a + // new interpreter frame as its callee (i.e. the stack space is allocated and + // the amount was determined by an earlier call to this method with f == NULL). + // On return f (if not NULL) while describe the interpreter frame we just layed out. + + int monitor_size = moncount * frame::interpreter_frame_monitor_size(); + int rounded_vm_local_words = round_to(frame::interpreter_frame_vm_local_words,WordsPerLong); + + assert(monitor_size == round_to(monitor_size, WordsPerLong), "must align"); + // + // Note: if you look closely this appears to be doing something much different + // than generate_fixed_frame. What is happening is this. On sparc we have to do + // this dance with interpreter_sp_adjustment because the window save area would + // appear just below the bottom (tos) of the caller's java expression stack. Because + // the interpreter want to have the locals completely contiguous generate_fixed_frame + // will adjust the caller's sp for the "extra locals" (max_locals - parameter_size). + // Now in generate_fixed_frame the extension of the caller's sp happens in the callee. + // In this code the opposite occurs the caller adjusts it's own stack base on the callee. + // This is mostly ok but it does cause a problem when we get to the initial frame (the oldest) + // because the oldest frame would have adjust its callers frame and yet that frame + // already exists and isn't part of this array of frames we are unpacking. So at first + // glance this would seem to mess up that frame. However Deoptimization::fetch_unroll_info_helper() + // will after it calculates all of the frame's on_stack_size()'s will then figure out the + // amount to adjust the caller of the initial (oldest) frame and the calculation will all + // add up. It does seem like it simpler to account for the adjustment here (and remove the + // callee... parameters here). However this would mean that this routine would have to take + // the caller frame as input so we could adjust its sp (and set it's interpreter_sp_adjustment) + // and run the calling loop in the reverse order. This would also would appear to mean making + // this code aware of what the interactions are when that initial caller fram was an osr or + // other adapter frame. deoptimization is complicated enough and hard enough to debug that + // there is no sense in messing working code. + // + + int rounded_cls = round_to((callee_local_count - callee_param_count), WordsPerLong); + assert(rounded_cls == round_to(rounded_cls, WordsPerLong), "must align"); + + int raw_frame_size = size_activation_helper(rounded_cls, method->max_stack(), + monitor_size); + + if (interpreter_frame != NULL) { + // The skeleton frame must already look like an interpreter frame + // even if not fully filled out. + assert(interpreter_frame->is_interpreted_frame(), "Must be interpreted frame"); + + intptr_t* fp = interpreter_frame->fp(); + + JavaThread* thread = JavaThread::current(); + RegisterMap map(thread, false); + // More verification that skeleton frame is properly walkable + assert(fp == caller->sp(), "fp must match"); + + intptr_t* montop = fp - rounded_vm_local_words; + + // preallocate monitors (cf. __ add_monitor_to_stack) + intptr_t* monitors = montop - monitor_size; + + // preallocate stack space + intptr_t* esp = monitors - 1 - + (tempcount * Interpreter::stackElementWords()) - + popframe_extra_args; + + int local_words = method->max_locals() * Interpreter::stackElementWords(); + int parm_words = method->size_of_parameters() * Interpreter::stackElementWords(); + NEEDS_CLEANUP; + intptr_t* locals; + if (caller->is_interpreted_frame()) { + // Can force the locals area to end up properly overlapping the top of the expression stack. + intptr_t* Lesp_ptr = caller->interpreter_frame_tos_address() - 1; + // Note that this computation means we replace size_of_parameters() values from the caller + // interpreter frame's expression stack with our argument locals + locals = Lesp_ptr + parm_words; + int delta = local_words - parm_words; + int computed_sp_adjustment = (delta > 0) ? round_to(delta, WordsPerLong) : 0; + *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + computed_sp_adjustment) - STACK_BIAS; + } else { + assert(caller->is_compiled_frame() || caller->is_entry_frame(), "only possible cases"); + // Don't have Lesp available; lay out locals block in the caller + // adjacent to the register window save area. + // + // Compiled frames do not allocate a varargs area which is why this if + // statement is needed. + // + if (caller->is_compiled_frame()) { + locals = fp + frame::register_save_words + local_words - 1; + } else { + locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1; + } + if (!caller->is_entry_frame()) { + // Caller wants his own SP back + int caller_frame_size = caller->cb()->frame_size(); + *interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - STACK_BIAS; + } + } + if (TraceDeoptimization) { + if (caller->is_entry_frame()) { + // make sure I5_savedSP and the entry frames notion of saved SP + // agree. This assertion duplicate a check in entry frame code + // but catches the failure earlier. + assert(*caller->register_addr(Lscratch) == *interpreter_frame->register_addr(I5_savedSP), + "would change callers SP"); + } + if (caller->is_entry_frame()) { + tty->print("entry "); + } + if (caller->is_compiled_frame()) { + tty->print("compiled "); + if (caller->is_deoptimized_frame()) { + tty->print("(deopt) "); + } + } + if (caller->is_interpreted_frame()) { + tty->print("interpreted "); + } + tty->print_cr("caller fp=0x%x sp=0x%x", caller->fp(), caller->sp()); + tty->print_cr("save area = 0x%x, 0x%x", caller->sp(), caller->sp() + 16); + tty->print_cr("save area = 0x%x, 0x%x", caller->fp(), caller->fp() + 16); + tty->print_cr("interpreter fp=0x%x sp=0x%x", interpreter_frame->fp(), interpreter_frame->sp()); + tty->print_cr("save area = 0x%x, 0x%x", interpreter_frame->sp(), interpreter_frame->sp() + 16); + tty->print_cr("save area = 0x%x, 0x%x", interpreter_frame->fp(), interpreter_frame->fp() + 16); + tty->print_cr("Llocals = 0x%x", locals); + tty->print_cr("Lesp = 0x%x", esp); + tty->print_cr("Lmonitors = 0x%x", monitors); + } + + if (method->max_locals() > 0) { + assert(locals < caller->sp() || locals >= (caller->sp() + 16), "locals in save area"); + assert(locals < caller->fp() || locals > (caller->fp() + 16), "locals in save area"); + assert(locals < interpreter_frame->sp() || locals > (interpreter_frame->sp() + 16), "locals in save area"); + assert(locals < interpreter_frame->fp() || locals >= (interpreter_frame->fp() + 16), "locals in save area"); + } +#ifdef _LP64 + assert(*interpreter_frame->register_addr(I5_savedSP) & 1, "must be odd"); +#endif + + *interpreter_frame->register_addr(Lmethod) = (intptr_t) method; + *interpreter_frame->register_addr(Llocals) = (intptr_t) locals; + *interpreter_frame->register_addr(Lmonitors) = (intptr_t) monitors; + *interpreter_frame->register_addr(Lesp) = (intptr_t) esp; + // Llast_SP will be same as SP as there is no adapter space + *interpreter_frame->register_addr(Llast_SP) = (intptr_t) interpreter_frame->sp() - STACK_BIAS; + *interpreter_frame->register_addr(LcpoolCache) = (intptr_t) method->constants()->cache(); +#ifdef FAST_DISPATCH + *interpreter_frame->register_addr(IdispatchTables) = (intptr_t) Interpreter::dispatch_table(); +#endif + + +#ifdef ASSERT + BasicObjectLock* mp = (BasicObjectLock*)monitors; + + assert(interpreter_frame->interpreter_frame_method() == method, "method matches"); + assert(interpreter_frame->interpreter_frame_local_at(9) == (intptr_t *)((intptr_t)locals - (9 * Interpreter::stackElementSize())+Interpreter::value_offset_in_bytes()), "locals match"); + assert(interpreter_frame->interpreter_frame_monitor_end() == mp, "monitor_end matches"); + assert(((intptr_t *)interpreter_frame->interpreter_frame_monitor_begin()) == ((intptr_t *)mp)+monitor_size, "monitor_begin matches"); + assert(interpreter_frame->interpreter_frame_tos_address()-1 == esp, "esp matches"); + + // check bounds + intptr_t* lo = interpreter_frame->sp() + (frame::memory_parameter_word_sp_offset - 1); + intptr_t* hi = interpreter_frame->fp() - rounded_vm_local_words; + assert(lo < monitors && montop <= hi, "monitors in bounds"); + assert(lo <= esp && esp < monitors, "esp in bounds"); +#endif // ASSERT + } + + return raw_frame_size; +} + +//---------------------------------------------------------------------------------------------------- +// Exceptions +void TemplateInterpreterGenerator::generate_throw_exception() { + + // Entry point in previous activation (i.e., if the caller was interpreted) + Interpreter::_rethrow_exception_entry = __ pc(); + // O0: exception + + // entry point for exceptions thrown within interpreter code + Interpreter::_throw_exception_entry = __ pc(); + __ verify_thread(); + // expression stack is undefined here + // O0: exception, i.e. Oexception + // Lbcp: exception bcx + __ verify_oop(Oexception); + + + // expression stack must be empty before entering the VM in case of an exception + __ empty_expression_stack(); + // find exception handler address and preserve exception oop + // call C routine to find handler and jump to it + __ call_VM(O1, CAST_FROM_FN_PTR(address, InterpreterRuntime::exception_handler_for_exception), Oexception); + __ push_ptr(O1); // push exception for exception handler bytecodes + + __ JMP(O0, 0); // jump to exception handler (may be remove activation entry!) + __ delayed()->nop(); + + + // if the exception is not handled in the current frame + // the frame is removed and the exception is rethrown + // (i.e. exception continuation is _rethrow_exception) + // + // Note: At this point the bci is still the bxi for the instruction which caused + // the exception and the expression stack is empty. Thus, for any VM calls + // at this point, GC will find a legal oop map (with empty expression stack). + + // in current activation + // tos: exception + // Lbcp: exception bcp + + // + // JVMTI PopFrame support + // + + Interpreter::_remove_activation_preserving_args_entry = __ pc(); + Address popframe_condition_addr (G2_thread, 0, in_bytes(JavaThread::popframe_condition_offset())); + // Set the popframe_processing bit in popframe_condition indicating that we are + // currently handling popframe, so that call_VMs that may happen later do not trigger new + // popframe handling cycles. + + __ ld(popframe_condition_addr, G3_scratch); + __ or3(G3_scratch, JavaThread::popframe_processing_bit, G3_scratch); + __ stw(G3_scratch, popframe_condition_addr); + + // Empty the expression stack, as in normal exception handling + __ empty_expression_stack(); + __ unlock_if_synchronized_method(vtos, /* throw_monitor_exception */ false, /* install_monitor_exception */ false); + + { + // Check to see whether we are returning to a deoptimized frame. + // (The PopFrame call ensures that the caller of the popped frame is + // either interpreted or compiled and deoptimizes it if compiled.) + // In this case, we can't call dispatch_next() after the frame is + // popped, but instead must save the incoming arguments and restore + // them after deoptimization has occurred. + // + // Note that we don't compare the return PC against the + // deoptimization blob's unpack entry because of the presence of + // adapter frames in C2. + Label caller_not_deoptimized; + __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, InterpreterRuntime::interpreter_contains), I7); + __ tst(O0); + __ brx(Assembler::notEqual, false, Assembler::pt, caller_not_deoptimized); + __ delayed()->nop(); + + const Register Gtmp1 = G3_scratch; + const Register Gtmp2 = G1_scratch; + + // Compute size of arguments for saving when returning to deoptimized caller + __ lduh(Lmethod, in_bytes(methodOopDesc::size_of_parameters_offset()), Gtmp1); + __ sll(Gtmp1, Interpreter::logStackElementSize(), Gtmp1); + __ sub(Llocals, Gtmp1, Gtmp2); + __ add(Gtmp2, wordSize, Gtmp2); + // Save these arguments + __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::popframe_preserve_args), G2_thread, Gtmp1, Gtmp2); + // Inform deoptimization that it is responsible for restoring these arguments + __ set(JavaThread::popframe_force_deopt_reexecution_bit, Gtmp1); + Address popframe_condition_addr(G2_thread, 0, in_bytes(JavaThread::popframe_condition_offset())); + __ st(Gtmp1, popframe_condition_addr); + + // Return from the current method + // The caller's SP was adjusted upon method entry to accomodate + // the callee's non-argument locals. Undo that adjustment. + __ ret(); + __ delayed()->restore(I5_savedSP, G0, SP); + + __ bind(caller_not_deoptimized); + } + + // Clear the popframe condition flag + __ stw(G0 /* popframe_inactive */, popframe_condition_addr); + + // Get out of the current method (how this is done depends on the particular compiler calling + // convention that the interpreter currently follows) + // The caller's SP was adjusted upon method entry to accomodate + // the callee's non-argument locals. Undo that adjustment. + __ restore(I5_savedSP, G0, SP); + // The method data pointer was incremented already during + // call profiling. We have to restore the mdp for the current bcp. + if (ProfileInterpreter) { + __ set_method_data_pointer_for_bcp(); + } + // Resume bytecode interpretation at the current bcp + __ dispatch_next(vtos); + // end of JVMTI PopFrame support + + Interpreter::_remove_activation_entry = __ pc(); + + // preserve exception over this code sequence (remove activation calls the vm, but oopmaps are not correct here) + __ pop_ptr(Oexception); // get exception + + // Intel has the following comment: + //// remove the activation (without doing throws on illegalMonitorExceptions) + // They remove the activation without checking for bad monitor state. + // %%% We should make sure this is the right semantics before implementing. + + // %%% changed set_vm_result_2 to set_vm_result and get_vm_result_2 to get_vm_result. Is there a bug here? + __ set_vm_result(Oexception); + __ unlock_if_synchronized_method(vtos, /* throw_monitor_exception */ false); + + __ notify_method_exit(false, vtos, InterpreterMacroAssembler::SkipNotifyJVMTI); + + __ get_vm_result(Oexception); + __ verify_oop(Oexception); + + const int return_reg_adjustment = frame::pc_return_offset; + Address issuing_pc_addr(I7, 0, return_reg_adjustment); + + // We are done with this activation frame; find out where to go next. + // The continuation point will be an exception handler, which expects + // the following registers set up: + // + // Oexception: exception + // Oissuing_pc: the local call that threw exception + // Other On: garbage + // In/Ln: the contents of the caller's register window + // + // We do the required restore at the last possible moment, because we + // need to preserve some state across a runtime call. + // (Remember that the caller activation is unknown--it might not be + // interpreted, so things like Lscratch are useless in the caller.) + + // Although the Intel version uses call_C, we can use the more + // compact call_VM. (The only real difference on SPARC is a + // harmlessly ignored [re]set_last_Java_frame, compared with + // the Intel code which lacks this.) + __ mov(Oexception, Oexception ->after_save()); // get exception in I0 so it will be on O0 after restore + __ add(issuing_pc_addr, Oissuing_pc->after_save()); // likewise set I1 to a value local to the caller + __ super_call_VM_leaf(L7_thread_cache, + CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), + Oissuing_pc->after_save()); + + // The caller's SP was adjusted upon method entry to accomodate + // the callee's non-argument locals. Undo that adjustment. + __ JMP(O0, 0); // return exception handler in caller + __ delayed()->restore(I5_savedSP, G0, SP); + + // (same old exception object is already in Oexception; see above) + // Note that an "issuing PC" is actually the next PC after the call +} + + +// +// JVMTI ForceEarlyReturn support +// + +address TemplateInterpreterGenerator::generate_earlyret_entry_for(TosState state) { + address entry = __ pc(); + + __ empty_expression_stack(); + __ load_earlyret_value(state); + + __ ld_ptr(Address(G2_thread, 0, in_bytes(JavaThread::jvmti_thread_state_offset())), G3_scratch); + Address cond_addr(G3_scratch, 0, in_bytes(JvmtiThreadState::earlyret_state_offset())); + + // Clear the earlyret state + __ stw(G0 /* JvmtiThreadState::earlyret_inactive */, cond_addr); + + __ remove_activation(state, + /* throw_monitor_exception */ false, + /* install_monitor_exception */ false); + + // The caller's SP was adjusted upon method entry to accomodate + // the callee's non-argument locals. Undo that adjustment. + __ ret(); // return to caller + __ delayed()->restore(I5_savedSP, G0, SP); + + return entry; +} // end of JVMTI ForceEarlyReturn support + + +//------------------------------------------------------------------------------------------------------------------------ +// Helper for vtos entry point generation + +void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep) { + assert(t->is_valid() && t->tos_in() == vtos, "illegal template"); + Label L; + aep = __ pc(); __ push_ptr(); __ ba(false, L); __ delayed()->nop(); + fep = __ pc(); __ push_f(); __ ba(false, L); __ delayed()->nop(); + dep = __ pc(); __ push_d(); __ ba(false, L); __ delayed()->nop(); + lep = __ pc(); __ push_l(); __ ba(false, L); __ delayed()->nop(); + iep = __ pc(); __ push_i(); + bep = cep = sep = iep; // there aren't any + vep = __ pc(); __ bind(L); // fall through + generate_and_dispatch(t); +} + +// -------------------------------------------------------------------------------- + + +InterpreterGenerator::InterpreterGenerator(StubQueue* code) + : TemplateInterpreterGenerator(code) { + generate_all(); // down here so it can be "virtual" +} + +// -------------------------------------------------------------------------------- + +// Non-product code +#ifndef PRODUCT +address TemplateInterpreterGenerator::generate_trace_code(TosState state) { + address entry = __ pc(); + + __ push(state); + __ mov(O7, Lscratch); // protect return address within interpreter + + // Pass a 0 (not used in sparc) and the top of stack to the bytecode tracer + __ mov( Otos_l2, G3_scratch ); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::trace_bytecode), G0, Otos_l1, G3_scratch); + __ mov(Lscratch, O7); // restore return address + __ pop(state); + __ retl(); + __ delayed()->nop(); + + return entry; +} + + +// helpers for generate_and_dispatch + +void TemplateInterpreterGenerator::count_bytecode() { + Address c(G3_scratch, (address)&BytecodeCounter::_counter_value); + __ load_contents(c, G4_scratch); + __ inc(G4_scratch); + __ st(G4_scratch, c); +} + + +void TemplateInterpreterGenerator::histogram_bytecode(Template* t) { + Address bucket( G3_scratch, (address) &BytecodeHistogram::_counters[t->bytecode()] ); + __ load_contents(bucket, G4_scratch); + __ inc(G4_scratch); + __ st(G4_scratch, bucket); +} + + +void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) { + address index_addr = (address)&BytecodePairHistogram::_index; + Address index(G3_scratch, index_addr); + + address counters_addr = (address)&BytecodePairHistogram::_counters; + Address counters(G3_scratch, counters_addr); + + // get index, shift out old bytecode, bring in new bytecode, and store it + // _index = (_index >> log2_number_of_codes) | + // (bytecode << log2_number_of_codes); + + + __ load_contents( index, G4_scratch ); + __ srl( G4_scratch, BytecodePairHistogram::log2_number_of_codes, G4_scratch ); + __ set( ((int)t->bytecode()) << BytecodePairHistogram::log2_number_of_codes, G3_scratch ); + __ or3( G3_scratch, G4_scratch, G4_scratch ); + __ store_contents( G4_scratch, index ); + + // bump bucket contents + // _counters[_index] ++; + + __ load_address( counters ); // loads into G3_scratch + __ sll( G4_scratch, LogBytesPerWord, G4_scratch ); // Index is word address + __ add (G3_scratch, G4_scratch, G3_scratch); // Add in index + __ ld (G3_scratch, 0, G4_scratch); + __ inc (G4_scratch); + __ st (G4_scratch, 0, G3_scratch); +} + + +void TemplateInterpreterGenerator::trace_bytecode(Template* t) { + // Call a little run-time stub to avoid blow-up for each bytecode. + // The run-time runtime saves the right registers, depending on + // the tosca in-state for the given template. + address entry = Interpreter::trace_code(t->tos_in()); + guarantee(entry != NULL, "entry must have been generated"); + __ call(entry, relocInfo::none); + __ delayed()->nop(); +} + + +void TemplateInterpreterGenerator::stop_interpreter_at() { + Address counter(G3_scratch , (address)&BytecodeCounter::_counter_value); + __ load_contents (counter, G3_scratch ); + Address stop_at(G4_scratch, (address)&StopInterpreterAt); + __ load_ptr_contents(stop_at, G4_scratch); + __ cmp(G3_scratch, G4_scratch); + __ breakpoint_trap(Assembler::equal); +} +#endif // not PRODUCT +#endif // !CC_INTERP diff --git a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.hpp b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.hpp new file mode 100644 index 00000000000..6a510688888 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.hpp @@ -0,0 +1,39 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + + protected: + + // Size of interpreter code. Increase if too small. Interpreter will + // fail with a guarantee ("not enough space for interpreter generation"); + // if too small. + // Run with +PrintInterpreter to get the VM to print out the size. + // Max size with JVMTI and TaggedStackInterpreter +#ifdef _LP64 + // The sethi() instruction generates lots more instructions when shell + // stack limit is unlimited, so that's why this is much bigger. + const static int InterpreterCodeSize = 210 * K; +#else + const static int InterpreterCodeSize = 180 * K; +#endif diff --git a/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp b/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp new file mode 100644 index 00000000000..220b8813980 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp @@ -0,0 +1,3573 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_templateTable_sparc.cpp.incl" + +#ifndef CC_INTERP +#define __ _masm-> + + +//---------------------------------------------------------------------------------------------------- +// Platform-dependent initialization + +void TemplateTable::pd_initialize() { + // (none) +} + + +//---------------------------------------------------------------------------------------------------- +// Condition conversion +Assembler::Condition ccNot(TemplateTable::Condition cc) { + switch (cc) { + case TemplateTable::equal : return Assembler::notEqual; + case TemplateTable::not_equal : return Assembler::equal; + case TemplateTable::less : return Assembler::greaterEqual; + case TemplateTable::less_equal : return Assembler::greater; + case TemplateTable::greater : return Assembler::lessEqual; + case TemplateTable::greater_equal: return Assembler::less; + } + ShouldNotReachHere(); + return Assembler::zero; +} + +//---------------------------------------------------------------------------------------------------- +// Miscelaneous helper routines + + +Address TemplateTable::at_bcp(int offset) { + assert(_desc->uses_bcp(), "inconsistent uses_bcp information"); + return Address( Lbcp, 0, offset); +} + + +void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register Rbyte_code, + Register Rscratch, + bool load_bc_into_scratch /*=true*/) { + // With sharing on, may need to test methodOop flag. + if (!RewriteBytecodes) return; + if (load_bc_into_scratch) __ set(bc, Rbyte_code); + Label patch_done; + if (JvmtiExport::can_post_breakpoint()) { + Label fast_patch; + __ ldub(at_bcp(0), Rscratch); + __ cmp(Rscratch, Bytecodes::_breakpoint); + __ br(Assembler::notEqual, false, Assembler::pt, fast_patch); + __ delayed()->nop(); // don't bother to hoist the stb here + // perform the quickening, slowly, in the bowels of the breakpoint table + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::set_original_bytecode_at), Lmethod, Lbcp, Rbyte_code); + __ ba(false, patch_done); + __ delayed()->nop(); + __ bind(fast_patch); + } +#ifdef ASSERT + Bytecodes::Code orig_bytecode = Bytecodes::java_code(bc); + Label okay; + __ ldub(at_bcp(0), Rscratch); + __ cmp(Rscratch, orig_bytecode); + __ br(Assembler::equal, false, Assembler::pt, okay); + __ delayed() ->cmp(Rscratch, Rbyte_code); + __ br(Assembler::equal, false, Assembler::pt, okay); + __ delayed()->nop(); + __ stop("Rewriting wrong bytecode location"); + __ bind(okay); +#endif + __ stb(Rbyte_code, at_bcp(0)); + __ bind(patch_done); +} + +//---------------------------------------------------------------------------------------------------- +// Individual instructions + +void TemplateTable::nop() { + transition(vtos, vtos); + // nothing to do +} + +void TemplateTable::shouldnotreachhere() { + transition(vtos, vtos); + __ stop("shouldnotreachhere bytecode"); +} + +void TemplateTable::aconst_null() { + transition(vtos, atos); + __ clr(Otos_i); +} + + +void TemplateTable::iconst(int value) { + transition(vtos, itos); + __ set(value, Otos_i); +} + + +void TemplateTable::lconst(int value) { + transition(vtos, ltos); + assert(value >= 0, "check this code"); +#ifdef _LP64 + __ set(value, Otos_l); +#else + __ set(value, Otos_l2); + __ clr( Otos_l1); +#endif +} + + +void TemplateTable::fconst(int value) { + transition(vtos, ftos); + static float zero = 0.0, one = 1.0, two = 2.0; + float* p; + switch( value ) { + default: ShouldNotReachHere(); + case 0: p = &zero; break; + case 1: p = &one; break; + case 2: p = &two; break; + } + Address a(G3_scratch, (address)p); + __ sethi(a); + __ ldf(FloatRegisterImpl::S, a, Ftos_f); +} + + +void TemplateTable::dconst(int value) { + transition(vtos, dtos); + static double zero = 0.0, one = 1.0; + double* p; + switch( value ) { + default: ShouldNotReachHere(); + case 0: p = &zero; break; + case 1: p = &one; break; + } + Address a(G3_scratch, (address)p); + __ sethi(a); + __ ldf(FloatRegisterImpl::D, a, Ftos_d); +} + + +// %%%%% Should factore most snippet templates across platforms + +void TemplateTable::bipush() { + transition(vtos, itos); + __ ldsb( at_bcp(1), Otos_i ); +} + +void TemplateTable::sipush() { + transition(vtos, itos); + __ get_2_byte_integer_at_bcp(1, G3_scratch, Otos_i, InterpreterMacroAssembler::Signed); +} + +void TemplateTable::ldc(bool wide) { + transition(vtos, vtos); + Label call_ldc, notInt, notString, notClass, exit; + + if (wide) { + __ get_2_byte_integer_at_bcp(1, G3_scratch, O1, InterpreterMacroAssembler::Unsigned); + } else { + __ ldub(Lbcp, 1, O1); + } + __ get_cpool_and_tags(O0, O2); + + const int base_offset = constantPoolOopDesc::header_size() * wordSize; + const int tags_offset = typeArrayOopDesc::header_size(T_BYTE) * wordSize; + + // get type from tags + __ add(O2, tags_offset, O2); + __ ldub(O2, O1, O2); + __ cmp(O2, JVM_CONSTANT_UnresolvedString); // unresolved string? If so, must resolve + __ brx(Assembler::equal, true, Assembler::pt, call_ldc); + __ delayed()->nop(); + + __ cmp(O2, JVM_CONSTANT_UnresolvedClass); // unresolved class? If so, must resolve + __ brx(Assembler::equal, true, Assembler::pt, call_ldc); + __ delayed()->nop(); + + __ cmp(O2, JVM_CONSTANT_UnresolvedClassInError); // unresolved class in error state + __ brx(Assembler::equal, true, Assembler::pn, call_ldc); + __ delayed()->nop(); + + __ cmp(O2, JVM_CONSTANT_Class); // need to call vm to get java mirror of the class + __ brx(Assembler::notEqual, true, Assembler::pt, notClass); + __ delayed()->add(O0, base_offset, O0); + + __ bind(call_ldc); + __ set(wide, O1); + call_VM(Otos_i, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), O1); + __ push(atos); + __ ba(false, exit); + __ delayed()->nop(); + + __ bind(notClass); + // __ add(O0, base_offset, O0); + __ sll(O1, LogBytesPerWord, O1); + __ cmp(O2, JVM_CONSTANT_Integer); + __ brx(Assembler::notEqual, true, Assembler::pt, notInt); + __ delayed()->cmp(O2, JVM_CONSTANT_String); + __ ld(O0, O1, Otos_i); + __ push(itos); + __ ba(false, exit); + __ delayed()->nop(); + + __ bind(notInt); + // __ cmp(O2, JVM_CONSTANT_String); + __ brx(Assembler::notEqual, true, Assembler::pt, notString); + __ delayed()->ldf(FloatRegisterImpl::S, O0, O1, Ftos_f); + __ ld_ptr(O0, O1, Otos_i); + __ verify_oop(Otos_i); + __ push(atos); + __ ba(false, exit); + __ delayed()->nop(); + + __ bind(notString); + // __ ldf(FloatRegisterImpl::S, O0, O1, Ftos_f); + __ push(ftos); + + __ bind(exit); +} + +void TemplateTable::ldc2_w() { + transition(vtos, vtos); + Label retry, resolved, Long, exit; + + __ bind(retry); + __ get_2_byte_integer_at_bcp(1, G3_scratch, O1, InterpreterMacroAssembler::Unsigned); + __ get_cpool_and_tags(O0, O2); + + const int base_offset = constantPoolOopDesc::header_size() * wordSize; + const int tags_offset = typeArrayOopDesc::header_size(T_BYTE) * wordSize; + // get type from tags + __ add(O2, tags_offset, O2); + __ ldub(O2, O1, O2); + + __ sll(O1, LogBytesPerWord, O1); + __ add(O0, O1, G3_scratch); + + __ cmp(O2, JVM_CONSTANT_Double); + __ brx(Assembler::notEqual, false, Assembler::pt, Long); + __ delayed()->nop(); + // A double can be placed at word-aligned locations in the constant pool. + // Check out Conversions.java for an example. + // Also constantPoolOopDesc::header_size() is 20, which makes it very difficult + // to double-align double on the constant pool. SG, 11/7/97 +#ifdef _LP64 + __ ldf(FloatRegisterImpl::D, G3_scratch, base_offset, Ftos_d); +#else + FloatRegister f = Ftos_d; + __ ldf(FloatRegisterImpl::S, G3_scratch, base_offset, f); + __ ldf(FloatRegisterImpl::S, G3_scratch, base_offset + sizeof(jdouble)/2, + f->successor()); +#endif + __ push(dtos); + __ ba(false, exit); + __ delayed()->nop(); + + __ bind(Long); +#ifdef _LP64 + __ ldx(G3_scratch, base_offset, Otos_l); +#else + __ ld(G3_scratch, base_offset, Otos_l); + __ ld(G3_scratch, base_offset + sizeof(jlong)/2, Otos_l->successor()); +#endif + __ push(ltos); + + __ bind(exit); +} + + +void TemplateTable::locals_index(Register reg, int offset) { + __ ldub( at_bcp(offset), reg ); +} + + +void TemplateTable::locals_index_wide(Register reg) { + // offset is 2, not 1, because Lbcp points to wide prefix code + __ get_2_byte_integer_at_bcp(2, G4_scratch, reg, InterpreterMacroAssembler::Unsigned); +} + +void TemplateTable::iload() { + transition(vtos, itos); + // Rewrite iload,iload pair into fast_iload2 + // iload,caload pair into fast_icaload + if (RewriteFrequentPairs) { + Label rewrite, done; + + // get next byte + __ ldub(at_bcp(Bytecodes::length_for(Bytecodes::_iload)), G3_scratch); + + // if _iload, wait to rewrite to iload2. We only want to rewrite the + // last two iloads in a pair. Comparing against fast_iload means that + // the next bytecode is neither an iload or a caload, and therefore + // an iload pair. + __ cmp(G3_scratch, (int)Bytecodes::_iload); + __ br(Assembler::equal, false, Assembler::pn, done); + __ delayed()->nop(); + + __ cmp(G3_scratch, (int)Bytecodes::_fast_iload); + __ br(Assembler::equal, false, Assembler::pn, rewrite); + __ delayed()->set(Bytecodes::_fast_iload2, G4_scratch); + + __ cmp(G3_scratch, (int)Bytecodes::_caload); + __ br(Assembler::equal, false, Assembler::pn, rewrite); + __ delayed()->set(Bytecodes::_fast_icaload, G4_scratch); + + __ set(Bytecodes::_fast_iload, G4_scratch); // don't check again + // rewrite + // G4_scratch: fast bytecode + __ bind(rewrite); + patch_bytecode(Bytecodes::_iload, G4_scratch, G3_scratch, false); + __ bind(done); + } + + // Get the local value into tos + locals_index(G3_scratch); + __ access_local_int( G3_scratch, Otos_i ); +} + +void TemplateTable::fast_iload2() { + transition(vtos, itos); + locals_index(G3_scratch); + __ access_local_int( G3_scratch, Otos_i ); + __ push_i(); + locals_index(G3_scratch, 3); // get next bytecode's local index. + __ access_local_int( G3_scratch, Otos_i ); +} + +void TemplateTable::fast_iload() { + transition(vtos, itos); + locals_index(G3_scratch); + __ access_local_int( G3_scratch, Otos_i ); +} + +void TemplateTable::lload() { + transition(vtos, ltos); + locals_index(G3_scratch); + __ access_local_long( G3_scratch, Otos_l ); +} + + +void TemplateTable::fload() { + transition(vtos, ftos); + locals_index(G3_scratch); + __ access_local_float( G3_scratch, Ftos_f ); +} + + +void TemplateTable::dload() { + transition(vtos, dtos); + locals_index(G3_scratch); + __ access_local_double( G3_scratch, Ftos_d ); +} + + +void TemplateTable::aload() { + transition(vtos, atos); + locals_index(G3_scratch); + __ access_local_ptr( G3_scratch, Otos_i); +} + + +void TemplateTable::wide_iload() { + transition(vtos, itos); + locals_index_wide(G3_scratch); + __ access_local_int( G3_scratch, Otos_i ); +} + + +void TemplateTable::wide_lload() { + transition(vtos, ltos); + locals_index_wide(G3_scratch); + __ access_local_long( G3_scratch, Otos_l ); +} + + +void TemplateTable::wide_fload() { + transition(vtos, ftos); + locals_index_wide(G3_scratch); + __ access_local_float( G3_scratch, Ftos_f ); +} + + +void TemplateTable::wide_dload() { + transition(vtos, dtos); + locals_index_wide(G3_scratch); + __ access_local_double( G3_scratch, Ftos_d ); +} + + +void TemplateTable::wide_aload() { + transition(vtos, atos); + locals_index_wide(G3_scratch); + __ access_local_ptr( G3_scratch, Otos_i ); + __ verify_oop(Otos_i); +} + + +void TemplateTable::iaload() { + transition(itos, itos); + // Otos_i: index + // tos: array + __ index_check(O2, Otos_i, LogBytesPerInt, G3_scratch, O3); + __ ld(O3, arrayOopDesc::base_offset_in_bytes(T_INT), Otos_i); +} + + +void TemplateTable::laload() { + transition(itos, ltos); + // Otos_i: index + // O2: array + __ index_check(O2, Otos_i, LogBytesPerLong, G3_scratch, O3); + __ ld_long(O3, arrayOopDesc::base_offset_in_bytes(T_LONG), Otos_l); +} + + +void TemplateTable::faload() { + transition(itos, ftos); + // Otos_i: index + // O2: array + __ index_check(O2, Otos_i, LogBytesPerInt, G3_scratch, O3); + __ ldf(FloatRegisterImpl::S, O3, arrayOopDesc::base_offset_in_bytes(T_FLOAT), Ftos_f); +} + + +void TemplateTable::daload() { + transition(itos, dtos); + // Otos_i: index + // O2: array + __ index_check(O2, Otos_i, LogBytesPerLong, G3_scratch, O3); + __ ldf(FloatRegisterImpl::D, O3, arrayOopDesc::base_offset_in_bytes(T_DOUBLE), Ftos_d); +} + + +void TemplateTable::aaload() { + transition(itos, atos); + // Otos_i: index + // tos: array + __ index_check(O2, Otos_i, LogBytesPerWord, G3_scratch, O3); + __ ld_ptr(O3, arrayOopDesc::base_offset_in_bytes(T_OBJECT), Otos_i); + __ verify_oop(Otos_i); +} + + +void TemplateTable::baload() { + transition(itos, itos); + // Otos_i: index + // tos: array + __ index_check(O2, Otos_i, 0, G3_scratch, O3); + __ ldsb(O3, arrayOopDesc::base_offset_in_bytes(T_BYTE), Otos_i); +} + + +void TemplateTable::caload() { + transition(itos, itos); + // Otos_i: index + // tos: array + __ index_check(O2, Otos_i, LogBytesPerShort, G3_scratch, O3); + __ lduh(O3, arrayOopDesc::base_offset_in_bytes(T_CHAR), Otos_i); +} + +void TemplateTable::fast_icaload() { + transition(vtos, itos); + // Otos_i: index + // tos: array + locals_index(G3_scratch); + __ access_local_int( G3_scratch, Otos_i ); + __ index_check(O2, Otos_i, LogBytesPerShort, G3_scratch, O3); + __ lduh(O3, arrayOopDesc::base_offset_in_bytes(T_CHAR), Otos_i); +} + + +void TemplateTable::saload() { + transition(itos, itos); + // Otos_i: index + // tos: array + __ index_check(O2, Otos_i, LogBytesPerShort, G3_scratch, O3); + __ ldsh(O3, arrayOopDesc::base_offset_in_bytes(T_SHORT), Otos_i); +} + + +void TemplateTable::iload(int n) { + transition(vtos, itos); + debug_only(__ verify_local_tag(frame::TagValue, Llocals, Otos_i, n)); + __ ld( Llocals, Interpreter::local_offset_in_bytes(n), Otos_i ); +} + + +void TemplateTable::lload(int n) { + transition(vtos, ltos); + assert(n+1 < Argument::n_register_parameters, "would need more code"); + debug_only(__ verify_local_tag(frame::TagCategory2, Llocals, Otos_l, n)); + __ load_unaligned_long(Llocals, Interpreter::local_offset_in_bytes(n+1), Otos_l); +} + + +void TemplateTable::fload(int n) { + transition(vtos, ftos); + assert(n < Argument::n_register_parameters, "would need more code"); + debug_only(__ verify_local_tag(frame::TagValue, Llocals, G3_scratch, n)); + __ ldf( FloatRegisterImpl::S, Llocals, Interpreter::local_offset_in_bytes(n), Ftos_f ); +} + + +void TemplateTable::dload(int n) { + transition(vtos, dtos); + FloatRegister dst = Ftos_d; + debug_only(__ verify_local_tag(frame::TagCategory2, Llocals, G3_scratch, n)); + __ load_unaligned_double(Llocals, Interpreter::local_offset_in_bytes(n+1), dst); +} + + +void TemplateTable::aload(int n) { + transition(vtos, atos); + debug_only(__ verify_local_tag(frame::TagReference, Llocals, Otos_i, n)); + __ ld_ptr( Llocals, Interpreter::local_offset_in_bytes(n), Otos_i ); +} + + +void TemplateTable::aload_0() { + transition(vtos, atos); + + // According to bytecode histograms, the pairs: + // + // _aload_0, _fast_igetfield (itos) + // _aload_0, _fast_agetfield (atos) + // _aload_0, _fast_fgetfield (ftos) + // + // occur frequently. If RewriteFrequentPairs is set, the (slow) _aload_0 + // bytecode checks the next bytecode and then rewrites the current + // bytecode into a pair bytecode; otherwise it rewrites the current + // bytecode into _fast_aload_0 that doesn't do the pair check anymore. + // + if (RewriteFrequentPairs) { + Label rewrite, done; + + // get next byte + __ ldub(at_bcp(Bytecodes::length_for(Bytecodes::_aload_0)), G3_scratch); + + // do actual aload_0 + aload(0); + + // if _getfield then wait with rewrite + __ cmp(G3_scratch, (int)Bytecodes::_getfield); + __ br(Assembler::equal, false, Assembler::pn, done); + __ delayed()->nop(); + + // if _igetfield then rewrite to _fast_iaccess_0 + assert(Bytecodes::java_code(Bytecodes::_fast_iaccess_0) == Bytecodes::_aload_0, "adjust fast bytecode def"); + __ cmp(G3_scratch, (int)Bytecodes::_fast_igetfield); + __ br(Assembler::equal, false, Assembler::pn, rewrite); + __ delayed()->set(Bytecodes::_fast_iaccess_0, G4_scratch); + + // if _agetfield then rewrite to _fast_aaccess_0 + assert(Bytecodes::java_code(Bytecodes::_fast_aaccess_0) == Bytecodes::_aload_0, "adjust fast bytecode def"); + __ cmp(G3_scratch, (int)Bytecodes::_fast_agetfield); + __ br(Assembler::equal, false, Assembler::pn, rewrite); + __ delayed()->set(Bytecodes::_fast_aaccess_0, G4_scratch); + + // if _fgetfield then rewrite to _fast_faccess_0 + assert(Bytecodes::java_code(Bytecodes::_fast_faccess_0) == Bytecodes::_aload_0, "adjust fast bytecode def"); + __ cmp(G3_scratch, (int)Bytecodes::_fast_fgetfield); + __ br(Assembler::equal, false, Assembler::pn, rewrite); + __ delayed()->set(Bytecodes::_fast_faccess_0, G4_scratch); + + // else rewrite to _fast_aload0 + assert(Bytecodes::java_code(Bytecodes::_fast_aload_0) == Bytecodes::_aload_0, "adjust fast bytecode def"); + __ set(Bytecodes::_fast_aload_0, G4_scratch); + + // rewrite + // G4_scratch: fast bytecode + __ bind(rewrite); + patch_bytecode(Bytecodes::_aload_0, G4_scratch, G3_scratch, false); + __ bind(done); + } else { + aload(0); + } +} + + +void TemplateTable::istore() { + transition(itos, vtos); + locals_index(G3_scratch); + __ store_local_int( G3_scratch, Otos_i ); +} + + +void TemplateTable::lstore() { + transition(ltos, vtos); + locals_index(G3_scratch); + __ store_local_long( G3_scratch, Otos_l ); +} + + +void TemplateTable::fstore() { + transition(ftos, vtos); + locals_index(G3_scratch); + __ store_local_float( G3_scratch, Ftos_f ); +} + + +void TemplateTable::dstore() { + transition(dtos, vtos); + locals_index(G3_scratch); + __ store_local_double( G3_scratch, Ftos_d ); +} + + +void TemplateTable::astore() { + transition(vtos, vtos); + // astore tos can also be a returnAddress, so load and store the tag too + __ load_ptr_and_tag(0, Otos_i, Otos_l2); + __ inc(Lesp, Interpreter::stackElementSize()); + __ verify_oop_or_return_address(Otos_i, G3_scratch); + locals_index(G3_scratch); + __ store_local_ptr( G3_scratch, Otos_i, Otos_l2 ); +} + + +void TemplateTable::wide_istore() { + transition(vtos, vtos); + __ pop_i(); + locals_index_wide(G3_scratch); + __ store_local_int( G3_scratch, Otos_i ); +} + + +void TemplateTable::wide_lstore() { + transition(vtos, vtos); + __ pop_l(); + locals_index_wide(G3_scratch); + __ store_local_long( G3_scratch, Otos_l ); +} + + +void TemplateTable::wide_fstore() { + transition(vtos, vtos); + __ pop_f(); + locals_index_wide(G3_scratch); + __ store_local_float( G3_scratch, Ftos_f ); +} + + +void TemplateTable::wide_dstore() { + transition(vtos, vtos); + __ pop_d(); + locals_index_wide(G3_scratch); + __ store_local_double( G3_scratch, Ftos_d ); +} + + +void TemplateTable::wide_astore() { + transition(vtos, vtos); + // astore tos can also be a returnAddress, so load and store the tag too + __ load_ptr_and_tag(0, Otos_i, Otos_l2); + __ inc(Lesp, Interpreter::stackElementSize()); + __ verify_oop_or_return_address(Otos_i, G3_scratch); + locals_index_wide(G3_scratch); + __ store_local_ptr( G3_scratch, Otos_i, Otos_l2 ); +} + + +void TemplateTable::iastore() { + transition(itos, vtos); + __ pop_i(O2); // index + // Otos_i: val + // O3: array + __ index_check(O3, O2, LogBytesPerInt, G3_scratch, O2); + __ st(Otos_i, O2, arrayOopDesc::base_offset_in_bytes(T_INT)); +} + + +void TemplateTable::lastore() { + transition(ltos, vtos); + __ pop_i(O2); // index + // Otos_l: val + // O3: array + __ index_check(O3, O2, LogBytesPerLong, G3_scratch, O2); + __ st_long(Otos_l, O2, arrayOopDesc::base_offset_in_bytes(T_LONG)); +} + + +void TemplateTable::fastore() { + transition(ftos, vtos); + __ pop_i(O2); // index + // Ftos_f: val + // O3: array + __ index_check(O3, O2, LogBytesPerInt, G3_scratch, O2); + __ stf(FloatRegisterImpl::S, Ftos_f, O2, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); +} + + +void TemplateTable::dastore() { + transition(dtos, vtos); + __ pop_i(O2); // index + // Fos_d: val + // O3: array + __ index_check(O3, O2, LogBytesPerLong, G3_scratch, O2); + __ stf(FloatRegisterImpl::D, Ftos_d, O2, arrayOopDesc::base_offset_in_bytes(T_DOUBLE)); +} + + +void TemplateTable::aastore() { + Label store_ok, is_null, done; + transition(vtos, vtos); + __ ld_ptr(Lesp, Interpreter::expr_offset_in_bytes(0), Otos_i); + __ ld(Lesp, Interpreter::expr_offset_in_bytes(1), O2); // get index + __ ld_ptr(Lesp, Interpreter::expr_offset_in_bytes(2), O3); // get array + // Otos_i: val + // O2: index + // O3: array + __ verify_oop(Otos_i); + __ index_check_without_pop(O3, O2, LogBytesPerWord, G3_scratch, O1); + + // do array store check - check for NULL value first + __ br_null( Otos_i, false, Assembler::pn, is_null ); + __ delayed()-> + ld_ptr(O3, oopDesc::klass_offset_in_bytes(), O4); // get array klass + + // do fast instanceof cache test + __ ld_ptr(Otos_i, oopDesc::klass_offset_in_bytes(), O5); // get value klass + + __ ld_ptr(O4, sizeof(oopDesc) + objArrayKlass::element_klass_offset_in_bytes(), O4); + + assert(Otos_i == O0, "just checking"); + + // Otos_i: value + // O1: addr - offset + // O2: index + // O3: array + // O4: array element klass + // O5: value klass + + // Generate a fast subtype check. Branch to store_ok if no + // failure. Throw if failure. + __ gen_subtype_check( O5, O4, G3_scratch, G4_scratch, G1_scratch, store_ok ); + + // Not a subtype; so must throw exception + __ throw_if_not_x( Assembler::never, Interpreter::_throw_ArrayStoreException_entry, G3_scratch ); + + // Store is OK. + __ bind(store_ok); + __ st_ptr(Otos_i, O1, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); + // Quote from rememberedSet.hpp: For objArrays, the precise card + // corresponding to the pointer store is dirtied so we don't need to + // scavenge the entire array. + Address element(O1, 0, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); + __ add(element, O1); // address the element precisely + __ store_check(G3_scratch, O1); + __ ba(false,done); + __ delayed()->inc(Lesp, 3* Interpreter::stackElementSize()); // adj sp (pops array, index and value) + + __ bind(is_null); + __ st_ptr(Otos_i, element); + __ profile_null_seen(G3_scratch); + __ inc(Lesp, 3* Interpreter::stackElementSize()); // adj sp (pops array, index and value) + __ bind(done); +} + + +void TemplateTable::bastore() { + transition(itos, vtos); + __ pop_i(O2); // index + // Otos_i: val + // O3: array + __ index_check(O3, O2, 0, G3_scratch, O2); + __ stb(Otos_i, O2, arrayOopDesc::base_offset_in_bytes(T_BYTE)); +} + + +void TemplateTable::castore() { + transition(itos, vtos); + __ pop_i(O2); // index + // Otos_i: val + // O3: array + __ index_check(O3, O2, LogBytesPerShort, G3_scratch, O2); + __ sth(Otos_i, O2, arrayOopDesc::base_offset_in_bytes(T_CHAR)); +} + + +void TemplateTable::sastore() { + // %%%%% Factor across platform + castore(); +} + + +void TemplateTable::istore(int n) { + transition(itos, vtos); + __ tag_local(frame::TagValue, Llocals, Otos_i, n); + __ st(Otos_i, Llocals, Interpreter::local_offset_in_bytes(n)); +} + + +void TemplateTable::lstore(int n) { + transition(ltos, vtos); + assert(n+1 < Argument::n_register_parameters, "only handle register cases"); + __ tag_local(frame::TagCategory2, Llocals, Otos_l, n); + __ store_unaligned_long(Otos_l, Llocals, Interpreter::local_offset_in_bytes(n+1)); + +} + + +void TemplateTable::fstore(int n) { + transition(ftos, vtos); + assert(n < Argument::n_register_parameters, "only handle register cases"); + __ tag_local(frame::TagValue, Llocals, Otos_l, n); + __ stf(FloatRegisterImpl::S, Ftos_f, Llocals, Interpreter::local_offset_in_bytes(n)); +} + + +void TemplateTable::dstore(int n) { + transition(dtos, vtos); + FloatRegister src = Ftos_d; + __ tag_local(frame::TagCategory2, Llocals, Otos_l, n); + __ store_unaligned_double(src, Llocals, Interpreter::local_offset_in_bytes(n+1)); +} + + +void TemplateTable::astore(int n) { + transition(vtos, vtos); + // astore tos can also be a returnAddress, so load and store the tag too + __ load_ptr_and_tag(0, Otos_i, Otos_l2); + __ inc(Lesp, Interpreter::stackElementSize()); + __ verify_oop_or_return_address(Otos_i, G3_scratch); + __ store_local_ptr( n, Otos_i, Otos_l2 ); +} + + +void TemplateTable::pop() { + transition(vtos, vtos); + __ inc(Lesp, Interpreter::stackElementSize()); +} + + +void TemplateTable::pop2() { + transition(vtos, vtos); + __ inc(Lesp, 2 * Interpreter::stackElementSize()); +} + + +void TemplateTable::dup() { + transition(vtos, vtos); + // stack: ..., a + // load a and tag + __ load_ptr_and_tag(0, Otos_i, Otos_l2); + __ push_ptr(Otos_i, Otos_l2); + // stack: ..., a, a +} + + +void TemplateTable::dup_x1() { + transition(vtos, vtos); + // stack: ..., a, b + __ load_ptr_and_tag(1, G3_scratch, G4_scratch); // get a + __ load_ptr_and_tag(0, Otos_l1, Otos_l2); // get b + __ store_ptr_and_tag(1, Otos_l1, Otos_l2); // put b + __ store_ptr_and_tag(0, G3_scratch, G4_scratch); // put a - like swap + __ push_ptr(Otos_l1, Otos_l2); // push b + // stack: ..., b, a, b +} + + +void TemplateTable::dup_x2() { + transition(vtos, vtos); + // stack: ..., a, b, c + // get c and push on stack, reuse registers + __ load_ptr_and_tag(0, G3_scratch, G4_scratch); // get c + __ push_ptr(G3_scratch, G4_scratch); // push c with tag + // stack: ..., a, b, c, c (c in reg) (Lesp - 4) + // (stack offsets n+1 now) + __ load_ptr_and_tag(3, Otos_l1, Otos_l2); // get a + __ store_ptr_and_tag(3, G3_scratch, G4_scratch); // put c at 3 + // stack: ..., c, b, c, c (a in reg) + __ load_ptr_and_tag(2, G3_scratch, G4_scratch); // get b + __ store_ptr_and_tag(2, Otos_l1, Otos_l2); // put a at 2 + // stack: ..., c, a, c, c (b in reg) + __ store_ptr_and_tag(1, G3_scratch, G4_scratch); // put b at 1 + // stack: ..., c, a, b, c +} + + +void TemplateTable::dup2() { + transition(vtos, vtos); + __ load_ptr_and_tag(1, G3_scratch, G4_scratch); // get a + __ load_ptr_and_tag(0, Otos_l1, Otos_l2); // get b + __ push_ptr(G3_scratch, G4_scratch); // push a + __ push_ptr(Otos_l1, Otos_l2); // push b + // stack: ..., a, b, a, b +} + + +void TemplateTable::dup2_x1() { + transition(vtos, vtos); + // stack: ..., a, b, c + __ load_ptr_and_tag(1, Lscratch, G1_scratch); // get b + __ load_ptr_and_tag(2, Otos_l1, Otos_l2); // get a + __ store_ptr_and_tag(2, Lscratch, G1_scratch); // put b at a + // stack: ..., b, b, c + __ load_ptr_and_tag(0, G3_scratch, G4_scratch); // get c + __ store_ptr_and_tag(1, G3_scratch, G4_scratch); // put c at b + // stack: ..., b, c, c + __ store_ptr_and_tag(0, Otos_l1, Otos_l2); // put a at c + // stack: ..., b, c, a + __ push_ptr(Lscratch, G1_scratch); // push b + __ push_ptr(G3_scratch, G4_scratch); // push c + // stack: ..., b, c, a, b, c +} + + +// The spec says that these types can be a mixture of category 1 (1 word) +// types and/or category 2 types (long and doubles) +void TemplateTable::dup2_x2() { + transition(vtos, vtos); + // stack: ..., a, b, c, d + __ load_ptr_and_tag(1, Lscratch, G1_scratch); // get c + __ load_ptr_and_tag(3, Otos_l1, Otos_l2); // get a + __ store_ptr_and_tag(3, Lscratch, G1_scratch); // put c at 3 + __ store_ptr_and_tag(1, Otos_l1, Otos_l2); // put a at 1 + // stack: ..., c, b, a, d + __ load_ptr_and_tag(2, G3_scratch, G4_scratch); // get b + __ load_ptr_and_tag(0, Otos_l1, Otos_l2); // get d + __ store_ptr_and_tag(0, G3_scratch, G4_scratch); // put b at 0 + __ store_ptr_and_tag(2, Otos_l1, Otos_l2); // put d at 2 + // stack: ..., c, d, a, b + __ push_ptr(Lscratch, G1_scratch); // push c + __ push_ptr(Otos_l1, Otos_l2); // push d + // stack: ..., c, d, a, b, c, d +} + + +void TemplateTable::swap() { + transition(vtos, vtos); + // stack: ..., a, b + __ load_ptr_and_tag(1, G3_scratch, G4_scratch); // get a + __ load_ptr_and_tag(0, Otos_l1, Otos_l2); // get b + __ store_ptr_and_tag(0, G3_scratch, G4_scratch); // put b + __ store_ptr_and_tag(1, Otos_l1, Otos_l2); // put a + // stack: ..., b, a +} + + +void TemplateTable::iop2(Operation op) { + transition(itos, itos); + __ pop_i(O1); + switch (op) { + case add: __ add(O1, Otos_i, Otos_i); break; + case sub: __ sub(O1, Otos_i, Otos_i); break; + // %%%%% Mul may not exist: better to call .mul? + case mul: __ smul(O1, Otos_i, Otos_i); break; + case _and: __ and3(O1, Otos_i, Otos_i); break; + case _or: __ or3(O1, Otos_i, Otos_i); break; + case _xor: __ xor3(O1, Otos_i, Otos_i); break; + case shl: __ sll(O1, Otos_i, Otos_i); break; + case shr: __ sra(O1, Otos_i, Otos_i); break; + case ushr: __ srl(O1, Otos_i, Otos_i); break; + default: ShouldNotReachHere(); + } +} + + +void TemplateTable::lop2(Operation op) { + transition(ltos, ltos); + __ pop_l(O2); + switch (op) { +#ifdef _LP64 + case add: __ add(O2, Otos_l, Otos_l); break; + case sub: __ sub(O2, Otos_l, Otos_l); break; + case _and: __ and3( O2, Otos_l, Otos_l); break; + case _or: __ or3( O2, Otos_l, Otos_l); break; + case _xor: __ xor3( O2, Otos_l, Otos_l); break; +#else + case add: __ addcc(O3, Otos_l2, Otos_l2); __ addc(O2, Otos_l1, Otos_l1); break; + case sub: __ subcc(O3, Otos_l2, Otos_l2); __ subc(O2, Otos_l1, Otos_l1); break; + case _and: __ and3( O3, Otos_l2, Otos_l2); __ and3( O2, Otos_l1, Otos_l1); break; + case _or: __ or3( O3, Otos_l2, Otos_l2); __ or3( O2, Otos_l1, Otos_l1); break; + case _xor: __ xor3( O3, Otos_l2, Otos_l2); __ xor3( O2, Otos_l1, Otos_l1); break; +#endif + default: ShouldNotReachHere(); + } +} + + +void TemplateTable::idiv() { + // %%%%% Later: ForSPARC/V7 call .sdiv library routine, + // %%%%% Use ldsw...sdivx on pure V9 ABI. 64 bit safe. + + transition(itos, itos); + __ pop_i(O1); // get 1st op + + // Y contains upper 32 bits of result, set it to 0 or all ones + __ wry(G0); + __ mov(~0, G3_scratch); + + __ tst(O1); + Label neg; + __ br(Assembler::negative, true, Assembler::pn, neg); + __ delayed()->wry(G3_scratch); + __ bind(neg); + + Label ok; + __ tst(Otos_i); + __ throw_if_not_icc( Assembler::notZero, Interpreter::_throw_ArithmeticException_entry, G3_scratch ); + + const int min_int = 0x80000000; + Label regular; + __ cmp(Otos_i, -1); + __ br(Assembler::notEqual, false, Assembler::pt, regular); +#ifdef _LP64 + // Don't put set in delay slot + // Set will turn into multiple instructions in 64 bit mode + __ delayed()->nop(); + __ set(min_int, G4_scratch); +#else + __ delayed()->set(min_int, G4_scratch); +#endif + Label done; + __ cmp(O1, G4_scratch); + __ br(Assembler::equal, true, Assembler::pt, done); + __ delayed()->mov(O1, Otos_i); // (mov only executed if branch taken) + + __ bind(regular); + __ sdiv(O1, Otos_i, Otos_i); // note: irem uses O1 after this instruction! + __ bind(done); +} + + +void TemplateTable::irem() { + transition(itos, itos); + __ mov(Otos_i, O2); // save divisor + idiv(); // %%%% Hack: exploits fact that idiv leaves dividend in O1 + __ smul(Otos_i, O2, Otos_i); + __ sub(O1, Otos_i, Otos_i); +} + + +void TemplateTable::lmul() { + transition(ltos, ltos); + __ pop_l(O2); +#ifdef _LP64 + __ mulx(Otos_l, O2, Otos_l); +#else + __ call_VM_leaf(Lscratch, CAST_FROM_FN_PTR(address, SharedRuntime::lmul)); +#endif + +} + + +void TemplateTable::ldiv() { + transition(ltos, ltos); + + // check for zero + __ pop_l(O2); +#ifdef _LP64 + __ tst(Otos_l); + __ throw_if_not_xcc( Assembler::notZero, Interpreter::_throw_ArithmeticException_entry, G3_scratch); + __ sdivx(O2, Otos_l, Otos_l); +#else + __ orcc(Otos_l1, Otos_l2, G0); + __ throw_if_not_icc( Assembler::notZero, Interpreter::_throw_ArithmeticException_entry, G3_scratch); + __ call_VM_leaf(Lscratch, CAST_FROM_FN_PTR(address, SharedRuntime::ldiv)); +#endif +} + + +void TemplateTable::lrem() { + transition(ltos, ltos); + + // check for zero + __ pop_l(O2); +#ifdef _LP64 + __ tst(Otos_l); + __ throw_if_not_xcc( Assembler::notZero, Interpreter::_throw_ArithmeticException_entry, G3_scratch); + __ sdivx(O2, Otos_l, Otos_l2); + __ mulx (Otos_l2, Otos_l, Otos_l2); + __ sub (O2, Otos_l2, Otos_l); +#else + __ orcc(Otos_l1, Otos_l2, G0); + __ throw_if_not_icc(Assembler::notZero, Interpreter::_throw_ArithmeticException_entry, G3_scratch); + __ call_VM_leaf(Lscratch, CAST_FROM_FN_PTR(address, SharedRuntime::lrem)); +#endif +} + + +void TemplateTable::lshl() { + transition(itos, ltos); // %%%% could optimize, fill delay slot or opt for ultra + + __ pop_l(O2); // shift value in O2, O3 +#ifdef _LP64 + __ sllx(O2, Otos_i, Otos_l); +#else + __ lshl(O2, O3, Otos_i, Otos_l1, Otos_l2, O4); +#endif +} + + +void TemplateTable::lshr() { + transition(itos, ltos); // %%%% see lshl comment + + __ pop_l(O2); // shift value in O2, O3 +#ifdef _LP64 + __ srax(O2, Otos_i, Otos_l); +#else + __ lshr(O2, O3, Otos_i, Otos_l1, Otos_l2, O4); +#endif +} + + + +void TemplateTable::lushr() { + transition(itos, ltos); // %%%% see lshl comment + + __ pop_l(O2); // shift value in O2, O3 +#ifdef _LP64 + __ srlx(O2, Otos_i, Otos_l); +#else + __ lushr(O2, O3, Otos_i, Otos_l1, Otos_l2, O4); +#endif +} + + +void TemplateTable::fop2(Operation op) { + transition(ftos, ftos); + switch (op) { + case add: __ pop_f(F4); __ fadd(FloatRegisterImpl::S, F4, Ftos_f, Ftos_f); break; + case sub: __ pop_f(F4); __ fsub(FloatRegisterImpl::S, F4, Ftos_f, Ftos_f); break; + case mul: __ pop_f(F4); __ fmul(FloatRegisterImpl::S, F4, Ftos_f, Ftos_f); break; + case div: __ pop_f(F4); __ fdiv(FloatRegisterImpl::S, F4, Ftos_f, Ftos_f); break; + case rem: + assert(Ftos_f == F0, "just checking"); +#ifdef _LP64 + // LP64 calling conventions use F1, F3 for passing 2 floats + __ pop_f(F1); + __ fmov(FloatRegisterImpl::S, Ftos_f, F3); +#else + __ pop_i(O0); + __ stf(FloatRegisterImpl::S, Ftos_f, __ d_tmp); + __ ld( __ d_tmp, O1 ); +#endif + __ call_VM_leaf(Lscratch, CAST_FROM_FN_PTR(address, SharedRuntime::frem)); + assert( Ftos_f == F0, "fix this code" ); + break; + + default: ShouldNotReachHere(); + } +} + + +void TemplateTable::dop2(Operation op) { + transition(dtos, dtos); + switch (op) { + case add: __ pop_d(F4); __ fadd(FloatRegisterImpl::D, F4, Ftos_d, Ftos_d); break; + case sub: __ pop_d(F4); __ fsub(FloatRegisterImpl::D, F4, Ftos_d, Ftos_d); break; + case mul: __ pop_d(F4); __ fmul(FloatRegisterImpl::D, F4, Ftos_d, Ftos_d); break; + case div: __ pop_d(F4); __ fdiv(FloatRegisterImpl::D, F4, Ftos_d, Ftos_d); break; + case rem: +#ifdef _LP64 + // Pass arguments in D0, D2 + __ fmov(FloatRegisterImpl::D, Ftos_f, F2 ); + __ pop_d( F0 ); +#else + // Pass arguments in O0O1, O2O3 + __ stf(FloatRegisterImpl::D, Ftos_f, __ d_tmp); + __ ldd( __ d_tmp, O2 ); + __ pop_d(Ftos_f); + __ stf(FloatRegisterImpl::D, Ftos_f, __ d_tmp); + __ ldd( __ d_tmp, O0 ); +#endif + __ call_VM_leaf(Lscratch, CAST_FROM_FN_PTR(address, SharedRuntime::drem)); + assert( Ftos_d == F0, "fix this code" ); + break; + + default: ShouldNotReachHere(); + } +} + + +void TemplateTable::ineg() { + transition(itos, itos); + __ neg(Otos_i); +} + + +void TemplateTable::lneg() { + transition(ltos, ltos); +#ifdef _LP64 + __ sub(G0, Otos_l, Otos_l); +#else + __ lneg(Otos_l1, Otos_l2); +#endif +} + + +void TemplateTable::fneg() { + transition(ftos, ftos); + __ fneg(FloatRegisterImpl::S, Ftos_f); +} + + +void TemplateTable::dneg() { + transition(dtos, dtos); + // v8 has fnegd if source and dest are the same + __ fneg(FloatRegisterImpl::D, Ftos_f); +} + + +void TemplateTable::iinc() { + transition(vtos, vtos); + locals_index(G3_scratch); + __ ldsb(Lbcp, 2, O2); // load constant + __ access_local_int(G3_scratch, Otos_i); + __ add(Otos_i, O2, Otos_i); + __ st(Otos_i, G3_scratch, Interpreter::value_offset_in_bytes()); // access_local_int puts E.A. in G3_scratch +} + + +void TemplateTable::wide_iinc() { + transition(vtos, vtos); + locals_index_wide(G3_scratch); + __ get_2_byte_integer_at_bcp( 4, O2, O3, InterpreterMacroAssembler::Signed); + __ access_local_int(G3_scratch, Otos_i); + __ add(Otos_i, O3, Otos_i); + __ st(Otos_i, G3_scratch, Interpreter::value_offset_in_bytes()); // access_local_int puts E.A. in G3_scratch +} + + +void TemplateTable::convert() { +// %%%%% Factor this first part accross platforms + #ifdef ASSERT + TosState tos_in = ilgl; + TosState tos_out = ilgl; + switch (bytecode()) { + case Bytecodes::_i2l: // fall through + case Bytecodes::_i2f: // fall through + case Bytecodes::_i2d: // fall through + case Bytecodes::_i2b: // fall through + case Bytecodes::_i2c: // fall through + case Bytecodes::_i2s: tos_in = itos; break; + case Bytecodes::_l2i: // fall through + case Bytecodes::_l2f: // fall through + case Bytecodes::_l2d: tos_in = ltos; break; + case Bytecodes::_f2i: // fall through + case Bytecodes::_f2l: // fall through + case Bytecodes::_f2d: tos_in = ftos; break; + case Bytecodes::_d2i: // fall through + case Bytecodes::_d2l: // fall through + case Bytecodes::_d2f: tos_in = dtos; break; + default : ShouldNotReachHere(); + } + switch (bytecode()) { + case Bytecodes::_l2i: // fall through + case Bytecodes::_f2i: // fall through + case Bytecodes::_d2i: // fall through + case Bytecodes::_i2b: // fall through + case Bytecodes::_i2c: // fall through + case Bytecodes::_i2s: tos_out = itos; break; + case Bytecodes::_i2l: // fall through + case Bytecodes::_f2l: // fall through + case Bytecodes::_d2l: tos_out = ltos; break; + case Bytecodes::_i2f: // fall through + case Bytecodes::_l2f: // fall through + case Bytecodes::_d2f: tos_out = ftos; break; + case Bytecodes::_i2d: // fall through + case Bytecodes::_l2d: // fall through + case Bytecodes::_f2d: tos_out = dtos; break; + default : ShouldNotReachHere(); + } + transition(tos_in, tos_out); + #endif + + + // Conversion + Label done; + switch (bytecode()) { + case Bytecodes::_i2l: +#ifdef _LP64 + // Sign extend the 32 bits + __ sra ( Otos_i, 0, Otos_l ); +#else + __ addcc(Otos_i, 0, Otos_l2); + __ br(Assembler::greaterEqual, true, Assembler::pt, done); + __ delayed()->clr(Otos_l1); + __ set(~0, Otos_l1); +#endif + break; + + case Bytecodes::_i2f: + __ st(Otos_i, __ d_tmp ); + __ ldf(FloatRegisterImpl::S, __ d_tmp, F0); + __ fitof(FloatRegisterImpl::S, F0, Ftos_f); + break; + + case Bytecodes::_i2d: + __ st(Otos_i, __ d_tmp); + __ ldf(FloatRegisterImpl::S, __ d_tmp, F0); + __ fitof(FloatRegisterImpl::D, F0, Ftos_f); + break; + + case Bytecodes::_i2b: + __ sll(Otos_i, 24, Otos_i); + __ sra(Otos_i, 24, Otos_i); + break; + + case Bytecodes::_i2c: + __ sll(Otos_i, 16, Otos_i); + __ srl(Otos_i, 16, Otos_i); + break; + + case Bytecodes::_i2s: + __ sll(Otos_i, 16, Otos_i); + __ sra(Otos_i, 16, Otos_i); + break; + + case Bytecodes::_l2i: +#ifndef _LP64 + __ mov(Otos_l2, Otos_i); +#else + // Sign-extend into the high 32 bits + __ sra(Otos_l, 0, Otos_i); +#endif + break; + + case Bytecodes::_l2f: + case Bytecodes::_l2d: + __ st_long(Otos_l, __ d_tmp); + __ ldf(FloatRegisterImpl::D, __ d_tmp, Ftos_d); + + if (VM_Version::v9_instructions_work()) { + if (bytecode() == Bytecodes::_l2f) { + __ fxtof(FloatRegisterImpl::S, Ftos_d, Ftos_f); + } else { + __ fxtof(FloatRegisterImpl::D, Ftos_d, Ftos_d); + } + } else { + __ call_VM_leaf( + Lscratch, + bytecode() == Bytecodes::_l2f + ? CAST_FROM_FN_PTR(address, SharedRuntime::l2f) + : CAST_FROM_FN_PTR(address, SharedRuntime::l2d) + ); + } + break; + + case Bytecodes::_f2i: { + Label isNaN; + // result must be 0 if value is NaN; test by comparing value to itself + __ fcmp(FloatRegisterImpl::S, Assembler::fcc0, Ftos_f, Ftos_f); + // According to the v8 manual, you have to have a non-fp instruction + // between fcmp and fb. + if (!VM_Version::v9_instructions_work()) { + __ nop(); + } + __ fb(Assembler::f_unordered, true, Assembler::pn, isNaN); + __ delayed()->clr(Otos_i); // NaN + __ ftoi(FloatRegisterImpl::S, Ftos_f, F30); + __ stf(FloatRegisterImpl::S, F30, __ d_tmp); + __ ld(__ d_tmp, Otos_i); + __ bind(isNaN); + } + break; + + case Bytecodes::_f2l: + // must uncache tos + __ push_f(); +#ifdef _LP64 + __ pop_f(F1); +#else + __ pop_i(O0); +#endif + __ call_VM_leaf(Lscratch, CAST_FROM_FN_PTR(address, SharedRuntime::f2l)); + break; + + case Bytecodes::_f2d: + __ ftof( FloatRegisterImpl::S, FloatRegisterImpl::D, Ftos_f, Ftos_f); + break; + + case Bytecodes::_d2i: + case Bytecodes::_d2l: + // must uncache tos + __ push_d(); +#ifdef _LP64 + // LP64 calling conventions pass first double arg in D0 + __ pop_d( Ftos_d ); +#else + __ pop_i( O0 ); + __ pop_i( O1 ); +#endif + __ call_VM_leaf(Lscratch, + bytecode() == Bytecodes::_d2i + ? CAST_FROM_FN_PTR(address, SharedRuntime::d2i) + : CAST_FROM_FN_PTR(address, SharedRuntime::d2l)); + break; + + case Bytecodes::_d2f: + if (VM_Version::v9_instructions_work()) { + __ ftof( FloatRegisterImpl::D, FloatRegisterImpl::S, Ftos_d, Ftos_f); + } + else { + // must uncache tos + __ push_d(); + __ pop_i(O0); + __ pop_i(O1); + __ call_VM_leaf(Lscratch, CAST_FROM_FN_PTR(address, SharedRuntime::d2f)); + } + break; + + default: ShouldNotReachHere(); + } + __ bind(done); +} + + +void TemplateTable::lcmp() { + transition(ltos, itos); + +#ifdef _LP64 + __ pop_l(O1); // pop off value 1, value 2 is in O0 + __ lcmp( O1, Otos_l, Otos_i ); +#else + __ pop_l(O2); // cmp O2,3 to O0,1 + __ lcmp( O2, O3, Otos_l1, Otos_l2, Otos_i ); +#endif +} + + +void TemplateTable::float_cmp(bool is_float, int unordered_result) { + + if (is_float) __ pop_f(F2); + else __ pop_d(F2); + + assert(Ftos_f == F0 && Ftos_d == F0, "alias checking:"); + + __ float_cmp( is_float, unordered_result, F2, F0, Otos_i ); +} + +void TemplateTable::branch(bool is_jsr, bool is_wide) { + // Note: on SPARC, we use InterpreterMacroAssembler::if_cmp also. + __ verify_oop(Lmethod); + __ verify_thread(); + + const Register O2_bumped_count = O2; + __ profile_taken_branch(G3_scratch, O2_bumped_count); + + // get (wide) offset to O1_disp + const Register O1_disp = O1; + if (is_wide) __ get_4_byte_integer_at_bcp( 1, G4_scratch, O1_disp, InterpreterMacroAssembler::set_CC); + else __ get_2_byte_integer_at_bcp( 1, G4_scratch, O1_disp, InterpreterMacroAssembler::Signed, InterpreterMacroAssembler::set_CC); + + // Handle all the JSR stuff here, then exit. + // It's much shorter and cleaner than intermingling with the + // non-JSR normal-branch stuff occuring below. + if( is_jsr ) { + // compute return address as bci in Otos_i + __ ld_ptr(Address(Lmethod, 0, in_bytes(methodOopDesc::const_offset())), G3_scratch); + __ sub(Lbcp, G3_scratch, G3_scratch); + __ sub(G3_scratch, in_bytes(constMethodOopDesc::codes_offset()) - (is_wide ? 5 : 3), Otos_i); + + // Bump Lbcp to target of JSR + __ add(Lbcp, O1_disp, Lbcp); + // Push returnAddress for "ret" on stack + __ push_ptr(Otos_i, G0); // push ptr sized thing plus 0 for tag. + // And away we go! + __ dispatch_next(vtos); + return; + } + + // Normal (non-jsr) branch handling + + // Save the current Lbcp + const Register O0_cur_bcp = O0; + __ mov( Lbcp, O0_cur_bcp ); + + bool increment_invocation_counter_for_backward_branches = UseCompiler && UseLoopCounter; + if ( increment_invocation_counter_for_backward_branches ) { + Label Lforward; + // check branch direction + __ br( Assembler::positive, false, Assembler::pn, Lforward ); + // Bump bytecode pointer by displacement (take the branch) + __ delayed()->add( O1_disp, Lbcp, Lbcp ); // add to bc addr + + // Update Backedge branch separately from invocations + const Register G4_invoke_ctr = G4; + __ increment_backedge_counter(G4_invoke_ctr, G1_scratch); + if (ProfileInterpreter) { + __ test_invocation_counter_for_mdp(G4_invoke_ctr, Lbcp, G3_scratch, Lforward); + if (UseOnStackReplacement) { + __ test_backedge_count_for_osr(O2_bumped_count, O0_cur_bcp, G3_scratch); + } + } else { + if (UseOnStackReplacement) { + __ test_backedge_count_for_osr(G4_invoke_ctr, O0_cur_bcp, G3_scratch); + } + } + + __ bind(Lforward); + } else + // Bump bytecode pointer by displacement (take the branch) + __ add( O1_disp, Lbcp, Lbcp );// add to bc addr + + // continue with bytecode @ target + // %%%%% Like Intel, could speed things up by moving bytecode fetch to code above, + // %%%%% and changing dispatch_next to dispatch_only + __ dispatch_next(vtos); +} + + +// Note Condition in argument is TemplateTable::Condition +// arg scope is within class scope + +void TemplateTable::if_0cmp(Condition cc) { + // no pointers, integer only! + transition(itos, vtos); + // assume branch is more often taken than not (loops use backward branches) + __ cmp( Otos_i, 0); + __ if_cmp(ccNot(cc), false); +} + + +void TemplateTable::if_icmp(Condition cc) { + transition(itos, vtos); + __ pop_i(O1); + __ cmp(O1, Otos_i); + __ if_cmp(ccNot(cc), false); +} + + +void TemplateTable::if_nullcmp(Condition cc) { + transition(atos, vtos); + __ tst(Otos_i); + __ if_cmp(ccNot(cc), true); +} + + +void TemplateTable::if_acmp(Condition cc) { + transition(atos, vtos); + __ pop_ptr(O1); + __ verify_oop(O1); + __ verify_oop(Otos_i); + __ cmp(O1, Otos_i); + __ if_cmp(ccNot(cc), true); +} + + + +void TemplateTable::ret() { + transition(vtos, vtos); + locals_index(G3_scratch); + __ access_local_returnAddress(G3_scratch, Otos_i); + // Otos_i contains the bci, compute the bcp from that + +#ifdef _LP64 +#ifdef ASSERT + // jsr result was labeled as an 'itos' not an 'atos' because we cannot GC + // the result. The return address (really a BCI) was stored with an + // 'astore' because JVM specs claim it's a pointer-sized thing. Hence in + // the 64-bit build the 32-bit BCI is actually in the low bits of a 64-bit + // loaded value. + { Label zzz ; + __ set (65536, G3_scratch) ; + __ cmp (Otos_i, G3_scratch) ; + __ bp( Assembler::lessEqualUnsigned, false, Assembler::xcc, Assembler::pn, zzz); + __ delayed()->nop(); + __ stop("BCI is in the wrong register half?"); + __ bind (zzz) ; + } +#endif +#endif + + __ profile_ret(vtos, Otos_i, G4_scratch); + + __ ld_ptr(Address(Lmethod, 0, in_bytes(methodOopDesc::const_offset())), G3_scratch); + __ add(G3_scratch, Otos_i, G3_scratch); + __ add(G3_scratch, in_bytes(constMethodOopDesc::codes_offset()), Lbcp); + __ dispatch_next(vtos); +} + + +void TemplateTable::wide_ret() { + transition(vtos, vtos); + locals_index_wide(G3_scratch); + __ access_local_returnAddress(G3_scratch, Otos_i); + // Otos_i contains the bci, compute the bcp from that + + __ profile_ret(vtos, Otos_i, G4_scratch); + + __ ld_ptr(Address(Lmethod, 0, in_bytes(methodOopDesc::const_offset())), G3_scratch); + __ add(G3_scratch, Otos_i, G3_scratch); + __ add(G3_scratch, in_bytes(constMethodOopDesc::codes_offset()), Lbcp); + __ dispatch_next(vtos); +} + + +void TemplateTable::tableswitch() { + transition(itos, vtos); + Label default_case, continue_execution; + + // align bcp + __ add(Lbcp, BytesPerInt, O1); + __ and3(O1, -BytesPerInt, O1); + // load lo, hi + __ ld(O1, 1 * BytesPerInt, O2); // Low Byte + __ ld(O1, 2 * BytesPerInt, O3); // High Byte +#ifdef _LP64 + // Sign extend the 32 bits + __ sra ( Otos_i, 0, Otos_i ); +#endif /* _LP64 */ + + // check against lo & hi + __ cmp( Otos_i, O2); + __ br( Assembler::less, false, Assembler::pn, default_case); + __ delayed()->cmp( Otos_i, O3 ); + __ br( Assembler::greater, false, Assembler::pn, default_case); + // lookup dispatch offset + __ delayed()->sub(Otos_i, O2, O2); + __ profile_switch_case(O2, O3, G3_scratch, G4_scratch); + __ sll(O2, LogBytesPerInt, O2); + __ add(O2, 3 * BytesPerInt, O2); + __ ba(false, continue_execution); + __ delayed()->ld(O1, O2, O2); + // handle default + __ bind(default_case); + __ profile_switch_default(O3); + __ ld(O1, 0, O2); // get default offset + // continue execution + __ bind(continue_execution); + __ add(Lbcp, O2, Lbcp); + __ dispatch_next(vtos); +} + + +void TemplateTable::lookupswitch() { + transition(itos, itos); + __ stop("lookupswitch bytecode should have been rewritten"); +} + +void TemplateTable::fast_linearswitch() { + transition(itos, vtos); + Label loop_entry, loop, found, continue_execution; + // align bcp + __ add(Lbcp, BytesPerInt, O1); + __ and3(O1, -BytesPerInt, O1); + // set counter + __ ld(O1, BytesPerInt, O2); + __ sll(O2, LogBytesPerInt + 1, O2); // in word-pairs + __ add(O1, 2 * BytesPerInt, O3); // set first pair addr + __ ba(false, loop_entry); + __ delayed()->add(O3, O2, O2); // counter now points past last pair + + // table search + __ bind(loop); + __ cmp(O4, Otos_i); + __ br(Assembler::equal, true, Assembler::pn, found); + __ delayed()->ld(O3, BytesPerInt, O4); // offset -> O4 + __ inc(O3, 2 * BytesPerInt); + + __ bind(loop_entry); + __ cmp(O2, O3); + __ brx(Assembler::greaterUnsigned, true, Assembler::pt, loop); + __ delayed()->ld(O3, 0, O4); + + // default case + __ ld(O1, 0, O4); // get default offset + if (ProfileInterpreter) { + __ profile_switch_default(O3); + __ ba(false, continue_execution); + __ delayed()->nop(); + } + + // entry found -> get offset + __ bind(found); + if (ProfileInterpreter) { + __ sub(O3, O1, O3); + __ sub(O3, 2*BytesPerInt, O3); + __ srl(O3, LogBytesPerInt + 1, O3); // in word-pairs + __ profile_switch_case(O3, O1, O2, G3_scratch); + + __ bind(continue_execution); + } + __ add(Lbcp, O4, Lbcp); + __ dispatch_next(vtos); +} + + +void TemplateTable::fast_binaryswitch() { + transition(itos, vtos); + // Implementation using the following core algorithm: (copied from Intel) + // + // int binary_search(int key, LookupswitchPair* array, int n) { + // // Binary search according to "Methodik des Programmierens" by + // // Edsger W. Dijkstra and W.H.J. Feijen, Addison Wesley Germany 1985. + // int i = 0; + // int j = n; + // while (i+1 < j) { + // // invariant P: 0 <= i < j <= n and (a[i] <= key < a[j] or Q) + // // with Q: for all i: 0 <= i < n: key < a[i] + // // where a stands for the array and assuming that the (inexisting) + // // element a[n] is infinitely big. + // int h = (i + j) >> 1; + // // i < h < j + // if (key < array[h].fast_match()) { + // j = h; + // } else { + // i = h; + // } + // } + // // R: a[i] <= key < a[i+1] or Q + // // (i.e., if key is within array, i is the correct index) + // return i; + // } + + // register allocation + assert(Otos_i == O0, "alias checking"); + const Register Rkey = Otos_i; // already set (tosca) + const Register Rarray = O1; + const Register Ri = O2; + const Register Rj = O3; + const Register Rh = O4; + const Register Rscratch = O5; + + const int log_entry_size = 3; + const int entry_size = 1 << log_entry_size; + + Label found; + // Find Array start + __ add(Lbcp, 3 * BytesPerInt, Rarray); + __ and3(Rarray, -BytesPerInt, Rarray); + // initialize i & j (in delay slot) + __ clr( Ri ); + + // and start + Label entry; + __ ba(false, entry); + __ delayed()->ld( Rarray, -BytesPerInt, Rj); + // (Rj is already in the native byte-ordering.) + + // binary search loop + { Label loop; + __ bind( loop ); + // int h = (i + j) >> 1; + __ sra( Rh, 1, Rh ); + // if (key < array[h].fast_match()) { + // j = h; + // } else { + // i = h; + // } + __ sll( Rh, log_entry_size, Rscratch ); + __ ld( Rarray, Rscratch, Rscratch ); + // (Rscratch is already in the native byte-ordering.) + __ cmp( Rkey, Rscratch ); + if ( VM_Version::v9_instructions_work() ) { + __ movcc( Assembler::less, false, Assembler::icc, Rh, Rj ); // j = h if (key < array[h].fast_match()) + __ movcc( Assembler::greaterEqual, false, Assembler::icc, Rh, Ri ); // i = h if (key >= array[h].fast_match()) + } + else { + Label end_of_if; + __ br( Assembler::less, true, Assembler::pt, end_of_if ); + __ delayed()->mov( Rh, Rj ); // if (<) Rj = Rh + __ mov( Rh, Ri ); // else i = h + __ bind(end_of_if); // } + } + + // while (i+1 < j) + __ bind( entry ); + __ add( Ri, 1, Rscratch ); + __ cmp(Rscratch, Rj); + __ br( Assembler::less, true, Assembler::pt, loop ); + __ delayed()->add( Ri, Rj, Rh ); // start h = i + j >> 1; + } + + // end of binary search, result index is i (must check again!) + Label default_case; + Label continue_execution; + if (ProfileInterpreter) { + __ mov( Ri, Rh ); // Save index in i for profiling + } + __ sll( Ri, log_entry_size, Ri ); + __ ld( Rarray, Ri, Rscratch ); + // (Rscratch is already in the native byte-ordering.) + __ cmp( Rkey, Rscratch ); + __ br( Assembler::notEqual, true, Assembler::pn, default_case ); + __ delayed()->ld( Rarray, -2 * BytesPerInt, Rj ); // load default offset -> j + + // entry found -> j = offset + __ inc( Ri, BytesPerInt ); + __ profile_switch_case(Rh, Rj, Rscratch, Rkey); + __ ld( Rarray, Ri, Rj ); + // (Rj is already in the native byte-ordering.) + + if (ProfileInterpreter) { + __ ba(false, continue_execution); + __ delayed()->nop(); + } + + __ bind(default_case); // fall through (if not profiling) + __ profile_switch_default(Ri); + + __ bind(continue_execution); + __ add( Lbcp, Rj, Lbcp ); + __ dispatch_next( vtos ); +} + + +void TemplateTable::_return(TosState state) { + transition(state, state); + assert(_desc->calls_vm(), "inconsistent calls_vm information"); + + if (_desc->bytecode() == Bytecodes::_return_register_finalizer) { + assert(state == vtos, "only valid state"); + __ mov(G0, G3_scratch); + __ access_local_ptr(G3_scratch, Otos_i); + __ ld_ptr(Otos_i, oopDesc::klass_offset_in_bytes(), O2); + __ set(JVM_ACC_HAS_FINALIZER, G3); + __ ld(O2, Klass::access_flags_offset_in_bytes() + sizeof(oopDesc), O2); + __ andcc(G3, O2, G0); + Label skip_register_finalizer; + __ br(Assembler::zero, false, Assembler::pn, skip_register_finalizer); + __ delayed()->nop(); + + // Call out to do finalizer registration + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::register_finalizer), Otos_i); + + __ bind(skip_register_finalizer); + } + + __ remove_activation(state, /* throw_monitor_exception */ true); + + // The caller's SP was adjusted upon method entry to accomodate + // the callee's non-argument locals. Undo that adjustment. + __ ret(); // return to caller + __ delayed()->restore(I5_savedSP, G0, SP); +} + + +// ---------------------------------------------------------------------------- +// Volatile variables demand their effects be made known to all CPU's in +// order. Store buffers on most chips allow reads & writes to reorder; the +// JMM's ReadAfterWrite.java test fails in -Xint mode without some kind of +// memory barrier (i.e., it's not sufficient that the interpreter does not +// reorder volatile references, the hardware also must not reorder them). +// +// According to the new Java Memory Model (JMM): +// (1) All volatiles are serialized wrt to each other. +// ALSO reads & writes act as aquire & release, so: +// (2) A read cannot let unrelated NON-volatile memory refs that happen after +// the read float up to before the read. It's OK for non-volatile memory refs +// that happen before the volatile read to float down below it. +// (3) Similar a volatile write cannot let unrelated NON-volatile memory refs +// that happen BEFORE the write float down to after the write. It's OK for +// non-volatile memory refs that happen after the volatile write to float up +// before it. +// +// We only put in barriers around volatile refs (they are expensive), not +// _between_ memory refs (that would require us to track the flavor of the +// previous memory refs). Requirements (2) and (3) require some barriers +// before volatile stores and after volatile loads. These nearly cover +// requirement (1) but miss the volatile-store-volatile-load case. This final +// case is placed after volatile-stores although it could just as well go +// before volatile-loads. +void TemplateTable::volatile_barrier(Assembler::Membar_mask_bits order_constraint) { + // Helper function to insert a is-volatile test and memory barrier + // All current sparc implementations run in TSO, needing only StoreLoad + if ((order_constraint & Assembler::StoreLoad) == 0) return; + __ membar( order_constraint ); +} + +// ---------------------------------------------------------------------------- +void TemplateTable::resolve_cache_and_index(int byte_no, Register Rcache, Register index) { + assert(byte_no == 1 || byte_no == 2, "byte_no out of range"); + // Depends on cpCacheOop layout! + const int shift_count = (1 + byte_no)*BitsPerByte; + Label resolved; + + __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ ld_ptr(Address(Rcache, 0, in_bytes(constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::indices_offset())), Lbyte_code); + + __ srl( Lbyte_code, shift_count, Lbyte_code ); + __ and3( Lbyte_code, 0xFF, Lbyte_code ); + __ cmp( Lbyte_code, (int)bytecode()); + __ br( Assembler::equal, false, Assembler::pt, resolved); + __ delayed()->set((int)bytecode(), O1); + + address entry; + switch (bytecode()) { + case Bytecodes::_getstatic : // fall through + case Bytecodes::_putstatic : // fall through + case Bytecodes::_getfield : // fall through + case Bytecodes::_putfield : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_get_put); break; + case Bytecodes::_invokevirtual : // fall through + case Bytecodes::_invokespecial : // fall through + case Bytecodes::_invokestatic : // fall through + case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); break; + default : ShouldNotReachHere(); break; + } + // first time invocation - must resolve first + __ call_VM(noreg, entry, O1); + // Update registers with resolved info + __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ bind(resolved); +} + +void TemplateTable::load_invoke_cp_cache_entry(int byte_no, + Register Rmethod, + Register Ritable_index, + Register Rflags, + bool is_invokevirtual, + bool is_invokevfinal) { + // Uses both G3_scratch and G4_scratch + Register Rcache = G3_scratch; + Register Rscratch = G4_scratch; + assert_different_registers(Rcache, Rmethod, Ritable_index); + + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + // determine constant pool cache field offsets + const int method_offset = in_bytes( + cp_base_offset + + (is_invokevirtual + ? ConstantPoolCacheEntry::f2_offset() + : ConstantPoolCacheEntry::f1_offset() + ) + ); + const int flags_offset = in_bytes(cp_base_offset + + ConstantPoolCacheEntry::flags_offset()); + // access constant pool cache fields + const int index_offset = in_bytes(cp_base_offset + + ConstantPoolCacheEntry::f2_offset()); + + if (is_invokevfinal) { + __ get_cache_and_index_at_bcp(Rcache, Rscratch, 1); + } else { + resolve_cache_and_index(byte_no, Rcache, Rscratch); + } + + __ ld_ptr(Address(Rcache, 0, method_offset), Rmethod); + if (Ritable_index != noreg) { + __ ld_ptr(Address(Rcache, 0, index_offset), Ritable_index); + } + __ ld_ptr(Address(Rcache, 0, flags_offset), Rflags); +} + +// The Rcache register must be set before call +void TemplateTable::load_field_cp_cache_entry(Register Robj, + Register Rcache, + Register index, + Register Roffset, + Register Rflags, + bool is_static) { + assert_different_registers(Rcache, Rflags, Roffset); + + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + __ ld_ptr(Address(Rcache, 0, in_bytes(cp_base_offset + + ConstantPoolCacheEntry::flags_offset())), Rflags); + __ ld_ptr(Address(Rcache, 0, in_bytes(cp_base_offset + + ConstantPoolCacheEntry::f2_offset())), Roffset); + if (is_static) { + __ ld_ptr(Address(Rcache, 0, in_bytes(cp_base_offset + + ConstantPoolCacheEntry::f1_offset())), Robj); + } +} + +// The registers Rcache and index expected to be set before call. +// Correct values of the Rcache and index registers are preserved. +void TemplateTable::jvmti_post_field_access(Register Rcache, + Register index, + bool is_static, + bool has_tos) { + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we take + // the time to call into the VM. + Label Label1; + assert_different_registers(Rcache, index, G1_scratch); + Address get_field_access_count_addr(G1_scratch, + (address)JvmtiExport::get_field_access_count_addr(), + relocInfo::none); + __ load_contents(get_field_access_count_addr, G1_scratch); + __ tst(G1_scratch); + __ br(Assembler::zero, false, Assembler::pt, Label1); + __ delayed()->nop(); + + __ add(Rcache, in_bytes(cp_base_offset), Rcache); + + if (is_static) { + __ clr(Otos_i); + } else { + if (has_tos) { + // save object pointer before call_VM() clobbers it + __ mov(Otos_i, Lscratch); + } else { + // Load top of stack (do not pop the value off the stack); + __ ld_ptr(Lesp, Interpreter::expr_offset_in_bytes(0), Otos_i); + } + __ verify_oop(Otos_i); + } + // Otos_i: object pointer or NULL if static + // Rcache: cache entry pointer + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), + Otos_i, Rcache); + if (!is_static && has_tos) { + __ mov(Lscratch, Otos_i); // restore object pointer + __ verify_oop(Otos_i); + } + __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ bind(Label1); + } +} + +void TemplateTable::getfield_or_static(int byte_no, bool is_static) { + transition(vtos, vtos); + + Register Rcache = G3_scratch; + Register index = G4_scratch; + Register Rclass = Rcache; + Register Roffset= G4_scratch; + Register Rflags = G1_scratch; + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + resolve_cache_and_index(byte_no, Rcache, index); + jvmti_post_field_access(Rcache, index, is_static, false); + load_field_cp_cache_entry(Rclass, Rcache, index, Roffset, Rflags, is_static); + + if (!is_static) { + pop_and_check_object(Rclass); + } else { + __ verify_oop(Rclass); + } + + Label exit; + + Assembler::Membar_mask_bits membar_bits = + Assembler::Membar_mask_bits(Assembler::LoadLoad | Assembler::LoadStore); + + if (__ membar_has_effect(membar_bits)) { + // Get volatile flag + __ set((1 << ConstantPoolCacheEntry::volatileField), Lscratch); + __ and3(Rflags, Lscratch, Lscratch); + } + + Label checkVolatile; + + // compute field type + Label notByte, notInt, notShort, notChar, notLong, notFloat, notObj; + __ srl(Rflags, ConstantPoolCacheEntry::tosBits, Rflags); + // Make sure we don't need to mask Rflags for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + + // Check atos before itos for getstatic, more likely (in Queens at least) + __ cmp(Rflags, atos); + __ br(Assembler::notEqual, false, Assembler::pt, notObj); + __ delayed() ->cmp(Rflags, itos); + + // atos + __ ld_ptr(Rclass, Roffset, Otos_i); + __ verify_oop(Otos_i); + __ push(atos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_agetfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notObj); + + // cmp(Rflags, itos); + __ br(Assembler::notEqual, false, Assembler::pt, notInt); + __ delayed() ->cmp(Rflags, ltos); + + // itos + __ ld(Rclass, Roffset, Otos_i); + __ push(itos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_igetfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notInt); + + // cmp(Rflags, ltos); + __ br(Assembler::notEqual, false, Assembler::pt, notLong); + __ delayed() ->cmp(Rflags, btos); + + // ltos + // load must be atomic + __ ld_long(Rclass, Roffset, Otos_l); + __ push(ltos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_lgetfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notLong); + + // cmp(Rflags, btos); + __ br(Assembler::notEqual, false, Assembler::pt, notByte); + __ delayed() ->cmp(Rflags, ctos); + + // btos + __ ldsb(Rclass, Roffset, Otos_i); + __ push(itos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_bgetfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notByte); + + // cmp(Rflags, ctos); + __ br(Assembler::notEqual, false, Assembler::pt, notChar); + __ delayed() ->cmp(Rflags, stos); + + // ctos + __ lduh(Rclass, Roffset, Otos_i); + __ push(itos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_cgetfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notChar); + + // cmp(Rflags, stos); + __ br(Assembler::notEqual, false, Assembler::pt, notShort); + __ delayed() ->cmp(Rflags, ftos); + + // stos + __ ldsh(Rclass, Roffset, Otos_i); + __ push(itos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_sgetfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notShort); + + + // cmp(Rflags, ftos); + __ br(Assembler::notEqual, false, Assembler::pt, notFloat); + __ delayed() ->tst(Lscratch); + + // ftos + __ ldf(FloatRegisterImpl::S, Rclass, Roffset, Ftos_f); + __ push(ftos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_fgetfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notFloat); + + + // dtos + __ ldf(FloatRegisterImpl::D, Rclass, Roffset, Ftos_d); + __ push(dtos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_dgetfield, G3_scratch, G4_scratch); + } + + __ bind(checkVolatile); + if (__ membar_has_effect(membar_bits)) { + // __ tst(Lscratch); executed in delay slot + __ br(Assembler::zero, false, Assembler::pt, exit); + __ delayed()->nop(); + volatile_barrier(membar_bits); + } + + __ bind(exit); +} + + +void TemplateTable::getfield(int byte_no) { + getfield_or_static(byte_no, false); +} + +void TemplateTable::getstatic(int byte_no) { + getfield_or_static(byte_no, true); +} + + +void TemplateTable::fast_accessfield(TosState state) { + transition(atos, state); + Register Rcache = G3_scratch; + Register index = G4_scratch; + Register Roffset = G4_scratch; + Register Rflags = Rcache; + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + __ get_cache_and_index_at_bcp(Rcache, index, 1); + jvmti_post_field_access(Rcache, index, /*is_static*/false, /*has_tos*/true); + + __ ld_ptr(Address(Rcache, 0, in_bytes(cp_base_offset + ConstantPoolCacheEntry::f2_offset())), Roffset); + + __ null_check(Otos_i); + __ verify_oop(Otos_i); + + Label exit; + + Assembler::Membar_mask_bits membar_bits = + Assembler::Membar_mask_bits(Assembler::LoadLoad | Assembler::LoadStore); + if (__ membar_has_effect(membar_bits)) { + // Get volatile flag + __ ld_ptr(Address(Rcache, 0, in_bytes(cp_base_offset + ConstantPoolCacheEntry::f2_offset())), Rflags); + __ set((1 << ConstantPoolCacheEntry::volatileField), Lscratch); + } + + switch (bytecode()) { + case Bytecodes::_fast_bgetfield: + __ ldsb(Otos_i, Roffset, Otos_i); + break; + case Bytecodes::_fast_cgetfield: + __ lduh(Otos_i, Roffset, Otos_i); + break; + case Bytecodes::_fast_sgetfield: + __ ldsh(Otos_i, Roffset, Otos_i); + break; + case Bytecodes::_fast_igetfield: + __ ld(Otos_i, Roffset, Otos_i); + break; + case Bytecodes::_fast_lgetfield: + __ ld_long(Otos_i, Roffset, Otos_l); + break; + case Bytecodes::_fast_fgetfield: + __ ldf(FloatRegisterImpl::S, Otos_i, Roffset, Ftos_f); + break; + case Bytecodes::_fast_dgetfield: + __ ldf(FloatRegisterImpl::D, Otos_i, Roffset, Ftos_d); + break; + case Bytecodes::_fast_agetfield: + __ ld_ptr(Otos_i, Roffset, Otos_i); + break; + default: + ShouldNotReachHere(); + } + + if (__ membar_has_effect(membar_bits)) { + __ btst(Lscratch, Rflags); + __ br(Assembler::zero, false, Assembler::pt, exit); + __ delayed()->nop(); + volatile_barrier(membar_bits); + __ bind(exit); + } + + if (state == atos) { + __ verify_oop(Otos_i); // does not blow flags! + } +} + +void TemplateTable::jvmti_post_fast_field_mod() { + if (JvmtiExport::can_post_field_modification()) { + // Check to see if a field modification watch has been set before we take + // the time to call into the VM. + Label done; + Address get_field_modification_count_addr(G4_scratch, (address)JvmtiExport::get_field_modification_count_addr(), relocInfo::none); + __ load_contents(get_field_modification_count_addr, G4_scratch); + __ tst(G4_scratch); + __ br(Assembler::zero, false, Assembler::pt, done); + __ delayed()->nop(); + __ pop_ptr(G4_scratch); // copy the object pointer from tos + __ verify_oop(G4_scratch); + __ push_ptr(G4_scratch); // put the object pointer back on tos + __ get_cache_entry_pointer_at_bcp(G1_scratch, G3_scratch, 1); + // Save tos values before call_VM() clobbers them. Since we have + // to do it for every data type, we use the saved values as the + // jvalue object. + switch (bytecode()) { // save tos values before call_VM() clobbers them + case Bytecodes::_fast_aputfield: __ push_ptr(Otos_i); break; + case Bytecodes::_fast_bputfield: // fall through + case Bytecodes::_fast_sputfield: // fall through + case Bytecodes::_fast_cputfield: // fall through + case Bytecodes::_fast_iputfield: __ push_i(Otos_i); break; + case Bytecodes::_fast_dputfield: __ push_d(Ftos_d); break; + case Bytecodes::_fast_fputfield: __ push_f(Ftos_f); break; + // get words in right order for use as jvalue object + case Bytecodes::_fast_lputfield: __ push_l(Otos_l); break; + } + // setup pointer to jvalue object + __ mov(Lesp, G3_scratch); __ inc(G3_scratch, wordSize); + // G4_scratch: object pointer + // G1_scratch: cache entry pointer + // G3_scratch: jvalue object on the stack + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), G4_scratch, G1_scratch, G3_scratch); + switch (bytecode()) { // restore tos values + case Bytecodes::_fast_aputfield: __ pop_ptr(Otos_i); break; + case Bytecodes::_fast_bputfield: // fall through + case Bytecodes::_fast_sputfield: // fall through + case Bytecodes::_fast_cputfield: // fall through + case Bytecodes::_fast_iputfield: __ pop_i(Otos_i); break; + case Bytecodes::_fast_dputfield: __ pop_d(Ftos_d); break; + case Bytecodes::_fast_fputfield: __ pop_f(Ftos_f); break; + case Bytecodes::_fast_lputfield: __ pop_l(Otos_l); break; + } + __ bind(done); + } +} + +// The registers Rcache and index expected to be set before call. +// The function may destroy various registers, just not the Rcache and index registers. +void TemplateTable::jvmti_post_field_mod(Register Rcache, Register index, bool is_static) { + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + if (JvmtiExport::can_post_field_modification()) { + // Check to see if a field modification watch has been set before we take + // the time to call into the VM. + Label Label1; + assert_different_registers(Rcache, index, G1_scratch); + Address get_field_modification_count_addr(G1_scratch, + (address)JvmtiExport::get_field_modification_count_addr(), + relocInfo::none); + __ load_contents(get_field_modification_count_addr, G1_scratch); + __ tst(G1_scratch); + __ br(Assembler::zero, false, Assembler::pt, Label1); + __ delayed()->nop(); + + // The Rcache and index registers have been already set. + // This allows to eliminate this call but the Rcache and index + // registers must be correspondingly used after this line. + __ get_cache_and_index_at_bcp(G1_scratch, G4_scratch, 1); + + __ add(G1_scratch, in_bytes(cp_base_offset), G3_scratch); + if (is_static) { + // Life is simple. Null out the object pointer. + __ clr(G4_scratch); + } else { + Register Rflags = G1_scratch; + // Life is harder. The stack holds the value on top, followed by the + // object. We don't know the size of the value, though; it could be + // one or two words depending on its type. As a result, we must find + // the type to determine where the object is. + + Label two_word, valsizeknown; + __ ld_ptr(Address(G1_scratch, 0, in_bytes(cp_base_offset + ConstantPoolCacheEntry::flags_offset())), Rflags); + __ mov(Lesp, G4_scratch); + __ srl(Rflags, ConstantPoolCacheEntry::tosBits, Rflags); + // Make sure we don't need to mask Rflags for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ cmp(Rflags, ltos); + __ br(Assembler::equal, false, Assembler::pt, two_word); + __ delayed()->cmp(Rflags, dtos); + __ br(Assembler::equal, false, Assembler::pt, two_word); + __ delayed()->nop(); + __ inc(G4_scratch, Interpreter::expr_offset_in_bytes(1)); + __ br(Assembler::always, false, Assembler::pt, valsizeknown); + __ delayed()->nop(); + __ bind(two_word); + + __ inc(G4_scratch, Interpreter::expr_offset_in_bytes(2)); + + __ bind(valsizeknown); + // setup object pointer + __ ld_ptr(G4_scratch, 0, G4_scratch); + __ verify_oop(G4_scratch); + } + // setup pointer to jvalue object + __ mov(Lesp, G1_scratch); __ inc(G1_scratch, wordSize); + // G4_scratch: object pointer or NULL if static + // G3_scratch: cache entry pointer + // G1_scratch: jvalue object on the stack + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), + G4_scratch, G3_scratch, G1_scratch); + __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ bind(Label1); + } +} + +void TemplateTable::pop_and_check_object(Register r) { + __ pop_ptr(r); + __ null_check(r); // for field access must check obj. + __ verify_oop(r); +} + +void TemplateTable::putfield_or_static(int byte_no, bool is_static) { + transition(vtos, vtos); + Register Rcache = G3_scratch; + Register index = G4_scratch; + Register Rclass = Rcache; + Register Roffset= G4_scratch; + Register Rflags = G1_scratch; + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + resolve_cache_and_index(byte_no, Rcache, index); + jvmti_post_field_mod(Rcache, index, is_static); + load_field_cp_cache_entry(Rclass, Rcache, index, Roffset, Rflags, is_static); + + Assembler::Membar_mask_bits read_bits = + Assembler::Membar_mask_bits(Assembler::LoadStore | Assembler::StoreStore); + Assembler::Membar_mask_bits write_bits = Assembler::StoreLoad; + + Label notVolatile, checkVolatile, exit; + if (__ membar_has_effect(read_bits) || __ membar_has_effect(write_bits)) { + __ set((1 << ConstantPoolCacheEntry::volatileField), Lscratch); + __ and3(Rflags, Lscratch, Lscratch); + + if (__ membar_has_effect(read_bits)) { + __ tst(Lscratch); + __ br(Assembler::zero, false, Assembler::pt, notVolatile); + __ delayed()->nop(); + volatile_barrier(read_bits); + __ bind(notVolatile); + } + } + + __ srl(Rflags, ConstantPoolCacheEntry::tosBits, Rflags); + // Make sure we don't need to mask Rflags for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + + // compute field type + Label notInt, notShort, notChar, notObj, notByte, notLong, notFloat; + + if (is_static) { + // putstatic with object type most likely, check that first + __ cmp(Rflags, atos ); + __ br(Assembler::notEqual, false, Assembler::pt, notObj); + __ delayed() ->cmp(Rflags, itos ); + + // atos + __ pop_ptr(); + __ verify_oop(Otos_i); + __ st_ptr(Otos_i, Rclass, Roffset); + __ store_check(G1_scratch, Rclass, Roffset); + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notObj); + + // cmp(Rflags, itos ); + __ br(Assembler::notEqual, false, Assembler::pt, notInt); + __ delayed() ->cmp(Rflags, btos ); + + // itos + __ pop_i(); + __ st(Otos_i, Rclass, Roffset); + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notInt); + + } else { + // putfield with int type most likely, check that first + __ cmp(Rflags, itos ); + __ br(Assembler::notEqual, false, Assembler::pt, notInt); + __ delayed() ->cmp(Rflags, atos ); + + // itos + __ pop_i(); + pop_and_check_object(Rclass); + __ st(Otos_i, Rclass, Roffset); + patch_bytecode(Bytecodes::_fast_iputfield, G3_scratch, G4_scratch); + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notInt); + // cmp(Rflags, atos ); + __ br(Assembler::notEqual, false, Assembler::pt, notObj); + __ delayed() ->cmp(Rflags, btos ); + + // atos + __ pop_ptr(); + pop_and_check_object(Rclass); + __ verify_oop(Otos_i); + __ st_ptr(Otos_i, Rclass, Roffset); + __ store_check(G1_scratch, Rclass, Roffset); + patch_bytecode(Bytecodes::_fast_aputfield, G3_scratch, G4_scratch); + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notObj); + } + + // cmp(Rflags, btos ); + __ br(Assembler::notEqual, false, Assembler::pt, notByte); + __ delayed() ->cmp(Rflags, ltos ); + + // btos + __ pop_i(); + if (!is_static) pop_and_check_object(Rclass); + __ stb(Otos_i, Rclass, Roffset); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_bputfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notByte); + + // cmp(Rflags, ltos ); + __ br(Assembler::notEqual, false, Assembler::pt, notLong); + __ delayed() ->cmp(Rflags, ctos ); + + // ltos + __ pop_l(); + if (!is_static) pop_and_check_object(Rclass); + __ st_long(Otos_l, Rclass, Roffset); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_lputfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notLong); + + // cmp(Rflags, ctos ); + __ br(Assembler::notEqual, false, Assembler::pt, notChar); + __ delayed() ->cmp(Rflags, stos ); + + // ctos (char) + __ pop_i(); + if (!is_static) pop_and_check_object(Rclass); + __ sth(Otos_i, Rclass, Roffset); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_cputfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notChar); + // cmp(Rflags, stos ); + __ br(Assembler::notEqual, false, Assembler::pt, notShort); + __ delayed() ->cmp(Rflags, ftos ); + + // stos (char) + __ pop_i(); + if (!is_static) pop_and_check_object(Rclass); + __ sth(Otos_i, Rclass, Roffset); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_sputfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notShort); + // cmp(Rflags, ftos ); + __ br(Assembler::notZero, false, Assembler::pt, notFloat); + __ delayed()->nop(); + + // ftos + __ pop_f(); + if (!is_static) pop_and_check_object(Rclass); + __ stf(FloatRegisterImpl::S, Ftos_f, Rclass, Roffset); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_fputfield, G3_scratch, G4_scratch); + } + __ ba(false, checkVolatile); + __ delayed()->tst(Lscratch); + + __ bind(notFloat); + + // dtos + __ pop_d(); + if (!is_static) pop_and_check_object(Rclass); + __ stf(FloatRegisterImpl::D, Ftos_d, Rclass, Roffset); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_dputfield, G3_scratch, G4_scratch); + } + + __ bind(checkVolatile); + __ tst(Lscratch); + + if (__ membar_has_effect(write_bits)) { + // __ tst(Lscratch); in delay slot + __ br(Assembler::zero, false, Assembler::pt, exit); + __ delayed()->nop(); + volatile_barrier(Assembler::StoreLoad); + __ bind(exit); + } +} + +void TemplateTable::fast_storefield(TosState state) { + transition(state, vtos); + Register Rcache = G3_scratch; + Register Rclass = Rcache; + Register Roffset= G4_scratch; + Register Rflags = G1_scratch; + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + jvmti_post_fast_field_mod(); + + __ get_cache_and_index_at_bcp(Rcache, G4_scratch, 1); + + Assembler::Membar_mask_bits read_bits = + Assembler::Membar_mask_bits(Assembler::LoadStore | Assembler::StoreStore); + Assembler::Membar_mask_bits write_bits = Assembler::StoreLoad; + + Label notVolatile, checkVolatile, exit; + if (__ membar_has_effect(read_bits) || __ membar_has_effect(write_bits)) { + __ ld_ptr(Address(Rcache, 0, in_bytes(cp_base_offset + + ConstantPoolCacheEntry::flags_offset())), Rflags); + __ set((1 << ConstantPoolCacheEntry::volatileField), Lscratch); + __ and3(Rflags, Lscratch, Lscratch); + if (__ membar_has_effect(read_bits)) { + __ tst(Lscratch); + __ br(Assembler::zero, false, Assembler::pt, notVolatile); + __ delayed()->nop(); + volatile_barrier(read_bits); + __ bind(notVolatile); + } + } + + __ ld_ptr(Address(Rcache, 0, in_bytes(cp_base_offset + + ConstantPoolCacheEntry::f2_offset())), Roffset); + pop_and_check_object(Rclass); + + switch (bytecode()) { + case Bytecodes::_fast_bputfield: __ stb(Otos_i, Rclass, Roffset); break; + case Bytecodes::_fast_cputfield: /* fall through */ + case Bytecodes::_fast_sputfield: __ sth(Otos_i, Rclass, Roffset); break; + case Bytecodes::_fast_iputfield: __ st(Otos_i, Rclass, Roffset); break; + case Bytecodes::_fast_lputfield: __ st_long(Otos_l, Rclass, Roffset); break; + case Bytecodes::_fast_fputfield: + __ stf(FloatRegisterImpl::S, Ftos_f, Rclass, Roffset); + break; + case Bytecodes::_fast_dputfield: + __ stf(FloatRegisterImpl::D, Ftos_d, Rclass, Roffset); + break; + case Bytecodes::_fast_aputfield: + __ st_ptr(Otos_i, Rclass, Roffset); + __ store_check(G1_scratch, Rclass, Roffset); + break; + default: + ShouldNotReachHere(); + } + + if (__ membar_has_effect(write_bits)) { + __ tst(Lscratch); + __ br(Assembler::zero, false, Assembler::pt, exit); + __ delayed()->nop(); + volatile_barrier(Assembler::StoreLoad); + __ bind(exit); + } +} + + +void TemplateTable::putfield(int byte_no) { + putfield_or_static(byte_no, false); +} + +void TemplateTable::putstatic(int byte_no) { + putfield_or_static(byte_no, true); +} + + +void TemplateTable::fast_xaccess(TosState state) { + transition(vtos, state); + Register Rcache = G3_scratch; + Register Roffset = G4_scratch; + Register Rflags = G4_scratch; + Register Rreceiver = Lscratch; + + __ ld_ptr(Llocals, Interpreter::value_offset_in_bytes(), Rreceiver); + + // access constant pool cache (is resolved) + __ get_cache_and_index_at_bcp(Rcache, G4_scratch, 2); + __ ld_ptr(Address(Rcache, 0, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f2_offset())), Roffset); + __ add(Lbcp, 1, Lbcp); // needed to report exception at the correct bcp + + __ verify_oop(Rreceiver); + __ null_check(Rreceiver); + if (state == atos) { + __ ld_ptr(Rreceiver, Roffset, Otos_i); + } else if (state == itos) { + __ ld (Rreceiver, Roffset, Otos_i) ; + } else if (state == ftos) { + __ ldf(FloatRegisterImpl::S, Rreceiver, Roffset, Ftos_f); + } else { + ShouldNotReachHere(); + } + + Assembler::Membar_mask_bits membar_bits = + Assembler::Membar_mask_bits(Assembler::LoadLoad | Assembler::LoadStore); + if (__ membar_has_effect(membar_bits)) { + + // Get is_volatile value in Rflags and check if membar is needed + __ ld_ptr(Address(Rcache, 0, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::flags_offset())), Rflags); + + // Test volatile + Label notVolatile; + __ set((1 << ConstantPoolCacheEntry::volatileField), Lscratch); + __ btst(Rflags, Lscratch); + __ br(Assembler::zero, false, Assembler::pt, notVolatile); + __ delayed()->nop(); + volatile_barrier(membar_bits); + __ bind(notVolatile); + } + + __ interp_verify_oop(Otos_i, state, __FILE__, __LINE__); + __ sub(Lbcp, 1, Lbcp); +} + +//---------------------------------------------------------------------------------------------------- +// Calls + +void TemplateTable::count_calls(Register method, Register temp) { + // implemented elsewhere + ShouldNotReachHere(); +} + +void TemplateTable::generate_vtable_call(Register Rrecv, Register Rindex, Register Rret) { + Register Rtemp = G4_scratch; + Register Rcall = Rindex; + assert_different_registers(Rcall, G5_method, Gargs, Rret); + + // get target methodOop & entry point + const int base = instanceKlass::vtable_start_offset() * wordSize; + if (vtableEntry::size() % 3 == 0) { + // scale the vtable index by 12: + int one_third = vtableEntry::size() / 3; + __ sll(Rindex, exact_log2(one_third * 1 * wordSize), Rtemp); + __ sll(Rindex, exact_log2(one_third * 2 * wordSize), Rindex); + __ add(Rindex, Rtemp, Rindex); + } else { + // scale the vtable index by 8: + __ sll(Rindex, exact_log2(vtableEntry::size() * wordSize), Rindex); + } + + __ add(Rrecv, Rindex, Rrecv); + __ ld_ptr(Rrecv, base + vtableEntry::method_offset_in_bytes(), G5_method); + + __ call_from_interpreter(Rcall, Gargs, Rret); +} + +void TemplateTable::invokevirtual(int byte_no) { + transition(vtos, vtos); + + Register Rscratch = G3_scratch; + Register Rtemp = G4_scratch; + Register Rret = Lscratch; + Register Rrecv = G5_method; + Label notFinal; + + load_invoke_cp_cache_entry(byte_no, G5_method, noreg, Rret, true); + __ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore + + // Check for vfinal + __ set((1 << ConstantPoolCacheEntry::vfinalMethod), G4_scratch); + __ btst(Rret, G4_scratch); + __ br(Assembler::zero, false, Assembler::pt, notFinal); + __ delayed()->and3(Rret, 0xFF, G4_scratch); // gets number of parameters + + patch_bytecode(Bytecodes::_fast_invokevfinal, Rscratch, Rtemp); + + invokevfinal_helper(Rscratch, Rret); + + __ bind(notFinal); + + __ mov(G5_method, Rscratch); // better scratch register + __ load_receiver(G4_scratch, O0); // gets receiverOop + // receiver is in O0 + __ verify_oop(O0); + + // get return address + Address table(Rtemp, (address)Interpreter::return_3_addrs_by_index_table()); + __ load_address(table); + __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type + // Make sure we don't need to mask Rret for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ sll(Rret, LogBytesPerWord, Rret); + __ ld_ptr(Rtemp, Rret, Rret); // get return address + + // get receiver klass + __ null_check(O0, oopDesc::klass_offset_in_bytes()); + __ ld_ptr(Address(O0, 0, oopDesc::klass_offset_in_bytes()), Rrecv); + __ verify_oop(Rrecv); + + __ profile_virtual_call(Rrecv, O4); + + generate_vtable_call(Rrecv, Rscratch, Rret); +} + +void TemplateTable::fast_invokevfinal(int byte_no) { + transition(vtos, vtos); + + load_invoke_cp_cache_entry(byte_no, G5_method, noreg, Lscratch, true, + /*is_invokevfinal*/true); + __ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore + invokevfinal_helper(G3_scratch, Lscratch); +} + +void TemplateTable::invokevfinal_helper(Register Rscratch, Register Rret) { + Register Rtemp = G4_scratch; + + __ verify_oop(G5_method); + + // Load receiver from stack slot + __ lduh(Address(G5_method, 0, in_bytes(methodOopDesc::size_of_parameters_offset())), G4_scratch); + __ load_receiver(G4_scratch, O0); + + // receiver NULL check + __ null_check(O0); + + __ profile_final_call(O4); + + // get return address + Address table(Rtemp, (address)Interpreter::return_3_addrs_by_index_table()); + __ load_address(table); + __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type + // Make sure we don't need to mask Rret for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ sll(Rret, LogBytesPerWord, Rret); + __ ld_ptr(Rtemp, Rret, Rret); // get return address + + + // do the call + __ call_from_interpreter(Rscratch, Gargs, Rret); +} + +void TemplateTable::invokespecial(int byte_no) { + transition(vtos, vtos); + + Register Rscratch = G3_scratch; + Register Rtemp = G4_scratch; + Register Rret = Lscratch; + + load_invoke_cp_cache_entry(byte_no, G5_method, noreg, Rret, false); + __ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore + + __ verify_oop(G5_method); + + __ lduh(Address(G5_method, 0, in_bytes(methodOopDesc::size_of_parameters_offset())), G4_scratch); + __ load_receiver(G4_scratch, O0); + + // receiver NULL check + __ null_check(O0); + + __ profile_call(O4); + + // get return address + Address table(Rtemp, (address)Interpreter::return_3_addrs_by_index_table()); + __ load_address(table); + __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type + // Make sure we don't need to mask Rret for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ sll(Rret, LogBytesPerWord, Rret); + __ ld_ptr(Rtemp, Rret, Rret); // get return address + + // do the call + __ call_from_interpreter(Rscratch, Gargs, Rret); +} + +void TemplateTable::invokestatic(int byte_no) { + transition(vtos, vtos); + + Register Rscratch = G3_scratch; + Register Rtemp = G4_scratch; + Register Rret = Lscratch; + + load_invoke_cp_cache_entry(byte_no, G5_method, noreg, Rret, false); + __ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore + + __ verify_oop(G5_method); + + __ profile_call(O4); + + // get return address + Address table(Rtemp, (address)Interpreter::return_3_addrs_by_index_table()); + __ load_address(table); + __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type + // Make sure we don't need to mask Rret for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ sll(Rret, LogBytesPerWord, Rret); + __ ld_ptr(Rtemp, Rret, Rret); // get return address + + // do the call + __ call_from_interpreter(Rscratch, Gargs, Rret); +} + + +void TemplateTable::invokeinterface_object_method(Register RklassOop, + Register Rcall, + Register Rret, + Register Rflags) { + Register Rscratch = G4_scratch; + Register Rindex = Lscratch; + + assert_different_registers(Rscratch, Rindex, Rret); + + Label notFinal; + + // Check for vfinal + __ set((1 << ConstantPoolCacheEntry::vfinalMethod), Rscratch); + __ btst(Rflags, Rscratch); + __ br(Assembler::zero, false, Assembler::pt, notFinal); + __ delayed()->nop(); + + __ profile_final_call(O4); + + // do the call - the index (f2) contains the methodOop + assert_different_registers(G5_method, Gargs, Rcall); + __ mov(Rindex, G5_method); + __ call_from_interpreter(Rcall, Gargs, Rret); + __ bind(notFinal); + + __ profile_virtual_call(RklassOop, O4); + generate_vtable_call(RklassOop, Rindex, Rret); +} + + +void TemplateTable::invokeinterface(int byte_no) { + transition(vtos, vtos); + + Register Rscratch = G4_scratch; + Register Rret = G3_scratch; + Register Rindex = Lscratch; + Register Rinterface = G1_scratch; + Register RklassOop = G5_method; + Register Rflags = O1; + assert_different_registers(Rscratch, G5_method); + + load_invoke_cp_cache_entry(byte_no, Rinterface, Rindex, Rflags, false); + __ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore + + // get receiver + __ and3(Rflags, 0xFF, Rscratch); // gets number of parameters + __ load_receiver(Rscratch, O0); + __ verify_oop(O0); + + __ mov(Rflags, Rret); + + // get return address + Address table(Rscratch, (address)Interpreter::return_5_addrs_by_index_table()); + __ load_address(table); + __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type + // Make sure we don't need to mask Rret for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ sll(Rret, LogBytesPerWord, Rret); + __ ld_ptr(Rscratch, Rret, Rret); // get return address + + // get receiver klass + __ null_check(O0, oopDesc::klass_offset_in_bytes()); + __ ld_ptr(O0, oopDesc::klass_offset_in_bytes(), RklassOop); + __ verify_oop(RklassOop); + + // Special case of invokeinterface called for virtual method of + // java.lang.Object. See cpCacheOop.cpp for details. + // This code isn't produced by javac, but could be produced by + // another compliant java compiler. + Label notMethod; + __ set((1 << ConstantPoolCacheEntry::methodInterface), Rscratch); + __ btst(Rflags, Rscratch); + __ br(Assembler::zero, false, Assembler::pt, notMethod); + __ delayed()->nop(); + + invokeinterface_object_method(RklassOop, Rinterface, Rret, Rflags); + + __ bind(notMethod); + + __ profile_virtual_call(RklassOop, O4); + + // + // find entry point to call + // + + // compute start of first itableOffsetEntry (which is at end of vtable) + const int base = instanceKlass::vtable_start_offset() * wordSize; + Label search; + Register Rtemp = Rflags; + + __ ld(Address(RklassOop, 0, instanceKlass::vtable_length_offset() * wordSize), Rtemp); + if (align_object_offset(1) > 1) { + __ round_to(Rtemp, align_object_offset(1)); + } + __ sll(Rtemp, LogBytesPerWord, Rtemp); // Rscratch *= 4; + if (Assembler::is_simm13(base)) { + __ add(Rtemp, base, Rtemp); + } else { + __ set(base, Rscratch); + __ add(Rscratch, Rtemp, Rtemp); + } + __ add(RklassOop, Rtemp, Rscratch); + + __ bind(search); + + __ ld_ptr(Rscratch, itableOffsetEntry::interface_offset_in_bytes(), Rtemp); + { + Label ok; + + // Check that entry is non-null. Null entries are probably a bytecode + // problem. If the interface isn't implemented by the reciever class, + // the VM should throw IncompatibleClassChangeError. linkResolver checks + // this too but that's only if the entry isn't already resolved, so we + // need to check again. + __ br_notnull( Rtemp, false, Assembler::pt, ok); + __ delayed()->nop(); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError)); + __ should_not_reach_here(); + __ bind(ok); + __ verify_oop(Rtemp); + } + + __ verify_oop(Rinterface); + + __ cmp(Rinterface, Rtemp); + __ brx(Assembler::notEqual, true, Assembler::pn, search); + __ delayed()->add(Rscratch, itableOffsetEntry::size() * wordSize, Rscratch); + + // entry found and Rscratch points to it + __ ld(Rscratch, itableOffsetEntry::offset_offset_in_bytes(), Rscratch); + + assert(itableMethodEntry::method_offset_in_bytes() == 0, "adjust instruction below"); + __ sll(Rindex, exact_log2(itableMethodEntry::size() * wordSize), Rindex); // Rindex *= 8; + __ add(Rscratch, Rindex, Rscratch); + __ ld_ptr(RklassOop, Rscratch, G5_method); + + // Check for abstract method error. + { + Label ok; + __ tst(G5_method); + __ brx(Assembler::notZero, false, Assembler::pt, ok); + __ delayed()->nop(); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); + __ should_not_reach_here(); + __ bind(ok); + } + + Register Rcall = Rinterface; + assert_different_registers(Rcall, G5_method, Gargs, Rret); + + __ verify_oop(G5_method); + __ call_from_interpreter(Rcall, Gargs, Rret); + +} + + +//---------------------------------------------------------------------------------------------------- +// Allocation + +void TemplateTable::_new() { + transition(vtos, atos); + + Label slow_case; + Label done; + Label initialize_header; + Label initialize_object; // including clearing the fields + + Register RallocatedObject = Otos_i; + Register RinstanceKlass = O1; + Register Roffset = O3; + Register Rscratch = O4; + + __ get_2_byte_integer_at_bcp(1, Rscratch, Roffset, InterpreterMacroAssembler::Unsigned); + __ get_cpool_and_tags(Rscratch, G3_scratch); + // make sure the class we're about to instantiate has been resolved + __ add(G3_scratch, typeArrayOopDesc::header_size(T_BYTE) * wordSize, G3_scratch); + __ ldub(G3_scratch, Roffset, G3_scratch); + __ cmp(G3_scratch, JVM_CONSTANT_Class); + __ br(Assembler::notEqual, false, Assembler::pn, slow_case); + __ delayed()->sll(Roffset, LogBytesPerWord, Roffset); + + //__ sll(Roffset, LogBytesPerWord, Roffset); // executed in delay slot + __ add(Roffset, sizeof(constantPoolOopDesc), Roffset); + __ ld_ptr(Rscratch, Roffset, RinstanceKlass); + + // make sure klass is fully initialized: + __ ld(RinstanceKlass, instanceKlass::init_state_offset_in_bytes() + sizeof(oopDesc), G3_scratch); + __ cmp(G3_scratch, instanceKlass::fully_initialized); + __ br(Assembler::notEqual, false, Assembler::pn, slow_case); + __ delayed()->ld(RinstanceKlass, Klass::layout_helper_offset_in_bytes() + sizeof(oopDesc), Roffset); + + // get instance_size in instanceKlass (already aligned) + //__ ld(RinstanceKlass, Klass::layout_helper_offset_in_bytes() + sizeof(oopDesc), Roffset); + + // make sure klass does not have has_finalizer, or is abstract, or interface or java/lang/Class + __ btst(Klass::_lh_instance_slow_path_bit, Roffset); + __ br(Assembler::notZero, false, Assembler::pn, slow_case); + __ delayed()->nop(); + + // allocate the instance + // 1) Try to allocate in the TLAB + // 2) if fail, and the TLAB is not full enough to discard, allocate in the shared Eden + // 3) if the above fails (or is not applicable), go to a slow case + // (creates a new TLAB, etc.) + + const bool allow_shared_alloc = + Universe::heap()->supports_inline_contig_alloc() && !CMSIncrementalMode; + + if(UseTLAB) { + Register RoldTopValue = RallocatedObject; + Register RtopAddr = G3_scratch, RtlabWasteLimitValue = G3_scratch; + Register RnewTopValue = G1_scratch; + Register RendValue = Rscratch; + Register RfreeValue = RnewTopValue; + + // check if we can allocate in the TLAB + __ ld_ptr(G2_thread, in_bytes(JavaThread::tlab_top_offset()), RoldTopValue); // sets up RalocatedObject + __ ld_ptr(G2_thread, in_bytes(JavaThread::tlab_end_offset()), RendValue); + __ add(RoldTopValue, Roffset, RnewTopValue); + + // if there is enough space, we do not CAS and do not clear + __ cmp(RnewTopValue, RendValue); + if(ZeroTLAB) { + // the fields have already been cleared + __ brx(Assembler::lessEqualUnsigned, true, Assembler::pt, initialize_header); + } else { + // initialize both the header and fields + __ brx(Assembler::lessEqualUnsigned, true, Assembler::pt, initialize_object); + } + __ delayed()->st_ptr(RnewTopValue, G2_thread, in_bytes(JavaThread::tlab_top_offset())); + + if (allow_shared_alloc) { + // Check if tlab should be discarded (refill_waste_limit >= free) + __ ld_ptr(G2_thread, in_bytes(JavaThread::tlab_refill_waste_limit_offset()), RtlabWasteLimitValue); + __ sub(RendValue, RoldTopValue, RfreeValue); +#ifdef _LP64 + __ srlx(RfreeValue, LogHeapWordSize, RfreeValue); +#else + __ srl(RfreeValue, LogHeapWordSize, RfreeValue); +#endif + __ cmp(RtlabWasteLimitValue, RfreeValue); + __ brx(Assembler::greaterEqualUnsigned, false, Assembler::pt, slow_case); // tlab waste is small + __ delayed()->nop(); + + // increment waste limit to prevent getting stuck on this slow path + __ add(RtlabWasteLimitValue, ThreadLocalAllocBuffer::refill_waste_limit_increment(), RtlabWasteLimitValue); + __ st_ptr(RtlabWasteLimitValue, G2_thread, in_bytes(JavaThread::tlab_refill_waste_limit_offset())); + } else { + // No allocation in the shared eden. + __ br(Assembler::always, false, Assembler::pt, slow_case); + __ delayed()->nop(); + } + } + + // Allocation in the shared Eden + if (allow_shared_alloc) { + Register RoldTopValue = G1_scratch; + Register RtopAddr = G3_scratch; + Register RnewTopValue = RallocatedObject; + Register RendValue = Rscratch; + + __ set((intptr_t)Universe::heap()->top_addr(), RtopAddr); + + Label retry; + __ bind(retry); + __ set((intptr_t)Universe::heap()->end_addr(), RendValue); + __ ld_ptr(RendValue, 0, RendValue); + __ ld_ptr(RtopAddr, 0, RoldTopValue); + __ add(RoldTopValue, Roffset, RnewTopValue); + + // RnewTopValue contains the top address after the new object + // has been allocated. + __ cmp(RnewTopValue, RendValue); + __ brx(Assembler::greaterUnsigned, false, Assembler::pn, slow_case); + __ delayed()->nop(); + + __ casx_under_lock(RtopAddr, RoldTopValue, RnewTopValue, + VM_Version::v9_instructions_work() ? NULL : + (address)StubRoutines::Sparc::atomic_memory_operation_lock_addr()); + + // if someone beat us on the allocation, try again, otherwise continue + __ cmp(RoldTopValue, RnewTopValue); + __ brx(Assembler::notEqual, false, Assembler::pn, retry); + __ delayed()->nop(); + } + + if (UseTLAB || Universe::heap()->supports_inline_contig_alloc()) { + // clear object fields + __ bind(initialize_object); + __ deccc(Roffset, sizeof(oopDesc)); + __ br(Assembler::zero, false, Assembler::pt, initialize_header); + __ delayed()->add(RallocatedObject, sizeof(oopDesc), G3_scratch); + + // initialize remaining object fields + { Label loop; + __ subcc(Roffset, wordSize, Roffset); + __ bind(loop); + //__ subcc(Roffset, wordSize, Roffset); // executed above loop or in delay slot + __ st_ptr(G0, G3_scratch, Roffset); + __ br(Assembler::notEqual, false, Assembler::pt, loop); + __ delayed()->subcc(Roffset, wordSize, Roffset); + } + __ br(Assembler::always, false, Assembler::pt, initialize_header); + __ delayed()->nop(); + } + + // slow case + __ bind(slow_case); + __ get_2_byte_integer_at_bcp(1, G3_scratch, O2, InterpreterMacroAssembler::Unsigned); + __ get_constant_pool(O1); + + call_VM(Otos_i, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), O1, O2); + + __ ba(false, done); + __ delayed()->nop(); + + // Initialize the header: mark, klass + __ bind(initialize_header); + + if (UseBiasedLocking) { + __ ld_ptr(RinstanceKlass, Klass::prototype_header_offset_in_bytes() + sizeof(oopDesc), G4_scratch); + } else { + __ set((intptr_t)markOopDesc::prototype(), G4_scratch); + } + __ st_ptr(G4_scratch, RallocatedObject, oopDesc::mark_offset_in_bytes()); // mark + __ st_ptr(RinstanceKlass, RallocatedObject, oopDesc::klass_offset_in_bytes()); // klass + + { + SkipIfEqual skip_if( + _masm, G4_scratch, &DTraceAllocProbes, Assembler::zero); + // Trigger dtrace event + __ push(atos); + __ call_VM_leaf(noreg, + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), O0); + __ pop(atos); + } + + // continue + __ bind(done); +} + + + +void TemplateTable::newarray() { + transition(itos, atos); + __ ldub(Lbcp, 1, O1); + call_VM(Otos_i, CAST_FROM_FN_PTR(address, InterpreterRuntime::newarray), O1, Otos_i); +} + + +void TemplateTable::anewarray() { + transition(itos, atos); + __ get_constant_pool(O1); + __ get_2_byte_integer_at_bcp(1, G4_scratch, O2, InterpreterMacroAssembler::Unsigned); + call_VM(Otos_i, CAST_FROM_FN_PTR(address, InterpreterRuntime::anewarray), O1, O2, Otos_i); +} + + +void TemplateTable::arraylength() { + transition(atos, itos); + Label ok; + __ verify_oop(Otos_i); + __ tst(Otos_i); + __ throw_if_not_1_x( Assembler::notZero, ok ); + __ delayed()->ld(Otos_i, arrayOopDesc::length_offset_in_bytes(), Otos_i); + __ throw_if_not_2( Interpreter::_throw_NullPointerException_entry, G3_scratch, ok); +} + + +void TemplateTable::checkcast() { + transition(atos, atos); + Label done, is_null, quicked, cast_ok, resolved; + Register Roffset = G1_scratch; + Register RobjKlass = O5; + Register RspecifiedKlass = O4; + + // Check for casting a NULL + __ br_null(Otos_i, false, Assembler::pn, is_null); + __ delayed()->nop(); + + // Get value klass in RobjKlass + __ ld_ptr(Otos_i, oopDesc::klass_offset_in_bytes(), RobjKlass); // get value klass + + // Get constant pool tag + __ get_2_byte_integer_at_bcp(1, Lscratch, Roffset, InterpreterMacroAssembler::Unsigned); + + // See if the checkcast has been quickened + __ get_cpool_and_tags(Lscratch, G3_scratch); + __ add(G3_scratch, typeArrayOopDesc::header_size(T_BYTE) * wordSize, G3_scratch); + __ ldub(G3_scratch, Roffset, G3_scratch); + __ cmp(G3_scratch, JVM_CONSTANT_Class); + __ br(Assembler::equal, true, Assembler::pt, quicked); + __ delayed()->sll(Roffset, LogBytesPerWord, Roffset); + + __ push_ptr(); // save receiver for result, and for GC + call_VM(RspecifiedKlass, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc) ); + __ pop_ptr(Otos_i, G3_scratch); // restore receiver + + __ br(Assembler::always, false, Assembler::pt, resolved); + __ delayed()->ld_ptr(Otos_i, oopDesc::klass_offset_in_bytes(), RobjKlass); // get value klass + + // Extract target class from constant pool + __ bind(quicked); + __ add(Roffset, sizeof(constantPoolOopDesc), Roffset); + __ ld_ptr(Lscratch, Roffset, RspecifiedKlass); + __ bind(resolved); + + // Generate a fast subtype check. Branch to cast_ok if no + // failure. Throw exception if failure. + __ gen_subtype_check( RobjKlass, RspecifiedKlass, G3_scratch, G4_scratch, G1_scratch, cast_ok ); + + // Not a subtype; so must throw exception + __ throw_if_not_x( Assembler::never, Interpreter::_throw_ClassCastException_entry, G3_scratch ); + + __ bind(cast_ok); + + if (ProfileInterpreter) { + __ ba(false, done); + __ delayed()->nop(); + } + __ bind(is_null); + __ profile_null_seen(G3_scratch); + __ bind(done); +} + + +void TemplateTable::instanceof() { + Label done, is_null, quicked, resolved; + transition(atos, itos); + Register Roffset = G1_scratch; + Register RobjKlass = O5; + Register RspecifiedKlass = O4; + + // Check for casting a NULL + __ br_null(Otos_i, false, Assembler::pt, is_null); + __ delayed()->nop(); + + // Get value klass in RobjKlass + __ ld_ptr(Otos_i, oopDesc::klass_offset_in_bytes(), RobjKlass); // get value klass + + // Get constant pool tag + __ get_2_byte_integer_at_bcp(1, Lscratch, Roffset, InterpreterMacroAssembler::Unsigned); + + // See if the checkcast has been quickened + __ get_cpool_and_tags(Lscratch, G3_scratch); + __ add(G3_scratch, typeArrayOopDesc::header_size(T_BYTE) * wordSize, G3_scratch); + __ ldub(G3_scratch, Roffset, G3_scratch); + __ cmp(G3_scratch, JVM_CONSTANT_Class); + __ br(Assembler::equal, true, Assembler::pt, quicked); + __ delayed()->sll(Roffset, LogBytesPerWord, Roffset); + + __ push_ptr(); // save receiver for result, and for GC + call_VM(RspecifiedKlass, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc) ); + __ pop_ptr(Otos_i, G3_scratch); // restore receiver + + __ br(Assembler::always, false, Assembler::pt, resolved); + __ delayed()->ld_ptr(Otos_i, oopDesc::klass_offset_in_bytes(), RobjKlass); // get value klass + + + // Extract target class from constant pool + __ bind(quicked); + __ add(Roffset, sizeof(constantPoolOopDesc), Roffset); + __ get_constant_pool(Lscratch); + __ ld_ptr(Lscratch, Roffset, RspecifiedKlass); + __ bind(resolved); + + // Generate a fast subtype check. Branch to cast_ok if no + // failure. Return 0 if failure. + __ or3(G0, 1, Otos_i); // set result assuming quick tests succeed + __ gen_subtype_check( RobjKlass, RspecifiedKlass, G3_scratch, G4_scratch, G1_scratch, done ); + // Not a subtype; return 0; + __ clr( Otos_i ); + + if (ProfileInterpreter) { + __ ba(false, done); + __ delayed()->nop(); + } + __ bind(is_null); + __ profile_null_seen(G3_scratch); + __ bind(done); +} + +void TemplateTable::_breakpoint() { + + // Note: We get here even if we are single stepping.. + // jbug inists on setting breakpoints at every bytecode + // even if we are in single step mode. + + transition(vtos, vtos); + // get the unpatched byte code + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::get_original_bytecode_at), Lmethod, Lbcp); + __ mov(O0, Lbyte_code); + + // post the breakpoint event + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::_breakpoint), Lmethod, Lbcp); + + // complete the execution of original bytecode + __ dispatch_normal(vtos); +} + + +//---------------------------------------------------------------------------------------------------- +// Exceptions + +void TemplateTable::athrow() { + transition(atos, vtos); + + // This works because exception is cached in Otos_i which is same as O0, + // which is same as what throw_exception_entry_expects + assert(Otos_i == Oexception, "see explanation above"); + + __ verify_oop(Otos_i); + __ null_check(Otos_i); + __ throw_if_not_x(Assembler::never, Interpreter::throw_exception_entry(), G3_scratch); +} + + +//---------------------------------------------------------------------------------------------------- +// Synchronization + + +// See frame_sparc.hpp for monitor block layout. +// Monitor elements are dynamically allocated by growing stack as needed. + +void TemplateTable::monitorenter() { + transition(atos, vtos); + __ verify_oop(Otos_i); + // Try to acquire a lock on the object + // Repeat until succeeded (i.e., until + // monitorenter returns true). + + { Label ok; + __ tst(Otos_i); + __ throw_if_not_1_x( Assembler::notZero, ok); + __ delayed()->mov(Otos_i, Lscratch); // save obj + __ throw_if_not_2( Interpreter::_throw_NullPointerException_entry, G3_scratch, ok); + } + + assert(O0 == Otos_i, "Be sure where the object to lock is"); + + // find a free slot in the monitor block + + + // initialize entry pointer + __ clr(O1); // points to free slot or NULL + + { + Label entry, loop, exit; + __ add( __ top_most_monitor(), O2 ); // last one to check + __ ba( false, entry ); + __ delayed()->mov( Lmonitors, O3 ); // first one to check + + + __ bind( loop ); + + __ verify_oop(O4); // verify each monitor's oop + __ tst(O4); // is this entry unused? + if (VM_Version::v9_instructions_work()) + __ movcc( Assembler::zero, false, Assembler::ptr_cc, O3, O1); + else { + Label L; + __ br( Assembler::zero, true, Assembler::pn, L ); + __ delayed()->mov(O3, O1); // rememeber this one if match + __ bind(L); + } + + __ cmp(O4, O0); // check if current entry is for same object + __ brx( Assembler::equal, false, Assembler::pn, exit ); + __ delayed()->inc( O3, frame::interpreter_frame_monitor_size() * wordSize ); // check next one + + __ bind( entry ); + + __ cmp( O3, O2 ); + __ brx( Assembler::lessEqualUnsigned, true, Assembler::pt, loop ); + __ delayed()->ld_ptr(O3, BasicObjectLock::obj_offset_in_bytes(), O4); + + __ bind( exit ); + } + + { Label allocated; + + // found free slot? + __ br_notnull(O1, false, Assembler::pn, allocated); + __ delayed()->nop(); + + __ add_monitor_to_stack( false, O2, O3 ); + __ mov(Lmonitors, O1); + + __ bind(allocated); + } + + // Increment bcp to point to the next bytecode, so exception handling for async. exceptions work correctly. + // The object has already been poped from the stack, so the expression stack looks correct. + __ inc(Lbcp); + + __ st_ptr(O0, O1, BasicObjectLock::obj_offset_in_bytes()); // store object + __ lock_object(O1, O0); + + // check if there's enough space on the stack for the monitors after locking + __ generate_stack_overflow_check(0); + + // The bcp has already been incremented. Just need to dispatch to next instruction. + __ dispatch_next(vtos); +} + + +void TemplateTable::monitorexit() { + transition(atos, vtos); + __ verify_oop(Otos_i); + __ tst(Otos_i); + __ throw_if_not_x( Assembler::notZero, Interpreter::_throw_NullPointerException_entry, G3_scratch ); + + assert(O0 == Otos_i, "just checking"); + + { Label entry, loop, found; + __ add( __ top_most_monitor(), O2 ); // last one to check + __ ba(false, entry ); + // use Lscratch to hold monitor elem to check, start with most recent monitor, + // By using a local it survives the call to the C routine. + __ delayed()->mov( Lmonitors, Lscratch ); + + __ bind( loop ); + + __ verify_oop(O4); // verify each monitor's oop + __ cmp(O4, O0); // check if current entry is for desired object + __ brx( Assembler::equal, true, Assembler::pt, found ); + __ delayed()->mov(Lscratch, O1); // pass found entry as argument to monitorexit + + __ inc( Lscratch, frame::interpreter_frame_monitor_size() * wordSize ); // advance to next + + __ bind( entry ); + + __ cmp( Lscratch, O2 ); + __ brx( Assembler::lessEqualUnsigned, true, Assembler::pt, loop ); + __ delayed()->ld_ptr(Lscratch, BasicObjectLock::obj_offset_in_bytes(), O4); + + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + __ should_not_reach_here(); + + __ bind(found); + } + __ unlock_object(O1); +} + + +//---------------------------------------------------------------------------------------------------- +// Wide instructions + +void TemplateTable::wide() { + transition(vtos, vtos); + __ ldub(Lbcp, 1, G3_scratch);// get next bc + __ sll(G3_scratch, LogBytesPerWord, G3_scratch); + Address ep(G4_scratch, (address)Interpreter::_wentry_point); + __ load_address(ep); + __ ld_ptr(ep.base(), G3_scratch, G3_scratch); + __ jmp(G3_scratch, G0); + __ delayed()->nop(); + // Note: the Lbcp increment step is part of the individual wide bytecode implementations +} + + +//---------------------------------------------------------------------------------------------------- +// Multi arrays + +void TemplateTable::multianewarray() { + transition(vtos, atos); + // put ndims * wordSize into Lscratch + __ ldub( Lbcp, 3, Lscratch); + __ sll( Lscratch, Interpreter::logStackElementSize(), Lscratch); + // Lesp points past last_dim, so set to O1 to first_dim address + __ add( Lesp, Lscratch, O1); + call_VM(Otos_i, CAST_FROM_FN_PTR(address, InterpreterRuntime::multianewarray), O1); + __ add( Lesp, Lscratch, Lesp); // pop all dimensions off the stack +} +#endif /* !CC_INTERP */ diff --git a/hotspot/src/cpu/sparc/vm/templateTable_sparc.hpp b/hotspot/src/cpu/sparc/vm/templateTable_sparc.hpp new file mode 100644 index 00000000000..08f9a0d748b --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/templateTable_sparc.hpp @@ -0,0 +1,31 @@ +/* + * Copyright 1998-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // helper function + static void invokevfinal_helper(Register Rcache, Register Rret); + static void invokeinterface_object_method(Register RklassOop, Register Rcall, + Register Rret, + Register Rflags); + static void generate_vtable_call(Register Rrecv, Register Rindex, Register Rret); + static void volatile_barrier(Assembler::Membar_mask_bits order_constraint); diff --git a/hotspot/src/cpu/sparc/vm/vmStructs_sparc.hpp b/hotspot/src/cpu/sparc/vm/vmStructs_sparc.hpp new file mode 100644 index 00000000000..0b4abe1ad6e --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/vmStructs_sparc.hpp @@ -0,0 +1,99 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// These are the CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ + \ + /******************************/ \ + /* JavaCallWrapper */ \ + /******************************/ \ + /******************************/ \ + /* JavaFrameAnchor */ \ + /******************************/ \ + volatile_nonstatic_field(JavaFrameAnchor, _flags, int) \ + \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_STRUCTS_OS_CPU macro (and must */ + /* be present there) */ + + +#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_TYPES_OS_CPU macro (and must */ + /* be present there) */ + + +#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + /******************************/ \ + /* Register numbers (C2 only) */ \ + /******************************/ \ + \ + declare_c2_constant(R_L0_num) \ + declare_c2_constant(R_L1_num) \ + declare_c2_constant(R_L2_num) \ + declare_c2_constant(R_L3_num) \ + declare_c2_constant(R_L4_num) \ + declare_c2_constant(R_L5_num) \ + declare_c2_constant(R_L6_num) \ + declare_c2_constant(R_L7_num) \ + declare_c2_constant(R_I0_num) \ + declare_c2_constant(R_I1_num) \ + declare_c2_constant(R_I2_num) \ + declare_c2_constant(R_I3_num) \ + declare_c2_constant(R_I4_num) \ + declare_c2_constant(R_I5_num) \ + declare_c2_constant(R_FP_num) \ + declare_c2_constant(R_I7_num) \ + declare_c2_constant(R_O0_num) \ + declare_c2_constant(R_O1_num) \ + declare_c2_constant(R_O2_num) \ + declare_c2_constant(R_O3_num) \ + declare_c2_constant(R_O4_num) \ + declare_c2_constant(R_O5_num) \ + declare_c2_constant(R_SP_num) \ + declare_c2_constant(R_O7_num) \ + declare_c2_constant(R_G0_num) \ + declare_c2_constant(R_G1_num) \ + declare_c2_constant(R_G2_num) \ + declare_c2_constant(R_G3_num) \ + declare_c2_constant(R_G4_num) \ + declare_c2_constant(R_G5_num) \ + declare_c2_constant(R_G6_num) \ + declare_c2_constant(R_G7_num) + + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_INT_CONSTANTS_OS_CPU macro (and must */ + /* be present there) */ + +#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_LONG_CONSTANTS_OS_CPU macro (and must */ + /* be present there) */ diff --git a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp new file mode 100644 index 00000000000..3d43a1b51a2 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp @@ -0,0 +1,162 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vm_version_sparc.cpp.incl" + +int VM_Version::_features = VM_Version::unknown_m; +const char* VM_Version::_features_str = ""; + +void VM_Version::initialize() { + _features = determine_features(); + PrefetchCopyIntervalInBytes = prefetch_copy_interval_in_bytes(); + PrefetchScanIntervalInBytes = prefetch_scan_interval_in_bytes(); + PrefetchFieldsAhead = prefetch_fields_ahead(); + + // Allocation prefetch settings + intx cache_line_size = L1_data_cache_line_size(); + if( cache_line_size > AllocatePrefetchStepSize ) + AllocatePrefetchStepSize = cache_line_size; + if( FLAG_IS_DEFAULT(AllocatePrefetchLines) ) + AllocatePrefetchLines = 3; // Optimistic value + assert( AllocatePrefetchLines > 0, "invalid value"); + if( AllocatePrefetchLines < 1 ) // set valid value in product VM + AllocatePrefetchLines = 1; // Conservative value + + AllocatePrefetchDistance = allocate_prefetch_distance(); + AllocatePrefetchStyle = allocate_prefetch_style(); + + assert(AllocatePrefetchDistance % AllocatePrefetchStepSize == 0, "invalid value"); + + UseSSE = 0; // Only on x86 and x64 + + _supports_cx8 = has_v9(); + + if (is_niagara1()) { + // Indirect branch is the same cost as direct + if (FLAG_IS_DEFAULT(UseInlineCaches)) { + UseInlineCaches = false; + } +#ifdef COMPILER2 + // Indirect branch is the same cost as direct + if (FLAG_IS_DEFAULT(UseJumpTables)) { + UseJumpTables = true; + } + // Single-issue, so entry and loop tops are + // aligned on a single instruction boundary + if (FLAG_IS_DEFAULT(InteriorEntryAlignment)) { + InteriorEntryAlignment = 4; + } + if (FLAG_IS_DEFAULT(OptoLoopAlignment)) { + OptoLoopAlignment = 4; + } +#endif + } + + char buf[512]; + jio_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s", + (has_v8() ? ", has_v8" : ""), + (has_v9() ? ", has_v9" : ""), + (has_vis1() ? ", has_vis1" : ""), + (has_vis2() ? ", has_vis2" : ""), + (is_ultra3() ? ", is_ultra3" : ""), + (is_sun4v() ? ", is_sun4v" : ""), + (is_niagara1() ? ", is_niagara1" : ""), + (!has_hardware_int_muldiv() ? ", no-muldiv" : ""), + (!has_hardware_fsmuld() ? ", no-fsmuld" : "")); + + // buf is started with ", " or is empty + _features_str = strdup(strlen(buf) > 2 ? buf + 2 : buf); + +#ifndef PRODUCT + if (PrintMiscellaneous && Verbose) { + tty->print("Allocation: "); + if (AllocatePrefetchStyle <= 0) { + tty->print_cr("no prefetching"); + } else { + if (AllocatePrefetchLines > 1) { + tty->print_cr("PREFETCH %d, %d lines of size %d bytes", AllocatePrefetchDistance, AllocatePrefetchLines, AllocatePrefetchStepSize); + } else { + tty->print_cr("PREFETCH %d, one line", AllocatePrefetchDistance); + } + } + if (PrefetchCopyIntervalInBytes > 0) { + tty->print_cr("PrefetchCopyIntervalInBytes %d", PrefetchCopyIntervalInBytes); + } + if (PrefetchScanIntervalInBytes > 0) { + tty->print_cr("PrefetchScanIntervalInBytes %d", PrefetchScanIntervalInBytes); + } + if (PrefetchFieldsAhead > 0) { + tty->print_cr("PrefetchFieldsAhead %d", PrefetchFieldsAhead); + } + } +#endif // PRODUCT +} + +void VM_Version::print_features() { + tty->print_cr("Version:%s", cpu_features()); +} + +int VM_Version::determine_features() { + if (UseV8InstrsOnly) { + NOT_PRODUCT(if (PrintMiscellaneous && Verbose) tty->print_cr("Version is Forced-V8");) + return generic_v8_m; + } + + int features = platform_features(unknown_m); // platform_features() is os_arch specific + + if (features == unknown_m) { + features = generic_v9_m; + warning("Cannot recognize SPARC version. Default to V9"); + } + + if (UseNiagaraInstrs) { + if (is_niagara1(features)) { + // Happy to accomodate... + } else { + NOT_PRODUCT(if (PrintMiscellaneous && Verbose) tty->print_cr("Version is Forced-Niagara");) + features = niagara1_m; + } + } else { + if (is_niagara1(features) && !FLAG_IS_DEFAULT(UseNiagaraInstrs)) { + NOT_PRODUCT(if (PrintMiscellaneous && Verbose) tty->print_cr("Version is Forced-Not-Niagara");) + features &= ~niagara1_unique_m; + } else { + // Happy to accomodate... + } + } + + return features; +} + +static int saved_features = 0; + +void VM_Version::allow_all() { + saved_features = _features; + _features = all_features_m; +} + +void VM_Version::revert() { + _features = saved_features; +} diff --git a/hotspot/src/cpu/sparc/vm/vm_version_sparc.hpp b/hotspot/src/cpu/sparc/vm/vm_version_sparc.hpp new file mode 100644 index 00000000000..8eda5c3108d --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.hpp @@ -0,0 +1,132 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class VM_Version: public Abstract_VM_Version { +protected: + enum Feature_Flag { + v8_instructions = 0, + hardware_int_muldiv = 1, + hardware_fsmuld = 2, + v9_instructions = 3, + vis1_instructions = 4, + vis2_instructions = 5, + sun4v_instructions = 6 + }; + + enum Feature_Flag_Set { + unknown_m = 0, + all_features_m = -1, + + v8_instructions_m = 1 << v8_instructions, + hardware_int_muldiv_m = 1 << hardware_int_muldiv, + hardware_fsmuld_m = 1 << hardware_fsmuld, + v9_instructions_m = 1 << v9_instructions, + vis1_instructions_m = 1 << vis1_instructions, + vis2_instructions_m = 1 << vis2_instructions, + sun4v_m = 1 << sun4v_instructions, + + generic_v8_m = v8_instructions_m | hardware_int_muldiv_m | hardware_fsmuld_m, + generic_v9_m = generic_v8_m | v9_instructions_m | vis1_instructions_m, + ultra3_m = generic_v9_m | vis2_instructions_m, + + // Temporary until we have something more accurate + niagara1_unique_m = sun4v_m, + niagara1_m = generic_v9_m | niagara1_unique_m + }; + + static int _features; + static const char* _features_str; + + static void print_features(); + static int determine_features(); + static int platform_features(int features); + + static bool is_niagara1(int features) { return (features & niagara1_m) == niagara1_m; } + +public: + // Initialization + static void initialize(); + + // Instruction support + static bool has_v8() { return (_features & v8_instructions_m) != 0; } + static bool has_v9() { return (_features & v9_instructions_m) != 0; } + static bool has_hardware_int_muldiv() { return (_features & hardware_int_muldiv_m) != 0; } + static bool has_hardware_fsmuld() { return (_features & hardware_fsmuld_m) != 0; } + static bool has_vis1() { return (_features & vis1_instructions_m) != 0; } + static bool has_vis2() { return (_features & vis2_instructions_m) != 0; } + + static bool supports_compare_and_exchange() + { return has_v9(); } + + static bool is_ultra3() { return (_features & ultra3_m) == ultra3_m; } + static bool is_sun4v() { return (_features & sun4v_m) != 0; } + static bool is_niagara1() { return is_niagara1(_features); } + + static bool has_fast_fxtof() { return has_v9() && !is_ultra3(); } + + static const char* cpu_features() { return _features_str; } + + static intx L1_data_cache_line_size() { + return 64; // default prefetch block size on sparc + } + + // Prefetch + static intx prefetch_copy_interval_in_bytes() { + intx interval = PrefetchCopyIntervalInBytes; + return interval >= 0 ? interval : (has_v9() ? 512 : 0); + } + static intx prefetch_scan_interval_in_bytes() { + intx interval = PrefetchScanIntervalInBytes; + return interval >= 0 ? interval : (has_v9() ? 512 : 0); + } + static intx prefetch_fields_ahead() { + intx count = PrefetchFieldsAhead; + return count >= 0 ? count : (is_ultra3() ? 1 : 0); + } + + static intx allocate_prefetch_distance() { + // This method should be called before allocate_prefetch_style(). + intx count = AllocatePrefetchDistance; + if (count < 0) { // default is not defined ? + count = 512; + } + return count; + } + static intx allocate_prefetch_style() { + assert(AllocatePrefetchStyle >= 0, "AllocatePrefetchStyle should be positive"); + // Return 0 if AllocatePrefetchDistance was not defined. + return AllocatePrefetchDistance > 0 ? AllocatePrefetchStyle : 0; + } + + // Legacy + static bool v8_instructions_work() { return has_v8() && !has_v9(); } + static bool v9_instructions_work() { return has_v9(); } + + // Assembler testing + static void allow_all(); + static void revert(); + + // Override the Abstract_VM_Version implementation. + static uint page_size_count() { return is_sun4v() ? 4 : 2; } +}; diff --git a/hotspot/src/cpu/sparc/vm/vmreg_sparc.cpp b/hotspot/src/cpu/sparc/vm/vmreg_sparc.cpp new file mode 100644 index 00000000000..868db08e43b --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/vmreg_sparc.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vmreg_sparc.cpp.incl" + + + +void VMRegImpl::set_regName() { + Register reg = ::as_Register(0); + int i; + for (i = 0; i < ConcreteRegisterImpl::max_gpr ; ) { + regName[i++ ] = reg->name(); + regName[i++ ] = reg->name(); + reg = reg->successor(); + } + + FloatRegister freg = ::as_FloatRegister(0); + for ( ; i < ConcreteRegisterImpl::max_fpr ; ) { + regName[i++] = freg->name(); + if (freg->encoding() > 31) { + regName[i++] = freg->name(); + } + freg = freg->successor(); + } + + for ( ; i < ConcreteRegisterImpl::number_of_registers ; i ++ ) { + regName[i] = "NON-GPR-FPR"; + } +} diff --git a/hotspot/src/cpu/sparc/vm/vmreg_sparc.hpp b/hotspot/src/cpu/sparc/vm/vmreg_sparc.hpp new file mode 100644 index 00000000000..61c4a932b6a --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/vmreg_sparc.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + bool is_Register(); + Register as_Register(); + + bool is_FloatRegister(); + FloatRegister as_FloatRegister(); diff --git a/hotspot/src/cpu/sparc/vm/vmreg_sparc.inline.hpp b/hotspot/src/cpu/sparc/vm/vmreg_sparc.inline.hpp new file mode 100644 index 00000000000..76f79a89a9a --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/vmreg_sparc.inline.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline VMReg RegisterImpl::as_VMReg() { + if( this==noreg ) return VMRegImpl::Bad(); + return VMRegImpl::as_VMReg(encoding() << 1 ); +} + +inline VMReg FloatRegisterImpl::as_VMReg() { return VMRegImpl::as_VMReg( ConcreteRegisterImpl::max_gpr + encoding() ); } + + +inline bool VMRegImpl::is_Register() { return value() >= 0 && value() < ConcreteRegisterImpl::max_gpr; } +inline bool VMRegImpl::is_FloatRegister() { return value() >= ConcreteRegisterImpl::max_gpr && + value() < ConcreteRegisterImpl::max_fpr; } +inline Register VMRegImpl::as_Register() { + + assert( is_Register() && is_even(value()), "even-aligned GPR name" ); + // Yuk + return ::as_Register(value()>>1); +} + +inline FloatRegister VMRegImpl::as_FloatRegister() { + assert( is_FloatRegister(), "must be" ); + // Yuk + return ::as_FloatRegister( value() - ConcreteRegisterImpl::max_gpr ); +} + +inline bool VMRegImpl::is_concrete() { + assert(is_reg(), "must be"); + int v = value(); + if ( v < ConcreteRegisterImpl::max_gpr ) { + return is_even(v); + } + // F0..F31 + if ( v <= ConcreteRegisterImpl::max_gpr + 31) return true; + if ( v < ConcreteRegisterImpl::max_fpr) { + return is_even(v); + } + assert(false, "what register?"); + return false; +} diff --git a/hotspot/src/cpu/sparc/vm/vtableStubs_sparc.cpp b/hotspot/src/cpu/sparc/vm/vtableStubs_sparc.cpp new file mode 100644 index 00000000000..a1a5a82c838 --- /dev/null +++ b/hotspot/src/cpu/sparc/vm/vtableStubs_sparc.cpp @@ -0,0 +1,280 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vtableStubs_sparc.cpp.incl" + +// machine-dependent part of VtableStubs: create vtableStub of correct size and +// initialize its code + +#define __ masm-> + + +#ifndef PRODUCT +extern "C" void bad_compiled_vtable_index(JavaThread* thread, oopDesc* receiver, int index); +#endif + + +// Used by compiler only; may use only caller saved, non-argument registers +// NOTE: %%%% if any change is made to this stub make sure that the function +// pd_code_size_limit is changed to ensure the correct size for VtableStub +VtableStub* VtableStubs::create_vtable_stub(int vtable_index) { + const int sparc_code_length = VtableStub::pd_code_size_limit(true); + VtableStub* s = new(sparc_code_length) VtableStub(true, vtable_index); + ResourceMark rm; + CodeBuffer cb(s->entry_point(), sparc_code_length); + MacroAssembler* masm = new MacroAssembler(&cb); + +#ifndef PRODUCT + if (CountCompiledCalls) { + Address ctr(G5, SharedRuntime::nof_megamorphic_calls_addr()); + __ sethi(ctr); + __ ld(ctr, G3_scratch); + __ inc(G3_scratch); + __ st(G3_scratch, ctr); + } +#endif /* PRODUCT */ + + assert(VtableStub::receiver_location() == O0->as_VMReg(), "receiver expected in O0"); + + // get receiver klass + address npe_addr = __ pc(); + __ ld_ptr(O0, oopDesc::klass_offset_in_bytes(), G3_scratch); + + // set methodOop (in case of interpreted method), and destination address + int entry_offset = instanceKlass::vtable_start_offset() + vtable_index*vtableEntry::size(); +#ifndef PRODUCT + if (DebugVtables) { + Label L; + // check offset vs vtable length + __ ld(G3_scratch, instanceKlass::vtable_length_offset()*wordSize, G5); + __ cmp(G5, vtable_index*vtableEntry::size()); + __ br(Assembler::greaterUnsigned, false, Assembler::pt, L); + __ delayed()->nop(); + __ set(vtable_index, O2); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, bad_compiled_vtable_index), O0, O2); + __ bind(L); + } +#endif + int v_off = entry_offset*wordSize + vtableEntry::method_offset_in_bytes(); + if( __ is_simm13(v_off) ) { + __ ld_ptr(G3, v_off, G5_method); + } else { + __ set(v_off,G5); + __ ld_ptr(G3, G5, G5_method); + } + +#ifndef PRODUCT + if (DebugVtables) { + Label L; + __ br_notnull(G5_method, false, Assembler::pt, L); + __ delayed()->nop(); + __ stop("Vtable entry is ZERO"); + __ bind(L); + } +#endif + + address ame_addr = __ pc(); // if the vtable entry is null, the method is abstract + // NOTE: for vtable dispatches, the vtable entry will never be null. + + __ ld_ptr(G5_method, in_bytes(methodOopDesc::from_compiled_offset()), G3_scratch); + + // jump to target (either compiled code or c2iadapter) + __ JMP(G3_scratch, 0); + // load methodOop (in case we call c2iadapter) + __ delayed()->nop(); + + masm->flush(); + s->set_exception_points(npe_addr, ame_addr); + return s; +} + + +// NOTE: %%%% if any change is made to this stub make sure that the function +// pd_code_size_limit is changed to ensure the correct size for VtableStub +VtableStub* VtableStubs::create_itable_stub(int vtable_index) { + const int sparc_code_length = VtableStub::pd_code_size_limit(false); + VtableStub* s = new(sparc_code_length) VtableStub(false, vtable_index); + ResourceMark rm; + CodeBuffer cb(s->entry_point(), sparc_code_length); + MacroAssembler* masm = new MacroAssembler(&cb); + + Register G3_klassOop = G3_scratch; + Register G5_interface = G5; // Passed in as an argument + Label search; + + // Entry arguments: + // G5_interface: Interface + // O0: Receiver + assert(VtableStub::receiver_location() == O0->as_VMReg(), "receiver expected in O0"); + + // get receiver klass (also an implicit null-check) + address npe_addr = __ pc(); + __ ld_ptr(O0, oopDesc::klass_offset_in_bytes(), G3_klassOop); + __ verify_oop(G3_klassOop); + + // Push a new window to get some temp registers. This chops the head of all + // my 64-bit %o registers in the LION build, but this is OK because no longs + // are passed in the %o registers. Instead, longs are passed in G1 and G4 + // and so those registers are not available here. + __ save(SP,-frame::register_save_words*wordSize,SP); + Register I0_receiver = I0; // Location of receiver after save + +#ifndef PRODUCT + if (CountCompiledCalls) { + Address ctr(L0, SharedRuntime::nof_megamorphic_calls_addr()); + __ sethi(ctr); + __ ld(ctr, L1); + __ inc(L1); + __ st(L1, ctr); + } +#endif /* PRODUCT */ + + // load start of itable entries into L0 register + const int base = instanceKlass::vtable_start_offset() * wordSize; + __ ld(Address(G3_klassOop, 0, instanceKlass::vtable_length_offset() * wordSize), L0); + + // %%% Could store the aligned, prescaled offset in the klassoop. + __ sll(L0, exact_log2(vtableEntry::size() * wordSize), L0); + // see code for instanceKlass::start_of_itable! + const int vtable_alignment = align_object_offset(1); + assert(vtable_alignment == 1 || vtable_alignment == 2, ""); + const int odd_bit = vtableEntry::size() * wordSize; + if (vtable_alignment == 2) { + __ and3(L0, odd_bit, L1); // isolate the odd bit + } + __ add(G3_klassOop, L0, L0); + if (vtable_alignment == 2) { + __ add(L0, L1, L0); // double the odd bit, to align up + } + + // Loop over all itable entries until desired interfaceOop (G5_interface) found + __ bind(search); + + // %%%% Could load both offset and interface in one ldx, if they were + // in the opposite order. This would save a load. + __ ld_ptr(L0, base + itableOffsetEntry::interface_offset_in_bytes(), L1); +#ifdef ASSERT + Label ok; + // Check that entry is non-null and an Oop + __ bpr(Assembler::rc_nz, false, Assembler::pt, L1, ok); + __ delayed()->nop(); + __ stop("null entry point found in itable's offset table"); + __ bind(ok); + __ verify_oop(L1); +#endif // ASSERT + + __ cmp(G5_interface, L1); + __ brx(Assembler::notEqual, true, Assembler::pn, search); + __ delayed()->add(L0, itableOffsetEntry::size() * wordSize, L0); + + // entry found and L0 points to it, move offset of vtable for interface into L0 + __ ld(L0, base + itableOffsetEntry::offset_offset_in_bytes(), L0); + + // Compute itableMethodEntry and get methodOop(G5_method) and entrypoint(L0) for compiler + const int method_offset = (itableMethodEntry::size() * wordSize * vtable_index) + itableMethodEntry::method_offset_in_bytes(); + __ add(G3_klassOop, L0, L1); + __ ld_ptr(L1, method_offset, G5_method); + +#ifndef PRODUCT + if (DebugVtables) { + Label L01; + __ ld_ptr(L1, method_offset, G5_method); + __ bpr(Assembler::rc_nz, false, Assembler::pt, G5_method, L01); + __ delayed()->nop(); + __ stop("methodOop is null"); + __ bind(L01); + __ verify_oop(G5_method); + } +#endif + + // If the following load is through a NULL pointer, we'll take an OS + // exception that should translate into an AbstractMethodError. We need the + // window count to be correct at that time. + __ restore(); // Restore registers BEFORE the AME point + + address ame_addr = __ pc(); // if the vtable entry is null, the method is abstract + __ ld_ptr(G5_method, in_bytes(methodOopDesc::from_compiled_offset()), G3_scratch); + + // G5_method: methodOop + // O0: Receiver + // G3_scratch: entry point + __ JMP(G3_scratch, 0); + __ delayed()->nop(); + + masm->flush(); + s->set_exception_points(npe_addr, ame_addr); + return s; +} + + +int VtableStub::pd_code_size_limit(bool is_vtable_stub) { + if (TraceJumps || DebugVtables || CountCompiledCalls || VerifyOops) return 999; + else { + const int slop = 2*BytesPerInstWord; // sethi;add (needed for long offsets) + if (is_vtable_stub) { + const int basic = 5*BytesPerInstWord; // ld;ld;ld,jmp,nop + return basic + slop; + } else { +#ifdef ASSERT + return 999; +#endif // ASSERT + const int basic = 17*BytesPerInstWord; // save, ld, ld, sll, and, add, add, ld, cmp, br, add, ld, add, ld, ld, jmp, restore + return (basic + slop); + } + } +} + + +int VtableStub::pd_code_alignment() { + // UltraSPARC cache line size is 8 instructions: + const unsigned int icache_line_size = 32; + return icache_line_size; +} + + +//Reconciliation History +// 1.2 97/12/09 17:13:31 vtableStubs_i486.cpp +// 1.4 98/01/21 19:18:37 vtableStubs_i486.cpp +// 1.5 98/02/13 16:33:55 vtableStubs_i486.cpp +// 1.7 98/03/05 17:17:28 vtableStubs_i486.cpp +// 1.9 98/05/18 09:26:17 vtableStubs_i486.cpp +// 1.10 98/05/26 16:28:13 vtableStubs_i486.cpp +// 1.11 98/05/27 08:51:35 vtableStubs_i486.cpp +// 1.12 98/06/15 15:04:12 vtableStubs_i486.cpp +// 1.13 98/07/28 18:44:22 vtableStubs_i486.cpp +// 1.15 98/08/28 11:31:19 vtableStubs_i486.cpp +// 1.16 98/09/02 12:58:31 vtableStubs_i486.cpp +// 1.17 98/09/04 12:15:52 vtableStubs_i486.cpp +// 1.18 98/11/19 11:55:24 vtableStubs_i486.cpp +// 1.19 99/01/12 14:57:56 vtableStubs_i486.cpp +// 1.20 99/01/19 17:42:52 vtableStubs_i486.cpp +// 1.22 99/01/21 10:29:25 vtableStubs_i486.cpp +// 1.30 99/06/02 15:27:39 vtableStubs_i486.cpp +// 1.26 99/06/24 14:25:07 vtableStubs_i486.cpp +// 1.23 99/02/22 14:37:52 vtableStubs_i486.cpp +// 1.28 99/06/29 18:06:17 vtableStubs_i486.cpp +// 1.29 99/07/22 17:03:44 vtableStubs_i486.cpp +// 1.30 99/08/11 09:33:27 vtableStubs_i486.cpp +//End diff --git a/hotspot/src/cpu/x86/vm/assembler_x86_32.cpp b/hotspot/src/cpu/x86/vm/assembler_x86_32.cpp new file mode 100644 index 00000000000..638b3e68c3d --- /dev/null +++ b/hotspot/src/cpu/x86/vm/assembler_x86_32.cpp @@ -0,0 +1,4979 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler_x86_32.cpp.incl" + +// Implementation of AddressLiteral + +AddressLiteral::AddressLiteral(address target, relocInfo::relocType rtype) { + _is_lval = false; + _target = target; + switch (rtype) { + case relocInfo::oop_type: + // Oops are a special case. Normally they would be their own section + // but in cases like icBuffer they are literals in the code stream that + // we don't have a section for. We use none so that we get a literal address + // which is always patchable. + break; + case relocInfo::external_word_type: + _rspec = external_word_Relocation::spec(target); + break; + case relocInfo::internal_word_type: + _rspec = internal_word_Relocation::spec(target); + break; + case relocInfo::opt_virtual_call_type: + _rspec = opt_virtual_call_Relocation::spec(); + break; + case relocInfo::static_call_type: + _rspec = static_call_Relocation::spec(); + break; + case relocInfo::runtime_call_type: + _rspec = runtime_call_Relocation::spec(); + break; + case relocInfo::poll_type: + case relocInfo::poll_return_type: + _rspec = Relocation::spec_simple(rtype); + break; + case relocInfo::none: + break; + default: + ShouldNotReachHere(); + break; + } +} + +// Implementation of Address + +Address Address::make_array(ArrayAddress adr) { +#ifdef _LP64 + // Not implementable on 64bit machines + // Should have been handled higher up the call chain. + ShouldNotReachHere(); +#else + AddressLiteral base = adr.base(); + Address index = adr.index(); + assert(index._disp == 0, "must not have disp"); // maybe it can? + Address array(index._base, index._index, index._scale, (intptr_t) base.target()); + array._rspec = base._rspec; + return array; +#endif // _LP64 +} + +#ifndef _LP64 + +// exceedingly dangerous constructor +Address::Address(address loc, RelocationHolder spec) { + _base = noreg; + _index = noreg; + _scale = no_scale; + _disp = (intptr_t) loc; + _rspec = spec; +} +#endif // _LP64 + +// Convert the raw encoding form into the form expected by the constructor for +// Address. An index of 4 (rsp) corresponds to having no index, so convert +// that to noreg for the Address constructor. +Address Address::make_raw(int base, int index, int scale, int disp) { + bool valid_index = index != rsp->encoding(); + if (valid_index) { + Address madr(as_Register(base), as_Register(index), (Address::ScaleFactor)scale, in_ByteSize(disp)); + return madr; + } else { + Address madr(as_Register(base), noreg, Address::no_scale, in_ByteSize(disp)); + return madr; + } +} + +// Implementation of Assembler + +int AbstractAssembler::code_fill_byte() { + return (u_char)'\xF4'; // hlt +} + +// make this go away someday +void Assembler::emit_data(jint data, relocInfo::relocType rtype, int format) { + if (rtype == relocInfo::none) + emit_long(data); + else emit_data(data, Relocation::spec_simple(rtype), format); +} + + +void Assembler::emit_data(jint data, RelocationHolder const& rspec, int format) { + assert(imm32_operand == 0, "default format must be imm32 in this file"); + assert(inst_mark() != NULL, "must be inside InstructionMark"); + if (rspec.type() != relocInfo::none) { + #ifdef ASSERT + check_relocation(rspec, format); + #endif + // Do not use AbstractAssembler::relocate, which is not intended for + // embedded words. Instead, relocate to the enclosing instruction. + + // hack. call32 is too wide for mask so use disp32 + if (format == call32_operand) + code_section()->relocate(inst_mark(), rspec, disp32_operand); + else + code_section()->relocate(inst_mark(), rspec, format); + } + emit_long(data); +} + + +void Assembler::emit_arith_b(int op1, int op2, Register dst, int imm8) { + assert(dst->has_byte_register(), "must have byte register"); + assert(isByte(op1) && isByte(op2), "wrong opcode"); + assert(isByte(imm8), "not a byte"); + assert((op1 & 0x01) == 0, "should be 8bit operation"); + emit_byte(op1); + emit_byte(op2 | dst->encoding()); + emit_byte(imm8); +} + + +void Assembler::emit_arith(int op1, int op2, Register dst, int imm32) { + assert(isByte(op1) && isByte(op2), "wrong opcode"); + assert((op1 & 0x01) == 1, "should be 32bit operation"); + assert((op1 & 0x02) == 0, "sign-extension bit should not be set"); + if (is8bit(imm32)) { + emit_byte(op1 | 0x02); // set sign bit + emit_byte(op2 | dst->encoding()); + emit_byte(imm32 & 0xFF); + } else { + emit_byte(op1); + emit_byte(op2 | dst->encoding()); + emit_long(imm32); + } +} + +// immediate-to-memory forms +void Assembler::emit_arith_operand(int op1, Register rm, Address adr, int imm32) { + assert((op1 & 0x01) == 1, "should be 32bit operation"); + assert((op1 & 0x02) == 0, "sign-extension bit should not be set"); + if (is8bit(imm32)) { + emit_byte(op1 | 0x02); // set sign bit + emit_operand(rm,adr); + emit_byte(imm32 & 0xFF); + } else { + emit_byte(op1); + emit_operand(rm,adr); + emit_long(imm32); + } +} + +void Assembler::emit_arith(int op1, int op2, Register dst, jobject obj) { + assert(isByte(op1) && isByte(op2), "wrong opcode"); + assert((op1 & 0x01) == 1, "should be 32bit operation"); + assert((op1 & 0x02) == 0, "sign-extension bit should not be set"); + InstructionMark im(this); + emit_byte(op1); + emit_byte(op2 | dst->encoding()); + emit_data((int)obj, relocInfo::oop_type, 0); +} + + +void Assembler::emit_arith(int op1, int op2, Register dst, Register src) { + assert(isByte(op1) && isByte(op2), "wrong opcode"); + emit_byte(op1); + emit_byte(op2 | dst->encoding() << 3 | src->encoding()); +} + + +void Assembler::emit_operand(Register reg, + Register base, + Register index, + Address::ScaleFactor scale, + int disp, + RelocationHolder const& rspec) { + + relocInfo::relocType rtype = (relocInfo::relocType) rspec.type(); + if (base->is_valid()) { + if (index->is_valid()) { + assert(scale != Address::no_scale, "inconsistent address"); + // [base + index*scale + disp] + if (disp == 0 && rtype == relocInfo::none && base != rbp) { + // [base + index*scale] + // [00 reg 100][ss index base] + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x04 | reg->encoding() << 3); + emit_byte(scale << 6 | index->encoding() << 3 | base->encoding()); + } else if (is8bit(disp) && rtype == relocInfo::none) { + // [base + index*scale + imm8] + // [01 reg 100][ss index base] imm8 + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x44 | reg->encoding() << 3); + emit_byte(scale << 6 | index->encoding() << 3 | base->encoding()); + emit_byte(disp & 0xFF); + } else { + // [base + index*scale + imm32] + // [10 reg 100][ss index base] imm32 + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x84 | reg->encoding() << 3); + emit_byte(scale << 6 | index->encoding() << 3 | base->encoding()); + emit_data(disp, rspec, disp32_operand); + } + } else if (base == rsp) { + // [esp + disp] + if (disp == 0 && rtype == relocInfo::none) { + // [esp] + // [00 reg 100][00 100 100] + emit_byte(0x04 | reg->encoding() << 3); + emit_byte(0x24); + } else if (is8bit(disp) && rtype == relocInfo::none) { + // [esp + imm8] + // [01 reg 100][00 100 100] imm8 + emit_byte(0x44 | reg->encoding() << 3); + emit_byte(0x24); + emit_byte(disp & 0xFF); + } else { + // [esp + imm32] + // [10 reg 100][00 100 100] imm32 + emit_byte(0x84 | reg->encoding() << 3); + emit_byte(0x24); + emit_data(disp, rspec, disp32_operand); + } + } else { + // [base + disp] + assert(base != rsp, "illegal addressing mode"); + if (disp == 0 && rtype == relocInfo::none && base != rbp) { + // [base] + // [00 reg base] + assert(base != rbp, "illegal addressing mode"); + emit_byte(0x00 | reg->encoding() << 3 | base->encoding()); + } else if (is8bit(disp) && rtype == relocInfo::none) { + // [base + imm8] + // [01 reg base] imm8 + emit_byte(0x40 | reg->encoding() << 3 | base->encoding()); + emit_byte(disp & 0xFF); + } else { + // [base + imm32] + // [10 reg base] imm32 + emit_byte(0x80 | reg->encoding() << 3 | base->encoding()); + emit_data(disp, rspec, disp32_operand); + } + } + } else { + if (index->is_valid()) { + assert(scale != Address::no_scale, "inconsistent address"); + // [index*scale + disp] + // [00 reg 100][ss index 101] imm32 + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x04 | reg->encoding() << 3); + emit_byte(scale << 6 | index->encoding() << 3 | 0x05); + emit_data(disp, rspec, disp32_operand); + } else { + // [disp] + // [00 reg 101] imm32 + emit_byte(0x05 | reg->encoding() << 3); + emit_data(disp, rspec, disp32_operand); + } + } +} + +// Secret local extension to Assembler::WhichOperand: +#define end_pc_operand (_WhichOperand_limit) + +address Assembler::locate_operand(address inst, WhichOperand which) { + // Decode the given instruction, and return the address of + // an embedded 32-bit operand word. + + // If "which" is disp32_operand, selects the displacement portion + // of an effective address specifier. + // If "which" is imm32_operand, selects the trailing immediate constant. + // If "which" is call32_operand, selects the displacement of a call or jump. + // Caller is responsible for ensuring that there is such an operand, + // and that it is 32 bits wide. + + // If "which" is end_pc_operand, find the end of the instruction. + + address ip = inst; + + debug_only(bool has_imm32 = false); + int tail_size = 0; // other random bytes (#32, #16, etc.) at end of insn + + again_after_prefix: + switch (0xFF & *ip++) { + + // These convenience macros generate groups of "case" labels for the switch. + #define REP4(x) (x)+0: case (x)+1: case (x)+2: case (x)+3 + #define REP8(x) (x)+0: case (x)+1: case (x)+2: case (x)+3: \ + case (x)+4: case (x)+5: case (x)+6: case (x)+7 + #define REP16(x) REP8((x)+0): \ + case REP8((x)+8) + + case CS_segment: + case SS_segment: + case DS_segment: + case ES_segment: + case FS_segment: + case GS_segment: + assert(ip == inst+1, "only one prefix allowed"); + goto again_after_prefix; + + case 0xFF: // pushl a; decl a; incl a; call a; jmp a + case 0x88: // movb a, r + case 0x89: // movl a, r + case 0x8A: // movb r, a + case 0x8B: // movl r, a + case 0x8F: // popl a + break; + + case 0x68: // pushl #32(oop?) + if (which == end_pc_operand) return ip + 4; + assert(which == imm32_operand, "pushl has no disp32"); + return ip; // not produced by emit_operand + + case 0x66: // movw ... (size prefix) + switch (0xFF & *ip++) { + case 0x8B: // movw r, a + case 0x89: // movw a, r + break; + case 0xC7: // movw a, #16 + tail_size = 2; // the imm16 + break; + case 0x0F: // several SSE/SSE2 variants + ip--; // reparse the 0x0F + goto again_after_prefix; + default: + ShouldNotReachHere(); + } + break; + + case REP8(0xB8): // movl r, #32(oop?) + if (which == end_pc_operand) return ip + 4; + assert(which == imm32_operand || which == disp32_operand, ""); + return ip; + + case 0x69: // imul r, a, #32 + case 0xC7: // movl a, #32(oop?) + tail_size = 4; + debug_only(has_imm32 = true); // has both kinds of operands! + break; + + case 0x0F: // movx..., etc. + switch (0xFF & *ip++) { + case 0x12: // movlps + case 0x28: // movaps + case 0x2E: // ucomiss + case 0x2F: // comiss + case 0x54: // andps + case 0x55: // andnps + case 0x56: // orps + case 0x57: // xorps + case 0x6E: // movd + case 0x7E: // movd + case 0xAE: // ldmxcsr a + // amd side says it these have both operands but that doesn't + // appear to be true. + // debug_only(has_imm32 = true); // has both kinds of operands! + break; + + case 0xAD: // shrd r, a, %cl + case 0xAF: // imul r, a + case 0xBE: // movsxb r, a + case 0xBF: // movsxw r, a + case 0xB6: // movzxb r, a + case 0xB7: // movzxw r, a + case REP16(0x40): // cmovl cc, r, a + case 0xB0: // cmpxchgb + case 0xB1: // cmpxchg + case 0xC1: // xaddl + case 0xC7: // cmpxchg8 + case REP16(0x90): // setcc a + // fall out of the switch to decode the address + break; + case 0xAC: // shrd r, a, #8 + tail_size = 1; // the imm8 + break; + case REP16(0x80): // jcc rdisp32 + if (which == end_pc_operand) return ip + 4; + assert(which == call32_operand, "jcc has no disp32 or imm32"); + return ip; + default: + ShouldNotReachHere(); + } + break; + + case 0x81: // addl a, #32; addl r, #32 + // also: orl, adcl, sbbl, andl, subl, xorl, cmpl + // in the case of cmpl, the imm32 might be an oop + tail_size = 4; + debug_only(has_imm32 = true); // has both kinds of operands! + break; + + case 0x85: // test r/m, r + break; + + case 0x83: // addl a, #8; addl r, #8 + // also: orl, adcl, sbbl, andl, subl, xorl, cmpl + tail_size = 1; + break; + + case 0x9B: + switch (0xFF & *ip++) { + case 0xD9: // fnstcw a + break; + default: + ShouldNotReachHere(); + } + break; + + case REP4(0x00): // addb a, r; addl a, r; addb r, a; addl r, a + case REP4(0x10): // adc... + case REP4(0x20): // and... + case REP4(0x30): // xor... + case REP4(0x08): // or... + case REP4(0x18): // sbb... + case REP4(0x28): // sub... + case REP4(0x38): // cmp... + case 0xF7: // mull a + case 0x8D: // leal r, a + case 0x87: // xchg r, a + break; + + case 0xC1: // sal a, #8; sar a, #8; shl a, #8; shr a, #8 + case 0xC6: // movb a, #8 + case 0x80: // cmpb a, #8 + case 0x6B: // imul r, a, #8 + tail_size = 1; // the imm8 + break; + + case 0xE8: // call rdisp32 + case 0xE9: // jmp rdisp32 + if (which == end_pc_operand) return ip + 4; + assert(which == call32_operand, "call has no disp32 or imm32"); + return ip; + + case 0xD1: // sal a, 1; sar a, 1; shl a, 1; shr a, 1 + case 0xD3: // sal a, %cl; sar a, %cl; shl a, %cl; shr a, %cl + case 0xD9: // fld_s a; fst_s a; fstp_s a; fldcw a + case 0xDD: // fld_d a; fst_d a; fstp_d a + case 0xDB: // fild_s a; fistp_s a; fld_x a; fstp_x a + case 0xDF: // fild_d a; fistp_d a + case 0xD8: // fadd_s a; fsubr_s a; fmul_s a; fdivr_s a; fcomp_s a + case 0xDC: // fadd_d a; fsubr_d a; fmul_d a; fdivr_d a; fcomp_d a + case 0xDE: // faddp_d a; fsubrp_d a; fmulp_d a; fdivrp_d a; fcompp_d a + break; + + case 0xF3: // For SSE + case 0xF2: // For SSE2 + ip++; ip++; + break; + + default: + ShouldNotReachHere(); + + #undef REP8 + #undef REP16 + } + + assert(which != call32_operand, "instruction is not a call, jmp, or jcc"); + assert(which != imm32_operand || has_imm32, "instruction has no imm32 field"); + + // parse the output of emit_operand + int op2 = 0xFF & *ip++; + int base = op2 & 0x07; + int op3 = -1; + const int b100 = 4; + const int b101 = 5; + if (base == b100 && (op2 >> 6) != 3) { + op3 = 0xFF & *ip++; + base = op3 & 0x07; // refetch the base + } + // now ip points at the disp (if any) + + switch (op2 >> 6) { + case 0: + // [00 reg 100][ss index base] + // [00 reg 100][00 100 rsp] + // [00 reg base] + // [00 reg 100][ss index 101][disp32] + // [00 reg 101] [disp32] + + if (base == b101) { + if (which == disp32_operand) + return ip; // caller wants the disp32 + ip += 4; // skip the disp32 + } + break; + + case 1: + // [01 reg 100][ss index base][disp8] + // [01 reg 100][00 100 rsp][disp8] + // [01 reg base] [disp8] + ip += 1; // skip the disp8 + break; + + case 2: + // [10 reg 100][ss index base][disp32] + // [10 reg 100][00 100 rsp][disp32] + // [10 reg base] [disp32] + if (which == disp32_operand) + return ip; // caller wants the disp32 + ip += 4; // skip the disp32 + break; + + case 3: + // [11 reg base] (not a memory addressing mode) + break; + } + + if (which == end_pc_operand) { + return ip + tail_size; + } + + assert(which == imm32_operand, "instruction has only an imm32 field"); + return ip; +} + +address Assembler::locate_next_instruction(address inst) { + // Secretly share code with locate_operand: + return locate_operand(inst, end_pc_operand); +} + + +#ifdef ASSERT +void Assembler::check_relocation(RelocationHolder const& rspec, int format) { + address inst = inst_mark(); + assert(inst != NULL && inst < pc(), "must point to beginning of instruction"); + address opnd; + + Relocation* r = rspec.reloc(); + if (r->type() == relocInfo::none) { + return; + } else if (r->is_call() || format == call32_operand) { + // assert(format == imm32_operand, "cannot specify a nonzero format"); + opnd = locate_operand(inst, call32_operand); + } else if (r->is_data()) { + assert(format == imm32_operand || format == disp32_operand, "format ok"); + opnd = locate_operand(inst, (WhichOperand)format); + } else { + assert(format == imm32_operand, "cannot specify a format"); + return; + } + assert(opnd == pc(), "must put operand where relocs can find it"); +} +#endif + + + +void Assembler::emit_operand(Register reg, Address adr) { + emit_operand(reg, adr._base, adr._index, adr._scale, adr._disp, adr._rspec); +} + + +void Assembler::emit_farith(int b1, int b2, int i) { + assert(isByte(b1) && isByte(b2), "wrong opcode"); + assert(0 <= i && i < 8, "illegal stack offset"); + emit_byte(b1); + emit_byte(b2 + i); +} + + +void Assembler::pushad() { + emit_byte(0x60); +} + +void Assembler::popad() { + emit_byte(0x61); +} + +void Assembler::pushfd() { + emit_byte(0x9C); +} + +void Assembler::popfd() { + emit_byte(0x9D); +} + +void Assembler::pushl(int imm32) { + emit_byte(0x68); + emit_long(imm32); +} + +#ifndef _LP64 +void Assembler::push_literal32(int32_t imm32, RelocationHolder const& rspec) { + InstructionMark im(this); + emit_byte(0x68); + emit_data(imm32, rspec, 0); +} +#endif // _LP64 + +void Assembler::pushl(Register src) { + emit_byte(0x50 | src->encoding()); +} + + +void Assembler::pushl(Address src) { + InstructionMark im(this); + emit_byte(0xFF); + emit_operand(rsi, src); +} + +void Assembler::popl(Register dst) { + emit_byte(0x58 | dst->encoding()); +} + + +void Assembler::popl(Address dst) { + InstructionMark im(this); + emit_byte(0x8F); + emit_operand(rax, dst); +} + + +void Assembler::prefix(Prefix p) { + a_byte(p); +} + + +void Assembler::movb(Register dst, Address src) { + assert(dst->has_byte_register(), "must have byte register"); + InstructionMark im(this); + emit_byte(0x8A); + emit_operand(dst, src); +} + + +void Assembler::movb(Address dst, int imm8) { + InstructionMark im(this); + emit_byte(0xC6); + emit_operand(rax, dst); + emit_byte(imm8); +} + + +void Assembler::movb(Address dst, Register src) { + assert(src->has_byte_register(), "must have byte register"); + InstructionMark im(this); + emit_byte(0x88); + emit_operand(src, dst); +} + + +void Assembler::movw(Address dst, int imm16) { + InstructionMark im(this); + + emit_byte(0x66); // switch to 16-bit mode + emit_byte(0xC7); + emit_operand(rax, dst); + emit_word(imm16); +} + + +void Assembler::movw(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x66); + emit_byte(0x8B); + emit_operand(dst, src); +} + + +void Assembler::movw(Address dst, Register src) { + InstructionMark im(this); + emit_byte(0x66); + emit_byte(0x89); + emit_operand(src, dst); +} + + +void Assembler::movl(Register dst, int imm32) { + emit_byte(0xB8 | dst->encoding()); + emit_long(imm32); +} + +#ifndef _LP64 +void Assembler::mov_literal32(Register dst, int32_t imm32, RelocationHolder const& rspec) { + + InstructionMark im(this); + emit_byte(0xB8 | dst->encoding()); + emit_data((int)imm32, rspec, 0); +} +#endif // _LP64 + +void Assembler::movl(Register dst, Register src) { + emit_byte(0x8B); + emit_byte(0xC0 | (dst->encoding() << 3) | src->encoding()); +} + + +void Assembler::movl(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x8B); + emit_operand(dst, src); +} + + +void Assembler::movl(Address dst, int imm32) { + InstructionMark im(this); + emit_byte(0xC7); + emit_operand(rax, dst); + emit_long(imm32); +} + +#ifndef _LP64 +void Assembler::mov_literal32(Address dst, int32_t imm32, RelocationHolder const& rspec) { + InstructionMark im(this); + emit_byte(0xC7); + emit_operand(rax, dst); + emit_data((int)imm32, rspec, 0); +} +#endif // _LP64 + +void Assembler::movl(Address dst, Register src) { + InstructionMark im(this); + emit_byte(0x89); + emit_operand(src, dst); +} + +void Assembler::movsxb(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0xBE); + emit_operand(dst, src); +} + +void Assembler::movsxb(Register dst, Register src) { + assert(src->has_byte_register(), "must have byte register"); + emit_byte(0x0F); + emit_byte(0xBE); + emit_byte(0xC0 | (dst->encoding() << 3) | src->encoding()); +} + + +void Assembler::movsxw(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0xBF); + emit_operand(dst, src); +} + + +void Assembler::movsxw(Register dst, Register src) { + emit_byte(0x0F); + emit_byte(0xBF); + emit_byte(0xC0 | (dst->encoding() << 3) | src->encoding()); +} + + +void Assembler::movzxb(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0xB6); + emit_operand(dst, src); +} + + +void Assembler::movzxb(Register dst, Register src) { + assert(src->has_byte_register(), "must have byte register"); + emit_byte(0x0F); + emit_byte(0xB6); + emit_byte(0xC0 | (dst->encoding() << 3) | src->encoding()); +} + + +void Assembler::movzxw(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0xB7); + emit_operand(dst, src); +} + + +void Assembler::movzxw(Register dst, Register src) { + emit_byte(0x0F); + emit_byte(0xB7); + emit_byte(0xC0 | (dst->encoding() << 3) | src->encoding()); +} + + +void Assembler::cmovl(Condition cc, Register dst, Register src) { + guarantee(VM_Version::supports_cmov(), "illegal instruction"); + emit_byte(0x0F); + emit_byte(0x40 | cc); + emit_byte(0xC0 | (dst->encoding() << 3) | src->encoding()); +} + + +void Assembler::cmovl(Condition cc, Register dst, Address src) { + guarantee(VM_Version::supports_cmov(), "illegal instruction"); + // The code below seems to be wrong - however the manual is inconclusive + // do not use for now (remember to enable all callers when fixing this) + Unimplemented(); + // wrong bytes? + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0x40 | cc); + emit_operand(dst, src); +} + + +void Assembler::prefetcht0(Address src) { + assert(VM_Version::supports_sse(), "must support"); + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0x18); + emit_operand(rcx, src); // 1, src +} + + +void Assembler::prefetcht1(Address src) { + assert(VM_Version::supports_sse(), "must support"); + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0x18); + emit_operand(rdx, src); // 2, src +} + + +void Assembler::prefetcht2(Address src) { + assert(VM_Version::supports_sse(), "must support"); + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0x18); + emit_operand(rbx, src); // 3, src +} + + +void Assembler::prefetchnta(Address src) { + assert(VM_Version::supports_sse2(), "must support"); + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0x18); + emit_operand(rax, src); // 0, src +} + + +void Assembler::prefetchw(Address src) { + assert(VM_Version::supports_3dnow(), "must support"); + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0x0D); + emit_operand(rcx, src); // 1, src +} + + +void Assembler::prefetchr(Address src) { + assert(VM_Version::supports_3dnow(), "must support"); + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0x0D); + emit_operand(rax, src); // 0, src +} + + +void Assembler::adcl(Register dst, int imm32) { + emit_arith(0x81, 0xD0, dst, imm32); +} + + +void Assembler::adcl(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x13); + emit_operand(dst, src); +} + + +void Assembler::adcl(Register dst, Register src) { + emit_arith(0x13, 0xC0, dst, src); +} + + +void Assembler::addl(Address dst, int imm32) { + InstructionMark im(this); + emit_arith_operand(0x81,rax,dst,imm32); +} + + +void Assembler::addl(Address dst, Register src) { + InstructionMark im(this); + emit_byte(0x01); + emit_operand(src, dst); +} + + +void Assembler::addl(Register dst, int imm32) { + emit_arith(0x81, 0xC0, dst, imm32); +} + + +void Assembler::addl(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x03); + emit_operand(dst, src); +} + + +void Assembler::addl(Register dst, Register src) { + emit_arith(0x03, 0xC0, dst, src); +} + + +void Assembler::andl(Register dst, int imm32) { + emit_arith(0x81, 0xE0, dst, imm32); +} + + +void Assembler::andl(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x23); + emit_operand(dst, src); +} + + +void Assembler::andl(Register dst, Register src) { + emit_arith(0x23, 0xC0, dst, src); +} + + +void Assembler::cmpb(Address dst, int imm8) { + InstructionMark im(this); + emit_byte(0x80); + emit_operand(rdi, dst); + emit_byte(imm8); +} + +void Assembler::cmpw(Address dst, int imm16) { + InstructionMark im(this); + emit_byte(0x66); + emit_byte(0x81); + emit_operand(rdi, dst); + emit_word(imm16); +} + +void Assembler::cmpl(Address dst, int imm32) { + InstructionMark im(this); + emit_byte(0x81); + emit_operand(rdi, dst); + emit_long(imm32); +} + +#ifndef _LP64 +void Assembler::cmp_literal32(Register src1, int32_t imm32, RelocationHolder const& rspec) { + InstructionMark im(this); + emit_byte(0x81); + emit_byte(0xF8 | src1->encoding()); + emit_data(imm32, rspec, 0); +} + +void Assembler::cmp_literal32(Address src1, int32_t imm32, RelocationHolder const& rspec) { + InstructionMark im(this); + emit_byte(0x81); + emit_operand(rdi, src1); + emit_data(imm32, rspec, 0); +} +#endif // _LP64 + + +void Assembler::cmpl(Register dst, int imm32) { + emit_arith(0x81, 0xF8, dst, imm32); +} + + +void Assembler::cmpl(Register dst, Register src) { + emit_arith(0x3B, 0xC0, dst, src); +} + + +void Assembler::cmpl(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x3B); + emit_operand(dst, src); +} + + +void Assembler::decl(Register dst) { + // Don't use it directly. Use MacroAssembler::decrement() instead. + emit_byte(0x48 | dst->encoding()); +} + + +void Assembler::decl(Address dst) { + // Don't use it directly. Use MacroAssembler::decrement() instead. + InstructionMark im(this); + emit_byte(0xFF); + emit_operand(rcx, dst); +} + + +void Assembler::idivl(Register src) { + emit_byte(0xF7); + emit_byte(0xF8 | src->encoding()); +} + + +void Assembler::cdql() { + emit_byte(0x99); +} + + +void Assembler::imull(Register dst, Register src) { + emit_byte(0x0F); + emit_byte(0xAF); + emit_byte(0xC0 | dst->encoding() << 3 | src->encoding()); +} + + +void Assembler::imull(Register dst, Register src, int value) { + if (is8bit(value)) { + emit_byte(0x6B); + emit_byte(0xC0 | dst->encoding() << 3 | src->encoding()); + emit_byte(value); + } else { + emit_byte(0x69); + emit_byte(0xC0 | dst->encoding() << 3 | src->encoding()); + emit_long(value); + } +} + + +void Assembler::incl(Register dst) { + // Don't use it directly. Use MacroAssembler::increment() instead. + emit_byte(0x40 | dst->encoding()); +} + + +void Assembler::incl(Address dst) { + // Don't use it directly. Use MacroAssembler::increment() instead. + InstructionMark im(this); + emit_byte(0xFF); + emit_operand(rax, dst); +} + + +void Assembler::leal(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x8D); + emit_operand(dst, src); +} + +void Assembler::mull(Address src) { + InstructionMark im(this); + emit_byte(0xF7); + emit_operand(rsp, src); +} + + +void Assembler::mull(Register src) { + emit_byte(0xF7); + emit_byte(0xE0 | src->encoding()); +} + + +void Assembler::negl(Register dst) { + emit_byte(0xF7); + emit_byte(0xD8 | dst->encoding()); +} + + +void Assembler::notl(Register dst) { + emit_byte(0xF7); + emit_byte(0xD0 | dst->encoding()); +} + + +void Assembler::orl(Address dst, int imm32) { + InstructionMark im(this); + emit_byte(0x81); + emit_operand(rcx, dst); + emit_long(imm32); +} + +void Assembler::orl(Register dst, int imm32) { + emit_arith(0x81, 0xC8, dst, imm32); +} + + +void Assembler::orl(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x0B); + emit_operand(dst, src); +} + + +void Assembler::orl(Register dst, Register src) { + emit_arith(0x0B, 0xC0, dst, src); +} + + +void Assembler::rcll(Register dst, int imm8) { + assert(isShiftCount(imm8), "illegal shift count"); + if (imm8 == 1) { + emit_byte(0xD1); + emit_byte(0xD0 | dst->encoding()); + } else { + emit_byte(0xC1); + emit_byte(0xD0 | dst->encoding()); + emit_byte(imm8); + } +} + + +void Assembler::sarl(Register dst, int imm8) { + assert(isShiftCount(imm8), "illegal shift count"); + if (imm8 == 1) { + emit_byte(0xD1); + emit_byte(0xF8 | dst->encoding()); + } else { + emit_byte(0xC1); + emit_byte(0xF8 | dst->encoding()); + emit_byte(imm8); + } +} + + +void Assembler::sarl(Register dst) { + emit_byte(0xD3); + emit_byte(0xF8 | dst->encoding()); +} + + +void Assembler::sbbl(Address dst, int imm32) { + InstructionMark im(this); + emit_arith_operand(0x81,rbx,dst,imm32); +} + + +void Assembler::sbbl(Register dst, int imm32) { + emit_arith(0x81, 0xD8, dst, imm32); +} + + +void Assembler::sbbl(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x1B); + emit_operand(dst, src); +} + + +void Assembler::sbbl(Register dst, Register src) { + emit_arith(0x1B, 0xC0, dst, src); +} + + +void Assembler::shldl(Register dst, Register src) { + emit_byte(0x0F); + emit_byte(0xA5); + emit_byte(0xC0 | src->encoding() << 3 | dst->encoding()); +} + + +void Assembler::shll(Register dst, int imm8) { + assert(isShiftCount(imm8), "illegal shift count"); + if (imm8 == 1 ) { + emit_byte(0xD1); + emit_byte(0xE0 | dst->encoding()); + } else { + emit_byte(0xC1); + emit_byte(0xE0 | dst->encoding()); + emit_byte(imm8); + } +} + + +void Assembler::shll(Register dst) { + emit_byte(0xD3); + emit_byte(0xE0 | dst->encoding()); +} + + +void Assembler::shrdl(Register dst, Register src) { + emit_byte(0x0F); + emit_byte(0xAD); + emit_byte(0xC0 | src->encoding() << 3 | dst->encoding()); +} + + +void Assembler::shrl(Register dst, int imm8) { + assert(isShiftCount(imm8), "illegal shift count"); + emit_byte(0xC1); + emit_byte(0xE8 | dst->encoding()); + emit_byte(imm8); +} + + +void Assembler::shrl(Register dst) { + emit_byte(0xD3); + emit_byte(0xE8 | dst->encoding()); +} + + +void Assembler::subl(Address dst, int imm32) { + if (is8bit(imm32)) { + InstructionMark im(this); + emit_byte(0x83); + emit_operand(rbp, dst); + emit_byte(imm32 & 0xFF); + } else { + InstructionMark im(this); + emit_byte(0x81); + emit_operand(rbp, dst); + emit_long(imm32); + } +} + + +void Assembler::subl(Register dst, int imm32) { + emit_arith(0x81, 0xE8, dst, imm32); +} + + +void Assembler::subl(Address dst, Register src) { + InstructionMark im(this); + emit_byte(0x29); + emit_operand(src, dst); +} + + +void Assembler::subl(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x2B); + emit_operand(dst, src); +} + + +void Assembler::subl(Register dst, Register src) { + emit_arith(0x2B, 0xC0, dst, src); +} + + +void Assembler::testb(Register dst, int imm8) { + assert(dst->has_byte_register(), "must have byte register"); + emit_arith_b(0xF6, 0xC0, dst, imm8); +} + + +void Assembler::testl(Register dst, int imm32) { + // not using emit_arith because test + // doesn't support sign-extension of + // 8bit operands + if (dst->encoding() == 0) { + emit_byte(0xA9); + } else { + emit_byte(0xF7); + emit_byte(0xC0 | dst->encoding()); + } + emit_long(imm32); +} + + +void Assembler::testl(Register dst, Register src) { + emit_arith(0x85, 0xC0, dst, src); +} + +void Assembler::testl(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x85); + emit_operand(dst, src); +} + +void Assembler::xaddl(Address dst, Register src) { + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0xC1); + emit_operand(src, dst); +} + +void Assembler::xorl(Register dst, int imm32) { + emit_arith(0x81, 0xF0, dst, imm32); +} + + +void Assembler::xorl(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x33); + emit_operand(dst, src); +} + + +void Assembler::xorl(Register dst, Register src) { + emit_arith(0x33, 0xC0, dst, src); +} + + +void Assembler::bswap(Register reg) { + emit_byte(0x0F); + emit_byte(0xC8 | reg->encoding()); +} + + +void Assembler::lock() { + if (Atomics & 1) { + // Emit either nothing, a NOP, or a NOP: prefix + emit_byte(0x90) ; + } else { + emit_byte(0xF0); + } +} + + +void Assembler::xchg(Register reg, Address adr) { + InstructionMark im(this); + emit_byte(0x87); + emit_operand(reg, adr); +} + + +void Assembler::xchgl(Register dst, Register src) { + emit_byte(0x87); + emit_byte(0xc0 | dst->encoding() << 3 | src->encoding()); +} + + +// The 32-bit cmpxchg compares the value at adr with the contents of rax, +// and stores reg into adr if so; otherwise, the value at adr is loaded into rax,. +// The ZF is set if the compared values were equal, and cleared otherwise. +void Assembler::cmpxchg(Register reg, Address adr) { + if (Atomics & 2) { + // caveat: no instructionmark, so this isn't relocatable. + // Emit a synthetic, non-atomic, CAS equivalent. + // Beware. The synthetic form sets all ICCs, not just ZF. + // cmpxchg r,[m] is equivalent to rax, = CAS (m, rax, r) + cmpl (rax, adr) ; + movl (rax, adr) ; + if (reg != rax) { + Label L ; + jcc (Assembler::notEqual, L) ; + movl (adr, reg) ; + bind (L) ; + } + } else { + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0xB1); + emit_operand(reg, adr); + } +} + +// The 64-bit cmpxchg compares the value at adr with the contents of rdx:rax, +// and stores rcx:rbx into adr if so; otherwise, the value at adr is loaded +// into rdx:rax. The ZF is set if the compared values were equal, and cleared otherwise. +void Assembler::cmpxchg8(Address adr) { + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0xc7); + emit_operand(rcx, adr); +} + +void Assembler::hlt() { + emit_byte(0xF4); +} + + +void Assembler::addr_nop_4() { + // 4 bytes: NOP DWORD PTR [EAX+0] + emit_byte(0x0F); + emit_byte(0x1F); + emit_byte(0x40); // emit_rm(cbuf, 0x1, EAX_enc, EAX_enc); + emit_byte(0); // 8-bits offset (1 byte) +} + +void Assembler::addr_nop_5() { + // 5 bytes: NOP DWORD PTR [EAX+EAX*0+0] 8-bits offset + emit_byte(0x0F); + emit_byte(0x1F); + emit_byte(0x44); // emit_rm(cbuf, 0x1, EAX_enc, 0x4); + emit_byte(0x00); // emit_rm(cbuf, 0x0, EAX_enc, EAX_enc); + emit_byte(0); // 8-bits offset (1 byte) +} + +void Assembler::addr_nop_7() { + // 7 bytes: NOP DWORD PTR [EAX+0] 32-bits offset + emit_byte(0x0F); + emit_byte(0x1F); + emit_byte(0x80); // emit_rm(cbuf, 0x2, EAX_enc, EAX_enc); + emit_long(0); // 32-bits offset (4 bytes) +} + +void Assembler::addr_nop_8() { + // 8 bytes: NOP DWORD PTR [EAX+EAX*0+0] 32-bits offset + emit_byte(0x0F); + emit_byte(0x1F); + emit_byte(0x84); // emit_rm(cbuf, 0x2, EAX_enc, 0x4); + emit_byte(0x00); // emit_rm(cbuf, 0x0, EAX_enc, EAX_enc); + emit_long(0); // 32-bits offset (4 bytes) +} + +void Assembler::nop(int i) { + assert(i > 0, " "); + if (UseAddressNop && VM_Version::is_intel()) { + // + // Using multi-bytes nops "0x0F 0x1F [address]" for Intel + // 1: 0x90 + // 2: 0x66 0x90 + // 3: 0x66 0x66 0x90 (don't use "0x0F 0x1F 0x00" - need patching safe padding) + // 4: 0x0F 0x1F 0x40 0x00 + // 5: 0x0F 0x1F 0x44 0x00 0x00 + // 6: 0x66 0x0F 0x1F 0x44 0x00 0x00 + // 7: 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 + // 8: 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 9: 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 10: 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 11: 0x66 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + + // The rest coding is Intel specific - don't use consecutive address nops + + // 12: 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x66 0x66 0x66 0x90 + // 13: 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x66 0x66 0x66 0x90 + // 14: 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x66 0x66 0x66 0x90 + // 15: 0x66 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x66 0x66 0x66 0x90 + + while(i >= 15) { + // For Intel don't generate consecutive addess nops (mix with regular nops) + i -= 15; + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + addr_nop_8(); + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x90); // nop + } + switch (i) { + case 14: + emit_byte(0x66); // size prefix + case 13: + emit_byte(0x66); // size prefix + case 12: + addr_nop_8(); + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x90); // nop + break; + case 11: + emit_byte(0x66); // size prefix + case 10: + emit_byte(0x66); // size prefix + case 9: + emit_byte(0x66); // size prefix + case 8: + addr_nop_8(); + break; + case 7: + addr_nop_7(); + break; + case 6: + emit_byte(0x66); // size prefix + case 5: + addr_nop_5(); + break; + case 4: + addr_nop_4(); + break; + case 3: + // Don't use "0x0F 0x1F 0x00" - need patching safe padding + emit_byte(0x66); // size prefix + case 2: + emit_byte(0x66); // size prefix + case 1: + emit_byte(0x90); // nop + break; + default: + assert(i == 0, " "); + } + return; + } + if (UseAddressNop && VM_Version::is_amd()) { + // + // Using multi-bytes nops "0x0F 0x1F [address]" for AMD. + // 1: 0x90 + // 2: 0x66 0x90 + // 3: 0x66 0x66 0x90 (don't use "0x0F 0x1F 0x00" - need patching safe padding) + // 4: 0x0F 0x1F 0x40 0x00 + // 5: 0x0F 0x1F 0x44 0x00 0x00 + // 6: 0x66 0x0F 0x1F 0x44 0x00 0x00 + // 7: 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 + // 8: 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 9: 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 10: 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 11: 0x66 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + + // The rest coding is AMD specific - use consecutive address nops + + // 12: 0x66 0x0F 0x1F 0x44 0x00 0x00 0x66 0x0F 0x1F 0x44 0x00 0x00 + // 13: 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 0x66 0x0F 0x1F 0x44 0x00 0x00 + // 14: 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 + // 15: 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 + // 16: 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // Size prefixes (0x66) are added for larger sizes + + while(i >= 22) { + i -= 11; + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + addr_nop_8(); + } + // Generate first nop for size between 21-12 + switch (i) { + case 21: + i -= 1; + emit_byte(0x66); // size prefix + case 20: + case 19: + i -= 1; + emit_byte(0x66); // size prefix + case 18: + case 17: + i -= 1; + emit_byte(0x66); // size prefix + case 16: + case 15: + i -= 8; + addr_nop_8(); + break; + case 14: + case 13: + i -= 7; + addr_nop_7(); + break; + case 12: + i -= 6; + emit_byte(0x66); // size prefix + addr_nop_5(); + break; + default: + assert(i < 12, " "); + } + + // Generate second nop for size between 11-1 + switch (i) { + case 11: + emit_byte(0x66); // size prefix + case 10: + emit_byte(0x66); // size prefix + case 9: + emit_byte(0x66); // size prefix + case 8: + addr_nop_8(); + break; + case 7: + addr_nop_7(); + break; + case 6: + emit_byte(0x66); // size prefix + case 5: + addr_nop_5(); + break; + case 4: + addr_nop_4(); + break; + case 3: + // Don't use "0x0F 0x1F 0x00" - need patching safe padding + emit_byte(0x66); // size prefix + case 2: + emit_byte(0x66); // size prefix + case 1: + emit_byte(0x90); // nop + break; + default: + assert(i == 0, " "); + } + return; + } + + // Using nops with size prefixes "0x66 0x90". + // From AMD Optimization Guide: + // 1: 0x90 + // 2: 0x66 0x90 + // 3: 0x66 0x66 0x90 + // 4: 0x66 0x66 0x66 0x90 + // 5: 0x66 0x66 0x90 0x66 0x90 + // 6: 0x66 0x66 0x90 0x66 0x66 0x90 + // 7: 0x66 0x66 0x66 0x90 0x66 0x66 0x90 + // 8: 0x66 0x66 0x66 0x90 0x66 0x66 0x66 0x90 + // 9: 0x66 0x66 0x90 0x66 0x66 0x90 0x66 0x66 0x90 + // 10: 0x66 0x66 0x66 0x90 0x66 0x66 0x90 0x66 0x66 0x90 + // + while(i > 12) { + i -= 4; + emit_byte(0x66); // size prefix + emit_byte(0x66); + emit_byte(0x66); + emit_byte(0x90); // nop + } + // 1 - 12 nops + if(i > 8) { + if(i > 9) { + i -= 1; + emit_byte(0x66); + } + i -= 3; + emit_byte(0x66); + emit_byte(0x66); + emit_byte(0x90); + } + // 1 - 8 nops + if(i > 4) { + if(i > 6) { + i -= 1; + emit_byte(0x66); + } + i -= 3; + emit_byte(0x66); + emit_byte(0x66); + emit_byte(0x90); + } + switch (i) { + case 4: + emit_byte(0x66); + case 3: + emit_byte(0x66); + case 2: + emit_byte(0x66); + case 1: + emit_byte(0x90); + break; + default: + assert(i == 0, " "); + } +} + +void Assembler::ret(int imm16) { + if (imm16 == 0) { + emit_byte(0xC3); + } else { + emit_byte(0xC2); + emit_word(imm16); + } +} + + +void Assembler::set_byte_if_not_zero(Register dst) { + emit_byte(0x0F); + emit_byte(0x95); + emit_byte(0xE0 | dst->encoding()); +} + + +// copies a single word from [esi] to [edi] +void Assembler::smovl() { + emit_byte(0xA5); +} + +// copies data from [esi] to [edi] using rcx double words (m32) +void Assembler::rep_movl() { + emit_byte(0xF3); + emit_byte(0xA5); +} + + +// sets rcx double words (m32) with rax, value at [edi] +void Assembler::rep_set() { + emit_byte(0xF3); + emit_byte(0xAB); +} + +// scans rcx double words (m32) at [edi] for occurance of rax, +void Assembler::repne_scan() { + emit_byte(0xF2); + emit_byte(0xAF); +} + + +void Assembler::setb(Condition cc, Register dst) { + assert(0 <= cc && cc < 16, "illegal cc"); + emit_byte(0x0F); + emit_byte(0x90 | cc); + emit_byte(0xC0 | dst->encoding()); +} + +void Assembler::cld() { + emit_byte(0xfc); +} + +void Assembler::std() { + emit_byte(0xfd); +} + +void Assembler::emit_raw (unsigned char b) { + emit_byte (b) ; +} + +// Serializes memory. +void Assembler::membar() { + // Memory barriers are only needed on multiprocessors + if (os::is_MP()) { + if( VM_Version::supports_sse2() ) { + emit_byte( 0x0F ); // MFENCE; faster blows no regs + emit_byte( 0xAE ); + emit_byte( 0xF0 ); + } else { + // All usable chips support "locked" instructions which suffice + // as barriers, and are much faster than the alternative of + // using cpuid instruction. We use here a locked add [esp],0. + // This is conveniently otherwise a no-op except for blowing + // flags (which we save and restore.) + pushfd(); // Save eflags register + lock(); + addl(Address(rsp, 0), 0);// Assert the lock# signal here + popfd(); // Restore eflags register + } + } +} + +// Identify processor type and features +void Assembler::cpuid() { + // Note: we can't assert VM_Version::supports_cpuid() here + // because this instruction is used in the processor + // identification code. + emit_byte( 0x0F ); + emit_byte( 0xA2 ); +} + +void Assembler::call(Label& L, relocInfo::relocType rtype) { + if (L.is_bound()) { + const int long_size = 5; + int offs = target(L) - pc(); + assert(offs <= 0, "assembler error"); + InstructionMark im(this); + // 1110 1000 #32-bit disp + emit_byte(0xE8); + emit_data(offs - long_size, rtype, 0); + } else { + InstructionMark im(this); + // 1110 1000 #32-bit disp + L.add_patch_at(code(), locator()); + emit_byte(0xE8); + emit_data(int(0), rtype, 0); + } +} + +void Assembler::call(Register dst) { + emit_byte(0xFF); + emit_byte(0xD0 | dst->encoding()); +} + + +void Assembler::call(Address adr) { + InstructionMark im(this); + relocInfo::relocType rtype = adr.reloc(); + if (rtype != relocInfo::runtime_call_type) { + emit_byte(0xFF); + emit_operand(rdx, adr); + } else { + assert(false, "ack"); + } + +} + +void Assembler::call_literal(address dest, RelocationHolder const& rspec) { + InstructionMark im(this); + emit_byte(0xE8); + intptr_t disp = dest - (_code_pos + sizeof(int32_t)); + assert(dest != NULL, "must have a target"); + emit_data(disp, rspec, call32_operand); + +} + +void Assembler::jmp(Register entry) { + emit_byte(0xFF); + emit_byte(0xE0 | entry->encoding()); +} + + +void Assembler::jmp(Address adr) { + InstructionMark im(this); + emit_byte(0xFF); + emit_operand(rsp, adr); +} + +void Assembler::jmp_literal(address dest, RelocationHolder const& rspec) { + InstructionMark im(this); + emit_byte(0xE9); + assert(dest != NULL, "must have a target"); + intptr_t disp = dest - (_code_pos + sizeof(int32_t)); + emit_data(disp, rspec.reloc(), call32_operand); +} + +void Assembler::jmp(Label& L, relocInfo::relocType rtype) { + if (L.is_bound()) { + address entry = target(L); + assert(entry != NULL, "jmp most probably wrong"); + InstructionMark im(this); + const int short_size = 2; + const int long_size = 5; + intptr_t offs = entry - _code_pos; + if (rtype == relocInfo::none && is8bit(offs - short_size)) { + emit_byte(0xEB); + emit_byte((offs - short_size) & 0xFF); + } else { + emit_byte(0xE9); + emit_long(offs - long_size); + } + } else { + // By default, forward jumps are always 32-bit displacements, since + // we can't yet know where the label will be bound. If you're sure that + // the forward jump will not run beyond 256 bytes, use jmpb to + // force an 8-bit displacement. + InstructionMark im(this); + relocate(rtype); + L.add_patch_at(code(), locator()); + emit_byte(0xE9); + emit_long(0); + } +} + +void Assembler::jmpb(Label& L) { + if (L.is_bound()) { + const int short_size = 2; + address entry = target(L); + assert(is8bit((entry - _code_pos) + short_size), + "Dispacement too large for a short jmp"); + assert(entry != NULL, "jmp most probably wrong"); + intptr_t offs = entry - _code_pos; + emit_byte(0xEB); + emit_byte((offs - short_size) & 0xFF); + } else { + InstructionMark im(this); + L.add_patch_at(code(), locator()); + emit_byte(0xEB); + emit_byte(0); + } +} + +void Assembler::jcc(Condition cc, Label& L, relocInfo::relocType rtype) { + InstructionMark im(this); + relocate(rtype); + assert((0 <= cc) && (cc < 16), "illegal cc"); + if (L.is_bound()) { + address dst = target(L); + assert(dst != NULL, "jcc most probably wrong"); + + const int short_size = 2; + const int long_size = 6; + int offs = (int)dst - ((int)_code_pos); + if (rtype == relocInfo::none && is8bit(offs - short_size)) { + // 0111 tttn #8-bit disp + emit_byte(0x70 | cc); + emit_byte((offs - short_size) & 0xFF); + } else { + // 0000 1111 1000 tttn #32-bit disp + emit_byte(0x0F); + emit_byte(0x80 | cc); + emit_long(offs - long_size); + } + } else { + // Note: could eliminate cond. jumps to this jump if condition + // is the same however, seems to be rather unlikely case. + // Note: use jccb() if label to be bound is very close to get + // an 8-bit displacement + L.add_patch_at(code(), locator()); + emit_byte(0x0F); + emit_byte(0x80 | cc); + emit_long(0); + } +} + +void Assembler::jccb(Condition cc, Label& L) { + if (L.is_bound()) { + const int short_size = 2; + address entry = target(L); + assert(is8bit((intptr_t)entry - ((intptr_t)_code_pos + short_size)), + "Dispacement too large for a short jmp"); + intptr_t offs = (intptr_t)entry - (intptr_t)_code_pos; + // 0111 tttn #8-bit disp + emit_byte(0x70 | cc); + emit_byte((offs - short_size) & 0xFF); + jcc(cc, L); + } else { + InstructionMark im(this); + L.add_patch_at(code(), locator()); + emit_byte(0x70 | cc); + emit_byte(0); + } +} + +// FPU instructions + +void Assembler::fld1() { + emit_byte(0xD9); + emit_byte(0xE8); +} + + +void Assembler::fldz() { + emit_byte(0xD9); + emit_byte(0xEE); +} + + +void Assembler::fld_s(Address adr) { + InstructionMark im(this); + emit_byte(0xD9); + emit_operand(rax, adr); +} + + +void Assembler::fld_s (int index) { + emit_farith(0xD9, 0xC0, index); +} + + +void Assembler::fld_d(Address adr) { + InstructionMark im(this); + emit_byte(0xDD); + emit_operand(rax, adr); +} + + +void Assembler::fld_x(Address adr) { + InstructionMark im(this); + emit_byte(0xDB); + emit_operand(rbp, adr); +} + + +void Assembler::fst_s(Address adr) { + InstructionMark im(this); + emit_byte(0xD9); + emit_operand(rdx, adr); +} + + +void Assembler::fst_d(Address adr) { + InstructionMark im(this); + emit_byte(0xDD); + emit_operand(rdx, adr); +} + + +void Assembler::fstp_s(Address adr) { + InstructionMark im(this); + emit_byte(0xD9); + emit_operand(rbx, adr); +} + + +void Assembler::fstp_d(Address adr) { + InstructionMark im(this); + emit_byte(0xDD); + emit_operand(rbx, adr); +} + + +void Assembler::fstp_x(Address adr) { + InstructionMark im(this); + emit_byte(0xDB); + emit_operand(rdi, adr); +} + + +void Assembler::fstp_d(int index) { + emit_farith(0xDD, 0xD8, index); +} + + +void Assembler::fild_s(Address adr) { + InstructionMark im(this); + emit_byte(0xDB); + emit_operand(rax, adr); +} + + +void Assembler::fild_d(Address adr) { + InstructionMark im(this); + emit_byte(0xDF); + emit_operand(rbp, adr); +} + + +void Assembler::fistp_s(Address adr) { + InstructionMark im(this); + emit_byte(0xDB); + emit_operand(rbx, adr); +} + + +void Assembler::fistp_d(Address adr) { + InstructionMark im(this); + emit_byte(0xDF); + emit_operand(rdi, adr); +} + + +void Assembler::fist_s(Address adr) { + InstructionMark im(this); + emit_byte(0xDB); + emit_operand(rdx, adr); +} + + +void Assembler::fabs() { + emit_byte(0xD9); + emit_byte(0xE1); +} + + +void Assembler::fldln2() { + emit_byte(0xD9); + emit_byte(0xED); +} + +void Assembler::fyl2x() { + emit_byte(0xD9); + emit_byte(0xF1); +} + + +void Assembler::fldlg2() { + emit_byte(0xD9); + emit_byte(0xEC); +} + + +void Assembler::flog() { + fldln2(); + fxch(); + fyl2x(); +} + + +void Assembler::flog10() { + fldlg2(); + fxch(); + fyl2x(); +} + + +void Assembler::fsin() { + emit_byte(0xD9); + emit_byte(0xFE); +} + + +void Assembler::fcos() { + emit_byte(0xD9); + emit_byte(0xFF); +} + +void Assembler::ftan() { + emit_byte(0xD9); + emit_byte(0xF2); + emit_byte(0xDD); + emit_byte(0xD8); +} + +void Assembler::fsqrt() { + emit_byte(0xD9); + emit_byte(0xFA); +} + + +void Assembler::fchs() { + emit_byte(0xD9); + emit_byte(0xE0); +} + + +void Assembler::fadd_s(Address src) { + InstructionMark im(this); + emit_byte(0xD8); + emit_operand(rax, src); +} + + +void Assembler::fadd_d(Address src) { + InstructionMark im(this); + emit_byte(0xDC); + emit_operand(rax, src); +} + + +void Assembler::fadd(int i) { + emit_farith(0xD8, 0xC0, i); +} + + +void Assembler::fadda(int i) { + emit_farith(0xDC, 0xC0, i); +} + + +void Assembler::fsub_d(Address src) { + InstructionMark im(this); + emit_byte(0xDC); + emit_operand(rsp, src); +} + + +void Assembler::fsub_s(Address src) { + InstructionMark im(this); + emit_byte(0xD8); + emit_operand(rsp, src); +} + + +void Assembler::fsubr_s(Address src) { + InstructionMark im(this); + emit_byte(0xD8); + emit_operand(rbp, src); +} + + +void Assembler::fsubr_d(Address src) { + InstructionMark im(this); + emit_byte(0xDC); + emit_operand(rbp, src); +} + + +void Assembler::fmul_s(Address src) { + InstructionMark im(this); + emit_byte(0xD8); + emit_operand(rcx, src); +} + + +void Assembler::fmul_d(Address src) { + InstructionMark im(this); + emit_byte(0xDC); + emit_operand(rcx, src); +} + + +void Assembler::fmul(int i) { + emit_farith(0xD8, 0xC8, i); +} + + +void Assembler::fmula(int i) { + emit_farith(0xDC, 0xC8, i); +} + + +void Assembler::fdiv_s(Address src) { + InstructionMark im(this); + emit_byte(0xD8); + emit_operand(rsi, src); +} + + +void Assembler::fdiv_d(Address src) { + InstructionMark im(this); + emit_byte(0xDC); + emit_operand(rsi, src); +} + + +void Assembler::fdivr_s(Address src) { + InstructionMark im(this); + emit_byte(0xD8); + emit_operand(rdi, src); +} + + +void Assembler::fdivr_d(Address src) { + InstructionMark im(this); + emit_byte(0xDC); + emit_operand(rdi, src); +} + + +void Assembler::fsub(int i) { + emit_farith(0xD8, 0xE0, i); +} + + +void Assembler::fsuba(int i) { + emit_farith(0xDC, 0xE8, i); +} + + +void Assembler::fsubr(int i) { + emit_farith(0xD8, 0xE8, i); +} + + +void Assembler::fsubra(int i) { + emit_farith(0xDC, 0xE0, i); +} + + +void Assembler::fdiv(int i) { + emit_farith(0xD8, 0xF0, i); +} + + +void Assembler::fdiva(int i) { + emit_farith(0xDC, 0xF8, i); +} + + +void Assembler::fdivr(int i) { + emit_farith(0xD8, 0xF8, i); +} + + +void Assembler::fdivra(int i) { + emit_farith(0xDC, 0xF0, i); +} + + +// Note: The Intel manual (Pentium Processor User's Manual, Vol.3, 1994) +// is erroneous for some of the floating-point instructions below. + +void Assembler::fdivp(int i) { + emit_farith(0xDE, 0xF8, i); // ST(0) <- ST(0) / ST(1) and pop (Intel manual wrong) +} + + +void Assembler::fdivrp(int i) { + emit_farith(0xDE, 0xF0, i); // ST(0) <- ST(1) / ST(0) and pop (Intel manual wrong) +} + + +void Assembler::fsubp(int i) { + emit_farith(0xDE, 0xE8, i); // ST(0) <- ST(0) - ST(1) and pop (Intel manual wrong) +} + + +void Assembler::fsubrp(int i) { + emit_farith(0xDE, 0xE0, i); // ST(0) <- ST(1) - ST(0) and pop (Intel manual wrong) +} + + +void Assembler::faddp(int i) { + emit_farith(0xDE, 0xC0, i); +} + + +void Assembler::fmulp(int i) { + emit_farith(0xDE, 0xC8, i); +} + + +void Assembler::fprem() { + emit_byte(0xD9); + emit_byte(0xF8); +} + + +void Assembler::fprem1() { + emit_byte(0xD9); + emit_byte(0xF5); +} + + +void Assembler::fxch(int i) { + emit_farith(0xD9, 0xC8, i); +} + + +void Assembler::fincstp() { + emit_byte(0xD9); + emit_byte(0xF7); +} + + +void Assembler::fdecstp() { + emit_byte(0xD9); + emit_byte(0xF6); +} + + +void Assembler::ffree(int i) { + emit_farith(0xDD, 0xC0, i); +} + + +void Assembler::fcomp_s(Address src) { + InstructionMark im(this); + emit_byte(0xD8); + emit_operand(rbx, src); +} + + +void Assembler::fcomp_d(Address src) { + InstructionMark im(this); + emit_byte(0xDC); + emit_operand(rbx, src); +} + + +void Assembler::fcom(int i) { + emit_farith(0xD8, 0xD0, i); +} + + +void Assembler::fcomp(int i) { + emit_farith(0xD8, 0xD8, i); +} + + +void Assembler::fcompp() { + emit_byte(0xDE); + emit_byte(0xD9); +} + + +void Assembler::fucomi(int i) { + // make sure the instruction is supported (introduced for P6, together with cmov) + guarantee(VM_Version::supports_cmov(), "illegal instruction"); + emit_farith(0xDB, 0xE8, i); +} + + +void Assembler::fucomip(int i) { + // make sure the instruction is supported (introduced for P6, together with cmov) + guarantee(VM_Version::supports_cmov(), "illegal instruction"); + emit_farith(0xDF, 0xE8, i); +} + + +void Assembler::ftst() { + emit_byte(0xD9); + emit_byte(0xE4); +} + + +void Assembler::fnstsw_ax() { + emit_byte(0xdF); + emit_byte(0xE0); +} + + +void Assembler::fwait() { + emit_byte(0x9B); +} + + +void Assembler::finit() { + emit_byte(0x9B); + emit_byte(0xDB); + emit_byte(0xE3); +} + + +void Assembler::fldcw(Address src) { + InstructionMark im(this); + emit_byte(0xd9); + emit_operand(rbp, src); +} + + +void Assembler::fnstcw(Address src) { + InstructionMark im(this); + emit_byte(0x9B); + emit_byte(0xD9); + emit_operand(rdi, src); +} + +void Assembler::fnsave(Address dst) { + InstructionMark im(this); + emit_byte(0xDD); + emit_operand(rsi, dst); +} + + +void Assembler::frstor(Address src) { + InstructionMark im(this); + emit_byte(0xDD); + emit_operand(rsp, src); +} + + +void Assembler::fldenv(Address src) { + InstructionMark im(this); + emit_byte(0xD9); + emit_operand(rsp, src); +} + + +void Assembler::sahf() { + emit_byte(0x9E); +} + +// MMX operations +void Assembler::emit_operand(MMXRegister reg, Address adr) { + emit_operand((Register)reg, adr._base, adr._index, adr._scale, adr._disp, adr._rspec); +} + +void Assembler::movq( MMXRegister dst, Address src ) { + assert( VM_Version::supports_mmx(), "" ); + emit_byte(0x0F); + emit_byte(0x6F); + emit_operand(dst,src); +} + +void Assembler::movq( Address dst, MMXRegister src ) { + assert( VM_Version::supports_mmx(), "" ); + emit_byte(0x0F); + emit_byte(0x7F); + emit_operand(src,dst); +} + +void Assembler::emms() { + emit_byte(0x0F); + emit_byte(0x77); +} + + + + +// SSE and SSE2 instructions +inline void Assembler::emit_sse_operand(XMMRegister reg, Address adr) { + assert(((Register)reg)->encoding() == reg->encoding(), "otherwise typecast is invalid"); + emit_operand((Register)reg, adr._base, adr._index, adr._scale, adr._disp, adr._rspec); +} +inline void Assembler::emit_sse_operand(Register reg, Address adr) { + emit_operand(reg, adr._base, adr._index, adr._scale, adr._disp, adr._rspec); +} + +inline void Assembler::emit_sse_operand(XMMRegister dst, XMMRegister src) { + emit_byte(0xC0 | dst->encoding() << 3 | src->encoding()); +} +inline void Assembler::emit_sse_operand(XMMRegister dst, Register src) { + emit_byte(0xC0 | dst->encoding() << 3 | src->encoding()); +} +inline void Assembler::emit_sse_operand(Register dst, XMMRegister src) { + emit_byte(0xC0 | dst->encoding() << 3 | src->encoding()); +} + + +// Macro for creation of SSE2 instructions +// The SSE2 instricution set is highly regular, so this macro saves +// a lot of cut&paste +// Each macro expansion creates two methods (same name with different +// parameter list) +// +// Macro parameters: +// * name: name of the created methods +// * sse_version: either sse or sse2 for the assertion if instruction supported by processor +// * prefix: first opcode byte of the instruction (or 0 if no prefix byte) +// * opcode: last opcode byte of the instruction +// * conversion instruction have parameters of type Register instead of XMMRegister, +// so this can also configured with macro parameters +#define emit_sse_instruction(name, sse_version, prefix, opcode, dst_register_type, src_register_type) \ + \ + void Assembler:: name (dst_register_type dst, Address src) { \ + assert(VM_Version::supports_##sse_version(), ""); \ + \ + InstructionMark im(this); \ + if (prefix != 0) emit_byte(prefix); \ + emit_byte(0x0F); \ + emit_byte(opcode); \ + emit_sse_operand(dst, src); \ + } \ + \ + void Assembler:: name (dst_register_type dst, src_register_type src) { \ + assert(VM_Version::supports_##sse_version(), ""); \ + \ + if (prefix != 0) emit_byte(prefix); \ + emit_byte(0x0F); \ + emit_byte(opcode); \ + emit_sse_operand(dst, src); \ + } \ + +emit_sse_instruction(addss, sse, 0xF3, 0x58, XMMRegister, XMMRegister); +emit_sse_instruction(addsd, sse2, 0xF2, 0x58, XMMRegister, XMMRegister) +emit_sse_instruction(subss, sse, 0xF3, 0x5C, XMMRegister, XMMRegister) +emit_sse_instruction(subsd, sse2, 0xF2, 0x5C, XMMRegister, XMMRegister) +emit_sse_instruction(mulss, sse, 0xF3, 0x59, XMMRegister, XMMRegister) +emit_sse_instruction(mulsd, sse2, 0xF2, 0x59, XMMRegister, XMMRegister) +emit_sse_instruction(divss, sse, 0xF3, 0x5E, XMMRegister, XMMRegister) +emit_sse_instruction(divsd, sse2, 0xF2, 0x5E, XMMRegister, XMMRegister) +emit_sse_instruction(sqrtss, sse, 0xF3, 0x51, XMMRegister, XMMRegister) +emit_sse_instruction(sqrtsd, sse2, 0xF2, 0x51, XMMRegister, XMMRegister) + +emit_sse_instruction(pxor, sse2, 0x66, 0xEF, XMMRegister, XMMRegister) + +emit_sse_instruction(comiss, sse, 0, 0x2F, XMMRegister, XMMRegister) +emit_sse_instruction(comisd, sse2, 0x66, 0x2F, XMMRegister, XMMRegister) +emit_sse_instruction(ucomiss, sse, 0, 0x2E, XMMRegister, XMMRegister) +emit_sse_instruction(ucomisd, sse2, 0x66, 0x2E, XMMRegister, XMMRegister) + +emit_sse_instruction(cvtss2sd, sse2, 0xF3, 0x5A, XMMRegister, XMMRegister); +emit_sse_instruction(cvtsd2ss, sse2, 0xF2, 0x5A, XMMRegister, XMMRegister) +emit_sse_instruction(cvtsi2ss, sse, 0xF3, 0x2A, XMMRegister, Register); +emit_sse_instruction(cvtsi2sd, sse2, 0xF2, 0x2A, XMMRegister, Register) +emit_sse_instruction(cvtss2si, sse, 0xF3, 0x2D, Register, XMMRegister); +emit_sse_instruction(cvtsd2si, sse2, 0xF2, 0x2D, Register, XMMRegister) +emit_sse_instruction(cvttss2si, sse, 0xF3, 0x2C, Register, XMMRegister); +emit_sse_instruction(cvttsd2si, sse2, 0xF2, 0x2C, Register, XMMRegister) + +emit_sse_instruction(movss, sse, 0xF3, 0x10, XMMRegister, XMMRegister) +emit_sse_instruction(movsd, sse2, 0xF2, 0x10, XMMRegister, XMMRegister) + +emit_sse_instruction(movq, sse2, 0xF3, 0x7E, XMMRegister, XMMRegister); +emit_sse_instruction(movd, sse2, 0x66, 0x6E, XMMRegister, Register); +emit_sse_instruction(movdqa, sse2, 0x66, 0x6F, XMMRegister, XMMRegister); + +emit_sse_instruction(punpcklbw, sse2, 0x66, 0x60, XMMRegister, XMMRegister); + + +// Instruction not covered by macro +void Assembler::movq(Address dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + + InstructionMark im(this); + emit_byte(0x66); + emit_byte(0x0F); + emit_byte(0xD6); + emit_sse_operand(src, dst); +} + +void Assembler::movd(Address dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + + InstructionMark im(this); + emit_byte(0x66); + emit_byte(0x0F); + emit_byte(0x7E); + emit_sse_operand(src, dst); +} + +void Assembler::movd(Register dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + + emit_byte(0x66); + emit_byte(0x0F); + emit_byte(0x7E); + emit_sse_operand(src, dst); +} + +void Assembler::movdqa(Address dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + + InstructionMark im(this); + emit_byte(0x66); + emit_byte(0x0F); + emit_byte(0x7F); + emit_sse_operand(src, dst); +} + +void Assembler::pshufd(XMMRegister dst, XMMRegister src, int mode) { + assert(isByte(mode), "invalid value"); + assert(VM_Version::supports_sse2(), ""); + + emit_byte(0x66); + emit_byte(0x0F); + emit_byte(0x70); + emit_sse_operand(dst, src); + emit_byte(mode & 0xFF); +} + +void Assembler::pshufd(XMMRegister dst, Address src, int mode) { + assert(isByte(mode), "invalid value"); + assert(VM_Version::supports_sse2(), ""); + + InstructionMark im(this); + emit_byte(0x66); + emit_byte(0x0F); + emit_byte(0x70); + emit_sse_operand(dst, src); + emit_byte(mode & 0xFF); +} + +void Assembler::pshuflw(XMMRegister dst, XMMRegister src, int mode) { + assert(isByte(mode), "invalid value"); + assert(VM_Version::supports_sse2(), ""); + + emit_byte(0xF2); + emit_byte(0x0F); + emit_byte(0x70); + emit_sse_operand(dst, src); + emit_byte(mode & 0xFF); +} + +void Assembler::pshuflw(XMMRegister dst, Address src, int mode) { + assert(isByte(mode), "invalid value"); + assert(VM_Version::supports_sse2(), ""); + + InstructionMark im(this); + emit_byte(0xF2); + emit_byte(0x0F); + emit_byte(0x70); + emit_sse_operand(dst, src); + emit_byte(mode & 0xFF); +} + +void Assembler::psrlq(XMMRegister dst, int shift) { + assert(VM_Version::supports_sse2(), ""); + + emit_byte(0x66); + emit_byte(0x0F); + emit_byte(0x73); + emit_sse_operand(xmm2, dst); + emit_byte(shift); +} + +void Assembler::movss( Address dst, XMMRegister src ) { + assert(VM_Version::supports_sse(), ""); + + InstructionMark im(this); + emit_byte(0xF3); // single + emit_byte(0x0F); + emit_byte(0x11); // store + emit_sse_operand(src, dst); +} + +void Assembler::movsd( Address dst, XMMRegister src ) { + assert(VM_Version::supports_sse2(), ""); + + InstructionMark im(this); + emit_byte(0xF2); // double + emit_byte(0x0F); + emit_byte(0x11); // store + emit_sse_operand(src,dst); +} + +// New cpus require to use movaps and movapd to avoid partial register stall +// when moving between registers. +void Assembler::movaps(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse(), ""); + + emit_byte(0x0F); + emit_byte(0x28); + emit_sse_operand(dst, src); +} +void Assembler::movapd(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + + emit_byte(0x66); + emit_byte(0x0F); + emit_byte(0x28); + emit_sse_operand(dst, src); +} + +// New cpus require to use movsd and movss to avoid partial register stall +// when loading from memory. But for old Opteron use movlpd instead of movsd. +// The selection is done in MacroAssembler::movdbl() and movflt(). +void Assembler::movlpd(XMMRegister dst, Address src) { + assert(VM_Version::supports_sse(), ""); + + InstructionMark im(this); + emit_byte(0x66); + emit_byte(0x0F); + emit_byte(0x12); + emit_sse_operand(dst, src); +} + + +emit_sse_instruction(andps, sse, 0, 0x54, XMMRegister, XMMRegister); +emit_sse_instruction(andpd, sse2, 0x66, 0x54, XMMRegister, XMMRegister); +emit_sse_instruction(andnps, sse, 0, 0x55, XMMRegister, XMMRegister); +emit_sse_instruction(andnpd, sse2, 0x66, 0x55, XMMRegister, XMMRegister); +emit_sse_instruction(orps, sse, 0, 0x56, XMMRegister, XMMRegister); +emit_sse_instruction(orpd, sse2, 0x66, 0x56, XMMRegister, XMMRegister); +emit_sse_instruction(xorps, sse, 0, 0x57, XMMRegister, XMMRegister); +emit_sse_instruction(xorpd, sse2, 0x66, 0x57, XMMRegister, XMMRegister); + + +void Assembler::ldmxcsr( Address src) { + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0xAE); + emit_operand(rdx /* 2 */, src); +} + +void Assembler::stmxcsr( Address dst) { + InstructionMark im(this); + emit_byte(0x0F); + emit_byte(0xAE); + emit_operand(rbx /* 3 */, dst); +} + +// Implementation of MacroAssembler + +Address MacroAssembler::as_Address(AddressLiteral adr) { + // amd64 always does this as a pc-rel + // we can be absolute or disp based on the instruction type + // jmp/call are displacements others are absolute + assert(!adr.is_lval(), "must be rval"); + + return Address(adr.target(), adr.rspec()); +} + +Address MacroAssembler::as_Address(ArrayAddress adr) { + return Address::make_array(adr); +} + +void MacroAssembler::fat_nop() { + // A 5 byte nop that is safe for patching (see patch_verified_entry) + emit_byte(0x26); // es: + emit_byte(0x2e); // cs: + emit_byte(0x64); // fs: + emit_byte(0x65); // gs: + emit_byte(0x90); +} + +// 32bit can do a case table jump in one instruction but we no longer allow the base +// to be installed in the Address class +void MacroAssembler::jump(ArrayAddress entry) { + jmp(as_Address(entry)); +} + +void MacroAssembler::jump(AddressLiteral dst) { + jmp_literal(dst.target(), dst.rspec()); +} + +void MacroAssembler::jump_cc(Condition cc, AddressLiteral dst) { + assert((0 <= cc) && (cc < 16), "illegal cc"); + + InstructionMark im(this); + + relocInfo::relocType rtype = dst.reloc(); + relocate(rtype); + const int short_size = 2; + const int long_size = 6; + int offs = (int)dst.target() - ((int)_code_pos); + if (rtype == relocInfo::none && is8bit(offs - short_size)) { + // 0111 tttn #8-bit disp + emit_byte(0x70 | cc); + emit_byte((offs - short_size) & 0xFF); + } else { + // 0000 1111 1000 tttn #32-bit disp + emit_byte(0x0F); + emit_byte(0x80 | cc); + emit_long(offs - long_size); + } +} + +// Calls +void MacroAssembler::call(Label& L, relocInfo::relocType rtype) { + Assembler::call(L, rtype); +} + +void MacroAssembler::call(Register entry) { + Assembler::call(entry); +} + +void MacroAssembler::call(AddressLiteral entry) { + Assembler::call_literal(entry.target(), entry.rspec()); +} + + +void MacroAssembler::cmp8(AddressLiteral src1, int8_t imm) { + Assembler::cmpb(as_Address(src1), imm); +} + +void MacroAssembler::cmp32(AddressLiteral src1, int32_t imm) { + Assembler::cmpl(as_Address(src1), imm); +} + +void MacroAssembler::cmp32(Register src1, AddressLiteral src2) { + if (src2.is_lval()) { + cmp_literal32(src1, (int32_t) src2.target(), src2.rspec()); + } else { + Assembler::cmpl(src1, as_Address(src2)); + } +} + +void MacroAssembler::cmp32(Register src1, int32_t imm) { + Assembler::cmpl(src1, imm); +} + +void MacroAssembler::cmp32(Register src1, Address src2) { + Assembler::cmpl(src1, src2); +} + +void MacroAssembler::cmpoop(Address src1, jobject obj) { + cmp_literal32(src1, (int32_t)obj, oop_Relocation::spec_for_immediate()); +} + +void MacroAssembler::cmpoop(Register src1, jobject obj) { + cmp_literal32(src1, (int32_t)obj, oop_Relocation::spec_for_immediate()); +} + +void MacroAssembler::cmpptr(Register src1, AddressLiteral src2) { + if (src2.is_lval()) { + // compare the effect address of src2 to src1 + cmp_literal32(src1, (int32_t)src2.target(), src2.rspec()); + } else { + Assembler::cmpl(src1, as_Address(src2)); + } +} + +void MacroAssembler::cmpptr(Address src1, AddressLiteral src2) { + assert(src2.is_lval(), "not a mem-mem compare"); + cmp_literal32(src1, (int32_t) src2.target(), src2.rspec()); +} + + +void MacroAssembler::cmpxchgptr(Register reg, AddressLiteral adr) { + cmpxchg(reg, as_Address(adr)); +} + +void MacroAssembler::increment(AddressLiteral dst) { + increment(as_Address(dst)); +} + +void MacroAssembler::increment(ArrayAddress dst) { + increment(as_Address(dst)); +} + +void MacroAssembler::lea(Register dst, AddressLiteral adr) { + // leal(dst, as_Address(adr)); + // see note in movl as to why we musr use a move + mov_literal32(dst, (int32_t) adr.target(), adr.rspec()); +} + +void MacroAssembler::lea(Address dst, AddressLiteral adr) { + // leal(dst, as_Address(adr)); + // see note in movl as to why we musr use a move + mov_literal32(dst, (int32_t) adr.target(), adr.rspec()); +} + +void MacroAssembler::mov32(AddressLiteral dst, Register src) { + Assembler::movl(as_Address(dst), src); +} + +void MacroAssembler::mov32(Register dst, AddressLiteral src) { + Assembler::movl(dst, as_Address(src)); +} + +void MacroAssembler::movbyte(ArrayAddress dst, int src) { + movb(as_Address(dst), src); +} + +void MacroAssembler::movoop(Address dst, jobject obj) { + mov_literal32(dst, (int32_t)obj, oop_Relocation::spec_for_immediate()); +} + +void MacroAssembler::movoop(Register dst, jobject obj) { + mov_literal32(dst, (int32_t)obj, oop_Relocation::spec_for_immediate()); +} + +void MacroAssembler::movptr(Register dst, AddressLiteral src) { + if (src.is_lval()) { + // essentially an lea + mov_literal32(dst, (int32_t) src.target(), src.rspec()); + } else { + // mov 32bits from an absolute address + movl(dst, as_Address(src)); + } +} + +void MacroAssembler::movptr(ArrayAddress dst, Register src) { + movl(as_Address(dst), src); +} + +void MacroAssembler::movptr(Register dst, ArrayAddress src) { + movl(dst, as_Address(src)); +} + +void MacroAssembler::movflt(XMMRegister dst, AddressLiteral src) { + movss(dst, as_Address(src)); +} + +void MacroAssembler::movdbl(XMMRegister dst, AddressLiteral src) { + if (UseXmmLoadAndClearUpper) { movsd (dst, as_Address(src)); return; } + else { movlpd(dst, as_Address(src)); return; } +} + +void Assembler::pushoop(jobject obj) { + push_literal32((int32_t)obj, oop_Relocation::spec_for_immediate()); +} + + +void MacroAssembler::pushptr(AddressLiteral src) { + if (src.is_lval()) { + push_literal32((int32_t)src.target(), src.rspec()); + } else { + pushl(as_Address(src)); + } +} + +void MacroAssembler::test32(Register src1, AddressLiteral src2) { + // src2 must be rval + testl(src1, as_Address(src2)); +} + +// FPU + +void MacroAssembler::fld_x(AddressLiteral src) { + Assembler::fld_x(as_Address(src)); +} + +void MacroAssembler::fld_d(AddressLiteral src) { + fld_d(as_Address(src)); +} + +void MacroAssembler::fld_s(AddressLiteral src) { + fld_s(as_Address(src)); +} + +void MacroAssembler::fldcw(AddressLiteral src) { + Assembler::fldcw(as_Address(src)); +} + +void MacroAssembler::ldmxcsr(AddressLiteral src) { + Assembler::ldmxcsr(as_Address(src)); +} + +// SSE + +void MacroAssembler::andpd(XMMRegister dst, AddressLiteral src) { + andpd(dst, as_Address(src)); +} + +void MacroAssembler::comisd(XMMRegister dst, AddressLiteral src) { + comisd(dst, as_Address(src)); +} + +void MacroAssembler::comiss(XMMRegister dst, AddressLiteral src) { + comiss(dst, as_Address(src)); +} + +void MacroAssembler::movsd(XMMRegister dst, AddressLiteral src) { + movsd(dst, as_Address(src)); +} + +void MacroAssembler::movss(XMMRegister dst, AddressLiteral src) { + movss(dst, as_Address(src)); +} + +void MacroAssembler::xorpd(XMMRegister dst, AddressLiteral src) { + xorpd(dst, as_Address(src)); +} + +void MacroAssembler::xorps(XMMRegister dst, AddressLiteral src) { + xorps(dst, as_Address(src)); +} + +void MacroAssembler::ucomisd(XMMRegister dst, AddressLiteral src) { + ucomisd(dst, as_Address(src)); +} + +void MacroAssembler::ucomiss(XMMRegister dst, AddressLiteral src) { + ucomiss(dst, as_Address(src)); +} + +void MacroAssembler::null_check(Register reg, int offset) { + if (needs_explicit_null_check(offset)) { + // provoke OS NULL exception if reg = NULL by + // accessing M[reg] w/o changing any (non-CC) registers + cmpl(rax, Address(reg, 0)); + // Note: should probably use testl(rax, Address(reg, 0)); + // may be shorter code (however, this version of + // testl needs to be implemented first) + } else { + // nothing to do, (later) access of M[reg + offset] + // will provoke OS NULL exception if reg = NULL + } +} + + +int MacroAssembler::load_unsigned_byte(Register dst, Address src) { + // According to Intel Doc. AP-526, "Zero-Extension of Short", p.16, + // and "3.9 Partial Register Penalties", p. 22). + int off; + if (VM_Version::is_P6() || src.uses(dst)) { + off = offset(); + movzxb(dst, src); + } else { + xorl(dst, dst); + off = offset(); + movb(dst, src); + } + return off; +} + + +int MacroAssembler::load_unsigned_word(Register dst, Address src) { + // According to Intel Doc. AP-526, "Zero-Extension of Short", p.16, + // and "3.9 Partial Register Penalties", p. 22). + int off; + if (VM_Version::is_P6() || src.uses(dst)) { + off = offset(); + movzxw(dst, src); + } else { + xorl(dst, dst); + off = offset(); + movw(dst, src); + } + return off; +} + + +int MacroAssembler::load_signed_byte(Register dst, Address src) { + int off; + if (VM_Version::is_P6()) { + off = offset(); + movsxb(dst, src); + } else { + off = load_unsigned_byte(dst, src); + shll(dst, 24); + sarl(dst, 24); + } + return off; +} + + +int MacroAssembler::load_signed_word(Register dst, Address src) { + int off; + if (VM_Version::is_P6()) { + off = offset(); + movsxw(dst, src); + } else { + off = load_unsigned_word(dst, src); + shll(dst, 16); + sarl(dst, 16); + } + return off; +} + + +void MacroAssembler::extend_sign(Register hi, Register lo) { + // According to Intel Doc. AP-526, "Integer Divide", p.18. + if (VM_Version::is_P6() && hi == rdx && lo == rax) { + cdql(); + } else { + movl(hi, lo); + sarl(hi, 31); + } +} + + +void MacroAssembler::increment(Register reg, int value) { + if (value == min_jint) {addl(reg, value); return; } + if (value < 0) { decrement(reg, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { incl(reg); return; } + /* else */ { addl(reg, value) ; return; } +} + +void MacroAssembler::increment(Address dst, int value) { + if (value == min_jint) {addl(dst, value); return; } + if (value < 0) { decrement(dst, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { incl(dst); return; } + /* else */ { addl(dst, value) ; return; } +} + +void MacroAssembler::decrement(Register reg, int value) { + if (value == min_jint) {subl(reg, value); return; } + if (value < 0) { increment(reg, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { decl(reg); return; } + /* else */ { subl(reg, value) ; return; } +} + +void MacroAssembler::decrement(Address dst, int value) { + if (value == min_jint) {subl(dst, value); return; } + if (value < 0) { increment(dst, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { decl(dst); return; } + /* else */ { subl(dst, value) ; return; } +} + +void MacroAssembler::align(int modulus) { + if (offset() % modulus != 0) nop(modulus - (offset() % modulus)); +} + + +void MacroAssembler::enter() { + pushl(rbp); + movl(rbp, rsp); +} + + +void MacroAssembler::leave() { + movl(rsp, rbp); + popl(rbp); +} + +void MacroAssembler::set_last_Java_frame(Register java_thread, + Register last_java_sp, + Register last_java_fp, + address last_java_pc) { + // determine java_thread register + if (!java_thread->is_valid()) { + java_thread = rdi; + get_thread(java_thread); + } + // determine last_java_sp register + if (!last_java_sp->is_valid()) { + last_java_sp = rsp; + } + + // last_java_fp is optional + + if (last_java_fp->is_valid()) { + movl(Address(java_thread, JavaThread::last_Java_fp_offset()), last_java_fp); + } + + // last_java_pc is optional + + if (last_java_pc != NULL) { + lea(Address(java_thread, + JavaThread::frame_anchor_offset() + JavaFrameAnchor::last_Java_pc_offset()), + InternalAddress(last_java_pc)); + + } + movl(Address(java_thread, JavaThread::last_Java_sp_offset()), last_java_sp); +} + +void MacroAssembler::reset_last_Java_frame(Register java_thread, bool clear_fp, bool clear_pc) { + // determine java_thread register + if (!java_thread->is_valid()) { + java_thread = rdi; + get_thread(java_thread); + } + // we must set sp to zero to clear frame + movl(Address(java_thread, JavaThread::last_Java_sp_offset()), 0); + if (clear_fp) { + movl(Address(java_thread, JavaThread::last_Java_fp_offset()), 0); + } + + if (clear_pc) + movl(Address(java_thread, JavaThread::last_Java_pc_offset()), 0); + +} + + + +// Implementation of call_VM versions + +void MacroAssembler::call_VM_leaf_base( + address entry_point, + int number_of_arguments +) { + call(RuntimeAddress(entry_point)); + increment(rsp, number_of_arguments * wordSize); +} + + +void MacroAssembler::call_VM_base( + Register oop_result, + Register java_thread, + Register last_java_sp, + address entry_point, + int number_of_arguments, + bool check_exceptions +) { + // determine java_thread register + if (!java_thread->is_valid()) { + java_thread = rdi; + get_thread(java_thread); + } + // determine last_java_sp register + if (!last_java_sp->is_valid()) { + last_java_sp = rsp; + } + // debugging support + assert(number_of_arguments >= 0 , "cannot have negative number of arguments"); + assert(java_thread != oop_result , "cannot use the same register for java_thread & oop_result"); + assert(java_thread != last_java_sp, "cannot use the same register for java_thread & last_java_sp"); + // push java thread (becomes first argument of C function) + pushl(java_thread); + // set last Java frame before call + assert(last_java_sp != rbp, "this code doesn't work for last_java_sp == rbp, which currently can't portably work anyway since C2 doesn't save rbp,"); + // Only interpreter should have to set fp + set_last_Java_frame(java_thread, last_java_sp, rbp, NULL); + // do the call + call(RuntimeAddress(entry_point)); + // restore the thread (cannot use the pushed argument since arguments + // may be overwritten by C code generated by an optimizing compiler); + // however can use the register value directly if it is callee saved. + if (java_thread == rdi || java_thread == rsi) { + // rdi & rsi are callee saved -> nothing to do +#ifdef ASSERT + guarantee(java_thread != rax, "change this code"); + pushl(rax); + { Label L; + get_thread(rax); + cmpl(java_thread, rax); + jcc(Assembler::equal, L); + stop("MacroAssembler::call_VM_base: rdi not callee saved?"); + bind(L); + } + popl(rax); +#endif + } else { + get_thread(java_thread); + } + // reset last Java frame + // Only interpreter should have to clear fp + reset_last_Java_frame(java_thread, true, false); + // discard thread and arguments + addl(rsp, (1 + number_of_arguments)*wordSize); + +#ifndef CC_INTERP + // C++ interp handles this in the interpreter + check_and_handle_popframe(java_thread); + check_and_handle_earlyret(java_thread); +#endif /* CC_INTERP */ + + if (check_exceptions) { + // check for pending exceptions (java_thread is set upon return) + cmpl(Address(java_thread, Thread::pending_exception_offset()), NULL_WORD); + jump_cc(Assembler::notEqual, + RuntimeAddress(StubRoutines::forward_exception_entry())); + } + + // get oop result if there is one and reset the value in the thread + if (oop_result->is_valid()) { + movl(oop_result, Address(java_thread, JavaThread::vm_result_offset())); + movl(Address(java_thread, JavaThread::vm_result_offset()), NULL_WORD); + verify_oop(oop_result); + } +} + + +void MacroAssembler::check_and_handle_popframe(Register java_thread) { +} + +void MacroAssembler::check_and_handle_earlyret(Register java_thread) { +} + +void MacroAssembler::call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions) { + leal(rax, Address(rsp, (1 + number_of_arguments) * wordSize)); + call_VM_base(oop_result, noreg, rax, entry_point, number_of_arguments, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions) { + Label C, E; + call(C, relocInfo::none); + jmp(E); + + bind(C); + call_VM_helper(oop_result, entry_point, 0, check_exceptions); + ret(0); + + bind(E); +} + + +void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions) { + Label C, E; + call(C, relocInfo::none); + jmp(E); + + bind(C); + pushl(arg_1); + call_VM_helper(oop_result, entry_point, 1, check_exceptions); + ret(0); + + bind(E); +} + + +void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions) { + Label C, E; + call(C, relocInfo::none); + jmp(E); + + bind(C); + pushl(arg_2); + pushl(arg_1); + call_VM_helper(oop_result, entry_point, 2, check_exceptions); + ret(0); + + bind(E); +} + + +void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions) { + Label C, E; + call(C, relocInfo::none); + jmp(E); + + bind(C); + pushl(arg_3); + pushl(arg_2); + pushl(arg_1); + call_VM_helper(oop_result, entry_point, 3, check_exceptions); + ret(0); + + bind(E); +} + + +void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, int number_of_arguments, bool check_exceptions) { + call_VM_base(oop_result, noreg, last_java_sp, entry_point, number_of_arguments, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, bool check_exceptions) { + pushl(arg_1); + call_VM(oop_result, last_java_sp, entry_point, 1, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, bool check_exceptions) { + pushl(arg_2); + pushl(arg_1); + call_VM(oop_result, last_java_sp, entry_point, 2, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions) { + pushl(arg_3); + pushl(arg_2); + pushl(arg_1); + call_VM(oop_result, last_java_sp, entry_point, 3, check_exceptions); +} + + +void MacroAssembler::call_VM_leaf(address entry_point, int number_of_arguments) { + call_VM_leaf_base(entry_point, number_of_arguments); +} + + +void MacroAssembler::call_VM_leaf(address entry_point, Register arg_1) { + pushl(arg_1); + call_VM_leaf(entry_point, 1); +} + + +void MacroAssembler::call_VM_leaf(address entry_point, Register arg_1, Register arg_2) { + pushl(arg_2); + pushl(arg_1); + call_VM_leaf(entry_point, 2); +} + + +void MacroAssembler::call_VM_leaf(address entry_point, Register arg_1, Register arg_2, Register arg_3) { + pushl(arg_3); + pushl(arg_2); + pushl(arg_1); + call_VM_leaf(entry_point, 3); +} + + +// Calls to C land +// +// When entering C land, the rbp, & rsp of the last Java frame have to be recorded +// in the (thread-local) JavaThread object. When leaving C land, the last Java fp +// has to be reset to 0. This is required to allow proper stack traversal. + +void MacroAssembler::store_check(Register obj) { + // Does a store check for the oop in register obj. The content of + // register obj is destroyed afterwards. + store_check_part_1(obj); + store_check_part_2(obj); +} + + +void MacroAssembler::store_check(Register obj, Address dst) { + store_check(obj); +} + + +// split the store check operation so that other instructions can be scheduled inbetween +void MacroAssembler::store_check_part_1(Register obj) { + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); + shrl(obj, CardTableModRefBS::card_shift); +} + + +void MacroAssembler::store_check_part_2(Register obj) { + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); + CardTableModRefBS* ct = (CardTableModRefBS*)bs; + assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); + ExternalAddress cardtable((address)ct->byte_map_base); + Address index(noreg, obj, Address::times_1); + + movb(as_Address(ArrayAddress(cardtable, index)), 0); +} + + +void MacroAssembler::c2bool(Register x) { + // implements x == 0 ? 0 : 1 + // note: must only look at least-significant byte of x + // since C-style booleans are stored in one byte + // only! (was bug) + andl(x, 0xFF); + setb(Assembler::notZero, x); +} + + +int MacroAssembler::corrected_idivl(Register reg) { + // Full implementation of Java idiv and irem; checks for + // special case as described in JVM spec., p.243 & p.271. + // The function returns the (pc) offset of the idivl + // instruction - may be needed for implicit exceptions. + // + // normal case special case + // + // input : rax,: dividend min_int + // reg: divisor (may not be rax,/rdx) -1 + // + // output: rax,: quotient (= rax, idiv reg) min_int + // rdx: remainder (= rax, irem reg) 0 + assert(reg != rax && reg != rdx, "reg cannot be rax, or rdx register"); + const int min_int = 0x80000000; + Label normal_case, special_case; + + // check for special case + cmpl(rax, min_int); + jcc(Assembler::notEqual, normal_case); + xorl(rdx, rdx); // prepare rdx for possible special case (where remainder = 0) + cmpl(reg, -1); + jcc(Assembler::equal, special_case); + + // handle normal case + bind(normal_case); + cdql(); + int idivl_offset = offset(); + idivl(reg); + + // normal and special case exit + bind(special_case); + + return idivl_offset; +} + + +void MacroAssembler::lneg(Register hi, Register lo) { + negl(lo); + adcl(hi, 0); + negl(hi); +} + + +void MacroAssembler::lmul(int x_rsp_offset, int y_rsp_offset) { + // Multiplication of two Java long values stored on the stack + // as illustrated below. Result is in rdx:rax. + // + // rsp ---> [ ?? ] \ \ + // .... | y_rsp_offset | + // [ y_lo ] / (in bytes) | x_rsp_offset + // [ y_hi ] | (in bytes) + // .... | + // [ x_lo ] / + // [ x_hi ] + // .... + // + // Basic idea: lo(result) = lo(x_lo * y_lo) + // hi(result) = hi(x_lo * y_lo) + lo(x_hi * y_lo) + lo(x_lo * y_hi) + Address x_hi(rsp, x_rsp_offset + wordSize); Address x_lo(rsp, x_rsp_offset); + Address y_hi(rsp, y_rsp_offset + wordSize); Address y_lo(rsp, y_rsp_offset); + Label quick; + // load x_hi, y_hi and check if quick + // multiplication is possible + movl(rbx, x_hi); + movl(rcx, y_hi); + movl(rax, rbx); + orl(rbx, rcx); // rbx, = 0 <=> x_hi = 0 and y_hi = 0 + jcc(Assembler::zero, quick); // if rbx, = 0 do quick multiply + // do full multiplication + // 1st step + mull(y_lo); // x_hi * y_lo + movl(rbx, rax); // save lo(x_hi * y_lo) in rbx, + // 2nd step + movl(rax, x_lo); + mull(rcx); // x_lo * y_hi + addl(rbx, rax); // add lo(x_lo * y_hi) to rbx, + // 3rd step + bind(quick); // note: rbx, = 0 if quick multiply! + movl(rax, x_lo); + mull(y_lo); // x_lo * y_lo + addl(rdx, rbx); // correct hi(x_lo * y_lo) +} + + +void MacroAssembler::lshl(Register hi, Register lo) { + // Java shift left long support (semantics as described in JVM spec., p.305) + // (basic idea for shift counts s >= n: x << s == (x << n) << (s - n)) + // shift value is in rcx ! + assert(hi != rcx, "must not use rcx"); + assert(lo != rcx, "must not use rcx"); + const Register s = rcx; // shift count + const int n = BitsPerWord; + Label L; + andl(s, 0x3f); // s := s & 0x3f (s < 0x40) + cmpl(s, n); // if (s < n) + jcc(Assembler::less, L); // else (s >= n) + movl(hi, lo); // x := x << n + xorl(lo, lo); + // Note: subl(s, n) is not needed since the Intel shift instructions work rcx mod n! + bind(L); // s (mod n) < n + shldl(hi, lo); // x := x << s + shll(lo); +} + + +void MacroAssembler::lshr(Register hi, Register lo, bool sign_extension) { + // Java shift right long support (semantics as described in JVM spec., p.306 & p.310) + // (basic idea for shift counts s >= n: x >> s == (x >> n) >> (s - n)) + assert(hi != rcx, "must not use rcx"); + assert(lo != rcx, "must not use rcx"); + const Register s = rcx; // shift count + const int n = BitsPerWord; + Label L; + andl(s, 0x3f); // s := s & 0x3f (s < 0x40) + cmpl(s, n); // if (s < n) + jcc(Assembler::less, L); // else (s >= n) + movl(lo, hi); // x := x >> n + if (sign_extension) sarl(hi, 31); + else xorl(hi, hi); + // Note: subl(s, n) is not needed since the Intel shift instructions work rcx mod n! + bind(L); // s (mod n) < n + shrdl(lo, hi); // x := x >> s + if (sign_extension) sarl(hi); + else shrl(hi); +} + + +// Note: y_lo will be destroyed +void MacroAssembler::lcmp2int(Register x_hi, Register x_lo, Register y_hi, Register y_lo) { + // Long compare for Java (semantics as described in JVM spec.) + Label high, low, done; + + cmpl(x_hi, y_hi); + jcc(Assembler::less, low); + jcc(Assembler::greater, high); + // x_hi is the return register + xorl(x_hi, x_hi); + cmpl(x_lo, y_lo); + jcc(Assembler::below, low); + jcc(Assembler::equal, done); + + bind(high); + xorl(x_hi, x_hi); + increment(x_hi); + jmp(done); + + bind(low); + xorl(x_hi, x_hi); + decrement(x_hi); + + bind(done); +} + + +void MacroAssembler::save_rax(Register tmp) { + if (tmp == noreg) pushl(rax); + else if (tmp != rax) movl(tmp, rax); +} + + +void MacroAssembler::restore_rax(Register tmp) { + if (tmp == noreg) popl(rax); + else if (tmp != rax) movl(rax, tmp); +} + + +void MacroAssembler::fremr(Register tmp) { + save_rax(tmp); + { Label L; + bind(L); + fprem(); + fwait(); fnstsw_ax(); + sahf(); + jcc(Assembler::parity, L); + } + restore_rax(tmp); + // Result is in ST0. + // Note: fxch & fpop to get rid of ST1 + // (otherwise FPU stack could overflow eventually) + fxch(1); + fpop(); +} + + +static const double pi_4 = 0.7853981633974483; + +void MacroAssembler::trigfunc(char trig, int num_fpu_regs_in_use) { + // A hand-coded argument reduction for values in fabs(pi/4, pi/2) + // was attempted in this code; unfortunately it appears that the + // switch to 80-bit precision and back causes this to be + // unprofitable compared with simply performing a runtime call if + // the argument is out of the (-pi/4, pi/4) range. + + Register tmp = noreg; + if (!VM_Version::supports_cmov()) { + // fcmp needs a temporary so preserve rbx, + tmp = rbx; + pushl(tmp); + } + + Label slow_case, done; + + // x ?<= pi/4 + fld_d(ExternalAddress((address)&pi_4)); + fld_s(1); // Stack: X PI/4 X + fabs(); // Stack: |X| PI/4 X + fcmp(tmp); + jcc(Assembler::above, slow_case); + + // fastest case: -pi/4 <= x <= pi/4 + switch(trig) { + case 's': + fsin(); + break; + case 'c': + fcos(); + break; + case 't': + ftan(); + break; + default: + assert(false, "bad intrinsic"); + break; + } + jmp(done); + + // slow case: runtime call + bind(slow_case); + // Preserve registers across runtime call + pushad(); + int incoming_argument_and_return_value_offset = -1; + if (num_fpu_regs_in_use > 1) { + // Must preserve all other FPU regs (could alternatively convert + // SharedRuntime::dsin and dcos into assembly routines known not to trash + // FPU state, but can not trust C compiler) + NEEDS_CLEANUP; + // NOTE that in this case we also push the incoming argument to + // the stack and restore it later; we also use this stack slot to + // hold the return value from dsin or dcos. + for (int i = 0; i < num_fpu_regs_in_use; i++) { + subl(rsp, wordSize*2); + fstp_d(Address(rsp, 0)); + } + incoming_argument_and_return_value_offset = 2*wordSize*(num_fpu_regs_in_use-1); + fld_d(Address(rsp, incoming_argument_and_return_value_offset)); + } + subl(rsp, wordSize*2); + fstp_d(Address(rsp, 0)); + // NOTE: we must not use call_VM_leaf here because that requires a + // complete interpreter frame in debug mode -- same bug as 4387334 + NEEDS_CLEANUP; + // Need to add stack banging before this runtime call if it needs to + // be taken; however, there is no generic stack banging routine at + // the MacroAssembler level + switch(trig) { + case 's': + { + call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::dsin))); + } + break; + case 'c': + { + call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::dcos))); + } + break; + case 't': + { + call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::dtan))); + } + break; + default: + assert(false, "bad intrinsic"); + break; + } + addl(rsp, wordSize * 2); + if (num_fpu_regs_in_use > 1) { + // Must save return value to stack and then restore entire FPU stack + fstp_d(Address(rsp, incoming_argument_and_return_value_offset)); + for (int i = 0; i < num_fpu_regs_in_use; i++) { + fld_d(Address(rsp, 0)); + addl(rsp, wordSize*2); + } + } + popad(); + + // Come here with result in F-TOS + bind(done); + + if (tmp != noreg) { + popl(tmp); + } +} + +void MacroAssembler::jC2(Register tmp, Label& L) { + // set parity bit if FPU flag C2 is set (via rax) + save_rax(tmp); + fwait(); fnstsw_ax(); + sahf(); + restore_rax(tmp); + // branch + jcc(Assembler::parity, L); +} + + +void MacroAssembler::jnC2(Register tmp, Label& L) { + // set parity bit if FPU flag C2 is set (via rax) + save_rax(tmp); + fwait(); fnstsw_ax(); + sahf(); + restore_rax(tmp); + // branch + jcc(Assembler::noParity, L); +} + + +void MacroAssembler::fcmp(Register tmp) { + fcmp(tmp, 1, true, true); +} + + +void MacroAssembler::fcmp(Register tmp, int index, bool pop_left, bool pop_right) { + assert(!pop_right || pop_left, "usage error"); + if (VM_Version::supports_cmov()) { + assert(tmp == noreg, "unneeded temp"); + if (pop_left) { + fucomip(index); + } else { + fucomi(index); + } + if (pop_right) { + fpop(); + } + } else { + assert(tmp != noreg, "need temp"); + if (pop_left) { + if (pop_right) { + fcompp(); + } else { + fcomp(index); + } + } else { + fcom(index); + } + // convert FPU condition into eflags condition via rax, + save_rax(tmp); + fwait(); fnstsw_ax(); + sahf(); + restore_rax(tmp); + } + // condition codes set as follows: + // + // CF (corresponds to C0) if x < y + // PF (corresponds to C2) if unordered + // ZF (corresponds to C3) if x = y +} + + +void MacroAssembler::fcmp2int(Register dst, bool unordered_is_less) { + fcmp2int(dst, unordered_is_less, 1, true, true); +} + + +void MacroAssembler::fcmp2int(Register dst, bool unordered_is_less, int index, bool pop_left, bool pop_right) { + fcmp(VM_Version::supports_cmov() ? noreg : dst, index, pop_left, pop_right); + Label L; + if (unordered_is_less) { + movl(dst, -1); + jcc(Assembler::parity, L); + jcc(Assembler::below , L); + movl(dst, 0); + jcc(Assembler::equal , L); + increment(dst); + } else { // unordered is greater + movl(dst, 1); + jcc(Assembler::parity, L); + jcc(Assembler::above , L); + movl(dst, 0); + jcc(Assembler::equal , L); + decrement(dst); + } + bind(L); +} + +void MacroAssembler::cmpss2int(XMMRegister opr1, XMMRegister opr2, Register dst, bool unordered_is_less) { + ucomiss(opr1, opr2); + + Label L; + if (unordered_is_less) { + movl(dst, -1); + jcc(Assembler::parity, L); + jcc(Assembler::below , L); + movl(dst, 0); + jcc(Assembler::equal , L); + increment(dst); + } else { // unordered is greater + movl(dst, 1); + jcc(Assembler::parity, L); + jcc(Assembler::above , L); + movl(dst, 0); + jcc(Assembler::equal , L); + decrement(dst); + } + bind(L); +} + +void MacroAssembler::cmpsd2int(XMMRegister opr1, XMMRegister opr2, Register dst, bool unordered_is_less) { + ucomisd(opr1, opr2); + + Label L; + if (unordered_is_less) { + movl(dst, -1); + jcc(Assembler::parity, L); + jcc(Assembler::below , L); + movl(dst, 0); + jcc(Assembler::equal , L); + increment(dst); + } else { // unordered is greater + movl(dst, 1); + jcc(Assembler::parity, L); + jcc(Assembler::above , L); + movl(dst, 0); + jcc(Assembler::equal , L); + decrement(dst); + } + bind(L); +} + + + +void MacroAssembler::fpop() { + ffree(); + fincstp(); +} + + +void MacroAssembler::sign_extend_short(Register reg) { + if (VM_Version::is_P6()) { + movsxw(reg, reg); + } else { + shll(reg, 16); + sarl(reg, 16); + } +} + + +void MacroAssembler::sign_extend_byte(Register reg) { + if (VM_Version::is_P6() && reg->has_byte_register()) { + movsxb(reg, reg); + } else { + shll(reg, 24); + sarl(reg, 24); + } +} + + +void MacroAssembler::division_with_shift (Register reg, int shift_value) { + assert (shift_value > 0, "illegal shift value"); + Label _is_positive; + testl (reg, reg); + jcc (Assembler::positive, _is_positive); + int offset = (1 << shift_value) - 1 ; + + increment(reg, offset); + + bind (_is_positive); + sarl(reg, shift_value); +} + + +void MacroAssembler::round_to(Register reg, int modulus) { + addl(reg, modulus - 1); + andl(reg, -modulus); +} + +// C++ bool manipulation + +void MacroAssembler::movbool(Register dst, Address src) { + if(sizeof(bool) == 1) + movb(dst, src); + else if(sizeof(bool) == 2) + movw(dst, src); + else if(sizeof(bool) == 4) + movl(dst, src); + else + // unsupported + ShouldNotReachHere(); +} + +void MacroAssembler::movbool(Address dst, bool boolconst) { + if(sizeof(bool) == 1) + movb(dst, (int) boolconst); + else if(sizeof(bool) == 2) + movw(dst, (int) boolconst); + else if(sizeof(bool) == 4) + movl(dst, (int) boolconst); + else + // unsupported + ShouldNotReachHere(); +} + +void MacroAssembler::movbool(Address dst, Register src) { + if(sizeof(bool) == 1) + movb(dst, src); + else if(sizeof(bool) == 2) + movw(dst, src); + else if(sizeof(bool) == 4) + movl(dst, src); + else + // unsupported + ShouldNotReachHere(); +} + +void MacroAssembler::testbool(Register dst) { + if(sizeof(bool) == 1) + testb(dst, (int) 0xff); + else if(sizeof(bool) == 2) { + // testw implementation needed for two byte bools + ShouldNotReachHere(); + } else if(sizeof(bool) == 4) + testl(dst, dst); + else + // unsupported + ShouldNotReachHere(); +} + +void MacroAssembler::verify_oop(Register reg, const char* s) { + if (!VerifyOops) return; + // Pass register number to verify_oop_subroutine + char* b = new char[strlen(s) + 50]; + sprintf(b, "verify_oop: %s: %s", reg->name(), s); + pushl(rax); // save rax, + pushl(reg); // pass register argument + ExternalAddress buffer((address) b); + pushptr(buffer.addr()); + // call indirectly to solve generation ordering problem + movptr(rax, ExternalAddress(StubRoutines::verify_oop_subroutine_entry_address())); + call(rax); +} + + +void MacroAssembler::verify_oop_addr(Address addr, const char* s) { + if (!VerifyOops) return; + // QQQ fix this + // Address adjust(addr.base(), addr.index(), addr.scale(), addr.disp() + BytesPerWord); + // Pass register number to verify_oop_subroutine + char* b = new char[strlen(s) + 50]; + sprintf(b, "verify_oop_addr: %s", s); + pushl(rax); // save rax, + // addr may contain rsp so we will have to adjust it based on the push + // we just did + if (addr.uses(rsp)) { + leal(rax, addr); + pushl(Address(rax, BytesPerWord)); + } else { + pushl(addr); + } + ExternalAddress buffer((address) b); + // pass msg argument + pushptr(buffer.addr()); + // call indirectly to solve generation ordering problem + movptr(rax, ExternalAddress(StubRoutines::verify_oop_subroutine_entry_address())); + call(rax); + // Caller pops the arguments and restores rax, from the stack +} + + +void MacroAssembler::stop(const char* msg) { + ExternalAddress message((address)msg); + // push address of message + pushptr(message.addr()); + { Label L; call(L, relocInfo::none); bind(L); } // push eip + pushad(); // push registers + call(RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::debug))); + hlt(); +} + + +void MacroAssembler::warn(const char* msg) { + push_CPU_state(); + + ExternalAddress message((address) msg); + // push address of message + pushptr(message.addr()); + + call(RuntimeAddress(CAST_FROM_FN_PTR(address, warning))); + addl(rsp, wordSize); // discard argument + pop_CPU_state(); +} + + +void MacroAssembler::debug(int rdi, int rsi, int rbp, int rsp, int rbx, int rdx, int rcx, int rax, int eip, char* msg) { + // In order to get locks to work, we need to fake a in_VM state + JavaThread* thread = JavaThread::current(); + JavaThreadState saved_state = thread->thread_state(); + thread->set_thread_state(_thread_in_vm); + if (ShowMessageBoxOnError) { + JavaThread* thread = JavaThread::current(); + JavaThreadState saved_state = thread->thread_state(); + thread->set_thread_state(_thread_in_vm); + ttyLocker ttyl; + if (CountBytecodes || TraceBytecodes || StopInterpreterAt) { + BytecodeCounter::print(); + } + // To see where a verify_oop failed, get $ebx+40/X for this frame. + // This is the value of eip which points to where verify_oop will return. + if (os::message_box(msg, "Execution stopped, print registers?")) { + tty->print_cr("eip = 0x%08x", eip); + tty->print_cr("rax, = 0x%08x", rax); + tty->print_cr("rbx, = 0x%08x", rbx); + tty->print_cr("rcx = 0x%08x", rcx); + tty->print_cr("rdx = 0x%08x", rdx); + tty->print_cr("rdi = 0x%08x", rdi); + tty->print_cr("rsi = 0x%08x", rsi); + tty->print_cr("rbp, = 0x%08x", rbp); + tty->print_cr("rsp = 0x%08x", rsp); + BREAKPOINT; + } + } else { + ::tty->print_cr("=============== DEBUG MESSAGE: %s ================\n", msg); + assert(false, "DEBUG MESSAGE"); + } + ThreadStateTransition::transition(thread, _thread_in_vm, saved_state); +} + + + +void MacroAssembler::os_breakpoint() { + // instead of directly emitting a breakpoint, call os:breakpoint for better debugability + // (e.g., MSVC can't call ps() otherwise) + call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); +} + + +void MacroAssembler::push_fTOS() { + subl(rsp, 2 * wordSize); + fstp_d(Address(rsp, 0)); +} + + +void MacroAssembler::pop_fTOS() { + fld_d(Address(rsp, 0)); + addl(rsp, 2 * wordSize); +} + + +void MacroAssembler::empty_FPU_stack() { + if (VM_Version::supports_mmx()) { + emms(); + } else { + for (int i = 8; i-- > 0; ) ffree(i); + } +} + + +class ControlWord { + public: + int32_t _value; + + int rounding_control() const { return (_value >> 10) & 3 ; } + int precision_control() const { return (_value >> 8) & 3 ; } + bool precision() const { return ((_value >> 5) & 1) != 0; } + bool underflow() const { return ((_value >> 4) & 1) != 0; } + bool overflow() const { return ((_value >> 3) & 1) != 0; } + bool zero_divide() const { return ((_value >> 2) & 1) != 0; } + bool denormalized() const { return ((_value >> 1) & 1) != 0; } + bool invalid() const { return ((_value >> 0) & 1) != 0; } + + void print() const { + // rounding control + const char* rc; + switch (rounding_control()) { + case 0: rc = "round near"; break; + case 1: rc = "round down"; break; + case 2: rc = "round up "; break; + case 3: rc = "chop "; break; + }; + // precision control + const char* pc; + switch (precision_control()) { + case 0: pc = "24 bits "; break; + case 1: pc = "reserved"; break; + case 2: pc = "53 bits "; break; + case 3: pc = "64 bits "; break; + }; + // flags + char f[9]; + f[0] = ' '; + f[1] = ' '; + f[2] = (precision ()) ? 'P' : 'p'; + f[3] = (underflow ()) ? 'U' : 'u'; + f[4] = (overflow ()) ? 'O' : 'o'; + f[5] = (zero_divide ()) ? 'Z' : 'z'; + f[6] = (denormalized()) ? 'D' : 'd'; + f[7] = (invalid ()) ? 'I' : 'i'; + f[8] = '\x0'; + // output + printf("%04x masks = %s, %s, %s", _value & 0xFFFF, f, rc, pc); + } + +}; + + +class StatusWord { + public: + int32_t _value; + + bool busy() const { return ((_value >> 15) & 1) != 0; } + bool C3() const { return ((_value >> 14) & 1) != 0; } + bool C2() const { return ((_value >> 10) & 1) != 0; } + bool C1() const { return ((_value >> 9) & 1) != 0; } + bool C0() const { return ((_value >> 8) & 1) != 0; } + int top() const { return (_value >> 11) & 7 ; } + bool error_status() const { return ((_value >> 7) & 1) != 0; } + bool stack_fault() const { return ((_value >> 6) & 1) != 0; } + bool precision() const { return ((_value >> 5) & 1) != 0; } + bool underflow() const { return ((_value >> 4) & 1) != 0; } + bool overflow() const { return ((_value >> 3) & 1) != 0; } + bool zero_divide() const { return ((_value >> 2) & 1) != 0; } + bool denormalized() const { return ((_value >> 1) & 1) != 0; } + bool invalid() const { return ((_value >> 0) & 1) != 0; } + + void print() const { + // condition codes + char c[5]; + c[0] = (C3()) ? '3' : '-'; + c[1] = (C2()) ? '2' : '-'; + c[2] = (C1()) ? '1' : '-'; + c[3] = (C0()) ? '0' : '-'; + c[4] = '\x0'; + // flags + char f[9]; + f[0] = (error_status()) ? 'E' : '-'; + f[1] = (stack_fault ()) ? 'S' : '-'; + f[2] = (precision ()) ? 'P' : '-'; + f[3] = (underflow ()) ? 'U' : '-'; + f[4] = (overflow ()) ? 'O' : '-'; + f[5] = (zero_divide ()) ? 'Z' : '-'; + f[6] = (denormalized()) ? 'D' : '-'; + f[7] = (invalid ()) ? 'I' : '-'; + f[8] = '\x0'; + // output + printf("%04x flags = %s, cc = %s, top = %d", _value & 0xFFFF, f, c, top()); + } + +}; + + +class TagWord { + public: + int32_t _value; + + int tag_at(int i) const { return (_value >> (i*2)) & 3; } + + void print() const { + printf("%04x", _value & 0xFFFF); + } + +}; + + +class FPU_Register { + public: + int32_t _m0; + int32_t _m1; + int16_t _ex; + + bool is_indefinite() const { + return _ex == -1 && _m1 == (int32_t)0xC0000000 && _m0 == 0; + } + + void print() const { + char sign = (_ex < 0) ? '-' : '+'; + const char* kind = (_ex == 0x7FFF || _ex == (int16_t)-1) ? "NaN" : " "; + printf("%c%04hx.%08x%08x %s", sign, _ex, _m1, _m0, kind); + }; + +}; + + +class FPU_State { + public: + enum { + register_size = 10, + number_of_registers = 8, + register_mask = 7 + }; + + ControlWord _control_word; + StatusWord _status_word; + TagWord _tag_word; + int32_t _error_offset; + int32_t _error_selector; + int32_t _data_offset; + int32_t _data_selector; + int8_t _register[register_size * number_of_registers]; + + int tag_for_st(int i) const { return _tag_word.tag_at((_status_word.top() + i) & register_mask); } + FPU_Register* st(int i) const { return (FPU_Register*)&_register[register_size * i]; } + + const char* tag_as_string(int tag) const { + switch (tag) { + case 0: return "valid"; + case 1: return "zero"; + case 2: return "special"; + case 3: return "empty"; + } + ShouldNotReachHere() + return NULL; + } + + void print() const { + // print computation registers + { int t = _status_word.top(); + for (int i = 0; i < number_of_registers; i++) { + int j = (i - t) & register_mask; + printf("%c r%d = ST%d = ", (j == 0 ? '*' : ' '), i, j); + st(j)->print(); + printf(" %s\n", tag_as_string(_tag_word.tag_at(i))); + } + } + printf("\n"); + // print control registers + printf("ctrl = "); _control_word.print(); printf("\n"); + printf("stat = "); _status_word .print(); printf("\n"); + printf("tags = "); _tag_word .print(); printf("\n"); + } + +}; + + +class Flag_Register { + public: + int32_t _value; + + bool overflow() const { return ((_value >> 11) & 1) != 0; } + bool direction() const { return ((_value >> 10) & 1) != 0; } + bool sign() const { return ((_value >> 7) & 1) != 0; } + bool zero() const { return ((_value >> 6) & 1) != 0; } + bool auxiliary_carry() const { return ((_value >> 4) & 1) != 0; } + bool parity() const { return ((_value >> 2) & 1) != 0; } + bool carry() const { return ((_value >> 0) & 1) != 0; } + + void print() const { + // flags + char f[8]; + f[0] = (overflow ()) ? 'O' : '-'; + f[1] = (direction ()) ? 'D' : '-'; + f[2] = (sign ()) ? 'S' : '-'; + f[3] = (zero ()) ? 'Z' : '-'; + f[4] = (auxiliary_carry()) ? 'A' : '-'; + f[5] = (parity ()) ? 'P' : '-'; + f[6] = (carry ()) ? 'C' : '-'; + f[7] = '\x0'; + // output + printf("%08x flags = %s", _value, f); + } + +}; + + +class IU_Register { + public: + int32_t _value; + + void print() const { + printf("%08x %11d", _value, _value); + } + +}; + + +class IU_State { + public: + Flag_Register _eflags; + IU_Register _rdi; + IU_Register _rsi; + IU_Register _rbp; + IU_Register _rsp; + IU_Register _rbx; + IU_Register _rdx; + IU_Register _rcx; + IU_Register _rax; + + void print() const { + // computation registers + printf("rax, = "); _rax.print(); printf("\n"); + printf("rbx, = "); _rbx.print(); printf("\n"); + printf("rcx = "); _rcx.print(); printf("\n"); + printf("rdx = "); _rdx.print(); printf("\n"); + printf("rdi = "); _rdi.print(); printf("\n"); + printf("rsi = "); _rsi.print(); printf("\n"); + printf("rbp, = "); _rbp.print(); printf("\n"); + printf("rsp = "); _rsp.print(); printf("\n"); + printf("\n"); + // control registers + printf("flgs = "); _eflags.print(); printf("\n"); + } +}; + + +class CPU_State { + public: + FPU_State _fpu_state; + IU_State _iu_state; + + void print() const { + printf("--------------------------------------------------\n"); + _iu_state .print(); + printf("\n"); + _fpu_state.print(); + printf("--------------------------------------------------\n"); + } + +}; + + +static void _print_CPU_state(CPU_State* state) { + state->print(); +}; + + +void MacroAssembler::print_CPU_state() { + push_CPU_state(); + pushl(rsp); // pass CPU state + call(RuntimeAddress(CAST_FROM_FN_PTR(address, _print_CPU_state))); + addl(rsp, wordSize); // discard argument + pop_CPU_state(); +} + + +static bool _verify_FPU(int stack_depth, char* s, CPU_State* state) { + static int counter = 0; + FPU_State* fs = &state->_fpu_state; + counter++; + // For leaf calls, only verify that the top few elements remain empty. + // We only need 1 empty at the top for C2 code. + if( stack_depth < 0 ) { + if( fs->tag_for_st(7) != 3 ) { + printf("FPR7 not empty\n"); + state->print(); + assert(false, "error"); + return false; + } + return true; // All other stack states do not matter + } + + assert((fs->_control_word._value & 0xffff) == StubRoutines::_fpu_cntrl_wrd_std, + "bad FPU control word"); + + // compute stack depth + int i = 0; + while (i < FPU_State::number_of_registers && fs->tag_for_st(i) < 3) i++; + int d = i; + while (i < FPU_State::number_of_registers && fs->tag_for_st(i) == 3) i++; + // verify findings + if (i != FPU_State::number_of_registers) { + // stack not contiguous + printf("%s: stack not contiguous at ST%d\n", s, i); + state->print(); + assert(false, "error"); + return false; + } + // check if computed stack depth corresponds to expected stack depth + if (stack_depth < 0) { + // expected stack depth is -stack_depth or less + if (d > -stack_depth) { + // too many elements on the stack + printf("%s: <= %d stack elements expected but found %d\n", s, -stack_depth, d); + state->print(); + assert(false, "error"); + return false; + } + } else { + // expected stack depth is stack_depth + if (d != stack_depth) { + // wrong stack depth + printf("%s: %d stack elements expected but found %d\n", s, stack_depth, d); + state->print(); + assert(false, "error"); + return false; + } + } + // everything is cool + return true; +} + + +void MacroAssembler::verify_FPU(int stack_depth, const char* s) { + if (!VerifyFPU) return; + push_CPU_state(); + pushl(rsp); // pass CPU state + ExternalAddress msg((address) s); + // pass message string s + pushptr(msg.addr()); + pushl(stack_depth); // pass stack depth + call(RuntimeAddress(CAST_FROM_FN_PTR(address, _verify_FPU))); + addl(rsp, 3 * wordSize); // discard arguments + // check for error + { Label L; + testl(rax, rax); + jcc(Assembler::notZero, L); + int3(); // break if error condition + bind(L); + } + pop_CPU_state(); +} + + +void MacroAssembler::push_IU_state() { + pushad(); + pushfd(); +} + + +void MacroAssembler::pop_IU_state() { + popfd(); + popad(); +} + + +void MacroAssembler::push_FPU_state() { + subl(rsp, FPUStateSizeInWords * wordSize); + fnsave(Address(rsp, 0)); + fwait(); +} + + +void MacroAssembler::pop_FPU_state() { + frstor(Address(rsp, 0)); + addl(rsp, FPUStateSizeInWords * wordSize); +} + + +void MacroAssembler::push_CPU_state() { + push_IU_state(); + push_FPU_state(); +} + + +void MacroAssembler::pop_CPU_state() { + pop_FPU_state(); + pop_IU_state(); +} + + +void MacroAssembler::push_callee_saved_registers() { + pushl(rsi); + pushl(rdi); + pushl(rdx); + pushl(rcx); +} + + +void MacroAssembler::pop_callee_saved_registers() { + popl(rcx); + popl(rdx); + popl(rdi); + popl(rsi); +} + + +void MacroAssembler::set_word_if_not_zero(Register dst) { + xorl(dst, dst); + set_byte_if_not_zero(dst); +} + +// Write serialization page so VM thread can do a pseudo remote membar. +// We use the current thread pointer to calculate a thread specific +// offset to write to within the page. This minimizes bus traffic +// due to cache line collision. +void MacroAssembler::serialize_memory(Register thread, Register tmp) { + movl(tmp, thread); + shrl(tmp, os::get_serialize_page_shift_count()); + andl(tmp, (os::vm_page_size() - sizeof(int))); + + Address index(noreg, tmp, Address::times_1); + ExternalAddress page(os::get_memory_serialize_page()); + + movptr(ArrayAddress(page, index), tmp); +} + + +void MacroAssembler::verify_tlab() { +#ifdef ASSERT + if (UseTLAB && VerifyOops) { + Label next, ok; + Register t1 = rsi; + Register thread_reg = rbx; + + pushl(t1); + pushl(thread_reg); + get_thread(thread_reg); + + movl(t1, Address(thread_reg, in_bytes(JavaThread::tlab_top_offset()))); + cmpl(t1, Address(thread_reg, in_bytes(JavaThread::tlab_start_offset()))); + jcc(Assembler::aboveEqual, next); + stop("assert(top >= start)"); + should_not_reach_here(); + + bind(next); + movl(t1, Address(thread_reg, in_bytes(JavaThread::tlab_end_offset()))); + cmpl(t1, Address(thread_reg, in_bytes(JavaThread::tlab_top_offset()))); + jcc(Assembler::aboveEqual, ok); + stop("assert(top <= end)"); + should_not_reach_here(); + + bind(ok); + popl(thread_reg); + popl(t1); + } +#endif +} + + +// Defines obj, preserves var_size_in_bytes +void MacroAssembler::eden_allocate(Register obj, Register var_size_in_bytes, int con_size_in_bytes, + Register t1, Label& slow_case) { + assert(obj == rax, "obj must be in rax, for cmpxchg"); + assert_different_registers(obj, var_size_in_bytes, t1); + Register end = t1; + Label retry; + bind(retry); + ExternalAddress heap_top((address) Universe::heap()->top_addr()); + movptr(obj, heap_top); + if (var_size_in_bytes == noreg) { + leal(end, Address(obj, con_size_in_bytes)); + } else { + leal(end, Address(obj, var_size_in_bytes, Address::times_1)); + } + // if end < obj then we wrapped around => object too long => slow case + cmpl(end, obj); + jcc(Assembler::below, slow_case); + cmpptr(end, ExternalAddress((address) Universe::heap()->end_addr())); + jcc(Assembler::above, slow_case); + // Compare obj with the top addr, and if still equal, store the new top addr in + // end at the address of the top addr pointer. Sets ZF if was equal, and clears + // it otherwise. Use lock prefix for atomicity on MPs. + if (os::is_MP()) { + lock(); + } + cmpxchgptr(end, heap_top); + jcc(Assembler::notEqual, retry); +} + + +// Defines obj, preserves var_size_in_bytes, okay for t2 == var_size_in_bytes. +void MacroAssembler::tlab_allocate(Register obj, Register var_size_in_bytes, int con_size_in_bytes, + Register t1, Register t2, Label& slow_case) { + assert_different_registers(obj, t1, t2); + assert_different_registers(obj, var_size_in_bytes, t1); + Register end = t2; + Register thread = t1; + + verify_tlab(); + + get_thread(thread); + + movl(obj, Address(thread, JavaThread::tlab_top_offset())); + if (var_size_in_bytes == noreg) { + leal(end, Address(obj, con_size_in_bytes)); + } else { + leal(end, Address(obj, var_size_in_bytes, Address::times_1)); + } + cmpl(end, Address(thread, JavaThread::tlab_end_offset())); + jcc(Assembler::above, slow_case); + + // update the tlab top pointer + movl(Address(thread, JavaThread::tlab_top_offset()), end); + + // recover var_size_in_bytes if necessary + if (var_size_in_bytes == end) { + subl(var_size_in_bytes, obj); + } + verify_tlab(); +} + +// Preserves rbx, and rdx. +void MacroAssembler::tlab_refill(Label& retry, Label& try_eden, Label& slow_case) { + Register top = rax; + Register t1 = rcx; + Register t2 = rsi; + Register thread_reg = rdi; + assert_different_registers(top, thread_reg, t1, t2, /* preserve: */ rbx, rdx); + Label do_refill, discard_tlab; + + if (CMSIncrementalMode || !Universe::heap()->supports_inline_contig_alloc()) { + // No allocation in the shared eden. + jmp(slow_case); + } + + get_thread(thread_reg); + + movl(top, Address(thread_reg, in_bytes(JavaThread::tlab_top_offset()))); + movl(t1, Address(thread_reg, in_bytes(JavaThread::tlab_end_offset()))); + + // calculate amount of free space + subl(t1, top); + shrl(t1, LogHeapWordSize); + + // Retain tlab and allocate object in shared space if + // the amount free in the tlab is too large to discard. + cmpl(t1, Address(thread_reg, in_bytes(JavaThread::tlab_refill_waste_limit_offset()))); + jcc(Assembler::lessEqual, discard_tlab); + + // Retain + movl(t2, ThreadLocalAllocBuffer::refill_waste_limit_increment()); + addl(Address(thread_reg, in_bytes(JavaThread::tlab_refill_waste_limit_offset())), t2); + if (TLABStats) { + // increment number of slow_allocations + addl(Address(thread_reg, in_bytes(JavaThread::tlab_slow_allocations_offset())), 1); + } + jmp(try_eden); + + bind(discard_tlab); + if (TLABStats) { + // increment number of refills + addl(Address(thread_reg, in_bytes(JavaThread::tlab_number_of_refills_offset())), 1); + // accumulate wastage -- t1 is amount free in tlab + addl(Address(thread_reg, in_bytes(JavaThread::tlab_fast_refill_waste_offset())), t1); + } + + // if tlab is currently allocated (top or end != null) then + // fill [top, end + alignment_reserve) with array object + testl (top, top); + jcc(Assembler::zero, do_refill); + + // set up the mark word + movl(Address(top, oopDesc::mark_offset_in_bytes()), (int)markOopDesc::prototype()->copy_set_hash(0x2)); + // set the length to the remaining space + subl(t1, typeArrayOopDesc::header_size(T_INT)); + addl(t1, ThreadLocalAllocBuffer::alignment_reserve()); + shll(t1, log2_intptr(HeapWordSize/sizeof(jint))); + movl(Address(top, arrayOopDesc::length_offset_in_bytes()), t1); + // set klass to intArrayKlass + // dubious reloc why not an oop reloc? + movptr(t1, ExternalAddress((address) Universe::intArrayKlassObj_addr())); + movl(Address(top, oopDesc::klass_offset_in_bytes()), t1); + + // refill the tlab with an eden allocation + bind(do_refill); + movl(t1, Address(thread_reg, in_bytes(JavaThread::tlab_size_offset()))); + shll(t1, LogHeapWordSize); + // add object_size ?? + eden_allocate(top, t1, 0, t2, slow_case); + + // Check that t1 was preserved in eden_allocate. +#ifdef ASSERT + if (UseTLAB) { + Label ok; + Register tsize = rsi; + assert_different_registers(tsize, thread_reg, t1); + pushl(tsize); + movl(tsize, Address(thread_reg, in_bytes(JavaThread::tlab_size_offset()))); + shll(tsize, LogHeapWordSize); + cmpl(t1, tsize); + jcc(Assembler::equal, ok); + stop("assert(t1 != tlab size)"); + should_not_reach_here(); + + bind(ok); + popl(tsize); + } +#endif + movl(Address(thread_reg, in_bytes(JavaThread::tlab_start_offset())), top); + movl(Address(thread_reg, in_bytes(JavaThread::tlab_top_offset())), top); + addl(top, t1); + subl(top, ThreadLocalAllocBuffer::alignment_reserve_in_bytes()); + movl(Address(thread_reg, in_bytes(JavaThread::tlab_end_offset())), top); + verify_tlab(); + jmp(retry); +} + + +int MacroAssembler::biased_locking_enter(Register lock_reg, Register obj_reg, Register swap_reg, Register tmp_reg, + bool swap_reg_contains_mark, + Label& done, Label* slow_case, + BiasedLockingCounters* counters) { + assert(UseBiasedLocking, "why call this otherwise?"); + assert(swap_reg == rax, "swap_reg must be rax, for cmpxchg"); + assert_different_registers(lock_reg, obj_reg, swap_reg); + + if (PrintBiasedLockingStatistics && counters == NULL) + counters = BiasedLocking::counters(); + + bool need_tmp_reg = false; + if (tmp_reg == noreg) { + need_tmp_reg = true; + tmp_reg = lock_reg; + } else { + assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg); + } + assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, "biased locking makes assumptions about bit layout"); + Address mark_addr (obj_reg, oopDesc::mark_offset_in_bytes()); + Address klass_addr (obj_reg, oopDesc::klass_offset_in_bytes()); + Address saved_mark_addr(lock_reg, 0); + + // Biased locking + // See whether the lock is currently biased toward our thread and + // whether the epoch is still valid + // Note that the runtime guarantees sufficient alignment of JavaThread + // pointers to allow age to be placed into low bits + // First check to see whether biasing is even enabled for this object + Label cas_label; + int null_check_offset = -1; + if (!swap_reg_contains_mark) { + null_check_offset = offset(); + movl(swap_reg, mark_addr); + } + if (need_tmp_reg) { + pushl(tmp_reg); + } + movl(tmp_reg, swap_reg); + andl(tmp_reg, markOopDesc::biased_lock_mask_in_place); + cmpl(tmp_reg, markOopDesc::biased_lock_pattern); + if (need_tmp_reg) { + popl(tmp_reg); + } + jcc(Assembler::notEqual, cas_label); + // The bias pattern is present in the object's header. Need to check + // whether the bias owner and the epoch are both still current. + // Note that because there is no current thread register on x86 we + // need to store off the mark word we read out of the object to + // avoid reloading it and needing to recheck invariants below. This + // store is unfortunate but it makes the overall code shorter and + // simpler. + movl(saved_mark_addr, swap_reg); + if (need_tmp_reg) { + pushl(tmp_reg); + } + get_thread(tmp_reg); + xorl(swap_reg, tmp_reg); + if (swap_reg_contains_mark) { + null_check_offset = offset(); + } + movl(tmp_reg, klass_addr); + xorl(swap_reg, Address(tmp_reg, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes())); + andl(swap_reg, ~((int) markOopDesc::age_mask_in_place)); + if (need_tmp_reg) { + popl(tmp_reg); + } + if (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address)counters->biased_lock_entry_count_addr())); + } + jcc(Assembler::equal, done); + + Label try_revoke_bias; + Label try_rebias; + + // At this point we know that the header has the bias pattern and + // that we are not the bias owner in the current epoch. We need to + // figure out more details about the state of the header in order to + // know what operations can be legally performed on the object's + // header. + + // If the low three bits in the xor result aren't clear, that means + // the prototype header is no longer biased and we have to revoke + // the bias on this object. + testl(swap_reg, markOopDesc::biased_lock_mask_in_place); + jcc(Assembler::notZero, try_revoke_bias); + + // Biasing is still enabled for this data type. See whether the + // epoch of the current bias is still valid, meaning that the epoch + // bits of the mark word are equal to the epoch bits of the + // prototype header. (Note that the prototype header's epoch bits + // only change at a safepoint.) If not, attempt to rebias the object + // toward the current thread. Note that we must be absolutely sure + // that the current epoch is invalid in order to do this because + // otherwise the manipulations it performs on the mark word are + // illegal. + testl(swap_reg, markOopDesc::epoch_mask_in_place); + jcc(Assembler::notZero, try_rebias); + + // The epoch of the current bias is still valid but we know nothing + // about the owner; it might be set or it might be clear. Try to + // acquire the bias of the object using an atomic operation. If this + // fails we will go in to the runtime to revoke the object's bias. + // Note that we first construct the presumed unbiased header so we + // don't accidentally blow away another thread's valid bias. + movl(swap_reg, saved_mark_addr); + andl(swap_reg, + markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place); + if (need_tmp_reg) { + pushl(tmp_reg); + } + get_thread(tmp_reg); + orl(tmp_reg, swap_reg); + if (os::is_MP()) { + lock(); + } + cmpxchg(tmp_reg, Address(obj_reg, 0)); + if (need_tmp_reg) { + popl(tmp_reg); + } + // If the biasing toward our thread failed, this means that + // another thread succeeded in biasing it toward itself and we + // need to revoke that bias. The revocation will occur in the + // interpreter runtime in the slow case. + if (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address)counters->anonymously_biased_lock_entry_count_addr())); + } + if (slow_case != NULL) { + jcc(Assembler::notZero, *slow_case); + } + jmp(done); + + bind(try_rebias); + // At this point we know the epoch has expired, meaning that the + // current "bias owner", if any, is actually invalid. Under these + // circumstances _only_, we are allowed to use the current header's + // value as the comparison value when doing the cas to acquire the + // bias in the current epoch. In other words, we allow transfer of + // the bias from one thread to another directly in this situation. + // + // FIXME: due to a lack of registers we currently blow away the age + // bits in this situation. Should attempt to preserve them. + if (need_tmp_reg) { + pushl(tmp_reg); + } + get_thread(tmp_reg); + movl(swap_reg, klass_addr); + orl(tmp_reg, Address(swap_reg, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes())); + movl(swap_reg, saved_mark_addr); + if (os::is_MP()) { + lock(); + } + cmpxchg(tmp_reg, Address(obj_reg, 0)); + if (need_tmp_reg) { + popl(tmp_reg); + } + // If the biasing toward our thread failed, then another thread + // succeeded in biasing it toward itself and we need to revoke that + // bias. The revocation will occur in the runtime in the slow case. + if (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address)counters->rebiased_lock_entry_count_addr())); + } + if (slow_case != NULL) { + jcc(Assembler::notZero, *slow_case); + } + jmp(done); + + bind(try_revoke_bias); + // The prototype mark in the klass doesn't have the bias bit set any + // more, indicating that objects of this data type are not supposed + // to be biased any more. We are going to try to reset the mark of + // this object to the prototype value and fall through to the + // CAS-based locking scheme. Note that if our CAS fails, it means + // that another thread raced us for the privilege of revoking the + // bias of this particular object, so it's okay to continue in the + // normal locking code. + // + // FIXME: due to a lack of registers we currently blow away the age + // bits in this situation. Should attempt to preserve them. + movl(swap_reg, saved_mark_addr); + if (need_tmp_reg) { + pushl(tmp_reg); + } + movl(tmp_reg, klass_addr); + movl(tmp_reg, Address(tmp_reg, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes())); + if (os::is_MP()) { + lock(); + } + cmpxchg(tmp_reg, Address(obj_reg, 0)); + if (need_tmp_reg) { + popl(tmp_reg); + } + // Fall through to the normal CAS-based lock, because no matter what + // the result of the above CAS, some thread must have succeeded in + // removing the bias bit from the object's header. + if (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address)counters->revoked_lock_entry_count_addr())); + } + + bind(cas_label); + + return null_check_offset; +} + + +void MacroAssembler::biased_locking_exit(Register obj_reg, Register temp_reg, Label& done) { + assert(UseBiasedLocking, "why call this otherwise?"); + + // Check for biased locking unlock case, which is a no-op + // Note: we do not have to check the thread ID for two reasons. + // First, the interpreter checks for IllegalMonitorStateException at + // a higher level. Second, if the bias was revoked while we held the + // lock, the object could not be rebiased toward another thread, so + // the bias bit would be clear. + movl(temp_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); + andl(temp_reg, markOopDesc::biased_lock_mask_in_place); + cmpl(temp_reg, markOopDesc::biased_lock_pattern); + jcc(Assembler::equal, done); +} + + +Assembler::Condition MacroAssembler::negate_condition(Assembler::Condition cond) { + switch (cond) { + // Note some conditions are synonyms for others + case Assembler::zero: return Assembler::notZero; + case Assembler::notZero: return Assembler::zero; + case Assembler::less: return Assembler::greaterEqual; + case Assembler::lessEqual: return Assembler::greater; + case Assembler::greater: return Assembler::lessEqual; + case Assembler::greaterEqual: return Assembler::less; + case Assembler::below: return Assembler::aboveEqual; + case Assembler::belowEqual: return Assembler::above; + case Assembler::above: return Assembler::belowEqual; + case Assembler::aboveEqual: return Assembler::below; + case Assembler::overflow: return Assembler::noOverflow; + case Assembler::noOverflow: return Assembler::overflow; + case Assembler::negative: return Assembler::positive; + case Assembler::positive: return Assembler::negative; + case Assembler::parity: return Assembler::noParity; + case Assembler::noParity: return Assembler::parity; + } + ShouldNotReachHere(); return Assembler::overflow; +} + + +void MacroAssembler::cond_inc32(Condition cond, AddressLiteral counter_addr) { + Condition negated_cond = negate_condition(cond); + Label L; + jcc(negated_cond, L); + atomic_incl(counter_addr); + bind(L); +} + +void MacroAssembler::atomic_incl(AddressLiteral counter_addr) { + pushfd(); + if (os::is_MP()) + lock(); + increment(counter_addr); + popfd(); +} + +SkipIfEqual::SkipIfEqual( + MacroAssembler* masm, const bool* flag_addr, bool value) { + _masm = masm; + _masm->cmp8(ExternalAddress((address)flag_addr), value); + _masm->jcc(Assembler::equal, _label); +} + +SkipIfEqual::~SkipIfEqual() { + _masm->bind(_label); +} + + +// Writes to stack successive pages until offset reached to check for +// stack overflow + shadow pages. This clobbers tmp. +void MacroAssembler::bang_stack_size(Register size, Register tmp) { + movl(tmp, rsp); + // Bang stack for total size given plus shadow page size. + // Bang one page at a time because large size can bang beyond yellow and + // red zones. + Label loop; + bind(loop); + movl(Address(tmp, (-os::vm_page_size())), size ); + subl(tmp, os::vm_page_size()); + subl(size, os::vm_page_size()); + jcc(Assembler::greater, loop); + + // Bang down shadow pages too. + // The -1 because we already subtracted 1 page. + for (int i = 0; i< StackShadowPages-1; i++) { + movl(Address(tmp, (-i*os::vm_page_size())), size ); + } +} diff --git a/hotspot/src/cpu/x86/vm/assembler_x86_32.hpp b/hotspot/src/cpu/x86/vm/assembler_x86_32.hpp new file mode 100644 index 00000000000..0e9336b1487 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/assembler_x86_32.hpp @@ -0,0 +1,1504 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class BiasedLockingCounters; + +// Contains all the definitions needed for x86 assembly code generation. + +// Calling convention +class Argument VALUE_OBJ_CLASS_SPEC { + public: + enum { +#ifdef _LP64 +#ifdef _WIN64 + n_int_register_parameters_c = 4, // rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) + n_float_register_parameters_c = 4, // xmm0 - xmm3 (c_farg0, c_farg1, ... ) +#else + n_int_register_parameters_c = 6, // rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...) + n_float_register_parameters_c = 8, // xmm0 - xmm7 (c_farg0, c_farg1, ... ) +#endif // _WIN64 + n_int_register_parameters_j = 6, // j_rarg0, j_rarg1, ... + n_float_register_parameters_j = 8 // j_farg0, j_farg1, ... +#else + n_register_parameters = 0 // 0 registers used to pass arguments +#endif // _LP64 + }; +}; + + +#ifdef _LP64 +// Symbolically name the register arguments used by the c calling convention. +// Windows is different from linux/solaris. So much for standards... + +#ifdef _WIN64 + +REGISTER_DECLARATION(Register, c_rarg0, rcx); +REGISTER_DECLARATION(Register, c_rarg1, rdx); +REGISTER_DECLARATION(Register, c_rarg2, r8); +REGISTER_DECLARATION(Register, c_rarg3, r9); + +REGISTER_DECLARATION(FloatRegister, c_farg0, xmm0); +REGISTER_DECLARATION(FloatRegister, c_farg1, xmm1); +REGISTER_DECLARATION(FloatRegister, c_farg2, xmm2); +REGISTER_DECLARATION(FloatRegister, c_farg3, xmm3); + +#else + +REGISTER_DECLARATION(Register, c_rarg0, rdi); +REGISTER_DECLARATION(Register, c_rarg1, rsi); +REGISTER_DECLARATION(Register, c_rarg2, rdx); +REGISTER_DECLARATION(Register, c_rarg3, rcx); +REGISTER_DECLARATION(Register, c_rarg4, r8); +REGISTER_DECLARATION(Register, c_rarg5, r9); + +REGISTER_DECLARATION(FloatRegister, c_farg0, xmm0); +REGISTER_DECLARATION(FloatRegister, c_farg1, xmm1); +REGISTER_DECLARATION(FloatRegister, c_farg2, xmm2); +REGISTER_DECLARATION(FloatRegister, c_farg3, xmm3); +REGISTER_DECLARATION(FloatRegister, c_farg4, xmm4); +REGISTER_DECLARATION(FloatRegister, c_farg5, xmm5); +REGISTER_DECLARATION(FloatRegister, c_farg6, xmm6); +REGISTER_DECLARATION(FloatRegister, c_farg7, xmm7); + +#endif // _WIN64 + +// Symbolically name the register arguments used by the Java calling convention. +// We have control over the convention for java so we can do what we please. +// What pleases us is to offset the java calling convention so that when +// we call a suitable jni method the arguments are lined up and we don't +// have to do little shuffling. A suitable jni method is non-static and a +// small number of arguments (two fewer args on windows) +// +// |-------------------------------------------------------| +// | c_rarg0 c_rarg1 c_rarg2 c_rarg3 c_rarg4 c_rarg5 | +// |-------------------------------------------------------| +// | rcx rdx r8 r9 rdi* rsi* | windows (* not a c_rarg) +// | rdi rsi rdx rcx r8 r9 | solaris/linux +// |-------------------------------------------------------| +// | j_rarg5 j_rarg0 j_rarg1 j_rarg2 j_rarg3 j_rarg4 | +// |-------------------------------------------------------| + +REGISTER_DECLARATION(Register, j_rarg0, c_rarg1); +REGISTER_DECLARATION(Register, j_rarg1, c_rarg2); +REGISTER_DECLARATION(Register, j_rarg2, c_rarg3); +// Windows runs out of register args here +#ifdef _WIN64 +REGISTER_DECLARATION(Register, j_rarg3, rdi); +REGISTER_DECLARATION(Register, j_rarg4, rsi); +#else +REGISTER_DECLARATION(Register, j_rarg3, c_rarg4); +REGISTER_DECLARATION(Register, j_rarg4, c_rarg5); +#endif /* _WIN64 */ +REGISTER_DECLARATION(Register, j_rarg5, c_rarg0); + +REGISTER_DECLARATION(FloatRegister, j_farg0, xmm0); +REGISTER_DECLARATION(FloatRegister, j_farg1, xmm1); +REGISTER_DECLARATION(FloatRegister, j_farg2, xmm2); +REGISTER_DECLARATION(FloatRegister, j_farg3, xmm3); +REGISTER_DECLARATION(FloatRegister, j_farg4, xmm4); +REGISTER_DECLARATION(FloatRegister, j_farg5, xmm5); +REGISTER_DECLARATION(FloatRegister, j_farg6, xmm6); +REGISTER_DECLARATION(FloatRegister, j_farg7, xmm7); + +REGISTER_DECLARATION(Register, rscratch1, r10); // volatile +REGISTER_DECLARATION(Register, rscratch2, r11); // volatile + +REGISTER_DECLARATION(Register, r15_thread, r15); // callee-saved + +#endif // _LP64 + +// Address is an abstraction used to represent a memory location +// using any of the amd64 addressing modes with one object. +// +// Note: A register location is represented via a Register, not +// via an address for efficiency & simplicity reasons. + +class ArrayAddress; + +class Address VALUE_OBJ_CLASS_SPEC { + public: + enum ScaleFactor { + no_scale = -1, + times_1 = 0, + times_2 = 1, + times_4 = 2, + times_8 = 3 + }; + + private: + Register _base; + Register _index; + ScaleFactor _scale; + int _disp; + RelocationHolder _rspec; + + // Easily misused constructor make them private +#ifndef _LP64 + Address(address loc, RelocationHolder spec); +#endif // _LP64 + + public: + // creation + Address() + : _base(noreg), + _index(noreg), + _scale(no_scale), + _disp(0) { + } + + // No default displacement otherwise Register can be implicitly + // converted to 0(Register) which is quite a different animal. + + Address(Register base, int disp) + : _base(base), + _index(noreg), + _scale(no_scale), + _disp(disp) { + } + + Address(Register base, Register index, ScaleFactor scale, int disp = 0) + : _base (base), + _index(index), + _scale(scale), + _disp (disp) { + assert(!index->is_valid() == (scale == Address::no_scale), + "inconsistent address"); + } + + // The following two overloads are used in connection with the + // ByteSize type (see sizes.hpp). They simplify the use of + // ByteSize'd arguments in assembly code. Note that their equivalent + // for the optimized build are the member functions with int disp + // argument since ByteSize is mapped to an int type in that case. + // + // Note: DO NOT introduce similar overloaded functions for WordSize + // arguments as in the optimized mode, both ByteSize and WordSize + // are mapped to the same type and thus the compiler cannot make a + // distinction anymore (=> compiler errors). + +#ifdef ASSERT + Address(Register base, ByteSize disp) + : _base(base), + _index(noreg), + _scale(no_scale), + _disp(in_bytes(disp)) { + } + + Address(Register base, Register index, ScaleFactor scale, ByteSize disp) + : _base(base), + _index(index), + _scale(scale), + _disp(in_bytes(disp)) { + assert(!index->is_valid() == (scale == Address::no_scale), + "inconsistent address"); + } +#endif // ASSERT + + // accessors + bool uses(Register reg) const { + return _base == reg || _index == reg; + } + + // Convert the raw encoding form into the form expected by the constructor for + // Address. An index of 4 (rsp) corresponds to having no index, so convert + // that to noreg for the Address constructor. + static Address make_raw(int base, int index, int scale, int disp); + + static Address make_array(ArrayAddress); + + + private: + bool base_needs_rex() const { + return _base != noreg && _base->encoding() >= 8; + } + + bool index_needs_rex() const { + return _index != noreg &&_index->encoding() >= 8; + } + + relocInfo::relocType reloc() const { return _rspec.type(); } + + friend class Assembler; + friend class MacroAssembler; + friend class LIR_Assembler; // base/index/scale/disp +}; + +// +// AddressLiteral has been split out from Address because operands of this type +// need to be treated specially on 32bit vs. 64bit platforms. By splitting it out +// the few instructions that need to deal with address literals are unique and the +// MacroAssembler does not have to implement every instruction in the Assembler +// in order to search for address literals that may need special handling depending +// on the instruction and the platform. As small step on the way to merging i486/amd64 +// directories. +// +class AddressLiteral VALUE_OBJ_CLASS_SPEC { + friend class ArrayAddress; + RelocationHolder _rspec; + // Typically we use AddressLiterals we want to use their rval + // However in some situations we want the lval (effect address) of the item. + // We provide a special factory for making those lvals. + bool _is_lval; + + // If the target is far we'll need to load the ea of this to + // a register to reach it. Otherwise if near we can do rip + // relative addressing. + + address _target; + + protected: + // creation + AddressLiteral() + : _is_lval(false), + _target(NULL) + {} + + public: + + + AddressLiteral(address target, relocInfo::relocType rtype); + + AddressLiteral(address target, RelocationHolder const& rspec) + : _rspec(rspec), + _is_lval(false), + _target(target) + {} + + AddressLiteral addr() { + AddressLiteral ret = *this; + ret._is_lval = true; + return ret; + } + + + private: + + address target() { return _target; } + bool is_lval() { return _is_lval; } + + relocInfo::relocType reloc() const { return _rspec.type(); } + const RelocationHolder& rspec() const { return _rspec; } + + friend class Assembler; + friend class MacroAssembler; + friend class Address; + friend class LIR_Assembler; +}; + +// Convience classes +class RuntimeAddress: public AddressLiteral { + + public: + + RuntimeAddress(address target) : AddressLiteral(target, relocInfo::runtime_call_type) {} + +}; + +class OopAddress: public AddressLiteral { + + public: + + OopAddress(address target) : AddressLiteral(target, relocInfo::oop_type){} + +}; + +class ExternalAddress: public AddressLiteral { + + public: + + ExternalAddress(address target) : AddressLiteral(target, relocInfo::external_word_type){} + +}; + +class InternalAddress: public AddressLiteral { + + public: + + InternalAddress(address target) : AddressLiteral(target, relocInfo::internal_word_type) {} + +}; + +// x86 can do array addressing as a single operation since disp can be an absolute +// address amd64 can't. We create a class that expresses the concept but does extra +// magic on amd64 to get the final result + +class ArrayAddress VALUE_OBJ_CLASS_SPEC { + private: + + AddressLiteral _base; + Address _index; + + public: + + ArrayAddress() {}; + ArrayAddress(AddressLiteral base, Address index): _base(base), _index(index) {}; + AddressLiteral base() { return _base; } + Address index() { return _index; } + +}; + +#ifndef _LP64 +const int FPUStateSizeInWords = 27; +#else +const int FPUStateSizeInWords = 512 / wordSize; +#endif // _LP64 + +// The Intel x86/Amd64 Assembler: Pure assembler doing NO optimizations on the instruction +// level (e.g. mov rax, 0 is not translated into xor rax, rax!); i.e., what you write +// is what you get. The Assembler is generating code into a CodeBuffer. + +class Assembler : public AbstractAssembler { + friend class AbstractAssembler; // for the non-virtual hack + friend class LIR_Assembler; // as_Address() + + protected: + #ifdef ASSERT + void check_relocation(RelocationHolder const& rspec, int format); + #endif + + inline void emit_long64(jlong x); + + void emit_data(jint data, relocInfo::relocType rtype, int format /* = 0 */); + void emit_data(jint data, RelocationHolder const& rspec, int format /* = 0 */); + void emit_data64(jlong data, relocInfo::relocType rtype, int format = 0); + void emit_data64(jlong data, RelocationHolder const& rspec, int format = 0); + + // Helper functions for groups of instructions + void emit_arith_b(int op1, int op2, Register dst, int imm8); + + void emit_arith(int op1, int op2, Register dst, int imm32); + // only x86?? + void emit_arith(int op1, int op2, Register dst, jobject obj); + void emit_arith(int op1, int op2, Register dst, Register src); + + void emit_operand(Register reg, + Register base, Register index, Address::ScaleFactor scale, + int disp, + RelocationHolder const& rspec); + void emit_operand(Register reg, Address adr); + + // Immediate-to-memory forms + void emit_arith_operand(int op1, Register rm, Address adr, int imm32); + + void emit_farith(int b1, int b2, int i); + + // macroassembler?? QQQ + bool reachable(AddressLiteral adr) { return true; } + + // These are all easily abused and hence protected + + // Make these disappear in 64bit mode since they would never be correct +#ifndef _LP64 + void cmp_literal32(Register src1, int32_t imm32, RelocationHolder const& rspec); + void cmp_literal32(Address src1, int32_t imm32, RelocationHolder const& rspec); + + void mov_literal32(Register dst, int32_t imm32, RelocationHolder const& rspec); + void mov_literal32(Address dst, int32_t imm32, RelocationHolder const& rspec); + + void push_literal32(int32_t imm32, RelocationHolder const& rspec); +#endif // _LP64 + + // These are unique in that we are ensured by the caller that the 32bit + // relative in these instructions will always be able to reach the potentially + // 64bit address described by entry. Since they can take a 64bit address they + // don't have the 32 suffix like the other instructions in this class. + + void call_literal(address entry, RelocationHolder const& rspec); + void jmp_literal(address entry, RelocationHolder const& rspec); + + + public: + enum Condition { // The x86 condition codes used for conditional jumps/moves. + zero = 0x4, + notZero = 0x5, + equal = 0x4, + notEqual = 0x5, + less = 0xc, + lessEqual = 0xe, + greater = 0xf, + greaterEqual = 0xd, + below = 0x2, + belowEqual = 0x6, + above = 0x7, + aboveEqual = 0x3, + overflow = 0x0, + noOverflow = 0x1, + carrySet = 0x2, + carryClear = 0x3, + negative = 0x8, + positive = 0x9, + parity = 0xa, + noParity = 0xb + }; + + enum Prefix { + // segment overrides + CS_segment = 0x2e, + SS_segment = 0x36, + DS_segment = 0x3e, + ES_segment = 0x26, + FS_segment = 0x64, + GS_segment = 0x65, + + REX = 0x40, + + REX_B = 0x41, + REX_X = 0x42, + REX_XB = 0x43, + REX_R = 0x44, + REX_RB = 0x45, + REX_RX = 0x46, + REX_RXB = 0x47, + + REX_W = 0x48, + + REX_WB = 0x49, + REX_WX = 0x4A, + REX_WXB = 0x4B, + REX_WR = 0x4C, + REX_WRB = 0x4D, + REX_WRX = 0x4E, + REX_WRXB = 0x4F + }; + + enum WhichOperand { + // input to locate_operand, and format code for relocations + imm32_operand = 0, // embedded 32-bit immediate operand + disp32_operand = 1, // embedded 32-bit displacement or address + call32_operand = 2, // embedded 32-bit self-relative displacement + _WhichOperand_limit = 3 + }; + + public: + + // Creation + Assembler(CodeBuffer* code) : AbstractAssembler(code) {} + + // Decoding + static address locate_operand(address inst, WhichOperand which); + static address locate_next_instruction(address inst); + + // Stack + void pushad(); + void popad(); + + void pushfd(); + void popfd(); + + void pushl(int imm32); + void pushoop(jobject obj); + + void pushl(Register src); + void pushl(Address src); + // void pushl(Label& L, relocInfo::relocType rtype); ? needed? + + // dummy to prevent NULL being converted to Register + void pushl(void* dummy); + + void popl(Register dst); + void popl(Address dst); + + // Instruction prefixes + void prefix(Prefix p); + + // Moves + void movb(Register dst, Address src); + void movb(Address dst, int imm8); + void movb(Address dst, Register src); + + void movw(Address dst, int imm16); + void movw(Register dst, Address src); + void movw(Address dst, Register src); + + // these are dummies used to catch attempting to convert NULL to Register + void movl(Register dst, void* junk); + void movl(Address dst, void* junk); + + void movl(Register dst, int imm32); + void movl(Address dst, int imm32); + void movl(Register dst, Register src); + void movl(Register dst, Address src); + void movl(Address dst, Register src); + + void movsxb(Register dst, Address src); + void movsxb(Register dst, Register src); + + void movsxw(Register dst, Address src); + void movsxw(Register dst, Register src); + + void movzxb(Register dst, Address src); + void movzxb(Register dst, Register src); + + void movzxw(Register dst, Address src); + void movzxw(Register dst, Register src); + + // Conditional moves (P6 only) + void cmovl(Condition cc, Register dst, Register src); + void cmovl(Condition cc, Register dst, Address src); + + // Prefetches (SSE, SSE2, 3DNOW only) + void prefetcht0(Address src); + void prefetcht1(Address src); + void prefetcht2(Address src); + void prefetchnta(Address src); + void prefetchw(Address src); + void prefetchr(Address src); + + // Arithmetics + void adcl(Register dst, int imm32); + void adcl(Register dst, Address src); + void adcl(Register dst, Register src); + + void addl(Address dst, int imm32); + void addl(Address dst, Register src); + void addl(Register dst, int imm32); + void addl(Register dst, Address src); + void addl(Register dst, Register src); + + void andl(Register dst, int imm32); + void andl(Register dst, Address src); + void andl(Register dst, Register src); + + void cmpb(Address dst, int imm8); + void cmpw(Address dst, int imm16); + void cmpl(Address dst, int imm32); + void cmpl(Register dst, int imm32); + void cmpl(Register dst, Register src); + void cmpl(Register dst, Address src); + + // this is a dummy used to catch attempting to convert NULL to Register + void cmpl(Register dst, void* junk); + + protected: + // Don't use next inc() and dec() methods directly. INC & DEC instructions + // could cause a partial flag stall since they don't set CF flag. + // Use MacroAssembler::decrement() & MacroAssembler::increment() methods + // which call inc() & dec() or add() & sub() in accordance with + // the product flag UseIncDec value. + + void decl(Register dst); + void decl(Address dst); + + void incl(Register dst); + void incl(Address dst); + + public: + void idivl(Register src); + void cdql(); + + void imull(Register dst, Register src); + void imull(Register dst, Register src, int value); + + void leal(Register dst, Address src); + + void mull(Address src); + void mull(Register src); + + void negl(Register dst); + + void notl(Register dst); + + void orl(Address dst, int imm32); + void orl(Register dst, int imm32); + void orl(Register dst, Address src); + void orl(Register dst, Register src); + + void rcll(Register dst, int imm8); + + void sarl(Register dst, int imm8); + void sarl(Register dst); + + void sbbl(Address dst, int imm32); + void sbbl(Register dst, int imm32); + void sbbl(Register dst, Address src); + void sbbl(Register dst, Register src); + + void shldl(Register dst, Register src); + + void shll(Register dst, int imm8); + void shll(Register dst); + + void shrdl(Register dst, Register src); + + void shrl(Register dst, int imm8); + void shrl(Register dst); + + void subl(Address dst, int imm32); + void subl(Address dst, Register src); + void subl(Register dst, int imm32); + void subl(Register dst, Address src); + void subl(Register dst, Register src); + + void testb(Register dst, int imm8); + void testl(Register dst, int imm32); + void testl(Register dst, Address src); + void testl(Register dst, Register src); + + void xaddl(Address dst, Register src); + + void xorl(Register dst, int imm32); + void xorl(Register dst, Address src); + void xorl(Register dst, Register src); + + // Miscellaneous + void bswap(Register reg); + void lock(); + + void xchg (Register reg, Address adr); + void xchgl(Register dst, Register src); + + void cmpxchg (Register reg, Address adr); + void cmpxchg8 (Address adr); + + void nop(int i = 1); + void addr_nop_4(); + void addr_nop_5(); + void addr_nop_7(); + void addr_nop_8(); + + void hlt(); + void ret(int imm16); + void set_byte_if_not_zero(Register dst); // sets reg to 1 if not zero, otherwise 0 + void smovl(); + void rep_movl(); + void rep_set(); + void repne_scan(); + void setb(Condition cc, Register dst); + void membar(); // Serializing memory-fence + void cpuid(); + void cld(); + void std(); + + void emit_raw (unsigned char); + + // Calls + void call(Label& L, relocInfo::relocType rtype); + void call(Register reg); // push pc; pc <- reg + void call(Address adr); // push pc; pc <- adr + + // Jumps + void jmp(Address entry); // pc <- entry + void jmp(Register entry); // pc <- entry + + // Label operations & relative jumps (PPUM Appendix D) + void jmp(Label& L, relocInfo::relocType rtype = relocInfo::none); // unconditional jump to L + + // Force an 8-bit jump offset + // void jmpb(address entry); + + // Unconditional 8-bit offset jump to L. + // WARNING: be very careful using this for forward jumps. If the label is + // not bound within an 8-bit offset of this instruction, a run-time error + // will occur. + void jmpb(Label& L); + + // jcc is the generic conditional branch generator to run- + // time routines, jcc is used for branches to labels. jcc + // takes a branch opcode (cc) and a label (L) and generates + // either a backward branch or a forward branch and links it + // to the label fixup chain. Usage: + // + // Label L; // unbound label + // jcc(cc, L); // forward branch to unbound label + // bind(L); // bind label to the current pc + // jcc(cc, L); // backward branch to bound label + // bind(L); // illegal: a label may be bound only once + // + // Note: The same Label can be used for forward and backward branches + // but it may be bound only once. + + void jcc(Condition cc, Label& L, + relocInfo::relocType rtype = relocInfo::none); + + // Conditional jump to a 8-bit offset to L. + // WARNING: be very careful using this for forward jumps. If the label is + // not bound within an 8-bit offset of this instruction, a run-time error + // will occur. + void jccb(Condition cc, Label& L); + + // Floating-point operations + void fld1(); + void fldz(); + + void fld_s(Address adr); + void fld_s(int index); + void fld_d(Address adr); + void fld_x(Address adr); // extended-precision (80-bit) format + + void fst_s(Address adr); + void fst_d(Address adr); + + void fstp_s(Address adr); + void fstp_d(Address adr); + void fstp_d(int index); + void fstp_x(Address adr); // extended-precision (80-bit) format + + void fild_s(Address adr); + void fild_d(Address adr); + + void fist_s (Address adr); + void fistp_s(Address adr); + void fistp_d(Address adr); + + void fabs(); + void fchs(); + + void flog(); + void flog10(); + + void fldln2(); + void fyl2x(); + void fldlg2(); + + void fcos(); + void fsin(); + void ftan(); + void fsqrt(); + + // "Alternate" versions of instructions place result down in FPU + // stack instead of on TOS + void fadd_s(Address src); + void fadd_d(Address src); + void fadd(int i); + void fadda(int i); // "alternate" fadd + + void fsub_s(Address src); + void fsub_d(Address src); + void fsubr_s(Address src); + void fsubr_d(Address src); + + void fmul_s(Address src); + void fmul_d(Address src); + void fmul(int i); + void fmula(int i); // "alternate" fmul + + void fdiv_s(Address src); + void fdiv_d(Address src); + void fdivr_s(Address src); + void fdivr_d(Address src); + + void fsub(int i); + void fsuba(int i); // "alternate" fsub + void fsubr(int i); + void fsubra(int i); // "alternate" reversed fsub + void fdiv(int i); + void fdiva(int i); // "alternate" fdiv + void fdivr(int i); + void fdivra(int i); // "alternate" reversed fdiv + + void faddp(int i = 1); + void fsubp(int i = 1); + void fsubrp(int i = 1); + void fmulp(int i = 1); + void fdivp(int i = 1); + void fdivrp(int i = 1); + void fprem(); + void fprem1(); + + void fxch(int i = 1); + void fincstp(); + void fdecstp(); + void ffree(int i = 0); + + void fcomp_s(Address src); + void fcomp_d(Address src); + void fcom(int i); + void fcomp(int i = 1); + void fcompp(); + + void fucomi(int i = 1); + void fucomip(int i = 1); + + void ftst(); + void fnstsw_ax(); + void fwait(); + void finit(); + void fldcw(Address src); + void fnstcw(Address src); + + void fnsave(Address dst); + void frstor(Address src); + void fldenv(Address src); + + void sahf(); + + protected: + void emit_sse_operand(XMMRegister reg, Address adr); + void emit_sse_operand(Register reg, Address adr); + void emit_sse_operand(XMMRegister dst, XMMRegister src); + void emit_sse_operand(XMMRegister dst, Register src); + void emit_sse_operand(Register dst, XMMRegister src); + + void emit_operand(MMXRegister reg, Address adr); + + public: + // mmx operations + void movq( MMXRegister dst, Address src ); + void movq( Address dst, MMXRegister src ); + void emms(); + + // xmm operations + void addss(XMMRegister dst, Address src); // Add Scalar Single-Precision Floating-Point Values + void addss(XMMRegister dst, XMMRegister src); + void addsd(XMMRegister dst, Address src); // Add Scalar Double-Precision Floating-Point Values + void addsd(XMMRegister dst, XMMRegister src); + + void subss(XMMRegister dst, Address src); // Subtract Scalar Single-Precision Floating-Point Values + void subss(XMMRegister dst, XMMRegister src); + void subsd(XMMRegister dst, Address src); // Subtract Scalar Double-Precision Floating-Point Values + void subsd(XMMRegister dst, XMMRegister src); + + void mulss(XMMRegister dst, Address src); // Multiply Scalar Single-Precision Floating-Point Values + void mulss(XMMRegister dst, XMMRegister src); + void mulsd(XMMRegister dst, Address src); // Multiply Scalar Double-Precision Floating-Point Values + void mulsd(XMMRegister dst, XMMRegister src); + + void divss(XMMRegister dst, Address src); // Divide Scalar Single-Precision Floating-Point Values + void divss(XMMRegister dst, XMMRegister src); + void divsd(XMMRegister dst, Address src); // Divide Scalar Double-Precision Floating-Point Values + void divsd(XMMRegister dst, XMMRegister src); + + void sqrtss(XMMRegister dst, Address src); // Compute Square Root of Scalar Single-Precision Floating-Point Value + void sqrtss(XMMRegister dst, XMMRegister src); + void sqrtsd(XMMRegister dst, Address src); // Compute Square Root of Scalar Double-Precision Floating-Point Value + void sqrtsd(XMMRegister dst, XMMRegister src); + + void pxor(XMMRegister dst, Address src); // Xor Packed Byte Integer Values + void pxor(XMMRegister dst, XMMRegister src); // Xor Packed Byte Integer Values + + void comiss(XMMRegister dst, Address src); // Ordered Compare Scalar Single-Precision Floating-Point Values and set EFLAGS + void comiss(XMMRegister dst, XMMRegister src); + void comisd(XMMRegister dst, Address src); // Ordered Compare Scalar Double-Precision Floating-Point Values and set EFLAGS + void comisd(XMMRegister dst, XMMRegister src); + + void ucomiss(XMMRegister dst, Address src); // Unordered Compare Scalar Single-Precision Floating-Point Values and set EFLAGS + void ucomiss(XMMRegister dst, XMMRegister src); + void ucomisd(XMMRegister dst, Address src); // Unordered Compare Scalar Double-Precision Floating-Point Values and set EFLAGS + void ucomisd(XMMRegister dst, XMMRegister src); + + void cvtss2sd(XMMRegister dst, Address src); // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value + void cvtss2sd(XMMRegister dst, XMMRegister src); + void cvtsd2ss(XMMRegister dst, Address src); // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value + void cvtsd2ss(XMMRegister dst, XMMRegister src); + + void cvtsi2ss(XMMRegister dst, Address src); // Convert Doubleword Integer to Scalar Single-Precision Floating-Point Value + void cvtsi2ss(XMMRegister dst, Register src); + void cvtsi2sd(XMMRegister dst, Address src); // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value + void cvtsi2sd(XMMRegister dst, Register src); + + void cvtss2si(Register dst, Address src); // Convert Scalar Single-Precision Floating-Point Value to Doubleword Integer + void cvtss2si(Register dst, XMMRegister src); + void cvtsd2si(Register dst, Address src); // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer + void cvtsd2si(Register dst, XMMRegister src); + + void cvttss2si(Register dst, Address src); // Convert with Truncation Scalar Single-Precision Floating-Point Value to Doubleword Integer + void cvttss2si(Register dst, XMMRegister src); + void cvttsd2si(Register dst, Address src); // Convert with Truncation Scalar Double-Precision Floating-Point Value to Doubleword Integer + void cvttsd2si(Register dst, XMMRegister src); + + protected: // Avoid using the next instructions directly. + // New cpus require use of movsd and movss to avoid partial register stall + // when loading from memory. But for old Opteron use movlpd instead of movsd. + // The selection is done in MacroAssembler::movdbl() and movflt(). + void movss(XMMRegister dst, Address src); // Move Scalar Single-Precision Floating-Point Values + void movss(XMMRegister dst, XMMRegister src); + void movss(Address dst, XMMRegister src); + void movsd(XMMRegister dst, Address src); // Move Scalar Double-Precision Floating-Point Values + void movsd(XMMRegister dst, XMMRegister src); + void movsd(Address dst, XMMRegister src); + void movlpd(XMMRegister dst, Address src); + // New cpus require use of movaps and movapd to avoid partial register stall + // when moving between registers. + void movaps(XMMRegister dst, XMMRegister src); + void movapd(XMMRegister dst, XMMRegister src); + public: + + void andps(XMMRegister dst, Address src); // Bitwise Logical AND of Packed Single-Precision Floating-Point Values + void andps(XMMRegister dst, XMMRegister src); + void andpd(XMMRegister dst, Address src); // Bitwise Logical AND of Packed Double-Precision Floating-Point Values + void andpd(XMMRegister dst, XMMRegister src); + + void andnps(XMMRegister dst, Address src); // Bitwise Logical AND NOT of Packed Single-Precision Floating-Point Values + void andnps(XMMRegister dst, XMMRegister src); + void andnpd(XMMRegister dst, Address src); // Bitwise Logical AND NOT of Packed Double-Precision Floating-Point Values + void andnpd(XMMRegister dst, XMMRegister src); + + void orps(XMMRegister dst, Address src); // Bitwise Logical OR of Packed Single-Precision Floating-Point Values + void orps(XMMRegister dst, XMMRegister src); + void orpd(XMMRegister dst, Address src); // Bitwise Logical OR of Packed Double-Precision Floating-Point Values + void orpd(XMMRegister dst, XMMRegister src); + + void xorps(XMMRegister dst, Address src); // Bitwise Logical XOR of Packed Single-Precision Floating-Point Values + void xorps(XMMRegister dst, XMMRegister src); + void xorpd(XMMRegister dst, Address src); // Bitwise Logical XOR of Packed Double-Precision Floating-Point Values + void xorpd(XMMRegister dst, XMMRegister src); + + void movq(XMMRegister dst, Address src); // Move Quadword + void movq(XMMRegister dst, XMMRegister src); + void movq(Address dst, XMMRegister src); + + void movd(XMMRegister dst, Address src); // Move Doubleword + void movd(XMMRegister dst, Register src); + void movd(Register dst, XMMRegister src); + void movd(Address dst, XMMRegister src); + + void movdqa(XMMRegister dst, Address src); // Move Aligned Double Quadword + void movdqa(XMMRegister dst, XMMRegister src); + void movdqa(Address dst, XMMRegister src); + + void pshufd(XMMRegister dst, XMMRegister src, int mode); // Shuffle Packed Doublewords + void pshufd(XMMRegister dst, Address src, int mode); + void pshuflw(XMMRegister dst, XMMRegister src, int mode); // Shuffle Packed Low Words + void pshuflw(XMMRegister dst, Address src, int mode); + + void psrlq(XMMRegister dst, int shift); // Shift Right Logical Quadword Immediate + + void punpcklbw(XMMRegister dst, XMMRegister src); // Interleave Low Bytes + void punpcklbw(XMMRegister dst, Address src); + + void ldmxcsr( Address src ); + void stmxcsr( Address dst ); +}; + + +// MacroAssembler extends Assembler by frequently used macros. +// +// Instructions for which a 'better' code sequence exists depending +// on arguments should also go in here. + +class MacroAssembler: public Assembler { + friend class LIR_Assembler; + protected: + + Address as_Address(AddressLiteral adr); + Address as_Address(ArrayAddress adr); + + // Support for VM calls + // + // This is the base routine called by the different versions of call_VM_leaf. The interpreter + // may customize this version by overriding it for its purposes (e.g., to save/restore + // additional registers when doing a VM call). +#ifdef CC_INTERP + // c++ interpreter never wants to use interp_masm version of call_VM + #define VIRTUAL +#else + #define VIRTUAL virtual +#endif + + VIRTUAL void call_VM_leaf_base( + address entry_point, // the entry point + int number_of_arguments // the number of arguments to pop after the call + ); + + // This is the base routine called by the different versions of call_VM. The interpreter + // may customize this version by overriding it for its purposes (e.g., to save/restore + // additional registers when doing a VM call). + // + // If no java_thread register is specified (noreg) than rdi will be used instead. call_VM_base + // returns the register which contains the thread upon return. If a thread register has been + // specified, the return value will correspond to that register. If no last_java_sp is specified + // (noreg) than rsp will be used instead. + VIRTUAL void call_VM_base( // returns the register containing the thread upon return + Register oop_result, // where an oop-result ends up if any; use noreg otherwise + Register java_thread, // the thread if computed before ; use noreg otherwise + Register last_java_sp, // to set up last_Java_frame in stubs; use noreg otherwise + address entry_point, // the entry point + int number_of_arguments, // the number of arguments (w/o thread) to pop after the call + bool check_exceptions // whether to check for pending exceptions after return + ); + + // These routines should emit JVMTI PopFrame and ForceEarlyReturn handling code. + // The implementation is only non-empty for the InterpreterMacroAssembler, + // as only the interpreter handles PopFrame and ForceEarlyReturn requests. + virtual void check_and_handle_popframe(Register java_thread); + virtual void check_and_handle_earlyret(Register java_thread); + + void call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions = true); + + // helpers for FPU flag access + // tmp is a temporary register, if none is available use noreg + void save_rax (Register tmp); + void restore_rax(Register tmp); + + public: + MacroAssembler(CodeBuffer* code) : Assembler(code) {} + + // Support for NULL-checks + // + // Generates code that causes a NULL OS exception if the content of reg is NULL. + // If the accessed location is M[reg + offset] and the offset is known, provide the + // offset. No explicit code generation is needed if the offset is within a certain + // range (0 <= offset <= page_size). + + void null_check(Register reg, int offset = -1); + static bool needs_explicit_null_check(int offset); + + // Required platform-specific helpers for Label::patch_instructions. + // They _shadow_ the declarations in AbstractAssembler, which are undefined. + void pd_patch_instruction(address branch, address target); +#ifndef PRODUCT + static void pd_print_patched_instruction(address branch); +#endif + + // The following 4 methods return the offset of the appropriate move instruction + + // Support for fast byte/word loading with zero extension (depending on particular CPU) + int load_unsigned_byte(Register dst, Address src); + int load_unsigned_word(Register dst, Address src); + + // Support for fast byte/word loading with sign extension (depending on particular CPU) + int load_signed_byte(Register dst, Address src); + int load_signed_word(Register dst, Address src); + + // Support for sign-extension (hi:lo = extend_sign(lo)) + void extend_sign(Register hi, Register lo); + + // Support for inc/dec with optimal instruction selection depending on value + void increment(Register reg, int value = 1); + void decrement(Register reg, int value = 1); + void increment(Address dst, int value = 1); + void decrement(Address dst, int value = 1); + + // Support optimal SSE move instructions. + void movflt(XMMRegister dst, XMMRegister src) { + if (UseXmmRegToRegMoveAll) { movaps(dst, src); return; } + else { movss (dst, src); return; } + } + void movflt(XMMRegister dst, Address src) { movss(dst, src); } + void movflt(XMMRegister dst, AddressLiteral src); + void movflt(Address dst, XMMRegister src) { movss(dst, src); } + + void movdbl(XMMRegister dst, XMMRegister src) { + if (UseXmmRegToRegMoveAll) { movapd(dst, src); return; } + else { movsd (dst, src); return; } + } + + void movdbl(XMMRegister dst, AddressLiteral src); + + void movdbl(XMMRegister dst, Address src) { + if (UseXmmLoadAndClearUpper) { movsd (dst, src); return; } + else { movlpd(dst, src); return; } + } + void movdbl(Address dst, XMMRegister src) { movsd(dst, src); } + + void increment(AddressLiteral dst); + void increment(ArrayAddress dst); + + + // Alignment + void align(int modulus); + + // Misc + void fat_nop(); // 5 byte nop + + // Stack frame creation/removal + void enter(); + void leave(); + + // Support for getting the JavaThread pointer (i.e.; a reference to thread-local information) + // The pointer will be loaded into the thread register. + void get_thread(Register thread); + + // Support for VM calls + // + // It is imperative that all calls into the VM are handled via the call_VM macros. + // They make sure that the stack linkage is setup correctly. call_VM's correspond + // to ENTRY/ENTRY_X entry points while call_VM_leaf's correspond to LEAF entry points. + + void call_VM(Register oop_result, address entry_point, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions = true); + + void call_VM(Register oop_result, Register last_java_sp, address entry_point, int number_of_arguments = 0, bool check_exceptions = true); + void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, bool check_exceptions = true); + void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); + void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions = true); + + void call_VM_leaf(address entry_point, int number_of_arguments = 0); + void call_VM_leaf(address entry_point, Register arg_1); + void call_VM_leaf(address entry_point, Register arg_1, Register arg_2); + void call_VM_leaf(address entry_point, Register arg_1, Register arg_2, Register arg_3); + + // last Java Frame (fills frame anchor) + void set_last_Java_frame(Register thread, Register last_java_sp, Register last_java_fp, address last_java_pc); + void reset_last_Java_frame(Register thread, bool clear_fp, bool clear_pc); + + // Stores + void store_check(Register obj); // store check for obj - register is destroyed afterwards + void store_check(Register obj, Address dst); // same as above, dst is exact store location (reg. is destroyed) + + // split store_check(Register obj) to enhance instruction interleaving + void store_check_part_1(Register obj); + void store_check_part_2(Register obj); + + // C 'boolean' to Java boolean: x == 0 ? 0 : 1 + void c2bool(Register x); + + // C++ bool manipulation + + void movbool(Register dst, Address src); + void movbool(Address dst, bool boolconst); + void movbool(Address dst, Register src); + void testbool(Register dst); + + // Int division/reminder for Java + // (as idivl, but checks for special case as described in JVM spec.) + // returns idivl instruction offset for implicit exception handling + int corrected_idivl(Register reg); + + void int3(); + + // Long negation for Java + void lneg(Register hi, Register lo); + + // Long multiplication for Java + // (destroys contents of rax, rbx, rcx and rdx) + void lmul(int x_rsp_offset, int y_rsp_offset); // rdx:rax = x * y + + // Long shifts for Java + // (semantics as described in JVM spec.) + void lshl(Register hi, Register lo); // hi:lo << (rcx & 0x3f) + void lshr(Register hi, Register lo, bool sign_extension = false); // hi:lo >> (rcx & 0x3f) + + // Long compare for Java + // (semantics as described in JVM spec.) + void lcmp2int(Register x_hi, Register x_lo, Register y_hi, Register y_lo); // x_hi = lcmp(x, y) + + // Compares the top-most stack entries on the FPU stack and sets the eflags as follows: + // + // CF (corresponds to C0) if x < y + // PF (corresponds to C2) if unordered + // ZF (corresponds to C3) if x = y + // + // The arguments are in reversed order on the stack (i.e., top of stack is first argument). + // tmp is a temporary register, if none is available use noreg (only matters for non-P6 code) + void fcmp(Register tmp); + // Variant of the above which allows y to be further down the stack + // and which only pops x and y if specified. If pop_right is + // specified then pop_left must also be specified. + void fcmp(Register tmp, int index, bool pop_left, bool pop_right); + + // Floating-point comparison for Java + // Compares the top-most stack entries on the FPU stack and stores the result in dst. + // The arguments are in reversed order on the stack (i.e., top of stack is first argument). + // (semantics as described in JVM spec.) + void fcmp2int(Register dst, bool unordered_is_less); + // Variant of the above which allows y to be further down the stack + // and which only pops x and y if specified. If pop_right is + // specified then pop_left must also be specified. + void fcmp2int(Register dst, bool unordered_is_less, int index, bool pop_left, bool pop_right); + + // Floating-point remainder for Java (ST0 = ST0 fremr ST1, ST1 is empty afterwards) + // tmp is a temporary register, if none is available use noreg + void fremr(Register tmp); + + + // same as fcmp2int, but using SSE2 + void cmpss2int(XMMRegister opr1, XMMRegister opr2, Register dst, bool unordered_is_less); + void cmpsd2int(XMMRegister opr1, XMMRegister opr2, Register dst, bool unordered_is_less); + + // Inlined sin/cos generator for Java; must not use CPU instruction + // directly on Intel as it does not have high enough precision + // outside of the range [-pi/4, pi/4]. Extra argument indicate the + // number of FPU stack slots in use; all but the topmost will + // require saving if a slow case is necessary. Assumes argument is + // on FP TOS; result is on FP TOS. No cpu registers are changed by + // this code. + void trigfunc(char trig, int num_fpu_regs_in_use = 1); + + // branch to L if FPU flag C2 is set/not set + // tmp is a temporary register, if none is available use noreg + void jC2 (Register tmp, Label& L); + void jnC2(Register tmp, Label& L); + + // Pop ST (ffree & fincstp combined) + void fpop(); + + // pushes double TOS element of FPU stack on CPU stack; pops from FPU stack + void push_fTOS(); + + // pops double TOS element from CPU stack and pushes on FPU stack + void pop_fTOS(); + + void empty_FPU_stack(); + + void push_IU_state(); + void pop_IU_state(); + + void push_FPU_state(); + void pop_FPU_state(); + + void push_CPU_state(); + void pop_CPU_state(); + + // Sign extension + void sign_extend_short(Register reg); + void sign_extend_byte(Register reg); + + // Division by power of 2, rounding towards 0 + void division_with_shift(Register reg, int shift_value); + + // Round up to a power of two + void round_to(Register reg, int modulus); + + // Callee saved registers handling + void push_callee_saved_registers(); + void pop_callee_saved_registers(); + + // allocation + void eden_allocate( + Register obj, // result: pointer to object after successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Label& slow_case // continuation point if fast allocation fails + ); + void tlab_allocate( + Register obj, // result: pointer to object after successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Register t2, // temp register + Label& slow_case // continuation point if fast allocation fails + ); + void tlab_refill(Label& retry_tlab, Label& try_eden, Label& slow_case); + + //---- + void set_word_if_not_zero(Register reg); // sets reg to 1 if not zero, otherwise 0 + + // Debugging + void verify_oop(Register reg, const char* s = "broken oop"); // only if +VerifyOops + void verify_oop_addr(Address addr, const char * s = "broken oop addr"); + + void verify_FPU(int stack_depth, const char* s = "illegal FPU state"); // only if +VerifyFPU + void stop(const char* msg); // prints msg, dumps registers and stops execution + void warn(const char* msg); // prints msg and continues + static void debug(int rdi, int rsi, int rbp, int rsp, int rbx, int rdx, int rcx, int rax, int eip, char* msg); + void os_breakpoint(); + void untested() { stop("untested"); } + void unimplemented(const char* what = "") { char* b = new char[1024]; jio_snprintf(b, sizeof(b), "unimplemented: %s", what); stop(b); } + void should_not_reach_here() { stop("should not reach here"); } + void print_CPU_state(); + + // Stack overflow checking + void bang_stack_with_offset(int offset) { + // stack grows down, caller passes positive offset + assert(offset > 0, "must bang with negative offset"); + movl(Address(rsp, (-offset)), rax); + } + + // Writes to stack successive pages until offset reached to check for + // stack overflow + shadow pages. Also, clobbers tmp + void bang_stack_size(Register size, Register tmp); + + // Support for serializing memory accesses between threads + void serialize_memory(Register thread, Register tmp); + + void verify_tlab(); + + // Biased locking support + // lock_reg and obj_reg must be loaded up with the appropriate values. + // swap_reg must be rax, and is killed. + // tmp_reg is optional. If it is supplied (i.e., != noreg) it will + // be killed; if not supplied, push/pop will be used internally to + // allocate a temporary (inefficient, avoid if possible). + // Optional slow case is for implementations (interpreter and C1) which branch to + // slow case directly. Leaves condition codes set for C2's Fast_Lock node. + // Returns offset of first potentially-faulting instruction for null + // check info (currently consumed only by C1). If + // swap_reg_contains_mark is true then returns -1 as it is assumed + // the calling code has already passed any potential faults. + int biased_locking_enter(Register lock_reg, Register obj_reg, Register swap_reg, Register tmp_reg, + bool swap_reg_contains_mark, + Label& done, Label* slow_case = NULL, + BiasedLockingCounters* counters = NULL); + void biased_locking_exit (Register obj_reg, Register temp_reg, Label& done); + + + Condition negate_condition(Condition cond); + + // Instructions that use AddressLiteral operands. These instruction can handle 32bit/64bit + // operands. In general the names are modified to avoid hiding the instruction in Assembler + // so that we don't need to implement all the varieties in the Assembler with trivial wrappers + // here in MacroAssembler. The major exception to this rule is call + + // Arithmetics + + void cmp8(AddressLiteral src1, int8_t imm); + + // QQQ renamed to drag out the casting of address to int32_t/intptr_t + void cmp32(Register src1, int32_t imm); + + void cmp32(AddressLiteral src1, int32_t imm); + // compare reg - mem, or reg - &mem + void cmp32(Register src1, AddressLiteral src2); + + void cmp32(Register src1, Address src2); + + // NOTE src2 must be the lval. This is NOT an mem-mem compare + void cmpptr(Address src1, AddressLiteral src2); + + void cmpptr(Register src1, AddressLiteral src2); + + void cmpoop(Address dst, jobject obj); + void cmpoop(Register dst, jobject obj); + + + void cmpxchgptr(Register reg, AddressLiteral adr); + + // Helper functions for statistics gathering. + // Conditionally (atomically, on MPs) increments passed counter address, preserving condition codes. + void cond_inc32(Condition cond, AddressLiteral counter_addr); + // Unconditional atomic increment. + void atomic_incl(AddressLiteral counter_addr); + + void lea(Register dst, AddressLiteral adr); + void lea(Address dst, AddressLiteral adr); + + void test32(Register dst, AddressLiteral src); + + // Calls + + void call(Label& L, relocInfo::relocType rtype); + void call(Register entry); + + // NOTE: this call tranfers to the effective address of entry NOT + // the address contained by entry. This is because this is more natural + // for jumps/calls. + void call(AddressLiteral entry); + + // Jumps + + // NOTE: these jumps tranfer to the effective address of dst NOT + // the address contained by dst. This is because this is more natural + // for jumps/calls. + void jump(AddressLiteral dst); + void jump_cc(Condition cc, AddressLiteral dst); + + // 32bit can do a case table jump in one instruction but we no longer allow the base + // to be installed in the Address class. This jump will tranfers to the address + // contained in the location described by entry (not the address of entry) + void jump(ArrayAddress entry); + + // Floating + + void andpd(XMMRegister dst, Address src) { Assembler::andpd(dst, src); } + void andpd(XMMRegister dst, AddressLiteral src); + + void comiss(XMMRegister dst, Address src) { Assembler::comiss(dst, src); } + void comiss(XMMRegister dst, AddressLiteral src); + + void comisd(XMMRegister dst, Address src) { Assembler::comisd(dst, src); } + void comisd(XMMRegister dst, AddressLiteral src); + + void fldcw(Address src) { Assembler::fldcw(src); } + void fldcw(AddressLiteral src); + + void fld_s(int index) { Assembler::fld_s(index); } + void fld_s(Address src) { Assembler::fld_s(src); } + void fld_s(AddressLiteral src); + + void fld_d(Address src) { Assembler::fld_d(src); } + void fld_d(AddressLiteral src); + + void fld_x(Address src) { Assembler::fld_x(src); } + void fld_x(AddressLiteral src); + + void ldmxcsr(Address src) { Assembler::ldmxcsr(src); } + void ldmxcsr(AddressLiteral src); + + void movss(Address dst, XMMRegister src) { Assembler::movss(dst, src); } + void movss(XMMRegister dst, XMMRegister src) { Assembler::movss(dst, src); } + void movss(XMMRegister dst, Address src) { Assembler::movss(dst, src); } + void movss(XMMRegister dst, AddressLiteral src); + + void movsd(XMMRegister dst, XMMRegister src) { Assembler::movsd(dst, src); } + void movsd(Address dst, XMMRegister src) { Assembler::movsd(dst, src); } + void movsd(XMMRegister dst, Address src) { Assembler::movsd(dst, src); } + void movsd(XMMRegister dst, AddressLiteral src); + + void ucomiss(XMMRegister dst, XMMRegister src) { Assembler::ucomiss(dst, src); } + void ucomiss(XMMRegister dst, Address src) { Assembler::ucomiss(dst, src); } + void ucomiss(XMMRegister dst, AddressLiteral src); + + void ucomisd(XMMRegister dst, XMMRegister src) { Assembler::ucomisd(dst, src); } + void ucomisd(XMMRegister dst, Address src) { Assembler::ucomisd(dst, src); } + void ucomisd(XMMRegister dst, AddressLiteral src); + + // Bitwise Logical XOR of Packed Double-Precision Floating-Point Values + void xorpd(XMMRegister dst, XMMRegister src) { Assembler::xorpd(dst, src); } + void xorpd(XMMRegister dst, Address src) { Assembler::xorpd(dst, src); } + void xorpd(XMMRegister dst, AddressLiteral src); + + // Bitwise Logical XOR of Packed Single-Precision Floating-Point Values + void xorps(XMMRegister dst, XMMRegister src) { Assembler::xorps(dst, src); } + void xorps(XMMRegister dst, Address src) { Assembler::xorps(dst, src); } + void xorps(XMMRegister dst, AddressLiteral src); + + // Data + + void movoop(Register dst, jobject obj); + void movoop(Address dst, jobject obj); + + void movptr(ArrayAddress dst, Register src); + // can this do an lea? + void movptr(Register dst, ArrayAddress src); + + void movptr(Register dst, AddressLiteral src); + + // to avoid hiding movl + void mov32(AddressLiteral dst, Register src); + void mov32(Register dst, AddressLiteral src); + // to avoid hiding movb + void movbyte(ArrayAddress dst, int src); + + // Can push value or effective address + void pushptr(AddressLiteral src); + +#undef VIRTUAL + +}; + +/** + * class SkipIfEqual: + * + * Instantiating this class will result in assembly code being output that will + * jump around any code emitted between the creation of the instance and it's + * automatic destruction at the end of a scope block, depending on the value of + * the flag passed to the constructor, which will be checked at run-time. + */ +class SkipIfEqual { + private: + MacroAssembler* _masm; + Label _label; + + public: + SkipIfEqual(MacroAssembler*, const bool* flag_addr, bool value); + ~SkipIfEqual(); +}; + +#ifdef ASSERT +inline bool AbstractAssembler::pd_check_instruction_mark() { return true; } +#endif diff --git a/hotspot/src/cpu/x86/vm/assembler_x86_32.inline.hpp b/hotspot/src/cpu/x86/vm/assembler_x86_32.inline.hpp new file mode 100644 index 00000000000..8e20a2e0620 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/assembler_x86_32.inline.hpp @@ -0,0 +1,64 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void MacroAssembler::pd_patch_instruction(address branch, address target) { + unsigned char op = branch[0]; + assert(op == 0xE8 /* call */ || + op == 0xE9 /* jmp */ || + op == 0xEB /* short jmp */ || + (op & 0xF0) == 0x70 /* short jcc */ || + op == 0x0F && (branch[1] & 0xF0) == 0x80 /* jcc */, + "Invalid opcode at patch point"); + + if (op == 0xEB || (op & 0xF0) == 0x70) { + // short offset operators (jmp and jcc) + char* disp = (char*) &branch[1]; + int imm8 = target - (address) &disp[1]; + guarantee(this->is8bit(imm8), "Short forward jump exceeds 8-bit offset"); + *disp = imm8; + } else { + int* disp = (int*) &branch[(op == 0x0F)? 2: 1]; + int imm32 = target - (address) &disp[1]; + *disp = imm32; + } +} + +#ifndef PRODUCT +inline void MacroAssembler::pd_print_patched_instruction(address branch) { + const char* s; + unsigned char op = branch[0]; + if (op == 0xE8) { + s = "call"; + } else if (op == 0xE9 || op == 0xEB) { + s = "jmp"; + } else if ((op & 0xF0) == 0x70) { + s = "jcc"; + } else if (op == 0x0F) { + s = "jcc"; + } else { + s = "????"; + } + tty->print("%s (unresolved)", s); +} +#endif // ndef PRODUCT diff --git a/hotspot/src/cpu/x86/vm/assembler_x86_64.cpp b/hotspot/src/cpu/x86/vm/assembler_x86_64.cpp new file mode 100644 index 00000000000..0c457270632 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/assembler_x86_64.cpp @@ -0,0 +1,5160 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler_x86_64.cpp.incl" + +// Implementation of AddressLiteral + +AddressLiteral::AddressLiteral(address target, relocInfo::relocType rtype) { + _is_lval = false; + _target = target; + switch (rtype) { + case relocInfo::oop_type: + // Oops are a special case. Normally they would be their own section + // but in cases like icBuffer they are literals in the code stream that + // we don't have a section for. We use none so that we get a literal address + // which is always patchable. + break; + case relocInfo::external_word_type: + _rspec = external_word_Relocation::spec(target); + break; + case relocInfo::internal_word_type: + _rspec = internal_word_Relocation::spec(target); + break; + case relocInfo::opt_virtual_call_type: + _rspec = opt_virtual_call_Relocation::spec(); + break; + case relocInfo::static_call_type: + _rspec = static_call_Relocation::spec(); + break; + case relocInfo::runtime_call_type: + _rspec = runtime_call_Relocation::spec(); + break; + case relocInfo::none: + break; + default: + ShouldNotReachHere(); + break; + } +} + +// Implementation of Address + +Address Address::make_array(ArrayAddress adr) { +#ifdef _LP64 + // Not implementable on 64bit machines + // Should have been handled higher up the call chain. + ShouldNotReachHere(); + return Address(); +#else + AddressLiteral base = adr.base(); + Address index = adr.index(); + assert(index._disp == 0, "must not have disp"); // maybe it can? + Address array(index._base, index._index, index._scale, (intptr_t) base.target()); + array._rspec = base._rspec; + return array; +#endif // _LP64 +} + +// exceedingly dangerous constructor +Address::Address(int disp, address loc, relocInfo::relocType rtype) { + _base = noreg; + _index = noreg; + _scale = no_scale; + _disp = disp; + switch (rtype) { + case relocInfo::external_word_type: + _rspec = external_word_Relocation::spec(loc); + break; + case relocInfo::internal_word_type: + _rspec = internal_word_Relocation::spec(loc); + break; + case relocInfo::runtime_call_type: + // HMM + _rspec = runtime_call_Relocation::spec(); + break; + case relocInfo::none: + break; + default: + ShouldNotReachHere(); + } +} + +// Convert the raw encoding form into the form expected by the constructor for +// Address. An index of 4 (rsp) corresponds to having no index, so convert +// that to noreg for the Address constructor. +Address Address::make_raw(int base, int index, int scale, int disp) { + bool valid_index = index != rsp->encoding(); + if (valid_index) { + Address madr(as_Register(base), as_Register(index), (Address::ScaleFactor)scale, in_ByteSize(disp)); + return madr; + } else { + Address madr(as_Register(base), noreg, Address::no_scale, in_ByteSize(disp)); + return madr; + } +} + + +// Implementation of Assembler +int AbstractAssembler::code_fill_byte() { + return (u_char)'\xF4'; // hlt +} + +// This should only be used by 64bit instructions that can use rip-relative +// it cannot be used by instructions that want an immediate value. + +bool Assembler::reachable(AddressLiteral adr) { + int64_t disp; + // None will force a 64bit literal to the code stream. Likely a placeholder + // for something that will be patched later and we need to certain it will + // always be reachable. + if (adr.reloc() == relocInfo::none) { + return false; + } + if (adr.reloc() == relocInfo::internal_word_type) { + // This should be rip relative and easily reachable. + return true; + } + if (adr.reloc() != relocInfo::external_word_type && + adr.reloc() != relocInfo::runtime_call_type ) { + return false; + } + + // Stress the correction code + if (ForceUnreachable) { + // Must be runtimecall reloc, see if it is in the codecache + // Flipping stuff in the codecache to be unreachable causes issues + // with things like inline caches where the additional instructions + // are not handled. + if (CodeCache::find_blob(adr._target) == NULL) { + return false; + } + } + // For external_word_type/runtime_call_type if it is reachable from where we + // are now (possibly a temp buffer) and where we might end up + // anywhere in the codeCache then we are always reachable. + // This would have to change if we ever save/restore shared code + // to be more pessimistic. + + disp = (int64_t)adr._target - ((int64_t)CodeCache::low_bound() + sizeof(int)); + if (!is_simm32(disp)) return false; + disp = (int64_t)adr._target - ((int64_t)CodeCache::high_bound() + sizeof(int)); + if (!is_simm32(disp)) return false; + + disp = (int64_t)adr._target - ((int64_t)_code_pos + sizeof(int)); + + // Because rip relative is a disp + address_of_next_instruction and we + // don't know the value of address_of_next_instruction we apply a fudge factor + // to make sure we will be ok no matter the size of the instruction we get placed into. + // We don't have to fudge the checks above here because they are already worst case. + + // 12 == override/rex byte, opcode byte, rm byte, sib byte, a 4-byte disp , 4-byte literal + // + 4 because better safe than sorry. + const int fudge = 12 + 4; + if (disp < 0) { + disp -= fudge; + } else { + disp += fudge; + } + return is_simm32(disp); +} + + +// make this go away eventually +void Assembler::emit_data(jint data, + relocInfo::relocType rtype, + int format) { + if (rtype == relocInfo::none) { + emit_long(data); + } else { + emit_data(data, Relocation::spec_simple(rtype), format); + } +} + +void Assembler::emit_data(jint data, + RelocationHolder const& rspec, + int format) { + assert(imm64_operand == 0, "default format must be imm64 in this file"); + assert(imm64_operand != format, "must not be imm64"); + assert(inst_mark() != NULL, "must be inside InstructionMark"); + if (rspec.type() != relocInfo::none) { + #ifdef ASSERT + check_relocation(rspec, format); + #endif + // Do not use AbstractAssembler::relocate, which is not intended for + // embedded words. Instead, relocate to the enclosing instruction. + + // hack. call32 is too wide for mask so use disp32 + if (format == call32_operand) + code_section()->relocate(inst_mark(), rspec, disp32_operand); + else + code_section()->relocate(inst_mark(), rspec, format); + } + emit_long(data); +} + +void Assembler::emit_data64(jlong data, + relocInfo::relocType rtype, + int format) { + if (rtype == relocInfo::none) { + emit_long64(data); + } else { + emit_data64(data, Relocation::spec_simple(rtype), format); + } +} + +void Assembler::emit_data64(jlong data, + RelocationHolder const& rspec, + int format) { + assert(imm64_operand == 0, "default format must be imm64 in this file"); + assert(imm64_operand == format, "must be imm64"); + assert(inst_mark() != NULL, "must be inside InstructionMark"); + // Do not use AbstractAssembler::relocate, which is not intended for + // embedded words. Instead, relocate to the enclosing instruction. + code_section()->relocate(inst_mark(), rspec, format); +#ifdef ASSERT + check_relocation(rspec, format); +#endif + emit_long64(data); +} + +void Assembler::emit_arith_b(int op1, int op2, Register dst, int imm8) { + assert(isByte(op1) && isByte(op2), "wrong opcode"); + assert(isByte(imm8), "not a byte"); + assert((op1 & 0x01) == 0, "should be 8bit operation"); + int dstenc = dst->encoding(); + if (dstenc >= 8) { + dstenc -= 8; + } + emit_byte(op1); + emit_byte(op2 | dstenc); + emit_byte(imm8); +} + +void Assembler::emit_arith(int op1, int op2, Register dst, int imm32) { + assert(isByte(op1) && isByte(op2), "wrong opcode"); + assert((op1 & 0x01) == 1, "should be 32bit operation"); + assert((op1 & 0x02) == 0, "sign-extension bit should not be set"); + int dstenc = dst->encoding(); + if (dstenc >= 8) { + dstenc -= 8; + } + if (is8bit(imm32)) { + emit_byte(op1 | 0x02); // set sign bit + emit_byte(op2 | dstenc); + emit_byte(imm32 & 0xFF); + } else { + emit_byte(op1); + emit_byte(op2 | dstenc); + emit_long(imm32); + } +} + +// immediate-to-memory forms +void Assembler::emit_arith_operand(int op1, + Register rm, Address adr, + int imm32) { + assert((op1 & 0x01) == 1, "should be 32bit operation"); + assert((op1 & 0x02) == 0, "sign-extension bit should not be set"); + if (is8bit(imm32)) { + emit_byte(op1 | 0x02); // set sign bit + emit_operand(rm, adr, 1); + emit_byte(imm32 & 0xFF); + } else { + emit_byte(op1); + emit_operand(rm, adr, 4); + emit_long(imm32); + } +} + + +void Assembler::emit_arith(int op1, int op2, Register dst, Register src) { + assert(isByte(op1) && isByte(op2), "wrong opcode"); + int dstenc = dst->encoding(); + int srcenc = src->encoding(); + if (dstenc >= 8) { + dstenc -= 8; + } + if (srcenc >= 8) { + srcenc -= 8; + } + emit_byte(op1); + emit_byte(op2 | dstenc << 3 | srcenc); +} + +void Assembler::emit_operand(Register reg, Register base, Register index, + Address::ScaleFactor scale, int disp, + RelocationHolder const& rspec, + int rip_relative_correction) { + relocInfo::relocType rtype = (relocInfo::relocType) rspec.type(); + int regenc = reg->encoding(); + if (regenc >= 8) { + regenc -= 8; + } + if (base->is_valid()) { + if (index->is_valid()) { + assert(scale != Address::no_scale, "inconsistent address"); + int indexenc = index->encoding(); + if (indexenc >= 8) { + indexenc -= 8; + } + int baseenc = base->encoding(); + if (baseenc >= 8) { + baseenc -= 8; + } + // [base + index*scale + disp] + if (disp == 0 && rtype == relocInfo::none && + base != rbp && base != r13) { + // [base + index*scale] + // [00 reg 100][ss index base] + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x04 | regenc << 3); + emit_byte(scale << 6 | indexenc << 3 | baseenc); + } else if (is8bit(disp) && rtype == relocInfo::none) { + // [base + index*scale + imm8] + // [01 reg 100][ss index base] imm8 + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x44 | regenc << 3); + emit_byte(scale << 6 | indexenc << 3 | baseenc); + emit_byte(disp & 0xFF); + } else { + // [base + index*scale + disp32] + // [10 reg 100][ss index base] disp32 + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x84 | regenc << 3); + emit_byte(scale << 6 | indexenc << 3 | baseenc); + emit_data(disp, rspec, disp32_operand); + } + } else if (base == rsp || base == r12) { + // [rsp + disp] + if (disp == 0 && rtype == relocInfo::none) { + // [rsp] + // [00 reg 100][00 100 100] + emit_byte(0x04 | regenc << 3); + emit_byte(0x24); + } else if (is8bit(disp) && rtype == relocInfo::none) { + // [rsp + imm8] + // [01 reg 100][00 100 100] disp8 + emit_byte(0x44 | regenc << 3); + emit_byte(0x24); + emit_byte(disp & 0xFF); + } else { + // [rsp + imm32] + // [10 reg 100][00 100 100] disp32 + emit_byte(0x84 | regenc << 3); + emit_byte(0x24); + emit_data(disp, rspec, disp32_operand); + } + } else { + // [base + disp] + assert(base != rsp && base != r12, "illegal addressing mode"); + int baseenc = base->encoding(); + if (baseenc >= 8) { + baseenc -= 8; + } + if (disp == 0 && rtype == relocInfo::none && + base != rbp && base != r13) { + // [base] + // [00 reg base] + emit_byte(0x00 | regenc << 3 | baseenc); + } else if (is8bit(disp) && rtype == relocInfo::none) { + // [base + disp8] + // [01 reg base] disp8 + emit_byte(0x40 | regenc << 3 | baseenc); + emit_byte(disp & 0xFF); + } else { + // [base + disp32] + // [10 reg base] disp32 + emit_byte(0x80 | regenc << 3 | baseenc); + emit_data(disp, rspec, disp32_operand); + } + } + } else { + if (index->is_valid()) { + assert(scale != Address::no_scale, "inconsistent address"); + int indexenc = index->encoding(); + if (indexenc >= 8) { + indexenc -= 8; + } + // [index*scale + disp] + // [00 reg 100][ss index 101] disp32 + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x04 | regenc << 3); + emit_byte(scale << 6 | indexenc << 3 | 0x05); + emit_data(disp, rspec, disp32_operand); +#ifdef _LP64 + } else if (rtype != relocInfo::none ) { + // [disp] RIP-RELATIVE + // [00 000 101] disp32 + + emit_byte(0x05 | regenc << 3); + // Note that the RIP-rel. correction applies to the generated + // disp field, but _not_ to the target address in the rspec. + + // disp was created by converting the target address minus the pc + // at the start of the instruction. That needs more correction here. + // intptr_t disp = target - next_ip; + assert(inst_mark() != NULL, "must be inside InstructionMark"); + address next_ip = pc() + sizeof(int32_t) + rip_relative_correction; + int64_t adjusted = (int64_t) disp - (next_ip - inst_mark()); + assert(is_simm32(adjusted), + "must be 32bit offset (RIP relative address)"); + emit_data((int) adjusted, rspec, disp32_operand); + +#endif // _LP64 + } else { + // [disp] ABSOLUTE + // [00 reg 100][00 100 101] disp32 + emit_byte(0x04 | regenc << 3); + emit_byte(0x25); + emit_data(disp, rspec, disp32_operand); + } + } +} + +void Assembler::emit_operand(XMMRegister reg, Register base, Register index, + Address::ScaleFactor scale, int disp, + RelocationHolder const& rspec, + int rip_relative_correction) { + relocInfo::relocType rtype = (relocInfo::relocType) rspec.type(); + int regenc = reg->encoding(); + if (regenc >= 8) { + regenc -= 8; + } + if (base->is_valid()) { + if (index->is_valid()) { + assert(scale != Address::no_scale, "inconsistent address"); + int indexenc = index->encoding(); + if (indexenc >= 8) { + indexenc -= 8; + } + int baseenc = base->encoding(); + if (baseenc >= 8) { + baseenc -= 8; + } + // [base + index*scale + disp] + if (disp == 0 && rtype == relocInfo::none && + base != rbp && base != r13) { + // [base + index*scale] + // [00 reg 100][ss index base] + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x04 | regenc << 3); + emit_byte(scale << 6 | indexenc << 3 | baseenc); + } else if (is8bit(disp) && rtype == relocInfo::none) { + // [base + index*scale + disp8] + // [01 reg 100][ss index base] disp8 + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x44 | regenc << 3); + emit_byte(scale << 6 | indexenc << 3 | baseenc); + emit_byte(disp & 0xFF); + } else { + // [base + index*scale + disp32] + // [10 reg 100][ss index base] disp32 + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x84 | regenc << 3); + emit_byte(scale << 6 | indexenc << 3 | baseenc); + emit_data(disp, rspec, disp32_operand); + } + } else if (base == rsp || base == r12) { + // [rsp + disp] + if (disp == 0 && rtype == relocInfo::none) { + // [rsp] + // [00 reg 100][00 100 100] + emit_byte(0x04 | regenc << 3); + emit_byte(0x24); + } else if (is8bit(disp) && rtype == relocInfo::none) { + // [rsp + imm8] + // [01 reg 100][00 100 100] disp8 + emit_byte(0x44 | regenc << 3); + emit_byte(0x24); + emit_byte(disp & 0xFF); + } else { + // [rsp + imm32] + // [10 reg 100][00 100 100] disp32 + emit_byte(0x84 | regenc << 3); + emit_byte(0x24); + emit_data(disp, rspec, disp32_operand); + } + } else { + // [base + disp] + assert(base != rsp && base != r12, "illegal addressing mode"); + int baseenc = base->encoding(); + if (baseenc >= 8) { + baseenc -= 8; + } + if (disp == 0 && rtype == relocInfo::none && + base != rbp && base != r13) { + // [base] + // [00 reg base] + emit_byte(0x00 | regenc << 3 | baseenc); + } else if (is8bit(disp) && rtype == relocInfo::none) { + // [base + imm8] + // [01 reg base] disp8 + emit_byte(0x40 | regenc << 3 | baseenc); + emit_byte(disp & 0xFF); + } else { + // [base + imm32] + // [10 reg base] disp32 + emit_byte(0x80 | regenc << 3 | baseenc); + emit_data(disp, rspec, disp32_operand); + } + } + } else { + if (index->is_valid()) { + assert(scale != Address::no_scale, "inconsistent address"); + int indexenc = index->encoding(); + if (indexenc >= 8) { + indexenc -= 8; + } + // [index*scale + disp] + // [00 reg 100][ss index 101] disp32 + assert(index != rsp, "illegal addressing mode"); + emit_byte(0x04 | regenc << 3); + emit_byte(scale << 6 | indexenc << 3 | 0x05); + emit_data(disp, rspec, disp32_operand); +#ifdef _LP64 + } else if ( rtype != relocInfo::none ) { + // [disp] RIP-RELATIVE + // [00 reg 101] disp32 + emit_byte(0x05 | regenc << 3); + // Note that the RIP-rel. correction applies to the generated + // disp field, but _not_ to the target address in the rspec. + + // disp was created by converting the target address minus the pc + // at the start of the instruction. That needs more correction here. + // intptr_t disp = target - next_ip; + + assert(inst_mark() != NULL, "must be inside InstructionMark"); + address next_ip = pc() + sizeof(int32_t) + rip_relative_correction; + + int64_t adjusted = (int64_t) disp - (next_ip - inst_mark()); + assert(is_simm32(adjusted), + "must be 32bit offset (RIP relative address)"); + emit_data((int) adjusted, rspec, disp32_operand); +#endif // _LP64 + } else { + // [disp] ABSOLUTE + // [00 reg 100][00 100 101] disp32 + emit_byte(0x04 | regenc << 3); + emit_byte(0x25); + emit_data(disp, rspec, disp32_operand); + } + } +} + +// Secret local extension to Assembler::WhichOperand: +#define end_pc_operand (_WhichOperand_limit) + +address Assembler::locate_operand(address inst, WhichOperand which) { + // Decode the given instruction, and return the address of + // an embedded 32-bit operand word. + + // If "which" is disp32_operand, selects the displacement portion + // of an effective address specifier. + // If "which" is imm64_operand, selects the trailing immediate constant. + // If "which" is call32_operand, selects the displacement of a call or jump. + // Caller is responsible for ensuring that there is such an operand, + // and that it is 32/64 bits wide. + + // If "which" is end_pc_operand, find the end of the instruction. + + address ip = inst; + bool is_64bit = false; + + debug_only(bool has_disp32 = false); + int tail_size = 0; // other random bytes (#32, #16, etc.) at end of insn + + again_after_prefix: + switch (0xFF & *ip++) { + + // These convenience macros generate groups of "case" labels for the switch. +#define REP4(x) (x)+0: case (x)+1: case (x)+2: case (x)+3 +#define REP8(x) (x)+0: case (x)+1: case (x)+2: case (x)+3: \ + case (x)+4: case (x)+5: case (x)+6: case (x)+7 +#define REP16(x) REP8((x)+0): \ + case REP8((x)+8) + + case CS_segment: + case SS_segment: + case DS_segment: + case ES_segment: + case FS_segment: + case GS_segment: + assert(0, "shouldn't have that prefix"); + assert(ip == inst + 1 || ip == inst + 2, "only two prefixes allowed"); + goto again_after_prefix; + + case 0x67: + case REX: + case REX_B: + case REX_X: + case REX_XB: + case REX_R: + case REX_RB: + case REX_RX: + case REX_RXB: +// assert(ip == inst + 1, "only one prefix allowed"); + goto again_after_prefix; + + case REX_W: + case REX_WB: + case REX_WX: + case REX_WXB: + case REX_WR: + case REX_WRB: + case REX_WRX: + case REX_WRXB: + is_64bit = true; +// assert(ip == inst + 1, "only one prefix allowed"); + goto again_after_prefix; + + case 0xFF: // pushq a; decl a; incl a; call a; jmp a + case 0x88: // movb a, r + case 0x89: // movl a, r + case 0x8A: // movb r, a + case 0x8B: // movl r, a + case 0x8F: // popl a + debug_only(has_disp32 = true); + break; + + case 0x68: // pushq #32 + if (which == end_pc_operand) { + return ip + 4; + } + assert(0, "pushq has no disp32 or imm64"); + ShouldNotReachHere(); + + case 0x66: // movw ... (size prefix) + again_after_size_prefix2: + switch (0xFF & *ip++) { + case REX: + case REX_B: + case REX_X: + case REX_XB: + case REX_R: + case REX_RB: + case REX_RX: + case REX_RXB: + case REX_W: + case REX_WB: + case REX_WX: + case REX_WXB: + case REX_WR: + case REX_WRB: + case REX_WRX: + case REX_WRXB: + goto again_after_size_prefix2; + case 0x8B: // movw r, a + case 0x89: // movw a, r + break; + case 0xC7: // movw a, #16 + tail_size = 2; // the imm16 + break; + case 0x0F: // several SSE/SSE2 variants + ip--; // reparse the 0x0F + goto again_after_prefix; + default: + ShouldNotReachHere(); + } + break; + + case REP8(0xB8): // movl/q r, #32/#64(oop?) + if (which == end_pc_operand) return ip + (is_64bit ? 8 : 4); + assert((which == call32_operand || which == imm64_operand) && is_64bit, ""); + return ip; + + case 0x69: // imul r, a, #32 + case 0xC7: // movl a, #32(oop?) + tail_size = 4; + debug_only(has_disp32 = true); // has both kinds of operands! + break; + + case 0x0F: // movx..., etc. + switch (0xFF & *ip++) { + case 0x12: // movlps + case 0x28: // movaps + case 0x2E: // ucomiss + case 0x2F: // comiss + case 0x54: // andps + case 0x57: // xorps + case 0x6E: // movd + case 0x7E: // movd + case 0xAE: // ldmxcsr a + debug_only(has_disp32 = true); // has both kinds of operands! + break; + case 0xAD: // shrd r, a, %cl + case 0xAF: // imul r, a + case 0xBE: // movsbl r, a + case 0xBF: // movswl r, a + case 0xB6: // movzbl r, a + case 0xB7: // movzwl r, a + case REP16(0x40): // cmovl cc, r, a + case 0xB0: // cmpxchgb + case 0xB1: // cmpxchg + case 0xC1: // xaddl + case 0xC7: // cmpxchg8 + case REP16(0x90): // setcc a + debug_only(has_disp32 = true); + // fall out of the switch to decode the address + break; + case 0xAC: // shrd r, a, #8 + debug_only(has_disp32 = true); + tail_size = 1; // the imm8 + break; + case REP16(0x80): // jcc rdisp32 + if (which == end_pc_operand) return ip + 4; + assert(which == call32_operand, "jcc has no disp32 or imm64"); + return ip; + default: + ShouldNotReachHere(); + } + break; + + case 0x81: // addl a, #32; addl r, #32 + // also: orl, adcl, sbbl, andl, subl, xorl, cmpl + tail_size = 4; + debug_only(has_disp32 = true); // has both kinds of operands! + break; + + case 0x83: // addl a, #8; addl r, #8 + // also: orl, adcl, sbbl, andl, subl, xorl, cmpl + debug_only(has_disp32 = true); // has both kinds of operands! + tail_size = 1; + break; + + case 0x9B: + switch (0xFF & *ip++) { + case 0xD9: // fnstcw a + debug_only(has_disp32 = true); + break; + default: + ShouldNotReachHere(); + } + break; + + case REP4(0x00): // addb a, r; addl a, r; addb r, a; addl r, a + case REP4(0x10): // adc... + case REP4(0x20): // and... + case REP4(0x30): // xor... + case REP4(0x08): // or... + case REP4(0x18): // sbb... + case REP4(0x28): // sub... + case 0xF7: // mull a + case 0x87: // xchg r, a + debug_only(has_disp32 = true); + break; + case REP4(0x38): // cmp... + case 0x8D: // lea r, a + case 0x85: // test r, a + debug_only(has_disp32 = true); // has both kinds of operands! + break; + + case 0xC1: // sal a, #8; sar a, #8; shl a, #8; shr a, #8 + case 0xC6: // movb a, #8 + case 0x80: // cmpb a, #8 + case 0x6B: // imul r, a, #8 + debug_only(has_disp32 = true); // has both kinds of operands! + tail_size = 1; // the imm8 + break; + + case 0xE8: // call rdisp32 + case 0xE9: // jmp rdisp32 + if (which == end_pc_operand) return ip + 4; + assert(which == call32_operand, "call has no disp32 or imm32"); + return ip; + + case 0xD1: // sal a, 1; sar a, 1; shl a, 1; shr a, 1 + case 0xD3: // sal a, %cl; sar a, %cl; shl a, %cl; shr a, %cl + case 0xD9: // fld_s a; fst_s a; fstp_s a; fldcw a + case 0xDD: // fld_d a; fst_d a; fstp_d a + case 0xDB: // fild_s a; fistp_s a; fld_x a; fstp_x a + case 0xDF: // fild_d a; fistp_d a + case 0xD8: // fadd_s a; fsubr_s a; fmul_s a; fdivr_s a; fcomp_s a + case 0xDC: // fadd_d a; fsubr_d a; fmul_d a; fdivr_d a; fcomp_d a + case 0xDE: // faddp_d a; fsubrp_d a; fmulp_d a; fdivrp_d a; fcompp_d a + debug_only(has_disp32 = true); + break; + + case 0xF3: // For SSE + case 0xF2: // For SSE2 + switch (0xFF & *ip++) { + case REX: + case REX_B: + case REX_X: + case REX_XB: + case REX_R: + case REX_RB: + case REX_RX: + case REX_RXB: + case REX_W: + case REX_WB: + case REX_WX: + case REX_WXB: + case REX_WR: + case REX_WRB: + case REX_WRX: + case REX_WRXB: + ip++; + default: + ip++; + } + debug_only(has_disp32 = true); // has both kinds of operands! + break; + + default: + ShouldNotReachHere(); + +#undef REP8 +#undef REP16 + } + + assert(which != call32_operand, "instruction is not a call, jmp, or jcc"); + assert(which != imm64_operand, "instruction is not a movq reg, imm64"); + assert(which != disp32_operand || has_disp32, "instruction has no disp32 field"); + + // parse the output of emit_operand + int op2 = 0xFF & *ip++; + int base = op2 & 0x07; + int op3 = -1; + const int b100 = 4; + const int b101 = 5; + if (base == b100 && (op2 >> 6) != 3) { + op3 = 0xFF & *ip++; + base = op3 & 0x07; // refetch the base + } + // now ip points at the disp (if any) + + switch (op2 >> 6) { + case 0: + // [00 reg 100][ss index base] + // [00 reg 100][00 100 esp] + // [00 reg base] + // [00 reg 100][ss index 101][disp32] + // [00 reg 101] [disp32] + + if (base == b101) { + if (which == disp32_operand) + return ip; // caller wants the disp32 + ip += 4; // skip the disp32 + } + break; + + case 1: + // [01 reg 100][ss index base][disp8] + // [01 reg 100][00 100 esp][disp8] + // [01 reg base] [disp8] + ip += 1; // skip the disp8 + break; + + case 2: + // [10 reg 100][ss index base][disp32] + // [10 reg 100][00 100 esp][disp32] + // [10 reg base] [disp32] + if (which == disp32_operand) + return ip; // caller wants the disp32 + ip += 4; // skip the disp32 + break; + + case 3: + // [11 reg base] (not a memory addressing mode) + break; + } + + if (which == end_pc_operand) { + return ip + tail_size; + } + + assert(0, "fix locate_operand"); + return ip; +} + +address Assembler::locate_next_instruction(address inst) { + // Secretly share code with locate_operand: + return locate_operand(inst, end_pc_operand); +} + +#ifdef ASSERT +void Assembler::check_relocation(RelocationHolder const& rspec, int format) { + address inst = inst_mark(); + assert(inst != NULL && inst < pc(), + "must point to beginning of instruction"); + address opnd; + + Relocation* r = rspec.reloc(); + if (r->type() == relocInfo::none) { + return; + } else if (r->is_call() || format == call32_operand) { + opnd = locate_operand(inst, call32_operand); + } else if (r->is_data()) { + assert(format == imm64_operand || format == disp32_operand, "format ok"); + opnd = locate_operand(inst, (WhichOperand) format); + } else { + assert(format == 0, "cannot specify a format"); + return; + } + assert(opnd == pc(), "must put operand where relocs can find it"); +} +#endif + +int Assembler::prefix_and_encode(int reg_enc, bool byteinst) { + if (reg_enc >= 8) { + prefix(REX_B); + reg_enc -= 8; + } else if (byteinst && reg_enc >= 4) { + prefix(REX); + } + return reg_enc; +} + +int Assembler::prefixq_and_encode(int reg_enc) { + if (reg_enc < 8) { + prefix(REX_W); + } else { + prefix(REX_WB); + reg_enc -= 8; + } + return reg_enc; +} + +int Assembler::prefix_and_encode(int dst_enc, int src_enc, bool byteinst) { + if (dst_enc < 8) { + if (src_enc >= 8) { + prefix(REX_B); + src_enc -= 8; + } else if (byteinst && src_enc >= 4) { + prefix(REX); + } + } else { + if (src_enc < 8) { + prefix(REX_R); + } else { + prefix(REX_RB); + src_enc -= 8; + } + dst_enc -= 8; + } + return dst_enc << 3 | src_enc; +} + +int Assembler::prefixq_and_encode(int dst_enc, int src_enc) { + if (dst_enc < 8) { + if (src_enc < 8) { + prefix(REX_W); + } else { + prefix(REX_WB); + src_enc -= 8; + } + } else { + if (src_enc < 8) { + prefix(REX_WR); + } else { + prefix(REX_WRB); + src_enc -= 8; + } + dst_enc -= 8; + } + return dst_enc << 3 | src_enc; +} + +void Assembler::prefix(Register reg) { + if (reg->encoding() >= 8) { + prefix(REX_B); + } +} + +void Assembler::prefix(Address adr) { + if (adr.base_needs_rex()) { + if (adr.index_needs_rex()) { + prefix(REX_XB); + } else { + prefix(REX_B); + } + } else { + if (adr.index_needs_rex()) { + prefix(REX_X); + } + } +} + +void Assembler::prefixq(Address adr) { + if (adr.base_needs_rex()) { + if (adr.index_needs_rex()) { + prefix(REX_WXB); + } else { + prefix(REX_WB); + } + } else { + if (adr.index_needs_rex()) { + prefix(REX_WX); + } else { + prefix(REX_W); + } + } +} + + +void Assembler::prefix(Address adr, Register reg, bool byteinst) { + if (reg->encoding() < 8) { + if (adr.base_needs_rex()) { + if (adr.index_needs_rex()) { + prefix(REX_XB); + } else { + prefix(REX_B); + } + } else { + if (adr.index_needs_rex()) { + prefix(REX_X); + } else if (reg->encoding() >= 4 ) { + prefix(REX); + } + } + } else { + if (adr.base_needs_rex()) { + if (adr.index_needs_rex()) { + prefix(REX_RXB); + } else { + prefix(REX_RB); + } + } else { + if (adr.index_needs_rex()) { + prefix(REX_RX); + } else { + prefix(REX_R); + } + } + } +} + +void Assembler::prefixq(Address adr, Register src) { + if (src->encoding() < 8) { + if (adr.base_needs_rex()) { + if (adr.index_needs_rex()) { + prefix(REX_WXB); + } else { + prefix(REX_WB); + } + } else { + if (adr.index_needs_rex()) { + prefix(REX_WX); + } else { + prefix(REX_W); + } + } + } else { + if (adr.base_needs_rex()) { + if (adr.index_needs_rex()) { + prefix(REX_WRXB); + } else { + prefix(REX_WRB); + } + } else { + if (adr.index_needs_rex()) { + prefix(REX_WRX); + } else { + prefix(REX_WR); + } + } + } +} + +void Assembler::prefix(Address adr, XMMRegister reg) { + if (reg->encoding() < 8) { + if (adr.base_needs_rex()) { + if (adr.index_needs_rex()) { + prefix(REX_XB); + } else { + prefix(REX_B); + } + } else { + if (adr.index_needs_rex()) { + prefix(REX_X); + } + } + } else { + if (adr.base_needs_rex()) { + if (adr.index_needs_rex()) { + prefix(REX_RXB); + } else { + prefix(REX_RB); + } + } else { + if (adr.index_needs_rex()) { + prefix(REX_RX); + } else { + prefix(REX_R); + } + } + } +} + +void Assembler::emit_operand(Register reg, Address adr, + int rip_relative_correction) { + emit_operand(reg, adr._base, adr._index, adr._scale, adr._disp, + adr._rspec, + rip_relative_correction); +} + +void Assembler::emit_operand(XMMRegister reg, Address adr, + int rip_relative_correction) { + emit_operand(reg, adr._base, adr._index, adr._scale, adr._disp, + adr._rspec, + rip_relative_correction); +} + +void Assembler::emit_farith(int b1, int b2, int i) { + assert(isByte(b1) && isByte(b2), "wrong opcode"); + assert(0 <= i && i < 8, "illegal stack offset"); + emit_byte(b1); + emit_byte(b2 + i); +} + +// pushad is invalid, use this instead. +// NOTE: Kills flags!! +void Assembler::pushaq() { + // we have to store original rsp. ABI says that 128 bytes + // below rsp are local scratch. + movq(Address(rsp, -5 * wordSize), rsp); + + subq(rsp, 16 * wordSize); + + movq(Address(rsp, 15 * wordSize), rax); + movq(Address(rsp, 14 * wordSize), rcx); + movq(Address(rsp, 13 * wordSize), rdx); + movq(Address(rsp, 12 * wordSize), rbx); + // skip rsp + movq(Address(rsp, 10 * wordSize), rbp); + movq(Address(rsp, 9 * wordSize), rsi); + movq(Address(rsp, 8 * wordSize), rdi); + movq(Address(rsp, 7 * wordSize), r8); + movq(Address(rsp, 6 * wordSize), r9); + movq(Address(rsp, 5 * wordSize), r10); + movq(Address(rsp, 4 * wordSize), r11); + movq(Address(rsp, 3 * wordSize), r12); + movq(Address(rsp, 2 * wordSize), r13); + movq(Address(rsp, wordSize), r14); + movq(Address(rsp, 0), r15); +} + +// popad is invalid, use this instead +// NOTE: Kills flags!! +void Assembler::popaq() { + movq(r15, Address(rsp, 0)); + movq(r14, Address(rsp, wordSize)); + movq(r13, Address(rsp, 2 * wordSize)); + movq(r12, Address(rsp, 3 * wordSize)); + movq(r11, Address(rsp, 4 * wordSize)); + movq(r10, Address(rsp, 5 * wordSize)); + movq(r9, Address(rsp, 6 * wordSize)); + movq(r8, Address(rsp, 7 * wordSize)); + movq(rdi, Address(rsp, 8 * wordSize)); + movq(rsi, Address(rsp, 9 * wordSize)); + movq(rbp, Address(rsp, 10 * wordSize)); + // skip rsp + movq(rbx, Address(rsp, 12 * wordSize)); + movq(rdx, Address(rsp, 13 * wordSize)); + movq(rcx, Address(rsp, 14 * wordSize)); + movq(rax, Address(rsp, 15 * wordSize)); + + addq(rsp, 16 * wordSize); +} + +void Assembler::pushfq() { + emit_byte(0x9C); +} + +void Assembler::popfq() { + emit_byte(0x9D); +} + +void Assembler::pushq(int imm32) { + emit_byte(0x68); + emit_long(imm32); +} + +void Assembler::pushq(Register src) { + int encode = prefix_and_encode(src->encoding()); + + emit_byte(0x50 | encode); +} + +void Assembler::pushq(Address src) { + InstructionMark im(this); + prefix(src); + emit_byte(0xFF); + emit_operand(rsi, src); +} + +void Assembler::popq(Register dst) { + int encode = prefix_and_encode(dst->encoding()); + emit_byte(0x58 | encode); +} + +void Assembler::popq(Address dst) { + InstructionMark im(this); + prefix(dst); + emit_byte(0x8F); + emit_operand(rax, dst); +} + +void Assembler::prefix(Prefix p) { + a_byte(p); +} + +void Assembler::movb(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst, true); + emit_byte(0x8A); + emit_operand(dst, src); +} + +void Assembler::movb(Address dst, int imm8) { + InstructionMark im(this); + prefix(dst); + emit_byte(0xC6); + emit_operand(rax, dst, 1); + emit_byte(imm8); +} + +void Assembler::movb(Address dst, Register src) { + InstructionMark im(this); + prefix(dst, src, true); + emit_byte(0x88); + emit_operand(src, dst); +} + +void Assembler::movw(Address dst, int imm16) { + InstructionMark im(this); + emit_byte(0x66); // switch to 16-bit mode + prefix(dst); + emit_byte(0xC7); + emit_operand(rax, dst, 2); + emit_word(imm16); +} + +void Assembler::movw(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x66); + prefix(src, dst); + emit_byte(0x8B); + emit_operand(dst, src); +} + +void Assembler::movw(Address dst, Register src) { + InstructionMark im(this); + emit_byte(0x66); + prefix(dst, src); + emit_byte(0x89); + emit_operand(src, dst); +} + +// Uses zero extension. +void Assembler::movl(Register dst, int imm32) { + int encode = prefix_and_encode(dst->encoding()); + emit_byte(0xB8 | encode); + emit_long(imm32); +} + +void Assembler::movl(Register dst, Register src) { + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x8B); + emit_byte(0xC0 | encode); +} + +void Assembler::movl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x8B); + emit_operand(dst, src); +} + +void Assembler::movl(Address dst, int imm32) { + InstructionMark im(this); + prefix(dst); + emit_byte(0xC7); + emit_operand(rax, dst, 4); + emit_long(imm32); +} + +void Assembler::movl(Address dst, Register src) { + InstructionMark im(this); + prefix(dst, src); + emit_byte(0x89); + emit_operand(src, dst); +} + +void Assembler::mov64(Register dst, int64_t imm64) { + InstructionMark im(this); + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xB8 | encode); + emit_long64(imm64); +} + +void Assembler::mov_literal64(Register dst, intptr_t imm64, RelocationHolder const& rspec) { + InstructionMark im(this); + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xB8 | encode); + emit_data64(imm64, rspec); +} + +void Assembler::movq(Register dst, Register src) { + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x8B); + emit_byte(0xC0 | encode); +} + +void Assembler::movq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x8B); + emit_operand(dst, src); +} + +void Assembler::mov64(Address dst, int64_t imm32) { + assert(is_simm32(imm32), "lost bits"); + InstructionMark im(this); + prefixq(dst); + emit_byte(0xC7); + emit_operand(rax, dst, 4); + emit_long(imm32); +} + +void Assembler::movq(Address dst, Register src) { + InstructionMark im(this); + prefixq(dst, src); + emit_byte(0x89); + emit_operand(src, dst); +} + +void Assembler::movsbl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0xBE); + emit_operand(dst, src); +} + +void Assembler::movsbl(Register dst, Register src) { + int encode = prefix_and_encode(dst->encoding(), src->encoding(), true); + emit_byte(0x0F); + emit_byte(0xBE); + emit_byte(0xC0 | encode); +} + +void Assembler::movswl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0xBF); + emit_operand(dst, src); +} + +void Assembler::movswl(Register dst, Register src) { + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0xBF); + emit_byte(0xC0 | encode); +} + +void Assembler::movslq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x63); + emit_operand(dst, src); +} + +void Assembler::movslq(Register dst, Register src) { + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x63); + emit_byte(0xC0 | encode); +} + +void Assembler::movzbl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0xB6); + emit_operand(dst, src); +} + +void Assembler::movzbl(Register dst, Register src) { + int encode = prefix_and_encode(dst->encoding(), src->encoding(), true); + emit_byte(0x0F); + emit_byte(0xB6); + emit_byte(0xC0 | encode); +} + +void Assembler::movzwl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0xB7); + emit_operand(dst, src); +} + +void Assembler::movzwl(Register dst, Register src) { + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0xB7); + emit_byte(0xC0 | encode); +} + +void Assembler::movss(XMMRegister dst, XMMRegister src) { + emit_byte(0xF3); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x10); + emit_byte(0xC0 | encode); +} + +void Assembler::movss(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF3); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x10); + emit_operand(dst, src); +} + +void Assembler::movss(Address dst, XMMRegister src) { + InstructionMark im(this); + emit_byte(0xF3); + prefix(dst, src); + emit_byte(0x0F); + emit_byte(0x11); + emit_operand(src, dst); +} + +void Assembler::movsd(XMMRegister dst, XMMRegister src) { + emit_byte(0xF2); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x10); + emit_byte(0xC0 | encode); +} + +void Assembler::movsd(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF2); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x10); + emit_operand(dst, src); +} + +void Assembler::movsd(Address dst, XMMRegister src) { + InstructionMark im(this); + emit_byte(0xF2); + prefix(dst, src); + emit_byte(0x0F); + emit_byte(0x11); + emit_operand(src, dst); +} + +// New cpus require to use movsd and movss to avoid partial register stall +// when loading from memory. But for old Opteron use movlpd instead of movsd. +// The selection is done in MacroAssembler::movdbl() and movflt(). +void Assembler::movlpd(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0x66); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x12); + emit_operand(dst, src); +} + +void Assembler::movapd(XMMRegister dst, XMMRegister src) { + int dstenc = dst->encoding(); + int srcenc = src->encoding(); + emit_byte(0x66); + if (dstenc < 8) { + if (srcenc >= 8) { + prefix(REX_B); + srcenc -= 8; + } + } else { + if (srcenc < 8) { + prefix(REX_R); + } else { + prefix(REX_RB); + srcenc -= 8; + } + dstenc -= 8; + } + emit_byte(0x0F); + emit_byte(0x28); + emit_byte(0xC0 | dstenc << 3 | srcenc); +} + +void Assembler::movaps(XMMRegister dst, XMMRegister src) { + int dstenc = dst->encoding(); + int srcenc = src->encoding(); + if (dstenc < 8) { + if (srcenc >= 8) { + prefix(REX_B); + srcenc -= 8; + } + } else { + if (srcenc < 8) { + prefix(REX_R); + } else { + prefix(REX_RB); + srcenc -= 8; + } + dstenc -= 8; + } + emit_byte(0x0F); + emit_byte(0x28); + emit_byte(0xC0 | dstenc << 3 | srcenc); +} + +void Assembler::movdl(XMMRegister dst, Register src) { + emit_byte(0x66); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x6E); + emit_byte(0xC0 | encode); +} + +void Assembler::movdl(Register dst, XMMRegister src) { + emit_byte(0x66); + // swap src/dst to get correct prefix + int encode = prefix_and_encode(src->encoding(), dst->encoding()); + emit_byte(0x0F); + emit_byte(0x7E); + emit_byte(0xC0 | encode); +} + +void Assembler::movdq(XMMRegister dst, Register src) { + emit_byte(0x66); + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x6E); + emit_byte(0xC0 | encode); +} + +void Assembler::movdq(Register dst, XMMRegister src) { + emit_byte(0x66); + // swap src/dst to get correct prefix + int encode = prefixq_and_encode(src->encoding(), dst->encoding()); + emit_byte(0x0F); + emit_byte(0x7E); + emit_byte(0xC0 | encode); +} + +void Assembler::pxor(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0x66); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0xEF); + emit_operand(dst, src); +} + +void Assembler::pxor(XMMRegister dst, XMMRegister src) { + InstructionMark im(this); + emit_byte(0x66); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0xEF); + emit_byte(0xC0 | encode); +} + +void Assembler::movdqa(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0x66); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x6F); + emit_operand(dst, src); +} + +void Assembler::movdqa(XMMRegister dst, XMMRegister src) { + emit_byte(0x66); + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x6F); + emit_byte(0xC0 | encode); +} + +void Assembler::movdqa(Address dst, XMMRegister src) { + InstructionMark im(this); + emit_byte(0x66); + prefix(dst, src); + emit_byte(0x0F); + emit_byte(0x7F); + emit_operand(src, dst); +} + +void Assembler::movq(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF3); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x7E); + emit_operand(dst, src); +} + +void Assembler::movq(Address dst, XMMRegister src) { + InstructionMark im(this); + emit_byte(0x66); + prefix(dst, src); + emit_byte(0x0F); + emit_byte(0xD6); + emit_operand(src, dst); +} + +void Assembler::pshufd(XMMRegister dst, XMMRegister src, int mode) { + assert(isByte(mode), "invalid value"); + emit_byte(0x66); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x70); + emit_byte(0xC0 | encode); + emit_byte(mode & 0xFF); +} + +void Assembler::pshufd(XMMRegister dst, Address src, int mode) { + assert(isByte(mode), "invalid value"); + InstructionMark im(this); + emit_byte(0x66); + emit_byte(0x0F); + emit_byte(0x70); + emit_operand(dst, src); + emit_byte(mode & 0xFF); +} + +void Assembler::pshuflw(XMMRegister dst, XMMRegister src, int mode) { + assert(isByte(mode), "invalid value"); + emit_byte(0xF2); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x70); + emit_byte(0xC0 | encode); + emit_byte(mode & 0xFF); +} + +void Assembler::pshuflw(XMMRegister dst, Address src, int mode) { + assert(isByte(mode), "invalid value"); + InstructionMark im(this); + emit_byte(0xF2); + emit_byte(0x0F); + emit_byte(0x70); + emit_operand(dst, src); + emit_byte(mode & 0xFF); +} + +void Assembler::cmovl(Condition cc, Register dst, Register src) { + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x40 | cc); + emit_byte(0xC0 | encode); +} + +void Assembler::cmovl(Condition cc, Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x40 | cc); + emit_operand(dst, src); +} + +void Assembler::cmovq(Condition cc, Register dst, Register src) { + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x40 | cc); + emit_byte(0xC0 | encode); +} + +void Assembler::cmovq(Condition cc, Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x0F); + emit_byte(0x40 | cc); + emit_operand(dst, src); +} + +void Assembler::prefetch_prefix(Address src) { + prefix(src); + emit_byte(0x0F); +} + +void Assembler::prefetcht0(Address src) { + InstructionMark im(this); + prefetch_prefix(src); + emit_byte(0x18); + emit_operand(rcx, src); // 1, src +} + +void Assembler::prefetcht1(Address src) { + InstructionMark im(this); + prefetch_prefix(src); + emit_byte(0x18); + emit_operand(rdx, src); // 2, src +} + +void Assembler::prefetcht2(Address src) { + InstructionMark im(this); + prefetch_prefix(src); + emit_byte(0x18); + emit_operand(rbx, src); // 3, src +} + +void Assembler::prefetchnta(Address src) { + InstructionMark im(this); + prefetch_prefix(src); + emit_byte(0x18); + emit_operand(rax, src); // 0, src +} + +void Assembler::prefetchw(Address src) { + InstructionMark im(this); + prefetch_prefix(src); + emit_byte(0x0D); + emit_operand(rcx, src); // 1, src +} + +void Assembler::adcl(Register dst, int imm32) { + prefix(dst); + emit_arith(0x81, 0xD0, dst, imm32); +} + +void Assembler::adcl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x13); + emit_operand(dst, src); +} + +void Assembler::adcl(Register dst, Register src) { + (void) prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x13, 0xC0, dst, src); +} + +void Assembler::adcq(Register dst, int imm32) { + (void) prefixq_and_encode(dst->encoding()); + emit_arith(0x81, 0xD0, dst, imm32); +} + +void Assembler::adcq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x13); + emit_operand(dst, src); +} + +void Assembler::adcq(Register dst, Register src) { + (int) prefixq_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x13, 0xC0, dst, src); +} + +void Assembler::addl(Address dst, int imm32) { + InstructionMark im(this); + prefix(dst); + emit_arith_operand(0x81, rax, dst,imm32); +} + +void Assembler::addl(Address dst, Register src) { + InstructionMark im(this); + prefix(dst, src); + emit_byte(0x01); + emit_operand(src, dst); +} + +void Assembler::addl(Register dst, int imm32) { + prefix(dst); + emit_arith(0x81, 0xC0, dst, imm32); +} + +void Assembler::addl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x03); + emit_operand(dst, src); +} + +void Assembler::addl(Register dst, Register src) { + (void) prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x03, 0xC0, dst, src); +} + +void Assembler::addq(Address dst, int imm32) { + InstructionMark im(this); + prefixq(dst); + emit_arith_operand(0x81, rax, dst,imm32); +} + +void Assembler::addq(Address dst, Register src) { + InstructionMark im(this); + prefixq(dst, src); + emit_byte(0x01); + emit_operand(src, dst); +} + +void Assembler::addq(Register dst, int imm32) { + (void) prefixq_and_encode(dst->encoding()); + emit_arith(0x81, 0xC0, dst, imm32); +} + +void Assembler::addq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x03); + emit_operand(dst, src); +} + +void Assembler::addq(Register dst, Register src) { + (void) prefixq_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x03, 0xC0, dst, src); +} + +void Assembler::andl(Register dst, int imm32) { + prefix(dst); + emit_arith(0x81, 0xE0, dst, imm32); +} + +void Assembler::andl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x23); + emit_operand(dst, src); +} + +void Assembler::andl(Register dst, Register src) { + (void) prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x23, 0xC0, dst, src); +} + +void Assembler::andq(Register dst, int imm32) { + (void) prefixq_and_encode(dst->encoding()); + emit_arith(0x81, 0xE0, dst, imm32); +} + +void Assembler::andq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x23); + emit_operand(dst, src); +} + +void Assembler::andq(Register dst, Register src) { + (int) prefixq_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x23, 0xC0, dst, src); +} + +void Assembler::cmpb(Address dst, int imm8) { + InstructionMark im(this); + prefix(dst); + emit_byte(0x80); + emit_operand(rdi, dst, 1); + emit_byte(imm8); +} + +void Assembler::cmpl(Address dst, int imm32) { + InstructionMark im(this); + prefix(dst); + emit_byte(0x81); + emit_operand(rdi, dst, 4); + emit_long(imm32); +} + +void Assembler::cmpl(Register dst, int imm32) { + prefix(dst); + emit_arith(0x81, 0xF8, dst, imm32); +} + +void Assembler::cmpl(Register dst, Register src) { + (void) prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x3B, 0xC0, dst, src); +} + +void Assembler::cmpl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x3B); + emit_operand(dst, src); +} + +void Assembler::cmpq(Address dst, int imm32) { + InstructionMark im(this); + prefixq(dst); + emit_byte(0x81); + emit_operand(rdi, dst, 4); + emit_long(imm32); +} + +void Assembler::cmpq(Register dst, int imm32) { + (void) prefixq_and_encode(dst->encoding()); + emit_arith(0x81, 0xF8, dst, imm32); +} + +void Assembler::cmpq(Address dst, Register src) { + prefixq(dst, src); + emit_byte(0x3B); + emit_operand(src, dst); +} + +void Assembler::cmpq(Register dst, Register src) { + (void) prefixq_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x3B, 0xC0, dst, src); +} + +void Assembler::cmpq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x3B); + emit_operand(dst, src); +} + +void Assembler::ucomiss(XMMRegister dst, XMMRegister src) { + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x2E); + emit_byte(0xC0 | encode); +} + +void Assembler::ucomisd(XMMRegister dst, XMMRegister src) { + emit_byte(0x66); + ucomiss(dst, src); +} + +void Assembler::decl(Register dst) { + // Don't use it directly. Use MacroAssembler::decrementl() instead. + // Use two-byte form (one-byte from is a REX prefix in 64-bit mode) + int encode = prefix_and_encode(dst->encoding()); + emit_byte(0xFF); + emit_byte(0xC8 | encode); +} + +void Assembler::decl(Address dst) { + // Don't use it directly. Use MacroAssembler::decrementl() instead. + InstructionMark im(this); + prefix(dst); + emit_byte(0xFF); + emit_operand(rcx, dst); +} + +void Assembler::decq(Register dst) { + // Don't use it directly. Use MacroAssembler::decrementq() instead. + // Use two-byte form (one-byte from is a REX prefix in 64-bit mode) + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xFF); + emit_byte(0xC8 | encode); +} + +void Assembler::decq(Address dst) { + // Don't use it directly. Use MacroAssembler::decrementq() instead. + InstructionMark im(this); + prefixq(dst); + emit_byte(0xFF); + emit_operand(rcx, dst); +} + +void Assembler::idivl(Register src) { + int encode = prefix_and_encode(src->encoding()); + emit_byte(0xF7); + emit_byte(0xF8 | encode); +} + +void Assembler::idivq(Register src) { + int encode = prefixq_and_encode(src->encoding()); + emit_byte(0xF7); + emit_byte(0xF8 | encode); +} + +void Assembler::cdql() { + emit_byte(0x99); +} + +void Assembler::cdqq() { + prefix(REX_W); + emit_byte(0x99); +} + +void Assembler::imull(Register dst, Register src) { + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0xAF); + emit_byte(0xC0 | encode); +} + +void Assembler::imull(Register dst, Register src, int value) { + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + if (is8bit(value)) { + emit_byte(0x6B); + emit_byte(0xC0 | encode); + emit_byte(value); + } else { + emit_byte(0x69); + emit_byte(0xC0 | encode); + emit_long(value); + } +} + +void Assembler::imulq(Register dst, Register src) { + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0xAF); + emit_byte(0xC0 | encode); +} + +void Assembler::imulq(Register dst, Register src, int value) { + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + if (is8bit(value)) { + emit_byte(0x6B); + emit_byte(0xC0 | encode); + emit_byte(value); + } else { + emit_byte(0x69); + emit_byte(0xC0 | encode); + emit_long(value); + } +} + +void Assembler::incl(Register dst) { + // Don't use it directly. Use MacroAssembler::incrementl() instead. + // Use two-byte form (one-byte from is a REX prefix in 64-bit mode) + int encode = prefix_and_encode(dst->encoding()); + emit_byte(0xFF); + emit_byte(0xC0 | encode); +} + +void Assembler::incl(Address dst) { + // Don't use it directly. Use MacroAssembler::incrementl() instead. + InstructionMark im(this); + prefix(dst); + emit_byte(0xFF); + emit_operand(rax, dst); +} + +void Assembler::incq(Register dst) { + // Don't use it directly. Use MacroAssembler::incrementq() instead. + // Use two-byte form (one-byte from is a REX prefix in 64-bit mode) + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xFF); + emit_byte(0xC0 | encode); +} + +void Assembler::incq(Address dst) { + // Don't use it directly. Use MacroAssembler::incrementq() instead. + InstructionMark im(this); + prefixq(dst); + emit_byte(0xFF); + emit_operand(rax, dst); +} + +void Assembler::leal(Register dst, Address src) { + InstructionMark im(this); + emit_byte(0x67); // addr32 + prefix(src, dst); + emit_byte(0x8D); + emit_operand(dst, src); +} + +void Assembler::leaq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x8D); + emit_operand(dst, src); +} + +void Assembler::mull(Address src) { + InstructionMark im(this); + // was missing + prefix(src); + emit_byte(0xF7); + emit_operand(rsp, src); +} + +void Assembler::mull(Register src) { + // was missing + int encode = prefix_and_encode(src->encoding()); + emit_byte(0xF7); + emit_byte(0xE0 | encode); +} + +void Assembler::negl(Register dst) { + int encode = prefix_and_encode(dst->encoding()); + emit_byte(0xF7); + emit_byte(0xD8 | encode); +} + +void Assembler::negq(Register dst) { + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xF7); + emit_byte(0xD8 | encode); +} + +void Assembler::notl(Register dst) { + int encode = prefix_and_encode(dst->encoding()); + emit_byte(0xF7); + emit_byte(0xD0 | encode); +} + +void Assembler::notq(Register dst) { + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xF7); + emit_byte(0xD0 | encode); +} + +void Assembler::orl(Address dst, int imm32) { + InstructionMark im(this); + prefix(dst); + emit_byte(0x81); + emit_operand(rcx, dst, 4); + emit_long(imm32); +} + +void Assembler::orl(Register dst, int imm32) { + prefix(dst); + emit_arith(0x81, 0xC8, dst, imm32); +} + +void Assembler::orl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x0B); + emit_operand(dst, src); +} + +void Assembler::orl(Register dst, Register src) { + (void) prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x0B, 0xC0, dst, src); +} + +void Assembler::orq(Address dst, int imm32) { + InstructionMark im(this); + prefixq(dst); + emit_byte(0x81); + emit_operand(rcx, dst, 4); + emit_long(imm32); +} + +void Assembler::orq(Register dst, int imm32) { + (void) prefixq_and_encode(dst->encoding()); + emit_arith(0x81, 0xC8, dst, imm32); +} + +void Assembler::orq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x0B); + emit_operand(dst, src); +} + +void Assembler::orq(Register dst, Register src) { + (void) prefixq_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x0B, 0xC0, dst, src); +} + +void Assembler::rcll(Register dst, int imm8) { + assert(isShiftCount(imm8), "illegal shift count"); + int encode = prefix_and_encode(dst->encoding()); + if (imm8 == 1) { + emit_byte(0xD1); + emit_byte(0xD0 | encode); + } else { + emit_byte(0xC1); + emit_byte(0xD0 | encode); + emit_byte(imm8); + } +} + +void Assembler::rclq(Register dst, int imm8) { + assert(isShiftCount(imm8 >> 1), "illegal shift count"); + int encode = prefixq_and_encode(dst->encoding()); + if (imm8 == 1) { + emit_byte(0xD1); + emit_byte(0xD0 | encode); + } else { + emit_byte(0xC1); + emit_byte(0xD0 | encode); + emit_byte(imm8); + } +} + +void Assembler::sarl(Register dst, int imm8) { + int encode = prefix_and_encode(dst->encoding()); + assert(isShiftCount(imm8), "illegal shift count"); + if (imm8 == 1) { + emit_byte(0xD1); + emit_byte(0xF8 | encode); + } else { + emit_byte(0xC1); + emit_byte(0xF8 | encode); + emit_byte(imm8); + } +} + +void Assembler::sarl(Register dst) { + int encode = prefix_and_encode(dst->encoding()); + emit_byte(0xD3); + emit_byte(0xF8 | encode); +} + +void Assembler::sarq(Register dst, int imm8) { + assert(isShiftCount(imm8 >> 1), "illegal shift count"); + int encode = prefixq_and_encode(dst->encoding()); + if (imm8 == 1) { + emit_byte(0xD1); + emit_byte(0xF8 | encode); + } else { + emit_byte(0xC1); + emit_byte(0xF8 | encode); + emit_byte(imm8); + } +} + +void Assembler::sarq(Register dst) { + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xD3); + emit_byte(0xF8 | encode); +} + +void Assembler::sbbl(Address dst, int imm32) { + InstructionMark im(this); + prefix(dst); + emit_arith_operand(0x81, rbx, dst, imm32); +} + +void Assembler::sbbl(Register dst, int imm32) { + prefix(dst); + emit_arith(0x81, 0xD8, dst, imm32); +} + +void Assembler::sbbl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x1B); + emit_operand(dst, src); +} + +void Assembler::sbbl(Register dst, Register src) { + (void) prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x1B, 0xC0, dst, src); +} + +void Assembler::sbbq(Address dst, int imm32) { + InstructionMark im(this); + prefixq(dst); + emit_arith_operand(0x81, rbx, dst, imm32); +} + +void Assembler::sbbq(Register dst, int imm32) { + (void) prefixq_and_encode(dst->encoding()); + emit_arith(0x81, 0xD8, dst, imm32); +} + +void Assembler::sbbq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x1B); + emit_operand(dst, src); +} + +void Assembler::sbbq(Register dst, Register src) { + (void) prefixq_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x1B, 0xC0, dst, src); +} + +void Assembler::shll(Register dst, int imm8) { + assert(isShiftCount(imm8), "illegal shift count"); + int encode = prefix_and_encode(dst->encoding()); + if (imm8 == 1 ) { + emit_byte(0xD1); + emit_byte(0xE0 | encode); + } else { + emit_byte(0xC1); + emit_byte(0xE0 | encode); + emit_byte(imm8); + } +} + +void Assembler::shll(Register dst) { + int encode = prefix_and_encode(dst->encoding()); + emit_byte(0xD3); + emit_byte(0xE0 | encode); +} + +void Assembler::shlq(Register dst, int imm8) { + assert(isShiftCount(imm8 >> 1), "illegal shift count"); + int encode = prefixq_and_encode(dst->encoding()); + if (imm8 == 1) { + emit_byte(0xD1); + emit_byte(0xE0 | encode); + } else { + emit_byte(0xC1); + emit_byte(0xE0 | encode); + emit_byte(imm8); + } +} + +void Assembler::shlq(Register dst) { + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xD3); + emit_byte(0xE0 | encode); +} + +void Assembler::shrl(Register dst, int imm8) { + assert(isShiftCount(imm8), "illegal shift count"); + int encode = prefix_and_encode(dst->encoding()); + emit_byte(0xC1); + emit_byte(0xE8 | encode); + emit_byte(imm8); +} + +void Assembler::shrl(Register dst) { + int encode = prefix_and_encode(dst->encoding()); + emit_byte(0xD3); + emit_byte(0xE8 | encode); +} + +void Assembler::shrq(Register dst, int imm8) { + assert(isShiftCount(imm8 >> 1), "illegal shift count"); + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xC1); + emit_byte(0xE8 | encode); + emit_byte(imm8); +} + +void Assembler::shrq(Register dst) { + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xD3); + emit_byte(0xE8 | encode); +} + +void Assembler::subl(Address dst, int imm32) { + InstructionMark im(this); + prefix(dst); + if (is8bit(imm32)) { + emit_byte(0x83); + emit_operand(rbp, dst, 1); + emit_byte(imm32 & 0xFF); + } else { + emit_byte(0x81); + emit_operand(rbp, dst, 4); + emit_long(imm32); + } +} + +void Assembler::subl(Register dst, int imm32) { + prefix(dst); + emit_arith(0x81, 0xE8, dst, imm32); +} + +void Assembler::subl(Address dst, Register src) { + InstructionMark im(this); + prefix(dst, src); + emit_byte(0x29); + emit_operand(src, dst); +} + +void Assembler::subl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x2B); + emit_operand(dst, src); +} + +void Assembler::subl(Register dst, Register src) { + (void) prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x2B, 0xC0, dst, src); +} + +void Assembler::subq(Address dst, int imm32) { + InstructionMark im(this); + prefixq(dst); + if (is8bit(imm32)) { + emit_byte(0x83); + emit_operand(rbp, dst, 1); + emit_byte(imm32 & 0xFF); + } else { + emit_byte(0x81); + emit_operand(rbp, dst, 4); + emit_long(imm32); + } +} + +void Assembler::subq(Register dst, int imm32) { + (void) prefixq_and_encode(dst->encoding()); + emit_arith(0x81, 0xE8, dst, imm32); +} + +void Assembler::subq(Address dst, Register src) { + InstructionMark im(this); + prefixq(dst, src); + emit_byte(0x29); + emit_operand(src, dst); +} + +void Assembler::subq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x2B); + emit_operand(dst, src); +} + +void Assembler::subq(Register dst, Register src) { + (void) prefixq_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x2B, 0xC0, dst, src); +} + +void Assembler::testb(Register dst, int imm8) { + (void) prefix_and_encode(dst->encoding(), true); + emit_arith_b(0xF6, 0xC0, dst, imm8); +} + +void Assembler::testl(Register dst, int imm32) { + // not using emit_arith because test + // doesn't support sign-extension of + // 8bit operands + int encode = dst->encoding(); + if (encode == 0) { + emit_byte(0xA9); + } else { + encode = prefix_and_encode(encode); + emit_byte(0xF7); + emit_byte(0xC0 | encode); + } + emit_long(imm32); +} + +void Assembler::testl(Register dst, Register src) { + (void) prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x85, 0xC0, dst, src); +} + +void Assembler::testq(Register dst, int imm32) { + // not using emit_arith because test + // doesn't support sign-extension of + // 8bit operands + int encode = dst->encoding(); + if (encode == 0) { + prefix(REX_W); + emit_byte(0xA9); + } else { + encode = prefixq_and_encode(encode); + emit_byte(0xF7); + emit_byte(0xC0 | encode); + } + emit_long(imm32); +} + +void Assembler::testq(Register dst, Register src) { + (void) prefixq_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x85, 0xC0, dst, src); +} + +void Assembler::xaddl(Address dst, Register src) { + InstructionMark im(this); + prefix(dst, src); + emit_byte(0x0F); + emit_byte(0xC1); + emit_operand(src, dst); +} + +void Assembler::xaddq(Address dst, Register src) { + InstructionMark im(this); + prefixq(dst, src); + emit_byte(0x0F); + emit_byte(0xC1); + emit_operand(src, dst); +} + +void Assembler::xorl(Register dst, int imm32) { + prefix(dst); + emit_arith(0x81, 0xF0, dst, imm32); +} + +void Assembler::xorl(Register dst, Register src) { + (void) prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x33, 0xC0, dst, src); +} + +void Assembler::xorl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x33); + emit_operand(dst, src); +} + +void Assembler::xorq(Register dst, int imm32) { + (void) prefixq_and_encode(dst->encoding()); + emit_arith(0x81, 0xF0, dst, imm32); +} + +void Assembler::xorq(Register dst, Register src) { + (void) prefixq_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x33, 0xC0, dst, src); +} + +void Assembler::xorq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x33); + emit_operand(dst, src); +} + +void Assembler::bswapl(Register reg) { + int encode = prefix_and_encode(reg->encoding()); + emit_byte(0x0F); + emit_byte(0xC8 | encode); +} + +void Assembler::bswapq(Register reg) { + int encode = prefixq_and_encode(reg->encoding()); + emit_byte(0x0F); + emit_byte(0xC8 | encode); +} + +void Assembler::lock() { + emit_byte(0xF0); +} + +void Assembler::xchgl(Register dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x87); + emit_operand(dst, src); +} + +void Assembler::xchgl(Register dst, Register src) { + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x87); + emit_byte(0xc0 | encode); +} + +void Assembler::xchgq(Register dst, Address src) { + InstructionMark im(this); + prefixq(src, dst); + emit_byte(0x87); + emit_operand(dst, src); +} + +void Assembler::xchgq(Register dst, Register src) { + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x87); + emit_byte(0xc0 | encode); +} + +void Assembler::cmpxchgl(Register reg, Address adr) { + InstructionMark im(this); + prefix(adr, reg); + emit_byte(0x0F); + emit_byte(0xB1); + emit_operand(reg, adr); +} + +void Assembler::cmpxchgq(Register reg, Address adr) { + InstructionMark im(this); + prefixq(adr, reg); + emit_byte(0x0F); + emit_byte(0xB1); + emit_operand(reg, adr); +} + +void Assembler::hlt() { + emit_byte(0xF4); +} + + +void Assembler::addr_nop_4() { + // 4 bytes: NOP DWORD PTR [EAX+0] + emit_byte(0x0F); + emit_byte(0x1F); + emit_byte(0x40); // emit_rm(cbuf, 0x1, EAX_enc, EAX_enc); + emit_byte(0); // 8-bits offset (1 byte) +} + +void Assembler::addr_nop_5() { + // 5 bytes: NOP DWORD PTR [EAX+EAX*0+0] 8-bits offset + emit_byte(0x0F); + emit_byte(0x1F); + emit_byte(0x44); // emit_rm(cbuf, 0x1, EAX_enc, 0x4); + emit_byte(0x00); // emit_rm(cbuf, 0x0, EAX_enc, EAX_enc); + emit_byte(0); // 8-bits offset (1 byte) +} + +void Assembler::addr_nop_7() { + // 7 bytes: NOP DWORD PTR [EAX+0] 32-bits offset + emit_byte(0x0F); + emit_byte(0x1F); + emit_byte(0x80); // emit_rm(cbuf, 0x2, EAX_enc, EAX_enc); + emit_long(0); // 32-bits offset (4 bytes) +} + +void Assembler::addr_nop_8() { + // 8 bytes: NOP DWORD PTR [EAX+EAX*0+0] 32-bits offset + emit_byte(0x0F); + emit_byte(0x1F); + emit_byte(0x84); // emit_rm(cbuf, 0x2, EAX_enc, 0x4); + emit_byte(0x00); // emit_rm(cbuf, 0x0, EAX_enc, EAX_enc); + emit_long(0); // 32-bits offset (4 bytes) +} + +void Assembler::nop(int i) { + assert(i > 0, " "); + if (UseAddressNop && VM_Version::is_intel()) { + // + // Using multi-bytes nops "0x0F 0x1F [address]" for Intel + // 1: 0x90 + // 2: 0x66 0x90 + // 3: 0x66 0x66 0x90 (don't use "0x0F 0x1F 0x00" - need patching safe padding) + // 4: 0x0F 0x1F 0x40 0x00 + // 5: 0x0F 0x1F 0x44 0x00 0x00 + // 6: 0x66 0x0F 0x1F 0x44 0x00 0x00 + // 7: 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 + // 8: 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 9: 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 10: 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 11: 0x66 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + + // The rest coding is Intel specific - don't use consecutive address nops + + // 12: 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x66 0x66 0x66 0x90 + // 13: 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x66 0x66 0x66 0x90 + // 14: 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x66 0x66 0x66 0x90 + // 15: 0x66 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x66 0x66 0x66 0x90 + + while(i >= 15) { + // For Intel don't generate consecutive addess nops (mix with regular nops) + i -= 15; + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + addr_nop_8(); + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x90); // nop + } + switch (i) { + case 14: + emit_byte(0x66); // size prefix + case 13: + emit_byte(0x66); // size prefix + case 12: + addr_nop_8(); + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x90); // nop + break; + case 11: + emit_byte(0x66); // size prefix + case 10: + emit_byte(0x66); // size prefix + case 9: + emit_byte(0x66); // size prefix + case 8: + addr_nop_8(); + break; + case 7: + addr_nop_7(); + break; + case 6: + emit_byte(0x66); // size prefix + case 5: + addr_nop_5(); + break; + case 4: + addr_nop_4(); + break; + case 3: + // Don't use "0x0F 0x1F 0x00" - need patching safe padding + emit_byte(0x66); // size prefix + case 2: + emit_byte(0x66); // size prefix + case 1: + emit_byte(0x90); // nop + break; + default: + assert(i == 0, " "); + } + return; + } + if (UseAddressNop && VM_Version::is_amd()) { + // + // Using multi-bytes nops "0x0F 0x1F [address]" for AMD. + // 1: 0x90 + // 2: 0x66 0x90 + // 3: 0x66 0x66 0x90 (don't use "0x0F 0x1F 0x00" - need patching safe padding) + // 4: 0x0F 0x1F 0x40 0x00 + // 5: 0x0F 0x1F 0x44 0x00 0x00 + // 6: 0x66 0x0F 0x1F 0x44 0x00 0x00 + // 7: 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 + // 8: 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 9: 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 10: 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // 11: 0x66 0x66 0x66 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + + // The rest coding is AMD specific - use consecutive address nops + + // 12: 0x66 0x0F 0x1F 0x44 0x00 0x00 0x66 0x0F 0x1F 0x44 0x00 0x00 + // 13: 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 0x66 0x0F 0x1F 0x44 0x00 0x00 + // 14: 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 + // 15: 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x0F 0x1F 0x80 0x00 0x00 0x00 0x00 + // 16: 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 0x0F 0x1F 0x84 0x00 0x00 0x00 0x00 0x00 + // Size prefixes (0x66) are added for larger sizes + + while(i >= 22) { + i -= 11; + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + emit_byte(0x66); // size prefix + addr_nop_8(); + } + // Generate first nop for size between 21-12 + switch (i) { + case 21: + i -= 1; + emit_byte(0x66); // size prefix + case 20: + case 19: + i -= 1; + emit_byte(0x66); // size prefix + case 18: + case 17: + i -= 1; + emit_byte(0x66); // size prefix + case 16: + case 15: + i -= 8; + addr_nop_8(); + break; + case 14: + case 13: + i -= 7; + addr_nop_7(); + break; + case 12: + i -= 6; + emit_byte(0x66); // size prefix + addr_nop_5(); + break; + default: + assert(i < 12, " "); + } + + // Generate second nop for size between 11-1 + switch (i) { + case 11: + emit_byte(0x66); // size prefix + case 10: + emit_byte(0x66); // size prefix + case 9: + emit_byte(0x66); // size prefix + case 8: + addr_nop_8(); + break; + case 7: + addr_nop_7(); + break; + case 6: + emit_byte(0x66); // size prefix + case 5: + addr_nop_5(); + break; + case 4: + addr_nop_4(); + break; + case 3: + // Don't use "0x0F 0x1F 0x00" - need patching safe padding + emit_byte(0x66); // size prefix + case 2: + emit_byte(0x66); // size prefix + case 1: + emit_byte(0x90); // nop + break; + default: + assert(i == 0, " "); + } + return; + } + + // Using nops with size prefixes "0x66 0x90". + // From AMD Optimization Guide: + // 1: 0x90 + // 2: 0x66 0x90 + // 3: 0x66 0x66 0x90 + // 4: 0x66 0x66 0x66 0x90 + // 5: 0x66 0x66 0x90 0x66 0x90 + // 6: 0x66 0x66 0x90 0x66 0x66 0x90 + // 7: 0x66 0x66 0x66 0x90 0x66 0x66 0x90 + // 8: 0x66 0x66 0x66 0x90 0x66 0x66 0x66 0x90 + // 9: 0x66 0x66 0x90 0x66 0x66 0x90 0x66 0x66 0x90 + // 10: 0x66 0x66 0x66 0x90 0x66 0x66 0x90 0x66 0x66 0x90 + // + while(i > 12) { + i -= 4; + emit_byte(0x66); // size prefix + emit_byte(0x66); + emit_byte(0x66); + emit_byte(0x90); // nop + } + // 1 - 12 nops + if(i > 8) { + if(i > 9) { + i -= 1; + emit_byte(0x66); + } + i -= 3; + emit_byte(0x66); + emit_byte(0x66); + emit_byte(0x90); + } + // 1 - 8 nops + if(i > 4) { + if(i > 6) { + i -= 1; + emit_byte(0x66); + } + i -= 3; + emit_byte(0x66); + emit_byte(0x66); + emit_byte(0x90); + } + switch (i) { + case 4: + emit_byte(0x66); + case 3: + emit_byte(0x66); + case 2: + emit_byte(0x66); + case 1: + emit_byte(0x90); + break; + default: + assert(i == 0, " "); + } +} + +void Assembler::ret(int imm16) { + if (imm16 == 0) { + emit_byte(0xC3); + } else { + emit_byte(0xC2); + emit_word(imm16); + } +} + +// copies a single word from [esi] to [edi] +void Assembler::smovl() { + emit_byte(0xA5); +} + +// copies data from [rsi] to [rdi] using rcx words (m32) +void Assembler::rep_movl() { + // REP + emit_byte(0xF3); + // MOVSL + emit_byte(0xA5); +} + +// copies data from [rsi] to [rdi] using rcx double words (m64) +void Assembler::rep_movq() { + // REP + emit_byte(0xF3); + // MOVSQ + prefix(REX_W); + emit_byte(0xA5); +} + +// sets rcx double words (m64) with rax value at [rdi] +void Assembler::rep_set() { + // REP + emit_byte(0xF3); + // STOSQ + prefix(REX_W); + emit_byte(0xAB); +} + +// scans rcx double words (m64) at [rdi] for occurance of rax +void Assembler::repne_scan() { + // REPNE/REPNZ + emit_byte(0xF2); + // SCASQ + prefix(REX_W); + emit_byte(0xAF); +} + +void Assembler::setb(Condition cc, Register dst) { + assert(0 <= cc && cc < 16, "illegal cc"); + int encode = prefix_and_encode(dst->encoding(), true); + emit_byte(0x0F); + emit_byte(0x90 | cc); + emit_byte(0xC0 | encode); +} + +void Assembler::clflush(Address adr) { + prefix(adr); + emit_byte(0x0F); + emit_byte(0xAE); + emit_operand(rdi, adr); +} + +void Assembler::call(Label& L, relocInfo::relocType rtype) { + if (L.is_bound()) { + const int long_size = 5; + int offs = (int)( target(L) - pc() ); + assert(offs <= 0, "assembler error"); + InstructionMark im(this); + // 1110 1000 #32-bit disp + emit_byte(0xE8); + emit_data(offs - long_size, rtype, disp32_operand); + } else { + InstructionMark im(this); + // 1110 1000 #32-bit disp + L.add_patch_at(code(), locator()); + + emit_byte(0xE8); + emit_data(int(0), rtype, disp32_operand); + } +} + +void Assembler::call_literal(address entry, RelocationHolder const& rspec) { + assert(entry != NULL, "call most probably wrong"); + InstructionMark im(this); + emit_byte(0xE8); + intptr_t disp = entry - (_code_pos + sizeof(int32_t)); + assert(is_simm32(disp), "must be 32bit offset (call2)"); + // Technically, should use call32_operand, but this format is + // implied by the fact that we're emitting a call instruction. + emit_data((int) disp, rspec, disp32_operand); +} + + +void Assembler::call(Register dst) { + // This was originally using a 32bit register encoding + // and surely we want 64bit! + // this is a 32bit encoding but in 64bit mode the default + // operand size is 64bit so there is no need for the + // wide prefix. So prefix only happens if we use the + // new registers. Much like push/pop. + int encode = prefixq_and_encode(dst->encoding()); + emit_byte(0xFF); + emit_byte(0xD0 | encode); +} + +void Assembler::call(Address adr) { + InstructionMark im(this); + prefix(adr); + emit_byte(0xFF); + emit_operand(rdx, adr); +} + +void Assembler::jmp(Register reg) { + int encode = prefix_and_encode(reg->encoding()); + emit_byte(0xFF); + emit_byte(0xE0 | encode); +} + +void Assembler::jmp(Address adr) { + InstructionMark im(this); + prefix(adr); + emit_byte(0xFF); + emit_operand(rsp, adr); +} + +void Assembler::jmp_literal(address dest, RelocationHolder const& rspec) { + InstructionMark im(this); + emit_byte(0xE9); + assert(dest != NULL, "must have a target"); + intptr_t disp = dest - (_code_pos + sizeof(int32_t)); + assert(is_simm32(disp), "must be 32bit offset (jmp)"); + emit_data(disp, rspec.reloc(), call32_operand); +} + +void Assembler::jmp(Label& L, relocInfo::relocType rtype) { + if (L.is_bound()) { + address entry = target(L); + assert(entry != NULL, "jmp most probably wrong"); + InstructionMark im(this); + const int short_size = 2; + const int long_size = 5; + intptr_t offs = entry - _code_pos; + if (rtype == relocInfo::none && is8bit(offs - short_size)) { + emit_byte(0xEB); + emit_byte((offs - short_size) & 0xFF); + } else { + emit_byte(0xE9); + emit_long(offs - long_size); + } + } else { + // By default, forward jumps are always 32-bit displacements, since + // we can't yet know where the label will be bound. If you're sure that + // the forward jump will not run beyond 256 bytes, use jmpb to + // force an 8-bit displacement. + InstructionMark im(this); + relocate(rtype); + L.add_patch_at(code(), locator()); + emit_byte(0xE9); + emit_long(0); + } +} + +void Assembler::jmpb(Label& L) { + if (L.is_bound()) { + const int short_size = 2; + address entry = target(L); + assert(is8bit((entry - _code_pos) + short_size), + "Dispacement too large for a short jmp"); + assert(entry != NULL, "jmp most probably wrong"); + intptr_t offs = entry - _code_pos; + emit_byte(0xEB); + emit_byte((offs - short_size) & 0xFF); + } else { + InstructionMark im(this); + L.add_patch_at(code(), locator()); + emit_byte(0xEB); + emit_byte(0); + } +} + +void Assembler::jcc(Condition cc, Label& L, relocInfo::relocType rtype) { + InstructionMark im(this); + relocate(rtype); + assert((0 <= cc) && (cc < 16), "illegal cc"); + if (L.is_bound()) { + address dst = target(L); + assert(dst != NULL, "jcc most probably wrong"); + + const int short_size = 2; + const int long_size = 6; + intptr_t offs = (intptr_t)dst - (intptr_t)_code_pos; + if (rtype == relocInfo::none && is8bit(offs - short_size)) { + // 0111 tttn #8-bit disp + emit_byte(0x70 | cc); + emit_byte((offs - short_size) & 0xFF); + } else { + // 0000 1111 1000 tttn #32-bit disp + assert(is_simm32(offs - long_size), + "must be 32bit offset (call4)"); + emit_byte(0x0F); + emit_byte(0x80 | cc); + emit_long(offs - long_size); + } + } else { + // Note: could eliminate cond. jumps to this jump if condition + // is the same however, seems to be rather unlikely case. + // Note: use jccb() if label to be bound is very close to get + // an 8-bit displacement + L.add_patch_at(code(), locator()); + emit_byte(0x0F); + emit_byte(0x80 | cc); + emit_long(0); + } +} + +void Assembler::jccb(Condition cc, Label& L) { + if (L.is_bound()) { + const int short_size = 2; + const int long_size = 6; + address entry = target(L); + assert(is8bit((intptr_t)entry - ((intptr_t)_code_pos + short_size)), + "Dispacement too large for a short jmp"); + intptr_t offs = (intptr_t)entry - (intptr_t)_code_pos; + // 0111 tttn #8-bit disp + emit_byte(0x70 | cc); + emit_byte((offs - short_size) & 0xFF); + } else { + InstructionMark im(this); + L.add_patch_at(code(), locator()); + emit_byte(0x70 | cc); + emit_byte(0); + } +} + +// FP instructions + +void Assembler::fxsave(Address dst) { + prefixq(dst); + emit_byte(0x0F); + emit_byte(0xAE); + emit_operand(as_Register(0), dst); +} + +void Assembler::fxrstor(Address src) { + prefixq(src); + emit_byte(0x0F); + emit_byte(0xAE); + emit_operand(as_Register(1), src); +} + +void Assembler::ldmxcsr(Address src) { + InstructionMark im(this); + prefix(src); + emit_byte(0x0F); + emit_byte(0xAE); + emit_operand(as_Register(2), src); +} + +void Assembler::stmxcsr(Address dst) { + InstructionMark im(this); + prefix(dst); + emit_byte(0x0F); + emit_byte(0xAE); + emit_operand(as_Register(3), dst); +} + +void Assembler::addss(XMMRegister dst, XMMRegister src) { + emit_byte(0xF3); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x58); + emit_byte(0xC0 | encode); +} + +void Assembler::addss(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF3); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x58); + emit_operand(dst, src); +} + +void Assembler::subss(XMMRegister dst, XMMRegister src) { + emit_byte(0xF3); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x5C); + emit_byte(0xC0 | encode); +} + +void Assembler::subss(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF3); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x5C); + emit_operand(dst, src); +} + +void Assembler::mulss(XMMRegister dst, XMMRegister src) { + emit_byte(0xF3); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x59); + emit_byte(0xC0 | encode); +} + +void Assembler::mulss(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF3); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x59); + emit_operand(dst, src); +} + +void Assembler::divss(XMMRegister dst, XMMRegister src) { + emit_byte(0xF3); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x5E); + emit_byte(0xC0 | encode); +} + +void Assembler::divss(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF3); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x5E); + emit_operand(dst, src); +} + +void Assembler::addsd(XMMRegister dst, XMMRegister src) { + emit_byte(0xF2); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x58); + emit_byte(0xC0 | encode); +} + +void Assembler::addsd(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF2); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x58); + emit_operand(dst, src); +} + +void Assembler::subsd(XMMRegister dst, XMMRegister src) { + emit_byte(0xF2); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x5C); + emit_byte(0xC0 | encode); +} + +void Assembler::subsd(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF2); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x5C); + emit_operand(dst, src); +} + +void Assembler::mulsd(XMMRegister dst, XMMRegister src) { + emit_byte(0xF2); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x59); + emit_byte(0xC0 | encode); +} + +void Assembler::mulsd(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF2); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x59); + emit_operand(dst, src); +} + +void Assembler::divsd(XMMRegister dst, XMMRegister src) { + emit_byte(0xF2); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x5E); + emit_byte(0xC0 | encode); +} + +void Assembler::divsd(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF2); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x5E); + emit_operand(dst, src); +} + +void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) { + emit_byte(0xF2); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x51); + emit_byte(0xC0 | encode); +} + +void Assembler::sqrtsd(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0xF2); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x51); + emit_operand(dst, src); +} + +void Assembler::xorps(XMMRegister dst, XMMRegister src) { + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x57); + emit_byte(0xC0 | encode); +} + +void Assembler::xorps(XMMRegister dst, Address src) { + InstructionMark im(this); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x57); + emit_operand(dst, src); +} + +void Assembler::xorpd(XMMRegister dst, XMMRegister src) { + emit_byte(0x66); + xorps(dst, src); +} + +void Assembler::xorpd(XMMRegister dst, Address src) { + InstructionMark im(this); + emit_byte(0x66); + prefix(src, dst); + emit_byte(0x0F); + emit_byte(0x57); + emit_operand(dst, src); +} + +void Assembler::cvtsi2ssl(XMMRegister dst, Register src) { + emit_byte(0xF3); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x2A); + emit_byte(0xC0 | encode); +} + +void Assembler::cvtsi2ssq(XMMRegister dst, Register src) { + emit_byte(0xF3); + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x2A); + emit_byte(0xC0 | encode); +} + +void Assembler::cvtsi2sdl(XMMRegister dst, Register src) { + emit_byte(0xF2); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x2A); + emit_byte(0xC0 | encode); +} + +void Assembler::cvtsi2sdq(XMMRegister dst, Register src) { + emit_byte(0xF2); + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x2A); + emit_byte(0xC0 | encode); +} + +void Assembler::cvttss2sil(Register dst, XMMRegister src) { + emit_byte(0xF3); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x2C); + emit_byte(0xC0 | encode); +} + +void Assembler::cvttss2siq(Register dst, XMMRegister src) { + emit_byte(0xF3); + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x2C); + emit_byte(0xC0 | encode); +} + +void Assembler::cvttsd2sil(Register dst, XMMRegister src) { + emit_byte(0xF2); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x2C); + emit_byte(0xC0 | encode); +} + +void Assembler::cvttsd2siq(Register dst, XMMRegister src) { + emit_byte(0xF2); + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x2C); + emit_byte(0xC0 | encode); +} + +void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) { + emit_byte(0xF3); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x5A); + emit_byte(0xC0 | encode); +} + +void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) { + emit_byte(0xF2); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x5A); + emit_byte(0xC0 | encode); +} + +void Assembler::punpcklbw(XMMRegister dst, XMMRegister src) { + emit_byte(0x66); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_byte(0x0F); + emit_byte(0x60); + emit_byte(0xC0 | encode); +} + +// Implementation of MacroAssembler + +// On 32 bit it returns a vanilla displacement on 64 bit is a rip relative displacement +Address MacroAssembler::as_Address(AddressLiteral adr) { + assert(!adr.is_lval(), "must be rval"); + assert(reachable(adr), "must be"); + return Address((int)(intptr_t)(adr.target() - pc()), adr.target(), adr.reloc()); +} + +Address MacroAssembler::as_Address(ArrayAddress adr) { +#ifdef _LP64 + AddressLiteral base = adr.base(); + lea(rscratch1, base); + Address index = adr.index(); + assert(index._disp == 0, "must not have disp"); // maybe it can? + Address array(rscratch1, index._index, index._scale, index._disp); + return array; +#else + return Address::make_array(adr); +#endif // _LP64 + +} + +void MacroAssembler::fat_nop() { + // A 5 byte nop that is safe for patching (see patch_verified_entry) + // Recommened sequence from 'Software Optimization Guide for the AMD + // Hammer Processor' + emit_byte(0x66); + emit_byte(0x66); + emit_byte(0x90); + emit_byte(0x66); + emit_byte(0x90); +} + +static Assembler::Condition reverse[] = { + Assembler::noOverflow /* overflow = 0x0 */ , + Assembler::overflow /* noOverflow = 0x1 */ , + Assembler::aboveEqual /* carrySet = 0x2, below = 0x2 */ , + Assembler::below /* aboveEqual = 0x3, carryClear = 0x3 */ , + Assembler::notZero /* zero = 0x4, equal = 0x4 */ , + Assembler::zero /* notZero = 0x5, notEqual = 0x5 */ , + Assembler::above /* belowEqual = 0x6 */ , + Assembler::belowEqual /* above = 0x7 */ , + Assembler::positive /* negative = 0x8 */ , + Assembler::negative /* positive = 0x9 */ , + Assembler::noParity /* parity = 0xa */ , + Assembler::parity /* noParity = 0xb */ , + Assembler::greaterEqual /* less = 0xc */ , + Assembler::less /* greaterEqual = 0xd */ , + Assembler::greater /* lessEqual = 0xe */ , + Assembler::lessEqual /* greater = 0xf, */ + +}; + +// 32bit can do a case table jump in one instruction but we no longer allow the base +// to be installed in the Address class +void MacroAssembler::jump(ArrayAddress entry) { +#ifdef _LP64 + lea(rscratch1, entry.base()); + Address dispatch = entry.index(); + assert(dispatch._base == noreg, "must be"); + dispatch._base = rscratch1; + jmp(dispatch); +#else + jmp(as_Address(entry)); +#endif // _LP64 +} + +void MacroAssembler::jump(AddressLiteral dst) { + if (reachable(dst)) { + jmp_literal(dst.target(), dst.rspec()); + } else { + lea(rscratch1, dst); + jmp(rscratch1); + } +} + +void MacroAssembler::jump_cc(Condition cc, AddressLiteral dst) { + if (reachable(dst)) { + InstructionMark im(this); + relocate(dst.reloc()); + const int short_size = 2; + const int long_size = 6; + int offs = (intptr_t)dst.target() - ((intptr_t)_code_pos); + if (dst.reloc() == relocInfo::none && is8bit(offs - short_size)) { + // 0111 tttn #8-bit disp + emit_byte(0x70 | cc); + emit_byte((offs - short_size) & 0xFF); + } else { + // 0000 1111 1000 tttn #32-bit disp + emit_byte(0x0F); + emit_byte(0x80 | cc); + emit_long(offs - long_size); + } + } else { +#ifdef ASSERT + warning("reversing conditional branch"); +#endif /* ASSERT */ + Label skip; + jccb(reverse[cc], skip); + lea(rscratch1, dst); + Assembler::jmp(rscratch1); + bind(skip); + } +} + +// Wouldn't need if AddressLiteral version had new name +void MacroAssembler::call(Label& L, relocInfo::relocType rtype) { + Assembler::call(L, rtype); +} + +// Wouldn't need if AddressLiteral version had new name +void MacroAssembler::call(Register entry) { + Assembler::call(entry); +} + +void MacroAssembler::call(AddressLiteral entry) { + if (reachable(entry)) { + Assembler::call_literal(entry.target(), entry.rspec()); + } else { + lea(rscratch1, entry); + Assembler::call(rscratch1); + } +} + +void MacroAssembler::cmp8(AddressLiteral src1, int8_t src2) { + if (reachable(src1)) { + cmpb(as_Address(src1), src2); + } else { + lea(rscratch1, src1); + cmpb(Address(rscratch1, 0), src2); + } +} + +void MacroAssembler::cmp32(AddressLiteral src1, int32_t src2) { + if (reachable(src1)) { + cmpl(as_Address(src1), src2); + } else { + lea(rscratch1, src1); + cmpl(Address(rscratch1, 0), src2); + } +} + +void MacroAssembler::cmp32(Register src1, AddressLiteral src2) { + if (reachable(src2)) { + cmpl(src1, as_Address(src2)); + } else { + lea(rscratch1, src2); + cmpl(src1, Address(rscratch1, 0)); + } +} + +void MacroAssembler::cmpptr(Register src1, AddressLiteral src2) { +#ifdef _LP64 + if (src2.is_lval()) { + movptr(rscratch1, src2); + Assembler::cmpq(src1, rscratch1); + } else if (reachable(src2)) { + cmpq(src1, as_Address(src2)); + } else { + lea(rscratch1, src2); + Assembler::cmpq(src1, Address(rscratch1, 0)); + } +#else + if (src2.is_lval()) { + cmp_literal32(src1, (int32_t) src2.target(), src2.rspec()); + } else { + cmpl(src1, as_Address(src2)); + } +#endif // _LP64 +} + +void MacroAssembler::cmpptr(Address src1, AddressLiteral src2) { + assert(src2.is_lval(), "not a mem-mem compare"); +#ifdef _LP64 + // moves src2's literal address + movptr(rscratch1, src2); + Assembler::cmpq(src1, rscratch1); +#else + cmp_literal32(src1, (int32_t) src2.target(), src2.rspec()); +#endif // _LP64 +} + +void MacroAssembler::cmp64(Register src1, AddressLiteral src2) { + assert(!src2.is_lval(), "should use cmpptr"); + + if (reachable(src2)) { +#ifdef _LP64 + cmpq(src1, as_Address(src2)); +#else + ShouldNotReachHere(); +#endif // _LP64 + } else { + lea(rscratch1, src2); + Assembler::cmpq(src1, Address(rscratch1, 0)); + } +} + +void MacroAssembler::cmpxchgptr(Register reg, AddressLiteral adr) { + if (reachable(adr)) { +#ifdef _LP64 + cmpxchgq(reg, as_Address(adr)); +#else + cmpxchgl(reg, as_Address(adr)); +#endif // _LP64 + } else { + lea(rscratch1, adr); + cmpxchgq(reg, Address(rscratch1, 0)); + } +} + +void MacroAssembler::incrementl(AddressLiteral dst) { + if (reachable(dst)) { + incrementl(as_Address(dst)); + } else { + lea(rscratch1, dst); + incrementl(Address(rscratch1, 0)); + } +} + +void MacroAssembler::incrementl(ArrayAddress dst) { + incrementl(as_Address(dst)); +} + +void MacroAssembler::lea(Register dst, Address src) { +#ifdef _LP64 + leaq(dst, src); +#else + leal(dst, src); +#endif // _LP64 +} + +void MacroAssembler::lea(Register dst, AddressLiteral src) { +#ifdef _LP64 + mov_literal64(dst, (intptr_t)src.target(), src.rspec()); +#else + mov_literal32(dst, (intptr_t)src.target(), src.rspec()); +#endif // _LP64 +} + +void MacroAssembler::mov32(AddressLiteral dst, Register src) { + if (reachable(dst)) { + movl(as_Address(dst), src); + } else { + lea(rscratch1, dst); + movl(Address(rscratch1, 0), src); + } +} + +void MacroAssembler::mov32(Register dst, AddressLiteral src) { + if (reachable(src)) { + movl(dst, as_Address(src)); + } else { + lea(rscratch1, src); + movl(dst, Address(rscratch1, 0)); + } +} + +void MacroAssembler::movdbl(XMMRegister dst, AddressLiteral src) { + if (reachable(src)) { + if (UseXmmLoadAndClearUpper) { + movsd (dst, as_Address(src)); + } else { + movlpd(dst, as_Address(src)); + } + } else { + lea(rscratch1, src); + if (UseXmmLoadAndClearUpper) { + movsd (dst, Address(rscratch1, 0)); + } else { + movlpd(dst, Address(rscratch1, 0)); + } + } +} + +void MacroAssembler::movflt(XMMRegister dst, AddressLiteral src) { + if (reachable(src)) { + movss(dst, as_Address(src)); + } else { + lea(rscratch1, src); + movss(dst, Address(rscratch1, 0)); + } +} + +void MacroAssembler::movoop(Register dst, jobject obj) { + mov_literal64(dst, (intptr_t)obj, oop_Relocation::spec_for_immediate()); +} + +void MacroAssembler::movoop(Address dst, jobject obj) { + mov_literal64(rscratch1, (intptr_t)obj, oop_Relocation::spec_for_immediate()); + movq(dst, rscratch1); +} + +void MacroAssembler::movptr(Register dst, AddressLiteral src) { +#ifdef _LP64 + if (src.is_lval()) { + mov_literal64(dst, (intptr_t)src.target(), src.rspec()); + } else { + if (reachable(src)) { + movq(dst, as_Address(src)); + } else { + lea(rscratch1, src); + movq(dst, Address(rscratch1,0)); + } + } +#else + if (src.is_lval()) { + mov_literal32(dst, (intptr_t)src.target(), src.rspec()); + } else { + movl(dst, as_Address(src)); + } +#endif // LP64 +} + +void MacroAssembler::movptr(ArrayAddress dst, Register src) { +#ifdef _LP64 + movq(as_Address(dst), src); +#else + movl(as_Address(dst), src); +#endif // _LP64 +} + +void MacroAssembler::pushoop(jobject obj) { +#ifdef _LP64 + movoop(rscratch1, obj); + pushq(rscratch1); +#else + push_literal32((int32_t)obj, oop_Relocation::spec_for_immediate()); +#endif // _LP64 +} + +void MacroAssembler::pushptr(AddressLiteral src) { +#ifdef _LP64 + lea(rscratch1, src); + if (src.is_lval()) { + pushq(rscratch1); + } else { + pushq(Address(rscratch1, 0)); + } +#else + if (src.is_lval()) { + push_literal((int32_t)src.target(), src.rspec()); + else { + pushl(as_Address(src)); + } +#endif // _LP64 +} + +void MacroAssembler::ldmxcsr(AddressLiteral src) { + if (reachable(src)) { + Assembler::ldmxcsr(as_Address(src)); + } else { + lea(rscratch1, src); + Assembler::ldmxcsr(Address(rscratch1, 0)); + } +} + +void MacroAssembler::movlpd(XMMRegister dst, AddressLiteral src) { + if (reachable(src)) { + movlpd(dst, as_Address(src)); + } else { + lea(rscratch1, src); + movlpd(dst, Address(rscratch1, 0)); + } +} + +void MacroAssembler::movss(XMMRegister dst, AddressLiteral src) { + if (reachable(src)) { + movss(dst, as_Address(src)); + } else { + lea(rscratch1, src); + movss(dst, Address(rscratch1, 0)); + } +} +void MacroAssembler::xorpd(XMMRegister dst, AddressLiteral src) { + if (reachable(src)) { + xorpd(dst, as_Address(src)); + } else { + lea(rscratch1, src); + xorpd(dst, Address(rscratch1, 0)); + } +} + +void MacroAssembler::xorps(XMMRegister dst, AddressLiteral src) { + if (reachable(src)) { + xorps(dst, as_Address(src)); + } else { + lea(rscratch1, src); + xorps(dst, Address(rscratch1, 0)); + } +} + +void MacroAssembler::null_check(Register reg, int offset) { + if (needs_explicit_null_check(offset)) { + // provoke OS NULL exception if reg = NULL by + // accessing M[reg] w/o changing any (non-CC) registers + cmpq(rax, Address(reg, 0)); + // Note: should probably use testl(rax, Address(reg, 0)); + // may be shorter code (however, this version of + // testl needs to be implemented first) + } else { + // nothing to do, (later) access of M[reg + offset] + // will provoke OS NULL exception if reg = NULL + } +} + +int MacroAssembler::load_unsigned_byte(Register dst, Address src) { + int off = offset(); + movzbl(dst, src); + return off; +} + +int MacroAssembler::load_unsigned_word(Register dst, Address src) { + int off = offset(); + movzwl(dst, src); + return off; +} + +int MacroAssembler::load_signed_byte(Register dst, Address src) { + int off = offset(); + movsbl(dst, src); + return off; +} + +int MacroAssembler::load_signed_word(Register dst, Address src) { + int off = offset(); + movswl(dst, src); + return off; +} + +void MacroAssembler::incrementl(Register reg, int value) { + if (value == min_jint) { addl(reg, value); return; } + if (value < 0) { decrementl(reg, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { incl(reg) ; return; } + /* else */ { addl(reg, value) ; return; } +} + +void MacroAssembler::decrementl(Register reg, int value) { + if (value == min_jint) { subl(reg, value); return; } + if (value < 0) { incrementl(reg, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { decl(reg) ; return; } + /* else */ { subl(reg, value) ; return; } +} + +void MacroAssembler::incrementq(Register reg, int value) { + if (value == min_jint) { addq(reg, value); return; } + if (value < 0) { decrementq(reg, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { incq(reg) ; return; } + /* else */ { addq(reg, value) ; return; } +} + +void MacroAssembler::decrementq(Register reg, int value) { + if (value == min_jint) { subq(reg, value); return; } + if (value < 0) { incrementq(reg, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { decq(reg) ; return; } + /* else */ { subq(reg, value) ; return; } +} + +void MacroAssembler::incrementl(Address dst, int value) { + if (value == min_jint) { addl(dst, value); return; } + if (value < 0) { decrementl(dst, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { incl(dst) ; return; } + /* else */ { addl(dst, value) ; return; } +} + +void MacroAssembler::decrementl(Address dst, int value) { + if (value == min_jint) { subl(dst, value); return; } + if (value < 0) { incrementl(dst, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { decl(dst) ; return; } + /* else */ { subl(dst, value) ; return; } +} + +void MacroAssembler::incrementq(Address dst, int value) { + if (value == min_jint) { addq(dst, value); return; } + if (value < 0) { decrementq(dst, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { incq(dst) ; return; } + /* else */ { addq(dst, value) ; return; } +} + +void MacroAssembler::decrementq(Address dst, int value) { + if (value == min_jint) { subq(dst, value); return; } + if (value < 0) { incrementq(dst, -value); return; } + if (value == 0) { ; return; } + if (value == 1 && UseIncDec) { decq(dst) ; return; } + /* else */ { subq(dst, value) ; return; } +} + +void MacroAssembler::align(int modulus) { + if (offset() % modulus != 0) { + nop(modulus - (offset() % modulus)); + } +} + +void MacroAssembler::enter() { + pushq(rbp); + movq(rbp, rsp); +} + +void MacroAssembler::leave() { + emit_byte(0xC9); // LEAVE +} + +// C++ bool manipulation + +void MacroAssembler::movbool(Register dst, Address src) { + if(sizeof(bool) == 1) + movb(dst, src); + else if(sizeof(bool) == 2) + movw(dst, src); + else if(sizeof(bool) == 4) + movl(dst, src); + else { + // unsupported + ShouldNotReachHere(); + } +} + +void MacroAssembler::movbool(Address dst, bool boolconst) { + if(sizeof(bool) == 1) + movb(dst, (int) boolconst); + else if(sizeof(bool) == 2) + movw(dst, (int) boolconst); + else if(sizeof(bool) == 4) + movl(dst, (int) boolconst); + else { + // unsupported + ShouldNotReachHere(); + } +} + +void MacroAssembler::movbool(Address dst, Register src) { + if(sizeof(bool) == 1) + movb(dst, src); + else if(sizeof(bool) == 2) + movw(dst, src); + else if(sizeof(bool) == 4) + movl(dst, src); + else { + // unsupported + ShouldNotReachHere(); + } +} + +void MacroAssembler::testbool(Register dst) { + if(sizeof(bool) == 1) + testb(dst, (int) 0xff); + else if(sizeof(bool) == 2) { + // need testw impl + ShouldNotReachHere(); + } else if(sizeof(bool) == 4) + testl(dst, dst); + else { + // unsupported + ShouldNotReachHere(); + } +} + +void MacroAssembler::set_last_Java_frame(Register last_java_sp, + Register last_java_fp, + address last_java_pc) { + // determine last_java_sp register + if (!last_java_sp->is_valid()) { + last_java_sp = rsp; + } + + // last_java_fp is optional + if (last_java_fp->is_valid()) { + movq(Address(r15_thread, JavaThread::last_Java_fp_offset()), + last_java_fp); + } + + // last_java_pc is optional + if (last_java_pc != NULL) { + Address java_pc(r15_thread, + JavaThread::frame_anchor_offset() + JavaFrameAnchor::last_Java_pc_offset()); + lea(rscratch1, InternalAddress(last_java_pc)); + movq(java_pc, rscratch1); + } + + movq(Address(r15_thread, JavaThread::last_Java_sp_offset()), last_java_sp); +} + +void MacroAssembler::reset_last_Java_frame(bool clear_fp, + bool clear_pc) { + // we must set sp to zero to clear frame + movptr(Address(r15_thread, JavaThread::last_Java_sp_offset()), NULL_WORD); + // must clear fp, so that compiled frames are not confused; it is + // possible that we need it only for debugging + if (clear_fp) { + movptr(Address(r15_thread, JavaThread::last_Java_fp_offset()), NULL_WORD); + } + + if (clear_pc) { + movptr(Address(r15_thread, JavaThread::last_Java_pc_offset()), NULL_WORD); + } +} + + +// Implementation of call_VM versions + +void MacroAssembler::call_VM_leaf_base(address entry_point, int num_args) { + Label L, E; + +#ifdef _WIN64 + // Windows always allocates space for it's register args + assert(num_args <= 4, "only register arguments supported"); + subq(rsp, frame::arg_reg_save_area_bytes); +#endif + + // Align stack if necessary + testl(rsp, 15); + jcc(Assembler::zero, L); + + subq(rsp, 8); + { + call(RuntimeAddress(entry_point)); + } + addq(rsp, 8); + jmp(E); + + bind(L); + { + call(RuntimeAddress(entry_point)); + } + + bind(E); + +#ifdef _WIN64 + // restore stack pointer + addq(rsp, frame::arg_reg_save_area_bytes); +#endif + +} + + +void MacroAssembler::call_VM_base(Register oop_result, + Register java_thread, + Register last_java_sp, + address entry_point, + int num_args, + bool check_exceptions) { + // determine last_java_sp register + if (!last_java_sp->is_valid()) { + last_java_sp = rsp; + } + + // debugging support + assert(num_args >= 0, "cannot have negative number of arguments"); + assert(r15_thread != oop_result, + "cannot use the same register for java_thread & oop_result"); + assert(r15_thread != last_java_sp, + "cannot use the same register for java_thread & last_java_sp"); + + // set last Java frame before call + + // This sets last_Java_fp which is only needed from interpreted frames + // and should really be done only from the interp_masm version before + // calling the underlying call_VM. That doesn't happen yet so we set + // last_Java_fp here even though some callers don't need it and + // also clear it below. + set_last_Java_frame(last_java_sp, rbp, NULL); + + { + Label L, E; + + // Align stack if necessary +#ifdef _WIN64 + assert(num_args <= 4, "only register arguments supported"); + // Windows always allocates space for it's register args + subq(rsp, frame::arg_reg_save_area_bytes); +#endif + testl(rsp, 15); + jcc(Assembler::zero, L); + + subq(rsp, 8); + { + call(RuntimeAddress(entry_point)); + } + addq(rsp, 8); + jmp(E); + + + bind(L); + { + call(RuntimeAddress(entry_point)); + } + + bind(E); + +#ifdef _WIN64 + // restore stack pointer + addq(rsp, frame::arg_reg_save_area_bytes); +#endif + } + +#ifdef ASSERT + pushq(rax); + { + Label L; + get_thread(rax); + cmpq(r15_thread, rax); + jcc(Assembler::equal, L); + stop("MacroAssembler::call_VM_base: register not callee saved?"); + bind(L); + } + popq(rax); +#endif + + // reset last Java frame + // This really shouldn't have to clear fp set note above at the + // call to set_last_Java_frame + reset_last_Java_frame(true, false); + + check_and_handle_popframe(noreg); + check_and_handle_earlyret(noreg); + + if (check_exceptions) { + cmpq(Address(r15_thread, Thread::pending_exception_offset()), (int) NULL); + // This used to conditionally jump to forward_exception however it is + // possible if we relocate that the branch will not reach. So we must jump + // around so we can always reach + Label ok; + jcc(Assembler::equal, ok); + jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + bind(ok); + } + + // get oop result if there is one and reset the value in the thread + if (oop_result->is_valid()) { + movq(oop_result, Address(r15_thread, JavaThread::vm_result_offset())); + movptr(Address(r15_thread, JavaThread::vm_result_offset()), NULL_WORD); + verify_oop(oop_result); + } +} + +void MacroAssembler::check_and_handle_popframe(Register java_thread) {} +void MacroAssembler::check_and_handle_earlyret(Register java_thread) {} + +void MacroAssembler::call_VM_helper(Register oop_result, + address entry_point, + int num_args, + bool check_exceptions) { + // Java thread becomes first argument of C function + movq(c_rarg0, r15_thread); + + // We've pushed one address, correct last_Java_sp + leaq(rax, Address(rsp, wordSize)); + + call_VM_base(oop_result, noreg, rax, entry_point, num_args, + check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, + address entry_point, + bool check_exceptions) { + Label C, E; + Assembler::call(C, relocInfo::none); + jmp(E); + + bind(C); + call_VM_helper(oop_result, entry_point, 0, check_exceptions); + ret(0); + + bind(E); +} + + +void MacroAssembler::call_VM(Register oop_result, + address entry_point, + Register arg_1, + bool check_exceptions) { + assert(rax != arg_1, "smashed argument"); + assert(c_rarg0 != arg_1, "smashed argument"); + + Label C, E; + Assembler::call(C, relocInfo::none); + jmp(E); + + bind(C); + // c_rarg0 is reserved for thread + if (c_rarg1 != arg_1) { + movq(c_rarg1, arg_1); + } + call_VM_helper(oop_result, entry_point, 1, check_exceptions); + ret(0); + + bind(E); +} + +void MacroAssembler::call_VM(Register oop_result, + address entry_point, + Register arg_1, + Register arg_2, + bool check_exceptions) { + assert(rax != arg_1, "smashed argument"); + assert(rax != arg_2, "smashed argument"); + assert(c_rarg0 != arg_1, "smashed argument"); + assert(c_rarg0 != arg_2, "smashed argument"); + assert(c_rarg1 != arg_2, "smashed argument"); + assert(c_rarg2 != arg_1, "smashed argument"); + + Label C, E; + Assembler::call(C, relocInfo::none); + jmp(E); + + bind(C); + // c_rarg0 is reserved for thread + if (c_rarg1 != arg_1) { + movq(c_rarg1, arg_1); + } + if (c_rarg2 != arg_2) { + movq(c_rarg2, arg_2); + } + call_VM_helper(oop_result, entry_point, 2, check_exceptions); + ret(0); + + bind(E); +} + + +void MacroAssembler::call_VM(Register oop_result, + address entry_point, + Register arg_1, + Register arg_2, + Register arg_3, + bool check_exceptions) { + assert(rax != arg_1, "smashed argument"); + assert(rax != arg_2, "smashed argument"); + assert(rax != arg_3, "smashed argument"); + assert(c_rarg0 != arg_1, "smashed argument"); + assert(c_rarg0 != arg_2, "smashed argument"); + assert(c_rarg0 != arg_3, "smashed argument"); + assert(c_rarg1 != arg_2, "smashed argument"); + assert(c_rarg1 != arg_3, "smashed argument"); + assert(c_rarg2 != arg_1, "smashed argument"); + assert(c_rarg2 != arg_3, "smashed argument"); + assert(c_rarg3 != arg_1, "smashed argument"); + assert(c_rarg3 != arg_2, "smashed argument"); + + Label C, E; + Assembler::call(C, relocInfo::none); + jmp(E); + + bind(C); + // c_rarg0 is reserved for thread + if (c_rarg1 != arg_1) { + movq(c_rarg1, arg_1); + } + if (c_rarg2 != arg_2) { + movq(c_rarg2, arg_2); + } + if (c_rarg3 != arg_3) { + movq(c_rarg3, arg_3); + } + call_VM_helper(oop_result, entry_point, 3, check_exceptions); + ret(0); + + bind(E); +} + +void MacroAssembler::call_VM(Register oop_result, + Register last_java_sp, + address entry_point, + int num_args, + bool check_exceptions) { + call_VM_base(oop_result, noreg, last_java_sp, entry_point, num_args, + check_exceptions); +} + +void MacroAssembler::call_VM(Register oop_result, + Register last_java_sp, + address entry_point, + Register arg_1, + bool check_exceptions) { + assert(c_rarg0 != arg_1, "smashed argument"); + assert(c_rarg1 != last_java_sp, "smashed argument"); + // c_rarg0 is reserved for thread + if (c_rarg1 != arg_1) { + movq(c_rarg1, arg_1); + } + call_VM(oop_result, last_java_sp, entry_point, 1, check_exceptions); +} + +void MacroAssembler::call_VM(Register oop_result, + Register last_java_sp, + address entry_point, + Register arg_1, + Register arg_2, + bool check_exceptions) { + assert(c_rarg0 != arg_1, "smashed argument"); + assert(c_rarg0 != arg_2, "smashed argument"); + assert(c_rarg1 != arg_2, "smashed argument"); + assert(c_rarg1 != last_java_sp, "smashed argument"); + assert(c_rarg2 != arg_1, "smashed argument"); + assert(c_rarg2 != last_java_sp, "smashed argument"); + // c_rarg0 is reserved for thread + if (c_rarg1 != arg_1) { + movq(c_rarg1, arg_1); + } + if (c_rarg2 != arg_2) { + movq(c_rarg2, arg_2); + } + call_VM(oop_result, last_java_sp, entry_point, 2, check_exceptions); +} + + +void MacroAssembler::call_VM(Register oop_result, + Register last_java_sp, + address entry_point, + Register arg_1, + Register arg_2, + Register arg_3, + bool check_exceptions) { + assert(c_rarg0 != arg_1, "smashed argument"); + assert(c_rarg0 != arg_2, "smashed argument"); + assert(c_rarg0 != arg_3, "smashed argument"); + assert(c_rarg1 != arg_2, "smashed argument"); + assert(c_rarg1 != arg_3, "smashed argument"); + assert(c_rarg1 != last_java_sp, "smashed argument"); + assert(c_rarg2 != arg_1, "smashed argument"); + assert(c_rarg2 != arg_3, "smashed argument"); + assert(c_rarg2 != last_java_sp, "smashed argument"); + assert(c_rarg3 != arg_1, "smashed argument"); + assert(c_rarg3 != arg_2, "smashed argument"); + assert(c_rarg3 != last_java_sp, "smashed argument"); + // c_rarg0 is reserved for thread + if (c_rarg1 != arg_1) { + movq(c_rarg1, arg_1); + } + if (c_rarg2 != arg_2) { + movq(c_rarg2, arg_2); + } + if (c_rarg3 != arg_3) { + movq(c_rarg2, arg_3); + } + call_VM(oop_result, last_java_sp, entry_point, 3, check_exceptions); +} + +void MacroAssembler::call_VM_leaf(address entry_point, int num_args) { + call_VM_leaf_base(entry_point, num_args); +} + +void MacroAssembler::call_VM_leaf(address entry_point, Register arg_1) { + if (c_rarg0 != arg_1) { + movq(c_rarg0, arg_1); + } + call_VM_leaf(entry_point, 1); +} + +void MacroAssembler::call_VM_leaf(address entry_point, + Register arg_1, + Register arg_2) { + assert(c_rarg0 != arg_2, "smashed argument"); + assert(c_rarg1 != arg_1, "smashed argument"); + if (c_rarg0 != arg_1) { + movq(c_rarg0, arg_1); + } + if (c_rarg1 != arg_2) { + movq(c_rarg1, arg_2); + } + call_VM_leaf(entry_point, 2); +} + +void MacroAssembler::call_VM_leaf(address entry_point, + Register arg_1, + Register arg_2, + Register arg_3) { + assert(c_rarg0 != arg_2, "smashed argument"); + assert(c_rarg0 != arg_3, "smashed argument"); + assert(c_rarg1 != arg_1, "smashed argument"); + assert(c_rarg1 != arg_3, "smashed argument"); + assert(c_rarg2 != arg_1, "smashed argument"); + assert(c_rarg2 != arg_2, "smashed argument"); + if (c_rarg0 != arg_1) { + movq(c_rarg0, arg_1); + } + if (c_rarg1 != arg_2) { + movq(c_rarg1, arg_2); + } + if (c_rarg2 != arg_3) { + movq(c_rarg2, arg_3); + } + call_VM_leaf(entry_point, 3); +} + + +// Calls to C land +// +// When entering C land, the rbp & rsp of the last Java frame have to +// be recorded in the (thread-local) JavaThread object. When leaving C +// land, the last Java fp has to be reset to 0. This is required to +// allow proper stack traversal. +void MacroAssembler::store_check(Register obj) { + // Does a store check for the oop in register obj. The content of + // register obj is destroyed afterwards. + store_check_part_1(obj); + store_check_part_2(obj); +} + +void MacroAssembler::store_check(Register obj, Address dst) { + store_check(obj); +} + +// split the store check operation so that other instructions can be +// scheduled inbetween +void MacroAssembler::store_check_part_1(Register obj) { + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); + shrq(obj, CardTableModRefBS::card_shift); +} + +void MacroAssembler::store_check_part_2(Register obj) { + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); + CardTableModRefBS* ct = (CardTableModRefBS*)bs; + assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); + ExternalAddress cardtable((address)ct->byte_map_base); + Address index(noreg, obj, Address::times_1); + movb(as_Address(ArrayAddress(cardtable, index)), 0); +} + +void MacroAssembler::c2bool(Register x) { + // implements x == 0 ? 0 : 1 + // note: must only look at least-significant byte of x + // since C-style booleans are stored in one byte + // only! (was bug) + andl(x, 0xFF); + setb(Assembler::notZero, x); +} + +int MacroAssembler::corrected_idivl(Register reg) { + // Full implementation of Java idiv and irem; checks for special + // case as described in JVM spec., p.243 & p.271. The function + // returns the (pc) offset of the idivl instruction - may be needed + // for implicit exceptions. + // + // normal case special case + // + // input : eax: dividend min_int + // reg: divisor (may not be eax/edx) -1 + // + // output: eax: quotient (= eax idiv reg) min_int + // edx: remainder (= eax irem reg) 0 + assert(reg != rax && reg != rdx, "reg cannot be rax or rdx register"); + const int min_int = 0x80000000; + Label normal_case, special_case; + + // check for special case + cmpl(rax, min_int); + jcc(Assembler::notEqual, normal_case); + xorl(rdx, rdx); // prepare edx for possible special case (where + // remainder = 0) + cmpl(reg, -1); + jcc(Assembler::equal, special_case); + + // handle normal case + bind(normal_case); + cdql(); + int idivl_offset = offset(); + idivl(reg); + + // normal and special case exit + bind(special_case); + + return idivl_offset; +} + +int MacroAssembler::corrected_idivq(Register reg) { + // Full implementation of Java ldiv and lrem; checks for special + // case as described in JVM spec., p.243 & p.271. The function + // returns the (pc) offset of the idivl instruction - may be needed + // for implicit exceptions. + // + // normal case special case + // + // input : rax: dividend min_long + // reg: divisor (may not be eax/edx) -1 + // + // output: rax: quotient (= rax idiv reg) min_long + // rdx: remainder (= rax irem reg) 0 + assert(reg != rax && reg != rdx, "reg cannot be rax or rdx register"); + static const int64_t min_long = 0x8000000000000000; + Label normal_case, special_case; + + // check for special case + cmp64(rax, ExternalAddress((address) &min_long)); + jcc(Assembler::notEqual, normal_case); + xorl(rdx, rdx); // prepare rdx for possible special case (where + // remainder = 0) + cmpq(reg, -1); + jcc(Assembler::equal, special_case); + + // handle normal case + bind(normal_case); + cdqq(); + int idivq_offset = offset(); + idivq(reg); + + // normal and special case exit + bind(special_case); + + return idivq_offset; +} + +void MacroAssembler::push_IU_state() { + pushfq(); // Push flags first because pushaq kills them + subq(rsp, 8); // Make sure rsp stays 16-byte aligned + pushaq(); +} + +void MacroAssembler::pop_IU_state() { + popaq(); + addq(rsp, 8); + popfq(); +} + +void MacroAssembler::push_FPU_state() { + subq(rsp, FPUStateSizeInWords * wordSize); + fxsave(Address(rsp, 0)); +} + +void MacroAssembler::pop_FPU_state() { + fxrstor(Address(rsp, 0)); + addq(rsp, FPUStateSizeInWords * wordSize); +} + +// Save Integer and Float state +// Warning: Stack must be 16 byte aligned +void MacroAssembler::push_CPU_state() { + push_IU_state(); + push_FPU_state(); +} + +void MacroAssembler::pop_CPU_state() { + pop_FPU_state(); + pop_IU_state(); +} + +void MacroAssembler::sign_extend_short(Register reg) { + movswl(reg, reg); +} + +void MacroAssembler::sign_extend_byte(Register reg) { + movsbl(reg, reg); +} + +void MacroAssembler::division_with_shift(Register reg, int shift_value) { + assert (shift_value > 0, "illegal shift value"); + Label _is_positive; + testl (reg, reg); + jcc (Assembler::positive, _is_positive); + int offset = (1 << shift_value) - 1 ; + + if (offset == 1) { + incrementl(reg); + } else { + addl(reg, offset); + } + + bind (_is_positive); + sarl(reg, shift_value); +} + +void MacroAssembler::round_to_l(Register reg, int modulus) { + addl(reg, modulus - 1); + andl(reg, -modulus); +} + +void MacroAssembler::round_to_q(Register reg, int modulus) { + addq(reg, modulus - 1); + andq(reg, -modulus); +} + +void MacroAssembler::verify_oop(Register reg, const char* s) { + if (!VerifyOops) { + return; + } + + // Pass register number to verify_oop_subroutine + char* b = new char[strlen(s) + 50]; + sprintf(b, "verify_oop: %s: %s", reg->name(), s); + + pushq(rax); // save rax, restored by receiver + + // pass args on stack, only touch rax + pushq(reg); + + // avoid using pushptr, as it modifies scratch registers + // and our contract is not to modify anything + ExternalAddress buffer((address)b); + movptr(rax, buffer.addr()); + pushq(rax); + + // call indirectly to solve generation ordering problem + movptr(rax, ExternalAddress(StubRoutines::verify_oop_subroutine_entry_address())); + call(rax); // no alignment requirement + // everything popped by receiver +} + +void MacroAssembler::verify_oop_addr(Address addr, const char* s) { + if (!VerifyOops) return; + // Pass register number to verify_oop_subroutine + char* b = new char[strlen(s) + 50]; + sprintf(b, "verify_oop_addr: %s", s); + pushq(rax); // save rax + movq(addr, rax); + pushq(rax); // pass register argument + + + // avoid using pushptr, as it modifies scratch registers + // and our contract is not to modify anything + ExternalAddress buffer((address)b); + movptr(rax, buffer.addr()); + pushq(rax); + + // call indirectly to solve generation ordering problem + movptr(rax, ExternalAddress(StubRoutines::verify_oop_subroutine_entry_address())); + call(rax); // no alignment requirement + // everything popped by receiver +} + + +void MacroAssembler::stop(const char* msg) { + address rip = pc(); + pushaq(); // get regs on stack + lea(c_rarg0, ExternalAddress((address) msg)); + lea(c_rarg1, InternalAddress(rip)); + movq(c_rarg2, rsp); // pass pointer to regs array + andq(rsp, -16); // align stack as required by ABI + call(RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::debug))); + hlt(); +} + +void MacroAssembler::warn(const char* msg) { + pushq(r12); + movq(r12, rsp); + andq(rsp, -16); // align stack as required by push_CPU_state and call + + push_CPU_state(); // keeps alignment at 16 bytes + lea(c_rarg0, ExternalAddress((address) msg)); + call_VM_leaf(CAST_FROM_FN_PTR(address, warning), c_rarg0); + pop_CPU_state(); + + movq(rsp, r12); + popq(r12); +} + +void MacroAssembler::debug(char* msg, int64_t pc, int64_t regs[]) { + // In order to get locks to work, we need to fake a in_VM state + if (ShowMessageBoxOnError ) { + JavaThread* thread = JavaThread::current(); + JavaThreadState saved_state = thread->thread_state(); + thread->set_thread_state(_thread_in_vm); + ttyLocker ttyl; +#ifndef PRODUCT + if (CountBytecodes || TraceBytecodes || StopInterpreterAt) { + BytecodeCounter::print(); + } +#endif + // To see where a verify_oop failed, get $ebx+40/X for this frame. + // XXX correct this offset for amd64 + // This is the value of eip which points to where verify_oop will return. + if (os::message_box(msg, "Execution stopped, print registers?")) { + tty->print_cr("rip = 0x%016lx", pc); + tty->print_cr("rax = 0x%016lx", regs[15]); + tty->print_cr("rbx = 0x%016lx", regs[12]); + tty->print_cr("rcx = 0x%016lx", regs[14]); + tty->print_cr("rdx = 0x%016lx", regs[13]); + tty->print_cr("rdi = 0x%016lx", regs[8]); + tty->print_cr("rsi = 0x%016lx", regs[9]); + tty->print_cr("rbp = 0x%016lx", regs[10]); + tty->print_cr("rsp = 0x%016lx", regs[11]); + tty->print_cr("r8 = 0x%016lx", regs[7]); + tty->print_cr("r9 = 0x%016lx", regs[6]); + tty->print_cr("r10 = 0x%016lx", regs[5]); + tty->print_cr("r11 = 0x%016lx", regs[4]); + tty->print_cr("r12 = 0x%016lx", regs[3]); + tty->print_cr("r13 = 0x%016lx", regs[2]); + tty->print_cr("r14 = 0x%016lx", regs[1]); + tty->print_cr("r15 = 0x%016lx", regs[0]); + BREAKPOINT; + } + ThreadStateTransition::transition(thread, _thread_in_vm, saved_state); + } else { + ::tty->print_cr("=============== DEBUG MESSAGE: %s ================\n", + msg); + } +} + +void MacroAssembler::os_breakpoint() { + // instead of directly emitting a breakpoint, call os:breakpoint for + // better debugability + // This shouldn't need alignment, it's an empty function + call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); +} + +// Write serialization page so VM thread can do a pseudo remote membar. +// We use the current thread pointer to calculate a thread specific +// offset to write to within the page. This minimizes bus traffic +// due to cache line collision. +void MacroAssembler::serialize_memory(Register thread, + Register tmp) { + + movl(tmp, thread); + shrl(tmp, os::get_serialize_page_shift_count()); + andl(tmp, (os::vm_page_size() - sizeof(int))); + + Address index(noreg, tmp, Address::times_1); + ExternalAddress page(os::get_memory_serialize_page()); + + movptr(ArrayAddress(page, index), tmp); +} + +void MacroAssembler::verify_tlab() { +#ifdef ASSERT + if (UseTLAB) { + Label next, ok; + Register t1 = rsi; + + pushq(t1); + + movq(t1, Address(r15_thread, in_bytes(JavaThread::tlab_top_offset()))); + cmpq(t1, Address(r15_thread, in_bytes(JavaThread::tlab_start_offset()))); + jcc(Assembler::aboveEqual, next); + stop("assert(top >= start)"); + should_not_reach_here(); + + bind(next); + movq(t1, Address(r15_thread, in_bytes(JavaThread::tlab_end_offset()))); + cmpq(t1, Address(r15_thread, in_bytes(JavaThread::tlab_top_offset()))); + jcc(Assembler::aboveEqual, ok); + stop("assert(top <= end)"); + should_not_reach_here(); + + bind(ok); + + popq(t1); + } +#endif +} + +// Defines obj, preserves var_size_in_bytes +void MacroAssembler::eden_allocate(Register obj, + Register var_size_in_bytes, + int con_size_in_bytes, + Register t1, + Label& slow_case) { + assert(obj == rax, "obj must be in rax for cmpxchg"); + assert_different_registers(obj, var_size_in_bytes, t1); + Register end = t1; + Label retry; + bind(retry); + ExternalAddress heap_top((address) Universe::heap()->top_addr()); + movptr(obj, heap_top); + if (var_size_in_bytes == noreg) { + leaq(end, Address(obj, con_size_in_bytes)); + } else { + leaq(end, Address(obj, var_size_in_bytes, Address::times_1)); + } + // if end < obj then we wrapped around => object too long => slow case + cmpq(end, obj); + jcc(Assembler::below, slow_case); + cmpptr(end, ExternalAddress((address) Universe::heap()->end_addr())); + + jcc(Assembler::above, slow_case); + // Compare obj with the top addr, and if still equal, store the new + // top addr in end at the address of the top addr pointer. Sets ZF + // if was equal, and clears it otherwise. Use lock prefix for + // atomicity on MPs. + if (os::is_MP()) { + lock(); + } + cmpxchgptr(end, heap_top); + // if someone beat us on the allocation, try again, otherwise continue + jcc(Assembler::notEqual, retry); +} + +// Defines obj, preserves var_size_in_bytes, okay for t2 == var_size_in_bytes. +void MacroAssembler::tlab_allocate(Register obj, + Register var_size_in_bytes, + int con_size_in_bytes, + Register t1, + Register t2, + Label& slow_case) { + assert_different_registers(obj, t1, t2); + assert_different_registers(obj, var_size_in_bytes, t1); + Register end = t2; + + verify_tlab(); + + movq(obj, Address(r15_thread, JavaThread::tlab_top_offset())); + if (var_size_in_bytes == noreg) { + leaq(end, Address(obj, con_size_in_bytes)); + } else { + leaq(end, Address(obj, var_size_in_bytes, Address::times_1)); + } + cmpq(end, Address(r15_thread, JavaThread::tlab_end_offset())); + jcc(Assembler::above, slow_case); + + // update the tlab top pointer + movq(Address(r15_thread, JavaThread::tlab_top_offset()), end); + + // recover var_size_in_bytes if necessary + if (var_size_in_bytes == end) { + subq(var_size_in_bytes, obj); + } + verify_tlab(); +} + +// Preserves rbx and rdx. +void MacroAssembler::tlab_refill(Label& retry, + Label& try_eden, + Label& slow_case) { + Register top = rax; + Register t1 = rcx; + Register t2 = rsi; + Register t3 = r10; + Register thread_reg = r15_thread; + assert_different_registers(top, thread_reg, t1, t2, t3, + /* preserve: */ rbx, rdx); + Label do_refill, discard_tlab; + + if (CMSIncrementalMode || !Universe::heap()->supports_inline_contig_alloc()) { + // No allocation in the shared eden. + jmp(slow_case); + } + + movq(top, Address(thread_reg, in_bytes(JavaThread::tlab_top_offset()))); + movq(t1, Address(thread_reg, in_bytes(JavaThread::tlab_end_offset()))); + + // calculate amount of free space + subq(t1, top); + shrq(t1, LogHeapWordSize); + + // Retain tlab and allocate object in shared space if + // the amount free in the tlab is too large to discard. + cmpq(t1, Address(thread_reg, // size_t + in_bytes(JavaThread::tlab_refill_waste_limit_offset()))); + jcc(Assembler::lessEqual, discard_tlab); + + // Retain + mov64(t2, ThreadLocalAllocBuffer::refill_waste_limit_increment()); + addq(Address(thread_reg, // size_t + in_bytes(JavaThread::tlab_refill_waste_limit_offset())), + t2); + if (TLABStats) { + // increment number of slow_allocations + addl(Address(thread_reg, // unsigned int + in_bytes(JavaThread::tlab_slow_allocations_offset())), + 1); + } + jmp(try_eden); + + bind(discard_tlab); + if (TLABStats) { + // increment number of refills + addl(Address(thread_reg, // unsigned int + in_bytes(JavaThread::tlab_number_of_refills_offset())), + 1); + // accumulate wastage -- t1 is amount free in tlab + addl(Address(thread_reg, // unsigned int + in_bytes(JavaThread::tlab_fast_refill_waste_offset())), + t1); + } + + // if tlab is currently allocated (top or end != null) then + // fill [top, end + alignment_reserve) with array object + testq(top, top); + jcc(Assembler::zero, do_refill); + + // set up the mark word + mov64(t3, (int64_t) markOopDesc::prototype()->copy_set_hash(0x2)); + movq(Address(top, oopDesc::mark_offset_in_bytes()), t3); + // set the length to the remaining space + subq(t1, typeArrayOopDesc::header_size(T_INT)); + addq(t1, (int)ThreadLocalAllocBuffer::alignment_reserve()); + shlq(t1, log2_intptr(HeapWordSize / sizeof(jint))); + movq(Address(top, arrayOopDesc::length_offset_in_bytes()), t1); + // set klass to intArrayKlass + movptr(t1, ExternalAddress((address) Universe::intArrayKlassObj_addr())); + movq(Address(top, oopDesc::klass_offset_in_bytes()), t1); + + // refill the tlab with an eden allocation + bind(do_refill); + movq(t1, Address(thread_reg, in_bytes(JavaThread::tlab_size_offset()))); + shlq(t1, LogHeapWordSize); + // add object_size ?? + eden_allocate(top, t1, 0, t2, slow_case); + + // Check that t1 was preserved in eden_allocate. +#ifdef ASSERT + if (UseTLAB) { + Label ok; + Register tsize = rsi; + assert_different_registers(tsize, thread_reg, t1); + pushq(tsize); + movq(tsize, Address(thread_reg, in_bytes(JavaThread::tlab_size_offset()))); + shlq(tsize, LogHeapWordSize); + cmpq(t1, tsize); + jcc(Assembler::equal, ok); + stop("assert(t1 != tlab size)"); + should_not_reach_here(); + + bind(ok); + popq(tsize); + } +#endif + movq(Address(thread_reg, in_bytes(JavaThread::tlab_start_offset())), top); + movq(Address(thread_reg, in_bytes(JavaThread::tlab_top_offset())), top); + addq(top, t1); + subq(top, (int)ThreadLocalAllocBuffer::alignment_reserve_in_bytes()); + movq(Address(thread_reg, in_bytes(JavaThread::tlab_end_offset())), top); + verify_tlab(); + jmp(retry); +} + + +int MacroAssembler::biased_locking_enter(Register lock_reg, Register obj_reg, Register swap_reg, Register tmp_reg, + bool swap_reg_contains_mark, + Label& done, Label* slow_case, + BiasedLockingCounters* counters) { + assert(UseBiasedLocking, "why call this otherwise?"); + assert(swap_reg == rax, "swap_reg must be rax for cmpxchgq"); + assert(tmp_reg != noreg, "tmp_reg must be supplied"); + assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg); + assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, "biased locking makes assumptions about bit layout"); + Address mark_addr (obj_reg, oopDesc::mark_offset_in_bytes()); + Address klass_addr (obj_reg, oopDesc::klass_offset_in_bytes()); + Address saved_mark_addr(lock_reg, 0); + + if (PrintBiasedLockingStatistics && counters == NULL) + counters = BiasedLocking::counters(); + + // Biased locking + // See whether the lock is currently biased toward our thread and + // whether the epoch is still valid + // Note that the runtime guarantees sufficient alignment of JavaThread + // pointers to allow age to be placed into low bits + // First check to see whether biasing is even enabled for this object + Label cas_label; + int null_check_offset = -1; + if (!swap_reg_contains_mark) { + null_check_offset = offset(); + movq(swap_reg, mark_addr); + } + movq(tmp_reg, swap_reg); + andq(tmp_reg, markOopDesc::biased_lock_mask_in_place); + cmpq(tmp_reg, markOopDesc::biased_lock_pattern); + jcc(Assembler::notEqual, cas_label); + // The bias pattern is present in the object's header. Need to check + // whether the bias owner and the epoch are both still current. + movq(tmp_reg, klass_addr); + movq(tmp_reg, Address(tmp_reg, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes())); + orq(tmp_reg, r15_thread); + xorq(tmp_reg, swap_reg); + andq(tmp_reg, ~((int) markOopDesc::age_mask_in_place)); + if (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address) counters->anonymously_biased_lock_entry_count_addr())); + } + jcc(Assembler::equal, done); + + Label try_revoke_bias; + Label try_rebias; + + // At this point we know that the header has the bias pattern and + // that we are not the bias owner in the current epoch. We need to + // figure out more details about the state of the header in order to + // know what operations can be legally performed on the object's + // header. + + // If the low three bits in the xor result aren't clear, that means + // the prototype header is no longer biased and we have to revoke + // the bias on this object. + testq(tmp_reg, markOopDesc::biased_lock_mask_in_place); + jcc(Assembler::notZero, try_revoke_bias); + + // Biasing is still enabled for this data type. See whether the + // epoch of the current bias is still valid, meaning that the epoch + // bits of the mark word are equal to the epoch bits of the + // prototype header. (Note that the prototype header's epoch bits + // only change at a safepoint.) If not, attempt to rebias the object + // toward the current thread. Note that we must be absolutely sure + // that the current epoch is invalid in order to do this because + // otherwise the manipulations it performs on the mark word are + // illegal. + testq(tmp_reg, markOopDesc::epoch_mask_in_place); + jcc(Assembler::notZero, try_rebias); + + // The epoch of the current bias is still valid but we know nothing + // about the owner; it might be set or it might be clear. Try to + // acquire the bias of the object using an atomic operation. If this + // fails we will go in to the runtime to revoke the object's bias. + // Note that we first construct the presumed unbiased header so we + // don't accidentally blow away another thread's valid bias. + andq(swap_reg, + markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place); + movq(tmp_reg, swap_reg); + orq(tmp_reg, r15_thread); + if (os::is_MP()) { + lock(); + } + cmpxchgq(tmp_reg, Address(obj_reg, 0)); + // If the biasing toward our thread failed, this means that + // another thread succeeded in biasing it toward itself and we + // need to revoke that bias. The revocation will occur in the + // interpreter runtime in the slow case. + if (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address) counters->anonymously_biased_lock_entry_count_addr())); + } + if (slow_case != NULL) { + jcc(Assembler::notZero, *slow_case); + } + jmp(done); + + bind(try_rebias); + // At this point we know the epoch has expired, meaning that the + // current "bias owner", if any, is actually invalid. Under these + // circumstances _only_, we are allowed to use the current header's + // value as the comparison value when doing the cas to acquire the + // bias in the current epoch. In other words, we allow transfer of + // the bias from one thread to another directly in this situation. + // + // FIXME: due to a lack of registers we currently blow away the age + // bits in this situation. Should attempt to preserve them. + movq(tmp_reg, klass_addr); + movq(tmp_reg, Address(tmp_reg, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes())); + orq(tmp_reg, r15_thread); + if (os::is_MP()) { + lock(); + } + cmpxchgq(tmp_reg, Address(obj_reg, 0)); + // If the biasing toward our thread failed, then another thread + // succeeded in biasing it toward itself and we need to revoke that + // bias. The revocation will occur in the runtime in the slow case. + if (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address) counters->rebiased_lock_entry_count_addr())); + } + if (slow_case != NULL) { + jcc(Assembler::notZero, *slow_case); + } + jmp(done); + + bind(try_revoke_bias); + // The prototype mark in the klass doesn't have the bias bit set any + // more, indicating that objects of this data type are not supposed + // to be biased any more. We are going to try to reset the mark of + // this object to the prototype value and fall through to the + // CAS-based locking scheme. Note that if our CAS fails, it means + // that another thread raced us for the privilege of revoking the + // bias of this particular object, so it's okay to continue in the + // normal locking code. + // + // FIXME: due to a lack of registers we currently blow away the age + // bits in this situation. Should attempt to preserve them. + movq(tmp_reg, klass_addr); + movq(tmp_reg, Address(tmp_reg, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes())); + if (os::is_MP()) { + lock(); + } + cmpxchgq(tmp_reg, Address(obj_reg, 0)); + // Fall through to the normal CAS-based lock, because no matter what + // the result of the above CAS, some thread must have succeeded in + // removing the bias bit from the object's header. + if (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address) counters->revoked_lock_entry_count_addr())); + } + + bind(cas_label); + + return null_check_offset; +} + + +void MacroAssembler::biased_locking_exit(Register obj_reg, Register temp_reg, Label& done) { + assert(UseBiasedLocking, "why call this otherwise?"); + + // Check for biased locking unlock case, which is a no-op + // Note: we do not have to check the thread ID for two reasons. + // First, the interpreter checks for IllegalMonitorStateException at + // a higher level. Second, if the bias was revoked while we held the + // lock, the object could not be rebiased toward another thread, so + // the bias bit would be clear. + movq(temp_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); + andq(temp_reg, markOopDesc::biased_lock_mask_in_place); + cmpq(temp_reg, markOopDesc::biased_lock_pattern); + jcc(Assembler::equal, done); +} + + +Assembler::Condition MacroAssembler::negate_condition(Assembler::Condition cond) { + switch (cond) { + // Note some conditions are synonyms for others + case Assembler::zero: return Assembler::notZero; + case Assembler::notZero: return Assembler::zero; + case Assembler::less: return Assembler::greaterEqual; + case Assembler::lessEqual: return Assembler::greater; + case Assembler::greater: return Assembler::lessEqual; + case Assembler::greaterEqual: return Assembler::less; + case Assembler::below: return Assembler::aboveEqual; + case Assembler::belowEqual: return Assembler::above; + case Assembler::above: return Assembler::belowEqual; + case Assembler::aboveEqual: return Assembler::below; + case Assembler::overflow: return Assembler::noOverflow; + case Assembler::noOverflow: return Assembler::overflow; + case Assembler::negative: return Assembler::positive; + case Assembler::positive: return Assembler::negative; + case Assembler::parity: return Assembler::noParity; + case Assembler::noParity: return Assembler::parity; + } + ShouldNotReachHere(); return Assembler::overflow; +} + + +void MacroAssembler::cond_inc32(Condition cond, AddressLiteral counter_addr) { + Condition negated_cond = negate_condition(cond); + Label L; + jcc(negated_cond, L); + atomic_incl(counter_addr); + bind(L); +} + +void MacroAssembler::atomic_incl(AddressLiteral counter_addr) { + pushfq(); + if (os::is_MP()) + lock(); + incrementl(counter_addr); + popfq(); +} + +SkipIfEqual::SkipIfEqual( + MacroAssembler* masm, const bool* flag_addr, bool value) { + _masm = masm; + _masm->cmp8(ExternalAddress((address)flag_addr), value); + _masm->jcc(Assembler::equal, _label); +} + +SkipIfEqual::~SkipIfEqual() { + _masm->bind(_label); +} + +void MacroAssembler::bang_stack_size(Register size, Register tmp) { + movq(tmp, rsp); + // Bang stack for total size given plus shadow page size. + // Bang one page at a time because large size can bang beyond yellow and + // red zones. + Label loop; + bind(loop); + movl(Address(tmp, (-os::vm_page_size())), size ); + subq(tmp, os::vm_page_size()); + subl(size, os::vm_page_size()); + jcc(Assembler::greater, loop); + + // Bang down shadow pages too. + // The -1 because we already subtracted 1 page. + for (int i = 0; i< StackShadowPages-1; i++) { + movq(Address(tmp, (-i*os::vm_page_size())), size ); + } +} diff --git a/hotspot/src/cpu/x86/vm/assembler_x86_64.hpp b/hotspot/src/cpu/x86/vm/assembler_x86_64.hpp new file mode 100644 index 00000000000..1fa47dd7146 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/assembler_x86_64.hpp @@ -0,0 +1,1447 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class BiasedLockingCounters; + +// Contains all the definitions needed for amd64 assembly code generation. + +#ifdef _LP64 +// Calling convention +class Argument VALUE_OBJ_CLASS_SPEC { + public: + enum { +#ifdef _WIN64 + n_int_register_parameters_c = 4, // rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) + n_float_register_parameters_c = 4, // xmm0 - xmm3 (c_farg0, c_farg1, ... ) +#else + n_int_register_parameters_c = 6, // rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...) + n_float_register_parameters_c = 8, // xmm0 - xmm7 (c_farg0, c_farg1, ... ) +#endif + n_int_register_parameters_j = 6, // j_rarg0, j_rarg1, ... + n_float_register_parameters_j = 8 // j_farg0, j_farg1, ... + }; +}; + + +// Symbolically name the register arguments used by the c calling convention. +// Windows is different from linux/solaris. So much for standards... + +#ifdef _WIN64 + +REGISTER_DECLARATION(Register, c_rarg0, rcx); +REGISTER_DECLARATION(Register, c_rarg1, rdx); +REGISTER_DECLARATION(Register, c_rarg2, r8); +REGISTER_DECLARATION(Register, c_rarg3, r9); + +REGISTER_DECLARATION(XMMRegister, c_farg0, xmm0); +REGISTER_DECLARATION(XMMRegister, c_farg1, xmm1); +REGISTER_DECLARATION(XMMRegister, c_farg2, xmm2); +REGISTER_DECLARATION(XMMRegister, c_farg3, xmm3); + +#else + +REGISTER_DECLARATION(Register, c_rarg0, rdi); +REGISTER_DECLARATION(Register, c_rarg1, rsi); +REGISTER_DECLARATION(Register, c_rarg2, rdx); +REGISTER_DECLARATION(Register, c_rarg3, rcx); +REGISTER_DECLARATION(Register, c_rarg4, r8); +REGISTER_DECLARATION(Register, c_rarg5, r9); + +REGISTER_DECLARATION(XMMRegister, c_farg0, xmm0); +REGISTER_DECLARATION(XMMRegister, c_farg1, xmm1); +REGISTER_DECLARATION(XMMRegister, c_farg2, xmm2); +REGISTER_DECLARATION(XMMRegister, c_farg3, xmm3); +REGISTER_DECLARATION(XMMRegister, c_farg4, xmm4); +REGISTER_DECLARATION(XMMRegister, c_farg5, xmm5); +REGISTER_DECLARATION(XMMRegister, c_farg6, xmm6); +REGISTER_DECLARATION(XMMRegister, c_farg7, xmm7); + +#endif + +// Symbolically name the register arguments used by the Java calling convention. +// We have control over the convention for java so we can do what we please. +// What pleases us is to offset the java calling convention so that when +// we call a suitable jni method the arguments are lined up and we don't +// have to do little shuffling. A suitable jni method is non-static and a +// small number of arguments (two fewer args on windows) +// +// |-------------------------------------------------------| +// | c_rarg0 c_rarg1 c_rarg2 c_rarg3 c_rarg4 c_rarg5 | +// |-------------------------------------------------------| +// | rcx rdx r8 r9 rdi* rsi* | windows (* not a c_rarg) +// | rdi rsi rdx rcx r8 r9 | solaris/linux +// |-------------------------------------------------------| +// | j_rarg5 j_rarg0 j_rarg1 j_rarg2 j_rarg3 j_rarg4 | +// |-------------------------------------------------------| + +REGISTER_DECLARATION(Register, j_rarg0, c_rarg1); +REGISTER_DECLARATION(Register, j_rarg1, c_rarg2); +REGISTER_DECLARATION(Register, j_rarg2, c_rarg3); +// Windows runs out of register args here +#ifdef _WIN64 +REGISTER_DECLARATION(Register, j_rarg3, rdi); +REGISTER_DECLARATION(Register, j_rarg4, rsi); +#else +REGISTER_DECLARATION(Register, j_rarg3, c_rarg4); +REGISTER_DECLARATION(Register, j_rarg4, c_rarg5); +#endif /* _WIN64 */ +REGISTER_DECLARATION(Register, j_rarg5, c_rarg0); + +REGISTER_DECLARATION(XMMRegister, j_farg0, xmm0); +REGISTER_DECLARATION(XMMRegister, j_farg1, xmm1); +REGISTER_DECLARATION(XMMRegister, j_farg2, xmm2); +REGISTER_DECLARATION(XMMRegister, j_farg3, xmm3); +REGISTER_DECLARATION(XMMRegister, j_farg4, xmm4); +REGISTER_DECLARATION(XMMRegister, j_farg5, xmm5); +REGISTER_DECLARATION(XMMRegister, j_farg6, xmm6); +REGISTER_DECLARATION(XMMRegister, j_farg7, xmm7); + +REGISTER_DECLARATION(Register, rscratch1, r10); // volatile +REGISTER_DECLARATION(Register, rscratch2, r11); // volatile + +REGISTER_DECLARATION(Register, r15_thread, r15); // callee-saved + +#endif // _LP64 + +// Address is an abstraction used to represent a memory location +// using any of the amd64 addressing modes with one object. +// +// Note: A register location is represented via a Register, not +// via an address for efficiency & simplicity reasons. + +class ArrayAddress; + +class Address VALUE_OBJ_CLASS_SPEC { + public: + enum ScaleFactor { + no_scale = -1, + times_1 = 0, + times_2 = 1, + times_4 = 2, + times_8 = 3 + }; + + private: + Register _base; + Register _index; + ScaleFactor _scale; + int _disp; + RelocationHolder _rspec; + + // Easily misused constructors make them private + Address(int disp, address loc, relocInfo::relocType rtype); + Address(int disp, address loc, RelocationHolder spec); + + public: + // creation + Address() + : _base(noreg), + _index(noreg), + _scale(no_scale), + _disp(0) { + } + + // No default displacement otherwise Register can be implicitly + // converted to 0(Register) which is quite a different animal. + + Address(Register base, int disp) + : _base(base), + _index(noreg), + _scale(no_scale), + _disp(disp) { + } + + Address(Register base, Register index, ScaleFactor scale, int disp = 0) + : _base (base), + _index(index), + _scale(scale), + _disp (disp) { + assert(!index->is_valid() == (scale == Address::no_scale), + "inconsistent address"); + } + + // The following two overloads are used in connection with the + // ByteSize type (see sizes.hpp). They simplify the use of + // ByteSize'd arguments in assembly code. Note that their equivalent + // for the optimized build are the member functions with int disp + // argument since ByteSize is mapped to an int type in that case. + // + // Note: DO NOT introduce similar overloaded functions for WordSize + // arguments as in the optimized mode, both ByteSize and WordSize + // are mapped to the same type and thus the compiler cannot make a + // distinction anymore (=> compiler errors). + +#ifdef ASSERT + Address(Register base, ByteSize disp) + : _base(base), + _index(noreg), + _scale(no_scale), + _disp(in_bytes(disp)) { + } + + Address(Register base, Register index, ScaleFactor scale, ByteSize disp) + : _base(base), + _index(index), + _scale(scale), + _disp(in_bytes(disp)) { + assert(!index->is_valid() == (scale == Address::no_scale), + "inconsistent address"); + } +#endif // ASSERT + + // accessors + bool uses(Register reg) const { + return _base == reg || _index == reg; + } + + // Convert the raw encoding form into the form expected by the constructor for + // Address. An index of 4 (rsp) corresponds to having no index, so convert + // that to noreg for the Address constructor. + static Address make_raw(int base, int index, int scale, int disp); + + static Address make_array(ArrayAddress); + + private: + bool base_needs_rex() const { + return _base != noreg && _base->encoding() >= 8; + } + + bool index_needs_rex() const { + return _index != noreg &&_index->encoding() >= 8; + } + + relocInfo::relocType reloc() const { return _rspec.type(); } + + friend class Assembler; + friend class MacroAssembler; + friend class LIR_Assembler; // base/index/scale/disp +}; + +// +// AddressLiteral has been split out from Address because operands of this type +// need to be treated specially on 32bit vs. 64bit platforms. By splitting it out +// the few instructions that need to deal with address literals are unique and the +// MacroAssembler does not have to implement every instruction in the Assembler +// in order to search for address literals that may need special handling depending +// on the instruction and the platform. As small step on the way to merging i486/amd64 +// directories. +// +class AddressLiteral VALUE_OBJ_CLASS_SPEC { + friend class ArrayAddress; + RelocationHolder _rspec; + // Typically we use AddressLiterals we want to use their rval + // However in some situations we want the lval (effect address) of the item. + // We provide a special factory for making those lvals. + bool _is_lval; + + // If the target is far we'll need to load the ea of this to + // a register to reach it. Otherwise if near we can do rip + // relative addressing. + + address _target; + + protected: + // creation + AddressLiteral() + : _is_lval(false), + _target(NULL) + {} + + public: + + + AddressLiteral(address target, relocInfo::relocType rtype); + + AddressLiteral(address target, RelocationHolder const& rspec) + : _rspec(rspec), + _is_lval(false), + _target(target) + {} + + AddressLiteral addr() { + AddressLiteral ret = *this; + ret._is_lval = true; + return ret; + } + + + private: + + address target() { return _target; } + bool is_lval() { return _is_lval; } + + relocInfo::relocType reloc() const { return _rspec.type(); } + const RelocationHolder& rspec() const { return _rspec; } + + friend class Assembler; + friend class MacroAssembler; + friend class Address; + friend class LIR_Assembler; +}; + +// Convience classes +class RuntimeAddress: public AddressLiteral { + + public: + + RuntimeAddress(address target) : AddressLiteral(target, relocInfo::runtime_call_type) {} + +}; + +class OopAddress: public AddressLiteral { + + public: + + OopAddress(address target) : AddressLiteral(target, relocInfo::oop_type){} + +}; + +class ExternalAddress: public AddressLiteral { + + public: + + ExternalAddress(address target) : AddressLiteral(target, relocInfo::external_word_type){} + +}; + +class InternalAddress: public AddressLiteral { + + public: + + InternalAddress(address target) : AddressLiteral(target, relocInfo::internal_word_type) {} + +}; + +// x86 can do array addressing as a single operation since disp can be an absolute +// address but amd64 can't [e.g. array_base(rx, ry:width) ]. We create a class +// that expresses the concept but does extra magic on amd64 to get the final result + +class ArrayAddress VALUE_OBJ_CLASS_SPEC { + private: + + AddressLiteral _base; + Address _index; + + public: + + ArrayAddress() {}; + ArrayAddress(AddressLiteral base, Address index): _base(base), _index(index) {}; + AddressLiteral base() { return _base; } + Address index() { return _index; } + +}; + +// The amd64 Assembler: Pure assembler doing NO optimizations on +// the instruction level (e.g. mov rax, 0 is not translated into xor +// rax, rax!); i.e., what you write is what you get. The Assembler is +// generating code into a CodeBuffer. + +const int FPUStateSizeInWords = 512 / wordSize; + +class Assembler : public AbstractAssembler { + friend class AbstractAssembler; // for the non-virtual hack + friend class StubGenerator; + + + protected: +#ifdef ASSERT + void check_relocation(RelocationHolder const& rspec, int format); +#endif + + inline void emit_long64(jlong x); + + void emit_data(jint data, relocInfo::relocType rtype, int format /* = 1 */); + void emit_data(jint data, RelocationHolder const& rspec, int format /* = 1 */); + void emit_data64(jlong data, relocInfo::relocType rtype, int format = 0); + void emit_data64(jlong data, RelocationHolder const& rspec, int format = 0); + + // Helper functions for groups of instructions + void emit_arith_b(int op1, int op2, Register dst, int imm8); + + void emit_arith(int op1, int op2, Register dst, int imm32); + // only x86?? + void emit_arith(int op1, int op2, Register dst, jobject obj); + void emit_arith(int op1, int op2, Register dst, Register src); + + void emit_operand(Register reg, + Register base, Register index, Address::ScaleFactor scale, + int disp, + RelocationHolder const& rspec, + int rip_relative_correction = 0); + void emit_operand(Register reg, Address adr, + int rip_relative_correction = 0); + void emit_operand(XMMRegister reg, + Register base, Register index, Address::ScaleFactor scale, + int disp, + RelocationHolder const& rspec, + int rip_relative_correction = 0); + void emit_operand(XMMRegister reg, Address adr, + int rip_relative_correction = 0); + + // Immediate-to-memory forms + void emit_arith_operand(int op1, Register rm, Address adr, int imm32); + + void emit_farith(int b1, int b2, int i); + + bool reachable(AddressLiteral adr); + + // These are all easily abused and hence protected + + // Make these disappear in 64bit mode since they would never be correct +#ifndef _LP64 + void cmp_literal32(Register src1, int32_t imm32, RelocationHolder const& rspec); + void cmp_literal32(Address src1, int32_t imm32, RelocationHolder const& rspec); + + void mov_literal32(Register dst, int32_t imm32, RelocationHolder const& rspec); + void mov_literal32(Address dst, int32_t imm32, RelocationHolder const& rspec); + + void push_literal32(int32_t imm32, RelocationHolder const& rspec); +#endif // _LP64 + + + void mov_literal64(Register dst, intptr_t imm64, RelocationHolder const& rspec); + + // These are unique in that we are ensured by the caller that the 32bit + // relative in these instructions will always be able to reach the potentially + // 64bit address described by entry. Since they can take a 64bit address they + // don't have the 32 suffix like the other instructions in this class. + void jmp_literal(address entry, RelocationHolder const& rspec); + void call_literal(address entry, RelocationHolder const& rspec); + + public: + enum Condition { // The amd64 condition codes used for conditional jumps/moves. + zero = 0x4, + notZero = 0x5, + equal = 0x4, + notEqual = 0x5, + less = 0xc, + lessEqual = 0xe, + greater = 0xf, + greaterEqual = 0xd, + below = 0x2, + belowEqual = 0x6, + above = 0x7, + aboveEqual = 0x3, + overflow = 0x0, + noOverflow = 0x1, + carrySet = 0x2, + carryClear = 0x3, + negative = 0x8, + positive = 0x9, + parity = 0xa, + noParity = 0xb + }; + + enum Prefix { + // segment overrides + // XXX remove segment prefixes + CS_segment = 0x2e, + SS_segment = 0x36, + DS_segment = 0x3e, + ES_segment = 0x26, + FS_segment = 0x64, + GS_segment = 0x65, + + REX = 0x40, + + REX_B = 0x41, + REX_X = 0x42, + REX_XB = 0x43, + REX_R = 0x44, + REX_RB = 0x45, + REX_RX = 0x46, + REX_RXB = 0x47, + + REX_W = 0x48, + + REX_WB = 0x49, + REX_WX = 0x4A, + REX_WXB = 0x4B, + REX_WR = 0x4C, + REX_WRB = 0x4D, + REX_WRX = 0x4E, + REX_WRXB = 0x4F + }; + + enum WhichOperand { + // input to locate_operand, and format code for relocations + imm64_operand = 0, // embedded 64-bit immediate operand + disp32_operand = 1, // embedded 32-bit displacement + call32_operand = 2, // embedded 32-bit self-relative displacement + _WhichOperand_limit = 3 + }; + + public: + + // Creation + Assembler(CodeBuffer* code) + : AbstractAssembler(code) { + } + + // Decoding + static address locate_operand(address inst, WhichOperand which); + static address locate_next_instruction(address inst); + + // Utilities + + static bool is_simm(int64_t x, int nbits) { return -( CONST64(1) << (nbits-1) ) <= x && x < ( CONST64(1) << (nbits-1) ); } + static bool is_simm32 (int64_t x) { return x == (int64_t)(int32_t)x; } + + + // Stack + void pushaq(); + void popaq(); + + void pushfq(); + void popfq(); + + void pushq(int imm32); + + void pushq(Register src); + void pushq(Address src); + + void popq(Register dst); + void popq(Address dst); + + // Instruction prefixes + void prefix(Prefix p); + + int prefix_and_encode(int reg_enc, bool byteinst = false); + int prefixq_and_encode(int reg_enc); + + int prefix_and_encode(int dst_enc, int src_enc, bool byteinst = false); + int prefixq_and_encode(int dst_enc, int src_enc); + + void prefix(Register reg); + void prefix(Address adr); + void prefixq(Address adr); + + void prefix(Address adr, Register reg, bool byteinst = false); + void prefixq(Address adr, Register reg); + + void prefix(Address adr, XMMRegister reg); + + // Moves + void movb(Register dst, Address src); + void movb(Address dst, int imm8); + void movb(Address dst, Register src); + + void movw(Address dst, int imm16); + void movw(Register dst, Address src); + void movw(Address dst, Register src); + + void movl(Register dst, int imm32); + void movl(Register dst, Register src); + void movl(Register dst, Address src); + void movl(Address dst, int imm32); + void movl(Address dst, Register src); + + void movq(Register dst, Register src); + void movq(Register dst, Address src); + void movq(Address dst, Register src); + // These prevent using movq from converting a zero (like NULL) into Register + // by giving the compiler two choices it can't resolve + void movq(Address dst, void* dummy); + void movq(Register dst, void* dummy); + + void mov64(Register dst, intptr_t imm64); + void mov64(Address dst, intptr_t imm64); + + void movsbl(Register dst, Address src); + void movsbl(Register dst, Register src); + void movswl(Register dst, Address src); + void movswl(Register dst, Register src); + void movslq(Register dst, Address src); + void movslq(Register dst, Register src); + + void movzbl(Register dst, Address src); + void movzbl(Register dst, Register src); + void movzwl(Register dst, Address src); + void movzwl(Register dst, Register src); + + protected: // Avoid using the next instructions directly. + // New cpus require use of movsd and movss to avoid partial register stall + // when loading from memory. But for old Opteron use movlpd instead of movsd. + // The selection is done in MacroAssembler::movdbl() and movflt(). + void movss(XMMRegister dst, XMMRegister src); + void movss(XMMRegister dst, Address src); + void movss(Address dst, XMMRegister src); + void movsd(XMMRegister dst, XMMRegister src); + void movsd(Address dst, XMMRegister src); + void movsd(XMMRegister dst, Address src); + void movlpd(XMMRegister dst, Address src); + // New cpus require use of movaps and movapd to avoid partial register stall + // when moving between registers. + void movapd(XMMRegister dst, XMMRegister src); + void movaps(XMMRegister dst, XMMRegister src); + public: + + void movdl(XMMRegister dst, Register src); + void movdl(Register dst, XMMRegister src); + void movdq(XMMRegister dst, Register src); + void movdq(Register dst, XMMRegister src); + + void cmovl(Condition cc, Register dst, Register src); + void cmovl(Condition cc, Register dst, Address src); + void cmovq(Condition cc, Register dst, Register src); + void cmovq(Condition cc, Register dst, Address src); + + // Prefetches + private: + void prefetch_prefix(Address src); + public: + void prefetcht0(Address src); + void prefetcht1(Address src); + void prefetcht2(Address src); + void prefetchnta(Address src); + void prefetchw(Address src); + + // Arithmetics + void adcl(Register dst, int imm32); + void adcl(Register dst, Address src); + void adcl(Register dst, Register src); + void adcq(Register dst, int imm32); + void adcq(Register dst, Address src); + void adcq(Register dst, Register src); + + void addl(Address dst, int imm32); + void addl(Address dst, Register src); + void addl(Register dst, int imm32); + void addl(Register dst, Address src); + void addl(Register dst, Register src); + void addq(Address dst, int imm32); + void addq(Address dst, Register src); + void addq(Register dst, int imm32); + void addq(Register dst, Address src); + void addq(Register dst, Register src); + + void andl(Register dst, int imm32); + void andl(Register dst, Address src); + void andl(Register dst, Register src); + void andq(Register dst, int imm32); + void andq(Register dst, Address src); + void andq(Register dst, Register src); + + void cmpb(Address dst, int imm8); + void cmpl(Address dst, int imm32); + void cmpl(Register dst, int imm32); + void cmpl(Register dst, Register src); + void cmpl(Register dst, Address src); + void cmpq(Address dst, int imm32); + void cmpq(Address dst, Register src); + void cmpq(Register dst, int imm32); + void cmpq(Register dst, Register src); + void cmpq(Register dst, Address src); + + void ucomiss(XMMRegister dst, XMMRegister src); + void ucomisd(XMMRegister dst, XMMRegister src); + + protected: + // Don't use next inc() and dec() methods directly. INC & DEC instructions + // could cause a partial flag stall since they don't set CF flag. + // Use MacroAssembler::decrement() & MacroAssembler::increment() methods + // which call inc() & dec() or add() & sub() in accordance with + // the product flag UseIncDec value. + + void decl(Register dst); + void decl(Address dst); + void decq(Register dst); + void decq(Address dst); + + void incl(Register dst); + void incl(Address dst); + void incq(Register dst); + void incq(Address dst); + + public: + void idivl(Register src); + void idivq(Register src); + void cdql(); + void cdqq(); + + void imull(Register dst, Register src); + void imull(Register dst, Register src, int value); + void imulq(Register dst, Register src); + void imulq(Register dst, Register src, int value); + + void leal(Register dst, Address src); + void leaq(Register dst, Address src); + + void mull(Address src); + void mull(Register src); + + void negl(Register dst); + void negq(Register dst); + + void notl(Register dst); + void notq(Register dst); + + void orl(Address dst, int imm32); + void orl(Register dst, int imm32); + void orl(Register dst, Address src); + void orl(Register dst, Register src); + void orq(Address dst, int imm32); + void orq(Register dst, int imm32); + void orq(Register dst, Address src); + void orq(Register dst, Register src); + + void rcll(Register dst, int imm8); + void rclq(Register dst, int imm8); + + void sarl(Register dst, int imm8); + void sarl(Register dst); + void sarq(Register dst, int imm8); + void sarq(Register dst); + + void sbbl(Address dst, int imm32); + void sbbl(Register dst, int imm32); + void sbbl(Register dst, Address src); + void sbbl(Register dst, Register src); + void sbbq(Address dst, int imm32); + void sbbq(Register dst, int imm32); + void sbbq(Register dst, Address src); + void sbbq(Register dst, Register src); + + void shll(Register dst, int imm8); + void shll(Register dst); + void shlq(Register dst, int imm8); + void shlq(Register dst); + + void shrl(Register dst, int imm8); + void shrl(Register dst); + void shrq(Register dst, int imm8); + void shrq(Register dst); + + void subl(Address dst, int imm32); + void subl(Address dst, Register src); + void subl(Register dst, int imm32); + void subl(Register dst, Address src); + void subl(Register dst, Register src); + void subq(Address dst, int imm32); + void subq(Address dst, Register src); + void subq(Register dst, int imm32); + void subq(Register dst, Address src); + void subq(Register dst, Register src); + + void testb(Register dst, int imm8); + void testl(Register dst, int imm32); + void testl(Register dst, Register src); + void testq(Register dst, int imm32); + void testq(Register dst, Register src); + + void xaddl(Address dst, Register src); + void xaddq(Address dst, Register src); + + void xorl(Register dst, int imm32); + void xorl(Register dst, Address src); + void xorl(Register dst, Register src); + void xorq(Register dst, int imm32); + void xorq(Register dst, Address src); + void xorq(Register dst, Register src); + + // Miscellaneous + void bswapl(Register reg); + void bswapq(Register reg); + void lock(); + + void xchgl(Register reg, Address adr); + void xchgl(Register dst, Register src); + void xchgq(Register reg, Address adr); + void xchgq(Register dst, Register src); + + void cmpxchgl(Register reg, Address adr); + void cmpxchgq(Register reg, Address adr); + + void nop(int i = 1); + void addr_nop_4(); + void addr_nop_5(); + void addr_nop_7(); + void addr_nop_8(); + + void hlt(); + void ret(int imm16); + void smovl(); + void rep_movl(); + void rep_movq(); + void rep_set(); + void repne_scan(); + void setb(Condition cc, Register dst); + + void clflush(Address adr); + + enum Membar_mask_bits { + StoreStore = 1 << 3, + LoadStore = 1 << 2, + StoreLoad = 1 << 1, + LoadLoad = 1 << 0 + }; + + // Serializes memory. + void membar(Membar_mask_bits order_constraint) { + // We only have to handle StoreLoad and LoadLoad + if (order_constraint & StoreLoad) { + // MFENCE subsumes LFENCE + mfence(); + } /* [jk] not needed currently: else if (order_constraint & LoadLoad) { + lfence(); + } */ + } + + void lfence() { + emit_byte(0x0F); + emit_byte(0xAE); + emit_byte(0xE8); + } + + void mfence() { + emit_byte(0x0F); + emit_byte(0xAE); + emit_byte(0xF0); + } + + // Identify processor type and features + void cpuid() { + emit_byte(0x0F); + emit_byte(0xA2); + } + + void cld() { emit_byte(0xfc); + } + + void std() { emit_byte(0xfd); + } + + + // Calls + + void call(Label& L, relocInfo::relocType rtype); + void call(Register reg); + void call(Address adr); + + // Jumps + + void jmp(Register reg); + void jmp(Address adr); + + // Label operations & relative jumps (PPUM Appendix D) + // unconditional jump to L + void jmp(Label& L, relocInfo::relocType rtype = relocInfo::none); + + + // Unconditional 8-bit offset jump to L. + // WARNING: be very careful using this for forward jumps. If the label is + // not bound within an 8-bit offset of this instruction, a run-time error + // will occur. + void jmpb(Label& L); + + // jcc is the generic conditional branch generator to run- time + // routines, jcc is used for branches to labels. jcc takes a branch + // opcode (cc) and a label (L) and generates either a backward + // branch or a forward branch and links it to the label fixup + // chain. Usage: + // + // Label L; // unbound label + // jcc(cc, L); // forward branch to unbound label + // bind(L); // bind label to the current pc + // jcc(cc, L); // backward branch to bound label + // bind(L); // illegal: a label may be bound only once + // + // Note: The same Label can be used for forward and backward branches + // but it may be bound only once. + + void jcc(Condition cc, Label& L, + relocInfo::relocType rtype = relocInfo::none); + + // Conditional jump to a 8-bit offset to L. + // WARNING: be very careful using this for forward jumps. If the label is + // not bound within an 8-bit offset of this instruction, a run-time error + // will occur. + void jccb(Condition cc, Label& L); + + // Floating-point operations + + void fxsave(Address dst); + void fxrstor(Address src); + void ldmxcsr(Address src); + void stmxcsr(Address dst); + + void addss(XMMRegister dst, XMMRegister src); + void addss(XMMRegister dst, Address src); + void subss(XMMRegister dst, XMMRegister src); + void subss(XMMRegister dst, Address src); + void mulss(XMMRegister dst, XMMRegister src); + void mulss(XMMRegister dst, Address src); + void divss(XMMRegister dst, XMMRegister src); + void divss(XMMRegister dst, Address src); + void addsd(XMMRegister dst, XMMRegister src); + void addsd(XMMRegister dst, Address src); + void subsd(XMMRegister dst, XMMRegister src); + void subsd(XMMRegister dst, Address src); + void mulsd(XMMRegister dst, XMMRegister src); + void mulsd(XMMRegister dst, Address src); + void divsd(XMMRegister dst, XMMRegister src); + void divsd(XMMRegister dst, Address src); + + // We only need the double form + void sqrtsd(XMMRegister dst, XMMRegister src); + void sqrtsd(XMMRegister dst, Address src); + + void xorps(XMMRegister dst, XMMRegister src); + void xorps(XMMRegister dst, Address src); + void xorpd(XMMRegister dst, XMMRegister src); + void xorpd(XMMRegister dst, Address src); + + void cvtsi2ssl(XMMRegister dst, Register src); + void cvtsi2ssq(XMMRegister dst, Register src); + void cvtsi2sdl(XMMRegister dst, Register src); + void cvtsi2sdq(XMMRegister dst, Register src); + void cvttss2sil(Register dst, XMMRegister src); // truncates + void cvttss2siq(Register dst, XMMRegister src); // truncates + void cvttsd2sil(Register dst, XMMRegister src); // truncates + void cvttsd2siq(Register dst, XMMRegister src); // truncates + void cvtss2sd(XMMRegister dst, XMMRegister src); + void cvtsd2ss(XMMRegister dst, XMMRegister src); + + void pxor(XMMRegister dst, Address src); // Xor Packed Byte Integer Values + void pxor(XMMRegister dst, XMMRegister src); // Xor Packed Byte Integer Values + + void movdqa(XMMRegister dst, Address src); // Move Aligned Double Quadword + void movdqa(XMMRegister dst, XMMRegister src); + void movdqa(Address dst, XMMRegister src); + + void movq(XMMRegister dst, Address src); + void movq(Address dst, XMMRegister src); + + void pshufd(XMMRegister dst, XMMRegister src, int mode); // Shuffle Packed Doublewords + void pshufd(XMMRegister dst, Address src, int mode); + void pshuflw(XMMRegister dst, XMMRegister src, int mode); // Shuffle Packed Low Words + void pshuflw(XMMRegister dst, Address src, int mode); + + void psrlq(XMMRegister dst, int shift); // Shift Right Logical Quadword Immediate + + void punpcklbw(XMMRegister dst, XMMRegister src); // Interleave Low Bytes + void punpcklbw(XMMRegister dst, Address src); +}; + + +// MacroAssembler extends Assembler by frequently used macros. +// +// Instructions for which a 'better' code sequence exists depending +// on arguments should also go in here. + +class MacroAssembler : public Assembler { + friend class LIR_Assembler; + protected: + + Address as_Address(AddressLiteral adr); + Address as_Address(ArrayAddress adr); + + // Support for VM calls + // + // This is the base routine called by the different versions of + // call_VM_leaf. The interpreter may customize this version by + // overriding it for its purposes (e.g., to save/restore additional + // registers when doing a VM call). + + virtual void call_VM_leaf_base( + address entry_point, // the entry point + int number_of_arguments // the number of arguments to + // pop after the call + ); + + // This is the base routine called by the different versions of + // call_VM. The interpreter may customize this version by overriding + // it for its purposes (e.g., to save/restore additional registers + // when doing a VM call). + // + // If no java_thread register is specified (noreg) than rdi will be + // used instead. call_VM_base returns the register which contains + // the thread upon return. If a thread register has been specified, + // the return value will correspond to that register. If no + // last_java_sp is specified (noreg) than rsp will be used instead. + virtual void call_VM_base( // returns the register + // containing the thread upon + // return + Register oop_result, // where an oop-result ends up + // if any; use noreg otherwise + Register java_thread, // the thread if computed + // before ; use noreg otherwise + Register last_java_sp, // to set up last_Java_frame in + // stubs; use noreg otherwise + address entry_point, // the entry point + int number_of_arguments, // the number of arguments (w/o + // thread) to pop after the + // call + bool check_exceptions // whether to check for pending + // exceptions after return + ); + + // This routines should emit JVMTI PopFrame handling and ForceEarlyReturn code. + // The implementation is only non-empty for the InterpreterMacroAssembler, + // as only the interpreter handles PopFrame and ForceEarlyReturn requests. + virtual void check_and_handle_popframe(Register java_thread); + virtual void check_and_handle_earlyret(Register java_thread); + + void call_VM_helper(Register oop_result, + address entry_point, + int number_of_arguments, + bool check_exceptions = true); + + public: + MacroAssembler(CodeBuffer* code) : Assembler(code) {} + + // Support for NULL-checks + // + // Generates code that causes a NULL OS exception if the content of + // reg is NULL. If the accessed location is M[reg + offset] and the + // offset is known, provide the offset. No explicit code generation + // is needed if the offset is within a certain range (0 <= offset <= + // page_size). + void null_check(Register reg, int offset = -1); + static bool needs_explicit_null_check(int offset); + + // Required platform-specific helpers for Label::patch_instructions. + // They _shadow_ the declarations in AbstractAssembler, which are undefined. + void pd_patch_instruction(address branch, address target); +#ifndef PRODUCT + static void pd_print_patched_instruction(address branch); +#endif + + + // The following 4 methods return the offset of the appropriate move + // instruction. Note: these are 32 bit instructions + + // Support for fast byte/word loading with zero extension (depending + // on particular CPU) + int load_unsigned_byte(Register dst, Address src); + int load_unsigned_word(Register dst, Address src); + + // Support for fast byte/word loading with sign extension (depending + // on particular CPU) + int load_signed_byte(Register dst, Address src); + int load_signed_word(Register dst, Address src); + + // Support for inc/dec with optimal instruction selection depending + // on value + void incrementl(Register reg, int value = 1); + void decrementl(Register reg, int value = 1); + void incrementq(Register reg, int value = 1); + void decrementq(Register reg, int value = 1); + + void incrementl(Address dst, int value = 1); + void decrementl(Address dst, int value = 1); + void incrementq(Address dst, int value = 1); + void decrementq(Address dst, int value = 1); + + // Support optimal SSE move instructions. + void movflt(XMMRegister dst, XMMRegister src) { + if (UseXmmRegToRegMoveAll) { movaps(dst, src); return; } + else { movss (dst, src); return; } + } + + void movflt(XMMRegister dst, Address src) { movss(dst, src); } + + void movflt(XMMRegister dst, AddressLiteral src); + + void movflt(Address dst, XMMRegister src) { movss(dst, src); } + + void movdbl(XMMRegister dst, XMMRegister src) { + if (UseXmmRegToRegMoveAll) { movapd(dst, src); return; } + else { movsd (dst, src); return; } + } + + void movdbl(XMMRegister dst, AddressLiteral src); + + void movdbl(XMMRegister dst, Address src) { + if (UseXmmLoadAndClearUpper) { movsd (dst, src); return; } + else { movlpd(dst, src); return; } + } + + void movdbl(Address dst, XMMRegister src) { movsd(dst, src); } + + void incrementl(AddressLiteral dst); + void incrementl(ArrayAddress dst); + + // Alignment + void align(int modulus); + + // Misc + void fat_nop(); // 5 byte nop + + + // C++ bool manipulation + + void movbool(Register dst, Address src); + void movbool(Address dst, bool boolconst); + void movbool(Address dst, Register src); + void testbool(Register dst); + + // Stack frame creation/removal + void enter(); + void leave(); + + // Support for getting the JavaThread pointer (i.e.; a reference to + // thread-local information) The pointer will be loaded into the + // thread register. + void get_thread(Register thread); + + void int3(); + + // Support for VM calls + // + // It is imperative that all calls into the VM are handled via the + // call_VM macros. They make sure that the stack linkage is setup + // correctly. call_VM's correspond to ENTRY/ENTRY_X entry points + // while call_VM_leaf's correspond to LEAF entry points. + void call_VM(Register oop_result, + address entry_point, + bool check_exceptions = true); + void call_VM(Register oop_result, + address entry_point, + Register arg_1, + bool check_exceptions = true); + void call_VM(Register oop_result, + address entry_point, + Register arg_1, Register arg_2, + bool check_exceptions = true); + void call_VM(Register oop_result, + address entry_point, + Register arg_1, Register arg_2, Register arg_3, + bool check_exceptions = true); + + // Overloadings with last_Java_sp + void call_VM(Register oop_result, + Register last_java_sp, + address entry_point, + int number_of_arguments = 0, + bool check_exceptions = true); + void call_VM(Register oop_result, + Register last_java_sp, + address entry_point, + Register arg_1, bool + check_exceptions = true); + void call_VM(Register oop_result, + Register last_java_sp, + address entry_point, + Register arg_1, Register arg_2, + bool check_exceptions = true); + void call_VM(Register oop_result, + Register last_java_sp, + address entry_point, + Register arg_1, Register arg_2, Register arg_3, + bool check_exceptions = true); + + void call_VM_leaf(address entry_point, + int number_of_arguments = 0); + void call_VM_leaf(address entry_point, + Register arg_1); + void call_VM_leaf(address entry_point, + Register arg_1, Register arg_2); + void call_VM_leaf(address entry_point, + Register arg_1, Register arg_2, Register arg_3); + + // last Java Frame (fills frame anchor) + void set_last_Java_frame(Register last_java_sp, + Register last_java_fp, + address last_java_pc); + void reset_last_Java_frame(bool clear_fp, bool clear_pc); + + // Stores + void store_check(Register obj); // store check for + // obj - register is + // destroyed + // afterwards + void store_check(Register obj, Address dst); // same as above, dst + // is exact store + // location (reg. is + // destroyed) + + // split store_check(Register obj) to enhance instruction interleaving + void store_check_part_1(Register obj); + void store_check_part_2(Register obj); + + // C 'boolean' to Java boolean: x == 0 ? 0 : 1 + void c2bool(Register x); + + // Int division/reminder for Java + // (as idivl, but checks for special case as described in JVM spec.) + // returns idivl instruction offset for implicit exception handling + int corrected_idivl(Register reg); + // Long division/reminder for Java + // (as idivq, but checks for special case as described in JVM spec.) + // returns idivq instruction offset for implicit exception handling + int corrected_idivq(Register reg); + + // Push and pop integer/fpu/cpu state + void push_IU_state(); + void pop_IU_state(); + + void push_FPU_state(); + void pop_FPU_state(); + + void push_CPU_state(); + void pop_CPU_state(); + + // Sign extension + void sign_extend_short(Register reg); + void sign_extend_byte(Register reg); + + // Division by power of 2, rounding towards 0 + void division_with_shift(Register reg, int shift_value); + + // Round up to a power of two + void round_to_l(Register reg, int modulus); + void round_to_q(Register reg, int modulus); + + // allocation + void eden_allocate( + Register obj, // result: pointer to object after + // successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at + // compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at + // compile time + Register t1, // temp register + Label& slow_case // continuation point if fast + // allocation fails + ); + void tlab_allocate( + Register obj, // result: pointer to object after + // successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at + // compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at + // compile time + Register t1, // temp register + Register t2, // temp register + Label& slow_case // continuation point if fast + // allocation fails + ); + void tlab_refill(Label& retry_tlab, Label& try_eden, Label& slow_case); + + //---- + + // Debugging + + // only if +VerifyOops + void verify_oop(Register reg, const char* s = "broken oop"); + void verify_oop_addr(Address addr, const char * s = "broken oop addr"); + + // only if +VerifyFPU + void verify_FPU(int stack_depth, const char* s = "illegal FPU state") {} + + // prints msg, dumps registers and stops execution + void stop(const char* msg); + + // prints message and continues + void warn(const char* msg); + + static void debug(char* msg, int64_t pc, int64_t regs[]); + + void os_breakpoint(); + + void untested() + { + stop("untested"); + } + + void unimplemented(const char* what = "") + { + char* b = new char[1024]; + sprintf(b, "unimplemented: %s", what); + stop(b); + } + + void should_not_reach_here() + { + stop("should not reach here"); + } + + // Stack overflow checking + void bang_stack_with_offset(int offset) + { + // stack grows down, caller passes positive offset + assert(offset > 0, "must bang with negative offset"); + movl(Address(rsp, (-offset)), rax); + } + + // Writes to stack successive pages until offset reached to check for + // stack overflow + shadow pages. Also, clobbers tmp + void bang_stack_size(Register offset, Register tmp); + + // Support for serializing memory accesses between threads. + void serialize_memory(Register thread, Register tmp); + + void verify_tlab(); + + // Biased locking support + // lock_reg and obj_reg must be loaded up with the appropriate values. + // swap_reg must be rax and is killed. + // tmp_reg must be supplied and is killed. + // If swap_reg_contains_mark is true then the code assumes that the + // mark word of the object has already been loaded into swap_reg. + // Optional slow case is for implementations (interpreter and C1) which branch to + // slow case directly. Leaves condition codes set for C2's Fast_Lock node. + // Returns offset of first potentially-faulting instruction for null + // check info (currently consumed only by C1). If + // swap_reg_contains_mark is true then returns -1 as it is assumed + // the calling code has already passed any potential faults. + int biased_locking_enter(Register lock_reg, Register obj_reg, Register swap_reg, Register tmp_reg, + bool swap_reg_contains_mark, + Label& done, Label* slow_case = NULL, + BiasedLockingCounters* counters = NULL); + void biased_locking_exit (Register obj_reg, Register temp_reg, Label& done); + + Condition negate_condition(Condition cond); + + // Instructions that use AddressLiteral operands. These instruction can handle 32bit/64bit + // operands. In general the names are modified to avoid hiding the instruction in Assembler + // so that we don't need to implement all the varieties in the Assembler with trivial wrappers + // here in MacroAssembler. The major exception to this rule is call + + // Arithmetics + + void cmp8(AddressLiteral src1, int8_t imm32); + + void cmp32(AddressLiteral src1, int32_t src2); + // compare reg - mem, or reg - &mem + void cmp32(Register src1, AddressLiteral src2); + + void cmp32(Register src1, Address src2); + +#ifndef _LP64 + void cmpoop(Address dst, jobject obj); + void cmpoop(Register dst, jobject obj); +#endif // _LP64 + + // NOTE src2 must be the lval. This is NOT an mem-mem compare + void cmpptr(Address src1, AddressLiteral src2); + + void cmpptr(Register src1, AddressLiteral src); + + // will be cmpreg(?) + void cmp64(Register src1, AddressLiteral src); + + void cmpxchgptr(Register reg, Address adr); + void cmpxchgptr(Register reg, AddressLiteral adr); + + // Helper functions for statistics gathering. + // Conditionally (atomically, on MPs) increments passed counter address, preserving condition codes. + void cond_inc32(Condition cond, AddressLiteral counter_addr); + // Unconditional atomic increment. + void atomic_incl(AddressLiteral counter_addr); + + + void lea(Register dst, AddressLiteral src); + void lea(Register dst, Address src); + + + // Calls + void call(Label& L, relocInfo::relocType rtype); + void call(Register entry); + void call(AddressLiteral entry); + + // Jumps + + // 32bit can do a case table jump in one instruction but we no longer allow the base + // to be installed in the Address class + void jump(ArrayAddress entry); + + void jump(AddressLiteral entry); + void jump_cc(Condition cc, AddressLiteral dst); + + // Floating + + void ldmxcsr(Address src) { Assembler::ldmxcsr(src); } + void ldmxcsr(AddressLiteral src); + +private: + // these are private because users should be doing movflt/movdbl + + void movss(XMMRegister dst, XMMRegister src) { Assembler::movss(dst, src); } + void movss(Address dst, XMMRegister src) { Assembler::movss(dst, src); } + void movss(XMMRegister dst, Address src) { Assembler::movss(dst, src); } + void movss(XMMRegister dst, AddressLiteral src); + + void movlpd(XMMRegister dst, Address src) {Assembler::movlpd(dst, src); } + void movlpd(XMMRegister dst, AddressLiteral src); + +public: + + + void xorpd(XMMRegister dst, XMMRegister src) {Assembler::xorpd(dst, src); } + void xorpd(XMMRegister dst, Address src) {Assembler::xorpd(dst, src); } + void xorpd(XMMRegister dst, AddressLiteral src); + + void xorps(XMMRegister dst, XMMRegister src) {Assembler::xorps(dst, src); } + void xorps(XMMRegister dst, Address src) {Assembler::xorps(dst, src); } + void xorps(XMMRegister dst, AddressLiteral src); + + + // Data + + void movoop(Register dst, jobject obj); + void movoop(Address dst, jobject obj); + + void movptr(ArrayAddress dst, Register src); + void movptr(Register dst, AddressLiteral src); + + void movptr(Register dst, intptr_t src); + void movptr(Address dst, intptr_t src); + + void movptr(Register dst, ArrayAddress src); + + // to avoid hiding movl + void mov32(AddressLiteral dst, Register src); + void mov32(Register dst, AddressLiteral src); + + void pushoop(jobject obj); + + // Can push value or effective address + void pushptr(AddressLiteral src); + +}; + +/** + * class SkipIfEqual: + * + * Instantiating this class will result in assembly code being output that will + * jump around any code emitted between the creation of the instance and it's + * automatic destruction at the end of a scope block, depending on the value of + * the flag passed to the constructor, which will be checked at run-time. + */ +class SkipIfEqual { + private: + MacroAssembler* _masm; + Label _label; + + public: + SkipIfEqual(MacroAssembler*, const bool* flag_addr, bool value); + ~SkipIfEqual(); +}; + + +#ifdef ASSERT +inline bool AbstractAssembler::pd_check_instruction_mark() { return true; } +#endif diff --git a/hotspot/src/cpu/x86/vm/assembler_x86_64.inline.hpp b/hotspot/src/cpu/x86/vm/assembler_x86_64.inline.hpp new file mode 100644 index 00000000000..3a705ea3ea1 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/assembler_x86_64.inline.hpp @@ -0,0 +1,86 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void Assembler::emit_long64(jlong x) { + *(jlong*) _code_pos = x; + _code_pos += sizeof(jlong); + code_section()->set_end(_code_pos); +} + +inline void MacroAssembler::pd_patch_instruction(address branch, address target) { + unsigned char op = branch[0]; + assert(op == 0xE8 /* call */ || + op == 0xE9 /* jmp */ || + op == 0xEB /* short jmp */ || + (op & 0xF0) == 0x70 /* short jcc */ || + op == 0x0F && (branch[1] & 0xF0) == 0x80 /* jcc */, + "Invalid opcode at patch point"); + + if (op == 0xEB || (op & 0xF0) == 0x70) { + // short offset operators (jmp and jcc) + char* disp = (char*) &branch[1]; + int imm8 = target - (address) &disp[1]; + guarantee(this->is8bit(imm8), "Short forward jump exceeds 8-bit offset"); + *disp = imm8; + } else { + int* disp = (int*) &branch[(op == 0x0F)? 2: 1]; + int imm32 = target - (address) &disp[1]; + *disp = imm32; + } +} + +#ifndef PRODUCT +inline void MacroAssembler::pd_print_patched_instruction(address branch) { + const char* s; + unsigned char op = branch[0]; + if (op == 0xE8) { + s = "call"; + } else if (op == 0xE9 || op == 0xEB) { + s = "jmp"; + } else if ((op & 0xF0) == 0x70) { + s = "jcc"; + } else if (op == 0x0F) { + s = "jcc"; + } else { + s = "????"; + } + tty->print("%s (unresolved)", s); +} +#endif // ndef PRODUCT + +inline void MacroAssembler::movptr(Address dst, intptr_t src) { +#ifdef _LP64 + Assembler::mov64(dst, src); +#else + Assembler::movl(dst, src); +#endif // _LP64 +} + +inline void MacroAssembler::movptr(Register dst, intptr_t src) { +#ifdef _LP64 + Assembler::mov64(dst, src); +#else + Assembler::movl(dst, src); +#endif // _LP64 +} diff --git a/hotspot/src/cpu/x86/vm/bytecodeInterpreter_x86.cpp b/hotspot/src/cpu/x86/vm/bytecodeInterpreter_x86.cpp new file mode 100644 index 00000000000..63f1574dcad --- /dev/null +++ b/hotspot/src/cpu/x86/vm/bytecodeInterpreter_x86.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_bytecodeInterpreter_x86.cpp.incl" + +#ifdef CC_INTERP + +#endif // CC_INTERP (all) diff --git a/hotspot/src/cpu/x86/vm/bytecodeInterpreter_x86.hpp b/hotspot/src/cpu/x86/vm/bytecodeInterpreter_x86.hpp new file mode 100644 index 00000000000..cc93b7258aa --- /dev/null +++ b/hotspot/src/cpu/x86/vm/bytecodeInterpreter_x86.hpp @@ -0,0 +1,110 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Platform specific for C++ based Interpreter + +private: + + interpreterState _self_link; /* Previous interpreter state */ /* sometimes points to self??? */ + address _result_handler; /* temp for saving native result handler */ + intptr_t* _sender_sp; /* sender's sp before stack (locals) extension */ + + address _extra_junk1; /* temp to save on recompiles */ + address _extra_junk2; /* temp to save on recompiles */ + address _extra_junk3; /* temp to save on recompiles */ + // address dummy_for_native2; /* a native frame result handler would be here... */ + // address dummy_for_native1; /* native result type stored here in a interpreter native frame */ + address _extra_junk4; /* temp to save on recompiles */ + address _extra_junk5; /* temp to save on recompiles */ + address _extra_junk6; /* temp to save on recompiles */ +public: + // we have an interpreter frame... +inline intptr_t* sender_sp() { + return _sender_sp; +} + +// The interpreter always has the frame anchor fully setup so we don't +// have to do anything going to vm from the interpreter. On return +// we do have to clear the flags in case they we're modified to +// maintain the stack walking invariants. +// +#define SET_LAST_JAVA_FRAME() + +#define RESET_LAST_JAVA_FRAME() + +/* + * Macros for accessing the stack. + */ +#undef STACK_INT +#undef STACK_FLOAT +#undef STACK_ADDR +#undef STACK_OBJECT +#undef STACK_DOUBLE +#undef STACK_LONG + +// JavaStack Implementation + +#define GET_STACK_SLOT(offset) (*((intptr_t*) &topOfStack[-(offset)])) +#define STACK_SLOT(offset) ((address) &topOfStack[-(offset)]) +#define STACK_ADDR(offset) (*((address *) &topOfStack[-(offset)])) +#define STACK_INT(offset) (*((jint*) &topOfStack[-(offset)])) +#define STACK_FLOAT(offset) (*((jfloat *) &topOfStack[-(offset)])) +#define STACK_OBJECT(offset) (*((oop *) &topOfStack [-(offset)])) +#define STACK_DOUBLE(offset) (((VMJavaVal64*) &topOfStack[-(offset)])->d) +#define STACK_LONG(offset) (((VMJavaVal64 *) &topOfStack[-(offset)])->l) + +#define SET_STACK_SLOT(value, offset) (*(intptr_t*)&topOfStack[-(offset)] = *(intptr_t*)(value)) +#define SET_STACK_ADDR(value, offset) (*((address *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_INT(value, offset) (*((jint *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_FLOAT(value, offset) (*((jfloat *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_OBJECT(value, offset) (*((oop *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_DOUBLE(value, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->d = (value)) +#define SET_STACK_DOUBLE_FROM_ADDR(addr, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->d = \ + ((VMJavaVal64*)(addr))->d) +#define SET_STACK_LONG(value, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->l = (value)) +#define SET_STACK_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->l = \ + ((VMJavaVal64*)(addr))->l) +// JavaLocals implementation + +#define LOCALS_SLOT(offset) ((intptr_t*)&locals[-(offset)]) +#define LOCALS_ADDR(offset) ((address)locals[-(offset)]) +#define LOCALS_INT(offset) ((jint)(locals[-(offset)])) +#define LOCALS_FLOAT(offset) (*((jfloat*)&locals[-(offset)])) +#define LOCALS_OBJECT(offset) ((oop)locals[-(offset)]) +#define LOCALS_DOUBLE(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->d) +#define LOCALS_LONG(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->l) +#define LOCALS_LONG_AT(offset) (((address)&locals[-((offset) + 1)])) +#define LOCALS_DOUBLE_AT(offset) (((address)&locals[-((offset) + 1)])) + +#define SET_LOCALS_SLOT(value, offset) (*(intptr_t*)&locals[-(offset)] = *(intptr_t *)(value)) +#define SET_LOCALS_ADDR(value, offset) (*((address *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_INT(value, offset) (*((jint *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_FLOAT(value, offset) (*((jfloat *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_OBJECT(value, offset) (*((oop *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_DOUBLE(value, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->d = (value)) +#define SET_LOCALS_LONG(value, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = (value)) +#define SET_LOCALS_DOUBLE_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->d = \ + ((VMJavaVal64*)(addr))->d) +#define SET_LOCALS_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = \ + ((VMJavaVal64*)(addr))->l) diff --git a/hotspot/src/cpu/x86/vm/bytecodeInterpreter_x86.inline.hpp b/hotspot/src/cpu/x86/vm/bytecodeInterpreter_x86.inline.hpp new file mode 100644 index 00000000000..0e41dbc4a21 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/bytecodeInterpreter_x86.inline.hpp @@ -0,0 +1,280 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline interpreter functions for IA32 + +inline jfloat BytecodeInterpreter::VMfloatAdd(jfloat op1, jfloat op2) { return op1 + op2; } +inline jfloat BytecodeInterpreter::VMfloatSub(jfloat op1, jfloat op2) { return op1 - op2; } +inline jfloat BytecodeInterpreter::VMfloatMul(jfloat op1, jfloat op2) { return op1 * op2; } +inline jfloat BytecodeInterpreter::VMfloatDiv(jfloat op1, jfloat op2) { return op1 / op2; } +inline jfloat BytecodeInterpreter::VMfloatRem(jfloat op1, jfloat op2) { return fmod(op1, op2); } + +inline jfloat BytecodeInterpreter::VMfloatNeg(jfloat op) { return -op; } + +inline int32_t BytecodeInterpreter::VMfloatCompare(jfloat op1, jfloat op2, int32_t direction) { + return ( op1 < op2 ? -1 : + op1 > op2 ? 1 : + op1 == op2 ? 0 : + (direction == -1 || direction == 1) ? direction : 0); + +} + +inline void BytecodeInterpreter::VMmemCopy64(uint32_t to[2], const uint32_t from[2]) { + // x86 can do unaligned copies but not 64bits at a time + to[0] = from[0]; to[1] = from[1]; +} + +// The long operations depend on compiler support for "long long" on x86 + +inline jlong BytecodeInterpreter::VMlongAdd(jlong op1, jlong op2) { + return op1 + op2; +} + +inline jlong BytecodeInterpreter::VMlongAnd(jlong op1, jlong op2) { + return op1 & op2; +} + +inline jlong BytecodeInterpreter::VMlongDiv(jlong op1, jlong op2) { + // QQQ what about check and throw... + return op1 / op2; +} + +inline jlong BytecodeInterpreter::VMlongMul(jlong op1, jlong op2) { + return op1 * op2; +} + +inline jlong BytecodeInterpreter::VMlongOr(jlong op1, jlong op2) { + return op1 | op2; +} + +inline jlong BytecodeInterpreter::VMlongSub(jlong op1, jlong op2) { + return op1 - op2; +} + +inline jlong BytecodeInterpreter::VMlongXor(jlong op1, jlong op2) { + return op1 ^ op2; +} + +inline jlong BytecodeInterpreter::VMlongRem(jlong op1, jlong op2) { + return op1 % op2; +} + +inline jlong BytecodeInterpreter::VMlongUshr(jlong op1, jint op2) { + // CVM did this 0x3f mask, is the really needed??? QQQ + return ((unsigned long long) op1) >> (op2 & 0x3F); +} + +inline jlong BytecodeInterpreter::VMlongShr(jlong op1, jint op2) { + return op1 >> (op2 & 0x3F); +} + +inline jlong BytecodeInterpreter::VMlongShl(jlong op1, jint op2) { + return op1 << (op2 & 0x3F); +} + +inline jlong BytecodeInterpreter::VMlongNeg(jlong op) { + return -op; +} + +inline jlong BytecodeInterpreter::VMlongNot(jlong op) { + return ~op; +} + +inline int32_t BytecodeInterpreter::VMlongLtz(jlong op) { + return (op <= 0); +} + +inline int32_t BytecodeInterpreter::VMlongGez(jlong op) { + return (op >= 0); +} + +inline int32_t BytecodeInterpreter::VMlongEqz(jlong op) { + return (op == 0); +} + +inline int32_t BytecodeInterpreter::VMlongEq(jlong op1, jlong op2) { + return (op1 == op2); +} + +inline int32_t BytecodeInterpreter::VMlongNe(jlong op1, jlong op2) { + return (op1 != op2); +} + +inline int32_t BytecodeInterpreter::VMlongGe(jlong op1, jlong op2) { + return (op1 >= op2); +} + +inline int32_t BytecodeInterpreter::VMlongLe(jlong op1, jlong op2) { + return (op1 <= op2); +} + +inline int32_t BytecodeInterpreter::VMlongLt(jlong op1, jlong op2) { + return (op1 < op2); +} + +inline int32_t BytecodeInterpreter::VMlongGt(jlong op1, jlong op2) { + return (op1 > op2); +} + +inline int32_t BytecodeInterpreter::VMlongCompare(jlong op1, jlong op2) { + return (VMlongLt(op1, op2) ? -1 : VMlongGt(op1, op2) ? 1 : 0); +} + +// Long conversions + +inline jdouble BytecodeInterpreter::VMlong2Double(jlong val) { + return (jdouble) val; +} + +inline jfloat BytecodeInterpreter::VMlong2Float(jlong val) { + return (jfloat) val; +} + +inline jint BytecodeInterpreter::VMlong2Int(jlong val) { + return (jint) val; +} + +// Double Arithmetic + +inline jdouble BytecodeInterpreter::VMdoubleAdd(jdouble op1, jdouble op2) { + return op1 + op2; +} + +inline jdouble BytecodeInterpreter::VMdoubleDiv(jdouble op1, jdouble op2) { + // Divide by zero... QQQ + return op1 / op2; +} + +inline jdouble BytecodeInterpreter::VMdoubleMul(jdouble op1, jdouble op2) { + return op1 * op2; +} + +inline jdouble BytecodeInterpreter::VMdoubleNeg(jdouble op) { + return -op; +} + +inline jdouble BytecodeInterpreter::VMdoubleRem(jdouble op1, jdouble op2) { + return fmod(op1, op2); +} + +inline jdouble BytecodeInterpreter::VMdoubleSub(jdouble op1, jdouble op2) { + return op1 - op2; +} + +inline int32_t BytecodeInterpreter::VMdoubleCompare(jdouble op1, jdouble op2, int32_t direction) { + return ( op1 < op2 ? -1 : + op1 > op2 ? 1 : + op1 == op2 ? 0 : + (direction == -1 || direction == 1) ? direction : 0); +} + +// Double Conversions + +inline jfloat BytecodeInterpreter::VMdouble2Float(jdouble val) { + return (jfloat) val; +} + +// Float Conversions + +inline jdouble BytecodeInterpreter::VMfloat2Double(jfloat op) { + return (jdouble) op; +} + +// Integer Arithmetic + +inline jint BytecodeInterpreter::VMintAdd(jint op1, jint op2) { + return op1 + op2; +} + +inline jint BytecodeInterpreter::VMintAnd(jint op1, jint op2) { + return op1 & op2; +} + +inline jint BytecodeInterpreter::VMintDiv(jint op1, jint op2) { + /* it's possible we could catch this special case implicitly */ + if (op1 == 0x80000000 && op2 == -1) return op1; + else return op1 / op2; +} + +inline jint BytecodeInterpreter::VMintMul(jint op1, jint op2) { + return op1 * op2; +} + +inline jint BytecodeInterpreter::VMintNeg(jint op) { + return -op; +} + +inline jint BytecodeInterpreter::VMintOr(jint op1, jint op2) { + return op1 | op2; +} + +inline jint BytecodeInterpreter::VMintRem(jint op1, jint op2) { + /* it's possible we could catch this special case implicitly */ + if (op1 == 0x80000000 && op2 == -1) return 0; + else return op1 % op2; +} + +inline jint BytecodeInterpreter::VMintShl(jint op1, jint op2) { + return op1 << op2; +} + +inline jint BytecodeInterpreter::VMintShr(jint op1, jint op2) { + return op1 >> op2; // QQ op2 & 0x1f?? +} + +inline jint BytecodeInterpreter::VMintSub(jint op1, jint op2) { + return op1 - op2; +} + +inline jint BytecodeInterpreter::VMintUshr(jint op1, jint op2) { + return ((juint) op1) >> op2; // QQ op2 & 0x1f?? +} + +inline jint BytecodeInterpreter::VMintXor(jint op1, jint op2) { + return op1 ^ op2; +} + +inline jdouble BytecodeInterpreter::VMint2Double(jint val) { + return (jdouble) val; +} + +inline jfloat BytecodeInterpreter::VMint2Float(jint val) { + return (jfloat) val; +} + +inline jlong BytecodeInterpreter::VMint2Long(jint val) { + return (jlong) val; +} + +inline jchar BytecodeInterpreter::VMint2Char(jint val) { + return (jchar) val; +} + +inline jshort BytecodeInterpreter::VMint2Short(jint val) { + return (jshort) val; +} + +inline jbyte BytecodeInterpreter::VMint2Byte(jint val) { + return (jbyte) val; +} diff --git a/hotspot/src/cpu/x86/vm/bytecodes_x86.cpp b/hotspot/src/cpu/x86/vm/bytecodes_x86.cpp new file mode 100644 index 00000000000..f74871fe0e5 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/bytecodes_x86.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_bytecodes_x86.cpp.incl" + + +void Bytecodes::pd_initialize() { + // No i486 specific initialization +} + + +Bytecodes::Code Bytecodes::pd_base_code_for(Code code) { + // No i486 specific bytecodes + return code; +} diff --git a/hotspot/src/cpu/x86/vm/bytecodes_x86.hpp b/hotspot/src/cpu/x86/vm/bytecodes_x86.hpp new file mode 100644 index 00000000000..76c17338b86 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/bytecodes_x86.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// No i486 specific bytecodes diff --git a/hotspot/src/cpu/x86/vm/bytes_x86.hpp b/hotspot/src/cpu/x86/vm/bytes_x86.hpp new file mode 100644 index 00000000000..dcbc16250f1 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/bytes_x86.hpp @@ -0,0 +1,70 @@ +/* + * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Bytes: AllStatic { + private: +#ifndef AMD64 + // Helper function for swap_u8 + static inline u8 swap_u8_base(u4 x, u4 y); // compiler-dependent implementation +#endif // AMD64 + + public: + // Returns true if the byte ordering used by Java is different from the native byte ordering + // of the underlying machine. For example, this is true for Intel x86, but false for Solaris + // on Sparc. + static inline bool is_Java_byte_ordering_different(){ return true; } + + + // Efficient reading and writing of unaligned unsigned data in platform-specific byte ordering + // (no special code is needed since x86 CPUs can access unaligned data) + static inline u2 get_native_u2(address p) { return *(u2*)p; } + static inline u4 get_native_u4(address p) { return *(u4*)p; } + static inline u8 get_native_u8(address p) { return *(u8*)p; } + + static inline void put_native_u2(address p, u2 x) { *(u2*)p = x; } + static inline void put_native_u4(address p, u4 x) { *(u4*)p = x; } + static inline void put_native_u8(address p, u8 x) { *(u8*)p = x; } + + + // Efficient reading and writing of unaligned unsigned data in Java + // byte ordering (i.e. big-endian ordering). Byte-order reversal is + // needed since x86 CPUs use little-endian format. + static inline u2 get_Java_u2(address p) { return swap_u2(get_native_u2(p)); } + static inline u4 get_Java_u4(address p) { return swap_u4(get_native_u4(p)); } + static inline u8 get_Java_u8(address p) { return swap_u8(get_native_u8(p)); } + + static inline void put_Java_u2(address p, u2 x) { put_native_u2(p, swap_u2(x)); } + static inline void put_Java_u4(address p, u4 x) { put_native_u4(p, swap_u4(x)); } + static inline void put_Java_u8(address p, u8 x) { put_native_u8(p, swap_u8(x)); } + + + // Efficient swapping of byte ordering + static inline u2 swap_u2(u2 x); // compiler-dependent implementation + static inline u4 swap_u4(u4 x); // compiler-dependent implementation + static inline u8 swap_u8(u8 x); +}; + + +// The following header contains the implementations of swap_u2, swap_u4, and swap_u8[_base] +#include "incls/_bytes_pd.inline.hpp.incl" diff --git a/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp b/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp new file mode 100644 index 00000000000..09419c95650 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp @@ -0,0 +1,459 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_CodeStubs_x86.cpp.incl" + + +#define __ ce->masm()-> + +float ConversionStub::float_zero = 0.0; +double ConversionStub::double_zero = 0.0; + +void ConversionStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + assert(bytecode() == Bytecodes::_f2i || bytecode() == Bytecodes::_d2i, "other conversions do not require stub"); + + + if (input()->is_single_xmm()) { + __ comiss(input()->as_xmm_float_reg(), + ExternalAddress((address)&float_zero)); + } else if (input()->is_double_xmm()) { + __ comisd(input()->as_xmm_double_reg(), + ExternalAddress((address)&double_zero)); + } else { + __ pushl(rax); + __ ftst(); + __ fnstsw_ax(); + __ sahf(); + __ popl(rax); + } + + Label NaN, do_return; + __ jccb(Assembler::parity, NaN); + __ jccb(Assembler::below, do_return); + + // input is > 0 -> return maxInt + // result register already contains 0x80000000, so subtracting 1 gives 0x7fffffff + __ decrement(result()->as_register()); + __ jmpb(do_return); + + // input is NaN -> return 0 + __ bind(NaN); + __ xorl(result()->as_register(), result()->as_register()); + + __ bind(do_return); + __ jmp(_continuation); +} + +#ifdef TIERED +void CounterOverflowStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + ce->store_parameter(_bci, 0); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::counter_overflow_id))); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); + + __ jmp(_continuation); +} +#endif // TIERED + + + +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, + bool throw_index_out_of_bounds_exception) + : _throw_index_out_of_bounds_exception(throw_index_out_of_bounds_exception) + , _index(index) +{ + _info = info == NULL ? NULL : new CodeEmitInfo(info); +} + + +void RangeCheckStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + // pass the array index on stack because all registers must be preserved + if (_index->is_cpu_register()) { + ce->store_parameter(_index->as_register(), 0); + } else { + ce->store_parameter(_index->as_jint(), 0); + } + Runtime1::StubID stub_id; + if (_throw_index_out_of_bounds_exception) { + stub_id = Runtime1::throw_index_exception_id; + } else { + stub_id = Runtime1::throw_range_check_failed_id; + } + __ call(RuntimeAddress(Runtime1::entry_for(stub_id))); + ce->add_call_info_here(_info); + debug_only(__ should_not_reach_here()); +} + + +void DivByZeroStub::emit_code(LIR_Assembler* ce) { + if (_offset != -1) { + ce->compilation()->implicit_exception_table()->append(_offset, __ offset()); + } + __ bind(_entry); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::throw_div0_exception_id))); + ce->add_call_info_here(_info); + debug_only(__ should_not_reach_here()); +} + + +// Implementation of NewInstanceStub + +NewInstanceStub::NewInstanceStub(LIR_Opr klass_reg, LIR_Opr result, ciInstanceKlass* klass, CodeEmitInfo* info, Runtime1::StubID stub_id) { + _result = result; + _klass = klass; + _klass_reg = klass_reg; + _info = new CodeEmitInfo(info); + assert(stub_id == Runtime1::new_instance_id || + stub_id == Runtime1::fast_new_instance_id || + stub_id == Runtime1::fast_new_instance_init_check_id, + "need new_instance id"); + _stub_id = stub_id; +} + + +void NewInstanceStub::emit_code(LIR_Assembler* ce) { + assert(__ rsp_offset() == 0, "frame size should be fixed"); + __ bind(_entry); + __ movl(rdx, _klass_reg->as_register()); + __ call(RuntimeAddress(Runtime1::entry_for(_stub_id))); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); + assert(_result->as_register() == rax, "result must in rax,"); + __ jmp(_continuation); +} + + +// Implementation of NewTypeArrayStub + +NewTypeArrayStub::NewTypeArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, CodeEmitInfo* info) { + _klass_reg = klass_reg; + _length = length; + _result = result; + _info = new CodeEmitInfo(info); +} + + +void NewTypeArrayStub::emit_code(LIR_Assembler* ce) { + assert(__ rsp_offset() == 0, "frame size should be fixed"); + __ bind(_entry); + assert(_length->as_register() == rbx, "length must in rbx,"); + assert(_klass_reg->as_register() == rdx, "klass_reg must in rdx"); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::new_type_array_id))); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); + assert(_result->as_register() == rax, "result must in rax,"); + __ jmp(_continuation); +} + + +// Implementation of NewObjectArrayStub + +NewObjectArrayStub::NewObjectArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, CodeEmitInfo* info) { + _klass_reg = klass_reg; + _result = result; + _length = length; + _info = new CodeEmitInfo(info); +} + + +void NewObjectArrayStub::emit_code(LIR_Assembler* ce) { + assert(__ rsp_offset() == 0, "frame size should be fixed"); + __ bind(_entry); + assert(_length->as_register() == rbx, "length must in rbx,"); + assert(_klass_reg->as_register() == rdx, "klass_reg must in rdx"); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::new_object_array_id))); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); + assert(_result->as_register() == rax, "result must in rax,"); + __ jmp(_continuation); +} + + +// Implementation of MonitorAccessStubs + +MonitorEnterStub::MonitorEnterStub(LIR_Opr obj_reg, LIR_Opr lock_reg, CodeEmitInfo* info) +: MonitorAccessStub(obj_reg, lock_reg) +{ + _info = new CodeEmitInfo(info); +} + + +void MonitorEnterStub::emit_code(LIR_Assembler* ce) { + assert(__ rsp_offset() == 0, "frame size should be fixed"); + __ bind(_entry); + ce->store_parameter(_obj_reg->as_register(), 1); + ce->store_parameter(_lock_reg->as_register(), 0); + Runtime1::StubID enter_id; + if (ce->compilation()->has_fpu_code()) { + enter_id = Runtime1::monitorenter_id; + } else { + enter_id = Runtime1::monitorenter_nofpu_id; + } + __ call(RuntimeAddress(Runtime1::entry_for(enter_id))); + ce->add_call_info_here(_info); + ce->verify_oop_map(_info); + __ jmp(_continuation); +} + + +void MonitorExitStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + if (_compute_lock) { + // lock_reg was destroyed by fast unlocking attempt => recompute it + ce->monitor_address(_monitor_ix, _lock_reg); + } + ce->store_parameter(_lock_reg->as_register(), 0); + // note: non-blocking leaf routine => no call info needed + Runtime1::StubID exit_id; + if (ce->compilation()->has_fpu_code()) { + exit_id = Runtime1::monitorexit_id; + } else { + exit_id = Runtime1::monitorexit_nofpu_id; + } + __ call(RuntimeAddress(Runtime1::entry_for(exit_id))); + __ jmp(_continuation); +} + + +// Implementation of patching: +// - Copy the code at given offset to an inlined buffer (first the bytes, then the number of bytes) +// - Replace original code with a call to the stub +// At Runtime: +// - call to stub, jump to runtime +// - in runtime: preserve all registers (rspecially objects, i.e., source and destination object) +// - in runtime: after initializing class, restore original code, reexecute instruction + +int PatchingStub::_patch_info_offset = -NativeGeneralJump::instruction_size; + +void PatchingStub::align_patch_site(MacroAssembler* masm) { + // We're patching a 5-7 byte instruction on intel and we need to + // make sure that we don't see a piece of the instruction. It + // appears mostly impossible on Intel to simply invalidate other + // processors caches and since they may do aggressive prefetch it's + // very hard to make a guess about what code might be in the icache. + // Force the instruction to be double word aligned so that it + // doesn't span a cache line. + masm->align(round_to(NativeGeneralJump::instruction_size, wordSize)); +} + +void PatchingStub::emit_code(LIR_Assembler* ce) { + assert(NativeCall::instruction_size <= _bytes_to_copy && _bytes_to_copy <= 0xFF, "not enough room for call"); + + Label call_patch; + + // static field accesses have special semantics while the class + // initializer is being run so we emit a test which can be used to + // check that this code is being executed by the initializing + // thread. + address being_initialized_entry = __ pc(); + if (CommentedAssembly) { + __ block_comment(" patch template"); + } + if (_id == load_klass_id) { + // produce a copy of the load klass instruction for use by the being initialized case + address start = __ pc(); + jobject o = NULL; + __ movoop(_obj, o); +#ifdef ASSERT + for (int i = 0; i < _bytes_to_copy; i++) { + address ptr = (address)(_pc_start + i); + int a_byte = (*ptr) & 0xFF; + assert(a_byte == *start++, "should be the same code"); + } +#endif + } else { + // make a copy the code which is going to be patched. + for ( int i = 0; i < _bytes_to_copy; i++) { + address ptr = (address)(_pc_start + i); + int a_byte = (*ptr) & 0xFF; + __ a_byte (a_byte); + *ptr = 0x90; // make the site look like a nop + } + } + + address end_of_patch = __ pc(); + int bytes_to_skip = 0; + if (_id == load_klass_id) { + int offset = __ offset(); + if (CommentedAssembly) { + __ block_comment(" being_initialized check"); + } + assert(_obj != noreg, "must be a valid register"); + Register tmp = rax; + if (_obj == tmp) tmp = rbx; + __ pushl(tmp); + __ get_thread(tmp); + __ cmpl(tmp, Address(_obj, instanceKlass::init_thread_offset_in_bytes() + sizeof(klassOopDesc))); + __ popl(tmp); + __ jcc(Assembler::notEqual, call_patch); + + // access_field patches may execute the patched code before it's + // copied back into place so we need to jump back into the main + // code of the nmethod to continue execution. + __ jmp(_patch_site_continuation); + + // make sure this extra code gets skipped + bytes_to_skip += __ offset() - offset; + } + if (CommentedAssembly) { + __ block_comment("patch data encoded as movl"); + } + // Now emit the patch record telling the runtime how to find the + // pieces of the patch. We only need 3 bytes but for readability of + // the disassembly we make the data look like a movl reg, imm32, + // which requires 5 bytes + int sizeof_patch_record = 5; + bytes_to_skip += sizeof_patch_record; + + // emit the offsets needed to find the code to patch + int being_initialized_entry_offset = __ pc() - being_initialized_entry + sizeof_patch_record; + + __ a_byte(0xB8); + __ a_byte(0); + __ a_byte(being_initialized_entry_offset); + __ a_byte(bytes_to_skip); + __ a_byte(_bytes_to_copy); + address patch_info_pc = __ pc(); + assert(patch_info_pc - end_of_patch == bytes_to_skip, "incorrect patch info"); + + address entry = __ pc(); + NativeGeneralJump::insert_unconditional((address)_pc_start, entry); + address target = NULL; + switch (_id) { + case access_field_id: target = Runtime1::entry_for(Runtime1::access_field_patching_id); break; + case load_klass_id: target = Runtime1::entry_for(Runtime1::load_klass_patching_id); break; + default: ShouldNotReachHere(); + } + __ bind(call_patch); + + if (CommentedAssembly) { + __ block_comment("patch entry point"); + } + __ call(RuntimeAddress(target)); + assert(_patch_info_offset == (patch_info_pc - __ pc()), "must not change"); + ce->add_call_info_here(_info); + int jmp_off = __ offset(); + __ jmp(_patch_site_entry); + // Add enough nops so deoptimization can overwrite the jmp above with a call + // and not destroy the world. + for (int j = __ offset() ; j < jmp_off + 5 ; j++ ) { + __ nop(); + } + if (_id == load_klass_id) { + CodeSection* cs = __ code_section(); + RelocIterator iter(cs, (address)_pc_start, (address)(_pc_start + 1)); + relocInfo::change_reloc_info_for_address(&iter, (address) _pc_start, relocInfo::oop_type, relocInfo::none); + } +} + + +void ImplicitNullCheckStub::emit_code(LIR_Assembler* ce) { + ce->compilation()->implicit_exception_table()->append(_offset, __ offset()); + __ bind(_entry); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::throw_null_pointer_exception_id))); + ce->add_call_info_here(_info); + debug_only(__ should_not_reach_here()); +} + + +void SimpleExceptionStub::emit_code(LIR_Assembler* ce) { + assert(__ rsp_offset() == 0, "frame size should be fixed"); + + __ bind(_entry); + // pass the object on stack because all registers must be preserved + if (_obj->is_cpu_register()) { + ce->store_parameter(_obj->as_register(), 0); + } + __ call(RuntimeAddress(Runtime1::entry_for(_stub))); + ce->add_call_info_here(_info); + debug_only(__ should_not_reach_here()); +} + + +ArrayStoreExceptionStub::ArrayStoreExceptionStub(CodeEmitInfo* info): + _info(info) { +} + + +void ArrayStoreExceptionStub::emit_code(LIR_Assembler* ce) { + assert(__ rsp_offset() == 0, "frame size should be fixed"); + __ bind(_entry); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::throw_array_store_exception_id))); + ce->add_call_info_here(_info); + debug_only(__ should_not_reach_here()); +} + + +void ArrayCopyStub::emit_code(LIR_Assembler* ce) { + //---------------slow case: call to native----------------- + __ bind(_entry); + // Figure out where the args should go + // This should really convert the IntrinsicID to the methodOop and signature + // but I don't know how to do that. + // + VMRegPair args[5]; + BasicType signature[5] = { T_OBJECT, T_INT, T_OBJECT, T_INT, T_INT}; + SharedRuntime::java_calling_convention(signature, args, 5, true); + + // push parameters + // (src, src_pos, dest, destPos, length) + Register r[5]; + r[0] = src()->as_register(); + r[1] = src_pos()->as_register(); + r[2] = dst()->as_register(); + r[3] = dst_pos()->as_register(); + r[4] = length()->as_register(); + + // next registers will get stored on the stack + for (int i = 0; i < 5 ; i++ ) { + VMReg r_1 = args[i].first(); + if (r_1->is_stack()) { + int st_off = r_1->reg2stack() * wordSize; + __ movl (Address(rsp, st_off), r[i]); + } else { + assert(r[i] == args[i].first()->as_Register(), "Wrong register for arg "); + } + } + + ce->align_call(lir_static_call); + + ce->emit_static_call_stub(); + AddressLiteral resolve(SharedRuntime::get_resolve_static_call_stub(), + relocInfo::static_call_type); + __ call(resolve); + ce->add_call_info_here(info()); + +#ifndef PRODUCT + __ increment(ExternalAddress((address)&Runtime1::_arraycopy_slowcase_cnt)); +#endif + + __ jmp(_continuation); +} + + +#undef __ diff --git a/hotspot/src/cpu/x86/vm/c1_Defs_x86.hpp b/hotspot/src/cpu/x86/vm/c1_Defs_x86.hpp new file mode 100644 index 00000000000..8594a834907 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_Defs_x86.hpp @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// native word offsets from memory address (little endian) +enum { + pd_lo_word_offset_in_bytes = 0, + pd_hi_word_offset_in_bytes = BytesPerWord +}; + +// explicit rounding operations are required to implement the strictFP mode +enum { + pd_strict_fp_requires_explicit_rounding = true +}; + + +// registers +enum { + pd_nof_cpu_regs_frame_map = 8, // number of registers used during code emission + pd_nof_fpu_regs_frame_map = 8, // number of registers used during code emission + pd_nof_xmm_regs_frame_map = 8, // number of registers used during code emission + pd_nof_caller_save_cpu_regs_frame_map = 6, // number of registers killed by calls + pd_nof_caller_save_fpu_regs_frame_map = 8, // number of registers killed by calls + pd_nof_caller_save_xmm_regs_frame_map = 8, // number of registers killed by calls + + pd_nof_cpu_regs_reg_alloc = 6, // number of registers that are visible to register allocator + pd_nof_fpu_regs_reg_alloc = 6, // number of registers that are visible to register allocator + + pd_nof_cpu_regs_linearscan = 8, // number of registers visible to linear scan + pd_nof_fpu_regs_linearscan = 8, // number of registers visible to linear scan + pd_nof_xmm_regs_linearscan = 8, // number of registers visible to linear scan + pd_first_cpu_reg = 0, + pd_last_cpu_reg = 5, + pd_first_byte_reg = 2, + pd_last_byte_reg = 5, + pd_first_fpu_reg = pd_nof_cpu_regs_frame_map, + pd_last_fpu_reg = pd_first_fpu_reg + 7, + pd_first_xmm_reg = pd_nof_cpu_regs_frame_map + pd_nof_fpu_regs_frame_map, + pd_last_xmm_reg = pd_first_xmm_reg + 7 +}; + + +// encoding of float value in debug info: +enum { + pd_float_saved_as_double = true +}; diff --git a/hotspot/src/cpu/x86/vm/c1_FpuStackSim_x86.cpp b/hotspot/src/cpu/x86/vm/c1_FpuStackSim_x86.cpp new file mode 100644 index 00000000000..8074c093241 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_FpuStackSim_x86.cpp @@ -0,0 +1,198 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_FpuStackSim_x86.cpp.incl" + +//-------------------------------------------------------- +// FpuStackSim +//-------------------------------------------------------- + +// This class maps the FPU registers to their stack locations; it computes +// the offsets between individual registers and simulates the FPU stack. + +const int EMPTY = -1; + +int FpuStackSim::regs_at(int i) const { + assert(i >= 0 && i < FrameMap::nof_fpu_regs, "out of bounds"); + return _regs[i]; +} + +void FpuStackSim::set_regs_at(int i, int val) { + assert(i >= 0 && i < FrameMap::nof_fpu_regs, "out of bounds"); + _regs[i] = val; +} + +void FpuStackSim::dec_stack_size() { + _stack_size--; + assert(_stack_size >= 0, "FPU stack underflow"); +} + +void FpuStackSim::inc_stack_size() { + _stack_size++; + assert(_stack_size <= FrameMap::nof_fpu_regs, "FPU stack overflow"); +} + +FpuStackSim::FpuStackSim(Compilation* compilation) + : _compilation(compilation) +{ + _stack_size = 0; + for (int i = 0; i < FrameMap::nof_fpu_regs; i++) { + set_regs_at(i, EMPTY); + } +} + + +void FpuStackSim::pop() { + if (TraceFPUStack) { tty->print("FPU-pop "); print(); tty->cr(); } + set_regs_at(tos_index(), EMPTY); + dec_stack_size(); +} + +void FpuStackSim::pop(int rnr) { + if (TraceFPUStack) { tty->print("FPU-pop %d", rnr); print(); tty->cr(); } + assert(regs_at(tos_index()) == rnr, "rnr is not on TOS"); + set_regs_at(tos_index(), EMPTY); + dec_stack_size(); +} + + +void FpuStackSim::push(int rnr) { + if (TraceFPUStack) { tty->print("FPU-push %d", rnr); print(); tty->cr(); } + assert(regs_at(stack_size()) == EMPTY, "should be empty"); + set_regs_at(stack_size(), rnr); + inc_stack_size(); +} + + +void FpuStackSim::swap(int offset) { + if (TraceFPUStack) { tty->print("FPU-swap %d", offset); print(); tty->cr(); } + int t = regs_at(tos_index() - offset); + set_regs_at(tos_index() - offset, regs_at(tos_index())); + set_regs_at(tos_index(), t); +} + + +int FpuStackSim::offset_from_tos(int rnr) const { + for (int i = tos_index(); i >= 0; i--) { + if (regs_at(i) == rnr) { + return tos_index() - i; + } + } + assert(false, "FpuStackSim: register not found"); + BAILOUT_("FpuStackSim: register not found", 0); +} + + +int FpuStackSim::get_slot(int tos_offset) const { + return regs_at(tos_index() - tos_offset); +} + +void FpuStackSim::set_slot(int tos_offset, int rnr) { + set_regs_at(tos_index() - tos_offset, rnr); +} + +void FpuStackSim::rename(int old_rnr, int new_rnr) { + if (TraceFPUStack) { tty->print("FPU-rename %d %d", old_rnr, new_rnr); print(); tty->cr(); } + if (old_rnr == new_rnr) + return; + bool found = false; + for (int i = 0; i < stack_size(); i++) { + assert(regs_at(i) != new_rnr, "should not see old occurrences of new_rnr on the stack"); + if (regs_at(i) == old_rnr) { + set_regs_at(i, new_rnr); + found = true; + } + } + assert(found, "should have found at least one instance of old_rnr"); +} + + +bool FpuStackSim::contains(int rnr) { + for (int i = 0; i < stack_size(); i++) { + if (regs_at(i) == rnr) { + return true; + } + } + return false; +} + +bool FpuStackSim::is_empty() { +#ifdef ASSERT + if (stack_size() == 0) { + for (int i = 0; i < FrameMap::nof_fpu_regs; i++) { + assert(regs_at(i) == EMPTY, "must be empty"); + } + } +#endif + return stack_size() == 0; +} + + +bool FpuStackSim::slot_is_empty(int tos_offset) { + return (regs_at(tos_index() - tos_offset) == EMPTY); +} + + +void FpuStackSim::clear() { + if (TraceFPUStack) { tty->print("FPU-clear"); print(); tty->cr(); } + for (int i = tos_index(); i >= 0; i--) { + set_regs_at(i, EMPTY); + } + _stack_size = 0; +} + + +intArray* FpuStackSim::write_state() { + intArray* res = new intArray(1 + FrameMap::nof_fpu_regs); + (*res)[0] = stack_size(); + for (int i = 0; i < FrameMap::nof_fpu_regs; i++) { + (*res)[1 + i] = regs_at(i); + } + return res; +} + + +void FpuStackSim::read_state(intArray* fpu_stack_state) { + _stack_size = (*fpu_stack_state)[0]; + for (int i = 0; i < FrameMap::nof_fpu_regs; i++) { + set_regs_at(i, (*fpu_stack_state)[1 + i]); + } +} + + +#ifndef PRODUCT +void FpuStackSim::print() { + tty->print(" N=%d[", stack_size());\ + for (int i = 0; i < stack_size(); i++) { + int reg = regs_at(i); + if (reg != EMPTY) { + tty->print("%d", reg); + } else { + tty->print("_"); + } + }; + tty->print(" ]"); +} +#endif diff --git a/hotspot/src/cpu/x86/vm/c1_FpuStackSim_x86.hpp b/hotspot/src/cpu/x86/vm/c1_FpuStackSim_x86.hpp new file mode 100644 index 00000000000..5460a24fbb3 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_FpuStackSim_x86.hpp @@ -0,0 +1,67 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Simulates the FPU stack and maintains mapping [fpu-register -> stack offset] +// FPU registers are described as numbers from 0..nof_fpu_regs-1 + +class Compilation; + +class FpuStackSim VALUE_OBJ_CLASS_SPEC { + private: + Compilation* _compilation; + int _stack_size; + int _regs[FrameMap::nof_fpu_regs]; + + int tos_index() const { return _stack_size - 1; } + + int regs_at(int i) const; + void set_regs_at(int i, int val); + void dec_stack_size(); + void inc_stack_size(); + + // unified bailout support + Compilation* compilation() const { return _compilation; } + void bailout(const char* msg) const { compilation()->bailout(msg); } + bool bailed_out() const { return compilation()->bailed_out(); } + + public: + FpuStackSim(Compilation* compilation); + void pop (); + void pop (int rnr); // rnr must be on tos + void push(int rnr); + void swap(int offset); // exchange tos with tos + offset + int offset_from_tos(int rnr) const; // return the offset of the topmost instance of rnr from TOS + int get_slot(int tos_offset) const; // return the entry at the given offset from TOS + void set_slot(int tos_offset, int rnr); // set the entry at the given offset from TOS + void rename(int old_rnr, int new_rnr); // rename all instances of old_rnr to new_rnr + bool contains(int rnr); // debugging support only + bool is_empty(); + bool slot_is_empty(int tos_offset); + int stack_size() const { return _stack_size; } + void clear(); + intArray* write_state(); + void read_state(intArray* fpu_stack_state); + + void print() PRODUCT_RETURN; +}; diff --git a/hotspot/src/cpu/x86/vm/c1_FrameMap_x86.cpp b/hotspot/src/cpu/x86/vm/c1_FrameMap_x86.cpp new file mode 100644 index 00000000000..2118ec4483e --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_FrameMap_x86.cpp @@ -0,0 +1,225 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_FrameMap_x86.cpp.incl" + +const int FrameMap::pd_c_runtime_reserved_arg_size = 0; + +LIR_Opr FrameMap::map_to_opr(BasicType type, VMRegPair* reg, bool) { + LIR_Opr opr = LIR_OprFact::illegalOpr; + VMReg r_1 = reg->first(); + VMReg r_2 = reg->second(); + if (r_1->is_stack()) { + // Convert stack slot to an SP offset + // The calling convention does not count the SharedRuntime::out_preserve_stack_slots() value + // so we must add it in here. + int st_off = (r_1->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size; + opr = LIR_OprFact::address(new LIR_Address(rsp_opr, st_off, type)); + } else if (r_1->is_Register()) { + Register reg = r_1->as_Register(); + if (r_2->is_Register()) { + Register reg2 = r_2->as_Register(); + opr = as_long_opr(reg2, reg); + } else if (type == T_OBJECT) { + opr = as_oop_opr(reg); + } else { + opr = as_opr(reg); + } + } else if (r_1->is_FloatRegister()) { + assert(type == T_DOUBLE || type == T_FLOAT, "wrong type"); + int num = r_1->as_FloatRegister()->encoding(); + if (type == T_FLOAT) { + opr = LIR_OprFact::single_fpu(num); + } else { + opr = LIR_OprFact::double_fpu(num); + } + } else if (r_1->is_XMMRegister()) { + assert(type == T_DOUBLE || type == T_FLOAT, "wrong type"); + int num = r_1->as_XMMRegister()->encoding(); + if (type == T_FLOAT) { + opr = LIR_OprFact::single_xmm(num); + } else { + opr = LIR_OprFact::double_xmm(num); + } + } else { + ShouldNotReachHere(); + } + return opr; +} + + +LIR_Opr FrameMap::rsi_opr; +LIR_Opr FrameMap::rdi_opr; +LIR_Opr FrameMap::rbx_opr; +LIR_Opr FrameMap::rax_opr; +LIR_Opr FrameMap::rdx_opr; +LIR_Opr FrameMap::rcx_opr; +LIR_Opr FrameMap::rsp_opr; +LIR_Opr FrameMap::rbp_opr; + +LIR_Opr FrameMap::receiver_opr; + +LIR_Opr FrameMap::rsi_oop_opr; +LIR_Opr FrameMap::rdi_oop_opr; +LIR_Opr FrameMap::rbx_oop_opr; +LIR_Opr FrameMap::rax_oop_opr; +LIR_Opr FrameMap::rdx_oop_opr; +LIR_Opr FrameMap::rcx_oop_opr; + +LIR_Opr FrameMap::rax_rdx_long_opr; +LIR_Opr FrameMap::rbx_rcx_long_opr; +LIR_Opr FrameMap::fpu0_float_opr; +LIR_Opr FrameMap::fpu0_double_opr; +LIR_Opr FrameMap::xmm0_float_opr; +LIR_Opr FrameMap::xmm0_double_opr; + +LIR_Opr FrameMap::_caller_save_cpu_regs[] = { 0, }; +LIR_Opr FrameMap::_caller_save_fpu_regs[] = { 0, }; +LIR_Opr FrameMap::_caller_save_xmm_regs[] = { 0, }; + +XMMRegister FrameMap::_xmm_regs [8] = { 0, }; + +XMMRegister FrameMap::nr2xmmreg(int rnr) { + assert(_init_done, "tables not initialized"); + return _xmm_regs[rnr]; +} + +//-------------------------------------------------------- +// FrameMap +//-------------------------------------------------------- + +void FrameMap::init() { + if (_init_done) return; + + assert(nof_cpu_regs == 8, "wrong number of CPU registers"); + map_register(0, rsi); rsi_opr = LIR_OprFact::single_cpu(0); rsi_oop_opr = LIR_OprFact::single_cpu_oop(0); + map_register(1, rdi); rdi_opr = LIR_OprFact::single_cpu(1); rdi_oop_opr = LIR_OprFact::single_cpu_oop(1); + map_register(2, rbx); rbx_opr = LIR_OprFact::single_cpu(2); rbx_oop_opr = LIR_OprFact::single_cpu_oop(2); + map_register(3, rax); rax_opr = LIR_OprFact::single_cpu(3); rax_oop_opr = LIR_OprFact::single_cpu_oop(3); + map_register(4, rdx); rdx_opr = LIR_OprFact::single_cpu(4); rdx_oop_opr = LIR_OprFact::single_cpu_oop(4); + map_register(5, rcx); rcx_opr = LIR_OprFact::single_cpu(5); rcx_oop_opr = LIR_OprFact::single_cpu_oop(5); + map_register(6, rsp); rsp_opr = LIR_OprFact::single_cpu(6); + map_register(7, rbp); rbp_opr = LIR_OprFact::single_cpu(7); + + rax_rdx_long_opr = LIR_OprFact::double_cpu(3 /*eax*/, 4 /*edx*/); + rbx_rcx_long_opr = LIR_OprFact::double_cpu(2 /*ebx*/, 5 /*ecx*/); + fpu0_float_opr = LIR_OprFact::single_fpu(0); + fpu0_double_opr = LIR_OprFact::double_fpu(0); + xmm0_float_opr = LIR_OprFact::single_xmm(0); + xmm0_double_opr = LIR_OprFact::double_xmm(0); + + _caller_save_cpu_regs[0] = rsi_opr; + _caller_save_cpu_regs[1] = rdi_opr; + _caller_save_cpu_regs[2] = rbx_opr; + _caller_save_cpu_regs[3] = rax_opr; + _caller_save_cpu_regs[4] = rdx_opr; + _caller_save_cpu_regs[5] = rcx_opr; + + + _xmm_regs[0] = xmm0; + _xmm_regs[1] = xmm1; + _xmm_regs[2] = xmm2; + _xmm_regs[3] = xmm3; + _xmm_regs[4] = xmm4; + _xmm_regs[5] = xmm5; + _xmm_regs[6] = xmm6; + _xmm_regs[7] = xmm7; + + for (int i = 0; i < 8; i++) { + _caller_save_fpu_regs[i] = LIR_OprFact::single_fpu(i); + _caller_save_xmm_regs[i] = LIR_OprFact::single_xmm(i); + } + + _init_done = true; + + VMRegPair regs; + BasicType sig_bt = T_OBJECT; + SharedRuntime::java_calling_convention(&sig_bt, ®s, 1, true); + receiver_opr = as_oop_opr(regs.first()->as_Register()); + assert(receiver_opr == rcx_oop_opr, "rcvr ought to be rcx"); +} + + +Address FrameMap::make_new_address(ByteSize sp_offset) const { + // for rbp, based address use this: + // return Address(rbp, in_bytes(sp_offset) - (framesize() - 2) * 4); + return Address(rsp, in_bytes(sp_offset)); +} + + +// ----------------mapping----------------------- +// all mapping is based on rbp, addressing, except for simple leaf methods where we access +// the locals rsp based (and no frame is built) + + +// Frame for simple leaf methods (quick entries) +// +// +----------+ +// | ret addr | <- TOS +// +----------+ +// | args | +// | ...... | + +// Frame for standard methods +// +// | .........| <- TOS +// | locals | +// +----------+ +// | old rbp, | <- EBP +// +----------+ +// | ret addr | +// +----------+ +// | args | +// | .........| + + +// For OopMaps, map a local variable or spill index to an VMRegImpl name. +// This is the offset from sp() in the frame of the slot for the index, +// skewed by VMRegImpl::stack0 to indicate a stack location (vs.a register.) +// +// framesize + +// stack0 stack0 0 <- VMReg +// | | | +// ...........|..............|.............| +// 0 1 2 3 x x 4 5 6 ... | <- local indices +// ^ ^ sp() ( x x indicate link +// | | and return addr) +// arguments non-argument locals + + +VMReg FrameMap::fpu_regname (int n) { + // Return the OptoReg name for the fpu stack slot "n" + // A spilled fpu stack slot comprises to two single-word OptoReg's. + return as_FloatRegister(n)->as_VMReg(); +} + +LIR_Opr FrameMap::stack_pointer() { + return FrameMap::rsp_opr; +} + + +bool FrameMap::validate_frame() { + return true; +} diff --git a/hotspot/src/cpu/x86/vm/c1_FrameMap_x86.hpp b/hotspot/src/cpu/x86/vm/c1_FrameMap_x86.hpp new file mode 100644 index 00000000000..419b8600a3c --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_FrameMap_x86.hpp @@ -0,0 +1,91 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// On i486 the frame looks as follows: +// +// +-----------------------------+---------+----------------------------------------+----------------+----------- +// | size_arguments-nof_reg_args | 2 words | size_locals-size_arguments+numreg_args | _size_monitors | spilling . +// +-----------------------------+---------+----------------------------------------+----------------+----------- +// +// The FPU registers are mapped with their offset from TOS; therefore the +// status of FPU stack must be updated during code emission. + + public: + static const int pd_c_runtime_reserved_arg_size; + + enum { + nof_xmm_regs = pd_nof_xmm_regs_frame_map, + nof_caller_save_xmm_regs = pd_nof_caller_save_xmm_regs_frame_map, + first_available_sp_in_frame = 0, + frame_pad_in_bytes = 8, + nof_reg_args = 2 + }; + + private: + static LIR_Opr _caller_save_xmm_regs [nof_caller_save_xmm_regs]; + + static XMMRegister _xmm_regs[nof_xmm_regs]; + + public: + static LIR_Opr receiver_opr; + + static LIR_Opr rsi_opr; + static LIR_Opr rdi_opr; + static LIR_Opr rbx_opr; + static LIR_Opr rax_opr; + static LIR_Opr rdx_opr; + static LIR_Opr rcx_opr; + static LIR_Opr rsp_opr; + static LIR_Opr rbp_opr; + + static LIR_Opr rsi_oop_opr; + static LIR_Opr rdi_oop_opr; + static LIR_Opr rbx_oop_opr; + static LIR_Opr rax_oop_opr; + static LIR_Opr rdx_oop_opr; + static LIR_Opr rcx_oop_opr; + + static LIR_Opr rax_rdx_long_opr; + static LIR_Opr rbx_rcx_long_opr; + static LIR_Opr fpu0_float_opr; + static LIR_Opr fpu0_double_opr; + static LIR_Opr xmm0_float_opr; + static LIR_Opr xmm0_double_opr; + + static LIR_Opr as_long_opr(Register r, Register r2) { + return LIR_OprFact::double_cpu(cpu_reg2rnr(r), cpu_reg2rnr(r2)); + } + + // VMReg name for spilled physical FPU stack slot n + static VMReg fpu_regname (int n); + + static XMMRegister nr2xmmreg(int rnr); + + static bool is_caller_save_register (LIR_Opr opr) { return true; } + static bool is_caller_save_register (Register r) { return true; } + + static LIR_Opr caller_save_xmm_reg_at(int i) { + assert(i >= 0 && i < nof_caller_save_xmm_regs, "out of bounds"); + return _caller_save_xmm_regs[i]; + } diff --git a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp new file mode 100644 index 00000000000..e87d55ce228 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp @@ -0,0 +1,3136 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_LIRAssembler_x86.cpp.incl" + + +// These masks are used to provide 128-bit aligned bitmasks to the XMM +// instructions, to allow sign-masking or sign-bit flipping. They allow +// fast versions of NegF/NegD and AbsF/AbsD. + +// Note: 'double' and 'long long' have 32-bits alignment on x86. +static jlong* double_quadword(jlong *adr, jlong lo, jlong hi) { + // Use the expression (adr)&(~0xF) to provide 128-bits aligned address + // of 128-bits operands for SSE instructions. + jlong *operand = (jlong*)(((long)adr)&((long)(~0xF))); + // Store the value to a 128-bits operand. + operand[0] = lo; + operand[1] = hi; + return operand; +} + +// Buffer for 128-bits masks used by SSE instructions. +static jlong fp_signmask_pool[(4+1)*2]; // 4*128bits(data) + 128bits(alignment) + +// Static initialization during VM startup. +static jlong *float_signmask_pool = double_quadword(&fp_signmask_pool[1*2], CONST64(0x7FFFFFFF7FFFFFFF), CONST64(0x7FFFFFFF7FFFFFFF)); +static jlong *double_signmask_pool = double_quadword(&fp_signmask_pool[2*2], CONST64(0x7FFFFFFFFFFFFFFF), CONST64(0x7FFFFFFFFFFFFFFF)); +static jlong *float_signflip_pool = double_quadword(&fp_signmask_pool[3*2], CONST64(0x8000000080000000), CONST64(0x8000000080000000)); +static jlong *double_signflip_pool = double_quadword(&fp_signmask_pool[4*2], CONST64(0x8000000000000000), CONST64(0x8000000000000000)); + + + +NEEDS_CLEANUP // remove this definitions ? +const Register IC_Klass = rax; // where the IC klass is cached +const Register SYNC_header = rax; // synchronization header +const Register SHIFT_count = rcx; // where count for shift operations must be + +#define __ _masm-> + + +static void select_different_registers(Register preserve, + Register extra, + Register &tmp1, + Register &tmp2) { + if (tmp1 == preserve) { + assert_different_registers(tmp1, tmp2, extra); + tmp1 = extra; + } else if (tmp2 == preserve) { + assert_different_registers(tmp1, tmp2, extra); + tmp2 = extra; + } + assert_different_registers(preserve, tmp1, tmp2); +} + + + +static void select_different_registers(Register preserve, + Register extra, + Register &tmp1, + Register &tmp2, + Register &tmp3) { + if (tmp1 == preserve) { + assert_different_registers(tmp1, tmp2, tmp3, extra); + tmp1 = extra; + } else if (tmp2 == preserve) { + assert_different_registers(tmp1, tmp2, tmp3, extra); + tmp2 = extra; + } else if (tmp3 == preserve) { + assert_different_registers(tmp1, tmp2, tmp3, extra); + tmp3 = extra; + } + assert_different_registers(preserve, tmp1, tmp2, tmp3); +} + + + +bool LIR_Assembler::is_small_constant(LIR_Opr opr) { + if (opr->is_constant()) { + LIR_Const* constant = opr->as_constant_ptr(); + switch (constant->type()) { + case T_INT: { + return true; + } + + default: + return false; + } + } + return false; +} + + +LIR_Opr LIR_Assembler::receiverOpr() { + return FrameMap::rcx_oop_opr; +} + +LIR_Opr LIR_Assembler::incomingReceiverOpr() { + return receiverOpr(); +} + +LIR_Opr LIR_Assembler::osrBufferPointer() { + return FrameMap::rcx_opr; +} + +//--------------fpu register translations----------------------- + + +address LIR_Assembler::float_constant(float f) { + address const_addr = __ float_constant(f); + if (const_addr == NULL) { + bailout("const section overflow"); + return __ code()->consts()->start(); + } else { + return const_addr; + } +} + + +address LIR_Assembler::double_constant(double d) { + address const_addr = __ double_constant(d); + if (const_addr == NULL) { + bailout("const section overflow"); + return __ code()->consts()->start(); + } else { + return const_addr; + } +} + + +void LIR_Assembler::set_24bit_FPU() { + __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_24())); +} + +void LIR_Assembler::reset_FPU() { + __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); +} + +void LIR_Assembler::fpop() { + __ fpop(); +} + +void LIR_Assembler::fxch(int i) { + __ fxch(i); +} + +void LIR_Assembler::fld(int i) { + __ fld_s(i); +} + +void LIR_Assembler::ffree(int i) { + __ ffree(i); +} + +void LIR_Assembler::breakpoint() { + __ int3(); +} + +void LIR_Assembler::push(LIR_Opr opr) { + if (opr->is_single_cpu()) { + __ push_reg(opr->as_register()); + } else if (opr->is_double_cpu()) { + __ push_reg(opr->as_register_hi()); + __ push_reg(opr->as_register_lo()); + } else if (opr->is_stack()) { + __ push_addr(frame_map()->address_for_slot(opr->single_stack_ix())); + } else if (opr->is_constant()) { + LIR_Const* const_opr = opr->as_constant_ptr(); + if (const_opr->type() == T_OBJECT) { + __ push_oop(const_opr->as_jobject()); + } else if (const_opr->type() == T_INT) { + __ push_jint(const_opr->as_jint()); + } else { + ShouldNotReachHere(); + } + + } else { + ShouldNotReachHere(); + } +} + +void LIR_Assembler::pop(LIR_Opr opr) { + if (opr->is_single_cpu()) { + __ pop(opr->as_register()); + } else { + ShouldNotReachHere(); + } +} + +//------------------------------------------- +Address LIR_Assembler::as_Address(LIR_Address* addr) { + if (addr->base()->is_illegal()) { + assert(addr->index()->is_illegal(), "must be illegal too"); + //return Address(addr->disp(), relocInfo::none); + // hack for now since this should really return an AddressLiteral + // which will have to await 64bit c1 changes. + return Address(noreg, addr->disp()); + } + + Register base = addr->base()->as_register(); + + if (addr->index()->is_illegal()) { + return Address( base, addr->disp()); + } else if (addr->index()->is_single_cpu()) { + Register index = addr->index()->as_register(); + return Address(base, index, (Address::ScaleFactor) addr->scale(), addr->disp()); + } else if (addr->index()->is_constant()) { + int addr_offset = (addr->index()->as_constant_ptr()->as_jint() << addr->scale()) + addr->disp(); + + return Address(base, addr_offset); + } else { + Unimplemented(); + return Address(); + } +} + + +Address LIR_Assembler::as_Address_hi(LIR_Address* addr) { + Address base = as_Address(addr); + return Address(base._base, base._index, base._scale, base._disp + BytesPerWord); +} + + +Address LIR_Assembler::as_Address_lo(LIR_Address* addr) { + return as_Address(addr); +} + + +void LIR_Assembler::osr_entry() { + offsets()->set_value(CodeOffsets::OSR_Entry, code_offset()); + BlockBegin* osr_entry = compilation()->hir()->osr_entry(); + ValueStack* entry_state = osr_entry->state(); + int number_of_locks = entry_state->locks_size(); + + // we jump here if osr happens with the interpreter + // state set up to continue at the beginning of the + // loop that triggered osr - in particular, we have + // the following registers setup: + // + // rcx: osr buffer + // + + // build frame + ciMethod* m = compilation()->method(); + __ build_frame(initial_frame_size_in_bytes()); + + // OSR buffer is + // + // locals[nlocals-1..0] + // monitors[0..number_of_locks] + // + // locals is a direct copy of the interpreter frame so in the osr buffer + // so first slot in the local array is the last local from the interpreter + // and last slot is local[0] (receiver) from the interpreter + // + // Similarly with locks. The first lock slot in the osr buffer is the nth lock + // from the interpreter frame, the nth lock slot in the osr buffer is 0th lock + // in the interpreter frame (the method lock if a sync method) + + // Initialize monitors in the compiled activation. + // rcx: pointer to osr buffer + // + // All other registers are dead at this point and the locals will be + // copied into place by code emitted in the IR. + + Register OSR_buf = osrBufferPointer()->as_register(); + { assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); + int monitor_offset = BytesPerWord * method()->max_locals() + + (BasicObjectLock::size() * BytesPerWord) * (number_of_locks - 1); + for (int i = 0; i < number_of_locks; i++) { + int slot_offset = monitor_offset - ((i * BasicObjectLock::size()) * BytesPerWord); +#ifdef ASSERT + // verify the interpreter's monitor has a non-null object + { + Label L; + __ cmpl(Address(OSR_buf, slot_offset + BasicObjectLock::obj_offset_in_bytes()), NULL_WORD); + __ jcc(Assembler::notZero, L); + __ stop("locked object is NULL"); + __ bind(L); + } +#endif + __ movl(rbx, Address(OSR_buf, slot_offset + BasicObjectLock::lock_offset_in_bytes())); + __ movl(frame_map()->address_for_monitor_lock(i), rbx); + __ movl(rbx, Address(OSR_buf, slot_offset + BasicObjectLock::obj_offset_in_bytes())); + __ movl(frame_map()->address_for_monitor_object(i), rbx); + } + } +} + + +// inline cache check; done before the frame is built. +int LIR_Assembler::check_icache() { + Register receiver = FrameMap::receiver_opr->as_register(); + Register ic_klass = IC_Klass; + + if (!VerifyOops) { + // insert some nops so that the verified entry point is aligned on CodeEntryAlignment + while ((__ offset() + 9) % CodeEntryAlignment != 0) { + __ nop(); + } + } + int offset = __ offset(); + __ inline_cache_check(receiver, IC_Klass); + assert(__ offset() % CodeEntryAlignment == 0 || VerifyOops, "alignment must be correct"); + if (VerifyOops) { + // force alignment after the cache check. + // It's been verified to be aligned if !VerifyOops + __ align(CodeEntryAlignment); + } + return offset; +} + + +void LIR_Assembler::jobject2reg_with_patching(Register reg, CodeEmitInfo* info) { + jobject o = NULL; + PatchingStub* patch = new PatchingStub(_masm, PatchingStub::load_klass_id); + __ movoop(reg, o); + patching_epilog(patch, lir_patch_normal, reg, info); +} + + +void LIR_Assembler::monitorexit(LIR_Opr obj_opr, LIR_Opr lock_opr, Register new_hdr, int monitor_no, Register exception) { + if (exception->is_valid()) { + // preserve exception + // note: the monitor_exit runtime call is a leaf routine + // and cannot block => no GC can happen + // The slow case (MonitorAccessStub) uses the first two stack slots + // ([esp+0] and [esp+4]), therefore we store the exception at [esp+8] + __ movl (Address(rsp, 2*wordSize), exception); + } + + Register obj_reg = obj_opr->as_register(); + Register lock_reg = lock_opr->as_register(); + + // setup registers (lock_reg must be rax, for lock_object) + assert(obj_reg != SYNC_header && lock_reg != SYNC_header, "rax, must be available here"); + Register hdr = lock_reg; + assert(new_hdr == SYNC_header, "wrong register"); + lock_reg = new_hdr; + // compute pointer to BasicLock + Address lock_addr = frame_map()->address_for_monitor_lock(monitor_no); + __ leal(lock_reg, lock_addr); + // unlock object + MonitorAccessStub* slow_case = new MonitorExitStub(lock_opr, true, monitor_no); + // _slow_case_stubs->append(slow_case); + // temporary fix: must be created after exceptionhandler, therefore as call stub + _slow_case_stubs->append(slow_case); + if (UseFastLocking) { + // try inlined fast unlocking first, revert to slow locking if it fails + // note: lock_reg points to the displaced header since the displaced header offset is 0! + assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); + __ unlock_object(hdr, obj_reg, lock_reg, *slow_case->entry()); + } else { + // always do slow unlocking + // note: the slow unlocking code could be inlined here, however if we use + // slow unlocking, speed doesn't matter anyway and this solution is + // simpler and requires less duplicated code - additionally, the + // slow unlocking code is the same in either case which simplifies + // debugging + __ jmp(*slow_case->entry()); + } + // done + __ bind(*slow_case->continuation()); + + if (exception->is_valid()) { + // restore exception + __ movl (exception, Address(rsp, 2 * wordSize)); + } +} + +// This specifies the rsp decrement needed to build the frame +int LIR_Assembler::initial_frame_size_in_bytes() { + // if rounding, must let FrameMap know! + return (frame_map()->framesize() - 2) * BytesPerWord; // subtract two words to account for return address and link +} + + +void LIR_Assembler::emit_exception_handler() { + // if the last instruction is a call (typically to do a throw which + // is coming at the end after block reordering) the return address + // must still point into the code area in order to avoid assertion + // failures when searching for the corresponding bci => add a nop + // (was bug 5/14/1999 - gri) + + __ nop(); + + // generate code for exception handler + address handler_base = __ start_a_stub(exception_handler_size); + if (handler_base == NULL) { + // not enough space left for the handler + bailout("exception handler overflow"); + return; + } +#ifdef ASSERT + int offset = code_offset(); +#endif // ASSERT + + compilation()->offsets()->set_value(CodeOffsets::Exceptions, code_offset()); + + // if the method does not have an exception handler, then there is + // no reason to search for one + if (compilation()->has_exception_handlers() || JvmtiExport::can_post_exceptions()) { + // the exception oop and pc are in rax, and rdx + // no other registers need to be preserved, so invalidate them + __ invalidate_registers(false, true, true, false, true, true); + + // check that there is really an exception + __ verify_not_null_oop(rax); + + // search an exception handler (rax: exception oop, rdx: throwing pc) + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::handle_exception_nofpu_id))); + + // if the call returns here, then the exception handler for particular + // exception doesn't exist -> unwind activation and forward exception to caller + } + + // the exception oop is in rax, + // no other registers need to be preserved, so invalidate them + __ invalidate_registers(false, true, true, true, true, true); + + // check that there is really an exception + __ verify_not_null_oop(rax); + + // unlock the receiver/klass if necessary + // rax,: exception + ciMethod* method = compilation()->method(); + if (method->is_synchronized() && GenerateSynchronizationCode) { + monitorexit(FrameMap::rbx_oop_opr, FrameMap::rcx_opr, SYNC_header, 0, rax); + } + + // unwind activation and forward exception to caller + // rax,: exception + __ jump(RuntimeAddress(Runtime1::entry_for(Runtime1::unwind_exception_id))); + + assert(code_offset() - offset <= exception_handler_size, "overflow"); + + __ end_a_stub(); +} + +void LIR_Assembler::emit_deopt_handler() { + // if the last instruction is a call (typically to do a throw which + // is coming at the end after block reordering) the return address + // must still point into the code area in order to avoid assertion + // failures when searching for the corresponding bci => add a nop + // (was bug 5/14/1999 - gri) + + __ nop(); + + // generate code for exception handler + address handler_base = __ start_a_stub(deopt_handler_size); + if (handler_base == NULL) { + // not enough space left for the handler + bailout("deopt handler overflow"); + return; + } +#ifdef ASSERT + int offset = code_offset(); +#endif // ASSERT + + compilation()->offsets()->set_value(CodeOffsets::Deopt, code_offset()); + + InternalAddress here(__ pc()); + __ pushptr(here.addr()); + + __ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + + assert(code_offset() - offset <= deopt_handler_size, "overflow"); + + __ end_a_stub(); + +} + + +// This is the fast version of java.lang.String.compare; it has not +// OSR-entry and therefore, we generate a slow version for OSR's +void LIR_Assembler::emit_string_compare(LIR_Opr arg0, LIR_Opr arg1, LIR_Opr dst, CodeEmitInfo* info) { + __ movl (rbx, rcx); // receiver is in rcx + __ movl (rax, arg1->as_register()); + + // Get addresses of first characters from both Strings + __ movl (rsi, Address(rax, java_lang_String::value_offset_in_bytes())); + __ movl (rcx, Address(rax, java_lang_String::offset_offset_in_bytes())); + __ leal (rsi, Address(rsi, rcx, Address::times_2, arrayOopDesc::base_offset_in_bytes(T_CHAR))); + + + // rbx, may be NULL + add_debug_info_for_null_check_here(info); + __ movl (rdi, Address(rbx, java_lang_String::value_offset_in_bytes())); + __ movl (rcx, Address(rbx, java_lang_String::offset_offset_in_bytes())); + __ leal (rdi, Address(rdi, rcx, Address::times_2, arrayOopDesc::base_offset_in_bytes(T_CHAR))); + + // compute minimum length (in rax) and difference of lengths (on top of stack) + if (VM_Version::supports_cmov()) { + __ movl (rbx, Address(rbx, java_lang_String::count_offset_in_bytes())); + __ movl (rax, Address(rax, java_lang_String::count_offset_in_bytes())); + __ movl (rcx, rbx); + __ subl (rbx, rax); // subtract lengths + __ pushl(rbx); // result + __ cmovl(Assembler::lessEqual, rax, rcx); + } else { + Label L; + __ movl (rbx, Address(rbx, java_lang_String::count_offset_in_bytes())); + __ movl (rcx, Address(rax, java_lang_String::count_offset_in_bytes())); + __ movl (rax, rbx); + __ subl (rbx, rcx); + __ pushl(rbx); + __ jcc (Assembler::lessEqual, L); + __ movl (rax, rcx); + __ bind (L); + } + // is minimum length 0? + Label noLoop, haveResult; + __ testl (rax, rax); + __ jcc (Assembler::zero, noLoop); + + // compare first characters + __ load_unsigned_word(rcx, Address(rdi, 0)); + __ load_unsigned_word(rbx, Address(rsi, 0)); + __ subl(rcx, rbx); + __ jcc(Assembler::notZero, haveResult); + // starting loop + __ decrement(rax); // we already tested index: skip one + __ jcc(Assembler::zero, noLoop); + + // set rsi.edi to the end of the arrays (arrays have same length) + // negate the index + + __ leal(rsi, Address(rsi, rax, Address::times_2, type2aelembytes[T_CHAR])); + __ leal(rdi, Address(rdi, rax, Address::times_2, type2aelembytes[T_CHAR])); + __ negl(rax); + + // compare the strings in a loop + + Label loop; + __ align(wordSize); + __ bind(loop); + __ load_unsigned_word(rcx, Address(rdi, rax, Address::times_2, 0)); + __ load_unsigned_word(rbx, Address(rsi, rax, Address::times_2, 0)); + __ subl(rcx, rbx); + __ jcc(Assembler::notZero, haveResult); + __ increment(rax); + __ jcc(Assembler::notZero, loop); + + // strings are equal up to min length + + __ bind(noLoop); + __ popl(rax); + return_op(LIR_OprFact::illegalOpr); + + __ bind(haveResult); + // leave instruction is going to discard the TOS value + __ movl (rax, rcx); // result of call is in rax, +} + + +void LIR_Assembler::return_op(LIR_Opr result) { + assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == rax, "word returns are in rax,"); + if (!result->is_illegal() && result->is_float_kind() && !result->is_xmm_register()) { + assert(result->fpu() == 0, "result must already be on TOS"); + } + + // Pop the stack before the safepoint code + __ leave(); + + bool result_is_oop = result->is_valid() ? result->is_oop() : false; + + // Note: we do not need to round double result; float result has the right precision + // the poll sets the condition code, but no data registers + AddressLiteral polling_page(os::get_polling_page() + (SafepointPollOffset % os::vm_page_size()), + relocInfo::poll_return_type); + __ test32(rax, polling_page); + + __ ret(0); +} + + +int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) { + AddressLiteral polling_page(os::get_polling_page() + (SafepointPollOffset % os::vm_page_size()), + relocInfo::poll_type); + + if (info != NULL) { + add_debug_info_for_branch(info); + } else { + ShouldNotReachHere(); + } + + int offset = __ offset(); + __ test32(rax, polling_page); + return offset; +} + + +void LIR_Assembler::move_regs(Register from_reg, Register to_reg) { + if (from_reg != to_reg) __ movl(to_reg, from_reg); +} + +void LIR_Assembler::swap_reg(Register a, Register b) { + __ xchgl(a, b); +} + + +void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_code, CodeEmitInfo* info) { + assert(src->is_constant(), "should not call otherwise"); + assert(dest->is_register(), "should not call otherwise"); + LIR_Const* c = src->as_constant_ptr(); + + switch (c->type()) { + case T_INT: { + assert(patch_code == lir_patch_none, "no patching handled here"); + __ movl(dest->as_register(), c->as_jint()); + break; + } + + case T_LONG: { + assert(patch_code == lir_patch_none, "no patching handled here"); + __ movl(dest->as_register_lo(), c->as_jint_lo()); + __ movl(dest->as_register_hi(), c->as_jint_hi()); + break; + } + + case T_OBJECT: { + if (patch_code != lir_patch_none) { + jobject2reg_with_patching(dest->as_register(), info); + } else { + __ movoop(dest->as_register(), c->as_jobject()); + } + break; + } + + case T_FLOAT: { + if (dest->is_single_xmm()) { + if (c->is_zero_float()) { + __ xorps(dest->as_xmm_float_reg(), dest->as_xmm_float_reg()); + } else { + __ movflt(dest->as_xmm_float_reg(), + InternalAddress(float_constant(c->as_jfloat()))); + } + } else { + assert(dest->is_single_fpu(), "must be"); + assert(dest->fpu_regnr() == 0, "dest must be TOS"); + if (c->is_zero_float()) { + __ fldz(); + } else if (c->is_one_float()) { + __ fld1(); + } else { + __ fld_s (InternalAddress(float_constant(c->as_jfloat()))); + } + } + break; + } + + case T_DOUBLE: { + if (dest->is_double_xmm()) { + if (c->is_zero_double()) { + __ xorpd(dest->as_xmm_double_reg(), dest->as_xmm_double_reg()); + } else { + __ movdbl(dest->as_xmm_double_reg(), + InternalAddress(double_constant(c->as_jdouble()))); + } + } else { + assert(dest->is_double_fpu(), "must be"); + assert(dest->fpu_regnrLo() == 0, "dest must be TOS"); + if (c->is_zero_double()) { + __ fldz(); + } else if (c->is_one_double()) { + __ fld1(); + } else { + __ fld_d (InternalAddress(double_constant(c->as_jdouble()))); + } + } + break; + } + + default: + ShouldNotReachHere(); + } +} + +void LIR_Assembler::const2stack(LIR_Opr src, LIR_Opr dest) { + assert(src->is_constant(), "should not call otherwise"); + assert(dest->is_stack(), "should not call otherwise"); + LIR_Const* c = src->as_constant_ptr(); + + switch (c->type()) { + case T_INT: // fall through + case T_FLOAT: + __ movl(frame_map()->address_for_slot(dest->single_stack_ix()), c->as_jint_bits()); + break; + + case T_OBJECT: + __ movoop(frame_map()->address_for_slot(dest->single_stack_ix()), c->as_jobject()); + break; + + case T_LONG: // fall through + case T_DOUBLE: + __ movl(frame_map()->address_for_slot(dest->double_stack_ix(), + lo_word_offset_in_bytes), c->as_jint_lo_bits()); + __ movl(frame_map()->address_for_slot(dest->double_stack_ix(), + hi_word_offset_in_bytes), c->as_jint_hi_bits()); + break; + + default: + ShouldNotReachHere(); + } +} + +void LIR_Assembler::const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info ) { + assert(src->is_constant(), "should not call otherwise"); + assert(dest->is_address(), "should not call otherwise"); + LIR_Const* c = src->as_constant_ptr(); + LIR_Address* addr = dest->as_address_ptr(); + + if (info != NULL) add_debug_info_for_null_check_here(info); + switch (type) { + case T_INT: // fall through + case T_FLOAT: + __ movl(as_Address(addr), c->as_jint_bits()); + break; + + case T_OBJECT: // fall through + case T_ARRAY: + if (c->as_jobject() == NULL) { + __ movl(as_Address(addr), NULL_WORD); + } else { + __ movoop(as_Address(addr), c->as_jobject()); + } + break; + + case T_LONG: // fall through + case T_DOUBLE: + __ movl(as_Address_hi(addr), c->as_jint_hi_bits()); + __ movl(as_Address_lo(addr), c->as_jint_lo_bits()); + break; + + case T_BOOLEAN: // fall through + case T_BYTE: + __ movb(as_Address(addr), c->as_jint() & 0xFF); + break; + + case T_CHAR: // fall through + case T_SHORT: + __ movw(as_Address(addr), c->as_jint() & 0xFFFF); + break; + + default: + ShouldNotReachHere(); + }; +} + + +void LIR_Assembler::reg2reg(LIR_Opr src, LIR_Opr dest) { + assert(src->is_register(), "should not call otherwise"); + assert(dest->is_register(), "should not call otherwise"); + + // move between cpu-registers + if (dest->is_single_cpu()) { + assert(src->is_single_cpu(), "must match"); + if (src->type() == T_OBJECT) { + __ verify_oop(src->as_register()); + } + move_regs(src->as_register(), dest->as_register()); + + } else if (dest->is_double_cpu()) { + assert(src->is_double_cpu(), "must match"); + Register f_lo = src->as_register_lo(); + Register f_hi = src->as_register_hi(); + Register t_lo = dest->as_register_lo(); + Register t_hi = dest->as_register_hi(); + assert(f_lo != f_hi && t_lo != t_hi, "invalid register allocation"); + + if (f_lo == t_hi && f_hi == t_lo) { + swap_reg(f_lo, f_hi); + } else if (f_hi == t_lo) { + assert(f_lo != t_hi, "overwriting register"); + move_regs(f_hi, t_hi); + move_regs(f_lo, t_lo); + } else { + assert(f_hi != t_lo, "overwriting register"); + move_regs(f_lo, t_lo); + move_regs(f_hi, t_hi); + } + + // special moves from fpu-register to xmm-register + // necessary for method results + } else if (src->is_single_xmm() && !dest->is_single_xmm()) { + __ movflt(Address(rsp, 0), src->as_xmm_float_reg()); + __ fld_s(Address(rsp, 0)); + } else if (src->is_double_xmm() && !dest->is_double_xmm()) { + __ movdbl(Address(rsp, 0), src->as_xmm_double_reg()); + __ fld_d(Address(rsp, 0)); + } else if (dest->is_single_xmm() && !src->is_single_xmm()) { + __ fstp_s(Address(rsp, 0)); + __ movflt(dest->as_xmm_float_reg(), Address(rsp, 0)); + } else if (dest->is_double_xmm() && !src->is_double_xmm()) { + __ fstp_d(Address(rsp, 0)); + __ movdbl(dest->as_xmm_double_reg(), Address(rsp, 0)); + + // move between xmm-registers + } else if (dest->is_single_xmm()) { + assert(src->is_single_xmm(), "must match"); + __ movflt(dest->as_xmm_float_reg(), src->as_xmm_float_reg()); + } else if (dest->is_double_xmm()) { + assert(src->is_double_xmm(), "must match"); + __ movdbl(dest->as_xmm_double_reg(), src->as_xmm_double_reg()); + + // move between fpu-registers (no instruction necessary because of fpu-stack) + } else if (dest->is_single_fpu() || dest->is_double_fpu()) { + assert(src->is_single_fpu() || src->is_double_fpu(), "must match"); + assert(src->fpu() == dest->fpu(), "currently should be nothing to do"); + } else { + ShouldNotReachHere(); + } +} + +void LIR_Assembler::reg2stack(LIR_Opr src, LIR_Opr dest, BasicType type, bool pop_fpu_stack) { + assert(src->is_register(), "should not call otherwise"); + assert(dest->is_stack(), "should not call otherwise"); + + if (src->is_single_cpu()) { + Address dst = frame_map()->address_for_slot(dest->single_stack_ix()); + if (type == T_OBJECT || type == T_ARRAY) { + __ verify_oop(src->as_register()); + } + __ movl (dst, src->as_register()); + + } else if (src->is_double_cpu()) { + Address dstLO = frame_map()->address_for_slot(dest->double_stack_ix(), lo_word_offset_in_bytes); + Address dstHI = frame_map()->address_for_slot(dest->double_stack_ix(), hi_word_offset_in_bytes); + __ movl (dstLO, src->as_register_lo()); + __ movl (dstHI, src->as_register_hi()); + + } else if (src->is_single_xmm()) { + Address dst_addr = frame_map()->address_for_slot(dest->single_stack_ix()); + __ movflt(dst_addr, src->as_xmm_float_reg()); + + } else if (src->is_double_xmm()) { + Address dst_addr = frame_map()->address_for_slot(dest->double_stack_ix()); + __ movdbl(dst_addr, src->as_xmm_double_reg()); + + } else if (src->is_single_fpu()) { + assert(src->fpu_regnr() == 0, "argument must be on TOS"); + Address dst_addr = frame_map()->address_for_slot(dest->single_stack_ix()); + if (pop_fpu_stack) __ fstp_s (dst_addr); + else __ fst_s (dst_addr); + + } else if (src->is_double_fpu()) { + assert(src->fpu_regnrLo() == 0, "argument must be on TOS"); + Address dst_addr = frame_map()->address_for_slot(dest->double_stack_ix()); + if (pop_fpu_stack) __ fstp_d (dst_addr); + else __ fst_d (dst_addr); + + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::reg2mem(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool pop_fpu_stack, bool /* unaligned */) { + LIR_Address* to_addr = dest->as_address_ptr(); + PatchingStub* patch = NULL; + + if (type == T_ARRAY || type == T_OBJECT) { + __ verify_oop(src->as_register()); + } + if (patch_code != lir_patch_none) { + patch = new PatchingStub(_masm, PatchingStub::access_field_id); + } + if (info != NULL) { + add_debug_info_for_null_check_here(info); + } + + switch (type) { + case T_FLOAT: { + if (src->is_single_xmm()) { + __ movflt(as_Address(to_addr), src->as_xmm_float_reg()); + } else { + assert(src->is_single_fpu(), "must be"); + assert(src->fpu_regnr() == 0, "argument must be on TOS"); + if (pop_fpu_stack) __ fstp_s(as_Address(to_addr)); + else __ fst_s (as_Address(to_addr)); + } + break; + } + + case T_DOUBLE: { + if (src->is_double_xmm()) { + __ movdbl(as_Address(to_addr), src->as_xmm_double_reg()); + } else { + assert(src->is_double_fpu(), "must be"); + assert(src->fpu_regnrLo() == 0, "argument must be on TOS"); + if (pop_fpu_stack) __ fstp_d(as_Address(to_addr)); + else __ fst_d (as_Address(to_addr)); + } + break; + } + + case T_ADDRESS: // fall through + case T_ARRAY: // fall through + case T_OBJECT: // fall through + case T_INT: + __ movl(as_Address(to_addr), src->as_register()); + break; + + case T_LONG: { + Register from_lo = src->as_register_lo(); + Register from_hi = src->as_register_hi(); + Register base = to_addr->base()->as_register(); + Register index = noreg; + if (to_addr->index()->is_register()) { + index = to_addr->index()->as_register(); + } + if (base == from_lo || index == from_lo) { + assert(base != from_hi, "can't be"); + assert(index == noreg || (index != base && index != from_hi), "can't handle this"); + __ movl(as_Address_hi(to_addr), from_hi); + if (patch != NULL) { + patching_epilog(patch, lir_patch_high, base, info); + patch = new PatchingStub(_masm, PatchingStub::access_field_id); + patch_code = lir_patch_low; + } + __ movl(as_Address_lo(to_addr), from_lo); + } else { + assert(index == noreg || (index != base && index != from_lo), "can't handle this"); + __ movl(as_Address_lo(to_addr), from_lo); + if (patch != NULL) { + patching_epilog(patch, lir_patch_low, base, info); + patch = new PatchingStub(_masm, PatchingStub::access_field_id); + patch_code = lir_patch_high; + } + __ movl(as_Address_hi(to_addr), from_hi); + } + break; + } + + case T_BYTE: // fall through + case T_BOOLEAN: { + Register src_reg = src->as_register(); + Address dst_addr = as_Address(to_addr); + assert(VM_Version::is_P6() || src_reg->has_byte_register(), "must use byte registers if not P6"); + __ movb(dst_addr, src_reg); + break; + } + + case T_CHAR: // fall through + case T_SHORT: + __ movw(as_Address(to_addr), src->as_register()); + break; + + default: + ShouldNotReachHere(); + } + + if (patch_code != lir_patch_none) { + patching_epilog(patch, patch_code, to_addr->base()->as_register(), info); + } +} + + +void LIR_Assembler::stack2reg(LIR_Opr src, LIR_Opr dest, BasicType type) { + assert(src->is_stack(), "should not call otherwise"); + assert(dest->is_register(), "should not call otherwise"); + + if (dest->is_single_cpu()) { + __ movl(dest->as_register(), frame_map()->address_for_slot(src->single_stack_ix())); + if (type == T_ARRAY || type == T_OBJECT) { + __ verify_oop(dest->as_register()); + } + + } else if (dest->is_double_cpu()) { + Address src_addr_LO = frame_map()->address_for_slot(src->double_stack_ix(), lo_word_offset_in_bytes); + Address src_addr_HI = frame_map()->address_for_slot(src->double_stack_ix(), hi_word_offset_in_bytes); + __ movl(dest->as_register_hi(), src_addr_HI); + __ movl(dest->as_register_lo(), src_addr_LO); + + } else if (dest->is_single_xmm()) { + Address src_addr = frame_map()->address_for_slot(src->single_stack_ix()); + __ movflt(dest->as_xmm_float_reg(), src_addr); + + } else if (dest->is_double_xmm()) { + Address src_addr = frame_map()->address_for_slot(src->double_stack_ix()); + __ movdbl(dest->as_xmm_double_reg(), src_addr); + + } else if (dest->is_single_fpu()) { + assert(dest->fpu_regnr() == 0, "dest must be TOS"); + Address src_addr = frame_map()->address_for_slot(src->single_stack_ix()); + __ fld_s(src_addr); + + } else if (dest->is_double_fpu()) { + assert(dest->fpu_regnrLo() == 0, "dest must be TOS"); + Address src_addr = frame_map()->address_for_slot(src->double_stack_ix()); + __ fld_d(src_addr); + + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::stack2stack(LIR_Opr src, LIR_Opr dest, BasicType type) { + if (src->is_single_stack()) { + __ pushl(frame_map()->address_for_slot(src ->single_stack_ix())); + __ popl (frame_map()->address_for_slot(dest->single_stack_ix())); + + } else if (src->is_double_stack()) { + __ pushl(frame_map()->address_for_slot(src ->double_stack_ix(), 0)); + // push and pop the part at src + 4, adding 4 for the previous push + __ pushl(frame_map()->address_for_slot(src ->double_stack_ix(), 4 + 4)); + __ popl (frame_map()->address_for_slot(dest->double_stack_ix(), 4 + 4)); + __ popl (frame_map()->address_for_slot(dest->double_stack_ix(), 0)); + + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool /* unaligned */) { + assert(src->is_address(), "should not call otherwise"); + assert(dest->is_register(), "should not call otherwise"); + + LIR_Address* addr = src->as_address_ptr(); + Address from_addr = as_Address(addr); + + switch (type) { + case T_BOOLEAN: // fall through + case T_BYTE: // fall through + case T_CHAR: // fall through + case T_SHORT: + if (!VM_Version::is_P6() && !from_addr.uses(dest->as_register())) { + // on pre P6 processors we may get partial register stalls + // so blow away the value of to_rinfo before loading a + // partial word into it. Do it here so that it precedes + // the potential patch point below. + __ xorl(dest->as_register(), dest->as_register()); + } + break; + } + + PatchingStub* patch = NULL; + if (patch_code != lir_patch_none) { + patch = new PatchingStub(_masm, PatchingStub::access_field_id); + } + if (info != NULL) { + add_debug_info_for_null_check_here(info); + } + + switch (type) { + case T_FLOAT: { + if (dest->is_single_xmm()) { + __ movflt(dest->as_xmm_float_reg(), from_addr); + } else { + assert(dest->is_single_fpu(), "must be"); + assert(dest->fpu_regnr() == 0, "dest must be TOS"); + __ fld_s(from_addr); + } + break; + } + + case T_DOUBLE: { + if (dest->is_double_xmm()) { + __ movdbl(dest->as_xmm_double_reg(), from_addr); + } else { + assert(dest->is_double_fpu(), "must be"); + assert(dest->fpu_regnrLo() == 0, "dest must be TOS"); + __ fld_d(from_addr); + } + break; + } + + case T_ADDRESS: // fall through + case T_OBJECT: // fall through + case T_ARRAY: // fall through + case T_INT: + __ movl(dest->as_register(), from_addr); + break; + + case T_LONG: { + Register to_lo = dest->as_register_lo(); + Register to_hi = dest->as_register_hi(); + Register base = addr->base()->as_register(); + Register index = noreg; + if (addr->index()->is_register()) { + index = addr->index()->as_register(); + } + if ((base == to_lo && index == to_hi) || + (base == to_hi && index == to_lo)) { + // addresses with 2 registers are only formed as a result of + // array access so this code will never have to deal with + // patches or null checks. + assert(info == NULL && patch == NULL, "must be"); + __ leal(to_hi, as_Address(addr)); + __ movl(to_lo, Address(to_hi, 0)); + __ movl(to_hi, Address(to_hi, BytesPerWord)); + } else if (base == to_lo || index == to_lo) { + assert(base != to_hi, "can't be"); + assert(index == noreg || (index != base && index != to_hi), "can't handle this"); + __ movl(to_hi, as_Address_hi(addr)); + if (patch != NULL) { + patching_epilog(patch, lir_patch_high, base, info); + patch = new PatchingStub(_masm, PatchingStub::access_field_id); + patch_code = lir_patch_low; + } + __ movl(to_lo, as_Address_lo(addr)); + } else { + assert(index == noreg || (index != base && index != to_lo), "can't handle this"); + __ movl(to_lo, as_Address_lo(addr)); + if (patch != NULL) { + patching_epilog(patch, lir_patch_low, base, info); + patch = new PatchingStub(_masm, PatchingStub::access_field_id); + patch_code = lir_patch_high; + } + __ movl(to_hi, as_Address_hi(addr)); + } + break; + } + + case T_BOOLEAN: // fall through + case T_BYTE: { + Register dest_reg = dest->as_register(); + assert(VM_Version::is_P6() || dest_reg->has_byte_register(), "must use byte registers if not P6"); + if (VM_Version::is_P6() || from_addr.uses(dest_reg)) { + __ movsxb(dest_reg, from_addr); + } else { + __ movb(dest_reg, from_addr); + __ shll(dest_reg, 24); + __ sarl(dest_reg, 24); + } + break; + } + + case T_CHAR: { + Register dest_reg = dest->as_register(); + assert(VM_Version::is_P6() || dest_reg->has_byte_register(), "must use byte registers if not P6"); + if (VM_Version::is_P6() || from_addr.uses(dest_reg)) { + __ movzxw(dest_reg, from_addr); + } else { + __ movw(dest_reg, from_addr); + } + break; + } + + case T_SHORT: { + Register dest_reg = dest->as_register(); + if (VM_Version::is_P6() || from_addr.uses(dest_reg)) { + __ movsxw(dest_reg, from_addr); + } else { + __ movw(dest_reg, from_addr); + __ shll(dest_reg, 16); + __ sarl(dest_reg, 16); + } + break; + } + + default: + ShouldNotReachHere(); + } + + if (patch != NULL) { + patching_epilog(patch, patch_code, addr->base()->as_register(), info); + } + + if (type == T_ARRAY || type == T_OBJECT) { + __ verify_oop(dest->as_register()); + } +} + + +void LIR_Assembler::prefetchr(LIR_Opr src) { + LIR_Address* addr = src->as_address_ptr(); + Address from_addr = as_Address(addr); + + if (VM_Version::supports_sse()) { + switch (ReadPrefetchInstr) { + case 0: + __ prefetchnta(from_addr); break; + case 1: + __ prefetcht0(from_addr); break; + case 2: + __ prefetcht2(from_addr); break; + default: + ShouldNotReachHere(); break; + } + } else if (VM_Version::supports_3dnow()) { + __ prefetchr(from_addr); + } +} + + +void LIR_Assembler::prefetchw(LIR_Opr src) { + LIR_Address* addr = src->as_address_ptr(); + Address from_addr = as_Address(addr); + + if (VM_Version::supports_sse()) { + switch (AllocatePrefetchInstr) { + case 0: + __ prefetchnta(from_addr); break; + case 1: + __ prefetcht0(from_addr); break; + case 2: + __ prefetcht2(from_addr); break; + case 3: + __ prefetchw(from_addr); break; + default: + ShouldNotReachHere(); break; + } + } else if (VM_Version::supports_3dnow()) { + __ prefetchw(from_addr); + } +} + + +NEEDS_CLEANUP; // This could be static? +Address::ScaleFactor LIR_Assembler::array_element_size(BasicType type) const { + int elem_size = type2aelembytes[type]; + switch (elem_size) { + case 1: return Address::times_1; + case 2: return Address::times_2; + case 4: return Address::times_4; + case 8: return Address::times_8; + } + ShouldNotReachHere(); + return Address::no_scale; +} + + +void LIR_Assembler::emit_op3(LIR_Op3* op) { + switch (op->code()) { + case lir_idiv: + case lir_irem: + arithmetic_idiv(op->code(), + op->in_opr1(), + op->in_opr2(), + op->in_opr3(), + op->result_opr(), + op->info()); + break; + default: ShouldNotReachHere(); break; + } +} + +void LIR_Assembler::emit_opBranch(LIR_OpBranch* op) { +#ifdef ASSERT + assert(op->block() == NULL || op->block()->label() == op->label(), "wrong label"); + if (op->block() != NULL) _branch_target_blocks.append(op->block()); + if (op->ublock() != NULL) _branch_target_blocks.append(op->ublock()); +#endif + + if (op->cond() == lir_cond_always) { + if (op->info() != NULL) add_debug_info_for_branch(op->info()); + __ jmp (*(op->label())); + } else { + Assembler::Condition acond = Assembler::zero; + if (op->code() == lir_cond_float_branch) { + assert(op->ublock() != NULL, "must have unordered successor"); + __ jcc(Assembler::parity, *(op->ublock()->label())); + switch(op->cond()) { + case lir_cond_equal: acond = Assembler::equal; break; + case lir_cond_notEqual: acond = Assembler::notEqual; break; + case lir_cond_less: acond = Assembler::below; break; + case lir_cond_lessEqual: acond = Assembler::belowEqual; break; + case lir_cond_greaterEqual: acond = Assembler::aboveEqual; break; + case lir_cond_greater: acond = Assembler::above; break; + default: ShouldNotReachHere(); + } + } else { + switch (op->cond()) { + case lir_cond_equal: acond = Assembler::equal; break; + case lir_cond_notEqual: acond = Assembler::notEqual; break; + case lir_cond_less: acond = Assembler::less; break; + case lir_cond_lessEqual: acond = Assembler::lessEqual; break; + case lir_cond_greaterEqual: acond = Assembler::greaterEqual;break; + case lir_cond_greater: acond = Assembler::greater; break; + case lir_cond_belowEqual: acond = Assembler::belowEqual; break; + case lir_cond_aboveEqual: acond = Assembler::aboveEqual; break; + default: ShouldNotReachHere(); + } + } + __ jcc(acond,*(op->label())); + } +} + +void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { + LIR_Opr src = op->in_opr(); + LIR_Opr dest = op->result_opr(); + + switch (op->bytecode()) { + case Bytecodes::_i2l: + move_regs(src->as_register(), dest->as_register_lo()); + move_regs(src->as_register(), dest->as_register_hi()); + __ sarl(dest->as_register_hi(), 31); + break; + + case Bytecodes::_l2i: + move_regs(src->as_register_lo(), dest->as_register()); + break; + + case Bytecodes::_i2b: + move_regs(src->as_register(), dest->as_register()); + __ sign_extend_byte(dest->as_register()); + break; + + case Bytecodes::_i2c: + move_regs(src->as_register(), dest->as_register()); + __ andl(dest->as_register(), 0xFFFF); + break; + + case Bytecodes::_i2s: + move_regs(src->as_register(), dest->as_register()); + __ sign_extend_short(dest->as_register()); + break; + + + case Bytecodes::_f2d: + case Bytecodes::_d2f: + if (dest->is_single_xmm()) { + __ cvtsd2ss(dest->as_xmm_float_reg(), src->as_xmm_double_reg()); + } else if (dest->is_double_xmm()) { + __ cvtss2sd(dest->as_xmm_double_reg(), src->as_xmm_float_reg()); + } else { + assert(src->fpu() == dest->fpu(), "register must be equal"); + // do nothing (float result is rounded later through spilling) + } + break; + + case Bytecodes::_i2f: + case Bytecodes::_i2d: + if (dest->is_single_xmm()) { + __ cvtsi2ss(dest->as_xmm_float_reg(), src->as_register()); + } else if (dest->is_double_xmm()) { + __ cvtsi2sd(dest->as_xmm_double_reg(), src->as_register()); + } else { + assert(dest->fpu() == 0, "result must be on TOS"); + __ movl(Address(rsp, 0), src->as_register()); + __ fild_s(Address(rsp, 0)); + } + break; + + case Bytecodes::_f2i: + case Bytecodes::_d2i: + if (src->is_single_xmm()) { + __ cvttss2si(dest->as_register(), src->as_xmm_float_reg()); + } else if (src->is_double_xmm()) { + __ cvttsd2si(dest->as_register(), src->as_xmm_double_reg()); + } else { + assert(src->fpu() == 0, "input must be on TOS"); + __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_trunc())); + __ fist_s(Address(rsp, 0)); + __ movl(dest->as_register(), Address(rsp, 0)); + __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); + } + + // IA32 conversion instructions do not match JLS for overflow, underflow and NaN -> fixup in stub + assert(op->stub() != NULL, "stub required"); + __ cmpl(dest->as_register(), 0x80000000); + __ jcc(Assembler::equal, *op->stub()->entry()); + __ bind(*op->stub()->continuation()); + break; + + case Bytecodes::_l2f: + case Bytecodes::_l2d: + assert(!dest->is_xmm_register(), "result in xmm register not supported (no SSE instruction present)"); + assert(dest->fpu() == 0, "result must be on TOS"); + + __ movl(Address(rsp, 0), src->as_register_lo()); + __ movl(Address(rsp, BytesPerWord), src->as_register_hi()); + __ fild_d(Address(rsp, 0)); + // float result is rounded later through spilling + break; + + case Bytecodes::_f2l: + case Bytecodes::_d2l: + assert(!src->is_xmm_register(), "input in xmm register not supported (no SSE instruction present)"); + assert(src->fpu() == 0, "input must be on TOS"); + assert(dest == FrameMap::rax_rdx_long_opr, "runtime stub places result in these registers"); + + // instruction sequence too long to inline it here + { + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::fpu2long_stub_id))); + } + break; + + default: ShouldNotReachHere(); + } +} + +void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { + if (op->init_check()) { + __ cmpl(Address(op->klass()->as_register(), + instanceKlass::init_state_offset_in_bytes() + sizeof(oopDesc)), + instanceKlass::fully_initialized); + add_debug_info_for_null_check_here(op->stub()->info()); + __ jcc(Assembler::notEqual, *op->stub()->entry()); + } + __ allocate_object(op->obj()->as_register(), + op->tmp1()->as_register(), + op->tmp2()->as_register(), + op->header_size(), + op->object_size(), + op->klass()->as_register(), + *op->stub()->entry()); + __ bind(*op->stub()->continuation()); +} + +void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) { + if (UseSlowPath || + (!UseFastNewObjectArray && (op->type() == T_OBJECT || op->type() == T_ARRAY)) || + (!UseFastNewTypeArray && (op->type() != T_OBJECT && op->type() != T_ARRAY))) { + __ jmp(*op->stub()->entry()); + } else { + Register len = op->len()->as_register(); + Register tmp1 = op->tmp1()->as_register(); + Register tmp2 = op->tmp2()->as_register(); + Register tmp3 = op->tmp3()->as_register(); + if (len == tmp1) { + tmp1 = tmp3; + } else if (len == tmp2) { + tmp2 = tmp3; + } else if (len == tmp3) { + // everything is ok + } else { + __ movl(tmp3, len); + } + __ allocate_array(op->obj()->as_register(), + len, + tmp1, + tmp2, + arrayOopDesc::header_size(op->type()), + array_element_size(op->type()), + op->klass()->as_register(), + *op->stub()->entry()); + } + __ bind(*op->stub()->continuation()); +} + + + +void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { + LIR_Code code = op->code(); + if (code == lir_store_check) { + Register value = op->object()->as_register(); + Register array = op->array()->as_register(); + Register k_RInfo = op->tmp1()->as_register(); + Register klass_RInfo = op->tmp2()->as_register(); + Register Rtmp1 = op->tmp3()->as_register(); + + CodeStub* stub = op->stub(); + Label done; + __ cmpl(value, 0); + __ jcc(Assembler::equal, done); + add_debug_info_for_null_check_here(op->info_for_exception()); + __ movl(k_RInfo, Address(array, oopDesc::klass_offset_in_bytes())); + __ movl(klass_RInfo, Address(value, oopDesc::klass_offset_in_bytes())); + + // get instance klass + __ movl(k_RInfo, Address(k_RInfo, objArrayKlass::element_klass_offset_in_bytes() + sizeof(oopDesc))); + // get super_check_offset + __ movl(Rtmp1, Address(k_RInfo, sizeof(oopDesc) + Klass::super_check_offset_offset_in_bytes())); + // See if we get an immediate positive hit + __ cmpl(k_RInfo, Address(klass_RInfo, Rtmp1, Address::times_1)); + __ jcc(Assembler::equal, done); + // check for immediate negative hit + __ cmpl(Rtmp1, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes()); + __ jcc(Assembler::notEqual, *stub->entry()); + // check for self + __ cmpl(klass_RInfo, k_RInfo); + __ jcc(Assembler::equal, done); + + __ pushl(klass_RInfo); + __ pushl(k_RInfo); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id))); + __ popl(klass_RInfo); + __ popl(k_RInfo); + __ cmpl(k_RInfo, 0); + __ jcc(Assembler::equal, *stub->entry()); + __ bind(done); + } else if (op->code() == lir_checkcast) { + // we always need a stub for the failure case. + CodeStub* stub = op->stub(); + Register obj = op->object()->as_register(); + Register k_RInfo = op->tmp1()->as_register(); + Register klass_RInfo = op->tmp2()->as_register(); + Register dst = op->result_opr()->as_register(); + ciKlass* k = op->klass(); + Register Rtmp1 = noreg; + + Label done; + if (obj == k_RInfo) { + k_RInfo = dst; + } else if (obj == klass_RInfo) { + klass_RInfo = dst; + } + if (k->is_loaded()) { + select_different_registers(obj, dst, k_RInfo, klass_RInfo); + } else { + Rtmp1 = op->tmp3()->as_register(); + select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); + } + + assert_different_registers(obj, k_RInfo, klass_RInfo); + if (!k->is_loaded()) { + jobject2reg_with_patching(k_RInfo, op->info_for_patch()); + } else { + k_RInfo = noreg; + } + assert(obj != k_RInfo, "must be different"); + __ cmpl(obj, 0); + if (op->profiled_method() != NULL) { + ciMethod* method = op->profiled_method(); + int bci = op->profiled_bci(); + + Label profile_done; + __ jcc(Assembler::notEqual, profile_done); + // Object is null; update methodDataOop + ciMethodData* md = method->method_data(); + if (md == NULL) { + bailout("out of memory building methodDataOop"); + return; + } + ciProfileData* data = md->bci_to_data(bci); + assert(data != NULL, "need data for checkcast"); + assert(data->is_BitData(), "need BitData for checkcast"); + Register mdo = klass_RInfo; + __ movoop(mdo, md->encoding()); + Address data_addr(mdo, md->byte_offset_of_slot(data, DataLayout::header_offset())); + int header_bits = DataLayout::flag_mask_to_header_mask(BitData::null_seen_byte_constant()); + __ orl(data_addr, header_bits); + __ jmp(done); + __ bind(profile_done); + } else { + __ jcc(Assembler::equal, done); + } + __ verify_oop(obj); + + if (op->fast_check()) { + // get object classo + // not a safepoint as obj null check happens earlier + if (k->is_loaded()) { + __ cmpoop(Address(obj, oopDesc::klass_offset_in_bytes()), k->encoding()); + } else { + __ cmpl(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); + + } + __ jcc(Assembler::notEqual, *stub->entry()); + __ bind(done); + } else { + // get object class + // not a safepoint as obj null check happens earlier + __ movl(klass_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); + if (k->is_loaded()) { + // See if we get an immediate positive hit + __ cmpoop(Address(klass_RInfo, k->super_check_offset()), k->encoding()); + if (sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() != k->super_check_offset()) { + __ jcc(Assembler::notEqual, *stub->entry()); + } else { + // See if we get an immediate positive hit + __ jcc(Assembler::equal, done); + // check for self + __ cmpoop(klass_RInfo, k->encoding()); + __ jcc(Assembler::equal, done); + + __ pushl(klass_RInfo); + __ pushoop(k->encoding()); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id))); + __ popl(klass_RInfo); + __ popl(klass_RInfo); + __ cmpl(klass_RInfo, 0); + __ jcc(Assembler::equal, *stub->entry()); + } + __ bind(done); + } else { + __ movl(Rtmp1, Address(k_RInfo, sizeof(oopDesc) + Klass::super_check_offset_offset_in_bytes())); + // See if we get an immediate positive hit + __ cmpl(k_RInfo, Address(klass_RInfo, Rtmp1, Address::times_1)); + __ jcc(Assembler::equal, done); + // check for immediate negative hit + __ cmpl(Rtmp1, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes()); + __ jcc(Assembler::notEqual, *stub->entry()); + // check for self + __ cmpl(klass_RInfo, k_RInfo); + __ jcc(Assembler::equal, done); + + __ pushl(klass_RInfo); + __ pushl(k_RInfo); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id))); + __ popl(klass_RInfo); + __ popl(k_RInfo); + __ cmpl(k_RInfo, 0); + __ jcc(Assembler::equal, *stub->entry()); + __ bind(done); + } + + } + if (dst != obj) { + __ movl(dst, obj); + } + } else if (code == lir_instanceof) { + Register obj = op->object()->as_register(); + Register k_RInfo = op->tmp1()->as_register(); + Register klass_RInfo = op->tmp2()->as_register(); + Register dst = op->result_opr()->as_register(); + ciKlass* k = op->klass(); + + Label done; + Label zero; + Label one; + if (obj == k_RInfo) { + k_RInfo = klass_RInfo; + klass_RInfo = obj; + } + // patching may screw with our temporaries on sparc, + // so let's do it before loading the class + if (!k->is_loaded()) { + jobject2reg_with_patching(k_RInfo, op->info_for_patch()); + } + assert(obj != k_RInfo, "must be different"); + + __ verify_oop(obj); + if (op->fast_check()) { + __ cmpl(obj, 0); + __ jcc(Assembler::equal, zero); + // get object class + // not a safepoint as obj null check happens earlier + if (k->is_loaded()) { + __ cmpoop(Address(obj, oopDesc::klass_offset_in_bytes()), k->encoding()); + k_RInfo = noreg; + } else { + __ cmpl(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); + + } + __ jcc(Assembler::equal, one); + } else { + // get object class + // not a safepoint as obj null check happens earlier + __ cmpl(obj, 0); + __ jcc(Assembler::equal, zero); + __ movl(klass_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); + if (k->is_loaded()) { + // See if we get an immediate positive hit + __ cmpoop(Address(klass_RInfo, k->super_check_offset()), k->encoding()); + __ jcc(Assembler::equal, one); + if (sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() == k->super_check_offset()) { + // check for self + __ cmpoop(klass_RInfo, k->encoding()); + __ jcc(Assembler::equal, one); + __ pushl(klass_RInfo); + __ pushoop(k->encoding()); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id))); + __ popl(klass_RInfo); + __ popl(dst); + __ jmp(done); + } + } else { + assert(dst != klass_RInfo && dst != k_RInfo, "need 3 registers"); + + __ movl(dst, Address(k_RInfo, sizeof(oopDesc) + Klass::super_check_offset_offset_in_bytes())); + // See if we get an immediate positive hit + __ cmpl(k_RInfo, Address(klass_RInfo, dst, Address::times_1)); + __ jcc(Assembler::equal, one); + // check for immediate negative hit + __ cmpl(dst, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes()); + __ jcc(Assembler::notEqual, zero); + // check for self + __ cmpl(klass_RInfo, k_RInfo); + __ jcc(Assembler::equal, one); + + __ pushl(klass_RInfo); + __ pushl(k_RInfo); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id))); + __ popl(klass_RInfo); + __ popl(dst); + __ jmp(done); + } + } + __ bind(zero); + __ xorl(dst, dst); + __ jmp(done); + __ bind(one); + __ movl(dst, 1); + __ bind(done); + } else { + ShouldNotReachHere(); + } + +} + + +void LIR_Assembler::emit_compare_and_swap(LIR_OpCompareAndSwap* op) { + if (op->code() == lir_cas_long) { + assert(VM_Version::supports_cx8(), "wrong machine"); + assert(op->cmp_value()->as_register_lo() == rax, "wrong register"); + assert(op->cmp_value()->as_register_hi() == rdx, "wrong register"); + assert(op->new_value()->as_register_lo() == rbx, "wrong register"); + assert(op->new_value()->as_register_hi() == rcx, "wrong register"); + Register addr = op->addr()->as_register(); + if (os::is_MP()) { + __ lock(); + } + __ cmpxchg8(Address(addr, 0)); + + } else if (op->code() == lir_cas_int || op->code() == lir_cas_obj) { + Register addr = op->addr()->as_register(); + Register newval = op->new_value()->as_register(); + Register cmpval = op->cmp_value()->as_register(); + assert(cmpval == rax, "wrong register"); + assert(newval != NULL, "new val must be register"); + assert(cmpval != newval, "cmp and new values must be in different registers"); + assert(cmpval != addr, "cmp and addr must be in different registers"); + assert(newval != addr, "new value and addr must be in different registers"); + if (os::is_MP()) { + __ lock(); + } + __ cmpxchg(newval, Address(addr, 0)); + } else { + Unimplemented(); + } +} + + +void LIR_Assembler::cmove(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr result) { + Assembler::Condition acond, ncond; + switch (condition) { + case lir_cond_equal: acond = Assembler::equal; ncond = Assembler::notEqual; break; + case lir_cond_notEqual: acond = Assembler::notEqual; ncond = Assembler::equal; break; + case lir_cond_less: acond = Assembler::less; ncond = Assembler::greaterEqual; break; + case lir_cond_lessEqual: acond = Assembler::lessEqual; ncond = Assembler::greater; break; + case lir_cond_greaterEqual: acond = Assembler::greaterEqual; ncond = Assembler::less; break; + case lir_cond_greater: acond = Assembler::greater; ncond = Assembler::lessEqual; break; + case lir_cond_belowEqual: acond = Assembler::belowEqual; ncond = Assembler::above; break; + case lir_cond_aboveEqual: acond = Assembler::aboveEqual; ncond = Assembler::below; break; + default: ShouldNotReachHere(); + } + + if (opr1->is_cpu_register()) { + reg2reg(opr1, result); + } else if (opr1->is_stack()) { + stack2reg(opr1, result, result->type()); + } else if (opr1->is_constant()) { + const2reg(opr1, result, lir_patch_none, NULL); + } else { + ShouldNotReachHere(); + } + + if (VM_Version::supports_cmov() && !opr2->is_constant()) { + // optimized version that does not require a branch + if (opr2->is_single_cpu()) { + assert(opr2->cpu_regnr() != result->cpu_regnr(), "opr2 already overwritten by previous move"); + __ cmovl(ncond, result->as_register(), opr2->as_register()); + } else if (opr2->is_double_cpu()) { + assert(opr2->cpu_regnrLo() != result->cpu_regnrLo() && opr2->cpu_regnrLo() != result->cpu_regnrHi(), "opr2 already overwritten by previous move"); + assert(opr2->cpu_regnrHi() != result->cpu_regnrLo() && opr2->cpu_regnrHi() != result->cpu_regnrHi(), "opr2 already overwritten by previous move"); + __ cmovl(ncond, result->as_register_lo(), opr2->as_register_lo()); + __ cmovl(ncond, result->as_register_hi(), opr2->as_register_hi()); + } else if (opr2->is_single_stack()) { + __ cmovl(ncond, result->as_register(), frame_map()->address_for_slot(opr2->single_stack_ix())); + } else if (opr2->is_double_stack()) { + __ cmovl(ncond, result->as_register_lo(), frame_map()->address_for_slot(opr2->double_stack_ix(), lo_word_offset_in_bytes)); + __ cmovl(ncond, result->as_register_hi(), frame_map()->address_for_slot(opr2->double_stack_ix(), hi_word_offset_in_bytes)); + } else { + ShouldNotReachHere(); + } + + } else { + Label skip; + __ jcc (acond, skip); + if (opr2->is_cpu_register()) { + reg2reg(opr2, result); + } else if (opr2->is_stack()) { + stack2reg(opr2, result, result->type()); + } else if (opr2->is_constant()) { + const2reg(opr2, result, lir_patch_none, NULL); + } else { + ShouldNotReachHere(); + } + __ bind(skip); + } +} + + +void LIR_Assembler::arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest, CodeEmitInfo* info, bool pop_fpu_stack) { + assert(info == NULL, "should never be used, idiv/irem and ldiv/lrem not handled by this method"); + + if (left->is_single_cpu()) { + assert(left == dest, "left and dest must be equal"); + Register lreg = left->as_register(); + + if (right->is_single_cpu()) { + // cpu register - cpu register + Register rreg = right->as_register(); + switch (code) { + case lir_add: __ addl (lreg, rreg); break; + case lir_sub: __ subl (lreg, rreg); break; + case lir_mul: __ imull(lreg, rreg); break; + default: ShouldNotReachHere(); + } + + } else if (right->is_stack()) { + // cpu register - stack + Address raddr = frame_map()->address_for_slot(right->single_stack_ix()); + switch (code) { + case lir_add: __ addl(lreg, raddr); break; + case lir_sub: __ subl(lreg, raddr); break; + default: ShouldNotReachHere(); + } + + } else if (right->is_constant()) { + // cpu register - constant + jint c = right->as_constant_ptr()->as_jint(); + switch (code) { + case lir_add: { + __ increment(lreg, c); + break; + } + case lir_sub: { + __ decrement(lreg, c); + break; + } + default: ShouldNotReachHere(); + } + + } else { + ShouldNotReachHere(); + } + + } else if (left->is_double_cpu()) { + assert(left == dest, "left and dest must be equal"); + Register lreg_lo = left->as_register_lo(); + Register lreg_hi = left->as_register_hi(); + + if (right->is_double_cpu()) { + // cpu register - cpu register + Register rreg_lo = right->as_register_lo(); + Register rreg_hi = right->as_register_hi(); + assert_different_registers(lreg_lo, lreg_hi, rreg_lo, rreg_hi); + switch (code) { + case lir_add: + __ addl(lreg_lo, rreg_lo); + __ adcl(lreg_hi, rreg_hi); + break; + case lir_sub: + __ subl(lreg_lo, rreg_lo); + __ sbbl(lreg_hi, rreg_hi); + break; + case lir_mul: + assert(lreg_lo == rax && lreg_hi == rdx, "must be"); + __ imull(lreg_hi, rreg_lo); + __ imull(rreg_hi, lreg_lo); + __ addl (rreg_hi, lreg_hi); + __ mull (rreg_lo); + __ addl (lreg_hi, rreg_hi); + break; + default: + ShouldNotReachHere(); + } + + } else if (right->is_constant()) { + // cpu register - constant + jint c_lo = right->as_constant_ptr()->as_jint_lo(); + jint c_hi = right->as_constant_ptr()->as_jint_hi(); + switch (code) { + case lir_add: + __ addl(lreg_lo, c_lo); + __ adcl(lreg_hi, c_hi); + break; + case lir_sub: + __ subl(lreg_lo, c_lo); + __ sbbl(lreg_hi, c_hi); + break; + default: + ShouldNotReachHere(); + } + + } else { + ShouldNotReachHere(); + } + + } else if (left->is_single_xmm()) { + assert(left == dest, "left and dest must be equal"); + XMMRegister lreg = left->as_xmm_float_reg(); + + if (right->is_single_xmm()) { + XMMRegister rreg = right->as_xmm_float_reg(); + switch (code) { + case lir_add: __ addss(lreg, rreg); break; + case lir_sub: __ subss(lreg, rreg); break; + case lir_mul_strictfp: // fall through + case lir_mul: __ mulss(lreg, rreg); break; + case lir_div_strictfp: // fall through + case lir_div: __ divss(lreg, rreg); break; + default: ShouldNotReachHere(); + } + } else { + Address raddr; + if (right->is_single_stack()) { + raddr = frame_map()->address_for_slot(right->single_stack_ix()); + } else if (right->is_constant()) { + // hack for now + raddr = __ as_Address(InternalAddress(float_constant(right->as_jfloat()))); + } else { + ShouldNotReachHere(); + } + switch (code) { + case lir_add: __ addss(lreg, raddr); break; + case lir_sub: __ subss(lreg, raddr); break; + case lir_mul_strictfp: // fall through + case lir_mul: __ mulss(lreg, raddr); break; + case lir_div_strictfp: // fall through + case lir_div: __ divss(lreg, raddr); break; + default: ShouldNotReachHere(); + } + } + + } else if (left->is_double_xmm()) { + assert(left == dest, "left and dest must be equal"); + + XMMRegister lreg = left->as_xmm_double_reg(); + if (right->is_double_xmm()) { + XMMRegister rreg = right->as_xmm_double_reg(); + switch (code) { + case lir_add: __ addsd(lreg, rreg); break; + case lir_sub: __ subsd(lreg, rreg); break; + case lir_mul_strictfp: // fall through + case lir_mul: __ mulsd(lreg, rreg); break; + case lir_div_strictfp: // fall through + case lir_div: __ divsd(lreg, rreg); break; + default: ShouldNotReachHere(); + } + } else { + Address raddr; + if (right->is_double_stack()) { + raddr = frame_map()->address_for_slot(right->double_stack_ix()); + } else if (right->is_constant()) { + // hack for now + raddr = __ as_Address(InternalAddress(double_constant(right->as_jdouble()))); + } else { + ShouldNotReachHere(); + } + switch (code) { + case lir_add: __ addsd(lreg, raddr); break; + case lir_sub: __ subsd(lreg, raddr); break; + case lir_mul_strictfp: // fall through + case lir_mul: __ mulsd(lreg, raddr); break; + case lir_div_strictfp: // fall through + case lir_div: __ divsd(lreg, raddr); break; + default: ShouldNotReachHere(); + } + } + + } else if (left->is_single_fpu()) { + assert(dest->is_single_fpu(), "fpu stack allocation required"); + + if (right->is_single_fpu()) { + arith_fpu_implementation(code, left->fpu_regnr(), right->fpu_regnr(), dest->fpu_regnr(), pop_fpu_stack); + + } else { + assert(left->fpu_regnr() == 0, "left must be on TOS"); + assert(dest->fpu_regnr() == 0, "dest must be on TOS"); + + Address raddr; + if (right->is_single_stack()) { + raddr = frame_map()->address_for_slot(right->single_stack_ix()); + } else if (right->is_constant()) { + address const_addr = float_constant(right->as_jfloat()); + assert(const_addr != NULL, "incorrect float/double constant maintainance"); + // hack for now + raddr = __ as_Address(InternalAddress(const_addr)); + } else { + ShouldNotReachHere(); + } + + switch (code) { + case lir_add: __ fadd_s(raddr); break; + case lir_sub: __ fsub_s(raddr); break; + case lir_mul_strictfp: // fall through + case lir_mul: __ fmul_s(raddr); break; + case lir_div_strictfp: // fall through + case lir_div: __ fdiv_s(raddr); break; + default: ShouldNotReachHere(); + } + } + + } else if (left->is_double_fpu()) { + assert(dest->is_double_fpu(), "fpu stack allocation required"); + + if (code == lir_mul_strictfp || code == lir_div_strictfp) { + // Double values require special handling for strictfp mul/div on x86 + __ fld_x(ExternalAddress(StubRoutines::addr_fpu_subnormal_bias1())); + __ fmulp(left->fpu_regnrLo() + 1); + } + + if (right->is_double_fpu()) { + arith_fpu_implementation(code, left->fpu_regnrLo(), right->fpu_regnrLo(), dest->fpu_regnrLo(), pop_fpu_stack); + + } else { + assert(left->fpu_regnrLo() == 0, "left must be on TOS"); + assert(dest->fpu_regnrLo() == 0, "dest must be on TOS"); + + Address raddr; + if (right->is_double_stack()) { + raddr = frame_map()->address_for_slot(right->double_stack_ix()); + } else if (right->is_constant()) { + // hack for now + raddr = __ as_Address(InternalAddress(double_constant(right->as_jdouble()))); + } else { + ShouldNotReachHere(); + } + + switch (code) { + case lir_add: __ fadd_d(raddr); break; + case lir_sub: __ fsub_d(raddr); break; + case lir_mul_strictfp: // fall through + case lir_mul: __ fmul_d(raddr); break; + case lir_div_strictfp: // fall through + case lir_div: __ fdiv_d(raddr); break; + default: ShouldNotReachHere(); + } + } + + if (code == lir_mul_strictfp || code == lir_div_strictfp) { + // Double values require special handling for strictfp mul/div on x86 + __ fld_x(ExternalAddress(StubRoutines::addr_fpu_subnormal_bias2())); + __ fmulp(dest->fpu_regnrLo() + 1); + } + + } else if (left->is_single_stack() || left->is_address()) { + assert(left == dest, "left and dest must be equal"); + + Address laddr; + if (left->is_single_stack()) { + laddr = frame_map()->address_for_slot(left->single_stack_ix()); + } else if (left->is_address()) { + laddr = as_Address(left->as_address_ptr()); + } else { + ShouldNotReachHere(); + } + + if (right->is_single_cpu()) { + Register rreg = right->as_register(); + switch (code) { + case lir_add: __ addl(laddr, rreg); break; + case lir_sub: __ subl(laddr, rreg); break; + default: ShouldNotReachHere(); + } + } else if (right->is_constant()) { + jint c = right->as_constant_ptr()->as_jint(); + switch (code) { + case lir_add: { + __ increment(laddr, c); + break; + } + case lir_sub: { + __ decrement(laddr, c); + break; + } + default: ShouldNotReachHere(); + } + } else { + ShouldNotReachHere(); + } + + } else { + ShouldNotReachHere(); + } +} + +void LIR_Assembler::arith_fpu_implementation(LIR_Code code, int left_index, int right_index, int dest_index, bool pop_fpu_stack) { + assert(pop_fpu_stack || (left_index == dest_index || right_index == dest_index), "invalid LIR"); + assert(!pop_fpu_stack || (left_index - 1 == dest_index || right_index - 1 == dest_index), "invalid LIR"); + assert(left_index == 0 || right_index == 0, "either must be on top of stack"); + + bool left_is_tos = (left_index == 0); + bool dest_is_tos = (dest_index == 0); + int non_tos_index = (left_is_tos ? right_index : left_index); + + switch (code) { + case lir_add: + if (pop_fpu_stack) __ faddp(non_tos_index); + else if (dest_is_tos) __ fadd (non_tos_index); + else __ fadda(non_tos_index); + break; + + case lir_sub: + if (left_is_tos) { + if (pop_fpu_stack) __ fsubrp(non_tos_index); + else if (dest_is_tos) __ fsub (non_tos_index); + else __ fsubra(non_tos_index); + } else { + if (pop_fpu_stack) __ fsubp (non_tos_index); + else if (dest_is_tos) __ fsubr (non_tos_index); + else __ fsuba (non_tos_index); + } + break; + + case lir_mul_strictfp: // fall through + case lir_mul: + if (pop_fpu_stack) __ fmulp(non_tos_index); + else if (dest_is_tos) __ fmul (non_tos_index); + else __ fmula(non_tos_index); + break; + + case lir_div_strictfp: // fall through + case lir_div: + if (left_is_tos) { + if (pop_fpu_stack) __ fdivrp(non_tos_index); + else if (dest_is_tos) __ fdiv (non_tos_index); + else __ fdivra(non_tos_index); + } else { + if (pop_fpu_stack) __ fdivp (non_tos_index); + else if (dest_is_tos) __ fdivr (non_tos_index); + else __ fdiva (non_tos_index); + } + break; + + case lir_rem: + assert(left_is_tos && dest_is_tos && right_index == 1, "must be guaranteed by FPU stack allocation"); + __ fremr(noreg); + break; + + default: + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr unused, LIR_Opr dest, LIR_Op* op) { + if (value->is_double_xmm()) { + switch(code) { + case lir_abs : + { + if (dest->as_xmm_double_reg() != value->as_xmm_double_reg()) { + __ movdbl(dest->as_xmm_double_reg(), value->as_xmm_double_reg()); + } + __ andpd(dest->as_xmm_double_reg(), + ExternalAddress((address)double_signmask_pool)); + } + break; + + case lir_sqrt: __ sqrtsd(dest->as_xmm_double_reg(), value->as_xmm_double_reg()); break; + // all other intrinsics are not available in the SSE instruction set, so FPU is used + default : ShouldNotReachHere(); + } + + } else if (value->is_double_fpu()) { + assert(value->fpu_regnrLo() == 0 && dest->fpu_regnrLo() == 0, "both must be on TOS"); + switch(code) { + case lir_log : __ flog() ; break; + case lir_log10 : __ flog10() ; break; + case lir_abs : __ fabs() ; break; + case lir_sqrt : __ fsqrt(); break; + case lir_sin : + // Should consider not saving rbx, if not necessary + __ trigfunc('s', op->as_Op2()->fpu_stack_size()); + break; + case lir_cos : + // Should consider not saving rbx, if not necessary + assert(op->as_Op2()->fpu_stack_size() <= 6, "sin and cos need two free stack slots"); + __ trigfunc('c', op->as_Op2()->fpu_stack_size()); + break; + case lir_tan : + // Should consider not saving rbx, if not necessary + __ trigfunc('t', op->as_Op2()->fpu_stack_size()); + break; + default : ShouldNotReachHere(); + } + } else { + Unimplemented(); + } +} + +void LIR_Assembler::logic_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dst) { + // assert(left->destroys_register(), "check"); + if (left->is_single_cpu()) { + Register reg = left->as_register(); + if (right->is_constant()) { + int val = right->as_constant_ptr()->as_jint(); + switch (code) { + case lir_logic_and: __ andl (reg, val); break; + case lir_logic_or: __ orl (reg, val); break; + case lir_logic_xor: __ xorl (reg, val); break; + default: ShouldNotReachHere(); + } + } else if (right->is_stack()) { + // added support for stack operands + Address raddr = frame_map()->address_for_slot(right->single_stack_ix()); + switch (code) { + case lir_logic_and: __ andl (reg, raddr); break; + case lir_logic_or: __ orl (reg, raddr); break; + case lir_logic_xor: __ xorl (reg, raddr); break; + default: ShouldNotReachHere(); + } + } else { + Register rright = right->as_register(); + switch (code) { + case lir_logic_and: __ andl (reg, rright); break; + case lir_logic_or : __ orl (reg, rright); break; + case lir_logic_xor: __ xorl (reg, rright); break; + default: ShouldNotReachHere(); + } + } + move_regs(reg, dst->as_register()); + } else { + Register l_lo = left->as_register_lo(); + Register l_hi = left->as_register_hi(); + if (right->is_constant()) { + int r_lo = right->as_constant_ptr()->as_jint_lo(); + int r_hi = right->as_constant_ptr()->as_jint_hi(); + switch (code) { + case lir_logic_and: + __ andl(l_lo, r_lo); + __ andl(l_hi, r_hi); + break; + case lir_logic_or: + __ orl(l_lo, r_lo); + __ orl(l_hi, r_hi); + break; + case lir_logic_xor: + __ xorl(l_lo, r_lo); + __ xorl(l_hi, r_hi); + break; + default: ShouldNotReachHere(); + } + } else { + Register r_lo = right->as_register_lo(); + Register r_hi = right->as_register_hi(); + assert(l_lo != r_hi, "overwriting registers"); + switch (code) { + case lir_logic_and: + __ andl(l_lo, r_lo); + __ andl(l_hi, r_hi); + break; + case lir_logic_or: + __ orl(l_lo, r_lo); + __ orl(l_hi, r_hi); + break; + case lir_logic_xor: + __ xorl(l_lo, r_lo); + __ xorl(l_hi, r_hi); + break; + default: ShouldNotReachHere(); + } + } + + Register dst_lo = dst->as_register_lo(); + Register dst_hi = dst->as_register_hi(); + + if (dst_lo == l_hi) { + assert(dst_hi != l_lo, "overwriting registers"); + move_regs(l_hi, dst_hi); + move_regs(l_lo, dst_lo); + } else { + assert(dst_lo != l_hi, "overwriting registers"); + move_regs(l_lo, dst_lo); + move_regs(l_hi, dst_hi); + } + } +} + + +// we assume that rax, and rdx can be overwritten +void LIR_Assembler::arithmetic_idiv(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr temp, LIR_Opr result, CodeEmitInfo* info) { + + assert(left->is_single_cpu(), "left must be register"); + assert(right->is_single_cpu() || right->is_constant(), "right must be register or constant"); + assert(result->is_single_cpu(), "result must be register"); + + // assert(left->destroys_register(), "check"); + // assert(right->destroys_register(), "check"); + + Register lreg = left->as_register(); + Register dreg = result->as_register(); + + if (right->is_constant()) { + int divisor = right->as_constant_ptr()->as_jint(); + assert(divisor > 0 && is_power_of_2(divisor), "must be"); + if (code == lir_idiv) { + assert(lreg == rax, "must be rax,"); + assert(temp->as_register() == rdx, "tmp register must be rdx"); + __ cdql(); // sign extend into rdx:rax + if (divisor == 2) { + __ subl(lreg, rdx); + } else { + __ andl(rdx, divisor - 1); + __ addl(lreg, rdx); + } + __ sarl(lreg, log2_intptr(divisor)); + move_regs(lreg, dreg); + } else if (code == lir_irem) { + Label done; + __ movl(dreg, lreg); + __ andl(dreg, 0x80000000 | (divisor - 1)); + __ jcc(Assembler::positive, done); + __ decrement(dreg); + __ orl(dreg, ~(divisor - 1)); + __ increment(dreg); + __ bind(done); + } else { + ShouldNotReachHere(); + } + } else { + Register rreg = right->as_register(); + assert(lreg == rax, "left register must be rax,"); + assert(rreg != rdx, "right register must not be rdx"); + assert(temp->as_register() == rdx, "tmp register must be rdx"); + + move_regs(lreg, rax); + + int idivl_offset = __ corrected_idivl(rreg); + add_debug_info_for_div0(idivl_offset, info); + if (code == lir_irem) { + move_regs(rdx, dreg); // result is in rdx + } else { + move_regs(rax, dreg); + } + } +} + + +void LIR_Assembler::comp_op(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Op2* op) { + if (opr1->is_single_cpu()) { + Register reg1 = opr1->as_register(); + if (opr2->is_single_cpu()) { + // cpu register - cpu register + __ cmpl(reg1, opr2->as_register()); + } else if (opr2->is_stack()) { + // cpu register - stack + __ cmpl(reg1, frame_map()->address_for_slot(opr2->single_stack_ix())); + } else if (opr2->is_constant()) { + // cpu register - constant + LIR_Const* c = opr2->as_constant_ptr(); + if (c->type() == T_INT) { + __ cmpl(reg1, c->as_jint()); + } else if (c->type() == T_OBJECT) { + jobject o = c->as_jobject(); + if (o == NULL) { + __ cmpl(reg1, NULL_WORD); + } else { + __ cmpoop(reg1, c->as_jobject()); + } + } else { + ShouldNotReachHere(); + } + // cpu register - address + } else if (opr2->is_address()) { + if (op->info() != NULL) { + add_debug_info_for_null_check_here(op->info()); + } + __ cmpl(reg1, as_Address(opr2->as_address_ptr())); + } else { + ShouldNotReachHere(); + } + + } else if(opr1->is_double_cpu()) { + Register xlo = opr1->as_register_lo(); + Register xhi = opr1->as_register_hi(); + if (opr2->is_double_cpu()) { + // cpu register - cpu register + Register ylo = opr2->as_register_lo(); + Register yhi = opr2->as_register_hi(); + __ subl(xlo, ylo); + __ sbbl(xhi, yhi); + if (condition == lir_cond_equal || condition == lir_cond_notEqual) { + __ orl(xhi, xlo); + } + } else if (opr2->is_constant()) { + // cpu register - constant 0 + assert(opr2->as_jlong() == (jlong)0, "only handles zero"); + assert(condition == lir_cond_equal || condition == lir_cond_notEqual, "only handles equals case"); + __ orl(xhi, xlo); + } else { + ShouldNotReachHere(); + } + + } else if (opr1->is_single_xmm()) { + XMMRegister reg1 = opr1->as_xmm_float_reg(); + if (opr2->is_single_xmm()) { + // xmm register - xmm register + __ ucomiss(reg1, opr2->as_xmm_float_reg()); + } else if (opr2->is_stack()) { + // xmm register - stack + __ ucomiss(reg1, frame_map()->address_for_slot(opr2->single_stack_ix())); + } else if (opr2->is_constant()) { + // xmm register - constant + __ ucomiss(reg1, InternalAddress(float_constant(opr2->as_jfloat()))); + } else if (opr2->is_address()) { + // xmm register - address + if (op->info() != NULL) { + add_debug_info_for_null_check_here(op->info()); + } + __ ucomiss(reg1, as_Address(opr2->as_address_ptr())); + } else { + ShouldNotReachHere(); + } + + } else if (opr1->is_double_xmm()) { + XMMRegister reg1 = opr1->as_xmm_double_reg(); + if (opr2->is_double_xmm()) { + // xmm register - xmm register + __ ucomisd(reg1, opr2->as_xmm_double_reg()); + } else if (opr2->is_stack()) { + // xmm register - stack + __ ucomisd(reg1, frame_map()->address_for_slot(opr2->double_stack_ix())); + } else if (opr2->is_constant()) { + // xmm register - constant + __ ucomisd(reg1, InternalAddress(double_constant(opr2->as_jdouble()))); + } else if (opr2->is_address()) { + // xmm register - address + if (op->info() != NULL) { + add_debug_info_for_null_check_here(op->info()); + } + __ ucomisd(reg1, as_Address(opr2->pointer()->as_address())); + } else { + ShouldNotReachHere(); + } + + } else if(opr1->is_single_fpu() || opr1->is_double_fpu()) { + assert(opr1->is_fpu_register() && opr1->fpu() == 0, "currently left-hand side must be on TOS (relax this restriction)"); + assert(opr2->is_fpu_register(), "both must be registers"); + __ fcmp(noreg, opr2->fpu(), op->fpu_pop_count() > 0, op->fpu_pop_count() > 1); + + } else if (opr1->is_address() && opr2->is_constant()) { + if (op->info() != NULL) { + add_debug_info_for_null_check_here(op->info()); + } + // special case: address - constant + LIR_Address* addr = opr1->as_address_ptr(); + LIR_Const* c = opr2->as_constant_ptr(); + if (c->type() == T_INT) { + __ cmpl(as_Address(addr), c->as_jint()); + } else if (c->type() == T_OBJECT) { + __ cmpoop(as_Address(addr), c->as_jobject()); + } else { + ShouldNotReachHere(); + } + + } else { + ShouldNotReachHere(); + } +} + +void LIR_Assembler::comp_fl2i(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dst, LIR_Op2* op) { + if (code == lir_cmp_fd2i || code == lir_ucmp_fd2i) { + if (left->is_single_xmm()) { + assert(right->is_single_xmm(), "must match"); + __ cmpss2int(left->as_xmm_float_reg(), right->as_xmm_float_reg(), dst->as_register(), code == lir_ucmp_fd2i); + } else if (left->is_double_xmm()) { + assert(right->is_double_xmm(), "must match"); + __ cmpsd2int(left->as_xmm_double_reg(), right->as_xmm_double_reg(), dst->as_register(), code == lir_ucmp_fd2i); + + } else { + assert(left->is_single_fpu() || left->is_double_fpu(), "must be"); + assert(right->is_single_fpu() || right->is_double_fpu(), "must match"); + + assert(left->fpu() == 0, "left must be on TOS"); + __ fcmp2int(dst->as_register(), code == lir_ucmp_fd2i, right->fpu(), + op->fpu_pop_count() > 0, op->fpu_pop_count() > 1); + } + } else { + assert(code == lir_cmp_l2i, "check"); + __ lcmp2int(left->as_register_hi(), + left->as_register_lo(), + right->as_register_hi(), + right->as_register_lo()); + move_regs(left->as_register_hi(), dst->as_register()); + } +} + + +void LIR_Assembler::align_call(LIR_Code code) { + if (os::is_MP()) { + // make sure that the displacement word of the call ends up word aligned + int offset = __ offset(); + switch (code) { + case lir_static_call: + case lir_optvirtual_call: + offset += NativeCall::displacement_offset; + break; + case lir_icvirtual_call: + offset += NativeCall::displacement_offset + NativeMovConstReg::instruction_size; + break; + case lir_virtual_call: // currently, sparc-specific for niagara + default: ShouldNotReachHere(); + } + while (offset++ % BytesPerWord != 0) { + __ nop(); + } + } +} + + +void LIR_Assembler::call(address entry, relocInfo::relocType rtype, CodeEmitInfo* info) { + assert(!os::is_MP() || (__ offset() + NativeCall::displacement_offset) % BytesPerWord == 0, + "must be aligned"); + __ call(AddressLiteral(entry, rtype)); + add_call_info(code_offset(), info); +} + + +void LIR_Assembler::ic_call(address entry, CodeEmitInfo* info) { + RelocationHolder rh = virtual_call_Relocation::spec(pc()); + __ movoop(IC_Klass, (jobject)Universe::non_oop_word()); + assert(!os::is_MP() || + (__ offset() + NativeCall::displacement_offset) % BytesPerWord == 0, + "must be aligned"); + __ call(AddressLiteral(entry, rh)); + add_call_info(code_offset(), info); +} + + +/* Currently, vtable-dispatch is only enabled for sparc platforms */ +void LIR_Assembler::vtable_call(int vtable_offset, CodeEmitInfo* info) { + ShouldNotReachHere(); +} + +void LIR_Assembler::emit_static_call_stub() { + address call_pc = __ pc(); + address stub = __ start_a_stub(call_stub_size); + if (stub == NULL) { + bailout("static call stub overflow"); + return; + } + + int start = __ offset(); + if (os::is_MP()) { + // make sure that the displacement word of the call ends up word aligned + int offset = __ offset() + NativeMovConstReg::instruction_size + NativeCall::displacement_offset; + while (offset++ % BytesPerWord != 0) { + __ nop(); + } + } + __ relocate(static_stub_Relocation::spec(call_pc)); + __ movoop(rbx, (jobject)NULL); + // must be set to -1 at code generation time + assert(!os::is_MP() || ((__ offset() + 1) % BytesPerWord) == 0, "must be aligned on MP"); + __ jump(RuntimeAddress((address)-1)); + + assert(__ offset() - start <= call_stub_size, "stub too big") + __ end_a_stub(); +} + + +void LIR_Assembler::throw_op(LIR_Opr exceptionPC, LIR_Opr exceptionOop, CodeEmitInfo* info, bool unwind) { + assert(exceptionOop->as_register() == rax, "must match"); + assert(unwind || exceptionPC->as_register() == rdx, "must match"); + + // exception object is not added to oop map by LinearScan + // (LinearScan assumes that no oops are in fixed registers) + info->add_register_oop(exceptionOop); + Runtime1::StubID unwind_id; + + if (!unwind) { + // get current pc information + // pc is only needed if the method has an exception handler, the unwind code does not need it. + int pc_for_athrow_offset = __ offset(); + InternalAddress pc_for_athrow(__ pc()); + __ lea(exceptionPC->as_register(), pc_for_athrow); + add_call_info(pc_for_athrow_offset, info); // for exception handler + + __ verify_not_null_oop(rax); + // search an exception handler (rax: exception oop, rdx: throwing pc) + if (compilation()->has_fpu_code()) { + unwind_id = Runtime1::handle_exception_id; + } else { + unwind_id = Runtime1::handle_exception_nofpu_id; + } + } else { + unwind_id = Runtime1::unwind_exception_id; + } + __ call(RuntimeAddress(Runtime1::entry_for(unwind_id))); + + // enough room for two byte trap + __ nop(); +} + + +void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, LIR_Opr count, LIR_Opr dest, LIR_Opr tmp) { + + // optimized version for linear scan: + // * count must be already in ECX (guaranteed by LinearScan) + // * left and dest must be equal + // * tmp must be unused + assert(count->as_register() == SHIFT_count, "count must be in ECX"); + assert(left == dest, "left and dest must be equal"); + assert(tmp->is_illegal(), "wasting a register if tmp is allocated"); + + if (left->is_single_cpu()) { + Register value = left->as_register(); + assert(value != SHIFT_count, "left cannot be ECX"); + + switch (code) { + case lir_shl: __ shll(value); break; + case lir_shr: __ sarl(value); break; + case lir_ushr: __ shrl(value); break; + default: ShouldNotReachHere(); + } + } else if (left->is_double_cpu()) { + Register lo = left->as_register_lo(); + Register hi = left->as_register_hi(); + assert(lo != SHIFT_count && hi != SHIFT_count, "left cannot be ECX"); + + switch (code) { + case lir_shl: __ lshl(hi, lo); break; + case lir_shr: __ lshr(hi, lo, true); break; + case lir_ushr: __ lshr(hi, lo, false); break; + default: ShouldNotReachHere(); + } + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr dest) { + if (dest->is_single_cpu()) { + // first move left into dest so that left is not destroyed by the shift + Register value = dest->as_register(); + count = count & 0x1F; // Java spec + + move_regs(left->as_register(), value); + switch (code) { + case lir_shl: __ shll(value, count); break; + case lir_shr: __ sarl(value, count); break; + case lir_ushr: __ shrl(value, count); break; + default: ShouldNotReachHere(); + } + } else if (dest->is_double_cpu()) { + Unimplemented(); + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::store_parameter(Register r, int offset_from_rsp_in_words) { + assert(offset_from_rsp_in_words >= 0, "invalid offset from rsp"); + int offset_from_rsp_in_bytes = offset_from_rsp_in_words * BytesPerWord; + assert(offset_from_rsp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset"); + __ movl (Address(rsp, offset_from_rsp_in_bytes), r); +} + + +void LIR_Assembler::store_parameter(jint c, int offset_from_rsp_in_words) { + assert(offset_from_rsp_in_words >= 0, "invalid offset from rsp"); + int offset_from_rsp_in_bytes = offset_from_rsp_in_words * BytesPerWord; + assert(offset_from_rsp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset"); + __ movl (Address(rsp, offset_from_rsp_in_bytes), c); +} + + +void LIR_Assembler::store_parameter(jobject o, int offset_from_rsp_in_words) { + assert(offset_from_rsp_in_words >= 0, "invalid offset from rsp"); + int offset_from_rsp_in_bytes = offset_from_rsp_in_words * BytesPerWord; + assert(offset_from_rsp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset"); + __ movoop (Address(rsp, offset_from_rsp_in_bytes), o); +} + + +// This code replaces a call to arraycopy; no exception may +// be thrown in this code, they must be thrown in the System.arraycopy +// activation frame; we could save some checks if this would not be the case +void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { + ciArrayKlass* default_type = op->expected_type(); + Register src = op->src()->as_register(); + Register dst = op->dst()->as_register(); + Register src_pos = op->src_pos()->as_register(); + Register dst_pos = op->dst_pos()->as_register(); + Register length = op->length()->as_register(); + Register tmp = op->tmp()->as_register(); + + CodeStub* stub = op->stub(); + int flags = op->flags(); + BasicType basic_type = default_type != NULL ? default_type->element_type()->basic_type() : T_ILLEGAL; + if (basic_type == T_ARRAY) basic_type = T_OBJECT; + + // if we don't know anything or it's an object array, just go through the generic arraycopy + if (default_type == NULL) { + Label done; + // save outgoing arguments on stack in case call to System.arraycopy is needed + // HACK ALERT. This code used to push the parameters in a hardwired fashion + // for interpreter calling conventions. Now we have to do it in new style conventions. + // For the moment until C1 gets the new register allocator I just force all the + // args to the right place (except the register args) and then on the back side + // reload the register args properly if we go slow path. Yuck + + // These are proper for the calling convention + + store_parameter(length, 2); + store_parameter(dst_pos, 1); + store_parameter(dst, 0); + + // these are just temporary placements until we need to reload + store_parameter(src_pos, 3); + store_parameter(src, 4); + assert(src == rcx && src_pos == rdx, "mismatch in calling convention"); + + // pass arguments: may push as this is not a safepoint; SP must be fix at each safepoint + __ pushl(length); + __ pushl(dst_pos); + __ pushl(dst); + __ pushl(src_pos); + __ pushl(src); + address entry = CAST_FROM_FN_PTR(address, Runtime1::arraycopy); + __ call_VM_leaf(entry, 5); // removes pushed parameter from the stack + + __ cmpl(rax, 0); + __ jcc(Assembler::equal, *stub->continuation()); + + // Reload values from the stack so they are where the stub + // expects them. + __ movl (dst, Address(rsp, 0*BytesPerWord)); + __ movl (dst_pos, Address(rsp, 1*BytesPerWord)); + __ movl (length, Address(rsp, 2*BytesPerWord)); + __ movl (src_pos, Address(rsp, 3*BytesPerWord)); + __ movl (src, Address(rsp, 4*BytesPerWord)); + __ jmp(*stub->entry()); + + __ bind(*stub->continuation()); + return; + } + + assert(default_type != NULL && default_type->is_array_klass() && default_type->is_loaded(), "must be true at this point"); + + int elem_size = type2aelembytes[basic_type]; + int shift_amount; + Address::ScaleFactor scale; + + switch (elem_size) { + case 1 : + shift_amount = 0; + scale = Address::times_1; + break; + case 2 : + shift_amount = 1; + scale = Address::times_2; + break; + case 4 : + shift_amount = 2; + scale = Address::times_4; + break; + case 8 : + shift_amount = 3; + scale = Address::times_8; + break; + default: + ShouldNotReachHere(); + } + + Address src_length_addr = Address(src, arrayOopDesc::length_offset_in_bytes()); + Address dst_length_addr = Address(dst, arrayOopDesc::length_offset_in_bytes()); + Address src_klass_addr = Address(src, oopDesc::klass_offset_in_bytes()); + Address dst_klass_addr = Address(dst, oopDesc::klass_offset_in_bytes()); + + // test for NULL + if (flags & LIR_OpArrayCopy::src_null_check) { + __ testl(src, src); + __ jcc(Assembler::zero, *stub->entry()); + } + if (flags & LIR_OpArrayCopy::dst_null_check) { + __ testl(dst, dst); + __ jcc(Assembler::zero, *stub->entry()); + } + + // check if negative + if (flags & LIR_OpArrayCopy::src_pos_positive_check) { + __ testl(src_pos, src_pos); + __ jcc(Assembler::less, *stub->entry()); + } + if (flags & LIR_OpArrayCopy::dst_pos_positive_check) { + __ testl(dst_pos, dst_pos); + __ jcc(Assembler::less, *stub->entry()); + } + if (flags & LIR_OpArrayCopy::length_positive_check) { + __ testl(length, length); + __ jcc(Assembler::less, *stub->entry()); + } + + if (flags & LIR_OpArrayCopy::src_range_check) { + __ leal(tmp, Address(src_pos, length, Address::times_1, 0)); + __ cmpl(tmp, src_length_addr); + __ jcc(Assembler::above, *stub->entry()); + } + if (flags & LIR_OpArrayCopy::dst_range_check) { + __ leal(tmp, Address(dst_pos, length, Address::times_1, 0)); + __ cmpl(tmp, dst_length_addr); + __ jcc(Assembler::above, *stub->entry()); + } + + if (flags & LIR_OpArrayCopy::type_check) { + __ movl(tmp, src_klass_addr); + __ cmpl(tmp, dst_klass_addr); + __ jcc(Assembler::notEqual, *stub->entry()); + } + +#ifdef ASSERT + if (basic_type != T_OBJECT || !(flags & LIR_OpArrayCopy::type_check)) { + // Sanity check the known type with the incoming class. For the + // primitive case the types must match exactly with src.klass and + // dst.klass each exactly matching the default type. For the + // object array case, if no type check is needed then either the + // dst type is exactly the expected type and the src type is a + // subtype which we can't check or src is the same array as dst + // but not necessarily exactly of type default_type. + Label known_ok, halt; + __ movoop(tmp, default_type->encoding()); + if (basic_type != T_OBJECT) { + __ cmpl(tmp, dst_klass_addr); + __ jcc(Assembler::notEqual, halt); + __ cmpl(tmp, src_klass_addr); + __ jcc(Assembler::equal, known_ok); + } else { + __ cmpl(tmp, dst_klass_addr); + __ jcc(Assembler::equal, known_ok); + __ cmpl(src, dst); + __ jcc(Assembler::equal, known_ok); + } + __ bind(halt); + __ stop("incorrect type information in arraycopy"); + __ bind(known_ok); + } +#endif + + __ leal(tmp, Address(src, src_pos, scale, arrayOopDesc::base_offset_in_bytes(basic_type))); + store_parameter(tmp, 0); + __ leal(tmp, Address(dst, dst_pos, scale, arrayOopDesc::base_offset_in_bytes(basic_type))); + store_parameter(tmp, 1); + if (shift_amount > 0 && basic_type != T_OBJECT) { + __ shll(length, shift_amount); + } + store_parameter(length, 2); + if (basic_type == T_OBJECT) { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Runtime1::oop_arraycopy), 0); + } else { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Runtime1::primitive_arraycopy), 0); + } + + __ bind(*stub->continuation()); +} + + +void LIR_Assembler::emit_lock(LIR_OpLock* op) { + Register obj = op->obj_opr()->as_register(); // may not be an oop + Register hdr = op->hdr_opr()->as_register(); + Register lock = op->lock_opr()->as_register(); + if (!UseFastLocking) { + __ jmp(*op->stub()->entry()); + } else if (op->code() == lir_lock) { + Register scratch = noreg; + if (UseBiasedLocking) { + scratch = op->scratch_opr()->as_register(); + } + assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); + // add debug info for NullPointerException only if one is possible + int null_check_offset = __ lock_object(hdr, obj, lock, scratch, *op->stub()->entry()); + if (op->info() != NULL) { + add_debug_info_for_null_check(null_check_offset, op->info()); + } + // done + } else if (op->code() == lir_unlock) { + assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); + __ unlock_object(hdr, obj, lock, *op->stub()->entry()); + } else { + Unimplemented(); + } + __ bind(*op->stub()->continuation()); +} + + +void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { + ciMethod* method = op->profiled_method(); + int bci = op->profiled_bci(); + + // Update counter for all call types + ciMethodData* md = method->method_data(); + if (md == NULL) { + bailout("out of memory building methodDataOop"); + return; + } + ciProfileData* data = md->bci_to_data(bci); + assert(data->is_CounterData(), "need CounterData for calls"); + assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); + Register mdo = op->mdo()->as_register(); + __ movoop(mdo, md->encoding()); + Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); + __ addl(counter_addr, DataLayout::counter_increment); + Bytecodes::Code bc = method->java_code_at_bci(bci); + // Perform additional virtual call profiling for invokevirtual and + // invokeinterface bytecodes + if ((bc == Bytecodes::_invokevirtual || bc == Bytecodes::_invokeinterface) && + Tier1ProfileVirtualCalls) { + assert(op->recv()->is_single_cpu(), "recv must be allocated"); + Register recv = op->recv()->as_register(); + assert_different_registers(mdo, recv); + assert(data->is_VirtualCallData(), "need VirtualCallData for virtual calls"); + ciKlass* known_klass = op->known_holder(); + if (Tier1OptimizeVirtualCallProfiling && known_klass != NULL) { + // We know the type that will be seen at this call site; we can + // statically update the methodDataOop rather than needing to do + // dynamic tests on the receiver type + + // NOTE: we should probably put a lock around this search to + // avoid collisions by concurrent compilations + ciVirtualCallData* vc_data = (ciVirtualCallData*) data; + uint i; + for (i = 0; i < VirtualCallData::row_limit(); i++) { + ciKlass* receiver = vc_data->receiver(i); + if (known_klass->equals(receiver)) { + Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); + __ addl(data_addr, DataLayout::counter_increment); + return; + } + } + + // Receiver type not found in profile data; select an empty slot + + // Note that this is less efficient than it should be because it + // always does a write to the receiver part of the + // VirtualCallData rather than just the first time + for (i = 0; i < VirtualCallData::row_limit(); i++) { + ciKlass* receiver = vc_data->receiver(i); + if (receiver == NULL) { + Address recv_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i))); + __ movoop(recv_addr, known_klass->encoding()); + Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); + __ addl(data_addr, DataLayout::counter_increment); + return; + } + } + } else { + __ movl(recv, Address(recv, oopDesc::klass_offset_in_bytes())); + Label update_done; + uint i; + for (i = 0; i < VirtualCallData::row_limit(); i++) { + Label next_test; + // See if the receiver is receiver[n]. + __ cmpl(recv, Address(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)))); + __ jcc(Assembler::notEqual, next_test); + Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); + __ addl(data_addr, DataLayout::counter_increment); + __ jmp(update_done); + __ bind(next_test); + } + + // Didn't find receiver; find next empty slot and fill it in + for (i = 0; i < VirtualCallData::row_limit(); i++) { + Label next_test; + Address recv_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i))); + __ cmpl(recv_addr, NULL_WORD); + __ jcc(Assembler::notEqual, next_test); + __ movl(recv_addr, recv); + __ movl(Address(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))), DataLayout::counter_increment); + if (i < (VirtualCallData::row_limit() - 1)) { + __ jmp(update_done); + } + __ bind(next_test); + } + + __ bind(update_done); + } + } +} + + +void LIR_Assembler::emit_delay(LIR_OpDelay*) { + Unimplemented(); +} + + +void LIR_Assembler::monitor_address(int monitor_no, LIR_Opr dst) { + __ leal(dst->as_register(), frame_map()->address_for_monitor_lock(monitor_no)); +} + + +void LIR_Assembler::align_backward_branch_target() { + __ align(BytesPerWord); +} + + +void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) { + if (left->is_single_cpu()) { + __ negl(left->as_register()); + move_regs(left->as_register(), dest->as_register()); + + } else if (left->is_double_cpu()) { + Register lo = left->as_register_lo(); + Register hi = left->as_register_hi(); + __ lneg(hi, lo); + if (dest->as_register_lo() == hi) { + assert(dest->as_register_hi() != lo, "destroying register"); + move_regs(hi, dest->as_register_hi()); + move_regs(lo, dest->as_register_lo()); + } else { + move_regs(lo, dest->as_register_lo()); + move_regs(hi, dest->as_register_hi()); + } + + } else if (dest->is_single_xmm()) { + if (left->as_xmm_float_reg() != dest->as_xmm_float_reg()) { + __ movflt(dest->as_xmm_float_reg(), left->as_xmm_float_reg()); + } + __ xorps(dest->as_xmm_float_reg(), + ExternalAddress((address)float_signflip_pool)); + + } else if (dest->is_double_xmm()) { + if (left->as_xmm_double_reg() != dest->as_xmm_double_reg()) { + __ movdbl(dest->as_xmm_double_reg(), left->as_xmm_double_reg()); + } + __ xorpd(dest->as_xmm_double_reg(), + ExternalAddress((address)double_signflip_pool)); + + } else if (left->is_single_fpu() || left->is_double_fpu()) { + assert(left->fpu() == 0, "arg must be on TOS"); + assert(dest->fpu() == 0, "dest must be TOS"); + __ fchs(); + + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::leal(LIR_Opr addr, LIR_Opr dest) { + assert(addr->is_address() && dest->is_register(), "check"); + Register reg = dest->as_register(); + __ leal(dest->as_register(), as_Address(addr->as_address_ptr())); +} + + + +void LIR_Assembler::rt_call(LIR_Opr result, address dest, const LIR_OprList* args, LIR_Opr tmp, CodeEmitInfo* info) { + assert(!tmp->is_valid(), "don't need temporary"); + __ call(RuntimeAddress(dest)); + if (info != NULL) { + add_call_info_here(info); + } +} + + +void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info) { + assert(type == T_LONG, "only for volatile long fields"); + + if (info != NULL) { + add_debug_info_for_null_check_here(info); + } + + if (src->is_double_xmm()) { + if (dest->is_double_cpu()) { + __ movd(dest->as_register_lo(), src->as_xmm_double_reg()); + __ psrlq(src->as_xmm_double_reg(), 32); + __ movd(dest->as_register_hi(), src->as_xmm_double_reg()); + } else if (dest->is_double_stack()) { + __ movdbl(frame_map()->address_for_slot(dest->double_stack_ix()), src->as_xmm_double_reg()); + } else if (dest->is_address()) { + __ movdbl(as_Address(dest->as_address_ptr()), src->as_xmm_double_reg()); + } else { + ShouldNotReachHere(); + } + + } else if (dest->is_double_xmm()) { + if (src->is_double_stack()) { + __ movdbl(dest->as_xmm_double_reg(), frame_map()->address_for_slot(src->double_stack_ix())); + } else if (src->is_address()) { + __ movdbl(dest->as_xmm_double_reg(), as_Address(src->as_address_ptr())); + } else { + ShouldNotReachHere(); + } + + } else if (src->is_double_fpu()) { + assert(src->fpu_regnrLo() == 0, "must be TOS"); + if (dest->is_double_stack()) { + __ fistp_d(frame_map()->address_for_slot(dest->double_stack_ix())); + } else if (dest->is_address()) { + __ fistp_d(as_Address(dest->as_address_ptr())); + } else { + ShouldNotReachHere(); + } + + } else if (dest->is_double_fpu()) { + assert(dest->fpu_regnrLo() == 0, "must be TOS"); + if (src->is_double_stack()) { + __ fild_d(frame_map()->address_for_slot(src->double_stack_ix())); + } else if (src->is_address()) { + __ fild_d(as_Address(src->as_address_ptr())); + } else { + ShouldNotReachHere(); + } + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::membar() { + __ membar(); +} + +void LIR_Assembler::membar_acquire() { + // No x86 machines currently require load fences + // __ load_fence(); +} + +void LIR_Assembler::membar_release() { + // No x86 machines currently require store fences + // __ store_fence(); +} + +void LIR_Assembler::get_thread(LIR_Opr result_reg) { + assert(result_reg->is_register(), "check"); + __ get_thread(result_reg->as_register()); +} + + +void LIR_Assembler::peephole(LIR_List*) { + // do nothing for now +} + + +#undef __ diff --git a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.hpp b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.hpp new file mode 100644 index 00000000000..5621a95a372 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.hpp @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: + + Address::ScaleFactor array_element_size(BasicType type) const; + + void monitorexit(LIR_Opr obj_opr, LIR_Opr lock_opr, Register new_hdr, int monitor_no, Register exception); + + void arith_fpu_implementation(LIR_Code code, int left_index, int right_index, int dest_index, bool pop_fpu_stack); + + // helper functions which checks for overflow and sets bailout if it + // occurs. Always returns a valid embeddable pointer but in the + // bailout case the pointer won't be to unique storage. + address float_constant(float f); + address double_constant(double d); + +public: + + void store_parameter(Register r, int offset_from_esp_in_words); + void store_parameter(jint c, int offset_from_esp_in_words); + void store_parameter(jobject c, int offset_from_esp_in_words); + + enum { call_stub_size = 15, + exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175), + deopt_handler_size = 10 + }; diff --git a/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp new file mode 100644 index 00000000000..a98d1b95416 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp @@ -0,0 +1,1261 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_LIRGenerator_x86.cpp.incl" + +#ifdef ASSERT +#define __ gen()->lir(__FILE__, __LINE__)-> +#else +#define __ gen()->lir()-> +#endif + +// Item will be loaded into a byte register; Intel only +void LIRItem::load_byte_item() { + load_item(); + LIR_Opr res = result(); + + if (!res->is_virtual() || !_gen->is_vreg_flag_set(res, LIRGenerator::byte_reg)) { + // make sure that it is a byte register + assert(!value()->type()->is_float() && !value()->type()->is_double(), + "can't load floats in byte register"); + LIR_Opr reg = _gen->rlock_byte(T_BYTE); + __ move(res, reg); + + _result = reg; + } +} + + +void LIRItem::load_nonconstant() { + LIR_Opr r = value()->operand(); + if (r->is_constant()) { + _result = r; + } else { + load_item(); + } +} + +//-------------------------------------------------------------- +// LIRGenerator +//-------------------------------------------------------------- + + +LIR_Opr LIRGenerator::exceptionOopOpr() { return FrameMap::rax_oop_opr; } +LIR_Opr LIRGenerator::exceptionPcOpr() { return FrameMap::rdx_opr; } +LIR_Opr LIRGenerator::divInOpr() { return FrameMap::rax_opr; } +LIR_Opr LIRGenerator::divOutOpr() { return FrameMap::rax_opr; } +LIR_Opr LIRGenerator::remOutOpr() { return FrameMap::rdx_opr; } +LIR_Opr LIRGenerator::shiftCountOpr() { return FrameMap::rcx_opr; } +LIR_Opr LIRGenerator::syncTempOpr() { return FrameMap::rax_opr; } +LIR_Opr LIRGenerator::getThreadTemp() { return LIR_OprFact::illegalOpr; } + + +LIR_Opr LIRGenerator::result_register_for(ValueType* type, bool callee) { + LIR_Opr opr; + switch (type->tag()) { + case intTag: opr = FrameMap::rax_opr; break; + case objectTag: opr = FrameMap::rax_oop_opr; break; + case longTag: opr = FrameMap::rax_rdx_long_opr; break; + case floatTag: opr = UseSSE >= 1 ? FrameMap::xmm0_float_opr : FrameMap::fpu0_float_opr; break; + case doubleTag: opr = UseSSE >= 2 ? FrameMap::xmm0_double_opr : FrameMap::fpu0_double_opr; break; + + case addressTag: + default: ShouldNotReachHere(); return LIR_OprFact::illegalOpr; + } + + assert(opr->type_field() == as_OprType(as_BasicType(type)), "type mismatch"); + return opr; +} + + +LIR_Opr LIRGenerator::rlock_byte(BasicType type) { + LIR_Opr reg = new_register(T_INT); + set_vreg_flag(reg, LIRGenerator::byte_reg); + return reg; +} + + +//--------- loading items into registers -------------------------------- + + +// i486 instructions can inline constants +bool LIRGenerator::can_store_as_constant(Value v, BasicType type) const { + if (type == T_SHORT || type == T_CHAR) { + // there is no immediate move of word values in asembler_i486.?pp + return false; + } + Constant* c = v->as_Constant(); + if (c && c->state() == NULL) { + // constants of any type can be stored directly, except for + // unloaded object constants. + return true; + } + return false; +} + + +bool LIRGenerator::can_inline_as_constant(Value v) const { + return v->type()->tag() != objectTag || + (v->type()->is_constant() && v->type()->as_ObjectType()->constant_value()->is_null_object()); +} + + +bool LIRGenerator::can_inline_as_constant(LIR_Const* c) const { + return c->type() != T_OBJECT || c->as_jobject() == NULL; +} + + +LIR_Opr LIRGenerator::safepoint_poll_register() { + return LIR_OprFact::illegalOpr; +} + + +LIR_Address* LIRGenerator::generate_address(LIR_Opr base, LIR_Opr index, + int shift, int disp, BasicType type) { + assert(base->is_register(), "must be"); + if (index->is_constant()) { + return new LIR_Address(base, + (index->as_constant_ptr()->as_jint() << shift) + disp, + type); + } else { + return new LIR_Address(base, index, (LIR_Address::Scale)shift, disp, type); + } +} + + +LIR_Address* LIRGenerator::emit_array_address(LIR_Opr array_opr, LIR_Opr index_opr, + BasicType type, bool needs_card_mark) { + int offset_in_bytes = arrayOopDesc::base_offset_in_bytes(type); + + LIR_Address* addr; + if (index_opr->is_constant()) { + int elem_size = type2aelembytes[type]; + addr = new LIR_Address(array_opr, + offset_in_bytes + index_opr->as_jint() * elem_size, type); + } else { + addr = new LIR_Address(array_opr, + index_opr, + LIR_Address::scale(type), + offset_in_bytes, type); + } + if (needs_card_mark) { + // This store will need a precise card mark, so go ahead and + // compute the full adddres instead of computing once for the + // store and again for the card mark. + LIR_Opr tmp = new_register(T_INT); + __ leal(LIR_OprFact::address(addr), tmp); + return new LIR_Address(tmp, 0, type); + } else { + return addr; + } +} + + +void LIRGenerator::increment_counter(address counter, int step) { + LIR_Opr temp = new_register(T_INT); + LIR_Opr pointer = new_register(T_INT); + __ move(LIR_OprFact::intConst((int)counter), pointer); + LIR_Address* addr = new LIR_Address(pointer, 0, T_INT); + increment_counter(addr, step); +} + + +void LIRGenerator::increment_counter(LIR_Address* addr, int step) { + __ add((LIR_Opr)addr, LIR_OprFact::intConst(step), (LIR_Opr)addr); +} + + +void LIRGenerator::cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int c, CodeEmitInfo* info) { + __ cmp_mem_int(condition, base, disp, c, info); +} + + +void LIRGenerator::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, int disp, BasicType type, CodeEmitInfo* info) { + __ cmp_reg_mem(condition, reg, new LIR_Address(base, disp, type), info); +} + + +void LIRGenerator::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, LIR_Opr disp, BasicType type, CodeEmitInfo* info) { + __ cmp_reg_mem(condition, reg, new LIR_Address(base, disp, type), info); +} + + +bool LIRGenerator::strength_reduce_multiply(LIR_Opr left, int c, LIR_Opr result, LIR_Opr tmp) { + if (tmp->is_valid()) { + if (is_power_of_2(c + 1)) { + __ move(left, tmp); + __ shift_left(left, log2_intptr(c + 1), left); + __ sub(left, tmp, result); + return true; + } else if (is_power_of_2(c - 1)) { + __ move(left, tmp); + __ shift_left(left, log2_intptr(c - 1), left); + __ add(left, tmp, result); + return true; + } + } + return false; +} + + +void LIRGenerator::store_stack_parameter (LIR_Opr item, ByteSize offset_from_sp) { + BasicType type = item->type(); + __ store(item, new LIR_Address(FrameMap::rsp_opr, in_bytes(offset_from_sp), type)); +} + +//---------------------------------------------------------------------- +// visitor functions +//---------------------------------------------------------------------- + + +void LIRGenerator::do_StoreIndexed(StoreIndexed* x) { + assert(x->is_root(),""); + bool needs_range_check = true; + bool use_length = x->length() != NULL; + bool obj_store = x->elt_type() == T_ARRAY || x->elt_type() == T_OBJECT; + bool needs_store_check = obj_store && (x->value()->as_Constant() == NULL || + !get_jobject_constant(x->value())->is_null_object()); + + LIRItem array(x->array(), this); + LIRItem index(x->index(), this); + LIRItem value(x->value(), this); + LIRItem length(this); + + array.load_item(); + index.load_nonconstant(); + + if (use_length) { + needs_range_check = x->compute_needs_range_check(); + if (needs_range_check) { + length.set_instruction(x->length()); + length.load_item(); + } + } + if (needs_store_check) { + value.load_item(); + } else { + value.load_for_store(x->elt_type()); + } + + set_no_result(x); + + // the CodeEmitInfo must be duplicated for each different + // LIR-instruction because spilling can occur anywhere between two + // instructions and so the debug information must be different + CodeEmitInfo* range_check_info = state_for(x); + CodeEmitInfo* null_check_info = NULL; + if (x->needs_null_check()) { + null_check_info = new CodeEmitInfo(range_check_info); + } + + // emit array address setup early so it schedules better + LIR_Address* array_addr = emit_array_address(array.result(), index.result(), x->elt_type(), obj_store); + + if (GenerateRangeChecks && needs_range_check) { + if (use_length) { + __ cmp(lir_cond_belowEqual, length.result(), index.result()); + __ branch(lir_cond_belowEqual, T_INT, new RangeCheckStub(range_check_info, index.result())); + } else { + array_range_check(array.result(), index.result(), null_check_info, range_check_info); + // range_check also does the null check + null_check_info = NULL; + } + } + + if (GenerateArrayStoreCheck && needs_store_check) { + LIR_Opr tmp1 = new_register(objectType); + LIR_Opr tmp2 = new_register(objectType); + LIR_Opr tmp3 = new_register(objectType); + + CodeEmitInfo* store_check_info = new CodeEmitInfo(range_check_info); + __ store_check(value.result(), array.result(), tmp1, tmp2, tmp3, store_check_info); + } + + if (obj_store) { + __ move(value.result(), array_addr, null_check_info); + // Seems to be a precise + post_barrier(LIR_OprFact::address(array_addr), value.result()); + } else { + __ move(value.result(), array_addr, null_check_info); + } +} + + +void LIRGenerator::do_MonitorEnter(MonitorEnter* x) { + assert(x->is_root(),""); + LIRItem obj(x->obj(), this); + obj.load_item(); + + set_no_result(x); + + // "lock" stores the address of the monitor stack slot, so this is not an oop + LIR_Opr lock = new_register(T_INT); + // Need a scratch register for biased locking on x86 + LIR_Opr scratch = LIR_OprFact::illegalOpr; + if (UseBiasedLocking) { + scratch = new_register(T_INT); + } + + CodeEmitInfo* info_for_exception = NULL; + if (x->needs_null_check()) { + info_for_exception = state_for(x, x->lock_stack_before()); + } + // this CodeEmitInfo must not have the xhandlers because here the + // object is already locked (xhandlers expect object to be unlocked) + CodeEmitInfo* info = state_for(x, x->state(), true); + monitor_enter(obj.result(), lock, syncTempOpr(), scratch, + x->monitor_no(), info_for_exception, info); +} + + +void LIRGenerator::do_MonitorExit(MonitorExit* x) { + assert(x->is_root(),""); + + LIRItem obj(x->obj(), this); + obj.dont_load_item(); + + LIR_Opr lock = new_register(T_INT); + LIR_Opr obj_temp = new_register(T_INT); + set_no_result(x); + monitor_exit(obj_temp, lock, syncTempOpr(), x->monitor_no()); +} + + +// _ineg, _lneg, _fneg, _dneg +void LIRGenerator::do_NegateOp(NegateOp* x) { + LIRItem value(x->x(), this); + value.set_destroys_register(); + value.load_item(); + LIR_Opr reg = rlock(x); + __ negate(value.result(), reg); + + set_result(x, round_item(reg)); +} + + +// for _fadd, _fmul, _fsub, _fdiv, _frem +// _dadd, _dmul, _dsub, _ddiv, _drem +void LIRGenerator::do_ArithmeticOp_FPU(ArithmeticOp* x) { + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + LIRItem* left_arg = &left; + LIRItem* right_arg = &right; + assert(!left.is_stack() || !right.is_stack(), "can't both be memory operands"); + bool must_load_both = (x->op() == Bytecodes::_frem || x->op() == Bytecodes::_drem); + if (left.is_register() || x->x()->type()->is_constant() || must_load_both) { + left.load_item(); + } else { + left.dont_load_item(); + } + + // do not load right operand if it is a constant. only 0 and 1 are + // loaded because there are special instructions for loading them + // without memory access (not needed for SSE2 instructions) + bool must_load_right = false; + if (right.is_constant()) { + LIR_Const* c = right.result()->as_constant_ptr(); + assert(c != NULL, "invalid constant"); + assert(c->type() == T_FLOAT || c->type() == T_DOUBLE, "invalid type"); + + if (c->type() == T_FLOAT) { + must_load_right = UseSSE < 1 && (c->is_one_float() || c->is_zero_float()); + } else { + must_load_right = UseSSE < 2 && (c->is_one_double() || c->is_zero_double()); + } + } + + if (must_load_both) { + // frem and drem destroy also right operand, so move it to a new register + right.set_destroys_register(); + right.load_item(); + } else if (right.is_register() || must_load_right) { + right.load_item(); + } else { + right.dont_load_item(); + } + LIR_Opr reg = rlock(x); + LIR_Opr tmp = LIR_OprFact::illegalOpr; + if (x->is_strictfp() && (x->op() == Bytecodes::_dmul || x->op() == Bytecodes::_ddiv)) { + tmp = new_register(T_DOUBLE); + } + + if ((UseSSE >= 1 && x->op() == Bytecodes::_frem) || (UseSSE >= 2 && x->op() == Bytecodes::_drem)) { + // special handling for frem and drem: no SSE instruction, so must use FPU with temporary fpu stack slots + LIR_Opr fpu0, fpu1; + if (x->op() == Bytecodes::_frem) { + fpu0 = LIR_OprFact::single_fpu(0); + fpu1 = LIR_OprFact::single_fpu(1); + } else { + fpu0 = LIR_OprFact::double_fpu(0); + fpu1 = LIR_OprFact::double_fpu(1); + } + __ move(right.result(), fpu1); // order of left and right operand is important! + __ move(left.result(), fpu0); + __ rem (fpu0, fpu1, fpu0); + __ move(fpu0, reg); + + } else { + arithmetic_op_fpu(x->op(), reg, left.result(), right.result(), x->is_strictfp(), tmp); + } + + set_result(x, round_item(reg)); +} + + +// for _ladd, _lmul, _lsub, _ldiv, _lrem +void LIRGenerator::do_ArithmeticOp_Long(ArithmeticOp* x) { + if (x->op() == Bytecodes::_ldiv || x->op() == Bytecodes::_lrem ) { + // long division is implemented as a direct call into the runtime + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + + // the check for division by zero destroys the right operand + right.set_destroys_register(); + + BasicTypeList signature(2); + signature.append(T_LONG); + signature.append(T_LONG); + CallingConvention* cc = frame_map()->c_calling_convention(&signature); + + // check for division by zero (destroys registers of right operand!) + CodeEmitInfo* info = state_for(x); + + const LIR_Opr result_reg = result_register_for(x->type()); + left.load_item_force(cc->at(1)); + right.load_item(); + + __ move(right.result(), cc->at(0)); + + __ cmp(lir_cond_equal, right.result(), LIR_OprFact::longConst(0)); + __ branch(lir_cond_equal, T_LONG, new DivByZeroStub(info)); + + address entry; + switch (x->op()) { + case Bytecodes::_lrem: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::lrem); + break; // check if dividend is 0 is done elsewhere + case Bytecodes::_ldiv: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::ldiv); + break; // check if dividend is 0 is done elsewhere + case Bytecodes::_lmul: + entry = CAST_FROM_FN_PTR(address, SharedRuntime::lmul); + break; + default: + ShouldNotReachHere(); + } + + LIR_Opr result = rlock_result(x); + __ call_runtime_leaf(entry, getThreadTemp(), result_reg, cc->args()); + __ move(result_reg, result); + } else if (x->op() == Bytecodes::_lmul) { + // missing test if instr is commutative and if we should swap + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + + // right register is destroyed by the long mul, so it must be + // copied to a new register. + right.set_destroys_register(); + + left.load_item(); + right.load_item(); + + LIR_Opr reg = FrameMap::rax_rdx_long_opr; + arithmetic_op_long(x->op(), reg, left.result(), right.result(), NULL); + LIR_Opr result = rlock_result(x); + __ move(reg, result); + } else { + // missing test if instr is commutative and if we should swap + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + + left.load_item(); + // dont load constants to save register + right.load_nonconstant(); + rlock_result(x); + arithmetic_op_long(x->op(), x->operand(), left.result(), right.result(), NULL); + } +} + + + +// for: _iadd, _imul, _isub, _idiv, _irem +void LIRGenerator::do_ArithmeticOp_Int(ArithmeticOp* x) { + if (x->op() == Bytecodes::_idiv || x->op() == Bytecodes::_irem) { + // The requirements for division and modulo + // input : rax,: dividend min_int + // reg: divisor (may not be rax,/rdx) -1 + // + // output: rax,: quotient (= rax, idiv reg) min_int + // rdx: remainder (= rax, irem reg) 0 + + // rax, and rdx will be destroyed + + // Note: does this invalidate the spec ??? + LIRItem right(x->y(), this); + LIRItem left(x->x() , this); // visit left second, so that the is_register test is valid + + // call state_for before load_item_force because state_for may + // force the evaluation of other instructions that are needed for + // correct debug info. Otherwise the live range of the fix + // register might be too long. + CodeEmitInfo* info = state_for(x); + + left.load_item_force(divInOpr()); + + right.load_item(); + + LIR_Opr result = rlock_result(x); + LIR_Opr result_reg; + if (x->op() == Bytecodes::_idiv) { + result_reg = divOutOpr(); + } else { + result_reg = remOutOpr(); + } + + if (!ImplicitDiv0Checks) { + __ cmp(lir_cond_equal, right.result(), LIR_OprFact::intConst(0)); + __ branch(lir_cond_equal, T_INT, new DivByZeroStub(info)); + } + LIR_Opr tmp = FrameMap::rdx_opr; // idiv and irem use rdx in their implementation + if (x->op() == Bytecodes::_irem) { + __ irem(left.result(), right.result(), result_reg, tmp, info); + } else if (x->op() == Bytecodes::_idiv) { + __ idiv(left.result(), right.result(), result_reg, tmp, info); + } else { + ShouldNotReachHere(); + } + + __ move(result_reg, result); + } else { + // missing test if instr is commutative and if we should swap + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + LIRItem* left_arg = &left; + LIRItem* right_arg = &right; + if (x->is_commutative() && left.is_stack() && right.is_register()) { + // swap them if left is real stack (or cached) and right is real register(not cached) + left_arg = &right; + right_arg = &left; + } + + left_arg->load_item(); + + // do not need to load right, as we can handle stack and constants + if (x->op() == Bytecodes::_imul ) { + // check if we can use shift instead + bool use_constant = false; + bool use_tmp = false; + if (right_arg->is_constant()) { + int iconst = right_arg->get_jint_constant(); + if (iconst > 0) { + if (is_power_of_2(iconst)) { + use_constant = true; + } else if (is_power_of_2(iconst - 1) || is_power_of_2(iconst + 1)) { + use_constant = true; + use_tmp = true; + } + } + } + if (use_constant) { + right_arg->dont_load_item(); + } else { + right_arg->load_item(); + } + LIR_Opr tmp = LIR_OprFact::illegalOpr; + if (use_tmp) { + tmp = new_register(T_INT); + } + rlock_result(x); + + arithmetic_op_int(x->op(), x->operand(), left_arg->result(), right_arg->result(), tmp); + } else { + right_arg->dont_load_item(); + rlock_result(x); + LIR_Opr tmp = LIR_OprFact::illegalOpr; + arithmetic_op_int(x->op(), x->operand(), left_arg->result(), right_arg->result(), tmp); + } + } +} + + +void LIRGenerator::do_ArithmeticOp(ArithmeticOp* x) { + // when an operand with use count 1 is the left operand, then it is + // likely that no move for 2-operand-LIR-form is necessary + if (x->is_commutative() && x->y()->as_Constant() == NULL && x->x()->use_count() > x->y()->use_count()) { + x->swap_operands(); + } + + ValueTag tag = x->type()->tag(); + assert(x->x()->type()->tag() == tag && x->y()->type()->tag() == tag, "wrong parameters"); + switch (tag) { + case floatTag: + case doubleTag: do_ArithmeticOp_FPU(x); return; + case longTag: do_ArithmeticOp_Long(x); return; + case intTag: do_ArithmeticOp_Int(x); return; + } + ShouldNotReachHere(); +} + + +// _ishl, _lshl, _ishr, _lshr, _iushr, _lushr +void LIRGenerator::do_ShiftOp(ShiftOp* x) { + // count must always be in rcx + LIRItem value(x->x(), this); + LIRItem count(x->y(), this); + + ValueTag elemType = x->type()->tag(); + bool must_load_count = !count.is_constant() || elemType == longTag; + if (must_load_count) { + // count for long must be in register + count.load_item_force(shiftCountOpr()); + } else { + count.dont_load_item(); + } + value.load_item(); + LIR_Opr reg = rlock_result(x); + + shift_op(x->op(), reg, value.result(), count.result(), LIR_OprFact::illegalOpr); +} + + +// _iand, _land, _ior, _lor, _ixor, _lxor +void LIRGenerator::do_LogicOp(LogicOp* x) { + // when an operand with use count 1 is the left operand, then it is + // likely that no move for 2-operand-LIR-form is necessary + if (x->is_commutative() && x->y()->as_Constant() == NULL && x->x()->use_count() > x->y()->use_count()) { + x->swap_operands(); + } + + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + + left.load_item(); + right.load_nonconstant(); + LIR_Opr reg = rlock_result(x); + + logic_op(x->op(), reg, left.result(), right.result()); +} + + + +// _lcmp, _fcmpl, _fcmpg, _dcmpl, _dcmpg +void LIRGenerator::do_CompareOp(CompareOp* x) { + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + ValueTag tag = x->x()->type()->tag(); + if (tag == longTag) { + left.set_destroys_register(); + } + left.load_item(); + right.load_item(); + LIR_Opr reg = rlock_result(x); + + if (x->x()->type()->is_float_kind()) { + Bytecodes::Code code = x->op(); + __ fcmp2int(left.result(), right.result(), reg, (code == Bytecodes::_fcmpl || code == Bytecodes::_dcmpl)); + } else if (x->x()->type()->tag() == longTag) { + __ lcmp2int(left.result(), right.result(), reg); + } else { + Unimplemented(); + } +} + + +void LIRGenerator::do_AttemptUpdate(Intrinsic* x) { + assert(x->number_of_arguments() == 3, "wrong type"); + LIRItem obj (x->argument_at(0), this); // AtomicLong object + LIRItem cmp_value (x->argument_at(1), this); // value to compare with field + LIRItem new_value (x->argument_at(2), this); // replace field with new_value if it matches cmp_value + + // compare value must be in rdx,eax (hi,lo); may be destroyed by cmpxchg8 instruction + cmp_value.load_item_force(FrameMap::rax_rdx_long_opr); + + // new value must be in rcx,ebx (hi,lo) + new_value.load_item_force(FrameMap::rbx_rcx_long_opr); + + // object pointer register is overwritten with field address + obj.load_item(); + + // generate compare-and-swap; produces zero condition if swap occurs + int value_offset = sun_misc_AtomicLongCSImpl::value_offset(); + LIR_Opr addr = obj.result(); + __ add(addr, LIR_OprFact::intConst(value_offset), addr); + LIR_Opr t1 = LIR_OprFact::illegalOpr; // no temp needed + LIR_Opr t2 = LIR_OprFact::illegalOpr; // no temp needed + __ cas_long(addr, cmp_value.result(), new_value.result(), t1, t2); + + // generate conditional move of boolean result + LIR_Opr result = rlock_result(x); + __ cmove(lir_cond_equal, LIR_OprFact::intConst(1), LIR_OprFact::intConst(0), result); +} + + +void LIRGenerator::do_CompareAndSwap(Intrinsic* x, ValueType* type) { + assert(x->number_of_arguments() == 4, "wrong type"); + LIRItem obj (x->argument_at(0), this); // object + LIRItem offset(x->argument_at(1), this); // offset of field + LIRItem cmp (x->argument_at(2), this); // value to compare with field + LIRItem val (x->argument_at(3), this); // replace field with val if matches cmp + + assert(obj.type()->tag() == objectTag, "invalid type"); + assert(offset.type()->tag() == intTag, "invalid type"); + assert(cmp.type()->tag() == type->tag(), "invalid type"); + assert(val.type()->tag() == type->tag(), "invalid type"); + + // get address of field + obj.load_item(); + offset.load_nonconstant(); + + if (type == objectType) { + cmp.load_item_force(FrameMap::rax_oop_opr); + val.load_item(); + } else if (type == intType) { + cmp.load_item_force(FrameMap::rax_opr); + val.load_item(); + } else if (type == longType) { + cmp.load_item_force(FrameMap::rax_rdx_long_opr); + val.load_item_force(FrameMap::rbx_rcx_long_opr); + } else { + ShouldNotReachHere(); + } + + LIR_Opr addr = new_pointer_register(); + __ move(obj.result(), addr); + __ add(addr, offset.result(), addr); + + + + LIR_Opr ill = LIR_OprFact::illegalOpr; // for convenience + if (type == objectType) + __ cas_obj(addr, cmp.result(), val.result(), ill, ill); + else if (type == intType) + __ cas_int(addr, cmp.result(), val.result(), ill, ill); + else if (type == longType) + __ cas_long(addr, cmp.result(), val.result(), ill, ill); + else { + ShouldNotReachHere(); + } + + // generate conditional move of boolean result + LIR_Opr result = rlock_result(x); + __ cmove(lir_cond_equal, LIR_OprFact::intConst(1), LIR_OprFact::intConst(0), result); + if (type == objectType) { // Write-barrier needed for Object fields. + // Seems to be precise + post_barrier(addr, val.result()); + } +} + + +void LIRGenerator::do_MathIntrinsic(Intrinsic* x) { + assert(x->number_of_arguments() == 1, "wrong type"); + LIRItem value(x->argument_at(0), this); + + bool use_fpu = false; + if (UseSSE >= 2) { + switch(x->id()) { + case vmIntrinsics::_dsin: + case vmIntrinsics::_dcos: + case vmIntrinsics::_dtan: + case vmIntrinsics::_dlog: + case vmIntrinsics::_dlog10: + use_fpu = true; + } + } else { + value.set_destroys_register(); + } + + value.load_item(); + + LIR_Opr calc_input = value.result(); + LIR_Opr calc_result = rlock_result(x); + + // sin and cos need two free fpu stack slots, so register two temporary operands + LIR_Opr tmp1 = FrameMap::caller_save_fpu_reg_at(0); + LIR_Opr tmp2 = FrameMap::caller_save_fpu_reg_at(1); + + if (use_fpu) { + LIR_Opr tmp = FrameMap::fpu0_double_opr; + __ move(calc_input, tmp); + + calc_input = tmp; + calc_result = tmp; + tmp1 = FrameMap::caller_save_fpu_reg_at(1); + tmp2 = FrameMap::caller_save_fpu_reg_at(2); + } + + switch(x->id()) { + case vmIntrinsics::_dabs: __ abs (calc_input, calc_result, LIR_OprFact::illegalOpr); break; + case vmIntrinsics::_dsqrt: __ sqrt (calc_input, calc_result, LIR_OprFact::illegalOpr); break; + case vmIntrinsics::_dsin: __ sin (calc_input, calc_result, tmp1, tmp2); break; + case vmIntrinsics::_dcos: __ cos (calc_input, calc_result, tmp1, tmp2); break; + case vmIntrinsics::_dtan: __ tan (calc_input, calc_result, tmp1, tmp2); break; + case vmIntrinsics::_dlog: __ log (calc_input, calc_result, LIR_OprFact::illegalOpr); break; + case vmIntrinsics::_dlog10: __ log10(calc_input, calc_result, LIR_OprFact::illegalOpr); break; + default: ShouldNotReachHere(); + } + + if (use_fpu) { + __ move(calc_result, x->operand()); + } +} + + +void LIRGenerator::do_ArrayCopy(Intrinsic* x) { + assert(x->number_of_arguments() == 5, "wrong type"); + LIRItem src(x->argument_at(0), this); + LIRItem src_pos(x->argument_at(1), this); + LIRItem dst(x->argument_at(2), this); + LIRItem dst_pos(x->argument_at(3), this); + LIRItem length(x->argument_at(4), this); + + // operands for arraycopy must use fixed registers, otherwise + // LinearScan will fail allocation (because arraycopy always needs a + // call) + src.load_item_force (FrameMap::rcx_oop_opr); + src_pos.load_item_force (FrameMap::rdx_opr); + dst.load_item_force (FrameMap::rax_oop_opr); + dst_pos.load_item_force (FrameMap::rbx_opr); + length.load_item_force (FrameMap::rdi_opr); + LIR_Opr tmp = (FrameMap::rsi_opr); + set_no_result(x); + + int flags; + ciArrayKlass* expected_type; + arraycopy_helper(x, &flags, &expected_type); + + CodeEmitInfo* info = state_for(x, x->state()); // we may want to have stack (deoptimization?) + __ arraycopy(src.result(), src_pos.result(), dst.result(), dst_pos.result(), length.result(), tmp, expected_type, flags, info); // does add_safepoint +} + + +// _i2l, _i2f, _i2d, _l2i, _l2f, _l2d, _f2i, _f2l, _f2d, _d2i, _d2l, _d2f +// _i2b, _i2c, _i2s +LIR_Opr fixed_register_for(BasicType type) { + switch (type) { + case T_FLOAT: return FrameMap::fpu0_float_opr; + case T_DOUBLE: return FrameMap::fpu0_double_opr; + case T_INT: return FrameMap::rax_opr; + case T_LONG: return FrameMap::rax_rdx_long_opr; + default: ShouldNotReachHere(); return LIR_OprFact::illegalOpr; + } +} + +void LIRGenerator::do_Convert(Convert* x) { + // flags that vary for the different operations and different SSE-settings + bool fixed_input, fixed_result, round_result, needs_stub; + + switch (x->op()) { + case Bytecodes::_i2l: // fall through + case Bytecodes::_l2i: // fall through + case Bytecodes::_i2b: // fall through + case Bytecodes::_i2c: // fall through + case Bytecodes::_i2s: fixed_input = false; fixed_result = false; round_result = false; needs_stub = false; break; + + case Bytecodes::_f2d: fixed_input = UseSSE == 1; fixed_result = false; round_result = false; needs_stub = false; break; + case Bytecodes::_d2f: fixed_input = false; fixed_result = UseSSE == 1; round_result = UseSSE < 1; needs_stub = false; break; + case Bytecodes::_i2f: fixed_input = false; fixed_result = false; round_result = UseSSE < 1; needs_stub = false; break; + case Bytecodes::_i2d: fixed_input = false; fixed_result = false; round_result = false; needs_stub = false; break; + case Bytecodes::_f2i: fixed_input = false; fixed_result = false; round_result = false; needs_stub = true; break; + case Bytecodes::_d2i: fixed_input = false; fixed_result = false; round_result = false; needs_stub = true; break; + case Bytecodes::_l2f: fixed_input = false; fixed_result = UseSSE >= 1; round_result = UseSSE < 1; needs_stub = false; break; + case Bytecodes::_l2d: fixed_input = false; fixed_result = UseSSE >= 2; round_result = UseSSE < 2; needs_stub = false; break; + case Bytecodes::_f2l: fixed_input = true; fixed_result = true; round_result = false; needs_stub = false; break; + case Bytecodes::_d2l: fixed_input = true; fixed_result = true; round_result = false; needs_stub = false; break; + default: ShouldNotReachHere(); + } + + LIRItem value(x->value(), this); + value.load_item(); + LIR_Opr input = value.result(); + LIR_Opr result = rlock(x); + + // arguments of lir_convert + LIR_Opr conv_input = input; + LIR_Opr conv_result = result; + ConversionStub* stub = NULL; + + if (fixed_input) { + conv_input = fixed_register_for(input->type()); + __ move(input, conv_input); + } + + assert(fixed_result == false || round_result == false, "cannot set both"); + if (fixed_result) { + conv_result = fixed_register_for(result->type()); + } else if (round_result) { + result = new_register(result->type()); + set_vreg_flag(result, must_start_in_memory); + } + + if (needs_stub) { + stub = new ConversionStub(x->op(), conv_input, conv_result); + } + + __ convert(x->op(), conv_input, conv_result, stub); + + if (result != conv_result) { + __ move(conv_result, result); + } + + assert(result->is_virtual(), "result must be virtual register"); + set_result(x, result); +} + + +void LIRGenerator::do_NewInstance(NewInstance* x) { + if (PrintNotLoaded && !x->klass()->is_loaded()) { + tty->print_cr(" ###class not loaded at new bci %d", x->bci()); + } + CodeEmitInfo* info = state_for(x, x->state()); + LIR_Opr reg = result_register_for(x->type()); + LIR_Opr klass_reg = new_register(objectType); + new_instance(reg, x->klass(), + FrameMap::rcx_oop_opr, + FrameMap::rdi_oop_opr, + FrameMap::rsi_oop_opr, + LIR_OprFact::illegalOpr, + FrameMap::rdx_oop_opr, info); + LIR_Opr result = rlock_result(x); + __ move(reg, result); +} + + +void LIRGenerator::do_NewTypeArray(NewTypeArray* x) { + CodeEmitInfo* info = state_for(x, x->state()); + + LIRItem length(x->length(), this); + length.load_item_force(FrameMap::rbx_opr); + + LIR_Opr reg = result_register_for(x->type()); + LIR_Opr tmp1 = FrameMap::rcx_oop_opr; + LIR_Opr tmp2 = FrameMap::rsi_oop_opr; + LIR_Opr tmp3 = FrameMap::rdi_oop_opr; + LIR_Opr tmp4 = reg; + LIR_Opr klass_reg = FrameMap::rdx_oop_opr; + LIR_Opr len = length.result(); + BasicType elem_type = x->elt_type(); + + __ oop2reg(ciTypeArrayKlass::make(elem_type)->encoding(), klass_reg); + + CodeStub* slow_path = new NewTypeArrayStub(klass_reg, len, reg, info); + __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, elem_type, klass_reg, slow_path); + + LIR_Opr result = rlock_result(x); + __ move(reg, result); +} + + +void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { + LIRItem length(x->length(), this); + // in case of patching (i.e., object class is not yet loaded), we need to reexecute the instruction + // and therefore provide the state before the parameters have been consumed + CodeEmitInfo* patching_info = NULL; + if (!x->klass()->is_loaded() || PatchALot) { + patching_info = state_for(x, x->state_before()); + } + + CodeEmitInfo* info = state_for(x, x->state()); + + const LIR_Opr reg = result_register_for(x->type()); + LIR_Opr tmp1 = FrameMap::rcx_oop_opr; + LIR_Opr tmp2 = FrameMap::rsi_oop_opr; + LIR_Opr tmp3 = FrameMap::rdi_oop_opr; + LIR_Opr tmp4 = reg; + LIR_Opr klass_reg = FrameMap::rdx_oop_opr; + + length.load_item_force(FrameMap::rbx_opr); + LIR_Opr len = length.result(); + + CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info); + ciObject* obj = (ciObject*) ciObjArrayKlass::make(x->klass()); + if (obj == ciEnv::unloaded_ciobjarrayklass()) { + BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error"); + } + jobject2reg_with_patching(klass_reg, obj, patching_info); + __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, T_OBJECT, klass_reg, slow_path); + + LIR_Opr result = rlock_result(x); + __ move(reg, result); +} + + +void LIRGenerator::do_NewMultiArray(NewMultiArray* x) { + Values* dims = x->dims(); + int i = dims->length(); + LIRItemList* items = new LIRItemList(dims->length(), NULL); + while (i-- > 0) { + LIRItem* size = new LIRItem(dims->at(i), this); + items->at_put(i, size); + } + + // need to get the info before, as the items may become invalid through item_free + CodeEmitInfo* patching_info = NULL; + if (!x->klass()->is_loaded() || PatchALot) { + patching_info = state_for(x, x->state_before()); + + // cannot re-use same xhandlers for multiple CodeEmitInfos, so + // clone all handlers. + x->set_exception_handlers(new XHandlers(x->exception_handlers())); + } + + CodeEmitInfo* info = state_for(x, x->state()); + + i = dims->length(); + while (i-- > 0) { + LIRItem* size = items->at(i); + size->load_nonconstant(); + + store_stack_parameter(size->result(), in_ByteSize(i*4)); + } + + LIR_Opr reg = result_register_for(x->type()); + jobject2reg_with_patching(reg, x->klass(), patching_info); + + LIR_Opr rank = FrameMap::rbx_opr; + __ move(LIR_OprFact::intConst(x->rank()), rank); + LIR_Opr varargs = FrameMap::rcx_opr; + __ move(FrameMap::rsp_opr, varargs); + LIR_OprList* args = new LIR_OprList(3); + args->append(reg); + args->append(rank); + args->append(varargs); + __ call_runtime(Runtime1::entry_for(Runtime1::new_multi_array_id), + LIR_OprFact::illegalOpr, + reg, args, info); + + LIR_Opr result = rlock_result(x); + __ move(reg, result); +} + + +void LIRGenerator::do_BlockBegin(BlockBegin* x) { + // nothing to do for now +} + + +void LIRGenerator::do_CheckCast(CheckCast* x) { + LIRItem obj(x->obj(), this); + + CodeEmitInfo* patching_info = NULL; + if (!x->klass()->is_loaded() || (PatchALot && !x->is_incompatible_class_change_check())) { + // must do this before locking the destination register as an oop register, + // and before the obj is loaded (the latter is for deoptimization) + patching_info = state_for(x, x->state_before()); + } + obj.load_item(); + + // info for exceptions + CodeEmitInfo* info_for_exception = state_for(x, x->state()->copy_locks()); + + CodeStub* stub; + if (x->is_incompatible_class_change_check()) { + assert(patching_info == NULL, "can't patch this"); + stub = new SimpleExceptionStub(Runtime1::throw_incompatible_class_change_error_id, LIR_OprFact::illegalOpr, info_for_exception); + } else { + stub = new SimpleExceptionStub(Runtime1::throw_class_cast_exception_id, obj.result(), info_for_exception); + } + LIR_Opr reg = rlock_result(x); + __ checkcast(reg, obj.result(), x->klass(), + new_register(objectType), new_register(objectType), + !x->klass()->is_loaded() ? new_register(objectType) : LIR_OprFact::illegalOpr, + x->direct_compare(), info_for_exception, patching_info, stub, + x->profiled_method(), x->profiled_bci()); +} + + +void LIRGenerator::do_InstanceOf(InstanceOf* x) { + LIRItem obj(x->obj(), this); + + // result and test object may not be in same register + LIR_Opr reg = rlock_result(x); + CodeEmitInfo* patching_info = NULL; + if ((!x->klass()->is_loaded() || PatchALot)) { + // must do this before locking the destination register as an oop register + patching_info = state_for(x, x->state_before()); + } + obj.load_item(); + LIR_Opr tmp = new_register(objectType); + __ instanceof(reg, obj.result(), x->klass(), + tmp, new_register(objectType), LIR_OprFact::illegalOpr, + x->direct_compare(), patching_info); +} + + +void LIRGenerator::do_If(If* x) { + assert(x->number_of_sux() == 2, "inconsistency"); + ValueTag tag = x->x()->type()->tag(); + bool is_safepoint = x->is_safepoint(); + + If::Condition cond = x->cond(); + + LIRItem xitem(x->x(), this); + LIRItem yitem(x->y(), this); + LIRItem* xin = &xitem; + LIRItem* yin = &yitem; + + if (tag == longTag) { + // for longs, only conditions "eql", "neq", "lss", "geq" are valid; + // mirror for other conditions + if (cond == If::gtr || cond == If::leq) { + cond = Instruction::mirror(cond); + xin = &yitem; + yin = &xitem; + } + xin->set_destroys_register(); + } + xin->load_item(); + if (tag == longTag && yin->is_constant() && yin->get_jlong_constant() == 0 && (cond == If::eql || cond == If::neq)) { + // inline long zero + yin->dont_load_item(); + } else if (tag == longTag || tag == floatTag || tag == doubleTag) { + // longs cannot handle constants at right side + yin->load_item(); + } else { + yin->dont_load_item(); + } + + // add safepoint before generating condition code so it can be recomputed + if (x->is_safepoint()) { + // increment backedge counter if needed + increment_backedge_counter(state_for(x, x->state_before())); + + __ safepoint(LIR_OprFact::illegalOpr, state_for(x, x->state_before())); + } + set_no_result(x); + + LIR_Opr left = xin->result(); + LIR_Opr right = yin->result(); + __ cmp(lir_cond(cond), left, right); + profile_branch(x, cond); + move_to_phi(x->state()); + if (x->x()->type()->is_float_kind()) { + __ branch(lir_cond(cond), right->type(), x->tsux(), x->usux()); + } else { + __ branch(lir_cond(cond), right->type(), x->tsux()); + } + assert(x->default_sux() == x->fsux(), "wrong destination above"); + __ jump(x->default_sux()); +} + + +LIR_Opr LIRGenerator::getThreadPointer() { + LIR_Opr result = new_register(T_INT); + __ get_thread(result); + return result; +} + +void LIRGenerator::trace_block_entry(BlockBegin* block) { + store_stack_parameter(LIR_OprFact::intConst(block->block_id()), in_ByteSize(0)); + LIR_OprList* args = new LIR_OprList(); + address func = CAST_FROM_FN_PTR(address, Runtime1::trace_block_entry); + __ call_runtime_leaf(func, LIR_OprFact::illegalOpr, LIR_OprFact::illegalOpr, args); +} + + +void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, + CodeEmitInfo* info) { + if (address->type() == T_LONG) { + address = new LIR_Address(address->base(), + address->index(), address->scale(), + address->disp(), T_DOUBLE); + // Transfer the value atomically by using FP moves. This means + // the value has to be moved between CPU and FPU registers. It + // always has to be moved through spill slot since there's no + // quick way to pack the value into an SSE register. + LIR_Opr temp_double = new_register(T_DOUBLE); + LIR_Opr spill = new_register(T_LONG); + set_vreg_flag(spill, must_start_in_memory); + __ move(value, spill); + __ volatile_move(spill, temp_double, T_LONG); + __ volatile_move(temp_double, LIR_OprFact::address(address), T_LONG, info); + } else { + __ store(value, address, info); + } +} + + + +void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, + CodeEmitInfo* info) { + if (address->type() == T_LONG) { + address = new LIR_Address(address->base(), + address->index(), address->scale(), + address->disp(), T_DOUBLE); + // Transfer the value atomically by using FP moves. This means + // the value has to be moved between CPU and FPU registers. In + // SSE0 and SSE1 mode it has to be moved through spill slot but in + // SSE2+ mode it can be moved directly. + LIR_Opr temp_double = new_register(T_DOUBLE); + __ volatile_move(LIR_OprFact::address(address), temp_double, T_LONG, info); + __ volatile_move(temp_double, result, T_LONG); + if (UseSSE < 2) { + // no spill slot needed in SSE2 mode because xmm->cpu register move is possible + set_vreg_flag(result, must_start_in_memory); + } + } else { + __ load(address, result, info); + } +} + +void LIRGenerator::get_Object_unsafe(LIR_Opr dst, LIR_Opr src, LIR_Opr offset, + BasicType type, bool is_volatile) { + if (is_volatile && type == T_LONG) { + LIR_Address* addr = new LIR_Address(src, offset, T_DOUBLE); + LIR_Opr tmp = new_register(T_DOUBLE); + __ load(addr, tmp); + LIR_Opr spill = new_register(T_LONG); + set_vreg_flag(spill, must_start_in_memory); + __ move(tmp, spill); + __ move(spill, dst); + } else { + LIR_Address* addr = new LIR_Address(src, offset, type); + __ load(addr, dst); + } +} + + +void LIRGenerator::put_Object_unsafe(LIR_Opr src, LIR_Opr offset, LIR_Opr data, + BasicType type, bool is_volatile) { + if (is_volatile && type == T_LONG) { + LIR_Address* addr = new LIR_Address(src, offset, T_DOUBLE); + LIR_Opr tmp = new_register(T_DOUBLE); + LIR_Opr spill = new_register(T_DOUBLE); + set_vreg_flag(spill, must_start_in_memory); + __ move(data, spill); + __ move(spill, tmp); + __ move(tmp, addr); + } else { + LIR_Address* addr = new LIR_Address(src, offset, type); + bool is_obj = (type == T_ARRAY || type == T_OBJECT); + if (is_obj) { + __ move(data, addr); + assert(src->is_register(), "must be register"); + // Seems to be a precise address + post_barrier(LIR_OprFact::address(addr), data); + } else { + __ move(data, addr); + } + } +} diff --git a/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.cpp new file mode 100644 index 00000000000..d0b5fa39774 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.cpp @@ -0,0 +1,1168 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_LinearScan_x86.cpp.incl" + + +//---------------------------------------------------------------------- +// Allocation of FPU stack slots (Intel x86 only) +//---------------------------------------------------------------------- + +void LinearScan::allocate_fpu_stack() { + // First compute which FPU registers are live at the start of each basic block + // (To minimize the amount of work we have to do if we have to merge FPU stacks) + if (ComputeExactFPURegisterUsage) { + Interval* intervals_in_register, *intervals_in_memory; + create_unhandled_lists(&intervals_in_register, &intervals_in_memory, is_in_fpu_register, NULL); + + // ignore memory intervals by overwriting intervals_in_memory + // the dummy interval is needed to enforce the walker to walk until the given id: + // without it, the walker stops when the unhandled-list is empty -> live information + // beyond this point would be incorrect. + Interval* dummy_interval = new Interval(any_reg); + dummy_interval->add_range(max_jint - 2, max_jint - 1); + dummy_interval->set_next(Interval::end()); + intervals_in_memory = dummy_interval; + + IntervalWalker iw(this, intervals_in_register, intervals_in_memory); + + const int num_blocks = block_count(); + for (int i = 0; i < num_blocks; i++) { + BlockBegin* b = block_at(i); + + // register usage is only needed for merging stacks -> compute only + // when more than one predecessor. + // the block must not have any spill moves at the beginning (checked by assertions) + // spill moves would use intervals that are marked as handled and so the usage bit + // would been set incorrectly + + // NOTE: the check for number_of_preds > 1 is necessary. A block with only one + // predecessor may have spill moves at the begin of the block. + // If an interval ends at the current instruction id, it is not possible + // to decide if the register is live or not at the block begin -> the + // register information would be incorrect. + if (b->number_of_preds() > 1) { + int id = b->first_lir_instruction_id(); + BitMap regs(FrameMap::nof_fpu_regs); + regs.clear(); + + iw.walk_to(id); // walk after the first instruction (always a label) of the block + assert(iw.current_position() == id, "did not walk completely to id"); + + // Only consider FPU values in registers + Interval* interval = iw.active_first(fixedKind); + while (interval != Interval::end()) { + int reg = interval->assigned_reg(); + assert(reg >= pd_first_fpu_reg && reg <= pd_last_fpu_reg, "no fpu register"); + assert(interval->assigned_regHi() == -1, "must not have hi register (doubles stored in one register)"); + assert(interval->from() <= id && id < interval->to(), "interval out of range"); + +#ifndef PRODUCT + if (TraceFPURegisterUsage) { + tty->print("fpu reg %d is live because of ", reg - pd_first_fpu_reg); interval->print(); + } +#endif + + regs.set_bit(reg - pd_first_fpu_reg); + interval = interval->next(); + } + + b->set_fpu_register_usage(regs); + +#ifndef PRODUCT + if (TraceFPURegisterUsage) { + tty->print("FPU regs for block %d, LIR instr %d): ", b->block_id(), id); regs.print_on(tty); tty->print_cr(""); + } +#endif + } + } + } + + FpuStackAllocator alloc(ir()->compilation(), this); + _fpu_stack_allocator = &alloc; + alloc.allocate(); + _fpu_stack_allocator = NULL; +} + + +FpuStackAllocator::FpuStackAllocator(Compilation* compilation, LinearScan* allocator) + : _compilation(compilation) + , _lir(NULL) + , _pos(-1) + , _allocator(allocator) + , _sim(compilation) + , _temp_sim(compilation) +{} + +void FpuStackAllocator::allocate() { + int num_blocks = allocator()->block_count(); + for (int i = 0; i < num_blocks; i++) { + // Set up to process block + BlockBegin* block = allocator()->block_at(i); + intArray* fpu_stack_state = block->fpu_stack_state(); + +#ifndef PRODUCT + if (TraceFPUStack) { + tty->cr(); + tty->print_cr("------- Begin of new Block %d -------", block->block_id()); + } +#endif + + assert(fpu_stack_state != NULL || + block->end()->as_Base() != NULL || + block->is_set(BlockBegin::exception_entry_flag), + "FPU stack state must be present due to linear-scan order for FPU stack allocation"); + // note: exception handler entries always start with an empty fpu stack + // because stack merging would be too complicated + + if (fpu_stack_state != NULL) { + sim()->read_state(fpu_stack_state); + } else { + sim()->clear(); + } + +#ifndef PRODUCT + if (TraceFPUStack) { + tty->print("Reading FPU state for block %d:", block->block_id()); + sim()->print(); + tty->cr(); + } +#endif + + allocate_block(block); + CHECK_BAILOUT(); + } +} + +void FpuStackAllocator::allocate_block(BlockBegin* block) { + bool processed_merge = false; + LIR_OpList* insts = block->lir()->instructions_list(); + set_lir(block->lir()); + set_pos(0); + + + // Note: insts->length() may change during loop + while (pos() < insts->length()) { + LIR_Op* op = insts->at(pos()); + _debug_information_computed = false; + +#ifndef PRODUCT + if (TraceFPUStack) { + op->print(); + } + check_invalid_lir_op(op); +#endif + + LIR_OpBranch* branch = op->as_OpBranch(); + LIR_Op1* op1 = op->as_Op1(); + LIR_Op2* op2 = op->as_Op2(); + LIR_OpCall* opCall = op->as_OpCall(); + + if (branch != NULL && branch->block() != NULL) { + if (!processed_merge) { + // propagate stack at first branch to a successor + processed_merge = true; + bool required_merge = merge_fpu_stack_with_successors(block); + + assert(!required_merge || branch->cond() == lir_cond_always, "splitting of critical edges should prevent FPU stack mismatches at cond branches"); + } + + } else if (op1 != NULL) { + handle_op1(op1); + } else if (op2 != NULL) { + handle_op2(op2); + } else if (opCall != NULL) { + handle_opCall(opCall); + } + + compute_debug_information(op); + + set_pos(1 + pos()); + } + + // Propagate stack when block does not end with branch + if (!processed_merge) { + merge_fpu_stack_with_successors(block); + } +} + + +void FpuStackAllocator::compute_debug_information(LIR_Op* op) { + if (!_debug_information_computed && op->id() != -1 && allocator()->has_info(op->id())) { + visitor.visit(op); + + // exception handling + if (allocator()->compilation()->has_exception_handlers()) { + XHandlers* xhandlers = visitor.all_xhandler(); + int n = xhandlers->length(); + for (int k = 0; k < n; k++) { + allocate_exception_handler(xhandlers->handler_at(k)); + } + } else { + assert(visitor.all_xhandler()->length() == 0, "missed exception handler"); + } + + // compute debug information + int n = visitor.info_count(); + assert(n > 0, "should not visit operation otherwise"); + + for (int j = 0; j < n; j++) { + CodeEmitInfo* info = visitor.info_at(j); + // Compute debug information + allocator()->compute_debug_info(info, op->id()); + } + } + _debug_information_computed = true; +} + +void FpuStackAllocator::allocate_exception_handler(XHandler* xhandler) { + if (!sim()->is_empty()) { + LIR_List* old_lir = lir(); + int old_pos = pos(); + intArray* old_state = sim()->write_state(); + +#ifndef PRODUCT + if (TraceFPUStack) { + tty->cr(); + tty->print_cr("------- begin of exception handler -------"); + } +#endif + + if (xhandler->entry_code() == NULL) { + // need entry code to clear FPU stack + LIR_List* entry_code = new LIR_List(_compilation); + entry_code->jump(xhandler->entry_block()); + xhandler->set_entry_code(entry_code); + } + + LIR_OpList* insts = xhandler->entry_code()->instructions_list(); + set_lir(xhandler->entry_code()); + set_pos(0); + + // Note: insts->length() may change during loop + while (pos() < insts->length()) { + LIR_Op* op = insts->at(pos()); + +#ifndef PRODUCT + if (TraceFPUStack) { + op->print(); + } + check_invalid_lir_op(op); +#endif + + switch (op->code()) { + case lir_move: + assert(op->as_Op1() != NULL, "must be LIR_Op1"); + assert(pos() != insts->length() - 1, "must not be last operation"); + + handle_op1((LIR_Op1*)op); + break; + + case lir_branch: + assert(op->as_OpBranch()->cond() == lir_cond_always, "must be unconditional branch"); + assert(pos() == insts->length() - 1, "must be last operation"); + + // remove all remaining dead registers from FPU stack + clear_fpu_stack(LIR_OprFact::illegalOpr); + break; + + default: + // other operations not allowed in exception entry code + ShouldNotReachHere(); + } + + set_pos(pos() + 1); + } + +#ifndef PRODUCT + if (TraceFPUStack) { + tty->cr(); + tty->print_cr("------- end of exception handler -------"); + } +#endif + + set_lir(old_lir); + set_pos(old_pos); + sim()->read_state(old_state); + } +} + + +int FpuStackAllocator::fpu_num(LIR_Opr opr) { + assert(opr->is_fpu_register() && !opr->is_xmm_register(), "shouldn't call this otherwise"); + return opr->is_single_fpu() ? opr->fpu_regnr() : opr->fpu_regnrLo(); +} + +int FpuStackAllocator::tos_offset(LIR_Opr opr) { + return sim()->offset_from_tos(fpu_num(opr)); +} + + +LIR_Opr FpuStackAllocator::to_fpu_stack(LIR_Opr opr) { + assert(opr->is_fpu_register() && !opr->is_xmm_register(), "shouldn't call this otherwise"); + + int stack_offset = tos_offset(opr); + if (opr->is_single_fpu()) { + return LIR_OprFact::single_fpu(stack_offset)->make_fpu_stack_offset(); + } else { + assert(opr->is_double_fpu(), "shouldn't call this otherwise"); + return LIR_OprFact::double_fpu(stack_offset)->make_fpu_stack_offset(); + } +} + +LIR_Opr FpuStackAllocator::to_fpu_stack_top(LIR_Opr opr, bool dont_check_offset) { + assert(opr->is_fpu_register() && !opr->is_xmm_register(), "shouldn't call this otherwise"); + assert(dont_check_offset || tos_offset(opr) == 0, "operand is not on stack top"); + + int stack_offset = 0; + if (opr->is_single_fpu()) { + return LIR_OprFact::single_fpu(stack_offset)->make_fpu_stack_offset(); + } else { + assert(opr->is_double_fpu(), "shouldn't call this otherwise"); + return LIR_OprFact::double_fpu(stack_offset)->make_fpu_stack_offset(); + } +} + + + +void FpuStackAllocator::insert_op(LIR_Op* op) { + lir()->insert_before(pos(), op); + set_pos(1 + pos()); +} + + +void FpuStackAllocator::insert_exchange(int offset) { + if (offset > 0) { + LIR_Op1* fxch_op = new LIR_Op1(lir_fxch, LIR_OprFact::intConst(offset), LIR_OprFact::illegalOpr); + insert_op(fxch_op); + sim()->swap(offset); + +#ifndef PRODUCT + if (TraceFPUStack) { + tty->print("Exchanged register: %d New state: ", sim()->get_slot(0)); sim()->print(); tty->cr(); + } +#endif + + } +} + +void FpuStackAllocator::insert_exchange(LIR_Opr opr) { + insert_exchange(tos_offset(opr)); +} + + +void FpuStackAllocator::insert_free(int offset) { + // move stack slot to the top of stack and then pop it + insert_exchange(offset); + + LIR_Op* fpop = new LIR_Op0(lir_fpop_raw); + insert_op(fpop); + sim()->pop(); + +#ifndef PRODUCT + if (TraceFPUStack) { + tty->print("Inserted pop New state: "); sim()->print(); tty->cr(); + } +#endif +} + + +void FpuStackAllocator::insert_free_if_dead(LIR_Opr opr) { + if (sim()->contains(fpu_num(opr))) { + int res_slot = tos_offset(opr); + insert_free(res_slot); + } +} + +void FpuStackAllocator::insert_free_if_dead(LIR_Opr opr, LIR_Opr ignore) { + if (fpu_num(opr) != fpu_num(ignore) && sim()->contains(fpu_num(opr))) { + int res_slot = tos_offset(opr); + insert_free(res_slot); + } +} + +void FpuStackAllocator::insert_copy(LIR_Opr from, LIR_Opr to) { + int offset = tos_offset(from); + LIR_Op1* fld = new LIR_Op1(lir_fld, LIR_OprFact::intConst(offset), LIR_OprFact::illegalOpr); + insert_op(fld); + + sim()->push(fpu_num(to)); + +#ifndef PRODUCT + if (TraceFPUStack) { + tty->print("Inserted copy (%d -> %d) New state: ", fpu_num(from), fpu_num(to)); sim()->print(); tty->cr(); + } +#endif +} + +void FpuStackAllocator::do_rename(LIR_Opr from, LIR_Opr to) { + sim()->rename(fpu_num(from), fpu_num(to)); +} + +void FpuStackAllocator::do_push(LIR_Opr opr) { + sim()->push(fpu_num(opr)); +} + +void FpuStackAllocator::pop_if_last_use(LIR_Op* op, LIR_Opr opr) { + assert(op->fpu_pop_count() == 0, "fpu_pop_count alredy set"); + assert(tos_offset(opr) == 0, "can only pop stack top"); + + if (opr->is_last_use()) { + op->set_fpu_pop_count(1); + sim()->pop(); + } +} + +void FpuStackAllocator::pop_always(LIR_Op* op, LIR_Opr opr) { + assert(op->fpu_pop_count() == 0, "fpu_pop_count alredy set"); + assert(tos_offset(opr) == 0, "can only pop stack top"); + + op->set_fpu_pop_count(1); + sim()->pop(); +} + +void FpuStackAllocator::clear_fpu_stack(LIR_Opr preserve) { + int result_stack_size = (preserve->is_fpu_register() && !preserve->is_xmm_register() ? 1 : 0); + while (sim()->stack_size() > result_stack_size) { + assert(!sim()->slot_is_empty(0), "not allowed"); + + if (result_stack_size == 0 || sim()->get_slot(0) != fpu_num(preserve)) { + insert_free(0); + } else { + // move "preserve" to bottom of stack so that all other stack slots can be popped + insert_exchange(sim()->stack_size() - 1); + } + } +} + + +void FpuStackAllocator::handle_op1(LIR_Op1* op1) { + LIR_Opr in = op1->in_opr(); + LIR_Opr res = op1->result_opr(); + + LIR_Opr new_in = in; // new operands relative to the actual fpu stack top + LIR_Opr new_res = res; + + // Note: this switch is processed for all LIR_Op1, regardless if they have FPU-arguments, + // so checks for is_float_kind() are necessary inside the cases + switch (op1->code()) { + + case lir_return: { + // FPU-Stack must only contain the (optional) fpu return value. + // All remaining dead values are popped from the stack + // If the input operand is a fpu-register, it is exchanged to the bottom of the stack + + clear_fpu_stack(in); + if (in->is_fpu_register() && !in->is_xmm_register()) { + new_in = to_fpu_stack_top(in); + } + + break; + } + + case lir_move: { + if (in->is_fpu_register() && !in->is_xmm_register()) { + if (res->is_xmm_register()) { + // move from fpu register to xmm register (necessary for operations that + // are not available in the SSE instruction set) + insert_exchange(in); + new_in = to_fpu_stack_top(in); + pop_always(op1, in); + + } else if (res->is_fpu_register() && !res->is_xmm_register()) { + // move from fpu-register to fpu-register: + // * input and result register equal: + // nothing to do + // * input register is last use: + // rename the input register to result register -> input register + // not present on fpu-stack afterwards + // * input register not last use: + // duplicate input register to result register to preserve input + // + // Note: The LIR-Assembler does not produce any code for fpu register moves, + // so input and result stack index must be equal + + if (fpu_num(in) == fpu_num(res)) { + // nothing to do + } else if (in->is_last_use()) { + insert_free_if_dead(res);//, in); + do_rename(in, res); + } else { + insert_free_if_dead(res); + insert_copy(in, res); + } + new_in = to_fpu_stack(res); + new_res = new_in; + + } else { + // move from fpu-register to memory + // input operand must be on top of stack + + insert_exchange(in); + + // create debug information here because afterwards the register may have been popped + compute_debug_information(op1); + + new_in = to_fpu_stack_top(in); + pop_if_last_use(op1, in); + } + + } else if (res->is_fpu_register() && !res->is_xmm_register()) { + // move from memory/constant to fpu register + // result is pushed on the stack + + insert_free_if_dead(res); + + // create debug information before register is pushed + compute_debug_information(op1); + + do_push(res); + new_res = to_fpu_stack_top(res); + } + break; + } + + case lir_neg: { + if (in->is_fpu_register() && !in->is_xmm_register()) { + assert(res->is_fpu_register() && !res->is_xmm_register(), "must be"); + assert(in->is_last_use(), "old value gets destroyed"); + + insert_free_if_dead(res, in); + insert_exchange(in); + new_in = to_fpu_stack_top(in); + + do_rename(in, res); + new_res = to_fpu_stack_top(res); + } + break; + } + + case lir_convert: { + Bytecodes::Code bc = op1->as_OpConvert()->bytecode(); + switch (bc) { + case Bytecodes::_d2f: + case Bytecodes::_f2d: + assert(res->is_fpu_register(), "must be"); + assert(in->is_fpu_register(), "must be"); + + if (!in->is_xmm_register() && !res->is_xmm_register()) { + // this is quite the same as a move from fpu-register to fpu-register + // Note: input and result operands must have different types + if (fpu_num(in) == fpu_num(res)) { + // nothing to do + new_in = to_fpu_stack(in); + } else if (in->is_last_use()) { + insert_free_if_dead(res);//, in); + new_in = to_fpu_stack(in); + do_rename(in, res); + } else { + insert_free_if_dead(res); + insert_copy(in, res); + new_in = to_fpu_stack_top(in, true); + } + new_res = to_fpu_stack(res); + } + + break; + + case Bytecodes::_i2f: + case Bytecodes::_l2f: + case Bytecodes::_i2d: + case Bytecodes::_l2d: + assert(res->is_fpu_register(), "must be"); + if (!res->is_xmm_register()) { + insert_free_if_dead(res); + do_push(res); + new_res = to_fpu_stack_top(res); + } + break; + + case Bytecodes::_f2i: + case Bytecodes::_d2i: + assert(in->is_fpu_register(), "must be"); + if (!in->is_xmm_register()) { + insert_exchange(in); + new_in = to_fpu_stack_top(in); + + // TODO: update registes of stub + } + break; + + case Bytecodes::_f2l: + case Bytecodes::_d2l: + assert(in->is_fpu_register(), "must be"); + if (!in->is_xmm_register()) { + insert_exchange(in); + new_in = to_fpu_stack_top(in); + pop_always(op1, in); + } + break; + + case Bytecodes::_i2l: + case Bytecodes::_l2i: + case Bytecodes::_i2b: + case Bytecodes::_i2c: + case Bytecodes::_i2s: + // no fpu operands + break; + + default: + ShouldNotReachHere(); + } + break; + } + + case lir_roundfp: { + assert(in->is_fpu_register() && !in->is_xmm_register(), "input must be in register"); + assert(res->is_stack(), "result must be on stack"); + + insert_exchange(in); + new_in = to_fpu_stack_top(in); + pop_if_last_use(op1, in); + break; + } + + default: { + assert(!in->is_float_kind() && !res->is_float_kind(), "missed a fpu-operation"); + } + } + + op1->set_in_opr(new_in); + op1->set_result_opr(new_res); +} + +void FpuStackAllocator::handle_op2(LIR_Op2* op2) { + LIR_Opr left = op2->in_opr1(); + if (!left->is_float_kind()) { + return; + } + if (left->is_xmm_register()) { + return; + } + + LIR_Opr right = op2->in_opr2(); + LIR_Opr res = op2->result_opr(); + LIR_Opr new_left = left; // new operands relative to the actual fpu stack top + LIR_Opr new_right = right; + LIR_Opr new_res = res; + + assert(!left->is_xmm_register() && !right->is_xmm_register() && !res->is_xmm_register(), "not for xmm registers"); + + switch (op2->code()) { + case lir_cmp: + case lir_cmp_fd2i: + case lir_ucmp_fd2i: { + assert(left->is_fpu_register(), "invalid LIR"); + assert(right->is_fpu_register(), "invalid LIR"); + + // the left-hand side must be on top of stack. + // the right-hand side is never popped, even if is_last_use is set + insert_exchange(left); + new_left = to_fpu_stack_top(left); + new_right = to_fpu_stack(right); + pop_if_last_use(op2, left); + break; + } + + case lir_mul_strictfp: + case lir_div_strictfp: { + assert(op2->tmp_opr()->is_fpu_register(), "strict operations need temporary fpu stack slot"); + insert_free_if_dead(op2->tmp_opr()); + assert(sim()->stack_size() <= 7, "at least one stack slot must be free"); + // fall-through: continue with the normal handling of lir_mul and lir_div + } + case lir_add: + case lir_sub: + case lir_mul: + case lir_div: { + assert(left->is_fpu_register(), "must be"); + assert(res->is_fpu_register(), "must be"); + assert(left->is_equal(res), "must be"); + + // either the left-hand or the right-hand side must be on top of stack + // (if right is not a register, left must be on top) + if (!right->is_fpu_register()) { + insert_exchange(left); + new_left = to_fpu_stack_top(left); + } else { + // no exchange necessary if right is alredy on top of stack + if (tos_offset(right) == 0) { + new_left = to_fpu_stack(left); + new_right = to_fpu_stack_top(right); + } else { + insert_exchange(left); + new_left = to_fpu_stack_top(left); + new_right = to_fpu_stack(right); + } + + if (right->is_last_use()) { + op2->set_fpu_pop_count(1); + + if (tos_offset(right) == 0) { + sim()->pop(); + } else { + // if left is on top of stack, the result is placed in the stack + // slot of right, so a renaming from right to res is necessary + assert(tos_offset(left) == 0, "must be"); + sim()->pop(); + do_rename(right, res); + } + } + } + new_res = to_fpu_stack(res); + + break; + } + + case lir_rem: { + assert(left->is_fpu_register(), "must be"); + assert(right->is_fpu_register(), "must be"); + assert(res->is_fpu_register(), "must be"); + assert(left->is_equal(res), "must be"); + + // Must bring both operands to top of stack with following operand ordering: + // * fpu stack before rem: ... right left + // * fpu stack after rem: ... left + if (tos_offset(right) != 1) { + insert_exchange(right); + insert_exchange(1); + } + insert_exchange(left); + assert(tos_offset(right) == 1, "check"); + assert(tos_offset(left) == 0, "check"); + + new_left = to_fpu_stack_top(left); + new_right = to_fpu_stack(right); + + op2->set_fpu_pop_count(1); + sim()->pop(); + do_rename(right, res); + + new_res = to_fpu_stack_top(res); + break; + } + + case lir_log: + case lir_log10: + case lir_abs: + case lir_sqrt: { + // Right argument appears to be unused + assert(right->is_illegal(), "must be"); + assert(left->is_fpu_register(), "must be"); + assert(res->is_fpu_register(), "must be"); + assert(left->is_last_use(), "old value gets destroyed"); + + insert_free_if_dead(res, left); + insert_exchange(left); + do_rename(left, res); + + new_left = to_fpu_stack_top(res); + new_res = new_left; + + op2->set_fpu_stack_size(sim()->stack_size()); + break; + } + + + case lir_tan: + case lir_sin: + case lir_cos: { + // sin and cos need two temporary fpu stack slots, so there are two temporary + // registers (stored in right and temp of the operation). + // the stack allocator must guarantee that the stack slots are really free, + // otherwise there might be a stack overflow. + assert(left->is_fpu_register(), "must be"); + assert(res->is_fpu_register(), "must be"); + // assert(left->is_last_use(), "old value gets destroyed"); + assert(right->is_fpu_register(), "right is used as the first temporary register"); + assert(op2->tmp_opr()->is_fpu_register(), "temp is used as the second temporary register"); + assert(fpu_num(left) != fpu_num(right) && fpu_num(right) != fpu_num(op2->tmp_opr()) && fpu_num(op2->tmp_opr()) != fpu_num(res), "need distinct temp registers"); + + insert_free_if_dead(right); + insert_free_if_dead(op2->tmp_opr()); + + insert_free_if_dead(res, left); + insert_exchange(left); + do_rename(left, res); + + new_left = to_fpu_stack_top(res); + new_res = new_left; + + op2->set_fpu_stack_size(sim()->stack_size()); + assert(sim()->stack_size() <= 6, "at least two stack slots must be free"); + break; + } + + default: { + assert(false, "missed a fpu-operation"); + } + } + + op2->set_in_opr1(new_left); + op2->set_in_opr2(new_right); + op2->set_result_opr(new_res); +} + +void FpuStackAllocator::handle_opCall(LIR_OpCall* opCall) { + LIR_Opr res = opCall->result_opr(); + + // clear fpu-stack before call + // it may contain dead values that could not have been remved by previous operations + clear_fpu_stack(LIR_OprFact::illegalOpr); + assert(sim()->is_empty(), "fpu stack must be empty now"); + + // compute debug information before (possible) fpu result is pushed + compute_debug_information(opCall); + + if (res->is_fpu_register() && !res->is_xmm_register()) { + do_push(res); + opCall->set_result_opr(to_fpu_stack_top(res)); + } +} + +#ifndef PRODUCT +void FpuStackAllocator::check_invalid_lir_op(LIR_Op* op) { + switch (op->code()) { + case lir_24bit_FPU: + case lir_reset_FPU: + case lir_ffree: + assert(false, "operations not allowed in lir. If one of these operations is needed, check if they have fpu operands"); + break; + + case lir_fpop_raw: + case lir_fxch: + case lir_fld: + assert(false, "operations only inserted by FpuStackAllocator"); + break; + } +} +#endif + + +void FpuStackAllocator::merge_insert_add(LIR_List* instrs, FpuStackSim* cur_sim, int reg) { + LIR_Op1* move = new LIR_Op1(lir_move, LIR_OprFact::doubleConst(0), LIR_OprFact::double_fpu(reg)->make_fpu_stack_offset()); + + instrs->instructions_list()->push(move); + + cur_sim->push(reg); + move->set_result_opr(to_fpu_stack(move->result_opr())); + + #ifndef PRODUCT + if (TraceFPUStack) { + tty->print("Added new register: %d New state: ", reg); cur_sim->print(); tty->cr(); + } + #endif +} + +void FpuStackAllocator::merge_insert_xchg(LIR_List* instrs, FpuStackSim* cur_sim, int slot) { + assert(slot > 0, "no exchange necessary"); + + LIR_Op1* fxch = new LIR_Op1(lir_fxch, LIR_OprFact::intConst(slot)); + instrs->instructions_list()->push(fxch); + cur_sim->swap(slot); + + #ifndef PRODUCT + if (TraceFPUStack) { + tty->print("Exchanged register: %d New state: ", cur_sim->get_slot(slot)); cur_sim->print(); tty->cr(); + } + #endif +} + +void FpuStackAllocator::merge_insert_pop(LIR_List* instrs, FpuStackSim* cur_sim) { + int reg = cur_sim->get_slot(0); + + LIR_Op* fpop = new LIR_Op0(lir_fpop_raw); + instrs->instructions_list()->push(fpop); + cur_sim->pop(reg); + + #ifndef PRODUCT + if (TraceFPUStack) { + tty->print("Removed register: %d New state: ", reg); cur_sim->print(); tty->cr(); + } + #endif +} + +bool FpuStackAllocator::merge_rename(FpuStackSim* cur_sim, FpuStackSim* sux_sim, int start_slot, int change_slot) { + int reg = cur_sim->get_slot(change_slot); + + for (int slot = start_slot; slot >= 0; slot--) { + int new_reg = sux_sim->get_slot(slot); + + if (!cur_sim->contains(new_reg)) { + cur_sim->set_slot(change_slot, new_reg); + + #ifndef PRODUCT + if (TraceFPUStack) { + tty->print("Renamed register %d to %d New state: ", reg, new_reg); cur_sim->print(); tty->cr(); + } + #endif + + return true; + } + } + return false; +} + + +void FpuStackAllocator::merge_fpu_stack(LIR_List* instrs, FpuStackSim* cur_sim, FpuStackSim* sux_sim) { +#ifndef PRODUCT + if (TraceFPUStack) { + tty->cr(); + tty->print("before merging: pred: "); cur_sim->print(); tty->cr(); + tty->print(" sux: "); sux_sim->print(); tty->cr(); + } + + int slot; + for (slot = 0; slot < cur_sim->stack_size(); slot++) { + assert(!cur_sim->slot_is_empty(slot), "not handled by algorithm"); + } + for (slot = 0; slot < sux_sim->stack_size(); slot++) { + assert(!sux_sim->slot_is_empty(slot), "not handled by algorithm"); + } +#endif + + // size difference between cur and sux that must be resolved by adding or removing values form the stack + int size_diff = cur_sim->stack_size() - sux_sim->stack_size(); + + if (!ComputeExactFPURegisterUsage) { + // add slots that are currently free, but used in successor + // When the exact FPU register usage is computed, the stack does + // not contain dead values at merging -> no values must be added + + int sux_slot = sux_sim->stack_size() - 1; + while (size_diff < 0) { + assert(sux_slot >= 0, "slot out of bounds -> error in algorithm"); + + int reg = sux_sim->get_slot(sux_slot); + if (!cur_sim->contains(reg)) { + merge_insert_add(instrs, cur_sim, reg); + size_diff++; + + if (sux_slot + size_diff != 0) { + merge_insert_xchg(instrs, cur_sim, sux_slot + size_diff); + } + } + sux_slot--; + } + } + + assert(cur_sim->stack_size() >= sux_sim->stack_size(), "stack size must be equal or greater now"); + assert(size_diff == cur_sim->stack_size() - sux_sim->stack_size(), "must be"); + + // stack merge algorithm: + // 1) as long as the current stack top is not in the right location (that meens + // it should not be on the stack top), exchange it into the right location + // 2) if the stack top is right, but the remaining stack is not ordered correctly, + // the stack top is exchanged away to get another value on top -> + // now step 1) can be continued + // the stack can also contain unused items -> these items are removed from stack + + int finished_slot = sux_sim->stack_size() - 1; + while (finished_slot >= 0 || size_diff > 0) { + while (size_diff > 0 || (cur_sim->stack_size() > 0 && cur_sim->get_slot(0) != sux_sim->get_slot(0))) { + int reg = cur_sim->get_slot(0); + if (sux_sim->contains(reg)) { + int sux_slot = sux_sim->offset_from_tos(reg); + merge_insert_xchg(instrs, cur_sim, sux_slot + size_diff); + + } else if (!merge_rename(cur_sim, sux_sim, finished_slot, 0)) { + assert(size_diff > 0, "must be"); + + merge_insert_pop(instrs, cur_sim); + size_diff--; + } + assert(cur_sim->stack_size() == 0 || cur_sim->get_slot(0) != reg, "register must have been changed"); + } + + while (finished_slot >= 0 && cur_sim->get_slot(finished_slot) == sux_sim->get_slot(finished_slot)) { + finished_slot--; + } + + if (finished_slot >= 0) { + int reg = cur_sim->get_slot(finished_slot); + + if (sux_sim->contains(reg) || !merge_rename(cur_sim, sux_sim, finished_slot, finished_slot)) { + assert(sux_sim->contains(reg) || size_diff > 0, "must be"); + merge_insert_xchg(instrs, cur_sim, finished_slot); + } + assert(cur_sim->get_slot(finished_slot) != reg, "register must have been changed"); + } + } + +#ifndef PRODUCT + if (TraceFPUStack) { + tty->print("after merging: pred: "); cur_sim->print(); tty->cr(); + tty->print(" sux: "); sux_sim->print(); tty->cr(); + tty->cr(); + } +#endif + assert(cur_sim->stack_size() == sux_sim->stack_size(), "stack size must be equal now"); +} + + +void FpuStackAllocator::merge_cleanup_fpu_stack(LIR_List* instrs, FpuStackSim* cur_sim, BitMap& live_fpu_regs) { +#ifndef PRODUCT + if (TraceFPUStack) { + tty->cr(); + tty->print("before cleanup: state: "); cur_sim->print(); tty->cr(); + tty->print(" live: "); live_fpu_regs.print_on(tty); tty->cr(); + } +#endif + + int slot = 0; + while (slot < cur_sim->stack_size()) { + int reg = cur_sim->get_slot(slot); + if (!live_fpu_regs.at(reg)) { + if (slot != 0) { + merge_insert_xchg(instrs, cur_sim, slot); + } + merge_insert_pop(instrs, cur_sim); + } else { + slot++; + } + } + +#ifndef PRODUCT + if (TraceFPUStack) { + tty->print("after cleanup: state: "); cur_sim->print(); tty->cr(); + tty->print(" live: "); live_fpu_regs.print_on(tty); tty->cr(); + tty->cr(); + } + + // check if fpu stack only contains live registers + for (unsigned int i = 0; i < live_fpu_regs.size(); i++) { + if (live_fpu_regs.at(i) != cur_sim->contains(i)) { + tty->print_cr("mismatch between required and actual stack content"); + break; + } + } +#endif +} + + +bool FpuStackAllocator::merge_fpu_stack_with_successors(BlockBegin* block) { +#ifndef PRODUCT + if (TraceFPUStack) { + tty->print_cr("Propagating FPU stack state for B%d at LIR_Op position %d to successors:", + block->block_id(), pos()); + sim()->print(); + tty->cr(); + } +#endif + + bool changed = false; + int number_of_sux = block->number_of_sux(); + + if (number_of_sux == 1 && block->sux_at(0)->number_of_preds() > 1) { + // The successor has at least two incoming edges, so a stack merge will be necessary + // If this block is the first predecessor, cleanup the current stack and propagate it + // If this block is not the first predecessor, a stack merge will be necessary + + BlockBegin* sux = block->sux_at(0); + intArray* state = sux->fpu_stack_state(); + LIR_List* instrs = new LIR_List(_compilation); + + if (state != NULL) { + // Merge with a successors that already has a FPU stack state + // the block must only have one successor because critical edges must been split + FpuStackSim* cur_sim = sim(); + FpuStackSim* sux_sim = temp_sim(); + sux_sim->read_state(state); + + merge_fpu_stack(instrs, cur_sim, sux_sim); + + } else { + // propagate current FPU stack state to successor without state + // clean up stack first so that there are no dead values on the stack + if (ComputeExactFPURegisterUsage) { + FpuStackSim* cur_sim = sim(); + BitMap live_fpu_regs = block->sux_at(0)->fpu_register_usage(); + assert(live_fpu_regs.size() == FrameMap::nof_fpu_regs, "missing register usage"); + + merge_cleanup_fpu_stack(instrs, cur_sim, live_fpu_regs); + } + + intArray* state = sim()->write_state(); + if (TraceFPUStack) { + tty->print_cr("Setting FPU stack state of B%d (merge path)", sux->block_id()); + sim()->print(); tty->cr(); + } + sux->set_fpu_stack_state(state); + } + + if (instrs->instructions_list()->length() > 0) { + lir()->insert_before(pos(), instrs); + set_pos(instrs->instructions_list()->length() + pos()); + changed = true; + } + + } else { + // Propagate unmodified Stack to successors where a stack merge is not necessary + intArray* state = sim()->write_state(); + for (int i = 0; i < number_of_sux; i++) { + BlockBegin* sux = block->sux_at(i); + +#ifdef ASSERT + for (int j = 0; j < sux->number_of_preds(); j++) { + assert(block == sux->pred_at(j), "all critical edges must be broken"); + } + + // check if new state is same + if (sux->fpu_stack_state() != NULL) { + intArray* sux_state = sux->fpu_stack_state(); + assert(state->length() == sux_state->length(), "overwriting existing stack state"); + for (int j = 0; j < state->length(); j++) { + assert(state->at(j) == sux_state->at(j), "overwriting existing stack state"); + } + } +#endif +#ifndef PRODUCT + if (TraceFPUStack) { + tty->print_cr("Setting FPU stack state of B%d", sux->block_id()); + sim()->print(); tty->cr(); + } +#endif + + sux->set_fpu_stack_state(state); + } + } + +#ifndef PRODUCT + // assertions that FPU stack state conforms to all successors' states + intArray* cur_state = sim()->write_state(); + for (int i = 0; i < number_of_sux; i++) { + BlockBegin* sux = block->sux_at(i); + intArray* sux_state = sux->fpu_stack_state(); + + assert(sux_state != NULL, "no fpu state"); + assert(cur_state->length() == sux_state->length(), "incorrect length"); + for (int i = 0; i < cur_state->length(); i++) { + assert(cur_state->at(i) == sux_state->at(i), "element not equal"); + } + } +#endif + + return changed; +} diff --git a/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.hpp b/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.hpp new file mode 100644 index 00000000000..cdfa446daa2 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.hpp @@ -0,0 +1,176 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline bool LinearScan::is_processed_reg_num(int reg_num) { + // rsp and rbp (numbers 6 ancd 7) are ignored + assert(FrameMap::rsp_opr->cpu_regnr() == 6, "wrong assumption below"); + assert(FrameMap::rbp_opr->cpu_regnr() == 7, "wrong assumption below"); + assert(reg_num >= 0, "invalid reg_num"); + + return reg_num < 6 || reg_num > 7; +} + +inline int LinearScan::num_physical_regs(BasicType type) { + // Intel requires two cpu registers for long, + // but requires only one fpu register for double + if (type == T_LONG) { + return 2; + } + return 1; +} + + +inline bool LinearScan::requires_adjacent_regs(BasicType type) { + return false; +} + +inline bool LinearScan::is_caller_save(int assigned_reg) { + assert(assigned_reg >= 0 && assigned_reg < nof_regs, "should call this only for registers"); + return true; // no callee-saved registers on Intel + +} + + +inline void LinearScan::pd_add_temps(LIR_Op* op) { + switch (op->code()) { + case lir_tan: + case lir_sin: + case lir_cos: { + // The slow path for these functions may need to save and + // restore all live registers but we don't want to save and + // restore everything all the time, so mark the xmms as being + // killed. If the slow path were explicit or we could propagate + // live register masks down to the assembly we could do better + // but we don't have any easy way to do that right now. We + // could also consider not killing all xmm registers if we + // assume that slow paths are uncommon but it's not clear that + // would be a good idea. + if (UseSSE > 0) { +#ifndef PRODUCT + if (TraceLinearScanLevel >= 2) { + tty->print_cr("killing XMMs for trig"); + } +#endif + int op_id = op->id(); + for (int xmm = 0; xmm < FrameMap::nof_caller_save_xmm_regs; xmm++) { + LIR_Opr opr = FrameMap::caller_save_xmm_reg_at(xmm); + add_temp(reg_num(opr), op_id, noUse, T_ILLEGAL); + } + } + break; + } + } +} + + +// Implementation of LinearScanWalker + +inline bool LinearScanWalker::pd_init_regs_for_alloc(Interval* cur) { + if (allocator()->gen()->is_vreg_flag_set(cur->reg_num(), LIRGenerator::byte_reg)) { + assert(cur->type() != T_FLOAT && cur->type() != T_DOUBLE, "cpu regs only"); + _first_reg = pd_first_byte_reg; + _last_reg = pd_last_byte_reg; + return true; + } else if ((UseSSE >= 1 && cur->type() == T_FLOAT) || (UseSSE >= 2 && cur->type() == T_DOUBLE)) { + _first_reg = pd_first_xmm_reg; + _last_reg = pd_last_xmm_reg; + return true; + } + + return false; +} + + +class FpuStackAllocator VALUE_OBJ_CLASS_SPEC { + private: + Compilation* _compilation; + LinearScan* _allocator; + + LIR_OpVisitState visitor; + + LIR_List* _lir; + int _pos; + FpuStackSim _sim; + FpuStackSim _temp_sim; + + bool _debug_information_computed; + + LinearScan* allocator() { return _allocator; } + Compilation* compilation() const { return _compilation; } + + // unified bailout support + void bailout(const char* msg) const { compilation()->bailout(msg); } + bool bailed_out() const { return compilation()->bailed_out(); } + + int pos() { return _pos; } + void set_pos(int pos) { _pos = pos; } + LIR_Op* cur_op() { return lir()->instructions_list()->at(pos()); } + LIR_List* lir() { return _lir; } + void set_lir(LIR_List* lir) { _lir = lir; } + FpuStackSim* sim() { return &_sim; } + FpuStackSim* temp_sim() { return &_temp_sim; } + + int fpu_num(LIR_Opr opr); + int tos_offset(LIR_Opr opr); + LIR_Opr to_fpu_stack_top(LIR_Opr opr, bool dont_check_offset = false); + + // Helper functions for handling operations + void insert_op(LIR_Op* op); + void insert_exchange(int offset); + void insert_exchange(LIR_Opr opr); + void insert_free(int offset); + void insert_free_if_dead(LIR_Opr opr); + void insert_free_if_dead(LIR_Opr opr, LIR_Opr ignore); + void insert_copy(LIR_Opr from, LIR_Opr to); + void do_rename(LIR_Opr from, LIR_Opr to); + void do_push(LIR_Opr opr); + void pop_if_last_use(LIR_Op* op, LIR_Opr opr); + void pop_always(LIR_Op* op, LIR_Opr opr); + void clear_fpu_stack(LIR_Opr preserve); + void handle_op1(LIR_Op1* op1); + void handle_op2(LIR_Op2* op2); + void handle_opCall(LIR_OpCall* opCall); + void compute_debug_information(LIR_Op* op); + void allocate_exception_handler(XHandler* xhandler); + void allocate_block(BlockBegin* block); + +#ifndef PRODUCT + void check_invalid_lir_op(LIR_Op* op); +#endif + + // Helper functions for merging of fpu stacks + void merge_insert_add(LIR_List* instrs, FpuStackSim* cur_sim, int reg); + void merge_insert_xchg(LIR_List* instrs, FpuStackSim* cur_sim, int slot); + void merge_insert_pop(LIR_List* instrs, FpuStackSim* cur_sim); + bool merge_rename(FpuStackSim* cur_sim, FpuStackSim* sux_sim, int start_slot, int change_slot); + void merge_fpu_stack(LIR_List* instrs, FpuStackSim* cur_sim, FpuStackSim* sux_sim); + void merge_cleanup_fpu_stack(LIR_List* instrs, FpuStackSim* cur_sim, BitMap& live_fpu_regs); + bool merge_fpu_stack_with_successors(BlockBegin* block); + + public: + LIR_Opr to_fpu_stack(LIR_Opr opr); // used by LinearScan for creation of debug information + + FpuStackAllocator(Compilation* compilation, LinearScan* allocator); + void allocate(); +}; diff --git a/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.cpp new file mode 100644 index 00000000000..39fc06190fc --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.cpp @@ -0,0 +1,385 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_MacroAssembler_x86.cpp.incl" + +int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Register scratch, Label& slow_case) { + const int aligned_mask = 3; + const int hdr_offset = oopDesc::mark_offset_in_bytes(); + assert(hdr == rax, "hdr must be rax, for the cmpxchg instruction"); + assert(hdr != obj && hdr != disp_hdr && obj != disp_hdr, "registers must be different"); + assert(BytesPerWord == 4, "adjust aligned_mask and code"); + Label done; + int null_check_offset = -1; + + verify_oop(obj); + + // save object being locked into the BasicObjectLock + movl(Address(disp_hdr, BasicObjectLock::obj_offset_in_bytes()), obj); + + if (UseBiasedLocking) { + assert(scratch != noreg, "should have scratch register at this point"); + null_check_offset = biased_locking_enter(disp_hdr, obj, hdr, scratch, false, done, &slow_case); + } else { + null_check_offset = offset(); + } + + // Load object header + movl(hdr, Address(obj, hdr_offset)); + // and mark it as unlocked + orl(hdr, markOopDesc::unlocked_value); + // save unlocked object header into the displaced header location on the stack + movl(Address(disp_hdr, 0), hdr); + // test if object header is still the same (i.e. unlocked), and if so, store the + // displaced header address in the object header - if it is not the same, get the + // object header instead + if (os::is_MP()) MacroAssembler::lock(); // must be immediately before cmpxchg! + cmpxchg(disp_hdr, Address(obj, hdr_offset)); + // if the object header was the same, we're done + if (PrintBiasedLockingStatistics) { + cond_inc32(Assembler::equal, + ExternalAddress((address)BiasedLocking::fast_path_entry_count_addr())); + } + jcc(Assembler::equal, done); + // if the object header was not the same, it is now in the hdr register + // => test if it is a stack pointer into the same stack (recursive locking), i.e.: + // + // 1) (hdr & aligned_mask) == 0 + // 2) rsp <= hdr + // 3) hdr <= rsp + page_size + // + // these 3 tests can be done by evaluating the following expression: + // + // (hdr - rsp) & (aligned_mask - page_size) + // + // assuming both the stack pointer and page_size have their least + // significant 2 bits cleared and page_size is a power of 2 + subl(hdr, rsp); + andl(hdr, aligned_mask - os::vm_page_size()); + // for recursive locking, the result is zero => save it in the displaced header + // location (NULL in the displaced hdr location indicates recursive locking) + movl(Address(disp_hdr, 0), hdr); + // otherwise we don't care about the result and handle locking via runtime call + jcc(Assembler::notZero, slow_case); + // done + bind(done); + return null_check_offset; +} + + +void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_hdr, Label& slow_case) { + const int aligned_mask = 3; + const int hdr_offset = oopDesc::mark_offset_in_bytes(); + assert(disp_hdr == rax, "disp_hdr must be rax, for the cmpxchg instruction"); + assert(hdr != obj && hdr != disp_hdr && obj != disp_hdr, "registers must be different"); + assert(BytesPerWord == 4, "adjust aligned_mask and code"); + Label done; + + if (UseBiasedLocking) { + // load object + movl(obj, Address(disp_hdr, BasicObjectLock::obj_offset_in_bytes())); + biased_locking_exit(obj, hdr, done); + } + + // load displaced header + movl(hdr, Address(disp_hdr, 0)); + // if the loaded hdr is NULL we had recursive locking + testl(hdr, hdr); + // if we had recursive locking, we are done + jcc(Assembler::zero, done); + if (!UseBiasedLocking) { + // load object + movl(obj, Address(disp_hdr, BasicObjectLock::obj_offset_in_bytes())); + } + verify_oop(obj); + // test if object header is pointing to the displaced header, and if so, restore + // the displaced header in the object - if the object header is not pointing to + // the displaced header, get the object header instead + if (os::is_MP()) MacroAssembler::lock(); // must be immediately before cmpxchg! + cmpxchg(hdr, Address(obj, hdr_offset)); + // if the object header was not pointing to the displaced header, + // we do unlocking via runtime call + jcc(Assembler::notEqual, slow_case); + // done + bind(done); +} + + +// Defines obj, preserves var_size_in_bytes +void C1_MacroAssembler::try_allocate(Register obj, Register var_size_in_bytes, int con_size_in_bytes, Register t1, Register t2, Label& slow_case) { + if (UseTLAB) { + tlab_allocate(obj, var_size_in_bytes, con_size_in_bytes, t1, t2, slow_case); + } else { + eden_allocate(obj, var_size_in_bytes, con_size_in_bytes, t1, slow_case); + } +} + + +void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register len, Register t1, Register t2) { + assert_different_registers(obj, klass, len); + if (UseBiasedLocking && !len->is_valid()) { + assert_different_registers(obj, klass, len, t1, t2); + movl(t1, Address(klass, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes())); + movl(Address(obj, oopDesc::mark_offset_in_bytes()), t1); + } else { + movl(Address(obj, oopDesc::mark_offset_in_bytes ()), (int)markOopDesc::prototype()); + } + + movl(Address(obj, oopDesc::klass_offset_in_bytes()), klass); + if (len->is_valid()) { + movl(Address(obj, arrayOopDesc::length_offset_in_bytes()), len); + } +} + + +// preserves obj, destroys len_in_bytes +void C1_MacroAssembler::initialize_body(Register obj, Register len_in_bytes, int hdr_size_in_bytes, Register t1) { + Label done; + assert(obj != len_in_bytes && obj != t1 && t1 != len_in_bytes, "registers must be different"); + assert((hdr_size_in_bytes & (BytesPerWord - 1)) == 0, "header size is not a multiple of BytesPerWord"); + Register index = len_in_bytes; + subl(index, hdr_size_in_bytes); + jcc(Assembler::zero, done); + // initialize topmost word, divide index by 2, check if odd and test if zero + // note: for the remaining code to work, index must be a multiple of BytesPerWord +#ifdef ASSERT + { Label L; + testl(index, BytesPerWord - 1); + jcc(Assembler::zero, L); + stop("index is not a multiple of BytesPerWord"); + bind(L); + } +#endif + xorl(t1, t1); // use _zero reg to clear memory (shorter code) + if (UseIncDec) { + shrl(index, 3); // divide by 8 and set carry flag if bit 2 was set + } else { + shrl(index, 2); // use 2 instructions to avoid partial flag stall + shrl(index, 1); + } + // index could have been not a multiple of 8 (i.e., bit 2 was set) + { Label even; + // note: if index was a multiple of 8, than it cannot + // be 0 now otherwise it must have been 0 before + // => if it is even, we don't need to check for 0 again + jcc(Assembler::carryClear, even); + // clear topmost word (no jump needed if conditional assignment would work here) + movl(Address(obj, index, Address::times_8, hdr_size_in_bytes - 0*BytesPerWord), t1); + // index could be 0 now, need to check again + jcc(Assembler::zero, done); + bind(even); + } + // initialize remaining object fields: rdx is a multiple of 2 now + { Label loop; + bind(loop); + movl(Address(obj, index, Address::times_8, hdr_size_in_bytes - 1*BytesPerWord), t1); + movl(Address(obj, index, Address::times_8, hdr_size_in_bytes - 2*BytesPerWord), t1); + decrement(index); + jcc(Assembler::notZero, loop); + } + + // done + bind(done); +} + + +void C1_MacroAssembler::allocate_object(Register obj, Register t1, Register t2, int header_size, int object_size, Register klass, Label& slow_case) { + assert(obj == rax, "obj must be in rax, for cmpxchg"); + assert(obj != t1 && obj != t2 && t1 != t2, "registers must be different"); // XXX really? + assert(header_size >= 0 && object_size >= header_size, "illegal sizes"); + + try_allocate(obj, noreg, object_size * BytesPerWord, t1, t2, slow_case); + + initialize_object(obj, klass, noreg, object_size * HeapWordSize, t1, t2); +} + +void C1_MacroAssembler::initialize_object(Register obj, Register klass, Register var_size_in_bytes, int con_size_in_bytes, Register t1, Register t2) { + assert((con_size_in_bytes & MinObjAlignmentInBytesMask) == 0, + "con_size_in_bytes is not multiple of alignment"); + const int hdr_size_in_bytes = oopDesc::header_size_in_bytes(); + + initialize_header(obj, klass, noreg, t1, t2); + + // clear rest of allocated space + const Register t1_zero = t1; + const Register index = t2; + const int threshold = 6 * BytesPerWord; // approximate break even point for code size (see comments below) + if (var_size_in_bytes != noreg) { + movl(index, var_size_in_bytes); + initialize_body(obj, index, hdr_size_in_bytes, t1_zero); + } else if (con_size_in_bytes <= threshold) { + // use explicit null stores + // code size = 2 + 3*n bytes (n = number of fields to clear) + xorl(t1_zero, t1_zero); // use t1_zero reg to clear memory (shorter code) + for (int i = hdr_size_in_bytes; i < con_size_in_bytes; i += BytesPerWord) + movl(Address(obj, i), t1_zero); + } else if (con_size_in_bytes > hdr_size_in_bytes) { + // use loop to null out the fields + // code size = 16 bytes for even n (n = number of fields to clear) + // initialize last object field first if odd number of fields + xorl(t1_zero, t1_zero); // use t1_zero reg to clear memory (shorter code) + movl(index, (con_size_in_bytes - hdr_size_in_bytes) >> 3); + // initialize last object field if constant size is odd + if (((con_size_in_bytes - hdr_size_in_bytes) & 4) != 0) + movl(Address(obj, con_size_in_bytes - (1*BytesPerWord)), t1_zero); + // initialize remaining object fields: rdx is a multiple of 2 + { Label loop; + bind(loop); + movl(Address(obj, index, Address::times_8, + hdr_size_in_bytes - (1*BytesPerWord)), t1_zero); + movl(Address(obj, index, Address::times_8, + hdr_size_in_bytes - (2*BytesPerWord)), t1_zero); + decrement(index); + jcc(Assembler::notZero, loop); + } + } + + if (DTraceAllocProbes) { + assert(obj == rax, "must be"); + call(RuntimeAddress(Runtime1::entry_for(Runtime1::dtrace_object_alloc_id))); + } + + verify_oop(obj); +} + +void C1_MacroAssembler::allocate_array(Register obj, Register len, Register t1, Register t2, int header_size, Address::ScaleFactor f, Register klass, Label& slow_case) { + assert(obj == rax, "obj must be in rax, for cmpxchg"); + assert_different_registers(obj, len, t1, t2, klass); + + // determine alignment mask + assert(BytesPerWord == 4, "must be a multiple of 2 for masking code to work"); + + // check for negative or excessive length + cmpl(len, max_array_allocation_length); + jcc(Assembler::above, slow_case); + + const Register arr_size = t2; // okay to be the same + // align object end + movl(arr_size, header_size * BytesPerWord + MinObjAlignmentInBytesMask); + leal(arr_size, Address(arr_size, len, f)); + andl(arr_size, ~MinObjAlignmentInBytesMask); + + try_allocate(obj, arr_size, 0, t1, t2, slow_case); + + initialize_header(obj, klass, len, t1, t2); + + // clear rest of allocated space + const Register len_zero = len; + initialize_body(obj, arr_size, header_size * BytesPerWord, len_zero); + + if (DTraceAllocProbes) { + assert(obj == rax, "must be"); + call(RuntimeAddress(Runtime1::entry_for(Runtime1::dtrace_object_alloc_id))); + } + + verify_oop(obj); +} + + + +void C1_MacroAssembler::inline_cache_check(Register receiver, Register iCache) { + verify_oop(receiver); + // explicit NULL check not needed since load from [klass_offset] causes a trap + // check against inline cache + assert(!MacroAssembler::needs_explicit_null_check(oopDesc::klass_offset_in_bytes()), "must add explicit null check"); + int start_offset = offset(); + cmpl(iCache, Address(receiver, oopDesc::klass_offset_in_bytes())); + // if icache check fails, then jump to runtime routine + // Note: RECEIVER must still contain the receiver! + jump_cc(Assembler::notEqual, + RuntimeAddress(SharedRuntime::get_ic_miss_stub())); + assert(offset() - start_offset == 9, "check alignment in emit_method_entry"); +} + + +void C1_MacroAssembler::method_exit(bool restore_frame) { + if (restore_frame) { + leave(); + } + ret(0); +} + + +void C1_MacroAssembler::build_frame(int frame_size_in_bytes) { + // Make sure there is enough stack space for this method's activation. + // Note that we do this before doing an enter(). This matches the + // ordering of C2's stack overflow check / rsp decrement and allows + // the SharedRuntime stack overflow handling to be consistent + // between the two compilers. + generate_stack_overflow_check(frame_size_in_bytes); + + enter(); +#ifdef TIERED + // c2 leaves fpu stack dirty. Clean it on entry + if (UseSSE < 2 ) { + empty_FPU_stack(); + } +#endif // TIERED + decrement(rsp, frame_size_in_bytes); // does not emit code for frame_size == 0 +} + + +void C1_MacroAssembler::unverified_entry(Register receiver, Register ic_klass) { + if (C1Breakpoint) int3(); + inline_cache_check(receiver, ic_klass); +} + + +void C1_MacroAssembler::verified_entry() { + if (C1Breakpoint)int3(); + // build frame + verify_FPU(0, "method_entry"); +} + + +#ifndef PRODUCT + +void C1_MacroAssembler::verify_stack_oop(int stack_offset) { + if (!VerifyOops) return; + verify_oop_addr(Address(rsp, stack_offset)); +} + +void C1_MacroAssembler::verify_not_null_oop(Register r) { + if (!VerifyOops) return; + Label not_null; + testl(r, r); + jcc(Assembler::notZero, not_null); + stop("non-null oop required"); + bind(not_null); + verify_oop(r); +} + +void C1_MacroAssembler::invalidate_registers(bool inv_rax, bool inv_rbx, bool inv_rcx, bool inv_rdx, bool inv_rsi, bool inv_rdi) { +#ifdef ASSERT + if (inv_rax) movl(rax, 0xDEAD); + if (inv_rbx) movl(rbx, 0xDEAD); + if (inv_rcx) movl(rcx, 0xDEAD); + if (inv_rdx) movl(rdx, 0xDEAD); + if (inv_rsi) movl(rsi, 0xDEAD); + if (inv_rdi) movl(rdi, 0xDEAD); +#endif +} + +#endif // ifndef PRODUCT diff --git a/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp b/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp new file mode 100644 index 00000000000..62f6d4c14a8 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp @@ -0,0 +1,114 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// C1_MacroAssembler contains high-level macros for C1 + + private: + int _rsp_offset; // track rsp changes + // initialization + void pd_init() { _rsp_offset = 0; } + + public: + void try_allocate( + Register obj, // result: pointer to object after successful allocation + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Register t2, // temp register + Label& slow_case // continuation point if fast allocation fails + ); + + void initialize_header(Register obj, Register klass, Register len, Register t1, Register t2); + void initialize_body(Register obj, Register len_in_bytes, int hdr_size_in_bytes, Register t1); + + // locking + // hdr : must be rax, contents destroyed + // obj : must point to the object to lock, contents preserved + // disp_hdr: must point to the displaced header location, contents preserved + // scratch : scratch register, contents destroyed + // returns code offset at which to add null check debug information + int lock_object (Register swap, Register obj, Register disp_hdr, Register scratch, Label& slow_case); + + // unlocking + // hdr : contents destroyed + // obj : must point to the object to lock, contents preserved + // disp_hdr: must be eax & must point to the displaced header location, contents destroyed + void unlock_object(Register swap, Register obj, Register lock, Label& slow_case); + + void initialize_object( + Register obj, // result: pointer to object after successful allocation + Register klass, // object klass + Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise + int con_size_in_bytes, // object size in bytes if known at compile time + Register t1, // temp register + Register t2 // temp register + ); + + // allocation of fixed-size objects + // (can also be used to allocate fixed-size arrays, by setting + // hdr_size correctly and storing the array length afterwards) + // obj : must be rax, will contain pointer to allocated object + // t1, t2 : scratch registers - contents destroyed + // header_size: size of object header in words + // object_size: total size of object in words + // slow_case : exit to slow case implementation if fast allocation fails + void allocate_object(Register obj, Register t1, Register t2, int header_size, int object_size, Register klass, Label& slow_case); + + enum { + max_array_allocation_length = 0x00FFFFFF + }; + + // allocation of arrays + // obj : must be rax, will contain pointer to allocated object + // len : array length in number of elements + // t : scratch register - contents destroyed + // header_size: size of object header in words + // f : element scale factor + // slow_case : exit to slow case implementation if fast allocation fails + void allocate_array(Register obj, Register len, Register t, Register t2, int header_size, Address::ScaleFactor f, Register klass, Label& slow_case); + + int rsp_offset() const { return _rsp_offset; } + void set_rsp_offset(int n) { _rsp_offset = n; } + + // Note: NEVER push values directly, but only through following push_xxx functions; + // This helps us to track the rsp changes compared to the entry rsp (->_rsp_offset) + + void push_jint (jint i) { _rsp_offset++; pushl(i); } + void push_oop (jobject o) { _rsp_offset++; pushoop(o); } + void push_addr (Address a) { _rsp_offset++; pushl(a); } + void push_reg (Register r) { _rsp_offset++; pushl(r); } + void pop (Register r) { _rsp_offset--; popl (r); assert(_rsp_offset >= 0, "stack offset underflow"); } + + void dec_stack (int nof_words) { + _rsp_offset -= nof_words; + assert(_rsp_offset >= 0, "stack offset underflow"); + addl(rsp, wordSize * nof_words); + } + + void dec_stack_after_call (int nof_words) { + _rsp_offset -= nof_words; + assert(_rsp_offset >= 0, "stack offset underflow"); + } + + void invalidate_registers(bool inv_rax, bool inv_rbx, bool inv_rcx, bool inv_rdx, bool inv_rsi, bool inv_rdi) PRODUCT_RETURN; diff --git a/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp b/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp new file mode 100644 index 00000000000..be73c79365b --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp @@ -0,0 +1,1399 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_Runtime1_x86.cpp.incl" + + +// Implementation of StubAssembler + +int StubAssembler::call_RT(Register oop_result1, Register oop_result2, address entry, int args_size) { + // setup registers + const Register thread = rdi; // is callee-saved register (Visual C++ calling conventions) + assert(!(oop_result1->is_valid() || oop_result2->is_valid()) || oop_result1 != oop_result2, "registers must be different"); + assert(oop_result1 != thread && oop_result2 != thread, "registers must be different"); + assert(args_size >= 0, "illegal args_size"); + + set_num_rt_args(1 + args_size); + + // push java thread (becomes first argument of C function) + get_thread(thread); + pushl(thread); + + set_last_Java_frame(thread, noreg, rbp, NULL); + // do the call + call(RuntimeAddress(entry)); + int call_offset = offset(); + // verify callee-saved register +#ifdef ASSERT + guarantee(thread != rax, "change this code"); + pushl(rax); + { Label L; + get_thread(rax); + cmpl(thread, rax); + jcc(Assembler::equal, L); + int3(); + stop("StubAssembler::call_RT: rdi not callee saved?"); + bind(L); + } + popl(rax); +#endif + reset_last_Java_frame(thread, true, false); + + // discard thread and arguments + addl(rsp, (1 + args_size)*BytesPerWord); + + // check for pending exceptions + { Label L; + cmpl(Address(thread, Thread::pending_exception_offset()), NULL_WORD); + jcc(Assembler::equal, L); + // exception pending => remove activation and forward to exception handler + movl(rax, Address(thread, Thread::pending_exception_offset())); + // make sure that the vm_results are cleared + if (oop_result1->is_valid()) { + movl(Address(thread, JavaThread::vm_result_offset()), NULL_WORD); + } + if (oop_result2->is_valid()) { + movl(Address(thread, JavaThread::vm_result_2_offset()), NULL_WORD); + } + if (frame_size() == no_frame_size) { + leave(); + jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + } else if (_stub_id == Runtime1::forward_exception_id) { + should_not_reach_here(); + } else { + jump(RuntimeAddress(Runtime1::entry_for(Runtime1::forward_exception_id))); + } + bind(L); + } + // get oop results if there are any and reset the values in the thread + if (oop_result1->is_valid()) { + movl(oop_result1, Address(thread, JavaThread::vm_result_offset())); + movl(Address(thread, JavaThread::vm_result_offset()), NULL_WORD); + verify_oop(oop_result1); + } + if (oop_result2->is_valid()) { + movl(oop_result2, Address(thread, JavaThread::vm_result_2_offset())); + movl(Address(thread, JavaThread::vm_result_2_offset()), NULL_WORD); + verify_oop(oop_result2); + } + return call_offset; +} + + +int StubAssembler::call_RT(Register oop_result1, Register oop_result2, address entry, Register arg1) { + pushl(arg1); + return call_RT(oop_result1, oop_result2, entry, 1); +} + + +int StubAssembler::call_RT(Register oop_result1, Register oop_result2, address entry, Register arg1, Register arg2) { + pushl(arg2); + pushl(arg1); + return call_RT(oop_result1, oop_result2, entry, 2); +} + + +int StubAssembler::call_RT(Register oop_result1, Register oop_result2, address entry, Register arg1, Register arg2, Register arg3) { + pushl(arg3); + pushl(arg2); + pushl(arg1); + return call_RT(oop_result1, oop_result2, entry, 3); +} + + +// Implementation of StubFrame + +class StubFrame: public StackObj { + private: + StubAssembler* _sasm; + + public: + StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments); + void load_argument(int offset_in_words, Register reg); + + ~StubFrame(); +}; + + +#define __ _sasm-> + +StubFrame::StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments) { + _sasm = sasm; + __ set_info(name, must_gc_arguments); + __ enter(); +} + +// load parameters that were stored with LIR_Assembler::store_parameter +// Note: offsets for store_parameter and load_argument must match +void StubFrame::load_argument(int offset_in_words, Register reg) { + // rbp, + 0: link + // + 1: return address + // + 2: argument with offset 0 + // + 3: argument with offset 1 + // + 4: ... + + __ movl(reg, Address(rbp, (offset_in_words + 2) * BytesPerWord)); +} + + +StubFrame::~StubFrame() { + __ leave(); + __ ret(0); +} + +#undef __ + + +// Implementation of Runtime1 + +#define __ sasm-> + +const int float_regs_as_doubles_size_in_words = 16; +const int xmm_regs_as_doubles_size_in_words = 16; + +// Stack layout for saving/restoring all the registers needed during a runtime +// call (this includes deoptimization) +// Note: note that users of this frame may well have arguments to some runtime +// while these values are on the stack. These positions neglect those arguments +// but the code in save_live_registers will take the argument count into +// account. +// +enum reg_save_layout { + dummy1, + dummy2, + // Two temps to be used as needed by users of save/restore callee registers + temp_2_off, + temp_1_off, + xmm_regs_as_doubles_off, + float_regs_as_doubles_off = xmm_regs_as_doubles_off + xmm_regs_as_doubles_size_in_words, + fpu_state_off = float_regs_as_doubles_off + float_regs_as_doubles_size_in_words, + fpu_state_end_off = fpu_state_off + FPUStateSizeInWords, + marker = fpu_state_end_off, + extra_space_offset, + rdi_off = extra_space_offset, + rsi_off, + rbp_off, + rsp_off, + rbx_off, + rdx_off, + rcx_off, + rax_off, + saved_rbp_off, + return_off, + reg_save_frame_size, // As noted: neglects any parameters to runtime + + // equates + + // illegal instruction handler + continue_dest_off = temp_1_off, + + // deoptimization equates + fp0_off = float_regs_as_doubles_off, // slot for java float/double return value + xmm0_off = xmm_regs_as_doubles_off, // slot for java float/double return value + deopt_type = temp_2_off, // slot for type of deopt in progress + ret_type = temp_1_off // slot for return type +}; + + + +// Save off registers which might be killed by calls into the runtime. +// Tries to smart of about FP registers. In particular we separate +// saving and describing the FPU registers for deoptimization since we +// have to save the FPU registers twice if we describe them and on P4 +// saving FPU registers which don't contain anything appears +// expensive. The deopt blob is the only thing which needs to +// describe FPU registers. In all other cases it should be sufficient +// to simply save their current value. + +static OopMap* generate_oop_map(StubAssembler* sasm, int num_rt_args, + bool save_fpu_registers = true) { + int frame_size = reg_save_frame_size + num_rt_args; // args + thread + sasm->set_frame_size(frame_size); + + // record saved value locations in an OopMap + // locations are offsets from sp after runtime call; num_rt_args is number of arguments in call, including thread + OopMap* map = new OopMap(frame_size, 0); + map->set_callee_saved(VMRegImpl::stack2reg(rax_off + num_rt_args), rax->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(rcx_off + num_rt_args), rcx->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(rdx_off + num_rt_args), rdx->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(rbx_off + num_rt_args), rbx->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(rsi_off + num_rt_args), rsi->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(rdi_off + num_rt_args), rdi->as_VMReg()); + + if (save_fpu_registers) { + if (UseSSE < 2) { + int fpu_off = float_regs_as_doubles_off; + for (int n = 0; n < FrameMap::nof_fpu_regs; n++) { + VMReg fpu_name_0 = FrameMap::fpu_regname(n); + map->set_callee_saved(VMRegImpl::stack2reg(fpu_off + num_rt_args), fpu_name_0); + // %%% This is really a waste but we'll keep things as they were for now + if (true) { + map->set_callee_saved(VMRegImpl::stack2reg(fpu_off + 1 + num_rt_args), fpu_name_0->next()); + } + fpu_off += 2; + } + assert(fpu_off == fpu_state_off, "incorrect number of fpu stack slots"); + } + + if (UseSSE >= 2) { + int xmm_off = xmm_regs_as_doubles_off; + for (int n = 0; n < FrameMap::nof_xmm_regs; n++) { + VMReg xmm_name_0 = as_XMMRegister(n)->as_VMReg(); + map->set_callee_saved(VMRegImpl::stack2reg(xmm_off + num_rt_args), xmm_name_0); + // %%% This is really a waste but we'll keep things as they were for now + if (true) { + map->set_callee_saved(VMRegImpl::stack2reg(xmm_off + 1 + num_rt_args), xmm_name_0->next()); + } + xmm_off += 2; + } + assert(xmm_off == float_regs_as_doubles_off, "incorrect number of xmm registers"); + + } else if (UseSSE == 1) { + int xmm_off = xmm_regs_as_doubles_off; + for (int n = 0; n < FrameMap::nof_xmm_regs; n++) { + VMReg xmm_name_0 = as_XMMRegister(n)->as_VMReg(); + map->set_callee_saved(VMRegImpl::stack2reg(xmm_off + num_rt_args), xmm_name_0); + xmm_off += 2; + } + assert(xmm_off == float_regs_as_doubles_off, "incorrect number of xmm registers"); + } + } + + return map; +} + +static OopMap* save_live_registers(StubAssembler* sasm, int num_rt_args, + bool save_fpu_registers = true) { + __ block_comment("save_live_registers"); + + int frame_size = reg_save_frame_size + num_rt_args; // args + thread + // frame_size = round_to(frame_size, 4); + sasm->set_frame_size(frame_size); + + __ pushad(); // integer registers + + // assert(float_regs_as_doubles_off % 2 == 0, "misaligned offset"); + // assert(xmm_regs_as_doubles_off % 2 == 0, "misaligned offset"); + + __ subl(rsp, extra_space_offset * wordSize); + +#ifdef ASSERT + __ movl(Address(rsp, marker * wordSize), 0xfeedbeef); +#endif + + if (save_fpu_registers) { + if (UseSSE < 2) { + // save FPU stack + __ fnsave(Address(rsp, fpu_state_off * wordSize)); + __ fwait(); + +#ifdef ASSERT + Label ok; + __ cmpw(Address(rsp, fpu_state_off * wordSize), StubRoutines::fpu_cntrl_wrd_std()); + __ jccb(Assembler::equal, ok); + __ stop("corrupted control word detected"); + __ bind(ok); +#endif + + // Reset the control word to guard against exceptions being unmasked + // since fstp_d can cause FPU stack underflow exceptions. Write it + // into the on stack copy and then reload that to make sure that the + // current and future values are correct. + __ movw(Address(rsp, fpu_state_off * wordSize), StubRoutines::fpu_cntrl_wrd_std()); + __ frstor(Address(rsp, fpu_state_off * wordSize)); + + // Save the FPU registers in de-opt-able form + __ fstp_d(Address(rsp, float_regs_as_doubles_off * BytesPerWord + 0)); + __ fstp_d(Address(rsp, float_regs_as_doubles_off * BytesPerWord + 8)); + __ fstp_d(Address(rsp, float_regs_as_doubles_off * BytesPerWord + 16)); + __ fstp_d(Address(rsp, float_regs_as_doubles_off * BytesPerWord + 24)); + __ fstp_d(Address(rsp, float_regs_as_doubles_off * BytesPerWord + 32)); + __ fstp_d(Address(rsp, float_regs_as_doubles_off * BytesPerWord + 40)); + __ fstp_d(Address(rsp, float_regs_as_doubles_off * BytesPerWord + 48)); + __ fstp_d(Address(rsp, float_regs_as_doubles_off * BytesPerWord + 56)); + } + + if (UseSSE >= 2) { + // save XMM registers + // XMM registers can contain float or double values, but this is not known here, + // so always save them as doubles. + // note that float values are _not_ converted automatically, so for float values + // the second word contains only garbage data. + __ movdbl(Address(rsp, xmm_regs_as_doubles_off * wordSize + 0), xmm0); + __ movdbl(Address(rsp, xmm_regs_as_doubles_off * wordSize + 8), xmm1); + __ movdbl(Address(rsp, xmm_regs_as_doubles_off * wordSize + 16), xmm2); + __ movdbl(Address(rsp, xmm_regs_as_doubles_off * wordSize + 24), xmm3); + __ movdbl(Address(rsp, xmm_regs_as_doubles_off * wordSize + 32), xmm4); + __ movdbl(Address(rsp, xmm_regs_as_doubles_off * wordSize + 40), xmm5); + __ movdbl(Address(rsp, xmm_regs_as_doubles_off * wordSize + 48), xmm6); + __ movdbl(Address(rsp, xmm_regs_as_doubles_off * wordSize + 56), xmm7); + } else if (UseSSE == 1) { + // save XMM registers as float because double not supported without SSE2 + __ movflt(Address(rsp, xmm_regs_as_doubles_off * wordSize + 0), xmm0); + __ movflt(Address(rsp, xmm_regs_as_doubles_off * wordSize + 8), xmm1); + __ movflt(Address(rsp, xmm_regs_as_doubles_off * wordSize + 16), xmm2); + __ movflt(Address(rsp, xmm_regs_as_doubles_off * wordSize + 24), xmm3); + __ movflt(Address(rsp, xmm_regs_as_doubles_off * wordSize + 32), xmm4); + __ movflt(Address(rsp, xmm_regs_as_doubles_off * wordSize + 40), xmm5); + __ movflt(Address(rsp, xmm_regs_as_doubles_off * wordSize + 48), xmm6); + __ movflt(Address(rsp, xmm_regs_as_doubles_off * wordSize + 56), xmm7); + } + } + + // FPU stack must be empty now + __ verify_FPU(0, "save_live_registers"); + + return generate_oop_map(sasm, num_rt_args, save_fpu_registers); +} + + +static void restore_fpu(StubAssembler* sasm, bool restore_fpu_registers = true) { + if (restore_fpu_registers) { + if (UseSSE >= 2) { + // restore XMM registers + __ movdbl(xmm0, Address(rsp, xmm_regs_as_doubles_off * wordSize + 0)); + __ movdbl(xmm1, Address(rsp, xmm_regs_as_doubles_off * wordSize + 8)); + __ movdbl(xmm2, Address(rsp, xmm_regs_as_doubles_off * wordSize + 16)); + __ movdbl(xmm3, Address(rsp, xmm_regs_as_doubles_off * wordSize + 24)); + __ movdbl(xmm4, Address(rsp, xmm_regs_as_doubles_off * wordSize + 32)); + __ movdbl(xmm5, Address(rsp, xmm_regs_as_doubles_off * wordSize + 40)); + __ movdbl(xmm6, Address(rsp, xmm_regs_as_doubles_off * wordSize + 48)); + __ movdbl(xmm7, Address(rsp, xmm_regs_as_doubles_off * wordSize + 56)); + } else if (UseSSE == 1) { + // restore XMM registers + __ movflt(xmm0, Address(rsp, xmm_regs_as_doubles_off * wordSize + 0)); + __ movflt(xmm1, Address(rsp, xmm_regs_as_doubles_off * wordSize + 8)); + __ movflt(xmm2, Address(rsp, xmm_regs_as_doubles_off * wordSize + 16)); + __ movflt(xmm3, Address(rsp, xmm_regs_as_doubles_off * wordSize + 24)); + __ movflt(xmm4, Address(rsp, xmm_regs_as_doubles_off * wordSize + 32)); + __ movflt(xmm5, Address(rsp, xmm_regs_as_doubles_off * wordSize + 40)); + __ movflt(xmm6, Address(rsp, xmm_regs_as_doubles_off * wordSize + 48)); + __ movflt(xmm7, Address(rsp, xmm_regs_as_doubles_off * wordSize + 56)); + } + + if (UseSSE < 2) { + __ frstor(Address(rsp, fpu_state_off * wordSize)); + } else { + // check that FPU stack is really empty + __ verify_FPU(0, "restore_live_registers"); + } + + } else { + // check that FPU stack is really empty + __ verify_FPU(0, "restore_live_registers"); + } + +#ifdef ASSERT + { + Label ok; + __ cmpl(Address(rsp, marker * wordSize), 0xfeedbeef); + __ jcc(Assembler::equal, ok); + __ stop("bad offsets in frame"); + __ bind(ok); + } +#endif + + __ addl(rsp, extra_space_offset * wordSize); +} + + +static void restore_live_registers(StubAssembler* sasm, bool restore_fpu_registers = true) { + __ block_comment("restore_live_registers"); + + restore_fpu(sasm, restore_fpu_registers); + __ popad(); +} + + +static void restore_live_registers_except_rax(StubAssembler* sasm, bool restore_fpu_registers = true) { + __ block_comment("restore_live_registers_except_rax"); + + restore_fpu(sasm, restore_fpu_registers); + + __ popl(rdi); + __ popl(rsi); + __ popl(rbp); + __ popl(rbx); // skip this value + __ popl(rbx); + __ popl(rdx); + __ popl(rcx); + __ addl(rsp, 4); +} + + +void Runtime1::initialize_pd() { + // nothing to do +} + + +// target: the entry point of the method that creates and posts the exception oop +// has_argument: true if the exception needs an argument (passed on stack because registers must be preserved) + +OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, bool has_argument) { + // preserve all registers + int num_rt_args = has_argument ? 2 : 1; + OopMap* oop_map = save_live_registers(sasm, num_rt_args); + + // now all registers are saved and can be used freely + // verify that no old value is used accidentally + __ invalidate_registers(true, true, true, true, true, true); + + // registers used by this stub + const Register temp_reg = rbx; + + // load argument for exception that is passed as an argument into the stub + if (has_argument) { + __ movl(temp_reg, Address(rbp, 2*BytesPerWord)); + __ pushl(temp_reg); + } + + int call_offset = __ call_RT(noreg, noreg, target, num_rt_args - 1); + + OopMapSet* oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, oop_map); + + __ stop("should not reach here"); + + return oop_maps; +} + + +void Runtime1::generate_handle_exception(StubAssembler *sasm, OopMapSet* oop_maps, OopMap* oop_map, bool save_fpu_registers) { + // incoming parameters + const Register exception_oop = rax; + const Register exception_pc = rdx; + // other registers used in this stub + const Register real_return_addr = rbx; + const Register thread = rdi; + + __ block_comment("generate_handle_exception"); + +#ifdef TIERED + // C2 can leave the fpu stack dirty + if (UseSSE < 2 ) { + __ empty_FPU_stack(); + } +#endif // TIERED + + // verify that only rax, and rdx is valid at this time + __ invalidate_registers(false, true, true, false, true, true); + // verify that rax, contains a valid exception + __ verify_not_null_oop(exception_oop); + + // load address of JavaThread object for thread-local data + __ get_thread(thread); + +#ifdef ASSERT + // check that fields in JavaThread for exception oop and issuing pc are + // empty before writing to them + Label oop_empty; + __ cmpl(Address(thread, JavaThread::exception_oop_offset()), 0); + __ jcc(Assembler::equal, oop_empty); + __ stop("exception oop already set"); + __ bind(oop_empty); + + Label pc_empty; + __ cmpl(Address(thread, JavaThread::exception_pc_offset()), 0); + __ jcc(Assembler::equal, pc_empty); + __ stop("exception pc already set"); + __ bind(pc_empty); +#endif + + // save exception oop and issuing pc into JavaThread + // (exception handler will load it from here) + __ movl(Address(thread, JavaThread::exception_oop_offset()), exception_oop); + __ movl(Address(thread, JavaThread::exception_pc_offset()), exception_pc); + + // save real return address (pc that called this stub) + __ movl(real_return_addr, Address(rbp, 1*BytesPerWord)); + __ movl(Address(rsp, temp_1_off * BytesPerWord), real_return_addr); + + // patch throwing pc into return address (has bci & oop map) + __ movl(Address(rbp, 1*BytesPerWord), exception_pc); + + // compute the exception handler. + // the exception oop and the throwing pc are read from the fields in JavaThread + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, exception_handler_for_pc)); + oop_maps->add_gc_map(call_offset, oop_map); + + // rax,: handler address or NULL if no handler exists + // will be the deopt blob if nmethod was deoptimized while we looked up + // handler regardless of whether handler existed in the nmethod. + + // only rax, is valid at this time, all other registers have been destroyed by the runtime call + __ invalidate_registers(false, true, true, true, true, true); + + // Do we have an exception handler in the nmethod? + Label no_handler; + Label done; + __ testl(rax, rax); + __ jcc(Assembler::zero, no_handler); + + // exception handler found + // patch the return address -> the stub will directly return to the exception handler + __ movl(Address(rbp, 1*BytesPerWord), rax); + + // restore registers + restore_live_registers(sasm, save_fpu_registers); + + // return to exception handler + __ leave(); + __ ret(0); + + __ bind(no_handler); + // no exception handler found in this method, so the exception is + // forwarded to the caller (using the unwind code of the nmethod) + // there is no need to restore the registers + + // restore the real return address that was saved before the RT-call + __ movl(real_return_addr, Address(rsp, temp_1_off * BytesPerWord)); + __ movl(Address(rbp, 1*BytesPerWord), real_return_addr); + + // load address of JavaThread object for thread-local data + __ get_thread(thread); + // restore exception oop into rax, (convention for unwind code) + __ movl(exception_oop, Address(thread, JavaThread::exception_oop_offset())); + + // clear exception fields in JavaThread because they are no longer needed + // (fields must be cleared because they are processed by GC otherwise) + __ movl(Address(thread, JavaThread::exception_oop_offset()), NULL_WORD); + __ movl(Address(thread, JavaThread::exception_pc_offset()), NULL_WORD); + + // pop the stub frame off + __ leave(); + + generate_unwind_exception(sasm); + __ stop("should not reach here"); +} + + +void Runtime1::generate_unwind_exception(StubAssembler *sasm) { + // incoming parameters + const Register exception_oop = rax; + // other registers used in this stub + const Register exception_pc = rdx; + const Register handler_addr = rbx; + const Register thread = rdi; + + // verify that only rax, is valid at this time + __ invalidate_registers(false, true, true, true, true, true); + +#ifdef ASSERT + // check that fields in JavaThread for exception oop and issuing pc are empty + __ get_thread(thread); + Label oop_empty; + __ cmpl(Address(thread, JavaThread::exception_oop_offset()), 0); + __ jcc(Assembler::equal, oop_empty); + __ stop("exception oop must be empty"); + __ bind(oop_empty); + + Label pc_empty; + __ cmpl(Address(thread, JavaThread::exception_pc_offset()), 0); + __ jcc(Assembler::equal, pc_empty); + __ stop("exception pc must be empty"); + __ bind(pc_empty); +#endif + + // clear the FPU stack in case any FPU results are left behind + __ empty_FPU_stack(); + + // leave activation of nmethod + __ leave(); + // store return address (is on top of stack after leave) + __ movl(exception_pc, Address(rsp, 0)); + + __ verify_oop(exception_oop); + + // save exception oop from rax, to stack before call + __ pushl(exception_oop); + + // search the exception handler address of the caller (using the return address) + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), exception_pc); + // rax,: exception handler address of the caller + + // only rax, is valid at this time, all other registers have been destroyed by the call + __ invalidate_registers(false, true, true, true, true, true); + + // move result of call into correct register + __ movl(handler_addr, rax); + + // restore exception oop in rax, (required convention of exception handler) + __ popl(exception_oop); + + __ verify_oop(exception_oop); + + // get throwing pc (= return address). + // rdx has been destroyed by the call, so it must be set again + // the pop is also necessary to simulate the effect of a ret(0) + __ popl(exception_pc); + + // verify that that there is really a valid exception in rax, + __ verify_not_null_oop(exception_oop); + + // continue at exception handler (return address removed) + // note: do *not* remove arguments when unwinding the + // activation since the caller assumes having + // all arguments on the stack when entering the + // runtime to determine the exception handler + // (GC happens at call site with arguments!) + // rax,: exception oop + // rdx: throwing pc + // rbx,: exception handler + __ jmp(handler_addr); +} + + +OopMapSet* Runtime1::generate_patching(StubAssembler* sasm, address target) { + // use the maximum number of runtime-arguments here because it is difficult to + // distinguish each RT-Call. + // Note: This number affects also the RT-Call in generate_handle_exception because + // the oop-map is shared for all calls. + const int num_rt_args = 2; // thread + dummy + + DeoptimizationBlob* deopt_blob = SharedRuntime::deopt_blob(); + assert(deopt_blob != NULL, "deoptimization blob must have been created"); + + OopMap* oop_map = save_live_registers(sasm, num_rt_args); + + __ pushl(rax); // push dummy + + const Register thread = rdi; // is callee-saved register (Visual C++ calling conventions) + // push java thread (becomes first argument of C function) + __ get_thread(thread); + __ pushl(thread); + __ set_last_Java_frame(thread, noreg, rbp, NULL); + // do the call + __ call(RuntimeAddress(target)); + OopMapSet* oop_maps = new OopMapSet(); + oop_maps->add_gc_map(__ offset(), oop_map); + // verify callee-saved register +#ifdef ASSERT + guarantee(thread != rax, "change this code"); + __ pushl(rax); + { Label L; + __ get_thread(rax); + __ cmpl(thread, rax); + __ jcc(Assembler::equal, L); + __ stop("StubAssembler::call_RT: rdi not callee saved?"); + __ bind(L); + } + __ popl(rax); +#endif + __ reset_last_Java_frame(thread, true, false); + __ popl(rcx); // discard thread arg + __ popl(rcx); // discard dummy + + // check for pending exceptions + { Label L; + __ cmpl(Address(thread, Thread::pending_exception_offset()), NULL_WORD); + __ jcc(Assembler::equal, L); + // exception pending => remove activation and forward to exception handler + + __ testl(rax, rax); // have we deoptimized? + __ jump_cc(Assembler::equal, + RuntimeAddress(Runtime1::entry_for(Runtime1::forward_exception_id))); + + // the deopt blob expects exceptions in the special fields of + // JavaThread, so copy and clear pending exception. + + // load and clear pending exception + __ movl(rax, Address(thread, Thread::pending_exception_offset())); + __ movl(Address(thread, Thread::pending_exception_offset()), NULL_WORD); + + // check that there is really a valid exception + __ verify_not_null_oop(rax); + + // load throwing pc: this is the return address of the stub + __ movl(rdx, Address(rsp, return_off * BytesPerWord)); + +#ifdef ASSERT + // check that fields in JavaThread for exception oop and issuing pc are empty + Label oop_empty; + __ cmpoop(Address(thread, JavaThread::exception_oop_offset()), 0); + __ jcc(Assembler::equal, oop_empty); + __ stop("exception oop must be empty"); + __ bind(oop_empty); + + Label pc_empty; + __ cmpl(Address(thread, JavaThread::exception_pc_offset()), 0); + __ jcc(Assembler::equal, pc_empty); + __ stop("exception pc must be empty"); + __ bind(pc_empty); +#endif + + // store exception oop and throwing pc to JavaThread + __ movl(Address(thread, JavaThread::exception_oop_offset()), rax); + __ movl(Address(thread, JavaThread::exception_pc_offset()), rdx); + + restore_live_registers(sasm); + + __ leave(); + __ addl(rsp, 4); // remove return address from stack + + // Forward the exception directly to deopt blob. We can blow no + // registers and must leave throwing pc on the stack. A patch may + // have values live in registers so the entry point with the + // exception in tls. + __ jump(RuntimeAddress(deopt_blob->unpack_with_exception_in_tls())); + + __ bind(L); + } + + + // Runtime will return true if the nmethod has been deoptimized during + // the patching process. In that case we must do a deopt reexecute instead. + + Label reexecuteEntry, cont; + + __ testl(rax, rax); // have we deoptimized? + __ jcc(Assembler::equal, cont); // no + + // Will reexecute. Proper return address is already on the stack we just restore + // registers, pop all of our frame but the return address and jump to the deopt blob + restore_live_registers(sasm); + __ leave(); + __ jump(RuntimeAddress(deopt_blob->unpack_with_reexecution())); + + __ bind(cont); + restore_live_registers(sasm); + __ leave(); + __ ret(0); + + return oop_maps; + +} + + +OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { + + // for better readability + const bool must_gc_arguments = true; + const bool dont_gc_arguments = false; + + // default value; overwritten for some optimized stubs that are called from methods that do not use the fpu + bool save_fpu_registers = true; + + // stub code & info for the different stubs + OopMapSet* oop_maps = NULL; + switch (id) { + case forward_exception_id: + { + // we're handling an exception in the context of a compiled + // frame. The registers have been saved in the standard + // places. Perform an exception lookup in the caller and + // dispatch to the handler if found. Otherwise unwind and + // dispatch to the callers exception handler. + + const Register thread = rdi; + const Register exception_oop = rax; + const Register exception_pc = rdx; + + // load pending exception oop into rax, + __ movl(exception_oop, Address(thread, Thread::pending_exception_offset())); + // clear pending exception + __ movl(Address(thread, Thread::pending_exception_offset()), NULL_WORD); + + // load issuing PC (the return address for this stub) into rdx + __ movl(exception_pc, Address(rbp, 1*BytesPerWord)); + + // make sure that the vm_results are cleared (may be unnecessary) + __ movl(Address(thread, JavaThread::vm_result_offset()), NULL_WORD); + __ movl(Address(thread, JavaThread::vm_result_2_offset()), NULL_WORD); + + // verify that that there is really a valid exception in rax, + __ verify_not_null_oop(exception_oop); + + + oop_maps = new OopMapSet(); + OopMap* oop_map = generate_oop_map(sasm, 1); + generate_handle_exception(sasm, oop_maps, oop_map); + __ stop("should not reach here"); + } + break; + + case new_instance_id: + case fast_new_instance_id: + case fast_new_instance_init_check_id: + { + Register klass = rdx; // Incoming + Register obj = rax; // Result + + if (id == new_instance_id) { + __ set_info("new_instance", dont_gc_arguments); + } else if (id == fast_new_instance_id) { + __ set_info("fast new_instance", dont_gc_arguments); + } else { + assert(id == fast_new_instance_init_check_id, "bad StubID"); + __ set_info("fast new_instance init check", dont_gc_arguments); + } + + if ((id == fast_new_instance_id || id == fast_new_instance_init_check_id) && + UseTLAB && FastTLABRefill) { + Label slow_path; + Register obj_size = rcx; + Register t1 = rbx; + Register t2 = rsi; + assert_different_registers(klass, obj, obj_size, t1, t2); + + __ pushl(rdi); + __ pushl(rbx); + + if (id == fast_new_instance_init_check_id) { + // make sure the klass is initialized + __ cmpl(Address(klass, instanceKlass::init_state_offset_in_bytes() + sizeof(oopDesc)), instanceKlass::fully_initialized); + __ jcc(Assembler::notEqual, slow_path); + } + +#ifdef ASSERT + // assert object can be fast path allocated + { + Label ok, not_ok; + __ movl(obj_size, Address(klass, Klass::layout_helper_offset_in_bytes() + sizeof(oopDesc))); + __ cmpl(obj_size, 0); // make sure it's an instance (LH > 0) + __ jcc(Assembler::lessEqual, not_ok); + __ testl(obj_size, Klass::_lh_instance_slow_path_bit); + __ jcc(Assembler::zero, ok); + __ bind(not_ok); + __ stop("assert(can be fast path allocated)"); + __ should_not_reach_here(); + __ bind(ok); + } +#endif // ASSERT + + // if we got here then the TLAB allocation failed, so try + // refilling the TLAB or allocating directly from eden. + Label retry_tlab, try_eden; + __ tlab_refill(retry_tlab, try_eden, slow_path); // does not destroy rdx (klass) + + __ bind(retry_tlab); + + // get the instance size + __ movl(obj_size, Address(klass, klassOopDesc::header_size() * HeapWordSize + Klass::layout_helper_offset_in_bytes())); + __ tlab_allocate(obj, obj_size, 0, t1, t2, slow_path); + __ initialize_object(obj, klass, obj_size, 0, t1, t2); + __ verify_oop(obj); + __ popl(rbx); + __ popl(rdi); + __ ret(0); + + __ bind(try_eden); + // get the instance size + __ movl(obj_size, Address(klass, klassOopDesc::header_size() * HeapWordSize + Klass::layout_helper_offset_in_bytes())); + __ eden_allocate(obj, obj_size, 0, t1, slow_path); + __ initialize_object(obj, klass, obj_size, 0, t1, t2); + __ verify_oop(obj); + __ popl(rbx); + __ popl(rdi); + __ ret(0); + + __ bind(slow_path); + __ popl(rbx); + __ popl(rdi); + } + + __ enter(); + OopMap* map = save_live_registers(sasm, 2); + int call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_instance), klass); + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, map); + restore_live_registers_except_rax(sasm); + __ verify_oop(obj); + __ leave(); + __ ret(0); + + // rax,: new instance + } + + break; + +#ifdef TIERED + case counter_overflow_id: + { + Register bci = rax; + __ enter(); + OopMap* map = save_live_registers(sasm, 2); + // Retrieve bci + __ movl(bci, Address(rbp, 2*BytesPerWord)); + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, counter_overflow), bci); + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, map); + restore_live_registers(sasm); + __ leave(); + __ ret(0); + } + break; +#endif // TIERED + + case new_type_array_id: + case new_object_array_id: + { + Register length = rbx; // Incoming + Register klass = rdx; // Incoming + Register obj = rax; // Result + + if (id == new_type_array_id) { + __ set_info("new_type_array", dont_gc_arguments); + } else { + __ set_info("new_object_array", dont_gc_arguments); + } + +#ifdef ASSERT + // assert object type is really an array of the proper kind + { + Label ok; + Register t0 = obj; + __ movl(t0, Address(klass, Klass::layout_helper_offset_in_bytes() + sizeof(oopDesc))); + __ sarl(t0, Klass::_lh_array_tag_shift); + int tag = ((id == new_type_array_id) + ? Klass::_lh_array_tag_type_value + : Klass::_lh_array_tag_obj_value); + __ cmpl(t0, tag); + __ jcc(Assembler::equal, ok); + __ stop("assert(is an array klass)"); + __ should_not_reach_here(); + __ bind(ok); + } +#endif // ASSERT + + if (UseTLAB && FastTLABRefill) { + Register arr_size = rsi; + Register t1 = rcx; // must be rcx for use as shift count + Register t2 = rdi; + Label slow_path; + assert_different_registers(length, klass, obj, arr_size, t1, t2); + + // check that array length is small enough for fast path. + __ cmpl(length, C1_MacroAssembler::max_array_allocation_length); + __ jcc(Assembler::above, slow_path); + + // if we got here then the TLAB allocation failed, so try + // refilling the TLAB or allocating directly from eden. + Label retry_tlab, try_eden; + __ tlab_refill(retry_tlab, try_eden, slow_path); // preserves rbx, & rdx + + __ bind(retry_tlab); + + // get the allocation size: round_up(hdr + length << (layout_helper & 0x1F)) + __ movl(t1, Address(klass, klassOopDesc::header_size() * HeapWordSize + Klass::layout_helper_offset_in_bytes())); + __ movl(arr_size, length); + assert(t1 == rcx, "fixed register usage"); + __ shll(arr_size /* by t1=rcx, mod 32 */); + __ shrl(t1, Klass::_lh_header_size_shift); + __ andl(t1, Klass::_lh_header_size_mask); + __ addl(arr_size, t1); + __ addl(arr_size, MinObjAlignmentInBytesMask); // align up + __ andl(arr_size, ~MinObjAlignmentInBytesMask); + + __ tlab_allocate(obj, arr_size, 0, t1, t2, slow_path); // preserves arr_size + + __ initialize_header(obj, klass, length, t1, t2); + __ movb(t1, Address(klass, klassOopDesc::header_size() * HeapWordSize + Klass::layout_helper_offset_in_bytes() + (Klass::_lh_header_size_shift / BitsPerByte))); + assert(Klass::_lh_header_size_shift % BitsPerByte == 0, "bytewise"); + assert(Klass::_lh_header_size_mask <= 0xFF, "bytewise"); + __ andl(t1, Klass::_lh_header_size_mask); + __ subl(arr_size, t1); // body length + __ addl(t1, obj); // body start + __ initialize_body(t1, arr_size, 0, t2); + __ verify_oop(obj); + __ ret(0); + + __ bind(try_eden); + // get the allocation size: round_up(hdr + length << (layout_helper & 0x1F)) + __ movl(t1, Address(klass, klassOopDesc::header_size() * HeapWordSize + Klass::layout_helper_offset_in_bytes())); + __ movl(arr_size, length); + assert(t1 == rcx, "fixed register usage"); + __ shll(arr_size /* by t1=rcx, mod 32 */); + __ shrl(t1, Klass::_lh_header_size_shift); + __ andl(t1, Klass::_lh_header_size_mask); + __ addl(arr_size, t1); + __ addl(arr_size, MinObjAlignmentInBytesMask); // align up + __ andl(arr_size, ~MinObjAlignmentInBytesMask); + + __ eden_allocate(obj, arr_size, 0, t1, slow_path); // preserves arr_size + + __ initialize_header(obj, klass, length, t1, t2); + __ movb(t1, Address(klass, klassOopDesc::header_size() * HeapWordSize + Klass::layout_helper_offset_in_bytes() + (Klass::_lh_header_size_shift / BitsPerByte))); + assert(Klass::_lh_header_size_shift % BitsPerByte == 0, "bytewise"); + assert(Klass::_lh_header_size_mask <= 0xFF, "bytewise"); + __ andl(t1, Klass::_lh_header_size_mask); + __ subl(arr_size, t1); // body length + __ addl(t1, obj); // body start + __ initialize_body(t1, arr_size, 0, t2); + __ verify_oop(obj); + __ ret(0); + + __ bind(slow_path); + } + + __ enter(); + OopMap* map = save_live_registers(sasm, 3); + int call_offset; + if (id == new_type_array_id) { + call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_type_array), klass, length); + } else { + call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_object_array), klass, length); + } + + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, map); + restore_live_registers_except_rax(sasm); + + __ verify_oop(obj); + __ leave(); + __ ret(0); + + // rax,: new array + } + break; + + case new_multi_array_id: + { StubFrame f(sasm, "new_multi_array", dont_gc_arguments); + // rax,: klass + // rbx,: rank + // rcx: address of 1st dimension + OopMap* map = save_live_registers(sasm, 4); + int call_offset = __ call_RT(rax, noreg, CAST_FROM_FN_PTR(address, new_multi_array), rax, rbx, rcx); + + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, map); + restore_live_registers_except_rax(sasm); + + // rax,: new multi array + __ verify_oop(rax); + } + break; + + case register_finalizer_id: + { + __ set_info("register_finalizer", dont_gc_arguments); + + // The object is passed on the stack and we haven't pushed a + // frame yet so it's one work away from top of stack. + __ movl(rax, Address(rsp, 1 * BytesPerWord)); + __ verify_oop(rax); + + // load the klass and check the has finalizer flag + Label register_finalizer; + Register t = rsi; + __ movl(t, Address(rax, oopDesc::klass_offset_in_bytes())); + __ movl(t, Address(t, Klass::access_flags_offset_in_bytes() + sizeof(oopDesc))); + __ testl(t, JVM_ACC_HAS_FINALIZER); + __ jcc(Assembler::notZero, register_finalizer); + __ ret(0); + + __ bind(register_finalizer); + __ enter(); + OopMap* oop_map = save_live_registers(sasm, 2 /*num_rt_args */); + int call_offset = __ call_RT(noreg, noreg, + CAST_FROM_FN_PTR(address, SharedRuntime::register_finalizer), rax); + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, oop_map); + + // Now restore all the live registers + restore_live_registers(sasm); + + __ leave(); + __ ret(0); + } + break; + + case throw_range_check_failed_id: + { StubFrame f(sasm, "range_check_failed", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_range_check_exception), true); + } + break; + + case throw_index_exception_id: + { StubFrame f(sasm, "index_range_check_failed", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_index_exception), true); + } + break; + + case throw_div0_exception_id: + { StubFrame f(sasm, "throw_div0_exception", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_div0_exception), false); + } + break; + + case throw_null_pointer_exception_id: + { StubFrame f(sasm, "throw_null_pointer_exception", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_null_pointer_exception), false); + } + break; + + case handle_exception_nofpu_id: + save_fpu_registers = false; + // fall through + case handle_exception_id: + { StubFrame f(sasm, "handle_exception", dont_gc_arguments); + oop_maps = new OopMapSet(); + OopMap* oop_map = save_live_registers(sasm, 1, save_fpu_registers); + generate_handle_exception(sasm, oop_maps, oop_map, save_fpu_registers); + } + break; + + case unwind_exception_id: + { __ set_info("unwind_exception", dont_gc_arguments); + // note: no stubframe since we are about to leave the current + // activation and we are calling a leaf VM function only. + generate_unwind_exception(sasm); + } + break; + + case throw_array_store_exception_id: + { StubFrame f(sasm, "throw_array_store_exception", dont_gc_arguments); + // tos + 0: link + // + 1: return address + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_array_store_exception), false); + } + break; + + case throw_class_cast_exception_id: + { StubFrame f(sasm, "throw_class_cast_exception", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_class_cast_exception), true); + } + break; + + case throw_incompatible_class_change_error_id: + { StubFrame f(sasm, "throw_incompatible_class_cast_exception", dont_gc_arguments); + oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_incompatible_class_change_error), false); + } + break; + + case slow_subtype_check_id: + { + enum layout { + rax_off, + rcx_off, + rsi_off, + rdi_off, + saved_rbp_off, + return_off, + sub_off, + super_off, + framesize + }; + + __ set_info("slow_subtype_check", dont_gc_arguments); + __ pushl(rdi); + __ pushl(rsi); + __ pushl(rcx); + __ pushl(rax); + __ movl(rsi, Address(rsp, (super_off - 1) * BytesPerWord)); // super + __ movl(rax, Address(rsp, (sub_off - 1) * BytesPerWord)); // sub + + __ movl(rdi,Address(rsi,sizeof(oopDesc) + Klass::secondary_supers_offset_in_bytes())); + __ movl(rcx,Address(rdi,arrayOopDesc::length_offset_in_bytes())); + __ addl(rdi,arrayOopDesc::base_offset_in_bytes(T_OBJECT)); + + Label miss; + __ repne_scan(); + __ jcc(Assembler::notEqual, miss); + __ movl(Address(rsi,sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes()), rax); + __ movl(Address(rsp, (super_off - 1) * BytesPerWord), 1); // result + __ popl(rax); + __ popl(rcx); + __ popl(rsi); + __ popl(rdi); + __ ret(0); + + __ bind(miss); + __ movl(Address(rsp, (super_off - 1) * BytesPerWord), 0); // result + __ popl(rax); + __ popl(rcx); + __ popl(rsi); + __ popl(rdi); + __ ret(0); + } + break; + + case monitorenter_nofpu_id: + save_fpu_registers = false; + // fall through + case monitorenter_id: + { + StubFrame f(sasm, "monitorenter", dont_gc_arguments); + OopMap* map = save_live_registers(sasm, 3, save_fpu_registers); + + f.load_argument(1, rax); // rax,: object + f.load_argument(0, rbx); // rbx,: lock address + + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, monitorenter), rax, rbx); + + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, map); + restore_live_registers(sasm, save_fpu_registers); + } + break; + + case monitorexit_nofpu_id: + save_fpu_registers = false; + // fall through + case monitorexit_id: + { + StubFrame f(sasm, "monitorexit", dont_gc_arguments); + OopMap* map = save_live_registers(sasm, 2, save_fpu_registers); + + f.load_argument(0, rax); // rax,: lock address + + // note: really a leaf routine but must setup last java sp + // => use call_RT for now (speed can be improved by + // doing last java sp setup manually) + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, monitorexit), rax); + + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, map); + restore_live_registers(sasm, save_fpu_registers); + + } + break; + + case access_field_patching_id: + { StubFrame f(sasm, "access_field_patching", dont_gc_arguments); + // we should set up register map + oop_maps = generate_patching(sasm, CAST_FROM_FN_PTR(address, access_field_patching)); + } + break; + + case load_klass_patching_id: + { StubFrame f(sasm, "load_klass_patching", dont_gc_arguments); + // we should set up register map + oop_maps = generate_patching(sasm, CAST_FROM_FN_PTR(address, move_klass_patching)); + } + break; + + case jvmti_exception_throw_id: + { // rax,: exception oop + StubFrame f(sasm, "jvmti_exception_throw", dont_gc_arguments); + // Preserve all registers across this potentially blocking call + const int num_rt_args = 2; // thread, exception oop + OopMap* map = save_live_registers(sasm, num_rt_args); + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, Runtime1::post_jvmti_exception_throw), rax); + oop_maps = new OopMapSet(); + oop_maps->add_gc_map(call_offset, map); + restore_live_registers(sasm); + } + break; + + case dtrace_object_alloc_id: + { // rax,: object + StubFrame f(sasm, "dtrace_object_alloc", dont_gc_arguments); + // we can't gc here so skip the oopmap but make sure that all + // the live registers get saved. + save_live_registers(sasm, 1); + + __ pushl(rax); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc))); + __ popl(rax); + + restore_live_registers(sasm); + } + break; + + case fpu2long_stub_id: + { + // rax, and rdx are destroyed, but should be free since the result is returned there + // preserve rsi,ecx + __ pushl(rsi); + __ pushl(rcx); + + // check for NaN + Label return0, do_return, return_min_jlong, do_convert; + + Address value_high_word(rsp, 8); + Address value_low_word(rsp, 4); + Address result_high_word(rsp, 16); + Address result_low_word(rsp, 12); + + __ subl(rsp, 20); + __ fst_d(value_low_word); + __ movl(rax, value_high_word); + __ andl(rax, 0x7ff00000); + __ cmpl(rax, 0x7ff00000); + __ jcc(Assembler::notEqual, do_convert); + __ movl(rax, value_high_word); + __ andl(rax, 0xfffff); + __ orl(rax, value_low_word); + __ jcc(Assembler::notZero, return0); + + __ bind(do_convert); + __ fnstcw(Address(rsp, 0)); + __ movzxw(rax, Address(rsp, 0)); + __ orl(rax, 0xc00); + __ movw(Address(rsp, 2), rax); + __ fldcw(Address(rsp, 2)); + __ fwait(); + __ fistp_d(result_low_word); + __ fldcw(Address(rsp, 0)); + __ fwait(); + __ movl(rax, result_low_word); + __ movl(rdx, result_high_word); + __ movl(rcx, rax); + // What the heck is the point of the next instruction??? + __ xorl(rcx, 0x0); + __ movl(rsi, 0x80000000); + __ xorl(rsi, rdx); + __ orl(rcx, rsi); + __ jcc(Assembler::notEqual, do_return); + __ fldz(); + __ fcomp_d(value_low_word); + __ fnstsw_ax(); + __ sahf(); + __ jcc(Assembler::above, return_min_jlong); + // return max_jlong + __ movl(rdx, 0x7fffffff); + __ movl(rax, 0xffffffff); + __ jmp(do_return); + + __ bind(return_min_jlong); + __ movl(rdx, 0x80000000); + __ xorl(rax, rax); + __ jmp(do_return); + + __ bind(return0); + __ fpop(); + __ xorl(rdx,rdx); + __ xorl(rax,rax); + + __ bind(do_return); + __ addl(rsp, 20); + __ popl(rcx); + __ popl(rsi); + __ ret(0); + } + break; + + default: + { StubFrame f(sasm, "unimplemented entry", dont_gc_arguments); + __ movl(rax, (int)id); + __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, unimplemented_entry), rax); + __ should_not_reach_here(); + } + break; + } + return oop_maps; +} + +#undef __ diff --git a/hotspot/src/cpu/x86/vm/c1_globals_x86.hpp b/hotspot/src/cpu/x86/vm/c1_globals_x86.hpp new file mode 100644 index 00000000000..659dcca4732 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c1_globals_x86.hpp @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for platform dependent flags used by the client compiler. +// (see c1_globals.hpp) +// + +#ifndef TIERED +define_pd_global(bool, BackgroundCompilation, true ); +define_pd_global(bool, UseTLAB, true ); +define_pd_global(bool, ResizeTLAB, true ); +define_pd_global(bool, InlineIntrinsics, true ); +define_pd_global(bool, PreferInterpreterNativeStubs, false); +define_pd_global(bool, ProfileTraps, false); +define_pd_global(bool, UseOnStackReplacement, true ); +define_pd_global(bool, TieredCompilation, false); +define_pd_global(intx, CompileThreshold, 1500 ); +define_pd_global(intx, Tier2CompileThreshold, 1500 ); +define_pd_global(intx, Tier3CompileThreshold, 2500 ); +define_pd_global(intx, Tier4CompileThreshold, 4500 ); + +define_pd_global(intx, BackEdgeThreshold, 100000); +define_pd_global(intx, Tier2BackEdgeThreshold, 100000); +define_pd_global(intx, Tier3BackEdgeThreshold, 100000); +define_pd_global(intx, Tier4BackEdgeThreshold, 100000); + +define_pd_global(intx, OnStackReplacePercentage, 933 ); +define_pd_global(intx, FreqInlineSize, 325 ); +define_pd_global(intx, NewRatio, 12 ); +define_pd_global(intx, NewSizeThreadIncrease, 4*K ); +define_pd_global(intx, InitialCodeCacheSize, 160*K); +define_pd_global(intx, ReservedCodeCacheSize, 32*M ); +define_pd_global(bool, ProfileInterpreter, false); +define_pd_global(intx, CodeCacheExpansionSize, 32*K ); +define_pd_global(uintx,CodeCacheMinBlockLength, 1); +define_pd_global(uintx, PermSize, 12*M ); +define_pd_global(uintx, MaxPermSize, 64*M ); +define_pd_global(bool, NeverActAsServerClassMachine, true); +define_pd_global(uintx, DefaultMaxRAM, 1*G); +define_pd_global(bool, CICompileOSR, true ); +#endif // TIERED +define_pd_global(bool, UseTypeProfile, false); +define_pd_global(bool, RoundFPResults, true ); + + +define_pd_global(bool, LIRFillDelaySlots, false); +define_pd_global(bool, OptimizeSinglePrecision, true); +define_pd_global(bool, CSEArrayLength, false); +define_pd_global(bool, TwoOperandLIRForm, true); + + +define_pd_global(intx, SafepointPollOffset, 256); diff --git a/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp b/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp new file mode 100644 index 00000000000..6b3d7250442 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for platform dependent flags used by the server compiler. +// (see c2_globals.hpp). Alpha-sorted. + +define_pd_global(bool, BackgroundCompilation, true); +define_pd_global(bool, UseTLAB, true); +define_pd_global(bool, ResizeTLAB, true); +define_pd_global(bool, CICompileOSR, true); +define_pd_global(bool, InlineIntrinsics, true); +define_pd_global(bool, PreferInterpreterNativeStubs, false); +define_pd_global(bool, ProfileTraps, true); +define_pd_global(bool, UseOnStackReplacement, true); +#ifdef CC_INTERP +define_pd_global(bool, ProfileInterpreter, false); +#else +define_pd_global(bool, ProfileInterpreter, true); +#endif // CC_INTERP +define_pd_global(bool, TieredCompilation, false); +#ifdef TIERED +define_pd_global(intx, CompileThreshold, 1000); +#else +define_pd_global(intx, CompileThreshold, 10000); +#endif // TIERED +define_pd_global(intx, Tier2CompileThreshold, 10000); +define_pd_global(intx, Tier3CompileThreshold, 20000 ); +define_pd_global(intx, Tier4CompileThreshold, 40000 ); + +define_pd_global(intx, BackEdgeThreshold, 100000); +define_pd_global(intx, Tier2BackEdgeThreshold, 100000); +define_pd_global(intx, Tier3BackEdgeThreshold, 100000); +define_pd_global(intx, Tier4BackEdgeThreshold, 100000); + +define_pd_global(intx, OnStackReplacePercentage, 140); +define_pd_global(intx, ConditionalMoveLimit, 3); +define_pd_global(intx, FLOATPRESSURE, 6); +define_pd_global(intx, FreqInlineSize, 325); +#ifdef AMD64 +define_pd_global(intx, INTPRESSURE, 13); +define_pd_global(intx, InteriorEntryAlignment, 16); +define_pd_global(intx, NewRatio, 2); +define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K)); +define_pd_global(intx, LoopUnrollLimit, 60); +// InitialCodeCacheSize derived from specjbb2000 run. +define_pd_global(intx, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize +define_pd_global(intx, CodeCacheExpansionSize, 64*K); + +// Ergonomics related flags +define_pd_global(uintx, DefaultMaxRAM, 32*G); +#else +define_pd_global(intx, INTPRESSURE, 6); +define_pd_global(intx, InteriorEntryAlignment, 4); +define_pd_global(intx, NewRatio, 8); // Design center runs on 1.3.1 +define_pd_global(intx, NewSizeThreadIncrease, 4*K); +define_pd_global(intx, LoopUnrollLimit, 50); // Design center runs on 1.3.1 +// InitialCodeCacheSize derived from specjbb2000 run. +define_pd_global(intx, InitialCodeCacheSize, 2304*K); // Integral multiple of CodeCacheExpansionSize +define_pd_global(intx, CodeCacheExpansionSize, 32*K); + +// Ergonomics related flags +define_pd_global(uintx, DefaultMaxRAM, 1*G); +#endif // AMD64 +define_pd_global(intx, OptoLoopAlignment, 16); +define_pd_global(intx, RegisterCostAreaRatio, 16000); + +// Peephole and CISC spilling both break the graph, and so makes the +// scheduler sick. +define_pd_global(bool, OptoPeephole, true); +define_pd_global(bool, UseCISCSpill, true); +define_pd_global(bool, OptoScheduling, false); +define_pd_global(bool, OptoBundling, false); + +define_pd_global(intx, ReservedCodeCacheSize, 48*M); +define_pd_global(uintx,CodeCacheMinBlockLength, 4); + +// Heap related flags +define_pd_global(uintx, PermSize, ScaleForWordSize(16*M)); +define_pd_global(uintx, MaxPermSize, ScaleForWordSize(64*M)); + +// Ergonomics related flags +define_pd_global(bool, NeverActAsServerClassMachine, false); diff --git a/hotspot/src/cpu/x86/vm/c2_init_x86.cpp b/hotspot/src/cpu/x86/vm/c2_init_x86.cpp new file mode 100644 index 00000000000..d59ae1e167c --- /dev/null +++ b/hotspot/src/cpu/x86/vm/c2_init_x86.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c2_init_x86.cpp.incl" + +// processor dependent initialization for i486 + +void Compile::pd_compiler2_init() { + guarantee(CodeEntryAlignment >= InteriorEntryAlignment, "" ); + // QQQ presumably all 64bit cpu's support this. Seems like the ifdef could + // simply be left out. +#ifndef AMD64 + if (!VM_Version::supports_cmov()) { + ConditionalMoveLimit = 0; + } +#endif // AMD64 +} diff --git a/hotspot/src/cpu/x86/vm/codeBuffer_x86.hpp b/hotspot/src/cpu/x86/vm/codeBuffer_x86.hpp new file mode 100644 index 00000000000..4c02862d2ed --- /dev/null +++ b/hotspot/src/cpu/x86/vm/codeBuffer_x86.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +private: + void pd_initialize() {} + +public: + void flush_bundle(bool start_new_bundle) {} diff --git a/hotspot/src/cpu/x86/vm/copy_x86.hpp b/hotspot/src/cpu/x86/vm/copy_x86.hpp new file mode 100644 index 00000000000..93dd363aafa --- /dev/null +++ b/hotspot/src/cpu/x86/vm/copy_x86.hpp @@ -0,0 +1,60 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline functions for memory copy and fill. + +// Contains inline asm implementations +#include "incls/_copy_pd.inline.hpp.incl" + +static void pd_fill_to_words(HeapWord* tohw, size_t count, juint value) { +#ifdef AMD64 + julong* to = (julong*) tohw; + julong v = ((julong) value << 32) | value; + while (count-- > 0) { + *to++ = v; + } +#else + juint* to = (juint*)tohw; + count *= HeapWordSize / BytesPerInt; + while (count-- > 0) { + *to++ = value; + } +#endif // AMD64 +} + +static void pd_fill_to_aligned_words(HeapWord* tohw, size_t count, juint value) { + pd_fill_to_words(tohw, count, value); +} + +static void pd_fill_to_bytes(void* to, size_t count, jubyte value) { + (void)memset(to, value, count); +} + +static void pd_zero_to_words(HeapWord* tohw, size_t count) { + pd_fill_to_words(tohw, count, 0); +} + +static void pd_zero_to_bytes(void* to, size_t count) { + (void)memset(to, 0, count); +} diff --git a/hotspot/src/cpu/x86/vm/cppInterpreterGenerator_x86.hpp b/hotspot/src/cpu/x86/vm/cppInterpreterGenerator_x86.hpp new file mode 100644 index 00000000000..e4a45f141bc --- /dev/null +++ b/hotspot/src/cpu/x86/vm/cppInterpreterGenerator_x86.hpp @@ -0,0 +1,47 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + protected: + +#if 0 + address generate_asm_interpreter_entry(bool synchronized); + address generate_native_entry(bool synchronized); + address generate_abstract_entry(void); + address generate_math_entry(AbstractInterpreter::MethodKind kind); + address generate_empty_entry(void); + address generate_accessor_entry(void); + void lock_method(void); + void generate_stack_overflow_check(void); + + void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); + void generate_counter_overflow(Label* do_continue); +#endif + + void generate_more_monitors(); + void generate_deopt_handling(); + address generate_interpreter_frame_manager(bool synchronized); // C++ interpreter only + void generate_compute_interpreter_state(const Register state, + const Register prev_state, + const Register sender_sp, + bool native); // C++ interpreter only diff --git a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp new file mode 100644 index 00000000000..89c497de7a4 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp @@ -0,0 +1,2332 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_cppInterpreter_x86.cpp.incl" + +#ifdef CC_INTERP + +// Routine exists to make tracebacks look decent in debugger +// while we are recursed in the frame manager/c++ interpreter. +// We could use an address in the frame manager but having +// frames look natural in the debugger is a plus. +extern "C" void RecursiveInterpreterActivation(interpreterState istate ) +{ + // + ShouldNotReachHere(); +} + + +#define __ _masm-> +#define STATE(field_name) (Address(state, byte_offset_of(BytecodeInterpreter, field_name))) + +Label fast_accessor_slow_entry_path; // fast accessor methods need to be able to jmp to unsynchronized + // c++ interpreter entry point this holds that entry point label. + +// NEEDED for JVMTI? +// address AbstractInterpreter::_remove_activation_preserving_args_entry; + +static address unctrap_frame_manager_entry = NULL; + +static address deopt_frame_manager_return_atos = NULL; +static address deopt_frame_manager_return_btos = NULL; +static address deopt_frame_manager_return_itos = NULL; +static address deopt_frame_manager_return_ltos = NULL; +static address deopt_frame_manager_return_ftos = NULL; +static address deopt_frame_manager_return_dtos = NULL; +static address deopt_frame_manager_return_vtos = NULL; + +int AbstractInterpreter::BasicType_as_index(BasicType type) { + int i = 0; + switch (type) { + case T_BOOLEAN: i = 0; break; + case T_CHAR : i = 1; break; + case T_BYTE : i = 2; break; + case T_SHORT : i = 3; break; + case T_INT : i = 4; break; + case T_VOID : i = 5; break; + case T_FLOAT : i = 8; break; + case T_LONG : i = 9; break; + case T_DOUBLE : i = 6; break; + case T_OBJECT : // fall through + case T_ARRAY : i = 7; break; + default : ShouldNotReachHere(); + } + assert(0 <= i && i < AbstractInterpreter::number_of_result_handlers, "index out of bounds"); + return i; +} + +// Is this pc anywhere within code owned by the interpreter? +// This only works for pc that might possibly be exposed to frame +// walkers. It clearly misses all of the actual c++ interpreter +// implementation +bool CppInterpreter::contains(address pc) { + return (_code->contains(pc) || + pc == CAST_FROM_FN_PTR(address, RecursiveInterpreterActivation)); +} + + +address CppInterpreterGenerator::generate_result_handler_for(BasicType type) { + const Register state = rsi; // current activation object, valid on entry + address entry = __ pc(); + switch (type) { + case T_BOOLEAN: __ c2bool(rax); break; + case T_CHAR : __ andl(rax, 0xFFFF); break; + case T_BYTE : __ sign_extend_byte (rax); break; + case T_SHORT : __ sign_extend_short(rax); break; + case T_VOID : // fall thru + case T_LONG : // fall thru + case T_INT : /* nothing to do */ break; + case T_DOUBLE : + case T_FLOAT : + { const Register t = InterpreterRuntime::SignatureHandlerGenerator::temp(); + __ popl(t); // remove return address first + __ pop_dtos_to_rsp(); + // Must return a result for interpreter or compiler. In SSE + // mode, results are returned in xmm0 and the FPU stack must + // be empty. + if (type == T_FLOAT && UseSSE >= 1) { + // Load ST0 + __ fld_d(Address(rsp, 0)); + // Store as float and empty fpu stack + __ fstp_s(Address(rsp, 0)); + // and reload + __ movflt(xmm0, Address(rsp, 0)); + } else if (type == T_DOUBLE && UseSSE >= 2 ) { + __ movdbl(xmm0, Address(rsp, 0)); + } else { + // restore ST0 + __ fld_d(Address(rsp, 0)); + } + // and pop the temp + __ addl(rsp, 2 * wordSize); + __ pushl(t); // restore return address + } + break; + case T_OBJECT : + // retrieve result from frame + __ movl(rax, STATE(_oop_temp)); + // and verify it + __ verify_oop(rax); + break; + default : ShouldNotReachHere(); + } + __ ret(0); // return from result handler + return entry; +} + +// tosca based result to c++ interpreter stack based result. +// Result goes to top of native stack. + +#undef EXTEND // SHOULD NOT BE NEEDED +address CppInterpreterGenerator::generate_tosca_to_stack_converter(BasicType type) { + // A result is in the tosca (abi result) from either a native method call or compiled + // code. Place this result on the java expression stack so C++ interpreter can use it. + address entry = __ pc(); + + const Register t = InterpreterRuntime::SignatureHandlerGenerator::temp(); + __ popl(t); // remove return address first + switch (type) { + case T_VOID: + break; + case T_BOOLEAN: +#ifdef EXTEND + __ c2bool(rax); +#endif + __ pushl(rax); + break; + case T_CHAR : +#ifdef EXTEND + __ andl(rax, 0xFFFF); +#endif + __ pushl(rax); + break; + case T_BYTE : +#ifdef EXTEND + __ sign_extend_byte (rax); +#endif + __ pushl(rax); + break; + case T_SHORT : +#ifdef EXTEND + __ sign_extend_short(rax); +#endif + __ pushl(rax); + break; + case T_LONG : + __ pushl(rdx); + __ pushl(rax); + break; + case T_INT : + __ pushl(rax); + break; + case T_FLOAT : + // Result is in ST(0) + if ( UseSSE < 1) { + __ push(ftos); // and save it + } else { + __ subl(rsp, wordSize); + __ movflt(Address(rsp, 0), xmm0); + } + break; + case T_DOUBLE : + if ( UseSSE < 2 ) { + __ push(dtos); // put ST0 on java stack + } else { + __ subl(rsp, 2*wordSize); + __ movdbl(Address(rsp, 0), xmm0); + } + break; + case T_OBJECT : + __ verify_oop(rax); // verify it + __ pushl(rax); + break; + default : ShouldNotReachHere(); + } + __ jmp(t); // return from result handler + return entry; +} + +address CppInterpreterGenerator::generate_stack_to_stack_converter(BasicType type) { + // A result is in the java expression stack of the interpreted method that has just + // returned. Place this result on the java expression stack of the caller. + // + // The current interpreter activation in rsi is for the method just returning its + // result. So we know that the result of this method is on the top of the current + // execution stack (which is pre-pushed) and will be return to the top of the caller + // stack. The top of the callers stack is the bottom of the locals of the current + // activation. + // Because of the way activation are managed by the frame manager the value of rsp is + // below both the stack top of the current activation and naturally the stack top + // of the calling activation. This enable this routine to leave the return address + // to the frame manager on the stack and do a vanilla return. + // + // On entry: rsi - interpreter state of activation returning a (potential) result + // On Return: rsi - unchanged + // rax - new stack top for caller activation (i.e. activation in _prev_link) + // + // Can destroy rdx, rcx. + // + + address entry = __ pc(); + const Register state = rsi; // current activation object, valid on entry + const Register t = InterpreterRuntime::SignatureHandlerGenerator::temp(); + switch (type) { + case T_VOID: + __ movl(rax, STATE(_locals)); // pop parameters get new stack value + __ addl(rax, wordSize); // account for prepush before we return + break; + case T_FLOAT : + case T_BOOLEAN: + case T_CHAR : + case T_BYTE : + case T_SHORT : + case T_INT : + // 1 word result + __ movl(rdx, STATE(_stack)); + __ movl(rax, STATE(_locals)); // address for result + __ movl(rdx, Address(rdx, wordSize)); // get result + __ movl(Address(rax, 0), rdx); // and store it + break; + case T_LONG : + case T_DOUBLE : + // return top two words on current expression stack to caller's expression stack + // The caller's expression stack is adjacent to the current frame manager's intepretState + // except we allocated one extra word for this intepretState so we won't overwrite it + // when we return a two word result. + + __ movl(rax, STATE(_locals)); // address for result + __ movl(rcx, STATE(_stack)); + __ subl(rax, wordSize); // need addition word besides locals[0] + __ movl(rdx, Address(rcx, 2*wordSize)); // get result word + __ movl(Address(rax, wordSize), rdx); // and store it + __ movl(rdx, Address(rcx, wordSize)); // get result word + __ movl(Address(rax, 0), rdx); // and store it + break; + case T_OBJECT : + __ movl(rdx, STATE(_stack)); + __ movl(rax, STATE(_locals)); // address for result + __ movl(rdx, Address(rdx, wordSize)); // get result + __ verify_oop(rdx); // verify it + __ movl(Address(rax, 0), rdx); // and store it + break; + default : ShouldNotReachHere(); + } + __ ret(0); + return entry; +} + +address CppInterpreterGenerator::generate_stack_to_native_abi_converter(BasicType type) { + // A result is in the java expression stack of the interpreted method that has just + // returned. Place this result in the native abi that the caller expects. + // + // Similar to generate_stack_to_stack_converter above. Called at a similar time from the + // frame manager execept in this situation the caller is native code (c1/c2/call_stub) + // and so rather than return result onto caller's java expression stack we return the + // result in the expected location based on the native abi. + // On entry: rsi - interpreter state of activation returning a (potential) result + // On Return: rsi - unchanged + // Other registers changed [rax/rdx/ST(0) as needed for the result returned] + + address entry = __ pc(); + const Register state = rsi; // current activation object, valid on entry + switch (type) { + case T_VOID: + break; + case T_BOOLEAN: + case T_CHAR : + case T_BYTE : + case T_SHORT : + case T_INT : + __ movl(rdx, STATE(_stack)); // get top of stack + __ movl(rax, Address(rdx, wordSize)); // get result word 1 + break; + case T_LONG : + __ movl(rdx, STATE(_stack)); // get top of stack + __ movl(rax, Address(rdx, wordSize)); // get result low word + __ movl(rdx, Address(rdx, 2*wordSize)); // get result high word + break; + break; + case T_FLOAT : + __ movl(rdx, STATE(_stack)); // get top of stack + if ( UseSSE >= 1) { + __ movflt(xmm0, Address(rdx, wordSize)); + } else { + __ fld_s(Address(rdx, wordSize)); // pushd float result + } + break; + case T_DOUBLE : + __ movl(rdx, STATE(_stack)); // get top of stack + if ( UseSSE > 1) { + __ movdbl(xmm0, Address(rdx, wordSize)); + } else { + __ fld_d(Address(rdx, wordSize)); // push double result + } + break; + case T_OBJECT : + __ movl(rdx, STATE(_stack)); // get top of stack + __ movl(rax, Address(rdx, wordSize)); // get result word 1 + __ verify_oop(rax); // verify it + break; + default : ShouldNotReachHere(); + } + __ ret(0); + return entry; +} + +address CppInterpreter::return_entry(TosState state, int length) { + // make it look good in the debugger + return CAST_FROM_FN_PTR(address, RecursiveInterpreterActivation); +} + +address CppInterpreter::deopt_entry(TosState state, int length) { + address ret = NULL; + if (length != 0) { + switch (state) { + case atos: ret = deopt_frame_manager_return_atos; break; + case btos: ret = deopt_frame_manager_return_btos; break; + case ctos: + case stos: + case itos: ret = deopt_frame_manager_return_itos; break; + case ltos: ret = deopt_frame_manager_return_ltos; break; + case ftos: ret = deopt_frame_manager_return_ftos; break; + case dtos: ret = deopt_frame_manager_return_dtos; break; + case vtos: ret = deopt_frame_manager_return_vtos; break; + } + } else { + ret = unctrap_frame_manager_entry; // re-execute the bytecode ( e.g. uncommon trap) + } + assert(ret != NULL, "Not initialized"); + return ret; +} + +// C++ Interpreter +void CppInterpreterGenerator::generate_compute_interpreter_state(const Register state, + const Register locals, + const Register sender_sp, + bool native) { + + // On entry the "locals" argument points to locals[0] (or where it would be in case no locals in + // a static method). "state" contains any previous frame manager state which we must save a link + // to in the newly generated state object. On return "state" is a pointer to the newly allocated + // state object. We must allocate and initialize a new interpretState object and the method + // expression stack. Because the returned result (if any) of the method will be placed on the caller's + // expression stack and this will overlap with locals[0] (and locals[1] if double/long) we must + // be sure to leave space on the caller's stack so that this result will not overwrite values when + // locals[0] and locals[1] do not exist (and in fact are return address and saved rbp). So when + // we are non-native we in essence ensure that locals[0-1] exist. We play an extra trick in + // non-product builds and initialize this last local with the previous interpreterState as + // this makes things look real nice in the debugger. + + // State on entry + // Assumes locals == &locals[0] + // Assumes state == any previous frame manager state (assuming call path from c++ interpreter) + // Assumes rax = return address + // rcx == senders_sp + // rbx == method + // Modifies rcx, rdx, rax + // Returns: + // state == address of new interpreterState + // rsp == bottom of method's expression stack. + + const Address const_offset (rbx, methodOopDesc::const_offset()); + + + // On entry sp is the sender's sp. This includes the space for the arguments + // that the sender pushed. If the sender pushed no args (a static) and the + // caller returns a long then we need two words on the sender's stack which + // are not present (although when we return a restore full size stack the + // space will be present). If we didn't allocate two words here then when + // we "push" the result of the caller's stack we would overwrite the return + // address and the saved rbp. Not good. So simply allocate 2 words now + // just to be safe. This is the "static long no_params() method" issue. + // See Lo.java for a testcase. + // We don't need this for native calls because they return result in + // register and the stack is expanded in the caller before we store + // the results on the stack. + + if (!native) { +#ifdef PRODUCT + __ subl(rsp, 2*wordSize); +#else /* PRODUCT */ + __ pushl((int)NULL); + __ pushl(state); // make it look like a real argument +#endif /* PRODUCT */ + } + + // Now that we are assure of space for stack result, setup typical linkage + + __ pushl(rax); + __ enter(); + + __ movl(rax, state); // save current state + + __ leal(rsp, Address(rsp, -(int)sizeof(BytecodeInterpreter))); + __ movl(state, rsp); + + // rsi == state/locals rax == prevstate + + // initialize the "shadow" frame so that use since C++ interpreter not directly + // recursive. Simpler to recurse but we can't trim expression stack as we call + // new methods. + __ movl(STATE(_locals), locals); // state->_locals = locals() + __ movl(STATE(_self_link), state); // point to self + __ movl(STATE(_prev_link), rax); // state->_link = state on entry (NULL or previous state) + __ movl(STATE(_sender_sp), sender_sp); // state->_sender_sp = sender_sp + __ get_thread(rax); // get vm's javathread* + __ movl(STATE(_thread), rax); // state->_bcp = codes() + __ movl(rdx, Address(rbx, methodOopDesc::const_offset())); // get constantMethodOop + __ leal(rdx, Address(rdx, constMethodOopDesc::codes_offset())); // get code base + if (native) { + __ movl(STATE(_bcp), (intptr_t)NULL); // state->_bcp = NULL + } else { + __ movl(STATE(_bcp), rdx); // state->_bcp = codes() + } + __ xorl(rdx, rdx); + __ movl(STATE(_oop_temp), rdx); // state->_oop_temp = NULL (only really needed for native) + __ movl(STATE(_mdx), rdx); // state->_mdx = NULL + __ movl(rdx, Address(rbx, methodOopDesc::constants_offset())); + __ movl(rdx, Address(rdx, constantPoolOopDesc::cache_offset_in_bytes())); + __ movl(STATE(_constants), rdx); // state->_constants = constants() + + __ movl(STATE(_method), rbx); // state->_method = method() + __ movl(STATE(_msg), (int) BytecodeInterpreter::method_entry); // state->_msg = initial method entry + __ movl(STATE(_result._to_call._callee), (int) NULL); // state->_result._to_call._callee_callee = NULL + + + __ movl(STATE(_monitor_base), rsp); // set monitor block bottom (grows down) this would point to entry [0] + // entries run from -1..x where &monitor[x] == + + { + // Must not attempt to lock method until we enter interpreter as gc won't be able to find the + // initial frame. However we allocate a free monitor so we don't have to shuffle the expression stack + // immediately. + + // synchronize method + const Address access_flags (rbx, methodOopDesc::access_flags_offset()); + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + Label not_synced; + + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::zero, not_synced); + + // Allocate initial monitor and pre initialize it + // get synchronization object + + Label done; + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + Klass::java_mirror_offset_in_bytes(); + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_STATIC); + __ movl(rax, Address(locals, 0)); // get receiver (assume this is frequent case) + __ jcc(Assembler::zero, done); + __ movl(rax, Address(rbx, methodOopDesc::constants_offset())); + __ movl(rax, Address(rax, constantPoolOopDesc::pool_holder_offset_in_bytes())); + __ movl(rax, Address(rax, mirror_offset)); + __ bind(done); + // add space for monitor & lock + __ subl(rsp, entry_size); // add space for a monitor entry + __ movl(Address(rsp, BasicObjectLock::obj_offset_in_bytes()), rax); // store object + __ bind(not_synced); + } + + __ movl(STATE(_stack_base), rsp); // set expression stack base ( == &monitors[-count]) + if (native) { + __ movl(STATE(_stack), rsp); // set current expression stack tos + __ movl(STATE(_stack_limit), rsp); + } else { + __ subl(rsp, wordSize); // pre-push stack + __ movl(STATE(_stack), rsp); // set current expression stack tos + + // compute full expression stack limit + + const Address size_of_stack (rbx, methodOopDesc::max_stack_offset()); + __ load_unsigned_word(rdx, size_of_stack); // get size of expression stack in words + __ negl(rdx); // so we can subtract in next step + // Allocate expression stack + __ leal(rsp, Address(rsp, rdx, Address::times_4)); + __ movl(STATE(_stack_limit), rsp); + } + +} + +// Helpers for commoning out cases in the various type of method entries. +// + +// increment invocation count & check for overflow +// +// Note: checking for negative value instead of overflow +// so we have a 'sticky' overflow test +// +// rbx,: method +// rcx: invocation counter +// +void InterpreterGenerator::generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue) { + + const Address invocation_counter(rbx, methodOopDesc::invocation_counter_offset() + InvocationCounter::counter_offset()); + const Address backedge_counter (rbx, methodOopDesc::backedge_counter_offset() + InvocationCounter::counter_offset()); + + if (ProfileInterpreter) { // %%% Merge this into methodDataOop + __ increment(Address(rbx,methodOopDesc::interpreter_invocation_counter_offset())); + } + // Update standard invocation counters + __ movl(rax, backedge_counter); // load backedge counter + + __ increment(rcx, InvocationCounter::count_increment); + __ andl(rax, InvocationCounter::count_mask_value); // mask out the status bits + + __ movl(invocation_counter, rcx); // save invocation count + __ addl(rcx, rax); // add both counters + + // profile_method is non-null only for interpreted method so + // profile_method != NULL == !native_call + // BytecodeInterpreter only calls for native so code is elided. + + __ cmp32(rcx, + ExternalAddress((address)&InvocationCounter::InterpreterInvocationLimit)); + __ jcc(Assembler::aboveEqual, *overflow); + +} + +void InterpreterGenerator::generate_counter_overflow(Label* do_continue) { + + // C++ interpreter on entry + // rsi - new interpreter state pointer + // rbp - interpreter frame pointer + // rbx - method + + // On return (i.e. jump to entry_point) [ back to invocation of interpreter ] + // rbx, - method + // rcx - rcvr (assuming there is one) + // top of stack return address of interpreter caller + // rsp - sender_sp + + // C++ interpreter only + // rsi - previous interpreter state pointer + + const Address size_of_parameters(rbx, methodOopDesc::size_of_parameters_offset()); + + // InterpreterRuntime::frequency_counter_overflow takes one argument + // indicating if the counter overflow occurs at a backwards branch (non-NULL bcp). + // The call returns the address of the verified entry point for the method or NULL + // if the compilation did not complete (either went background or bailed out). + __ movl(rax, (int)false); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), rax); + + // for c++ interpreter can rsi really be munged? + __ leal(rsi, Address(rbp, -sizeof(BytecodeInterpreter))); // restore state + __ movl(rbx, Address(rsi, byte_offset_of(BytecodeInterpreter, _method))); // restore method + __ movl(rdi, Address(rsi, byte_offset_of(BytecodeInterpreter, _locals))); // get locals pointer + + // Preserve invariant that rsi/rdi contain bcp/locals of sender frame + // and jump to the interpreted entry. + __ jmp(*do_continue, relocInfo::none); + +} + +void InterpreterGenerator::generate_stack_overflow_check(void) { + // see if we've got enough room on the stack for locals plus overhead. + // the expression stack grows down incrementally, so the normal guard + // page mechanism will work for that. + // + // Registers live on entry: + // + // Asm interpreter + // rdx: number of additional locals this frame needs (what we must check) + // rbx,: methodOop + + // C++ Interpreter + // rsi: previous interpreter frame state object + // rdi: &locals[0] + // rcx: # of locals + // rdx: number of additional locals this frame needs (what we must check) + // rbx: methodOop + + // destroyed on exit + // rax, + + // NOTE: since the additional locals are also always pushed (wasn't obvious in + // generate_method_entry) so the guard should work for them too. + // + + // monitor entry size: see picture of stack set (generate_method_entry) and frame_i486.hpp + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + + // total overhead size: entry_size + (saved rbp, thru expr stack bottom). + // be sure to change this if you add/subtract anything to/from the overhead area + const int overhead_size = (int)sizeof(BytecodeInterpreter); + + const int page_size = os::vm_page_size(); + + Label after_frame_check; + + // compute rsp as if this were going to be the last frame on + // the stack before the red zone + + Label after_frame_check_pop; + + // save rsi == caller's bytecode ptr (c++ previous interp. state) + // QQQ problem here?? rsi overload???? + __ pushl(rsi); + + const Register thread = rsi; + + __ get_thread(thread); + + const Address stack_base(thread, Thread::stack_base_offset()); + const Address stack_size(thread, Thread::stack_size_offset()); + + // locals + overhead, in bytes + const Address size_of_stack (rbx, methodOopDesc::max_stack_offset()); + // Always give one monitor to allow us to start interp if sync method. + // Any additional monitors need a check when moving the expression stack + const one_monitor = frame::interpreter_frame_monitor_size() * wordSize; + __ load_unsigned_word(rax, size_of_stack); // get size of expression stack in words + __ leal(rax, Address(noreg, rax, Interpreter::stackElementScale(), one_monitor)); + __ leal(rax, Address(rax, rdx, Interpreter::stackElementScale(), overhead_size)); + +#ifdef ASSERT + Label stack_base_okay, stack_size_okay; + // verify that thread stack base is non-zero + __ cmpl(stack_base, 0); + __ jcc(Assembler::notEqual, stack_base_okay); + __ stop("stack base is zero"); + __ bind(stack_base_okay); + // verify that thread stack size is non-zero + __ cmpl(stack_size, 0); + __ jcc(Assembler::notEqual, stack_size_okay); + __ stop("stack size is zero"); + __ bind(stack_size_okay); +#endif + + // Add stack base to locals and subtract stack size + __ addl(rax, stack_base); + __ subl(rax, stack_size); + + // We should have a magic number here for the size of the c++ interpreter frame. + // We can't actually tell this ahead of time. The debug version size is around 3k + // product is 1k and fastdebug is 4k + const int slop = 6 * K; + + // Use the maximum number of pages we might bang. + const int max_pages = StackShadowPages > (StackRedPages+StackYellowPages) ? StackShadowPages : + (StackRedPages+StackYellowPages); + // Only need this if we are stack banging which is temporary while + // we're debugging. + __ addl(rax, slop + 2*max_pages * page_size); + + // check against the current stack bottom + __ cmpl(rsp, rax); + __ jcc(Assembler::above, after_frame_check_pop); + + __ popl(rsi); // get saved bcp / (c++ prev state ). + + // throw exception return address becomes throwing pc + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_StackOverflowError)); + + // all done with frame size check + __ bind(after_frame_check_pop); + __ popl(rsi); + + __ bind(after_frame_check); +} + +// Find preallocated monitor and lock method (C++ interpreter) +// rbx - methodOop +// +void InterpreterGenerator::lock_method(void) { + // assumes state == rsi == pointer to current interpreterState + // minimally destroys rax, rdx, rdi + // + // synchronize method + const Register state = rsi; + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + const Address access_flags (rbx, methodOopDesc::access_flags_offset()); + + // find initial monitor i.e. monitors[-1] + __ movl(rdx, STATE(_monitor_base)); // get monitor bottom limit + __ subl(rdx, entry_size); // point to initial monitor + +#ifdef ASSERT + { Label L; + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::notZero, L); + __ stop("method doesn't need synchronization"); + __ bind(L); + } +#endif // ASSERT + // get synchronization object + { Label done; + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + Klass::java_mirror_offset_in_bytes(); + __ movl(rax, access_flags); + __ movl(rdi, STATE(_locals)); // prepare to get receiver (assume common case) + __ testl(rax, JVM_ACC_STATIC); + __ movl(rax, Address(rdi, 0)); // get receiver (assume this is frequent case) + __ jcc(Assembler::zero, done); + __ movl(rax, Address(rbx, methodOopDesc::constants_offset())); + __ movl(rax, Address(rax, constantPoolOopDesc::pool_holder_offset_in_bytes())); + __ movl(rax, Address(rax, mirror_offset)); + __ bind(done); + } +#ifdef ASSERT + { Label L; + __ cmpl(rax, Address(rdx, BasicObjectLock::obj_offset_in_bytes())); // correct object? + __ jcc(Assembler::equal, L); + __ stop("wrong synchronization lobject"); + __ bind(L); + } +#endif // ASSERT + // can destroy rax, rdx, rcx, and (via call_VM) rdi! + __ lock_object(rdx); +} + +// Call an accessor method (assuming it is resolved, otherwise drop into vanilla (slow path) entry + +address InterpreterGenerator::generate_accessor_entry(void) { + + // rbx,: methodOop + // rcx: receiver (preserve for slow entry into asm interpreter) + + // rsi: senderSP must preserved for slow path, set SP to it on fast path + + Label xreturn_path; + + // do fastpath for resolved accessor methods + if (UseFastAccessorMethods) { + + address entry_point = __ pc(); + + Label slow_path; + // If we need a safepoint check, generate full interpreter entry. + ExternalAddress state(SafepointSynchronize::address_of_state()); + __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), + SafepointSynchronize::_not_synchronized); + + __ jcc(Assembler::notEqual, slow_path); + // ASM/C++ Interpreter + // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites thereof; parameter size = 1 + // Note: We can only use this code if the getfield has been resolved + // and if we don't have a null-pointer exception => check for + // these conditions first and use slow path if necessary. + // rbx,: method + // rcx: receiver + __ movl(rax, Address(rsp, wordSize)); + + // check if local 0 != NULL and read field + __ testl(rax, rax); + __ jcc(Assembler::zero, slow_path); + + __ movl(rdi, Address(rbx, methodOopDesc::constants_offset())); + // read first instruction word and extract bytecode @ 1 and index @ 2 + __ movl(rdx, Address(rbx, methodOopDesc::const_offset())); + __ movl(rdx, Address(rdx, constMethodOopDesc::codes_offset())); + // Shift codes right to get the index on the right. + // The bytecode fetched looks like <0xb4><0x2a> + __ shrl(rdx, 2*BitsPerByte); + __ shll(rdx, exact_log2(in_words(ConstantPoolCacheEntry::size()))); + __ movl(rdi, Address(rdi, constantPoolOopDesc::cache_offset_in_bytes())); + + // rax,: local 0 + // rbx,: method + // rcx: receiver - do not destroy since it is needed for slow path! + // rcx: scratch + // rdx: constant pool cache index + // rdi: constant pool cache + // rsi: sender sp + + // check if getfield has been resolved and read constant pool cache entry + // check the validity of the cache entry by testing whether _indices field + // contains Bytecode::_getfield in b1 byte. + assert(in_words(ConstantPoolCacheEntry::size()) == 4, "adjust shift below"); + __ movl(rcx, + Address(rdi, + rdx, + Address::times_4, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset())); + __ shrl(rcx, 2*BitsPerByte); + __ andl(rcx, 0xFF); + __ cmpl(rcx, Bytecodes::_getfield); + __ jcc(Assembler::notEqual, slow_path); + + // Note: constant pool entry is not valid before bytecode is resolved + __ movl(rcx, + Address(rdi, + rdx, + Address::times_4, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f2_offset())); + __ movl(rdx, + Address(rdi, + rdx, + Address::times_4, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::flags_offset())); + + Label notByte, notShort, notChar; + const Address field_address (rax, rcx, Address::times_1); + + // Need to differentiate between igetfield, agetfield, bgetfield etc. + // because they are different sizes. + // Use the type from the constant pool cache + __ shrl(rdx, ConstantPoolCacheEntry::tosBits); + // Make sure we don't need to mask rdx for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ cmpl(rdx, btos); + __ jcc(Assembler::notEqual, notByte); + __ load_signed_byte(rax, field_address); + __ jmp(xreturn_path); + + __ bind(notByte); + __ cmpl(rdx, stos); + __ jcc(Assembler::notEqual, notShort); + __ load_signed_word(rax, field_address); + __ jmp(xreturn_path); + + __ bind(notShort); + __ cmpl(rdx, ctos); + __ jcc(Assembler::notEqual, notChar); + __ load_unsigned_word(rax, field_address); + __ jmp(xreturn_path); + + __ bind(notChar); +#ifdef ASSERT + Label okay; + __ cmpl(rdx, atos); + __ jcc(Assembler::equal, okay); + __ cmpl(rdx, itos); + __ jcc(Assembler::equal, okay); + __ stop("what type is this?"); + __ bind(okay); +#endif // ASSERT + // All the rest are a 32 bit wordsize + __ movl(rax, field_address); + + __ bind(xreturn_path); + + // _ireturn/_areturn + __ popl(rdi); // get return address + __ movl(rsp, rsi); // set sp to sender sp + __ jmp(rdi); + + // generate a vanilla interpreter entry as the slow path + __ bind(slow_path); + // We will enter c++ interpreter looking like it was + // called by the call_stub this will cause it to return + // a tosca result to the invoker which might have been + // the c++ interpreter itself. + + __ jmp(fast_accessor_slow_entry_path); + return entry_point; + + } else { + return NULL; + } + +} + +// +// C++ Interpreter stub for calling a native method. +// This sets up a somewhat different looking stack for calling the native method +// than the typical interpreter frame setup but still has the pointer to +// an interpreter state. +// + +address InterpreterGenerator::generate_native_entry(bool synchronized) { + // determine code generation flags + bool inc_counter = UseCompiler || CountCompiledCalls; + + // rbx: methodOop + // rcx: receiver (unused) + // rsi: previous interpreter state (if called from C++ interpreter) must preserve + // in any case. If called via c1/c2/call_stub rsi is junk (to use) but harmless + // to save/restore. + address entry_point = __ pc(); + + const Address size_of_parameters(rbx, methodOopDesc::size_of_parameters_offset()); + const Address size_of_locals (rbx, methodOopDesc::size_of_locals_offset()); + const Address invocation_counter(rbx, methodOopDesc::invocation_counter_offset() + InvocationCounter::counter_offset()); + const Address access_flags (rbx, methodOopDesc::access_flags_offset()); + + // rsi == state/locals rdi == prevstate + const Register state = rsi; + const Register locals = rdi; + + // get parameter size (always needed) + __ load_unsigned_word(rcx, size_of_parameters); + + // rbx: methodOop + // rcx: size of parameters + __ popl(rax); // get return address + // for natives the size of locals is zero + + // compute beginning of parameters /locals + __ leal(locals, Address(rsp, rcx, Address::times_4, -wordSize)); + + // initialize fixed part of activation frame + + // Assumes rax = return address + + // allocate and initialize new interpreterState and method expression stack + // IN(locals) -> locals + // IN(state) -> previous frame manager state (NULL from stub/c1/c2) + // destroys rax, rcx, rdx + // OUT (state) -> new interpreterState + // OUT(rsp) -> bottom of methods expression stack + + // save sender_sp + __ movl(rcx, rsi); + // start with NULL previous state + __ movl(state, 0); + generate_compute_interpreter_state(state, locals, rcx, true); + +#ifdef ASSERT + { Label L; + __ movl(rax, STATE(_stack_base)); + __ cmpl(rax, rsp); + __ jcc(Assembler::equal, L); + __ stop("broken stack frame setup in interpreter"); + __ bind(L); + } +#endif + + if (inc_counter) __ movl(rcx, invocation_counter); // (pre-)fetch invocation count + + __ movl(rax, STATE(_thread)); // get thread + // Since at this point in the method invocation the exception handler + // would try to exit the monitor of synchronized methods which hasn't + // been entered yet, we set the thread local variable + // _do_not_unlock_if_synchronized to true. The remove_activation will + // check this flag. + + const Address do_not_unlock_if_synchronized(rax, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + __ movbool(do_not_unlock_if_synchronized, true); + + // make sure method is native & not abstract +#ifdef ASSERT + __ movl(rax, access_flags); + { + Label L; + __ testl(rax, JVM_ACC_NATIVE); + __ jcc(Assembler::notZero, L); + __ stop("tried to execute non-native method as native"); + __ bind(L); + } + { Label L; + __ testl(rax, JVM_ACC_ABSTRACT); + __ jcc(Assembler::zero, L); + __ stop("tried to execute abstract method in interpreter"); + __ bind(L); + } +#endif + + + // increment invocation count & check for overflow + Label invocation_counter_overflow; + if (inc_counter) { + generate_counter_incr(&invocation_counter_overflow, NULL, NULL); + } + + Label continue_after_compile; + + __ bind(continue_after_compile); + + bang_stack_shadow_pages(true); + + // reset the _do_not_unlock_if_synchronized flag + __ movl(rax, STATE(_thread)); // get thread + __ movbool(do_not_unlock_if_synchronized, false); + + + // check for synchronized native methods + // + // Note: This must happen *after* invocation counter check, since + // when overflow happens, the method should not be locked. + if (synchronized) { + // potentially kills rax, rcx, rdx, rdi + lock_method(); + } else { + // no synchronization necessary +#ifdef ASSERT + { Label L; + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::zero, L); + __ stop("method needs synchronization"); + __ bind(L); + } +#endif + } + + // start execution + + // jvmti support + __ notify_method_entry(); + + // work registers + const Register method = rbx; + const Register thread = rdi; + const Register t = rcx; + + // allocate space for parameters + __ movl(method, STATE(_method)); + __ verify_oop(method); + __ load_unsigned_word(t, Address(method, methodOopDesc::size_of_parameters_offset())); + __ shll(t, 2); + __ addl(t, 2*wordSize); // allocate two more slots for JNIEnv and possible mirror + __ subl(rsp, t); + __ andl(rsp, -(StackAlignmentInBytes)); // gcc needs 16 byte aligned stacks to do XMM intrinsics + + // get signature handler + Label pending_exception_present; + + { Label L; + __ movl(t, Address(method, methodOopDesc::signature_handler_offset())); + __ testl(t, t); + __ jcc(Assembler::notZero, L); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), method, false); + __ movl(method, STATE(_method)); + __ cmpl(Address(thread, Thread::pending_exception_offset()), NULL_WORD); + __ jcc(Assembler::notEqual, pending_exception_present); + __ verify_oop(method); + __ movl(t, Address(method, methodOopDesc::signature_handler_offset())); + __ bind(L); + } +#ifdef ASSERT + { + Label L; + __ pushl(t); + __ get_thread(t); // get vm's javathread* + __ cmpl(t, STATE(_thread)); + __ jcc(Assembler::equal, L); + __ int3(); + __ bind(L); + __ popl(t); + } +#endif // + + // call signature handler + assert(InterpreterRuntime::SignatureHandlerGenerator::from() == rdi, "adjust this code"); + assert(InterpreterRuntime::SignatureHandlerGenerator::to () == rsp, "adjust this code"); + assert(InterpreterRuntime::SignatureHandlerGenerator::temp() == t , "adjust this code"); + // The generated handlers do not touch RBX (the method oop). + // However, large signatures cannot be cached and are generated + // each time here. The slow-path generator will blow RBX + // sometime, so we must reload it after the call. + __ movl(rdi, STATE(_locals)); // get the from pointer + __ call(t); + __ movl(method, STATE(_method)); + __ verify_oop(method); + + // result handler is in rax + // set result handler + __ movl(STATE(_result_handler), rax); + + // pass mirror handle if static call + { Label L; + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + Klass::java_mirror_offset_in_bytes(); + __ movl(t, Address(method, methodOopDesc::access_flags_offset())); + __ testl(t, JVM_ACC_STATIC); + __ jcc(Assembler::zero, L); + // get mirror + __ movl(t, Address(method, methodOopDesc:: constants_offset())); + __ movl(t, Address(t, constantPoolOopDesc::pool_holder_offset_in_bytes())); + __ movl(t, Address(t, mirror_offset)); + // copy mirror into activation object + __ movl(STATE(_oop_temp), t); + // pass handle to mirror + __ leal(t, STATE(_oop_temp)); + __ movl(Address(rsp, wordSize), t); + __ bind(L); + } +#ifdef ASSERT + { + Label L; + __ pushl(t); + __ get_thread(t); // get vm's javathread* + __ cmpl(t, STATE(_thread)); + __ jcc(Assembler::equal, L); + __ int3(); + __ bind(L); + __ popl(t); + } +#endif // + + // get native function entry point + { Label L; + __ movl(rax, Address(method, methodOopDesc::native_function_offset())); + __ testl(rax, rax); + __ jcc(Assembler::notZero, L); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), method); + __ movl(method, STATE(_method)); + __ verify_oop(method); + __ movl(rax, Address(method, methodOopDesc::native_function_offset())); + __ bind(L); + } + + // pass JNIEnv + __ movl(thread, STATE(_thread)); // get thread + __ leal(t, Address(thread, JavaThread::jni_environment_offset())); + __ movl(Address(rsp, 0), t); +#ifdef ASSERT + { + Label L; + __ pushl(t); + __ get_thread(t); // get vm's javathread* + __ cmpl(t, STATE(_thread)); + __ jcc(Assembler::equal, L); + __ int3(); + __ bind(L); + __ popl(t); + } +#endif // + +#ifdef ASSERT + { Label L; + __ movl(t, Address(thread, JavaThread::thread_state_offset())); + __ cmpl(t, _thread_in_Java); + __ jcc(Assembler::equal, L); + __ stop("Wrong thread state in native stub"); + __ bind(L); + } +#endif + + // Change state to native (we save the return address in the thread, since it might not + // be pushed on the stack when we do a a stack traversal). It is enough that the pc() + // points into the right code segment. It does not have to be the correct return pc. + + __ set_last_Java_frame(thread, noreg, rbp, __ pc()); + + __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_native); + + __ call(rax); + + // result potentially in rdx:rax or ST0 + __ movl(method, STATE(_method)); + __ movl(thread, STATE(_thread)); // get thread + + // The potential result is in ST(0) & rdx:rax + // With C++ interpreter we leave any possible result in ST(0) until we are in result handler and then + // we do the appropriate stuff for returning the result. rdx:rax must always be saved because just about + // anything we do here will destroy it, st(0) is only saved if we re-enter the vm where it would + // be destroyed. + // It is safe to do these pushes because state is _thread_in_native and return address will be found + // via _last_native_pc and not via _last_jave_sp + + // Must save the value of ST(0) since it could be destroyed before we get to result handler + { Label Lpush, Lskip; + ExternalAddress float_handler(AbstractInterpreter::result_handler(T_FLOAT)); + ExternalAddress double_handler(AbstractInterpreter::result_handler(T_DOUBLE)); + __ cmpptr(STATE(_result_handler), float_handler.addr()); + __ jcc(Assembler::equal, Lpush); + __ cmpptr(STATE(_result_handler), double_handler.addr()); + __ jcc(Assembler::notEqual, Lskip); + __ bind(Lpush); + __ push(dtos); + __ bind(Lskip); + } + + __ push(ltos); // save rax:rdx for potential use by result handler. + + // Either restore the MXCSR register after returning from the JNI Call + // or verify that it wasn't changed. + if (VM_Version::supports_sse()) { + if (RestoreMXCSROnJNICalls) { + __ ldmxcsr(ExternalAddress(StubRoutines::addr_mxcsr_std())); + } + else if (CheckJNICalls ) { + __ call(RuntimeAddress(StubRoutines::i486::verify_mxcsr_entry())); + } + } + + // Either restore the x87 floating pointer control word after returning + // from the JNI call or verify that it wasn't changed. + if (CheckJNICalls) { + __ call(RuntimeAddress(StubRoutines::i486::verify_fpu_cntrl_wrd_entry())); + } + + + // change thread state + __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_native_trans); + if(os::is_MP()) { + // Write serialization page so VM thread can do a pseudo remote membar. + // We use the current thread pointer to calculate a thread specific + // offset to write to within the page. This minimizes bus traffic + // due to cache line collision. + __ serialize_memory(thread, rcx); + } + + // check for safepoint operation in progress and/or pending suspend requests + { Label Continue; + + __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), + SafepointSynchronize::_not_synchronized); + + // threads running native code and they are expected to self-suspend + // when leaving the _thread_in_native state. We need to check for + // pending suspend requests here. + Label L; + __ jcc(Assembler::notEqual, L); + __ cmpl(Address(thread, JavaThread::suspend_flags_offset()), 0); + __ jcc(Assembler::equal, Continue); + __ bind(L); + + // Don't use call_VM as it will see a possible pending exception and forward it + // and never return here preventing us from clearing _last_native_pc down below. + // Also can't use call_VM_leaf either as it will check to see if rsi & rdi are + // preserved and correspond to the bcp/locals pointers. So we do a runtime call + // by hand. + // + __ pushl(thread); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, + JavaThread::check_special_condition_for_native_trans))); + __ increment(rsp, wordSize); + + __ movl(method, STATE(_method)); + __ verify_oop(method); + __ movl(thread, STATE(_thread)); // get thread + + __ bind(Continue); + } + + // change thread state + __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_Java); + + __ reset_last_Java_frame(thread, true, true); + + // reset handle block + __ movl(t, Address(thread, JavaThread::active_handles_offset())); + __ movl(Address(t, JNIHandleBlock::top_offset_in_bytes()), NULL_WORD); + + // If result was an oop then unbox and save it in the frame + { Label L; + Label no_oop, store_result; + ExternalAddress oop_handler(AbstractInterpreter::result_handler(T_OBJECT)); + __ cmpptr(STATE(_result_handler), oop_handler.addr()); + __ jcc(Assembler::notEqual, no_oop); + __ pop(ltos); + __ testl(rax, rax); + __ jcc(Assembler::zero, store_result); + // unbox + __ movl(rax, Address(rax, 0)); + __ bind(store_result); + __ movl(STATE(_oop_temp), rax); + // keep stack depth as expected by pushing oop which will eventually be discarded + __ push(ltos); + __ bind(no_oop); + } + + { + Label no_reguard; + __ cmpl(Address(thread, JavaThread::stack_guard_state_offset()), JavaThread::stack_guard_yellow_disabled); + __ jcc(Assembler::notEqual, no_reguard); + + __ pushad(); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages))); + __ popad(); + + __ bind(no_reguard); + } + + + // QQQ Seems like for native methods we simply return and the caller will see the pending + // exception and do the right thing. Certainly the interpreter will, don't know about + // compiled methods. + // Seems that the answer to above is no this is wrong. The old code would see the exception + // and forward it before doing the unlocking and notifying jvmdi that method has exited. + // This seems wrong need to investigate the spec. + + // handle exceptions (exception handling will handle unlocking!) + { Label L; + __ cmpl(Address(thread, Thread::pending_exception_offset()), NULL_WORD); + __ jcc(Assembler::zero, L); + __ bind(pending_exception_present); + + // There are potential results on the stack (rax/rdx, ST(0)) we ignore these and simply + // return and let caller deal with exception. This skips the unlocking here which + // seems wrong but seems to be what asm interpreter did. Can't find this in the spec. + // Note: must preverve method in rbx + // + + // remove activation + + __ movl(t, STATE(_sender_sp)); + __ leave(); // remove frame anchor + __ popl(rdi); // get return address + __ movl(state, STATE(_prev_link)); // get previous state for return + __ movl(rsp, t); // set sp to sender sp + __ pushl(rdi); // [ush throwing pc + // The skips unlocking!! This seems to be what asm interpreter does but seems + // very wrong. Not clear if this violates the spec. + __ jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + __ bind(L); + } + + // do unlocking if necessary + { Label L; + __ movl(t, Address(method, methodOopDesc::access_flags_offset())); + __ testl(t, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::zero, L); + // the code below should be shared with interpreter macro assembler implementation + { Label unlock; + // BasicObjectLock will be first in list, since this is a synchronized method. However, need + // to check that the object has not been unlocked by an explicit monitorexit bytecode. + __ movl(rdx, STATE(_monitor_base)); + __ subl(rdx, frame::interpreter_frame_monitor_size() * wordSize); // address of initial monitor + + __ movl(t, Address(rdx, BasicObjectLock::obj_offset_in_bytes())); + __ testl(t, t); + __ jcc(Assembler::notZero, unlock); + + // Entry already unlocked, need to throw exception + __ MacroAssembler::call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + __ should_not_reach_here(); + + __ bind(unlock); + __ unlock_object(rdx); + // unlock can blow rbx so restore it for path that needs it below + __ movl(method, STATE(_method)); + } + __ bind(L); + } + + // jvmti support + // Note: This must happen _after_ handling/throwing any exceptions since + // the exception handler code notifies the runtime of method exits + // too. If this happens before, method entry/exit notifications are + // not properly paired (was bug - gri 11/22/99). + __ notify_method_exit(vtos, InterpreterMacroAssembler::NotifyJVMTI); + + // restore potential result in rdx:rax, call result handler to restore potential result in ST0 & handle result + __ pop(ltos); // restore rax/rdx floating result if present still on stack + __ movl(t, STATE(_result_handler)); // get result handler + __ call(t); // call result handler to convert to tosca form + + // remove activation + + __ movl(t, STATE(_sender_sp)); + + __ leave(); // remove frame anchor + __ popl(rdi); // get return address + __ movl(state, STATE(_prev_link)); // get previous state for return (if c++ interpreter was caller) + __ movl(rsp, t); // set sp to sender sp + __ jmp(rdi); + + // invocation counter overflow + if (inc_counter) { + // Handle overflow of counter and compile method + __ bind(invocation_counter_overflow); + generate_counter_overflow(&continue_after_compile); + } + + return entry_point; +} + +// Generate entries that will put a result type index into rcx +void CppInterpreterGenerator::generate_deopt_handling() { + + const Register state = rsi; + Label return_from_deopt_common; + + // Generate entries that will put a result type index into rcx + // deopt needs to jump to here to enter the interpreter (return a result) + deopt_frame_manager_return_atos = __ pc(); + + // rax is live here + __ movl(rcx, AbstractInterpreter::BasicType_as_index(T_OBJECT)); // Result stub address array index + __ jmp(return_from_deopt_common); + + + // deopt needs to jump to here to enter the interpreter (return a result) + deopt_frame_manager_return_btos = __ pc(); + + // rax is live here + __ movl(rcx, AbstractInterpreter::BasicType_as_index(T_BOOLEAN)); // Result stub address array index + __ jmp(return_from_deopt_common); + + // deopt needs to jump to here to enter the interpreter (return a result) + deopt_frame_manager_return_itos = __ pc(); + + // rax is live here + __ movl(rcx, AbstractInterpreter::BasicType_as_index(T_INT)); // Result stub address array index + __ jmp(return_from_deopt_common); + + // deopt needs to jump to here to enter the interpreter (return a result) + + deopt_frame_manager_return_ltos = __ pc(); + // rax,rdx are live here + __ movl(rcx, AbstractInterpreter::BasicType_as_index(T_LONG)); // Result stub address array index + __ jmp(return_from_deopt_common); + + // deopt needs to jump to here to enter the interpreter (return a result) + + deopt_frame_manager_return_ftos = __ pc(); + // st(0) is live here + __ movl(rcx, AbstractInterpreter::BasicType_as_index(T_FLOAT)); // Result stub address array index + __ jmp(return_from_deopt_common); + + // deopt needs to jump to here to enter the interpreter (return a result) + deopt_frame_manager_return_dtos = __ pc(); + + // st(0) is live here + __ movl(rcx, AbstractInterpreter::BasicType_as_index(T_DOUBLE)); // Result stub address array index + __ jmp(return_from_deopt_common); + + // deopt needs to jump to here to enter the interpreter (return a result) + deopt_frame_manager_return_vtos = __ pc(); + + __ movl(rcx, AbstractInterpreter::BasicType_as_index(T_VOID)); + + // Deopt return common + // an index is present in rcx that lets us move any possible result being + // return to the interpreter's stack + // + // Because we have a full sized interpreter frame on the youngest + // activation the stack is pushed too deep to share the tosca to + // stack converters directly. We shrink the stack to the desired + // amount and then push result and then re-extend the stack. + // We could have the code in size_activation layout a short + // frame for the top activation but that would look different + // than say sparc (which needs a full size activation because + // the windows are in the way. Really it could be short? QQQ + // + __ bind(return_from_deopt_common); + + __ leal(state, Address(rbp, -(int)sizeof(BytecodeInterpreter))); + + // setup rsp so we can push the "result" as needed. + __ movl(rsp, STATE(_stack)); // trim stack (is prepushed) + __ addl(rsp, wordSize); // undo prepush + + ExternalAddress tosca_to_stack((address)CppInterpreter::_tosca_to_stack); + // Address index(noreg, rcx, Address::times_4); + __ movptr(rcx, ArrayAddress(tosca_to_stack, Address(noreg, rcx, Address::times_4))); + // __ movl(rcx, Address(noreg, rcx, Address::times_4, int(AbstractInterpreter::_tosca_to_stack))); + __ call(rcx); // call result converter + + __ movl(STATE(_msg), (int)BytecodeInterpreter::deopt_resume); + __ leal(rsp, Address(rsp, -wordSize)); // prepush stack (result if any already present) + __ movl(STATE(_stack), rsp); // inform interpreter of new stack depth (parameters removed, + // result if any on stack already ) + __ movl(rsp, STATE(_stack_limit)); // restore expression stack to full depth +} + +// Generate the code to handle a more_monitors message from the c++ interpreter +void CppInterpreterGenerator::generate_more_monitors() { + + const Register state = rsi; + + Label entry, loop; + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + // 1. compute new pointers // rsp: old expression stack top + __ movl(rdx, STATE(_stack_base)); // rdx: old expression stack bottom + __ subl(rsp, entry_size); // move expression stack top limit + __ subl(STATE(_stack), entry_size); // update interpreter stack top + __ movl(STATE(_stack_limit), rsp); // inform interpreter + __ subl(rdx, entry_size); // move expression stack bottom + __ movl(STATE(_stack_base), rdx); // inform interpreter + __ movl(rcx, STATE(_stack)); // set start value for copy loop + __ jmp(entry); + // 2. move expression stack contents + __ bind(loop); + __ movl(rbx, Address(rcx, entry_size)); // load expression stack word from old location + __ movl(Address(rcx, 0), rbx); // and store it at new location + __ addl(rcx, wordSize); // advance to next word + __ bind(entry); + __ cmpl(rcx, rdx); // check if bottom reached + __ jcc(Assembler::notEqual, loop); // if not at bottom then copy next word + // now zero the slot so we can find it. + __ movl(Address(rdx, BasicObjectLock::obj_offset_in_bytes()), (int) NULL); + __ movl(STATE(_msg), (int)BytecodeInterpreter::got_monitors); +} + + +// Initial entry to C++ interpreter from the call_stub. +// This entry point is called the frame manager since it handles the generation +// of interpreter activation frames via requests directly from the vm (via call_stub) +// and via requests from the interpreter. The requests from the call_stub happen +// directly thru the entry point. Requests from the interpreter happen via returning +// from the interpreter and examining the message the interpreter has returned to +// the frame manager. The frame manager can take the following requests: + +// NO_REQUEST - error, should never happen. +// MORE_MONITORS - need a new monitor. Shuffle the expression stack on down and +// allocate a new monitor. +// CALL_METHOD - setup a new activation to call a new method. Very similar to what +// happens during entry during the entry via the call stub. +// RETURN_FROM_METHOD - remove an activation. Return to interpreter or call stub. +// +// Arguments: +// +// rbx: methodOop +// rcx: receiver - unused (retrieved from stack as needed) +// rsi: previous frame manager state (NULL from the call_stub/c1/c2) +// +// +// Stack layout at entry +// +// [ return address ] <--- rsp +// [ parameter n ] +// ... +// [ parameter 1 ] +// [ expression stack ] +// +// +// We are free to blow any registers we like because the call_stub which brought us here +// initially has preserved the callee save registers already. +// +// + +static address interpreter_frame_manager = NULL; + +address InterpreterGenerator::generate_normal_entry(bool synchronized) { + + // rbx: methodOop + // rsi: sender sp + + // Because we redispatch "recursive" interpreter entries thru this same entry point + // the "input" register usage is a little strange and not what you expect coming + // from the call_stub. From the call stub rsi/rdi (current/previous) interpreter + // state are NULL but on "recursive" dispatches they are what you'd expect. + // rsi: current interpreter state (C++ interpreter) must preserve (null from call_stub/c1/c2) + + + // A single frame manager is plenty as we don't specialize for synchronized. We could and + // the code is pretty much ready. Would need to change the test below and for good measure + // modify generate_interpreter_state to only do the (pre) sync stuff stuff for synchronized + // routines. Not clear this is worth it yet. + + if (interpreter_frame_manager) return interpreter_frame_manager; + + address entry_point = __ pc(); + + // Fast accessor methods share this entry point. + // This works because frame manager is in the same codelet + if (UseFastAccessorMethods && !synchronized) __ bind(fast_accessor_slow_entry_path); + + Label dispatch_entry_2; + __ movl(rcx, rsi); + __ movl(rsi, 0); // no current activation + + __ jmp(dispatch_entry_2); + + const Register state = rsi; // current activation object, valid on entry + const Register locals = rdi; + + Label re_dispatch; + + __ bind(re_dispatch); + + // save sender sp (doesn't include return address + __ leal(rcx, Address(rsp, wordSize)); + + __ bind(dispatch_entry_2); + + // save sender sp + __ pushl(rcx); + + const Address size_of_parameters(rbx, methodOopDesc::size_of_parameters_offset()); + const Address size_of_locals (rbx, methodOopDesc::size_of_locals_offset()); + const Address access_flags (rbx, methodOopDesc::access_flags_offset()); + + // const Address monitor_block_top (rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize); + // const Address monitor_block_bot (rbp, frame::interpreter_frame_initial_sp_offset * wordSize); + // const Address monitor(rbp, frame::interpreter_frame_initial_sp_offset * wordSize - (int)sizeof(BasicObjectLock)); + + // get parameter size (always needed) + __ load_unsigned_word(rcx, size_of_parameters); + + // rbx: methodOop + // rcx: size of parameters + __ load_unsigned_word(rdx, size_of_locals); // get size of locals in words + + __ subl(rdx, rcx); // rdx = no. of additional locals + + // see if we've got enough room on the stack for locals plus overhead. + generate_stack_overflow_check(); // C++ + + // c++ interpreter does not use stack banging or any implicit exceptions + // leave for now to verify that check is proper. + bang_stack_shadow_pages(false); + + + + // compute beginning of parameters (rdi) + __ leal(locals, Address(rsp, rcx, Address::times_4, wordSize)); + + // save sender's sp + // __ movl(rcx, rsp); + + // get sender's sp + __ popl(rcx); + + // get return address + __ popl(rax); + + // rdx - # of additional locals + // allocate space for locals + // explicitly initialize locals + { + Label exit, loop; + __ testl(rdx, rdx); + __ jcc(Assembler::lessEqual, exit); // do nothing if rdx <= 0 + __ bind(loop); + __ pushl((int)NULL); // initialize local variables + __ decrement(rdx); // until everything initialized + __ jcc(Assembler::greater, loop); + __ bind(exit); + } + + + // Assumes rax = return address + + // allocate and initialize new interpreterState and method expression stack + // IN(locals) -> locals + // IN(state) -> any current interpreter activation + // destroys rax, rcx, rdx, rdi + // OUT (state) -> new interpreterState + // OUT(rsp) -> bottom of methods expression stack + + generate_compute_interpreter_state(state, locals, rcx, false); + + // Call interpreter + + Label call_interpreter; + __ bind(call_interpreter); + + // c++ interpreter does not use stack banging or any implicit exceptions + // leave for now to verify that check is proper. + bang_stack_shadow_pages(false); + + + // Call interpreter enter here if message is + // set and we know stack size is valid + + Label call_interpreter_2; + + __ bind(call_interpreter_2); + + { + const Register thread = rcx; + + __ pushl(state); // push arg to interpreter + __ movl(thread, STATE(_thread)); + + // We can setup the frame anchor with everything we want at this point + // as we are thread_in_Java and no safepoints can occur until we go to + // vm mode. We do have to clear flags on return from vm but that is it + // + __ movl(Address(thread, JavaThread::last_Java_fp_offset()), rbp); + __ movl(Address(thread, JavaThread::last_Java_sp_offset()), rsp); + + // Call the interpreter + + RuntimeAddress normal(CAST_FROM_FN_PTR(address, BytecodeInterpreter::run)); + RuntimeAddress checking(CAST_FROM_FN_PTR(address, BytecodeInterpreter::runWithChecks)); + + __ call(JvmtiExport::can_post_interpreter_events() ? checking : normal); + __ popl(rax); // discard parameter to run + // + // state is preserved since it is callee saved + // + + // reset_last_Java_frame + + __ movl(thread, STATE(_thread)); + __ reset_last_Java_frame(thread, true, true); + } + + // examine msg from interpreter to determine next action + + __ movl(rdx, STATE(_msg)); // Get new message + + Label call_method; + Label return_from_interpreted_method; + Label throw_exception; + Label bad_msg; + Label do_OSR; + + __ cmpl(rdx, (int)BytecodeInterpreter::call_method); + __ jcc(Assembler::equal, call_method); + __ cmpl(rdx, (int)BytecodeInterpreter::return_from_method); + __ jcc(Assembler::equal, return_from_interpreted_method); + __ cmpl(rdx, (int)BytecodeInterpreter::do_osr); + __ jcc(Assembler::equal, do_OSR); + __ cmpl(rdx, (int)BytecodeInterpreter::throwing_exception); + __ jcc(Assembler::equal, throw_exception); + __ cmpl(rdx, (int)BytecodeInterpreter::more_monitors); + __ jcc(Assembler::notEqual, bad_msg); + + // Allocate more monitor space, shuffle expression stack.... + + generate_more_monitors(); + + __ jmp(call_interpreter); + + // uncommon trap needs to jump to here to enter the interpreter (re-execute current bytecode) + unctrap_frame_manager_entry = __ pc(); + // + // Load the registers we need. + __ leal(state, Address(rbp, -(int)sizeof(BytecodeInterpreter))); + __ movl(rsp, STATE(_stack_limit)); // restore expression stack to full depth + __ jmp(call_interpreter_2); + + + + //============================================================================= + // Returning from a compiled method into a deopted method. The bytecode at the + // bcp has completed. The result of the bytecode is in the native abi (the tosca + // for the template based interpreter). Any stack space that was used by the + // bytecode that has completed has been removed (e.g. parameters for an invoke) + // so all that we have to do is place any pending result on the expression stack + // and resume execution on the next bytecode. + + + generate_deopt_handling(); + __ jmp(call_interpreter); + + + // Current frame has caught an exception we need to dispatch to the + // handler. We can get here because a native interpreter frame caught + // an exception in which case there is no handler and we must rethrow + // If it is a vanilla interpreted frame the we simply drop into the + // interpreter and let it do the lookup. + + Interpreter::_rethrow_exception_entry = __ pc(); + // rax: exception + // rdx: return address/pc that threw exception + + Label return_with_exception; + Label unwind_and_forward; + + // restore state pointer. + __ leal(state, Address(rbp, -sizeof(BytecodeInterpreter))); + + __ movl(rbx, STATE(_method)); // get method + __ movl(rcx, STATE(_thread)); // get thread + + // Store exception with interpreter will expect it + __ movl(Address(rcx, Thread::pending_exception_offset()), rax); + + // is current frame vanilla or native? + + __ movl(rdx, access_flags); + __ testl(rdx, JVM_ACC_NATIVE); + __ jcc(Assembler::zero, return_with_exception); // vanilla interpreted frame, handle directly + + // We drop thru to unwind a native interpreted frame with a pending exception + // We jump here for the initial interpreter frame with exception pending + // We unwind the current acivation and forward it to our caller. + + __ bind(unwind_and_forward); + + // unwind rbp, return stack to unextended value and re-push return address + + __ movl(rcx, STATE(_sender_sp)); + __ leave(); + __ popl(rdx); + __ movl(rsp, rcx); + __ pushl(rdx); + __ jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + + // Return point from a call which returns a result in the native abi + // (c1/c2/jni-native). This result must be processed onto the java + // expression stack. + // + // A pending exception may be present in which case there is no result present + + Label resume_interpreter; + Label do_float; + Label do_double; + Label done_conv; + + address compiled_entry = __ pc(); + + // The FPU stack is clean if UseSSE >= 2 but must be cleaned in other cases + if (UseSSE < 2) { + __ leal(state, Address(rbp, -sizeof(BytecodeInterpreter))); + __ movl(rbx, STATE(_result._to_call._callee)); // get method just executed + __ movl(rcx, Address(rbx, methodOopDesc::result_index_offset())); + __ cmpl(rcx, AbstractInterpreter::BasicType_as_index(T_FLOAT)); // Result stub address array index + __ jcc(Assembler::equal, do_float); + __ cmpl(rcx, AbstractInterpreter::BasicType_as_index(T_DOUBLE)); // Result stub address array index + __ jcc(Assembler::equal, do_double); +#ifdef COMPILER2 + __ empty_FPU_stack(); +#endif // COMPILER2 + __ jmp(done_conv); + + __ bind(do_float); +#ifdef COMPILER2 + for (int i = 1; i < 8; i++) { + __ ffree(i); + } +#endif // COMPILER2 + __ jmp(done_conv); + __ bind(do_double); +#ifdef COMPILER2 + for (int i = 1; i < 8; i++) { + __ ffree(i); + } +#endif // COMPILER2 + __ jmp(done_conv); + } else { + __ MacroAssembler::verify_FPU(0, "generate_return_entry_for compiled"); + __ jmp(done_conv); + } + + // emit a sentinel we can test for when converting an interpreter + // entry point to a compiled entry point. + __ a_long(Interpreter::return_sentinel); + __ a_long((int)compiled_entry); + + // Return point to interpreter from compiled/native method + + InternalAddress return_from_native_method(__ pc()); + + __ bind(done_conv); + + + // Result if any is in tosca. The java expression stack is in the state that the + // calling convention left it (i.e. params may or may not be present) + // Copy the result from tosca and place it on java expression stack. + + // Restore rsi as compiled code may not preserve it + + __ leal(state, Address(rbp, -sizeof(BytecodeInterpreter))); + + // restore stack to what we had when we left (in case i2c extended it) + + __ movl(rsp, STATE(_stack)); + __ leal(rsp, Address(rsp, wordSize)); + + // If there is a pending exception then we don't really have a result to process + + __ movl(rcx, STATE(_thread)); // get thread + __ cmpl(Address(rcx, Thread::pending_exception_offset()), (int)NULL); + __ jcc(Assembler::notZero, return_with_exception); + + // get method just executed + __ movl(rbx, STATE(_result._to_call._callee)); + + // callee left args on top of expression stack, remove them + __ load_unsigned_word(rcx, Address(rbx, methodOopDesc::size_of_parameters_offset())); + __ leal(rsp, Address(rsp, rcx, Address::times_4)); + + __ movl(rcx, Address(rbx, methodOopDesc::result_index_offset())); + ExternalAddress tosca_to_stack((address)CppInterpreter::_tosca_to_stack); + // Address index(noreg, rax, Address::times_4); + __ movptr(rcx, ArrayAddress(tosca_to_stack, Address(noreg, rcx, Address::times_4))); + // __ movl(rcx, Address(noreg, rcx, Address::times_4, int(AbstractInterpreter::_tosca_to_stack))); + __ call(rcx); // call result converter + __ jmp(resume_interpreter); + + // An exception is being caught on return to a vanilla interpreter frame. + // Empty the stack and resume interpreter + + __ bind(return_with_exception); + + // Exception present, empty stack + __ movl(rsp, STATE(_stack_base)); + __ jmp(resume_interpreter); + + // Return from interpreted method we return result appropriate to the caller (i.e. "recursive" + // interpreter call, or native) and unwind this interpreter activation. + // All monitors should be unlocked. + + __ bind(return_from_interpreted_method); + + Label return_to_initial_caller; + + __ movl(rbx, STATE(_method)); // get method just executed + __ cmpl(STATE(_prev_link), (int)NULL); // returning from "recursive" interpreter call? + __ movl(rax, Address(rbx, methodOopDesc::result_index_offset())); // get result type index + __ jcc(Assembler::equal, return_to_initial_caller); // back to native code (call_stub/c1/c2) + + // Copy result to callers java stack + ExternalAddress stack_to_stack((address)CppInterpreter::_stack_to_stack); + // Address index(noreg, rax, Address::times_4); + + __ movptr(rax, ArrayAddress(stack_to_stack, Address(noreg, rax, Address::times_4))); + // __ movl(rax, Address(noreg, rax, Address::times_4, int(AbstractInterpreter::_stack_to_stack))); + __ call(rax); // call result converter + + Label unwind_recursive_activation; + __ bind(unwind_recursive_activation); + + // returning to interpreter method from "recursive" interpreter call + // result converter left rax pointing to top of the java stack for method we are returning + // to. Now all we must do is unwind the state from the completed call + + __ movl(state, STATE(_prev_link)); // unwind state + __ leave(); // pop the frame + __ movl(rsp, rax); // unwind stack to remove args + + // Resume the interpreter. The current frame contains the current interpreter + // state object. + // + + __ bind(resume_interpreter); + + // state == interpreterState object for method we are resuming + + __ movl(STATE(_msg), (int)BytecodeInterpreter::method_resume); + __ leal(rsp, Address(rsp, -wordSize)); // prepush stack (result if any already present) + __ movl(STATE(_stack), rsp); // inform interpreter of new stack depth (parameters removed, + // result if any on stack already ) + __ movl(rsp, STATE(_stack_limit)); // restore expression stack to full depth + __ jmp(call_interpreter_2); // No need to bang + + // interpreter returning to native code (call_stub/c1/c2) + // convert result and unwind initial activation + // rax - result index + + __ bind(return_to_initial_caller); + ExternalAddress stack_to_native((address)CppInterpreter::_stack_to_native_abi); + // Address index(noreg, rax, Address::times_4); + + __ movptr(rax, ArrayAddress(stack_to_native, Address(noreg, rax, Address::times_4))); + __ call(rax); // call result converter + + Label unwind_initial_activation; + __ bind(unwind_initial_activation); + + // RETURN TO CALL_STUB/C1/C2 code (result if any in rax/rdx ST(0)) + + /* Current stack picture + + [ incoming parameters ] + [ extra locals ] + [ return address to CALL_STUB/C1/C2] + fp -> [ CALL_STUB/C1/C2 fp ] + BytecodeInterpreter object + expression stack + sp -> + + */ + + // return restoring the stack to the original sender_sp value + + __ movl(rcx, STATE(_sender_sp)); + __ leave(); + __ popl(rdi); // get return address + // set stack to sender's sp + __ movl(rsp, rcx); + __ jmp(rdi); // return to call_stub + + // OSR request, adjust return address to make current frame into adapter frame + // and enter OSR nmethod + + __ bind(do_OSR); + + Label remove_initial_frame; + + // We are going to pop this frame. Is there another interpreter frame underneath + // it or is it callstub/compiled? + + // Move buffer to the expected parameter location + __ movl(rcx, STATE(_result._osr._osr_buf)); + + __ movl(rax, STATE(_result._osr._osr_entry)); + + __ cmpl(STATE(_prev_link), (int)NULL); // returning from "recursive" interpreter call? + __ jcc(Assembler::equal, remove_initial_frame); // back to native code (call_stub/c1/c2) + + // __ movl(state, STATE(_prev_link)); // unwind state + __ movl(rsi, STATE(_sender_sp)); // get sender's sp in expected register + __ leave(); // pop the frame + __ movl(rsp, rsi); // trim any stack expansion + + + // We know we are calling compiled so push specialized return + // method uses specialized entry, push a return so we look like call stub setup + // this path will handle fact that result is returned in registers and not + // on the java stack. + + __ pushptr(return_from_native_method.addr()); + + __ jmp(rax); + + __ bind(remove_initial_frame); + + __ movl(rdx, STATE(_sender_sp)); + __ leave(); + // get real return + __ popl(rsi); + // set stack to sender's sp + __ movl(rsp, rdx); + // repush real return + __ pushl(rsi); + // Enter OSR nmethod + __ jmp(rax); + + + + + // Call a new method. All we do is (temporarily) trim the expression stack + // push a return address to bring us back to here and leap to the new entry. + + __ bind(call_method); + + // stack points to next free location and not top element on expression stack + // method expects sp to be pointing to topmost element + + __ movl(rsp, STATE(_stack)); // pop args to c++ interpreter, set sp to java stack top + __ leal(rsp, Address(rsp, wordSize)); + + __ movl(rbx, STATE(_result._to_call._callee)); // get method to execute + + // don't need a return address if reinvoking interpreter + + // Make it look like call_stub calling conventions + + // Get (potential) receiver + __ load_unsigned_word(rcx, size_of_parameters); // get size of parameters in words + + ExternalAddress recursive(CAST_FROM_FN_PTR(address, RecursiveInterpreterActivation)); + __ pushptr(recursive.addr()); // make it look good in the debugger + + InternalAddress entry(entry_point); + __ cmpptr(STATE(_result._to_call._callee_entry_point), entry.addr()); // returning to interpreter? + __ jcc(Assembler::equal, re_dispatch); // yes + + __ popl(rax); // pop dummy address + + + // get specialized entry + __ movl(rax, STATE(_result._to_call._callee_entry_point)); + // set sender SP + __ movl(rsi, rsp); + + // method uses specialized entry, push a return so we look like call stub setup + // this path will handle fact that result is returned in registers and not + // on the java stack. + + __ pushptr(return_from_native_method.addr()); + + __ jmp(rax); + + __ bind(bad_msg); + __ stop("Bad message from interpreter"); + + // Interpreted method "returned" with an exception pass it on... + // Pass result, unwind activation and continue/return to interpreter/call_stub + // We handle result (if any) differently based on return to interpreter or call_stub + + Label unwind_initial_with_pending_exception; + + __ bind(throw_exception); + __ cmpl(STATE(_prev_link), (int)NULL); // returning from recursive interpreter call? + __ jcc(Assembler::equal, unwind_initial_with_pending_exception); // no, back to native code (call_stub/c1/c2) + __ movl(rax, STATE(_locals)); // pop parameters get new stack value + __ addl(rax, wordSize); // account for prepush before we return + __ jmp(unwind_recursive_activation); + + __ bind(unwind_initial_with_pending_exception); + + // We will unwind the current (initial) interpreter frame and forward + // the exception to the caller. We must put the exception in the + // expected register and clear pending exception and then forward. + + __ jmp(unwind_and_forward); + + interpreter_frame_manager = entry_point; + return entry_point; +} + +address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) { + // determine code generation flags + bool synchronized = false; + address entry_point = NULL; + + switch (kind) { + case Interpreter::zerolocals : break; + case Interpreter::zerolocals_synchronized: synchronized = true; break; + case Interpreter::native : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(false); break; + case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(true); break; + case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break; + case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break; + case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break; + + case Interpreter::java_lang_math_sin : // fall thru + case Interpreter::java_lang_math_cos : // fall thru + case Interpreter::java_lang_math_tan : // fall thru + case Interpreter::java_lang_math_abs : // fall thru + case Interpreter::java_lang_math_log : // fall thru + case Interpreter::java_lang_math_log10 : // fall thru + case Interpreter::java_lang_math_sqrt : entry_point = ((InterpreterGenerator*)this)->generate_math_entry(kind); break; + default : ShouldNotReachHere(); break; + } + + if (entry_point) return entry_point; + + return ((InterpreterGenerator*)this)->generate_normal_entry(synchronized); + +} + +InterpreterGenerator::InterpreterGenerator(StubQueue* code) + : CppInterpreterGenerator(code) { + generate_all(); // down here so it can be "virtual" +} + +// Deoptimization helpers for C++ interpreter + +// How much stack a method activation needs in words. +int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { + + const int stub_code = 4; // see generate_call_stub + // Save space for one monitor to get into the interpreted method in case + // the method is synchronized + int monitor_size = method->is_synchronized() ? + 1*frame::interpreter_frame_monitor_size() : 0; + + // total static overhead size. Account for interpreter state object, return + // address, saved rbp and 2 words for a "static long no_params() method" issue. + + const int overhead_size = sizeof(BytecodeInterpreter)/wordSize + + ( frame::sender_sp_offset - frame::link_offset) + 2; + + const int method_stack = (method->max_locals() + method->max_stack()) * + Interpreter::stackElementWords(); + return overhead_size + method_stack + stub_code; +} + +// returns the activation size. +static int size_activation_helper(int extra_locals_size, int monitor_size) { + return (extra_locals_size + // the addition space for locals + 2*BytesPerWord + // return address and saved rbp + 2*BytesPerWord + // "static long no_params() method" issue + sizeof(BytecodeInterpreter) + // interpreterState + monitor_size); // monitors +} + +void BytecodeInterpreter::layout_interpreterState(interpreterState to_fill, + frame* caller, + frame* current, + methodOop method, + intptr_t* locals, + intptr_t* stack, + intptr_t* stack_base, + intptr_t* monitor_base, + intptr_t* frame_bottom, + bool is_top_frame + ) +{ + // What about any vtable? + // + to_fill->_thread = JavaThread::current(); + // This gets filled in later but make it something recognizable for now + to_fill->_bcp = method->code_base(); + to_fill->_locals = locals; + to_fill->_constants = method->constants()->cache(); + to_fill->_method = method; + to_fill->_mdx = NULL; + to_fill->_stack = stack; + if (is_top_frame && JavaThread::current()->popframe_forcing_deopt_reexecution() ) { + to_fill->_msg = deopt_resume2; + } else { + to_fill->_msg = method_resume; + } + to_fill->_result._to_call._bcp_advance = 0; + to_fill->_result._to_call._callee_entry_point = NULL; // doesn't matter to anyone + to_fill->_result._to_call._callee = NULL; // doesn't matter to anyone + to_fill->_prev_link = NULL; + + to_fill->_sender_sp = caller->unextended_sp(); + + if (caller->is_interpreted_frame()) { + interpreterState prev = caller->get_interpreterState(); + to_fill->_prev_link = prev; + // *current->register_addr(GR_Iprev_state) = (intptr_t) prev; + // Make the prev callee look proper + prev->_result._to_call._callee = method; + if (*prev->_bcp == Bytecodes::_invokeinterface) { + prev->_result._to_call._bcp_advance = 5; + } else { + prev->_result._to_call._bcp_advance = 3; + } + } + to_fill->_oop_temp = NULL; + to_fill->_stack_base = stack_base; + // Need +1 here because stack_base points to the word just above the first expr stack entry + // and stack_limit is supposed to point to the word just below the last expr stack entry. + // See generate_compute_interpreter_state. + to_fill->_stack_limit = stack_base - (method->max_stack() + 1); + to_fill->_monitor_base = (BasicObjectLock*) monitor_base; + + to_fill->_self_link = to_fill; + assert(stack >= to_fill->_stack_limit && stack < to_fill->_stack_base, + "Stack top out of range"); +} + +int AbstractInterpreter::layout_activation(methodOop method, + int tempcount, // + int popframe_extra_args, + int moncount, + int callee_param_count, + int callee_locals, + frame* caller, + frame* interpreter_frame, + bool is_top_frame) { + + assert(popframe_extra_args == 0, "FIX ME"); + // NOTE this code must exactly mimic what InterpreterGenerator::generate_compute_interpreter_state() + // does as far as allocating an interpreter frame. + // If interpreter_frame!=NULL, set up the method, locals, and monitors. + // The frame interpreter_frame, if not NULL, is guaranteed to be the right size, + // as determined by a previous call to this method. + // It is also guaranteed to be walkable even though it is in a skeletal state + // NOTE: return size is in words not bytes + // NOTE: tempcount is the current size of the java expression stack. For top most + // frames we will allocate a full sized expression stack and not the curback + // version that non-top frames have. + + // Calculate the amount our frame will be adjust by the callee. For top frame + // this is zero. + + // NOTE: ia64 seems to do this wrong (or at least backwards) in that it + // calculates the extra locals based on itself. Not what the callee does + // to it. So it ignores last_frame_adjust value. Seems suspicious as far + // as getting sender_sp correct. + + int extra_locals_size = (callee_locals - callee_param_count) * BytesPerWord; + int monitor_size = sizeof(BasicObjectLock) * moncount; + + // First calculate the frame size without any java expression stack + int short_frame_size = size_activation_helper(extra_locals_size, + monitor_size); + + // Now with full size expression stack + int full_frame_size = short_frame_size + method->max_stack() * BytesPerWord; + + // and now with only live portion of the expression stack + short_frame_size = short_frame_size + tempcount * BytesPerWord; + + // the size the activation is right now. Only top frame is full size + int frame_size = (is_top_frame ? full_frame_size : short_frame_size); + + if (interpreter_frame != NULL) { +#ifdef ASSERT + assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), "Frame not properly walkable"); +#endif + + // MUCHO HACK + + intptr_t* frame_bottom = (intptr_t*) ((intptr_t)interpreter_frame->sp() - (full_frame_size - frame_size)); + + /* Now fillin the interpreterState object */ + + // The state object is the first thing on the frame and easily located + + interpreterState cur_state = (interpreterState) ((intptr_t)interpreter_frame->fp() - sizeof(BytecodeInterpreter)); + + + // Find the locals pointer. This is rather simple on x86 because there is no + // confusing rounding at the callee to account for. We can trivially locate + // our locals based on the current fp(). + // Note: the + 2 is for handling the "static long no_params() method" issue. + // (too bad I don't really remember that issue well...) + + intptr_t* locals; + // If the caller is interpreted we need to make sure that locals points to the first + // argument that the caller passed and not in an area where the stack might have been extended. + // because the stack to stack to converter needs a proper locals value in order to remove the + // arguments from the caller and place the result in the proper location. Hmm maybe it'd be + // simpler if we simply stored the result in the BytecodeInterpreter object and let the c++ code + // adjust the stack?? HMMM QQQ + // + if (caller->is_interpreted_frame()) { + // locals must agree with the caller because it will be used to set the + // caller's tos when we return. + interpreterState prev = caller->get_interpreterState(); + // stack() is prepushed. + locals = prev->stack() + method->size_of_parameters(); + // locals = caller->unextended_sp() + (method->size_of_parameters() - 1); + if (locals != interpreter_frame->fp() + frame::sender_sp_offset + (method->max_locals() - 1) + 2) { + // os::breakpoint(); + } + } else { + // this is where a c2i would have placed locals (except for the +2) + locals = interpreter_frame->fp() + frame::sender_sp_offset + (method->max_locals() - 1) + 2; + } + + intptr_t* monitor_base = (intptr_t*) cur_state; + intptr_t* stack_base = (intptr_t*) ((intptr_t) monitor_base - monitor_size); + /* +1 because stack is always prepushed */ + intptr_t* stack = (intptr_t*) ((intptr_t) stack_base - (tempcount + 1) * BytesPerWord); + + + BytecodeInterpreter::layout_interpreterState(cur_state, + caller, + interpreter_frame, + method, + locals, + stack, + stack_base, + monitor_base, + frame_bottom, + is_top_frame); + + // BytecodeInterpreter::pd_layout_interpreterState(cur_state, interpreter_return_address, interpreter_frame->fp()); + } + return frame_size/BytesPerWord; +} + +#endif // CC_INTERP (all) diff --git a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.hpp b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.hpp new file mode 100644 index 00000000000..66c169e9722 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.hpp @@ -0,0 +1,33 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + + protected: + + // Size of interpreter code. Increase if too small. Interpreter will + // fail with a guarantee ("not enough space for interpreter generation"); + // if too small. + // Run with +PrintInterpreterSize to get the VM to print out the size. + // Max size with JVMTI and TaggedStackInterpreter + const static int InterpreterCodeSize = 168 * 1024; diff --git a/hotspot/src/cpu/x86/vm/debug_x86.cpp b/hotspot/src/cpu/x86/vm/debug_x86.cpp new file mode 100644 index 00000000000..d1a3dee3bd3 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/debug_x86.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_debug_x86.cpp.incl" + +void pd_ps(frame f) {} diff --git a/hotspot/src/cpu/x86/vm/depChecker_x86.cpp b/hotspot/src/cpu/x86/vm/depChecker_x86.cpp new file mode 100644 index 00000000000..fd26747f250 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/depChecker_x86.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_depChecker_x86.cpp.incl" + +// Nothing to do on i486 diff --git a/hotspot/src/cpu/x86/vm/depChecker_x86.hpp b/hotspot/src/cpu/x86/vm/depChecker_x86.hpp new file mode 100644 index 00000000000..cb8c701c86b --- /dev/null +++ b/hotspot/src/cpu/x86/vm/depChecker_x86.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Nothing to do on i486 diff --git a/hotspot/src/cpu/x86/vm/disassembler_x86.cpp b/hotspot/src/cpu/x86/vm/disassembler_x86.cpp new file mode 100644 index 00000000000..9e75de3548a --- /dev/null +++ b/hotspot/src/cpu/x86/vm/disassembler_x86.cpp @@ -0,0 +1,201 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_disassembler_x86.cpp.incl" + +#ifndef PRODUCT + +void* Disassembler::_library = NULL; +Disassembler::decode_func Disassembler::_decode_instruction = NULL; + +bool Disassembler::load_library() { + if (_library == NULL) { + char buf[1024]; + char ebuf[1024]; + sprintf(buf, "disassembler%s", os::dll_file_extension()); + _library = hpi::dll_load(buf, ebuf, sizeof ebuf); + if (_library != NULL) { + tty->print_cr("Loaded disassembler"); + _decode_instruction = CAST_TO_FN_PTR(Disassembler::decode_func, hpi::dll_lookup(_library, "decode_instruction")); + } + } + return (_library != NULL) && (_decode_instruction != NULL); +} + +class x86_env : public DisassemblerEnv { + private: + nmethod* code; + outputStream* output; + public: + x86_env(nmethod* rcode, outputStream* routput) { + code = rcode; + output = routput; + } + void print_label(intptr_t value); + void print_raw(char* str) { output->print_raw(str); } + void print(char* format, ...); + char* string_for_offset(intptr_t value); + char* string_for_constant(unsigned char* pc, intptr_t value, int is_decimal); +}; + + +void x86_env::print_label(intptr_t value) { + if (!Universe::is_fully_initialized()) { + output->print(INTPTR_FORMAT, value); + return; + } + address adr = (address) value; + if (StubRoutines::contains(adr)) { + StubCodeDesc* desc = StubCodeDesc::desc_for(adr); + const char * desc_name = "unknown stub"; + if (desc != NULL) { + desc_name = desc->name(); + } + output->print("Stub::%s", desc_name); + if (WizardMode) output->print(" " INTPTR_FORMAT, value); + } else { + output->print(INTPTR_FORMAT, value); + } +} + +void x86_env::print(char* format, ...) { + va_list ap; + va_start(ap, format); + output->vprint(format, ap); + va_end(ap); +} + +char* x86_env::string_for_offset(intptr_t value) { + stringStream st; + if (!Universe::is_fully_initialized()) { + st.print(INTX_FORMAT, value); + return st.as_string(); + } + BarrierSet* bs = Universe::heap()->barrier_set(); + BarrierSet::Name bsn = bs->kind(); + if (bs->kind() == BarrierSet::CardTableModRef && + (jbyte*) value == ((CardTableModRefBS*)(bs))->byte_map_base) { + st.print("word_map_base"); + } else { + st.print(INTX_FORMAT, value); + } + return st.as_string(); +} + +char* x86_env::string_for_constant(unsigned char* pc, intptr_t value, int is_decimal) { + stringStream st; + oop obj = NULL; + if (code && ((obj = code->embeddedOop_at(pc)) != NULL)) { + obj->print_value_on(&st); + } else { + if (is_decimal == 1) { + st.print(INTX_FORMAT, value); + } else { + st.print(INTPTR_FORMAT, value); + } + } + return st.as_string(); +} + + + +address Disassembler::decode_instruction(address start, DisassemblerEnv* env) { + return ((decode_func) _decode_instruction)(start, env); +} + + +void Disassembler::decode(CodeBlob* cb, outputStream* st) { + st = st ? st : tty; + st->print_cr("Decoding CodeBlob " INTPTR_FORMAT, cb); + decode(cb->instructions_begin(), cb->instructions_end(), st); +} + + +void Disassembler::decode(u_char* begin, u_char* end, outputStream* st) { + st = st ? st : tty; + + const int show_bytes = false; // for disassembler debugging + + if (!load_library()) { + st->print_cr("Could not load disassembler"); + return; + } + + x86_env env(NULL, st); + unsigned char* p = (unsigned char*) begin; + CodeBlob* cb = CodeCache::find_blob_unsafe(begin); + while (p < (unsigned char*) end) { + if (cb != NULL) { + cb->print_block_comment(st, (intptr_t)(p - cb->instructions_begin())); + } + + unsigned char* p0 = p; + st->print(" " INTPTR_FORMAT ": ", p); + p = decode_instruction(p, &env); + if (show_bytes) { + st->print("\t\t\t"); + while (p0 < p) st->print("%x ", *p0++); + } + st->cr(); + } +} + + +void Disassembler::decode(nmethod* nm, outputStream* st) { + st = st ? st : tty; + + st->print_cr("Decoding compiled method " INTPTR_FORMAT ":", nm); + st->print("Code:"); + st->cr(); + + if (!load_library()) { + st->print_cr("Could not load disassembler"); + return; + } + x86_env env(nm, st); + unsigned char* p = nm->instructions_begin(); + unsigned char* end = nm->instructions_end(); + while (p < end) { + if (p == nm->entry_point()) st->print_cr("[Entry Point]"); + if (p == nm->verified_entry_point()) st->print_cr("[Verified Entry Point]"); + if (p == nm->exception_begin()) st->print_cr("[Exception Handler]"); + if (p == nm->stub_begin()) st->print_cr("[Stub Code]"); + if (p == nm->consts_begin()) st->print_cr("[Constants]"); + nm->print_block_comment(st, (intptr_t)(p - nm->instructions_begin())); + unsigned char* p0 = p; + st->print(" " INTPTR_FORMAT ": ", p); + p = decode_instruction(p, &env); + nm->print_code_comment_on(st, 40, p0, p); + st->cr(); + // Output pc bucket ticks if we have any + address bucket_pc = FlatProfiler::bucket_start_for(p); + if (bucket_pc != NULL && bucket_pc > p0 && bucket_pc <= p) { + int bucket_count = FlatProfiler::bucket_count_for(bucket_pc); + tty->print_cr("[%d]", bucket_count); + } + } +} + +#endif // PRODUCT diff --git a/hotspot/src/cpu/x86/vm/disassembler_x86.hpp b/hotspot/src/cpu/x86/vm/disassembler_x86.hpp new file mode 100644 index 00000000000..75da808aedb --- /dev/null +++ b/hotspot/src/cpu/x86/vm/disassembler_x86.hpp @@ -0,0 +1,45 @@ +/* + * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The disassembler prints out intel 386 code annotated +// with Java specific information. + +class Disassembler { +#ifndef PRODUCT + private: + typedef address (*decode_func)(address start, DisassemblerEnv* env); + // points the library. + static void* _library; + // points to the decode function. + static decode_func _decode_instruction; + // tries to load library and return whether it succedded. + static bool load_library(); + // decodes one instruction and return the start of the next instruction. + static address decode_instruction(address start, DisassemblerEnv* env); +#endif + public: + static void decode(CodeBlob *cb, outputStream* st = NULL) PRODUCT_RETURN; + static void decode(nmethod* nm, outputStream* st = NULL) PRODUCT_RETURN; + static void decode(u_char* begin, u_char* end, outputStream* st = NULL) PRODUCT_RETURN; +}; diff --git a/hotspot/src/cpu/x86/vm/dump_x86_32.cpp b/hotspot/src/cpu/x86/vm/dump_x86_32.cpp new file mode 100644 index 00000000000..db80c6367f7 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/dump_x86_32.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_dump_x86_32.cpp.incl" + + + +// Generate the self-patching vtable method: +// +// This method will be called (as any other Klass virtual method) with +// the Klass itself as the first argument. Example: +// +// oop obj; +// int size = obj->klass()->klass_part()->oop_size(this); +// +// for which the virtual method call is Klass::oop_size(); +// +// The dummy method is called with the Klass object as the first +// operand, and an object as the second argument. +// + +//===================================================================== + +// All of the dummy methods in the vtable are essentially identical, +// differing only by an ordinal constant, and they bear no releationship +// to the original method which the caller intended. Also, there needs +// to be 'vtbl_list_size' instances of the vtable in order to +// differentiate between the 'vtable_list_size' original Klass objects. + +#define __ masm-> + +void CompactingPermGenGen::generate_vtable_methods(void** vtbl_list, + void** vtable, + char** md_top, + char* md_end, + char** mc_top, + char* mc_end) { + + intptr_t vtable_bytes = (num_virtuals * vtbl_list_size) * sizeof(void*); + *(intptr_t *)(*md_top) = vtable_bytes; + *md_top += sizeof(intptr_t); + void** dummy_vtable = (void**)*md_top; + *vtable = dummy_vtable; + *md_top += vtable_bytes; + + // Get ready to generate dummy methods. + + CodeBuffer cb((unsigned char*)*mc_top, mc_end - *mc_top); + MacroAssembler* masm = new MacroAssembler(&cb); + + Label common_code; + for (int i = 0; i < vtbl_list_size; ++i) { + for (int j = 0; j < num_virtuals; ++j) { + dummy_vtable[num_virtuals * i + j] = (void*)masm->pc(); + + // Load rax, with a value indicating vtable/offset pair. + // -- bits[ 7..0] (8 bits) which virtual method in table? + // -- bits[12..8] (5 bits) which virtual method table? + // -- must fit in 13-bit instruction immediate field. + __ movl(rax, (i << 8) + j); + __ jmp(common_code); + } + } + + __ bind(common_code); + +#ifdef WIN32 + // Expecting to be called with "thiscall" conventions -- the arguments + // are on the stack, except that the "this" pointer is in rcx. +#else + // Expecting to be called with Unix conventions -- the arguments + // are on the stack, including the "this" pointer. +#endif + + // In addition, rax was set (above) to the offset of the method in the + // table. + +#ifdef WIN32 + __ pushl(rcx); // save "this" +#endif + __ movl(rcx, rax); + __ shrl(rcx, 8); // isolate vtable identifier. + __ shll(rcx, LogBytesPerWord); + Address index(noreg, rcx, Address::times_1); + ExternalAddress vtbl((address)vtbl_list); + __ movptr(rdx, ArrayAddress(vtbl, index)); // get correct vtable address. +#ifdef WIN32 + __ popl(rcx); // restore "this" +#else + __ movl(rcx, Address(rsp, 4)); // fetch "this" +#endif + __ movl(Address(rcx, 0), rdx); // update vtable pointer. + + __ andl(rax, 0x00ff); // isolate vtable method index + __ shll(rax, LogBytesPerWord); + __ addl(rax, rdx); // address of real method pointer. + __ jmp(Address(rax, 0)); // get real method pointer. + + __ flush(); + + *mc_top = (char*)__ pc(); +} diff --git a/hotspot/src/cpu/x86/vm/dump_x86_64.cpp b/hotspot/src/cpu/x86/vm/dump_x86_64.cpp new file mode 100644 index 00000000000..7e80bbeacf2 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/dump_x86_64.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_dump_x86_64.cpp.incl" + + + +// Generate the self-patching vtable method: +// +// This method will be called (as any other Klass virtual method) with +// the Klass itself as the first argument. Example: +// +// oop obj; +// int size = obj->klass()->klass_part()->oop_size(this); +// +// for which the virtual method call is Klass::oop_size(); +// +// The dummy method is called with the Klass object as the first +// operand, and an object as the second argument. +// + +//===================================================================== + +// All of the dummy methods in the vtable are essentially identical, +// differing only by an ordinal constant, and they bear no releationship +// to the original method which the caller intended. Also, there needs +// to be 'vtbl_list_size' instances of the vtable in order to +// differentiate between the 'vtable_list_size' original Klass objects. + +#define __ masm-> + +void CompactingPermGenGen::generate_vtable_methods(void** vtbl_list, + void** vtable, + char** md_top, + char* md_end, + char** mc_top, + char* mc_end) { + + intptr_t vtable_bytes = (num_virtuals * vtbl_list_size) * sizeof(void*); + *(intptr_t *)(*md_top) = vtable_bytes; + *md_top += sizeof(intptr_t); + void** dummy_vtable = (void**)*md_top; + *vtable = dummy_vtable; + *md_top += vtable_bytes; + + // Get ready to generate dummy methods. + + CodeBuffer cb((unsigned char*)*mc_top, mc_end - *mc_top); + MacroAssembler* masm = new MacroAssembler(&cb); + + Label common_code; + for (int i = 0; i < vtbl_list_size; ++i) { + for (int j = 0; j < num_virtuals; ++j) { + dummy_vtable[num_virtuals * i + j] = (void*)masm->pc(); + + // Load eax with a value indicating vtable/offset pair. + // -- bits[ 7..0] (8 bits) which virtual method in table? + // -- bits[12..8] (5 bits) which virtual method table? + // -- must fit in 13-bit instruction immediate field. + __ movl(rax, (i << 8) + j); + __ jmp(common_code); + } + } + + __ bind(common_code); + + // Expecting to be called with "thiscall" convections -- the arguments + // are on the stack and the "this" pointer is in c_rarg0. In addition, rax + // was set (above) to the offset of the method in the table. + + __ pushq(c_rarg1); // save & free register + __ pushq(c_rarg0); // save "this" + __ movq(c_rarg0, rax); + __ shrq(c_rarg0, 8); // isolate vtable identifier. + __ shlq(c_rarg0, LogBytesPerWord); + __ lea(c_rarg1, ExternalAddress((address)vtbl_list)); // ptr to correct vtable list. + __ addq(c_rarg1, c_rarg0); // ptr to list entry. + __ movq(c_rarg1, Address(c_rarg1, 0)); // get correct vtable address. + __ popq(c_rarg0); // restore "this" + __ movq(Address(c_rarg0, 0), c_rarg1); // update vtable pointer. + + __ andq(rax, 0x00ff); // isolate vtable method index + __ shlq(rax, LogBytesPerWord); + __ addq(rax, c_rarg1); // address of real method pointer. + __ popq(c_rarg1); // restore register. + __ movq(rax, Address(rax, 0)); // get real method pointer. + __ jmp(rax); // jump to the real method. + + __ flush(); + + *mc_top = (char*)__ pc(); +} diff --git a/hotspot/src/cpu/x86/vm/frame_x86.cpp b/hotspot/src/cpu/x86/vm/frame_x86.cpp new file mode 100644 index 00000000000..5fa34595fc8 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/frame_x86.cpp @@ -0,0 +1,401 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_frame_x86.cpp.incl" + +#ifdef ASSERT +void RegisterMap::check_location_valid() { +} +#endif + + +// Profiling/safepoint support + +bool frame::safe_for_sender(JavaThread *thread) { + address sp = (address)_sp; + address fp = (address)_fp; + address unextended_sp = (address)_unextended_sp; + bool sp_safe = (sp != NULL && + (sp <= thread->stack_base()) && + (sp >= thread->stack_base() - thread->stack_size())); + bool unextended_sp_safe = (unextended_sp != NULL && + (unextended_sp <= thread->stack_base()) && + (unextended_sp >= thread->stack_base() - thread->stack_size())); + bool fp_safe = (fp != NULL && + (fp <= thread->stack_base()) && + (fp >= thread->stack_base() - thread->stack_size())); + if (sp_safe && unextended_sp_safe && fp_safe) { + // Unfortunately we can only check frame complete for runtime stubs and nmethod + // other generic buffer blobs are more problematic so we just assume they are + // ok. adapter blobs never have a frame complete and are never ok. + if (_cb != NULL && !_cb->is_frame_complete_at(_pc)) { + if (_cb->is_nmethod() || _cb->is_adapter_blob() || _cb->is_runtime_stub()) { + return false; + } + } + return true; + } + // Note: fp == NULL is not really a prerequisite for this to be safe to + // walk for c2. However we've modified the code such that if we get + // a failure with fp != NULL that we then try with FP == NULL. + // This is basically to mimic what a last_frame would look like if + // c2 had generated it. + if (sp_safe && unextended_sp_safe && fp == NULL) { + // frame must be complete if fp == NULL as fp == NULL is only sensible + // if we are looking at a nmethod and frame complete assures us of that. + if (_cb != NULL && _cb->is_frame_complete_at(_pc) && _cb->is_compiled_by_c2()) { + return true; + } + } + return false; +} + + +void frame::patch_pc(Thread* thread, address pc) { + if (TracePcPatching) { + tty->print_cr("patch_pc at address 0x%x [0x%x -> 0x%x] ", &((address *)sp())[-1], ((address *)sp())[-1], pc); + } + ((address *)sp())[-1] = pc; + _cb = CodeCache::find_blob(pc); + if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { + address orig = (((nmethod*)_cb)->get_original_pc(this)); + assert(orig == _pc, "expected original to be stored before patching"); + _deopt_state = is_deoptimized; + // leave _pc as is + } else { + _deopt_state = not_deoptimized; + _pc = pc; + } +} + +bool frame::is_interpreted_frame() const { + return Interpreter::contains(pc()); +} + +int frame::frame_size() const { + RegisterMap map(JavaThread::current(), false); + frame sender = this->sender(&map); + return sender.sp() - sp(); +} + +intptr_t* frame::entry_frame_argument_at(int offset) const { + // convert offset to index to deal with tsi + int index = (Interpreter::expr_offset_in_bytes(offset)/wordSize); + // Entry frame's arguments are always in relation to unextended_sp() + return &unextended_sp()[index]; +} + +// sender_sp +#ifdef CC_INTERP +intptr_t* frame::interpreter_frame_sender_sp() const { + assert(is_interpreted_frame(), "interpreted frame expected"); + // QQQ why does this specialize method exist if frame::sender_sp() does same thing? + // seems odd and if we always know interpreted vs. non then sender_sp() is really + // doing too much work. + return get_interpreterState()->sender_sp(); +} + +// monitor elements + +BasicObjectLock* frame::interpreter_frame_monitor_begin() const { + return get_interpreterState()->monitor_base(); +} + +BasicObjectLock* frame::interpreter_frame_monitor_end() const { + return (BasicObjectLock*) get_interpreterState()->stack_base(); +} + +#else // CC_INTERP + +intptr_t* frame::interpreter_frame_sender_sp() const { + assert(is_interpreted_frame(), "interpreted frame expected"); + return (intptr_t*) at(interpreter_frame_sender_sp_offset); +} + +void frame::set_interpreter_frame_sender_sp(intptr_t* sender_sp) { + assert(is_interpreted_frame(), "interpreted frame expected"); + ptr_at_put(interpreter_frame_sender_sp_offset, (intptr_t) sender_sp); +} + + +// monitor elements + +BasicObjectLock* frame::interpreter_frame_monitor_begin() const { + return (BasicObjectLock*) addr_at(interpreter_frame_monitor_block_bottom_offset); +} + +BasicObjectLock* frame::interpreter_frame_monitor_end() const { + BasicObjectLock* result = (BasicObjectLock*) *addr_at(interpreter_frame_monitor_block_top_offset); + // make sure the pointer points inside the frame + assert((intptr_t) fp() > (intptr_t) result, "result must < than frame pointer"); + assert((intptr_t) sp() <= (intptr_t) result, "result must >= than stack pointer"); + return result; +} + +void frame::interpreter_frame_set_monitor_end(BasicObjectLock* value) { + *((BasicObjectLock**)addr_at(interpreter_frame_monitor_block_top_offset)) = value; +} + +// Used by template based interpreter deoptimization +void frame::interpreter_frame_set_last_sp(intptr_t* sp) { + *((intptr_t**)addr_at(interpreter_frame_last_sp_offset)) = sp; +} +#endif // CC_INTERP + +frame frame::sender_for_entry_frame(RegisterMap* map) const { + assert(map != NULL, "map must be set"); + // Java frame called from C; skip all C frames and return top C + // frame of that chunk as the sender + JavaFrameAnchor* jfa = entry_frame_call_wrapper()->anchor(); + assert(!entry_frame_is_first(), "next Java fp must be non zero"); + assert(jfa->last_Java_sp() > sp(), "must be above this frame on stack"); + map->clear(); + assert(map->include_argument_oops(), "should be set by clear"); + if (jfa->last_Java_pc() != NULL ) { + frame fr(jfa->last_Java_sp(), jfa->last_Java_fp(), jfa->last_Java_pc()); + return fr; + } + frame fr(jfa->last_Java_sp(), jfa->last_Java_fp()); + return fr; +} + +frame frame::sender_for_interpreter_frame(RegisterMap* map) const { + // sp is the raw sp from the sender after adapter or interpreter extension + intptr_t* sp = (intptr_t*) addr_at(sender_sp_offset); + + // This is the sp before any possible extension (adapter/locals). + intptr_t* unextended_sp = interpreter_frame_sender_sp(); + + // The interpreter and compiler(s) always save EBP/RBP in a known + // location on entry. We must record where that location is + // so this if EBP/RBP was live on callout from c2 we can find + // the saved copy no matter what it called. + + // Since the interpreter always saves EBP/RBP if we record where it is then + // we don't have to always save EBP/RBP on entry and exit to c2 compiled + // code, on entry will be enough. +#ifdef COMPILER2 + if (map->update_map()) { + map->set_location(rbp->as_VMReg(), (address) addr_at(link_offset)); +#ifdef AMD64 + // this is weird "H" ought to be at a higher address however the + // oopMaps seems to have the "H" regs at the same address and the + // vanilla register. + // XXXX make this go away + if (true) { + map->set_location(rbp->as_VMReg()->next(), (address)addr_at(link_offset)); + } +#endif // AMD64 + } +#endif /* COMPILER2 */ + return frame(sp, unextended_sp, link(), sender_pc()); +} + + +//------------------------------sender_for_compiled_frame----------------------- +frame frame::sender_for_compiled_frame(RegisterMap* map) const { + assert(map != NULL, "map must be set"); + const bool c1_compiled = _cb->is_compiled_by_c1(); + + // frame owned by optimizing compiler + intptr_t* sender_sp = NULL; + + assert(_cb->frame_size() >= 0, "must have non-zero frame size"); + sender_sp = unextended_sp() + _cb->frame_size(); + + // On Intel the return_address is always the word on the stack + address sender_pc = (address) *(sender_sp-1); + + // This is the saved value of ebp which may or may not really be an fp. + // it is only an fp if the sender is an interpreter frame (or c1?) + + intptr_t *saved_fp = (intptr_t*)*(sender_sp - frame::sender_sp_offset); + + if (map->update_map()) { + // Tell GC to use argument oopmaps for some runtime stubs that need it. + // For C1, the runtime stub might not have oop maps, so set this flag + // outside of update_register_map. + map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); + if (_cb->oop_maps() != NULL) { + OopMapSet::update_register_map(this, map); + } + // Since the prolog does the save and restore of epb there is no oopmap + // for it so we must fill in its location as if there was an oopmap entry + // since if our caller was compiled code there could be live jvm state in it. + map->set_location(rbp->as_VMReg(), (address) (sender_sp - frame::sender_sp_offset)); +#ifdef AMD64 + // this is weird "H" ought to be at a higher address however the + // oopMaps seems to have the "H" regs at the same address and the + // vanilla register. + // XXXX make this go away + if (true) { + map->set_location(rbp->as_VMReg()->next(), (address) (sender_sp - frame::sender_sp_offset)); + } +#endif // AMD64 + } + + assert(sender_sp != sp(), "must have changed"); + return frame(sender_sp, saved_fp, sender_pc); +} + +frame frame::sender(RegisterMap* map) const { + // Default is we done have to follow them. The sender_for_xxx will + // update it accordingly + map->set_include_argument_oops(false); + + if (is_entry_frame()) return sender_for_entry_frame(map); + if (is_interpreted_frame()) return sender_for_interpreter_frame(map); + assert(_cb == CodeCache::find_blob(pc()),"Must be the same"); + + if (_cb != NULL) { + return sender_for_compiled_frame(map); + } + // Must be native-compiled frame, i.e. the marshaling code for native + // methods that exists in the core system. + return frame(sender_sp(), link(), sender_pc()); +} + + +bool frame::interpreter_frame_equals_unpacked_fp(intptr_t* fp) { + assert(is_interpreted_frame(), "must be interpreter frame"); + methodOop method = interpreter_frame_method(); + // When unpacking an optimized frame the frame pointer is + // adjusted with: + int diff = (method->max_locals() - method->size_of_parameters()) * + Interpreter::stackElementWords(); + return _fp == (fp - diff); +} + +void frame::pd_gc_epilog() { + // nothing done here now +} + +bool frame::is_interpreted_frame_valid() const { +// QQQ +#ifdef CC_INTERP +#else + assert(is_interpreted_frame(), "Not an interpreted frame"); + // These are reasonable sanity checks + if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) { + return false; + } + if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) { + return false; + } + if (fp() + interpreter_frame_initial_sp_offset < sp()) { + return false; + } + // These are hacks to keep us out of trouble. + // The problem with these is that they mask other problems + if (fp() <= sp()) { // this attempts to deal with unsigned comparison above + return false; + } + if (fp() - sp() > 4096) { // stack frames shouldn't be large. + return false; + } +#endif // CC_INTERP + return true; +} + +BasicType frame::interpreter_frame_result(oop* oop_result, jvalue* value_result) { +#ifdef CC_INTERP + // Needed for JVMTI. The result should always be in the interpreterState object + assert(false, "NYI"); + interpreterState istate = get_interpreterState(); +#endif // CC_INTERP + assert(is_interpreted_frame(), "interpreted frame expected"); + methodOop method = interpreter_frame_method(); + BasicType type = method->result_type(); + + intptr_t* tos_addr; + if (method->is_native()) { + // Prior to calling into the runtime to report the method_exit the possible + // return value is pushed to the native stack. If the result is a jfloat/jdouble + // then ST0 is saved before EAX/EDX. See the note in generate_native_result + tos_addr = (intptr_t*)sp(); + if (type == T_FLOAT || type == T_DOUBLE) { + // QQQ seems like this code is equivalent on the two platforms +#ifdef AMD64 + // This is times two because we do a push(ltos) after pushing XMM0 + // and that takes two interpreter stack slots. + tos_addr += 2 * Interpreter::stackElementWords(); +#else + tos_addr += 2; +#endif // AMD64 + } + } else { + tos_addr = (intptr_t*)interpreter_frame_tos_address(); + } + + switch (type) { + case T_OBJECT : + case T_ARRAY : { + oop obj; + if (method->is_native()) { +#ifdef CC_INTERP + obj = istate->_oop_temp; +#else + obj = (oop) at(interpreter_frame_oop_temp_offset); +#endif // CC_INTERP + } else { + oop* obj_p = (oop*)tos_addr; + obj = (obj_p == NULL) ? (oop)NULL : *obj_p; + } + assert(obj == NULL || Universe::heap()->is_in(obj), "sanity check"); + *oop_result = obj; + break; + } + case T_BOOLEAN : value_result->z = *(jboolean*)tos_addr; break; + case T_BYTE : value_result->b = *(jbyte*)tos_addr; break; + case T_CHAR : value_result->c = *(jchar*)tos_addr; break; + case T_SHORT : value_result->s = *(jshort*)tos_addr; break; + case T_INT : value_result->i = *(jint*)tos_addr; break; + case T_LONG : value_result->j = *(jlong*)tos_addr; break; + case T_FLOAT : { +#ifdef AMD64 + value_result->f = *(jfloat*)tos_addr; +#else + if (method->is_native()) { + jdouble d = *(jdouble*)tos_addr; // Result was in ST0 so need to convert to jfloat + value_result->f = (jfloat)d; + } else { + value_result->f = *(jfloat*)tos_addr; + } +#endif // AMD64 + break; + } + case T_DOUBLE : value_result->d = *(jdouble*)tos_addr; break; + case T_VOID : /* Nothing to do */ break; + default : ShouldNotReachHere(); + } + + return type; +} + + +intptr_t* frame::interpreter_frame_tos_at(jint offset) const { + int index = (Interpreter::expr_offset_in_bytes(offset)/wordSize); + return &interpreter_frame_tos_address()[index]; +} diff --git a/hotspot/src/cpu/x86/vm/frame_x86.hpp b/hotspot/src/cpu/x86/vm/frame_x86.hpp new file mode 100644 index 00000000000..3668f2da295 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/frame_x86.hpp @@ -0,0 +1,193 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A frame represents a physical stack frame (an activation). Frames can be +// C or Java frames, and the Java frames can be interpreted or compiled. +// In contrast, vframes represent source-level activations, so that one physical frame +// can correspond to multiple source level frames because of inlining. +// A frame is comprised of {pc, fp, sp} +// ------------------------------ Asm interpreter ---------------------------------------- +// Layout of asm interpreter frame: +// [expression stack ] * <- sp +// [monitors ] \ +// ... | monitor block size +// [monitors ] / +// [monitor block size ] +// [byte code index/pointr] = bcx() bcx_offset +// [pointer to locals ] = locals() locals_offset +// [constant pool cache ] = cache() cache_offset +// [methodData ] = mdp() mdx_offset +// [methodOop ] = method() method_offset +// [last sp ] = last_sp() last_sp_offset +// [old stack pointer ] (sender_sp) sender_sp_offset +// [old frame pointer ] <- fp = link() +// [return pc ] +// [oop temp ] (only for native calls) +// [locals and parameters ] +// <- sender sp +// ------------------------------ Asm interpreter ---------------------------------------- + +// ------------------------------ C++ interpreter ---------------------------------------- +// +// Layout of C++ interpreter frame: (While executing in BytecodeInterpreter::run) +// +// <- SP (current esp/rsp) +// [local variables ] BytecodeInterpreter::run local variables +// ... BytecodeInterpreter::run local variables +// [local variables ] BytecodeInterpreter::run local variables +// [old frame pointer ] fp [ BytecodeInterpreter::run's ebp/rbp ] +// [return pc ] (return to frame manager) +// [interpreter_state* ] (arg to BytecodeInterpreter::run) -------------- +// [expression stack ] <- last_Java_sp | +// [... ] * <- interpreter_state.stack | +// [expression stack ] * <- interpreter_state.stack_base | +// [monitors ] \ | +// ... | monitor block size | +// [monitors ] / <- interpreter_state.monitor_base | +// [struct interpretState ] <-----------------------------------------| +// [return pc ] (return to callee of frame manager [1] +// [locals and parameters ] +// <- sender sp + +// [1] When the c++ interpreter calls a new method it returns to the frame +// manager which allocates a new frame on the stack. In that case there +// is no real callee of this newly allocated frame. The frame manager is +// aware of the additional frame(s) and will pop them as nested calls +// complete. Howevers tTo make it look good in the debugger the frame +// manager actually installs a dummy pc pointing to RecursiveInterpreterActivation +// with a fake interpreter_state* parameter to make it easy to debug +// nested calls. + +// Note that contrary to the layout for the assembly interpreter the +// expression stack allocated for the C++ interpreter is full sized. +// However this is not as bad as it seems as the interpreter frame_manager +// will truncate the unused space on succesive method calls. +// +// ------------------------------ C++ interpreter ---------------------------------------- + + public: + enum { + pc_return_offset = 0, + // All frames + link_offset = 0, + return_addr_offset = 1, + // non-interpreter frames + sender_sp_offset = 2, + +#ifndef CC_INTERP + + // Interpreter frames + interpreter_frame_result_handler_offset = 3, // for native calls only + interpreter_frame_oop_temp_offset = 2, // for native calls only + + interpreter_frame_sender_sp_offset = -1, + // outgoing sp before a call to an invoked method + interpreter_frame_last_sp_offset = interpreter_frame_sender_sp_offset - 1, + interpreter_frame_method_offset = interpreter_frame_last_sp_offset - 1, + interpreter_frame_mdx_offset = interpreter_frame_method_offset - 1, + interpreter_frame_cache_offset = interpreter_frame_mdx_offset - 1, + interpreter_frame_locals_offset = interpreter_frame_cache_offset - 1, + interpreter_frame_bcx_offset = interpreter_frame_locals_offset - 1, + interpreter_frame_initial_sp_offset = interpreter_frame_bcx_offset - 1, + + interpreter_frame_monitor_block_top_offset = interpreter_frame_initial_sp_offset, + interpreter_frame_monitor_block_bottom_offset = interpreter_frame_initial_sp_offset, + +#endif // CC_INTERP + + // Entry frames +#ifdef AMD64 +#ifdef _WIN64 + entry_frame_after_call_words = 8, + entry_frame_call_wrapper_offset = 2, + + arg_reg_save_area_bytes = 32, // Register argument save area +#else + entry_frame_after_call_words = 13, + entry_frame_call_wrapper_offset = -6, + + arg_reg_save_area_bytes = 0, +#endif // _WIN64 +#else + entry_frame_call_wrapper_offset = 2, +#endif // AMD64 + + // Native frames + + native_frame_initial_param_offset = 2 + + }; + + intptr_t ptr_at(int offset) const { + return *ptr_at_addr(offset); + } + + void ptr_at_put(int offset, intptr_t value) { + *ptr_at_addr(offset) = value; + } + + private: + // an additional field beyond _sp and _pc: + intptr_t* _fp; // frame pointer + // The interpreter and adapters will extend the frame of the caller. + // Since oopMaps are based on the sp of the caller before extension + // we need to know that value. However in order to compute the address + // of the return address we need the real "raw" sp. Since sparc already + // uses sp() to mean "raw" sp and unextended_sp() to mean the caller's + // original sp we use that convention. + + intptr_t* _unextended_sp; + + intptr_t* ptr_at_addr(int offset) const { + return (intptr_t*) addr_at(offset); + } + + public: + // Constructors + + frame(intptr_t* sp, intptr_t* fp, address pc); + + frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc); + + frame(intptr_t* sp, intptr_t* fp); + + // accessors for the instance variables + intptr_t* fp() const { return _fp; } + + inline address* sender_pc_addr() const; + + // return address of param, zero origin index. + inline address* native_param_addr(int idx) const; + + // expression stack tos if we are nested in a java call + intptr_t* interpreter_frame_last_sp() const; + +#ifndef CC_INTERP + // deoptimization support + void interpreter_frame_set_last_sp(intptr_t* sp); +#endif // CC_INTERP + +#ifdef CC_INTERP + inline interpreterState get_interpreterState() const; +#endif // CC_INTERP diff --git a/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp b/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp new file mode 100644 index 00000000000..095fa10163c --- /dev/null +++ b/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp @@ -0,0 +1,288 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline functions for Intel frames: + +// Constructors: + +inline frame::frame() { + _pc = NULL; + _sp = NULL; + _unextended_sp = NULL; + _fp = NULL; + _cb = NULL; + _deopt_state = unknown; +} + +inline frame:: frame(intptr_t* sp, intptr_t* fp, address pc) { + _sp = sp; + _unextended_sp = sp; + _fp = fp; + _pc = pc; + assert(pc != NULL, "no pc?"); + _cb = CodeCache::find_blob(pc); + _deopt_state = not_deoptimized; + if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { + _pc = (((nmethod*)_cb)->get_original_pc(this)); + _deopt_state = is_deoptimized; + } else { + _deopt_state = not_deoptimized; + } +} + +inline frame:: frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc) { + _sp = sp; + _unextended_sp = unextended_sp; + _fp = fp; + _pc = pc; + assert(pc != NULL, "no pc?"); + _cb = CodeCache::find_blob(pc); + _deopt_state = not_deoptimized; + if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { + _pc = (((nmethod*)_cb)->get_original_pc(this)); + _deopt_state = is_deoptimized; + } else { + _deopt_state = not_deoptimized; + } +} + +inline frame::frame(intptr_t* sp, intptr_t* fp) { + _sp = sp; + _unextended_sp = sp; + _fp = fp; + _pc = (address)(sp[-1]); + assert(_pc != NULL, "no pc?"); + _cb = CodeCache::find_blob(_pc); + // In case of native stubs, the pc retreived here might be + // wrong. (the _last_native_pc will have the right value) + // So do not put add any asserts on the _pc here. + + // QQQ The above comment is wrong and has been wrong for years. This constructor + // should (and MUST) not be called in that situation. In the native situation + // the pc should be supplied to the constructor. + _deopt_state = not_deoptimized; + if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { + _pc = (((nmethod*)_cb)->get_original_pc(this)); + _deopt_state = is_deoptimized; + } else { + _deopt_state = not_deoptimized; + } +} + +// Accessors + +inline bool frame::equal(frame other) const { + bool ret = sp() == other.sp() + && unextended_sp() == other.unextended_sp() + && fp() == other.fp() + && pc() == other.pc(); + assert(!ret || ret && cb() == other.cb() && _deopt_state == other._deopt_state, "inconsistent construction"); + return ret; +} + +// Return unique id for this frame. The id must have a value where we can distinguish +// identity and younger/older relationship. NULL represents an invalid (incomparable) +// frame. +inline intptr_t* frame::id(void) const { return unextended_sp(); } + +// Relationals on frames based +// Return true if the frame is younger (more recent activation) than the frame represented by id +inline bool frame::is_younger(intptr_t* id) const { assert(this->id() != NULL && id != NULL, "NULL frame id"); + return this->id() < id ; } + +// Return true if the frame is older (less recent activation) than the frame represented by id +inline bool frame::is_older(intptr_t* id) const { assert(this->id() != NULL && id != NULL, "NULL frame id"); + return this->id() > id ; } + + + +inline intptr_t* frame::link() const { return (intptr_t*) *(intptr_t **)addr_at(link_offset); } +inline void frame::set_link(intptr_t* addr) { *(intptr_t **)addr_at(link_offset) = addr; } + + +inline intptr_t* frame::unextended_sp() const { return _unextended_sp; } + +// Return address: + +inline address* frame::sender_pc_addr() const { return (address*) addr_at( return_addr_offset); } +inline address frame::sender_pc() const { return *sender_pc_addr(); } + +// return address of param, zero origin index. +inline address* frame::native_param_addr(int idx) const { return (address*) addr_at( native_frame_initial_param_offset+idx); } + +#ifdef CC_INTERP + +inline interpreterState frame::get_interpreterState() const { + return ((interpreterState)addr_at( -sizeof(BytecodeInterpreter)/wordSize )); +} + +inline intptr_t* frame::sender_sp() const { + // Hmm this seems awfully expensive QQQ, is this really called with interpreted frames? + if (is_interpreted_frame()) { + assert(false, "should never happen"); + return get_interpreterState()->sender_sp(); + } else { + return addr_at(sender_sp_offset); + } +} + +inline intptr_t** frame::interpreter_frame_locals_addr() const { + assert(is_interpreted_frame(), "must be interpreted"); + return &(get_interpreterState()->_locals); +} + +inline intptr_t* frame::interpreter_frame_bcx_addr() const { + assert(is_interpreted_frame(), "must be interpreted"); + return (jint*) &(get_interpreterState()->_bcp); +} + + +// Constant pool cache + +inline constantPoolCacheOop* frame::interpreter_frame_cache_addr() const { + assert(is_interpreted_frame(), "must be interpreted"); + return &(get_interpreterState()->_constants); +} + +// Method + +inline methodOop* frame::interpreter_frame_method_addr() const { + assert(is_interpreted_frame(), "must be interpreted"); + return &(get_interpreterState()->_method); +} + +inline intptr_t* frame::interpreter_frame_mdx_addr() const { + assert(is_interpreted_frame(), "must be interpreted"); + return (jint*) &(get_interpreterState()->_mdx); +} + +// top of expression stack +inline intptr_t* frame::interpreter_frame_tos_address() const { + assert(is_interpreted_frame(), "wrong frame type"); + return get_interpreterState()->_stack + 1; +} + +#else /* asm interpreter */ +inline intptr_t* frame::sender_sp() const { return addr_at( sender_sp_offset); } + +inline intptr_t** frame::interpreter_frame_locals_addr() const { + return (intptr_t**)addr_at(interpreter_frame_locals_offset); +} + +inline intptr_t* frame::interpreter_frame_last_sp() const { + return *(intptr_t**)addr_at(interpreter_frame_last_sp_offset); +} + +inline intptr_t* frame::interpreter_frame_bcx_addr() const { + return (intptr_t*)addr_at(interpreter_frame_bcx_offset); +} + + +inline intptr_t* frame::interpreter_frame_mdx_addr() const { + return (intptr_t*)addr_at(interpreter_frame_mdx_offset); +} + + + +// Constant pool cache + +inline constantPoolCacheOop* frame::interpreter_frame_cache_addr() const { + return (constantPoolCacheOop*)addr_at(interpreter_frame_cache_offset); +} + +// Method + +inline methodOop* frame::interpreter_frame_method_addr() const { + return (methodOop*)addr_at(interpreter_frame_method_offset); +} + +// top of expression stack +inline intptr_t* frame::interpreter_frame_tos_address() const { + intptr_t* last_sp = interpreter_frame_last_sp(); + if (last_sp == NULL ) { + return sp(); + } else { + // sp() may have been extended by an adapter + assert(last_sp < fp() && last_sp >= sp(), "bad tos"); + return last_sp; + } +} + +#endif /* CC_INTERP */ + +inline int frame::pd_oop_map_offset_adjustment() const { + return 0; +} + +inline int frame::interpreter_frame_monitor_size() { + return BasicObjectLock::size(); +} + + +// expression stack +// (the max_stack arguments are used by the GC; see class FrameClosure) + +inline intptr_t* frame::interpreter_frame_expression_stack() const { + intptr_t* monitor_end = (intptr_t*) interpreter_frame_monitor_end(); + return monitor_end-1; +} + + +inline jint frame::interpreter_frame_expression_stack_direction() { return -1; } + + +// Entry frames + +inline JavaCallWrapper* frame::entry_frame_call_wrapper() const { + return (JavaCallWrapper*)at(entry_frame_call_wrapper_offset); +} + + +// Compiled frames + +inline int frame::local_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors) { + return (nof_args - local_index + (local_index < nof_args ? 1: -1)); +} + +inline int frame::monitor_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors) { + return local_offset_for_compiler(local_index, nof_args, max_nof_locals, max_nof_monitors); +} + +inline int frame::min_local_offset_for_compiler(int nof_args, int max_nof_locals, int max_nof_monitors) { + return (nof_args - (max_nof_locals + max_nof_monitors*2) - 1); +} + +inline bool frame::volatile_across_calls(Register reg) { + return true; +} + + + +inline oop frame::saved_oop_result(RegisterMap* map) const { + return *((oop*) map->location(rax->as_VMReg())); +} + +inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { + *((oop*) map->location(rax->as_VMReg())) = obj; +} diff --git a/hotspot/src/cpu/x86/vm/globalDefinitions_x86.hpp b/hotspot/src/cpu/x86/vm/globalDefinitions_x86.hpp new file mode 100644 index 00000000000..da08508fc46 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/globalDefinitions_x86.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +const int StackAlignmentInBytes = 16; diff --git a/hotspot/src/cpu/x86/vm/globals_x86.hpp b/hotspot/src/cpu/x86/vm/globals_x86.hpp new file mode 100644 index 00000000000..67f27d68125 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/globals_x86.hpp @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for platform dependent flags used by the runtime system. +// (see globals.hpp) +// + +define_pd_global(bool, ConvertSleepToYield, true); +define_pd_global(bool, ShareVtableStubs, true); +define_pd_global(bool, CountInterpCalls, true); + +define_pd_global(bool, ImplicitNullChecks, true); // Generate code for implicit null checks +define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs past to check cast + +// See 4827828 for this change. There is no globals_core_i486.hpp. I can't +// assign a different value for C2 without touching a number of files. Use +// #ifdef to minimize the change as it's late in Mantis. -- FIXME. +// c1 doesn't have this problem because the fix to 4858033 assures us +// the the vep is aligned at CodeEntryAlignment whereas c2 only aligns +// the uep and the vep doesn't get real alignment but just slops on by +// only assured that the entry instruction meets the 5 byte size requirement. +#ifdef COMPILER2 +define_pd_global(intx, CodeEntryAlignment, 32); +#else +define_pd_global(intx, CodeEntryAlignment, 16); +#endif // COMPILER2 + +define_pd_global(bool, NeedsDeoptSuspend, false); // only register window machines need this + +define_pd_global(uintx, TLABSize, 0); +#ifdef AMD64 +define_pd_global(uintx, NewSize, ScaleForWordSize(2048 * K)); +// Very large C++ stack frames using solaris-amd64 optimized builds +// due to lack of optimization caused by C++ compiler bugs +define_pd_global(intx, StackShadowPages, SOLARIS_ONLY(20) NOT_SOLARIS(6) DEBUG_ONLY(+2)); +#else +define_pd_global(uintx, NewSize, 1024 * K); +define_pd_global(intx, StackShadowPages, 3 DEBUG_ONLY(+1)); +#endif // AMD64 +define_pd_global(intx, InlineFrequencyCount, 100); +define_pd_global(intx, PreInflateSpin, 10); + +define_pd_global(intx, StackYellowPages, 2); +define_pd_global(intx, StackRedPages, 1); + +define_pd_global(bool, RewriteBytecodes, true); +define_pd_global(bool, RewriteFrequentPairs, true); diff --git a/hotspot/src/cpu/x86/vm/icBuffer_x86.cpp b/hotspot/src/cpu/x86/vm/icBuffer_x86.cpp new file mode 100644 index 00000000000..854366cd89f --- /dev/null +++ b/hotspot/src/cpu/x86/vm/icBuffer_x86.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_icBuffer_x86.cpp.incl" + +int InlineCacheBuffer::ic_stub_code_size() { + return NativeMovConstReg::instruction_size + + NativeJump::instruction_size + + 1; + // so that code_end can be set in CodeBuffer + // 64bit 16 = 5 + 10 bytes + 1 byte + // 32bit 11 = 10 bytes + 1 byte +} + + + +void InlineCacheBuffer::assemble_ic_buffer_code(address code_begin, oop cached_oop, address entry_point) { + ResourceMark rm; + CodeBuffer code(code_begin, ic_stub_code_size()); + MacroAssembler* masm = new MacroAssembler(&code); + // note: even though the code contains an embedded oop, we do not need reloc info + // because + // (1) the oop is old (i.e., doesn't matter for scavenges) + // (2) these ICStubs are removed *before* a GC happens, so the roots disappear + assert(cached_oop == NULL || cached_oop->is_perm(), "must be perm oop"); + masm->lea(rax, OopAddress((address) cached_oop)); + masm->jump(ExternalAddress(entry_point)); +} + + +address InlineCacheBuffer::ic_buffer_entry_point(address code_begin) { + NativeMovConstReg* move = nativeMovConstReg_at(code_begin); // creation also verifies the object + NativeJump* jump = nativeJump_at(move->next_instruction_address()); + return jump->jump_destination(); +} + + +oop InlineCacheBuffer::ic_buffer_cached_oop(address code_begin) { + // creation also verifies the object + NativeMovConstReg* move = nativeMovConstReg_at(code_begin); + // Verifies the jump + NativeJump* jump = nativeJump_at(move->next_instruction_address()); + return (oop)move->data(); +} diff --git a/hotspot/src/cpu/x86/vm/icache_x86.cpp b/hotspot/src/cpu/x86/vm/icache_x86.cpp new file mode 100644 index 00000000000..6a031f07413 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/icache_x86.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_icache_x86.cpp.incl" + +#define __ _masm-> + +void ICacheStubGenerator::generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub) { + StubCodeMark mark(this, "ICache", "flush_icache_stub"); + + address start = __ pc(); +#ifdef AMD64 + + const Register addr = c_rarg0; + const Register lines = c_rarg1; + const Register magic = c_rarg2; + + Label flush_line, done; + + __ testl(lines, lines); + __ jcc(Assembler::zero, done); + + // Force ordering wrt cflush. + // Other fence and sync instructions won't do the job. + __ mfence(); + + __ bind(flush_line); + __ clflush(Address(addr, 0)); + __ addq(addr, ICache::line_size); + __ decrementl(lines); + __ jcc(Assembler::notZero, flush_line); + + __ mfence(); + + __ bind(done); + +#else + const Address magic(rsp, 3*wordSize); + __ lock(); __ addl(Address(rsp, 0), 0); +#endif // AMD64 + __ movl(rax, magic); // Handshake with caller to make sure it happened! + __ ret(0); + + // Must be set here so StubCodeMark destructor can call the flush stub. + *flush_icache_stub = (ICache::flush_icache_stub_t)start; +} + +#undef __ diff --git a/hotspot/src/cpu/x86/vm/icache_x86.hpp b/hotspot/src/cpu/x86/vm/icache_x86.hpp new file mode 100644 index 00000000000..55ac4b8a488 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/icache_x86.hpp @@ -0,0 +1,55 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Interface for updating the instruction cache. Whenever the VM modifies +// code, part of the processor instruction cache potentially has to be flushed. + +// On the x86, this is a no-op -- the I-cache is guaranteed to be consistent +// after the next jump, and the VM never modifies instructions directly ahead +// of the instruction fetch path. + +// [phh] It's not clear that the above comment is correct, because on an MP +// system where the dcaches are not snooped, only the thread doing the invalidate +// will see the update. Even in the snooped case, a memory fence would be +// necessary if stores weren't ordered. Fortunately, they are on all known +// x86 implementations. + +class ICache : public AbstractICache { + public: +#ifdef AMD64 + enum { + stub_size = 64, // Size of the icache flush stub in bytes + line_size = 32, // Icache line size in bytes + log2_line_size = 5 // log2(line_size) + }; + + // Use default implementation +#else + enum { + stub_size = 16, // Size of the icache flush stub in bytes + line_size = BytesPerWord, // conservative + log2_line_size = LogBytesPerWord // log2(line_size) + }; +#endif // AMD64 +}; diff --git a/hotspot/src/cpu/x86/vm/interp_masm_x86_32.cpp b/hotspot/src/cpu/x86/vm/interp_masm_x86_32.cpp new file mode 100644 index 00000000000..4b174357a2f --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_32.cpp @@ -0,0 +1,1546 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interp_masm_x86_32.cpp.incl" + + +// Implementation of InterpreterMacroAssembler +#ifdef CC_INTERP +void InterpreterMacroAssembler::get_method(Register reg) { + movl(reg, Address(rbp, -(sizeof(BytecodeInterpreter) + 2 * wordSize))); + movl(reg, Address(reg, byte_offset_of(BytecodeInterpreter, _method))); +} +#endif // CC_INTERP + + +#ifndef CC_INTERP +void InterpreterMacroAssembler::call_VM_leaf_base( + address entry_point, + int number_of_arguments +) { + // interpreter specific + // + // Note: No need to save/restore bcp & locals (rsi & rdi) pointer + // since these are callee saved registers and no blocking/ + // GC can happen in leaf calls. + // Further Note: DO NOT save/restore bcp/locals. If a caller has + // already saved them so that it can use rsi/rdi as temporaries + // then a save/restore here will DESTROY the copy the caller + // saved! There used to be a save_bcp() that only happened in + // the ASSERT path (no restore_bcp). Which caused bizarre failures + // when jvm built with ASSERTs. +#ifdef ASSERT + { Label L; + cmpl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + jcc(Assembler::equal, L); + stop("InterpreterMacroAssembler::call_VM_leaf_base: last_sp != NULL"); + bind(L); + } +#endif + // super call + MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments); + // interpreter specific + + // Used to ASSERT that rsi/rdi were equal to frame's bcp/locals + // but since they may not have been saved (and we don't want to + // save them here (see note above) the assert is invalid. +} + + +void InterpreterMacroAssembler::call_VM_base( + Register oop_result, + Register java_thread, + Register last_java_sp, + address entry_point, + int number_of_arguments, + bool check_exceptions +) { +#ifdef ASSERT + { Label L; + cmpl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + jcc(Assembler::equal, L); + stop("InterpreterMacroAssembler::call_VM_base: last_sp != NULL"); + bind(L); + } +#endif /* ASSERT */ + // interpreter specific + // + // Note: Could avoid restoring locals ptr (callee saved) - however doesn't + // really make a difference for these runtime calls, since they are + // slow anyway. Btw., bcp must be saved/restored since it may change + // due to GC. + assert(java_thread == noreg , "not expecting a precomputed java thread"); + save_bcp(); + // super call + MacroAssembler::call_VM_base(oop_result, java_thread, last_java_sp, entry_point, number_of_arguments, check_exceptions); + // interpreter specific + restore_bcp(); + restore_locals(); +} + + +void InterpreterMacroAssembler::check_and_handle_popframe(Register java_thread) { + if (JvmtiExport::can_pop_frame()) { + Label L; + // Initiate popframe handling only if it is not already being processed. If the flag + // has the popframe_processing bit set, it means that this code is called *during* popframe + // handling - we don't want to reenter. + Register pop_cond = java_thread; // Not clear if any other register is available... + movl(pop_cond, Address(java_thread, JavaThread::popframe_condition_offset())); + testl(pop_cond, JavaThread::popframe_pending_bit); + jcc(Assembler::zero, L); + testl(pop_cond, JavaThread::popframe_processing_bit); + jcc(Assembler::notZero, L); + // Call Interpreter::remove_activation_preserving_args_entry() to get the + // address of the same-named entrypoint in the generated interpreter code. + call_VM_leaf(CAST_FROM_FN_PTR(address, Interpreter::remove_activation_preserving_args_entry)); + jmp(rax); + bind(L); + get_thread(java_thread); + } +} + + +void InterpreterMacroAssembler::load_earlyret_value(TosState state) { + get_thread(rcx); + movl(rcx, Address(rcx, JavaThread::jvmti_thread_state_offset())); + const Address tos_addr (rcx, JvmtiThreadState::earlyret_tos_offset()); + const Address oop_addr (rcx, JvmtiThreadState::earlyret_oop_offset()); + const Address val_addr (rcx, JvmtiThreadState::earlyret_value_offset()); + const Address val_addr1(rcx, JvmtiThreadState::earlyret_value_offset() + + in_ByteSize(wordSize)); + switch (state) { + case atos: movl(rax, oop_addr); + movl(oop_addr, NULL_WORD); + verify_oop(rax, state); break; + case ltos: movl(rdx, val_addr1); // fall through + case btos: // fall through + case ctos: // fall through + case stos: // fall through + case itos: movl(rax, val_addr); break; + case ftos: fld_s(val_addr); break; + case dtos: fld_d(val_addr); break; + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } + // Clean up tos value in the thread object + movl(tos_addr, (int) ilgl); + movl(val_addr, NULL_WORD); + movl(val_addr1, NULL_WORD); +} + + +void InterpreterMacroAssembler::check_and_handle_earlyret(Register java_thread) { + if (JvmtiExport::can_force_early_return()) { + Label L; + Register tmp = java_thread; + movl(tmp, Address(tmp, JavaThread::jvmti_thread_state_offset())); + testl(tmp, tmp); + jcc(Assembler::zero, L); // if (thread->jvmti_thread_state() == NULL) exit; + + // Initiate earlyret handling only if it is not already being processed. + // If the flag has the earlyret_processing bit set, it means that this code + // is called *during* earlyret handling - we don't want to reenter. + movl(tmp, Address(tmp, JvmtiThreadState::earlyret_state_offset())); + cmpl(tmp, JvmtiThreadState::earlyret_pending); + jcc(Assembler::notEqual, L); + + // Call Interpreter::remove_activation_early_entry() to get the address of the + // same-named entrypoint in the generated interpreter code. + get_thread(java_thread); + movl(tmp, Address(java_thread, JavaThread::jvmti_thread_state_offset())); + pushl(Address(tmp, JvmtiThreadState::earlyret_tos_offset())); + call_VM_leaf(CAST_FROM_FN_PTR(address, Interpreter::remove_activation_early_entry), 1); + jmp(rax); + bind(L); + get_thread(java_thread); + } +} + + +void InterpreterMacroAssembler::get_unsigned_2_byte_index_at_bcp(Register reg, int bcp_offset) { + assert(bcp_offset >= 0, "bcp is still pointing to start of bytecode"); + movl(reg, Address(rsi, bcp_offset)); + bswap(reg); + shrl(reg, 16); +} + + +void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, Register index, int bcp_offset) { + assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + assert(cache != index, "must use different registers"); + load_unsigned_word(index, Address(rsi, bcp_offset)); + movl(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize)); + assert(sizeof(ConstantPoolCacheEntry) == 4*wordSize, "adjust code below"); + shll(index, 2); // convert from field index to ConstantPoolCacheEntry index +} + + +void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset) { + assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + assert(cache != tmp, "must use different register"); + load_unsigned_word(tmp, Address(rsi, bcp_offset)); + assert(sizeof(ConstantPoolCacheEntry) == 4*wordSize, "adjust code below"); + // convert from field index to ConstantPoolCacheEntry index + // and from word offset to byte offset + shll(tmp, 2 + LogBytesPerWord); + movl(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize)); + // skip past the header + addl(cache, in_bytes(constantPoolCacheOopDesc::base_offset())); + addl(cache, tmp); // construct pointer to cache entry +} + + + // Generate a subtype check: branch to ok_is_subtype if sub_klass is + // a subtype of super_klass. EAX holds the super_klass. Blows ECX. + // Resets EDI to locals. Register sub_klass cannot be any of the above. +void InterpreterMacroAssembler::gen_subtype_check( Register Rsub_klass, Label &ok_is_subtype ) { + assert( Rsub_klass != rax, "rax, holds superklass" ); + assert( Rsub_klass != rcx, "rcx holds 2ndary super array length" ); + assert( Rsub_klass != rdi, "rdi holds 2ndary super array scan ptr" ); + Label not_subtype, loop; + + // Profile the not-null value's klass. + profile_typecheck(rcx, Rsub_klass, rdi); // blows rcx, rdi + + // Load the super-klass's check offset into ECX + movl( rcx, Address(rax, sizeof(oopDesc) + Klass::super_check_offset_offset_in_bytes() ) ); + // Load from the sub-klass's super-class display list, or a 1-word cache of + // the secondary superclass list, or a failing value with a sentinel offset + // if the super-klass is an interface or exceptionally deep in the Java + // hierarchy and we have to scan the secondary superclass list the hard way. + // See if we get an immediate positive hit + cmpl( rax, Address(Rsub_klass,rcx,Address::times_1) ); + jcc( Assembler::equal,ok_is_subtype ); + + // Check for immediate negative hit + cmpl( rcx, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() ); + jcc( Assembler::notEqual, not_subtype ); + // Check for self + cmpl( Rsub_klass, rax ); + jcc( Assembler::equal, ok_is_subtype ); + + // Now do a linear scan of the secondary super-klass chain. + movl( rdi, Address(Rsub_klass, sizeof(oopDesc) + Klass::secondary_supers_offset_in_bytes()) ); + // EDI holds the objArrayOop of secondary supers. + movl( rcx, Address(rdi, arrayOopDesc::length_offset_in_bytes()));// Load the array length + // Skip to start of data; also clear Z flag incase ECX is zero + addl( rdi, arrayOopDesc::base_offset_in_bytes(T_OBJECT) ); + // Scan ECX words at [EDI] for occurance of EAX + // Set NZ/Z based on last compare + repne_scan(); + restore_locals(); // Restore EDI; Must not blow flags + // Not equal? + jcc( Assembler::notEqual, not_subtype ); + // Must be equal but missed in cache. Update cache. + movl( Address(Rsub_klass, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes()), rax ); + jmp( ok_is_subtype ); + + bind(not_subtype); + profile_typecheck_failed(rcx); // blows rcx +} + +void InterpreterMacroAssembler::f2ieee() { + if (IEEEPrecision) { + fstp_s(Address(rsp, 0)); + fld_s(Address(rsp, 0)); + } +} + + +void InterpreterMacroAssembler::d2ieee() { + if (IEEEPrecision) { + fstp_d(Address(rsp, 0)); + fld_d(Address(rsp, 0)); + } +} +#endif // CC_INTERP + +// Java Expression Stack + +#ifdef ASSERT +void InterpreterMacroAssembler::verify_stack_tag(frame::Tag t) { + if (TaggedStackInterpreter) { + Label okay; + cmpl(Address(rsp, wordSize), (int)t); + jcc(Assembler::equal, okay); + // Also compare if the stack value is zero, then the tag might + // not have been set coming from deopt. + cmpl(Address(rsp, 0), 0); + jcc(Assembler::equal, okay); + stop("Java Expression stack tag value is bad"); + bind(okay); + } +} +#endif // ASSERT + +void InterpreterMacroAssembler::pop_ptr(Register r) { + debug_only(verify_stack_tag(frame::TagReference)); + popl(r); + if (TaggedStackInterpreter) addl(rsp, 1 * wordSize); +} + +void InterpreterMacroAssembler::pop_ptr(Register r, Register tag) { + popl(r); + // Tag may not be reference for jsr, can be returnAddress + if (TaggedStackInterpreter) popl(tag); +} + +void InterpreterMacroAssembler::pop_i(Register r) { + debug_only(verify_stack_tag(frame::TagValue)); + popl(r); + if (TaggedStackInterpreter) addl(rsp, 1 * wordSize); +} + +void InterpreterMacroAssembler::pop_l(Register lo, Register hi) { + debug_only(verify_stack_tag(frame::TagValue)); + popl(lo); + if (TaggedStackInterpreter) addl(rsp, 1 * wordSize); + debug_only(verify_stack_tag(frame::TagValue)); + popl(hi); + if (TaggedStackInterpreter) addl(rsp, 1 * wordSize); +} + +void InterpreterMacroAssembler::pop_f() { + debug_only(verify_stack_tag(frame::TagValue)); + fld_s(Address(rsp, 0)); + addl(rsp, 1 * wordSize); + if (TaggedStackInterpreter) addl(rsp, 1 * wordSize); +} + +void InterpreterMacroAssembler::pop_d() { + // Write double to stack contiguously and load into ST0 + pop_dtos_to_rsp(); + fld_d(Address(rsp, 0)); + addl(rsp, 2 * wordSize); +} + + +// Pop the top of the java expression stack to execution stack (which +// happens to be the same place). +void InterpreterMacroAssembler::pop_dtos_to_rsp() { + if (TaggedStackInterpreter) { + // Pop double value into scratch registers + debug_only(verify_stack_tag(frame::TagValue)); + popl(rax); + addl(rsp, 1* wordSize); + debug_only(verify_stack_tag(frame::TagValue)); + popl(rdx); + addl(rsp, 1* wordSize); + pushl(rdx); + pushl(rax); + } +} + +void InterpreterMacroAssembler::pop_ftos_to_rsp() { + if (TaggedStackInterpreter) { + debug_only(verify_stack_tag(frame::TagValue)); + popl(rax); + addl(rsp, 1 * wordSize); + pushl(rax); // ftos is at rsp + } +} + +void InterpreterMacroAssembler::pop(TosState state) { + switch (state) { + case atos: pop_ptr(rax); break; + case btos: // fall through + case ctos: // fall through + case stos: // fall through + case itos: pop_i(rax); break; + case ltos: pop_l(rax, rdx); break; + case ftos: pop_f(); break; + case dtos: pop_d(); break; + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } + verify_oop(rax, state); +} + +void InterpreterMacroAssembler::push_ptr(Register r) { + if (TaggedStackInterpreter) pushl(frame::TagReference); + pushl(r); +} + +void InterpreterMacroAssembler::push_ptr(Register r, Register tag) { + if (TaggedStackInterpreter) pushl(tag); // tag first + pushl(r); +} + +void InterpreterMacroAssembler::push_i(Register r) { + if (TaggedStackInterpreter) pushl(frame::TagValue); + pushl(r); +} + +void InterpreterMacroAssembler::push_l(Register lo, Register hi) { + if (TaggedStackInterpreter) pushl(frame::TagValue); + pushl(hi); + if (TaggedStackInterpreter) pushl(frame::TagValue); + pushl(lo); +} + +void InterpreterMacroAssembler::push_f() { + if (TaggedStackInterpreter) pushl(frame::TagValue); + // Do not schedule for no AGI! Never write beyond rsp! + subl(rsp, 1 * wordSize); + fstp_s(Address(rsp, 0)); +} + +void InterpreterMacroAssembler::push_d(Register r) { + if (TaggedStackInterpreter) { + // Double values are stored as: + // tag + // high + // tag + // low + pushl(frame::TagValue); + subl(rsp, 3 * wordSize); + fstp_d(Address(rsp, 0)); + // move high word up to slot n-1 + movl(r, Address(rsp, 1*wordSize)); + movl(Address(rsp, 2*wordSize), r); + // move tag + movl(Address(rsp, 1*wordSize), frame::TagValue); + } else { + // Do not schedule for no AGI! Never write beyond rsp! + subl(rsp, 2 * wordSize); + fstp_d(Address(rsp, 0)); + } +} + + +void InterpreterMacroAssembler::push(TosState state) { + verify_oop(rax, state); + switch (state) { + case atos: push_ptr(rax); break; + case btos: // fall through + case ctos: // fall through + case stos: // fall through + case itos: push_i(rax); break; + case ltos: push_l(rax, rdx); break; + case ftos: push_f(); break; + case dtos: push_d(rax); break; + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } +} + +#ifndef CC_INTERP + +// Tagged stack helpers for swap and dup +void InterpreterMacroAssembler::load_ptr_and_tag(int n, Register val, + Register tag) { + movl(val, Address(rsp, Interpreter::expr_offset_in_bytes(n))); + if (TaggedStackInterpreter) { + movl(tag, Address(rsp, Interpreter::expr_tag_offset_in_bytes(n))); + } +} + +void InterpreterMacroAssembler::store_ptr_and_tag(int n, Register val, + Register tag) { + movl(Address(rsp, Interpreter::expr_offset_in_bytes(n)), val); + if (TaggedStackInterpreter) { + movl(Address(rsp, Interpreter::expr_tag_offset_in_bytes(n)), tag); + } +} + + +// Tagged local support +void InterpreterMacroAssembler::tag_local(frame::Tag tag, int n) { + if (TaggedStackInterpreter) { + if (tag == frame::TagCategory2) { + movl(Address(rdi, Interpreter::local_tag_offset_in_bytes(n+1)), (int)frame::TagValue); + movl(Address(rdi, Interpreter::local_tag_offset_in_bytes(n)), (int)frame::TagValue); + } else { + movl(Address(rdi, Interpreter::local_tag_offset_in_bytes(n)), (int)tag); + } + } +} + +void InterpreterMacroAssembler::tag_local(frame::Tag tag, Register idx) { + if (TaggedStackInterpreter) { + if (tag == frame::TagCategory2) { + movl(Address(rdi, idx, Interpreter::stackElementScale(), + Interpreter::local_tag_offset_in_bytes(1)), (int)frame::TagValue); + movl(Address(rdi, idx, Interpreter::stackElementScale(), + Interpreter::local_tag_offset_in_bytes(0)), (int)frame::TagValue); + } else { + movl(Address(rdi, idx, Interpreter::stackElementScale(), + Interpreter::local_tag_offset_in_bytes(0)), (int)tag); + } + } +} + +void InterpreterMacroAssembler::tag_local(Register tag, Register idx) { + if (TaggedStackInterpreter) { + // can only be TagValue or TagReference + movl(Address(rdi, idx, Interpreter::stackElementScale(), + Interpreter::local_tag_offset_in_bytes(0)), tag); + } +} + + +void InterpreterMacroAssembler::tag_local(Register tag, int n) { + if (TaggedStackInterpreter) { + // can only be TagValue or TagReference + movl(Address(rdi, Interpreter::local_tag_offset_in_bytes(n)), tag); + } +} + +#ifdef ASSERT +void InterpreterMacroAssembler::verify_local_tag(frame::Tag tag, int n) { + if (TaggedStackInterpreter) { + frame::Tag t = tag; + if (tag == frame::TagCategory2) { + Label nbl; + t = frame::TagValue; // change to what is stored in locals + cmpl(Address(rdi, Interpreter::local_tag_offset_in_bytes(n+1)), (int)t); + jcc(Assembler::equal, nbl); + stop("Local tag is bad for long/double"); + bind(nbl); + } + Label notBad; + cmpl(Address(rdi, Interpreter::local_tag_offset_in_bytes(n)), (int)t); + jcc(Assembler::equal, notBad); + // Also compare if the local value is zero, then the tag might + // not have been set coming from deopt. + cmpl(Address(rdi, Interpreter::local_offset_in_bytes(n)), 0); + jcc(Assembler::equal, notBad); + stop("Local tag is bad"); + bind(notBad); + } +} + +void InterpreterMacroAssembler::verify_local_tag(frame::Tag tag, Register idx) { + if (TaggedStackInterpreter) { + frame::Tag t = tag; + if (tag == frame::TagCategory2) { + Label nbl; + t = frame::TagValue; // change to what is stored in locals + cmpl(Address(rdi, idx, Interpreter::stackElementScale(), + Interpreter::local_tag_offset_in_bytes(1)), (int)t); + jcc(Assembler::equal, nbl); + stop("Local tag is bad for long/double"); + bind(nbl); + } + Label notBad; + cmpl(Address(rdi, idx, Interpreter::stackElementScale(), + Interpreter::local_tag_offset_in_bytes(0)), (int)t); + jcc(Assembler::equal, notBad); + // Also compare if the local value is zero, then the tag might + // not have been set coming from deopt. + cmpl(Address(rdi, idx, Interpreter::stackElementScale(), + Interpreter::local_offset_in_bytes(0)), 0); + jcc(Assembler::equal, notBad); + stop("Local tag is bad"); + bind(notBad); + + } +} +#endif // ASSERT + +void InterpreterMacroAssembler::super_call_VM_leaf(address entry_point) { + MacroAssembler::call_VM_leaf_base(entry_point, 0); +} + + +void InterpreterMacroAssembler::super_call_VM_leaf(address entry_point, Register arg_1) { + pushl(arg_1); + MacroAssembler::call_VM_leaf_base(entry_point, 1); +} + + +void InterpreterMacroAssembler::super_call_VM_leaf(address entry_point, Register arg_1, Register arg_2) { + pushl(arg_2); + pushl(arg_1); + MacroAssembler::call_VM_leaf_base(entry_point, 2); +} + + +void InterpreterMacroAssembler::super_call_VM_leaf(address entry_point, Register arg_1, Register arg_2, Register arg_3) { + pushl(arg_3); + pushl(arg_2); + pushl(arg_1); + MacroAssembler::call_VM_leaf_base(entry_point, 3); +} + + +// Jump to from_interpreted entry of a call unless single stepping is possible +// in this thread in which case we must call the i2i entry +void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) { + // set sender sp + leal(rsi, Address(rsp, wordSize)); + // record last_sp + movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), rsi); + + if (JvmtiExport::can_post_interpreter_events()) { + Label run_compiled_code; + // JVMTI events, such as single-stepping, are implemented partly by avoiding running + // compiled code in threads for which the event is enabled. Check here for + // interp_only_mode if these events CAN be enabled. + get_thread(temp); + // interp_only is an int, on little endian it is sufficient to test the byte only + // Is a cmpl faster (ce + cmpb(Address(temp, JavaThread::interp_only_mode_offset()), 0); + jcc(Assembler::zero, run_compiled_code); + jmp(Address(method, methodOopDesc::interpreter_entry_offset())); + bind(run_compiled_code); + } + + jmp(Address(method, methodOopDesc::from_interpreted_offset())); + +} + + +// The following two routines provide a hook so that an implementation +// can schedule the dispatch in two parts. Intel does not do this. +void InterpreterMacroAssembler::dispatch_prolog(TosState state, int step) { + // Nothing Intel-specific to be done here. +} + +void InterpreterMacroAssembler::dispatch_epilog(TosState state, int step) { + dispatch_next(state, step); +} + +void InterpreterMacroAssembler::dispatch_base(TosState state, address* table, + bool verifyoop) { + verify_FPU(1, state); + if (VerifyActivationFrameSize) { + Label L; + movl(rcx, rbp); + subl(rcx, rsp); + int min_frame_size = (frame::link_offset - frame::interpreter_frame_initial_sp_offset) * wordSize; + cmpl(rcx, min_frame_size); + jcc(Assembler::greaterEqual, L); + stop("broken stack frame"); + bind(L); + } + if (verifyoop) verify_oop(rax, state); + Address index(noreg, rbx, Address::times_4); + ExternalAddress tbl((address)table); + ArrayAddress dispatch(tbl, index); + jump(dispatch); +} + + +void InterpreterMacroAssembler::dispatch_only(TosState state) { + dispatch_base(state, Interpreter::dispatch_table(state)); +} + + +void InterpreterMacroAssembler::dispatch_only_normal(TosState state) { + dispatch_base(state, Interpreter::normal_table(state)); +} + +void InterpreterMacroAssembler::dispatch_only_noverify(TosState state) { + dispatch_base(state, Interpreter::normal_table(state), false); +} + + +void InterpreterMacroAssembler::dispatch_next(TosState state, int step) { + // load next bytecode (load before advancing rsi to prevent AGI) + load_unsigned_byte(rbx, Address(rsi, step)); + // advance rsi + increment(rsi, step); + dispatch_base(state, Interpreter::dispatch_table(state)); +} + + +void InterpreterMacroAssembler::dispatch_via(TosState state, address* table) { + // load current bytecode + load_unsigned_byte(rbx, Address(rsi, 0)); + dispatch_base(state, table); +} + +// remove activation +// +// Unlock the receiver if this is a synchronized method. +// Unlock any Java monitors from syncronized blocks. +// Remove the activation from the stack. +// +// If there are locked Java monitors +// If throw_monitor_exception +// throws IllegalMonitorStateException +// Else if install_monitor_exception +// installs IllegalMonitorStateException +// Else +// no error processing +void InterpreterMacroAssembler::remove_activation(TosState state, Register ret_addr, + bool throw_monitor_exception, + bool install_monitor_exception, + bool notify_jvmdi) { + // Note: Registers rax, rdx and FPU ST(0) may be in use for the result + // check if synchronized method + Label unlocked, unlock, no_unlock; + + get_thread(rcx); + const Address do_not_unlock_if_synchronized(rcx, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + + movbool(rbx, do_not_unlock_if_synchronized); + movl(rdi,rbx); + movbool(do_not_unlock_if_synchronized, false); // reset the flag + + movl(rbx, Address(rbp, frame::interpreter_frame_method_offset * wordSize)); // get method access flags + movl(rcx, Address(rbx, methodOopDesc::access_flags_offset())); + + testl(rcx, JVM_ACC_SYNCHRONIZED); + jcc(Assembler::zero, unlocked); + + // Don't unlock anything if the _do_not_unlock_if_synchronized flag + // is set. + movl(rcx,rdi); + testbool(rcx); + jcc(Assembler::notZero, no_unlock); + + // unlock monitor + push(state); // save result + + // BasicObjectLock will be first in list, since this is a synchronized method. However, need + // to check that the object has not been unlocked by an explicit monitorexit bytecode. + const Address monitor(rbp, frame::interpreter_frame_initial_sp_offset * wordSize - (int)sizeof(BasicObjectLock)); + leal (rdx, monitor); // address of first monitor + + movl (rax, Address(rdx, BasicObjectLock::obj_offset_in_bytes())); + testl (rax, rax); + jcc (Assembler::notZero, unlock); + + pop(state); + if (throw_monitor_exception) { + empty_FPU_stack(); // remove possible return value from FPU-stack, otherwise stack could overflow + + // Entry already unlocked, need to throw exception + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + should_not_reach_here(); + } else { + // Monitor already unlocked during a stack unroll. + // If requested, install an illegal_monitor_state_exception. + // Continue with stack unrolling. + if (install_monitor_exception) { + empty_FPU_stack(); // remove possible return value from FPU-stack, otherwise stack could overflow + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::new_illegal_monitor_state_exception)); + } + jmp(unlocked); + } + + bind(unlock); + unlock_object(rdx); + pop(state); + + // Check that for block-structured locking (i.e., that all locked objects has been unlocked) + bind(unlocked); + + // rax, rdx: Might contain return value + + // Check that all monitors are unlocked + { + Label loop, exception, entry, restart; + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + const Address monitor_block_top(rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize); + const Address monitor_block_bot(rbp, frame::interpreter_frame_initial_sp_offset * wordSize); + + bind(restart); + movl(rcx, monitor_block_top); // points to current entry, starting with top-most entry + leal(rbx, monitor_block_bot); // points to word before bottom of monitor block + jmp(entry); + + // Entry already locked, need to throw exception + bind(exception); + + if (throw_monitor_exception) { + empty_FPU_stack(); // remove possible return value from FPU-stack, otherwise stack could overflow + + // Throw exception + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + should_not_reach_here(); + } else { + // Stack unrolling. Unlock object and install illegal_monitor_exception + // Unlock does not block, so don't have to worry about the frame + + push(state); + movl(rdx, rcx); + unlock_object(rdx); + pop(state); + + if (install_monitor_exception) { + empty_FPU_stack(); // remove possible return value from FPU-stack, otherwise stack could overflow + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::new_illegal_monitor_state_exception)); + } + + jmp(restart); + } + + bind(loop); + cmpl(Address(rcx, BasicObjectLock::obj_offset_in_bytes()), NULL_WORD); // check if current entry is used + jcc(Assembler::notEqual, exception); + + addl(rcx, entry_size); // otherwise advance to next entry + bind(entry); + cmpl(rcx, rbx); // check if bottom reached + jcc(Assembler::notEqual, loop); // if not at bottom then check this entry + } + + bind(no_unlock); + + // jvmti support + if (notify_jvmdi) { + notify_method_exit(state, NotifyJVMTI); // preserve TOSCA + } else { + notify_method_exit(state, SkipNotifyJVMTI); // preserve TOSCA + } + + // remove activation + movl(rbx, Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize)); // get sender sp + leave(); // remove frame anchor + popl(ret_addr); // get return address + movl(rsp, rbx); // set sp to sender sp + if (UseSSE) { + // float and double are returned in xmm register in SSE-mode + if (state == ftos && UseSSE >= 1) { + subl(rsp, wordSize); + fstp_s(Address(rsp, 0)); + movflt(xmm0, Address(rsp, 0)); + addl(rsp, wordSize); + } else if (state == dtos && UseSSE >= 2) { + subl(rsp, 2*wordSize); + fstp_d(Address(rsp, 0)); + movdbl(xmm0, Address(rsp, 0)); + addl(rsp, 2*wordSize); + } + } +} + +#endif /* !CC_INTERP */ + + +// Lock object +// +// Argument: rdx : Points to BasicObjectLock to be used for locking. Must +// be initialized with object to lock +void InterpreterMacroAssembler::lock_object(Register lock_reg) { + assert(lock_reg == rdx, "The argument is only for looks. It must be rdx"); + + if (UseHeavyMonitors) { + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); + } else { + + Label done; + + const Register swap_reg = rax; // Must use rax, for cmpxchg instruction + const Register obj_reg = rcx; // Will contain the oop + + const int obj_offset = BasicObjectLock::obj_offset_in_bytes(); + const int lock_offset = BasicObjectLock::lock_offset_in_bytes (); + const int mark_offset = lock_offset + BasicLock::displaced_header_offset_in_bytes(); + + Label slow_case; + + // Load object pointer into obj_reg %rcx + movl(obj_reg, Address(lock_reg, obj_offset)); + + if (UseBiasedLocking) { + // Note: we use noreg for the temporary register since it's hard + // to come up with a free register on all incoming code paths + biased_locking_enter(lock_reg, obj_reg, swap_reg, noreg, false, done, &slow_case); + } + + // Load immediate 1 into swap_reg %rax, + movl(swap_reg, 1); + + // Load (object->mark() | 1) into swap_reg %rax, + orl(swap_reg, Address(obj_reg, 0)); + + // Save (object->mark() | 1) into BasicLock's displaced header + movl(Address(lock_reg, mark_offset), swap_reg); + + assert(lock_offset == 0, "displached header must be first word in BasicObjectLock"); + if (os::is_MP()) { + lock(); + } + cmpxchg(lock_reg, Address(obj_reg, 0)); + if (PrintBiasedLockingStatistics) { + cond_inc32(Assembler::zero, + ExternalAddress((address) BiasedLocking::fast_path_entry_count_addr())); + } + jcc(Assembler::zero, done); + + // Test if the oopMark is an obvious stack pointer, i.e., + // 1) (mark & 3) == 0, and + // 2) rsp <= mark < mark + os::pagesize() + // + // These 3 tests can be done by evaluating the following + // expression: ((mark - rsp) & (3 - os::vm_page_size())), + // assuming both stack pointer and pagesize have their + // least significant 2 bits clear. + // NOTE: the oopMark is in swap_reg %rax, as the result of cmpxchg + subl(swap_reg, rsp); + andl(swap_reg, 3 - os::vm_page_size()); + + // Save the test result, for recursive case, the result is zero + movl(Address(lock_reg, mark_offset), swap_reg); + + if (PrintBiasedLockingStatistics) { + cond_inc32(Assembler::zero, + ExternalAddress((address) BiasedLocking::fast_path_entry_count_addr())); + } + jcc(Assembler::zero, done); + + bind(slow_case); + + // Call the runtime routine for slow case + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); + + bind(done); + } +} + + +// Unlocks an object. Used in monitorexit bytecode and remove_activation. +// +// Argument: rdx : Points to BasicObjectLock structure for lock +// Throw an IllegalMonitorException if object is not locked by current thread +// +// Uses: rax, rbx, rcx, rdx +void InterpreterMacroAssembler::unlock_object(Register lock_reg) { + assert(lock_reg == rdx, "The argument is only for looks. It must be rdx"); + + if (UseHeavyMonitors) { + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); + } else { + Label done; + + const Register swap_reg = rax; // Must use rax, for cmpxchg instruction + const Register header_reg = rbx; // Will contain the old oopMark + const Register obj_reg = rcx; // Will contain the oop + + save_bcp(); // Save in case of exception + + // Convert from BasicObjectLock structure to object and BasicLock structure + // Store the BasicLock address into %rax, + leal(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset_in_bytes())); + + // Load oop into obj_reg(%rcx) + movl(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset_in_bytes ())); + + // Free entry + movl(Address(lock_reg, BasicObjectLock::obj_offset_in_bytes()), NULL_WORD); + + if (UseBiasedLocking) { + biased_locking_exit(obj_reg, header_reg, done); + } + + // Load the old header from BasicLock structure + movl(header_reg, Address(swap_reg, BasicLock::displaced_header_offset_in_bytes())); + + // Test for recursion + testl(header_reg, header_reg); + + // zero for recursive case + jcc(Assembler::zero, done); + + // Atomic swap back the old header + if (os::is_MP()) lock(); + cmpxchg(header_reg, Address(obj_reg, 0)); + + // zero for recursive case + jcc(Assembler::zero, done); + + // Call the runtime routine for slow case. + movl(Address(lock_reg, BasicObjectLock::obj_offset_in_bytes()), obj_reg); // restore obj + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); + + bind(done); + + restore_bcp(); + } +} + + +#ifndef CC_INTERP + +// Test ImethodDataPtr. If it is null, continue at the specified label +void InterpreterMacroAssembler::test_method_data_pointer(Register mdp, Label& zero_continue) { + assert(ProfileInterpreter, "must be profiling interpreter"); + movl(mdp, Address(rbp, frame::interpreter_frame_mdx_offset * wordSize)); + testl(mdp, mdp); + jcc(Assembler::zero, zero_continue); +} + + +// Set the method data pointer for the current bcp. +void InterpreterMacroAssembler::set_method_data_pointer_for_bcp() { + assert(ProfileInterpreter, "must be profiling interpreter"); + Label zero_continue; + pushl(rax); + pushl(rbx); + + get_method(rbx); + // Test MDO to avoid the call if it is NULL. + movl(rax, Address(rbx, in_bytes(methodOopDesc::method_data_offset()))); + testl(rax, rax); + jcc(Assembler::zero, zero_continue); + + // rbx,: method + // rsi: bcp + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::bcp_to_di), rbx, rsi); + // rax,: mdi + + movl(rbx, Address(rbx, in_bytes(methodOopDesc::method_data_offset()))); + testl(rbx, rbx); + jcc(Assembler::zero, zero_continue); + addl(rbx, in_bytes(methodDataOopDesc::data_offset())); + addl(rbx, rax); + movl(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), rbx); + + bind(zero_continue); + popl(rbx); + popl(rax); +} + +void InterpreterMacroAssembler::verify_method_data_pointer() { + assert(ProfileInterpreter, "must be profiling interpreter"); +#ifdef ASSERT + Label verify_continue; + pushl(rax); + pushl(rbx); + pushl(rcx); + pushl(rdx); + test_method_data_pointer(rcx, verify_continue); // If mdp is zero, continue + get_method(rbx); + + // If the mdp is valid, it will point to a DataLayout header which is + // consistent with the bcp. The converse is highly probable also. + load_unsigned_word(rdx, Address(rcx, in_bytes(DataLayout::bci_offset()))); + addl(rdx, Address(rbx, methodOopDesc::const_offset())); + leal(rdx, Address(rdx, constMethodOopDesc::codes_offset())); + cmpl(rdx, rsi); + jcc(Assembler::equal, verify_continue); + // rbx,: method + // rsi: bcp + // rcx: mdp + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp), rbx, rsi, rcx); + bind(verify_continue); + popl(rdx); + popl(rcx); + popl(rbx); + popl(rax); +#endif // ASSERT +} + + +void InterpreterMacroAssembler::set_mdp_data_at(Register mdp_in, int constant, Register value) { + assert(ProfileInterpreter, "must be profiling interpreter"); + Address data(mdp_in, constant); + movl(data, value); +} + + +void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in, + int constant, + bool decrement) { + // Counter address + Address data(mdp_in, constant); + + increment_mdp_data_at(data, decrement); +} + + +void InterpreterMacroAssembler::increment_mdp_data_at(Address data, + bool decrement) { + + assert( DataLayout::counter_increment==1, "flow-free idiom only works with 1" ); + assert(ProfileInterpreter, "must be profiling interpreter"); + + if (decrement) { + // Decrement the register. Set condition codes. + addl(data, -DataLayout::counter_increment); + // If the decrement causes the counter to overflow, stay negative + Label L; + jcc(Assembler::negative, L); + addl(data, DataLayout::counter_increment); + bind(L); + } else { + assert(DataLayout::counter_increment == 1, + "flow-free idiom only works with 1"); + // Increment the register. Set carry flag. + addl(data, DataLayout::counter_increment); + // If the increment causes the counter to overflow, pull back by 1. + sbbl(data, 0); + } +} + + +void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in, + Register reg, + int constant, + bool decrement) { + Address data(mdp_in, reg, Address::times_1, constant); + + increment_mdp_data_at(data, decrement); +} + + +void InterpreterMacroAssembler::set_mdp_flag_at(Register mdp_in, int flag_byte_constant) { + assert(ProfileInterpreter, "must be profiling interpreter"); + int header_offset = in_bytes(DataLayout::header_offset()); + int header_bits = DataLayout::flag_mask_to_header_mask(flag_byte_constant); + // Set the flag + orl(Address(mdp_in, header_offset), header_bits); +} + + + +void InterpreterMacroAssembler::test_mdp_data_at(Register mdp_in, + int offset, + Register value, + Register test_value_out, + Label& not_equal_continue) { + assert(ProfileInterpreter, "must be profiling interpreter"); + if (test_value_out == noreg) { + cmpl(value, Address(mdp_in, offset)); + } else { + // Put the test value into a register, so caller can use it: + movl(test_value_out, Address(mdp_in, offset)); + cmpl(test_value_out, value); + } + jcc(Assembler::notEqual, not_equal_continue); +} + + +void InterpreterMacroAssembler::update_mdp_by_offset(Register mdp_in, int offset_of_disp) { + assert(ProfileInterpreter, "must be profiling interpreter"); + Address disp_address(mdp_in, offset_of_disp); + addl(mdp_in,disp_address); + movl(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), mdp_in); +} + + +void InterpreterMacroAssembler::update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp) { + assert(ProfileInterpreter, "must be profiling interpreter"); + Address disp_address(mdp_in, reg, Address::times_1, offset_of_disp); + addl(mdp_in, disp_address); + movl(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), mdp_in); +} + + +void InterpreterMacroAssembler::update_mdp_by_constant(Register mdp_in, int constant) { + assert(ProfileInterpreter, "must be profiling interpreter"); + addl(mdp_in, constant); + movl(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), mdp_in); +} + + +void InterpreterMacroAssembler::update_mdp_for_ret(Register return_bci) { + assert(ProfileInterpreter, "must be profiling interpreter"); + pushl(return_bci); // save/restore across call_VM + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::update_mdp_for_ret), return_bci); + popl(return_bci); +} + + +void InterpreterMacroAssembler::profile_taken_branch(Register mdp, Register bumped_count) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + // Otherwise, assign to mdp + test_method_data_pointer(mdp, profile_continue); + + // We are taking a branch. Increment the taken count. + // We inline increment_mdp_data_at to return bumped_count in a register + //increment_mdp_data_at(mdp, in_bytes(JumpData::taken_offset())); + Address data(mdp, in_bytes(JumpData::taken_offset())); + movl(bumped_count,data); + assert( DataLayout::counter_increment==1, "flow-free idiom only works with 1" ); + addl(bumped_count, DataLayout::counter_increment); + sbbl(bumped_count, 0); + movl(data,bumped_count); // Store back out + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_offset(mdp, in_bytes(JumpData::displacement_offset())); + bind (profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // We are taking a branch. Increment the not taken count. + increment_mdp_data_at(mdp, in_bytes(BranchData::not_taken_offset())); + + // The method data pointer needs to be updated to correspond to the next bytecode + update_mdp_by_constant(mdp, in_bytes(BranchData::branch_data_size())); + bind (profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_call(Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(mdp, in_bytes(CounterData::counter_data_size())); + bind (profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_final_call(Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size())); + bind (profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register mdp, Register reg2) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + + // Record the receiver type. + record_klass_in_profile(receiver, mdp, reg2); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(mdp, + in_bytes(VirtualCallData:: + virtual_call_data_size())); + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::record_klass_in_profile_helper( + Register receiver, Register mdp, + Register reg2, + int start_row, Label& done) { + int last_row = VirtualCallData::row_limit() - 1; + assert(start_row <= last_row, "must be work left to do"); + // Test this row for both the receiver and for null. + // Take any of three different outcomes: + // 1. found receiver => increment count and goto done + // 2. found null => keep looking for case 1, maybe allocate this cell + // 3. found something else => keep looking for cases 1 and 2 + // Case 3 is handled by a recursive call. + for (int row = start_row; row <= last_row; row++) { + Label next_test; + bool test_for_null_also = (row == start_row); + + // See if the receiver is receiver[n]. + int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row)); + test_mdp_data_at(mdp, recvr_offset, receiver, + (test_for_null_also ? reg2 : noreg), + next_test); + // (Reg2 now contains the receiver from the CallData.) + + // The receiver is receiver[n]. Increment count[n]. + int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row)); + increment_mdp_data_at(mdp, count_offset); + jmp(done); + bind(next_test); + + if (row == start_row) { + // Failed the equality check on receiver[n]... Test for null. + testl(reg2, reg2); + if (start_row == last_row) { + // The only thing left to do is handle the null case. + jcc(Assembler::notZero, done); + break; + } + // Since null is rare, make it be the branch-taken case. + Label found_null; + jcc(Assembler::zero, found_null); + + // Put all the "Case 3" tests here. + record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done); + + // Found a null. Keep searching for a matching receiver, + // but remember that this is an empty (unused) slot. + bind(found_null); + } + } + + // In the fall-through case, we found no matching receiver, but we + // observed the receiver[start_row] is NULL. + + // Fill in the receiver field and increment the count. + int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row)); + set_mdp_data_at(mdp, recvr_offset, receiver); + int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row)); + movl(reg2, DataLayout::counter_increment); + set_mdp_data_at(mdp, count_offset, reg2); + jmp(done); +} + +void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, + Register mdp, + Register reg2) { + assert(ProfileInterpreter, "must be profiling"); + Label done; + + record_klass_in_profile_helper(receiver, mdp, reg2, 0, done); + + bind (done); +} + +void InterpreterMacroAssembler::profile_ret(Register return_bci, Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + uint row; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // Update the total ret count. + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + + for (row = 0; row < RetData::row_limit(); row++) { + Label next_test; + + // See if return_bci is equal to bci[n]: + test_mdp_data_at(mdp, in_bytes(RetData::bci_offset(row)), return_bci, + noreg, next_test); + + // return_bci is equal to bci[n]. Increment the count. + increment_mdp_data_at(mdp, in_bytes(RetData::bci_count_offset(row))); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_offset(mdp, in_bytes(RetData::bci_displacement_offset(row))); + jmp(profile_continue); + bind(next_test); + } + + update_mdp_for_ret(return_bci); + + bind (profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_null_seen(Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // The method data pointer needs to be updated. + int mdp_delta = in_bytes(BitData::bit_data_size()); + if (TypeProfileCasts) { + mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); + } + update_mdp_by_constant(mdp, mdp_delta); + + bind (profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_typecheck_failed(Register mdp) { + if (ProfileInterpreter && TypeProfileCasts) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + int count_offset = in_bytes(CounterData::count_offset()); + // Back up the address, since we have already bumped the mdp. + count_offset -= in_bytes(VirtualCallData::virtual_call_data_size()); + + // *Decrement* the counter. We expect to see zero or small negatives. + increment_mdp_data_at(mdp, count_offset, true); + + bind (profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, Register reg2) +{ + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // The method data pointer needs to be updated. + int mdp_delta = in_bytes(BitData::bit_data_size()); + if (TypeProfileCasts) { + mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); + + // Record the object type. + record_klass_in_profile(klass, mdp, reg2); + assert(reg2 == rdi, "we know how to fix this blown reg"); + restore_locals(); // Restore EDI + } + update_mdp_by_constant(mdp, mdp_delta); + + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_switch_default(Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // Update the default case count + increment_mdp_data_at(mdp, in_bytes(MultiBranchData::default_count_offset())); + + // The method data pointer needs to be updated. + update_mdp_by_offset(mdp, in_bytes(MultiBranchData::default_displacement_offset())); + + bind (profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_switch_case(Register index, Register mdp, Register reg2) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // Build the base (index * per_case_size_in_bytes()) + case_array_offset_in_bytes() + movl(reg2, in_bytes(MultiBranchData::per_case_size())); + imull(index, reg2); + addl(index, in_bytes(MultiBranchData::case_array_offset())); + + // Update the case count + increment_mdp_data_at(mdp, index, in_bytes(MultiBranchData::relative_count_offset())); + + // The method data pointer needs to be updated. + update_mdp_by_offset(mdp, index, in_bytes(MultiBranchData::relative_displacement_offset())); + + bind (profile_continue); + } +} + +#endif // !CC_INTERP + + + +void InterpreterMacroAssembler::verify_oop(Register reg, TosState state) { + if (state == atos) MacroAssembler::verify_oop(reg); +} + + +#ifndef CC_INTERP +void InterpreterMacroAssembler::verify_FPU(int stack_depth, TosState state) { + if (state == ftos || state == dtos) MacroAssembler::verify_FPU(stack_depth); +} + +#endif /* CC_INTERP */ + + +void InterpreterMacroAssembler::notify_method_entry() { + // Whenever JVMTI is interp_only_mode, method entry/exit events are sent to + // track stack depth. If it is possible to enter interp_only_mode we add + // the code to check if the event should be sent. + if (JvmtiExport::can_post_interpreter_events()) { + Label L; + get_thread(rcx); + movl(rcx, Address(rcx, JavaThread::interp_only_mode_offset())); + testl(rcx,rcx); + jcc(Assembler::zero, L); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_entry)); + bind(L); + } + + { + SkipIfEqual skip_if(this, &DTraceMethodProbes, 0); + get_thread(rcx); + get_method(rbx); + call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), rcx, rbx); + } +} + + +void InterpreterMacroAssembler::notify_method_exit( + TosState state, NotifyMethodExitMode mode) { + // Whenever JVMTI is interp_only_mode, method entry/exit events are sent to + // track stack depth. If it is possible to enter interp_only_mode we add + // the code to check if the event should be sent. + if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) { + Label L; + // Note: frame::interpreter_frame_result has a dependency on how the + // method result is saved across the call to post_method_exit. If this + // is changed then the interpreter_frame_result implementation will + // need to be updated too. + + // For c++ interpreter the result is always stored at a known location in the frame + // template interpreter will leave it on the top of the stack. + NOT_CC_INTERP(push(state);) + get_thread(rcx); + movl(rcx, Address(rcx, JavaThread::interp_only_mode_offset())); + testl(rcx,rcx); + jcc(Assembler::zero, L); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit)); + bind(L); + NOT_CC_INTERP(pop(state);) + } + + { + SkipIfEqual skip_if(this, &DTraceMethodProbes, 0); + push(state); + get_thread(rbx); + get_method(rcx); + call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), + rbx, rcx); + pop(state); + } +} diff --git a/hotspot/src/cpu/x86/vm/interp_masm_x86_32.hpp b/hotspot/src/cpu/x86/vm/interp_masm_x86_32.hpp new file mode 100644 index 00000000000..10cf8140a22 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_32.hpp @@ -0,0 +1,235 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file specializes the assember with interpreter-specific macros + + +class InterpreterMacroAssembler: public MacroAssembler { +#ifndef CC_INTERP + protected: + // Interpreter specific version of call_VM_base + virtual void call_VM_leaf_base( + address entry_point, + int number_of_arguments + ); + + virtual void call_VM_base( + Register oop_result, + Register java_thread, + Register last_java_sp, + address entry_point, + int number_of_arguments, + bool check_exceptions + ); + + virtual void check_and_handle_popframe(Register java_thread); + virtual void check_and_handle_earlyret(Register java_thread); + + // base routine for all dispatches + void dispatch_base(TosState state, address* table, bool verifyoop = true); +#endif /* CC_INTERP */ + + public: + InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code) {} + + void load_earlyret_value(TosState state); + + // Interpreter-specific registers +#ifdef CC_INTERP + void save_bcp() { /* not needed in c++ interpreter and harmless */ } + void restore_bcp() { /* not needed in c++ interpreter and harmless */ } + + // Helpers for runtime call arguments/results + void get_method(Register reg); + +#else + + void save_bcp() { movl(Address(rbp, frame::interpreter_frame_bcx_offset * wordSize), rsi); } + void restore_bcp() { movl(rsi, Address(rbp, frame::interpreter_frame_bcx_offset * wordSize)); } + void restore_locals() { movl(rdi, Address(rbp, frame::interpreter_frame_locals_offset * wordSize)); } + + // Helpers for runtime call arguments/results + void get_method(Register reg) { movl(reg, Address(rbp, frame::interpreter_frame_method_offset * wordSize)); } + void get_constant_pool(Register reg) { get_method(reg); movl(reg, Address(reg, methodOopDesc::constants_offset())); } + void get_constant_pool_cache(Register reg) { get_constant_pool(reg); movl(reg, Address(reg, constantPoolOopDesc::cache_offset_in_bytes())); } + void get_cpool_and_tags(Register cpool, Register tags) { get_constant_pool(cpool); movl(tags, Address(cpool, constantPoolOopDesc::tags_offset_in_bytes())); + } + void get_unsigned_2_byte_index_at_bcp(Register reg, int bcp_offset); + void get_cache_and_index_at_bcp(Register cache, Register index, int bcp_offset); + void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset); + + // Expression stack + void f2ieee(); // truncate ftos to 32bits + void d2ieee(); // truncate dtos to 64bits +#endif // CC_INTERP + + + void pop_ptr(Register r = rax); + void pop_ptr(Register r, Register tag); + void pop_i(Register r = rax); + void pop_l(Register lo = rax, Register hi = rdx); + void pop_f(); + void pop_d(); + void pop_ftos_to_rsp(); + void pop_dtos_to_rsp(); + + void push_ptr(Register r = rax); + void push_ptr(Register r, Register tag); + void push_i(Register r = rax); + void push_l(Register lo = rax, Register hi = rdx); + void push_d(Register r = rax); + void push_f(); + + void pop(TosState state); // transition vtos -> state + void push(TosState state); // transition state -> vtos + + DEBUG_ONLY(void verify_stack_tag(frame::Tag t);) + +#ifndef CC_INTERP + + void empty_expression_stack() { + movl(rsp, Address(rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize)); + // NULL last_sp until next java call + movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + } + + // Tagged stack helpers for swap and dup + void load_ptr_and_tag(int n, Register val, Register tag); + void store_ptr_and_tag(int n, Register val, Register tag); + + // Tagged Local support + + void tag_local(frame::Tag tag, int n); + void tag_local(Register tag, int n); + void tag_local(frame::Tag tag, Register idx); + void tag_local(Register tag, Register idx); + +#ifdef ASSERT + void verify_local_tag(frame::Tag tag, int n); + void verify_local_tag(frame::Tag tag, Register idx); +#endif // ASSERT + + // Super call_VM calls - correspond to MacroAssembler::call_VM(_leaf) calls + void super_call_VM_leaf(address entry_point); + void super_call_VM_leaf(address entry_point, Register arg_1); + void super_call_VM_leaf(address entry_point, Register arg_1, Register arg_2); + void super_call_VM_leaf(address entry_point, Register arg_1, Register arg_2, Register arg_3); + + // Generate a subtype check: branch to ok_is_subtype if sub_klass is + // a subtype of super_klass. EAX holds the super_klass. Blows ECX + // and EDI. Register sub_klass cannot be any of the above. + void gen_subtype_check( Register sub_klass, Label &ok_is_subtype ); + + // Dispatching + void dispatch_prolog(TosState state, int step = 0); + void dispatch_epilog(TosState state, int step = 0); + void dispatch_only(TosState state); // dispatch via rbx, (assume rbx, is loaded already) + void dispatch_only_normal(TosState state); // dispatch normal table via rbx, (assume rbx, is loaded already) + void dispatch_only_noverify(TosState state); + void dispatch_next(TosState state, int step = 0); // load rbx, from [esi + step] and dispatch via rbx, + void dispatch_via (TosState state, address* table); // load rbx, from [esi] and dispatch via rbx, and table + + + // jump to an invoked target + void jump_from_interpreted(Register method, Register temp); + + // Returning from interpreted functions + // + // Removes the current activation (incl. unlocking of monitors) + // and sets up the return address. This code is also used for + // exception unwindwing. In that case, we do not want to throw + // IllegalMonitorStateExceptions, since that might get us into an + // infinite rethrow exception loop. + // Additionally this code is used for popFrame and earlyReturn. + // In popFrame case we want to skip throwing an exception, + // installing an exception, and notifying jvmdi. + // In earlyReturn case we only want to skip throwing an exception + // and installing an exception. + void remove_activation(TosState state, Register ret_addr, + bool throw_monitor_exception = true, + bool install_monitor_exception = true, + bool notify_jvmdi = true); +#endif /* !CC_INTERP */ + + // Debugging + void verify_oop(Register reg, TosState state = atos); // only if +VerifyOops && state == atos +#ifndef CC_INTERP + void verify_FPU(int stack_depth, TosState state = ftos); // only if +VerifyFPU && (state == ftos || state == dtos) + +#endif /* !CC_INTERP */ + + // Object locking + void lock_object (Register lock_reg); + void unlock_object(Register lock_reg); + +#ifndef CC_INTERP + + // Interpreter profiling operations + void set_method_data_pointer_for_bcp(); + void test_method_data_pointer(Register mdp, Label& zero_continue); + void verify_method_data_pointer(); + + void set_mdp_data_at(Register mdp_in, int constant, Register value); + void increment_mdp_data_at(Address data, bool decrement = false); + void increment_mdp_data_at(Register mdp_in, int constant, + bool decrement = false); + void increment_mdp_data_at(Register mdp_in, Register reg, int constant, + bool decrement = false); + void set_mdp_flag_at(Register mdp_in, int flag_constant); + void test_mdp_data_at(Register mdp_in, int offset, Register value, + Register test_value_out, + Label& not_equal_continue); + + void record_klass_in_profile(Register receiver, Register mdp, + Register reg2); + void record_klass_in_profile_helper(Register receiver, Register mdp, + Register reg2, + int start_row, Label& done); + + void update_mdp_by_offset(Register mdp_in, int offset_of_offset); + void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp); + void update_mdp_by_constant(Register mdp_in, int constant); + void update_mdp_for_ret(Register return_bci); + + void profile_taken_branch(Register mdp, Register bumped_count); + void profile_not_taken_branch(Register mdp); + void profile_call(Register mdp); + void profile_final_call(Register mdp); + void profile_virtual_call(Register receiver, Register mdp, Register scratch2); + void profile_ret(Register return_bci, Register mdp); + void profile_null_seen(Register mdp); + void profile_typecheck(Register mdp, Register klass, Register scratch); + void profile_typecheck_failed(Register mdp); + void profile_switch_default(Register mdp); + void profile_switch_case(Register index_in_scratch, Register mdp, Register scratch2); + +#endif /* !CC_INTERP */ + + typedef enum { NotifyJVMTI, SkipNotifyJVMTI } NotifyMethodExitMode; + + // support for jvmti + void notify_method_entry(); + void notify_method_exit(TosState state, NotifyMethodExitMode mode); + +}; diff --git a/hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp b/hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp new file mode 100644 index 00000000000..7ba7a83e156 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp @@ -0,0 +1,1603 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interp_masm_x86_64.cpp.incl" + + +// Implementation of InterpreterMacroAssembler + +void InterpreterMacroAssembler::call_VM_leaf_base(address entry_point, + int number_of_arguments) { + // interpreter specific + // + // Note: No need to save/restore bcp & locals (r13 & r14) pointer + // since these are callee saved registers and no blocking/ + // GC can happen in leaf calls. +#ifdef ASSERT + save_bcp(); + { + Label L; + cmpq(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), (int)NULL_WORD); + jcc(Assembler::equal, L); + stop("InterpreterMacroAssembler::call_VM_leaf_base:" + " last_sp != NULL"); + bind(L); + } +#endif + // super call + MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments); + // interpreter specific +#ifdef ASSERT + { + Label L; + cmpq(r13, Address(rbp, frame::interpreter_frame_bcx_offset * wordSize)); + jcc(Assembler::equal, L); + stop("InterpreterMacroAssembler::call_VM_leaf_base:" + " r13 not callee saved?"); + bind(L); + } + { + Label L; + cmpq(r14, Address(rbp, frame::interpreter_frame_locals_offset * wordSize)); + jcc(Assembler::equal, L); + stop("InterpreterMacroAssembler::call_VM_leaf_base:" + " r14 not callee saved?"); + bind(L); + } +#endif +} + +void InterpreterMacroAssembler::call_VM_base(Register oop_result, + Register java_thread, + Register last_java_sp, + address entry_point, + int number_of_arguments, + bool check_exceptions) { + // interpreter specific + // + // Note: Could avoid restoring locals ptr (callee saved) - however doesn't + // really make a difference for these runtime calls, since they are + // slow anyway. Btw., bcp must be saved/restored since it may change + // due to GC. + // assert(java_thread == noreg , "not expecting a precomputed java thread"); + save_bcp(); +#ifdef ASSERT + { + Label L; + cmpq(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), (int)NULL_WORD); + jcc(Assembler::equal, L); + stop("InterpreterMacroAssembler::call_VM_leaf_base:" + " last_sp != NULL"); + bind(L); + } +#endif /* ASSERT */ + // super call + MacroAssembler::call_VM_base(oop_result, noreg, last_java_sp, + entry_point, number_of_arguments, + check_exceptions); + // interpreter specific + restore_bcp(); + restore_locals(); +} + + +void InterpreterMacroAssembler::check_and_handle_popframe(Register java_thread) { + if (JvmtiExport::can_pop_frame()) { + Label L; + // Initiate popframe handling only if it is not already being + // processed. If the flag has the popframe_processing bit set, it + // means that this code is called *during* popframe handling - we + // don't want to reenter. + // This method is only called just after the call into the vm in + // call_VM_base, so the arg registers are available. + movl(c_rarg0, Address(r15_thread, JavaThread::popframe_condition_offset())); + testl(c_rarg0, JavaThread::popframe_pending_bit); + jcc(Assembler::zero, L); + testl(c_rarg0, JavaThread::popframe_processing_bit); + jcc(Assembler::notZero, L); + // Call Interpreter::remove_activation_preserving_args_entry() to get the + // address of the same-named entrypoint in the generated interpreter code. + call_VM_leaf(CAST_FROM_FN_PTR(address, Interpreter::remove_activation_preserving_args_entry)); + jmp(rax); + bind(L); + } +} + + +void InterpreterMacroAssembler::load_earlyret_value(TosState state) { + movq(rcx, Address(r15_thread, JavaThread::jvmti_thread_state_offset())); + const Address tos_addr(rcx, JvmtiThreadState::earlyret_tos_offset()); + const Address oop_addr(rcx, JvmtiThreadState::earlyret_oop_offset()); + const Address val_addr(rcx, JvmtiThreadState::earlyret_value_offset()); + switch (state) { + case atos: movq(rax, oop_addr); + movptr(oop_addr, NULL_WORD); + verify_oop(rax, state); break; + case ltos: movq(rax, val_addr); break; + case btos: // fall through + case ctos: // fall through + case stos: // fall through + case itos: movl(rax, val_addr); break; + case ftos: movflt(xmm0, val_addr); break; + case dtos: movdbl(xmm0, val_addr); break; + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } + // Clean up tos value in the thread object + movl(tos_addr, (int) ilgl); + movl(val_addr, (int) NULL_WORD); +} + + +void InterpreterMacroAssembler::check_and_handle_earlyret(Register java_thread) { + if (JvmtiExport::can_force_early_return()) { + Label L; + movq(c_rarg0, Address(r15_thread, JavaThread::jvmti_thread_state_offset())); + testq(c_rarg0, c_rarg0); + jcc(Assembler::zero, L); // if (thread->jvmti_thread_state() == NULL) exit; + + // Initiate earlyret handling only if it is not already being processed. + // If the flag has the earlyret_processing bit set, it means that this code + // is called *during* earlyret handling - we don't want to reenter. + movl(c_rarg0, Address(c_rarg0, JvmtiThreadState::earlyret_state_offset())); + cmpl(c_rarg0, JvmtiThreadState::earlyret_pending); + jcc(Assembler::notEqual, L); + + // Call Interpreter::remove_activation_early_entry() to get the address of the + // same-named entrypoint in the generated interpreter code. + movq(c_rarg0, Address(r15_thread, JavaThread::jvmti_thread_state_offset())); + movl(c_rarg0, Address(c_rarg0, JvmtiThreadState::earlyret_tos_offset())); + call_VM_leaf(CAST_FROM_FN_PTR(address, Interpreter::remove_activation_early_entry), c_rarg0); + jmp(rax); + bind(L); + } +} + + +void InterpreterMacroAssembler::get_unsigned_2_byte_index_at_bcp( + Register reg, + int bcp_offset) { + assert(bcp_offset >= 0, "bcp is still pointing to start of bytecode"); + movl(reg, Address(r13, bcp_offset)); + bswapl(reg); + shrl(reg, 16); +} + + +void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, + Register index, + int bcp_offset) { + assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + assert(cache != index, "must use different registers"); + load_unsigned_word(index, Address(r13, bcp_offset)); + movq(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize)); + assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); + // convert from field index to ConstantPoolCacheEntry index + shll(index, 2); +} + + +void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, + Register tmp, + int bcp_offset) { + assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + assert(cache != tmp, "must use different register"); + load_unsigned_word(tmp, Address(r13, bcp_offset)); + assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); + // convert from field index to ConstantPoolCacheEntry index + // and from word offset to byte offset + shll(tmp, 2 + LogBytesPerWord); + movq(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize)); + // skip past the header + addq(cache, in_bytes(constantPoolCacheOopDesc::base_offset())); + addq(cache, tmp); // construct pointer to cache entry +} + + +// Generate a subtype check: branch to ok_is_subtype if sub_klass is a +// subtype of super_klass. +// +// Args: +// rax: superklass +// Rsub_klass: subklass +// +// Kills: +// rcx, rdi +void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass, + Label& ok_is_subtype) { + assert(Rsub_klass != rax, "rax holds superklass"); + assert(Rsub_klass != r14, "r14 holds locals"); + assert(Rsub_klass != r13, "r13 holds bcp"); + assert(Rsub_klass != rcx, "rcx holds 2ndary super array length"); + assert(Rsub_klass != rdi, "rdi holds 2ndary super array scan ptr"); + + Label not_subtype, loop; + + // Profile the not-null value's klass. + profile_typecheck(rcx, Rsub_klass, rdi); // blows rcx, rdi + + // Load the super-klass's check offset into rcx + movl(rcx, Address(rax, sizeof(oopDesc) + + Klass::super_check_offset_offset_in_bytes())); + // Load from the sub-klass's super-class display list, or a 1-word + // cache of the secondary superclass list, or a failing value with a + // sentinel offset if the super-klass is an interface or + // exceptionally deep in the Java hierarchy and we have to scan the + // secondary superclass list the hard way. See if we get an + // immediate positive hit + cmpq(rax, Address(Rsub_klass, rcx, Address::times_1)); + jcc(Assembler::equal,ok_is_subtype); + + // Check for immediate negative hit + cmpl(rcx, sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes()); + jcc( Assembler::notEqual, not_subtype ); + // Check for self + cmpq(Rsub_klass, rax); + jcc(Assembler::equal, ok_is_subtype); + + // Now do a linear scan of the secondary super-klass chain. + movq(rdi, Address(Rsub_klass, sizeof(oopDesc) + + Klass::secondary_supers_offset_in_bytes())); + // rdi holds the objArrayOop of secondary supers. + // Load the array length + movl(rcx, Address(rdi, arrayOopDesc::length_offset_in_bytes())); + // Skip to start of data; also clear Z flag incase rcx is zero + addq(rdi, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); + // Scan rcx words at [rdi] for occurance of rax + // Set NZ/Z based on last compare + repne_scan(); + // Not equal? + jcc(Assembler::notEqual, not_subtype); + // Must be equal but missed in cache. Update cache. + movq(Address(Rsub_klass, sizeof(oopDesc) + + Klass::secondary_super_cache_offset_in_bytes()), rax); + jmp(ok_is_subtype); + + bind(not_subtype); + profile_typecheck_failed(rcx); // blows rcx +} + + +// Java Expression Stack + +#ifdef ASSERT +// Verifies that the stack tag matches. Must be called before the stack +// value is popped off the stack. +void InterpreterMacroAssembler::verify_stack_tag(frame::Tag t) { + if (TaggedStackInterpreter) { + frame::Tag tag = t; + if (t == frame::TagCategory2) { + tag = frame::TagValue; + Label hokay; + cmpq(Address(rsp, 3*wordSize), (int)tag); + jcc(Assembler::equal, hokay); + stop("Java Expression stack tag high value is bad"); + bind(hokay); + } + Label okay; + cmpq(Address(rsp, wordSize), (int)tag); + jcc(Assembler::equal, okay); + // Also compare if the stack value is zero, then the tag might + // not have been set coming from deopt. + cmpq(Address(rsp, 0), 0); + jcc(Assembler::equal, okay); + stop("Java Expression stack tag value is bad"); + bind(okay); + } +} +#endif // ASSERT + +void InterpreterMacroAssembler::pop_ptr(Register r) { + debug_only(verify_stack_tag(frame::TagReference)); + popq(r); + if (TaggedStackInterpreter) addq(rsp, 1 * wordSize); +} + +void InterpreterMacroAssembler::pop_ptr(Register r, Register tag) { + popq(r); + if (TaggedStackInterpreter) popq(tag); +} + +void InterpreterMacroAssembler::pop_i(Register r) { + // XXX can't use popq currently, upper half non clean + debug_only(verify_stack_tag(frame::TagValue)); + movl(r, Address(rsp, 0)); + addq(rsp, wordSize); + if (TaggedStackInterpreter) addq(rsp, 1 * wordSize); +} + +void InterpreterMacroAssembler::pop_l(Register r) { + debug_only(verify_stack_tag(frame::TagCategory2)); + movq(r, Address(rsp, 0)); + addq(rsp, 2 * Interpreter::stackElementSize()); +} + +void InterpreterMacroAssembler::pop_f(XMMRegister r) { + debug_only(verify_stack_tag(frame::TagValue)); + movflt(r, Address(rsp, 0)); + addq(rsp, wordSize); + if (TaggedStackInterpreter) addq(rsp, 1 * wordSize); +} + +void InterpreterMacroAssembler::pop_d(XMMRegister r) { + debug_only(verify_stack_tag(frame::TagCategory2)); + movdbl(r, Address(rsp, 0)); + addq(rsp, 2 * Interpreter::stackElementSize()); +} + +void InterpreterMacroAssembler::push_ptr(Register r) { + if (TaggedStackInterpreter) pushq(frame::TagReference); + pushq(r); +} + +void InterpreterMacroAssembler::push_ptr(Register r, Register tag) { + if (TaggedStackInterpreter) pushq(tag); + pushq(r); +} + +void InterpreterMacroAssembler::push_i(Register r) { + if (TaggedStackInterpreter) pushq(frame::TagValue); + pushq(r); +} + +void InterpreterMacroAssembler::push_l(Register r) { + if (TaggedStackInterpreter) { + pushq(frame::TagValue); + subq(rsp, 1 * wordSize); + pushq(frame::TagValue); + subq(rsp, 1 * wordSize); + } else { + subq(rsp, 2 * wordSize); + } + movq(Address(rsp, 0), r); +} + +void InterpreterMacroAssembler::push_f(XMMRegister r) { + if (TaggedStackInterpreter) pushq(frame::TagValue); + subq(rsp, wordSize); + movflt(Address(rsp, 0), r); +} + +void InterpreterMacroAssembler::push_d(XMMRegister r) { + if (TaggedStackInterpreter) { + pushq(frame::TagValue); + subq(rsp, 1 * wordSize); + pushq(frame::TagValue); + subq(rsp, 1 * wordSize); + } else { + subq(rsp, 2 * wordSize); + } + movdbl(Address(rsp, 0), r); +} + +void InterpreterMacroAssembler::pop(TosState state) { + switch (state) { + case atos: pop_ptr(); break; + case btos: + case ctos: + case stos: + case itos: pop_i(); break; + case ltos: pop_l(); break; + case ftos: pop_f(); break; + case dtos: pop_d(); break; + case vtos: /* nothing to do */ break; + default: ShouldNotReachHere(); + } + verify_oop(rax, state); +} + +void InterpreterMacroAssembler::push(TosState state) { + verify_oop(rax, state); + switch (state) { + case atos: push_ptr(); break; + case btos: + case ctos: + case stos: + case itos: push_i(); break; + case ltos: push_l(); break; + case ftos: push_f(); break; + case dtos: push_d(); break; + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } +} + + +// Tagged stack helpers for swap and dup +void InterpreterMacroAssembler::load_ptr_and_tag(int n, Register val, + Register tag) { + movq(val, Address(rsp, Interpreter::expr_offset_in_bytes(n))); + if (TaggedStackInterpreter) { + movq(tag, Address(rsp, Interpreter::expr_tag_offset_in_bytes(n))); + } +} + +void InterpreterMacroAssembler::store_ptr_and_tag(int n, Register val, + Register tag) { + movq(Address(rsp, Interpreter::expr_offset_in_bytes(n)), val); + if (TaggedStackInterpreter) { + movq(Address(rsp, Interpreter::expr_tag_offset_in_bytes(n)), tag); + } +} + + +// Tagged local support +void InterpreterMacroAssembler::tag_local(frame::Tag tag, int n) { + if (TaggedStackInterpreter) { + if (tag == frame::TagCategory2) { + mov64(Address(r14, Interpreter::local_tag_offset_in_bytes(n+1)), + (intptr_t)frame::TagValue); + mov64(Address(r14, Interpreter::local_tag_offset_in_bytes(n)), + (intptr_t)frame::TagValue); + } else { + mov64(Address(r14, Interpreter::local_tag_offset_in_bytes(n)), (intptr_t)tag); + } + } +} + +void InterpreterMacroAssembler::tag_local(frame::Tag tag, Register idx) { + if (TaggedStackInterpreter) { + if (tag == frame::TagCategory2) { + mov64(Address(r14, idx, Address::times_8, + Interpreter::local_tag_offset_in_bytes(1)), (intptr_t)frame::TagValue); + mov64(Address(r14, idx, Address::times_8, + Interpreter::local_tag_offset_in_bytes(0)), (intptr_t)frame::TagValue); + } else { + mov64(Address(r14, idx, Address::times_8, Interpreter::local_tag_offset_in_bytes(0)), + (intptr_t)tag); + } + } +} + +void InterpreterMacroAssembler::tag_local(Register tag, Register idx) { + if (TaggedStackInterpreter) { + // can only be TagValue or TagReference + movq(Address(r14, idx, Address::times_8, Interpreter::local_tag_offset_in_bytes(0)), tag); + } +} + + +void InterpreterMacroAssembler::tag_local(Register tag, int n) { + if (TaggedStackInterpreter) { + // can only be TagValue or TagReference + movq(Address(r14, Interpreter::local_tag_offset_in_bytes(n)), tag); + } +} + +#ifdef ASSERT +void InterpreterMacroAssembler::verify_local_tag(frame::Tag tag, int n) { + if (TaggedStackInterpreter) { + frame::Tag t = tag; + if (tag == frame::TagCategory2) { + Label nbl; + t = frame::TagValue; // change to what is stored in locals + cmpq(Address(r14, Interpreter::local_tag_offset_in_bytes(n+1)), (int)t); + jcc(Assembler::equal, nbl); + stop("Local tag is bad for long/double"); + bind(nbl); + } + Label notBad; + cmpq(Address(r14, Interpreter::local_tag_offset_in_bytes(n)), (int)t); + jcc(Assembler::equal, notBad); + // Also compare if the local value is zero, then the tag might + // not have been set coming from deopt. + cmpq(Address(r14, Interpreter::local_offset_in_bytes(n)), 0); + jcc(Assembler::equal, notBad); + stop("Local tag is bad"); + bind(notBad); + } +} + +void InterpreterMacroAssembler::verify_local_tag(frame::Tag tag, Register idx) { + if (TaggedStackInterpreter) { + frame::Tag t = tag; + if (tag == frame::TagCategory2) { + Label nbl; + t = frame::TagValue; // change to what is stored in locals + cmpq(Address(r14, idx, Address::times_8, Interpreter::local_tag_offset_in_bytes(1)), (int)t); + jcc(Assembler::equal, nbl); + stop("Local tag is bad for long/double"); + bind(nbl); + } + Label notBad; + cmpq(Address(r14, idx, Address::times_8, Interpreter::local_tag_offset_in_bytes(0)), (int)t); + jcc(Assembler::equal, notBad); + // Also compare if the local value is zero, then the tag might + // not have been set coming from deopt. + cmpq(Address(r14, idx, Address::times_8, Interpreter::local_offset_in_bytes(0)), 0); + jcc(Assembler::equal, notBad); + stop("Local tag is bad"); + bind(notBad); + } +} +#endif // ASSERT + + +void InterpreterMacroAssembler::super_call_VM_leaf(address entry_point) { + MacroAssembler::call_VM_leaf_base(entry_point, 0); +} + + +void InterpreterMacroAssembler::super_call_VM_leaf(address entry_point, + Register arg_1) { + if (c_rarg0 != arg_1) { + movq(c_rarg0, arg_1); + } + MacroAssembler::call_VM_leaf_base(entry_point, 1); +} + + +void InterpreterMacroAssembler::super_call_VM_leaf(address entry_point, + Register arg_1, + Register arg_2) { + assert(c_rarg0 != arg_2, "smashed argument"); + assert(c_rarg1 != arg_1, "smashed argument"); + if (c_rarg0 != arg_1) { + movq(c_rarg0, arg_1); + } + if (c_rarg1 != arg_2) { + movq(c_rarg1, arg_2); + } + MacroAssembler::call_VM_leaf_base(entry_point, 2); +} + +void InterpreterMacroAssembler::super_call_VM_leaf(address entry_point, + Register arg_1, + Register arg_2, + Register arg_3) { + assert(c_rarg0 != arg_2, "smashed argument"); + assert(c_rarg0 != arg_3, "smashed argument"); + assert(c_rarg1 != arg_1, "smashed argument"); + assert(c_rarg1 != arg_3, "smashed argument"); + assert(c_rarg2 != arg_1, "smashed argument"); + assert(c_rarg2 != arg_2, "smashed argument"); + if (c_rarg0 != arg_1) { + movq(c_rarg0, arg_1); + } + if (c_rarg1 != arg_2) { + movq(c_rarg1, arg_2); + } + if (c_rarg2 != arg_3) { + movq(c_rarg2, arg_3); + } + MacroAssembler::call_VM_leaf_base(entry_point, 3); +} + +// Jump to from_interpreted entry of a call unless single stepping is possible +// in this thread in which case we must call the i2i entry +void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) { + // set sender sp + leaq(r13, Address(rsp, wordSize)); + // record last_sp + movq(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), r13); + + if (JvmtiExport::can_post_interpreter_events()) { + Label run_compiled_code; + // JVMTI events, such as single-stepping, are implemented partly by avoiding running + // compiled code in threads for which the event is enabled. Check here for + // interp_only_mode if these events CAN be enabled. + get_thread(temp); + // interp_only is an int, on little endian it is sufficient to test the byte only + // Is a cmpl faster (ce + cmpb(Address(temp, JavaThread::interp_only_mode_offset()), 0); + jcc(Assembler::zero, run_compiled_code); + jmp(Address(method, methodOopDesc::interpreter_entry_offset())); + bind(run_compiled_code); + } + + jmp(Address(method, methodOopDesc::from_interpreted_offset())); + +} + + +// The following two routines provide a hook so that an implementation +// can schedule the dispatch in two parts. amd64 does not do this. +void InterpreterMacroAssembler::dispatch_prolog(TosState state, int step) { + // Nothing amd64 specific to be done here +} + +void InterpreterMacroAssembler::dispatch_epilog(TosState state, int step) { + dispatch_next(state, step); +} + +void InterpreterMacroAssembler::dispatch_base(TosState state, + address* table, + bool verifyoop) { + verify_FPU(1, state); + if (VerifyActivationFrameSize) { + Label L; + movq(rcx, rbp); + subq(rcx, rsp); + int min_frame_size = + (frame::link_offset - frame::interpreter_frame_initial_sp_offset) * + wordSize; + cmpq(rcx, min_frame_size); + jcc(Assembler::greaterEqual, L); + stop("broken stack frame"); + bind(L); + } + if (verifyoop) { + verify_oop(rax, state); + } + lea(rscratch1, ExternalAddress((address)table)); + jmp(Address(rscratch1, rbx, Address::times_8)); +} + +void InterpreterMacroAssembler::dispatch_only(TosState state) { + dispatch_base(state, Interpreter::dispatch_table(state)); +} + +void InterpreterMacroAssembler::dispatch_only_normal(TosState state) { + dispatch_base(state, Interpreter::normal_table(state)); +} + +void InterpreterMacroAssembler::dispatch_only_noverify(TosState state) { + dispatch_base(state, Interpreter::normal_table(state), false); +} + + +void InterpreterMacroAssembler::dispatch_next(TosState state, int step) { + // load next bytecode (load before advancing r13 to prevent AGI) + load_unsigned_byte(rbx, Address(r13, step)); + // advance r13 + incrementq(r13, step); + dispatch_base(state, Interpreter::dispatch_table(state)); +} + +void InterpreterMacroAssembler::dispatch_via(TosState state, address* table) { + // load current bytecode + load_unsigned_byte(rbx, Address(r13, 0)); + dispatch_base(state, table); +} + +// remove activation +// +// Unlock the receiver if this is a synchronized method. +// Unlock any Java monitors from syncronized blocks. +// Remove the activation from the stack. +// +// If there are locked Java monitors +// If throw_monitor_exception +// throws IllegalMonitorStateException +// Else if install_monitor_exception +// installs IllegalMonitorStateException +// Else +// no error processing +void InterpreterMacroAssembler::remove_activation( + TosState state, + Register ret_addr, + bool throw_monitor_exception, + bool install_monitor_exception, + bool notify_jvmdi) { + // Note: Registers rdx xmm0 may be in use for the + // result check if synchronized method + Label unlocked, unlock, no_unlock; + + // get the value of _do_not_unlock_if_synchronized into rdx + const Address do_not_unlock_if_synchronized(r15_thread, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + movbool(rdx, do_not_unlock_if_synchronized); + movbool(do_not_unlock_if_synchronized, false); // reset the flag + + // get method access flags + movq(rbx, Address(rbp, frame::interpreter_frame_method_offset * wordSize)); + movl(rcx, Address(rbx, methodOopDesc::access_flags_offset())); + testl(rcx, JVM_ACC_SYNCHRONIZED); + jcc(Assembler::zero, unlocked); + + // Don't unlock anything if the _do_not_unlock_if_synchronized flag + // is set. + testbool(rdx); + jcc(Assembler::notZero, no_unlock); + + // unlock monitor + push(state); // save result + + // BasicObjectLock will be first in list, since this is a + // synchronized method. However, need to check that the object has + // not been unlocked by an explicit monitorexit bytecode. + const Address monitor(rbp, frame::interpreter_frame_initial_sp_offset * + wordSize - (int) sizeof(BasicObjectLock)); + // We use c_rarg1 so that if we go slow path it will be the correct + // register for unlock_object to pass to VM directly + leaq(c_rarg1, monitor); // address of first monitor + + movq(rax, Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes())); + testq(rax, rax); + jcc(Assembler::notZero, unlock); + + pop(state); + if (throw_monitor_exception) { + // Entry already unlocked, need to throw exception + call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_illegal_monitor_state_exception)); + should_not_reach_here(); + } else { + // Monitor already unlocked during a stack unroll. If requested, + // install an illegal_monitor_state_exception. Continue with + // stack unrolling. + if (install_monitor_exception) { + call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::new_illegal_monitor_state_exception)); + } + jmp(unlocked); + } + + bind(unlock); + unlock_object(c_rarg1); + pop(state); + + // Check that for block-structured locking (i.e., that all locked + // objects has been unlocked) + bind(unlocked); + + // rax: Might contain return value + + // Check that all monitors are unlocked + { + Label loop, exception, entry, restart; + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + const Address monitor_block_top( + rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize); + const Address monitor_block_bot( + rbp, frame::interpreter_frame_initial_sp_offset * wordSize); + + bind(restart); + // We use c_rarg1 so that if we go slow path it will be the correct + // register for unlock_object to pass to VM directly + movq(c_rarg1, monitor_block_top); // points to current entry, starting + // with top-most entry + leaq(rbx, monitor_block_bot); // points to word before bottom of + // monitor block + jmp(entry); + + // Entry already locked, need to throw exception + bind(exception); + + if (throw_monitor_exception) { + // Throw exception + MacroAssembler::call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime:: + throw_illegal_monitor_state_exception)); + should_not_reach_here(); + } else { + // Stack unrolling. Unlock object and install illegal_monitor_exception. + // Unlock does not block, so don't have to worry about the frame. + // We don't have to preserve c_rarg1 since we are going to throw an exception. + + push(state); + unlock_object(c_rarg1); + pop(state); + + if (install_monitor_exception) { + call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime:: + new_illegal_monitor_state_exception)); + } + + jmp(restart); + } + + bind(loop); + // check if current entry is used + cmpq(Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()), (int) NULL); + jcc(Assembler::notEqual, exception); + + addq(c_rarg1, entry_size); // otherwise advance to next entry + bind(entry); + cmpq(c_rarg1, rbx); // check if bottom reached + jcc(Assembler::notEqual, loop); // if not at bottom then check this entry + } + + bind(no_unlock); + + // jvmti support + if (notify_jvmdi) { + notify_method_exit(state, NotifyJVMTI); // preserve TOSCA + } else { + notify_method_exit(state, SkipNotifyJVMTI); // preserve TOSCA + } + + // remove activation + // get sender sp + movq(rbx, + Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize)); + leave(); // remove frame anchor + popq(ret_addr); // get return address + movq(rsp, rbx); // set sp to sender sp +} + +// Lock object +// +// Args: +// c_rarg1: BasicObjectLock to be used for locking +// +// Kills: +// rax +// c_rarg0, c_rarg1, c_rarg2, c_rarg3, .. (param regs) +// rscratch1, rscratch2 (scratch regs) +void InterpreterMacroAssembler::lock_object(Register lock_reg) { + assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); + + if (UseHeavyMonitors) { + call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + lock_reg); + } else { + Label done; + + const Register swap_reg = rax; // Must use rax for cmpxchg instruction + const Register obj_reg = c_rarg3; // Will contain the oop + + const int obj_offset = BasicObjectLock::obj_offset_in_bytes(); + const int lock_offset = BasicObjectLock::lock_offset_in_bytes (); + const int mark_offset = lock_offset + + BasicLock::displaced_header_offset_in_bytes(); + + Label slow_case; + + // Load object pointer into obj_reg %c_rarg3 + movq(obj_reg, Address(lock_reg, obj_offset)); + + if (UseBiasedLocking) { + biased_locking_enter(lock_reg, obj_reg, swap_reg, rscratch1, false, done, &slow_case); + } + + // Load immediate 1 into swap_reg %rax + movl(swap_reg, 1); + + // Load (object->mark() | 1) into swap_reg %rax + orq(swap_reg, Address(obj_reg, 0)); + + // Save (object->mark() | 1) into BasicLock's displaced header + movq(Address(lock_reg, mark_offset), swap_reg); + + assert(lock_offset == 0, + "displached header must be first word in BasicObjectLock"); + + if (os::is_MP()) lock(); + cmpxchgq(lock_reg, Address(obj_reg, 0)); + if (PrintBiasedLockingStatistics) { + cond_inc32(Assembler::zero, + ExternalAddress((address) BiasedLocking::fast_path_entry_count_addr())); + } + jcc(Assembler::zero, done); + + // Test if the oopMark is an obvious stack pointer, i.e., + // 1) (mark & 7) == 0, and + // 2) rsp <= mark < mark + os::pagesize() + // + // These 3 tests can be done by evaluating the following + // expression: ((mark - rsp) & (7 - os::vm_page_size())), + // assuming both stack pointer and pagesize have their + // least significant 3 bits clear. + // NOTE: the oopMark is in swap_reg %rax as the result of cmpxchg + subq(swap_reg, rsp); + andq(swap_reg, 7 - os::vm_page_size()); + + // Save the test result, for recursive case, the result is zero + movq(Address(lock_reg, mark_offset), swap_reg); + + if (PrintBiasedLockingStatistics) { + cond_inc32(Assembler::zero, + ExternalAddress((address) BiasedLocking::fast_path_entry_count_addr())); + } + jcc(Assembler::zero, done); + + bind(slow_case); + + // Call the runtime routine for slow case + call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + lock_reg); + + bind(done); + } +} + + +// Unlocks an object. Used in monitorexit bytecode and +// remove_activation. Throws an IllegalMonitorException if object is +// not locked by current thread. +// +// Args: +// c_rarg1: BasicObjectLock for lock +// +// Kills: +// rax +// c_rarg0, c_rarg1, c_rarg2, c_rarg3, ... (param regs) +// rscratch1, rscratch2 (scratch regs) +void InterpreterMacroAssembler::unlock_object(Register lock_reg) { + assert(lock_reg == c_rarg1, "The argument is only for looks. It must be rarg1"); + + if (UseHeavyMonitors) { + call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), + lock_reg); + } else { + Label done; + + const Register swap_reg = rax; // Must use rax for cmpxchg instruction + const Register header_reg = c_rarg2; // Will contain the old oopMark + const Register obj_reg = c_rarg3; // Will contain the oop + + save_bcp(); // Save in case of exception + + // Convert from BasicObjectLock structure to object and BasicLock + // structure Store the BasicLock address into %rax + leaq(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset_in_bytes())); + + // Load oop into obj_reg(%c_rarg3) + movq(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset_in_bytes())); + + // Free entry + movptr(Address(lock_reg, BasicObjectLock::obj_offset_in_bytes()), NULL_WORD); + + if (UseBiasedLocking) { + biased_locking_exit(obj_reg, header_reg, done); + } + + // Load the old header from BasicLock structure + movq(header_reg, Address(swap_reg, + BasicLock::displaced_header_offset_in_bytes())); + + // Test for recursion + testq(header_reg, header_reg); + + // zero for recursive case + jcc(Assembler::zero, done); + + // Atomic swap back the old header + if (os::is_MP()) lock(); + cmpxchgq(header_reg, Address(obj_reg, 0)); + + // zero for recursive case + jcc(Assembler::zero, done); + + // Call the runtime routine for slow case. + movq(Address(lock_reg, BasicObjectLock::obj_offset_in_bytes()), + obj_reg); // restore obj + call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), + lock_reg); + + bind(done); + + restore_bcp(); + } +} + + +void InterpreterMacroAssembler::test_method_data_pointer(Register mdp, + Label& zero_continue) { + assert(ProfileInterpreter, "must be profiling interpreter"); + movq(mdp, Address(rbp, frame::interpreter_frame_mdx_offset * wordSize)); + testq(mdp, mdp); + jcc(Assembler::zero, zero_continue); +} + + +// Set the method data pointer for the current bcp. +void InterpreterMacroAssembler::set_method_data_pointer_for_bcp() { + assert(ProfileInterpreter, "must be profiling interpreter"); + Label zero_continue; + pushq(rax); + pushq(rbx); + + get_method(rbx); + // Test MDO to avoid the call if it is NULL. + movq(rax, Address(rbx, in_bytes(methodOopDesc::method_data_offset()))); + testq(rax, rax); + jcc(Assembler::zero, zero_continue); + + // rbx: method + // r13: bcp + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::bcp_to_di), rbx, r13); + // rax: mdi + + movq(rbx, Address(rbx, in_bytes(methodOopDesc::method_data_offset()))); + testq(rbx, rbx); + jcc(Assembler::zero, zero_continue); + addq(rbx, in_bytes(methodDataOopDesc::data_offset())); + addq(rbx, rax); + movq(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), rbx); + + bind(zero_continue); + popq(rbx); + popq(rax); +} + +void InterpreterMacroAssembler::verify_method_data_pointer() { + assert(ProfileInterpreter, "must be profiling interpreter"); +#ifdef ASSERT + Label verify_continue; + pushq(rax); + pushq(rbx); + pushq(c_rarg3); + pushq(c_rarg2); + test_method_data_pointer(c_rarg3, verify_continue); // If mdp is zero, continue + get_method(rbx); + + // If the mdp is valid, it will point to a DataLayout header which is + // consistent with the bcp. The converse is highly probable also. + load_unsigned_word(c_rarg2, + Address(c_rarg3, in_bytes(DataLayout::bci_offset()))); + addq(c_rarg2, Address(rbx, methodOopDesc::const_offset())); + leaq(c_rarg2, Address(c_rarg2, constMethodOopDesc::codes_offset())); + cmpq(c_rarg2, r13); + jcc(Assembler::equal, verify_continue); + // rbx: method + // r13: bcp + // c_rarg3: mdp + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp), + rbx, r13, c_rarg3); + bind(verify_continue); + popq(c_rarg2); + popq(c_rarg3); + popq(rbx); + popq(rax); +#endif // ASSERT +} + + +void InterpreterMacroAssembler::set_mdp_data_at(Register mdp_in, + int constant, + Register value) { + assert(ProfileInterpreter, "must be profiling interpreter"); + Address data(mdp_in, constant); + movq(data, value); +} + + +void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in, + int constant, + bool decrement) { + // Counter address + Address data(mdp_in, constant); + + increment_mdp_data_at(data, decrement); +} + +void InterpreterMacroAssembler::increment_mdp_data_at(Address data, + bool decrement) { + assert(ProfileInterpreter, "must be profiling interpreter"); + + if (decrement) { + // Decrement the register. Set condition codes. + addq(data, -DataLayout::counter_increment); + // If the decrement causes the counter to overflow, stay negative + Label L; + jcc(Assembler::negative, L); + addq(data, DataLayout::counter_increment); + bind(L); + } else { + assert(DataLayout::counter_increment == 1, + "flow-free idiom only works with 1"); + // Increment the register. Set carry flag. + addq(data, DataLayout::counter_increment); + // If the increment causes the counter to overflow, pull back by 1. + sbbq(data, 0); + } +} + + +void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in, + Register reg, + int constant, + bool decrement) { + Address data(mdp_in, reg, Address::times_1, constant); + + increment_mdp_data_at(data, decrement); +} + +void InterpreterMacroAssembler::set_mdp_flag_at(Register mdp_in, + int flag_byte_constant) { + assert(ProfileInterpreter, "must be profiling interpreter"); + int header_offset = in_bytes(DataLayout::header_offset()); + int header_bits = DataLayout::flag_mask_to_header_mask(flag_byte_constant); + // Set the flag + orl(Address(mdp_in, header_offset), header_bits); +} + + + +void InterpreterMacroAssembler::test_mdp_data_at(Register mdp_in, + int offset, + Register value, + Register test_value_out, + Label& not_equal_continue) { + assert(ProfileInterpreter, "must be profiling interpreter"); + if (test_value_out == noreg) { + cmpq(value, Address(mdp_in, offset)); + } else { + // Put the test value into a register, so caller can use it: + movq(test_value_out, Address(mdp_in, offset)); + cmpq(test_value_out, value); + } + jcc(Assembler::notEqual, not_equal_continue); +} + + +void InterpreterMacroAssembler::update_mdp_by_offset(Register mdp_in, + int offset_of_disp) { + assert(ProfileInterpreter, "must be profiling interpreter"); + Address disp_address(mdp_in, offset_of_disp); + addq(mdp_in, disp_address); + movq(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), mdp_in); +} + + +void InterpreterMacroAssembler::update_mdp_by_offset(Register mdp_in, + Register reg, + int offset_of_disp) { + assert(ProfileInterpreter, "must be profiling interpreter"); + Address disp_address(mdp_in, reg, Address::times_1, offset_of_disp); + addq(mdp_in, disp_address); + movq(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), mdp_in); +} + + +void InterpreterMacroAssembler::update_mdp_by_constant(Register mdp_in, + int constant) { + assert(ProfileInterpreter, "must be profiling interpreter"); + addq(mdp_in, constant); + movq(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), mdp_in); +} + + +void InterpreterMacroAssembler::update_mdp_for_ret(Register return_bci) { + assert(ProfileInterpreter, "must be profiling interpreter"); + pushq(return_bci); // save/restore across call_VM + call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::update_mdp_for_ret), + return_bci); + popq(return_bci); +} + + +void InterpreterMacroAssembler::profile_taken_branch(Register mdp, + Register bumped_count) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + // Otherwise, assign to mdp + test_method_data_pointer(mdp, profile_continue); + + // We are taking a branch. Increment the taken count. + // We inline increment_mdp_data_at to return bumped_count in a register + //increment_mdp_data_at(mdp, in_bytes(JumpData::taken_offset())); + Address data(mdp, in_bytes(JumpData::taken_offset())); + movq(bumped_count, data); + assert(DataLayout::counter_increment == 1, + "flow-free idiom only works with 1"); + addq(bumped_count, DataLayout::counter_increment); + sbbq(bumped_count, 0); + movq(data, bumped_count); // Store back out + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_offset(mdp, in_bytes(JumpData::displacement_offset())); + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // We are taking a branch. Increment the not taken count. + increment_mdp_data_at(mdp, in_bytes(BranchData::not_taken_offset())); + + // The method data pointer needs to be updated to correspond to + // the next bytecode + update_mdp_by_constant(mdp, in_bytes(BranchData::branch_data_size())); + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_call(Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(mdp, in_bytes(CounterData::counter_data_size())); + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_final_call(Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(mdp, + in_bytes(VirtualCallData:: + virtual_call_data_size())); + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_virtual_call(Register receiver, + Register mdp, + Register reg2) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + + // Record the receiver type. + record_klass_in_profile(receiver, mdp, reg2); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(mdp, + in_bytes(VirtualCallData:: + virtual_call_data_size())); + bind(profile_continue); + } +} + +// This routine creates a state machine for updating the multi-row +// type profile at a virtual call site (or other type-sensitive bytecode). +// The machine visits each row (of receiver/count) until the receiver type +// is found, or until it runs out of rows. At the same time, it remembers +// the location of the first empty row. (An empty row records null for its +// receiver, and can be allocated for a newly-observed receiver type.) +// Because there are two degrees of freedom in the state, a simple linear +// search will not work; it must be a decision tree. Hence this helper +// function is recursive, to generate the required tree structured code. +// It's the interpreter, so we are trading off code space for speed. +// See below for example code. +void InterpreterMacroAssembler::record_klass_in_profile_helper( + Register receiver, Register mdp, + Register reg2, + int start_row, Label& done) { + int last_row = VirtualCallData::row_limit() - 1; + assert(start_row <= last_row, "must be work left to do"); + // Test this row for both the receiver and for null. + // Take any of three different outcomes: + // 1. found receiver => increment count and goto done + // 2. found null => keep looking for case 1, maybe allocate this cell + // 3. found something else => keep looking for cases 1 and 2 + // Case 3 is handled by a recursive call. + for (int row = start_row; row <= last_row; row++) { + Label next_test; + bool test_for_null_also = (row == start_row); + + // See if the receiver is receiver[n]. + int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row)); + test_mdp_data_at(mdp, recvr_offset, receiver, + (test_for_null_also ? reg2 : noreg), + next_test); + // (Reg2 now contains the receiver from the CallData.) + + // The receiver is receiver[n]. Increment count[n]. + int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row)); + increment_mdp_data_at(mdp, count_offset); + jmp(done); + bind(next_test); + + if (test_for_null_also) { + // Failed the equality check on receiver[n]... Test for null. + testq(reg2, reg2); + if (start_row == last_row) { + // The only thing left to do is handle the null case. + jcc(Assembler::notZero, done); + break; + } + // Since null is rare, make it be the branch-taken case. + Label found_null; + jcc(Assembler::zero, found_null); + + // Put all the "Case 3" tests here. + record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done); + + // Found a null. Keep searching for a matching receiver, + // but remember that this is an empty (unused) slot. + bind(found_null); + } + } + + // In the fall-through case, we found no matching receiver, but we + // observed the receiver[start_row] is NULL. + + // Fill in the receiver field and increment the count. + int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row)); + set_mdp_data_at(mdp, recvr_offset, receiver); + int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row)); + movl(reg2, DataLayout::counter_increment); + set_mdp_data_at(mdp, count_offset, reg2); + jmp(done); +} + +// Example state machine code for three profile rows: +// // main copy of decision tree, rooted at row[1] +// if (row[0].rec == rec) { row[0].incr(); goto done; } +// if (row[0].rec != NULL) { +// // inner copy of decision tree, rooted at row[1] +// if (row[1].rec == rec) { row[1].incr(); goto done; } +// if (row[1].rec != NULL) { +// // degenerate decision tree, rooted at row[2] +// if (row[2].rec == rec) { row[2].incr(); goto done; } +// if (row[2].rec != NULL) { goto done; } // overflow +// row[2].init(rec); goto done; +// } else { +// // remember row[1] is empty +// if (row[2].rec == rec) { row[2].incr(); goto done; } +// row[1].init(rec); goto done; +// } +// } else { +// // remember row[0] is empty +// if (row[1].rec == rec) { row[1].incr(); goto done; } +// if (row[2].rec == rec) { row[2].incr(); goto done; } +// row[0].init(rec); goto done; +// } + +void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, + Register mdp, + Register reg2) { + assert(ProfileInterpreter, "must be profiling"); + Label done; + + record_klass_in_profile_helper(receiver, mdp, reg2, 0, done); + + bind (done); +} + +void InterpreterMacroAssembler::profile_ret(Register return_bci, + Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + uint row; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // Update the total ret count. + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + + for (row = 0; row < RetData::row_limit(); row++) { + Label next_test; + + // See if return_bci is equal to bci[n]: + test_mdp_data_at(mdp, + in_bytes(RetData::bci_offset(row)), + return_bci, noreg, + next_test); + + // return_bci is equal to bci[n]. Increment the count. + increment_mdp_data_at(mdp, in_bytes(RetData::bci_count_offset(row))); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_offset(mdp, + in_bytes(RetData::bci_displacement_offset(row))); + jmp(profile_continue); + bind(next_test); + } + + update_mdp_for_ret(return_bci); + + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_null_seen(Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // The method data pointer needs to be updated. + int mdp_delta = in_bytes(BitData::bit_data_size()); + if (TypeProfileCasts) { + mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); + } + update_mdp_by_constant(mdp, mdp_delta); + + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_typecheck_failed(Register mdp) { + if (ProfileInterpreter && TypeProfileCasts) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + int count_offset = in_bytes(CounterData::count_offset()); + // Back up the address, since we have already bumped the mdp. + count_offset -= in_bytes(VirtualCallData::virtual_call_data_size()); + + // *Decrement* the counter. We expect to see zero or small negatives. + increment_mdp_data_at(mdp, count_offset, true); + + bind (profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, Register reg2) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // The method data pointer needs to be updated. + int mdp_delta = in_bytes(BitData::bit_data_size()); + if (TypeProfileCasts) { + mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); + + // Record the object type. + record_klass_in_profile(klass, mdp, reg2); + } + update_mdp_by_constant(mdp, mdp_delta); + + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_switch_default(Register mdp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // Update the default case count + increment_mdp_data_at(mdp, + in_bytes(MultiBranchData::default_count_offset())); + + // The method data pointer needs to be updated. + update_mdp_by_offset(mdp, + in_bytes(MultiBranchData:: + default_displacement_offset())); + + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::profile_switch_case(Register index, + Register mdp, + Register reg2) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + // Build the base (index * per_case_size_in_bytes()) + + // case_array_offset_in_bytes() + movl(reg2, in_bytes(MultiBranchData::per_case_size())); + imulq(index, reg2); // XXX l ? + addq(index, in_bytes(MultiBranchData::case_array_offset())); // XXX l ? + + // Update the case count + increment_mdp_data_at(mdp, + index, + in_bytes(MultiBranchData::relative_count_offset())); + + // The method data pointer needs to be updated. + update_mdp_by_offset(mdp, + index, + in_bytes(MultiBranchData:: + relative_displacement_offset())); + + bind(profile_continue); + } +} + + +void InterpreterMacroAssembler::verify_oop(Register reg, TosState state) { + if (state == atos) { + MacroAssembler::verify_oop(reg); + } +} + +void InterpreterMacroAssembler::verify_FPU(int stack_depth, TosState state) { +} + + +void InterpreterMacroAssembler::notify_method_entry() { + // Whenever JVMTI is interp_only_mode, method entry/exit events are sent to + // track stack depth. If it is possible to enter interp_only_mode we add + // the code to check if the event should be sent. + if (JvmtiExport::can_post_interpreter_events()) { + Label L; + movl(rdx, Address(r15_thread, JavaThread::interp_only_mode_offset())); + testl(rdx, rdx); + jcc(Assembler::zero, L); + call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::post_method_entry)); + bind(L); + } + + { + SkipIfEqual skip(this, &DTraceMethodProbes, false); + get_method(c_rarg1); + call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), + r15_thread, c_rarg1); + } +} + + +void InterpreterMacroAssembler::notify_method_exit( + TosState state, NotifyMethodExitMode mode) { + // Whenever JVMTI is interp_only_mode, method entry/exit events are sent to + // track stack depth. If it is possible to enter interp_only_mode we add + // the code to check if the event should be sent. + if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) { + Label L; + // Note: frame::interpreter_frame_result has a dependency on how the + // method result is saved across the call to post_method_exit. If this + // is changed then the interpreter_frame_result implementation will + // need to be updated too. + push(state); + movl(rdx, Address(r15_thread, JavaThread::interp_only_mode_offset())); + testl(rdx, rdx); + jcc(Assembler::zero, L); + call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit)); + bind(L); + pop(state); + } + + { + SkipIfEqual skip(this, &DTraceMethodProbes, false); + push(state); + get_method(c_rarg1); + call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), + r15_thread, c_rarg1); + pop(state); + } +} diff --git a/hotspot/src/cpu/x86/vm/interp_masm_x86_64.hpp b/hotspot/src/cpu/x86/vm/interp_masm_x86_64.hpp new file mode 100644 index 00000000000..3b1baa258cf --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_64.hpp @@ -0,0 +1,245 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file specializes the assember with interpreter-specific macros + + +class InterpreterMacroAssembler + : public MacroAssembler { + protected: + // Interpreter specific version of call_VM_base + virtual void call_VM_leaf_base(address entry_point, + int number_of_arguments); + + virtual void call_VM_base(Register oop_result, + Register java_thread, + Register last_java_sp, + address entry_point, + int number_of_arguments, + bool check_exceptions); + + virtual void check_and_handle_popframe(Register java_thread); + virtual void check_and_handle_earlyret(Register java_thread); + + // base routine for all dispatches + void dispatch_base(TosState state, address* table, bool verifyoop = true); + + public: + InterpreterMacroAssembler(CodeBuffer* code) + : MacroAssembler(code) + {} + + void load_earlyret_value(TosState state); + + // Interpreter-specific registers + void save_bcp() + { + movq(Address(rbp, frame::interpreter_frame_bcx_offset * wordSize), r13); + } + + void restore_bcp() + { + movq(r13, Address(rbp, frame::interpreter_frame_bcx_offset * wordSize)); + } + + void restore_locals() + { + movq(r14, Address(rbp, frame::interpreter_frame_locals_offset * wordSize)); + } + + // Helpers for runtime call arguments/results + void get_method(Register reg) + { + movq(reg, Address(rbp, frame::interpreter_frame_method_offset * wordSize)); + } + + void get_constant_pool(Register reg) + { + get_method(reg); + movq(reg, Address(reg, methodOopDesc::constants_offset())); + } + + void get_constant_pool_cache(Register reg) + { + get_constant_pool(reg); + movq(reg, Address(reg, constantPoolOopDesc::cache_offset_in_bytes())); + } + + void get_cpool_and_tags(Register cpool, Register tags) + { + get_constant_pool(cpool); + movq(tags, Address(cpool, constantPoolOopDesc::tags_offset_in_bytes())); + } + + void get_unsigned_2_byte_index_at_bcp(Register reg, int bcp_offset); + void get_cache_and_index_at_bcp(Register cache, Register index, + int bcp_offset); + void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, + int bcp_offset); + + void pop_ptr(Register r = rax); + void pop_i(Register r = rax); + void pop_l(Register r = rax); + void pop_f(XMMRegister r = xmm0); + void pop_d(XMMRegister r = xmm0); + void push_ptr(Register r = rax); + void push_i(Register r = rax); + void push_l(Register r = rax); + void push_f(XMMRegister r = xmm0); + void push_d(XMMRegister r = xmm0); + + void pop(TosState state); // transition vtos -> state + void push(TosState state); // transition state -> vtos + + // Tagged stack support, pop and push both tag and value. + void pop_ptr(Register r, Register tag); + void push_ptr(Register r, Register tag); + + DEBUG_ONLY(void verify_stack_tag(frame::Tag t);) + + // Tagged stack helpers for swap and dup + void load_ptr_and_tag(int n, Register val, Register tag); + void store_ptr_and_tag(int n, Register val, Register tag); + + // Tagged Local support + void tag_local(frame::Tag tag, int n); + void tag_local(Register tag, int n); + void tag_local(frame::Tag tag, Register idx); + void tag_local(Register tag, Register idx); + +#ifdef ASSERT + void verify_local_tag(frame::Tag tag, int n); + void verify_local_tag(frame::Tag tag, Register idx); +#endif // ASSERT + + void empty_expression_stack() + { + movq(rsp, Address(rbp, frame::interpreter_frame_monitor_block_top_offset * + wordSize)); + // NULL last_sp until next java call + movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + } + + // Super call_VM calls - correspond to MacroAssembler::call_VM(_leaf) calls + void super_call_VM_leaf(address entry_point); + void super_call_VM_leaf(address entry_point, Register arg_1); + void super_call_VM_leaf(address entry_point, Register arg_1, Register arg_2); + void super_call_VM_leaf(address entry_point, + Register arg_1, Register arg_2, Register arg_3); + + // Generate a subtype check: branch to ok_is_subtype if sub_klass is + // a subtype of super_klass. + void gen_subtype_check( Register sub_klass, Label &ok_is_subtype ); + + // Dispatching + void dispatch_prolog(TosState state, int step = 0); + void dispatch_epilog(TosState state, int step = 0); + // dispatch via ebx (assume ebx is loaded already) + void dispatch_only(TosState state); + // dispatch normal table via ebx (assume ebx is loaded already) + void dispatch_only_normal(TosState state); + void dispatch_only_noverify(TosState state); + // load ebx from [esi + step] and dispatch via ebx + void dispatch_next(TosState state, int step = 0); + // load ebx from [esi] and dispatch via ebx and table + void dispatch_via (TosState state, address* table); + + // jump to an invoked target + void jump_from_interpreted(Register method, Register temp); + + + // Returning from interpreted functions + // + // Removes the current activation (incl. unlocking of monitors) + // and sets up the return address. This code is also used for + // exception unwindwing. In that case, we do not want to throw + // IllegalMonitorStateExceptions, since that might get us into an + // infinite rethrow exception loop. + // Additionally this code is used for popFrame and earlyReturn. + // In popFrame case we want to skip throwing an exception, + // installing an exception, and notifying jvmdi. + // In earlyReturn case we only want to skip throwing an exception + // and installing an exception. + void remove_activation(TosState state, Register ret_addr, + bool throw_monitor_exception = true, + bool install_monitor_exception = true, + bool notify_jvmdi = true); + + // Object locking + void lock_object (Register lock_reg); + void unlock_object(Register lock_reg); + + // Interpreter profiling operations + void set_method_data_pointer_for_bcp(); + void test_method_data_pointer(Register mdp, Label& zero_continue); + void verify_method_data_pointer(); + + void set_mdp_data_at(Register mdp_in, int constant, Register value); + void increment_mdp_data_at(Address data, bool decrement = false); + void increment_mdp_data_at(Register mdp_in, int constant, + bool decrement = false); + void increment_mdp_data_at(Register mdp_in, Register reg, int constant, + bool decrement = false); + void set_mdp_flag_at(Register mdp_in, int flag_constant); + void test_mdp_data_at(Register mdp_in, int offset, Register value, + Register test_value_out, + Label& not_equal_continue); + + void record_klass_in_profile(Register receiver, Register mdp, + Register reg2); + void record_klass_in_profile_helper(Register receiver, Register mdp, + Register reg2, + int start_row, Label& done); + + void update_mdp_by_offset(Register mdp_in, int offset_of_offset); + void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp); + void update_mdp_by_constant(Register mdp_in, int constant); + void update_mdp_for_ret(Register return_bci); + + void profile_taken_branch(Register mdp, Register bumped_count); + void profile_not_taken_branch(Register mdp); + void profile_call(Register mdp); + void profile_final_call(Register mdp); + void profile_virtual_call(Register receiver, Register mdp, + Register scratch2); + void profile_ret(Register return_bci, Register mdp); + void profile_null_seen(Register mdp); + void profile_typecheck(Register mdp, Register klass, Register scratch); + void profile_typecheck_failed(Register mdp); + void profile_switch_default(Register mdp); + void profile_switch_case(Register index_in_scratch, Register mdp, + Register scratch2); + + // Debugging + // only if +VerifyOops && state == atos + void verify_oop(Register reg, TosState state = atos); + // only if +VerifyFPU && (state == ftos || state == dtos) + void verify_FPU(int stack_depth, TosState state = ftos); + + typedef enum { NotifyJVMTI, SkipNotifyJVMTI } NotifyMethodExitMode; + + // support for jvmti/dtrace + void notify_method_entry(); + void notify_method_exit(TosState state, NotifyMethodExitMode mode); +}; diff --git a/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp b/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp new file mode 100644 index 00000000000..066d89d5fc0 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp @@ -0,0 +1,42 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +// Generation of Interpreter +// + friend class AbstractInterpreterGenerator; + + private: + + address generate_normal_entry(bool synchronized); + address generate_native_entry(bool synchronized); + address generate_abstract_entry(void); + address generate_math_entry(AbstractInterpreter::MethodKind kind); + address generate_empty_entry(void); + address generate_accessor_entry(void); + void lock_method(void); + void generate_stack_overflow_check(void); + + void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); + void generate_counter_overflow(Label* do_continue); diff --git a/hotspot/src/cpu/x86/vm/interpreterRT_x86.hpp b/hotspot/src/cpu/x86/vm/interpreterRT_x86.hpp new file mode 100644 index 00000000000..df95eea6e4a --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interpreterRT_x86.hpp @@ -0,0 +1,74 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// native method calls + +class SignatureHandlerGenerator: public NativeSignatureIterator { + private: + MacroAssembler* _masm; +#ifdef AMD64 +#ifdef _WIN64 + unsigned int _num_args; +#else + unsigned int _num_fp_args; + unsigned int _num_int_args; +#endif // _WIN64 + int _stack_offset; +#else + void move(int from_offset, int to_offset); + void box(int from_offset, int to_offset); +#endif // AMD64 + + void pass_int(); + void pass_long(); + void pass_float(); +#ifdef AMD64 + void pass_double(); +#endif // AMD64 + void pass_object(); + + public: + // Creation + SignatureHandlerGenerator(methodHandle method, CodeBuffer* buffer) : NativeSignatureIterator(method) { + _masm = new MacroAssembler(buffer); +#ifdef AMD64 +#ifdef _WIN64 + _num_args = (method->is_static() ? 1 : 0); + _stack_offset = (Argument::n_int_register_parameters_c+1)* wordSize; // don't overwrite return address +#else + _num_int_args = (method->is_static() ? 1 : 0); + _num_fp_args = 0; + _stack_offset = wordSize; // don't overwrite return address +#endif // _WIN64 +#endif // AMD64 + } + + // Code generation + void generate(uint64_t fingerprint); + + // Code generation support + static Register from(); + static Register to(); + static Register temp(); +}; diff --git a/hotspot/src/cpu/x86/vm/interpreterRT_x86_32.cpp b/hotspot/src/cpu/x86/vm/interpreterRT_x86_32.cpp new file mode 100644 index 00000000000..73f11d64ccd --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interpreterRT_x86_32.cpp @@ -0,0 +1,133 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreterRT_x86_32.cpp.incl" + + +#define __ _masm-> + + +// Implementation of SignatureHandlerGenerator +void InterpreterRuntime::SignatureHandlerGenerator::pass_int() { + move(offset(), jni_offset() + 1); +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_long() { + move(offset(), jni_offset() + 2); + move(offset() + 1, jni_offset() + 1); +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_object() { + box (offset(), jni_offset() + 1); +} + +void InterpreterRuntime::SignatureHandlerGenerator::move(int from_offset, int to_offset) { + __ movl(temp(), Address(from(), Interpreter::local_offset_in_bytes(from_offset))); + __ movl(Address(to(), to_offset * wordSize), temp()); +} + + +void InterpreterRuntime::SignatureHandlerGenerator::box(int from_offset, int to_offset) { + __ leal(temp(), Address(from(), Interpreter::local_offset_in_bytes(from_offset))); + __ cmpl(Address(from(), Interpreter::local_offset_in_bytes(from_offset)), 0); // do not use temp() to avoid AGI + Label L; + __ jcc(Assembler::notZero, L); + __ movl(temp(), 0); + __ bind(L); + __ movl(Address(to(), to_offset * wordSize), temp()); +} + + +void InterpreterRuntime::SignatureHandlerGenerator::generate( uint64_t fingerprint) { + // generate code to handle arguments + iterate(fingerprint); + // return result handler + __ lea(rax, + ExternalAddress((address)Interpreter::result_handler(method()->result_type()))); + // return + __ ret(0); + __ flush(); +} + + +Register InterpreterRuntime::SignatureHandlerGenerator::from() { return rdi; } +Register InterpreterRuntime::SignatureHandlerGenerator::to() { return rsp; } +Register InterpreterRuntime::SignatureHandlerGenerator::temp() { return rcx; } + + +// Implementation of SignatureHandlerLibrary + +void SignatureHandlerLibrary::pd_set_handler(address handler) {} + +class SlowSignatureHandler: public NativeSignatureIterator { + private: + address _from; + intptr_t* _to; + +#ifdef ASSERT + void verify_tag(frame::Tag t) { + assert(!TaggedStackInterpreter || + *(intptr_t*)(_from+Interpreter::local_tag_offset_in_bytes(0)) == t, "wrong tag"); + } +#endif // ASSERT + + virtual void pass_int() { + *_to++ = *(jint *)(_from+Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagValue)); + _from -= Interpreter::stackElementSize(); + } + + virtual void pass_long() { + _to[0] = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); + _to[1] = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagValue)); + _to += 2; + _from -= 2*Interpreter::stackElementSize(); + } + + virtual void pass_object() { + // pass address of from + intptr_t from_addr = (intptr_t)(_from + Interpreter::local_offset_in_bytes(0)); + *_to++ = (*(intptr_t*)from_addr == 0) ? NULL : from_addr; + debug_only(verify_tag(frame::TagReference)); + _from -= Interpreter::stackElementSize(); + } + + public: + SlowSignatureHandler(methodHandle method, address from, intptr_t* to) : + NativeSignatureIterator(method) { + _from = from; + _to = to + (is_static() ? 2 : 1); + } +}; + +IRT_ENTRY(address, InterpreterRuntime::slow_signature_handler(JavaThread* thread, methodOopDesc* method, intptr_t* from, intptr_t* to)) + methodHandle m(thread, (methodOop)method); + assert(m->is_native(), "sanity check"); + // handle arguments + SlowSignatureHandler(m, (address)from, to + 1).iterate(UCONST64(-1)); + // return result handler + return Interpreter::result_handler(m->result_type()); +IRT_END diff --git a/hotspot/src/cpu/x86/vm/interpreterRT_x86_64.cpp b/hotspot/src/cpu/x86/vm/interpreterRT_x86_64.cpp new file mode 100644 index 00000000000..90d0ad9d8d0 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interpreterRT_x86_64.cpp @@ -0,0 +1,508 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreterRT_x86_64.cpp.incl" + +#define __ _masm-> + +// Implementation of SignatureHandlerGenerator + +Register InterpreterRuntime::SignatureHandlerGenerator::from() { return r14; } +Register InterpreterRuntime::SignatureHandlerGenerator::to() { return rsp; } +Register InterpreterRuntime::SignatureHandlerGenerator::temp() { return rscratch1; } + +void InterpreterRuntime::SignatureHandlerGenerator::pass_int() { + const Address src(from(), Interpreter::local_offset_in_bytes(offset())); + +#ifdef _WIN64 + switch (_num_args) { + case 0: + __ movl(c_rarg1, src); + _num_args++; + break; + case 1: + __ movl(c_rarg2, src); + _num_args++; + break; + case 2: + __ movl(c_rarg3, src); + _num_args++; + break; + default: + __ movl(rax, src); + __ movl(Address(to(), _stack_offset), rax); + _stack_offset += wordSize; + break; + } +#else + switch (_num_int_args) { + case 0: + __ movl(c_rarg1, src); + _num_int_args++; + break; + case 1: + __ movl(c_rarg2, src); + _num_int_args++; + break; + case 2: + __ movl(c_rarg3, src); + _num_int_args++; + break; + case 3: + __ movl(c_rarg4, src); + _num_int_args++; + break; + case 4: + __ movl(c_rarg5, src); + _num_int_args++; + break; + default: + __ movl(rax, src); + __ movl(Address(to(), _stack_offset), rax); + _stack_offset += wordSize; + break; + } +#endif +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_long() { + const Address src(from(), Interpreter::local_offset_in_bytes(offset() + 1)); + +#ifdef _WIN64 + switch (_num_args) { + case 0: + __ movq(c_rarg1, src); + _num_args++; + break; + case 1: + __ movq(c_rarg2, src); + _num_args++; + break; + case 2: + __ movq(c_rarg3, src); + _num_args++; + break; + case 3: + default: + __ movq(rax, src); + __ movq(Address(to(), _stack_offset), rax); + _stack_offset += wordSize; + break; + } +#else + switch (_num_int_args) { + case 0: + __ movq(c_rarg1, src); + _num_int_args++; + break; + case 1: + __ movq(c_rarg2, src); + _num_int_args++; + break; + case 2: + __ movq(c_rarg3, src); + _num_int_args++; + break; + case 3: + __ movq(c_rarg4, src); + _num_int_args++; + break; + case 4: + __ movq(c_rarg5, src); + _num_int_args++; + break; + default: + __ movq(rax, src); + __ movq(Address(to(), _stack_offset), rax); + _stack_offset += wordSize; + break; + } +#endif +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_float() { + const Address src(from(), Interpreter::local_offset_in_bytes(offset())); + +#ifdef _WIN64 + if (_num_args < Argument::n_float_register_parameters_c-1) { + __ movflt(as_XMMRegister(++_num_args), src); + } else { + __ movl(rax, src); + __ movl(Address(to(), _stack_offset), rax); + _stack_offset += wordSize; + } +#else + if (_num_fp_args < Argument::n_float_register_parameters_c) { + __ movflt(as_XMMRegister(_num_fp_args++), src); + } else { + __ movl(rax, src); + __ movl(Address(to(), _stack_offset), rax); + _stack_offset += wordSize; + } +#endif +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_double() { + const Address src(from(), Interpreter::local_offset_in_bytes(offset() + 1)); + +#ifdef _WIN64 + if (_num_args < Argument::n_float_register_parameters_c-1) { + __ movdbl(as_XMMRegister(++_num_args), src); + } else { + __ movq(rax, src); + __ movq(Address(to(), _stack_offset), rax); + _stack_offset += wordSize; + } +#else + if (_num_fp_args < Argument::n_float_register_parameters_c) { + __ movdbl(as_XMMRegister(_num_fp_args++), src); + } else { + __ movq(rax, src); + __ movq(Address(to(), _stack_offset), rax); + _stack_offset += wordSize; + } +#endif +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_object() { + const Address src(from(), Interpreter::local_offset_in_bytes(offset())); + +#ifdef _WIN64 + switch (_num_args) { + case 0: + assert(offset() == 0, "argument register 1 can only be (non-null) receiver"); + __ leaq(c_rarg1, src); + _num_args++; + break; + case 1: + __ leaq(rax, src); + __ xorl(c_rarg2, c_rarg2); + __ cmpq(src, 0); + __ cmovq(Assembler::notEqual, c_rarg2, rax); + _num_args++; + break; + case 2: + __ leaq(rax, src); + __ xorl(c_rarg3, c_rarg3); + __ cmpq(src, 0); + __ cmovq(Assembler::notEqual, c_rarg3, rax); + _num_args++; + break; + default: + __ leaq(rax, src); + __ xorl(temp(), temp()); + __ cmpq(src, 0); + __ cmovq(Assembler::notEqual, temp(), rax); + __ movq(Address(to(), _stack_offset), temp()); + _stack_offset += wordSize; + break; + } +#else + switch (_num_int_args) { + case 0: + assert(offset() == 0, "argument register 1 can only be (non-null) receiver"); + __ leaq(c_rarg1, src); + _num_int_args++; + break; + case 1: + __ leaq(rax, src); + __ xorl(c_rarg2, c_rarg2); + __ cmpq(src, 0); + __ cmovq(Assembler::notEqual, c_rarg2, rax); + _num_int_args++; + break; + case 2: + __ leaq(rax, src); + __ xorl(c_rarg3, c_rarg3); + __ cmpq(src, 0); + __ cmovq(Assembler::notEqual, c_rarg3, rax); + _num_int_args++; + break; + case 3: + __ leaq(rax, src); + __ xorl(c_rarg4, c_rarg4); + __ cmpq(src, 0); + __ cmovq(Assembler::notEqual, c_rarg4, rax); + _num_int_args++; + break; + case 4: + __ leaq(rax, src); + __ xorl(c_rarg5, c_rarg5); + __ cmpq(src, 0); + __ cmovq(Assembler::notEqual, c_rarg5, rax); + _num_int_args++; + break; + default: + __ leaq(rax, src); + __ xorl(temp(), temp()); + __ cmpq(src, 0); + __ cmovq(Assembler::notEqual, temp(), rax); + __ movq(Address(to(), _stack_offset), temp()); + _stack_offset += wordSize; + break; + } +#endif +} + +void InterpreterRuntime::SignatureHandlerGenerator::generate(uint64_t fingerprint) { + // generate code to handle arguments + iterate(fingerprint); + + // return result handler + __ lea(rax, ExternalAddress(Interpreter::result_handler(method()->result_type()))); + __ ret(0); + + __ flush(); +} + + +// Implementation of SignatureHandlerLibrary + +void SignatureHandlerLibrary::pd_set_handler(address handler) {} + + +#ifdef _WIN64 +class SlowSignatureHandler + : public NativeSignatureIterator { + private: + address _from; + intptr_t* _to; + intptr_t* _reg_args; + intptr_t* _fp_identifiers; + unsigned int _num_args; + +#ifdef ASSERT + void verify_tag(frame::Tag t) { + assert(!TaggedStackInterpreter || + *(intptr_t*)(_from+Interpreter::local_tag_offset_in_bytes(0)) == t, "wrong tag"); + } +#endif // ASSERT + + virtual void pass_int() + { + jint from_obj = *(jint *)(_from+Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagValue)); + _from -= Interpreter::stackElementSize(); + + if (_num_args < Argument::n_int_register_parameters_c-1) { + *_reg_args++ = from_obj; + _num_args++; + } else { + *_to++ = from_obj; + } + } + + virtual void pass_long() + { + intptr_t from_obj = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); + debug_only(verify_tag(frame::TagValue)); + _from -= 2*Interpreter::stackElementSize(); + + if (_num_args < Argument::n_int_register_parameters_c-1) { + *_reg_args++ = from_obj; + _num_args++; + } else { + *_to++ = from_obj; + } + } + + virtual void pass_object() + { + intptr_t *from_addr = (intptr_t*)(_from + Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagReference)); + _from -= Interpreter::stackElementSize(); + if (_num_args < Argument::n_int_register_parameters_c-1) { + *_reg_args++ = (*from_addr == 0) ? NULL : (intptr_t) from_addr; + _num_args++; + } else { + *_to++ = (*from_addr == 0) ? NULL : (intptr_t) from_addr; + } + } + + virtual void pass_float() + { + jint from_obj = *(jint *)(_from+Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagValue)); + _from -= Interpreter::stackElementSize(); + + if (_num_args < Argument::n_float_register_parameters_c-1) { + *_reg_args++ = from_obj; + *_fp_identifiers |= (0x01 << (_num_args*2)); // mark as float + _num_args++; + } else { + *_to++ = from_obj; + } + } + + virtual void pass_double() + { + intptr_t from_obj = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); + debug_only(verify_tag(frame::TagValue)); + _from -= 2*Interpreter::stackElementSize(); + + if (_num_args < Argument::n_float_register_parameters_c-1) { + *_reg_args++ = from_obj; + *_fp_identifiers |= (0x3 << (_num_args*2)); // mark as double + _num_args++; + } else { + *_to++ = from_obj; + } + } + + public: + SlowSignatureHandler(methodHandle method, address from, intptr_t* to) + : NativeSignatureIterator(method) + { + _from = from; + _to = to; + + _reg_args = to - (method->is_static() ? 4 : 5); + _fp_identifiers = to - 2; + _to = _to + 4; // Windows reserves stack space for register arguments + *(int*) _fp_identifiers = 0; + _num_args = (method->is_static() ? 1 : 0); + } +}; +#else +class SlowSignatureHandler + : public NativeSignatureIterator { + private: + address _from; + intptr_t* _to; + intptr_t* _int_args; + intptr_t* _fp_args; + intptr_t* _fp_identifiers; + unsigned int _num_int_args; + unsigned int _num_fp_args; + +#ifdef ASSERT + void verify_tag(frame::Tag t) { + assert(!TaggedStackInterpreter || + *(intptr_t*)(_from+Interpreter::local_tag_offset_in_bytes(0)) == t, "wrong tag"); + } +#endif // ASSERT + + virtual void pass_int() + { + jint from_obj = *(jint *)(_from+Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagValue)); + _from -= Interpreter::stackElementSize(); + + if (_num_int_args < Argument::n_int_register_parameters_c-1) { + *_int_args++ = from_obj; + _num_int_args++; + } else { + *_to++ = from_obj; + } + } + + virtual void pass_long() + { + intptr_t from_obj = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); + debug_only(verify_tag(frame::TagValue)); + _from -= 2*Interpreter::stackElementSize(); + + if (_num_int_args < Argument::n_int_register_parameters_c-1) { + *_int_args++ = from_obj; + _num_int_args++; + } else { + *_to++ = from_obj; + } + } + + virtual void pass_object() + { + intptr_t *from_addr = (intptr_t*)(_from + Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagReference)); + _from -= Interpreter::stackElementSize(); + + if (_num_int_args < Argument::n_int_register_parameters_c-1) { + *_int_args++ = (*from_addr == 0) ? NULL : (intptr_t)from_addr; + _num_int_args++; + } else { + *_to++ = (*from_addr == 0) ? NULL : (intptr_t) from_addr; + } + } + + virtual void pass_float() + { + jint from_obj = *(jint*)(_from+Interpreter::local_offset_in_bytes(0)); + debug_only(verify_tag(frame::TagValue)); + _from -= Interpreter::stackElementSize(); + + if (_num_fp_args < Argument::n_float_register_parameters_c) { + *_fp_args++ = from_obj; + _num_fp_args++; + } else { + *_to++ = from_obj; + } + } + + virtual void pass_double() + { + intptr_t from_obj = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); + _from -= 2*Interpreter::stackElementSize(); + + if (_num_fp_args < Argument::n_float_register_parameters_c) { + *_fp_args++ = from_obj; + *_fp_identifiers |= (1 << _num_fp_args); // mark as double + _num_fp_args++; + } else { + *_to++ = from_obj; + } + } + + public: + SlowSignatureHandler(methodHandle method, address from, intptr_t* to) + : NativeSignatureIterator(method) + { + _from = from; + _to = to; + + _int_args = to - (method->is_static() ? 14 : 15); + _fp_args = to - 9; + _fp_identifiers = to - 10; + *(int*) _fp_identifiers = 0; + _num_int_args = (method->is_static() ? 1 : 0); + _num_fp_args = 0; + } +}; +#endif + + +IRT_ENTRY(address, + InterpreterRuntime::slow_signature_handler(JavaThread* thread, + methodOopDesc* method, + intptr_t* from, + intptr_t* to)) + methodHandle m(thread, (methodOop)method); + assert(m->is_native(), "sanity check"); + + // handle arguments + SlowSignatureHandler(m, (address)from, to + 1).iterate(UCONST64(-1)); + + // return result handler + return Interpreter::result_handler(m->result_type()); +IRT_END diff --git a/hotspot/src/cpu/x86/vm/interpreter_x86.hpp b/hotspot/src/cpu/x86/vm/interpreter_x86.hpp new file mode 100644 index 00000000000..04db7fdf7b3 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interpreter_x86.hpp @@ -0,0 +1,69 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + public: + + // Sentinel placed in the code for interpreter returns so + // that i2c adapters and osr code can recognize an interpreter + // return address and convert the return to a specialized + // block of code to handle compiedl return values and cleaning + // the fpu stack. + static const int return_sentinel; + + + static Address::ScaleFactor stackElementScale() { + return TaggedStackInterpreter? Address::times_8 : Address::times_4; + } + + // Offset from rsp (which points to the last stack element) + static int expr_offset_in_bytes(int i) { return stackElementSize()*i ; } + static int expr_tag_offset_in_bytes(int i) { + assert(TaggedStackInterpreter, "should not call this"); + return expr_offset_in_bytes(i) + wordSize; + } + + // Support for Tagged Stacks + + // Stack index relative to tos (which points at value) + static int expr_index_at(int i) { + return stackElementWords() * i; + } + + static int expr_tag_index_at(int i) { + assert(TaggedStackInterpreter, "should not call this"); + // tag is one word above java stack element + return stackElementWords() * i + 1; + } + + // Already negated by c++ interpreter + static int local_index_at(int i) { + assert(i<=0, "local direction already negated"); + return stackElementWords() * i + (value_offset_in_bytes()/wordSize); + } + + static int local_tag_index_at(int i) { + assert(i<=0, "local direction already negated"); + assert(TaggedStackInterpreter, "should not call this"); + return stackElementWords() * i + (tag_offset_in_bytes()/wordSize); + } diff --git a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp new file mode 100644 index 00000000000..fbdf398bc80 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp @@ -0,0 +1,250 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreter_x86_32.cpp.incl" + +#define __ _masm-> + +// Initialize the sentinel used to distinguish an interpreter return address. +const int Interpreter::return_sentinel = 0xfeedbeed; + +//------------------------------------------------------------------------------------------------------------------------ + +address AbstractInterpreterGenerator::generate_slow_signature_handler() { + address entry = __ pc(); + // rbx,: method + // rcx: temporary + // rdi: pointer to locals + // rsp: end of copied parameters area + __ movl(rcx, rsp); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::slow_signature_handler), rbx, rdi, rcx); + __ ret(0); + return entry; +} + + +// +// Various method entries (that c++ and asm interpreter agree upon) +//------------------------------------------------------------------------------------------------------------------------ +// +// + +// Empty method, generate a very fast return. + +address InterpreterGenerator::generate_empty_entry(void) { + + // rbx,: methodOop + // rcx: receiver (unused) + // rsi: previous interpreter state (C++ interpreter) must preserve + // rsi: sender sp must set sp to this value on return + + if (!UseFastEmptyMethods) return NULL; + + address entry_point = __ pc(); + + // If we need a safepoint check, generate full interpreter entry. + Label slow_path; + ExternalAddress state(SafepointSynchronize::address_of_state()); + __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), + SafepointSynchronize::_not_synchronized); + __ jcc(Assembler::notEqual, slow_path); + + // do nothing for empty methods (do not even increment invocation counter) + // Code: _return + // _return + // return w/o popping parameters + __ popl(rax); + __ movl(rsp, rsi); + __ jmp(rax); + + __ bind(slow_path); + (void) generate_normal_entry(false); + return entry_point; +} + +address InterpreterGenerator::generate_math_entry(AbstractInterpreter::MethodKind kind) { + + // rbx,: methodOop + // rcx: scratrch + // rsi: sender sp + + if (!InlineIntrinsics) return NULL; // Generate a vanilla entry + + address entry_point = __ pc(); + + // These don't need a safepoint check because they aren't virtually + // callable. We won't enter these intrinsics from compiled code. + // If in the future we added an intrinsic which was virtually callable + // we'd have to worry about how to safepoint so that this code is used. + + // mathematical functions inlined by compiler + // (interpreter must provide identical implementation + // in order to avoid monotonicity bugs when switching + // from interpreter to compiler in the middle of some + // computation) + // + // stack: [ ret adr ] <-- rsp + // [ lo(arg) ] + // [ hi(arg) ] + // + + // Note: For JDK 1.2 StrictMath doesn't exist and Math.sin/cos/sqrt are + // native methods. Interpreter::method_kind(...) does a check for + // native methods first before checking for intrinsic methods and + // thus will never select this entry point. Make sure it is not + // called accidentally since the SharedRuntime entry points will + // not work for JDK 1.2. + // + // We no longer need to check for JDK 1.2 since it's EOL'ed. + // The following check existed in pre 1.6 implementation, + // if (Universe::is_jdk12x_version()) { + // __ should_not_reach_here(); + // } + // Universe::is_jdk12x_version() always returns false since + // the JDK version is not yet determined when this method is called. + // This method is called during interpreter_init() whereas + // JDK version is only determined when universe2_init() is called. + + // Note: For JDK 1.3 StrictMath exists and Math.sin/cos/sqrt are + // java methods. Interpreter::method_kind(...) will select + // this entry point for the corresponding methods in JDK 1.3. + // get argument + if (TaggedStackInterpreter) { + __ pushl(Address(rsp, 3*wordSize)); // push hi (and note rsp -= wordSize) + __ pushl(Address(rsp, 2*wordSize)); // push lo + __ fld_d(Address(rsp, 0)); // get double in ST0 + __ addl(rsp, 2*wordSize); + } else { + __ fld_d(Address(rsp, 1*wordSize)); + } + switch (kind) { + case Interpreter::java_lang_math_sin : + __ trigfunc('s'); + break; + case Interpreter::java_lang_math_cos : + __ trigfunc('c'); + break; + case Interpreter::java_lang_math_tan : + __ trigfunc('t'); + break; + case Interpreter::java_lang_math_sqrt: + __ fsqrt(); + break; + case Interpreter::java_lang_math_abs: + __ fabs(); + break; + case Interpreter::java_lang_math_log: + __ flog(); + // Store to stack to convert 80bit precision back to 64bits + __ push_fTOS(); + __ pop_fTOS(); + break; + case Interpreter::java_lang_math_log10: + __ flog10(); + // Store to stack to convert 80bit precision back to 64bits + __ push_fTOS(); + __ pop_fTOS(); + break; + default : + ShouldNotReachHere(); + } + + // return double result in xmm0 for interpreter and compilers. + if (UseSSE >= 2) { + __ subl(rsp, 2*wordSize); + __ fstp_d(Address(rsp, 0)); + __ movdbl(xmm0, Address(rsp, 0)); + __ addl(rsp, 2*wordSize); + } + + // done, result in FPU ST(0) or XMM0 + __ popl(rdi); // get return address + __ movl(rsp, rsi); // set sp to sender sp + __ jmp(rdi); + + return entry_point; +} + + +// Abstract method entry +// Attempt to execute abstract method. Throw exception +address InterpreterGenerator::generate_abstract_entry(void) { + + // rbx,: methodOop + // rcx: receiver (unused) + // rsi: previous interpreter state (C++ interpreter) must preserve + + // rsi: sender SP + + address entry_point = __ pc(); + + // abstract method entry + // remove return address. Not really needed, since exception handling throws away expression stack + __ popl(rbx); + + // adjust stack to what a normal return would do + __ movl(rsp, rsi); + // throw exception + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); + // the call_VM checks for exception, so we should never return here. + __ should_not_reach_here(); + + return entry_point; +} + +// This method tells the deoptimizer how big an interpreted frame must be: +int AbstractInterpreter::size_activation(methodOop method, + int tempcount, + int popframe_extra_args, + int moncount, + int callee_param_count, + int callee_locals, + bool is_top_frame) { + return layout_activation(method, + tempcount, + popframe_extra_args, + moncount, + callee_param_count, + callee_locals, + (frame*) NULL, + (frame*) NULL, + is_top_frame); +} + +void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { + + // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in + // the days we had adapter frames. When we deoptimize a situation where a + // compiled caller calls a compiled caller will have registers it expects + // to survive the call to the callee. If we deoptimize the callee the only + // way we can restore these registers is to have the oldest interpreter + // frame that we create restore these values. That is what this routine + // will accomplish. + + // At the moment we have modified c2 to not have any callee save registers + // so this problem does not exist and this routine is just a place holder. + + assert(f->is_interpreted_frame(), "must be interpreted"); +} diff --git a/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp new file mode 100644 index 00000000000..c9709ae854d --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp @@ -0,0 +1,459 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreter_x86_64.cpp.incl" + +#define __ _masm-> + + +#ifdef _WIN64 +address AbstractInterpreterGenerator::generate_slow_signature_handler() { + address entry = __ pc(); + + // rbx: method + // r14: pointer to locals + // c_rarg3: first stack arg - wordSize + __ movq(c_rarg3, rsp); + // adjust rsp + __ subq(rsp, 4 * wordSize); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::slow_signature_handler), + rbx, r14, c_rarg3); + + // rax: result handler + + // Stack layout: + // rsp: 3 integer or float args (if static first is unused) + // 1 float/double identifiers + // return address + // stack args + // garbage + // expression stack bottom + // bcp (NULL) + // ... + + // Do FP first so we can use c_rarg3 as temp + __ movl(c_rarg3, Address(rsp, 3 * wordSize)); // float/double identifiers + + for ( int i= 0; i < Argument::n_int_register_parameters_c-1; i++ ) { + XMMRegister floatreg = as_XMMRegister(i+1); + Label isfloatordouble, isdouble, next; + + __ testl(c_rarg3, 1 << (i*2)); // Float or Double? + __ jcc(Assembler::notZero, isfloatordouble); + + // Do Int register here + switch ( i ) { + case 0: + __ movl(rscratch1, Address(rbx, methodOopDesc::access_flags_offset())); + __ testl(rscratch1, JVM_ACC_STATIC); + __ cmovq(Assembler::zero, c_rarg1, Address(rsp, 0)); + break; + case 1: + __ movq(c_rarg2, Address(rsp, wordSize)); + break; + case 2: + __ movq(c_rarg3, Address(rsp, 2 * wordSize)); + break; + default: + break; + } + + __ jmp (next); + + __ bind(isfloatordouble); + __ testl(c_rarg3, 1 << ((i*2)+1)); // Double? + __ jcc(Assembler::notZero, isdouble); + +// Do Float Here + __ movflt(floatreg, Address(rsp, i * wordSize)); + __ jmp(next); + +// Do Double here + __ bind(isdouble); + __ movdbl(floatreg, Address(rsp, i * wordSize)); + + __ bind(next); + } + + + // restore rsp + __ addq(rsp, 4 * wordSize); + + __ ret(0); + + return entry; +} +#else +address AbstractInterpreterGenerator::generate_slow_signature_handler() { + address entry = __ pc(); + + // rbx: method + // r14: pointer to locals + // c_rarg3: first stack arg - wordSize + __ movq(c_rarg3, rsp); + // adjust rsp + __ subq(rsp, 14 * wordSize); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::slow_signature_handler), + rbx, r14, c_rarg3); + + // rax: result handler + + // Stack layout: + // rsp: 5 integer args (if static first is unused) + // 1 float/double identifiers + // 8 double args + // return address + // stack args + // garbage + // expression stack bottom + // bcp (NULL) + // ... + + // Do FP first so we can use c_rarg3 as temp + __ movl(c_rarg3, Address(rsp, 5 * wordSize)); // float/double identifiers + + for (int i = 0; i < Argument::n_float_register_parameters_c; i++) { + const XMMRegister r = as_XMMRegister(i); + + Label d, done; + + __ testl(c_rarg3, 1 << i); + __ jcc(Assembler::notZero, d); + __ movflt(r, Address(rsp, (6 + i) * wordSize)); + __ jmp(done); + __ bind(d); + __ movdbl(r, Address(rsp, (6 + i) * wordSize)); + __ bind(done); + } + + // Now handle integrals. Only do c_rarg1 if not static. + __ movl(c_rarg3, Address(rbx, methodOopDesc::access_flags_offset())); + __ testl(c_rarg3, JVM_ACC_STATIC); + __ cmovq(Assembler::zero, c_rarg1, Address(rsp, 0)); + + __ movq(c_rarg2, Address(rsp, wordSize)); + __ movq(c_rarg3, Address(rsp, 2 * wordSize)); + __ movq(c_rarg4, Address(rsp, 3 * wordSize)); + __ movq(c_rarg5, Address(rsp, 4 * wordSize)); + + // restore rsp + __ addq(rsp, 14 * wordSize); + + __ ret(0); + + return entry; +} +#endif + + +// +// Various method entries +// + +address InterpreterGenerator::generate_math_entry( + AbstractInterpreter::MethodKind kind) { + // rbx: methodOop + + if (!InlineIntrinsics) return NULL; // Generate a vanilla entry + + assert(kind == Interpreter::java_lang_math_sqrt, + "Other intrinsics are not special"); + + address entry_point = __ pc(); + + // These don't need a safepoint check because they aren't virtually + // callable. We won't enter these intrinsics from compiled code. + // If in the future we added an intrinsic which was virtually callable + // we'd have to worry about how to safepoint so that this code is used. + + // mathematical functions inlined by compiler + // (interpreter must provide identical implementation + // in order to avoid monotonicity bugs when switching + // from interpreter to compiler in the middle of some + // computation) + + // Note: For JDK 1.2 StrictMath doesn't exist and Math.sin/cos/sqrt are + // native methods. Interpreter::method_kind(...) does a check for + // native methods first before checking for intrinsic methods and + // thus will never select this entry point. Make sure it is not + // called accidentally since the SharedRuntime entry points will + // not work for JDK 1.2. + // + // We no longer need to check for JDK 1.2 since it's EOL'ed. + // The following check existed in pre 1.6 implementation, + // if (Universe::is_jdk12x_version()) { + // __ should_not_reach_here(); + // } + // Universe::is_jdk12x_version() always returns false since + // the JDK version is not yet determined when this method is called. + // This method is called during interpreter_init() whereas + // JDK version is only determined when universe2_init() is called. + + // Note: For JDK 1.3 StrictMath exists and Math.sin/cos/sqrt are + // java methods. Interpreter::method_kind(...) will select + // this entry point for the corresponding methods in JDK 1.3. + __ sqrtsd(xmm0, Address(rsp, wordSize)); + + __ popq(rax); + __ movq(rsp, r13); + __ jmp(rax); + + return entry_point; +} + + +// Abstract method entry +// Attempt to execute abstract method. Throw exception +address InterpreterGenerator::generate_abstract_entry(void) { + // rbx: methodOop + // r13: sender SP + + address entry_point = __ pc(); + + // abstract method entry + // remove return address. Not really needed, since exception + // handling throws away expression stack + __ popq(rbx); + + // adjust stack to what a normal return would do + __ movq(rsp, r13); + + // throw exception + __ call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_AbstractMethodError)); + // the call_VM checks for exception, so we should never return here. + __ should_not_reach_here(); + + return entry_point; +} + + +// Empty method, generate a very fast return. + +address InterpreterGenerator::generate_empty_entry(void) { + // rbx: methodOop + // r13: sender sp must set sp to this value on return + + if (!UseFastEmptyMethods) { + return NULL; + } + + address entry_point = __ pc(); + + // If we need a safepoint check, generate full interpreter entry. + Label slow_path; + __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), + SafepointSynchronize::_not_synchronized); + __ jcc(Assembler::notEqual, slow_path); + + // do nothing for empty methods (do not even increment invocation counter) + // Code: _return + // _return + // return w/o popping parameters + __ popq(rax); + __ movq(rsp, r13); + __ jmp(rax); + + __ bind(slow_path); + (void) generate_normal_entry(false); + return entry_point; + +} + +// Call an accessor method (assuming it is resolved, otherwise drop +// into vanilla (slow path) entry +address InterpreterGenerator::generate_accessor_entry(void) { + // rbx: methodOop + + // r13: senderSP must preserver for slow path, set SP to it on fast path + + address entry_point = __ pc(); + Label xreturn_path; + + // do fastpath for resolved accessor methods + if (UseFastAccessorMethods) { + // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites + // thereof; parameter size = 1 + // Note: We can only use this code if the getfield has been resolved + // and if we don't have a null-pointer exception => check for + // these conditions first and use slow path if necessary. + Label slow_path; + // If we need a safepoint check, generate full interpreter entry. + __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), + SafepointSynchronize::_not_synchronized); + + __ jcc(Assembler::notEqual, slow_path); + // rbx: method + __ movq(rax, Address(rsp, wordSize)); + + // check if local 0 != NULL and read field + __ testq(rax, rax); + __ jcc(Assembler::zero, slow_path); + + __ movq(rdi, Address(rbx, methodOopDesc::constants_offset())); + // read first instruction word and extract bytecode @ 1 and index @ 2 + __ movq(rdx, Address(rbx, methodOopDesc::const_offset())); + __ movl(rdx, Address(rdx, constMethodOopDesc::codes_offset())); + // Shift codes right to get the index on the right. + // The bytecode fetched looks like <0xb4><0x2a> + __ shrl(rdx, 2 * BitsPerByte); + __ shll(rdx, exact_log2(in_words(ConstantPoolCacheEntry::size()))); + __ movq(rdi, Address(rdi, constantPoolOopDesc::cache_offset_in_bytes())); + + // rax: local 0 + // rbx: method + // rdx: constant pool cache index + // rdi: constant pool cache + + // check if getfield has been resolved and read constant pool cache entry + // check the validity of the cache entry by testing whether _indices field + // contains Bytecode::_getfield in b1 byte. + assert(in_words(ConstantPoolCacheEntry::size()) == 4, + "adjust shift below"); + __ movl(rcx, + Address(rdi, + rdx, + Address::times_8, + constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::indices_offset())); + __ shrl(rcx, 2 * BitsPerByte); + __ andl(rcx, 0xFF); + __ cmpl(rcx, Bytecodes::_getfield); + __ jcc(Assembler::notEqual, slow_path); + + // Note: constant pool entry is not valid before bytecode is resolved + __ movq(rcx, + Address(rdi, + rdx, + Address::times_8, + constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::f2_offset())); + // edx: flags + __ movl(rdx, + Address(rdi, + rdx, + Address::times_8, + constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::flags_offset())); + + Label notObj, notInt, notByte, notShort; + const Address field_address(rax, rcx, Address::times_1); + + // Need to differentiate between igetfield, agetfield, bgetfield etc. + // because they are different sizes. + // Use the type from the constant pool cache + __ shrl(rdx, ConstantPoolCacheEntry::tosBits); + // Make sure we don't need to mask edx for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + + __ cmpl(rdx, atos); + __ jcc(Assembler::notEqual, notObj); + // atos + __ movq(rax, field_address); + __ jmp(xreturn_path); + + __ bind(notObj); + __ cmpl(rdx, itos); + __ jcc(Assembler::notEqual, notInt); + // itos + __ movl(rax, field_address); + __ jmp(xreturn_path); + + __ bind(notInt); + __ cmpl(rdx, btos); + __ jcc(Assembler::notEqual, notByte); + // btos + __ load_signed_byte(rax, field_address); + __ jmp(xreturn_path); + + __ bind(notByte); + __ cmpl(rdx, stos); + __ jcc(Assembler::notEqual, notShort); + // stos + __ load_signed_word(rax, field_address); + __ jmp(xreturn_path); + + __ bind(notShort); +#ifdef ASSERT + Label okay; + __ cmpl(rdx, ctos); + __ jcc(Assembler::equal, okay); + __ stop("what type is this?"); + __ bind(okay); +#endif + // ctos + __ load_unsigned_word(rax, field_address); + + __ bind(xreturn_path); + + // _ireturn/_areturn + __ popq(rdi); + __ movq(rsp, r13); + __ jmp(rdi); + __ ret(0); + + // generate a vanilla interpreter entry as the slow path + __ bind(slow_path); + (void) generate_normal_entry(false); + } else { + (void) generate_normal_entry(false); + } + + return entry_point; +} + +// This method tells the deoptimizer how big an interpreted frame must be: +int AbstractInterpreter::size_activation(methodOop method, + int tempcount, + int popframe_extra_args, + int moncount, + int callee_param_count, + int callee_locals, + bool is_top_frame) { + return layout_activation(method, + tempcount, popframe_extra_args, moncount, + callee_param_count, callee_locals, + (frame*) NULL, (frame*) NULL, is_top_frame); +} + +void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { + + // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in + // the days we had adapter frames. When we deoptimize a situation where a + // compiled caller calls a compiled caller will have registers it expects + // to survive the call to the callee. If we deoptimize the callee the only + // way we can restore these registers is to have the oldest interpreter + // frame that we create restore these values. That is what this routine + // will accomplish. + + // At the moment we have modified c2 to not have any callee save registers + // so this problem does not exist and this routine is just a place holder. + + assert(f->is_interpreted_frame(), "must be interpreted"); +} diff --git a/hotspot/src/cpu/x86/vm/javaFrameAnchor_x86.hpp b/hotspot/src/cpu/x86/vm/javaFrameAnchor_x86.hpp new file mode 100644 index 00000000000..9dcadf9f92e --- /dev/null +++ b/hotspot/src/cpu/x86/vm/javaFrameAnchor_x86.hpp @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +private: + + // FP value associated with _last_Java_sp: + intptr_t* volatile _last_Java_fp; // pointer is volatile not what it points to + +public: + // Each arch must define reset, save, restore + // These are used by objects that only care about: + // 1 - initializing a new state (thread creation, javaCalls) + // 2 - saving a current state (javaCalls) + // 3 - restoring an old state (javaCalls) + + void clear(void) { + // clearing _last_Java_sp must be first + _last_Java_sp = NULL; + // fence? + _last_Java_fp = NULL; + _last_Java_pc = NULL; + } + + void copy(JavaFrameAnchor* src) { + // In order to make sure the transition state is valid for "this" + // We must clear _last_Java_sp before copying the rest of the new data + // + // Hack Alert: Temporary bugfix for 4717480/4721647 + // To act like previous version (pd_cache_state) don't NULL _last_Java_sp + // unless the value is changing + // + if (_last_Java_sp != src->_last_Java_sp) + _last_Java_sp = NULL; + + _last_Java_fp = src->_last_Java_fp; + _last_Java_pc = src->_last_Java_pc; + // Must be last so profiler will always see valid frame if has_last_frame() is true + _last_Java_sp = src->_last_Java_sp; + } + + // Always walkable + bool walkable(void) { return true; } + // Never any thing to do since we are always walkable and can find address of return addresses + void make_walkable(JavaThread* thread) { } + + intptr_t* last_Java_sp(void) const { return _last_Java_sp; } + +private: + + static ByteSize last_Java_fp_offset() { return byte_offset_of(JavaFrameAnchor, _last_Java_fp); } + +public: + + void set_last_Java_sp(intptr_t* sp) { _last_Java_sp = sp; } + + intptr_t* last_Java_fp(void) { return _last_Java_fp; } + // Assert (last_Java_sp == NULL || fp == NULL) + void set_last_Java_fp(intptr_t* fp) { _last_Java_fp = fp; } diff --git a/hotspot/src/cpu/x86/vm/jniFastGetField_x86_32.cpp b/hotspot/src/cpu/x86/vm/jniFastGetField_x86_32.cpp new file mode 100644 index 00000000000..9643c843d0d --- /dev/null +++ b/hotspot/src/cpu/x86/vm/jniFastGetField_x86_32.cpp @@ -0,0 +1,356 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jniFastGetField_x86_32.cpp.incl" + +#define __ masm-> + +#define BUFFER_SIZE 30 + +#ifdef _WINDOWS +GetBooleanField_t JNI_FastGetField::jni_fast_GetBooleanField_fp; +GetByteField_t JNI_FastGetField::jni_fast_GetByteField_fp; +GetCharField_t JNI_FastGetField::jni_fast_GetCharField_fp; +GetShortField_t JNI_FastGetField::jni_fast_GetShortField_fp; +GetIntField_t JNI_FastGetField::jni_fast_GetIntField_fp; +GetLongField_t JNI_FastGetField::jni_fast_GetLongField_fp; +GetFloatField_t JNI_FastGetField::jni_fast_GetFloatField_fp; +GetDoubleField_t JNI_FastGetField::jni_fast_GetDoubleField_fp; +#endif + +// Instead of issuing lfence for LoadLoad barrier, we create data dependency +// between loads, which is much more efficient than lfence. + +address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { + const char *name; + switch (type) { + case T_BOOLEAN: name = "jni_fast_GetBooleanField"; break; + case T_BYTE: name = "jni_fast_GetByteField"; break; + case T_CHAR: name = "jni_fast_GetCharField"; break; + case T_SHORT: name = "jni_fast_GetShortField"; break; + case T_INT: name = "jni_fast_GetIntField"; break; + default: ShouldNotReachHere(); + } + ResourceMark rm; + BufferBlob* b = BufferBlob::create(name, BUFFER_SIZE*wordSize); + address fast_entry = b->instructions_begin(); + CodeBuffer cbuf(fast_entry, b->instructions_size()); + MacroAssembler* masm = new MacroAssembler(&cbuf); + + Label slow; + + // stack layout: offset from rsp (in words): + // return pc 0 + // jni env 1 + // obj 2 + // jfieldID 3 + + ExternalAddress counter(SafepointSynchronize::safepoint_counter_addr()); + __ mov32 (rcx, counter); + __ testb (rcx, 1); + __ jcc (Assembler::notZero, slow); + if (os::is_MP()) { + __ movl (rax, rcx); + __ andl (rax, 1); // rax, must end up 0 + __ movl (rdx, Address(rsp, rax, Address::times_1, 2*wordSize)); + // obj, notice rax, is 0. + // rdx is data dependent on rcx. + } else { + __ movl (rdx, Address(rsp, 2*wordSize)); // obj + } + __ movl (rax, Address(rsp, 3*wordSize)); // jfieldID + __ movl (rdx, Address(rdx, 0)); // *obj + __ shrl (rax, 2); // offset + + assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); + speculative_load_pclist[count] = __ pc(); + switch (type) { + case T_BOOLEAN: __ movzxb (rax, Address(rdx, rax, Address::times_1)); break; + case T_BYTE: __ movsxb (rax, Address(rdx, rax, Address::times_1)); break; + case T_CHAR: __ movzxw (rax, Address(rdx, rax, Address::times_1)); break; + case T_SHORT: __ movsxw (rax, Address(rdx, rax, Address::times_1)); break; + case T_INT: __ movl (rax, Address(rdx, rax, Address::times_1)); break; + default: ShouldNotReachHere(); + } + + Address ca1; + if (os::is_MP()) { + __ lea(rdx, counter); + __ xorl(rdx, rax); + __ xorl(rdx, rax); + __ cmp32(rcx, Address(rdx, 0)); + // ca1 is the same as ca because + // rax, ^ counter_addr ^ rax, = address + // ca1 is data dependent on rax,. + } else { + __ cmp32(rcx, counter); + } + __ jcc (Assembler::notEqual, slow); + +#ifndef _WINDOWS + __ ret (0); +#else + // __stdcall calling convention + __ ret (3*wordSize); +#endif + + slowcase_entry_pclist[count++] = __ pc(); + __ bind (slow); + address slow_case_addr; + switch (type) { + case T_BOOLEAN: slow_case_addr = jni_GetBooleanField_addr(); break; + case T_BYTE: slow_case_addr = jni_GetByteField_addr(); break; + case T_CHAR: slow_case_addr = jni_GetCharField_addr(); break; + case T_SHORT: slow_case_addr = jni_GetShortField_addr(); break; + case T_INT: slow_case_addr = jni_GetIntField_addr(); + } + // tail call + __ jump (ExternalAddress(slow_case_addr)); + + __ flush (); + +#ifndef _WINDOWS + return fast_entry; +#else + switch (type) { + case T_BOOLEAN: jni_fast_GetBooleanField_fp = (GetBooleanField_t)fast_entry; break; + case T_BYTE: jni_fast_GetByteField_fp = (GetByteField_t)fast_entry; break; + case T_CHAR: jni_fast_GetCharField_fp = (GetCharField_t)fast_entry; break; + case T_SHORT: jni_fast_GetShortField_fp = (GetShortField_t)fast_entry; break; + case T_INT: jni_fast_GetIntField_fp = (GetIntField_t)fast_entry; + } + return os::win32::fast_jni_accessor_wrapper(type); +#endif +} + +address JNI_FastGetField::generate_fast_get_boolean_field() { + return generate_fast_get_int_field0(T_BOOLEAN); +} + +address JNI_FastGetField::generate_fast_get_byte_field() { + return generate_fast_get_int_field0(T_BYTE); +} + +address JNI_FastGetField::generate_fast_get_char_field() { + return generate_fast_get_int_field0(T_CHAR); +} + +address JNI_FastGetField::generate_fast_get_short_field() { + return generate_fast_get_int_field0(T_SHORT); +} + +address JNI_FastGetField::generate_fast_get_int_field() { + return generate_fast_get_int_field0(T_INT); +} + +address JNI_FastGetField::generate_fast_get_long_field() { + const char *name = "jni_fast_GetLongField"; + ResourceMark rm; + BufferBlob* b = BufferBlob::create(name, BUFFER_SIZE*wordSize); + address fast_entry = b->instructions_begin(); + CodeBuffer cbuf(fast_entry, b->instructions_size()); + MacroAssembler* masm = new MacroAssembler(&cbuf); + + Label slow; + + // stack layout: offset from rsp (in words): + // old rsi 0 + // return pc 1 + // jni env 2 + // obj 3 + // jfieldID 4 + + ExternalAddress counter(SafepointSynchronize::safepoint_counter_addr()); + + __ pushl (rsi); + __ mov32 (rcx, counter); + __ testb (rcx, 1); + __ jcc (Assembler::notZero, slow); + if (os::is_MP()) { + __ movl (rax, rcx); + __ andl (rax, 1); // rax, must end up 0 + __ movl (rdx, Address(rsp, rax, Address::times_1, 3*wordSize)); + // obj, notice rax, is 0. + // rdx is data dependent on rcx. + } else { + __ movl (rdx, Address(rsp, 3*wordSize)); // obj + } + __ movl (rsi, Address(rsp, 4*wordSize)); // jfieldID + __ movl (rdx, Address(rdx, 0)); // *obj + __ shrl (rsi, 2); // offset + + assert(count < LIST_CAPACITY-1, "LIST_CAPACITY too small"); + speculative_load_pclist[count++] = __ pc(); + __ movl (rax, Address(rdx, rsi, Address::times_1)); + speculative_load_pclist[count] = __ pc(); + __ movl (rdx, Address(rdx, rsi, Address::times_1, 4)); + + if (os::is_MP()) { + __ lea (rsi, counter); + __ xorl (rsi, rdx); + __ xorl (rsi, rax); + __ xorl (rsi, rdx); + __ xorl (rsi, rax); + __ cmp32(rcx, Address(rsi, 0)); + // ca1 is the same as ca because + // rax, ^ rdx ^ counter_addr ^ rax, ^ rdx = address + // ca1 is data dependent on both rax, and rdx. + } else { + __ cmp32(rcx, counter); + } + __ jcc (Assembler::notEqual, slow); + + __ popl (rsi); + +#ifndef _WINDOWS + __ ret (0); +#else + // __stdcall calling convention + __ ret (3*wordSize); +#endif + + slowcase_entry_pclist[count-1] = __ pc(); + slowcase_entry_pclist[count++] = __ pc(); + __ bind (slow); + __ popl (rsi); + address slow_case_addr = jni_GetLongField_addr();; + // tail call + __ jump (ExternalAddress(slow_case_addr)); + + __ flush (); + +#ifndef _WINDOWS + return fast_entry; +#else + jni_fast_GetLongField_fp = (GetLongField_t)fast_entry; + return os::win32::fast_jni_accessor_wrapper(T_LONG); +#endif +} + +address JNI_FastGetField::generate_fast_get_float_field0(BasicType type) { + const char *name; + switch (type) { + case T_FLOAT: name = "jni_fast_GetFloatField"; break; + case T_DOUBLE: name = "jni_fast_GetDoubleField"; break; + default: ShouldNotReachHere(); + } + ResourceMark rm; + BufferBlob* b = BufferBlob::create(name, BUFFER_SIZE*wordSize); + address fast_entry = b->instructions_begin(); + CodeBuffer cbuf(fast_entry, b->instructions_size()); + MacroAssembler* masm = new MacroAssembler(&cbuf); + + Label slow_with_pop, slow; + + // stack layout: offset from rsp (in words): + // return pc 0 + // jni env 1 + // obj 2 + // jfieldID 3 + + ExternalAddress counter(SafepointSynchronize::safepoint_counter_addr()); + + __ mov32 (rcx, counter); + __ testb (rcx, 1); + __ jcc (Assembler::notZero, slow); + if (os::is_MP()) { + __ movl (rax, rcx); + __ andl (rax, 1); // rax, must end up 0 + __ movl (rdx, Address(rsp, rax, Address::times_1, 2*wordSize)); + // obj, notice rax, is 0. + // rdx is data dependent on rcx. + } else { + __ movl (rdx, Address(rsp, 2*wordSize)); // obj + } + __ movl (rax, Address(rsp, 3*wordSize)); // jfieldID + __ movl (rdx, Address(rdx, 0)); // *obj + __ shrl (rax, 2); // offset + + assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); + speculative_load_pclist[count] = __ pc(); + switch (type) { + case T_FLOAT: __ fld_s (Address(rdx, rax, Address::times_1)); break; + case T_DOUBLE: __ fld_d (Address(rdx, rax, Address::times_1)); break; + default: ShouldNotReachHere(); + } + + Address ca1; + if (os::is_MP()) { + __ fst_s (Address(rsp, -4)); + __ lea(rdx, counter); + __ movl (rax, Address(rsp, -4)); + __ xorl(rdx, rax); + __ xorl(rdx, rax); + __ cmp32(rcx, Address(rdx, 0)); + // rax, ^ counter_addr ^ rax, = address + // ca1 is data dependent on the field + // access. + } else { + __ cmp32(rcx, counter); + } + __ jcc (Assembler::notEqual, slow_with_pop); + +#ifndef _WINDOWS + __ ret (0); +#else + // __stdcall calling convention + __ ret (3*wordSize); +#endif + + __ bind (slow_with_pop); + // invalid load. pop FPU stack. + __ fstp_d (0); + + slowcase_entry_pclist[count++] = __ pc(); + __ bind (slow); + address slow_case_addr; + switch (type) { + case T_FLOAT: slow_case_addr = jni_GetFloatField_addr(); break; + case T_DOUBLE: slow_case_addr = jni_GetDoubleField_addr(); break; + default: ShouldNotReachHere(); + } + // tail call + __ jump (ExternalAddress(slow_case_addr)); + + __ flush (); + +#ifndef _WINDOWS + return fast_entry; +#else + switch (type) { + case T_FLOAT: jni_fast_GetFloatField_fp = (GetFloatField_t)fast_entry; break; + case T_DOUBLE: jni_fast_GetDoubleField_fp = (GetDoubleField_t)fast_entry; + } + return os::win32::fast_jni_accessor_wrapper(type); +#endif +} + +address JNI_FastGetField::generate_fast_get_float_field() { + return generate_fast_get_float_field0(T_FLOAT); +} + +address JNI_FastGetField::generate_fast_get_double_field() { + return generate_fast_get_float_field0(T_DOUBLE); +} diff --git a/hotspot/src/cpu/x86/vm/jniFastGetField_x86_64.cpp b/hotspot/src/cpu/x86/vm/jniFastGetField_x86_64.cpp new file mode 100644 index 00000000000..31160b66f56 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/jniFastGetField_x86_64.cpp @@ -0,0 +1,224 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jniFastGetField_x86_64.cpp.incl" + +#define __ masm-> + +#define BUFFER_SIZE 30*wordSize + +// Instead of issuing lfence for LoadLoad barrier, we create data dependency +// between loads, which is more efficient than lfence. + +// Common register usage: +// rax/xmm0: result +// c_rarg0: jni env +// c_rarg1: obj +// c_rarg2: jfield id + +static const Register robj = r9; +static const Register rcounter = r10; +static const Register roffset = r11; +static const Register rcounter_addr = r11; + +// Warning: do not use rip relative addressing after the first counter load +// since that may scratch r10! + +address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { + const char *name; + switch (type) { + case T_BOOLEAN: name = "jni_fast_GetBooleanField"; break; + case T_BYTE: name = "jni_fast_GetByteField"; break; + case T_CHAR: name = "jni_fast_GetCharField"; break; + case T_SHORT: name = "jni_fast_GetShortField"; break; + case T_INT: name = "jni_fast_GetIntField"; break; + case T_LONG: name = "jni_fast_GetLongField"; break; + default: ShouldNotReachHere(); + } + ResourceMark rm; + BufferBlob* b = BufferBlob::create(name, BUFFER_SIZE); + address fast_entry = b->instructions_begin(); + CodeBuffer cbuf(fast_entry, b->instructions_size()); + MacroAssembler* masm = new MacroAssembler(&cbuf); + + Label slow; + + ExternalAddress counter(SafepointSynchronize::safepoint_counter_addr()); + __ mov32 (rcounter, counter); + __ movq (robj, c_rarg1); + __ testb (rcounter, 1); + __ jcc (Assembler::notZero, slow); + if (os::is_MP()) { + __ xorq (robj, rcounter); + __ xorq (robj, rcounter); // obj, since + // robj ^ rcounter ^ rcounter == robj + // robj is data dependent on rcounter. + } + __ movq (robj, Address(robj, 0)); // *obj + __ movq (roffset, c_rarg2); + __ shrq (roffset, 2); // offset + + assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); + speculative_load_pclist[count] = __ pc(); + switch (type) { + case T_BOOLEAN: __ movzbl (rax, Address(robj, roffset, Address::times_1)); break; + case T_BYTE: __ movsbl (rax, Address(robj, roffset, Address::times_1)); break; + case T_CHAR: __ movzwl (rax, Address(robj, roffset, Address::times_1)); break; + case T_SHORT: __ movswl (rax, Address(robj, roffset, Address::times_1)); break; + case T_INT: __ movl (rax, Address(robj, roffset, Address::times_1)); break; + case T_LONG: __ movq (rax, Address(robj, roffset, Address::times_1)); break; + default: ShouldNotReachHere(); + } + + if (os::is_MP()) { + __ lea(rcounter_addr, counter); + // ca is data dependent on rax. + __ xorq (rcounter_addr, rax); + __ xorq (rcounter_addr, rax); + __ cmpl (rcounter, Address(rcounter_addr, 0)); + } else { + __ cmp32 (rcounter, counter); + } + __ jcc (Assembler::notEqual, slow); + + __ ret (0); + + slowcase_entry_pclist[count++] = __ pc(); + __ bind (slow); + address slow_case_addr; + switch (type) { + case T_BOOLEAN: slow_case_addr = jni_GetBooleanField_addr(); break; + case T_BYTE: slow_case_addr = jni_GetByteField_addr(); break; + case T_CHAR: slow_case_addr = jni_GetCharField_addr(); break; + case T_SHORT: slow_case_addr = jni_GetShortField_addr(); break; + case T_INT: slow_case_addr = jni_GetIntField_addr(); break; + case T_LONG: slow_case_addr = jni_GetLongField_addr(); + } + // tail call + __ jump (ExternalAddress(slow_case_addr)); + + __ flush (); + + return fast_entry; +} + +address JNI_FastGetField::generate_fast_get_boolean_field() { + return generate_fast_get_int_field0(T_BOOLEAN); +} + +address JNI_FastGetField::generate_fast_get_byte_field() { + return generate_fast_get_int_field0(T_BYTE); +} + +address JNI_FastGetField::generate_fast_get_char_field() { + return generate_fast_get_int_field0(T_CHAR); +} + +address JNI_FastGetField::generate_fast_get_short_field() { + return generate_fast_get_int_field0(T_SHORT); +} + +address JNI_FastGetField::generate_fast_get_int_field() { + return generate_fast_get_int_field0(T_INT); +} + +address JNI_FastGetField::generate_fast_get_long_field() { + return generate_fast_get_int_field0(T_LONG); +} + +address JNI_FastGetField::generate_fast_get_float_field0(BasicType type) { + const char *name; + switch (type) { + case T_FLOAT: name = "jni_fast_GetFloatField"; break; + case T_DOUBLE: name = "jni_fast_GetDoubleField"; break; + default: ShouldNotReachHere(); + } + ResourceMark rm; + BufferBlob* b = BufferBlob::create(name, BUFFER_SIZE); + address fast_entry = b->instructions_begin(); + CodeBuffer cbuf(fast_entry, b->instructions_size()); + MacroAssembler* masm = new MacroAssembler(&cbuf); + + Label slow; + + ExternalAddress counter(SafepointSynchronize::safepoint_counter_addr()); + __ mov32 (rcounter, counter); + __ movq (robj, c_rarg1); + __ testb (rcounter, 1); + __ jcc (Assembler::notZero, slow); + if (os::is_MP()) { + __ xorq (robj, rcounter); + __ xorq (robj, rcounter); // obj, since + // robj ^ rcounter ^ rcounter == robj + // robj is data dependent on rcounter. + } + __ movq (robj, Address(robj, 0)); // *obj + __ movq (roffset, c_rarg2); + __ shrq (roffset, 2); // offset + + assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); + speculative_load_pclist[count] = __ pc(); + switch (type) { + case T_FLOAT: __ movflt (xmm0, Address(robj, roffset, Address::times_1)); break; + case T_DOUBLE: __ movdbl (xmm0, Address(robj, roffset, Address::times_1)); break; + default: ShouldNotReachHere(); + } + + if (os::is_MP()) { + __ lea(rcounter_addr, counter); + __ movdq (rax, xmm0); + // counter address is data dependent on xmm0. + __ xorq (rcounter_addr, rax); + __ xorq (rcounter_addr, rax); + __ cmpl (rcounter, Address(rcounter_addr, 0)); + } else { + __ cmp32 (rcounter, counter); + } + __ jcc (Assembler::notEqual, slow); + + __ ret (0); + + slowcase_entry_pclist[count++] = __ pc(); + __ bind (slow); + address slow_case_addr; + switch (type) { + case T_FLOAT: slow_case_addr = jni_GetFloatField_addr(); break; + case T_DOUBLE: slow_case_addr = jni_GetDoubleField_addr(); + } + // tail call + __ jump (ExternalAddress(slow_case_addr)); + + __ flush (); + + return fast_entry; +} + +address JNI_FastGetField::generate_fast_get_float_field() { + return generate_fast_get_float_field0(T_FLOAT); +} + +address JNI_FastGetField::generate_fast_get_double_field() { + return generate_fast_get_float_field0(T_DOUBLE); +} diff --git a/hotspot/src/cpu/x86/vm/jniTypes_x86.hpp b/hotspot/src/cpu/x86/vm/jniTypes_x86.hpp new file mode 100644 index 00000000000..349c0688de0 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/jniTypes_x86.hpp @@ -0,0 +1,124 @@ +/* + * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds platform-dependent routines used to write primitive jni +// types to the array of arguments passed into JavaCalls::call + +class JNITypes : AllStatic { + // These functions write a java primitive type (in native format) + // to a java stack slot array to be passed as an argument to JavaCalls:calls. + // I.e., they are functionally 'push' operations if they have a 'pos' + // formal parameter. Note that jlong's and jdouble's are written + // _in reverse_ of the order in which they appear in the interpreter + // stack. This is because call stubs (see stubGenerator_sparc.cpp) + // reverse the argument list constructed by JavaCallArguments (see + // javaCalls.hpp). + +private: + +#ifndef AMD64 + // 32bit Helper routines. + static inline void put_int2r(jint *from, intptr_t *to) { *(jint *)(to++) = from[1]; + *(jint *)(to ) = from[0]; } + static inline void put_int2r(jint *from, intptr_t *to, int& pos) { put_int2r(from, to + pos); pos += 2; } +#endif // AMD64 + +public: + // Ints are stored in native format in one JavaCallArgument slot at *to. + static inline void put_int(jint from, intptr_t *to) { *(jint *)(to + 0 ) = from; } + static inline void put_int(jint from, intptr_t *to, int& pos) { *(jint *)(to + pos++) = from; } + static inline void put_int(jint *from, intptr_t *to, int& pos) { *(jint *)(to + pos++) = *from; } + +#ifdef AMD64 + // Longs are stored in native format in one JavaCallArgument slot at + // *(to+1). + static inline void put_long(jlong from, intptr_t *to) { + *(jlong*) (to + 1) = from; + } + + static inline void put_long(jlong from, intptr_t *to, int& pos) { + *(jlong*) (to + 1 + pos) = from; + pos += 2; + } + + static inline void put_long(jlong *from, intptr_t *to, int& pos) { + *(jlong*) (to + 1 + pos) = *from; + pos += 2; + } +#else + // Longs are stored in big-endian word format in two JavaCallArgument slots at *to. + // The high half is in *to and the low half in *(to+1). + static inline void put_long(jlong from, intptr_t *to) { put_int2r((jint *)&from, to); } + static inline void put_long(jlong from, intptr_t *to, int& pos) { put_int2r((jint *)&from, to, pos); } + static inline void put_long(jlong *from, intptr_t *to, int& pos) { put_int2r((jint *) from, to, pos); } +#endif // AMD64 + + // Oops are stored in native format in one JavaCallArgument slot at *to. + static inline void put_obj(oop from, intptr_t *to) { *(oop *)(to + 0 ) = from; } + static inline void put_obj(oop from, intptr_t *to, int& pos) { *(oop *)(to + pos++) = from; } + static inline void put_obj(oop *from, intptr_t *to, int& pos) { *(oop *)(to + pos++) = *from; } + + // Floats are stored in native format in one JavaCallArgument slot at *to. + static inline void put_float(jfloat from, intptr_t *to) { *(jfloat *)(to + 0 ) = from; } + static inline void put_float(jfloat from, intptr_t *to, int& pos) { *(jfloat *)(to + pos++) = from; } + static inline void put_float(jfloat *from, intptr_t *to, int& pos) { *(jfloat *)(to + pos++) = *from; } + +#undef _JNI_SLOT_OFFSET +#ifdef AMD64 +#define _JNI_SLOT_OFFSET 1 + // Doubles are stored in native word format in one JavaCallArgument + // slot at *(to+1). + static inline void put_double(jdouble from, intptr_t *to) { + *(jdouble*) (to + 1) = from; + } + + static inline void put_double(jdouble from, intptr_t *to, int& pos) { + *(jdouble*) (to + 1 + pos) = from; + pos += 2; + } + + static inline void put_double(jdouble *from, intptr_t *to, int& pos) { + *(jdouble*) (to + 1 + pos) = *from; + pos += 2; + } +#else +#define _JNI_SLOT_OFFSET 0 + // Doubles are stored in big-endian word format in two JavaCallArgument slots at *to. + // The high half is in *to and the low half in *(to+1). + static inline void put_double(jdouble from, intptr_t *to) { put_int2r((jint *)&from, to); } + static inline void put_double(jdouble from, intptr_t *to, int& pos) { put_int2r((jint *)&from, to, pos); } + static inline void put_double(jdouble *from, intptr_t *to, int& pos) { put_int2r((jint *) from, to, pos); } +#endif // AMD64 + + + // The get_xxx routines, on the other hand, actually _do_ fetch + // java primitive types from the interpreter stack. + // No need to worry about alignment on Intel. + static inline jint get_int (intptr_t *from) { return *(jint *) from; } + static inline jlong get_long (intptr_t *from) { return *(jlong *) (from + _JNI_SLOT_OFFSET); } + static inline oop get_obj (intptr_t *from) { return *(oop *) from; } + static inline jfloat get_float (intptr_t *from) { return *(jfloat *) from; } + static inline jdouble get_double(intptr_t *from) { return *(jdouble *)(from + _JNI_SLOT_OFFSET); } +#undef _JNI_SLOT_OFFSET +}; diff --git a/hotspot/src/cpu/x86/vm/jni_x86.h b/hotspot/src/cpu/x86/vm/jni_x86.h new file mode 100644 index 00000000000..89f95e35749 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/jni_x86.h @@ -0,0 +1,47 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#ifndef _JAVASOFT_JNI_MD_H_ +#define _JAVASOFT_JNI_MD_H_ + +#if defined(SOLARIS) || defined(LINUX) + #define JNIEXPORT + #define JNIIMPORT + #define JNICALL + + typedef int jint; + typedef long long jlong; +#else + #define JNIEXPORT __declspec(dllexport) + #define JNIIMPORT __declspec(dllimport) + #define JNICALL __stdcall + + typedef int jint; + typedef __int64 jlong; +#endif + +typedef signed char jbyte; + +#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/hotspot/src/cpu/x86/vm/nativeInst_x86.cpp b/hotspot/src/cpu/x86/vm/nativeInst_x86.cpp new file mode 100644 index 00000000000..a501aef8fa2 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/nativeInst_x86.cpp @@ -0,0 +1,474 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_nativeInst_x86.cpp.incl" + +void NativeInstruction::wrote(int offset) { + ICache::invalidate_word(addr_at(offset)); +} + + +void NativeCall::verify() { + // Make sure code pattern is actually a call imm32 instruction. + int inst = ubyte_at(0); + if (inst != instruction_code) { + tty->print_cr("Addr: " INTPTR_FORMAT " Code: 0x%x", instruction_address(), + inst); + fatal("not a call disp32"); + } +} + +address NativeCall::destination() const { + // Getting the destination of a call isn't safe because that call can + // be getting patched while you're calling this. There's only special + // places where this can be called but not automatically verifiable by + // checking which locks are held. The solution is true atomic patching + // on x86, nyi. + return return_address() + displacement(); +} + +void NativeCall::print() { + tty->print_cr(PTR_FORMAT ": call " PTR_FORMAT, + instruction_address(), destination()); +} + +// Inserts a native call instruction at a given pc +void NativeCall::insert(address code_pos, address entry) { + intptr_t disp = (intptr_t)entry - ((intptr_t)code_pos + 1 + 4); +#ifdef AMD64 + guarantee(disp == (intptr_t)(jint)disp, "must be 32-bit offset"); +#endif // AMD64 + *code_pos = instruction_code; + *((int32_t *)(code_pos+1)) = (int32_t) disp; + ICache::invalidate_range(code_pos, instruction_size); +} + +// MT-safe patching of a call instruction. +// First patches first word of instruction to two jmp's that jmps to them +// selfs (spinlock). Then patches the last byte, and then atomicly replaces +// the jmp's with the first 4 byte of the new instruction. +void NativeCall::replace_mt_safe(address instr_addr, address code_buffer) { + assert(Patching_lock->is_locked() || + SafepointSynchronize::is_at_safepoint(), "concurrent code patching"); + assert (instr_addr != NULL, "illegal address for code patching"); + + NativeCall* n_call = nativeCall_at (instr_addr); // checking that it is a call + if (os::is_MP()) { + guarantee((intptr_t)instr_addr % BytesPerWord == 0, "must be aligned"); + } + + // First patch dummy jmp in place + unsigned char patch[4]; + assert(sizeof(patch)==sizeof(jint), "sanity check"); + patch[0] = 0xEB; // jmp rel8 + patch[1] = 0xFE; // jmp to self + patch[2] = 0xEB; + patch[3] = 0xFE; + + // First patch dummy jmp in place + *(jint*)instr_addr = *(jint *)patch; + + // Invalidate. Opteron requires a flush after every write. + n_call->wrote(0); + + // Patch 4th byte + instr_addr[4] = code_buffer[4]; + + n_call->wrote(4); + + // Patch bytes 0-3 + *(jint*)instr_addr = *(jint *)code_buffer; + + n_call->wrote(0); + +#ifdef ASSERT + // verify patching + for ( int i = 0; i < instruction_size; i++) { + address ptr = (address)((intptr_t)code_buffer + i); + int a_byte = (*ptr) & 0xFF; + assert(*((address)((intptr_t)instr_addr + i)) == a_byte, "mt safe patching failed"); + } +#endif + +} + + +// Similar to replace_mt_safe, but just changes the destination. The +// important thing is that free-running threads are able to execute this +// call instruction at all times. If the displacement field is aligned +// we can simply rely on atomicity of 32-bit writes to make sure other threads +// will see no intermediate states. Otherwise, the first two bytes of the +// call are guaranteed to be aligned, and can be atomically patched to a +// self-loop to guard the instruction while we change the other bytes. + +// We cannot rely on locks here, since the free-running threads must run at +// full speed. +// +// Used in the runtime linkage of calls; see class CompiledIC. +// (Cf. 4506997 and 4479829, where threads witnessed garbage displacements.) +void NativeCall::set_destination_mt_safe(address dest) { + debug_only(verify()); + // Make sure patching code is locked. No two threads can patch at the same + // time but one may be executing this code. + assert(Patching_lock->is_locked() || + SafepointSynchronize::is_at_safepoint(), "concurrent code patching"); + // Both C1 and C2 should now be generating code which aligns the patched address + // to be within a single cache line except that C1 does not do the alignment on + // uniprocessor systems. + bool is_aligned = ((uintptr_t)displacement_address() + 0) / cache_line_size == + ((uintptr_t)displacement_address() + 3) / cache_line_size; + + guarantee(!os::is_MP() || is_aligned, "destination must be aligned"); + + if (is_aligned) { + // Simple case: The destination lies within a single cache line. + set_destination(dest); + } else if ((uintptr_t)instruction_address() / cache_line_size == + ((uintptr_t)instruction_address()+1) / cache_line_size) { + // Tricky case: The instruction prefix lies within a single cache line. + intptr_t disp = dest - return_address(); +#ifdef AMD64 + guarantee(disp == (intptr_t)(jint)disp, "must be 32-bit offset"); +#endif // AMD64 + + int call_opcode = instruction_address()[0]; + + // First patch dummy jump in place: + { + u_char patch_jump[2]; + patch_jump[0] = 0xEB; // jmp rel8 + patch_jump[1] = 0xFE; // jmp to self + + assert(sizeof(patch_jump)==sizeof(short), "sanity check"); + *(short*)instruction_address() = *(short*)patch_jump; + } + // Invalidate. Opteron requires a flush after every write. + wrote(0); + + // (Note: We assume any reader which has already started to read + // the unpatched call will completely read the whole unpatched call + // without seeing the next writes we are about to make.) + + // Next, patch the last three bytes: + u_char patch_disp[5]; + patch_disp[0] = call_opcode; + *(int32_t*)&patch_disp[1] = (int32_t)disp; + assert(sizeof(patch_disp)==instruction_size, "sanity check"); + for (int i = sizeof(short); i < instruction_size; i++) + instruction_address()[i] = patch_disp[i]; + + // Invalidate. Opteron requires a flush after every write. + wrote(sizeof(short)); + + // (Note: We assume that any reader which reads the opcode we are + // about to repatch will also read the writes we just made.) + + // Finally, overwrite the jump: + *(short*)instruction_address() = *(short*)patch_disp; + // Invalidate. Opteron requires a flush after every write. + wrote(0); + + debug_only(verify()); + guarantee(destination() == dest, "patch succeeded"); + } else { + // Impossible: One or the other must be atomically writable. + ShouldNotReachHere(); + } +} + + +void NativeMovConstReg::verify() { +#ifdef AMD64 + // make sure code pattern is actually a mov reg64, imm64 instruction + if ((ubyte_at(0) != Assembler::REX_W && ubyte_at(0) != Assembler::REX_WB) || + (ubyte_at(1) & (0xff ^ register_mask)) != 0xB8) { + print(); + fatal("not a REX.W[B] mov reg64, imm64"); + } +#else + // make sure code pattern is actually a mov reg, imm32 instruction + u_char test_byte = *(u_char*)instruction_address(); + u_char test_byte_2 = test_byte & ( 0xff ^ register_mask); + if (test_byte_2 != instruction_code) fatal("not a mov reg, imm32"); +#endif // AMD64 +} + + +void NativeMovConstReg::print() { + tty->print_cr(PTR_FORMAT ": mov reg, " INTPTR_FORMAT, + instruction_address(), data()); +} + +//------------------------------------------------------------------- + +#ifndef AMD64 + +void NativeMovRegMem::copy_instruction_to(address new_instruction_address) { + int inst_size = instruction_size; + + // See if there's an instruction size prefix override. + if ( *(address(this)) == instruction_operandsize_prefix && + *(address(this)+1) != instruction_code_xmm_code ) { // Not SSE instr + inst_size += 1; + } + if ( *(address(this)) == instruction_extended_prefix ) inst_size += 1; + + for (int i = 0; i < instruction_size; i++) { + *(new_instruction_address + i) = *(address(this) + i); + } +} + +void NativeMovRegMem::verify() { + // make sure code pattern is actually a mov [reg+offset], reg instruction + u_char test_byte = *(u_char*)instruction_address(); + if ( ! ( (test_byte == instruction_code_reg2memb) + || (test_byte == instruction_code_mem2regb) + || (test_byte == instruction_code_mem2regl) + || (test_byte == instruction_code_reg2meml) + || (test_byte == instruction_code_mem2reg_movzxb ) + || (test_byte == instruction_code_mem2reg_movzxw ) + || (test_byte == instruction_code_mem2reg_movsxb ) + || (test_byte == instruction_code_mem2reg_movsxw ) + || (test_byte == instruction_code_float_s) + || (test_byte == instruction_code_float_d) + || (test_byte == instruction_code_long_volatile) ) ) + { + u_char byte1 = ((u_char*)instruction_address())[1]; + u_char byte2 = ((u_char*)instruction_address())[2]; + if ((test_byte != instruction_code_xmm_ss_prefix && + test_byte != instruction_code_xmm_sd_prefix && + test_byte != instruction_operandsize_prefix) || + byte1 != instruction_code_xmm_code || + (byte2 != instruction_code_xmm_load && + byte2 != instruction_code_xmm_lpd && + byte2 != instruction_code_xmm_store)) { + fatal ("not a mov [reg+offs], reg instruction"); + } + } +} + + +void NativeMovRegMem::print() { + tty->print_cr("0x%x: mov reg, [reg + %x]", instruction_address(), offset()); +} + +//------------------------------------------------------------------- + +void NativeLoadAddress::verify() { + // make sure code pattern is actually a mov [reg+offset], reg instruction + u_char test_byte = *(u_char*)instruction_address(); + if ( ! (test_byte == instruction_code) ) { + fatal ("not a lea reg, [reg+offs] instruction"); + } +} + + +void NativeLoadAddress::print() { + tty->print_cr("0x%x: lea [reg + %x], reg", instruction_address(), offset()); +} + +#endif // !AMD64 + +//-------------------------------------------------------------------------------- + +void NativeJump::verify() { + if (*(u_char*)instruction_address() != instruction_code) { + fatal("not a jump instruction"); + } +} + + +void NativeJump::insert(address code_pos, address entry) { + intptr_t disp = (intptr_t)entry - ((intptr_t)code_pos + 1 + 4); +#ifdef AMD64 + guarantee(disp == (intptr_t)(int32_t)disp, "must be 32-bit offset"); +#endif // AMD64 + + *code_pos = instruction_code; + *((int32_t*)(code_pos + 1)) = (int32_t)disp; + + ICache::invalidate_range(code_pos, instruction_size); +} + +void NativeJump::check_verified_entry_alignment(address entry, address verified_entry) { + // Patching to not_entrant can happen while activations of the method are + // in use. The patching in that instance must happen only when certain + // alignment restrictions are true. These guarantees check those + // conditions. +#ifdef AMD64 + const int linesize = 64; +#else + const int linesize = 32; +#endif // AMD64 + + // Must be wordSize aligned + guarantee(((uintptr_t) verified_entry & (wordSize -1)) == 0, + "illegal address for code patching 2"); + // First 5 bytes must be within the same cache line - 4827828 + guarantee((uintptr_t) verified_entry / linesize == + ((uintptr_t) verified_entry + 4) / linesize, + "illegal address for code patching 3"); +} + + +// MT safe inserting of a jump over an unknown instruction sequence (used by nmethod::makeZombie) +// The problem: jmp is a 5-byte instruction. Atomical write can be only with 4 bytes. +// First patches the first word atomically to be a jump to itself. +// Then patches the last byte and then atomically patches the first word (4-bytes), +// thus inserting the desired jump +// This code is mt-safe with the following conditions: entry point is 4 byte aligned, +// entry point is in same cache line as unverified entry point, and the instruction being +// patched is >= 5 byte (size of patch). +// +// In C2 the 5+ byte sized instruction is enforced by code in MachPrologNode::emit. +// In C1 the restriction is enforced by CodeEmitter::method_entry +// +void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) { + // complete jump instruction (to be inserted) is in code_buffer; + unsigned char code_buffer[5]; + code_buffer[0] = instruction_code; + intptr_t disp = (intptr_t)dest - ((intptr_t)verified_entry + 1 + 4); +#ifdef AMD64 + guarantee(disp == (intptr_t)(int32_t)disp, "must be 32-bit offset"); +#endif // AMD64 + *(int32_t*)(code_buffer + 1) = (int32_t)disp; + + check_verified_entry_alignment(entry, verified_entry); + + // Can't call nativeJump_at() because it's asserts jump exists + NativeJump* n_jump = (NativeJump*) verified_entry; + + //First patch dummy jmp in place + + unsigned char patch[4]; + assert(sizeof(patch)==sizeof(int32_t), "sanity check"); + patch[0] = 0xEB; // jmp rel8 + patch[1] = 0xFE; // jmp to self + patch[2] = 0xEB; + patch[3] = 0xFE; + + // First patch dummy jmp in place + *(int32_t*)verified_entry = *(int32_t *)patch; + + n_jump->wrote(0); + + // Patch 5th byte (from jump instruction) + verified_entry[4] = code_buffer[4]; + + n_jump->wrote(4); + + // Patch bytes 0-3 (from jump instruction) + *(int32_t*)verified_entry = *(int32_t *)code_buffer; + // Invalidate. Opteron requires a flush after every write. + n_jump->wrote(0); + +} + +void NativePopReg::insert(address code_pos, Register reg) { + assert(reg->encoding() < 8, "no space for REX"); + assert(NativePopReg::instruction_size == sizeof(char), "right address unit for update"); + *code_pos = (u_char)(instruction_code | reg->encoding()); + ICache::invalidate_range(code_pos, instruction_size); +} + + +void NativeIllegalInstruction::insert(address code_pos) { + assert(NativeIllegalInstruction::instruction_size == sizeof(short), "right address unit for update"); + *(short *)code_pos = instruction_code; + ICache::invalidate_range(code_pos, instruction_size); +} + +void NativeGeneralJump::verify() { + assert(((NativeInstruction *)this)->is_jump() || + ((NativeInstruction *)this)->is_cond_jump(), "not a general jump instruction"); +} + + +void NativeGeneralJump::insert_unconditional(address code_pos, address entry) { + intptr_t disp = (intptr_t)entry - ((intptr_t)code_pos + 1 + 4); +#ifdef AMD64 + guarantee(disp == (intptr_t)(int32_t)disp, "must be 32-bit offset"); +#endif // AMD64 + + *code_pos = unconditional_long_jump; + *((int32_t *)(code_pos+1)) = (int32_t) disp; + ICache::invalidate_range(code_pos, instruction_size); +} + + +// MT-safe patching of a long jump instruction. +// First patches first word of instruction to two jmp's that jmps to them +// selfs (spinlock). Then patches the last byte, and then atomicly replaces +// the jmp's with the first 4 byte of the new instruction. +void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) { + assert (instr_addr != NULL, "illegal address for code patching (4)"); + NativeGeneralJump* n_jump = nativeGeneralJump_at (instr_addr); // checking that it is a jump + + // Temporary code + unsigned char patch[4]; + assert(sizeof(patch)==sizeof(int32_t), "sanity check"); + patch[0] = 0xEB; // jmp rel8 + patch[1] = 0xFE; // jmp to self + patch[2] = 0xEB; + patch[3] = 0xFE; + + // First patch dummy jmp in place + *(int32_t*)instr_addr = *(int32_t *)patch; + n_jump->wrote(0); + + // Patch 4th byte + instr_addr[4] = code_buffer[4]; + + n_jump->wrote(4); + + // Patch bytes 0-3 + *(jint*)instr_addr = *(jint *)code_buffer; + + n_jump->wrote(0); + +#ifdef ASSERT + // verify patching + for ( int i = 0; i < instruction_size; i++) { + address ptr = (address)((intptr_t)code_buffer + i); + int a_byte = (*ptr) & 0xFF; + assert(*((address)((intptr_t)instr_addr + i)) == a_byte, "mt safe patching failed"); + } +#endif + +} + + + +address NativeGeneralJump::jump_destination() const { + int op_code = ubyte_at(0); + bool is_rel32off = (op_code == 0xE9 || op_code == 0x0F); + int offset = (op_code == 0x0F) ? 2 : 1; + int length = offset + ((is_rel32off) ? 4 : 1); + + if (is_rel32off) + return addr_at(0) + length + int_at(offset); + else + return addr_at(0) + length + sbyte_at(offset); +} diff --git a/hotspot/src/cpu/x86/vm/nativeInst_x86.hpp b/hotspot/src/cpu/x86/vm/nativeInst_x86.hpp new file mode 100644 index 00000000000..12b7b0f5486 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/nativeInst_x86.hpp @@ -0,0 +1,588 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// We have interfaces for the following instructions: +// - NativeInstruction +// - - NativeCall +// - - NativeMovConstReg +// - - NativeMovConstRegPatching +// - - NativeMovRegMem +// - - NativeMovRegMemPatching +// - - NativeJump +// - - NativeIllegalOpCode +// - - NativeGeneralJump +// - - NativeReturn +// - - NativeReturnX (return with argument) +// - - NativePushConst +// - - NativeTstRegMem + +// The base class for different kinds of native instruction abstractions. +// Provides the primitive operations to manipulate code relative to this. + +class NativeInstruction VALUE_OBJ_CLASS_SPEC { + friend class Relocation; + + public: + enum Intel_specific_constants { + nop_instruction_code = 0x90, + nop_instruction_size = 1 + }; + + bool is_nop() { return ubyte_at(0) == nop_instruction_code; } + inline bool is_call(); + inline bool is_illegal(); + inline bool is_return(); + inline bool is_jump(); + inline bool is_cond_jump(); + inline bool is_safepoint_poll(); + inline bool is_mov_literal64(); + + protected: + address addr_at(int offset) const { return address(this) + offset; } + + s_char sbyte_at(int offset) const { return *(s_char*) addr_at(offset); } + u_char ubyte_at(int offset) const { return *(u_char*) addr_at(offset); } + + jint int_at(int offset) const { return *(jint*) addr_at(offset); } + + intptr_t ptr_at(int offset) const { return *(intptr_t*) addr_at(offset); } + + oop oop_at (int offset) const { return *(oop*) addr_at(offset); } + + + void set_char_at(int offset, char c) { *addr_at(offset) = (u_char)c; wrote(offset); } + void set_int_at(int offset, jint i) { *(jint*)addr_at(offset) = i; wrote(offset); } + void set_ptr_at (int offset, intptr_t ptr) { *(intptr_t*) addr_at(offset) = ptr; wrote(offset); } + void set_oop_at (int offset, oop o) { *(oop*) addr_at(offset) = o; wrote(offset); } + + // This doesn't really do anything on Intel, but it is the place where + // cache invalidation belongs, generically: + void wrote(int offset); + + public: + + // unit test stuff + static void test() {} // override for testing + + inline friend NativeInstruction* nativeInstruction_at(address address); +}; + +inline NativeInstruction* nativeInstruction_at(address address) { + NativeInstruction* inst = (NativeInstruction*)address; +#ifdef ASSERT + //inst->verify(); +#endif + return inst; +} + +inline NativeCall* nativeCall_at(address address); +// The NativeCall is an abstraction for accessing/manipulating native call imm32/rel32off +// instructions (used to manipulate inline caches, primitive & dll calls, etc.). + +class NativeCall: public NativeInstruction { + public: + enum Intel_specific_constants { + instruction_code = 0xE8, + instruction_size = 5, + instruction_offset = 0, + displacement_offset = 1, + return_address_offset = 5 + }; + + enum { cache_line_size = BytesPerWord }; // conservative estimate! + + address instruction_address() const { return addr_at(instruction_offset); } + address next_instruction_address() const { return addr_at(return_address_offset); } + int displacement() const { return (jint) int_at(displacement_offset); } + address displacement_address() const { return addr_at(displacement_offset); } + address return_address() const { return addr_at(return_address_offset); } + address destination() const; + void set_destination(address dest) { +#ifdef AMD64 + assert((labs((intptr_t) dest - (intptr_t) return_address()) & + 0xFFFFFFFF00000000) == 0, + "must be 32bit offset"); +#endif // AMD64 + set_int_at(displacement_offset, dest - return_address()); + } + void set_destination_mt_safe(address dest); + + void verify_alignment() { assert((intptr_t)addr_at(displacement_offset) % BytesPerInt == 0, "must be aligned"); } + void verify(); + void print(); + + // Creation + inline friend NativeCall* nativeCall_at(address address); + inline friend NativeCall* nativeCall_before(address return_address); + + static bool is_call_at(address instr) { + return ((*instr) & 0xFF) == NativeCall::instruction_code; + } + + static bool is_call_before(address return_address) { + return is_call_at(return_address - NativeCall::return_address_offset); + } + + static bool is_call_to(address instr, address target) { + return nativeInstruction_at(instr)->is_call() && + nativeCall_at(instr)->destination() == target; + } + + // MT-safe patching of a call instruction. + static void insert(address code_pos, address entry); + + static void replace_mt_safe(address instr_addr, address code_buffer); +}; + +inline NativeCall* nativeCall_at(address address) { + NativeCall* call = (NativeCall*)(address - NativeCall::instruction_offset); +#ifdef ASSERT + call->verify(); +#endif + return call; +} + +inline NativeCall* nativeCall_before(address return_address) { + NativeCall* call = (NativeCall*)(return_address - NativeCall::return_address_offset); +#ifdef ASSERT + call->verify(); +#endif + return call; +} + +// An interface for accessing/manipulating native mov reg, imm32 instructions. +// (used to manipulate inlined 32bit data dll calls, etc.) +class NativeMovConstReg: public NativeInstruction { +#ifdef AMD64 + static const bool has_rex = true; + static const int rex_size = 1; +#else + static const bool has_rex = false; + static const int rex_size = 0; +#endif // AMD64 + public: + enum Intel_specific_constants { + instruction_code = 0xB8, + instruction_size = 1 + rex_size + wordSize, + instruction_offset = 0, + data_offset = 1 + rex_size, + next_instruction_offset = instruction_size, + register_mask = 0x07 + }; + + address instruction_address() const { return addr_at(instruction_offset); } + address next_instruction_address() const { return addr_at(next_instruction_offset); } + intptr_t data() const { return ptr_at(data_offset); } + void set_data(intptr_t x) { set_ptr_at(data_offset, x); } + + void verify(); + void print(); + + // unit test stuff + static void test() {} + + // Creation + inline friend NativeMovConstReg* nativeMovConstReg_at(address address); + inline friend NativeMovConstReg* nativeMovConstReg_before(address address); +}; + +inline NativeMovConstReg* nativeMovConstReg_at(address address) { + NativeMovConstReg* test = (NativeMovConstReg*)(address - NativeMovConstReg::instruction_offset); +#ifdef ASSERT + test->verify(); +#endif + return test; +} + +inline NativeMovConstReg* nativeMovConstReg_before(address address) { + NativeMovConstReg* test = (NativeMovConstReg*)(address - NativeMovConstReg::instruction_size - NativeMovConstReg::instruction_offset); +#ifdef ASSERT + test->verify(); +#endif + return test; +} + +class NativeMovConstRegPatching: public NativeMovConstReg { + private: + friend NativeMovConstRegPatching* nativeMovConstRegPatching_at(address address) { + NativeMovConstRegPatching* test = (NativeMovConstRegPatching*)(address - instruction_offset); + #ifdef ASSERT + test->verify(); + #endif + return test; + } +}; + +#ifndef AMD64 + +// An interface for accessing/manipulating native moves of the form: +// mov[b/w/l] [reg + offset], reg (instruction_code_reg2mem) +// mov[b/w/l] reg, [reg+offset] (instruction_code_mem2reg +// mov[s/z]x[w/b] [reg + offset], reg +// fld_s [reg+offset] +// fld_d [reg+offset] +// fstp_s [reg + offset] +// fstp_d [reg + offset] +// +// Warning: These routines must be able to handle any instruction sequences +// that are generated as a result of the load/store byte,word,long +// macros. For example: The load_unsigned_byte instruction generates +// an xor reg,reg inst prior to generating the movb instruction. This +// class must skip the xor instruction. + +class NativeMovRegMem: public NativeInstruction { + public: + enum Intel_specific_constants { + instruction_code_xor = 0x33, + instruction_extended_prefix = 0x0F, + instruction_code_mem2reg_movzxb = 0xB6, + instruction_code_mem2reg_movsxb = 0xBE, + instruction_code_mem2reg_movzxw = 0xB7, + instruction_code_mem2reg_movsxw = 0xBF, + instruction_operandsize_prefix = 0x66, + instruction_code_reg2meml = 0x89, + instruction_code_mem2regl = 0x8b, + instruction_code_reg2memb = 0x88, + instruction_code_mem2regb = 0x8a, + instruction_code_float_s = 0xd9, + instruction_code_float_d = 0xdd, + instruction_code_long_volatile = 0xdf, + instruction_code_xmm_ss_prefix = 0xf3, + instruction_code_xmm_sd_prefix = 0xf2, + instruction_code_xmm_code = 0x0f, + instruction_code_xmm_load = 0x10, + instruction_code_xmm_store = 0x11, + instruction_code_xmm_lpd = 0x12, + + instruction_size = 4, + instruction_offset = 0, + data_offset = 2, + next_instruction_offset = 4 + }; + + address instruction_address() const { + if (*addr_at(instruction_offset) == instruction_operandsize_prefix && + *addr_at(instruction_offset+1) != instruction_code_xmm_code) { + return addr_at(instruction_offset+1); // Not SSE instructions + } + else if (*addr_at(instruction_offset) == instruction_extended_prefix) { + return addr_at(instruction_offset+1); + } + else if (*addr_at(instruction_offset) == instruction_code_xor) { + return addr_at(instruction_offset+2); + } + else return addr_at(instruction_offset); + } + + address next_instruction_address() const { + switch (*addr_at(instruction_offset)) { + case instruction_operandsize_prefix: + if (*addr_at(instruction_offset+1) == instruction_code_xmm_code) + return instruction_address() + instruction_size; // SSE instructions + case instruction_extended_prefix: + return instruction_address() + instruction_size + 1; + case instruction_code_reg2meml: + case instruction_code_mem2regl: + case instruction_code_reg2memb: + case instruction_code_mem2regb: + case instruction_code_xor: + return instruction_address() + instruction_size + 2; + default: + return instruction_address() + instruction_size; + } + } + int offset() const{ + if (*addr_at(instruction_offset) == instruction_operandsize_prefix && + *addr_at(instruction_offset+1) != instruction_code_xmm_code) { + return int_at(data_offset+1); // Not SSE instructions + } + else if (*addr_at(instruction_offset) == instruction_extended_prefix) { + return int_at(data_offset+1); + } + else if (*addr_at(instruction_offset) == instruction_code_xor || + *addr_at(instruction_offset) == instruction_code_xmm_ss_prefix || + *addr_at(instruction_offset) == instruction_code_xmm_sd_prefix || + *addr_at(instruction_offset) == instruction_operandsize_prefix) { + return int_at(data_offset+2); + } + else return int_at(data_offset); + } + + void set_offset(int x) { + if (*addr_at(instruction_offset) == instruction_operandsize_prefix && + *addr_at(instruction_offset+1) != instruction_code_xmm_code) { + set_int_at(data_offset+1, x); // Not SSE instructions + } + else if (*addr_at(instruction_offset) == instruction_extended_prefix) { + set_int_at(data_offset+1, x); + } + else if (*addr_at(instruction_offset) == instruction_code_xor || + *addr_at(instruction_offset) == instruction_code_xmm_ss_prefix || + *addr_at(instruction_offset) == instruction_code_xmm_sd_prefix || + *addr_at(instruction_offset) == instruction_operandsize_prefix) { + set_int_at(data_offset+2, x); + } + else set_int_at(data_offset, x); + } + + void add_offset_in_bytes(int add_offset) { set_offset ( ( offset() + add_offset ) ); } + void copy_instruction_to(address new_instruction_address); + + void verify(); + void print (); + + // unit test stuff + static void test() {} + + private: + inline friend NativeMovRegMem* nativeMovRegMem_at (address address); +}; + +inline NativeMovRegMem* nativeMovRegMem_at (address address) { + NativeMovRegMem* test = (NativeMovRegMem*)(address - NativeMovRegMem::instruction_offset); +#ifdef ASSERT + test->verify(); +#endif + return test; +} + +class NativeMovRegMemPatching: public NativeMovRegMem { + private: + friend NativeMovRegMemPatching* nativeMovRegMemPatching_at (address address) { + NativeMovRegMemPatching* test = (NativeMovRegMemPatching*)(address - instruction_offset); + #ifdef ASSERT + test->verify(); + #endif + return test; + } +}; + + + +// An interface for accessing/manipulating native leal instruction of form: +// leal reg, [reg + offset] + +class NativeLoadAddress: public NativeMovRegMem { + public: + enum Intel_specific_constants { + instruction_code = 0x8D + }; + + void verify(); + void print (); + + // unit test stuff + static void test() {} + + private: + friend NativeLoadAddress* nativeLoadAddress_at (address address) { + NativeLoadAddress* test = (NativeLoadAddress*)(address - instruction_offset); + #ifdef ASSERT + test->verify(); + #endif + return test; + } +}; + +#endif // AMD64 + +// jump rel32off + +class NativeJump: public NativeInstruction { + public: + enum Intel_specific_constants { + instruction_code = 0xe9, + instruction_size = 5, + instruction_offset = 0, + data_offset = 1, + next_instruction_offset = 5 + }; + + address instruction_address() const { return addr_at(instruction_offset); } + address next_instruction_address() const { return addr_at(next_instruction_offset); } + address jump_destination() const { + address dest = (int_at(data_offset)+next_instruction_address()); +#ifdef AMD64 // What is this about? + // return -1 if jump to self + dest = (dest == (address) this) ? (address) -1 : dest; +#endif // AMD64 + return dest; + } + + void set_jump_destination(address dest) { + intptr_t val = dest - next_instruction_address(); +#ifdef AMD64 + if (dest == (address) -1) { // can't encode jump to -1 + val = -5; // jump to self + } else { + assert((labs(val) & 0xFFFFFFFF00000000) == 0, + "must be 32bit offset"); + } +#endif // AMD64 + set_int_at(data_offset, (jint)val); + } + + // Creation + inline friend NativeJump* nativeJump_at(address address); + + void verify(); + + // Unit testing stuff + static void test() {} + + // Insertion of native jump instruction + static void insert(address code_pos, address entry); + // MT-safe insertion of native jump at verified method entry + static void check_verified_entry_alignment(address entry, address verified_entry); + static void patch_verified_entry(address entry, address verified_entry, address dest); +}; + +inline NativeJump* nativeJump_at(address address) { + NativeJump* jump = (NativeJump*)(address - NativeJump::instruction_offset); +#ifdef ASSERT + jump->verify(); +#endif + return jump; +} + +// Handles all kinds of jump on Intel. Long/far, conditional/unconditional +class NativeGeneralJump: public NativeInstruction { + public: + enum Intel_specific_constants { + // Constants does not apply, since the lengths and offsets depends on the actual jump + // used + // Instruction codes: + // Unconditional jumps: 0xE9 (rel32off), 0xEB (rel8off) + // Conditional jumps: 0x0F8x (rel32off), 0x7x (rel8off) + unconditional_long_jump = 0xe9, + unconditional_short_jump = 0xeb, + instruction_size = 5 + }; + + address instruction_address() const { return addr_at(0); } + address jump_destination() const; + + // Creation + inline friend NativeGeneralJump* nativeGeneralJump_at(address address); + + // Insertion of native general jump instruction + static void insert_unconditional(address code_pos, address entry); + static void replace_mt_safe(address instr_addr, address code_buffer); + + void verify(); +}; + +inline NativeGeneralJump* nativeGeneralJump_at(address address) { + NativeGeneralJump* jump = (NativeGeneralJump*)(address); + debug_only(jump->verify();) + return jump; +} + +class NativePopReg : public NativeInstruction { + public: + enum Intel_specific_constants { + instruction_code = 0x58, + instruction_size = 1, + instruction_offset = 0, + data_offset = 1, + next_instruction_offset = 1 + }; + + // Insert a pop instruction + static void insert(address code_pos, Register reg); +}; + + +class NativeIllegalInstruction: public NativeInstruction { + public: + enum Intel_specific_constants { + instruction_code = 0x0B0F, // Real byte order is: 0x0F, 0x0B + instruction_size = 2, + instruction_offset = 0, + next_instruction_offset = 2 + }; + + // Insert illegal opcode as specific address + static void insert(address code_pos); +}; + +// return instruction that does not pop values of the stack +class NativeReturn: public NativeInstruction { + public: + enum Intel_specific_constants { + instruction_code = 0xC3, + instruction_size = 1, + instruction_offset = 0, + next_instruction_offset = 1 + }; +}; + +// return instruction that does pop values of the stack +class NativeReturnX: public NativeInstruction { + public: + enum Intel_specific_constants { + instruction_code = 0xC2, + instruction_size = 2, + instruction_offset = 0, + next_instruction_offset = 2 + }; +}; + +// Simple test vs memory +class NativeTstRegMem: public NativeInstruction { + public: + enum Intel_specific_constants { + instruction_code_memXregl = 0x85 + }; +}; + +inline bool NativeInstruction::is_illegal() { return (short)int_at(0) == (short)NativeIllegalInstruction::instruction_code; } +inline bool NativeInstruction::is_call() { return ubyte_at(0) == NativeCall::instruction_code; } +inline bool NativeInstruction::is_return() { return ubyte_at(0) == NativeReturn::instruction_code || + ubyte_at(0) == NativeReturnX::instruction_code; } +inline bool NativeInstruction::is_jump() { return ubyte_at(0) == NativeJump::instruction_code || + ubyte_at(0) == 0xEB; /* short jump */ } +inline bool NativeInstruction::is_cond_jump() { return (int_at(0) & 0xF0FF) == 0x800F /* long jump */ || + (ubyte_at(0) & 0xF0) == 0x70; /* short jump */ } +inline bool NativeInstruction::is_safepoint_poll() { +#ifdef AMD64 + return ubyte_at(0) == NativeTstRegMem::instruction_code_memXregl && + ubyte_at(1) == 0x05 && // 00 rax 101 + ((intptr_t) addr_at(6)) + int_at(2) == (intptr_t) os::get_polling_page(); +#else + return ( ubyte_at(0) == NativeMovRegMem::instruction_code_mem2regl || + ubyte_at(0) == NativeTstRegMem::instruction_code_memXregl ) && + (ubyte_at(1)&0xC7) == 0x05 && /* Mod R/M == disp32 */ + (os::is_poll_address((address)int_at(2))); +#endif // AMD64 +} + +inline bool NativeInstruction::is_mov_literal64() { +#ifdef AMD64 + return ((ubyte_at(0) == Assembler::REX_W || ubyte_at(0) == Assembler::REX_WB) && + (ubyte_at(1) & (0xff ^ NativeMovConstReg::register_mask)) == 0xB8); +#else + return false; +#endif // AMD64 +} diff --git a/hotspot/src/cpu/x86/vm/registerMap_x86.hpp b/hotspot/src/cpu/x86/vm/registerMap_x86.hpp new file mode 100644 index 00000000000..cc941e2ccae --- /dev/null +++ b/hotspot/src/cpu/x86/vm/registerMap_x86.hpp @@ -0,0 +1,39 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// machine-dependent implemention for register maps + friend class frame; + + private: + // This is the hook for finding a register in an "well-known" location, + // such as a register block of a predetermined format. + // Since there is none, we just return NULL. + // See registerMap_sparc.hpp for an example of grabbing registers + // from register save areas of a standard layout. + address pd_location(VMReg reg) const {return NULL;} + + // no PD state to clear or copy: + void pd_clear() {} + void pd_initialize() {} + void pd_initialize_from(const RegisterMap* map) {} diff --git a/hotspot/src/cpu/x86/vm/register_definitions_x86.cpp b/hotspot/src/cpu/x86/vm/register_definitions_x86.cpp new file mode 100644 index 00000000000..2379bfb58a7 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/register_definitions_x86.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// make sure the defines don't screw up the declarations later on in this file +#define DONT_USE_REGISTER_DEFINES + +#include "incls/_precompiled.incl" +#include "incls/_register_definitions_x86.cpp.incl" + +REGISTER_DEFINITION(Register, noreg); +REGISTER_DEFINITION(Register, rax); +REGISTER_DEFINITION(Register, rcx); +REGISTER_DEFINITION(Register, rdx); +REGISTER_DEFINITION(Register, rbx); +REGISTER_DEFINITION(Register, rsp); +REGISTER_DEFINITION(Register, rbp); +REGISTER_DEFINITION(Register, rsi); +REGISTER_DEFINITION(Register, rdi); +#ifdef AMD64 +REGISTER_DEFINITION(Register, r8); +REGISTER_DEFINITION(Register, r9); +REGISTER_DEFINITION(Register, r10); +REGISTER_DEFINITION(Register, r11); +REGISTER_DEFINITION(Register, r12); +REGISTER_DEFINITION(Register, r13); +REGISTER_DEFINITION(Register, r14); +REGISTER_DEFINITION(Register, r15); +#endif // AMD64 + +REGISTER_DEFINITION(XMMRegister, xmm0 ); +REGISTER_DEFINITION(XMMRegister, xmm1 ); +REGISTER_DEFINITION(XMMRegister, xmm2 ); +REGISTER_DEFINITION(XMMRegister, xmm3 ); +REGISTER_DEFINITION(XMMRegister, xmm4 ); +REGISTER_DEFINITION(XMMRegister, xmm5 ); +REGISTER_DEFINITION(XMMRegister, xmm6 ); +REGISTER_DEFINITION(XMMRegister, xmm7 ); +#ifdef AMD64 +REGISTER_DEFINITION(XMMRegister, xmm8); +REGISTER_DEFINITION(XMMRegister, xmm9); +REGISTER_DEFINITION(XMMRegister, xmm10); +REGISTER_DEFINITION(XMMRegister, xmm11); +REGISTER_DEFINITION(XMMRegister, xmm12); +REGISTER_DEFINITION(XMMRegister, xmm13); +REGISTER_DEFINITION(XMMRegister, xmm14); +REGISTER_DEFINITION(XMMRegister, xmm15); + +REGISTER_DEFINITION(Register, c_rarg0); +REGISTER_DEFINITION(Register, c_rarg1); +REGISTER_DEFINITION(Register, c_rarg2); +REGISTER_DEFINITION(Register, c_rarg3); + +REGISTER_DEFINITION(XMMRegister, c_farg0); +REGISTER_DEFINITION(XMMRegister, c_farg1); +REGISTER_DEFINITION(XMMRegister, c_farg2); +REGISTER_DEFINITION(XMMRegister, c_farg3); + +// Non windows OS's have a few more argument registers +#ifndef _WIN64 +REGISTER_DEFINITION(Register, c_rarg4); +REGISTER_DEFINITION(Register, c_rarg5); + +REGISTER_DEFINITION(XMMRegister, c_farg4); +REGISTER_DEFINITION(XMMRegister, c_farg5); +REGISTER_DEFINITION(XMMRegister, c_farg6); +REGISTER_DEFINITION(XMMRegister, c_farg7); +#endif /* _WIN64 */ + +REGISTER_DEFINITION(Register, j_rarg0); +REGISTER_DEFINITION(Register, j_rarg1); +REGISTER_DEFINITION(Register, j_rarg2); +REGISTER_DEFINITION(Register, j_rarg3); +REGISTER_DEFINITION(Register, j_rarg4); +REGISTER_DEFINITION(Register, j_rarg5); + +REGISTER_DEFINITION(XMMRegister, j_farg0); +REGISTER_DEFINITION(XMMRegister, j_farg1); +REGISTER_DEFINITION(XMMRegister, j_farg2); +REGISTER_DEFINITION(XMMRegister, j_farg3); +REGISTER_DEFINITION(XMMRegister, j_farg4); +REGISTER_DEFINITION(XMMRegister, j_farg5); +REGISTER_DEFINITION(XMMRegister, j_farg6); +REGISTER_DEFINITION(XMMRegister, j_farg7); + +REGISTER_DEFINITION(Register, rscratch1); +REGISTER_DEFINITION(Register, rscratch2); + +REGISTER_DEFINITION(Register, r15_thread); +#endif // AMD64 + +REGISTER_DEFINITION(MMXRegister, mmx0 ); +REGISTER_DEFINITION(MMXRegister, mmx1 ); +REGISTER_DEFINITION(MMXRegister, mmx2 ); +REGISTER_DEFINITION(MMXRegister, mmx3 ); +REGISTER_DEFINITION(MMXRegister, mmx4 ); +REGISTER_DEFINITION(MMXRegister, mmx5 ); +REGISTER_DEFINITION(MMXRegister, mmx6 ); +REGISTER_DEFINITION(MMXRegister, mmx7 ); diff --git a/hotspot/src/cpu/x86/vm/register_x86.cpp b/hotspot/src/cpu/x86/vm/register_x86.cpp new file mode 100644 index 00000000000..e985eb01fcb --- /dev/null +++ b/hotspot/src/cpu/x86/vm/register_x86.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_register_x86.cpp.incl" +#ifndef AMD64 +const int ConcreteRegisterImpl::max_gpr = RegisterImpl::number_of_registers; +#else +const int ConcreteRegisterImpl::max_gpr = RegisterImpl::number_of_registers << 1; +#endif // AMD64 + + +const int ConcreteRegisterImpl::max_fpr = ConcreteRegisterImpl::max_gpr + + 2 * FloatRegisterImpl::number_of_registers; +const int ConcreteRegisterImpl::max_xmm = ConcreteRegisterImpl::max_fpr + + 2 * XMMRegisterImpl::number_of_registers; +const char* RegisterImpl::name() const { + const char* names[number_of_registers] = { +#ifndef AMD64 + "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" +#else + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" +#endif // AMD64 + }; + return is_valid() ? names[encoding()] : "noreg"; +} + +const char* FloatRegisterImpl::name() const { + const char* names[number_of_registers] = { + "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7" + }; + return is_valid() ? names[encoding()] : "noreg"; +} + +const char* XMMRegisterImpl::name() const { + const char* names[number_of_registers] = { + "xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7" +#ifdef AMD64 + ,"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" +#endif // AMD64 + }; + return is_valid() ? names[encoding()] : "xnoreg"; +} diff --git a/hotspot/src/cpu/x86/vm/register_x86.hpp b/hotspot/src/cpu/x86/vm/register_x86.hpp new file mode 100644 index 00000000000..7b6deb9896c --- /dev/null +++ b/hotspot/src/cpu/x86/vm/register_x86.hpp @@ -0,0 +1,221 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class VMRegImpl; +typedef VMRegImpl* VMReg; + +// Use Register as shortcut +class RegisterImpl; +typedef RegisterImpl* Register; + + +// The implementation of integer registers for the ia32 architecture +inline Register as_Register(int encoding) { + return (Register)(intptr_t) encoding; +} + +class RegisterImpl: public AbstractRegisterImpl { + public: + enum { +#ifndef AMD64 + number_of_registers = 8, + number_of_byte_registers = 4 +#else + number_of_registers = 16, + number_of_byte_registers = 16 +#endif // AMD64 + }; + + // derived registers, offsets, and addresses + Register successor() const { return as_Register(encoding() + 1); } + + // construction + inline friend Register as_Register(int encoding); + + VMReg as_VMReg(); + + // accessors + int encoding() const { assert(is_valid(), "invalid register"); return (intptr_t)this; } + bool is_valid() const { return 0 <= (intptr_t)this && (intptr_t)this < number_of_registers; } + bool has_byte_register() const { return 0 <= (intptr_t)this && (intptr_t)this < number_of_byte_registers; } + const char* name() const; +}; + +// The integer registers of the ia32/amd64 architecture + +CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1)); + + +CONSTANT_REGISTER_DECLARATION(Register, rax, (0)); +CONSTANT_REGISTER_DECLARATION(Register, rcx, (1)); +CONSTANT_REGISTER_DECLARATION(Register, rdx, (2)); +CONSTANT_REGISTER_DECLARATION(Register, rbx, (3)); +CONSTANT_REGISTER_DECLARATION(Register, rsp, (4)); +CONSTANT_REGISTER_DECLARATION(Register, rbp, (5)); +CONSTANT_REGISTER_DECLARATION(Register, rsi, (6)); +CONSTANT_REGISTER_DECLARATION(Register, rdi, (7)); +#ifdef AMD64 +CONSTANT_REGISTER_DECLARATION(Register, r8, (8)); +CONSTANT_REGISTER_DECLARATION(Register, r9, (9)); +CONSTANT_REGISTER_DECLARATION(Register, r10, (10)); +CONSTANT_REGISTER_DECLARATION(Register, r11, (11)); +CONSTANT_REGISTER_DECLARATION(Register, r12, (12)); +CONSTANT_REGISTER_DECLARATION(Register, r13, (13)); +CONSTANT_REGISTER_DECLARATION(Register, r14, (14)); +CONSTANT_REGISTER_DECLARATION(Register, r15, (15)); +#endif // AMD64 + +// Use FloatRegister as shortcut +class FloatRegisterImpl; +typedef FloatRegisterImpl* FloatRegister; + +inline FloatRegister as_FloatRegister(int encoding) { + return (FloatRegister)(intptr_t) encoding; +} + +// The implementation of floating point registers for the ia32 architecture +class FloatRegisterImpl: public AbstractRegisterImpl { + public: + enum { + number_of_registers = 8 + }; + + // construction + inline friend FloatRegister as_FloatRegister(int encoding); + + VMReg as_VMReg(); + + // derived registers, offsets, and addresses + FloatRegister successor() const { return as_FloatRegister(encoding() + 1); } + + // accessors + int encoding() const { assert(is_valid(), "invalid register"); return (intptr_t)this; } + bool is_valid() const { return 0 <= (intptr_t)this && (intptr_t)this < number_of_registers; } + const char* name() const; + +}; + +// Use XMMRegister as shortcut +class XMMRegisterImpl; +typedef XMMRegisterImpl* XMMRegister; + +// Use MMXRegister as shortcut +class MMXRegisterImpl; +typedef MMXRegisterImpl* MMXRegister; + +inline XMMRegister as_XMMRegister(int encoding) { + return (XMMRegister)(intptr_t)encoding; +} + +inline MMXRegister as_MMXRegister(int encoding) { + return (MMXRegister)(intptr_t)encoding; +} + +// The implementation of XMM registers for the IA32 architecture +class XMMRegisterImpl: public AbstractRegisterImpl { + public: + enum { +#ifndef AMD64 + number_of_registers = 8 +#else + number_of_registers = 16 +#endif // AMD64 + }; + + // construction + friend XMMRegister as_XMMRegister(int encoding); + + VMReg as_VMReg(); + + // derived registers, offsets, and addresses + XMMRegister successor() const { return as_XMMRegister(encoding() + 1); } + + // accessors + int encoding() const { assert(is_valid(), "invalid register"); return (intptr_t)this; } + bool is_valid() const { return 0 <= (intptr_t)this && (intptr_t)this < number_of_registers; } + const char* name() const; +}; + + +// The XMM registers, for P3 and up chips +CONSTANT_REGISTER_DECLARATION(XMMRegister, xnoreg , (-1)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm0 , ( 0)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm1 , ( 1)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm2 , ( 2)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm3 , ( 3)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm4 , ( 4)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm5 , ( 5)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm6 , ( 6)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm7 , ( 7)); +#ifdef AMD64 +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm8, (8)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm9, (9)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm10, (10)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm11, (11)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm12, (12)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm13, (13)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm14, (14)); +CONSTANT_REGISTER_DECLARATION(XMMRegister, xmm15, (15)); +#endif // AMD64 + +// Only used by the 32bit stubGenerator. These can't be described by vmreg and hence +// can't be described in oopMaps and therefore can't be used by the compilers (at least +// were deopt might wan't to see them). + +// The MMX registers, for P3 and up chips +CONSTANT_REGISTER_DECLARATION(MMXRegister, mnoreg , (-1)); +CONSTANT_REGISTER_DECLARATION(MMXRegister, mmx0 , ( 0)); +CONSTANT_REGISTER_DECLARATION(MMXRegister, mmx1 , ( 1)); +CONSTANT_REGISTER_DECLARATION(MMXRegister, mmx2 , ( 2)); +CONSTANT_REGISTER_DECLARATION(MMXRegister, mmx3 , ( 3)); +CONSTANT_REGISTER_DECLARATION(MMXRegister, mmx4 , ( 4)); +CONSTANT_REGISTER_DECLARATION(MMXRegister, mmx5 , ( 5)); +CONSTANT_REGISTER_DECLARATION(MMXRegister, mmx6 , ( 6)); +CONSTANT_REGISTER_DECLARATION(MMXRegister, mmx7 , ( 7)); + + +// Need to know the total number of registers of all sorts for SharedInfo. +// Define a class that exports it. +class ConcreteRegisterImpl : public AbstractRegisterImpl { + public: + enum { + // A big enough number for C2: all the registers plus flags + // This number must be large enough to cover REG_COUNT (defined by c2) registers. + // There is no requirement that any ordering here matches any ordering c2 gives + // it's optoregs. + + number_of_registers = RegisterImpl::number_of_registers + +#ifdef AMD64 + RegisterImpl::number_of_registers + // "H" half of a 64bit register +#endif // AMD64 + 2 * FloatRegisterImpl::number_of_registers + + 2 * XMMRegisterImpl::number_of_registers + + 1 // eflags + }; + + static const int max_gpr; + static const int max_fpr; + static const int max_xmm; + +}; diff --git a/hotspot/src/cpu/x86/vm/relocInfo_x86.cpp b/hotspot/src/cpu/x86/vm/relocInfo_x86.cpp new file mode 100644 index 00000000000..2c547c64518 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/relocInfo_x86.cpp @@ -0,0 +1,167 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_relocInfo_x86.cpp.incl" + + +void Relocation::pd_set_data_value(address x, intptr_t o) { +#ifdef AMD64 + x += o; + typedef Assembler::WhichOperand WhichOperand; + WhichOperand which = (WhichOperand) format(); // that is, disp32 or imm64, call32 + assert(which == Assembler::disp32_operand || + which == Assembler::imm64_operand, "format unpacks ok"); + if (which == Assembler::imm64_operand) { + *pd_address_in_code() = x; + } else { + // Note: Use runtime_call_type relocations for call32_operand. + address ip = addr(); + address disp = Assembler::locate_operand(ip, which); + address next_ip = Assembler::locate_next_instruction(ip); + *(int32_t*) disp = x - next_ip; + } +#else + *pd_address_in_code() = x + o; +#endif // AMD64 +} + + +address Relocation::pd_call_destination(address orig_addr) { + intptr_t adj = 0; + if (orig_addr != NULL) { + // We just moved this call instruction from orig_addr to addr(). + // This means its target will appear to have grown by addr() - orig_addr. + adj = -( addr() - orig_addr ); + } + NativeInstruction* ni = nativeInstruction_at(addr()); + if (ni->is_call()) { + return nativeCall_at(addr())->destination() + adj; + } else if (ni->is_jump()) { + return nativeJump_at(addr())->jump_destination() + adj; + } else if (ni->is_cond_jump()) { + return nativeGeneralJump_at(addr())->jump_destination() + adj; + } else if (ni->is_mov_literal64()) { + return (address) ((NativeMovConstReg*)ni)->data(); + } else { + ShouldNotReachHere(); + return NULL; + } +} + + +void Relocation::pd_set_call_destination(address x) { + NativeInstruction* ni = nativeInstruction_at(addr()); + if (ni->is_call()) { + nativeCall_at(addr())->set_destination(x); + } else if (ni->is_jump()) { + NativeJump* nj = nativeJump_at(addr()); +#ifdef AMD64 + if (nj->jump_destination() == (address) -1) { + x = (address) -1; // retain jump to self + } +#endif // AMD64 + nj->set_jump_destination(x); + } else if (ni->is_cond_jump()) { + // %%%% kludge this, for now, until we get a jump_destination method + address old_dest = nativeGeneralJump_at(addr())->jump_destination(); + address disp = Assembler::locate_operand(addr(), Assembler::call32_operand); + *(jint*)disp += (x - old_dest); + } else if (ni->is_mov_literal64()) { + ((NativeMovConstReg*)ni)->set_data((intptr_t)x); + } else { + ShouldNotReachHere(); + } +} + + +address* Relocation::pd_address_in_code() { + // All embedded Intel addresses are stored in 32-bit words. + // Since the addr points at the start of the instruction, + // we must parse the instruction a bit to find the embedded word. + assert(is_data(), "must be a DataRelocation"); + typedef Assembler::WhichOperand WhichOperand; + WhichOperand which = (WhichOperand) format(); // that is, disp32 or imm64/imm32 +#ifdef AMD64 + assert(which == Assembler::disp32_operand || + which == Assembler::call32_operand || + which == Assembler::imm64_operand, "format unpacks ok"); + if (which != Assembler::imm64_operand) { + // The "address" in the code is a displacement can't return it as + // and address* since it is really a jint* + ShouldNotReachHere(); + return NULL; + } +#else + assert(which == Assembler::disp32_operand || which == Assembler::imm32_operand, "format unpacks ok"); +#endif // AMD64 + return (address*) Assembler::locate_operand(addr(), which); +} + + +address Relocation::pd_get_address_from_code() { +#ifdef AMD64 + // All embedded Intel addresses are stored in 32-bit words. + // Since the addr points at the start of the instruction, + // we must parse the instruction a bit to find the embedded word. + assert(is_data(), "must be a DataRelocation"); + typedef Assembler::WhichOperand WhichOperand; + WhichOperand which = (WhichOperand) format(); // that is, disp32 or imm64/imm32 + assert(which == Assembler::disp32_operand || + which == Assembler::call32_operand || + which == Assembler::imm64_operand, "format unpacks ok"); + if (which != Assembler::imm64_operand) { + address ip = addr(); + address disp = Assembler::locate_operand(ip, which); + address next_ip = Assembler::locate_next_instruction(ip); + address a = next_ip + *(int32_t*) disp; + return a; + } +#endif // AMD64 + return *pd_address_in_code(); +} + +int Relocation::pd_breakpoint_size() { + // minimum breakpoint size, in short words + return NativeIllegalInstruction::instruction_size / sizeof(short); +} + +void Relocation::pd_swap_in_breakpoint(address x, short* instrs, int instrlen) { + Untested("pd_swap_in_breakpoint"); + if (instrs != NULL) { + assert(instrlen * sizeof(short) == NativeIllegalInstruction::instruction_size, "enough instrlen in reloc. data"); + for (int i = 0; i < instrlen; i++) { + instrs[i] = ((short*)x)[i]; + } + } + NativeIllegalInstruction::insert(x); +} + + +void Relocation::pd_swap_out_breakpoint(address x, short* instrs, int instrlen) { + Untested("pd_swap_out_breakpoint"); + assert(NativeIllegalInstruction::instruction_size == sizeof(short), "right address unit for update"); + NativeInstruction* ni = nativeInstruction_at(x); + *(short*)ni->addr_at(0) = instrs[0]; +} diff --git a/hotspot/src/cpu/x86/vm/relocInfo_x86.hpp b/hotspot/src/cpu/x86/vm/relocInfo_x86.hpp new file mode 100644 index 00000000000..964cd1ff307 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/relocInfo_x86.hpp @@ -0,0 +1,33 @@ +/* + * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // machine-dependent parts of class relocInfo + private: + enum { + // Intel instructions are byte-aligned. + offset_unit = 1, + + // Encodes Assembler::disp32_operand vs. Assembler::imm32_operand. + format_width = 1 + }; diff --git a/hotspot/src/cpu/x86/vm/runtime_x86_32.cpp b/hotspot/src/cpu/x86/vm/runtime_x86_32.cpp new file mode 100644 index 00000000000..d8d190936b7 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/runtime_x86_32.cpp @@ -0,0 +1,150 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +#include "incls/_precompiled.incl" +#include "incls/_runtime_x86_32.cpp.incl" + +#define __ masm-> + +ExceptionBlob* OptoRuntime::_exception_blob; + +//------------------------------generate_exception_blob--------------------------- +// creates exception blob at the end +// Using exception blob, this code is jumped from a compiled method. +// +// Given an exception pc at a call we call into the runtime for the +// handler in this method. This handler might merely restore state +// (i.e. callee save registers) unwind the frame and jump to the +// exception handler for the nmethod if there is no Java level handler +// for the nmethod. +// +// This code is entered with a jmp. +// +// Arguments: +// rax,: exception oop +// rdx: exception pc +// +// Results: +// rax,: exception oop +// rdx: exception pc in caller or ??? +// destination: exception handler of caller +// +// Note: the exception pc MUST be at a call (precise debug information) +// Only register rax, rdx, rcx are not callee saved. +// + +void OptoRuntime::generate_exception_blob() { + + // Capture info about frame layout + enum layout { + thread_off, // last_java_sp + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + rbp_off, + return_off, // slot for return address + framesize + }; + + // allocate space for the code + ResourceMark rm; + // setup code generation tools + CodeBuffer buffer("exception_blob", 512, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + + OopMapSet *oop_maps = new OopMapSet(); + + address start = __ pc(); + + __ pushl(rdx); + __ subl(rsp, return_off * wordSize); // Prolog! + + // rbp, location is implicitly known + __ movl(Address(rsp,rbp_off *wordSize),rbp); + + // Store exception in Thread object. We cannot pass any arguments to the + // handle_exception call, since we do not want to make any assumption + // about the size of the frame where the exception happened in. + __ get_thread(rcx); + __ movl(Address(rcx, JavaThread::exception_oop_offset()), rax); + __ movl(Address(rcx, JavaThread::exception_pc_offset()), rdx); + + // This call does all the hard work. It checks if an exception handler + // exists in the method. + // If so, it returns the handler address. + // If not, it prepares for stack-unwinding, restoring the callee-save + // registers of the frame being removed. + // + __ movl(Address(rsp, thread_off * wordSize), rcx); // Thread is first argument + __ set_last_Java_frame(rcx, noreg, noreg, NULL); + + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C))); + + // No registers to map, rbp is known implicitly + oop_maps->add_gc_map( __ pc() - start, new OopMap( framesize, 0 )); + __ get_thread(rcx); + __ reset_last_Java_frame(rcx, false, false); + + // Restore callee-saved registers + __ movl(rbp, Address(rsp, rbp_off * wordSize)); + + __ addl(rsp, return_off * wordSize); // Epilog! + __ popl(rdx); // Exception pc + + + // rax,: exception handler for given + + // We have a handler in rax, (could be deopt blob) + // rdx - throwing pc, deopt blob will need it. + + __ pushl(rax); + + // rcx contains handler address + + __ get_thread(rcx); // TLS + // Get the exception + __ movl(rax, Address(rcx, JavaThread::exception_oop_offset())); + // Get the exception pc in case we are deoptimized + __ movl(rdx, Address(rcx, JavaThread::exception_pc_offset())); +#ifdef ASSERT + __ movl(Address(rcx, JavaThread::exception_handler_pc_offset()), 0); + __ movl(Address(rcx, JavaThread::exception_pc_offset()), 0); +#endif + // Clear the exception oop so GC no longer processes it as a root. + __ movl(Address(rcx, JavaThread::exception_oop_offset()), 0); + + __ popl(rcx); + + // rax,: exception oop + // rcx: exception handler + // rdx: exception pc + __ jmp (rcx); + + // ------------- + // make sure all code is generated + masm->flush(); + + _exception_blob = ExceptionBlob::create(&buffer, oop_maps, framesize); +} diff --git a/hotspot/src/cpu/x86/vm/runtime_x86_64.cpp b/hotspot/src/cpu/x86/vm/runtime_x86_64.cpp new file mode 100644 index 00000000000..ce541bc1a76 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/runtime_x86_64.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" + +// This file should really contain the code for generating the OptoRuntime +// exception_blob. However that code uses SimpleRuntimeFrame which only +// exists in sharedRuntime_x86_64.cpp. When there is a sharedRuntime_.hpp +// file and SimpleRuntimeFrame is able to move there then the exception_blob +// code will move here where it belongs. diff --git a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp new file mode 100644 index 00000000000..3d30eb251ed --- /dev/null +++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp @@ -0,0 +1,2633 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_sharedRuntime_x86_32.cpp.incl" + +#define __ masm-> +#ifdef COMPILER2 +UncommonTrapBlob *SharedRuntime::_uncommon_trap_blob; +#endif // COMPILER2 + +DeoptimizationBlob *SharedRuntime::_deopt_blob; +SafepointBlob *SharedRuntime::_polling_page_safepoint_handler_blob; +SafepointBlob *SharedRuntime::_polling_page_return_handler_blob; +RuntimeStub* SharedRuntime::_wrong_method_blob; +RuntimeStub* SharedRuntime::_ic_miss_blob; +RuntimeStub* SharedRuntime::_resolve_opt_virtual_call_blob; +RuntimeStub* SharedRuntime::_resolve_virtual_call_blob; +RuntimeStub* SharedRuntime::_resolve_static_call_blob; + +class RegisterSaver { + enum { FPU_regs_live = 8 /*for the FPU stack*/+8/*eight more for XMM registers*/ }; + // Capture info about frame layout + enum layout { + fpu_state_off = 0, + fpu_state_end = fpu_state_off+FPUStateSizeInWords-1, + st0_off, st0H_off, + st1_off, st1H_off, + st2_off, st2H_off, + st3_off, st3H_off, + st4_off, st4H_off, + st5_off, st5H_off, + st6_off, st6H_off, + st7_off, st7H_off, + + xmm0_off, xmm0H_off, + xmm1_off, xmm1H_off, + xmm2_off, xmm2H_off, + xmm3_off, xmm3H_off, + xmm4_off, xmm4H_off, + xmm5_off, xmm5H_off, + xmm6_off, xmm6H_off, + xmm7_off, xmm7H_off, + flags_off, + rdi_off, + rsi_off, + ignore_off, // extra copy of rbp, + rsp_off, + rbx_off, + rdx_off, + rcx_off, + rax_off, + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + rbp_off, + return_off, // slot for return address + reg_save_size }; + + + public: + + static OopMap* save_live_registers(MacroAssembler* masm, int additional_frame_words, + int* total_frame_words, bool verify_fpu = true); + static void restore_live_registers(MacroAssembler* masm); + + static int rax_offset() { return rax_off; } + static int rbx_offset() { return rbx_off; } + + // Offsets into the register save area + // Used by deoptimization when it is managing result register + // values on its own + + static int raxOffset(void) { return rax_off; } + static int rdxOffset(void) { return rdx_off; } + static int rbxOffset(void) { return rbx_off; } + static int xmm0Offset(void) { return xmm0_off; } + // This really returns a slot in the fp save area, which one is not important + static int fpResultOffset(void) { return st0_off; } + + // During deoptimization only the result register need to be restored + // all the other values have already been extracted. + + static void restore_result_registers(MacroAssembler* masm); + +}; + +OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_frame_words, + int* total_frame_words, bool verify_fpu) { + + int frame_size_in_bytes = (reg_save_size + additional_frame_words) * wordSize; + int frame_words = frame_size_in_bytes / wordSize; + *total_frame_words = frame_words; + + assert(FPUStateSizeInWords == 27, "update stack layout"); + + // save registers, fpu state, and flags + // We assume caller has already has return address slot on the stack + // We push epb twice in this sequence because we want the real rbp, + // to be under the return like a normal enter and we want to use pushad + // We push by hand instead of pusing push + __ enter(); + __ pushad(); + __ pushfd(); + __ subl(rsp,FPU_regs_live*sizeof(jdouble)); // Push FPU registers space + __ push_FPU_state(); // Save FPU state & init + + if (verify_fpu) { + // Some stubs may have non standard FPU control word settings so + // only check and reset the value when it required to be the + // standard value. The safepoint blob in particular can be used + // in methods which are using the 24 bit control word for + // optimized float math. + +#ifdef ASSERT + // Make sure the control word has the expected value + Label ok; + __ cmpw(Address(rsp, 0), StubRoutines::fpu_cntrl_wrd_std()); + __ jccb(Assembler::equal, ok); + __ stop("corrupted control word detected"); + __ bind(ok); +#endif + + // Reset the control word to guard against exceptions being unmasked + // since fstp_d can cause FPU stack underflow exceptions. Write it + // into the on stack copy and then reload that to make sure that the + // current and future values are correct. + __ movw(Address(rsp, 0), StubRoutines::fpu_cntrl_wrd_std()); + } + + __ frstor(Address(rsp, 0)); + if (!verify_fpu) { + // Set the control word so that exceptions are masked for the + // following code. + __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); + } + + // Save the FPU registers in de-opt-able form + + __ fstp_d(Address(rsp, st0_off*wordSize)); // st(0) + __ fstp_d(Address(rsp, st1_off*wordSize)); // st(1) + __ fstp_d(Address(rsp, st2_off*wordSize)); // st(2) + __ fstp_d(Address(rsp, st3_off*wordSize)); // st(3) + __ fstp_d(Address(rsp, st4_off*wordSize)); // st(4) + __ fstp_d(Address(rsp, st5_off*wordSize)); // st(5) + __ fstp_d(Address(rsp, st6_off*wordSize)); // st(6) + __ fstp_d(Address(rsp, st7_off*wordSize)); // st(7) + + if( UseSSE == 1 ) { // Save the XMM state + __ movflt(Address(rsp,xmm0_off*wordSize),xmm0); + __ movflt(Address(rsp,xmm1_off*wordSize),xmm1); + __ movflt(Address(rsp,xmm2_off*wordSize),xmm2); + __ movflt(Address(rsp,xmm3_off*wordSize),xmm3); + __ movflt(Address(rsp,xmm4_off*wordSize),xmm4); + __ movflt(Address(rsp,xmm5_off*wordSize),xmm5); + __ movflt(Address(rsp,xmm6_off*wordSize),xmm6); + __ movflt(Address(rsp,xmm7_off*wordSize),xmm7); + } else if( UseSSE >= 2 ) { + __ movdbl(Address(rsp,xmm0_off*wordSize),xmm0); + __ movdbl(Address(rsp,xmm1_off*wordSize),xmm1); + __ movdbl(Address(rsp,xmm2_off*wordSize),xmm2); + __ movdbl(Address(rsp,xmm3_off*wordSize),xmm3); + __ movdbl(Address(rsp,xmm4_off*wordSize),xmm4); + __ movdbl(Address(rsp,xmm5_off*wordSize),xmm5); + __ movdbl(Address(rsp,xmm6_off*wordSize),xmm6); + __ movdbl(Address(rsp,xmm7_off*wordSize),xmm7); + } + + // Set an oopmap for the call site. This oopmap will map all + // oop-registers and debug-info registers as callee-saved. This + // will allow deoptimization at this safepoint to find all possible + // debug-info recordings, as well as let GC find all oops. + + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map = new OopMap( frame_words, 0 ); + +#define STACK_OFFSET(x) VMRegImpl::stack2reg((x) + additional_frame_words) + + map->set_callee_saved(STACK_OFFSET( rax_off), rax->as_VMReg()); + map->set_callee_saved(STACK_OFFSET( rcx_off), rcx->as_VMReg()); + map->set_callee_saved(STACK_OFFSET( rdx_off), rdx->as_VMReg()); + map->set_callee_saved(STACK_OFFSET( rbx_off), rbx->as_VMReg()); + // rbp, location is known implicitly, no oopMap + map->set_callee_saved(STACK_OFFSET( rsi_off), rsi->as_VMReg()); + map->set_callee_saved(STACK_OFFSET( rdi_off), rdi->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(st0_off), as_FloatRegister(0)->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(st1_off), as_FloatRegister(1)->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(st2_off), as_FloatRegister(2)->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(st3_off), as_FloatRegister(3)->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(st4_off), as_FloatRegister(4)->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(st5_off), as_FloatRegister(5)->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(st6_off), as_FloatRegister(6)->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(st7_off), as_FloatRegister(7)->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(xmm0_off), xmm0->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(xmm1_off), xmm1->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(xmm2_off), xmm2->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(xmm3_off), xmm3->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(xmm4_off), xmm4->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(xmm5_off), xmm5->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(xmm6_off), xmm6->as_VMReg()); + map->set_callee_saved(STACK_OFFSET(xmm7_off), xmm7->as_VMReg()); + // %%% This is really a waste but we'll keep things as they were for now + if (true) { +#define NEXTREG(x) (x)->as_VMReg()->next() + map->set_callee_saved(STACK_OFFSET(st0H_off), NEXTREG(as_FloatRegister(0))); + map->set_callee_saved(STACK_OFFSET(st1H_off), NEXTREG(as_FloatRegister(1))); + map->set_callee_saved(STACK_OFFSET(st2H_off), NEXTREG(as_FloatRegister(2))); + map->set_callee_saved(STACK_OFFSET(st3H_off), NEXTREG(as_FloatRegister(3))); + map->set_callee_saved(STACK_OFFSET(st4H_off), NEXTREG(as_FloatRegister(4))); + map->set_callee_saved(STACK_OFFSET(st5H_off), NEXTREG(as_FloatRegister(5))); + map->set_callee_saved(STACK_OFFSET(st6H_off), NEXTREG(as_FloatRegister(6))); + map->set_callee_saved(STACK_OFFSET(st7H_off), NEXTREG(as_FloatRegister(7))); + map->set_callee_saved(STACK_OFFSET(xmm0H_off), NEXTREG(xmm0)); + map->set_callee_saved(STACK_OFFSET(xmm1H_off), NEXTREG(xmm1)); + map->set_callee_saved(STACK_OFFSET(xmm2H_off), NEXTREG(xmm2)); + map->set_callee_saved(STACK_OFFSET(xmm3H_off), NEXTREG(xmm3)); + map->set_callee_saved(STACK_OFFSET(xmm4H_off), NEXTREG(xmm4)); + map->set_callee_saved(STACK_OFFSET(xmm5H_off), NEXTREG(xmm5)); + map->set_callee_saved(STACK_OFFSET(xmm6H_off), NEXTREG(xmm6)); + map->set_callee_saved(STACK_OFFSET(xmm7H_off), NEXTREG(xmm7)); +#undef NEXTREG +#undef STACK_OFFSET + } + + return map; + +} + +void RegisterSaver::restore_live_registers(MacroAssembler* masm) { + + // Recover XMM & FPU state + if( UseSSE == 1 ) { + __ movflt(xmm0,Address(rsp,xmm0_off*wordSize)); + __ movflt(xmm1,Address(rsp,xmm1_off*wordSize)); + __ movflt(xmm2,Address(rsp,xmm2_off*wordSize)); + __ movflt(xmm3,Address(rsp,xmm3_off*wordSize)); + __ movflt(xmm4,Address(rsp,xmm4_off*wordSize)); + __ movflt(xmm5,Address(rsp,xmm5_off*wordSize)); + __ movflt(xmm6,Address(rsp,xmm6_off*wordSize)); + __ movflt(xmm7,Address(rsp,xmm7_off*wordSize)); + } else if( UseSSE >= 2 ) { + __ movdbl(xmm0,Address(rsp,xmm0_off*wordSize)); + __ movdbl(xmm1,Address(rsp,xmm1_off*wordSize)); + __ movdbl(xmm2,Address(rsp,xmm2_off*wordSize)); + __ movdbl(xmm3,Address(rsp,xmm3_off*wordSize)); + __ movdbl(xmm4,Address(rsp,xmm4_off*wordSize)); + __ movdbl(xmm5,Address(rsp,xmm5_off*wordSize)); + __ movdbl(xmm6,Address(rsp,xmm6_off*wordSize)); + __ movdbl(xmm7,Address(rsp,xmm7_off*wordSize)); + } + __ pop_FPU_state(); + __ addl(rsp,FPU_regs_live*sizeof(jdouble)); // Pop FPU registers + + __ popfd(); + __ popad(); + // Get the rbp, described implicitly by the frame sender code (no oopMap) + __ popl(rbp); + +} + +void RegisterSaver::restore_result_registers(MacroAssembler* masm) { + + // Just restore result register. Only used by deoptimization. By + // now any callee save register that needs to be restore to a c2 + // caller of the deoptee has been extracted into the vframeArray + // and will be stuffed into the c2i adapter we create for later + // restoration so only result registers need to be restored here. + // + + __ frstor(Address(rsp, 0)); // Restore fpu state + + // Recover XMM & FPU state + if( UseSSE == 1 ) { + __ movflt(xmm0, Address(rsp, xmm0_off*wordSize)); + } else if( UseSSE >= 2 ) { + __ movdbl(xmm0, Address(rsp, xmm0_off*wordSize)); + } + __ movl(rax, Address(rsp, rax_off*wordSize)); + __ movl(rdx, Address(rsp, rdx_off*wordSize)); + // Pop all of the register save are off the stack except the return address + __ addl(rsp, return_off * wordSize); +} + +// The java_calling_convention describes stack locations as ideal slots on +// a frame with no abi restrictions. Since we must observe abi restrictions +// (like the placement of the register window) the slots must be biased by +// the following value. +static int reg2offset_in(VMReg r) { + // Account for saved rbp, and return address + // This should really be in_preserve_stack_slots + return (r->reg2stack() + 2) * VMRegImpl::stack_slot_size; +} + +static int reg2offset_out(VMReg r) { + return (r->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size; +} + +// --------------------------------------------------------------------------- +// Read the array of BasicTypes from a signature, and compute where the +// arguments should go. Values in the VMRegPair regs array refer to 4-byte +// quantities. Values less than SharedInfo::stack0 are registers, those above +// refer to 4-byte stack slots. All stack slots are based off of the stack pointer +// as framesizes are fixed. +// VMRegImpl::stack0 refers to the first slot 0(sp). +// and VMRegImpl::stack0+1 refers to the memory word 4-byes higher. Register +// up to RegisterImpl::number_of_registers) are the 32-bit +// integer registers. + +// Pass first two oop/int args in registers ECX and EDX. +// Pass first two float/double args in registers XMM0 and XMM1. +// Doubles have precedence, so if you pass a mix of floats and doubles +// the doubles will grab the registers before the floats will. + +// Note: the INPUTS in sig_bt are in units of Java argument words, which are +// either 32-bit or 64-bit depending on the build. The OUTPUTS are in 32-bit +// units regardless of build. Of course for i486 there is no 64 bit build + + +// --------------------------------------------------------------------------- +// The compiled Java calling convention. +// Pass first two oop/int args in registers ECX and EDX. +// Pass first two float/double args in registers XMM0 and XMM1. +// Doubles have precedence, so if you pass a mix of floats and doubles +// the doubles will grab the registers before the floats will. +int SharedRuntime::java_calling_convention(const BasicType *sig_bt, + VMRegPair *regs, + int total_args_passed, + int is_outgoing) { + uint stack = 0; // Starting stack position for args on stack + + + // Pass first two oop/int args in registers ECX and EDX. + uint reg_arg0 = 9999; + uint reg_arg1 = 9999; + + // Pass first two float/double args in registers XMM0 and XMM1. + // Doubles have precedence, so if you pass a mix of floats and doubles + // the doubles will grab the registers before the floats will. + // CNC - TURNED OFF FOR non-SSE. + // On Intel we have to round all doubles (and most floats) at + // call sites by storing to the stack in any case. + // UseSSE=0 ==> Don't Use ==> 9999+0 + // UseSSE=1 ==> Floats only ==> 9999+1 + // UseSSE>=2 ==> Floats or doubles ==> 9999+2 + enum { fltarg_dontuse = 9999+0, fltarg_float_only = 9999+1, fltarg_flt_dbl = 9999+2 }; + uint fargs = (UseSSE>=2) ? 2 : UseSSE; + uint freg_arg0 = 9999+fargs; + uint freg_arg1 = 9999+fargs; + + // Pass doubles & longs aligned on the stack. First count stack slots for doubles + int i; + for( i = 0; i < total_args_passed; i++) { + if( sig_bt[i] == T_DOUBLE ) { + // first 2 doubles go in registers + if( freg_arg0 == fltarg_flt_dbl ) freg_arg0 = i; + else if( freg_arg1 == fltarg_flt_dbl ) freg_arg1 = i; + else // Else double is passed low on the stack to be aligned. + stack += 2; + } else if( sig_bt[i] == T_LONG ) { + stack += 2; + } + } + int dstack = 0; // Separate counter for placing doubles + + // Now pick where all else goes. + for( i = 0; i < total_args_passed; i++) { + // From the type and the argument number (count) compute the location + switch( sig_bt[i] ) { + case T_SHORT: + case T_CHAR: + case T_BYTE: + case T_BOOLEAN: + case T_INT: + case T_ARRAY: + case T_OBJECT: + case T_ADDRESS: + if( reg_arg0 == 9999 ) { + reg_arg0 = i; + regs[i].set1(rcx->as_VMReg()); + } else if( reg_arg1 == 9999 ) { + reg_arg1 = i; + regs[i].set1(rdx->as_VMReg()); + } else { + regs[i].set1(VMRegImpl::stack2reg(stack++)); + } + break; + case T_FLOAT: + if( freg_arg0 == fltarg_flt_dbl || freg_arg0 == fltarg_float_only ) { + freg_arg0 = i; + regs[i].set1(xmm0->as_VMReg()); + } else if( freg_arg1 == fltarg_flt_dbl || freg_arg1 == fltarg_float_only ) { + freg_arg1 = i; + regs[i].set1(xmm1->as_VMReg()); + } else { + regs[i].set1(VMRegImpl::stack2reg(stack++)); + } + break; + case T_LONG: + assert(sig_bt[i+1] == T_VOID, "missing Half" ); + regs[i].set2(VMRegImpl::stack2reg(dstack)); + dstack += 2; + break; + case T_DOUBLE: + assert(sig_bt[i+1] == T_VOID, "missing Half" ); + if( freg_arg0 == (uint)i ) { + regs[i].set2(xmm0->as_VMReg()); + } else if( freg_arg1 == (uint)i ) { + regs[i].set2(xmm1->as_VMReg()); + } else { + regs[i].set2(VMRegImpl::stack2reg(dstack)); + dstack += 2; + } + break; + case T_VOID: regs[i].set_bad(); break; + break; + default: + ShouldNotReachHere(); + break; + } + } + + // return value can be odd number of VMRegImpl stack slots make multiple of 2 + return round_to(stack, 2); +} + +// Patch the callers callsite with entry to compiled code if it exists. +static void patch_callers_callsite(MacroAssembler *masm) { + Label L; + __ verify_oop(rbx); + __ cmpl(Address(rbx, in_bytes(methodOopDesc::code_offset())), NULL_WORD); + __ jcc(Assembler::equal, L); + // Schedule the branch target address early. + // Call into the VM to patch the caller, then jump to compiled callee + // rax, isn't live so capture return address while we easily can + __ movl(rax, Address(rsp, 0)); + __ pushad(); + __ pushfd(); + + if (UseSSE == 1) { + __ subl(rsp, 2*wordSize); + __ movflt(Address(rsp, 0), xmm0); + __ movflt(Address(rsp, wordSize), xmm1); + } + if (UseSSE >= 2) { + __ subl(rsp, 4*wordSize); + __ movdbl(Address(rsp, 0), xmm0); + __ movdbl(Address(rsp, 2*wordSize), xmm1); + } +#ifdef COMPILER2 + // C2 may leave the stack dirty if not in SSE2+ mode + if (UseSSE >= 2) { + __ verify_FPU(0, "c2i transition should have clean FPU stack"); + } else { + __ empty_FPU_stack(); + } +#endif /* COMPILER2 */ + + // VM needs caller's callsite + __ pushl(rax); + // VM needs target method + __ pushl(rbx); + __ verify_oop(rbx); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::fixup_callers_callsite))); + __ addl(rsp, 2*wordSize); + + if (UseSSE == 1) { + __ movflt(xmm0, Address(rsp, 0)); + __ movflt(xmm1, Address(rsp, wordSize)); + __ addl(rsp, 2*wordSize); + } + if (UseSSE >= 2) { + __ movdbl(xmm0, Address(rsp, 0)); + __ movdbl(xmm1, Address(rsp, 2*wordSize)); + __ addl(rsp, 4*wordSize); + } + + __ popfd(); + __ popad(); + __ bind(L); +} + + +// Helper function to put tags in interpreter stack. +static void tag_stack(MacroAssembler *masm, const BasicType sig, int st_off) { + if (TaggedStackInterpreter) { + int tag_offset = st_off + Interpreter::expr_tag_offset_in_bytes(0); + if (sig == T_OBJECT || sig == T_ARRAY) { + __ movl(Address(rsp, tag_offset), frame::TagReference); + } else if (sig == T_LONG || sig == T_DOUBLE) { + int next_tag_offset = st_off + Interpreter::expr_tag_offset_in_bytes(1); + __ movl(Address(rsp, next_tag_offset), frame::TagValue); + __ movl(Address(rsp, tag_offset), frame::TagValue); + } else { + __ movl(Address(rsp, tag_offset), frame::TagValue); + } + } +} + +// Double and long values with Tagged stacks are not contiguous. +static void move_c2i_double(MacroAssembler *masm, XMMRegister r, int st_off) { + int next_off = st_off - Interpreter::stackElementSize(); + if (TaggedStackInterpreter) { + __ movdbl(Address(rsp, next_off), r); + // Move top half up and put tag in the middle. + __ movl(rdi, Address(rsp, next_off+wordSize)); + __ movl(Address(rsp, st_off), rdi); + tag_stack(masm, T_DOUBLE, next_off); + } else { + __ movdbl(Address(rsp, next_off), r); + } +} + +static void gen_c2i_adapter(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + Label& skip_fixup) { + // Before we get into the guts of the C2I adapter, see if we should be here + // at all. We've come from compiled code and are attempting to jump to the + // interpreter, which means the caller made a static call to get here + // (vcalls always get a compiled target if there is one). Check for a + // compiled target. If there is one, we need to patch the caller's call. + patch_callers_callsite(masm); + + __ bind(skip_fixup); + +#ifdef COMPILER2 + // C2 may leave the stack dirty if not in SSE2+ mode + if (UseSSE >= 2) { + __ verify_FPU(0, "c2i transition should have clean FPU stack"); + } else { + __ empty_FPU_stack(); + } +#endif /* COMPILER2 */ + + // Since all args are passed on the stack, total_args_passed * interpreter_ + // stack_element_size is the + // space we need. + int extraspace = total_args_passed * Interpreter::stackElementSize(); + + // Get return address + __ popl(rax); + + // set senderSP value + __ movl(rsi, rsp); + + __ subl(rsp, extraspace); + + // Now write the args into the outgoing interpreter space + for (int i = 0; i < total_args_passed; i++) { + if (sig_bt[i] == T_VOID) { + assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half"); + continue; + } + + // st_off points to lowest address on stack. + int st_off = ((total_args_passed - 1) - i) * Interpreter::stackElementSize(); + // Say 4 args: + // i st_off + // 0 12 T_LONG + // 1 8 T_VOID + // 2 4 T_OBJECT + // 3 0 T_BOOL + VMReg r_1 = regs[i].first(); + VMReg r_2 = regs[i].second(); + if (!r_1->is_valid()) { + assert(!r_2->is_valid(), ""); + continue; + } + + if (r_1->is_stack()) { + // memory to memory use fpu stack top + int ld_off = r_1->reg2stack() * VMRegImpl::stack_slot_size + extraspace; + + if (!r_2->is_valid()) { + __ movl(rdi, Address(rsp, ld_off)); + __ movl(Address(rsp, st_off), rdi); + tag_stack(masm, sig_bt[i], st_off); + } else { + + // ld_off == LSW, ld_off+VMRegImpl::stack_slot_size == MSW + // st_off == MSW, st_off-wordSize == LSW + + int next_off = st_off - Interpreter::stackElementSize(); + __ movl(rdi, Address(rsp, ld_off)); + __ movl(Address(rsp, next_off), rdi); + __ movl(rdi, Address(rsp, ld_off + wordSize)); + __ movl(Address(rsp, st_off), rdi); + tag_stack(masm, sig_bt[i], next_off); + } + } else if (r_1->is_Register()) { + Register r = r_1->as_Register(); + if (!r_2->is_valid()) { + __ movl(Address(rsp, st_off), r); + tag_stack(masm, sig_bt[i], st_off); + } else { + // long/double in gpr + ShouldNotReachHere(); + } + } else { + assert(r_1->is_XMMRegister(), ""); + if (!r_2->is_valid()) { + __ movflt(Address(rsp, st_off), r_1->as_XMMRegister()); + tag_stack(masm, sig_bt[i], st_off); + } else { + assert(sig_bt[i] == T_DOUBLE || sig_bt[i] == T_LONG, "wrong type"); + move_c2i_double(masm, r_1->as_XMMRegister(), st_off); + } + } + } + + // Schedule the branch target address early. + __ movl(rcx, Address(rbx, in_bytes(methodOopDesc::interpreter_entry_offset()))); + // And repush original return address + __ pushl(rax); + __ jmp(rcx); +} + + +// For tagged stacks, double or long value aren't contiguous on the stack +// so get them contiguous for the xmm load +static void move_i2c_double(MacroAssembler *masm, XMMRegister r, Register saved_sp, int ld_off) { + int next_val_off = ld_off - Interpreter::stackElementSize(); + if (TaggedStackInterpreter) { + // use tag slot temporarily for MSW + __ movl(rsi, Address(saved_sp, ld_off)); + __ movl(Address(saved_sp, next_val_off+wordSize), rsi); + __ movdbl(r, Address(saved_sp, next_val_off)); + // restore tag + __ movl(Address(saved_sp, next_val_off+wordSize), frame::TagValue); + } else { + __ movdbl(r, Address(saved_sp, next_val_off)); + } +} + +static void gen_i2c_adapter(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs) { + // we're being called from the interpreter but need to find the + // compiled return entry point. The return address on the stack + // should point at it and we just need to pull the old value out. + // load up the pointer to the compiled return entry point and + // rewrite our return pc. The code is arranged like so: + // + // .word Interpreter::return_sentinel + // .word address_of_compiled_return_point + // return_entry_point: blah_blah_blah + // + // So we can find the appropriate return point by loading up the word + // just prior to the current return address we have on the stack. + // + // We will only enter here from an interpreted frame and never from after + // passing thru a c2i. Azul allowed this but we do not. If we lose the + // race and use a c2i we will remain interpreted for the race loser(s). + // This removes all sorts of headaches on the x86 side and also eliminates + // the possibility of having c2i -> i2c -> c2i -> ... endless transitions. + + + // Note: rsi contains the senderSP on entry. We must preserve it since + // we may do a i2c -> c2i transition if we lose a race where compiled + // code goes non-entrant while we get args ready. + + // Pick up the return address + __ movl(rax, Address(rsp, 0)); + + // If UseSSE >= 2 then no cleanup is needed on the return to the + // interpreter so skip fixing up the return entry point unless + // VerifyFPU is enabled. + if (UseSSE < 2 || VerifyFPU) { + Label skip, chk_int; + // If we were called from the call stub we need to do a little bit different + // cleanup than if the interpreter returned to the call stub. + + ExternalAddress stub_return_address(StubRoutines::_call_stub_return_address); + __ cmp32(rax, stub_return_address.addr()); + __ jcc(Assembler::notEqual, chk_int); + assert(StubRoutines::i486::get_call_stub_compiled_return() != NULL, "must be set"); + __ lea(rax, ExternalAddress(StubRoutines::i486::get_call_stub_compiled_return())); + __ jmp(skip); + + // It must be the interpreter since we never get here via a c2i (unlike Azul) + + __ bind(chk_int); +#ifdef ASSERT + { + Label ok; + __ cmpl(Address(rax, -8), Interpreter::return_sentinel); + __ jcc(Assembler::equal, ok); + __ int3(); + __ bind(ok); + } +#endif // ASSERT + __ movl(rax, Address(rax, -4)); + __ bind(skip); + } + + // rax, now contains the compiled return entry point which will do an + // cleanup needed for the return from compiled to interpreted. + + // Must preserve original SP for loading incoming arguments because + // we need to align the outgoing SP for compiled code. + __ movl(rdi, rsp); + + // Cut-out for having no stack args. Since up to 2 int/oop args are passed + // in registers, we will occasionally have no stack args. + int comp_words_on_stack = 0; + if (comp_args_on_stack) { + // Sig words on the stack are greater-than VMRegImpl::stack0. Those in + // registers are below. By subtracting stack0, we either get a negative + // number (all values in registers) or the maximum stack slot accessed. + // int comp_args_on_stack = VMRegImpl::reg2stack(max_arg); + // Convert 4-byte stack slots to words. + comp_words_on_stack = round_to(comp_args_on_stack*4, wordSize)>>LogBytesPerWord; + // Round up to miminum stack alignment, in wordSize + comp_words_on_stack = round_to(comp_words_on_stack, 2); + __ subl(rsp, comp_words_on_stack * wordSize); + } + + // Align the outgoing SP + __ andl(rsp, -(StackAlignmentInBytes)); + + // push the return address on the stack (note that pushing, rather + // than storing it, yields the correct frame alignment for the callee) + __ pushl(rax); + + // Put saved SP in another register + const Register saved_sp = rax; + __ movl(saved_sp, rdi); + + + // Will jump to the compiled code just as if compiled code was doing it. + // Pre-load the register-jump target early, to schedule it better. + __ movl(rdi, Address(rbx, in_bytes(methodOopDesc::from_compiled_offset()))); + + // Now generate the shuffle code. Pick up all register args and move the + // rest through the floating point stack top. + for (int i = 0; i < total_args_passed; i++) { + if (sig_bt[i] == T_VOID) { + // Longs and doubles are passed in native word order, but misaligned + // in the 32-bit build. + assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half"); + continue; + } + + // Pick up 0, 1 or 2 words from SP+offset. + + assert(!regs[i].second()->is_valid() || regs[i].first()->next() == regs[i].second(), + "scrambled load targets?"); + // Load in argument order going down. + int ld_off = (total_args_passed - i)*Interpreter::stackElementSize() + Interpreter::value_offset_in_bytes(); + // Point to interpreter value (vs. tag) + int next_off = ld_off - Interpreter::stackElementSize(); + // + // + // + VMReg r_1 = regs[i].first(); + VMReg r_2 = regs[i].second(); + if (!r_1->is_valid()) { + assert(!r_2->is_valid(), ""); + continue; + } + if (r_1->is_stack()) { + // Convert stack slot to an SP offset (+ wordSize to account for return address ) + int st_off = regs[i].first()->reg2stack()*VMRegImpl::stack_slot_size + wordSize; + + // We can use rsi as a temp here because compiled code doesn't need rsi as an input + // and if we end up going thru a c2i because of a miss a reasonable value of rsi + // we be generated. + if (!r_2->is_valid()) { + // __ fld_s(Address(saved_sp, ld_off)); + // __ fstp_s(Address(rsp, st_off)); + __ movl(rsi, Address(saved_sp, ld_off)); + __ movl(Address(rsp, st_off), rsi); + } else { + // Interpreter local[n] == MSW, local[n+1] == LSW however locals + // are accessed as negative so LSW is at LOW address + + // ld_off is MSW so get LSW + // st_off is LSW (i.e. reg.first()) + // __ fld_d(Address(saved_sp, next_off)); + // __ fstp_d(Address(rsp, st_off)); + __ movl(rsi, Address(saved_sp, next_off)); + __ movl(Address(rsp, st_off), rsi); + __ movl(rsi, Address(saved_sp, ld_off)); + __ movl(Address(rsp, st_off + wordSize), rsi); + } + } else if (r_1->is_Register()) { // Register argument + Register r = r_1->as_Register(); + assert(r != rax, "must be different"); + if (r_2->is_valid()) { + assert(r_2->as_Register() != rax, "need another temporary register"); + // Remember r_1 is low address (and LSB on x86) + // So r_2 gets loaded from high address regardless of the platform + __ movl(r_2->as_Register(), Address(saved_sp, ld_off)); + __ movl(r, Address(saved_sp, next_off)); + } else { + __ movl(r, Address(saved_sp, ld_off)); + } + } else { + assert(r_1->is_XMMRegister(), ""); + if (!r_2->is_valid()) { + __ movflt(r_1->as_XMMRegister(), Address(saved_sp, ld_off)); + } else { + move_i2c_double(masm, r_1->as_XMMRegister(), saved_sp, ld_off); + } + } + } + + // 6243940 We might end up in handle_wrong_method if + // the callee is deoptimized as we race thru here. If that + // happens we don't want to take a safepoint because the + // caller frame will look interpreted and arguments are now + // "compiled" so it is much better to make this transition + // invisible to the stack walking code. Unfortunately if + // we try and find the callee by normal means a safepoint + // is possible. So we stash the desired callee in the thread + // and the vm will find there should this case occur. + + __ get_thread(rax); + __ movl(Address(rax, JavaThread::callee_target_offset()), rbx); + + // move methodOop to rax, in case we end up in an c2i adapter. + // the c2i adapters expect methodOop in rax, (c2) because c2's + // resolve stubs return the result (the method) in rax,. + // I'd love to fix this. + __ movl(rax, rbx); + + __ jmp(rdi); +} + +// --------------------------------------------------------------- +AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs) { + address i2c_entry = __ pc(); + + gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); + + // ------------------------------------------------------------------------- + // Generate a C2I adapter. On entry we know rbx, holds the methodOop during calls + // to the interpreter. The args start out packed in the compiled layout. They + // need to be unpacked into the interpreter layout. This will almost always + // require some stack space. We grow the current (compiled) stack, then repack + // the args. We finally end in a jump to the generic interpreter entry point. + // On exit from the interpreter, the interpreter will restore our SP (lest the + // compiled code, which relys solely on SP and not EBP, get sick). + + address c2i_unverified_entry = __ pc(); + Label skip_fixup; + + Register holder = rax; + Register receiver = rcx; + Register temp = rbx; + + { + + Label missed; + + __ verify_oop(holder); + __ movl(temp, Address(receiver, oopDesc::klass_offset_in_bytes())); + __ verify_oop(temp); + + __ cmpl(temp, Address(holder, compiledICHolderOopDesc::holder_klass_offset())); + __ movl(rbx, Address(holder, compiledICHolderOopDesc::holder_method_offset())); + __ jcc(Assembler::notEqual, missed); + // Method might have been compiled since the call site was patched to + // interpreted if that is the case treat it as a miss so we can get + // the call site corrected. + __ cmpl(Address(rbx, in_bytes(methodOopDesc::code_offset())), NULL_WORD); + __ jcc(Assembler::equal, skip_fixup); + + __ bind(missed); + __ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub())); + } + + address c2i_entry = __ pc(); + + gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); + + __ flush(); + return new AdapterHandlerEntry(i2c_entry, c2i_entry, c2i_unverified_entry); +} + +int SharedRuntime::c_calling_convention(const BasicType *sig_bt, + VMRegPair *regs, + int total_args_passed) { +// We return the amount of VMRegImpl stack slots we need to reserve for all +// the arguments NOT counting out_preserve_stack_slots. + + uint stack = 0; // All arguments on stack + + for( int i = 0; i < total_args_passed; i++) { + // From the type and the argument number (count) compute the location + switch( sig_bt[i] ) { + case T_BOOLEAN: + case T_CHAR: + case T_FLOAT: + case T_BYTE: + case T_SHORT: + case T_INT: + case T_OBJECT: + case T_ARRAY: + case T_ADDRESS: + regs[i].set1(VMRegImpl::stack2reg(stack++)); + break; + case T_LONG: + case T_DOUBLE: // The stack numbering is reversed from Java + // Since C arguments do not get reversed, the ordering for + // doubles on the stack must be opposite the Java convention + assert(sig_bt[i+1] == T_VOID, "missing Half" ); + regs[i].set2(VMRegImpl::stack2reg(stack)); + stack += 2; + break; + case T_VOID: regs[i].set_bad(); break; + default: + ShouldNotReachHere(); + break; + } + } + return stack; +} + +// A simple move of integer like type +static void simple_move32(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack + // __ ld(FP, reg2offset(src.first()) + STACK_BIAS, L5); + // __ st(L5, SP, reg2offset(dst.first()) + STACK_BIAS); + __ movl(rax, Address(rbp, reg2offset_in(src.first()))); + __ movl(Address(rsp, reg2offset_out(dst.first())), rax); + } else { + // stack to reg + __ movl(dst.first()->as_Register(), Address(rbp, reg2offset_in(src.first()))); + } + } else if (dst.first()->is_stack()) { + // reg to stack + __ movl(Address(rsp, reg2offset_out(dst.first())), src.first()->as_Register()); + } else { + __ movl(dst.first()->as_Register(), src.first()->as_Register()); + } +} + +// An oop arg. Must pass a handle not the oop itself +static void object_move(MacroAssembler* masm, + OopMap* map, + int oop_handle_offset, + int framesize_in_slots, + VMRegPair src, + VMRegPair dst, + bool is_receiver, + int* receiver_offset) { + + // Because of the calling conventions we know that src can be a + // register or a stack location. dst can only be a stack location. + + assert(dst.first()->is_stack(), "must be stack"); + // must pass a handle. First figure out the location we use as a handle + + if (src.first()->is_stack()) { + // Oop is already on the stack as an argument + Register rHandle = rax; + Label nil; + __ xorl(rHandle, rHandle); + __ cmpl(Address(rbp, reg2offset_in(src.first())), NULL_WORD); + __ jcc(Assembler::equal, nil); + __ leal(rHandle, Address(rbp, reg2offset_in(src.first()))); + __ bind(nil); + __ movl(Address(rsp, reg2offset_out(dst.first())), rHandle); + + int offset_in_older_frame = src.first()->reg2stack() + SharedRuntime::out_preserve_stack_slots(); + map->set_oop(VMRegImpl::stack2reg(offset_in_older_frame + framesize_in_slots)); + if (is_receiver) { + *receiver_offset = (offset_in_older_frame + framesize_in_slots) * VMRegImpl::stack_slot_size; + } + } else { + // Oop is in an a register we must store it to the space we reserve + // on the stack for oop_handles + const Register rOop = src.first()->as_Register(); + const Register rHandle = rax; + int oop_slot = (rOop == rcx ? 0 : 1) * VMRegImpl::slots_per_word + oop_handle_offset; + int offset = oop_slot*VMRegImpl::stack_slot_size; + Label skip; + __ movl(Address(rsp, offset), rOop); + map->set_oop(VMRegImpl::stack2reg(oop_slot)); + __ xorl(rHandle, rHandle); + __ cmpl(rOop, NULL_WORD); + __ jcc(Assembler::equal, skip); + __ leal(rHandle, Address(rsp, offset)); + __ bind(skip); + // Store the handle parameter + __ movl(Address(rsp, reg2offset_out(dst.first())), rHandle); + if (is_receiver) { + *receiver_offset = offset; + } + } +} + +// A float arg may have to do float reg int reg conversion +static void float_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + assert(!src.second()->is_valid() && !dst.second()->is_valid(), "bad float_move"); + + // Because of the calling convention we know that src is either a stack location + // or an xmm register. dst can only be a stack location. + + assert(dst.first()->is_stack() && ( src.first()->is_stack() || src.first()->is_XMMRegister()), "bad parameters"); + + if (src.first()->is_stack()) { + __ movl(rax, Address(rbp, reg2offset_in(src.first()))); + __ movl(Address(rsp, reg2offset_out(dst.first())), rax); + } else { + // reg to stack + __ movflt(Address(rsp, reg2offset_out(dst.first())), src.first()->as_XMMRegister()); + } +} + +// A long move +static void long_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + + // The only legal possibility for a long_move VMRegPair is: + // 1: two stack slots (possibly unaligned) + // as neither the java or C calling convention will use registers + // for longs. + + if (src.first()->is_stack() && dst.first()->is_stack()) { + assert(src.second()->is_stack() && dst.second()->is_stack(), "must be all stack"); + __ movl(rax, Address(rbp, reg2offset_in(src.first()))); + __ movl(rbx, Address(rbp, reg2offset_in(src.second()))); + __ movl(Address(rsp, reg2offset_out(dst.first())), rax); + __ movl(Address(rsp, reg2offset_out(dst.second())), rbx); + } else { + ShouldNotReachHere(); + } +} + +// A double move +static void double_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + + // The only legal possibilities for a double_move VMRegPair are: + // The painful thing here is that like long_move a VMRegPair might be + + // Because of the calling convention we know that src is either + // 1: a single physical register (xmm registers only) + // 2: two stack slots (possibly unaligned) + // dst can only be a pair of stack slots. + + assert(dst.first()->is_stack() && (src.first()->is_XMMRegister() || src.first()->is_stack()), "bad args"); + + if (src.first()->is_stack()) { + // source is all stack + __ movl(rax, Address(rbp, reg2offset_in(src.first()))); + __ movl(rbx, Address(rbp, reg2offset_in(src.second()))); + __ movl(Address(rsp, reg2offset_out(dst.first())), rax); + __ movl(Address(rsp, reg2offset_out(dst.second())), rbx); + } else { + // reg to stack + // No worries about stack alignment + __ movdbl(Address(rsp, reg2offset_out(dst.first())), src.first()->as_XMMRegister()); + } +} + + +void SharedRuntime::save_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { + // We always ignore the frame_slots arg and just use the space just below frame pointer + // which by this time is free to use + switch (ret_type) { + case T_FLOAT: + __ fstp_s(Address(rbp, -wordSize)); + break; + case T_DOUBLE: + __ fstp_d(Address(rbp, -2*wordSize)); + break; + case T_VOID: break; + case T_LONG: + __ movl(Address(rbp, -wordSize), rax); + __ movl(Address(rbp, -2*wordSize), rdx); + break; + default: { + __ movl(Address(rbp, -wordSize), rax); + } + } +} + +void SharedRuntime::restore_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { + // We always ignore the frame_slots arg and just use the space just below frame pointer + // which by this time is free to use + switch (ret_type) { + case T_FLOAT: + __ fld_s(Address(rbp, -wordSize)); + break; + case T_DOUBLE: + __ fld_d(Address(rbp, -2*wordSize)); + break; + case T_LONG: + __ movl(rax, Address(rbp, -wordSize)); + __ movl(rdx, Address(rbp, -2*wordSize)); + break; + case T_VOID: break; + default: { + __ movl(rax, Address(rbp, -wordSize)); + } + } +} + +// --------------------------------------------------------------------------- +// Generate a native wrapper for a given method. The method takes arguments +// in the Java compiled code convention, marshals them to the native +// convention (handlizes oops, etc), transitions to native, makes the call, +// returns to java state (possibly blocking), unhandlizes any result and +// returns. +nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, + methodHandle method, + int total_in_args, + int comp_args_on_stack, + BasicType *in_sig_bt, + VMRegPair *in_regs, + BasicType ret_type) { + + // An OopMap for lock (and class if static) + OopMapSet *oop_maps = new OopMapSet(); + + // We have received a description of where all the java arg are located + // on entry to the wrapper. We need to convert these args to where + // the jni function will expect them. To figure out where they go + // we convert the java signature to a C signature by inserting + // the hidden arguments as arg[0] and possibly arg[1] (static method) + + int total_c_args = total_in_args + 1; + if (method->is_static()) { + total_c_args++; + } + + BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_c_args); + VMRegPair* out_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_c_args); + + int argc = 0; + out_sig_bt[argc++] = T_ADDRESS; + if (method->is_static()) { + out_sig_bt[argc++] = T_OBJECT; + } + + int i; + for (i = 0; i < total_in_args ; i++ ) { + out_sig_bt[argc++] = in_sig_bt[i]; + } + + + // Now figure out where the args must be stored and how much stack space + // they require (neglecting out_preserve_stack_slots but space for storing + // the 1st six register arguments). It's weird see int_stk_helper. + // + int out_arg_slots; + out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args); + + // Compute framesize for the wrapper. We need to handlize all oops in + // registers a max of 2 on x86. + + // Calculate the total number of stack slots we will need. + + // First count the abi requirement plus all of the outgoing args + int stack_slots = SharedRuntime::out_preserve_stack_slots() + out_arg_slots; + + // Now the space for the inbound oop handle area + + int oop_handle_offset = stack_slots; + stack_slots += 2*VMRegImpl::slots_per_word; + + // Now any space we need for handlizing a klass if static method + + int klass_slot_offset = 0; + int klass_offset = -1; + int lock_slot_offset = 0; + bool is_static = false; + int oop_temp_slot_offset = 0; + + if (method->is_static()) { + klass_slot_offset = stack_slots; + stack_slots += VMRegImpl::slots_per_word; + klass_offset = klass_slot_offset * VMRegImpl::stack_slot_size; + is_static = true; + } + + // Plus a lock if needed + + if (method->is_synchronized()) { + lock_slot_offset = stack_slots; + stack_slots += VMRegImpl::slots_per_word; + } + + // Now a place (+2) to save return values or temp during shuffling + // + 2 for return address (which we own) and saved rbp, + stack_slots += 4; + + // Ok The space we have allocated will look like: + // + // + // FP-> | | + // |---------------------| + // | 2 slots for moves | + // |---------------------| + // | lock box (if sync) | + // |---------------------| <- lock_slot_offset (-lock_slot_rbp_offset) + // | klass (if static) | + // |---------------------| <- klass_slot_offset + // | oopHandle area | + // |---------------------| <- oop_handle_offset (a max of 2 registers) + // | outbound memory | + // | based arguments | + // | | + // |---------------------| + // | | + // SP-> | out_preserved_slots | + // + // + // **************************************************************************** + // WARNING - on Windows Java Natives use pascal calling convention and pop the + // arguments off of the stack after the jni call. Before the call we can use + // instructions that are SP relative. After the jni call we switch to FP + // relative instructions instead of re-adjusting the stack on windows. + // **************************************************************************** + + + // Now compute actual number of stack words we need rounding to make + // stack properly aligned. + stack_slots = round_to(stack_slots, 2 * VMRegImpl::slots_per_word); + + int stack_size = stack_slots * VMRegImpl::stack_slot_size; + + intptr_t start = (intptr_t)__ pc(); + + // First thing make an ic check to see if we should even be here + + // We are free to use all registers as temps without saving them and + // restoring them except rbp,. rbp, is the only callee save register + // as far as the interpreter and the compiler(s) are concerned. + + + const Register ic_reg = rax; + const Register receiver = rcx; + Label hit; + Label exception_pending; + + + __ verify_oop(receiver); + __ cmpl(ic_reg, Address(receiver, oopDesc::klass_offset_in_bytes())); + __ jcc(Assembler::equal, hit); + + __ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub())); + + // verified entry must be aligned for code patching. + // and the first 5 bytes must be in the same cache line + // if we align at 8 then we will be sure 5 bytes are in the same line + __ align(8); + + __ bind(hit); + + int vep_offset = ((intptr_t)__ pc()) - start; + +#ifdef COMPILER1 + if (InlineObjectHash && method->intrinsic_id() == vmIntrinsics::_hashCode) { + // Object.hashCode can pull the hashCode from the header word + // instead of doing a full VM transition once it's been computed. + // Since hashCode is usually polymorphic at call sites we can't do + // this optimization at the call site without a lot of work. + Label slowCase; + Register receiver = rcx; + Register result = rax; + __ movl(result, Address(receiver, oopDesc::mark_offset_in_bytes())); + + // check if locked + __ testl (result, markOopDesc::unlocked_value); + __ jcc (Assembler::zero, slowCase); + + if (UseBiasedLocking) { + // Check if biased and fall through to runtime if so + __ testl (result, markOopDesc::biased_lock_bit_in_place); + __ jcc (Assembler::notZero, slowCase); + } + + // get hash + __ andl (result, markOopDesc::hash_mask_in_place); + // test if hashCode exists + __ jcc (Assembler::zero, slowCase); + __ shrl (result, markOopDesc::hash_shift); + __ ret(0); + __ bind (slowCase); + } +#endif // COMPILER1 + + // The instruction at the verified entry point must be 5 bytes or longer + // because it can be patched on the fly by make_non_entrant. The stack bang + // instruction fits that requirement. + + // Generate stack overflow check + + if (UseStackBanging) { + __ bang_stack_with_offset(StackShadowPages*os::vm_page_size()); + } else { + // need a 5 byte instruction to allow MT safe patching to non-entrant + __ fat_nop(); + } + + // Generate a new frame for the wrapper. + __ enter(); + // -2 because return address is already present and so is saved rbp, + __ subl(rsp, stack_size - 2*wordSize); + + // Frame is now completed as far a size and linkage. + + int frame_complete = ((intptr_t)__ pc()) - start; + + // Calculate the difference between rsp and rbp,. We need to know it + // after the native call because on windows Java Natives will pop + // the arguments and it is painful to do rsp relative addressing + // in a platform independent way. So after the call we switch to + // rbp, relative addressing. + + int fp_adjustment = stack_size - 2*wordSize; + +#ifdef COMPILER2 + // C2 may leave the stack dirty if not in SSE2+ mode + if (UseSSE >= 2) { + __ verify_FPU(0, "c2i transition should have clean FPU stack"); + } else { + __ empty_FPU_stack(); + } +#endif /* COMPILER2 */ + + // Compute the rbp, offset for any slots used after the jni call + + int lock_slot_rbp_offset = (lock_slot_offset*VMRegImpl::stack_slot_size) - fp_adjustment; + int oop_temp_slot_rbp_offset = (oop_temp_slot_offset*VMRegImpl::stack_slot_size) - fp_adjustment; + + // We use rdi as a thread pointer because it is callee save and + // if we load it once it is usable thru the entire wrapper + const Register thread = rdi; + + // We use rsi as the oop handle for the receiver/klass + // It is callee save so it survives the call to native + + const Register oop_handle_reg = rsi; + + __ get_thread(thread); + + + // + // We immediately shuffle the arguments so that any vm call we have to + // make from here on out (sync slow path, jvmti, etc.) we will have + // captured the oops from our caller and have a valid oopMap for + // them. + + // ----------------- + // The Grand Shuffle + // + // Natives require 1 or 2 extra arguments over the normal ones: the JNIEnv* + // and, if static, the class mirror instead of a receiver. This pretty much + // guarantees that register layout will not match (and x86 doesn't use reg + // parms though amd does). Since the native abi doesn't use register args + // and the java conventions does we don't have to worry about collisions. + // All of our moved are reg->stack or stack->stack. + // We ignore the extra arguments during the shuffle and handle them at the + // last moment. The shuffle is described by the two calling convention + // vectors we have in our possession. We simply walk the java vector to + // get the source locations and the c vector to get the destinations. + + int c_arg = method->is_static() ? 2 : 1 ; + + // Record rsp-based slot for receiver on stack for non-static methods + int receiver_offset = -1; + + // This is a trick. We double the stack slots so we can claim + // the oops in the caller's frame. Since we are sure to have + // more args than the caller doubling is enough to make + // sure we can capture all the incoming oop args from the + // caller. + // + OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); + + // Mark location of rbp, + // map->set_callee_saved(VMRegImpl::stack2reg( stack_slots - 2), stack_slots * 2, 0, rbp->as_VMReg()); + + // We know that we only have args in at most two integer registers (rcx, rdx). So rax, rbx + // Are free to temporaries if we have to do stack to steck moves. + // All inbound args are referenced based on rbp, and all outbound args via rsp. + + for (i = 0; i < total_in_args ; i++, c_arg++ ) { + switch (in_sig_bt[i]) { + case T_ARRAY: + case T_OBJECT: + object_move(masm, map, oop_handle_offset, stack_slots, in_regs[i], out_regs[c_arg], + ((i == 0) && (!is_static)), + &receiver_offset); + break; + case T_VOID: + break; + + case T_FLOAT: + float_move(masm, in_regs[i], out_regs[c_arg]); + break; + + case T_DOUBLE: + assert( i + 1 < total_in_args && + in_sig_bt[i + 1] == T_VOID && + out_sig_bt[c_arg+1] == T_VOID, "bad arg list"); + double_move(masm, in_regs[i], out_regs[c_arg]); + break; + + case T_LONG : + long_move(masm, in_regs[i], out_regs[c_arg]); + break; + + case T_ADDRESS: assert(false, "found T_ADDRESS in java args"); + + default: + simple_move32(masm, in_regs[i], out_regs[c_arg]); + } + } + + // Pre-load a static method's oop into rsi. Used both by locking code and + // the normal JNI call code. + if (method->is_static()) { + + // load opp into a register + __ movoop(oop_handle_reg, JNIHandles::make_local(Klass::cast(method->method_holder())->java_mirror())); + + // Now handlize the static class mirror it's known not-null. + __ movl(Address(rsp, klass_offset), oop_handle_reg); + map->set_oop(VMRegImpl::stack2reg(klass_slot_offset)); + + // Now get the handle + __ leal(oop_handle_reg, Address(rsp, klass_offset)); + // store the klass handle as second argument + __ movl(Address(rsp, wordSize), oop_handle_reg); + } + + // Change state to native (we save the return address in the thread, since it might not + // be pushed on the stack when we do a a stack traversal). It is enough that the pc() + // points into the right code segment. It does not have to be the correct return pc. + // We use the same pc/oopMap repeatedly when we call out + + intptr_t the_pc = (intptr_t) __ pc(); + oop_maps->add_gc_map(the_pc - start, map); + + __ set_last_Java_frame(thread, rsp, noreg, (address)the_pc); + + + // We have all of the arguments setup at this point. We must not touch any register + // argument registers at this point (what if we save/restore them there are no oop? + + { + SkipIfEqual skip_if(masm, &DTraceMethodProbes, 0); + __ movoop(rax, JNIHandles::make_local(method())); + __ call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), + thread, rax); + } + + + // These are register definitions we need for locking/unlocking + const Register swap_reg = rax; // Must use rax, for cmpxchg instruction + const Register obj_reg = rcx; // Will contain the oop + const Register lock_reg = rdx; // Address of compiler lock object (BasicLock) + + Label slow_path_lock; + Label lock_done; + + // Lock a synchronized method + if (method->is_synchronized()) { + + + const int mark_word_offset = BasicLock::displaced_header_offset_in_bytes(); + + // Get the handle (the 2nd argument) + __ movl(oop_handle_reg, Address(rsp, wordSize)); + + // Get address of the box + + __ leal(lock_reg, Address(rbp, lock_slot_rbp_offset)); + + // Load the oop from the handle + __ movl(obj_reg, Address(oop_handle_reg, 0)); + + if (UseBiasedLocking) { + // Note that oop_handle_reg is trashed during this call + __ biased_locking_enter(lock_reg, obj_reg, swap_reg, oop_handle_reg, false, lock_done, &slow_path_lock); + } + + // Load immediate 1 into swap_reg %rax, + __ movl(swap_reg, 1); + + // Load (object->mark() | 1) into swap_reg %rax, + __ orl(swap_reg, Address(obj_reg, 0)); + + // Save (object->mark() | 1) into BasicLock's displaced header + __ movl(Address(lock_reg, mark_word_offset), swap_reg); + + if (os::is_MP()) { + __ lock(); + } + + // src -> dest iff dest == rax, else rax, <- dest + // *obj_reg = lock_reg iff *obj_reg == rax, else rax, = *(obj_reg) + __ cmpxchg(lock_reg, Address(obj_reg, 0)); + __ jcc(Assembler::equal, lock_done); + + // Test if the oopMark is an obvious stack pointer, i.e., + // 1) (mark & 3) == 0, and + // 2) rsp <= mark < mark + os::pagesize() + // These 3 tests can be done by evaluating the following + // expression: ((mark - rsp) & (3 - os::vm_page_size())), + // assuming both stack pointer and pagesize have their + // least significant 2 bits clear. + // NOTE: the oopMark is in swap_reg %rax, as the result of cmpxchg + + __ subl(swap_reg, rsp); + __ andl(swap_reg, 3 - os::vm_page_size()); + + // Save the test result, for recursive case, the result is zero + __ movl(Address(lock_reg, mark_word_offset), swap_reg); + __ jcc(Assembler::notEqual, slow_path_lock); + // Slow path will re-enter here + __ bind(lock_done); + + if (UseBiasedLocking) { + // Re-fetch oop_handle_reg as we trashed it above + __ movl(oop_handle_reg, Address(rsp, wordSize)); + } + } + + + // Finally just about ready to make the JNI call + + + // get JNIEnv* which is first argument to native + + __ leal(rdx, Address(thread, in_bytes(JavaThread::jni_environment_offset()))); + __ movl(Address(rsp, 0), rdx); + + // Now set thread in native + __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_native); + + __ call(RuntimeAddress(method->native_function())); + + // WARNING - on Windows Java Natives use pascal calling convention and pop the + // arguments off of the stack. We could just re-adjust the stack pointer here + // and continue to do SP relative addressing but we instead switch to FP + // relative addressing. + + // Unpack native results. + switch (ret_type) { + case T_BOOLEAN: __ c2bool(rax); break; + case T_CHAR : __ andl(rax, 0xFFFF); break; + case T_BYTE : __ sign_extend_byte (rax); break; + case T_SHORT : __ sign_extend_short(rax); break; + case T_INT : /* nothing to do */ break; + case T_DOUBLE : + case T_FLOAT : + // Result is in st0 we'll save as needed + break; + case T_ARRAY: // Really a handle + case T_OBJECT: // Really a handle + break; // can't de-handlize until after safepoint check + case T_VOID: break; + case T_LONG: break; + default : ShouldNotReachHere(); + } + + // Switch thread to "native transition" state before reading the synchronization state. + // This additional state is necessary because reading and testing the synchronization + // state is not atomic w.r.t. GC, as this scenario demonstrates: + // Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. + // VM thread changes sync state to synchronizing and suspends threads for GC. + // Thread A is resumed to finish this native method, but doesn't block here since it + // didn't see any synchronization is progress, and escapes. + __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_native_trans); + + if(os::is_MP()) { + if (UseMembar) { + __ membar(); // Force this write out before the read below + } else { + // Write serialization page so VM thread can do a pseudo remote membar. + // We use the current thread pointer to calculate a thread specific + // offset to write to within the page. This minimizes bus traffic + // due to cache line collision. + __ serialize_memory(thread, rcx); + } + } + + if (AlwaysRestoreFPU) { + // Make sure the control word is correct. + __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); + } + + // check for safepoint operation in progress and/or pending suspend requests + { Label Continue; + + __ cmp32(ExternalAddress((address)SafepointSynchronize::address_of_state()), + SafepointSynchronize::_not_synchronized); + + Label L; + __ jcc(Assembler::notEqual, L); + __ cmpl(Address(thread, JavaThread::suspend_flags_offset()), 0); + __ jcc(Assembler::equal, Continue); + __ bind(L); + + // Don't use call_VM as it will see a possible pending exception and forward it + // and never return here preventing us from clearing _last_native_pc down below. + // Also can't use call_VM_leaf either as it will check to see if rsi & rdi are + // preserved and correspond to the bcp/locals pointers. So we do a runtime call + // by hand. + // + save_native_result(masm, ret_type, stack_slots); + __ pushl(thread); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, + JavaThread::check_special_condition_for_native_trans))); + __ increment(rsp, wordSize); + // Restore any method result value + restore_native_result(masm, ret_type, stack_slots); + + __ bind(Continue); + } + + // change thread state + __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_Java); + + Label reguard; + Label reguard_done; + __ cmpl(Address(thread, JavaThread::stack_guard_state_offset()), JavaThread::stack_guard_yellow_disabled); + __ jcc(Assembler::equal, reguard); + + // slow path reguard re-enters here + __ bind(reguard_done); + + // Handle possible exception (will unlock if necessary) + + // native result if any is live + + // Unlock + Label slow_path_unlock; + Label unlock_done; + if (method->is_synchronized()) { + + Label done; + + // Get locked oop from the handle we passed to jni + __ movl(obj_reg, Address(oop_handle_reg, 0)); + + if (UseBiasedLocking) { + __ biased_locking_exit(obj_reg, rbx, done); + } + + // Simple recursive lock? + + __ cmpl(Address(rbp, lock_slot_rbp_offset), NULL_WORD); + __ jcc(Assembler::equal, done); + + // Must save rax, if if it is live now because cmpxchg must use it + if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { + save_native_result(masm, ret_type, stack_slots); + } + + // get old displaced header + __ movl(rbx, Address(rbp, lock_slot_rbp_offset)); + + // get address of the stack lock + __ leal(rax, Address(rbp, lock_slot_rbp_offset)); + + // Atomic swap old header if oop still contains the stack lock + if (os::is_MP()) { + __ lock(); + } + + // src -> dest iff dest == rax, else rax, <- dest + // *obj_reg = rbx, iff *obj_reg == rax, else rax, = *(obj_reg) + __ cmpxchg(rbx, Address(obj_reg, 0)); + __ jcc(Assembler::notEqual, slow_path_unlock); + + // slow path re-enters here + __ bind(unlock_done); + if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { + restore_native_result(masm, ret_type, stack_slots); + } + + __ bind(done); + + } + + { + SkipIfEqual skip_if(masm, &DTraceMethodProbes, 0); + // Tell dtrace about this method exit + save_native_result(masm, ret_type, stack_slots); + __ movoop(rax, JNIHandles::make_local(method())); + __ call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), + thread, rax); + restore_native_result(masm, ret_type, stack_slots); + } + + // We can finally stop using that last_Java_frame we setup ages ago + + __ reset_last_Java_frame(thread, false, true); + + // Unpack oop result + if (ret_type == T_OBJECT || ret_type == T_ARRAY) { + Label L; + __ cmpl(rax, NULL_WORD); + __ jcc(Assembler::equal, L); + __ movl(rax, Address(rax, 0)); + __ bind(L); + __ verify_oop(rax); + } + + // reset handle block + __ movl(rcx, Address(thread, JavaThread::active_handles_offset())); + + __ movl(Address(rcx, JNIHandleBlock::top_offset_in_bytes()), 0); + + // Any exception pending? + __ cmpl(Address(thread, in_bytes(Thread::pending_exception_offset())), NULL_WORD); + __ jcc(Assembler::notEqual, exception_pending); + + + // no exception, we're almost done + + // check that only result value is on FPU stack + __ verify_FPU(ret_type == T_FLOAT || ret_type == T_DOUBLE ? 1 : 0, "native_wrapper normal exit"); + + // Fixup floating pointer results so that result looks like a return from a compiled method + if (ret_type == T_FLOAT) { + if (UseSSE >= 1) { + // Pop st0 and store as float and reload into xmm register + __ fstp_s(Address(rbp, -4)); + __ movflt(xmm0, Address(rbp, -4)); + } + } else if (ret_type == T_DOUBLE) { + if (UseSSE >= 2) { + // Pop st0 and store as double and reload into xmm register + __ fstp_d(Address(rbp, -8)); + __ movdbl(xmm0, Address(rbp, -8)); + } + } + + // Return + + __ leave(); + __ ret(0); + + // Unexpected paths are out of line and go here + + // Slow path locking & unlocking + if (method->is_synchronized()) { + + // BEGIN Slow path lock + + __ bind(slow_path_lock); + + // has last_Java_frame setup. No exceptions so do vanilla call not call_VM + // args are (oop obj, BasicLock* lock, JavaThread* thread) + __ pushl(thread); + __ pushl(lock_reg); + __ pushl(obj_reg); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C))); + __ addl(rsp, 3*wordSize); + +#ifdef ASSERT + { Label L; + __ cmpl(Address(thread, in_bytes(Thread::pending_exception_offset())), (int)NULL_WORD); + __ jcc(Assembler::equal, L); + __ stop("no pending exception allowed on exit from monitorenter"); + __ bind(L); + } +#endif + __ jmp(lock_done); + + // END Slow path lock + + // BEGIN Slow path unlock + __ bind(slow_path_unlock); + + // Slow path unlock + + if (ret_type == T_FLOAT || ret_type == T_DOUBLE ) { + save_native_result(masm, ret_type, stack_slots); + } + // Save pending exception around call to VM (which contains an EXCEPTION_MARK) + + __ pushl(Address(thread, in_bytes(Thread::pending_exception_offset()))); + __ movl(Address(thread, in_bytes(Thread::pending_exception_offset())), NULL_WORD); + + + // should be a peal + // +wordSize because of the push above + __ leal(rax, Address(rbp, lock_slot_rbp_offset)); + __ pushl(rax); + + __ pushl(obj_reg); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_unlocking_C))); + __ addl(rsp, 2*wordSize); +#ifdef ASSERT + { + Label L; + __ cmpl(Address(thread, in_bytes(Thread::pending_exception_offset())), NULL_WORD); + __ jcc(Assembler::equal, L); + __ stop("no pending exception allowed on exit complete_monitor_unlocking_C"); + __ bind(L); + } +#endif /* ASSERT */ + + __ popl(Address(thread, in_bytes(Thread::pending_exception_offset()))); + + if (ret_type == T_FLOAT || ret_type == T_DOUBLE ) { + restore_native_result(masm, ret_type, stack_slots); + } + __ jmp(unlock_done); + // END Slow path unlock + + } + + // SLOW PATH Reguard the stack if needed + + __ bind(reguard); + save_native_result(masm, ret_type, stack_slots); + { + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages))); + } + restore_native_result(masm, ret_type, stack_slots); + __ jmp(reguard_done); + + + // BEGIN EXCEPTION PROCESSING + + // Forward the exception + __ bind(exception_pending); + + // remove possible return value from FPU register stack + __ empty_FPU_stack(); + + // pop our frame + __ leave(); + // and forward the exception + __ jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + + __ flush(); + + nmethod *nm = nmethod::new_native_nmethod(method, + masm->code(), + vep_offset, + frame_complete, + stack_slots / VMRegImpl::slots_per_word, + (is_static ? in_ByteSize(klass_offset) : in_ByteSize(receiver_offset)), + in_ByteSize(lock_slot_offset*VMRegImpl::stack_slot_size), + oop_maps); + return nm; + +} + +// this function returns the adjust size (in number of words) to a c2i adapter +// activation for use during deoptimization +int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals ) { + return (callee_locals - callee_parameters) * Interpreter::stackElementWords(); +} + + +uint SharedRuntime::out_preserve_stack_slots() { + return 0; +} + + +//------------------------------generate_deopt_blob---------------------------- +void SharedRuntime::generate_deopt_blob() { + // allocate space for the code + ResourceMark rm; + // setup code generation tools + CodeBuffer buffer("deopt_blob", 1024, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + int frame_size_in_words; + OopMap* map = NULL; + // Account for the extra args we place on the stack + // by the time we call fetch_unroll_info + const int additional_words = 2; // deopt kind, thread + + OopMapSet *oop_maps = new OopMapSet(); + + // ------------- + // This code enters when returning to a de-optimized nmethod. A return + // address has been pushed on the the stack, and return values are in + // registers. + // If we are doing a normal deopt then we were called from the patched + // nmethod from the point we returned to the nmethod. So the return + // address on the stack is wrong by NativeCall::instruction_size + // We will adjust the value to it looks like we have the original return + // address on the stack (like when we eagerly deoptimized). + // In the case of an exception pending with deoptimized then we enter + // with a return address on the stack that points after the call we patched + // into the exception handler. We have the following register state: + // rax,: exception + // rbx,: exception handler + // rdx: throwing pc + // So in this case we simply jam rdx into the useless return address and + // the stack looks just like we want. + // + // At this point we need to de-opt. We save the argument return + // registers. We call the first C routine, fetch_unroll_info(). This + // routine captures the return values and returns a structure which + // describes the current frame size and the sizes of all replacement frames. + // The current frame is compiled code and may contain many inlined + // functions, each with their own JVM state. We pop the current frame, then + // push all the new frames. Then we call the C routine unpack_frames() to + // populate these frames. Finally unpack_frames() returns us the new target + // address. Notice that callee-save registers are BLOWN here; they have + // already been captured in the vframeArray at the time the return PC was + // patched. + address start = __ pc(); + Label cont; + + // Prolog for non exception case! + + // Save everything in sight. + + map = RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words); + // Normal deoptimization + __ pushl(Deoptimization::Unpack_deopt); + __ jmp(cont); + + int reexecute_offset = __ pc() - start; + + // Reexecute case + // return address is the pc describes what bci to do re-execute at + + // No need to update map as each call to save_live_registers will produce identical oopmap + (void) RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words); + + __ pushl(Deoptimization::Unpack_reexecute); + __ jmp(cont); + + int exception_offset = __ pc() - start; + + // Prolog for exception case + + // all registers are dead at this entry point, except for rax, and + // rdx which contain the exception oop and exception pc + // respectively. Set them in TLS and fall thru to the + // unpack_with_exception_in_tls entry point. + + __ get_thread(rdi); + __ movl(Address(rdi, JavaThread::exception_pc_offset()), rdx); + __ movl(Address(rdi, JavaThread::exception_oop_offset()), rax); + + int exception_in_tls_offset = __ pc() - start; + + // new implementation because exception oop is now passed in JavaThread + + // Prolog for exception case + // All registers must be preserved because they might be used by LinearScan + // Exceptiop oop and throwing PC are passed in JavaThread + // tos: stack at point of call to method that threw the exception (i.e. only + // args are on the stack, no return address) + + // make room on stack for the return address + // It will be patched later with the throwing pc. The correct value is not + // available now because loading it from memory would destroy registers. + __ pushl(0); + + // Save everything in sight. + + // No need to update map as each call to save_live_registers will produce identical oopmap + (void) RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words); + + // Now it is safe to overwrite any register + + // store the correct deoptimization type + __ pushl(Deoptimization::Unpack_exception); + + // load throwing pc from JavaThread and patch it as the return address + // of the current frame. Then clear the field in JavaThread + __ get_thread(rdi); + __ movl(rdx, Address(rdi, JavaThread::exception_pc_offset())); + __ movl(Address(rbp, wordSize), rdx); + __ movl(Address(rdi, JavaThread::exception_pc_offset()), NULL_WORD); + +#ifdef ASSERT + // verify that there is really an exception oop in JavaThread + __ movl(rax, Address(rdi, JavaThread::exception_oop_offset())); + __ verify_oop(rax); + + // verify that there is no pending exception + Label no_pending_exception; + __ movl(rax, Address(rdi, Thread::pending_exception_offset())); + __ testl(rax, rax); + __ jcc(Assembler::zero, no_pending_exception); + __ stop("must not have pending exception here"); + __ bind(no_pending_exception); +#endif + + __ bind(cont); + + // Compiled code leaves the floating point stack dirty, empty it. + __ empty_FPU_stack(); + + + // Call C code. Need thread and this frame, but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. + __ get_thread(rcx); + __ pushl(rcx); + // fetch_unroll_info needs to call last_java_frame() + __ set_last_Java_frame(rcx, noreg, noreg, NULL); + + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info))); + + // Need to have an oopmap that tells fetch_unroll_info where to + // find any register it might need. + + oop_maps->add_gc_map( __ pc()-start, map); + + // Discard arg to fetch_unroll_info + __ popl(rcx); + + __ get_thread(rcx); + __ reset_last_Java_frame(rcx, false, false); + + // Load UnrollBlock into EDI + __ movl(rdi, rax); + + // Move the unpack kind to a safe place in the UnrollBlock because + // we are very short of registers + + Address unpack_kind(rdi, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes()); + // retrieve the deopt kind from where we left it. + __ popl(rax); + __ movl(unpack_kind, rax); // save the unpack_kind value + + Label noException; + __ cmpl(rax, Deoptimization::Unpack_exception); // Was exception pending? + __ jcc(Assembler::notEqual, noException); + __ movl(rax, Address(rcx, JavaThread::exception_oop_offset())); + __ movl(rdx, Address(rcx, JavaThread::exception_pc_offset())); + __ movl(Address(rcx, JavaThread::exception_oop_offset()), NULL_WORD); + __ movl(Address(rcx, JavaThread::exception_pc_offset()), NULL_WORD); + + __ verify_oop(rax); + + // Overwrite the result registers with the exception results. + __ movl(Address(rsp, RegisterSaver::raxOffset()*wordSize), rax); + __ movl(Address(rsp, RegisterSaver::rdxOffset()*wordSize), rdx); + + __ bind(noException); + + // Stack is back to only having register save data on the stack. + // Now restore the result registers. Everything else is either dead or captured + // in the vframeArray. + + RegisterSaver::restore_result_registers(masm); + + // All of the register save area has been popped of the stack. Only the + // return address remains. + + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + // + // Note: by leaving the return address of self-frame on the stack + // and using the size of frame 2 to adjust the stack + // when we are done the return to frame 3 will still be on the stack. + + // Pop deoptimized frame + __ addl(rsp,Address(rdi,Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset_in_bytes())); + + // sp should be pointing at the return address to the caller (3) + + // Stack bang to make sure there's enough room for these interpreter frames. + if (UseStackBanging) { + __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset_in_bytes())); + __ bang_stack_size(rbx, rcx); + } + + // Load array of frame pcs into ECX + __ movl(rcx,Address(rdi,Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes())); + + __ popl(rsi); // trash the old pc + + // Load array of frame sizes into ESI + __ movl(rsi,Address(rdi,Deoptimization::UnrollBlock::frame_sizes_offset_in_bytes())); + + Address counter(rdi, Deoptimization::UnrollBlock::counter_temp_offset_in_bytes()); + + __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::number_of_frames_offset_in_bytes())); + __ movl(counter, rbx); + + // Pick up the initial fp we should save + __ movl(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_fp_offset_in_bytes())); + + // Now adjust the caller's stack to make up for the extra locals + // but record the original sp so that we can save it in the skeletal interpreter + // frame and the stack walking of interpreter_sender will get the unextended sp + // value and not the "real" sp value. + + Address sp_temp(rdi, Deoptimization::UnrollBlock::sender_sp_temp_offset_in_bytes()); + __ movl(sp_temp, rsp); + __ subl(rsp, Address(rdi, Deoptimization::UnrollBlock::caller_adjustment_offset_in_bytes())); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ movl(rbx, Address(rsi, 0)); // Load frame size +#ifdef CC_INTERP + __ subl(rbx, 4*wordSize); // we'll push pc and ebp by hand and +#ifdef ASSERT + __ pushl(0xDEADDEAD); // Make a recognizable pattern + __ pushl(0xDEADDEAD); +#else /* ASSERT */ + __ subl(rsp, 2*wordSize); // skip the "static long no_param" +#endif /* ASSERT */ +#else /* CC_INTERP */ + __ subl(rbx, 2*wordSize); // we'll push pc and rbp, by hand +#endif /* CC_INTERP */ + __ pushl(Address(rcx, 0)); // save return address + __ enter(); // save old & set new rbp, + __ subl(rsp, rbx); // Prolog! + __ movl(rbx, sp_temp); // sender's sp +#ifdef CC_INTERP + __ movl(Address(rbp, + -(sizeof(BytecodeInterpreter)) + in_bytes(byte_offset_of(BytecodeInterpreter, _sender_sp))), + rbx); // Make it walkable +#else /* CC_INTERP */ + // This value is corrected by layout_activation_impl + __ movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD ); + __ movl(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), rbx); // Make it walkable +#endif /* CC_INTERP */ + __ movl(sp_temp, rsp); // pass to next frame + __ addl(rsi, 4); // Bump array pointer (sizes) + __ addl(rcx, 4); // Bump array pointer (pcs) + __ decrement(counter); // decrement counter + __ jcc(Assembler::notZero, loop); + __ pushl(Address(rcx, 0)); // save final return address + + // Re-push self-frame + __ enter(); // save old & set new rbp, + + // Return address and rbp, are in place + // We'll push additional args later. Just allocate a full sized + // register save area + __ subl(rsp, (frame_size_in_words-additional_words - 2) * wordSize); + + // Restore frame locals after moving the frame + __ movl(Address(rsp, RegisterSaver::raxOffset()*wordSize), rax); + __ movl(Address(rsp, RegisterSaver::rdxOffset()*wordSize), rdx); + __ fstp_d(Address(rsp, RegisterSaver::fpResultOffset()*wordSize)); // Pop float stack and store in local + if( UseSSE>=2 ) __ movdbl(Address(rsp, RegisterSaver::xmm0Offset()*wordSize), xmm0); + if( UseSSE==1 ) __ movflt(Address(rsp, RegisterSaver::xmm0Offset()*wordSize), xmm0); + + // Set up the args to unpack_frame + + __ pushl(unpack_kind); // get the unpack_kind value + __ get_thread(rcx); + __ pushl(rcx); + + // set last_Java_sp, last_Java_fp + __ set_last_Java_frame(rcx, noreg, rbp, NULL); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); + // Set an oopmap for the call site + oop_maps->add_gc_map( __ pc()-start, new OopMap( frame_size_in_words, 0 )); + + // rax, contains the return result type + __ pushl(rax); + + __ get_thread(rcx); + __ reset_last_Java_frame(rcx, false, false); + + // Collect return values + __ movl(rax,Address(rsp, (RegisterSaver::raxOffset() + additional_words + 1)*wordSize)); + __ movl(rdx,Address(rsp, (RegisterSaver::rdxOffset() + additional_words + 1)*wordSize)); + + // Clear floating point stack before returning to interpreter + __ empty_FPU_stack(); + + // Check if we should push the float or double return value. + Label results_done, yes_double_value; + __ cmpl(Address(rsp, 0), T_DOUBLE); + __ jcc (Assembler::zero, yes_double_value); + __ cmpl(Address(rsp, 0), T_FLOAT); + __ jcc (Assembler::notZero, results_done); + + // return float value as expected by interpreter + if( UseSSE>=1 ) __ movflt(xmm0, Address(rsp, (RegisterSaver::xmm0Offset() + additional_words + 1)*wordSize)); + else __ fld_d(Address(rsp, (RegisterSaver::fpResultOffset() + additional_words + 1)*wordSize)); + __ jmp(results_done); + + // return double value as expected by interpreter + __ bind(yes_double_value); + if( UseSSE>=2 ) __ movdbl(xmm0, Address(rsp, (RegisterSaver::xmm0Offset() + additional_words + 1)*wordSize)); + else __ fld_d(Address(rsp, (RegisterSaver::fpResultOffset() + additional_words + 1)*wordSize)); + + __ bind(results_done); + + // Pop self-frame. + __ leave(); // Epilog! + + // Jump to interpreter + __ ret(0); + + // ------------- + // make sure all code is generated + masm->flush(); + + _deopt_blob = DeoptimizationBlob::create( &buffer, oop_maps, 0, exception_offset, reexecute_offset, frame_size_in_words); + _deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset); +} + + +#ifdef COMPILER2 +//------------------------------generate_uncommon_trap_blob-------------------- +void SharedRuntime::generate_uncommon_trap_blob() { + // allocate space for the code + ResourceMark rm; + // setup code generation tools + CodeBuffer buffer("uncommon_trap_blob", 512, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + + enum frame_layout { + arg0_off, // thread sp + 0 // Arg location for + arg1_off, // unloaded_class_index sp + 1 // calling C + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + rbp_off, // callee saved register sp + 2 + return_off, // slot for return address sp + 3 + framesize + }; + + address start = __ pc(); + // Push self-frame. + __ subl(rsp, return_off*wordSize); // Epilog! + + // rbp, is an implicitly saved callee saved register (i.e. the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers no that adapter frames are gone. + __ movl(Address(rsp, rbp_off*wordSize),rbp); + + // Clear the floating point exception stack + __ empty_FPU_stack(); + + // set last_Java_sp + __ get_thread(rdx); + __ set_last_Java_frame(rdx, noreg, noreg, NULL); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // capture callee-saved registers as well as return values. + __ movl(Address(rsp, arg0_off*wordSize),rdx); + // argument already in ECX + __ movl(Address(rsp, arg1_off*wordSize),rcx); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); + + // Set an oopmap for the call site + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map = new OopMap( framesize, 0 ); + // No oopMap for rbp, it is known implicitly + + oop_maps->add_gc_map( __ pc()-start, map); + + __ get_thread(rcx); + + __ reset_last_Java_frame(rcx, false, false); + + // Load UnrollBlock into EDI + __ movl(rdi, rax); + + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + + // Pop self-frame. We have no frame, and must rely only on EAX and ESP. + __ addl(rsp,(framesize-1)*wordSize); // Epilog! + + // Pop deoptimized frame + __ addl(rsp,Address(rdi,Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset_in_bytes())); + + // sp should be pointing at the return address to the caller (3) + + // Stack bang to make sure there's enough room for these interpreter frames. + if (UseStackBanging) { + __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset_in_bytes())); + __ bang_stack_size(rbx, rcx); + } + + + // Load array of frame pcs into ECX + __ movl(rcx,Address(rdi,Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes())); + + __ popl(rsi); // trash the pc + + // Load array of frame sizes into ESI + __ movl(rsi,Address(rdi,Deoptimization::UnrollBlock::frame_sizes_offset_in_bytes())); + + Address counter(rdi, Deoptimization::UnrollBlock::counter_temp_offset_in_bytes()); + + __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::number_of_frames_offset_in_bytes())); + __ movl(counter, rbx); + + // Pick up the initial fp we should save + __ movl(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_fp_offset_in_bytes())); + + // Now adjust the caller's stack to make up for the extra locals + // but record the original sp so that we can save it in the skeletal interpreter + // frame and the stack walking of interpreter_sender will get the unextended sp + // value and not the "real" sp value. + + Address sp_temp(rdi, Deoptimization::UnrollBlock::sender_sp_temp_offset_in_bytes()); + __ movl(sp_temp, rsp); + __ subl(rsp, Address(rdi, Deoptimization::UnrollBlock::caller_adjustment_offset_in_bytes())); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ movl(rbx, Address(rsi, 0)); // Load frame size +#ifdef CC_INTERP + __ subl(rbx, 4*wordSize); // we'll push pc and ebp by hand and +#ifdef ASSERT + __ pushl(0xDEADDEAD); // Make a recognizable pattern + __ pushl(0xDEADDEAD); // (parm to RecursiveInterpreter...) +#else /* ASSERT */ + __ subl(rsp, 2*wordSize); // skip the "static long no_param" +#endif /* ASSERT */ +#else /* CC_INTERP */ + __ subl(rbx, 2*wordSize); // we'll push pc and rbp, by hand +#endif /* CC_INTERP */ + __ pushl(Address(rcx, 0)); // save return address + __ enter(); // save old & set new rbp, + __ subl(rsp, rbx); // Prolog! + __ movl(rbx, sp_temp); // sender's sp +#ifdef CC_INTERP + __ movl(Address(rbp, + -(sizeof(BytecodeInterpreter)) + in_bytes(byte_offset_of(BytecodeInterpreter, _sender_sp))), + rbx); // Make it walkable +#else /* CC_INTERP */ + // This value is corrected by layout_activation_impl + __ movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD ); + __ movl(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), rbx); // Make it walkable +#endif /* CC_INTERP */ + __ movl(sp_temp, rsp); // pass to next frame + __ addl(rsi, 4); // Bump array pointer (sizes) + __ addl(rcx, 4); // Bump array pointer (pcs) + __ decrement(counter); // decrement counter + __ jcc(Assembler::notZero, loop); + __ pushl(Address(rcx, 0)); // save final return address + + // Re-push self-frame + __ enter(); // save old & set new rbp, + __ subl(rsp, (framesize-2) * wordSize); // Prolog! + + + // set last_Java_sp, last_Java_fp + __ get_thread(rdi); + __ set_last_Java_frame(rdi, noreg, rbp, NULL); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + __ movl(Address(rsp,arg0_off*wordSize),rdi); + __ movl(Address(rsp,arg1_off*wordSize), Deoptimization::Unpack_uncommon_trap); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); + // Set an oopmap for the call site + oop_maps->add_gc_map( __ pc()-start, new OopMap( framesize, 0 ) ); + + __ get_thread(rdi); + __ reset_last_Java_frame(rdi, true, false); + + // Pop self-frame. + __ leave(); // Epilog! + + // Jump to interpreter + __ ret(0); + + // ------------- + // make sure all code is generated + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, framesize); +} +#endif // COMPILER2 + +//------------------------------generate_handler_blob------ +// +// Generate a special Compile2Runtime blob that saves all registers, +// setup oopmap, and calls safepoint code to stop the compiled code for +// a safepoint. +// +static SafepointBlob* generate_handler_blob(address call_ptr, bool cause_return) { + + // Account for thread arg in our frame + const int additional_words = 1; + int frame_size_in_words; + + assert (StubRoutines::forward_exception_entry() != NULL, "must be generated before"); + + ResourceMark rm; + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map; + + // allocate space for the code + // setup code generation tools + CodeBuffer buffer("handler_blob", 1024, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + + const Register java_thread = rdi; // callee-saved for VC++ + address start = __ pc(); + address call_pc = NULL; + + // If cause_return is true we are at a poll_return and there is + // the return address on the stack to the caller on the nmethod + // that is safepoint. We can leave this return on the stack and + // effectively complete the return and safepoint in the caller. + // Otherwise we push space for a return address that the safepoint + // handler will install later to make the stack walking sensible. + if( !cause_return ) + __ pushl(rbx); // Make room for return address (or push it again) + + map = RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words, false); + + // The following is basically a call_VM. However, we need the precise + // address of the call in order to generate an oopmap. Hence, we do all the + // work ourselves. + + // Push thread argument and setup last_Java_sp + __ get_thread(java_thread); + __ pushl(java_thread); + __ set_last_Java_frame(java_thread, noreg, noreg, NULL); + + // if this was not a poll_return then we need to correct the return address now. + if( !cause_return ) { + __ movl(rax, Address(java_thread, JavaThread::saved_exception_pc_offset())); + __ movl(Address(rbp, wordSize), rax); + } + + // do the call + __ call(RuntimeAddress(call_ptr)); + + // Set an oopmap for the call site. This oopmap will map all + // oop-registers and debug-info registers as callee-saved. This + // will allow deoptimization at this safepoint to find all possible + // debug-info recordings, as well as let GC find all oops. + + oop_maps->add_gc_map( __ pc() - start, map); + + // Discard arg + __ popl(rcx); + + Label noException; + + // Clear last_Java_sp again + __ get_thread(java_thread); + __ reset_last_Java_frame(java_thread, false, false); + + __ cmpl(Address(java_thread, Thread::pending_exception_offset()), NULL_WORD); + __ jcc(Assembler::equal, noException); + + // Exception pending + + RegisterSaver::restore_live_registers(masm); + + __ jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + + __ bind(noException); + + // Normal exit, register restoring and exit + RegisterSaver::restore_live_registers(masm); + + __ ret(0); + + // make sure all code is generated + masm->flush(); + + // Fill-out other meta info + return SafepointBlob::create(&buffer, oop_maps, frame_size_in_words); +} + +// +// generate_resolve_blob - call resolution (static/virtual/opt-virtual/ic-miss +// +// Generate a stub that calls into vm to find out the proper destination +// of a java call. All the argument registers are live at this point +// but since this is generic code we don't know what they are and the caller +// must do any gc of the args. +// +static RuntimeStub* generate_resolve_blob(address destination, const char* name) { + assert (StubRoutines::forward_exception_entry() != NULL, "must be generated before"); + + // allocate space for the code + ResourceMark rm; + + CodeBuffer buffer(name, 1000, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + + int frame_size_words; + enum frame_layout { + thread_off, + extra_words }; + + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map = NULL; + + int start = __ offset(); + + map = RegisterSaver::save_live_registers(masm, extra_words, &frame_size_words); + + int frame_complete = __ offset(); + + const Register thread = rdi; + __ get_thread(rdi); + + __ pushl(thread); + __ set_last_Java_frame(thread, noreg, rbp, NULL); + + __ call(RuntimeAddress(destination)); + + + // Set an oopmap for the call site. + // We need this not only for callee-saved registers, but also for volatile + // registers that the compiler might be keeping live across a safepoint. + + oop_maps->add_gc_map( __ offset() - start, map); + + // rax, contains the address we are going to jump to assuming no exception got installed + + __ addl(rsp, wordSize); + + // clear last_Java_sp + __ reset_last_Java_frame(thread, true, false); + // check for pending exceptions + Label pending; + __ cmpl(Address(thread, Thread::pending_exception_offset()), NULL_WORD); + __ jcc(Assembler::notEqual, pending); + + // get the returned methodOop + __ movl(rbx, Address(thread, JavaThread::vm_result_offset())); + __ movl(Address(rsp, RegisterSaver::rbx_offset() * wordSize), rbx); + + __ movl(Address(rsp, RegisterSaver::rax_offset() * wordSize), rax); + + RegisterSaver::restore_live_registers(masm); + + // We are back the the original state on entry and ready to go. + + __ jmp(rax); + + // Pending exception after the safepoint + + __ bind(pending); + + RegisterSaver::restore_live_registers(masm); + + // exception pending => remove activation and forward to exception handler + + __ get_thread(thread); + __ movl(Address(thread, JavaThread::vm_result_offset()), NULL_WORD); + __ movl(rax, Address(thread, Thread::pending_exception_offset())); + __ jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + + // ------------- + // make sure all code is generated + masm->flush(); + + // return the blob + // frame_size_words or bytes?? + return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_words, oop_maps, true); +} + +void SharedRuntime::generate_stubs() { + + _wrong_method_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method), + "wrong_method_stub"); + + _ic_miss_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method_ic_miss), + "ic_miss_stub"); + + _resolve_opt_virtual_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_opt_virtual_call_C), + "resolve_opt_virtual_call"); + + _resolve_virtual_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_virtual_call_C), + "resolve_virtual_call"); + + _resolve_static_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_static_call_C), + "resolve_static_call"); + + _polling_page_safepoint_handler_blob = + generate_handler_blob(CAST_FROM_FN_PTR(address, + SafepointSynchronize::handle_polling_page_exception), false); + + _polling_page_return_handler_blob = + generate_handler_blob(CAST_FROM_FN_PTR(address, + SafepointSynchronize::handle_polling_page_exception), true); + + generate_deopt_blob(); +#ifdef COMPILER2 + generate_uncommon_trap_blob(); +#endif // COMPILER2 +} diff --git a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp new file mode 100644 index 00000000000..7b0bf3e5458 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp @@ -0,0 +1,2609 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_sharedRuntime_x86_64.cpp.incl" + +DeoptimizationBlob *SharedRuntime::_deopt_blob; +#ifdef COMPILER2 +UncommonTrapBlob *SharedRuntime::_uncommon_trap_blob; +ExceptionBlob *OptoRuntime::_exception_blob; +#endif // COMPILER2 + +SafepointBlob *SharedRuntime::_polling_page_safepoint_handler_blob; +SafepointBlob *SharedRuntime::_polling_page_return_handler_blob; +RuntimeStub* SharedRuntime::_wrong_method_blob; +RuntimeStub* SharedRuntime::_ic_miss_blob; +RuntimeStub* SharedRuntime::_resolve_opt_virtual_call_blob; +RuntimeStub* SharedRuntime::_resolve_virtual_call_blob; +RuntimeStub* SharedRuntime::_resolve_static_call_blob; + +#define __ masm-> + +class SimpleRuntimeFrame { + + public: + + // Most of the runtime stubs have this simple frame layout. + // This class exists to make the layout shared in one place. + // Offsets are for compiler stack slots, which are jints. + enum layout { + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + rbp_off = frame::arg_reg_save_area_bytes/BytesPerInt, + rbp_off2, + return_off, return_off2, + framesize + }; +}; + +class RegisterSaver { + // Capture info about frame layout. Layout offsets are in jint + // units because compiler frame slots are jints. +#define DEF_XMM_OFFS(regnum) xmm ## regnum ## _off = xmm_off + (regnum)*16/BytesPerInt, xmm ## regnum ## H_off + enum layout { + fpu_state_off = frame::arg_reg_save_area_bytes/BytesPerInt, // fxsave save area + xmm_off = fpu_state_off + 160/BytesPerInt, // offset in fxsave save area + DEF_XMM_OFFS(0), + DEF_XMM_OFFS(1), + DEF_XMM_OFFS(2), + DEF_XMM_OFFS(3), + DEF_XMM_OFFS(4), + DEF_XMM_OFFS(5), + DEF_XMM_OFFS(6), + DEF_XMM_OFFS(7), + DEF_XMM_OFFS(8), + DEF_XMM_OFFS(9), + DEF_XMM_OFFS(10), + DEF_XMM_OFFS(11), + DEF_XMM_OFFS(12), + DEF_XMM_OFFS(13), + DEF_XMM_OFFS(14), + DEF_XMM_OFFS(15), + fpu_state_end = fpu_state_off + ((FPUStateSizeInWords-1)*wordSize / BytesPerInt), + fpu_stateH_end, + r15_off, r15H_off, + r14_off, r14H_off, + r13_off, r13H_off, + r12_off, r12H_off, + r11_off, r11H_off, + r10_off, r10H_off, + r9_off, r9H_off, + r8_off, r8H_off, + rdi_off, rdiH_off, + rsi_off, rsiH_off, + ignore_off, ignoreH_off, // extra copy of rbp + rsp_off, rspH_off, + rbx_off, rbxH_off, + rdx_off, rdxH_off, + rcx_off, rcxH_off, + rax_off, raxH_off, + // 16-byte stack alignment fill word: see MacroAssembler::push/pop_IU_state + align_off, alignH_off, + flags_off, flagsH_off, + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + rbp_off, rbpH_off, // copy of rbp we will restore + return_off, returnH_off, // slot for return address + reg_save_size // size in compiler stack slots + }; + + public: + static OopMap* save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words); + static void restore_live_registers(MacroAssembler* masm); + + // Offsets into the register save area + // Used by deoptimization when it is managing result register + // values on its own + + static int rax_offset_in_bytes(void) { return BytesPerInt * rax_off; } + static int rbx_offset_in_bytes(void) { return BytesPerInt * rbx_off; } + static int xmm0_offset_in_bytes(void) { return BytesPerInt * xmm0_off; } + static int return_offset_in_bytes(void) { return BytesPerInt * return_off; } + + // During deoptimization only the result registers need to be restored, + // all the other values have already been extracted. + static void restore_result_registers(MacroAssembler* masm); +}; + +OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words) { + + // Always make the frame size 16-byte aligned + int frame_size_in_bytes = round_to(additional_frame_words*wordSize + + reg_save_size*BytesPerInt, 16); + // OopMap frame size is in compiler stack slots (jint's) not bytes or words + int frame_size_in_slots = frame_size_in_bytes / BytesPerInt; + // The caller will allocate additional_frame_words + int additional_frame_slots = additional_frame_words*wordSize / BytesPerInt; + // CodeBlob frame size is in words. + int frame_size_in_words = frame_size_in_bytes / wordSize; + *total_frame_words = frame_size_in_words; + + // Save registers, fpu state, and flags. + // We assume caller has already pushed the return address onto the + // stack, so rsp is 8-byte aligned here. + // We push rpb twice in this sequence because we want the real rbp + // to be under the return like a normal enter. + + __ enter(); // rsp becomes 16-byte aligned here + __ push_CPU_state(); // Push a multiple of 16 bytes + if (frame::arg_reg_save_area_bytes != 0) { + // Allocate argument register save area + __ subq(rsp, frame::arg_reg_save_area_bytes); + } + + // Set an oopmap for the call site. This oopmap will map all + // oop-registers and debug-info registers as callee-saved. This + // will allow deoptimization at this safepoint to find all possible + // debug-info recordings, as well as let GC find all oops. + + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map = new OopMap(frame_size_in_slots, 0); + map->set_callee_saved(VMRegImpl::stack2reg( rax_off + additional_frame_slots), rax->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( rcx_off + additional_frame_slots), rcx->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( rdx_off + additional_frame_slots), rdx->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( rbx_off + additional_frame_slots), rbx->as_VMReg()); + // rbp location is known implicitly by the frame sender code, needs no oopmap + // and the location where rbp was saved by is ignored + map->set_callee_saved(VMRegImpl::stack2reg( rsi_off + additional_frame_slots), rsi->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( rdi_off + additional_frame_slots), rdi->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( r8_off + additional_frame_slots), r8->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( r9_off + additional_frame_slots), r9->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( r10_off + additional_frame_slots), r10->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( r11_off + additional_frame_slots), r11->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( r12_off + additional_frame_slots), r12->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( r13_off + additional_frame_slots), r13->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( r14_off + additional_frame_slots), r14->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg( r15_off + additional_frame_slots), r15->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm0_off + additional_frame_slots), xmm0->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm1_off + additional_frame_slots), xmm1->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm2_off + additional_frame_slots), xmm2->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm3_off + additional_frame_slots), xmm3->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm4_off + additional_frame_slots), xmm4->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm5_off + additional_frame_slots), xmm5->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm6_off + additional_frame_slots), xmm6->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm7_off + additional_frame_slots), xmm7->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm8_off + additional_frame_slots), xmm8->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm9_off + additional_frame_slots), xmm9->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm10_off + additional_frame_slots), xmm10->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm11_off + additional_frame_slots), xmm11->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm12_off + additional_frame_slots), xmm12->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm13_off + additional_frame_slots), xmm13->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm14_off + additional_frame_slots), xmm14->as_VMReg()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm15_off + additional_frame_slots), xmm15->as_VMReg()); + + // %%% These should all be a waste but we'll keep things as they were for now + if (true) { + map->set_callee_saved(VMRegImpl::stack2reg( raxH_off + additional_frame_slots), + rax->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( rcxH_off + additional_frame_slots), + rcx->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( rdxH_off + additional_frame_slots), + rdx->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( rbxH_off + additional_frame_slots), + rbx->as_VMReg()->next()); + // rbp location is known implicitly by the frame sender code, needs no oopmap + map->set_callee_saved(VMRegImpl::stack2reg( rsiH_off + additional_frame_slots), + rsi->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( rdiH_off + additional_frame_slots), + rdi->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( r8H_off + additional_frame_slots), + r8->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( r9H_off + additional_frame_slots), + r9->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( r10H_off + additional_frame_slots), + r10->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( r11H_off + additional_frame_slots), + r11->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( r12H_off + additional_frame_slots), + r12->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( r13H_off + additional_frame_slots), + r13->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( r14H_off + additional_frame_slots), + r14->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg( r15H_off + additional_frame_slots), + r15->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm0H_off + additional_frame_slots), + xmm0->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm1H_off + additional_frame_slots), + xmm1->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm2H_off + additional_frame_slots), + xmm2->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm3H_off + additional_frame_slots), + xmm3->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm4H_off + additional_frame_slots), + xmm4->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm5H_off + additional_frame_slots), + xmm5->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm6H_off + additional_frame_slots), + xmm6->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm7H_off + additional_frame_slots), + xmm7->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm8H_off + additional_frame_slots), + xmm8->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm9H_off + additional_frame_slots), + xmm9->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm10H_off + additional_frame_slots), + xmm10->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm11H_off + additional_frame_slots), + xmm11->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm12H_off + additional_frame_slots), + xmm12->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm13H_off + additional_frame_slots), + xmm13->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm14H_off + additional_frame_slots), + xmm14->as_VMReg()->next()); + map->set_callee_saved(VMRegImpl::stack2reg(xmm15H_off + additional_frame_slots), + xmm15->as_VMReg()->next()); + } + + return map; +} + +void RegisterSaver::restore_live_registers(MacroAssembler* masm) { + if (frame::arg_reg_save_area_bytes != 0) { + // Pop arg register save area + __ addq(rsp, frame::arg_reg_save_area_bytes); + } + // Recover CPU state + __ pop_CPU_state(); + // Get the rbp described implicitly by the calling convention (no oopMap) + __ popq(rbp); +} + +void RegisterSaver::restore_result_registers(MacroAssembler* masm) { + + // Just restore result register. Only used by deoptimization. By + // now any callee save register that needs to be restored to a c2 + // caller of the deoptee has been extracted into the vframeArray + // and will be stuffed into the c2i adapter we create for later + // restoration so only result registers need to be restored here. + + // Restore fp result register + __ movdbl(xmm0, Address(rsp, xmm0_offset_in_bytes())); + // Restore integer result register + __ movq(rax, Address(rsp, rax_offset_in_bytes())); + // Pop all of the register save are off the stack except the return address + __ addq(rsp, return_offset_in_bytes()); +} + +// The java_calling_convention describes stack locations as ideal slots on +// a frame with no abi restrictions. Since we must observe abi restrictions +// (like the placement of the register window) the slots must be biased by +// the following value. +static int reg2offset_in(VMReg r) { + // Account for saved rbp and return address + // This should really be in_preserve_stack_slots + return (r->reg2stack() + 4) * VMRegImpl::stack_slot_size; +} + +static int reg2offset_out(VMReg r) { + return (r->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size; +} + +// --------------------------------------------------------------------------- +// Read the array of BasicTypes from a signature, and compute where the +// arguments should go. Values in the VMRegPair regs array refer to 4-byte +// quantities. Values less than VMRegImpl::stack0 are registers, those above +// refer to 4-byte stack slots. All stack slots are based off of the stack pointer +// as framesizes are fixed. +// VMRegImpl::stack0 refers to the first slot 0(sp). +// and VMRegImpl::stack0+1 refers to the memory word 4-byes higher. Register +// up to RegisterImpl::number_of_registers) are the 64-bit +// integer registers. + +// Note: the INPUTS in sig_bt are in units of Java argument words, which are +// either 32-bit or 64-bit depending on the build. The OUTPUTS are in 32-bit +// units regardless of build. Of course for i486 there is no 64 bit build + +// The Java calling convention is a "shifted" version of the C ABI. +// By skipping the first C ABI register we can call non-static jni methods +// with small numbers of arguments without having to shuffle the arguments +// at all. Since we control the java ABI we ought to at least get some +// advantage out of it. + +int SharedRuntime::java_calling_convention(const BasicType *sig_bt, + VMRegPair *regs, + int total_args_passed, + int is_outgoing) { + + // Create the mapping between argument positions and + // registers. + static const Register INT_ArgReg[Argument::n_int_register_parameters_j] = { + j_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4, j_rarg5 + }; + static const XMMRegister FP_ArgReg[Argument::n_float_register_parameters_j] = { + j_farg0, j_farg1, j_farg2, j_farg3, + j_farg4, j_farg5, j_farg6, j_farg7 + }; + + + uint int_args = 0; + uint fp_args = 0; + uint stk_args = 0; // inc by 2 each time + + for (int i = 0; i < total_args_passed; i++) { + switch (sig_bt[i]) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + if (int_args < Argument::n_int_register_parameters_j) { + regs[i].set1(INT_ArgReg[int_args++]->as_VMReg()); + } else { + regs[i].set1(VMRegImpl::stack2reg(stk_args)); + stk_args += 2; + } + break; + case T_VOID: + // halves of T_LONG or T_DOUBLE + assert(i != 0 && (sig_bt[i - 1] == T_LONG || sig_bt[i - 1] == T_DOUBLE), "expecting half"); + regs[i].set_bad(); + break; + case T_LONG: + assert(sig_bt[i + 1] == T_VOID, "expecting half"); + // fall through + case T_OBJECT: + case T_ARRAY: + case T_ADDRESS: + if (int_args < Argument::n_int_register_parameters_j) { + regs[i].set2(INT_ArgReg[int_args++]->as_VMReg()); + } else { + regs[i].set2(VMRegImpl::stack2reg(stk_args)); + stk_args += 2; + } + break; + case T_FLOAT: + if (fp_args < Argument::n_float_register_parameters_j) { + regs[i].set1(FP_ArgReg[fp_args++]->as_VMReg()); + } else { + regs[i].set1(VMRegImpl::stack2reg(stk_args)); + stk_args += 2; + } + break; + case T_DOUBLE: + assert(sig_bt[i + 1] == T_VOID, "expecting half"); + if (fp_args < Argument::n_float_register_parameters_j) { + regs[i].set2(FP_ArgReg[fp_args++]->as_VMReg()); + } else { + regs[i].set2(VMRegImpl::stack2reg(stk_args)); + stk_args += 2; + } + break; + default: + ShouldNotReachHere(); + break; + } + } + + return round_to(stk_args, 2); +} + +// Patch the callers callsite with entry to compiled code if it exists. +static void patch_callers_callsite(MacroAssembler *masm) { + Label L; + __ verify_oop(rbx); + __ cmpq(Address(rbx, in_bytes(methodOopDesc::code_offset())), (int)NULL_WORD); + __ jcc(Assembler::equal, L); + + // Save the current stack pointer + __ movq(r13, rsp); + // Schedule the branch target address early. + // Call into the VM to patch the caller, then jump to compiled callee + // rax isn't live so capture return address while we easily can + __ movq(rax, Address(rsp, 0)); + + // align stack so push_CPU_state doesn't fault + __ andq(rsp, -(StackAlignmentInBytes)); + __ push_CPU_state(); + + + __ verify_oop(rbx); + // VM needs caller's callsite + // VM needs target method + // This needs to be a long call since we will relocate this adapter to + // the codeBuffer and it may not reach + + // Allocate argument register save area + if (frame::arg_reg_save_area_bytes != 0) { + __ subq(rsp, frame::arg_reg_save_area_bytes); + } + __ movq(c_rarg0, rbx); + __ movq(c_rarg1, rax); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::fixup_callers_callsite))); + + // De-allocate argument register save area + if (frame::arg_reg_save_area_bytes != 0) { + __ addq(rsp, frame::arg_reg_save_area_bytes); + } + + __ pop_CPU_state(); + // restore sp + __ movq(rsp, r13); + __ bind(L); +} + +// Helper function to put tags in interpreter stack. +static void tag_stack(MacroAssembler *masm, const BasicType sig, int st_off) { + if (TaggedStackInterpreter) { + int tag_offset = st_off + Interpreter::expr_tag_offset_in_bytes(0); + if (sig == T_OBJECT || sig == T_ARRAY) { + __ mov64(Address(rsp, tag_offset), frame::TagReference); + } else if (sig == T_LONG || sig == T_DOUBLE) { + int next_tag_offset = st_off + Interpreter::expr_tag_offset_in_bytes(1); + __ mov64(Address(rsp, next_tag_offset), frame::TagValue); + __ mov64(Address(rsp, tag_offset), frame::TagValue); + } else { + __ mov64(Address(rsp, tag_offset), frame::TagValue); + } + } +} + + +static void gen_c2i_adapter(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + Label& skip_fixup) { + // Before we get into the guts of the C2I adapter, see if we should be here + // at all. We've come from compiled code and are attempting to jump to the + // interpreter, which means the caller made a static call to get here + // (vcalls always get a compiled target if there is one). Check for a + // compiled target. If there is one, we need to patch the caller's call. + patch_callers_callsite(masm); + + __ bind(skip_fixup); + + // Since all args are passed on the stack, total_args_passed * + // Interpreter::stackElementSize is the space we need. Plus 1 because + // we also account for the return address location since + // we store it first rather than hold it in rax across all the shuffling + + int extraspace = (total_args_passed * Interpreter::stackElementSize()) + wordSize; + + // stack is aligned, keep it that way + extraspace = round_to(extraspace, 2*wordSize); + + // Get return address + __ popq(rax); + + // set senderSP value + __ movq(r13, rsp); + + __ subq(rsp, extraspace); + + // Store the return address in the expected location + __ movq(Address(rsp, 0), rax); + + // Now write the args into the outgoing interpreter space + for (int i = 0; i < total_args_passed; i++) { + if (sig_bt[i] == T_VOID) { + assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half"); + continue; + } + + // offset to start parameters + int st_off = (total_args_passed - i) * Interpreter::stackElementSize() + + Interpreter::value_offset_in_bytes(); + int next_off = st_off - Interpreter::stackElementSize(); + + // Say 4 args: + // i st_off + // 0 32 T_LONG + // 1 24 T_VOID + // 2 16 T_OBJECT + // 3 8 T_BOOL + // - 0 return address + // + // However to make thing extra confusing. Because we can fit a long/double in + // a single slot on a 64 bt vm and it would be silly to break them up, the interpreter + // leaves one slot empty and only stores to a single slot. In this case the + // slot that is occupied is the T_VOID slot. See I said it was confusing. + + VMReg r_1 = regs[i].first(); + VMReg r_2 = regs[i].second(); + if (!r_1->is_valid()) { + assert(!r_2->is_valid(), ""); + continue; + } + if (r_1->is_stack()) { + // memory to memory use rax + int ld_off = r_1->reg2stack() * VMRegImpl::stack_slot_size + extraspace; + if (!r_2->is_valid()) { + // sign extend?? + __ movl(rax, Address(rsp, ld_off)); + __ movq(Address(rsp, st_off), rax); + tag_stack(masm, sig_bt[i], st_off); + + } else { + + __ movq(rax, Address(rsp, ld_off)); + + // Two VMREgs|OptoRegs can be T_OBJECT, T_ADDRESS, T_DOUBLE, T_LONG + // T_DOUBLE and T_LONG use two slots in the interpreter + if ( sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) { + // ld_off == LSW, ld_off+wordSize == MSW + // st_off == MSW, next_off == LSW + __ movq(Address(rsp, next_off), rax); +#ifdef ASSERT + // Overwrite the unused slot with known junk + __ mov64(rax, CONST64(0xdeadffffdeadaaaa)); + __ movq(Address(rsp, st_off), rax); +#endif /* ASSERT */ + tag_stack(masm, sig_bt[i], next_off); + } else { + __ movq(Address(rsp, st_off), rax); + tag_stack(masm, sig_bt[i], st_off); + } + } + } else if (r_1->is_Register()) { + Register r = r_1->as_Register(); + if (!r_2->is_valid()) { + // must be only an int (or less ) so move only 32bits to slot + // why not sign extend?? + __ movl(Address(rsp, st_off), r); + tag_stack(masm, sig_bt[i], st_off); + } else { + // Two VMREgs|OptoRegs can be T_OBJECT, T_ADDRESS, T_DOUBLE, T_LONG + // T_DOUBLE and T_LONG use two slots in the interpreter + if ( sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) { + // long/double in gpr +#ifdef ASSERT + // Overwrite the unused slot with known junk + __ mov64(rax, CONST64(0xdeadffffdeadaaab)); + __ movq(Address(rsp, st_off), rax); +#endif /* ASSERT */ + __ movq(Address(rsp, next_off), r); + tag_stack(masm, sig_bt[i], next_off); + } else { + __ movq(Address(rsp, st_off), r); + tag_stack(masm, sig_bt[i], st_off); + } + } + } else { + assert(r_1->is_XMMRegister(), ""); + if (!r_2->is_valid()) { + // only a float use just part of the slot + __ movflt(Address(rsp, st_off), r_1->as_XMMRegister()); + tag_stack(masm, sig_bt[i], st_off); + } else { +#ifdef ASSERT + // Overwrite the unused slot with known junk + __ mov64(rax, CONST64(0xdeadffffdeadaaac)); + __ movq(Address(rsp, st_off), rax); +#endif /* ASSERT */ + __ movdbl(Address(rsp, next_off), r_1->as_XMMRegister()); + tag_stack(masm, sig_bt[i], next_off); + } + } + } + + // Schedule the branch target address early. + __ movq(rcx, Address(rbx, in_bytes(methodOopDesc::interpreter_entry_offset()))); + __ jmp(rcx); +} + +static void gen_i2c_adapter(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs) { + + // + // We will only enter here from an interpreted frame and never from after + // passing thru a c2i. Azul allowed this but we do not. If we lose the + // race and use a c2i we will remain interpreted for the race loser(s). + // This removes all sorts of headaches on the x86 side and also eliminates + // the possibility of having c2i -> i2c -> c2i -> ... endless transitions. + + + // Note: r13 contains the senderSP on entry. We must preserve it since + // we may do a i2c -> c2i transition if we lose a race where compiled + // code goes non-entrant while we get args ready. + // In addition we use r13 to locate all the interpreter args as + // we must align the stack to 16 bytes on an i2c entry else we + // lose alignment we expect in all compiled code and register + // save code can segv when fxsave instructions find improperly + // aligned stack pointer. + + __ movq(rax, Address(rsp, 0)); + + // Cut-out for having no stack args. Since up to 2 int/oop args are passed + // in registers, we will occasionally have no stack args. + int comp_words_on_stack = 0; + if (comp_args_on_stack) { + // Sig words on the stack are greater-than VMRegImpl::stack0. Those in + // registers are below. By subtracting stack0, we either get a negative + // number (all values in registers) or the maximum stack slot accessed. + + // Convert 4-byte c2 stack slots to words. + comp_words_on_stack = round_to(comp_args_on_stack*VMRegImpl::stack_slot_size, wordSize)>>LogBytesPerWord; + // Round up to miminum stack alignment, in wordSize + comp_words_on_stack = round_to(comp_words_on_stack, 2); + __ subq(rsp, comp_words_on_stack * wordSize); + } + + + // Ensure compiled code always sees stack at proper alignment + __ andq(rsp, -16); + + // push the return address and misalign the stack that youngest frame always sees + // as far as the placement of the call instruction + __ pushq(rax); + + // Will jump to the compiled code just as if compiled code was doing it. + // Pre-load the register-jump target early, to schedule it better. + __ movq(r11, Address(rbx, in_bytes(methodOopDesc::from_compiled_offset()))); + + // Now generate the shuffle code. Pick up all register args and move the + // rest through the floating point stack top. + for (int i = 0; i < total_args_passed; i++) { + if (sig_bt[i] == T_VOID) { + // Longs and doubles are passed in native word order, but misaligned + // in the 32-bit build. + assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half"); + continue; + } + + // Pick up 0, 1 or 2 words from SP+offset. + + assert(!regs[i].second()->is_valid() || regs[i].first()->next() == regs[i].second(), + "scrambled load targets?"); + // Load in argument order going down. + // int ld_off = (total_args_passed + comp_words_on_stack -i)*wordSize; + // base ld_off on r13 (sender_sp) as the stack alignment makes offsets from rsp + // unpredictable + int ld_off = ((total_args_passed - 1) - i)*Interpreter::stackElementSize(); + + // Point to interpreter value (vs. tag) + int next_off = ld_off - Interpreter::stackElementSize(); + // + // + // + VMReg r_1 = regs[i].first(); + VMReg r_2 = regs[i].second(); + if (!r_1->is_valid()) { + assert(!r_2->is_valid(), ""); + continue; + } + if (r_1->is_stack()) { + // Convert stack slot to an SP offset (+ wordSize to account for return address ) + int st_off = regs[i].first()->reg2stack()*VMRegImpl::stack_slot_size + wordSize; + if (!r_2->is_valid()) { + // sign extend??? + __ movl(rax, Address(r13, ld_off)); + __ movq(Address(rsp, st_off), rax); + } else { + // + // We are using two optoregs. This can be either T_OBJECT, T_ADDRESS, T_LONG, or T_DOUBLE + // the interpreter allocates two slots but only uses one for thr T_LONG or T_DOUBLE case + // So we must adjust where to pick up the data to match the interpreter. + // + // Interpreter local[n] == MSW, local[n+1] == LSW however locals + // are accessed as negative so LSW is at LOW address + + // ld_off is MSW so get LSW + const int offset = (sig_bt[i]==T_LONG||sig_bt[i]==T_DOUBLE)? + next_off : ld_off; + __ movq(rax, Address(r13, offset)); + // st_off is LSW (i.e. reg.first()) + __ movq(Address(rsp, st_off), rax); + } + } else if (r_1->is_Register()) { // Register argument + Register r = r_1->as_Register(); + assert(r != rax, "must be different"); + if (r_2->is_valid()) { + // + // We are using two VMRegs. This can be either T_OBJECT, T_ADDRESS, T_LONG, or T_DOUBLE + // the interpreter allocates two slots but only uses one for thr T_LONG or T_DOUBLE case + // So we must adjust where to pick up the data to match the interpreter. + + const int offset = (sig_bt[i]==T_LONG||sig_bt[i]==T_DOUBLE)? + next_off : ld_off; + + // this can be a misaligned move + __ movq(r, Address(r13, offset)); + } else { + // sign extend and use a full word? + __ movl(r, Address(r13, ld_off)); + } + } else { + if (!r_2->is_valid()) { + __ movflt(r_1->as_XMMRegister(), Address(r13, ld_off)); + } else { + __ movdbl(r_1->as_XMMRegister(), Address(r13, next_off)); + } + } + } + + // 6243940 We might end up in handle_wrong_method if + // the callee is deoptimized as we race thru here. If that + // happens we don't want to take a safepoint because the + // caller frame will look interpreted and arguments are now + // "compiled" so it is much better to make this transition + // invisible to the stack walking code. Unfortunately if + // we try and find the callee by normal means a safepoint + // is possible. So we stash the desired callee in the thread + // and the vm will find there should this case occur. + + __ movq(Address(r15_thread, JavaThread::callee_target_offset()), rbx); + + // put methodOop where a c2i would expect should we end up there + // only needed becaus eof c2 resolve stubs return methodOop as a result in + // rax + __ movq(rax, rbx); + __ jmp(r11); +} + +// --------------------------------------------------------------- +AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs) { + address i2c_entry = __ pc(); + + gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); + + // ------------------------------------------------------------------------- + // Generate a C2I adapter. On entry we know rbx holds the methodOop during calls + // to the interpreter. The args start out packed in the compiled layout. They + // need to be unpacked into the interpreter layout. This will almost always + // require some stack space. We grow the current (compiled) stack, then repack + // the args. We finally end in a jump to the generic interpreter entry point. + // On exit from the interpreter, the interpreter will restore our SP (lest the + // compiled code, which relys solely on SP and not RBP, get sick). + + address c2i_unverified_entry = __ pc(); + Label skip_fixup; + Label ok; + + Register holder = rax; + Register receiver = j_rarg0; + Register temp = rbx; + + { + __ verify_oop(holder); + __ movq(temp, Address(receiver, oopDesc::klass_offset_in_bytes())); + __ verify_oop(temp); + + __ cmpq(temp, Address(holder, compiledICHolderOopDesc::holder_klass_offset())); + __ movq(rbx, Address(holder, compiledICHolderOopDesc::holder_method_offset())); + __ jcc(Assembler::equal, ok); + __ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub())); + + __ bind(ok); + // Method might have been compiled since the call site was patched to + // interpreted if that is the case treat it as a miss so we can get + // the call site corrected. + __ cmpq(Address(rbx, in_bytes(methodOopDesc::code_offset())), (int)NULL_WORD); + __ jcc(Assembler::equal, skip_fixup); + __ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub())); + } + + address c2i_entry = __ pc(); + + gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); + + __ flush(); + return new AdapterHandlerEntry(i2c_entry, c2i_entry, c2i_unverified_entry); +} + +int SharedRuntime::c_calling_convention(const BasicType *sig_bt, + VMRegPair *regs, + int total_args_passed) { +// We return the amount of VMRegImpl stack slots we need to reserve for all +// the arguments NOT counting out_preserve_stack_slots. + +// NOTE: These arrays will have to change when c1 is ported +#ifdef _WIN64 + static const Register INT_ArgReg[Argument::n_int_register_parameters_c] = { + c_rarg0, c_rarg1, c_rarg2, c_rarg3 + }; + static const XMMRegister FP_ArgReg[Argument::n_float_register_parameters_c] = { + c_farg0, c_farg1, c_farg2, c_farg3 + }; +#else + static const Register INT_ArgReg[Argument::n_int_register_parameters_c] = { + c_rarg0, c_rarg1, c_rarg2, c_rarg3, c_rarg4, c_rarg5 + }; + static const XMMRegister FP_ArgReg[Argument::n_float_register_parameters_c] = { + c_farg0, c_farg1, c_farg2, c_farg3, + c_farg4, c_farg5, c_farg6, c_farg7 + }; +#endif // _WIN64 + + + uint int_args = 0; + uint fp_args = 0; + uint stk_args = 0; // inc by 2 each time + + for (int i = 0; i < total_args_passed; i++) { + switch (sig_bt[i]) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + if (int_args < Argument::n_int_register_parameters_c) { + regs[i].set1(INT_ArgReg[int_args++]->as_VMReg()); +#ifdef _WIN64 + fp_args++; + // Allocate slots for callee to stuff register args the stack. + stk_args += 2; +#endif + } else { + regs[i].set1(VMRegImpl::stack2reg(stk_args)); + stk_args += 2; + } + break; + case T_LONG: + assert(sig_bt[i + 1] == T_VOID, "expecting half"); + // fall through + case T_OBJECT: + case T_ARRAY: + case T_ADDRESS: + if (int_args < Argument::n_int_register_parameters_c) { + regs[i].set2(INT_ArgReg[int_args++]->as_VMReg()); +#ifdef _WIN64 + fp_args++; + stk_args += 2; +#endif + } else { + regs[i].set2(VMRegImpl::stack2reg(stk_args)); + stk_args += 2; + } + break; + case T_FLOAT: + if (fp_args < Argument::n_float_register_parameters_c) { + regs[i].set1(FP_ArgReg[fp_args++]->as_VMReg()); +#ifdef _WIN64 + int_args++; + // Allocate slots for callee to stuff register args the stack. + stk_args += 2; +#endif + } else { + regs[i].set1(VMRegImpl::stack2reg(stk_args)); + stk_args += 2; + } + break; + case T_DOUBLE: + assert(sig_bt[i + 1] == T_VOID, "expecting half"); + if (fp_args < Argument::n_float_register_parameters_c) { + regs[i].set2(FP_ArgReg[fp_args++]->as_VMReg()); +#ifdef _WIN64 + int_args++; + // Allocate slots for callee to stuff register args the stack. + stk_args += 2; +#endif + } else { + regs[i].set2(VMRegImpl::stack2reg(stk_args)); + stk_args += 2; + } + break; + case T_VOID: // Halves of longs and doubles + assert(i != 0 && (sig_bt[i - 1] == T_LONG || sig_bt[i - 1] == T_DOUBLE), "expecting half"); + regs[i].set_bad(); + break; + default: + ShouldNotReachHere(); + break; + } + } +#ifdef _WIN64 + // windows abi requires that we always allocate enough stack space + // for 4 64bit registers to be stored down. + if (stk_args < 8) { + stk_args = 8; + } +#endif // _WIN64 + + return stk_args; +} + +// On 64 bit we will store integer like items to the stack as +// 64 bits items (sparc abi) even though java would only store +// 32bits for a parameter. On 32bit it will simply be 32 bits +// So this routine will do 32->32 on 32bit and 32->64 on 64bit +static void move32_64(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack + __ movslq(rax, Address(rbp, reg2offset_in(src.first()))); + __ movq(Address(rsp, reg2offset_out(dst.first())), rax); + } else { + // stack to reg + __ movslq(dst.first()->as_Register(), Address(rbp, reg2offset_in(src.first()))); + } + } else if (dst.first()->is_stack()) { + // reg to stack + // Do we really have to sign extend??? + // __ movslq(src.first()->as_Register(), src.first()->as_Register()); + __ movq(Address(rsp, reg2offset_out(dst.first())), src.first()->as_Register()); + } else { + // Do we really have to sign extend??? + // __ movslq(dst.first()->as_Register(), src.first()->as_Register()); + if (dst.first() != src.first()) { + __ movq(dst.first()->as_Register(), src.first()->as_Register()); + } + } +} + + +// An oop arg. Must pass a handle not the oop itself +static void object_move(MacroAssembler* masm, + OopMap* map, + int oop_handle_offset, + int framesize_in_slots, + VMRegPair src, + VMRegPair dst, + bool is_receiver, + int* receiver_offset) { + + // must pass a handle. First figure out the location we use as a handle + + Register rHandle = dst.first()->is_stack() ? rax : dst.first()->as_Register(); + + // See if oop is NULL if it is we need no handle + + if (src.first()->is_stack()) { + + // Oop is already on the stack as an argument + int offset_in_older_frame = src.first()->reg2stack() + SharedRuntime::out_preserve_stack_slots(); + map->set_oop(VMRegImpl::stack2reg(offset_in_older_frame + framesize_in_slots)); + if (is_receiver) { + *receiver_offset = (offset_in_older_frame + framesize_in_slots) * VMRegImpl::stack_slot_size; + } + + __ cmpq(Address(rbp, reg2offset_in(src.first())), (int)NULL_WORD); + __ leaq(rHandle, Address(rbp, reg2offset_in(src.first()))); + // conditionally move a NULL + __ cmovq(Assembler::equal, rHandle, Address(rbp, reg2offset_in(src.first()))); + } else { + + // Oop is in an a register we must store it to the space we reserve + // on the stack for oop_handles and pass a handle if oop is non-NULL + + const Register rOop = src.first()->as_Register(); + int oop_slot; + if (rOop == j_rarg0) + oop_slot = 0; + else if (rOop == j_rarg1) + oop_slot = 1; + else if (rOop == j_rarg2) + oop_slot = 2; + else if (rOop == j_rarg3) + oop_slot = 3; + else if (rOop == j_rarg4) + oop_slot = 4; + else { + assert(rOop == j_rarg5, "wrong register"); + oop_slot = 5; + } + + oop_slot = oop_slot * VMRegImpl::slots_per_word + oop_handle_offset; + int offset = oop_slot*VMRegImpl::stack_slot_size; + + map->set_oop(VMRegImpl::stack2reg(oop_slot)); + // Store oop in handle area, may be NULL + __ movq(Address(rsp, offset), rOop); + if (is_receiver) { + *receiver_offset = offset; + } + + __ cmpq(rOop, (int)NULL); + __ leaq(rHandle, Address(rsp, offset)); + // conditionally move a NULL from the handle area where it was just stored + __ cmovq(Assembler::equal, rHandle, Address(rsp, offset)); + } + + // If arg is on the stack then place it otherwise it is already in correct reg. + if (dst.first()->is_stack()) { + __ movq(Address(rsp, reg2offset_out(dst.first())), rHandle); + } +} + +// A float arg may have to do float reg int reg conversion +static void float_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + assert(!src.second()->is_valid() && !dst.second()->is_valid(), "bad float_move"); + + // The calling conventions assures us that each VMregpair is either + // all really one physical register or adjacent stack slots. + // This greatly simplifies the cases here compared to sparc. + + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + __ movl(rax, Address(rbp, reg2offset_in(src.first()))); + __ movq(Address(rsp, reg2offset_out(dst.first())), rax); + } else { + // stack to reg + assert(dst.first()->is_XMMRegister(), "only expect xmm registers as parameters"); + __ movflt(dst.first()->as_XMMRegister(), Address(rbp, reg2offset_in(src.first()))); + } + } else if (dst.first()->is_stack()) { + // reg to stack + assert(src.first()->is_XMMRegister(), "only expect xmm registers as parameters"); + __ movflt(Address(rsp, reg2offset_out(dst.first())), src.first()->as_XMMRegister()); + } else { + // reg to reg + // In theory these overlap but the ordering is such that this is likely a nop + if ( src.first() != dst.first()) { + __ movdbl(dst.first()->as_XMMRegister(), src.first()->as_XMMRegister()); + } + } +} + +// A long move +static void long_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + + // The calling conventions assures us that each VMregpair is either + // all really one physical register or adjacent stack slots. + // This greatly simplifies the cases here compared to sparc. + + if (src.is_single_phys_reg() ) { + if (dst.is_single_phys_reg()) { + if (dst.first() != src.first()) { + __ movq(dst.first()->as_Register(), src.first()->as_Register()); + } + } else { + assert(dst.is_single_reg(), "not a stack pair"); + __ movq(Address(rsp, reg2offset_out(dst.first())), src.first()->as_Register()); + } + } else if (dst.is_single_phys_reg()) { + assert(src.is_single_reg(), "not a stack pair"); + __ movq(dst.first()->as_Register(), Address(rbp, reg2offset_out(src.first()))); + } else { + assert(src.is_single_reg() && dst.is_single_reg(), "not stack pairs"); + __ movq(rax, Address(rbp, reg2offset_in(src.first()))); + __ movq(Address(rsp, reg2offset_out(dst.first())), rax); + } +} + +// A double move +static void double_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { + + // The calling conventions assures us that each VMregpair is either + // all really one physical register or adjacent stack slots. + // This greatly simplifies the cases here compared to sparc. + + if (src.is_single_phys_reg() ) { + if (dst.is_single_phys_reg()) { + // In theory these overlap but the ordering is such that this is likely a nop + if ( src.first() != dst.first()) { + __ movdbl(dst.first()->as_XMMRegister(), src.first()->as_XMMRegister()); + } + } else { + assert(dst.is_single_reg(), "not a stack pair"); + __ movdbl(Address(rsp, reg2offset_out(dst.first())), src.first()->as_XMMRegister()); + } + } else if (dst.is_single_phys_reg()) { + assert(src.is_single_reg(), "not a stack pair"); + __ movdbl(dst.first()->as_XMMRegister(), Address(rbp, reg2offset_out(src.first()))); + } else { + assert(src.is_single_reg() && dst.is_single_reg(), "not stack pairs"); + __ movq(rax, Address(rbp, reg2offset_in(src.first()))); + __ movq(Address(rsp, reg2offset_out(dst.first())), rax); + } +} + + +void SharedRuntime::save_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { + // We always ignore the frame_slots arg and just use the space just below frame pointer + // which by this time is free to use + switch (ret_type) { + case T_FLOAT: + __ movflt(Address(rbp, -wordSize), xmm0); + break; + case T_DOUBLE: + __ movdbl(Address(rbp, -wordSize), xmm0); + break; + case T_VOID: break; + default: { + __ movq(Address(rbp, -wordSize), rax); + } + } +} + +void SharedRuntime::restore_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { + // We always ignore the frame_slots arg and just use the space just below frame pointer + // which by this time is free to use + switch (ret_type) { + case T_FLOAT: + __ movflt(xmm0, Address(rbp, -wordSize)); + break; + case T_DOUBLE: + __ movdbl(xmm0, Address(rbp, -wordSize)); + break; + case T_VOID: break; + default: { + __ movq(rax, Address(rbp, -wordSize)); + } + } +} + +static void save_args(MacroAssembler *masm, int arg_count, int first_arg, VMRegPair *args) { + for ( int i = first_arg ; i < arg_count ; i++ ) { + if (args[i].first()->is_Register()) { + __ pushq(args[i].first()->as_Register()); + } else if (args[i].first()->is_XMMRegister()) { + __ subq(rsp, 2*wordSize); + __ movdbl(Address(rsp, 0), args[i].first()->as_XMMRegister()); + } + } +} + +static void restore_args(MacroAssembler *masm, int arg_count, int first_arg, VMRegPair *args) { + for ( int i = arg_count - 1 ; i >= first_arg ; i-- ) { + if (args[i].first()->is_Register()) { + __ popq(args[i].first()->as_Register()); + } else if (args[i].first()->is_XMMRegister()) { + __ movdbl(args[i].first()->as_XMMRegister(), Address(rsp, 0)); + __ addq(rsp, 2*wordSize); + } + } +} + +// --------------------------------------------------------------------------- +// Generate a native wrapper for a given method. The method takes arguments +// in the Java compiled code convention, marshals them to the native +// convention (handlizes oops, etc), transitions to native, makes the call, +// returns to java state (possibly blocking), unhandlizes any result and +// returns. +nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, + methodHandle method, + int total_in_args, + int comp_args_on_stack, + BasicType *in_sig_bt, + VMRegPair *in_regs, + BasicType ret_type) { + // Native nmethod wrappers never take possesion of the oop arguments. + // So the caller will gc the arguments. The only thing we need an + // oopMap for is if the call is static + // + // An OopMap for lock (and class if static) + OopMapSet *oop_maps = new OopMapSet(); + intptr_t start = (intptr_t)__ pc(); + + // We have received a description of where all the java arg are located + // on entry to the wrapper. We need to convert these args to where + // the jni function will expect them. To figure out where they go + // we convert the java signature to a C signature by inserting + // the hidden arguments as arg[0] and possibly arg[1] (static method) + + int total_c_args = total_in_args + 1; + if (method->is_static()) { + total_c_args++; + } + + BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_c_args); + VMRegPair* out_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_c_args); + + int argc = 0; + out_sig_bt[argc++] = T_ADDRESS; + if (method->is_static()) { + out_sig_bt[argc++] = T_OBJECT; + } + + for (int i = 0; i < total_in_args ; i++ ) { + out_sig_bt[argc++] = in_sig_bt[i]; + } + + // Now figure out where the args must be stored and how much stack space + // they require. + // + int out_arg_slots; + out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args); + + // Compute framesize for the wrapper. We need to handlize all oops in + // incoming registers + + // Calculate the total number of stack slots we will need. + + // First count the abi requirement plus all of the outgoing args + int stack_slots = SharedRuntime::out_preserve_stack_slots() + out_arg_slots; + + // Now the space for the inbound oop handle area + + int oop_handle_offset = stack_slots; + stack_slots += 6*VMRegImpl::slots_per_word; + + // Now any space we need for handlizing a klass if static method + + int oop_temp_slot_offset = 0; + int klass_slot_offset = 0; + int klass_offset = -1; + int lock_slot_offset = 0; + bool is_static = false; + + if (method->is_static()) { + klass_slot_offset = stack_slots; + stack_slots += VMRegImpl::slots_per_word; + klass_offset = klass_slot_offset * VMRegImpl::stack_slot_size; + is_static = true; + } + + // Plus a lock if needed + + if (method->is_synchronized()) { + lock_slot_offset = stack_slots; + stack_slots += VMRegImpl::slots_per_word; + } + + // Now a place (+2) to save return values or temp during shuffling + // + 4 for return address (which we own) and saved rbp + stack_slots += 6; + + // Ok The space we have allocated will look like: + // + // + // FP-> | | + // |---------------------| + // | 2 slots for moves | + // |---------------------| + // | lock box (if sync) | + // |---------------------| <- lock_slot_offset + // | klass (if static) | + // |---------------------| <- klass_slot_offset + // | oopHandle area | + // |---------------------| <- oop_handle_offset (6 java arg registers) + // | outbound memory | + // | based arguments | + // | | + // |---------------------| + // | | + // SP-> | out_preserved_slots | + // + // + + + // Now compute actual number of stack words we need rounding to make + // stack properly aligned. + stack_slots = round_to(stack_slots, 4 * VMRegImpl::slots_per_word); + + int stack_size = stack_slots * VMRegImpl::stack_slot_size; + + + // First thing make an ic check to see if we should even be here + + // We are free to use all registers as temps without saving them and + // restoring them except rbp. rbp is the only callee save register + // as far as the interpreter and the compiler(s) are concerned. + + + const Register ic_reg = rax; + const Register receiver = j_rarg0; + + Label ok; + Label exception_pending; + + __ verify_oop(receiver); + __ cmpq(ic_reg, Address(receiver, oopDesc::klass_offset_in_bytes())); + __ jcc(Assembler::equal, ok); + + __ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub())); + + // Verified entry point must be aligned + __ align(8); + + __ bind(ok); + + int vep_offset = ((intptr_t)__ pc()) - start; + + // The instruction at the verified entry point must be 5 bytes or longer + // because it can be patched on the fly by make_non_entrant. The stack bang + // instruction fits that requirement. + + // Generate stack overflow check + + if (UseStackBanging) { + __ bang_stack_with_offset(StackShadowPages*os::vm_page_size()); + } else { + // need a 5 byte instruction to allow MT safe patching to non-entrant + __ fat_nop(); + } + + // Generate a new frame for the wrapper. + __ enter(); + // -2 because return address is already present and so is saved rbp + __ subq(rsp, stack_size - 2*wordSize); + + // Frame is now completed as far as size and linkage. + + int frame_complete = ((intptr_t)__ pc()) - start; + +#ifdef ASSERT + { + Label L; + __ movq(rax, rsp); + __ andq(rax, -16); // must be 16 byte boundry (see amd64 ABI) + __ cmpq(rax, rsp); + __ jcc(Assembler::equal, L); + __ stop("improperly aligned stack"); + __ bind(L); + } +#endif /* ASSERT */ + + + // We use r14 as the oop handle for the receiver/klass + // It is callee save so it survives the call to native + + const Register oop_handle_reg = r14; + + + + // + // We immediately shuffle the arguments so that any vm call we have to + // make from here on out (sync slow path, jvmti, etc.) we will have + // captured the oops from our caller and have a valid oopMap for + // them. + + // ----------------- + // The Grand Shuffle + + // The Java calling convention is either equal (linux) or denser (win64) than the + // c calling convention. However the because of the jni_env argument the c calling + // convention always has at least one more (and two for static) arguments than Java. + // Therefore if we move the args from java -> c backwards then we will never have + // a register->register conflict and we don't have to build a dependency graph + // and figure out how to break any cycles. + // + + // Record esp-based slot for receiver on stack for non-static methods + int receiver_offset = -1; + + // This is a trick. We double the stack slots so we can claim + // the oops in the caller's frame. Since we are sure to have + // more args than the caller doubling is enough to make + // sure we can capture all the incoming oop args from the + // caller. + // + OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); + + // Mark location of rbp (someday) + // map->set_callee_saved(VMRegImpl::stack2reg( stack_slots - 2), stack_slots * 2, 0, vmreg(rbp)); + + // Use eax, ebx as temporaries during any memory-memory moves we have to do + // All inbound args are referenced based on rbp and all outbound args via rsp. + + +#ifdef ASSERT + bool reg_destroyed[RegisterImpl::number_of_registers]; + bool freg_destroyed[XMMRegisterImpl::number_of_registers]; + for ( int r = 0 ; r < RegisterImpl::number_of_registers ; r++ ) { + reg_destroyed[r] = false; + } + for ( int f = 0 ; f < XMMRegisterImpl::number_of_registers ; f++ ) { + freg_destroyed[f] = false; + } + +#endif /* ASSERT */ + + + int c_arg = total_c_args - 1; + for ( int i = total_in_args - 1; i >= 0 ; i--, c_arg-- ) { +#ifdef ASSERT + if (in_regs[i].first()->is_Register()) { + assert(!reg_destroyed[in_regs[i].first()->as_Register()->encoding()], "destroyed reg!"); + } else if (in_regs[i].first()->is_XMMRegister()) { + assert(!freg_destroyed[in_regs[i].first()->as_XMMRegister()->encoding()], "destroyed reg!"); + } + if (out_regs[c_arg].first()->is_Register()) { + reg_destroyed[out_regs[c_arg].first()->as_Register()->encoding()] = true; + } else if (out_regs[c_arg].first()->is_XMMRegister()) { + freg_destroyed[out_regs[c_arg].first()->as_XMMRegister()->encoding()] = true; + } +#endif /* ASSERT */ + switch (in_sig_bt[i]) { + case T_ARRAY: + case T_OBJECT: + object_move(masm, map, oop_handle_offset, stack_slots, in_regs[i], out_regs[c_arg], + ((i == 0) && (!is_static)), + &receiver_offset); + break; + case T_VOID: + break; + + case T_FLOAT: + float_move(masm, in_regs[i], out_regs[c_arg]); + break; + + case T_DOUBLE: + assert( i + 1 < total_in_args && + in_sig_bt[i + 1] == T_VOID && + out_sig_bt[c_arg+1] == T_VOID, "bad arg list"); + double_move(masm, in_regs[i], out_regs[c_arg]); + break; + + case T_LONG : + long_move(masm, in_regs[i], out_regs[c_arg]); + break; + + case T_ADDRESS: assert(false, "found T_ADDRESS in java args"); + + default: + move32_64(masm, in_regs[i], out_regs[c_arg]); + } + } + + // point c_arg at the first arg that is already loaded in case we + // need to spill before we call out + c_arg++; + + // Pre-load a static method's oop into r14. Used both by locking code and + // the normal JNI call code. + if (method->is_static()) { + + // load oop into a register + __ movoop(oop_handle_reg, JNIHandles::make_local(Klass::cast(method->method_holder())->java_mirror())); + + // Now handlize the static class mirror it's known not-null. + __ movq(Address(rsp, klass_offset), oop_handle_reg); + map->set_oop(VMRegImpl::stack2reg(klass_slot_offset)); + + // Now get the handle + __ leaq(oop_handle_reg, Address(rsp, klass_offset)); + // store the klass handle as second argument + __ movq(c_rarg1, oop_handle_reg); + // and protect the arg if we must spill + c_arg--; + } + + // Change state to native (we save the return address in the thread, since it might not + // be pushed on the stack when we do a a stack traversal). It is enough that the pc() + // points into the right code segment. It does not have to be the correct return pc. + // We use the same pc/oopMap repeatedly when we call out + + intptr_t the_pc = (intptr_t) __ pc(); + oop_maps->add_gc_map(the_pc - start, map); + + __ set_last_Java_frame(rsp, noreg, (address)the_pc); + + + // We have all of the arguments setup at this point. We must not touch any register + // argument registers at this point (what if we save/restore them there are no oop? + + { + SkipIfEqual skip(masm, &DTraceMethodProbes, false); + // protect the args we've loaded + save_args(masm, total_c_args, c_arg, out_regs); + __ movoop(c_rarg1, JNIHandles::make_local(method())); + __ call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), + r15_thread, c_rarg1); + restore_args(masm, total_c_args, c_arg, out_regs); + } + + // Lock a synchronized method + + // Register definitions used by locking and unlocking + + const Register swap_reg = rax; // Must use rax for cmpxchg instruction + const Register obj_reg = rbx; // Will contain the oop + const Register lock_reg = r13; // Address of compiler lock object (BasicLock) + const Register old_hdr = r13; // value of old header at unlock time + + Label slow_path_lock; + Label lock_done; + + if (method->is_synchronized()) { + + + const int mark_word_offset = BasicLock::displaced_header_offset_in_bytes(); + + // Get the handle (the 2nd argument) + __ movq(oop_handle_reg, c_rarg1); + + // Get address of the box + + __ leaq(lock_reg, Address(rsp, lock_slot_offset * VMRegImpl::stack_slot_size)); + + // Load the oop from the handle + __ movq(obj_reg, Address(oop_handle_reg, 0)); + + if (UseBiasedLocking) { + __ biased_locking_enter(lock_reg, obj_reg, swap_reg, rscratch1, false, lock_done, &slow_path_lock); + } + + // Load immediate 1 into swap_reg %rax + __ movl(swap_reg, 1); + + // Load (object->mark() | 1) into swap_reg %rax + __ orq(swap_reg, Address(obj_reg, 0)); + + // Save (object->mark() | 1) into BasicLock's displaced header + __ movq(Address(lock_reg, mark_word_offset), swap_reg); + + if (os::is_MP()) { + __ lock(); + } + + // src -> dest iff dest == rax else rax <- dest + __ cmpxchgq(lock_reg, Address(obj_reg, 0)); + __ jcc(Assembler::equal, lock_done); + + // Hmm should this move to the slow path code area??? + + // Test if the oopMark is an obvious stack pointer, i.e., + // 1) (mark & 3) == 0, and + // 2) rsp <= mark < mark + os::pagesize() + // These 3 tests can be done by evaluating the following + // expression: ((mark - rsp) & (3 - os::vm_page_size())), + // assuming both stack pointer and pagesize have their + // least significant 2 bits clear. + // NOTE: the oopMark is in swap_reg %rax as the result of cmpxchg + + __ subq(swap_reg, rsp); + __ andq(swap_reg, 3 - os::vm_page_size()); + + // Save the test result, for recursive case, the result is zero + __ movq(Address(lock_reg, mark_word_offset), swap_reg); + __ jcc(Assembler::notEqual, slow_path_lock); + + // Slow path will re-enter here + + __ bind(lock_done); + } + + + // Finally just about ready to make the JNI call + + + // get JNIEnv* which is first argument to native + + __ leaq(c_rarg0, Address(r15_thread, in_bytes(JavaThread::jni_environment_offset()))); + + // Now set thread in native + __ mov64(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native); + + __ call(RuntimeAddress(method->native_function())); + + // Either restore the MXCSR register after returning from the JNI Call + // or verify that it wasn't changed. + if (RestoreMXCSROnJNICalls) { + __ ldmxcsr(ExternalAddress(StubRoutines::amd64::mxcsr_std())); + + } + else if (CheckJNICalls ) { + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::amd64::verify_mxcsr_entry()))); + } + + + // Unpack native results. + switch (ret_type) { + case T_BOOLEAN: __ c2bool(rax); break; + case T_CHAR : __ movzwl(rax, rax); break; + case T_BYTE : __ sign_extend_byte (rax); break; + case T_SHORT : __ sign_extend_short(rax); break; + case T_INT : /* nothing to do */ break; + case T_DOUBLE : + case T_FLOAT : + // Result is in xmm0 we'll save as needed + break; + case T_ARRAY: // Really a handle + case T_OBJECT: // Really a handle + break; // can't de-handlize until after safepoint check + case T_VOID: break; + case T_LONG: break; + default : ShouldNotReachHere(); + } + + // Switch thread to "native transition" state before reading the synchronization state. + // This additional state is necessary because reading and testing the synchronization + // state is not atomic w.r.t. GC, as this scenario demonstrates: + // Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. + // VM thread changes sync state to synchronizing and suspends threads for GC. + // Thread A is resumed to finish this native method, but doesn't block here since it + // didn't see any synchronization is progress, and escapes. + __ mov64(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native_trans); + + if(os::is_MP()) { + if (UseMembar) { + // Force this write out before the read below + __ membar(Assembler::Membar_mask_bits( + Assembler::LoadLoad | Assembler::LoadStore | + Assembler::StoreLoad | Assembler::StoreStore)); + } else { + // Write serialization page so VM thread can do a pseudo remote membar. + // We use the current thread pointer to calculate a thread specific + // offset to write to within the page. This minimizes bus traffic + // due to cache line collision. + __ serialize_memory(r15_thread, rcx); + } + } + + + // check for safepoint operation in progress and/or pending suspend requests + { + Label Continue; + + __ cmp32(ExternalAddress((address)SafepointSynchronize::address_of_state()), + SafepointSynchronize::_not_synchronized); + + Label L; + __ jcc(Assembler::notEqual, L); + __ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0); + __ jcc(Assembler::equal, Continue); + __ bind(L); + + // Don't use call_VM as it will see a possible pending exception and forward it + // and never return here preventing us from clearing _last_native_pc down below. + // Also can't use call_VM_leaf either as it will check to see if rsi & rdi are + // preserved and correspond to the bcp/locals pointers. So we do a runtime call + // by hand. + // + save_native_result(masm, ret_type, stack_slots); + __ movq(c_rarg0, r15_thread); + __ movq(r12, rsp); // remember sp + __ subq(rsp, frame::arg_reg_save_area_bytes); // windows + __ andq(rsp, -16); // align stack as required by ABI + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); + __ movq(rsp, r12); // restore sp + // Restore any method result value + restore_native_result(masm, ret_type, stack_slots); + __ bind(Continue); + } + + // change thread state + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java); + + Label reguard; + Label reguard_done; + __ cmpl(Address(r15_thread, JavaThread::stack_guard_state_offset()), JavaThread::stack_guard_yellow_disabled); + __ jcc(Assembler::equal, reguard); + __ bind(reguard_done); + + // native result if any is live + + // Unlock + Label unlock_done; + Label slow_path_unlock; + if (method->is_synchronized()) { + + // Get locked oop from the handle we passed to jni + __ movq(obj_reg, Address(oop_handle_reg, 0)); + + Label done; + + if (UseBiasedLocking) { + __ biased_locking_exit(obj_reg, old_hdr, done); + } + + // Simple recursive lock? + + __ cmpq(Address(rsp, lock_slot_offset * VMRegImpl::stack_slot_size), (int)NULL_WORD); + __ jcc(Assembler::equal, done); + + // Must save rax if if it is live now because cmpxchg must use it + if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { + save_native_result(masm, ret_type, stack_slots); + } + + + // get address of the stack lock + __ leaq(rax, Address(rsp, lock_slot_offset * VMRegImpl::stack_slot_size)); + // get old displaced header + __ movq(old_hdr, Address(rax, 0)); + + // Atomic swap old header if oop still contains the stack lock + if (os::is_MP()) { + __ lock(); + } + __ cmpxchgq(old_hdr, Address(obj_reg, 0)); + __ jcc(Assembler::notEqual, slow_path_unlock); + + // slow path re-enters here + __ bind(unlock_done); + if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { + restore_native_result(masm, ret_type, stack_slots); + } + + __ bind(done); + + } + + { + SkipIfEqual skip(masm, &DTraceMethodProbes, false); + save_native_result(masm, ret_type, stack_slots); + __ movoop(c_rarg1, JNIHandles::make_local(method())); + __ call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), + r15_thread, c_rarg1); + restore_native_result(masm, ret_type, stack_slots); + } + + __ reset_last_Java_frame(false, true); + + // Unpack oop result + if (ret_type == T_OBJECT || ret_type == T_ARRAY) { + Label L; + __ testq(rax, rax); + __ jcc(Assembler::zero, L); + __ movq(rax, Address(rax, 0)); + __ bind(L); + __ verify_oop(rax); + } + + // reset handle block + __ movq(rcx, Address(r15_thread, JavaThread::active_handles_offset())); + __ movptr(Address(rcx, JNIHandleBlock::top_offset_in_bytes()), (int)NULL_WORD); + + // pop our frame + + __ leave(); + + // Any exception pending? + __ cmpq(Address(r15_thread, in_bytes(Thread::pending_exception_offset())), (int)NULL_WORD); + __ jcc(Assembler::notEqual, exception_pending); + + // Return + + __ ret(0); + + // Unexpected paths are out of line and go here + + // forward the exception + __ bind(exception_pending); + + // and forward the exception + __ jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + + + // Slow path locking & unlocking + if (method->is_synchronized()) { + + // BEGIN Slow path lock + __ bind(slow_path_lock); + + // has last_Java_frame setup. No exceptions so do vanilla call not call_VM + // args are (oop obj, BasicLock* lock, JavaThread* thread) + + // protect the args we've loaded + save_args(masm, total_c_args, c_arg, out_regs); + + __ movq(c_rarg0, obj_reg); + __ movq(c_rarg1, lock_reg); + __ movq(c_rarg2, r15_thread); + + // Not a leaf but we have last_Java_frame setup as we want + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C), 3); + restore_args(masm, total_c_args, c_arg, out_regs); + +#ifdef ASSERT + { Label L; + __ cmpq(Address(r15_thread, in_bytes(Thread::pending_exception_offset())), (int)NULL_WORD); + __ jcc(Assembler::equal, L); + __ stop("no pending exception allowed on exit from monitorenter"); + __ bind(L); + } +#endif + __ jmp(lock_done); + + // END Slow path lock + + // BEGIN Slow path unlock + __ bind(slow_path_unlock); + + // If we haven't already saved the native result we must save it now as xmm registers + // are still exposed. + + if (ret_type == T_FLOAT || ret_type == T_DOUBLE ) { + save_native_result(masm, ret_type, stack_slots); + } + + __ leaq(c_rarg1, Address(rsp, lock_slot_offset * VMRegImpl::stack_slot_size)); + + __ movq(c_rarg0, obj_reg); + __ movq(r12, rsp); // remember sp + __ subq(rsp, frame::arg_reg_save_area_bytes); // windows + __ andq(rsp, -16); // align stack as required by ABI + + // Save pending exception around call to VM (which contains an EXCEPTION_MARK) + // NOTE that obj_reg == rbx currently + __ movq(rbx, Address(r15_thread, in_bytes(Thread::pending_exception_offset()))); + __ movptr(Address(r15_thread, in_bytes(Thread::pending_exception_offset())), (int)NULL_WORD); + + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_unlocking_C))); + __ movq(rsp, r12); // restore sp +#ifdef ASSERT + { + Label L; + __ cmpq(Address(r15_thread, in_bytes(Thread::pending_exception_offset())), (int)NULL_WORD); + __ jcc(Assembler::equal, L); + __ stop("no pending exception allowed on exit complete_monitor_unlocking_C"); + __ bind(L); + } +#endif /* ASSERT */ + + __ movq(Address(r15_thread, in_bytes(Thread::pending_exception_offset())), rbx); + + if (ret_type == T_FLOAT || ret_type == T_DOUBLE ) { + restore_native_result(masm, ret_type, stack_slots); + } + __ jmp(unlock_done); + + // END Slow path unlock + + } // synchronized + + // SLOW PATH Reguard the stack if needed + + __ bind(reguard); + save_native_result(masm, ret_type, stack_slots); + __ movq(r12, rsp); // remember sp + __ subq(rsp, frame::arg_reg_save_area_bytes); // windows + __ andq(rsp, -16); // align stack as required by ABI + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages))); + __ movq(rsp, r12); // restore sp + restore_native_result(masm, ret_type, stack_slots); + // and continue + __ jmp(reguard_done); + + + + __ flush(); + + nmethod *nm = nmethod::new_native_nmethod(method, + masm->code(), + vep_offset, + frame_complete, + stack_slots / VMRegImpl::slots_per_word, + (is_static ? in_ByteSize(klass_offset) : in_ByteSize(receiver_offset)), + in_ByteSize(lock_slot_offset*VMRegImpl::stack_slot_size), + oop_maps); + return nm; + +} + +// this function returns the adjust size (in number of words) to a c2i adapter +// activation for use during deoptimization +int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals ) { + return (callee_locals - callee_parameters) * Interpreter::stackElementWords(); +} + + +uint SharedRuntime::out_preserve_stack_slots() { + return 0; +} + + +//------------------------------generate_deopt_blob---------------------------- +void SharedRuntime::generate_deopt_blob() { + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("deopt_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + int frame_size_in_words; + OopMap* map = NULL; + OopMapSet *oop_maps = new OopMapSet(); + + // ------------- + // This code enters when returning to a de-optimized nmethod. A return + // address has been pushed on the the stack, and return values are in + // registers. + // If we are doing a normal deopt then we were called from the patched + // nmethod from the point we returned to the nmethod. So the return + // address on the stack is wrong by NativeCall::instruction_size + // We will adjust the value so it looks like we have the original return + // address on the stack (like when we eagerly deoptimized). + // In the case of an exception pending when deoptimizing, we enter + // with a return address on the stack that points after the call we patched + // into the exception handler. We have the following register state from, + // e.g., the forward exception stub (see stubGenerator_x86_64.cpp). + // rax: exception oop + // rbx: exception handler + // rdx: throwing pc + // So in this case we simply jam rdx into the useless return address and + // the stack looks just like we want. + // + // At this point we need to de-opt. We save the argument return + // registers. We call the first C routine, fetch_unroll_info(). This + // routine captures the return values and returns a structure which + // describes the current frame size and the sizes of all replacement frames. + // The current frame is compiled code and may contain many inlined + // functions, each with their own JVM state. We pop the current frame, then + // push all the new frames. Then we call the C routine unpack_frames() to + // populate these frames. Finally unpack_frames() returns us the new target + // address. Notice that callee-save registers are BLOWN here; they have + // already been captured in the vframeArray at the time the return PC was + // patched. + address start = __ pc(); + Label cont; + + // Prolog for non exception case! + + // Save everything in sight. + map = RegisterSaver::save_live_registers(masm, 0, &frame_size_in_words); + + // Normal deoptimization. Save exec mode for unpack_frames. + __ movl(r12, Deoptimization::Unpack_deopt); // callee-saved + __ jmp(cont); + + int exception_offset = __ pc() - start; + + // Prolog for exception case + + // Push throwing pc as return address + __ pushq(rdx); + + // Save everything in sight. + map = RegisterSaver::save_live_registers(masm, 0, &frame_size_in_words); + + // Deopt during an exception. Save exec mode for unpack_frames. + __ movl(r12, Deoptimization::Unpack_exception); // callee-saved + + __ bind(cont); + + // Call C code. Need thread and this frame, but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. + // + // UnrollBlock* fetch_unroll_info(JavaThread* thread) + + // fetch_unroll_info needs to call last_java_frame(). + + __ set_last_Java_frame(noreg, noreg, NULL); +#ifdef ASSERT + { Label L; + __ cmpq(Address(r15_thread, + JavaThread::last_Java_fp_offset()), + 0); + __ jcc(Assembler::equal, L); + __ stop("SharedRuntime::generate_deopt_blob: last_Java_fp not cleared"); + __ bind(L); + } +#endif // ASSERT + __ movq(c_rarg0, r15_thread); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info))); + + // Need to have an oopmap that tells fetch_unroll_info where to + // find any register it might need. + oop_maps->add_gc_map(__ pc() - start, map); + + __ reset_last_Java_frame(false, false); + + // Load UnrollBlock* into rdi + __ movq(rdi, rax); + + // Only register save data is on the stack. + // Now restore the result registers. Everything else is either dead + // or captured in the vframeArray. + RegisterSaver::restore_result_registers(masm); + + // All of the register save area has been popped of the stack. Only the + // return address remains. + + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + // + // Note: by leaving the return address of self-frame on the stack + // and using the size of frame 2 to adjust the stack + // when we are done the return to frame 3 will still be on the stack. + + // Pop deoptimized frame + __ movl(rcx, Address(rdi, Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset_in_bytes())); + __ addq(rsp, rcx); + + // rsp should be pointing at the return address to the caller (3) + + // Stack bang to make sure there's enough room for these interpreter frames. + if (UseStackBanging) { + __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::total_frame_sizes_offset_in_bytes())); + __ bang_stack_size(rbx, rcx); + } + + // Load address of array of frame pcs into rcx + __ movq(rcx, Address(rdi, Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes())); + + // Trash the old pc + __ addq(rsp, wordSize); + + // Load address of array of frame sizes into rsi + __ movq(rsi, Address(rdi, Deoptimization::UnrollBlock::frame_sizes_offset_in_bytes())); + + // Load counter into rdx + __ movl(rdx, Address(rdi, Deoptimization::UnrollBlock::number_of_frames_offset_in_bytes())); + + // Pick up the initial fp we should save + __ movq(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_fp_offset_in_bytes())); + + // Now adjust the caller's stack to make up for the extra locals + // but record the original sp so that we can save it in the skeletal interpreter + // frame and the stack walking of interpreter_sender will get the unextended sp + // value and not the "real" sp value. + + const Register sender_sp = r8; + + __ movq(sender_sp, rsp); + __ movl(rbx, Address(rdi, + Deoptimization::UnrollBlock:: + caller_adjustment_offset_in_bytes())); + __ subq(rsp, rbx); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ movq(rbx, Address(rsi, 0)); // Load frame size + __ subq(rbx, 2*wordSize); // We'll push pc and ebp by hand + __ pushq(Address(rcx, 0)); // Save return address + __ enter(); // Save old & set new ebp + __ subq(rsp, rbx); // Prolog + __ movq(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), + sender_sp); // Make it walkable + // This value is corrected by layout_activation_impl + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), (int)NULL_WORD ); + __ movq(sender_sp, rsp); // Pass sender_sp to next frame + __ addq(rsi, wordSize); // Bump array pointer (sizes) + __ addq(rcx, wordSize); // Bump array pointer (pcs) + __ decrementl(rdx); // Decrement counter + __ jcc(Assembler::notZero, loop); + __ pushq(Address(rcx, 0)); // Save final return address + + // Re-push self-frame + __ enter(); // Save old & set new ebp + + // Allocate a full sized register save area. + // Return address and rbp are in place, so we allocate two less words. + __ subq(rsp, (frame_size_in_words - 2) * wordSize); + + // Restore frame locals after moving the frame + __ movdbl(Address(rsp, RegisterSaver::xmm0_offset_in_bytes()), xmm0); + __ movq(Address(rsp, RegisterSaver::rax_offset_in_bytes()), rax); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + // + // void Deoptimization::unpack_frames(JavaThread* thread, int exec_mode) + + // Use rbp because the frames look interpreted now + __ set_last_Java_frame(noreg, rbp, NULL); + + __ movq(c_rarg0, r15_thread); + __ movl(c_rarg1, r12); // second arg: exec_mode + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); + + // Set an oopmap for the call site + oop_maps->add_gc_map(__ pc() - start, + new OopMap( frame_size_in_words, 0 )); + + __ reset_last_Java_frame(true, false); + + // Collect return values + __ movdbl(xmm0, Address(rsp, RegisterSaver::xmm0_offset_in_bytes())); + __ movq(rax, Address(rsp, RegisterSaver::rax_offset_in_bytes())); + + // Pop self-frame. + __ leave(); // Epilog + + // Jump to interpreter + __ ret(0); + + // Make sure all code is generated + masm->flush(); + + _deopt_blob = DeoptimizationBlob::create(&buffer, oop_maps, 0, exception_offset, 0, frame_size_in_words); +} + +#ifdef COMPILER2 +//------------------------------generate_uncommon_trap_blob-------------------- +void SharedRuntime::generate_uncommon_trap_blob() { + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + address start = __ pc(); + + // Push self-frame. We get here with a return address on the + // stack, so rsp is 8-byte aligned until we allocate our frame. + __ subq(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog! + + // No callee saved registers. rbp is assumed implicitly saved + __ movq(Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt), rbp); + + // compiler left unloaded_class_index in j_rarg0 move to where the + // runtime expects it. + __ movl(c_rarg1, j_rarg0); + + __ set_last_Java_frame(noreg, noreg, NULL); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // capture callee-saved registers as well as return values. + // Thread is in rdi already. + // + // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); + + __ movq(c_rarg0, r15_thread); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); + + // Set an oopmap for the call site + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); + + // location of rbp is known implicitly by the frame sender code + + oop_maps->add_gc_map(__ pc() - start, map); + + __ reset_last_Java_frame(false, false); + + // Load UnrollBlock* into rdi + __ movq(rdi, rax); + + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + + // Pop self-frame. We have no frame, and must rely only on rax and rsp. + __ addq(rsp, (SimpleRuntimeFrame::framesize - 2) << LogBytesPerInt); // Epilog! + + // Pop deoptimized frame (int) + __ movl(rcx, Address(rdi, + Deoptimization::UnrollBlock:: + size_of_deoptimized_frame_offset_in_bytes())); + __ addq(rsp, rcx); + + // rsp should be pointing at the return address to the caller (3) + + // Stack bang to make sure there's enough room for these interpreter frames. + if (UseStackBanging) { + __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset_in_bytes())); + __ bang_stack_size(rbx, rcx); + } + + // Load address of array of frame pcs into rcx (address*) + __ movq(rcx, + Address(rdi, + Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes())); + + // Trash the return pc + __ addq(rsp, wordSize); + + // Load address of array of frame sizes into rsi (intptr_t*) + __ movq(rsi, Address(rdi, + Deoptimization::UnrollBlock:: + frame_sizes_offset_in_bytes())); + + // Counter + __ movl(rdx, Address(rdi, + Deoptimization::UnrollBlock:: + number_of_frames_offset_in_bytes())); // (int) + + // Pick up the initial fp we should save + __ movq(rbp, + Address(rdi, + Deoptimization::UnrollBlock::initial_fp_offset_in_bytes())); + + // Now adjust the caller's stack to make up for the extra locals but + // record the original sp so that we can save it in the skeletal + // interpreter frame and the stack walking of interpreter_sender + // will get the unextended sp value and not the "real" sp value. + + const Register sender_sp = r8; + + __ movq(sender_sp, rsp); + __ movl(rbx, Address(rdi, + Deoptimization::UnrollBlock:: + caller_adjustment_offset_in_bytes())); // (int) + __ subq(rsp, rbx); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ movq(rbx, Address(rsi, 0)); // Load frame size + __ subq(rbx, 2 * wordSize); // We'll push pc and rbp by hand + __ pushq(Address(rcx, 0)); // Save return address + __ enter(); // Save old & set new rbp + __ subq(rsp, rbx); // Prolog + __ movq(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), + sender_sp); // Make it walkable + // This value is corrected by layout_activation_impl + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), (int)NULL_WORD ); + __ movq(sender_sp, rsp); // Pass sender_sp to next frame + __ addq(rsi, wordSize); // Bump array pointer (sizes) + __ addq(rcx, wordSize); // Bump array pointer (pcs) + __ decrementl(rdx); // Decrement counter + __ jcc(Assembler::notZero, loop); + __ pushq(Address(rcx, 0)); // Save final return address + + // Re-push self-frame + __ enter(); // Save old & set new rbp + __ subq(rsp, (SimpleRuntimeFrame::framesize - 4) << LogBytesPerInt); + // Prolog + + // Use rbp because the frames look interpreted now + __ set_last_Java_frame(noreg, rbp, NULL); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + // Thread is in rdi already. + // + // BasicType unpack_frames(JavaThread* thread, int exec_mode); + + __ movq(c_rarg0, r15_thread); + __ movl(c_rarg1, Deoptimization::Unpack_uncommon_trap); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); + + // Set an oopmap for the call site + oop_maps->add_gc_map(__ pc() - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + __ reset_last_Java_frame(true, false); + + // Pop self-frame. + __ leave(); // Epilog + + // Jump to interpreter + __ ret(0); + + // Make sure all code is generated + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, + SimpleRuntimeFrame::framesize >> 1); +} +#endif // COMPILER2 + + +//------------------------------generate_handler_blob------ +// +// Generate a special Compile2Runtime blob that saves all registers, +// and setup oopmap. +// +static SafepointBlob* generate_handler_blob(address call_ptr, bool cause_return) { + assert(StubRoutines::forward_exception_entry() != NULL, + "must be generated before"); + + ResourceMark rm; + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map; + + // Allocate space for the code. Setup code generation tools. + CodeBuffer buffer("handler_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + address start = __ pc(); + address call_pc = NULL; + int frame_size_in_words; + + // Make room for return address (or push it again) + if (!cause_return) { + __ pushq(rbx); + } + + // Save registers, fpu state, and flags + map = RegisterSaver::save_live_registers(masm, 0, &frame_size_in_words); + + // The following is basically a call_VM. However, we need the precise + // address of the call in order to generate an oopmap. Hence, we do all the + // work outselves. + + __ set_last_Java_frame(noreg, noreg, NULL); + + // The return address must always be correct so that frame constructor never + // sees an invalid pc. + + if (!cause_return) { + // overwrite the dummy value we pushed on entry + __ movq(c_rarg0, Address(r15_thread, JavaThread::saved_exception_pc_offset())); + __ movq(Address(rbp, wordSize), c_rarg0); + } + + // Do the call + __ movq(c_rarg0, r15_thread); + __ call(RuntimeAddress(call_ptr)); + + // Set an oopmap for the call site. This oopmap will map all + // oop-registers and debug-info registers as callee-saved. This + // will allow deoptimization at this safepoint to find all possible + // debug-info recordings, as well as let GC find all oops. + + oop_maps->add_gc_map( __ pc() - start, map); + + Label noException; + + __ reset_last_Java_frame(false, false); + + __ cmpq(Address(r15_thread, Thread::pending_exception_offset()), (int)NULL_WORD); + __ jcc(Assembler::equal, noException); + + // Exception pending + + RegisterSaver::restore_live_registers(masm); + + __ jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + + // No exception case + __ bind(noException); + + // Normal exit, restore registers and exit. + RegisterSaver::restore_live_registers(masm); + + __ ret(0); + + // Make sure all code is generated + masm->flush(); + + // Fill-out other meta info + return SafepointBlob::create(&buffer, oop_maps, frame_size_in_words); +} + +// +// generate_resolve_blob - call resolution (static/virtual/opt-virtual/ic-miss +// +// Generate a stub that calls into vm to find out the proper destination +// of a java call. All the argument registers are live at this point +// but since this is generic code we don't know what they are and the caller +// must do any gc of the args. +// +static RuntimeStub* generate_resolve_blob(address destination, const char* name) { + assert (StubRoutines::forward_exception_entry() != NULL, "must be generated before"); + + // allocate space for the code + ResourceMark rm; + + CodeBuffer buffer(name, 1000, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + + int frame_size_in_words; + + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map = NULL; + + int start = __ offset(); + + map = RegisterSaver::save_live_registers(masm, 0, &frame_size_in_words); + + int frame_complete = __ offset(); + + __ set_last_Java_frame(noreg, noreg, NULL); + + __ movq(c_rarg0, r15_thread); + + __ call(RuntimeAddress(destination)); + + + // Set an oopmap for the call site. + // We need this not only for callee-saved registers, but also for volatile + // registers that the compiler might be keeping live across a safepoint. + + oop_maps->add_gc_map( __ offset() - start, map); + + // rax contains the address we are going to jump to assuming no exception got installed + + // clear last_Java_sp + __ reset_last_Java_frame(false, false); + // check for pending exceptions + Label pending; + __ cmpq(Address(r15_thread, Thread::pending_exception_offset()), (int)NULL_WORD); + __ jcc(Assembler::notEqual, pending); + + // get the returned methodOop + __ movq(rbx, Address(r15_thread, JavaThread::vm_result_offset())); + __ movq(Address(rsp, RegisterSaver::rbx_offset_in_bytes()), rbx); + + __ movq(Address(rsp, RegisterSaver::rax_offset_in_bytes()), rax); + + RegisterSaver::restore_live_registers(masm); + + // We are back the the original state on entry and ready to go. + + __ jmp(rax); + + // Pending exception after the safepoint + + __ bind(pending); + + RegisterSaver::restore_live_registers(masm); + + // exception pending => remove activation and forward to exception handler + + __ movptr(Address(r15_thread, JavaThread::vm_result_offset()), (int)NULL_WORD); + + __ movq(rax, Address(r15_thread, Thread::pending_exception_offset())); + __ jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + + // ------------- + // make sure all code is generated + masm->flush(); + + // return the blob + // frame_size_words or bytes?? + return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true); +} + + +void SharedRuntime::generate_stubs() { + + _wrong_method_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method), + "wrong_method_stub"); + _ic_miss_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method_ic_miss), + "ic_miss_stub"); + _resolve_opt_virtual_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_opt_virtual_call_C), + "resolve_opt_virtual_call"); + + _resolve_virtual_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_virtual_call_C), + "resolve_virtual_call"); + + _resolve_static_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_static_call_C), + "resolve_static_call"); + _polling_page_safepoint_handler_blob = + generate_handler_blob(CAST_FROM_FN_PTR(address, + SafepointSynchronize::handle_polling_page_exception), false); + + _polling_page_return_handler_blob = + generate_handler_blob(CAST_FROM_FN_PTR(address, + SafepointSynchronize::handle_polling_page_exception), true); + + generate_deopt_blob(); + +#ifdef COMPILER2 + generate_uncommon_trap_blob(); +#endif // COMPILER2 +} + + +#ifdef COMPILER2 +// This is here instead of runtime_x86_64.cpp because it uses SimpleRuntimeFrame +// +//------------------------------generate_exception_blob--------------------------- +// creates exception blob at the end +// Using exception blob, this code is jumped from a compiled method. +// (see emit_exception_handler in x86_64.ad file) +// +// Given an exception pc at a call we call into the runtime for the +// handler in this method. This handler might merely restore state +// (i.e. callee save registers) unwind the frame and jump to the +// exception handler for the nmethod if there is no Java level handler +// for the nmethod. +// +// This code is entered with a jmp. +// +// Arguments: +// rax: exception oop +// rdx: exception pc +// +// Results: +// rax: exception oop +// rdx: exception pc in caller or ??? +// destination: exception handler of caller +// +// Note: the exception pc MUST be at a call (precise debug information) +// Registers rax, rdx, rcx, rsi, rdi, r8-r11 are not callee saved. +// + +void OptoRuntime::generate_exception_blob() { + assert(!OptoRuntime::is_callee_saved_register(RDX_num), ""); + assert(!OptoRuntime::is_callee_saved_register(RAX_num), ""); + assert(!OptoRuntime::is_callee_saved_register(RCX_num), ""); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("exception_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + + address start = __ pc(); + + // Exception pc is 'return address' for stack walker + __ pushq(rdx); + __ subq(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Prolog + + // Save callee-saved registers. See x86_64.ad. + + // rbp is an implicitly saved callee saved register (i.e. the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers now that adapter frames are gone. + + __ movq(Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt), rbp); + + // Store exception in Thread object. We cannot pass any arguments to the + // handle_exception call, since we do not want to make any assumption + // about the size of the frame where the exception happened in. + // c_rarg0 is either rdi (Linux) or rcx (Windows). + __ movq(Address(r15_thread, JavaThread::exception_oop_offset()),rax); + __ movq(Address(r15_thread, JavaThread::exception_pc_offset()), rdx); + + // This call does all the hard work. It checks if an exception handler + // exists in the method. + // If so, it returns the handler address. + // If not, it prepares for stack-unwinding, restoring the callee-save + // registers of the frame being removed. + // + // address OptoRuntime::handle_exception_C(JavaThread* thread) + + __ set_last_Java_frame(noreg, noreg, NULL); + __ movq(c_rarg0, r15_thread); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C))); + + // Set an oopmap for the call site. This oopmap will only be used if we + // are unwinding the stack. Hence, all locations will be dead. + // Callee-saved registers will be the same as the frame above (i.e., + // handle_exception_stub), since they were restored when we got the + // exception. + + OopMapSet* oop_maps = new OopMapSet(); + + oop_maps->add_gc_map( __ pc()-start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + __ reset_last_Java_frame(false, false); + + // Restore callee-saved registers + + // rbp is an implicitly saved callee saved register (i.e. the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers no that adapter frames are gone. + + __ movq(rbp, Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt)); + + __ addq(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog + __ popq(rdx); // No need for exception pc anymore + + // rax: exception handler + + // We have a handler in rax (could be deopt blob). + __ movq(r8, rax); + + // Get the exception oop + __ movq(rax, Address(r15_thread, JavaThread::exception_oop_offset())); + // Get the exception pc in case we are deoptimized + __ movq(rdx, Address(r15_thread, JavaThread::exception_pc_offset())); +#ifdef ASSERT + __ movptr(Address(r15_thread, JavaThread::exception_handler_pc_offset()), (int)NULL_WORD); + __ movptr(Address(r15_thread, JavaThread::exception_pc_offset()), (int)NULL_WORD); +#endif + // Clear the exception oop so GC no longer processes it as a root. + __ movptr(Address(r15_thread, JavaThread::exception_oop_offset()), (int)NULL_WORD); + + // rax: exception oop + // r8: exception handler + // rdx: exception pc + // Jump to handler + + __ jmp(r8); + + // Make sure all code is generated + masm->flush(); + + // Set exception blob + _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); +} +#endif // COMPILER2 diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp new file mode 100644 index 00000000000..98c1ce59a60 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp @@ -0,0 +1,2183 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubGenerator_x86_32.cpp.incl" + +// Declaration and definition of StubGenerator (no .hpp file). +// For a more detailed description of the stub routine structure +// see the comment in stubRoutines.hpp + +#define __ _masm-> + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) /* nothing */ +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") + +const int MXCSR_MASK = 0xFFC0; // Mask out any pending exceptions +const int FPU_CNTRL_WRD_MASK = 0xFFFF; + +// ------------------------------------------------------------------------------------------------------------------------- +// Stub Code definitions + +static address handle_unsafe_access() { + JavaThread* thread = JavaThread::current(); + address pc = thread->saved_exception_pc(); + // pc is the instruction which we must emulate + // doing a no-op is fine: return garbage from the load + // therefore, compute npc + address npc = Assembler::locate_next_instruction(pc); + + // request an async exception + thread->set_pending_unsafe_access_error(); + + // return address of next instruction to execute + return npc; +} + +class StubGenerator: public StubCodeGenerator { + private: + +#ifdef PRODUCT +#define inc_counter_np(counter) (0) +#else + void inc_counter_np_(int& counter) { + __ increment(ExternalAddress((address)&counter)); + } +#define inc_counter_np(counter) \ + BLOCK_COMMENT("inc_counter " #counter); \ + inc_counter_np_(counter); +#endif //PRODUCT + + void inc_copy_counter_np(BasicType t) { +#ifndef PRODUCT + switch (t) { + case T_BYTE: inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr); return; + case T_SHORT: inc_counter_np(SharedRuntime::_jshort_array_copy_ctr); return; + case T_INT: inc_counter_np(SharedRuntime::_jint_array_copy_ctr); return; + case T_LONG: inc_counter_np(SharedRuntime::_jlong_array_copy_ctr); return; + case T_OBJECT: inc_counter_np(SharedRuntime::_oop_array_copy_ctr); return; + } + ShouldNotReachHere(); +#endif //PRODUCT + } + + //------------------------------------------------------------------------------------------------------------------------ + // Call stubs are used to call Java from C + // + // [ return_from_Java ] <--- rsp + // [ argument word n ] + // ... + // -N [ argument word 1 ] + // -7 [ Possible padding for stack alignment ] + // -6 [ Possible padding for stack alignment ] + // -5 [ Possible padding for stack alignment ] + // -4 [ mxcsr save ] <--- rsp_after_call + // -3 [ saved rbx, ] + // -2 [ saved rsi ] + // -1 [ saved rdi ] + // 0 [ saved rbp, ] <--- rbp, + // 1 [ return address ] + // 2 [ ptr. to call wrapper ] + // 3 [ result ] + // 4 [ result_type ] + // 5 [ method ] + // 6 [ entry_point ] + // 7 [ parameters ] + // 8 [ parameter_size ] + // 9 [ thread ] + + + address generate_call_stub(address& return_address) { + StubCodeMark mark(this, "StubRoutines", "call_stub"); + address start = __ pc(); + + // stub code parameters / addresses + assert(frame::entry_frame_call_wrapper_offset == 2, "adjust this code"); + bool sse_save = false; + const Address rsp_after_call(rbp, -4 * wordSize); // same as in generate_catch_exception()! + const int locals_count_in_bytes (4*wordSize); + const Address mxcsr_save (rbp, -4 * wordSize); + const Address saved_rbx (rbp, -3 * wordSize); + const Address saved_rsi (rbp, -2 * wordSize); + const Address saved_rdi (rbp, -1 * wordSize); + const Address result (rbp, 3 * wordSize); + const Address result_type (rbp, 4 * wordSize); + const Address method (rbp, 5 * wordSize); + const Address entry_point (rbp, 6 * wordSize); + const Address parameters (rbp, 7 * wordSize); + const Address parameter_size(rbp, 8 * wordSize); + const Address thread (rbp, 9 * wordSize); // same as in generate_catch_exception()! + sse_save = UseSSE > 0; + + // stub code + __ enter(); + __ movl(rcx, parameter_size); // parameter counter + __ shll(rcx, Interpreter::logStackElementSize()); // convert parameter count to bytes + __ addl(rcx, locals_count_in_bytes); // reserve space for register saves + __ subl(rsp, rcx); + __ andl(rsp, -(StackAlignmentInBytes)); // Align stack + + // save rdi, rsi, & rbx, according to C calling conventions + __ movl(saved_rdi, rdi); + __ movl(saved_rsi, rsi); + __ movl(saved_rbx, rbx); + // save and initialize %mxcsr + if (sse_save) { + Label skip_ldmx; + __ stmxcsr(mxcsr_save); + __ movl(rax, mxcsr_save); + __ andl(rax, MXCSR_MASK); // Only check control and mask bits + ExternalAddress mxcsr_std(StubRoutines::addr_mxcsr_std()); + __ cmp32(rax, mxcsr_std); + __ jcc(Assembler::equal, skip_ldmx); + __ ldmxcsr(mxcsr_std); + __ bind(skip_ldmx); + } + + // make sure the control word is correct. + __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); + +#ifdef ASSERT + // make sure we have no pending exceptions + { Label L; + __ movl(rcx, thread); + __ cmpl(Address(rcx, Thread::pending_exception_offset()), NULL_WORD); + __ jcc(Assembler::equal, L); + __ stop("StubRoutines::call_stub: entered with pending exception"); + __ bind(L); + } +#endif + + // pass parameters if any + BLOCK_COMMENT("pass parameters if any"); + Label parameters_done; + __ movl(rcx, parameter_size); // parameter counter + __ testl(rcx, rcx); + __ jcc(Assembler::zero, parameters_done); + + // parameter passing loop + + Label loop; + // Copy Java parameters in reverse order (receiver last) + // Note that the argument order is inverted in the process + // source is rdx[rcx: N-1..0] + // dest is rsp[rbx: 0..N-1] + + __ movl(rdx, parameters); // parameter pointer + __ xorl(rbx, rbx); + + __ BIND(loop); + if (TaggedStackInterpreter) { + __ movl(rax, Address(rdx, rcx, Interpreter::stackElementScale(), + -2*wordSize)); // get tag + __ movl(Address(rsp, rbx, Interpreter::stackElementScale(), + Interpreter::expr_tag_offset_in_bytes(0)), rax); // store tag + } + + // get parameter + __ movl(rax, Address(rdx, rcx, Interpreter::stackElementScale(), -wordSize)); + __ movl(Address(rsp, rbx, Interpreter::stackElementScale(), + Interpreter::expr_offset_in_bytes(0)), rax); // store parameter + __ increment(rbx); + __ decrement(rcx); + __ jcc(Assembler::notZero, loop); + + // call Java function + __ BIND(parameters_done); + __ movl(rbx, method); // get methodOop + __ movl(rax, entry_point); // get entry_point + __ movl(rsi, rsp); // set sender sp + BLOCK_COMMENT("call Java function"); + __ call(rax); + + BLOCK_COMMENT("call_stub_return_address:"); + return_address = __ pc(); + + Label common_return; + + __ BIND(common_return); + + // store result depending on type + // (everything that is not T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT) + __ movl(rdi, result); + Label is_long, is_float, is_double, exit; + __ movl(rsi, result_type); + __ cmpl(rsi, T_LONG); + __ jcc(Assembler::equal, is_long); + __ cmpl(rsi, T_FLOAT); + __ jcc(Assembler::equal, is_float); + __ cmpl(rsi, T_DOUBLE); + __ jcc(Assembler::equal, is_double); + + // handle T_INT case + __ movl(Address(rdi, 0), rax); + __ BIND(exit); + + // check that FPU stack is empty + __ verify_FPU(0, "generate_call_stub"); + + // pop parameters + __ leal(rsp, rsp_after_call); + + // restore %mxcsr + if (sse_save) { + __ ldmxcsr(mxcsr_save); + } + + // restore rdi, rsi and rbx, + __ movl(rbx, saved_rbx); + __ movl(rsi, saved_rsi); + __ movl(rdi, saved_rdi); + __ addl(rsp, 4*wordSize); + + // return + __ popl(rbp); + __ ret(0); + + // handle return types different from T_INT + __ BIND(is_long); + __ movl(Address(rdi, 0 * wordSize), rax); + __ movl(Address(rdi, 1 * wordSize), rdx); + __ jmp(exit); + + __ BIND(is_float); + // interpreter uses xmm0 for return values + if (UseSSE >= 1) { + __ movflt(Address(rdi, 0), xmm0); + } else { + __ fstp_s(Address(rdi, 0)); + } + __ jmp(exit); + + __ BIND(is_double); + // interpreter uses xmm0 for return values + if (UseSSE >= 2) { + __ movdbl(Address(rdi, 0), xmm0); + } else { + __ fstp_d(Address(rdi, 0)); + } + __ jmp(exit); + + // If we call compiled code directly from the call stub we will + // need to adjust the return back to the call stub to a specialized + // piece of code that can handle compiled results and cleaning the fpu + // stack. compiled code will be set to return here instead of the + // return above that handles interpreter returns. + + BLOCK_COMMENT("call_stub_compiled_return:"); + StubRoutines::i486::set_call_stub_compiled_return( __ pc()); + +#ifdef COMPILER2 + if (UseSSE >= 2) { + __ verify_FPU(0, "call_stub_compiled_return"); + } else { + for (int i = 1; i < 8; i++) { + __ ffree(i); + } + + // UseSSE <= 1 so double result should be left on TOS + __ movl(rsi, result_type); + __ cmpl(rsi, T_DOUBLE); + __ jcc(Assembler::equal, common_return); + if (UseSSE == 0) { + // UseSSE == 0 so float result should be left on TOS + __ cmpl(rsi, T_FLOAT); + __ jcc(Assembler::equal, common_return); + } + __ ffree(0); + } +#endif /* COMPILER2 */ + __ jmp(common_return); + + return start; + } + + + //------------------------------------------------------------------------------------------------------------------------ + // Return point for a Java call if there's an exception thrown in Java code. + // The exception is caught and transformed into a pending exception stored in + // JavaThread that can be tested from within the VM. + // + // Note: Usually the parameters are removed by the callee. In case of an exception + // crossing an activation frame boundary, that is not the case if the callee + // is compiled code => need to setup the rsp. + // + // rax,: exception oop + + address generate_catch_exception() { + StubCodeMark mark(this, "StubRoutines", "catch_exception"); + const Address rsp_after_call(rbp, -4 * wordSize); // same as in generate_call_stub()! + const Address thread (rbp, 9 * wordSize); // same as in generate_call_stub()! + address start = __ pc(); + + // get thread directly + __ movl(rcx, thread); +#ifdef ASSERT + // verify that threads correspond + { Label L; + __ get_thread(rbx); + __ cmpl(rbx, rcx); + __ jcc(Assembler::equal, L); + __ stop("StubRoutines::catch_exception: threads must correspond"); + __ bind(L); + } +#endif + // set pending exception + __ verify_oop(rax); + __ movl(Address(rcx, Thread::pending_exception_offset()), rax ); + __ lea(Address(rcx, Thread::exception_file_offset ()), + ExternalAddress((address)__FILE__)); + __ movl(Address(rcx, Thread::exception_line_offset ()), __LINE__ ); + // complete return to VM + assert(StubRoutines::_call_stub_return_address != NULL, "_call_stub_return_address must have been generated before"); + __ jump(RuntimeAddress(StubRoutines::_call_stub_return_address)); + + return start; + } + + + //------------------------------------------------------------------------------------------------------------------------ + // Continuation point for runtime calls returning with a pending exception. + // The pending exception check happened in the runtime or native call stub. + // The pending exception in Thread is converted into a Java-level exception. + // + // Contract with Java-level exception handlers: + // rax,: exception + // rdx: throwing pc + // + // NOTE: At entry of this stub, exception-pc must be on stack !! + + address generate_forward_exception() { + StubCodeMark mark(this, "StubRoutines", "forward exception"); + address start = __ pc(); + + // Upon entry, the sp points to the return address returning into Java + // (interpreted or compiled) code; i.e., the return address becomes the + // throwing pc. + // + // Arguments pushed before the runtime call are still on the stack but + // the exception handler will reset the stack pointer -> ignore them. + // A potential result in registers can be ignored as well. + +#ifdef ASSERT + // make sure this code is only executed if there is a pending exception + { Label L; + __ get_thread(rcx); + __ cmpl(Address(rcx, Thread::pending_exception_offset()), NULL_WORD); + __ jcc(Assembler::notEqual, L); + __ stop("StubRoutines::forward exception: no pending exception (1)"); + __ bind(L); + } +#endif + + // compute exception handler into rbx, + __ movl(rax, Address(rsp, 0)); + BLOCK_COMMENT("call exception_handler_for_return_address"); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), rax); + __ movl(rbx, rax); + + // setup rax, & rdx, remove return address & clear pending exception + __ get_thread(rcx); + __ popl(rdx); + __ movl(rax, Address(rcx, Thread::pending_exception_offset())); + __ movl(Address(rcx, Thread::pending_exception_offset()), NULL_WORD); + +#ifdef ASSERT + // make sure exception is set + { Label L; + __ testl(rax, rax); + __ jcc(Assembler::notEqual, L); + __ stop("StubRoutines::forward exception: no pending exception (2)"); + __ bind(L); + } +#endif + + // continue at exception handler (return address removed) + // rax,: exception + // rbx,: exception handler + // rdx: throwing pc + __ verify_oop(rax); + __ jmp(rbx); + + return start; + } + + + //---------------------------------------------------------------------------------------------------- + // Support for jint Atomic::xchg(jint exchange_value, volatile jint* dest) + // + // xchg exists as far back as 8086, lock needed for MP only + // Stack layout immediately after call: + // + // 0 [ret addr ] <--- rsp + // 1 [ ex ] + // 2 [ dest ] + // + // Result: *dest <- ex, return (old *dest) + // + // Note: win32 does not currently use this code + + address generate_atomic_xchg() { + StubCodeMark mark(this, "StubRoutines", "atomic_xchg"); + address start = __ pc(); + + __ pushl(rdx); + Address exchange(rsp, 2 * wordSize); + Address dest_addr(rsp, 3 * wordSize); + __ movl(rax, exchange); + __ movl(rdx, dest_addr); + __ xchg(rax, Address(rdx, 0)); + __ popl(rdx); + __ ret(0); + + return start; + } + + //---------------------------------------------------------------------------------------------------- + // Support for void verify_mxcsr() + // + // This routine is used with -Xcheck:jni to verify that native + // JNI code does not return to Java code without restoring the + // MXCSR register to our expected state. + + + address generate_verify_mxcsr() { + StubCodeMark mark(this, "StubRoutines", "verify_mxcsr"); + address start = __ pc(); + + const Address mxcsr_save(rsp, 0); + + if (CheckJNICalls && UseSSE > 0 ) { + Label ok_ret; + ExternalAddress mxcsr_std(StubRoutines::addr_mxcsr_std()); + __ pushl(rax); + __ subl(rsp, wordSize); // allocate a temp location + __ stmxcsr(mxcsr_save); + __ movl(rax, mxcsr_save); + __ andl(rax, MXCSR_MASK); + __ cmp32(rax, mxcsr_std); + __ jcc(Assembler::equal, ok_ret); + + __ warn("MXCSR changed by native JNI code."); + + __ ldmxcsr(mxcsr_std); + + __ bind(ok_ret); + __ addl(rsp, wordSize); + __ popl(rax); + } + + __ ret(0); + + return start; + } + + + //--------------------------------------------------------------------------- + // Support for void verify_fpu_cntrl_wrd() + // + // This routine is used with -Xcheck:jni to verify that native + // JNI code does not return to Java code without restoring the + // FP control word to our expected state. + + address generate_verify_fpu_cntrl_wrd() { + StubCodeMark mark(this, "StubRoutines", "verify_spcw"); + address start = __ pc(); + + const Address fpu_cntrl_wrd_save(rsp, 0); + + if (CheckJNICalls) { + Label ok_ret; + __ pushl(rax); + __ subl(rsp, wordSize); // allocate a temp location + __ fnstcw(fpu_cntrl_wrd_save); + __ movl(rax, fpu_cntrl_wrd_save); + __ andl(rax, FPU_CNTRL_WRD_MASK); + ExternalAddress fpu_std(StubRoutines::addr_fpu_cntrl_wrd_std()); + __ cmp32(rax, fpu_std); + __ jcc(Assembler::equal, ok_ret); + + __ warn("Floating point control word changed by native JNI code."); + + __ fldcw(fpu_std); + + __ bind(ok_ret); + __ addl(rsp, wordSize); + __ popl(rax); + } + + __ ret(0); + + return start; + } + + //--------------------------------------------------------------------------- + // Wrapper for slow-case handling of double-to-integer conversion + // d2i or f2i fast case failed either because it is nan or because + // of under/overflow. + // Input: FPU TOS: float value + // Output: rax, (rdx): integer (long) result + + address generate_d2i_wrapper(BasicType t, address fcn) { + StubCodeMark mark(this, "StubRoutines", "d2i_wrapper"); + address start = __ pc(); + + // Capture info about frame layout + enum layout { FPUState_off = 0, + rbp_off = FPUStateSizeInWords, + rdi_off, + rsi_off, + rcx_off, + rbx_off, + saved_argument_off, + saved_argument_off2, // 2nd half of double + framesize + }; + + assert(FPUStateSizeInWords == 27, "update stack layout"); + + // Save outgoing argument to stack across push_FPU_state() + __ subl(rsp, wordSize * 2); + __ fstp_d(Address(rsp, 0)); + + // Save CPU & FPU state + __ pushl(rbx); + __ pushl(rcx); + __ pushl(rsi); + __ pushl(rdi); + __ pushl(rbp); + __ push_FPU_state(); + + // push_FPU_state() resets the FP top of stack + // Load original double into FP top of stack + __ fld_d(Address(rsp, saved_argument_off * wordSize)); + // Store double into stack as outgoing argument + __ subl(rsp, wordSize*2); + __ fst_d(Address(rsp, 0)); + + // Prepare FPU for doing math in C-land + __ empty_FPU_stack(); + // Call the C code to massage the double. Result in EAX + if (t == T_INT) + { BLOCK_COMMENT("SharedRuntime::d2i"); } + else if (t == T_LONG) + { BLOCK_COMMENT("SharedRuntime::d2l"); } + __ call_VM_leaf( fcn, 2 ); + + // Restore CPU & FPU state + __ pop_FPU_state(); + __ popl(rbp); + __ popl(rdi); + __ popl(rsi); + __ popl(rcx); + __ popl(rbx); + __ addl(rsp, wordSize * 2); + + __ ret(0); + + return start; + } + + + //--------------------------------------------------------------------------- + // The following routine generates a subroutine to throw an asynchronous + // UnknownError when an unsafe access gets a fault that could not be + // reasonably prevented by the programmer. (Example: SIGBUS/OBJERR.) + address generate_handler_for_unsafe_access() { + StubCodeMark mark(this, "StubRoutines", "handler_for_unsafe_access"); + address start = __ pc(); + + __ pushl(0); // hole for return address-to-be + __ pushad(); // push registers + Address next_pc(rsp, RegisterImpl::number_of_registers * BytesPerWord); + BLOCK_COMMENT("call handle_unsafe_access"); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, handle_unsafe_access))); + __ movl(next_pc, rax); // stuff next address + __ popad(); + __ ret(0); // jump to next address + + return start; + } + + + //---------------------------------------------------------------------------------------------------- + // Non-destructive plausibility checks for oops + + address generate_verify_oop() { + StubCodeMark mark(this, "StubRoutines", "verify_oop"); + address start = __ pc(); + + // Incoming arguments on stack after saving rax,: + // + // [tos ]: saved rdx + // [tos + 1]: saved EFLAGS + // [tos + 2]: return address + // [tos + 3]: char* error message + // [tos + 4]: oop object to verify + // [tos + 5]: saved rax, - saved by caller and bashed + + Label exit, error; + __ pushfd(); + __ increment(ExternalAddress((address) StubRoutines::verify_oop_count_addr())); + __ pushl(rdx); // save rdx + // make sure object is 'reasonable' + __ movl(rax, Address(rsp, 4 * wordSize)); // get object + __ testl(rax, rax); + __ jcc(Assembler::zero, exit); // if obj is NULL it is ok + + // Check if the oop is in the right area of memory + const int oop_mask = Universe::verify_oop_mask(); + const int oop_bits = Universe::verify_oop_bits(); + __ movl(rdx, rax); + __ andl(rdx, oop_mask); + __ cmpl(rdx, oop_bits); + __ jcc(Assembler::notZero, error); + + // make sure klass is 'reasonable' + __ movl(rax, Address(rax, oopDesc::klass_offset_in_bytes())); // get klass + __ testl(rax, rax); + __ jcc(Assembler::zero, error); // if klass is NULL it is broken + + // Check if the klass is in the right area of memory + const int klass_mask = Universe::verify_klass_mask(); + const int klass_bits = Universe::verify_klass_bits(); + __ movl(rdx, rax); + __ andl(rdx, klass_mask); + __ cmpl(rdx, klass_bits); + __ jcc(Assembler::notZero, error); + + // make sure klass' klass is 'reasonable' + __ movl(rax, Address(rax, oopDesc::klass_offset_in_bytes())); // get klass' klass + __ testl(rax, rax); + __ jcc(Assembler::zero, error); // if klass' klass is NULL it is broken + + __ movl(rdx, rax); + __ andl(rdx, klass_mask); + __ cmpl(rdx, klass_bits); + __ jcc(Assembler::notZero, error); // if klass not in right area + // of memory it is broken too. + + // return if everything seems ok + __ bind(exit); + __ movl(rax, Address(rsp, 5 * wordSize)); // get saved rax, back + __ popl(rdx); // restore rdx + __ popfd(); // restore EFLAGS + __ ret(3 * wordSize); // pop arguments + + // handle errors + __ bind(error); + __ movl(rax, Address(rsp, 5 * wordSize)); // get saved rax, back + __ popl(rdx); // get saved rdx back + __ popfd(); // get saved EFLAGS off stack -- will be ignored + __ pushad(); // push registers (eip = return address & msg are already pushed) + BLOCK_COMMENT("call MacroAssembler::debug"); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::debug))); + __ popad(); + __ ret(3 * wordSize); // pop arguments + return start; + } + + // + // Generate pre-barrier for array stores + // + // Input: + // start - starting address + // end - element count + void gen_write_ref_array_pre_barrier(Register start, Register count) { + assert_different_registers(start, count); +#if 0 // G1 only + BarrierSet* bs = Universe::heap()->barrier_set(); + switch (bs->kind()) { + case BarrierSet::G1SATBCT: + case BarrierSet::G1SATBCTLogging: + { + __ pushad(); // push registers + __ pushl(count); + __ pushl(start); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_pre)); + __ addl(esp, wordSize * 2); + __ popad(); + } + break; + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + case BarrierSet::ModRef: + break; + default : + ShouldNotReachHere(); + + } +#endif // 0 - G1 only + } + + + // + // Generate a post-barrier for an array store + // + // start - starting address + // count - element count + // + // The two input registers are overwritten. + // + void gen_write_ref_array_post_barrier(Register start, Register count) { + BarrierSet* bs = Universe::heap()->barrier_set(); + assert_different_registers(start, count); + switch (bs->kind()) { +#if 0 // G1 only + case BarrierSet::G1SATBCT: + case BarrierSet::G1SATBCTLogging: + { + __ pushad(); // push registers + __ pushl(count); + __ pushl(start); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post)); + __ addl(esp, wordSize * 2); + __ popad(); + + } + break; +#endif // 0 G1 only + + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + { + CardTableModRefBS* ct = (CardTableModRefBS*)bs; + assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); + + Label L_loop; + const Register end = count; // elements count; end == start+count-1 + assert_different_registers(start, end); + + __ leal(end, Address(start, count, Address::times_4, -4)); + __ shrl(start, CardTableModRefBS::card_shift); + __ shrl(end, CardTableModRefBS::card_shift); + __ subl(end, start); // end --> count + __ BIND(L_loop); + ExternalAddress base((address)ct->byte_map_base); + Address index(start, count, Address::times_1, 0); + __ movbyte(ArrayAddress(base, index), 0); + __ decrement(count); + __ jcc(Assembler::greaterEqual, L_loop); + } + break; + case BarrierSet::ModRef: + break; + default : + ShouldNotReachHere(); + + } + } + + // Copy 64 bytes chunks + // + // Inputs: + // from - source array address + // to_from - destination array address - from + // qword_count - 8-bytes element count, negative + // + void mmx_copy_forward(Register from, Register to_from, Register qword_count) { + Label L_copy_64_bytes_loop, L_copy_64_bytes, L_copy_8_bytes, L_exit; + // Copy 64-byte chunks + __ jmpb(L_copy_64_bytes); + __ align(16); + __ BIND(L_copy_64_bytes_loop); + __ movq(mmx0, Address(from, 0)); + __ movq(mmx1, Address(from, 8)); + __ movq(mmx2, Address(from, 16)); + __ movq(Address(from, to_from, Address::times_1, 0), mmx0); + __ movq(mmx3, Address(from, 24)); + __ movq(Address(from, to_from, Address::times_1, 8), mmx1); + __ movq(mmx4, Address(from, 32)); + __ movq(Address(from, to_from, Address::times_1, 16), mmx2); + __ movq(mmx5, Address(from, 40)); + __ movq(Address(from, to_from, Address::times_1, 24), mmx3); + __ movq(mmx6, Address(from, 48)); + __ movq(Address(from, to_from, Address::times_1, 32), mmx4); + __ movq(mmx7, Address(from, 56)); + __ movq(Address(from, to_from, Address::times_1, 40), mmx5); + __ movq(Address(from, to_from, Address::times_1, 48), mmx6); + __ movq(Address(from, to_from, Address::times_1, 56), mmx7); + __ addl(from, 64); + __ BIND(L_copy_64_bytes); + __ subl(qword_count, 8); + __ jcc(Assembler::greaterEqual, L_copy_64_bytes_loop); + __ addl(qword_count, 8); + __ jccb(Assembler::zero, L_exit); + // + // length is too short, just copy qwords + // + __ BIND(L_copy_8_bytes); + __ movq(mmx0, Address(from, 0)); + __ movq(Address(from, to_from, Address::times_1), mmx0); + __ addl(from, 8); + __ decrement(qword_count); + __ jcc(Assembler::greater, L_copy_8_bytes); + __ BIND(L_exit); + __ emms(); + } + + address generate_disjoint_copy(BasicType t, bool aligned, + Address::ScaleFactor sf, + address* entry, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_0_count, L_exit, L_skip_align1, L_skip_align2, L_copy_byte; + Label L_copy_2_bytes, L_copy_4_bytes, L_copy_64_bytes; + + int shift = Address::times_4 - sf; + + const Register from = rsi; // source array address + const Register to = rdi; // destination array address + const Register count = rcx; // elements count + const Register to_from = to; // (to - from) + const Register saved_to = rdx; // saved destination array address + + __ enter(); // required for proper stackwalking of RuntimeStub frame + __ pushl(rsi); + __ pushl(rdi); + __ movl(from , Address(rsp, 12+ 4)); + __ movl(to , Address(rsp, 12+ 8)); + __ movl(count, Address(rsp, 12+ 12)); + if (t == T_OBJECT) { + __ testl(count, count); + __ jcc(Assembler::zero, L_0_count); + gen_write_ref_array_pre_barrier(to, count); + __ movl(saved_to, to); // save 'to' + } + + *entry = __ pc(); // Entry point from conjoint arraycopy stub. + BLOCK_COMMENT("Entry:"); + + __ subl(to, from); // to --> to_from + __ cmpl(count, 2< to_from + if (VM_Version::supports_mmx()) { + mmx_copy_forward(from, to_from, count); + } else { + __ jmpb(L_copy_8_bytes); + __ align(16); + __ BIND(L_copy_8_bytes_loop); + __ fild_d(Address(from, 0)); + __ fistp_d(Address(from, to_from, Address::times_1)); + __ addl(from, 8); + __ BIND(L_copy_8_bytes); + __ decrement(count); + __ jcc(Assembler::greaterEqual, L_copy_8_bytes_loop); + } + inc_copy_counter_np(T_LONG); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ xorl(rax, rax); // return 0 + __ ret(0); + return start; + } + + address generate_conjoint_long_copy(address nooverlap_target, + address* entry, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_copy_8_bytes, L_copy_8_bytes_loop; + const Register from = rax; // source array address + const Register to = rdx; // destination array address + const Register count = rcx; // elements count + const Register end_from = rax; // source array end address + + __ enter(); // required for proper stackwalking of RuntimeStub frame + __ movl(from , Address(rsp, 8+0)); // from + __ movl(to , Address(rsp, 8+4)); // to + __ movl(count, Address(rsp, 8+8)); // count + + *entry = __ pc(); // Entry point from generic arraycopy stub. + BLOCK_COMMENT("Entry:"); + + // arrays overlap test + __ cmpl(to, from); + RuntimeAddress nooverlap(nooverlap_target); + __ jump_cc(Assembler::belowEqual, nooverlap); + __ leal(end_from, Address(from, count, Address::times_8, 0)); + __ cmpl(to, end_from); + __ movl(from, Address(rsp, 8)); // from + __ jump_cc(Assembler::aboveEqual, nooverlap); + + __ jmpb(L_copy_8_bytes); + + __ align(16); + __ BIND(L_copy_8_bytes_loop); + if (VM_Version::supports_mmx()) { + __ movq(mmx0, Address(from, count, Address::times_8)); + __ movq(Address(to, count, Address::times_8), mmx0); + } else { + __ fild_d(Address(from, count, Address::times_8)); + __ fistp_d(Address(to, count, Address::times_8)); + } + __ BIND(L_copy_8_bytes); + __ decrement(count); + __ jcc(Assembler::greaterEqual, L_copy_8_bytes_loop); + + if (VM_Version::supports_mmx()) { + __ emms(); + } + inc_copy_counter_np(T_LONG); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ xorl(rax, rax); // return 0 + __ ret(0); + return start; + } + + + // Helper for generating a dynamic type check. + // The sub_klass must be one of {rbx, rdx, rsi}. + // The temp is killed. + void generate_type_check(Register sub_klass, + Address& super_check_offset_addr, + Address& super_klass_addr, + Register temp, + Label* L_success_ptr, Label* L_failure_ptr) { + BLOCK_COMMENT("type_check:"); + + Label L_fallthrough; + bool fall_through_on_success = (L_success_ptr == NULL); + if (fall_through_on_success) { + L_success_ptr = &L_fallthrough; + } else { + L_failure_ptr = &L_fallthrough; + } + Label& L_success = *L_success_ptr; + Label& L_failure = *L_failure_ptr; + + assert_different_registers(sub_klass, temp); + + // a couple of useful fields in sub_klass: + int ss_offset = (klassOopDesc::header_size() * HeapWordSize + + Klass::secondary_supers_offset_in_bytes()); + int sc_offset = (klassOopDesc::header_size() * HeapWordSize + + Klass::secondary_super_cache_offset_in_bytes()); + Address secondary_supers_addr(sub_klass, ss_offset); + Address super_cache_addr( sub_klass, sc_offset); + + // if the pointers are equal, we are done (e.g., String[] elements) + __ cmpl(sub_klass, super_klass_addr); + __ jcc(Assembler::equal, L_success); + + // check the supertype display: + __ movl(temp, super_check_offset_addr); + Address super_check_addr(sub_klass, temp, Address::times_1, 0); + __ movl(temp, super_check_addr); // load displayed supertype + __ cmpl(temp, super_klass_addr); // test the super type + __ jcc(Assembler::equal, L_success); + + // if it was a primary super, we can just fail immediately + __ cmpl(super_check_offset_addr, sc_offset); + __ jcc(Assembler::notEqual, L_failure); + + // Now do a linear scan of the secondary super-klass chain. + // This code is rarely used, so simplicity is a virtue here. + inc_counter_np(SharedRuntime::_partial_subtype_ctr); + { + // The repne_scan instruction uses fixed registers, which we must spill. + // (We need a couple more temps in any case.) + __ pushl(rax); + __ pushl(rcx); + __ pushl(rdi); + assert_different_registers(sub_klass, rax, rcx, rdi); + + __ movl(rdi, secondary_supers_addr); + // Load the array length. + __ movl(rcx, Address(rdi, arrayOopDesc::length_offset_in_bytes())); + // Skip to start of data. + __ addl(rdi, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); + // Scan rcx words at [edi] for occurance of rax, + // Set NZ/Z based on last compare + __ movl(rax, super_klass_addr); + __ repne_scan(); + + // Unspill the temp. registers: + __ popl(rdi); + __ popl(rcx); + __ popl(rax); + } + __ jcc(Assembler::notEqual, L_failure); + + // Success. Cache the super we found and proceed in triumph. + __ movl(temp, super_klass_addr); // note: rax, is dead + __ movl(super_cache_addr, temp); + + if (!fall_through_on_success) + __ jmp(L_success); + + // Fall through on failure! + __ bind(L_fallthrough); + } + + // + // Generate checkcasting array copy stub + // + // Input: + // 4(rsp) - source array address + // 8(rsp) - destination array address + // 12(rsp) - element count, can be zero + // 16(rsp) - size_t ckoff (super_check_offset) + // 20(rsp) - oop ckval (super_klass) + // + // Output: + // rax, == 0 - success + // rax, == -1^K - failure, where K is partial transfer count + // + address generate_checkcast_copy(const char *name, address* entry) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_load_element, L_store_element, L_do_card_marks, L_done; + + // register use: + // rax, rdx, rcx -- loop control (end_from, end_to, count) + // rdi, rsi -- element access (oop, klass) + // rbx, -- temp + const Register from = rax; // source array address + const Register to = rdx; // destination array address + const Register length = rcx; // elements count + const Register elem = rdi; // each oop copied + const Register elem_klass = rsi; // each elem._klass (sub_klass) + const Register temp = rbx; // lone remaining temp + + __ enter(); // required for proper stackwalking of RuntimeStub frame + + __ pushl(rsi); + __ pushl(rdi); + __ pushl(rbx); + + Address from_arg(rsp, 16+ 4); // from + Address to_arg(rsp, 16+ 8); // to + Address length_arg(rsp, 16+12); // elements count + Address ckoff_arg(rsp, 16+16); // super_check_offset + Address ckval_arg(rsp, 16+20); // super_klass + + // Load up: + __ movl(from, from_arg); + __ movl(to, to_arg); + __ movl(length, length_arg); + + *entry = __ pc(); // Entry point from generic arraycopy stub. + BLOCK_COMMENT("Entry:"); + + //--------------------------------------------------------------- + // Assembler stub will be used for this call to arraycopy + // if the two arrays are subtypes of Object[] but the + // destination array type is not equal to or a supertype + // of the source type. Each element must be separately + // checked. + + // Loop-invariant addresses. They are exclusive end pointers. + Address end_from_addr(from, length, Address::times_4, 0); + Address end_to_addr(to, length, Address::times_4, 0); + + Register end_from = from; // re-use + Register end_to = to; // re-use + Register count = length; // re-use + + // Loop-variant addresses. They assume post-incremented count < 0. + Address from_element_addr(end_from, count, Address::times_4, 0); + Address to_element_addr(end_to, count, Address::times_4, 0); + Address elem_klass_addr(elem, oopDesc::klass_offset_in_bytes()); + + // Copy from low to high addresses, indexed from the end of each array. + __ leal(end_from, end_from_addr); + __ leal(end_to, end_to_addr); + gen_write_ref_array_pre_barrier(to, count); + assert(length == count, ""); // else fix next line: + __ negl(count); // negate and test the length + __ jccb(Assembler::notZero, L_load_element); + + // Empty array: Nothing to do. + __ xorl(rax, rax); // return 0 on (trivial) success + __ jmp(L_done); + + // ======== begin loop ======== + // (Loop is rotated; its entry is L_load_element.) + // Loop control: + // for (count = -count; count != 0; count++) + // Base pointers src, dst are biased by 8*count,to last element. + __ align(16); + + __ BIND(L_store_element); + __ movl(to_element_addr, elem); // store the oop + __ increment(count); // increment the count toward zero + __ jccb(Assembler::zero, L_do_card_marks); + + // ======== loop entry is here ======== + __ BIND(L_load_element); + __ movl(elem, from_element_addr); // load the oop + __ testl(elem, elem); + __ jccb(Assembler::zero, L_store_element); + + // (Could do a trick here: Remember last successful non-null + // element stored and make a quick oop equality check on it.) + + __ movl(elem_klass, elem_klass_addr); // query the object klass + generate_type_check(elem_klass, ckoff_arg, ckval_arg, temp, + &L_store_element, NULL); + // (On fall-through, we have failed the element type check.) + // ======== end loop ======== + + // It was a real error; we must depend on the caller to finish the job. + // Register rdx = -1 * number of *remaining* oops, r14 = *total* oops. + // Emit GC store barriers for the oops we have copied (r14 + rdx), + // and report their number to the caller. + __ addl(count, length_arg); // transfers = (length - remaining) + __ movl(rax, count); // save the value + __ notl(rax); // report (-1^K) to caller + __ movl(to, to_arg); // reload + assert_different_registers(to, count, rax); + gen_write_ref_array_post_barrier(to, count); + __ jmpb(L_done); + + // Come here on success only. + __ BIND(L_do_card_marks); + __ movl(count, length_arg); + gen_write_ref_array_post_barrier(to, count); + __ xorl(rax, rax); // return 0 on success + + // Common exit point (success or failure). + __ BIND(L_done); + __ popl(rbx); + __ popl(rdi); + __ popl(rsi); + inc_counter_np(SharedRuntime::_checkcast_array_copy_ctr); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + // + // Generate 'unsafe' array copy stub + // Though just as safe as the other stubs, it takes an unscaled + // size_t argument instead of an element count. + // + // Input: + // 4(rsp) - source array address + // 8(rsp) - destination array address + // 12(rsp) - byte count, can be zero + // + // Output: + // rax, == 0 - success + // rax, == -1 - need to call System.arraycopy + // + // Examines the alignment of the operands and dispatches + // to a long, int, short, or byte copy loop. + // + address generate_unsafe_copy(const char *name, + address byte_copy_entry, + address short_copy_entry, + address int_copy_entry, + address long_copy_entry) { + + Label L_long_aligned, L_int_aligned, L_short_aligned; + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + const Register from = rax; // source array address + const Register to = rdx; // destination array address + const Register count = rcx; // elements count + + __ enter(); // required for proper stackwalking of RuntimeStub frame + __ pushl(rsi); + __ pushl(rdi); + Address from_arg(rsp, 12+ 4); // from + Address to_arg(rsp, 12+ 8); // to + Address count_arg(rsp, 12+12); // byte count + + // Load up: + __ movl(from , from_arg); + __ movl(to , to_arg); + __ movl(count, count_arg); + + // bump this on entry, not on exit: + inc_counter_np(SharedRuntime::_unsafe_array_copy_ctr); + + const Register bits = rsi; + __ movl(bits, from); + __ orl(bits, to); + __ orl(bits, count); + + __ testl(bits, BytesPerLong-1); + __ jccb(Assembler::zero, L_long_aligned); + + __ testl(bits, BytesPerInt-1); + __ jccb(Assembler::zero, L_int_aligned); + + __ testl(bits, BytesPerShort-1); + __ jump_cc(Assembler::notZero, RuntimeAddress(byte_copy_entry)); + + __ BIND(L_short_aligned); + __ shrl(count, LogBytesPerShort); // size => short_count + __ movl(count_arg, count); // update 'count' + __ jump(RuntimeAddress(short_copy_entry)); + + __ BIND(L_int_aligned); + __ shrl(count, LogBytesPerInt); // size => int_count + __ movl(count_arg, count); // update 'count' + __ jump(RuntimeAddress(int_copy_entry)); + + __ BIND(L_long_aligned); + __ shrl(count, LogBytesPerLong); // size => qword_count + __ movl(count_arg, count); // update 'count' + __ popl(rdi); // Do pops here since jlong_arraycopy stub does not do it. + __ popl(rsi); + __ jump(RuntimeAddress(long_copy_entry)); + + return start; + } + + + // Perform range checks on the proposed arraycopy. + // Smashes src_pos and dst_pos. (Uses them up for temps.) + void arraycopy_range_checks(Register src, + Register src_pos, + Register dst, + Register dst_pos, + Address& length, + Label& L_failed) { + BLOCK_COMMENT("arraycopy_range_checks:"); + const Register src_end = src_pos; // source array end position + const Register dst_end = dst_pos; // destination array end position + __ addl(src_end, length); // src_pos + length + __ addl(dst_end, length); // dst_pos + length + + // if (src_pos + length > arrayOop(src)->length() ) FAIL; + __ cmpl(src_end, Address(src, arrayOopDesc::length_offset_in_bytes())); + __ jcc(Assembler::above, L_failed); + + // if (dst_pos + length > arrayOop(dst)->length() ) FAIL; + __ cmpl(dst_end, Address(dst, arrayOopDesc::length_offset_in_bytes())); + __ jcc(Assembler::above, L_failed); + + BLOCK_COMMENT("arraycopy_range_checks done"); + } + + + // + // Generate generic array copy stubs + // + // Input: + // 4(rsp) - src oop + // 8(rsp) - src_pos + // 12(rsp) - dst oop + // 16(rsp) - dst_pos + // 20(rsp) - element count + // + // Output: + // rax, == 0 - success + // rax, == -1^K - failure, where K is partial transfer count + // + address generate_generic_copy(const char *name, + address entry_jbyte_arraycopy, + address entry_jshort_arraycopy, + address entry_jint_arraycopy, + address entry_oop_arraycopy, + address entry_jlong_arraycopy, + address entry_checkcast_arraycopy) { + Label L_failed, L_failed_0, L_objArray; + + { int modulus = CodeEntryAlignment; + int target = modulus - 5; // 5 = sizeof jmp(L_failed) + int advance = target - (__ offset() % modulus); + if (advance < 0) advance += modulus; + if (advance > 0) __ nop(advance); + } + StubCodeMark mark(this, "StubRoutines", name); + + // Short-hop target to L_failed. Makes for denser prologue code. + __ BIND(L_failed_0); + __ jmp(L_failed); + assert(__ offset() % CodeEntryAlignment == 0, "no further alignment needed"); + + __ align(CodeEntryAlignment); + address start = __ pc(); + + __ enter(); // required for proper stackwalking of RuntimeStub frame + __ pushl(rsi); + __ pushl(rdi); + + // bump this on entry, not on exit: + inc_counter_np(SharedRuntime::_generic_array_copy_ctr); + + // Input values + Address SRC (rsp, 12+ 4); + Address SRC_POS (rsp, 12+ 8); + Address DST (rsp, 12+12); + Address DST_POS (rsp, 12+16); + Address LENGTH (rsp, 12+20); + + //----------------------------------------------------------------------- + // Assembler stub will be used for this call to arraycopy + // if the following conditions are met: + // + // (1) src and dst must not be null. + // (2) src_pos must not be negative. + // (3) dst_pos must not be negative. + // (4) length must not be negative. + // (5) src klass and dst klass should be the same and not NULL. + // (6) src and dst should be arrays. + // (7) src_pos + length must not exceed length of src. + // (8) dst_pos + length must not exceed length of dst. + // + + const Register src = rax; // source array oop + const Register src_pos = rsi; + const Register dst = rdx; // destination array oop + const Register dst_pos = rdi; + const Register length = rcx; // transfer count + + // if (src == NULL) return -1; + __ movl(src, SRC); // src oop + __ testl(src, src); + __ jccb(Assembler::zero, L_failed_0); + + // if (src_pos < 0) return -1; + __ movl(src_pos, SRC_POS); // src_pos + __ testl(src_pos, src_pos); + __ jccb(Assembler::negative, L_failed_0); + + // if (dst == NULL) return -1; + __ movl(dst, DST); // dst oop + __ testl(dst, dst); + __ jccb(Assembler::zero, L_failed_0); + + // if (dst_pos < 0) return -1; + __ movl(dst_pos, DST_POS); // dst_pos + __ testl(dst_pos, dst_pos); + __ jccb(Assembler::negative, L_failed_0); + + // if (length < 0) return -1; + __ movl(length, LENGTH); // length + __ testl(length, length); + __ jccb(Assembler::negative, L_failed_0); + + // if (src->klass() == NULL) return -1; + Address src_klass_addr(src, oopDesc::klass_offset_in_bytes()); + Address dst_klass_addr(dst, oopDesc::klass_offset_in_bytes()); + const Register rcx_src_klass = rcx; // array klass + __ movl(rcx_src_klass, Address(src, oopDesc::klass_offset_in_bytes())); + +#ifdef ASSERT + // assert(src->klass() != NULL); + BLOCK_COMMENT("assert klasses not null"); + { Label L1, L2; + __ testl(rcx_src_klass, rcx_src_klass); + __ jccb(Assembler::notZero, L2); // it is broken if klass is NULL + __ bind(L1); + __ stop("broken null klass"); + __ bind(L2); + __ cmpl(dst_klass_addr, 0); + __ jccb(Assembler::equal, L1); // this would be broken also + BLOCK_COMMENT("assert done"); + } +#endif //ASSERT + + // Load layout helper (32-bits) + // + // |array_tag| | header_size | element_type | |log2_element_size| + // 32 30 24 16 8 2 0 + // + // array_tag: typeArray = 0x3, objArray = 0x2, non-array = 0x0 + // + + int lh_offset = klassOopDesc::header_size() * HeapWordSize + + Klass::layout_helper_offset_in_bytes(); + Address src_klass_lh_addr(rcx_src_klass, lh_offset); + + // Handle objArrays completely differently... + jint objArray_lh = Klass::array_layout_helper(T_OBJECT); + __ cmpl(src_klass_lh_addr, objArray_lh); + __ jcc(Assembler::equal, L_objArray); + + // if (src->klass() != dst->klass()) return -1; + __ cmpl(rcx_src_klass, dst_klass_addr); + __ jccb(Assembler::notEqual, L_failed_0); + + const Register rcx_lh = rcx; // layout helper + assert(rcx_lh == rcx_src_klass, "known alias"); + __ movl(rcx_lh, src_klass_lh_addr); + + // if (!src->is_Array()) return -1; + __ cmpl(rcx_lh, Klass::_lh_neutral_value); + __ jcc(Assembler::greaterEqual, L_failed_0); // signed cmp + + // At this point, it is known to be a typeArray (array_tag 0x3). +#ifdef ASSERT + { Label L; + __ cmpl(rcx_lh, (Klass::_lh_array_tag_type_value << Klass::_lh_array_tag_shift)); + __ jcc(Assembler::greaterEqual, L); // signed cmp + __ stop("must be a primitive array"); + __ bind(L); + } +#endif + + assert_different_registers(src, src_pos, dst, dst_pos, rcx_lh); + arraycopy_range_checks(src, src_pos, dst, dst_pos, LENGTH, L_failed); + + // typeArrayKlass + // + // src_addr = (src + array_header_in_bytes()) + (src_pos << log2elemsize); + // dst_addr = (dst + array_header_in_bytes()) + (dst_pos << log2elemsize); + // + const Register rsi_offset = rsi; // array offset + const Register src_array = src; // src array offset + const Register dst_array = dst; // dst array offset + const Register rdi_elsize = rdi; // log2 element size + + __ movl(rsi_offset, rcx_lh); + __ shrl(rsi_offset, Klass::_lh_header_size_shift); + __ andl(rsi_offset, Klass::_lh_header_size_mask); // array_offset + __ addl(src_array, rsi_offset); // src array offset + __ addl(dst_array, rsi_offset); // dst array offset + __ andl(rcx_lh, Klass::_lh_log2_element_size_mask); // log2 elsize + + // next registers should be set before the jump to corresponding stub + const Register from = src; // source array address + const Register to = dst; // destination array address + const Register count = rcx; // elements count + // some of them should be duplicated on stack +#define FROM Address(rsp, 12+ 4) +#define TO Address(rsp, 12+ 8) // Not used now +#define COUNT Address(rsp, 12+12) // Only for oop arraycopy + + BLOCK_COMMENT("scale indexes to element size"); + __ movl(rsi, SRC_POS); // src_pos + __ shll(rsi); // src_pos << rcx (log2 elsize) + assert(src_array == from, ""); + __ addl(from, rsi); // from = src_array + SRC_POS << log2 elsize + __ movl(rdi, DST_POS); // dst_pos + __ shll(rdi); // dst_pos << rcx (log2 elsize) + assert(dst_array == to, ""); + __ addl(to, rdi); // to = dst_array + DST_POS << log2 elsize + __ movl(FROM, from); // src_addr + __ movl(rdi_elsize, rcx_lh); // log2 elsize + __ movl(count, LENGTH); // elements count + + BLOCK_COMMENT("choose copy loop based on element size"); + __ cmpl(rdi_elsize, 0); + + __ jump_cc(Assembler::equal, RuntimeAddress(entry_jbyte_arraycopy)); + __ cmpl(rdi_elsize, LogBytesPerShort); + __ jump_cc(Assembler::equal, RuntimeAddress(entry_jshort_arraycopy)); + __ cmpl(rdi_elsize, LogBytesPerInt); + __ jump_cc(Assembler::equal, RuntimeAddress(entry_jint_arraycopy)); +#ifdef ASSERT + __ cmpl(rdi_elsize, LogBytesPerLong); + __ jccb(Assembler::notEqual, L_failed); +#endif + __ popl(rdi); // Do pops here since jlong_arraycopy stub does not do it. + __ popl(rsi); + __ jump(RuntimeAddress(entry_jlong_arraycopy)); + + __ BIND(L_failed); + __ xorl(rax, rax); + __ notl(rax); // return -1 + __ popl(rdi); + __ popl(rsi); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + // objArrayKlass + __ BIND(L_objArray); + // live at this point: rcx_src_klass, src[_pos], dst[_pos] + + Label L_plain_copy, L_checkcast_copy; + // test array classes for subtyping + __ cmpl(rcx_src_klass, dst_klass_addr); // usual case is exact equality + __ jccb(Assembler::notEqual, L_checkcast_copy); + + // Identically typed arrays can be copied without element-wise checks. + assert_different_registers(src, src_pos, dst, dst_pos, rcx_src_klass); + arraycopy_range_checks(src, src_pos, dst, dst_pos, LENGTH, L_failed); + + __ BIND(L_plain_copy); + __ movl(count, LENGTH); // elements count + __ movl(src_pos, SRC_POS); // reload src_pos + __ leal(from, Address(src, src_pos, Address::times_4, + arrayOopDesc::base_offset_in_bytes(T_OBJECT))); // src_addr + __ movl(dst_pos, DST_POS); // reload dst_pos + __ leal(to, Address(dst, dst_pos, Address::times_4, + arrayOopDesc::base_offset_in_bytes(T_OBJECT))); // dst_addr + __ movl(FROM, from); // src_addr + __ movl(TO, to); // dst_addr + __ movl(COUNT, count); // count + __ jump(RuntimeAddress(entry_oop_arraycopy)); + + __ BIND(L_checkcast_copy); + // live at this point: rcx_src_klass, dst[_pos], src[_pos] + { + // Handy offsets: + int ek_offset = (klassOopDesc::header_size() * HeapWordSize + + objArrayKlass::element_klass_offset_in_bytes()); + int sco_offset = (klassOopDesc::header_size() * HeapWordSize + + Klass::super_check_offset_offset_in_bytes()); + + Register rsi_dst_klass = rsi; + Register rdi_temp = rdi; + assert(rsi_dst_klass == src_pos, "expected alias w/ src_pos"); + assert(rdi_temp == dst_pos, "expected alias w/ dst_pos"); + Address dst_klass_lh_addr(rsi_dst_klass, lh_offset); + + // Before looking at dst.length, make sure dst is also an objArray. + __ movl(rsi_dst_klass, dst_klass_addr); + __ cmpl(dst_klass_lh_addr, objArray_lh); + __ jccb(Assembler::notEqual, L_failed); + + // It is safe to examine both src.length and dst.length. + __ movl(src_pos, SRC_POS); // reload rsi + arraycopy_range_checks(src, src_pos, dst, dst_pos, LENGTH, L_failed); + // (Now src_pos and dst_pos are killed, but not src and dst.) + + // We'll need this temp (don't forget to pop it after the type check). + __ pushl(rbx); + Register rbx_src_klass = rbx; + + __ movl(rbx_src_klass, rcx_src_klass); // spill away from rcx + __ movl(rsi_dst_klass, dst_klass_addr); + Address super_check_offset_addr(rsi_dst_klass, sco_offset); + Label L_fail_array_check; + generate_type_check(rbx_src_klass, + super_check_offset_addr, dst_klass_addr, + rdi_temp, NULL, &L_fail_array_check); + // (On fall-through, we have passed the array type check.) + __ popl(rbx); + __ jmp(L_plain_copy); + + __ BIND(L_fail_array_check); + // Reshuffle arguments so we can call checkcast_arraycopy: + + // match initial saves for checkcast_arraycopy + // pushl(rsi); // already done; see above + // pushl(rdi); // already done; see above + // pushl(rbx); // already done; see above + + // Marshal outgoing arguments now, freeing registers. + Address from_arg(rsp, 16+ 4); // from + Address to_arg(rsp, 16+ 8); // to + Address length_arg(rsp, 16+12); // elements count + Address ckoff_arg(rsp, 16+16); // super_check_offset + Address ckval_arg(rsp, 16+20); // super_klass + + Address SRC_POS_arg(rsp, 16+ 8); + Address DST_POS_arg(rsp, 16+16); + Address LENGTH_arg(rsp, 16+20); + // push rbx, changed the incoming offsets (why not just use rbp,??) + // assert(SRC_POS_arg.disp() == SRC_POS.disp() + 4, ""); + + __ movl(rbx, Address(rsi_dst_klass, ek_offset)); + __ movl(length, LENGTH_arg); // reload elements count + __ movl(src_pos, SRC_POS_arg); // reload src_pos + __ movl(dst_pos, DST_POS_arg); // reload dst_pos + + __ movl(ckval_arg, rbx); // destination element type + __ movl(rbx, Address(rbx, sco_offset)); + __ movl(ckoff_arg, rbx); // corresponding class check offset + + __ movl(length_arg, length); // outgoing length argument + + __ leal(from, Address(src, src_pos, Address::times_4, + arrayOopDesc::base_offset_in_bytes(T_OBJECT))); + __ movl(from_arg, from); + + __ leal(to, Address(dst, dst_pos, Address::times_4, + arrayOopDesc::base_offset_in_bytes(T_OBJECT))); + __ movl(to_arg, to); + __ jump(RuntimeAddress(entry_checkcast_arraycopy)); + } + + return start; + } + + void generate_arraycopy_stubs() { + address entry; + address entry_jbyte_arraycopy; + address entry_jshort_arraycopy; + address entry_jint_arraycopy; + address entry_oop_arraycopy; + address entry_jlong_arraycopy; + address entry_checkcast_arraycopy; + + StubRoutines::_arrayof_jbyte_disjoint_arraycopy = + generate_disjoint_copy(T_BYTE, true, Address::times_1, &entry, + "arrayof_jbyte_disjoint_arraycopy"); + StubRoutines::_arrayof_jbyte_arraycopy = + generate_conjoint_copy(T_BYTE, true, Address::times_1, entry, + NULL, "arrayof_jbyte_arraycopy"); + StubRoutines::_jbyte_disjoint_arraycopy = + generate_disjoint_copy(T_BYTE, false, Address::times_1, &entry, + "jbyte_disjoint_arraycopy"); + StubRoutines::_jbyte_arraycopy = + generate_conjoint_copy(T_BYTE, false, Address::times_1, entry, + &entry_jbyte_arraycopy, "jbyte_arraycopy"); + + StubRoutines::_arrayof_jshort_disjoint_arraycopy = + generate_disjoint_copy(T_SHORT, true, Address::times_2, &entry, + "arrayof_jshort_disjoint_arraycopy"); + StubRoutines::_arrayof_jshort_arraycopy = + generate_conjoint_copy(T_SHORT, true, Address::times_2, entry, + NULL, "arrayof_jshort_arraycopy"); + StubRoutines::_jshort_disjoint_arraycopy = + generate_disjoint_copy(T_SHORT, false, Address::times_2, &entry, + "jshort_disjoint_arraycopy"); + StubRoutines::_jshort_arraycopy = + generate_conjoint_copy(T_SHORT, false, Address::times_2, entry, + &entry_jshort_arraycopy, "jshort_arraycopy"); + + // Next arrays are always aligned on 4 bytes at least. + StubRoutines::_jint_disjoint_arraycopy = + generate_disjoint_copy(T_INT, true, Address::times_4, &entry, + "jint_disjoint_arraycopy"); + StubRoutines::_jint_arraycopy = + generate_conjoint_copy(T_INT, true, Address::times_4, entry, + &entry_jint_arraycopy, "jint_arraycopy"); + + StubRoutines::_oop_disjoint_arraycopy = + generate_disjoint_copy(T_OBJECT, true, Address::times_4, &entry, + "oop_disjoint_arraycopy"); + StubRoutines::_oop_arraycopy = + generate_conjoint_copy(T_OBJECT, true, Address::times_4, entry, + &entry_oop_arraycopy, "oop_arraycopy"); + + StubRoutines::_jlong_disjoint_arraycopy = + generate_disjoint_long_copy(&entry, "jlong_disjoint_arraycopy"); + StubRoutines::_jlong_arraycopy = + generate_conjoint_long_copy(entry, &entry_jlong_arraycopy, + "jlong_arraycopy"); + + StubRoutines::_arrayof_jint_disjoint_arraycopy = + StubRoutines::_jint_disjoint_arraycopy; + StubRoutines::_arrayof_oop_disjoint_arraycopy = + StubRoutines::_oop_disjoint_arraycopy; + StubRoutines::_arrayof_jlong_disjoint_arraycopy = + StubRoutines::_jlong_disjoint_arraycopy; + + StubRoutines::_arrayof_jint_arraycopy = StubRoutines::_jint_arraycopy; + StubRoutines::_arrayof_oop_arraycopy = StubRoutines::_oop_arraycopy; + StubRoutines::_arrayof_jlong_arraycopy = StubRoutines::_jlong_arraycopy; + + StubRoutines::_checkcast_arraycopy = + generate_checkcast_copy("checkcast_arraycopy", + &entry_checkcast_arraycopy); + + StubRoutines::_unsafe_arraycopy = + generate_unsafe_copy("unsafe_arraycopy", + entry_jbyte_arraycopy, + entry_jshort_arraycopy, + entry_jint_arraycopy, + entry_jlong_arraycopy); + + StubRoutines::_generic_arraycopy = + generate_generic_copy("generic_arraycopy", + entry_jbyte_arraycopy, + entry_jshort_arraycopy, + entry_jint_arraycopy, + entry_oop_arraycopy, + entry_jlong_arraycopy, + entry_checkcast_arraycopy); + } + + public: + // Information about frame layout at time of blocking runtime call. + // Note that we only have to preserve callee-saved registers since + // the compilers are responsible for supplying a continuation point + // if they expect all registers to be preserved. + enum layout { + thread_off, // last_java_sp + rbp_off, // callee saved register + ret_pc, + framesize + }; + + private: + +#undef __ +#define __ masm-> + + //------------------------------------------------------------------------------------------------------------------------ + // Continuation point for throwing of implicit exceptions that are not handled in + // the current activation. Fabricates an exception oop and initiates normal + // exception dispatching in this frame. + // + // Previously the compiler (c2) allowed for callee save registers on Java calls. + // This is no longer true after adapter frames were removed but could possibly + // be brought back in the future if the interpreter code was reworked and it + // was deemed worthwhile. The comment below was left to describe what must + // happen here if callee saves were resurrected. As it stands now this stub + // could actually be a vanilla BufferBlob and have now oopMap at all. + // Since it doesn't make much difference we've chosen to leave it the + // way it was in the callee save days and keep the comment. + + // If we need to preserve callee-saved values we need a callee-saved oop map and + // therefore have to make these stubs into RuntimeStubs rather than BufferBlobs. + // If the compiler needs all registers to be preserved between the fault + // point and the exception handler then it must assume responsibility for that in + // AbstractCompiler::continuation_for_implicit_null_exception or + // continuation_for_implicit_division_by_zero_exception. All other implicit + // exceptions (e.g., NullPointerException or AbstractMethodError on entry) are + // either at call sites or otherwise assume that stack unwinding will be initiated, + // so caller saved registers were assumed volatile in the compiler. + address generate_throw_exception(const char* name, address runtime_entry, + bool restore_saved_exception_pc) { + + int insts_size = 256; + int locs_size = 32; + + CodeBuffer code(name, insts_size, locs_size); + OopMapSet* oop_maps = new OopMapSet(); + MacroAssembler* masm = new MacroAssembler(&code); + + address start = __ pc(); + + // This is an inlined and slightly modified version of call_VM + // which has the ability to fetch the return PC out of + // thread-local storage and also sets up last_Java_sp slightly + // differently than the real call_VM + Register java_thread = rbx; + __ get_thread(java_thread); + if (restore_saved_exception_pc) { + __ movl(rax, Address(java_thread, in_bytes(JavaThread::saved_exception_pc_offset()))); + __ pushl(rax); + } + + __ enter(); // required for proper stackwalking of RuntimeStub frame + + // pc and rbp, already pushed + __ subl(rsp, (framesize-2) * wordSize); // prolog + + // Frame is now completed as far as size and linkage. + + int frame_complete = __ pc() - start; + + // push java thread (becomes first argument of C function) + __ movl(Address(rsp, thread_off * wordSize), java_thread); + + // Set up last_Java_sp and last_Java_fp + __ set_last_Java_frame(java_thread, rsp, rbp, NULL); + + // Call runtime + BLOCK_COMMENT("call runtime_entry"); + __ call(RuntimeAddress(runtime_entry)); + // Generate oop map + OopMap* map = new OopMap(framesize, 0); + oop_maps->add_gc_map(__ pc() - start, map); + + // restore the thread (cannot use the pushed argument since arguments + // may be overwritten by C code generated by an optimizing compiler); + // however can use the register value directly if it is callee saved. + __ get_thread(java_thread); + + __ reset_last_Java_frame(java_thread, true, false); + + __ leave(); // required for proper stackwalking of RuntimeStub frame + + // check for pending exceptions +#ifdef ASSERT + Label L; + __ cmpl(Address(java_thread, Thread::pending_exception_offset()), NULL_WORD); + __ jcc(Assembler::notEqual, L); + __ should_not_reach_here(); + __ bind(L); +#endif /* ASSERT */ + __ jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + + + RuntimeStub* stub = RuntimeStub::new_runtime_stub(name, &code, frame_complete, framesize, oop_maps, false); + return stub->entry_point(); + } + + + void create_control_words() { + // Round to nearest, 53-bit mode, exceptions masked + StubRoutines::_fpu_cntrl_wrd_std = 0x027F; + // Round to zero, 53-bit mode, exception mased + StubRoutines::_fpu_cntrl_wrd_trunc = 0x0D7F; + // Round to nearest, 24-bit mode, exceptions masked + StubRoutines::_fpu_cntrl_wrd_24 = 0x007F; + // Round to nearest, 64-bit mode, exceptions masked + StubRoutines::_fpu_cntrl_wrd_64 = 0x037F; + // Round to nearest, 64-bit mode, exceptions masked + StubRoutines::_mxcsr_std = 0x1F80; + // Note: the following two constants are 80-bit values + // layout is critical for correct loading by FPU. + // Bias for strict fp multiply/divide + StubRoutines::_fpu_subnormal_bias1[0]= 0x00000000; // 2^(-15360) == 0x03ff 8000 0000 0000 0000 + StubRoutines::_fpu_subnormal_bias1[1]= 0x80000000; + StubRoutines::_fpu_subnormal_bias1[2]= 0x03ff; + // Un-Bias for strict fp multiply/divide + StubRoutines::_fpu_subnormal_bias2[0]= 0x00000000; // 2^(+15360) == 0x7bff 8000 0000 0000 0000 + StubRoutines::_fpu_subnormal_bias2[1]= 0x80000000; + StubRoutines::_fpu_subnormal_bias2[2]= 0x7bff; + } + + //--------------------------------------------------------------------------- + // Initialization + + void generate_initial() { + // Generates all stubs and initializes the entry points + + //------------------------------------------------------------------------------------------------------------------------ + // entry points that exist in all platforms + // Note: This is code that could be shared among different platforms - however the benefit seems to be smaller than + // the disadvantage of having a much more complicated generator structure. See also comment in stubRoutines.hpp. + StubRoutines::_forward_exception_entry = generate_forward_exception(); + + StubRoutines::_call_stub_entry = + generate_call_stub(StubRoutines::_call_stub_return_address); + // is referenced by megamorphic call + StubRoutines::_catch_exception_entry = generate_catch_exception(); + + // These are currently used by Solaris/Intel + StubRoutines::_atomic_xchg_entry = generate_atomic_xchg(); + + StubRoutines::_handler_for_unsafe_access_entry = + generate_handler_for_unsafe_access(); + + // platform dependent + create_control_words(); + + StubRoutines::i486::_verify_mxcsr_entry = generate_verify_mxcsr(); + StubRoutines::i486::_verify_fpu_cntrl_wrd_entry = generate_verify_fpu_cntrl_wrd(); + StubRoutines::_d2i_wrapper = generate_d2i_wrapper(T_INT, + CAST_FROM_FN_PTR(address, SharedRuntime::d2i)); + StubRoutines::_d2l_wrapper = generate_d2i_wrapper(T_LONG, + CAST_FROM_FN_PTR(address, SharedRuntime::d2l)); + } + + + void generate_all() { + // Generates all stubs and initializes the entry points + + // These entry points require SharedInfo::stack0 to be set up in non-core builds + // and need to be relocatable, so they each fabricate a RuntimeStub internally. + StubRoutines::_throw_AbstractMethodError_entry = generate_throw_exception("AbstractMethodError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_AbstractMethodError), false); + StubRoutines::_throw_ArithmeticException_entry = generate_throw_exception("ArithmeticException throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_ArithmeticException), true); + StubRoutines::_throw_NullPointerException_entry = generate_throw_exception("NullPointerException throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException), true); + StubRoutines::_throw_NullPointerException_at_call_entry= generate_throw_exception("NullPointerException at call throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException_at_call), false); + StubRoutines::_throw_StackOverflowError_entry = generate_throw_exception("StackOverflowError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_StackOverflowError), false); + + //------------------------------------------------------------------------------------------------------------------------ + // entry points that are platform specific + + // support for verify_oop (must happen after universe_init) + StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop(); + + // arraycopy stubs used by compilers + generate_arraycopy_stubs(); + } + + + public: + StubGenerator(CodeBuffer* code, bool all) : StubCodeGenerator(code) { + if (all) { + generate_all(); + } else { + generate_initial(); + } + } +}; // end class declaration + + +void StubGenerator_generate(CodeBuffer* code, bool all) { + StubGenerator g(code, all); +} diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp new file mode 100644 index 00000000000..738beafa314 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp @@ -0,0 +1,2907 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubGenerator_x86_64.cpp.incl" + +// Declaration and definition of StubGenerator (no .hpp file). +// For a more detailed description of the stub routine structure +// see the comment in stubRoutines.hpp + +#define __ _masm-> + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) /* nothing */ +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") +const int MXCSR_MASK = 0xFFC0; // Mask out any pending exceptions + +// Stub Code definitions + +static address handle_unsafe_access() { + JavaThread* thread = JavaThread::current(); + address pc = thread->saved_exception_pc(); + // pc is the instruction which we must emulate + // doing a no-op is fine: return garbage from the load + // therefore, compute npc + address npc = Assembler::locate_next_instruction(pc); + + // request an async exception + thread->set_pending_unsafe_access_error(); + + // return address of next instruction to execute + return npc; +} + +class StubGenerator: public StubCodeGenerator { + private: + +#ifdef PRODUCT +#define inc_counter_np(counter) (0) +#else + void inc_counter_np_(int& counter) { + __ incrementl(ExternalAddress((address)&counter)); + } +#define inc_counter_np(counter) \ + BLOCK_COMMENT("inc_counter " #counter); \ + inc_counter_np_(counter); +#endif + + // Call stubs are used to call Java from C + // + // Linux Arguments: + // c_rarg0: call wrapper address address + // c_rarg1: result address + // c_rarg2: result type BasicType + // c_rarg3: method methodOop + // c_rarg4: (interpreter) entry point address + // c_rarg5: parameters intptr_t* + // 16(rbp): parameter size (in words) int + // 24(rbp): thread Thread* + // + // [ return_from_Java ] <--- rsp + // [ argument word n ] + // ... + // -12 [ argument word 1 ] + // -11 [ saved r15 ] <--- rsp_after_call + // -10 [ saved r14 ] + // -9 [ saved r13 ] + // -8 [ saved r12 ] + // -7 [ saved rbx ] + // -6 [ call wrapper ] + // -5 [ result ] + // -4 [ result type ] + // -3 [ method ] + // -2 [ entry point ] + // -1 [ parameters ] + // 0 [ saved rbp ] <--- rbp + // 1 [ return address ] + // 2 [ parameter size ] + // 3 [ thread ] + // + // Windows Arguments: + // c_rarg0: call wrapper address address + // c_rarg1: result address + // c_rarg2: result type BasicType + // c_rarg3: method methodOop + // 48(rbp): (interpreter) entry point address + // 56(rbp): parameters intptr_t* + // 64(rbp): parameter size (in words) int + // 72(rbp): thread Thread* + // + // [ return_from_Java ] <--- rsp + // [ argument word n ] + // ... + // -8 [ argument word 1 ] + // -7 [ saved r15 ] <--- rsp_after_call + // -6 [ saved r14 ] + // -5 [ saved r13 ] + // -4 [ saved r12 ] + // -3 [ saved rdi ] + // -2 [ saved rsi ] + // -1 [ saved rbx ] + // 0 [ saved rbp ] <--- rbp + // 1 [ return address ] + // 2 [ call wrapper ] + // 3 [ result ] + // 4 [ result type ] + // 5 [ method ] + // 6 [ entry point ] + // 7 [ parameters ] + // 8 [ parameter size ] + // 9 [ thread ] + // + // Windows reserves the callers stack space for arguments 1-4. + // We spill c_rarg0-c_rarg3 to this space. + + // Call stub stack layout word offsets from rbp + enum call_stub_layout { +#ifdef _WIN64 + rsp_after_call_off = -7, + r15_off = rsp_after_call_off, + r14_off = -6, + r13_off = -5, + r12_off = -4, + rdi_off = -3, + rsi_off = -2, + rbx_off = -1, + rbp_off = 0, + retaddr_off = 1, + call_wrapper_off = 2, + result_off = 3, + result_type_off = 4, + method_off = 5, + entry_point_off = 6, + parameters_off = 7, + parameter_size_off = 8, + thread_off = 9 +#else + rsp_after_call_off = -12, + mxcsr_off = rsp_after_call_off, + r15_off = -11, + r14_off = -10, + r13_off = -9, + r12_off = -8, + rbx_off = -7, + call_wrapper_off = -6, + result_off = -5, + result_type_off = -4, + method_off = -3, + entry_point_off = -2, + parameters_off = -1, + rbp_off = 0, + retaddr_off = 1, + parameter_size_off = 2, + thread_off = 3 +#endif + }; + + address generate_call_stub(address& return_address) { + assert((int)frame::entry_frame_after_call_words == -(int)rsp_after_call_off + 1 && + (int)frame::entry_frame_call_wrapper_offset == (int)call_wrapper_off, + "adjust this code"); + StubCodeMark mark(this, "StubRoutines", "call_stub"); + address start = __ pc(); + + // same as in generate_catch_exception()! + const Address rsp_after_call(rbp, rsp_after_call_off * wordSize); + + const Address call_wrapper (rbp, call_wrapper_off * wordSize); + const Address result (rbp, result_off * wordSize); + const Address result_type (rbp, result_type_off * wordSize); + const Address method (rbp, method_off * wordSize); + const Address entry_point (rbp, entry_point_off * wordSize); + const Address parameters (rbp, parameters_off * wordSize); + const Address parameter_size(rbp, parameter_size_off * wordSize); + + // same as in generate_catch_exception()! + const Address thread (rbp, thread_off * wordSize); + + const Address r15_save(rbp, r15_off * wordSize); + const Address r14_save(rbp, r14_off * wordSize); + const Address r13_save(rbp, r13_off * wordSize); + const Address r12_save(rbp, r12_off * wordSize); + const Address rbx_save(rbp, rbx_off * wordSize); + + // stub code + __ enter(); + __ subq(rsp, -rsp_after_call_off * wordSize); + + // save register parameters +#ifndef _WIN64 + __ movq(parameters, c_rarg5); // parameters + __ movq(entry_point, c_rarg4); // entry_point +#endif + + __ movq(method, c_rarg3); // method + __ movl(result_type, c_rarg2); // result type + __ movq(result, c_rarg1); // result + __ movq(call_wrapper, c_rarg0); // call wrapper + + // save regs belonging to calling function + __ movq(rbx_save, rbx); + __ movq(r12_save, r12); + __ movq(r13_save, r13); + __ movq(r14_save, r14); + __ movq(r15_save, r15); + +#ifdef _WIN64 + const Address rdi_save(rbp, rdi_off * wordSize); + const Address rsi_save(rbp, rsi_off * wordSize); + + __ movq(rsi_save, rsi); + __ movq(rdi_save, rdi); +#else + const Address mxcsr_save(rbp, mxcsr_off * wordSize); + { + Label skip_ldmx; + __ stmxcsr(mxcsr_save); + __ movl(rax, mxcsr_save); + __ andl(rax, MXCSR_MASK); // Only check control and mask bits + ExternalAddress mxcsr_std(StubRoutines::amd64::mxcsr_std()); + __ cmp32(rax, mxcsr_std); + __ jcc(Assembler::equal, skip_ldmx); + __ ldmxcsr(mxcsr_std); + __ bind(skip_ldmx); + } +#endif + + // Load up thread register + __ movq(r15_thread, thread); + +#ifdef ASSERT + // make sure we have no pending exceptions + { + Label L; + __ cmpq(Address(r15_thread, Thread::pending_exception_offset()), (int)NULL_WORD); + __ jcc(Assembler::equal, L); + __ stop("StubRoutines::call_stub: entered with pending exception"); + __ bind(L); + } +#endif + + // pass parameters if any + BLOCK_COMMENT("pass parameters if any"); + Label parameters_done; + __ movl(c_rarg3, parameter_size); + __ testl(c_rarg3, c_rarg3); + __ jcc(Assembler::zero, parameters_done); + + Label loop; + __ movq(c_rarg2, parameters); // parameter pointer + __ movl(c_rarg1, c_rarg3); // parameter counter is in c_rarg1 + __ BIND(loop); + if (TaggedStackInterpreter) { + __ movq(rax, Address(c_rarg2, 0)); // get tag + __ addq(c_rarg2, wordSize); // advance to next tag + __ pushq(rax); // pass tag + } + __ movq(rax, Address(c_rarg2, 0)); // get parameter + __ addq(c_rarg2, wordSize); // advance to next parameter + __ decrementl(c_rarg1); // decrement counter + __ pushq(rax); // pass parameter + __ jcc(Assembler::notZero, loop); + + // call Java function + __ BIND(parameters_done); + __ movq(rbx, method); // get methodOop + __ movq(c_rarg1, entry_point); // get entry_point + __ movq(r13, rsp); // set sender sp + BLOCK_COMMENT("call Java function"); + __ call(c_rarg1); + + BLOCK_COMMENT("call_stub_return_address:"); + return_address = __ pc(); + + // store result depending on type (everything that is not + // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT) + __ movq(c_rarg0, result); + Label is_long, is_float, is_double, exit; + __ movl(c_rarg1, result_type); + __ cmpl(c_rarg1, T_OBJECT); + __ jcc(Assembler::equal, is_long); + __ cmpl(c_rarg1, T_LONG); + __ jcc(Assembler::equal, is_long); + __ cmpl(c_rarg1, T_FLOAT); + __ jcc(Assembler::equal, is_float); + __ cmpl(c_rarg1, T_DOUBLE); + __ jcc(Assembler::equal, is_double); + + // handle T_INT case + __ movl(Address(c_rarg0, 0), rax); + + __ BIND(exit); + + // pop parameters + __ leaq(rsp, rsp_after_call); + +#ifdef ASSERT + // verify that threads correspond + { + Label L, S; + __ cmpq(r15_thread, thread); + __ jcc(Assembler::notEqual, S); + __ get_thread(rbx); + __ cmpq(r15_thread, rbx); + __ jcc(Assembler::equal, L); + __ bind(S); + __ jcc(Assembler::equal, L); + __ stop("StubRoutines::call_stub: threads must correspond"); + __ bind(L); + } +#endif + + // restore regs belonging to calling function + __ movq(r15, r15_save); + __ movq(r14, r14_save); + __ movq(r13, r13_save); + __ movq(r12, r12_save); + __ movq(rbx, rbx_save); + +#ifdef _WIN64 + __ movq(rdi, rdi_save); + __ movq(rsi, rsi_save); +#else + __ ldmxcsr(mxcsr_save); +#endif + + // restore rsp + __ addq(rsp, -rsp_after_call_off * wordSize); + + // return + __ popq(rbp); + __ ret(0); + + // handle return types different from T_INT + __ BIND(is_long); + __ movq(Address(c_rarg0, 0), rax); + __ jmp(exit); + + __ BIND(is_float); + __ movflt(Address(c_rarg0, 0), xmm0); + __ jmp(exit); + + __ BIND(is_double); + __ movdbl(Address(c_rarg0, 0), xmm0); + __ jmp(exit); + + return start; + } + + // Return point for a Java call if there's an exception thrown in + // Java code. The exception is caught and transformed into a + // pending exception stored in JavaThread that can be tested from + // within the VM. + // + // Note: Usually the parameters are removed by the callee. In case + // of an exception crossing an activation frame boundary, that is + // not the case if the callee is compiled code => need to setup the + // rsp. + // + // rax: exception oop + + address generate_catch_exception() { + StubCodeMark mark(this, "StubRoutines", "catch_exception"); + address start = __ pc(); + + // same as in generate_call_stub(): + const Address rsp_after_call(rbp, rsp_after_call_off * wordSize); + const Address thread (rbp, thread_off * wordSize); + +#ifdef ASSERT + // verify that threads correspond + { + Label L, S; + __ cmpq(r15_thread, thread); + __ jcc(Assembler::notEqual, S); + __ get_thread(rbx); + __ cmpq(r15_thread, rbx); + __ jcc(Assembler::equal, L); + __ bind(S); + __ stop("StubRoutines::catch_exception: threads must correspond"); + __ bind(L); + } +#endif + + // set pending exception + __ verify_oop(rax); + + __ movq(Address(r15_thread, Thread::pending_exception_offset()), rax); + __ lea(rscratch1, ExternalAddress((address)__FILE__)); + __ movq(Address(r15_thread, Thread::exception_file_offset()), rscratch1); + __ movl(Address(r15_thread, Thread::exception_line_offset()), (int) __LINE__); + + // complete return to VM + assert(StubRoutines::_call_stub_return_address != NULL, + "_call_stub_return_address must have been generated before"); + __ jump(RuntimeAddress(StubRoutines::_call_stub_return_address)); + + return start; + } + + // Continuation point for runtime calls returning with a pending + // exception. The pending exception check happened in the runtime + // or native call stub. The pending exception in Thread is + // converted into a Java-level exception. + // + // Contract with Java-level exception handlers: + // rax: exception + // rdx: throwing pc + // + // NOTE: At entry of this stub, exception-pc must be on stack !! + + address generate_forward_exception() { + StubCodeMark mark(this, "StubRoutines", "forward exception"); + address start = __ pc(); + + // Upon entry, the sp points to the return address returning into + // Java (interpreted or compiled) code; i.e., the return address + // becomes the throwing pc. + // + // Arguments pushed before the runtime call are still on the stack + // but the exception handler will reset the stack pointer -> + // ignore them. A potential result in registers can be ignored as + // well. + +#ifdef ASSERT + // make sure this code is only executed if there is a pending exception + { + Label L; + __ cmpq(Address(r15_thread, Thread::pending_exception_offset()), (int) NULL); + __ jcc(Assembler::notEqual, L); + __ stop("StubRoutines::forward exception: no pending exception (1)"); + __ bind(L); + } +#endif + + // compute exception handler into rbx + __ movq(c_rarg0, Address(rsp, 0)); + BLOCK_COMMENT("call exception_handler_for_return_address"); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, + SharedRuntime::exception_handler_for_return_address), + c_rarg0); + __ movq(rbx, rax); + + // setup rax & rdx, remove return address & clear pending exception + __ popq(rdx); + __ movq(rax, Address(r15_thread, Thread::pending_exception_offset())); + __ movptr(Address(r15_thread, Thread::pending_exception_offset()), (int)NULL_WORD); + +#ifdef ASSERT + // make sure exception is set + { + Label L; + __ testq(rax, rax); + __ jcc(Assembler::notEqual, L); + __ stop("StubRoutines::forward exception: no pending exception (2)"); + __ bind(L); + } +#endif + + // continue at exception handler (return address removed) + // rax: exception + // rbx: exception handler + // rdx: throwing pc + __ verify_oop(rax); + __ jmp(rbx); + + return start; + } + + // Support for jint atomic::xchg(jint exchange_value, volatile jint* dest) + // + // Arguments : + // c_rarg0: exchange_value + // c_rarg0: dest + // + // Result: + // *dest <- ex, return (orig *dest) + address generate_atomic_xchg() { + StubCodeMark mark(this, "StubRoutines", "atomic_xchg"); + address start = __ pc(); + + __ movl(rax, c_rarg0); // Copy to eax we need a return value anyhow + __ xchgl(rax, Address(c_rarg1, 0)); // automatic LOCK + __ ret(0); + + return start; + } + + // Support for intptr_t atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) + // + // Arguments : + // c_rarg0: exchange_value + // c_rarg1: dest + // + // Result: + // *dest <- ex, return (orig *dest) + address generate_atomic_xchg_ptr() { + StubCodeMark mark(this, "StubRoutines", "atomic_xchg_ptr"); + address start = __ pc(); + + __ movq(rax, c_rarg0); // Copy to eax we need a return value anyhow + __ xchgq(rax, Address(c_rarg1, 0)); // automatic LOCK + __ ret(0); + + return start; + } + + // Support for jint atomic::atomic_cmpxchg(jint exchange_value, volatile jint* dest, + // jint compare_value) + // + // Arguments : + // c_rarg0: exchange_value + // c_rarg1: dest + // c_rarg2: compare_value + // + // Result: + // if ( compare_value == *dest ) { + // *dest = exchange_value + // return compare_value; + // else + // return *dest; + address generate_atomic_cmpxchg() { + StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg"); + address start = __ pc(); + + __ movl(rax, c_rarg2); + if ( os::is_MP() ) __ lock(); + __ cmpxchgl(c_rarg0, Address(c_rarg1, 0)); + __ ret(0); + + return start; + } + + // Support for jint atomic::atomic_cmpxchg_long(jlong exchange_value, + // volatile jlong* dest, + // jlong compare_value) + // Arguments : + // c_rarg0: exchange_value + // c_rarg1: dest + // c_rarg2: compare_value + // + // Result: + // if ( compare_value == *dest ) { + // *dest = exchange_value + // return compare_value; + // else + // return *dest; + address generate_atomic_cmpxchg_long() { + StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg_long"); + address start = __ pc(); + + __ movq(rax, c_rarg2); + if ( os::is_MP() ) __ lock(); + __ cmpxchgq(c_rarg0, Address(c_rarg1, 0)); + __ ret(0); + + return start; + } + + // Support for jint atomic::add(jint add_value, volatile jint* dest) + // + // Arguments : + // c_rarg0: add_value + // c_rarg1: dest + // + // Result: + // *dest += add_value + // return *dest; + address generate_atomic_add() { + StubCodeMark mark(this, "StubRoutines", "atomic_add"); + address start = __ pc(); + + __ movl(rax, c_rarg0); + if ( os::is_MP() ) __ lock(); + __ xaddl(Address(c_rarg1, 0), c_rarg0); + __ addl(rax, c_rarg0); + __ ret(0); + + return start; + } + + // Support for intptr_t atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) + // + // Arguments : + // c_rarg0: add_value + // c_rarg1: dest + // + // Result: + // *dest += add_value + // return *dest; + address generate_atomic_add_ptr() { + StubCodeMark mark(this, "StubRoutines", "atomic_add_ptr"); + address start = __ pc(); + + __ movq(rax, c_rarg0); // Copy to eax we need a return value anyhow + if ( os::is_MP() ) __ lock(); + __ xaddl(Address(c_rarg1, 0), c_rarg0); + __ addl(rax, c_rarg0); + __ ret(0); + + return start; + } + + // Support for intptr_t OrderAccess::fence() + // + // Arguments : + // + // Result: + address generate_orderaccess_fence() { + StubCodeMark mark(this, "StubRoutines", "orderaccess_fence"); + address start = __ pc(); + __ mfence(); + __ ret(0); + + return start; + } + + // Support for intptr_t get_previous_fp() + // + // This routine is used to find the previous frame pointer for the + // caller (current_frame_guess). This is used as part of debugging + // ps() is seemingly lost trying to find frames. + // This code assumes that caller current_frame_guess) has a frame. + address generate_get_previous_fp() { + StubCodeMark mark(this, "StubRoutines", "get_previous_fp"); + const Address old_fp(rbp, 0); + const Address older_fp(rax, 0); + address start = __ pc(); + + __ enter(); + __ movq(rax, old_fp); // callers fp + __ movq(rax, older_fp); // the frame for ps() + __ popq(rbp); + __ ret(0); + + return start; + } + + //---------------------------------------------------------------------------------------------------- + // Support for void verify_mxcsr() + // + // This routine is used with -Xcheck:jni to verify that native + // JNI code does not return to Java code without restoring the + // MXCSR register to our expected state. + + address generate_verify_mxcsr() { + StubCodeMark mark(this, "StubRoutines", "verify_mxcsr"); + address start = __ pc(); + + const Address mxcsr_save(rsp, 0); + + if (CheckJNICalls) { + Label ok_ret; + __ pushq(rax); + __ subq(rsp, wordSize); // allocate a temp location + __ stmxcsr(mxcsr_save); + __ movl(rax, mxcsr_save); + __ andl(rax, MXCSR_MASK); // Only check control and mask bits + __ cmpl(rax, *(int *)(StubRoutines::amd64::mxcsr_std())); + __ jcc(Assembler::equal, ok_ret); + + __ warn("MXCSR changed by native JNI code, use -XX:+RestoreMXCSROnJNICall"); + + __ ldmxcsr(ExternalAddress(StubRoutines::amd64::mxcsr_std())); + + __ bind(ok_ret); + __ addq(rsp, wordSize); + __ popq(rax); + } + + __ ret(0); + + return start; + } + + address generate_f2i_fixup() { + StubCodeMark mark(this, "StubRoutines", "f2i_fixup"); + Address inout(rsp, 5 * wordSize); // return address + 4 saves + + address start = __ pc(); + + Label L; + + __ pushq(rax); + __ pushq(c_rarg3); + __ pushq(c_rarg2); + __ pushq(c_rarg1); + + __ movl(rax, 0x7f800000); + __ xorl(c_rarg3, c_rarg3); + __ movl(c_rarg2, inout); + __ movl(c_rarg1, c_rarg2); + __ andl(c_rarg1, 0x7fffffff); + __ cmpl(rax, c_rarg1); // NaN? -> 0 + __ jcc(Assembler::negative, L); + __ testl(c_rarg2, c_rarg2); // signed ? min_jint : max_jint + __ movl(c_rarg3, 0x80000000); + __ movl(rax, 0x7fffffff); + __ cmovl(Assembler::positive, c_rarg3, rax); + + __ bind(L); + __ movq(inout, c_rarg3); + + __ popq(c_rarg1); + __ popq(c_rarg2); + __ popq(c_rarg3); + __ popq(rax); + + __ ret(0); + + return start; + } + + address generate_f2l_fixup() { + StubCodeMark mark(this, "StubRoutines", "f2l_fixup"); + Address inout(rsp, 5 * wordSize); // return address + 4 saves + address start = __ pc(); + + Label L; + + __ pushq(rax); + __ pushq(c_rarg3); + __ pushq(c_rarg2); + __ pushq(c_rarg1); + + __ movl(rax, 0x7f800000); + __ xorl(c_rarg3, c_rarg3); + __ movl(c_rarg2, inout); + __ movl(c_rarg1, c_rarg2); + __ andl(c_rarg1, 0x7fffffff); + __ cmpl(rax, c_rarg1); // NaN? -> 0 + __ jcc(Assembler::negative, L); + __ testl(c_rarg2, c_rarg2); // signed ? min_jlong : max_jlong + __ mov64(c_rarg3, 0x8000000000000000); + __ mov64(rax, 0x7fffffffffffffff); + __ cmovq(Assembler::positive, c_rarg3, rax); + + __ bind(L); + __ movq(inout, c_rarg3); + + __ popq(c_rarg1); + __ popq(c_rarg2); + __ popq(c_rarg3); + __ popq(rax); + + __ ret(0); + + return start; + } + + address generate_d2i_fixup() { + StubCodeMark mark(this, "StubRoutines", "d2i_fixup"); + Address inout(rsp, 6 * wordSize); // return address + 5 saves + + address start = __ pc(); + + Label L; + + __ pushq(rax); + __ pushq(c_rarg3); + __ pushq(c_rarg2); + __ pushq(c_rarg1); + __ pushq(c_rarg0); + + __ movl(rax, 0x7ff00000); + __ movq(c_rarg2, inout); + __ movl(c_rarg3, c_rarg2); + __ movq(c_rarg1, c_rarg2); + __ movq(c_rarg0, c_rarg2); + __ negl(c_rarg3); + __ shrq(c_rarg1, 0x20); + __ orl(c_rarg3, c_rarg2); + __ andl(c_rarg1, 0x7fffffff); + __ xorl(c_rarg2, c_rarg2); + __ shrl(c_rarg3, 0x1f); + __ orl(c_rarg1, c_rarg3); + __ cmpl(rax, c_rarg1); + __ jcc(Assembler::negative, L); // NaN -> 0 + __ testq(c_rarg0, c_rarg0); // signed ? min_jint : max_jint + __ movl(c_rarg2, 0x80000000); + __ movl(rax, 0x7fffffff); + __ cmovl(Assembler::positive, c_rarg2, rax); + + __ bind(L); + __ movq(inout, c_rarg2); + + __ popq(c_rarg0); + __ popq(c_rarg1); + __ popq(c_rarg2); + __ popq(c_rarg3); + __ popq(rax); + + __ ret(0); + + return start; + } + + address generate_d2l_fixup() { + StubCodeMark mark(this, "StubRoutines", "d2l_fixup"); + Address inout(rsp, 6 * wordSize); // return address + 5 saves + + address start = __ pc(); + + Label L; + + __ pushq(rax); + __ pushq(c_rarg3); + __ pushq(c_rarg2); + __ pushq(c_rarg1); + __ pushq(c_rarg0); + + __ movl(rax, 0x7ff00000); + __ movq(c_rarg2, inout); + __ movl(c_rarg3, c_rarg2); + __ movq(c_rarg1, c_rarg2); + __ movq(c_rarg0, c_rarg2); + __ negl(c_rarg3); + __ shrq(c_rarg1, 0x20); + __ orl(c_rarg3, c_rarg2); + __ andl(c_rarg1, 0x7fffffff); + __ xorl(c_rarg2, c_rarg2); + __ shrl(c_rarg3, 0x1f); + __ orl(c_rarg1, c_rarg3); + __ cmpl(rax, c_rarg1); + __ jcc(Assembler::negative, L); // NaN -> 0 + __ testq(c_rarg0, c_rarg0); // signed ? min_jlong : max_jlong + __ mov64(c_rarg2, 0x8000000000000000); + __ mov64(rax, 0x7fffffffffffffff); + __ cmovq(Assembler::positive, c_rarg2, rax); + + __ bind(L); + __ movq(inout, c_rarg2); + + __ popq(c_rarg0); + __ popq(c_rarg1); + __ popq(c_rarg2); + __ popq(c_rarg3); + __ popq(rax); + + __ ret(0); + + return start; + } + + address generate_fp_mask(const char *stub_name, int64_t mask) { + StubCodeMark mark(this, "StubRoutines", stub_name); + + __ align(16); + address start = __ pc(); + + __ emit_data64( mask, relocInfo::none ); + __ emit_data64( mask, relocInfo::none ); + + return start; + } + + // The following routine generates a subroutine to throw an + // asynchronous UnknownError when an unsafe access gets a fault that + // could not be reasonably prevented by the programmer. (Example: + // SIGBUS/OBJERR.) + address generate_handler_for_unsafe_access() { + StubCodeMark mark(this, "StubRoutines", "handler_for_unsafe_access"); + address start = __ pc(); + + __ pushq(0); // hole for return address-to-be + __ pushaq(); // push registers + Address next_pc(rsp, RegisterImpl::number_of_registers * BytesPerWord); + + __ subq(rsp, frame::arg_reg_save_area_bytes); + BLOCK_COMMENT("call handle_unsafe_access"); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, handle_unsafe_access))); + __ addq(rsp, frame::arg_reg_save_area_bytes); + + __ movq(next_pc, rax); // stuff next address + __ popaq(); + __ ret(0); // jump to next address + + return start; + } + + // Non-destructive plausibility checks for oops + // + // Arguments: + // all args on stack! + // + // Stack after saving c_rarg3: + // [tos + 0]: saved c_rarg3 + // [tos + 1]: saved c_rarg2 + // [tos + 2]: saved flags + // [tos + 3]: return address + // * [tos + 4]: error message (char*) + // * [tos + 5]: object to verify (oop) + // * [tos + 6]: saved rax - saved by caller and bashed + // * = popped on exit + address generate_verify_oop() { + StubCodeMark mark(this, "StubRoutines", "verify_oop"); + address start = __ pc(); + + Label exit, error; + + __ pushfq(); + __ incrementl(ExternalAddress((address) StubRoutines::verify_oop_count_addr())); + + // save c_rarg2 and c_rarg3 + __ pushq(c_rarg2); + __ pushq(c_rarg3); + + // get object + __ movq(rax, Address(rsp, 5 * wordSize)); + + // make sure object is 'reasonable' + __ testq(rax, rax); + __ jcc(Assembler::zero, exit); // if obj is NULL it is OK + // Check if the oop is in the right area of memory + __ movq(c_rarg2, rax); + __ movptr(c_rarg3, (int64_t) Universe::verify_oop_mask()); + __ andq(c_rarg2, c_rarg3); + __ movptr(c_rarg3, (int64_t) Universe::verify_oop_bits()); + __ cmpq(c_rarg2, c_rarg3); + __ jcc(Assembler::notZero, error); + + // make sure klass is 'reasonable' + __ movq(rax, Address(rax, oopDesc::klass_offset_in_bytes())); // get klass + __ testq(rax, rax); + __ jcc(Assembler::zero, error); // if klass is NULL it is broken + // Check if the klass is in the right area of memory + __ movq(c_rarg2, rax); + __ movptr(c_rarg3, (int64_t) Universe::verify_klass_mask()); + __ andq(c_rarg2, c_rarg3); + __ movptr(c_rarg3, (int64_t) Universe::verify_klass_bits()); + __ cmpq(c_rarg2, c_rarg3); + __ jcc(Assembler::notZero, error); + + // make sure klass' klass is 'reasonable' + __ movq(rax, Address(rax, oopDesc::klass_offset_in_bytes())); + __ testq(rax, rax); + __ jcc(Assembler::zero, error); // if klass' klass is NULL it is broken + // Check if the klass' klass is in the right area of memory + __ movptr(c_rarg3, (int64_t) Universe::verify_klass_mask()); + __ andq(rax, c_rarg3); + __ movptr(c_rarg3, (int64_t) Universe::verify_klass_bits()); + __ cmpq(rax, c_rarg3); + __ jcc(Assembler::notZero, error); + + // return if everything seems ok + __ bind(exit); + __ movq(rax, Address(rsp, 6 * wordSize)); // get saved rax back + __ popq(c_rarg3); // restore c_rarg3 + __ popq(c_rarg2); // restore c_rarg2 + __ popfq(); // restore flags + __ ret(3 * wordSize); // pop caller saved stuff + + // handle errors + __ bind(error); + __ movq(rax, Address(rsp, 6 * wordSize)); // get saved rax back + __ popq(c_rarg3); // get saved c_rarg3 back + __ popq(c_rarg2); // get saved c_rarg2 back + __ popfq(); // get saved flags off stack -- + // will be ignored + + __ pushaq(); // push registers + // (rip is already + // already pushed) + // debug(char* msg, int64_t regs[]) + // We've popped the registers we'd saved (c_rarg3, c_rarg2 and flags), and + // pushed all the registers, so now the stack looks like: + // [tos + 0] 16 saved registers + // [tos + 16] return address + // [tos + 17] error message (char*) + + __ movq(c_rarg0, Address(rsp, 17 * wordSize)); // pass address of error message + __ movq(c_rarg1, rsp); // pass address of regs on stack + __ movq(r12, rsp); // remember rsp + __ subq(rsp, frame::arg_reg_save_area_bytes);// windows + __ andq(rsp, -16); // align stack as required by ABI + BLOCK_COMMENT("call MacroAssembler::debug"); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::debug))); + __ movq(rsp, r12); // restore rsp + __ popaq(); // pop registers + __ ret(3 * wordSize); // pop caller saved stuff + + return start; + } + + static address disjoint_byte_copy_entry; + static address disjoint_short_copy_entry; + static address disjoint_int_copy_entry; + static address disjoint_long_copy_entry; + static address disjoint_oop_copy_entry; + + static address byte_copy_entry; + static address short_copy_entry; + static address int_copy_entry; + static address long_copy_entry; + static address oop_copy_entry; + + static address checkcast_copy_entry; + + // + // Verify that a register contains clean 32-bits positive value + // (high 32-bits are 0) so it could be used in 64-bits shifts. + // + // Input: + // Rint - 32-bits value + // Rtmp - scratch + // + void assert_clean_int(Register Rint, Register Rtmp) { +#ifdef ASSERT + Label L; + assert_different_registers(Rtmp, Rint); + __ movslq(Rtmp, Rint); + __ cmpq(Rtmp, Rint); + __ jccb(Assembler::equal, L); + __ stop("high 32-bits of int value are not 0"); + __ bind(L); +#endif + } + + // Generate overlap test for array copy stubs + // + // Input: + // c_rarg0 - from + // c_rarg1 - to + // c_rarg2 - element count + // + // Output: + // rax - &from[element count - 1] + // + void array_overlap_test(address no_overlap_target, Address::ScaleFactor sf) { + assert(no_overlap_target != NULL, "must be generated"); + array_overlap_test(no_overlap_target, NULL, sf); + } + void array_overlap_test(Label& L_no_overlap, Address::ScaleFactor sf) { + array_overlap_test(NULL, &L_no_overlap, sf); + } + void array_overlap_test(address no_overlap_target, Label* NOLp, Address::ScaleFactor sf) { + const Register from = c_rarg0; + const Register to = c_rarg1; + const Register count = c_rarg2; + const Register end_from = rax; + + __ cmpq(to, from); + __ leaq(end_from, Address(from, count, sf, 0)); + if (NOLp == NULL) { + ExternalAddress no_overlap(no_overlap_target); + __ jump_cc(Assembler::belowEqual, no_overlap); + __ cmpq(to, end_from); + __ jump_cc(Assembler::aboveEqual, no_overlap); + } else { + __ jcc(Assembler::belowEqual, (*NOLp)); + __ cmpq(to, end_from); + __ jcc(Assembler::aboveEqual, (*NOLp)); + } + } + + // Shuffle first three arg regs on Windows into Linux/Solaris locations. + // + // Outputs: + // rdi - rcx + // rsi - rdx + // rdx - r8 + // rcx - r9 + // + // Registers r9 and r10 are used to save rdi and rsi on Windows, which latter + // are non-volatile. r9 and r10 should not be used by the caller. + // + void setup_arg_regs(int nargs = 3) { + const Register saved_rdi = r9; + const Register saved_rsi = r10; + assert(nargs == 3 || nargs == 4, "else fix"); +#ifdef _WIN64 + assert(c_rarg0 == rcx && c_rarg1 == rdx && c_rarg2 == r8 && c_rarg3 == r9, + "unexpected argument registers"); + if (nargs >= 4) + __ movq(rax, r9); // r9 is also saved_rdi + __ movq(saved_rdi, rdi); + __ movq(saved_rsi, rsi); + __ movq(rdi, rcx); // c_rarg0 + __ movq(rsi, rdx); // c_rarg1 + __ movq(rdx, r8); // c_rarg2 + if (nargs >= 4) + __ movq(rcx, rax); // c_rarg3 (via rax) +#else + assert(c_rarg0 == rdi && c_rarg1 == rsi && c_rarg2 == rdx && c_rarg3 == rcx, + "unexpected argument registers"); +#endif + } + + void restore_arg_regs() { + const Register saved_rdi = r9; + const Register saved_rsi = r10; +#ifdef _WIN64 + __ movq(rdi, saved_rdi); + __ movq(rsi, saved_rsi); +#endif + } + + // Generate code for an array write pre barrier + // + // addr - starting address + // count - element count + // + // Destroy no registers! + // + void gen_write_ref_array_pre_barrier(Register addr, Register count) { +#if 0 // G1 - only + assert_different_registers(addr, c_rarg1); + assert_different_registers(count, c_rarg0); + BarrierSet* bs = Universe::heap()->barrier_set(); + switch (bs->kind()) { + case BarrierSet::G1SATBCT: + case BarrierSet::G1SATBCTLogging: + { + __ pushaq(); // push registers + __ movq(c_rarg0, addr); + __ movq(c_rarg1, count); + __ call(RuntimeAddress(BarrierSet::static_write_ref_array_pre)); + __ popaq(); + } + break; + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + case BarrierSet::ModRef: + break; + default : + ShouldNotReachHere(); + + } +#endif // 0 G1 - only + } + + // + // Generate code for an array write post barrier + // + // Input: + // start - register containing starting address of destination array + // end - register containing ending address of destination array + // scratch - scratch register + // + // The input registers are overwritten. + // The ending address is inclusive. + void gen_write_ref_array_post_barrier(Register start, Register end, Register scratch) { + assert_different_registers(start, end, scratch); + BarrierSet* bs = Universe::heap()->barrier_set(); + switch (bs->kind()) { +#if 0 // G1 - only + case BarrierSet::G1SATBCT: + case BarrierSet::G1SATBCTLogging: + + { + __ pushaq(); // push registers (overkill) + // must compute element count unless barrier set interface is changed (other platforms supply count) + assert_different_registers(start, end, scratch); + __ leaq(scratch, Address(end, wordSize)); + __ subq(scratch, start); + __ shrq(scratch, LogBytesPerWord); + __ movq(c_rarg0, start); + __ movq(c_rarg1, scratch); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post)); + __ popaq(); + } + break; +#endif // 0 G1 - only + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + { + CardTableModRefBS* ct = (CardTableModRefBS*)bs; + assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); + + Label L_loop; + + __ shrq(start, CardTableModRefBS::card_shift); + __ shrq(end, CardTableModRefBS::card_shift); + __ subq(end, start); // number of bytes to copy + + const Register count = end; // 'end' register contains bytes count now + __ lea(scratch, ExternalAddress((address)ct->byte_map_base)); + __ addq(start, scratch); + __ BIND(L_loop); + __ movb(Address(start, count, Address::times_1), 0); + __ decrementq(count); + __ jcc(Assembler::greaterEqual, L_loop); + } + } + } + + // Copy big chunks forward + // + // Inputs: + // end_from - source arrays end address + // end_to - destination array end address + // qword_count - 64-bits element count, negative + // to - scratch + // L_copy_32_bytes - entry label + // L_copy_8_bytes - exit label + // + void copy_32_bytes_forward(Register end_from, Register end_to, + Register qword_count, Register to, + Label& L_copy_32_bytes, Label& L_copy_8_bytes) { + DEBUG_ONLY(__ stop("enter at entry label, not here")); + Label L_loop; + __ align(16); + __ BIND(L_loop); + __ movq(to, Address(end_from, qword_count, Address::times_8, -24)); + __ movq(Address(end_to, qword_count, Address::times_8, -24), to); + __ movq(to, Address(end_from, qword_count, Address::times_8, -16)); + __ movq(Address(end_to, qword_count, Address::times_8, -16), to); + __ movq(to, Address(end_from, qword_count, Address::times_8, - 8)); + __ movq(Address(end_to, qword_count, Address::times_8, - 8), to); + __ movq(to, Address(end_from, qword_count, Address::times_8, - 0)); + __ movq(Address(end_to, qword_count, Address::times_8, - 0), to); + __ BIND(L_copy_32_bytes); + __ addq(qword_count, 4); + __ jcc(Assembler::lessEqual, L_loop); + __ subq(qword_count, 4); + __ jcc(Assembler::less, L_copy_8_bytes); // Copy trailing qwords + } + + + // Copy big chunks backward + // + // Inputs: + // from - source arrays address + // dest - destination array address + // qword_count - 64-bits element count + // to - scratch + // L_copy_32_bytes - entry label + // L_copy_8_bytes - exit label + // + void copy_32_bytes_backward(Register from, Register dest, + Register qword_count, Register to, + Label& L_copy_32_bytes, Label& L_copy_8_bytes) { + DEBUG_ONLY(__ stop("enter at entry label, not here")); + Label L_loop; + __ align(16); + __ BIND(L_loop); + __ movq(to, Address(from, qword_count, Address::times_8, 24)); + __ movq(Address(dest, qword_count, Address::times_8, 24), to); + __ movq(to, Address(from, qword_count, Address::times_8, 16)); + __ movq(Address(dest, qword_count, Address::times_8, 16), to); + __ movq(to, Address(from, qword_count, Address::times_8, 8)); + __ movq(Address(dest, qword_count, Address::times_8, 8), to); + __ movq(to, Address(from, qword_count, Address::times_8, 0)); + __ movq(Address(dest, qword_count, Address::times_8, 0), to); + __ BIND(L_copy_32_bytes); + __ subq(qword_count, 4); + __ jcc(Assembler::greaterEqual, L_loop); + __ addq(qword_count, 4); + __ jcc(Assembler::greater, L_copy_8_bytes); // Copy trailing qwords + } + + + // Arguments: + // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary + // ignored + // name - stub name string + // + // Inputs: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // + // If 'from' and/or 'to' are aligned on 4-, 2-, or 1-byte boundaries, + // we let the hardware handle it. The one to eight bytes within words, + // dwords or qwords that span cache line boundaries will still be loaded + // and stored atomically. + // + // Side Effects: + // disjoint_byte_copy_entry is set to the no-overlap entry point + // used by generate_conjoint_byte_copy(). + // + address generate_disjoint_byte_copy(bool aligned, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_copy_32_bytes, L_copy_8_bytes, L_copy_4_bytes, L_copy_2_bytes; + Label L_copy_byte, L_exit; + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register count = rdx; // elements count + const Register byte_count = rcx; + const Register qword_count = count; + const Register end_from = from; // source array end address + const Register end_to = to; // destination array end address + // End pointers are inclusive, and if count is not zero they point + // to the last unit copied: end_to[0] := end_from[0] + + __ enter(); // required for proper stackwalking of RuntimeStub frame + assert_clean_int(c_rarg2, rax); // Make sure 'count' is clean int. + + disjoint_byte_copy_entry = __ pc(); + BLOCK_COMMENT("Entry:"); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + + setup_arg_regs(); // from => rdi, to => rsi, count => rdx + // r9 and r10 may be used to save non-volatile registers + + // 'from', 'to' and 'count' are now valid + __ movq(byte_count, count); + __ shrq(count, 3); // count => qword_count + + // Copy from low to high addresses. Use 'to' as scratch. + __ leaq(end_from, Address(from, qword_count, Address::times_8, -8)); + __ leaq(end_to, Address(to, qword_count, Address::times_8, -8)); + __ negq(qword_count); // make the count negative + __ jmp(L_copy_32_bytes); + + // Copy trailing qwords + __ BIND(L_copy_8_bytes); + __ movq(rax, Address(end_from, qword_count, Address::times_8, 8)); + __ movq(Address(end_to, qword_count, Address::times_8, 8), rax); + __ incrementq(qword_count); + __ jcc(Assembler::notZero, L_copy_8_bytes); + + // Check for and copy trailing dword + __ BIND(L_copy_4_bytes); + __ testq(byte_count, 4); + __ jccb(Assembler::zero, L_copy_2_bytes); + __ movl(rax, Address(end_from, 8)); + __ movl(Address(end_to, 8), rax); + + __ addq(end_from, 4); + __ addq(end_to, 4); + + // Check for and copy trailing word + __ BIND(L_copy_2_bytes); + __ testq(byte_count, 2); + __ jccb(Assembler::zero, L_copy_byte); + __ movw(rax, Address(end_from, 8)); + __ movw(Address(end_to, 8), rax); + + __ addq(end_from, 2); + __ addq(end_to, 2); + + // Check for and copy trailing byte + __ BIND(L_copy_byte); + __ testq(byte_count, 1); + __ jccb(Assembler::zero, L_exit); + __ movb(rax, Address(end_from, 8)); + __ movb(Address(end_to, 8), rax); + + __ BIND(L_exit); + inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + // Copy in 32-bytes chunks + copy_32_bytes_forward(end_from, end_to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes); + __ jmp(L_copy_4_bytes); + + return start; + } + + // Arguments: + // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary + // ignored + // name - stub name string + // + // Inputs: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // + // If 'from' and/or 'to' are aligned on 4-, 2-, or 1-byte boundaries, + // we let the hardware handle it. The one to eight bytes within words, + // dwords or qwords that span cache line boundaries will still be loaded + // and stored atomically. + // + address generate_conjoint_byte_copy(bool aligned, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_copy_32_bytes, L_copy_8_bytes, L_copy_4_bytes, L_copy_2_bytes; + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register count = rdx; // elements count + const Register byte_count = rcx; + const Register qword_count = count; + + __ enter(); // required for proper stackwalking of RuntimeStub frame + assert_clean_int(c_rarg2, rax); // Make sure 'count' is clean int. + + byte_copy_entry = __ pc(); + BLOCK_COMMENT("Entry:"); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + + array_overlap_test(disjoint_byte_copy_entry, Address::times_1); + setup_arg_regs(); // from => rdi, to => rsi, count => rdx + // r9 and r10 may be used to save non-volatile registers + + // 'from', 'to' and 'count' are now valid + __ movq(byte_count, count); + __ shrq(count, 3); // count => qword_count + + // Copy from high to low addresses. + + // Check for and copy trailing byte + __ testq(byte_count, 1); + __ jcc(Assembler::zero, L_copy_2_bytes); + __ movb(rax, Address(from, byte_count, Address::times_1, -1)); + __ movb(Address(to, byte_count, Address::times_1, -1), rax); + __ decrementq(byte_count); // Adjust for possible trailing word + + // Check for and copy trailing word + __ BIND(L_copy_2_bytes); + __ testq(byte_count, 2); + __ jcc(Assembler::zero, L_copy_4_bytes); + __ movw(rax, Address(from, byte_count, Address::times_1, -2)); + __ movw(Address(to, byte_count, Address::times_1, -2), rax); + + // Check for and copy trailing dword + __ BIND(L_copy_4_bytes); + __ testq(byte_count, 4); + __ jcc(Assembler::zero, L_copy_32_bytes); + __ movl(rax, Address(from, qword_count, Address::times_8)); + __ movl(Address(to, qword_count, Address::times_8), rax); + __ jmp(L_copy_32_bytes); + + // Copy trailing qwords + __ BIND(L_copy_8_bytes); + __ movq(rax, Address(from, qword_count, Address::times_8, -8)); + __ movq(Address(to, qword_count, Address::times_8, -8), rax); + __ decrementq(qword_count); + __ jcc(Assembler::notZero, L_copy_8_bytes); + + inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + // Copy in 32-bytes chunks + copy_32_bytes_backward(from, to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes); + + inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + // Arguments: + // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary + // ignored + // name - stub name string + // + // Inputs: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // + // If 'from' and/or 'to' are aligned on 4- or 2-byte boundaries, we + // let the hardware handle it. The two or four words within dwords + // or qwords that span cache line boundaries will still be loaded + // and stored atomically. + // + // Side Effects: + // disjoint_short_copy_entry is set to the no-overlap entry point + // used by generate_conjoint_short_copy(). + // + address generate_disjoint_short_copy(bool aligned, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_copy_32_bytes, L_copy_8_bytes, L_copy_4_bytes,L_copy_2_bytes,L_exit; + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register count = rdx; // elements count + const Register word_count = rcx; + const Register qword_count = count; + const Register end_from = from; // source array end address + const Register end_to = to; // destination array end address + // End pointers are inclusive, and if count is not zero they point + // to the last unit copied: end_to[0] := end_from[0] + + __ enter(); // required for proper stackwalking of RuntimeStub frame + assert_clean_int(c_rarg2, rax); // Make sure 'count' is clean int. + + disjoint_short_copy_entry = __ pc(); + BLOCK_COMMENT("Entry:"); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + + setup_arg_regs(); // from => rdi, to => rsi, count => rdx + // r9 and r10 may be used to save non-volatile registers + + // 'from', 'to' and 'count' are now valid + __ movq(word_count, count); + __ shrq(count, 2); // count => qword_count + + // Copy from low to high addresses. Use 'to' as scratch. + __ leaq(end_from, Address(from, qword_count, Address::times_8, -8)); + __ leaq(end_to, Address(to, qword_count, Address::times_8, -8)); + __ negq(qword_count); + __ jmp(L_copy_32_bytes); + + // Copy trailing qwords + __ BIND(L_copy_8_bytes); + __ movq(rax, Address(end_from, qword_count, Address::times_8, 8)); + __ movq(Address(end_to, qword_count, Address::times_8, 8), rax); + __ incrementq(qword_count); + __ jcc(Assembler::notZero, L_copy_8_bytes); + + // Original 'dest' is trashed, so we can't use it as a + // base register for a possible trailing word copy + + // Check for and copy trailing dword + __ BIND(L_copy_4_bytes); + __ testq(word_count, 2); + __ jccb(Assembler::zero, L_copy_2_bytes); + __ movl(rax, Address(end_from, 8)); + __ movl(Address(end_to, 8), rax); + + __ addq(end_from, 4); + __ addq(end_to, 4); + + // Check for and copy trailing word + __ BIND(L_copy_2_bytes); + __ testq(word_count, 1); + __ jccb(Assembler::zero, L_exit); + __ movw(rax, Address(end_from, 8)); + __ movw(Address(end_to, 8), rax); + + __ BIND(L_exit); + inc_counter_np(SharedRuntime::_jshort_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + // Copy in 32-bytes chunks + copy_32_bytes_forward(end_from, end_to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes); + __ jmp(L_copy_4_bytes); + + return start; + } + + // Arguments: + // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary + // ignored + // name - stub name string + // + // Inputs: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // + // If 'from' and/or 'to' are aligned on 4- or 2-byte boundaries, we + // let the hardware handle it. The two or four words within dwords + // or qwords that span cache line boundaries will still be loaded + // and stored atomically. + // + address generate_conjoint_short_copy(bool aligned, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_copy_32_bytes, L_copy_8_bytes, L_copy_4_bytes; + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register count = rdx; // elements count + const Register word_count = rcx; + const Register qword_count = count; + + __ enter(); // required for proper stackwalking of RuntimeStub frame + assert_clean_int(c_rarg2, rax); // Make sure 'count' is clean int. + + short_copy_entry = __ pc(); + BLOCK_COMMENT("Entry:"); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + + array_overlap_test(disjoint_short_copy_entry, Address::times_2); + setup_arg_regs(); // from => rdi, to => rsi, count => rdx + // r9 and r10 may be used to save non-volatile registers + + // 'from', 'to' and 'count' are now valid + __ movq(word_count, count); + __ shrq(count, 2); // count => qword_count + + // Copy from high to low addresses. Use 'to' as scratch. + + // Check for and copy trailing word + __ testq(word_count, 1); + __ jccb(Assembler::zero, L_copy_4_bytes); + __ movw(rax, Address(from, word_count, Address::times_2, -2)); + __ movw(Address(to, word_count, Address::times_2, -2), rax); + + // Check for and copy trailing dword + __ BIND(L_copy_4_bytes); + __ testq(word_count, 2); + __ jcc(Assembler::zero, L_copy_32_bytes); + __ movl(rax, Address(from, qword_count, Address::times_8)); + __ movl(Address(to, qword_count, Address::times_8), rax); + __ jmp(L_copy_32_bytes); + + // Copy trailing qwords + __ BIND(L_copy_8_bytes); + __ movq(rax, Address(from, qword_count, Address::times_8, -8)); + __ movq(Address(to, qword_count, Address::times_8, -8), rax); + __ decrementq(qword_count); + __ jcc(Assembler::notZero, L_copy_8_bytes); + + inc_counter_np(SharedRuntime::_jshort_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + // Copy in 32-bytes chunks + copy_32_bytes_backward(from, to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes); + + inc_counter_np(SharedRuntime::_jshort_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + // Arguments: + // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary + // ignored + // name - stub name string + // + // Inputs: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // + // If 'from' and/or 'to' are aligned on 4-byte boundaries, we let + // the hardware handle it. The two dwords within qwords that span + // cache line boundaries will still be loaded and stored atomicly. + // + // Side Effects: + // disjoint_int_copy_entry is set to the no-overlap entry point + // used by generate_conjoint_int_copy(). + // + address generate_disjoint_int_copy(bool aligned, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_copy_32_bytes, L_copy_8_bytes, L_copy_4_bytes, L_exit; + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register count = rdx; // elements count + const Register dword_count = rcx; + const Register qword_count = count; + const Register end_from = from; // source array end address + const Register end_to = to; // destination array end address + // End pointers are inclusive, and if count is not zero they point + // to the last unit copied: end_to[0] := end_from[0] + + __ enter(); // required for proper stackwalking of RuntimeStub frame + assert_clean_int(c_rarg2, rax); // Make sure 'count' is clean int. + + disjoint_int_copy_entry = __ pc(); + BLOCK_COMMENT("Entry:"); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + + setup_arg_regs(); // from => rdi, to => rsi, count => rdx + // r9 and r10 may be used to save non-volatile registers + + // 'from', 'to' and 'count' are now valid + __ movq(dword_count, count); + __ shrq(count, 1); // count => qword_count + + // Copy from low to high addresses. Use 'to' as scratch. + __ leaq(end_from, Address(from, qword_count, Address::times_8, -8)); + __ leaq(end_to, Address(to, qword_count, Address::times_8, -8)); + __ negq(qword_count); + __ jmp(L_copy_32_bytes); + + // Copy trailing qwords + __ BIND(L_copy_8_bytes); + __ movq(rax, Address(end_from, qword_count, Address::times_8, 8)); + __ movq(Address(end_to, qword_count, Address::times_8, 8), rax); + __ incrementq(qword_count); + __ jcc(Assembler::notZero, L_copy_8_bytes); + + // Check for and copy trailing dword + __ BIND(L_copy_4_bytes); + __ testq(dword_count, 1); // Only byte test since the value is 0 or 1 + __ jccb(Assembler::zero, L_exit); + __ movl(rax, Address(end_from, 8)); + __ movl(Address(end_to, 8), rax); + + __ BIND(L_exit); + inc_counter_np(SharedRuntime::_jint_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + // Copy 32-bytes chunks + copy_32_bytes_forward(end_from, end_to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes); + __ jmp(L_copy_4_bytes); + + return start; + } + + // Arguments: + // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary + // ignored + // name - stub name string + // + // Inputs: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // + // If 'from' and/or 'to' are aligned on 4-byte boundaries, we let + // the hardware handle it. The two dwords within qwords that span + // cache line boundaries will still be loaded and stored atomicly. + // + address generate_conjoint_int_copy(bool aligned, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_copy_32_bytes, L_copy_8_bytes, L_copy_2_bytes; + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register count = rdx; // elements count + const Register dword_count = rcx; + const Register qword_count = count; + + __ enter(); // required for proper stackwalking of RuntimeStub frame + assert_clean_int(c_rarg2, rax); // Make sure 'count' is clean int. + + int_copy_entry = __ pc(); + BLOCK_COMMENT("Entry:"); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + + array_overlap_test(disjoint_int_copy_entry, Address::times_4); + setup_arg_regs(); // from => rdi, to => rsi, count => rdx + // r9 and r10 may be used to save non-volatile registers + + // 'from', 'to' and 'count' are now valid + __ movq(dword_count, count); + __ shrq(count, 1); // count => qword_count + + // Copy from high to low addresses. Use 'to' as scratch. + + // Check for and copy trailing dword + __ testq(dword_count, 1); + __ jcc(Assembler::zero, L_copy_32_bytes); + __ movl(rax, Address(from, dword_count, Address::times_4, -4)); + __ movl(Address(to, dword_count, Address::times_4, -4), rax); + __ jmp(L_copy_32_bytes); + + // Copy trailing qwords + __ BIND(L_copy_8_bytes); + __ movq(rax, Address(from, qword_count, Address::times_8, -8)); + __ movq(Address(to, qword_count, Address::times_8, -8), rax); + __ decrementq(qword_count); + __ jcc(Assembler::notZero, L_copy_8_bytes); + + inc_counter_np(SharedRuntime::_jint_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + // Copy in 32-bytes chunks + copy_32_bytes_backward(from, to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes); + + inc_counter_np(SharedRuntime::_jint_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + // Arguments: + // aligned - true => Input and output aligned on a HeapWord boundary == 8 bytes + // ignored + // is_oop - true => oop array, so generate store check code + // name - stub name string + // + // Inputs: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // + // Side Effects: + // disjoint_oop_copy_entry or disjoint_long_copy_entry is set to the + // no-overlap entry point used by generate_conjoint_long_oop_copy(). + // + address generate_disjoint_long_oop_copy(bool aligned, bool is_oop, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_copy_32_bytes, L_copy_8_bytes, L_exit; + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register qword_count = rdx; // elements count + const Register end_from = from; // source array end address + const Register end_to = rcx; // destination array end address + const Register saved_to = to; + // End pointers are inclusive, and if count is not zero they point + // to the last unit copied: end_to[0] := end_from[0] + + __ enter(); // required for proper stackwalking of RuntimeStub frame + // Save no-overlap entry point for generate_conjoint_long_oop_copy() + assert_clean_int(c_rarg2, rax); // Make sure 'count' is clean int. + + if (is_oop) { + disjoint_oop_copy_entry = __ pc(); + // no registers are destroyed by this call + gen_write_ref_array_pre_barrier(/* dest */ c_rarg1, /* count */ c_rarg2); + } else { + disjoint_long_copy_entry = __ pc(); + } + BLOCK_COMMENT("Entry:"); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + + setup_arg_regs(); // from => rdi, to => rsi, count => rdx + // r9 and r10 may be used to save non-volatile registers + + // 'from', 'to' and 'qword_count' are now valid + + // Copy from low to high addresses. Use 'to' as scratch. + __ leaq(end_from, Address(from, qword_count, Address::times_8, -8)); + __ leaq(end_to, Address(to, qword_count, Address::times_8, -8)); + __ negq(qword_count); + __ jmp(L_copy_32_bytes); + + // Copy trailing qwords + __ BIND(L_copy_8_bytes); + __ movq(rax, Address(end_from, qword_count, Address::times_8, 8)); + __ movq(Address(end_to, qword_count, Address::times_8, 8), rax); + __ incrementq(qword_count); + __ jcc(Assembler::notZero, L_copy_8_bytes); + + if (is_oop) { + __ jmp(L_exit); + } else { + inc_counter_np(SharedRuntime::_jlong_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + } + + // Copy 64-byte chunks + copy_32_bytes_forward(end_from, end_to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes); + + if (is_oop) { + __ BIND(L_exit); + gen_write_ref_array_post_barrier(saved_to, end_to, rax); + inc_counter_np(SharedRuntime::_oop_array_copy_ctr); + } else { + inc_counter_np(SharedRuntime::_jlong_array_copy_ctr); + } + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + // Arguments: + // aligned - true => Input and output aligned on a HeapWord boundary == 8 bytes + // ignored + // is_oop - true => oop array, so generate store check code + // name - stub name string + // + // Inputs: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // + address generate_conjoint_long_oop_copy(bool aligned, bool is_oop, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Label L_copy_32_bytes, L_copy_8_bytes, L_exit; + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register qword_count = rdx; // elements count + const Register saved_count = rcx; + + __ enter(); // required for proper stackwalking of RuntimeStub frame + assert_clean_int(c_rarg2, rax); // Make sure 'count' is clean int. + + address disjoint_copy_entry = NULL; + if (is_oop) { + disjoint_copy_entry = disjoint_oop_copy_entry; + oop_copy_entry = __ pc(); + } else { + disjoint_copy_entry = disjoint_long_copy_entry; + long_copy_entry = __ pc(); + } + BLOCK_COMMENT("Entry:"); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + + array_overlap_test(disjoint_copy_entry, Address::times_8); + setup_arg_regs(); // from => rdi, to => rsi, count => rdx + // r9 and r10 may be used to save non-volatile registers + + // 'from', 'to' and 'qword_count' are now valid + + if (is_oop) { + // Save to and count for store barrier + __ movq(saved_count, qword_count); + // No registers are destroyed by this call + gen_write_ref_array_pre_barrier(to, saved_count); + } + + // Copy from high to low addresses. Use rcx as scratch. + + __ jmp(L_copy_32_bytes); + + // Copy trailing qwords + __ BIND(L_copy_8_bytes); + __ movq(rax, Address(from, qword_count, Address::times_8, -8)); + __ movq(Address(to, qword_count, Address::times_8, -8), rax); + __ decrementq(qword_count); + __ jcc(Assembler::notZero, L_copy_8_bytes); + + if (is_oop) { + __ jmp(L_exit); + } else { + inc_counter_np(SharedRuntime::_jlong_array_copy_ctr); + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + } + + // Copy in 32-bytes chunks + copy_32_bytes_backward(from, to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes); + + if (is_oop) { + __ BIND(L_exit); + __ leaq(rcx, Address(to, saved_count, Address::times_8, -8)); + gen_write_ref_array_post_barrier(to, rcx, rax); + inc_counter_np(SharedRuntime::_oop_array_copy_ctr); + } else { + inc_counter_np(SharedRuntime::_jlong_array_copy_ctr); + } + restore_arg_regs(); + __ xorq(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + + // Helper for generating a dynamic type check. + // Smashes no registers. + void generate_type_check(Register sub_klass, + Register super_check_offset, + Register super_klass, + Label& L_success) { + assert_different_registers(sub_klass, super_check_offset, super_klass); + + BLOCK_COMMENT("type_check:"); + + Label L_miss; + + // a couple of useful fields in sub_klass: + int ss_offset = (klassOopDesc::header_size() * HeapWordSize + + Klass::secondary_supers_offset_in_bytes()); + int sc_offset = (klassOopDesc::header_size() * HeapWordSize + + Klass::secondary_super_cache_offset_in_bytes()); + Address secondary_supers_addr(sub_klass, ss_offset); + Address super_cache_addr( sub_klass, sc_offset); + + // if the pointers are equal, we are done (e.g., String[] elements) + __ cmpq(super_klass, sub_klass); + __ jcc(Assembler::equal, L_success); + + // check the supertype display: + Address super_check_addr(sub_klass, super_check_offset, Address::times_1, 0); + __ cmpq(super_klass, super_check_addr); // test the super type + __ jcc(Assembler::equal, L_success); + + // if it was a primary super, we can just fail immediately + __ cmpl(super_check_offset, sc_offset); + __ jcc(Assembler::notEqual, L_miss); + + // Now do a linear scan of the secondary super-klass chain. + // The repne_scan instruction uses fixed registers, which we must spill. + // (We need a couple more temps in any case.) + // This code is rarely used, so simplicity is a virtue here. + inc_counter_np(SharedRuntime::_partial_subtype_ctr); + { + __ pushq(rax); + __ pushq(rcx); + __ pushq(rdi); + assert_different_registers(sub_klass, super_klass, rax, rcx, rdi); + + __ movq(rdi, secondary_supers_addr); + // Load the array length. + __ movl(rcx, Address(rdi, arrayOopDesc::length_offset_in_bytes())); + // Skip to start of data. + __ addq(rdi, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); + // Scan rcx words at [rdi] for occurance of rax + // Set NZ/Z based on last compare + __ movq(rax, super_klass); + __ repne_scan(); + + // Unspill the temp. registers: + __ popq(rdi); + __ popq(rcx); + __ popq(rax); + + __ jcc(Assembler::notEqual, L_miss); + } + + // Success. Cache the super we found and proceed in triumph. + __ movq(super_cache_addr, super_klass); // note: rax is dead + __ jmp(L_success); + + // Fall through on failure! + __ BIND(L_miss); + } + + // + // Generate checkcasting array copy stub + // + // Input: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // c_rarg3 - size_t ckoff (super_check_offset) + // not Win64 + // c_rarg4 - oop ckval (super_klass) + // Win64 + // rsp+40 - oop ckval (super_klass) + // + // Output: + // rax == 0 - success + // rax == -1^K - failure, where K is partial transfer count + // + address generate_checkcast_copy(const char *name) { + + Label L_load_element, L_store_element, L_do_card_marks, L_done; + + // Input registers (after setup_arg_regs) + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register length = rdx; // elements count + const Register ckoff = rcx; // super_check_offset + const Register ckval = r8; // super_klass + + // Registers used as temps (r13, r14 are save-on-entry) + const Register end_from = from; // source array end address + const Register end_to = r13; // destination array end address + const Register count = rdx; // -(count_remaining) + const Register r14_length = r14; // saved copy of length + // End pointers are inclusive, and if length is not zero they point + // to the last unit copied: end_to[0] := end_from[0] + + const Register rax_oop = rax; // actual oop copied + const Register r11_klass = r11; // oop._klass + + //--------------------------------------------------------------- + // Assembler stub will be used for this call to arraycopy + // if the two arrays are subtypes of Object[] but the + // destination array type is not equal to or a supertype + // of the source type. Each element must be separately + // checked. + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + __ enter(); // required for proper stackwalking of RuntimeStub frame + + checkcast_copy_entry = __ pc(); + BLOCK_COMMENT("Entry:"); + +#ifdef ASSERT + // caller guarantees that the arrays really are different + // otherwise, we would have to make conjoint checks + { Label L; + array_overlap_test(L, Address::times_8); + __ stop("checkcast_copy within a single array"); + __ bind(L); + } +#endif //ASSERT + + // allocate spill slots for r13, r14 + enum { + saved_r13_offset, + saved_r14_offset, + saved_rbp_offset, + saved_rip_offset, + saved_rarg0_offset + }; + __ subq(rsp, saved_rbp_offset * wordSize); + __ movq(Address(rsp, saved_r13_offset * wordSize), r13); + __ movq(Address(rsp, saved_r14_offset * wordSize), r14); + setup_arg_regs(4); // from => rdi, to => rsi, length => rdx + // ckoff => rcx, ckval => r8 + // r9 and r10 may be used to save non-volatile registers +#ifdef _WIN64 + // last argument (#4) is on stack on Win64 + const int ckval_offset = saved_rarg0_offset + 4; + __ movq(ckval, Address(rsp, ckval_offset * wordSize)); +#endif + + // check that int operands are properly extended to size_t + assert_clean_int(length, rax); + assert_clean_int(ckoff, rax); + +#ifdef ASSERT + BLOCK_COMMENT("assert consistent ckoff/ckval"); + // The ckoff and ckval must be mutually consistent, + // even though caller generates both. + { Label L; + int sco_offset = (klassOopDesc::header_size() * HeapWordSize + + Klass::super_check_offset_offset_in_bytes()); + __ cmpl(ckoff, Address(ckval, sco_offset)); + __ jcc(Assembler::equal, L); + __ stop("super_check_offset inconsistent"); + __ bind(L); + } +#endif //ASSERT + + // Loop-invariant addresses. They are exclusive end pointers. + Address end_from_addr(from, length, Address::times_8, 0); + Address end_to_addr(to, length, Address::times_8, 0); + // Loop-variant addresses. They assume post-incremented count < 0. + Address from_element_addr(end_from, count, Address::times_8, 0); + Address to_element_addr(end_to, count, Address::times_8, 0); + Address oop_klass_addr(rax_oop, oopDesc::klass_offset_in_bytes()); + + gen_write_ref_array_pre_barrier(to, count); + + // Copy from low to high addresses, indexed from the end of each array. + __ leaq(end_from, end_from_addr); + __ leaq(end_to, end_to_addr); + __ movq(r14_length, length); // save a copy of the length + assert(length == count, ""); // else fix next line: + __ negq(count); // negate and test the length + __ jcc(Assembler::notZero, L_load_element); + + // Empty array: Nothing to do. + __ xorq(rax, rax); // return 0 on (trivial) success + __ jmp(L_done); + + // ======== begin loop ======== + // (Loop is rotated; its entry is L_load_element.) + // Loop control: + // for (count = -count; count != 0; count++) + // Base pointers src, dst are biased by 8*(count-1),to last element. + __ align(16); + + __ BIND(L_store_element); + __ movq(to_element_addr, rax_oop); // store the oop + __ incrementq(count); // increment the count toward zero + __ jcc(Assembler::zero, L_do_card_marks); + + // ======== loop entry is here ======== + __ BIND(L_load_element); + __ movq(rax_oop, from_element_addr); // load the oop + __ testq(rax_oop, rax_oop); + __ jcc(Assembler::zero, L_store_element); + + __ movq(r11_klass, oop_klass_addr); // query the object klass + generate_type_check(r11_klass, ckoff, ckval, L_store_element); + // ======== end loop ======== + + // It was a real error; we must depend on the caller to finish the job. + // Register rdx = -1 * number of *remaining* oops, r14 = *total* oops. + // Emit GC store barriers for the oops we have copied (r14 + rdx), + // and report their number to the caller. + assert_different_registers(rax, r14_length, count, to, end_to, rcx); + __ leaq(end_to, to_element_addr); + gen_write_ref_array_post_barrier(to, end_to, rcx); + __ movq(rax, r14_length); // original oops + __ addq(rax, count); // K = (original - remaining) oops + __ notq(rax); // report (-1^K) to caller + __ jmp(L_done); + + // Come here on success only. + __ BIND(L_do_card_marks); + __ addq(end_to, -wordSize); // make an inclusive end pointer + gen_write_ref_array_post_barrier(to, end_to, rcx); + __ xorq(rax, rax); // return 0 on success + + // Common exit point (success or failure). + __ BIND(L_done); + __ movq(r13, Address(rsp, saved_r13_offset * wordSize)); + __ movq(r14, Address(rsp, saved_r14_offset * wordSize)); + inc_counter_np(SharedRuntime::_checkcast_array_copy_ctr); + restore_arg_regs(); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + // + // Generate 'unsafe' array copy stub + // Though just as safe as the other stubs, it takes an unscaled + // size_t argument instead of an element count. + // + // Input: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - byte count, treated as ssize_t, can be zero + // + // Examines the alignment of the operands and dispatches + // to a long, int, short, or byte copy loop. + // + address generate_unsafe_copy(const char *name) { + + Label L_long_aligned, L_int_aligned, L_short_aligned; + + // Input registers (before setup_arg_regs) + const Register from = c_rarg0; // source array address + const Register to = c_rarg1; // destination array address + const Register size = c_rarg2; // byte count (size_t) + + // Register used as a temp + const Register bits = rax; // test copy of low bits + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + __ enter(); // required for proper stackwalking of RuntimeStub frame + + // bump this on entry, not on exit: + inc_counter_np(SharedRuntime::_unsafe_array_copy_ctr); + + __ movq(bits, from); + __ orq(bits, to); + __ orq(bits, size); + + __ testb(bits, BytesPerLong-1); + __ jccb(Assembler::zero, L_long_aligned); + + __ testb(bits, BytesPerInt-1); + __ jccb(Assembler::zero, L_int_aligned); + + __ testb(bits, BytesPerShort-1); + __ jump_cc(Assembler::notZero, RuntimeAddress(byte_copy_entry)); + + __ BIND(L_short_aligned); + __ shrq(size, LogBytesPerShort); // size => short_count + __ jump(RuntimeAddress(short_copy_entry)); + + __ BIND(L_int_aligned); + __ shrq(size, LogBytesPerInt); // size => int_count + __ jump(RuntimeAddress(int_copy_entry)); + + __ BIND(L_long_aligned); + __ shrq(size, LogBytesPerLong); // size => qword_count + __ jump(RuntimeAddress(long_copy_entry)); + + return start; + } + + // Perform range checks on the proposed arraycopy. + // Kills temp, but nothing else. + // Also, clean the sign bits of src_pos and dst_pos. + void arraycopy_range_checks(Register src, // source array oop (c_rarg0) + Register src_pos, // source position (c_rarg1) + Register dst, // destination array oo (c_rarg2) + Register dst_pos, // destination position (c_rarg3) + Register length, + Register temp, + Label& L_failed) { + BLOCK_COMMENT("arraycopy_range_checks:"); + + // if (src_pos + length > arrayOop(src)->length()) FAIL; + __ movl(temp, length); + __ addl(temp, src_pos); // src_pos + length + __ cmpl(temp, Address(src, arrayOopDesc::length_offset_in_bytes())); + __ jcc(Assembler::above, L_failed); + + // if (dst_pos + length > arrayOop(dst)->length()) FAIL; + __ movl(temp, length); + __ addl(temp, dst_pos); // dst_pos + length + __ cmpl(temp, Address(dst, arrayOopDesc::length_offset_in_bytes())); + __ jcc(Assembler::above, L_failed); + + // Have to clean up high 32-bits of 'src_pos' and 'dst_pos'. + // Move with sign extension can be used since they are positive. + __ movslq(src_pos, src_pos); + __ movslq(dst_pos, dst_pos); + + BLOCK_COMMENT("arraycopy_range_checks done"); + } + + // + // Generate generic array copy stubs + // + // Input: + // c_rarg0 - src oop + // c_rarg1 - src_pos (32-bits) + // c_rarg2 - dst oop + // c_rarg3 - dst_pos (32-bits) + // not Win64 + // c_rarg4 - element count (32-bits) + // Win64 + // rsp+40 - element count (32-bits) + // + // Output: + // rax == 0 - success + // rax == -1^K - failure, where K is partial transfer count + // + address generate_generic_copy(const char *name) { + + Label L_failed, L_failed_0, L_objArray; + Label L_copy_bytes, L_copy_shorts, L_copy_ints, L_copy_longs; + + // Input registers + const Register src = c_rarg0; // source array oop + const Register src_pos = c_rarg1; // source position + const Register dst = c_rarg2; // destination array oop + const Register dst_pos = c_rarg3; // destination position + // elements count is on stack on Win64 +#ifdef _WIN64 +#define C_RARG4 Address(rsp, 6 * wordSize) +#else +#define C_RARG4 c_rarg4 +#endif + + { int modulus = CodeEntryAlignment; + int target = modulus - 5; // 5 = sizeof jmp(L_failed) + int advance = target - (__ offset() % modulus); + if (advance < 0) advance += modulus; + if (advance > 0) __ nop(advance); + } + StubCodeMark mark(this, "StubRoutines", name); + + // Short-hop target to L_failed. Makes for denser prologue code. + __ BIND(L_failed_0); + __ jmp(L_failed); + assert(__ offset() % CodeEntryAlignment == 0, "no further alignment needed"); + + __ align(CodeEntryAlignment); + address start = __ pc(); + + __ enter(); // required for proper stackwalking of RuntimeStub frame + + // bump this on entry, not on exit: + inc_counter_np(SharedRuntime::_generic_array_copy_ctr); + + //----------------------------------------------------------------------- + // Assembler stub will be used for this call to arraycopy + // if the following conditions are met: + // + // (1) src and dst must not be null. + // (2) src_pos must not be negative. + // (3) dst_pos must not be negative. + // (4) length must not be negative. + // (5) src klass and dst klass should be the same and not NULL. + // (6) src and dst should be arrays. + // (7) src_pos + length must not exceed length of src. + // (8) dst_pos + length must not exceed length of dst. + // + + // if (src == NULL) return -1; + __ testq(src, src); // src oop + size_t j1off = __ offset(); + __ jccb(Assembler::zero, L_failed_0); + + // if (src_pos < 0) return -1; + __ testl(src_pos, src_pos); // src_pos (32-bits) + __ jccb(Assembler::negative, L_failed_0); + + // if (dst == NULL) return -1; + __ testq(dst, dst); // dst oop + __ jccb(Assembler::zero, L_failed_0); + + // if (dst_pos < 0) return -1; + __ testl(dst_pos, dst_pos); // dst_pos (32-bits) + size_t j4off = __ offset(); + __ jccb(Assembler::negative, L_failed_0); + + // The first four tests are very dense code, + // but not quite dense enough to put four + // jumps in a 16-byte instruction fetch buffer. + // That's good, because some branch predicters + // do not like jumps so close together. + // Make sure of this. + guarantee(((j1off ^ j4off) & ~15) != 0, "I$ line of 1st & 4th jumps"); + + // registers used as temp + const Register r11_length = r11; // elements count to copy + const Register r10_src_klass = r10; // array klass + + // if (length < 0) return -1; + __ movl(r11_length, C_RARG4); // length (elements count, 32-bits value) + __ testl(r11_length, r11_length); + __ jccb(Assembler::negative, L_failed_0); + + Address src_klass_addr(src, oopDesc::klass_offset_in_bytes()); + Address dst_klass_addr(dst, oopDesc::klass_offset_in_bytes()); + __ movq(r10_src_klass, src_klass_addr); +#ifdef ASSERT + // assert(src->klass() != NULL); + BLOCK_COMMENT("assert klasses not null"); + { Label L1, L2; + __ testq(r10_src_klass, r10_src_klass); + __ jcc(Assembler::notZero, L2); // it is broken if klass is NULL + __ bind(L1); + __ stop("broken null klass"); + __ bind(L2); + __ cmpq(dst_klass_addr, 0); + __ jcc(Assembler::equal, L1); // this would be broken also + BLOCK_COMMENT("assert done"); + } +#endif + + // Load layout helper (32-bits) + // + // |array_tag| | header_size | element_type | |log2_element_size| + // 32 30 24 16 8 2 0 + // + // array_tag: typeArray = 0x3, objArray = 0x2, non-array = 0x0 + // + + int lh_offset = klassOopDesc::header_size() * HeapWordSize + + Klass::layout_helper_offset_in_bytes(); + + const Register rax_lh = rax; // layout helper + + __ movl(rax_lh, Address(r10_src_klass, lh_offset)); + + // Handle objArrays completely differently... + jint objArray_lh = Klass::array_layout_helper(T_OBJECT); + __ cmpl(rax_lh, objArray_lh); + __ jcc(Assembler::equal, L_objArray); + + // if (src->klass() != dst->klass()) return -1; + __ cmpq(r10_src_klass, dst_klass_addr); + __ jcc(Assembler::notEqual, L_failed); + + // if (!src->is_Array()) return -1; + __ cmpl(rax_lh, Klass::_lh_neutral_value); + __ jcc(Assembler::greaterEqual, L_failed); + + // At this point, it is known to be a typeArray (array_tag 0x3). +#ifdef ASSERT + { Label L; + __ cmpl(rax_lh, (Klass::_lh_array_tag_type_value << Klass::_lh_array_tag_shift)); + __ jcc(Assembler::greaterEqual, L); + __ stop("must be a primitive array"); + __ bind(L); + } +#endif + + arraycopy_range_checks(src, src_pos, dst, dst_pos, r11_length, + r10, L_failed); + + // typeArrayKlass + // + // src_addr = (src + array_header_in_bytes()) + (src_pos << log2elemsize); + // dst_addr = (dst + array_header_in_bytes()) + (dst_pos << log2elemsize); + // + + const Register r10_offset = r10; // array offset + const Register rax_elsize = rax_lh; // element size + + __ movl(r10_offset, rax_lh); + __ shrl(r10_offset, Klass::_lh_header_size_shift); + __ andq(r10_offset, Klass::_lh_header_size_mask); // array_offset + __ addq(src, r10_offset); // src array offset + __ addq(dst, r10_offset); // dst array offset + BLOCK_COMMENT("choose copy loop based on element size"); + __ andl(rax_lh, Klass::_lh_log2_element_size_mask); // rax_lh -> rax_elsize + + // next registers should be set before the jump to corresponding stub + const Register from = c_rarg0; // source array address + const Register to = c_rarg1; // destination array address + const Register count = c_rarg2; // elements count + + // 'from', 'to', 'count' registers should be set in such order + // since they are the same as 'src', 'src_pos', 'dst'. + + __ BIND(L_copy_bytes); + __ cmpl(rax_elsize, 0); + __ jccb(Assembler::notEqual, L_copy_shorts); + __ leaq(from, Address(src, src_pos, Address::times_1, 0));// src_addr + __ leaq(to, Address(dst, dst_pos, Address::times_1, 0));// dst_addr + __ movslq(count, r11_length); // length + __ jump(RuntimeAddress(byte_copy_entry)); + + __ BIND(L_copy_shorts); + __ cmpl(rax_elsize, LogBytesPerShort); + __ jccb(Assembler::notEqual, L_copy_ints); + __ leaq(from, Address(src, src_pos, Address::times_2, 0));// src_addr + __ leaq(to, Address(dst, dst_pos, Address::times_2, 0));// dst_addr + __ movslq(count, r11_length); // length + __ jump(RuntimeAddress(short_copy_entry)); + + __ BIND(L_copy_ints); + __ cmpl(rax_elsize, LogBytesPerInt); + __ jccb(Assembler::notEqual, L_copy_longs); + __ leaq(from, Address(src, src_pos, Address::times_4, 0));// src_addr + __ leaq(to, Address(dst, dst_pos, Address::times_4, 0));// dst_addr + __ movslq(count, r11_length); // length + __ jump(RuntimeAddress(int_copy_entry)); + + __ BIND(L_copy_longs); +#ifdef ASSERT + { Label L; + __ cmpl(rax_elsize, LogBytesPerLong); + __ jcc(Assembler::equal, L); + __ stop("must be long copy, but elsize is wrong"); + __ bind(L); + } +#endif + __ leaq(from, Address(src, src_pos, Address::times_8, 0));// src_addr + __ leaq(to, Address(dst, dst_pos, Address::times_8, 0));// dst_addr + __ movslq(count, r11_length); // length + __ jump(RuntimeAddress(long_copy_entry)); + + // objArrayKlass + __ BIND(L_objArray); + // live at this point: r10_src_klass, src[_pos], dst[_pos] + + Label L_plain_copy, L_checkcast_copy; + // test array classes for subtyping + __ cmpq(r10_src_klass, dst_klass_addr); // usual case is exact equality + __ jcc(Assembler::notEqual, L_checkcast_copy); + + // Identically typed arrays can be copied without element-wise checks. + arraycopy_range_checks(src, src_pos, dst, dst_pos, r11_length, + r10, L_failed); + + __ leaq(from, Address(src, src_pos, Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_OBJECT))); // src_addr + __ leaq(to, Address(dst, dst_pos, Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_OBJECT))); // dst_addr + __ movslq(count, r11_length); // length + __ BIND(L_plain_copy); + __ jump(RuntimeAddress(oop_copy_entry)); + + __ BIND(L_checkcast_copy); + // live at this point: r10_src_klass, !r11_length + { + // assert(r11_length == C_RARG4); // will reload from here + Register r11_dst_klass = r11; + __ movq(r11_dst_klass, dst_klass_addr); + + // Before looking at dst.length, make sure dst is also an objArray. + __ cmpl(Address(r11_dst_klass, lh_offset), objArray_lh); + __ jcc(Assembler::notEqual, L_failed); + + // It is safe to examine both src.length and dst.length. +#ifndef _WIN64 + arraycopy_range_checks(src, src_pos, dst, dst_pos, C_RARG4, + rax, L_failed); +#else + __ movl(r11_length, C_RARG4); // reload + arraycopy_range_checks(src, src_pos, dst, dst_pos, r11_length, + rax, L_failed); + __ movl(r11_dst_klass, dst_klass_addr); // reload +#endif + + // Marshal the base address arguments now, freeing registers. + __ leaq(from, Address(src, src_pos, Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_OBJECT))); + __ leaq(to, Address(dst, dst_pos, Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_OBJECT))); + __ movl(count, C_RARG4); // length (reloaded) + Register sco_temp = c_rarg3; // this register is free now + assert_different_registers(from, to, count, sco_temp, + r11_dst_klass, r10_src_klass); + assert_clean_int(count, sco_temp); + + // Generate the type check. + int sco_offset = (klassOopDesc::header_size() * HeapWordSize + + Klass::super_check_offset_offset_in_bytes()); + __ movl(sco_temp, Address(r11_dst_klass, sco_offset)); + assert_clean_int(sco_temp, rax); + generate_type_check(r10_src_klass, sco_temp, r11_dst_klass, L_plain_copy); + + // Fetch destination element klass from the objArrayKlass header. + int ek_offset = (klassOopDesc::header_size() * HeapWordSize + + objArrayKlass::element_klass_offset_in_bytes()); + __ movq(r11_dst_klass, Address(r11_dst_klass, ek_offset)); + __ movl(sco_temp, Address(r11_dst_klass, sco_offset)); + assert_clean_int(sco_temp, rax); + + // the checkcast_copy loop needs two extra arguments: + assert(c_rarg3 == sco_temp, "#3 already in place"); + __ movq(C_RARG4, r11_dst_klass); // dst.klass.element_klass + __ jump(RuntimeAddress(checkcast_copy_entry)); + } + + __ BIND(L_failed); + __ xorq(rax, rax); + __ notq(rax); // return -1 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + +#undef length_arg + + void generate_arraycopy_stubs() { + // Call the conjoint generation methods immediately after + // the disjoint ones so that short branches from the former + // to the latter can be generated. + StubRoutines::_jbyte_disjoint_arraycopy = generate_disjoint_byte_copy(false, "jbyte_disjoint_arraycopy"); + StubRoutines::_jbyte_arraycopy = generate_conjoint_byte_copy(false, "jbyte_arraycopy"); + + StubRoutines::_jshort_disjoint_arraycopy = generate_disjoint_short_copy(false, "jshort_disjoint_arraycopy"); + StubRoutines::_jshort_arraycopy = generate_conjoint_short_copy(false, "jshort_arraycopy"); + + StubRoutines::_jint_disjoint_arraycopy = generate_disjoint_int_copy(false, "jint_disjoint_arraycopy"); + StubRoutines::_jint_arraycopy = generate_conjoint_int_copy(false, "jint_arraycopy"); + + StubRoutines::_jlong_disjoint_arraycopy = generate_disjoint_long_oop_copy(false, false, "jlong_disjoint_arraycopy"); + StubRoutines::_jlong_arraycopy = generate_conjoint_long_oop_copy(false, false, "jlong_arraycopy"); + + StubRoutines::_oop_disjoint_arraycopy = generate_disjoint_long_oop_copy(false, true, "oop_disjoint_arraycopy"); + StubRoutines::_oop_arraycopy = generate_conjoint_long_oop_copy(false, true, "oop_arraycopy"); + + StubRoutines::_checkcast_arraycopy = generate_checkcast_copy("checkcast_arraycopy"); + StubRoutines::_unsafe_arraycopy = generate_unsafe_copy("unsafe_arraycopy"); + StubRoutines::_generic_arraycopy = generate_generic_copy("generic_arraycopy"); + + // We don't generate specialized code for HeapWord-aligned source + // arrays, so just use the code we've already generated + StubRoutines::_arrayof_jbyte_disjoint_arraycopy = StubRoutines::_jbyte_disjoint_arraycopy; + StubRoutines::_arrayof_jbyte_arraycopy = StubRoutines::_jbyte_arraycopy; + + StubRoutines::_arrayof_jshort_disjoint_arraycopy = StubRoutines::_jshort_disjoint_arraycopy; + StubRoutines::_arrayof_jshort_arraycopy = StubRoutines::_jshort_arraycopy; + + StubRoutines::_arrayof_jint_disjoint_arraycopy = StubRoutines::_jint_disjoint_arraycopy; + StubRoutines::_arrayof_jint_arraycopy = StubRoutines::_jint_arraycopy; + + StubRoutines::_arrayof_jlong_disjoint_arraycopy = StubRoutines::_jlong_disjoint_arraycopy; + StubRoutines::_arrayof_jlong_arraycopy = StubRoutines::_jlong_arraycopy; + + StubRoutines::_arrayof_oop_disjoint_arraycopy = StubRoutines::_oop_disjoint_arraycopy; + StubRoutines::_arrayof_oop_arraycopy = StubRoutines::_oop_arraycopy; + } + +#undef __ +#define __ masm-> + + // Continuation point for throwing of implicit exceptions that are + // not handled in the current activation. Fabricates an exception + // oop and initiates normal exception dispatching in this + // frame. Since we need to preserve callee-saved values (currently + // only for C2, but done for C1 as well) we need a callee-saved oop + // map and therefore have to make these stubs into RuntimeStubs + // rather than BufferBlobs. If the compiler needs all registers to + // be preserved between the fault point and the exception handler + // then it must assume responsibility for that in + // AbstractCompiler::continuation_for_implicit_null_exception or + // continuation_for_implicit_division_by_zero_exception. All other + // implicit exceptions (e.g., NullPointerException or + // AbstractMethodError on entry) are either at call sites or + // otherwise assume that stack unwinding will be initiated, so + // caller saved registers were assumed volatile in the compiler. + address generate_throw_exception(const char* name, + address runtime_entry, + bool restore_saved_exception_pc) { + // Information about frame layout at time of blocking runtime call. + // Note that we only have to preserve callee-saved registers since + // the compilers are responsible for supplying a continuation point + // if they expect all registers to be preserved. + enum layout { + rbp_off = frame::arg_reg_save_area_bytes/BytesPerInt, + rbp_off2, + return_off, + return_off2, + framesize // inclusive of return address + }; + + int insts_size = 512; + int locs_size = 64; + + CodeBuffer code(name, insts_size, locs_size); + OopMapSet* oop_maps = new OopMapSet(); + MacroAssembler* masm = new MacroAssembler(&code); + + address start = __ pc(); + + // This is an inlined and slightly modified version of call_VM + // which has the ability to fetch the return PC out of + // thread-local storage and also sets up last_Java_sp slightly + // differently than the real call_VM + if (restore_saved_exception_pc) { + __ movq(rax, + Address(r15_thread, + in_bytes(JavaThread::saved_exception_pc_offset()))); + __ pushq(rax); + } + + __ enter(); // required for proper stackwalking of RuntimeStub frame + + assert(is_even(framesize/2), "sp not 16-byte aligned"); + + // return address and rbp are already in place + __ subq(rsp, (framesize-4) << LogBytesPerInt); // prolog + + int frame_complete = __ pc() - start; + + // Set up last_Java_sp and last_Java_fp + __ set_last_Java_frame(rsp, rbp, NULL); + + // Call runtime + __ movq(c_rarg0, r15_thread); + BLOCK_COMMENT("call runtime_entry"); + __ call(RuntimeAddress(runtime_entry)); + + // Generate oop map + OopMap* map = new OopMap(framesize, 0); + + oop_maps->add_gc_map(__ pc() - start, map); + + __ reset_last_Java_frame(true, false); + + __ leave(); // required for proper stackwalking of RuntimeStub frame + + // check for pending exceptions +#ifdef ASSERT + Label L; + __ cmpq(Address(r15_thread, Thread::pending_exception_offset()), + (int) NULL); + __ jcc(Assembler::notEqual, L); + __ should_not_reach_here(); + __ bind(L); +#endif // ASSERT + __ jump(RuntimeAddress(StubRoutines::forward_exception_entry())); + + + // codeBlob framesize is in words (not VMRegImpl::slot_size) + RuntimeStub* stub = + RuntimeStub::new_runtime_stub(name, + &code, + frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, false); + return stub->entry_point(); + } + + // Initialization + void generate_initial() { + // Generates all stubs and initializes the entry points + + // This platform-specific stub is needed by generate_call_stub() + StubRoutines::amd64::_mxcsr_std = generate_fp_mask("mxcsr_std", 0x0000000000001F80); + + // entry points that exist in all platforms Note: This is code + // that could be shared among different platforms - however the + // benefit seems to be smaller than the disadvantage of having a + // much more complicated generator structure. See also comment in + // stubRoutines.hpp. + + StubRoutines::_forward_exception_entry = generate_forward_exception(); + + StubRoutines::_call_stub_entry = + generate_call_stub(StubRoutines::_call_stub_return_address); + + // is referenced by megamorphic call + StubRoutines::_catch_exception_entry = generate_catch_exception(); + + // atomic calls + StubRoutines::_atomic_xchg_entry = generate_atomic_xchg(); + StubRoutines::_atomic_xchg_ptr_entry = generate_atomic_xchg_ptr(); + StubRoutines::_atomic_cmpxchg_entry = generate_atomic_cmpxchg(); + StubRoutines::_atomic_cmpxchg_long_entry = generate_atomic_cmpxchg_long(); + StubRoutines::_atomic_add_entry = generate_atomic_add(); + StubRoutines::_atomic_add_ptr_entry = generate_atomic_add_ptr(); + StubRoutines::_fence_entry = generate_orderaccess_fence(); + + StubRoutines::_handler_for_unsafe_access_entry = + generate_handler_for_unsafe_access(); + + // platform dependent + StubRoutines::amd64::_get_previous_fp_entry = generate_get_previous_fp(); + + StubRoutines::amd64::_verify_mxcsr_entry = generate_verify_mxcsr(); + } + + void generate_all() { + // Generates all stubs and initializes the entry points + + // These entry points require SharedInfo::stack0 to be set up in + // non-core builds and need to be relocatable, so they each + // fabricate a RuntimeStub internally. + StubRoutines::_throw_AbstractMethodError_entry = + generate_throw_exception("AbstractMethodError throw_exception", + CAST_FROM_FN_PTR(address, + SharedRuntime:: + throw_AbstractMethodError), + false); + + StubRoutines::_throw_ArithmeticException_entry = + generate_throw_exception("ArithmeticException throw_exception", + CAST_FROM_FN_PTR(address, + SharedRuntime:: + throw_ArithmeticException), + true); + + StubRoutines::_throw_NullPointerException_entry = + generate_throw_exception("NullPointerException throw_exception", + CAST_FROM_FN_PTR(address, + SharedRuntime:: + throw_NullPointerException), + true); + + StubRoutines::_throw_NullPointerException_at_call_entry = + generate_throw_exception("NullPointerException at call throw_exception", + CAST_FROM_FN_PTR(address, + SharedRuntime:: + throw_NullPointerException_at_call), + false); + + StubRoutines::_throw_StackOverflowError_entry = + generate_throw_exception("StackOverflowError throw_exception", + CAST_FROM_FN_PTR(address, + SharedRuntime:: + throw_StackOverflowError), + false); + + // entry points that are platform specific + StubRoutines::amd64::_f2i_fixup = generate_f2i_fixup(); + StubRoutines::amd64::_f2l_fixup = generate_f2l_fixup(); + StubRoutines::amd64::_d2i_fixup = generate_d2i_fixup(); + StubRoutines::amd64::_d2l_fixup = generate_d2l_fixup(); + + StubRoutines::amd64::_float_sign_mask = generate_fp_mask("float_sign_mask", 0x7FFFFFFF7FFFFFFF); + StubRoutines::amd64::_float_sign_flip = generate_fp_mask("float_sign_flip", 0x8000000080000000); + StubRoutines::amd64::_double_sign_mask = generate_fp_mask("double_sign_mask", 0x7FFFFFFFFFFFFFFF); + StubRoutines::amd64::_double_sign_flip = generate_fp_mask("double_sign_flip", 0x8000000000000000); + + // support for verify_oop (must happen after universe_init) + StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop(); + + // arraycopy stubs used by compilers + generate_arraycopy_stubs(); + } + + public: + StubGenerator(CodeBuffer* code, bool all) : StubCodeGenerator(code) { + if (all) { + generate_all(); + } else { + generate_initial(); + } + } +}; // end class declaration + +address StubGenerator::disjoint_byte_copy_entry = NULL; +address StubGenerator::disjoint_short_copy_entry = NULL; +address StubGenerator::disjoint_int_copy_entry = NULL; +address StubGenerator::disjoint_long_copy_entry = NULL; +address StubGenerator::disjoint_oop_copy_entry = NULL; + +address StubGenerator::byte_copy_entry = NULL; +address StubGenerator::short_copy_entry = NULL; +address StubGenerator::int_copy_entry = NULL; +address StubGenerator::long_copy_entry = NULL; +address StubGenerator::oop_copy_entry = NULL; + +address StubGenerator::checkcast_copy_entry = NULL; + +void StubGenerator_generate(CodeBuffer* code, bool all) { + StubGenerator g(code, all); +} diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.cpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.cpp new file mode 100644 index 00000000000..285eef97ef0 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.cpp @@ -0,0 +1,33 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubRoutines_x86_32.cpp.incl" + +// Implementation of the platform-specific part of StubRoutines - for +// a description of how to extend it, see the stubRoutines.hpp file. + +address StubRoutines::i486::_verify_mxcsr_entry = NULL; +address StubRoutines::i486::_verify_fpu_cntrl_wrd_entry= NULL; +address StubRoutines::i486::_call_stub_compiled_return = NULL; diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp new file mode 100644 index 00000000000..8f79b51ecb2 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp @@ -0,0 +1,57 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds the platform specific parts of the StubRoutines +// definition. See stubRoutines.hpp for a description on how to +// extend it. + +enum platform_dependent_constants { + code_size1 = 9000, // simply increase if too small (assembler will crash if too small) + code_size2 = 22000 // simply increase if too small (assembler will crash if too small) +}; + +class i486 { + friend class StubGenerator; + friend class VMStructs; + + private: + // If we call compiled code directly from the call stub we will + // need to adjust the return back to the call stub to a specialized + // piece of code that can handle compiled results and cleaning the fpu + // stack. The variable holds that location. + static address _call_stub_compiled_return; + static address _verify_mxcsr_entry; + static address _verify_fpu_cntrl_wrd_entry; + static jint _mxcsr_std; + + public: + static address verify_mxcsr_entry() { return _verify_mxcsr_entry; } + static address verify_fpu_cntrl_wrd_entry() { return _verify_fpu_cntrl_wrd_entry; } + + static address get_call_stub_compiled_return() { return _call_stub_compiled_return; } + static void set_call_stub_compiled_return(address ret) { _call_stub_compiled_return = ret; } +}; + + static bool returns_to_call_stub(address return_pc) { return (return_pc == _call_stub_return_address) || + return_pc == i486::get_call_stub_compiled_return(); } diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.cpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.cpp new file mode 100644 index 00000000000..7979f3f0b99 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubRoutines_x86_64.cpp.incl" + +// Implementation of the platform-specific part of StubRoutines - for +// a description of how to extend it, see the stubRoutines.hpp file. + +address StubRoutines::amd64::_get_previous_fp_entry = NULL; + +address StubRoutines::amd64::_verify_mxcsr_entry = NULL; + +address StubRoutines::amd64::_f2i_fixup = NULL; +address StubRoutines::amd64::_f2l_fixup = NULL; +address StubRoutines::amd64::_d2i_fixup = NULL; +address StubRoutines::amd64::_d2l_fixup = NULL; +address StubRoutines::amd64::_float_sign_mask = NULL; +address StubRoutines::amd64::_float_sign_flip = NULL; +address StubRoutines::amd64::_double_sign_mask = NULL; +address StubRoutines::amd64::_double_sign_flip = NULL; +address StubRoutines::amd64::_mxcsr_std = NULL; diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp new file mode 100644 index 00000000000..da7e8fd56a5 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp @@ -0,0 +1,113 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds the platform specific parts of the StubRoutines +// definition. See stubRoutines.hpp for a description on how to +// extend it. + +static bool returns_to_call_stub(address return_pc) { return return_pc == _call_stub_return_address; } + +enum platform_dependent_constants +{ + code_size1 = 9000, // simply increase if too small (assembler will + // crash if too small) + code_size2 = 22000 // simply increase if too small (assembler will + // crash if too small) +}; + +class amd64 { + friend class StubGenerator; + + private: + static address _get_previous_fp_entry; + static address _verify_mxcsr_entry; + + static address _f2i_fixup; + static address _f2l_fixup; + static address _d2i_fixup; + static address _d2l_fixup; + + static address _float_sign_mask; + static address _float_sign_flip; + static address _double_sign_mask; + static address _double_sign_flip; + static address _mxcsr_std; + + public: + + static address get_previous_fp_entry() + { + return _get_previous_fp_entry; + } + + static address verify_mxcsr_entry() + { + return _verify_mxcsr_entry; + } + + static address f2i_fixup() + { + return _f2i_fixup; + } + + static address f2l_fixup() + { + return _f2l_fixup; + } + + static address d2i_fixup() + { + return _d2i_fixup; + } + + static address d2l_fixup() + { + return _d2l_fixup; + } + + static address float_sign_mask() + { + return _float_sign_mask; + } + + static address float_sign_flip() + { + return _float_sign_flip; + } + + static address double_sign_mask() + { + return _double_sign_mask; + } + + static address double_sign_flip() + { + return _double_sign_flip; + } + + static address mxcsr_std() + { + return _mxcsr_std; + } +}; diff --git a/hotspot/src/cpu/x86/vm/templateInterpreterGenerator_x86.hpp b/hotspot/src/cpu/x86/vm/templateInterpreterGenerator_x86.hpp new file mode 100644 index 00000000000..b80066e44af --- /dev/null +++ b/hotspot/src/cpu/x86/vm/templateInterpreterGenerator_x86.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + protected: + + void generate_fixed_frame(bool native_call); + + // address generate_asm_interpreter_entry(bool synchronized); diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86.hpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86.hpp new file mode 100644 index 00000000000..40a70f9349a --- /dev/null +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86.hpp @@ -0,0 +1,37 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + + protected: + + // Size of interpreter code. Increase if too small. Interpreter will + // fail with a guarantee ("not enough space for interpreter generation"); + // if too small. + // Run with +PrintInterpreterSize to get the VM to print out the size. + // Max size with JVMTI and TaggedStackInterpreter +#ifdef AMD64 + const static int InterpreterCodeSize = 200 * 1024; +#else + const static int InterpreterCodeSize = 168 * 1024; +#endif // AMD64 diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp new file mode 100644 index 00000000000..61eb99b99b2 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp @@ -0,0 +1,1773 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_templateInterpreter_x86_32.cpp.incl" + +#define __ _masm-> + + +#ifndef CC_INTERP +const int method_offset = frame::interpreter_frame_method_offset * wordSize; +const int bci_offset = frame::interpreter_frame_bcx_offset * wordSize; +const int locals_offset = frame::interpreter_frame_locals_offset * wordSize; + +//------------------------------------------------------------------------------------------------------------------------ + +address TemplateInterpreterGenerator::generate_StackOverflowError_handler() { + address entry = __ pc(); + + // Note: There should be a minimal interpreter frame set up when stack + // overflow occurs since we check explicitly for it now. + // +#ifdef ASSERT + { Label L; + __ leal(rax, Address(rbp, + frame::interpreter_frame_monitor_block_top_offset * wordSize)); + __ cmpl(rax, rsp); // rax, = maximal rsp for current rbp, + // (stack grows negative) + __ jcc(Assembler::aboveEqual, L); // check if frame is complete + __ stop ("interpreter frame not set up"); + __ bind(L); + } +#endif // ASSERT + // Restore bcp under the assumption that the current frame is still + // interpreted + __ restore_bcp(); + + // expression stack must be empty before entering the VM if an exception + // happened + __ empty_expression_stack(); + __ empty_FPU_stack(); + // throw exception + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_StackOverflowError)); + return entry; +} + +address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler(const char* name) { + address entry = __ pc(); + // expression stack must be empty before entering the VM if an exception happened + __ empty_expression_stack(); + __ empty_FPU_stack(); + // setup parameters + // ??? convention: expect aberrant index in register rbx, + __ lea(rax, ExternalAddress((address)name)); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException), rax, rbx); + return entry; +} + +address TemplateInterpreterGenerator::generate_ClassCastException_handler() { + address entry = __ pc(); + // object is at TOS + __ popl(rax); + // expression stack must be empty before entering the VM if an exception + // happened + __ empty_expression_stack(); + __ empty_FPU_stack(); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_ClassCastException), + rax); + return entry; +} + +address TemplateInterpreterGenerator::generate_exception_handler_common(const char* name, const char* message, bool pass_oop) { + assert(!pass_oop || message == NULL, "either oop or message but not both"); + address entry = __ pc(); + if (pass_oop) { + // object is at TOS + __ popl(rbx); + } + // expression stack must be empty before entering the VM if an exception happened + __ empty_expression_stack(); + __ empty_FPU_stack(); + // setup parameters + __ lea(rax, ExternalAddress((address)name)); + if (pass_oop) { + __ call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::create_klass_exception), rax, rbx); + } else { + if (message != NULL) { + __ lea(rbx, ExternalAddress((address)message)); + } else { + __ movl(rbx, NULL_WORD); + } + __ call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::create_exception), rax, rbx); + } + // throw exception + __ jump(ExternalAddress(Interpreter::throw_exception_entry())); + return entry; +} + + +address TemplateInterpreterGenerator::generate_continuation_for(TosState state) { + address entry = __ pc(); + // NULL last_sp until next java call + __ movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + __ dispatch_next(state); + return entry; +} + + +address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step) { + Label interpreter_entry; + address compiled_entry = __ pc(); + +#ifdef COMPILER2 + // The FPU stack is clean if UseSSE >= 2 but must be cleaned in other cases + if ((state == ftos && UseSSE < 1) || (state == dtos && UseSSE < 2)) { + for (int i = 1; i < 8; i++) { + __ ffree(i); + } + } else if (UseSSE < 2) { + __ empty_FPU_stack(); + } +#endif + if ((state == ftos && UseSSE < 1) || (state == dtos && UseSSE < 2)) { + __ MacroAssembler::verify_FPU(1, "generate_return_entry_for compiled"); + } else { + __ MacroAssembler::verify_FPU(0, "generate_return_entry_for compiled"); + } + + __ jmp(interpreter_entry, relocInfo::none); + // emit a sentinel we can test for when converting an interpreter + // entry point to a compiled entry point. + __ a_long(Interpreter::return_sentinel); + __ a_long((int)compiled_entry); + address entry = __ pc(); + __ bind(interpreter_entry); + + // In SSE mode, interpreter returns FP results in xmm0 but they need + // to end up back on the FPU so it can operate on them. + if (state == ftos && UseSSE >= 1) { + __ subl(rsp, wordSize); + __ movflt(Address(rsp, 0), xmm0); + __ fld_s(Address(rsp, 0)); + __ addl(rsp, wordSize); + } else if (state == dtos && UseSSE >= 2) { + __ subl(rsp, 2*wordSize); + __ movdbl(Address(rsp, 0), xmm0); + __ fld_d(Address(rsp, 0)); + __ addl(rsp, 2*wordSize); + } + + __ MacroAssembler::verify_FPU(state == ftos || state == dtos ? 1 : 0, "generate_return_entry_for in interpreter"); + + // Restore stack bottom in case i2c adjusted stack + __ movl(rsp, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize)); + // and NULL it as marker that rsp is now tos until next java call + __ movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + + __ restore_bcp(); + __ restore_locals(); + __ get_cache_and_index_at_bcp(rbx, rcx, 1); + __ movl(rbx, Address(rbx, rcx, + Address::times_4, constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::flags_offset())); + __ andl(rbx, 0xFF); + __ leal(rsp, Address(rsp, rbx, Interpreter::stackElementScale())); + __ dispatch_next(state, step); + return entry; +} + + +address TemplateInterpreterGenerator::generate_deopt_entry_for(TosState state, int step) { + address entry = __ pc(); + + // In SSE mode, FP results are in xmm0 + if (state == ftos && UseSSE > 0) { + __ subl(rsp, wordSize); + __ movflt(Address(rsp, 0), xmm0); + __ fld_s(Address(rsp, 0)); + __ addl(rsp, wordSize); + } else if (state == dtos && UseSSE >= 2) { + __ subl(rsp, 2*wordSize); + __ movdbl(Address(rsp, 0), xmm0); + __ fld_d(Address(rsp, 0)); + __ addl(rsp, 2*wordSize); + } + + __ MacroAssembler::verify_FPU(state == ftos || state == dtos ? 1 : 0, "generate_deopt_entry_for in interpreter"); + + // The stack is not extended by deopt but we must NULL last_sp as this + // entry is like a "return". + __ movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + __ restore_bcp(); + __ restore_locals(); + // handle exceptions + { Label L; + const Register thread = rcx; + __ get_thread(thread); + __ cmpl(Address(thread, Thread::pending_exception_offset()), NULL_WORD); + __ jcc(Assembler::zero, L); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_pending_exception)); + __ should_not_reach_here(); + __ bind(L); + } + __ dispatch_next(state, step); + return entry; +} + + +int AbstractInterpreter::BasicType_as_index(BasicType type) { + int i = 0; + switch (type) { + case T_BOOLEAN: i = 0; break; + case T_CHAR : i = 1; break; + case T_BYTE : i = 2; break; + case T_SHORT : i = 3; break; + case T_INT : // fall through + case T_LONG : // fall through + case T_VOID : i = 4; break; + case T_FLOAT : i = 5; break; // have to treat float and double separately for SSE + case T_DOUBLE : i = 6; break; + case T_OBJECT : // fall through + case T_ARRAY : i = 7; break; + default : ShouldNotReachHere(); + } + assert(0 <= i && i < AbstractInterpreter::number_of_result_handlers, "index out of bounds"); + return i; +} + + +address TemplateInterpreterGenerator::generate_result_handler_for(BasicType type) { + address entry = __ pc(); + switch (type) { + case T_BOOLEAN: __ c2bool(rax); break; + case T_CHAR : __ andl(rax, 0xFFFF); break; + case T_BYTE : __ sign_extend_byte (rax); break; + case T_SHORT : __ sign_extend_short(rax); break; + case T_INT : /* nothing to do */ break; + case T_DOUBLE : + case T_FLOAT : + { const Register t = InterpreterRuntime::SignatureHandlerGenerator::temp(); + __ popl(t); // remove return address first + __ pop_dtos_to_rsp(); + // Must return a result for interpreter or compiler. In SSE + // mode, results are returned in xmm0 and the FPU stack must + // be empty. + if (type == T_FLOAT && UseSSE >= 1) { + // Load ST0 + __ fld_d(Address(rsp, 0)); + // Store as float and empty fpu stack + __ fstp_s(Address(rsp, 0)); + // and reload + __ movflt(xmm0, Address(rsp, 0)); + } else if (type == T_DOUBLE && UseSSE >= 2 ) { + __ movdbl(xmm0, Address(rsp, 0)); + } else { + // restore ST0 + __ fld_d(Address(rsp, 0)); + } + // and pop the temp + __ addl(rsp, 2 * wordSize); + __ pushl(t); // restore return address + } + break; + case T_OBJECT : + // retrieve result from frame + __ movl(rax, Address(rbp, frame::interpreter_frame_oop_temp_offset*wordSize)); + // and verify it + __ verify_oop(rax); + break; + default : ShouldNotReachHere(); + } + __ ret(0); // return from result handler + return entry; +} + +address TemplateInterpreterGenerator::generate_safept_entry_for(TosState state, address runtime_entry) { + address entry = __ pc(); + __ push(state); + __ call_VM(noreg, runtime_entry); + __ dispatch_via(vtos, Interpreter::_normal_table.table_for(vtos)); + return entry; +} + + +// Helpers for commoning out cases in the various type of method entries. +// + +// increment invocation count & check for overflow +// +// Note: checking for negative value instead of overflow +// so we have a 'sticky' overflow test +// +// rbx,: method +// rcx: invocation counter +// +void InterpreterGenerator::generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue) { + + const Address invocation_counter(rbx, methodOopDesc::invocation_counter_offset() + InvocationCounter::counter_offset()); + const Address backedge_counter (rbx, methodOopDesc::backedge_counter_offset() + InvocationCounter::counter_offset()); + + if (ProfileInterpreter) { // %%% Merge this into methodDataOop + __ increment(Address(rbx,methodOopDesc::interpreter_invocation_counter_offset())); + } + // Update standard invocation counters + __ movl(rax, backedge_counter); // load backedge counter + + __ increment(rcx, InvocationCounter::count_increment); + __ andl(rax, InvocationCounter::count_mask_value); // mask out the status bits + + __ movl(invocation_counter, rcx); // save invocation count + __ addl(rcx, rax); // add both counters + + // profile_method is non-null only for interpreted method so + // profile_method != NULL == !native_call + // BytecodeInterpreter only calls for native so code is elided. + + if (ProfileInterpreter && profile_method != NULL) { + // Test to see if we should create a method data oop + __ cmp32(rcx, + ExternalAddress((address)&InvocationCounter::InterpreterProfileLimit)); + __ jcc(Assembler::less, *profile_method_continue); + + // if no method data exists, go to profile_method + __ test_method_data_pointer(rax, *profile_method); + } + + __ cmp32(rcx, + ExternalAddress((address)&InvocationCounter::InterpreterInvocationLimit)); + __ jcc(Assembler::aboveEqual, *overflow); + +} + +void InterpreterGenerator::generate_counter_overflow(Label* do_continue) { + + // Asm interpreter on entry + // rdi - locals + // rsi - bcp + // rbx, - method + // rdx - cpool + // rbp, - interpreter frame + + // C++ interpreter on entry + // rsi - new interpreter state pointer + // rbp - interpreter frame pointer + // rbx - method + + // On return (i.e. jump to entry_point) [ back to invocation of interpreter ] + // rbx, - method + // rcx - rcvr (assuming there is one) + // top of stack return address of interpreter caller + // rsp - sender_sp + + // C++ interpreter only + // rsi - previous interpreter state pointer + + const Address size_of_parameters(rbx, methodOopDesc::size_of_parameters_offset()); + + // InterpreterRuntime::frequency_counter_overflow takes one argument + // indicating if the counter overflow occurs at a backwards branch (non-NULL bcp). + // The call returns the address of the verified entry point for the method or NULL + // if the compilation did not complete (either went background or bailed out). + __ movl(rax, (int)false); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), rax); + + __ movl(rbx, Address(rbp, method_offset)); // restore methodOop + + // Preserve invariant that rsi/rdi contain bcp/locals of sender frame + // and jump to the interpreted entry. + __ jmp(*do_continue, relocInfo::none); + +} + +void InterpreterGenerator::generate_stack_overflow_check(void) { + // see if we've got enough room on the stack for locals plus overhead. + // the expression stack grows down incrementally, so the normal guard + // page mechanism will work for that. + // + // Registers live on entry: + // + // Asm interpreter + // rdx: number of additional locals this frame needs (what we must check) + // rbx,: methodOop + + // destroyed on exit + // rax, + + // NOTE: since the additional locals are also always pushed (wasn't obvious in + // generate_method_entry) so the guard should work for them too. + // + + // monitor entry size: see picture of stack set (generate_method_entry) and frame_x86.hpp + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + + // total overhead size: entry_size + (saved rbp, thru expr stack bottom). + // be sure to change this if you add/subtract anything to/from the overhead area + const int overhead_size = -(frame::interpreter_frame_initial_sp_offset*wordSize) + entry_size; + + const int page_size = os::vm_page_size(); + + Label after_frame_check; + + // see if the frame is greater than one page in size. If so, + // then we need to verify there is enough stack space remaining + // for the additional locals. + __ cmpl(rdx, (page_size - overhead_size)/Interpreter::stackElementSize()); + __ jcc(Assembler::belowEqual, after_frame_check); + + // compute rsp as if this were going to be the last frame on + // the stack before the red zone + + Label after_frame_check_pop; + + __ pushl(rsi); + + const Register thread = rsi; + + __ get_thread(thread); + + const Address stack_base(thread, Thread::stack_base_offset()); + const Address stack_size(thread, Thread::stack_size_offset()); + + // locals + overhead, in bytes + __ leal(rax, Address(noreg, rdx, Interpreter::stackElementScale(), overhead_size)); + +#ifdef ASSERT + Label stack_base_okay, stack_size_okay; + // verify that thread stack base is non-zero + __ cmpl(stack_base, 0); + __ jcc(Assembler::notEqual, stack_base_okay); + __ stop("stack base is zero"); + __ bind(stack_base_okay); + // verify that thread stack size is non-zero + __ cmpl(stack_size, 0); + __ jcc(Assembler::notEqual, stack_size_okay); + __ stop("stack size is zero"); + __ bind(stack_size_okay); +#endif + + // Add stack base to locals and subtract stack size + __ addl(rax, stack_base); + __ subl(rax, stack_size); + + // Use the maximum number of pages we might bang. + const int max_pages = StackShadowPages > (StackRedPages+StackYellowPages) ? StackShadowPages : + (StackRedPages+StackYellowPages); + __ addl(rax, max_pages * page_size); + + // check against the current stack bottom + __ cmpl(rsp, rax); + __ jcc(Assembler::above, after_frame_check_pop); + + __ popl(rsi); // get saved bcp / (c++ prev state ). + + __ popl(rax); // get return address + __ jump(ExternalAddress(Interpreter::throw_StackOverflowError_entry())); + + // all done with frame size check + __ bind(after_frame_check_pop); + __ popl(rsi); + + __ bind(after_frame_check); +} + +// Allocate monitor and lock method (asm interpreter) +// rbx, - methodOop +// +void InterpreterGenerator::lock_method(void) { + // synchronize method + const Address access_flags (rbx, methodOopDesc::access_flags_offset()); + const Address monitor_block_top (rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize); + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + + #ifdef ASSERT + { Label L; + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::notZero, L); + __ stop("method doesn't need synchronization"); + __ bind(L); + } + #endif // ASSERT + // get synchronization object + { Label done; + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + Klass::java_mirror_offset_in_bytes(); + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_STATIC); + __ movl(rax, Address(rdi, Interpreter::local_offset_in_bytes(0))); // get receiver (assume this is frequent case) + __ jcc(Assembler::zero, done); + __ movl(rax, Address(rbx, methodOopDesc::constants_offset())); + __ movl(rax, Address(rax, constantPoolOopDesc::pool_holder_offset_in_bytes())); + __ movl(rax, Address(rax, mirror_offset)); + __ bind(done); + } + // add space for monitor & lock + __ subl(rsp, entry_size); // add space for a monitor entry + __ movl(monitor_block_top, rsp); // set new monitor block top + __ movl(Address(rsp, BasicObjectLock::obj_offset_in_bytes()), rax); // store object + __ movl(rdx, rsp); // object address + __ lock_object(rdx); +} + +// +// Generate a fixed interpreter frame. This is identical setup for interpreted methods +// and for native methods hence the shared code. + +void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) { + // initialize fixed part of activation frame + __ pushl(rax); // save return address + __ enter(); // save old & set new rbp, + + + __ pushl(rsi); // set sender sp + __ pushl(NULL_WORD); // leave last_sp as null + __ movl(rsi, Address(rbx,methodOopDesc::const_offset())); // get constMethodOop + __ leal(rsi, Address(rsi,constMethodOopDesc::codes_offset())); // get codebase + __ pushl(rbx); // save methodOop + if (ProfileInterpreter) { + Label method_data_continue; + __ movl(rdx, Address(rbx, in_bytes(methodOopDesc::method_data_offset()))); + __ testl(rdx, rdx); + __ jcc(Assembler::zero, method_data_continue); + __ addl(rdx, in_bytes(methodDataOopDesc::data_offset())); + __ bind(method_data_continue); + __ pushl(rdx); // set the mdp (method data pointer) + } else { + __ pushl(0); + } + + __ movl(rdx, Address(rbx, methodOopDesc::constants_offset())); + __ movl(rdx, Address(rdx, constantPoolOopDesc::cache_offset_in_bytes())); + __ pushl(rdx); // set constant pool cache + __ pushl(rdi); // set locals pointer + if (native_call) { + __ pushl(0); // no bcp + } else { + __ pushl(rsi); // set bcp + } + __ pushl(0); // reserve word for pointer to expression stack bottom + __ movl(Address(rsp, 0), rsp); // set expression stack bottom +} + +// End of helpers + +// +// Various method entries +//------------------------------------------------------------------------------------------------------------------------ +// +// + +// Call an accessor method (assuming it is resolved, otherwise drop into vanilla (slow path) entry + +address InterpreterGenerator::generate_accessor_entry(void) { + + // rbx,: methodOop + // rcx: receiver (preserve for slow entry into asm interpreter) + + // rsi: senderSP must preserved for slow path, set SP to it on fast path + + address entry_point = __ pc(); + Label xreturn_path; + + // do fastpath for resolved accessor methods + if (UseFastAccessorMethods) { + Label slow_path; + // If we need a safepoint check, generate full interpreter entry. + ExternalAddress state(SafepointSynchronize::address_of_state()); + __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), + SafepointSynchronize::_not_synchronized); + + __ jcc(Assembler::notEqual, slow_path); + // ASM/C++ Interpreter + // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites thereof; parameter size = 1 + // Note: We can only use this code if the getfield has been resolved + // and if we don't have a null-pointer exception => check for + // these conditions first and use slow path if necessary. + // rbx,: method + // rcx: receiver + __ movl(rax, Address(rsp, wordSize)); + + // check if local 0 != NULL and read field + __ testl(rax, rax); + __ jcc(Assembler::zero, slow_path); + + __ movl(rdi, Address(rbx, methodOopDesc::constants_offset())); + // read first instruction word and extract bytecode @ 1 and index @ 2 + __ movl(rdx, Address(rbx, methodOopDesc::const_offset())); + __ movl(rdx, Address(rdx, constMethodOopDesc::codes_offset())); + // Shift codes right to get the index on the right. + // The bytecode fetched looks like <0xb4><0x2a> + __ shrl(rdx, 2*BitsPerByte); + __ shll(rdx, exact_log2(in_words(ConstantPoolCacheEntry::size()))); + __ movl(rdi, Address(rdi, constantPoolOopDesc::cache_offset_in_bytes())); + + // rax,: local 0 + // rbx,: method + // rcx: receiver - do not destroy since it is needed for slow path! + // rcx: scratch + // rdx: constant pool cache index + // rdi: constant pool cache + // rsi: sender sp + + // check if getfield has been resolved and read constant pool cache entry + // check the validity of the cache entry by testing whether _indices field + // contains Bytecode::_getfield in b1 byte. + assert(in_words(ConstantPoolCacheEntry::size()) == 4, "adjust shift below"); + __ movl(rcx, + Address(rdi, + rdx, + Address::times_4, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset())); + __ shrl(rcx, 2*BitsPerByte); + __ andl(rcx, 0xFF); + __ cmpl(rcx, Bytecodes::_getfield); + __ jcc(Assembler::notEqual, slow_path); + + // Note: constant pool entry is not valid before bytecode is resolved + __ movl(rcx, + Address(rdi, + rdx, + Address::times_4, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f2_offset())); + __ movl(rdx, + Address(rdi, + rdx, + Address::times_4, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::flags_offset())); + + Label notByte, notShort, notChar; + const Address field_address (rax, rcx, Address::times_1); + + // Need to differentiate between igetfield, agetfield, bgetfield etc. + // because they are different sizes. + // Use the type from the constant pool cache + __ shrl(rdx, ConstantPoolCacheEntry::tosBits); + // Make sure we don't need to mask rdx for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ cmpl(rdx, btos); + __ jcc(Assembler::notEqual, notByte); + __ load_signed_byte(rax, field_address); + __ jmp(xreturn_path); + + __ bind(notByte); + __ cmpl(rdx, stos); + __ jcc(Assembler::notEqual, notShort); + __ load_signed_word(rax, field_address); + __ jmp(xreturn_path); + + __ bind(notShort); + __ cmpl(rdx, ctos); + __ jcc(Assembler::notEqual, notChar); + __ load_unsigned_word(rax, field_address); + __ jmp(xreturn_path); + + __ bind(notChar); +#ifdef ASSERT + Label okay; + __ cmpl(rdx, atos); + __ jcc(Assembler::equal, okay); + __ cmpl(rdx, itos); + __ jcc(Assembler::equal, okay); + __ stop("what type is this?"); + __ bind(okay); +#endif // ASSERT + // All the rest are a 32 bit wordsize + __ movl(rax, field_address); + + __ bind(xreturn_path); + + // _ireturn/_areturn + __ popl(rdi); // get return address + __ movl(rsp, rsi); // set sp to sender sp + __ jmp(rdi); + + // generate a vanilla interpreter entry as the slow path + __ bind(slow_path); + + (void) generate_normal_entry(false); + return entry_point; + } + return NULL; + +} + +// +// Interpreter stub for calling a native method. (asm interpreter) +// This sets up a somewhat different looking stack for calling the native method +// than the typical interpreter frame setup. +// + +address InterpreterGenerator::generate_native_entry(bool synchronized) { + // determine code generation flags + bool inc_counter = UseCompiler || CountCompiledCalls; + + // rbx,: methodOop + // rsi: sender sp + // rsi: previous interpreter state (C++ interpreter) must preserve + address entry_point = __ pc(); + + + const Address size_of_parameters(rbx, methodOopDesc::size_of_parameters_offset()); + const Address invocation_counter(rbx, methodOopDesc::invocation_counter_offset() + InvocationCounter::counter_offset()); + const Address access_flags (rbx, methodOopDesc::access_flags_offset()); + + // get parameter size (always needed) + __ load_unsigned_word(rcx, size_of_parameters); + + // native calls don't need the stack size check since they have no expression stack + // and the arguments are already on the stack and we only add a handful of words + // to the stack + + // rbx,: methodOop + // rcx: size of parameters + // rsi: sender sp + + __ popl(rax); // get return address + // for natives the size of locals is zero + + // compute beginning of parameters (rdi) + __ leal(rdi, Address(rsp, rcx, Interpreter::stackElementScale(), -wordSize)); + + + // add 2 zero-initialized slots for native calls + // NULL result handler + __ pushl(NULL_WORD); + // NULL oop temp (mirror or jni oop result) + __ pushl(NULL_WORD); + + if (inc_counter) __ movl(rcx, invocation_counter); // (pre-)fetch invocation count + // initialize fixed part of activation frame + + generate_fixed_frame(true); + + // make sure method is native & not abstract +#ifdef ASSERT + __ movl(rax, access_flags); + { + Label L; + __ testl(rax, JVM_ACC_NATIVE); + __ jcc(Assembler::notZero, L); + __ stop("tried to execute non-native method as native"); + __ bind(L); + } + { Label L; + __ testl(rax, JVM_ACC_ABSTRACT); + __ jcc(Assembler::zero, L); + __ stop("tried to execute abstract method in interpreter"); + __ bind(L); + } +#endif + + // Since at this point in the method invocation the exception handler + // would try to exit the monitor of synchronized methods which hasn't + // been entered yet, we set the thread local variable + // _do_not_unlock_if_synchronized to true. The remove_activation will + // check this flag. + + __ get_thread(rax); + const Address do_not_unlock_if_synchronized(rax, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + __ movbool(do_not_unlock_if_synchronized, true); + + // increment invocation count & check for overflow + Label invocation_counter_overflow; + if (inc_counter) { + generate_counter_incr(&invocation_counter_overflow, NULL, NULL); + } + + Label continue_after_compile; + __ bind(continue_after_compile); + + bang_stack_shadow_pages(true); + + // reset the _do_not_unlock_if_synchronized flag + __ get_thread(rax); + __ movbool(do_not_unlock_if_synchronized, false); + + // check for synchronized methods + // Must happen AFTER invocation_counter check and stack overflow check, + // so method is not locked if overflows. + // + if (synchronized) { + lock_method(); + } else { + // no synchronization necessary +#ifdef ASSERT + { Label L; + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::zero, L); + __ stop("method needs synchronization"); + __ bind(L); + } +#endif + } + + // start execution +#ifdef ASSERT + { Label L; + const Address monitor_block_top (rbp, + frame::interpreter_frame_monitor_block_top_offset * wordSize); + __ movl(rax, monitor_block_top); + __ cmpl(rax, rsp); + __ jcc(Assembler::equal, L); + __ stop("broken stack frame setup in interpreter"); + __ bind(L); + } +#endif + + // jvmti/dtrace support + __ notify_method_entry(); + + // work registers + const Register method = rbx; + const Register thread = rdi; + const Register t = rcx; + + // allocate space for parameters + __ get_method(method); + __ verify_oop(method); + __ load_unsigned_word(t, Address(method, methodOopDesc::size_of_parameters_offset())); + __ shll(t, Interpreter::logStackElementSize()); + __ addl(t, 2*wordSize); // allocate two more slots for JNIEnv and possible mirror + __ subl(rsp, t); + __ andl(rsp, -(StackAlignmentInBytes)); // gcc needs 16 byte aligned stacks to do XMM intrinsics + + // get signature handler + { Label L; + __ movl(t, Address(method, methodOopDesc::signature_handler_offset())); + __ testl(t, t); + __ jcc(Assembler::notZero, L); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), method); + __ get_method(method); + __ movl(t, Address(method, methodOopDesc::signature_handler_offset())); + __ bind(L); + } + + // call signature handler + assert(InterpreterRuntime::SignatureHandlerGenerator::from() == rdi, "adjust this code"); + assert(InterpreterRuntime::SignatureHandlerGenerator::to () == rsp, "adjust this code"); + assert(InterpreterRuntime::SignatureHandlerGenerator::temp() == t , "adjust this code"); + // The generated handlers do not touch RBX (the method oop). + // However, large signatures cannot be cached and are generated + // each time here. The slow-path generator will blow RBX + // sometime, so we must reload it after the call. + __ call(t); + __ get_method(method); // slow path call blows RBX on DevStudio 5.0 + + // result handler is in rax, + // set result handler + __ movl(Address(rbp, frame::interpreter_frame_result_handler_offset*wordSize), rax); + + // pass mirror handle if static call + { Label L; + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + Klass::java_mirror_offset_in_bytes(); + __ movl(t, Address(method, methodOopDesc::access_flags_offset())); + __ testl(t, JVM_ACC_STATIC); + __ jcc(Assembler::zero, L); + // get mirror + __ movl(t, Address(method, methodOopDesc:: constants_offset())); + __ movl(t, Address(t, constantPoolOopDesc::pool_holder_offset_in_bytes())); + __ movl(t, Address(t, mirror_offset)); + // copy mirror into activation frame + __ movl(Address(rbp, frame::interpreter_frame_oop_temp_offset * wordSize), t); + // pass handle to mirror + __ leal(t, Address(rbp, frame::interpreter_frame_oop_temp_offset * wordSize)); + __ movl(Address(rsp, wordSize), t); + __ bind(L); + } + + // get native function entry point + { Label L; + __ movl(rax, Address(method, methodOopDesc::native_function_offset())); + ExternalAddress unsatisfied(SharedRuntime::native_method_throw_unsatisfied_link_error_entry()); + __ cmp32(rax, unsatisfied.addr()); + __ jcc(Assembler::notEqual, L); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), method); + __ get_method(method); + __ verify_oop(method); + __ movl(rax, Address(method, methodOopDesc::native_function_offset())); + __ bind(L); + } + + // pass JNIEnv + __ get_thread(thread); + __ leal(t, Address(thread, JavaThread::jni_environment_offset())); + __ movl(Address(rsp, 0), t); + + // set_last_Java_frame_before_call + // It is enough that the pc() + // points into the right code segment. It does not have to be the correct return pc. + __ set_last_Java_frame(thread, noreg, rbp, __ pc()); + + // change thread state +#ifdef ASSERT + { Label L; + __ movl(t, Address(thread, JavaThread::thread_state_offset())); + __ cmpl(t, _thread_in_Java); + __ jcc(Assembler::equal, L); + __ stop("Wrong thread state in native stub"); + __ bind(L); + } +#endif + + // Change state to native + __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_native); + __ call(rax); + + // result potentially in rdx:rax or ST0 + + // Either restore the MXCSR register after returning from the JNI Call + // or verify that it wasn't changed. + if (VM_Version::supports_sse()) { + if (RestoreMXCSROnJNICalls) { + __ ldmxcsr(ExternalAddress(StubRoutines::addr_mxcsr_std())); + } + else if (CheckJNICalls ) { + __ call(RuntimeAddress(StubRoutines::i486::verify_mxcsr_entry())); + } + } + + // Either restore the x87 floating pointer control word after returning + // from the JNI call or verify that it wasn't changed. + if (CheckJNICalls) { + __ call(RuntimeAddress(StubRoutines::i486::verify_fpu_cntrl_wrd_entry())); + } + + // save potential result in ST(0) & rdx:rax + // (if result handler is the T_FLOAT or T_DOUBLE handler, result must be in ST0 - + // the check is necessary to avoid potential Intel FPU overflow problems by saving/restoring 'empty' FPU registers) + // It is safe to do this push because state is _thread_in_native and return address will be found + // via _last_native_pc and not via _last_jave_sp + + // NOTE: the order of theses push(es) is known to frame::interpreter_frame_result. + // If the order changes or anything else is added to the stack the code in + // interpreter_frame_result will have to be changed. + + { Label L; + Label push_double; + ExternalAddress float_handler(AbstractInterpreter::result_handler(T_FLOAT)); + ExternalAddress double_handler(AbstractInterpreter::result_handler(T_DOUBLE)); + __ cmpptr(Address(rbp, (frame::interpreter_frame_oop_temp_offset + 1)*wordSize), + float_handler.addr()); + __ jcc(Assembler::equal, push_double); + __ cmpptr(Address(rbp, (frame::interpreter_frame_oop_temp_offset + 1)*wordSize), + double_handler.addr()); + __ jcc(Assembler::notEqual, L); + __ bind(push_double); + __ push(dtos); + __ bind(L); + } + __ push(ltos); + + // change thread state + __ get_thread(thread); + __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_native_trans); + if(os::is_MP()) { + if (UseMembar) { + __ membar(); // Force this write out before the read below + } else { + // Write serialization page so VM thread can do a pseudo remote membar. + // We use the current thread pointer to calculate a thread specific + // offset to write to within the page. This minimizes bus traffic + // due to cache line collision. + __ serialize_memory(thread, rcx); + } + } + + if (AlwaysRestoreFPU) { + // Make sure the control word is correct. + __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); + } + + // check for safepoint operation in progress and/or pending suspend requests + { Label Continue; + + __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), + SafepointSynchronize::_not_synchronized); + + Label L; + __ jcc(Assembler::notEqual, L); + __ cmpl(Address(thread, JavaThread::suspend_flags_offset()), 0); + __ jcc(Assembler::equal, Continue); + __ bind(L); + + // Don't use call_VM as it will see a possible pending exception and forward it + // and never return here preventing us from clearing _last_native_pc down below. + // Also can't use call_VM_leaf either as it will check to see if rsi & rdi are + // preserved and correspond to the bcp/locals pointers. So we do a runtime call + // by hand. + // + __ pushl(thread); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, + JavaThread::check_special_condition_for_native_trans))); + __ increment(rsp, wordSize); + __ get_thread(thread); + + __ bind(Continue); + } + + // change thread state + __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_Java); + + __ reset_last_Java_frame(thread, true, true); + + // reset handle block + __ movl(t, Address(thread, JavaThread::active_handles_offset())); + __ movl(Address(t, JNIHandleBlock::top_offset_in_bytes()), 0); + + // If result was an oop then unbox and save it in the frame + { Label L; + Label no_oop, store_result; + ExternalAddress handler(AbstractInterpreter::result_handler(T_OBJECT)); + __ cmpptr(Address(rbp, frame::interpreter_frame_result_handler_offset*wordSize), + handler.addr()); + __ jcc(Assembler::notEqual, no_oop); + __ cmpl(Address(rsp, 0), NULL_WORD); + __ pop(ltos); + __ testl(rax, rax); + __ jcc(Assembler::zero, store_result); + // unbox + __ movl(rax, Address(rax, 0)); + __ bind(store_result); + __ movl(Address(rbp, (frame::interpreter_frame_oop_temp_offset)*wordSize), rax); + // keep stack depth as expected by pushing oop which will eventually be discarded + __ push(ltos); + __ bind(no_oop); + } + + { + Label no_reguard; + __ cmpl(Address(thread, JavaThread::stack_guard_state_offset()), JavaThread::stack_guard_yellow_disabled); + __ jcc(Assembler::notEqual, no_reguard); + + __ pushad(); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages))); + __ popad(); + + __ bind(no_reguard); + } + + // restore rsi to have legal interpreter frame, + // i.e., bci == 0 <=> rsi == code_base() + // Can't call_VM until bcp is within reasonable. + __ get_method(method); // method is junk from thread_in_native to now. + __ verify_oop(method); + __ movl(rsi, Address(method,methodOopDesc::const_offset())); // get constMethodOop + __ leal(rsi, Address(rsi,constMethodOopDesc::codes_offset())); // get codebase + + // handle exceptions (exception handling will handle unlocking!) + { Label L; + __ cmpl(Address(thread, Thread::pending_exception_offset()), NULL_WORD); + __ jcc(Assembler::zero, L); + // Note: At some point we may want to unify this with the code used in call_VM_base(); + // i.e., we should use the StubRoutines::forward_exception code. For now this + // doesn't work here because the rsp is not correctly set at this point. + __ MacroAssembler::call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_pending_exception)); + __ should_not_reach_here(); + __ bind(L); + } + + // do unlocking if necessary + { Label L; + __ movl(t, Address(method, methodOopDesc::access_flags_offset())); + __ testl(t, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::zero, L); + // the code below should be shared with interpreter macro assembler implementation + { Label unlock; + // BasicObjectLock will be first in list, since this is a synchronized method. However, need + // to check that the object has not been unlocked by an explicit monitorexit bytecode. + const Address monitor(rbp, frame::interpreter_frame_initial_sp_offset * wordSize - (int)sizeof(BasicObjectLock)); + + __ leal(rdx, monitor); // address of first monitor + + __ movl(t, Address(rdx, BasicObjectLock::obj_offset_in_bytes())); + __ testl(t, t); + __ jcc(Assembler::notZero, unlock); + + // Entry already unlocked, need to throw exception + __ MacroAssembler::call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + __ should_not_reach_here(); + + __ bind(unlock); + __ unlock_object(rdx); + } + __ bind(L); + } + + // jvmti/dtrace support + // Note: This must happen _after_ handling/throwing any exceptions since + // the exception handler code notifies the runtime of method exits + // too. If this happens before, method entry/exit notifications are + // not properly paired (was bug - gri 11/22/99). + __ notify_method_exit(vtos, InterpreterMacroAssembler::NotifyJVMTI); + + // restore potential result in rdx:rax, call result handler to restore potential result in ST0 & handle result + __ pop(ltos); + __ movl(t, Address(rbp, frame::interpreter_frame_result_handler_offset*wordSize)); + __ call(t); + + // remove activation + __ movl(t, Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize)); // get sender sp + __ leave(); // remove frame anchor + __ popl(rdi); // get return address + __ movl(rsp, t); // set sp to sender sp + __ jmp(rdi); + + if (inc_counter) { + // Handle overflow of counter and compile method + __ bind(invocation_counter_overflow); + generate_counter_overflow(&continue_after_compile); + } + + return entry_point; +} + +// +// Generic interpreted method entry to (asm) interpreter +// +address InterpreterGenerator::generate_normal_entry(bool synchronized) { + // determine code generation flags + bool inc_counter = UseCompiler || CountCompiledCalls; + + // rbx,: methodOop + // rsi: sender sp + address entry_point = __ pc(); + + + const Address size_of_parameters(rbx, methodOopDesc::size_of_parameters_offset()); + const Address size_of_locals (rbx, methodOopDesc::size_of_locals_offset()); + const Address invocation_counter(rbx, methodOopDesc::invocation_counter_offset() + InvocationCounter::counter_offset()); + const Address access_flags (rbx, methodOopDesc::access_flags_offset()); + + // get parameter size (always needed) + __ load_unsigned_word(rcx, size_of_parameters); + + // rbx,: methodOop + // rcx: size of parameters + + // rsi: sender_sp (could differ from sp+wordSize if we were called via c2i ) + + __ load_unsigned_word(rdx, size_of_locals); // get size of locals in words + __ subl(rdx, rcx); // rdx = no. of additional locals + + // see if we've got enough room on the stack for locals plus overhead. + generate_stack_overflow_check(); + + // get return address + __ popl(rax); + + // compute beginning of parameters (rdi) + __ leal(rdi, Address(rsp, rcx, Interpreter::stackElementScale(), -wordSize)); + + // rdx - # of additional locals + // allocate space for locals + // explicitly initialize locals + { + Label exit, loop; + __ testl(rdx, rdx); + __ jcc(Assembler::lessEqual, exit); // do nothing if rdx <= 0 + __ bind(loop); + if (TaggedStackInterpreter) __ pushl(NULL_WORD); // push tag + __ pushl(NULL_WORD); // initialize local variables + __ decrement(rdx); // until everything initialized + __ jcc(Assembler::greater, loop); + __ bind(exit); + } + + if (inc_counter) __ movl(rcx, invocation_counter); // (pre-)fetch invocation count + // initialize fixed part of activation frame + generate_fixed_frame(false); + + // make sure method is not native & not abstract +#ifdef ASSERT + __ movl(rax, access_flags); + { + Label L; + __ testl(rax, JVM_ACC_NATIVE); + __ jcc(Assembler::zero, L); + __ stop("tried to execute native method as non-native"); + __ bind(L); + } + { Label L; + __ testl(rax, JVM_ACC_ABSTRACT); + __ jcc(Assembler::zero, L); + __ stop("tried to execute abstract method in interpreter"); + __ bind(L); + } +#endif + + // Since at this point in the method invocation the exception handler + // would try to exit the monitor of synchronized methods which hasn't + // been entered yet, we set the thread local variable + // _do_not_unlock_if_synchronized to true. The remove_activation will + // check this flag. + + __ get_thread(rax); + const Address do_not_unlock_if_synchronized(rax, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + __ movbool(do_not_unlock_if_synchronized, true); + + // increment invocation count & check for overflow + Label invocation_counter_overflow; + Label profile_method; + Label profile_method_continue; + if (inc_counter) { + generate_counter_incr(&invocation_counter_overflow, &profile_method, &profile_method_continue); + if (ProfileInterpreter) { + __ bind(profile_method_continue); + } + } + Label continue_after_compile; + __ bind(continue_after_compile); + + bang_stack_shadow_pages(false); + + // reset the _do_not_unlock_if_synchronized flag + __ get_thread(rax); + __ movbool(do_not_unlock_if_synchronized, false); + + // check for synchronized methods + // Must happen AFTER invocation_counter check and stack overflow check, + // so method is not locked if overflows. + // + if (synchronized) { + // Allocate monitor and lock method + lock_method(); + } else { + // no synchronization necessary +#ifdef ASSERT + { Label L; + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::zero, L); + __ stop("method needs synchronization"); + __ bind(L); + } +#endif + } + + // start execution +#ifdef ASSERT + { Label L; + const Address monitor_block_top (rbp, + frame::interpreter_frame_monitor_block_top_offset * wordSize); + __ movl(rax, monitor_block_top); + __ cmpl(rax, rsp); + __ jcc(Assembler::equal, L); + __ stop("broken stack frame setup in interpreter"); + __ bind(L); + } +#endif + + // jvmti support + __ notify_method_entry(); + + __ dispatch_next(vtos); + + // invocation counter overflow + if (inc_counter) { + if (ProfileInterpreter) { + // We have decided to profile this method in the interpreter + __ bind(profile_method); + + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method), rsi, true); + + __ movl(rbx, Address(rbp, method_offset)); // restore methodOop + __ movl(rax, Address(rbx, in_bytes(methodOopDesc::method_data_offset()))); + __ movl(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), rax); + __ test_method_data_pointer(rax, profile_method_continue); + __ addl(rax, in_bytes(methodDataOopDesc::data_offset())); + __ movl(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), rax); + __ jmp(profile_method_continue); + } + // Handle overflow of counter and compile method + __ bind(invocation_counter_overflow); + generate_counter_overflow(&continue_after_compile); + } + + return entry_point; +} + +//------------------------------------------------------------------------------------------------------------------------ +// Entry points +// +// Here we generate the various kind of entries into the interpreter. +// The two main entry type are generic bytecode methods and native call method. +// These both come in synchronized and non-synchronized versions but the +// frame layout they create is very similar. The other method entry +// types are really just special purpose entries that are really entry +// and interpretation all in one. These are for trivial methods like +// accessor, empty, or special math methods. +// +// When control flow reaches any of the entry types for the interpreter +// the following holds -> +// +// Arguments: +// +// rbx,: methodOop +// rcx: receiver +// +// +// Stack layout immediately at entry +// +// [ return address ] <--- rsp +// [ parameter n ] +// ... +// [ parameter 1 ] +// [ expression stack ] (caller's java expression stack) + +// Assuming that we don't go to one of the trivial specialized +// entries the stack will look like below when we are ready to execute +// the first bytecode (or call the native routine). The register usage +// will be as the template based interpreter expects (see interpreter_x86.hpp). +// +// local variables follow incoming parameters immediately; i.e. +// the return address is moved to the end of the locals). +// +// [ monitor entry ] <--- rsp +// ... +// [ monitor entry ] +// [ expr. stack bottom ] +// [ saved rsi ] +// [ current rdi ] +// [ methodOop ] +// [ saved rbp, ] <--- rbp, +// [ return address ] +// [ local variable m ] +// ... +// [ local variable 1 ] +// [ parameter n ] +// ... +// [ parameter 1 ] <--- rdi + +address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) { + // determine code generation flags + bool synchronized = false; + address entry_point = NULL; + + switch (kind) { + case Interpreter::zerolocals : break; + case Interpreter::zerolocals_synchronized: synchronized = true; break; + case Interpreter::native : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(false); break; + case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(true); break; + case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break; + case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break; + case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break; + + case Interpreter::java_lang_math_sin : // fall thru + case Interpreter::java_lang_math_cos : // fall thru + case Interpreter::java_lang_math_tan : // fall thru + case Interpreter::java_lang_math_abs : // fall thru + case Interpreter::java_lang_math_log : // fall thru + case Interpreter::java_lang_math_log10 : // fall thru + case Interpreter::java_lang_math_sqrt : entry_point = ((InterpreterGenerator*)this)->generate_math_entry(kind); break; + default : ShouldNotReachHere(); break; + } + + if (entry_point) return entry_point; + + return ((InterpreterGenerator*)this)->generate_normal_entry(synchronized); + +} + +// How much stack a method activation needs in words. +int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { + + const int stub_code = 4; // see generate_call_stub + // Save space for one monitor to get into the interpreted method in case + // the method is synchronized + int monitor_size = method->is_synchronized() ? + 1*frame::interpreter_frame_monitor_size() : 0; + + // total overhead size: entry_size + (saved rbp, thru expr stack bottom). + // be sure to change this if you add/subtract anything to/from the overhead area + const int overhead_size = -frame::interpreter_frame_initial_sp_offset; + + const int method_stack = (method->max_locals() + method->max_stack()) * + Interpreter::stackElementWords(); + return overhead_size + method_stack + stub_code; +} + +// asm based interpreter deoptimization helpers + +int AbstractInterpreter::layout_activation(methodOop method, + int tempcount, + int popframe_extra_args, + int moncount, + int callee_param_count, + int callee_locals, + frame* caller, + frame* interpreter_frame, + bool is_top_frame) { + // Note: This calculation must exactly parallel the frame setup + // in AbstractInterpreterGenerator::generate_method_entry. + // If interpreter_frame!=NULL, set up the method, locals, and monitors. + // The frame interpreter_frame, if not NULL, is guaranteed to be the right size, + // as determined by a previous call to this method. + // It is also guaranteed to be walkable even though it is in a skeletal state + // NOTE: return size is in words not bytes + + // fixed size of an interpreter frame: + int max_locals = method->max_locals() * Interpreter::stackElementWords(); + int extra_locals = (method->max_locals() - method->size_of_parameters()) * + Interpreter::stackElementWords(); + + int overhead = frame::sender_sp_offset - frame::interpreter_frame_initial_sp_offset; + + // Our locals were accounted for by the caller (or last_frame_adjust on the transistion) + // Since the callee parameters already account for the callee's params we only need to account for + // the extra locals. + + + int size = overhead + + ((callee_locals - callee_param_count)*Interpreter::stackElementWords()) + + (moncount*frame::interpreter_frame_monitor_size()) + + tempcount*Interpreter::stackElementWords() + popframe_extra_args; + + if (interpreter_frame != NULL) { +#ifdef ASSERT + assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), "Frame not properly walkable"); + assert(caller->sp() == interpreter_frame->sender_sp(), "Frame not properly walkable(2)"); +#endif + + interpreter_frame->interpreter_frame_set_method(method); + // NOTE the difference in using sender_sp and interpreter_frame_sender_sp + // interpreter_frame_sender_sp is the original sp of the caller (the unextended_sp) + // and sender_sp is fp+8 + intptr_t* locals = interpreter_frame->sender_sp() + max_locals - 1; + + interpreter_frame->interpreter_frame_set_locals(locals); + BasicObjectLock* montop = interpreter_frame->interpreter_frame_monitor_begin(); + BasicObjectLock* monbot = montop - moncount; + interpreter_frame->interpreter_frame_set_monitor_end(monbot); + + // Set last_sp + intptr_t* rsp = (intptr_t*) monbot - + tempcount*Interpreter::stackElementWords() - + popframe_extra_args; + interpreter_frame->interpreter_frame_set_last_sp(rsp); + + // All frames but the initial (oldest) interpreter frame we fill in have a + // value for sender_sp that allows walking the stack but isn't + // truly correct. Correct the value here. + + if (extra_locals != 0 && + interpreter_frame->sender_sp() == interpreter_frame->interpreter_frame_sender_sp() ) { + interpreter_frame->set_interpreter_frame_sender_sp(caller->sp() + extra_locals); + } + *interpreter_frame->interpreter_frame_cache_addr() = + method->constants()->cache(); + } + return size; +} + + +//------------------------------------------------------------------------------------------------------------------------ +// Exceptions + +void TemplateInterpreterGenerator::generate_throw_exception() { + // Entry point in previous activation (i.e., if the caller was interpreted) + Interpreter::_rethrow_exception_entry = __ pc(); + + // Restore sp to interpreter_frame_last_sp even though we are going + // to empty the expression stack for the exception processing. + __ movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + // rax,: exception + // rdx: return address/pc that threw exception + __ restore_bcp(); // rsi points to call/send + __ restore_locals(); + + // Entry point for exceptions thrown within interpreter code + Interpreter::_throw_exception_entry = __ pc(); + // expression stack is undefined here + // rax,: exception + // rsi: exception bcp + __ verify_oop(rax); + + // expression stack must be empty before entering the VM in case of an exception + __ empty_expression_stack(); + __ empty_FPU_stack(); + // find exception handler address and preserve exception oop + __ call_VM(rdx, CAST_FROM_FN_PTR(address, InterpreterRuntime::exception_handler_for_exception), rax); + // rax,: exception handler entry point + // rdx: preserved exception oop + // rsi: bcp for exception handler + __ push_ptr(rdx); // push exception which is now the only value on the stack + __ jmp(rax); // jump to exception handler (may be _remove_activation_entry!) + + // If the exception is not handled in the current frame the frame is removed and + // the exception is rethrown (i.e. exception continuation is _rethrow_exception). + // + // Note: At this point the bci is still the bxi for the instruction which caused + // the exception and the expression stack is empty. Thus, for any VM calls + // at this point, GC will find a legal oop map (with empty expression stack). + + // In current activation + // tos: exception + // rsi: exception bcp + + // + // JVMTI PopFrame support + // + + Interpreter::_remove_activation_preserving_args_entry = __ pc(); + __ empty_expression_stack(); + __ empty_FPU_stack(); + // Set the popframe_processing bit in pending_popframe_condition indicating that we are + // currently handling popframe, so that call_VMs that may happen later do not trigger new + // popframe handling cycles. + __ get_thread(rcx); + __ movl(rdx, Address(rcx, JavaThread::popframe_condition_offset())); + __ orl(rdx, JavaThread::popframe_processing_bit); + __ movl(Address(rcx, JavaThread::popframe_condition_offset()), rdx); + + { + // Check to see whether we are returning to a deoptimized frame. + // (The PopFrame call ensures that the caller of the popped frame is + // either interpreted or compiled and deoptimizes it if compiled.) + // In this case, we can't call dispatch_next() after the frame is + // popped, but instead must save the incoming arguments and restore + // them after deoptimization has occurred. + // + // Note that we don't compare the return PC against the + // deoptimization blob's unpack entry because of the presence of + // adapter frames in C2. + Label caller_not_deoptimized; + __ movl(rdx, Address(rbp, frame::return_addr_offset * wordSize)); + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::interpreter_contains), rdx); + __ testl(rax, rax); + __ jcc(Assembler::notZero, caller_not_deoptimized); + + // Compute size of arguments for saving when returning to deoptimized caller + __ get_method(rax); + __ verify_oop(rax); + __ load_unsigned_word(rax, Address(rax, in_bytes(methodOopDesc::size_of_parameters_offset()))); + __ shll(rax, Interpreter::logStackElementSize()); + __ restore_locals(); + __ subl(rdi, rax); + __ addl(rdi, wordSize); + // Save these arguments + __ get_thread(rcx); + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::popframe_preserve_args), rcx, rax, rdi); + + __ remove_activation(vtos, rdx, + /* throw_monitor_exception */ false, + /* install_monitor_exception */ false, + /* notify_jvmdi */ false); + + // Inform deoptimization that it is responsible for restoring these arguments + __ get_thread(rcx); + __ movl(Address(rcx, JavaThread::popframe_condition_offset()), JavaThread::popframe_force_deopt_reexecution_bit); + + // Continue in deoptimization handler + __ jmp(rdx); + + __ bind(caller_not_deoptimized); + } + + __ remove_activation(vtos, rdx, + /* throw_monitor_exception */ false, + /* install_monitor_exception */ false, + /* notify_jvmdi */ false); + + // Finish with popframe handling + // A previous I2C followed by a deoptimization might have moved the + // outgoing arguments further up the stack. PopFrame expects the + // mutations to those outgoing arguments to be preserved and other + // constraints basically require this frame to look exactly as + // though it had previously invoked an interpreted activation with + // no space between the top of the expression stack (current + // last_sp) and the top of stack. Rather than force deopt to + // maintain this kind of invariant all the time we call a small + // fixup routine to move the mutated arguments onto the top of our + // expression stack if necessary. + __ movl(rax, rsp); + __ movl(rbx, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize)); + __ get_thread(rcx); + // PC must point into interpreter here + __ set_last_Java_frame(rcx, noreg, rbp, __ pc()); + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::popframe_move_outgoing_args), rcx, rax, rbx); + __ get_thread(rcx); + __ reset_last_Java_frame(rcx, true, true); + // Restore the last_sp and null it out + __ movl(rsp, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize)); + __ movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + + __ restore_bcp(); + __ restore_locals(); + // The method data pointer was incremented already during + // call profiling. We have to restore the mdp for the current bcp. + if (ProfileInterpreter) { + __ set_method_data_pointer_for_bcp(); + } + + // Clear the popframe condition flag + __ get_thread(rcx); + __ movl(Address(rcx, JavaThread::popframe_condition_offset()), JavaThread::popframe_inactive); + + __ dispatch_next(vtos); + // end of PopFrame support + + Interpreter::_remove_activation_entry = __ pc(); + + // preserve exception over this code sequence + __ pop_ptr(rax); + __ get_thread(rcx); + __ movl(Address(rcx, JavaThread::vm_result_offset()), rax); + // remove the activation (without doing throws on illegalMonitorExceptions) + __ remove_activation(vtos, rdx, false, true, false); + // restore exception + __ get_thread(rcx); + __ movl(rax, Address(rcx, JavaThread::vm_result_offset())); + __ movl(Address(rcx, JavaThread::vm_result_offset()), NULL_WORD); + __ verify_oop(rax); + + // Inbetween activations - previous activation type unknown yet + // compute continuation point - the continuation point expects + // the following registers set up: + // + // rax,: exception + // rdx: return address/pc that threw exception + // rsp: expression stack of caller + // rbp,: rbp, of caller + __ pushl(rax); // save exception + __ pushl(rdx); // save return address + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), rdx); + __ movl(rbx, rax); // save exception handler + __ popl(rdx); // restore return address + __ popl(rax); // restore exception + // Note that an "issuing PC" is actually the next PC after the call + __ jmp(rbx); // jump to exception handler of caller +} + + +// +// JVMTI ForceEarlyReturn support +// +address TemplateInterpreterGenerator::generate_earlyret_entry_for(TosState state) { + address entry = __ pc(); + + __ restore_bcp(); + __ restore_locals(); + __ empty_expression_stack(); + __ empty_FPU_stack(); + __ load_earlyret_value(state); + + __ get_thread(rcx); + __ movl(rcx, Address(rcx, JavaThread::jvmti_thread_state_offset())); + const Address cond_addr(rcx, JvmtiThreadState::earlyret_state_offset()); + + // Clear the earlyret state + __ movl(cond_addr, JvmtiThreadState::earlyret_inactive); + + __ remove_activation(state, rsi, + false, /* throw_monitor_exception */ + false, /* install_monitor_exception */ + true); /* notify_jvmdi */ + __ jmp(rsi); + return entry; +} // end of ForceEarlyReturn support + + +//------------------------------------------------------------------------------------------------------------------------ +// Helper for vtos entry point generation + +void TemplateInterpreterGenerator::set_vtos_entry_points (Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep) { + assert(t->is_valid() && t->tos_in() == vtos, "illegal template"); + Label L; + fep = __ pc(); __ push(ftos); __ jmp(L); + dep = __ pc(); __ push(dtos); __ jmp(L); + lep = __ pc(); __ push(ltos); __ jmp(L); + aep = __ pc(); __ push(atos); __ jmp(L); + bep = cep = sep = // fall through + iep = __ pc(); __ push(itos); // fall through + vep = __ pc(); __ bind(L); // fall through + generate_and_dispatch(t); +} + +//------------------------------------------------------------------------------------------------------------------------ +// Generation of individual instructions + +// helpers for generate_and_dispatch + + + +InterpreterGenerator::InterpreterGenerator(StubQueue* code) + : TemplateInterpreterGenerator(code) { + generate_all(); // down here so it can be "virtual" +} + +//------------------------------------------------------------------------------------------------------------------------ + +// Non-product code +#ifndef PRODUCT +address TemplateInterpreterGenerator::generate_trace_code(TosState state) { + address entry = __ pc(); + + // prepare expression stack + __ popl(rcx); // pop return address so expression stack is 'pure' + __ push(state); // save tosca + + // pass tosca registers as arguments & call tracer + __ call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::trace_bytecode), rcx, rax, rdx); + __ movl(rcx, rax); // make sure return address is not destroyed by pop(state) + __ pop(state); // restore tosca + + // return + __ jmp(rcx); + + return entry; +} + + +void TemplateInterpreterGenerator::count_bytecode() { + __ increment(ExternalAddress((address) &BytecodeCounter::_counter_value)); +} + + +void TemplateInterpreterGenerator::histogram_bytecode(Template* t) { + __ increment(ExternalAddress((address) &BytecodeHistogram::_counters[t->bytecode()])); +} + + +void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) { + __ mov32(ExternalAddress((address) &BytecodePairHistogram::_index), rbx); + __ shrl(rbx, BytecodePairHistogram::log2_number_of_codes); + __ orl(rbx, ((int)t->bytecode()) << BytecodePairHistogram::log2_number_of_codes); + ExternalAddress table((address) BytecodePairHistogram::_counters); + Address index(noreg, rbx, Address::times_4); + __ increment(ArrayAddress(table, index)); +} + + +void TemplateInterpreterGenerator::trace_bytecode(Template* t) { + // Call a little run-time stub to avoid blow-up for each bytecode. + // The run-time runtime saves the right registers, depending on + // the tosca in-state for the given template. + assert(Interpreter::trace_code(t->tos_in()) != NULL, + "entry must have been generated"); + __ call(RuntimeAddress(Interpreter::trace_code(t->tos_in()))); +} + + +void TemplateInterpreterGenerator::stop_interpreter_at() { + Label L; + __ cmp32(ExternalAddress((address) &BytecodeCounter::_counter_value), + StopInterpreterAt); + __ jcc(Assembler::notEqual, L); + __ int3(); + __ bind(L); +} +#endif // !PRODUCT +#endif // CC_INTERP diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp new file mode 100644 index 00000000000..5750142530d --- /dev/null +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp @@ -0,0 +1,1672 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreter_x86_64.cpp.incl" + +#define __ _masm-> + +const int method_offset = frame::interpreter_frame_method_offset * wordSize; +const int bci_offset = frame::interpreter_frame_bcx_offset * wordSize; +const int locals_offset = frame::interpreter_frame_locals_offset * wordSize; + +//----------------------------------------------------------------------------- + +address TemplateInterpreterGenerator::generate_StackOverflowError_handler() { + address entry = __ pc(); + +#ifdef ASSERT + { + Label L; + __ leaq(rax, Address(rbp, + frame::interpreter_frame_monitor_block_top_offset * + wordSize)); + __ cmpq(rax, rsp); // rax = maximal rsp for current rbp (stack + // grows negative) + __ jcc(Assembler::aboveEqual, L); // check if frame is complete + __ stop ("interpreter frame not set up"); + __ bind(L); + } +#endif // ASSERT + // Restore bcp under the assumption that the current frame is still + // interpreted + __ restore_bcp(); + + // expression stack must be empty before entering the VM if an + // exception happened + __ empty_expression_stack(); + // throw exception + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_StackOverflowError)); + return entry; +} + +address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler( + const char* name) { + address entry = __ pc(); + // expression stack must be empty before entering the VM if an + // exception happened + __ empty_expression_stack(); + // setup parameters + // ??? convention: expect aberrant index in register ebx + __ lea(c_rarg1, ExternalAddress((address)name)); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime:: + throw_ArrayIndexOutOfBoundsException), + c_rarg1, rbx); + return entry; +} + +address TemplateInterpreterGenerator::generate_ClassCastException_handler() { + address entry = __ pc(); + + // object is at TOS + __ popq(c_rarg1); + + // expression stack must be empty before entering the VM if an + // exception happened + __ empty_expression_stack(); + + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime:: + throw_ClassCastException), + c_rarg1); + return entry; +} + +address TemplateInterpreterGenerator::generate_exception_handler_common( + const char* name, const char* message, bool pass_oop) { + assert(!pass_oop || message == NULL, "either oop or message but not both"); + address entry = __ pc(); + if (pass_oop) { + // object is at TOS + __ popq(c_rarg2); + } + // expression stack must be empty before entering the VM if an + // exception happened + __ empty_expression_stack(); + // setup parameters + __ lea(c_rarg1, ExternalAddress((address)name)); + if (pass_oop) { + __ call_VM(rax, CAST_FROM_FN_PTR(address, + InterpreterRuntime:: + create_klass_exception), + c_rarg1, c_rarg2); + } else { + // kind of lame ExternalAddress can't take NULL because + // external_word_Relocation will assert. + if (message != NULL) { + __ lea(c_rarg2, ExternalAddress((address)message)); + } else { + __ movptr(c_rarg2, NULL_WORD); + } + __ call_VM(rax, + CAST_FROM_FN_PTR(address, InterpreterRuntime::create_exception), + c_rarg1, c_rarg2); + } + // throw exception + __ jump(ExternalAddress(Interpreter::throw_exception_entry())); + return entry; +} + + +address TemplateInterpreterGenerator::generate_continuation_for(TosState state) { + address entry = __ pc(); + // NULL last_sp until next java call + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + __ dispatch_next(state); + return entry; +} + + +address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, + int step) { + + // amd64 doesn't need to do anything special about compiled returns + // to the interpreter so the code that exists on x86 to place a sentinel + // here and the specialized cleanup code is not needed here. + + address entry = __ pc(); + + // Restore stack bottom in case i2c adjusted stack + __ movq(rsp, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize)); + // and NULL it as marker that esp is now tos until next java call + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + + __ restore_bcp(); + __ restore_locals(); + __ get_cache_and_index_at_bcp(rbx, rcx, 1); + __ movl(rbx, Address(rbx, rcx, + Address::times_8, + in_bytes(constantPoolCacheOopDesc::base_offset()) + + 3 * wordSize)); + __ andl(rbx, 0xFF); + if (TaggedStackInterpreter) __ shll(rbx, 1); // 2 slots per parameter. + __ leaq(rsp, Address(rsp, rbx, Address::times_8)); + __ dispatch_next(state, step); + return entry; +} + + +address TemplateInterpreterGenerator::generate_deopt_entry_for(TosState state, + int step) { + address entry = __ pc(); + // NULL last_sp until next java call + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + __ restore_bcp(); + __ restore_locals(); + // handle exceptions + { + Label L; + __ cmpq(Address(r15_thread, Thread::pending_exception_offset()), (int) NULL); + __ jcc(Assembler::zero, L); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_pending_exception)); + __ should_not_reach_here(); + __ bind(L); + } + __ dispatch_next(state, step); + return entry; +} + +int AbstractInterpreter::BasicType_as_index(BasicType type) { + int i = 0; + switch (type) { + case T_BOOLEAN: i = 0; break; + case T_CHAR : i = 1; break; + case T_BYTE : i = 2; break; + case T_SHORT : i = 3; break; + case T_INT : i = 4; break; + case T_LONG : i = 5; break; + case T_VOID : i = 6; break; + case T_FLOAT : i = 7; break; + case T_DOUBLE : i = 8; break; + case T_OBJECT : i = 9; break; + case T_ARRAY : i = 9; break; + default : ShouldNotReachHere(); + } + assert(0 <= i && i < AbstractInterpreter::number_of_result_handlers, + "index out of bounds"); + return i; +} + + +address TemplateInterpreterGenerator::generate_result_handler_for( + BasicType type) { + address entry = __ pc(); + switch (type) { + case T_BOOLEAN: __ c2bool(rax); break; + case T_CHAR : __ movzwl(rax, rax); break; + case T_BYTE : __ sign_extend_byte(rax); break; + case T_SHORT : __ sign_extend_short(rax); break; + case T_INT : /* nothing to do */ break; + case T_LONG : /* nothing to do */ break; + case T_VOID : /* nothing to do */ break; + case T_FLOAT : /* nothing to do */ break; + case T_DOUBLE : /* nothing to do */ break; + case T_OBJECT : + // retrieve result from frame + __ movq(rax, Address(rbp, frame::interpreter_frame_oop_temp_offset*wordSize)); + // and verify it + __ verify_oop(rax); + break; + default : ShouldNotReachHere(); + } + __ ret(0); // return from result handler + return entry; +} + +address TemplateInterpreterGenerator::generate_safept_entry_for( + TosState state, + address runtime_entry) { + address entry = __ pc(); + __ push(state); + __ call_VM(noreg, runtime_entry); + __ dispatch_via(vtos, Interpreter::_normal_table.table_for(vtos)); + return entry; +} + + + +// Helpers for commoning out cases in the various type of method entries. +// + + +// increment invocation count & check for overflow +// +// Note: checking for negative value instead of overflow +// so we have a 'sticky' overflow test +// +// rbx: method +// ecx: invocation counter +// +void InterpreterGenerator::generate_counter_incr( + Label* overflow, + Label* profile_method, + Label* profile_method_continue) { + + const Address invocation_counter(rbx, + methodOopDesc::invocation_counter_offset() + + InvocationCounter::counter_offset()); + const Address backedge_counter(rbx, + methodOopDesc::backedge_counter_offset() + + InvocationCounter::counter_offset()); + + if (ProfileInterpreter) { // %%% Merge this into methodDataOop + __ incrementl(Address(rbx, + methodOopDesc::interpreter_invocation_counter_offset())); + } + // Update standard invocation counters + __ movl(rax, backedge_counter); // load backedge counter + + __ incrementl(rcx, InvocationCounter::count_increment); + __ andl(rax, InvocationCounter::count_mask_value); // mask out the + // status bits + + __ movl(invocation_counter, rcx); // save invocation count + __ addl(rcx, rax); // add both counters + + // profile_method is non-null only for interpreted method so + // profile_method != NULL == !native_call + + if (ProfileInterpreter && profile_method != NULL) { + // Test to see if we should create a method data oop + __ cmp32(rcx, ExternalAddress((address)&InvocationCounter::InterpreterProfileLimit)); + __ jcc(Assembler::less, *profile_method_continue); + + // if no method data exists, go to profile_method + __ test_method_data_pointer(rax, *profile_method); + } + + __ cmp32(rcx, ExternalAddress((address)&InvocationCounter::InterpreterInvocationLimit)); + __ jcc(Assembler::aboveEqual, *overflow); +} + +void InterpreterGenerator::generate_counter_overflow(Label* do_continue) { + + // Asm interpreter on entry + // r14 - locals + // r13 - bcp + // rbx - method + // edx - cpool --- DOES NOT APPEAR TO BE TRUE + // rbp - interpreter frame + + // On return (i.e. jump to entry_point) [ back to invocation of interpreter ] + // Everything as it was on entry + // rdx is not restored. Doesn't appear to really be set. + + const Address size_of_parameters(rbx, + methodOopDesc::size_of_parameters_offset()); + + // InterpreterRuntime::frequency_counter_overflow takes two + // arguments, the first (thread) is passed by call_VM, the second + // indicates if the counter overflow occurs at a backwards branch + // (NULL bcp). We pass zero for it. The call returns the address + // of the verified entry point for the method or NULL if the + // compilation did not complete (either went background or bailed + // out). + __ movl(c_rarg1, 0); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::frequency_counter_overflow), + c_rarg1); + + __ movq(rbx, Address(rbp, method_offset)); // restore methodOop + // Preserve invariant that r13/r14 contain bcp/locals of sender frame + // and jump to the interpreted entry. + __ jmp(*do_continue, relocInfo::none); +} + +// See if we've got enough room on the stack for locals plus overhead. +// The expression stack grows down incrementally, so the normal guard +// page mechanism will work for that. +// +// NOTE: Since the additional locals are also always pushed (wasn't +// obvious in generate_method_entry) so the guard should work for them +// too. +// +// Args: +// rdx: number of additional locals this frame needs (what we must check) +// rbx: methodOop +// +// Kills: +// rax +void InterpreterGenerator::generate_stack_overflow_check(void) { + + // monitor entry size: see picture of stack set + // (generate_method_entry) and frame_amd64.hpp + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + + // total overhead size: entry_size + (saved rbp through expr stack + // bottom). be sure to change this if you add/subtract anything + // to/from the overhead area + const int overhead_size = + -(frame::interpreter_frame_initial_sp_offset * wordSize) + entry_size; + + const int page_size = os::vm_page_size(); + + Label after_frame_check; + + // see if the frame is greater than one page in size. If so, + // then we need to verify there is enough stack space remaining + // for the additional locals. + __ cmpl(rdx, (page_size - overhead_size) / Interpreter::stackElementSize()); + __ jcc(Assembler::belowEqual, after_frame_check); + + // compute rsp as if this were going to be the last frame on + // the stack before the red zone + + const Address stack_base(r15_thread, Thread::stack_base_offset()); + const Address stack_size(r15_thread, Thread::stack_size_offset()); + + // locals + overhead, in bytes + __ movq(rax, rdx); + __ shll(rax, Interpreter::logStackElementSize()); // 2 slots per parameter. + __ addq(rax, overhead_size); + +#ifdef ASSERT + Label stack_base_okay, stack_size_okay; + // verify that thread stack base is non-zero + __ cmpq(stack_base, 0); + __ jcc(Assembler::notEqual, stack_base_okay); + __ stop("stack base is zero"); + __ bind(stack_base_okay); + // verify that thread stack size is non-zero + __ cmpq(stack_size, 0); + __ jcc(Assembler::notEqual, stack_size_okay); + __ stop("stack size is zero"); + __ bind(stack_size_okay); +#endif + + // Add stack base to locals and subtract stack size + __ addq(rax, stack_base); + __ subq(rax, stack_size); + + // add in the red and yellow zone sizes + __ addq(rax, (StackRedPages + StackYellowPages) * page_size); + + // check against the current stack bottom + __ cmpq(rsp, rax); + __ jcc(Assembler::above, after_frame_check); + + __ popq(rax); // get return address + __ jump(ExternalAddress(Interpreter::throw_StackOverflowError_entry())); + + // all done with frame size check + __ bind(after_frame_check); +} + +// Allocate monitor and lock method (asm interpreter) +// +// Args: +// rbx: methodOop +// r14: locals +// +// Kills: +// rax +// c_rarg0, c_rarg1, c_rarg2, c_rarg3, ...(param regs) +// rscratch1, rscratch2 (scratch regs) +void InterpreterGenerator::lock_method(void) { + // synchronize method + const Address access_flags(rbx, methodOopDesc::access_flags_offset()); + const Address monitor_block_top( + rbp, + frame::interpreter_frame_monitor_block_top_offset * wordSize); + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + +#ifdef ASSERT + { + Label L; + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::notZero, L); + __ stop("method doesn't need synchronization"); + __ bind(L); + } +#endif // ASSERT + + // get synchronization object + { + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + + Klass::java_mirror_offset_in_bytes(); + Label done; + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_STATIC); + // get receiver (assume this is frequent case) + __ movq(rax, Address(r14, Interpreter::local_offset_in_bytes(0))); + __ jcc(Assembler::zero, done); + __ movq(rax, Address(rbx, methodOopDesc::constants_offset())); + __ movq(rax, Address(rax, + constantPoolOopDesc::pool_holder_offset_in_bytes())); + __ movq(rax, Address(rax, mirror_offset)); + +#ifdef ASSERT + { + Label L; + __ testq(rax, rax); + __ jcc(Assembler::notZero, L); + __ stop("synchronization object is NULL"); + __ bind(L); + } +#endif // ASSERT + + __ bind(done); + } + + // add space for monitor & lock + __ subq(rsp, entry_size); // add space for a monitor entry + __ movq(monitor_block_top, rsp); // set new monitor block top + // store object + __ movq(Address(rsp, BasicObjectLock::obj_offset_in_bytes()), rax); + __ movq(c_rarg1, rsp); // object address + __ lock_object(c_rarg1); +} + +// Generate a fixed interpreter frame. This is identical setup for +// interpreted methods and for native methods hence the shared code. +// +// Args: +// rax: return address +// rbx: methodOop +// r14: pointer to locals +// r13: sender sp +// rdx: cp cache +void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) { + // initialize fixed part of activation frame + __ pushq(rax); // save return address + __ enter(); // save old & set new rbp + __ pushq(r13); // set sender sp + __ pushq((int)NULL_WORD); // leave last_sp as null + __ movq(r13, Address(rbx, methodOopDesc::const_offset())); // get constMethodOop + __ leaq(r13, Address(r13, constMethodOopDesc::codes_offset())); // get codebase + __ pushq(rbx); // save methodOop + if (ProfileInterpreter) { + Label method_data_continue; + __ movq(rdx, Address(rbx, in_bytes(methodOopDesc::method_data_offset()))); + __ testq(rdx, rdx); + __ jcc(Assembler::zero, method_data_continue); + __ addq(rdx, in_bytes(methodDataOopDesc::data_offset())); + __ bind(method_data_continue); + __ pushq(rdx); // set the mdp (method data pointer) + } else { + __ pushq(0); + } + + __ movq(rdx, Address(rbx, methodOopDesc::constants_offset())); + __ movq(rdx, Address(rdx, constantPoolOopDesc::cache_offset_in_bytes())); + __ pushq(rdx); // set constant pool cache + __ pushq(r14); // set locals pointer + if (native_call) { + __ pushq(0); // no bcp + } else { + __ pushq(r13); // set bcp + } + __ pushq(0); // reserve word for pointer to expression stack bottom + __ movq(Address(rsp, 0), rsp); // set expression stack bottom +} + +// End of helpers + +// Interpreter stub for calling a native method. (asm interpreter) +// This sets up a somewhat different looking stack for calling the +// native method than the typical interpreter frame setup. +address InterpreterGenerator::generate_native_entry(bool synchronized) { + // determine code generation flags + bool inc_counter = UseCompiler || CountCompiledCalls; + + // rbx: methodOop + // r13: sender sp + + address entry_point = __ pc(); + + const Address size_of_parameters(rbx, methodOopDesc:: + size_of_parameters_offset()); + const Address invocation_counter(rbx, methodOopDesc:: + invocation_counter_offset() + + InvocationCounter::counter_offset()); + const Address access_flags (rbx, methodOopDesc::access_flags_offset()); + + // get parameter size (always needed) + __ load_unsigned_word(rcx, size_of_parameters); + + // native calls don't need the stack size check since they have no + // expression stack and the arguments are already on the stack and + // we only add a handful of words to the stack + + // rbx: methodOop + // rcx: size of parameters + // r13: sender sp + __ popq(rax); // get return address + + // for natives the size of locals is zero + + // compute beginning of parameters (r14) + if (TaggedStackInterpreter) __ shll(rcx, 1); // 2 slots per parameter. + __ leaq(r14, Address(rsp, rcx, Address::times_8, -wordSize)); + + // add 2 zero-initialized slots for native calls + // initialize result_handler slot + __ pushq((int) NULL); + // slot for oop temp + // (static native method holder mirror/jni oop result) + __ pushq((int) NULL); + + if (inc_counter) { + __ movl(rcx, invocation_counter); // (pre-)fetch invocation count + } + + // initialize fixed part of activation frame + generate_fixed_frame(true); + + // make sure method is native & not abstract +#ifdef ASSERT + __ movl(rax, access_flags); + { + Label L; + __ testl(rax, JVM_ACC_NATIVE); + __ jcc(Assembler::notZero, L); + __ stop("tried to execute non-native method as native"); + __ bind(L); + } + { + Label L; + __ testl(rax, JVM_ACC_ABSTRACT); + __ jcc(Assembler::zero, L); + __ stop("tried to execute abstract method in interpreter"); + __ bind(L); + } +#endif + + // Since at this point in the method invocation the exception handler + // would try to exit the monitor of synchronized methods which hasn't + // been entered yet, we set the thread local variable + // _do_not_unlock_if_synchronized to true. The remove_activation will + // check this flag. + + const Address do_not_unlock_if_synchronized(r15_thread, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + __ movbool(do_not_unlock_if_synchronized, true); + + // increment invocation count & check for overflow + Label invocation_counter_overflow; + if (inc_counter) { + generate_counter_incr(&invocation_counter_overflow, NULL, NULL); + } + + Label continue_after_compile; + __ bind(continue_after_compile); + + bang_stack_shadow_pages(true); + + // reset the _do_not_unlock_if_synchronized flag + __ movbool(do_not_unlock_if_synchronized, false); + + // check for synchronized methods + // Must happen AFTER invocation_counter check and stack overflow check, + // so method is not locked if overflows. + if (synchronized) { + lock_method(); + } else { + // no synchronization necessary +#ifdef ASSERT + { + Label L; + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::zero, L); + __ stop("method needs synchronization"); + __ bind(L); + } +#endif + } + + // start execution +#ifdef ASSERT + { + Label L; + const Address monitor_block_top(rbp, + frame::interpreter_frame_monitor_block_top_offset * wordSize); + __ movq(rax, monitor_block_top); + __ cmpq(rax, rsp); + __ jcc(Assembler::equal, L); + __ stop("broken stack frame setup in interpreter"); + __ bind(L); + } +#endif + + // jvmti support + __ notify_method_entry(); + + // work registers + const Register method = rbx; + const Register t = r12; + + // allocate space for parameters + __ get_method(method); + __ verify_oop(method); + __ load_unsigned_word(t, + Address(method, + methodOopDesc::size_of_parameters_offset())); + __ shll(t, Interpreter::logStackElementSize()); + + __ subq(rsp, t); + __ subq(rsp, frame::arg_reg_save_area_bytes); // windows + __ andq(rsp, -16); // must be 16 byte boundry (see amd64 ABI) + + // get signature handler + { + Label L; + __ movq(t, Address(method, methodOopDesc::signature_handler_offset())); + __ testq(t, t); + __ jcc(Assembler::notZero, L); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::prepare_native_call), + method); + __ get_method(method); + __ movq(t, Address(method, methodOopDesc::signature_handler_offset())); + __ bind(L); + } + + // call signature handler + assert(InterpreterRuntime::SignatureHandlerGenerator::from() == r14, + "adjust this code"); + assert(InterpreterRuntime::SignatureHandlerGenerator::to() == rsp, + "adjust this code"); + assert(InterpreterRuntime::SignatureHandlerGenerator::temp() == rscratch1, + "adjust this code"); + + // The generated handlers do not touch RBX (the method oop). + // However, large signatures cannot be cached and are generated + // each time here. The slow-path generator can do a GC on return, + // so we must reload it after the call. + __ call(t); + __ get_method(method); // slow path can do a GC, reload RBX + + + // result handler is in rax + // set result handler + __ movq(Address(rbp, + (frame::interpreter_frame_result_handler_offset) * wordSize), + rax); + + // pass mirror handle if static call + { + Label L; + const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + + Klass::java_mirror_offset_in_bytes(); + __ movl(t, Address(method, methodOopDesc::access_flags_offset())); + __ testl(t, JVM_ACC_STATIC); + __ jcc(Assembler::zero, L); + // get mirror + __ movq(t, Address(method, methodOopDesc::constants_offset())); + __ movq(t, Address(t, constantPoolOopDesc::pool_holder_offset_in_bytes())); + __ movq(t, Address(t, mirror_offset)); + // copy mirror into activation frame + __ movq(Address(rbp, frame::interpreter_frame_oop_temp_offset * wordSize), + t); + // pass handle to mirror + __ leaq(c_rarg1, + Address(rbp, frame::interpreter_frame_oop_temp_offset * wordSize)); + __ bind(L); + } + + // get native function entry point + { + Label L; + __ movq(rax, Address(method, methodOopDesc::native_function_offset())); + ExternalAddress unsatisfied(SharedRuntime::native_method_throw_unsatisfied_link_error_entry()); + __ movptr(rscratch2, unsatisfied.addr()); + __ cmpq(rax, rscratch2); + __ jcc(Assembler::notEqual, L); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::prepare_native_call), + method); + __ get_method(method); + __ verify_oop(method); + __ movq(rax, Address(method, methodOopDesc::native_function_offset())); + __ bind(L); + } + + // pass JNIEnv + __ leaq(c_rarg0, Address(r15_thread, JavaThread::jni_environment_offset())); + + // It is enough that the pc() points into the right code + // segment. It does not have to be the correct return pc. + __ set_last_Java_frame(rsp, rbp, (address) __ pc()); + + // change thread state +#ifdef ASSERT + { + Label L; + __ movl(t, Address(r15_thread, JavaThread::thread_state_offset())); + __ cmpl(t, _thread_in_Java); + __ jcc(Assembler::equal, L); + __ stop("Wrong thread state in native stub"); + __ bind(L); + } +#endif + + // Change state to native + + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), + _thread_in_native); + + // Call the native method. + __ call(rax); + // result potentially in rax or xmm0 + + // Depending on runtime options, either restore the MXCSR + // register after returning from the JNI Call or verify that + // it wasn't changed during -Xcheck:jni. + if (RestoreMXCSROnJNICalls) { + __ ldmxcsr(ExternalAddress(StubRoutines::amd64::mxcsr_std())); + } + else if (CheckJNICalls) { + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::amd64::verify_mxcsr_entry()))); + } + + // NOTE: The order of these pushes is known to frame::interpreter_frame_result + // in order to extract the result of a method call. If the order of these + // pushes change or anything else is added to the stack then the code in + // interpreter_frame_result must also change. + + __ push(dtos); + __ push(ltos); + + // change thread state + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), + _thread_in_native_trans); + + if (os::is_MP()) { + if (UseMembar) { + // Force this write out before the read below + __ membar(Assembler::Membar_mask_bits( + Assembler::LoadLoad | Assembler::LoadStore | + Assembler::StoreLoad | Assembler::StoreStore)); + } else { + // Write serialization page so VM thread can do a pseudo remote membar. + // We use the current thread pointer to calculate a thread specific + // offset to write to within the page. This minimizes bus traffic + // due to cache line collision. + __ serialize_memory(r15_thread, rscratch2); + } + } + + // check for safepoint operation in progress and/or pending suspend requests + { + Label Continue; + __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), + SafepointSynchronize::_not_synchronized); + + Label L; + __ jcc(Assembler::notEqual, L); + __ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0); + __ jcc(Assembler::equal, Continue); + __ bind(L); + + // Don't use call_VM as it will see a possible pending exception + // and forward it and never return here preventing us from + // clearing _last_native_pc down below. Also can't use + // call_VM_leaf either as it will check to see if r13 & r14 are + // preserved and correspond to the bcp/locals pointers. So we do a + // runtime call by hand. + // + __ movq(c_rarg0, r15_thread); + __ movq(r12, rsp); // remember sp + __ subq(rsp, frame::arg_reg_save_area_bytes); // windows + __ andq(rsp, -16); // align stack as required by ABI + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); + __ movq(rsp, r12); // restore sp + __ bind(Continue); + } + + // change thread state + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java); + + // reset_last_Java_frame + __ reset_last_Java_frame(true, true); + + // reset handle block + __ movq(t, Address(r15_thread, JavaThread::active_handles_offset())); + __ movptr(Address(t, JNIHandleBlock::top_offset_in_bytes()), NULL_WORD); + + // If result is an oop unbox and store it in frame where gc will see it + // and result handler will pick it up + + { + Label no_oop, store_result; + __ lea(t, ExternalAddress(AbstractInterpreter::result_handler(T_OBJECT))); + __ cmpq(t, Address(rbp, frame::interpreter_frame_result_handler_offset*wordSize)); + __ jcc(Assembler::notEqual, no_oop); + // retrieve result + __ pop(ltos); + __ testq(rax, rax); + __ jcc(Assembler::zero, store_result); + __ movq(rax, Address(rax, 0)); + __ bind(store_result); + __ movq(Address(rbp, frame::interpreter_frame_oop_temp_offset*wordSize), rax); + // keep stack depth as expected by pushing oop which will eventually be discarde + __ push(ltos); + __ bind(no_oop); + } + + + { + Label no_reguard; + __ cmpl(Address(r15_thread, JavaThread::stack_guard_state_offset()), + JavaThread::stack_guard_yellow_disabled); + __ jcc(Assembler::notEqual, no_reguard); + + __ pushaq(); // XXX only save smashed registers + __ movq(r12, rsp); // remember sp + __ subq(rsp, frame::arg_reg_save_area_bytes); // windows + __ andq(rsp, -16); // align stack as required by ABI + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages))); + __ movq(rsp, r12); // restore sp + __ popaq(); // XXX only restore smashed registers + + __ bind(no_reguard); + } + + + // The method register is junk from after the thread_in_native transition + // until here. Also can't call_VM until the bcp has been + // restored. Need bcp for throwing exception below so get it now. + __ get_method(method); + __ verify_oop(method); + + // restore r13 to have legal interpreter frame, i.e., bci == 0 <=> + // r13 == code_base() + __ movq(r13, Address(method, methodOopDesc::const_offset())); // get constMethodOop + __ leaq(r13, Address(r13, constMethodOopDesc::codes_offset())); // get codebase + // handle exceptions (exception handling will handle unlocking!) + { + Label L; + __ cmpq(Address(r15_thread, Thread::pending_exception_offset()), (int) NULL); + __ jcc(Assembler::zero, L); + // Note: At some point we may want to unify this with the code + // used in call_VM_base(); i.e., we should use the + // StubRoutines::forward_exception code. For now this doesn't work + // here because the rsp is not correctly set at this point. + __ MacroAssembler::call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_pending_exception)); + __ should_not_reach_here(); + __ bind(L); + } + + // do unlocking if necessary + { + Label L; + __ movl(t, Address(method, methodOopDesc::access_flags_offset())); + __ testl(t, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::zero, L); + // the code below should be shared with interpreter macro + // assembler implementation + { + Label unlock; + // BasicObjectLock will be first in list, since this is a + // synchronized method. However, need to check that the object + // has not been unlocked by an explicit monitorexit bytecode. + const Address monitor(rbp, + (intptr_t)(frame::interpreter_frame_initial_sp_offset * + wordSize - sizeof(BasicObjectLock))); + + // monitor expect in c_rarg1 for slow unlock path + __ leaq(c_rarg1, monitor); // address of first monitor + + __ movq(t, Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes())); + __ testq(t, t); + __ jcc(Assembler::notZero, unlock); + + // Entry already unlocked, need to throw exception + __ MacroAssembler::call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_illegal_monitor_state_exception)); + __ should_not_reach_here(); + + __ bind(unlock); + __ unlock_object(c_rarg1); + } + __ bind(L); + } + + // jvmti support + // Note: This must happen _after_ handling/throwing any exceptions since + // the exception handler code notifies the runtime of method exits + // too. If this happens before, method entry/exit notifications are + // not properly paired (was bug - gri 11/22/99). + __ notify_method_exit(vtos, InterpreterMacroAssembler::NotifyJVMTI); + + // restore potential result in edx:eax, call result handler to + // restore potential result in ST0 & handle result + + __ pop(ltos); + __ pop(dtos); + + __ movq(t, Address(rbp, + (frame::interpreter_frame_result_handler_offset) * wordSize)); + __ call(t); + + // remove activation + __ movq(t, Address(rbp, + frame::interpreter_frame_sender_sp_offset * + wordSize)); // get sender sp + __ leave(); // remove frame anchor + __ popq(rdi); // get return address + __ movq(rsp, t); // set sp to sender sp + __ jmp(rdi); + + if (inc_counter) { + // Handle overflow of counter and compile method + __ bind(invocation_counter_overflow); + generate_counter_overflow(&continue_after_compile); + } + + return entry_point; +} + +// +// Generic interpreted method entry to (asm) interpreter +// +address InterpreterGenerator::generate_normal_entry(bool synchronized) { + // determine code generation flags + bool inc_counter = UseCompiler || CountCompiledCalls; + + // ebx: methodOop + // r13: sender sp + address entry_point = __ pc(); + + const Address size_of_parameters(rbx, + methodOopDesc::size_of_parameters_offset()); + const Address size_of_locals(rbx, methodOopDesc::size_of_locals_offset()); + const Address invocation_counter(rbx, + methodOopDesc::invocation_counter_offset() + + InvocationCounter::counter_offset()); + const Address access_flags(rbx, methodOopDesc::access_flags_offset()); + + // get parameter size (always needed) + __ load_unsigned_word(rcx, size_of_parameters); + + // rbx: methodOop + // rcx: size of parameters + // r13: sender_sp (could differ from sp+wordSize if we were called via c2i ) + + __ load_unsigned_word(rdx, size_of_locals); // get size of locals in words + __ subl(rdx, rcx); // rdx = no. of additional locals + + // YYY +// __ incrementl(rdx); +// __ andl(rdx, -2); + + // see if we've got enough room on the stack for locals plus overhead. + generate_stack_overflow_check(); + + // get return address + __ popq(rax); + + // compute beginning of parameters (r14) + if (TaggedStackInterpreter) __ shll(rcx, 1); // 2 slots per parameter. + __ leaq(r14, Address(rsp, rcx, Address::times_8, -wordSize)); + + // rdx - # of additional locals + // allocate space for locals + // explicitly initialize locals + { + Label exit, loop; + __ testl(rdx, rdx); + __ jcc(Assembler::lessEqual, exit); // do nothing if rdx <= 0 + __ bind(loop); + if (TaggedStackInterpreter) __ pushq((int) NULL); // push tag + __ pushq((int) NULL); // initialize local variables + __ decrementl(rdx); // until everything initialized + __ jcc(Assembler::greater, loop); + __ bind(exit); + } + + // (pre-)fetch invocation count + if (inc_counter) { + __ movl(rcx, invocation_counter); + } + // initialize fixed part of activation frame + generate_fixed_frame(false); + + // make sure method is not native & not abstract +#ifdef ASSERT + __ movl(rax, access_flags); + { + Label L; + __ testl(rax, JVM_ACC_NATIVE); + __ jcc(Assembler::zero, L); + __ stop("tried to execute native method as non-native"); + __ bind(L); + } + { + Label L; + __ testl(rax, JVM_ACC_ABSTRACT); + __ jcc(Assembler::zero, L); + __ stop("tried to execute abstract method in interpreter"); + __ bind(L); + } +#endif + + // Since at this point in the method invocation the exception + // handler would try to exit the monitor of synchronized methods + // which hasn't been entered yet, we set the thread local variable + // _do_not_unlock_if_synchronized to true. The remove_activation + // will check this flag. + + const Address do_not_unlock_if_synchronized(r15_thread, + in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); + __ movbool(do_not_unlock_if_synchronized, true); + + // increment invocation count & check for overflow + Label invocation_counter_overflow; + Label profile_method; + Label profile_method_continue; + if (inc_counter) { + generate_counter_incr(&invocation_counter_overflow, + &profile_method, + &profile_method_continue); + if (ProfileInterpreter) { + __ bind(profile_method_continue); + } + } + + Label continue_after_compile; + __ bind(continue_after_compile); + + // check for synchronized interpreted methods + bang_stack_shadow_pages(false); + + // reset the _do_not_unlock_if_synchronized flag + __ movbool(do_not_unlock_if_synchronized, false); + + // check for synchronized methods + // Must happen AFTER invocation_counter check and stack overflow check, + // so method is not locked if overflows. + if (synchronized) { + // Allocate monitor and lock method + lock_method(); + } else { + // no synchronization necessary +#ifdef ASSERT + { + Label L; + __ movl(rax, access_flags); + __ testl(rax, JVM_ACC_SYNCHRONIZED); + __ jcc(Assembler::zero, L); + __ stop("method needs synchronization"); + __ bind(L); + } +#endif + } + + // start execution +#ifdef ASSERT + { + Label L; + const Address monitor_block_top (rbp, + frame::interpreter_frame_monitor_block_top_offset * wordSize); + __ movq(rax, monitor_block_top); + __ cmpq(rax, rsp); + __ jcc(Assembler::equal, L); + __ stop("broken stack frame setup in interpreter"); + __ bind(L); + } +#endif + + // jvmti support + __ notify_method_entry(); + + __ dispatch_next(vtos); + + // invocation counter overflow + if (inc_counter) { + if (ProfileInterpreter) { + // We have decided to profile this method in the interpreter + __ bind(profile_method); + + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method), + r13, true); + + __ movq(rbx, Address(rbp, method_offset)); // restore methodOop + __ movq(rax, Address(rbx, + in_bytes(methodOopDesc::method_data_offset()))); + __ movq(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), + rax); + __ test_method_data_pointer(rax, profile_method_continue); + __ addq(rax, in_bytes(methodDataOopDesc::data_offset())); + __ movq(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), + rax); + __ jmp(profile_method_continue); + } + // Handle overflow of counter and compile method + __ bind(invocation_counter_overflow); + generate_counter_overflow(&continue_after_compile); + } + + return entry_point; +} + +// Entry points +// +// Here we generate the various kind of entries into the interpreter. +// The two main entry type are generic bytecode methods and native +// call method. These both come in synchronized and non-synchronized +// versions but the frame layout they create is very similar. The +// other method entry types are really just special purpose entries +// that are really entry and interpretation all in one. These are for +// trivial methods like accessor, empty, or special math methods. +// +// When control flow reaches any of the entry types for the interpreter +// the following holds -> +// +// Arguments: +// +// rbx: methodOop +// +// Stack layout immediately at entry +// +// [ return address ] <--- rsp +// [ parameter n ] +// ... +// [ parameter 1 ] +// [ expression stack ] (caller's java expression stack) + +// Assuming that we don't go to one of the trivial specialized entries +// the stack will look like below when we are ready to execute the +// first bytecode (or call the native routine). The register usage +// will be as the template based interpreter expects (see +// interpreter_amd64.hpp). +// +// local variables follow incoming parameters immediately; i.e. +// the return address is moved to the end of the locals). +// +// [ monitor entry ] <--- rsp +// ... +// [ monitor entry ] +// [ expr. stack bottom ] +// [ saved r13 ] +// [ current r14 ] +// [ methodOop ] +// [ saved ebp ] <--- rbp +// [ return address ] +// [ local variable m ] +// ... +// [ local variable 1 ] +// [ parameter n ] +// ... +// [ parameter 1 ] <--- r14 + +address AbstractInterpreterGenerator::generate_method_entry( + AbstractInterpreter::MethodKind kind) { + // determine code generation flags + bool synchronized = false; + address entry_point = NULL; + + switch (kind) { + case Interpreter::zerolocals : break; + case Interpreter::zerolocals_synchronized: synchronized = true; break; + case Interpreter::native : entry_point = ((InterpreterGenerator*) this)->generate_native_entry(false); break; + case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*) this)->generate_native_entry(true); break; + case Interpreter::empty : entry_point = ((InterpreterGenerator*) this)->generate_empty_entry(); break; + case Interpreter::accessor : entry_point = ((InterpreterGenerator*) this)->generate_accessor_entry(); break; + case Interpreter::abstract : entry_point = ((InterpreterGenerator*) this)->generate_abstract_entry(); break; + case Interpreter::java_lang_math_sin : break; + case Interpreter::java_lang_math_cos : break; + case Interpreter::java_lang_math_tan : break; + case Interpreter::java_lang_math_abs : break; + case Interpreter::java_lang_math_log : break; + case Interpreter::java_lang_math_log10 : break; + case Interpreter::java_lang_math_sqrt : entry_point = ((InterpreterGenerator*) this)->generate_math_entry(kind); break; + default : ShouldNotReachHere(); break; + } + + if (entry_point) { + return entry_point; + } + + return ((InterpreterGenerator*) this)-> + generate_normal_entry(synchronized); +} + +// How much stack a method activation needs in words. +int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { + const int entry_size = frame::interpreter_frame_monitor_size(); + + // total overhead size: entry_size + (saved rbp thru expr stack + // bottom). be sure to change this if you add/subtract anything + // to/from the overhead area + const int overhead_size = + -(frame::interpreter_frame_initial_sp_offset) + entry_size; + + const int stub_code = frame::entry_frame_after_call_words; + const int method_stack = (method->max_locals() + method->max_stack()) * + Interpreter::stackElementWords(); + return (overhead_size + method_stack + stub_code); +} + +int AbstractInterpreter::layout_activation(methodOop method, + int tempcount, + int popframe_extra_args, + int moncount, + int callee_param_count, + int callee_locals, + frame* caller, + frame* interpreter_frame, + bool is_top_frame) { + // Note: This calculation must exactly parallel the frame setup + // in AbstractInterpreterGenerator::generate_method_entry. + // If interpreter_frame!=NULL, set up the method, locals, and monitors. + // The frame interpreter_frame, if not NULL, is guaranteed to be the + // right size, as determined by a previous call to this method. + // It is also guaranteed to be walkable even though it is in a skeletal state + + // fixed size of an interpreter frame: + int max_locals = method->max_locals() * Interpreter::stackElementWords(); + int extra_locals = (method->max_locals() - method->size_of_parameters()) * + Interpreter::stackElementWords(); + + int overhead = frame::sender_sp_offset - + frame::interpreter_frame_initial_sp_offset; + // Our locals were accounted for by the caller (or last_frame_adjust + // on the transistion) Since the callee parameters already account + // for the callee's params we only need to account for the extra + // locals. + int size = overhead + + (callee_locals - callee_param_count)*Interpreter::stackElementWords() + + moncount * frame::interpreter_frame_monitor_size() + + tempcount* Interpreter::stackElementWords() + popframe_extra_args; + if (interpreter_frame != NULL) { +#ifdef ASSERT + assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), + "Frame not properly walkable"); + assert(caller->sp() == interpreter_frame->sender_sp(), "Frame not properly walkable(2)"); +#endif + + interpreter_frame->interpreter_frame_set_method(method); + // NOTE the difference in using sender_sp and + // interpreter_frame_sender_sp interpreter_frame_sender_sp is + // the original sp of the caller (the unextended_sp) and + // sender_sp is fp+16 XXX + intptr_t* locals = interpreter_frame->sender_sp() + max_locals - 1; + + interpreter_frame->interpreter_frame_set_locals(locals); + BasicObjectLock* montop = interpreter_frame->interpreter_frame_monitor_begin(); + BasicObjectLock* monbot = montop - moncount; + interpreter_frame->interpreter_frame_set_monitor_end(monbot); + + // Set last_sp + intptr_t* esp = (intptr_t*) monbot - + tempcount*Interpreter::stackElementWords() - + popframe_extra_args; + interpreter_frame->interpreter_frame_set_last_sp(esp); + + // All frames but the initial (oldest) interpreter frame we fill in have + // a value for sender_sp that allows walking the stack but isn't + // truly correct. Correct the value here. + if (extra_locals != 0 && + interpreter_frame->sender_sp() == + interpreter_frame->interpreter_frame_sender_sp()) { + interpreter_frame->set_interpreter_frame_sender_sp(caller->sp() + + extra_locals); + } + *interpreter_frame->interpreter_frame_cache_addr() = + method->constants()->cache(); + } + return size; +} + +//----------------------------------------------------------------------------- +// Exceptions + +void TemplateInterpreterGenerator::generate_throw_exception() { + // Entry point in previous activation (i.e., if the caller was + // interpreted) + Interpreter::_rethrow_exception_entry = __ pc(); + // Restore sp to interpreter_frame_last_sp even though we are going + // to empty the expression stack for the exception processing. + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + // rax: exception + // rdx: return address/pc that threw exception + __ restore_bcp(); // r13 points to call/send + __ restore_locals(); + // Entry point for exceptions thrown within interpreter code + Interpreter::_throw_exception_entry = __ pc(); + // expression stack is undefined here + // rax: exception + // r13: exception bcp + __ verify_oop(rax); + __ movq(c_rarg1, rax); + + // expression stack must be empty before entering the VM in case of + // an exception + __ empty_expression_stack(); + // find exception handler address and preserve exception oop + __ call_VM(rdx, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::exception_handler_for_exception), + c_rarg1); + // rax: exception handler entry point + // rdx: preserved exception oop + // r13: bcp for exception handler + __ push_ptr(rdx); // push exception which is now the only value on the stack + __ jmp(rax); // jump to exception handler (may be _remove_activation_entry!) + + // If the exception is not handled in the current frame the frame is + // removed and the exception is rethrown (i.e. exception + // continuation is _rethrow_exception). + // + // Note: At this point the bci is still the bxi for the instruction + // which caused the exception and the expression stack is + // empty. Thus, for any VM calls at this point, GC will find a legal + // oop map (with empty expression stack). + + // In current activation + // tos: exception + // esi: exception bcp + + // + // JVMTI PopFrame support + // + + Interpreter::_remove_activation_preserving_args_entry = __ pc(); + __ empty_expression_stack(); + // Set the popframe_processing bit in pending_popframe_condition + // indicating that we are currently handling popframe, so that + // call_VMs that may happen later do not trigger new popframe + // handling cycles. + __ movl(rdx, Address(r15_thread, JavaThread::popframe_condition_offset())); + __ orl(rdx, JavaThread::popframe_processing_bit); + __ movl(Address(r15_thread, JavaThread::popframe_condition_offset()), rdx); + + { + // Check to see whether we are returning to a deoptimized frame. + // (The PopFrame call ensures that the caller of the popped frame is + // either interpreted or compiled and deoptimizes it if compiled.) + // In this case, we can't call dispatch_next() after the frame is + // popped, but instead must save the incoming arguments and restore + // them after deoptimization has occurred. + // + // Note that we don't compare the return PC against the + // deoptimization blob's unpack entry because of the presence of + // adapter frames in C2. + Label caller_not_deoptimized; + __ movq(c_rarg1, Address(rbp, frame::return_addr_offset * wordSize)); + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, + InterpreterRuntime::interpreter_contains), c_rarg1); + __ testl(rax, rax); + __ jcc(Assembler::notZero, caller_not_deoptimized); + + // Compute size of arguments for saving when returning to + // deoptimized caller + __ get_method(rax); + __ load_unsigned_word(rax, Address(rax, in_bytes(methodOopDesc:: + size_of_parameters_offset()))); + __ shll(rax, Interpreter::logStackElementSize()); + __ restore_locals(); // XXX do we need this? + __ subq(r14, rax); + __ addq(r14, wordSize); + // Save these arguments + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, + Deoptimization:: + popframe_preserve_args), + r15_thread, rax, r14); + + __ remove_activation(vtos, rdx, + /* throw_monitor_exception */ false, + /* install_monitor_exception */ false, + /* notify_jvmdi */ false); + + // Inform deoptimization that it is responsible for restoring + // these arguments + __ movl(Address(r15_thread, JavaThread::popframe_condition_offset()), + JavaThread::popframe_force_deopt_reexecution_bit); + + // Continue in deoptimization handler + __ jmp(rdx); + + __ bind(caller_not_deoptimized); + } + + __ remove_activation(vtos, rdx, /* rdx result (retaddr) is not used */ + /* throw_monitor_exception */ false, + /* install_monitor_exception */ false, + /* notify_jvmdi */ false); + + // Finish with popframe handling + // A previous I2C followed by a deoptimization might have moved the + // outgoing arguments further up the stack. PopFrame expects the + // mutations to those outgoing arguments to be preserved and other + // constraints basically require this frame to look exactly as + // though it had previously invoked an interpreted activation with + // no space between the top of the expression stack (current + // last_sp) and the top of stack. Rather than force deopt to + // maintain this kind of invariant all the time we call a small + // fixup routine to move the mutated arguments onto the top of our + // expression stack if necessary. + __ movq(c_rarg1, rsp); + __ movq(c_rarg2, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize)); + // PC must point into interpreter here + __ set_last_Java_frame(noreg, rbp, __ pc()); + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::popframe_move_outgoing_args), r15_thread, c_rarg1, c_rarg2); + __ reset_last_Java_frame(true, true); + // Restore the last_sp and null it out + __ movq(rsp, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize)); + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + + __ restore_bcp(); // XXX do we need this? + __ restore_locals(); // XXX do we need this? + // The method data pointer was incremented already during + // call profiling. We have to restore the mdp for the current bcp. + if (ProfileInterpreter) { + __ set_method_data_pointer_for_bcp(); + } + + // Clear the popframe condition flag + __ movl(Address(r15_thread, JavaThread::popframe_condition_offset()), + JavaThread::popframe_inactive); + + __ dispatch_next(vtos); + // end of PopFrame support + + Interpreter::_remove_activation_entry = __ pc(); + + // preserve exception over this code sequence + __ pop_ptr(rax); + __ movq(Address(r15_thread, JavaThread::vm_result_offset()), rax); + // remove the activation (without doing throws on illegalMonitorExceptions) + __ remove_activation(vtos, rdx, false, true, false); + // restore exception + __ movq(rax, Address(r15_thread, JavaThread::vm_result_offset())); + __ movptr(Address(r15_thread, JavaThread::vm_result_offset()), NULL_WORD); + __ verify_oop(rax); + + // In between activations - previous activation type unknown yet + // compute continuation point - the continuation point expects the + // following registers set up: + // + // rax: exception + // rdx: return address/pc that threw exception + // rsp: expression stack of caller + // rbp: ebp of caller + __ pushq(rax); // save exception + __ pushq(rdx); // save return address + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, + SharedRuntime::exception_handler_for_return_address), + rdx); + __ movq(rbx, rax); // save exception handler + __ popq(rdx); // restore return address + __ popq(rax); // restore exception + // Note that an "issuing PC" is actually the next PC after the call + __ jmp(rbx); // jump to exception + // handler of caller +} + + +// +// JVMTI ForceEarlyReturn support +// +address TemplateInterpreterGenerator::generate_earlyret_entry_for(TosState state) { + address entry = __ pc(); + + __ restore_bcp(); + __ restore_locals(); + __ empty_expression_stack(); + __ load_earlyret_value(state); + + __ movq(rdx, Address(r15_thread, JavaThread::jvmti_thread_state_offset())); + Address cond_addr(rdx, JvmtiThreadState::earlyret_state_offset()); + + // Clear the earlyret state + __ movl(cond_addr, JvmtiThreadState::earlyret_inactive); + + __ remove_activation(state, rsi, + false, /* throw_monitor_exception */ + false, /* install_monitor_exception */ + true); /* notify_jvmdi */ + __ jmp(rsi); + + return entry; +} // end of ForceEarlyReturn support + + +//----------------------------------------------------------------------------- +// Helper for vtos entry point generation + +void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, + address& bep, + address& cep, + address& sep, + address& aep, + address& iep, + address& lep, + address& fep, + address& dep, + address& vep) { + assert(t->is_valid() && t->tos_in() == vtos, "illegal template"); + Label L; + aep = __ pc(); __ push_ptr(); __ jmp(L); + fep = __ pc(); __ push_f(); __ jmp(L); + dep = __ pc(); __ push_d(); __ jmp(L); + lep = __ pc(); __ push_l(); __ jmp(L); + bep = cep = sep = + iep = __ pc(); __ push_i(); + vep = __ pc(); + __ bind(L); + generate_and_dispatch(t); +} + + +//----------------------------------------------------------------------------- +// Generation of individual instructions + +// helpers for generate_and_dispatch + + +InterpreterGenerator::InterpreterGenerator(StubQueue* code) + : TemplateInterpreterGenerator(code) { + generate_all(); // down here so it can be "virtual" +} + +//----------------------------------------------------------------------------- + +// Non-product code +#ifndef PRODUCT +address TemplateInterpreterGenerator::generate_trace_code(TosState state) { + address entry = __ pc(); + + __ push(state); + __ pushq(c_rarg0); + __ pushq(c_rarg1); + __ pushq(c_rarg2); + __ pushq(c_rarg3); + __ movq(c_rarg2, rax); // Pass itos +#ifdef _WIN64 + __ movflt(xmm3, xmm0); // Pass ftos +#endif + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, SharedRuntime::trace_bytecode), + c_rarg1, c_rarg2, c_rarg3); + __ popq(c_rarg3); + __ popq(c_rarg2); + __ popq(c_rarg1); + __ popq(c_rarg0); + __ pop(state); + __ ret(0); // return from result handler + + return entry; +} + +void TemplateInterpreterGenerator::count_bytecode() { + __ incrementl(ExternalAddress((address) &BytecodeCounter::_counter_value)); +} + +void TemplateInterpreterGenerator::histogram_bytecode(Template* t) { + __ incrementl(ExternalAddress((address) &BytecodeHistogram::_counters[t->bytecode()])); +} + +void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) { + __ mov32(rbx, ExternalAddress((address) &BytecodePairHistogram::_index)); + __ shrl(rbx, BytecodePairHistogram::log2_number_of_codes); + __ orl(rbx, + ((int) t->bytecode()) << + BytecodePairHistogram::log2_number_of_codes); + __ mov32(ExternalAddress((address) &BytecodePairHistogram::_index), rbx); + __ lea(rscratch1, ExternalAddress((address) BytecodePairHistogram::_counters)); + __ incrementl(Address(rscratch1, rbx, Address::times_4)); +} + + +void TemplateInterpreterGenerator::trace_bytecode(Template* t) { + // Call a little run-time stub to avoid blow-up for each bytecode. + // The run-time runtime saves the right registers, depending on + // the tosca in-state for the given template. + + assert(Interpreter::trace_code(t->tos_in()) != NULL, + "entry must have been generated"); + __ movq(r12, rsp); // remember sp + __ andq(rsp, -16); // align stack as required by ABI + __ call(RuntimeAddress(Interpreter::trace_code(t->tos_in()))); + __ movq(rsp, r12); // restore sp +} + + +void TemplateInterpreterGenerator::stop_interpreter_at() { + Label L; + __ cmp32(ExternalAddress((address) &BytecodeCounter::_counter_value), + StopInterpreterAt); + __ jcc(Assembler::notEqual, L); + __ int3(); + __ bind(L); +} +#endif // !PRODUCT diff --git a/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp b/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp new file mode 100644 index 00000000000..53c908562f9 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp @@ -0,0 +1,3499 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_templateTable_x86_32.cpp.incl" + +#ifndef CC_INTERP +#define __ _masm-> + +//---------------------------------------------------------------------------------------------------- +// Platform-dependent initialization + +void TemplateTable::pd_initialize() { + // No i486 specific initialization +} + +//---------------------------------------------------------------------------------------------------- +// Address computation + +// local variables +static inline Address iaddress(int n) { + return Address(rdi, Interpreter::local_offset_in_bytes(n)); +} + +static inline Address laddress(int n) { return iaddress(n + 1); } +static inline Address haddress(int n) { return iaddress(n + 0); } +static inline Address faddress(int n) { return iaddress(n); } +static inline Address daddress(int n) { return laddress(n); } +static inline Address aaddress(int n) { return iaddress(n); } + +static inline Address iaddress(Register r) { + return Address(rdi, r, Interpreter::stackElementScale(), Interpreter::value_offset_in_bytes()); +} +static inline Address laddress(Register r) { + return Address(rdi, r, Interpreter::stackElementScale(), Interpreter::local_offset_in_bytes(1)); +} +static inline Address haddress(Register r) { + return Address(rdi, r, Interpreter::stackElementScale(), Interpreter::local_offset_in_bytes(0)); +} + +static inline Address faddress(Register r) { return iaddress(r); }; +static inline Address daddress(Register r) { + assert(!TaggedStackInterpreter, "This doesn't work"); + return laddress(r); +}; +static inline Address aaddress(Register r) { return iaddress(r); }; + +// expression stack +// (Note: Must not use symmetric equivalents at_rsp_m1/2 since they store +// data beyond the rsp which is potentially unsafe in an MT environment; +// an interrupt may overwrite that data.) +static inline Address at_rsp () { + return Address(rsp, 0); +} + +// At top of Java expression stack which may be different than rsp(). It +// isn't for category 1 objects. +static inline Address at_tos () { + Address tos = Address(rsp, Interpreter::expr_offset_in_bytes(0)); + return tos; +} + +static inline Address at_tos_p1() { + return Address(rsp, Interpreter::expr_offset_in_bytes(1)); +} + +static inline Address at_tos_p2() { + return Address(rsp, Interpreter::expr_offset_in_bytes(2)); +} + +// Condition conversion +static Assembler::Condition j_not(TemplateTable::Condition cc) { + switch (cc) { + case TemplateTable::equal : return Assembler::notEqual; + case TemplateTable::not_equal : return Assembler::equal; + case TemplateTable::less : return Assembler::greaterEqual; + case TemplateTable::less_equal : return Assembler::greater; + case TemplateTable::greater : return Assembler::lessEqual; + case TemplateTable::greater_equal: return Assembler::less; + } + ShouldNotReachHere(); + return Assembler::zero; +} + + +//---------------------------------------------------------------------------------------------------- +// Miscelaneous helper routines + +Address TemplateTable::at_bcp(int offset) { + assert(_desc->uses_bcp(), "inconsistent uses_bcp information"); + return Address(rsi, offset); +} + + +void TemplateTable::patch_bytecode(Bytecodes::Code bytecode, Register bc, + Register scratch, + bool load_bc_into_scratch/*=true*/) { + + if (!RewriteBytecodes) return; + // the pair bytecodes have already done the load. + if (load_bc_into_scratch) __ movl(bc, bytecode); + Label patch_done; + if (JvmtiExport::can_post_breakpoint()) { + Label fast_patch; + // if a breakpoint is present we can't rewrite the stream directly + __ movzxb(scratch, at_bcp(0)); + __ cmpl(scratch, Bytecodes::_breakpoint); + __ jcc(Assembler::notEqual, fast_patch); + __ get_method(scratch); + // Let breakpoint table handling rewrite to quicker bytecode + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::set_original_bytecode_at), scratch, rsi, bc); +#ifndef ASSERT + __ jmpb(patch_done); + __ bind(fast_patch); + } +#else + __ jmp(patch_done); + __ bind(fast_patch); + } + Label okay; + __ load_unsigned_byte(scratch, at_bcp(0)); + __ cmpl(scratch, (int)Bytecodes::java_code(bytecode)); + __ jccb(Assembler::equal, okay); + __ cmpl(scratch, bc); + __ jcc(Assembler::equal, okay); + __ stop("patching the wrong bytecode"); + __ bind(okay); +#endif + // patch bytecode + __ movb(at_bcp(0), bc); + __ bind(patch_done); +} + +//---------------------------------------------------------------------------------------------------- +// Individual instructions + +void TemplateTable::nop() { + transition(vtos, vtos); + // nothing to do +} + +void TemplateTable::shouldnotreachhere() { + transition(vtos, vtos); + __ stop("shouldnotreachhere bytecode"); +} + + + +void TemplateTable::aconst_null() { + transition(vtos, atos); + __ xorl(rax, rax); +} + + +void TemplateTable::iconst(int value) { + transition(vtos, itos); + if (value == 0) { + __ xorl(rax, rax); + } else { + __ movl(rax, value); + } +} + + +void TemplateTable::lconst(int value) { + transition(vtos, ltos); + if (value == 0) { + __ xorl(rax, rax); + } else { + __ movl(rax, value); + } + assert(value >= 0, "check this code"); + __ xorl(rdx, rdx); +} + + +void TemplateTable::fconst(int value) { + transition(vtos, ftos); + if (value == 0) { __ fldz(); + } else if (value == 1) { __ fld1(); + } else if (value == 2) { __ fld1(); __ fld1(); __ faddp(); // should do a better solution here + } else { ShouldNotReachHere(); + } +} + + +void TemplateTable::dconst(int value) { + transition(vtos, dtos); + if (value == 0) { __ fldz(); + } else if (value == 1) { __ fld1(); + } else { ShouldNotReachHere(); + } +} + + +void TemplateTable::bipush() { + transition(vtos, itos); + __ load_signed_byte(rax, at_bcp(1)); +} + + +void TemplateTable::sipush() { + transition(vtos, itos); + __ load_unsigned_word(rax, at_bcp(1)); + __ bswap(rax); + __ sarl(rax, 16); +} + +void TemplateTable::ldc(bool wide) { + transition(vtos, vtos); + Label call_ldc, notFloat, notClass, Done; + + if (wide) { + __ get_unsigned_2_byte_index_at_bcp(rbx, 1); + } else { + __ load_unsigned_byte(rbx, at_bcp(1)); + } + __ get_cpool_and_tags(rcx, rax); + const int base_offset = constantPoolOopDesc::header_size() * wordSize; + const int tags_offset = typeArrayOopDesc::header_size(T_BYTE) * wordSize; + + // get type + __ xorl(rdx, rdx); + __ movb(rdx, Address(rax, rbx, Address::times_1, tags_offset)); + + // unresolved string - get the resolved string + __ cmpl(rdx, JVM_CONSTANT_UnresolvedString); + __ jccb(Assembler::equal, call_ldc); + + // unresolved class - get the resolved class + __ cmpl(rdx, JVM_CONSTANT_UnresolvedClass); + __ jccb(Assembler::equal, call_ldc); + + // unresolved class in error (resolution failed) - call into runtime + // so that the same error from first resolution attempt is thrown. + __ cmpl(rdx, JVM_CONSTANT_UnresolvedClassInError); + __ jccb(Assembler::equal, call_ldc); + + // resolved class - need to call vm to get java mirror of the class + __ cmpl(rdx, JVM_CONSTANT_Class); + __ jcc(Assembler::notEqual, notClass); + + __ bind(call_ldc); + __ movl(rcx, wide); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), rcx); + __ push(atos); + __ jmp(Done); + + __ bind(notClass); + __ cmpl(rdx, JVM_CONSTANT_Float); + __ jccb(Assembler::notEqual, notFloat); + // ftos + __ fld_s( Address(rcx, rbx, Address::times_4, base_offset)); + __ push(ftos); + __ jmp(Done); + + __ bind(notFloat); +#ifdef ASSERT + { Label L; + __ cmpl(rdx, JVM_CONSTANT_Integer); + __ jcc(Assembler::equal, L); + __ cmpl(rdx, JVM_CONSTANT_String); + __ jcc(Assembler::equal, L); + __ stop("unexpected tag type in ldc"); + __ bind(L); + } +#endif + Label isOop; + // atos and itos + __ movl(rax, Address(rcx, rbx, Address::times_4, base_offset)); + // String is only oop type we will see here + __ cmpl(rdx, JVM_CONSTANT_String); + __ jccb(Assembler::equal, isOop); + __ push(itos); + __ jmp(Done); + __ bind(isOop); + __ push(atos); + + if (VerifyOops) { + __ verify_oop(rax); + } + __ bind(Done); +} + +void TemplateTable::ldc2_w() { + transition(vtos, vtos); + Label Long, Done; + __ get_unsigned_2_byte_index_at_bcp(rbx, 1); + + __ get_cpool_and_tags(rcx, rax); + const int base_offset = constantPoolOopDesc::header_size() * wordSize; + const int tags_offset = typeArrayOopDesc::header_size(T_BYTE) * wordSize; + + // get type + __ cmpb(Address(rax, rbx, Address::times_1, tags_offset), JVM_CONSTANT_Double); + __ jccb(Assembler::notEqual, Long); + // dtos + __ fld_d( Address(rcx, rbx, Address::times_4, base_offset)); + __ push(dtos); + __ jmpb(Done); + + __ bind(Long); + // ltos + __ movl(rax, Address(rcx, rbx, Address::times_4, base_offset + 0 * wordSize)); + __ movl(rdx, Address(rcx, rbx, Address::times_4, base_offset + 1 * wordSize)); + + __ push(ltos); + + __ bind(Done); +} + + +void TemplateTable::locals_index(Register reg, int offset) { + __ load_unsigned_byte(reg, at_bcp(offset)); + __ negl(reg); +} + + +void TemplateTable::iload() { + transition(vtos, itos); + if (RewriteFrequentPairs) { + Label rewrite, done; + + // get next byte + __ load_unsigned_byte(rbx, at_bcp(Bytecodes::length_for(Bytecodes::_iload))); + // if _iload, wait to rewrite to iload2. We only want to rewrite the + // last two iloads in a pair. Comparing against fast_iload means that + // the next bytecode is neither an iload or a caload, and therefore + // an iload pair. + __ cmpl(rbx, Bytecodes::_iload); + __ jcc(Assembler::equal, done); + + __ cmpl(rbx, Bytecodes::_fast_iload); + __ movl(rcx, Bytecodes::_fast_iload2); + __ jccb(Assembler::equal, rewrite); + + // if _caload, rewrite to fast_icaload + __ cmpl(rbx, Bytecodes::_caload); + __ movl(rcx, Bytecodes::_fast_icaload); + __ jccb(Assembler::equal, rewrite); + + // rewrite so iload doesn't check again. + __ movl(rcx, Bytecodes::_fast_iload); + + // rewrite + // rcx: fast bytecode + __ bind(rewrite); + patch_bytecode(Bytecodes::_iload, rcx, rbx, false); + __ bind(done); + } + + // Get the local value into tos + locals_index(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + + +void TemplateTable::fast_iload2() { + transition(vtos, itos); + locals_index(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); + __ push(itos); + locals_index(rbx, 3); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + +void TemplateTable::fast_iload() { + transition(vtos, itos); + locals_index(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + + +void TemplateTable::lload() { + transition(vtos, ltos); + locals_index(rbx); + __ movl(rax, laddress(rbx)); + __ movl(rdx, haddress(rbx)); + debug_only(__ verify_local_tag(frame::TagCategory2, rbx)); +} + + +void TemplateTable::fload() { + transition(vtos, ftos); + locals_index(rbx); + __ fld_s(faddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + + +void TemplateTable::dload() { + transition(vtos, dtos); + locals_index(rbx); + if (TaggedStackInterpreter) { + // Get double out of locals array, onto temp stack and load with + // float instruction into ST0 + __ movl(rax, laddress(rbx)); + __ movl(rdx, haddress(rbx)); + __ pushl(rdx); // push hi first + __ pushl(rax); + __ fld_d(Address(rsp, 0)); + __ addl(rsp, 2*wordSize); + debug_only(__ verify_local_tag(frame::TagCategory2, rbx)); + } else { + __ fld_d(daddress(rbx)); + } +} + + +void TemplateTable::aload() { + transition(vtos, atos); + locals_index(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagReference, rbx)); +} + + +void TemplateTable::locals_index_wide(Register reg) { + __ movl(reg, at_bcp(2)); + __ bswap(reg); + __ shrl(reg, 16); + __ negl(reg); +} + + +void TemplateTable::wide_iload() { + transition(vtos, itos); + locals_index_wide(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + + +void TemplateTable::wide_lload() { + transition(vtos, ltos); + locals_index_wide(rbx); + __ movl(rax, laddress(rbx)); + __ movl(rdx, haddress(rbx)); + debug_only(__ verify_local_tag(frame::TagCategory2, rbx)); +} + + +void TemplateTable::wide_fload() { + transition(vtos, ftos); + locals_index_wide(rbx); + __ fld_s(faddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + + +void TemplateTable::wide_dload() { + transition(vtos, dtos); + locals_index_wide(rbx); + if (TaggedStackInterpreter) { + // Get double out of locals array, onto temp stack and load with + // float instruction into ST0 + __ movl(rax, laddress(rbx)); + __ movl(rdx, haddress(rbx)); + __ pushl(rdx); // push hi first + __ pushl(rax); + __ fld_d(Address(rsp, 0)); + __ addl(rsp, 2*wordSize); + debug_only(__ verify_local_tag(frame::TagCategory2, rbx)); + } else { + __ fld_d(daddress(rbx)); + } +} + + +void TemplateTable::wide_aload() { + transition(vtos, atos); + locals_index_wide(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagReference, rbx)); +} + +void TemplateTable::index_check(Register array, Register index) { + // Pop ptr into array + __ pop_ptr(array); + index_check_without_pop(array, index); +} + +void TemplateTable::index_check_without_pop(Register array, Register index) { + // destroys rbx, + // check array + __ null_check(array, arrayOopDesc::length_offset_in_bytes()); + // check index + __ cmpl(index, Address(array, arrayOopDesc::length_offset_in_bytes())); + if (index != rbx) { + // ??? convention: move aberrant index into rbx, for exception message + assert(rbx != array, "different registers"); + __ movl(rbx, index); + } + __ jump_cc(Assembler::aboveEqual, + ExternalAddress(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry)); +} + + +void TemplateTable::iaload() { + transition(itos, itos); + // rdx: array + index_check(rdx, rax); // kills rbx, + // rax,: index + __ movl(rax, Address(rdx, rax, Address::times_4, arrayOopDesc::base_offset_in_bytes(T_INT))); +} + + +void TemplateTable::laload() { + transition(itos, ltos); + // rax,: index + // rdx: array + index_check(rdx, rax); + __ movl(rbx, rax); + // rbx,: index + __ movl(rax, Address(rdx, rbx, Address::times_8, arrayOopDesc::base_offset_in_bytes(T_LONG) + 0 * wordSize)); + __ movl(rdx, Address(rdx, rbx, Address::times_8, arrayOopDesc::base_offset_in_bytes(T_LONG) + 1 * wordSize)); +} + + +void TemplateTable::faload() { + transition(itos, ftos); + // rdx: array + index_check(rdx, rax); // kills rbx, + // rax,: index + __ fld_s(Address(rdx, rax, Address::times_4, arrayOopDesc::base_offset_in_bytes(T_FLOAT))); +} + + +void TemplateTable::daload() { + transition(itos, dtos); + // rdx: array + index_check(rdx, rax); // kills rbx, + // rax,: index + __ fld_d(Address(rdx, rax, Address::times_8, arrayOopDesc::base_offset_in_bytes(T_DOUBLE))); +} + + +void TemplateTable::aaload() { + transition(itos, atos); + // rdx: array + index_check(rdx, rax); // kills rbx, + // rax,: index + __ movl(rax, Address(rdx, rax, Address::times_4, arrayOopDesc::base_offset_in_bytes(T_OBJECT))); +} + + +void TemplateTable::baload() { + transition(itos, itos); + // rdx: array + index_check(rdx, rax); // kills rbx, + // rax,: index + // can do better code for P5 - fix this at some point + __ load_signed_byte(rbx, Address(rdx, rax, Address::times_1, arrayOopDesc::base_offset_in_bytes(T_BYTE))); + __ movl(rax, rbx); +} + + +void TemplateTable::caload() { + transition(itos, itos); + // rdx: array + index_check(rdx, rax); // kills rbx, + // rax,: index + // can do better code for P5 - may want to improve this at some point + __ load_unsigned_word(rbx, Address(rdx, rax, Address::times_2, arrayOopDesc::base_offset_in_bytes(T_CHAR))); + __ movl(rax, rbx); +} + +// iload followed by caload frequent pair +void TemplateTable::fast_icaload() { + transition(vtos, itos); + // load index out of locals + locals_index(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); + + // rdx: array + index_check(rdx, rax); + // rax,: index + __ load_unsigned_word(rbx, Address(rdx, rax, Address::times_2, arrayOopDesc::base_offset_in_bytes(T_CHAR))); + __ movl(rax, rbx); +} + +void TemplateTable::saload() { + transition(itos, itos); + // rdx: array + index_check(rdx, rax); // kills rbx, + // rax,: index + // can do better code for P5 - may want to improve this at some point + __ load_signed_word(rbx, Address(rdx, rax, Address::times_2, arrayOopDesc::base_offset_in_bytes(T_SHORT))); + __ movl(rax, rbx); +} + + +void TemplateTable::iload(int n) { + transition(vtos, itos); + __ movl(rax, iaddress(n)); + debug_only(__ verify_local_tag(frame::TagValue, n)); +} + + +void TemplateTable::lload(int n) { + transition(vtos, ltos); + __ movl(rax, laddress(n)); + __ movl(rdx, haddress(n)); + debug_only(__ verify_local_tag(frame::TagCategory2, n)); +} + + +void TemplateTable::fload(int n) { + transition(vtos, ftos); + __ fld_s(faddress(n)); + debug_only(__ verify_local_tag(frame::TagValue, n)); +} + + +void TemplateTable::dload(int n) { + transition(vtos, dtos); + if (TaggedStackInterpreter) { + // Get double out of locals array, onto temp stack and load with + // float instruction into ST0 + __ movl(rax, laddress(n)); + __ movl(rdx, haddress(n)); + __ pushl(rdx); // push hi first + __ pushl(rax); + __ fld_d(Address(rsp, 0)); + __ addl(rsp, 2*wordSize); // reset rsp + debug_only(__ verify_local_tag(frame::TagCategory2, n)); + } else { + __ fld_d(daddress(n)); + } +} + + +void TemplateTable::aload(int n) { + transition(vtos, atos); + __ movl(rax, aaddress(n)); + debug_only(__ verify_local_tag(frame::TagReference, n)); +} + + +void TemplateTable::aload_0() { + transition(vtos, atos); + // According to bytecode histograms, the pairs: + // + // _aload_0, _fast_igetfield + // _aload_0, _fast_agetfield + // _aload_0, _fast_fgetfield + // + // occur frequently. If RewriteFrequentPairs is set, the (slow) _aload_0 + // bytecode checks if the next bytecode is either _fast_igetfield, + // _fast_agetfield or _fast_fgetfield and then rewrites the + // current bytecode into a pair bytecode; otherwise it rewrites the current + // bytecode into _fast_aload_0 that doesn't do the pair check anymore. + // + // Note: If the next bytecode is _getfield, the rewrite must be delayed, + // otherwise we may miss an opportunity for a pair. + // + // Also rewrite frequent pairs + // aload_0, aload_1 + // aload_0, iload_1 + // These bytecodes with a small amount of code are most profitable to rewrite + if (RewriteFrequentPairs) { + Label rewrite, done; + // get next byte + __ load_unsigned_byte(rbx, at_bcp(Bytecodes::length_for(Bytecodes::_aload_0))); + + // do actual aload_0 + aload(0); + + // if _getfield then wait with rewrite + __ cmpl(rbx, Bytecodes::_getfield); + __ jcc(Assembler::equal, done); + + // if _igetfield then reqrite to _fast_iaccess_0 + assert(Bytecodes::java_code(Bytecodes::_fast_iaccess_0) == Bytecodes::_aload_0, "fix bytecode definition"); + __ cmpl(rbx, Bytecodes::_fast_igetfield); + __ movl(rcx, Bytecodes::_fast_iaccess_0); + __ jccb(Assembler::equal, rewrite); + + // if _agetfield then reqrite to _fast_aaccess_0 + assert(Bytecodes::java_code(Bytecodes::_fast_aaccess_0) == Bytecodes::_aload_0, "fix bytecode definition"); + __ cmpl(rbx, Bytecodes::_fast_agetfield); + __ movl(rcx, Bytecodes::_fast_aaccess_0); + __ jccb(Assembler::equal, rewrite); + + // if _fgetfield then reqrite to _fast_faccess_0 + assert(Bytecodes::java_code(Bytecodes::_fast_faccess_0) == Bytecodes::_aload_0, "fix bytecode definition"); + __ cmpl(rbx, Bytecodes::_fast_fgetfield); + __ movl(rcx, Bytecodes::_fast_faccess_0); + __ jccb(Assembler::equal, rewrite); + + // else rewrite to _fast_aload0 + assert(Bytecodes::java_code(Bytecodes::_fast_aload_0) == Bytecodes::_aload_0, "fix bytecode definition"); + __ movl(rcx, Bytecodes::_fast_aload_0); + + // rewrite + // rcx: fast bytecode + __ bind(rewrite); + patch_bytecode(Bytecodes::_aload_0, rcx, rbx, false); + + __ bind(done); + } else { + aload(0); + } +} + +void TemplateTable::istore() { + transition(itos, vtos); + locals_index(rbx); + __ movl(iaddress(rbx), rax); + __ tag_local(frame::TagValue, rbx); +} + + +void TemplateTable::lstore() { + transition(ltos, vtos); + locals_index(rbx); + __ movl(laddress(rbx), rax); + __ movl(haddress(rbx), rdx); + __ tag_local(frame::TagCategory2, rbx); +} + + +void TemplateTable::fstore() { + transition(ftos, vtos); + locals_index(rbx); + __ fstp_s(faddress(rbx)); + __ tag_local(frame::TagValue, rbx); +} + + +void TemplateTable::dstore() { + transition(dtos, vtos); + locals_index(rbx); + if (TaggedStackInterpreter) { + // Store double on stack and reload into locals nonadjacently + __ subl(rsp, 2 * wordSize); + __ fstp_d(Address(rsp, 0)); + __ popl(rax); + __ popl(rdx); + __ movl(laddress(rbx), rax); + __ movl(haddress(rbx), rdx); + __ tag_local(frame::TagCategory2, rbx); + } else { + __ fstp_d(daddress(rbx)); + } +} + + +void TemplateTable::astore() { + transition(vtos, vtos); + __ pop_ptr(rax, rdx); // will need to pop tag too + locals_index(rbx); + __ movl(aaddress(rbx), rax); + __ tag_local(rdx, rbx); // need to store same tag in local may be returnAddr +} + + +void TemplateTable::wide_istore() { + transition(vtos, vtos); + __ pop_i(rax); + locals_index_wide(rbx); + __ movl(iaddress(rbx), rax); + __ tag_local(frame::TagValue, rbx); +} + + +void TemplateTable::wide_lstore() { + transition(vtos, vtos); + __ pop_l(rax, rdx); + locals_index_wide(rbx); + __ movl(laddress(rbx), rax); + __ movl(haddress(rbx), rdx); + __ tag_local(frame::TagCategory2, rbx); +} + + +void TemplateTable::wide_fstore() { + wide_istore(); +} + + +void TemplateTable::wide_dstore() { + wide_lstore(); +} + + +void TemplateTable::wide_astore() { + transition(vtos, vtos); + __ pop_ptr(rax, rdx); + locals_index_wide(rbx); + __ movl(aaddress(rbx), rax); + __ tag_local(rdx, rbx); +} + + +void TemplateTable::iastore() { + transition(itos, vtos); + __ pop_i(rbx); + // rax,: value + // rdx: array + index_check(rdx, rbx); // prefer index in rbx, + // rbx,: index + __ movl(Address(rdx, rbx, Address::times_4, arrayOopDesc::base_offset_in_bytes(T_INT)), rax); +} + + +void TemplateTable::lastore() { + transition(ltos, vtos); + __ pop_i(rbx); + // rax,: low(value) + // rcx: array + // rdx: high(value) + index_check(rcx, rbx); // prefer index in rbx, + // rbx,: index + __ movl(Address(rcx, rbx, Address::times_8, arrayOopDesc::base_offset_in_bytes(T_LONG) + 0 * wordSize), rax); + __ movl(Address(rcx, rbx, Address::times_8, arrayOopDesc::base_offset_in_bytes(T_LONG) + 1 * wordSize), rdx); +} + + +void TemplateTable::fastore() { + transition(ftos, vtos); + __ pop_i(rbx); + // rdx: array + // st0: value + index_check(rdx, rbx); // prefer index in rbx, + // rbx,: index + __ fstp_s(Address(rdx, rbx, Address::times_4, arrayOopDesc::base_offset_in_bytes(T_FLOAT))); +} + + +void TemplateTable::dastore() { + transition(dtos, vtos); + __ pop_i(rbx); + // rdx: array + // st0: value + index_check(rdx, rbx); // prefer index in rbx, + // rbx,: index + __ fstp_d(Address(rdx, rbx, Address::times_8, arrayOopDesc::base_offset_in_bytes(T_DOUBLE))); +} + + +void TemplateTable::aastore() { + Label is_null, ok_is_subtype, done; + transition(vtos, vtos); + // stack: ..., array, index, value + __ movl(rax, at_tos()); // Value + __ movl(rcx, at_tos_p1()); // Index + __ movl(rdx, at_tos_p2()); // Array + index_check_without_pop(rdx, rcx); // kills rbx, + // do array store check - check for NULL value first + __ testl(rax, rax); + __ jcc(Assembler::zero, is_null); + + // Move subklass into EBX + __ movl(rbx, Address(rax, oopDesc::klass_offset_in_bytes())); + // Move superklass into EAX + __ movl(rax, Address(rdx, oopDesc::klass_offset_in_bytes())); + __ movl(rax, Address(rax, sizeof(oopDesc) + objArrayKlass::element_klass_offset_in_bytes())); + // Compress array+index*4+12 into a single register. Frees ECX. + __ leal(rdx, Address(rdx, rcx, Address::times_4, arrayOopDesc::base_offset_in_bytes(T_OBJECT))); + + // Generate subtype check. Blows ECX. Resets EDI to locals. + // Superklass in EAX. Subklass in EBX. + __ gen_subtype_check( rbx, ok_is_subtype ); + + // Come here on failure + // object is at TOS + __ jump(ExternalAddress(Interpreter::_throw_ArrayStoreException_entry)); + + // Come here on success + __ bind(ok_is_subtype); + __ movl(rax, at_rsp()); // Value + __ movl(Address(rdx, 0), rax); + __ store_check(rdx); + __ jmpb(done); + + // Have a NULL in EAX, EDX=array, ECX=index. Store NULL at ary[idx] + __ bind(is_null); + __ profile_null_seen(rbx); + __ movl(Address(rdx, rcx, Address::times_4, arrayOopDesc::base_offset_in_bytes(T_OBJECT)), rax); + + // Pop stack arguments + __ bind(done); + __ addl(rsp, 3 * Interpreter::stackElementSize()); +} + + +void TemplateTable::bastore() { + transition(itos, vtos); + __ pop_i(rbx); + // rax,: value + // rdx: array + index_check(rdx, rbx); // prefer index in rbx, + // rbx,: index + __ movb(Address(rdx, rbx, Address::times_1, arrayOopDesc::base_offset_in_bytes(T_BYTE)), rax); +} + + +void TemplateTable::castore() { + transition(itos, vtos); + __ pop_i(rbx); + // rax,: value + // rdx: array + index_check(rdx, rbx); // prefer index in rbx, + // rbx,: index + __ movw(Address(rdx, rbx, Address::times_2, arrayOopDesc::base_offset_in_bytes(T_CHAR)), rax); +} + + +void TemplateTable::sastore() { + castore(); +} + + +void TemplateTable::istore(int n) { + transition(itos, vtos); + __ movl(iaddress(n), rax); + __ tag_local(frame::TagValue, n); +} + + +void TemplateTable::lstore(int n) { + transition(ltos, vtos); + __ movl(laddress(n), rax); + __ movl(haddress(n), rdx); + __ tag_local(frame::TagCategory2, n); +} + + +void TemplateTable::fstore(int n) { + transition(ftos, vtos); + __ fstp_s(faddress(n)); + __ tag_local(frame::TagValue, n); +} + + +void TemplateTable::dstore(int n) { + transition(dtos, vtos); + if (TaggedStackInterpreter) { + __ subl(rsp, 2 * wordSize); + __ fstp_d(Address(rsp, 0)); + __ popl(rax); + __ popl(rdx); + __ movl(laddress(n), rax); + __ movl(haddress(n), rdx); + __ tag_local(frame::TagCategory2, n); + } else { + __ fstp_d(daddress(n)); + } +} + + +void TemplateTable::astore(int n) { + transition(vtos, vtos); + __ pop_ptr(rax, rdx); + __ movl(aaddress(n), rax); + __ tag_local(rdx, n); +} + + +void TemplateTable::pop() { + transition(vtos, vtos); + __ addl(rsp, Interpreter::stackElementSize()); +} + + +void TemplateTable::pop2() { + transition(vtos, vtos); + __ addl(rsp, 2*Interpreter::stackElementSize()); +} + + +void TemplateTable::dup() { + transition(vtos, vtos); + // stack: ..., a + __ load_ptr_and_tag(0, rax, rdx); + __ push_ptr(rax, rdx); + // stack: ..., a, a +} + + +void TemplateTable::dup_x1() { + transition(vtos, vtos); + // stack: ..., a, b + __ load_ptr_and_tag(0, rax, rdx); // load b + __ load_ptr_and_tag(1, rcx, rbx); // load a + __ store_ptr_and_tag(1, rax, rdx); // store b + __ store_ptr_and_tag(0, rcx, rbx); // store a + __ push_ptr(rax, rdx); // push b + // stack: ..., b, a, b +} + + +void TemplateTable::dup_x2() { + transition(vtos, vtos); + // stack: ..., a, b, c + __ load_ptr_and_tag(0, rax, rdx); // load c + __ load_ptr_and_tag(2, rcx, rbx); // load a + __ store_ptr_and_tag(2, rax, rdx); // store c in a + __ push_ptr(rax, rdx); // push c + // stack: ..., c, b, c, c + __ load_ptr_and_tag(2, rax, rdx); // load b + __ store_ptr_and_tag(2, rcx, rbx); // store a in b + // stack: ..., c, a, c, c + __ store_ptr_and_tag(1, rax, rdx); // store b in c + // stack: ..., c, a, b, c +} + + +void TemplateTable::dup2() { + transition(vtos, vtos); + // stack: ..., a, b + __ load_ptr_and_tag(1, rax, rdx); // load a + __ push_ptr(rax, rdx); // push a + __ load_ptr_and_tag(1, rax, rdx); // load b + __ push_ptr(rax, rdx); // push b + // stack: ..., a, b, a, b +} + + +void TemplateTable::dup2_x1() { + transition(vtos, vtos); + // stack: ..., a, b, c + __ load_ptr_and_tag(0, rcx, rbx); // load c + __ load_ptr_and_tag(1, rax, rdx); // load b + __ push_ptr(rax, rdx); // push b + __ push_ptr(rcx, rbx); // push c + // stack: ..., a, b, c, b, c + __ store_ptr_and_tag(3, rcx, rbx); // store c in b + // stack: ..., a, c, c, b, c + __ load_ptr_and_tag(4, rcx, rbx); // load a + __ store_ptr_and_tag(2, rcx, rbx); // store a in 2nd c + // stack: ..., a, c, a, b, c + __ store_ptr_and_tag(4, rax, rdx); // store b in a + // stack: ..., b, c, a, b, c + // stack: ..., b, c, a, b, c +} + + +void TemplateTable::dup2_x2() { + transition(vtos, vtos); + // stack: ..., a, b, c, d + __ load_ptr_and_tag(0, rcx, rbx); // load d + __ load_ptr_and_tag(1, rax, rdx); // load c + __ push_ptr(rax, rdx); // push c + __ push_ptr(rcx, rbx); // push d + // stack: ..., a, b, c, d, c, d + __ load_ptr_and_tag(4, rax, rdx); // load b + __ store_ptr_and_tag(2, rax, rdx); // store b in d + __ store_ptr_and_tag(4, rcx, rbx); // store d in b + // stack: ..., a, d, c, b, c, d + __ load_ptr_and_tag(5, rcx, rbx); // load a + __ load_ptr_and_tag(3, rax, rdx); // load c + __ store_ptr_and_tag(3, rcx, rbx); // store a in c + __ store_ptr_and_tag(5, rax, rdx); // store c in a + // stack: ..., c, d, a, b, c, d + // stack: ..., c, d, a, b, c, d +} + + +void TemplateTable::swap() { + transition(vtos, vtos); + // stack: ..., a, b + __ load_ptr_and_tag(1, rcx, rbx); // load a + __ load_ptr_and_tag(0, rax, rdx); // load b + __ store_ptr_and_tag(0, rcx, rbx); // store a in b + __ store_ptr_and_tag(1, rax, rdx); // store b in a + // stack: ..., b, a +} + + +void TemplateTable::iop2(Operation op) { + transition(itos, itos); + switch (op) { + case add : __ pop_i(rdx); __ addl (rax, rdx); break; + case sub : __ movl(rdx, rax); __ pop_i(rax); __ subl (rax, rdx); break; + case mul : __ pop_i(rdx); __ imull(rax, rdx); break; + case _and : __ pop_i(rdx); __ andl (rax, rdx); break; + case _or : __ pop_i(rdx); __ orl (rax, rdx); break; + case _xor : __ pop_i(rdx); __ xorl (rax, rdx); break; + case shl : __ movl(rcx, rax); __ pop_i(rax); __ shll (rax); break; // implicit masking of lower 5 bits by Intel shift instr. + case shr : __ movl(rcx, rax); __ pop_i(rax); __ sarl (rax); break; // implicit masking of lower 5 bits by Intel shift instr. + case ushr : __ movl(rcx, rax); __ pop_i(rax); __ shrl (rax); break; // implicit masking of lower 5 bits by Intel shift instr. + default : ShouldNotReachHere(); + } +} + + +void TemplateTable::lop2(Operation op) { + transition(ltos, ltos); + __ pop_l(rbx, rcx); + switch (op) { + case add : __ addl(rax, rbx); __ adcl(rdx, rcx); break; + case sub : __ subl(rbx, rax); __ sbbl(rcx, rdx); + __ movl(rax, rbx); __ movl(rdx, rcx); break; + case _and: __ andl(rax, rbx); __ andl(rdx, rcx); break; + case _or : __ orl (rax, rbx); __ orl (rdx, rcx); break; + case _xor: __ xorl(rax, rbx); __ xorl(rdx, rcx); break; + default : ShouldNotReachHere(); + } +} + + +void TemplateTable::idiv() { + transition(itos, itos); + __ movl(rcx, rax); + __ pop_i(rax); + // Note: could xor rax, and rcx and compare with (-1 ^ min_int). If + // they are not equal, one could do a normal division (no correction + // needed), which may speed up this implementation for the common case. + // (see also JVM spec., p.243 & p.271) + __ corrected_idivl(rcx); +} + + +void TemplateTable::irem() { + transition(itos, itos); + __ movl(rcx, rax); + __ pop_i(rax); + // Note: could xor rax, and rcx and compare with (-1 ^ min_int). If + // they are not equal, one could do a normal division (no correction + // needed), which may speed up this implementation for the common case. + // (see also JVM spec., p.243 & p.271) + __ corrected_idivl(rcx); + __ movl(rax, rdx); +} + + +void TemplateTable::lmul() { + transition(ltos, ltos); + __ pop_l(rbx, rcx); + __ pushl(rcx); __ pushl(rbx); + __ pushl(rdx); __ pushl(rax); + __ lmul(2 * wordSize, 0); + __ addl(rsp, 4 * wordSize); // take off temporaries +} + + +void TemplateTable::ldiv() { + transition(ltos, ltos); + __ pop_l(rbx, rcx); + __ pushl(rcx); __ pushl(rbx); + __ pushl(rdx); __ pushl(rax); + // check if y = 0 + __ orl(rax, rdx); + __ jump_cc(Assembler::zero, + ExternalAddress(Interpreter::_throw_ArithmeticException_entry)); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::ldiv)); + __ addl(rsp, 4 * wordSize); // take off temporaries +} + + +void TemplateTable::lrem() { + transition(ltos, ltos); + __ pop_l(rbx, rcx); + __ pushl(rcx); __ pushl(rbx); + __ pushl(rdx); __ pushl(rax); + // check if y = 0 + __ orl(rax, rdx); + __ jump_cc(Assembler::zero, + ExternalAddress(Interpreter::_throw_ArithmeticException_entry)); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::lrem)); + __ addl(rsp, 4 * wordSize); +} + + +void TemplateTable::lshl() { + transition(itos, ltos); + __ movl(rcx, rax); // get shift count + __ pop_l(rax, rdx); // get shift value + __ lshl(rdx, rax); +} + + +void TemplateTable::lshr() { + transition(itos, ltos); + __ movl(rcx, rax); // get shift count + __ pop_l(rax, rdx); // get shift value + __ lshr(rdx, rax, true); +} + + +void TemplateTable::lushr() { + transition(itos, ltos); + __ movl(rcx, rax); // get shift count + __ pop_l(rax, rdx); // get shift value + __ lshr(rdx, rax); +} + + +void TemplateTable::fop2(Operation op) { + transition(ftos, ftos); + __ pop_ftos_to_rsp(); // pop ftos into rsp + switch (op) { + case add: __ fadd_s (at_rsp()); break; + case sub: __ fsubr_s(at_rsp()); break; + case mul: __ fmul_s (at_rsp()); break; + case div: __ fdivr_s(at_rsp()); break; + case rem: __ fld_s (at_rsp()); __ fremr(rax); break; + default : ShouldNotReachHere(); + } + __ f2ieee(); + __ popl(rax); // pop float thing off +} + + +void TemplateTable::dop2(Operation op) { + transition(dtos, dtos); + __ pop_dtos_to_rsp(); // pop dtos into rsp + + switch (op) { + case add: __ fadd_d (at_rsp()); break; + case sub: __ fsubr_d(at_rsp()); break; + case mul: { + Label L_strict; + Label L_join; + const Address access_flags (rcx, methodOopDesc::access_flags_offset()); + __ get_method(rcx); + __ movl(rcx, access_flags); + __ testl(rcx, JVM_ACC_STRICT); + __ jccb(Assembler::notZero, L_strict); + __ fmul_d (at_rsp()); + __ jmpb(L_join); + __ bind(L_strict); + __ fld_x(ExternalAddress(StubRoutines::addr_fpu_subnormal_bias1())); + __ fmulp(); + __ fmul_d (at_rsp()); + __ fld_x(ExternalAddress(StubRoutines::addr_fpu_subnormal_bias2())); + __ fmulp(); + __ bind(L_join); + break; + } + case div: { + Label L_strict; + Label L_join; + const Address access_flags (rcx, methodOopDesc::access_flags_offset()); + __ get_method(rcx); + __ movl(rcx, access_flags); + __ testl(rcx, JVM_ACC_STRICT); + __ jccb(Assembler::notZero, L_strict); + __ fdivr_d(at_rsp()); + __ jmp(L_join); + __ bind(L_strict); + __ fld_x(ExternalAddress(StubRoutines::addr_fpu_subnormal_bias1())); + __ fmul_d (at_rsp()); + __ fdivrp(); + __ fld_x(ExternalAddress(StubRoutines::addr_fpu_subnormal_bias2())); + __ fmulp(); + __ bind(L_join); + break; + } + case rem: __ fld_d (at_rsp()); __ fremr(rax); break; + default : ShouldNotReachHere(); + } + __ d2ieee(); + // Pop double precision number from rsp. + __ popl(rax); + __ popl(rdx); +} + + +void TemplateTable::ineg() { + transition(itos, itos); + __ negl(rax); +} + + +void TemplateTable::lneg() { + transition(ltos, ltos); + __ lneg(rdx, rax); +} + + +void TemplateTable::fneg() { + transition(ftos, ftos); + __ fchs(); +} + + +void TemplateTable::dneg() { + transition(dtos, dtos); + __ fchs(); +} + + +void TemplateTable::iinc() { + transition(vtos, vtos); + __ load_signed_byte(rdx, at_bcp(2)); // get constant + locals_index(rbx); + __ addl(iaddress(rbx), rdx); +} + + +void TemplateTable::wide_iinc() { + transition(vtos, vtos); + __ movl(rdx, at_bcp(4)); // get constant + locals_index_wide(rbx); + __ bswap(rdx); // swap bytes & sign-extend constant + __ sarl(rdx, 16); + __ addl(iaddress(rbx), rdx); + // Note: should probably use only one movl to get both + // the index and the constant -> fix this +} + + +void TemplateTable::convert() { + // Checking +#ifdef ASSERT + { TosState tos_in = ilgl; + TosState tos_out = ilgl; + switch (bytecode()) { + case Bytecodes::_i2l: // fall through + case Bytecodes::_i2f: // fall through + case Bytecodes::_i2d: // fall through + case Bytecodes::_i2b: // fall through + case Bytecodes::_i2c: // fall through + case Bytecodes::_i2s: tos_in = itos; break; + case Bytecodes::_l2i: // fall through + case Bytecodes::_l2f: // fall through + case Bytecodes::_l2d: tos_in = ltos; break; + case Bytecodes::_f2i: // fall through + case Bytecodes::_f2l: // fall through + case Bytecodes::_f2d: tos_in = ftos; break; + case Bytecodes::_d2i: // fall through + case Bytecodes::_d2l: // fall through + case Bytecodes::_d2f: tos_in = dtos; break; + default : ShouldNotReachHere(); + } + switch (bytecode()) { + case Bytecodes::_l2i: // fall through + case Bytecodes::_f2i: // fall through + case Bytecodes::_d2i: // fall through + case Bytecodes::_i2b: // fall through + case Bytecodes::_i2c: // fall through + case Bytecodes::_i2s: tos_out = itos; break; + case Bytecodes::_i2l: // fall through + case Bytecodes::_f2l: // fall through + case Bytecodes::_d2l: tos_out = ltos; break; + case Bytecodes::_i2f: // fall through + case Bytecodes::_l2f: // fall through + case Bytecodes::_d2f: tos_out = ftos; break; + case Bytecodes::_i2d: // fall through + case Bytecodes::_l2d: // fall through + case Bytecodes::_f2d: tos_out = dtos; break; + default : ShouldNotReachHere(); + } + transition(tos_in, tos_out); + } +#endif // ASSERT + + // Conversion + // (Note: use pushl(rcx)/popl(rcx) for 1/2-word stack-ptr manipulation) + switch (bytecode()) { + case Bytecodes::_i2l: + __ extend_sign(rdx, rax); + break; + case Bytecodes::_i2f: + __ pushl(rax); // store int on tos + __ fild_s(at_rsp()); // load int to ST0 + __ f2ieee(); // truncate to float size + __ popl(rcx); // adjust rsp + break; + case Bytecodes::_i2d: + __ pushl(rax); // add one slot for d2ieee() + __ pushl(rax); // store int on tos + __ fild_s(at_rsp()); // load int to ST0 + __ d2ieee(); // truncate to double size + __ popl(rcx); // adjust rsp + __ popl(rcx); + break; + case Bytecodes::_i2b: + __ shll(rax, 24); // truncate upper 24 bits + __ sarl(rax, 24); // and sign-extend byte + break; + case Bytecodes::_i2c: + __ andl(rax, 0xFFFF); // truncate upper 16 bits + break; + case Bytecodes::_i2s: + __ shll(rax, 16); // truncate upper 16 bits + __ sarl(rax, 16); // and sign-extend short + break; + case Bytecodes::_l2i: + /* nothing to do */ + break; + case Bytecodes::_l2f: + __ pushl(rdx); // store long on tos + __ pushl(rax); + __ fild_d(at_rsp()); // load long to ST0 + __ f2ieee(); // truncate to float size + __ popl(rcx); // adjust rsp + __ popl(rcx); + break; + case Bytecodes::_l2d: + __ pushl(rdx); // store long on tos + __ pushl(rax); + __ fild_d(at_rsp()); // load long to ST0 + __ d2ieee(); // truncate to double size + __ popl(rcx); // adjust rsp + __ popl(rcx); + break; + case Bytecodes::_f2i: + __ pushl(rcx); // reserve space for argument + __ fstp_s(at_rsp()); // pass float argument on stack + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2i), 1); + break; + case Bytecodes::_f2l: + __ pushl(rcx); // reserve space for argument + __ fstp_s(at_rsp()); // pass float argument on stack + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2l), 1); + break; + case Bytecodes::_f2d: + /* nothing to do */ + break; + case Bytecodes::_d2i: + __ pushl(rcx); // reserve space for argument + __ pushl(rcx); + __ fstp_d(at_rsp()); // pass double argument on stack + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2i), 2); + break; + case Bytecodes::_d2l: + __ pushl(rcx); // reserve space for argument + __ pushl(rcx); + __ fstp_d(at_rsp()); // pass double argument on stack + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2l), 2); + break; + case Bytecodes::_d2f: + __ pushl(rcx); // reserve space for f2ieee() + __ f2ieee(); // truncate to float size + __ popl(rcx); // adjust rsp + break; + default : + ShouldNotReachHere(); + } +} + + +void TemplateTable::lcmp() { + transition(ltos, itos); + // y = rdx:rax + __ pop_l(rbx, rcx); // get x = rcx:rbx + __ lcmp2int(rcx, rbx, rdx, rax);// rcx := cmp(x, y) + __ movl(rax, rcx); +} + + +void TemplateTable::float_cmp(bool is_float, int unordered_result) { + if (is_float) { + __ pop_ftos_to_rsp(); + __ fld_s(at_rsp()); + } else { + __ pop_dtos_to_rsp(); + __ fld_d(at_rsp()); + __ popl(rdx); + } + __ popl(rcx); + __ fcmp2int(rax, unordered_result < 0); +} + + +void TemplateTable::branch(bool is_jsr, bool is_wide) { + __ get_method(rcx); // ECX holds method + __ profile_taken_branch(rax,rbx); // EAX holds updated MDP, EBX holds bumped taken count + + const ByteSize be_offset = methodOopDesc::backedge_counter_offset() + InvocationCounter::counter_offset(); + const ByteSize inv_offset = methodOopDesc::invocation_counter_offset() + InvocationCounter::counter_offset(); + const int method_offset = frame::interpreter_frame_method_offset * wordSize; + + // Load up EDX with the branch displacement + __ movl(rdx, at_bcp(1)); + __ bswap(rdx); + if (!is_wide) __ sarl(rdx, 16); + + // Handle all the JSR stuff here, then exit. + // It's much shorter and cleaner than intermingling with the + // non-JSR normal-branch stuff occuring below. + if (is_jsr) { + // Pre-load the next target bytecode into EBX + __ load_unsigned_byte(rbx, Address(rsi, rdx, Address::times_1, 0)); + + // compute return address as bci in rax, + __ leal(rax, at_bcp((is_wide ? 5 : 3) - in_bytes(constMethodOopDesc::codes_offset()))); + __ subl(rax, Address(rcx, methodOopDesc::const_offset())); + // Adjust the bcp in ESI by the displacement in EDX + __ addl(rsi, rdx); + // Push return address + __ push_i(rax); + // jsr returns vtos + __ dispatch_only_noverify(vtos); + return; + } + + // Normal (non-jsr) branch handling + + // Adjust the bcp in ESI by the displacement in EDX + __ addl(rsi, rdx); + + assert(UseLoopCounter || !UseOnStackReplacement, "on-stack-replacement requires loop counters"); + Label backedge_counter_overflow; + Label profile_method; + Label dispatch; + if (UseLoopCounter) { + // increment backedge counter for backward branches + // rax,: MDO + // rbx,: MDO bumped taken-count + // rcx: method + // rdx: target offset + // rsi: target bcp + // rdi: locals pointer + __ testl(rdx, rdx); // check if forward or backward branch + __ jcc(Assembler::positive, dispatch); // count only if backward branch + + // increment counter + __ movl(rax, Address(rcx, be_offset)); // load backedge counter + __ increment(rax, InvocationCounter::count_increment); // increment counter + __ movl(Address(rcx, be_offset), rax); // store counter + + __ movl(rax, Address(rcx, inv_offset)); // load invocation counter + __ andl(rax, InvocationCounter::count_mask_value); // and the status bits + __ addl(rax, Address(rcx, be_offset)); // add both counters + + if (ProfileInterpreter) { + // Test to see if we should create a method data oop + __ cmp32(rax, + ExternalAddress((address) &InvocationCounter::InterpreterProfileLimit)); + __ jcc(Assembler::less, dispatch); + + // if no method data exists, go to profile method + __ test_method_data_pointer(rax, profile_method); + + if (UseOnStackReplacement) { + // check for overflow against rbx, which is the MDO taken count + __ cmp32(rbx, + ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit)); + __ jcc(Assembler::below, dispatch); + + // When ProfileInterpreter is on, the backedge_count comes from the + // methodDataOop, which value does not get reset on the call to + // frequency_counter_overflow(). To avoid excessive calls to the overflow + // routine while the method is being compiled, add a second test to make + // sure the overflow function is called only once every overflow_frequency. + const int overflow_frequency = 1024; + __ andl(rbx, overflow_frequency-1); + __ jcc(Assembler::zero, backedge_counter_overflow); + + } + } else { + if (UseOnStackReplacement) { + // check for overflow against rax, which is the sum of the counters + __ cmp32(rax, + ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit)); + __ jcc(Assembler::aboveEqual, backedge_counter_overflow); + + } + } + __ bind(dispatch); + } + + // Pre-load the next target bytecode into EBX + __ load_unsigned_byte(rbx, Address(rsi, 0)); + + // continue with the bytecode @ target + // rax,: return bci for jsr's, unused otherwise + // rbx,: target bytecode + // rsi: target bcp + __ dispatch_only(vtos); + + if (UseLoopCounter) { + if (ProfileInterpreter) { + // Out-of-line code to allocate method data oop. + __ bind(profile_method); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method), rsi); + __ load_unsigned_byte(rbx, Address(rsi, 0)); // restore target bytecode + __ movl(rcx, Address(rbp, method_offset)); + __ movl(rcx, Address(rcx, in_bytes(methodOopDesc::method_data_offset()))); + __ movl(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), rcx); + __ test_method_data_pointer(rcx, dispatch); + // offset non-null mdp by MDO::data_offset() + IR::profile_method() + __ addl(rcx, in_bytes(methodDataOopDesc::data_offset())); + __ addl(rcx, rax); + __ movl(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), rcx); + __ jmp(dispatch); + } + + if (UseOnStackReplacement) { + + // invocation counter overflow + __ bind(backedge_counter_overflow); + __ negl(rdx); + __ addl(rdx, rsi); // branch bcp + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), rdx); + __ load_unsigned_byte(rbx, Address(rsi, 0)); // restore target bytecode + + // rax,: osr nmethod (osr ok) or NULL (osr not possible) + // rbx,: target bytecode + // rdx: scratch + // rdi: locals pointer + // rsi: bcp + __ testl(rax, rax); // test result + __ jcc(Assembler::zero, dispatch); // no osr if null + // nmethod may have been invalidated (VM may block upon call_VM return) + __ movl(rcx, Address(rax, nmethod::entry_bci_offset())); + __ cmpl(rcx, InvalidOSREntryBci); + __ jcc(Assembler::equal, dispatch); + + // We have the address of an on stack replacement routine in rax, + // We need to prepare to execute the OSR method. First we must + // migrate the locals and monitors off of the stack. + + __ movl(rsi, rax); // save the nmethod + + const Register thread = rcx; + __ get_thread(thread); + call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin)); + // rax, is OSR buffer, move it to expected parameter location + __ movl(rcx, rax); + + // pop the interpreter frame + __ movl(rdx, Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize)); // get sender sp + __ leave(); // remove frame anchor + __ popl(rdi); // get return address + __ movl(rsp, rdx); // set sp to sender sp + + + Label skip; + Label chkint; + + // The interpreter frame we have removed may be returning to + // either the callstub or the interpreter. Since we will + // now be returning from a compiled (OSR) nmethod we must + // adjust the return to the return were it can handler compiled + // results and clean the fpu stack. This is very similar to + // what a i2c adapter must do. + + // Are we returning to the call stub? + + __ cmp32(rdi, ExternalAddress(StubRoutines::_call_stub_return_address)); + __ jcc(Assembler::notEqual, chkint); + + // yes adjust to the specialized call stub return. + assert(StubRoutines::i486::get_call_stub_compiled_return() != NULL, "must be set"); + __ lea(rdi, ExternalAddress(StubRoutines::i486::get_call_stub_compiled_return())); + __ jmp(skip); + + __ bind(chkint); + + // Are we returning to the interpreter? Look for sentinel + + __ cmpl(Address(rdi, -8), Interpreter::return_sentinel); + __ jcc(Assembler::notEqual, skip); + + // Adjust to compiled return back to interpreter + + __ movl(rdi, Address(rdi, -4)); + __ bind(skip); + + // Align stack pointer for compiled code (note that caller is + // responsible for undoing this fixup by remembering the old SP + // in an rbp,-relative location) + __ andl(rsp, -(StackAlignmentInBytes)); + + // push the (possibly adjusted) return address + __ pushl(rdi); + + // and begin the OSR nmethod + __ jmp(Address(rsi, nmethod::osr_entry_point_offset())); + } + } +} + + +void TemplateTable::if_0cmp(Condition cc) { + transition(itos, vtos); + // assume branch is more often taken than not (loops use backward branches) + Label not_taken; + __ testl(rax, rax); + __ jcc(j_not(cc), not_taken); + branch(false, false); + __ bind(not_taken); + __ profile_not_taken_branch(rax); +} + + +void TemplateTable::if_icmp(Condition cc) { + transition(itos, vtos); + // assume branch is more often taken than not (loops use backward branches) + Label not_taken; + __ pop_i(rdx); + __ cmpl(rdx, rax); + __ jcc(j_not(cc), not_taken); + branch(false, false); + __ bind(not_taken); + __ profile_not_taken_branch(rax); +} + + +void TemplateTable::if_nullcmp(Condition cc) { + transition(atos, vtos); + // assume branch is more often taken than not (loops use backward branches) + Label not_taken; + __ testl(rax, rax); + __ jcc(j_not(cc), not_taken); + branch(false, false); + __ bind(not_taken); + __ profile_not_taken_branch(rax); +} + + +void TemplateTable::if_acmp(Condition cc) { + transition(atos, vtos); + // assume branch is more often taken than not (loops use backward branches) + Label not_taken; + __ pop_ptr(rdx); + __ cmpl(rdx, rax); + __ jcc(j_not(cc), not_taken); + branch(false, false); + __ bind(not_taken); + __ profile_not_taken_branch(rax); +} + + +void TemplateTable::ret() { + transition(vtos, vtos); + locals_index(rbx); + __ movl(rbx, iaddress(rbx)); // get return bci, compute return bcp + __ profile_ret(rbx, rcx); + __ get_method(rax); + __ movl(rsi, Address(rax, methodOopDesc::const_offset())); + __ leal(rsi, Address(rsi, rbx, Address::times_1, + constMethodOopDesc::codes_offset())); + __ dispatch_next(vtos); +} + + +void TemplateTable::wide_ret() { + transition(vtos, vtos); + locals_index_wide(rbx); + __ movl(rbx, iaddress(rbx)); // get return bci, compute return bcp + __ profile_ret(rbx, rcx); + __ get_method(rax); + __ movl(rsi, Address(rax, methodOopDesc::const_offset())); + __ leal(rsi, Address(rsi, rbx, Address::times_1, constMethodOopDesc::codes_offset())); + __ dispatch_next(vtos); +} + + +void TemplateTable::tableswitch() { + Label default_case, continue_execution; + transition(itos, vtos); + // align rsi + __ leal(rbx, at_bcp(wordSize)); + __ andl(rbx, -wordSize); + // load lo & hi + __ movl(rcx, Address(rbx, 1 * wordSize)); + __ movl(rdx, Address(rbx, 2 * wordSize)); + __ bswap(rcx); + __ bswap(rdx); + // check against lo & hi + __ cmpl(rax, rcx); + __ jccb(Assembler::less, default_case); + __ cmpl(rax, rdx); + __ jccb(Assembler::greater, default_case); + // lookup dispatch offset + __ subl(rax, rcx); + __ movl(rdx, Address(rbx, rax, Address::times_4, 3 * wordSize)); + __ profile_switch_case(rax, rbx, rcx); + // continue execution + __ bind(continue_execution); + __ bswap(rdx); + __ load_unsigned_byte(rbx, Address(rsi, rdx, Address::times_1)); + __ addl(rsi, rdx); + __ dispatch_only(vtos); + // handle default + __ bind(default_case); + __ profile_switch_default(rax); + __ movl(rdx, Address(rbx, 0)); + __ jmp(continue_execution); +} + + +void TemplateTable::lookupswitch() { + transition(itos, itos); + __ stop("lookupswitch bytecode should have been rewritten"); +} + + +void TemplateTable::fast_linearswitch() { + transition(itos, vtos); + Label loop_entry, loop, found, continue_execution; + // bswap rax, so we can avoid bswapping the table entries + __ bswap(rax); + // align rsi + __ leal(rbx, at_bcp(wordSize)); // btw: should be able to get rid of this instruction (change offsets below) + __ andl(rbx, -wordSize); + // set counter + __ movl(rcx, Address(rbx, wordSize)); + __ bswap(rcx); + __ jmpb(loop_entry); + // table search + __ bind(loop); + __ cmpl(rax, Address(rbx, rcx, Address::times_8, 2 * wordSize)); + __ jccb(Assembler::equal, found); + __ bind(loop_entry); + __ decrement(rcx); + __ jcc(Assembler::greaterEqual, loop); + // default case + __ profile_switch_default(rax); + __ movl(rdx, Address(rbx, 0)); + __ jmpb(continue_execution); + // entry found -> get offset + __ bind(found); + __ movl(rdx, Address(rbx, rcx, Address::times_8, 3 * wordSize)); + __ profile_switch_case(rcx, rax, rbx); + // continue execution + __ bind(continue_execution); + __ bswap(rdx); + __ load_unsigned_byte(rbx, Address(rsi, rdx, Address::times_1)); + __ addl(rsi, rdx); + __ dispatch_only(vtos); +} + + +void TemplateTable::fast_binaryswitch() { + transition(itos, vtos); + // Implementation using the following core algorithm: + // + // int binary_search(int key, LookupswitchPair* array, int n) { + // // Binary search according to "Methodik des Programmierens" by + // // Edsger W. Dijkstra and W.H.J. Feijen, Addison Wesley Germany 1985. + // int i = 0; + // int j = n; + // while (i+1 < j) { + // // invariant P: 0 <= i < j <= n and (a[i] <= key < a[j] or Q) + // // with Q: for all i: 0 <= i < n: key < a[i] + // // where a stands for the array and assuming that the (inexisting) + // // element a[n] is infinitely big. + // int h = (i + j) >> 1; + // // i < h < j + // if (key < array[h].fast_match()) { + // j = h; + // } else { + // i = h; + // } + // } + // // R: a[i] <= key < a[i+1] or Q + // // (i.e., if key is within array, i is the correct index) + // return i; + // } + + // register allocation + const Register key = rax; // already set (tosca) + const Register array = rbx; + const Register i = rcx; + const Register j = rdx; + const Register h = rdi; // needs to be restored + const Register temp = rsi; + // setup array + __ save_bcp(); + + __ leal(array, at_bcp(3*wordSize)); // btw: should be able to get rid of this instruction (change offsets below) + __ andl(array, -wordSize); + // initialize i & j + __ xorl(i, i); // i = 0; + __ movl(j, Address(array, -wordSize)); // j = length(array); + // Convert j into native byteordering + __ bswap(j); + // and start + Label entry; + __ jmp(entry); + + // binary search loop + { Label loop; + __ bind(loop); + // int h = (i + j) >> 1; + __ leal(h, Address(i, j, Address::times_1)); // h = i + j; + __ sarl(h, 1); // h = (i + j) >> 1; + // if (key < array[h].fast_match()) { + // j = h; + // } else { + // i = h; + // } + // Convert array[h].match to native byte-ordering before compare + __ movl(temp, Address(array, h, Address::times_8, 0*wordSize)); + __ bswap(temp); + __ cmpl(key, temp); + if (VM_Version::supports_cmov()) { + __ cmovl(Assembler::less , j, h); // j = h if (key < array[h].fast_match()) + __ cmovl(Assembler::greaterEqual, i, h); // i = h if (key >= array[h].fast_match()) + } else { + Label set_i, end_of_if; + __ jccb(Assembler::greaterEqual, set_i); // { + __ movl(j, h); // j = h; + __ jmp(end_of_if); // } + __ bind(set_i); // else { + __ movl(i, h); // i = h; + __ bind(end_of_if); // } + } + // while (i+1 < j) + __ bind(entry); + __ leal(h, Address(i, 1)); // i+1 + __ cmpl(h, j); // i+1 < j + __ jcc(Assembler::less, loop); + } + + // end of binary search, result index is i (must check again!) + Label default_case; + // Convert array[i].match to native byte-ordering before compare + __ movl(temp, Address(array, i, Address::times_8, 0*wordSize)); + __ bswap(temp); + __ cmpl(key, temp); + __ jcc(Assembler::notEqual, default_case); + + // entry found -> j = offset + __ movl(j , Address(array, i, Address::times_8, 1*wordSize)); + __ profile_switch_case(i, key, array); + __ bswap(j); + __ restore_bcp(); + __ restore_locals(); // restore rdi + __ load_unsigned_byte(rbx, Address(rsi, j, Address::times_1)); + + __ addl(rsi, j); + __ dispatch_only(vtos); + + // default case -> j = default offset + __ bind(default_case); + __ profile_switch_default(i); + __ movl(j, Address(array, -2*wordSize)); + __ bswap(j); + __ restore_bcp(); + __ restore_locals(); // restore rdi + __ load_unsigned_byte(rbx, Address(rsi, j, Address::times_1)); + __ addl(rsi, j); + __ dispatch_only(vtos); +} + + +void TemplateTable::_return(TosState state) { + transition(state, state); + assert(_desc->calls_vm(), "inconsistent calls_vm information"); // call in remove_activation + + if (_desc->bytecode() == Bytecodes::_return_register_finalizer) { + assert(state == vtos, "only valid state"); + __ movl(rax, aaddress(0)); + __ movl(rdi, Address(rax, oopDesc::klass_offset_in_bytes())); + __ movl(rdi, Address(rdi, Klass::access_flags_offset_in_bytes() + sizeof(oopDesc))); + __ testl(rdi, JVM_ACC_HAS_FINALIZER); + Label skip_register_finalizer; + __ jcc(Assembler::zero, skip_register_finalizer); + + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::register_finalizer), rax); + + __ bind(skip_register_finalizer); + } + + __ remove_activation(state, rsi); + __ jmp(rsi); +} + + +// ---------------------------------------------------------------------------- +// Volatile variables demand their effects be made known to all CPU's in +// order. Store buffers on most chips allow reads & writes to reorder; the +// JMM's ReadAfterWrite.java test fails in -Xint mode without some kind of +// memory barrier (i.e., it's not sufficient that the interpreter does not +// reorder volatile references, the hardware also must not reorder them). +// +// According to the new Java Memory Model (JMM): +// (1) All volatiles are serialized wrt to each other. +// ALSO reads & writes act as aquire & release, so: +// (2) A read cannot let unrelated NON-volatile memory refs that happen after +// the read float up to before the read. It's OK for non-volatile memory refs +// that happen before the volatile read to float down below it. +// (3) Similar a volatile write cannot let unrelated NON-volatile memory refs +// that happen BEFORE the write float down to after the write. It's OK for +// non-volatile memory refs that happen after the volatile write to float up +// before it. +// +// We only put in barriers around volatile refs (they are expensive), not +// _between_ memory refs (that would require us to track the flavor of the +// previous memory refs). Requirements (2) and (3) require some barriers +// before volatile stores and after volatile loads. These nearly cover +// requirement (1) but miss the volatile-store-volatile-load case. This final +// case is placed after volatile-stores although it could just as well go +// before volatile-loads. +void TemplateTable::volatile_barrier( ) { + // Helper function to insert a is-volatile test and memory barrier + if( !os::is_MP() ) return; // Not needed on single CPU + __ membar(); +} + +void TemplateTable::resolve_cache_and_index(int byte_no, Register Rcache, Register index) { + assert(byte_no == 1 || byte_no == 2, "byte_no out of range"); + + Register temp = rbx; + + assert_different_registers(Rcache, index, temp); + + const int shift_count = (1 + byte_no)*BitsPerByte; + Label resolved; + __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ movl(temp, Address(Rcache, index, Address::times_4, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset())); + __ shrl(temp, shift_count); + // have we resolved this bytecode? + __ andl(temp, 0xFF); + __ cmpl(temp, (int)bytecode()); + __ jcc(Assembler::equal, resolved); + + // resolve first time through + address entry; + switch (bytecode()) { + case Bytecodes::_getstatic : // fall through + case Bytecodes::_putstatic : // fall through + case Bytecodes::_getfield : // fall through + case Bytecodes::_putfield : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_get_put); break; + case Bytecodes::_invokevirtual : // fall through + case Bytecodes::_invokespecial : // fall through + case Bytecodes::_invokestatic : // fall through + case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); break; + default : ShouldNotReachHere(); break; + } + __ movl(temp, (int)bytecode()); + __ call_VM(noreg, entry, temp); + // Update registers with resolved info + __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ bind(resolved); +} + + +// The cache and index registers must be set before call +void TemplateTable::load_field_cp_cache_entry(Register obj, + Register cache, + Register index, + Register off, + Register flags, + bool is_static = false) { + assert_different_registers(cache, index, flags, off); + + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + // Field offset + __ movl(off, Address(cache, index, Address::times_4, + in_bytes(cp_base_offset + ConstantPoolCacheEntry::f2_offset()))); + // Flags + __ movl(flags, Address(cache, index, Address::times_4, + in_bytes(cp_base_offset + ConstantPoolCacheEntry::flags_offset()))); + + // klass overwrite register + if (is_static) { + __ movl(obj, Address(cache, index, Address::times_4, + in_bytes(cp_base_offset + ConstantPoolCacheEntry::f1_offset()))); + } +} + +void TemplateTable::load_invoke_cp_cache_entry(int byte_no, + Register method, + Register itable_index, + Register flags, + bool is_invokevirtual, + bool is_invokevfinal /*unused*/) { + // setup registers + const Register cache = rcx; + const Register index = rdx; + assert_different_registers(method, flags); + assert_different_registers(method, cache, index); + assert_different_registers(itable_index, flags); + assert_different_registers(itable_index, cache, index); + // determine constant pool cache field offsets + const int method_offset = in_bytes( + constantPoolCacheOopDesc::base_offset() + + (is_invokevirtual + ? ConstantPoolCacheEntry::f2_offset() + : ConstantPoolCacheEntry::f1_offset() + ) + ); + const int flags_offset = in_bytes(constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::flags_offset()); + // access constant pool cache fields + const int index_offset = in_bytes(constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::f2_offset()); + + resolve_cache_and_index(byte_no, cache, index); + + assert(wordSize == 4, "adjust code below"); + __ movl(method, Address(cache, index, Address::times_4, method_offset)); + if (itable_index != noreg) { + __ movl(itable_index, Address(cache, index, Address::times_4, index_offset)); + } + __ movl(flags , Address(cache, index, Address::times_4, flags_offset )); +} + + +// The registers cache and index expected to be set before call. +// Correct values of the cache and index registers are preserved. +void TemplateTable::jvmti_post_field_access(Register cache, + Register index, + bool is_static, + bool has_tos) { + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we take + // the time to call into the VM. + Label L1; + assert_different_registers(cache, index, rax); + __ mov32(rax, ExternalAddress((address) JvmtiExport::get_field_access_count_addr())); + __ testl(rax,rax); + __ jcc(Assembler::zero, L1); + + // cache entry pointer + __ addl(cache, in_bytes(constantPoolCacheOopDesc::base_offset())); + __ shll(index, LogBytesPerWord); + __ addl(cache, index); + if (is_static) { + __ movl(rax, 0); // NULL object reference + } else { + __ pop(atos); // Get the object + __ verify_oop(rax); + __ push(atos); // Restore stack state + } + // rax,: object pointer or NULL + // cache: cache entry pointer + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), + rax, cache); + __ get_cache_and_index_at_bcp(cache, index, 1); + __ bind(L1); + } +} + +void TemplateTable::pop_and_check_object(Register r) { + __ pop_ptr(r); + __ null_check(r); // for field access must check obj. + __ verify_oop(r); +} + +void TemplateTable::getfield_or_static(int byte_no, bool is_static) { + transition(vtos, vtos); + + const Register cache = rcx; + const Register index = rdx; + const Register obj = rcx; + const Register off = rbx; + const Register flags = rax; + + resolve_cache_and_index(byte_no, cache, index); + jvmti_post_field_access(cache, index, is_static, false); + load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); + + if (!is_static) pop_and_check_object(obj); + + const Address lo(obj, off, Address::times_1, 0*wordSize); + const Address hi(obj, off, Address::times_1, 1*wordSize); + + Label Done, notByte, notInt, notShort, notChar, notLong, notFloat, notObj, notDouble; + + __ shrl(flags, ConstantPoolCacheEntry::tosBits); + assert(btos == 0, "change code, btos != 0"); + // btos + __ andl(flags, 0x0f); + __ jcc(Assembler::notZero, notByte); + + __ load_signed_byte(rax, lo ); + __ push(btos); + // Rewrite bytecode to be faster + if (!is_static) { + patch_bytecode(Bytecodes::_fast_bgetfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notByte); + // itos + __ cmpl(flags, itos ); + __ jcc(Assembler::notEqual, notInt); + + __ movl(rax, lo ); + __ push(itos); + // Rewrite bytecode to be faster + if (!is_static) { + patch_bytecode(Bytecodes::_fast_igetfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notInt); + // atos + __ cmpl(flags, atos ); + __ jcc(Assembler::notEqual, notObj); + + __ movl(rax, lo ); + __ push(atos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_agetfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notObj); + // ctos + __ cmpl(flags, ctos ); + __ jcc(Assembler::notEqual, notChar); + + __ load_unsigned_word(rax, lo ); + __ push(ctos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_cgetfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notChar); + // stos + __ cmpl(flags, stos ); + __ jcc(Assembler::notEqual, notShort); + + __ load_signed_word(rax, lo ); + __ push(stos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_sgetfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notShort); + // ltos + __ cmpl(flags, ltos ); + __ jcc(Assembler::notEqual, notLong); + + // Generate code as if volatile. There just aren't enough registers to + // save that information and this code is faster than the test. + __ fild_d(lo); // Must load atomically + __ subl(rsp,2*wordSize); // Make space for store + __ fistp_d(Address(rsp,0)); + __ popl(rax); + __ popl(rdx); + + __ push(ltos); + // Don't rewrite to _fast_lgetfield for potential volatile case. + __ jmp(Done); + + __ bind(notLong); + // ftos + __ cmpl(flags, ftos ); + __ jcc(Assembler::notEqual, notFloat); + + __ fld_s(lo); + __ push(ftos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_fgetfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notFloat); + // dtos + __ cmpl(flags, dtos ); + __ jcc(Assembler::notEqual, notDouble); + + __ fld_d(lo); + __ push(dtos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_dgetfield, rcx, rbx); + } + __ jmpb(Done); + + __ bind(notDouble); + + __ stop("Bad state"); + + __ bind(Done); + // Doug Lea believes this is not needed with current Sparcs (TSO) and Intel (PSO). + // volatile_barrier( ); +} + + +void TemplateTable::getfield(int byte_no) { + getfield_or_static(byte_no, false); +} + + +void TemplateTable::getstatic(int byte_no) { + getfield_or_static(byte_no, true); +} + +// The registers cache and index expected to be set before call. +// The function may destroy various registers, just not the cache and index registers. +void TemplateTable::jvmti_post_field_mod(Register cache, Register index, bool is_static) { + + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + if (JvmtiExport::can_post_field_modification()) { + // Check to see if a field modification watch has been set before we take + // the time to call into the VM. + Label L1; + assert_different_registers(cache, index, rax); + __ mov32(rax, ExternalAddress((address)JvmtiExport::get_field_modification_count_addr())); + __ testl(rax, rax); + __ jcc(Assembler::zero, L1); + + // The cache and index registers have been already set. + // This allows to eliminate this call but the cache and index + // registers have to be correspondingly used after this line. + __ get_cache_and_index_at_bcp(rax, rdx, 1); + + if (is_static) { + // Life is simple. Null out the object pointer. + __ xorl(rbx, rbx); + } else { + // Life is harder. The stack holds the value on top, followed by the object. + // We don't know the size of the value, though; it could be one or two words + // depending on its type. As a result, we must find the type to determine where + // the object is. + Label two_word, valsize_known; + __ movl(rcx, Address(rax, rdx, Address::times_4, in_bytes(cp_base_offset + + ConstantPoolCacheEntry::flags_offset()))); + __ movl(rbx, rsp); + __ shrl(rcx, ConstantPoolCacheEntry::tosBits); + // Make sure we don't need to mask rcx for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ cmpl(rcx, ltos); + __ jccb(Assembler::equal, two_word); + __ cmpl(rcx, dtos); + __ jccb(Assembler::equal, two_word); + __ addl(rbx, Interpreter::expr_offset_in_bytes(1)); // one word jvalue (not ltos, dtos) + __ jmpb(valsize_known); + + __ bind(two_word); + __ addl(rbx, Interpreter::expr_offset_in_bytes(2)); // two words jvalue + + __ bind(valsize_known); + // setup object pointer + __ movl(rbx, Address(rbx, 0)); + } + // cache entry pointer + __ addl(rax, in_bytes(cp_base_offset)); + __ shll(rdx, LogBytesPerWord); + __ addl(rax, rdx); + // object (tos) + __ movl(rcx, rsp); + // rbx,: object pointer set up above (NULL if static) + // rax,: cache entry pointer + // rcx: jvalue object on the stack + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), + rbx, rax, rcx); + __ get_cache_and_index_at_bcp(cache, index, 1); + __ bind(L1); + } +} + + +void TemplateTable::putfield_or_static(int byte_no, bool is_static) { + transition(vtos, vtos); + + const Register cache = rcx; + const Register index = rdx; + const Register obj = rcx; + const Register off = rbx; + const Register flags = rax; + + resolve_cache_and_index(byte_no, cache, index); + jvmti_post_field_mod(cache, index, is_static); + load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); + + // Doug Lea believes this is not needed with current Sparcs (TSO) and Intel (PSO). + // volatile_barrier( ); + + Label notVolatile, Done; + __ movl(rdx, flags); + __ shrl(rdx, ConstantPoolCacheEntry::volatileField); + __ andl(rdx, 0x1); + + // field addresses + const Address lo(obj, off, Address::times_1, 0*wordSize); + const Address hi(obj, off, Address::times_1, 1*wordSize); + + Label notByte, notInt, notShort, notChar, notLong, notFloat, notObj, notDouble; + + __ shrl(flags, ConstantPoolCacheEntry::tosBits); + assert(btos == 0, "change code, btos != 0"); + // btos + __ andl(flags, 0x0f); + __ jcc(Assembler::notZero, notByte); + + __ pop(btos); + if (!is_static) pop_and_check_object(obj); + __ movb(lo, rax ); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_bputfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notByte); + // itos + __ cmpl(flags, itos ); + __ jcc(Assembler::notEqual, notInt); + + __ pop(itos); + if (!is_static) pop_and_check_object(obj); + + __ movl(lo, rax ); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_iputfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notInt); + // atos + __ cmpl(flags, atos ); + __ jcc(Assembler::notEqual, notObj); + + __ pop(atos); + if (!is_static) pop_and_check_object(obj); + + __ movl(lo, rax ); + __ store_check(obj, lo); // Need to mark card + if (!is_static) { + patch_bytecode(Bytecodes::_fast_aputfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notObj); + // ctos + __ cmpl(flags, ctos ); + __ jcc(Assembler::notEqual, notChar); + + __ pop(ctos); + if (!is_static) pop_and_check_object(obj); + __ movw(lo, rax ); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_cputfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notChar); + // stos + __ cmpl(flags, stos ); + __ jcc(Assembler::notEqual, notShort); + + __ pop(stos); + if (!is_static) pop_and_check_object(obj); + __ movw(lo, rax ); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_sputfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notShort); + // ltos + __ cmpl(flags, ltos ); + __ jcc(Assembler::notEqual, notLong); + + Label notVolatileLong; + __ testl(rdx, rdx); + __ jcc(Assembler::zero, notVolatileLong); + + __ pop(ltos); // overwrites rdx, do this after testing volatile. + if (!is_static) pop_and_check_object(obj); + + // Replace with real volatile test + __ pushl(rdx); + __ pushl(rax); // Must update atomically with FIST + __ fild_d(Address(rsp,0)); // So load into FPU register + __ fistp_d(lo); // and put into memory atomically + __ addl(rsp,2*wordSize); + volatile_barrier(); + // Don't rewrite volatile version + __ jmp(notVolatile); + + __ bind(notVolatileLong); + + __ pop(ltos); // overwrites rdx + if (!is_static) pop_and_check_object(obj); + __ movl(hi, rdx); + __ movl(lo, rax); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_lputfield, rcx, rbx); + } + __ jmp(notVolatile); + + __ bind(notLong); + // ftos + __ cmpl(flags, ftos ); + __ jcc(Assembler::notEqual, notFloat); + + __ pop(ftos); + if (!is_static) pop_and_check_object(obj); + __ fstp_s(lo); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_fputfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notFloat); + // dtos + __ cmpl(flags, dtos ); + __ jcc(Assembler::notEqual, notDouble); + + __ pop(dtos); + if (!is_static) pop_and_check_object(obj); + __ fstp_d(lo); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_dputfield, rcx, rbx); + } + __ jmp(Done); + + __ bind(notDouble); + + __ stop("Bad state"); + + __ bind(Done); + + // Check for volatile store + __ testl(rdx, rdx); + __ jcc(Assembler::zero, notVolatile); + volatile_barrier( ); + __ bind(notVolatile); +} + + +void TemplateTable::putfield(int byte_no) { + putfield_or_static(byte_no, false); +} + + +void TemplateTable::putstatic(int byte_no) { + putfield_or_static(byte_no, true); +} + +void TemplateTable::jvmti_post_fast_field_mod() { + if (JvmtiExport::can_post_field_modification()) { + // Check to see if a field modification watch has been set before we take + // the time to call into the VM. + Label L2; + __ mov32(rcx, ExternalAddress((address)JvmtiExport::get_field_modification_count_addr())); + __ testl(rcx,rcx); + __ jcc(Assembler::zero, L2); + __ pop_ptr(rbx); // copy the object pointer from tos + __ verify_oop(rbx); + __ push_ptr(rbx); // put the object pointer back on tos + __ subl(rsp, sizeof(jvalue)); // add space for a jvalue object + __ movl(rcx, rsp); + __ push_ptr(rbx); // save object pointer so we can steal rbx, + __ movl(rbx, 0); + const Address lo_value(rcx, rbx, Address::times_1, 0*wordSize); + const Address hi_value(rcx, rbx, Address::times_1, 1*wordSize); + switch (bytecode()) { // load values into the jvalue object + case Bytecodes::_fast_bputfield: __ movb(lo_value, rax); break; + case Bytecodes::_fast_sputfield: __ movw(lo_value, rax); break; + case Bytecodes::_fast_cputfield: __ movw(lo_value, rax); break; + case Bytecodes::_fast_iputfield: __ movl(lo_value, rax); break; + case Bytecodes::_fast_lputfield: __ movl(hi_value, rdx); __ movl(lo_value, rax); break; + // need to call fld_s() after fstp_s() to restore the value for below + case Bytecodes::_fast_fputfield: __ fstp_s(lo_value); __ fld_s(lo_value); break; + // need to call fld_d() after fstp_d() to restore the value for below + case Bytecodes::_fast_dputfield: __ fstp_d(lo_value); __ fld_d(lo_value); break; + // since rcx is not an object we don't call store_check() here + case Bytecodes::_fast_aputfield: __ movl(lo_value, rax); break; + default: ShouldNotReachHere(); + } + __ pop_ptr(rbx); // restore copy of object pointer + + // Save rax, and sometimes rdx because call_VM() will clobber them, + // then use them for JVM/DI purposes + __ pushl(rax); + if (bytecode() == Bytecodes::_fast_lputfield) __ pushl(rdx); + // access constant pool cache entry + __ get_cache_entry_pointer_at_bcp(rax, rdx, 1); + __ verify_oop(rbx); + // rbx,: object pointer copied above + // rax,: cache entry pointer + // rcx: jvalue object on the stack + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), rbx, rax, rcx); + if (bytecode() == Bytecodes::_fast_lputfield) __ popl(rdx); // restore high value + __ popl(rax); // restore lower value + __ addl(rsp, sizeof(jvalue)); // release jvalue object space + __ bind(L2); + } +} + +void TemplateTable::fast_storefield(TosState state) { + transition(state, vtos); + + ByteSize base = constantPoolCacheOopDesc::base_offset(); + + jvmti_post_fast_field_mod(); + + // access constant pool cache + __ get_cache_and_index_at_bcp(rcx, rbx, 1); + + // test for volatile with rdx but rdx is tos register for lputfield. + if (bytecode() == Bytecodes::_fast_lputfield) __ pushl(rdx); + __ movl(rdx, Address(rcx, rbx, Address::times_4, in_bytes(base + + ConstantPoolCacheEntry::flags_offset()))); + + // replace index with field offset from cache entry + __ movl(rbx, Address(rcx, rbx, Address::times_4, in_bytes(base + ConstantPoolCacheEntry::f2_offset()))); + + // Doug Lea believes this is not needed with current Sparcs (TSO) and Intel (PSO). + // volatile_barrier( ); + + Label notVolatile, Done; + __ shrl(rdx, ConstantPoolCacheEntry::volatileField); + __ andl(rdx, 0x1); + // Check for volatile store + __ testl(rdx, rdx); + __ jcc(Assembler::zero, notVolatile); + + if (bytecode() == Bytecodes::_fast_lputfield) __ popl(rdx); + + // Get object from stack + pop_and_check_object(rcx); + + // field addresses + const Address lo(rcx, rbx, Address::times_1, 0*wordSize); + const Address hi(rcx, rbx, Address::times_1, 1*wordSize); + + // access field + switch (bytecode()) { + case Bytecodes::_fast_bputfield: __ movb(lo, rax); break; + case Bytecodes::_fast_sputfield: // fall through + case Bytecodes::_fast_cputfield: __ movw(lo, rax); break; + case Bytecodes::_fast_iputfield: __ movl(lo, rax); break; + case Bytecodes::_fast_lputfield: __ movl(hi, rdx); __ movl(lo, rax); break; + case Bytecodes::_fast_fputfield: __ fstp_s(lo); break; + case Bytecodes::_fast_dputfield: __ fstp_d(lo); break; + case Bytecodes::_fast_aputfield: __ movl(lo, rax); __ store_check(rcx, lo); break; + default: + ShouldNotReachHere(); + } + + Label done; + volatile_barrier( ); + __ jmpb(done); + + // Same code as above, but don't need rdx to test for volatile. + __ bind(notVolatile); + + if (bytecode() == Bytecodes::_fast_lputfield) __ popl(rdx); + + // Get object from stack + pop_and_check_object(rcx); + + // access field + switch (bytecode()) { + case Bytecodes::_fast_bputfield: __ movb(lo, rax); break; + case Bytecodes::_fast_sputfield: // fall through + case Bytecodes::_fast_cputfield: __ movw(lo, rax); break; + case Bytecodes::_fast_iputfield: __ movl(lo, rax); break; + case Bytecodes::_fast_lputfield: __ movl(hi, rdx); __ movl(lo, rax); break; + case Bytecodes::_fast_fputfield: __ fstp_s(lo); break; + case Bytecodes::_fast_dputfield: __ fstp_d(lo); break; + case Bytecodes::_fast_aputfield: __ movl(lo, rax); __ store_check(rcx, lo); break; + default: + ShouldNotReachHere(); + } + __ bind(done); +} + + +void TemplateTable::fast_accessfield(TosState state) { + transition(atos, state); + + // do the JVMTI work here to avoid disturbing the register state below + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we take + // the time to call into the VM. + Label L1; + __ mov32(rcx, ExternalAddress((address) JvmtiExport::get_field_access_count_addr())); + __ testl(rcx,rcx); + __ jcc(Assembler::zero, L1); + // access constant pool cache entry + __ get_cache_entry_pointer_at_bcp(rcx, rdx, 1); + __ push_ptr(rax); // save object pointer before call_VM() clobbers it + __ verify_oop(rax); + // rax,: object pointer copied above + // rcx: cache entry pointer + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), rax, rcx); + __ pop_ptr(rax); // restore object pointer + __ bind(L1); + } + + // access constant pool cache + __ get_cache_and_index_at_bcp(rcx, rbx, 1); + // replace index with field offset from cache entry + __ movl(rbx, Address(rcx, rbx, Address::times_4, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f2_offset()))); + + + // rax,: object + __ verify_oop(rax); + __ null_check(rax); + // field addresses + const Address lo = Address(rax, rbx, Address::times_1, 0*wordSize); + const Address hi = Address(rax, rbx, Address::times_1, 1*wordSize); + + // access field + switch (bytecode()) { + case Bytecodes::_fast_bgetfield: __ movsxb(rax, lo ); break; + case Bytecodes::_fast_sgetfield: __ load_signed_word(rax, lo ); break; + case Bytecodes::_fast_cgetfield: __ load_unsigned_word(rax, lo ); break; + case Bytecodes::_fast_igetfield: __ movl(rax, lo); break; + case Bytecodes::_fast_lgetfield: __ stop("should not be rewritten"); break; + case Bytecodes::_fast_fgetfield: __ fld_s(lo); break; + case Bytecodes::_fast_dgetfield: __ fld_d(lo); break; + case Bytecodes::_fast_agetfield: __ movl(rax, lo); __ verify_oop(rax); break; + default: + ShouldNotReachHere(); + } + + // Doug Lea believes this is not needed with current Sparcs(TSO) and Intel(PSO) + // volatile_barrier( ); +} + +void TemplateTable::fast_xaccess(TosState state) { + transition(vtos, state); + // get receiver + __ movl(rax, aaddress(0)); + debug_only(__ verify_local_tag(frame::TagReference, 0)); + // access constant pool cache + __ get_cache_and_index_at_bcp(rcx, rdx, 2); + __ movl(rbx, Address(rcx, rdx, Address::times_4, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f2_offset()))); + // make sure exception is reported in correct bcp range (getfield is next instruction) + __ increment(rsi); + __ null_check(rax); + const Address lo = Address(rax, rbx, Address::times_1, 0*wordSize); + if (state == itos) { + __ movl(rax, lo); + } else if (state == atos) { + __ movl(rax, lo); + __ verify_oop(rax); + } else if (state == ftos) { + __ fld_s(lo); + } else { + ShouldNotReachHere(); + } + __ decrement(rsi); +} + + + +//---------------------------------------------------------------------------------------------------- +// Calls + +void TemplateTable::count_calls(Register method, Register temp) { + // implemented elsewhere + ShouldNotReachHere(); +} + + +void TemplateTable::prepare_invoke(Register method, Register index, int byte_no, Bytecodes::Code code) { + // determine flags + const bool is_invokeinterface = code == Bytecodes::_invokeinterface; + const bool is_invokevirtual = code == Bytecodes::_invokevirtual; + const bool is_invokespecial = code == Bytecodes::_invokespecial; + const bool load_receiver = code != Bytecodes::_invokestatic; + const bool receiver_null_check = is_invokespecial; + const bool save_flags = is_invokeinterface || is_invokevirtual; + // setup registers & access constant pool cache + const Register recv = rcx; + const Register flags = rdx; + assert_different_registers(method, index, recv, flags); + + // save 'interpreter return address' + __ save_bcp(); + + load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual); + + // load receiver if needed (note: no return address pushed yet) + if (load_receiver) { + __ movl(recv, flags); + __ andl(recv, 0xFF); + // recv count is 0 based? + __ movl(recv, Address(rsp, recv, Interpreter::stackElementScale(), -Interpreter::expr_offset_in_bytes(1))); + __ verify_oop(recv); + } + + // do null check if needed + if (receiver_null_check) { + __ null_check(recv); + } + + if (save_flags) { + __ movl(rsi, flags); + } + + // compute return type + __ shrl(flags, ConstantPoolCacheEntry::tosBits); + // Make sure we don't need to mask flags for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + // load return address + { const int table = + is_invokeinterface + ? (int)Interpreter::return_5_addrs_by_index_table() + : (int)Interpreter::return_3_addrs_by_index_table(); + __ movl(flags, Address(noreg, flags, Address::times_4, table)); + } + + // push return address + __ pushl(flags); + + // Restore flag value from the constant pool cache, and restore rsi + // for later null checks. rsi is the bytecode pointer + if (save_flags) { + __ movl(flags, rsi); + __ restore_bcp(); + } +} + + +void TemplateTable::invokevirtual_helper(Register index, Register recv, + Register flags) { + + // Uses temporary registers rax, rdx + assert_different_registers(index, recv, rax, rdx); + + // Test for an invoke of a final method + Label notFinal; + __ movl(rax, flags); + __ andl(rax, (1 << ConstantPoolCacheEntry::vfinalMethod)); + __ jcc(Assembler::zero, notFinal); + + Register method = index; // method must be rbx, + assert(method == rbx, "methodOop must be rbx, for interpreter calling convention"); + + // do the call - the index is actually the method to call + __ verify_oop(method); + + // It's final, need a null check here! + __ null_check(recv); + + // profile this call + __ profile_final_call(rax); + + __ jump_from_interpreted(method, rax); + + __ bind(notFinal); + + // get receiver klass + __ null_check(recv, oopDesc::klass_offset_in_bytes()); + // Keep recv in rcx for callee expects it there + __ movl(rax, Address(recv, oopDesc::klass_offset_in_bytes())); + __ verify_oop(rax); + + // profile this call + __ profile_virtual_call(rax, rdi, rdx); + + // get target methodOop & entry point + const int base = instanceKlass::vtable_start_offset() * wordSize; + assert(vtableEntry::size() * wordSize == 4, "adjust the scaling in the code below"); + __ movl(method, Address(rax, index, Address::times_4, base + vtableEntry::method_offset_in_bytes())); + __ jump_from_interpreted(method, rdx); +} + + +void TemplateTable::invokevirtual(int byte_no) { + transition(vtos, vtos); + prepare_invoke(rbx, noreg, byte_no, bytecode()); + + // rbx,: index + // rcx: receiver + // rdx: flags + + invokevirtual_helper(rbx, rcx, rdx); +} + + +void TemplateTable::invokespecial(int byte_no) { + transition(vtos, vtos); + prepare_invoke(rbx, noreg, byte_no, bytecode()); + // do the call + __ verify_oop(rbx); + __ profile_call(rax); + __ jump_from_interpreted(rbx, rax); +} + + +void TemplateTable::invokestatic(int byte_no) { + transition(vtos, vtos); + prepare_invoke(rbx, noreg, byte_no, bytecode()); + // do the call + __ verify_oop(rbx); + __ profile_call(rax); + __ jump_from_interpreted(rbx, rax); +} + + +void TemplateTable::fast_invokevfinal(int byte_no) { + transition(vtos, vtos); + __ stop("fast_invokevfinal not used on x86"); +} + + +void TemplateTable::invokeinterface(int byte_no) { + transition(vtos, vtos); + prepare_invoke(rax, rbx, byte_no, bytecode()); + + // rax,: Interface + // rbx,: index + // rcx: receiver + // rdx: flags + + // Special case of invokeinterface called for virtual method of + // java.lang.Object. See cpCacheOop.cpp for details. + // This code isn't produced by javac, but could be produced by + // another compliant java compiler. + Label notMethod; + __ movl(rdi, rdx); + __ andl(rdi, (1 << ConstantPoolCacheEntry::methodInterface)); + __ jcc(Assembler::zero, notMethod); + + invokevirtual_helper(rbx, rcx, rdx); + __ bind(notMethod); + + // Get receiver klass into rdx - also a null check + __ restore_locals(); // restore rdi + __ movl(rdx, Address(rcx, oopDesc::klass_offset_in_bytes())); + __ verify_oop(rdx); + + // profile this call + __ profile_virtual_call(rdx, rsi, rdi); + + __ movl(rdi, rdx); // Save klassOop in rdi + + // Compute start of first itableOffsetEntry (which is at the end of the vtable) + const int base = instanceKlass::vtable_start_offset() * wordSize; + assert(vtableEntry::size() * wordSize == 4, "adjust the scaling in the code below"); + __ movl(rsi, Address(rdx, instanceKlass::vtable_length_offset() * wordSize)); // Get length of vtable + __ leal(rdx, Address(rdx, rsi, Address::times_4, base)); + if (HeapWordsPerLong > 1) { + // Round up to align_object_offset boundary + __ round_to(rdx, BytesPerLong); + } + + Label entry, search, interface_ok; + + __ jmpb(entry); + __ bind(search); + __ addl(rdx, itableOffsetEntry::size() * wordSize); + + __ bind(entry); + + // Check that the entry is non-null. A null entry means that the receiver + // class doesn't implement the interface, and wasn't the same as the + // receiver class checked when the interface was resolved. + __ pushl(rdx); + __ movl(rdx, Address(rdx, itableOffsetEntry::interface_offset_in_bytes())); + __ testl(rdx, rdx); + __ jcc(Assembler::notZero, interface_ok); + // throw exception + __ popl(rdx); // pop saved register first. + __ popl(rbx); // pop return address (pushed by prepare_invoke) + __ restore_bcp(); // rsi must be correct for exception handler (was destroyed) + __ restore_locals(); // make sure locals pointer is correct as well (was destroyed) + __ call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_IncompatibleClassChangeError)); + // the call_VM checks for exception, so we should never return here. + __ should_not_reach_here(); + __ bind(interface_ok); + + __ popl(rdx); + + __ cmpl(rax, Address(rdx, itableOffsetEntry::interface_offset_in_bytes())); + __ jcc(Assembler::notEqual, search); + + __ movl(rdx, Address(rdx, itableOffsetEntry::offset_offset_in_bytes())); + __ addl(rdx, rdi); // Add offset to klassOop + assert(itableMethodEntry::size() * wordSize == 4, "adjust the scaling in the code below"); + __ movl(rbx, Address(rdx, rbx, Address::times_4)); + // rbx,: methodOop to call + // rcx: receiver + // Check for abstract method error + // Note: This should be done more efficiently via a throw_abstract_method_error + // interpreter entry point and a conditional jump to it in case of a null + // method. + { Label L; + __ testl(rbx, rbx); + __ jcc(Assembler::notZero, L); + // throw exception + // note: must restore interpreter registers to canonical + // state for exception handling to work correctly! + __ popl(rbx); // pop return address (pushed by prepare_invoke) + __ restore_bcp(); // rsi must be correct for exception handler (was destroyed) + __ restore_locals(); // make sure locals pointer is correct as well (was destroyed) + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); + // the call_VM checks for exception, so we should never return here. + __ should_not_reach_here(); + __ bind(L); + } + + // do the call + // rcx: receiver + // rbx,: methodOop + __ jump_from_interpreted(rbx, rdx); +} + +//---------------------------------------------------------------------------------------------------- +// Allocation + +void TemplateTable::_new() { + transition(vtos, atos); + __ get_unsigned_2_byte_index_at_bcp(rdx, 1); + Label slow_case; + Label done; + Label initialize_header; + Label initialize_object; // including clearing the fields + Label allocate_shared; + + ExternalAddress heap_top((address)Universe::heap()->top_addr()); + + __ get_cpool_and_tags(rcx, rax); + // get instanceKlass + __ movl(rcx, Address(rcx, rdx, Address::times_4, sizeof(constantPoolOopDesc))); + __ pushl(rcx); // save the contexts of klass for initializing the header + + // make sure the class we're about to instantiate has been resolved. + // Note: slow_case does a pop of stack, which is why we loaded class/pushed above + const int tags_offset = typeArrayOopDesc::header_size(T_BYTE) * wordSize; + __ cmpb(Address(rax, rdx, Address::times_1, tags_offset), JVM_CONSTANT_Class); + __ jcc(Assembler::notEqual, slow_case); + + // make sure klass is initialized & doesn't have finalizer + // make sure klass is fully initialized + __ cmpl(Address(rcx, instanceKlass::init_state_offset_in_bytes() + sizeof(oopDesc)), instanceKlass::fully_initialized); + __ jcc(Assembler::notEqual, slow_case); + + // get instance_size in instanceKlass (scaled to a count of bytes) + __ movl(rdx, Address(rcx, Klass::layout_helper_offset_in_bytes() + sizeof(oopDesc))); + // test to see if it has a finalizer or is malformed in some way + __ testl(rdx, Klass::_lh_instance_slow_path_bit); + __ jcc(Assembler::notZero, slow_case); + + // + // Allocate the instance + // 1) Try to allocate in the TLAB + // 2) if fail and the object is large allocate in the shared Eden + // 3) if the above fails (or is not applicable), go to a slow case + // (creates a new TLAB, etc.) + + const bool allow_shared_alloc = + Universe::heap()->supports_inline_contig_alloc() && !CMSIncrementalMode; + + if (UseTLAB) { + const Register thread = rcx; + + __ get_thread(thread); + __ movl(rax, Address(thread, in_bytes(JavaThread::tlab_top_offset()))); + __ leal(rbx, Address(rax, rdx, Address::times_1)); + __ cmpl(rbx, Address(thread, in_bytes(JavaThread::tlab_end_offset()))); + __ jcc(Assembler::above, allow_shared_alloc ? allocate_shared : slow_case); + __ movl(Address(thread, in_bytes(JavaThread::tlab_top_offset())), rbx); + if (ZeroTLAB) { + // the fields have been already cleared + __ jmp(initialize_header); + } else { + // initialize both the header and fields + __ jmp(initialize_object); + } + } + + // Allocation in the shared Eden, if allowed. + // + // rdx: instance size in bytes + if (allow_shared_alloc) { + __ bind(allocate_shared); + + Label retry; + __ bind(retry); + __ mov32(rax, heap_top); + __ leal(rbx, Address(rax, rdx, Address::times_1)); + __ cmp32(rbx, ExternalAddress((address)Universe::heap()->end_addr())); + __ jcc(Assembler::above, slow_case); + + // Compare rax, with the top addr, and if still equal, store the new + // top addr in rbx, at the address of the top addr pointer. Sets ZF if was + // equal, and clears it otherwise. Use lock prefix for atomicity on MPs. + // + // rax,: object begin + // rbx,: object end + // rdx: instance size in bytes + if (os::is_MP()) __ lock(); + __ cmpxchgptr(rbx, heap_top); + + // if someone beat us on the allocation, try again, otherwise continue + __ jcc(Assembler::notEqual, retry); + } + + if (UseTLAB || Universe::heap()->supports_inline_contig_alloc()) { + // The object is initialized before the header. If the object size is + // zero, go directly to the header initialization. + __ bind(initialize_object); + __ decrement(rdx, sizeof(oopDesc)); + __ jcc(Assembler::zero, initialize_header); + + // Initialize topmost object field, divide rdx by 8, check if odd and + // test if zero. + __ xorl(rcx, rcx); // use zero reg to clear memory (shorter code) + __ shrl(rdx, LogBytesPerLong); // divide by 2*oopSize and set carry flag if odd + + // rdx must have been multiple of 8 +#ifdef ASSERT + // make sure rdx was multiple of 8 + Label L; + // Ignore partial flag stall after shrl() since it is debug VM + __ jccb(Assembler::carryClear, L); + __ stop("object size is not multiple of 2 - adjust this code"); + __ bind(L); + // rdx must be > 0, no extra check needed here +#endif + + // initialize remaining object fields: rdx was a multiple of 8 + { Label loop; + __ bind(loop); + __ movl(Address(rax, rdx, Address::times_8, sizeof(oopDesc) - 1*oopSize), rcx); + __ movl(Address(rax, rdx, Address::times_8, sizeof(oopDesc) - 2*oopSize), rcx); + __ decrement(rdx); + __ jcc(Assembler::notZero, loop); + } + + // initialize object header only. + __ bind(initialize_header); + if (UseBiasedLocking) { + __ popl(rcx); // get saved klass back in the register. + __ movl(rbx, Address(rcx, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes())); + __ movl(Address(rax, oopDesc::mark_offset_in_bytes ()), rbx); + } else { + __ movl(Address(rax, oopDesc::mark_offset_in_bytes ()), + (int)markOopDesc::prototype()); // header + __ popl(rcx); // get saved klass back in the register. + } + __ movl(Address(rax, oopDesc::klass_offset_in_bytes()), rcx); // klass + + { + SkipIfEqual skip_if(_masm, &DTraceAllocProbes, 0); + // Trigger dtrace event for fastpath + __ push(atos); + __ call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), rax); + __ pop(atos); + } + + __ jmp(done); + } + + // slow case + __ bind(slow_case); + __ popl(rcx); // restore stack pointer to what it was when we came in. + __ get_constant_pool(rax); + __ get_unsigned_2_byte_index_at_bcp(rdx, 1); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), rax, rdx); + + // continue + __ bind(done); +} + + +void TemplateTable::newarray() { + transition(itos, atos); + __ push_i(rax); // make sure everything is on the stack + __ load_unsigned_byte(rdx, at_bcp(1)); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::newarray), rdx, rax); + __ pop_i(rdx); // discard size +} + + +void TemplateTable::anewarray() { + transition(itos, atos); + __ get_unsigned_2_byte_index_at_bcp(rdx, 1); + __ get_constant_pool(rcx); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::anewarray), rcx, rdx, rax); +} + + +void TemplateTable::arraylength() { + transition(atos, itos); + __ null_check(rax, arrayOopDesc::length_offset_in_bytes()); + __ movl(rax, Address(rax, arrayOopDesc::length_offset_in_bytes())); +} + + +void TemplateTable::checkcast() { + transition(atos, atos); + Label done, is_null, ok_is_subtype, quicked, resolved; + __ testl(rax, rax); // Object is in EAX + __ jcc(Assembler::zero, is_null); + + // Get cpool & tags index + __ get_cpool_and_tags(rcx, rdx); // ECX=cpool, EDX=tags array + __ get_unsigned_2_byte_index_at_bcp(rbx, 1); // EBX=index + // See if bytecode has already been quicked + __ cmpb(Address(rdx, rbx, Address::times_1, typeArrayOopDesc::header_size(T_BYTE) * wordSize), JVM_CONSTANT_Class); + __ jcc(Assembler::equal, quicked); + + __ push(atos); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc) ); + __ pop_ptr(rdx); + __ jmpb(resolved); + + // Get superklass in EAX and subklass in EBX + __ bind(quicked); + __ movl(rdx, rax); // Save object in EDX; EAX needed for subtype check + __ movl(rax, Address(rcx, rbx, Address::times_4, sizeof(constantPoolOopDesc))); + + __ bind(resolved); + __ movl(rbx, Address(rdx, oopDesc::klass_offset_in_bytes())); + + // Generate subtype check. Blows ECX. Resets EDI. Object in EDX. + // Superklass in EAX. Subklass in EBX. + __ gen_subtype_check( rbx, ok_is_subtype ); + + // Come here on failure + __ pushl(rdx); + // object is at TOS + __ jump(ExternalAddress(Interpreter::_throw_ClassCastException_entry)); + + // Come here on success + __ bind(ok_is_subtype); + __ movl(rax,rdx); // Restore object in EDX + + // Collect counts on whether this check-cast sees NULLs a lot or not. + if (ProfileInterpreter) { + __ jmp(done); + __ bind(is_null); + __ profile_null_seen(rcx); + } else { + __ bind(is_null); // same as 'done' + } + __ bind(done); +} + + +void TemplateTable::instanceof() { + transition(atos, itos); + Label done, is_null, ok_is_subtype, quicked, resolved; + __ testl(rax, rax); + __ jcc(Assembler::zero, is_null); + + // Get cpool & tags index + __ get_cpool_and_tags(rcx, rdx); // ECX=cpool, EDX=tags array + __ get_unsigned_2_byte_index_at_bcp(rbx, 1); // EBX=index + // See if bytecode has already been quicked + __ cmpb(Address(rdx, rbx, Address::times_1, typeArrayOopDesc::header_size(T_BYTE) * wordSize), JVM_CONSTANT_Class); + __ jcc(Assembler::equal, quicked); + + __ push(atos); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc) ); + __ pop_ptr(rdx); + __ movl(rdx, Address(rdx, oopDesc::klass_offset_in_bytes())); + __ jmp(resolved); + + // Get superklass in EAX and subklass in EDX + __ bind(quicked); + __ movl(rdx, Address(rax, oopDesc::klass_offset_in_bytes())); + __ movl(rax, Address(rcx, rbx, Address::times_4, sizeof(constantPoolOopDesc))); + + __ bind(resolved); + + // Generate subtype check. Blows ECX. Resets EDI. + // Superklass in EAX. Subklass in EDX. + __ gen_subtype_check( rdx, ok_is_subtype ); + + // Come here on failure + __ xorl(rax,rax); + __ jmpb(done); + // Come here on success + __ bind(ok_is_subtype); + __ movl(rax, 1); + + // Collect counts on whether this test sees NULLs a lot or not. + if (ProfileInterpreter) { + __ jmp(done); + __ bind(is_null); + __ profile_null_seen(rcx); + } else { + __ bind(is_null); // same as 'done' + } + __ bind(done); + // rax, = 0: obj == NULL or obj is not an instanceof the specified klass + // rax, = 1: obj != NULL and obj is an instanceof the specified klass +} + + +//---------------------------------------------------------------------------------------------------- +// Breakpoints +void TemplateTable::_breakpoint() { + + // Note: We get here even if we are single stepping.. + // jbug inists on setting breakpoints at every bytecode + // even if we are in single step mode. + + transition(vtos, vtos); + + // get the unpatched byte code + __ get_method(rcx); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::get_original_bytecode_at), rcx, rsi); + __ movl(rbx, rax); + + // post the breakpoint event + __ get_method(rcx); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::_breakpoint), rcx, rsi); + + // complete the execution of original bytecode + __ dispatch_only_normal(vtos); +} + + +//---------------------------------------------------------------------------------------------------- +// Exceptions + +void TemplateTable::athrow() { + transition(atos, vtos); + __ null_check(rax); + __ jump(ExternalAddress(Interpreter::throw_exception_entry())); +} + + +//---------------------------------------------------------------------------------------------------- +// Synchronization +// +// Note: monitorenter & exit are symmetric routines; which is reflected +// in the assembly code structure as well +// +// Stack layout: +// +// [expressions ] <--- rsp = expression stack top +// .. +// [expressions ] +// [monitor entry] <--- monitor block top = expression stack bot +// .. +// [monitor entry] +// [frame data ] <--- monitor block bot +// ... +// [saved rbp, ] <--- rbp, + + +void TemplateTable::monitorenter() { + transition(atos, vtos); + + // check for NULL object + __ null_check(rax); + + const Address monitor_block_top(rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize); + const Address monitor_block_bot(rbp, frame::interpreter_frame_initial_sp_offset * wordSize); + const int entry_size = ( frame::interpreter_frame_monitor_size() * wordSize); + Label allocated; + + // initialize entry pointer + __ xorl(rdx, rdx); // points to free slot or NULL + + // find a free slot in the monitor block (result in rdx) + { Label entry, loop, exit; + __ movl(rcx, monitor_block_top); // points to current entry, starting with top-most entry + __ leal(rbx, monitor_block_bot); // points to word before bottom of monitor block + __ jmpb(entry); + + __ bind(loop); + __ cmpl(Address(rcx, BasicObjectLock::obj_offset_in_bytes()), NULL_WORD); // check if current entry is used + +// TODO - need new func here - kbt + if (VM_Version::supports_cmov()) { + __ cmovl(Assembler::equal, rdx, rcx); // if not used then remember entry in rdx + } else { + Label L; + __ jccb(Assembler::notEqual, L); + __ movl(rdx, rcx); // if not used then remember entry in rdx + __ bind(L); + } + __ cmpl(rax, Address(rcx, BasicObjectLock::obj_offset_in_bytes())); // check if current entry is for same object + __ jccb(Assembler::equal, exit); // if same object then stop searching + __ addl(rcx, entry_size); // otherwise advance to next entry + __ bind(entry); + __ cmpl(rcx, rbx); // check if bottom reached + __ jcc(Assembler::notEqual, loop); // if not at bottom then check this entry + __ bind(exit); + } + + __ testl(rdx, rdx); // check if a slot has been found + __ jccb(Assembler::notZero, allocated); // if found, continue with that one + + // allocate one if there's no free slot + { Label entry, loop; + // 1. compute new pointers // rsp: old expression stack top + __ movl(rdx, monitor_block_bot); // rdx: old expression stack bottom + __ subl(rsp, entry_size); // move expression stack top + __ subl(rdx, entry_size); // move expression stack bottom + __ movl(rcx, rsp); // set start value for copy loop + __ movl(monitor_block_bot, rdx); // set new monitor block top + __ jmp(entry); + // 2. move expression stack contents + __ bind(loop); + __ movl(rbx, Address(rcx, entry_size)); // load expression stack word from old location + __ movl(Address(rcx, 0), rbx); // and store it at new location + __ addl(rcx, wordSize); // advance to next word + __ bind(entry); + __ cmpl(rcx, rdx); // check if bottom reached + __ jcc(Assembler::notEqual, loop); // if not at bottom then copy next word + } + + // call run-time routine + // rdx: points to monitor entry + __ bind(allocated); + + // Increment bcp to point to the next bytecode, so exception handling for async. exceptions work correctly. + // The object has already been poped from the stack, so the expression stack looks correct. + __ increment(rsi); + + __ movl(Address(rdx, BasicObjectLock::obj_offset_in_bytes()), rax); // store object + __ lock_object(rdx); + + // check to make sure this monitor doesn't cause stack overflow after locking + __ save_bcp(); // in case of exception + __ generate_stack_overflow_check(0); + + // The bcp has already been incremented. Just need to dispatch to next instruction. + __ dispatch_next(vtos); +} + + +void TemplateTable::monitorexit() { + transition(atos, vtos); + + // check for NULL object + __ null_check(rax); + + const Address monitor_block_top(rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize); + const Address monitor_block_bot(rbp, frame::interpreter_frame_initial_sp_offset * wordSize); + const int entry_size = ( frame::interpreter_frame_monitor_size() * wordSize); + Label found; + + // find matching slot + { Label entry, loop; + __ movl(rdx, monitor_block_top); // points to current entry, starting with top-most entry + __ leal(rbx, monitor_block_bot); // points to word before bottom of monitor block + __ jmpb(entry); + + __ bind(loop); + __ cmpl(rax, Address(rdx, BasicObjectLock::obj_offset_in_bytes())); // check if current entry is for same object + __ jcc(Assembler::equal, found); // if same object then stop searching + __ addl(rdx, entry_size); // otherwise advance to next entry + __ bind(entry); + __ cmpl(rdx, rbx); // check if bottom reached + __ jcc(Assembler::notEqual, loop); // if not at bottom then check this entry + } + + // error handling. Unlocking was not block-structured + Label end; + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + __ should_not_reach_here(); + + // call run-time routine + // rcx: points to monitor entry + __ bind(found); + __ push_ptr(rax); // make sure object is on stack (contract with oopMaps) + __ unlock_object(rdx); + __ pop_ptr(rax); // discard object + __ bind(end); +} + + +//---------------------------------------------------------------------------------------------------- +// Wide instructions + +void TemplateTable::wide() { + transition(vtos, vtos); + __ load_unsigned_byte(rbx, at_bcp(1)); + __ jmp(Address(noreg, rbx, Address::times_4, int(Interpreter::_wentry_point))); + // Note: the rsi increment step is part of the individual wide bytecode implementations +} + + +//---------------------------------------------------------------------------------------------------- +// Multi arrays + +void TemplateTable::multianewarray() { + transition(vtos, atos); + __ load_unsigned_byte(rax, at_bcp(3)); // get number of dimensions + // last dim is on top of stack; we want address of first one: + // first_addr = last_addr + (ndims - 1) * stackElementSize - 1*wordsize + // the latter wordSize to point to the beginning of the array. + __ leal( rax, Address(rsp, rax, Interpreter::stackElementScale(), -wordSize)); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::multianewarray), rax); // pass in rax, + __ load_unsigned_byte(rbx, at_bcp(3)); + __ leal(rsp, Address(rsp, rbx, Interpreter::stackElementScale())); // get rid of counts +} + +#endif /* !CC_INTERP */ diff --git a/hotspot/src/cpu/x86/vm/templateTable_x86_32.hpp b/hotspot/src/cpu/x86/vm/templateTable_x86_32.hpp new file mode 100644 index 00000000000..58f09235684 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/templateTable_x86_32.hpp @@ -0,0 +1,33 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + static void prepare_invoke(Register method, Register index, int byte_no, + Bytecodes::Code code); + static void invokevirtual_helper(Register index, Register recv, + Register flags); + static void volatile_barrier( ); + + // Helpers + static void index_check(Register array, Register index); + static void index_check_without_pop(Register array, Register index); diff --git a/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp b/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp new file mode 100644 index 00000000000..50db0e5853a --- /dev/null +++ b/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp @@ -0,0 +1,3546 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_templateTable_x86_64.cpp.incl" + +#define __ _masm-> + +// Platform-dependent initialization + +void TemplateTable::pd_initialize() { + // No amd64 specific initialization +} + +// Address computation: local variables + +static inline Address iaddress(int n) { + return Address(r14, Interpreter::local_offset_in_bytes(n)); +} + +static inline Address laddress(int n) { + return iaddress(n + 1); +} + +static inline Address faddress(int n) { + return iaddress(n); +} + +static inline Address daddress(int n) { + return laddress(n); +} + +static inline Address aaddress(int n) { + return iaddress(n); +} + +static inline Address iaddress(Register r) { + return Address(r14, r, Address::times_8, Interpreter::value_offset_in_bytes()); +} + +static inline Address laddress(Register r) { + return Address(r14, r, Address::times_8, Interpreter::local_offset_in_bytes(1)); +} + +static inline Address faddress(Register r) { + return iaddress(r); +} + +static inline Address daddress(Register r) { + return laddress(r); +} + +static inline Address aaddress(Register r) { + return iaddress(r); +} + +static inline Address at_rsp() { + return Address(rsp, 0); +} + +// At top of Java expression stack which may be different than esp(). It +// isn't for category 1 objects. +static inline Address at_tos () { + return Address(rsp, Interpreter::expr_offset_in_bytes(0)); +} + +static inline Address at_tos_p1() { + return Address(rsp, Interpreter::expr_offset_in_bytes(1)); +} + +static inline Address at_tos_p2() { + return Address(rsp, Interpreter::expr_offset_in_bytes(2)); +} + +static inline Address at_tos_p3() { + return Address(rsp, Interpreter::expr_offset_in_bytes(3)); +} + +// Condition conversion +static Assembler::Condition j_not(TemplateTable::Condition cc) { + switch (cc) { + case TemplateTable::equal : return Assembler::notEqual; + case TemplateTable::not_equal : return Assembler::equal; + case TemplateTable::less : return Assembler::greaterEqual; + case TemplateTable::less_equal : return Assembler::greater; + case TemplateTable::greater : return Assembler::lessEqual; + case TemplateTable::greater_equal: return Assembler::less; + } + ShouldNotReachHere(); + return Assembler::zero; +} + + +// Miscelaneous helper routines + +Address TemplateTable::at_bcp(int offset) { + assert(_desc->uses_bcp(), "inconsistent uses_bcp information"); + return Address(r13, offset); +} + +void TemplateTable::patch_bytecode(Bytecodes::Code bytecode, Register bc, + Register scratch, + bool load_bc_into_scratch/*=true*/) { + if (!RewriteBytecodes) { + return; + } + // the pair bytecodes have already done the load. + if (load_bc_into_scratch) { + __ movl(bc, bytecode); + } + Label patch_done; + if (JvmtiExport::can_post_breakpoint()) { + Label fast_patch; + // if a breakpoint is present we can't rewrite the stream directly + __ movzbl(scratch, at_bcp(0)); + __ cmpl(scratch, Bytecodes::_breakpoint); + __ jcc(Assembler::notEqual, fast_patch); + __ get_method(scratch); + // Let breakpoint table handling rewrite to quicker bytecode + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::set_original_bytecode_at), + scratch, r13, bc); +#ifndef ASSERT + __ jmpb(patch_done); + __ bind(fast_patch); + } +#else + __ jmp(patch_done); + __ bind(fast_patch); + } + Label okay; + __ load_unsigned_byte(scratch, at_bcp(0)); + __ cmpl(scratch, (int) Bytecodes::java_code(bytecode)); + __ jcc(Assembler::equal, okay); + __ cmpl(scratch, bc); + __ jcc(Assembler::equal, okay); + __ stop("patching the wrong bytecode"); + __ bind(okay); +#endif + // patch bytecode + __ movb(at_bcp(0), bc); + __ bind(patch_done); +} + + +// Individual instructions + +void TemplateTable::nop() { + transition(vtos, vtos); + // nothing to do +} + +void TemplateTable::shouldnotreachhere() { + transition(vtos, vtos); + __ stop("shouldnotreachhere bytecode"); +} + +void TemplateTable::aconst_null() { + transition(vtos, atos); + __ xorl(rax, rax); +} + +void TemplateTable::iconst(int value) { + transition(vtos, itos); + if (value == 0) { + __ xorl(rax, rax); + } else { + __ movl(rax, value); + } +} + +void TemplateTable::lconst(int value) { + transition(vtos, ltos); + if (value == 0) { + __ xorl(rax, rax); + } else { + __ movl(rax, value); + } +} + +void TemplateTable::fconst(int value) { + transition(vtos, ftos); + static float one = 1.0f, two = 2.0f; + switch (value) { + case 0: + __ xorps(xmm0, xmm0); + break; + case 1: + __ movflt(xmm0, ExternalAddress((address) &one)); + break; + case 2: + __ movflt(xmm0, ExternalAddress((address) &two)); + break; + default: + ShouldNotReachHere(); + break; + } +} + +void TemplateTable::dconst(int value) { + transition(vtos, dtos); + static double one = 1.0; + switch (value) { + case 0: + __ xorpd(xmm0, xmm0); + break; + case 1: + __ movdbl(xmm0, ExternalAddress((address) &one)); + break; + default: + ShouldNotReachHere(); + break; + } +} + +void TemplateTable::bipush() { + transition(vtos, itos); + __ load_signed_byte(rax, at_bcp(1)); +} + +void TemplateTable::sipush() { + transition(vtos, itos); + __ load_unsigned_word(rax, at_bcp(1)); + __ bswapl(rax); + __ sarl(rax, 16); +} + +void TemplateTable::ldc(bool wide) { + transition(vtos, vtos); + Label call_ldc, notFloat, notClass, Done; + + if (wide) { + __ get_unsigned_2_byte_index_at_bcp(rbx, 1); + } else { + __ load_unsigned_byte(rbx, at_bcp(1)); + } + + __ get_cpool_and_tags(rcx, rax); + const int base_offset = constantPoolOopDesc::header_size() * wordSize; + const int tags_offset = typeArrayOopDesc::header_size(T_BYTE) * wordSize; + + // get type + __ movzbl(rdx, Address(rax, rbx, Address::times_1, tags_offset)); + + // unresolved string - get the resolved string + __ cmpl(rdx, JVM_CONSTANT_UnresolvedString); + __ jccb(Assembler::equal, call_ldc); + + // unresolved class - get the resolved class + __ cmpl(rdx, JVM_CONSTANT_UnresolvedClass); + __ jccb(Assembler::equal, call_ldc); + + // unresolved class in error state - call into runtime to throw the error + // from the first resolution attempt + __ cmpl(rdx, JVM_CONSTANT_UnresolvedClassInError); + __ jccb(Assembler::equal, call_ldc); + + // resolved class - need to call vm to get java mirror of the class + __ cmpl(rdx, JVM_CONSTANT_Class); + __ jcc(Assembler::notEqual, notClass); + + __ bind(call_ldc); + __ movl(c_rarg1, wide); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), c_rarg1); + __ push_ptr(rax); + __ verify_oop(rax); + __ jmp(Done); + + __ bind(notClass); + __ cmpl(rdx, JVM_CONSTANT_Float); + __ jccb(Assembler::notEqual, notFloat); + // ftos + __ movflt(xmm0, Address(rcx, rbx, Address::times_8, base_offset)); + __ push_f(); + __ jmp(Done); + + __ bind(notFloat); +#ifdef ASSERT + { + Label L; + __ cmpl(rdx, JVM_CONSTANT_Integer); + __ jcc(Assembler::equal, L); + __ cmpl(rdx, JVM_CONSTANT_String); + __ jcc(Assembler::equal, L); + __ stop("unexpected tag type in ldc"); + __ bind(L); + } +#endif + // atos and itos + Label isOop; + __ cmpl(rdx, JVM_CONSTANT_Integer); + __ jcc(Assembler::notEqual, isOop); + __ movl(rax, Address(rcx, rbx, Address::times_8, base_offset)); + __ push_i(rax); + __ jmp(Done); + + __ bind(isOop); + __ movq(rax, Address(rcx, rbx, Address::times_8, base_offset)); + __ push_ptr(rax); + + if (VerifyOops) { + __ verify_oop(rax); + } + + __ bind(Done); +} + +void TemplateTable::ldc2_w() { + transition(vtos, vtos); + Label Long, Done; + __ get_unsigned_2_byte_index_at_bcp(rbx, 1); + + __ get_cpool_and_tags(rcx, rax); + const int base_offset = constantPoolOopDesc::header_size() * wordSize; + const int tags_offset = typeArrayOopDesc::header_size(T_BYTE) * wordSize; + + // get type + __ cmpb(Address(rax, rbx, Address::times_1, tags_offset), + JVM_CONSTANT_Double); + __ jccb(Assembler::notEqual, Long); + // dtos + __ movdbl(xmm0, Address(rcx, rbx, Address::times_8, base_offset)); + __ push_d(); + __ jmpb(Done); + + __ bind(Long); + // ltos + __ movq(rax, Address(rcx, rbx, Address::times_8, base_offset)); + __ push_l(); + + __ bind(Done); +} + +void TemplateTable::locals_index(Register reg, int offset) { + __ load_unsigned_byte(reg, at_bcp(offset)); + __ negq(reg); + if (TaggedStackInterpreter) __ shlq(reg, 1); // index = index*2 +} + +void TemplateTable::iload() { + transition(vtos, itos); + if (RewriteFrequentPairs) { + Label rewrite, done; + const Register bc = c_rarg3; + assert(rbx != bc, "register damaged"); + + // get next byte + __ load_unsigned_byte(rbx, + at_bcp(Bytecodes::length_for(Bytecodes::_iload))); + // if _iload, wait to rewrite to iload2. We only want to rewrite the + // last two iloads in a pair. Comparing against fast_iload means that + // the next bytecode is neither an iload or a caload, and therefore + // an iload pair. + __ cmpl(rbx, Bytecodes::_iload); + __ jcc(Assembler::equal, done); + + __ cmpl(rbx, Bytecodes::_fast_iload); + __ movl(bc, Bytecodes::_fast_iload2); + __ jccb(Assembler::equal, rewrite); + + // if _caload, rewrite to fast_icaload + __ cmpl(rbx, Bytecodes::_caload); + __ movl(bc, Bytecodes::_fast_icaload); + __ jccb(Assembler::equal, rewrite); + + // rewrite so iload doesn't check again. + __ movl(bc, Bytecodes::_fast_iload); + + // rewrite + // bc: fast bytecode + __ bind(rewrite); + patch_bytecode(Bytecodes::_iload, bc, rbx, false); + __ bind(done); + } + + // Get the local value into tos + locals_index(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + +void TemplateTable::fast_iload2() { + transition(vtos, itos); + locals_index(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); + __ push(itos); + locals_index(rbx, 3); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + +void TemplateTable::fast_iload() { + transition(vtos, itos); + locals_index(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + +void TemplateTable::lload() { + transition(vtos, ltos); + locals_index(rbx); + __ movq(rax, laddress(rbx)); + debug_only(__ verify_local_tag(frame::TagCategory2, rbx)); +} + +void TemplateTable::fload() { + transition(vtos, ftos); + locals_index(rbx); + __ movflt(xmm0, faddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + +void TemplateTable::dload() { + transition(vtos, dtos); + locals_index(rbx); + __ movdbl(xmm0, daddress(rbx)); + debug_only(__ verify_local_tag(frame::TagCategory2, rbx)); +} + +void TemplateTable::aload() { + transition(vtos, atos); + locals_index(rbx); + __ movq(rax, aaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagReference, rbx)); +} + +void TemplateTable::locals_index_wide(Register reg) { + __ movl(reg, at_bcp(2)); + __ bswapl(reg); + __ shrl(reg, 16); + __ negq(reg); + if (TaggedStackInterpreter) __ shlq(reg, 1); // index = index*2 +} + +void TemplateTable::wide_iload() { + transition(vtos, itos); + locals_index_wide(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + +void TemplateTable::wide_lload() { + transition(vtos, ltos); + locals_index_wide(rbx); + __ movq(rax, laddress(rbx)); + debug_only(__ verify_local_tag(frame::TagCategory2, rbx)); +} + +void TemplateTable::wide_fload() { + transition(vtos, ftos); + locals_index_wide(rbx); + __ movflt(xmm0, faddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); +} + +void TemplateTable::wide_dload() { + transition(vtos, dtos); + locals_index_wide(rbx); + __ movdbl(xmm0, daddress(rbx)); + debug_only(__ verify_local_tag(frame::TagCategory2, rbx)); +} + +void TemplateTable::wide_aload() { + transition(vtos, atos); + locals_index_wide(rbx); + __ movq(rax, aaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagReference, rbx)); +} + +void TemplateTable::index_check(Register array, Register index) { + // destroys rbx + // check array + __ null_check(array, arrayOopDesc::length_offset_in_bytes()); + // sign extend index for use by indexed load + __ movslq(index, index); + // check index + __ cmpl(index, Address(array, arrayOopDesc::length_offset_in_bytes())); + if (index != rbx) { + // ??? convention: move aberrant index into ebx for exception message + assert(rbx != array, "different registers"); + __ movl(rbx, index); + } + __ jump_cc(Assembler::aboveEqual, + ExternalAddress(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry)); +} + +void TemplateTable::iaload() { + transition(itos, itos); + __ pop_ptr(rdx); + // eax: index + // rdx: array + index_check(rdx, rax); // kills rbx + __ movl(rax, Address(rdx, rax, + Address::times_4, + arrayOopDesc::base_offset_in_bytes(T_INT))); +} + +void TemplateTable::laload() { + transition(itos, ltos); + __ pop_ptr(rdx); + // eax: index + // rdx: array + index_check(rdx, rax); // kills rbx + __ movq(rax, Address(rdx, rbx, + Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_LONG))); +} + +void TemplateTable::faload() { + transition(itos, ftos); + __ pop_ptr(rdx); + // eax: index + // rdx: array + index_check(rdx, rax); // kills rbx + __ movflt(xmm0, Address(rdx, rax, + Address::times_4, + arrayOopDesc::base_offset_in_bytes(T_FLOAT))); +} + +void TemplateTable::daload() { + transition(itos, dtos); + __ pop_ptr(rdx); + // eax: index + // rdx: array + index_check(rdx, rax); // kills rbx + __ movdbl(xmm0, Address(rdx, rax, + Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_DOUBLE))); +} + +void TemplateTable::aaload() { + transition(itos, atos); + __ pop_ptr(rdx); + // eax: index + // rdx: array + index_check(rdx, rax); // kills rbx + __ movq(rax, Address(rdx, rax, + Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_OBJECT))); +} + +void TemplateTable::baload() { + transition(itos, itos); + __ pop_ptr(rdx); + // eax: index + // rdx: array + index_check(rdx, rax); // kills rbx + __ load_signed_byte(rax, + Address(rdx, rax, + Address::times_1, + arrayOopDesc::base_offset_in_bytes(T_BYTE))); +} + +void TemplateTable::caload() { + transition(itos, itos); + __ pop_ptr(rdx); + // eax: index + // rdx: array + index_check(rdx, rax); // kills rbx + __ load_unsigned_word(rax, + Address(rdx, rax, + Address::times_2, + arrayOopDesc::base_offset_in_bytes(T_CHAR))); +} + +// iload followed by caload frequent pair +void TemplateTable::fast_icaload() { + transition(vtos, itos); + // load index out of locals + locals_index(rbx); + __ movl(rax, iaddress(rbx)); + debug_only(__ verify_local_tag(frame::TagValue, rbx)); + + // eax: index + // rdx: array + __ pop_ptr(rdx); + index_check(rdx, rax); // kills rbx + __ load_unsigned_word(rax, + Address(rdx, rax, + Address::times_2, + arrayOopDesc::base_offset_in_bytes(T_CHAR))); +} + +void TemplateTable::saload() { + transition(itos, itos); + __ pop_ptr(rdx); + // eax: index + // rdx: array + index_check(rdx, rax); // kills rbx + __ load_signed_word(rax, + Address(rdx, rax, + Address::times_2, + arrayOopDesc::base_offset_in_bytes(T_SHORT))); +} + +void TemplateTable::iload(int n) { + transition(vtos, itos); + __ movl(rax, iaddress(n)); + debug_only(__ verify_local_tag(frame::TagValue, n)); +} + +void TemplateTable::lload(int n) { + transition(vtos, ltos); + __ movq(rax, laddress(n)); + debug_only(__ verify_local_tag(frame::TagCategory2, n)); +} + +void TemplateTable::fload(int n) { + transition(vtos, ftos); + __ movflt(xmm0, faddress(n)); + debug_only(__ verify_local_tag(frame::TagValue, n)); +} + +void TemplateTable::dload(int n) { + transition(vtos, dtos); + __ movdbl(xmm0, daddress(n)); + debug_only(__ verify_local_tag(frame::TagCategory2, n)); +} + +void TemplateTable::aload(int n) { + transition(vtos, atos); + __ movq(rax, aaddress(n)); + debug_only(__ verify_local_tag(frame::TagReference, n)); +} + +void TemplateTable::aload_0() { + transition(vtos, atos); + // According to bytecode histograms, the pairs: + // + // _aload_0, _fast_igetfield + // _aload_0, _fast_agetfield + // _aload_0, _fast_fgetfield + // + // occur frequently. If RewriteFrequentPairs is set, the (slow) + // _aload_0 bytecode checks if the next bytecode is either + // _fast_igetfield, _fast_agetfield or _fast_fgetfield and then + // rewrites the current bytecode into a pair bytecode; otherwise it + // rewrites the current bytecode into _fast_aload_0 that doesn't do + // the pair check anymore. + // + // Note: If the next bytecode is _getfield, the rewrite must be + // delayed, otherwise we may miss an opportunity for a pair. + // + // Also rewrite frequent pairs + // aload_0, aload_1 + // aload_0, iload_1 + // These bytecodes with a small amount of code are most profitable + // to rewrite + if (RewriteFrequentPairs) { + Label rewrite, done; + const Register bc = c_rarg3; + assert(rbx != bc, "register damaged"); + // get next byte + __ load_unsigned_byte(rbx, + at_bcp(Bytecodes::length_for(Bytecodes::_aload_0))); + + // do actual aload_0 + aload(0); + + // if _getfield then wait with rewrite + __ cmpl(rbx, Bytecodes::_getfield); + __ jcc(Assembler::equal, done); + + // if _igetfield then reqrite to _fast_iaccess_0 + assert(Bytecodes::java_code(Bytecodes::_fast_iaccess_0) == + Bytecodes::_aload_0, + "fix bytecode definition"); + __ cmpl(rbx, Bytecodes::_fast_igetfield); + __ movl(bc, Bytecodes::_fast_iaccess_0); + __ jccb(Assembler::equal, rewrite); + + // if _agetfield then reqrite to _fast_aaccess_0 + assert(Bytecodes::java_code(Bytecodes::_fast_aaccess_0) == + Bytecodes::_aload_0, + "fix bytecode definition"); + __ cmpl(rbx, Bytecodes::_fast_agetfield); + __ movl(bc, Bytecodes::_fast_aaccess_0); + __ jccb(Assembler::equal, rewrite); + + // if _fgetfield then reqrite to _fast_faccess_0 + assert(Bytecodes::java_code(Bytecodes::_fast_faccess_0) == + Bytecodes::_aload_0, + "fix bytecode definition"); + __ cmpl(rbx, Bytecodes::_fast_fgetfield); + __ movl(bc, Bytecodes::_fast_faccess_0); + __ jccb(Assembler::equal, rewrite); + + // else rewrite to _fast_aload0 + assert(Bytecodes::java_code(Bytecodes::_fast_aload_0) == + Bytecodes::_aload_0, + "fix bytecode definition"); + __ movl(bc, Bytecodes::_fast_aload_0); + + // rewrite + // bc: fast bytecode + __ bind(rewrite); + patch_bytecode(Bytecodes::_aload_0, bc, rbx, false); + + __ bind(done); + } else { + aload(0); + } +} + +void TemplateTable::istore() { + transition(itos, vtos); + locals_index(rbx); + __ movl(iaddress(rbx), rax); + __ tag_local(frame::TagValue, rbx); +} + +void TemplateTable::lstore() { + transition(ltos, vtos); + locals_index(rbx); + __ movq(laddress(rbx), rax); + __ tag_local(frame::TagCategory2, rbx); +} + +void TemplateTable::fstore() { + transition(ftos, vtos); + locals_index(rbx); + __ movflt(faddress(rbx), xmm0); + __ tag_local(frame::TagValue, rbx); +} + +void TemplateTable::dstore() { + transition(dtos, vtos); + locals_index(rbx); + __ movdbl(daddress(rbx), xmm0); + __ tag_local(frame::TagCategory2, rbx); +} + +void TemplateTable::astore() { + transition(vtos, vtos); + __ pop_ptr(rax, rdx); // will need to pop tag too + locals_index(rbx); + __ movq(aaddress(rbx), rax); + __ tag_local(rdx, rbx); // store tag from stack, might be returnAddr +} + +void TemplateTable::wide_istore() { + transition(vtos, vtos); + __ pop_i(); + locals_index_wide(rbx); + __ movl(iaddress(rbx), rax); + __ tag_local(frame::TagValue, rbx); +} + +void TemplateTable::wide_lstore() { + transition(vtos, vtos); + __ pop_l(); + locals_index_wide(rbx); + __ movq(laddress(rbx), rax); + __ tag_local(frame::TagCategory2, rbx); +} + +void TemplateTable::wide_fstore() { + transition(vtos, vtos); + __ pop_f(); + locals_index_wide(rbx); + __ movflt(faddress(rbx), xmm0); + __ tag_local(frame::TagValue, rbx); +} + +void TemplateTable::wide_dstore() { + transition(vtos, vtos); + __ pop_d(); + locals_index_wide(rbx); + __ movdbl(daddress(rbx), xmm0); + __ tag_local(frame::TagCategory2, rbx); +} + +void TemplateTable::wide_astore() { + transition(vtos, vtos); + __ pop_ptr(rax, rdx); // will need to pop tag too + locals_index_wide(rbx); + __ movq(aaddress(rbx), rax); + __ tag_local(rdx, rbx); // store tag from stack, might be returnAddr +} + +void TemplateTable::iastore() { + transition(itos, vtos); + __ pop_i(rbx); + __ pop_ptr(rdx); + // eax: value + // ebx: index + // rdx: array + index_check(rdx, rbx); // prefer index in ebx + __ movl(Address(rdx, rbx, + Address::times_4, + arrayOopDesc::base_offset_in_bytes(T_INT)), + rax); +} + +void TemplateTable::lastore() { + transition(ltos, vtos); + __ pop_i(rbx); + __ pop_ptr(rdx); + // rax: value + // ebx: index + // rdx: array + index_check(rdx, rbx); // prefer index in ebx + __ movq(Address(rdx, rbx, + Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_LONG)), + rax); +} + +void TemplateTable::fastore() { + transition(ftos, vtos); + __ pop_i(rbx); + __ pop_ptr(rdx); + // xmm0: value + // ebx: index + // rdx: array + index_check(rdx, rbx); // prefer index in ebx + __ movflt(Address(rdx, rbx, + Address::times_4, + arrayOopDesc::base_offset_in_bytes(T_FLOAT)), + xmm0); +} + +void TemplateTable::dastore() { + transition(dtos, vtos); + __ pop_i(rbx); + __ pop_ptr(rdx); + // xmm0: value + // ebx: index + // rdx: array + index_check(rdx, rbx); // prefer index in ebx + __ movdbl(Address(rdx, rbx, + Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_DOUBLE)), + xmm0); +} + +void TemplateTable::aastore() { + Label is_null, ok_is_subtype, done; + transition(vtos, vtos); + // stack: ..., array, index, value + __ movq(rax, at_tos()); // value + __ movl(rcx, at_tos_p1()); // index + __ movq(rdx, at_tos_p2()); // array + index_check(rdx, rcx); // kills rbx + // do array store check - check for NULL value first + __ testq(rax, rax); + __ jcc(Assembler::zero, is_null); + + // Move subklass into rbx + __ movq(rbx, Address(rax, oopDesc::klass_offset_in_bytes())); + // Move superklass into rax + __ movq(rax, Address(rdx, oopDesc::klass_offset_in_bytes())); + __ movq(rax, Address(rax, + sizeof(oopDesc) + + objArrayKlass::element_klass_offset_in_bytes())); + // Compress array + index*8 + 12 into a single register. Frees rcx. + __ leaq(rdx, Address(rdx, rcx, + Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_OBJECT))); + + // Generate subtype check. Blows rcx, rdi + // Superklass in rax. Subklass in rbx. + __ gen_subtype_check(rbx, ok_is_subtype); + + // Come here on failure + // object is at TOS + __ jump(ExternalAddress(Interpreter::_throw_ArrayStoreException_entry)); + + // Come here on success + __ bind(ok_is_subtype); + __ movq(rax, at_tos()); // Value + __ movq(Address(rdx, 0), rax); + __ store_check(rdx); + __ jmp(done); + + // Have a NULL in rax, rdx=array, ecx=index. Store NULL at ary[idx] + __ bind(is_null); + __ profile_null_seen(rbx); + __ movq(Address(rdx, rcx, + Address::times_8, + arrayOopDesc::base_offset_in_bytes(T_OBJECT)), + rax); + + // Pop stack arguments + __ bind(done); + __ addq(rsp, 3 * Interpreter::stackElementSize()); +} + +void TemplateTable::bastore() { + transition(itos, vtos); + __ pop_i(rbx); + __ pop_ptr(rdx); + // eax: value + // ebx: index + // rdx: array + index_check(rdx, rbx); // prefer index in ebx + __ movb(Address(rdx, rbx, + Address::times_1, + arrayOopDesc::base_offset_in_bytes(T_BYTE)), + rax); +} + +void TemplateTable::castore() { + transition(itos, vtos); + __ pop_i(rbx); + __ pop_ptr(rdx); + // eax: value + // ebx: index + // rdx: array + index_check(rdx, rbx); // prefer index in ebx + __ movw(Address(rdx, rbx, + Address::times_2, + arrayOopDesc::base_offset_in_bytes(T_CHAR)), + rax); +} + +void TemplateTable::sastore() { + castore(); +} + +void TemplateTable::istore(int n) { + transition(itos, vtos); + __ movl(iaddress(n), rax); + __ tag_local(frame::TagValue, n); +} + +void TemplateTable::lstore(int n) { + transition(ltos, vtos); + __ movq(laddress(n), rax); + __ tag_local(frame::TagCategory2, n); +} + +void TemplateTable::fstore(int n) { + transition(ftos, vtos); + __ movflt(faddress(n), xmm0); + __ tag_local(frame::TagValue, n); +} + +void TemplateTable::dstore(int n) { + transition(dtos, vtos); + __ movdbl(daddress(n), xmm0); + __ tag_local(frame::TagCategory2, n); +} + +void TemplateTable::astore(int n) { + transition(vtos, vtos); + __ pop_ptr(rax, rdx); + __ movq(aaddress(n), rax); + __ tag_local(rdx, n); +} + +void TemplateTable::pop() { + transition(vtos, vtos); + __ addq(rsp, Interpreter::stackElementSize()); +} + +void TemplateTable::pop2() { + transition(vtos, vtos); + __ addq(rsp, 2 * Interpreter::stackElementSize()); +} + +void TemplateTable::dup() { + transition(vtos, vtos); + __ load_ptr_and_tag(0, rax, rdx); + __ push_ptr(rax, rdx); + // stack: ..., a, a +} + +void TemplateTable::dup_x1() { + transition(vtos, vtos); + // stack: ..., a, b + __ load_ptr_and_tag(0, rax, rdx); // load b + __ load_ptr_and_tag(1, rcx, rbx); // load a + __ store_ptr_and_tag(1, rax, rdx); // store b + __ store_ptr_and_tag(0, rcx, rbx); // store a + __ push_ptr(rax, rdx); // push b + // stack: ..., b, a, b +} + +void TemplateTable::dup_x2() { + transition(vtos, vtos); + // stack: ..., a, b, c + __ load_ptr_and_tag(0, rax, rdx); // load c + __ load_ptr_and_tag(2, rcx, rbx); // load a + __ store_ptr_and_tag(2, rax, rdx); // store c in a + __ push_ptr(rax, rdx); // push c + // stack: ..., c, b, c, c + __ load_ptr_and_tag(2, rax, rdx); // load b + __ store_ptr_and_tag(2, rcx, rbx); // store a in b + // stack: ..., c, a, c, c + __ store_ptr_and_tag(1, rax, rdx); // store b in c + // stack: ..., c, a, b, c +} + +void TemplateTable::dup2() { + transition(vtos, vtos); + // stack: ..., a, b + __ load_ptr_and_tag(1, rax, rdx); // load a + __ push_ptr(rax, rdx); // push a + __ load_ptr_and_tag(1, rax, rdx); // load b + __ push_ptr(rax, rdx); // push b + // stack: ..., a, b, a, b +} + +void TemplateTable::dup2_x1() { + transition(vtos, vtos); + // stack: ..., a, b, c + __ load_ptr_and_tag(0, rcx, rbx); // load c + __ load_ptr_and_tag(1, rax, rdx); // load b + __ push_ptr(rax, rdx); // push b + __ push_ptr(rcx, rbx); // push c + // stack: ..., a, b, c, b, c + __ store_ptr_and_tag(3, rcx, rbx); // store c in b + // stack: ..., a, c, c, b, c + __ load_ptr_and_tag(4, rcx, rbx); // load a + __ store_ptr_and_tag(2, rcx, rbx); // store a in 2nd c + // stack: ..., a, c, a, b, c + __ store_ptr_and_tag(4, rax, rdx); // store b in a + // stack: ..., b, c, a, b, c +} + +void TemplateTable::dup2_x2() { + transition(vtos, vtos); + // stack: ..., a, b, c, d + __ load_ptr_and_tag(0, rcx, rbx); // load d + __ load_ptr_and_tag(1, rax, rdx); // load c + __ push_ptr(rax, rdx); // push c + __ push_ptr(rcx, rbx); // push d + // stack: ..., a, b, c, d, c, d + __ load_ptr_and_tag(4, rax, rdx); // load b + __ store_ptr_and_tag(2, rax, rdx); // store b in d + __ store_ptr_and_tag(4, rcx, rbx); // store d in b + // stack: ..., a, d, c, b, c, d + __ load_ptr_and_tag(5, rcx, rbx); // load a + __ load_ptr_and_tag(3, rax, rdx); // load c + __ store_ptr_and_tag(3, rcx, rbx); // store a in c + __ store_ptr_and_tag(5, rax, rdx); // store c in a + // stack: ..., c, d, a, b, c, d +} + +void TemplateTable::swap() { + transition(vtos, vtos); + // stack: ..., a, b + __ load_ptr_and_tag(1, rcx, rbx); // load a + __ load_ptr_and_tag(0, rax, rdx); // load b + __ store_ptr_and_tag(0, rcx, rbx); // store a in b + __ store_ptr_and_tag(1, rax, rdx); // store b in a + // stack: ..., b, a +} + +void TemplateTable::iop2(Operation op) { + transition(itos, itos); + switch (op) { + case add : __ pop_i(rdx); __ addl (rax, rdx); break; + case sub : __ movl(rdx, rax); __ pop_i(rax); __ subl (rax, rdx); break; + case mul : __ pop_i(rdx); __ imull(rax, rdx); break; + case _and : __ pop_i(rdx); __ andl (rax, rdx); break; + case _or : __ pop_i(rdx); __ orl (rax, rdx); break; + case _xor : __ pop_i(rdx); __ xorl (rax, rdx); break; + case shl : __ movl(rcx, rax); __ pop_i(rax); __ shll (rax); break; + case shr : __ movl(rcx, rax); __ pop_i(rax); __ sarl (rax); break; + case ushr : __ movl(rcx, rax); __ pop_i(rax); __ shrl (rax); break; + default : ShouldNotReachHere(); + } +} + +void TemplateTable::lop2(Operation op) { + transition(ltos, ltos); + switch (op) { + case add : __ pop_l(rdx); __ addq (rax, rdx); break; + case sub : __ movq(rdx, rax); __ pop_l(rax); __ subq (rax, rdx); break; + case _and : __ pop_l(rdx); __ andq (rax, rdx); break; + case _or : __ pop_l(rdx); __ orq (rax, rdx); break; + case _xor : __ pop_l(rdx); __ xorq (rax, rdx); break; + default : ShouldNotReachHere(); + } +} + +void TemplateTable::idiv() { + transition(itos, itos); + __ movl(rcx, rax); + __ pop_i(rax); + // Note: could xor eax and ecx and compare with (-1 ^ min_int). If + // they are not equal, one could do a normal division (no correction + // needed), which may speed up this implementation for the common case. + // (see also JVM spec., p.243 & p.271) + __ corrected_idivl(rcx); +} + +void TemplateTable::irem() { + transition(itos, itos); + __ movl(rcx, rax); + __ pop_i(rax); + // Note: could xor eax and ecx and compare with (-1 ^ min_int). If + // they are not equal, one could do a normal division (no correction + // needed), which may speed up this implementation for the common case. + // (see also JVM spec., p.243 & p.271) + __ corrected_idivl(rcx); + __ movl(rax, rdx); +} + +void TemplateTable::lmul() { + transition(ltos, ltos); + __ pop_l(rdx); + __ imulq(rax, rdx); +} + +void TemplateTable::ldiv() { + transition(ltos, ltos); + __ movq(rcx, rax); + __ pop_l(rax); + // generate explicit div0 check + __ testq(rcx, rcx); + __ jump_cc(Assembler::zero, + ExternalAddress(Interpreter::_throw_ArithmeticException_entry)); + // Note: could xor rax and rcx and compare with (-1 ^ min_int). If + // they are not equal, one could do a normal division (no correction + // needed), which may speed up this implementation for the common case. + // (see also JVM spec., p.243 & p.271) + __ corrected_idivq(rcx); // kills rbx +} + +void TemplateTable::lrem() { + transition(ltos, ltos); + __ movq(rcx, rax); + __ pop_l(rax); + __ testq(rcx, rcx); + __ jump_cc(Assembler::zero, + ExternalAddress(Interpreter::_throw_ArithmeticException_entry)); + // Note: could xor rax and rcx and compare with (-1 ^ min_int). If + // they are not equal, one could do a normal division (no correction + // needed), which may speed up this implementation for the common case. + // (see also JVM spec., p.243 & p.271) + __ corrected_idivq(rcx); // kills rbx + __ movq(rax, rdx); +} + +void TemplateTable::lshl() { + transition(itos, ltos); + __ movl(rcx, rax); // get shift count + __ pop_l(rax); // get shift value + __ shlq(rax); +} + +void TemplateTable::lshr() { + transition(itos, ltos); + __ movl(rcx, rax); // get shift count + __ pop_l(rax); // get shift value + __ sarq(rax); +} + +void TemplateTable::lushr() { + transition(itos, ltos); + __ movl(rcx, rax); // get shift count + __ pop_l(rax); // get shift value + __ shrq(rax); +} + +void TemplateTable::fop2(Operation op) { + transition(ftos, ftos); + switch (op) { + case add: + __ addss(xmm0, at_rsp()); + __ addq(rsp, Interpreter::stackElementSize()); + break; + case sub: + __ movflt(xmm1, xmm0); + __ pop_f(xmm0); + __ subss(xmm0, xmm1); + break; + case mul: + __ mulss(xmm0, at_rsp()); + __ addq(rsp, Interpreter::stackElementSize()); + break; + case div: + __ movflt(xmm1, xmm0); + __ pop_f(xmm0); + __ divss(xmm0, xmm1); + break; + case rem: + __ movflt(xmm1, xmm0); + __ pop_f(xmm0); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::frem), 2); + break; + default: + ShouldNotReachHere(); + break; + } +} + +void TemplateTable::dop2(Operation op) { + transition(dtos, dtos); + switch (op) { + case add: + __ addsd(xmm0, at_rsp()); + __ addq(rsp, 2 * Interpreter::stackElementSize()); + break; + case sub: + __ movdbl(xmm1, xmm0); + __ pop_d(xmm0); + __ subsd(xmm0, xmm1); + break; + case mul: + __ mulsd(xmm0, at_rsp()); + __ addq(rsp, 2 * Interpreter::stackElementSize()); + break; + case div: + __ movdbl(xmm1, xmm0); + __ pop_d(xmm0); + __ divsd(xmm0, xmm1); + break; + case rem: + __ movdbl(xmm1, xmm0); + __ pop_d(xmm0); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::drem), 2); + break; + default: + ShouldNotReachHere(); + break; + } +} + +void TemplateTable::ineg() { + transition(itos, itos); + __ negl(rax); +} + +void TemplateTable::lneg() { + transition(ltos, ltos); + __ negq(rax); +} + +// Note: 'double' and 'long long' have 32-bits alignment on x86. +static jlong* double_quadword(jlong *adr, jlong lo, jlong hi) { + // Use the expression (adr)&(~0xF) to provide 128-bits aligned address + // of 128-bits operands for SSE instructions. + jlong *operand = (jlong*)(((intptr_t)adr)&((intptr_t)(~0xF))); + // Store the value to a 128-bits operand. + operand[0] = lo; + operand[1] = hi; + return operand; +} + +// Buffer for 128-bits masks used by SSE instructions. +static jlong float_signflip_pool[2*2]; +static jlong double_signflip_pool[2*2]; + +void TemplateTable::fneg() { + transition(ftos, ftos); + static jlong *float_signflip = double_quadword(&float_signflip_pool[1], 0x8000000080000000, 0x8000000080000000); + __ xorps(xmm0, ExternalAddress((address) float_signflip)); +} + +void TemplateTable::dneg() { + transition(dtos, dtos); + static jlong *double_signflip = double_quadword(&double_signflip_pool[1], 0x8000000000000000, 0x8000000000000000); + __ xorpd(xmm0, ExternalAddress((address) double_signflip)); +} + +void TemplateTable::iinc() { + transition(vtos, vtos); + __ load_signed_byte(rdx, at_bcp(2)); // get constant + locals_index(rbx); + __ addl(iaddress(rbx), rdx); +} + +void TemplateTable::wide_iinc() { + transition(vtos, vtos); + __ movl(rdx, at_bcp(4)); // get constant + locals_index_wide(rbx); + __ bswapl(rdx); // swap bytes & sign-extend constant + __ sarl(rdx, 16); + __ addl(iaddress(rbx), rdx); + // Note: should probably use only one movl to get both + // the index and the constant -> fix this +} + +void TemplateTable::convert() { + // Checking +#ifdef ASSERT + { + TosState tos_in = ilgl; + TosState tos_out = ilgl; + switch (bytecode()) { + case Bytecodes::_i2l: // fall through + case Bytecodes::_i2f: // fall through + case Bytecodes::_i2d: // fall through + case Bytecodes::_i2b: // fall through + case Bytecodes::_i2c: // fall through + case Bytecodes::_i2s: tos_in = itos; break; + case Bytecodes::_l2i: // fall through + case Bytecodes::_l2f: // fall through + case Bytecodes::_l2d: tos_in = ltos; break; + case Bytecodes::_f2i: // fall through + case Bytecodes::_f2l: // fall through + case Bytecodes::_f2d: tos_in = ftos; break; + case Bytecodes::_d2i: // fall through + case Bytecodes::_d2l: // fall through + case Bytecodes::_d2f: tos_in = dtos; break; + default : ShouldNotReachHere(); + } + switch (bytecode()) { + case Bytecodes::_l2i: // fall through + case Bytecodes::_f2i: // fall through + case Bytecodes::_d2i: // fall through + case Bytecodes::_i2b: // fall through + case Bytecodes::_i2c: // fall through + case Bytecodes::_i2s: tos_out = itos; break; + case Bytecodes::_i2l: // fall through + case Bytecodes::_f2l: // fall through + case Bytecodes::_d2l: tos_out = ltos; break; + case Bytecodes::_i2f: // fall through + case Bytecodes::_l2f: // fall through + case Bytecodes::_d2f: tos_out = ftos; break; + case Bytecodes::_i2d: // fall through + case Bytecodes::_l2d: // fall through + case Bytecodes::_f2d: tos_out = dtos; break; + default : ShouldNotReachHere(); + } + transition(tos_in, tos_out); + } +#endif // ASSERT + + static const int64_t is_nan = 0x8000000000000000L; + + // Conversion + switch (bytecode()) { + case Bytecodes::_i2l: + __ movslq(rax, rax); + break; + case Bytecodes::_i2f: + __ cvtsi2ssl(xmm0, rax); + break; + case Bytecodes::_i2d: + __ cvtsi2sdl(xmm0, rax); + break; + case Bytecodes::_i2b: + __ movsbl(rax, rax); + break; + case Bytecodes::_i2c: + __ movzwl(rax, rax); + break; + case Bytecodes::_i2s: + __ movswl(rax, rax); + break; + case Bytecodes::_l2i: + __ movl(rax, rax); + break; + case Bytecodes::_l2f: + __ cvtsi2ssq(xmm0, rax); + break; + case Bytecodes::_l2d: + __ cvtsi2sdq(xmm0, rax); + break; + case Bytecodes::_f2i: + { + Label L; + __ cvttss2sil(rax, xmm0); + __ cmpl(rax, 0x80000000); // NaN or overflow/underflow? + __ jcc(Assembler::notEqual, L); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2i), 1); + __ bind(L); + } + break; + case Bytecodes::_f2l: + { + Label L; + __ cvttss2siq(rax, xmm0); + // NaN or overflow/underflow? + __ cmp64(rax, ExternalAddress((address) &is_nan)); + __ jcc(Assembler::notEqual, L); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2l), 1); + __ bind(L); + } + break; + case Bytecodes::_f2d: + __ cvtss2sd(xmm0, xmm0); + break; + case Bytecodes::_d2i: + { + Label L; + __ cvttsd2sil(rax, xmm0); + __ cmpl(rax, 0x80000000); // NaN or overflow/underflow? + __ jcc(Assembler::notEqual, L); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2i), 1); + __ bind(L); + } + break; + case Bytecodes::_d2l: + { + Label L; + __ cvttsd2siq(rax, xmm0); + // NaN or overflow/underflow? + __ cmp64(rax, ExternalAddress((address) &is_nan)); + __ jcc(Assembler::notEqual, L); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2l), 1); + __ bind(L); + } + break; + case Bytecodes::_d2f: + __ cvtsd2ss(xmm0, xmm0); + break; + default: + ShouldNotReachHere(); + } +} + +void TemplateTable::lcmp() { + transition(ltos, itos); + Label done; + __ pop_l(rdx); + __ cmpq(rdx, rax); + __ movl(rax, -1); + __ jccb(Assembler::less, done); + __ setb(Assembler::notEqual, rax); + __ movzbl(rax, rax); + __ bind(done); +} + +void TemplateTable::float_cmp(bool is_float, int unordered_result) { + Label done; + if (is_float) { + // XXX get rid of pop here, use ... reg, mem32 + __ pop_f(xmm1); + __ ucomiss(xmm1, xmm0); + } else { + // XXX get rid of pop here, use ... reg, mem64 + __ pop_d(xmm1); + __ ucomisd(xmm1, xmm0); + } + if (unordered_result < 0) { + __ movl(rax, -1); + __ jccb(Assembler::parity, done); + __ jccb(Assembler::below, done); + __ setb(Assembler::notEqual, rdx); + __ movzbl(rax, rdx); + } else { + __ movl(rax, 1); + __ jccb(Assembler::parity, done); + __ jccb(Assembler::above, done); + __ movl(rax, 0); + __ jccb(Assembler::equal, done); + __ decrementl(rax); + } + __ bind(done); +} + +void TemplateTable::branch(bool is_jsr, bool is_wide) { + __ get_method(rcx); // rcx holds method + __ profile_taken_branch(rax, rbx); // rax holds updated MDP, rbx + // holds bumped taken count + + const ByteSize be_offset = methodOopDesc::backedge_counter_offset() + + InvocationCounter::counter_offset(); + const ByteSize inv_offset = methodOopDesc::invocation_counter_offset() + + InvocationCounter::counter_offset(); + const int method_offset = frame::interpreter_frame_method_offset * wordSize; + + // Load up edx with the branch displacement + __ movl(rdx, at_bcp(1)); + __ bswapl(rdx); + + if (!is_wide) { + __ sarl(rdx, 16); + } + __ movslq(rdx, rdx); + + // Handle all the JSR stuff here, then exit. + // It's much shorter and cleaner than intermingling with the non-JSR + // normal-branch stuff occuring below. + if (is_jsr) { + // Pre-load the next target bytecode into rbx + __ load_unsigned_byte(rbx, Address(r13, rdx, Address::times_1, 0)); + + // compute return address as bci in rax + __ leaq(rax, at_bcp((is_wide ? 5 : 3) - + in_bytes(constMethodOopDesc::codes_offset()))); + __ subq(rax, Address(rcx, methodOopDesc::const_offset())); + // Adjust the bcp in r13 by the displacement in rdx + __ addq(r13, rdx); + // jsr returns atos that is not an oop + __ push_i(rax); + __ dispatch_only(vtos); + return; + } + + // Normal (non-jsr) branch handling + + // Adjust the bcp in r13 by the displacement in rdx + __ addq(r13, rdx); + + assert(UseLoopCounter || !UseOnStackReplacement, + "on-stack-replacement requires loop counters"); + Label backedge_counter_overflow; + Label profile_method; + Label dispatch; + if (UseLoopCounter) { + // increment backedge counter for backward branches + // rax: MDO + // ebx: MDO bumped taken-count + // rcx: method + // rdx: target offset + // r13: target bcp + // r14: locals pointer + __ testl(rdx, rdx); // check if forward or backward branch + __ jcc(Assembler::positive, dispatch); // count only if backward branch + + // increment counter + __ movl(rax, Address(rcx, be_offset)); // load backedge counter + __ incrementl(rax, InvocationCounter::count_increment); // increment + // counter + __ movl(Address(rcx, be_offset), rax); // store counter + + __ movl(rax, Address(rcx, inv_offset)); // load invocation counter + __ andl(rax, InvocationCounter::count_mask_value); // and the status bits + __ addl(rax, Address(rcx, be_offset)); // add both counters + + if (ProfileInterpreter) { + // Test to see if we should create a method data oop + __ cmp32(rax, + ExternalAddress((address) &InvocationCounter::InterpreterProfileLimit)); + __ jcc(Assembler::less, dispatch); + + // if no method data exists, go to profile method + __ test_method_data_pointer(rax, profile_method); + + if (UseOnStackReplacement) { + // check for overflow against ebx which is the MDO taken count + __ cmp32(rbx, + ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit)); + __ jcc(Assembler::below, dispatch); + + // When ProfileInterpreter is on, the backedge_count comes + // from the methodDataOop, which value does not get reset on + // the call to frequency_counter_overflow(). To avoid + // excessive calls to the overflow routine while the method is + // being compiled, add a second test to make sure the overflow + // function is called only once every overflow_frequency. + const int overflow_frequency = 1024; + __ andl(rbx, overflow_frequency - 1); + __ jcc(Assembler::zero, backedge_counter_overflow); + + } + } else { + if (UseOnStackReplacement) { + // check for overflow against eax, which is the sum of the + // counters + __ cmp32(rax, + ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit)); + __ jcc(Assembler::aboveEqual, backedge_counter_overflow); + + } + } + __ bind(dispatch); + } + + // Pre-load the next target bytecode into rbx + __ load_unsigned_byte(rbx, Address(r13, 0)); + + // continue with the bytecode @ target + // eax: return bci for jsr's, unused otherwise + // ebx: target bytecode + // r13: target bcp + __ dispatch_only(vtos); + + if (UseLoopCounter) { + if (ProfileInterpreter) { + // Out-of-line code to allocate method data oop. + __ bind(profile_method); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::profile_method), r13); + __ load_unsigned_byte(rbx, Address(r13, 0)); // restore target bytecode + __ movq(rcx, Address(rbp, method_offset)); + __ movq(rcx, Address(rcx, + in_bytes(methodOopDesc::method_data_offset()))); + __ movq(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), + rcx); + __ test_method_data_pointer(rcx, dispatch); + // offset non-null mdp by MDO::data_offset() + IR::profile_method() + __ addq(rcx, in_bytes(methodDataOopDesc::data_offset())); + __ addq(rcx, rax); + __ movq(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), + rcx); + __ jmp(dispatch); + } + + if (UseOnStackReplacement) { + // invocation counter overflow + __ bind(backedge_counter_overflow); + __ negq(rdx); + __ addq(rdx, r13); // branch bcp + // IcoResult frequency_counter_overflow([JavaThread*], address branch_bcp) + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::frequency_counter_overflow), + rdx); + __ load_unsigned_byte(rbx, Address(r13, 0)); // restore target bytecode + + // rax: osr nmethod (osr ok) or NULL (osr not possible) + // ebx: target bytecode + // rdx: scratch + // r14: locals pointer + // r13: bcp + __ testq(rax, rax); // test result + __ jcc(Assembler::zero, dispatch); // no osr if null + // nmethod may have been invalidated (VM may block upon call_VM return) + __ movl(rcx, Address(rax, nmethod::entry_bci_offset())); + __ cmpl(rcx, InvalidOSREntryBci); + __ jcc(Assembler::equal, dispatch); + + // We have the address of an on stack replacement routine in eax + // We need to prepare to execute the OSR method. First we must + // migrate the locals and monitors off of the stack. + + __ movq(r13, rax); // save the nmethod + + call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin)); + + // eax is OSR buffer, move it to expected parameter location + __ movq(j_rarg0, rax); + + // We use j_rarg definitions here so that registers don't conflict as parameter + // registers change across platforms as we are in the midst of a calling + // sequence to the OSR nmethod and we don't want collision. These are NOT parameters. + + const Register retaddr = j_rarg2; + const Register sender_sp = j_rarg1; + + // pop the interpreter frame + __ movq(sender_sp, Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize)); // get sender sp + __ leave(); // remove frame anchor + __ popq(retaddr); // get return address + __ movq(rsp, sender_sp); // set sp to sender sp + // Ensure compiled code always sees stack at proper alignment + __ andq(rsp, -(StackAlignmentInBytes)); + + // unlike x86 we need no specialized return from compiled code + // to the interpreter or the call stub. + + // push the return address + __ pushq(retaddr); + + // and begin the OSR nmethod + __ jmp(Address(r13, nmethod::osr_entry_point_offset())); + } + } +} + + +void TemplateTable::if_0cmp(Condition cc) { + transition(itos, vtos); + // assume branch is more often taken than not (loops use backward branches) + Label not_taken; + __ testl(rax, rax); + __ jcc(j_not(cc), not_taken); + branch(false, false); + __ bind(not_taken); + __ profile_not_taken_branch(rax); +} + +void TemplateTable::if_icmp(Condition cc) { + transition(itos, vtos); + // assume branch is more often taken than not (loops use backward branches) + Label not_taken; + __ pop_i(rdx); + __ cmpl(rdx, rax); + __ jcc(j_not(cc), not_taken); + branch(false, false); + __ bind(not_taken); + __ profile_not_taken_branch(rax); +} + +void TemplateTable::if_nullcmp(Condition cc) { + transition(atos, vtos); + // assume branch is more often taken than not (loops use backward branches) + Label not_taken; + __ testq(rax, rax); + __ jcc(j_not(cc), not_taken); + branch(false, false); + __ bind(not_taken); + __ profile_not_taken_branch(rax); +} + +void TemplateTable::if_acmp(Condition cc) { + transition(atos, vtos); + // assume branch is more often taken than not (loops use backward branches) + Label not_taken; + __ pop_ptr(rdx); + __ cmpq(rdx, rax); + __ jcc(j_not(cc), not_taken); + branch(false, false); + __ bind(not_taken); + __ profile_not_taken_branch(rax); +} + +void TemplateTable::ret() { + transition(vtos, vtos); + locals_index(rbx); + __ movq(rbx, aaddress(rbx)); // get return bci, compute return bcp + __ profile_ret(rbx, rcx); + __ get_method(rax); + __ movq(r13, Address(rax, methodOopDesc::const_offset())); + __ leaq(r13, Address(r13, rbx, Address::times_1, + constMethodOopDesc::codes_offset())); + __ dispatch_next(vtos); +} + +void TemplateTable::wide_ret() { + transition(vtos, vtos); + locals_index_wide(rbx); + __ movq(rbx, aaddress(rbx)); // get return bci, compute return bcp + __ profile_ret(rbx, rcx); + __ get_method(rax); + __ movq(r13, Address(rax, methodOopDesc::const_offset())); + __ leaq(r13, Address(r13, rbx, Address::times_1, constMethodOopDesc::codes_offset())); + __ dispatch_next(vtos); +} + +void TemplateTable::tableswitch() { + Label default_case, continue_execution; + transition(itos, vtos); + // align r13 + __ leaq(rbx, at_bcp(BytesPerInt)); + __ andq(rbx, -BytesPerInt); + // load lo & hi + __ movl(rcx, Address(rbx, BytesPerInt)); + __ movl(rdx, Address(rbx, 2 * BytesPerInt)); + __ bswapl(rcx); + __ bswapl(rdx); + // check against lo & hi + __ cmpl(rax, rcx); + __ jcc(Assembler::less, default_case); + __ cmpl(rax, rdx); + __ jcc(Assembler::greater, default_case); + // lookup dispatch offset + __ subl(rax, rcx); + __ movl(rdx, Address(rbx, rax, Address::times_4, 3 * BytesPerInt)); + __ profile_switch_case(rax, rbx, rcx); + // continue execution + __ bind(continue_execution); + __ bswapl(rdx); + __ movslq(rdx, rdx); + __ load_unsigned_byte(rbx, Address(r13, rdx, Address::times_1)); + __ addq(r13, rdx); + __ dispatch_only(vtos); + // handle default + __ bind(default_case); + __ profile_switch_default(rax); + __ movl(rdx, Address(rbx, 0)); + __ jmp(continue_execution); +} + +void TemplateTable::lookupswitch() { + transition(itos, itos); + __ stop("lookupswitch bytecode should have been rewritten"); +} + +void TemplateTable::fast_linearswitch() { + transition(itos, vtos); + Label loop_entry, loop, found, continue_execution; + // bswap rax so we can avoid bswapping the table entries + __ bswapl(rax); + // align r13 + __ leaq(rbx, at_bcp(BytesPerInt)); // btw: should be able to get rid of + // this instruction (change offsets + // below) + __ andq(rbx, -BytesPerInt); + // set counter + __ movl(rcx, Address(rbx, BytesPerInt)); + __ bswapl(rcx); + __ jmpb(loop_entry); + // table search + __ bind(loop); + __ cmpl(rax, Address(rbx, rcx, Address::times_8, 2 * BytesPerInt)); + __ jcc(Assembler::equal, found); + __ bind(loop_entry); + __ decrementl(rcx); + __ jcc(Assembler::greaterEqual, loop); + // default case + __ profile_switch_default(rax); + __ movl(rdx, Address(rbx, 0)); + __ jmp(continue_execution); + // entry found -> get offset + __ bind(found); + __ movl(rdx, Address(rbx, rcx, Address::times_8, 3 * BytesPerInt)); + __ profile_switch_case(rcx, rax, rbx); + // continue execution + __ bind(continue_execution); + __ bswapl(rdx); + __ movslq(rdx, rdx); + __ load_unsigned_byte(rbx, Address(r13, rdx, Address::times_1)); + __ addq(r13, rdx); + __ dispatch_only(vtos); +} + +void TemplateTable::fast_binaryswitch() { + transition(itos, vtos); + // Implementation using the following core algorithm: + // + // int binary_search(int key, LookupswitchPair* array, int n) { + // // Binary search according to "Methodik des Programmierens" by + // // Edsger W. Dijkstra and W.H.J. Feijen, Addison Wesley Germany 1985. + // int i = 0; + // int j = n; + // while (i+1 < j) { + // // invariant P: 0 <= i < j <= n and (a[i] <= key < a[j] or Q) + // // with Q: for all i: 0 <= i < n: key < a[i] + // // where a stands for the array and assuming that the (inexisting) + // // element a[n] is infinitely big. + // int h = (i + j) >> 1; + // // i < h < j + // if (key < array[h].fast_match()) { + // j = h; + // } else { + // i = h; + // } + // } + // // R: a[i] <= key < a[i+1] or Q + // // (i.e., if key is within array, i is the correct index) + // return i; + // } + + // Register allocation + const Register key = rax; // already set (tosca) + const Register array = rbx; + const Register i = rcx; + const Register j = rdx; + const Register h = rdi; + const Register temp = rsi; + + // Find array start + __ leaq(array, at_bcp(3 * BytesPerInt)); // btw: should be able to + // get rid of this + // instruction (change + // offsets below) + __ andq(array, -BytesPerInt); + + // Initialize i & j + __ xorl(i, i); // i = 0; + __ movl(j, Address(array, -BytesPerInt)); // j = length(array); + + // Convert j into native byteordering + __ bswapl(j); + + // And start + Label entry; + __ jmp(entry); + + // binary search loop + { + Label loop; + __ bind(loop); + // int h = (i + j) >> 1; + __ leal(h, Address(i, j, Address::times_1)); // h = i + j; + __ sarl(h, 1); // h = (i + j) >> 1; + // if (key < array[h].fast_match()) { + // j = h; + // } else { + // i = h; + // } + // Convert array[h].match to native byte-ordering before compare + __ movl(temp, Address(array, h, Address::times_8)); + __ bswapl(temp); + __ cmpl(key, temp); + // j = h if (key < array[h].fast_match()) + __ cmovl(Assembler::less, j, h); + // i = h if (key >= array[h].fast_match()) + __ cmovl(Assembler::greaterEqual, i, h); + // while (i+1 < j) + __ bind(entry); + __ leal(h, Address(i, 1)); // i+1 + __ cmpl(h, j); // i+1 < j + __ jcc(Assembler::less, loop); + } + + // end of binary search, result index is i (must check again!) + Label default_case; + // Convert array[i].match to native byte-ordering before compare + __ movl(temp, Address(array, i, Address::times_8)); + __ bswapl(temp); + __ cmpl(key, temp); + __ jcc(Assembler::notEqual, default_case); + + // entry found -> j = offset + __ movl(j , Address(array, i, Address::times_8, BytesPerInt)); + __ profile_switch_case(i, key, array); + __ bswapl(j); + __ movslq(j, j); + __ load_unsigned_byte(rbx, Address(r13, j, Address::times_1)); + __ addq(r13, j); + __ dispatch_only(vtos); + + // default case -> j = default offset + __ bind(default_case); + __ profile_switch_default(i); + __ movl(j, Address(array, -2 * BytesPerInt)); + __ bswapl(j); + __ movslq(j, j); + __ load_unsigned_byte(rbx, Address(r13, j, Address::times_1)); + __ addq(r13, j); + __ dispatch_only(vtos); +} + + +void TemplateTable::_return(TosState state) { + transition(state, state); + assert(_desc->calls_vm(), + "inconsistent calls_vm information"); // call in remove_activation + + if (_desc->bytecode() == Bytecodes::_return_register_finalizer) { + assert(state == vtos, "only valid state"); + __ movq(c_rarg1, aaddress(0)); + __ movq(rdi, Address(c_rarg1, oopDesc::klass_offset_in_bytes())); + __ movl(rdi, Address(rdi, Klass::access_flags_offset_in_bytes() + sizeof(oopDesc))); + __ testl(rdi, JVM_ACC_HAS_FINALIZER); + Label skip_register_finalizer; + __ jcc(Assembler::zero, skip_register_finalizer); + + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::register_finalizer), c_rarg1); + + __ bind(skip_register_finalizer); + } + + __ remove_activation(state, r13); + __ jmp(r13); +} + +// ---------------------------------------------------------------------------- +// Volatile variables demand their effects be made known to all CPU's +// in order. Store buffers on most chips allow reads & writes to +// reorder; the JMM's ReadAfterWrite.java test fails in -Xint mode +// without some kind of memory barrier (i.e., it's not sufficient that +// the interpreter does not reorder volatile references, the hardware +// also must not reorder them). +// +// According to the new Java Memory Model (JMM): +// (1) All volatiles are serialized wrt to each other. ALSO reads & +// writes act as aquire & release, so: +// (2) A read cannot let unrelated NON-volatile memory refs that +// happen after the read float up to before the read. It's OK for +// non-volatile memory refs that happen before the volatile read to +// float down below it. +// (3) Similar a volatile write cannot let unrelated NON-volatile +// memory refs that happen BEFORE the write float down to after the +// write. It's OK for non-volatile memory refs that happen after the +// volatile write to float up before it. +// +// We only put in barriers around volatile refs (they are expensive), +// not _between_ memory refs (that would require us to track the +// flavor of the previous memory refs). Requirements (2) and (3) +// require some barriers before volatile stores and after volatile +// loads. These nearly cover requirement (1) but miss the +// volatile-store-volatile-load case. This final case is placed after +// volatile-stores although it could just as well go before +// volatile-loads. +void TemplateTable::volatile_barrier(Assembler::Membar_mask_bits + order_constraint) { + // Helper function to insert a is-volatile test and memory barrier + if (os::is_MP()) { // Not needed on single CPU + __ membar(order_constraint); + } +} + +void TemplateTable::resolve_cache_and_index(int byte_no, + Register Rcache, + Register index) { + assert(byte_no == 1 || byte_no == 2, "byte_no out of range"); + + const Register temp = rbx; + assert_different_registers(Rcache, index, temp); + + const int shift_count = (1 + byte_no) * BitsPerByte; + Label resolved; + __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ movl(temp, Address(Rcache, + index, Address::times_8, + constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::indices_offset())); + __ shrl(temp, shift_count); + // have we resolved this bytecode? + __ andl(temp, 0xFF); + __ cmpl(temp, (int) bytecode()); + __ jcc(Assembler::equal, resolved); + + // resolve first time through + address entry; + switch (bytecode()) { + case Bytecodes::_getstatic: + case Bytecodes::_putstatic: + case Bytecodes::_getfield: + case Bytecodes::_putfield: + entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_get_put); + break; + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokeinterface: + entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); + break; + default: + ShouldNotReachHere(); + break; + } + __ movl(temp, (int) bytecode()); + __ call_VM(noreg, entry, temp); + + // Update registers with resolved info + __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ bind(resolved); +} + +// The Rcache and index registers must be set before call +void TemplateTable::load_field_cp_cache_entry(Register obj, + Register cache, + Register index, + Register off, + Register flags, + bool is_static = false) { + assert_different_registers(cache, index, flags, off); + + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + // Field offset + __ movq(off, Address(cache, index, Address::times_8, + in_bytes(cp_base_offset + + ConstantPoolCacheEntry::f2_offset()))); + // Flags + __ movl(flags, Address(cache, index, Address::times_8, + in_bytes(cp_base_offset + + ConstantPoolCacheEntry::flags_offset()))); + + // klass overwrite register + if (is_static) { + __ movq(obj, Address(cache, index, Address::times_8, + in_bytes(cp_base_offset + + ConstantPoolCacheEntry::f1_offset()))); + } +} + +void TemplateTable::load_invoke_cp_cache_entry(int byte_no, + Register method, + Register itable_index, + Register flags, + bool is_invokevirtual, + bool is_invokevfinal /*unused*/) { + // setup registers + const Register cache = rcx; + const Register index = rdx; + assert_different_registers(method, flags); + assert_different_registers(method, cache, index); + assert_different_registers(itable_index, flags); + assert_different_registers(itable_index, cache, index); + // determine constant pool cache field offsets + const int method_offset = in_bytes( + constantPoolCacheOopDesc::base_offset() + + (is_invokevirtual + ? ConstantPoolCacheEntry::f2_offset() + : ConstantPoolCacheEntry::f1_offset())); + const int flags_offset = in_bytes(constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::flags_offset()); + // access constant pool cache fields + const int index_offset = in_bytes(constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::f2_offset()); + + resolve_cache_and_index(byte_no, cache, index); + + assert(wordSize == 8, "adjust code below"); + __ movq(method, Address(cache, index, Address::times_8, method_offset)); + if (itable_index != noreg) { + __ movq(itable_index, + Address(cache, index, Address::times_8, index_offset)); + } + __ movl(flags , Address(cache, index, Address::times_8, flags_offset)); +} + + +// The registers cache and index expected to be set before call. +// Correct values of the cache and index registers are preserved. +void TemplateTable::jvmti_post_field_access(Register cache, Register index, + bool is_static, bool has_tos) { + // do the JVMTI work here to avoid disturbing the register state below + // We use c_rarg registers here because we want to use the register used in + // the call to the VM + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the time to call into the VM. + Label L1; + assert_different_registers(cache, index, rax); + __ mov32(rax, ExternalAddress((address) JvmtiExport::get_field_access_count_addr())); + __ testl(rax, rax); + __ jcc(Assembler::zero, L1); + + __ get_cache_and_index_at_bcp(c_rarg2, c_rarg3, 1); + + // cache entry pointer + __ addq(c_rarg2, in_bytes(constantPoolCacheOopDesc::base_offset())); + __ shll(c_rarg3, LogBytesPerWord); + __ addq(c_rarg2, c_rarg3); + if (is_static) { + __ xorl(c_rarg1, c_rarg1); // NULL object reference + } else { + __ movq(c_rarg1, at_tos()); // get object pointer without popping it + __ verify_oop(c_rarg1); + } + // c_rarg1: object pointer or NULL + // c_rarg2: cache entry pointer + // c_rarg3: jvalue object on the stack + __ call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::post_field_access), + c_rarg1, c_rarg2, c_rarg3); + __ get_cache_and_index_at_bcp(cache, index, 1); + __ bind(L1); + } +} + +void TemplateTable::pop_and_check_object(Register r) { + __ pop_ptr(r); + __ null_check(r); // for field access must check obj. + __ verify_oop(r); +} + +void TemplateTable::getfield_or_static(int byte_no, bool is_static) { + transition(vtos, vtos); + + const Register cache = rcx; + const Register index = rdx; + const Register obj = c_rarg3; + const Register off = rbx; + const Register flags = rax; + const Register bc = c_rarg3; // uses same reg as obj, so don't mix them + + resolve_cache_and_index(byte_no, cache, index); + jvmti_post_field_access(cache, index, is_static, false); + load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); + + if (!is_static) { + // obj is on the stack + pop_and_check_object(obj); + } + + const Address field(obj, off, Address::times_1); + + Label Done, notByte, notInt, notShort, notChar, + notLong, notFloat, notObj, notDouble; + + __ shrl(flags, ConstantPoolCacheEntry::tosBits); + assert(btos == 0, "change code, btos != 0"); + + __ andl(flags, 0x0F); + __ jcc(Assembler::notZero, notByte); + // btos + __ load_signed_byte(rax, field); + __ push(btos); + // Rewrite bytecode to be faster + if (!is_static) { + patch_bytecode(Bytecodes::_fast_bgetfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notByte); + __ cmpl(flags, atos); + __ jcc(Assembler::notEqual, notObj); + // atos + __ movq(rax, field); + __ push(atos); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_agetfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notObj); + __ cmpl(flags, itos); + __ jcc(Assembler::notEqual, notInt); + // itos + __ movl(rax, field); + __ push(itos); + // Rewrite bytecode to be faster + if (!is_static) { + patch_bytecode(Bytecodes::_fast_igetfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notInt); + __ cmpl(flags, ctos); + __ jcc(Assembler::notEqual, notChar); + // ctos + __ load_unsigned_word(rax, field); + __ push(ctos); + // Rewrite bytecode to be faster + if (!is_static) { + patch_bytecode(Bytecodes::_fast_cgetfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notChar); + __ cmpl(flags, stos); + __ jcc(Assembler::notEqual, notShort); + // stos + __ load_signed_word(rax, field); + __ push(stos); + // Rewrite bytecode to be faster + if (!is_static) { + patch_bytecode(Bytecodes::_fast_sgetfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notShort); + __ cmpl(flags, ltos); + __ jcc(Assembler::notEqual, notLong); + // ltos + __ movq(rax, field); + __ push(ltos); + // Rewrite bytecode to be faster + if (!is_static) { + patch_bytecode(Bytecodes::_fast_lgetfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notLong); + __ cmpl(flags, ftos); + __ jcc(Assembler::notEqual, notFloat); + // ftos + __ movflt(xmm0, field); + __ push(ftos); + // Rewrite bytecode to be faster + if (!is_static) { + patch_bytecode(Bytecodes::_fast_fgetfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notFloat); +#ifdef ASSERT + __ cmpl(flags, dtos); + __ jcc(Assembler::notEqual, notDouble); +#endif + // dtos + __ movdbl(xmm0, field); + __ push(dtos); + // Rewrite bytecode to be faster + if (!is_static) { + patch_bytecode(Bytecodes::_fast_dgetfield, bc, rbx); + } +#ifdef ASSERT + __ jmp(Done); + + __ bind(notDouble); + __ stop("Bad state"); +#endif + + __ bind(Done); + // [jk] not needed currently + // volatile_barrier(Assembler::Membar_mask_bits(Assembler::LoadLoad | + // Assembler::LoadStore)); +} + + +void TemplateTable::getfield(int byte_no) { + getfield_or_static(byte_no, false); +} + +void TemplateTable::getstatic(int byte_no) { + getfield_or_static(byte_no, true); +} + +// The registers cache and index expected to be set before call. +// The function may destroy various registers, just not the cache and index registers. +void TemplateTable::jvmti_post_field_mod(Register cache, Register index, bool is_static) { + transition(vtos, vtos); + + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + if (JvmtiExport::can_post_field_modification()) { + // Check to see if a field modification watch has been set before + // we take the time to call into the VM. + Label L1; + assert_different_registers(cache, index, rax); + __ mov32(rax, ExternalAddress((address)JvmtiExport::get_field_modification_count_addr())); + __ testl(rax, rax); + __ jcc(Assembler::zero, L1); + + __ get_cache_and_index_at_bcp(c_rarg2, rscratch1, 1); + + if (is_static) { + // Life is simple. Null out the object pointer. + __ xorl(c_rarg1, c_rarg1); + } else { + // Life is harder. The stack holds the value on top, followed by + // the object. We don't know the size of the value, though; it + // could be one or two words depending on its type. As a result, + // we must find the type to determine where the object is. + __ movl(c_rarg3, Address(c_rarg2, rscratch1, + Address::times_8, + in_bytes(cp_base_offset + + ConstantPoolCacheEntry::flags_offset()))); + __ shrl(c_rarg3, ConstantPoolCacheEntry::tosBits); + // Make sure we don't need to mask rcx for tosBits after the + // above shift + ConstantPoolCacheEntry::verify_tosBits(); + __ movq(c_rarg1, at_tos_p1()); // initially assume a one word jvalue + __ cmpl(c_rarg3, ltos); + __ cmovq(Assembler::equal, + c_rarg1, at_tos_p2()); // ltos (two word jvalue) + __ cmpl(c_rarg3, dtos); + __ cmovq(Assembler::equal, + c_rarg1, at_tos_p2()); // dtos (two word jvalue) + } + // cache entry pointer + __ addq(c_rarg2, in_bytes(cp_base_offset)); + __ shll(rscratch1, LogBytesPerWord); + __ addq(c_rarg2, rscratch1); + // object (tos) + __ movq(c_rarg3, rsp); + // c_rarg1: object pointer set up above (NULL if static) + // c_rarg2: cache entry pointer + // c_rarg3: jvalue object on the stack + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::post_field_modification), + c_rarg1, c_rarg2, c_rarg3); + __ get_cache_and_index_at_bcp(cache, index, 1); + __ bind(L1); + } +} + +void TemplateTable::putfield_or_static(int byte_no, bool is_static) { + transition(vtos, vtos); + + const Register cache = rcx; + const Register index = rdx; + const Register obj = rcx; + const Register off = rbx; + const Register flags = rax; + const Register bc = c_rarg3; + + resolve_cache_and_index(byte_no, cache, index); + jvmti_post_field_mod(cache, index, is_static); + load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); + + // [jk] not needed currently + // volatile_barrier(Assembler::Membar_mask_bits(Assembler::LoadStore | + // Assembler::StoreStore)); + + Label notVolatile, Done; + __ movl(rdx, flags); + __ shrl(rdx, ConstantPoolCacheEntry::volatileField); + __ andl(rdx, 0x1); + + // field address + const Address field(obj, off, Address::times_1); + + Label notByte, notInt, notShort, notChar, + notLong, notFloat, notObj, notDouble; + + __ shrl(flags, ConstantPoolCacheEntry::tosBits); + + assert(btos == 0, "change code, btos != 0"); + __ andl(flags, 0x0f); + __ jcc(Assembler::notZero, notByte); + // btos + __ pop(btos); + if (!is_static) pop_and_check_object(obj); + __ movb(field, rax); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_bputfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notByte); + __ cmpl(flags, atos); + __ jcc(Assembler::notEqual, notObj); + // atos + __ pop(atos); + if (!is_static) pop_and_check_object(obj); + __ movq(field, rax); + __ store_check(obj, field); // Need to mark card + if (!is_static) { + patch_bytecode(Bytecodes::_fast_aputfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notObj); + __ cmpl(flags, itos); + __ jcc(Assembler::notEqual, notInt); + // itos + __ pop(itos); + if (!is_static) pop_and_check_object(obj); + __ movl(field, rax); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_iputfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notInt); + __ cmpl(flags, ctos); + __ jcc(Assembler::notEqual, notChar); + // ctos + __ pop(ctos); + if (!is_static) pop_and_check_object(obj); + __ movw(field, rax); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_cputfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notChar); + __ cmpl(flags, stos); + __ jcc(Assembler::notEqual, notShort); + // stos + __ pop(stos); + if (!is_static) pop_and_check_object(obj); + __ movw(field, rax); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_sputfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notShort); + __ cmpl(flags, ltos); + __ jcc(Assembler::notEqual, notLong); + // ltos + __ pop(ltos); + if (!is_static) pop_and_check_object(obj); + __ movq(field, rax); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_lputfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notLong); + __ cmpl(flags, ftos); + __ jcc(Assembler::notEqual, notFloat); + // ftos + __ pop(ftos); + if (!is_static) pop_and_check_object(obj); + __ movflt(field, xmm0); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_fputfield, bc, rbx); + } + __ jmp(Done); + + __ bind(notFloat); +#ifdef ASSERT + __ cmpl(flags, dtos); + __ jcc(Assembler::notEqual, notDouble); +#endif + // dtos + __ pop(dtos); + if (!is_static) pop_and_check_object(obj); + __ movdbl(field, xmm0); + if (!is_static) { + patch_bytecode(Bytecodes::_fast_dputfield, bc, rbx); + } + +#ifdef ASSERT + __ jmp(Done); + + __ bind(notDouble); + __ stop("Bad state"); +#endif + + __ bind(Done); + // Check for volatile store + __ testl(rdx, rdx); + __ jcc(Assembler::zero, notVolatile); + volatile_barrier(Assembler::Membar_mask_bits(Assembler::StoreLoad | + Assembler::StoreStore)); + + __ bind(notVolatile); +} + +void TemplateTable::putfield(int byte_no) { + putfield_or_static(byte_no, false); +} + +void TemplateTable::putstatic(int byte_no) { + putfield_or_static(byte_no, true); +} + +void TemplateTable::jvmti_post_fast_field_mod() { + if (JvmtiExport::can_post_field_modification()) { + // Check to see if a field modification watch has been set before + // we take the time to call into the VM. + Label L2; + __ mov32(c_rarg3, ExternalAddress((address)JvmtiExport::get_field_modification_count_addr())); + __ testl(c_rarg3, c_rarg3); + __ jcc(Assembler::zero, L2); + __ pop_ptr(rbx); // copy the object pointer from tos + __ verify_oop(rbx); + __ push_ptr(rbx); // put the object pointer back on tos + __ subq(rsp, sizeof(jvalue)); // add space for a jvalue object + __ movq(c_rarg3, rsp); + const Address field(c_rarg3, 0); + + switch (bytecode()) { // load values into the jvalue object + case Bytecodes::_fast_aputfield: // fall through + case Bytecodes::_fast_lputfield: __ movq(field, rax); break; + case Bytecodes::_fast_iputfield: __ movl(field, rax); break; + case Bytecodes::_fast_bputfield: __ movb(field, rax); break; + case Bytecodes::_fast_sputfield: // fall through + case Bytecodes::_fast_cputfield: __ movw(field, rax); break; + case Bytecodes::_fast_fputfield: __ movflt(field, xmm0); break; + case Bytecodes::_fast_dputfield: __ movdbl(field, xmm0); break; + default: + ShouldNotReachHere(); + } + + // Save rax because call_VM() will clobber it, then use it for + // JVMTI purposes + __ pushq(rax); + // access constant pool cache entry + __ get_cache_entry_pointer_at_bcp(c_rarg2, rax, 1); + __ verify_oop(rbx); + // rbx: object pointer copied above + // c_rarg2: cache entry pointer + // c_rarg3: jvalue object on the stack + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::post_field_modification), + rbx, c_rarg2, c_rarg3); + __ popq(rax); // restore lower value + __ addq(rsp, sizeof(jvalue)); // release jvalue object space + __ bind(L2); + } +} + +void TemplateTable::fast_storefield(TosState state) { + transition(state, vtos); + + ByteSize base = constantPoolCacheOopDesc::base_offset(); + + jvmti_post_fast_field_mod(); + + // access constant pool cache + __ get_cache_and_index_at_bcp(rcx, rbx, 1); + + // test for volatile with rdx + __ movl(rdx, Address(rcx, rbx, Address::times_8, + in_bytes(base + + ConstantPoolCacheEntry::flags_offset()))); + + // replace index with field offset from cache entry + __ movq(rbx, Address(rcx, rbx, Address::times_8, + in_bytes(base + ConstantPoolCacheEntry::f2_offset()))); + + // [jk] not needed currently + // volatile_barrier(Assembler::Membar_mask_bits(Assembler::LoadStore | + // Assembler::StoreStore)); + + Label notVolatile; + __ shrl(rdx, ConstantPoolCacheEntry::volatileField); + __ andl(rdx, 0x1); + + // Get object from stack + pop_and_check_object(rcx); + + // field address + const Address field(rcx, rbx, Address::times_1); + + // access field + switch (bytecode()) { + case Bytecodes::_fast_aputfield: + __ movq(field, rax); + __ store_check(rcx, field); + break; + case Bytecodes::_fast_lputfield: + __ movq(field, rax); + break; + case Bytecodes::_fast_iputfield: + __ movl(field, rax); + break; + case Bytecodes::_fast_bputfield: + __ movb(field, rax); + break; + case Bytecodes::_fast_sputfield: + // fall through + case Bytecodes::_fast_cputfield: + __ movw(field, rax); + break; + case Bytecodes::_fast_fputfield: + __ movflt(field, xmm0); + break; + case Bytecodes::_fast_dputfield: + __ movdbl(field, xmm0); + break; + default: + ShouldNotReachHere(); + } + + // Check for volatile store + __ testl(rdx, rdx); + __ jcc(Assembler::zero, notVolatile); + volatile_barrier(Assembler::Membar_mask_bits(Assembler::StoreLoad | + Assembler::StoreStore)); + __ bind(notVolatile); +} + + +void TemplateTable::fast_accessfield(TosState state) { + transition(atos, state); + + // Do the JVMTI work here to avoid disturbing the register state below + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the time to call into the VM. + Label L1; + __ mov32(rcx, ExternalAddress((address) JvmtiExport::get_field_access_count_addr())); + __ testl(rcx, rcx); + __ jcc(Assembler::zero, L1); + // access constant pool cache entry + __ get_cache_entry_pointer_at_bcp(c_rarg2, rcx, 1); + __ movq(r12, rax); // save object pointer before call_VM() clobbers it + __ verify_oop(rax); + __ movq(c_rarg1, rax); + // c_rarg1: object pointer copied above + // c_rarg2: cache entry pointer + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::post_field_access), + c_rarg1, c_rarg2); + __ movq(rax, r12); // restore object pointer + __ bind(L1); + } + + // access constant pool cache + __ get_cache_and_index_at_bcp(rcx, rbx, 1); + // replace index with field offset from cache entry + // [jk] not needed currently + // if (os::is_MP()) { + // __ movl(rdx, Address(rcx, rbx, Address::times_8, + // in_bytes(constantPoolCacheOopDesc::base_offset() + + // ConstantPoolCacheEntry::flags_offset()))); + // __ shrl(rdx, ConstantPoolCacheEntry::volatileField); + // __ andl(rdx, 0x1); + // } + __ movq(rbx, Address(rcx, rbx, Address::times_8, + in_bytes(constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::f2_offset()))); + + // rax: object + __ verify_oop(rax); + __ null_check(rax); + Address field(rax, rbx, Address::times_1); + + // access field + switch (bytecode()) { + case Bytecodes::_fast_agetfield: + __ movq(rax, field); + __ verify_oop(rax); + break; + case Bytecodes::_fast_lgetfield: + __ movq(rax, field); + break; + case Bytecodes::_fast_igetfield: + __ movl(rax, field); + break; + case Bytecodes::_fast_bgetfield: + __ movsbl(rax, field); + break; + case Bytecodes::_fast_sgetfield: + __ load_signed_word(rax, field); + break; + case Bytecodes::_fast_cgetfield: + __ load_unsigned_word(rax, field); + break; + case Bytecodes::_fast_fgetfield: + __ movflt(xmm0, field); + break; + case Bytecodes::_fast_dgetfield: + __ movdbl(xmm0, field); + break; + default: + ShouldNotReachHere(); + } + // [jk] not needed currently + // if (os::is_MP()) { + // Label notVolatile; + // __ testl(rdx, rdx); + // __ jcc(Assembler::zero, notVolatile); + // __ membar(Assembler::LoadLoad); + // __ bind(notVolatile); + //}; +} + +void TemplateTable::fast_xaccess(TosState state) { + transition(vtos, state); + + // get receiver + __ movq(rax, aaddress(0)); + debug_only(__ verify_local_tag(frame::TagReference, 0)); + // access constant pool cache + __ get_cache_and_index_at_bcp(rcx, rdx, 2); + __ movq(rbx, + Address(rcx, rdx, Address::times_8, + in_bytes(constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::f2_offset()))); + // make sure exception is reported in correct bcp range (getfield is + // next instruction) + __ incrementq(r13); + __ null_check(rax); + switch (state) { + case itos: + __ movl(rax, Address(rax, rbx, Address::times_1)); + break; + case atos: + __ movq(rax, Address(rax, rbx, Address::times_1)); + __ verify_oop(rax); + break; + case ftos: + __ movflt(xmm0, Address(rax, rbx, Address::times_1)); + break; + default: + ShouldNotReachHere(); + } + + // [jk] not needed currently + // if (os::is_MP()) { + // Label notVolatile; + // __ movl(rdx, Address(rcx, rdx, Address::times_8, + // in_bytes(constantPoolCacheOopDesc::base_offset() + + // ConstantPoolCacheEntry::flags_offset()))); + // __ shrl(rdx, ConstantPoolCacheEntry::volatileField); + // __ testl(rdx, 0x1); + // __ jcc(Assembler::zero, notVolatile); + // __ membar(Assembler::LoadLoad); + // __ bind(notVolatile); + // } + + __ decrementq(r13); +} + + + +//----------------------------------------------------------------------------- +// Calls + +void TemplateTable::count_calls(Register method, Register temp) { + // implemented elsewhere + ShouldNotReachHere(); +} + +void TemplateTable::prepare_invoke(Register method, + Register index, + int byte_no, + Bytecodes::Code code) { + // determine flags + const bool is_invokeinterface = code == Bytecodes::_invokeinterface; + const bool is_invokevirtual = code == Bytecodes::_invokevirtual; + const bool is_invokespecial = code == Bytecodes::_invokespecial; + const bool load_receiver = code != Bytecodes::_invokestatic; + const bool receiver_null_check = is_invokespecial; + const bool save_flags = is_invokeinterface || is_invokevirtual; + // setup registers & access constant pool cache + const Register recv = rcx; + const Register flags = rdx; + assert_different_registers(method, index, recv, flags); + + // save 'interpreter return address' + __ save_bcp(); + + load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual); + + // load receiver if needed (note: no return address pushed yet) + if (load_receiver) { + __ movl(recv, flags); + __ andl(recv, 0xFF); + if (TaggedStackInterpreter) __ shll(recv, 1); // index*2 + __ movq(recv, Address(rsp, recv, Address::times_8, -Interpreter::expr_offset_in_bytes(1))); + __ verify_oop(recv); + } + + // do null check if needed + if (receiver_null_check) { + __ null_check(recv); + } + + if (save_flags) { + __ movl(r13, flags); + } + + // compute return type + __ shrl(flags, ConstantPoolCacheEntry::tosBits); + // Make sure we don't need to mask flags for tosBits after the above shift + ConstantPoolCacheEntry::verify_tosBits(); + // load return address + { + ExternalAddress return_5((address)Interpreter::return_5_addrs_by_index_table()); + ExternalAddress return_3((address)Interpreter::return_3_addrs_by_index_table()); + __ lea(rscratch1, (is_invokeinterface ? return_5 : return_3)); + __ movq(flags, Address(rscratch1, flags, Address::times_8)); + } + + // push return address + __ pushq(flags); + + // Restore flag field from the constant pool cache, and restore esi + // for later null checks. r13 is the bytecode pointer + if (save_flags) { + __ movl(flags, r13); + __ restore_bcp(); + } +} + + +void TemplateTable::invokevirtual_helper(Register index, + Register recv, + Register flags) { + // Uses temporary registers rax, rdx assert_different_registers(index, recv, rax, rdx); + + // Test for an invoke of a final method + Label notFinal; + __ movl(rax, flags); + __ andl(rax, (1 << ConstantPoolCacheEntry::vfinalMethod)); + __ jcc(Assembler::zero, notFinal); + + const Register method = index; // method must be rbx + assert(method == rbx, + "methodOop must be rbx for interpreter calling convention"); + + // do the call - the index is actually the method to call + __ verify_oop(method); + + // It's final, need a null check here! + __ null_check(recv); + + // profile this call + __ profile_final_call(rax); + + __ jump_from_interpreted(method, rax); + + __ bind(notFinal); + + // get receiver klass + __ null_check(recv, oopDesc::klass_offset_in_bytes()); + __ movq(rax, Address(recv, oopDesc::klass_offset_in_bytes())); + + __ verify_oop(rax); + + // profile this call + __ profile_virtual_call(rax, r14, rdx); + + // get target methodOop & entry point + const int base = instanceKlass::vtable_start_offset() * wordSize; + assert(vtableEntry::size() * wordSize == 8, + "adjust the scaling in the code below"); + __ movq(method, Address(rax, index, + Address::times_8, + base + vtableEntry::method_offset_in_bytes())); + __ movq(rdx, Address(method, methodOopDesc::interpreter_entry_offset())); + __ jump_from_interpreted(method, rdx); +} + + +void TemplateTable::invokevirtual(int byte_no) { + transition(vtos, vtos); + prepare_invoke(rbx, noreg, byte_no, bytecode()); + + // rbx: index + // rcx: receiver + // rdx: flags + + invokevirtual_helper(rbx, rcx, rdx); +} + + +void TemplateTable::invokespecial(int byte_no) { + transition(vtos, vtos); + prepare_invoke(rbx, noreg, byte_no, bytecode()); + // do the call + __ verify_oop(rbx); + __ profile_call(rax); + __ jump_from_interpreted(rbx, rax); +} + + +void TemplateTable::invokestatic(int byte_no) { + transition(vtos, vtos); + prepare_invoke(rbx, noreg, byte_no, bytecode()); + // do the call + __ verify_oop(rbx); + __ profile_call(rax); + __ jump_from_interpreted(rbx, rax); +} + +void TemplateTable::fast_invokevfinal(int byte_no) { + transition(vtos, vtos); + __ stop("fast_invokevfinal not used on amd64"); +} + +void TemplateTable::invokeinterface(int byte_no) { + transition(vtos, vtos); + prepare_invoke(rax, rbx, byte_no, bytecode()); + + // rax: Interface + // rbx: index + // rcx: receiver + // rdx: flags + + // Special case of invokeinterface called for virtual method of + // java.lang.Object. See cpCacheOop.cpp for details. + // This code isn't produced by javac, but could be produced by + // another compliant java compiler. + Label notMethod; + __ movl(r14, rdx); + __ andl(r14, (1 << ConstantPoolCacheEntry::methodInterface)); + __ jcc(Assembler::zero, notMethod); + + invokevirtual_helper(rbx, rcx, rdx); + __ bind(notMethod); + + // Get receiver klass into rdx - also a null check + __ restore_locals(); // restore r14 + __ movq(rdx, Address(rcx, oopDesc::klass_offset_in_bytes())); + __ verify_oop(rdx); + + // profile this call + __ profile_virtual_call(rdx, r13, r14); + + __ movq(r14, rdx); // Save klassOop in r14 + + // Compute start of first itableOffsetEntry (which is at the end of + // the vtable) + const int base = instanceKlass::vtable_start_offset() * wordSize; + // Get length of vtable + assert(vtableEntry::size() * wordSize == 8, + "adjust the scaling in the code below"); + __ movl(r13, Address(rdx, + instanceKlass::vtable_length_offset() * wordSize)); + __ leaq(rdx, Address(rdx, r13, Address::times_8, base)); + + if (HeapWordsPerLong > 1) { + // Round up to align_object_offset boundary + __ round_to_q(rdx, BytesPerLong); + } + + Label entry, search, interface_ok; + + __ jmpb(entry); + __ bind(search); + __ addq(rdx, itableOffsetEntry::size() * wordSize); + + __ bind(entry); + + // Check that the entry is non-null. A null entry means that the + // receiver class doesn't implement the interface, and wasn't the + // same as the receiver class checked when the interface was + // resolved. + __ pushq(rdx); + __ movq(rdx, Address(rdx, itableOffsetEntry::interface_offset_in_bytes())); + __ testq(rdx, rdx); + __ jcc(Assembler::notZero, interface_ok); + // throw exception + __ popq(rdx); // pop saved register first. + __ popq(rbx); // pop return address (pushed by prepare_invoke) + __ restore_bcp(); // r13 must be correct for exception handler (was + // destroyed) + __ restore_locals(); // make sure locals pointer is correct as well + // (was destroyed) + __ call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_IncompatibleClassChangeError)); + // the call_VM checks for exception, so we should never return here. + __ should_not_reach_here(); + __ bind(interface_ok); + + __ popq(rdx); + + __ cmpq(rax, Address(rdx, itableOffsetEntry::interface_offset_in_bytes())); + __ jcc(Assembler::notEqual, search); + + __ movl(rdx, Address(rdx, itableOffsetEntry::offset_offset_in_bytes())); + + __ addq(rdx, r14); // Add offset to klassOop + assert(itableMethodEntry::size() * wordSize == 8, + "adjust the scaling in the code below"); + __ movq(rbx, Address(rdx, rbx, Address::times_8)); + // rbx: methodOop to call + // rcx: receiver + // Check for abstract method error + // Note: This should be done more efficiently via a + // throw_abstract_method_error interpreter entry point and a + // conditional jump to it in case of a null method. + { + Label L; + __ testq(rbx, rbx); + __ jcc(Assembler::notZero, L); + // throw exception + // note: must restore interpreter registers to canonical + // state for exception handling to work correctly! + __ popq(rbx); // pop return address (pushed by prepare_invoke) + __ restore_bcp(); // r13 must be correct for exception handler + // (was destroyed) + __ restore_locals(); // make sure locals pointer is correct as + // well (was destroyed) + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_AbstractMethodError)); + // the call_VM checks for exception, so we should never return here. + __ should_not_reach_here(); + __ bind(L); + } + + __ movq(rcx, Address(rbx, methodOopDesc::interpreter_entry_offset())); + + // do the call + // rcx: receiver + // rbx: methodOop + __ jump_from_interpreted(rbx, rdx); +} + +//----------------------------------------------------------------------------- +// Allocation + +void TemplateTable::_new() { + transition(vtos, atos); + __ get_unsigned_2_byte_index_at_bcp(rdx, 1); + Label slow_case; + Label done; + Label initialize_header; + Label initialize_object; // including clearing the fields + Label allocate_shared; + ExternalAddress top((address)Universe::heap()->top_addr()); + ExternalAddress end((address)Universe::heap()->end_addr()); + + __ get_cpool_and_tags(rsi, rax); + // get instanceKlass + __ movq(rsi, Address(rsi, rdx, + Address::times_8, sizeof(constantPoolOopDesc))); + + // make sure the class we're about to instantiate has been + // resolved. Note: slow_case does a pop of stack, which is why we + // loaded class/pushed above + const int tags_offset = typeArrayOopDesc::header_size(T_BYTE) * wordSize; + __ cmpb(Address(rax, rdx, Address::times_1, tags_offset), + JVM_CONSTANT_Class); + __ jcc(Assembler::notEqual, slow_case); + + // make sure klass is initialized & doesn't have finalizer + // make sure klass is fully initialized + __ cmpl(Address(rsi, + instanceKlass::init_state_offset_in_bytes() + + sizeof(oopDesc)), + instanceKlass::fully_initialized); + __ jcc(Assembler::notEqual, slow_case); + + // get instance_size in instanceKlass (scaled to a count of bytes) + __ movl(rdx, + Address(rsi, + Klass::layout_helper_offset_in_bytes() + sizeof(oopDesc))); + // test to see if it has a finalizer or is malformed in some way + __ testl(rdx, Klass::_lh_instance_slow_path_bit); + __ jcc(Assembler::notZero, slow_case); + + // Allocate the instance + // 1) Try to allocate in the TLAB + // 2) if fail and the object is large allocate in the shared Eden + // 3) if the above fails (or is not applicable), go to a slow case + // (creates a new TLAB, etc.) + + const bool allow_shared_alloc = + Universe::heap()->supports_inline_contig_alloc() && !CMSIncrementalMode; + + if (UseTLAB) { + __ movq(rax, Address(r15_thread, in_bytes(JavaThread::tlab_top_offset()))); + __ leaq(rbx, Address(rax, rdx, Address::times_1)); + __ cmpq(rbx, Address(r15_thread, in_bytes(JavaThread::tlab_end_offset()))); + __ jcc(Assembler::above, allow_shared_alloc ? allocate_shared : slow_case); + __ movq(Address(r15_thread, in_bytes(JavaThread::tlab_top_offset())), rbx); + if (ZeroTLAB) { + // the fields have been already cleared + __ jmp(initialize_header); + } else { + // initialize both the header and fields + __ jmp(initialize_object); + } + } + + // Allocation in the shared Eden, if allowed. + // + // rdx: instance size in bytes + if (allow_shared_alloc) { + __ bind(allocate_shared); + + const Register RtopAddr = rscratch1; + const Register RendAddr = rscratch2; + + __ lea(RtopAddr, top); + __ lea(RendAddr, end); + __ movq(rax, Address(RtopAddr, 0)); + + // For retries rax gets set by cmpxchgq + Label retry; + __ bind(retry); + __ leaq(rbx, Address(rax, rdx, Address::times_1)); + __ cmpq(rbx, Address(RendAddr, 0)); + __ jcc(Assembler::above, slow_case); + + // Compare rax with the top addr, and if still equal, store the new + // top addr in rbx at the address of the top addr pointer. Sets ZF if was + // equal, and clears it otherwise. Use lock prefix for atomicity on MPs. + // + // rax: object begin + // rbx: object end + // rdx: instance size in bytes + if (os::is_MP()) { + __ lock(); + } + __ cmpxchgq(rbx, Address(RtopAddr, 0)); + + // if someone beat us on the allocation, try again, otherwise continue + __ jcc(Assembler::notEqual, retry); + } + + if (UseTLAB || Universe::heap()->supports_inline_contig_alloc()) { + // The object is initialized before the header. If the object size is + // zero, go directly to the header initialization. + __ bind(initialize_object); + __ decrementl(rdx, sizeof(oopDesc)); + __ jcc(Assembler::zero, initialize_header); + + // Initialize object fields + __ xorl(rcx, rcx); // use zero reg to clear memory (shorter code) + __ shrl(rdx, LogBytesPerLong); // divide by oopSize to simplify the loop + { + Label loop; + __ bind(loop); + __ movq(Address(rax, rdx, Address::times_8, + sizeof(oopDesc) - oopSize), + rcx); + __ decrementl(rdx); + __ jcc(Assembler::notZero, loop); + } + + // initialize object header only. + __ bind(initialize_header); + if (UseBiasedLocking) { + __ movq(rscratch1, Address(rsi, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes())); + __ movq(Address(rax, oopDesc::mark_offset_in_bytes()), rscratch1); + } else { + __ movptr(Address(rax, oopDesc::mark_offset_in_bytes()), + (intptr_t) markOopDesc::prototype()); // header (address 0x1) + } + __ movq(Address(rax, oopDesc::klass_offset_in_bytes()), rsi); // klass + __ jmp(done); + } + + { + SkipIfEqual skip(_masm, &DTraceAllocProbes, false); + // Trigger dtrace event for fastpath + __ push(atos); // save the return value + __ call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), rax); + __ pop(atos); // restore the return value + } + + // slow case + __ bind(slow_case); + __ get_constant_pool(c_rarg1); + __ get_unsigned_2_byte_index_at_bcp(c_rarg2, 1); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), c_rarg1, c_rarg2); + __ verify_oop(rax); + + // continue + __ bind(done); +} + +void TemplateTable::newarray() { + transition(itos, atos); + __ load_unsigned_byte(c_rarg1, at_bcp(1)); + __ movl(c_rarg2, rax); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::newarray), + c_rarg1, c_rarg2); +} + +void TemplateTable::anewarray() { + transition(itos, atos); + __ get_unsigned_2_byte_index_at_bcp(c_rarg2, 1); + __ get_constant_pool(c_rarg1); + __ movl(c_rarg3, rax); + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::anewarray), + c_rarg1, c_rarg2, c_rarg3); +} + +void TemplateTable::arraylength() { + transition(atos, itos); + __ null_check(rax, arrayOopDesc::length_offset_in_bytes()); + __ movl(rax, Address(rax, arrayOopDesc::length_offset_in_bytes())); +} + +void TemplateTable::checkcast() { + transition(atos, atos); + Label done, is_null, ok_is_subtype, quicked, resolved; + __ testq(rax, rax); // object is in rax + __ jcc(Assembler::zero, is_null); + + // Get cpool & tags index + __ get_cpool_and_tags(rcx, rdx); // rcx=cpool, rdx=tags array + __ get_unsigned_2_byte_index_at_bcp(rbx, 1); // rbx=index + // See if bytecode has already been quicked + __ cmpb(Address(rdx, rbx, + Address::times_1, + typeArrayOopDesc::header_size(T_BYTE) * wordSize), + JVM_CONSTANT_Class); + __ jcc(Assembler::equal, quicked); + + __ movq(r12, rcx); // save rcx XXX + __ push(atos); // save receiver for result, and for GC + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc)); + __ pop_ptr(rdx); // restore receiver + __ movq(rcx, r12); // restore rcx XXX + __ jmpb(resolved); + + // Get superklass in rax and subklass in rbx + __ bind(quicked); + __ movq(rdx, rax); // Save object in rdx; rax needed for subtype check + __ movq(rax, Address(rcx, rbx, + Address::times_8, sizeof(constantPoolOopDesc))); + + __ bind(resolved); + __ movq(rbx, Address(rdx, oopDesc::klass_offset_in_bytes())); + + // Generate subtype check. Blows rcx, rdi. Object in rdx. + // Superklass in rax. Subklass in rbx. + __ gen_subtype_check(rbx, ok_is_subtype); + + // Come here on failure + __ push_ptr(rdx); + // object is at TOS + __ jump(ExternalAddress(Interpreter::_throw_ClassCastException_entry)); + + // Come here on success + __ bind(ok_is_subtype); + __ movq(rax, rdx); // Restore object in rdx + + // Collect counts on whether this check-cast sees NULLs a lot or not. + if (ProfileInterpreter) { + __ jmp(done); + __ bind(is_null); + __ profile_null_seen(rcx); + } else { + __ bind(is_null); // same as 'done' + } + __ bind(done); +} + +void TemplateTable::instanceof() { + transition(atos, itos); + Label done, is_null, ok_is_subtype, quicked, resolved; + __ testq(rax, rax); + __ jcc(Assembler::zero, is_null); + + // Get cpool & tags index + __ get_cpool_and_tags(rcx, rdx); // rcx=cpool, rdx=tags array + __ get_unsigned_2_byte_index_at_bcp(rbx, 1); // rbx=index + // See if bytecode has already been quicked + __ cmpb(Address(rdx, rbx, + Address::times_1, + typeArrayOopDesc::header_size(T_BYTE) * wordSize), + JVM_CONSTANT_Class); + __ jcc(Assembler::equal, quicked); + + __ movq(r12, rcx); // save rcx + __ push(atos); // save receiver for result, and for GC + call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc)); + __ pop_ptr(rdx); // restore receiver + __ movq(rdx, Address(rdx, oopDesc::klass_offset_in_bytes())); + __ movq(rcx, r12); // restore rcx + __ jmpb(resolved); + + // Get superklass in rax and subklass in rdx + __ bind(quicked); + __ movq(rdx, Address(rax, oopDesc::klass_offset_in_bytes())); + __ movq(rax, Address(rcx, rbx, + Address::times_8, sizeof(constantPoolOopDesc))); + + __ bind(resolved); + + // Generate subtype check. Blows rcx, rdi + // Superklass in rax. Subklass in rdx. + __ gen_subtype_check(rdx, ok_is_subtype); + + // Come here on failure + __ xorl(rax, rax); + __ jmpb(done); + // Come here on success + __ bind(ok_is_subtype); + __ movl(rax, 1); + + // Collect counts on whether this test sees NULLs a lot or not. + if (ProfileInterpreter) { + __ jmp(done); + __ bind(is_null); + __ profile_null_seen(rcx); + } else { + __ bind(is_null); // same as 'done' + } + __ bind(done); + // rax = 0: obj == NULL or obj is not an instanceof the specified klass + // rax = 1: obj != NULL and obj is an instanceof the specified klass +} + +//----------------------------------------------------------------------------- +// Breakpoints +void TemplateTable::_breakpoint() { + // Note: We get here even if we are single stepping.. + // jbug inists on setting breakpoints at every bytecode + // even if we are in single step mode. + + transition(vtos, vtos); + + // get the unpatched byte code + __ get_method(c_rarg1); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::get_original_bytecode_at), + c_rarg1, r13); + __ movq(rbx, rax); + + // post the breakpoint event + __ get_method(c_rarg1); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::_breakpoint), + c_rarg1, r13); + + // complete the execution of original bytecode + __ dispatch_only_normal(vtos); +} + +//----------------------------------------------------------------------------- +// Exceptions + +void TemplateTable::athrow() { + transition(atos, vtos); + __ null_check(rax); + __ jump(ExternalAddress(Interpreter::throw_exception_entry())); +} + +//----------------------------------------------------------------------------- +// Synchronization +// +// Note: monitorenter & exit are symmetric routines; which is reflected +// in the assembly code structure as well +// +// Stack layout: +// +// [expressions ] <--- rsp = expression stack top +// .. +// [expressions ] +// [monitor entry] <--- monitor block top = expression stack bot +// .. +// [monitor entry] +// [frame data ] <--- monitor block bot +// ... +// [saved rbp ] <--- rbp +void TemplateTable::monitorenter() { + transition(atos, vtos); + + // check for NULL object + __ null_check(rax); + + const Address monitor_block_top( + rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize); + const Address monitor_block_bot( + rbp, frame::interpreter_frame_initial_sp_offset * wordSize); + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + + Label allocated; + + // initialize entry pointer + __ xorl(c_rarg1, c_rarg1); // points to free slot or NULL + + // find a free slot in the monitor block (result in c_rarg1) + { + Label entry, loop, exit; + __ movq(c_rarg3, monitor_block_top); // points to current entry, + // starting with top-most entry + __ leaq(c_rarg2, monitor_block_bot); // points to word before bottom + // of monitor block + __ jmpb(entry); + + __ bind(loop); + // check if current entry is used + __ cmpq(Address(c_rarg3, BasicObjectLock::obj_offset_in_bytes()), (int) NULL); + // if not used then remember entry in c_rarg1 + __ cmovq(Assembler::equal, c_rarg1, c_rarg3); + // check if current entry is for same object + __ cmpq(rax, Address(c_rarg3, BasicObjectLock::obj_offset_in_bytes())); + // if same object then stop searching + __ jccb(Assembler::equal, exit); + // otherwise advance to next entry + __ addq(c_rarg3, entry_size); + __ bind(entry); + // check if bottom reached + __ cmpq(c_rarg3, c_rarg2); + // if not at bottom then check this entry + __ jcc(Assembler::notEqual, loop); + __ bind(exit); + } + + __ testq(c_rarg1, c_rarg1); // check if a slot has been found + __ jcc(Assembler::notZero, allocated); // if found, continue with that one + + // allocate one if there's no free slot + { + Label entry, loop; + // 1. compute new pointers // rsp: old expression stack top + __ movq(c_rarg1, monitor_block_bot); // c_rarg1: old expression stack bottom + __ subq(rsp, entry_size); // move expression stack top + __ subq(c_rarg1, entry_size); // move expression stack bottom + __ movq(c_rarg3, rsp); // set start value for copy loop + __ movq(monitor_block_bot, c_rarg1); // set new monitor block bottom + __ jmp(entry); + // 2. move expression stack contents + __ bind(loop); + __ movq(c_rarg2, Address(c_rarg3, entry_size)); // load expression stack + // word from old location + __ movq(Address(c_rarg3, 0), c_rarg2); // and store it at new location + __ addq(c_rarg3, wordSize); // advance to next word + __ bind(entry); + __ cmpq(c_rarg3, c_rarg1); // check if bottom reached + __ jcc(Assembler::notEqual, loop); // if not at bottom then + // copy next word + } + + // call run-time routine + // c_rarg1: points to monitor entry + __ bind(allocated); + + // Increment bcp to point to the next bytecode, so exception + // handling for async. exceptions work correctly. + // The object has already been poped from the stack, so the + // expression stack looks correct. + __ incrementq(r13); + + // store object + __ movq(Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()), rax); + __ lock_object(c_rarg1); + + // check to make sure this monitor doesn't cause stack overflow after locking + __ save_bcp(); // in case of exception + __ generate_stack_overflow_check(0); + + // The bcp has already been incremented. Just need to dispatch to + // next instruction. + __ dispatch_next(vtos); +} + + +void TemplateTable::monitorexit() { + transition(atos, vtos); + + // check for NULL object + __ null_check(rax); + + const Address monitor_block_top( + rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize); + const Address monitor_block_bot( + rbp, frame::interpreter_frame_initial_sp_offset * wordSize); + const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; + + Label found; + + // find matching slot + { + Label entry, loop; + __ movq(c_rarg1, monitor_block_top); // points to current entry, + // starting with top-most entry + __ leaq(c_rarg2, monitor_block_bot); // points to word before bottom + // of monitor block + __ jmpb(entry); + + __ bind(loop); + // check if current entry is for same object + __ cmpq(rax, Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes())); + // if same object then stop searching + __ jcc(Assembler::equal, found); + // otherwise advance to next entry + __ addq(c_rarg1, entry_size); + __ bind(entry); + // check if bottom reached + __ cmpq(c_rarg1, c_rarg2); + // if not at bottom then check this entry + __ jcc(Assembler::notEqual, loop); + } + + // error handling. Unlocking was not block-structured + __ call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_illegal_monitor_state_exception)); + __ should_not_reach_here(); + + // call run-time routine + // rsi: points to monitor entry + __ bind(found); + __ push_ptr(rax); // make sure object is on stack (contract with oopMaps) + __ unlock_object(c_rarg1); + __ pop_ptr(rax); // discard object +} + + +// Wide instructions +void TemplateTable::wide() { + transition(vtos, vtos); + __ load_unsigned_byte(rbx, at_bcp(1)); + __ lea(rscratch1, ExternalAddress((address)Interpreter::_wentry_point)); + __ jmp(Address(rscratch1, rbx, Address::times_8)); + // Note: the r13 increment step is part of the individual wide + // bytecode implementations +} + + +// Multi arrays +void TemplateTable::multianewarray() { + transition(vtos, atos); + __ load_unsigned_byte(rax, at_bcp(3)); // get number of dimensions + // last dim is on top of stack; we want address of first one: + // first_addr = last_addr + (ndims - 1) * wordSize + if (TaggedStackInterpreter) __ shll(rax, 1); // index*2 + __ leaq(c_rarg1, Address(rsp, rax, Address::times_8, -wordSize)); + call_VM(rax, + CAST_FROM_FN_PTR(address, InterpreterRuntime::multianewarray), + c_rarg1); + __ load_unsigned_byte(rbx, at_bcp(3)); + if (TaggedStackInterpreter) __ shll(rbx, 1); // index*2 + __ leaq(rsp, Address(rsp, rbx, Address::times_8)); +} diff --git a/hotspot/src/cpu/x86/vm/templateTable_x86_64.hpp b/hotspot/src/cpu/x86/vm/templateTable_x86_64.hpp new file mode 100644 index 00000000000..ec531a7bc1a --- /dev/null +++ b/hotspot/src/cpu/x86/vm/templateTable_x86_64.hpp @@ -0,0 +1,33 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + static void prepare_invoke(Register method, Register index, int byte_no, + Bytecodes::Code code); + static void invokevirtual_helper(Register index, Register recv, + Register flags); + static void volatile_barrier(Assembler::Membar_mask_bits order_constraint); + + // Helpers + static void index_check(Register array, Register index); + static void index_check_without_pop(Register array, Register index); diff --git a/hotspot/src/cpu/x86/vm/vmStructs_x86.hpp b/hotspot/src/cpu/x86/vm/vmStructs_x86.hpp new file mode 100644 index 00000000000..8134d0fa26e --- /dev/null +++ b/hotspot/src/cpu/x86/vm/vmStructs_x86.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// These are the CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ + \ + /******************************/ \ + /* JavaCallWrapper */ \ + /******************************/ \ + /******************************/ \ + /* JavaFrameAnchor */ \ + /******************************/ \ + volatile_nonstatic_field(JavaFrameAnchor, _last_Java_fp, intptr_t*) \ + \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_STRUCTS_OS_CPU macro (and must */ + /* be present there) */ + + +#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_TYPES_OS_CPU macro (and must */ + /* be present there) */ + + +#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_INT_CONSTANTS_OS_CPU macro (and must */ + /* be present there) */ + +#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_LONG_CONSTANTS_OS_CPU macro (and must */ + /* be present there) */ diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86_32.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86_32.cpp new file mode 100644 index 00000000000..8e8236098da --- /dev/null +++ b/hotspot/src/cpu/x86/vm/vm_version_x86_32.cpp @@ -0,0 +1,443 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vm_version_x86_32.cpp.incl" + + +int VM_Version::_cpu; +int VM_Version::_model; +int VM_Version::_stepping; +int VM_Version::_cpuFeatures; +const char* VM_Version::_features_str = ""; +VM_Version::CpuidInfo VM_Version::_cpuid_info = { 0, }; + +static BufferBlob* stub_blob; +static const int stub_size = 300; + +extern "C" { + typedef void (*getPsrInfo_stub_t)(void*); +} +static getPsrInfo_stub_t getPsrInfo_stub = NULL; + + +class VM_Version_StubGenerator: public StubCodeGenerator { + public: + + VM_Version_StubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} + + address generate_getPsrInfo() { + // Flags to test CPU type. + const uint32_t EFL_AC = 0x40000; + const uint32_t EFL_ID = 0x200000; + // Values for when we don't have a CPUID instruction. + const int CPU_FAMILY_SHIFT = 8; + const uint32_t CPU_FAMILY_386 = (3 << CPU_FAMILY_SHIFT); + const uint32_t CPU_FAMILY_486 = (4 << CPU_FAMILY_SHIFT); + + Label detect_486, cpu486, detect_586, std_cpuid1; + Label ext_cpuid1, ext_cpuid5, done; + + StubCodeMark mark(this, "VM_Version", "getPsrInfo_stub"); +# define __ _masm-> + + address start = __ pc(); + + // + // void getPsrInfo(VM_Version::CpuidInfo* cpuid_info); + // + __ pushl(rbp); + __ movl(rbp, Address(rsp, 8)); // cpuid_info address + __ pushl(rbx); + __ pushl(rsi); + __ pushfd(); // preserve rbx, and flags + __ popl(rax); + __ pushl(rax); + __ movl(rcx, rax); + // + // if we are unable to change the AC flag, we have a 386 + // + __ xorl(rax, EFL_AC); + __ pushl(rax); + __ popfd(); + __ pushfd(); + __ popl(rax); + __ cmpl(rax, rcx); + __ jccb(Assembler::notEqual, detect_486); + + __ movl(rax, CPU_FAMILY_386); + __ movl(Address(rbp, in_bytes(VM_Version::std_cpuid1_offset())), rax); + __ jmp(done); + + // + // If we are unable to change the ID flag, we have a 486 which does + // not support the "cpuid" instruction. + // + __ bind(detect_486); + __ movl(rax, rcx); + __ xorl(rax, EFL_ID); + __ pushl(rax); + __ popfd(); + __ pushfd(); + __ popl(rax); + __ cmpl(rcx, rax); + __ jccb(Assembler::notEqual, detect_586); + + __ bind(cpu486); + __ movl(rax, CPU_FAMILY_486); + __ movl(Address(rbp, in_bytes(VM_Version::std_cpuid1_offset())), rax); + __ jmp(done); + + // + // at this point, we have a chip which supports the "cpuid" instruction + // + __ bind(detect_586); + __ xorl(rax, rax); + __ cpuid(); + __ orl(rax, rax); + __ jcc(Assembler::equal, cpu486); // if cpuid doesn't support an input + // value of at least 1, we give up and + // assume a 486 + __ leal(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid0_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + __ cmpl(rax, 3); // Is cpuid(0x4) supported? + __ jccb(Assembler::belowEqual, std_cpuid1); + + // + // cpuid(0x4) Deterministic cache params + // + __ movl(rax, 4); // and rcx already set to 0x0 + __ xorl(rcx, rcx); + __ cpuid(); + __ pushl(rax); + __ andl(rax, 0x1f); // Determine if valid cache parameters used + __ orl(rax, rax); // rax,[4:0] == 0 indicates invalid cache + __ popl(rax); + __ jccb(Assembler::equal, std_cpuid1); + + __ leal(rsi, Address(rbp, in_bytes(VM_Version::dcp_cpuid4_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + // + // Standard cpuid(0x1) + // + __ bind(std_cpuid1); + __ movl(rax, 1); + __ cpuid(); + __ leal(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid1_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + __ movl(rax, 0x80000000); + __ cpuid(); + __ cmpl(rax, 0x80000000); // Is cpuid(0x80000001) supported? + __ jcc(Assembler::belowEqual, done); + __ cmpl(rax, 0x80000004); // Is cpuid(0x80000005) supported? + __ jccb(Assembler::belowEqual, ext_cpuid1); + __ cmpl(rax, 0x80000007); // Is cpuid(0x80000008) supported? + __ jccb(Assembler::belowEqual, ext_cpuid5); + // + // Extended cpuid(0x80000008) + // + __ movl(rax, 0x80000008); + __ cpuid(); + __ leal(rsi, Address(rbp, in_bytes(VM_Version::ext_cpuid8_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + // + // Extended cpuid(0x80000005) + // + __ bind(ext_cpuid5); + __ movl(rax, 0x80000005); + __ cpuid(); + __ leal(rsi, Address(rbp, in_bytes(VM_Version::ext_cpuid5_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + // + // Extended cpuid(0x80000001) + // + __ bind(ext_cpuid1); + __ movl(rax, 0x80000001); + __ cpuid(); + __ leal(rsi, Address(rbp, in_bytes(VM_Version::ext_cpuid1_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + // + // return + // + __ bind(done); + __ popfd(); + __ popl(rsi); + __ popl(rbx); + __ popl(rbp); + __ ret(0); + +# undef __ + + return start; + }; +}; + + +void VM_Version::get_processor_features() { + + _cpu = 4; // 486 by default + _model = 0; + _stepping = 0; + _cpuFeatures = 0; + _logical_processors_per_package = 1; + if (!Use486InstrsOnly) { + // Get raw processor info + getPsrInfo_stub(&_cpuid_info); + assert_is_initialized(); + _cpu = extended_cpu_family(); + _model = extended_cpu_model(); + _stepping = cpu_stepping(); + if (cpu_family() > 4) { // it supports CPUID + _cpuFeatures = feature_flags(); + // Logical processors are only available on P4s and above, + // and only if hyperthreading is available. + _logical_processors_per_package = logical_processor_count(); + } + } + _supports_cx8 = supports_cmpxchg8(); + // if the OS doesn't support SSE, we can't use this feature even if the HW does + if( !os::supports_sse()) + _cpuFeatures &= ~(CPU_SSE|CPU_SSE2|CPU_SSE3|CPU_SSSE3|CPU_SSE4|CPU_SSE4A); + if (UseSSE < 4) + _cpuFeatures &= ~CPU_SSE4; + if (UseSSE < 3) { + _cpuFeatures &= ~CPU_SSE3; + _cpuFeatures &= ~CPU_SSSE3; + _cpuFeatures &= ~CPU_SSE4A; + } + if (UseSSE < 2) + _cpuFeatures &= ~CPU_SSE2; + if (UseSSE < 1) + _cpuFeatures &= ~CPU_SSE; + + if (logical_processors_per_package() == 1) { + // HT processor could be installed on a system which doesn't support HT. + _cpuFeatures &= ~CPU_HT; + } + + char buf[256]; + jio_snprintf(buf, sizeof(buf), "(%u cores per cpu, %u threads per core) family %d model %d stepping %d%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + cores_per_cpu(), threads_per_core(), + cpu_family(), _model, _stepping, + (supports_cmov() ? ", cmov" : ""), + (supports_cmpxchg8() ? ", cx8" : ""), + (supports_fxsr() ? ", fxsr" : ""), + (supports_mmx() ? ", mmx" : ""), + (supports_sse() ? ", sse" : ""), + (supports_sse2() ? ", sse2" : ""), + (supports_sse3() ? ", sse3" : ""), + (supports_ssse3()? ", ssse3": ""), + (supports_sse4() ? ", sse4" : ""), + (supports_mmx_ext() ? ", mmxext" : ""), + (supports_3dnow() ? ", 3dnow" : ""), + (supports_3dnow2() ? ", 3dnowext" : ""), + (supports_sse4a() ? ", sse4a": ""), + (supports_ht() ? ", ht": "")); + _features_str = strdup(buf); + + // UseSSE is set to the smaller of what hardware supports and what + // the command line requires. I.e., you cannot set UseSSE to 2 on + // older Pentiums which do not support it. + if( UseSSE > 4 ) UseSSE=4; + if( UseSSE < 0 ) UseSSE=0; + if( !supports_sse4() ) // Drop to 3 if no SSE4 support + UseSSE = MIN2((intx)3,UseSSE); + if( !supports_sse3() ) // Drop to 2 if no SSE3 support + UseSSE = MIN2((intx)2,UseSSE); + if( !supports_sse2() ) // Drop to 1 if no SSE2 support + UseSSE = MIN2((intx)1,UseSSE); + if( !supports_sse () ) // Drop to 0 if no SSE support + UseSSE = 0; + + // On new cpus instructions which update whole XMM register should be used + // to prevent partial register stall due to dependencies on high half. + // + // UseXmmLoadAndClearUpper == true --> movsd(xmm, mem) + // UseXmmLoadAndClearUpper == false --> movlpd(xmm, mem) + // UseXmmRegToRegMoveAll == true --> movaps(xmm, xmm), movapd(xmm, xmm). + // UseXmmRegToRegMoveAll == false --> movss(xmm, xmm), movsd(xmm, xmm). + + if( is_amd() ) { // AMD cpus specific settings + if( supports_sse2() && FLAG_IS_DEFAULT(UseAddressNop) ) { + // Use it on new AMD cpus starting from Opteron. + UseAddressNop = true; + } + if( FLAG_IS_DEFAULT(UseXmmLoadAndClearUpper) ) { + if( supports_sse4a() ) { + UseXmmLoadAndClearUpper = true; // use movsd only on '10h' Opteron + } else { + UseXmmLoadAndClearUpper = false; + } + } + if( FLAG_IS_DEFAULT(UseXmmRegToRegMoveAll) ) { + if( supports_sse4a() ) { + UseXmmRegToRegMoveAll = true; // use movaps, movapd only on '10h' + } else { + UseXmmRegToRegMoveAll = false; + } + } + } + + if( is_intel() ) { // Intel cpus specific settings + if( FLAG_IS_DEFAULT(UseStoreImmI16) ) { + UseStoreImmI16 = false; // don't use it on Intel cpus + } + if( cpu_family() == 6 || cpu_family() == 15 ) { + if( FLAG_IS_DEFAULT(UseAddressNop) ) { + // Use it on all Intel cpus starting from PentiumPro + UseAddressNop = true; + } + } + if( FLAG_IS_DEFAULT(UseXmmLoadAndClearUpper) ) { + UseXmmLoadAndClearUpper = true; // use movsd on all Intel cpus + } + if( FLAG_IS_DEFAULT(UseXmmRegToRegMoveAll) ) { + if( supports_sse3() ) { + UseXmmRegToRegMoveAll = true; // use movaps, movapd on new Intel cpus + } else { + UseXmmRegToRegMoveAll = false; + } + } + if( cpu_family() == 6 && supports_sse3() ) { // New Intel cpus +#ifdef COMPILER2 + if( FLAG_IS_DEFAULT(MaxLoopPad) ) { + // For new Intel cpus do the next optimization: + // don't align the beginning of a loop if there are enough instructions + // left (NumberOfLoopInstrToAlign defined in c2_globals.hpp) + // in current fetch line (OptoLoopAlignment) or the padding + // is big (> MaxLoopPad). + // Set MaxLoopPad to 11 for new Intel cpus to reduce number of + // generated NOP instructions. 11 is the largest size of one + // address NOP instruction '0F 1F' (see Assembler::nop(i)). + MaxLoopPad = 11; + } +#endif // COMPILER2 + } + } + + assert(0 <= ReadPrefetchInstr && ReadPrefetchInstr <= 3, "invalid value"); + assert(0 <= AllocatePrefetchInstr && AllocatePrefetchInstr <= 3, "invalid value"); + + // set valid Prefetch instruction + if( ReadPrefetchInstr < 0 ) ReadPrefetchInstr = 0; + if( ReadPrefetchInstr > 3 ) ReadPrefetchInstr = 3; + if( ReadPrefetchInstr == 3 && !supports_3dnow() ) ReadPrefetchInstr = 0; + if( !supports_sse() && supports_3dnow() ) ReadPrefetchInstr = 3; + + if( AllocatePrefetchInstr < 0 ) AllocatePrefetchInstr = 0; + if( AllocatePrefetchInstr > 3 ) AllocatePrefetchInstr = 3; + if( AllocatePrefetchInstr == 3 && !supports_3dnow() ) AllocatePrefetchInstr=0; + if( !supports_sse() && supports_3dnow() ) AllocatePrefetchInstr = 3; + + // Allocation prefetch settings + intx cache_line_size = L1_data_cache_line_size(); + if( cache_line_size > AllocatePrefetchStepSize ) + AllocatePrefetchStepSize = cache_line_size; + if( FLAG_IS_DEFAULT(AllocatePrefetchLines) ) + AllocatePrefetchLines = 3; // Optimistic value + assert(AllocatePrefetchLines > 0, "invalid value"); + if( AllocatePrefetchLines < 1 ) // set valid value in product VM + AllocatePrefetchLines = 1; // Conservative value + + AllocatePrefetchDistance = allocate_prefetch_distance(); + AllocatePrefetchStyle = allocate_prefetch_style(); + + if( AllocatePrefetchStyle == 2 && is_intel() && + cpu_family() == 6 && supports_sse3() ) { // watermark prefetching on Core + AllocatePrefetchDistance = 320; + } + assert(AllocatePrefetchDistance % AllocatePrefetchStepSize == 0, "invalid value"); + +#ifndef PRODUCT + if (PrintMiscellaneous && Verbose) { + tty->print_cr("Logical CPUs per package: %u", + logical_processors_per_package()); + tty->print_cr("UseSSE=%d",UseSSE); + tty->print("Allocation: "); + if (AllocatePrefetchStyle <= 0 || UseSSE == 0 && !supports_3dnow()) { + tty->print_cr("no prefetching"); + } else { + if (UseSSE == 0 && supports_3dnow()) { + tty->print("PREFETCHW"); + } else if (UseSSE >= 1) { + if (AllocatePrefetchInstr == 0) { + tty->print("PREFETCHNTA"); + } else if (AllocatePrefetchInstr == 1) { + tty->print("PREFETCHT0"); + } else if (AllocatePrefetchInstr == 2) { + tty->print("PREFETCHT2"); + } else if (AllocatePrefetchInstr == 3) { + tty->print("PREFETCHW"); + } + } + if (AllocatePrefetchLines > 1) { + tty->print_cr(" %d, %d lines with step %d bytes", AllocatePrefetchDistance, AllocatePrefetchLines, AllocatePrefetchStepSize); + } else { + tty->print_cr(" %d, one line", AllocatePrefetchDistance); + } + } + } +#endif // !PRODUCT +} + +void VM_Version::initialize() { + ResourceMark rm; + // Making this stub must be FIRST use of assembler + + stub_blob = BufferBlob::create("getPsrInfo_stub", stub_size); + if (stub_blob == NULL) { + vm_exit_during_initialization("Unable to allocate getPsrInfo_stub"); + } + CodeBuffer c(stub_blob->instructions_begin(), + stub_blob->instructions_size()); + VM_Version_StubGenerator g(&c); + getPsrInfo_stub = CAST_TO_FN_PTR(getPsrInfo_stub_t, + g.generate_getPsrInfo()); + + get_processor_features(); +} diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86_32.hpp b/hotspot/src/cpu/x86/vm/vm_version_x86_32.hpp new file mode 100644 index 00000000000..361bfb2eb11 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/vm_version_x86_32.hpp @@ -0,0 +1,441 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class VM_Version: public Abstract_VM_Version { +public: + // cpuid result register layouts. These are all unions of a uint32_t + // (in case anyone wants access to the register as a whole) and a bitfield. + + union StdCpuid1Eax { + uint32_t value; + struct { + uint32_t stepping : 4, + model : 4, + family : 4, + proc_type : 2, + : 2, + ext_model : 4, + ext_family : 8, + : 4; + } bits; + }; + + union StdCpuid1Ebx { // example, unused + uint32_t value; + struct { + uint32_t brand_id : 8, + clflush_size : 8, + threads_per_cpu : 8, + apic_id : 8; + } bits; + }; + + union StdCpuid1Ecx { + uint32_t value; + struct { + uint32_t sse3 : 1, + : 2, + monitor : 1, + : 1, + vmx : 1, + : 1, + est : 1, + : 1, + ssse3 : 1, + cid : 1, + : 2, + cmpxchg16: 1, + : 4, + dca : 1, + : 4, + popcnt : 1, + : 8; + } bits; + }; + + union StdCpuid1Edx { + uint32_t value; + struct { + uint32_t : 4, + tsc : 1, + : 3, + cmpxchg8 : 1, + : 6, + cmov : 1, + : 7, + mmx : 1, + fxsr : 1, + sse : 1, + sse2 : 1, + : 1, + ht : 1, + : 3; + } bits; + }; + + union DcpCpuid4Eax { + uint32_t value; + struct { + uint32_t cache_type : 5, + : 21, + cores_per_cpu : 6; + } bits; + }; + + union DcpCpuid4Ebx { + uint32_t value; + struct { + uint32_t L1_line_size : 12, + partitions : 10, + associativity : 10; + } bits; + }; + + union ExtCpuid1Ecx { + uint32_t value; + struct { + uint32_t LahfSahf : 1, + CmpLegacy : 1, + : 4, + abm : 1, + sse4a : 1, + misalignsse : 1, + prefetchw : 1, + : 22; + } bits; + }; + + union ExtCpuid1Edx { + uint32_t value; + struct { + uint32_t : 22, + mmx_amd : 1, + mmx : 1, + fxsr : 1, + : 4, + long_mode : 1, + tdnow2 : 1, + tdnow : 1; + } bits; + }; + + union ExtCpuid5Ex { + uint32_t value; + struct { + uint32_t L1_line_size : 8, + L1_tag_lines : 8, + L1_assoc : 8, + L1_size : 8; + } bits; + }; + + union ExtCpuid8Ecx { + uint32_t value; + struct { + uint32_t cores_per_cpu : 8, + : 24; + } bits; + }; + +protected: + static int _cpu; + static int _model; + static int _stepping; + static int _cpuFeatures; // features returned by the "cpuid" instruction + // 0 if this instruction is not available + static const char* _features_str; + + enum { + CPU_CX8 = (1 << 0), // next bits are from cpuid 1 (EDX) + CPU_CMOV = (1 << 1), + CPU_FXSR = (1 << 2), + CPU_HT = (1 << 3), + CPU_MMX = (1 << 4), + CPU_3DNOW= (1 << 5), // 3DNow comes from cpuid 0x80000001 (EDX) + CPU_SSE = (1 << 6), + CPU_SSE2 = (1 << 7), + CPU_SSE3 = (1 << 8), // sse3 comes from cpuid 1 (ECX) + CPU_SSSE3= (1 << 9), + CPU_SSE4 = (1 <<10), + CPU_SSE4A= (1 <<11) + } cpuFeatureFlags; + + // cpuid information block. All info derived from executing cpuid with + // various function numbers is stored here. Intel and AMD info is + // merged in this block: accessor methods disentangle it. + // + // The info block is laid out in subblocks of 4 dwords corresponding to + // rax, rbx, rcx and rdx, whether or not they contain anything useful. + struct CpuidInfo { + // cpuid function 0 + uint32_t std_max_function; + uint32_t std_vendor_name_0; + uint32_t std_vendor_name_1; + uint32_t std_vendor_name_2; + + // cpuid function 1 + StdCpuid1Eax std_cpuid1_rax; + StdCpuid1Ebx std_cpuid1_rbx; + StdCpuid1Ecx std_cpuid1_rcx; + StdCpuid1Edx std_cpuid1_rdx; + + // cpuid function 4 (deterministic cache parameters) + DcpCpuid4Eax dcp_cpuid4_rax; + DcpCpuid4Ebx dcp_cpuid4_rbx; + uint32_t dcp_cpuid4_rcx; // unused currently + uint32_t dcp_cpuid4_rdx; // unused currently + + // cpuid function 0x80000000 // example, unused + uint32_t ext_max_function; + uint32_t ext_vendor_name_0; + uint32_t ext_vendor_name_1; + uint32_t ext_vendor_name_2; + + // cpuid function 0x80000001 + uint32_t ext_cpuid1_rax; // reserved + uint32_t ext_cpuid1_rbx; // reserved + ExtCpuid1Ecx ext_cpuid1_rcx; + ExtCpuid1Edx ext_cpuid1_rdx; + + // cpuid functions 0x80000002 thru 0x80000004: example, unused + uint32_t proc_name_0, proc_name_1, proc_name_2, proc_name_3; + uint32_t proc_name_4, proc_name_5, proc_name_6, proc_name_7; + uint32_t proc_name_8, proc_name_9, proc_name_10,proc_name_11; + + // cpuid function 0x80000005 //AMD L1, Intel reserved + uint32_t ext_cpuid5_rax; // unused currently + uint32_t ext_cpuid5_rbx; // reserved + ExtCpuid5Ex ext_cpuid5_rcx; // L1 data cache info (AMD) + ExtCpuid5Ex ext_cpuid5_rdx; // L1 instruction cache info (AMD) + + // cpuid function 0x80000008 + uint32_t ext_cpuid8_rax; // unused currently + uint32_t ext_cpuid8_rbx; // reserved + ExtCpuid8Ecx ext_cpuid8_rcx; + uint32_t ext_cpuid8_rdx; // reserved + }; + + // The actual cpuid info block + static CpuidInfo _cpuid_info; + + // Extractors and predicates + static bool is_extended_cpu_family() { + const uint32_t Extended_Cpu_Family = 0xf; + return _cpuid_info.std_cpuid1_rax.bits.family == Extended_Cpu_Family; + } + static uint32_t extended_cpu_family() { + uint32_t result = _cpuid_info.std_cpuid1_rax.bits.family; + if (is_extended_cpu_family()) { + result += _cpuid_info.std_cpuid1_rax.bits.ext_family; + } + return result; + } + static uint32_t extended_cpu_model() { + uint32_t result = _cpuid_info.std_cpuid1_rax.bits.model; + if (is_extended_cpu_family()) { + result |= _cpuid_info.std_cpuid1_rax.bits.ext_model << 4; + } + return result; + } + static uint32_t cpu_stepping() { + uint32_t result = _cpuid_info.std_cpuid1_rax.bits.stepping; + return result; + } + static uint logical_processor_count() { + uint result = threads_per_core(); + return result; + } + static uint32_t feature_flags() { + uint32_t result = 0; + if (_cpuid_info.std_cpuid1_rdx.bits.cmpxchg8 != 0) + result |= CPU_CX8; + if (_cpuid_info.std_cpuid1_rdx.bits.cmov != 0) + result |= CPU_CMOV; + if (_cpuid_info.std_cpuid1_rdx.bits.fxsr != 0 || is_amd() && + _cpuid_info.ext_cpuid1_rdx.bits.fxsr != 0) + result |= CPU_FXSR; + // HT flag is set for multi-core processors also. + if (threads_per_core() > 1) + result |= CPU_HT; + if (_cpuid_info.std_cpuid1_rdx.bits.mmx != 0 || is_amd() && + _cpuid_info.ext_cpuid1_rdx.bits.mmx != 0) + result |= CPU_MMX; + if (is_amd() && _cpuid_info.ext_cpuid1_rdx.bits.tdnow != 0) + result |= CPU_3DNOW; + if (_cpuid_info.std_cpuid1_rdx.bits.sse != 0) + result |= CPU_SSE; + if (_cpuid_info.std_cpuid1_rdx.bits.sse2 != 0) + result |= CPU_SSE2; + if (_cpuid_info.std_cpuid1_rcx.bits.sse3 != 0) + result |= CPU_SSE3; + if (_cpuid_info.std_cpuid1_rcx.bits.ssse3 != 0) + result |= CPU_SSSE3; + if (is_amd() && _cpuid_info.ext_cpuid1_rcx.bits.sse4a != 0) + result |= CPU_SSE4A; + return result; + } + + static void get_processor_features(); + +public: + // Offsets for cpuid asm stub + static ByteSize std_cpuid0_offset() { return byte_offset_of(CpuidInfo, std_max_function); } + static ByteSize std_cpuid1_offset() { return byte_offset_of(CpuidInfo, std_cpuid1_rax); } + static ByteSize dcp_cpuid4_offset() { return byte_offset_of(CpuidInfo, dcp_cpuid4_rax); } + static ByteSize ext_cpuid1_offset() { return byte_offset_of(CpuidInfo, ext_cpuid1_rax); } + static ByteSize ext_cpuid5_offset() { return byte_offset_of(CpuidInfo, ext_cpuid5_rax); } + static ByteSize ext_cpuid8_offset() { return byte_offset_of(CpuidInfo, ext_cpuid8_rax); } + + // Initialization + static void initialize(); + + // Asserts + static void assert_is_initialized() { + assert(_cpuid_info.std_cpuid1_rax.bits.family != 0, "VM_Version not initialized"); + } + + // + // Processor family: + // 3 - 386 + // 4 - 486 + // 5 - Pentium + // 6 - PentiumPro, Pentium II, Celeron, Xeon, Pentium III, Athlon, + // Pentium M, Core Solo, Core Duo, Core2 Duo + // family 6 model: 9, 13, 14, 15 + // 0x0f - Pentium 4, Opteron + // + // Note: The cpu family should be used to select between + // instruction sequences which are valid on all Intel + // processors. Use the feature test functions below to + // determine whether a particular instruction is supported. + // + static int cpu_family() { return _cpu;} + static bool is_P6() { return cpu_family() >= 6; } + + static bool is_amd() { assert_is_initialized(); return _cpuid_info.std_vendor_name_0 == 0x68747541; } // 'htuA' + static bool is_intel() { assert_is_initialized(); return _cpuid_info.std_vendor_name_0 == 0x756e6547; } // 'uneG' + + static uint cores_per_cpu() { + uint result = 1; + if (is_intel()) { + result = (_cpuid_info.dcp_cpuid4_rax.bits.cores_per_cpu + 1); + } else if (is_amd()) { + result = (_cpuid_info.ext_cpuid8_rcx.bits.cores_per_cpu + 1); + } + return result; + } + + static uint threads_per_core() { + uint result = 1; + if (_cpuid_info.std_cpuid1_rdx.bits.ht != 0) { + result = _cpuid_info.std_cpuid1_rbx.bits.threads_per_cpu / + cores_per_cpu(); + } + return result; + } + + static intx L1_data_cache_line_size() { + intx result = 0; + if (is_intel()) { + result = (_cpuid_info.dcp_cpuid4_rbx.bits.L1_line_size + 1); + } else if (is_amd()) { + result = _cpuid_info.ext_cpuid5_rcx.bits.L1_line_size; + } + if (result < 32) // not defined ? + result = 32; // 32 bytes by default on x86 + return result; + } + + // + // Feature identification + // + static bool supports_cpuid() { return _cpuFeatures != 0; } + static bool supports_cmpxchg8() { return (_cpuFeatures & CPU_CX8) != 0; } + static bool supports_cmov() { return (_cpuFeatures & CPU_CMOV) != 0; } + static bool supports_fxsr() { return (_cpuFeatures & CPU_FXSR) != 0; } + static bool supports_ht() { return (_cpuFeatures & CPU_HT) != 0; } + static bool supports_mmx() { return (_cpuFeatures & CPU_MMX) != 0; } + static bool supports_sse() { return (_cpuFeatures & CPU_SSE) != 0; } + static bool supports_sse2() { return (_cpuFeatures & CPU_SSE2) != 0; } + static bool supports_sse3() { return (_cpuFeatures & CPU_SSE3) != 0; } + static bool supports_ssse3() { return (_cpuFeatures & CPU_SSSE3)!= 0; } + static bool supports_sse4() { return (_cpuFeatures & CPU_SSE4) != 0; } + // + // AMD features + // + static bool supports_3dnow() { return (_cpuFeatures & CPU_3DNOW) != 0; } + static bool supports_mmx_ext() { return is_amd() && _cpuid_info.ext_cpuid1_rdx.bits.mmx_amd != 0; } + static bool supports_3dnow2() { return is_amd() && _cpuid_info.ext_cpuid1_rdx.bits.tdnow2 != 0; } + static bool supports_sse4a() { return (_cpuFeatures & CPU_SSE4A) != 0; } + + static bool supports_compare_and_exchange() { return true; } + + static const char* cpu_features() { return _features_str; } + + static intx allocate_prefetch_distance() { + // This method should be called before allocate_prefetch_style(). + // + // Hardware prefetching (distance/size in bytes): + // Pentium 3 - 64 / 32 + // Pentium 4 - 256 / 128 + // Athlon - 64 / 32 ???? + // Opteron - 128 / 64 only when 2 sequential cache lines accessed + // Core - 128 / 64 + // + // Software prefetching (distance in bytes / instruction with best score): + // Pentium 3 - 128 / prefetchnta + // Pentium 4 - 512 / prefetchnta + // Athlon - 128 / prefetchnta + // Opteron - 256 / prefetchnta + // Core - 256 / prefetchnta + // It will be used only when AllocatePrefetchStyle > 0 + + intx count = AllocatePrefetchDistance; + if (count < 0) { // default ? + if (is_amd()) { // AMD + if (supports_sse2()) + count = 256; // Opteron + else + count = 128; // Athlon + } else { // Intel + if (supports_sse2()) + if (cpu_family() == 6) { + count = 256; // Pentium M, Core, Core2 + } else { + count = 512; // Pentium 4 + } + else + count = 128; // Pentium 3 (and all other old CPUs) + } + } + return count; + } + static intx allocate_prefetch_style() { + assert(AllocatePrefetchStyle >= 0, "AllocatePrefetchStyle should be positive"); + // Return 0 if AllocatePrefetchDistance was not defined or + // prefetch instruction is not supported. + return (AllocatePrefetchDistance > 0 && + (supports_3dnow() || supports_sse())) ? AllocatePrefetchStyle : 0; + } +}; diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86_64.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86_64.cpp new file mode 100644 index 00000000000..9777a520066 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/vm_version_x86_64.cpp @@ -0,0 +1,394 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vm_version_x86_64.cpp.incl" + +int VM_Version::_cpu; +int VM_Version::_model; +int VM_Version::_stepping; +int VM_Version::_cpuFeatures; +const char* VM_Version::_features_str = ""; +VM_Version::CpuidInfo VM_Version::_cpuid_info = { 0, }; + +static BufferBlob* stub_blob; +static const int stub_size = 300; + +extern "C" { + typedef void (*getPsrInfo_stub_t)(void*); +} +static getPsrInfo_stub_t getPsrInfo_stub = NULL; + + +class VM_Version_StubGenerator: public StubCodeGenerator { + public: + + VM_Version_StubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} + + address generate_getPsrInfo() { + + Label std_cpuid1, ext_cpuid1, ext_cpuid5, done; + + StubCodeMark mark(this, "VM_Version", "getPsrInfo_stub"); +# define __ _masm-> + + address start = __ pc(); + + // + // void getPsrInfo(VM_Version::CpuidInfo* cpuid_info); + // + // rcx and rdx are first and second argument registers on windows + + __ pushq(rbp); + __ movq(rbp, c_rarg0); // cpuid_info address + __ pushq(rbx); + __ pushq(rsi); + + // + // we have a chip which supports the "cpuid" instruction + // + __ xorl(rax, rax); + __ cpuid(); + __ leaq(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid0_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + __ cmpl(rax, 3); // Is cpuid(0x4) supported? + __ jccb(Assembler::belowEqual, std_cpuid1); + + // + // cpuid(0x4) Deterministic cache params + // + __ movl(rax, 4); + __ xorl(rcx, rcx); // L1 cache + __ cpuid(); + __ pushq(rax); + __ andl(rax, 0x1f); // Determine if valid cache parameters used + __ orl(rax, rax); // eax[4:0] == 0 indicates invalid cache + __ popq(rax); + __ jccb(Assembler::equal, std_cpuid1); + + __ leaq(rsi, Address(rbp, in_bytes(VM_Version::dcp_cpuid4_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + // + // Standard cpuid(0x1) + // + __ bind(std_cpuid1); + __ movl(rax, 1); + __ cpuid(); + __ leaq(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid1_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + __ movl(rax, 0x80000000); + __ cpuid(); + __ cmpl(rax, 0x80000000); // Is cpuid(0x80000001) supported? + __ jcc(Assembler::belowEqual, done); + __ cmpl(rax, 0x80000004); // Is cpuid(0x80000005) supported? + __ jccb(Assembler::belowEqual, ext_cpuid1); + __ cmpl(rax, 0x80000007); // Is cpuid(0x80000008) supported? + __ jccb(Assembler::belowEqual, ext_cpuid5); + // + // Extended cpuid(0x80000008) + // + __ movl(rax, 0x80000008); + __ cpuid(); + __ leaq(rsi, Address(rbp, in_bytes(VM_Version::ext_cpuid8_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + // + // Extended cpuid(0x80000005) + // + __ bind(ext_cpuid5); + __ movl(rax, 0x80000005); + __ cpuid(); + __ leaq(rsi, Address(rbp, in_bytes(VM_Version::ext_cpuid5_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + // + // Extended cpuid(0x80000001) + // + __ bind(ext_cpuid1); + __ movl(rax, 0x80000001); + __ cpuid(); + __ leaq(rsi, Address(rbp, in_bytes(VM_Version::ext_cpuid1_offset()))); + __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rbx); + __ movl(Address(rsi, 8), rcx); + __ movl(Address(rsi,12), rdx); + + // + // return + // + __ bind(done); + __ popq(rsi); + __ popq(rbx); + __ popq(rbp); + __ ret(0); + +# undef __ + + return start; + }; +}; + + +void VM_Version::get_processor_features() { + + _logical_processors_per_package = 1; + // Get raw processor info + getPsrInfo_stub(&_cpuid_info); + assert_is_initialized(); + _cpu = extended_cpu_family(); + _model = extended_cpu_model(); + _stepping = cpu_stepping(); + _cpuFeatures = feature_flags(); + // Logical processors are only available on P4s and above, + // and only if hyperthreading is available. + _logical_processors_per_package = logical_processor_count(); + _supports_cx8 = supports_cmpxchg8(); + // OS should support SSE for x64 and hardware should support at least SSE2. + if (!VM_Version::supports_sse2()) { + vm_exit_during_initialization("Unknown x64 processor: SSE2 not supported"); + } + if (UseSSE < 4) + _cpuFeatures &= ~CPU_SSE4; + if (UseSSE < 3) { + _cpuFeatures &= ~CPU_SSE3; + _cpuFeatures &= ~CPU_SSSE3; + _cpuFeatures &= ~CPU_SSE4A; + } + if (UseSSE < 2) + _cpuFeatures &= ~CPU_SSE2; + if (UseSSE < 1) + _cpuFeatures &= ~CPU_SSE; + + if (logical_processors_per_package() == 1) { + // HT processor could be installed on a system which doesn't support HT. + _cpuFeatures &= ~CPU_HT; + } + + char buf[256]; + jio_snprintf(buf, sizeof(buf), "(%u cores per cpu, %u threads per core) family %d model %d stepping %d%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + cores_per_cpu(), threads_per_core(), + cpu_family(), _model, _stepping, + (supports_cmov() ? ", cmov" : ""), + (supports_cmpxchg8() ? ", cx8" : ""), + (supports_fxsr() ? ", fxsr" : ""), + (supports_mmx() ? ", mmx" : ""), + (supports_sse() ? ", sse" : ""), + (supports_sse2() ? ", sse2" : ""), + (supports_sse3() ? ", sse3" : ""), + (supports_ssse3()? ", ssse3": ""), + (supports_sse4() ? ", sse4" : ""), + (supports_mmx_ext() ? ", mmxext" : ""), + (supports_3dnow() ? ", 3dnow" : ""), + (supports_3dnow2() ? ", 3dnowext" : ""), + (supports_sse4a() ? ", sse4a": ""), + (supports_ht() ? ", ht": "")); + _features_str = strdup(buf); + + // UseSSE is set to the smaller of what hardware supports and what + // the command line requires. I.e., you cannot set UseSSE to 2 on + // older Pentiums which do not support it. + if( UseSSE > 4 ) UseSSE=4; + if( UseSSE < 0 ) UseSSE=0; + if( !supports_sse4() ) // Drop to 3 if no SSE4 support + UseSSE = MIN2((intx)3,UseSSE); + if( !supports_sse3() ) // Drop to 2 if no SSE3 support + UseSSE = MIN2((intx)2,UseSSE); + if( !supports_sse2() ) // Drop to 1 if no SSE2 support + UseSSE = MIN2((intx)1,UseSSE); + if( !supports_sse () ) // Drop to 0 if no SSE support + UseSSE = 0; + + // On new cpus instructions which update whole XMM register should be used + // to prevent partial register stall due to dependencies on high half. + // + // UseXmmLoadAndClearUpper == true --> movsd(xmm, mem) + // UseXmmLoadAndClearUpper == false --> movlpd(xmm, mem) + // UseXmmRegToRegMoveAll == true --> movaps(xmm, xmm), movapd(xmm, xmm). + // UseXmmRegToRegMoveAll == false --> movss(xmm, xmm), movsd(xmm, xmm). + + if( is_amd() ) { // AMD cpus specific settings + if( FLAG_IS_DEFAULT(UseAddressNop) ) { + // Use it on all AMD cpus starting from Opteron (don't need + // a cpu check since only Opteron and new cpus support 64-bits mode). + UseAddressNop = true; + } + if( FLAG_IS_DEFAULT(UseXmmLoadAndClearUpper) ) { + if( supports_sse4a() ) { + UseXmmLoadAndClearUpper = true; // use movsd only on '10h' Opteron + } else { + UseXmmLoadAndClearUpper = false; + } + } + if( FLAG_IS_DEFAULT(UseXmmRegToRegMoveAll) ) { + if( supports_sse4a() ) { + UseXmmRegToRegMoveAll = true; // use movaps, movapd only on '10h' + } else { + UseXmmRegToRegMoveAll = false; + } + } + } + + if( is_intel() ) { // Intel cpus specific settings + if( FLAG_IS_DEFAULT(UseStoreImmI16) ) { + UseStoreImmI16 = false; // don't use it on Intel cpus + } + if( FLAG_IS_DEFAULT(UseAddressNop) ) { + // Use it on all Intel cpus starting from PentiumPro + // (don't need a cpu check since only new cpus support 64-bits mode). + UseAddressNop = true; + } + if( FLAG_IS_DEFAULT(UseXmmLoadAndClearUpper) ) { + UseXmmLoadAndClearUpper = true; // use movsd on all Intel cpus + } + if( FLAG_IS_DEFAULT(UseXmmRegToRegMoveAll) ) { + if( supports_sse3() ) { + UseXmmRegToRegMoveAll = true; // use movaps, movapd on new Intel cpus + } else { + UseXmmRegToRegMoveAll = false; + } + } + if( cpu_family() == 6 && supports_sse3() ) { // New Intel cpus +#ifdef COMPILER2 + if( FLAG_IS_DEFAULT(MaxLoopPad) ) { + // For new Intel cpus do the next optimization: + // don't align the beginning of a loop if there are enough instructions + // left (NumberOfLoopInstrToAlign defined in c2_globals.hpp) + // in current fetch line (OptoLoopAlignment) or the padding + // is big (> MaxLoopPad). + // Set MaxLoopPad to 11 for new Intel cpus to reduce number of + // generated NOP instructions. 11 is the largest size of one + // address NOP instruction '0F 1F' (see Assembler::nop(i)). + MaxLoopPad = 11; + } +#endif // COMPILER2 + } + } + + assert(0 <= ReadPrefetchInstr && ReadPrefetchInstr <= 3, "invalid value"); + assert(0 <= AllocatePrefetchInstr && AllocatePrefetchInstr <= 3, "invalid value"); + + // set valid Prefetch instruction + if( ReadPrefetchInstr < 0 ) ReadPrefetchInstr = 0; + if( ReadPrefetchInstr > 3 ) ReadPrefetchInstr = 3; + if( ReadPrefetchInstr == 3 && !supports_3dnow() ) ReadPrefetchInstr = 0; + + if( AllocatePrefetchInstr < 0 ) AllocatePrefetchInstr = 0; + if( AllocatePrefetchInstr > 3 ) AllocatePrefetchInstr = 3; + if( AllocatePrefetchInstr == 3 && !supports_3dnow() ) AllocatePrefetchInstr=0; + + // Allocation prefetch settings + intx cache_line_size = L1_data_cache_line_size(); + if( cache_line_size > AllocatePrefetchStepSize ) + AllocatePrefetchStepSize = cache_line_size; + if( FLAG_IS_DEFAULT(AllocatePrefetchLines) ) + AllocatePrefetchLines = 3; // Optimistic value + assert(AllocatePrefetchLines > 0, "invalid value"); + if( AllocatePrefetchLines < 1 ) // set valid value in product VM + AllocatePrefetchLines = 1; // Conservative value + + AllocatePrefetchDistance = allocate_prefetch_distance(); + AllocatePrefetchStyle = allocate_prefetch_style(); + + if( AllocatePrefetchStyle == 2 && is_intel() && + cpu_family() == 6 && supports_sse3() ) { // watermark prefetching on Core + AllocatePrefetchDistance = 384; + } + assert(AllocatePrefetchDistance % AllocatePrefetchStepSize == 0, "invalid value"); + + // Prefetch settings + PrefetchCopyIntervalInBytes = prefetch_copy_interval_in_bytes(); + PrefetchScanIntervalInBytes = prefetch_scan_interval_in_bytes(); + PrefetchFieldsAhead = prefetch_fields_ahead(); + +#ifndef PRODUCT + if (PrintMiscellaneous && Verbose) { + tty->print_cr("Logical CPUs per package: %u", + logical_processors_per_package()); + tty->print_cr("UseSSE=%d",UseSSE); + tty->print("Allocation: "); + if (AllocatePrefetchStyle <= 0) { + tty->print_cr("no prefetching"); + } else { + if (AllocatePrefetchInstr == 0) { + tty->print("PREFETCHNTA"); + } else if (AllocatePrefetchInstr == 1) { + tty->print("PREFETCHT0"); + } else if (AllocatePrefetchInstr == 2) { + tty->print("PREFETCHT2"); + } else if (AllocatePrefetchInstr == 3) { + tty->print("PREFETCHW"); + } + if (AllocatePrefetchLines > 1) { + tty->print_cr(" %d, %d lines with step %d bytes", AllocatePrefetchDistance, AllocatePrefetchLines, AllocatePrefetchStepSize); + } else { + tty->print_cr(" %d, one line", AllocatePrefetchDistance); + } + } + if (PrefetchCopyIntervalInBytes > 0) { + tty->print_cr("PrefetchCopyIntervalInBytes %d", PrefetchCopyIntervalInBytes); + } + if (PrefetchScanIntervalInBytes > 0) { + tty->print_cr("PrefetchScanIntervalInBytes %d", PrefetchScanIntervalInBytes); + } + if (PrefetchFieldsAhead > 0) { + tty->print_cr("PrefetchFieldsAhead %d", PrefetchFieldsAhead); + } + } +#endif // !PRODUCT +} + +void VM_Version::initialize() { + ResourceMark rm; + // Making this stub must be FIRST use of assembler + + stub_blob = BufferBlob::create("getPsrInfo_stub", stub_size); + if (stub_blob == NULL) { + vm_exit_during_initialization("Unable to allocate getPsrInfo_stub"); + } + CodeBuffer c(stub_blob->instructions_begin(), + stub_blob->instructions_size()); + VM_Version_StubGenerator g(&c); + getPsrInfo_stub = CAST_TO_FN_PTR(getPsrInfo_stub_t, + g.generate_getPsrInfo()); + + get_processor_features(); +} diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86_64.hpp b/hotspot/src/cpu/x86/vm/vm_version_x86_64.hpp new file mode 100644 index 00000000000..dc60b370073 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/vm_version_x86_64.hpp @@ -0,0 +1,451 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class VM_Version : public Abstract_VM_Version { +public: + // cpuid result register layouts. These are all unions of a uint32_t + // (in case anyone wants access to the register as a whole) and a bitfield. + + union StdCpuid1Eax { + uint32_t value; + struct { + uint32_t stepping : 4, + model : 4, + family : 4, + proc_type : 2, + : 2, + ext_model : 4, + ext_family : 8, + : 4; + } bits; + }; + + union StdCpuid1Ebx { // example, unused + uint32_t value; + struct { + uint32_t brand_id : 8, + clflush_size : 8, + threads_per_cpu : 8, + apic_id : 8; + } bits; + }; + + union StdCpuid1Ecx { + uint32_t value; + struct { + uint32_t sse3 : 1, + : 2, + monitor : 1, + : 1, + vmx : 1, + : 1, + est : 1, + : 1, + ssse3 : 1, + cid : 1, + : 2, + cmpxchg16: 1, + : 4, + dca : 1, + : 4, + popcnt : 1, + : 8; + } bits; + }; + + union StdCpuid1Edx { + uint32_t value; + struct { + uint32_t : 4, + tsc : 1, + : 3, + cmpxchg8 : 1, + : 6, + cmov : 1, + : 7, + mmx : 1, + fxsr : 1, + sse : 1, + sse2 : 1, + : 1, + ht : 1, + : 3; + } bits; + }; + + union DcpCpuid4Eax { + uint32_t value; + struct { + uint32_t cache_type : 5, + : 21, + cores_per_cpu : 6; + } bits; + }; + + union DcpCpuid4Ebx { + uint32_t value; + struct { + uint32_t L1_line_size : 12, + partitions : 10, + associativity : 10; + } bits; + }; + + union ExtCpuid1Edx { + uint32_t value; + struct { + uint32_t : 22, + mmx_amd : 1, + mmx : 1, + fxsr : 1, + : 4, + long_mode : 1, + tdnow2 : 1, + tdnow : 1; + } bits; + }; + + union ExtCpuid1Ecx { + uint32_t value; + struct { + uint32_t LahfSahf : 1, + CmpLegacy : 1, + : 4, + abm : 1, + sse4a : 1, + misalignsse : 1, + prefetchw : 1, + : 22; + } bits; + }; + + union ExtCpuid5Ex { + uint32_t value; + struct { + uint32_t L1_line_size : 8, + L1_tag_lines : 8, + L1_assoc : 8, + L1_size : 8; + } bits; + }; + + union ExtCpuid8Ecx { + uint32_t value; + struct { + uint32_t cores_per_cpu : 8, + : 24; + } bits; + }; + +protected: + static int _cpu; + static int _model; + static int _stepping; + static int _cpuFeatures; // features returned by the "cpuid" instruction + // 0 if this instruction is not available + static const char* _features_str; + + enum { + CPU_CX8 = (1 << 0), // next bits are from cpuid 1 (EDX) + CPU_CMOV = (1 << 1), + CPU_FXSR = (1 << 2), + CPU_HT = (1 << 3), + CPU_MMX = (1 << 4), + CPU_3DNOW= (1 << 5), + CPU_SSE = (1 << 6), + CPU_SSE2 = (1 << 7), + CPU_SSE3 = (1 << 8), + CPU_SSSE3= (1 << 9), + CPU_SSE4 = (1 <<10), + CPU_SSE4A= (1 <<11) + } cpuFeatureFlags; + + // cpuid information block. All info derived from executing cpuid with + // various function numbers is stored here. Intel and AMD info is + // merged in this block: accessor methods disentangle it. + // + // The info block is laid out in subblocks of 4 dwords corresponding to + // eax, ebx, ecx and edx, whether or not they contain anything useful. + struct CpuidInfo { + // cpuid function 0 + uint32_t std_max_function; + uint32_t std_vendor_name_0; + uint32_t std_vendor_name_1; + uint32_t std_vendor_name_2; + + // cpuid function 1 + StdCpuid1Eax std_cpuid1_eax; + StdCpuid1Ebx std_cpuid1_ebx; + StdCpuid1Ecx std_cpuid1_ecx; + StdCpuid1Edx std_cpuid1_edx; + + // cpuid function 4 (deterministic cache parameters) + DcpCpuid4Eax dcp_cpuid4_eax; + DcpCpuid4Ebx dcp_cpuid4_ebx; + uint32_t dcp_cpuid4_ecx; // unused currently + uint32_t dcp_cpuid4_edx; // unused currently + + // cpuid function 0x80000000 // example, unused + uint32_t ext_max_function; + uint32_t ext_vendor_name_0; + uint32_t ext_vendor_name_1; + uint32_t ext_vendor_name_2; + + // cpuid function 0x80000001 + uint32_t ext_cpuid1_eax; // reserved + uint32_t ext_cpuid1_ebx; // reserved + ExtCpuid1Ecx ext_cpuid1_ecx; + ExtCpuid1Edx ext_cpuid1_edx; + + // cpuid functions 0x80000002 thru 0x80000004: example, unused + uint32_t proc_name_0, proc_name_1, proc_name_2, proc_name_3; + uint32_t proc_name_4, proc_name_5, proc_name_6, proc_name_7; + uint32_t proc_name_8, proc_name_9, proc_name_10,proc_name_11; + + // cpuid function 0x80000005 //AMD L1, Intel reserved + uint32_t ext_cpuid5_eax; // unused currently + uint32_t ext_cpuid5_ebx; // reserved + ExtCpuid5Ex ext_cpuid5_ecx; // L1 data cache info (AMD) + ExtCpuid5Ex ext_cpuid5_edx; // L1 instruction cache info (AMD) + + // cpuid function 0x80000008 + uint32_t ext_cpuid8_eax; // unused currently + uint32_t ext_cpuid8_ebx; // reserved + ExtCpuid8Ecx ext_cpuid8_ecx; + uint32_t ext_cpuid8_edx; // reserved + }; + + // The actual cpuid info block + static CpuidInfo _cpuid_info; + + // Extractors and predicates + static bool is_extended_cpu_family() { + const uint32_t Extended_Cpu_Family = 0xf; + return _cpuid_info.std_cpuid1_eax.bits.family == Extended_Cpu_Family; + } + static uint32_t extended_cpu_family() { + uint32_t result = _cpuid_info.std_cpuid1_eax.bits.family; + if (is_extended_cpu_family()) { + result += _cpuid_info.std_cpuid1_eax.bits.ext_family; + } + return result; + } + static uint32_t extended_cpu_model() { + uint32_t result = _cpuid_info.std_cpuid1_eax.bits.model; + if (is_extended_cpu_family()) { + result |= _cpuid_info.std_cpuid1_eax.bits.ext_model << 4; + } + return result; + } + static uint32_t cpu_stepping() { + uint32_t result = _cpuid_info.std_cpuid1_eax.bits.stepping; + return result; + } + static uint logical_processor_count() { + uint result = threads_per_core(); + return result; + } + static uint32_t feature_flags() { + uint32_t result = 0; + if (_cpuid_info.std_cpuid1_edx.bits.cmpxchg8 != 0) + result |= CPU_CX8; + if (_cpuid_info.std_cpuid1_edx.bits.cmov != 0) + result |= CPU_CMOV; + if (_cpuid_info.std_cpuid1_edx.bits.fxsr != 0 || is_amd() && + _cpuid_info.ext_cpuid1_edx.bits.fxsr != 0) + result |= CPU_FXSR; + // HT flag is set for multi-core processors also. + if (threads_per_core() > 1) + result |= CPU_HT; + if (_cpuid_info.std_cpuid1_edx.bits.mmx != 0 || is_amd() && + _cpuid_info.ext_cpuid1_edx.bits.mmx != 0) + result |= CPU_MMX; + if (is_amd() && _cpuid_info.ext_cpuid1_edx.bits.tdnow != 0) + result |= CPU_3DNOW; + if (_cpuid_info.std_cpuid1_edx.bits.sse != 0) + result |= CPU_SSE; + if (_cpuid_info.std_cpuid1_edx.bits.sse2 != 0) + result |= CPU_SSE2; + if (_cpuid_info.std_cpuid1_ecx.bits.sse3 != 0) + result |= CPU_SSE3; + if (_cpuid_info.std_cpuid1_ecx.bits.ssse3 != 0) + result |= CPU_SSSE3; + if (is_amd() && _cpuid_info.ext_cpuid1_ecx.bits.sse4a != 0) + result |= CPU_SSE4A; + return result; + } + + static void get_processor_features(); + +public: + // Offsets for cpuid asm stub + static ByteSize std_cpuid0_offset() { return byte_offset_of(CpuidInfo, std_max_function); } + static ByteSize std_cpuid1_offset() { return byte_offset_of(CpuidInfo, std_cpuid1_eax); } + static ByteSize dcp_cpuid4_offset() { return byte_offset_of(CpuidInfo, dcp_cpuid4_eax); } + static ByteSize ext_cpuid1_offset() { return byte_offset_of(CpuidInfo, ext_cpuid1_eax); } + static ByteSize ext_cpuid5_offset() { return byte_offset_of(CpuidInfo, ext_cpuid5_eax); } + static ByteSize ext_cpuid8_offset() { return byte_offset_of(CpuidInfo, ext_cpuid8_eax); } + + // Initialization + static void initialize(); + + // Asserts + static void assert_is_initialized() { + assert(_cpuid_info.std_cpuid1_eax.bits.family != 0, "VM_Version not initialized"); + } + + // + // Processor family: + // 3 - 386 + // 4 - 486 + // 5 - Pentium + // 6 - PentiumPro, Pentium II, Celeron, Xeon, Pentium III, Athlon, + // Pentium M, Core Solo, Core Duo, Core2 Duo + // family 6 model: 9, 13, 14, 15 + // 0x0f - Pentium 4, Opteron + // + // Note: The cpu family should be used to select between + // instruction sequences which are valid on all Intel + // processors. Use the feature test functions below to + // determine whether a particular instruction is supported. + // + static int cpu_family() { return _cpu;} + static bool is_P6() { return cpu_family() >= 6; } + + static bool is_amd() { assert_is_initialized(); return _cpuid_info.std_vendor_name_0 == 0x68747541; } // 'htuA' + static bool is_intel() { assert_is_initialized(); return _cpuid_info.std_vendor_name_0 == 0x756e6547; } // 'uneG' + + static uint cores_per_cpu() { + uint result = 1; + if (is_intel()) { + result = (_cpuid_info.dcp_cpuid4_eax.bits.cores_per_cpu + 1); + } else if (is_amd()) { + result = (_cpuid_info.ext_cpuid8_ecx.bits.cores_per_cpu + 1); + } + return result; + } + + static uint threads_per_core() { + uint result = 1; + if (_cpuid_info.std_cpuid1_edx.bits.ht != 0) { + result = _cpuid_info.std_cpuid1_ebx.bits.threads_per_cpu / + cores_per_cpu(); + } + return result; + } + + static intx L1_data_cache_line_size() { + intx result = 0; + if (is_intel()) { + result = (_cpuid_info.dcp_cpuid4_ebx.bits.L1_line_size + 1); + } else if (is_amd()) { + result = _cpuid_info.ext_cpuid5_ecx.bits.L1_line_size; + } + if (result < 32) // not defined ? + result = 32; // 32 bytes by default for other x64 + return result; + } + + // + // Feature identification + // + static bool supports_cpuid() { return _cpuFeatures != 0; } + static bool supports_cmpxchg8() { return (_cpuFeatures & CPU_CX8) != 0; } + static bool supports_cmov() { return (_cpuFeatures & CPU_CMOV) != 0; } + static bool supports_fxsr() { return (_cpuFeatures & CPU_FXSR) != 0; } + static bool supports_ht() { return (_cpuFeatures & CPU_HT) != 0; } + static bool supports_mmx() { return (_cpuFeatures & CPU_MMX) != 0; } + static bool supports_sse() { return (_cpuFeatures & CPU_SSE) != 0; } + static bool supports_sse2() { return (_cpuFeatures & CPU_SSE2) != 0; } + static bool supports_sse3() { return (_cpuFeatures & CPU_SSE3) != 0; } + static bool supports_ssse3() { return (_cpuFeatures & CPU_SSSE3)!= 0; } + static bool supports_sse4() { return (_cpuFeatures & CPU_SSE4) != 0; } + // + // AMD features + // + static bool supports_3dnow() { return (_cpuFeatures & CPU_3DNOW) != 0; } + static bool supports_mmx_ext() { return is_amd() && _cpuid_info.ext_cpuid1_edx.bits.mmx_amd != 0; } + static bool supports_3dnow2() { return is_amd() && _cpuid_info.ext_cpuid1_edx.bits.tdnow2 != 0; } + static bool supports_sse4a() { return (_cpuFeatures & CPU_SSE4A) != 0; } + + static bool supports_compare_and_exchange() { return true; } + + static const char* cpu_features() { return _features_str; } + + static intx allocate_prefetch_distance() { + // This method should be called before allocate_prefetch_style(). + // + // Hardware prefetching (distance/size in bytes): + // Pentium 4 - 256 / 128 + // Opteron - 128 / 64 only when 2 sequential cache lines accessed + // Core - 128 / 64 + // + // Software prefetching (distance in bytes / instruction with best score): + // Pentium 4 - 512 / prefetchnta + // Opteron - 256 / prefetchnta + // Core - 256 / prefetchnta + // It will be used only when AllocatePrefetchStyle > 0 + + intx count = AllocatePrefetchDistance; + if (count < 0) { // default ? + if (is_amd()) { // AMD + count = 256; // Opteron + } else { // Intel + if (cpu_family() == 6) { + count = 256;// Pentium M, Core, Core2 + } else { + count = 512;// Pentium 4 + } + } + } + return count; + } + static intx allocate_prefetch_style() { + assert(AllocatePrefetchStyle >= 0, "AllocatePrefetchStyle should be positive"); + // Return 0 if AllocatePrefetchDistance was not defined. + return AllocatePrefetchDistance > 0 ? AllocatePrefetchStyle : 0; + } + + // Prefetch interval for gc copy/scan == 9 dcache lines. Derived from + // 50-warehouse specjbb runs on a 2-way 1.8ghz opteron using a 4gb heap. + // Tested intervals from 128 to 2048 in increments of 64 == one cache line. + // 256 bytes (4 dcache lines) was the nearest runner-up to 576. + + // gc copy/scan is disabled if prefetchw isn't supported, because + // Prefetch::write emits an inlined prefetchw on Linux. + // Do not use the 3dnow prefetchw instruction. It isn't supported on em64t. + // The used prefetcht0 instruction works for both amd64 and em64t. + static intx prefetch_copy_interval_in_bytes() { + intx interval = PrefetchCopyIntervalInBytes; + return interval >= 0 ? interval : 576; + } + static intx prefetch_scan_interval_in_bytes() { + intx interval = PrefetchScanIntervalInBytes; + return interval >= 0 ? interval : 576; + } + static intx prefetch_fields_ahead() { + intx count = PrefetchFieldsAhead; + return count >= 0 ? count : 1; + } +}; diff --git a/hotspot/src/cpu/x86/vm/vmreg_x86.cpp b/hotspot/src/cpu/x86/vm/vmreg_x86.cpp new file mode 100644 index 00000000000..5dc09f6c9e4 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/vmreg_x86.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vmreg_x86.cpp.incl" + + + +void VMRegImpl::set_regName() { + Register reg = ::as_Register(0); + int i; + for (i = 0; i < ConcreteRegisterImpl::max_gpr ; ) { + regName[i++] = reg->name(); +#ifdef AMD64 + regName[i++] = reg->name(); +#endif // AMD64 + reg = reg->successor(); + } + + FloatRegister freg = ::as_FloatRegister(0); + for ( ; i < ConcreteRegisterImpl::max_fpr ; ) { + regName[i++] = freg->name(); + regName[i++] = freg->name(); + freg = freg->successor(); + } + + XMMRegister xreg = ::as_XMMRegister(0); + for ( ; i < ConcreteRegisterImpl::max_xmm ; ) { + regName[i++] = xreg->name(); + regName[i++] = xreg->name(); + xreg = xreg->successor(); + } + for ( ; i < ConcreteRegisterImpl::number_of_registers ; i ++ ) { + regName[i] = "NON-GPR-FPR-XMM"; + } +} diff --git a/hotspot/src/cpu/x86/vm/vmreg_x86.hpp b/hotspot/src/cpu/x86/vm/vmreg_x86.hpp new file mode 100644 index 00000000000..048ae5016f2 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/vmreg_x86.hpp @@ -0,0 +1,32 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + bool is_Register(); + Register as_Register(); + + bool is_FloatRegister(); + FloatRegister as_FloatRegister(); + + bool is_XMMRegister(); + XMMRegister as_XMMRegister(); diff --git a/hotspot/src/cpu/x86/vm/vmreg_x86.inline.hpp b/hotspot/src/cpu/x86/vm/vmreg_x86.inline.hpp new file mode 100644 index 00000000000..1c47efdcfbd --- /dev/null +++ b/hotspot/src/cpu/x86/vm/vmreg_x86.inline.hpp @@ -0,0 +1,84 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline VMReg RegisterImpl::as_VMReg() { + if( this==noreg ) return VMRegImpl::Bad(); +#ifdef AMD64 + return VMRegImpl::as_VMReg(encoding() << 1 ); +#else + return VMRegImpl::as_VMReg(encoding() ); +#endif // AMD64 +} + +inline VMReg FloatRegisterImpl::as_VMReg() { + return VMRegImpl::as_VMReg((encoding() << 1) + ConcreteRegisterImpl::max_gpr); +} + +inline VMReg XMMRegisterImpl::as_VMReg() { + return VMRegImpl::as_VMReg((encoding() << 1) + ConcreteRegisterImpl::max_fpr); +} + + +inline bool VMRegImpl::is_Register() { + return (unsigned int) value() < (unsigned int) ConcreteRegisterImpl::max_gpr; +} + +inline bool VMRegImpl::is_FloatRegister() { + return value() >= ConcreteRegisterImpl::max_gpr && value() < ConcreteRegisterImpl::max_fpr; +} + +inline bool VMRegImpl::is_XMMRegister() { + return value() >= ConcreteRegisterImpl::max_fpr && value() < ConcreteRegisterImpl::max_xmm; +} + +inline Register VMRegImpl::as_Register() { + + assert( is_Register(), "must be"); + // Yuk +#ifdef AMD64 + return ::as_Register(value() >> 1); +#else + return ::as_Register(value()); +#endif // AMD64 +} + +inline FloatRegister VMRegImpl::as_FloatRegister() { + assert( is_FloatRegister() && is_even(value()), "must be" ); + // Yuk + return ::as_FloatRegister((value() - ConcreteRegisterImpl::max_gpr) >> 1); +} + +inline XMMRegister VMRegImpl::as_XMMRegister() { + assert( is_XMMRegister() && is_even(value()), "must be" ); + // Yuk + return ::as_XMMRegister((value() - ConcreteRegisterImpl::max_fpr) >> 1); +} + +inline bool VMRegImpl::is_concrete() { + assert(is_reg(), "must be"); +#ifndef AMD64 + if (is_Register()) return true; +#endif // AMD64 + return is_even(value()); +} diff --git a/hotspot/src/cpu/x86/vm/vtableStubs_x86_32.cpp b/hotspot/src/cpu/x86/vm/vtableStubs_x86_32.cpp new file mode 100644 index 00000000000..fcf84f7b494 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/vtableStubs_x86_32.cpp @@ -0,0 +1,216 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vtableStubs_x86_32.cpp.incl" + +// machine-dependent part of VtableStubs: create VtableStub of correct size and +// initialize its code + +#define __ masm-> + +#ifndef PRODUCT +extern "C" void bad_compiled_vtable_index(JavaThread* thread, oop receiver, int index); +#endif + +// used by compiler only; may use only caller saved registers rax, rbx, rcx. +// rdx holds first int arg, rsi, rdi, rbp are callee-save & must be preserved. +// Leave receiver in rcx; required behavior when +OptoArgsInRegisters +// is modifed to put first oop in rcx. +// +VtableStub* VtableStubs::create_vtable_stub(int vtable_index) { + const int i486_code_length = VtableStub::pd_code_size_limit(true); + VtableStub* s = new(i486_code_length) VtableStub(true, vtable_index); + ResourceMark rm; + CodeBuffer cb(s->entry_point(), i486_code_length); + MacroAssembler* masm = new MacroAssembler(&cb); + +#ifndef PRODUCT + + if (CountCompiledCalls) { + __ increment(ExternalAddress((address) SharedRuntime::nof_megamorphic_calls_addr())); + } +#endif /* PRODUCT */ + + // get receiver (need to skip return address on top of stack) + assert(VtableStub::receiver_location() == rcx->as_VMReg(), "receiver expected in rcx"); + + // get receiver klass + address npe_addr = __ pc(); + __ movl(rax, Address(rcx, oopDesc::klass_offset_in_bytes())); + // compute entry offset (in words) + int entry_offset = instanceKlass::vtable_start_offset() + vtable_index*vtableEntry::size(); +#ifndef PRODUCT + if (DebugVtables) { + Label L; + // check offset vs vtable length + __ cmpl(Address(rax, instanceKlass::vtable_length_offset()*wordSize), vtable_index*vtableEntry::size()); + __ jcc(Assembler::greater, L); + __ movl(rbx, vtable_index); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, bad_compiled_vtable_index), rcx, rbx); + __ bind(L); + } +#endif // PRODUCT + + const Register method = rbx; + + // load methodOop and target address + __ movl(method, Address(rax, entry_offset*wordSize + vtableEntry::method_offset_in_bytes())); + if (DebugVtables) { + Label L; + __ cmpl(method, NULL_WORD); + __ jcc(Assembler::equal, L); + __ cmpl(Address(method, methodOopDesc::from_compiled_offset()), NULL_WORD); + __ jcc(Assembler::notZero, L); + __ stop("Vtable entry is NULL"); + __ bind(L); + } + + // rax,: receiver klass + // method (rbx): methodOop + // rcx: receiver + address ame_addr = __ pc(); + __ jmp( Address(method, methodOopDesc::from_compiled_offset())); + + masm->flush(); + s->set_exception_points(npe_addr, ame_addr); + return s; +} + + +VtableStub* VtableStubs::create_itable_stub(int vtable_index) { + // Note well: pd_code_size_limit is the absolute minimum we can get away with. If you + // add code here, bump the code stub size returned by pd_code_size_limit! + const int i486_code_length = VtableStub::pd_code_size_limit(false); + VtableStub* s = new(i486_code_length) VtableStub(false, vtable_index); + ResourceMark rm; + CodeBuffer cb(s->entry_point(), i486_code_length); + MacroAssembler* masm = new MacroAssembler(&cb); + + // Entry arguments: + // rax,: Interface + // rcx: Receiver + +#ifndef PRODUCT + if (CountCompiledCalls) { + __ increment(ExternalAddress((address) SharedRuntime::nof_megamorphic_calls_addr())); + } +#endif /* PRODUCT */ + // get receiver (need to skip return address on top of stack) + + assert(VtableStub::receiver_location() == rcx->as_VMReg(), "receiver expected in rcx"); + + // get receiver klass (also an implicit null-check) + address npe_addr = __ pc(); + __ movl(rbx, Address(rcx, oopDesc::klass_offset_in_bytes())); + + __ movl(rsi, rbx); // Save klass in free register + // Most registers are in use, so save a few + __ pushl(rdx); + // compute itable entry offset (in words) + const int base = instanceKlass::vtable_start_offset() * wordSize; + assert(vtableEntry::size() * wordSize == 4, "adjust the scaling in the code below"); + __ movl(rdx, Address(rbx, instanceKlass::vtable_length_offset() * wordSize)); // Get length of vtable + __ leal(rbx, Address(rbx, rdx, Address::times_4, base)); + if (HeapWordsPerLong > 1) { + // Round up to align_object_offset boundary + __ round_to(rbx, BytesPerLong); + } + + Label hit, next, entry; + + __ jmp(entry); + + __ bind(next); + __ addl(rbx, itableOffsetEntry::size() * wordSize); + + __ bind(entry); + +#ifdef ASSERT + // Check that the entry is non-null + if (DebugVtables) { + Label L; + __ pushl(rbx); + __ movl(rbx, Address(rbx, itableOffsetEntry::interface_offset_in_bytes())); + __ testl(rbx, rbx); + __ jcc(Assembler::notZero, L); + __ stop("null entry point found in itable's offset table"); + __ bind(L); + __ popl(rbx); + } +#endif + __ cmpl(rax, Address(rbx, itableOffsetEntry::interface_offset_in_bytes())); + __ jcc(Assembler::notEqual, next); + + // We found a hit, move offset into rbx, + __ movl(rdx, Address(rbx, itableOffsetEntry::offset_offset_in_bytes())); + + // Compute itableMethodEntry. + const int method_offset = (itableMethodEntry::size() * wordSize * vtable_index) + itableMethodEntry::method_offset_in_bytes(); + + // Get methodOop and entrypoint for compiler + const Register method = rbx; + __ movl(method, Address(rsi, rdx, Address::times_1, method_offset)); + + // Restore saved register, before possible trap. + __ popl(rdx); + + // method (rbx): methodOop + // rcx: receiver + +#ifdef ASSERT + if (DebugVtables) { + Label L1; + __ cmpl(method, NULL_WORD); + __ jcc(Assembler::equal, L1); + __ cmpl(Address(method, methodOopDesc::from_compiled_offset()), NULL_WORD); + __ jcc(Assembler::notZero, L1); + __ stop("methodOop is null"); + __ bind(L1); + } +#endif // ASSERT + + address ame_addr = __ pc(); + __ jmp(Address(method, methodOopDesc::from_compiled_offset())); + + masm->flush(); + s->set_exception_points(npe_addr, ame_addr); + return s; +} + + + +int VtableStub::pd_code_size_limit(bool is_vtable_stub) { + if (is_vtable_stub) { + // Vtable stub size + return (DebugVtables ? 210 : 16) + (CountCompiledCalls ? 6 : 0); + } else { + // Itable stub size + return (DebugVtables ? 140 : 55) + (CountCompiledCalls ? 6 : 0); + } +} + +int VtableStub::pd_code_alignment() { + return wordSize; +} diff --git a/hotspot/src/cpu/x86/vm/vtableStubs_x86_64.cpp b/hotspot/src/cpu/x86/vm/vtableStubs_x86_64.cpp new file mode 100644 index 00000000000..fb6e87bb796 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/vtableStubs_x86_64.cpp @@ -0,0 +1,239 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vtableStubs_x86_64.cpp.incl" + +// machine-dependent part of VtableStubs: create VtableStub of correct size and +// initialize its code + +#define __ masm-> + +#ifndef PRODUCT +extern "C" void bad_compiled_vtable_index(JavaThread* thread, + oop receiver, + int index); +#endif + +VtableStub* VtableStubs::create_vtable_stub(int vtable_index) { + const int amd64_code_length = VtableStub::pd_code_size_limit(true); + VtableStub* s = new(amd64_code_length) VtableStub(true, vtable_index); + ResourceMark rm; + CodeBuffer cb(s->entry_point(), amd64_code_length); + MacroAssembler* masm = new MacroAssembler(&cb); + +#ifndef PRODUCT + if (CountCompiledCalls) { + __ incrementl(ExternalAddress((address) SharedRuntime::nof_megamorphic_calls_addr())); + } +#endif + + // get receiver (need to skip return address on top of stack) + assert(VtableStub::receiver_location() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); + + // Free registers (non-args) are rax, rbx + + // get receiver klass + address npe_addr = __ pc(); + __ movq(rax, Address(j_rarg0, oopDesc::klass_offset_in_bytes())); + + // compute entry offset (in words) + int entry_offset = + instanceKlass::vtable_start_offset() + vtable_index * vtableEntry::size(); + +#ifndef PRODUCT + if (DebugVtables) { + Label L; + // check offset vs vtable length + __ cmpl(Address(rax, instanceKlass::vtable_length_offset() * wordSize), + vtable_index * vtableEntry::size()); + __ jcc(Assembler::greater, L); + __ movl(rbx, vtable_index); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, bad_compiled_vtable_index), j_rarg0, rbx); + __ bind(L); + } +#endif // PRODUCT + + // load methodOop and target address + const Register method = rbx; + + __ movq(method, Address(rax, + entry_offset * wordSize + + vtableEntry::method_offset_in_bytes())); + if (DebugVtables) { + Label L; + __ cmpq(method, (int)NULL); + __ jcc(Assembler::equal, L); + __ cmpq(Address(method, methodOopDesc::from_compiled_offset()), (int)NULL_WORD); + __ jcc(Assembler::notZero, L); + __ stop("Vtable entry is NULL"); + __ bind(L); + } + // rax: receiver klass + // rbx: methodOop + // rcx: receiver + address ame_addr = __ pc(); + __ jmp( Address(rbx, methodOopDesc::from_compiled_offset())); + + __ flush(); + s->set_exception_points(npe_addr, ame_addr); + return s; +} + + +VtableStub* VtableStubs::create_itable_stub(int vtable_index) { + // Note well: pd_code_size_limit is the absolute minimum we can get + // away with. If you add code here, bump the code stub size + // returned by pd_code_size_limit! + const int amd64_code_length = VtableStub::pd_code_size_limit(false); + VtableStub* s = new(amd64_code_length) VtableStub(false, vtable_index); + ResourceMark rm; + CodeBuffer cb(s->entry_point(), amd64_code_length); + MacroAssembler* masm = new MacroAssembler(&cb); + +#ifndef PRODUCT + if (CountCompiledCalls) { + __ incrementl(ExternalAddress((address) SharedRuntime::nof_megamorphic_calls_addr())); + } +#endif + + // Entry arguments: + // rax: Interface + // j_rarg0: Receiver + + // Free registers (non-args) are rax (interface), rbx + + // get receiver (need to skip return address on top of stack) + + assert(VtableStub::receiver_location() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); + // get receiver klass (also an implicit null-check) + address npe_addr = __ pc(); + + __ movq(rbx, Address(j_rarg0, oopDesc::klass_offset_in_bytes())); + + // If we take a trap while this arg is on the stack we will not + // be able to walk the stack properly. This is not an issue except + // when there are mistakes in this assembly code that could generate + // a spurious fault. Ask me how I know... + + __ pushq(j_rarg1); // Most registers are in use, so save one + + // compute itable entry offset (in words) + const int base = instanceKlass::vtable_start_offset() * wordSize; + assert(vtableEntry::size() * wordSize == 8, + "adjust the scaling in the code below"); + // Get length of vtable + __ movl(j_rarg1, + Address(rbx, instanceKlass::vtable_length_offset() * wordSize)); + __ leaq(rbx, Address(rbx, j_rarg1, Address::times_8, base)); + + if (HeapWordsPerLong > 1) { + // Round up to align_object_offset boundary + __ round_to_q(rbx, BytesPerLong); + } + Label hit, next, entry; + + __ jmpb(entry); + + __ bind(next); + __ addq(rbx, itableOffsetEntry::size() * wordSize); + + __ bind(entry); + +#ifdef ASSERT + // Check that the entry is non-null + if (DebugVtables) { + Label L; + __ pushq(rbx); + __ movq(rbx, Address(rbx, itableOffsetEntry::interface_offset_in_bytes())); + __ testq(rbx, rbx); + __ jcc(Assembler::notZero, L); + __ stop("null entry point found in itable's offset table"); + __ bind(L); + __ popq(rbx); + } +#endif + + __ cmpq(rax, Address(rbx, itableOffsetEntry::interface_offset_in_bytes())); + __ jcc(Assembler::notEqual, next); + + // We found a hit, move offset into j_rarg1 + __ movl(j_rarg1, Address(rbx, itableOffsetEntry::offset_offset_in_bytes())); + + // Compute itableMethodEntry + const int method_offset = + (itableMethodEntry::size() * wordSize * vtable_index) + + itableMethodEntry::method_offset_in_bytes(); + + // Get methodOop and entrypoint for compiler + + // Get klass pointer again + __ movq(rax, Address(j_rarg0, oopDesc::klass_offset_in_bytes())); + + const Register method = rbx; + __ movq(method, Address(rax, j_rarg1, Address::times_1, method_offset)); + + // Restore saved register, before possible trap. + __ popq(j_rarg1); + + // method (rbx): methodOop + // j_rarg0: receiver + + +#ifdef ASSERT + if (DebugVtables) { + Label L2; + __ cmpq(method, (int)NULL); + __ jcc(Assembler::equal, L2); + __ cmpq(Address(method, methodOopDesc::from_compiled_offset()), (int)NULL_WORD); + __ jcc(Assembler::notZero, L2); + __ stop("compiler entrypoint is null"); + __ bind(L2); + } +#endif // ASSERT + + // rbx: methodOop + // j_rarg0: receiver + address ame_addr = __ pc(); + __ jmp(Address(method, methodOopDesc::from_compiled_offset())); + + __ flush(); + s->set_exception_points(npe_addr, ame_addr); + return s; +} + +int VtableStub::pd_code_size_limit(bool is_vtable_stub) { + if (is_vtable_stub) { + // Vtable stub size + return (DebugVtables ? 512 : 24) + (CountCompiledCalls ? 13 : 0); + } else { + // Itable stub size + return (DebugVtables ? 636 : 64) + (CountCompiledCalls ? 13 : 0); + } +} + +int VtableStub::pd_code_alignment() { + return wordSize; +} diff --git a/hotspot/src/cpu/x86/vm/x86_32.ad b/hotspot/src/cpu/x86/vm/x86_32.ad new file mode 100644 index 00000000000..e0a8c43af90 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/x86_32.ad @@ -0,0 +1,12778 @@ +// +// Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// X86 Architecture Description File + +//----------REGISTER DEFINITION BLOCK------------------------------------------ +// This information is used by the matcher and the register allocator to +// describe individual registers and classes of registers within the target +// archtecture. + +register %{ +//----------Architecture Description Register Definitions---------------------- +// General Registers +// "reg_def" name ( register save type, C convention save type, +// ideal register type, encoding ); +// Register Save Types: +// +// NS = No-Save: The register allocator assumes that these registers +// can be used without saving upon entry to the method, & +// that they do not need to be saved at call sites. +// +// SOC = Save-On-Call: The register allocator assumes that these registers +// can be used without saving upon entry to the method, +// but that they must be saved at call sites. +// +// SOE = Save-On-Entry: The register allocator assumes that these registers +// must be saved before using them upon entry to the +// method, but they do not need to be saved at call +// sites. +// +// AS = Always-Save: The register allocator assumes that these registers +// must be saved before using them upon entry to the +// method, & that they must be saved at call sites. +// +// Ideal Register Type is used to determine how to save & restore a +// register. Op_RegI will get spilled with LoadI/StoreI, Op_RegP will get +// spilled with LoadP/StoreP. If the register supports both, use Op_RegI. +// +// The encoding number is the actual bit-pattern placed into the opcodes. + +// General Registers +// Previously set EBX, ESI, and EDI as save-on-entry for java code +// Turn off SOE in java-code due to frequent use of uncommon-traps. +// Now that allocator is better, turn on ESI and EDI as SOE registers. + +reg_def EBX(SOC, SOE, Op_RegI, 3, rbx->as_VMReg()); +reg_def ECX(SOC, SOC, Op_RegI, 1, rcx->as_VMReg()); +reg_def ESI(SOC, SOE, Op_RegI, 6, rsi->as_VMReg()); +reg_def EDI(SOC, SOE, Op_RegI, 7, rdi->as_VMReg()); +// now that adapter frames are gone EBP is always saved and restored by the prolog/epilog code +reg_def EBP(NS, SOE, Op_RegI, 5, rbp->as_VMReg()); +reg_def EDX(SOC, SOC, Op_RegI, 2, rdx->as_VMReg()); +reg_def EAX(SOC, SOC, Op_RegI, 0, rax->as_VMReg()); +reg_def ESP( NS, NS, Op_RegI, 4, rsp->as_VMReg()); + +// Special Registers +reg_def EFLAGS(SOC, SOC, 0, 8, VMRegImpl::Bad()); + +// Float registers. We treat TOS/FPR0 special. It is invisible to the +// allocator, and only shows up in the encodings. +reg_def FPR0L( SOC, SOC, Op_RegF, 0, VMRegImpl::Bad()); +reg_def FPR0H( SOC, SOC, Op_RegF, 0, VMRegImpl::Bad()); +// Ok so here's the trick FPR1 is really st(0) except in the midst +// of emission of assembly for a machnode. During the emission the fpu stack +// is pushed making FPR1 == st(1) temporarily. However at any safepoint +// the stack will not have this element so FPR1 == st(0) from the +// oopMap viewpoint. This same weirdness with numbering causes +// instruction encoding to have to play games with the register +// encode to correct for this 0/1 issue. See MachSpillCopyNode::implementation +// where it does flt->flt moves to see an example +// +reg_def FPR1L( SOC, SOC, Op_RegF, 1, as_FloatRegister(0)->as_VMReg()); +reg_def FPR1H( SOC, SOC, Op_RegF, 1, as_FloatRegister(0)->as_VMReg()->next()); +reg_def FPR2L( SOC, SOC, Op_RegF, 2, as_FloatRegister(1)->as_VMReg()); +reg_def FPR2H( SOC, SOC, Op_RegF, 2, as_FloatRegister(1)->as_VMReg()->next()); +reg_def FPR3L( SOC, SOC, Op_RegF, 3, as_FloatRegister(2)->as_VMReg()); +reg_def FPR3H( SOC, SOC, Op_RegF, 3, as_FloatRegister(2)->as_VMReg()->next()); +reg_def FPR4L( SOC, SOC, Op_RegF, 4, as_FloatRegister(3)->as_VMReg()); +reg_def FPR4H( SOC, SOC, Op_RegF, 4, as_FloatRegister(3)->as_VMReg()->next()); +reg_def FPR5L( SOC, SOC, Op_RegF, 5, as_FloatRegister(4)->as_VMReg()); +reg_def FPR5H( SOC, SOC, Op_RegF, 5, as_FloatRegister(4)->as_VMReg()->next()); +reg_def FPR6L( SOC, SOC, Op_RegF, 6, as_FloatRegister(5)->as_VMReg()); +reg_def FPR6H( SOC, SOC, Op_RegF, 6, as_FloatRegister(5)->as_VMReg()->next()); +reg_def FPR7L( SOC, SOC, Op_RegF, 7, as_FloatRegister(6)->as_VMReg()); +reg_def FPR7H( SOC, SOC, Op_RegF, 7, as_FloatRegister(6)->as_VMReg()->next()); + +// XMM registers. 128-bit registers or 4 words each, labeled a-d. +// Word a in each register holds a Float, words ab hold a Double. +// We currently do not use the SIMD capabilities, so registers cd +// are unused at the moment. +reg_def XMM0a( SOC, SOC, Op_RegF, 0, xmm0->as_VMReg()); +reg_def XMM0b( SOC, SOC, Op_RegF, 0, xmm0->as_VMReg()->next()); +reg_def XMM1a( SOC, SOC, Op_RegF, 1, xmm1->as_VMReg()); +reg_def XMM1b( SOC, SOC, Op_RegF, 1, xmm1->as_VMReg()->next()); +reg_def XMM2a( SOC, SOC, Op_RegF, 2, xmm2->as_VMReg()); +reg_def XMM2b( SOC, SOC, Op_RegF, 2, xmm2->as_VMReg()->next()); +reg_def XMM3a( SOC, SOC, Op_RegF, 3, xmm3->as_VMReg()); +reg_def XMM3b( SOC, SOC, Op_RegF, 3, xmm3->as_VMReg()->next()); +reg_def XMM4a( SOC, SOC, Op_RegF, 4, xmm4->as_VMReg()); +reg_def XMM4b( SOC, SOC, Op_RegF, 4, xmm4->as_VMReg()->next()); +reg_def XMM5a( SOC, SOC, Op_RegF, 5, xmm5->as_VMReg()); +reg_def XMM5b( SOC, SOC, Op_RegF, 5, xmm5->as_VMReg()->next()); +reg_def XMM6a( SOC, SOC, Op_RegF, 6, xmm6->as_VMReg()); +reg_def XMM6b( SOC, SOC, Op_RegF, 6, xmm6->as_VMReg()->next()); +reg_def XMM7a( SOC, SOC, Op_RegF, 7, xmm7->as_VMReg()); +reg_def XMM7b( SOC, SOC, Op_RegF, 7, xmm7->as_VMReg()->next()); + +// Specify priority of register selection within phases of register +// allocation. Highest priority is first. A useful heuristic is to +// give registers a low priority when they are required by machine +// instructions, like EAX and EDX. Registers which are used as +// pairs must fall on an even boundry (witness the FPR#L's in this list). +// For the Intel integer registers, the equivalent Long pairs are +// EDX:EAX, EBX:ECX, and EDI:EBP. +alloc_class chunk0( ECX, EBX, EBP, EDI, EAX, EDX, ESI, ESP, + FPR0L, FPR0H, FPR1L, FPR1H, FPR2L, FPR2H, + FPR3L, FPR3H, FPR4L, FPR4H, FPR5L, FPR5H, + FPR6L, FPR6H, FPR7L, FPR7H ); + +alloc_class chunk1( XMM0a, XMM0b, + XMM1a, XMM1b, + XMM2a, XMM2b, + XMM3a, XMM3b, + XMM4a, XMM4b, + XMM5a, XMM5b, + XMM6a, XMM6b, + XMM7a, XMM7b, EFLAGS); + + +//----------Architecture Description Register Classes-------------------------- +// Several register classes are automatically defined based upon information in +// this architecture description. +// 1) reg_class inline_cache_reg ( /* as def'd in frame section */ ) +// 2) reg_class compiler_method_oop_reg ( /* as def'd in frame section */ ) +// 2) reg_class interpreter_method_oop_reg ( /* as def'd in frame section */ ) +// 3) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) +// +// Class for all registers +reg_class any_reg(EAX, EDX, EBP, EDI, ESI, ECX, EBX, ESP); +// Class for general registers +reg_class e_reg(EAX, EDX, EBP, EDI, ESI, ECX, EBX); +// Class for general registers which may be used for implicit null checks on win95 +// Also safe for use by tailjump. We don't want to allocate in rbp, +reg_class e_reg_no_rbp(EAX, EDX, EDI, ESI, ECX, EBX); +// Class of "X" registers +reg_class x_reg(EBX, ECX, EDX, EAX); +// Class of registers that can appear in an address with no offset. +// EBP and ESP require an extra instruction byte for zero offset. +// Used in fast-unlock +reg_class p_reg(EDX, EDI, ESI, EBX); +// Class for general registers not including ECX +reg_class ncx_reg(EAX, EDX, EBP, EDI, ESI, EBX); +// Class for general registers not including EAX +reg_class nax_reg(EDX, EDI, ESI, ECX, EBX); +// Class for general registers not including EAX or EBX. +reg_class nabx_reg(EDX, EDI, ESI, ECX, EBP); +// Class of EAX (for multiply and divide operations) +reg_class eax_reg(EAX); +// Class of EBX (for atomic add) +reg_class ebx_reg(EBX); +// Class of ECX (for shift and JCXZ operations and cmpLTMask) +reg_class ecx_reg(ECX); +// Class of EDX (for multiply and divide operations) +reg_class edx_reg(EDX); +// Class of EDI (for synchronization) +reg_class edi_reg(EDI); +// Class of ESI (for synchronization) +reg_class esi_reg(ESI); +// Singleton class for interpreter's stack pointer +reg_class ebp_reg(EBP); +// Singleton class for stack pointer +reg_class sp_reg(ESP); +// Singleton class for instruction pointer +// reg_class ip_reg(EIP); +// Singleton class for condition codes +reg_class int_flags(EFLAGS); +// Class of integer register pairs +reg_class long_reg( EAX,EDX, ECX,EBX, EBP,EDI ); +// Class of integer register pairs that aligns with calling convention +reg_class eadx_reg( EAX,EDX ); +reg_class ebcx_reg( ECX,EBX ); +// Not AX or DX, used in divides +reg_class nadx_reg( EBX,ECX,ESI,EDI,EBP ); + +// Floating point registers. Notice FPR0 is not a choice. +// FPR0 is not ever allocated; we use clever encodings to fake +// a 2-address instructions out of Intels FP stack. +reg_class flt_reg( FPR1L,FPR2L,FPR3L,FPR4L,FPR5L,FPR6L,FPR7L ); + +// make a register class for SSE registers +reg_class xmm_reg(XMM0a, XMM1a, XMM2a, XMM3a, XMM4a, XMM5a, XMM6a, XMM7a); + +// make a double register class for SSE2 registers +reg_class xdb_reg(XMM0a,XMM0b, XMM1a,XMM1b, XMM2a,XMM2b, XMM3a,XMM3b, + XMM4a,XMM4b, XMM5a,XMM5b, XMM6a,XMM6b, XMM7a,XMM7b ); + +reg_class dbl_reg( FPR1L,FPR1H, FPR2L,FPR2H, FPR3L,FPR3H, + FPR4L,FPR4H, FPR5L,FPR5H, FPR6L,FPR6H, + FPR7L,FPR7H ); + +reg_class flt_reg0( FPR1L ); +reg_class dbl_reg0( FPR1L,FPR1H ); +reg_class dbl_reg1( FPR2L,FPR2H ); +reg_class dbl_notreg0( FPR2L,FPR2H, FPR3L,FPR3H, FPR4L,FPR4H, + FPR5L,FPR5H, FPR6L,FPR6H, FPR7L,FPR7H ); + +// XMM6 and XMM7 could be used as temporary registers for long, float and +// double values for SSE2. +reg_class xdb_reg6( XMM6a,XMM6b ); +reg_class xdb_reg7( XMM7a,XMM7b ); +%} + + +//----------SOURCE BLOCK------------------------------------------------------- +// This is a block of C++ code which provides values, functions, and +// definitions necessary in the rest of the architecture description +source %{ +#define RELOC_IMM32 Assembler::imm32_operand +#define RELOC_DISP32 Assembler::disp32_operand + +#define __ _masm. + +// How to find the high register of a Long pair, given the low register +#define HIGH_FROM_LOW(x) ((x)+2) + +// These masks are used to provide 128-bit aligned bitmasks to the XMM +// instructions, to allow sign-masking or sign-bit flipping. They allow +// fast versions of NegF/NegD and AbsF/AbsD. + +// Note: 'double' and 'long long' have 32-bits alignment on x86. +static jlong* double_quadword(jlong *adr, jlong lo, jlong hi) { + // Use the expression (adr)&(~0xF) to provide 128-bits aligned address + // of 128-bits operands for SSE instructions. + jlong *operand = (jlong*)(((uintptr_t)adr)&((uintptr_t)(~0xF))); + // Store the value to a 128-bits operand. + operand[0] = lo; + operand[1] = hi; + return operand; +} + +// Buffer for 128-bits masks used by SSE instructions. +static jlong fp_signmask_pool[(4+1)*2]; // 4*128bits(data) + 128bits(alignment) + +// Static initialization during VM startup. +static jlong *float_signmask_pool = double_quadword(&fp_signmask_pool[1*2], CONST64(0x7FFFFFFF7FFFFFFF), CONST64(0x7FFFFFFF7FFFFFFF)); +static jlong *double_signmask_pool = double_quadword(&fp_signmask_pool[2*2], CONST64(0x7FFFFFFFFFFFFFFF), CONST64(0x7FFFFFFFFFFFFFFF)); +static jlong *float_signflip_pool = double_quadword(&fp_signmask_pool[3*2], CONST64(0x8000000080000000), CONST64(0x8000000080000000)); +static jlong *double_signflip_pool = double_quadword(&fp_signmask_pool[4*2], CONST64(0x8000000000000000), CONST64(0x8000000000000000)); + +// !!!!! Special hack to get all type of calls to specify the byte offset +// from the start of the call to the point where the return address +// will point. +int MachCallStaticJavaNode::ret_addr_offset() { + return 5 + (Compile::current()->in_24_bit_fp_mode() ? 6 : 0); // 5 bytes from start of call to where return address points +} + +int MachCallDynamicJavaNode::ret_addr_offset() { + return 10 + (Compile::current()->in_24_bit_fp_mode() ? 6 : 0); // 10 bytes from start of call to where return address points +} + +static int sizeof_FFree_Float_Stack_All = -1; + +int MachCallRuntimeNode::ret_addr_offset() { + assert(sizeof_FFree_Float_Stack_All != -1, "must have been emitted already"); + return sizeof_FFree_Float_Stack_All + 5 + (Compile::current()->in_24_bit_fp_mode() ? 6 : 0); +} + +// Indicate if the safepoint node needs the polling page as an input. +// Since x86 does have absolute addressing, it doesn't. +bool SafePointNode::needs_polling_address_input() { + return false; +} + +// +// Compute padding required for nodes which need alignment +// + +// The address of the call instruction needs to be 4-byte aligned to +// ensure that it does not span a cache line so that it can be patched. +int CallStaticJavaDirectNode::compute_padding(int current_offset) const { + if (Compile::current()->in_24_bit_fp_mode()) + current_offset += 6; // skip fldcw in pre_call_FPU, if any + current_offset += 1; // skip call opcode byte + return round_to(current_offset, alignment_required()) - current_offset; +} + +// The address of the call instruction needs to be 4-byte aligned to +// ensure that it does not span a cache line so that it can be patched. +int CallDynamicJavaDirectNode::compute_padding(int current_offset) const { + if (Compile::current()->in_24_bit_fp_mode()) + current_offset += 6; // skip fldcw in pre_call_FPU, if any + current_offset += 5; // skip MOV instruction + current_offset += 1; // skip call opcode byte + return round_to(current_offset, alignment_required()) - current_offset; +} + +#ifndef PRODUCT +void MachBreakpointNode::format( PhaseRegAlloc *, outputStream* st ) const { + st->print("INT3"); +} +#endif + +// EMIT_RM() +void emit_rm(CodeBuffer &cbuf, int f1, int f2, int f3) { + unsigned char c = (unsigned char)((f1 << 6) | (f2 << 3) | f3); + *(cbuf.code_end()) = c; + cbuf.set_code_end(cbuf.code_end() + 1); +} + +// EMIT_CC() +void emit_cc(CodeBuffer &cbuf, int f1, int f2) { + unsigned char c = (unsigned char)( f1 | f2 ); + *(cbuf.code_end()) = c; + cbuf.set_code_end(cbuf.code_end() + 1); +} + +// EMIT_OPCODE() +void emit_opcode(CodeBuffer &cbuf, int code) { + *(cbuf.code_end()) = (unsigned char)code; + cbuf.set_code_end(cbuf.code_end() + 1); +} + +// EMIT_OPCODE() w/ relocation information +void emit_opcode(CodeBuffer &cbuf, int code, relocInfo::relocType reloc, int offset = 0) { + cbuf.relocate(cbuf.inst_mark() + offset, reloc); + emit_opcode(cbuf, code); +} + +// EMIT_D8() +void emit_d8(CodeBuffer &cbuf, int d8) { + *(cbuf.code_end()) = (unsigned char)d8; + cbuf.set_code_end(cbuf.code_end() + 1); +} + +// EMIT_D16() +void emit_d16(CodeBuffer &cbuf, int d16) { + *((short *)(cbuf.code_end())) = d16; + cbuf.set_code_end(cbuf.code_end() + 2); +} + +// EMIT_D32() +void emit_d32(CodeBuffer &cbuf, int d32) { + *((int *)(cbuf.code_end())) = d32; + cbuf.set_code_end(cbuf.code_end() + 4); +} + +// emit 32 bit value and construct relocation entry from relocInfo::relocType +void emit_d32_reloc(CodeBuffer &cbuf, int d32, relocInfo::relocType reloc, + int format) { + cbuf.relocate(cbuf.inst_mark(), reloc, format); + + *((int *)(cbuf.code_end())) = d32; + cbuf.set_code_end(cbuf.code_end() + 4); +} + +// emit 32 bit value and construct relocation entry from RelocationHolder +void emit_d32_reloc(CodeBuffer &cbuf, int d32, RelocationHolder const& rspec, + int format) { +#ifdef ASSERT + if (rspec.reloc()->type() == relocInfo::oop_type && d32 != 0 && d32 != (int)Universe::non_oop_word()) { + assert(oop(d32)->is_oop() && oop(d32)->is_perm(), "cannot embed non-perm oops in code"); + } +#endif + cbuf.relocate(cbuf.inst_mark(), rspec, format); + + *((int *)(cbuf.code_end())) = d32; + cbuf.set_code_end(cbuf.code_end() + 4); +} + +// Access stack slot for load or store +void store_to_stackslot(CodeBuffer &cbuf, int opcode, int rm_field, int disp) { + emit_opcode( cbuf, opcode ); // (e.g., FILD [ESP+src]) + if( -128 <= disp && disp <= 127 ) { + emit_rm( cbuf, 0x01, rm_field, ESP_enc ); // R/M byte + emit_rm( cbuf, 0x00, ESP_enc, ESP_enc); // SIB byte + emit_d8 (cbuf, disp); // Displacement // R/M byte + } else { + emit_rm( cbuf, 0x02, rm_field, ESP_enc ); // R/M byte + emit_rm( cbuf, 0x00, ESP_enc, ESP_enc); // SIB byte + emit_d32(cbuf, disp); // Displacement // R/M byte + } +} + + // eRegI ereg, memory mem) %{ // emit_reg_mem +void encode_RegMem( CodeBuffer &cbuf, int reg_encoding, int base, int index, int scale, int displace, bool displace_is_oop ) { + // There is no index & no scale, use form without SIB byte + if ((index == 0x4) && + (scale == 0) && (base != ESP_enc)) { + // If no displacement, mode is 0x0; unless base is [EBP] + if ( (displace == 0) && (base != EBP_enc) ) { + emit_rm(cbuf, 0x0, reg_encoding, base); + } + else { // If 8-bit displacement, mode 0x1 + if ((displace >= -128) && (displace <= 127) + && !(displace_is_oop) ) { + emit_rm(cbuf, 0x1, reg_encoding, base); + emit_d8(cbuf, displace); + } + else { // If 32-bit displacement + if (base == -1) { // Special flag for absolute address + emit_rm(cbuf, 0x0, reg_encoding, 0x5); + // (manual lies; no SIB needed here) + if ( displace_is_oop ) { + emit_d32_reloc(cbuf, displace, relocInfo::oop_type, 1); + } else { + emit_d32 (cbuf, displace); + } + } + else { // Normal base + offset + emit_rm(cbuf, 0x2, reg_encoding, base); + if ( displace_is_oop ) { + emit_d32_reloc(cbuf, displace, relocInfo::oop_type, 1); + } else { + emit_d32 (cbuf, displace); + } + } + } + } + } + else { // Else, encode with the SIB byte + // If no displacement, mode is 0x0; unless base is [EBP] + if (displace == 0 && (base != EBP_enc)) { // If no displacement + emit_rm(cbuf, 0x0, reg_encoding, 0x4); + emit_rm(cbuf, scale, index, base); + } + else { // If 8-bit displacement, mode 0x1 + if ((displace >= -128) && (displace <= 127) + && !(displace_is_oop) ) { + emit_rm(cbuf, 0x1, reg_encoding, 0x4); + emit_rm(cbuf, scale, index, base); + emit_d8(cbuf, displace); + } + else { // If 32-bit displacement + if (base == 0x04 ) { + emit_rm(cbuf, 0x2, reg_encoding, 0x4); + emit_rm(cbuf, scale, index, 0x04); + } else { + emit_rm(cbuf, 0x2, reg_encoding, 0x4); + emit_rm(cbuf, scale, index, base); + } + if ( displace_is_oop ) { + emit_d32_reloc(cbuf, displace, relocInfo::oop_type, 1); + } else { + emit_d32 (cbuf, displace); + } + } + } + } +} + + +void encode_Copy( CodeBuffer &cbuf, int dst_encoding, int src_encoding ) { + if( dst_encoding == src_encoding ) { + // reg-reg copy, use an empty encoding + } else { + emit_opcode( cbuf, 0x8B ); + emit_rm(cbuf, 0x3, dst_encoding, src_encoding ); + } +} + +void encode_CopyXD( CodeBuffer &cbuf, int dst_encoding, int src_encoding ) { + if( dst_encoding == src_encoding ) { + // reg-reg copy, use an empty encoding + } else { + MacroAssembler _masm(&cbuf); + + __ movdqa(as_XMMRegister(dst_encoding), as_XMMRegister(src_encoding)); + } +} + + +//============================================================================= +#ifndef PRODUCT +void MachPrologNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { + Compile* C = ra_->C; + if( C->in_24_bit_fp_mode() ) { + tty->print("FLDCW 24 bit fpu control word"); + tty->print_cr(""); tty->print("\t"); + } + + int framesize = C->frame_slots() << LogBytesPerInt; + assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); + // Remove two words for return addr and rbp, + framesize -= 2*wordSize; + + // Calls to C2R adapters often do not accept exceptional returns. + // We require that their callers must bang for them. But be careful, because + // some VM calls (such as call site linkage) can use several kilobytes of + // stack. But the stack safety zone should account for that. + // See bugs 4446381, 4468289, 4497237. + if (C->need_stack_bang(framesize)) { + tty->print_cr("# stack bang"); tty->print("\t"); + } + tty->print_cr("PUSHL EBP"); tty->print("\t"); + + if( VerifyStackAtCalls ) { // Majik cookie to verify stack depth + tty->print("PUSH 0xBADB100D\t# Majik cookie for stack depth check"); + tty->print_cr(""); tty->print("\t"); + framesize -= wordSize; + } + + if ((C->in_24_bit_fp_mode() || VerifyStackAtCalls ) && framesize < 128 ) { + if (framesize) { + tty->print("SUB ESP,%d\t# Create frame",framesize); + } + } else { + tty->print("SUB ESP,%d\t# Create frame",framesize); + } +} +#endif + + +void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + Compile* C = ra_->C; + + if (UseSSE >= 2 && VerifyFPU) { + MacroAssembler masm(&cbuf); + masm.verify_FPU(0, "FPU stack must be clean on entry"); + } + + // WARNING: Initial instruction MUST be 5 bytes or longer so that + // NativeJump::patch_verified_entry will be able to patch out the entry + // code safely. The fldcw is ok at 6 bytes, the push to verify stack + // depth is ok at 5 bytes, the frame allocation can be either 3 or + // 6 bytes. So if we don't do the fldcw or the push then we must + // use the 6 byte frame allocation even if we have no frame. :-( + // If method sets FPU control word do it now + if( C->in_24_bit_fp_mode() ) { + MacroAssembler masm(&cbuf); + masm.fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_24())); + } + + int framesize = C->frame_slots() << LogBytesPerInt; + assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); + // Remove two words for return addr and rbp, + framesize -= 2*wordSize; + + // Calls to C2R adapters often do not accept exceptional returns. + // We require that their callers must bang for them. But be careful, because + // some VM calls (such as call site linkage) can use several kilobytes of + // stack. But the stack safety zone should account for that. + // See bugs 4446381, 4468289, 4497237. + if (C->need_stack_bang(framesize)) { + MacroAssembler masm(&cbuf); + masm.generate_stack_overflow_check(framesize); + } + + // We always push rbp, so that on return to interpreter rbp, will be + // restored correctly and we can correct the stack. + emit_opcode(cbuf, 0x50 | EBP_enc); + + if( VerifyStackAtCalls ) { // Majik cookie to verify stack depth + emit_opcode(cbuf, 0x68); // push 0xbadb100d + emit_d32(cbuf, 0xbadb100d); + framesize -= wordSize; + } + + if ((C->in_24_bit_fp_mode() || VerifyStackAtCalls ) && framesize < 128 ) { + if (framesize) { + emit_opcode(cbuf, 0x83); // sub SP,#framesize + emit_rm(cbuf, 0x3, 0x05, ESP_enc); + emit_d8(cbuf, framesize); + } + } else { + emit_opcode(cbuf, 0x81); // sub SP,#framesize + emit_rm(cbuf, 0x3, 0x05, ESP_enc); + emit_d32(cbuf, framesize); + } + C->set_frame_complete(cbuf.code_end() - cbuf.code_begin()); + +#ifdef ASSERT + if (VerifyStackAtCalls) { + Label L; + MacroAssembler masm(&cbuf); + masm.pushl(rax); + masm.movl(rax, rsp); + masm.andl(rax, StackAlignmentInBytes-1); + masm.cmpl(rax, StackAlignmentInBytes-wordSize); + masm.popl(rax); + masm.jcc(Assembler::equal, L); + masm.stop("Stack is not properly aligned!"); + masm.bind(L); + } +#endif + +} + +uint MachPrologNode::size(PhaseRegAlloc *ra_) const { + return MachNode::size(ra_); // too many variables; just compute it the hard way +} + +int MachPrologNode::reloc() const { + return 0; // a large enough number +} + +//============================================================================= +#ifndef PRODUCT +void MachEpilogNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { + Compile *C = ra_->C; + int framesize = C->frame_slots() << LogBytesPerInt; + assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); + // Remove two words for return addr and rbp, + framesize -= 2*wordSize; + + if( C->in_24_bit_fp_mode() ) { + st->print("FLDCW standard control word"); + st->cr(); st->print("\t"); + } + if( framesize ) { + st->print("ADD ESP,%d\t# Destroy frame",framesize); + st->cr(); st->print("\t"); + } + st->print_cr("POPL EBP"); st->print("\t"); + if( do_polling() && C->is_method_compilation() ) { + st->print("TEST PollPage,EAX\t! Poll Safepoint"); + st->cr(); st->print("\t"); + } +} +#endif + +void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + Compile *C = ra_->C; + + // If method set FPU control word, restore to standard control word + if( C->in_24_bit_fp_mode() ) { + MacroAssembler masm(&cbuf); + masm.fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); + } + + int framesize = C->frame_slots() << LogBytesPerInt; + assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); + // Remove two words for return addr and rbp, + framesize -= 2*wordSize; + + // Note that VerifyStackAtCalls' Majik cookie does not change the frame size popped here + + if( framesize >= 128 ) { + emit_opcode(cbuf, 0x81); // add SP, #framesize + emit_rm(cbuf, 0x3, 0x00, ESP_enc); + emit_d32(cbuf, framesize); + } + else if( framesize ) { + emit_opcode(cbuf, 0x83); // add SP, #framesize + emit_rm(cbuf, 0x3, 0x00, ESP_enc); + emit_d8(cbuf, framesize); + } + + emit_opcode(cbuf, 0x58 | EBP_enc); + + if( do_polling() && C->is_method_compilation() ) { + cbuf.relocate(cbuf.code_end(), relocInfo::poll_return_type, 0); + emit_opcode(cbuf,0x85); + emit_rm(cbuf, 0x0, EAX_enc, 0x5); // EAX + emit_d32(cbuf, (intptr_t)os::get_polling_page()); + } +} + +uint MachEpilogNode::size(PhaseRegAlloc *ra_) const { + Compile *C = ra_->C; + // If method set FPU control word, restore to standard control word + int size = C->in_24_bit_fp_mode() ? 6 : 0; + if( do_polling() && C->is_method_compilation() ) size += 6; + + int framesize = C->frame_slots() << LogBytesPerInt; + assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); + // Remove two words for return addr and rbp, + framesize -= 2*wordSize; + + size++; // popl rbp, + + if( framesize >= 128 ) { + size += 6; + } else { + size += framesize ? 3 : 0; + } + return size; +} + +int MachEpilogNode::reloc() const { + return 0; // a large enough number +} + +const Pipeline * MachEpilogNode::pipeline() const { + return MachNode::pipeline_class(); +} + +int MachEpilogNode::safepoint_offset() const { return 0; } + +//============================================================================= + +enum RC { rc_bad, rc_int, rc_float, rc_xmm, rc_stack }; +static enum RC rc_class( OptoReg::Name reg ) { + + if( !OptoReg::is_valid(reg) ) return rc_bad; + if (OptoReg::is_stack(reg)) return rc_stack; + + VMReg r = OptoReg::as_VMReg(reg); + if (r->is_Register()) return rc_int; + if (r->is_FloatRegister()) { + assert(UseSSE < 2, "shouldn't be used in SSE2+ mode"); + return rc_float; + } + assert(r->is_XMMRegister(), "must be"); + return rc_xmm; +} + +static int impl_helper( CodeBuffer *cbuf, bool do_size, bool is_load, int offset, int reg, int opcode, const char *op_str, int size ) { + if( cbuf ) { + emit_opcode (*cbuf, opcode ); + encode_RegMem(*cbuf, Matcher::_regEncode[reg], ESP_enc, 0x4, 0, offset, false); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) tty->print("\n\t"); + if( opcode == 0x8B || opcode == 0x89 ) { // MOV + if( is_load ) tty->print("%s %s,[ESP + #%d]",op_str,Matcher::regName[reg],offset); + else tty->print("%s [ESP + #%d],%s",op_str,offset,Matcher::regName[reg]); + } else { // FLD, FST, PUSH, POP + tty->print("%s [ESP + #%d]",op_str,offset); + } +#endif + } + int offset_size = (offset == 0) ? 0 : ((offset <= 127) ? 1 : 4); + return size+3+offset_size; +} + +// Helper for XMM registers. Extra opcode bits, limited syntax. +static int impl_x_helper( CodeBuffer *cbuf, bool do_size, bool is_load, + int offset, int reg_lo, int reg_hi, int size ) { + if( cbuf ) { + if( reg_lo+1 == reg_hi ) { // double move? + if( is_load && !UseXmmLoadAndClearUpper ) + emit_opcode(*cbuf, 0x66 ); // use 'movlpd' for load + else + emit_opcode(*cbuf, 0xF2 ); // use 'movsd' otherwise + } else { + emit_opcode(*cbuf, 0xF3 ); + } + emit_opcode(*cbuf, 0x0F ); + if( reg_lo+1 == reg_hi && is_load && !UseXmmLoadAndClearUpper ) + emit_opcode(*cbuf, 0x12 ); // use 'movlpd' for load + else + emit_opcode(*cbuf, is_load ? 0x10 : 0x11 ); + encode_RegMem(*cbuf, Matcher::_regEncode[reg_lo], ESP_enc, 0x4, 0, offset, false); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) tty->print("\n\t"); + if( reg_lo+1 == reg_hi ) { // double move? + if( is_load ) tty->print("%s %s,[ESP + #%d]", + UseXmmLoadAndClearUpper ? "MOVSD " : "MOVLPD", + Matcher::regName[reg_lo], offset); + else tty->print("MOVSD [ESP + #%d],%s", + offset, Matcher::regName[reg_lo]); + } else { + if( is_load ) tty->print("MOVSS %s,[ESP + #%d]", + Matcher::regName[reg_lo], offset); + else tty->print("MOVSS [ESP + #%d],%s", + offset, Matcher::regName[reg_lo]); + } +#endif + } + int offset_size = (offset == 0) ? 0 : ((offset <= 127) ? 1 : 4); + return size+5+offset_size; +} + + +static int impl_movx_helper( CodeBuffer *cbuf, bool do_size, int src_lo, int dst_lo, + int src_hi, int dst_hi, int size ) { + if( UseXmmRegToRegMoveAll ) {//Use movaps,movapd to move between xmm registers + if( cbuf ) { + if( (src_lo+1 == src_hi && dst_lo+1 == dst_hi) ) { + emit_opcode(*cbuf, 0x66 ); + } + emit_opcode(*cbuf, 0x0F ); + emit_opcode(*cbuf, 0x28 ); + emit_rm (*cbuf, 0x3, Matcher::_regEncode[dst_lo], Matcher::_regEncode[src_lo] ); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) tty->print("\n\t"); + if( src_lo+1 == src_hi && dst_lo+1 == dst_hi ) { // double move? + tty->print("MOVAPD %s,%s",Matcher::regName[dst_lo],Matcher::regName[src_lo]); + } else { + tty->print("MOVAPS %s,%s",Matcher::regName[dst_lo],Matcher::regName[src_lo]); + } +#endif + } + return size + ((src_lo+1 == src_hi && dst_lo+1 == dst_hi) ? 4 : 3); + } else { + if( cbuf ) { + emit_opcode(*cbuf, (src_lo+1 == src_hi && dst_lo+1 == dst_hi) ? 0xF2 : 0xF3 ); + emit_opcode(*cbuf, 0x0F ); + emit_opcode(*cbuf, 0x10 ); + emit_rm (*cbuf, 0x3, Matcher::_regEncode[dst_lo], Matcher::_regEncode[src_lo] ); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) tty->print("\n\t"); + if( src_lo+1 == src_hi && dst_lo+1 == dst_hi ) { // double move? + tty->print("MOVSD %s,%s",Matcher::regName[dst_lo],Matcher::regName[src_lo]); + } else { + tty->print("MOVSS %s,%s",Matcher::regName[dst_lo],Matcher::regName[src_lo]); + } +#endif + } + return size+4; + } +} + +static int impl_mov_helper( CodeBuffer *cbuf, bool do_size, int src, int dst, int size ) { + if( cbuf ) { + emit_opcode(*cbuf, 0x8B ); + emit_rm (*cbuf, 0x3, Matcher::_regEncode[dst], Matcher::_regEncode[src] ); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) tty->print("\n\t"); + tty->print("MOV %s,%s",Matcher::regName[dst],Matcher::regName[src]); +#endif + } + return size+2; +} + +static int impl_fp_store_helper( CodeBuffer *cbuf, bool do_size, int src_lo, int src_hi, int dst_lo, int dst_hi, int offset, int size ) { + if( src_lo != FPR1L_num ) { // Move value to top of FP stack, if not already there + if( cbuf ) { + emit_opcode( *cbuf, 0xD9 ); // FLD (i.e., push it) + emit_d8( *cbuf, 0xC0-1+Matcher::_regEncode[src_lo] ); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) tty->print("\n\t"); + tty->print("FLD %s",Matcher::regName[src_lo]); +#endif + } + size += 2; + } + + int st_op = (src_lo != FPR1L_num) ? EBX_num /*store & pop*/ : EDX_num /*store no pop*/; + const char *op_str; + int op; + if( src_lo+1 == src_hi && dst_lo+1 == dst_hi ) { // double store? + op_str = (src_lo != FPR1L_num) ? "FSTP_D" : "FST_D "; + op = 0xDD; + } else { // 32-bit store + op_str = (src_lo != FPR1L_num) ? "FSTP_S" : "FST_S "; + op = 0xD9; + assert( !OptoReg::is_valid(src_hi) && !OptoReg::is_valid(dst_hi), "no non-adjacent float-stores" ); + } + + return impl_helper(cbuf,do_size,false,offset,st_op,op,op_str,size); +} + +uint MachSpillCopyNode::implementation( CodeBuffer *cbuf, PhaseRegAlloc *ra_, bool do_size, outputStream* st ) const { + // Get registers to move + OptoReg::Name src_second = ra_->get_reg_second(in(1)); + OptoReg::Name src_first = ra_->get_reg_first(in(1)); + OptoReg::Name dst_second = ra_->get_reg_second(this ); + OptoReg::Name dst_first = ra_->get_reg_first(this ); + + enum RC src_second_rc = rc_class(src_second); + enum RC src_first_rc = rc_class(src_first); + enum RC dst_second_rc = rc_class(dst_second); + enum RC dst_first_rc = rc_class(dst_first); + + assert( OptoReg::is_valid(src_first) && OptoReg::is_valid(dst_first), "must move at least 1 register" ); + + // Generate spill code! + int size = 0; + + if( src_first == dst_first && src_second == dst_second ) + return size; // Self copy, no move + + // -------------------------------------- + // Check for mem-mem move. push/pop to move. + if( src_first_rc == rc_stack && dst_first_rc == rc_stack ) { + if( src_second == dst_first ) { // overlapping stack copy ranges + assert( src_second_rc == rc_stack && dst_second_rc == rc_stack, "we only expect a stk-stk copy here" ); + size = impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_second),ESI_num,0xFF,"PUSH ",size); + size = impl_helper(cbuf,do_size,false,ra_->reg2offset(dst_second),EAX_num,0x8F,"POP ",size); + src_second_rc = dst_second_rc = rc_bad; // flag as already moved the second bits + } + // move low bits + size = impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_first),ESI_num,0xFF,"PUSH ",size); + size = impl_helper(cbuf,do_size,false,ra_->reg2offset(dst_first),EAX_num,0x8F,"POP ",size); + if( src_second_rc == rc_stack && dst_second_rc == rc_stack ) { // mov second bits + size = impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_second),ESI_num,0xFF,"PUSH ",size); + size = impl_helper(cbuf,do_size,false,ra_->reg2offset(dst_second),EAX_num,0x8F,"POP ",size); + } + return size; + } + + // -------------------------------------- + // Check for integer reg-reg copy + if( src_first_rc == rc_int && dst_first_rc == rc_int ) + size = impl_mov_helper(cbuf,do_size,src_first,dst_first,size); + + // Check for integer store + if( src_first_rc == rc_int && dst_first_rc == rc_stack ) + size = impl_helper(cbuf,do_size,false,ra_->reg2offset(dst_first),src_first,0x89,"MOV ",size); + + // Check for integer load + if( dst_first_rc == rc_int && src_first_rc == rc_stack ) + size = impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_first),dst_first,0x8B,"MOV ",size); + + // -------------------------------------- + // Check for float reg-reg copy + if( src_first_rc == rc_float && dst_first_rc == rc_float ) { + assert( (src_second_rc == rc_bad && dst_second_rc == rc_bad) || + (src_first+1 == src_second && dst_first+1 == dst_second), "no non-adjacent float-moves" ); + if( cbuf ) { + + // Note the mucking with the register encode to compensate for the 0/1 + // indexing issue mentioned in a comment in the reg_def sections + // for FPR registers many lines above here. + + if( src_first != FPR1L_num ) { + emit_opcode (*cbuf, 0xD9 ); // FLD ST(i) + emit_d8 (*cbuf, 0xC0+Matcher::_regEncode[src_first]-1 ); + emit_opcode (*cbuf, 0xDD ); // FSTP ST(i) + emit_d8 (*cbuf, 0xD8+Matcher::_regEncode[dst_first] ); + } else { + emit_opcode (*cbuf, 0xDD ); // FST ST(i) + emit_d8 (*cbuf, 0xD0+Matcher::_regEncode[dst_first]-1 ); + } +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); + if( src_first != FPR1L_num ) st->print("FLD %s\n\tFSTP %s",Matcher::regName[src_first],Matcher::regName[dst_first]); + else st->print( "FST %s", Matcher::regName[dst_first]); +#endif + } + return size + ((src_first != FPR1L_num) ? 2+2 : 2); + } + + // Check for float store + if( src_first_rc == rc_float && dst_first_rc == rc_stack ) { + return impl_fp_store_helper(cbuf,do_size,src_first,src_second,dst_first,dst_second,ra_->reg2offset(dst_first),size); + } + + // Check for float load + if( dst_first_rc == rc_float && src_first_rc == rc_stack ) { + int offset = ra_->reg2offset(src_first); + const char *op_str; + int op; + if( src_first+1 == src_second && dst_first+1 == dst_second ) { // double load? + op_str = "FLD_D"; + op = 0xDD; + } else { // 32-bit load + op_str = "FLD_S"; + op = 0xD9; + assert( src_second_rc == rc_bad && dst_second_rc == rc_bad, "no non-adjacent float-loads" ); + } + if( cbuf ) { + emit_opcode (*cbuf, op ); + encode_RegMem(*cbuf, 0x0, ESP_enc, 0x4, 0, offset, false); + emit_opcode (*cbuf, 0xDD ); // FSTP ST(i) + emit_d8 (*cbuf, 0xD8+Matcher::_regEncode[dst_first] ); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); + st->print("%s ST,[ESP + #%d]\n\tFSTP %s",op_str, offset,Matcher::regName[dst_first]); +#endif + } + int offset_size = (offset == 0) ? 0 : ((offset <= 127) ? 1 : 4); + return size + 3+offset_size+2; + } + + // Check for xmm reg-reg copy + if( src_first_rc == rc_xmm && dst_first_rc == rc_xmm ) { + assert( (src_second_rc == rc_bad && dst_second_rc == rc_bad) || + (src_first+1 == src_second && dst_first+1 == dst_second), + "no non-adjacent float-moves" ); + return impl_movx_helper(cbuf,do_size,src_first,dst_first,src_second, dst_second, size); + } + + // Check for xmm store + if( src_first_rc == rc_xmm && dst_first_rc == rc_stack ) { + return impl_x_helper(cbuf,do_size,false,ra_->reg2offset(dst_first),src_first, src_second, size); + } + + // Check for float xmm load + if( dst_first_rc == rc_xmm && src_first_rc == rc_stack ) { + return impl_x_helper(cbuf,do_size,true ,ra_->reg2offset(src_first),dst_first, dst_second, size); + } + + // Copy from float reg to xmm reg + if( dst_first_rc == rc_xmm && src_first_rc == rc_float ) { + // copy to the top of stack from floating point reg + // and use LEA to preserve flags + if( cbuf ) { + emit_opcode(*cbuf,0x8D); // LEA ESP,[ESP-8] + emit_rm(*cbuf, 0x1, ESP_enc, 0x04); + emit_rm(*cbuf, 0x0, 0x04, ESP_enc); + emit_d8(*cbuf,0xF8); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); + st->print("LEA ESP,[ESP-8]"); +#endif + } + size += 4; + + size = impl_fp_store_helper(cbuf,do_size,src_first,src_second,dst_first,dst_second,0,size); + + // Copy from the temp memory to the xmm reg. + size = impl_x_helper(cbuf,do_size,true ,0,dst_first, dst_second, size); + + if( cbuf ) { + emit_opcode(*cbuf,0x8D); // LEA ESP,[ESP+8] + emit_rm(*cbuf, 0x1, ESP_enc, 0x04); + emit_rm(*cbuf, 0x0, 0x04, ESP_enc); + emit_d8(*cbuf,0x08); +#ifndef PRODUCT + } else if( !do_size ) { + if( size != 0 ) st->print("\n\t"); + st->print("LEA ESP,[ESP+8]"); +#endif + } + size += 4; + return size; + } + + assert( size > 0, "missed a case" ); + + // -------------------------------------------------------------------- + // Check for second bits still needing moving. + if( src_second == dst_second ) + return size; // Self copy; no move + assert( src_second_rc != rc_bad && dst_second_rc != rc_bad, "src_second & dst_second cannot be Bad" ); + + // Check for second word int-int move + if( src_second_rc == rc_int && dst_second_rc == rc_int ) + return impl_mov_helper(cbuf,do_size,src_second,dst_second,size); + + // Check for second word integer store + if( src_second_rc == rc_int && dst_second_rc == rc_stack ) + return impl_helper(cbuf,do_size,false,ra_->reg2offset(dst_second),src_second,0x89,"MOV ",size); + + // Check for second word integer load + if( dst_second_rc == rc_int && src_second_rc == rc_stack ) + return impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_second),dst_second,0x8B,"MOV ",size); + + + Unimplemented(); +} + +#ifndef PRODUCT +void MachSpillCopyNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { + implementation( NULL, ra_, false, st ); +} +#endif + +void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + implementation( &cbuf, ra_, false, NULL ); +} + +uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const { + return implementation( NULL, ra_, true, NULL ); +} + +//============================================================================= +#ifndef PRODUCT +void MachNopNode::format( PhaseRegAlloc *, outputStream* st ) const { + st->print("NOP \t# %d bytes pad for loops and calls", _count); +} +#endif + +void MachNopNode::emit(CodeBuffer &cbuf, PhaseRegAlloc * ) const { + MacroAssembler _masm(&cbuf); + __ nop(_count); +} + +uint MachNopNode::size(PhaseRegAlloc *) const { + return _count; +} + + +//============================================================================= +#ifndef PRODUCT +void BoxLockNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { + int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); + int reg = ra_->get_reg_first(this); + st->print("LEA %s,[ESP + #%d]",Matcher::regName[reg],offset); +} +#endif + +void BoxLockNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); + int reg = ra_->get_encode(this); + if( offset >= 128 ) { + emit_opcode(cbuf, 0x8D); // LEA reg,[SP+offset] + emit_rm(cbuf, 0x2, reg, 0x04); + emit_rm(cbuf, 0x0, 0x04, ESP_enc); + emit_d32(cbuf, offset); + } + else { + emit_opcode(cbuf, 0x8D); // LEA reg,[SP+offset] + emit_rm(cbuf, 0x1, reg, 0x04); + emit_rm(cbuf, 0x0, 0x04, ESP_enc); + emit_d8(cbuf, offset); + } +} + +uint BoxLockNode::size(PhaseRegAlloc *ra_) const { + int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); + if( offset >= 128 ) { + return 7; + } + else { + return 4; + } +} + +//============================================================================= + +// emit call stub, compiled java to interpreter +void emit_java_to_interp(CodeBuffer &cbuf ) { + // Stub is fixed up when the corresponding call is converted from calling + // compiled code to calling interpreted code. + // mov rbx,0 + // jmp -1 + + address mark = cbuf.inst_mark(); // get mark within main instrs section + + // Note that the code buffer's inst_mark is always relative to insts. + // That's why we must use the macroassembler to generate a stub. + MacroAssembler _masm(&cbuf); + + address base = + __ start_a_stub(Compile::MAX_stubs_size); + if (base == NULL) return; // CodeBuffer::expand failed + // static stub relocation stores the instruction address of the call + __ relocate(static_stub_Relocation::spec(mark), RELOC_IMM32); + // static stub relocation also tags the methodOop in the code-stream. + __ movoop(rbx, (jobject)NULL); // method is zapped till fixup time + __ jump(RuntimeAddress((address)-1)); + + __ end_a_stub(); + // Update current stubs pointer and restore code_end. +} +// size of call stub, compiled java to interpretor +uint size_java_to_interp() { + return 10; // movl; jmp +} +// relocation entries for call stub, compiled java to interpretor +uint reloc_java_to_interp() { + return 4; // 3 in emit_java_to_interp + 1 in Java_Static_Call +} + +//============================================================================= +#ifndef PRODUCT +void MachUEPNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { + st->print_cr( "CMP EAX,[ECX+4]\t# Inline cache check"); + st->print_cr("\tJNE SharedRuntime::handle_ic_miss_stub"); + st->print_cr("\tNOP"); + st->print_cr("\tNOP"); + if( !OptoBreakpoint ) + st->print_cr("\tNOP"); +} +#endif + +void MachUEPNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + MacroAssembler masm(&cbuf); +#ifdef ASSERT + uint code_size = cbuf.code_size(); +#endif + masm.cmpl(rax, Address(rcx, oopDesc::klass_offset_in_bytes())); + masm.jump_cc(Assembler::notEqual, + RuntimeAddress(SharedRuntime::get_ic_miss_stub())); + /* WARNING these NOPs are critical so that verified entry point is properly + aligned for patching by NativeJump::patch_verified_entry() */ + int nops_cnt = 2; + if( !OptoBreakpoint ) // Leave space for int3 + nops_cnt += 1; + masm.nop(nops_cnt); + + assert(cbuf.code_size() - code_size == size(ra_), "checking code size of inline cache node"); +} + +uint MachUEPNode::size(PhaseRegAlloc *ra_) const { + return OptoBreakpoint ? 11 : 12; +} + + +//============================================================================= +uint size_exception_handler() { + // NativeCall instruction size is the same as NativeJump. + // exception handler starts out as jump and can be patched to + // a call be deoptimization. (4932387) + // Note that this value is also credited (in output.cpp) to + // the size of the code section. + return NativeJump::instruction_size; +} + +// Emit exception handler code. Stuff framesize into a register +// and call a VM stub routine. +int emit_exception_handler(CodeBuffer& cbuf) { + + // Note that the code buffer's inst_mark is always relative to insts. + // That's why we must use the macroassembler to generate a handler. + MacroAssembler _masm(&cbuf); + address base = + __ start_a_stub(size_exception_handler()); + if (base == NULL) return 0; // CodeBuffer::expand failed + int offset = __ offset(); + __ jump(RuntimeAddress(OptoRuntime::exception_blob()->instructions_begin())); + assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); + __ end_a_stub(); + return offset; +} + +uint size_deopt_handler() { + // NativeCall instruction size is the same as NativeJump. + // exception handler starts out as jump and can be patched to + // a call be deoptimization. (4932387) + // Note that this value is also credited (in output.cpp) to + // the size of the code section. + return 5 + NativeJump::instruction_size; // pushl(); jmp; +} + +// Emit deopt handler code. +int emit_deopt_handler(CodeBuffer& cbuf) { + + // Note that the code buffer's inst_mark is always relative to insts. + // That's why we must use the macroassembler to generate a handler. + MacroAssembler _masm(&cbuf); + address base = + __ start_a_stub(size_exception_handler()); + if (base == NULL) return 0; // CodeBuffer::expand failed + int offset = __ offset(); + InternalAddress here(__ pc()); + __ pushptr(here.addr()); + + __ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); + __ end_a_stub(); + return offset; +} + + +static void emit_double_constant(CodeBuffer& cbuf, double x) { + int mark = cbuf.insts()->mark_off(); + MacroAssembler _masm(&cbuf); + address double_address = __ double_constant(x); + cbuf.insts()->set_mark_off(mark); // preserve mark across masm shift + emit_d32_reloc(cbuf, + (int)double_address, + internal_word_Relocation::spec(double_address), + RELOC_DISP32); +} + +static void emit_float_constant(CodeBuffer& cbuf, float x) { + int mark = cbuf.insts()->mark_off(); + MacroAssembler _masm(&cbuf); + address float_address = __ float_constant(x); + cbuf.insts()->set_mark_off(mark); // preserve mark across masm shift + emit_d32_reloc(cbuf, + (int)float_address, + internal_word_Relocation::spec(float_address), + RELOC_DISP32); +} + + +int Matcher::regnum_to_fpu_offset(int regnum) { + return regnum - 32; // The FP registers are in the second chunk +} + +bool is_positive_zero_float(jfloat f) { + return jint_cast(f) == jint_cast(0.0F); +} + +bool is_positive_one_float(jfloat f) { + return jint_cast(f) == jint_cast(1.0F); +} + +bool is_positive_zero_double(jdouble d) { + return jlong_cast(d) == jlong_cast(0.0); +} + +bool is_positive_one_double(jdouble d) { + return jlong_cast(d) == jlong_cast(1.0); +} + +// This is UltraSparc specific, true just means we have fast l2f conversion +const bool Matcher::convL2FSupported(void) { + return true; +} + +// Vector width in bytes +const uint Matcher::vector_width_in_bytes(void) { + return UseSSE >= 2 ? 8 : 0; +} + +// Vector ideal reg +const uint Matcher::vector_ideal_reg(void) { + return Op_RegD; +} + +// Is this branch offset short enough that a short branch can be used? +// +// NOTE: If the platform does not provide any short branch variants, then +// this method should return false for offset 0. +bool Matcher::is_short_branch_offset(int offset) { + return (-128 <= offset && offset <= 127); +} + +const bool Matcher::isSimpleConstant64(jlong value) { + // Will one (StoreL ConL) be cheaper than two (StoreI ConI)?. + return false; +} + +// The ecx parameter to rep stos for the ClearArray node is in dwords. +const bool Matcher::init_array_count_is_in_bytes = false; + +// Threshold size for cleararray. +const int Matcher::init_array_short_size = 8 * BytesPerLong; + +// Should the Matcher clone shifts on addressing modes, expecting them to +// be subsumed into complex addressing expressions or compute them into +// registers? True for Intel but false for most RISCs +const bool Matcher::clone_shift_expressions = true; + +// Is it better to copy float constants, or load them directly from memory? +// Intel can load a float constant from a direct address, requiring no +// extra registers. Most RISCs will have to materialize an address into a +// register first, so they would do better to copy the constant from stack. +const bool Matcher::rematerialize_float_constants = true; + +// If CPU can load and store mis-aligned doubles directly then no fixup is +// needed. Else we split the double into 2 integer pieces and move it +// piece-by-piece. Only happens when passing doubles into C code as the +// Java calling convention forces doubles to be aligned. +const bool Matcher::misaligned_doubles_ok = true; + + +void Matcher::pd_implicit_null_fixup(MachNode *node, uint idx) { + // Get the memory operand from the node + uint numopnds = node->num_opnds(); // Virtual call for number of operands + uint skipped = node->oper_input_base(); // Sum of leaves skipped so far + assert( idx >= skipped, "idx too low in pd_implicit_null_fixup" ); + uint opcnt = 1; // First operand + uint num_edges = node->_opnds[1]->num_edges(); // leaves for first operand + while( idx >= skipped+num_edges ) { + skipped += num_edges; + opcnt++; // Bump operand count + assert( opcnt < numopnds, "Accessing non-existent operand" ); + num_edges = node->_opnds[opcnt]->num_edges(); // leaves for next operand + } + + MachOper *memory = node->_opnds[opcnt]; + MachOper *new_memory = NULL; + switch (memory->opcode()) { + case DIRECT: + case INDOFFSET32X: + // No transformation necessary. + return; + case INDIRECT: + new_memory = new (C) indirect_win95_safeOper( ); + break; + case INDOFFSET8: + new_memory = new (C) indOffset8_win95_safeOper(memory->disp(NULL, NULL, 0)); + break; + case INDOFFSET32: + new_memory = new (C) indOffset32_win95_safeOper(memory->disp(NULL, NULL, 0)); + break; + case INDINDEXOFFSET: + new_memory = new (C) indIndexOffset_win95_safeOper(memory->disp(NULL, NULL, 0)); + break; + case INDINDEXSCALE: + new_memory = new (C) indIndexScale_win95_safeOper(memory->scale()); + break; + case INDINDEXSCALEOFFSET: + new_memory = new (C) indIndexScaleOffset_win95_safeOper(memory->scale(), memory->disp(NULL, NULL, 0)); + break; + case LOAD_LONG_INDIRECT: + case LOAD_LONG_INDOFFSET32: + // Does not use EBP as address register, use { EDX, EBX, EDI, ESI} + return; + default: + assert(false, "unexpected memory operand in pd_implicit_null_fixup()"); + return; + } + node->_opnds[opcnt] = new_memory; +} + +// Advertise here if the CPU requires explicit rounding operations +// to implement the UseStrictFP mode. +const bool Matcher::strict_fp_requires_explicit_rounding = true; + +// Do floats take an entire double register or just half? +const bool Matcher::float_in_double = true; +// Do ints take an entire long register or just half? +const bool Matcher::int_in_long = false; + +// Return whether or not this register is ever used as an argument. This +// function is used on startup to build the trampoline stubs in generateOptoStub. +// Registers not mentioned will be killed by the VM call in the trampoline, and +// arguments in those registers not be available to the callee. +bool Matcher::can_be_java_arg( int reg ) { + if( reg == ECX_num || reg == EDX_num ) return true; + if( (reg == XMM0a_num || reg == XMM1a_num) && UseSSE>=1 ) return true; + if( (reg == XMM0b_num || reg == XMM1b_num) && UseSSE>=2 ) return true; + return false; +} + +bool Matcher::is_spillable_arg( int reg ) { + return can_be_java_arg(reg); +} + +// Register for DIVI projection of divmodI +RegMask Matcher::divI_proj_mask() { + return EAX_REG_mask; +} + +// Register for MODI projection of divmodI +RegMask Matcher::modI_proj_mask() { + return EDX_REG_mask; +} + +// Register for DIVL projection of divmodL +RegMask Matcher::divL_proj_mask() { + ShouldNotReachHere(); + return RegMask(); +} + +// Register for MODL projection of divmodL +RegMask Matcher::modL_proj_mask() { + ShouldNotReachHere(); + return RegMask(); +} + +%} + +//----------ENCODING BLOCK----------------------------------------------------- +// This block specifies the encoding classes used by the compiler to output +// byte streams. Encoding classes generate functions which are called by +// Machine Instruction Nodes in order to generate the bit encoding of the +// instruction. Operands specify their base encoding interface with the +// interface keyword. There are currently supported four interfaces, +// REG_INTER, CONST_INTER, MEMORY_INTER, & COND_INTER. REG_INTER causes an +// operand to generate a function which returns its register number when +// queried. CONST_INTER causes an operand to generate a function which +// returns the value of the constant when queried. MEMORY_INTER causes an +// operand to generate four functions which return the Base Register, the +// Index Register, the Scale Value, and the Offset Value of the operand when +// queried. COND_INTER causes an operand to generate six functions which +// return the encoding code (ie - encoding bits for the instruction) +// associated with each basic boolean condition for a conditional instruction. +// Instructions specify two basic values for encoding. They use the +// ins_encode keyword to specify their encoding class (which must be one of +// the class names specified in the encoding block), and they use the +// opcode keyword to specify, in order, their primary, secondary, and +// tertiary opcode. Only the opcode sections which a particular instruction +// needs for encoding need to be specified. +encode %{ + // Build emit functions for each basic byte or larger field in the intel + // encoding scheme (opcode, rm, sib, immediate), and call them from C++ + // code in the enc_class source block. Emit functions will live in the + // main source block for now. In future, we can generalize this by + // adding a syntax that specifies the sizes of fields in an order, + // so that the adlc can build the emit functions automagically + enc_class OpcP %{ // Emit opcode + emit_opcode(cbuf,$primary); + %} + + enc_class OpcS %{ // Emit opcode + emit_opcode(cbuf,$secondary); + %} + + enc_class Opcode(immI d8 ) %{ // Emit opcode + emit_opcode(cbuf,$d8$$constant); + %} + + enc_class SizePrefix %{ + emit_opcode(cbuf,0x66); + %} + + enc_class RegReg (eRegI dst, eRegI src) %{ // RegReg(Many) + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + %} + + enc_class OpcRegReg (immI opcode, eRegI dst, eRegI src) %{ // OpcRegReg(Many) + emit_opcode(cbuf,$opcode$$constant); + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + %} + + enc_class mov_r32_imm0( eRegI dst ) %{ + emit_opcode( cbuf, 0xB8 + $dst$$reg ); // 0xB8+ rd -- MOV r32 ,imm32 + emit_d32 ( cbuf, 0x0 ); // imm32==0x0 + %} + + enc_class cdq_enc %{ + // Full implementation of Java idiv and irem; checks for + // special case as described in JVM spec., p.243 & p.271. + // + // normal case special case + // + // input : rax,: dividend min_int + // reg: divisor -1 + // + // output: rax,: quotient (= rax, idiv reg) min_int + // rdx: remainder (= rax, irem reg) 0 + // + // Code sequnce: + // + // 81 F8 00 00 00 80 cmp rax,80000000h + // 0F 85 0B 00 00 00 jne normal_case + // 33 D2 xor rdx,edx + // 83 F9 FF cmp rcx,0FFh + // 0F 84 03 00 00 00 je done + // normal_case: + // 99 cdq + // F7 F9 idiv rax,ecx + // done: + // + emit_opcode(cbuf,0x81); emit_d8(cbuf,0xF8); + emit_opcode(cbuf,0x00); emit_d8(cbuf,0x00); + emit_opcode(cbuf,0x00); emit_d8(cbuf,0x80); // cmp rax,80000000h + emit_opcode(cbuf,0x0F); emit_d8(cbuf,0x85); + emit_opcode(cbuf,0x0B); emit_d8(cbuf,0x00); + emit_opcode(cbuf,0x00); emit_d8(cbuf,0x00); // jne normal_case + emit_opcode(cbuf,0x33); emit_d8(cbuf,0xD2); // xor rdx,edx + emit_opcode(cbuf,0x83); emit_d8(cbuf,0xF9); emit_d8(cbuf,0xFF); // cmp rcx,0FFh + emit_opcode(cbuf,0x0F); emit_d8(cbuf,0x84); + emit_opcode(cbuf,0x03); emit_d8(cbuf,0x00); + emit_opcode(cbuf,0x00); emit_d8(cbuf,0x00); // je done + // normal_case: + emit_opcode(cbuf,0x99); // cdq + // idiv (note: must be emitted by the user of this rule) + // normal: + %} + + // Dense encoding for older common ops + enc_class Opc_plus(immI opcode, eRegI reg) %{ + emit_opcode(cbuf, $opcode$$constant + $reg$$reg); + %} + + + // Opcde enc_class for 8/32 bit immediate instructions with sign-extension + enc_class OpcSE (immI imm) %{ // Emit primary opcode and set sign-extend bit + // Check for 8-bit immediate, and set sign extend bit in opcode + if (($imm$$constant >= -128) && ($imm$$constant <= 127)) { + emit_opcode(cbuf, $primary | 0x02); + } + else { // If 32-bit immediate + emit_opcode(cbuf, $primary); + } + %} + + enc_class OpcSErm (eRegI dst, immI imm) %{ // OpcSEr/m + // Emit primary opcode and set sign-extend bit + // Check for 8-bit immediate, and set sign extend bit in opcode + if (($imm$$constant >= -128) && ($imm$$constant <= 127)) { + emit_opcode(cbuf, $primary | 0x02); } + else { // If 32-bit immediate + emit_opcode(cbuf, $primary); + } + // Emit r/m byte with secondary opcode, after primary opcode. + emit_rm(cbuf, 0x3, $secondary, $dst$$reg); + %} + + enc_class Con8or32 (immI imm) %{ // Con8or32(storeImmI), 8 or 32 bits + // Check for 8-bit immediate, and set sign extend bit in opcode + if (($imm$$constant >= -128) && ($imm$$constant <= 127)) { + $$$emit8$imm$$constant; + } + else { // If 32-bit immediate + // Output immediate + $$$emit32$imm$$constant; + } + %} + + enc_class Long_OpcSErm_Lo(eRegL dst, immL imm) %{ + // Emit primary opcode and set sign-extend bit + // Check for 8-bit immediate, and set sign extend bit in opcode + int con = (int)$imm$$constant; // Throw away top bits + emit_opcode(cbuf, ((con >= -128) && (con <= 127)) ? ($primary | 0x02) : $primary); + // Emit r/m byte with secondary opcode, after primary opcode. + emit_rm(cbuf, 0x3, $secondary, $dst$$reg); + if ((con >= -128) && (con <= 127)) emit_d8 (cbuf,con); + else emit_d32(cbuf,con); + %} + + enc_class Long_OpcSErm_Hi(eRegL dst, immL imm) %{ + // Emit primary opcode and set sign-extend bit + // Check for 8-bit immediate, and set sign extend bit in opcode + int con = (int)($imm$$constant >> 32); // Throw away bottom bits + emit_opcode(cbuf, ((con >= -128) && (con <= 127)) ? ($primary | 0x02) : $primary); + // Emit r/m byte with tertiary opcode, after primary opcode. + emit_rm(cbuf, 0x3, $tertiary, HIGH_FROM_LOW($dst$$reg)); + if ((con >= -128) && (con <= 127)) emit_d8 (cbuf,con); + else emit_d32(cbuf,con); + %} + + enc_class Lbl (label labl) %{ // JMP, CALL + Label *l = $labl$$label; + emit_d32(cbuf, l ? (l->loc_pos() - (cbuf.code_size()+4)) : 0); + %} + + enc_class LblShort (label labl) %{ // JMP, CALL + Label *l = $labl$$label; + int disp = l ? (l->loc_pos() - (cbuf.code_size()+1)) : 0; + assert(-128 <= disp && disp <= 127, "Displacement too large for short jmp"); + emit_d8(cbuf, disp); + %} + + enc_class OpcSReg (eRegI dst) %{ // BSWAP + emit_cc(cbuf, $secondary, $dst$$reg ); + %} + + enc_class bswap_long_bytes(eRegL dst) %{ // BSWAP + int destlo = $dst$$reg; + int desthi = HIGH_FROM_LOW(destlo); + // bswap lo + emit_opcode(cbuf, 0x0F); + emit_cc(cbuf, 0xC8, destlo); + // bswap hi + emit_opcode(cbuf, 0x0F); + emit_cc(cbuf, 0xC8, desthi); + // xchg lo and hi + emit_opcode(cbuf, 0x87); + emit_rm(cbuf, 0x3, destlo, desthi); + %} + + enc_class RegOpc (eRegI div) %{ // IDIV, IMOD, JMP indirect, ... + emit_rm(cbuf, 0x3, $secondary, $div$$reg ); + %} + + enc_class Jcc (cmpOp cop, label labl) %{ // JCC + Label *l = $labl$$label; + $$$emit8$primary; + emit_cc(cbuf, $secondary, $cop$$cmpcode); + emit_d32(cbuf, l ? (l->loc_pos() - (cbuf.code_size()+4)) : 0); + %} + + enc_class JccShort (cmpOp cop, label labl) %{ // JCC + Label *l = $labl$$label; + emit_cc(cbuf, $primary, $cop$$cmpcode); + int disp = l ? (l->loc_pos() - (cbuf.code_size()+1)) : 0; + assert(-128 <= disp && disp <= 127, "Displacement too large for short jmp"); + emit_d8(cbuf, disp); + %} + + enc_class enc_cmov(cmpOp cop ) %{ // CMOV + $$$emit8$primary; + emit_cc(cbuf, $secondary, $cop$$cmpcode); + %} + + enc_class enc_cmov_d(cmpOp cop, regD src ) %{ // CMOV + int op = 0xDA00 + $cop$$cmpcode + ($src$$reg-1); + emit_d8(cbuf, op >> 8 ); + emit_d8(cbuf, op & 255); + %} + + // emulate a CMOV with a conditional branch around a MOV + enc_class enc_cmov_branch( cmpOp cop, immI brOffs ) %{ // CMOV + // Invert sense of branch from sense of CMOV + emit_cc( cbuf, 0x70, ($cop$$cmpcode^1) ); + emit_d8( cbuf, $brOffs$$constant ); + %} + + enc_class enc_PartialSubtypeCheck( ) %{ + Register Redi = as_Register(EDI_enc); // result register + Register Reax = as_Register(EAX_enc); // super class + Register Recx = as_Register(ECX_enc); // killed + Register Resi = as_Register(ESI_enc); // sub class + Label hit, miss; + + MacroAssembler _masm(&cbuf); + // Compare super with sub directly, since super is not in its own SSA. + // The compiler used to emit this test, but we fold it in here, + // to allow platform-specific tweaking on sparc. + __ cmpl(Reax, Resi); + __ jcc(Assembler::equal, hit); +#ifndef PRODUCT + __ increment(ExternalAddress((address)&SharedRuntime::_partial_subtype_ctr)); +#endif //PRODUCT + __ movl(Redi,Address(Resi,sizeof(oopDesc) + Klass::secondary_supers_offset_in_bytes())); + __ movl(Recx,Address(Redi,arrayOopDesc::length_offset_in_bytes())); + __ addl(Redi,arrayOopDesc::base_offset_in_bytes(T_OBJECT)); + __ repne_scan(); + __ jcc(Assembler::notEqual, miss); + __ movl(Address(Resi,sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes()),Reax); + __ bind(hit); + if( $primary ) + __ xorl(Redi,Redi); + __ bind(miss); + %} + + enc_class FFree_Float_Stack_All %{ // Free_Float_Stack_All + MacroAssembler masm(&cbuf); + int start = masm.offset(); + if (UseSSE >= 2) { + if (VerifyFPU) { + masm.verify_FPU(0, "must be empty in SSE2+ mode"); + } + } else { + // External c_calling_convention expects the FPU stack to be 'clean'. + // Compiled code leaves it dirty. Do cleanup now. + masm.empty_FPU_stack(); + } + if (sizeof_FFree_Float_Stack_All == -1) { + sizeof_FFree_Float_Stack_All = masm.offset() - start; + } else { + assert(masm.offset() - start == sizeof_FFree_Float_Stack_All, "wrong size"); + } + %} + + enc_class Verify_FPU_For_Leaf %{ + if( VerifyFPU ) { + MacroAssembler masm(&cbuf); + masm.verify_FPU( -3, "Returning from Runtime Leaf call"); + } + %} + + enc_class Java_To_Runtime (method meth) %{ // CALL Java_To_Runtime, Java_To_Runtime_Leaf + // This is the instruction starting address for relocation info. + cbuf.set_inst_mark(); + $$$emit8$primary; + // CALL directly to the runtime + emit_d32_reloc(cbuf, ($meth$$method - (int)(cbuf.code_end()) - 4), + runtime_call_Relocation::spec(), RELOC_IMM32 ); + + if (UseSSE >= 2) { + MacroAssembler _masm(&cbuf); + BasicType rt = tf()->return_type(); + + if ((rt == T_FLOAT || rt == T_DOUBLE) && !return_value_is_used()) { + // A C runtime call where the return value is unused. In SSE2+ + // mode the result needs to be removed from the FPU stack. It's + // likely that this function call could be removed by the + // optimizer if the C function is a pure function. + __ ffree(0); + } else if (rt == T_FLOAT) { + __ leal(rsp, Address(rsp, -4)); + __ fstp_s(Address(rsp, 0)); + __ movflt(xmm0, Address(rsp, 0)); + __ leal(rsp, Address(rsp, 4)); + } else if (rt == T_DOUBLE) { + __ leal(rsp, Address(rsp, -8)); + __ fstp_d(Address(rsp, 0)); + __ movdbl(xmm0, Address(rsp, 0)); + __ leal(rsp, Address(rsp, 8)); + } + } + %} + + + enc_class pre_call_FPU %{ + // If method sets FPU control word restore it here + if( Compile::current()->in_24_bit_fp_mode() ) { + MacroAssembler masm(&cbuf); + masm.fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); + } + %} + + enc_class post_call_FPU %{ + // If method sets FPU control word do it here also + if( Compile::current()->in_24_bit_fp_mode() ) { + MacroAssembler masm(&cbuf); + masm.fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_24())); + } + %} + + enc_class Java_Static_Call (method meth) %{ // JAVA STATIC CALL + // CALL to fixup routine. Fixup routine uses ScopeDesc info to determine + // who we intended to call. + cbuf.set_inst_mark(); + $$$emit8$primary; + if ( !_method ) { + emit_d32_reloc(cbuf, ($meth$$method - (int)(cbuf.code_end()) - 4), + runtime_call_Relocation::spec(), RELOC_IMM32 ); + } else if(_optimized_virtual) { + emit_d32_reloc(cbuf, ($meth$$method - (int)(cbuf.code_end()) - 4), + opt_virtual_call_Relocation::spec(), RELOC_IMM32 ); + } else { + emit_d32_reloc(cbuf, ($meth$$method - (int)(cbuf.code_end()) - 4), + static_call_Relocation::spec(), RELOC_IMM32 ); + } + if( _method ) { // Emit stub for static call + emit_java_to_interp(cbuf); + } + %} + + enc_class Java_Dynamic_Call (method meth) %{ // JAVA DYNAMIC CALL + // !!!!! + // Generate "Mov EAX,0x00", placeholder instruction to load oop-info + // emit_call_dynamic_prologue( cbuf ); + cbuf.set_inst_mark(); + emit_opcode(cbuf, 0xB8 + EAX_enc); // mov EAX,-1 + emit_d32_reloc(cbuf, (int)Universe::non_oop_word(), oop_Relocation::spec_for_immediate(), RELOC_IMM32); + address virtual_call_oop_addr = cbuf.inst_mark(); + // CALL to fixup routine. Fixup routine uses ScopeDesc info to determine + // who we intended to call. + cbuf.set_inst_mark(); + $$$emit8$primary; + emit_d32_reloc(cbuf, ($meth$$method - (int)(cbuf.code_end()) - 4), + virtual_call_Relocation::spec(virtual_call_oop_addr), RELOC_IMM32 ); + %} + + enc_class Java_Compiled_Call (method meth) %{ // JAVA COMPILED CALL + int disp = in_bytes(methodOopDesc::from_compiled_offset()); + assert( -128 <= disp && disp <= 127, "compiled_code_offset isn't small"); + + // CALL *[EAX+in_bytes(methodOopDesc::from_compiled_code_entry_point_offset())] + cbuf.set_inst_mark(); + $$$emit8$primary; + emit_rm(cbuf, 0x01, $secondary, EAX_enc ); // R/M byte + emit_d8(cbuf, disp); // Displacement + + %} + + enc_class Xor_Reg (eRegI dst) %{ + emit_opcode(cbuf, 0x33); + emit_rm(cbuf, 0x3, $dst$$reg, $dst$$reg); + %} + +// Following encoding is no longer used, but may be restored if calling +// convention changes significantly. +// Became: Xor_Reg(EBP), Java_To_Runtime( labl ) +// +// enc_class Java_Interpreter_Call (label labl) %{ // JAVA INTERPRETER CALL +// // int ic_reg = Matcher::inline_cache_reg(); +// // int ic_encode = Matcher::_regEncode[ic_reg]; +// // int imo_reg = Matcher::interpreter_method_oop_reg(); +// // int imo_encode = Matcher::_regEncode[imo_reg]; +// +// // // Interpreter expects method_oop in EBX, currently a callee-saved register, +// // // so we load it immediately before the call +// // emit_opcode(cbuf, 0x8B); // MOV imo_reg,ic_reg # method_oop +// // emit_rm(cbuf, 0x03, imo_encode, ic_encode ); // R/M byte +// +// // xor rbp,ebp +// emit_opcode(cbuf, 0x33); +// emit_rm(cbuf, 0x3, EBP_enc, EBP_enc); +// +// // CALL to interpreter. +// cbuf.set_inst_mark(); +// $$$emit8$primary; +// emit_d32_reloc(cbuf, ($labl$$label - (int)(cbuf.code_end()) - 4), +// runtime_call_Relocation::spec(), RELOC_IMM32 ); +// %} + + enc_class RegOpcImm (eRegI dst, immI8 shift) %{ // SHL, SAR, SHR + $$$emit8$primary; + emit_rm(cbuf, 0x3, $secondary, $dst$$reg); + $$$emit8$shift$$constant; + %} + + enc_class LdImmI (eRegI dst, immI src) %{ // Load Immediate + // Load immediate does not have a zero or sign extended version + // for 8-bit immediates + emit_opcode(cbuf, 0xB8 + $dst$$reg); + $$$emit32$src$$constant; + %} + + enc_class LdImmP (eRegI dst, immI src) %{ // Load Immediate + // Load immediate does not have a zero or sign extended version + // for 8-bit immediates + emit_opcode(cbuf, $primary + $dst$$reg); + $$$emit32$src$$constant; + %} + + enc_class LdImmL_Lo( eRegL dst, immL src) %{ // Load Immediate + // Load immediate does not have a zero or sign extended version + // for 8-bit immediates + int dst_enc = $dst$$reg; + int src_con = $src$$constant & 0x0FFFFFFFFL; + if (src_con == 0) { + // xor dst, dst + emit_opcode(cbuf, 0x33); + emit_rm(cbuf, 0x3, dst_enc, dst_enc); + } else { + emit_opcode(cbuf, $primary + dst_enc); + emit_d32(cbuf, src_con); + } + %} + + enc_class LdImmL_Hi( eRegL dst, immL src) %{ // Load Immediate + // Load immediate does not have a zero or sign extended version + // for 8-bit immediates + int dst_enc = $dst$$reg + 2; + int src_con = ((julong)($src$$constant)) >> 32; + if (src_con == 0) { + // xor dst, dst + emit_opcode(cbuf, 0x33); + emit_rm(cbuf, 0x3, dst_enc, dst_enc); + } else { + emit_opcode(cbuf, $primary + dst_enc); + emit_d32(cbuf, src_con); + } + %} + + + enc_class LdImmD (immD src) %{ // Load Immediate + if( is_positive_zero_double($src$$constant)) { + // FLDZ + emit_opcode(cbuf,0xD9); + emit_opcode(cbuf,0xEE); + } else if( is_positive_one_double($src$$constant)) { + // FLD1 + emit_opcode(cbuf,0xD9); + emit_opcode(cbuf,0xE8); + } else { + emit_opcode(cbuf,0xDD); + emit_rm(cbuf, 0x0, 0x0, 0x5); + emit_double_constant(cbuf, $src$$constant); + } + %} + + + enc_class LdImmF (immF src) %{ // Load Immediate + if( is_positive_zero_float($src$$constant)) { + emit_opcode(cbuf,0xD9); + emit_opcode(cbuf,0xEE); + } else if( is_positive_one_float($src$$constant)) { + emit_opcode(cbuf,0xD9); + emit_opcode(cbuf,0xE8); + } else { + $$$emit8$primary; + // Load immediate does not have a zero or sign extended version + // for 8-bit immediates + // First load to TOS, then move to dst + emit_rm(cbuf, 0x0, 0x0, 0x5); + emit_float_constant(cbuf, $src$$constant); + } + %} + + enc_class LdImmX (regX dst, immXF con) %{ // Load Immediate + emit_rm(cbuf, 0x0, $dst$$reg, 0x5); + emit_float_constant(cbuf, $con$$constant); + %} + + enc_class LdImmXD (regXD dst, immXD con) %{ // Load Immediate + emit_rm(cbuf, 0x0, $dst$$reg, 0x5); + emit_double_constant(cbuf, $con$$constant); + %} + + enc_class load_conXD (regXD dst, immXD con) %{ // Load double constant + // UseXmmLoadAndClearUpper ? movsd(dst, con) : movlpd(dst, con) + emit_opcode(cbuf, UseXmmLoadAndClearUpper ? 0xF2 : 0x66); + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, UseXmmLoadAndClearUpper ? 0x10 : 0x12); + emit_rm(cbuf, 0x0, $dst$$reg, 0x5); + emit_double_constant(cbuf, $con$$constant); + %} + + enc_class Opc_MemImm_F(immF src) %{ + cbuf.set_inst_mark(); + $$$emit8$primary; + emit_rm(cbuf, 0x0, $secondary, 0x5); + emit_float_constant(cbuf, $src$$constant); + %} + + + enc_class MovI2X_reg(regX dst, eRegI src) %{ + emit_opcode(cbuf, 0x66 ); // MOVD dst,src + emit_opcode(cbuf, 0x0F ); + emit_opcode(cbuf, 0x6E ); + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + %} + + enc_class MovX2I_reg(eRegI dst, regX src) %{ + emit_opcode(cbuf, 0x66 ); // MOVD dst,src + emit_opcode(cbuf, 0x0F ); + emit_opcode(cbuf, 0x7E ); + emit_rm(cbuf, 0x3, $src$$reg, $dst$$reg); + %} + + enc_class MovL2XD_reg(regXD dst, eRegL src, regXD tmp) %{ + { // MOVD $dst,$src.lo + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x6E); + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + } + { // MOVD $tmp,$src.hi + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x6E); + emit_rm(cbuf, 0x3, $tmp$$reg, HIGH_FROM_LOW($src$$reg)); + } + { // PUNPCKLDQ $dst,$tmp + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x62); + emit_rm(cbuf, 0x3, $dst$$reg, $tmp$$reg); + } + %} + + enc_class MovXD2L_reg(eRegL dst, regXD src, regXD tmp) %{ + { // MOVD $dst.lo,$src + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x7E); + emit_rm(cbuf, 0x3, $src$$reg, $dst$$reg); + } + { // PSHUFLW $tmp,$src,0x4E (01001110b) + emit_opcode(cbuf,0xF2); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x70); + emit_rm(cbuf, 0x3, $tmp$$reg, $src$$reg); + emit_d8(cbuf, 0x4E); + } + { // MOVD $dst.hi,$tmp + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x7E); + emit_rm(cbuf, 0x3, $tmp$$reg, HIGH_FROM_LOW($dst$$reg)); + } + %} + + + // Encode a reg-reg copy. If it is useless, then empty encoding. + enc_class enc_Copy( eRegI dst, eRegI src ) %{ + encode_Copy( cbuf, $dst$$reg, $src$$reg ); + %} + + enc_class enc_CopyL_Lo( eRegI dst, eRegL src ) %{ + encode_Copy( cbuf, $dst$$reg, $src$$reg ); + %} + + // Encode xmm reg-reg copy. If it is useless, then empty encoding. + enc_class enc_CopyXD( RegXD dst, RegXD src ) %{ + encode_CopyXD( cbuf, $dst$$reg, $src$$reg ); + %} + + enc_class RegReg (eRegI dst, eRegI src) %{ // RegReg(Many) + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + %} + + enc_class RegReg_Lo(eRegL dst, eRegL src) %{ // RegReg(Many) + $$$emit8$primary; + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + %} + + enc_class RegReg_Hi(eRegL dst, eRegL src) %{ // RegReg(Many) + $$$emit8$secondary; + emit_rm(cbuf, 0x3, HIGH_FROM_LOW($dst$$reg), HIGH_FROM_LOW($src$$reg)); + %} + + enc_class RegReg_Lo2(eRegL dst, eRegL src) %{ // RegReg(Many) + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + %} + + enc_class RegReg_Hi2(eRegL dst, eRegL src) %{ // RegReg(Many) + emit_rm(cbuf, 0x3, HIGH_FROM_LOW($dst$$reg), HIGH_FROM_LOW($src$$reg)); + %} + + enc_class RegReg_HiLo( eRegL src, eRegI dst ) %{ + emit_rm(cbuf, 0x3, $dst$$reg, HIGH_FROM_LOW($src$$reg)); + %} + + enc_class Con32 (immI src) %{ // Con32(storeImmI) + // Output immediate + $$$emit32$src$$constant; + %} + + enc_class Con32F_as_bits(immF src) %{ // storeF_imm + // Output Float immediate bits + jfloat jf = $src$$constant; + int jf_as_bits = jint_cast( jf ); + emit_d32(cbuf, jf_as_bits); + %} + + enc_class Con32XF_as_bits(immXF src) %{ // storeX_imm + // Output Float immediate bits + jfloat jf = $src$$constant; + int jf_as_bits = jint_cast( jf ); + emit_d32(cbuf, jf_as_bits); + %} + + enc_class Con16 (immI src) %{ // Con16(storeImmI) + // Output immediate + $$$emit16$src$$constant; + %} + + enc_class Con_d32(immI src) %{ + emit_d32(cbuf,$src$$constant); + %} + + enc_class conmemref (eRegP t1) %{ // Con32(storeImmI) + // Output immediate memory reference + emit_rm(cbuf, 0x00, $t1$$reg, 0x05 ); + emit_d32(cbuf, 0x00); + %} + + enc_class lock_prefix( ) %{ + if( os::is_MP() ) + emit_opcode(cbuf,0xF0); // [Lock] + %} + + // Cmp-xchg long value. + // Note: we need to swap rbx, and rcx before and after the + // cmpxchg8 instruction because the instruction uses + // rcx as the high order word of the new value to store but + // our register encoding uses rbx,. + enc_class enc_cmpxchg8(eSIRegP mem_ptr) %{ + + // XCHG rbx,ecx + emit_opcode(cbuf,0x87); + emit_opcode(cbuf,0xD9); + // [Lock] + if( os::is_MP() ) + emit_opcode(cbuf,0xF0); + // CMPXCHG8 [Eptr] + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0xC7); + emit_rm( cbuf, 0x0, 1, $mem_ptr$$reg ); + // XCHG rbx,ecx + emit_opcode(cbuf,0x87); + emit_opcode(cbuf,0xD9); + %} + + enc_class enc_cmpxchg(eSIRegP mem_ptr) %{ + // [Lock] + if( os::is_MP() ) + emit_opcode(cbuf,0xF0); + + // CMPXCHG [Eptr] + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0xB1); + emit_rm( cbuf, 0x0, 1, $mem_ptr$$reg ); + %} + + enc_class enc_flags_ne_to_boolean( iRegI res ) %{ + int res_encoding = $res$$reg; + + // MOV res,0 + emit_opcode( cbuf, 0xB8 + res_encoding); + emit_d32( cbuf, 0 ); + // JNE,s fail + emit_opcode(cbuf,0x75); + emit_d8(cbuf, 5 ); + // MOV res,1 + emit_opcode( cbuf, 0xB8 + res_encoding); + emit_d32( cbuf, 1 ); + // fail: + %} + + enc_class set_instruction_start( ) %{ + cbuf.set_inst_mark(); // Mark start of opcode for reloc info in mem operand + %} + + enc_class RegMem (eRegI ereg, memory mem) %{ // emit_reg_mem + int reg_encoding = $ereg$$reg; + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); + encode_RegMem(cbuf, reg_encoding, base, index, scale, displace, disp_is_oop); + %} + + enc_class RegMem_Hi(eRegL ereg, memory mem) %{ // emit_reg_mem + int reg_encoding = HIGH_FROM_LOW($ereg$$reg); // Hi register of pair, computed from lo + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp + 4; // Offset is 4 further in memory + assert( !$mem->disp_is_oop(), "Cannot add 4 to oop" ); + encode_RegMem(cbuf, reg_encoding, base, index, scale, displace, false/*disp_is_oop*/); + %} + + enc_class move_long_small_shift( eRegL dst, immI_1_31 cnt ) %{ + int r1, r2; + if( $tertiary == 0xA4 ) { r1 = $dst$$reg; r2 = HIGH_FROM_LOW($dst$$reg); } + else { r2 = $dst$$reg; r1 = HIGH_FROM_LOW($dst$$reg); } + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,$tertiary); + emit_rm(cbuf, 0x3, r1, r2); + emit_d8(cbuf,$cnt$$constant); + emit_d8(cbuf,$primary); + emit_rm(cbuf, 0x3, $secondary, r1); + emit_d8(cbuf,$cnt$$constant); + %} + + enc_class move_long_big_shift_sign( eRegL dst, immI_32_63 cnt ) %{ + emit_opcode( cbuf, 0x8B ); // Move + emit_rm(cbuf, 0x3, $dst$$reg, HIGH_FROM_LOW($dst$$reg)); + emit_d8(cbuf,$primary); + emit_rm(cbuf, 0x3, $secondary, $dst$$reg); + emit_d8(cbuf,$cnt$$constant-32); + emit_d8(cbuf,$primary); + emit_rm(cbuf, 0x3, $secondary, HIGH_FROM_LOW($dst$$reg)); + emit_d8(cbuf,31); + %} + + enc_class move_long_big_shift_clr( eRegL dst, immI_32_63 cnt ) %{ + int r1, r2; + if( $secondary == 0x5 ) { r1 = $dst$$reg; r2 = HIGH_FROM_LOW($dst$$reg); } + else { r2 = $dst$$reg; r1 = HIGH_FROM_LOW($dst$$reg); } + + emit_opcode( cbuf, 0x8B ); // Move r1,r2 + emit_rm(cbuf, 0x3, r1, r2); + if( $cnt$$constant > 32 ) { // Shift, if not by zero + emit_opcode(cbuf,$primary); + emit_rm(cbuf, 0x3, $secondary, r1); + emit_d8(cbuf,$cnt$$constant-32); + } + emit_opcode(cbuf,0x33); // XOR r2,r2 + emit_rm(cbuf, 0x3, r2, r2); + %} + + // Clone of RegMem but accepts an extra parameter to access each + // half of a double in memory; it never needs relocation info. + enc_class Mov_MemD_half_to_Reg (immI opcode, memory mem, immI disp_for_half, eRegI rm_reg) %{ + emit_opcode(cbuf,$opcode$$constant); + int reg_encoding = $rm_reg$$reg; + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp + $disp_for_half$$constant; + bool disp_is_oop = false; + encode_RegMem(cbuf, reg_encoding, base, index, scale, displace, disp_is_oop); + %} + + // !!!!! Special Custom Code used by MemMove, and stack access instructions !!!!! + // + // Clone of RegMem except the RM-byte's reg/opcode field is an ADLC-time constant + // and it never needs relocation information. + // Frequently used to move data between FPU's Stack Top and memory. + enc_class RMopc_Mem_no_oop (immI rm_opcode, memory mem) %{ + int rm_byte_opcode = $rm_opcode$$constant; + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + assert( !$mem->disp_is_oop(), "No oops here because no relo info allowed" ); + encode_RegMem(cbuf, rm_byte_opcode, base, index, scale, displace, false); + %} + + enc_class RMopc_Mem (immI rm_opcode, memory mem) %{ + int rm_byte_opcode = $rm_opcode$$constant; + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); // disp-as-oop when working with static globals + encode_RegMem(cbuf, rm_byte_opcode, base, index, scale, displace, disp_is_oop); + %} + + enc_class RegLea (eRegI dst, eRegI src0, immI src1 ) %{ // emit_reg_lea + int reg_encoding = $dst$$reg; + int base = $src0$$reg; // 0xFFFFFFFF indicates no base + int index = 0x04; // 0x04 indicates no index + int scale = 0x00; // 0x00 indicates no scale + int displace = $src1$$constant; // 0x00 indicates no displacement + bool disp_is_oop = false; + encode_RegMem(cbuf, reg_encoding, base, index, scale, displace, disp_is_oop); + %} + + enc_class min_enc (eRegI dst, eRegI src) %{ // MIN + // Compare dst,src + emit_opcode(cbuf,0x3B); + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + // jmp dst < src around move + emit_opcode(cbuf,0x7C); + emit_d8(cbuf,2); + // move dst,src + emit_opcode(cbuf,0x8B); + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + %} + + enc_class max_enc (eRegI dst, eRegI src) %{ // MAX + // Compare dst,src + emit_opcode(cbuf,0x3B); + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + // jmp dst > src around move + emit_opcode(cbuf,0x7F); + emit_d8(cbuf,2); + // move dst,src + emit_opcode(cbuf,0x8B); + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + %} + + enc_class enc_FP_store(memory mem, regD src) %{ + // If src is FPR1, we can just FST to store it. + // Else we need to FLD it to FPR1, then FSTP to store/pop it. + int reg_encoding = 0x2; // Just store + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); // disp-as-oop when working with static globals + if( $src$$reg != FPR1L_enc ) { + reg_encoding = 0x3; // Store & pop + emit_opcode( cbuf, 0xD9 ); // FLD (i.e., push it) + emit_d8( cbuf, 0xC0-1+$src$$reg ); + } + cbuf.set_inst_mark(); // Mark start of opcode for reloc info in mem operand + emit_opcode(cbuf,$primary); + encode_RegMem(cbuf, reg_encoding, base, index, scale, displace, disp_is_oop); + %} + + enc_class neg_reg(eRegI dst) %{ + // NEG $dst + emit_opcode(cbuf,0xF7); + emit_rm(cbuf, 0x3, 0x03, $dst$$reg ); + %} + + enc_class setLT_reg(eCXRegI dst) %{ + // SETLT $dst + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x9C); + emit_rm( cbuf, 0x3, 0x4, $dst$$reg ); + %} + + enc_class enc_cmpLTP(ncxRegI p, ncxRegI q, ncxRegI y, eCXRegI tmp) %{ // cadd_cmpLT + int tmpReg = $tmp$$reg; + + // SUB $p,$q + emit_opcode(cbuf,0x2B); + emit_rm(cbuf, 0x3, $p$$reg, $q$$reg); + // SBB $tmp,$tmp + emit_opcode(cbuf,0x1B); + emit_rm(cbuf, 0x3, tmpReg, tmpReg); + // AND $tmp,$y + emit_opcode(cbuf,0x23); + emit_rm(cbuf, 0x3, tmpReg, $y$$reg); + // ADD $p,$tmp + emit_opcode(cbuf,0x03); + emit_rm(cbuf, 0x3, $p$$reg, tmpReg); + %} + + enc_class enc_cmpLTP_mem(eRegI p, eRegI q, memory mem, eCXRegI tmp) %{ // cadd_cmpLT + int tmpReg = $tmp$$reg; + + // SUB $p,$q + emit_opcode(cbuf,0x2B); + emit_rm(cbuf, 0x3, $p$$reg, $q$$reg); + // SBB $tmp,$tmp + emit_opcode(cbuf,0x1B); + emit_rm(cbuf, 0x3, tmpReg, tmpReg); + // AND $tmp,$y + cbuf.set_inst_mark(); // Mark start of opcode for reloc info in mem operand + emit_opcode(cbuf,0x23); + int reg_encoding = tmpReg; + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); + encode_RegMem(cbuf, reg_encoding, base, index, scale, displace, disp_is_oop); + // ADD $p,$tmp + emit_opcode(cbuf,0x03); + emit_rm(cbuf, 0x3, $p$$reg, tmpReg); + %} + + enc_class shift_left_long( eRegL dst, eCXRegI shift ) %{ + // TEST shift,32 + emit_opcode(cbuf,0xF7); + emit_rm(cbuf, 0x3, 0, ECX_enc); + emit_d32(cbuf,0x20); + // JEQ,s small + emit_opcode(cbuf, 0x74); + emit_d8(cbuf, 0x04); + // MOV $dst.hi,$dst.lo + emit_opcode( cbuf, 0x8B ); + emit_rm(cbuf, 0x3, HIGH_FROM_LOW($dst$$reg), $dst$$reg ); + // CLR $dst.lo + emit_opcode(cbuf, 0x33); + emit_rm(cbuf, 0x3, $dst$$reg, $dst$$reg); +// small: + // SHLD $dst.hi,$dst.lo,$shift + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0xA5); + emit_rm(cbuf, 0x3, $dst$$reg, HIGH_FROM_LOW($dst$$reg)); + // SHL $dst.lo,$shift" + emit_opcode(cbuf,0xD3); + emit_rm(cbuf, 0x3, 0x4, $dst$$reg ); + %} + + enc_class shift_right_long( eRegL dst, eCXRegI shift ) %{ + // TEST shift,32 + emit_opcode(cbuf,0xF7); + emit_rm(cbuf, 0x3, 0, ECX_enc); + emit_d32(cbuf,0x20); + // JEQ,s small + emit_opcode(cbuf, 0x74); + emit_d8(cbuf, 0x04); + // MOV $dst.lo,$dst.hi + emit_opcode( cbuf, 0x8B ); + emit_rm(cbuf, 0x3, $dst$$reg, HIGH_FROM_LOW($dst$$reg) ); + // CLR $dst.hi + emit_opcode(cbuf, 0x33); + emit_rm(cbuf, 0x3, HIGH_FROM_LOW($dst$$reg), HIGH_FROM_LOW($dst$$reg)); +// small: + // SHRD $dst.lo,$dst.hi,$shift + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0xAD); + emit_rm(cbuf, 0x3, HIGH_FROM_LOW($dst$$reg), $dst$$reg); + // SHR $dst.hi,$shift" + emit_opcode(cbuf,0xD3); + emit_rm(cbuf, 0x3, 0x5, HIGH_FROM_LOW($dst$$reg) ); + %} + + enc_class shift_right_arith_long( eRegL dst, eCXRegI shift ) %{ + // TEST shift,32 + emit_opcode(cbuf,0xF7); + emit_rm(cbuf, 0x3, 0, ECX_enc); + emit_d32(cbuf,0x20); + // JEQ,s small + emit_opcode(cbuf, 0x74); + emit_d8(cbuf, 0x05); + // MOV $dst.lo,$dst.hi + emit_opcode( cbuf, 0x8B ); + emit_rm(cbuf, 0x3, $dst$$reg, HIGH_FROM_LOW($dst$$reg) ); + // SAR $dst.hi,31 + emit_opcode(cbuf, 0xC1); + emit_rm(cbuf, 0x3, 7, HIGH_FROM_LOW($dst$$reg) ); + emit_d8(cbuf, 0x1F ); +// small: + // SHRD $dst.lo,$dst.hi,$shift + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0xAD); + emit_rm(cbuf, 0x3, HIGH_FROM_LOW($dst$$reg), $dst$$reg); + // SAR $dst.hi,$shift" + emit_opcode(cbuf,0xD3); + emit_rm(cbuf, 0x3, 0x7, HIGH_FROM_LOW($dst$$reg) ); + %} + + + // ----------------- Encodings for floating point unit ----------------- + // May leave result in FPU-TOS or FPU reg depending on opcodes + enc_class OpcReg_F (regF src) %{ // FMUL, FDIV + $$$emit8$primary; + emit_rm(cbuf, 0x3, $secondary, $src$$reg ); + %} + + // Pop argument in FPR0 with FSTP ST(0) + enc_class PopFPU() %{ + emit_opcode( cbuf, 0xDD ); + emit_d8( cbuf, 0xD8 ); + %} + + // !!!!! equivalent to Pop_Reg_F + enc_class Pop_Reg_D( regD dst ) %{ + emit_opcode( cbuf, 0xDD ); // FSTP ST(i) + emit_d8( cbuf, 0xD8+$dst$$reg ); + %} + + enc_class Push_Reg_D( regD dst ) %{ + emit_opcode( cbuf, 0xD9 ); + emit_d8( cbuf, 0xC0-1+$dst$$reg ); // FLD ST(i-1) + %} + + enc_class strictfp_bias1( regD dst ) %{ + emit_opcode( cbuf, 0xDB ); // FLD m80real + emit_opcode( cbuf, 0x2D ); + emit_d32( cbuf, (int)StubRoutines::addr_fpu_subnormal_bias1() ); + emit_opcode( cbuf, 0xDE ); // FMULP ST(dst), ST0 + emit_opcode( cbuf, 0xC8+$dst$$reg ); + %} + + enc_class strictfp_bias2( regD dst ) %{ + emit_opcode( cbuf, 0xDB ); // FLD m80real + emit_opcode( cbuf, 0x2D ); + emit_d32( cbuf, (int)StubRoutines::addr_fpu_subnormal_bias2() ); + emit_opcode( cbuf, 0xDE ); // FMULP ST(dst), ST0 + emit_opcode( cbuf, 0xC8+$dst$$reg ); + %} + + // Special case for moving an integer register to a stack slot. + enc_class OpcPRegSS( stackSlotI dst, eRegI src ) %{ // RegSS + store_to_stackslot( cbuf, $primary, $src$$reg, $dst$$disp ); + %} + + // Special case for moving a register to a stack slot. + enc_class RegSS( stackSlotI dst, eRegI src ) %{ // RegSS + // Opcode already emitted + emit_rm( cbuf, 0x02, $src$$reg, ESP_enc ); // R/M byte + emit_rm( cbuf, 0x00, ESP_enc, ESP_enc); // SIB byte + emit_d32(cbuf, $dst$$disp); // Displacement + %} + + // Push the integer in stackSlot 'src' onto FP-stack + enc_class Push_Mem_I( memory src ) %{ // FILD [ESP+src] + store_to_stackslot( cbuf, $primary, $secondary, $src$$disp ); + %} + + // Push the float in stackSlot 'src' onto FP-stack + enc_class Push_Mem_F( memory src ) %{ // FLD_S [ESP+src] + store_to_stackslot( cbuf, 0xD9, 0x00, $src$$disp ); + %} + + // Push the double in stackSlot 'src' onto FP-stack + enc_class Push_Mem_D( memory src ) %{ // FLD_D [ESP+src] + store_to_stackslot( cbuf, 0xDD, 0x00, $src$$disp ); + %} + + // Push FPU's TOS float to a stack-slot, and pop FPU-stack + enc_class Pop_Mem_F( stackSlotF dst ) %{ // FSTP_S [ESP+dst] + store_to_stackslot( cbuf, 0xD9, 0x03, $dst$$disp ); + %} + + // Same as Pop_Mem_F except for opcode + // Push FPU's TOS double to a stack-slot, and pop FPU-stack + enc_class Pop_Mem_D( stackSlotD dst ) %{ // FSTP_D [ESP+dst] + store_to_stackslot( cbuf, 0xDD, 0x03, $dst$$disp ); + %} + + enc_class Pop_Reg_F( regF dst ) %{ + emit_opcode( cbuf, 0xDD ); // FSTP ST(i) + emit_d8( cbuf, 0xD8+$dst$$reg ); + %} + + enc_class Push_Reg_F( regF dst ) %{ + emit_opcode( cbuf, 0xD9 ); // FLD ST(i-1) + emit_d8( cbuf, 0xC0-1+$dst$$reg ); + %} + + // Push FPU's float to a stack-slot, and pop FPU-stack + enc_class Pop_Mem_Reg_F( stackSlotF dst, regF src ) %{ + int pop = 0x02; + if ($src$$reg != FPR1L_enc) { + emit_opcode( cbuf, 0xD9 ); // FLD ST(i-1) + emit_d8( cbuf, 0xC0-1+$src$$reg ); + pop = 0x03; + } + store_to_stackslot( cbuf, 0xD9, pop, $dst$$disp ); // FST

_S [ESP+dst] + %} + + // Push FPU's double to a stack-slot, and pop FPU-stack + enc_class Pop_Mem_Reg_D( stackSlotD dst, regD src ) %{ + int pop = 0x02; + if ($src$$reg != FPR1L_enc) { + emit_opcode( cbuf, 0xD9 ); // FLD ST(i-1) + emit_d8( cbuf, 0xC0-1+$src$$reg ); + pop = 0x03; + } + store_to_stackslot( cbuf, 0xDD, pop, $dst$$disp ); // FST

_D [ESP+dst] + %} + + // Push FPU's double to a FPU-stack-slot, and pop FPU-stack + enc_class Pop_Reg_Reg_D( regD dst, regF src ) %{ + int pop = 0xD0 - 1; // -1 since we skip FLD + if ($src$$reg != FPR1L_enc) { + emit_opcode( cbuf, 0xD9 ); // FLD ST(src-1) + emit_d8( cbuf, 0xC0-1+$src$$reg ); + pop = 0xD8; + } + emit_opcode( cbuf, 0xDD ); + emit_d8( cbuf, pop+$dst$$reg ); // FST

ST(i) + %} + + + enc_class Mul_Add_F( regF dst, regF src, regF src1, regF src2 ) %{ + MacroAssembler masm(&cbuf); + masm.fld_s( $src1$$reg-1); // nothing at TOS, load TOS from src1.reg + masm.fmul( $src2$$reg+0); // value at TOS + masm.fadd( $src$$reg+0); // value at TOS + masm.fstp_d( $dst$$reg+0); // value at TOS, popped off after store + %} + + + enc_class Push_Reg_Mod_D( regD dst, regD src) %{ + // load dst in FPR0 + emit_opcode( cbuf, 0xD9 ); + emit_d8( cbuf, 0xC0-1+$dst$$reg ); + if ($src$$reg != FPR1L_enc) { + // fincstp + emit_opcode (cbuf, 0xD9); + emit_opcode (cbuf, 0xF7); + // swap src with FPR1: + // FXCH FPR1 with src + emit_opcode(cbuf, 0xD9); + emit_d8(cbuf, 0xC8-1+$src$$reg ); + // fdecstp + emit_opcode (cbuf, 0xD9); + emit_opcode (cbuf, 0xF6); + } + %} + + enc_class Push_ModD_encoding( regXD src0, regXD src1) %{ + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,8 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x08); + + emit_opcode (cbuf, 0xF2 ); // MOVSD [ESP], src1 + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src1$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xDD ); // FLD_D [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + + emit_opcode (cbuf, 0xF2 ); // MOVSD [ESP], src0 + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src0$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xDD ); // FLD_D [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + + %} + + enc_class Push_ModX_encoding( regX src0, regX src1) %{ + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,4 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x04); + + emit_opcode (cbuf, 0xF3 ); // MOVSS [ESP], src1 + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src1$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xD9 ); // FLD [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + + emit_opcode (cbuf, 0xF3 ); // MOVSS [ESP], src0 + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src0$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xD9 ); // FLD [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + + %} + + enc_class Push_ResultXD(regXD dst) %{ + store_to_stackslot( cbuf, 0xDD, 0x03, 0 ); //FSTP [ESP] + + // UseXmmLoadAndClearUpper ? movsd dst,[esp] : movlpd dst,[esp] + emit_opcode (cbuf, UseXmmLoadAndClearUpper ? 0xF2 : 0x66); + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, UseXmmLoadAndClearUpper ? 0x10 : 0x12); + encode_RegMem(cbuf, $dst$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0x83); // ADD ESP,8 + emit_opcode(cbuf,0xC4); + emit_d8(cbuf,0x08); + %} + + enc_class Push_ResultX(regX dst, immI d8) %{ + store_to_stackslot( cbuf, 0xD9, 0x03, 0 ); //FSTP_S [ESP] + + emit_opcode (cbuf, 0xF3 ); // MOVSS dst(xmm), [ESP] + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x10 ); + encode_RegMem(cbuf, $dst$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0x83); // ADD ESP,d8 (4 or 8) + emit_opcode(cbuf,0xC4); + emit_d8(cbuf,$d8$$constant); + %} + + enc_class Push_SrcXD(regXD src) %{ + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,8 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x08); + + emit_opcode (cbuf, 0xF2 ); // MOVSD [ESP], src + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xDD ); // FLD_D [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + %} + + enc_class push_stack_temp_qword() %{ + emit_opcode(cbuf,0x83); // SUB ESP,8 + emit_opcode(cbuf,0xEC); + emit_d8 (cbuf,0x08); + %} + + enc_class pop_stack_temp_qword() %{ + emit_opcode(cbuf,0x83); // ADD ESP,8 + emit_opcode(cbuf,0xC4); + emit_d8 (cbuf,0x08); + %} + + enc_class push_xmm_to_fpr1( regXD xmm_src ) %{ + emit_opcode (cbuf, 0xF2 ); // MOVSD [ESP], xmm_src + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $xmm_src$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xDD ); // FLD_D [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + %} + + // Compute X^Y using Intel's fast hardware instructions, if possible. + // Otherwise return a NaN. + enc_class pow_exp_core_encoding %{ + // FPR1 holds Y*ln2(X). Compute FPR1 = 2^(Y*ln2(X)) + emit_opcode(cbuf,0xD9); emit_opcode(cbuf,0xC0); // fdup = fld st(0) Q Q + emit_opcode(cbuf,0xD9); emit_opcode(cbuf,0xFC); // frndint int(Q) Q + emit_opcode(cbuf,0xDC); emit_opcode(cbuf,0xE9); // fsub st(1) -= st(0); int(Q) frac(Q) + emit_opcode(cbuf,0xDB); // FISTP [ESP] frac(Q) + emit_opcode(cbuf,0x1C); + emit_d8(cbuf,0x24); + emit_opcode(cbuf,0xD9); emit_opcode(cbuf,0xF0); // f2xm1 2^frac(Q)-1 + emit_opcode(cbuf,0xD9); emit_opcode(cbuf,0xE8); // fld1 1 2^frac(Q)-1 + emit_opcode(cbuf,0xDE); emit_opcode(cbuf,0xC1); // faddp 2^frac(Q) + emit_opcode(cbuf,0x8B); // mov rax,[esp+0]=int(Q) + encode_RegMem(cbuf, EAX_enc, ESP_enc, 0x4, 0, 0, false); + emit_opcode(cbuf,0xC7); // mov rcx,0xFFFFF800 - overflow mask + emit_rm(cbuf, 0x3, 0x0, ECX_enc); + emit_d32(cbuf,0xFFFFF800); + emit_opcode(cbuf,0x81); // add rax,1023 - the double exponent bias + emit_rm(cbuf, 0x3, 0x0, EAX_enc); + emit_d32(cbuf,1023); + emit_opcode(cbuf,0x8B); // mov rbx,eax + emit_rm(cbuf, 0x3, EBX_enc, EAX_enc); + emit_opcode(cbuf,0xC1); // shl rax,20 - Slide to exponent position + emit_rm(cbuf,0x3,0x4,EAX_enc); + emit_d8(cbuf,20); + emit_opcode(cbuf,0x85); // test rbx,ecx - check for overflow + emit_rm(cbuf, 0x3, EBX_enc, ECX_enc); + emit_opcode(cbuf,0x0F); emit_opcode(cbuf,0x45); // CMOVne rax,ecx - overflow; stuff NAN into EAX + emit_rm(cbuf, 0x3, EAX_enc, ECX_enc); + emit_opcode(cbuf,0x89); // mov [esp+4],eax - Store as part of double word + encode_RegMem(cbuf, EAX_enc, ESP_enc, 0x4, 0, 4, false); + emit_opcode(cbuf,0xC7); // mov [esp+0],0 - [ESP] = (double)(1< 0) { + emit_opcode(cbuf, 0xC1); + emit_rm(cbuf, 0x3, 7, $dst$$reg ); + emit_d8(cbuf, shift_count); + } + %} + + // this version doesn't have add sp, 8 + enc_class convert_long_double2( eRegL src ) %{ + // push $src.hi + emit_opcode(cbuf, 0x50+HIGH_FROM_LOW($src$$reg)); + // push $src.lo + emit_opcode(cbuf, 0x50+$src$$reg ); + // fild 64-bits at [SP] + emit_opcode(cbuf,0xdf); + emit_d8(cbuf, 0x6C); + emit_d8(cbuf, 0x24); + emit_d8(cbuf, 0x00); + %} + + enc_class long_int_multiply( eADXRegL dst, nadxRegI src) %{ + // Basic idea: long = (long)int * (long)int + // IMUL EDX:EAX, src + emit_opcode( cbuf, 0xF7 ); + emit_rm( cbuf, 0x3, 0x5, $src$$reg); + %} + + enc_class long_uint_multiply( eADXRegL dst, nadxRegI src) %{ + // Basic Idea: long = (int & 0xffffffffL) * (int & 0xffffffffL) + // MUL EDX:EAX, src + emit_opcode( cbuf, 0xF7 ); + emit_rm( cbuf, 0x3, 0x4, $src$$reg); + %} + + enc_class long_multiply( eADXRegL dst, eRegL src, eRegI tmp ) %{ + // Basic idea: lo(result) = lo(x_lo * y_lo) + // hi(result) = hi(x_lo * y_lo) + lo(x_hi * y_lo) + lo(x_lo * y_hi) + // MOV $tmp,$src.lo + encode_Copy( cbuf, $tmp$$reg, $src$$reg ); + // IMUL $tmp,EDX + emit_opcode( cbuf, 0x0F ); + emit_opcode( cbuf, 0xAF ); + emit_rm( cbuf, 0x3, $tmp$$reg, HIGH_FROM_LOW($dst$$reg) ); + // MOV EDX,$src.hi + encode_Copy( cbuf, HIGH_FROM_LOW($dst$$reg), HIGH_FROM_LOW($src$$reg) ); + // IMUL EDX,EAX + emit_opcode( cbuf, 0x0F ); + emit_opcode( cbuf, 0xAF ); + emit_rm( cbuf, 0x3, HIGH_FROM_LOW($dst$$reg), $dst$$reg ); + // ADD $tmp,EDX + emit_opcode( cbuf, 0x03 ); + emit_rm( cbuf, 0x3, $tmp$$reg, HIGH_FROM_LOW($dst$$reg) ); + // MUL EDX:EAX,$src.lo + emit_opcode( cbuf, 0xF7 ); + emit_rm( cbuf, 0x3, 0x4, $src$$reg ); + // ADD EDX,ESI + emit_opcode( cbuf, 0x03 ); + emit_rm( cbuf, 0x3, HIGH_FROM_LOW($dst$$reg), $tmp$$reg ); + %} + + enc_class long_multiply_con( eADXRegL dst, immL_127 src, eRegI tmp ) %{ + // Basic idea: lo(result) = lo(src * y_lo) + // hi(result) = hi(src * y_lo) + lo(src * y_hi) + // IMUL $tmp,EDX,$src + emit_opcode( cbuf, 0x6B ); + emit_rm( cbuf, 0x3, $tmp$$reg, HIGH_FROM_LOW($dst$$reg) ); + emit_d8( cbuf, (int)$src$$constant ); + // MOV EDX,$src + emit_opcode(cbuf, 0xB8 + EDX_enc); + emit_d32( cbuf, (int)$src$$constant ); + // MUL EDX:EAX,EDX + emit_opcode( cbuf, 0xF7 ); + emit_rm( cbuf, 0x3, 0x4, EDX_enc ); + // ADD EDX,ESI + emit_opcode( cbuf, 0x03 ); + emit_rm( cbuf, 0x3, EDX_enc, $tmp$$reg ); + %} + + enc_class long_div( eRegL src1, eRegL src2 ) %{ + // PUSH src1.hi + emit_opcode(cbuf, HIGH_FROM_LOW(0x50+$src1$$reg) ); + // PUSH src1.lo + emit_opcode(cbuf, 0x50+$src1$$reg ); + // PUSH src2.hi + emit_opcode(cbuf, HIGH_FROM_LOW(0x50+$src2$$reg) ); + // PUSH src2.lo + emit_opcode(cbuf, 0x50+$src2$$reg ); + // CALL directly to the runtime + cbuf.set_inst_mark(); + emit_opcode(cbuf,0xE8); // Call into runtime + emit_d32_reloc(cbuf, (CAST_FROM_FN_PTR(address, SharedRuntime::ldiv) - cbuf.code_end()) - 4, runtime_call_Relocation::spec(), RELOC_IMM32 ); + // Restore stack + emit_opcode(cbuf, 0x83); // add SP, #framesize + emit_rm(cbuf, 0x3, 0x00, ESP_enc); + emit_d8(cbuf, 4*4); + %} + + enc_class long_mod( eRegL src1, eRegL src2 ) %{ + // PUSH src1.hi + emit_opcode(cbuf, HIGH_FROM_LOW(0x50+$src1$$reg) ); + // PUSH src1.lo + emit_opcode(cbuf, 0x50+$src1$$reg ); + // PUSH src2.hi + emit_opcode(cbuf, HIGH_FROM_LOW(0x50+$src2$$reg) ); + // PUSH src2.lo + emit_opcode(cbuf, 0x50+$src2$$reg ); + // CALL directly to the runtime + cbuf.set_inst_mark(); + emit_opcode(cbuf,0xE8); // Call into runtime + emit_d32_reloc(cbuf, (CAST_FROM_FN_PTR(address, SharedRuntime::lrem ) - cbuf.code_end()) - 4, runtime_call_Relocation::spec(), RELOC_IMM32 ); + // Restore stack + emit_opcode(cbuf, 0x83); // add SP, #framesize + emit_rm(cbuf, 0x3, 0x00, ESP_enc); + emit_d8(cbuf, 4*4); + %} + + enc_class long_cmp_flags0( eRegL src, eRegI tmp ) %{ + // MOV $tmp,$src.lo + emit_opcode(cbuf, 0x8B); + emit_rm(cbuf, 0x3, $tmp$$reg, $src$$reg); + // OR $tmp,$src.hi + emit_opcode(cbuf, 0x0B); + emit_rm(cbuf, 0x3, $tmp$$reg, HIGH_FROM_LOW($src$$reg)); + %} + + enc_class long_cmp_flags1( eRegL src1, eRegL src2 ) %{ + // CMP $src1.lo,$src2.lo + emit_opcode( cbuf, 0x3B ); + emit_rm(cbuf, 0x3, $src1$$reg, $src2$$reg ); + // JNE,s skip + emit_cc(cbuf, 0x70, 0x5); + emit_d8(cbuf,2); + // CMP $src1.hi,$src2.hi + emit_opcode( cbuf, 0x3B ); + emit_rm(cbuf, 0x3, HIGH_FROM_LOW($src1$$reg), HIGH_FROM_LOW($src2$$reg) ); + %} + + enc_class long_cmp_flags2( eRegL src1, eRegL src2, eRegI tmp ) %{ + // CMP $src1.lo,$src2.lo\t! Long compare; set flags for low bits + emit_opcode( cbuf, 0x3B ); + emit_rm(cbuf, 0x3, $src1$$reg, $src2$$reg ); + // MOV $tmp,$src1.hi + emit_opcode( cbuf, 0x8B ); + emit_rm(cbuf, 0x3, $tmp$$reg, HIGH_FROM_LOW($src1$$reg) ); + // SBB $tmp,$src2.hi\t! Compute flags for long compare + emit_opcode( cbuf, 0x1B ); + emit_rm(cbuf, 0x3, $tmp$$reg, HIGH_FROM_LOW($src2$$reg) ); + %} + + enc_class long_cmp_flags3( eRegL src, eRegI tmp ) %{ + // XOR $tmp,$tmp + emit_opcode(cbuf,0x33); // XOR + emit_rm(cbuf,0x3, $tmp$$reg, $tmp$$reg); + // CMP $tmp,$src.lo + emit_opcode( cbuf, 0x3B ); + emit_rm(cbuf, 0x3, $tmp$$reg, $src$$reg ); + // SBB $tmp,$src.hi + emit_opcode( cbuf, 0x1B ); + emit_rm(cbuf, 0x3, $tmp$$reg, HIGH_FROM_LOW($src$$reg) ); + %} + + // Sniff, sniff... smells like Gnu Superoptimizer + enc_class neg_long( eRegL dst ) %{ + emit_opcode(cbuf,0xF7); // NEG hi + emit_rm (cbuf,0x3, 0x3, HIGH_FROM_LOW($dst$$reg)); + emit_opcode(cbuf,0xF7); // NEG lo + emit_rm (cbuf,0x3, 0x3, $dst$$reg ); + emit_opcode(cbuf,0x83); // SBB hi,0 + emit_rm (cbuf,0x3, 0x3, HIGH_FROM_LOW($dst$$reg)); + emit_d8 (cbuf,0 ); + %} + + enc_class movq_ld(regXD dst, memory mem) %{ + MacroAssembler _masm(&cbuf); + Address madr = Address::make_raw($mem$$base, $mem$$index, $mem$$scale, $mem$$disp); + __ movq(as_XMMRegister($dst$$reg), madr); + %} + + enc_class movq_st(memory mem, regXD src) %{ + MacroAssembler _masm(&cbuf); + Address madr = Address::make_raw($mem$$base, $mem$$index, $mem$$scale, $mem$$disp); + __ movq(madr, as_XMMRegister($src$$reg)); + %} + + enc_class pshufd_8x8(regX dst, regX src) %{ + MacroAssembler _masm(&cbuf); + + encode_CopyXD(cbuf, $dst$$reg, $src$$reg); + __ punpcklbw(as_XMMRegister($dst$$reg), as_XMMRegister($dst$$reg)); + __ pshuflw(as_XMMRegister($dst$$reg), as_XMMRegister($dst$$reg), 0x00); + %} + + enc_class pshufd_4x16(regX dst, regX src) %{ + MacroAssembler _masm(&cbuf); + + __ pshuflw(as_XMMRegister($dst$$reg), as_XMMRegister($src$$reg), 0x00); + %} + + enc_class pshufd(regXD dst, regXD src, int mode) %{ + MacroAssembler _masm(&cbuf); + + __ pshufd(as_XMMRegister($dst$$reg), as_XMMRegister($src$$reg), $mode); + %} + + enc_class pxor(regXD dst, regXD src) %{ + MacroAssembler _masm(&cbuf); + + __ pxor(as_XMMRegister($dst$$reg), as_XMMRegister($src$$reg)); + %} + + enc_class mov_i2x(regXD dst, eRegI src) %{ + MacroAssembler _masm(&cbuf); + + __ movd(as_XMMRegister($dst$$reg), as_Register($src$$reg)); + %} + + + // Because the transitions from emitted code to the runtime + // monitorenter/exit helper stubs are so slow it's critical that + // we inline both the stack-locking fast-path and the inflated fast path. + // + // See also: cmpFastLock and cmpFastUnlock. + // + // What follows is a specialized inline transliteration of the code + // in slow_enter() and slow_exit(). If we're concerned about I$ bloat + // another option would be to emit TrySlowEnter and TrySlowExit methods + // at startup-time. These methods would accept arguments as + // (rax,=Obj, rbx=Self, rcx=box, rdx=Scratch) and return success-failure + // indications in the icc.ZFlag. Fast_Lock and Fast_Unlock would simply + // marshal the arguments and emit calls to TrySlowEnter and TrySlowExit. + // In practice, however, the # of lock sites is bounded and is usually small. + // Besides the call overhead, TrySlowEnter and TrySlowExit might suffer + // if the processor uses simple bimodal branch predictors keyed by EIP + // Since the helper routines would be called from multiple synchronization + // sites. + // + // An even better approach would be write "MonitorEnter()" and "MonitorExit()" + // in java - using j.u.c and unsafe - and just bind the lock and unlock sites + // to those specialized methods. That'd give us a mostly platform-independent + // implementation that the JITs could optimize and inline at their pleasure. + // Done correctly, the only time we'd need to cross to native could would be + // to park() or unpark() threads. We'd also need a few more unsafe operators + // to (a) prevent compiler-JIT reordering of non-volatile accesses, and + // (b) explicit barriers or fence operations. + // + // TODO: + // + // * Arrange for C2 to pass "Self" into Fast_Lock and Fast_Unlock in one of the registers (scr). + // This avoids manifesting the Self pointer in the Fast_Lock and Fast_Unlock terminals. + // Given TLAB allocation, Self is usually manifested in a register, so passing it into + // the lock operators would typically be faster than reifying Self. + // + // * Ideally I'd define the primitives as: + // fast_lock (nax Obj, nax box, EAX tmp, nax scr) where box, tmp and scr are KILLED. + // fast_unlock (nax Obj, EAX box, nax tmp) where box and tmp are KILLED + // Unfortunately ADLC bugs prevent us from expressing the ideal form. + // Instead, we're stuck with a rather awkward and brittle register assignments below. + // Furthermore the register assignments are overconstrained, possibly resulting in + // sub-optimal code near the synchronization site. + // + // * Eliminate the sp-proximity tests and just use "== Self" tests instead. + // Alternately, use a better sp-proximity test. + // + // * Currently ObjectMonitor._Owner can hold either an sp value or a (THREAD *) value. + // Either one is sufficient to uniquely identify a thread. + // TODO: eliminate use of sp in _owner and use get_thread(tr) instead. + // + // * Intrinsify notify() and notifyAll() for the common cases where the + // object is locked by the calling thread but the waitlist is empty. + // avoid the expensive JNI call to JVM_Notify() and JVM_NotifyAll(). + // + // * use jccb and jmpb instead of jcc and jmp to improve code density. + // But beware of excessive branch density on AMD Opterons. + // + // * Both Fast_Lock and Fast_Unlock set the ICC.ZF to indicate success + // or failure of the fast-path. If the fast-path fails then we pass + // control to the slow-path, typically in C. In Fast_Lock and + // Fast_Unlock we often branch to DONE_LABEL, just to find that C2 + // will emit a conditional branch immediately after the node. + // So we have branches to branches and lots of ICC.ZF games. + // Instead, it might be better to have C2 pass a "FailureLabel" + // into Fast_Lock and Fast_Unlock. In the case of success, control + // will drop through the node. ICC.ZF is undefined at exit. + // In the case of failure, the node will branch directly to the + // FailureLabel + + + // obj: object to lock + // box: on-stack box address (displaced header location) - KILLED + // rax,: tmp -- KILLED + // scr: tmp -- KILLED + enc_class Fast_Lock( eRegP obj, eRegP box, eAXRegI tmp, eRegP scr ) %{ + + Register objReg = as_Register($obj$$reg); + Register boxReg = as_Register($box$$reg); + Register tmpReg = as_Register($tmp$$reg); + Register scrReg = as_Register($scr$$reg); + + // Ensure the register assignents are disjoint + guarantee (objReg != boxReg, "") ; + guarantee (objReg != tmpReg, "") ; + guarantee (objReg != scrReg, "") ; + guarantee (boxReg != tmpReg, "") ; + guarantee (boxReg != scrReg, "") ; + guarantee (tmpReg == as_Register(EAX_enc), "") ; + + MacroAssembler masm(&cbuf); + + if (_counters != NULL) { + masm.atomic_incl(ExternalAddress((address) _counters->total_entry_count_addr())); + } + if (EmitSync & 1) { + // set box->dhw = unused_mark (3) + // Force all sync thru slow-path: slow_enter() and slow_exit() + masm.movl (Address(boxReg, 0), intptr_t(markOopDesc::unused_mark())) ; + masm.cmpl (rsp, 0) ; + } else + if (EmitSync & 2) { + Label DONE_LABEL ; + if (UseBiasedLocking) { + // Note: tmpReg maps to the swap_reg argument and scrReg to the tmp_reg argument. + masm.biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, DONE_LABEL, NULL, _counters); + } + + masm.movl (tmpReg, Address(objReg, 0)) ; // fetch markword + masm.orl (tmpReg, 0x1); + masm.movl (Address(boxReg, 0), tmpReg); // Anticipate successful CAS + if (os::is_MP()) { masm.lock(); } + masm.cmpxchg(boxReg, Address(objReg, 0)); // Updates tmpReg + masm.jcc(Assembler::equal, DONE_LABEL); + // Recursive locking + masm.subl(tmpReg, rsp); + masm.andl(tmpReg, 0xFFFFF003 ); + masm.movl(Address(boxReg, 0), tmpReg); + masm.bind(DONE_LABEL) ; + } else { + // Possible cases that we'll encounter in fast_lock + // ------------------------------------------------ + // * Inflated + // -- unlocked + // -- Locked + // = by self + // = by other + // * biased + // -- by Self + // -- by other + // * neutral + // * stack-locked + // -- by self + // = sp-proximity test hits + // = sp-proximity test generates false-negative + // -- by other + // + + Label IsInflated, DONE_LABEL, PopDone ; + + // TODO: optimize away redundant LDs of obj->mark and improve the markword triage + // order to reduce the number of conditional branches in the most common cases. + // Beware -- there's a subtle invariant that fetch of the markword + // at [FETCH], below, will never observe a biased encoding (*101b). + // If this invariant is not held we risk exclusion (safety) failure. + if (UseBiasedLocking) { + masm.biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, DONE_LABEL, NULL, _counters); + } + + masm.movl (tmpReg, Address(objReg, 0)) ; // [FETCH] + masm.testl (tmpReg, 0x02) ; // Inflated v (Stack-locked or neutral) + masm.jccb (Assembler::notZero, IsInflated) ; + + // Attempt stack-locking ... + masm.orl (tmpReg, 0x1); + masm.movl (Address(boxReg, 0), tmpReg); // Anticipate successful CAS + if (os::is_MP()) { masm.lock(); } + masm.cmpxchg(boxReg, Address(objReg, 0)); // Updates tmpReg + if (_counters != NULL) { + masm.cond_inc32(Assembler::equal, + ExternalAddress((address)_counters->fast_path_entry_count_addr())); + } + masm.jccb (Assembler::equal, DONE_LABEL); + + // Recursive locking + masm.subl(tmpReg, rsp); + masm.andl(tmpReg, 0xFFFFF003 ); + masm.movl(Address(boxReg, 0), tmpReg); + if (_counters != NULL) { + masm.cond_inc32(Assembler::equal, + ExternalAddress((address)_counters->fast_path_entry_count_addr())); + } + masm.jmp (DONE_LABEL) ; + + masm.bind (IsInflated) ; + + // The object is inflated. + // + // TODO-FIXME: eliminate the ugly use of manifest constants: + // Use markOopDesc::monitor_value instead of "2". + // use markOop::unused_mark() instead of "3". + // The tmpReg value is an objectMonitor reference ORed with + // markOopDesc::monitor_value (2). We can either convert tmpReg to an + // objectmonitor pointer by masking off the "2" bit or we can just + // use tmpReg as an objectmonitor pointer but bias the objectmonitor + // field offsets with "-2" to compensate for and annul the low-order tag bit. + // + // I use the latter as it avoids AGI stalls. + // As such, we write "mov r, [tmpReg+OFFSETOF(Owner)-2]" + // instead of "mov r, [tmpReg+OFFSETOF(Owner)]". + // + #define OFFSET_SKEWED(f) ((ObjectMonitor::f ## _offset_in_bytes())-2) + + // boxReg refers to the on-stack BasicLock in the current frame. + // We'd like to write: + // set box->_displaced_header = markOop::unused_mark(). Any non-0 value suffices. + // This is convenient but results a ST-before-CAS penalty. The following CAS suffers + // additional latency as we have another ST in the store buffer that must drain. + + if (EmitSync & 8192) { + masm.movl (Address(boxReg, 0), 3) ; // results in ST-before-CAS penalty + masm.get_thread (scrReg) ; + masm.movl (boxReg, tmpReg); // consider: LEA box, [tmp-2] + masm.movl (tmpReg, 0); // consider: xor vs mov + if (os::is_MP()) { masm.lock(); } + masm.cmpxchg (scrReg, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; + } else + if ((EmitSync & 128) == 0) { // avoid ST-before-CAS + masm.movl (scrReg, boxReg) ; + masm.movl (boxReg, tmpReg); // consider: LEA box, [tmp-2] + + // Using a prefetchw helps avoid later RTS->RTO upgrades and cache probes + if ((EmitSync & 2048) && VM_Version::supports_3dnow() && os::is_MP()) { + // prefetchw [eax + Offset(_owner)-2] + masm.emit_raw (0x0F) ; + masm.emit_raw (0x0D) ; + masm.emit_raw (0x48) ; + masm.emit_raw (ObjectMonitor::owner_offset_in_bytes()-2) ; + } + + if ((EmitSync & 64) == 0) { + // Optimistic form: consider XORL tmpReg,tmpReg + masm.movl (tmpReg, 0 ) ; + } else { + // Can suffer RTS->RTO upgrades on shared or cold $ lines + // Test-And-CAS instead of CAS + masm.movl (tmpReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; // rax, = m->_owner + masm.testl (tmpReg, tmpReg) ; // Locked ? + masm.jccb (Assembler::notZero, DONE_LABEL) ; + } + + // Appears unlocked - try to swing _owner from null to non-null. + // Ideally, I'd manifest "Self" with get_thread and then attempt + // to CAS the register containing Self into m->Owner. + // But we don't have enough registers, so instead we can either try to CAS + // rsp or the address of the box (in scr) into &m->owner. If the CAS succeeds + // we later store "Self" into m->Owner. Transiently storing a stack address + // (rsp or the address of the box) into m->owner is harmless. + // Invariant: tmpReg == 0. tmpReg is EAX which is the implicit cmpxchg comparand. + if (os::is_MP()) { masm.lock(); } + masm.cmpxchg (scrReg, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; + masm.movl (Address(scrReg, 0), 3) ; // box->_displaced_header = 3 + masm.jccb (Assembler::notZero, DONE_LABEL) ; + masm.get_thread (scrReg) ; // beware: clobbers ICCs + masm.movl (Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2), scrReg) ; + masm.xorl (boxReg, boxReg) ; // set icc.ZFlag = 1 to indicate success + + // If the CAS fails we can either retry or pass control to the slow-path. + // We use the latter tactic. + // Pass the CAS result in the icc.ZFlag into DONE_LABEL + // If the CAS was successful ... + // Self has acquired the lock + // Invariant: m->_recursions should already be 0, so we don't need to explicitly set it. + // Intentional fall-through into DONE_LABEL ... + } else { + masm.movl (Address(boxReg, 0), 3) ; // results in ST-before-CAS penalty + masm.movl (boxReg, tmpReg) ; + + // Using a prefetchw helps avoid later RTS->RTO upgrades and cache probes + if ((EmitSync & 2048) && VM_Version::supports_3dnow() && os::is_MP()) { + // prefetchw [eax + Offset(_owner)-2] + masm.emit_raw (0x0F) ; + masm.emit_raw (0x0D) ; + masm.emit_raw (0x48) ; + masm.emit_raw (ObjectMonitor::owner_offset_in_bytes()-2) ; + } + + if ((EmitSync & 64) == 0) { + // Optimistic form + masm.xorl (tmpReg, tmpReg) ; + } else { + // Can suffer RTS->RTO upgrades on shared or cold $ lines + masm.movl (tmpReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; // rax, = m->_owner + masm.testl (tmpReg, tmpReg) ; // Locked ? + masm.jccb (Assembler::notZero, DONE_LABEL) ; + } + + // Appears unlocked - try to swing _owner from null to non-null. + // Use either "Self" (in scr) or rsp as thread identity in _owner. + // Invariant: tmpReg == 0. tmpReg is EAX which is the implicit cmpxchg comparand. + masm.get_thread (scrReg) ; + if (os::is_MP()) { masm.lock(); } + masm.cmpxchg (scrReg, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; + + // If the CAS fails we can either retry or pass control to the slow-path. + // We use the latter tactic. + // Pass the CAS result in the icc.ZFlag into DONE_LABEL + // If the CAS was successful ... + // Self has acquired the lock + // Invariant: m->_recursions should already be 0, so we don't need to explicitly set it. + // Intentional fall-through into DONE_LABEL ... + } + + // DONE_LABEL is a hot target - we'd really like to place it at the + // start of cache line by padding with NOPs. + // See the AMD and Intel software optimization manuals for the + // most efficient "long" NOP encodings. + // Unfortunately none of our alignment mechanisms suffice. + masm.bind(DONE_LABEL); + + // Avoid branch-to-branch on AMD processors + // This appears to be superstition. + if (EmitSync & 32) masm.nop() ; + + + // At DONE_LABEL the icc ZFlag is set as follows ... + // Fast_Unlock uses the same protocol. + // ZFlag == 1 -> Success + // ZFlag == 0 -> Failure - force control through the slow-path + } + %} + + // obj: object to unlock + // box: box address (displaced header location), killed. Must be EAX. + // rbx,: killed tmp; cannot be obj nor box. + // + // Some commentary on balanced locking: + // + // Fast_Lock and Fast_Unlock are emitted only for provably balanced lock sites. + // Methods that don't have provably balanced locking are forced to run in the + // interpreter - such methods won't be compiled to use fast_lock and fast_unlock. + // The interpreter provides two properties: + // I1: At return-time the interpreter automatically and quietly unlocks any + // objects acquired the current activation (frame). Recall that the + // interpreter maintains an on-stack list of locks currently held by + // a frame. + // I2: If a method attempts to unlock an object that is not held by the + // the frame the interpreter throws IMSX. + // + // Lets say A(), which has provably balanced locking, acquires O and then calls B(). + // B() doesn't have provably balanced locking so it runs in the interpreter. + // Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O + // is still locked by A(). + // + // The only other source of unbalanced locking would be JNI. The "Java Native Interface: + // Programmer's Guide and Specification" claims that an object locked by jni_monitorenter + // should not be unlocked by "normal" java-level locking and vice-versa. The specification + // doesn't specify what will occur if a program engages in such mixed-mode locking, however. + + enc_class Fast_Unlock( nabxRegP obj, eAXRegP box, eRegP tmp) %{ + + Register objReg = as_Register($obj$$reg); + Register boxReg = as_Register($box$$reg); + Register tmpReg = as_Register($tmp$$reg); + + guarantee (objReg != boxReg, "") ; + guarantee (objReg != tmpReg, "") ; + guarantee (boxReg != tmpReg, "") ; + guarantee (boxReg == as_Register(EAX_enc), "") ; + MacroAssembler masm(&cbuf); + + if (EmitSync & 4) { + // Disable - inhibit all inlining. Force control through the slow-path + masm.cmpl (rsp, 0) ; + } else + if (EmitSync & 8) { + Label DONE_LABEL ; + if (UseBiasedLocking) { + masm.biased_locking_exit(objReg, tmpReg, DONE_LABEL); + } + // classic stack-locking code ... + masm.movl (tmpReg, Address(boxReg, 0)) ; + masm.testl (tmpReg, tmpReg) ; + masm.jcc (Assembler::zero, DONE_LABEL) ; + if (os::is_MP()) { masm.lock(); } + masm.cmpxchg(tmpReg, Address(objReg, 0)); // Uses EAX which is box + masm.bind(DONE_LABEL); + } else { + Label DONE_LABEL, Stacked, CheckSucc, Inflated ; + + // Critically, the biased locking test must have precedence over + // and appear before the (box->dhw == 0) recursive stack-lock test. + if (UseBiasedLocking) { + masm.biased_locking_exit(objReg, tmpReg, DONE_LABEL); + } + + masm.cmpl (Address(boxReg, 0), 0) ; // Examine the displaced header + masm.movl (tmpReg, Address(objReg, 0)) ; // Examine the object's markword + masm.jccb (Assembler::zero, DONE_LABEL) ; // 0 indicates recursive stack-lock + + masm.testl (tmpReg, 0x02) ; // Inflated? + masm.jccb (Assembler::zero, Stacked) ; + + masm.bind (Inflated) ; + // It's inflated. + // Despite our balanced locking property we still check that m->_owner == Self + // as java routines or native JNI code called by this thread might + // have released the lock. + // Refer to the comments in synchronizer.cpp for how we might encode extra + // state in _succ so we can avoid fetching EntryList|cxq. + // + // I'd like to add more cases in fast_lock() and fast_unlock() -- + // such as recursive enter and exit -- but we have to be wary of + // I$ bloat, T$ effects and BP$ effects. + // + // If there's no contention try a 1-0 exit. That is, exit without + // a costly MEMBAR or CAS. See synchronizer.cpp for details on how + // we detect and recover from the race that the 1-0 exit admits. + // + // Conceptually Fast_Unlock() must execute a STST|LDST "release" barrier + // before it STs null into _owner, releasing the lock. Updates + // to data protected by the critical section must be visible before + // we drop the lock (and thus before any other thread could acquire + // the lock and observe the fields protected by the lock). + // IA32's memory-model is SPO, so STs are ordered with respect to + // each other and there's no need for an explicit barrier (fence). + // See also http://gee.cs.oswego.edu/dl/jmm/cookbook.html. + + masm.get_thread (boxReg) ; + if ((EmitSync & 4096) && VM_Version::supports_3dnow() && os::is_MP()) { + // prefetchw [ebx + Offset(_owner)-2] + masm.emit_raw (0x0F) ; + masm.emit_raw (0x0D) ; + masm.emit_raw (0x4B) ; + masm.emit_raw (ObjectMonitor::owner_offset_in_bytes()-2) ; + } + + // Note that we could employ various encoding schemes to reduce + // the number of loads below (currently 4) to just 2 or 3. + // Refer to the comments in synchronizer.cpp. + // In practice the chain of fetches doesn't seem to impact performance, however. + if ((EmitSync & 65536) == 0 && (EmitSync & 256)) { + // Attempt to reduce branch density - AMD's branch predictor. + masm.xorl (boxReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; + masm.orl (boxReg, Address (tmpReg, ObjectMonitor::recursions_offset_in_bytes()-2)) ; + masm.orl (boxReg, Address (tmpReg, ObjectMonitor::EntryList_offset_in_bytes()-2)) ; + masm.orl (boxReg, Address (tmpReg, ObjectMonitor::cxq_offset_in_bytes()-2)) ; + masm.jccb (Assembler::notZero, DONE_LABEL) ; + masm.movl (Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), 0) ; + masm.jmpb (DONE_LABEL) ; + } else { + masm.xorl (boxReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; + masm.orl (boxReg, Address (tmpReg, ObjectMonitor::recursions_offset_in_bytes()-2)) ; + masm.jccb (Assembler::notZero, DONE_LABEL) ; + masm.movl (boxReg, Address (tmpReg, ObjectMonitor::EntryList_offset_in_bytes()-2)) ; + masm.orl (boxReg, Address (tmpReg, ObjectMonitor::cxq_offset_in_bytes()-2)) ; + masm.jccb (Assembler::notZero, CheckSucc) ; + masm.movl (Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), 0) ; + masm.jmpb (DONE_LABEL) ; + } + + // The Following code fragment (EmitSync & 65536) improves the performance of + // contended applications and contended synchronization microbenchmarks. + // Unfortunately the emission of the code - even though not executed - causes regressions + // in scimark and jetstream, evidently because of $ effects. Replacing the code + // with an equal number of never-executed NOPs results in the same regression. + // We leave it off by default. + + if ((EmitSync & 65536) != 0) { + Label LSuccess, LGoSlowPath ; + + masm.bind (CheckSucc) ; + + // Optional pre-test ... it's safe to elide this + if ((EmitSync & 16) == 0) { + masm.cmpl (Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), 0) ; + masm.jccb (Assembler::zero, LGoSlowPath) ; + } + + // We have a classic Dekker-style idiom: + // ST m->_owner = 0 ; MEMBAR; LD m->_succ + // There are a number of ways to implement the barrier: + // (1) lock:andl &m->_owner, 0 + // is fast, but mask doesn't currently support the "ANDL M,IMM32" form. + // LOCK: ANDL [ebx+Offset(_Owner)-2], 0 + // Encodes as 81 31 OFF32 IMM32 or 83 63 OFF8 IMM8 + // (2) If supported, an explicit MFENCE is appealing. + // In older IA32 processors MFENCE is slower than lock:add or xchg + // particularly if the write-buffer is full as might be the case if + // if stores closely precede the fence or fence-equivalent instruction. + // In more modern implementations MFENCE appears faster, however. + // (3) In lieu of an explicit fence, use lock:addl to the top-of-stack + // The $lines underlying the top-of-stack should be in M-state. + // The locked add instruction is serializing, of course. + // (4) Use xchg, which is serializing + // mov boxReg, 0; xchgl boxReg, [tmpReg + Offset(_owner)-2] also works + // (5) ST m->_owner = 0 and then execute lock:orl &m->_succ, 0. + // The integer condition codes will tell us if succ was 0. + // Since _succ and _owner should reside in the same $line and + // we just stored into _owner, it's likely that the $line + // remains in M-state for the lock:orl. + // + // We currently use (3), although it's likely that switching to (2) + // is correct for the future. + + masm.movl (Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), 0) ; + if (os::is_MP()) { + if (VM_Version::supports_sse2() && 1 == FenceInstruction) { + masm.emit_raw (0x0F) ; // MFENCE ... + masm.emit_raw (0xAE) ; + masm.emit_raw (0xF0) ; + } else { + masm.lock () ; masm.addl (Address(rsp, 0), 0) ; + } + } + // Ratify _succ remains non-null + masm.cmpl (Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), 0) ; + masm.jccb (Assembler::notZero, LSuccess) ; + + masm.xorl (boxReg, boxReg) ; // box is really EAX + if (os::is_MP()) { masm.lock(); } + masm.cmpxchg(rsp, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); + masm.jccb (Assembler::notEqual, LSuccess) ; + // Since we're low on registers we installed rsp as a placeholding in _owner. + // Now install Self over rsp. This is safe as we're transitioning from + // non-null to non=null + masm.get_thread (boxReg) ; + masm.movl (Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), boxReg) ; + // Intentional fall-through into LGoSlowPath ... + + masm.bind (LGoSlowPath) ; + masm.orl (boxReg, 1) ; // set ICC.ZF=0 to indicate failure + masm.jmpb (DONE_LABEL) ; + + masm.bind (LSuccess) ; + masm.xorl (boxReg, boxReg) ; // set ICC.ZF=1 to indicate success + masm.jmpb (DONE_LABEL) ; + } + + masm.bind (Stacked) ; + // It's not inflated and it's not recursively stack-locked and it's not biased. + // It must be stack-locked. + // Try to reset the header to displaced header. + // The "box" value on the stack is stable, so we can reload + // and be assured we observe the same value as above. + masm.movl (tmpReg, Address(boxReg, 0)) ; + if (os::is_MP()) { masm.lock(); } + masm.cmpxchg(tmpReg, Address(objReg, 0)); // Uses EAX which is box + // Intention fall-thru into DONE_LABEL + + + // DONE_LABEL is a hot target - we'd really like to place it at the + // start of cache line by padding with NOPs. + // See the AMD and Intel software optimization manuals for the + // most efficient "long" NOP encodings. + // Unfortunately none of our alignment mechanisms suffice. + if ((EmitSync & 65536) == 0) { + masm.bind (CheckSucc) ; + } + masm.bind(DONE_LABEL); + + // Avoid branch to branch on AMD processors + if (EmitSync & 32768) { masm.nop() ; } + } + %} + + enc_class enc_String_Compare() %{ + Label ECX_GOOD_LABEL, LENGTH_DIFF_LABEL, + POP_LABEL, DONE_LABEL, CONT_LABEL, + WHILE_HEAD_LABEL; + MacroAssembler masm(&cbuf); + + // Get the first character position in both strings + // [8] char array, [12] offset, [16] count + int value_offset = java_lang_String::value_offset_in_bytes(); + int offset_offset = java_lang_String::offset_offset_in_bytes(); + int count_offset = java_lang_String::count_offset_in_bytes(); + int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); + + masm.movl(rax, Address(rsi, value_offset)); + masm.movl(rcx, Address(rsi, offset_offset)); + masm.leal(rax, Address(rax, rcx, Address::times_2, base_offset)); + masm.movl(rbx, Address(rdi, value_offset)); + masm.movl(rcx, Address(rdi, offset_offset)); + masm.leal(rbx, Address(rbx, rcx, Address::times_2, base_offset)); + + // Compute the minimum of the string lengths(rsi) and the + // difference of the string lengths (stack) + + + if (VM_Version::supports_cmov()) { + masm.movl(rdi, Address(rdi, count_offset)); + masm.movl(rsi, Address(rsi, count_offset)); + masm.movl(rcx, rdi); + masm.subl(rdi, rsi); + masm.pushl(rdi); + masm.cmovl(Assembler::lessEqual, rsi, rcx); + } else { + masm.movl(rdi, Address(rdi, count_offset)); + masm.movl(rcx, Address(rsi, count_offset)); + masm.movl(rsi, rdi); + masm.subl(rdi, rcx); + masm.pushl(rdi); + masm.jcc(Assembler::lessEqual, ECX_GOOD_LABEL); + masm.movl(rsi, rcx); + // rsi holds min, rcx is unused + } + + // Is the minimum length zero? + masm.bind(ECX_GOOD_LABEL); + masm.testl(rsi, rsi); + masm.jcc(Assembler::zero, LENGTH_DIFF_LABEL); + + // Load first characters + masm.load_unsigned_word(rcx, Address(rbx, 0)); + masm.load_unsigned_word(rdi, Address(rax, 0)); + + // Compare first characters + masm.subl(rcx, rdi); + masm.jcc(Assembler::notZero, POP_LABEL); + masm.decrement(rsi); + masm.jcc(Assembler::zero, LENGTH_DIFF_LABEL); + + { + // Check after comparing first character to see if strings are equivalent + Label LSkip2; + // Check if the strings start at same location + masm.cmpl(rbx,rax); + masm.jcc(Assembler::notEqual, LSkip2); + + // Check if the length difference is zero (from stack) + masm.cmpl(Address(rsp, 0), 0x0); + masm.jcc(Assembler::equal, LENGTH_DIFF_LABEL); + + // Strings might not be equivalent + masm.bind(LSkip2); + } + + // Shift rax, and rbx, to the end of the arrays, negate min + masm.leal(rax, Address(rax, rsi, Address::times_2, 2)); + masm.leal(rbx, Address(rbx, rsi, Address::times_2, 2)); + masm.negl(rsi); + + // Compare the rest of the characters + masm.bind(WHILE_HEAD_LABEL); + masm.load_unsigned_word(rcx, Address(rbx, rsi, Address::times_2, 0)); + masm.load_unsigned_word(rdi, Address(rax, rsi, Address::times_2, 0)); + masm.subl(rcx, rdi); + masm.jcc(Assembler::notZero, POP_LABEL); + masm.increment(rsi); + masm.jcc(Assembler::notZero, WHILE_HEAD_LABEL); + + // Strings are equal up to min length. Return the length difference. + masm.bind(LENGTH_DIFF_LABEL); + masm.popl(rcx); + masm.jmp(DONE_LABEL); + + // Discard the stored length difference + masm.bind(POP_LABEL); + masm.addl(rsp, 4); + + // That's it + masm.bind(DONE_LABEL); + %} + + enc_class enc_pop_rdx() %{ + emit_opcode(cbuf,0x5A); + %} + + enc_class enc_rethrow() %{ + cbuf.set_inst_mark(); + emit_opcode(cbuf, 0xE9); // jmp entry + emit_d32_reloc(cbuf, (int)OptoRuntime::rethrow_stub() - ((int)cbuf.code_end())-4, + runtime_call_Relocation::spec(), RELOC_IMM32 ); + %} + + + // Convert a double to an int. Java semantics require we do complex + // manglelations in the corner cases. So we set the rounding mode to + // 'zero', store the darned double down as an int, and reset the + // rounding mode to 'nearest'. The hardware throws an exception which + // patches up the correct value directly to the stack. + enc_class D2I_encoding( regD src ) %{ + // Flip to round-to-zero mode. We attempted to allow invalid-op + // exceptions here, so that a NAN or other corner-case value will + // thrown an exception (but normal values get converted at full speed). + // However, I2C adapters and other float-stack manglers leave pending + // invalid-op exceptions hanging. We would have to clear them before + // enabling them and that is more expensive than just testing for the + // invalid value Intel stores down in the corner cases. + emit_opcode(cbuf,0xD9); // FLDCW trunc + emit_opcode(cbuf,0x2D); + emit_d32(cbuf,(int)StubRoutines::addr_fpu_cntrl_wrd_trunc()); + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,4 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x04); + // Encoding assumes a double has been pushed into FPR0. + // Store down the double as an int, popping the FPU stack + emit_opcode(cbuf,0xDB); // FISTP [ESP] + emit_opcode(cbuf,0x1C); + emit_d8(cbuf,0x24); + // Restore the rounding mode; mask the exception + emit_opcode(cbuf,0xD9); // FLDCW std/24-bit mode + emit_opcode(cbuf,0x2D); + emit_d32( cbuf, Compile::current()->in_24_bit_fp_mode() + ? (int)StubRoutines::addr_fpu_cntrl_wrd_24() + : (int)StubRoutines::addr_fpu_cntrl_wrd_std()); + + // Load the converted int; adjust CPU stack + emit_opcode(cbuf,0x58); // POP EAX + emit_opcode(cbuf,0x3D); // CMP EAX,imm + emit_d32 (cbuf,0x80000000); // 0x80000000 + emit_opcode(cbuf,0x75); // JNE around_slow_call + emit_d8 (cbuf,0x07); // Size of slow_call + // Push src onto stack slow-path + emit_opcode(cbuf,0xD9 ); // FLD ST(i) + emit_d8 (cbuf,0xC0-1+$src$$reg ); + // CALL directly to the runtime + cbuf.set_inst_mark(); + emit_opcode(cbuf,0xE8); // Call into runtime + emit_d32_reloc(cbuf, (StubRoutines::d2i_wrapper() - cbuf.code_end()) - 4, runtime_call_Relocation::spec(), RELOC_IMM32 ); + // Carry on here... + %} + + enc_class D2L_encoding( regD src ) %{ + emit_opcode(cbuf,0xD9); // FLDCW trunc + emit_opcode(cbuf,0x2D); + emit_d32(cbuf,(int)StubRoutines::addr_fpu_cntrl_wrd_trunc()); + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,8 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x08); + // Encoding assumes a double has been pushed into FPR0. + // Store down the double as a long, popping the FPU stack + emit_opcode(cbuf,0xDF); // FISTP [ESP] + emit_opcode(cbuf,0x3C); + emit_d8(cbuf,0x24); + // Restore the rounding mode; mask the exception + emit_opcode(cbuf,0xD9); // FLDCW std/24-bit mode + emit_opcode(cbuf,0x2D); + emit_d32( cbuf, Compile::current()->in_24_bit_fp_mode() + ? (int)StubRoutines::addr_fpu_cntrl_wrd_24() + : (int)StubRoutines::addr_fpu_cntrl_wrd_std()); + + // Load the converted int; adjust CPU stack + emit_opcode(cbuf,0x58); // POP EAX + emit_opcode(cbuf,0x5A); // POP EDX + emit_opcode(cbuf,0x81); // CMP EDX,imm + emit_d8 (cbuf,0xFA); // rdx + emit_d32 (cbuf,0x80000000); // 0x80000000 + emit_opcode(cbuf,0x75); // JNE around_slow_call + emit_d8 (cbuf,0x07+4); // Size of slow_call + emit_opcode(cbuf,0x85); // TEST EAX,EAX + emit_opcode(cbuf,0xC0); // 2/rax,/rax, + emit_opcode(cbuf,0x75); // JNE around_slow_call + emit_d8 (cbuf,0x07); // Size of slow_call + // Push src onto stack slow-path + emit_opcode(cbuf,0xD9 ); // FLD ST(i) + emit_d8 (cbuf,0xC0-1+$src$$reg ); + // CALL directly to the runtime + cbuf.set_inst_mark(); + emit_opcode(cbuf,0xE8); // Call into runtime + emit_d32_reloc(cbuf, (StubRoutines::d2l_wrapper() - cbuf.code_end()) - 4, runtime_call_Relocation::spec(), RELOC_IMM32 ); + // Carry on here... + %} + + enc_class X2L_encoding( regX src ) %{ + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,8 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x08); + + emit_opcode (cbuf, 0xF3 ); // MOVSS [ESP], src + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xD9 ); // FLD_S [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xD9); // FLDCW trunc + emit_opcode(cbuf,0x2D); + emit_d32(cbuf,(int)StubRoutines::addr_fpu_cntrl_wrd_trunc()); + + // Encoding assumes a double has been pushed into FPR0. + // Store down the double as a long, popping the FPU stack + emit_opcode(cbuf,0xDF); // FISTP [ESP] + emit_opcode(cbuf,0x3C); + emit_d8(cbuf,0x24); + + // Restore the rounding mode; mask the exception + emit_opcode(cbuf,0xD9); // FLDCW std/24-bit mode + emit_opcode(cbuf,0x2D); + emit_d32( cbuf, Compile::current()->in_24_bit_fp_mode() + ? (int)StubRoutines::addr_fpu_cntrl_wrd_24() + : (int)StubRoutines::addr_fpu_cntrl_wrd_std()); + + // Load the converted int; adjust CPU stack + emit_opcode(cbuf,0x58); // POP EAX + + emit_opcode(cbuf,0x5A); // POP EDX + + emit_opcode(cbuf,0x81); // CMP EDX,imm + emit_d8 (cbuf,0xFA); // rdx + emit_d32 (cbuf,0x80000000);// 0x80000000 + + emit_opcode(cbuf,0x75); // JNE around_slow_call + emit_d8 (cbuf,0x13+4); // Size of slow_call + + emit_opcode(cbuf,0x85); // TEST EAX,EAX + emit_opcode(cbuf,0xC0); // 2/rax,/rax, + + emit_opcode(cbuf,0x75); // JNE around_slow_call + emit_d8 (cbuf,0x13); // Size of slow_call + + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,4 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x04); + + emit_opcode (cbuf, 0xF3 ); // MOVSS [ESP], src + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xD9 ); // FLD_S [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0x83); // ADD ESP,4 + emit_opcode(cbuf,0xC4); + emit_d8(cbuf,0x04); + + // CALL directly to the runtime + cbuf.set_inst_mark(); + emit_opcode(cbuf,0xE8); // Call into runtime + emit_d32_reloc(cbuf, (StubRoutines::d2l_wrapper() - cbuf.code_end()) - 4, runtime_call_Relocation::spec(), RELOC_IMM32 ); + // Carry on here... + %} + + enc_class XD2L_encoding( regXD src ) %{ + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,8 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x08); + + emit_opcode (cbuf, 0xF2 ); // MOVSD [ESP], src + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xDD ); // FLD_D [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xD9); // FLDCW trunc + emit_opcode(cbuf,0x2D); + emit_d32(cbuf,(int)StubRoutines::addr_fpu_cntrl_wrd_trunc()); + + // Encoding assumes a double has been pushed into FPR0. + // Store down the double as a long, popping the FPU stack + emit_opcode(cbuf,0xDF); // FISTP [ESP] + emit_opcode(cbuf,0x3C); + emit_d8(cbuf,0x24); + + // Restore the rounding mode; mask the exception + emit_opcode(cbuf,0xD9); // FLDCW std/24-bit mode + emit_opcode(cbuf,0x2D); + emit_d32( cbuf, Compile::current()->in_24_bit_fp_mode() + ? (int)StubRoutines::addr_fpu_cntrl_wrd_24() + : (int)StubRoutines::addr_fpu_cntrl_wrd_std()); + + // Load the converted int; adjust CPU stack + emit_opcode(cbuf,0x58); // POP EAX + + emit_opcode(cbuf,0x5A); // POP EDX + + emit_opcode(cbuf,0x81); // CMP EDX,imm + emit_d8 (cbuf,0xFA); // rdx + emit_d32 (cbuf,0x80000000); // 0x80000000 + + emit_opcode(cbuf,0x75); // JNE around_slow_call + emit_d8 (cbuf,0x13+4); // Size of slow_call + + emit_opcode(cbuf,0x85); // TEST EAX,EAX + emit_opcode(cbuf,0xC0); // 2/rax,/rax, + + emit_opcode(cbuf,0x75); // JNE around_slow_call + emit_d8 (cbuf,0x13); // Size of slow_call + + // Push src onto stack slow-path + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,8 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x08); + + emit_opcode (cbuf, 0xF2 ); // MOVSD [ESP], src + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xDD ); // FLD_D [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0x83); // ADD ESP,8 + emit_opcode(cbuf,0xC4); + emit_d8(cbuf,0x08); + + // CALL directly to the runtime + cbuf.set_inst_mark(); + emit_opcode(cbuf,0xE8); // Call into runtime + emit_d32_reloc(cbuf, (StubRoutines::d2l_wrapper() - cbuf.code_end()) - 4, runtime_call_Relocation::spec(), RELOC_IMM32 ); + // Carry on here... + %} + + enc_class D2X_encoding( regX dst, regD src ) %{ + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,4 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x04); + int pop = 0x02; + if ($src$$reg != FPR1L_enc) { + emit_opcode( cbuf, 0xD9 ); // FLD ST(i-1) + emit_d8( cbuf, 0xC0-1+$src$$reg ); + pop = 0x03; + } + store_to_stackslot( cbuf, 0xD9, pop, 0 ); // FST

_S [ESP] + + emit_opcode (cbuf, 0xF3 ); // MOVSS dst(xmm), [ESP] + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x10 ); + encode_RegMem(cbuf, $dst$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0x83); // ADD ESP,4 + emit_opcode(cbuf,0xC4); + emit_d8(cbuf,0x04); + // Carry on here... + %} + + enc_class FX2I_encoding( regX src, eRegI dst ) %{ + emit_rm(cbuf, 0x3, $dst$$reg, $src$$reg); + + // Compare the result to see if we need to go to the slow path + emit_opcode(cbuf,0x81); // CMP dst,imm + emit_rm (cbuf,0x3,0x7,$dst$$reg); + emit_d32 (cbuf,0x80000000); // 0x80000000 + + emit_opcode(cbuf,0x75); // JNE around_slow_call + emit_d8 (cbuf,0x13); // Size of slow_call + // Store xmm to a temp memory + // location and push it onto stack. + + emit_opcode(cbuf,0x83); // SUB ESP,4 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf, $primary ? 0x8 : 0x4); + + emit_opcode (cbuf, $primary ? 0xF2 : 0xF3 ); // MOVSS [ESP], xmm + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf, $primary ? 0xDD : 0xD9 ); // FLD [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0x83); // ADD ESP,4 + emit_opcode(cbuf,0xC4); + emit_d8(cbuf, $primary ? 0x8 : 0x4); + + // CALL directly to the runtime + cbuf.set_inst_mark(); + emit_opcode(cbuf,0xE8); // Call into runtime + emit_d32_reloc(cbuf, (StubRoutines::d2i_wrapper() - cbuf.code_end()) - 4, runtime_call_Relocation::spec(), RELOC_IMM32 ); + + // Carry on here... + %} + + enc_class X2D_encoding( regD dst, regX src ) %{ + // Allocate a word + emit_opcode(cbuf,0x83); // SUB ESP,4 + emit_opcode(cbuf,0xEC); + emit_d8(cbuf,0x04); + + emit_opcode (cbuf, 0xF3 ); // MOVSS [ESP], xmm + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, 0x11 ); + encode_RegMem(cbuf, $src$$reg, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0xD9 ); // FLD_S [ESP] + encode_RegMem(cbuf, 0x0, ESP_enc, 0x4, 0, 0, false); + + emit_opcode(cbuf,0x83); // ADD ESP,4 + emit_opcode(cbuf,0xC4); + emit_d8(cbuf,0x04); + + // Carry on here... + %} + + enc_class AbsXF_encoding(regX dst) %{ + address signmask_address=(address)float_signmask_pool; + // andpd:\tANDPS $dst,[signconst] + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x54); + emit_rm(cbuf, 0x0, $dst$$reg, 0x5); + emit_d32(cbuf, (int)signmask_address); + %} + + enc_class AbsXD_encoding(regXD dst) %{ + address signmask_address=(address)double_signmask_pool; + // andpd:\tANDPD $dst,[signconst] + emit_opcode(cbuf, 0x66); + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x54); + emit_rm(cbuf, 0x0, $dst$$reg, 0x5); + emit_d32(cbuf, (int)signmask_address); + %} + + enc_class NegXF_encoding(regX dst) %{ + address signmask_address=(address)float_signflip_pool; + // andpd:\tXORPS $dst,[signconst] + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x57); + emit_rm(cbuf, 0x0, $dst$$reg, 0x5); + emit_d32(cbuf, (int)signmask_address); + %} + + enc_class NegXD_encoding(regXD dst) %{ + address signmask_address=(address)double_signflip_pool; + // andpd:\tXORPD $dst,[signconst] + emit_opcode(cbuf, 0x66); + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x57); + emit_rm(cbuf, 0x0, $dst$$reg, 0x5); + emit_d32(cbuf, (int)signmask_address); + %} + + enc_class FMul_ST_reg( eRegF src1 ) %{ + // Operand was loaded from memory into fp ST (stack top) + // FMUL ST,$src /* D8 C8+i */ + emit_opcode(cbuf, 0xD8); + emit_opcode(cbuf, 0xC8 + $src1$$reg); + %} + + enc_class FAdd_ST_reg( eRegF src2 ) %{ + // FADDP ST,src2 /* D8 C0+i */ + emit_opcode(cbuf, 0xD8); + emit_opcode(cbuf, 0xC0 + $src2$$reg); + //could use FADDP src2,fpST /* DE C0+i */ + %} + + enc_class FAddP_reg_ST( eRegF src2 ) %{ + // FADDP src2,ST /* DE C0+i */ + emit_opcode(cbuf, 0xDE); + emit_opcode(cbuf, 0xC0 + $src2$$reg); + %} + + enc_class subF_divF_encode( eRegF src1, eRegF src2) %{ + // Operand has been loaded into fp ST (stack top) + // FSUB ST,$src1 + emit_opcode(cbuf, 0xD8); + emit_opcode(cbuf, 0xE0 + $src1$$reg); + + // FDIV + emit_opcode(cbuf, 0xD8); + emit_opcode(cbuf, 0xF0 + $src2$$reg); + %} + + enc_class MulFAddF (eRegF src1, eRegF src2) %{ + // Operand was loaded from memory into fp ST (stack top) + // FADD ST,$src /* D8 C0+i */ + emit_opcode(cbuf, 0xD8); + emit_opcode(cbuf, 0xC0 + $src1$$reg); + + // FMUL ST,src2 /* D8 C*+i */ + emit_opcode(cbuf, 0xD8); + emit_opcode(cbuf, 0xC8 + $src2$$reg); + %} + + + enc_class MulFAddFreverse (eRegF src1, eRegF src2) %{ + // Operand was loaded from memory into fp ST (stack top) + // FADD ST,$src /* D8 C0+i */ + emit_opcode(cbuf, 0xD8); + emit_opcode(cbuf, 0xC0 + $src1$$reg); + + // FMULP src2,ST /* DE C8+i */ + emit_opcode(cbuf, 0xDE); + emit_opcode(cbuf, 0xC8 + $src2$$reg); + %} + + enc_class enc_membar_acquire %{ + // Doug Lea believes this is not needed with current Sparcs and TSO. + // MacroAssembler masm(&cbuf); + // masm.membar(); + %} + + enc_class enc_membar_release %{ + // Doug Lea believes this is not needed with current Sparcs and TSO. + // MacroAssembler masm(&cbuf); + // masm.membar(); + %} + + enc_class enc_membar_volatile %{ + MacroAssembler masm(&cbuf); + masm.membar(); + %} + + // Atomically load the volatile long + enc_class enc_loadL_volatile( memory mem, stackSlotL dst ) %{ + emit_opcode(cbuf,0xDF); + int rm_byte_opcode = 0x05; + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); // disp-as-oop when working with static globals + encode_RegMem(cbuf, rm_byte_opcode, base, index, scale, displace, disp_is_oop); + store_to_stackslot( cbuf, 0x0DF, 0x07, $dst$$disp ); + %} + + enc_class enc_loadLX_volatile( memory mem, stackSlotL dst, regXD tmp ) %{ + { // Atomic long load + // UseXmmLoadAndClearUpper ? movsd $tmp,$mem : movlpd $tmp,$mem + emit_opcode(cbuf,UseXmmLoadAndClearUpper ? 0xF2 : 0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,UseXmmLoadAndClearUpper ? 0x10 : 0x12); + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); // disp-as-oop when working with static globals + encode_RegMem(cbuf, $tmp$$reg, base, index, scale, displace, disp_is_oop); + } + { // MOVSD $dst,$tmp ! atomic long store + emit_opcode(cbuf,0xF2); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x11); + int base = $dst$$base; + int index = $dst$$index; + int scale = $dst$$scale; + int displace = $dst$$disp; + bool disp_is_oop = $dst->disp_is_oop(); // disp-as-oop when working with static globals + encode_RegMem(cbuf, $tmp$$reg, base, index, scale, displace, disp_is_oop); + } + %} + + enc_class enc_loadLX_reg_volatile( memory mem, eRegL dst, regXD tmp ) %{ + { // Atomic long load + // UseXmmLoadAndClearUpper ? movsd $tmp,$mem : movlpd $tmp,$mem + emit_opcode(cbuf,UseXmmLoadAndClearUpper ? 0xF2 : 0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,UseXmmLoadAndClearUpper ? 0x10 : 0x12); + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); // disp-as-oop when working with static globals + encode_RegMem(cbuf, $tmp$$reg, base, index, scale, displace, disp_is_oop); + } + { // MOVD $dst.lo,$tmp + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x7E); + emit_rm(cbuf, 0x3, $tmp$$reg, $dst$$reg); + } + { // PSRLQ $tmp,32 + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x73); + emit_rm(cbuf, 0x3, 0x02, $tmp$$reg); + emit_d8(cbuf, 0x20); + } + { // MOVD $dst.hi,$tmp + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x7E); + emit_rm(cbuf, 0x3, $tmp$$reg, HIGH_FROM_LOW($dst$$reg)); + } + %} + + // Volatile Store Long. Must be atomic, so move it into + // the FP TOS and then do a 64-bit FIST. Has to probe the + // target address before the store (for null-ptr checks) + // so the memory operand is used twice in the encoding. + enc_class enc_storeL_volatile( memory mem, stackSlotL src ) %{ + store_to_stackslot( cbuf, 0x0DF, 0x05, $src$$disp ); + cbuf.set_inst_mark(); // Mark start of FIST in case $mem has an oop + emit_opcode(cbuf,0xDF); + int rm_byte_opcode = 0x07; + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); // disp-as-oop when working with static globals + encode_RegMem(cbuf, rm_byte_opcode, base, index, scale, displace, disp_is_oop); + %} + + enc_class enc_storeLX_volatile( memory mem, stackSlotL src, regXD tmp) %{ + { // Atomic long load + // UseXmmLoadAndClearUpper ? movsd $tmp,[$src] : movlpd $tmp,[$src] + emit_opcode(cbuf,UseXmmLoadAndClearUpper ? 0xF2 : 0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,UseXmmLoadAndClearUpper ? 0x10 : 0x12); + int base = $src$$base; + int index = $src$$index; + int scale = $src$$scale; + int displace = $src$$disp; + bool disp_is_oop = $src->disp_is_oop(); // disp-as-oop when working with static globals + encode_RegMem(cbuf, $tmp$$reg, base, index, scale, displace, disp_is_oop); + } + cbuf.set_inst_mark(); // Mark start of MOVSD in case $mem has an oop + { // MOVSD $mem,$tmp ! atomic long store + emit_opcode(cbuf,0xF2); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x11); + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); // disp-as-oop when working with static globals + encode_RegMem(cbuf, $tmp$$reg, base, index, scale, displace, disp_is_oop); + } + %} + + enc_class enc_storeLX_reg_volatile( memory mem, eRegL src, regXD tmp, regXD tmp2) %{ + { // MOVD $tmp,$src.lo + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x6E); + emit_rm(cbuf, 0x3, $tmp$$reg, $src$$reg); + } + { // MOVD $tmp2,$src.hi + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x6E); + emit_rm(cbuf, 0x3, $tmp2$$reg, HIGH_FROM_LOW($src$$reg)); + } + { // PUNPCKLDQ $tmp,$tmp2 + emit_opcode(cbuf,0x66); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x62); + emit_rm(cbuf, 0x3, $tmp$$reg, $tmp2$$reg); + } + cbuf.set_inst_mark(); // Mark start of MOVSD in case $mem has an oop + { // MOVSD $mem,$tmp ! atomic long store + emit_opcode(cbuf,0xF2); + emit_opcode(cbuf,0x0F); + emit_opcode(cbuf,0x11); + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); // disp-as-oop when working with static globals + encode_RegMem(cbuf, $tmp$$reg, base, index, scale, displace, disp_is_oop); + } + %} + + // Safepoint Poll. This polls the safepoint page, and causes an + // exception if it is not readable. Unfortunately, it kills the condition code + // in the process + // We current use TESTL [spp],EDI + // A better choice might be TESTB [spp + pagesize() - CacheLineSize()],0 + + enc_class Safepoint_Poll() %{ + cbuf.relocate(cbuf.inst_mark(), relocInfo::poll_type, 0); + emit_opcode(cbuf,0x85); + emit_rm (cbuf, 0x0, 0x7, 0x5); + emit_d32(cbuf, (intptr_t)os::get_polling_page()); + %} +%} + + +//----------FRAME-------------------------------------------------------------- +// Definition of frame structure and management information. +// +// S T A C K L A Y O U T Allocators stack-slot number +// | (to get allocators register number +// G Owned by | | v add OptoReg::stack0()) +// r CALLER | | +// o | +--------+ pad to even-align allocators stack-slot +// w V | pad0 | numbers; owned by CALLER +// t -----------+--------+----> Matcher::_in_arg_limit, unaligned +// h ^ | in | 5 +// | | args | 4 Holes in incoming args owned by SELF +// | | | | 3 +// | | +--------+ +// V | | old out| Empty on Intel, window on Sparc +// | old |preserve| Must be even aligned. +// | SP-+--------+----> Matcher::_old_SP, even aligned +// | | in | 3 area for Intel ret address +// Owned by |preserve| Empty on Sparc. +// SELF +--------+ +// | | pad2 | 2 pad to align old SP +// | +--------+ 1 +// | | locks | 0 +// | +--------+----> OptoReg::stack0(), even aligned +// | | pad1 | 11 pad to align new SP +// | +--------+ +// | | | 10 +// | | spills | 9 spills +// V | | 8 (pad0 slot for callee) +// -----------+--------+----> Matcher::_out_arg_limit, unaligned +// ^ | out | 7 +// | | args | 6 Holes in outgoing args owned by CALLEE +// Owned by +--------+ +// CALLEE | new out| 6 Empty on Intel, window on Sparc +// | new |preserve| Must be even-aligned. +// | SP-+--------+----> Matcher::_new_SP, even aligned +// | | | +// +// Note 1: Only region 8-11 is determined by the allocator. Region 0-5 is +// known from SELF's arguments and the Java calling convention. +// Region 6-7 is determined per call site. +// Note 2: If the calling convention leaves holes in the incoming argument +// area, those holes are owned by SELF. Holes in the outgoing area +// are owned by the CALLEE. Holes should not be nessecary in the +// incoming area, as the Java calling convention is completely under +// the control of the AD file. Doubles can be sorted and packed to +// avoid holes. Holes in the outgoing arguments may be nessecary for +// varargs C calling conventions. +// Note 3: Region 0-3 is even aligned, with pad2 as needed. Region 3-5 is +// even aligned with pad0 as needed. +// Region 6 is even aligned. Region 6-7 is NOT even aligned; +// region 6-11 is even aligned; it may be padded out more so that +// the region from SP to FP meets the minimum stack alignment. + +frame %{ + // What direction does stack grow in (assumed to be same for C & Java) + stack_direction(TOWARDS_LOW); + + // These three registers define part of the calling convention + // between compiled code and the interpreter. + inline_cache_reg(EAX); // Inline Cache Register + interpreter_method_oop_reg(EBX); // Method Oop Register when calling interpreter + + // Optional: name the operand used by cisc-spilling to access [stack_pointer + offset] + cisc_spilling_operand_name(indOffset32); + + // Number of stack slots consumed by locking an object + sync_stack_slots(1); + + // Compiled code's Frame Pointer + frame_pointer(ESP); + // Interpreter stores its frame pointer in a register which is + // stored to the stack by I2CAdaptors. + // I2CAdaptors convert from interpreted java to compiled java. + interpreter_frame_pointer(EBP); + + // Stack alignment requirement + // Alignment size in bytes (128-bit -> 16 bytes) + stack_alignment(StackAlignmentInBytes); + + // Number of stack slots between incoming argument block and the start of + // a new frame. The PROLOG must add this many slots to the stack. The + // EPILOG must remove this many slots. Intel needs one slot for + // return address and one for rbp, (must save rbp) + in_preserve_stack_slots(2+VerifyStackAtCalls); + + // Number of outgoing stack slots killed above the out_preserve_stack_slots + // for calls to C. Supports the var-args backing area for register parms. + varargs_C_out_slots_killed(0); + + // The after-PROLOG location of the return address. Location of + // return address specifies a type (REG or STACK) and a number + // representing the register number (i.e. - use a register name) or + // stack slot. + // Ret Addr is on stack in slot 0 if no locks or verification or alignment. + // Otherwise, it is above the locks and verification slot and alignment word + return_addr(STACK - 1 + + round_to(1+VerifyStackAtCalls+ + Compile::current()->fixed_slots(), + (StackAlignmentInBytes/wordSize))); + + // Body of function which returns an integer array locating + // arguments either in registers or in stack slots. Passed an array + // of ideal registers called "sig" and a "length" count. Stack-slot + // offsets are based on outgoing arguments, i.e. a CALLER setting up + // arguments for a CALLEE. Incoming stack arguments are + // automatically biased by the preserve_stack_slots field above. + calling_convention %{ + // No difference between ingoing/outgoing just pass false + SharedRuntime::java_calling_convention(sig_bt, regs, length, false); + %} + + + // Body of function which returns an integer array locating + // arguments either in registers or in stack slots. Passed an array + // of ideal registers called "sig" and a "length" count. Stack-slot + // offsets are based on outgoing arguments, i.e. a CALLER setting up + // arguments for a CALLEE. Incoming stack arguments are + // automatically biased by the preserve_stack_slots field above. + c_calling_convention %{ + // This is obviously always outgoing + (void) SharedRuntime::c_calling_convention(sig_bt, regs, length); + %} + + // Location of C & interpreter return values + c_return_value %{ + assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" ); + static int lo[Op_RegL+1] = { 0, 0, EAX_num, EAX_num, FPR1L_num, FPR1L_num, EAX_num }; + static int hi[Op_RegL+1] = { 0, 0, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, FPR1H_num, EDX_num }; + + // in SSE2+ mode we want to keep the FPU stack clean so pretend + // that C functions return float and double results in XMM0. + if( ideal_reg == Op_RegD && UseSSE>=2 ) + return OptoRegPair(XMM0b_num,XMM0a_num); + if( ideal_reg == Op_RegF && UseSSE>=2 ) + return OptoRegPair(OptoReg::Bad,XMM0a_num); + + return OptoRegPair(hi[ideal_reg],lo[ideal_reg]); + %} + + // Location of return values + return_value %{ + assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" ); + static int lo[Op_RegL+1] = { 0, 0, EAX_num, EAX_num, FPR1L_num, FPR1L_num, EAX_num }; + static int hi[Op_RegL+1] = { 0, 0, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, FPR1H_num, EDX_num }; + if( ideal_reg == Op_RegD && UseSSE>=2 ) + return OptoRegPair(XMM0b_num,XMM0a_num); + if( ideal_reg == Op_RegF && UseSSE>=1 ) + return OptoRegPair(OptoReg::Bad,XMM0a_num); + return OptoRegPair(hi[ideal_reg],lo[ideal_reg]); + %} + +%} + +//----------ATTRIBUTES--------------------------------------------------------- +//----------Operand Attributes------------------------------------------------- +op_attrib op_cost(0); // Required cost attribute + +//----------Instruction Attributes--------------------------------------------- +ins_attrib ins_cost(100); // Required cost attribute +ins_attrib ins_size(8); // Required size attribute (in bits) +ins_attrib ins_pc_relative(0); // Required PC Relative flag +ins_attrib ins_short_branch(0); // Required flag: is this instruction a + // non-matching short branch variant of some + // long branch? +ins_attrib ins_alignment(1); // Required alignment attribute (must be a power of 2) + // specifies the alignment that some part of the instruction (not + // necessarily the start) requires. If > 1, a compute_padding() + // function must be provided for the instruction + +//----------OPERANDS----------------------------------------------------------- +// Operand definitions must precede instruction definitions for correct parsing +// in the ADLC because operands constitute user defined types which are used in +// instruction definitions. + +//----------Simple Operands---------------------------------------------------- +// Immediate Operands +// Integer Immediate +operand immI() %{ + match(ConI); + + op_cost(10); + format %{ %} + interface(CONST_INTER); +%} + +// Constant for test vs zero +operand immI0() %{ + predicate(n->get_int() == 0); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Constant for increment +operand immI1() %{ + predicate(n->get_int() == 1); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Constant for decrement +operand immI_M1() %{ + predicate(n->get_int() == -1); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Valid scale values for addressing modes +operand immI2() %{ + predicate(0 <= n->get_int() && (n->get_int() <= 3)); + match(ConI); + + format %{ %} + interface(CONST_INTER); +%} + +operand immI8() %{ + predicate((-128 <= n->get_int()) && (n->get_int() <= 127)); + match(ConI); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +operand immI16() %{ + predicate((-32768 <= n->get_int()) && (n->get_int() <= 32767)); + match(ConI); + + op_cost(10); + format %{ %} + interface(CONST_INTER); +%} + +// Constant for long shifts +operand immI_32() %{ + predicate( n->get_int() == 32 ); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immI_1_31() %{ + predicate( n->get_int() >= 1 && n->get_int() <= 31 ); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immI_32_63() %{ + predicate( n->get_int() >= 32 && n->get_int() <= 63 ); + match(ConI); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Pointer Immediate +operand immP() %{ + match(ConP); + + op_cost(10); + format %{ %} + interface(CONST_INTER); +%} + +// NULL Pointer Immediate +operand immP0() %{ + predicate( n->get_ptr() == 0 ); + match(ConP); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate +operand immL() %{ + match(ConL); + + op_cost(20); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate zero +operand immL0() %{ + predicate( n->get_long() == 0L ); + match(ConL); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Long immediate from 0 to 127. +// Used for a shorter form of long mul by 10. +operand immL_127() %{ + predicate((0 <= n->get_long()) && (n->get_long() <= 127)); + match(ConL); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: low 32-bit mask +operand immL_32bits() %{ + predicate(n->get_long() == 0xFFFFFFFFL); + match(ConL); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: low 32-bit mask +operand immL32() %{ + predicate(n->get_long() == (int)(n->get_long())); + match(ConL); + op_cost(20); + + format %{ %} + interface(CONST_INTER); +%} + +//Double Immediate zero +operand immD0() %{ + // Do additional (and counter-intuitive) test against NaN to work around VC++ + // bug that generates code such that NaNs compare equal to 0.0 + predicate( UseSSE<=1 && n->getd() == 0.0 && !g_isnan(n->getd()) ); + match(ConD); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Double Immediate +operand immD1() %{ + predicate( UseSSE<=1 && n->getd() == 1.0 ); + match(ConD); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Double Immediate +operand immD() %{ + predicate(UseSSE<=1); + match(ConD); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +operand immXD() %{ + predicate(UseSSE>=2); + match(ConD); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Double Immediate zero +operand immXD0() %{ + // Do additional (and counter-intuitive) test against NaN to work around VC++ + // bug that generates code such that NaNs compare equal to 0.0 AND do not + // compare equal to -0.0. + predicate( UseSSE>=2 && jlong_cast(n->getd()) == 0 ); + match(ConD); + + format %{ %} + interface(CONST_INTER); +%} + +// Float Immediate zero +operand immF0() %{ + predicate( UseSSE == 0 && n->getf() == 0.0 ); + match(ConF); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Float Immediate +operand immF() %{ + predicate( UseSSE == 0 ); + match(ConF); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Float Immediate +operand immXF() %{ + predicate(UseSSE >= 1); + match(ConF); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Float Immediate zero. Zero and not -0.0 +operand immXF0() %{ + predicate( UseSSE >= 1 && jint_cast(n->getf()) == 0 ); + match(ConF); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Immediates for special shifts (sign extend) + +// Constants for increment +operand immI_16() %{ + predicate( n->get_int() == 16 ); + match(ConI); + + format %{ %} + interface(CONST_INTER); +%} + +operand immI_24() %{ + predicate( n->get_int() == 24 ); + match(ConI); + + format %{ %} + interface(CONST_INTER); +%} + +// Constant for byte-wide masking +operand immI_255() %{ + predicate( n->get_int() == 255 ); + match(ConI); + + format %{ %} + interface(CONST_INTER); +%} + +// Register Operands +// Integer Register +operand eRegI() %{ + constraint(ALLOC_IN_RC(e_reg)); + match(RegI); + match(xRegI); + match(eAXRegI); + match(eBXRegI); + match(eCXRegI); + match(eDXRegI); + match(eDIRegI); + match(eSIRegI); + + format %{ %} + interface(REG_INTER); +%} + +// Subset of Integer Register +operand xRegI(eRegI reg) %{ + constraint(ALLOC_IN_RC(x_reg)); + match(reg); + match(eAXRegI); + match(eBXRegI); + match(eCXRegI); + match(eDXRegI); + + format %{ %} + interface(REG_INTER); +%} + +// Special Registers +operand eAXRegI(xRegI reg) %{ + constraint(ALLOC_IN_RC(eax_reg)); + match(reg); + match(eRegI); + + format %{ "EAX" %} + interface(REG_INTER); +%} + +// Special Registers +operand eBXRegI(xRegI reg) %{ + constraint(ALLOC_IN_RC(ebx_reg)); + match(reg); + match(eRegI); + + format %{ "EBX" %} + interface(REG_INTER); +%} + +operand eCXRegI(xRegI reg) %{ + constraint(ALLOC_IN_RC(ecx_reg)); + match(reg); + match(eRegI); + + format %{ "ECX" %} + interface(REG_INTER); +%} + +operand eDXRegI(xRegI reg) %{ + constraint(ALLOC_IN_RC(edx_reg)); + match(reg); + match(eRegI); + + format %{ "EDX" %} + interface(REG_INTER); +%} + +operand eDIRegI(xRegI reg) %{ + constraint(ALLOC_IN_RC(edi_reg)); + match(reg); + match(eRegI); + + format %{ "EDI" %} + interface(REG_INTER); +%} + +operand naxRegI() %{ + constraint(ALLOC_IN_RC(nax_reg)); + match(RegI); + match(eCXRegI); + match(eDXRegI); + match(eSIRegI); + match(eDIRegI); + + format %{ %} + interface(REG_INTER); +%} + +operand nadxRegI() %{ + constraint(ALLOC_IN_RC(nadx_reg)); + match(RegI); + match(eBXRegI); + match(eCXRegI); + match(eSIRegI); + match(eDIRegI); + + format %{ %} + interface(REG_INTER); +%} + +operand ncxRegI() %{ + constraint(ALLOC_IN_RC(ncx_reg)); + match(RegI); + match(eAXRegI); + match(eDXRegI); + match(eSIRegI); + match(eDIRegI); + + format %{ %} + interface(REG_INTER); +%} + +// // This operand was used by cmpFastUnlock, but conflicted with 'object' reg +// // +operand eSIRegI(xRegI reg) %{ + constraint(ALLOC_IN_RC(esi_reg)); + match(reg); + match(eRegI); + + format %{ "ESI" %} + interface(REG_INTER); +%} + +// Pointer Register +operand anyRegP() %{ + constraint(ALLOC_IN_RC(any_reg)); + match(RegP); + match(eAXRegP); + match(eBXRegP); + match(eCXRegP); + match(eDIRegP); + match(eRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand eRegP() %{ + constraint(ALLOC_IN_RC(e_reg)); + match(RegP); + match(eAXRegP); + match(eBXRegP); + match(eCXRegP); + match(eDIRegP); + + format %{ %} + interface(REG_INTER); +%} + +// On windows95, EBP is not safe to use for implicit null tests. +operand eRegP_no_EBP() %{ + constraint(ALLOC_IN_RC(e_reg_no_rbp)); + match(RegP); + match(eAXRegP); + match(eBXRegP); + match(eCXRegP); + match(eDIRegP); + + op_cost(100); + format %{ %} + interface(REG_INTER); +%} + +operand naxRegP() %{ + constraint(ALLOC_IN_RC(nax_reg)); + match(RegP); + match(eBXRegP); + match(eDXRegP); + match(eCXRegP); + match(eSIRegP); + match(eDIRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand nabxRegP() %{ + constraint(ALLOC_IN_RC(nabx_reg)); + match(RegP); + match(eCXRegP); + match(eDXRegP); + match(eSIRegP); + match(eDIRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand pRegP() %{ + constraint(ALLOC_IN_RC(p_reg)); + match(RegP); + match(eBXRegP); + match(eDXRegP); + match(eSIRegP); + match(eDIRegP); + + format %{ %} + interface(REG_INTER); +%} + +// Special Registers +// Return a pointer value +operand eAXRegP(eRegP reg) %{ + constraint(ALLOC_IN_RC(eax_reg)); + match(reg); + format %{ "EAX" %} + interface(REG_INTER); +%} + +// Used in AtomicAdd +operand eBXRegP(eRegP reg) %{ + constraint(ALLOC_IN_RC(ebx_reg)); + match(reg); + format %{ "EBX" %} + interface(REG_INTER); +%} + +// Tail-call (interprocedural jump) to interpreter +operand eCXRegP(eRegP reg) %{ + constraint(ALLOC_IN_RC(ecx_reg)); + match(reg); + format %{ "ECX" %} + interface(REG_INTER); +%} + +operand eSIRegP(eRegP reg) %{ + constraint(ALLOC_IN_RC(esi_reg)); + match(reg); + format %{ "ESI" %} + interface(REG_INTER); +%} + +// Used in rep stosw +operand eDIRegP(eRegP reg) %{ + constraint(ALLOC_IN_RC(edi_reg)); + match(reg); + format %{ "EDI" %} + interface(REG_INTER); +%} + +operand eBPRegP() %{ + constraint(ALLOC_IN_RC(ebp_reg)); + match(RegP); + format %{ "EBP" %} + interface(REG_INTER); +%} + +operand eRegL() %{ + constraint(ALLOC_IN_RC(long_reg)); + match(RegL); + match(eADXRegL); + + format %{ %} + interface(REG_INTER); +%} + +operand eADXRegL( eRegL reg ) %{ + constraint(ALLOC_IN_RC(eadx_reg)); + match(reg); + + format %{ "EDX:EAX" %} + interface(REG_INTER); +%} + +operand eBCXRegL( eRegL reg ) %{ + constraint(ALLOC_IN_RC(ebcx_reg)); + match(reg); + + format %{ "EBX:ECX" %} + interface(REG_INTER); +%} + +// Special case for integer high multiply +operand eADXRegL_low_only() %{ + constraint(ALLOC_IN_RC(eadx_reg)); + match(RegL); + + format %{ "EAX" %} + interface(REG_INTER); +%} + +// Flags register, used as output of compare instructions +operand eFlagsReg() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "EFLAGS" %} + interface(REG_INTER); +%} + +// Flags register, used as output of FLOATING POINT compare instructions +operand eFlagsRegU() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "EFLAGS_U" %} + interface(REG_INTER); +%} + +// Condition Code Register used by long compare +operand flagsReg_long_LTGE() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + format %{ "FLAGS_LTGE" %} + interface(REG_INTER); +%} +operand flagsReg_long_EQNE() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + format %{ "FLAGS_EQNE" %} + interface(REG_INTER); +%} +operand flagsReg_long_LEGT() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + format %{ "FLAGS_LEGT" %} + interface(REG_INTER); +%} + +// Float register operands +operand regD() %{ + predicate( UseSSE < 2 ); + constraint(ALLOC_IN_RC(dbl_reg)); + match(RegD); + match(regDPR1); + match(regDPR2); + format %{ %} + interface(REG_INTER); +%} + +operand regDPR1(regD reg) %{ + predicate( UseSSE < 2 ); + constraint(ALLOC_IN_RC(dbl_reg0)); + match(reg); + format %{ "FPR1" %} + interface(REG_INTER); +%} + +operand regDPR2(regD reg) %{ + predicate( UseSSE < 2 ); + constraint(ALLOC_IN_RC(dbl_reg1)); + match(reg); + format %{ "FPR2" %} + interface(REG_INTER); +%} + +operand regnotDPR1(regD reg) %{ + predicate( UseSSE < 2 ); + constraint(ALLOC_IN_RC(dbl_notreg0)); + match(reg); + format %{ %} + interface(REG_INTER); +%} + +// XMM Double register operands +operand regXD() %{ + predicate( UseSSE>=2 ); + constraint(ALLOC_IN_RC(xdb_reg)); + match(RegD); + match(regXD6); + match(regXD7); + format %{ %} + interface(REG_INTER); +%} + +// XMM6 double register operands +operand regXD6(regXD reg) %{ + predicate( UseSSE>=2 ); + constraint(ALLOC_IN_RC(xdb_reg6)); + match(reg); + format %{ "XMM6" %} + interface(REG_INTER); +%} + +// XMM7 double register operands +operand regXD7(regXD reg) %{ + predicate( UseSSE>=2 ); + constraint(ALLOC_IN_RC(xdb_reg7)); + match(reg); + format %{ "XMM7" %} + interface(REG_INTER); +%} + +// Float register operands +operand regF() %{ + predicate( UseSSE < 2 ); + constraint(ALLOC_IN_RC(flt_reg)); + match(RegF); + match(regFPR1); + format %{ %} + interface(REG_INTER); +%} + +// Float register operands +operand regFPR1(regF reg) %{ + predicate( UseSSE < 2 ); + constraint(ALLOC_IN_RC(flt_reg0)); + match(reg); + format %{ "FPR1" %} + interface(REG_INTER); +%} + +// XMM register operands +operand regX() %{ + predicate( UseSSE>=1 ); + constraint(ALLOC_IN_RC(xmm_reg)); + match(RegF); + format %{ %} + interface(REG_INTER); +%} + + +//----------Memory Operands---------------------------------------------------- +// Direct Memory Operand +operand direct(immP addr) %{ + match(addr); + + format %{ "[$addr]" %} + interface(MEMORY_INTER) %{ + base(0xFFFFFFFF); + index(0x4); + scale(0x0); + disp($addr); + %} +%} + +// Indirect Memory Operand +operand indirect(eRegP reg) %{ + constraint(ALLOC_IN_RC(e_reg)); + match(reg); + + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp(0x0); + %} +%} + +// Indirect Memory Plus Short Offset Operand +operand indOffset8(eRegP reg, immI8 off) %{ + match(AddP reg off); + + format %{ "[$reg + $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp($off); + %} +%} + +// Indirect Memory Plus Long Offset Operand +operand indOffset32(eRegP reg, immI off) %{ + match(AddP reg off); + + format %{ "[$reg + $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp($off); + %} +%} + +// Indirect Memory Plus Long Offset Operand +operand indOffset32X(eRegI reg, immP off) %{ + match(AddP off reg); + + format %{ "[$reg + $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp($off); + %} +%} + +// Indirect Memory Plus Index Register Plus Offset Operand +operand indIndexOffset(eRegP reg, eRegI ireg, immI off) %{ + match(AddP (AddP reg ireg) off); + + op_cost(10); + format %{"[$reg + $off + $ireg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($ireg); + scale(0x0); + disp($off); + %} +%} + +// Indirect Memory Plus Index Register Plus Offset Operand +operand indIndex(eRegP reg, eRegI ireg) %{ + match(AddP reg ireg); + + op_cost(10); + format %{"[$reg + $ireg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($ireg); + scale(0x0); + disp(0x0); + %} +%} + +// // ------------------------------------------------------------------------- +// // 486 architecture doesn't support "scale * index + offset" with out a base +// // ------------------------------------------------------------------------- +// // Scaled Memory Operands +// // Indirect Memory Times Scale Plus Offset Operand +// operand indScaleOffset(immP off, eRegI ireg, immI2 scale) %{ +// match(AddP off (LShiftI ireg scale)); +// +// op_cost(10); +// format %{"[$off + $ireg << $scale]" %} +// interface(MEMORY_INTER) %{ +// base(0x4); +// index($ireg); +// scale($scale); +// disp($off); +// %} +// %} + +// Indirect Memory Times Scale Plus Index Register +operand indIndexScale(eRegP reg, eRegI ireg, immI2 scale) %{ + match(AddP reg (LShiftI ireg scale)); + + op_cost(10); + format %{"[$reg + $ireg << $scale]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($ireg); + scale($scale); + disp(0x0); + %} +%} + +// Indirect Memory Times Scale Plus Index Register Plus Offset Operand +operand indIndexScaleOffset(eRegP reg, immI off, eRegI ireg, immI2 scale) %{ + match(AddP (AddP reg (LShiftI ireg scale)) off); + + op_cost(10); + format %{"[$reg + $off + $ireg << $scale]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($ireg); + scale($scale); + disp($off); + %} +%} + +//----------Load Long Memory Operands------------------------------------------ +// The load-long idiom will use it's address expression again after loading +// the first word of the long. If the load-long destination overlaps with +// registers used in the addressing expression, the 2nd half will be loaded +// from a clobbered address. Fix this by requiring that load-long use +// address registers that do not overlap with the load-long target. + +// load-long support +operand load_long_RegP() %{ + constraint(ALLOC_IN_RC(esi_reg)); + match(RegP); + match(eSIRegP); + op_cost(100); + format %{ %} + interface(REG_INTER); +%} + +// Indirect Memory Operand Long +operand load_long_indirect(load_long_RegP reg) %{ + constraint(ALLOC_IN_RC(esi_reg)); + match(reg); + + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp(0x0); + %} +%} + +// Indirect Memory Plus Long Offset Operand +operand load_long_indOffset32(load_long_RegP reg, immI off) %{ + match(AddP reg off); + + format %{ "[$reg + $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp($off); + %} +%} + +opclass load_long_memory(load_long_indirect, load_long_indOffset32); + + +//----------Special Memory Operands-------------------------------------------- +// Stack Slot Operand - This operand is used for loading and storing temporary +// values on the stack where a match requires a value to +// flow through memory. +operand stackSlotP(sRegP reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + // No match rule because this operand is only generated in matching + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0x4); // ESP + index(0x4); // No Index + scale(0x0); // No Scale + disp($reg); // Stack Offset + %} +%} + +operand stackSlotI(sRegI reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + // No match rule because this operand is only generated in matching + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0x4); // ESP + index(0x4); // No Index + scale(0x0); // No Scale + disp($reg); // Stack Offset + %} +%} + +operand stackSlotF(sRegF reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + // No match rule because this operand is only generated in matching + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0x4); // ESP + index(0x4); // No Index + scale(0x0); // No Scale + disp($reg); // Stack Offset + %} +%} + +operand stackSlotD(sRegD reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + // No match rule because this operand is only generated in matching + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0x4); // ESP + index(0x4); // No Index + scale(0x0); // No Scale + disp($reg); // Stack Offset + %} +%} + +operand stackSlotL(sRegL reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + // No match rule because this operand is only generated in matching + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0x4); // ESP + index(0x4); // No Index + scale(0x0); // No Scale + disp($reg); // Stack Offset + %} +%} + +//----------Memory Operands - Win95 Implicit Null Variants---------------- +// Indirect Memory Operand +operand indirect_win95_safe(eRegP_no_EBP reg) +%{ + constraint(ALLOC_IN_RC(e_reg)); + match(reg); + + op_cost(100); + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp(0x0); + %} +%} + +// Indirect Memory Plus Short Offset Operand +operand indOffset8_win95_safe(eRegP_no_EBP reg, immI8 off) +%{ + match(AddP reg off); + + op_cost(100); + format %{ "[$reg + $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp($off); + %} +%} + +// Indirect Memory Plus Long Offset Operand +operand indOffset32_win95_safe(eRegP_no_EBP reg, immI off) +%{ + match(AddP reg off); + + op_cost(100); + format %{ "[$reg + $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp($off); + %} +%} + +// Indirect Memory Plus Index Register Plus Offset Operand +operand indIndexOffset_win95_safe(eRegP_no_EBP reg, eRegI ireg, immI off) +%{ + match(AddP (AddP reg ireg) off); + + op_cost(100); + format %{"[$reg + $off + $ireg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($ireg); + scale(0x0); + disp($off); + %} +%} + +// Indirect Memory Times Scale Plus Index Register +operand indIndexScale_win95_safe(eRegP_no_EBP reg, eRegI ireg, immI2 scale) +%{ + match(AddP reg (LShiftI ireg scale)); + + op_cost(100); + format %{"[$reg + $ireg << $scale]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($ireg); + scale($scale); + disp(0x0); + %} +%} + +// Indirect Memory Times Scale Plus Index Register Plus Offset Operand +operand indIndexScaleOffset_win95_safe(eRegP_no_EBP reg, immI off, eRegI ireg, immI2 scale) +%{ + match(AddP (AddP reg (LShiftI ireg scale)) off); + + op_cost(100); + format %{"[$reg + $off + $ireg << $scale]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($ireg); + scale($scale); + disp($off); + %} +%} + +//----------Conditional Branch Operands---------------------------------------- +// Comparison Op - This is the operation of the comparison, and is limited to +// the following set of codes: +// L (<), LE (<=), G (>), GE (>=), E (==), NE (!=) +// +// Other attributes of the comparison, such as unsignedness, are specified +// by the comparison instruction that sets a condition code flags register. +// That result is represented by a flags operand whose subtype is appropriate +// to the unsignedness (etc.) of the comparison. +// +// Later, the instruction which matches both the Comparison Op (a Bool) and +// the flags (produced by the Cmp) specifies the coding of the comparison op +// by matching a specific subtype of Bool operand below, such as cmpOpU. + +// Comparision Code +operand cmpOp() %{ + match(Bool); + + format %{ "" %} + interface(COND_INTER) %{ + equal(0x4); + not_equal(0x5); + less(0xC); + greater_equal(0xD); + less_equal(0xE); + greater(0xF); + %} +%} + +// Comparison Code, unsigned compare. Used by FP also, with +// C2 (unordered) turned into GT or LT already. The other bits +// C0 and C3 are turned into Carry & Zero flags. +operand cmpOpU() %{ + match(Bool); + + format %{ "" %} + interface(COND_INTER) %{ + equal(0x4); + not_equal(0x5); + less(0x2); + greater_equal(0x3); + less_equal(0x6); + greater(0x7); + %} +%} + +// Comparison Code for FP conditional move +operand cmpOp_fcmov() %{ + match(Bool); + + format %{ "" %} + interface(COND_INTER) %{ + equal (0x0C8); + not_equal (0x1C8); + less (0x0C0); + greater_equal(0x1C0); + less_equal (0x0D0); + greater (0x1D0); + %} +%} + +// Comparision Code used in long compares +operand cmpOp_commute() %{ + match(Bool); + + format %{ "" %} + interface(COND_INTER) %{ + equal(0x4); + not_equal(0x5); + less(0xF); + greater_equal(0xE); + less_equal(0xD); + greater(0xC); + %} +%} + +//----------OPERAND CLASSES---------------------------------------------------- +// Operand Classes are groups of operands that are used as to simplify +// instruction definitions by not requiring the AD writer to specify seperate +// instructions for every form of operand when the instruction accepts +// multiple operand types with the same basic encoding and format. The classic +// case of this is memory operands. + +opclass memory(direct, indirect, indOffset8, indOffset32, indOffset32X, indIndexOffset, + indIndex, indIndexScale, indIndexScaleOffset); + +// Long memory operations are encoded in 2 instructions and a +4 offset. +// This means some kind of offset is always required and you cannot use +// an oop as the offset (done when working on static globals). +opclass long_memory(direct, indirect, indOffset8, indOffset32, indIndexOffset, + indIndex, indIndexScale, indIndexScaleOffset); + + +//----------PIPELINE----------------------------------------------------------- +// Rules which define the behavior of the target architectures pipeline. +pipeline %{ + +//----------ATTRIBUTES--------------------------------------------------------- +attributes %{ + variable_size_instructions; // Fixed size instructions + max_instructions_per_bundle = 3; // Up to 3 instructions per bundle + instruction_unit_size = 1; // An instruction is 1 bytes long + instruction_fetch_unit_size = 16; // The processor fetches one line + instruction_fetch_units = 1; // of 16 bytes + + // List of nop instructions + nops( MachNop ); +%} + +//----------RESOURCES---------------------------------------------------------- +// Resources are the functional units available to the machine + +// Generic P2/P3 pipeline +// 3 decoders, only D0 handles big operands; a "bundle" is the limit of +// 3 instructions decoded per cycle. +// 2 load/store ops per cycle, 1 branch, 1 FPU, +// 2 ALU op, only ALU0 handles mul/div instructions. +resources( D0, D1, D2, DECODE = D0 | D1 | D2, + MS0, MS1, MEM = MS0 | MS1, + BR, FPU, + ALU0, ALU1, ALU = ALU0 | ALU1 ); + +//----------PIPELINE DESCRIPTION----------------------------------------------- +// Pipeline Description specifies the stages in the machine's pipeline + +// Generic P2/P3 pipeline +pipe_desc(S0, S1, S2, S3, S4, S5); + +//----------PIPELINE CLASSES--------------------------------------------------- +// Pipeline Classes describe the stages in which input and output are +// referenced by the hardware pipeline. + +// Naming convention: ialu or fpu +// Then: _reg +// Then: _reg if there is a 2nd register +// Then: _long if it's a pair of instructions implementing a long +// Then: _fat if it requires the big decoder +// Or: _mem if it requires the big decoder and a memory unit. + +// Integer ALU reg operation +pipe_class ialu_reg(eRegI dst) %{ + single_instruction; + dst : S4(write); + dst : S3(read); + DECODE : S0; // any decoder + ALU : S3; // any alu +%} + +// Long ALU reg operation +pipe_class ialu_reg_long(eRegL dst) %{ + instruction_count(2); + dst : S4(write); + dst : S3(read); + DECODE : S0(2); // any 2 decoders + ALU : S3(2); // both alus +%} + +// Integer ALU reg operation using big decoder +pipe_class ialu_reg_fat(eRegI dst) %{ + single_instruction; + dst : S4(write); + dst : S3(read); + D0 : S0; // big decoder only + ALU : S3; // any alu +%} + +// Long ALU reg operation using big decoder +pipe_class ialu_reg_long_fat(eRegL dst) %{ + instruction_count(2); + dst : S4(write); + dst : S3(read); + D0 : S0(2); // big decoder only; twice + ALU : S3(2); // any 2 alus +%} + +// Integer ALU reg-reg operation +pipe_class ialu_reg_reg(eRegI dst, eRegI src) %{ + single_instruction; + dst : S4(write); + src : S3(read); + DECODE : S0; // any decoder + ALU : S3; // any alu +%} + +// Long ALU reg-reg operation +pipe_class ialu_reg_reg_long(eRegL dst, eRegL src) %{ + instruction_count(2); + dst : S4(write); + src : S3(read); + DECODE : S0(2); // any 2 decoders + ALU : S3(2); // both alus +%} + +// Integer ALU reg-reg operation +pipe_class ialu_reg_reg_fat(eRegI dst, memory src) %{ + single_instruction; + dst : S4(write); + src : S3(read); + D0 : S0; // big decoder only + ALU : S3; // any alu +%} + +// Long ALU reg-reg operation +pipe_class ialu_reg_reg_long_fat(eRegL dst, eRegL src) %{ + instruction_count(2); + dst : S4(write); + src : S3(read); + D0 : S0(2); // big decoder only; twice + ALU : S3(2); // both alus +%} + +// Integer ALU reg-mem operation +pipe_class ialu_reg_mem(eRegI dst, memory mem) %{ + single_instruction; + dst : S5(write); + mem : S3(read); + D0 : S0; // big decoder only + ALU : S4; // any alu + MEM : S3; // any mem +%} + +// Long ALU reg-mem operation +pipe_class ialu_reg_long_mem(eRegL dst, load_long_memory mem) %{ + instruction_count(2); + dst : S5(write); + mem : S3(read); + D0 : S0(2); // big decoder only; twice + ALU : S4(2); // any 2 alus + MEM : S3(2); // both mems +%} + +// Integer mem operation (prefetch) +pipe_class ialu_mem(memory mem) +%{ + single_instruction; + mem : S3(read); + D0 : S0; // big decoder only + MEM : S3; // any mem +%} + +// Integer Store to Memory +pipe_class ialu_mem_reg(memory mem, eRegI src) %{ + single_instruction; + mem : S3(read); + src : S5(read); + D0 : S0; // big decoder only + ALU : S4; // any alu + MEM : S3; +%} + +// Long Store to Memory +pipe_class ialu_mem_long_reg(memory mem, eRegL src) %{ + instruction_count(2); + mem : S3(read); + src : S5(read); + D0 : S0(2); // big decoder only; twice + ALU : S4(2); // any 2 alus + MEM : S3(2); // Both mems +%} + +// Integer Store to Memory +pipe_class ialu_mem_imm(memory mem) %{ + single_instruction; + mem : S3(read); + D0 : S0; // big decoder only + ALU : S4; // any alu + MEM : S3; +%} + +// Integer ALU0 reg-reg operation +pipe_class ialu_reg_reg_alu0(eRegI dst, eRegI src) %{ + single_instruction; + dst : S4(write); + src : S3(read); + D0 : S0; // Big decoder only + ALU0 : S3; // only alu0 +%} + +// Integer ALU0 reg-mem operation +pipe_class ialu_reg_mem_alu0(eRegI dst, memory mem) %{ + single_instruction; + dst : S5(write); + mem : S3(read); + D0 : S0; // big decoder only + ALU0 : S4; // ALU0 only + MEM : S3; // any mem +%} + +// Integer ALU reg-reg operation +pipe_class ialu_cr_reg_reg(eFlagsReg cr, eRegI src1, eRegI src2) %{ + single_instruction; + cr : S4(write); + src1 : S3(read); + src2 : S3(read); + DECODE : S0; // any decoder + ALU : S3; // any alu +%} + +// Integer ALU reg-imm operation +pipe_class ialu_cr_reg_imm(eFlagsReg cr, eRegI src1) %{ + single_instruction; + cr : S4(write); + src1 : S3(read); + DECODE : S0; // any decoder + ALU : S3; // any alu +%} + +// Integer ALU reg-mem operation +pipe_class ialu_cr_reg_mem(eFlagsReg cr, eRegI src1, memory src2) %{ + single_instruction; + cr : S4(write); + src1 : S3(read); + src2 : S3(read); + D0 : S0; // big decoder only + ALU : S4; // any alu + MEM : S3; +%} + +// Conditional move reg-reg +pipe_class pipe_cmplt( eRegI p, eRegI q, eRegI y ) %{ + instruction_count(4); + y : S4(read); + q : S3(read); + p : S3(read); + DECODE : S0(4); // any decoder +%} + +// Conditional move reg-reg +pipe_class pipe_cmov_reg( eRegI dst, eRegI src, eFlagsReg cr ) %{ + single_instruction; + dst : S4(write); + src : S3(read); + cr : S3(read); + DECODE : S0; // any decoder +%} + +// Conditional move reg-mem +pipe_class pipe_cmov_mem( eFlagsReg cr, eRegI dst, memory src) %{ + single_instruction; + dst : S4(write); + src : S3(read); + cr : S3(read); + DECODE : S0; // any decoder + MEM : S3; +%} + +// Conditional move reg-reg long +pipe_class pipe_cmov_reg_long( eFlagsReg cr, eRegL dst, eRegL src) %{ + single_instruction; + dst : S4(write); + src : S3(read); + cr : S3(read); + DECODE : S0(2); // any 2 decoders +%} + +// Conditional move double reg-reg +pipe_class pipe_cmovD_reg( eFlagsReg cr, regDPR1 dst, regD src) %{ + single_instruction; + dst : S4(write); + src : S3(read); + cr : S3(read); + DECODE : S0; // any decoder +%} + +// Float reg-reg operation +pipe_class fpu_reg(regD dst) %{ + instruction_count(2); + dst : S3(read); + DECODE : S0(2); // any 2 decoders + FPU : S3; +%} + +// Float reg-reg operation +pipe_class fpu_reg_reg(regD dst, regD src) %{ + instruction_count(2); + dst : S4(write); + src : S3(read); + DECODE : S0(2); // any 2 decoders + FPU : S3; +%} + +// Float reg-reg operation +pipe_class fpu_reg_reg_reg(regD dst, regD src1, regD src2) %{ + instruction_count(3); + dst : S4(write); + src1 : S3(read); + src2 : S3(read); + DECODE : S0(3); // any 3 decoders + FPU : S3(2); +%} + +// Float reg-reg operation +pipe_class fpu_reg_reg_reg_reg(regD dst, regD src1, regD src2, regD src3) %{ + instruction_count(4); + dst : S4(write); + src1 : S3(read); + src2 : S3(read); + src3 : S3(read); + DECODE : S0(4); // any 3 decoders + FPU : S3(2); +%} + +// Float reg-reg operation +pipe_class fpu_reg_mem_reg_reg(regD dst, memory src1, regD src2, regD src3) %{ + instruction_count(4); + dst : S4(write); + src1 : S3(read); + src2 : S3(read); + src3 : S3(read); + DECODE : S1(3); // any 3 decoders + D0 : S0; // Big decoder only + FPU : S3(2); + MEM : S3; +%} + +// Float reg-mem operation +pipe_class fpu_reg_mem(regD dst, memory mem) %{ + instruction_count(2); + dst : S5(write); + mem : S3(read); + D0 : S0; // big decoder only + DECODE : S1; // any decoder for FPU POP + FPU : S4; + MEM : S3; // any mem +%} + +// Float reg-mem operation +pipe_class fpu_reg_reg_mem(regD dst, regD src1, memory mem) %{ + instruction_count(3); + dst : S5(write); + src1 : S3(read); + mem : S3(read); + D0 : S0; // big decoder only + DECODE : S1(2); // any decoder for FPU POP + FPU : S4; + MEM : S3; // any mem +%} + +// Float mem-reg operation +pipe_class fpu_mem_reg(memory mem, regD src) %{ + instruction_count(2); + src : S5(read); + mem : S3(read); + DECODE : S0; // any decoder for FPU PUSH + D0 : S1; // big decoder only + FPU : S4; + MEM : S3; // any mem +%} + +pipe_class fpu_mem_reg_reg(memory mem, regD src1, regD src2) %{ + instruction_count(3); + src1 : S3(read); + src2 : S3(read); + mem : S3(read); + DECODE : S0(2); // any decoder for FPU PUSH + D0 : S1; // big decoder only + FPU : S4; + MEM : S3; // any mem +%} + +pipe_class fpu_mem_reg_mem(memory mem, regD src1, memory src2) %{ + instruction_count(3); + src1 : S3(read); + src2 : S3(read); + mem : S4(read); + DECODE : S0; // any decoder for FPU PUSH + D0 : S0(2); // big decoder only + FPU : S4; + MEM : S3(2); // any mem +%} + +pipe_class fpu_mem_mem(memory dst, memory src1) %{ + instruction_count(2); + src1 : S3(read); + dst : S4(read); + D0 : S0(2); // big decoder only + MEM : S3(2); // any mem +%} + +pipe_class fpu_mem_mem_mem(memory dst, memory src1, memory src2) %{ + instruction_count(3); + src1 : S3(read); + src2 : S3(read); + dst : S4(read); + D0 : S0(3); // big decoder only + FPU : S4; + MEM : S3(3); // any mem +%} + +pipe_class fpu_mem_reg_con(memory mem, regD src1) %{ + instruction_count(3); + src1 : S4(read); + mem : S4(read); + DECODE : S0; // any decoder for FPU PUSH + D0 : S0(2); // big decoder only + FPU : S4; + MEM : S3(2); // any mem +%} + +// Float load constant +pipe_class fpu_reg_con(regD dst) %{ + instruction_count(2); + dst : S5(write); + D0 : S0; // big decoder only for the load + DECODE : S1; // any decoder for FPU POP + FPU : S4; + MEM : S3; // any mem +%} + +// Float load constant +pipe_class fpu_reg_reg_con(regD dst, regD src) %{ + instruction_count(3); + dst : S5(write); + src : S3(read); + D0 : S0; // big decoder only for the load + DECODE : S1(2); // any decoder for FPU POP + FPU : S4; + MEM : S3; // any mem +%} + +// UnConditional branch +pipe_class pipe_jmp( label labl ) %{ + single_instruction; + BR : S3; +%} + +// Conditional branch +pipe_class pipe_jcc( cmpOp cmp, eFlagsReg cr, label labl ) %{ + single_instruction; + cr : S1(read); + BR : S3; +%} + +// Allocation idiom +pipe_class pipe_cmpxchg( eRegP dst, eRegP heap_ptr ) %{ + instruction_count(1); force_serialization; + fixed_latency(6); + heap_ptr : S3(read); + DECODE : S0(3); + D0 : S2; + MEM : S3; + ALU : S3(2); + dst : S5(write); + BR : S5; +%} + +// Generic big/slow expanded idiom +pipe_class pipe_slow( ) %{ + instruction_count(10); multiple_bundles; force_serialization; + fixed_latency(100); + D0 : S0(2); + MEM : S3(2); +%} + +// The real do-nothing guy +pipe_class empty( ) %{ + instruction_count(0); +%} + +// Define the class for the Nop node +define %{ + MachNop = empty; +%} + +%} + +//----------INSTRUCTIONS------------------------------------------------------- +// +// match -- States which machine-independent subtree may be replaced +// by this instruction. +// ins_cost -- The estimated cost of this instruction is used by instruction +// selection to identify a minimum cost tree of machine +// instructions that matches a tree of machine-independent +// instructions. +// format -- A string providing the disassembly for this instruction. +// The value of an instruction's operand may be inserted +// by referring to it with a '$' prefix. +// opcode -- Three instruction opcodes may be provided. These are referred +// to within an encode class as $primary, $secondary, and $tertiary +// respectively. The primary opcode is commonly used to +// indicate the type of machine instruction, while secondary +// and tertiary are often used for prefix options or addressing +// modes. +// ins_encode -- A list of encode classes with parameters. The encode class +// name must have been defined in an 'enc_class' specification +// in the encode section of the architecture description. + +//----------BSWAP-Instruction-------------------------------------------------- +instruct bytes_reverse_int(eRegI dst) %{ + match(Set dst (ReverseBytesI dst)); + + format %{ "BSWAP $dst" %} + opcode(0x0F, 0xC8); + ins_encode( OpcP, OpcSReg(dst) ); + ins_pipe( ialu_reg ); +%} + +instruct bytes_reverse_long(eRegL dst) %{ + match(Set dst (ReverseBytesL dst)); + + format %{ "BSWAP $dst.lo\n\t" + "BSWAP $dst.hi\n\t" + "XCHG $dst.lo $dst.hi" %} + + ins_cost(125); + ins_encode( bswap_long_bytes(dst) ); + ins_pipe( ialu_reg_reg); +%} + + +//----------Load/Store/Move Instructions--------------------------------------- +//----------Load Instructions-------------------------------------------------- +// Load Byte (8bit signed) +instruct loadB(xRegI dst, memory mem) %{ + match(Set dst (LoadB mem)); + + ins_cost(125); + format %{ "MOVSX8 $dst,$mem" %} + opcode(0xBE, 0x0F); + ins_encode( OpcS, OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_mem ); +%} + +// Load Byte (8bit UNsigned) +instruct loadUB(xRegI dst, memory mem, immI_255 bytemask) %{ + match(Set dst (AndI (LoadB mem) bytemask)); + + ins_cost(125); + format %{ "MOVZX8 $dst,$mem" %} + opcode(0xB6, 0x0F); + ins_encode( OpcS, OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_mem ); +%} + +// Load Char (16bit unsigned) +instruct loadC(eRegI dst, memory mem) %{ + match(Set dst (LoadC mem)); + + ins_cost(125); + format %{ "MOVZX $dst,$mem" %} + opcode(0xB7, 0x0F); + ins_encode( OpcS, OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_mem ); +%} + +// Load Integer +instruct loadI(eRegI dst, memory mem) %{ + match(Set dst (LoadI mem)); + + ins_cost(125); + format %{ "MOV $dst,$mem" %} + opcode(0x8B); + ins_encode( OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_mem ); +%} + +// Load Long. Cannot clobber address while loading, so restrict address +// register to ESI +instruct loadL(eRegL dst, load_long_memory mem) %{ + predicate(!((LoadLNode*)n)->require_atomic_access()); + match(Set dst (LoadL mem)); + + ins_cost(250); + format %{ "MOV $dst.lo,$mem\n\t" + "MOV $dst.hi,$mem+4" %} + opcode(0x8B, 0x8B); + ins_encode( OpcP, RegMem(dst,mem), OpcS, RegMem_Hi(dst,mem)); + ins_pipe( ialu_reg_long_mem ); +%} + +// Volatile Load Long. Must be atomic, so do 64-bit FILD +// then store it down to the stack and reload on the int +// side. +instruct loadL_volatile(stackSlotL dst, memory mem) %{ + predicate(UseSSE<=1 && ((LoadLNode*)n)->require_atomic_access()); + match(Set dst (LoadL mem)); + + ins_cost(200); + format %{ "FILD $mem\t# Atomic volatile long load\n\t" + "FISTp $dst" %} + ins_encode(enc_loadL_volatile(mem,dst)); + ins_pipe( fpu_reg_mem ); +%} + +instruct loadLX_volatile(stackSlotL dst, memory mem, regXD tmp) %{ + predicate(UseSSE>=2 && ((LoadLNode*)n)->require_atomic_access()); + match(Set dst (LoadL mem)); + effect(TEMP tmp); + ins_cost(180); + format %{ "MOVSD $tmp,$mem\t# Atomic volatile long load\n\t" + "MOVSD $dst,$tmp" %} + ins_encode(enc_loadLX_volatile(mem, dst, tmp)); + ins_pipe( pipe_slow ); +%} + +instruct loadLX_reg_volatile(eRegL dst, memory mem, regXD tmp) %{ + predicate(UseSSE>=2 && ((LoadLNode*)n)->require_atomic_access()); + match(Set dst (LoadL mem)); + effect(TEMP tmp); + ins_cost(160); + format %{ "MOVSD $tmp,$mem\t# Atomic volatile long load\n\t" + "MOVD $dst.lo,$tmp\n\t" + "PSRLQ $tmp,32\n\t" + "MOVD $dst.hi,$tmp" %} + ins_encode(enc_loadLX_reg_volatile(mem, dst, tmp)); + ins_pipe( pipe_slow ); +%} + +// Load Range +instruct loadRange(eRegI dst, memory mem) %{ + match(Set dst (LoadRange mem)); + + ins_cost(125); + format %{ "MOV $dst,$mem" %} + opcode(0x8B); + ins_encode( OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_mem ); +%} + + +// Load Pointer +instruct loadP(eRegP dst, memory mem) %{ + match(Set dst (LoadP mem)); + + ins_cost(125); + format %{ "MOV $dst,$mem" %} + opcode(0x8B); + ins_encode( OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_mem ); +%} + +// Load Klass Pointer +instruct loadKlass(eRegP dst, memory mem) %{ + match(Set dst (LoadKlass mem)); + + ins_cost(125); + format %{ "MOV $dst,$mem" %} + opcode(0x8B); + ins_encode( OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_mem ); +%} + +// Load Short (16bit signed) +instruct loadS(eRegI dst, memory mem) %{ + match(Set dst (LoadS mem)); + + ins_cost(125); + format %{ "MOVSX $dst,$mem" %} + opcode(0xBF, 0x0F); + ins_encode( OpcS, OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_mem ); +%} + +// Load Double +instruct loadD(regD dst, memory mem) %{ + predicate(UseSSE<=1); + match(Set dst (LoadD mem)); + + ins_cost(150); + format %{ "FLD_D ST,$mem\n\t" + "FSTP $dst" %} + opcode(0xDD); /* DD /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), + Pop_Reg_D(dst) ); + ins_pipe( fpu_reg_mem ); +%} + +// Load Double to XMM +instruct loadXD(regXD dst, memory mem) %{ + predicate(UseSSE>=2 && UseXmmLoadAndClearUpper); + match(Set dst (LoadD mem)); + ins_cost(145); + format %{ "MOVSD $dst,$mem" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x10), RegMem(dst,mem)); + ins_pipe( pipe_slow ); +%} + +instruct loadXD_partial(regXD dst, memory mem) %{ + predicate(UseSSE>=2 && !UseXmmLoadAndClearUpper); + match(Set dst (LoadD mem)); + ins_cost(145); + format %{ "MOVLPD $dst,$mem" %} + ins_encode( Opcode(0x66), Opcode(0x0F), Opcode(0x12), RegMem(dst,mem)); + ins_pipe( pipe_slow ); +%} + +// Load to XMM register (single-precision floating point) +// MOVSS instruction +instruct loadX(regX dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (LoadF mem)); + ins_cost(145); + format %{ "MOVSS $dst,$mem" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x10), RegMem(dst,mem)); + ins_pipe( pipe_slow ); +%} + +// Load Float +instruct loadF(regF dst, memory mem) %{ + predicate(UseSSE==0); + match(Set dst (LoadF mem)); + + ins_cost(150); + format %{ "FLD_S ST,$mem\n\t" + "FSTP $dst" %} + opcode(0xD9); /* D9 /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), + Pop_Reg_F(dst) ); + ins_pipe( fpu_reg_mem ); +%} + +// Load Aligned Packed Byte to XMM register +instruct loadA8B(regXD dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (Load8B mem)); + ins_cost(125); + format %{ "MOVQ $dst,$mem\t! packed8B" %} + ins_encode( movq_ld(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Load Aligned Packed Short to XMM register +instruct loadA4S(regXD dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (Load4S mem)); + ins_cost(125); + format %{ "MOVQ $dst,$mem\t! packed4S" %} + ins_encode( movq_ld(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Load Aligned Packed Char to XMM register +instruct loadA4C(regXD dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (Load4C mem)); + ins_cost(125); + format %{ "MOVQ $dst,$mem\t! packed4C" %} + ins_encode( movq_ld(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Load Aligned Packed Integer to XMM register +instruct load2IU(regXD dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (Load2I mem)); + ins_cost(125); + format %{ "MOVQ $dst,$mem\t! packed2I" %} + ins_encode( movq_ld(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Load Aligned Packed Single to XMM +instruct loadA2F(regXD dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (Load2F mem)); + ins_cost(145); + format %{ "MOVQ $dst,$mem\t! packed2F" %} + ins_encode( movq_ld(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Load Effective Address +instruct leaP8(eRegP dst, indOffset8 mem) %{ + match(Set dst mem); + + ins_cost(110); + format %{ "LEA $dst,$mem" %} + opcode(0x8D); + ins_encode( OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_reg_fat ); +%} + +instruct leaP32(eRegP dst, indOffset32 mem) %{ + match(Set dst mem); + + ins_cost(110); + format %{ "LEA $dst,$mem" %} + opcode(0x8D); + ins_encode( OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_reg_fat ); +%} + +instruct leaPIdxOff(eRegP dst, indIndexOffset mem) %{ + match(Set dst mem); + + ins_cost(110); + format %{ "LEA $dst,$mem" %} + opcode(0x8D); + ins_encode( OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_reg_fat ); +%} + +instruct leaPIdxScale(eRegP dst, indIndexScale mem) %{ + match(Set dst mem); + + ins_cost(110); + format %{ "LEA $dst,$mem" %} + opcode(0x8D); + ins_encode( OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_reg_fat ); +%} + +instruct leaPIdxScaleOff(eRegP dst, indIndexScaleOffset mem) %{ + match(Set dst mem); + + ins_cost(110); + format %{ "LEA $dst,$mem" %} + opcode(0x8D); + ins_encode( OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_reg_fat ); +%} + +// Load Constant +instruct loadConI(eRegI dst, immI src) %{ + match(Set dst src); + + format %{ "MOV $dst,$src" %} + ins_encode( LdImmI(dst, src) ); + ins_pipe( ialu_reg_fat ); +%} + +// Load Constant zero +instruct loadConI0(eRegI dst, immI0 src, eFlagsReg cr) %{ + match(Set dst src); + effect(KILL cr); + + ins_cost(50); + format %{ "XOR $dst,$dst" %} + opcode(0x33); /* + rd */ + ins_encode( OpcP, RegReg( dst, dst ) ); + ins_pipe( ialu_reg ); +%} + +instruct loadConP(eRegP dst, immP src) %{ + match(Set dst src); + + format %{ "MOV $dst,$src" %} + opcode(0xB8); /* + rd */ + ins_encode( LdImmP(dst, src) ); + ins_pipe( ialu_reg_fat ); +%} + +instruct loadConL(eRegL dst, immL src, eFlagsReg cr) %{ + match(Set dst src); + effect(KILL cr); + ins_cost(200); + format %{ "MOV $dst.lo,$src.lo\n\t" + "MOV $dst.hi,$src.hi" %} + opcode(0xB8); + ins_encode( LdImmL_Lo(dst, src), LdImmL_Hi(dst, src) ); + ins_pipe( ialu_reg_long_fat ); +%} + +instruct loadConL0(eRegL dst, immL0 src, eFlagsReg cr) %{ + match(Set dst src); + effect(KILL cr); + ins_cost(150); + format %{ "XOR $dst.lo,$dst.lo\n\t" + "XOR $dst.hi,$dst.hi" %} + opcode(0x33,0x33); + ins_encode( RegReg_Lo(dst,dst), RegReg_Hi(dst, dst) ); + ins_pipe( ialu_reg_long ); +%} + +// The instruction usage is guarded by predicate in operand immF(). +instruct loadConF(regF dst, immF src) %{ + match(Set dst src); + ins_cost(125); + + format %{ "FLD_S ST,$src\n\t" + "FSTP $dst" %} + opcode(0xD9, 0x00); /* D9 /0 */ + ins_encode(LdImmF(src), Pop_Reg_F(dst) ); + ins_pipe( fpu_reg_con ); +%} + +// The instruction usage is guarded by predicate in operand immXF(). +instruct loadConX(regX dst, immXF con) %{ + match(Set dst con); + ins_cost(125); + format %{ "MOVSS $dst,[$con]" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x10), LdImmX(dst, con)); + ins_pipe( pipe_slow ); +%} + +// The instruction usage is guarded by predicate in operand immXF0(). +instruct loadConX0(regX dst, immXF0 src) %{ + match(Set dst src); + ins_cost(100); + format %{ "XORPS $dst,$dst\t# float 0.0" %} + ins_encode( Opcode(0x0F), Opcode(0x57), RegReg(dst,dst)); + ins_pipe( pipe_slow ); +%} + +// The instruction usage is guarded by predicate in operand immD(). +instruct loadConD(regD dst, immD src) %{ + match(Set dst src); + ins_cost(125); + + format %{ "FLD_D ST,$src\n\t" + "FSTP $dst" %} + ins_encode(LdImmD(src), Pop_Reg_D(dst) ); + ins_pipe( fpu_reg_con ); +%} + +// The instruction usage is guarded by predicate in operand immXD(). +instruct loadConXD(regXD dst, immXD con) %{ + match(Set dst con); + ins_cost(125); + format %{ "MOVSD $dst,[$con]" %} + ins_encode(load_conXD(dst, con)); + ins_pipe( pipe_slow ); +%} + +// The instruction usage is guarded by predicate in operand immXD0(). +instruct loadConXD0(regXD dst, immXD0 src) %{ + match(Set dst src); + ins_cost(100); + format %{ "XORPD $dst,$dst\t# double 0.0" %} + ins_encode( Opcode(0x66), Opcode(0x0F), Opcode(0x57), RegReg(dst,dst)); + ins_pipe( pipe_slow ); +%} + +// Load Stack Slot +instruct loadSSI(eRegI dst, stackSlotI src) %{ + match(Set dst src); + ins_cost(125); + + format %{ "MOV $dst,$src" %} + opcode(0x8B); + ins_encode( OpcP, RegMem(dst,src)); + ins_pipe( ialu_reg_mem ); +%} + +instruct loadSSL(eRegL dst, stackSlotL src) %{ + match(Set dst src); + + ins_cost(200); + format %{ "MOV $dst,$src.lo\n\t" + "MOV $dst+4,$src.hi" %} + opcode(0x8B, 0x8B); + ins_encode( OpcP, RegMem( dst, src ), OpcS, RegMem_Hi( dst, src ) ); + ins_pipe( ialu_mem_long_reg ); +%} + +// Load Stack Slot +instruct loadSSP(eRegP dst, stackSlotP src) %{ + match(Set dst src); + ins_cost(125); + + format %{ "MOV $dst,$src" %} + opcode(0x8B); + ins_encode( OpcP, RegMem(dst,src)); + ins_pipe( ialu_reg_mem ); +%} + +// Load Stack Slot +instruct loadSSF(regF dst, stackSlotF src) %{ + match(Set dst src); + ins_cost(125); + + format %{ "FLD_S $src\n\t" + "FSTP $dst" %} + opcode(0xD9); /* D9 /0, FLD m32real */ + ins_encode( OpcP, RMopc_Mem_no_oop(0x00,src), + Pop_Reg_F(dst) ); + ins_pipe( fpu_reg_mem ); +%} + +// Load Stack Slot +instruct loadSSD(regD dst, stackSlotD src) %{ + match(Set dst src); + ins_cost(125); + + format %{ "FLD_D $src\n\t" + "FSTP $dst" %} + opcode(0xDD); /* DD /0, FLD m64real */ + ins_encode( OpcP, RMopc_Mem_no_oop(0x00,src), + Pop_Reg_D(dst) ); + ins_pipe( fpu_reg_mem ); +%} + +// Prefetch instructions. +// Must be safe to execute with invalid address (cannot fault). + +instruct prefetchr0( memory mem ) %{ + predicate(UseSSE==0 && !VM_Version::supports_3dnow()); + match(PrefetchRead mem); + ins_cost(0); + size(0); + format %{ "PREFETCHR (non-SSE is empty encoding)" %} + ins_encode(); + ins_pipe(empty); +%} + +instruct prefetchr( memory mem ) %{ + predicate(UseSSE==0 && VM_Version::supports_3dnow() || ReadPrefetchInstr==3); + match(PrefetchRead mem); + ins_cost(100); + + format %{ "PREFETCHR $mem\t! Prefetch into level 1 cache for read" %} + opcode(0x0F, 0x0d); /* Opcode 0F 0d /0 */ + ins_encode(OpcP, OpcS, RMopc_Mem(0x00,mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchrNTA( memory mem ) %{ + predicate(UseSSE>=1 && ReadPrefetchInstr==0); + match(PrefetchRead mem); + ins_cost(100); + + format %{ "PREFETCHNTA $mem\t! Prefetch into non-temporal cache for read" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /0 */ + ins_encode(OpcP, OpcS, RMopc_Mem(0x00,mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchrT0( memory mem ) %{ + predicate(UseSSE>=1 && ReadPrefetchInstr==1); + match(PrefetchRead mem); + ins_cost(100); + + format %{ "PREFETCHT0 $mem\t! Prefetch into L1 and L2 caches for read" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /1 */ + ins_encode(OpcP, OpcS, RMopc_Mem(0x01,mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchrT2( memory mem ) %{ + predicate(UseSSE>=1 && ReadPrefetchInstr==2); + match(PrefetchRead mem); + ins_cost(100); + + format %{ "PREFETCHT2 $mem\t! Prefetch into L2 cache for read" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /3 */ + ins_encode(OpcP, OpcS, RMopc_Mem(0x03,mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchw0( memory mem ) %{ + predicate(UseSSE==0 && !VM_Version::supports_3dnow()); + match(PrefetchWrite mem); + ins_cost(0); + size(0); + format %{ "Prefetch (non-SSE is empty encoding)" %} + ins_encode(); + ins_pipe(empty); +%} + +instruct prefetchw( memory mem ) %{ + predicate(UseSSE==0 && VM_Version::supports_3dnow() || AllocatePrefetchInstr==3); + match( PrefetchWrite mem ); + ins_cost(100); + + format %{ "PREFETCHW $mem\t! Prefetch into L1 cache and mark modified" %} + opcode(0x0F, 0x0D); /* Opcode 0F 0D /1 */ + ins_encode(OpcP, OpcS, RMopc_Mem(0x01,mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchwNTA( memory mem ) %{ + predicate(UseSSE>=1 && AllocatePrefetchInstr==0); + match(PrefetchWrite mem); + ins_cost(100); + + format %{ "PREFETCHNTA $mem\t! Prefetch into non-temporal cache for write" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /0 */ + ins_encode(OpcP, OpcS, RMopc_Mem(0x00,mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchwT0( memory mem ) %{ + predicate(UseSSE>=1 && AllocatePrefetchInstr==1); + match(PrefetchWrite mem); + ins_cost(100); + + format %{ "PREFETCHT0 $mem\t! Prefetch into L1 and L2 caches for write" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /1 */ + ins_encode(OpcP, OpcS, RMopc_Mem(0x01,mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchwT2( memory mem ) %{ + predicate(UseSSE>=1 && AllocatePrefetchInstr==2); + match(PrefetchWrite mem); + ins_cost(100); + + format %{ "PREFETCHT2 $mem\t! Prefetch into L2 cache for write" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /3 */ + ins_encode(OpcP, OpcS, RMopc_Mem(0x03,mem)); + ins_pipe(ialu_mem); +%} + +//----------Store Instructions------------------------------------------------- + +// Store Byte +instruct storeB(memory mem, xRegI src) %{ + match(Set mem (StoreB mem src)); + + ins_cost(125); + format %{ "MOV8 $mem,$src" %} + opcode(0x88); + ins_encode( OpcP, RegMem( src, mem ) ); + ins_pipe( ialu_mem_reg ); +%} + +// Store Char/Short +instruct storeC(memory mem, eRegI src) %{ + match(Set mem (StoreC mem src)); + + ins_cost(125); + format %{ "MOV16 $mem,$src" %} + opcode(0x89, 0x66); + ins_encode( OpcS, OpcP, RegMem( src, mem ) ); + ins_pipe( ialu_mem_reg ); +%} + +// Store Integer +instruct storeI(memory mem, eRegI src) %{ + match(Set mem (StoreI mem src)); + + ins_cost(125); + format %{ "MOV $mem,$src" %} + opcode(0x89); + ins_encode( OpcP, RegMem( src, mem ) ); + ins_pipe( ialu_mem_reg ); +%} + +// Store Long +instruct storeL(long_memory mem, eRegL src) %{ + predicate(!((StoreLNode*)n)->require_atomic_access()); + match(Set mem (StoreL mem src)); + + ins_cost(200); + format %{ "MOV $mem,$src.lo\n\t" + "MOV $mem+4,$src.hi" %} + opcode(0x89, 0x89); + ins_encode( OpcP, RegMem( src, mem ), OpcS, RegMem_Hi( src, mem ) ); + ins_pipe( ialu_mem_long_reg ); +%} + +// Volatile Store Long. Must be atomic, so move it into +// the FP TOS and then do a 64-bit FIST. Has to probe the +// target address before the store (for null-ptr checks) +// so the memory operand is used twice in the encoding. +instruct storeL_volatile(memory mem, stackSlotL src, eFlagsReg cr ) %{ + predicate(UseSSE<=1 && ((StoreLNode*)n)->require_atomic_access()); + match(Set mem (StoreL mem src)); + effect( KILL cr ); + ins_cost(400); + format %{ "CMP $mem,EAX\t# Probe address for implicit null check\n\t" + "FILD $src\n\t" + "FISTp $mem\t # 64-bit atomic volatile long store" %} + opcode(0x3B); + ins_encode( OpcP, RegMem( EAX, mem ), enc_storeL_volatile(mem,src)); + ins_pipe( fpu_reg_mem ); +%} + +instruct storeLX_volatile(memory mem, stackSlotL src, regXD tmp, eFlagsReg cr) %{ + predicate(UseSSE>=2 && ((StoreLNode*)n)->require_atomic_access()); + match(Set mem (StoreL mem src)); + effect( TEMP tmp, KILL cr ); + ins_cost(380); + format %{ "CMP $mem,EAX\t# Probe address for implicit null check\n\t" + "MOVSD $tmp,$src\n\t" + "MOVSD $mem,$tmp\t # 64-bit atomic volatile long store" %} + opcode(0x3B); + ins_encode( OpcP, RegMem( EAX, mem ), enc_storeLX_volatile(mem, src, tmp)); + ins_pipe( pipe_slow ); +%} + +instruct storeLX_reg_volatile(memory mem, eRegL src, regXD tmp2, regXD tmp, eFlagsReg cr) %{ + predicate(UseSSE>=2 && ((StoreLNode*)n)->require_atomic_access()); + match(Set mem (StoreL mem src)); + effect( TEMP tmp2 , TEMP tmp, KILL cr ); + ins_cost(360); + format %{ "CMP $mem,EAX\t# Probe address for implicit null check\n\t" + "MOVD $tmp,$src.lo\n\t" + "MOVD $tmp2,$src.hi\n\t" + "PUNPCKLDQ $tmp,$tmp2\n\t" + "MOVSD $mem,$tmp\t # 64-bit atomic volatile long store" %} + opcode(0x3B); + ins_encode( OpcP, RegMem( EAX, mem ), enc_storeLX_reg_volatile(mem, src, tmp, tmp2)); + ins_pipe( pipe_slow ); +%} + +// Store Pointer; for storing unknown oops and raw pointers +instruct storeP(memory mem, anyRegP src) %{ + match(Set mem (StoreP mem src)); + + ins_cost(125); + format %{ "MOV $mem,$src" %} + opcode(0x89); + ins_encode( OpcP, RegMem( src, mem ) ); + ins_pipe( ialu_mem_reg ); +%} + +// Store Integer Immediate +instruct storeImmI(memory mem, immI src) %{ + match(Set mem (StoreI mem src)); + + ins_cost(150); + format %{ "MOV $mem,$src" %} + opcode(0xC7); /* C7 /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), Con32( src )); + ins_pipe( ialu_mem_imm ); +%} + +// Store Short/Char Immediate +instruct storeImmI16(memory mem, immI16 src) %{ + predicate(UseStoreImmI16); + match(Set mem (StoreC mem src)); + + ins_cost(150); + format %{ "MOV16 $mem,$src" %} + opcode(0xC7); /* C7 /0 Same as 32 store immediate with prefix */ + ins_encode( SizePrefix, OpcP, RMopc_Mem(0x00,mem), Con16( src )); + ins_pipe( ialu_mem_imm ); +%} + +// Store Pointer Immediate; null pointers or constant oops that do not +// need card-mark barriers. +instruct storeImmP(memory mem, immP src) %{ + match(Set mem (StoreP mem src)); + + ins_cost(150); + format %{ "MOV $mem,$src" %} + opcode(0xC7); /* C7 /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), Con32( src )); + ins_pipe( ialu_mem_imm ); +%} + +// Store Byte Immediate +instruct storeImmB(memory mem, immI8 src) %{ + match(Set mem (StoreB mem src)); + + ins_cost(150); + format %{ "MOV8 $mem,$src" %} + opcode(0xC6); /* C6 /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), Con8or32( src )); + ins_pipe( ialu_mem_imm ); +%} + +// Store Aligned Packed Byte XMM register to memory +instruct storeA8B(memory mem, regXD src) %{ + predicate(UseSSE>=1); + match(Set mem (Store8B mem src)); + ins_cost(145); + format %{ "MOVQ $mem,$src\t! packed8B" %} + ins_encode( movq_st(mem, src)); + ins_pipe( pipe_slow ); +%} + +// Store Aligned Packed Char/Short XMM register to memory +instruct storeA4C(memory mem, regXD src) %{ + predicate(UseSSE>=1); + match(Set mem (Store4C mem src)); + ins_cost(145); + format %{ "MOVQ $mem,$src\t! packed4C" %} + ins_encode( movq_st(mem, src)); + ins_pipe( pipe_slow ); +%} + +// Store Aligned Packed Integer XMM register to memory +instruct storeA2I(memory mem, regXD src) %{ + predicate(UseSSE>=1); + match(Set mem (Store2I mem src)); + ins_cost(145); + format %{ "MOVQ $mem,$src\t! packed2I" %} + ins_encode( movq_st(mem, src)); + ins_pipe( pipe_slow ); +%} + +// Store CMS card-mark Immediate +instruct storeImmCM(memory mem, immI8 src) %{ + match(Set mem (StoreCM mem src)); + + ins_cost(150); + format %{ "MOV8 $mem,$src\t! CMS card-mark imm0" %} + opcode(0xC6); /* C6 /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), Con8or32( src )); + ins_pipe( ialu_mem_imm ); +%} + +// Store Double +instruct storeD( memory mem, regDPR1 src) %{ + predicate(UseSSE<=1); + match(Set mem (StoreD mem src)); + + ins_cost(100); + format %{ "FST_D $mem,$src" %} + opcode(0xDD); /* DD /2 */ + ins_encode( enc_FP_store(mem,src) ); + ins_pipe( fpu_mem_reg ); +%} + +// Store double does rounding on x86 +instruct storeD_rounded( memory mem, regDPR1 src) %{ + predicate(UseSSE<=1); + match(Set mem (StoreD mem (RoundDouble src))); + + ins_cost(100); + format %{ "FST_D $mem,$src\t# round" %} + opcode(0xDD); /* DD /2 */ + ins_encode( enc_FP_store(mem,src) ); + ins_pipe( fpu_mem_reg ); +%} + +// Store XMM register to memory (double-precision floating points) +// MOVSD instruction +instruct storeXD(memory mem, regXD src) %{ + predicate(UseSSE>=2); + match(Set mem (StoreD mem src)); + ins_cost(95); + format %{ "MOVSD $mem,$src" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x11), RegMem(src, mem)); + ins_pipe( pipe_slow ); +%} + +// Store XMM register to memory (single-precision floating point) +// MOVSS instruction +instruct storeX(memory mem, regX src) %{ + predicate(UseSSE>=1); + match(Set mem (StoreF mem src)); + ins_cost(95); + format %{ "MOVSS $mem,$src" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x11), RegMem(src, mem)); + ins_pipe( pipe_slow ); +%} + +// Store Aligned Packed Single Float XMM register to memory +instruct storeA2F(memory mem, regXD src) %{ + predicate(UseSSE>=1); + match(Set mem (Store2F mem src)); + ins_cost(145); + format %{ "MOVQ $mem,$src\t! packed2F" %} + ins_encode( movq_st(mem, src)); + ins_pipe( pipe_slow ); +%} + +// Store Float +instruct storeF( memory mem, regFPR1 src) %{ + predicate(UseSSE==0); + match(Set mem (StoreF mem src)); + + ins_cost(100); + format %{ "FST_S $mem,$src" %} + opcode(0xD9); /* D9 /2 */ + ins_encode( enc_FP_store(mem,src) ); + ins_pipe( fpu_mem_reg ); +%} + +// Store Float does rounding on x86 +instruct storeF_rounded( memory mem, regFPR1 src) %{ + predicate(UseSSE==0); + match(Set mem (StoreF mem (RoundFloat src))); + + ins_cost(100); + format %{ "FST_S $mem,$src\t# round" %} + opcode(0xD9); /* D9 /2 */ + ins_encode( enc_FP_store(mem,src) ); + ins_pipe( fpu_mem_reg ); +%} + +// Store Float does rounding on x86 +instruct storeF_Drounded( memory mem, regDPR1 src) %{ + predicate(UseSSE<=1); + match(Set mem (StoreF mem (ConvD2F src))); + + ins_cost(100); + format %{ "FST_S $mem,$src\t# D-round" %} + opcode(0xD9); /* D9 /2 */ + ins_encode( enc_FP_store(mem,src) ); + ins_pipe( fpu_mem_reg ); +%} + +// Store immediate Float value (it is faster than store from FPU register) +// The instruction usage is guarded by predicate in operand immF(). +instruct storeF_imm( memory mem, immF src) %{ + match(Set mem (StoreF mem src)); + + ins_cost(50); + format %{ "MOV $mem,$src\t# store float" %} + opcode(0xC7); /* C7 /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), Con32F_as_bits( src )); + ins_pipe( ialu_mem_imm ); +%} + +// Store immediate Float value (it is faster than store from XMM register) +// The instruction usage is guarded by predicate in operand immXF(). +instruct storeX_imm( memory mem, immXF src) %{ + match(Set mem (StoreF mem src)); + + ins_cost(50); + format %{ "MOV $mem,$src\t# store float" %} + opcode(0xC7); /* C7 /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), Con32XF_as_bits( src )); + ins_pipe( ialu_mem_imm ); +%} + +// Store Integer to stack slot +instruct storeSSI(stackSlotI dst, eRegI src) %{ + match(Set dst src); + + ins_cost(100); + format %{ "MOV $dst,$src" %} + opcode(0x89); + ins_encode( OpcPRegSS( dst, src ) ); + ins_pipe( ialu_mem_reg ); +%} + +// Store Integer to stack slot +instruct storeSSP(stackSlotP dst, eRegP src) %{ + match(Set dst src); + + ins_cost(100); + format %{ "MOV $dst,$src" %} + opcode(0x89); + ins_encode( OpcPRegSS( dst, src ) ); + ins_pipe( ialu_mem_reg ); +%} + +// Store Long to stack slot +instruct storeSSL(stackSlotL dst, eRegL src) %{ + match(Set dst src); + + ins_cost(200); + format %{ "MOV $dst,$src.lo\n\t" + "MOV $dst+4,$src.hi" %} + opcode(0x89, 0x89); + ins_encode( OpcP, RegMem( src, dst ), OpcS, RegMem_Hi( src, dst ) ); + ins_pipe( ialu_mem_long_reg ); +%} + +//----------MemBar Instructions----------------------------------------------- +// Memory barrier flavors + +instruct membar_acquire() %{ + match(MemBarAcquire); + ins_cost(400); + + size(0); + format %{ "MEMBAR-acquire" %} + ins_encode( enc_membar_acquire ); + ins_pipe(pipe_slow); +%} + +instruct membar_acquire_lock() %{ + match(MemBarAcquire); + predicate(Matcher::prior_fast_lock(n)); + ins_cost(0); + + size(0); + format %{ "MEMBAR-acquire (prior CMPXCHG in FastLock so empty encoding)" %} + ins_encode( ); + ins_pipe(empty); +%} + +instruct membar_release() %{ + match(MemBarRelease); + ins_cost(400); + + size(0); + format %{ "MEMBAR-release" %} + ins_encode( enc_membar_release ); + ins_pipe(pipe_slow); +%} + +instruct membar_release_lock() %{ + match(MemBarRelease); + predicate(Matcher::post_fast_unlock(n)); + ins_cost(0); + + size(0); + format %{ "MEMBAR-release (a FastUnlock follows so empty encoding)" %} + ins_encode( ); + ins_pipe(empty); +%} + +instruct membar_volatile() %{ + match(MemBarVolatile); + ins_cost(400); + + format %{ "MEMBAR-volatile" %} + ins_encode( enc_membar_volatile ); + ins_pipe(pipe_slow); +%} + +instruct unnecessary_membar_volatile() %{ + match(MemBarVolatile); + predicate(Matcher::post_store_load_barrier(n)); + ins_cost(0); + + size(0); + format %{ "MEMBAR-volatile (unnecessary so empty encoding)" %} + ins_encode( ); + ins_pipe(empty); +%} + +//----------Move Instructions-------------------------------------------------- +instruct castX2P(eAXRegP dst, eAXRegI src) %{ + match(Set dst (CastX2P src)); + format %{ "# X2P $dst, $src" %} + ins_encode( /*empty encoding*/ ); + ins_cost(0); + ins_pipe(empty); +%} + +instruct castP2X(eRegI dst, eRegP src ) %{ + match(Set dst (CastP2X src)); + ins_cost(50); + format %{ "MOV $dst, $src\t# CastP2X" %} + ins_encode( enc_Copy( dst, src) ); + ins_pipe( ialu_reg_reg ); +%} + +//----------Conditional Move--------------------------------------------------- +// Conditional move +instruct cmovI_reg(eRegI dst, eRegI src, eFlagsReg cr, cmpOp cop ) %{ + predicate(VM_Version::supports_cmov() ); + match(Set dst (CMoveI (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cop $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cop), RegReg( dst, src ) ); + ins_pipe( pipe_cmov_reg ); +%} + +instruct cmovI_regU( eRegI dst, eRegI src, eFlagsRegU cr, cmpOpU cop ) %{ + predicate(VM_Version::supports_cmov() ); + match(Set dst (CMoveI (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cop $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cop), RegReg( dst, src ) ); + ins_pipe( pipe_cmov_reg ); +%} + +// Conditional move +instruct cmovI_mem(cmpOp cop, eFlagsReg cr, eRegI dst, memory src) %{ + predicate(VM_Version::supports_cmov() ); + match(Set dst (CMoveI (Binary cop cr) (Binary dst (LoadI src)))); + ins_cost(250); + format %{ "CMOV$cop $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cop), RegMem( dst, src ) ); + ins_pipe( pipe_cmov_mem ); +%} + +// Conditional move +instruct cmovI_memu(cmpOpU cop, eFlagsRegU cr, eRegI dst, memory src) %{ + predicate(VM_Version::supports_cmov() ); + match(Set dst (CMoveI (Binary cop cr) (Binary dst (LoadI src)))); + ins_cost(250); + format %{ "CMOV$cop $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cop), RegMem( dst, src ) ); + ins_pipe( pipe_cmov_mem ); +%} + +// Conditional move +instruct cmovP_reg(eRegP dst, eRegP src, eFlagsReg cr, cmpOp cop ) %{ + predicate(VM_Version::supports_cmov() ); + match(Set dst (CMoveP (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cop $dst,$src\t# ptr" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cop), RegReg( dst, src ) ); + ins_pipe( pipe_cmov_reg ); +%} + +// Conditional move (non-P6 version) +// Note: a CMoveP is generated for stubs and native wrappers +// regardless of whether we are on a P6, so we +// emulate a cmov here +instruct cmovP_reg_nonP6(eRegP dst, eRegP src, eFlagsReg cr, cmpOp cop ) %{ + match(Set dst (CMoveP (Binary cop cr) (Binary dst src))); + ins_cost(300); + format %{ "Jn$cop skip\n\t" + "MOV $dst,$src\t# pointer\n" + "skip:" %} + opcode(0x8b); + ins_encode( enc_cmov_branch(cop, 0x2), OpcP, RegReg(dst, src)); + ins_pipe( pipe_cmov_reg ); +%} + +// Conditional move +instruct cmovP_regU(eRegP dst, eRegP src, eFlagsRegU cr, cmpOpU cop ) %{ + predicate(VM_Version::supports_cmov() ); + match(Set dst (CMoveP (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cop $dst,$src\t# ptr" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cop), RegReg( dst, src ) ); + ins_pipe( pipe_cmov_reg ); +%} + +// DISABLED: Requires the ADLC to emit a bottom_type call that +// correctly meets the two pointer arguments; one is an incoming +// register but the other is a memory operand. ALSO appears to +// be buggy with implicit null checks. +// +//// Conditional move +//instruct cmovP_mem(cmpOp cop, eFlagsReg cr, eRegP dst, memory src) %{ +// predicate(VM_Version::supports_cmov() ); +// match(Set dst (CMoveP (Binary cop cr) (Binary dst (LoadP src)))); +// ins_cost(250); +// format %{ "CMOV$cop $dst,$src\t# ptr" %} +// opcode(0x0F,0x40); +// ins_encode( enc_cmov(cop), RegMem( dst, src ) ); +// ins_pipe( pipe_cmov_mem ); +//%} +// +//// Conditional move +//instruct cmovP_memU(cmpOpU cop, eFlagsRegU cr, eRegP dst, memory src) %{ +// predicate(VM_Version::supports_cmov() ); +// match(Set dst (CMoveP (Binary cop cr) (Binary dst (LoadP src)))); +// ins_cost(250); +// format %{ "CMOV$cop $dst,$src\t# ptr" %} +// opcode(0x0F,0x40); +// ins_encode( enc_cmov(cop), RegMem( dst, src ) ); +// ins_pipe( pipe_cmov_mem ); +//%} + +// Conditional move +instruct fcmovD_regU(cmpOp_fcmov cop, eFlagsRegU cr, regDPR1 dst, regD src) %{ + predicate(UseSSE<=1); + match(Set dst (CMoveD (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "FCMOV$cop $dst,$src\t# double" %} + opcode(0xDA); + ins_encode( enc_cmov_d(cop,src) ); + ins_pipe( pipe_cmovD_reg ); +%} + +// Conditional move +instruct fcmovF_regU(cmpOp_fcmov cop, eFlagsRegU cr, regFPR1 dst, regF src) %{ + predicate(UseSSE==0); + match(Set dst (CMoveF (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "FCMOV$cop $dst,$src\t# float" %} + opcode(0xDA); + ins_encode( enc_cmov_d(cop,src) ); + ins_pipe( pipe_cmovD_reg ); +%} + +// Float CMOV on Intel doesn't handle *signed* compares, only unsigned. +instruct fcmovD_regS(cmpOp cop, eFlagsReg cr, regD dst, regD src) %{ + predicate(UseSSE<=1); + match(Set dst (CMoveD (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "Jn$cop skip\n\t" + "MOV $dst,$src\t# double\n" + "skip:" %} + opcode (0xdd, 0x3); /* DD D8+i or DD /3 */ + ins_encode( enc_cmov_branch( cop, 0x4 ), Push_Reg_D(src), OpcP, RegOpc(dst) ); + ins_pipe( pipe_cmovD_reg ); +%} + +// Float CMOV on Intel doesn't handle *signed* compares, only unsigned. +instruct fcmovF_regS(cmpOp cop, eFlagsReg cr, regF dst, regF src) %{ + predicate(UseSSE==0); + match(Set dst (CMoveF (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "Jn$cop skip\n\t" + "MOV $dst,$src\t# float\n" + "skip:" %} + opcode (0xdd, 0x3); /* DD D8+i or DD /3 */ + ins_encode( enc_cmov_branch( cop, 0x4 ), Push_Reg_F(src), OpcP, RegOpc(dst) ); + ins_pipe( pipe_cmovD_reg ); +%} + +// No CMOVE with SSE/SSE2 +instruct fcmovX_regS(cmpOp cop, eFlagsReg cr, regX dst, regX src) %{ + predicate (UseSSE>=1); + match(Set dst (CMoveF (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "Jn$cop skip\n\t" + "MOVSS $dst,$src\t# float\n" + "skip:" %} + ins_encode %{ + Label skip; + // Invert sense of branch from sense of CMOV + __ jccb((Assembler::Condition)($cop$$cmpcode^1), skip); + __ movflt($dst$$XMMRegister, $src$$XMMRegister); + __ bind(skip); + %} + ins_pipe( pipe_slow ); +%} + +// No CMOVE with SSE/SSE2 +instruct fcmovXD_regS(cmpOp cop, eFlagsReg cr, regXD dst, regXD src) %{ + predicate (UseSSE>=2); + match(Set dst (CMoveD (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "Jn$cop skip\n\t" + "MOVSD $dst,$src\t# float\n" + "skip:" %} + ins_encode %{ + Label skip; + // Invert sense of branch from sense of CMOV + __ jccb((Assembler::Condition)($cop$$cmpcode^1), skip); + __ movdbl($dst$$XMMRegister, $src$$XMMRegister); + __ bind(skip); + %} + ins_pipe( pipe_slow ); +%} + +// unsigned version +instruct fcmovX_regU(cmpOpU cop, eFlagsRegU cr, regX dst, regX src) %{ + predicate (UseSSE>=1); + match(Set dst (CMoveF (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "Jn$cop skip\n\t" + "MOVSS $dst,$src\t# float\n" + "skip:" %} + ins_encode %{ + Label skip; + // Invert sense of branch from sense of CMOV + __ jccb((Assembler::Condition)($cop$$cmpcode^1), skip); + __ movflt($dst$$XMMRegister, $src$$XMMRegister); + __ bind(skip); + %} + ins_pipe( pipe_slow ); +%} + +// unsigned version +instruct fcmovXD_regU(cmpOpU cop, eFlagsRegU cr, regXD dst, regXD src) %{ + predicate (UseSSE>=2); + match(Set dst (CMoveD (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "Jn$cop skip\n\t" + "MOVSD $dst,$src\t# float\n" + "skip:" %} + ins_encode %{ + Label skip; + // Invert sense of branch from sense of CMOV + __ jccb((Assembler::Condition)($cop$$cmpcode^1), skip); + __ movdbl($dst$$XMMRegister, $src$$XMMRegister); + __ bind(skip); + %} + ins_pipe( pipe_slow ); +%} + +instruct cmovL_reg(cmpOp cop, eFlagsReg cr, eRegL dst, eRegL src) %{ + predicate(VM_Version::supports_cmov() ); + match(Set dst (CMoveL (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cop $dst.lo,$src.lo\n\t" + "CMOV$cop $dst.hi,$src.hi" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cop), RegReg_Lo2( dst, src ), enc_cmov(cop), RegReg_Hi2( dst, src ) ); + ins_pipe( pipe_cmov_reg_long ); +%} + +instruct cmovL_regU(cmpOpU cop, eFlagsRegU cr, eRegL dst, eRegL src) %{ + predicate(VM_Version::supports_cmov() ); + match(Set dst (CMoveL (Binary cop cr) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cop $dst.lo,$src.lo\n\t" + "CMOV$cop $dst.hi,$src.hi" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cop), RegReg_Lo2( dst, src ), enc_cmov(cop), RegReg_Hi2( dst, src ) ); + ins_pipe( pipe_cmov_reg_long ); +%} + +//----------Arithmetic Instructions-------------------------------------------- +//----------Addition Instructions---------------------------------------------- +// Integer Addition Instructions +instruct addI_eReg(eRegI dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (AddI dst src)); + effect(KILL cr); + + size(2); + format %{ "ADD $dst,$src" %} + opcode(0x03); + ins_encode( OpcP, RegReg( dst, src) ); + ins_pipe( ialu_reg_reg ); +%} + +instruct addI_eReg_imm(eRegI dst, immI src, eFlagsReg cr) %{ + match(Set dst (AddI dst src)); + effect(KILL cr); + + format %{ "ADD $dst,$src" %} + opcode(0x81, 0x00); /* /0 id */ + ins_encode( OpcSErm( dst, src ), Con8or32( src ) ); + ins_pipe( ialu_reg ); +%} + +instruct incI_eReg(eRegI dst, immI1 src, eFlagsReg cr) %{ + predicate(UseIncDec); + match(Set dst (AddI dst src)); + effect(KILL cr); + + size(1); + format %{ "INC $dst" %} + opcode(0x40); /* */ + ins_encode( Opc_plus( primary, dst ) ); + ins_pipe( ialu_reg ); +%} + +instruct leaI_eReg_immI(eRegI dst, eRegI src0, immI src1) %{ + match(Set dst (AddI src0 src1)); + ins_cost(110); + + format %{ "LEA $dst,[$src0 + $src1]" %} + opcode(0x8D); /* 0x8D /r */ + ins_encode( OpcP, RegLea( dst, src0, src1 ) ); + ins_pipe( ialu_reg_reg ); +%} + +instruct leaP_eReg_immI(eRegP dst, eRegP src0, immI src1) %{ + match(Set dst (AddP src0 src1)); + ins_cost(110); + + format %{ "LEA $dst,[$src0 + $src1]\t# ptr" %} + opcode(0x8D); /* 0x8D /r */ + ins_encode( OpcP, RegLea( dst, src0, src1 ) ); + ins_pipe( ialu_reg_reg ); +%} + +instruct decI_eReg(eRegI dst, immI_M1 src, eFlagsReg cr) %{ + predicate(UseIncDec); + match(Set dst (AddI dst src)); + effect(KILL cr); + + size(1); + format %{ "DEC $dst" %} + opcode(0x48); /* */ + ins_encode( Opc_plus( primary, dst ) ); + ins_pipe( ialu_reg ); +%} + +instruct addP_eReg(eRegP dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (AddP dst src)); + effect(KILL cr); + + size(2); + format %{ "ADD $dst,$src" %} + opcode(0x03); + ins_encode( OpcP, RegReg( dst, src) ); + ins_pipe( ialu_reg_reg ); +%} + +instruct addP_eReg_imm(eRegP dst, immI src, eFlagsReg cr) %{ + match(Set dst (AddP dst src)); + effect(KILL cr); + + format %{ "ADD $dst,$src" %} + opcode(0x81,0x00); /* Opcode 81 /0 id */ + // ins_encode( RegImm( dst, src) ); + ins_encode( OpcSErm( dst, src ), Con8or32( src ) ); + ins_pipe( ialu_reg ); +%} + +instruct addI_eReg_mem(eRegI dst, memory src, eFlagsReg cr) %{ + match(Set dst (AddI dst (LoadI src))); + effect(KILL cr); + + ins_cost(125); + format %{ "ADD $dst,$src" %} + opcode(0x03); + ins_encode( OpcP, RegMem( dst, src) ); + ins_pipe( ialu_reg_mem ); +%} + +instruct addI_mem_eReg(memory dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (AddI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "ADD $dst,$src" %} + opcode(0x01); /* Opcode 01 /r */ + ins_encode( OpcP, RegMem( src, dst ) ); + ins_pipe( ialu_mem_reg ); +%} + +// Add Memory with Immediate +instruct addI_mem_imm(memory dst, immI src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (AddI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "ADD $dst,$src" %} + opcode(0x81); /* Opcode 81 /0 id */ + ins_encode( OpcSE( src ), RMopc_Mem(0x00,dst), Con8or32( src ) ); + ins_pipe( ialu_mem_imm ); +%} + +instruct incI_mem(memory dst, immI1 src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (AddI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "INC $dst" %} + opcode(0xFF); /* Opcode FF /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,dst)); + ins_pipe( ialu_mem_imm ); +%} + +instruct decI_mem(memory dst, immI_M1 src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (AddI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "DEC $dst" %} + opcode(0xFF); /* Opcode FF /1 */ + ins_encode( OpcP, RMopc_Mem(0x01,dst)); + ins_pipe( ialu_mem_imm ); +%} + + +instruct checkCastPP( eRegP dst ) %{ + match(Set dst (CheckCastPP dst)); + + size(0); + format %{ "#checkcastPP of $dst" %} + ins_encode( /*empty encoding*/ ); + ins_pipe( empty ); +%} + +instruct castPP( eRegP dst ) %{ + match(Set dst (CastPP dst)); + format %{ "#castPP of $dst" %} + ins_encode( /*empty encoding*/ ); + ins_pipe( empty ); +%} + +instruct castII( eRegI dst ) %{ + match(Set dst (CastII dst)); + format %{ "#castII of $dst" %} + ins_encode( /*empty encoding*/ ); + ins_cost(0); + ins_pipe( empty ); +%} + + +// Load-locked - same as a regular pointer load when used with compare-swap +instruct loadPLocked(eRegP dst, memory mem) %{ + match(Set dst (LoadPLocked mem)); + + ins_cost(125); + format %{ "MOV $dst,$mem\t# Load ptr. locked" %} + opcode(0x8B); + ins_encode( OpcP, RegMem(dst,mem)); + ins_pipe( ialu_reg_mem ); +%} + +// LoadLong-locked - same as a volatile long load when used with compare-swap +instruct loadLLocked(stackSlotL dst, load_long_memory mem) %{ + predicate(UseSSE<=1); + match(Set dst (LoadLLocked mem)); + + ins_cost(200); + format %{ "FILD $mem\t# Atomic volatile long load\n\t" + "FISTp $dst" %} + ins_encode(enc_loadL_volatile(mem,dst)); + ins_pipe( fpu_reg_mem ); +%} + +instruct loadLX_Locked(stackSlotL dst, load_long_memory mem, regXD tmp) %{ + predicate(UseSSE>=2); + match(Set dst (LoadLLocked mem)); + effect(TEMP tmp); + ins_cost(180); + format %{ "MOVSD $tmp,$mem\t# Atomic volatile long load\n\t" + "MOVSD $dst,$tmp" %} + ins_encode(enc_loadLX_volatile(mem, dst, tmp)); + ins_pipe( pipe_slow ); +%} + +instruct loadLX_reg_Locked(eRegL dst, load_long_memory mem, regXD tmp) %{ + predicate(UseSSE>=2); + match(Set dst (LoadLLocked mem)); + effect(TEMP tmp); + ins_cost(160); + format %{ "MOVSD $tmp,$mem\t# Atomic volatile long load\n\t" + "MOVD $dst.lo,$tmp\n\t" + "PSRLQ $tmp,32\n\t" + "MOVD $dst.hi,$tmp" %} + ins_encode(enc_loadLX_reg_volatile(mem, dst, tmp)); + ins_pipe( pipe_slow ); +%} + +// Conditional-store of the updated heap-top. +// Used during allocation of the shared heap. +// Sets flags (EQ) on success. Implemented with a CMPXCHG on Intel. +instruct storePConditional( memory heap_top_ptr, eAXRegP oldval, eRegP newval, eFlagsReg cr ) %{ + match(Set cr (StorePConditional heap_top_ptr (Binary oldval newval))); + // EAX is killed if there is contention, but then it's also unused. + // In the common case of no contention, EAX holds the new oop address. + format %{ "CMPXCHG $heap_top_ptr,$newval\t# If EAX==$heap_top_ptr Then store $newval into $heap_top_ptr" %} + ins_encode( lock_prefix, Opcode(0x0F), Opcode(0xB1), RegMem(newval,heap_top_ptr) ); + ins_pipe( pipe_cmpxchg ); +%} + +// Conditional-store of a long value +// Returns a boolean value (0/1) on success. Implemented with a CMPXCHG8 on Intel. +// mem_ptr can actually be in either ESI or EDI +instruct storeLConditional( eRegI res, eSIRegP mem_ptr, eADXRegL oldval, eBCXRegL newval, eFlagsReg cr ) %{ + match(Set res (StoreLConditional mem_ptr (Binary oldval newval))); + effect(KILL cr); + // EDX:EAX is killed if there is contention, but then it's also unused. + // In the common case of no contention, EDX:EAX holds the new oop address. + format %{ "CMPXCHG8 [$mem_ptr],$newval\t# If EDX:EAX==[$mem_ptr] Then store $newval into [$mem_ptr]\n\t" + "MOV $res,0\n\t" + "JNE,s fail\n\t" + "MOV $res,1\n" + "fail:" %} + ins_encode( enc_cmpxchg8(mem_ptr), + enc_flags_ne_to_boolean(res) ); + ins_pipe( pipe_cmpxchg ); +%} + +// Conditional-store of a long value +// ZF flag is set on success, reset otherwise. Implemented with a CMPXCHG8 on Intel. +// mem_ptr can actually be in either ESI or EDI +instruct storeLConditional_flags( eSIRegP mem_ptr, eADXRegL oldval, eBCXRegL newval, eFlagsReg cr, immI0 zero ) %{ + match(Set cr (CmpI (StoreLConditional mem_ptr (Binary oldval newval)) zero)); + // EDX:EAX is killed if there is contention, but then it's also unused. + // In the common case of no contention, EDX:EAX holds the new oop address. + format %{ "CMPXCHG8 [$mem_ptr],$newval\t# If EAX==[$mem_ptr] Then store $newval into [$mem_ptr]\n\t" %} + ins_encode( enc_cmpxchg8(mem_ptr) ); + ins_pipe( pipe_cmpxchg ); +%} + +// No flag versions for CompareAndSwap{P,I,L} because matcher can't match them + +instruct compareAndSwapL( eRegI res, eSIRegP mem_ptr, eADXRegL oldval, eBCXRegL newval, eFlagsReg cr ) %{ + match(Set res (CompareAndSwapL mem_ptr (Binary oldval newval))); + effect(KILL cr, KILL oldval); + format %{ "CMPXCHG8 [$mem_ptr],$newval\t# If EDX:EAX==[$mem_ptr] Then store $newval into [$mem_ptr]\n\t" + "MOV $res,0\n\t" + "JNE,s fail\n\t" + "MOV $res,1\n" + "fail:" %} + ins_encode( enc_cmpxchg8(mem_ptr), + enc_flags_ne_to_boolean(res) ); + ins_pipe( pipe_cmpxchg ); +%} + +instruct compareAndSwapP( eRegI res, pRegP mem_ptr, eAXRegP oldval, eCXRegP newval, eFlagsReg cr) %{ + match(Set res (CompareAndSwapP mem_ptr (Binary oldval newval))); + effect(KILL cr, KILL oldval); + format %{ "CMPXCHG [$mem_ptr],$newval\t# If EAX==[$mem_ptr] Then store $newval into [$mem_ptr]\n\t" + "MOV $res,0\n\t" + "JNE,s fail\n\t" + "MOV $res,1\n" + "fail:" %} + ins_encode( enc_cmpxchg(mem_ptr), enc_flags_ne_to_boolean(res) ); + ins_pipe( pipe_cmpxchg ); +%} + +instruct compareAndSwapI( eRegI res, pRegP mem_ptr, eAXRegI oldval, eCXRegI newval, eFlagsReg cr) %{ + match(Set res (CompareAndSwapI mem_ptr (Binary oldval newval))); + effect(KILL cr, KILL oldval); + format %{ "CMPXCHG [$mem_ptr],$newval\t# If EAX==[$mem_ptr] Then store $newval into [$mem_ptr]\n\t" + "MOV $res,0\n\t" + "JNE,s fail\n\t" + "MOV $res,1\n" + "fail:" %} + ins_encode( enc_cmpxchg(mem_ptr), enc_flags_ne_to_boolean(res) ); + ins_pipe( pipe_cmpxchg ); +%} + +//----------Subtraction Instructions------------------------------------------- +// Integer Subtraction Instructions +instruct subI_eReg(eRegI dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (SubI dst src)); + effect(KILL cr); + + size(2); + format %{ "SUB $dst,$src" %} + opcode(0x2B); + ins_encode( OpcP, RegReg( dst, src) ); + ins_pipe( ialu_reg_reg ); +%} + +instruct subI_eReg_imm(eRegI dst, immI src, eFlagsReg cr) %{ + match(Set dst (SubI dst src)); + effect(KILL cr); + + format %{ "SUB $dst,$src" %} + opcode(0x81,0x05); /* Opcode 81 /5 */ + // ins_encode( RegImm( dst, src) ); + ins_encode( OpcSErm( dst, src ), Con8or32( src ) ); + ins_pipe( ialu_reg ); +%} + +instruct subI_eReg_mem(eRegI dst, memory src, eFlagsReg cr) %{ + match(Set dst (SubI dst (LoadI src))); + effect(KILL cr); + + ins_cost(125); + format %{ "SUB $dst,$src" %} + opcode(0x2B); + ins_encode( OpcP, RegMem( dst, src) ); + ins_pipe( ialu_reg_mem ); +%} + +instruct subI_mem_eReg(memory dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (SubI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "SUB $dst,$src" %} + opcode(0x29); /* Opcode 29 /r */ + ins_encode( OpcP, RegMem( src, dst ) ); + ins_pipe( ialu_mem_reg ); +%} + +// Subtract from a pointer +instruct subP_eReg(eRegP dst, eRegI src, immI0 zero, eFlagsReg cr) %{ + match(Set dst (AddP dst (SubI zero src))); + effect(KILL cr); + + size(2); + format %{ "SUB $dst,$src" %} + opcode(0x2B); + ins_encode( OpcP, RegReg( dst, src) ); + ins_pipe( ialu_reg_reg ); +%} + +instruct negI_eReg(eRegI dst, immI0 zero, eFlagsReg cr) %{ + match(Set dst (SubI zero dst)); + effect(KILL cr); + + size(2); + format %{ "NEG $dst" %} + opcode(0xF7,0x03); // Opcode F7 /3 + ins_encode( OpcP, RegOpc( dst ) ); + ins_pipe( ialu_reg ); +%} + + +//----------Multiplication/Division Instructions------------------------------- +// Integer Multiplication Instructions +// Multiply Register +instruct mulI_eReg(eRegI dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (MulI dst src)); + effect(KILL cr); + + size(3); + ins_cost(300); + format %{ "IMUL $dst,$src" %} + opcode(0xAF, 0x0F); + ins_encode( OpcS, OpcP, RegReg( dst, src) ); + ins_pipe( ialu_reg_reg_alu0 ); +%} + +// Multiply 32-bit Immediate +instruct mulI_eReg_imm(eRegI dst, eRegI src, immI imm, eFlagsReg cr) %{ + match(Set dst (MulI src imm)); + effect(KILL cr); + + ins_cost(300); + format %{ "IMUL $dst,$src,$imm" %} + opcode(0x69); /* 69 /r id */ + ins_encode( OpcSE(imm), RegReg( dst, src ), Con8or32( imm ) ); + ins_pipe( ialu_reg_reg_alu0 ); +%} + +instruct loadConL_low_only(eADXRegL_low_only dst, immL32 src, eFlagsReg cr) %{ + match(Set dst src); + effect(KILL cr); + + // Note that this is artificially increased to make it more expensive than loadConL + ins_cost(250); + format %{ "MOV EAX,$src\t// low word only" %} + opcode(0xB8); + ins_encode( LdImmL_Lo(dst, src) ); + ins_pipe( ialu_reg_fat ); +%} + +// Multiply by 32-bit Immediate, taking the shifted high order results +// (special case for shift by 32) +instruct mulI_imm_high(eDXRegI dst, nadxRegI src1, eADXRegL_low_only src2, immI_32 cnt, eFlagsReg cr) %{ + match(Set dst (ConvL2I (RShiftL (MulL (ConvI2L src1) src2) cnt))); + predicate( _kids[0]->_kids[0]->_kids[1]->_leaf->Opcode() == Op_ConL && + _kids[0]->_kids[0]->_kids[1]->_leaf->as_Type()->type()->is_long()->get_con() >= min_jint && + _kids[0]->_kids[0]->_kids[1]->_leaf->as_Type()->type()->is_long()->get_con() <= max_jint ); + effect(USE src1, KILL cr); + + // Note that this is adjusted by 150 to compensate for the overcosting of loadConL_low_only + ins_cost(0*100 + 1*400 - 150); + format %{ "IMUL EDX:EAX,$src1" %} + ins_encode( multiply_con_and_shift_high( dst, src1, src2, cnt, cr ) ); + ins_pipe( pipe_slow ); +%} + +// Multiply by 32-bit Immediate, taking the shifted high order results +instruct mulI_imm_RShift_high(eDXRegI dst, nadxRegI src1, eADXRegL_low_only src2, immI_32_63 cnt, eFlagsReg cr) %{ + match(Set dst (ConvL2I (RShiftL (MulL (ConvI2L src1) src2) cnt))); + predicate( _kids[0]->_kids[0]->_kids[1]->_leaf->Opcode() == Op_ConL && + _kids[0]->_kids[0]->_kids[1]->_leaf->as_Type()->type()->is_long()->get_con() >= min_jint && + _kids[0]->_kids[0]->_kids[1]->_leaf->as_Type()->type()->is_long()->get_con() <= max_jint ); + effect(USE src1, KILL cr); + + // Note that this is adjusted by 150 to compensate for the overcosting of loadConL_low_only + ins_cost(1*100 + 1*400 - 150); + format %{ "IMUL EDX:EAX,$src1\n\t" + "SAR EDX,$cnt-32" %} + ins_encode( multiply_con_and_shift_high( dst, src1, src2, cnt, cr ) ); + ins_pipe( pipe_slow ); +%} + +// Multiply Memory 32-bit Immediate +instruct mulI_mem_imm(eRegI dst, memory src, immI imm, eFlagsReg cr) %{ + match(Set dst (MulI (LoadI src) imm)); + effect(KILL cr); + + ins_cost(300); + format %{ "IMUL $dst,$src,$imm" %} + opcode(0x69); /* 69 /r id */ + ins_encode( OpcSE(imm), RegMem( dst, src ), Con8or32( imm ) ); + ins_pipe( ialu_reg_mem_alu0 ); +%} + +// Multiply Memory +instruct mulI(eRegI dst, memory src, eFlagsReg cr) %{ + match(Set dst (MulI dst (LoadI src))); + effect(KILL cr); + + ins_cost(350); + format %{ "IMUL $dst,$src" %} + opcode(0xAF, 0x0F); + ins_encode( OpcS, OpcP, RegMem( dst, src) ); + ins_pipe( ialu_reg_mem_alu0 ); +%} + +// Multiply Register Int to Long +instruct mulI2L(eADXRegL dst, eAXRegI src, nadxRegI src1, eFlagsReg flags) %{ + // Basic Idea: long = (long)int * (long)int + match(Set dst (MulL (ConvI2L src) (ConvI2L src1))); + effect(DEF dst, USE src, USE src1, KILL flags); + + ins_cost(300); + format %{ "IMUL $dst,$src1" %} + + ins_encode( long_int_multiply( dst, src1 ) ); + ins_pipe( ialu_reg_reg_alu0 ); +%} + +instruct mulIS_eReg(eADXRegL dst, immL_32bits mask, eFlagsReg flags, eAXRegI src, nadxRegI src1) %{ + // Basic Idea: long = (int & 0xffffffffL) * (int & 0xffffffffL) + match(Set dst (MulL (AndL (ConvI2L src) mask) (AndL (ConvI2L src1) mask))); + effect(KILL flags); + + ins_cost(300); + format %{ "MUL $dst,$src1" %} + + ins_encode( long_uint_multiply(dst, src1) ); + ins_pipe( ialu_reg_reg_alu0 ); +%} + +// Multiply Register Long +instruct mulL_eReg(eADXRegL dst, eRegL src, eRegI tmp, eFlagsReg cr) %{ + match(Set dst (MulL dst src)); + effect(KILL cr, TEMP tmp); + ins_cost(4*100+3*400); +// Basic idea: lo(result) = lo(x_lo * y_lo) +// hi(result) = hi(x_lo * y_lo) + lo(x_hi * y_lo) + lo(x_lo * y_hi) + format %{ "MOV $tmp,$src.lo\n\t" + "IMUL $tmp,EDX\n\t" + "MOV EDX,$src.hi\n\t" + "IMUL EDX,EAX\n\t" + "ADD $tmp,EDX\n\t" + "MUL EDX:EAX,$src.lo\n\t" + "ADD EDX,$tmp" %} + ins_encode( long_multiply( dst, src, tmp ) ); + ins_pipe( pipe_slow ); +%} + +// Multiply Register Long by small constant +instruct mulL_eReg_con(eADXRegL dst, immL_127 src, eRegI tmp, eFlagsReg cr) %{ + match(Set dst (MulL dst src)); + effect(KILL cr, TEMP tmp); + ins_cost(2*100+2*400); + size(12); +// Basic idea: lo(result) = lo(src * EAX) +// hi(result) = hi(src * EAX) + lo(src * EDX) + format %{ "IMUL $tmp,EDX,$src\n\t" + "MOV EDX,$src\n\t" + "MUL EDX\t# EDX*EAX -> EDX:EAX\n\t" + "ADD EDX,$tmp" %} + ins_encode( long_multiply_con( dst, src, tmp ) ); + ins_pipe( pipe_slow ); +%} + +// Integer DIV with Register +instruct divI_eReg(eAXRegI rax, eDXRegI rdx, eCXRegI div, eFlagsReg cr) %{ + match(Set rax (DivI rax div)); + effect(KILL rdx, KILL cr); + size(26); + ins_cost(30*100+10*100); + format %{ "CMP EAX,0x80000000\n\t" + "JNE,s normal\n\t" + "XOR EDX,EDX\n\t" + "CMP ECX,-1\n\t" + "JE,s done\n" + "normal: CDQ\n\t" + "IDIV $div\n\t" + "done:" %} + opcode(0xF7, 0x7); /* Opcode F7 /7 */ + ins_encode( cdq_enc, OpcP, RegOpc(div) ); + ins_pipe( ialu_reg_reg_alu0 ); +%} + +// Divide Register Long +instruct divL_eReg( eADXRegL dst, eRegL src1, eRegL src2, eFlagsReg cr, eCXRegI cx, eBXRegI bx ) %{ + match(Set dst (DivL src1 src2)); + effect( KILL cr, KILL cx, KILL bx ); + ins_cost(10000); + format %{ "PUSH $src1.hi\n\t" + "PUSH $src1.lo\n\t" + "PUSH $src2.hi\n\t" + "PUSH $src2.lo\n\t" + "CALL SharedRuntime::ldiv\n\t" + "ADD ESP,16" %} + ins_encode( long_div(src1,src2) ); + ins_pipe( pipe_slow ); +%} + +// Integer DIVMOD with Register, both quotient and mod results +instruct divModI_eReg_divmod(eAXRegI rax, eDXRegI rdx, eCXRegI div, eFlagsReg cr) %{ + match(DivModI rax div); + effect(KILL cr); + size(26); + ins_cost(30*100+10*100); + format %{ "CMP EAX,0x80000000\n\t" + "JNE,s normal\n\t" + "XOR EDX,EDX\n\t" + "CMP ECX,-1\n\t" + "JE,s done\n" + "normal: CDQ\n\t" + "IDIV $div\n\t" + "done:" %} + opcode(0xF7, 0x7); /* Opcode F7 /7 */ + ins_encode( cdq_enc, OpcP, RegOpc(div) ); + ins_pipe( pipe_slow ); +%} + +// Integer MOD with Register +instruct modI_eReg(eDXRegI rdx, eAXRegI rax, eCXRegI div, eFlagsReg cr) %{ + match(Set rdx (ModI rax div)); + effect(KILL rax, KILL cr); + + size(26); + ins_cost(300); + format %{ "CDQ\n\t" + "IDIV $div" %} + opcode(0xF7, 0x7); /* Opcode F7 /7 */ + ins_encode( cdq_enc, OpcP, RegOpc(div) ); + ins_pipe( ialu_reg_reg_alu0 ); +%} + +// Remainder Register Long +instruct modL_eReg( eADXRegL dst, eRegL src1, eRegL src2, eFlagsReg cr, eCXRegI cx, eBXRegI bx ) %{ + match(Set dst (ModL src1 src2)); + effect( KILL cr, KILL cx, KILL bx ); + ins_cost(10000); + format %{ "PUSH $src1.hi\n\t" + "PUSH $src1.lo\n\t" + "PUSH $src2.hi\n\t" + "PUSH $src2.lo\n\t" + "CALL SharedRuntime::lrem\n\t" + "ADD ESP,16" %} + ins_encode( long_mod(src1,src2) ); + ins_pipe( pipe_slow ); +%} + +// Integer Shift Instructions +// Shift Left by one +instruct shlI_eReg_1(eRegI dst, immI1 shift, eFlagsReg cr) %{ + match(Set dst (LShiftI dst shift)); + effect(KILL cr); + + size(2); + format %{ "SHL $dst,$shift" %} + opcode(0xD1, 0x4); /* D1 /4 */ + ins_encode( OpcP, RegOpc( dst ) ); + ins_pipe( ialu_reg ); +%} + +// Shift Left by 8-bit immediate +instruct salI_eReg_imm(eRegI dst, immI8 shift, eFlagsReg cr) %{ + match(Set dst (LShiftI dst shift)); + effect(KILL cr); + + size(3); + format %{ "SHL $dst,$shift" %} + opcode(0xC1, 0x4); /* C1 /4 ib */ + ins_encode( RegOpcImm( dst, shift) ); + ins_pipe( ialu_reg ); +%} + +// Shift Left by variable +instruct salI_eReg_CL(eRegI dst, eCXRegI shift, eFlagsReg cr) %{ + match(Set dst (LShiftI dst shift)); + effect(KILL cr); + + size(2); + format %{ "SHL $dst,$shift" %} + opcode(0xD3, 0x4); /* D3 /4 */ + ins_encode( OpcP, RegOpc( dst ) ); + ins_pipe( ialu_reg_reg ); +%} + +// Arithmetic shift right by one +instruct sarI_eReg_1(eRegI dst, immI1 shift, eFlagsReg cr) %{ + match(Set dst (RShiftI dst shift)); + effect(KILL cr); + + size(2); + format %{ "SAR $dst,$shift" %} + opcode(0xD1, 0x7); /* D1 /7 */ + ins_encode( OpcP, RegOpc( dst ) ); + ins_pipe( ialu_reg ); +%} + +// Arithmetic shift right by one +instruct sarI_mem_1(memory dst, immI1 shift, eFlagsReg cr) %{ + match(Set dst (StoreI dst (RShiftI (LoadI dst) shift))); + effect(KILL cr); + format %{ "SAR $dst,$shift" %} + opcode(0xD1, 0x7); /* D1 /7 */ + ins_encode( OpcP, RMopc_Mem(secondary,dst) ); + ins_pipe( ialu_mem_imm ); +%} + +// Arithmetic Shift Right by 8-bit immediate +instruct sarI_eReg_imm(eRegI dst, immI8 shift, eFlagsReg cr) %{ + match(Set dst (RShiftI dst shift)); + effect(KILL cr); + + size(3); + format %{ "SAR $dst,$shift" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode( RegOpcImm( dst, shift ) ); + ins_pipe( ialu_mem_imm ); +%} + +// Arithmetic Shift Right by 8-bit immediate +instruct sarI_mem_imm(memory dst, immI8 shift, eFlagsReg cr) %{ + match(Set dst (StoreI dst (RShiftI (LoadI dst) shift))); + effect(KILL cr); + + format %{ "SAR $dst,$shift" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode( OpcP, RMopc_Mem(secondary, dst ), Con8or32( shift ) ); + ins_pipe( ialu_mem_imm ); +%} + +// Arithmetic Shift Right by variable +instruct sarI_eReg_CL(eRegI dst, eCXRegI shift, eFlagsReg cr) %{ + match(Set dst (RShiftI dst shift)); + effect(KILL cr); + + size(2); + format %{ "SAR $dst,$shift" %} + opcode(0xD3, 0x7); /* D3 /7 */ + ins_encode( OpcP, RegOpc( dst ) ); + ins_pipe( ialu_reg_reg ); +%} + +// Logical shift right by one +instruct shrI_eReg_1(eRegI dst, immI1 shift, eFlagsReg cr) %{ + match(Set dst (URShiftI dst shift)); + effect(KILL cr); + + size(2); + format %{ "SHR $dst,$shift" %} + opcode(0xD1, 0x5); /* D1 /5 */ + ins_encode( OpcP, RegOpc( dst ) ); + ins_pipe( ialu_reg ); +%} + +// Logical Shift Right by 8-bit immediate +instruct shrI_eReg_imm(eRegI dst, immI8 shift, eFlagsReg cr) %{ + match(Set dst (URShiftI dst shift)); + effect(KILL cr); + + size(3); + format %{ "SHR $dst,$shift" %} + opcode(0xC1, 0x5); /* C1 /5 ib */ + ins_encode( RegOpcImm( dst, shift) ); + ins_pipe( ialu_reg ); +%} + +// Logical Shift Right by 24, followed by Arithmetic Shift Left by 24. +// This idiom is used by the compiler for the i2b bytecode. +instruct i2b(eRegI dst, xRegI src, immI_24 twentyfour, eFlagsReg cr) %{ + match(Set dst (RShiftI (LShiftI src twentyfour) twentyfour)); + effect(KILL cr); + + size(3); + format %{ "MOVSX $dst,$src :8" %} + opcode(0xBE, 0x0F); + ins_encode( OpcS, OpcP, RegReg( dst, src)); + ins_pipe( ialu_reg_reg ); +%} + +// Logical Shift Right by 16, followed by Arithmetic Shift Left by 16. +// This idiom is used by the compiler the i2s bytecode. +instruct i2s(eRegI dst, xRegI src, immI_16 sixteen, eFlagsReg cr) %{ + match(Set dst (RShiftI (LShiftI src sixteen) sixteen)); + effect(KILL cr); + + size(3); + format %{ "MOVSX $dst,$src :16" %} + opcode(0xBF, 0x0F); + ins_encode( OpcS, OpcP, RegReg( dst, src)); + ins_pipe( ialu_reg_reg ); +%} + + +// Logical Shift Right by variable +instruct shrI_eReg_CL(eRegI dst, eCXRegI shift, eFlagsReg cr) %{ + match(Set dst (URShiftI dst shift)); + effect(KILL cr); + + size(2); + format %{ "SHR $dst,$shift" %} + opcode(0xD3, 0x5); /* D3 /5 */ + ins_encode( OpcP, RegOpc( dst ) ); + ins_pipe( ialu_reg_reg ); +%} + + +//----------Logical Instructions----------------------------------------------- +//----------Integer Logical Instructions--------------------------------------- +// And Instructions +// And Register with Register +instruct andI_eReg(eRegI dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (AndI dst src)); + effect(KILL cr); + + size(2); + format %{ "AND $dst,$src" %} + opcode(0x23); + ins_encode( OpcP, RegReg( dst, src) ); + ins_pipe( ialu_reg_reg ); +%} + +// And Register with Immediate +instruct andI_eReg_imm(eRegI dst, immI src, eFlagsReg cr) %{ + match(Set dst (AndI dst src)); + effect(KILL cr); + + format %{ "AND $dst,$src" %} + opcode(0x81,0x04); /* Opcode 81 /4 */ + // ins_encode( RegImm( dst, src) ); + ins_encode( OpcSErm( dst, src ), Con8or32( src ) ); + ins_pipe( ialu_reg ); +%} + +// And Register with Memory +instruct andI_eReg_mem(eRegI dst, memory src, eFlagsReg cr) %{ + match(Set dst (AndI dst (LoadI src))); + effect(KILL cr); + + ins_cost(125); + format %{ "AND $dst,$src" %} + opcode(0x23); + ins_encode( OpcP, RegMem( dst, src) ); + ins_pipe( ialu_reg_mem ); +%} + +// And Memory with Register +instruct andI_mem_eReg(memory dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (AndI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "AND $dst,$src" %} + opcode(0x21); /* Opcode 21 /r */ + ins_encode( OpcP, RegMem( src, dst ) ); + ins_pipe( ialu_mem_reg ); +%} + +// And Memory with Immediate +instruct andI_mem_imm(memory dst, immI src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (AndI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "AND $dst,$src" %} + opcode(0x81, 0x4); /* Opcode 81 /4 id */ + // ins_encode( MemImm( dst, src) ); + ins_encode( OpcSE( src ), RMopc_Mem(secondary, dst ), Con8or32( src ) ); + ins_pipe( ialu_mem_imm ); +%} + +// Or Instructions +// Or Register with Register +instruct orI_eReg(eRegI dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (OrI dst src)); + effect(KILL cr); + + size(2); + format %{ "OR $dst,$src" %} + opcode(0x0B); + ins_encode( OpcP, RegReg( dst, src) ); + ins_pipe( ialu_reg_reg ); +%} + +// Or Register with Immediate +instruct orI_eReg_imm(eRegI dst, immI src, eFlagsReg cr) %{ + match(Set dst (OrI dst src)); + effect(KILL cr); + + format %{ "OR $dst,$src" %} + opcode(0x81,0x01); /* Opcode 81 /1 id */ + // ins_encode( RegImm( dst, src) ); + ins_encode( OpcSErm( dst, src ), Con8or32( src ) ); + ins_pipe( ialu_reg ); +%} + +// Or Register with Memory +instruct orI_eReg_mem(eRegI dst, memory src, eFlagsReg cr) %{ + match(Set dst (OrI dst (LoadI src))); + effect(KILL cr); + + ins_cost(125); + format %{ "OR $dst,$src" %} + opcode(0x0B); + ins_encode( OpcP, RegMem( dst, src) ); + ins_pipe( ialu_reg_mem ); +%} + +// Or Memory with Register +instruct orI_mem_eReg(memory dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (OrI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "OR $dst,$src" %} + opcode(0x09); /* Opcode 09 /r */ + ins_encode( OpcP, RegMem( src, dst ) ); + ins_pipe( ialu_mem_reg ); +%} + +// Or Memory with Immediate +instruct orI_mem_imm(memory dst, immI src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (OrI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "OR $dst,$src" %} + opcode(0x81,0x1); /* Opcode 81 /1 id */ + // ins_encode( MemImm( dst, src) ); + ins_encode( OpcSE( src ), RMopc_Mem(secondary, dst ), Con8or32( src ) ); + ins_pipe( ialu_mem_imm ); +%} + +// ROL/ROR +// ROL expand +instruct rolI_eReg_imm1(eRegI dst, immI1 shift, eFlagsReg cr) %{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "ROL $dst, $shift" %} + opcode(0xD1, 0x0); /* Opcode D1 /0 */ + ins_encode( OpcP, RegOpc( dst )); + ins_pipe( ialu_reg ); +%} + +instruct rolI_eReg_imm8(eRegI dst, immI8 shift, eFlagsReg cr) %{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "ROL $dst, $shift" %} + opcode(0xC1, 0x0); /*Opcode /C1 /0 */ + ins_encode( RegOpcImm(dst, shift) ); + ins_pipe(ialu_reg); +%} + +instruct rolI_eReg_CL(ncxRegI dst, eCXRegI shift, eFlagsReg cr) %{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "ROL $dst, $shift" %} + opcode(0xD3, 0x0); /* Opcode D3 /0 */ + ins_encode(OpcP, RegOpc(dst)); + ins_pipe( ialu_reg_reg ); +%} +// end of ROL expand + +// ROL 32bit by one once +instruct rolI_eReg_i1(eRegI dst, immI1 lshift, immI_M1 rshift, eFlagsReg cr) %{ + match(Set dst ( OrI (LShiftI dst lshift) (URShiftI dst rshift))); + + expand %{ + rolI_eReg_imm1(dst, lshift, cr); + %} +%} + +// ROL 32bit var by imm8 once +instruct rolI_eReg_i8(eRegI dst, immI8 lshift, immI8 rshift, eFlagsReg cr) %{ + predicate( 0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); + match(Set dst ( OrI (LShiftI dst lshift) (URShiftI dst rshift))); + + expand %{ + rolI_eReg_imm8(dst, lshift, cr); + %} +%} + +// ROL 32bit var by var once +instruct rolI_eReg_Var_C0(ncxRegI dst, eCXRegI shift, immI0 zero, eFlagsReg cr) %{ + match(Set dst ( OrI (LShiftI dst shift) (URShiftI dst (SubI zero shift)))); + + expand %{ + rolI_eReg_CL(dst, shift, cr); + %} +%} + +// ROL 32bit var by var once +instruct rolI_eReg_Var_C32(ncxRegI dst, eCXRegI shift, immI_32 c32, eFlagsReg cr) %{ + match(Set dst ( OrI (LShiftI dst shift) (URShiftI dst (SubI c32 shift)))); + + expand %{ + rolI_eReg_CL(dst, shift, cr); + %} +%} + +// ROR expand +instruct rorI_eReg_imm1(eRegI dst, immI1 shift, eFlagsReg cr) %{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "ROR $dst, $shift" %} + opcode(0xD1,0x1); /* Opcode D1 /1 */ + ins_encode( OpcP, RegOpc( dst ) ); + ins_pipe( ialu_reg ); +%} + +instruct rorI_eReg_imm8(eRegI dst, immI8 shift, eFlagsReg cr) %{ + effect (USE_DEF dst, USE shift, KILL cr); + + format %{ "ROR $dst, $shift" %} + opcode(0xC1, 0x1); /* Opcode /C1 /1 ib */ + ins_encode( RegOpcImm(dst, shift) ); + ins_pipe( ialu_reg ); +%} + +instruct rorI_eReg_CL(ncxRegI dst, eCXRegI shift, eFlagsReg cr)%{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "ROR $dst, $shift" %} + opcode(0xD3, 0x1); /* Opcode D3 /1 */ + ins_encode(OpcP, RegOpc(dst)); + ins_pipe( ialu_reg_reg ); +%} +// end of ROR expand + +// ROR right once +instruct rorI_eReg_i1(eRegI dst, immI1 rshift, immI_M1 lshift, eFlagsReg cr) %{ + match(Set dst ( OrI (URShiftI dst rshift) (LShiftI dst lshift))); + + expand %{ + rorI_eReg_imm1(dst, rshift, cr); + %} +%} + +// ROR 32bit by immI8 once +instruct rorI_eReg_i8(eRegI dst, immI8 rshift, immI8 lshift, eFlagsReg cr) %{ + predicate( 0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); + match(Set dst ( OrI (URShiftI dst rshift) (LShiftI dst lshift))); + + expand %{ + rorI_eReg_imm8(dst, rshift, cr); + %} +%} + +// ROR 32bit var by var once +instruct rorI_eReg_Var_C0(ncxRegI dst, eCXRegI shift, immI0 zero, eFlagsReg cr) %{ + match(Set dst ( OrI (URShiftI dst shift) (LShiftI dst (SubI zero shift)))); + + expand %{ + rorI_eReg_CL(dst, shift, cr); + %} +%} + +// ROR 32bit var by var once +instruct rorI_eReg_Var_C32(ncxRegI dst, eCXRegI shift, immI_32 c32, eFlagsReg cr) %{ + match(Set dst ( OrI (URShiftI dst shift) (LShiftI dst (SubI c32 shift)))); + + expand %{ + rorI_eReg_CL(dst, shift, cr); + %} +%} + +// Xor Instructions +// Xor Register with Register +instruct xorI_eReg(eRegI dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (XorI dst src)); + effect(KILL cr); + + size(2); + format %{ "XOR $dst,$src" %} + opcode(0x33); + ins_encode( OpcP, RegReg( dst, src) ); + ins_pipe( ialu_reg_reg ); +%} + +// Xor Register with Immediate +instruct xorI_eReg_imm(eRegI dst, immI src, eFlagsReg cr) %{ + match(Set dst (XorI dst src)); + effect(KILL cr); + + format %{ "XOR $dst,$src" %} + opcode(0x81,0x06); /* Opcode 81 /6 id */ + // ins_encode( RegImm( dst, src) ); + ins_encode( OpcSErm( dst, src ), Con8or32( src ) ); + ins_pipe( ialu_reg ); +%} + +// Xor Register with Memory +instruct xorI_eReg_mem(eRegI dst, memory src, eFlagsReg cr) %{ + match(Set dst (XorI dst (LoadI src))); + effect(KILL cr); + + ins_cost(125); + format %{ "XOR $dst,$src" %} + opcode(0x33); + ins_encode( OpcP, RegMem(dst, src) ); + ins_pipe( ialu_reg_mem ); +%} + +// Xor Memory with Register +instruct xorI_mem_eReg(memory dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (XorI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "XOR $dst,$src" %} + opcode(0x31); /* Opcode 31 /r */ + ins_encode( OpcP, RegMem( src, dst ) ); + ins_pipe( ialu_mem_reg ); +%} + +// Xor Memory with Immediate +instruct xorI_mem_imm(memory dst, immI src, eFlagsReg cr) %{ + match(Set dst (StoreI dst (XorI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "XOR $dst,$src" %} + opcode(0x81,0x6); /* Opcode 81 /6 id */ + ins_encode( OpcSE( src ), RMopc_Mem(secondary, dst ), Con8or32( src ) ); + ins_pipe( ialu_mem_imm ); +%} + +//----------Convert Int to Boolean--------------------------------------------- + +instruct movI_nocopy(eRegI dst, eRegI src) %{ + effect( DEF dst, USE src ); + format %{ "MOV $dst,$src" %} + ins_encode( enc_Copy( dst, src) ); + ins_pipe( ialu_reg_reg ); +%} + +instruct ci2b( eRegI dst, eRegI src, eFlagsReg cr ) %{ + effect( USE_DEF dst, USE src, KILL cr ); + + size(4); + format %{ "NEG $dst\n\t" + "ADC $dst,$src" %} + ins_encode( neg_reg(dst), + OpcRegReg(0x13,dst,src) ); + ins_pipe( ialu_reg_reg_long ); +%} + +instruct convI2B( eRegI dst, eRegI src, eFlagsReg cr ) %{ + match(Set dst (Conv2B src)); + + expand %{ + movI_nocopy(dst,src); + ci2b(dst,src,cr); + %} +%} + +instruct movP_nocopy(eRegI dst, eRegP src) %{ + effect( DEF dst, USE src ); + format %{ "MOV $dst,$src" %} + ins_encode( enc_Copy( dst, src) ); + ins_pipe( ialu_reg_reg ); +%} + +instruct cp2b( eRegI dst, eRegP src, eFlagsReg cr ) %{ + effect( USE_DEF dst, USE src, KILL cr ); + format %{ "NEG $dst\n\t" + "ADC $dst,$src" %} + ins_encode( neg_reg(dst), + OpcRegReg(0x13,dst,src) ); + ins_pipe( ialu_reg_reg_long ); +%} + +instruct convP2B( eRegI dst, eRegP src, eFlagsReg cr ) %{ + match(Set dst (Conv2B src)); + + expand %{ + movP_nocopy(dst,src); + cp2b(dst,src,cr); + %} +%} + +instruct cmpLTMask( eCXRegI dst, ncxRegI p, ncxRegI q, eFlagsReg cr ) %{ + match(Set dst (CmpLTMask p q)); + effect( KILL cr ); + ins_cost(400); + + // SETlt can only use low byte of EAX,EBX, ECX, or EDX as destination + format %{ "XOR $dst,$dst\n\t" + "CMP $p,$q\n\t" + "SETlt $dst\n\t" + "NEG $dst" %} + ins_encode( OpcRegReg(0x33,dst,dst), + OpcRegReg(0x3B,p,q), + setLT_reg(dst), neg_reg(dst) ); + ins_pipe( pipe_slow ); +%} + +instruct cmpLTMask0( eRegI dst, immI0 zero, eFlagsReg cr ) %{ + match(Set dst (CmpLTMask dst zero)); + effect( DEF dst, KILL cr ); + ins_cost(100); + + format %{ "SAR $dst,31" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode( RegOpcImm( dst, 0x1F ) ); + ins_pipe( ialu_reg ); +%} + + +instruct cadd_cmpLTMask( ncxRegI p, ncxRegI q, ncxRegI y, eCXRegI tmp, eFlagsReg cr ) %{ + match(Set p (AddI (AndI (CmpLTMask p q) y) (SubI p q))); + effect( KILL tmp, KILL cr ); + ins_cost(400); + // annoyingly, $tmp has no edges so you cant ask for it in + // any format or encoding + format %{ "SUB $p,$q\n\t" + "SBB ECX,ECX\n\t" + "AND ECX,$y\n\t" + "ADD $p,ECX" %} + ins_encode( enc_cmpLTP(p,q,y,tmp) ); + ins_pipe( pipe_cmplt ); +%} + +/* If I enable this, I encourage spilling in the inner loop of compress. +instruct cadd_cmpLTMask_mem( ncxRegI p, ncxRegI q, memory y, eCXRegI tmp, eFlagsReg cr ) %{ + match(Set p (AddI (AndI (CmpLTMask p q) (LoadI y)) (SubI p q))); + effect( USE_KILL tmp, KILL cr ); + ins_cost(400); + + format %{ "SUB $p,$q\n\t" + "SBB ECX,ECX\n\t" + "AND ECX,$y\n\t" + "ADD $p,ECX" %} + ins_encode( enc_cmpLTP_mem(p,q,y,tmp) ); +%} +*/ + +//----------Long Instructions------------------------------------------------ +// Add Long Register with Register +instruct addL_eReg(eRegL dst, eRegL src, eFlagsReg cr) %{ + match(Set dst (AddL dst src)); + effect(KILL cr); + ins_cost(200); + format %{ "ADD $dst.lo,$src.lo\n\t" + "ADC $dst.hi,$src.hi" %} + opcode(0x03, 0x13); + ins_encode( RegReg_Lo(dst, src), RegReg_Hi(dst,src) ); + ins_pipe( ialu_reg_reg_long ); +%} + +// Add Long Register with Immediate +instruct addL_eReg_imm(eRegL dst, immL src, eFlagsReg cr) %{ + match(Set dst (AddL dst src)); + effect(KILL cr); + format %{ "ADD $dst.lo,$src.lo\n\t" + "ADC $dst.hi,$src.hi" %} + opcode(0x81,0x00,0x02); /* Opcode 81 /0, 81 /2 */ + ins_encode( Long_OpcSErm_Lo( dst, src ), Long_OpcSErm_Hi( dst, src ) ); + ins_pipe( ialu_reg_long ); +%} + +// Add Long Register with Memory +instruct addL_eReg_mem(eRegL dst, load_long_memory mem, eFlagsReg cr) %{ + match(Set dst (AddL dst (LoadL mem))); + effect(KILL cr); + ins_cost(125); + format %{ "ADD $dst.lo,$mem\n\t" + "ADC $dst.hi,$mem+4" %} + opcode(0x03, 0x13); + ins_encode( OpcP, RegMem( dst, mem), OpcS, RegMem_Hi(dst,mem) ); + ins_pipe( ialu_reg_long_mem ); +%} + +// Subtract Long Register with Register. +instruct subL_eReg(eRegL dst, eRegL src, eFlagsReg cr) %{ + match(Set dst (SubL dst src)); + effect(KILL cr); + ins_cost(200); + format %{ "SUB $dst.lo,$src.lo\n\t" + "SBB $dst.hi,$src.hi" %} + opcode(0x2B, 0x1B); + ins_encode( RegReg_Lo(dst, src), RegReg_Hi(dst,src) ); + ins_pipe( ialu_reg_reg_long ); +%} + +// Subtract Long Register with Immediate +instruct subL_eReg_imm(eRegL dst, immL src, eFlagsReg cr) %{ + match(Set dst (SubL dst src)); + effect(KILL cr); + format %{ "SUB $dst.lo,$src.lo\n\t" + "SBB $dst.hi,$src.hi" %} + opcode(0x81,0x05,0x03); /* Opcode 81 /5, 81 /3 */ + ins_encode( Long_OpcSErm_Lo( dst, src ), Long_OpcSErm_Hi( dst, src ) ); + ins_pipe( ialu_reg_long ); +%} + +// Subtract Long Register with Memory +instruct subL_eReg_mem(eRegL dst, load_long_memory mem, eFlagsReg cr) %{ + match(Set dst (SubL dst (LoadL mem))); + effect(KILL cr); + ins_cost(125); + format %{ "SUB $dst.lo,$mem\n\t" + "SBB $dst.hi,$mem+4" %} + opcode(0x2B, 0x1B); + ins_encode( OpcP, RegMem( dst, mem), OpcS, RegMem_Hi(dst,mem) ); + ins_pipe( ialu_reg_long_mem ); +%} + +instruct negL_eReg(eRegL dst, immL0 zero, eFlagsReg cr) %{ + match(Set dst (SubL zero dst)); + effect(KILL cr); + ins_cost(300); + format %{ "NEG $dst.hi\n\tNEG $dst.lo\n\tSBB $dst.hi,0" %} + ins_encode( neg_long(dst) ); + ins_pipe( ialu_reg_reg_long ); +%} + +// And Long Register with Register +instruct andL_eReg(eRegL dst, eRegL src, eFlagsReg cr) %{ + match(Set dst (AndL dst src)); + effect(KILL cr); + format %{ "AND $dst.lo,$src.lo\n\t" + "AND $dst.hi,$src.hi" %} + opcode(0x23,0x23); + ins_encode( RegReg_Lo( dst, src), RegReg_Hi( dst, src) ); + ins_pipe( ialu_reg_reg_long ); +%} + +// And Long Register with Immediate +instruct andL_eReg_imm(eRegL dst, immL src, eFlagsReg cr) %{ + match(Set dst (AndL dst src)); + effect(KILL cr); + format %{ "AND $dst.lo,$src.lo\n\t" + "AND $dst.hi,$src.hi" %} + opcode(0x81,0x04,0x04); /* Opcode 81 /4, 81 /4 */ + ins_encode( Long_OpcSErm_Lo( dst, src ), Long_OpcSErm_Hi( dst, src ) ); + ins_pipe( ialu_reg_long ); +%} + +// And Long Register with Memory +instruct andL_eReg_mem(eRegL dst, load_long_memory mem, eFlagsReg cr) %{ + match(Set dst (AndL dst (LoadL mem))); + effect(KILL cr); + ins_cost(125); + format %{ "AND $dst.lo,$mem\n\t" + "AND $dst.hi,$mem+4" %} + opcode(0x23, 0x23); + ins_encode( OpcP, RegMem( dst, mem), OpcS, RegMem_Hi(dst,mem) ); + ins_pipe( ialu_reg_long_mem ); +%} + +// Or Long Register with Register +instruct orl_eReg(eRegL dst, eRegL src, eFlagsReg cr) %{ + match(Set dst (OrL dst src)); + effect(KILL cr); + format %{ "OR $dst.lo,$src.lo\n\t" + "OR $dst.hi,$src.hi" %} + opcode(0x0B,0x0B); + ins_encode( RegReg_Lo( dst, src), RegReg_Hi( dst, src) ); + ins_pipe( ialu_reg_reg_long ); +%} + +// Or Long Register with Immediate +instruct orl_eReg_imm(eRegL dst, immL src, eFlagsReg cr) %{ + match(Set dst (OrL dst src)); + effect(KILL cr); + format %{ "OR $dst.lo,$src.lo\n\t" + "OR $dst.hi,$src.hi" %} + opcode(0x81,0x01,0x01); /* Opcode 81 /1, 81 /1 */ + ins_encode( Long_OpcSErm_Lo( dst, src ), Long_OpcSErm_Hi( dst, src ) ); + ins_pipe( ialu_reg_long ); +%} + +// Or Long Register with Memory +instruct orl_eReg_mem(eRegL dst, load_long_memory mem, eFlagsReg cr) %{ + match(Set dst (OrL dst (LoadL mem))); + effect(KILL cr); + ins_cost(125); + format %{ "OR $dst.lo,$mem\n\t" + "OR $dst.hi,$mem+4" %} + opcode(0x0B,0x0B); + ins_encode( OpcP, RegMem( dst, mem), OpcS, RegMem_Hi(dst,mem) ); + ins_pipe( ialu_reg_long_mem ); +%} + +// Xor Long Register with Register +instruct xorl_eReg(eRegL dst, eRegL src, eFlagsReg cr) %{ + match(Set dst (XorL dst src)); + effect(KILL cr); + format %{ "XOR $dst.lo,$src.lo\n\t" + "XOR $dst.hi,$src.hi" %} + opcode(0x33,0x33); + ins_encode( RegReg_Lo( dst, src), RegReg_Hi( dst, src) ); + ins_pipe( ialu_reg_reg_long ); +%} + +// Xor Long Register with Immediate +instruct xorl_eReg_imm(eRegL dst, immL src, eFlagsReg cr) %{ + match(Set dst (XorL dst src)); + effect(KILL cr); + format %{ "XOR $dst.lo,$src.lo\n\t" + "XOR $dst.hi,$src.hi" %} + opcode(0x81,0x06,0x06); /* Opcode 81 /6, 81 /6 */ + ins_encode( Long_OpcSErm_Lo( dst, src ), Long_OpcSErm_Hi( dst, src ) ); + ins_pipe( ialu_reg_long ); +%} + +// Xor Long Register with Memory +instruct xorl_eReg_mem(eRegL dst, load_long_memory mem, eFlagsReg cr) %{ + match(Set dst (XorL dst (LoadL mem))); + effect(KILL cr); + ins_cost(125); + format %{ "XOR $dst.lo,$mem\n\t" + "XOR $dst.hi,$mem+4" %} + opcode(0x33,0x33); + ins_encode( OpcP, RegMem( dst, mem), OpcS, RegMem_Hi(dst,mem) ); + ins_pipe( ialu_reg_long_mem ); +%} + +// Shift Left Long by 1-31 +instruct shlL_eReg_1_31(eRegL dst, immI_1_31 cnt, eFlagsReg cr) %{ + match(Set dst (LShiftL dst cnt)); + effect(KILL cr); + ins_cost(200); + format %{ "SHLD $dst.hi,$dst.lo,$cnt\n\t" + "SHL $dst.lo,$cnt" %} + opcode(0xC1, 0x4, 0xA4); /* 0F/A4, then C1 /4 ib */ + ins_encode( move_long_small_shift(dst,cnt) ); + ins_pipe( ialu_reg_long ); +%} + +// Shift Left Long by 32-63 +instruct shlL_eReg_32_63(eRegL dst, immI_32_63 cnt, eFlagsReg cr) %{ + match(Set dst (LShiftL dst cnt)); + effect(KILL cr); + ins_cost(300); + format %{ "MOV $dst.hi,$dst.lo\n" + "\tSHL $dst.hi,$cnt-32\n" + "\tXOR $dst.lo,$dst.lo" %} + opcode(0xC1, 0x4); /* C1 /4 ib */ + ins_encode( move_long_big_shift_clr(dst,cnt) ); + ins_pipe( ialu_reg_long ); +%} + +// Shift Left Long by variable +instruct salL_eReg_CL(eRegL dst, eCXRegI shift, eFlagsReg cr) %{ + match(Set dst (LShiftL dst shift)); + effect(KILL cr); + ins_cost(500+200); + size(17); + format %{ "TEST $shift,32\n\t" + "JEQ,s small\n\t" + "MOV $dst.hi,$dst.lo\n\t" + "XOR $dst.lo,$dst.lo\n" + "small:\tSHLD $dst.hi,$dst.lo,$shift\n\t" + "SHL $dst.lo,$shift" %} + ins_encode( shift_left_long( dst, shift ) ); + ins_pipe( pipe_slow ); +%} + +// Shift Right Long by 1-31 +instruct shrL_eReg_1_31(eRegL dst, immI_1_31 cnt, eFlagsReg cr) %{ + match(Set dst (URShiftL dst cnt)); + effect(KILL cr); + ins_cost(200); + format %{ "SHRD $dst.lo,$dst.hi,$cnt\n\t" + "SHR $dst.hi,$cnt" %} + opcode(0xC1, 0x5, 0xAC); /* 0F/AC, then C1 /5 ib */ + ins_encode( move_long_small_shift(dst,cnt) ); + ins_pipe( ialu_reg_long ); +%} + +// Shift Right Long by 32-63 +instruct shrL_eReg_32_63(eRegL dst, immI_32_63 cnt, eFlagsReg cr) %{ + match(Set dst (URShiftL dst cnt)); + effect(KILL cr); + ins_cost(300); + format %{ "MOV $dst.lo,$dst.hi\n" + "\tSHR $dst.lo,$cnt-32\n" + "\tXOR $dst.hi,$dst.hi" %} + opcode(0xC1, 0x5); /* C1 /5 ib */ + ins_encode( move_long_big_shift_clr(dst,cnt) ); + ins_pipe( ialu_reg_long ); +%} + +// Shift Right Long by variable +instruct shrL_eReg_CL(eRegL dst, eCXRegI shift, eFlagsReg cr) %{ + match(Set dst (URShiftL dst shift)); + effect(KILL cr); + ins_cost(600); + size(17); + format %{ "TEST $shift,32\n\t" + "JEQ,s small\n\t" + "MOV $dst.lo,$dst.hi\n\t" + "XOR $dst.hi,$dst.hi\n" + "small:\tSHRD $dst.lo,$dst.hi,$shift\n\t" + "SHR $dst.hi,$shift" %} + ins_encode( shift_right_long( dst, shift ) ); + ins_pipe( pipe_slow ); +%} + +// Shift Right Long by 1-31 +instruct sarL_eReg_1_31(eRegL dst, immI_1_31 cnt, eFlagsReg cr) %{ + match(Set dst (RShiftL dst cnt)); + effect(KILL cr); + ins_cost(200); + format %{ "SHRD $dst.lo,$dst.hi,$cnt\n\t" + "SAR $dst.hi,$cnt" %} + opcode(0xC1, 0x7, 0xAC); /* 0F/AC, then C1 /7 ib */ + ins_encode( move_long_small_shift(dst,cnt) ); + ins_pipe( ialu_reg_long ); +%} + +// Shift Right Long by 32-63 +instruct sarL_eReg_32_63( eRegL dst, immI_32_63 cnt, eFlagsReg cr) %{ + match(Set dst (RShiftL dst cnt)); + effect(KILL cr); + ins_cost(300); + format %{ "MOV $dst.lo,$dst.hi\n" + "\tSAR $dst.lo,$cnt-32\n" + "\tSAR $dst.hi,31" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode( move_long_big_shift_sign(dst,cnt) ); + ins_pipe( ialu_reg_long ); +%} + +// Shift Right arithmetic Long by variable +instruct sarL_eReg_CL(eRegL dst, eCXRegI shift, eFlagsReg cr) %{ + match(Set dst (RShiftL dst shift)); + effect(KILL cr); + ins_cost(600); + size(18); + format %{ "TEST $shift,32\n\t" + "JEQ,s small\n\t" + "MOV $dst.lo,$dst.hi\n\t" + "SAR $dst.hi,31\n" + "small:\tSHRD $dst.lo,$dst.hi,$shift\n\t" + "SAR $dst.hi,$shift" %} + ins_encode( shift_right_arith_long( dst, shift ) ); + ins_pipe( pipe_slow ); +%} + + +//----------Double Instructions------------------------------------------------ +// Double Math + +// Compare & branch + +// P6 version of float compare, sets condition codes in EFLAGS +instruct cmpD_cc_P6(eFlagsRegU cr, regD src1, regD src2, eAXRegI rax) %{ + predicate(VM_Version::supports_cmov() && UseSSE <=1); + match(Set cr (CmpD src1 src2)); + effect(KILL rax); + ins_cost(150); + format %{ "FLD $src1\n\t" + "FUCOMIP ST,$src2 // P6 instruction\n\t" + "JNP exit\n\t" + "MOV ah,1 // saw a NaN, set CF\n\t" + "SAHF\n" + "exit:\tNOP // avoid branch to branch" %} + opcode(0xDF, 0x05); /* DF E8+i or DF /5 */ + ins_encode( Push_Reg_D(src1), + OpcP, RegOpc(src2), + cmpF_P6_fixup ); + ins_pipe( pipe_slow ); +%} + +// Compare & branch +instruct cmpD_cc(eFlagsRegU cr, regD src1, regD src2, eAXRegI rax) %{ + predicate(UseSSE<=1); + match(Set cr (CmpD src1 src2)); + effect(KILL rax); + ins_cost(200); + format %{ "FLD $src1\n\t" + "FCOMp $src2\n\t" + "FNSTSW AX\n\t" + "TEST AX,0x400\n\t" + "JZ,s flags\n\t" + "MOV AH,1\t# unordered treat as LT\n" + "flags:\tSAHF" %} + opcode(0xD8, 0x3); /* D8 D8+i or D8 /3 */ + ins_encode( Push_Reg_D(src1), + OpcP, RegOpc(src2), + fpu_flags); + ins_pipe( pipe_slow ); +%} + +// Compare vs zero into -1,0,1 +instruct cmpD_0(eRegI dst, regD src1, immD0 zero, eAXRegI rax, eFlagsReg cr) %{ + predicate(UseSSE<=1); + match(Set dst (CmpD3 src1 zero)); + effect(KILL cr, KILL rax); + ins_cost(280); + format %{ "FTSTD $dst,$src1" %} + opcode(0xE4, 0xD9); + ins_encode( Push_Reg_D(src1), + OpcS, OpcP, PopFPU, + CmpF_Result(dst)); + ins_pipe( pipe_slow ); +%} + +// Compare into -1,0,1 +instruct cmpD_reg(eRegI dst, regD src1, regD src2, eAXRegI rax, eFlagsReg cr) %{ + predicate(UseSSE<=1); + match(Set dst (CmpD3 src1 src2)); + effect(KILL cr, KILL rax); + ins_cost(300); + format %{ "FCMPD $dst,$src1,$src2" %} + opcode(0xD8, 0x3); /* D8 D8+i or D8 /3 */ + ins_encode( Push_Reg_D(src1), + OpcP, RegOpc(src2), + CmpF_Result(dst)); + ins_pipe( pipe_slow ); +%} + +// float compare and set condition codes in EFLAGS by XMM regs +instruct cmpXD_cc(eFlagsRegU cr, regXD dst, regXD src, eAXRegI rax) %{ + predicate(UseSSE>=2); + match(Set cr (CmpD dst src)); + effect(KILL rax); + ins_cost(125); + format %{ "COMISD $dst,$src\n" + "\tJNP exit\n" + "\tMOV ah,1 // saw a NaN, set CF\n" + "\tSAHF\n" + "exit:\tNOP // avoid branch to branch" %} + opcode(0x66, 0x0F, 0x2F); + ins_encode(OpcP, OpcS, Opcode(tertiary), RegReg(dst, src), cmpF_P6_fixup); + ins_pipe( pipe_slow ); +%} + +// float compare and set condition codes in EFLAGS by XMM regs +instruct cmpXD_ccmem(eFlagsRegU cr, regXD dst, memory src, eAXRegI rax) %{ + predicate(UseSSE>=2); + match(Set cr (CmpD dst (LoadD src))); + effect(KILL rax); + ins_cost(145); + format %{ "COMISD $dst,$src\n" + "\tJNP exit\n" + "\tMOV ah,1 // saw a NaN, set CF\n" + "\tSAHF\n" + "exit:\tNOP // avoid branch to branch" %} + opcode(0x66, 0x0F, 0x2F); + ins_encode(OpcP, OpcS, Opcode(tertiary), RegMem(dst, src), cmpF_P6_fixup); + ins_pipe( pipe_slow ); +%} + +// Compare into -1,0,1 in XMM +instruct cmpXD_reg(eRegI dst, regXD src1, regXD src2, eFlagsReg cr) %{ + predicate(UseSSE>=2); + match(Set dst (CmpD3 src1 src2)); + effect(KILL cr); + ins_cost(255); + format %{ "XOR $dst,$dst\n" + "\tCOMISD $src1,$src2\n" + "\tJP,s nan\n" + "\tJEQ,s exit\n" + "\tJA,s inc\n" + "nan:\tDEC $dst\n" + "\tJMP,s exit\n" + "inc:\tINC $dst\n" + "exit:" + %} + opcode(0x66, 0x0F, 0x2F); + ins_encode(Xor_Reg(dst), OpcP, OpcS, Opcode(tertiary), RegReg(src1, src2), + CmpX_Result(dst)); + ins_pipe( pipe_slow ); +%} + +// Compare into -1,0,1 in XMM and memory +instruct cmpXD_regmem(eRegI dst, regXD src1, memory mem, eFlagsReg cr) %{ + predicate(UseSSE>=2); + match(Set dst (CmpD3 src1 (LoadD mem))); + effect(KILL cr); + ins_cost(275); + format %{ "COMISD $src1,$mem\n" + "\tMOV $dst,0\t\t# do not blow flags\n" + "\tJP,s nan\n" + "\tJEQ,s exit\n" + "\tJA,s inc\n" + "nan:\tDEC $dst\n" + "\tJMP,s exit\n" + "inc:\tINC $dst\n" + "exit:" + %} + opcode(0x66, 0x0F, 0x2F); + ins_encode(OpcP, OpcS, Opcode(tertiary), RegMem(src1, mem), + LdImmI(dst,0x0), CmpX_Result(dst)); + ins_pipe( pipe_slow ); +%} + + +instruct subD_reg(regD dst, regD src) %{ + predicate (UseSSE <=1); + match(Set dst (SubD dst src)); + + format %{ "FLD $src\n\t" + "DSUBp $dst,ST" %} + opcode(0xDE, 0x5); /* DE E8+i or DE /5 */ + ins_cost(150); + ins_encode( Push_Reg_D(src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_reg ); +%} + +instruct subD_reg_round(stackSlotD dst, regD src1, regD src2) %{ + predicate (UseSSE <=1); + match(Set dst (RoundDouble (SubD src1 src2))); + ins_cost(250); + + format %{ "FLD $src2\n\t" + "DSUB ST,$src1\n\t" + "FSTP_D $dst\t# D-round" %} + opcode(0xD8, 0x5); + ins_encode( Push_Reg_D(src2), + OpcP, RegOpc(src1), Pop_Mem_D(dst) ); + ins_pipe( fpu_mem_reg_reg ); +%} + + +instruct subD_reg_mem(regD dst, memory src) %{ + predicate (UseSSE <=1); + match(Set dst (SubD dst (LoadD src))); + ins_cost(150); + + format %{ "FLD $src\n\t" + "DSUBp $dst,ST" %} + opcode(0xDE, 0x5, 0xDD); /* DE C0+i */ /* LoadD DD /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_mem ); +%} + +instruct absD_reg(regDPR1 dst, regDPR1 src) %{ + predicate (UseSSE<=1); + match(Set dst (AbsD src)); + ins_cost(100); + format %{ "FABS" %} + opcode(0xE1, 0xD9); + ins_encode( OpcS, OpcP ); + ins_pipe( fpu_reg_reg ); +%} + +instruct absXD_reg( regXD dst ) %{ + predicate(UseSSE>=2); + match(Set dst (AbsD dst)); + format %{ "ANDPD $dst,[0x7FFFFFFFFFFFFFFF]\t# ABS D by sign masking" %} + ins_encode( AbsXD_encoding(dst)); + ins_pipe( pipe_slow ); +%} + +instruct negD_reg(regDPR1 dst, regDPR1 src) %{ + predicate(UseSSE<=1); + match(Set dst (NegD src)); + ins_cost(100); + format %{ "FCHS" %} + opcode(0xE0, 0xD9); + ins_encode( OpcS, OpcP ); + ins_pipe( fpu_reg_reg ); +%} + +instruct negXD_reg( regXD dst ) %{ + predicate(UseSSE>=2); + match(Set dst (NegD dst)); + format %{ "XORPD $dst,[0x8000000000000000]\t# CHS D by sign flipping" %} + ins_encode %{ + __ xorpd($dst$$XMMRegister, + ExternalAddress((address)double_signflip_pool)); + %} + ins_pipe( pipe_slow ); +%} + +instruct addD_reg(regD dst, regD src) %{ + predicate(UseSSE<=1); + match(Set dst (AddD dst src)); + format %{ "FLD $src\n\t" + "DADD $dst,ST" %} + size(4); + ins_cost(150); + opcode(0xDE, 0x0); /* DE C0+i or DE /0*/ + ins_encode( Push_Reg_D(src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_reg ); +%} + + +instruct addD_reg_round(stackSlotD dst, regD src1, regD src2) %{ + predicate(UseSSE<=1); + match(Set dst (RoundDouble (AddD src1 src2))); + ins_cost(250); + + format %{ "FLD $src2\n\t" + "DADD ST,$src1\n\t" + "FSTP_D $dst\t# D-round" %} + opcode(0xD8, 0x0); /* D8 C0+i or D8 /0*/ + ins_encode( Push_Reg_D(src2), + OpcP, RegOpc(src1), Pop_Mem_D(dst) ); + ins_pipe( fpu_mem_reg_reg ); +%} + + +instruct addD_reg_mem(regD dst, memory src) %{ + predicate(UseSSE<=1); + match(Set dst (AddD dst (LoadD src))); + ins_cost(150); + + format %{ "FLD $src\n\t" + "DADDp $dst,ST" %} + opcode(0xDE, 0x0, 0xDD); /* DE C0+i */ /* LoadD DD /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_mem ); +%} + +// add-to-memory +instruct addD_mem_reg(memory dst, regD src) %{ + predicate(UseSSE<=1); + match(Set dst (StoreD dst (RoundDouble (AddD (LoadD dst) src)))); + ins_cost(150); + + format %{ "FLD_D $dst\n\t" + "DADD ST,$src\n\t" + "FST_D $dst" %} + opcode(0xDD, 0x0); + ins_encode( Opcode(0xDD), RMopc_Mem(0x00,dst), + Opcode(0xD8), RegOpc(src), + set_instruction_start, + Opcode(0xDD), RMopc_Mem(0x03,dst) ); + ins_pipe( fpu_reg_mem ); +%} + +instruct addD_reg_imm1(regD dst, immD1 src) %{ + predicate(UseSSE<=1); + match(Set dst (AddD dst src)); + ins_cost(125); + format %{ "FLD1\n\t" + "DADDp $dst,ST" %} + opcode(0xDE, 0x00); + ins_encode( LdImmD(src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg ); +%} + +instruct addD_reg_imm(regD dst, immD src) %{ + predicate(UseSSE<=1 && _kids[1]->_leaf->getd() != 0.0 && _kids[1]->_leaf->getd() != 1.0 ); + match(Set dst (AddD dst src)); + ins_cost(200); + format %{ "FLD_D [$src]\n\t" + "DADDp $dst,ST" %} + opcode(0xDE, 0x00); /* DE /0 */ + ins_encode( LdImmD(src), + OpcP, RegOpc(dst)); + ins_pipe( fpu_reg_mem ); +%} + +instruct addD_reg_imm_round(stackSlotD dst, regD src, immD con) %{ + predicate(UseSSE<=1 && _kids[0]->_kids[1]->_leaf->getd() != 0.0 && _kids[0]->_kids[1]->_leaf->getd() != 1.0 ); + match(Set dst (RoundDouble (AddD src con))); + ins_cost(200); + format %{ "FLD_D [$con]\n\t" + "DADD ST,$src\n\t" + "FSTP_D $dst\t# D-round" %} + opcode(0xD8, 0x00); /* D8 /0 */ + ins_encode( LdImmD(con), + OpcP, RegOpc(src), Pop_Mem_D(dst)); + ins_pipe( fpu_mem_reg_con ); +%} + +// Add two double precision floating point values in xmm +instruct addXD_reg(regXD dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (AddD dst src)); + format %{ "ADDSD $dst,$src" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x58), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct addXD_imm(regXD dst, immXD con) %{ + predicate(UseSSE>=2); + match(Set dst (AddD dst con)); + format %{ "ADDSD $dst,[$con]" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x58), LdImmXD(dst, con) ); + ins_pipe( pipe_slow ); +%} + +instruct addXD_mem(regXD dst, memory mem) %{ + predicate(UseSSE>=2); + match(Set dst (AddD dst (LoadD mem))); + format %{ "ADDSD $dst,$mem" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x58), RegMem(dst,mem)); + ins_pipe( pipe_slow ); +%} + +// Sub two double precision floating point values in xmm +instruct subXD_reg(regXD dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (SubD dst src)); + format %{ "SUBSD $dst,$src" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x5C), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct subXD_imm(regXD dst, immXD con) %{ + predicate(UseSSE>=2); + match(Set dst (SubD dst con)); + format %{ "SUBSD $dst,[$con]" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x5C), LdImmXD(dst, con) ); + ins_pipe( pipe_slow ); +%} + +instruct subXD_mem(regXD dst, memory mem) %{ + predicate(UseSSE>=2); + match(Set dst (SubD dst (LoadD mem))); + format %{ "SUBSD $dst,$mem" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x5C), RegMem(dst,mem)); + ins_pipe( pipe_slow ); +%} + +// Mul two double precision floating point values in xmm +instruct mulXD_reg(regXD dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (MulD dst src)); + format %{ "MULSD $dst,$src" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x59), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct mulXD_imm(regXD dst, immXD con) %{ + predicate(UseSSE>=2); + match(Set dst (MulD dst con)); + format %{ "MULSD $dst,[$con]" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x59), LdImmXD(dst, con) ); + ins_pipe( pipe_slow ); +%} + +instruct mulXD_mem(regXD dst, memory mem) %{ + predicate(UseSSE>=2); + match(Set dst (MulD dst (LoadD mem))); + format %{ "MULSD $dst,$mem" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x59), RegMem(dst,mem)); + ins_pipe( pipe_slow ); +%} + +// Div two double precision floating point values in xmm +instruct divXD_reg(regXD dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (DivD dst src)); + format %{ "DIVSD $dst,$src" %} + opcode(0xF2, 0x0F, 0x5E); + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x5E), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct divXD_imm(regXD dst, immXD con) %{ + predicate(UseSSE>=2); + match(Set dst (DivD dst con)); + format %{ "DIVSD $dst,[$con]" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x5E), LdImmXD(dst, con)); + ins_pipe( pipe_slow ); +%} + +instruct divXD_mem(regXD dst, memory mem) %{ + predicate(UseSSE>=2); + match(Set dst (DivD dst (LoadD mem))); + format %{ "DIVSD $dst,$mem" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x5E), RegMem(dst,mem)); + ins_pipe( pipe_slow ); +%} + + +instruct mulD_reg(regD dst, regD src) %{ + predicate(UseSSE<=1); + match(Set dst (MulD dst src)); + format %{ "FLD $src\n\t" + "DMULp $dst,ST" %} + opcode(0xDE, 0x1); /* DE C8+i or DE /1*/ + ins_cost(150); + ins_encode( Push_Reg_D(src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_reg ); +%} + +// Strict FP instruction biases argument before multiply then +// biases result to avoid double rounding of subnormals. +// +// scale arg1 by multiplying arg1 by 2^(-15360) +// load arg2 +// multiply scaled arg1 by arg2 +// rescale product by 2^(15360) +// +instruct strictfp_mulD_reg(regDPR1 dst, regnotDPR1 src) %{ + predicate( UseSSE<=1 && Compile::current()->has_method() && Compile::current()->method()->is_strict() ); + match(Set dst (MulD dst src)); + ins_cost(1); // Select this instruction for all strict FP double multiplies + + format %{ "FLD StubRoutines::_fpu_subnormal_bias1\n\t" + "DMULp $dst,ST\n\t" + "FLD $src\n\t" + "DMULp $dst,ST\n\t" + "FLD StubRoutines::_fpu_subnormal_bias2\n\t" + "DMULp $dst,ST\n\t" %} + opcode(0xDE, 0x1); /* DE C8+i or DE /1*/ + ins_encode( strictfp_bias1(dst), + Push_Reg_D(src), + OpcP, RegOpc(dst), + strictfp_bias2(dst) ); + ins_pipe( fpu_reg_reg ); +%} + +instruct mulD_reg_imm(regD dst, immD src) %{ + predicate( UseSSE<=1 && _kids[1]->_leaf->getd() != 0.0 && _kids[1]->_leaf->getd() != 1.0 ); + match(Set dst (MulD dst src)); + ins_cost(200); + format %{ "FLD_D [$src]\n\t" + "DMULp $dst,ST" %} + opcode(0xDE, 0x1); /* DE /1 */ + ins_encode( LdImmD(src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_mem ); +%} + + +instruct mulD_reg_mem(regD dst, memory src) %{ + predicate( UseSSE<=1 ); + match(Set dst (MulD dst (LoadD src))); + ins_cost(200); + format %{ "FLD_D $src\n\t" + "DMULp $dst,ST" %} + opcode(0xDE, 0x1, 0xDD); /* DE C8+i or DE /1*/ /* LoadD DD /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_mem ); +%} + +// +// Cisc-alternate to reg-reg multiply +instruct mulD_reg_mem_cisc(regD dst, regD src, memory mem) %{ + predicate( UseSSE<=1 ); + match(Set dst (MulD src (LoadD mem))); + ins_cost(250); + format %{ "FLD_D $mem\n\t" + "DMUL ST,$src\n\t" + "FSTP_D $dst" %} + opcode(0xD8, 0x1, 0xD9); /* D8 C8+i */ /* LoadD D9 /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,mem), + OpcReg_F(src), + Pop_Reg_D(dst) ); + ins_pipe( fpu_reg_reg_mem ); +%} + + +// MACRO3 -- addD a mulD +// This instruction is a '2-address' instruction in that the result goes +// back to src2. This eliminates a move from the macro; possibly the +// register allocator will have to add it back (and maybe not). +instruct addD_mulD_reg(regD src2, regD src1, regD src0) %{ + predicate( UseSSE<=1 ); + match(Set src2 (AddD (MulD src0 src1) src2)); + format %{ "FLD $src0\t# ===MACRO3d===\n\t" + "DMUL ST,$src1\n\t" + "DADDp $src2,ST" %} + ins_cost(250); + opcode(0xDD); /* LoadD DD /0 */ + ins_encode( Push_Reg_F(src0), + FMul_ST_reg(src1), + FAddP_reg_ST(src2) ); + ins_pipe( fpu_reg_reg_reg ); +%} + + +// MACRO3 -- subD a mulD +instruct subD_mulD_reg(regD src2, regD src1, regD src0) %{ + predicate( UseSSE<=1 ); + match(Set src2 (SubD (MulD src0 src1) src2)); + format %{ "FLD $src0\t# ===MACRO3d===\n\t" + "DMUL ST,$src1\n\t" + "DSUBRp $src2,ST" %} + ins_cost(250); + ins_encode( Push_Reg_F(src0), + FMul_ST_reg(src1), + Opcode(0xDE), Opc_plus(0xE0,src2)); + ins_pipe( fpu_reg_reg_reg ); +%} + + +instruct divD_reg(regD dst, regD src) %{ + predicate( UseSSE<=1 ); + match(Set dst (DivD dst src)); + + format %{ "FLD $src\n\t" + "FDIVp $dst,ST" %} + opcode(0xDE, 0x7); /* DE F8+i or DE /7*/ + ins_cost(150); + ins_encode( Push_Reg_D(src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_reg ); +%} + +// Strict FP instruction biases argument before division then +// biases result, to avoid double rounding of subnormals. +// +// scale dividend by multiplying dividend by 2^(-15360) +// load divisor +// divide scaled dividend by divisor +// rescale quotient by 2^(15360) +// +instruct strictfp_divD_reg(regDPR1 dst, regnotDPR1 src) %{ + predicate (UseSSE<=1); + match(Set dst (DivD dst src)); + predicate( UseSSE<=1 && Compile::current()->has_method() && Compile::current()->method()->is_strict() ); + ins_cost(01); + + format %{ "FLD StubRoutines::_fpu_subnormal_bias1\n\t" + "DMULp $dst,ST\n\t" + "FLD $src\n\t" + "FDIVp $dst,ST\n\t" + "FLD StubRoutines::_fpu_subnormal_bias2\n\t" + "DMULp $dst,ST\n\t" %} + opcode(0xDE, 0x7); /* DE F8+i or DE /7*/ + ins_encode( strictfp_bias1(dst), + Push_Reg_D(src), + OpcP, RegOpc(dst), + strictfp_bias2(dst) ); + ins_pipe( fpu_reg_reg ); +%} + +instruct divD_reg_round(stackSlotD dst, regD src1, regD src2) %{ + predicate( UseSSE<=1 && !(Compile::current()->has_method() && Compile::current()->method()->is_strict()) ); + match(Set dst (RoundDouble (DivD src1 src2))); + + format %{ "FLD $src1\n\t" + "FDIV ST,$src2\n\t" + "FSTP_D $dst\t# D-round" %} + opcode(0xD8, 0x6); /* D8 F0+i or D8 /6 */ + ins_encode( Push_Reg_D(src1), + OpcP, RegOpc(src2), Pop_Mem_D(dst) ); + ins_pipe( fpu_mem_reg_reg ); +%} + + +instruct modD_reg(regD dst, regD src, eAXRegI rax, eFlagsReg cr) %{ + predicate(UseSSE<=1); + match(Set dst (ModD dst src)); + effect(KILL rax, KILL cr); // emitModD() uses EAX and EFLAGS + + format %{ "DMOD $dst,$src" %} + ins_cost(250); + ins_encode(Push_Reg_Mod_D(dst, src), + emitModD(), + Push_Result_Mod_D(src), + Pop_Reg_D(dst)); + ins_pipe( pipe_slow ); +%} + +instruct modXD_reg(regXD dst, regXD src0, regXD src1, eAXRegI rax, eFlagsReg cr) %{ + predicate(UseSSE>=2); + match(Set dst (ModD src0 src1)); + effect(KILL rax, KILL cr); + + format %{ "SUB ESP,8\t # DMOD\n" + "\tMOVSD [ESP+0],$src1\n" + "\tFLD_D [ESP+0]\n" + "\tMOVSD [ESP+0],$src0\n" + "\tFLD_D [ESP+0]\n" + "loop:\tFPREM\n" + "\tFWAIT\n" + "\tFNSTSW AX\n" + "\tSAHF\n" + "\tJP loop\n" + "\tFSTP_D [ESP+0]\n" + "\tMOVSD $dst,[ESP+0]\n" + "\tADD ESP,8\n" + "\tFSTP ST0\t # Restore FPU Stack" + %} + ins_cost(250); + ins_encode( Push_ModD_encoding(src0, src1), emitModD(), Push_ResultXD(dst), PopFPU); + ins_pipe( pipe_slow ); +%} + +instruct sinD_reg(regDPR1 dst, regDPR1 src) %{ + predicate (UseSSE<=1); + match(Set dst (SinD src)); + ins_cost(1800); + format %{ "DSIN $dst" %} + opcode(0xD9, 0xFE); + ins_encode( OpcP, OpcS ); + ins_pipe( pipe_slow ); +%} + +instruct sinXD_reg(regXD dst, eFlagsReg cr) %{ + predicate (UseSSE>=2); + match(Set dst (SinD dst)); + effect(KILL cr); // Push_{Src|Result}XD() uses "{SUB|ADD} ESP,8" + ins_cost(1800); + format %{ "DSIN $dst" %} + opcode(0xD9, 0xFE); + ins_encode( Push_SrcXD(dst), OpcP, OpcS, Push_ResultXD(dst) ); + ins_pipe( pipe_slow ); +%} + +instruct cosD_reg(regDPR1 dst, regDPR1 src) %{ + predicate (UseSSE<=1); + match(Set dst (CosD src)); + ins_cost(1800); + format %{ "DCOS $dst" %} + opcode(0xD9, 0xFF); + ins_encode( OpcP, OpcS ); + ins_pipe( pipe_slow ); +%} + +instruct cosXD_reg(regXD dst, eFlagsReg cr) %{ + predicate (UseSSE>=2); + match(Set dst (CosD dst)); + effect(KILL cr); // Push_{Src|Result}XD() uses "{SUB|ADD} ESP,8" + ins_cost(1800); + format %{ "DCOS $dst" %} + opcode(0xD9, 0xFF); + ins_encode( Push_SrcXD(dst), OpcP, OpcS, Push_ResultXD(dst) ); + ins_pipe( pipe_slow ); +%} + +instruct tanD_reg(regDPR1 dst, regDPR1 src) %{ + predicate (UseSSE<=1); + match(Set dst(TanD src)); + format %{ "DTAN $dst" %} + ins_encode( Opcode(0xD9), Opcode(0xF2), // fptan + Opcode(0xDD), Opcode(0xD8)); // fstp st + ins_pipe( pipe_slow ); +%} + +instruct tanXD_reg(regXD dst, eFlagsReg cr) %{ + predicate (UseSSE>=2); + match(Set dst(TanD dst)); + effect(KILL cr); // Push_{Src|Result}XD() uses "{SUB|ADD} ESP,8" + format %{ "DTAN $dst" %} + ins_encode( Push_SrcXD(dst), + Opcode(0xD9), Opcode(0xF2), // fptan + Opcode(0xDD), Opcode(0xD8), // fstp st + Push_ResultXD(dst) ); + ins_pipe( pipe_slow ); +%} + +instruct atanD_reg(regD dst, regD src) %{ + predicate (UseSSE<=1); + match(Set dst(AtanD dst src)); + format %{ "DATA $dst,$src" %} + opcode(0xD9, 0xF3); + ins_encode( Push_Reg_D(src), + OpcP, OpcS, RegOpc(dst) ); + ins_pipe( pipe_slow ); +%} + +instruct atanXD_reg(regXD dst, regXD src, eFlagsReg cr) %{ + predicate (UseSSE>=2); + match(Set dst(AtanD dst src)); + effect(KILL cr); // Push_{Src|Result}XD() uses "{SUB|ADD} ESP,8" + format %{ "DATA $dst,$src" %} + opcode(0xD9, 0xF3); + ins_encode( Push_SrcXD(src), + OpcP, OpcS, Push_ResultXD(dst) ); + ins_pipe( pipe_slow ); +%} + +instruct sqrtD_reg(regD dst, regD src) %{ + predicate (UseSSE<=1); + match(Set dst (SqrtD src)); + format %{ "DSQRT $dst,$src" %} + opcode(0xFA, 0xD9); + ins_encode( Push_Reg_D(src), + OpcS, OpcP, Pop_Reg_D(dst) ); + ins_pipe( pipe_slow ); +%} + +instruct powD_reg(regD X, regDPR1 Y, eAXRegI rax, eBXRegI rbx, eCXRegI rcx) %{ + predicate (UseSSE<=1); + match(Set Y (PowD X Y)); // Raise X to the Yth power + effect(KILL rax, KILL rbx, KILL rcx); + format %{ "SUB ESP,8\t\t# Fast-path POW encoding\n\t" + "FLD_D $X\n\t" + "FYL2X \t\t\t# Q=Y*ln2(X)\n\t" + + "FDUP \t\t\t# Q Q\n\t" + "FRNDINT\t\t\t# int(Q) Q\n\t" + "FSUB ST(1),ST(0)\t# int(Q) frac(Q)\n\t" + "FISTP dword [ESP]\n\t" + "F2XM1 \t\t\t# 2^frac(Q)-1 int(Q)\n\t" + "FLD1 \t\t\t# 1 2^frac(Q)-1 int(Q)\n\t" + "FADDP \t\t\t# 2^frac(Q) int(Q)\n\t" // could use FADD [1.000] instead + "MOV EAX,[ESP]\t# Pick up int(Q)\n\t" + "MOV ECX,0xFFFFF800\t# Overflow mask\n\t" + "ADD EAX,1023\t\t# Double exponent bias\n\t" + "MOV EBX,EAX\t\t# Preshifted biased expo\n\t" + "SHL EAX,20\t\t# Shift exponent into place\n\t" + "TEST EBX,ECX\t\t# Check for overflow\n\t" + "CMOVne EAX,ECX\t\t# If overflow, stuff NaN into EAX\n\t" + "MOV [ESP+4],EAX\t# Marshal 64-bit scaling double\n\t" + "MOV [ESP+0],0\n\t" + "FMUL ST(0),[ESP+0]\t# Scale\n\t" + + "ADD ESP,8" + %} + ins_encode( push_stack_temp_qword, + Push_Reg_D(X), + Opcode(0xD9), Opcode(0xF1), // fyl2x + pow_exp_core_encoding, + pop_stack_temp_qword); + ins_pipe( pipe_slow ); +%} + +instruct powXD_reg(regXD dst, regXD src0, regXD src1, regDPR1 tmp1, eAXRegI rax, eBXRegI rbx, eCXRegI rcx ) %{ + predicate (UseSSE>=2); + match(Set dst (PowD src0 src1)); // Raise src0 to the src1'th power + effect(KILL tmp1, KILL rax, KILL rbx, KILL rcx ); + format %{ "SUB ESP,8\t\t# Fast-path POW encoding\n\t" + "MOVSD [ESP],$src1\n\t" + "FLD FPR1,$src1\n\t" + "MOVSD [ESP],$src0\n\t" + "FLD FPR1,$src0\n\t" + "FYL2X \t\t\t# Q=Y*ln2(X)\n\t" + + "FDUP \t\t\t# Q Q\n\t" + "FRNDINT\t\t\t# int(Q) Q\n\t" + "FSUB ST(1),ST(0)\t# int(Q) frac(Q)\n\t" + "FISTP dword [ESP]\n\t" + "F2XM1 \t\t\t# 2^frac(Q)-1 int(Q)\n\t" + "FLD1 \t\t\t# 1 2^frac(Q)-1 int(Q)\n\t" + "FADDP \t\t\t# 2^frac(Q) int(Q)\n\t" // could use FADD [1.000] instead + "MOV EAX,[ESP]\t# Pick up int(Q)\n\t" + "MOV ECX,0xFFFFF800\t# Overflow mask\n\t" + "ADD EAX,1023\t\t# Double exponent bias\n\t" + "MOV EBX,EAX\t\t# Preshifted biased expo\n\t" + "SHL EAX,20\t\t# Shift exponent into place\n\t" + "TEST EBX,ECX\t\t# Check for overflow\n\t" + "CMOVne EAX,ECX\t\t# If overflow, stuff NaN into EAX\n\t" + "MOV [ESP+4],EAX\t# Marshal 64-bit scaling double\n\t" + "MOV [ESP+0],0\n\t" + "FMUL ST(0),[ESP+0]\t# Scale\n\t" + + "FST_D [ESP]\n\t" + "MOVSD $dst,[ESP]\n\t" + "ADD ESP,8" + %} + ins_encode( push_stack_temp_qword, + push_xmm_to_fpr1(src1), + push_xmm_to_fpr1(src0), + Opcode(0xD9), Opcode(0xF1), // fyl2x + pow_exp_core_encoding, + Push_ResultXD(dst) ); + ins_pipe( pipe_slow ); +%} + + +instruct expD_reg(regDPR1 dpr1, eAXRegI rax, eBXRegI rbx, eCXRegI rcx) %{ + predicate (UseSSE<=1); + match(Set dpr1 (ExpD dpr1)); + effect(KILL rax, KILL rbx, KILL rcx); + format %{ "SUB ESP,8\t\t# Fast-path EXP encoding" + "FLDL2E \t\t\t# Ld log2(e) X\n\t" + "FMULP \t\t\t# Q=X*log2(e)\n\t" + + "FDUP \t\t\t# Q Q\n\t" + "FRNDINT\t\t\t# int(Q) Q\n\t" + "FSUB ST(1),ST(0)\t# int(Q) frac(Q)\n\t" + "FISTP dword [ESP]\n\t" + "F2XM1 \t\t\t# 2^frac(Q)-1 int(Q)\n\t" + "FLD1 \t\t\t# 1 2^frac(Q)-1 int(Q)\n\t" + "FADDP \t\t\t# 2^frac(Q) int(Q)\n\t" // could use FADD [1.000] instead + "MOV EAX,[ESP]\t# Pick up int(Q)\n\t" + "MOV ECX,0xFFFFF800\t# Overflow mask\n\t" + "ADD EAX,1023\t\t# Double exponent bias\n\t" + "MOV EBX,EAX\t\t# Preshifted biased expo\n\t" + "SHL EAX,20\t\t# Shift exponent into place\n\t" + "TEST EBX,ECX\t\t# Check for overflow\n\t" + "CMOVne EAX,ECX\t\t# If overflow, stuff NaN into EAX\n\t" + "MOV [ESP+4],EAX\t# Marshal 64-bit scaling double\n\t" + "MOV [ESP+0],0\n\t" + "FMUL ST(0),[ESP+0]\t# Scale\n\t" + + "ADD ESP,8" + %} + ins_encode( push_stack_temp_qword, + Opcode(0xD9), Opcode(0xEA), // fldl2e + Opcode(0xDE), Opcode(0xC9), // fmulp + pow_exp_core_encoding, + pop_stack_temp_qword); + ins_pipe( pipe_slow ); +%} + +instruct expXD_reg(regXD dst, regXD src, regDPR1 tmp1, eAXRegI rax, eBXRegI rbx, eCXRegI rcx) %{ + predicate (UseSSE>=2); + match(Set dst (ExpD src)); + effect(KILL tmp1, KILL rax, KILL rbx, KILL rcx); + format %{ "SUB ESP,8\t\t# Fast-path EXP encoding\n\t" + "MOVSD [ESP],$src\n\t" + "FLDL2E \t\t\t# Ld log2(e) X\n\t" + "FMULP \t\t\t# Q=X*log2(e) X\n\t" + + "FDUP \t\t\t# Q Q\n\t" + "FRNDINT\t\t\t# int(Q) Q\n\t" + "FSUB ST(1),ST(0)\t# int(Q) frac(Q)\n\t" + "FISTP dword [ESP]\n\t" + "F2XM1 \t\t\t# 2^frac(Q)-1 int(Q)\n\t" + "FLD1 \t\t\t# 1 2^frac(Q)-1 int(Q)\n\t" + "FADDP \t\t\t# 2^frac(Q) int(Q)\n\t" // could use FADD [1.000] instead + "MOV EAX,[ESP]\t# Pick up int(Q)\n\t" + "MOV ECX,0xFFFFF800\t# Overflow mask\n\t" + "ADD EAX,1023\t\t# Double exponent bias\n\t" + "MOV EBX,EAX\t\t# Preshifted biased expo\n\t" + "SHL EAX,20\t\t# Shift exponent into place\n\t" + "TEST EBX,ECX\t\t# Check for overflow\n\t" + "CMOVne EAX,ECX\t\t# If overflow, stuff NaN into EAX\n\t" + "MOV [ESP+4],EAX\t# Marshal 64-bit scaling double\n\t" + "MOV [ESP+0],0\n\t" + "FMUL ST(0),[ESP+0]\t# Scale\n\t" + + "FST_D [ESP]\n\t" + "MOVSD $dst,[ESP]\n\t" + "ADD ESP,8" + %} + ins_encode( Push_SrcXD(src), + Opcode(0xD9), Opcode(0xEA), // fldl2e + Opcode(0xDE), Opcode(0xC9), // fmulp + pow_exp_core_encoding, + Push_ResultXD(dst) ); + ins_pipe( pipe_slow ); +%} + + + +instruct log10D_reg(regDPR1 dst, regDPR1 src) %{ + predicate (UseSSE<=1); + // The source Double operand on FPU stack + match(Set dst (Log10D src)); + // fldlg2 ; push log_10(2) on the FPU stack; full 80-bit number + // fxch ; swap ST(0) with ST(1) + // fyl2x ; compute log_10(2) * log_2(x) + format %{ "FLDLG2 \t\t\t#Log10\n\t" + "FXCH \n\t" + "FYL2X \t\t\t# Q=Log10*Log_2(x)" + %} + ins_encode( Opcode(0xD9), Opcode(0xEC), // fldlg2 + Opcode(0xD9), Opcode(0xC9), // fxch + Opcode(0xD9), Opcode(0xF1)); // fyl2x + + ins_pipe( pipe_slow ); +%} + +instruct log10XD_reg(regXD dst, regXD src, eFlagsReg cr) %{ + predicate (UseSSE>=2); + effect(KILL cr); + match(Set dst (Log10D src)); + // fldlg2 ; push log_10(2) on the FPU stack; full 80-bit number + // fyl2x ; compute log_10(2) * log_2(x) + format %{ "FLDLG2 \t\t\t#Log10\n\t" + "FYL2X \t\t\t# Q=Log10*Log_2(x)" + %} + ins_encode( Opcode(0xD9), Opcode(0xEC), // fldlg2 + Push_SrcXD(src), + Opcode(0xD9), Opcode(0xF1), // fyl2x + Push_ResultXD(dst)); + + ins_pipe( pipe_slow ); +%} + +instruct logD_reg(regDPR1 dst, regDPR1 src) %{ + predicate (UseSSE<=1); + // The source Double operand on FPU stack + match(Set dst (LogD src)); + // fldln2 ; push log_e(2) on the FPU stack; full 80-bit number + // fxch ; swap ST(0) with ST(1) + // fyl2x ; compute log_e(2) * log_2(x) + format %{ "FLDLN2 \t\t\t#Log_e\n\t" + "FXCH \n\t" + "FYL2X \t\t\t# Q=Log_e*Log_2(x)" + %} + ins_encode( Opcode(0xD9), Opcode(0xED), // fldln2 + Opcode(0xD9), Opcode(0xC9), // fxch + Opcode(0xD9), Opcode(0xF1)); // fyl2x + + ins_pipe( pipe_slow ); +%} + +instruct logXD_reg(regXD dst, regXD src, eFlagsReg cr) %{ + predicate (UseSSE>=2); + effect(KILL cr); + // The source and result Double operands in XMM registers + match(Set dst (LogD src)); + // fldln2 ; push log_e(2) on the FPU stack; full 80-bit number + // fyl2x ; compute log_e(2) * log_2(x) + format %{ "FLDLN2 \t\t\t#Log_e\n\t" + "FYL2X \t\t\t# Q=Log_e*Log_2(x)" + %} + ins_encode( Opcode(0xD9), Opcode(0xED), // fldln2 + Push_SrcXD(src), + Opcode(0xD9), Opcode(0xF1), // fyl2x + Push_ResultXD(dst)); + ins_pipe( pipe_slow ); +%} + +//-------------Float Instructions------------------------------- +// Float Math + +// Code for float compare: +// fcompp(); +// fwait(); fnstsw_ax(); +// sahf(); +// movl(dst, unordered_result); +// jcc(Assembler::parity, exit); +// movl(dst, less_result); +// jcc(Assembler::below, exit); +// movl(dst, equal_result); +// jcc(Assembler::equal, exit); +// movl(dst, greater_result); +// exit: + +// P6 version of float compare, sets condition codes in EFLAGS +instruct cmpF_cc_P6(eFlagsRegU cr, regF src1, regF src2, eAXRegI rax) %{ + predicate(VM_Version::supports_cmov() && UseSSE == 0); + match(Set cr (CmpF src1 src2)); + effect(KILL rax); + ins_cost(150); + format %{ "FLD $src1\n\t" + "FUCOMIP ST,$src2 // P6 instruction\n\t" + "JNP exit\n\t" + "MOV ah,1 // saw a NaN, set CF (treat as LT)\n\t" + "SAHF\n" + "exit:\tNOP // avoid branch to branch" %} + opcode(0xDF, 0x05); /* DF E8+i or DF /5 */ + ins_encode( Push_Reg_D(src1), + OpcP, RegOpc(src2), + cmpF_P6_fixup ); + ins_pipe( pipe_slow ); +%} + + +// Compare & branch +instruct cmpF_cc(eFlagsRegU cr, regF src1, regF src2, eAXRegI rax) %{ + predicate(UseSSE == 0); + match(Set cr (CmpF src1 src2)); + effect(KILL rax); + ins_cost(200); + format %{ "FLD $src1\n\t" + "FCOMp $src2\n\t" + "FNSTSW AX\n\t" + "TEST AX,0x400\n\t" + "JZ,s flags\n\t" + "MOV AH,1\t# unordered treat as LT\n" + "flags:\tSAHF" %} + opcode(0xD8, 0x3); /* D8 D8+i or D8 /3 */ + ins_encode( Push_Reg_D(src1), + OpcP, RegOpc(src2), + fpu_flags); + ins_pipe( pipe_slow ); +%} + +// Compare vs zero into -1,0,1 +instruct cmpF_0(eRegI dst, regF src1, immF0 zero, eAXRegI rax, eFlagsReg cr) %{ + predicate(UseSSE == 0); + match(Set dst (CmpF3 src1 zero)); + effect(KILL cr, KILL rax); + ins_cost(280); + format %{ "FTSTF $dst,$src1" %} + opcode(0xE4, 0xD9); + ins_encode( Push_Reg_D(src1), + OpcS, OpcP, PopFPU, + CmpF_Result(dst)); + ins_pipe( pipe_slow ); +%} + +// Compare into -1,0,1 +instruct cmpF_reg(eRegI dst, regF src1, regF src2, eAXRegI rax, eFlagsReg cr) %{ + predicate(UseSSE == 0); + match(Set dst (CmpF3 src1 src2)); + effect(KILL cr, KILL rax); + ins_cost(300); + format %{ "FCMPF $dst,$src1,$src2" %} + opcode(0xD8, 0x3); /* D8 D8+i or D8 /3 */ + ins_encode( Push_Reg_D(src1), + OpcP, RegOpc(src2), + CmpF_Result(dst)); + ins_pipe( pipe_slow ); +%} + +// float compare and set condition codes in EFLAGS by XMM regs +instruct cmpX_cc(eFlagsRegU cr, regX dst, regX src, eAXRegI rax) %{ + predicate(UseSSE>=1); + match(Set cr (CmpF dst src)); + effect(KILL rax); + ins_cost(145); + format %{ "COMISS $dst,$src\n" + "\tJNP exit\n" + "\tMOV ah,1 // saw a NaN, set CF\n" + "\tSAHF\n" + "exit:\tNOP // avoid branch to branch" %} + opcode(0x0F, 0x2F); + ins_encode(OpcP, OpcS, RegReg(dst, src), cmpF_P6_fixup); + ins_pipe( pipe_slow ); +%} + +// float compare and set condition codes in EFLAGS by XMM regs +instruct cmpX_ccmem(eFlagsRegU cr, regX dst, memory src, eAXRegI rax) %{ + predicate(UseSSE>=1); + match(Set cr (CmpF dst (LoadF src))); + effect(KILL rax); + ins_cost(165); + format %{ "COMISS $dst,$src\n" + "\tJNP exit\n" + "\tMOV ah,1 // saw a NaN, set CF\n" + "\tSAHF\n" + "exit:\tNOP // avoid branch to branch" %} + opcode(0x0F, 0x2F); + ins_encode(OpcP, OpcS, RegMem(dst, src), cmpF_P6_fixup); + ins_pipe( pipe_slow ); +%} + +// Compare into -1,0,1 in XMM +instruct cmpX_reg(eRegI dst, regX src1, regX src2, eFlagsReg cr) %{ + predicate(UseSSE>=1); + match(Set dst (CmpF3 src1 src2)); + effect(KILL cr); + ins_cost(255); + format %{ "XOR $dst,$dst\n" + "\tCOMISS $src1,$src2\n" + "\tJP,s nan\n" + "\tJEQ,s exit\n" + "\tJA,s inc\n" + "nan:\tDEC $dst\n" + "\tJMP,s exit\n" + "inc:\tINC $dst\n" + "exit:" + %} + opcode(0x0F, 0x2F); + ins_encode(Xor_Reg(dst), OpcP, OpcS, RegReg(src1, src2), CmpX_Result(dst)); + ins_pipe( pipe_slow ); +%} + +// Compare into -1,0,1 in XMM and memory +instruct cmpX_regmem(eRegI dst, regX src1, memory mem, eFlagsReg cr) %{ + predicate(UseSSE>=1); + match(Set dst (CmpF3 src1 (LoadF mem))); + effect(KILL cr); + ins_cost(275); + format %{ "COMISS $src1,$mem\n" + "\tMOV $dst,0\t\t# do not blow flags\n" + "\tJP,s nan\n" + "\tJEQ,s exit\n" + "\tJA,s inc\n" + "nan:\tDEC $dst\n" + "\tJMP,s exit\n" + "inc:\tINC $dst\n" + "exit:" + %} + opcode(0x0F, 0x2F); + ins_encode(OpcP, OpcS, RegMem(src1, mem), LdImmI(dst,0x0), CmpX_Result(dst)); + ins_pipe( pipe_slow ); +%} + +// Spill to obtain 24-bit precision +instruct subF24_reg(stackSlotF dst, regF src1, regF src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (SubF src1 src2)); + + format %{ "FSUB $dst,$src1 - $src2" %} + opcode(0xD8, 0x4); /* D8 E0+i or D8 /4 mod==0x3 ;; result in TOS */ + ins_encode( Push_Reg_F(src1), + OpcReg_F(src2), + Pop_Mem_F(dst) ); + ins_pipe( fpu_mem_reg_reg ); +%} +// +// This instruction does not round to 24-bits +instruct subF_reg(regF dst, regF src) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (SubF dst src)); + + format %{ "FSUB $dst,$src" %} + opcode(0xDE, 0x5); /* DE E8+i or DE /5 */ + ins_encode( Push_Reg_F(src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_reg ); +%} + +// Spill to obtain 24-bit precision +instruct addF24_reg(stackSlotF dst, regF src1, regF src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (AddF src1 src2)); + + format %{ "FADD $dst,$src1,$src2" %} + opcode(0xD8, 0x0); /* D8 C0+i */ + ins_encode( Push_Reg_F(src2), + OpcReg_F(src1), + Pop_Mem_F(dst) ); + ins_pipe( fpu_mem_reg_reg ); +%} +// +// This instruction does not round to 24-bits +instruct addF_reg(regF dst, regF src) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (AddF dst src)); + + format %{ "FLD $src\n\t" + "FADDp $dst,ST" %} + opcode(0xDE, 0x0); /* DE C0+i or DE /0*/ + ins_encode( Push_Reg_F(src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_reg ); +%} + +// Add two single precision floating point values in xmm +instruct addX_reg(regX dst, regX src) %{ + predicate(UseSSE>=1); + match(Set dst (AddF dst src)); + format %{ "ADDSS $dst,$src" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x58), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct addX_imm(regX dst, immXF con) %{ + predicate(UseSSE>=1); + match(Set dst (AddF dst con)); + format %{ "ADDSS $dst,[$con]" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x58), LdImmX(dst, con) ); + ins_pipe( pipe_slow ); +%} + +instruct addX_mem(regX dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (AddF dst (LoadF mem))); + format %{ "ADDSS $dst,$mem" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x58), RegMem(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Subtract two single precision floating point values in xmm +instruct subX_reg(regX dst, regX src) %{ + predicate(UseSSE>=1); + match(Set dst (SubF dst src)); + format %{ "SUBSS $dst,$src" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x5C), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct subX_imm(regX dst, immXF con) %{ + predicate(UseSSE>=1); + match(Set dst (SubF dst con)); + format %{ "SUBSS $dst,[$con]" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x5C), LdImmX(dst, con) ); + ins_pipe( pipe_slow ); +%} + +instruct subX_mem(regX dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (SubF dst (LoadF mem))); + format %{ "SUBSS $dst,$mem" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x5C), RegMem(dst,mem)); + ins_pipe( pipe_slow ); +%} + +// Multiply two single precision floating point values in xmm +instruct mulX_reg(regX dst, regX src) %{ + predicate(UseSSE>=1); + match(Set dst (MulF dst src)); + format %{ "MULSS $dst,$src" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x59), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct mulX_imm(regX dst, immXF con) %{ + predicate(UseSSE>=1); + match(Set dst (MulF dst con)); + format %{ "MULSS $dst,[$con]" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x59), LdImmX(dst, con) ); + ins_pipe( pipe_slow ); +%} + +instruct mulX_mem(regX dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (MulF dst (LoadF mem))); + format %{ "MULSS $dst,$mem" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x59), RegMem(dst,mem)); + ins_pipe( pipe_slow ); +%} + +// Divide two single precision floating point values in xmm +instruct divX_reg(regX dst, regX src) %{ + predicate(UseSSE>=1); + match(Set dst (DivF dst src)); + format %{ "DIVSS $dst,$src" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x5E), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct divX_imm(regX dst, immXF con) %{ + predicate(UseSSE>=1); + match(Set dst (DivF dst con)); + format %{ "DIVSS $dst,[$con]" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x5E), LdImmX(dst, con) ); + ins_pipe( pipe_slow ); +%} + +instruct divX_mem(regX dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (DivF dst (LoadF mem))); + format %{ "DIVSS $dst,$mem" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x5E), RegMem(dst,mem)); + ins_pipe( pipe_slow ); +%} + +// Get the square root of a single precision floating point values in xmm +instruct sqrtX_reg(regX dst, regX src) %{ + predicate(UseSSE>=1); + match(Set dst (ConvD2F (SqrtD (ConvF2D src)))); + format %{ "SQRTSS $dst,$src" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x51), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct sqrtX_mem(regX dst, memory mem) %{ + predicate(UseSSE>=1); + match(Set dst (ConvD2F (SqrtD (ConvF2D (LoadF mem))))); + format %{ "SQRTSS $dst,$mem" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x51), RegMem(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Get the square root of a double precision floating point values in xmm +instruct sqrtXD_reg(regXD dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (SqrtD src)); + format %{ "SQRTSD $dst,$src" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x51), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct sqrtXD_mem(regXD dst, memory mem) %{ + predicate(UseSSE>=2); + match(Set dst (SqrtD (LoadD mem))); + format %{ "SQRTSD $dst,$mem" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x51), RegMem(dst, mem)); + ins_pipe( pipe_slow ); +%} + +instruct absF_reg(regFPR1 dst, regFPR1 src) %{ + predicate(UseSSE==0); + match(Set dst (AbsF src)); + ins_cost(100); + format %{ "FABS" %} + opcode(0xE1, 0xD9); + ins_encode( OpcS, OpcP ); + ins_pipe( fpu_reg_reg ); +%} + +instruct absX_reg(regX dst ) %{ + predicate(UseSSE>=1); + match(Set dst (AbsF dst)); + format %{ "ANDPS $dst,[0x7FFFFFFF]\t# ABS F by sign masking" %} + ins_encode( AbsXF_encoding(dst)); + ins_pipe( pipe_slow ); +%} + +instruct negF_reg(regFPR1 dst, regFPR1 src) %{ + predicate(UseSSE==0); + match(Set dst (NegF src)); + ins_cost(100); + format %{ "FCHS" %} + opcode(0xE0, 0xD9); + ins_encode( OpcS, OpcP ); + ins_pipe( fpu_reg_reg ); +%} + +instruct negX_reg( regX dst ) %{ + predicate(UseSSE>=1); + match(Set dst (NegF dst)); + format %{ "XORPS $dst,[0x80000000]\t# CHS F by sign flipping" %} + ins_encode( NegXF_encoding(dst)); + ins_pipe( pipe_slow ); +%} + +// Cisc-alternate to addF_reg +// Spill to obtain 24-bit precision +instruct addF24_reg_mem(stackSlotF dst, regF src1, memory src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (AddF src1 (LoadF src2))); + + format %{ "FLD $src2\n\t" + "FADD ST,$src1\n\t" + "FSTP_S $dst" %} + opcode(0xD8, 0x0, 0xD9); /* D8 C0+i */ /* LoadF D9 /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src2), + OpcReg_F(src1), + Pop_Mem_F(dst) ); + ins_pipe( fpu_mem_reg_mem ); +%} +// +// Cisc-alternate to addF_reg +// This instruction does not round to 24-bits +instruct addF_reg_mem(regF dst, memory src) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (AddF dst (LoadF src))); + + format %{ "FADD $dst,$src" %} + opcode(0xDE, 0x0, 0xD9); /* DE C0+i or DE /0*/ /* LoadF D9 /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_mem ); +%} + +// // Following two instructions for _222_mpegaudio +// Spill to obtain 24-bit precision +instruct addF24_mem_reg(stackSlotF dst, regF src2, memory src1 ) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (AddF src1 src2)); + + format %{ "FADD $dst,$src1,$src2" %} + opcode(0xD8, 0x0, 0xD9); /* D8 C0+i */ /* LoadF D9 /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src1), + OpcReg_F(src2), + Pop_Mem_F(dst) ); + ins_pipe( fpu_mem_reg_mem ); +%} + +// Cisc-spill variant +// Spill to obtain 24-bit precision +instruct addF24_mem_cisc(stackSlotF dst, memory src1, memory src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (AddF src1 (LoadF src2))); + + format %{ "FADD $dst,$src1,$src2 cisc" %} + opcode(0xD8, 0x0, 0xD9); /* D8 C0+i */ /* LoadF D9 /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src2), + set_instruction_start, + OpcP, RMopc_Mem(secondary,src1), + Pop_Mem_F(dst) ); + ins_pipe( fpu_mem_mem_mem ); +%} + +// Spill to obtain 24-bit precision +instruct addF24_mem_mem(stackSlotF dst, memory src1, memory src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (AddF src1 src2)); + + format %{ "FADD $dst,$src1,$src2" %} + opcode(0xD8, 0x0, 0xD9); /* D8 /0 */ /* LoadF D9 /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src2), + set_instruction_start, + OpcP, RMopc_Mem(secondary,src1), + Pop_Mem_F(dst) ); + ins_pipe( fpu_mem_mem_mem ); +%} + + +// Spill to obtain 24-bit precision +instruct addF24_reg_imm(stackSlotF dst, regF src1, immF src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (AddF src1 src2)); + format %{ "FLD $src1\n\t" + "FADD $src2\n\t" + "FSTP_S $dst" %} + opcode(0xD8, 0x00); /* D8 /0 */ + ins_encode( Push_Reg_F(src1), + Opc_MemImm_F(src2), + Pop_Mem_F(dst)); + ins_pipe( fpu_mem_reg_con ); +%} +// +// This instruction does not round to 24-bits +instruct addF_reg_imm(regF dst, regF src1, immF src2) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (AddF src1 src2)); + format %{ "FLD $src1\n\t" + "FADD $src2\n\t" + "FSTP_S $dst" %} + opcode(0xD8, 0x00); /* D8 /0 */ + ins_encode( Push_Reg_F(src1), + Opc_MemImm_F(src2), + Pop_Reg_F(dst)); + ins_pipe( fpu_reg_reg_con ); +%} + +// Spill to obtain 24-bit precision +instruct mulF24_reg(stackSlotF dst, regF src1, regF src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (MulF src1 src2)); + + format %{ "FLD $src1\n\t" + "FMUL $src2\n\t" + "FSTP_S $dst" %} + opcode(0xD8, 0x1); /* D8 C8+i or D8 /1 ;; result in TOS */ + ins_encode( Push_Reg_F(src1), + OpcReg_F(src2), + Pop_Mem_F(dst) ); + ins_pipe( fpu_mem_reg_reg ); +%} +// +// This instruction does not round to 24-bits +instruct mulF_reg(regF dst, regF src1, regF src2) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (MulF src1 src2)); + + format %{ "FLD $src1\n\t" + "FMUL $src2\n\t" + "FSTP_S $dst" %} + opcode(0xD8, 0x1); /* D8 C8+i */ + ins_encode( Push_Reg_F(src2), + OpcReg_F(src1), + Pop_Reg_F(dst) ); + ins_pipe( fpu_reg_reg_reg ); +%} + + +// Spill to obtain 24-bit precision +// Cisc-alternate to reg-reg multiply +instruct mulF24_reg_mem(stackSlotF dst, regF src1, memory src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (MulF src1 (LoadF src2))); + + format %{ "FLD_S $src2\n\t" + "FMUL $src1\n\t" + "FSTP_S $dst" %} + opcode(0xD8, 0x1, 0xD9); /* D8 C8+i or DE /1*/ /* LoadF D9 /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src2), + OpcReg_F(src1), + Pop_Mem_F(dst) ); + ins_pipe( fpu_mem_reg_mem ); +%} +// +// This instruction does not round to 24-bits +// Cisc-alternate to reg-reg multiply +instruct mulF_reg_mem(regF dst, regF src1, memory src2) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (MulF src1 (LoadF src2))); + + format %{ "FMUL $dst,$src1,$src2" %} + opcode(0xD8, 0x1, 0xD9); /* D8 C8+i */ /* LoadF D9 /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src2), + OpcReg_F(src1), + Pop_Reg_F(dst) ); + ins_pipe( fpu_reg_reg_mem ); +%} + +// Spill to obtain 24-bit precision +instruct mulF24_mem_mem(stackSlotF dst, memory src1, memory src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (MulF src1 src2)); + + format %{ "FMUL $dst,$src1,$src2" %} + opcode(0xD8, 0x1, 0xD9); /* D8 /1 */ /* LoadF D9 /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,src2), + set_instruction_start, + OpcP, RMopc_Mem(secondary,src1), + Pop_Mem_F(dst) ); + ins_pipe( fpu_mem_mem_mem ); +%} + +// Spill to obtain 24-bit precision +instruct mulF24_reg_imm(stackSlotF dst, regF src1, immF src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (MulF src1 src2)); + + format %{ "FMULc $dst,$src1,$src2" %} + opcode(0xD8, 0x1); /* D8 /1*/ + ins_encode( Push_Reg_F(src1), + Opc_MemImm_F(src2), + Pop_Mem_F(dst)); + ins_pipe( fpu_mem_reg_con ); +%} +// +// This instruction does not round to 24-bits +instruct mulF_reg_imm(regF dst, regF src1, immF src2) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (MulF src1 src2)); + + format %{ "FMULc $dst. $src1, $src2" %} + opcode(0xD8, 0x1); /* D8 /1*/ + ins_encode( Push_Reg_F(src1), + Opc_MemImm_F(src2), + Pop_Reg_F(dst)); + ins_pipe( fpu_reg_reg_con ); +%} + + +// +// MACRO1 -- subsume unshared load into mulF +// This instruction does not round to 24-bits +instruct mulF_reg_load1(regF dst, regF src, memory mem1 ) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (MulF (LoadF mem1) src)); + + format %{ "FLD $mem1 ===MACRO1===\n\t" + "FMUL ST,$src\n\t" + "FSTP $dst" %} + opcode(0xD8, 0x1, 0xD9); /* D8 C8+i or D8 /1 */ /* LoadF D9 /0 */ + ins_encode( Opcode(tertiary), RMopc_Mem(0x00,mem1), + OpcReg_F(src), + Pop_Reg_F(dst) ); + ins_pipe( fpu_reg_reg_mem ); +%} +// +// MACRO2 -- addF a mulF which subsumed an unshared load +// This instruction does not round to 24-bits +instruct addF_mulF_reg_load1(regF dst, memory mem1, regF src1, regF src2) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (AddF (MulF (LoadF mem1) src1) src2)); + ins_cost(95); + + format %{ "FLD $mem1 ===MACRO2===\n\t" + "FMUL ST,$src1 subsume mulF left load\n\t" + "FADD ST,$src2\n\t" + "FSTP $dst" %} + opcode(0xD9); /* LoadF D9 /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem1), + FMul_ST_reg(src1), + FAdd_ST_reg(src2), + Pop_Reg_F(dst) ); + ins_pipe( fpu_reg_mem_reg_reg ); +%} + +// MACRO3 -- addF a mulF +// This instruction does not round to 24-bits. It is a '2-address' +// instruction in that the result goes back to src2. This eliminates +// a move from the macro; possibly the register allocator will have +// to add it back (and maybe not). +instruct addF_mulF_reg(regF src2, regF src1, regF src0) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set src2 (AddF (MulF src0 src1) src2)); + + format %{ "FLD $src0 ===MACRO3===\n\t" + "FMUL ST,$src1\n\t" + "FADDP $src2,ST" %} + opcode(0xD9); /* LoadF D9 /0 */ + ins_encode( Push_Reg_F(src0), + FMul_ST_reg(src1), + FAddP_reg_ST(src2) ); + ins_pipe( fpu_reg_reg_reg ); +%} + +// MACRO4 -- divF subF +// This instruction does not round to 24-bits +instruct subF_divF_reg(regF dst, regF src1, regF src2, regF src3) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (DivF (SubF src2 src1) src3)); + + format %{ "FLD $src2 ===MACRO4===\n\t" + "FSUB ST,$src1\n\t" + "FDIV ST,$src3\n\t" + "FSTP $dst" %} + opcode(0xDE, 0x7); /* DE F8+i or DE /7*/ + ins_encode( Push_Reg_F(src2), + subF_divF_encode(src1,src3), + Pop_Reg_F(dst) ); + ins_pipe( fpu_reg_reg_reg_reg ); +%} + +// Spill to obtain 24-bit precision +instruct divF24_reg(stackSlotF dst, regF src1, regF src2) %{ + predicate(UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (DivF src1 src2)); + + format %{ "FDIV $dst,$src1,$src2" %} + opcode(0xD8, 0x6); /* D8 F0+i or DE /6*/ + ins_encode( Push_Reg_F(src1), + OpcReg_F(src2), + Pop_Mem_F(dst) ); + ins_pipe( fpu_mem_reg_reg ); +%} +// +// This instruction does not round to 24-bits +instruct divF_reg(regF dst, regF src) %{ + predicate(UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (DivF dst src)); + + format %{ "FDIV $dst,$src" %} + opcode(0xDE, 0x7); /* DE F8+i or DE /7*/ + ins_encode( Push_Reg_F(src), + OpcP, RegOpc(dst) ); + ins_pipe( fpu_reg_reg ); +%} + + +// Spill to obtain 24-bit precision +instruct modF24_reg(stackSlotF dst, regF src1, regF src2, eAXRegI rax, eFlagsReg cr) %{ + predicate( UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (ModF src1 src2)); + effect(KILL rax, KILL cr); // emitModD() uses EAX and EFLAGS + + format %{ "FMOD $dst,$src1,$src2" %} + ins_encode( Push_Reg_Mod_D(src1, src2), + emitModD(), + Push_Result_Mod_D(src2), + Pop_Mem_F(dst)); + ins_pipe( pipe_slow ); +%} +// +// This instruction does not round to 24-bits +instruct modF_reg(regF dst, regF src, eAXRegI rax, eFlagsReg cr) %{ + predicate( UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (ModF dst src)); + effect(KILL rax, KILL cr); // emitModD() uses EAX and EFLAGS + + format %{ "FMOD $dst,$src" %} + ins_encode(Push_Reg_Mod_D(dst, src), + emitModD(), + Push_Result_Mod_D(src), + Pop_Reg_F(dst)); + ins_pipe( pipe_slow ); +%} + +instruct modX_reg(regX dst, regX src0, regX src1, eAXRegI rax, eFlagsReg cr) %{ + predicate(UseSSE>=1); + match(Set dst (ModF src0 src1)); + effect(KILL rax, KILL cr); + format %{ "SUB ESP,4\t # FMOD\n" + "\tMOVSS [ESP+0],$src1\n" + "\tFLD_S [ESP+0]\n" + "\tMOVSS [ESP+0],$src0\n" + "\tFLD_S [ESP+0]\n" + "loop:\tFPREM\n" + "\tFWAIT\n" + "\tFNSTSW AX\n" + "\tSAHF\n" + "\tJP loop\n" + "\tFSTP_S [ESP+0]\n" + "\tMOVSS $dst,[ESP+0]\n" + "\tADD ESP,4\n" + "\tFSTP ST0\t # Restore FPU Stack" + %} + ins_cost(250); + ins_encode( Push_ModX_encoding(src0, src1), emitModD(), Push_ResultX(dst,0x4), PopFPU); + ins_pipe( pipe_slow ); +%} + + +//----------Arithmetic Conversion Instructions--------------------------------- +// The conversions operations are all Alpha sorted. Please keep it that way! + +instruct roundFloat_mem_reg(stackSlotF dst, regF src) %{ + predicate(UseSSE==0); + match(Set dst (RoundFloat src)); + ins_cost(125); + format %{ "FST_S $dst,$src\t# F-round" %} + ins_encode( Pop_Mem_Reg_F(dst, src) ); + ins_pipe( fpu_mem_reg ); +%} + +instruct roundDouble_mem_reg(stackSlotD dst, regD src) %{ + predicate(UseSSE<=1); + match(Set dst (RoundDouble src)); + ins_cost(125); + format %{ "FST_D $dst,$src\t# D-round" %} + ins_encode( Pop_Mem_Reg_D(dst, src) ); + ins_pipe( fpu_mem_reg ); +%} + +// Force rounding to 24-bit precision and 6-bit exponent +instruct convD2F_reg(stackSlotF dst, regD src) %{ + predicate(UseSSE==0); + match(Set dst (ConvD2F src)); + format %{ "FST_S $dst,$src\t# F-round" %} + expand %{ + roundFloat_mem_reg(dst,src); + %} +%} + +// Force rounding to 24-bit precision and 6-bit exponent +instruct convD2X_reg(regX dst, regD src, eFlagsReg cr) %{ + predicate(UseSSE==1); + match(Set dst (ConvD2F src)); + effect( KILL cr ); + format %{ "SUB ESP,4\n\t" + "FST_S [ESP],$src\t# F-round\n\t" + "MOVSS $dst,[ESP]\n\t" + "ADD ESP,4" %} + ins_encode( D2X_encoding(dst, src) ); + ins_pipe( pipe_slow ); +%} + +// Force rounding double precision to single precision +instruct convXD2X_reg(regX dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (ConvD2F src)); + format %{ "CVTSD2SS $dst,$src\t# F-round" %} + opcode(0xF2, 0x0F, 0x5A); + ins_encode( OpcP, OpcS, Opcode(tertiary), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct convF2D_reg_reg(regD dst, regF src) %{ + predicate(UseSSE==0); + match(Set dst (ConvF2D src)); + format %{ "FST_S $dst,$src\t# D-round" %} + ins_encode( Pop_Reg_Reg_D(dst, src)); + ins_pipe( fpu_reg_reg ); +%} + +instruct convF2D_reg(stackSlotD dst, regF src) %{ + predicate(UseSSE==1); + match(Set dst (ConvF2D src)); + format %{ "FST_D $dst,$src\t# D-round" %} + expand %{ + roundDouble_mem_reg(dst,src); + %} +%} + +instruct convX2D_reg(regD dst, regX src, eFlagsReg cr) %{ + predicate(UseSSE==1); + match(Set dst (ConvF2D src)); + effect( KILL cr ); + format %{ "SUB ESP,4\n\t" + "MOVSS [ESP] $src\n\t" + "FLD_S [ESP]\n\t" + "ADD ESP,4\n\t" + "FSTP $dst\t# D-round" %} + ins_encode( X2D_encoding(dst, src), Pop_Reg_D(dst)); + ins_pipe( pipe_slow ); +%} + +instruct convX2XD_reg(regXD dst, regX src) %{ + predicate(UseSSE>=2); + match(Set dst (ConvF2D src)); + format %{ "CVTSS2SD $dst,$src\t# D-round" %} + opcode(0xF3, 0x0F, 0x5A); + ins_encode( OpcP, OpcS, Opcode(tertiary), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +// Convert a double to an int. If the double is a NAN, stuff a zero in instead. +instruct convD2I_reg_reg( eAXRegI dst, eDXRegI tmp, regD src, eFlagsReg cr ) %{ + predicate(UseSSE<=1); + match(Set dst (ConvD2I src)); + effect( KILL tmp, KILL cr ); + format %{ "FLD $src\t# Convert double to int \n\t" + "FLDCW trunc mode\n\t" + "SUB ESP,4\n\t" + "FISTp [ESP + #0]\n\t" + "FLDCW std/24-bit mode\n\t" + "POP EAX\n\t" + "CMP EAX,0x80000000\n\t" + "JNE,s fast\n\t" + "FLD_D $src\n\t" + "CALL d2i_wrapper\n" + "fast:" %} + ins_encode( Push_Reg_D(src), D2I_encoding(src) ); + ins_pipe( pipe_slow ); +%} + +// Convert a double to an int. If the double is a NAN, stuff a zero in instead. +instruct convXD2I_reg_reg( eAXRegI dst, eDXRegI tmp, regXD src, eFlagsReg cr ) %{ + predicate(UseSSE>=2); + match(Set dst (ConvD2I src)); + effect( KILL tmp, KILL cr ); + format %{ "CVTTSD2SI $dst, $src\n\t" + "CMP $dst,0x80000000\n\t" + "JNE,s fast\n\t" + "SUB ESP, 8\n\t" + "MOVSD [ESP], $src\n\t" + "FLD_D [ESP]\n\t" + "ADD ESP, 8\n\t" + "CALL d2i_wrapper\n" + "fast:" %} + opcode(0x1); // double-precision conversion + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x2C), FX2I_encoding(src,dst)); + ins_pipe( pipe_slow ); +%} + +instruct convD2L_reg_reg( eADXRegL dst, regD src, eFlagsReg cr ) %{ + predicate(UseSSE<=1); + match(Set dst (ConvD2L src)); + effect( KILL cr ); + format %{ "FLD $src\t# Convert double to long\n\t" + "FLDCW trunc mode\n\t" + "SUB ESP,8\n\t" + "FISTp [ESP + #0]\n\t" + "FLDCW std/24-bit mode\n\t" + "POP EAX\n\t" + "POP EDX\n\t" + "CMP EDX,0x80000000\n\t" + "JNE,s fast\n\t" + "TEST EAX,EAX\n\t" + "JNE,s fast\n\t" + "FLD $src\n\t" + "CALL d2l_wrapper\n" + "fast:" %} + ins_encode( Push_Reg_D(src), D2L_encoding(src) ); + ins_pipe( pipe_slow ); +%} + +// XMM lacks a float/double->long conversion, so use the old FPU stack. +instruct convXD2L_reg_reg( eADXRegL dst, regXD src, eFlagsReg cr ) %{ + predicate (UseSSE>=2); + match(Set dst (ConvD2L src)); + effect( KILL cr ); + format %{ "SUB ESP,8\t# Convert double to long\n\t" + "MOVSD [ESP],$src\n\t" + "FLD_D [ESP]\n\t" + "FLDCW trunc mode\n\t" + "FISTp [ESP + #0]\n\t" + "FLDCW std/24-bit mode\n\t" + "POP EAX\n\t" + "POP EDX\n\t" + "CMP EDX,0x80000000\n\t" + "JNE,s fast\n\t" + "TEST EAX,EAX\n\t" + "JNE,s fast\n\t" + "SUB ESP,8\n\t" + "MOVSD [ESP],$src\n\t" + "FLD_D [ESP]\n\t" + "CALL d2l_wrapper\n" + "fast:" %} + ins_encode( XD2L_encoding(src) ); + ins_pipe( pipe_slow ); +%} + +// Convert a double to an int. Java semantics require we do complex +// manglations in the corner cases. So we set the rounding mode to +// 'zero', store the darned double down as an int, and reset the +// rounding mode to 'nearest'. The hardware stores a flag value down +// if we would overflow or converted a NAN; we check for this and +// and go the slow path if needed. +instruct convF2I_reg_reg(eAXRegI dst, eDXRegI tmp, regF src, eFlagsReg cr ) %{ + predicate(UseSSE==0); + match(Set dst (ConvF2I src)); + effect( KILL tmp, KILL cr ); + format %{ "FLD $src\t# Convert float to int \n\t" + "FLDCW trunc mode\n\t" + "SUB ESP,4\n\t" + "FISTp [ESP + #0]\n\t" + "FLDCW std/24-bit mode\n\t" + "POP EAX\n\t" + "CMP EAX,0x80000000\n\t" + "JNE,s fast\n\t" + "FLD $src\n\t" + "CALL d2i_wrapper\n" + "fast:" %} + // D2I_encoding works for F2I + ins_encode( Push_Reg_F(src), D2I_encoding(src) ); + ins_pipe( pipe_slow ); +%} + +// Convert a float in xmm to an int reg. +instruct convX2I_reg(eAXRegI dst, eDXRegI tmp, regX src, eFlagsReg cr ) %{ + predicate(UseSSE>=1); + match(Set dst (ConvF2I src)); + effect( KILL tmp, KILL cr ); + format %{ "CVTTSS2SI $dst, $src\n\t" + "CMP $dst,0x80000000\n\t" + "JNE,s fast\n\t" + "SUB ESP, 4\n\t" + "MOVSS [ESP], $src\n\t" + "FLD [ESP]\n\t" + "ADD ESP, 4\n\t" + "CALL d2i_wrapper\n" + "fast:" %} + opcode(0x0); // single-precision conversion + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x2C), FX2I_encoding(src,dst)); + ins_pipe( pipe_slow ); +%} + +instruct convF2L_reg_reg( eADXRegL dst, regF src, eFlagsReg cr ) %{ + predicate(UseSSE==0); + match(Set dst (ConvF2L src)); + effect( KILL cr ); + format %{ "FLD $src\t# Convert float to long\n\t" + "FLDCW trunc mode\n\t" + "SUB ESP,8\n\t" + "FISTp [ESP + #0]\n\t" + "FLDCW std/24-bit mode\n\t" + "POP EAX\n\t" + "POP EDX\n\t" + "CMP EDX,0x80000000\n\t" + "JNE,s fast\n\t" + "TEST EAX,EAX\n\t" + "JNE,s fast\n\t" + "FLD $src\n\t" + "CALL d2l_wrapper\n" + "fast:" %} + // D2L_encoding works for F2L + ins_encode( Push_Reg_F(src), D2L_encoding(src) ); + ins_pipe( pipe_slow ); +%} + +// XMM lacks a float/double->long conversion, so use the old FPU stack. +instruct convX2L_reg_reg( eADXRegL dst, regX src, eFlagsReg cr ) %{ + predicate (UseSSE>=1); + match(Set dst (ConvF2L src)); + effect( KILL cr ); + format %{ "SUB ESP,8\t# Convert float to long\n\t" + "MOVSS [ESP],$src\n\t" + "FLD_S [ESP]\n\t" + "FLDCW trunc mode\n\t" + "FISTp [ESP + #0]\n\t" + "FLDCW std/24-bit mode\n\t" + "POP EAX\n\t" + "POP EDX\n\t" + "CMP EDX,0x80000000\n\t" + "JNE,s fast\n\t" + "TEST EAX,EAX\n\t" + "JNE,s fast\n\t" + "SUB ESP,4\t# Convert float to long\n\t" + "MOVSS [ESP],$src\n\t" + "FLD_S [ESP]\n\t" + "ADD ESP,4\n\t" + "CALL d2l_wrapper\n" + "fast:" %} + ins_encode( X2L_encoding(src) ); + ins_pipe( pipe_slow ); +%} + +instruct convI2D_reg(regD dst, stackSlotI src) %{ + predicate( UseSSE<=1 ); + match(Set dst (ConvI2D src)); + format %{ "FILD $src\n\t" + "FSTP $dst" %} + opcode(0xDB, 0x0); /* DB /0 */ + ins_encode(Push_Mem_I(src), Pop_Reg_D(dst)); + ins_pipe( fpu_reg_mem ); +%} + +instruct convI2XD_reg(regXD dst, eRegI src) %{ + predicate( UseSSE>=2 ); + match(Set dst (ConvI2D src)); + format %{ "CVTSI2SD $dst,$src" %} + opcode(0xF2, 0x0F, 0x2A); + ins_encode( OpcP, OpcS, Opcode(tertiary), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct convI2XD_mem(regXD dst, memory mem) %{ + predicate( UseSSE>=2 ); + match(Set dst (ConvI2D (LoadI mem))); + format %{ "CVTSI2SD $dst,$mem" %} + opcode(0xF2, 0x0F, 0x2A); + ins_encode( OpcP, OpcS, Opcode(tertiary), RegMem(dst, mem)); + ins_pipe( pipe_slow ); +%} + +instruct convI2D_mem(regD dst, memory mem) %{ + predicate( UseSSE<=1 && !Compile::current()->select_24_bit_instr()); + match(Set dst (ConvI2D (LoadI mem))); + format %{ "FILD $mem\n\t" + "FSTP $dst" %} + opcode(0xDB); /* DB /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), + Pop_Reg_D(dst)); + ins_pipe( fpu_reg_mem ); +%} + +// Convert a byte to a float; no rounding step needed. +instruct conv24I2F_reg(regF dst, stackSlotI src) %{ + predicate( UseSSE==0 && n->in(1)->Opcode() == Op_AndI && n->in(1)->in(2)->is_Con() && n->in(1)->in(2)->get_int() == 255 ); + match(Set dst (ConvI2F src)); + format %{ "FILD $src\n\t" + "FSTP $dst" %} + + opcode(0xDB, 0x0); /* DB /0 */ + ins_encode(Push_Mem_I(src), Pop_Reg_F(dst)); + ins_pipe( fpu_reg_mem ); +%} + +// In 24-bit mode, force exponent rounding by storing back out +instruct convI2F_SSF(stackSlotF dst, stackSlotI src) %{ + predicate( UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (ConvI2F src)); + ins_cost(200); + format %{ "FILD $src\n\t" + "FSTP_S $dst" %} + opcode(0xDB, 0x0); /* DB /0 */ + ins_encode( Push_Mem_I(src), + Pop_Mem_F(dst)); + ins_pipe( fpu_mem_mem ); +%} + +// In 24-bit mode, force exponent rounding by storing back out +instruct convI2F_SSF_mem(stackSlotF dst, memory mem) %{ + predicate( UseSSE==0 && Compile::current()->select_24_bit_instr()); + match(Set dst (ConvI2F (LoadI mem))); + ins_cost(200); + format %{ "FILD $mem\n\t" + "FSTP_S $dst" %} + opcode(0xDB); /* DB /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), + Pop_Mem_F(dst)); + ins_pipe( fpu_mem_mem ); +%} + +// This instruction does not round to 24-bits +instruct convI2F_reg(regF dst, stackSlotI src) %{ + predicate( UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (ConvI2F src)); + format %{ "FILD $src\n\t" + "FSTP $dst" %} + opcode(0xDB, 0x0); /* DB /0 */ + ins_encode( Push_Mem_I(src), + Pop_Reg_F(dst)); + ins_pipe( fpu_reg_mem ); +%} + +// This instruction does not round to 24-bits +instruct convI2F_mem(regF dst, memory mem) %{ + predicate( UseSSE==0 && !Compile::current()->select_24_bit_instr()); + match(Set dst (ConvI2F (LoadI mem))); + format %{ "FILD $mem\n\t" + "FSTP $dst" %} + opcode(0xDB); /* DB /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,mem), + Pop_Reg_F(dst)); + ins_pipe( fpu_reg_mem ); +%} + +// Convert an int to a float in xmm; no rounding step needed. +instruct convI2X_reg(regX dst, eRegI src) %{ + predicate(UseSSE>=1); + match(Set dst (ConvI2F src)); + format %{ "CVTSI2SS $dst, $src" %} + + opcode(0xF3, 0x0F, 0x2A); /* F3 0F 2A /r */ + ins_encode( OpcP, OpcS, Opcode(tertiary), RegReg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct convI2L_reg( eRegL dst, eRegI src, eFlagsReg cr) %{ + match(Set dst (ConvI2L src)); + effect(KILL cr); + format %{ "MOV $dst.lo,$src\n\t" + "MOV $dst.hi,$src\n\t" + "SAR $dst.hi,31" %} + ins_encode(convert_int_long(dst,src)); + ins_pipe( ialu_reg_reg_long ); +%} + +// Zero-extend convert int to long +instruct convI2L_reg_zex(eRegL dst, eRegI src, immL_32bits mask, eFlagsReg flags ) %{ + match(Set dst (AndL (ConvI2L src) mask) ); + effect( KILL flags ); + format %{ "MOV $dst.lo,$src\n\t" + "XOR $dst.hi,$dst.hi" %} + opcode(0x33); // XOR + ins_encode(enc_Copy(dst,src), OpcP, RegReg_Hi2(dst,dst) ); + ins_pipe( ialu_reg_reg_long ); +%} + +// Zero-extend long +instruct zerox_long(eRegL dst, eRegL src, immL_32bits mask, eFlagsReg flags ) %{ + match(Set dst (AndL src mask) ); + effect( KILL flags ); + format %{ "MOV $dst.lo,$src.lo\n\t" + "XOR $dst.hi,$dst.hi\n\t" %} + opcode(0x33); // XOR + ins_encode(enc_Copy(dst,src), OpcP, RegReg_Hi2(dst,dst) ); + ins_pipe( ialu_reg_reg_long ); +%} + +instruct convL2D_reg( stackSlotD dst, eRegL src, eFlagsReg cr) %{ + predicate (UseSSE<=1); + match(Set dst (ConvL2D src)); + effect( KILL cr ); + format %{ "PUSH $src.hi\t# Convert long to double\n\t" + "PUSH $src.lo\n\t" + "FILD ST,[ESP + #0]\n\t" + "ADD ESP,8\n\t" + "FSTP_D $dst\t# D-round" %} + opcode(0xDF, 0x5); /* DF /5 */ + ins_encode(convert_long_double(src), Pop_Mem_D(dst)); + ins_pipe( pipe_slow ); +%} + +instruct convL2XD_reg( regXD dst, eRegL src, eFlagsReg cr) %{ + predicate (UseSSE>=2); + match(Set dst (ConvL2D src)); + effect( KILL cr ); + format %{ "PUSH $src.hi\t# Convert long to double\n\t" + "PUSH $src.lo\n\t" + "FILD_D [ESP]\n\t" + "FSTP_D [ESP]\n\t" + "MOVSD $dst,[ESP]\n\t" + "ADD ESP,8" %} + opcode(0xDF, 0x5); /* DF /5 */ + ins_encode(convert_long_double2(src), Push_ResultXD(dst)); + ins_pipe( pipe_slow ); +%} + +instruct convL2X_reg( regX dst, eRegL src, eFlagsReg cr) %{ + predicate (UseSSE>=1); + match(Set dst (ConvL2F src)); + effect( KILL cr ); + format %{ "PUSH $src.hi\t# Convert long to single float\n\t" + "PUSH $src.lo\n\t" + "FILD_D [ESP]\n\t" + "FSTP_S [ESP]\n\t" + "MOVSS $dst,[ESP]\n\t" + "ADD ESP,8" %} + opcode(0xDF, 0x5); /* DF /5 */ + ins_encode(convert_long_double2(src), Push_ResultX(dst,0x8)); + ins_pipe( pipe_slow ); +%} + +instruct convL2F_reg( stackSlotF dst, eRegL src, eFlagsReg cr) %{ + match(Set dst (ConvL2F src)); + effect( KILL cr ); + format %{ "PUSH $src.hi\t# Convert long to single float\n\t" + "PUSH $src.lo\n\t" + "FILD ST,[ESP + #0]\n\t" + "ADD ESP,8\n\t" + "FSTP_S $dst\t# F-round" %} + opcode(0xDF, 0x5); /* DF /5 */ + ins_encode(convert_long_double(src), Pop_Mem_F(dst)); + ins_pipe( pipe_slow ); +%} + +instruct convL2I_reg( eRegI dst, eRegL src ) %{ + match(Set dst (ConvL2I src)); + effect( DEF dst, USE src ); + format %{ "MOV $dst,$src.lo" %} + ins_encode(enc_CopyL_Lo(dst,src)); + ins_pipe( ialu_reg_reg ); +%} + + +instruct MoveF2I_stack_reg(eRegI dst, stackSlotF src) %{ + match(Set dst (MoveF2I src)); + effect( DEF dst, USE src ); + ins_cost(100); + format %{ "MOV $dst,$src\t# MoveF2I_stack_reg" %} + opcode(0x8B); + ins_encode( OpcP, RegMem(dst,src)); + ins_pipe( ialu_reg_mem ); +%} + +instruct MoveF2I_reg_stack(stackSlotI dst, regF src) %{ + predicate(UseSSE==0); + match(Set dst (MoveF2I src)); + effect( DEF dst, USE src ); + + ins_cost(125); + format %{ "FST_S $dst,$src\t# MoveF2I_reg_stack" %} + ins_encode( Pop_Mem_Reg_F(dst, src) ); + ins_pipe( fpu_mem_reg ); +%} + +instruct MoveF2I_reg_stack_sse(stackSlotI dst, regX src) %{ + predicate(UseSSE>=1); + match(Set dst (MoveF2I src)); + effect( DEF dst, USE src ); + + ins_cost(95); + format %{ "MOVSS $dst,$src\t# MoveF2I_reg_stack_sse" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x11), RegMem(src, dst)); + ins_pipe( pipe_slow ); +%} + +instruct MoveF2I_reg_reg_sse(eRegI dst, regX src) %{ + predicate(UseSSE>=2); + match(Set dst (MoveF2I src)); + effect( DEF dst, USE src ); + ins_cost(85); + format %{ "MOVD $dst,$src\t# MoveF2I_reg_reg_sse" %} + ins_encode( MovX2I_reg(dst, src)); + ins_pipe( pipe_slow ); +%} + +instruct MoveI2F_reg_stack(stackSlotF dst, eRegI src) %{ + match(Set dst (MoveI2F src)); + effect( DEF dst, USE src ); + + ins_cost(100); + format %{ "MOV $dst,$src\t# MoveI2F_reg_stack" %} + opcode(0x89); + ins_encode( OpcPRegSS( dst, src ) ); + ins_pipe( ialu_mem_reg ); +%} + + +instruct MoveI2F_stack_reg(regF dst, stackSlotI src) %{ + predicate(UseSSE==0); + match(Set dst (MoveI2F src)); + effect(DEF dst, USE src); + + ins_cost(125); + format %{ "FLD_S $src\n\t" + "FSTP $dst\t# MoveI2F_stack_reg" %} + opcode(0xD9); /* D9 /0, FLD m32real */ + ins_encode( OpcP, RMopc_Mem_no_oop(0x00,src), + Pop_Reg_F(dst) ); + ins_pipe( fpu_reg_mem ); +%} + +instruct MoveI2F_stack_reg_sse(regX dst, stackSlotI src) %{ + predicate(UseSSE>=1); + match(Set dst (MoveI2F src)); + effect( DEF dst, USE src ); + + ins_cost(95); + format %{ "MOVSS $dst,$src\t# MoveI2F_stack_reg_sse" %} + ins_encode( Opcode(0xF3), Opcode(0x0F), Opcode(0x10), RegMem(dst,src)); + ins_pipe( pipe_slow ); +%} + +instruct MoveI2F_reg_reg_sse(regX dst, eRegI src) %{ + predicate(UseSSE>=2); + match(Set dst (MoveI2F src)); + effect( DEF dst, USE src ); + + ins_cost(85); + format %{ "MOVD $dst,$src\t# MoveI2F_reg_reg_sse" %} + ins_encode( MovI2X_reg(dst, src) ); + ins_pipe( pipe_slow ); +%} + +instruct MoveD2L_stack_reg(eRegL dst, stackSlotD src) %{ + match(Set dst (MoveD2L src)); + effect(DEF dst, USE src); + + ins_cost(250); + format %{ "MOV $dst.lo,$src\n\t" + "MOV $dst.hi,$src+4\t# MoveD2L_stack_reg" %} + opcode(0x8B, 0x8B); + ins_encode( OpcP, RegMem(dst,src), OpcS, RegMem_Hi(dst,src)); + ins_pipe( ialu_mem_long_reg ); +%} + +instruct MoveD2L_reg_stack(stackSlotL dst, regD src) %{ + predicate(UseSSE<=1); + match(Set dst (MoveD2L src)); + effect(DEF dst, USE src); + + ins_cost(125); + format %{ "FST_D $dst,$src\t# MoveD2L_reg_stack" %} + ins_encode( Pop_Mem_Reg_D(dst, src) ); + ins_pipe( fpu_mem_reg ); +%} + +instruct MoveD2L_reg_stack_sse(stackSlotL dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (MoveD2L src)); + effect(DEF dst, USE src); + ins_cost(95); + + format %{ "MOVSD $dst,$src\t# MoveD2L_reg_stack_sse" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x11), RegMem(src,dst)); + ins_pipe( pipe_slow ); +%} + +instruct MoveD2L_reg_reg_sse(eRegL dst, regXD src, regXD tmp) %{ + predicate(UseSSE>=2); + match(Set dst (MoveD2L src)); + effect(DEF dst, USE src, TEMP tmp); + ins_cost(85); + format %{ "MOVD $dst.lo,$src\n\t" + "PSHUFLW $tmp,$src,0x4E\n\t" + "MOVD $dst.hi,$tmp\t# MoveD2L_reg_reg_sse" %} + ins_encode( MovXD2L_reg(dst, src, tmp) ); + ins_pipe( pipe_slow ); +%} + +instruct MoveL2D_reg_stack(stackSlotD dst, eRegL src) %{ + match(Set dst (MoveL2D src)); + effect(DEF dst, USE src); + + ins_cost(200); + format %{ "MOV $dst,$src.lo\n\t" + "MOV $dst+4,$src.hi\t# MoveL2D_reg_stack" %} + opcode(0x89, 0x89); + ins_encode( OpcP, RegMem( src, dst ), OpcS, RegMem_Hi( src, dst ) ); + ins_pipe( ialu_mem_long_reg ); +%} + + +instruct MoveL2D_stack_reg(regD dst, stackSlotL src) %{ + predicate(UseSSE<=1); + match(Set dst (MoveL2D src)); + effect(DEF dst, USE src); + ins_cost(125); + + format %{ "FLD_D $src\n\t" + "FSTP $dst\t# MoveL2D_stack_reg" %} + opcode(0xDD); /* DD /0, FLD m64real */ + ins_encode( OpcP, RMopc_Mem_no_oop(0x00,src), + Pop_Reg_D(dst) ); + ins_pipe( fpu_reg_mem ); +%} + + +instruct MoveL2D_stack_reg_sse(regXD dst, stackSlotL src) %{ + predicate(UseSSE>=2 && UseXmmLoadAndClearUpper); + match(Set dst (MoveL2D src)); + effect(DEF dst, USE src); + + ins_cost(95); + format %{ "MOVSD $dst,$src\t# MoveL2D_stack_reg_sse" %} + ins_encode( Opcode(0xF2), Opcode(0x0F), Opcode(0x10), RegMem(dst,src)); + ins_pipe( pipe_slow ); +%} + +instruct MoveL2D_stack_reg_sse_partial(regXD dst, stackSlotL src) %{ + predicate(UseSSE>=2 && !UseXmmLoadAndClearUpper); + match(Set dst (MoveL2D src)); + effect(DEF dst, USE src); + + ins_cost(95); + format %{ "MOVLPD $dst,$src\t# MoveL2D_stack_reg_sse" %} + ins_encode( Opcode(0x66), Opcode(0x0F), Opcode(0x12), RegMem(dst,src)); + ins_pipe( pipe_slow ); +%} + +instruct MoveL2D_reg_reg_sse(regXD dst, eRegL src, regXD tmp) %{ + predicate(UseSSE>=2); + match(Set dst (MoveL2D src)); + effect(TEMP dst, USE src, TEMP tmp); + ins_cost(85); + format %{ "MOVD $dst,$src.lo\n\t" + "MOVD $tmp,$src.hi\n\t" + "PUNPCKLDQ $dst,$tmp\t# MoveL2D_reg_reg_sse" %} + ins_encode( MovL2XD_reg(dst, src, tmp) ); + ins_pipe( pipe_slow ); +%} + +// Replicate scalar to packed byte (1 byte) values in xmm +instruct Repl8B_reg(regXD dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate8B src)); + format %{ "MOVDQA $dst,$src\n\t" + "PUNPCKLBW $dst,$dst\n\t" + "PSHUFLW $dst,$dst,0x00\t! replicate8B" %} + ins_encode( pshufd_8x8(dst, src)); + ins_pipe( pipe_slow ); +%} + +// Replicate scalar to packed byte (1 byte) values in xmm +instruct Repl8B_eRegI(regXD dst, eRegI src) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate8B src)); + format %{ "MOVD $dst,$src\n\t" + "PUNPCKLBW $dst,$dst\n\t" + "PSHUFLW $dst,$dst,0x00\t! replicate8B" %} + ins_encode( mov_i2x(dst, src), pshufd_8x8(dst, dst)); + ins_pipe( pipe_slow ); +%} + +// Replicate scalar zero to packed byte (1 byte) values in xmm +instruct Repl8B_immI0(regXD dst, immI0 zero) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate8B zero)); + format %{ "PXOR $dst,$dst\t! replicate8B" %} + ins_encode( pxor(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed shore (2 byte) values in xmm +instruct Repl4S_reg(regXD dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate4S src)); + format %{ "PSHUFLW $dst,$src,0x00\t! replicate4S" %} + ins_encode( pshufd_4x16(dst, src)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed shore (2 byte) values in xmm +instruct Repl4S_eRegI(regXD dst, eRegI src) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate4S src)); + format %{ "MOVD $dst,$src\n\t" + "PSHUFLW $dst,$dst,0x00\t! replicate4S" %} + ins_encode( mov_i2x(dst, src), pshufd_4x16(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar zero to packed short (2 byte) values in xmm +instruct Repl4S_immI0(regXD dst, immI0 zero) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate4S zero)); + format %{ "PXOR $dst,$dst\t! replicate4S" %} + ins_encode( pxor(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed char (2 byte) values in xmm +instruct Repl4C_reg(regXD dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate4C src)); + format %{ "PSHUFLW $dst,$src,0x00\t! replicate4C" %} + ins_encode( pshufd_4x16(dst, src)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed char (2 byte) values in xmm +instruct Repl4C_eRegI(regXD dst, eRegI src) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate4C src)); + format %{ "MOVD $dst,$src\n\t" + "PSHUFLW $dst,$dst,0x00\t! replicate4C" %} + ins_encode( mov_i2x(dst, src), pshufd_4x16(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar zero to packed char (2 byte) values in xmm +instruct Repl4C_immI0(regXD dst, immI0 zero) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate4C zero)); + format %{ "PXOR $dst,$dst\t! replicate4C" %} + ins_encode( pxor(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed integer (4 byte) values in xmm +instruct Repl2I_reg(regXD dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate2I src)); + format %{ "PSHUFD $dst,$src,0x00\t! replicate2I" %} + ins_encode( pshufd(dst, src, 0x00)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed integer (4 byte) values in xmm +instruct Repl2I_eRegI(regXD dst, eRegI src) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate2I src)); + format %{ "MOVD $dst,$src\n\t" + "PSHUFD $dst,$dst,0x00\t! replicate2I" %} + ins_encode( mov_i2x(dst, src), pshufd(dst, dst, 0x00)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar zero to packed integer (2 byte) values in xmm +instruct Repl2I_immI0(regXD dst, immI0 zero) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate2I zero)); + format %{ "PXOR $dst,$dst\t! replicate2I" %} + ins_encode( pxor(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed single precision floating point values in xmm +instruct Repl2F_reg(regXD dst, regXD src) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate2F src)); + format %{ "PSHUFD $dst,$src,0xe0\t! replicate2F" %} + ins_encode( pshufd(dst, src, 0xe0)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed single precision floating point values in xmm +instruct Repl2F_regX(regXD dst, regX src) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate2F src)); + format %{ "PSHUFD $dst,$src,0xe0\t! replicate2F" %} + ins_encode( pshufd(dst, src, 0xe0)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed single precision floating point values in xmm +instruct Repl2F_immXF0(regXD dst, immXF0 zero) %{ + predicate(UseSSE>=2); + match(Set dst (Replicate2F zero)); + format %{ "PXOR $dst,$dst\t! replicate2F" %} + ins_encode( pxor(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + + + +// ======================================================================= +// fast clearing of an array + +instruct rep_stos(eCXRegI cnt, eDIRegP base, eAXRegI zero, Universe dummy, eFlagsReg cr) %{ + match(Set dummy (ClearArray cnt base)); + effect(USE_KILL cnt, USE_KILL base, KILL zero, KILL cr); + format %{ "SHL ECX,1\t# Convert doublewords to words\n\t" + "XOR EAX,EAX\n\t" + "REP STOS\t# store EAX into [EDI++] while ECX--" %} + opcode(0,0x4); + ins_encode( Opcode(0xD1), RegOpc(ECX), + OpcRegReg(0x33,EAX,EAX), + Opcode(0xF3), Opcode(0xAB) ); + ins_pipe( pipe_slow ); +%} + +instruct string_compare(eDIRegP str1, eSIRegP str2, eAXRegI tmp1, eBXRegI tmp2, eCXRegI result, eFlagsReg cr) %{ + match(Set result (StrComp str1 str2)); + effect(USE_KILL str1, USE_KILL str2, KILL tmp1, KILL tmp2, KILL cr); + //ins_cost(300); + + format %{ "String Compare $str1,$str2 -> $result // KILL EAX, EBX" %} + ins_encode( enc_String_Compare() ); + ins_pipe( pipe_slow ); +%} + +//----------Control Flow Instructions------------------------------------------ +// Signed compare Instructions +instruct compI_eReg(eFlagsReg cr, eRegI op1, eRegI op2) %{ + match(Set cr (CmpI op1 op2)); + effect( DEF cr, USE op1, USE op2 ); + format %{ "CMP $op1,$op2" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode( OpcP, RegReg( op1, op2) ); + ins_pipe( ialu_cr_reg_reg ); +%} + +instruct compI_eReg_imm(eFlagsReg cr, eRegI op1, immI op2) %{ + match(Set cr (CmpI op1 op2)); + effect( DEF cr, USE op1 ); + format %{ "CMP $op1,$op2" %} + opcode(0x81,0x07); /* Opcode 81 /7 */ + // ins_encode( RegImm( op1, op2) ); /* Was CmpImm */ + ins_encode( OpcSErm( op1, op2 ), Con8or32( op2 ) ); + ins_pipe( ialu_cr_reg_imm ); +%} + +// Cisc-spilled version of cmpI_eReg +instruct compI_eReg_mem(eFlagsReg cr, eRegI op1, memory op2) %{ + match(Set cr (CmpI op1 (LoadI op2))); + + format %{ "CMP $op1,$op2" %} + ins_cost(500); + opcode(0x3B); /* Opcode 3B /r */ + ins_encode( OpcP, RegMem( op1, op2) ); + ins_pipe( ialu_cr_reg_mem ); +%} + +instruct testI_reg( eFlagsReg cr, eRegI src, immI0 zero ) %{ + match(Set cr (CmpI src zero)); + effect( DEF cr, USE src ); + + format %{ "TEST $src,$src" %} + opcode(0x85); + ins_encode( OpcP, RegReg( src, src ) ); + ins_pipe( ialu_cr_reg_imm ); +%} + +instruct testI_reg_imm( eFlagsReg cr, eRegI src, immI con, immI0 zero ) %{ + match(Set cr (CmpI (AndI src con) zero)); + + format %{ "TEST $src,$con" %} + opcode(0xF7,0x00); + ins_encode( OpcP, RegOpc(src), Con32(con) ); + ins_pipe( ialu_cr_reg_imm ); +%} + +instruct testI_reg_mem( eFlagsReg cr, eRegI src, memory mem, immI0 zero ) %{ + match(Set cr (CmpI (AndI src mem) zero)); + + format %{ "TEST $src,$mem" %} + opcode(0x85); + ins_encode( OpcP, RegMem( src, mem ) ); + ins_pipe( ialu_cr_reg_mem ); +%} + +// Unsigned compare Instructions; really, same as signed except they +// produce an eFlagsRegU instead of eFlagsReg. +instruct compU_eReg(eFlagsRegU cr, eRegI op1, eRegI op2) %{ + match(Set cr (CmpU op1 op2)); + + format %{ "CMPu $op1,$op2" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode( OpcP, RegReg( op1, op2) ); + ins_pipe( ialu_cr_reg_reg ); +%} + +instruct compU_eReg_imm(eFlagsRegU cr, eRegI op1, immI op2) %{ + match(Set cr (CmpU op1 op2)); + + format %{ "CMPu $op1,$op2" %} + opcode(0x81,0x07); /* Opcode 81 /7 */ + ins_encode( OpcSErm( op1, op2 ), Con8or32( op2 ) ); + ins_pipe( ialu_cr_reg_imm ); +%} + +// // Cisc-spilled version of cmpU_eReg +instruct compU_eReg_mem(eFlagsRegU cr, eRegI op1, memory op2) %{ + match(Set cr (CmpU op1 (LoadI op2))); + + format %{ "CMPu $op1,$op2" %} + ins_cost(500); + opcode(0x3B); /* Opcode 3B /r */ + ins_encode( OpcP, RegMem( op1, op2) ); + ins_pipe( ialu_cr_reg_mem ); +%} + +// // Cisc-spilled version of cmpU_eReg +//instruct compU_mem_eReg(eFlagsRegU cr, memory op1, eRegI op2) %{ +// match(Set cr (CmpU (LoadI op1) op2)); +// +// format %{ "CMPu $op1,$op2" %} +// ins_cost(500); +// opcode(0x39); /* Opcode 39 /r */ +// ins_encode( OpcP, RegMem( op1, op2) ); +//%} + +instruct testU_reg( eFlagsRegU cr, eRegI src, immI0 zero ) %{ + match(Set cr (CmpU src zero)); + + format %{ "TESTu $src,$src" %} + opcode(0x85); + ins_encode( OpcP, RegReg( src, src ) ); + ins_pipe( ialu_cr_reg_imm ); +%} + +// Unsigned pointer compare Instructions +instruct compP_eReg(eFlagsRegU cr, eRegP op1, eRegP op2) %{ + match(Set cr (CmpP op1 op2)); + + format %{ "CMPu $op1,$op2" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode( OpcP, RegReg( op1, op2) ); + ins_pipe( ialu_cr_reg_reg ); +%} + +instruct compP_eReg_imm(eFlagsRegU cr, eRegP op1, immP op2) %{ + match(Set cr (CmpP op1 op2)); + + format %{ "CMPu $op1,$op2" %} + opcode(0x81,0x07); /* Opcode 81 /7 */ + ins_encode( OpcSErm( op1, op2 ), Con8or32( op2 ) ); + ins_pipe( ialu_cr_reg_imm ); +%} + +// // Cisc-spilled version of cmpP_eReg +instruct compP_eReg_mem(eFlagsRegU cr, eRegP op1, memory op2) %{ + match(Set cr (CmpP op1 (LoadP op2))); + + format %{ "CMPu $op1,$op2" %} + ins_cost(500); + opcode(0x3B); /* Opcode 3B /r */ + ins_encode( OpcP, RegMem( op1, op2) ); + ins_pipe( ialu_cr_reg_mem ); +%} + +// // Cisc-spilled version of cmpP_eReg +//instruct compP_mem_eReg(eFlagsRegU cr, memory op1, eRegP op2) %{ +// match(Set cr (CmpP (LoadP op1) op2)); +// +// format %{ "CMPu $op1,$op2" %} +// ins_cost(500); +// opcode(0x39); /* Opcode 39 /r */ +// ins_encode( OpcP, RegMem( op1, op2) ); +//%} + +// Compare raw pointer (used in out-of-heap check). +// Only works because non-oop pointers must be raw pointers +// and raw pointers have no anti-dependencies. +instruct compP_mem_eReg( eFlagsRegU cr, eRegP op1, memory op2 ) %{ + predicate( !n->in(2)->in(2)->bottom_type()->isa_oop_ptr() ); + match(Set cr (CmpP op1 (LoadP op2))); + + format %{ "CMPu $op1,$op2" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode( OpcP, RegMem( op1, op2) ); + ins_pipe( ialu_cr_reg_mem ); +%} + +// +// This will generate a signed flags result. This should be ok +// since any compare to a zero should be eq/neq. +instruct testP_reg( eFlagsReg cr, eRegP src, immP0 zero ) %{ + match(Set cr (CmpP src zero)); + + format %{ "TEST $src,$src" %} + opcode(0x85); + ins_encode( OpcP, RegReg( src, src ) ); + ins_pipe( ialu_cr_reg_imm ); +%} + +// Cisc-spilled version of testP_reg +// This will generate a signed flags result. This should be ok +// since any compare to a zero should be eq/neq. +instruct testP_Reg_mem( eFlagsReg cr, memory op, immI0 zero ) %{ + match(Set cr (CmpP (LoadP op) zero)); + + format %{ "TEST $op,0xFFFFFFFF" %} + ins_cost(500); + opcode(0xF7); /* Opcode F7 /0 */ + ins_encode( OpcP, RMopc_Mem(0x00,op), Con_d32(0xFFFFFFFF) ); + ins_pipe( ialu_cr_reg_imm ); +%} + +// Yanked all unsigned pointer compare operations. +// Pointer compares are done with CmpP which is already unsigned. + +//----------Max and Min-------------------------------------------------------- +// Min Instructions +//// +// *** Min and Max using the conditional move are slower than the +// *** branch version on a Pentium III. +// // Conditional move for min +//instruct cmovI_reg_lt( eRegI op2, eRegI op1, eFlagsReg cr ) %{ +// effect( USE_DEF op2, USE op1, USE cr ); +// format %{ "CMOVlt $op2,$op1\t! min" %} +// opcode(0x4C,0x0F); +// ins_encode( OpcS, OpcP, RegReg( op2, op1 ) ); +// ins_pipe( pipe_cmov_reg ); +//%} +// +//// Min Register with Register (P6 version) +//instruct minI_eReg_p6( eRegI op1, eRegI op2 ) %{ +// predicate(VM_Version::supports_cmov() ); +// match(Set op2 (MinI op1 op2)); +// ins_cost(200); +// expand %{ +// eFlagsReg cr; +// compI_eReg(cr,op1,op2); +// cmovI_reg_lt(op2,op1,cr); +// %} +//%} + +// Min Register with Register (generic version) +instruct minI_eReg(eRegI dst, eRegI src, eFlagsReg flags) %{ + match(Set dst (MinI dst src)); + effect(KILL flags); + ins_cost(300); + + format %{ "MIN $dst,$src" %} + opcode(0xCC); + ins_encode( min_enc(dst,src) ); + ins_pipe( pipe_slow ); +%} + +// Max Register with Register +// *** Min and Max using the conditional move are slower than the +// *** branch version on a Pentium III. +// // Conditional move for max +//instruct cmovI_reg_gt( eRegI op2, eRegI op1, eFlagsReg cr ) %{ +// effect( USE_DEF op2, USE op1, USE cr ); +// format %{ "CMOVgt $op2,$op1\t! max" %} +// opcode(0x4F,0x0F); +// ins_encode( OpcS, OpcP, RegReg( op2, op1 ) ); +// ins_pipe( pipe_cmov_reg ); +//%} +// +// // Max Register with Register (P6 version) +//instruct maxI_eReg_p6( eRegI op1, eRegI op2 ) %{ +// predicate(VM_Version::supports_cmov() ); +// match(Set op2 (MaxI op1 op2)); +// ins_cost(200); +// expand %{ +// eFlagsReg cr; +// compI_eReg(cr,op1,op2); +// cmovI_reg_gt(op2,op1,cr); +// %} +//%} + +// Max Register with Register (generic version) +instruct maxI_eReg(eRegI dst, eRegI src, eFlagsReg flags) %{ + match(Set dst (MaxI dst src)); + effect(KILL flags); + ins_cost(300); + + format %{ "MAX $dst,$src" %} + opcode(0xCC); + ins_encode( max_enc(dst,src) ); + ins_pipe( pipe_slow ); +%} + +// ============================================================================ +// Branch Instructions +// Jump Table +instruct jumpXtnd(eRegI switch_val) %{ + match(Jump switch_val); + ins_cost(350); + + format %{ "JMP [table_base](,$switch_val,1)\n\t" %} + + ins_encode %{ + address table_base = __ address_table_constant(_index2label); + + // Jump to Address(table_base + switch_reg) + InternalAddress table(table_base); + Address index(noreg, $switch_val$$Register, Address::times_1); + __ jump(ArrayAddress(table, index)); + %} + ins_pc_relative(1); + ins_pipe(pipe_jmp); +%} + +// Jump Direct - Label defines a relative address from JMP+1 +instruct jmpDir(label labl) %{ + match(Goto); + effect(USE labl); + + ins_cost(300); + format %{ "JMP $labl" %} + size(5); + opcode(0xE9); + ins_encode( OpcP, Lbl( labl ) ); + ins_pipe( pipe_jmp ); + ins_pc_relative(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpCon(cmpOp cop, eFlagsReg cr, label labl) %{ + match(If cop cr); + effect(USE labl); + + ins_cost(300); + format %{ "J$cop $labl" %} + size(6); + opcode(0x0F, 0x80); + ins_encode( Jcc( cop, labl) ); + ins_pipe( pipe_jcc ); + ins_pc_relative(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpLoopEnd(cmpOp cop, eFlagsReg cr, label labl) %{ + match(CountedLoopEnd cop cr); + effect(USE labl); + + ins_cost(300); + format %{ "J$cop $labl\t# Loop end" %} + size(6); + opcode(0x0F, 0x80); + ins_encode( Jcc( cop, labl) ); + ins_pipe( pipe_jcc ); + ins_pc_relative(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpLoopEndU(cmpOpU cop, eFlagsRegU cmp, label labl) %{ + match(CountedLoopEnd cop cmp); + effect(USE labl); + + ins_cost(300); + format %{ "J$cop,u $labl\t# Loop end" %} + size(6); + opcode(0x0F, 0x80); + ins_encode( Jcc( cop, labl) ); + ins_pipe( pipe_jcc ); + ins_pc_relative(1); +%} + +// Jump Direct Conditional - using unsigned comparison +instruct jmpConU(cmpOpU cop, eFlagsRegU cmp, label labl) %{ + match(If cop cmp); + effect(USE labl); + + ins_cost(300); + format %{ "J$cop,u $labl" %} + size(6); + opcode(0x0F, 0x80); + ins_encode( Jcc( cop, labl) ); + ins_pipe( pipe_jcc ); + ins_pc_relative(1); +%} + +// ============================================================================ +// The 2nd slow-half of a subtype check. Scan the subklass's 2ndary superklass +// array for an instance of the superklass. Set a hidden internal cache on a +// hit (cache is checked with exposed code in gen_subtype_check()). Return +// NZ for a miss or zero for a hit. The encoding ALSO sets flags. +instruct partialSubtypeCheck( eDIRegP result, eSIRegP sub, eAXRegP super, eCXRegI rcx, eFlagsReg cr ) %{ + match(Set result (PartialSubtypeCheck sub super)); + effect( KILL rcx, KILL cr ); + + ins_cost(1100); // slightly larger than the next version + format %{ "CMPL EAX,ESI\n\t" + "JEQ,s hit\n\t" + "MOV EDI,[$sub+Klass::secondary_supers]\n\t" + "MOV ECX,[EDI+arrayKlass::length]\t# length to scan\n\t" + "ADD EDI,arrayKlass::base_offset\t# Skip to start of data; set NZ in case count is zero\n\t" + "REPNE SCASD\t# Scan *EDI++ for a match with EAX while CX-- != 0\n\t" + "JNE,s miss\t\t# Missed: EDI not-zero\n\t" + "MOV [$sub+Klass::secondary_super_cache],$super\t# Hit: update cache\n\t" + "hit:\n\t" + "XOR $result,$result\t\t Hit: EDI zero\n\t" + "miss:\t" %} + + opcode(0x1); // Force a XOR of EDI + ins_encode( enc_PartialSubtypeCheck() ); + ins_pipe( pipe_slow ); +%} + +instruct partialSubtypeCheck_vs_Zero( eFlagsReg cr, eSIRegP sub, eAXRegP super, eCXRegI rcx, eDIRegP result, immP0 zero ) %{ + match(Set cr (CmpP (PartialSubtypeCheck sub super) zero)); + effect( KILL rcx, KILL result ); + + ins_cost(1000); + format %{ "CMPL EAX,ESI\n\t" + "JEQ,s miss\t# Actually a hit; we are done.\n\t" + "MOV EDI,[$sub+Klass::secondary_supers]\n\t" + "MOV ECX,[EDI+arrayKlass::length]\t# length to scan\n\t" + "ADD EDI,arrayKlass::base_offset\t# Skip to start of data; set NZ in case count is zero\n\t" + "REPNE SCASD\t# Scan *EDI++ for a match with EAX while CX-- != 0\n\t" + "JNE,s miss\t\t# Missed: flags NZ\n\t" + "MOV [$sub+Klass::secondary_super_cache],$super\t# Hit: update cache, flags Z\n\t" + "miss:\t" %} + + opcode(0x0); // No need to XOR EDI + ins_encode( enc_PartialSubtypeCheck() ); + ins_pipe( pipe_slow ); +%} + +// ============================================================================ +// Branch Instructions -- short offset versions +// +// These instructions are used to replace jumps of a long offset (the default +// match) with jumps of a shorter offset. These instructions are all tagged +// with the ins_short_branch attribute, which causes the ADLC to suppress the +// match rules in general matching. Instead, the ADLC generates a conversion +// method in the MachNode which can be used to do in-place replacement of the +// long variant with the shorter variant. The compiler will determine if a +// branch can be taken by the is_short_branch_offset() predicate in the machine +// specific code section of the file. + +// Jump Direct - Label defines a relative address from JMP+1 +instruct jmpDir_short(label labl) %{ + match(Goto); + effect(USE labl); + + ins_cost(300); + format %{ "JMP,s $labl" %} + size(2); + opcode(0xEB); + ins_encode( OpcP, LblShort( labl ) ); + ins_pipe( pipe_jmp ); + ins_pc_relative(1); + ins_short_branch(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpCon_short(cmpOp cop, eFlagsReg cr, label labl) %{ + match(If cop cr); + effect(USE labl); + + ins_cost(300); + format %{ "J$cop,s $labl" %} + size(2); + opcode(0x70); + ins_encode( JccShort( cop, labl) ); + ins_pipe( pipe_jcc ); + ins_pc_relative(1); + ins_short_branch(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpLoopEnd_short(cmpOp cop, eFlagsReg cr, label labl) %{ + match(CountedLoopEnd cop cr); + effect(USE labl); + + ins_cost(300); + format %{ "J$cop,s $labl" %} + size(2); + opcode(0x70); + ins_encode( JccShort( cop, labl) ); + ins_pipe( pipe_jcc ); + ins_pc_relative(1); + ins_short_branch(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpLoopEndU_short(cmpOpU cop, eFlagsRegU cmp, label labl) %{ + match(CountedLoopEnd cop cmp); + effect(USE labl); + + ins_cost(300); + format %{ "J$cop,us $labl" %} + size(2); + opcode(0x70); + ins_encode( JccShort( cop, labl) ); + ins_pipe( pipe_jcc ); + ins_pc_relative(1); + ins_short_branch(1); +%} + +// Jump Direct Conditional - using unsigned comparison +instruct jmpConU_short(cmpOpU cop, eFlagsRegU cmp, label labl) %{ + match(If cop cmp); + effect(USE labl); + + ins_cost(300); + format %{ "J$cop,us $labl" %} + size(2); + opcode(0x70); + ins_encode( JccShort( cop, labl) ); + ins_pipe( pipe_jcc ); + ins_pc_relative(1); + ins_short_branch(1); +%} + +// ============================================================================ +// Long Compare +// +// Currently we hold longs in 2 registers. Comparing such values efficiently +// is tricky. The flavor of compare used depends on whether we are testing +// for LT, LE, or EQ. For a simple LT test we can check just the sign bit. +// The GE test is the negated LT test. The LE test can be had by commuting +// the operands (yielding a GE test) and then negating; negate again for the +// GT test. The EQ test is done by ORcc'ing the high and low halves, and the +// NE test is negated from that. + +// Due to a shortcoming in the ADLC, it mixes up expressions like: +// (foo (CmpI (CmpL X Y) 0)) and (bar (CmpI (CmpL X 0L) 0)). Note the +// difference between 'Y' and '0L'. The tree-matches for the CmpI sections +// are collapsed internally in the ADLC's dfa-gen code. The match for +// (CmpI (CmpL X Y) 0) is silently replaced with (CmpI (CmpL X 0L) 0) and the +// foo match ends up with the wrong leaf. One fix is to not match both +// reg-reg and reg-zero forms of long-compare. This is unfortunate because +// both forms beat the trinary form of long-compare and both are very useful +// on Intel which has so few registers. + +// Manifest a CmpL result in an integer register. Very painful. +// This is the test to avoid. +instruct cmpL3_reg_reg(eSIRegI dst, eRegL src1, eRegL src2, eFlagsReg flags ) %{ + match(Set dst (CmpL3 src1 src2)); + effect( KILL flags ); + ins_cost(1000); + format %{ "XOR $dst,$dst\n\t" + "CMP $src1.hi,$src2.hi\n\t" + "JLT,s m_one\n\t" + "JGT,s p_one\n\t" + "CMP $src1.lo,$src2.lo\n\t" + "JB,s m_one\n\t" + "JEQ,s done\n" + "p_one:\tINC $dst\n\t" + "JMP,s done\n" + "m_one:\tDEC $dst\n" + "done:" %} + ins_encode %{ + Label p_one, m_one, done; + __ xorl($dst$$Register, $dst$$Register); + __ cmpl(HIGH_FROM_LOW($src1$$Register), HIGH_FROM_LOW($src2$$Register)); + __ jccb(Assembler::less, m_one); + __ jccb(Assembler::greater, p_one); + __ cmpl($src1$$Register, $src2$$Register); + __ jccb(Assembler::below, m_one); + __ jccb(Assembler::equal, done); + __ bind(p_one); + __ increment($dst$$Register); + __ jmpb(done); + __ bind(m_one); + __ decrement($dst$$Register); + __ bind(done); + %} + ins_pipe( pipe_slow ); +%} + +//====== +// Manifest a CmpL result in the normal flags. Only good for LT or GE +// compares. Can be used for LE or GT compares by reversing arguments. +// NOT GOOD FOR EQ/NE tests. +instruct cmpL_zero_flags_LTGE( flagsReg_long_LTGE flags, eRegL src, immL0 zero ) %{ + match( Set flags (CmpL src zero )); + ins_cost(100); + format %{ "TEST $src.hi,$src.hi" %} + opcode(0x85); + ins_encode( OpcP, RegReg_Hi2( src, src ) ); + ins_pipe( ialu_cr_reg_reg ); +%} + +// Manifest a CmpL result in the normal flags. Only good for LT or GE +// compares. Can be used for LE or GT compares by reversing arguments. +// NOT GOOD FOR EQ/NE tests. +instruct cmpL_reg_flags_LTGE( flagsReg_long_LTGE flags, eRegL src1, eRegL src2, eRegI tmp ) %{ + match( Set flags (CmpL src1 src2 )); + effect( TEMP tmp ); + ins_cost(300); + format %{ "CMP $src1.lo,$src2.lo\t! Long compare; set flags for low bits\n\t" + "MOV $tmp,$src1.hi\n\t" + "SBB $tmp,$src2.hi\t! Compute flags for long compare" %} + ins_encode( long_cmp_flags2( src1, src2, tmp ) ); + ins_pipe( ialu_cr_reg_reg ); +%} + +// Long compares reg < zero/req OR reg >= zero/req. +// Just a wrapper for a normal branch, plus the predicate test. +instruct cmpL_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, label labl) %{ + match(If cmp flags); + effect(USE labl); + predicate( _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge ); + expand %{ + jmpCon(cmp,flags,labl); // JLT or JGE... + %} +%} + +// Compare 2 longs and CMOVE longs. +instruct cmovLL_reg_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, eRegL dst, eRegL src) %{ + match(Set dst (CMoveL (Binary cmp flags) (Binary dst src))); + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge )); + ins_cost(400); + format %{ "CMOV$cmp $dst.lo,$src.lo\n\t" + "CMOV$cmp $dst.hi,$src.hi" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegReg_Lo2( dst, src ), enc_cmov(cmp), RegReg_Hi2( dst, src ) ); + ins_pipe( pipe_cmov_reg_long ); +%} + +instruct cmovLL_mem_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, eRegL dst, load_long_memory src) %{ + match(Set dst (CMoveL (Binary cmp flags) (Binary dst (LoadL src)))); + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge )); + ins_cost(500); + format %{ "CMOV$cmp $dst.lo,$src.lo\n\t" + "CMOV$cmp $dst.hi,$src.hi" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegMem(dst, src), enc_cmov(cmp), RegMem_Hi(dst, src) ); + ins_pipe( pipe_cmov_reg_long ); +%} + +// Compare 2 longs and CMOVE ints. +instruct cmovII_reg_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, eRegI dst, eRegI src) %{ + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge )); + match(Set dst (CMoveI (Binary cmp flags) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cmp $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegReg( dst, src ) ); + ins_pipe( pipe_cmov_reg ); +%} + +instruct cmovII_mem_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, eRegI dst, memory src) %{ + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge )); + match(Set dst (CMoveI (Binary cmp flags) (Binary dst (LoadI src)))); + ins_cost(250); + format %{ "CMOV$cmp $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegMem( dst, src ) ); + ins_pipe( pipe_cmov_mem ); +%} + +// Compare 2 longs and CMOVE ints. +instruct cmovPP_reg_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, eRegP dst, eRegP src) %{ + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge )); + match(Set dst (CMoveP (Binary cmp flags) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cmp $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegReg( dst, src ) ); + ins_pipe( pipe_cmov_reg ); +%} + +// Compare 2 longs and CMOVE doubles +instruct cmovDD_reg_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, regD dst, regD src) %{ + predicate( UseSSE<=1 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge ); + match(Set dst (CMoveD (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovD_regS(cmp,flags,dst,src); + %} +%} + +// Compare 2 longs and CMOVE doubles +instruct cmovXDD_reg_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, regXD dst, regXD src) %{ + predicate( UseSSE>=2 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge ); + match(Set dst (CMoveD (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovXD_regS(cmp,flags,dst,src); + %} +%} + +instruct cmovFF_reg_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, regF dst, regF src) %{ + predicate( UseSSE==0 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge ); + match(Set dst (CMoveF (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovF_regS(cmp,flags,dst,src); + %} +%} + +instruct cmovXX_reg_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, regX dst, regX src) %{ + predicate( UseSSE>=1 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge ); + match(Set dst (CMoveF (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovX_regS(cmp,flags,dst,src); + %} +%} + +//====== +// Manifest a CmpL result in the normal flags. Only good for EQ/NE compares. +instruct cmpL_zero_flags_EQNE( flagsReg_long_EQNE flags, eRegL src, immL0 zero, eRegI tmp ) %{ + match( Set flags (CmpL src zero )); + effect(TEMP tmp); + ins_cost(200); + format %{ "MOV $tmp,$src.lo\n\t" + "OR $tmp,$src.hi\t! Long is EQ/NE 0?" %} + ins_encode( long_cmp_flags0( src, tmp ) ); + ins_pipe( ialu_reg_reg_long ); +%} + +// Manifest a CmpL result in the normal flags. Only good for EQ/NE compares. +instruct cmpL_reg_flags_EQNE( flagsReg_long_EQNE flags, eRegL src1, eRegL src2 ) %{ + match( Set flags (CmpL src1 src2 )); + ins_cost(200+300); + format %{ "CMP $src1.lo,$src2.lo\t! Long compare; set flags for low bits\n\t" + "JNE,s skip\n\t" + "CMP $src1.hi,$src2.hi\n\t" + "skip:\t" %} + ins_encode( long_cmp_flags1( src1, src2 ) ); + ins_pipe( ialu_cr_reg_reg ); +%} + +// Long compare reg == zero/reg OR reg != zero/reg +// Just a wrapper for a normal branch, plus the predicate test. +instruct cmpL_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, label labl) %{ + match(If cmp flags); + effect(USE labl); + predicate( _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne ); + expand %{ + jmpCon(cmp,flags,labl); // JEQ or JNE... + %} +%} + +// Compare 2 longs and CMOVE longs. +instruct cmovLL_reg_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, eRegL dst, eRegL src) %{ + match(Set dst (CMoveL (Binary cmp flags) (Binary dst src))); + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne )); + ins_cost(400); + format %{ "CMOV$cmp $dst.lo,$src.lo\n\t" + "CMOV$cmp $dst.hi,$src.hi" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegReg_Lo2( dst, src ), enc_cmov(cmp), RegReg_Hi2( dst, src ) ); + ins_pipe( pipe_cmov_reg_long ); +%} + +instruct cmovLL_mem_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, eRegL dst, load_long_memory src) %{ + match(Set dst (CMoveL (Binary cmp flags) (Binary dst (LoadL src)))); + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne )); + ins_cost(500); + format %{ "CMOV$cmp $dst.lo,$src.lo\n\t" + "CMOV$cmp $dst.hi,$src.hi" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegMem(dst, src), enc_cmov(cmp), RegMem_Hi(dst, src) ); + ins_pipe( pipe_cmov_reg_long ); +%} + +// Compare 2 longs and CMOVE ints. +instruct cmovII_reg_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, eRegI dst, eRegI src) %{ + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne )); + match(Set dst (CMoveI (Binary cmp flags) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cmp $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegReg( dst, src ) ); + ins_pipe( pipe_cmov_reg ); +%} + +instruct cmovII_mem_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, eRegI dst, memory src) %{ + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne )); + match(Set dst (CMoveI (Binary cmp flags) (Binary dst (LoadI src)))); + ins_cost(250); + format %{ "CMOV$cmp $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegMem( dst, src ) ); + ins_pipe( pipe_cmov_mem ); +%} + +// Compare 2 longs and CMOVE ints. +instruct cmovPP_reg_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, eRegP dst, eRegP src) %{ + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne )); + match(Set dst (CMoveP (Binary cmp flags) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cmp $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegReg( dst, src ) ); + ins_pipe( pipe_cmov_reg ); +%} + +// Compare 2 longs and CMOVE doubles +instruct cmovDD_reg_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, regD dst, regD src) %{ + predicate( UseSSE<=1 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne ); + match(Set dst (CMoveD (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovD_regS(cmp,flags,dst,src); + %} +%} + +// Compare 2 longs and CMOVE doubles +instruct cmovXDD_reg_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, regXD dst, regXD src) %{ + predicate( UseSSE>=2 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne ); + match(Set dst (CMoveD (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovXD_regS(cmp,flags,dst,src); + %} +%} + +instruct cmovFF_reg_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, regF dst, regF src) %{ + predicate( UseSSE==0 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne ); + match(Set dst (CMoveF (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovF_regS(cmp,flags,dst,src); + %} +%} + +instruct cmovXX_reg_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, regX dst, regX src) %{ + predicate( UseSSE>=1 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne ); + match(Set dst (CMoveF (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovX_regS(cmp,flags,dst,src); + %} +%} + +//====== +// Manifest a CmpL result in the normal flags. Only good for LE or GT compares. +// Same as cmpL_reg_flags_LEGT except must negate src +instruct cmpL_zero_flags_LEGT( flagsReg_long_LEGT flags, eRegL src, immL0 zero, eRegI tmp ) %{ + match( Set flags (CmpL src zero )); + effect( TEMP tmp ); + ins_cost(300); + format %{ "XOR $tmp,$tmp\t# Long compare for -$src < 0, use commuted test\n\t" + "CMP $tmp,$src.lo\n\t" + "SBB $tmp,$src.hi\n\t" %} + ins_encode( long_cmp_flags3(src, tmp) ); + ins_pipe( ialu_reg_reg_long ); +%} + +// Manifest a CmpL result in the normal flags. Only good for LE or GT compares. +// Same as cmpL_reg_flags_LTGE except operands swapped. Swapping operands +// requires a commuted test to get the same result. +instruct cmpL_reg_flags_LEGT( flagsReg_long_LEGT flags, eRegL src1, eRegL src2, eRegI tmp ) %{ + match( Set flags (CmpL src1 src2 )); + effect( TEMP tmp ); + ins_cost(300); + format %{ "CMP $src2.lo,$src1.lo\t! Long compare, swapped operands, use with commuted test\n\t" + "MOV $tmp,$src2.hi\n\t" + "SBB $tmp,$src1.hi\t! Compute flags for long compare" %} + ins_encode( long_cmp_flags2( src2, src1, tmp ) ); + ins_pipe( ialu_cr_reg_reg ); +%} + +// Long compares reg < zero/req OR reg >= zero/req. +// Just a wrapper for a normal branch, plus the predicate test +instruct cmpL_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, label labl) %{ + match(If cmp flags); + effect(USE labl); + predicate( _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt || _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le ); + ins_cost(300); + expand %{ + jmpCon(cmp,flags,labl); // JGT or JLE... + %} +%} + +// Compare 2 longs and CMOVE longs. +instruct cmovLL_reg_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, eRegL dst, eRegL src) %{ + match(Set dst (CMoveL (Binary cmp flags) (Binary dst src))); + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt )); + ins_cost(400); + format %{ "CMOV$cmp $dst.lo,$src.lo\n\t" + "CMOV$cmp $dst.hi,$src.hi" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegReg_Lo2( dst, src ), enc_cmov(cmp), RegReg_Hi2( dst, src ) ); + ins_pipe( pipe_cmov_reg_long ); +%} + +instruct cmovLL_mem_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, eRegL dst, load_long_memory src) %{ + match(Set dst (CMoveL (Binary cmp flags) (Binary dst (LoadL src)))); + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt )); + ins_cost(500); + format %{ "CMOV$cmp $dst.lo,$src.lo\n\t" + "CMOV$cmp $dst.hi,$src.hi+4" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegMem(dst, src), enc_cmov(cmp), RegMem_Hi(dst, src) ); + ins_pipe( pipe_cmov_reg_long ); +%} + +// Compare 2 longs and CMOVE ints. +instruct cmovII_reg_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, eRegI dst, eRegI src) %{ + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt )); + match(Set dst (CMoveI (Binary cmp flags) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cmp $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegReg( dst, src ) ); + ins_pipe( pipe_cmov_reg ); +%} + +instruct cmovII_mem_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, eRegI dst, memory src) %{ + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt )); + match(Set dst (CMoveI (Binary cmp flags) (Binary dst (LoadI src)))); + ins_cost(250); + format %{ "CMOV$cmp $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegMem( dst, src ) ); + ins_pipe( pipe_cmov_mem ); +%} + +// Compare 2 longs and CMOVE ptrs. +instruct cmovPP_reg_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, eRegP dst, eRegP src) %{ + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt )); + match(Set dst (CMoveP (Binary cmp flags) (Binary dst src))); + ins_cost(200); + format %{ "CMOV$cmp $dst,$src" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegReg( dst, src ) ); + ins_pipe( pipe_cmov_reg ); +%} + +// Compare 2 longs and CMOVE doubles +instruct cmovDD_reg_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, regD dst, regD src) %{ + predicate( UseSSE<=1 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt ); + match(Set dst (CMoveD (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovD_regS(cmp,flags,dst,src); + %} +%} + +// Compare 2 longs and CMOVE doubles +instruct cmovXDD_reg_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, regXD dst, regXD src) %{ + predicate( UseSSE>=2 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt ); + match(Set dst (CMoveD (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovXD_regS(cmp,flags,dst,src); + %} +%} + +instruct cmovFF_reg_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, regF dst, regF src) %{ + predicate( UseSSE==0 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt ); + match(Set dst (CMoveF (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovF_regS(cmp,flags,dst,src); + %} +%} + + +instruct cmovXX_reg_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, regX dst, regX src) %{ + predicate( UseSSE>=1 && _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt ); + match(Set dst (CMoveF (Binary cmp flags) (Binary dst src))); + ins_cost(200); + expand %{ + fcmovX_regS(cmp,flags,dst,src); + %} +%} + + +// ============================================================================ +// Procedure Call/Return Instructions +// Call Java Static Instruction +// Note: If this code changes, the corresponding ret_addr_offset() and +// compute_padding() functions will have to be adjusted. +instruct CallStaticJavaDirect(method meth) %{ + match(CallStaticJava); + effect(USE meth); + + ins_cost(300); + format %{ "CALL,static " %} + opcode(0xE8); /* E8 cd */ + ins_encode( pre_call_FPU, + Java_Static_Call( meth ), + call_epilog, + post_call_FPU ); + ins_pipe( pipe_slow ); + ins_pc_relative(1); + ins_alignment(4); +%} + +// Call Java Dynamic Instruction +// Note: If this code changes, the corresponding ret_addr_offset() and +// compute_padding() functions will have to be adjusted. +instruct CallDynamicJavaDirect(method meth) %{ + match(CallDynamicJava); + effect(USE meth); + + ins_cost(300); + format %{ "MOV EAX,(oop)-1\n\t" + "CALL,dynamic" %} + opcode(0xE8); /* E8 cd */ + ins_encode( pre_call_FPU, + Java_Dynamic_Call( meth ), + call_epilog, + post_call_FPU ); + ins_pipe( pipe_slow ); + ins_pc_relative(1); + ins_alignment(4); +%} + +// Call Runtime Instruction +instruct CallRuntimeDirect(method meth) %{ + match(CallRuntime ); + effect(USE meth); + + ins_cost(300); + format %{ "CALL,runtime " %} + opcode(0xE8); /* E8 cd */ + // Use FFREEs to clear entries in float stack + ins_encode( pre_call_FPU, + FFree_Float_Stack_All, + Java_To_Runtime( meth ), + post_call_FPU ); + ins_pipe( pipe_slow ); + ins_pc_relative(1); +%} + +// Call runtime without safepoint +instruct CallLeafDirect(method meth) %{ + match(CallLeaf); + effect(USE meth); + + ins_cost(300); + format %{ "CALL_LEAF,runtime " %} + opcode(0xE8); /* E8 cd */ + ins_encode( pre_call_FPU, + FFree_Float_Stack_All, + Java_To_Runtime( meth ), + Verify_FPU_For_Leaf, post_call_FPU ); + ins_pipe( pipe_slow ); + ins_pc_relative(1); +%} + +instruct CallLeafNoFPDirect(method meth) %{ + match(CallLeafNoFP); + effect(USE meth); + + ins_cost(300); + format %{ "CALL_LEAF_NOFP,runtime " %} + opcode(0xE8); /* E8 cd */ + ins_encode(Java_To_Runtime(meth)); + ins_pipe( pipe_slow ); + ins_pc_relative(1); +%} + + +// Return Instruction +// Remove the return address & jump to it. +instruct Ret() %{ + match(Return); + format %{ "RET" %} + opcode(0xC3); + ins_encode(OpcP); + ins_pipe( pipe_jmp ); +%} + +// Tail Call; Jump from runtime stub to Java code. +// Also known as an 'interprocedural jump'. +// Target of jump will eventually return to caller. +// TailJump below removes the return address. +instruct TailCalljmpInd(eRegP_no_EBP jump_target, eBXRegP method_oop) %{ + match(TailCall jump_target method_oop ); + ins_cost(300); + format %{ "JMP $jump_target \t# EBX holds method oop" %} + opcode(0xFF, 0x4); /* Opcode FF /4 */ + ins_encode( OpcP, RegOpc(jump_target) ); + ins_pipe( pipe_jmp ); +%} + + +// Tail Jump; remove the return address; jump to target. +// TailCall above leaves the return address around. +instruct tailjmpInd(eRegP_no_EBP jump_target, eAXRegP ex_oop) %{ + match( TailJump jump_target ex_oop ); + ins_cost(300); + format %{ "POP EDX\t# pop return address into dummy\n\t" + "JMP $jump_target " %} + opcode(0xFF, 0x4); /* Opcode FF /4 */ + ins_encode( enc_pop_rdx, + OpcP, RegOpc(jump_target) ); + ins_pipe( pipe_jmp ); +%} + +// Create exception oop: created by stack-crawling runtime code. +// Created exception is now available to this handler, and is setup +// just prior to jumping to this handler. No code emitted. +instruct CreateException( eAXRegP ex_oop ) +%{ + match(Set ex_oop (CreateEx)); + + size(0); + // use the following format syntax + format %{ "# exception oop is in EAX; no code emitted" %} + ins_encode(); + ins_pipe( empty ); +%} + + +// Rethrow exception: +// The exception oop will come in the first argument position. +// Then JUMP (not call) to the rethrow stub code. +instruct RethrowException() +%{ + match(Rethrow); + + // use the following format syntax + format %{ "JMP rethrow_stub" %} + ins_encode(enc_rethrow); + ins_pipe( pipe_jmp ); +%} + +// inlined locking and unlocking + + +instruct cmpFastLock( eFlagsReg cr, eRegP object, eRegP box, eAXRegI tmp, eRegP scr) %{ + match( Set cr (FastLock object box) ); + effect( TEMP tmp, TEMP scr ); + ins_cost(300); + format %{ "FASTLOCK $object, $box KILLS $tmp,$scr" %} + ins_encode( Fast_Lock(object,box,tmp,scr) ); + ins_pipe( pipe_slow ); + ins_pc_relative(1); +%} + +instruct cmpFastUnlock( eFlagsReg cr, eRegP object, eAXRegP box, eRegP tmp ) %{ + match( Set cr (FastUnlock object box) ); + effect( TEMP tmp ); + ins_cost(300); + format %{ "FASTUNLOCK $object, $box, $tmp" %} + ins_encode( Fast_Unlock(object,box,tmp) ); + ins_pipe( pipe_slow ); + ins_pc_relative(1); +%} + + + +// ============================================================================ +// Safepoint Instruction +instruct safePoint_poll(eFlagsReg cr) %{ + match(SafePoint); + effect(KILL cr); + + // TODO-FIXME: we currently poll at offset 0 of the safepoint polling page. + // On SPARC that might be acceptable as we can generate the address with + // just a sethi, saving an or. By polling at offset 0 we can end up + // putting additional pressure on the index-0 in the D$. Because of + // alignment (just like the situation at hand) the lower indices tend + // to see more traffic. It'd be better to change the polling address + // to offset 0 of the last $line in the polling page. + + format %{ "TSTL #polladdr,EAX\t! Safepoint: poll for GC" %} + ins_cost(125); + size(6) ; + ins_encode( Safepoint_Poll() ); + ins_pipe( ialu_reg_mem ); +%} + +//----------PEEPHOLE RULES----------------------------------------------------- +// These must follow all instruction definitions as they use the names +// defined in the instructions definitions. +// +// peepmatch ( root_instr_name [preceeding_instruction]* ); +// +// peepconstraint %{ +// (instruction_number.operand_name relational_op instruction_number.operand_name +// [, ...] ); +// // instruction numbers are zero-based using left to right order in peepmatch +// +// peepreplace ( instr_name ( [instruction_number.operand_name]* ) ); +// // provide an instruction_number.operand_name for each operand that appears +// // in the replacement instruction's match rule +// +// ---------VM FLAGS--------------------------------------------------------- +// +// All peephole optimizations can be turned off using -XX:-OptoPeephole +// +// Each peephole rule is given an identifying number starting with zero and +// increasing by one in the order seen by the parser. An individual peephole +// can be enabled, and all others disabled, by using -XX:OptoPeepholeAt=# +// on the command-line. +// +// ---------CURRENT LIMITATIONS---------------------------------------------- +// +// Only match adjacent instructions in same basic block +// Only equality constraints +// Only constraints between operands, not (0.dest_reg == EAX_enc) +// Only one replacement instruction +// +// ---------EXAMPLE---------------------------------------------------------- +// +// // pertinent parts of existing instructions in architecture description +// instruct movI(eRegI dst, eRegI src) %{ +// match(Set dst (CopyI src)); +// %} +// +// instruct incI_eReg(eRegI dst, immI1 src, eFlagsReg cr) %{ +// match(Set dst (AddI dst src)); +// effect(KILL cr); +// %} +// +// // Change (inc mov) to lea +// peephole %{ +// // increment preceeded by register-register move +// peepmatch ( incI_eReg movI ); +// // require that the destination register of the increment +// // match the destination register of the move +// peepconstraint ( 0.dst == 1.dst ); +// // construct a replacement instruction that sets +// // the destination to ( move's source register + one ) +// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); +// %} +// +// Implementation no longer uses movX instructions since +// machine-independent system no longer uses CopyX nodes. +// +// peephole %{ +// peepmatch ( incI_eReg movI ); +// peepconstraint ( 0.dst == 1.dst ); +// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); +// %} +// +// peephole %{ +// peepmatch ( decI_eReg movI ); +// peepconstraint ( 0.dst == 1.dst ); +// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); +// %} +// +// peephole %{ +// peepmatch ( addI_eReg_imm movI ); +// peepconstraint ( 0.dst == 1.dst ); +// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); +// %} +// +// peephole %{ +// peepmatch ( addP_eReg_imm movP ); +// peepconstraint ( 0.dst == 1.dst ); +// peepreplace ( leaP_eReg_immI( 0.dst 1.src 0.src ) ); +// %} + +// // Change load of spilled value to only a spill +// instruct storeI(memory mem, eRegI src) %{ +// match(Set mem (StoreI mem src)); +// %} +// +// instruct loadI(eRegI dst, memory mem) %{ +// match(Set dst (LoadI mem)); +// %} +// +peephole %{ + peepmatch ( loadI storeI ); + peepconstraint ( 1.src == 0.dst, 1.mem == 0.mem ); + peepreplace ( storeI( 1.mem 1.mem 1.src ) ); +%} + +//----------SMARTSPILL RULES--------------------------------------------------- +// These must follow all instruction definitions as they use the names +// defined in the instructions definitions. diff --git a/hotspot/src/cpu/x86/vm/x86_64.ad b/hotspot/src/cpu/x86/vm/x86_64.ad new file mode 100644 index 00000000000..dddc81c2f4e --- /dev/null +++ b/hotspot/src/cpu/x86/vm/x86_64.ad @@ -0,0 +1,11435 @@ +// +// Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// AMD64 Architecture Description File + +//----------REGISTER DEFINITION BLOCK------------------------------------------ +// This information is used by the matcher and the register allocator to +// describe individual registers and classes of registers within the target +// archtecture. + +register %{ +//----------Architecture Description Register Definitions---------------------- +// General Registers +// "reg_def" name ( register save type, C convention save type, +// ideal register type, encoding ); +// Register Save Types: +// +// NS = No-Save: The register allocator assumes that these registers +// can be used without saving upon entry to the method, & +// that they do not need to be saved at call sites. +// +// SOC = Save-On-Call: The register allocator assumes that these registers +// can be used without saving upon entry to the method, +// but that they must be saved at call sites. +// +// SOE = Save-On-Entry: The register allocator assumes that these registers +// must be saved before using them upon entry to the +// method, but they do not need to be saved at call +// sites. +// +// AS = Always-Save: The register allocator assumes that these registers +// must be saved before using them upon entry to the +// method, & that they must be saved at call sites. +// +// Ideal Register Type is used to determine how to save & restore a +// register. Op_RegI will get spilled with LoadI/StoreI, Op_RegP will get +// spilled with LoadP/StoreP. If the register supports both, use Op_RegI. +// +// The encoding number is the actual bit-pattern placed into the opcodes. + +// General Registers +// R8-R15 must be encoded with REX. (RSP, RBP, RSI, RDI need REX when +// used as byte registers) + +// Previously set RBX, RSI, and RDI as save-on-entry for java code +// Turn off SOE in java-code due to frequent use of uncommon-traps. +// Now that allocator is better, turn on RSI and RDI as SOE registers. + +reg_def RAX (SOC, SOC, Op_RegI, 0, rax->as_VMReg()); +reg_def RAX_H(SOC, SOC, Op_RegI, 0, rax->as_VMReg()->next()); + +reg_def RCX (SOC, SOC, Op_RegI, 1, rcx->as_VMReg()); +reg_def RCX_H(SOC, SOC, Op_RegI, 1, rcx->as_VMReg()->next()); + +reg_def RDX (SOC, SOC, Op_RegI, 2, rdx->as_VMReg()); +reg_def RDX_H(SOC, SOC, Op_RegI, 2, rdx->as_VMReg()->next()); + +reg_def RBX (SOC, SOE, Op_RegI, 3, rbx->as_VMReg()); +reg_def RBX_H(SOC, SOE, Op_RegI, 3, rbx->as_VMReg()->next()); + +reg_def RSP (NS, NS, Op_RegI, 4, rsp->as_VMReg()); +reg_def RSP_H(NS, NS, Op_RegI, 4, rsp->as_VMReg()->next()); + +// now that adapter frames are gone RBP is always saved and restored by the prolog/epilog code +reg_def RBP (NS, SOE, Op_RegI, 5, rbp->as_VMReg()); +reg_def RBP_H(NS, SOE, Op_RegI, 5, rbp->as_VMReg()->next()); + +#ifdef _WIN64 + +reg_def RSI (SOC, SOE, Op_RegI, 6, rsi->as_VMReg()); +reg_def RSI_H(SOC, SOE, Op_RegI, 6, rsi->as_VMReg()->next()); + +reg_def RDI (SOC, SOE, Op_RegI, 7, rdi->as_VMReg()); +reg_def RDI_H(SOC, SOE, Op_RegI, 7, rdi->as_VMReg()->next()); + +#else + +reg_def RSI (SOC, SOC, Op_RegI, 6, rsi->as_VMReg()); +reg_def RSI_H(SOC, SOC, Op_RegI, 6, rsi->as_VMReg()->next()); + +reg_def RDI (SOC, SOC, Op_RegI, 7, rdi->as_VMReg()); +reg_def RDI_H(SOC, SOC, Op_RegI, 7, rdi->as_VMReg()->next()); + +#endif + +reg_def R8 (SOC, SOC, Op_RegI, 8, r8->as_VMReg()); +reg_def R8_H (SOC, SOC, Op_RegI, 8, r8->as_VMReg()->next()); + +reg_def R9 (SOC, SOC, Op_RegI, 9, r9->as_VMReg()); +reg_def R9_H (SOC, SOC, Op_RegI, 9, r9->as_VMReg()->next()); + +reg_def R10 (SOC, SOC, Op_RegI, 10, r10->as_VMReg()); +reg_def R10_H(SOC, SOC, Op_RegI, 10, r10->as_VMReg()->next()); + +reg_def R11 (SOC, SOC, Op_RegI, 11, r11->as_VMReg()); +reg_def R11_H(SOC, SOC, Op_RegI, 11, r11->as_VMReg()->next()); + +reg_def R12 (SOC, SOE, Op_RegI, 12, r12->as_VMReg()); +reg_def R12_H(SOC, SOE, Op_RegI, 12, r12->as_VMReg()->next()); + +reg_def R13 (SOC, SOE, Op_RegI, 13, r13->as_VMReg()); +reg_def R13_H(SOC, SOE, Op_RegI, 13, r13->as_VMReg()->next()); + +reg_def R14 (SOC, SOE, Op_RegI, 14, r14->as_VMReg()); +reg_def R14_H(SOC, SOE, Op_RegI, 14, r14->as_VMReg()->next()); + +reg_def R15 (SOC, SOE, Op_RegI, 15, r15->as_VMReg()); +reg_def R15_H(SOC, SOE, Op_RegI, 15, r15->as_VMReg()->next()); + + +// Floating Point Registers + +// XMM registers. 128-bit registers or 4 words each, labeled (a)-d. +// Word a in each register holds a Float, words ab hold a Double. We +// currently do not use the SIMD capabilities, so registers cd are +// unused at the moment. +// XMM8-XMM15 must be encoded with REX. +// Linux ABI: No register preserved across function calls +// XMM0-XMM7 might hold parameters +// Windows ABI: XMM6-XMM15 preserved across function calls +// XMM0-XMM3 might hold parameters + +reg_def XMM0 (SOC, SOC, Op_RegF, 0, xmm0->as_VMReg()); +reg_def XMM0_H (SOC, SOC, Op_RegF, 0, xmm0->as_VMReg()->next()); + +reg_def XMM1 (SOC, SOC, Op_RegF, 1, xmm1->as_VMReg()); +reg_def XMM1_H (SOC, SOC, Op_RegF, 1, xmm1->as_VMReg()->next()); + +reg_def XMM2 (SOC, SOC, Op_RegF, 2, xmm2->as_VMReg()); +reg_def XMM2_H (SOC, SOC, Op_RegF, 2, xmm2->as_VMReg()->next()); + +reg_def XMM3 (SOC, SOC, Op_RegF, 3, xmm3->as_VMReg()); +reg_def XMM3_H (SOC, SOC, Op_RegF, 3, xmm3->as_VMReg()->next()); + +reg_def XMM4 (SOC, SOC, Op_RegF, 4, xmm4->as_VMReg()); +reg_def XMM4_H (SOC, SOC, Op_RegF, 4, xmm4->as_VMReg()->next()); + +reg_def XMM5 (SOC, SOC, Op_RegF, 5, xmm5->as_VMReg()); +reg_def XMM5_H (SOC, SOC, Op_RegF, 5, xmm5->as_VMReg()->next()); + +#ifdef _WIN64 + +reg_def XMM6 (SOC, SOE, Op_RegF, 6, xmm6->as_VMReg()); +reg_def XMM6_H (SOC, SOE, Op_RegF, 6, xmm6->as_VMReg()->next()); + +reg_def XMM7 (SOC, SOE, Op_RegF, 7, xmm7->as_VMReg()); +reg_def XMM7_H (SOC, SOE, Op_RegF, 7, xmm7->as_VMReg()->next()); + +reg_def XMM8 (SOC, SOE, Op_RegF, 8, xmm8->as_VMReg()); +reg_def XMM8_H (SOC, SOE, Op_RegF, 8, xmm8->as_VMReg()->next()); + +reg_def XMM9 (SOC, SOE, Op_RegF, 9, xmm9->as_VMReg()); +reg_def XMM9_H (SOC, SOE, Op_RegF, 9, xmm9->as_VMReg()->next()); + +reg_def XMM10 (SOC, SOE, Op_RegF, 10, xmm10->as_VMReg()); +reg_def XMM10_H(SOC, SOE, Op_RegF, 10, xmm10->as_VMReg()->next()); + +reg_def XMM11 (SOC, SOE, Op_RegF, 11, xmm11->as_VMReg()); +reg_def XMM11_H(SOC, SOE, Op_RegF, 11, xmm11->as_VMReg()->next()); + +reg_def XMM12 (SOC, SOE, Op_RegF, 12, xmm12->as_VMReg()); +reg_def XMM12_H(SOC, SOE, Op_RegF, 12, xmm12->as_VMReg()->next()); + +reg_def XMM13 (SOC, SOE, Op_RegF, 13, xmm13->as_VMReg()); +reg_def XMM13_H(SOC, SOE, Op_RegF, 13, xmm13->as_VMReg()->next()); + +reg_def XMM14 (SOC, SOE, Op_RegF, 14, xmm14->as_VMReg()); +reg_def XMM14_H(SOC, SOE, Op_RegF, 14, xmm14->as_VMReg()->next()); + +reg_def XMM15 (SOC, SOE, Op_RegF, 15, xmm15->as_VMReg()); +reg_def XMM15_H(SOC, SOE, Op_RegF, 15, xmm15->as_VMReg()->next()); + +#else + +reg_def XMM6 (SOC, SOC, Op_RegF, 6, xmm6->as_VMReg()); +reg_def XMM6_H (SOC, SOC, Op_RegF, 6, xmm6->as_VMReg()->next()); + +reg_def XMM7 (SOC, SOC, Op_RegF, 7, xmm7->as_VMReg()); +reg_def XMM7_H (SOC, SOC, Op_RegF, 7, xmm7->as_VMReg()->next()); + +reg_def XMM8 (SOC, SOC, Op_RegF, 8, xmm8->as_VMReg()); +reg_def XMM8_H (SOC, SOC, Op_RegF, 8, xmm8->as_VMReg()->next()); + +reg_def XMM9 (SOC, SOC, Op_RegF, 9, xmm9->as_VMReg()); +reg_def XMM9_H (SOC, SOC, Op_RegF, 9, xmm9->as_VMReg()->next()); + +reg_def XMM10 (SOC, SOC, Op_RegF, 10, xmm10->as_VMReg()); +reg_def XMM10_H(SOC, SOC, Op_RegF, 10, xmm10->as_VMReg()->next()); + +reg_def XMM11 (SOC, SOC, Op_RegF, 11, xmm11->as_VMReg()); +reg_def XMM11_H(SOC, SOC, Op_RegF, 11, xmm11->as_VMReg()->next()); + +reg_def XMM12 (SOC, SOC, Op_RegF, 12, xmm12->as_VMReg()); +reg_def XMM12_H(SOC, SOC, Op_RegF, 12, xmm12->as_VMReg()->next()); + +reg_def XMM13 (SOC, SOC, Op_RegF, 13, xmm13->as_VMReg()); +reg_def XMM13_H(SOC, SOC, Op_RegF, 13, xmm13->as_VMReg()->next()); + +reg_def XMM14 (SOC, SOC, Op_RegF, 14, xmm14->as_VMReg()); +reg_def XMM14_H(SOC, SOC, Op_RegF, 14, xmm14->as_VMReg()->next()); + +reg_def XMM15 (SOC, SOC, Op_RegF, 15, xmm15->as_VMReg()); +reg_def XMM15_H(SOC, SOC, Op_RegF, 15, xmm15->as_VMReg()->next()); + +#endif // _WIN64 + +reg_def RFLAGS(SOC, SOC, 0, 16, VMRegImpl::Bad()); + +// Specify priority of register selection within phases of register +// allocation. Highest priority is first. A useful heuristic is to +// give registers a low priority when they are required by machine +// instructions, like EAX and EDX on I486, and choose no-save registers +// before save-on-call, & save-on-call before save-on-entry. Registers +// which participate in fixed calling sequences should come last. +// Registers which are used as pairs must fall on an even boundary. + +alloc_class chunk0(R10, R10_H, + R11, R11_H, + R8, R8_H, + R9, R9_H, + R12, R12_H, + RCX, RCX_H, + RBX, RBX_H, + RDI, RDI_H, + RDX, RDX_H, + RSI, RSI_H, + RAX, RAX_H, + RBP, RBP_H, + R13, R13_H, + R14, R14_H, + R15, R15_H, + RSP, RSP_H); + +// XXX probably use 8-15 first on Linux +alloc_class chunk1(XMM0, XMM0_H, + XMM1, XMM1_H, + XMM2, XMM2_H, + XMM3, XMM3_H, + XMM4, XMM4_H, + XMM5, XMM5_H, + XMM6, XMM6_H, + XMM7, XMM7_H, + XMM8, XMM8_H, + XMM9, XMM9_H, + XMM10, XMM10_H, + XMM11, XMM11_H, + XMM12, XMM12_H, + XMM13, XMM13_H, + XMM14, XMM14_H, + XMM15, XMM15_H); + +alloc_class chunk2(RFLAGS); + + +//----------Architecture Description Register Classes-------------------------- +// Several register classes are automatically defined based upon information in +// this architecture description. +// 1) reg_class inline_cache_reg ( /* as def'd in frame section */ ) +// 2) reg_class compiler_method_oop_reg ( /* as def'd in frame section */ ) +// 2) reg_class interpreter_method_oop_reg ( /* as def'd in frame section */ ) +// 3) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) +// + +// Class for all pointer registers (including RSP) +reg_class any_reg(RAX, RAX_H, + RDX, RDX_H, + RBP, RBP_H, + RDI, RDI_H, + RSI, RSI_H, + RCX, RCX_H, + RBX, RBX_H, + RSP, RSP_H, + R8, R8_H, + R9, R9_H, + R10, R10_H, + R11, R11_H, + R12, R12_H, + R13, R13_H, + R14, R14_H, + R15, R15_H); + +// Class for all pointer registers except RSP +reg_class ptr_reg(RAX, RAX_H, + RDX, RDX_H, + RBP, RBP_H, + RDI, RDI_H, + RSI, RSI_H, + RCX, RCX_H, + RBX, RBX_H, + R8, R8_H, + R9, R9_H, + R10, R10_H, + R11, R11_H, + R12, R12_H, + R13, R13_H, + R14, R14_H); + +// Class for all pointer registers except RAX and RSP +reg_class ptr_no_rax_reg(RDX, RDX_H, + RBP, RBP_H, + RDI, RDI_H, + RSI, RSI_H, + RCX, RCX_H, + RBX, RBX_H, + R8, R8_H, + R9, R9_H, + R10, R10_H, + R11, R11_H, + R12, R12_H, + R13, R13_H, + R14, R14_H); + +reg_class ptr_no_rbp_reg(RDX, RDX_H, + RAX, RAX_H, + RDI, RDI_H, + RSI, RSI_H, + RCX, RCX_H, + RBX, RBX_H, + R8, R8_H, + R9, R9_H, + R10, R10_H, + R11, R11_H, + R12, R12_H, + R13, R13_H, + R14, R14_H); + +// Class for all pointer registers except RAX, RBX and RSP +reg_class ptr_no_rax_rbx_reg(RDX, RDX_H, + RBP, RBP_H, + RDI, RDI_H, + RSI, RSI_H, + RCX, RCX_H, + R8, R8_H, + R9, R9_H, + R10, R10_H, + R11, R11_H, + R12, R12_H, + R13, R13_H, + R14, R14_H); + +// Singleton class for RAX pointer register +reg_class ptr_rax_reg(RAX, RAX_H); + +// Singleton class for RBX pointer register +reg_class ptr_rbx_reg(RBX, RBX_H); + +// Singleton class for RSI pointer register +reg_class ptr_rsi_reg(RSI, RSI_H); + +// Singleton class for RDI pointer register +reg_class ptr_rdi_reg(RDI, RDI_H); + +// Singleton class for RBP pointer register +reg_class ptr_rbp_reg(RBP, RBP_H); + +// Singleton class for stack pointer +reg_class ptr_rsp_reg(RSP, RSP_H); + +// Singleton class for TLS pointer +reg_class ptr_r15_reg(R15, R15_H); + +// Class for all long registers (except RSP) +reg_class long_reg(RAX, RAX_H, + RDX, RDX_H, + RBP, RBP_H, + RDI, RDI_H, + RSI, RSI_H, + RCX, RCX_H, + RBX, RBX_H, + R8, R8_H, + R9, R9_H, + R10, R10_H, + R11, R11_H, + R12, R12_H, + R13, R13_H, + R14, R14_H); + +// Class for all long registers except RAX, RDX (and RSP) +reg_class long_no_rax_rdx_reg(RBP, RBP_H, + RDI, RDI_H, + RSI, RSI_H, + RCX, RCX_H, + RBX, RBX_H, + R8, R8_H, + R9, R9_H, + R10, R10_H, + R11, R11_H, + R12, R12_H, + R13, R13_H, + R14, R14_H); + +// Class for all long registers except RCX (and RSP) +reg_class long_no_rcx_reg(RBP, RBP_H, + RDI, RDI_H, + RSI, RSI_H, + RAX, RAX_H, + RDX, RDX_H, + RBX, RBX_H, + R8, R8_H, + R9, R9_H, + R10, R10_H, + R11, R11_H, + R12, R12_H, + R13, R13_H, + R14, R14_H); + +// Class for all long registers except RAX (and RSP) +reg_class long_no_rax_reg(RBP, RBP_H, + RDX, RDX_H, + RDI, RDI_H, + RSI, RSI_H, + RCX, RCX_H, + RBX, RBX_H, + R8, R8_H, + R9, R9_H, + R10, R10_H, + R11, R11_H, + R12, R12_H, + R13, R13_H, + R14, R14_H); + +// Singleton class for RAX long register +reg_class long_rax_reg(RAX, RAX_H); + +// Singleton class for RCX long register +reg_class long_rcx_reg(RCX, RCX_H); + +// Singleton class for RDX long register +reg_class long_rdx_reg(RDX, RDX_H); + +// Class for all int registers (except RSP) +reg_class int_reg(RAX, + RDX, + RBP, + RDI, + RSI, + RCX, + RBX, + R8, + R9, + R10, + R11, + R12, + R13, + R14); + +// Class for all int registers except RCX (and RSP) +reg_class int_no_rcx_reg(RAX, + RDX, + RBP, + RDI, + RSI, + RBX, + R8, + R9, + R10, + R11, + R12, + R13, + R14); + +// Class for all int registers except RAX, RDX (and RSP) +reg_class int_no_rax_rdx_reg(RBP, + RDI + RSI, + RCX, + RBX, + R8, + R9, + R10, + R11, + R12, + R13, + R14); + +// Singleton class for RAX int register +reg_class int_rax_reg(RAX); + +// Singleton class for RBX int register +reg_class int_rbx_reg(RBX); + +// Singleton class for RCX int register +reg_class int_rcx_reg(RCX); + +// Singleton class for RCX int register +reg_class int_rdx_reg(RDX); + +// Singleton class for RCX int register +reg_class int_rdi_reg(RDI); + +// Singleton class for instruction pointer +// reg_class ip_reg(RIP); + +// Singleton class for condition codes +reg_class int_flags(RFLAGS); + +// Class for all float registers +reg_class float_reg(XMM0, + XMM1, + XMM2, + XMM3, + XMM4, + XMM5, + XMM6, + XMM7, + XMM8, + XMM9, + XMM10, + XMM11, + XMM12, + XMM13, + XMM14, + XMM15); + +// Class for all double registers +reg_class double_reg(XMM0, XMM0_H, + XMM1, XMM1_H, + XMM2, XMM2_H, + XMM3, XMM3_H, + XMM4, XMM4_H, + XMM5, XMM5_H, + XMM6, XMM6_H, + XMM7, XMM7_H, + XMM8, XMM8_H, + XMM9, XMM9_H, + XMM10, XMM10_H, + XMM11, XMM11_H, + XMM12, XMM12_H, + XMM13, XMM13_H, + XMM14, XMM14_H, + XMM15, XMM15_H); +%} + + +//----------SOURCE BLOCK------------------------------------------------------- +// This is a block of C++ code which provides values, functions, and +// definitions necessary in the rest of the architecture description +source %{ +#define RELOC_IMM64 Assembler::imm64_operand +#define RELOC_DISP32 Assembler::disp32_operand + +#define __ _masm. + +// !!!!! Special hack to get all types of calls to specify the byte offset +// from the start of the call to the point where the return address +// will point. +int MachCallStaticJavaNode::ret_addr_offset() +{ + return 5; // 5 bytes from start of call to where return address points +} + +int MachCallDynamicJavaNode::ret_addr_offset() +{ + return 15; // 15 bytes from start of call to where return address points +} + +// In os_cpu .ad file +// int MachCallRuntimeNode::ret_addr_offset() + +// Indicate if the safepoint node needs the polling page as an input. +// Since amd64 does not have absolute addressing but RIP-relative +// addressing and the polling page is within 2G, it doesn't. +bool SafePointNode::needs_polling_address_input() +{ + return false; +} + +// +// Compute padding required for nodes which need alignment +// + +// The address of the call instruction needs to be 4-byte aligned to +// ensure that it does not span a cache line so that it can be patched. +int CallStaticJavaDirectNode::compute_padding(int current_offset) const +{ + current_offset += 1; // skip call opcode byte + return round_to(current_offset, alignment_required()) - current_offset; +} + +// The address of the call instruction needs to be 4-byte aligned to +// ensure that it does not span a cache line so that it can be patched. +int CallDynamicJavaDirectNode::compute_padding(int current_offset) const +{ + current_offset += 11; // skip movq instruction + call opcode byte + return round_to(current_offset, alignment_required()) - current_offset; +} + +#ifndef PRODUCT +void MachBreakpointNode::format(PhaseRegAlloc*, outputStream* st) const +{ + st->print("INT3"); +} +#endif + +// EMIT_RM() +void emit_rm(CodeBuffer &cbuf, int f1, int f2, int f3) +{ + unsigned char c = (unsigned char) ((f1 << 6) | (f2 << 3) | f3); + *(cbuf.code_end()) = c; + cbuf.set_code_end(cbuf.code_end() + 1); +} + +// EMIT_CC() +void emit_cc(CodeBuffer &cbuf, int f1, int f2) +{ + unsigned char c = (unsigned char) (f1 | f2); + *(cbuf.code_end()) = c; + cbuf.set_code_end(cbuf.code_end() + 1); +} + +// EMIT_OPCODE() +void emit_opcode(CodeBuffer &cbuf, int code) +{ + *(cbuf.code_end()) = (unsigned char) code; + cbuf.set_code_end(cbuf.code_end() + 1); +} + +// EMIT_OPCODE() w/ relocation information +void emit_opcode(CodeBuffer &cbuf, + int code, relocInfo::relocType reloc, int offset, int format) +{ + cbuf.relocate(cbuf.inst_mark() + offset, reloc, format); + emit_opcode(cbuf, code); +} + +// EMIT_D8() +void emit_d8(CodeBuffer &cbuf, int d8) +{ + *(cbuf.code_end()) = (unsigned char) d8; + cbuf.set_code_end(cbuf.code_end() + 1); +} + +// EMIT_D16() +void emit_d16(CodeBuffer &cbuf, int d16) +{ + *((short *)(cbuf.code_end())) = d16; + cbuf.set_code_end(cbuf.code_end() + 2); +} + +// EMIT_D32() +void emit_d32(CodeBuffer &cbuf, int d32) +{ + *((int *)(cbuf.code_end())) = d32; + cbuf.set_code_end(cbuf.code_end() + 4); +} + +// EMIT_D64() +void emit_d64(CodeBuffer &cbuf, int64_t d64) +{ + *((int64_t*) (cbuf.code_end())) = d64; + cbuf.set_code_end(cbuf.code_end() + 8); +} + +// emit 32 bit value and construct relocation entry from relocInfo::relocType +void emit_d32_reloc(CodeBuffer& cbuf, + int d32, + relocInfo::relocType reloc, + int format) +{ + assert(reloc != relocInfo::external_word_type, "use 2-arg emit_d32_reloc"); + cbuf.relocate(cbuf.inst_mark(), reloc, format); + + *((int*) (cbuf.code_end())) = d32; + cbuf.set_code_end(cbuf.code_end() + 4); +} + +// emit 32 bit value and construct relocation entry from RelocationHolder +void emit_d32_reloc(CodeBuffer& cbuf, + int d32, + RelocationHolder const& rspec, + int format) +{ +#ifdef ASSERT + if (rspec.reloc()->type() == relocInfo::oop_type && + d32 != 0 && d32 != (intptr_t) Universe::non_oop_word()) { + assert(oop((intptr_t)d32)->is_oop() && oop((intptr_t)d32)->is_perm(), "cannot embed non-perm oops in code"); + } +#endif + cbuf.relocate(cbuf.inst_mark(), rspec, format); + + *((int* )(cbuf.code_end())) = d32; + cbuf.set_code_end(cbuf.code_end() + 4); +} + +void emit_d32_reloc(CodeBuffer& cbuf, address addr) { + address next_ip = cbuf.code_end() + 4; + emit_d32_reloc(cbuf, (int) (addr - next_ip), + external_word_Relocation::spec(addr), + RELOC_DISP32); +} + + +// emit 64 bit value and construct relocation entry from relocInfo::relocType +void emit_d64_reloc(CodeBuffer& cbuf, + int64_t d64, + relocInfo::relocType reloc, + int format) +{ + cbuf.relocate(cbuf.inst_mark(), reloc, format); + + *((int64_t*) (cbuf.code_end())) = d64; + cbuf.set_code_end(cbuf.code_end() + 8); +} + +// emit 64 bit value and construct relocation entry from RelocationHolder +void emit_d64_reloc(CodeBuffer& cbuf, + int64_t d64, + RelocationHolder const& rspec, + int format) +{ +#ifdef ASSERT + if (rspec.reloc()->type() == relocInfo::oop_type && + d64 != 0 && d64 != (int64_t) Universe::non_oop_word()) { + assert(oop(d64)->is_oop() && oop(d64)->is_perm(), + "cannot embed non-perm oops in code"); + } +#endif + cbuf.relocate(cbuf.inst_mark(), rspec, format); + + *((int64_t*) (cbuf.code_end())) = d64; + cbuf.set_code_end(cbuf.code_end() + 8); +} + +// Access stack slot for load or store +void store_to_stackslot(CodeBuffer &cbuf, int opcode, int rm_field, int disp) +{ + emit_opcode(cbuf, opcode); // (e.g., FILD [RSP+src]) + if (-0x80 <= disp && disp < 0x80) { + emit_rm(cbuf, 0x01, rm_field, RSP_enc); // R/M byte + emit_rm(cbuf, 0x00, RSP_enc, RSP_enc); // SIB byte + emit_d8(cbuf, disp); // Displacement // R/M byte + } else { + emit_rm(cbuf, 0x02, rm_field, RSP_enc); // R/M byte + emit_rm(cbuf, 0x00, RSP_enc, RSP_enc); // SIB byte + emit_d32(cbuf, disp); // Displacement // R/M byte + } +} + + // rRegI ereg, memory mem) %{ // emit_reg_mem +void encode_RegMem(CodeBuffer &cbuf, + int reg, + int base, int index, int scale, int disp, bool disp_is_oop) +{ + assert(!disp_is_oop, "cannot have disp"); + int regenc = reg & 7; + int baseenc = base & 7; + int indexenc = index & 7; + + // There is no index & no scale, use form without SIB byte + if (index == 0x4 && scale == 0 && base != RSP_enc && base != R12_enc) { + // If no displacement, mode is 0x0; unless base is [RBP] or [R13] + if (disp == 0 && base != RBP_enc && base != R13_enc) { + emit_rm(cbuf, 0x0, regenc, baseenc); // * + } else if (-0x80 <= disp && disp < 0x80 && !disp_is_oop) { + // If 8-bit displacement, mode 0x1 + emit_rm(cbuf, 0x1, regenc, baseenc); // * + emit_d8(cbuf, disp); + } else { + // If 32-bit displacement + if (base == -1) { // Special flag for absolute address + emit_rm(cbuf, 0x0, regenc, 0x5); // * + if (disp_is_oop) { + emit_d32_reloc(cbuf, disp, relocInfo::oop_type, RELOC_DISP32); + } else { + emit_d32(cbuf, disp); + } + } else { + // Normal base + offset + emit_rm(cbuf, 0x2, regenc, baseenc); // * + if (disp_is_oop) { + emit_d32_reloc(cbuf, disp, relocInfo::oop_type, RELOC_DISP32); + } else { + emit_d32(cbuf, disp); + } + } + } + } else { + // Else, encode with the SIB byte + // If no displacement, mode is 0x0; unless base is [RBP] or [R13] + if (disp == 0 && base != RBP_enc && base != R13_enc) { + // If no displacement + emit_rm(cbuf, 0x0, regenc, 0x4); // * + emit_rm(cbuf, scale, indexenc, baseenc); + } else { + if (-0x80 <= disp && disp < 0x80 && !disp_is_oop) { + // If 8-bit displacement, mode 0x1 + emit_rm(cbuf, 0x1, regenc, 0x4); // * + emit_rm(cbuf, scale, indexenc, baseenc); + emit_d8(cbuf, disp); + } else { + // If 32-bit displacement + if (base == 0x04 ) { + emit_rm(cbuf, 0x2, regenc, 0x4); + emit_rm(cbuf, scale, indexenc, 0x04); // XXX is this valid??? + } else { + emit_rm(cbuf, 0x2, regenc, 0x4); + emit_rm(cbuf, scale, indexenc, baseenc); // * + } + if (disp_is_oop) { + emit_d32_reloc(cbuf, disp, relocInfo::oop_type, RELOC_DISP32); + } else { + emit_d32(cbuf, disp); + } + } + } + } +} + +void encode_copy(CodeBuffer &cbuf, int dstenc, int srcenc) +{ + if (dstenc != srcenc) { + if (dstenc < 8) { + if (srcenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + srcenc -= 8; + } + } else { + if (srcenc < 8) { + emit_opcode(cbuf, Assembler::REX_R); + } else { + emit_opcode(cbuf, Assembler::REX_RB); + srcenc -= 8; + } + dstenc -= 8; + } + + emit_opcode(cbuf, 0x8B); + emit_rm(cbuf, 0x3, dstenc, srcenc); + } +} + +void encode_CopyXD( CodeBuffer &cbuf, int dst_encoding, int src_encoding ) { + if( dst_encoding == src_encoding ) { + // reg-reg copy, use an empty encoding + } else { + MacroAssembler _masm(&cbuf); + + __ movdqa(as_XMMRegister(dst_encoding), as_XMMRegister(src_encoding)); + } +} + + +//============================================================================= +#ifndef PRODUCT +void MachPrologNode::format(PhaseRegAlloc* ra_, outputStream* st) const +{ + Compile* C = ra_->C; + + int framesize = C->frame_slots() << LogBytesPerInt; + assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); + // Remove wordSize for return adr already pushed + // and another for the RBP we are going to save + framesize -= 2*wordSize; + bool need_nop = true; + + // Calls to C2R adapters often do not accept exceptional returns. + // We require that their callers must bang for them. But be + // careful, because some VM calls (such as call site linkage) can + // use several kilobytes of stack. But the stack safety zone should + // account for that. See bugs 4446381, 4468289, 4497237. + if (C->need_stack_bang(framesize)) { + st->print_cr("# stack bang"); st->print("\t"); + need_nop = false; + } + st->print_cr("pushq rbp"); st->print("\t"); + + if (VerifyStackAtCalls) { + // Majik cookie to verify stack depth + st->print_cr("pushq 0xffffffffbadb100d" + "\t# Majik cookie for stack depth check"); + st->print("\t"); + framesize -= wordSize; // Remove 2 for cookie + need_nop = false; + } + + if (framesize) { + st->print("subq rsp, #%d\t# Create frame", framesize); + if (framesize < 0x80 && need_nop) { + st->print("\n\tnop\t# nop for patch_verified_entry"); + } + } +} +#endif + +void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const +{ + Compile* C = ra_->C; + + // WARNING: Initial instruction MUST be 5 bytes or longer so that + // NativeJump::patch_verified_entry will be able to patch out the entry + // code safely. The fldcw is ok at 6 bytes, the push to verify stack + // depth is ok at 5 bytes, the frame allocation can be either 3 or + // 6 bytes. So if we don't do the fldcw or the push then we must + // use the 6 byte frame allocation even if we have no frame. :-( + // If method sets FPU control word do it now + + int framesize = C->frame_slots() << LogBytesPerInt; + assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); + // Remove wordSize for return adr already pushed + // and another for the RBP we are going to save + framesize -= 2*wordSize; + bool need_nop = true; + + // Calls to C2R adapters often do not accept exceptional returns. + // We require that their callers must bang for them. But be + // careful, because some VM calls (such as call site linkage) can + // use several kilobytes of stack. But the stack safety zone should + // account for that. See bugs 4446381, 4468289, 4497237. + if (C->need_stack_bang(framesize)) { + MacroAssembler masm(&cbuf); + masm.generate_stack_overflow_check(framesize); + need_nop = false; + } + + // We always push rbp so that on return to interpreter rbp will be + // restored correctly and we can correct the stack. + emit_opcode(cbuf, 0x50 | RBP_enc); + + if (VerifyStackAtCalls) { + // Majik cookie to verify stack depth + emit_opcode(cbuf, 0x68); // pushq (sign-extended) 0xbadb100d + emit_d32(cbuf, 0xbadb100d); + framesize -= wordSize; // Remove 2 for cookie + need_nop = false; + } + + if (framesize) { + emit_opcode(cbuf, Assembler::REX_W); + if (framesize < 0x80) { + emit_opcode(cbuf, 0x83); // sub SP,#framesize + emit_rm(cbuf, 0x3, 0x05, RSP_enc); + emit_d8(cbuf, framesize); + if (need_nop) { + emit_opcode(cbuf, 0x90); // nop + } + } else { + emit_opcode(cbuf, 0x81); // sub SP,#framesize + emit_rm(cbuf, 0x3, 0x05, RSP_enc); + emit_d32(cbuf, framesize); + } + } + + C->set_frame_complete(cbuf.code_end() - cbuf.code_begin()); + +#ifdef ASSERT + if (VerifyStackAtCalls) { + Label L; + MacroAssembler masm(&cbuf); + masm.pushq(rax); + masm.movq(rax, rsp); + masm.andq(rax, StackAlignmentInBytes-1); + masm.cmpq(rax, StackAlignmentInBytes-wordSize); + masm.popq(rax); + masm.jcc(Assembler::equal, L); + masm.stop("Stack is not properly aligned!"); + masm.bind(L); + } +#endif +} + +uint MachPrologNode::size(PhaseRegAlloc* ra_) const +{ + return MachNode::size(ra_); // too many variables; just compute it + // the hard way +} + +int MachPrologNode::reloc() const +{ + return 0; // a large enough number +} + +//============================================================================= +#ifndef PRODUCT +void MachEpilogNode::format(PhaseRegAlloc* ra_, outputStream* st) const +{ + Compile* C = ra_->C; + int framesize = C->frame_slots() << LogBytesPerInt; + assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); + // Remove word for return adr already pushed + // and RBP + framesize -= 2*wordSize; + + if (framesize) { + st->print_cr("addq\trsp, %d\t# Destroy frame", framesize); + st->print("\t"); + } + + st->print_cr("popq\trbp"); + if (do_polling() && C->is_method_compilation()) { + st->print_cr("\ttestl\trax, [rip + #offset_to_poll_page]\t" + "# Safepoint: poll for GC"); + st->print("\t"); + } +} +#endif + +void MachEpilogNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const +{ + Compile* C = ra_->C; + int framesize = C->frame_slots() << LogBytesPerInt; + assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); + // Remove word for return adr already pushed + // and RBP + framesize -= 2*wordSize; + + // Note that VerifyStackAtCalls' Majik cookie does not change the frame size popped here + + if (framesize) { + emit_opcode(cbuf, Assembler::REX_W); + if (framesize < 0x80) { + emit_opcode(cbuf, 0x83); // addq rsp, #framesize + emit_rm(cbuf, 0x3, 0x00, RSP_enc); + emit_d8(cbuf, framesize); + } else { + emit_opcode(cbuf, 0x81); // addq rsp, #framesize + emit_rm(cbuf, 0x3, 0x00, RSP_enc); + emit_d32(cbuf, framesize); + } + } + + // popq rbp + emit_opcode(cbuf, 0x58 | RBP_enc); + + if (do_polling() && C->is_method_compilation()) { + // testl %rax, off(%rip) // Opcode + ModRM + Disp32 == 6 bytes + // XXX reg_mem doesn't support RIP-relative addressing yet + cbuf.set_inst_mark(); + cbuf.relocate(cbuf.inst_mark(), relocInfo::poll_return_type, 0); // XXX + emit_opcode(cbuf, 0x85); // testl + emit_rm(cbuf, 0x0, RAX_enc, 0x5); // 00 rax 101 == 0x5 + // cbuf.inst_mark() is beginning of instruction + emit_d32_reloc(cbuf, os::get_polling_page()); +// relocInfo::poll_return_type, + } +} + +uint MachEpilogNode::size(PhaseRegAlloc* ra_) const +{ + Compile* C = ra_->C; + int framesize = C->frame_slots() << LogBytesPerInt; + assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); + // Remove word for return adr already pushed + // and RBP + framesize -= 2*wordSize; + + uint size = 0; + + if (do_polling() && C->is_method_compilation()) { + size += 6; + } + + // count popq rbp + size++; + + if (framesize) { + if (framesize < 0x80) { + size += 4; + } else if (framesize) { + size += 7; + } + } + + return size; +} + +int MachEpilogNode::reloc() const +{ + return 2; // a large enough number +} + +const Pipeline* MachEpilogNode::pipeline() const +{ + return MachNode::pipeline_class(); +} + +int MachEpilogNode::safepoint_offset() const +{ + return 0; +} + +//============================================================================= + +enum RC { + rc_bad, + rc_int, + rc_float, + rc_stack +}; + +static enum RC rc_class(OptoReg::Name reg) +{ + if( !OptoReg::is_valid(reg) ) return rc_bad; + + if (OptoReg::is_stack(reg)) return rc_stack; + + VMReg r = OptoReg::as_VMReg(reg); + + if (r->is_Register()) return rc_int; + + assert(r->is_XMMRegister(), "must be"); + return rc_float; +} + +uint MachSpillCopyNode::implementation(CodeBuffer* cbuf, + PhaseRegAlloc* ra_, + bool do_size, + outputStream* st) const +{ + + // Get registers to move + OptoReg::Name src_second = ra_->get_reg_second(in(1)); + OptoReg::Name src_first = ra_->get_reg_first(in(1)); + OptoReg::Name dst_second = ra_->get_reg_second(this); + OptoReg::Name dst_first = ra_->get_reg_first(this); + + enum RC src_second_rc = rc_class(src_second); + enum RC src_first_rc = rc_class(src_first); + enum RC dst_second_rc = rc_class(dst_second); + enum RC dst_first_rc = rc_class(dst_first); + + assert(OptoReg::is_valid(src_first) && OptoReg::is_valid(dst_first), + "must move at least 1 register" ); + + if (src_first == dst_first && src_second == dst_second) { + // Self copy, no move + return 0; + } else if (src_first_rc == rc_stack) { + // mem -> + if (dst_first_rc == rc_stack) { + // mem -> mem + assert(src_second != dst_first, "overlap"); + if ((src_first & 1) == 0 && src_first + 1 == src_second && + (dst_first & 1) == 0 && dst_first + 1 == dst_second) { + // 64-bit + int src_offset = ra_->reg2offset(src_first); + int dst_offset = ra_->reg2offset(dst_first); + if (cbuf) { + emit_opcode(*cbuf, 0xFF); + encode_RegMem(*cbuf, RSI_enc, RSP_enc, 0x4, 0, src_offset, false); + + emit_opcode(*cbuf, 0x8F); + encode_RegMem(*cbuf, RAX_enc, RSP_enc, 0x4, 0, dst_offset, false); + +#ifndef PRODUCT + } else if (!do_size) { + st->print("pushq [rsp + #%d]\t# 64-bit mem-mem spill\n\t" + "popq [rsp + #%d]", + src_offset, + dst_offset); +#endif + } + return + 3 + ((src_offset == 0) ? 0 : (src_offset < 0x80 ? 1 : 4)) + + 3 + ((dst_offset == 0) ? 0 : (dst_offset < 0x80 ? 1 : 4)); + } else { + // 32-bit + assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); + assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); + // No pushl/popl, so: + int src_offset = ra_->reg2offset(src_first); + int dst_offset = ra_->reg2offset(dst_first); + if (cbuf) { + emit_opcode(*cbuf, Assembler::REX_W); + emit_opcode(*cbuf, 0x89); + emit_opcode(*cbuf, 0x44); + emit_opcode(*cbuf, 0x24); + emit_opcode(*cbuf, 0xF8); + + emit_opcode(*cbuf, 0x8B); + encode_RegMem(*cbuf, + RAX_enc, + RSP_enc, 0x4, 0, src_offset, + false); + + emit_opcode(*cbuf, 0x89); + encode_RegMem(*cbuf, + RAX_enc, + RSP_enc, 0x4, 0, dst_offset, + false); + + emit_opcode(*cbuf, Assembler::REX_W); + emit_opcode(*cbuf, 0x8B); + emit_opcode(*cbuf, 0x44); + emit_opcode(*cbuf, 0x24); + emit_opcode(*cbuf, 0xF8); + +#ifndef PRODUCT + } else if (!do_size) { + st->print("movq [rsp - #8], rax\t# 32-bit mem-mem spill\n\t" + "movl rax, [rsp + #%d]\n\t" + "movl [rsp + #%d], rax\n\t" + "movq rax, [rsp - #8]", + src_offset, + dst_offset); +#endif + } + return + 5 + // movq + 3 + ((src_offset == 0) ? 0 : (src_offset < 0x80 ? 1 : 4)) + // movl + 3 + ((dst_offset == 0) ? 0 : (dst_offset < 0x80 ? 1 : 4)) + // movl + 5; // movq + } + } else if (dst_first_rc == rc_int) { + // mem -> gpr + if ((src_first & 1) == 0 && src_first + 1 == src_second && + (dst_first & 1) == 0 && dst_first + 1 == dst_second) { + // 64-bit + int offset = ra_->reg2offset(src_first); + if (cbuf) { + if (Matcher::_regEncode[dst_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_W); + } else { + emit_opcode(*cbuf, Assembler::REX_WR); + } + emit_opcode(*cbuf, 0x8B); + encode_RegMem(*cbuf, + Matcher::_regEncode[dst_first], + RSP_enc, 0x4, 0, offset, + false); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movq %s, [rsp + #%d]\t# spill", + Matcher::regName[dst_first], + offset); +#endif + } + return + ((offset == 0) ? 0 : (offset < 0x80 ? 1 : 4)) + 4; // REX + } else { + // 32-bit + assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); + assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); + int offset = ra_->reg2offset(src_first); + if (cbuf) { + if (Matcher::_regEncode[dst_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_R); + } + emit_opcode(*cbuf, 0x8B); + encode_RegMem(*cbuf, + Matcher::_regEncode[dst_first], + RSP_enc, 0x4, 0, offset, + false); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movl %s, [rsp + #%d]\t# spill", + Matcher::regName[dst_first], + offset); +#endif + } + return + ((offset == 0) ? 0 : (offset < 0x80 ? 1 : 4)) + + ((Matcher::_regEncode[dst_first] < 8) + ? 3 + : 4); // REX + } + } else if (dst_first_rc == rc_float) { + // mem-> xmm + if ((src_first & 1) == 0 && src_first + 1 == src_second && + (dst_first & 1) == 0 && dst_first + 1 == dst_second) { + // 64-bit + int offset = ra_->reg2offset(src_first); + if (cbuf) { + emit_opcode(*cbuf, UseXmmLoadAndClearUpper ? 0xF2 : 0x66); + if (Matcher::_regEncode[dst_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_R); + } + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, UseXmmLoadAndClearUpper ? 0x10 : 0x12); + encode_RegMem(*cbuf, + Matcher::_regEncode[dst_first], + RSP_enc, 0x4, 0, offset, + false); +#ifndef PRODUCT + } else if (!do_size) { + st->print("%s %s, [rsp + #%d]\t# spill", + UseXmmLoadAndClearUpper ? "movsd " : "movlpd", + Matcher::regName[dst_first], + offset); +#endif + } + return + ((offset == 0) ? 0 : (offset < 0x80 ? 1 : 4)) + + ((Matcher::_regEncode[dst_first] < 8) + ? 5 + : 6); // REX + } else { + // 32-bit + assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); + assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); + int offset = ra_->reg2offset(src_first); + if (cbuf) { + emit_opcode(*cbuf, 0xF3); + if (Matcher::_regEncode[dst_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_R); + } + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, 0x10); + encode_RegMem(*cbuf, + Matcher::_regEncode[dst_first], + RSP_enc, 0x4, 0, offset, + false); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movss %s, [rsp + #%d]\t# spill", + Matcher::regName[dst_first], + offset); +#endif + } + return + ((offset == 0) ? 0 : (offset < 0x80 ? 1 : 4)) + + ((Matcher::_regEncode[dst_first] < 8) + ? 5 + : 6); // REX + } + } + } else if (src_first_rc == rc_int) { + // gpr -> + if (dst_first_rc == rc_stack) { + // gpr -> mem + if ((src_first & 1) == 0 && src_first + 1 == src_second && + (dst_first & 1) == 0 && dst_first + 1 == dst_second) { + // 64-bit + int offset = ra_->reg2offset(dst_first); + if (cbuf) { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_W); + } else { + emit_opcode(*cbuf, Assembler::REX_WR); + } + emit_opcode(*cbuf, 0x89); + encode_RegMem(*cbuf, + Matcher::_regEncode[src_first], + RSP_enc, 0x4, 0, offset, + false); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movq [rsp + #%d], %s\t# spill", + offset, + Matcher::regName[src_first]); +#endif + } + return ((offset == 0) ? 0 : (offset < 0x80 ? 1 : 4)) + 4; // REX + } else { + // 32-bit + assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); + assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); + int offset = ra_->reg2offset(dst_first); + if (cbuf) { + if (Matcher::_regEncode[src_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_R); + } + emit_opcode(*cbuf, 0x89); + encode_RegMem(*cbuf, + Matcher::_regEncode[src_first], + RSP_enc, 0x4, 0, offset, + false); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movl [rsp + #%d], %s\t# spill", + offset, + Matcher::regName[src_first]); +#endif + } + return + ((offset == 0) ? 0 : (offset < 0x80 ? 1 : 4)) + + ((Matcher::_regEncode[src_first] < 8) + ? 3 + : 4); // REX + } + } else if (dst_first_rc == rc_int) { + // gpr -> gpr + if ((src_first & 1) == 0 && src_first + 1 == src_second && + (dst_first & 1) == 0 && dst_first + 1 == dst_second) { + // 64-bit + if (cbuf) { + if (Matcher::_regEncode[dst_first] < 8) { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_W); + } else { + emit_opcode(*cbuf, Assembler::REX_WB); + } + } else { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_WR); + } else { + emit_opcode(*cbuf, Assembler::REX_WRB); + } + } + emit_opcode(*cbuf, 0x8B); + emit_rm(*cbuf, 0x3, + Matcher::_regEncode[dst_first] & 7, + Matcher::_regEncode[src_first] & 7); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movq %s, %s\t# spill", + Matcher::regName[dst_first], + Matcher::regName[src_first]); +#endif + } + return 3; // REX + } else { + // 32-bit + assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); + assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); + if (cbuf) { + if (Matcher::_regEncode[dst_first] < 8) { + if (Matcher::_regEncode[src_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_B); + } + } else { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_R); + } else { + emit_opcode(*cbuf, Assembler::REX_RB); + } + } + emit_opcode(*cbuf, 0x8B); + emit_rm(*cbuf, 0x3, + Matcher::_regEncode[dst_first] & 7, + Matcher::_regEncode[src_first] & 7); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movl %s, %s\t# spill", + Matcher::regName[dst_first], + Matcher::regName[src_first]); +#endif + } + return + (Matcher::_regEncode[src_first] < 8 && Matcher::_regEncode[dst_first] < 8) + ? 2 + : 3; // REX + } + } else if (dst_first_rc == rc_float) { + // gpr -> xmm + if ((src_first & 1) == 0 && src_first + 1 == src_second && + (dst_first & 1) == 0 && dst_first + 1 == dst_second) { + // 64-bit + if (cbuf) { + emit_opcode(*cbuf, 0x66); + if (Matcher::_regEncode[dst_first] < 8) { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_W); + } else { + emit_opcode(*cbuf, Assembler::REX_WB); + } + } else { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_WR); + } else { + emit_opcode(*cbuf, Assembler::REX_WRB); + } + } + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, 0x6E); + emit_rm(*cbuf, 0x3, + Matcher::_regEncode[dst_first] & 7, + Matcher::_regEncode[src_first] & 7); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movdq %s, %s\t# spill", + Matcher::regName[dst_first], + Matcher::regName[src_first]); +#endif + } + return 5; // REX + } else { + // 32-bit + assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); + assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); + if (cbuf) { + emit_opcode(*cbuf, 0x66); + if (Matcher::_regEncode[dst_first] < 8) { + if (Matcher::_regEncode[src_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_B); + } + } else { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_R); + } else { + emit_opcode(*cbuf, Assembler::REX_RB); + } + } + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, 0x6E); + emit_rm(*cbuf, 0x3, + Matcher::_regEncode[dst_first] & 7, + Matcher::_regEncode[src_first] & 7); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movdl %s, %s\t# spill", + Matcher::regName[dst_first], + Matcher::regName[src_first]); +#endif + } + return + (Matcher::_regEncode[src_first] < 8 && Matcher::_regEncode[dst_first] < 8) + ? 4 + : 5; // REX + } + } + } else if (src_first_rc == rc_float) { + // xmm -> + if (dst_first_rc == rc_stack) { + // xmm -> mem + if ((src_first & 1) == 0 && src_first + 1 == src_second && + (dst_first & 1) == 0 && dst_first + 1 == dst_second) { + // 64-bit + int offset = ra_->reg2offset(dst_first); + if (cbuf) { + emit_opcode(*cbuf, 0xF2); + if (Matcher::_regEncode[src_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_R); + } + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, 0x11); + encode_RegMem(*cbuf, + Matcher::_regEncode[src_first], + RSP_enc, 0x4, 0, offset, + false); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movsd [rsp + #%d], %s\t# spill", + offset, + Matcher::regName[src_first]); +#endif + } + return + ((offset == 0) ? 0 : (offset < 0x80 ? 1 : 4)) + + ((Matcher::_regEncode[src_first] < 8) + ? 5 + : 6); // REX + } else { + // 32-bit + assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); + assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); + int offset = ra_->reg2offset(dst_first); + if (cbuf) { + emit_opcode(*cbuf, 0xF3); + if (Matcher::_regEncode[src_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_R); + } + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, 0x11); + encode_RegMem(*cbuf, + Matcher::_regEncode[src_first], + RSP_enc, 0x4, 0, offset, + false); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movss [rsp + #%d], %s\t# spill", + offset, + Matcher::regName[src_first]); +#endif + } + return + ((offset == 0) ? 0 : (offset < 0x80 ? 1 : 4)) + + ((Matcher::_regEncode[src_first] < 8) + ? 5 + : 6); // REX + } + } else if (dst_first_rc == rc_int) { + // xmm -> gpr + if ((src_first & 1) == 0 && src_first + 1 == src_second && + (dst_first & 1) == 0 && dst_first + 1 == dst_second) { + // 64-bit + if (cbuf) { + emit_opcode(*cbuf, 0x66); + if (Matcher::_regEncode[dst_first] < 8) { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_W); + } else { + emit_opcode(*cbuf, Assembler::REX_WR); // attention! + } + } else { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_WB); // attention! + } else { + emit_opcode(*cbuf, Assembler::REX_WRB); + } + } + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, 0x7E); + emit_rm(*cbuf, 0x3, + Matcher::_regEncode[dst_first] & 7, + Matcher::_regEncode[src_first] & 7); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movdq %s, %s\t# spill", + Matcher::regName[dst_first], + Matcher::regName[src_first]); +#endif + } + return 5; // REX + } else { + // 32-bit + assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); + assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); + if (cbuf) { + emit_opcode(*cbuf, 0x66); + if (Matcher::_regEncode[dst_first] < 8) { + if (Matcher::_regEncode[src_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_R); // attention! + } + } else { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_B); // attention! + } else { + emit_opcode(*cbuf, Assembler::REX_RB); + } + } + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, 0x7E); + emit_rm(*cbuf, 0x3, + Matcher::_regEncode[dst_first] & 7, + Matcher::_regEncode[src_first] & 7); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movdl %s, %s\t# spill", + Matcher::regName[dst_first], + Matcher::regName[src_first]); +#endif + } + return + (Matcher::_regEncode[src_first] < 8 && Matcher::_regEncode[dst_first] < 8) + ? 4 + : 5; // REX + } + } else if (dst_first_rc == rc_float) { + // xmm -> xmm + if ((src_first & 1) == 0 && src_first + 1 == src_second && + (dst_first & 1) == 0 && dst_first + 1 == dst_second) { + // 64-bit + if (cbuf) { + emit_opcode(*cbuf, UseXmmRegToRegMoveAll ? 0x66 : 0xF2); + if (Matcher::_regEncode[dst_first] < 8) { + if (Matcher::_regEncode[src_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_B); + } + } else { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_R); + } else { + emit_opcode(*cbuf, Assembler::REX_RB); + } + } + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, UseXmmRegToRegMoveAll ? 0x28 : 0x10); + emit_rm(*cbuf, 0x3, + Matcher::_regEncode[dst_first] & 7, + Matcher::_regEncode[src_first] & 7); +#ifndef PRODUCT + } else if (!do_size) { + st->print("%s %s, %s\t# spill", + UseXmmRegToRegMoveAll ? "movapd" : "movsd ", + Matcher::regName[dst_first], + Matcher::regName[src_first]); +#endif + } + return + (Matcher::_regEncode[src_first] < 8 && Matcher::_regEncode[dst_first] < 8) + ? 4 + : 5; // REX + } else { + // 32-bit + assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); + assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); + if (cbuf) { + if (!UseXmmRegToRegMoveAll) + emit_opcode(*cbuf, 0xF3); + if (Matcher::_regEncode[dst_first] < 8) { + if (Matcher::_regEncode[src_first] >= 8) { + emit_opcode(*cbuf, Assembler::REX_B); + } + } else { + if (Matcher::_regEncode[src_first] < 8) { + emit_opcode(*cbuf, Assembler::REX_R); + } else { + emit_opcode(*cbuf, Assembler::REX_RB); + } + } + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, UseXmmRegToRegMoveAll ? 0x28 : 0x10); + emit_rm(*cbuf, 0x3, + Matcher::_regEncode[dst_first] & 7, + Matcher::_regEncode[src_first] & 7); +#ifndef PRODUCT + } else if (!do_size) { + st->print("%s %s, %s\t# spill", + UseXmmRegToRegMoveAll ? "movaps" : "movss ", + Matcher::regName[dst_first], + Matcher::regName[src_first]); +#endif + } + return + (Matcher::_regEncode[src_first] < 8 && Matcher::_regEncode[dst_first] < 8) + ? (UseXmmRegToRegMoveAll ? 3 : 4) + : (UseXmmRegToRegMoveAll ? 4 : 5); // REX + } + } + } + + assert(0," foo "); + Unimplemented(); + + return 0; +} + +#ifndef PRODUCT +void MachSpillCopyNode::format(PhaseRegAlloc *ra_, outputStream* st) const +{ + implementation(NULL, ra_, false, st); +} +#endif + +void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const +{ + implementation(&cbuf, ra_, false, NULL); +} + +uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const +{ + return implementation(NULL, ra_, true, NULL); +} + +//============================================================================= +#ifndef PRODUCT +void MachNopNode::format(PhaseRegAlloc*, outputStream* st) const +{ + st->print("nop \t# %d bytes pad for loops and calls", _count); +} +#endif + +void MachNopNode::emit(CodeBuffer &cbuf, PhaseRegAlloc*) const +{ + MacroAssembler _masm(&cbuf); + __ nop(_count); +} + +uint MachNopNode::size(PhaseRegAlloc*) const +{ + return _count; +} + + +//============================================================================= +#ifndef PRODUCT +void BoxLockNode::format(PhaseRegAlloc* ra_, outputStream* st) const +{ + int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); + int reg = ra_->get_reg_first(this); + st->print("leaq %s, [rsp + #%d]\t# box lock", + Matcher::regName[reg], offset); +} +#endif + +void BoxLockNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const +{ + int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); + int reg = ra_->get_encode(this); + if (offset >= 0x80) { + emit_opcode(cbuf, reg < 8 ? Assembler::REX_W : Assembler::REX_WR); + emit_opcode(cbuf, 0x8D); // LEA reg,[SP+offset] + emit_rm(cbuf, 0x2, reg & 7, 0x04); + emit_rm(cbuf, 0x0, 0x04, RSP_enc); + emit_d32(cbuf, offset); + } else { + emit_opcode(cbuf, reg < 8 ? Assembler::REX_W : Assembler::REX_WR); + emit_opcode(cbuf, 0x8D); // LEA reg,[SP+offset] + emit_rm(cbuf, 0x1, reg & 7, 0x04); + emit_rm(cbuf, 0x0, 0x04, RSP_enc); + emit_d8(cbuf, offset); + } +} + +uint BoxLockNode::size(PhaseRegAlloc *ra_) const +{ + int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); + return (offset < 0x80) ? 5 : 8; // REX +} + +//============================================================================= + +// emit call stub, compiled java to interpreter +void emit_java_to_interp(CodeBuffer& cbuf) +{ + // Stub is fixed up when the corresponding call is converted from + // calling compiled code to calling interpreted code. + // movq rbx, 0 + // jmp -5 # to self + + address mark = cbuf.inst_mark(); // get mark within main instrs section + + // Note that the code buffer's inst_mark is always relative to insts. + // That's why we must use the macroassembler to generate a stub. + MacroAssembler _masm(&cbuf); + + address base = + __ start_a_stub(Compile::MAX_stubs_size); + if (base == NULL) return; // CodeBuffer::expand failed + // static stub relocation stores the instruction address of the call + __ relocate(static_stub_Relocation::spec(mark), RELOC_IMM64); + // static stub relocation also tags the methodOop in the code-stream. + __ movoop(rbx, (jobject) NULL); // method is zapped till fixup time + __ jump(RuntimeAddress(__ pc())); + + // Update current stubs pointer and restore code_end. + __ end_a_stub(); +} + +// size of call stub, compiled java to interpretor +uint size_java_to_interp() +{ + return 15; // movq (1+1+8); jmp (1+4) +} + +// relocation entries for call stub, compiled java to interpretor +uint reloc_java_to_interp() +{ + return 4; // 3 in emit_java_to_interp + 1 in Java_Static_Call +} + +//============================================================================= +#ifndef PRODUCT +void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const +{ + st->print_cr("cmpq rax, [j_rarg0 + oopDesc::klass_offset_in_bytes() #%d]\t" + "# Inline cache check", oopDesc::klass_offset_in_bytes()); + st->print_cr("\tjne SharedRuntime::_ic_miss_stub"); + st->print_cr("\tnop"); + if (!OptoBreakpoint) { + st->print_cr("\tnop"); + } +} +#endif + +void MachUEPNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const +{ + MacroAssembler masm(&cbuf); +#ifdef ASSERT + uint code_size = cbuf.code_size(); +#endif + masm.cmpq(rax, Address(j_rarg0, oopDesc::klass_offset_in_bytes())); + + masm.jump_cc(Assembler::notEqual, RuntimeAddress(SharedRuntime::get_ic_miss_stub())); + + /* WARNING these NOPs are critical so that verified entry point is properly + aligned for patching by NativeJump::patch_verified_entry() */ + int nops_cnt = 1; + if (!OptoBreakpoint) { + // Leave space for int3 + nops_cnt += 1; + } + masm.nop(nops_cnt); + + assert(cbuf.code_size() - code_size == size(ra_), + "checking code size of inline cache node"); +} + +uint MachUEPNode::size(PhaseRegAlloc* ra_) const +{ + return OptoBreakpoint ? 11 : 12; +} + + +//============================================================================= +uint size_exception_handler() +{ + // NativeCall instruction size is the same as NativeJump. + // Note that this value is also credited (in output.cpp) to + // the size of the code section. + return NativeJump::instruction_size; +} + +// Emit exception handler code. +int emit_exception_handler(CodeBuffer& cbuf) +{ + + // Note that the code buffer's inst_mark is always relative to insts. + // That's why we must use the macroassembler to generate a handler. + MacroAssembler _masm(&cbuf); + address base = + __ start_a_stub(size_exception_handler()); + if (base == NULL) return 0; // CodeBuffer::expand failed + int offset = __ offset(); + __ jump(RuntimeAddress(OptoRuntime::exception_blob()->instructions_begin())); + assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); + __ end_a_stub(); + return offset; +} + +uint size_deopt_handler() +{ + // three 5 byte instructions + return 15; +} + +// Emit deopt handler code. +int emit_deopt_handler(CodeBuffer& cbuf) +{ + + // Note that the code buffer's inst_mark is always relative to insts. + // That's why we must use the macroassembler to generate a handler. + MacroAssembler _masm(&cbuf); + address base = + __ start_a_stub(size_deopt_handler()); + if (base == NULL) return 0; // CodeBuffer::expand failed + int offset = __ offset(); + address the_pc = (address) __ pc(); + Label next; + // push a "the_pc" on the stack without destroying any registers + // as they all may be live. + + // push address of "next" + __ call(next, relocInfo::none); // reloc none is fine since it is a disp32 + __ bind(next); + // adjust it so it matches "the_pc" + __ subq(Address(rsp, 0), __ offset() - offset); + __ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); + __ end_a_stub(); + return offset; +} + +static void emit_double_constant(CodeBuffer& cbuf, double x) { + int mark = cbuf.insts()->mark_off(); + MacroAssembler _masm(&cbuf); + address double_address = __ double_constant(x); + cbuf.insts()->set_mark_off(mark); // preserve mark across masm shift + emit_d32_reloc(cbuf, + (int) (double_address - cbuf.code_end() - 4), + internal_word_Relocation::spec(double_address), + RELOC_DISP32); +} + +static void emit_float_constant(CodeBuffer& cbuf, float x) { + int mark = cbuf.insts()->mark_off(); + MacroAssembler _masm(&cbuf); + address float_address = __ float_constant(x); + cbuf.insts()->set_mark_off(mark); // preserve mark across masm shift + emit_d32_reloc(cbuf, + (int) (float_address - cbuf.code_end() - 4), + internal_word_Relocation::spec(float_address), + RELOC_DISP32); +} + + +int Matcher::regnum_to_fpu_offset(int regnum) +{ + return regnum - 32; // The FP registers are in the second chunk +} + +// This is UltraSparc specific, true just means we have fast l2f conversion +const bool Matcher::convL2FSupported(void) { + return true; +} + +// Vector width in bytes +const uint Matcher::vector_width_in_bytes(void) { + return 8; +} + +// Vector ideal reg +const uint Matcher::vector_ideal_reg(void) { + return Op_RegD; +} + +// Is this branch offset short enough that a short branch can be used? +// +// NOTE: If the platform does not provide any short branch variants, then +// this method should return false for offset 0. +bool Matcher::is_short_branch_offset(int offset) +{ + return -0x80 <= offset && offset < 0x80; +} + +const bool Matcher::isSimpleConstant64(jlong value) { + // Will one (StoreL ConL) be cheaper than two (StoreI ConI)?. + //return value == (int) value; // Cf. storeImmL and immL32. + + // Probably always true, even if a temp register is required. + return true; +} + +// The ecx parameter to rep stosq for the ClearArray node is in words. +const bool Matcher::init_array_count_is_in_bytes = false; + +// Threshold size for cleararray. +const int Matcher::init_array_short_size = 8 * BytesPerLong; + +// Should the Matcher clone shifts on addressing modes, expecting them +// to be subsumed into complex addressing expressions or compute them +// into registers? True for Intel but false for most RISCs +const bool Matcher::clone_shift_expressions = true; + +// Is it better to copy float constants, or load them directly from +// memory? Intel can load a float constant from a direct address, +// requiring no extra registers. Most RISCs will have to materialize +// an address into a register first, so they would do better to copy +// the constant from stack. +const bool Matcher::rematerialize_float_constants = true; // XXX + +// If CPU can load and store mis-aligned doubles directly then no +// fixup is needed. Else we split the double into 2 integer pieces +// and move it piece-by-piece. Only happens when passing doubles into +// C code as the Java calling convention forces doubles to be aligned. +const bool Matcher::misaligned_doubles_ok = true; + +// No-op on amd64 +void Matcher::pd_implicit_null_fixup(MachNode *node, uint idx) {} + +// Advertise here if the CPU requires explicit rounding operations to +// implement the UseStrictFP mode. +const bool Matcher::strict_fp_requires_explicit_rounding = true; + +// Do floats take an entire double register or just half? +const bool Matcher::float_in_double = true; +// Do ints take an entire long register or just half? +const bool Matcher::int_in_long = true; + +// Return whether or not this register is ever used as an argument. +// This function is used on startup to build the trampoline stubs in +// generateOptoStub. Registers not mentioned will be killed by the VM +// call in the trampoline, and arguments in those registers not be +// available to the callee. +bool Matcher::can_be_java_arg(int reg) +{ + return + reg == RDI_num || reg == RDI_H_num || + reg == RSI_num || reg == RSI_H_num || + reg == RDX_num || reg == RDX_H_num || + reg == RCX_num || reg == RCX_H_num || + reg == R8_num || reg == R8_H_num || + reg == R9_num || reg == R9_H_num || + reg == XMM0_num || reg == XMM0_H_num || + reg == XMM1_num || reg == XMM1_H_num || + reg == XMM2_num || reg == XMM2_H_num || + reg == XMM3_num || reg == XMM3_H_num || + reg == XMM4_num || reg == XMM4_H_num || + reg == XMM5_num || reg == XMM5_H_num || + reg == XMM6_num || reg == XMM6_H_num || + reg == XMM7_num || reg == XMM7_H_num; +} + +bool Matcher::is_spillable_arg(int reg) +{ + return can_be_java_arg(reg); +} + +// Register for DIVI projection of divmodI +RegMask Matcher::divI_proj_mask() { + return INT_RAX_REG_mask; +} + +// Register for MODI projection of divmodI +RegMask Matcher::modI_proj_mask() { + return INT_RDX_REG_mask; +} + +// Register for DIVL projection of divmodL +RegMask Matcher::divL_proj_mask() { + return LONG_RAX_REG_mask; +} + +// Register for MODL projection of divmodL +RegMask Matcher::modL_proj_mask() { + return LONG_RDX_REG_mask; +} + +%} + +//----------ENCODING BLOCK----------------------------------------------------- +// This block specifies the encoding classes used by the compiler to +// output byte streams. Encoding classes are parameterized macros +// used by Machine Instruction Nodes in order to generate the bit +// encoding of the instruction. Operands specify their base encoding +// interface with the interface keyword. There are currently +// supported four interfaces, REG_INTER, CONST_INTER, MEMORY_INTER, & +// COND_INTER. REG_INTER causes an operand to generate a function +// which returns its register number when queried. CONST_INTER causes +// an operand to generate a function which returns the value of the +// constant when queried. MEMORY_INTER causes an operand to generate +// four functions which return the Base Register, the Index Register, +// the Scale Value, and the Offset Value of the operand when queried. +// COND_INTER causes an operand to generate six functions which return +// the encoding code (ie - encoding bits for the instruction) +// associated with each basic boolean condition for a conditional +// instruction. +// +// Instructions specify two basic values for encoding. Again, a +// function is available to check if the constant displacement is an +// oop. They use the ins_encode keyword to specify their encoding +// classes (which must be a sequence of enc_class names, and their +// parameters, specified in the encoding block), and they use the +// opcode keyword to specify, in order, their primary, secondary, and +// tertiary opcode. Only the opcode sections which a particular +// instruction needs for encoding need to be specified. +encode %{ + // Build emit functions for each basic byte or larger field in the + // intel encoding scheme (opcode, rm, sib, immediate), and call them + // from C++ code in the enc_class source block. Emit functions will + // live in the main source block for now. In future, we can + // generalize this by adding a syntax that specifies the sizes of + // fields in an order, so that the adlc can build the emit functions + // automagically + + // Emit primary opcode + enc_class OpcP + %{ + emit_opcode(cbuf, $primary); + %} + + // Emit secondary opcode + enc_class OpcS + %{ + emit_opcode(cbuf, $secondary); + %} + + // Emit tertiary opcode + enc_class OpcT + %{ + emit_opcode(cbuf, $tertiary); + %} + + // Emit opcode directly + enc_class Opcode(immI d8) + %{ + emit_opcode(cbuf, $d8$$constant); + %} + + // Emit size prefix + enc_class SizePrefix + %{ + emit_opcode(cbuf, 0x66); + %} + + enc_class reg(rRegI reg) + %{ + emit_rm(cbuf, 0x3, 0, $reg$$reg & 7); + %} + + enc_class reg_reg(rRegI dst, rRegI src) + %{ + emit_rm(cbuf, 0x3, $dst$$reg & 7, $src$$reg & 7); + %} + + enc_class opc_reg_reg(immI opcode, rRegI dst, rRegI src) + %{ + emit_opcode(cbuf, $opcode$$constant); + emit_rm(cbuf, 0x3, $dst$$reg & 7, $src$$reg & 7); + %} + + enc_class cmpfp_fixup() + %{ + // jnp,s exit + emit_opcode(cbuf, 0x7B); + emit_d8(cbuf, 0x0A); + + // pushfq + emit_opcode(cbuf, 0x9C); + + // andq $0xffffff2b, (%rsp) + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x81); + emit_opcode(cbuf, 0x24); + emit_opcode(cbuf, 0x24); + emit_d32(cbuf, 0xffffff2b); + + // popfq + emit_opcode(cbuf, 0x9D); + + // nop (target for branch to avoid branch to branch) + emit_opcode(cbuf, 0x90); + %} + + enc_class cmpfp3(rRegI dst) + %{ + int dstenc = $dst$$reg; + + // movl $dst, -1 + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + emit_opcode(cbuf, 0xB8 | (dstenc & 7)); + emit_d32(cbuf, -1); + + // jp,s done + emit_opcode(cbuf, 0x7A); + emit_d8(cbuf, dstenc < 4 ? 0x08 : 0x0A); + + // jb,s done + emit_opcode(cbuf, 0x72); + emit_d8(cbuf, dstenc < 4 ? 0x06 : 0x08); + + // setne $dst + if (dstenc >= 4) { + emit_opcode(cbuf, dstenc < 8 ? Assembler::REX : Assembler::REX_B); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x95); + emit_opcode(cbuf, 0xC0 | (dstenc & 7)); + + // movzbl $dst, $dst + if (dstenc >= 4) { + emit_opcode(cbuf, dstenc < 8 ? Assembler::REX : Assembler::REX_RB); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0xB6); + emit_rm(cbuf, 0x3, dstenc & 7, dstenc & 7); + %} + + enc_class cdql_enc(no_rax_rdx_RegI div) + %{ + // Full implementation of Java idiv and irem; checks for + // special case as described in JVM spec., p.243 & p.271. + // + // normal case special case + // + // input : rax: dividend min_int + // reg: divisor -1 + // + // output: rax: quotient (= rax idiv reg) min_int + // rdx: remainder (= rax irem reg) 0 + // + // Code sequnce: + // + // 0: 3d 00 00 00 80 cmp $0x80000000,%eax + // 5: 75 07/08 jne e + // 7: 33 d2 xor %edx,%edx + // [div >= 8 -> offset + 1] + // [REX_B] + // 9: 83 f9 ff cmp $0xffffffffffffffff,$div + // c: 74 03/04 je 11 + // 000000000000000e : + // e: 99 cltd + // [div >= 8 -> offset + 1] + // [REX_B] + // f: f7 f9 idiv $div + // 0000000000000011 : + + // cmp $0x80000000,%eax + emit_opcode(cbuf, 0x3d); + emit_d8(cbuf, 0x00); + emit_d8(cbuf, 0x00); + emit_d8(cbuf, 0x00); + emit_d8(cbuf, 0x80); + + // jne e + emit_opcode(cbuf, 0x75); + emit_d8(cbuf, $div$$reg < 8 ? 0x07 : 0x08); + + // xor %edx,%edx + emit_opcode(cbuf, 0x33); + emit_d8(cbuf, 0xD2); + + // cmp $0xffffffffffffffff,%ecx + if ($div$$reg >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + emit_opcode(cbuf, 0x83); + emit_rm(cbuf, 0x3, 0x7, $div$$reg & 7); + emit_d8(cbuf, 0xFF); + + // je 11 + emit_opcode(cbuf, 0x74); + emit_d8(cbuf, $div$$reg < 8 ? 0x03 : 0x04); + + // + // cltd + emit_opcode(cbuf, 0x99); + + // idivl (note: must be emitted by the user of this rule) + // + %} + + enc_class cdqq_enc(no_rax_rdx_RegL div) + %{ + // Full implementation of Java ldiv and lrem; checks for + // special case as described in JVM spec., p.243 & p.271. + // + // normal case special case + // + // input : rax: dividend min_long + // reg: divisor -1 + // + // output: rax: quotient (= rax idiv reg) min_long + // rdx: remainder (= rax irem reg) 0 + // + // Code sequnce: + // + // 0: 48 ba 00 00 00 00 00 mov $0x8000000000000000,%rdx + // 7: 00 00 80 + // a: 48 39 d0 cmp %rdx,%rax + // d: 75 08 jne 17 + // f: 33 d2 xor %edx,%edx + // 11: 48 83 f9 ff cmp $0xffffffffffffffff,$div + // 15: 74 05 je 1c + // 0000000000000017 : + // 17: 48 99 cqto + // 19: 48 f7 f9 idiv $div + // 000000000000001c : + + // mov $0x8000000000000000,%rdx + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0xBA); + emit_d8(cbuf, 0x00); + emit_d8(cbuf, 0x00); + emit_d8(cbuf, 0x00); + emit_d8(cbuf, 0x00); + emit_d8(cbuf, 0x00); + emit_d8(cbuf, 0x00); + emit_d8(cbuf, 0x00); + emit_d8(cbuf, 0x80); + + // cmp %rdx,%rax + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x39); + emit_d8(cbuf, 0xD0); + + // jne 17 + emit_opcode(cbuf, 0x75); + emit_d8(cbuf, 0x08); + + // xor %edx,%edx + emit_opcode(cbuf, 0x33); + emit_d8(cbuf, 0xD2); + + // cmp $0xffffffffffffffff,$div + emit_opcode(cbuf, $div$$reg < 8 ? Assembler::REX_W : Assembler::REX_WB); + emit_opcode(cbuf, 0x83); + emit_rm(cbuf, 0x3, 0x7, $div$$reg & 7); + emit_d8(cbuf, 0xFF); + + // je 1e + emit_opcode(cbuf, 0x74); + emit_d8(cbuf, 0x05); + + // + // cqto + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x99); + + // idivq (note: must be emitted by the user of this rule) + // + %} + + // Opcde enc_class for 8/32 bit immediate instructions with sign-extension + enc_class OpcSE(immI imm) + %{ + // Emit primary opcode and set sign-extend bit + // Check for 8-bit immediate, and set sign extend bit in opcode + if (-0x80 <= $imm$$constant && $imm$$constant < 0x80) { + emit_opcode(cbuf, $primary | 0x02); + } else { + // 32-bit immediate + emit_opcode(cbuf, $primary); + } + %} + + enc_class OpcSErm(rRegI dst, immI imm) + %{ + // OpcSEr/m + int dstenc = $dst$$reg; + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + dstenc -= 8; + } + // Emit primary opcode and set sign-extend bit + // Check for 8-bit immediate, and set sign extend bit in opcode + if (-0x80 <= $imm$$constant && $imm$$constant < 0x80) { + emit_opcode(cbuf, $primary | 0x02); + } else { + // 32-bit immediate + emit_opcode(cbuf, $primary); + } + // Emit r/m byte with secondary opcode, after primary opcode. + emit_rm(cbuf, 0x3, $secondary, dstenc); + %} + + enc_class OpcSErm_wide(rRegL dst, immI imm) + %{ + // OpcSEr/m + int dstenc = $dst$$reg; + if (dstenc < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WB); + dstenc -= 8; + } + // Emit primary opcode and set sign-extend bit + // Check for 8-bit immediate, and set sign extend bit in opcode + if (-0x80 <= $imm$$constant && $imm$$constant < 0x80) { + emit_opcode(cbuf, $primary | 0x02); + } else { + // 32-bit immediate + emit_opcode(cbuf, $primary); + } + // Emit r/m byte with secondary opcode, after primary opcode. + emit_rm(cbuf, 0x3, $secondary, dstenc); + %} + + enc_class Con8or32(immI imm) + %{ + // Check for 8-bit immediate, and set sign extend bit in opcode + if (-0x80 <= $imm$$constant && $imm$$constant < 0x80) { + $$$emit8$imm$$constant; + } else { + // 32-bit immediate + $$$emit32$imm$$constant; + } + %} + + enc_class Lbl(label labl) + %{ + // JMP, CALL + Label* l = $labl$$label; + emit_d32(cbuf, l ? (l->loc_pos() - (cbuf.code_size() + 4)) : 0); + %} + + enc_class LblShort(label labl) + %{ + // JMP, CALL + Label* l = $labl$$label; + int disp = l ? (l->loc_pos() - (cbuf.code_size() + 1)) : 0; + assert(-128 <= disp && disp <= 127, "Displacement too large for short jmp"); + emit_d8(cbuf, disp); + %} + + enc_class opc2_reg(rRegI dst) + %{ + // BSWAP + emit_cc(cbuf, $secondary, $dst$$reg); + %} + + enc_class opc3_reg(rRegI dst) + %{ + // BSWAP + emit_cc(cbuf, $tertiary, $dst$$reg); + %} + + enc_class reg_opc(rRegI div) + %{ + // INC, DEC, IDIV, IMOD, JMP indirect, ... + emit_rm(cbuf, 0x3, $secondary, $div$$reg & 7); + %} + + enc_class Jcc(cmpOp cop, label labl) + %{ + // JCC + Label* l = $labl$$label; + $$$emit8$primary; + emit_cc(cbuf, $secondary, $cop$$cmpcode); + emit_d32(cbuf, l ? (l->loc_pos() - (cbuf.code_size() + 4)) : 0); + %} + + enc_class JccShort (cmpOp cop, label labl) + %{ + // JCC + Label *l = $labl$$label; + emit_cc(cbuf, $primary, $cop$$cmpcode); + int disp = l ? (l->loc_pos() - (cbuf.code_size() + 1)) : 0; + assert(-128 <= disp && disp <= 127, "Displacement too large for short jmp"); + emit_d8(cbuf, disp); + %} + + enc_class enc_cmov(cmpOp cop) + %{ + // CMOV + $$$emit8$primary; + emit_cc(cbuf, $secondary, $cop$$cmpcode); + %} + + enc_class enc_cmovf_branch(cmpOp cop, regF dst, regF src) + %{ + // Invert sense of branch from sense of cmov + emit_cc(cbuf, 0x70, $cop$$cmpcode ^ 1); + emit_d8(cbuf, ($dst$$reg < 8 && $src$$reg < 8) + ? (UseXmmRegToRegMoveAll ? 3 : 4) + : (UseXmmRegToRegMoveAll ? 4 : 5) ); // REX + // UseXmmRegToRegMoveAll ? movaps(dst, src) : movss(dst, src) + if (!UseXmmRegToRegMoveAll) emit_opcode(cbuf, 0xF3); + if ($dst$$reg < 8) { + if ($src$$reg >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + } else { + if ($src$$reg < 8) { + emit_opcode(cbuf, Assembler::REX_R); + } else { + emit_opcode(cbuf, Assembler::REX_RB); + } + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, UseXmmRegToRegMoveAll ? 0x28 : 0x10); + emit_rm(cbuf, 0x3, $dst$$reg & 7, $src$$reg & 7); + %} + + enc_class enc_cmovd_branch(cmpOp cop, regD dst, regD src) + %{ + // Invert sense of branch from sense of cmov + emit_cc(cbuf, 0x70, $cop$$cmpcode ^ 1); + emit_d8(cbuf, $dst$$reg < 8 && $src$$reg < 8 ? 4 : 5); // REX + + // UseXmmRegToRegMoveAll ? movapd(dst, src) : movsd(dst, src) + emit_opcode(cbuf, UseXmmRegToRegMoveAll ? 0x66 : 0xF2); + if ($dst$$reg < 8) { + if ($src$$reg >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + } else { + if ($src$$reg < 8) { + emit_opcode(cbuf, Assembler::REX_R); + } else { + emit_opcode(cbuf, Assembler::REX_RB); + } + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, UseXmmRegToRegMoveAll ? 0x28 : 0x10); + emit_rm(cbuf, 0x3, $dst$$reg & 7, $src$$reg & 7); + %} + + enc_class enc_PartialSubtypeCheck() + %{ + Register Rrdi = as_Register(RDI_enc); // result register + Register Rrax = as_Register(RAX_enc); // super class + Register Rrcx = as_Register(RCX_enc); // killed + Register Rrsi = as_Register(RSI_enc); // sub class + Label hit, miss; + + MacroAssembler _masm(&cbuf); + // Compare super with sub directly, since super is not in its own SSA. + // The compiler used to emit this test, but we fold it in here, + // to allow platform-specific tweaking on sparc. + __ cmpq(Rrax, Rrsi); + __ jcc(Assembler::equal, hit); +#ifndef PRODUCT + __ lea(Rrcx, ExternalAddress((address)&SharedRuntime::_partial_subtype_ctr)); + __ incrementl(Address(Rrcx, 0)); +#endif //PRODUCT + __ movq(Rrdi, Address(Rrsi, + sizeof(oopDesc) + + Klass::secondary_supers_offset_in_bytes())); + __ movl(Rrcx, Address(Rrdi, arrayOopDesc::length_offset_in_bytes())); + __ addq(Rrdi, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); + __ repne_scan(); + __ jcc(Assembler::notEqual, miss); + __ movq(Address(Rrsi, + sizeof(oopDesc) + + Klass::secondary_super_cache_offset_in_bytes()), + Rrax); + __ bind(hit); + if ($primary) { + __ xorq(Rrdi, Rrdi); + } + __ bind(miss); + %} + + enc_class Java_To_Interpreter(method meth) + %{ + // CALL Java_To_Interpreter + // This is the instruction starting address for relocation info. + cbuf.set_inst_mark(); + $$$emit8$primary; + // CALL directly to the runtime + emit_d32_reloc(cbuf, + (int) ($meth$$method - ((intptr_t) cbuf.code_end()) - 4), + runtime_call_Relocation::spec(), + RELOC_DISP32); + %} + + enc_class Java_Static_Call(method meth) + %{ + // JAVA STATIC CALL + // CALL to fixup routine. Fixup routine uses ScopeDesc info to + // determine who we intended to call. + cbuf.set_inst_mark(); + $$$emit8$primary; + + if (!_method) { + emit_d32_reloc(cbuf, + (int) ($meth$$method - ((intptr_t) cbuf.code_end()) - 4), + runtime_call_Relocation::spec(), + RELOC_DISP32); + } else if (_optimized_virtual) { + emit_d32_reloc(cbuf, + (int) ($meth$$method - ((intptr_t) cbuf.code_end()) - 4), + opt_virtual_call_Relocation::spec(), + RELOC_DISP32); + } else { + emit_d32_reloc(cbuf, + (int) ($meth$$method - ((intptr_t) cbuf.code_end()) - 4), + static_call_Relocation::spec(), + RELOC_DISP32); + } + if (_method) { + // Emit stub for static call + emit_java_to_interp(cbuf); + } + %} + + enc_class Java_Dynamic_Call(method meth) + %{ + // JAVA DYNAMIC CALL + // !!!!! + // Generate "movq rax, -1", placeholder instruction to load oop-info + // emit_call_dynamic_prologue( cbuf ); + cbuf.set_inst_mark(); + + // movq rax, -1 + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0xB8 | RAX_enc); + emit_d64_reloc(cbuf, + (int64_t) Universe::non_oop_word(), + oop_Relocation::spec_for_immediate(), RELOC_IMM64); + address virtual_call_oop_addr = cbuf.inst_mark(); + // CALL to fixup routine. Fixup routine uses ScopeDesc info to determine + // who we intended to call. + cbuf.set_inst_mark(); + $$$emit8$primary; + emit_d32_reloc(cbuf, + (int) ($meth$$method - ((intptr_t) cbuf.code_end()) - 4), + virtual_call_Relocation::spec(virtual_call_oop_addr), + RELOC_DISP32); + %} + + enc_class Java_Compiled_Call(method meth) + %{ + // JAVA COMPILED CALL + int disp = in_bytes(methodOopDesc:: from_compiled_offset()); + + // XXX XXX offset is 128 is 1.5 NON-PRODUCT !!! + // assert(-0x80 <= disp && disp < 0x80, "compiled_code_offset isn't small"); + + // callq *disp(%rax) + cbuf.set_inst_mark(); + $$$emit8$primary; + if (disp < 0x80) { + emit_rm(cbuf, 0x01, $secondary, RAX_enc); // R/M byte + emit_d8(cbuf, disp); // Displacement + } else { + emit_rm(cbuf, 0x02, $secondary, RAX_enc); // R/M byte + emit_d32(cbuf, disp); // Displacement + } + %} + + enc_class reg_opc_imm(rRegI dst, immI8 shift) + %{ + // SAL, SAR, SHR + int dstenc = $dst$$reg; + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + dstenc -= 8; + } + $$$emit8$primary; + emit_rm(cbuf, 0x3, $secondary, dstenc); + $$$emit8$shift$$constant; + %} + + enc_class reg_opc_imm_wide(rRegL dst, immI8 shift) + %{ + // SAL, SAR, SHR + int dstenc = $dst$$reg; + if (dstenc < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WB); + dstenc -= 8; + } + $$$emit8$primary; + emit_rm(cbuf, 0x3, $secondary, dstenc); + $$$emit8$shift$$constant; + %} + + enc_class load_immI(rRegI dst, immI src) + %{ + int dstenc = $dst$$reg; + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + dstenc -= 8; + } + emit_opcode(cbuf, 0xB8 | dstenc); + $$$emit32$src$$constant; + %} + + enc_class load_immL(rRegL dst, immL src) + %{ + int dstenc = $dst$$reg; + if (dstenc < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WB); + dstenc -= 8; + } + emit_opcode(cbuf, 0xB8 | dstenc); + emit_d64(cbuf, $src$$constant); + %} + + enc_class load_immUL32(rRegL dst, immUL32 src) + %{ + // same as load_immI, but this time we care about zeroes in the high word + int dstenc = $dst$$reg; + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + dstenc -= 8; + } + emit_opcode(cbuf, 0xB8 | dstenc); + $$$emit32$src$$constant; + %} + + enc_class load_immL32(rRegL dst, immL32 src) + %{ + int dstenc = $dst$$reg; + if (dstenc < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WB); + dstenc -= 8; + } + emit_opcode(cbuf, 0xC7); + emit_rm(cbuf, 0x03, 0x00, dstenc); + $$$emit32$src$$constant; + %} + + enc_class load_immP31(rRegP dst, immP32 src) + %{ + // same as load_immI, but this time we care about zeroes in the high word + int dstenc = $dst$$reg; + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + dstenc -= 8; + } + emit_opcode(cbuf, 0xB8 | dstenc); + $$$emit32$src$$constant; + %} + + enc_class load_immP(rRegP dst, immP src) + %{ + int dstenc = $dst$$reg; + if (dstenc < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WB); + dstenc -= 8; + } + emit_opcode(cbuf, 0xB8 | dstenc); + // This next line should be generated from ADLC + if ($src->constant_is_oop()) { + emit_d64_reloc(cbuf, $src$$constant, relocInfo::oop_type, RELOC_IMM64); + } else { + emit_d64(cbuf, $src$$constant); + } + %} + + enc_class load_immF(regF dst, immF con) + %{ + // XXX reg_mem doesn't support RIP-relative addressing yet + emit_rm(cbuf, 0x0, $dst$$reg & 7, 0x5); // 00 reg 101 + emit_float_constant(cbuf, $con$$constant); + %} + + enc_class load_immD(regD dst, immD con) + %{ + // XXX reg_mem doesn't support RIP-relative addressing yet + emit_rm(cbuf, 0x0, $dst$$reg & 7, 0x5); // 00 reg 101 + emit_double_constant(cbuf, $con$$constant); + %} + + enc_class load_conF (regF dst, immF con) %{ // Load float constant + emit_opcode(cbuf, 0xF3); + if ($dst$$reg >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x10); + emit_rm(cbuf, 0x0, $dst$$reg & 7, 0x5); // 00 reg 101 + emit_float_constant(cbuf, $con$$constant); + %} + + enc_class load_conD (regD dst, immD con) %{ // Load double constant + // UseXmmLoadAndClearUpper ? movsd(dst, con) : movlpd(dst, con) + emit_opcode(cbuf, UseXmmLoadAndClearUpper ? 0xF2 : 0x66); + if ($dst$$reg >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, UseXmmLoadAndClearUpper ? 0x10 : 0x12); + emit_rm(cbuf, 0x0, $dst$$reg & 7, 0x5); // 00 reg 101 + emit_double_constant(cbuf, $con$$constant); + %} + + // Encode a reg-reg copy. If it is useless, then empty encoding. + enc_class enc_copy(rRegI dst, rRegI src) + %{ + encode_copy(cbuf, $dst$$reg, $src$$reg); + %} + + // Encode xmm reg-reg copy. If it is useless, then empty encoding. + enc_class enc_CopyXD( RegD dst, RegD src ) %{ + encode_CopyXD( cbuf, $dst$$reg, $src$$reg ); + %} + + enc_class enc_copy_always(rRegI dst, rRegI src) + %{ + int srcenc = $src$$reg; + int dstenc = $dst$$reg; + + if (dstenc < 8) { + if (srcenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + srcenc -= 8; + } + } else { + if (srcenc < 8) { + emit_opcode(cbuf, Assembler::REX_R); + } else { + emit_opcode(cbuf, Assembler::REX_RB); + srcenc -= 8; + } + dstenc -= 8; + } + + emit_opcode(cbuf, 0x8B); + emit_rm(cbuf, 0x3, dstenc, srcenc); + %} + + enc_class enc_copy_wide(rRegL dst, rRegL src) + %{ + int srcenc = $src$$reg; + int dstenc = $dst$$reg; + + if (dstenc != srcenc) { + if (dstenc < 8) { + if (srcenc < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WB); + srcenc -= 8; + } + } else { + if (srcenc < 8) { + emit_opcode(cbuf, Assembler::REX_WR); + } else { + emit_opcode(cbuf, Assembler::REX_WRB); + srcenc -= 8; + } + dstenc -= 8; + } + emit_opcode(cbuf, 0x8B); + emit_rm(cbuf, 0x3, dstenc, srcenc); + } + %} + + enc_class Con32(immI src) + %{ + // Output immediate + $$$emit32$src$$constant; + %} + + enc_class Con64(immL src) + %{ + // Output immediate + emit_d64($src$$constant); + %} + + enc_class Con32F_as_bits(immF src) + %{ + // Output Float immediate bits + jfloat jf = $src$$constant; + jint jf_as_bits = jint_cast(jf); + emit_d32(cbuf, jf_as_bits); + %} + + enc_class Con16(immI src) + %{ + // Output immediate + $$$emit16$src$$constant; + %} + + // How is this different from Con32??? XXX + enc_class Con_d32(immI src) + %{ + emit_d32(cbuf,$src$$constant); + %} + + enc_class conmemref (rRegP t1) %{ // Con32(storeImmI) + // Output immediate memory reference + emit_rm(cbuf, 0x00, $t1$$reg, 0x05 ); + emit_d32(cbuf, 0x00); + %} + + enc_class jump_enc(rRegL switch_val, rRegI dest) %{ + MacroAssembler masm(&cbuf); + + Register switch_reg = as_Register($switch_val$$reg); + Register dest_reg = as_Register($dest$$reg); + address table_base = masm.address_table_constant(_index2label); + + // We could use jump(ArrayAddress) except that the macro assembler needs to use r10 + // to do that and the compiler is using that register as one it can allocate. + // So we build it all by hand. + // Address index(noreg, switch_reg, Address::times_1); + // ArrayAddress dispatch(table, index); + + Address dispatch(dest_reg, switch_reg, Address::times_1); + + masm.lea(dest_reg, InternalAddress(table_base)); + masm.jmp(dispatch); + %} + + enc_class jump_enc_addr(rRegL switch_val, immI2 shift, immL32 offset, rRegI dest) %{ + MacroAssembler masm(&cbuf); + + Register switch_reg = as_Register($switch_val$$reg); + Register dest_reg = as_Register($dest$$reg); + address table_base = masm.address_table_constant(_index2label); + + // We could use jump(ArrayAddress) except that the macro assembler needs to use r10 + // to do that and the compiler is using that register as one it can allocate. + // So we build it all by hand. + // Address index(noreg, switch_reg, (Address::ScaleFactor)$shift$$constant, (int)$offset$$constant); + // ArrayAddress dispatch(table, index); + + Address dispatch(dest_reg, switch_reg, (Address::ScaleFactor)$shift$$constant, (int)$offset$$constant); + + masm.lea(dest_reg, InternalAddress(table_base)); + masm.jmp(dispatch); + %} + + enc_class jump_enc_offset(rRegL switch_val, immI2 shift, rRegI dest) %{ + MacroAssembler masm(&cbuf); + + Register switch_reg = as_Register($switch_val$$reg); + Register dest_reg = as_Register($dest$$reg); + address table_base = masm.address_table_constant(_index2label); + + // We could use jump(ArrayAddress) except that the macro assembler needs to use r10 + // to do that and the compiler is using that register as one it can allocate. + // So we build it all by hand. + // Address index(noreg, switch_reg, (Address::ScaleFactor)$shift$$constant); + // ArrayAddress dispatch(table, index); + + Address dispatch(dest_reg, switch_reg, (Address::ScaleFactor)$shift$$constant); + masm.lea(dest_reg, InternalAddress(table_base)); + masm.jmp(dispatch); + + %} + + enc_class lock_prefix() + %{ + if (os::is_MP()) { + emit_opcode(cbuf, 0xF0); // lock + } + %} + + enc_class REX_mem(memory mem) + %{ + if ($mem$$base >= 8) { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_B); + } else { + emit_opcode(cbuf, Assembler::REX_XB); + } + } else { + if ($mem$$index >= 8) { + emit_opcode(cbuf, Assembler::REX_X); + } + } + %} + + enc_class REX_mem_wide(memory mem) + %{ + if ($mem$$base >= 8) { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_WB); + } else { + emit_opcode(cbuf, Assembler::REX_WXB); + } + } else { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WX); + } + } + %} + + // for byte regs + enc_class REX_breg(rRegI reg) + %{ + if ($reg$$reg >= 4) { + emit_opcode(cbuf, $reg$$reg < 8 ? Assembler::REX : Assembler::REX_B); + } + %} + + // for byte regs + enc_class REX_reg_breg(rRegI dst, rRegI src) + %{ + if ($dst$$reg < 8) { + if ($src$$reg >= 4) { + emit_opcode(cbuf, $src$$reg < 8 ? Assembler::REX : Assembler::REX_B); + } + } else { + if ($src$$reg < 8) { + emit_opcode(cbuf, Assembler::REX_R); + } else { + emit_opcode(cbuf, Assembler::REX_RB); + } + } + %} + + // for byte regs + enc_class REX_breg_mem(rRegI reg, memory mem) + %{ + if ($reg$$reg < 8) { + if ($mem$$base < 8) { + if ($mem$$index >= 8) { + emit_opcode(cbuf, Assembler::REX_X); + } else if ($reg$$reg >= 4) { + emit_opcode(cbuf, Assembler::REX); + } + } else { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_B); + } else { + emit_opcode(cbuf, Assembler::REX_XB); + } + } + } else { + if ($mem$$base < 8) { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_R); + } else { + emit_opcode(cbuf, Assembler::REX_RX); + } + } else { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_RB); + } else { + emit_opcode(cbuf, Assembler::REX_RXB); + } + } + } + %} + + enc_class REX_reg(rRegI reg) + %{ + if ($reg$$reg >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + %} + + enc_class REX_reg_wide(rRegI reg) + %{ + if ($reg$$reg < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WB); + } + %} + + enc_class REX_reg_reg(rRegI dst, rRegI src) + %{ + if ($dst$$reg < 8) { + if ($src$$reg >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + } else { + if ($src$$reg < 8) { + emit_opcode(cbuf, Assembler::REX_R); + } else { + emit_opcode(cbuf, Assembler::REX_RB); + } + } + %} + + enc_class REX_reg_reg_wide(rRegI dst, rRegI src) + %{ + if ($dst$$reg < 8) { + if ($src$$reg < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WB); + } + } else { + if ($src$$reg < 8) { + emit_opcode(cbuf, Assembler::REX_WR); + } else { + emit_opcode(cbuf, Assembler::REX_WRB); + } + } + %} + + enc_class REX_reg_mem(rRegI reg, memory mem) + %{ + if ($reg$$reg < 8) { + if ($mem$$base < 8) { + if ($mem$$index >= 8) { + emit_opcode(cbuf, Assembler::REX_X); + } + } else { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_B); + } else { + emit_opcode(cbuf, Assembler::REX_XB); + } + } + } else { + if ($mem$$base < 8) { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_R); + } else { + emit_opcode(cbuf, Assembler::REX_RX); + } + } else { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_RB); + } else { + emit_opcode(cbuf, Assembler::REX_RXB); + } + } + } + %} + + enc_class REX_reg_mem_wide(rRegL reg, memory mem) + %{ + if ($reg$$reg < 8) { + if ($mem$$base < 8) { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WX); + } + } else { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_WB); + } else { + emit_opcode(cbuf, Assembler::REX_WXB); + } + } + } else { + if ($mem$$base < 8) { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_WR); + } else { + emit_opcode(cbuf, Assembler::REX_WRX); + } + } else { + if ($mem$$index < 8) { + emit_opcode(cbuf, Assembler::REX_WRB); + } else { + emit_opcode(cbuf, Assembler::REX_WRXB); + } + } + } + %} + + enc_class reg_mem(rRegI ereg, memory mem) + %{ + // High registers handle in encode_RegMem + int reg = $ereg$$reg; + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int disp = $mem$$disp; + bool disp_is_oop = $mem->disp_is_oop(); + + encode_RegMem(cbuf, reg, base, index, scale, disp, disp_is_oop); + %} + + enc_class RM_opc_mem(immI rm_opcode, memory mem) + %{ + int rm_byte_opcode = $rm_opcode$$constant; + + // High registers handle in encode_RegMem + int base = $mem$$base; + int index = $mem$$index; + int scale = $mem$$scale; + int displace = $mem$$disp; + + bool disp_is_oop = $mem->disp_is_oop(); // disp-as-oop when + // working with static + // globals + encode_RegMem(cbuf, rm_byte_opcode, base, index, scale, displace, + disp_is_oop); + %} + + enc_class reg_lea(rRegI dst, rRegI src0, immI src1) + %{ + int reg_encoding = $dst$$reg; + int base = $src0$$reg; // 0xFFFFFFFF indicates no base + int index = 0x04; // 0x04 indicates no index + int scale = 0x00; // 0x00 indicates no scale + int displace = $src1$$constant; // 0x00 indicates no displacement + bool disp_is_oop = false; + encode_RegMem(cbuf, reg_encoding, base, index, scale, displace, + disp_is_oop); + %} + + enc_class neg_reg(rRegI dst) + %{ + int dstenc = $dst$$reg; + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + dstenc -= 8; + } + // NEG $dst + emit_opcode(cbuf, 0xF7); + emit_rm(cbuf, 0x3, 0x03, dstenc); + %} + + enc_class neg_reg_wide(rRegI dst) + %{ + int dstenc = $dst$$reg; + if (dstenc < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WB); + dstenc -= 8; + } + // NEG $dst + emit_opcode(cbuf, 0xF7); + emit_rm(cbuf, 0x3, 0x03, dstenc); + %} + + enc_class setLT_reg(rRegI dst) + %{ + int dstenc = $dst$$reg; + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + dstenc -= 8; + } else if (dstenc >= 4) { + emit_opcode(cbuf, Assembler::REX); + } + // SETLT $dst + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x9C); + emit_rm(cbuf, 0x3, 0x0, dstenc); + %} + + enc_class setNZ_reg(rRegI dst) + %{ + int dstenc = $dst$$reg; + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + dstenc -= 8; + } else if (dstenc >= 4) { + emit_opcode(cbuf, Assembler::REX); + } + // SETNZ $dst + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x95); + emit_rm(cbuf, 0x3, 0x0, dstenc); + %} + + enc_class enc_cmpLTP(no_rcx_RegI p, no_rcx_RegI q, no_rcx_RegI y, + rcx_RegI tmp) + %{ + // cadd_cmpLT + + int tmpReg = $tmp$$reg; + + int penc = $p$$reg; + int qenc = $q$$reg; + int yenc = $y$$reg; + + // subl $p,$q + if (penc < 8) { + if (qenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + } else { + if (qenc < 8) { + emit_opcode(cbuf, Assembler::REX_R); + } else { + emit_opcode(cbuf, Assembler::REX_RB); + } + } + emit_opcode(cbuf, 0x2B); + emit_rm(cbuf, 0x3, penc & 7, qenc & 7); + + // sbbl $tmp, $tmp + emit_opcode(cbuf, 0x1B); + emit_rm(cbuf, 0x3, tmpReg, tmpReg); + + // andl $tmp, $y + if (yenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + emit_opcode(cbuf, 0x23); + emit_rm(cbuf, 0x3, tmpReg, yenc & 7); + + // addl $p,$tmp + if (penc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + } + emit_opcode(cbuf, 0x03); + emit_rm(cbuf, 0x3, penc & 7, tmpReg); + %} + + // Compare the lonogs and set -1, 0, or 1 into dst + enc_class cmpl3_flag(rRegL src1, rRegL src2, rRegI dst) + %{ + int src1enc = $src1$$reg; + int src2enc = $src2$$reg; + int dstenc = $dst$$reg; + + // cmpq $src1, $src2 + if (src1enc < 8) { + if (src2enc < 8) { + emit_opcode(cbuf, Assembler::REX_W); + } else { + emit_opcode(cbuf, Assembler::REX_WB); + } + } else { + if (src2enc < 8) { + emit_opcode(cbuf, Assembler::REX_WR); + } else { + emit_opcode(cbuf, Assembler::REX_WRB); + } + } + emit_opcode(cbuf, 0x3B); + emit_rm(cbuf, 0x3, src1enc & 7, src2enc & 7); + + // movl $dst, -1 + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + emit_opcode(cbuf, 0xB8 | (dstenc & 7)); + emit_d32(cbuf, -1); + + // jl,s done + emit_opcode(cbuf, 0x7C); + emit_d8(cbuf, dstenc < 4 ? 0x06 : 0x08); + + // setne $dst + if (dstenc >= 4) { + emit_opcode(cbuf, dstenc < 8 ? Assembler::REX : Assembler::REX_B); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x95); + emit_opcode(cbuf, 0xC0 | (dstenc & 7)); + + // movzbl $dst, $dst + if (dstenc >= 4) { + emit_opcode(cbuf, dstenc < 8 ? Assembler::REX : Assembler::REX_RB); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0xB6); + emit_rm(cbuf, 0x3, dstenc & 7, dstenc & 7); + %} + + enc_class Push_ResultXD(regD dst) %{ + int dstenc = $dst$$reg; + + store_to_stackslot( cbuf, 0xDD, 0x03, 0 ); //FSTP [RSP] + + // UseXmmLoadAndClearUpper ? movsd dst,[rsp] : movlpd dst,[rsp] + emit_opcode (cbuf, UseXmmLoadAndClearUpper ? 0xF2 : 0x66); + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + } + emit_opcode (cbuf, 0x0F ); + emit_opcode (cbuf, UseXmmLoadAndClearUpper ? 0x10 : 0x12 ); + encode_RegMem(cbuf, dstenc, RSP_enc, 0x4, 0, 0, false); + + // add rsp,8 + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf,0x83); + emit_rm(cbuf,0x3, 0x0, RSP_enc); + emit_d8(cbuf,0x08); + %} + + enc_class Push_SrcXD(regD src) %{ + int srcenc = $src$$reg; + + // subq rsp,#8 + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x83); + emit_rm(cbuf, 0x3, 0x5, RSP_enc); + emit_d8(cbuf, 0x8); + + // movsd [rsp],src + emit_opcode(cbuf, 0xF2); + if (srcenc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x11); + encode_RegMem(cbuf, srcenc, RSP_enc, 0x4, 0, 0, false); + + // fldd [rsp] + emit_opcode(cbuf, 0x66); + emit_opcode(cbuf, 0xDD); + encode_RegMem(cbuf, 0x0, RSP_enc, 0x4, 0, 0, false); + %} + + + enc_class movq_ld(regD dst, memory mem) %{ + MacroAssembler _masm(&cbuf); + Address madr = Address::make_raw($mem$$base, $mem$$index, $mem$$scale, $mem$$disp); + __ movq(as_XMMRegister($dst$$reg), madr); + %} + + enc_class movq_st(memory mem, regD src) %{ + MacroAssembler _masm(&cbuf); + Address madr = Address::make_raw($mem$$base, $mem$$index, $mem$$scale, $mem$$disp); + __ movq(madr, as_XMMRegister($src$$reg)); + %} + + enc_class pshufd_8x8(regF dst, regF src) %{ + MacroAssembler _masm(&cbuf); + + encode_CopyXD(cbuf, $dst$$reg, $src$$reg); + __ punpcklbw(as_XMMRegister($dst$$reg), as_XMMRegister($dst$$reg)); + __ pshuflw(as_XMMRegister($dst$$reg), as_XMMRegister($dst$$reg), 0x00); + %} + + enc_class pshufd_4x16(regF dst, regF src) %{ + MacroAssembler _masm(&cbuf); + + __ pshuflw(as_XMMRegister($dst$$reg), as_XMMRegister($src$$reg), 0x00); + %} + + enc_class pshufd(regD dst, regD src, int mode) %{ + MacroAssembler _masm(&cbuf); + + __ pshufd(as_XMMRegister($dst$$reg), as_XMMRegister($src$$reg), $mode); + %} + + enc_class pxor(regD dst, regD src) %{ + MacroAssembler _masm(&cbuf); + + __ pxor(as_XMMRegister($dst$$reg), as_XMMRegister($src$$reg)); + %} + + enc_class mov_i2x(regD dst, rRegI src) %{ + MacroAssembler _masm(&cbuf); + + __ movdl(as_XMMRegister($dst$$reg), as_Register($src$$reg)); + %} + + // obj: object to lock + // box: box address (header location) -- killed + // tmp: rax -- killed + // scr: rbx -- killed + // + // What follows is a direct transliteration of fast_lock() and fast_unlock() + // from i486.ad. See that file for comments. + // TODO: where possible switch from movq (r, 0) to movl(r,0) and + // use the shorter encoding. (Movl clears the high-order 32-bits). + + + enc_class Fast_Lock(rRegP obj, rRegP box, rax_RegI tmp, rRegP scr) + %{ + Register objReg = as_Register((int)$obj$$reg); + Register boxReg = as_Register((int)$box$$reg); + Register tmpReg = as_Register($tmp$$reg); + Register scrReg = as_Register($scr$$reg); + MacroAssembler masm(&cbuf); + + // Verify uniqueness of register assignments -- necessary but not sufficient + assert (objReg != boxReg && objReg != tmpReg && + objReg != scrReg && tmpReg != scrReg, "invariant") ; + + if (_counters != NULL) { + masm.atomic_incl(ExternalAddress((address) _counters->total_entry_count_addr())); + } + if (EmitSync & 1) { + masm.movptr (Address(boxReg, 0), intptr_t(markOopDesc::unused_mark())) ; + masm.cmpq (rsp, 0) ; + } else + if (EmitSync & 2) { + Label DONE_LABEL; + if (UseBiasedLocking) { + // Note: tmpReg maps to the swap_reg argument and scrReg to the tmp_reg argument. + masm.biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, DONE_LABEL, NULL, _counters); + } + masm.movl(tmpReg, 0x1); + masm.orq(tmpReg, Address(objReg, 0)); + masm.movq(Address(boxReg, 0), tmpReg); + if (os::is_MP()) { + masm.lock(); + } + masm.cmpxchgq(boxReg, Address(objReg, 0)); // Updates tmpReg + masm.jcc(Assembler::equal, DONE_LABEL); + + // Recursive locking + masm.subq(tmpReg, rsp); + masm.andq(tmpReg, 7 - os::vm_page_size()); + masm.movq(Address(boxReg, 0), tmpReg); + + masm.bind(DONE_LABEL); + masm.nop(); // avoid branch to branch + } else { + Label DONE_LABEL, IsInflated, Egress; + + masm.movq (tmpReg, Address(objReg, 0)) ; + masm.testq (tmpReg, 0x02) ; // inflated vs stack-locked|neutral|biased + masm.jcc (Assembler::notZero, IsInflated) ; + + // it's stack-locked, biased or neutral + // TODO: optimize markword triage order to reduce the number of + // conditional branches in the most common cases. + // Beware -- there's a subtle invariant that fetch of the markword + // at [FETCH], below, will never observe a biased encoding (*101b). + // If this invariant is not held we'll suffer exclusion (safety) failure. + + if (UseBiasedLocking) { + masm.biased_locking_enter(boxReg, objReg, tmpReg, scrReg, true, DONE_LABEL, NULL, _counters); + masm.movq (tmpReg, Address(objReg, 0)) ; // [FETCH] + } + + masm.orq (tmpReg, 1) ; + masm.movq (Address(boxReg, 0), tmpReg) ; + if (os::is_MP()) { masm.lock(); } + masm.cmpxchgq(boxReg, Address(objReg, 0)); // Updates tmpReg + if (_counters != NULL) { + masm.cond_inc32(Assembler::equal, + ExternalAddress((address) _counters->fast_path_entry_count_addr())); + } + masm.jcc (Assembler::equal, DONE_LABEL); + + // Recursive locking + masm.subq (tmpReg, rsp); + masm.andq (tmpReg, 7 - os::vm_page_size()); + masm.movq (Address(boxReg, 0), tmpReg); + if (_counters != NULL) { + masm.cond_inc32(Assembler::equal, + ExternalAddress((address) _counters->fast_path_entry_count_addr())); + } + masm.jmp (DONE_LABEL) ; + + masm.bind (IsInflated) ; + // It's inflated + + // TODO: someday avoid the ST-before-CAS penalty by + // relocating (deferring) the following ST. + // We should also think about trying a CAS without having + // fetched _owner. If the CAS is successful we may + // avoid an RTO->RTS upgrade on the $line. + masm.movptr(Address(boxReg, 0), intptr_t(markOopDesc::unused_mark())) ; + + masm.movq (boxReg, tmpReg) ; + masm.movq (tmpReg, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; + masm.testq (tmpReg, tmpReg) ; + masm.jcc (Assembler::notZero, DONE_LABEL) ; + + // It's inflated and appears unlocked + if (os::is_MP()) { masm.lock(); } + masm.cmpxchgq(r15_thread, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; + // Intentional fall-through into DONE_LABEL ... + + masm.bind (DONE_LABEL) ; + masm.nop () ; // avoid jmp to jmp + } + %} + + // obj: object to unlock + // box: box address (displaced header location), killed + // RBX: killed tmp; cannot be obj nor box + enc_class Fast_Unlock(rRegP obj, rax_RegP box, rRegP tmp) + %{ + + Register objReg = as_Register($obj$$reg); + Register boxReg = as_Register($box$$reg); + Register tmpReg = as_Register($tmp$$reg); + MacroAssembler masm(&cbuf); + + if (EmitSync & 4) { + masm.cmpq (rsp, 0) ; + } else + if (EmitSync & 8) { + Label DONE_LABEL; + if (UseBiasedLocking) { + masm.biased_locking_exit(objReg, tmpReg, DONE_LABEL); + } + + // Check whether the displaced header is 0 + //(=> recursive unlock) + masm.movq(tmpReg, Address(boxReg, 0)); + masm.testq(tmpReg, tmpReg); + masm.jcc(Assembler::zero, DONE_LABEL); + + // If not recursive lock, reset the header to displaced header + if (os::is_MP()) { + masm.lock(); + } + masm.cmpxchgq(tmpReg, Address(objReg, 0)); // Uses RAX which is box + masm.bind(DONE_LABEL); + masm.nop(); // avoid branch to branch + } else { + Label DONE_LABEL, Stacked, CheckSucc ; + + if (UseBiasedLocking) { + masm.biased_locking_exit(objReg, tmpReg, DONE_LABEL); + } + + masm.movq (tmpReg, Address(objReg, 0)) ; + masm.cmpq (Address(boxReg, 0), (int)NULL_WORD) ; + masm.jcc (Assembler::zero, DONE_LABEL) ; + masm.testq (tmpReg, 0x02) ; + masm.jcc (Assembler::zero, Stacked) ; + + // It's inflated + masm.movq (boxReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; + masm.xorq (boxReg, r15_thread) ; + masm.orq (boxReg, Address (tmpReg, ObjectMonitor::recursions_offset_in_bytes()-2)) ; + masm.jcc (Assembler::notZero, DONE_LABEL) ; + masm.movq (boxReg, Address (tmpReg, ObjectMonitor::cxq_offset_in_bytes()-2)) ; + masm.orq (boxReg, Address (tmpReg, ObjectMonitor::EntryList_offset_in_bytes()-2)) ; + masm.jcc (Assembler::notZero, CheckSucc) ; + masm.mov64 (Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), (int)NULL_WORD) ; + masm.jmp (DONE_LABEL) ; + + if ((EmitSync & 65536) == 0) { + Label LSuccess, LGoSlowPath ; + masm.bind (CheckSucc) ; + masm.cmpq (Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), (int)NULL_WORD) ; + masm.jcc (Assembler::zero, LGoSlowPath) ; + + // I'd much rather use lock:andl m->_owner, 0 as it's faster than the + // the explicit ST;MEMBAR combination, but masm doesn't currently support + // "ANDQ M,IMM". Don't use MFENCE here. lock:add to TOS, xchg, etc + // are all faster when the write buffer is populated. + masm.movptr (Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), (int)NULL_WORD) ; + if (os::is_MP()) { + masm.lock () ; masm.addq (Address(rsp, 0), 0) ; + } + masm.cmpq (Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), (int)NULL_WORD) ; + masm.jcc (Assembler::notZero, LSuccess) ; + + masm.movptr (boxReg, (int)NULL_WORD) ; // box is really EAX + if (os::is_MP()) { masm.lock(); } + masm.cmpxchgq (r15_thread, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); + masm.jcc (Assembler::notEqual, LSuccess) ; + // Intentional fall-through into slow-path + + masm.bind (LGoSlowPath) ; + masm.orl (boxReg, 1) ; // set ICC.ZF=0 to indicate failure + masm.jmp (DONE_LABEL) ; + + masm.bind (LSuccess) ; + masm.testl (boxReg, 0) ; // set ICC.ZF=1 to indicate success + masm.jmp (DONE_LABEL) ; + } + + masm.bind (Stacked) ; + masm.movq (tmpReg, Address (boxReg, 0)) ; // re-fetch + if (os::is_MP()) { masm.lock(); } + masm.cmpxchgq(tmpReg, Address(objReg, 0)); // Uses RAX which is box + + if (EmitSync & 65536) { + masm.bind (CheckSucc) ; + } + masm.bind(DONE_LABEL); + if (EmitSync & 32768) { + masm.nop(); // avoid branch to branch + } + } + %} + + enc_class enc_String_Compare() + %{ + Label RCX_GOOD_LABEL, LENGTH_DIFF_LABEL, + POP_LABEL, DONE_LABEL, CONT_LABEL, + WHILE_HEAD_LABEL; + MacroAssembler masm(&cbuf); + + // Get the first character position in both strings + // [8] char array, [12] offset, [16] count + int value_offset = java_lang_String::value_offset_in_bytes(); + int offset_offset = java_lang_String::offset_offset_in_bytes(); + int count_offset = java_lang_String::count_offset_in_bytes(); + int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); + + masm.movq(rax, Address(rsi, value_offset)); + masm.movl(rcx, Address(rsi, offset_offset)); + masm.leaq(rax, Address(rax, rcx, Address::times_2, base_offset)); + masm.movq(rbx, Address(rdi, value_offset)); + masm.movl(rcx, Address(rdi, offset_offset)); + masm.leaq(rbx, Address(rbx, rcx, Address::times_2, base_offset)); + + // Compute the minimum of the string lengths(rsi) and the + // difference of the string lengths (stack) + + masm.movl(rdi, Address(rdi, count_offset)); + masm.movl(rsi, Address(rsi, count_offset)); + masm.movl(rcx, rdi); + masm.subl(rdi, rsi); + masm.pushq(rdi); + masm.cmovl(Assembler::lessEqual, rsi, rcx); + + // Is the minimum length zero? + masm.bind(RCX_GOOD_LABEL); + masm.testl(rsi, rsi); + masm.jcc(Assembler::zero, LENGTH_DIFF_LABEL); + + // Load first characters + masm.load_unsigned_word(rcx, Address(rbx, 0)); + masm.load_unsigned_word(rdi, Address(rax, 0)); + + // Compare first characters + masm.subl(rcx, rdi); + masm.jcc(Assembler::notZero, POP_LABEL); + masm.decrementl(rsi); + masm.jcc(Assembler::zero, LENGTH_DIFF_LABEL); + + { + // Check after comparing first character to see if strings are equivalent + Label LSkip2; + // Check if the strings start at same location + masm.cmpq(rbx, rax); + masm.jcc(Assembler::notEqual, LSkip2); + + // Check if the length difference is zero (from stack) + masm.cmpl(Address(rsp, 0), 0x0); + masm.jcc(Assembler::equal, LENGTH_DIFF_LABEL); + + // Strings might not be equivalent + masm.bind(LSkip2); + } + + // Shift RAX and RBX to the end of the arrays, negate min + masm.leaq(rax, Address(rax, rsi, Address::times_2, 2)); + masm.leaq(rbx, Address(rbx, rsi, Address::times_2, 2)); + masm.negq(rsi); + + // Compare the rest of the characters + masm.bind(WHILE_HEAD_LABEL); + masm.load_unsigned_word(rcx, Address(rbx, rsi, Address::times_2, 0)); + masm.load_unsigned_word(rdi, Address(rax, rsi, Address::times_2, 0)); + masm.subl(rcx, rdi); + masm.jcc(Assembler::notZero, POP_LABEL); + masm.incrementq(rsi); + masm.jcc(Assembler::notZero, WHILE_HEAD_LABEL); + + // Strings are equal up to min length. Return the length difference. + masm.bind(LENGTH_DIFF_LABEL); + masm.popq(rcx); + masm.jmp(DONE_LABEL); + + // Discard the stored length difference + masm.bind(POP_LABEL); + masm.addq(rsp, 8); + + // That's it + masm.bind(DONE_LABEL); + %} + + enc_class enc_rethrow() + %{ + cbuf.set_inst_mark(); + emit_opcode(cbuf, 0xE9); // jmp entry + emit_d32_reloc(cbuf, + (int) (OptoRuntime::rethrow_stub() - cbuf.code_end() - 4), + runtime_call_Relocation::spec(), + RELOC_DISP32); + %} + + enc_class absF_encoding(regF dst) + %{ + int dstenc = $dst$$reg; + address signmask_address = (address) StubRoutines::amd64::float_sign_mask(); + + cbuf.set_inst_mark(); + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + dstenc -= 8; + } + // XXX reg_mem doesn't support RIP-relative addressing yet + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x54); + emit_rm(cbuf, 0x0, dstenc, 0x5); // 00 reg 101 + emit_d32_reloc(cbuf, signmask_address); + %} + + enc_class absD_encoding(regD dst) + %{ + int dstenc = $dst$$reg; + address signmask_address = (address) StubRoutines::amd64::double_sign_mask(); + + cbuf.set_inst_mark(); + emit_opcode(cbuf, 0x66); + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + dstenc -= 8; + } + // XXX reg_mem doesn't support RIP-relative addressing yet + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x54); + emit_rm(cbuf, 0x0, dstenc, 0x5); // 00 reg 101 + emit_d32_reloc(cbuf, signmask_address); + %} + + enc_class negF_encoding(regF dst) + %{ + int dstenc = $dst$$reg; + address signflip_address = (address) StubRoutines::amd64::float_sign_flip(); + + cbuf.set_inst_mark(); + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + dstenc -= 8; + } + // XXX reg_mem doesn't support RIP-relative addressing yet + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x57); + emit_rm(cbuf, 0x0, dstenc, 0x5); // 00 reg 101 + emit_d32_reloc(cbuf, signflip_address); + %} + + enc_class negD_encoding(regD dst) + %{ + int dstenc = $dst$$reg; + address signflip_address = (address) StubRoutines::amd64::double_sign_flip(); + + cbuf.set_inst_mark(); + emit_opcode(cbuf, 0x66); + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + dstenc -= 8; + } + // XXX reg_mem doesn't support RIP-relative addressing yet + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x57); + emit_rm(cbuf, 0x0, dstenc, 0x5); // 00 reg 101 + emit_d32_reloc(cbuf, signflip_address); + %} + + enc_class f2i_fixup(rRegI dst, regF src) + %{ + int dstenc = $dst$$reg; + int srcenc = $src$$reg; + + // cmpl $dst, #0x80000000 + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + emit_opcode(cbuf, 0x81); + emit_rm(cbuf, 0x3, 0x7, dstenc & 7); + emit_d32(cbuf, 0x80000000); + + // jne,s done + emit_opcode(cbuf, 0x75); + if (srcenc < 8 && dstenc < 8) { + emit_d8(cbuf, 0xF); + } else if (srcenc >= 8 && dstenc >= 8) { + emit_d8(cbuf, 0x11); + } else { + emit_d8(cbuf, 0x10); + } + + // subq rsp, #8 + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x83); + emit_rm(cbuf, 0x3, 0x5, RSP_enc); + emit_d8(cbuf, 8); + + // movss [rsp], $src + emit_opcode(cbuf, 0xF3); + if (srcenc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x11); + encode_RegMem(cbuf, srcenc, RSP_enc, 0x4, 0, 0, false); // 2 bytes + + // call f2i_fixup + cbuf.set_inst_mark(); + emit_opcode(cbuf, 0xE8); + emit_d32_reloc(cbuf, + (int) + (StubRoutines::amd64::f2i_fixup() - cbuf.code_end() - 4), + runtime_call_Relocation::spec(), + RELOC_DISP32); + + // popq $dst + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + emit_opcode(cbuf, 0x58 | (dstenc & 7)); + + // done: + %} + + enc_class f2l_fixup(rRegL dst, regF src) + %{ + int dstenc = $dst$$reg; + int srcenc = $src$$reg; + address const_address = (address) StubRoutines::amd64::double_sign_flip(); + + // cmpq $dst, [0x8000000000000000] + cbuf.set_inst_mark(); + emit_opcode(cbuf, dstenc < 8 ? Assembler::REX_W : Assembler::REX_WR); + emit_opcode(cbuf, 0x39); + // XXX reg_mem doesn't support RIP-relative addressing yet + emit_rm(cbuf, 0x0, dstenc & 7, 0x5); // 00 reg 101 + emit_d32_reloc(cbuf, const_address); + + + // jne,s done + emit_opcode(cbuf, 0x75); + if (srcenc < 8 && dstenc < 8) { + emit_d8(cbuf, 0xF); + } else if (srcenc >= 8 && dstenc >= 8) { + emit_d8(cbuf, 0x11); + } else { + emit_d8(cbuf, 0x10); + } + + // subq rsp, #8 + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x83); + emit_rm(cbuf, 0x3, 0x5, RSP_enc); + emit_d8(cbuf, 8); + + // movss [rsp], $src + emit_opcode(cbuf, 0xF3); + if (srcenc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x11); + encode_RegMem(cbuf, srcenc, RSP_enc, 0x4, 0, 0, false); // 2 bytes + + // call f2l_fixup + cbuf.set_inst_mark(); + emit_opcode(cbuf, 0xE8); + emit_d32_reloc(cbuf, + (int) + (StubRoutines::amd64::f2l_fixup() - cbuf.code_end() - 4), + runtime_call_Relocation::spec(), + RELOC_DISP32); + + // popq $dst + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + emit_opcode(cbuf, 0x58 | (dstenc & 7)); + + // done: + %} + + enc_class d2i_fixup(rRegI dst, regD src) + %{ + int dstenc = $dst$$reg; + int srcenc = $src$$reg; + + // cmpl $dst, #0x80000000 + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + emit_opcode(cbuf, 0x81); + emit_rm(cbuf, 0x3, 0x7, dstenc & 7); + emit_d32(cbuf, 0x80000000); + + // jne,s done + emit_opcode(cbuf, 0x75); + if (srcenc < 8 && dstenc < 8) { + emit_d8(cbuf, 0xF); + } else if (srcenc >= 8 && dstenc >= 8) { + emit_d8(cbuf, 0x11); + } else { + emit_d8(cbuf, 0x10); + } + + // subq rsp, #8 + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x83); + emit_rm(cbuf, 0x3, 0x5, RSP_enc); + emit_d8(cbuf, 8); + + // movsd [rsp], $src + emit_opcode(cbuf, 0xF2); + if (srcenc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x11); + encode_RegMem(cbuf, srcenc, RSP_enc, 0x4, 0, 0, false); // 2 bytes + + // call d2i_fixup + cbuf.set_inst_mark(); + emit_opcode(cbuf, 0xE8); + emit_d32_reloc(cbuf, + (int) + (StubRoutines::amd64::d2i_fixup() - cbuf.code_end() - 4), + runtime_call_Relocation::spec(), + RELOC_DISP32); + + // popq $dst + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + emit_opcode(cbuf, 0x58 | (dstenc & 7)); + + // done: + %} + + enc_class d2l_fixup(rRegL dst, regD src) + %{ + int dstenc = $dst$$reg; + int srcenc = $src$$reg; + address const_address = (address) StubRoutines::amd64::double_sign_flip(); + + // cmpq $dst, [0x8000000000000000] + cbuf.set_inst_mark(); + emit_opcode(cbuf, dstenc < 8 ? Assembler::REX_W : Assembler::REX_WR); + emit_opcode(cbuf, 0x39); + // XXX reg_mem doesn't support RIP-relative addressing yet + emit_rm(cbuf, 0x0, dstenc & 7, 0x5); // 00 reg 101 + emit_d32_reloc(cbuf, const_address); + + + // jne,s done + emit_opcode(cbuf, 0x75); + if (srcenc < 8 && dstenc < 8) { + emit_d8(cbuf, 0xF); + } else if (srcenc >= 8 && dstenc >= 8) { + emit_d8(cbuf, 0x11); + } else { + emit_d8(cbuf, 0x10); + } + + // subq rsp, #8 + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x83); + emit_rm(cbuf, 0x3, 0x5, RSP_enc); + emit_d8(cbuf, 8); + + // movsd [rsp], $src + emit_opcode(cbuf, 0xF2); + if (srcenc >= 8) { + emit_opcode(cbuf, Assembler::REX_R); + } + emit_opcode(cbuf, 0x0F); + emit_opcode(cbuf, 0x11); + encode_RegMem(cbuf, srcenc, RSP_enc, 0x4, 0, 0, false); // 2 bytes + + // call d2l_fixup + cbuf.set_inst_mark(); + emit_opcode(cbuf, 0xE8); + emit_d32_reloc(cbuf, + (int) + (StubRoutines::amd64::d2l_fixup() - cbuf.code_end() - 4), + runtime_call_Relocation::spec(), + RELOC_DISP32); + + // popq $dst + if (dstenc >= 8) { + emit_opcode(cbuf, Assembler::REX_B); + } + emit_opcode(cbuf, 0x58 | (dstenc & 7)); + + // done: + %} + + enc_class enc_membar_acquire + %{ + // [jk] not needed currently, if you enable this and it really + // emits code don't forget to the remove the "size(0)" line in + // membar_acquire() + // MacroAssembler masm(&cbuf); + // masm.membar(Assembler::Membar_mask_bits(Assembler::LoadStore | + // Assembler::LoadLoad)); + %} + + enc_class enc_membar_release + %{ + // [jk] not needed currently, if you enable this and it really + // emits code don't forget to the remove the "size(0)" line in + // membar_release() + // MacroAssembler masm(&cbuf); + // masm.membar(Assembler::Membar_mask_bits(Assembler::LoadStore | + // Assembler::StoreStore)); + %} + + enc_class enc_membar_volatile + %{ + MacroAssembler masm(&cbuf); + masm.membar(Assembler::Membar_mask_bits(Assembler::StoreLoad | + Assembler::StoreStore)); + %} + + // Safepoint Poll. This polls the safepoint page, and causes an + // exception if it is not readable. Unfortunately, it kills + // RFLAGS in the process. + enc_class enc_safepoint_poll + %{ + // testl %rax, off(%rip) // Opcode + ModRM + Disp32 == 6 bytes + // XXX reg_mem doesn't support RIP-relative addressing yet + cbuf.set_inst_mark(); + cbuf.relocate(cbuf.inst_mark(), relocInfo::poll_type, 0); // XXX + emit_opcode(cbuf, 0x85); // testl + emit_rm(cbuf, 0x0, RAX_enc, 0x5); // 00 rax 101 == 0x5 + // cbuf.inst_mark() is beginning of instruction + emit_d32_reloc(cbuf, os::get_polling_page()); +// relocInfo::poll_type, + %} +%} + + +//----------FRAME-------------------------------------------------------------- +// Definition of frame structure and management information. +// +// S T A C K L A Y O U T Allocators stack-slot number +// | (to get allocators register number +// G Owned by | | v add OptoReg::stack0()) +// r CALLER | | +// o | +--------+ pad to even-align allocators stack-slot +// w V | pad0 | numbers; owned by CALLER +// t -----------+--------+----> Matcher::_in_arg_limit, unaligned +// h ^ | in | 5 +// | | args | 4 Holes in incoming args owned by SELF +// | | | | 3 +// | | +--------+ +// V | | old out| Empty on Intel, window on Sparc +// | old |preserve| Must be even aligned. +// | SP-+--------+----> Matcher::_old_SP, even aligned +// | | in | 3 area for Intel ret address +// Owned by |preserve| Empty on Sparc. +// SELF +--------+ +// | | pad2 | 2 pad to align old SP +// | +--------+ 1 +// | | locks | 0 +// | +--------+----> OptoReg::stack0(), even aligned +// | | pad1 | 11 pad to align new SP +// | +--------+ +// | | | 10 +// | | spills | 9 spills +// V | | 8 (pad0 slot for callee) +// -----------+--------+----> Matcher::_out_arg_limit, unaligned +// ^ | out | 7 +// | | args | 6 Holes in outgoing args owned by CALLEE +// Owned by +--------+ +// CALLEE | new out| 6 Empty on Intel, window on Sparc +// | new |preserve| Must be even-aligned. +// | SP-+--------+----> Matcher::_new_SP, even aligned +// | | | +// +// Note 1: Only region 8-11 is determined by the allocator. Region 0-5 is +// known from SELF's arguments and the Java calling convention. +// Region 6-7 is determined per call site. +// Note 2: If the calling convention leaves holes in the incoming argument +// area, those holes are owned by SELF. Holes in the outgoing area +// are owned by the CALLEE. Holes should not be nessecary in the +// incoming area, as the Java calling convention is completely under +// the control of the AD file. Doubles can be sorted and packed to +// avoid holes. Holes in the outgoing arguments may be nessecary for +// varargs C calling conventions. +// Note 3: Region 0-3 is even aligned, with pad2 as needed. Region 3-5 is +// even aligned with pad0 as needed. +// Region 6 is even aligned. Region 6-7 is NOT even aligned; +// region 6-11 is even aligned; it may be padded out more so that +// the region from SP to FP meets the minimum stack alignment. +// Note 4: For I2C adapters, the incoming FP may not meet the minimum stack +// alignment. Region 11, pad1, may be dynamically extended so that +// SP meets the minimum alignment. + +frame +%{ + // What direction does stack grow in (assumed to be same for C & Java) + stack_direction(TOWARDS_LOW); + + // These three registers define part of the calling convention + // between compiled code and the interpreter. + inline_cache_reg(RAX); // Inline Cache Register + interpreter_method_oop_reg(RBX); // Method Oop Register when + // calling interpreter + + // Optional: name the operand used by cisc-spilling to access + // [stack_pointer + offset] + cisc_spilling_operand_name(indOffset32); + + // Number of stack slots consumed by locking an object + sync_stack_slots(2); + + // Compiled code's Frame Pointer + frame_pointer(RSP); + + // Interpreter stores its frame pointer in a register which is + // stored to the stack by I2CAdaptors. + // I2CAdaptors convert from interpreted java to compiled java. + interpreter_frame_pointer(RBP); + + // Stack alignment requirement + stack_alignment(StackAlignmentInBytes); // Alignment size in bytes (128-bit -> 16 bytes) + + // Number of stack slots between incoming argument block and the start of + // a new frame. The PROLOG must add this many slots to the stack. The + // EPILOG must remove this many slots. amd64 needs two slots for + // return address. + in_preserve_stack_slots(4 + 2 * VerifyStackAtCalls); + + // Number of outgoing stack slots killed above the out_preserve_stack_slots + // for calls to C. Supports the var-args backing area for register parms. + varargs_C_out_slots_killed(frame::arg_reg_save_area_bytes/BytesPerInt); + + // The after-PROLOG location of the return address. Location of + // return address specifies a type (REG or STACK) and a number + // representing the register number (i.e. - use a register name) or + // stack slot. + // Ret Addr is on stack in slot 0 if no locks or verification or alignment. + // Otherwise, it is above the locks and verification slot and alignment word + return_addr(STACK - 2 + + round_to(2 + 2 * VerifyStackAtCalls + + Compile::current()->fixed_slots(), + WordsPerLong * 2)); + + // Body of function which returns an integer array locating + // arguments either in registers or in stack slots. Passed an array + // of ideal registers called "sig" and a "length" count. Stack-slot + // offsets are based on outgoing arguments, i.e. a CALLER setting up + // arguments for a CALLEE. Incoming stack arguments are + // automatically biased by the preserve_stack_slots field above. + + calling_convention + %{ + // No difference between ingoing/outgoing just pass false + SharedRuntime::java_calling_convention(sig_bt, regs, length, false); + %} + + c_calling_convention + %{ + // This is obviously always outgoing + (void) SharedRuntime::c_calling_convention(sig_bt, regs, length); + %} + + // Location of compiled Java return values. Same as C for now. + return_value + %{ + assert(ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, + "only return normal values"); + + static const int lo[Op_RegL + 1] = { + 0, + 0, + RAX_num, // Op_RegI + RAX_num, // Op_RegP + XMM0_num, // Op_RegF + XMM0_num, // Op_RegD + RAX_num // Op_RegL + }; + static const int hi[Op_RegL + 1] = { + 0, + 0, + OptoReg::Bad, // Op_RegI + RAX_H_num, // Op_RegP + OptoReg::Bad, // Op_RegF + XMM0_H_num, // Op_RegD + RAX_H_num // Op_RegL + }; + + return OptoRegPair(hi[ideal_reg], lo[ideal_reg]); + %} +%} + +//----------ATTRIBUTES--------------------------------------------------------- +//----------Operand Attributes------------------------------------------------- +op_attrib op_cost(0); // Required cost attribute + +//----------Instruction Attributes--------------------------------------------- +ins_attrib ins_cost(100); // Required cost attribute +ins_attrib ins_size(8); // Required size attribute (in bits) +ins_attrib ins_pc_relative(0); // Required PC Relative flag +ins_attrib ins_short_branch(0); // Required flag: is this instruction + // a non-matching short branch variant + // of some long branch? +ins_attrib ins_alignment(1); // Required alignment attribute (must + // be a power of 2) specifies the + // alignment that some part of the + // instruction (not necessarily the + // start) requires. If > 1, a + // compute_padding() function must be + // provided for the instruction + +//----------OPERANDS----------------------------------------------------------- +// Operand definitions must precede instruction definitions for correct parsing +// in the ADLC because operands constitute user defined types which are used in +// instruction definitions. + +//----------Simple Operands---------------------------------------------------- +// Immediate Operands +// Integer Immediate +operand immI() +%{ + match(ConI); + + op_cost(10); + format %{ %} + interface(CONST_INTER); +%} + +// Constant for test vs zero +operand immI0() +%{ + predicate(n->get_int() == 0); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Constant for increment +operand immI1() +%{ + predicate(n->get_int() == 1); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Constant for decrement +operand immI_M1() +%{ + predicate(n->get_int() == -1); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Valid scale values for addressing modes +operand immI2() +%{ + predicate(0 <= n->get_int() && (n->get_int() <= 3)); + match(ConI); + + format %{ %} + interface(CONST_INTER); +%} + +operand immI8() +%{ + predicate((-0x80 <= n->get_int()) && (n->get_int() < 0x80)); + match(ConI); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +operand immI16() +%{ + predicate((-32768 <= n->get_int()) && (n->get_int() <= 32767)); + match(ConI); + + op_cost(10); + format %{ %} + interface(CONST_INTER); +%} + +// Constant for long shifts +operand immI_32() +%{ + predicate( n->get_int() == 32 ); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Constant for long shifts +operand immI_64() +%{ + predicate( n->get_int() == 64 ); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Pointer Immediate +operand immP() +%{ + match(ConP); + + op_cost(10); + format %{ %} + interface(CONST_INTER); +%} + +// NULL Pointer Immediate +operand immP0() +%{ + predicate(n->get_ptr() == 0); + match(ConP); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Unsigned 31-bit Pointer Immediate +// Can be used in both 32-bit signed and 32-bit unsigned insns. +// Works for nulls and markOops; not for relocatable (oop) pointers. +operand immP31() +%{ + predicate(!n->as_Type()->type()->isa_oopptr() + && (n->get_ptr() >> 31) == 0); + match(ConP); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate +operand immL() +%{ + match(ConL); + + op_cost(20); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate 8-bit +operand immL8() +%{ + predicate(-0x80L <= n->get_long() && n->get_long() < 0x80L); + match(ConL); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate 32-bit unsigned +operand immUL32() +%{ + predicate(n->get_long() == (unsigned int) (n->get_long())); + match(ConL); + + op_cost(10); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate 32-bit signed +operand immL32() +%{ + predicate(n->get_long() == (int) (n->get_long())); + match(ConL); + + op_cost(15); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate zero +operand immL0() +%{ + predicate(n->get_long() == 0L); + match(ConL); + + op_cost(10); + format %{ %} + interface(CONST_INTER); +%} + +// Constant for increment +operand immL1() +%{ + predicate(n->get_long() == 1); + match(ConL); + + format %{ %} + interface(CONST_INTER); +%} + +// Constant for decrement +operand immL_M1() +%{ + predicate(n->get_long() == -1); + match(ConL); + + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: the value 10 +operand immL10() +%{ + predicate(n->get_long() == 10); + match(ConL); + + format %{ %} + interface(CONST_INTER); +%} + +// Long immediate from 0 to 127. +// Used for a shorter form of long mul by 10. +operand immL_127() +%{ + predicate(0 <= n->get_long() && n->get_long() < 0x80); + match(ConL); + + op_cost(10); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: low 32-bit mask +operand immL_32bits() +%{ + predicate(n->get_long() == 0xFFFFFFFFL); + match(ConL); + op_cost(20); + + format %{ %} + interface(CONST_INTER); +%} + +// Float Immediate zero +operand immF0() +%{ + predicate(jint_cast(n->getf()) == 0); + match(ConF); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Float Immediate +operand immF() +%{ + match(ConF); + + op_cost(15); + format %{ %} + interface(CONST_INTER); +%} + +// Double Immediate zero +operand immD0() +%{ + predicate(jlong_cast(n->getd()) == 0); + match(ConD); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + +// Double Immediate +operand immD() +%{ + match(ConD); + + op_cost(15); + format %{ %} + interface(CONST_INTER); +%} + +// Immediates for special shifts (sign extend) + +// Constants for increment +operand immI_16() +%{ + predicate(n->get_int() == 16); + match(ConI); + + format %{ %} + interface(CONST_INTER); +%} + +operand immI_24() +%{ + predicate(n->get_int() == 24); + match(ConI); + + format %{ %} + interface(CONST_INTER); +%} + +// Constant for byte-wide masking +operand immI_255() +%{ + predicate(n->get_int() == 255); + match(ConI); + + format %{ %} + interface(CONST_INTER); +%} + +// Constant for short-wide masking +operand immI_65535() +%{ + predicate(n->get_int() == 65535); + match(ConI); + + format %{ %} + interface(CONST_INTER); +%} + +// Constant for byte-wide masking +operand immL_255() +%{ + predicate(n->get_long() == 255); + match(ConL); + + format %{ %} + interface(CONST_INTER); +%} + +// Constant for short-wide masking +operand immL_65535() +%{ + predicate(n->get_long() == 65535); + match(ConL); + + format %{ %} + interface(CONST_INTER); +%} + +// Register Operands +// Integer Register +operand rRegI() +%{ + constraint(ALLOC_IN_RC(int_reg)); + match(RegI); + + match(rax_RegI); + match(rbx_RegI); + match(rcx_RegI); + match(rdx_RegI); + match(rdi_RegI); + + format %{ %} + interface(REG_INTER); +%} + +// Special Registers +operand rax_RegI() +%{ + constraint(ALLOC_IN_RC(int_rax_reg)); + match(RegI); + match(rRegI); + + format %{ "RAX" %} + interface(REG_INTER); +%} + +// Special Registers +operand rbx_RegI() +%{ + constraint(ALLOC_IN_RC(int_rbx_reg)); + match(RegI); + match(rRegI); + + format %{ "RBX" %} + interface(REG_INTER); +%} + +operand rcx_RegI() +%{ + constraint(ALLOC_IN_RC(int_rcx_reg)); + match(RegI); + match(rRegI); + + format %{ "RCX" %} + interface(REG_INTER); +%} + +operand rdx_RegI() +%{ + constraint(ALLOC_IN_RC(int_rdx_reg)); + match(RegI); + match(rRegI); + + format %{ "RDX" %} + interface(REG_INTER); +%} + +operand rdi_RegI() +%{ + constraint(ALLOC_IN_RC(int_rdi_reg)); + match(RegI); + match(rRegI); + + format %{ "RDI" %} + interface(REG_INTER); +%} + +operand no_rcx_RegI() +%{ + constraint(ALLOC_IN_RC(int_no_rcx_reg)); + match(RegI); + match(rax_RegI); + match(rbx_RegI); + match(rdx_RegI); + match(rdi_RegI); + + format %{ %} + interface(REG_INTER); +%} + +operand no_rax_rdx_RegI() +%{ + constraint(ALLOC_IN_RC(int_no_rax_rdx_reg)); + match(RegI); + match(rbx_RegI); + match(rcx_RegI); + match(rdi_RegI); + + format %{ %} + interface(REG_INTER); +%} + +// Pointer Register +operand any_RegP() +%{ + constraint(ALLOC_IN_RC(any_reg)); + match(RegP); + match(rax_RegP); + match(rbx_RegP); + match(rdi_RegP); + match(rsi_RegP); + match(rbp_RegP); + match(r15_RegP); + match(rRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand rRegP() +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(RegP); + match(rax_RegP); + match(rbx_RegP); + match(rdi_RegP); + match(rsi_RegP); + match(rbp_RegP); + match(r15_RegP); // See Q&A below about r15_RegP. + + format %{ %} + interface(REG_INTER); +%} + +// Question: Why is r15_RegP (the read-only TLS register) a match for rRegP? +// Answer: Operand match rules govern the DFA as it processes instruction inputs. +// It's fine for an instruction input which expects rRegP to match a r15_RegP. +// The output of an instruction is controlled by the allocator, which respects +// register class masks, not match rules. Unless an instruction mentions +// r15_RegP or any_RegP explicitly as its output, r15 will not be considered +// by the allocator as an input. + +operand no_rax_RegP() +%{ + constraint(ALLOC_IN_RC(ptr_no_rax_reg)); + match(RegP); + match(rbx_RegP); + match(rsi_RegP); + match(rdi_RegP); + + format %{ %} + interface(REG_INTER); +%} + +operand no_rbp_RegP() +%{ + constraint(ALLOC_IN_RC(ptr_no_rbp_reg)); + match(RegP); + match(rbx_RegP); + match(rsi_RegP); + match(rdi_RegP); + + format %{ %} + interface(REG_INTER); +%} + +operand no_rax_rbx_RegP() +%{ + constraint(ALLOC_IN_RC(ptr_no_rax_rbx_reg)); + match(RegP); + match(rsi_RegP); + match(rdi_RegP); + + format %{ %} + interface(REG_INTER); +%} + +// Special Registers +// Return a pointer value +operand rax_RegP() +%{ + constraint(ALLOC_IN_RC(ptr_rax_reg)); + match(RegP); + match(rRegP); + + format %{ %} + interface(REG_INTER); +%} + +// Used in AtomicAdd +operand rbx_RegP() +%{ + constraint(ALLOC_IN_RC(ptr_rbx_reg)); + match(RegP); + match(rRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand rsi_RegP() +%{ + constraint(ALLOC_IN_RC(ptr_rsi_reg)); + match(RegP); + match(rRegP); + + format %{ %} + interface(REG_INTER); +%} + +// Used in rep stosq +operand rdi_RegP() +%{ + constraint(ALLOC_IN_RC(ptr_rdi_reg)); + match(RegP); + match(rRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand rbp_RegP() +%{ + constraint(ALLOC_IN_RC(ptr_rbp_reg)); + match(RegP); + match(rRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand r15_RegP() +%{ + constraint(ALLOC_IN_RC(ptr_r15_reg)); + match(RegP); + match(rRegP); + + format %{ %} + interface(REG_INTER); +%} + +operand rRegL() +%{ + constraint(ALLOC_IN_RC(long_reg)); + match(RegL); + match(rax_RegL); + match(rdx_RegL); + + format %{ %} + interface(REG_INTER); +%} + +// Special Registers +operand no_rax_rdx_RegL() +%{ + constraint(ALLOC_IN_RC(long_no_rax_rdx_reg)); + match(RegL); + match(rRegL); + + format %{ %} + interface(REG_INTER); +%} + +operand no_rax_RegL() +%{ + constraint(ALLOC_IN_RC(long_no_rax_rdx_reg)); + match(RegL); + match(rRegL); + match(rdx_RegL); + + format %{ %} + interface(REG_INTER); +%} + +operand no_rcx_RegL() +%{ + constraint(ALLOC_IN_RC(long_no_rcx_reg)); + match(RegL); + match(rRegL); + + format %{ %} + interface(REG_INTER); +%} + +operand rax_RegL() +%{ + constraint(ALLOC_IN_RC(long_rax_reg)); + match(RegL); + match(rRegL); + + format %{ "RAX" %} + interface(REG_INTER); +%} + +operand rcx_RegL() +%{ + constraint(ALLOC_IN_RC(long_rcx_reg)); + match(RegL); + match(rRegL); + + format %{ %} + interface(REG_INTER); +%} + +operand rdx_RegL() +%{ + constraint(ALLOC_IN_RC(long_rdx_reg)); + match(RegL); + match(rRegL); + + format %{ %} + interface(REG_INTER); +%} + +// Flags register, used as output of compare instructions +operand rFlagsReg() +%{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "RFLAGS" %} + interface(REG_INTER); +%} + +// Flags register, used as output of FLOATING POINT compare instructions +operand rFlagsRegU() +%{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "RFLAGS_U" %} + interface(REG_INTER); +%} + +// Float register operands +operand regF() +%{ + constraint(ALLOC_IN_RC(float_reg)); + match(RegF); + + format %{ %} + interface(REG_INTER); +%} + +// Double register operands +operand regD() +%{ + constraint(ALLOC_IN_RC(double_reg)); + match(RegD); + + format %{ %} + interface(REG_INTER); +%} + + +//----------Memory Operands---------------------------------------------------- +// Direct Memory Operand +// operand direct(immP addr) +// %{ +// match(addr); + +// format %{ "[$addr]" %} +// interface(MEMORY_INTER) %{ +// base(0xFFFFFFFF); +// index(0x4); +// scale(0x0); +// disp($addr); +// %} +// %} + +// Indirect Memory Operand +operand indirect(any_RegP reg) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(reg); + + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp(0x0); + %} +%} + +// Indirect Memory Plus Short Offset Operand +operand indOffset8(any_RegP reg, immL8 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + + format %{ "[$reg + $off (8-bit)]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp($off); + %} +%} + +// Indirect Memory Plus Long Offset Operand +operand indOffset32(any_RegP reg, immL32 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + + format %{ "[$reg + $off (32-bit)]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x4); + scale(0x0); + disp($off); + %} +%} + +// Indirect Memory Plus Index Register Plus Offset Operand +operand indIndexOffset(any_RegP reg, rRegL lreg, immL32 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP (AddP reg lreg) off); + + op_cost(10); + format %{"[$reg + $off + $lreg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($lreg); + scale(0x0); + disp($off); + %} +%} + +// Indirect Memory Plus Index Register Plus Offset Operand +operand indIndex(any_RegP reg, rRegL lreg) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg lreg); + + op_cost(10); + format %{"[$reg + $lreg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($lreg); + scale(0x0); + disp(0x0); + %} +%} + +// Indirect Memory Times Scale Plus Index Register +operand indIndexScale(any_RegP reg, rRegL lreg, immI2 scale) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg (LShiftL lreg scale)); + + op_cost(10); + format %{"[$reg + $lreg << $scale]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($lreg); + scale($scale); + disp(0x0); + %} +%} + +// Indirect Memory Times Scale Plus Index Register Plus Offset Operand +operand indIndexScaleOffset(any_RegP reg, immL32 off, rRegL lreg, immI2 scale) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP (AddP reg (LShiftL lreg scale)) off); + + op_cost(10); + format %{"[$reg + $off + $lreg << $scale]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($lreg); + scale($scale); + disp($off); + %} +%} + +// Indirect Memory Times Scale Plus Positive Index Register Plus Offset Operand +operand indPosIndexScaleOffset(any_RegP reg, immL32 off, rRegI idx, immI2 scale) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + predicate(n->in(2)->in(3)->in(1)->as_Type()->type()->is_long()->_lo >= 0); + match(AddP (AddP reg (LShiftL (ConvI2L idx) scale)) off); + + op_cost(10); + format %{"[$reg + $off + $idx << $scale]" %} + interface(MEMORY_INTER) %{ + base($reg); + index($idx); + scale($scale); + disp($off); + %} +%} + +//----------Special Memory Operands-------------------------------------------- +// Stack Slot Operand - This operand is used for loading and storing temporary +// values on the stack where a match requires a value to +// flow through memory. +operand stackSlotP(sRegP reg) +%{ + constraint(ALLOC_IN_RC(stack_slots)); + // No match rule because this operand is only generated in matching + + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0x4); // RSP + index(0x4); // No Index + scale(0x0); // No Scale + disp($reg); // Stack Offset + %} +%} + +operand stackSlotI(sRegI reg) +%{ + constraint(ALLOC_IN_RC(stack_slots)); + // No match rule because this operand is only generated in matching + + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0x4); // RSP + index(0x4); // No Index + scale(0x0); // No Scale + disp($reg); // Stack Offset + %} +%} + +operand stackSlotF(sRegF reg) +%{ + constraint(ALLOC_IN_RC(stack_slots)); + // No match rule because this operand is only generated in matching + + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0x4); // RSP + index(0x4); // No Index + scale(0x0); // No Scale + disp($reg); // Stack Offset + %} +%} + +operand stackSlotD(sRegD reg) +%{ + constraint(ALLOC_IN_RC(stack_slots)); + // No match rule because this operand is only generated in matching + + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0x4); // RSP + index(0x4); // No Index + scale(0x0); // No Scale + disp($reg); // Stack Offset + %} +%} +operand stackSlotL(sRegL reg) +%{ + constraint(ALLOC_IN_RC(stack_slots)); + // No match rule because this operand is only generated in matching + + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base(0x4); // RSP + index(0x4); // No Index + scale(0x0); // No Scale + disp($reg); // Stack Offset + %} +%} + +//----------Conditional Branch Operands---------------------------------------- +// Comparison Op - This is the operation of the comparison, and is limited to +// the following set of codes: +// L (<), LE (<=), G (>), GE (>=), E (==), NE (!=) +// +// Other attributes of the comparison, such as unsignedness, are specified +// by the comparison instruction that sets a condition code flags register. +// That result is represented by a flags operand whose subtype is appropriate +// to the unsignedness (etc.) of the comparison. +// +// Later, the instruction which matches both the Comparison Op (a Bool) and +// the flags (produced by the Cmp) specifies the coding of the comparison op +// by matching a specific subtype of Bool operand below, such as cmpOpU. + +// Comparision Code +operand cmpOp() +%{ + match(Bool); + + format %{ "" %} + interface(COND_INTER) %{ + equal(0x4); + not_equal(0x5); + less(0xC); + greater_equal(0xD); + less_equal(0xE); + greater(0xF); + %} +%} + +// Comparison Code, unsigned compare. Used by FP also, with +// C2 (unordered) turned into GT or LT already. The other bits +// C0 and C3 are turned into Carry & Zero flags. +operand cmpOpU() +%{ + match(Bool); + + format %{ "" %} + interface(COND_INTER) %{ + equal(0x4); + not_equal(0x5); + less(0x2); + greater_equal(0x3); + less_equal(0x6); + greater(0x7); + %} +%} + + +//----------OPERAND CLASSES---------------------------------------------------- +// Operand Classes are groups of operands that are used as to simplify +// instruction definitions by not requiring the AD writer to specify seperate +// instructions for every form of operand when the instruction accepts +// multiple operand types with the same basic encoding and format. The classic +// case of this is memory operands. + +opclass memory(indirect, indOffset8, indOffset32, indIndexOffset, indIndex, + indIndexScale, indIndexScaleOffset, indPosIndexScaleOffset); + +//----------PIPELINE----------------------------------------------------------- +// Rules which define the behavior of the target architectures pipeline. +pipeline %{ + +//----------ATTRIBUTES--------------------------------------------------------- +attributes %{ + variable_size_instructions; // Fixed size instructions + max_instructions_per_bundle = 3; // Up to 3 instructions per bundle + instruction_unit_size = 1; // An instruction is 1 bytes long + instruction_fetch_unit_size = 16; // The processor fetches one line + instruction_fetch_units = 1; // of 16 bytes + + // List of nop instructions + nops( MachNop ); +%} + +//----------RESOURCES---------------------------------------------------------- +// Resources are the functional units available to the machine + +// Generic P2/P3 pipeline +// 3 decoders, only D0 handles big operands; a "bundle" is the limit of +// 3 instructions decoded per cycle. +// 2 load/store ops per cycle, 1 branch, 1 FPU, +// 3 ALU op, only ALU0 handles mul instructions. +resources( D0, D1, D2, DECODE = D0 | D1 | D2, + MS0, MS1, MS2, MEM = MS0 | MS1 | MS2, + BR, FPU, + ALU0, ALU1, ALU2, ALU = ALU0 | ALU1 | ALU2); + +//----------PIPELINE DESCRIPTION----------------------------------------------- +// Pipeline Description specifies the stages in the machine's pipeline + +// Generic P2/P3 pipeline +pipe_desc(S0, S1, S2, S3, S4, S5); + +//----------PIPELINE CLASSES--------------------------------------------------- +// Pipeline Classes describe the stages in which input and output are +// referenced by the hardware pipeline. + +// Naming convention: ialu or fpu +// Then: _reg +// Then: _reg if there is a 2nd register +// Then: _long if it's a pair of instructions implementing a long +// Then: _fat if it requires the big decoder +// Or: _mem if it requires the big decoder and a memory unit. + +// Integer ALU reg operation +pipe_class ialu_reg(rRegI dst) +%{ + single_instruction; + dst : S4(write); + dst : S3(read); + DECODE : S0; // any decoder + ALU : S3; // any alu +%} + +// Long ALU reg operation +pipe_class ialu_reg_long(rRegL dst) +%{ + instruction_count(2); + dst : S4(write); + dst : S3(read); + DECODE : S0(2); // any 2 decoders + ALU : S3(2); // both alus +%} + +// Integer ALU reg operation using big decoder +pipe_class ialu_reg_fat(rRegI dst) +%{ + single_instruction; + dst : S4(write); + dst : S3(read); + D0 : S0; // big decoder only + ALU : S3; // any alu +%} + +// Long ALU reg operation using big decoder +pipe_class ialu_reg_long_fat(rRegL dst) +%{ + instruction_count(2); + dst : S4(write); + dst : S3(read); + D0 : S0(2); // big decoder only; twice + ALU : S3(2); // any 2 alus +%} + +// Integer ALU reg-reg operation +pipe_class ialu_reg_reg(rRegI dst, rRegI src) +%{ + single_instruction; + dst : S4(write); + src : S3(read); + DECODE : S0; // any decoder + ALU : S3; // any alu +%} + +// Long ALU reg-reg operation +pipe_class ialu_reg_reg_long(rRegL dst, rRegL src) +%{ + instruction_count(2); + dst : S4(write); + src : S3(read); + DECODE : S0(2); // any 2 decoders + ALU : S3(2); // both alus +%} + +// Integer ALU reg-reg operation +pipe_class ialu_reg_reg_fat(rRegI dst, memory src) +%{ + single_instruction; + dst : S4(write); + src : S3(read); + D0 : S0; // big decoder only + ALU : S3; // any alu +%} + +// Long ALU reg-reg operation +pipe_class ialu_reg_reg_long_fat(rRegL dst, rRegL src) +%{ + instruction_count(2); + dst : S4(write); + src : S3(read); + D0 : S0(2); // big decoder only; twice + ALU : S3(2); // both alus +%} + +// Integer ALU reg-mem operation +pipe_class ialu_reg_mem(rRegI dst, memory mem) +%{ + single_instruction; + dst : S5(write); + mem : S3(read); + D0 : S0; // big decoder only + ALU : S4; // any alu + MEM : S3; // any mem +%} + +// Integer mem operation (prefetch) +pipe_class ialu_mem(memory mem) +%{ + single_instruction; + mem : S3(read); + D0 : S0; // big decoder only + MEM : S3; // any mem +%} + +// Integer Store to Memory +pipe_class ialu_mem_reg(memory mem, rRegI src) +%{ + single_instruction; + mem : S3(read); + src : S5(read); + D0 : S0; // big decoder only + ALU : S4; // any alu + MEM : S3; +%} + +// // Long Store to Memory +// pipe_class ialu_mem_long_reg(memory mem, rRegL src) +// %{ +// instruction_count(2); +// mem : S3(read); +// src : S5(read); +// D0 : S0(2); // big decoder only; twice +// ALU : S4(2); // any 2 alus +// MEM : S3(2); // Both mems +// %} + +// Integer Store to Memory +pipe_class ialu_mem_imm(memory mem) +%{ + single_instruction; + mem : S3(read); + D0 : S0; // big decoder only + ALU : S4; // any alu + MEM : S3; +%} + +// Integer ALU0 reg-reg operation +pipe_class ialu_reg_reg_alu0(rRegI dst, rRegI src) +%{ + single_instruction; + dst : S4(write); + src : S3(read); + D0 : S0; // Big decoder only + ALU0 : S3; // only alu0 +%} + +// Integer ALU0 reg-mem operation +pipe_class ialu_reg_mem_alu0(rRegI dst, memory mem) +%{ + single_instruction; + dst : S5(write); + mem : S3(read); + D0 : S0; // big decoder only + ALU0 : S4; // ALU0 only + MEM : S3; // any mem +%} + +// Integer ALU reg-reg operation +pipe_class ialu_cr_reg_reg(rFlagsReg cr, rRegI src1, rRegI src2) +%{ + single_instruction; + cr : S4(write); + src1 : S3(read); + src2 : S3(read); + DECODE : S0; // any decoder + ALU : S3; // any alu +%} + +// Integer ALU reg-imm operation +pipe_class ialu_cr_reg_imm(rFlagsReg cr, rRegI src1) +%{ + single_instruction; + cr : S4(write); + src1 : S3(read); + DECODE : S0; // any decoder + ALU : S3; // any alu +%} + +// Integer ALU reg-mem operation +pipe_class ialu_cr_reg_mem(rFlagsReg cr, rRegI src1, memory src2) +%{ + single_instruction; + cr : S4(write); + src1 : S3(read); + src2 : S3(read); + D0 : S0; // big decoder only + ALU : S4; // any alu + MEM : S3; +%} + +// Conditional move reg-reg +pipe_class pipe_cmplt( rRegI p, rRegI q, rRegI y) +%{ + instruction_count(4); + y : S4(read); + q : S3(read); + p : S3(read); + DECODE : S0(4); // any decoder +%} + +// Conditional move reg-reg +pipe_class pipe_cmov_reg( rRegI dst, rRegI src, rFlagsReg cr) +%{ + single_instruction; + dst : S4(write); + src : S3(read); + cr : S3(read); + DECODE : S0; // any decoder +%} + +// Conditional move reg-mem +pipe_class pipe_cmov_mem( rFlagsReg cr, rRegI dst, memory src) +%{ + single_instruction; + dst : S4(write); + src : S3(read); + cr : S3(read); + DECODE : S0; // any decoder + MEM : S3; +%} + +// Conditional move reg-reg long +pipe_class pipe_cmov_reg_long( rFlagsReg cr, rRegL dst, rRegL src) +%{ + single_instruction; + dst : S4(write); + src : S3(read); + cr : S3(read); + DECODE : S0(2); // any 2 decoders +%} + +// XXX +// // Conditional move double reg-reg +// pipe_class pipe_cmovD_reg( rFlagsReg cr, regDPR1 dst, regD src) +// %{ +// single_instruction; +// dst : S4(write); +// src : S3(read); +// cr : S3(read); +// DECODE : S0; // any decoder +// %} + +// Float reg-reg operation +pipe_class fpu_reg(regD dst) +%{ + instruction_count(2); + dst : S3(read); + DECODE : S0(2); // any 2 decoders + FPU : S3; +%} + +// Float reg-reg operation +pipe_class fpu_reg_reg(regD dst, regD src) +%{ + instruction_count(2); + dst : S4(write); + src : S3(read); + DECODE : S0(2); // any 2 decoders + FPU : S3; +%} + +// Float reg-reg operation +pipe_class fpu_reg_reg_reg(regD dst, regD src1, regD src2) +%{ + instruction_count(3); + dst : S4(write); + src1 : S3(read); + src2 : S3(read); + DECODE : S0(3); // any 3 decoders + FPU : S3(2); +%} + +// Float reg-reg operation +pipe_class fpu_reg_reg_reg_reg(regD dst, regD src1, regD src2, regD src3) +%{ + instruction_count(4); + dst : S4(write); + src1 : S3(read); + src2 : S3(read); + src3 : S3(read); + DECODE : S0(4); // any 3 decoders + FPU : S3(2); +%} + +// Float reg-reg operation +pipe_class fpu_reg_mem_reg_reg(regD dst, memory src1, regD src2, regD src3) +%{ + instruction_count(4); + dst : S4(write); + src1 : S3(read); + src2 : S3(read); + src3 : S3(read); + DECODE : S1(3); // any 3 decoders + D0 : S0; // Big decoder only + FPU : S3(2); + MEM : S3; +%} + +// Float reg-mem operation +pipe_class fpu_reg_mem(regD dst, memory mem) +%{ + instruction_count(2); + dst : S5(write); + mem : S3(read); + D0 : S0; // big decoder only + DECODE : S1; // any decoder for FPU POP + FPU : S4; + MEM : S3; // any mem +%} + +// Float reg-mem operation +pipe_class fpu_reg_reg_mem(regD dst, regD src1, memory mem) +%{ + instruction_count(3); + dst : S5(write); + src1 : S3(read); + mem : S3(read); + D0 : S0; // big decoder only + DECODE : S1(2); // any decoder for FPU POP + FPU : S4; + MEM : S3; // any mem +%} + +// Float mem-reg operation +pipe_class fpu_mem_reg(memory mem, regD src) +%{ + instruction_count(2); + src : S5(read); + mem : S3(read); + DECODE : S0; // any decoder for FPU PUSH + D0 : S1; // big decoder only + FPU : S4; + MEM : S3; // any mem +%} + +pipe_class fpu_mem_reg_reg(memory mem, regD src1, regD src2) +%{ + instruction_count(3); + src1 : S3(read); + src2 : S3(read); + mem : S3(read); + DECODE : S0(2); // any decoder for FPU PUSH + D0 : S1; // big decoder only + FPU : S4; + MEM : S3; // any mem +%} + +pipe_class fpu_mem_reg_mem(memory mem, regD src1, memory src2) +%{ + instruction_count(3); + src1 : S3(read); + src2 : S3(read); + mem : S4(read); + DECODE : S0; // any decoder for FPU PUSH + D0 : S0(2); // big decoder only + FPU : S4; + MEM : S3(2); // any mem +%} + +pipe_class fpu_mem_mem(memory dst, memory src1) +%{ + instruction_count(2); + src1 : S3(read); + dst : S4(read); + D0 : S0(2); // big decoder only + MEM : S3(2); // any mem +%} + +pipe_class fpu_mem_mem_mem(memory dst, memory src1, memory src2) +%{ + instruction_count(3); + src1 : S3(read); + src2 : S3(read); + dst : S4(read); + D0 : S0(3); // big decoder only + FPU : S4; + MEM : S3(3); // any mem +%} + +pipe_class fpu_mem_reg_con(memory mem, regD src1) +%{ + instruction_count(3); + src1 : S4(read); + mem : S4(read); + DECODE : S0; // any decoder for FPU PUSH + D0 : S0(2); // big decoder only + FPU : S4; + MEM : S3(2); // any mem +%} + +// Float load constant +pipe_class fpu_reg_con(regD dst) +%{ + instruction_count(2); + dst : S5(write); + D0 : S0; // big decoder only for the load + DECODE : S1; // any decoder for FPU POP + FPU : S4; + MEM : S3; // any mem +%} + +// Float load constant +pipe_class fpu_reg_reg_con(regD dst, regD src) +%{ + instruction_count(3); + dst : S5(write); + src : S3(read); + D0 : S0; // big decoder only for the load + DECODE : S1(2); // any decoder for FPU POP + FPU : S4; + MEM : S3; // any mem +%} + +// UnConditional branch +pipe_class pipe_jmp(label labl) +%{ + single_instruction; + BR : S3; +%} + +// Conditional branch +pipe_class pipe_jcc(cmpOp cmp, rFlagsReg cr, label labl) +%{ + single_instruction; + cr : S1(read); + BR : S3; +%} + +// Allocation idiom +pipe_class pipe_cmpxchg(rRegP dst, rRegP heap_ptr) +%{ + instruction_count(1); force_serialization; + fixed_latency(6); + heap_ptr : S3(read); + DECODE : S0(3); + D0 : S2; + MEM : S3; + ALU : S3(2); + dst : S5(write); + BR : S5; +%} + +// Generic big/slow expanded idiom +pipe_class pipe_slow() +%{ + instruction_count(10); multiple_bundles; force_serialization; + fixed_latency(100); + D0 : S0(2); + MEM : S3(2); +%} + +// The real do-nothing guy +pipe_class empty() +%{ + instruction_count(0); +%} + +// Define the class for the Nop node +define +%{ + MachNop = empty; +%} + +%} + +//----------INSTRUCTIONS------------------------------------------------------- +// +// match -- States which machine-independent subtree may be replaced +// by this instruction. +// ins_cost -- The estimated cost of this instruction is used by instruction +// selection to identify a minimum cost tree of machine +// instructions that matches a tree of machine-independent +// instructions. +// format -- A string providing the disassembly for this instruction. +// The value of an instruction's operand may be inserted +// by referring to it with a '$' prefix. +// opcode -- Three instruction opcodes may be provided. These are referred +// to within an encode class as $primary, $secondary, and $tertiary +// rrspectively. The primary opcode is commonly used to +// indicate the type of machine instruction, while secondary +// and tertiary are often used for prefix options or addressing +// modes. +// ins_encode -- A list of encode classes with parameters. The encode class +// name must have been defined in an 'enc_class' specification +// in the encode section of the architecture description. + + +//----------Load/Store/Move Instructions--------------------------------------- +//----------Load Instructions-------------------------------------------------- + +// Load Byte (8 bit signed) +instruct loadB(rRegI dst, memory mem) +%{ + match(Set dst (LoadB mem)); + + ins_cost(125); + format %{ "movsbl $dst, $mem\t# byte" %} + opcode(0x0F, 0xBE); + ins_encode(REX_reg_mem(dst, mem), OpcP, OpcS, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); +%} + +// Load Byte (8 bit signed) into long +// instruct loadB2L(rRegL dst, memory mem) +// %{ +// match(Set dst (ConvI2L (LoadB mem))); + +// ins_cost(125); +// format %{ "movsbq $dst, $mem\t# byte -> long" %} +// opcode(0x0F, 0xBE); +// ins_encode(REX_reg_mem_wide(dst, mem), OpcP, OpcS, reg_mem(dst, mem)); +// ins_pipe(ialu_reg_mem); +// %} + +// Load Byte (8 bit UNsigned) +instruct loadUB(rRegI dst, memory mem, immI_255 bytemask) +%{ + match(Set dst (AndI (LoadB mem) bytemask)); + + ins_cost(125); + format %{ "movzbl $dst, $mem\t# ubyte" %} + opcode(0x0F, 0xB6); + ins_encode(REX_reg_mem(dst, mem), OpcP, OpcS, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); +%} + +// Load Byte (8 bit UNsigned) into long +// instruct loadUB2L(rRegL dst, memory mem, immI_255 bytemask) +// %{ +// match(Set dst (ConvI2L (AndI (LoadB mem) bytemask))); + +// ins_cost(125); +// format %{ "movzbl $dst, $mem\t# ubyte -> long" %} +// opcode(0x0F, 0xB6); +// ins_encode(REX_reg_mem(dst, mem), OpcP, OpcS, reg_mem(dst, mem)); +// ins_pipe(ialu_reg_mem); +// %} + +// Load Short (16 bit signed) +instruct loadS(rRegI dst, memory mem) +%{ + match(Set dst (LoadS mem)); + + ins_cost(125); // XXX + format %{ "movswl $dst, $mem\t# short" %} + opcode(0x0F, 0xBF); + ins_encode(REX_reg_mem(dst, mem), OpcP, OpcS, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); +%} + +// Load Short (16 bit signed) into long +// instruct loadS2L(rRegL dst, memory mem) +// %{ +// match(Set dst (ConvI2L (LoadS mem))); + +// ins_cost(125); // XXX +// format %{ "movswq $dst, $mem\t# short -> long" %} +// opcode(0x0F, 0xBF); +// ins_encode(REX_reg_mem_wide(dst, mem), OpcP, OpcS, reg_mem(dst, mem)); +// ins_pipe(ialu_reg_mem); +// %} + +// Load Char (16 bit UNsigned) +instruct loadC(rRegI dst, memory mem) +%{ + match(Set dst (LoadC mem)); + + ins_cost(125); + format %{ "movzwl $dst, $mem\t# char" %} + opcode(0x0F, 0xB7); + ins_encode(REX_reg_mem(dst, mem), OpcP, OpcS, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); +%} + +// Load Char (16 bit UNsigned) into long +// instruct loadC2L(rRegL dst, memory mem) +// %{ +// match(Set dst (ConvI2L (LoadC mem))); + +// ins_cost(125); +// format %{ "movzwl $dst, $mem\t# char -> long" %} +// opcode(0x0F, 0xB7); +// ins_encode(REX_reg_mem(dst, mem), OpcP, OpcS, reg_mem(dst, mem)); +// ins_pipe(ialu_reg_mem); +// %} + +// Load Integer +instruct loadI(rRegI dst, memory mem) +%{ + match(Set dst (LoadI mem)); + + ins_cost(125); // XXX + format %{ "movl $dst, $mem\t# int" %} + opcode(0x8B); + ins_encode(REX_reg_mem(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); +%} + +// Load Long +instruct loadL(rRegL dst, memory mem) +%{ + match(Set dst (LoadL mem)); + + ins_cost(125); // XXX + format %{ "movq $dst, $mem\t# long" %} + opcode(0x8B); + ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); // XXX +%} + +// Load Range +instruct loadRange(rRegI dst, memory mem) +%{ + match(Set dst (LoadRange mem)); + + ins_cost(125); // XXX + format %{ "movl $dst, $mem\t# range" %} + opcode(0x8B); + ins_encode(REX_reg_mem(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); +%} + +// Load Pointer +instruct loadP(rRegP dst, memory mem) +%{ + match(Set dst (LoadP mem)); + + ins_cost(125); // XXX + format %{ "movq $dst, $mem\t# ptr" %} + opcode(0x8B); + ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); // XXX +%} + +// Load Klass Pointer +instruct loadKlass(rRegP dst, memory mem) +%{ + match(Set dst (LoadKlass mem)); + + ins_cost(125); // XXX + format %{ "movq $dst, $mem\t# class" %} + opcode(0x8B); + ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); // XXX +%} + +// Load Float +instruct loadF(regF dst, memory mem) +%{ + match(Set dst (LoadF mem)); + + ins_cost(145); // XXX + format %{ "movss $dst, $mem\t# float" %} + opcode(0xF3, 0x0F, 0x10); + ins_encode(OpcP, REX_reg_mem(dst, mem), OpcS, OpcT, reg_mem(dst, mem)); + ins_pipe(pipe_slow); // XXX +%} + +// Load Double +instruct loadD_partial(regD dst, memory mem) +%{ + predicate(!UseXmmLoadAndClearUpper); + match(Set dst (LoadD mem)); + + ins_cost(145); // XXX + format %{ "movlpd $dst, $mem\t# double" %} + opcode(0x66, 0x0F, 0x12); + ins_encode(OpcP, REX_reg_mem(dst, mem), OpcS, OpcT, reg_mem(dst, mem)); + ins_pipe(pipe_slow); // XXX +%} + +instruct loadD(regD dst, memory mem) +%{ + predicate(UseXmmLoadAndClearUpper); + match(Set dst (LoadD mem)); + + ins_cost(145); // XXX + format %{ "movsd $dst, $mem\t# double" %} + opcode(0xF2, 0x0F, 0x10); + ins_encode(OpcP, REX_reg_mem(dst, mem), OpcS, OpcT, reg_mem(dst, mem)); + ins_pipe(pipe_slow); // XXX +%} + +// Load Aligned Packed Byte to XMM register +instruct loadA8B(regD dst, memory mem) %{ + match(Set dst (Load8B mem)); + ins_cost(125); + format %{ "MOVQ $dst,$mem\t! packed8B" %} + ins_encode( movq_ld(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Load Aligned Packed Short to XMM register +instruct loadA4S(regD dst, memory mem) %{ + match(Set dst (Load4S mem)); + ins_cost(125); + format %{ "MOVQ $dst,$mem\t! packed4S" %} + ins_encode( movq_ld(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Load Aligned Packed Char to XMM register +instruct loadA4C(regD dst, memory mem) %{ + match(Set dst (Load4C mem)); + ins_cost(125); + format %{ "MOVQ $dst,$mem\t! packed4C" %} + ins_encode( movq_ld(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Load Aligned Packed Integer to XMM register +instruct load2IU(regD dst, memory mem) %{ + match(Set dst (Load2I mem)); + ins_cost(125); + format %{ "MOVQ $dst,$mem\t! packed2I" %} + ins_encode( movq_ld(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Load Aligned Packed Single to XMM +instruct loadA2F(regD dst, memory mem) %{ + match(Set dst (Load2F mem)); + ins_cost(145); + format %{ "MOVQ $dst,$mem\t! packed2F" %} + ins_encode( movq_ld(dst, mem)); + ins_pipe( pipe_slow ); +%} + +// Load Effective Address +instruct leaP8(rRegP dst, indOffset8 mem) +%{ + match(Set dst mem); + + ins_cost(110); // XXX + format %{ "leaq $dst, $mem\t# ptr 8" %} + opcode(0x8D); + ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_reg_fat); +%} + +instruct leaP32(rRegP dst, indOffset32 mem) +%{ + match(Set dst mem); + + ins_cost(110); + format %{ "leaq $dst, $mem\t# ptr 32" %} + opcode(0x8D); + ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_reg_fat); +%} + +// instruct leaPIdx(rRegP dst, indIndex mem) +// %{ +// match(Set dst mem); + +// ins_cost(110); +// format %{ "leaq $dst, $mem\t# ptr idx" %} +// opcode(0x8D); +// ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); +// ins_pipe(ialu_reg_reg_fat); +// %} + +instruct leaPIdxOff(rRegP dst, indIndexOffset mem) +%{ + match(Set dst mem); + + ins_cost(110); + format %{ "leaq $dst, $mem\t# ptr idxoff" %} + opcode(0x8D); + ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_reg_fat); +%} + +instruct leaPIdxScale(rRegP dst, indIndexScale mem) +%{ + match(Set dst mem); + + ins_cost(110); + format %{ "leaq $dst, $mem\t# ptr idxscale" %} + opcode(0x8D); + ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_reg_fat); +%} + +instruct leaPIdxScaleOff(rRegP dst, indIndexScaleOffset mem) +%{ + match(Set dst mem); + + ins_cost(110); + format %{ "leaq $dst, $mem\t# ptr idxscaleoff" %} + opcode(0x8D); + ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_reg_fat); +%} + +instruct loadConI(rRegI dst, immI src) +%{ + match(Set dst src); + + format %{ "movl $dst, $src\t# int" %} + ins_encode(load_immI(dst, src)); + ins_pipe(ialu_reg_fat); // XXX +%} + +instruct loadConI0(rRegI dst, immI0 src, rFlagsReg cr) +%{ + match(Set dst src); + effect(KILL cr); + + ins_cost(50); + format %{ "xorl $dst, $dst\t# int" %} + opcode(0x33); /* + rd */ + ins_encode(REX_reg_reg(dst, dst), OpcP, reg_reg(dst, dst)); + ins_pipe(ialu_reg); +%} + +instruct loadConL(rRegL dst, immL src) +%{ + match(Set dst src); + + ins_cost(150); + format %{ "movq $dst, $src\t# long" %} + ins_encode(load_immL(dst, src)); + ins_pipe(ialu_reg); +%} + +instruct loadConL0(rRegL dst, immL0 src, rFlagsReg cr) +%{ + match(Set dst src); + effect(KILL cr); + + ins_cost(50); + format %{ "xorl $dst, $dst\t# long" %} + opcode(0x33); /* + rd */ + ins_encode(REX_reg_reg(dst, dst), OpcP, reg_reg(dst, dst)); + ins_pipe(ialu_reg); // XXX +%} + +instruct loadConUL32(rRegL dst, immUL32 src) +%{ + match(Set dst src); + + ins_cost(60); + format %{ "movl $dst, $src\t# long (unsigned 32-bit)" %} + ins_encode(load_immUL32(dst, src)); + ins_pipe(ialu_reg); +%} + +instruct loadConL32(rRegL dst, immL32 src) +%{ + match(Set dst src); + + ins_cost(70); + format %{ "movq $dst, $src\t# long (32-bit)" %} + ins_encode(load_immL32(dst, src)); + ins_pipe(ialu_reg); +%} + +instruct loadConP(rRegP dst, immP src) +%{ + match(Set dst src); + + format %{ "movq $dst, $src\t# ptr" %} + ins_encode(load_immP(dst, src)); + ins_pipe(ialu_reg_fat); // XXX +%} + +instruct loadConP0(rRegP dst, immP0 src, rFlagsReg cr) +%{ + match(Set dst src); + effect(KILL cr); + + ins_cost(50); + format %{ "xorl $dst, $dst\t# ptr" %} + opcode(0x33); /* + rd */ + ins_encode(REX_reg_reg(dst, dst), OpcP, reg_reg(dst, dst)); + ins_pipe(ialu_reg); +%} + +instruct loadConP31(rRegP dst, immP31 src, rFlagsReg cr) +%{ + match(Set dst src); + effect(KILL cr); + + ins_cost(60); + format %{ "movl $dst, $src\t# ptr (positive 32-bit)" %} + ins_encode(load_immP31(dst, src)); + ins_pipe(ialu_reg); +%} + +instruct loadConF(regF dst, immF src) +%{ + match(Set dst src); + ins_cost(125); + + format %{ "movss $dst, [$src]" %} + ins_encode(load_conF(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct loadConF0(regF dst, immF0 src) +%{ + match(Set dst src); + ins_cost(100); + + format %{ "xorps $dst, $dst\t# float 0.0" %} + opcode(0x0F, 0x57); + ins_encode(REX_reg_reg(dst, dst), OpcP, OpcS, reg_reg(dst, dst)); + ins_pipe(pipe_slow); +%} + +// Use the same format since predicate() can not be used here. +instruct loadConD(regD dst, immD src) +%{ + match(Set dst src); + ins_cost(125); + + format %{ "movsd $dst, [$src]" %} + ins_encode(load_conD(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct loadConD0(regD dst, immD0 src) +%{ + match(Set dst src); + ins_cost(100); + + format %{ "xorpd $dst, $dst\t# double 0.0" %} + opcode(0x66, 0x0F, 0x57); + ins_encode(OpcP, REX_reg_reg(dst, dst), OpcS, OpcT, reg_reg(dst, dst)); + ins_pipe(pipe_slow); +%} + +instruct loadSSI(rRegI dst, stackSlotI src) +%{ + match(Set dst src); + + ins_cost(125); + format %{ "movl $dst, $src\t# int stk" %} + opcode(0x8B); + ins_encode(REX_reg_mem(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +instruct loadSSL(rRegL dst, stackSlotL src) +%{ + match(Set dst src); + + ins_cost(125); + format %{ "movq $dst, $src\t# long stk" %} + opcode(0x8B); + ins_encode(REX_reg_mem_wide(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +instruct loadSSP(rRegP dst, stackSlotP src) +%{ + match(Set dst src); + + ins_cost(125); + format %{ "movq $dst, $src\t# ptr stk" %} + opcode(0x8B); + ins_encode(REX_reg_mem_wide(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +instruct loadSSF(regF dst, stackSlotF src) +%{ + match(Set dst src); + + ins_cost(125); + format %{ "movss $dst, $src\t# float stk" %} + opcode(0xF3, 0x0F, 0x10); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +// Use the same format since predicate() can not be used here. +instruct loadSSD(regD dst, stackSlotD src) +%{ + match(Set dst src); + + ins_cost(125); + format %{ "movsd $dst, $src\t# double stk" %} + ins_encode %{ + __ movdbl($dst$$XMMRegister, Address(rsp, $src$$disp)); + %} + ins_pipe(pipe_slow); // XXX +%} + +// Prefetch instructions. +// Must be safe to execute with invalid address (cannot fault). + +instruct prefetchr( memory mem ) %{ + predicate(ReadPrefetchInstr==3); + match(PrefetchRead mem); + ins_cost(125); + + format %{ "PREFETCHR $mem\t# Prefetch into level 1 cache" %} + opcode(0x0F, 0x0D); /* Opcode 0F 0D /0 */ + ins_encode(REX_mem(mem), OpcP, OpcS, RM_opc_mem(0x00, mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchrNTA( memory mem ) %{ + predicate(ReadPrefetchInstr==0); + match(PrefetchRead mem); + ins_cost(125); + + format %{ "PREFETCHNTA $mem\t# Prefetch into non-temporal cache for read" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /0 */ + ins_encode(REX_mem(mem), OpcP, OpcS, RM_opc_mem(0x00, mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchrT0( memory mem ) %{ + predicate(ReadPrefetchInstr==1); + match(PrefetchRead mem); + ins_cost(125); + + format %{ "PREFETCHT0 $mem\t# prefetch into L1 and L2 caches for read" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /1 */ + ins_encode(REX_mem(mem), OpcP, OpcS, RM_opc_mem(0x01, mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchrT2( memory mem ) %{ + predicate(ReadPrefetchInstr==2); + match(PrefetchRead mem); + ins_cost(125); + + format %{ "PREFETCHT2 $mem\t# prefetch into L2 caches for read" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /3 */ + ins_encode(REX_mem(mem), OpcP, OpcS, RM_opc_mem(0x03, mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchw( memory mem ) %{ + predicate(AllocatePrefetchInstr==3); + match(PrefetchWrite mem); + ins_cost(125); + + format %{ "PREFETCHW $mem\t# Prefetch into level 1 cache and mark modified" %} + opcode(0x0F, 0x0D); /* Opcode 0F 0D /1 */ + ins_encode(REX_mem(mem), OpcP, OpcS, RM_opc_mem(0x01, mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchwNTA( memory mem ) %{ + predicate(AllocatePrefetchInstr==0); + match(PrefetchWrite mem); + ins_cost(125); + + format %{ "PREFETCHNTA $mem\t# Prefetch to non-temporal cache for write" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /0 */ + ins_encode(REX_mem(mem), OpcP, OpcS, RM_opc_mem(0x00, mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchwT0( memory mem ) %{ + predicate(AllocatePrefetchInstr==1); + match(PrefetchWrite mem); + ins_cost(125); + + format %{ "PREFETCHT0 $mem\t# Prefetch to level 1 and 2 caches for write" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /1 */ + ins_encode(REX_mem(mem), OpcP, OpcS, RM_opc_mem(0x01, mem)); + ins_pipe(ialu_mem); +%} + +instruct prefetchwT2( memory mem ) %{ + predicate(AllocatePrefetchInstr==2); + match(PrefetchWrite mem); + ins_cost(125); + + format %{ "PREFETCHT2 $mem\t# Prefetch to level 2 cache for write" %} + opcode(0x0F, 0x18); /* Opcode 0F 18 /3 */ + ins_encode(REX_mem(mem), OpcP, OpcS, RM_opc_mem(0x03, mem)); + ins_pipe(ialu_mem); +%} + +//----------Store Instructions------------------------------------------------- + +// Store Byte +instruct storeB(memory mem, rRegI src) +%{ + match(Set mem (StoreB mem src)); + + ins_cost(125); // XXX + format %{ "movb $mem, $src\t# byte" %} + opcode(0x88); + ins_encode(REX_breg_mem(src, mem), OpcP, reg_mem(src, mem)); + ins_pipe(ialu_mem_reg); +%} + +// Store Char/Short +instruct storeC(memory mem, rRegI src) +%{ + match(Set mem (StoreC mem src)); + + ins_cost(125); // XXX + format %{ "movw $mem, $src\t# char/short" %} + opcode(0x89); + ins_encode(SizePrefix, REX_reg_mem(src, mem), OpcP, reg_mem(src, mem)); + ins_pipe(ialu_mem_reg); +%} + +// Store Integer +instruct storeI(memory mem, rRegI src) +%{ + match(Set mem (StoreI mem src)); + + ins_cost(125); // XXX + format %{ "movl $mem, $src\t# int" %} + opcode(0x89); + ins_encode(REX_reg_mem(src, mem), OpcP, reg_mem(src, mem)); + ins_pipe(ialu_mem_reg); +%} + +// Store Long +instruct storeL(memory mem, rRegL src) +%{ + match(Set mem (StoreL mem src)); + + ins_cost(125); // XXX + format %{ "movq $mem, $src\t# long" %} + opcode(0x89); + ins_encode(REX_reg_mem_wide(src, mem), OpcP, reg_mem(src, mem)); + ins_pipe(ialu_mem_reg); // XXX +%} + +// Store Pointer +instruct storeP(memory mem, any_RegP src) +%{ + match(Set mem (StoreP mem src)); + + ins_cost(125); // XXX + format %{ "movq $mem, $src\t# ptr" %} + opcode(0x89); + ins_encode(REX_reg_mem_wide(src, mem), OpcP, reg_mem(src, mem)); + ins_pipe(ialu_mem_reg); +%} + +// Store NULL Pointer, mark word, or other simple pointer constant. +instruct storeImmP(memory mem, immP31 src) +%{ + match(Set mem (StoreP mem src)); + + ins_cost(125); // XXX + format %{ "movq $mem, $src\t# ptr" %} + opcode(0xC7); /* C7 /0 */ + ins_encode(REX_mem_wide(mem), OpcP, RM_opc_mem(0x00, mem), Con32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Store Integer Immediate +instruct storeImmI(memory mem, immI src) +%{ + match(Set mem (StoreI mem src)); + + ins_cost(150); + format %{ "movl $mem, $src\t# int" %} + opcode(0xC7); /* C7 /0 */ + ins_encode(REX_mem(mem), OpcP, RM_opc_mem(0x00, mem), Con32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Store Long Immediate +instruct storeImmL(memory mem, immL32 src) +%{ + match(Set mem (StoreL mem src)); + + ins_cost(150); + format %{ "movq $mem, $src\t# long" %} + opcode(0xC7); /* C7 /0 */ + ins_encode(REX_mem_wide(mem), OpcP, RM_opc_mem(0x00, mem), Con32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Store Short/Char Immediate +instruct storeImmI16(memory mem, immI16 src) +%{ + predicate(UseStoreImmI16); + match(Set mem (StoreC mem src)); + + ins_cost(150); + format %{ "movw $mem, $src\t# short/char" %} + opcode(0xC7); /* C7 /0 Same as 32 store immediate with prefix */ + ins_encode(SizePrefix, REX_mem(mem), OpcP, RM_opc_mem(0x00, mem),Con16(src)); + ins_pipe(ialu_mem_imm); +%} + +// Store Byte Immediate +instruct storeImmB(memory mem, immI8 src) +%{ + match(Set mem (StoreB mem src)); + + ins_cost(150); // XXX + format %{ "movb $mem, $src\t# byte" %} + opcode(0xC6); /* C6 /0 */ + ins_encode(REX_mem(mem), OpcP, RM_opc_mem(0x00, mem), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Store Aligned Packed Byte XMM register to memory +instruct storeA8B(memory mem, regD src) %{ + match(Set mem (Store8B mem src)); + ins_cost(145); + format %{ "MOVQ $mem,$src\t! packed8B" %} + ins_encode( movq_st(mem, src)); + ins_pipe( pipe_slow ); +%} + +// Store Aligned Packed Char/Short XMM register to memory +instruct storeA4C(memory mem, regD src) %{ + match(Set mem (Store4C mem src)); + ins_cost(145); + format %{ "MOVQ $mem,$src\t! packed4C" %} + ins_encode( movq_st(mem, src)); + ins_pipe( pipe_slow ); +%} + +// Store Aligned Packed Integer XMM register to memory +instruct storeA2I(memory mem, regD src) %{ + match(Set mem (Store2I mem src)); + ins_cost(145); + format %{ "MOVQ $mem,$src\t! packed2I" %} + ins_encode( movq_st(mem, src)); + ins_pipe( pipe_slow ); +%} + +// Store CMS card-mark Immediate +instruct storeImmCM0(memory mem, immI0 src) +%{ + match(Set mem (StoreCM mem src)); + + ins_cost(150); // XXX + format %{ "movb $mem, $src\t# CMS card-mark byte 0" %} + opcode(0xC6); /* C6 /0 */ + ins_encode(REX_mem(mem), OpcP, RM_opc_mem(0x00, mem), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Store Aligned Packed Single Float XMM register to memory +instruct storeA2F(memory mem, regD src) %{ + match(Set mem (Store2F mem src)); + ins_cost(145); + format %{ "MOVQ $mem,$src\t! packed2F" %} + ins_encode( movq_st(mem, src)); + ins_pipe( pipe_slow ); +%} + +// Store Float +instruct storeF(memory mem, regF src) +%{ + match(Set mem (StoreF mem src)); + + ins_cost(95); // XXX + format %{ "movss $mem, $src\t# float" %} + opcode(0xF3, 0x0F, 0x11); + ins_encode(OpcP, REX_reg_mem(src, mem), OpcS, OpcT, reg_mem(src, mem)); + ins_pipe(pipe_slow); // XXX +%} + +// Store immediate Float value (it is faster than store from XMM register) +instruct storeF_imm(memory mem, immF src) +%{ + match(Set mem (StoreF mem src)); + + ins_cost(50); + format %{ "movl $mem, $src\t# float" %} + opcode(0xC7); /* C7 /0 */ + ins_encode(REX_mem(mem), OpcP, RM_opc_mem(0x00, mem), Con32F_as_bits(src)); + ins_pipe(ialu_mem_imm); +%} + +// Store Double +instruct storeD(memory mem, regD src) +%{ + match(Set mem (StoreD mem src)); + + ins_cost(95); // XXX + format %{ "movsd $mem, $src\t# double" %} + opcode(0xF2, 0x0F, 0x11); + ins_encode(OpcP, REX_reg_mem(src, mem), OpcS, OpcT, reg_mem(src, mem)); + ins_pipe(pipe_slow); // XXX +%} + +// Store immediate double 0.0 (it is faster than store from XMM register) +instruct storeD0_imm(memory mem, immD0 src) +%{ + match(Set mem (StoreD mem src)); + + ins_cost(50); + format %{ "movq $mem, $src\t# double 0." %} + opcode(0xC7); /* C7 /0 */ + ins_encode(REX_mem_wide(mem), OpcP, RM_opc_mem(0x00, mem), Con32F_as_bits(src)); + ins_pipe(ialu_mem_imm); +%} + +instruct storeSSI(stackSlotI dst, rRegI src) +%{ + match(Set dst src); + + ins_cost(100); + format %{ "movl $dst, $src\t# int stk" %} + opcode(0x89); + ins_encode(REX_reg_mem(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe( ialu_mem_reg ); +%} + +instruct storeSSL(stackSlotL dst, rRegL src) +%{ + match(Set dst src); + + ins_cost(100); + format %{ "movq $dst, $src\t# long stk" %} + opcode(0x89); + ins_encode(REX_reg_mem_wide(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +instruct storeSSP(stackSlotP dst, rRegP src) +%{ + match(Set dst src); + + ins_cost(100); + format %{ "movq $dst, $src\t# ptr stk" %} + opcode(0x89); + ins_encode(REX_reg_mem_wide(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +instruct storeSSF(stackSlotF dst, regF src) +%{ + match(Set dst src); + + ins_cost(95); // XXX + format %{ "movss $dst, $src\t# float stk" %} + opcode(0xF3, 0x0F, 0x11); + ins_encode(OpcP, REX_reg_mem(src, dst), OpcS, OpcT, reg_mem(src, dst)); + ins_pipe(pipe_slow); // XXX +%} + +instruct storeSSD(stackSlotD dst, regD src) +%{ + match(Set dst src); + + ins_cost(95); // XXX + format %{ "movsd $dst, $src\t# double stk" %} + opcode(0xF2, 0x0F, 0x11); + ins_encode(OpcP, REX_reg_mem(src, dst), OpcS, OpcT, reg_mem(src, dst)); + ins_pipe(pipe_slow); // XXX +%} + +//----------BSWAP Instructions------------------------------------------------- +instruct bytes_reverse_int(rRegI dst) %{ + match(Set dst (ReverseBytesI dst)); + + format %{ "bswapl $dst" %} + opcode(0x0F, 0xC8); /*Opcode 0F /C8 */ + ins_encode( REX_reg(dst), OpcP, opc2_reg(dst) ); + ins_pipe( ialu_reg ); +%} + +instruct bytes_reverse_long(rRegL dst) %{ + match(Set dst (ReverseBytesL dst)); + + format %{ "bswapq $dst" %} + + opcode(0x0F, 0xC8); /* Opcode 0F /C8 */ + ins_encode( REX_reg_wide(dst), OpcP, opc2_reg(dst) ); + ins_pipe( ialu_reg); +%} + +instruct loadI_reversed(rRegI dst, memory src) %{ + match(Set dst (ReverseBytesI (LoadI src))); + + format %{ "bswap_movl $dst, $src" %} + opcode(0x8B, 0x0F, 0xC8); /* Opcode 8B 0F C8 */ + ins_encode(REX_reg_mem(dst, src), OpcP, reg_mem(dst, src), REX_reg(dst), OpcS, opc3_reg(dst)); + ins_pipe( ialu_reg_mem ); +%} + +instruct loadL_reversed(rRegL dst, memory src) %{ + match(Set dst (ReverseBytesL (LoadL src))); + + format %{ "bswap_movq $dst, $src" %} + opcode(0x8B, 0x0F, 0xC8); /* Opcode 8B 0F C8 */ + ins_encode(REX_reg_mem_wide(dst, src), OpcP, reg_mem(dst, src), REX_reg_wide(dst), OpcS, opc3_reg(dst)); + ins_pipe( ialu_reg_mem ); +%} + +instruct storeI_reversed(memory dst, rRegI src) %{ + match(Set dst (StoreI dst (ReverseBytesI src))); + + format %{ "movl_bswap $dst, $src" %} + opcode(0x0F, 0xC8, 0x89); /* Opcode 0F C8 89 */ + ins_encode( REX_reg(src), OpcP, opc2_reg(src), REX_reg_mem(src, dst), OpcT, reg_mem(src, dst) ); + ins_pipe( ialu_mem_reg ); +%} + +instruct storeL_reversed(memory dst, rRegL src) %{ + match(Set dst (StoreL dst (ReverseBytesL src))); + + format %{ "movq_bswap $dst, $src" %} + opcode(0x0F, 0xC8, 0x89); /* Opcode 0F C8 89 */ + ins_encode( REX_reg_wide(src), OpcP, opc2_reg(src), REX_reg_mem_wide(src, dst), OpcT, reg_mem(src, dst) ); + ins_pipe( ialu_mem_reg ); +%} + +//----------MemBar Instructions----------------------------------------------- +// Memory barrier flavors + +instruct membar_acquire() +%{ + match(MemBarAcquire); + ins_cost(0); + + size(0); + format %{ "MEMBAR-acquire" %} + ins_encode(); + ins_pipe(empty); +%} + +instruct membar_acquire_lock() +%{ + match(MemBarAcquire); + predicate(Matcher::prior_fast_lock(n)); + ins_cost(0); + + size(0); + format %{ "MEMBAR-acquire (prior CMPXCHG in FastLock so empty encoding)" %} + ins_encode(); + ins_pipe(empty); +%} + +instruct membar_release() +%{ + match(MemBarRelease); + ins_cost(0); + + size(0); + format %{ "MEMBAR-release" %} + ins_encode(); + ins_pipe(empty); +%} + +instruct membar_release_lock() +%{ + match(MemBarRelease); + predicate(Matcher::post_fast_unlock(n)); + ins_cost(0); + + size(0); + format %{ "MEMBAR-release (a FastUnlock follows so empty encoding)" %} + ins_encode(); + ins_pipe(empty); +%} + +instruct membar_volatile() +%{ + match(MemBarVolatile); + ins_cost(400); + + format %{ "MEMBAR-volatile" %} + ins_encode(enc_membar_volatile); + ins_pipe(pipe_slow); +%} + +instruct unnecessary_membar_volatile() +%{ + match(MemBarVolatile); + predicate(Matcher::post_store_load_barrier(n)); + ins_cost(0); + + size(0); + format %{ "MEMBAR-volatile (unnecessary so empty encoding)" %} + ins_encode(); + ins_pipe(empty); +%} + +//----------Move Instructions-------------------------------------------------- + +instruct castX2P(rRegP dst, rRegL src) +%{ + match(Set dst (CastX2P src)); + + format %{ "movq $dst, $src\t# long->ptr" %} + ins_encode(enc_copy_wide(dst, src)); + ins_pipe(ialu_reg_reg); // XXX +%} + +instruct castP2X(rRegL dst, rRegP src) +%{ + match(Set dst (CastP2X src)); + + format %{ "movq $dst, $src\t# ptr -> long" %} + ins_encode(enc_copy_wide(dst, src)); + ins_pipe(ialu_reg_reg); // XXX +%} + +//----------Conditional Move--------------------------------------------------- +// Jump +// dummy instruction for generating temp registers +instruct jumpXtnd_offset(rRegL switch_val, immI2 shift, rRegI dest) %{ + match(Jump (LShiftL switch_val shift)); + ins_cost(350); + predicate(false); + effect(TEMP dest); + + format %{ "leaq $dest, table_base\n\t" + "jmp [$dest + $switch_val << $shift]\n\t" %} + ins_encode(jump_enc_offset(switch_val, shift, dest)); + ins_pipe(pipe_jmp); + ins_pc_relative(1); +%} + +instruct jumpXtnd_addr(rRegL switch_val, immI2 shift, immL32 offset, rRegI dest) %{ + match(Jump (AddL (LShiftL switch_val shift) offset)); + ins_cost(350); + effect(TEMP dest); + + format %{ "leaq $dest, table_base\n\t" + "jmp [$dest + $switch_val << $shift + $offset]\n\t" %} + ins_encode(jump_enc_addr(switch_val, shift, offset, dest)); + ins_pipe(pipe_jmp); + ins_pc_relative(1); +%} + +instruct jumpXtnd(rRegL switch_val, rRegI dest) %{ + match(Jump switch_val); + ins_cost(350); + effect(TEMP dest); + + format %{ "leaq $dest, table_base\n\t" + "jmp [$dest + $switch_val]\n\t" %} + ins_encode(jump_enc(switch_val, dest)); + ins_pipe(pipe_jmp); + ins_pc_relative(1); +%} + +// Conditional move +instruct cmovI_reg(rRegI dst, rRegI src, rFlagsReg cr, cmpOp cop) +%{ + match(Set dst (CMoveI (Binary cop cr) (Binary dst src))); + + ins_cost(200); // XXX + format %{ "cmovl$cop $dst, $src\t# signed, int" %} + opcode(0x0F, 0x40); + ins_encode(REX_reg_reg(dst, src), enc_cmov(cop), reg_reg(dst, src)); + ins_pipe(pipe_cmov_reg); +%} + +instruct cmovI_regU(rRegI dst, rRegI src, rFlagsRegU cr, cmpOpU cop) +%{ + match(Set dst (CMoveI (Binary cop cr) (Binary dst src))); + + ins_cost(200); // XXX + format %{ "cmovl$cop $dst, $src\t# unsigned, int" %} + opcode(0x0F, 0x40); + ins_encode(REX_reg_reg(dst, src), enc_cmov(cop), reg_reg(dst, src)); + ins_pipe(pipe_cmov_reg); +%} + +// Conditional move +instruct cmovI_mem(cmpOp cop, rFlagsReg cr, rRegI dst, memory src) +%{ + match(Set dst (CMoveI (Binary cop cr) (Binary dst (LoadI src)))); + + ins_cost(250); // XXX + format %{ "cmovl$cop $dst, $src\t# signed, int" %} + opcode(0x0F, 0x40); + ins_encode(REX_reg_mem(dst, src), enc_cmov(cop), reg_mem(dst, src)); + ins_pipe(pipe_cmov_mem); +%} + +// Conditional move +instruct cmovI_memU(cmpOpU cop, rFlagsRegU cr, rRegI dst, memory src) +%{ + match(Set dst (CMoveI (Binary cop cr) (Binary dst (LoadI src)))); + + ins_cost(250); // XXX + format %{ "cmovl$cop $dst, $src\t# unsigned, int" %} + opcode(0x0F, 0x40); + ins_encode(REX_reg_mem(dst, src), enc_cmov(cop), reg_mem(dst, src)); + ins_pipe(pipe_cmov_mem); +%} + +// Conditional move +instruct cmovP_reg(rRegP dst, rRegP src, rFlagsReg cr, cmpOp cop) +%{ + match(Set dst (CMoveP (Binary cop cr) (Binary dst src))); + + ins_cost(200); // XXX + format %{ "cmovq$cop $dst, $src\t# signed, ptr" %} + opcode(0x0F, 0x40); + ins_encode(REX_reg_reg_wide(dst, src), enc_cmov(cop), reg_reg(dst, src)); + ins_pipe(pipe_cmov_reg); // XXX +%} + +// Conditional move +instruct cmovP_regU(rRegP dst, rRegP src, rFlagsRegU cr, cmpOpU cop) +%{ + match(Set dst (CMoveP (Binary cop cr) (Binary dst src))); + + ins_cost(200); // XXX + format %{ "cmovq$cop $dst, $src\t# unsigned, ptr" %} + opcode(0x0F, 0x40); + ins_encode(REX_reg_reg_wide(dst, src), enc_cmov(cop), reg_reg(dst, src)); + ins_pipe(pipe_cmov_reg); // XXX +%} + +// DISABLED: Requires the ADLC to emit a bottom_type call that +// correctly meets the two pointer arguments; one is an incoming +// register but the other is a memory operand. ALSO appears to +// be buggy with implicit null checks. +// +//// Conditional move +//instruct cmovP_mem(cmpOp cop, rFlagsReg cr, rRegP dst, memory src) +//%{ +// match(Set dst (CMoveP (Binary cop cr) (Binary dst (LoadP src)))); +// ins_cost(250); +// format %{ "CMOV$cop $dst,$src\t# ptr" %} +// opcode(0x0F,0x40); +// ins_encode( enc_cmov(cop), reg_mem( dst, src ) ); +// ins_pipe( pipe_cmov_mem ); +//%} +// +//// Conditional move +//instruct cmovP_memU(cmpOpU cop, rFlagsRegU cr, rRegP dst, memory src) +//%{ +// match(Set dst (CMoveP (Binary cop cr) (Binary dst (LoadP src)))); +// ins_cost(250); +// format %{ "CMOV$cop $dst,$src\t# ptr" %} +// opcode(0x0F,0x40); +// ins_encode( enc_cmov(cop), reg_mem( dst, src ) ); +// ins_pipe( pipe_cmov_mem ); +//%} + +instruct cmovL_reg(cmpOp cop, rFlagsReg cr, rRegL dst, rRegL src) +%{ + match(Set dst (CMoveL (Binary cop cr) (Binary dst src))); + + ins_cost(200); // XXX + format %{ "cmovq$cop $dst, $src\t# signed, long" %} + opcode(0x0F, 0x40); + ins_encode(REX_reg_reg_wide(dst, src), enc_cmov(cop), reg_reg(dst, src)); + ins_pipe(pipe_cmov_reg); // XXX +%} + +instruct cmovL_mem(cmpOp cop, rFlagsReg cr, rRegL dst, memory src) +%{ + match(Set dst (CMoveL (Binary cop cr) (Binary dst (LoadL src)))); + + ins_cost(200); // XXX + format %{ "cmovq$cop $dst, $src\t# signed, long" %} + opcode(0x0F, 0x40); + ins_encode(REX_reg_mem_wide(dst, src), enc_cmov(cop), reg_mem(dst, src)); + ins_pipe(pipe_cmov_mem); // XXX +%} + +instruct cmovL_regU(cmpOpU cop, rFlagsRegU cr, rRegL dst, rRegL src) +%{ + match(Set dst (CMoveL (Binary cop cr) (Binary dst src))); + + ins_cost(200); // XXX + format %{ "cmovq$cop $dst, $src\t# unsigned, long" %} + opcode(0x0F, 0x40); + ins_encode(REX_reg_reg_wide(dst, src), enc_cmov(cop), reg_reg(dst, src)); + ins_pipe(pipe_cmov_reg); // XXX +%} + +instruct cmovL_memU(cmpOpU cop, rFlagsRegU cr, rRegL dst, memory src) +%{ + match(Set dst (CMoveL (Binary cop cr) (Binary dst (LoadL src)))); + + ins_cost(200); // XXX + format %{ "cmovq$cop $dst, $src\t# unsigned, long" %} + opcode(0x0F, 0x40); + ins_encode(REX_reg_mem_wide(dst, src), enc_cmov(cop), reg_mem(dst, src)); + ins_pipe(pipe_cmov_mem); // XXX +%} + +instruct cmovF_reg(cmpOp cop, rFlagsReg cr, regF dst, regF src) +%{ + match(Set dst (CMoveF (Binary cop cr) (Binary dst src))); + + ins_cost(200); // XXX + format %{ "jn$cop skip\t# signed cmove float\n\t" + "movss $dst, $src\n" + "skip:" %} + ins_encode(enc_cmovf_branch(cop, dst, src)); + ins_pipe(pipe_slow); +%} + +// instruct cmovF_mem(cmpOp cop, rFlagsReg cr, regF dst, memory src) +// %{ +// match(Set dst (CMoveF (Binary cop cr) (Binary dst (LoadL src)))); + +// ins_cost(200); // XXX +// format %{ "jn$cop skip\t# signed cmove float\n\t" +// "movss $dst, $src\n" +// "skip:" %} +// ins_encode(enc_cmovf_mem_branch(cop, dst, src)); +// ins_pipe(pipe_slow); +// %} + +instruct cmovF_regU(cmpOpU cop, rFlagsRegU cr, regF dst, regF src) +%{ + match(Set dst (CMoveF (Binary cop cr) (Binary dst src))); + + ins_cost(200); // XXX + format %{ "jn$cop skip\t# unsigned cmove float\n\t" + "movss $dst, $src\n" + "skip:" %} + ins_encode(enc_cmovf_branch(cop, dst, src)); + ins_pipe(pipe_slow); +%} + +instruct cmovD_reg(cmpOp cop, rFlagsReg cr, regD dst, regD src) +%{ + match(Set dst (CMoveD (Binary cop cr) (Binary dst src))); + + ins_cost(200); // XXX + format %{ "jn$cop skip\t# signed cmove double\n\t" + "movsd $dst, $src\n" + "skip:" %} + ins_encode(enc_cmovd_branch(cop, dst, src)); + ins_pipe(pipe_slow); +%} + +instruct cmovD_regU(cmpOpU cop, rFlagsRegU cr, regD dst, regD src) +%{ + match(Set dst (CMoveD (Binary cop cr) (Binary dst src))); + + ins_cost(200); // XXX + format %{ "jn$cop skip\t# unsigned cmove double\n\t" + "movsd $dst, $src\n" + "skip:" %} + ins_encode(enc_cmovd_branch(cop, dst, src)); + ins_pipe(pipe_slow); +%} + +//----------Arithmetic Instructions-------------------------------------------- +//----------Addition Instructions---------------------------------------------- + +instruct addI_rReg(rRegI dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (AddI dst src)); + effect(KILL cr); + + format %{ "addl $dst, $src\t# int" %} + opcode(0x03); + ins_encode(REX_reg_reg(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +instruct addI_rReg_imm(rRegI dst, immI src, rFlagsReg cr) +%{ + match(Set dst (AddI dst src)); + effect(KILL cr); + + format %{ "addl $dst, $src\t# int" %} + opcode(0x81, 0x00); /* /0 id */ + ins_encode(OpcSErm(dst, src), Con8or32(src)); + ins_pipe( ialu_reg ); +%} + +instruct addI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) +%{ + match(Set dst (AddI dst (LoadI src))); + effect(KILL cr); + + ins_cost(125); // XXX + format %{ "addl $dst, $src\t# int" %} + opcode(0x03); + ins_encode(REX_reg_mem(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +instruct addI_mem_rReg(memory dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (AddI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(150); // XXX + format %{ "addl $dst, $src\t# int" %} + opcode(0x01); /* Opcode 01 /r */ + ins_encode(REX_reg_mem(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +instruct addI_mem_imm(memory dst, immI src, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (AddI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); // XXX + format %{ "addl $dst, $src\t# int" %} + opcode(0x81); /* Opcode 81 /0 id */ + ins_encode(REX_mem(dst), OpcSE(src), RM_opc_mem(0x00, dst), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +instruct incI_rReg(rRegI dst, immI1 src, rFlagsReg cr) +%{ + predicate(UseIncDec); + match(Set dst (AddI dst src)); + effect(KILL cr); + + format %{ "incl $dst\t# int" %} + opcode(0xFF, 0x00); // FF /0 + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +instruct incI_mem(memory dst, immI1 src, rFlagsReg cr) +%{ + predicate(UseIncDec); + match(Set dst (StoreI dst (AddI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); // XXX + format %{ "incl $dst\t# int" %} + opcode(0xFF); /* Opcode FF /0 */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(0x00, dst)); + ins_pipe(ialu_mem_imm); +%} + +// XXX why does that use AddI +instruct decI_rReg(rRegI dst, immI_M1 src, rFlagsReg cr) +%{ + predicate(UseIncDec); + match(Set dst (AddI dst src)); + effect(KILL cr); + + format %{ "decl $dst\t# int" %} + opcode(0xFF, 0x01); // FF /1 + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +// XXX why does that use AddI +instruct decI_mem(memory dst, immI_M1 src, rFlagsReg cr) +%{ + predicate(UseIncDec); + match(Set dst (StoreI dst (AddI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); // XXX + format %{ "decl $dst\t# int" %} + opcode(0xFF); /* Opcode FF /1 */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(0x01, dst)); + ins_pipe(ialu_mem_imm); +%} + +instruct leaI_rReg_immI(rRegI dst, rRegI src0, immI src1) +%{ + match(Set dst (AddI src0 src1)); + + ins_cost(110); + format %{ "addr32 leal $dst, [$src0 + $src1]\t# int" %} + opcode(0x8D); /* 0x8D /r */ + ins_encode(Opcode(0x67), REX_reg_reg(dst, src0), OpcP, reg_lea(dst, src0, src1)); // XXX + ins_pipe(ialu_reg_reg); +%} + +instruct addL_rReg(rRegL dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (AddL dst src)); + effect(KILL cr); + + format %{ "addq $dst, $src\t# long" %} + opcode(0x03); + ins_encode(REX_reg_reg_wide(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +instruct addL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (AddL dst src)); + effect(KILL cr); + + format %{ "addq $dst, $src\t# long" %} + opcode(0x81, 0x00); /* /0 id */ + ins_encode(OpcSErm_wide(dst, src), Con8or32(src)); + ins_pipe( ialu_reg ); +%} + +instruct addL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) +%{ + match(Set dst (AddL dst (LoadL src))); + effect(KILL cr); + + ins_cost(125); // XXX + format %{ "addq $dst, $src\t# long" %} + opcode(0x03); + ins_encode(REX_reg_mem_wide(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +instruct addL_mem_rReg(memory dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (AddL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(150); // XXX + format %{ "addq $dst, $src\t# long" %} + opcode(0x01); /* Opcode 01 /r */ + ins_encode(REX_reg_mem_wide(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +instruct addL_mem_imm(memory dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (AddL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(125); // XXX + format %{ "addq $dst, $src\t# long" %} + opcode(0x81); /* Opcode 81 /0 id */ + ins_encode(REX_mem_wide(dst), + OpcSE(src), RM_opc_mem(0x00, dst), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +instruct incL_rReg(rRegI dst, immL1 src, rFlagsReg cr) +%{ + predicate(UseIncDec); + match(Set dst (AddL dst src)); + effect(KILL cr); + + format %{ "incq $dst\t# long" %} + opcode(0xFF, 0x00); // FF /0 + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +instruct incL_mem(memory dst, immL1 src, rFlagsReg cr) +%{ + predicate(UseIncDec); + match(Set dst (StoreL dst (AddL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(125); // XXX + format %{ "incq $dst\t# long" %} + opcode(0xFF); /* Opcode FF /0 */ + ins_encode(REX_mem_wide(dst), OpcP, RM_opc_mem(0x00, dst)); + ins_pipe(ialu_mem_imm); +%} + +// XXX why does that use AddL +instruct decL_rReg(rRegL dst, immL_M1 src, rFlagsReg cr) +%{ + predicate(UseIncDec); + match(Set dst (AddL dst src)); + effect(KILL cr); + + format %{ "decq $dst\t# long" %} + opcode(0xFF, 0x01); // FF /1 + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +// XXX why does that use AddL +instruct decL_mem(memory dst, immL_M1 src, rFlagsReg cr) +%{ + predicate(UseIncDec); + match(Set dst (StoreL dst (AddL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(125); // XXX + format %{ "decq $dst\t# long" %} + opcode(0xFF); /* Opcode FF /1 */ + ins_encode(REX_mem_wide(dst), OpcP, RM_opc_mem(0x01, dst)); + ins_pipe(ialu_mem_imm); +%} + +instruct leaL_rReg_immL(rRegL dst, rRegL src0, immL32 src1) +%{ + match(Set dst (AddL src0 src1)); + + ins_cost(110); + format %{ "leaq $dst, [$src0 + $src1]\t# long" %} + opcode(0x8D); /* 0x8D /r */ + ins_encode(REX_reg_reg_wide(dst, src0), OpcP, reg_lea(dst, src0, src1)); // XXX + ins_pipe(ialu_reg_reg); +%} + +instruct addP_rReg(rRegP dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (AddP dst src)); + effect(KILL cr); + + format %{ "addq $dst, $src\t# ptr" %} + opcode(0x03); + ins_encode(REX_reg_reg_wide(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +instruct addP_rReg_imm(rRegP dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (AddP dst src)); + effect(KILL cr); + + format %{ "addq $dst, $src\t# ptr" %} + opcode(0x81, 0x00); /* /0 id */ + ins_encode(OpcSErm_wide(dst, src), Con8or32(src)); + ins_pipe( ialu_reg ); +%} + +// XXX addP mem ops ???? + +instruct leaP_rReg_imm(rRegP dst, rRegP src0, immL32 src1) +%{ + match(Set dst (AddP src0 src1)); + + ins_cost(110); + format %{ "leaq $dst, [$src0 + $src1]\t# ptr" %} + opcode(0x8D); /* 0x8D /r */ + ins_encode(REX_reg_reg_wide(dst, src0), OpcP, reg_lea(dst, src0, src1));// XXX + ins_pipe(ialu_reg_reg); +%} + +instruct checkCastPP(rRegP dst) +%{ + match(Set dst (CheckCastPP dst)); + + size(0); + format %{ "# checkcastPP of $dst" %} + ins_encode(/* empty encoding */); + ins_pipe(empty); +%} + +instruct castPP(rRegP dst) +%{ + match(Set dst (CastPP dst)); + + size(0); + format %{ "# castPP of $dst" %} + ins_encode(/* empty encoding */); + ins_pipe(empty); +%} + +instruct castII(rRegI dst) +%{ + match(Set dst (CastII dst)); + + size(0); + format %{ "# castII of $dst" %} + ins_encode(/* empty encoding */); + ins_cost(0); + ins_pipe(empty); +%} + +// LoadP-locked same as a regular LoadP when used with compare-swap +instruct loadPLocked(rRegP dst, memory mem) +%{ + match(Set dst (LoadPLocked mem)); + + ins_cost(125); // XXX + format %{ "movq $dst, $mem\t# ptr locked" %} + opcode(0x8B); + ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); // XXX +%} + +// LoadL-locked - same as a regular LoadL when used with compare-swap +instruct loadLLocked(rRegL dst, memory mem) +%{ + match(Set dst (LoadLLocked mem)); + + ins_cost(125); // XXX + format %{ "movq $dst, $mem\t# long locked" %} + opcode(0x8B); + ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + ins_pipe(ialu_reg_mem); // XXX +%} + +// Conditional-store of the updated heap-top. +// Used during allocation of the shared heap. +// Sets flags (EQ) on success. Implemented with a CMPXCHG on Intel. + +instruct storePConditional(memory heap_top_ptr, + rax_RegP oldval, rRegP newval, + rFlagsReg cr) +%{ + match(Set cr (StorePConditional heap_top_ptr (Binary oldval newval))); + + format %{ "cmpxchgq $heap_top_ptr, $newval\t# (ptr) " + "If rax == $heap_top_ptr then store $newval into $heap_top_ptr" %} + opcode(0x0F, 0xB1); + ins_encode(lock_prefix, + REX_reg_mem_wide(newval, heap_top_ptr), + OpcP, OpcS, + reg_mem(newval, heap_top_ptr)); + ins_pipe(pipe_cmpxchg); +%} + +// Conditional-store of a long value +// Returns a boolean value (0/1) on success. Implemented with a +// CMPXCHG8 on Intel. mem_ptr can actually be in either RSI or RDI + +instruct storeLConditional(rRegI res, + memory mem_ptr, + rax_RegL oldval, rRegL newval, + rFlagsReg cr) +%{ + match(Set res (StoreLConditional mem_ptr (Binary oldval newval))); + effect(KILL cr); + + format %{ "cmpxchgq $mem_ptr, $newval\t# (long) " + "If rax == $mem_ptr then store $newval into $mem_ptr\n\t" + "sete $res\n\t" + "movzbl $res, $res" %} + opcode(0x0F, 0xB1); + ins_encode(lock_prefix, + REX_reg_mem_wide(newval, mem_ptr), + OpcP, OpcS, + reg_mem(newval, mem_ptr), + REX_breg(res), Opcode(0x0F), Opcode(0x94), reg(res), // sete + REX_reg_breg(res, res), // movzbl + Opcode(0xF), Opcode(0xB6), reg_reg(res, res)); + ins_pipe(pipe_cmpxchg); +%} + +// Conditional-store of a long value +// ZF flag is set on success, reset otherwise. Implemented with a +// CMPXCHG8 on Intel. mem_ptr can actually be in either RSI or RDI +instruct storeLConditional_flags(memory mem_ptr, + rax_RegL oldval, rRegL newval, + rFlagsReg cr, + immI0 zero) +%{ + match(Set cr (CmpI (StoreLConditional mem_ptr (Binary oldval newval)) zero)); + + format %{ "cmpxchgq $mem_ptr, $newval\t# (long) " + "If rax == $mem_ptr then store $newval into $mem_ptr" %} + opcode(0x0F, 0xB1); + ins_encode(lock_prefix, + REX_reg_mem_wide(newval, mem_ptr), + OpcP, OpcS, + reg_mem(newval, mem_ptr)); + ins_pipe(pipe_cmpxchg); +%} + +instruct compareAndSwapP(rRegI res, + memory mem_ptr, + rax_RegP oldval, rRegP newval, + rFlagsReg cr) +%{ + match(Set res (CompareAndSwapP mem_ptr (Binary oldval newval))); + effect(KILL cr, KILL oldval); + + format %{ "cmpxchgq $mem_ptr,$newval\t# " + "If rax == $mem_ptr then store $newval into $mem_ptr\n\t" + "sete $res\n\t" + "movzbl $res, $res" %} + opcode(0x0F, 0xB1); + ins_encode(lock_prefix, + REX_reg_mem_wide(newval, mem_ptr), + OpcP, OpcS, + reg_mem(newval, mem_ptr), + REX_breg(res), Opcode(0x0F), Opcode(0x94), reg(res), // sete + REX_reg_breg(res, res), // movzbl + Opcode(0xF), Opcode(0xB6), reg_reg(res, res)); + ins_pipe( pipe_cmpxchg ); +%} + +// XXX No flag versions for CompareAndSwap{P,I,L} because matcher can't match them +instruct compareAndSwapL(rRegI res, + memory mem_ptr, + rax_RegL oldval, rRegL newval, + rFlagsReg cr) +%{ + match(Set res (CompareAndSwapL mem_ptr (Binary oldval newval))); + effect(KILL cr, KILL oldval); + + format %{ "cmpxchgq $mem_ptr,$newval\t# " + "If rax == $mem_ptr then store $newval into $mem_ptr\n\t" + "sete $res\n\t" + "movzbl $res, $res" %} + opcode(0x0F, 0xB1); + ins_encode(lock_prefix, + REX_reg_mem_wide(newval, mem_ptr), + OpcP, OpcS, + reg_mem(newval, mem_ptr), + REX_breg(res), Opcode(0x0F), Opcode(0x94), reg(res), // sete + REX_reg_breg(res, res), // movzbl + Opcode(0xF), Opcode(0xB6), reg_reg(res, res)); + ins_pipe( pipe_cmpxchg ); +%} + +instruct compareAndSwapI(rRegI res, + memory mem_ptr, + rax_RegI oldval, rRegI newval, + rFlagsReg cr) +%{ + match(Set res (CompareAndSwapI mem_ptr (Binary oldval newval))); + effect(KILL cr, KILL oldval); + + format %{ "cmpxchgl $mem_ptr,$newval\t# " + "If rax == $mem_ptr then store $newval into $mem_ptr\n\t" + "sete $res\n\t" + "movzbl $res, $res" %} + opcode(0x0F, 0xB1); + ins_encode(lock_prefix, + REX_reg_mem(newval, mem_ptr), + OpcP, OpcS, + reg_mem(newval, mem_ptr), + REX_breg(res), Opcode(0x0F), Opcode(0x94), reg(res), // sete + REX_reg_breg(res, res), // movzbl + Opcode(0xF), Opcode(0xB6), reg_reg(res, res)); + ins_pipe( pipe_cmpxchg ); +%} + + +//----------Subtraction Instructions------------------------------------------- + +// Integer Subtraction Instructions +instruct subI_rReg(rRegI dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (SubI dst src)); + effect(KILL cr); + + format %{ "subl $dst, $src\t# int" %} + opcode(0x2B); + ins_encode(REX_reg_reg(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +instruct subI_rReg_imm(rRegI dst, immI src, rFlagsReg cr) +%{ + match(Set dst (SubI dst src)); + effect(KILL cr); + + format %{ "subl $dst, $src\t# int" %} + opcode(0x81, 0x05); /* Opcode 81 /5 */ + ins_encode(OpcSErm(dst, src), Con8or32(src)); + ins_pipe(ialu_reg); +%} + +instruct subI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) +%{ + match(Set dst (SubI dst (LoadI src))); + effect(KILL cr); + + ins_cost(125); + format %{ "subl $dst, $src\t# int" %} + opcode(0x2B); + ins_encode(REX_reg_mem(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +instruct subI_mem_rReg(memory dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (SubI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "subl $dst, $src\t# int" %} + opcode(0x29); /* Opcode 29 /r */ + ins_encode(REX_reg_mem(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +instruct subI_mem_imm(memory dst, immI src, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (SubI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); // XXX + format %{ "subl $dst, $src\t# int" %} + opcode(0x81); /* Opcode 81 /5 id */ + ins_encode(REX_mem(dst), OpcSE(src), RM_opc_mem(0x05, dst), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +instruct subL_rReg(rRegL dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (SubL dst src)); + effect(KILL cr); + + format %{ "subq $dst, $src\t# long" %} + opcode(0x2B); + ins_encode(REX_reg_reg_wide(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +instruct subL_rReg_imm(rRegI dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (SubL dst src)); + effect(KILL cr); + + format %{ "subq $dst, $src\t# long" %} + opcode(0x81, 0x05); /* Opcode 81 /5 */ + ins_encode(OpcSErm_wide(dst, src), Con8or32(src)); + ins_pipe(ialu_reg); +%} + +instruct subL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) +%{ + match(Set dst (SubL dst (LoadL src))); + effect(KILL cr); + + ins_cost(125); + format %{ "subq $dst, $src\t# long" %} + opcode(0x2B); + ins_encode(REX_reg_mem_wide(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +instruct subL_mem_rReg(memory dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (SubL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "subq $dst, $src\t# long" %} + opcode(0x29); /* Opcode 29 /r */ + ins_encode(REX_reg_mem_wide(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +instruct subL_mem_imm(memory dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (SubL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(125); // XXX + format %{ "subq $dst, $src\t# long" %} + opcode(0x81); /* Opcode 81 /5 id */ + ins_encode(REX_mem_wide(dst), + OpcSE(src), RM_opc_mem(0x05, dst), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Subtract from a pointer +// XXX hmpf??? +instruct subP_rReg(rRegP dst, rRegI src, immI0 zero, rFlagsReg cr) +%{ + match(Set dst (AddP dst (SubI zero src))); + effect(KILL cr); + + format %{ "subq $dst, $src\t# ptr - int" %} + opcode(0x2B); + ins_encode(REX_reg_reg_wide(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +instruct negI_rReg(rRegI dst, immI0 zero, rFlagsReg cr) +%{ + match(Set dst (SubI zero dst)); + effect(KILL cr); + + format %{ "negl $dst\t# int" %} + opcode(0xF7, 0x03); // Opcode F7 /3 + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +instruct negI_mem(memory dst, immI0 zero, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (SubI zero (LoadI dst)))); + effect(KILL cr); + + format %{ "negl $dst\t# int" %} + opcode(0xF7, 0x03); // Opcode F7 /3 + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_reg); +%} + +instruct negL_rReg(rRegL dst, immL0 zero, rFlagsReg cr) +%{ + match(Set dst (SubL zero dst)); + effect(KILL cr); + + format %{ "negq $dst\t# long" %} + opcode(0xF7, 0x03); // Opcode F7 /3 + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +instruct negL_mem(memory dst, immL0 zero, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (SubL zero (LoadL dst)))); + effect(KILL cr); + + format %{ "negq $dst\t# long" %} + opcode(0xF7, 0x03); // Opcode F7 /3 + ins_encode(REX_mem_wide(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_reg); +%} + + +//----------Multiplication/Division Instructions------------------------------- +// Integer Multiplication Instructions +// Multiply Register + +instruct mulI_rReg(rRegI dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (MulI dst src)); + effect(KILL cr); + + ins_cost(300); + format %{ "imull $dst, $src\t# int" %} + opcode(0x0F, 0xAF); + ins_encode(REX_reg_reg(dst, src), OpcP, OpcS, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct mulI_rReg_imm(rRegI dst, rRegI src, immI imm, rFlagsReg cr) +%{ + match(Set dst (MulI src imm)); + effect(KILL cr); + + ins_cost(300); + format %{ "imull $dst, $src, $imm\t# int" %} + opcode(0x69); /* 69 /r id */ + ins_encode(REX_reg_reg(dst, src), + OpcSE(imm), reg_reg(dst, src), Con8or32(imm)); + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct mulI_mem(rRegI dst, memory src, rFlagsReg cr) +%{ + match(Set dst (MulI dst (LoadI src))); + effect(KILL cr); + + ins_cost(350); + format %{ "imull $dst, $src\t# int" %} + opcode(0x0F, 0xAF); + ins_encode(REX_reg_mem(dst, src), OpcP, OpcS, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem_alu0); +%} + +instruct mulI_mem_imm(rRegI dst, memory src, immI imm, rFlagsReg cr) +%{ + match(Set dst (MulI (LoadI src) imm)); + effect(KILL cr); + + ins_cost(300); + format %{ "imull $dst, $src, $imm\t# int" %} + opcode(0x69); /* 69 /r id */ + ins_encode(REX_reg_mem(dst, src), + OpcSE(imm), reg_mem(dst, src), Con8or32(imm)); + ins_pipe(ialu_reg_mem_alu0); +%} + +instruct mulL_rReg(rRegL dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (MulL dst src)); + effect(KILL cr); + + ins_cost(300); + format %{ "imulq $dst, $src\t# long" %} + opcode(0x0F, 0xAF); + ins_encode(REX_reg_reg_wide(dst, src), OpcP, OpcS, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct mulL_rReg_imm(rRegL dst, rRegL src, immL32 imm, rFlagsReg cr) +%{ + match(Set dst (MulL src imm)); + effect(KILL cr); + + ins_cost(300); + format %{ "imulq $dst, $src, $imm\t# long" %} + opcode(0x69); /* 69 /r id */ + ins_encode(REX_reg_reg_wide(dst, src), + OpcSE(imm), reg_reg(dst, src), Con8or32(imm)); + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct mulL_mem(rRegL dst, memory src, rFlagsReg cr) +%{ + match(Set dst (MulL dst (LoadL src))); + effect(KILL cr); + + ins_cost(350); + format %{ "imulq $dst, $src\t# long" %} + opcode(0x0F, 0xAF); + ins_encode(REX_reg_mem_wide(dst, src), OpcP, OpcS, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem_alu0); +%} + +instruct mulL_mem_imm(rRegL dst, memory src, immL32 imm, rFlagsReg cr) +%{ + match(Set dst (MulL (LoadL src) imm)); + effect(KILL cr); + + ins_cost(300); + format %{ "imulq $dst, $src, $imm\t# long" %} + opcode(0x69); /* 69 /r id */ + ins_encode(REX_reg_mem_wide(dst, src), + OpcSE(imm), reg_mem(dst, src), Con8or32(imm)); + ins_pipe(ialu_reg_mem_alu0); +%} + +instruct divI_rReg(rax_RegI rax, rdx_RegI rdx, no_rax_rdx_RegI div, + rFlagsReg cr) +%{ + match(Set rax (DivI rax div)); + effect(KILL rdx, KILL cr); + + ins_cost(30*100+10*100); // XXX + format %{ "cmpl rax, 0x80000000\t# idiv\n\t" + "jne,s normal\n\t" + "xorl rdx, rdx\n\t" + "cmpl $div, -1\n\t" + "je,s done\n" + "normal: cdql\n\t" + "idivl $div\n" + "done:" %} + opcode(0xF7, 0x7); /* Opcode F7 /7 */ + ins_encode(cdql_enc(div), REX_reg(div), OpcP, reg_opc(div)); + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct divL_rReg(rax_RegL rax, rdx_RegL rdx, no_rax_rdx_RegL div, + rFlagsReg cr) +%{ + match(Set rax (DivL rax div)); + effect(KILL rdx, KILL cr); + + ins_cost(30*100+10*100); // XXX + format %{ "movq rdx, 0x8000000000000000\t# ldiv\n\t" + "cmpq rax, rdx\n\t" + "jne,s normal\n\t" + "xorl rdx, rdx\n\t" + "cmpq $div, -1\n\t" + "je,s done\n" + "normal: cdqq\n\t" + "idivq $div\n" + "done:" %} + opcode(0xF7, 0x7); /* Opcode F7 /7 */ + ins_encode(cdqq_enc(div), REX_reg_wide(div), OpcP, reg_opc(div)); + ins_pipe(ialu_reg_reg_alu0); +%} + +// Integer DIVMOD with Register, both quotient and mod results +instruct divModI_rReg_divmod(rax_RegI rax, rdx_RegI rdx, no_rax_rdx_RegI div, + rFlagsReg cr) +%{ + match(DivModI rax div); + effect(KILL cr); + + ins_cost(30*100+10*100); // XXX + format %{ "cmpl rax, 0x80000000\t# idiv\n\t" + "jne,s normal\n\t" + "xorl rdx, rdx\n\t" + "cmpl $div, -1\n\t" + "je,s done\n" + "normal: cdql\n\t" + "idivl $div\n" + "done:" %} + opcode(0xF7, 0x7); /* Opcode F7 /7 */ + ins_encode(cdql_enc(div), REX_reg(div), OpcP, reg_opc(div)); + ins_pipe(pipe_slow); +%} + +// Long DIVMOD with Register, both quotient and mod results +instruct divModL_rReg_divmod(rax_RegL rax, rdx_RegL rdx, no_rax_rdx_RegL div, + rFlagsReg cr) +%{ + match(DivModL rax div); + effect(KILL cr); + + ins_cost(30*100+10*100); // XXX + format %{ "movq rdx, 0x8000000000000000\t# ldiv\n\t" + "cmpq rax, rdx\n\t" + "jne,s normal\n\t" + "xorl rdx, rdx\n\t" + "cmpq $div, -1\n\t" + "je,s done\n" + "normal: cdqq\n\t" + "idivq $div\n" + "done:" %} + opcode(0xF7, 0x7); /* Opcode F7 /7 */ + ins_encode(cdqq_enc(div), REX_reg_wide(div), OpcP, reg_opc(div)); + ins_pipe(pipe_slow); +%} + +//----------- DivL-By-Constant-Expansions-------------------------------------- +// DivI cases are handled by the compiler + +// Magic constant, reciprical of 10 +instruct loadConL_0x6666666666666667(rRegL dst) +%{ + effect(DEF dst); + + format %{ "movq $dst, #0x666666666666667\t# Used in div-by-10" %} + ins_encode(load_immL(dst, 0x6666666666666667)); + ins_pipe(ialu_reg); +%} + +instruct mul_hi(rdx_RegL dst, no_rax_RegL src, rax_RegL rax, rFlagsReg cr) +%{ + effect(DEF dst, USE src, USE_KILL rax, KILL cr); + + format %{ "imulq rdx:rax, rax, $src\t# Used in div-by-10" %} + opcode(0xF7, 0x5); /* Opcode F7 /5 */ + ins_encode(REX_reg_wide(src), OpcP, reg_opc(src)); + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct sarL_rReg_63(rRegL dst, rFlagsReg cr) +%{ + effect(USE_DEF dst, KILL cr); + + format %{ "sarq $dst, #63\t# Used in div-by-10" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode(reg_opc_imm_wide(dst, 0x3F)); + ins_pipe(ialu_reg); +%} + +instruct sarL_rReg_2(rRegL dst, rFlagsReg cr) +%{ + effect(USE_DEF dst, KILL cr); + + format %{ "sarq $dst, #2\t# Used in div-by-10" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode(reg_opc_imm_wide(dst, 0x2)); + ins_pipe(ialu_reg); +%} + +instruct divL_10(rdx_RegL dst, no_rax_RegL src, immL10 div) +%{ + match(Set dst (DivL src div)); + + ins_cost((5+8)*100); + expand %{ + rax_RegL rax; // Killed temp + rFlagsReg cr; // Killed + loadConL_0x6666666666666667(rax); // movq rax, 0x6666666666666667 + mul_hi(dst, src, rax, cr); // mulq rdx:rax <= rax * $src + sarL_rReg_63(src, cr); // sarq src, 63 + sarL_rReg_2(dst, cr); // sarq rdx, 2 + subL_rReg(dst, src, cr); // subl rdx, src + %} +%} + +//----------------------------------------------------------------------------- + +instruct modI_rReg(rdx_RegI rdx, rax_RegI rax, no_rax_rdx_RegI div, + rFlagsReg cr) +%{ + match(Set rdx (ModI rax div)); + effect(KILL rax, KILL cr); + + ins_cost(300); // XXX + format %{ "cmpl rax, 0x80000000\t# irem\n\t" + "jne,s normal\n\t" + "xorl rdx, rdx\n\t" + "cmpl $div, -1\n\t" + "je,s done\n" + "normal: cdql\n\t" + "idivl $div\n" + "done:" %} + opcode(0xF7, 0x7); /* Opcode F7 /7 */ + ins_encode(cdql_enc(div), REX_reg(div), OpcP, reg_opc(div)); + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct modL_rReg(rdx_RegL rdx, rax_RegL rax, no_rax_rdx_RegL div, + rFlagsReg cr) +%{ + match(Set rdx (ModL rax div)); + effect(KILL rax, KILL cr); + + ins_cost(300); // XXX + format %{ "movq rdx, 0x8000000000000000\t# lrem\n\t" + "cmpq rax, rdx\n\t" + "jne,s normal\n\t" + "xorl rdx, rdx\n\t" + "cmpq $div, -1\n\t" + "je,s done\n" + "normal: cdqq\n\t" + "idivq $div\n" + "done:" %} + opcode(0xF7, 0x7); /* Opcode F7 /7 */ + ins_encode(cdqq_enc(div), REX_reg_wide(div), OpcP, reg_opc(div)); + ins_pipe(ialu_reg_reg_alu0); +%} + +// Integer Shift Instructions +// Shift Left by one +instruct salI_rReg_1(rRegI dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (LShiftI dst shift)); + effect(KILL cr); + + format %{ "sall $dst, $shift" %} + opcode(0xD1, 0x4); /* D1 /4 */ + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +// Shift Left by one +instruct salI_mem_1(memory dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (LShiftI (LoadI dst) shift))); + effect(KILL cr); + + format %{ "sall $dst, $shift\t" %} + opcode(0xD1, 0x4); /* D1 /4 */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_imm); +%} + +// Shift Left by 8-bit immediate +instruct salI_rReg_imm(rRegI dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (LShiftI dst shift)); + effect(KILL cr); + + format %{ "sall $dst, $shift" %} + opcode(0xC1, 0x4); /* C1 /4 ib */ + ins_encode(reg_opc_imm(dst, shift)); + ins_pipe(ialu_reg); +%} + +// Shift Left by 8-bit immediate +instruct salI_mem_imm(memory dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (LShiftI (LoadI dst) shift))); + effect(KILL cr); + + format %{ "sall $dst, $shift" %} + opcode(0xC1, 0x4); /* C1 /4 ib */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(secondary, dst), Con8or32(shift)); + ins_pipe(ialu_mem_imm); +%} + +// Shift Left by variable +instruct salI_rReg_CL(rRegI dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (LShiftI dst shift)); + effect(KILL cr); + + format %{ "sall $dst, $shift" %} + opcode(0xD3, 0x4); /* D3 /4 */ + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg_reg); +%} + +// Shift Left by variable +instruct salI_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (LShiftI (LoadI dst) shift))); + effect(KILL cr); + + format %{ "sall $dst, $shift" %} + opcode(0xD3, 0x4); /* D3 /4 */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_reg); +%} + +// Arithmetic shift right by one +instruct sarI_rReg_1(rRegI dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (RShiftI dst shift)); + effect(KILL cr); + + format %{ "sarl $dst, $shift" %} + opcode(0xD1, 0x7); /* D1 /7 */ + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +// Arithmetic shift right by one +instruct sarI_mem_1(memory dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (RShiftI (LoadI dst) shift))); + effect(KILL cr); + + format %{ "sarl $dst, $shift" %} + opcode(0xD1, 0x7); /* D1 /7 */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_imm); +%} + +// Arithmetic Shift Right by 8-bit immediate +instruct sarI_rReg_imm(rRegI dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (RShiftI dst shift)); + effect(KILL cr); + + format %{ "sarl $dst, $shift" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode(reg_opc_imm(dst, shift)); + ins_pipe(ialu_mem_imm); +%} + +// Arithmetic Shift Right by 8-bit immediate +instruct sarI_mem_imm(memory dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (RShiftI (LoadI dst) shift))); + effect(KILL cr); + + format %{ "sarl $dst, $shift" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(secondary, dst), Con8or32(shift)); + ins_pipe(ialu_mem_imm); +%} + +// Arithmetic Shift Right by variable +instruct sarI_rReg_CL(rRegI dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (RShiftI dst shift)); + effect(KILL cr); + + format %{ "sarl $dst, $shift" %} + opcode(0xD3, 0x7); /* D3 /7 */ + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg_reg); +%} + +// Arithmetic Shift Right by variable +instruct sarI_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (RShiftI (LoadI dst) shift))); + effect(KILL cr); + + format %{ "sarl $dst, $shift" %} + opcode(0xD3, 0x7); /* D3 /7 */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_reg); +%} + +// Logical shift right by one +instruct shrI_rReg_1(rRegI dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (URShiftI dst shift)); + effect(KILL cr); + + format %{ "shrl $dst, $shift" %} + opcode(0xD1, 0x5); /* D1 /5 */ + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +// Logical shift right by one +instruct shrI_mem_1(memory dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (URShiftI (LoadI dst) shift))); + effect(KILL cr); + + format %{ "shrl $dst, $shift" %} + opcode(0xD1, 0x5); /* D1 /5 */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_imm); +%} + +// Logical Shift Right by 8-bit immediate +instruct shrI_rReg_imm(rRegI dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (URShiftI dst shift)); + effect(KILL cr); + + format %{ "shrl $dst, $shift" %} + opcode(0xC1, 0x5); /* C1 /5 ib */ + ins_encode(reg_opc_imm(dst, shift)); + ins_pipe(ialu_reg); +%} + +// Logical Shift Right by 8-bit immediate +instruct shrI_mem_imm(memory dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (URShiftI (LoadI dst) shift))); + effect(KILL cr); + + format %{ "shrl $dst, $shift" %} + opcode(0xC1, 0x5); /* C1 /5 ib */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(secondary, dst), Con8or32(shift)); + ins_pipe(ialu_mem_imm); +%} + +// Logical Shift Right by variable +instruct shrI_rReg_CL(rRegI dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (URShiftI dst shift)); + effect(KILL cr); + + format %{ "shrl $dst, $shift" %} + opcode(0xD3, 0x5); /* D3 /5 */ + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg_reg); +%} + +// Logical Shift Right by variable +instruct shrI_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (URShiftI (LoadI dst) shift))); + effect(KILL cr); + + format %{ "shrl $dst, $shift" %} + opcode(0xD3, 0x5); /* D3 /5 */ + ins_encode(REX_mem(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_reg); +%} + +// Long Shift Instructions +// Shift Left by one +instruct salL_rReg_1(rRegL dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (LShiftL dst shift)); + effect(KILL cr); + + format %{ "salq $dst, $shift" %} + opcode(0xD1, 0x4); /* D1 /4 */ + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +// Shift Left by one +instruct salL_mem_1(memory dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (LShiftL (LoadL dst) shift))); + effect(KILL cr); + + format %{ "salq $dst, $shift" %} + opcode(0xD1, 0x4); /* D1 /4 */ + ins_encode(REX_mem_wide(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_imm); +%} + +// Shift Left by 8-bit immediate +instruct salL_rReg_imm(rRegL dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (LShiftL dst shift)); + effect(KILL cr); + + format %{ "salq $dst, $shift" %} + opcode(0xC1, 0x4); /* C1 /4 ib */ + ins_encode(reg_opc_imm_wide(dst, shift)); + ins_pipe(ialu_reg); +%} + +// Shift Left by 8-bit immediate +instruct salL_mem_imm(memory dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (LShiftL (LoadL dst) shift))); + effect(KILL cr); + + format %{ "salq $dst, $shift" %} + opcode(0xC1, 0x4); /* C1 /4 ib */ + ins_encode(REX_mem_wide(dst), OpcP, + RM_opc_mem(secondary, dst), Con8or32(shift)); + ins_pipe(ialu_mem_imm); +%} + +// Shift Left by variable +instruct salL_rReg_CL(rRegL dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (LShiftL dst shift)); + effect(KILL cr); + + format %{ "salq $dst, $shift" %} + opcode(0xD3, 0x4); /* D3 /4 */ + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg_reg); +%} + +// Shift Left by variable +instruct salL_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (LShiftL (LoadL dst) shift))); + effect(KILL cr); + + format %{ "salq $dst, $shift" %} + opcode(0xD3, 0x4); /* D3 /4 */ + ins_encode(REX_mem_wide(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_reg); +%} + +// Arithmetic shift right by one +instruct sarL_rReg_1(rRegL dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (RShiftL dst shift)); + effect(KILL cr); + + format %{ "sarq $dst, $shift" %} + opcode(0xD1, 0x7); /* D1 /7 */ + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +// Arithmetic shift right by one +instruct sarL_mem_1(memory dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (RShiftL (LoadL dst) shift))); + effect(KILL cr); + + format %{ "sarq $dst, $shift" %} + opcode(0xD1, 0x7); /* D1 /7 */ + ins_encode(REX_mem_wide(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_imm); +%} + +// Arithmetic Shift Right by 8-bit immediate +instruct sarL_rReg_imm(rRegL dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (RShiftL dst shift)); + effect(KILL cr); + + format %{ "sarq $dst, $shift" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode(reg_opc_imm_wide(dst, shift)); + ins_pipe(ialu_mem_imm); +%} + +// Arithmetic Shift Right by 8-bit immediate +instruct sarL_mem_imm(memory dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (RShiftL (LoadL dst) shift))); + effect(KILL cr); + + format %{ "sarq $dst, $shift" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode(REX_mem_wide(dst), OpcP, + RM_opc_mem(secondary, dst), Con8or32(shift)); + ins_pipe(ialu_mem_imm); +%} + +// Arithmetic Shift Right by variable +instruct sarL_rReg_CL(rRegL dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (RShiftL dst shift)); + effect(KILL cr); + + format %{ "sarq $dst, $shift" %} + opcode(0xD3, 0x7); /* D3 /7 */ + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg_reg); +%} + +// Arithmetic Shift Right by variable +instruct sarL_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (RShiftL (LoadL dst) shift))); + effect(KILL cr); + + format %{ "sarq $dst, $shift" %} + opcode(0xD3, 0x7); /* D3 /7 */ + ins_encode(REX_mem_wide(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_reg); +%} + +// Logical shift right by one +instruct shrL_rReg_1(rRegL dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (URShiftL dst shift)); + effect(KILL cr); + + format %{ "shrq $dst, $shift" %} + opcode(0xD1, 0x5); /* D1 /5 */ + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst )); + ins_pipe(ialu_reg); +%} + +// Logical shift right by one +instruct shrL_mem_1(memory dst, immI1 shift, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (URShiftL (LoadL dst) shift))); + effect(KILL cr); + + format %{ "shrq $dst, $shift" %} + opcode(0xD1, 0x5); /* D1 /5 */ + ins_encode(REX_mem_wide(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_imm); +%} + +// Logical Shift Right by 8-bit immediate +instruct shrL_rReg_imm(rRegL dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (URShiftL dst shift)); + effect(KILL cr); + + format %{ "shrq $dst, $shift" %} + opcode(0xC1, 0x5); /* C1 /5 ib */ + ins_encode(reg_opc_imm_wide(dst, shift)); + ins_pipe(ialu_reg); +%} + +// Logical Shift Right by 8-bit immediate +instruct shrL_mem_imm(memory dst, immI8 shift, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (URShiftL (LoadL dst) shift))); + effect(KILL cr); + + format %{ "shrq $dst, $shift" %} + opcode(0xC1, 0x5); /* C1 /5 ib */ + ins_encode(REX_mem_wide(dst), OpcP, + RM_opc_mem(secondary, dst), Con8or32(shift)); + ins_pipe(ialu_mem_imm); +%} + +// Logical Shift Right by variable +instruct shrL_rReg_CL(rRegL dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (URShiftL dst shift)); + effect(KILL cr); + + format %{ "shrq $dst, $shift" %} + opcode(0xD3, 0x5); /* D3 /5 */ + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg_reg); +%} + +// Logical Shift Right by variable +instruct shrL_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (URShiftL (LoadL dst) shift))); + effect(KILL cr); + + format %{ "shrq $dst, $shift" %} + opcode(0xD3, 0x5); /* D3 /5 */ + ins_encode(REX_mem_wide(dst), OpcP, RM_opc_mem(secondary, dst)); + ins_pipe(ialu_mem_reg); +%} + +// Logical Shift Right by 24, followed by Arithmetic Shift Left by 24. +// This idiom is used by the compiler for the i2b bytecode. +instruct i2b(rRegI dst, rRegI src, immI_24 twentyfour) +%{ + match(Set dst (RShiftI (LShiftI src twentyfour) twentyfour)); + + format %{ "movsbl $dst, $src\t# i2b" %} + opcode(0x0F, 0xBE); + ins_encode(REX_reg_breg(dst, src), OpcP, OpcS, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +// Logical Shift Right by 16, followed by Arithmetic Shift Left by 16. +// This idiom is used by the compiler the i2s bytecode. +instruct i2s(rRegI dst, rRegI src, immI_16 sixteen) +%{ + match(Set dst (RShiftI (LShiftI src sixteen) sixteen)); + + format %{ "movswl $dst, $src\t# i2s" %} + opcode(0x0F, 0xBF); + ins_encode(REX_reg_reg(dst, src), OpcP, OpcS, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +// ROL/ROR instructions + +// ROL expand +instruct rolI_rReg_imm1(rRegI dst, rFlagsReg cr) %{ + effect(KILL cr, USE_DEF dst); + + format %{ "roll $dst" %} + opcode(0xD1, 0x0); /* Opcode D1 /0 */ + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +instruct rolI_rReg_imm8(rRegI dst, immI8 shift, rFlagsReg cr) %{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "roll $dst, $shift" %} + opcode(0xC1, 0x0); /* Opcode C1 /0 ib */ + ins_encode( reg_opc_imm(dst, shift) ); + ins_pipe(ialu_reg); +%} + +instruct rolI_rReg_CL(no_rcx_RegI dst, rcx_RegI shift, rFlagsReg cr) +%{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "roll $dst, $shift" %} + opcode(0xD3, 0x0); /* Opcode D3 /0 */ + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg_reg); +%} +// end of ROL expand + +// Rotate Left by one +instruct rolI_rReg_i1(rRegI dst, immI1 lshift, immI_M1 rshift, rFlagsReg cr) +%{ + match(Set dst (OrI (LShiftI dst lshift) (URShiftI dst rshift))); + + expand %{ + rolI_rReg_imm1(dst, cr); + %} +%} + +// Rotate Left by 8-bit immediate +instruct rolI_rReg_i8(rRegI dst, immI8 lshift, immI8 rshift, rFlagsReg cr) +%{ + predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); + match(Set dst (OrI (LShiftI dst lshift) (URShiftI dst rshift))); + + expand %{ + rolI_rReg_imm8(dst, lshift, cr); + %} +%} + +// Rotate Left by variable +instruct rolI_rReg_Var_C0(no_rcx_RegI dst, rcx_RegI shift, immI0 zero, rFlagsReg cr) +%{ + match(Set dst (OrI (LShiftI dst shift) (URShiftI dst (SubI zero shift)))); + + expand %{ + rolI_rReg_CL(dst, shift, cr); + %} +%} + +// Rotate Left by variable +instruct rolI_rReg_Var_C32(no_rcx_RegI dst, rcx_RegI shift, immI_32 c32, rFlagsReg cr) +%{ + match(Set dst (OrI (LShiftI dst shift) (URShiftI dst (SubI c32 shift)))); + + expand %{ + rolI_rReg_CL(dst, shift, cr); + %} +%} + +// ROR expand +instruct rorI_rReg_imm1(rRegI dst, rFlagsReg cr) +%{ + effect(USE_DEF dst, KILL cr); + + format %{ "rorl $dst" %} + opcode(0xD1, 0x1); /* D1 /1 */ + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +instruct rorI_rReg_imm8(rRegI dst, immI8 shift, rFlagsReg cr) +%{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "rorl $dst, $shift" %} + opcode(0xC1, 0x1); /* C1 /1 ib */ + ins_encode(reg_opc_imm(dst, shift)); + ins_pipe(ialu_reg); +%} + +instruct rorI_rReg_CL(no_rcx_RegI dst, rcx_RegI shift, rFlagsReg cr) +%{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "rorl $dst, $shift" %} + opcode(0xD3, 0x1); /* D3 /1 */ + ins_encode(REX_reg(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg_reg); +%} +// end of ROR expand + +// Rotate Right by one +instruct rorI_rReg_i1(rRegI dst, immI1 rshift, immI_M1 lshift, rFlagsReg cr) +%{ + match(Set dst (OrI (URShiftI dst rshift) (LShiftI dst lshift))); + + expand %{ + rorI_rReg_imm1(dst, cr); + %} +%} + +// Rotate Right by 8-bit immediate +instruct rorI_rReg_i8(rRegI dst, immI8 rshift, immI8 lshift, rFlagsReg cr) +%{ + predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); + match(Set dst (OrI (URShiftI dst rshift) (LShiftI dst lshift))); + + expand %{ + rorI_rReg_imm8(dst, rshift, cr); + %} +%} + +// Rotate Right by variable +instruct rorI_rReg_Var_C0(no_rcx_RegI dst, rcx_RegI shift, immI0 zero, rFlagsReg cr) +%{ + match(Set dst (OrI (URShiftI dst shift) (LShiftI dst (SubI zero shift)))); + + expand %{ + rorI_rReg_CL(dst, shift, cr); + %} +%} + +// Rotate Right by variable +instruct rorI_rReg_Var_C32(no_rcx_RegI dst, rcx_RegI shift, immI_32 c32, rFlagsReg cr) +%{ + match(Set dst (OrI (URShiftI dst shift) (LShiftI dst (SubI c32 shift)))); + + expand %{ + rorI_rReg_CL(dst, shift, cr); + %} +%} + +// for long rotate +// ROL expand +instruct rolL_rReg_imm1(rRegL dst, rFlagsReg cr) %{ + effect(USE_DEF dst, KILL cr); + + format %{ "rolq $dst" %} + opcode(0xD1, 0x0); /* Opcode D1 /0 */ + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +instruct rolL_rReg_imm8(rRegL dst, immI8 shift, rFlagsReg cr) %{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "rolq $dst, $shift" %} + opcode(0xC1, 0x0); /* Opcode C1 /0 ib */ + ins_encode( reg_opc_imm_wide(dst, shift) ); + ins_pipe(ialu_reg); +%} + +instruct rolL_rReg_CL(no_rcx_RegL dst, rcx_RegI shift, rFlagsReg cr) +%{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "rolq $dst, $shift" %} + opcode(0xD3, 0x0); /* Opcode D3 /0 */ + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg_reg); +%} +// end of ROL expand + +// Rotate Left by one +instruct rolL_rReg_i1(rRegL dst, immI1 lshift, immI_M1 rshift, rFlagsReg cr) +%{ + match(Set dst (OrL (LShiftL dst lshift) (URShiftL dst rshift))); + + expand %{ + rolL_rReg_imm1(dst, cr); + %} +%} + +// Rotate Left by 8-bit immediate +instruct rolL_rReg_i8(rRegL dst, immI8 lshift, immI8 rshift, rFlagsReg cr) +%{ + predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x3f)); + match(Set dst (OrL (LShiftL dst lshift) (URShiftL dst rshift))); + + expand %{ + rolL_rReg_imm8(dst, lshift, cr); + %} +%} + +// Rotate Left by variable +instruct rolL_rReg_Var_C0(no_rcx_RegL dst, rcx_RegI shift, immI0 zero, rFlagsReg cr) +%{ + match(Set dst (OrL (LShiftL dst shift) (URShiftL dst (SubI zero shift)))); + + expand %{ + rolL_rReg_CL(dst, shift, cr); + %} +%} + +// Rotate Left by variable +instruct rolL_rReg_Var_C64(no_rcx_RegL dst, rcx_RegI shift, immI_64 c64, rFlagsReg cr) +%{ + match(Set dst (OrL (LShiftL dst shift) (URShiftL dst (SubI c64 shift)))); + + expand %{ + rolL_rReg_CL(dst, shift, cr); + %} +%} + +// ROR expand +instruct rorL_rReg_imm1(rRegL dst, rFlagsReg cr) +%{ + effect(USE_DEF dst, KILL cr); + + format %{ "rorq $dst" %} + opcode(0xD1, 0x1); /* D1 /1 */ + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg); +%} + +instruct rorL_rReg_imm8(rRegL dst, immI8 shift, rFlagsReg cr) +%{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "rorq $dst, $shift" %} + opcode(0xC1, 0x1); /* C1 /1 ib */ + ins_encode(reg_opc_imm_wide(dst, shift)); + ins_pipe(ialu_reg); +%} + +instruct rorL_rReg_CL(no_rcx_RegL dst, rcx_RegI shift, rFlagsReg cr) +%{ + effect(USE_DEF dst, USE shift, KILL cr); + + format %{ "rorq $dst, $shift" %} + opcode(0xD3, 0x1); /* D3 /1 */ + ins_encode(REX_reg_wide(dst), OpcP, reg_opc(dst)); + ins_pipe(ialu_reg_reg); +%} +// end of ROR expand + +// Rotate Right by one +instruct rorL_rReg_i1(rRegL dst, immI1 rshift, immI_M1 lshift, rFlagsReg cr) +%{ + match(Set dst (OrL (URShiftL dst rshift) (LShiftL dst lshift))); + + expand %{ + rorL_rReg_imm1(dst, cr); + %} +%} + +// Rotate Right by 8-bit immediate +instruct rorL_rReg_i8(rRegL dst, immI8 rshift, immI8 lshift, rFlagsReg cr) +%{ + predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x3f)); + match(Set dst (OrL (URShiftL dst rshift) (LShiftL dst lshift))); + + expand %{ + rorL_rReg_imm8(dst, rshift, cr); + %} +%} + +// Rotate Right by variable +instruct rorL_rReg_Var_C0(no_rcx_RegL dst, rcx_RegI shift, immI0 zero, rFlagsReg cr) +%{ + match(Set dst (OrL (URShiftL dst shift) (LShiftL dst (SubI zero shift)))); + + expand %{ + rorL_rReg_CL(dst, shift, cr); + %} +%} + +// Rotate Right by variable +instruct rorL_rReg_Var_C64(no_rcx_RegL dst, rcx_RegI shift, immI_64 c64, rFlagsReg cr) +%{ + match(Set dst (OrL (URShiftL dst shift) (LShiftL dst (SubI c64 shift)))); + + expand %{ + rorL_rReg_CL(dst, shift, cr); + %} +%} + +// Logical Instructions + +// Integer Logical Instructions + +// And Instructions +// And Register with Register +instruct andI_rReg(rRegI dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (AndI dst src)); + effect(KILL cr); + + format %{ "andl $dst, $src\t# int" %} + opcode(0x23); + ins_encode(REX_reg_reg(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +// And Register with Immediate 255 +instruct andI_rReg_imm255(rRegI dst, immI_255 src) +%{ + match(Set dst (AndI dst src)); + + format %{ "movzbl $dst, $dst\t# int & 0xFF" %} + opcode(0x0F, 0xB6); + ins_encode(REX_reg_breg(dst, dst), OpcP, OpcS, reg_reg(dst, dst)); + ins_pipe(ialu_reg); +%} + +// And Register with Immediate 255 and promote to long +instruct andI2L_rReg_imm255(rRegL dst, rRegI src, immI_255 mask) +%{ + match(Set dst (ConvI2L (AndI src mask))); + + format %{ "movzbl $dst, $src\t# int & 0xFF -> long" %} + opcode(0x0F, 0xB6); + ins_encode(REX_reg_breg(dst, src), OpcP, OpcS, reg_reg(dst, src)); + ins_pipe(ialu_reg); +%} + +// And Register with Immediate 65535 +instruct andI_rReg_imm65535(rRegI dst, immI_65535 src) +%{ + match(Set dst (AndI dst src)); + + format %{ "movzwl $dst, $dst\t# int & 0xFFFF" %} + opcode(0x0F, 0xB7); + ins_encode(REX_reg_reg(dst, dst), OpcP, OpcS, reg_reg(dst, dst)); + ins_pipe(ialu_reg); +%} + +// And Register with Immediate 65535 and promote to long +instruct andI2L_rReg_imm65535(rRegL dst, rRegI src, immI_65535 mask) +%{ + match(Set dst (ConvI2L (AndI src mask))); + + format %{ "movzwl $dst, $src\t# int & 0xFFFF -> long" %} + opcode(0x0F, 0xB7); + ins_encode(REX_reg_reg(dst, src), OpcP, OpcS, reg_reg(dst, src)); + ins_pipe(ialu_reg); +%} + +// And Register with Immediate +instruct andI_rReg_imm(rRegI dst, immI src, rFlagsReg cr) +%{ + match(Set dst (AndI dst src)); + effect(KILL cr); + + format %{ "andl $dst, $src\t# int" %} + opcode(0x81, 0x04); /* Opcode 81 /4 */ + ins_encode(OpcSErm(dst, src), Con8or32(src)); + ins_pipe(ialu_reg); +%} + +// And Register with Memory +instruct andI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) +%{ + match(Set dst (AndI dst (LoadI src))); + effect(KILL cr); + + ins_cost(125); + format %{ "andl $dst, $src\t# int" %} + opcode(0x23); + ins_encode(REX_reg_mem(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +// And Memory with Register +instruct andI_mem_rReg(memory dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (AndI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "andl $dst, $src\t# int" %} + opcode(0x21); /* Opcode 21 /r */ + ins_encode(REX_reg_mem(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +// And Memory with Immediate +instruct andI_mem_imm(memory dst, immI src, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (AndI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "andl $dst, $src\t# int" %} + opcode(0x81, 0x4); /* Opcode 81 /4 id */ + ins_encode(REX_mem(dst), OpcSE(src), + RM_opc_mem(secondary, dst), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Or Instructions +// Or Register with Register +instruct orI_rReg(rRegI dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (OrI dst src)); + effect(KILL cr); + + format %{ "orl $dst, $src\t# int" %} + opcode(0x0B); + ins_encode(REX_reg_reg(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +// Or Register with Immediate +instruct orI_rReg_imm(rRegI dst, immI src, rFlagsReg cr) +%{ + match(Set dst (OrI dst src)); + effect(KILL cr); + + format %{ "orl $dst, $src\t# int" %} + opcode(0x81, 0x01); /* Opcode 81 /1 id */ + ins_encode(OpcSErm(dst, src), Con8or32(src)); + ins_pipe(ialu_reg); +%} + +// Or Register with Memory +instruct orI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) +%{ + match(Set dst (OrI dst (LoadI src))); + effect(KILL cr); + + ins_cost(125); + format %{ "orl $dst, $src\t# int" %} + opcode(0x0B); + ins_encode(REX_reg_mem(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +// Or Memory with Register +instruct orI_mem_rReg(memory dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (OrI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "orl $dst, $src\t# int" %} + opcode(0x09); /* Opcode 09 /r */ + ins_encode(REX_reg_mem(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +// Or Memory with Immediate +instruct orI_mem_imm(memory dst, immI src, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (OrI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "orl $dst, $src\t# int" %} + opcode(0x81, 0x1); /* Opcode 81 /1 id */ + ins_encode(REX_mem(dst), OpcSE(src), + RM_opc_mem(secondary, dst), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Xor Instructions +// Xor Register with Register +instruct xorI_rReg(rRegI dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (XorI dst src)); + effect(KILL cr); + + format %{ "xorl $dst, $src\t# int" %} + opcode(0x33); + ins_encode(REX_reg_reg(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +// Xor Register with Immediate +instruct xorI_rReg_imm(rRegI dst, immI src, rFlagsReg cr) +%{ + match(Set dst (XorI dst src)); + effect(KILL cr); + + format %{ "xorl $dst, $src\t# int" %} + opcode(0x81, 0x06); /* Opcode 81 /6 id */ + ins_encode(OpcSErm(dst, src), Con8or32(src)); + ins_pipe(ialu_reg); +%} + +// Xor Register with Memory +instruct xorI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) +%{ + match(Set dst (XorI dst (LoadI src))); + effect(KILL cr); + + ins_cost(125); + format %{ "xorl $dst, $src\t# int" %} + opcode(0x33); + ins_encode(REX_reg_mem(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +// Xor Memory with Register +instruct xorI_mem_rReg(memory dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (XorI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "xorl $dst, $src\t# int" %} + opcode(0x31); /* Opcode 31 /r */ + ins_encode(REX_reg_mem(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +// Xor Memory with Immediate +instruct xorI_mem_imm(memory dst, immI src, rFlagsReg cr) +%{ + match(Set dst (StoreI dst (XorI (LoadI dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "xorl $dst, $src\t# int" %} + opcode(0x81, 0x6); /* Opcode 81 /6 id */ + ins_encode(REX_mem(dst), OpcSE(src), + RM_opc_mem(secondary, dst), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + + +// Long Logical Instructions + +// And Instructions +// And Register with Register +instruct andL_rReg(rRegL dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (AndL dst src)); + effect(KILL cr); + + format %{ "andq $dst, $src\t# long" %} + opcode(0x23); + ins_encode(REX_reg_reg_wide(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +// And Register with Immediate 255 +instruct andL_rReg_imm255(rRegL dst, immL_255 src) +%{ + match(Set dst (AndL dst src)); + + format %{ "movzbq $dst, $src\t# long & 0xFF" %} + opcode(0x0F, 0xB6); + ins_encode(REX_reg_reg_wide(dst, dst), OpcP, OpcS, reg_reg(dst, dst)); + ins_pipe(ialu_reg); +%} + +// And Register with Immediate 65535 +instruct andL_rReg_imm65535(rRegI dst, immL_65535 src) +%{ + match(Set dst (AndL dst src)); + + format %{ "movzwq $dst, $dst\t# long & 0xFFFF" %} + opcode(0x0F, 0xB7); + ins_encode(REX_reg_reg_wide(dst, dst), OpcP, OpcS, reg_reg(dst, dst)); + ins_pipe(ialu_reg); +%} + +// And Register with Immediate +instruct andL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (AndL dst src)); + effect(KILL cr); + + format %{ "andq $dst, $src\t# long" %} + opcode(0x81, 0x04); /* Opcode 81 /4 */ + ins_encode(OpcSErm_wide(dst, src), Con8or32(src)); + ins_pipe(ialu_reg); +%} + +// And Register with Memory +instruct andL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) +%{ + match(Set dst (AndL dst (LoadL src))); + effect(KILL cr); + + ins_cost(125); + format %{ "andq $dst, $src\t# long" %} + opcode(0x23); + ins_encode(REX_reg_mem_wide(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +// And Memory with Register +instruct andL_mem_rReg(memory dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (AndL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "andq $dst, $src\t# long" %} + opcode(0x21); /* Opcode 21 /r */ + ins_encode(REX_reg_mem_wide(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +// And Memory with Immediate +instruct andL_mem_imm(memory dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (AndL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "andq $dst, $src\t# long" %} + opcode(0x81, 0x4); /* Opcode 81 /4 id */ + ins_encode(REX_mem_wide(dst), OpcSE(src), + RM_opc_mem(secondary, dst), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Or Instructions +// Or Register with Register +instruct orL_rReg(rRegL dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (OrL dst src)); + effect(KILL cr); + + format %{ "orq $dst, $src\t# long" %} + opcode(0x0B); + ins_encode(REX_reg_reg_wide(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +// Or Register with Immediate +instruct orL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (OrL dst src)); + effect(KILL cr); + + format %{ "orq $dst, $src\t# long" %} + opcode(0x81, 0x01); /* Opcode 81 /1 id */ + ins_encode(OpcSErm_wide(dst, src), Con8or32(src)); + ins_pipe(ialu_reg); +%} + +// Or Register with Memory +instruct orL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) +%{ + match(Set dst (OrL dst (LoadL src))); + effect(KILL cr); + + ins_cost(125); + format %{ "orq $dst, $src\t# long" %} + opcode(0x0B); + ins_encode(REX_reg_mem_wide(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +// Or Memory with Register +instruct orL_mem_rReg(memory dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (OrL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "orq $dst, $src\t# long" %} + opcode(0x09); /* Opcode 09 /r */ + ins_encode(REX_reg_mem_wide(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +// Or Memory with Immediate +instruct orL_mem_imm(memory dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (OrL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "orq $dst, $src\t# long" %} + opcode(0x81, 0x1); /* Opcode 81 /1 id */ + ins_encode(REX_mem_wide(dst), OpcSE(src), + RM_opc_mem(secondary, dst), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Xor Instructions +// Xor Register with Register +instruct xorL_rReg(rRegL dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (XorL dst src)); + effect(KILL cr); + + format %{ "xorq $dst, $src\t# long" %} + opcode(0x33); + ins_encode(REX_reg_reg_wide(dst, src), OpcP, reg_reg(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +// Xor Register with Immediate +instruct xorL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (XorL dst src)); + effect(KILL cr); + + format %{ "xorq $dst, $src\t# long" %} + opcode(0x81, 0x06); /* Opcode 81 /6 id */ + ins_encode(OpcSErm_wide(dst, src), Con8or32(src)); + ins_pipe(ialu_reg); +%} + +// Xor Register with Memory +instruct xorL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) +%{ + match(Set dst (XorL dst (LoadL src))); + effect(KILL cr); + + ins_cost(125); + format %{ "xorq $dst, $src\t# long" %} + opcode(0x33); + ins_encode(REX_reg_mem_wide(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +// Xor Memory with Register +instruct xorL_mem_rReg(memory dst, rRegL src, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (XorL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(150); + format %{ "xorq $dst, $src\t# long" %} + opcode(0x31); /* Opcode 31 /r */ + ins_encode(REX_reg_mem_wide(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +// Xor Memory with Immediate +instruct xorL_mem_imm(memory dst, immL32 src, rFlagsReg cr) +%{ + match(Set dst (StoreL dst (XorL (LoadL dst) src))); + effect(KILL cr); + + ins_cost(125); + format %{ "xorq $dst, $src\t# long" %} + opcode(0x81, 0x6); /* Opcode 81 /6 id */ + ins_encode(REX_mem_wide(dst), OpcSE(src), + RM_opc_mem(secondary, dst), Con8or32(src)); + ins_pipe(ialu_mem_imm); +%} + +// Convert Int to Boolean +instruct convI2B(rRegI dst, rRegI src, rFlagsReg cr) +%{ + match(Set dst (Conv2B src)); + effect(KILL cr); + + format %{ "testl $src, $src\t# ci2b\n\t" + "setnz $dst\n\t" + "movzbl $dst, $dst" %} + ins_encode(REX_reg_reg(src, src), opc_reg_reg(0x85, src, src), // testl + setNZ_reg(dst), + REX_reg_breg(dst, dst), // movzbl + Opcode(0x0F), Opcode(0xB6), reg_reg(dst, dst)); + ins_pipe(pipe_slow); // XXX +%} + +// Convert Pointer to Boolean +instruct convP2B(rRegI dst, rRegP src, rFlagsReg cr) +%{ + match(Set dst (Conv2B src)); + effect(KILL cr); + + format %{ "testq $src, $src\t# cp2b\n\t" + "setnz $dst\n\t" + "movzbl $dst, $dst" %} + ins_encode(REX_reg_reg_wide(src, src), opc_reg_reg(0x85, src, src), // testq + setNZ_reg(dst), + REX_reg_breg(dst, dst), // movzbl + Opcode(0x0F), Opcode(0xB6), reg_reg(dst, dst)); + ins_pipe(pipe_slow); // XXX +%} + +instruct cmpLTMask(rRegI dst, rRegI p, rRegI q, rFlagsReg cr) +%{ + match(Set dst (CmpLTMask p q)); + effect(KILL cr); + + ins_cost(400); // XXX + format %{ "cmpl $p, $q\t# cmpLTMask\n\t" + "setlt $dst\n\t" + "movzbl $dst, $dst\n\t" + "negl $dst" %} + ins_encode(REX_reg_reg(p, q), opc_reg_reg(0x3B, p, q), // cmpl + setLT_reg(dst), + REX_reg_breg(dst, dst), // movzbl + Opcode(0x0F), Opcode(0xB6), reg_reg(dst, dst), + neg_reg(dst)); + ins_pipe(pipe_slow); +%} + +instruct cmpLTMask0(rRegI dst, immI0 zero, rFlagsReg cr) +%{ + match(Set dst (CmpLTMask dst zero)); + effect(KILL cr); + + ins_cost(100); // XXX + format %{ "sarl $dst, #31\t# cmpLTMask0" %} + opcode(0xC1, 0x7); /* C1 /7 ib */ + ins_encode(reg_opc_imm(dst, 0x1F)); + ins_pipe(ialu_reg); +%} + + +instruct cadd_cmpLTMask(rRegI p, rRegI q, rRegI y, + rRegI tmp, + rFlagsReg cr) +%{ + match(Set p (AddI (AndI (CmpLTMask p q) y) (SubI p q))); + effect(TEMP tmp, KILL cr); + + ins_cost(400); // XXX + format %{ "subl $p, $q\t# cadd_cmpLTMask1\n\t" + "sbbl $tmp, $tmp\n\t" + "andl $tmp, $y\n\t" + "addl $p, $tmp" %} + ins_encode(enc_cmpLTP(p, q, y, tmp)); + ins_pipe(pipe_cmplt); +%} + +/* If I enable this, I encourage spilling in the inner loop of compress. +instruct cadd_cmpLTMask_mem( rRegI p, rRegI q, memory y, rRegI tmp, rFlagsReg cr ) +%{ + match(Set p (AddI (AndI (CmpLTMask p q) (LoadI y)) (SubI p q))); + effect( TEMP tmp, KILL cr ); + ins_cost(400); + + format %{ "SUB $p,$q\n\t" + "SBB RCX,RCX\n\t" + "AND RCX,$y\n\t" + "ADD $p,RCX" %} + ins_encode( enc_cmpLTP_mem(p,q,y,tmp) ); +%} +*/ + +//---------- FP Instructions------------------------------------------------ + +instruct cmpF_cc_reg(rFlagsRegU cr, regF src1, regF src2) +%{ + match(Set cr (CmpF src1 src2)); + + ins_cost(145); + format %{ "ucomiss $src1, $src2\n\t" + "jnp,s exit\n\t" + "pushfq\t# saw NaN, set CF\n\t" + "andq [rsp], #0xffffff2b\n\t" + "popfq\n" + "exit: nop\t# avoid branch to branch" %} + opcode(0x0F, 0x2E); + ins_encode(REX_reg_reg(src1, src2), OpcP, OpcS, reg_reg(src1, src2), + cmpfp_fixup); + ins_pipe(pipe_slow); +%} + +instruct cmpF_cc_mem(rFlagsRegU cr, regF src1, memory src2) +%{ + match(Set cr (CmpF src1 (LoadF src2))); + + ins_cost(145); + format %{ "ucomiss $src1, $src2\n\t" + "jnp,s exit\n\t" + "pushfq\t# saw NaN, set CF\n\t" + "andq [rsp], #0xffffff2b\n\t" + "popfq\n" + "exit: nop\t# avoid branch to branch" %} + opcode(0x0F, 0x2E); + ins_encode(REX_reg_mem(src1, src2), OpcP, OpcS, reg_mem(src1, src2), + cmpfp_fixup); + ins_pipe(pipe_slow); +%} + +instruct cmpF_cc_imm(rFlagsRegU cr, regF src1, immF src2) +%{ + match(Set cr (CmpF src1 src2)); + + ins_cost(145); + format %{ "ucomiss $src1, $src2\n\t" + "jnp,s exit\n\t" + "pushfq\t# saw NaN, set CF\n\t" + "andq [rsp], #0xffffff2b\n\t" + "popfq\n" + "exit: nop\t# avoid branch to branch" %} + opcode(0x0F, 0x2E); + ins_encode(REX_reg_mem(src1, src2), OpcP, OpcS, load_immF(src1, src2), + cmpfp_fixup); + ins_pipe(pipe_slow); +%} + +instruct cmpD_cc_reg(rFlagsRegU cr, regD src1, regD src2) +%{ + match(Set cr (CmpD src1 src2)); + + ins_cost(145); + format %{ "ucomisd $src1, $src2\n\t" + "jnp,s exit\n\t" + "pushfq\t# saw NaN, set CF\n\t" + "andq [rsp], #0xffffff2b\n\t" + "popfq\n" + "exit: nop\t# avoid branch to branch" %} + opcode(0x66, 0x0F, 0x2E); + ins_encode(OpcP, REX_reg_reg(src1, src2), OpcS, OpcT, reg_reg(src1, src2), + cmpfp_fixup); + ins_pipe(pipe_slow); +%} + +instruct cmpD_cc_mem(rFlagsRegU cr, regD src1, memory src2) +%{ + match(Set cr (CmpD src1 (LoadD src2))); + + ins_cost(145); + format %{ "ucomisd $src1, $src2\n\t" + "jnp,s exit\n\t" + "pushfq\t# saw NaN, set CF\n\t" + "andq [rsp], #0xffffff2b\n\t" + "popfq\n" + "exit: nop\t# avoid branch to branch" %} + opcode(0x66, 0x0F, 0x2E); + ins_encode(OpcP, REX_reg_mem(src1, src2), OpcS, OpcT, reg_mem(src1, src2), + cmpfp_fixup); + ins_pipe(pipe_slow); +%} + +instruct cmpD_cc_imm(rFlagsRegU cr, regD src1, immD src2) +%{ + match(Set cr (CmpD src1 src2)); + + ins_cost(145); + format %{ "ucomisd $src1, [$src2]\n\t" + "jnp,s exit\n\t" + "pushfq\t# saw NaN, set CF\n\t" + "andq [rsp], #0xffffff2b\n\t" + "popfq\n" + "exit: nop\t# avoid branch to branch" %} + opcode(0x66, 0x0F, 0x2E); + ins_encode(OpcP, REX_reg_mem(src1, src2), OpcS, OpcT, load_immD(src1, src2), + cmpfp_fixup); + ins_pipe(pipe_slow); +%} + +// Compare into -1,0,1 +instruct cmpF_reg(rRegI dst, regF src1, regF src2, rFlagsReg cr) +%{ + match(Set dst (CmpF3 src1 src2)); + effect(KILL cr); + + ins_cost(275); + format %{ "ucomiss $src1, $src2\n\t" + "movl $dst, #-1\n\t" + "jp,s done\n\t" + "jb,s done\n\t" + "setne $dst\n\t" + "movzbl $dst, $dst\n" + "done:" %} + + opcode(0x0F, 0x2E); + ins_encode(REX_reg_reg(src1, src2), OpcP, OpcS, reg_reg(src1, src2), + cmpfp3(dst)); + ins_pipe(pipe_slow); +%} + +// Compare into -1,0,1 +instruct cmpF_mem(rRegI dst, regF src1, memory src2, rFlagsReg cr) +%{ + match(Set dst (CmpF3 src1 (LoadF src2))); + effect(KILL cr); + + ins_cost(275); + format %{ "ucomiss $src1, $src2\n\t" + "movl $dst, #-1\n\t" + "jp,s done\n\t" + "jb,s done\n\t" + "setne $dst\n\t" + "movzbl $dst, $dst\n" + "done:" %} + + opcode(0x0F, 0x2E); + ins_encode(REX_reg_mem(src1, src2), OpcP, OpcS, reg_mem(src1, src2), + cmpfp3(dst)); + ins_pipe(pipe_slow); +%} + +// Compare into -1,0,1 +instruct cmpF_imm(rRegI dst, regF src1, immF src2, rFlagsReg cr) +%{ + match(Set dst (CmpF3 src1 src2)); + effect(KILL cr); + + ins_cost(275); + format %{ "ucomiss $src1, [$src2]\n\t" + "movl $dst, #-1\n\t" + "jp,s done\n\t" + "jb,s done\n\t" + "setne $dst\n\t" + "movzbl $dst, $dst\n" + "done:" %} + + opcode(0x0F, 0x2E); + ins_encode(REX_reg_mem(src1, src2), OpcP, OpcS, load_immF(src1, src2), + cmpfp3(dst)); + ins_pipe(pipe_slow); +%} + +// Compare into -1,0,1 +instruct cmpD_reg(rRegI dst, regD src1, regD src2, rFlagsReg cr) +%{ + match(Set dst (CmpD3 src1 src2)); + effect(KILL cr); + + ins_cost(275); + format %{ "ucomisd $src1, $src2\n\t" + "movl $dst, #-1\n\t" + "jp,s done\n\t" + "jb,s done\n\t" + "setne $dst\n\t" + "movzbl $dst, $dst\n" + "done:" %} + + opcode(0x66, 0x0F, 0x2E); + ins_encode(OpcP, REX_reg_reg(src1, src2), OpcS, OpcT, reg_reg(src1, src2), + cmpfp3(dst)); + ins_pipe(pipe_slow); +%} + +// Compare into -1,0,1 +instruct cmpD_mem(rRegI dst, regD src1, memory src2, rFlagsReg cr) +%{ + match(Set dst (CmpD3 src1 (LoadD src2))); + effect(KILL cr); + + ins_cost(275); + format %{ "ucomisd $src1, $src2\n\t" + "movl $dst, #-1\n\t" + "jp,s done\n\t" + "jb,s done\n\t" + "setne $dst\n\t" + "movzbl $dst, $dst\n" + "done:" %} + + opcode(0x66, 0x0F, 0x2E); + ins_encode(OpcP, REX_reg_mem(src1, src2), OpcS, OpcT, reg_mem(src1, src2), + cmpfp3(dst)); + ins_pipe(pipe_slow); +%} + +// Compare into -1,0,1 +instruct cmpD_imm(rRegI dst, regD src1, immD src2, rFlagsReg cr) +%{ + match(Set dst (CmpD3 src1 src2)); + effect(KILL cr); + + ins_cost(275); + format %{ "ucomisd $src1, [$src2]\n\t" + "movl $dst, #-1\n\t" + "jp,s done\n\t" + "jb,s done\n\t" + "setne $dst\n\t" + "movzbl $dst, $dst\n" + "done:" %} + + opcode(0x66, 0x0F, 0x2E); + ins_encode(OpcP, REX_reg_mem(src1, src2), OpcS, OpcT, load_immD(src1, src2), + cmpfp3(dst)); + ins_pipe(pipe_slow); +%} + +instruct addF_reg(regF dst, regF src) +%{ + match(Set dst (AddF dst src)); + + format %{ "addss $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x58); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct addF_mem(regF dst, memory src) +%{ + match(Set dst (AddF dst (LoadF src))); + + format %{ "addss $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x58); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct addF_imm(regF dst, immF src) +%{ + match(Set dst (AddF dst src)); + + format %{ "addss $dst, [$src]" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x58); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, load_immF(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct addD_reg(regD dst, regD src) +%{ + match(Set dst (AddD dst src)); + + format %{ "addsd $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x58); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct addD_mem(regD dst, memory src) +%{ + match(Set dst (AddD dst (LoadD src))); + + format %{ "addsd $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x58); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct addD_imm(regD dst, immD src) +%{ + match(Set dst (AddD dst src)); + + format %{ "addsd $dst, [$src]" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x58); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, load_immD(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct subF_reg(regF dst, regF src) +%{ + match(Set dst (SubF dst src)); + + format %{ "subss $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x5C); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct subF_mem(regF dst, memory src) +%{ + match(Set dst (SubF dst (LoadF src))); + + format %{ "subss $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x5C); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct subF_imm(regF dst, immF src) +%{ + match(Set dst (SubF dst src)); + + format %{ "subss $dst, [$src]" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x5C); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, load_immF(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct subD_reg(regD dst, regD src) +%{ + match(Set dst (SubD dst src)); + + format %{ "subsd $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x5C); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct subD_mem(regD dst, memory src) +%{ + match(Set dst (SubD dst (LoadD src))); + + format %{ "subsd $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x5C); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct subD_imm(regD dst, immD src) +%{ + match(Set dst (SubD dst src)); + + format %{ "subsd $dst, [$src]" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x5C); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, load_immD(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct mulF_reg(regF dst, regF src) +%{ + match(Set dst (MulF dst src)); + + format %{ "mulss $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x59); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct mulF_mem(regF dst, memory src) +%{ + match(Set dst (MulF dst (LoadF src))); + + format %{ "mulss $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x59); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct mulF_imm(regF dst, immF src) +%{ + match(Set dst (MulF dst src)); + + format %{ "mulss $dst, [$src]" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x59); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, load_immF(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct mulD_reg(regD dst, regD src) +%{ + match(Set dst (MulD dst src)); + + format %{ "mulsd $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x59); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct mulD_mem(regD dst, memory src) +%{ + match(Set dst (MulD dst (LoadD src))); + + format %{ "mulsd $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x59); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct mulD_imm(regD dst, immD src) +%{ + match(Set dst (MulD dst src)); + + format %{ "mulsd $dst, [$src]" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x59); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, load_immD(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct divF_reg(regF dst, regF src) +%{ + match(Set dst (DivF dst src)); + + format %{ "divss $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x5E); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct divF_mem(regF dst, memory src) +%{ + match(Set dst (DivF dst (LoadF src))); + + format %{ "divss $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x5E); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct divF_imm(regF dst, immF src) +%{ + match(Set dst (DivF dst src)); + + format %{ "divss $dst, [$src]" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x5E); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, load_immF(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct divD_reg(regD dst, regD src) +%{ + match(Set dst (DivD dst src)); + + format %{ "divsd $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x5E); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct divD_mem(regD dst, memory src) +%{ + match(Set dst (DivD dst (LoadD src))); + + format %{ "divsd $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x5E); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct divD_imm(regD dst, immD src) +%{ + match(Set dst (DivD dst src)); + + format %{ "divsd $dst, [$src]" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x5E); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, load_immD(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct sqrtF_reg(regF dst, regF src) +%{ + match(Set dst (ConvD2F (SqrtD (ConvF2D src)))); + + format %{ "sqrtss $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x51); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct sqrtF_mem(regF dst, memory src) +%{ + match(Set dst (ConvD2F (SqrtD (ConvF2D (LoadF src))))); + + format %{ "sqrtss $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x51); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct sqrtF_imm(regF dst, immF src) +%{ + match(Set dst (ConvD2F (SqrtD (ConvF2D src)))); + + format %{ "sqrtss $dst, [$src]" %} + ins_cost(150); // XXX + opcode(0xF3, 0x0F, 0x51); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, load_immF(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct sqrtD_reg(regD dst, regD src) +%{ + match(Set dst (SqrtD src)); + + format %{ "sqrtsd $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x51); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct sqrtD_mem(regD dst, memory src) +%{ + match(Set dst (SqrtD (LoadD src))); + + format %{ "sqrtsd $dst, $src" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x51); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct sqrtD_imm(regD dst, immD src) +%{ + match(Set dst (SqrtD src)); + + format %{ "sqrtsd $dst, [$src]" %} + ins_cost(150); // XXX + opcode(0xF2, 0x0F, 0x51); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, load_immD(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct absF_reg(regF dst) +%{ + match(Set dst (AbsF dst)); + + format %{ "andps $dst, [0x7fffffff]\t# abs float by sign masking" %} + ins_encode(absF_encoding(dst)); + ins_pipe(pipe_slow); +%} + +instruct absD_reg(regD dst) +%{ + match(Set dst (AbsD dst)); + + format %{ "andpd $dst, [0x7fffffffffffffff]\t" + "# abs double by sign masking" %} + ins_encode(absD_encoding(dst)); + ins_pipe(pipe_slow); +%} + +instruct negF_reg(regF dst) +%{ + match(Set dst (NegF dst)); + + format %{ "xorps $dst, [0x80000000]\t# neg float by sign flipping" %} + ins_encode(negF_encoding(dst)); + ins_pipe(pipe_slow); +%} + +instruct negD_reg(regD dst) +%{ + match(Set dst (NegD dst)); + + format %{ "xorpd $dst, [0x8000000000000000]\t" + "# neg double by sign flipping" %} + ins_encode(negD_encoding(dst)); + ins_pipe(pipe_slow); +%} + +// -----------Trig and Trancendental Instructions------------------------------ +instruct cosD_reg(regD dst) %{ + match(Set dst (CosD dst)); + + format %{ "dcos $dst\n\t" %} + opcode(0xD9, 0xFF); + ins_encode( Push_SrcXD(dst), OpcP, OpcS, Push_ResultXD(dst) ); + ins_pipe( pipe_slow ); +%} + +instruct sinD_reg(regD dst) %{ + match(Set dst (SinD dst)); + + format %{ "dsin $dst\n\t" %} + opcode(0xD9, 0xFE); + ins_encode( Push_SrcXD(dst), OpcP, OpcS, Push_ResultXD(dst) ); + ins_pipe( pipe_slow ); +%} + +instruct tanD_reg(regD dst) %{ + match(Set dst (TanD dst)); + + format %{ "dtan $dst\n\t" %} + ins_encode( Push_SrcXD(dst), + Opcode(0xD9), Opcode(0xF2), //fptan + Opcode(0xDD), Opcode(0xD8), //fstp st + Push_ResultXD(dst) ); + ins_pipe( pipe_slow ); +%} + +instruct log10D_reg(regD dst) %{ + // The source and result Double operands in XMM registers + match(Set dst (Log10D dst)); + // fldlg2 ; push log_10(2) on the FPU stack; full 80-bit number + // fyl2x ; compute log_10(2) * log_2(x) + format %{ "fldlg2\t\t\t#Log10\n\t" + "fyl2x\t\t\t# Q=Log10*Log_2(x)\n\t" + %} + ins_encode(Opcode(0xD9), Opcode(0xEC), // fldlg2 + Push_SrcXD(dst), + Opcode(0xD9), Opcode(0xF1), // fyl2x + Push_ResultXD(dst)); + + ins_pipe( pipe_slow ); +%} + +instruct logD_reg(regD dst) %{ + // The source and result Double operands in XMM registers + match(Set dst (LogD dst)); + // fldln2 ; push log_e(2) on the FPU stack; full 80-bit number + // fyl2x ; compute log_e(2) * log_2(x) + format %{ "fldln2\t\t\t#Log_e\n\t" + "fyl2x\t\t\t# Q=Log_e*Log_2(x)\n\t" + %} + ins_encode( Opcode(0xD9), Opcode(0xED), // fldln2 + Push_SrcXD(dst), + Opcode(0xD9), Opcode(0xF1), // fyl2x + Push_ResultXD(dst)); + ins_pipe( pipe_slow ); +%} + + + +//----------Arithmetic Conversion Instructions--------------------------------- + +instruct roundFloat_nop(regF dst) +%{ + match(Set dst (RoundFloat dst)); + + ins_cost(0); + ins_encode(); + ins_pipe(empty); +%} + +instruct roundDouble_nop(regD dst) +%{ + match(Set dst (RoundDouble dst)); + + ins_cost(0); + ins_encode(); + ins_pipe(empty); +%} + +instruct convF2D_reg_reg(regD dst, regF src) +%{ + match(Set dst (ConvF2D src)); + + format %{ "cvtss2sd $dst, $src" %} + opcode(0xF3, 0x0F, 0x5A); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convF2D_reg_mem(regD dst, memory src) +%{ + match(Set dst (ConvF2D (LoadF src))); + + format %{ "cvtss2sd $dst, $src" %} + opcode(0xF3, 0x0F, 0x5A); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convD2F_reg_reg(regF dst, regD src) +%{ + match(Set dst (ConvD2F src)); + + format %{ "cvtsd2ss $dst, $src" %} + opcode(0xF2, 0x0F, 0x5A); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convD2F_reg_mem(regF dst, memory src) +%{ + match(Set dst (ConvD2F (LoadD src))); + + format %{ "cvtsd2ss $dst, $src" %} + opcode(0xF2, 0x0F, 0x5A); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +// XXX do mem variants +instruct convF2I_reg_reg(rRegI dst, regF src, rFlagsReg cr) +%{ + match(Set dst (ConvF2I src)); + effect(KILL cr); + + format %{ "cvttss2sil $dst, $src\t# f2i\n\t" + "cmpl $dst, #0x80000000\n\t" + "jne,s done\n\t" + "subq rsp, #8\n\t" + "movss [rsp], $src\n\t" + "call f2i_fixup\n\t" + "popq $dst\n" + "done: "%} + opcode(0xF3, 0x0F, 0x2C); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src), + f2i_fixup(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct convF2L_reg_reg(rRegL dst, regF src, rFlagsReg cr) +%{ + match(Set dst (ConvF2L src)); + effect(KILL cr); + + format %{ "cvttss2siq $dst, $src\t# f2l\n\t" + "cmpq $dst, [0x8000000000000000]\n\t" + "jne,s done\n\t" + "subq rsp, #8\n\t" + "movss [rsp], $src\n\t" + "call f2l_fixup\n\t" + "popq $dst\n" + "done: "%} + opcode(0xF3, 0x0F, 0x2C); + ins_encode(OpcP, REX_reg_reg_wide(dst, src), OpcS, OpcT, reg_reg(dst, src), + f2l_fixup(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct convD2I_reg_reg(rRegI dst, regD src, rFlagsReg cr) +%{ + match(Set dst (ConvD2I src)); + effect(KILL cr); + + format %{ "cvttsd2sil $dst, $src\t# d2i\n\t" + "cmpl $dst, #0x80000000\n\t" + "jne,s done\n\t" + "subq rsp, #8\n\t" + "movsd [rsp], $src\n\t" + "call d2i_fixup\n\t" + "popq $dst\n" + "done: "%} + opcode(0xF2, 0x0F, 0x2C); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src), + d2i_fixup(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct convD2L_reg_reg(rRegL dst, regD src, rFlagsReg cr) +%{ + match(Set dst (ConvD2L src)); + effect(KILL cr); + + format %{ "cvttsd2siq $dst, $src\t# d2l\n\t" + "cmpq $dst, [0x8000000000000000]\n\t" + "jne,s done\n\t" + "subq rsp, #8\n\t" + "movsd [rsp], $src\n\t" + "call d2l_fixup\n\t" + "popq $dst\n" + "done: "%} + opcode(0xF2, 0x0F, 0x2C); + ins_encode(OpcP, REX_reg_reg_wide(dst, src), OpcS, OpcT, reg_reg(dst, src), + d2l_fixup(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct convI2F_reg_reg(regF dst, rRegI src) +%{ + match(Set dst (ConvI2F src)); + + format %{ "cvtsi2ssl $dst, $src\t# i2f" %} + opcode(0xF3, 0x0F, 0x2A); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convI2F_reg_mem(regF dst, memory src) +%{ + match(Set dst (ConvI2F (LoadI src))); + + format %{ "cvtsi2ssl $dst, $src\t# i2f" %} + opcode(0xF3, 0x0F, 0x2A); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convI2D_reg_reg(regD dst, rRegI src) +%{ + match(Set dst (ConvI2D src)); + + format %{ "cvtsi2sdl $dst, $src\t# i2d" %} + opcode(0xF2, 0x0F, 0x2A); + ins_encode(OpcP, REX_reg_reg(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convI2D_reg_mem(regD dst, memory src) +%{ + match(Set dst (ConvI2D (LoadI src))); + + format %{ "cvtsi2sdl $dst, $src\t# i2d" %} + opcode(0xF2, 0x0F, 0x2A); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convL2F_reg_reg(regF dst, rRegL src) +%{ + match(Set dst (ConvL2F src)); + + format %{ "cvtsi2ssq $dst, $src\t# l2f" %} + opcode(0xF3, 0x0F, 0x2A); + ins_encode(OpcP, REX_reg_reg_wide(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convL2F_reg_mem(regF dst, memory src) +%{ + match(Set dst (ConvL2F (LoadL src))); + + format %{ "cvtsi2ssq $dst, $src\t# l2f" %} + opcode(0xF3, 0x0F, 0x2A); + ins_encode(OpcP, REX_reg_mem_wide(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convL2D_reg_reg(regD dst, rRegL src) +%{ + match(Set dst (ConvL2D src)); + + format %{ "cvtsi2sdq $dst, $src\t# l2d" %} + opcode(0xF2, 0x0F, 0x2A); + ins_encode(OpcP, REX_reg_reg_wide(dst, src), OpcS, OpcT, reg_reg(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convL2D_reg_mem(regD dst, memory src) +%{ + match(Set dst (ConvL2D (LoadL src))); + + format %{ "cvtsi2sdq $dst, $src\t# l2d" %} + opcode(0xF2, 0x0F, 0x2A); + ins_encode(OpcP, REX_reg_mem_wide(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); // XXX +%} + +instruct convI2L_reg_reg(rRegL dst, rRegI src) +%{ + match(Set dst (ConvI2L src)); + + ins_cost(125); + format %{ "movslq $dst, $src\t# i2l" %} + opcode(0x63); // needs REX.W + ins_encode(REX_reg_reg_wide(dst, src), OpcP, reg_reg(dst,src)); + ins_pipe(ialu_reg_reg); +%} + +// instruct convI2L_reg_reg_foo(rRegL dst, rRegI src) +// %{ +// match(Set dst (ConvI2L src)); +// // predicate(_kids[0]->_leaf->as_Type()->type()->is_int()->_lo >= 0 && +// // _kids[0]->_leaf->as_Type()->type()->is_int()->_hi >= 0); +// predicate(((const TypeNode*) n)->type()->is_long()->_hi == +// (unsigned int) ((const TypeNode*) n)->type()->is_long()->_hi && +// ((const TypeNode*) n)->type()->is_long()->_lo == +// (unsigned int) ((const TypeNode*) n)->type()->is_long()->_lo); + +// format %{ "movl $dst, $src\t# unsigned i2l" %} +// ins_encode(enc_copy(dst, src)); +// // opcode(0x63); // needs REX.W +// // ins_encode(REX_reg_reg_wide(dst, src), OpcP, reg_reg(dst,src)); +// ins_pipe(ialu_reg_reg); +// %} + +instruct convI2L_reg_mem(rRegL dst, memory src) +%{ + match(Set dst (ConvI2L (LoadI src))); + + format %{ "movslq $dst, $src\t# i2l" %} + opcode(0x63); // needs REX.W + ins_encode(REX_reg_mem_wide(dst, src), OpcP, reg_mem(dst,src)); + ins_pipe(ialu_reg_mem); +%} + +// Zero-extend convert int to long +instruct convI2L_reg_reg_zex(rRegL dst, rRegI src, immL_32bits mask) +%{ + match(Set dst (AndL (ConvI2L src) mask)); + + format %{ "movl $dst, $src\t# i2l zero-extend\n\t" %} + ins_encode(enc_copy(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +// Zero-extend convert int to long +instruct convI2L_reg_mem_zex(rRegL dst, memory src, immL_32bits mask) +%{ + match(Set dst (AndL (ConvI2L (LoadI src)) mask)); + + format %{ "movl $dst, $src\t# i2l zero-extend\n\t" %} + opcode(0x8B); + ins_encode(REX_reg_mem(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +instruct zerox_long_reg_reg(rRegL dst, rRegL src, immL_32bits mask) +%{ + match(Set dst (AndL src mask)); + + format %{ "movl $dst, $src\t# zero-extend long" %} + ins_encode(enc_copy_always(dst, src)); + ins_pipe(ialu_reg_reg); +%} + +instruct convL2I_reg_reg(rRegI dst, rRegL src) +%{ + match(Set dst (ConvL2I src)); + + format %{ "movl $dst, $src\t# l2i" %} + ins_encode(enc_copy_always(dst, src)); + ins_pipe(ialu_reg_reg); +%} + + +instruct MoveF2I_stack_reg(rRegI dst, stackSlotF src) %{ + match(Set dst (MoveF2I src)); + effect(DEF dst, USE src); + + ins_cost(125); + format %{ "movl $dst, $src\t# MoveF2I_stack_reg" %} + opcode(0x8B); + ins_encode(REX_reg_mem(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +instruct MoveI2F_stack_reg(regF dst, stackSlotI src) %{ + match(Set dst (MoveI2F src)); + effect(DEF dst, USE src); + + ins_cost(125); + format %{ "movss $dst, $src\t# MoveI2F_stack_reg" %} + opcode(0xF3, 0x0F, 0x10); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct MoveD2L_stack_reg(rRegL dst, stackSlotD src) %{ + match(Set dst (MoveD2L src)); + effect(DEF dst, USE src); + + ins_cost(125); + format %{ "movq $dst, $src\t# MoveD2L_stack_reg" %} + opcode(0x8B); + ins_encode(REX_reg_mem_wide(dst, src), OpcP, reg_mem(dst, src)); + ins_pipe(ialu_reg_mem); +%} + +instruct MoveL2D_stack_reg_partial(regD dst, stackSlotL src) %{ + predicate(!UseXmmLoadAndClearUpper); + match(Set dst (MoveL2D src)); + effect(DEF dst, USE src); + + ins_cost(125); + format %{ "movlpd $dst, $src\t# MoveL2D_stack_reg" %} + opcode(0x66, 0x0F, 0x12); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + +instruct MoveL2D_stack_reg(regD dst, stackSlotL src) %{ + predicate(UseXmmLoadAndClearUpper); + match(Set dst (MoveL2D src)); + effect(DEF dst, USE src); + + ins_cost(125); + format %{ "movsd $dst, $src\t# MoveL2D_stack_reg" %} + opcode(0xF2, 0x0F, 0x10); + ins_encode(OpcP, REX_reg_mem(dst, src), OpcS, OpcT, reg_mem(dst, src)); + ins_pipe(pipe_slow); +%} + + +instruct MoveF2I_reg_stack(stackSlotI dst, regF src) %{ + match(Set dst (MoveF2I src)); + effect(DEF dst, USE src); + + ins_cost(95); // XXX + format %{ "movss $dst, $src\t# MoveF2I_reg_stack" %} + opcode(0xF3, 0x0F, 0x11); + ins_encode(OpcP, REX_reg_mem(src, dst), OpcS, OpcT, reg_mem(src, dst)); + ins_pipe(pipe_slow); +%} + +instruct MoveI2F_reg_stack(stackSlotF dst, rRegI src) %{ + match(Set dst (MoveI2F src)); + effect(DEF dst, USE src); + + ins_cost(100); + format %{ "movl $dst, $src\t# MoveI2F_reg_stack" %} + opcode(0x89); + ins_encode(REX_reg_mem(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe( ialu_mem_reg ); +%} + +instruct MoveD2L_reg_stack(stackSlotL dst, regD src) %{ + match(Set dst (MoveD2L src)); + effect(DEF dst, USE src); + + ins_cost(95); // XXX + format %{ "movsd $dst, $src\t# MoveL2D_reg_stack" %} + opcode(0xF2, 0x0F, 0x11); + ins_encode(OpcP, REX_reg_mem(src, dst), OpcS, OpcT, reg_mem(src, dst)); + ins_pipe(pipe_slow); +%} + +instruct MoveL2D_reg_stack(stackSlotD dst, rRegL src) %{ + match(Set dst (MoveL2D src)); + effect(DEF dst, USE src); + + ins_cost(100); + format %{ "movq $dst, $src\t# MoveL2D_reg_stack" %} + opcode(0x89); + ins_encode(REX_reg_mem_wide(src, dst), OpcP, reg_mem(src, dst)); + ins_pipe(ialu_mem_reg); +%} + +instruct MoveF2I_reg_reg(rRegI dst, regF src) %{ + match(Set dst (MoveF2I src)); + effect(DEF dst, USE src); + ins_cost(85); + format %{ "movd $dst,$src\t# MoveF2I" %} + ins_encode %{ __ movdl($dst$$Register, $src$$XMMRegister); %} + ins_pipe( pipe_slow ); +%} + +instruct MoveD2L_reg_reg(rRegL dst, regD src) %{ + match(Set dst (MoveD2L src)); + effect(DEF dst, USE src); + ins_cost(85); + format %{ "movd $dst,$src\t# MoveD2L" %} + ins_encode %{ __ movdq($dst$$Register, $src$$XMMRegister); %} + ins_pipe( pipe_slow ); +%} + +// The next instructions have long latency and use Int unit. Set high cost. +instruct MoveI2F_reg_reg(regF dst, rRegI src) %{ + match(Set dst (MoveI2F src)); + effect(DEF dst, USE src); + ins_cost(300); + format %{ "movd $dst,$src\t# MoveI2F" %} + ins_encode %{ __ movdl($dst$$XMMRegister, $src$$Register); %} + ins_pipe( pipe_slow ); +%} + +instruct MoveL2D_reg_reg(regD dst, rRegL src) %{ + match(Set dst (MoveL2D src)); + effect(DEF dst, USE src); + ins_cost(300); + format %{ "movd $dst,$src\t# MoveL2D" %} + ins_encode %{ __ movdq($dst$$XMMRegister, $src$$Register); %} + ins_pipe( pipe_slow ); +%} + +// Replicate scalar to packed byte (1 byte) values in xmm +instruct Repl8B_reg(regD dst, regD src) %{ + match(Set dst (Replicate8B src)); + format %{ "MOVDQA $dst,$src\n\t" + "PUNPCKLBW $dst,$dst\n\t" + "PSHUFLW $dst,$dst,0x00\t! replicate8B" %} + ins_encode( pshufd_8x8(dst, src)); + ins_pipe( pipe_slow ); +%} + +// Replicate scalar to packed byte (1 byte) values in xmm +instruct Repl8B_rRegI(regD dst, rRegI src) %{ + match(Set dst (Replicate8B src)); + format %{ "MOVD $dst,$src\n\t" + "PUNPCKLBW $dst,$dst\n\t" + "PSHUFLW $dst,$dst,0x00\t! replicate8B" %} + ins_encode( mov_i2x(dst, src), pshufd_8x8(dst, dst)); + ins_pipe( pipe_slow ); +%} + +// Replicate scalar zero to packed byte (1 byte) values in xmm +instruct Repl8B_immI0(regD dst, immI0 zero) %{ + match(Set dst (Replicate8B zero)); + format %{ "PXOR $dst,$dst\t! replicate8B" %} + ins_encode( pxor(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed shore (2 byte) values in xmm +instruct Repl4S_reg(regD dst, regD src) %{ + match(Set dst (Replicate4S src)); + format %{ "PSHUFLW $dst,$src,0x00\t! replicate4S" %} + ins_encode( pshufd_4x16(dst, src)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed shore (2 byte) values in xmm +instruct Repl4S_rRegI(regD dst, rRegI src) %{ + match(Set dst (Replicate4S src)); + format %{ "MOVD $dst,$src\n\t" + "PSHUFLW $dst,$dst,0x00\t! replicate4S" %} + ins_encode( mov_i2x(dst, src), pshufd_4x16(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar zero to packed short (2 byte) values in xmm +instruct Repl4S_immI0(regD dst, immI0 zero) %{ + match(Set dst (Replicate4S zero)); + format %{ "PXOR $dst,$dst\t! replicate4S" %} + ins_encode( pxor(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed char (2 byte) values in xmm +instruct Repl4C_reg(regD dst, regD src) %{ + match(Set dst (Replicate4C src)); + format %{ "PSHUFLW $dst,$src,0x00\t! replicate4C" %} + ins_encode( pshufd_4x16(dst, src)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed char (2 byte) values in xmm +instruct Repl4C_rRegI(regD dst, rRegI src) %{ + match(Set dst (Replicate4C src)); + format %{ "MOVD $dst,$src\n\t" + "PSHUFLW $dst,$dst,0x00\t! replicate4C" %} + ins_encode( mov_i2x(dst, src), pshufd_4x16(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar zero to packed char (2 byte) values in xmm +instruct Repl4C_immI0(regD dst, immI0 zero) %{ + match(Set dst (Replicate4C zero)); + format %{ "PXOR $dst,$dst\t! replicate4C" %} + ins_encode( pxor(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed integer (4 byte) values in xmm +instruct Repl2I_reg(regD dst, regD src) %{ + match(Set dst (Replicate2I src)); + format %{ "PSHUFD $dst,$src,0x00\t! replicate2I" %} + ins_encode( pshufd(dst, src, 0x00)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed integer (4 byte) values in xmm +instruct Repl2I_rRegI(regD dst, rRegI src) %{ + match(Set dst (Replicate2I src)); + format %{ "MOVD $dst,$src\n\t" + "PSHUFD $dst,$dst,0x00\t! replicate2I" %} + ins_encode( mov_i2x(dst, src), pshufd(dst, dst, 0x00)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar zero to packed integer (2 byte) values in xmm +instruct Repl2I_immI0(regD dst, immI0 zero) %{ + match(Set dst (Replicate2I zero)); + format %{ "PXOR $dst,$dst\t! replicate2I" %} + ins_encode( pxor(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed single precision floating point values in xmm +instruct Repl2F_reg(regD dst, regD src) %{ + match(Set dst (Replicate2F src)); + format %{ "PSHUFD $dst,$src,0xe0\t! replicate2F" %} + ins_encode( pshufd(dst, src, 0xe0)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed single precision floating point values in xmm +instruct Repl2F_regF(regD dst, regF src) %{ + match(Set dst (Replicate2F src)); + format %{ "PSHUFD $dst,$src,0xe0\t! replicate2F" %} + ins_encode( pshufd(dst, src, 0xe0)); + ins_pipe( fpu_reg_reg ); +%} + +// Replicate scalar to packed single precision floating point values in xmm +instruct Repl2F_immF0(regD dst, immF0 zero) %{ + match(Set dst (Replicate2F zero)); + format %{ "PXOR $dst,$dst\t! replicate2F" %} + ins_encode( pxor(dst, dst)); + ins_pipe( fpu_reg_reg ); +%} + + +// ======================================================================= +// fast clearing of an array +instruct rep_stos(rcx_RegL cnt, rdi_RegP base, rax_RegI zero, Universe dummy, + rFlagsReg cr) +%{ + match(Set dummy (ClearArray cnt base)); + effect(USE_KILL cnt, USE_KILL base, KILL zero, KILL cr); + + format %{ "xorl rax, rax\t# ClearArray:\n\t" + "rep stosq\t# Store rax to *rdi++ while rcx--" %} + ins_encode(opc_reg_reg(0x33, RAX, RAX), // xorl %eax, %eax + Opcode(0xF3), Opcode(0x48), Opcode(0xAB)); // rep REX_W stos + ins_pipe(pipe_slow); +%} + +instruct string_compare(rdi_RegP str1, rsi_RegP str2, rax_RegI tmp1, + rbx_RegI tmp2, rcx_RegI result, rFlagsReg cr) +%{ + match(Set result (StrComp str1 str2)); + effect(USE_KILL str1, USE_KILL str2, KILL tmp1, KILL tmp2, KILL cr); + //ins_cost(300); + + format %{ "String Compare $str1, $str2 -> $result // XXX KILL RAX, RBX" %} + ins_encode( enc_String_Compare() ); + ins_pipe( pipe_slow ); +%} + +//----------Control Flow Instructions------------------------------------------ +// Signed compare Instructions + +// XXX more variants!! +instruct compI_rReg(rFlagsReg cr, rRegI op1, rRegI op2) +%{ + match(Set cr (CmpI op1 op2)); + effect(DEF cr, USE op1, USE op2); + + format %{ "cmpl $op1, $op2" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_reg(op1, op2), OpcP, reg_reg(op1, op2)); + ins_pipe(ialu_cr_reg_reg); +%} + +instruct compI_rReg_imm(rFlagsReg cr, rRegI op1, immI op2) +%{ + match(Set cr (CmpI op1 op2)); + + format %{ "cmpl $op1, $op2" %} + opcode(0x81, 0x07); /* Opcode 81 /7 */ + ins_encode(OpcSErm(op1, op2), Con8or32(op2)); + ins_pipe(ialu_cr_reg_imm); +%} + +instruct compI_rReg_mem(rFlagsReg cr, rRegI op1, memory op2) +%{ + match(Set cr (CmpI op1 (LoadI op2))); + + ins_cost(500); // XXX + format %{ "cmpl $op1, $op2" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_mem(op1, op2), OpcP, reg_mem(op1, op2)); + ins_pipe(ialu_cr_reg_mem); +%} + +instruct testI_reg(rFlagsReg cr, rRegI src, immI0 zero) +%{ + match(Set cr (CmpI src zero)); + + format %{ "testl $src, $src" %} + opcode(0x85); + ins_encode(REX_reg_reg(src, src), OpcP, reg_reg(src, src)); + ins_pipe(ialu_cr_reg_imm); +%} + +instruct testI_reg_imm(rFlagsReg cr, rRegI src, immI con, immI0 zero) +%{ + match(Set cr (CmpI (AndI src con) zero)); + + format %{ "testl $src, $con" %} + opcode(0xF7, 0x00); + ins_encode(REX_reg(src), OpcP, reg_opc(src), Con32(con)); + ins_pipe(ialu_cr_reg_imm); +%} + +instruct testI_reg_mem(rFlagsReg cr, rRegI src, memory mem, immI0 zero) +%{ + match(Set cr (CmpI (AndI src (LoadI mem)) zero)); + + format %{ "testl $src, $mem" %} + opcode(0x85); + ins_encode(REX_reg_mem(src, mem), OpcP, reg_mem(src, mem)); + ins_pipe(ialu_cr_reg_mem); +%} + +// Unsigned compare Instructions; really, same as signed except they +// produce an rFlagsRegU instead of rFlagsReg. +instruct compU_rReg(rFlagsRegU cr, rRegI op1, rRegI op2) +%{ + match(Set cr (CmpU op1 op2)); + + format %{ "cmpl $op1, $op2\t# unsigned" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_reg(op1, op2), OpcP, reg_reg(op1, op2)); + ins_pipe(ialu_cr_reg_reg); +%} + +instruct compU_rReg_imm(rFlagsRegU cr, rRegI op1, immI op2) +%{ + match(Set cr (CmpU op1 op2)); + + format %{ "cmpl $op1, $op2\t# unsigned" %} + opcode(0x81,0x07); /* Opcode 81 /7 */ + ins_encode(OpcSErm(op1, op2), Con8or32(op2)); + ins_pipe(ialu_cr_reg_imm); +%} + +instruct compU_rReg_mem(rFlagsRegU cr, rRegI op1, memory op2) +%{ + match(Set cr (CmpU op1 (LoadI op2))); + + ins_cost(500); // XXX + format %{ "cmpl $op1, $op2\t# unsigned" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_mem(op1, op2), OpcP, reg_mem(op1, op2)); + ins_pipe(ialu_cr_reg_mem); +%} + +// // // Cisc-spilled version of cmpU_rReg +// //instruct compU_mem_rReg(rFlagsRegU cr, memory op1, rRegI op2) +// //%{ +// // match(Set cr (CmpU (LoadI op1) op2)); +// // +// // format %{ "CMPu $op1,$op2" %} +// // ins_cost(500); +// // opcode(0x39); /* Opcode 39 /r */ +// // ins_encode( OpcP, reg_mem( op1, op2) ); +// //%} + +instruct testU_reg(rFlagsRegU cr, rRegI src, immI0 zero) +%{ + match(Set cr (CmpU src zero)); + + format %{ "testl $src, $src\t# unsigned" %} + opcode(0x85); + ins_encode(REX_reg_reg(src, src), OpcP, reg_reg(src, src)); + ins_pipe(ialu_cr_reg_imm); +%} + +instruct compP_rReg(rFlagsRegU cr, rRegP op1, rRegP op2) +%{ + match(Set cr (CmpP op1 op2)); + + format %{ "cmpq $op1, $op2\t# ptr" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_reg_wide(op1, op2), OpcP, reg_reg(op1, op2)); + ins_pipe(ialu_cr_reg_reg); +%} + +instruct compP_rReg_mem(rFlagsRegU cr, rRegP op1, memory op2) +%{ + match(Set cr (CmpP op1 (LoadP op2))); + + ins_cost(500); // XXX + format %{ "cmpq $op1, $op2\t# ptr" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_mem_wide(op1, op2), OpcP, reg_mem(op1, op2)); + ins_pipe(ialu_cr_reg_mem); +%} + +// // // Cisc-spilled version of cmpP_rReg +// //instruct compP_mem_rReg(rFlagsRegU cr, memory op1, rRegP op2) +// //%{ +// // match(Set cr (CmpP (LoadP op1) op2)); +// // +// // format %{ "CMPu $op1,$op2" %} +// // ins_cost(500); +// // opcode(0x39); /* Opcode 39 /r */ +// // ins_encode( OpcP, reg_mem( op1, op2) ); +// //%} + +// XXX this is generalized by compP_rReg_mem??? +// Compare raw pointer (used in out-of-heap check). +// Only works because non-oop pointers must be raw pointers +// and raw pointers have no anti-dependencies. +instruct compP_mem_rReg(rFlagsRegU cr, rRegP op1, memory op2) +%{ + predicate(!n->in(2)->in(2)->bottom_type()->isa_oop_ptr()); + match(Set cr (CmpP op1 (LoadP op2))); + + format %{ "cmpq $op1, $op2\t# raw ptr" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_mem_wide(op1, op2), OpcP, reg_mem(op1, op2)); + ins_pipe(ialu_cr_reg_mem); +%} + +// This will generate a signed flags result. This should be OK since +// any compare to a zero should be eq/neq. +instruct testP_reg(rFlagsReg cr, rRegP src, immP0 zero) +%{ + match(Set cr (CmpP src zero)); + + format %{ "testq $src, $src\t# ptr" %} + opcode(0x85); + ins_encode(REX_reg_reg_wide(src, src), OpcP, reg_reg(src, src)); + ins_pipe(ialu_cr_reg_imm); +%} + +// This will generate a signed flags result. This should be OK since +// any compare to a zero should be eq/neq. +instruct testP_reg_mem(rFlagsReg cr, memory op, immP0 zero) +%{ + match(Set cr (CmpP (LoadP op) zero)); + + ins_cost(500); // XXX + format %{ "testq $op, 0xffffffffffffffff\t# ptr" %} + opcode(0xF7); /* Opcode F7 /0 */ + ins_encode(REX_mem_wide(op), + OpcP, RM_opc_mem(0x00, op), Con_d32(0xFFFFFFFF)); + ins_pipe(ialu_cr_reg_imm); +%} + +// Yanked all unsigned pointer compare operations. +// Pointer compares are done with CmpP which is already unsigned. + +instruct compL_rReg(rFlagsReg cr, rRegL op1, rRegL op2) +%{ + match(Set cr (CmpL op1 op2)); + + format %{ "cmpq $op1, $op2" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_reg_wide(op1, op2), OpcP, reg_reg(op1, op2)); + ins_pipe(ialu_cr_reg_reg); +%} + +instruct compL_rReg_imm(rFlagsReg cr, rRegL op1, immL32 op2) +%{ + match(Set cr (CmpL op1 op2)); + + format %{ "cmpq $op1, $op2" %} + opcode(0x81, 0x07); /* Opcode 81 /7 */ + ins_encode(OpcSErm_wide(op1, op2), Con8or32(op2)); + ins_pipe(ialu_cr_reg_imm); +%} + +instruct compL_rReg_mem(rFlagsReg cr, rRegL op1, memory op2) +%{ + match(Set cr (CmpL op1 (LoadL op2))); + + ins_cost(500); // XXX + format %{ "cmpq $op1, $op2" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_mem_wide(op1, op2), OpcP, reg_mem(op1, op2)); + ins_pipe(ialu_cr_reg_mem); +%} + +instruct testL_reg(rFlagsReg cr, rRegL src, immL0 zero) +%{ + match(Set cr (CmpL src zero)); + + format %{ "testq $src, $src" %} + opcode(0x85); + ins_encode(REX_reg_reg_wide(src, src), OpcP, reg_reg(src, src)); + ins_pipe(ialu_cr_reg_imm); +%} + +instruct testL_reg_imm(rFlagsReg cr, rRegL src, immL32 con, immL0 zero) +%{ + match(Set cr (CmpL (AndL src con) zero)); + + format %{ "testq $src, $con\t# long" %} + opcode(0xF7, 0x00); + ins_encode(REX_reg_wide(src), OpcP, reg_opc(src), Con32(con)); + ins_pipe(ialu_cr_reg_imm); +%} + +instruct testL_reg_mem(rFlagsReg cr, rRegL src, memory mem, immL0 zero) +%{ + match(Set cr (CmpL (AndL src (LoadL mem)) zero)); + + format %{ "testq $src, $mem" %} + opcode(0x85); + ins_encode(REX_reg_mem_wide(src, mem), OpcP, reg_mem(src, mem)); + ins_pipe(ialu_cr_reg_mem); +%} + +// Manifest a CmpL result in an integer register. Very painful. +// This is the test to avoid. +instruct cmpL3_reg_reg(rRegI dst, rRegL src1, rRegL src2, rFlagsReg flags) +%{ + match(Set dst (CmpL3 src1 src2)); + effect(KILL flags); + + ins_cost(275); // XXX + format %{ "cmpq $src1, $src2\t# CmpL3\n\t" + "movl $dst, -1\n\t" + "jl,s done\n\t" + "setne $dst\n\t" + "movzbl $dst, $dst\n\t" + "done:" %} + ins_encode(cmpl3_flag(src1, src2, dst)); + ins_pipe(pipe_slow); +%} + +//----------Max and Min-------------------------------------------------------- +// Min Instructions + +instruct cmovI_reg_g(rRegI dst, rRegI src, rFlagsReg cr) +%{ + effect(USE_DEF dst, USE src, USE cr); + + format %{ "cmovlgt $dst, $src\t# min" %} + opcode(0x0F, 0x4F); + ins_encode(REX_reg_reg(dst, src), OpcP, OpcS, reg_reg(dst, src)); + ins_pipe(pipe_cmov_reg); +%} + + +instruct minI_rReg(rRegI dst, rRegI src) +%{ + match(Set dst (MinI dst src)); + + ins_cost(200); + expand %{ + rFlagsReg cr; + compI_rReg(cr, dst, src); + cmovI_reg_g(dst, src, cr); + %} +%} + +instruct cmovI_reg_l(rRegI dst, rRegI src, rFlagsReg cr) +%{ + effect(USE_DEF dst, USE src, USE cr); + + format %{ "cmovllt $dst, $src\t# max" %} + opcode(0x0F, 0x4C); + ins_encode(REX_reg_reg(dst, src), OpcP, OpcS, reg_reg(dst, src)); + ins_pipe(pipe_cmov_reg); +%} + + +instruct maxI_rReg(rRegI dst, rRegI src) +%{ + match(Set dst (MaxI dst src)); + + ins_cost(200); + expand %{ + rFlagsReg cr; + compI_rReg(cr, dst, src); + cmovI_reg_l(dst, src, cr); + %} +%} + +// ============================================================================ +// Branch Instructions + +// Jump Direct - Label defines a relative address from JMP+1 +instruct jmpDir(label labl) +%{ + match(Goto); + effect(USE labl); + + ins_cost(300); + format %{ "jmp $labl" %} + size(5); + opcode(0xE9); + ins_encode(OpcP, Lbl(labl)); + ins_pipe(pipe_jmp); + ins_pc_relative(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpCon(cmpOp cop, rFlagsReg cr, label labl) +%{ + match(If cop cr); + effect(USE labl); + + ins_cost(300); + format %{ "j$cop $labl" %} + size(6); + opcode(0x0F, 0x80); + ins_encode(Jcc(cop, labl)); + ins_pipe(pipe_jcc); + ins_pc_relative(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpLoopEnd(cmpOp cop, rFlagsReg cr, label labl) +%{ + match(CountedLoopEnd cop cr); + effect(USE labl); + + ins_cost(300); + format %{ "j$cop $labl\t# loop end" %} + size(6); + opcode(0x0F, 0x80); + ins_encode(Jcc(cop, labl)); + ins_pipe(pipe_jcc); + ins_pc_relative(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpLoopEndU(cmpOpU cop, rFlagsRegU cmp, label labl) +%{ + match(CountedLoopEnd cop cmp); + effect(USE labl); + + ins_cost(300); + format %{ "j$cop,u $labl\t# loop end" %} + size(6); + opcode(0x0F, 0x80); + ins_encode(Jcc(cop, labl)); + ins_pipe(pipe_jcc); + ins_pc_relative(1); +%} + +// Jump Direct Conditional - using unsigned comparison +instruct jmpConU(cmpOpU cop, rFlagsRegU cmp, label labl) +%{ + match(If cop cmp); + effect(USE labl); + + ins_cost(300); + format %{ "j$cop,u $labl" %} + size(6); + opcode(0x0F, 0x80); + ins_encode(Jcc(cop, labl)); + ins_pipe(pipe_jcc); + ins_pc_relative(1); +%} + +// ============================================================================ +// The 2nd slow-half of a subtype check. Scan the subklass's 2ndary +// superklass array for an instance of the superklass. Set a hidden +// internal cache on a hit (cache is checked with exposed code in +// gen_subtype_check()). Return NZ for a miss or zero for a hit. The +// encoding ALSO sets flags. + +instruct partialSubtypeCheck(rdi_RegP result, + rsi_RegP sub, rax_RegP super, rcx_RegI rcx, + rFlagsReg cr) +%{ + match(Set result (PartialSubtypeCheck sub super)); + effect(KILL rcx, KILL cr); + + ins_cost(1100); // slightly larger than the next version + format %{ "cmpq rax, rsi\n\t" + "jeq,s hit\n\t" + "movq rdi, [$sub + (sizeof(oopDesc) + Klass::secondary_supers_offset_in_bytes())]\n\t" + "movl rcx, [rdi + arrayOopDesc::length_offset_in_bytes()]\t# length to scan\n\t" + "addq rdi, arrayOopDex::base_offset_in_bytes(T_OBJECT)\t# Skip to start of data; set NZ in case count is zero\n\t" + "repne scasq\t# Scan *rdi++ for a match with rax while rcx--\n\t" + "jne,s miss\t\t# Missed: rdi not-zero\n\t" + "movq [$sub + (sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes())], $super\t# Hit: update cache\n\t" + "hit:\n\t" + "xorq $result, $result\t\t Hit: rdi zero\n\t" + "miss:\t" %} + + opcode(0x1); // Force a XOR of RDI + ins_encode(enc_PartialSubtypeCheck()); + ins_pipe(pipe_slow); +%} + +instruct partialSubtypeCheck_vs_Zero(rFlagsReg cr, + rsi_RegP sub, rax_RegP super, rcx_RegI rcx, + immP0 zero, + rdi_RegP result) +%{ + match(Set cr (CmpP (PartialSubtypeCheck sub super) zero)); + effect(KILL rcx, KILL result); + + ins_cost(1000); + format %{ "cmpq rax, rsi\n\t" + "jeq,s miss\t# Actually a hit; we are done.\n\t" + "movq rdi, [$sub + (sizeof(oopDesc) + Klass::secondary_supers_offset_in_bytes())]\n\t" + "movl rcx, [rdi + arrayOopDesc::length_offset_in_bytes()]\t# length to scan\n\t" + "addq rdi, arrayOopDex::base_offset_in_bytes(T_OBJECT)\t# Skip to start of data; set NZ in case count is zero\n\t" + "repne scasq\t# Scan *rdi++ for a match with rax while cx-- != 0\n\t" + "jne,s miss\t\t# Missed: flags nz\n\t" + "movq [$sub + (sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes())], $super\t# Hit: update cache\n\t" + "miss:\t" %} + + opcode(0x0); // No need to XOR RDI + ins_encode(enc_PartialSubtypeCheck()); + ins_pipe(pipe_slow); +%} + +// ============================================================================ +// Branch Instructions -- short offset versions +// +// These instructions are used to replace jumps of a long offset (the default +// match) with jumps of a shorter offset. These instructions are all tagged +// with the ins_short_branch attribute, which causes the ADLC to suppress the +// match rules in general matching. Instead, the ADLC generates a conversion +// method in the MachNode which can be used to do in-place replacement of the +// long variant with the shorter variant. The compiler will determine if a +// branch can be taken by the is_short_branch_offset() predicate in the machine +// specific code section of the file. + +// Jump Direct - Label defines a relative address from JMP+1 +instruct jmpDir_short(label labl) +%{ + match(Goto); + effect(USE labl); + + ins_cost(300); + format %{ "jmp,s $labl" %} + size(2); + opcode(0xEB); + ins_encode(OpcP, LblShort(labl)); + ins_pipe(pipe_jmp); + ins_pc_relative(1); + ins_short_branch(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpCon_short(cmpOp cop, rFlagsReg cr, label labl) +%{ + match(If cop cr); + effect(USE labl); + + ins_cost(300); + format %{ "j$cop,s $labl" %} + size(2); + opcode(0x70); + ins_encode(JccShort(cop, labl)); + ins_pipe(pipe_jcc); + ins_pc_relative(1); + ins_short_branch(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpLoopEnd_short(cmpOp cop, rFlagsReg cr, label labl) +%{ + match(CountedLoopEnd cop cr); + effect(USE labl); + + ins_cost(300); + format %{ "j$cop,s $labl" %} + size(2); + opcode(0x70); + ins_encode(JccShort(cop, labl)); + ins_pipe(pipe_jcc); + ins_pc_relative(1); + ins_short_branch(1); +%} + +// Jump Direct Conditional - Label defines a relative address from Jcc+1 +instruct jmpLoopEndU_short(cmpOpU cop, rFlagsRegU cmp, label labl) +%{ + match(CountedLoopEnd cop cmp); + effect(USE labl); + + ins_cost(300); + format %{ "j$cop,us $labl" %} + size(2); + opcode(0x70); + ins_encode(JccShort(cop, labl)); + ins_pipe(pipe_jcc); + ins_pc_relative(1); + ins_short_branch(1); +%} + +// Jump Direct Conditional - using unsigned comparison +instruct jmpConU_short(cmpOpU cop, rFlagsRegU cmp, label labl) +%{ + match(If cop cmp); + effect(USE labl); + + ins_cost(300); + format %{ "j$cop,us $labl" %} + size(2); + opcode(0x70); + ins_encode(JccShort(cop, labl)); + ins_pipe(pipe_jcc); + ins_pc_relative(1); + ins_short_branch(1); +%} + +// ============================================================================ +// inlined locking and unlocking + +instruct cmpFastLock(rFlagsReg cr, + rRegP object, rRegP box, rax_RegI tmp, rRegP scr) +%{ + match(Set cr (FastLock object box)); + effect(TEMP tmp, TEMP scr); + + ins_cost(300); + format %{ "fastlock $object,$box,$tmp,$scr" %} + ins_encode(Fast_Lock(object, box, tmp, scr)); + ins_pipe(pipe_slow); + ins_pc_relative(1); +%} + +instruct cmpFastUnlock(rFlagsReg cr, + rRegP object, rax_RegP box, rRegP tmp) +%{ + match(Set cr (FastUnlock object box)); + effect(TEMP tmp); + + ins_cost(300); + format %{ "fastunlock $object, $box, $tmp" %} + ins_encode(Fast_Unlock(object, box, tmp)); + ins_pipe(pipe_slow); + ins_pc_relative(1); +%} + + +// ============================================================================ +// Safepoint Instructions +instruct safePoint_poll(rFlagsReg cr) +%{ + match(SafePoint); + effect(KILL cr); + + format %{ "testl rax, [rip + #offset_to_poll_page]\t" + "# Safepoint: poll for GC" %} + size(6); // Opcode + ModRM + Disp32 == 6 bytes + ins_cost(125); + ins_encode(enc_safepoint_poll); + ins_pipe(ialu_reg_mem); +%} + +// ============================================================================ +// Procedure Call/Return Instructions +// Call Java Static Instruction +// Note: If this code changes, the corresponding ret_addr_offset() and +// compute_padding() functions will have to be adjusted. +instruct CallStaticJavaDirect(method meth) +%{ + match(CallStaticJava); + effect(USE meth); + + ins_cost(300); + format %{ "call,static " %} + opcode(0xE8); /* E8 cd */ + ins_encode(Java_Static_Call(meth), call_epilog); + ins_pipe(pipe_slow); + ins_pc_relative(1); + ins_alignment(4); +%} + +// Call Java Dynamic Instruction +// Note: If this code changes, the corresponding ret_addr_offset() and +// compute_padding() functions will have to be adjusted. +instruct CallDynamicJavaDirect(method meth) +%{ + match(CallDynamicJava); + effect(USE meth); + + ins_cost(300); + format %{ "movq rax, #Universe::non_oop_word()\n\t" + "call,dynamic " %} + opcode(0xE8); /* E8 cd */ + ins_encode(Java_Dynamic_Call(meth), call_epilog); + ins_pipe(pipe_slow); + ins_pc_relative(1); + ins_alignment(4); +%} + +// Call Runtime Instruction +instruct CallRuntimeDirect(method meth) +%{ + match(CallRuntime); + effect(USE meth); + + ins_cost(300); + format %{ "call,runtime " %} + opcode(0xE8); /* E8 cd */ + ins_encode(Java_To_Runtime(meth)); + ins_pipe(pipe_slow); + ins_pc_relative(1); +%} + +// Call runtime without safepoint +instruct CallLeafDirect(method meth) +%{ + match(CallLeaf); + effect(USE meth); + + ins_cost(300); + format %{ "call_leaf,runtime " %} + opcode(0xE8); /* E8 cd */ + ins_encode(Java_To_Runtime(meth)); + ins_pipe(pipe_slow); + ins_pc_relative(1); +%} + +// Call runtime without safepoint +instruct CallLeafNoFPDirect(method meth) +%{ + match(CallLeafNoFP); + effect(USE meth); + + ins_cost(300); + format %{ "call_leaf_nofp,runtime " %} + opcode(0xE8); /* E8 cd */ + ins_encode(Java_To_Runtime(meth)); + ins_pipe(pipe_slow); + ins_pc_relative(1); +%} + +// Return Instruction +// Remove the return address & jump to it. +// Notice: We always emit a nop after a ret to make sure there is room +// for safepoint patching +instruct Ret() +%{ + match(Return); + + format %{ "ret" %} + opcode(0xC3); + ins_encode(OpcP); + ins_pipe(pipe_jmp); +%} + +// Tail Call; Jump from runtime stub to Java code. +// Also known as an 'interprocedural jump'. +// Target of jump will eventually return to caller. +// TailJump below removes the return address. +instruct TailCalljmpInd(no_rbp_RegP jump_target, rbx_RegP method_oop) +%{ + match(TailCall jump_target method_oop); + + ins_cost(300); + format %{ "jmp $jump_target\t# rbx holds method oop" %} + opcode(0xFF, 0x4); /* Opcode FF /4 */ + ins_encode(REX_reg(jump_target), OpcP, reg_opc(jump_target)); + ins_pipe(pipe_jmp); +%} + +// Tail Jump; remove the return address; jump to target. +// TailCall above leaves the return address around. +instruct tailjmpInd(no_rbp_RegP jump_target, rax_RegP ex_oop) +%{ + match(TailJump jump_target ex_oop); + + ins_cost(300); + format %{ "popq rdx\t# pop return address\n\t" + "jmp $jump_target" %} + opcode(0xFF, 0x4); /* Opcode FF /4 */ + ins_encode(Opcode(0x5a), // popq rdx + REX_reg(jump_target), OpcP, reg_opc(jump_target)); + ins_pipe(pipe_jmp); +%} + +// Create exception oop: created by stack-crawling runtime code. +// Created exception is now available to this handler, and is setup +// just prior to jumping to this handler. No code emitted. +instruct CreateException(rax_RegP ex_oop) +%{ + match(Set ex_oop (CreateEx)); + + size(0); + // use the following format syntax + format %{ "# exception oop is in rax; no code emitted" %} + ins_encode(); + ins_pipe(empty); +%} + +// Rethrow exception: +// The exception oop will come in the first argument position. +// Then JUMP (not call) to the rethrow stub code. +instruct RethrowException() +%{ + match(Rethrow); + + // use the following format syntax + format %{ "jmp rethrow_stub" %} + ins_encode(enc_rethrow); + ins_pipe(pipe_jmp); +%} + + +//----------PEEPHOLE RULES----------------------------------------------------- +// These must follow all instruction definitions as they use the names +// defined in the instructions definitions. +// +// peepmatch ( root_instr_name [precerding_instruction]* ); +// +// peepconstraint %{ +// (instruction_number.operand_name relational_op instruction_number.operand_name +// [, ...] ); +// // instruction numbers are zero-based using left to right order in peepmatch +// +// peepreplace ( instr_name ( [instruction_number.operand_name]* ) ); +// // provide an instruction_number.operand_name for each operand that appears +// // in the replacement instruction's match rule +// +// ---------VM FLAGS--------------------------------------------------------- +// +// All peephole optimizations can be turned off using -XX:-OptoPeephole +// +// Each peephole rule is given an identifying number starting with zero and +// increasing by one in the order seen by the parser. An individual peephole +// can be enabled, and all others disabled, by using -XX:OptoPeepholeAt=# +// on the command-line. +// +// ---------CURRENT LIMITATIONS---------------------------------------------- +// +// Only match adjacent instructions in same basic block +// Only equality constraints +// Only constraints between operands, not (0.dest_reg == RAX_enc) +// Only one replacement instruction +// +// ---------EXAMPLE---------------------------------------------------------- +// +// // pertinent parts of existing instructions in architecture description +// instruct movI(rRegI dst, rRegI src) +// %{ +// match(Set dst (CopyI src)); +// %} +// +// instruct incI_rReg(rRegI dst, immI1 src, rFlagsReg cr) +// %{ +// match(Set dst (AddI dst src)); +// effect(KILL cr); +// %} +// +// // Change (inc mov) to lea +// peephole %{ +// // increment preceeded by register-register move +// peepmatch ( incI_rReg movI ); +// // require that the destination register of the increment +// // match the destination register of the move +// peepconstraint ( 0.dst == 1.dst ); +// // construct a replacement instruction that sets +// // the destination to ( move's source register + one ) +// peepreplace ( leaI_rReg_immI( 0.dst 1.src 0.src ) ); +// %} +// + +// Implementation no longer uses movX instructions since +// machine-independent system no longer uses CopyX nodes. +// +// peephole +// %{ +// peepmatch (incI_rReg movI); +// peepconstraint (0.dst == 1.dst); +// peepreplace (leaI_rReg_immI(0.dst 1.src 0.src)); +// %} + +// peephole +// %{ +// peepmatch (decI_rReg movI); +// peepconstraint (0.dst == 1.dst); +// peepreplace (leaI_rReg_immI(0.dst 1.src 0.src)); +// %} + +// peephole +// %{ +// peepmatch (addI_rReg_imm movI); +// peepconstraint (0.dst == 1.dst); +// peepreplace (leaI_rReg_immI(0.dst 1.src 0.src)); +// %} + +// peephole +// %{ +// peepmatch (incL_rReg movL); +// peepconstraint (0.dst == 1.dst); +// peepreplace (leaL_rReg_immL(0.dst 1.src 0.src)); +// %} + +// peephole +// %{ +// peepmatch (decL_rReg movL); +// peepconstraint (0.dst == 1.dst); +// peepreplace (leaL_rReg_immL(0.dst 1.src 0.src)); +// %} + +// peephole +// %{ +// peepmatch (addL_rReg_imm movL); +// peepconstraint (0.dst == 1.dst); +// peepreplace (leaL_rReg_immL(0.dst 1.src 0.src)); +// %} + +// peephole +// %{ +// peepmatch (addP_rReg_imm movP); +// peepconstraint (0.dst == 1.dst); +// peepreplace (leaP_rReg_imm(0.dst 1.src 0.src)); +// %} + +// // Change load of spilled value to only a spill +// instruct storeI(memory mem, rRegI src) +// %{ +// match(Set mem (StoreI mem src)); +// %} +// +// instruct loadI(rRegI dst, memory mem) +// %{ +// match(Set dst (LoadI mem)); +// %} +// + +peephole +%{ + peepmatch (loadI storeI); + peepconstraint (1.src == 0.dst, 1.mem == 0.mem); + peepreplace (storeI(1.mem 1.mem 1.src)); +%} + +peephole +%{ + peepmatch (loadL storeL); + peepconstraint (1.src == 0.dst, 1.mem == 0.mem); + peepreplace (storeL(1.mem 1.mem 1.src)); +%} + +//----------SMARTSPILL RULES--------------------------------------------------- +// These must follow all instruction definitions as they use the names +// defined in the instructions definitions. diff --git a/hotspot/src/os/linux/launcher/java.c b/hotspot/src/os/linux/launcher/java.c new file mode 100644 index 00000000000..d538db1d03d --- /dev/null +++ b/hotspot/src/os/linux/launcher/java.c @@ -0,0 +1,1841 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Gamma (Hotspot internal engineering test) launcher based on 1.6.0-b28 JDK, + * search "GAMMA" for gamma specific changes. + * + * GAMMA: gamma launcher is much simpler than regular java launcher in that + * JVM is either statically linked in or it is installed in the + * same directory where the launcher exists, so we don't have to + * worry about choosing the right JVM based on command line flag, jar + * file and/or ergonomics. Intead of removing unused logic from source + * they are commented out with #ifndef GAMMA, hopefully it'll be easier + * to maintain this file in sync with regular JDK launcher. + */ + +/* + * Shared source for 'java' command line tool. + * + * If JAVA_ARGS is defined, then acts as a launcher for applications. For + * instance, the JDK command line tools such as javac and javadoc (see + * makefiles for more details) are built with this program. Any arguments + * prefixed with '-J' will be passed directly to the 'java' command. + */ + +#ifdef GAMMA +# ifdef JAVA_ARGS +# error Do NOT define JAVA_ARGS when building gamma launcher +# endif +# if !defined(LINK_INTO_AOUT) && !defined(LINK_INTO_LIBJVM) +# error Either LINK_INTO_AOUT or LINK_INTO_LIBJVM must be defined +# endif +#endif + +/* + * One job of the launcher is to remove command line options which the + * vm does not understand and will not process. These options include + * options which select which style of vm is run (e.g. -client and + * -server) as well as options which select the data model to use. + * Additionally, for tools which invoke an underlying vm "-J-foo" + * options are turned into "-foo" options to the vm. This option + * filtering is handled in a number of places in the launcher, some of + * it in machine-dependent code. In this file, the function + * CheckJVMType removes vm style options and TranslateDashJArgs + * removes "-J" prefixes. On unix platforms, the + * CreateExecutionEnvironment function from the unix java_md.c file + * processes and removes -d options. However, in case + * CreateExecutionEnvironment does not need to exec because + * LD_LIBRARY_PATH is set acceptably and the data model does not need + * to be changed, ParseArguments will screen out the redundant -d + * options and prevent them from being passed to the vm; this is done + * by using the machine-dependent call + * RemovableMachineDependentOption. + */ + +#include +#include +#include + +#include +#include "java.h" + +#ifndef GAMMA +#include "manifest_info.h" +#include "version_comp.h" +#endif + +#ifndef FULL_VERSION +#define FULL_VERSION JDK_MAJOR_VERSION "." JDK_MINOR_VERSION +#endif + +/* + * The following environment variable is used to influence the behavior + * of the jre exec'd through the SelectVersion routine. The command line + * options which specify the version are not passed to the exec'd version, + * because that jre may be an older version which wouldn't recognize them. + * This environment variable is known to this (and later) version and serves + * to suppress the version selection code. This is not only for efficiency, + * but also for correctness, since any command line options have been + * removed which would cause any value found in the manifest to be used. + * This would be incorrect because the command line options are defined + * to take precedence. + * + * The value associated with this environment variable is the MainClass + * name from within the executable jar file (if any). This is strictly a + * performance enhancement to avoid re-reading the jar file manifest. + * + * A NOTE TO DEVELOPERS: For performance reasons it is important that + * the program image remain relatively small until after SelectVersion + * CreateExecutionEnvironment have finished their possibly recursive + * processing. Watch everything, but resist all temptations to use Java + * interfaces. + */ +#define ENV_ENTRY "_JAVA_VERSION_SET" + +static jboolean printVersion = JNI_FALSE; /* print and exit */ +static jboolean showVersion = JNI_FALSE; /* print but continue */ +static char *progname; +jboolean _launcher_debug = JNI_FALSE; + +/* + * List of VM options to be specified when the VM is created. + */ +static JavaVMOption *options; +static int numOptions, maxOptions; + +/* + * Prototypes for functions internal to launcher. + */ +static void AddOption(char *str, void *info); +static void SetClassPath(char *s); +static void SelectVersion(int argc, char **argv, char **main_class); +static jboolean ParseArguments(int *pargc, char ***pargv, char **pjarfile, + char **pclassname, int *pret); +static jboolean InitializeJVM(JavaVM **pvm, JNIEnv **penv, + InvocationFunctions *ifn); +static jstring NewPlatformString(JNIEnv *env, char *s); +static jobjectArray NewPlatformStringArray(JNIEnv *env, char **strv, int strc); +static jclass LoadClass(JNIEnv *env, char *name); +static jstring GetMainClassName(JNIEnv *env, char *jarname); +static void SetJavaCommandLineProp(char* classname, char* jarfile, int argc, char** argv); +#ifdef GAMMA +static void SetJavaLauncherProp(void); +#endif + +#ifdef JAVA_ARGS +static void TranslateDashJArgs(int *pargc, char ***pargv); +static jboolean AddApplicationOptions(void); +#endif + +static void PrintJavaVersion(JNIEnv *env); +static void PrintUsage(void); +static jint PrintXUsage(void); + +static void SetPaths(int argc, char **argv); + +/* Maximum supported entries from jvm.cfg. */ +#define INIT_MAX_KNOWN_VMS 10 +/* Values for vmdesc.flag */ +#define VM_UNKNOWN -1 +#define VM_KNOWN 0 +#define VM_ALIASED_TO 1 +#define VM_WARN 2 +#define VM_ERROR 3 +#define VM_IF_SERVER_CLASS 4 +#define VM_IGNORE 5 +struct vmdesc { + char *name; + int flag; + char *alias; + char *server_class; +}; +static struct vmdesc *knownVMs = NULL; +static int knownVMsCount = 0; +static int knownVMsLimit = 0; + +static void GrowKnownVMs(); +static int KnownVMIndex(const char* name); +static void FreeKnownVMs(); + +jboolean ServerClassMachine(); + +/* flag which if set suppresses error messages from the launcher */ +static int noExitErrorMessage = 0; + +/* + * Entry point. + */ +int +main(int argc, char ** argv) +{ + JavaVM *vm = 0; + JNIEnv *env = 0; + char *jarfile = 0; + char *classname = 0; + char *s = 0; + char *main_class = NULL; + jstring mainClassName; + jclass mainClass; + jmethodID mainID; + jobjectArray mainArgs; + int ret; + InvocationFunctions ifn; + jlong start, end; + char jrepath[MAXPATHLEN], jvmpath[MAXPATHLEN]; + char ** original_argv = argv; + + /* + * Error message to print or display; by default the message will + * only be displayed in a window. + */ + char * message = "Fatal exception occurred. Program will exit."; + jboolean messageDest = JNI_FALSE; + + if (getenv("_JAVA_LAUNCHER_DEBUG") != 0) { + _launcher_debug = JNI_TRUE; + printf("----_JAVA_LAUNCHER_DEBUG----\n"); + } + +#ifndef GAMMA + /* + * Make sure the specified version of the JRE is running. + * + * There are three things to note about the SelectVersion() routine: + * 1) If the version running isn't correct, this routine doesn't + * return (either the correct version has been exec'd or an error + * was issued). + * 2) Argc and Argv in this scope are *not* altered by this routine. + * It is the responsibility of subsequent code to ignore the + * arguments handled by this routine. + * 3) As a side-effect, the variable "main_class" is guaranteed to + * be set (if it should ever be set). This isn't exactly the + * poster child for structured programming, but it is a small + * price to pay for not processing a jar file operand twice. + * (Note: This side effect has been disabled. See comment on + * bugid 5030265 below.) + */ + SelectVersion(argc, argv, &main_class); +#endif /* ifndef GAMMA */ + + /* copy original argv */ + { + int i; + original_argv = (char**)MemAlloc(sizeof(char*)*(argc+1)); + for(i = 0; i < argc+1; i++) + original_argv[i] = argv[i]; + } + + CreateExecutionEnvironment(&argc, &argv, + jrepath, sizeof(jrepath), + jvmpath, sizeof(jvmpath), + original_argv); + ifn.CreateJavaVM = 0; + ifn.GetDefaultJavaVMInitArgs = 0; + + if (_launcher_debug) + start = CounterGet(); + if (!LoadJavaVM(jvmpath, &ifn)) { + exit(6); + } + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to LoadJavaVM\n", + (long)(jint)Counter2Micros(end-start)); + } + +#ifdef JAVA_ARGS /* javac, jar and friends. */ + progname = "java"; +#else /* java, oldjava, javaw and friends */ +#ifdef PROGNAME + progname = PROGNAME; +#else + progname = *argv; + if ((s = strrchr(progname, FILE_SEPARATOR)) != 0) { + progname = s + 1; + } +#endif /* PROGNAME */ +#endif /* JAVA_ARGS */ + ++argv; + --argc; + +#ifdef JAVA_ARGS + /* Preprocess wrapper arguments */ + TranslateDashJArgs(&argc, &argv); + if (!AddApplicationOptions()) { + exit(1); + } +#endif + + /* Set default CLASSPATH */ + if ((s = getenv("CLASSPATH")) == 0) { + s = "."; + } +#ifndef JAVA_ARGS + SetClassPath(s); +#endif + + /* + * Parse command line options; if the return value of + * ParseArguments is false, the program should exit. + */ + if (!ParseArguments(&argc, &argv, &jarfile, &classname, &ret)) { + exit(ret); + } + + /* Override class path if -jar flag was specified */ + if (jarfile != 0) { + SetClassPath(jarfile); + } + + /* set the -Dsun.java.command pseudo property */ + SetJavaCommandLineProp(classname, jarfile, argc, argv); + +#ifdef GAMMA + /* Set the -Dsun.java.launcher pseudo property */ + SetJavaLauncherProp(); +#endif + + /* + * Done with all command line processing and potential re-execs so + * clean up the environment. + */ + (void)UnsetEnv(ENV_ENTRY); + + /* Initialize the virtual machine */ + + if (_launcher_debug) + start = CounterGet(); + if (!InitializeJVM(&vm, &env, &ifn)) { + ReportErrorMessage("Could not create the Java virtual machine.", + JNI_TRUE); + exit(1); + } + + if (printVersion || showVersion) { + PrintJavaVersion(env); + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + goto leave; + } + if (printVersion) { + ret = 0; + message = NULL; + goto leave; + } + if (showVersion) { + fprintf(stderr, "\n"); + } + } + + /* If the user specified neither a class name nor a JAR file */ + if (jarfile == 0 && classname == 0) { + PrintUsage(); + message = NULL; + goto leave; + } + +#ifndef GAMMA + FreeKnownVMs(); /* after last possible PrintUsage() */ +#endif + + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to InitializeJVM\n", + (long)(jint)Counter2Micros(end-start)); + } + + /* At this stage, argc/argv have the applications' arguments */ + if (_launcher_debug) { + int i = 0; + printf("Main-Class is '%s'\n", classname ? classname : ""); + printf("Apps' argc is %d\n", argc); + for (; i < argc; i++) { + printf(" argv[%2d] = '%s'\n", i, argv[i]); + } + } + + ret = 1; + + /* + * Get the application's main class. + * + * See bugid 5030265. The Main-Class name has already been parsed + * from the manifest, but not parsed properly for UTF-8 support. + * Hence the code here ignores the value previously extracted and + * uses the pre-existing code to reextract the value. This is + * possibly an end of release cycle expedient. However, it has + * also been discovered that passing some character sets through + * the environment has "strange" behavior on some variants of + * Windows. Hence, maybe the manifest parsing code local to the + * launcher should never be enhanced. + * + * Hence, future work should either: + * 1) Correct the local parsing code and verify that the + * Main-Class attribute gets properly passed through + * all environments, + * 2) Remove the vestages of maintaining main_class through + * the environment (and remove these comments). + */ + if (jarfile != 0) { + mainClassName = GetMainClassName(env, jarfile); + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + goto leave; + } + if (mainClassName == NULL) { + const char * format = "Failed to load Main-Class manifest " + "attribute from\n%s"; + message = (char*)MemAlloc((strlen(format) + strlen(jarfile)) * + sizeof(char)); + sprintf(message, format, jarfile); + messageDest = JNI_TRUE; + goto leave; + } + classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0); + if (classname == NULL) { + ReportExceptionDescription(env); + goto leave; + } + mainClass = LoadClass(env, classname); + if(mainClass == NULL) { /* exception occured */ + ReportExceptionDescription(env); + message = "Could not find the main class. Program will exit."; + goto leave; + } + (*env)->ReleaseStringUTFChars(env, mainClassName, classname); + } else { + mainClassName = NewPlatformString(env, classname); + if (mainClassName == NULL) { + const char * format = "Failed to load Main Class: %s"; + message = (char *)MemAlloc((strlen(format) + strlen(classname)) * + sizeof(char) ); + sprintf(message, format, classname); + messageDest = JNI_TRUE; + goto leave; + } + classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0); + if (classname == NULL) { + ReportExceptionDescription(env); + goto leave; + } + mainClass = LoadClass(env, classname); + if(mainClass == NULL) { /* exception occured */ + ReportExceptionDescription(env); + message = "Could not find the main class. Program will exit."; + goto leave; + } + (*env)->ReleaseStringUTFChars(env, mainClassName, classname); + } + + /* Get the application's main method */ + mainID = (*env)->GetStaticMethodID(env, mainClass, "main", + "([Ljava/lang/String;)V"); + if (mainID == NULL) { + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + } else { + message = "No main method found in specified class."; + messageDest = JNI_TRUE; + } + goto leave; + } + + { /* Make sure the main method is public */ + jint mods; + jmethodID mid; + jobject obj = (*env)->ToReflectedMethod(env, mainClass, + mainID, JNI_TRUE); + + if( obj == NULL) { /* exception occurred */ + ReportExceptionDescription(env); + goto leave; + } + + mid = + (*env)->GetMethodID(env, + (*env)->GetObjectClass(env, obj), + "getModifiers", "()I"); + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + goto leave; + } + + mods = (*env)->CallIntMethod(env, obj, mid); + if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) ... */ + message = "Main method not public."; + messageDest = JNI_TRUE; + goto leave; + } + } + + /* Build argument array */ + mainArgs = NewPlatformStringArray(env, argv, argc); + if (mainArgs == NULL) { + ReportExceptionDescription(env); + goto leave; + } + + /* Invoke main method. */ + (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs); + + /* + * The launcher's exit code (in the absence of calls to + * System.exit) will be non-zero if main threw an exception. + */ + ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1; + + /* + * Detach the main thread so that it appears to have ended when + * the application's main method exits. This will invoke the + * uncaught exception handler machinery if main threw an + * exception. An uncaught exception handler cannot change the + * launcher's return code except by calling System.exit. + */ + if ((*vm)->DetachCurrentThread(vm) != 0) { + message = "Could not detach main thread."; + messageDest = JNI_TRUE; + ret = 1; + goto leave; + } + + message = NULL; + + leave: + /* + * Wait for all non-daemon threads to end, then destroy the VM. + * This will actually create a trivial new Java waiter thread + * named "DestroyJavaVM", but this will be seen as a different + * thread from the one that executed main, even though they are + * the same C thread. This allows mainThread.join() and + * mainThread.isAlive() to work as expected. + */ + (*vm)->DestroyJavaVM(vm); + + if(message != NULL && !noExitErrorMessage) + ReportErrorMessage(message, messageDest); + return ret; +} + + +#ifndef GAMMA +/* + * Checks the command line options to find which JVM type was + * specified. If no command line option was given for the JVM type, + * the default type is used. The environment variable + * JDK_ALTERNATE_VM and the command line option -XXaltjvm= are also + * checked as ways of specifying which JVM type to invoke. + */ +char * +CheckJvmType(int *pargc, char ***argv, jboolean speculative) { + int i, argi; + int argc; + char **newArgv; + int newArgvIdx = 0; + int isVMType; + int jvmidx = -1; + char *jvmtype = getenv("JDK_ALTERNATE_VM"); + + argc = *pargc; + + /* To make things simpler we always copy the argv array */ + newArgv = MemAlloc((argc + 1) * sizeof(char *)); + + /* The program name is always present */ + newArgv[newArgvIdx++] = (*argv)[0]; + + for (argi = 1; argi < argc; argi++) { + char *arg = (*argv)[argi]; + isVMType = 0; + +#ifdef JAVA_ARGS + if (arg[0] != '-') { + newArgv[newArgvIdx++] = arg; + continue; + } +#else + if (strcmp(arg, "-classpath") == 0 || + strcmp(arg, "-cp") == 0) { + newArgv[newArgvIdx++] = arg; + argi++; + if (argi < argc) { + newArgv[newArgvIdx++] = (*argv)[argi]; + } + continue; + } + if (arg[0] != '-') break; +#endif + + /* Did the user pass an explicit VM type? */ + i = KnownVMIndex(arg); + if (i >= 0) { + jvmtype = knownVMs[jvmidx = i].name + 1; /* skip the - */ + isVMType = 1; + *pargc = *pargc - 1; + } + + /* Did the user specify an "alternate" VM? */ + else if (strncmp(arg, "-XXaltjvm=", 10) == 0 || strncmp(arg, "-J-XXaltjvm=", 12) == 0) { + isVMType = 1; + jvmtype = arg+((arg[1]=='X')? 10 : 12); + jvmidx = -1; + } + + if (!isVMType) { + newArgv[newArgvIdx++] = arg; + } + } + + /* + * Finish copying the arguments if we aborted the above loop. + * NOTE that if we aborted via "break" then we did NOT copy the + * last argument above, and in addition argi will be less than + * argc. + */ + while (argi < argc) { + newArgv[newArgvIdx++] = (*argv)[argi]; + argi++; + } + + /* argv is null-terminated */ + newArgv[newArgvIdx] = 0; + + /* Copy back argv */ + *argv = newArgv; + *pargc = newArgvIdx; + + /* use the default VM type if not specified (no alias processing) */ + if (jvmtype == NULL) { + char* result = knownVMs[0].name+1; + /* Use a different VM type if we are on a server class machine? */ + if ((knownVMs[0].flag == VM_IF_SERVER_CLASS) && + (ServerClassMachine() == JNI_TRUE)) { + result = knownVMs[0].server_class+1; + } + if (_launcher_debug) { + printf("Default VM: %s\n", result); + } + return result; + } + + /* if using an alternate VM, no alias processing */ + if (jvmidx < 0) + return jvmtype; + + /* Resolve aliases first */ + { + int loopCount = 0; + while (knownVMs[jvmidx].flag == VM_ALIASED_TO) { + int nextIdx = KnownVMIndex(knownVMs[jvmidx].alias); + + if (loopCount > knownVMsCount) { + if (!speculative) { + ReportErrorMessage("Error: Corrupt jvm.cfg file; cycle in alias list.", + JNI_TRUE); + exit(1); + } else { + return "ERROR"; + /* break; */ + } + } + + if (nextIdx < 0) { + if (!speculative) { + ReportErrorMessage2("Error: Unable to resolve VM alias %s", + knownVMs[jvmidx].alias, JNI_TRUE); + exit(1); + } else { + return "ERROR"; + } + } + jvmidx = nextIdx; + jvmtype = knownVMs[jvmidx].name+1; + loopCount++; + } + } + + switch (knownVMs[jvmidx].flag) { + case VM_WARN: + if (!speculative) { + fprintf(stderr, "Warning: %s VM not supported; %s VM will be used\n", + jvmtype, knownVMs[0].name + 1); + } + /* fall through */ + case VM_IGNORE: + jvmtype = knownVMs[jvmidx=0].name + 1; + /* fall through */ + case VM_KNOWN: + break; + case VM_ERROR: + if (!speculative) { + ReportErrorMessage2("Error: %s VM not supported", jvmtype, JNI_TRUE); + exit(1); + } else { + return "ERROR"; + } + } + + return jvmtype; +} +#endif /* ifndef GAMMA */ + +/* + * Adds a new VM option with the given given name and value. + */ +static void +AddOption(char *str, void *info) +{ + /* + * Expand options array if needed to accommodate at least one more + * VM option. + */ + if (numOptions >= maxOptions) { + if (options == 0) { + maxOptions = 4; + options = MemAlloc(maxOptions * sizeof(JavaVMOption)); + } else { + JavaVMOption *tmp; + maxOptions *= 2; + tmp = MemAlloc(maxOptions * sizeof(JavaVMOption)); + memcpy(tmp, options, numOptions * sizeof(JavaVMOption)); + free(options); + options = tmp; + } + } + options[numOptions].optionString = str; + options[numOptions++].extraInfo = info; +} + +static void +SetClassPath(char *s) +{ + char *def = MemAlloc(strlen(s) + 40); + sprintf(def, "-Djava.class.path=%s", s); + AddOption(def, NULL); +} + +#ifndef GAMMA +/* + * The SelectVersion() routine ensures that an appropriate version of + * the JRE is running. The specification for the appropriate version + * is obtained from either the manifest of a jar file (preferred) or + * from command line options. + */ +static void +SelectVersion(int argc, char **argv, char **main_class) +{ + char *arg; + char **new_argv; + char **new_argp; + char *operand; + char *version = NULL; + char *jre = NULL; + int jarflag = 0; + int restrict_search = -1; /* -1 implies not known */ + manifest_info info; + char env_entry[MAXNAMELEN + 24] = ENV_ENTRY "="; + char *env_in; + int res; + + /* + * If the version has already been selected, set *main_class + * with the value passed through the environment (if any) and + * simply return. + */ + if ((env_in = getenv(ENV_ENTRY)) != NULL) { + if (*env_in != '\0') + *main_class = strdup(env_in); + return; + } + + /* + * Scan through the arguments for options relevant to multiple JRE + * support. For reference, the command line syntax is defined as: + * + * SYNOPSIS + * java [options] class [argument...] + * + * java [options] -jar file.jar [argument...] + * + * As the scan is performed, make a copy of the argument list with + * the version specification options (new to 1.5) removed, so that + * a version less than 1.5 can be exec'd. + */ + new_argv = MemAlloc((argc + 1) * sizeof(char*)); + new_argv[0] = argv[0]; + new_argp = &new_argv[1]; + argc--; + argv++; + while ((arg = *argv) != 0 && *arg == '-') { + if (strncmp(arg, "-version:", 9) == 0) { + version = arg + 9; + } else if (strcmp(arg, "-jre-restrict-search") == 0) { + restrict_search = 1; + } else if (strcmp(arg, "-no-jre-restrict-search") == 0) { + restrict_search = 0; + } else { + if (strcmp(arg, "-jar") == 0) + jarflag = 1; + /* deal with "unfortunate" classpath syntax */ + if ((strcmp(arg, "-classpath") == 0 || strcmp(arg, "-cp") == 0) && + (argc >= 2)) { + *new_argp++ = arg; + argc--; + argv++; + arg = *argv; + } + *new_argp++ = arg; + } + argc--; + argv++; + } + if (argc <= 0) { /* No operand? Possibly legit with -[full]version */ + operand = NULL; + } else { + argc--; + *new_argp++ = operand = *argv++; + } + while (argc-- > 0) /* Copy over [argument...] */ + *new_argp++ = *argv++; + *new_argp = NULL; + + /* + * If there is a jar file, read the manifest. If the jarfile can't be + * read, the manifest can't be read from the jar file, or the manifest + * is corrupt, issue the appropriate error messages and exit. + * + * Even if there isn't a jar file, construct a manifest_info structure + * containing the command line information. It's a convenient way to carry + * this data around. + */ + if (jarflag && operand) { + if ((res = parse_manifest(operand, &info)) != 0) { + if (res == -1) + ReportErrorMessage2("Unable to access jarfile %s", + operand, JNI_TRUE); + else + ReportErrorMessage2("Invalid or corrupt jarfile %s", + operand, JNI_TRUE); + exit(1); + } + } else { + info.manifest_version = NULL; + info.main_class = NULL; + info.jre_version = NULL; + info.jre_restrict_search = 0; + } + + /* + * The JRE-Version and JRE-Restrict-Search values (if any) from the + * manifest are overwritten by any specified on the command line. + */ + if (version != NULL) + info.jre_version = version; + if (restrict_search != -1) + info.jre_restrict_search = restrict_search; + + /* + * "Valid" returns (other than unrecoverable errors) follow. Set + * main_class as a side-effect of this routine. + */ + if (info.main_class != NULL) + *main_class = strdup(info.main_class); + + /* + * If no version selection information is found either on the command + * line or in the manifest, simply return. + */ + if (info.jre_version == NULL) { + free_manifest(); + free(new_argv); + return; + } + + /* + * Check for correct syntax of the version specification (JSR 56). + */ + if (!valid_version_string(info.jre_version)) { + ReportErrorMessage2("Syntax error in version specification \"%s\"", + info.jre_version, JNI_TRUE); + exit(1); + } + + /* + * Find the appropriate JVM on the system. Just to be as forgiving as + * possible, if the standard algorithms don't locate an appropriate + * jre, check to see if the one running will satisfy the requirements. + * This can happen on systems which haven't been set-up for multiple + * JRE support. + */ + jre = LocateJRE(&info); + if (_launcher_debug) + printf("JRE-Version = %s, JRE-Restrict-Search = %s Selected = %s\n", + (info.jre_version?info.jre_version:"null"), + (info.jre_restrict_search?"true":"false"), (jre?jre:"null")); + if (jre == NULL) { + if (acceptable_release(FULL_VERSION, info.jre_version)) { + free_manifest(); + free(new_argv); + return; + } else { + ReportErrorMessage2( + "Unable to locate JRE meeting specification \"%s\"", + info.jre_version, JNI_TRUE); + exit(1); + } + } + + /* + * If I'm not the chosen one, exec the chosen one. Returning from + * ExecJRE indicates that I am indeed the chosen one. + * + * The private environment variable _JAVA_VERSION_SET is used to + * prevent the chosen one from re-reading the manifest file and + * using the values found within to override the (potential) command + * line flags stripped from argv (because the target may not + * understand them). Passing the MainClass value is an optimization + * to avoid locating, expanding and parsing the manifest extra + * times. + */ + if (info.main_class != NULL) + (void)strcat(env_entry, info.main_class); + (void)putenv(env_entry); + ExecJRE(jre, new_argv); + free_manifest(); + free(new_argv); + return; +} +#endif /* ifndef GAMMA */ + +/* + * Parses command line arguments. Returns JNI_FALSE if launcher + * should exit without starting vm (e.g. certain version and usage + * options); returns JNI_TRUE if vm needs to be started to process + * given options. *pret (the launcher process return value) is set to + * 0 for a normal exit. + */ +static jboolean +ParseArguments(int *pargc, char ***pargv, char **pjarfile, + char **pclassname, int *pret) +{ + int argc = *pargc; + char **argv = *pargv; + jboolean jarflag = JNI_FALSE; + char *arg; + + *pret = 1; + while ((arg = *argv) != 0 && *arg == '-') { + argv++; --argc; + if (strcmp(arg, "-classpath") == 0 || strcmp(arg, "-cp") == 0) { + if (argc < 1) { + ReportErrorMessage2("%s requires class path specification", + arg, JNI_TRUE); + PrintUsage(); + return JNI_FALSE; + } + SetClassPath(*argv); + argv++; --argc; + } else if (strcmp(arg, "-jar") == 0) { + jarflag = JNI_TRUE; + } else if (strcmp(arg, "-help") == 0 || + strcmp(arg, "-h") == 0 || + strcmp(arg, "-?") == 0) { + PrintUsage(); + *pret = 0; + return JNI_FALSE; + } else if (strcmp(arg, "-version") == 0) { + printVersion = JNI_TRUE; + return JNI_TRUE; + } else if (strcmp(arg, "-showversion") == 0) { + showVersion = JNI_TRUE; + } else if (strcmp(arg, "-X") == 0) { + *pret = PrintXUsage(); + return JNI_FALSE; +/* + * The following case provide backward compatibility with old-style + * command line options. + */ + } else if (strcmp(arg, "-fullversion") == 0) { + fprintf(stderr, "%s full version \"%s\"\n", progname, + FULL_VERSION); + *pret = 0; + return JNI_FALSE; + } else if (strcmp(arg, "-verbosegc") == 0) { + AddOption("-verbose:gc", NULL); + } else if (strcmp(arg, "-t") == 0) { + AddOption("-Xt", NULL); + } else if (strcmp(arg, "-tm") == 0) { + AddOption("-Xtm", NULL); + } else if (strcmp(arg, "-debug") == 0) { + AddOption("-Xdebug", NULL); + } else if (strcmp(arg, "-noclassgc") == 0) { + AddOption("-Xnoclassgc", NULL); + } else if (strcmp(arg, "-Xfuture") == 0) { + AddOption("-Xverify:all", NULL); + } else if (strcmp(arg, "-verify") == 0) { + AddOption("-Xverify:all", NULL); + } else if (strcmp(arg, "-verifyremote") == 0) { + AddOption("-Xverify:remote", NULL); + } else if (strcmp(arg, "-noverify") == 0) { + AddOption("-Xverify:none", NULL); + } else if (strcmp(arg, "-XXsuppressExitMessage") == 0) { + noExitErrorMessage = 1; + } else if (strncmp(arg, "-prof", 5) == 0) { + char *p = arg + 5; + char *tmp = MemAlloc(strlen(arg) + 50); + if (*p) { + sprintf(tmp, "-Xrunhprof:cpu=old,file=%s", p + 1); + } else { + sprintf(tmp, "-Xrunhprof:cpu=old,file=java.prof"); + } + AddOption(tmp, NULL); + } else if (strncmp(arg, "-ss", 3) == 0 || + strncmp(arg, "-oss", 4) == 0 || + strncmp(arg, "-ms", 3) == 0 || + strncmp(arg, "-mx", 3) == 0) { + char *tmp = MemAlloc(strlen(arg) + 6); + sprintf(tmp, "-X%s", arg + 1); /* skip '-' */ + AddOption(tmp, NULL); + } else if (strcmp(arg, "-checksource") == 0 || + strcmp(arg, "-cs") == 0 || + strcmp(arg, "-noasyncgc") == 0) { + /* No longer supported */ + fprintf(stderr, + "Warning: %s option is no longer supported.\n", + arg); + } else if (strncmp(arg, "-version:", 9) == 0 || + strcmp(arg, "-no-jre-restrict-search") == 0 || + strcmp(arg, "-jre-restrict-search") == 0) { + ; /* Ignore machine independent options already handled */ + } else if (RemovableMachineDependentOption(arg) ) { + ; /* Do not pass option to vm. */ + } + else { + AddOption(arg, NULL); + } + } + + if (--argc >= 0) { + if (jarflag) { + *pjarfile = *argv++; + *pclassname = 0; + } else { + *pjarfile = 0; + *pclassname = *argv++; + } + *pargc = argc; + *pargv = argv; + } + + return JNI_TRUE; +} + +/* + * Initializes the Java Virtual Machine. Also frees options array when + * finished. + */ +static jboolean +InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn) +{ + JavaVMInitArgs args; + jint r; + + memset(&args, 0, sizeof(args)); + args.version = JNI_VERSION_1_2; + args.nOptions = numOptions; + args.options = options; + args.ignoreUnrecognized = JNI_FALSE; + + if (_launcher_debug) { + int i = 0; + printf("JavaVM args:\n "); + printf("version 0x%08lx, ", (long)args.version); + printf("ignoreUnrecognized is %s, ", + args.ignoreUnrecognized ? "JNI_TRUE" : "JNI_FALSE"); + printf("nOptions is %ld\n", (long)args.nOptions); + for (i = 0; i < numOptions; i++) + printf(" option[%2d] = '%s'\n", + i, args.options[i].optionString); + } + + r = ifn->CreateJavaVM(pvm, (void **)penv, &args); + free(options); + return r == JNI_OK; +} + + +#define NULL_CHECK0(e) if ((e) == 0) return 0 +#define NULL_CHECK(e) if ((e) == 0) return + +/* + * Returns a pointer to a block of at least 'size' bytes of memory. + * Prints error message and exits if the memory could not be allocated. + */ +void * +MemAlloc(size_t size) +{ + void *p = malloc(size); + if (p == 0) { + perror("malloc"); + exit(1); + } + return p; +} + +static jstring platformEncoding = NULL; +static jstring getPlatformEncoding(JNIEnv *env) { + if (platformEncoding == NULL) { + jstring propname = (*env)->NewStringUTF(env, "sun.jnu.encoding"); + if (propname) { + jclass cls; + jmethodID mid; + NULL_CHECK0 (cls = (*env)->FindClass(env, "java/lang/System")); + NULL_CHECK0 (mid = (*env)->GetStaticMethodID( + env, cls, + "getProperty", + "(Ljava/lang/String;)Ljava/lang/String;")); + platformEncoding = (*env)->CallStaticObjectMethod ( + env, cls, mid, propname); + } + } + return platformEncoding; +} + +static jboolean isEncodingSupported(JNIEnv *env, jstring enc) { + jclass cls; + jmethodID mid; + NULL_CHECK0 (cls = (*env)->FindClass(env, "java/nio/charset/Charset")); + NULL_CHECK0 (mid = (*env)->GetStaticMethodID( + env, cls, + "isSupported", + "(Ljava/lang/String;)Z")); + return (*env)->CallStaticBooleanMethod (env, cls, mid, enc); +} + +/* + * Returns a new Java string object for the specified platform string. + */ +static jstring +NewPlatformString(JNIEnv *env, char *s) +{ + int len = (int)strlen(s); + jclass cls; + jmethodID mid; + jbyteArray ary; + jstring enc; + + if (s == NULL) + return 0; + enc = getPlatformEncoding(env); + + ary = (*env)->NewByteArray(env, len); + if (ary != 0) { + jstring str = 0; + (*env)->SetByteArrayRegion(env, ary, 0, len, (jbyte *)s); + if (!(*env)->ExceptionOccurred(env)) { +#ifdef GAMMA + /* We support running JVM with older JDK, so here we have to deal */ + /* with the case that sun.jnu.encoding is undefined (enc == NULL) */ + if (enc != NULL && isEncodingSupported(env, enc) == JNI_TRUE) { +#else + if (isEncodingSupported(env, enc) == JNI_TRUE) { +#endif + NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String")); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "", + "([BLjava/lang/String;)V")); + str = (*env)->NewObject(env, cls, mid, ary, enc); + } else { + /*If the encoding specified in sun.jnu.encoding is not + endorsed by "Charset.isSupported" we have to fall back + to use String(byte[]) explicitly here without specifying + the encoding name, in which the StringCoding class will + pickup the iso-8859-1 as the fallback converter for us. + */ + NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String")); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "", + "([B)V")); + str = (*env)->NewObject(env, cls, mid, ary); + } + (*env)->DeleteLocalRef(env, ary); + return str; + } + } + return 0; +} + +/* + * Returns a new array of Java string objects for the specified + * array of platform strings. + */ +static jobjectArray +NewPlatformStringArray(JNIEnv *env, char **strv, int strc) +{ + jarray cls; + jarray ary; + int i; + + NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String")); + NULL_CHECK0(ary = (*env)->NewObjectArray(env, strc, cls, 0)); + for (i = 0; i < strc; i++) { + jstring str = NewPlatformString(env, *strv++); + NULL_CHECK0(str); + (*env)->SetObjectArrayElement(env, ary, i, str); + (*env)->DeleteLocalRef(env, str); + } + return ary; +} + +/* + * Loads a class, convert the '.' to '/'. + */ +static jclass +LoadClass(JNIEnv *env, char *name) +{ + char *buf = MemAlloc(strlen(name) + 1); + char *s = buf, *t = name, c; + jclass cls; + jlong start, end; + + if (_launcher_debug) + start = CounterGet(); + + do { + c = *t++; + *s++ = (c == '.') ? '/' : c; + } while (c != '\0'); + cls = (*env)->FindClass(env, buf); + free(buf); + + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to load main class\n", + (long)(jint)Counter2Micros(end-start)); + printf("----_JAVA_LAUNCHER_DEBUG----\n"); + } + + return cls; +} + + +/* + * Returns the main class name for the specified jar file. + */ +static jstring +GetMainClassName(JNIEnv *env, char *jarname) +{ +#define MAIN_CLASS "Main-Class" + jclass cls; + jmethodID mid; + jobject jar, man, attr; + jstring str, result = 0; + + NULL_CHECK0(cls = (*env)->FindClass(env, "java/util/jar/JarFile")); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "", + "(Ljava/lang/String;)V")); + NULL_CHECK0(str = NewPlatformString(env, jarname)); + NULL_CHECK0(jar = (*env)->NewObject(env, cls, mid, str)); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "getManifest", + "()Ljava/util/jar/Manifest;")); + man = (*env)->CallObjectMethod(env, jar, mid); + if (man != 0) { + NULL_CHECK0(mid = (*env)->GetMethodID(env, + (*env)->GetObjectClass(env, man), + "getMainAttributes", + "()Ljava/util/jar/Attributes;")); + attr = (*env)->CallObjectMethod(env, man, mid); + if (attr != 0) { + NULL_CHECK0(mid = (*env)->GetMethodID(env, + (*env)->GetObjectClass(env, attr), + "getValue", + "(Ljava/lang/String;)Ljava/lang/String;")); + NULL_CHECK0(str = NewPlatformString(env, MAIN_CLASS)); + result = (*env)->CallObjectMethod(env, attr, mid, str); + } + } + return result; +} + +#ifdef JAVA_ARGS +static char *java_args[] = JAVA_ARGS; +static char *app_classpath[] = APP_CLASSPATH; + +/* + * For tools convert 'javac -J-ms32m' to 'java -ms32m ...' + */ +static void +TranslateDashJArgs(int *pargc, char ***pargv) +{ + const int NUM_ARGS = (sizeof(java_args) / sizeof(char *)); + int argc = *pargc; + char **argv = *pargv; + int nargc = argc + NUM_ARGS; + char **nargv = MemAlloc((nargc + 1) * sizeof(char *)); + int i; + + *pargc = nargc; + *pargv = nargv; + + /* Copy the VM arguments (i.e. prefixed with -J) */ + for (i = 0; i < NUM_ARGS; i++) { + char *arg = java_args[i]; + if (arg[0] == '-' && arg[1] == 'J') { + *nargv++ = arg + 2; + } + } + + for (i = 0; i < argc; i++) { + char *arg = argv[i]; + if (arg[0] == '-' && arg[1] == 'J') { + if (arg[2] == '\0') { + ReportErrorMessage("Error: the -J option should not be " + "followed by a space.", JNI_TRUE); + exit(1); + } + *nargv++ = arg + 2; + } + } + + /* Copy the rest of the arguments */ + for (i = 0; i < NUM_ARGS; i++) { + char *arg = java_args[i]; + if (arg[0] != '-' || arg[1] != 'J') { + *nargv++ = arg; + } + } + for (i = 0; i < argc; i++) { + char *arg = argv[i]; + if (arg[0] != '-' || arg[1] != 'J') { + *nargv++ = arg; + } + } + *nargv = 0; +} + +/* + * For our tools, we try to add 3 VM options: + * -Denv.class.path= + * -Dapplication.home= + * -Djava.class.path= + * is the user's setting of CLASSPATH -- for instance the user + * tells javac where to find binary classes through this environment + * variable. Notice that users will be able to compile against our + * tools classes (sun.tools.javac.Main) only if they explicitly add + * tools.jar to CLASSPATH. + * is the directory where the application is installed. + * is the classpath to where our apps' classfiles are. + */ +static jboolean +AddApplicationOptions() +{ + const int NUM_APP_CLASSPATH = (sizeof(app_classpath) / sizeof(char *)); + char *s, *envcp, *appcp, *apphome; + char home[MAXPATHLEN]; /* application home */ + char separator[] = { PATH_SEPARATOR, '\0' }; + int size, i; + int strlenHome; + + s = getenv("CLASSPATH"); + if (s) { + /* 40 for -Denv.class.path= */ + envcp = (char *)MemAlloc(strlen(s) + 40); + sprintf(envcp, "-Denv.class.path=%s", s); + AddOption(envcp, NULL); + } + + if (!GetApplicationHome(home, sizeof(home))) { + ReportErrorMessage("Can't determine application home", JNI_TRUE); + return JNI_FALSE; + } + + /* 40 for '-Dapplication.home=' */ + apphome = (char *)MemAlloc(strlen(home) + 40); + sprintf(apphome, "-Dapplication.home=%s", home); + AddOption(apphome, NULL); + + /* How big is the application's classpath? */ + size = 40; /* 40: "-Djava.class.path=" */ + strlenHome = (int)strlen(home); + for (i = 0; i < NUM_APP_CLASSPATH; i++) { + size += strlenHome + (int)strlen(app_classpath[i]) + 1; /* 1: separator */ + } + appcp = (char *)MemAlloc(size + 1); + strcpy(appcp, "-Djava.class.path="); + for (i = 0; i < NUM_APP_CLASSPATH; i++) { + strcat(appcp, home); /* c:\program files\myapp */ + strcat(appcp, app_classpath[i]); /* \lib\myapp.jar */ + strcat(appcp, separator); /* ; */ + } + appcp[strlen(appcp)-1] = '\0'; /* remove trailing path separator */ + AddOption(appcp, NULL); + return JNI_TRUE; +} +#endif + +/* + * inject the -Dsun.java.command pseudo property into the args structure + * this pseudo property is used in the HotSpot VM to expose the + * Java class name and arguments to the main method to the VM. The + * HotSpot VM uses this pseudo property to store the Java class name + * (or jar file name) and the arguments to the class's main method + * to the instrumentation memory region. The sun.java.command pseudo + * property is not exported by HotSpot to the Java layer. + */ +void +SetJavaCommandLineProp(char *classname, char *jarfile, + int argc, char **argv) +{ + + int i = 0; + size_t len = 0; + char* javaCommand = NULL; + char* dashDstr = "-Dsun.java.command="; + + if (classname == NULL && jarfile == NULL) { + /* unexpected, one of these should be set. just return without + * setting the property + */ + return; + } + + /* if the class name is not set, then use the jarfile name */ + if (classname == NULL) { + classname = jarfile; + } + + /* determine the amount of memory to allocate assuming + * the individual components will be space separated + */ + len = strlen(classname); + for (i = 0; i < argc; i++) { + len += strlen(argv[i]) + 1; + } + + /* allocate the memory */ + javaCommand = (char*) MemAlloc(len + strlen(dashDstr) + 1); + + /* build the -D string */ + *javaCommand = '\0'; + strcat(javaCommand, dashDstr); + strcat(javaCommand, classname); + + for (i = 0; i < argc; i++) { + /* the components of the string are space separated. In + * the case of embedded white space, the relationship of + * the white space separated components to their true + * positional arguments will be ambiguous. This issue may + * be addressed in a future release. + */ + strcat(javaCommand, " "); + strcat(javaCommand, argv[i]); + } + + AddOption(javaCommand, NULL); +} + +/* + * JVM wants to know launcher type, so tell it. + */ +#ifdef GAMMA +void SetJavaLauncherProp() { + AddOption("-Dsun.java.launcher=" LAUNCHER_TYPE, NULL); +} +#endif + +/* + * Prints the version information from the java.version and other properties. + */ +static void +PrintJavaVersion(JNIEnv *env) +{ + jclass ver; + jmethodID print; + + NULL_CHECK(ver = (*env)->FindClass(env, "sun/misc/Version")); + NULL_CHECK(print = (*env)->GetStaticMethodID(env, ver, "print", "()V")); + + (*env)->CallStaticVoidMethod(env, ver, print); +} + +/* + * Prints default usage message. + */ +static void +PrintUsage(void) +{ + int i; + + fprintf(stdout, + "Usage: %s [-options] class [args...]\n" + " (to execute a class)\n" + " or %s [-options] -jar jarfile [args...]\n" + " (to execute a jar file)\n" + "\n" + "where options include:\n", + progname, + progname); + +#ifndef GAMMA + PrintMachineDependentOptions(); + + if ((knownVMs[0].flag == VM_KNOWN) || + (knownVMs[0].flag == VM_IF_SERVER_CLASS)) { + fprintf(stdout, " %s\t to select the \"%s\" VM\n", + knownVMs[0].name, knownVMs[0].name+1); + } + for (i=1; i\n" +" -classpath \n" +" A %c separated list of directories, JAR archives,\n" +" and ZIP archives to search for class files.\n" +" -D=\n" +" set a system property\n" +" -verbose[:class|gc|jni]\n" +" enable verbose output\n" +" -version print product version and exit\n" +" -version:\n" +" require the specified version to run\n" +" -showversion print product version and continue\n" +" -jre-restrict-search | -jre-no-restrict-search\n" +" include/exclude user private JREs in the version search\n" +" -? -help print this help message\n" +" -X print help on non-standard options\n" +" -ea[:...|:]\n" +" -enableassertions[:...|:]\n" +" enable assertions\n" +" -da[:...|:]\n" +" -disableassertions[:...|:]\n" +" disable assertions\n" +" -esa | -enablesystemassertions\n" +" enable system assertions\n" +" -dsa | -disablesystemassertions\n" +" disable system assertions\n" +" -agentlib:[=]\n" +" load native agent library , e.g. -agentlib:hprof\n" +" see also, -agentlib:jdwp=help and -agentlib:hprof=help\n" +" -agentpath:[=]\n" +" load native agent library by full pathname\n" +" -javaagent:[=]\n" +" load Java programming language agent, see java.lang.instrument\n" + + ,PATH_SEPARATOR); +} + +/* + * Print usage message for -X options. + */ +static jint +PrintXUsage(void) +{ + char path[MAXPATHLEN]; + char buf[128]; + size_t n; + FILE *fp; + + GetXUsagePath(path, sizeof(path)); + fp = fopen(path, "r"); + if (fp == 0) { + fprintf(stderr, "Can't open %s\n", path); + return 1; + } + while ((n = fread(buf, 1, sizeof(buf), fp)) != 0) { + fwrite(buf, 1, n, stdout); + } + fclose(fp); + return 0; +} + +#ifndef GAMMA + +/* + * Read the jvm.cfg file and fill the knownJVMs[] array. + * + * The functionality of the jvm.cfg file is subject to change without + * notice and the mechanism will be removed in the future. + * + * The lexical structure of the jvm.cfg file is as follows: + * + * jvmcfg := { vmLine } + * vmLine := knownLine + * | aliasLine + * | warnLine + * | ignoreLine + * | errorLine + * | predicateLine + * | commentLine + * knownLine := flag "KNOWN" EOL + * warnLine := flag "WARN" EOL + * ignoreLine := flag "IGNORE" EOL + * errorLine := flag "ERROR" EOL + * aliasLine := flag "ALIASED_TO" flag EOL + * predicateLine := flag "IF_SERVER_CLASS" flag EOL + * commentLine := "#" text EOL + * flag := "-" identifier + * + * The semantics are that when someone specifies a flag on the command line: + * - if the flag appears on a knownLine, then the identifier is used as + * the name of the directory holding the JVM library (the name of the JVM). + * - if the flag appears as the first flag on an aliasLine, the identifier + * of the second flag is used as the name of the JVM. + * - if the flag appears on a warnLine, the identifier is used as the + * name of the JVM, but a warning is generated. + * - if the flag appears on an ignoreLine, the identifier is recognized as the + * name of a JVM, but the identifier is ignored and the default vm used + * - if the flag appears on an errorLine, an error is generated. + * - if the flag appears as the first flag on a predicateLine, and + * the machine on which you are running passes the predicate indicated, + * then the identifier of the second flag is used as the name of the JVM, + * otherwise the identifier of the first flag is used as the name of the JVM. + * If no flag is given on the command line, the first vmLine of the jvm.cfg + * file determines the name of the JVM. + * PredicateLines are only interpreted on first vmLine of a jvm.cfg file, + * since they only make sense if someone hasn't specified the name of the + * JVM on the command line. + * + * The intent of the jvm.cfg file is to allow several JVM libraries to + * be installed in different subdirectories of a single JRE installation, + * for space-savings and convenience in testing. + * The intent is explicitly not to provide a full aliasing or predicate + * mechanism. + */ +jint +ReadKnownVMs(const char *jrepath, char * arch, jboolean speculative) +{ + FILE *jvmCfg; + char jvmCfgName[MAXPATHLEN+20]; + char line[MAXPATHLEN+20]; + int cnt = 0; + int lineno = 0; + jlong start, end; + int vmType; + char *tmpPtr; + char *altVMName; + char *serverClassVMName; + static char *whiteSpace = " \t"; + if (_launcher_debug) { + start = CounterGet(); + } + + strcpy(jvmCfgName, jrepath); + strcat(jvmCfgName, FILESEP "lib" FILESEP); + strcat(jvmCfgName, arch); + strcat(jvmCfgName, FILESEP "jvm.cfg"); + + jvmCfg = fopen(jvmCfgName, "r"); + if (jvmCfg == NULL) { + if (!speculative) { + ReportErrorMessage2("Error: could not open `%s'", jvmCfgName, + JNI_TRUE); + exit(1); + } else { + return -1; + } + } + while (fgets(line, sizeof(line), jvmCfg) != NULL) { + vmType = VM_UNKNOWN; + lineno++; + if (line[0] == '#') + continue; + if (line[0] != '-') { + fprintf(stderr, "Warning: no leading - on line %d of `%s'\n", + lineno, jvmCfgName); + } + if (cnt >= knownVMsLimit) { + GrowKnownVMs(cnt); + } + line[strlen(line)-1] = '\0'; /* remove trailing newline */ + tmpPtr = line + strcspn(line, whiteSpace); + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing VM type on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + /* Null-terminate this string for strdup below */ + *tmpPtr++ = 0; + tmpPtr += strspn(tmpPtr, whiteSpace); + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing VM type on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + if (!strncmp(tmpPtr, "KNOWN", strlen("KNOWN"))) { + vmType = VM_KNOWN; + } else if (!strncmp(tmpPtr, "ALIASED_TO", strlen("ALIASED_TO"))) { + tmpPtr += strcspn(tmpPtr, whiteSpace); + if (*tmpPtr != 0) { + tmpPtr += strspn(tmpPtr, whiteSpace); + } + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing VM alias on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + /* Null terminate altVMName */ + altVMName = tmpPtr; + tmpPtr += strcspn(tmpPtr, whiteSpace); + *tmpPtr = 0; + vmType = VM_ALIASED_TO; + } + } else if (!strncmp(tmpPtr, "WARN", strlen("WARN"))) { + vmType = VM_WARN; + } else if (!strncmp(tmpPtr, "IGNORE", strlen("IGNORE"))) { + vmType = VM_IGNORE; + } else if (!strncmp(tmpPtr, "ERROR", strlen("ERROR"))) { + vmType = VM_ERROR; + } else if (!strncmp(tmpPtr, + "IF_SERVER_CLASS", + strlen("IF_SERVER_CLASS"))) { + tmpPtr += strcspn(tmpPtr, whiteSpace); + if (*tmpPtr != 0) { + tmpPtr += strspn(tmpPtr, whiteSpace); + } + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing server class VM on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + /* Null terminate server class VM name */ + serverClassVMName = tmpPtr; + tmpPtr += strcspn(tmpPtr, whiteSpace); + *tmpPtr = 0; + vmType = VM_IF_SERVER_CLASS; + } + } else { + fprintf(stderr, "Warning: unknown VM type on line %d of `%s'\n", + lineno, &jvmCfgName[0]); + vmType = VM_KNOWN; + } + } + } + + if (_launcher_debug) + printf("jvm.cfg[%d] = ->%s<-\n", cnt, line); + if (vmType != VM_UNKNOWN) { + knownVMs[cnt].name = strdup(line); + knownVMs[cnt].flag = vmType; + switch (vmType) { + default: + break; + case VM_ALIASED_TO: + knownVMs[cnt].alias = strdup(altVMName); + if (_launcher_debug) { + printf(" name: %s vmType: %s alias: %s\n", + knownVMs[cnt].name, "VM_ALIASED_TO", knownVMs[cnt].alias); + } + break; + case VM_IF_SERVER_CLASS: + knownVMs[cnt].server_class = strdup(serverClassVMName); + if (_launcher_debug) { + printf(" name: %s vmType: %s server_class: %s\n", + knownVMs[cnt].name, "VM_IF_SERVER_CLASS", knownVMs[cnt].server_class); + } + break; + } + cnt++; + } + } + fclose(jvmCfg); + knownVMsCount = cnt; + + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to parse jvm.cfg\n", + (long)(jint)Counter2Micros(end-start)); + } + + return cnt; +} + + +static void +GrowKnownVMs(int minimum) +{ + struct vmdesc* newKnownVMs; + int newMax; + + newMax = (knownVMsLimit == 0 ? INIT_MAX_KNOWN_VMS : (2 * knownVMsLimit)); + if (newMax <= minimum) { + newMax = minimum; + } + newKnownVMs = (struct vmdesc*) MemAlloc(newMax * sizeof(struct vmdesc)); + if (knownVMs != NULL) { + memcpy(newKnownVMs, knownVMs, knownVMsLimit * sizeof(struct vmdesc)); + } + free(knownVMs); + knownVMs = newKnownVMs; + knownVMsLimit = newMax; +} + + +/* Returns index of VM or -1 if not found */ +static int +KnownVMIndex(const char* name) +{ + int i; + if (strncmp(name, "-J", 2) == 0) name += 2; + for (i = 0; i < knownVMsCount; i++) { + if (!strcmp(name, knownVMs[i].name)) { + return i; + } + } + return -1; +} + +static void +FreeKnownVMs() +{ + int i; + for (i = 0; i < knownVMsCount; i++) { + free(knownVMs[i].name); + knownVMs[i].name = NULL; + } + free(knownVMs); +} + +#endif /* ifndef GAMMA */ diff --git a/hotspot/src/os/linux/launcher/java.h b/hotspot/src/os/linux/launcher/java.h new file mode 100644 index 00000000000..c6f43df9c00 --- /dev/null +++ b/hotspot/src/os/linux/launcher/java.h @@ -0,0 +1,104 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Gamma (Hotspot internal engineering test) launcher based on 1.6.0-b28 JDK, + * search "GAMMA" for gamma specific changes. + */ + +#ifndef _JAVA_H_ +#define _JAVA_H_ + +/* + * Get system specific defines. + */ +#include "jni.h" +#include "java_md.h" + +/* + * Pointers to the needed JNI invocation API, initialized by LoadJavaVM. + */ +typedef jint (JNICALL *CreateJavaVM_t)(JavaVM **pvm, void **env, void *args); +typedef jint (JNICALL *GetDefaultJavaVMInitArgs_t)(void *args); + +typedef struct { + CreateJavaVM_t CreateJavaVM; + GetDefaultJavaVMInitArgs_t GetDefaultJavaVMInitArgs; +} InvocationFunctions; + +/* + * Prototypes for launcher functions in the system specific java_md.c. + */ + +jboolean +LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn); + +void +GetXUsagePath(char *buf, jint bufsize); + +jboolean +GetApplicationHome(char *buf, jint bufsize); + +const char * +GetArch(); + +void CreateExecutionEnvironment(int *_argc, + char ***_argv, + char jrepath[], + jint so_jrepath, + char jvmpath[], + jint so_jvmpath, + char **original_argv); + +/* + * Report an error message to stderr or a window as appropriate. The + * flag always is set to JNI_TRUE if message is to be reported to both + * strerr and windows and set to JNI_FALSE if the message should only + * be sent to a window. + */ +void ReportErrorMessage(char * message, jboolean always); +void ReportErrorMessage2(char * format, char * string, jboolean always); + +/* + * Report an exception which terminates the vm to stderr or a window + * as appropriate. + */ +void ReportExceptionDescription(JNIEnv * env); + +jboolean RemovableMachineDependentOption(char * option); +void PrintMachineDependentOptions(); + +/* + * Functions defined in java.c and used in java_md.c. + */ +jint ReadKnownVMs(const char *jrepath, char * arch, jboolean speculative); +char *CheckJvmType(int *argc, char ***argv, jboolean speculative); +void* MemAlloc(size_t size); + +/* + * Make launcher spit debug output. + */ +extern jboolean _launcher_debug; + +#endif /* _JAVA_H_ */ diff --git a/hotspot/src/os/linux/launcher/java_md.c b/hotspot/src/os/linux/launcher/java_md.c new file mode 100644 index 00000000000..90c6e62c31b --- /dev/null +++ b/hotspot/src/os/linux/launcher/java_md.c @@ -0,0 +1,1828 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Gamma (Hotspot internal engineering test) launcher based on 1.6.0-b28 JDK, + * search "GAMMA" for gamma specific changes. + */ + +#include "java.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GAMMA +#include "manifest_info.h" +#include "version_comp.h" +#endif + +#define JVM_DLL "libjvm.so" +#define JAVA_DLL "libjava.so" + +#ifndef GAMMA /* launcher.make defines ARCH */ + +/* + * If a processor / os combination has the ability to run binaries of + * two data models and cohabitation of jre/jdk bits with both data + * models is supported, then DUAL_MODE is defined. When DUAL_MODE is + * defined, the architecture names for the narrow and wide version of + * the architecture are defined in BIG_ARCH and SMALL_ARCH. Currently + * only Solaris on sparc/sparcv9 and i586/amd64 is DUAL_MODE; linux + * i586/amd64 could be defined as DUAL_MODE but that is not the + * current policy. + */ + +#ifdef _LP64 + +# ifdef ia64 +# define ARCH "ia64" +# elif defined(amd64) +# define ARCH "amd64" +# elif defined(__sparc) +# define ARCH "sparcv9" +# else +# define ARCH "unknown" /* unknown 64-bit architecture */ +# endif + +#else /* 32-bit data model */ + +# ifdef i586 +# define ARCH "i386" +# elif defined(__sparc) +# define ARCH "sparc" +# endif + +#endif /* _LP64 */ + +#ifdef __sun +# define DUAL_MODE +# ifdef __sparc +# define BIG_ARCH "sparcv9" +# define SMALL_ARCH "sparc" +# else +# define BIG_ARCH "amd64" +# define SMALL_ARCH "i386" +# endif +# include +# include +# include +#else +# ifndef ARCH +# include +# endif +#endif + +#endif /* ifndef GAMMA */ + +/* pointer to environment */ +extern char **environ; + +#ifndef GAMMA + +/* + * A collection of useful strings. One should think of these as #define + * entries, but actual strings can be more efficient (with many compilers). + */ +#ifdef __linux__ +static const char *system_dir = "/usr/java"; +static const char *user_dir = "/java"; +#else /* Solaris */ +static const char *system_dir = "/usr/jdk"; +static const char *user_dir = "/jdk"; +#endif + +#endif /* ifndef GAMMA */ + +/* + * Flowchart of launcher execs and options processing on unix + * + * The selection of the proper vm shared library to open depends on + * several classes of command line options, including vm "flavor" + * options (-client, -server) and the data model options, -d32 and + * -d64, as well as a version specification which may have come from + * the command line or from the manifest of an executable jar file. + * The vm selection options are not passed to the running + * virtual machine; they must be screened out by the launcher. + * + * The version specification (if any) is processed first by the + * platform independent routine SelectVersion. This may result in + * the exec of the specified launcher version. + * + * Typically, the launcher execs at least once to ensure a suitable + * LD_LIBRARY_PATH is in effect for the process. The first exec + * screens out all the data model options; leaving the choice of data + * model implicit in the binary selected to run. However, in case no + * exec is done, the data model options are screened out before the vm + * is invoked. + * + * incoming argv ------------------------------ + * | | + * \|/ | + * CheckJVMType | + * (removes -client, -server, etc.) | + * \|/ + * CreateExecutionEnvironment + * (removes -d32 and -d64, + * determines desired data model, + * sets up LD_LIBRARY_PATH, + * and exec's) + * | + * -------------------------------------------- + * | + * \|/ + * exec child 1 incoming argv ----------------- + * | | + * \|/ | + * CheckJVMType | + * (removes -client, -server, etc.) | + * | \|/ + * | CreateExecutionEnvironment + * | (verifies desired data model + * | is running and acceptable + * | LD_LIBRARY_PATH; + * | no-op in child) + * | + * \|/ + * TranslateDashJArgs... + * (Prepare to pass args to vm) + * | + * | + * | + * \|/ + * ParseArguments + * (ignores -d32 and -d64, + * processes version options, + * creates argument list for vm, + * etc.) + * + */ + +static char *SetExecname(char **argv); +static char * GetExecname(); +static jboolean GetJVMPath(const char *jrepath, const char *jvmtype, + char *jvmpath, jint jvmpathsize, char * arch); +static jboolean GetJREPath(char *path, jint pathsize, char * arch, jboolean speculative); + +const char * +GetArch() +{ + static char *arch = NULL; + static char buf[12]; + if (arch) { + return arch; + } + +#ifdef ARCH + strcpy(buf, ARCH); +#else + sysinfo(SI_ARCHITECTURE, buf, sizeof(buf)); +#endif + arch = buf; + return arch; +} + +void +CreateExecutionEnvironment(int *_argcp, + char ***_argvp, + char jrepath[], + jint so_jrepath, + char jvmpath[], + jint so_jvmpath, + char **original_argv) { + /* + * First, determine if we are running the desired data model. If we + * are running the desired data model, all the error messages + * associated with calling GetJREPath, ReadKnownVMs, etc. should be + * output. However, if we are not running the desired data model, + * some of the errors should be suppressed since it is more + * informative to issue an error message based on whether or not the + * os/processor combination has dual mode capabilities. + */ + + char *execname = NULL; + int original_argc = *_argcp; + jboolean jvmpathExists; + + /* Compute the name of the executable */ + execname = SetExecname(*_argvp); + +#ifndef GAMMA + /* Set the LD_LIBRARY_PATH environment variable, check data model + flags, and exec process, if needed */ + { + char *arch = (char *)GetArch(); /* like sparc or sparcv9 */ + char * jvmtype = NULL; + int argc = *_argcp; + char **argv = original_argv; + + char *runpath = NULL; /* existing effective LD_LIBRARY_PATH + setting */ + + int running = /* What data model is being ILP32 => + 32 bit vm; LP64 => 64 bit vm */ +#ifdef _LP64 + 64; +#else + 32; +#endif + + int wanted = running; /* What data mode is being + asked for? Current model is + fine unless another model + is asked for */ + + char* new_runpath = NULL; /* desired new LD_LIBRARY_PATH string */ + char* newpath = NULL; /* path on new LD_LIBRARY_PATH */ + char* lastslash = NULL; + + char** newenvp = NULL; /* current environment */ + + char** newargv = NULL; + int newargc = 0; +#ifdef __sun + char* dmpath = NULL; /* data model specific LD_LIBRARY_PATH, + Solaris only */ +#endif + + /* + * Starting in 1.5, all unix platforms accept the -d32 and -d64 + * options. On platforms where only one data-model is supported + * (e.g. ia-64 Linux), using the flag for the other data model is + * an error and will terminate the program. + */ + + { /* open new scope to declare local variables */ + int i; + + newargv = (char **)MemAlloc((argc+1) * sizeof(*newargv)); + newargv[newargc++] = argv[0]; + + /* scan for data model arguments and remove from argument list; + last occurrence determines desired data model */ + for (i=1; i < argc; i++) { + + if (strcmp(argv[i], "-J-d64") == 0 || strcmp(argv[i], "-d64") == 0) { + wanted = 64; + continue; + } + if (strcmp(argv[i], "-J-d32") == 0 || strcmp(argv[i], "-d32") == 0) { + wanted = 32; + continue; + } + newargv[newargc++] = argv[i]; + +#ifdef JAVA_ARGS + if (argv[i][0] != '-') + continue; +#else + if (strcmp(argv[i], "-classpath") == 0 || strcmp(argv[i], "-cp") == 0) { + i++; + if (i >= argc) break; + newargv[newargc++] = argv[i]; + continue; + } + if (argv[i][0] != '-') { i++; break; } +#endif + } + + /* copy rest of args [i .. argc) */ + while (i < argc) { + newargv[newargc++] = argv[i++]; + } + newargv[newargc] = NULL; + + /* + * newargv has all proper arguments here + */ + + argc = newargc; + argv = newargv; + } + + /* If the data model is not changing, it is an error if the + jvmpath does not exist */ + if (wanted == running) { + /* Find out where the JRE is that we will be using. */ + if (!GetJREPath(jrepath, so_jrepath, arch, JNI_FALSE) ) { + fprintf(stderr, "Error: could not find Java 2 Runtime Environment.\n"); + exit(2); + } + + /* Find the specified JVM type */ + if (ReadKnownVMs(jrepath, arch, JNI_FALSE) < 1) { + fprintf(stderr, "Error: no known VMs. (check for corrupt jvm.cfg file)\n"); + exit(1); + } + + jvmpath[0] = '\0'; + jvmtype = CheckJvmType(_argcp, _argvp, JNI_FALSE); + + if (!GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath, arch )) { + fprintf(stderr, "Error: no `%s' JVM at `%s'.\n", jvmtype, jvmpath); + exit(4); + } + } else { /* do the same speculatively or exit */ +#ifdef DUAL_MODE + if (running != wanted) { + /* Find out where the JRE is that we will be using. */ + if (!GetJREPath(jrepath, so_jrepath, ((wanted==64)?BIG_ARCH:SMALL_ARCH), JNI_TRUE)) { + goto EndDataModelSpeculate; + } + + /* + * Read in jvm.cfg for target data model and process vm + * selection options. + */ + if (ReadKnownVMs(jrepath, ((wanted==64)?BIG_ARCH:SMALL_ARCH), JNI_TRUE) < 1) { + goto EndDataModelSpeculate; + } + jvmpath[0] = '\0'; + jvmtype = CheckJvmType(_argcp, _argvp, JNI_TRUE); + /* exec child can do error checking on the existence of the path */ + jvmpathExists = GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath, + ((wanted==64)?BIG_ARCH:SMALL_ARCH)); + + } + EndDataModelSpeculate: /* give up and let other code report error message */ + ; +#else + fprintf(stderr, "Running a %d-bit JVM is not supported on this platform.\n", wanted); + exit(1); +#endif + } + + /* + * We will set the LD_LIBRARY_PATH as follows: + * + * o $JVMPATH (directory portion only) + * o $JRE/lib/$ARCH + * o $JRE/../lib/$ARCH + * + * followed by the user's previous effective LD_LIBRARY_PATH, if + * any. + */ + +#ifdef __sun + /* + * Starting in Solaris 7, ld.so.1 supports three LD_LIBRARY_PATH + * variables: + * + * 1. LD_LIBRARY_PATH -- used for 32 and 64 bit searches if + * data-model specific variables are not set. + * + * 2. LD_LIBRARY_PATH_64 -- overrides and replaces LD_LIBRARY_PATH + * for 64-bit binaries. + * + * 3. LD_LIBRARY_PATH_32 -- overrides and replaces LD_LIBRARY_PATH + * for 32-bit binaries. + * + * The vm uses LD_LIBRARY_PATH to set the java.library.path system + * property. To shield the vm from the complication of multiple + * LD_LIBRARY_PATH variables, if the appropriate data model + * specific variable is set, we will act as if LD_LIBRARY_PATH had + * the value of the data model specific variant and the data model + * specific variant will be unset. Note that the variable for the + * *wanted* data model must be used (if it is set), not simply the + * current running data model. + */ + + switch(wanted) { + case 0: + if(running == 32) { + dmpath = getenv("LD_LIBRARY_PATH_32"); + wanted = 32; + } + else { + dmpath = getenv("LD_LIBRARY_PATH_64"); + wanted = 64; + } + break; + + case 32: + dmpath = getenv("LD_LIBRARY_PATH_32"); + break; + + case 64: + dmpath = getenv("LD_LIBRARY_PATH_64"); + break; + + default: + fprintf(stderr, "Improper value at line %d.", __LINE__); + exit(1); /* unknown value in wanted */ + break; + } + + /* + * If dmpath is NULL, the relevant data model specific variable is + * not set and normal LD_LIBRARY_PATH should be used. + */ + if( dmpath == NULL) { + runpath = getenv("LD_LIBRARY_PATH"); + } + else { + runpath = dmpath; + } +#else + /* + * If not on Solaris, assume only a single LD_LIBRARY_PATH + * variable. + */ + runpath = getenv("LD_LIBRARY_PATH"); +#endif /* __sun */ + +#ifdef __linux + /* + * On linux, if a binary is running as sgid or suid, glibc sets + * LD_LIBRARY_PATH to the empty string for security purposes. (In + * contrast, on Solaris the LD_LIBRARY_PATH variable for a + * privileged binary does not lose its settings; but the dynamic + * linker does apply more scrutiny to the path.) The launcher uses + * the value of LD_LIBRARY_PATH to prevent an exec loop. + * Therefore, if we are running sgid or suid, this function's + * setting of LD_LIBRARY_PATH will be ineffective and we should + * return from the function now. Getting the right libraries to + * be found must be handled through other mechanisms. + */ + if((getgid() != getegid()) || (getuid() != geteuid()) ) { + return; + } +#endif + + /* runpath contains current effective LD_LIBRARY_PATH setting */ + + jvmpath = strdup(jvmpath); + new_runpath = MemAlloc( ((runpath!=NULL)?strlen(runpath):0) + + 2*strlen(jrepath) + 2*strlen(arch) + + strlen(jvmpath) + 52); + newpath = new_runpath + strlen("LD_LIBRARY_PATH="); + + + /* + * Create desired LD_LIBRARY_PATH value for target data model. + */ + { + /* remove the name of the .so from the JVM path */ + lastslash = strrchr(jvmpath, '/'); + if (lastslash) + *lastslash = '\0'; + + + /* jvmpath, ((running != wanted)?((wanted==64)?"/"BIG_ARCH:"/.."):""), */ + + sprintf(new_runpath, "LD_LIBRARY_PATH=" + "%s:" + "%s/lib/%s:" + "%s/../lib/%s", + jvmpath, +#ifdef DUAL_MODE + jrepath, ((wanted==64)?BIG_ARCH:SMALL_ARCH), + jrepath, ((wanted==64)?BIG_ARCH:SMALL_ARCH) +#else + jrepath, arch, + jrepath, arch +#endif + ); + + + /* + * Check to make sure that the prefix of the current path is the + * desired environment variable setting. + */ + if (runpath != NULL && + strncmp(newpath, runpath, strlen(newpath))==0 && + (runpath[strlen(newpath)] == 0 || runpath[strlen(newpath)] == ':') && + (running == wanted) /* data model does not have to be changed */ +#ifdef __sun + && (dmpath == NULL) /* data model specific variables not set */ +#endif + ) { + + return; + + } + } + + /* + * Place the desired environment setting onto the prefix of + * LD_LIBRARY_PATH. Note that this prevents any possible infinite + * loop of execv() because we test for the prefix, above. + */ + if (runpath != 0) { + strcat(new_runpath, ":"); + strcat(new_runpath, runpath); + } + + if( putenv(new_runpath) != 0) { + exit(1); /* problem allocating memory; LD_LIBRARY_PATH not set + properly */ + } + + /* + * Unix systems document that they look at LD_LIBRARY_PATH only + * once at startup, so we have to re-exec the current executable + * to get the changed environment variable to have an effect. + */ + +#ifdef __sun + /* + * If dmpath is not NULL, remove the data model specific string + * in the environment for the exec'ed child. + */ + + if( dmpath != NULL) + (void)UnsetEnv((wanted==32)?"LD_LIBRARY_PATH_32":"LD_LIBRARY_PATH_64"); +#endif + + newenvp = environ; + + { + char *newexec = execname; +#ifdef DUAL_MODE + /* + * If the data model is being changed, the path to the + * executable must be updated accordingly; the executable name + * and directory the executable resides in are separate. In the + * case of 32 => 64, the new bits are assumed to reside in, e.g. + * "olddir/BIGARCH/execname"; in the case of 64 => 32, + * the bits are assumed to be in "olddir/../execname". For example, + * + * olddir/sparcv9/execname + * olddir/amd64/execname + * + * for Solaris SPARC and Linux amd64, respectively. + */ + + if (running != wanted) { + char *oldexec = strcpy(MemAlloc(strlen(execname) + 1), execname); + char *olddir = oldexec; + char *oldbase = strrchr(oldexec, '/'); + + + newexec = MemAlloc(strlen(execname) + 20); + *oldbase++ = 0; + sprintf(newexec, "%s/%s/%s", olddir, + ((wanted==64) ? BIG_ARCH : ".."), oldbase); + argv[0] = newexec; + } +#endif + + execve(newexec, argv, newenvp); + perror("execve()"); + + fprintf(stderr, "Error trying to exec %s.\n", newexec); + fprintf(stderr, "Check if file exists and permissions are set correctly.\n"); + +#ifdef DUAL_MODE + if (running != wanted) { + fprintf(stderr, "Failed to start a %d-bit JVM process from a %d-bit JVM.\n", + wanted, running); +# ifdef __sun + +# ifdef __sparc + fprintf(stderr, "Verify all necessary J2SE components have been installed.\n" ); + fprintf(stderr, + "(Solaris SPARC 64-bit components must be installed after 32-bit components.)\n" ); +# else + fprintf(stderr, "Either 64-bit processes are not supported by this platform\n"); + fprintf(stderr, "or the 64-bit components have not been installed.\n"); +# endif + } +# endif +#endif + + } + + exit(1); + } + +#else /* ifndef GAMMA */ + + /* gamma launcher is simpler in that it doesn't handle VM flavors, data */ + /* model, LD_LIBRARY_PATH, etc. Assuming everything is set-up correctly */ + /* all we need to do here is to return correct path names. See also */ + /* GetJVMPath() and GetApplicationHome(). */ + + { char *arch = (char *)GetArch(); /* like sparc or sparcv9 */ + char *p; + + if (!GetJREPath(jrepath, so_jrepath, arch, JNI_FALSE) ) { + fprintf(stderr, "Error: could not find Java 2 Runtime Environment.\n"); + exit(2); + } + + if (!GetJVMPath(jrepath, NULL, jvmpath, so_jvmpath, arch )) { + fprintf(stderr, "Error: no JVM at `%s'.\n", jvmpath); + exit(4); + } + } + +#endif /* ifndef GAMMA */ +} + + +/* + * On Solaris VM choosing is done by the launcher (java.c). + */ +static jboolean +GetJVMPath(const char *jrepath, const char *jvmtype, + char *jvmpath, jint jvmpathsize, char * arch) +{ + struct stat s; + +#ifndef GAMMA + if (strchr(jvmtype, '/')) { + sprintf(jvmpath, "%s/" JVM_DLL, jvmtype); + } else { + sprintf(jvmpath, "%s/lib/%s/%s/" JVM_DLL, jrepath, arch, jvmtype); + } +#else + /* For gamma launcher, JVM is either built-in or in the same directory. */ + /* Either way we return "/libjvm.so" where is the */ + /* directory where gamma launcher is located. */ + + char *p; + + snprintf(jvmpath, jvmpathsize, "%s", GetExecname()); + p = strrchr(jvmpath, '/'); + if (p) { + /* replace executable name with libjvm.so */ + snprintf(p + 1, jvmpathsize - (p + 1 - jvmpath), "%s", JVM_DLL); + } else { + /* this case shouldn't happen */ + snprintf(jvmpath, jvmpathsize, "%s", JVM_DLL); + } +#endif + + if (_launcher_debug) + printf("Does `%s' exist ... ", jvmpath); + + if (stat(jvmpath, &s) == 0) { + if (_launcher_debug) + printf("yes.\n"); + return JNI_TRUE; + } else { + if (_launcher_debug) + printf("no.\n"); + return JNI_FALSE; + } +} + +/* + * Find path to JRE based on .exe's location or registry settings. + */ +static jboolean +GetJREPath(char *path, jint pathsize, char * arch, jboolean speculative) +{ + char libjava[MAXPATHLEN]; + + if (GetApplicationHome(path, pathsize)) { + /* Is JRE co-located with the application? */ + sprintf(libjava, "%s/lib/%s/" JAVA_DLL, path, arch); + if (access(libjava, F_OK) == 0) { + goto found; + } + + /* Does the app ship a private JRE in /jre directory? */ + sprintf(libjava, "%s/jre/lib/%s/" JAVA_DLL, path, arch); + if (access(libjava, F_OK) == 0) { + strcat(path, "/jre"); + goto found; + } + } + + if (!speculative) + fprintf(stderr, "Error: could not find " JAVA_DLL "\n"); + return JNI_FALSE; + + found: + if (_launcher_debug) + printf("JRE path is %s\n", path); + return JNI_TRUE; +} + +jboolean +LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) +{ +#ifdef GAMMA + /* JVM is directly linked with gamma launcher; no dlopen() */ + ifn->CreateJavaVM = JNI_CreateJavaVM; + ifn->GetDefaultJavaVMInitArgs = JNI_GetDefaultJavaVMInitArgs; + return JNI_TRUE; +#else + Dl_info dlinfo; + void *libjvm; + + if (_launcher_debug) { + printf("JVM path is %s\n", jvmpath); + } + + libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL); + if (libjvm == NULL) { +#if defined(__sparc) && !defined(_LP64) /* i.e. 32-bit sparc */ + FILE * fp; + Elf32_Ehdr elf_head; + int count; + int location; + + fp = fopen(jvmpath, "r"); + if(fp == NULL) + goto error; + + /* read in elf header */ + count = fread((void*)(&elf_head), sizeof(Elf32_Ehdr), 1, fp); + fclose(fp); + if(count < 1) + goto error; + + /* + * Check for running a server vm (compiled with -xarch=v8plus) + * on a stock v8 processor. In this case, the machine type in + * the elf header would not be included the architecture list + * provided by the isalist command, which is turn is gotten from + * sysinfo. This case cannot occur on 64-bit hardware and thus + * does not have to be checked for in binaries with an LP64 data + * model. + */ + if(elf_head.e_machine == EM_SPARC32PLUS) { + char buf[257]; /* recommended buffer size from sysinfo man + page */ + long length; + char* location; + + length = sysinfo(SI_ISALIST, buf, 257); + if(length > 0) { + location = strstr(buf, "sparcv8plus "); + if(location == NULL) { + fprintf(stderr, "SPARC V8 processor detected; Server compiler requires V9 or better.\n"); + fprintf(stderr, "Use Client compiler on V8 processors.\n"); + fprintf(stderr, "Could not create the Java virtual machine.\n"); + return JNI_FALSE; + } + } + } +#endif + fprintf(stderr, "dl failure on line %d", __LINE__); + goto error; + } + + ifn->CreateJavaVM = (CreateJavaVM_t) + dlsym(libjvm, "JNI_CreateJavaVM"); + if (ifn->CreateJavaVM == NULL) + goto error; + + ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t) + dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs"); + if (ifn->GetDefaultJavaVMInitArgs == NULL) + goto error; + + return JNI_TRUE; + +error: + fprintf(stderr, "Error: failed %s, because %s\n", jvmpath, dlerror()); + return JNI_FALSE; +#endif /* GAMMA */ +} + +/* + * Get the path to the file that has the usage message for -X options. + */ +void +GetXUsagePath(char *buf, jint bufsize) +{ + static const char Xusage_txt[] = "/Xusage.txt"; + Dl_info dlinfo; + + /* we use RTLD_NOW because of problems with ld.so.1 and green threads */ + dladdr(dlsym(dlopen(JVM_DLL, RTLD_NOW), "JNI_CreateJavaVM"), &dlinfo); + strncpy(buf, (char *)dlinfo.dli_fname, bufsize - sizeof(Xusage_txt)); + + buf[bufsize-1] = '\0'; + strcpy(strrchr(buf, '/'), Xusage_txt); +} + +/* + * If app is "/foo/bin/javac", or "/foo/bin/sparcv9/javac" then put + * "/foo" into buf. + */ +jboolean +GetApplicationHome(char *buf, jint bufsize) +{ +#ifdef __linux__ + char *execname = GetExecname(); + if (execname) { + strncpy(buf, execname, bufsize-1); + buf[bufsize-1] = '\0'; + } else { + return JNI_FALSE; + } +#else + Dl_info dlinfo; + + dladdr((void *)GetApplicationHome, &dlinfo); + if (realpath(dlinfo.dli_fname, buf) == NULL) { + fprintf(stderr, "Error: realpath(`%s') failed.\n", dlinfo.dli_fname); + return JNI_FALSE; + } +#endif + +#ifdef GAMMA + { + /* gamma launcher uses JAVA_HOME environment variable to find JDK/JRE */ + char* java_home_var = getenv("JAVA_HOME"); + if (java_home_var == NULL) { + printf("JAVA_HOME must point to a valid JDK/JRE to run gamma\n"); + return JNI_FALSE; + } + snprintf(buf, bufsize, "%s", java_home_var); + } +#else + if (strrchr(buf, '/') == 0) { + buf[0] = '\0'; + return JNI_FALSE; + } + *(strrchr(buf, '/')) = '\0'; /* executable file */ + if (strlen(buf) < 4 || strrchr(buf, '/') == 0) { + buf[0] = '\0'; + return JNI_FALSE; + } + if (strcmp("/bin", buf + strlen(buf) - 4) != 0) + *(strrchr(buf, '/')) = '\0'; /* sparcv9 or amd64 */ + if (strlen(buf) < 4 || strcmp("/bin", buf + strlen(buf) - 4) != 0) { + buf[0] = '\0'; + return JNI_FALSE; + } + *(strrchr(buf, '/')) = '\0'; /* bin */ +#endif /* GAMMA */ + + return JNI_TRUE; +} + + +/* + * Return true if the named program exists + */ +static int +ProgramExists(char *name) +{ + struct stat sb; + if (stat(name, &sb) != 0) return 0; + if (S_ISDIR(sb.st_mode)) return 0; + return (sb.st_mode & S_IEXEC) != 0; +} + + +/* + * Find a command in a directory, returning the path. + */ +static char * +Resolve(char *indir, char *cmd) +{ + char name[PATH_MAX + 2], *real; + + if ((strlen(indir) + strlen(cmd) + 1) > PATH_MAX) return 0; + sprintf(name, "%s%c%s", indir, FILE_SEPARATOR, cmd); + if (!ProgramExists(name)) return 0; + real = MemAlloc(PATH_MAX + 2); + if (!realpath(name, real)) + strcpy(real, name); + return real; +} + + +/* + * Find a path for the executable + */ +static char * +FindExecName(char *program) +{ + char cwdbuf[PATH_MAX+2]; + char *path; + char *tmp_path; + char *f; + char *result = NULL; + + /* absolute path? */ + if (*program == FILE_SEPARATOR || + (FILE_SEPARATOR=='\\' && strrchr(program, ':'))) + return Resolve("", program+1); + + /* relative path? */ + if (strrchr(program, FILE_SEPARATOR) != 0) { + char buf[PATH_MAX+2]; + return Resolve(getcwd(cwdbuf, sizeof(cwdbuf)), program); + } + + /* from search path? */ + path = getenv("PATH"); + if (!path || !*path) path = "."; + tmp_path = MemAlloc(strlen(path) + 2); + strcpy(tmp_path, path); + + for (f=tmp_path; *f && result==0; ) { + char *s = f; + while (*f && (*f != PATH_SEPARATOR)) ++f; + if (*f) *f++ = 0; + if (*s == FILE_SEPARATOR) + result = Resolve(s, program); + else { + /* relative path element */ + char dir[2*PATH_MAX]; + sprintf(dir, "%s%c%s", getcwd(cwdbuf, sizeof(cwdbuf)), + FILE_SEPARATOR, s); + result = Resolve(dir, program); + } + if (result != 0) break; + } + + free(tmp_path); + return result; +} + + +/* Store the name of the executable once computed */ +static char *execname = NULL; + +/* + * Compute the name of the executable + * + * In order to re-exec securely we need the absolute path of the + * executable. On Solaris getexecname(3c) may not return an absolute + * path so we use dladdr to get the filename of the executable and + * then use realpath to derive an absolute path. From Solaris 9 + * onwards the filename returned in DL_info structure from dladdr is + * an absolute pathname so technically realpath isn't required. + * On Linux we read the executable name from /proc/self/exe. + * As a fallback, and for platforms other than Solaris and Linux, + * we use FindExecName to compute the executable name. + */ +static char * +SetExecname(char **argv) +{ + char* exec_path = NULL; + + if (execname != NULL) /* Already determined */ + return (execname); + +#if defined(__sun) + { + Dl_info dlinfo; + if (dladdr((void*)&SetExecname, &dlinfo)) { + char *resolved = (char*)MemAlloc(PATH_MAX+1); + if (resolved != NULL) { + exec_path = realpath(dlinfo.dli_fname, resolved); + if (exec_path == NULL) { + free(resolved); + } + } + } + } +#elif defined(__linux__) + { + const char* self = "/proc/self/exe"; + char buf[PATH_MAX+1]; + int len = readlink(self, buf, PATH_MAX); + if (len >= 0) { + buf[len] = '\0'; /* readlink doesn't nul terminate */ + exec_path = strdup(buf); + } + } +#else /* !__sun && !__linux */ + { + /* Not implemented */ + } +#endif + + if (exec_path == NULL) { + exec_path = FindExecName(argv[0]); + } + execname = exec_path; + return exec_path; +} + +/* + * Return the name of the executable. Used in java_md.c to find the JRE area. + */ +static char * +GetExecname() { + return execname; +} + +void ReportErrorMessage(char * message, jboolean always) { + if (always) { + fprintf(stderr, "%s\n", message); + } +} + +void ReportErrorMessage2(char * format, char * string, jboolean always) { + if (always) { + fprintf(stderr, format, string); + fprintf(stderr, "\n"); + } +} + +void ReportExceptionDescription(JNIEnv * env) { + (*env)->ExceptionDescribe(env); +} + +/* + * Return JNI_TRUE for an option string that has no effect but should + * _not_ be passed on to the vm; return JNI_FALSE otherwise. On + * Solaris SPARC, this screening needs to be done if: + * 1) LD_LIBRARY_PATH does _not_ need to be reset and + * 2) -d32 or -d64 is passed to a binary with a matching data model + * (the exec in SetLibraryPath removes -d options and points the + * exec to the proper binary). When this exec is not done, these options + * would end up getting passed onto the vm. + */ +jboolean RemovableMachineDependentOption(char * option) { + /* + * Unconditionally remove both -d32 and -d64 options since only + * the last such options has an effect; e.g. + * java -d32 -d64 -d32 -version + * is equivalent to + * java -d32 -version + */ + + if( (strcmp(option, "-d32") == 0 ) || + (strcmp(option, "-d64") == 0 )) + return JNI_TRUE; + else + return JNI_FALSE; +} + +void PrintMachineDependentOptions() { + fprintf(stdout, + " -d32 use a 32-bit data model if available\n" + "\n" + " -d64 use a 64-bit data model if available\n"); + return; +} + +#ifndef GAMMA /* gamma launcher does not have ergonomics */ + +/* + * The following methods (down to ServerClassMachine()) answer + * the question about whether a machine is a "server-class" + * machine. A server-class machine is loosely defined as one + * with 2 or more processors and 2 gigabytes or more physical + * memory. The definition of a processor is a physical package, + * not a hyperthreaded chip masquerading as a multi-processor. + * The definition of memory is also somewhat fuzzy, since x86 + * machines seem not to report all the memory in their DIMMs, we + * think because of memory mapping of graphics cards, etc. + * + * This code is somewhat more confused with #ifdef's than we'd + * like because this file is used by both Solaris and Linux + * platforms, and so needs to be parameterized for SPARC and + * i586 hardware. The other Linux platforms (amd64 and ia64) + * don't even ask this question, because they only come with + * server JVMs. */ + +# define KB (1024UL) +# define MB (1024UL * KB) +# define GB (1024UL * MB) + +/* Compute physical memory by asking the OS */ +uint64_t +physical_memory(void) { + const uint64_t pages = (uint64_t) sysconf(_SC_PHYS_PAGES); + const uint64_t page_size = (uint64_t) sysconf(_SC_PAGESIZE); + const uint64_t result = pages * page_size; +# define UINT64_FORMAT "%" PRIu64 + + if (_launcher_debug) { + printf("pages: " UINT64_FORMAT + " page_size: " UINT64_FORMAT + " physical memory: " UINT64_FORMAT " (%.3fGB)\n", + pages, page_size, result, result / (double) GB); + } + return result; +} + +#if defined(__sun) && defined(__sparc) + +/* Methods for solaris-sparc: these are easy. */ + +/* Ask the OS how many processors there are. */ +unsigned long +physical_processors(void) { + const unsigned long sys_processors = sysconf(_SC_NPROCESSORS_CONF); + + if (_launcher_debug) { + printf("sysconf(_SC_NPROCESSORS_CONF): %lu\n", sys_processors); + } + return sys_processors; +} + +/* The solaris-sparc version of the "server-class" predicate. */ +jboolean +solaris_sparc_ServerClassMachine(void) { + jboolean result = JNI_FALSE; + /* How big is a server class machine? */ + const unsigned long server_processors = 2UL; + const uint64_t server_memory = 2UL * GB; + const uint64_t actual_memory = physical_memory(); + + /* Is this a server class machine? */ + if (actual_memory >= server_memory) { + const unsigned long actual_processors = physical_processors(); + if (actual_processors >= server_processors) { + result = JNI_TRUE; + } + } + if (_launcher_debug) { + printf("solaris_" ARCH "_ServerClassMachine: %s\n", + (result == JNI_TRUE ? "JNI_TRUE" : "JNI_FALSE")); + } + return result; +} + +#endif /* __sun && __sparc */ + +#if defined(__sun) && defined(i586) + +/* + * A utility method for asking the CPU about itself. + * There's a corresponding version of linux-i586 + * because the compilers are different. + */ +void +get_cpuid(uint32_t arg, + uint32_t* eaxp, + uint32_t* ebxp, + uint32_t* ecxp, + uint32_t* edxp) { +#ifdef _LP64 + asm( + /* rbx is a callee-saved register */ + " movq %rbx, %r11 \n" + /* rdx and rcx are 3rd and 4th argument registers */ + " movq %rdx, %r10 \n" + " movq %rcx, %r9 \n" + " movl %edi, %eax \n" + " cpuid \n" + " movl %eax, (%rsi)\n" + " movl %ebx, (%r10)\n" + " movl %ecx, (%r9) \n" + " movl %edx, (%r8) \n" + /* Restore rbx */ + " movq %r11, %rbx"); +#else + /* EBX is a callee-saved register */ + asm(" pushl %ebx"); + /* Need ESI for storing through arguments */ + asm(" pushl %esi"); + asm(" movl 8(%ebp), %eax \n" + " cpuid \n" + " movl 12(%ebp), %esi \n" + " movl %eax, (%esi) \n" + " movl 16(%ebp), %esi \n" + " movl %ebx, (%esi) \n" + " movl 20(%ebp), %esi \n" + " movl %ecx, (%esi) \n" + " movl 24(%ebp), %esi \n" + " movl %edx, (%esi) "); + /* Restore ESI and EBX */ + asm(" popl %esi"); + /* Restore EBX */ + asm(" popl %ebx"); +#endif +} + +#endif /* __sun && i586 */ + +#if defined(__linux__) && defined(i586) + +/* + * A utility method for asking the CPU about itself. + * There's a corresponding version of solaris-i586 + * because the compilers are different. + */ +void +get_cpuid(uint32_t arg, + uint32_t* eaxp, + uint32_t* ebxp, + uint32_t* ecxp, + uint32_t* edxp) { +#ifdef _LP64 + __asm__ volatile (/* Instructions */ + " movl %4, %%eax \n" + " cpuid \n" + " movl %%eax, (%0)\n" + " movl %%ebx, (%1)\n" + " movl %%ecx, (%2)\n" + " movl %%edx, (%3)\n" + : /* Outputs */ + : /* Inputs */ + "r" (eaxp), + "r" (ebxp), + "r" (ecxp), + "r" (edxp), + "r" (arg) + : /* Clobbers */ + "%rax", "%rbx", "%rcx", "%rdx", "memory" + ); +#else + uint32_t value_of_eax = 0; + uint32_t value_of_ebx = 0; + uint32_t value_of_ecx = 0; + uint32_t value_of_edx = 0; + __asm__ volatile (/* Instructions */ + /* ebx is callee-save, so push it */ + /* even though it's in the clobbers section */ + " pushl %%ebx \n" + " movl %4, %%eax \n" + " cpuid \n" + " movl %%eax, %0 \n" + " movl %%ebx, %1 \n" + " movl %%ecx, %2 \n" + " movl %%edx, %3 \n" + /* restore ebx */ + " popl %%ebx \n" + + : /* Outputs */ + "=m" (value_of_eax), + "=m" (value_of_ebx), + "=m" (value_of_ecx), + "=m" (value_of_edx) + : /* Inputs */ + "m" (arg) + : /* Clobbers */ + "%eax", "%ebx", "%ecx", "%edx" + ); + *eaxp = value_of_eax; + *ebxp = value_of_ebx; + *ecxp = value_of_ecx; + *edxp = value_of_edx; +#endif +} + +#endif /* __linux__ && i586 */ + +#ifdef i586 +/* + * Routines shared by solaris-i586 and linux-i586. + */ + +enum HyperThreadingSupport_enum { + hts_supported = 1, + hts_too_soon_to_tell = 0, + hts_not_supported = -1, + hts_not_pentium4 = -2, + hts_not_intel = -3 +}; +typedef enum HyperThreadingSupport_enum HyperThreadingSupport; + +/* Determine if hyperthreading is supported */ +HyperThreadingSupport +hyperthreading_support(void) { + HyperThreadingSupport result = hts_too_soon_to_tell; + /* Bits 11 through 8 is family processor id */ +# define FAMILY_ID_SHIFT 8 +# define FAMILY_ID_MASK 0xf + /* Bits 23 through 20 is extended family processor id */ +# define EXT_FAMILY_ID_SHIFT 20 +# define EXT_FAMILY_ID_MASK 0xf + /* Pentium 4 family processor id */ +# define PENTIUM4_FAMILY_ID 0xf + /* Bit 28 indicates Hyper-Threading Technology support */ +# define HT_BIT_SHIFT 28 +# define HT_BIT_MASK 1 + uint32_t vendor_id[3] = { 0U, 0U, 0U }; + uint32_t value_of_eax = 0U; + uint32_t value_of_edx = 0U; + uint32_t dummy = 0U; + + /* Yes, this is supposed to be [0], [2], [1] */ + get_cpuid(0, &dummy, &vendor_id[0], &vendor_id[2], &vendor_id[1]); + if (_launcher_debug) { + printf("vendor: %c %c %c %c %c %c %c %c %c %c %c %c \n", + ((vendor_id[0] >> 0) & 0xff), + ((vendor_id[0] >> 8) & 0xff), + ((vendor_id[0] >> 16) & 0xff), + ((vendor_id[0] >> 24) & 0xff), + ((vendor_id[1] >> 0) & 0xff), + ((vendor_id[1] >> 8) & 0xff), + ((vendor_id[1] >> 16) & 0xff), + ((vendor_id[1] >> 24) & 0xff), + ((vendor_id[2] >> 0) & 0xff), + ((vendor_id[2] >> 8) & 0xff), + ((vendor_id[2] >> 16) & 0xff), + ((vendor_id[2] >> 24) & 0xff)); + } + get_cpuid(1, &value_of_eax, &dummy, &dummy, &value_of_edx); + if (_launcher_debug) { + printf("value_of_eax: 0x%x value_of_edx: 0x%x\n", + value_of_eax, value_of_edx); + } + if ((((value_of_eax >> FAMILY_ID_SHIFT) & FAMILY_ID_MASK) == PENTIUM4_FAMILY_ID) || + (((value_of_eax >> EXT_FAMILY_ID_SHIFT) & EXT_FAMILY_ID_MASK) != 0)) { + if ((((vendor_id[0] >> 0) & 0xff) == 'G') && + (((vendor_id[0] >> 8) & 0xff) == 'e') && + (((vendor_id[0] >> 16) & 0xff) == 'n') && + (((vendor_id[0] >> 24) & 0xff) == 'u') && + (((vendor_id[1] >> 0) & 0xff) == 'i') && + (((vendor_id[1] >> 8) & 0xff) == 'n') && + (((vendor_id[1] >> 16) & 0xff) == 'e') && + (((vendor_id[1] >> 24) & 0xff) == 'I') && + (((vendor_id[2] >> 0) & 0xff) == 'n') && + (((vendor_id[2] >> 8) & 0xff) == 't') && + (((vendor_id[2] >> 16) & 0xff) == 'e') && + (((vendor_id[2] >> 24) & 0xff) == 'l')) { + if (((value_of_edx >> HT_BIT_SHIFT) & HT_BIT_MASK) == HT_BIT_MASK) { + if (_launcher_debug) { + printf("Hyperthreading supported\n"); + } + result = hts_supported; + } else { + if (_launcher_debug) { + printf("Hyperthreading not supported\n"); + } + result = hts_not_supported; + } + } else { + if (_launcher_debug) { + printf("Not GenuineIntel\n"); + } + result = hts_not_intel; + } + } else { + if (_launcher_debug) { + printf("not Pentium 4 or extended\n"); + } + result = hts_not_pentium4; + } + return result; +} + +/* Determine how many logical processors there are per CPU */ +unsigned int +logical_processors_per_package(void) { + /* + * After CPUID with EAX==1, register EBX bits 23 through 16 + * indicate the number of logical processors per package + */ +# define NUM_LOGICAL_SHIFT 16 +# define NUM_LOGICAL_MASK 0xff + unsigned int result = 1U; + const HyperThreadingSupport hyperthreading = hyperthreading_support(); + + if (hyperthreading == hts_supported) { + uint32_t value_of_ebx = 0U; + uint32_t dummy = 0U; + + get_cpuid(1, &dummy, &value_of_ebx, &dummy, &dummy); + result = (value_of_ebx >> NUM_LOGICAL_SHIFT) & NUM_LOGICAL_MASK; + if (_launcher_debug) { + printf("logical processors per package: %u\n", result); + } + } + return result; +} + +/* Compute the number of physical processors, not logical processors */ +unsigned long +physical_processors(void) { + const long sys_processors = sysconf(_SC_NPROCESSORS_CONF); + unsigned long result = sys_processors; + + if (_launcher_debug) { + printf("sysconf(_SC_NPROCESSORS_CONF): %lu\n", sys_processors); + } + if (sys_processors > 1) { + unsigned int logical_processors = logical_processors_per_package(); + if (logical_processors > 1) { + result = (unsigned long) sys_processors / logical_processors; + } + } + if (_launcher_debug) { + printf("physical processors: %lu\n", result); + } + return result; +} + +#endif /* i586 */ + +#if defined(__sun) && defined(i586) + +/* The definition of a server-class machine for solaris-i586/amd64 */ +jboolean +solaris_i586_ServerClassMachine(void) { + jboolean result = JNI_FALSE; + /* How big is a server class machine? */ + const unsigned long server_processors = 2UL; + const uint64_t server_memory = 2UL * GB; + /* + * We seem not to get our full complement of memory. + * We allow some part (1/8?) of the memory to be "missing", + * based on the sizes of DIMMs, and maybe graphics cards. + */ + const uint64_t missing_memory = 256UL * MB; + const uint64_t actual_memory = physical_memory(); + + /* Is this a server class machine? */ + if (actual_memory >= (server_memory - missing_memory)) { + const unsigned long actual_processors = physical_processors(); + if (actual_processors >= server_processors) { + result = JNI_TRUE; + } + } + if (_launcher_debug) { + printf("solaris_" ARCH "_ServerClassMachine: %s\n", + (result == JNI_TRUE ? "true" : "false")); + } + return result; +} + +#endif /* __sun && i586 */ + +#if defined(__linux__) && defined(i586) + +/* The definition of a server-class machine for linux-i586 */ +jboolean +linux_i586_ServerClassMachine(void) { + jboolean result = JNI_FALSE; + /* How big is a server class machine? */ + const unsigned long server_processors = 2UL; + const uint64_t server_memory = 2UL * GB; + /* + * We seem not to get our full complement of memory. + * We allow some part (1/8?) of the memory to be "missing", + * based on the sizes of DIMMs, and maybe graphics cards. + */ + const uint64_t missing_memory = 256UL * MB; + const uint64_t actual_memory = physical_memory(); + + /* Is this a server class machine? */ + if (actual_memory >= (server_memory - missing_memory)) { + const unsigned long actual_processors = physical_processors(); + if (actual_processors >= server_processors) { + result = JNI_TRUE; + } + } + if (_launcher_debug) { + printf("linux_" ARCH "_ServerClassMachine: %s\n", + (result == JNI_TRUE ? "true" : "false")); + } + return result; +} + +#endif /* __linux__ && i586 */ + +/* Dispatch to the platform-specific definition of "server-class" */ +jboolean +ServerClassMachine(void) { + jboolean result = JNI_FALSE; +#if defined(__sun) && defined(__sparc) + result = solaris_sparc_ServerClassMachine(); +#elif defined(__sun) && defined(i586) + result = solaris_i586_ServerClassMachine(); +#elif defined(__linux__) && defined(i586) + result = linux_i586_ServerClassMachine(); +#else + if (_launcher_debug) { + printf("ServerClassMachine: returns default value of %s\n", + (result == JNI_TRUE ? "true" : "false")); + } +#endif + return result; +} + +#endif /* ifndef GAMMA */ + +#ifndef GAMMA /* gamma launcher does not choose JDK/JRE/JVM */ + +/* + * Since using the file system as a registry is a bit risky, perform + * additional sanity checks on the identified directory to validate + * it as a valid jre/sdk. + * + * Return 0 if the tests fail; otherwise return non-zero (true). + * + * Note that checking for anything more than the existence of an + * executable object at bin/java relative to the path being checked + * will break the regression tests. + */ +static int +CheckSanity(char *path, char *dir) +{ + char buffer[PATH_MAX]; + + if (strlen(path) + strlen(dir) + 11 > PATH_MAX) + return (0); /* Silently reject "impossibly" long paths */ + + (void)strcat(strcat(strcat(strcpy(buffer, path), "/"), dir), "/bin/java"); + return ((access(buffer, X_OK) == 0) ? 1 : 0); +} + +/* + * Determine if there is an acceptable JRE in the directory dirname. + * Upon locating the "best" one, return a fully qualified path to + * it. "Best" is defined as the most advanced JRE meeting the + * constraints contained in the manifest_info. If no JRE in this + * directory meets the constraints, return NULL. + * + * Note that we don't check for errors in reading the directory + * (which would be done by checking errno). This is because it + * doesn't matter if we get an error reading the directory, or + * we just don't find anything interesting in the directory. We + * just return NULL in either case. + * + * The historical names of j2sdk and j2re were changed to jdk and + * jre respecively as part of the 1.5 rebranding effort. Since the + * former names are legacy on Linux, they must be recognized for + * all time. Fortunately, this is a minor cost. + */ +static char +*ProcessDir(manifest_info *info, char *dirname) +{ + DIR *dirp; + struct dirent *dp; + char *best = NULL; + int offset; + int best_offset = 0; + char *ret_str = NULL; + char buffer[PATH_MAX]; + + if ((dirp = opendir(dirname)) == NULL) + return (NULL); + + do { + if ((dp = readdir(dirp)) != NULL) { + offset = 0; + if ((strncmp(dp->d_name, "jre", 3) == 0) || + (strncmp(dp->d_name, "jdk", 3) == 0)) + offset = 3; + else if (strncmp(dp->d_name, "j2re", 4) == 0) + offset = 4; + else if (strncmp(dp->d_name, "j2sdk", 5) == 0) + offset = 5; + if (offset > 0) { + if ((acceptable_release(dp->d_name + offset, + info->jre_version)) && CheckSanity(dirname, dp->d_name)) + if ((best == NULL) || (exact_version_id( + dp->d_name + offset, best + best_offset) > 0)) { + if (best != NULL) + free(best); + best = strdup(dp->d_name); + best_offset = offset; + } + } + } + } while (dp != NULL); + (void) closedir(dirp); + if (best == NULL) + return (NULL); + else { + ret_str = MemAlloc(strlen(dirname) + strlen(best) + 2); + ret_str = strcat(strcat(strcpy(ret_str, dirname), "/"), best); + free(best); + return (ret_str); + } +} + +/* + * This is the global entry point. It examines the host for the optimal + * JRE to be used by scanning a set of directories. The set of directories + * is platform dependent and can be overridden by the environment + * variable JAVA_VERSION_PATH. + * + * This routine itself simply determines the set of appropriate + * directories before passing control onto ProcessDir(). + */ +char* +LocateJRE(manifest_info* info) +{ + char *path; + char *home; + char *target = NULL; + char *dp; + char *cp; + + /* + * Start by getting JAVA_VERSION_PATH + */ + if (info->jre_restrict_search) + path = strdup(system_dir); + else if ((path = getenv("JAVA_VERSION_PATH")) != NULL) + path = strdup(path); + else + if ((home = getenv("HOME")) != NULL) { + path = (char *)MemAlloc(strlen(home) + 13); + path = strcat(strcat(strcat(strcpy(path, home), + user_dir), ":"), system_dir); + } else + path = strdup(system_dir); + + /* + * Step through each directory on the path. Terminate the scan with + * the first directory with an acceptable JRE. + */ + cp = dp = path; + while (dp != NULL) { + cp = strchr(dp, (int)':'); + if (cp != NULL) + *cp = (char)NULL; + if ((target = ProcessDir(info, dp)) != NULL) + break; + dp = cp; + if (dp != NULL) + dp++; + } + free(path); + return (target); +} + +/* + * Given a path to a jre to execute, this routine checks if this process + * is indeed that jre. If not, it exec's that jre. + * + * We want to actually check the paths rather than just the version string + * built into the executable, so that given version specification (and + * JAVA_VERSION_PATH) will yield the exact same Java environment, regardless + * of the version of the arbitrary launcher we start with. + */ +void +ExecJRE(char *jre, char **argv) +{ + char wanted[PATH_MAX]; + char *execname; + char *progname; + + /* + * Resolve the real path to the directory containing the selected JRE. + */ + if (realpath(jre, wanted) == NULL) { + fprintf(stderr, "Unable to resolve %s\n", jre); + exit(1); + } + + /* + * Resolve the real path to the currently running launcher. + */ + execname = SetExecname(argv); + if (execname == NULL) { + fprintf(stderr, "Unable to resolve current executable\n"); + exit(1); + } + + /* + * If the path to the selected JRE directory is a match to the initial + * portion of the path to the currently executing JRE, we have a winner! + * If so, just return. + */ + if (strncmp(wanted, execname, strlen(wanted)) == 0) + return; /* I am the droid you were looking for */ + + /* + * If this isn't the selected version, exec the selected version. + */ +#ifdef JAVA_ARGS /* javac, jar and friends. */ + progname = "java"; +#else /* java, oldjava, javaw and friends */ +#ifdef PROGNAME + progname = PROGNAME; +#else + progname = *argv; + if ((s = strrchr(progname, FILE_SEPARATOR)) != 0) { + progname = s + 1; + } +#endif /* PROGNAME */ +#endif /* JAVA_ARGS */ + + /* + * This should never happen (because of the selection code in SelectJRE), + * but check for "impossibly" long path names just because buffer overruns + * can be so deadly. + */ + if (strlen(wanted) + strlen(progname) + 6 > PATH_MAX) { + fprintf(stderr, "Path length exceeds maximum length (PATH_MAX)\n"); + exit(1); + } + + /* + * Construct the path and exec it. + */ + (void)strcat(strcat(wanted, "/bin/"), progname); + argv[0] = progname; + if (_launcher_debug) { + int i; + printf("execv(\"%s\"", wanted); + for (i = 0; argv[i] != NULL; i++) + printf(", \"%s\"", argv[i]); + printf(")\n"); + } + execv(wanted, argv); + fprintf(stderr, "Exec of %s failed\n", wanted); + exit(1); +} + +#endif /* ifndef GAMMA */ + +/* + * "Borrowed" from Solaris 10 where the unsetenv() function is being added + * to libc thanks to SUSv3 (Standard Unix Specification, version 3). As + * such, in the fullness of time this will appear in libc on all relevant + * Solaris/Linux platforms and maybe even the Windows platform. At that + * time, this stub can be removed. + * + * This implementation removes the environment locking for multithreaded + * applications. (We don't have access to these mutexes within libc and + * the launcher isn't multithreaded.) Note that what remains is platform + * independent, because it only relies on attributes that a POSIX environment + * defines. + * + * Returns 0 on success, -1 on failure. + * + * Also removed was the setting of errno. The only value of errno set + * was EINVAL ("Invalid Argument"). + */ + +/* + * s1(environ) is name=value + * s2(name) is name(not the form of name=value). + * if names match, return value of 1, else return 0 + */ +static int +match_noeq(const char *s1, const char *s2) +{ + while (*s1 == *s2++) { + if (*s1++ == '=') + return (1); + } + if (*s1 == '=' && s2[-1] == '\0') + return (1); + return (0); +} + +/* + * added for SUSv3 standard + * + * Delete entry from environ. + * Do not free() memory! Other threads may be using it. + * Keep it around forever. + */ +static int +borrowed_unsetenv(const char *name) +{ + long idx; /* index into environ */ + + if (name == NULL || *name == '\0' || + strchr(name, '=') != NULL) { + return (-1); + } + + for (idx = 0; environ[idx] != NULL; idx++) { + if (match_noeq(environ[idx], name)) + break; + } + if (environ[idx] == NULL) { + /* name not found but still a success */ + return (0); + } + /* squeeze up one entry */ + do { + environ[idx] = environ[idx+1]; + } while (environ[++idx] != NULL); + + return (0); +} +/* --- End of "borrowed" code --- */ + +/* + * Wrapper for unsetenv() function. + */ +int +UnsetEnv(char *name) +{ + return(borrowed_unsetenv(name)); +} diff --git a/hotspot/src/os/linux/launcher/java_md.h b/hotspot/src/os/linux/launcher/java_md.h new file mode 100644 index 00000000000..89e4d0b7ac8 --- /dev/null +++ b/hotspot/src/os/linux/launcher/java_md.h @@ -0,0 +1,79 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Gamma (Hotspot internal engineering test) launcher based on 1.6.0-b28 JDK, + * search "GAMMA" for gamma specific changes. + */ + +#ifndef JAVA_MD_H +#define JAVA_MD_H + +#include +#include +#include +#ifndef GAMMA +#include "manifest_info.h" +#endif + +#define PATH_SEPARATOR ':' +#define FILESEP "/" +#define FILE_SEPARATOR '/' +#ifndef MAXNAMELEN +#define MAXNAMELEN PATH_MAX +#endif + +#ifdef JAVA_ARGS +/* + * ApplicationHome is prepended to each of these entries; the resulting + * strings are concatenated (seperated by PATH_SEPARATOR) and used as the + * value of -cp option to the launcher. + */ +#ifndef APP_CLASSPATH +#define APP_CLASSPATH { "/lib/tools.jar", "/classes" } +#endif +#endif + +#ifdef HAVE_GETHRTIME +/* + * Support for doing cheap, accurate interval timing. + */ +#include +#define CounterGet() (gethrtime()/1000) +#define Counter2Micros(counts) (counts) +#else +#define CounterGet() (0) +#define Counter2Micros(counts) (1) +#endif /* HAVE_GETHRTIME */ + +/* + * Function prototypes. + */ +#ifndef GAMMA +char *LocateJRE(manifest_info* info); +void ExecJRE(char *jre, char **argv); +#endif +int UnsetEnv(char *name); + +#endif diff --git a/hotspot/src/os/linux/vm/attachListener_linux.cpp b/hotspot/src/os/linux/vm/attachListener_linux.cpp new file mode 100644 index 00000000000..f0ffe6c8d39 --- /dev/null +++ b/hotspot/src/os/linux/vm/attachListener_linux.cpp @@ -0,0 +1,503 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_attachListener_linux.cpp.incl" + +#include +#include +#include +#include +#include +#include + +// The attach mechanism on Linux uses a UNIX domain socket. An attach listener +// thread is created at startup or is created on-demand via a signal from +// the client tool. The attach listener creates a socket and binds it to a file +// in the filesystem. The attach listener then acts as a simple (single- +// threaded) server - tt waits for a client to connect, reads the request, +// executes it, and returns the response to the client via the socket +// connection. +// +// As the socket is a UNIX domain socket it means that only clients on the +// local machine can connect. In addition there are two other aspects to +// the security: +// 1. The well known file that the socket is bound to has permission 400 +// 2. When a client connect, the SO_PEERCRED socket option is used to +// obtain the credentials of client. We check that the effective uid +// of the client matches this process. + +// forward reference +class LinuxAttachOperation; + +class LinuxAttachListener: AllStatic { + private: + // the path to which we bind the UNIX domain socket + static char _path[PATH_MAX+1]; + static bool _has_path; + + // the file descriptor for the listening socket + static int _listener; + + static void set_path(char* path) { + if (path == NULL) { + _has_path = false; + } else { + strncpy(_path, path, PATH_MAX); + _path[PATH_MAX] = '\0'; + _has_path = true; + } + } + + static void set_listener(int s) { _listener = s; } + + // reads a request from the given connected socket + static LinuxAttachOperation* read_request(int s); + + public: + enum { + ATTACH_PROTOCOL_VER = 1 // protocol version + }; + enum { + ATTACH_ERROR_BADVERSION = 101 // error codes + }; + + // initialize the listener, returns 0 if okay + static int init(); + + static char* path() { return _path; } + static bool has_path() { return _has_path; } + static int listener() { return _listener; } + + // write the given buffer to a socket + static int write_fully(int s, char* buf, int len); + + static LinuxAttachOperation* dequeue(); +}; + +class LinuxAttachOperation: public AttachOperation { + private: + // the connection to the client + int _socket; + + public: + void complete(jint res, bufferedStream* st); + + void set_socket(int s) { _socket = s; } + int socket() const { return _socket; } + + LinuxAttachOperation(char* name) : AttachOperation(name) { + set_socket(-1); + } +}; + +// statics +char LinuxAttachListener::_path[PATH_MAX+1]; +bool LinuxAttachListener::_has_path; +int LinuxAttachListener::_listener = -1; + +// Supporting class to help split a buffer into individual components +class ArgumentIterator : public StackObj { + private: + char* _pos; + char* _end; + public: + ArgumentIterator(char* arg_buffer, size_t arg_size) { + _pos = arg_buffer; + _end = _pos + arg_size - 1; + } + char* next() { + if (*_pos == '\0') { + return NULL; + } + char* res = _pos; + char* next_pos = strchr(_pos, '\0'); + if (next_pos < _end) { + next_pos++; + } + _pos = next_pos; + return res; + } +}; + + +// atexit hook to stop listener and unlink the file that it is +// bound too. +extern "C" { + static void listener_cleanup() { + static int cleanup_done; + if (!cleanup_done) { + cleanup_done = 1; + int s = LinuxAttachListener::listener(); + if (s != -1) { + ::close(s); + } + if (LinuxAttachListener::has_path()) { + ::unlink(LinuxAttachListener::path()); + } + } + } +} + +// Initialization - create a listener socket and bind it to a file + +int LinuxAttachListener::init() { + char path[PATH_MAX+1]; // socket file + int listener; // listener socket (file descriptor) + + // register function to cleanup + ::atexit(listener_cleanup); + + // create the listener socket + listener = ::socket(PF_UNIX, SOCK_STREAM, 0); + if (listener == -1) { + return -1; + } + + int res = -1; + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + + // FIXME: Prior to b39 the tool-side API expected to find the well + // known file in the working directory. To allow this libjvm.so work with + // a pre-b39 SDK we create it in the working directory if + // +StartAttachListener is used is used. All unit tests for this feature + // currently used this flag. Once b39 SDK has been promoted we can remove + // this code. + if (StartAttachListener) { + sprintf(path, ".java_pid%d", os::current_process_id()); + strcpy(addr.sun_path, path); + ::unlink(path); + res = ::bind(listener, (struct sockaddr*)&addr, sizeof(addr)); + } + if (res == -1) { + sprintf(path, "%s/.java_pid%d", os::get_temp_directory(), os::current_process_id()); + strcpy(addr.sun_path, path); + ::unlink(path); + res = ::bind(listener, (struct sockaddr*)&addr, sizeof(addr)); + } + if (res == -1) { + RESTARTABLE(::close(listener), res); + return -1; + } + set_path(path); + + // put in listen mode and set permission + if ((::listen(listener, 5) == -1) || (::chmod(path, S_IREAD|S_IWRITE) == -1)) { + RESTARTABLE(::close(listener), res); + ::unlink(path); + set_path(NULL); + return -1; + } + set_listener(listener); + + return 0; +} + +// Given a socket that is connected to a peer we read the request and +// create an AttachOperation. As the socket is blocking there is potential +// for a denial-of-service if the peer does not response. However this happens +// after the peer credentials have been checked and in the worst case it just +// means that the attach listener thread is blocked. +// +LinuxAttachOperation* LinuxAttachListener::read_request(int s) { + char ver_str[8]; + sprintf(ver_str, "%d", ATTACH_PROTOCOL_VER); + + // The request is a sequence of strings so we first figure out the + // expected count and the maximum possible length of the request. + // The request is: + // 00000 + // where is the protocol version (1), is the command + // name ("load", "datadump", ...), and is an argument + int expected_str_count = 2 + AttachOperation::arg_count_max; + int max_len = (strlen(ver_str) + 1) + (AttachOperation::name_length_max + 1) + + AttachOperation::arg_count_max*(AttachOperation::arg_length_max + 1); + + char buf[max_len]; + int str_count = 0; + + // Read until all (expected) strings have been read, the buffer is + // full, or EOF. + + int off = 0; + int left = max_len; + + do { + int n; + RESTARTABLE(read(s, buf+off, left), n); + if (n == -1) { + return NULL; // reset by peer or other error + } + if (n == 0) { + break; + } + for (int i=0; i so check it now to + // check for protocol mis-match + if (str_count == 1) { + if ((strlen(buf) != strlen(ver_str)) || + (atoi(buf) != ATTACH_PROTOCOL_VER)) { + char msg[32]; + sprintf(msg, "%d\n", ATTACH_ERROR_BADVERSION); + write_fully(s, msg, strlen(msg)); + return NULL; + } + } + } + } + off += n; + left -= n; + } while (left > 0 && str_count < expected_str_count); + + if (str_count != expected_str_count) { + return NULL; // incomplete request + } + + // parse request + + ArgumentIterator args(buf, (max_len)-left); + + // version already checked + char* v = args.next(); + + char* name = args.next(); + if (name == NULL || strlen(name) > AttachOperation::name_length_max) { + return NULL; + } + + LinuxAttachOperation* op = new LinuxAttachOperation(name); + + for (int i=0; iset_arg(i, NULL); + } else { + if (strlen(arg) > AttachOperation::arg_length_max) { + delete op; + return NULL; + } + op->set_arg(i, arg); + } + } + + op->set_socket(s); + return op; +} + + +// Dequeue an operation +// +// In the Linux implementation there is only a single operation and clients +// cannot queue commands (except at the socket level). +// +LinuxAttachOperation* LinuxAttachListener::dequeue() { + for (;;) { + int s; + + // wait for client to connect + struct sockaddr addr; + socklen_t len = sizeof(addr); + RESTARTABLE(::accept(listener(), &addr, &len), s); + if (s == -1) { + return NULL; // log a warning? + } + + // get the credentials of the peer and check the effective uid/guid + // - check with jeff on this. + struct ucred cred_info; + socklen_t optlen = sizeof(cred_info); + if (::getsockopt(s, SOL_SOCKET, SO_PEERCRED, (void*)&cred_info, &optlen) == -1) { + int res; + RESTARTABLE(::close(s), res); + continue; + } + uid_t euid = geteuid(); + gid_t egid = getegid(); + + if (cred_info.uid != euid || cred_info.gid != egid) { + int res; + RESTARTABLE(::close(s), res); + continue; + } + + // peer credential look okay so we read the request + LinuxAttachOperation* op = read_request(s); + if (op == NULL) { + int res; + RESTARTABLE(::close(s), res); + continue; + } else { + return op; + } + } +} + +// write the given buffer to the socket +int LinuxAttachListener::write_fully(int s, char* buf, int len) { + do { + int n = ::write(s, buf, len); + if (n == -1) { + if (errno != EINTR) return -1; + } else { + buf += n; + len -= n; + } + } + while (len > 0); + return 0; +} + +// Complete an operation by sending the operation result and any result +// output to the client. At this time the socket is in blocking mode so +// potentially we can block if there is a lot of data and the client is +// non-responsive. For most operations this is a non-issue because the +// default send buffer is sufficient to buffer everything. In the future +// if there are operations that involves a very big reply then it the +// socket could be made non-blocking and a timeout could be used. + +void LinuxAttachOperation::complete(jint result, bufferedStream* st) { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + // write operation result + char msg[32]; + sprintf(msg, "%d\n", result); + int rc = LinuxAttachListener::write_fully(this->socket(), msg, strlen(msg)); + + // write any result data + if (rc == 0) { + LinuxAttachListener::write_fully(this->socket(), (char*) st->base(), st->size()); + ::shutdown(this->socket(), 2); + } + + // done + RESTARTABLE(::close(this->socket()), rc); + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); + + delete this; +} + + +// AttachListener functions + +AttachOperation* AttachListener::dequeue() { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + AttachOperation* op = LinuxAttachListener::dequeue(); + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); + + return op; +} + +int AttachListener::pd_init() { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + int ret_code = LinuxAttachListener::init(); + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); + + return ret_code; +} + +// Attach Listener is started lazily except in the case when +// +ReduseSignalUsage is used +bool AttachListener::init_at_startup() { + if (ReduceSignalUsage) { + return true; + } else { + return false; + } +} + +// If the file .attach_pid exists in the working directory +// or /tmp then this is the trigger to start the attach mechanism +bool AttachListener::is_init_trigger() { + if (init_at_startup() || is_initialized()) { + return false; // initialized at startup or already initialized + } + char fn[32]; + sprintf(fn, ".attach_pid%d", os::current_process_id()); + int ret; + struct stat64 st; + RESTARTABLE(::stat64(fn, &st), ret); + if (ret == -1) { + sprintf(fn, "/tmp/.attach_pid%d", os::current_process_id()); + RESTARTABLE(::stat64(fn, &st), ret); + } + if (ret == 0) { + // simple check to avoid starting the attach mechanism when + // a bogus user creates the file + if (st.st_uid == geteuid()) { + init(); + return true; + } + } + return false; +} + +// if VM aborts then remove listener +void AttachListener::abort() { + listener_cleanup(); +} + +void AttachListener::pd_data_dump() { + os::signal_notify(SIGQUIT); +} + +AttachOperationFunctionInfo* AttachListener::pd_find_operation(const char* n) { + return NULL; +} + +jint AttachListener::pd_set_flag(AttachOperation* op, outputStream* out) { + out->print_cr("flag '%s' cannot be changed", op->arg(0)); + return JNI_ERR; +} + +void AttachListener::pd_detachall() { + // do nothing for now +} diff --git a/hotspot/src/os/linux/vm/c1_globals_linux.hpp b/hotspot/src/os/linux/vm/c1_globals_linux.hpp new file mode 100644 index 00000000000..84dd36c19da --- /dev/null +++ b/hotspot/src/os/linux/vm/c1_globals_linux.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for operating system dependent flags used by the +// client compiler. (see c1_globals.hpp) +// diff --git a/hotspot/src/os/linux/vm/c2_globals_linux.hpp b/hotspot/src/os/linux/vm/c2_globals_linux.hpp new file mode 100644 index 00000000000..4102058ae77 --- /dev/null +++ b/hotspot/src/os/linux/vm/c2_globals_linux.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for operating system dependent flags used by the +// server compiler. (see c2_globals.hpp) +// diff --git a/hotspot/src/os/linux/vm/chaitin_linux.cpp b/hotspot/src/os/linux/vm/chaitin_linux.cpp new file mode 100644 index 00000000000..96c26a12663 --- /dev/null +++ b/hotspot/src/os/linux/vm/chaitin_linux.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_chaitin_linux.cpp.incl" + +void PhaseRegAlloc::pd_preallocate_hook() { + // no action +} + +#ifdef ASSERT +void PhaseRegAlloc::pd_postallocate_verify_hook() { + // no action +} +#endif + + +// Reconciliation History +// chaitin_solaris.cpp 1.7 99/07/12 23:54:22 +// End diff --git a/hotspot/src/os/linux/vm/globals_linux.hpp b/hotspot/src/os/linux/vm/globals_linux.hpp new file mode 100644 index 00000000000..e22b84b0f92 --- /dev/null +++ b/hotspot/src/os/linux/vm/globals_linux.hpp @@ -0,0 +1,42 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Defines Linux specific flags. They are not available on other platforms. +// +#define RUNTIME_OS_FLAGS(develop, develop_pd, product, product_pd, diagnostic, notproduct) \ + product(bool, UseOprofile, false, \ + "enable support for Oprofile profiler") \ + \ + product(bool, UseLinuxPosixThreadCPUClocks, false, \ + "enable fast Linux Posix clocks where available") \ + + +// +// Defines Linux-specific default values. The flags are available on all +// platforms, but they may have different default values on other platforms. +// +define_pd_global(bool, UseLargePages, false); +define_pd_global(bool, UseOSErrorReporting, false); +define_pd_global(bool, UseThreadPriorities, true) ; diff --git a/hotspot/src/os/linux/vm/hpi_linux.cpp b/hotspot/src/os/linux/vm/hpi_linux.cpp new file mode 100644 index 00000000000..b8a0c1e0565 --- /dev/null +++ b/hotspot/src/os/linux/vm/hpi_linux.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_hpi_linux.cpp.incl" + +# include +# include + +typedef jint (JNICALL *init_t)(GetInterfaceFunc *, void *); + +void hpi::initialize_get_interface(vm_calls_t *callbacks) { + char buf[JVM_MAXPATHLEN]; + void *hpi_handle; + GetInterfaceFunc& getintf = _get_interface; + jint (JNICALL * DLL_Initialize)(GetInterfaceFunc *, void *); + + if (HPILibPath && HPILibPath[0]) { + strncpy(buf, HPILibPath, JVM_MAXPATHLEN - 1); + buf[JVM_MAXPATHLEN - 1] = '\0'; + } else { + const char *thread_type = "native_threads"; + + os::jvm_path(buf, JVM_MAXPATHLEN); + +#ifdef PRODUCT + const char * hpi_lib = "/libhpi.so"; +#else + char * ptr = strrchr(buf, '/'); + assert(strstr(ptr, "/libjvm") == ptr, "invalid library name"); + const char * hpi_lib = strstr(ptr, "_g") ? "/libhpi_g.so" : "/libhpi.so"; +#endif + + *(strrchr(buf, '/')) = '\0'; /* get rid of /libjvm.so */ + char* p = strrchr(buf, '/'); + if (p != NULL) p[1] = '\0'; /* get rid of hotspot */ + strcat(buf, thread_type); + strcat(buf, hpi_lib); + } + + if (TraceHPI) tty->print_cr("Loading HPI %s ", buf); +#ifdef SPARC + // On 64-bit Ubuntu Sparc RTLD_NOW leads to unresolved deps in libpthread.so +# define OPEN_MODE RTLD_LAZY +#else + // We use RTLD_NOW because of bug 4032715 +# define OPEN_MODE RTLD_NOW +#endif + hpi_handle = dlopen(buf, OPEN_MODE); +#undef OPEN_MODE + + if (hpi_handle == NULL) { + if (TraceHPI) tty->print_cr("HPI dlopen failed: %s", dlerror()); + return; + } + DLL_Initialize = CAST_TO_FN_PTR(jint (JNICALL *)(GetInterfaceFunc *, void *), + dlsym(hpi_handle, "DLL_Initialize")); + if (TraceHPI && DLL_Initialize == NULL) tty->print_cr("HPI dlsym of DLL_Initialize failed: %s", dlerror()); + if (DLL_Initialize == NULL || + (*DLL_Initialize)(&getintf, callbacks) < 0) { + if (TraceHPI) tty->print_cr("HPI DLL_Initialize failed"); + return; + } + if (TraceHPI) tty->print_cr("HPI loaded successfully"); +} diff --git a/hotspot/src/os/linux/vm/hpi_linux.hpp b/hotspot/src/os/linux/vm/hpi_linux.hpp new file mode 100644 index 00000000000..d14ca94347c --- /dev/null +++ b/hotspot/src/os/linux/vm/hpi_linux.hpp @@ -0,0 +1,220 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Because the interruptible IO has been dropped for HotSpot/Linux, +// the following HPI interface is very different from HotSparc. +// + +#include +#include +#include +#include +#include + +// HPI_FileInterface + +inline int hpi::close(int fd) { + return ::close(fd); +} + +inline size_t hpi::read(int fd, void *buf, unsigned int nBytes) { + size_t res; + RESTARTABLE( (size_t) ::read(fd, buf, (size_t) nBytes), res); + return res; +} + +inline size_t hpi::write(int fd, const void *buf, unsigned int nBytes) { + size_t res; + RESTARTABLE((size_t) ::write(fd, buf, (size_t) nBytes), res); + return res; +} + + +// HPI_SocketInterface + +inline int hpi::socket_close(int fd) { + return ::close(fd); +} + +inline int hpi::socket(int domain, int type, int protocol) { + return ::socket(domain, type, protocol); +} + +inline int hpi::recv(int fd, char *buf, int nBytes, int flags) { + RESTARTABLE_RETURN_INT(::recv(fd, buf, nBytes, (unsigned int) flags)); +} + +inline int hpi::send(int fd, char *buf, int nBytes, int flags) { + RESTARTABLE_RETURN_INT(::send(fd, buf, nBytes, (unsigned int) flags)); +} + +inline int hpi::timeout(int fd, long timeout) { + julong prevtime,newtime; + struct timeval t; + + gettimeofday(&t, NULL); + prevtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; + + for(;;) { + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLIN | POLLERR; + + int res = ::poll(&pfd, 1, timeout); + + if (res == OS_ERR && errno == EINTR) { + + // On Linux any value < 0 means "forever" + + if(timeout >= 0) { + gettimeofday(&t, NULL); + newtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; + timeout -= newtime - prevtime; + if(timeout <= 0) + return OS_OK; + prevtime = newtime; + } + } else + return res; + } +} + +inline int hpi::listen(int fd, int count) { + return ::listen(fd, count); +} + +inline int hpi::connect(int fd, struct sockaddr *him, int len) { + RESTARTABLE_RETURN_INT(::connect(fd, him, len)); +} + +inline int hpi::accept(int fd, struct sockaddr *him, int *len) { + // This cast is from int to unsigned int on linux. Since we + // only pass the parameter "len" around the vm and don't try to + // fetch it's value, this cast is safe for now. The java.net group + // may need and want to change this interface someday if socklen_t goes + // to 64 bits on some platform that we support. + // Linux doc says this can't return EINTR, unlike accept() on Solaris + + return ::accept(fd, him, (socklen_t *)len); +} + +inline int hpi::recvfrom(int fd, char *buf, int nBytes, int flags, + sockaddr *from, int *fromlen) { + RESTARTABLE_RETURN_INT(::recvfrom(fd, buf, nBytes, (unsigned int) flags, from, (socklen_t *)fromlen)); +} + +inline int hpi::sendto(int fd, char *buf, int len, int flags, + struct sockaddr *to, int tolen) { + RESTARTABLE_RETURN_INT(::sendto(fd, buf, len, (unsigned int) flags, to, tolen)); +} + +inline int hpi::socket_available(int fd, jint *pbytes) { + // Linux doc says EINTR not returned, unlike Solaris + int ret = ::ioctl(fd, FIONREAD, pbytes); + + //%% note ioctl can return 0 when successful, JVM_SocketAvailable + // is expected to return 0 on failure and 1 on success to the jdk. + return (ret < 0) ? 0 : 1; +} + + +// following methods have been updated to avoid problems in +// hpi's sockets calls based on sys_api_td.c (JDK1.3) + +/* +HPIDECL(socket_shutdown, "socket_shutdown", _socket, SocketShutdown, + int, "%d", + (int fd, int howto), + ("fd = %d, howto = %d", fd, howto), + (fd, howto)); + */ +inline int hpi::socket_shutdown(int fd, int howto){ + return ::shutdown(fd, howto); +} + +/* +HPIDECL(bind, "bind", _socket, Bind, + int, "%d", + (int fd, struct sockaddr *him, int len), + ("fd = %d, him = %p, len = %d", + fd, him, len), + (fd, him, len)); +*/ +inline int hpi::bind(int fd, struct sockaddr *him, int len){ + return ::bind(fd, him, len); +} + +/* +HPIDECL(get_sock_name, "get_sock_name", _socket, GetSocketName, + int, "%d", + (int fd, struct sockaddr *him, int *len), + ("fd = %d, him = %p, len = %p", + fd, him, len), + (fd, him, len)); + */ +inline int hpi::get_sock_name(int fd, struct sockaddr *him, int *len){ + return ::getsockname(fd, him, (socklen_t *)len); +} + +/* +HPIDECL(get_host_name, "get_host_name", _socket, GetHostName, int, "%d", + (char *hostname, int namelen), + ("hostname = %p, namelen = %d", + hostname, namelen), + (hostname, namelen)); + */ +inline int hpi::get_host_name(char* name, int namelen){ + return ::gethostname(name, namelen); +} + +/* +HPIDECL(get_sock_opt, "get_sock_opt", _socket, SocketGetOption, int, "%d", + (int fd, int level, int optname, char *optval, int* optlen), + ("fd = %d, level = %d, optname = %d, optval = %p, optlen = %p", + fd, level, optname, optval, optlen), + (fd, level, optname, optval, optlen)); + */ +inline int hpi::get_sock_opt(int fd, int level, int optname, + char *optval, int* optlen){ + return ::getsockopt(fd, level, optname, optval, (socklen_t *)optlen); +} + +/* +HPIDECL(set_sock_opt, "set_sock_opt", _socket, SocketSetOption, int, "%d", + (int fd, int level, int optname, const char *optval, int optlen), + ("fd = %d, level = %d, optname = %d, optval = %p, optlen = %d", + fd, level, optname, optval, optlen), + (fd, level, optname, optval, optlen)); + */ +inline int hpi::set_sock_opt(int fd, int level, int optname, + const char *optval, int optlen){ + return ::setsockopt(fd, level, optname, optval, optlen); +} + + +// Reconciliation History +// hpi_solaris.hpp 1.9 99/08/30 16:31:23 +// End diff --git a/hotspot/src/os/linux/vm/interfaceSupport_linux.hpp b/hotspot/src/os/linux/vm/interfaceSupport_linux.hpp new file mode 100644 index 00000000000..bc9a83b9df6 --- /dev/null +++ b/hotspot/src/os/linux/vm/interfaceSupport_linux.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Contains inlined functions for class InterfaceSupport + +static inline void serialize_memory(JavaThread *thread) { + os::write_memory_serialize_page(thread); +} diff --git a/hotspot/src/os/linux/vm/jsig.c b/hotspot/src/os/linux/vm/jsig.c new file mode 100644 index 00000000000..ed344921cd7 --- /dev/null +++ b/hotspot/src/os/linux/vm/jsig.c @@ -0,0 +1,227 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* CopyrightVersion 1.2 */ + +/* This is a special library that should be loaded before libc & + * libthread to interpose the signal handler installation functions: + * sigaction(), signal(), sigset(). + * Used for signal-chaining. See RFE 4381843. + */ + +#include +#include +#include +#include +#include + +#define bool int +#define true 1 +#define false 0 + +#define MAXSIGNUM 32 +#define MASK(sig) ((unsigned int)1 << sig) + +static struct sigaction sact[MAXSIGNUM]; /* saved signal handlers */ +static unsigned int jvmsigs = 0; /* signals used by jvm */ + +/* used to synchronize the installation of signal handlers */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +static pthread_t tid = 0; + +typedef void (*sa_handler_t)(int); +typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); +typedef sa_handler_t (*signal_t)(int, sa_handler_t); +typedef int (*sigaction_t)(int, const struct sigaction *, struct sigaction *); + +static signal_t os_signal = 0; /* os's version of signal()/sigset() */ +static sigaction_t os_sigaction = 0; /* os's version of sigaction() */ + +static bool jvm_signal_installing = false; +static bool jvm_signal_installed = false; + +static void signal_lock() { + pthread_mutex_lock(&mutex); + /* When the jvm is installing its set of signal handlers, threads + * other than the jvm thread should wait */ + if (jvm_signal_installing) { + if (tid != pthread_self()) { + pthread_cond_wait(&cond, &mutex); + } + } +} + +static void signal_unlock() { + pthread_mutex_unlock(&mutex); +} + +static sa_handler_t call_os_signal(int sig, sa_handler_t disp, + bool is_sigset) { + if (os_signal == NULL) { + if (!is_sigset) { + os_signal = (signal_t)dlsym(RTLD_NEXT, "signal"); + } else { + os_signal = (signal_t)dlsym(RTLD_NEXT, "sigset"); + } + if (os_signal == NULL) { + printf("%s\n", dlerror()); + exit(0); + } + } + return (*os_signal)(sig, disp); +} + +static void save_signal_handler(int sig, sa_handler_t disp) { + sigset_t set; + sact[sig].sa_handler = disp; + sigemptyset(&set); + sact[sig].sa_mask = set; + sact[sig].sa_flags = 0; +} + +static sa_handler_t set_signal(int sig, sa_handler_t disp, bool is_sigset) { + sa_handler_t oldhandler; + bool sigused; + + signal_lock(); + + sigused = (MASK(sig) & jvmsigs) != 0; + if (jvm_signal_installed && sigused) { + /* jvm has installed its signal handler for this signal. */ + /* Save the handler. Don't really install it. */ + oldhandler = sact[sig].sa_handler; + save_signal_handler(sig, disp); + + signal_unlock(); + return oldhandler; + } else if (jvm_signal_installing) { + /* jvm is installing its signal handlers. Install the new + * handlers and save the old ones. jvm uses sigaction(). + * Leave the piece here just in case. */ + oldhandler = call_os_signal(sig, disp, is_sigset); + save_signal_handler(sig, oldhandler); + + /* Record the signals used by jvm */ + jvmsigs |= MASK(sig); + + signal_unlock(); + return oldhandler; + } else { + /* jvm has no relation with this signal (yet). Install the + * the handler. */ + oldhandler = call_os_signal(sig, disp, is_sigset); + + signal_unlock(); + return oldhandler; + } +} + +sa_handler_t signal(int sig, sa_handler_t disp) { + return set_signal(sig, disp, false); +} + +sa_handler_t sigset(int sig, sa_handler_t disp) { + return set_signal(sig, disp, true); + } + +static int call_os_sigaction(int sig, const struct sigaction *act, + struct sigaction *oact) { + if (os_sigaction == NULL) { + os_sigaction = (sigaction_t)dlsym(RTLD_NEXT, "sigaction"); + if (os_sigaction == NULL) { + printf("%s\n", dlerror()); + exit(0); + } + } + return (*os_sigaction)(sig, act, oact); +} + +int sigaction(int sig, const struct sigaction *act, struct sigaction *oact) { + int res; + bool sigused; + struct sigaction oldAct; + + signal_lock(); + + sigused = (MASK(sig) & jvmsigs) != 0; + if (jvm_signal_installed && sigused) { + /* jvm has installed its signal handler for this signal. */ + /* Save the handler. Don't really install it. */ + if (oact != NULL) { + *oact = sact[sig]; + } + if (act != NULL) { + sact[sig] = *act; + } + + signal_unlock(); + return 0; + } else if (jvm_signal_installing) { + /* jvm is installing its signal handlers. Install the new + * handlers and save the old ones. */ + res = call_os_sigaction(sig, act, &oldAct); + sact[sig] = oldAct; + if (oact != NULL) { + *oact = oldAct; + } + + /* Record the signals used by jvm */ + jvmsigs |= MASK(sig); + + signal_unlock(); + return res; + } else { + /* jvm has no relation with this signal (yet). Install the + * the handler. */ + res = call_os_sigaction(sig, act, oact); + + signal_unlock(); + return res; + } +} + +/* The three functions for the jvm to call into */ +void JVM_begin_signal_setting() { + signal_lock(); + jvm_signal_installing = true; + tid = pthread_self(); + signal_unlock(); +} + +void JVM_end_signal_setting() { + signal_lock(); + jvm_signal_installed = true; + jvm_signal_installing = false; + pthread_cond_broadcast(&cond); + signal_unlock(); +} + +struct sigaction *JVM_get_signal_action(int sig) { + /* Does race condition make sense here? */ + if ((MASK(sig) & jvmsigs) != 0) { + return &sact[sig]; + } + return NULL; +} diff --git a/hotspot/src/os/linux/vm/jvm_linux.cpp b/hotspot/src/os/linux/vm/jvm_linux.cpp new file mode 100644 index 00000000000..dc4d2859c6a --- /dev/null +++ b/hotspot/src/os/linux/vm/jvm_linux.cpp @@ -0,0 +1,202 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_jvm_linux.cpp.incl" + +#include + +/* + * FIXME: This is temporary hack to keep Linux Runtime.exec() + * code happy. See $JDK/src/linux/native/java/lang/UnixProcess_md.c + */ +int _JVM_native_threads = 1; + +// sun.misc.Signal /////////////////////////////////////////////////////////// +// Signal code is mostly copied from classic vm, signals_md.c 1.4 98/08/23 +/* + * This function is included primarily as a debugging aid. If Java is + * running in a console window, then pressing will cause + * the current state of all active threads and monitors to be written + * to the console window. + */ + +JVM_ENTRY_NO_ENV(void*, JVM_RegisterSignal(jint sig, void* handler)) + // Copied from classic vm + // signals_md.c 1.4 98/08/23 + void* newHandler = handler == (void *)2 + ? os::user_handler() + : handler; + switch (sig) { + /* The following are already used by the VM. */ + case INTERRUPT_SIGNAL: + case SIGFPE: + case SIGILL: + case SIGSEGV: + + /* The following signal is used by the VM to dump thread stacks unless + ReduceSignalUsage is set, in which case the user is allowed to set + his own _native_ handler for this signal; thus, in either case, + we do not allow JVM_RegisterSignal to change the handler. */ + case BREAK_SIGNAL: + return (void *)-1; + + /* The following signals are used for Shutdown Hooks support. However, if + ReduceSignalUsage (-Xrs) is set, Shutdown Hooks must be invoked via + System.exit(), Java is not allowed to use these signals, and the the + user is allowed to set his own _native_ handler for these signals and + invoke System.exit() as needed. Terminator.setup() is avoiding + registration of these signals when -Xrs is present. + - If the HUP signal is ignored (from the nohup) command, then Java + is not allowed to use this signal. + */ + + case SHUTDOWN1_SIGNAL: + case SHUTDOWN2_SIGNAL: + case SHUTDOWN3_SIGNAL: + if (ReduceSignalUsage) return (void*)-1; + if (os::Linux::is_sig_ignored(sig)) return (void*)1; + } + + void* oldHandler = os::signal(sig, newHandler); + if (oldHandler == os::user_handler()) { + return (void *)2; + } else { + return oldHandler; + } +JVM_END + + +JVM_ENTRY_NO_ENV(jboolean, JVM_RaiseSignal(jint sig)) + if (ReduceSignalUsage) { + // do not allow SHUTDOWN1_SIGNAL,SHUTDOWN2_SIGNAL,SHUTDOWN3_SIGNAL, + // BREAK_SIGNAL to be raised when ReduceSignalUsage is set, since + // no handler for them is actually registered in JVM or via + // JVM_RegisterSignal. + if (sig == SHUTDOWN1_SIGNAL || sig == SHUTDOWN2_SIGNAL || + sig == SHUTDOWN3_SIGNAL || sig == BREAK_SIGNAL) { + return JNI_FALSE; + } + } + else if ((sig == SHUTDOWN1_SIGNAL || sig == SHUTDOWN2_SIGNAL || + sig == SHUTDOWN3_SIGNAL) && os::Linux::is_sig_ignored(sig)) { + // do not allow SHUTDOWN1_SIGNAL to be raised when SHUTDOWN1_SIGNAL + // is ignored, since no handler for them is actually registered in JVM + // or via JVM_RegisterSignal. + // This also applies for SHUTDOWN2_SIGNAL and SHUTDOWN3_SIGNAL + return JNI_FALSE; + } + + os::signal_raise(sig); + return JNI_TRUE; +JVM_END + +/* + All the defined signal names for Linux. + + NOTE that not all of these names are accepted by our Java implementation + + Via an existing claim by the VM, sigaction restrictions, or + the "rules of Unix" some of these names will be rejected at runtime. + For example the VM sets up to handle USR1, sigaction returns EINVAL for + STOP, and Linux simply doesn't allow catching of KILL. + + Here are the names currently accepted by a user of sun.misc.Signal with + 1.4.1 (ignoring potential interaction with use of chaining, etc): + + HUP, INT, TRAP, ABRT, IOT, BUS, USR2, PIPE, ALRM, TERM, STKFLT, + CLD, CHLD, CONT, TSTP, TTIN, TTOU, URG, XCPU, XFSZ, VTALRM, PROF, + WINCH, POLL, IO, PWR, SYS + +*/ + +struct siglabel { + char *name; + int number; +}; + +struct siglabel siglabels[] = { + /* derived from /usr/include/bits/signum.h on RH7.2 */ + "HUP", SIGHUP, /* Hangup (POSIX). */ + "INT", SIGINT, /* Interrupt (ANSI). */ + "QUIT", SIGQUIT, /* Quit (POSIX). */ + "ILL", SIGILL, /* Illegal instruction (ANSI). */ + "TRAP", SIGTRAP, /* Trace trap (POSIX). */ + "ABRT", SIGABRT, /* Abort (ANSI). */ + "IOT", SIGIOT, /* IOT trap (4.2 BSD). */ + "BUS", SIGBUS, /* BUS error (4.2 BSD). */ + "FPE", SIGFPE, /* Floating-point exception (ANSI). */ + "KILL", SIGKILL, /* Kill, unblockable (POSIX). */ + "USR1", SIGUSR1, /* User-defined signal 1 (POSIX). */ + "SEGV", SIGSEGV, /* Segmentation violation (ANSI). */ + "USR2", SIGUSR2, /* User-defined signal 2 (POSIX). */ + "PIPE", SIGPIPE, /* Broken pipe (POSIX). */ + "ALRM", SIGALRM, /* Alarm clock (POSIX). */ + "TERM", SIGTERM, /* Termination (ANSI). */ +#ifdef SIGSTKFLT + "STKFLT", SIGSTKFLT, /* Stack fault. */ +#endif + "CLD", SIGCLD, /* Same as SIGCHLD (System V). */ + "CHLD", SIGCHLD, /* Child status has changed (POSIX). */ + "CONT", SIGCONT, /* Continue (POSIX). */ + "STOP", SIGSTOP, /* Stop, unblockable (POSIX). */ + "TSTP", SIGTSTP, /* Keyboard stop (POSIX). */ + "TTIN", SIGTTIN, /* Background read from tty (POSIX). */ + "TTOU", SIGTTOU, /* Background write to tty (POSIX). */ + "URG", SIGURG, /* Urgent condition on socket (4.2 BSD). */ + "XCPU", SIGXCPU, /* CPU limit exceeded (4.2 BSD). */ + "XFSZ", SIGXFSZ, /* File size limit exceeded (4.2 BSD). */ + "VTALRM", SIGVTALRM, /* Virtual alarm clock (4.2 BSD). */ + "PROF", SIGPROF, /* Profiling alarm clock (4.2 BSD). */ + "WINCH", SIGWINCH, /* Window size change (4.3 BSD, Sun). */ + "POLL", SIGPOLL, /* Pollable event occurred (System V). */ + "IO", SIGIO, /* I/O now possible (4.2 BSD). */ + "PWR", SIGPWR, /* Power failure restart (System V). */ +#ifdef SIGSYS + "SYS", SIGSYS /* Bad system call. Only on some Linuxen! */ +#endif + }; + +JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) + + /* find and return the named signal's number */ + + for(uint i=0; i /* For DIR */ +#include /* For MAXPATHLEN */ +#include /* For F_OK, R_OK, W_OK */ + +#define JNI_ONLOAD_SYMBOLS {"JNI_OnLoad"} +#define JNI_ONUNLOAD_SYMBOLS {"JNI_OnUnload"} +#define JVM_ONLOAD_SYMBOLS {"JVM_OnLoad"} +#define AGENT_ONLOAD_SYMBOLS {"Agent_OnLoad"} +#define AGENT_ONUNLOAD_SYMBOLS {"Agent_OnUnload"} +#define AGENT_ONATTACH_SYMBOLS {"Agent_OnAttach"} + +#define JNI_LIB_PREFIX "lib" +#define JNI_LIB_SUFFIX ".so" + +// Hack: MAXPATHLEN is 4095 on some Linux and 4096 on others. This may +// cause problems if JVM and the rest of JDK are built on different +// Linux releases. Here we define JVM_MAXPATHLEN to be MAXPATHLEN + 1, +// so buffers declared in VM are always >= 4096. +#define JVM_MAXPATHLEN MAXPATHLEN + 1 + +#define JVM_R_OK R_OK +#define JVM_W_OK W_OK +#define JVM_X_OK X_OK +#define JVM_F_OK F_OK + +/* + * File I/O + */ + +#include +#include +#include +#include + +/* O Flags */ + +#define JVM_O_RDONLY O_RDONLY +#define JVM_O_WRONLY O_WRONLY +#define JVM_O_RDWR O_RDWR +#define JVM_O_O_APPEND O_APPEND +#define JVM_O_EXCL O_EXCL +#define JVM_O_CREAT O_CREAT + +/* Signal definitions */ + +#define BREAK_SIGNAL SIGQUIT /* Thread dumping support. */ +#define INTERRUPT_SIGNAL SIGUSR1 /* Interruptible I/O support. */ +#define SHUTDOWN1_SIGNAL SIGHUP /* Shutdown Hooks support. */ +#define SHUTDOWN2_SIGNAL SIGINT +#define SHUTDOWN3_SIGNAL SIGTERM + +#endif /* JVM_MD_H */ + +// Reconciliation History +// jvm_solaris.h 1.6 99/06/22 16:38:47 +// End diff --git a/hotspot/src/os/linux/vm/mutex_linux.cpp b/hotspot/src/os/linux/vm/mutex_linux.cpp new file mode 100644 index 00000000000..cd9a8a764cf --- /dev/null +++ b/hotspot/src/os/linux/vm/mutex_linux.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_mutex_linux.cpp.incl" + +// put OS-includes here +# include diff --git a/hotspot/src/os/linux/vm/mutex_linux.inline.hpp b/hotspot/src/os/linux/vm/mutex_linux.inline.hpp new file mode 100644 index 00000000000..291159bb4a2 --- /dev/null +++ b/hotspot/src/os/linux/vm/mutex_linux.inline.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 1999-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +// Reconciliation History +// mutex_solaris.inline.hpp 1.5 99/06/22 16:38:49 +// End diff --git a/hotspot/src/os/linux/vm/objectMonitor_linux.cpp b/hotspot/src/os/linux/vm/objectMonitor_linux.cpp new file mode 100644 index 00000000000..c4c22fbe39a --- /dev/null +++ b/hotspot/src/os/linux/vm/objectMonitor_linux.cpp @@ -0,0 +1,24 @@ + +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ diff --git a/hotspot/src/os/linux/vm/objectMonitor_linux.hpp b/hotspot/src/os/linux/vm/objectMonitor_linux.hpp new file mode 100644 index 00000000000..f8620a31210 --- /dev/null +++ b/hotspot/src/os/linux/vm/objectMonitor_linux.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: diff --git a/hotspot/src/os/linux/vm/objectMonitor_linux.inline.hpp b/hotspot/src/os/linux/vm/objectMonitor_linux.inline.hpp new file mode 100644 index 00000000000..760fc57d6a8 --- /dev/null +++ b/hotspot/src/os/linux/vm/objectMonitor_linux.inline.hpp @@ -0,0 +1,23 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ diff --git a/hotspot/src/os/linux/vm/osThread_linux.cpp b/hotspot/src/os/linux/vm/osThread_linux.cpp new file mode 100644 index 00000000000..a6454604327 --- /dev/null +++ b/hotspot/src/os/linux/vm/osThread_linux.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// do not include precompiled header file +# include "incls/_osThread_linux.cpp.incl" + + +void OSThread::pd_initialize() { + assert(this != NULL, "check"); + _thread_id = 0; + _pthread_id = 0; + _siginfo = NULL; + _ucontext = NULL; + _expanding_stack = 0; + _alt_sig_stack = NULL; + + sigemptyset(&_caller_sigmask); + + _startThread_lock = new Monitor(Mutex::event, "startThread_lock", true); + assert(_startThread_lock !=NULL, "check"); +} + +void OSThread::pd_destroy() { + delete _startThread_lock; +} diff --git a/hotspot/src/os/linux/vm/osThread_linux.hpp b/hotspot/src/os/linux/vm/osThread_linux.hpp new file mode 100644 index 00000000000..812901ea3c8 --- /dev/null +++ b/hotspot/src/os/linux/vm/osThread_linux.hpp @@ -0,0 +1,141 @@ +/* + * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: + int _thread_type; + + public: + + int thread_type() const { + return _thread_type; + } + void set_thread_type(int type) { + _thread_type = type; + } + + private: + + // _thread_id is kernel thread id (similar to LWP id on Solaris). Each + // thread has a unique thread_id (LinuxThreads or NPTL). It can be used + // to access /proc. + pid_t _thread_id; + + // _pthread_id is the pthread id, which is used by library calls + // (e.g. pthread_kill). + pthread_t _pthread_id; + + sigset_t _caller_sigmask; // Caller's signal mask + + public: + + // Methods to save/restore caller's signal mask + sigset_t caller_sigmask() const { return _caller_sigmask; } + void set_caller_sigmask(sigset_t sigmask) { _caller_sigmask = sigmask; } + + pid_t thread_id() const { + return _thread_id; + } +#ifndef PRODUCT + // Used for debugging, return a unique integer for each thread. + int thread_identifier() const { return _thread_id; } +#endif +#ifdef ASSERT + // We expect no reposition failures so kill vm if we get one. + // + bool valid_reposition_failure() { + return false; + } +#endif // ASSERT + void set_thread_id(pid_t id) { + _thread_id = id; + } + pthread_t pthread_id() const { + return _pthread_id; + } + void set_pthread_id(pthread_t tid) { + _pthread_id = tid; + } + + // *************************************************************** + // suspension support. + // *************************************************************** + +public: + // flags that support signal based suspend/resume on Linux are in a + // separate class to avoid confusion with many flags in OSThread that + // are used by VM level suspend/resume. + os::Linux::SuspendResume sr; + + // _ucontext and _siginfo are used by SR_handler() to save thread context, + // and they will later be used to walk the stack or reposition thread PC. + // If the thread is not suspended in SR_handler() (e.g. self suspend), + // the value in _ucontext is meaningless, so we must use the last Java + // frame information as the frame. This will mean that for threads + // that are parked on a mutex the profiler (and safepoint mechanism) + // will see the thread as if it were still in the Java frame. This + // not a problem for the profiler since the Java frame is a close + // enough result. For the safepoint mechanism when the give it the + // Java frame we are not at a point where the safepoint needs the + // frame to that accurate (like for a compiled safepoint) since we + // should be in a place where we are native and will block ourselves + // if we transition. +private: + void* _siginfo; + ucontext_t* _ucontext; + int _expanding_stack; /* non zero if manually expanding stack */ + address _alt_sig_stack; /* address of base of alternate signal stack */ + +public: + void* siginfo() const { return _siginfo; } + void set_siginfo(void* ptr) { _siginfo = ptr; } + ucontext_t* ucontext() const { return _ucontext; } + void set_ucontext(ucontext_t* ptr) { _ucontext = ptr; } + void set_expanding_stack(void) { _expanding_stack = 1; } + void clear_expanding_stack(void) { _expanding_stack = 0; } + int expanding_stack(void) { return _expanding_stack; } + + void set_alt_sig_stack(address val) { _alt_sig_stack = val; } + address alt_sig_stack(void) { return _alt_sig_stack; } + +private: + Monitor* _startThread_lock; // sync parent and child in thread creation + +public: + + Monitor* startThread_lock() const { + return _startThread_lock; + } + + // *************************************************************** + // Platform dependent initialization and cleanup + // *************************************************************** + +private: + + void pd_initialize(); + void pd_destroy(); + +// Reconciliation History +// osThread_solaris.hpp 1.24 99/08/27 13:11:54 +// End diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp new file mode 100644 index 00000000000..a327fd91592 --- /dev/null +++ b/hotspot/src/os/linux/vm/os_linux.cpp @@ -0,0 +1,4594 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// do not include precompiled header file +# include "incls/_os_linux.cpp.incl" + +// put OS-includes here +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +#define MAX_PATH (2 * K) + +// for timer info max values which include all bits +#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) +#define SEC_IN_NANOSECS 1000000000LL + +//////////////////////////////////////////////////////////////////////////////// +// global variables +julong os::Linux::_physical_memory = 0; + +address os::Linux::_initial_thread_stack_bottom = NULL; +uintptr_t os::Linux::_initial_thread_stack_size = 0; + +int (*os::Linux::_clock_gettime)(clockid_t, struct timespec *) = NULL; +int (*os::Linux::_pthread_getcpuclockid)(pthread_t, clockid_t *) = NULL; +Mutex* os::Linux::_createThread_lock = NULL; +pthread_t os::Linux::_main_thread; +int os::Linux::_page_size = -1; +bool os::Linux::_is_floating_stack = false; +bool os::Linux::_is_NPTL = false; +bool os::Linux::_supports_fast_thread_cpu_time = false; +char * os::Linux::_glibc_version = NULL; +char * os::Linux::_libpthread_version = NULL; + +static jlong initial_time_count=0; + +static int clock_tics_per_sec = 100; + +// For diagnostics to print a message once. see run_periodic_checks +static sigset_t check_signal_done; +static bool check_signals = true;; + +static pid_t _initial_pid = 0; + +/* Signal number used to suspend/resume a thread */ + +/* do not use any signal number less than SIGSEGV, see 4355769 */ +static int SR_signum = SIGUSR2; +sigset_t SR_sigset; + +//////////////////////////////////////////////////////////////////////////////// +// utility functions + +static int SR_initialize(); +static int SR_finalize(); + +julong os::available_memory() { + return Linux::available_memory(); +} + +julong os::Linux::available_memory() { + // values in struct sysinfo are "unsigned long" + struct sysinfo si; + sysinfo(&si); + + return (julong)si.freeram * si.mem_unit; +} + +julong os::physical_memory() { + return Linux::physical_memory(); +} + +//////////////////////////////////////////////////////////////////////////////// +// environment support + +bool os::getenv(const char* name, char* buf, int len) { + const char* val = ::getenv(name); + if (val != NULL && strlen(val) < (size_t)len) { + strcpy(buf, val); + return true; + } + if (len > 0) buf[0] = 0; // return a null string + return false; +} + + +// Return true if user is running as root. + +bool os::have_special_privileges() { + static bool init = false; + static bool privileges = false; + if (!init) { + privileges = (getuid() != geteuid()) || (getgid() != getegid()); + init = true; + } + return privileges; +} + + +#ifndef SYS_gettid +// i386: 224, ia64: 1105, amd64: 186, sparc 143 +#ifdef __ia64__ +#define SYS_gettid 1105 +#elif __i386__ +#define SYS_gettid 224 +#elif __amd64__ +#define SYS_gettid 186 +#elif __sparc__ +#define SYS_gettid 143 +#else +#error define gettid for the arch +#endif +#endif + +// Cpu architecture string +#if defined(IA64) +static char cpu_arch[] = "ia64"; +#elif defined(IA32) +static char cpu_arch[] = "i386"; +#elif defined(AMD64) +static char cpu_arch[] = "amd64"; +#elif defined(SPARC) +# ifdef _LP64 +static char cpu_arch[] = "sparcv9"; +# else +static char cpu_arch[] = "sparc"; +# endif +#else +#error Add appropriate cpu_arch setting +#endif + + +// pid_t gettid() +// +// Returns the kernel thread id of the currently running thread. Kernel +// thread id is used to access /proc. +// +// (Note that getpid() on LinuxThreads returns kernel thread id too; but +// on NPTL, it returns the same pid for all threads, as required by POSIX.) +// +pid_t os::Linux::gettid() { + int rslt = syscall(SYS_gettid); + if (rslt == -1) { + // old kernel, no NPTL support + return getpid(); + } else { + return (pid_t)rslt; + } +} + +// Most versions of linux have a bug where the number of processors are +// determined by looking at the /proc file system. In a chroot environment, +// the system call returns 1. This causes the VM to act as if it is +// a single processor and elide locking (see is_MP() call). +static bool unsafe_chroot_detected = false; +static char *unstable_chroot_error = "/proc file system not found.\n" + "Java may be unstable running multithreaded in a chroot " + "environment on Linux when /proc filesystem is not mounted."; + +void os::Linux::initialize_system_info() { + _processor_count = sysconf(_SC_NPROCESSORS_CONF); + if (_processor_count == 1) { + pid_t pid = os::Linux::gettid(); + char fname[32]; + jio_snprintf(fname, sizeof(fname), "/proc/%d", pid); + FILE *fp = fopen(fname, "r"); + if (fp == NULL) { + unsafe_chroot_detected = true; + } else { + fclose(fp); + } + } + _physical_memory = (julong)sysconf(_SC_PHYS_PAGES) * (julong)sysconf(_SC_PAGESIZE); + assert(_processor_count > 0, "linux error"); +} + +void os::init_system_properties_values() { +// char arch[12]; +// sysinfo(SI_ARCHITECTURE, arch, sizeof(arch)); + + // The next steps are taken in the product version: + // + // Obtain the JAVA_HOME value from the location of libjvm[_g].so. + // This library should be located at: + // /jre/lib//{client|server}/libjvm[_g].so. + // + // If "/jre/lib/" appears at the right place in the path, then we + // assume libjvm[_g].so is installed in a JDK and we use this path. + // + // Otherwise exit with message: "Could not create the Java virtual machine." + // + // The following extra steps are taken in the debugging version: + // + // If "/jre/lib/" does NOT appear at the right place in the path + // instead of exit check for $JAVA_HOME environment variable. + // + // If it is defined and we are able to locate $JAVA_HOME/jre/lib/, + // then we append a fake suffix "hotspot/libjvm[_g].so" to this path so + // it looks like libjvm[_g].so is installed there + // /jre/lib//hotspot/libjvm[_g].so. + // + // Otherwise exit. + // + // Important note: if the location of libjvm.so changes this + // code needs to be changed accordingly. + + // The next few definitions allow the code to be verbatim: +#define malloc(n) (char*)NEW_C_HEAP_ARRAY(char, (n)) +#define getenv(n) ::getenv(n) + +/* + * See ld(1): + * The linker uses the following search paths to locate required + * shared libraries: + * 1: ... + * ... + * 7: The default directories, normally /lib and /usr/lib. + */ +#define DEFAULT_LIBPATH "/lib:/usr/lib" + +#define EXTENSIONS_DIR "/lib/ext" +#define ENDORSED_DIR "/lib/endorsed" +#define REG_DIR "/usr/java/packages" + + { + /* sysclasspath, java_home, dll_dir */ + { + char *home_path; + char *dll_path; + char *pslash; + char buf[MAXPATHLEN]; + os::jvm_path(buf, sizeof(buf)); + + // Found the full path to libjvm.so. + // Now cut the path to /jre if we can. + *(strrchr(buf, '/')) = '\0'; /* get rid of /libjvm.so */ + pslash = strrchr(buf, '/'); + if (pslash != NULL) + *pslash = '\0'; /* get rid of /{client|server|hotspot} */ + dll_path = malloc(strlen(buf) + 1); + if (dll_path == NULL) + return; + strcpy(dll_path, buf); + Arguments::set_dll_dir(dll_path); + + if (pslash != NULL) { + pslash = strrchr(buf, '/'); + if (pslash != NULL) { + *pslash = '\0'; /* get rid of / */ + pslash = strrchr(buf, '/'); + if (pslash != NULL) + *pslash = '\0'; /* get rid of /lib */ + } + } + + home_path = malloc(strlen(buf) + 1); + if (home_path == NULL) + return; + strcpy(home_path, buf); + Arguments::set_java_home(home_path); + + if (!set_boot_path('/', ':')) + return; + } + + /* + * Where to look for native libraries + * + * Note: Due to a legacy implementation, most of the library path + * is set in the launcher. This was to accomodate linking restrictions + * on legacy Linux implementations (which are no longer supported). + * Eventually, all the library path setting will be done here. + * + * However, to prevent the proliferation of improperly built native + * libraries, the new path component /usr/java/packages is added here. + * Eventually, all the library path setting will be done here. + */ + { + char *ld_library_path; + + /* + * Construct the invariant part of ld_library_path. Note that the + * space for the colon and the trailing null are provided by the + * nulls included by the sizeof operator (so actually we allocate + * a byte more than necessary). + */ + ld_library_path = (char *) malloc(sizeof(REG_DIR) + sizeof("/lib/") + + strlen(cpu_arch) + sizeof(DEFAULT_LIBPATH)); + sprintf(ld_library_path, REG_DIR "/lib/%s:" DEFAULT_LIBPATH, cpu_arch); + + /* + * Get the user setting of LD_LIBRARY_PATH, and prepended it. It + * should always exist (until the legacy problem cited above is + * addressed). + */ + char *v = getenv("LD_LIBRARY_PATH"); + if (v != NULL) { + char *t = ld_library_path; + /* That's +1 for the colon and +1 for the trailing '\0' */ + ld_library_path = (char *) malloc(strlen(v) + 1 + strlen(t) + 1); + sprintf(ld_library_path, "%s:%s", v, t); + } + Arguments::set_library_path(ld_library_path); + } + + /* + * Extensions directories. + * + * Note that the space for the colon and the trailing null are provided + * by the nulls included by the sizeof operator (so actually one byte more + * than necessary is allocated). + */ + { + char *buf = malloc(strlen(Arguments::get_java_home()) + + sizeof(EXTENSIONS_DIR) + sizeof(REG_DIR) + sizeof(EXTENSIONS_DIR)); + sprintf(buf, "%s" EXTENSIONS_DIR ":" REG_DIR EXTENSIONS_DIR, + Arguments::get_java_home()); + Arguments::set_ext_dirs(buf); + } + + /* Endorsed standards default directory. */ + { + char * buf; + buf = malloc(strlen(Arguments::get_java_home()) + sizeof(ENDORSED_DIR)); + sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home()); + Arguments::set_endorsed_dirs(buf); + } + } + +#undef malloc +#undef getenv +#undef EXTENSIONS_DIR +#undef ENDORSED_DIR + + // Done + return; +} + +//////////////////////////////////////////////////////////////////////////////// +// breakpoint support + +void os::breakpoint() { + BREAKPOINT; +} + +extern "C" void breakpoint() { + // use debugger to set breakpoint here +} + +//////////////////////////////////////////////////////////////////////////////// +// signal support + +debug_only(static bool signal_sets_initialized = false); +static sigset_t unblocked_sigs, vm_sigs, allowdebug_blocked_sigs; + +bool os::Linux::is_sig_ignored(int sig) { + struct sigaction oact; + sigaction(sig, (struct sigaction*)NULL, &oact); + void* ohlr = oact.sa_sigaction ? CAST_FROM_FN_PTR(void*, oact.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oact.sa_handler); + if (ohlr == CAST_FROM_FN_PTR(void*, SIG_IGN)) + return true; + else + return false; +} + +void os::Linux::signal_sets_init() { + // Should also have an assertion stating we are still single-threaded. + assert(!signal_sets_initialized, "Already initialized"); + // Fill in signals that are necessarily unblocked for all threads in + // the VM. Currently, we unblock the following signals: + // SHUTDOWN{1,2,3}_SIGNAL: for shutdown hooks support (unless over-ridden + // by -Xrs (=ReduceSignalUsage)); + // BREAK_SIGNAL which is unblocked only by the VM thread and blocked by all + // other threads. The "ReduceSignalUsage" boolean tells us not to alter + // the dispositions or masks wrt these signals. + // Programs embedding the VM that want to use the above signals for their + // own purposes must, at this time, use the "-Xrs" option to prevent + // interference with shutdown hooks and BREAK_SIGNAL thread dumping. + // (See bug 4345157, and other related bugs). + // In reality, though, unblocking these signals is really a nop, since + // these signals are not blocked by default. + sigemptyset(&unblocked_sigs); + sigemptyset(&allowdebug_blocked_sigs); + sigaddset(&unblocked_sigs, SIGILL); + sigaddset(&unblocked_sigs, SIGSEGV); + sigaddset(&unblocked_sigs, SIGBUS); + sigaddset(&unblocked_sigs, SIGFPE); + sigaddset(&unblocked_sigs, SR_signum); + + if (!ReduceSignalUsage) { + if (!os::Linux::is_sig_ignored(SHUTDOWN1_SIGNAL)) { + sigaddset(&unblocked_sigs, SHUTDOWN1_SIGNAL); + sigaddset(&allowdebug_blocked_sigs, SHUTDOWN1_SIGNAL); + } + if (!os::Linux::is_sig_ignored(SHUTDOWN2_SIGNAL)) { + sigaddset(&unblocked_sigs, SHUTDOWN2_SIGNAL); + sigaddset(&allowdebug_blocked_sigs, SHUTDOWN2_SIGNAL); + } + if (!os::Linux::is_sig_ignored(SHUTDOWN3_SIGNAL)) { + sigaddset(&unblocked_sigs, SHUTDOWN3_SIGNAL); + sigaddset(&allowdebug_blocked_sigs, SHUTDOWN3_SIGNAL); + } + } + // Fill in signals that are blocked by all but the VM thread. + sigemptyset(&vm_sigs); + if (!ReduceSignalUsage) + sigaddset(&vm_sigs, BREAK_SIGNAL); + debug_only(signal_sets_initialized = true); + +} + +// These are signals that are unblocked while a thread is running Java. +// (For some reason, they get blocked by default.) +sigset_t* os::Linux::unblocked_signals() { + assert(signal_sets_initialized, "Not initialized"); + return &unblocked_sigs; +} + +// These are the signals that are blocked while a (non-VM) thread is +// running Java. Only the VM thread handles these signals. +sigset_t* os::Linux::vm_signals() { + assert(signal_sets_initialized, "Not initialized"); + return &vm_sigs; +} + +// These are signals that are blocked during cond_wait to allow debugger in +sigset_t* os::Linux::allowdebug_blocked_signals() { + assert(signal_sets_initialized, "Not initialized"); + return &allowdebug_blocked_sigs; +} + +void os::Linux::hotspot_sigmask(Thread* thread) { + + //Save caller's signal mask before setting VM signal mask + sigset_t caller_sigmask; + pthread_sigmask(SIG_BLOCK, NULL, &caller_sigmask); + + OSThread* osthread = thread->osthread(); + osthread->set_caller_sigmask(caller_sigmask); + + pthread_sigmask(SIG_UNBLOCK, os::Linux::unblocked_signals(), NULL); + + if (!ReduceSignalUsage) { + if (thread->is_VM_thread()) { + // Only the VM thread handles BREAK_SIGNAL ... + pthread_sigmask(SIG_UNBLOCK, vm_signals(), NULL); + } else { + // ... all other threads block BREAK_SIGNAL + pthread_sigmask(SIG_BLOCK, vm_signals(), NULL); + } + } +} + +////////////////////////////////////////////////////////////////////////////// +// detecting pthread library + +void os::Linux::libpthread_init() { + // Save glibc and pthread version strings. Note that _CS_GNU_LIBC_VERSION + // and _CS_GNU_LIBPTHREAD_VERSION are supported in glibc >= 2.3.2. Use a + // generic name for earlier versions. + // Define macros here so we can build HotSpot on old systems. +# ifndef _CS_GNU_LIBC_VERSION +# define _CS_GNU_LIBC_VERSION 2 +# endif +# ifndef _CS_GNU_LIBPTHREAD_VERSION +# define _CS_GNU_LIBPTHREAD_VERSION 3 +# endif + + size_t n = confstr(_CS_GNU_LIBC_VERSION, NULL, 0); + if (n > 0) { + char *str = (char *)malloc(n); + confstr(_CS_GNU_LIBC_VERSION, str, n); + os::Linux::set_glibc_version(str); + } else { + // _CS_GNU_LIBC_VERSION is not supported, try gnu_get_libc_version() + static char _gnu_libc_version[32]; + jio_snprintf(_gnu_libc_version, sizeof(_gnu_libc_version), + "glibc %s %s", gnu_get_libc_version(), gnu_get_libc_release()); + os::Linux::set_glibc_version(_gnu_libc_version); + } + + n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, 0); + if (n > 0) { + char *str = (char *)malloc(n); + confstr(_CS_GNU_LIBPTHREAD_VERSION, str, n); + + // Vanilla RH-9 (glibc 2.3.2) has a bug that confstr() always tells + // us "NPTL-0.29" even we are running with LinuxThreads. Check if this + // is the case: + if (strcmp(os::Linux::glibc_version(), "glibc 2.3.2") == 0 && + strstr(str, "NPTL")) { + // LinuxThreads has a hard limit on max number of threads. So + // sysconf(_SC_THREAD_THREADS_MAX) will return a positive value. + // On the other hand, NPTL does not have such a limit, sysconf() + // will return -1 and errno is not changed. Check if it is really + // NPTL: + if (sysconf(_SC_THREAD_THREADS_MAX) > 0) { + free(str); + str = "linuxthreads"; + } + } + os::Linux::set_libpthread_version(str); + } else { + // glibc before 2.3.2 only has LinuxThreads. + os::Linux::set_libpthread_version("linuxthreads"); + } + + if (strstr(libpthread_version(), "NPTL")) { + os::Linux::set_is_NPTL(); + } else { + os::Linux::set_is_LinuxThreads(); + } + + // LinuxThreads have two flavors: floating-stack mode, which allows variable + // stack size; and fixed-stack mode. NPTL is always floating-stack. + if (os::Linux::is_NPTL() || os::Linux::supports_variable_stack_size()) { + os::Linux::set_is_floating_stack(); + } +} + +///////////////////////////////////////////////////////////////////////////// +// thread stack + +// Force Linux kernel to expand current thread stack. If "bottom" is close +// to the stack guard, caller should block all signals. +// +// MAP_GROWSDOWN: +// A special mmap() flag that is used to implement thread stacks. It tells +// kernel that the memory region should extend downwards when needed. This +// allows early versions of LinuxThreads to only mmap the first few pages +// when creating a new thread. Linux kernel will automatically expand thread +// stack as needed (on page faults). +// +// However, because the memory region of a MAP_GROWSDOWN stack can grow on +// demand, if a page fault happens outside an already mapped MAP_GROWSDOWN +// region, it's hard to tell if the fault is due to a legitimate stack +// access or because of reading/writing non-exist memory (e.g. buffer +// overrun). As a rule, if the fault happens below current stack pointer, +// Linux kernel does not expand stack, instead a SIGSEGV is sent to the +// application (see Linux kernel fault.c). +// +// This Linux feature can cause SIGSEGV when VM bangs thread stack for +// stack overflow detection. +// +// Newer version of LinuxThreads (since glibc-2.2, or, RH-7.x) and NPTL do +// not use this flag. However, the stack of initial thread is not created +// by pthread, it is still MAP_GROWSDOWN. Also it's possible (though +// unlikely) that user code can create a thread with MAP_GROWSDOWN stack +// and then attach the thread to JVM. +// +// To get around the problem and allow stack banging on Linux, we need to +// manually expand thread stack after receiving the SIGSEGV. +// +// There are two ways to expand thread stack to address "bottom", we used +// both of them in JVM before 1.5: +// 1. adjust stack pointer first so that it is below "bottom", and then +// touch "bottom" +// 2. mmap() the page in question +// +// Now alternate signal stack is gone, it's harder to use 2. For instance, +// if current sp is already near the lower end of page 101, and we need to +// call mmap() to map page 100, it is possible that part of the mmap() frame +// will be placed in page 100. When page 100 is mapped, it is zero-filled. +// That will destroy the mmap() frame and cause VM to crash. +// +// The following code works by adjusting sp first, then accessing the "bottom" +// page to force a page fault. Linux kernel will then automatically expand the +// stack mapping. +// +// _expand_stack_to() assumes its frame size is less than page size, which +// should always be true if the function is not inlined. + +#if __GNUC__ < 3 // gcc 2.x does not support noinline attribute +#define NOINLINE +#else +#define NOINLINE __attribute__ ((noinline)) +#endif + +static void _expand_stack_to(address bottom) NOINLINE; + +static void _expand_stack_to(address bottom) { + address sp; + size_t size; + volatile char *p; + + // Adjust bottom to point to the largest address within the same page, it + // gives us a one-page buffer if alloca() allocates slightly more memory. + bottom = (address)align_size_down((uintptr_t)bottom, os::Linux::page_size()); + bottom += os::Linux::page_size() - 1; + + // sp might be slightly above current stack pointer; if that's the case, we + // will alloca() a little more space than necessary, which is OK. Don't use + // os::current_stack_pointer(), as its result can be slightly below current + // stack pointer, causing us to not alloca enough to reach "bottom". + sp = (address)&sp; + + if (sp > bottom) { + size = sp - bottom; + p = (volatile char *)alloca(size); + assert(p != NULL && p <= (volatile char *)bottom, "alloca problem?"); + p[0] = '\0'; + } +} + +bool os::Linux::manually_expand_stack(JavaThread * t, address addr) { + assert(t!=NULL, "just checking"); + assert(t->osthread()->expanding_stack(), "expand should be set"); + assert(t->stack_base() != NULL, "stack_base was not initialized"); + + if (addr < t->stack_base() && addr >= t->stack_yellow_zone_base()) { + sigset_t mask_all, old_sigset; + sigfillset(&mask_all); + pthread_sigmask(SIG_SETMASK, &mask_all, &old_sigset); + _expand_stack_to(addr); + pthread_sigmask(SIG_SETMASK, &old_sigset, NULL); + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////////////////// +// create new thread + +static address highest_vm_reserved_address(); + +// check if it's safe to start a new thread +static bool _thread_safety_check(Thread* thread) { + if (os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack()) { + // Fixed stack LinuxThreads (SuSE Linux/x86, and some versions of Redhat) + // Heap is mmap'ed at lower end of memory space. Thread stacks are + // allocated (MAP_FIXED) from high address space. Every thread stack + // occupies a fixed size slot (usually 2Mbytes, but user can change + // it to other values if they rebuild LinuxThreads). + // + // Problem with MAP_FIXED is that mmap() can still succeed even part of + // the memory region has already been mmap'ed. That means if we have too + // many threads and/or very large heap, eventually thread stack will + // collide with heap. + // + // Here we try to prevent heap/stack collision by comparing current + // stack bottom with the highest address that has been mmap'ed by JVM + // plus a safety margin for memory maps created by native code. + // + // This feature can be disabled by setting ThreadSafetyMargin to 0 + // + if (ThreadSafetyMargin > 0) { + address stack_bottom = os::current_stack_base() - os::current_stack_size(); + + // not safe if our stack extends below the safety margin + return stack_bottom - ThreadSafetyMargin >= highest_vm_reserved_address(); + } else { + return true; + } + } else { + // Floating stack LinuxThreads or NPTL: + // Unlike fixed stack LinuxThreads, thread stacks are not MAP_FIXED. When + // there's not enough space left, pthread_create() will fail. If we come + // here, that means enough space has been reserved for stack. + return true; + } +} + +// Thread start routine for all newly created threads +static void *java_start(Thread *thread) { + // Try to randomize the cache line index of hot stack frames. + // This helps when threads of the same stack traces evict each other's + // cache lines. The threads can be either from the same JVM instance, or + // from different JVM instances. The benefit is especially true for + // processors with hyperthreading technology. + static int counter = 0; + int pid = os::current_process_id(); + alloca(((pid ^ counter++) & 7) * 128); + + ThreadLocalStorage::set_thread(thread); + + OSThread* osthread = thread->osthread(); + Monitor* sync = osthread->startThread_lock(); + + // non floating stack LinuxThreads needs extra check, see above + if (!_thread_safety_check(thread)) { + // notify parent thread + MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag); + osthread->set_state(ZOMBIE); + sync->notify_all(); + return NULL; + } + + // thread_id is kernel thread id (similar to Solaris LWP id) + osthread->set_thread_id(os::Linux::gettid()); + + if (UseNUMA) { + int lgrp_id = os::numa_get_group_id(); + if (lgrp_id != -1) { + thread->set_lgrp_id(lgrp_id); + } + } + // initialize signal mask for this thread + os::Linux::hotspot_sigmask(thread); + + // initialize floating point control register + os::Linux::init_thread_fpu_state(); + + // handshaking with parent thread + { + MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag); + + // notify parent thread + osthread->set_state(INITIALIZED); + sync->notify_all(); + + // wait until os::start_thread() + while (osthread->get_state() == INITIALIZED) { + sync->wait(Mutex::_no_safepoint_check_flag); + } + } + + // call one more level start routine + thread->run(); + + return 0; +} + +bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { + assert(thread->osthread() == NULL, "caller responsible"); + + // Allocate the OSThread object + OSThread* osthread = new OSThread(NULL, NULL); + if (osthread == NULL) { + return false; + } + + // set the correct thread state + osthread->set_thread_type(thr_type); + + // Initial state is ALLOCATED but not INITIALIZED + osthread->set_state(ALLOCATED); + + thread->set_osthread(osthread); + + // init thread attributes + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + // stack size + if (os::Linux::supports_variable_stack_size()) { + // calculate stack size if it's not specified by caller + if (stack_size == 0) { + stack_size = os::Linux::default_stack_size(thr_type); + + switch (thr_type) { + case os::java_thread: + // Java threads use ThreadStackSize which default value can be changed with the flag -Xss + if (JavaThread::stack_size_at_create() > 0) stack_size = JavaThread::stack_size_at_create(); + break; + case os::compiler_thread: + if (CompilerThreadStackSize > 0) { + stack_size = (size_t)(CompilerThreadStackSize * K); + break; + } // else fall through: + // use VMThreadStackSize if CompilerThreadStackSize is not defined + case os::vm_thread: + case os::pgc_thread: + case os::cgc_thread: + case os::watcher_thread: + if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K); + break; + } + } + + stack_size = MAX2(stack_size, os::Linux::min_stack_allowed); + pthread_attr_setstacksize(&attr, stack_size); + } else { + // let pthread_create() pick the default value. + } + + // glibc guard page + pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type)); + + ThreadState state; + + { + // Serialize thread creation if we are running with fixed stack LinuxThreads + bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack(); + if (lock) { + os::Linux::createThread_lock()->lock_without_safepoint_check(); + } + + pthread_t tid; + int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread); + + pthread_attr_destroy(&attr); + + if (ret != 0) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + perror("pthread_create()"); + } + // Need to clean up stuff we've allocated so far + thread->set_osthread(NULL); + delete osthread; + if (lock) os::Linux::createThread_lock()->unlock(); + return false; + } + + // Store pthread info into the OSThread + osthread->set_pthread_id(tid); + + // Wait until child thread is either initialized or aborted + { + Monitor* sync_with_child = osthread->startThread_lock(); + MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag); + while ((state = osthread->get_state()) == ALLOCATED) { + sync_with_child->wait(Mutex::_no_safepoint_check_flag); + } + } + + if (lock) { + os::Linux::createThread_lock()->unlock(); + } + } + + // Aborted due to thread limit being reached + if (state == ZOMBIE) { + thread->set_osthread(NULL); + delete osthread; + return false; + } + + // The thread is returned suspended (in state INITIALIZED), + // and is started higher up in the call chain + assert(state == INITIALIZED, "race condition"); + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// attach existing thread + +// bootstrap the main thread +bool os::create_main_thread(JavaThread* thread) { + assert(os::Linux::_main_thread == pthread_self(), "should be called inside main thread"); + return create_attached_thread(thread); +} + +bool os::create_attached_thread(JavaThread* thread) { +#ifdef ASSERT + thread->verify_not_published(); +#endif + + // Allocate the OSThread object + OSThread* osthread = new OSThread(NULL, NULL); + + if (osthread == NULL) { + return false; + } + + // Store pthread info into the OSThread + osthread->set_thread_id(os::Linux::gettid()); + osthread->set_pthread_id(::pthread_self()); + + // initialize floating point control register + os::Linux::init_thread_fpu_state(); + + // Initial thread state is RUNNABLE + osthread->set_state(RUNNABLE); + + thread->set_osthread(osthread); + + if (UseNUMA) { + int lgrp_id = os::numa_get_group_id(); + if (lgrp_id != -1) { + thread->set_lgrp_id(lgrp_id); + } + } + + if (os::Linux::is_initial_thread()) { + // If current thread is initial thread, its stack is mapped on demand, + // see notes about MAP_GROWSDOWN. Here we try to force kernel to map + // the entire stack region to avoid SEGV in stack banging. + // It is also useful to get around the heap-stack-gap problem on SuSE + // kernel (see 4821821 for details). We first expand stack to the top + // of yellow zone, then enable stack yellow zone (order is significant, + // enabling yellow zone first will crash JVM on SuSE Linux), so there + // is no gap between the last two virtual memory regions. + + JavaThread *jt = (JavaThread *)thread; + address addr = jt->stack_yellow_zone_base(); + assert(addr != NULL, "initialization problem?"); + assert(jt->stack_available(addr) > 0, "stack guard should not be enabled"); + + osthread->set_expanding_stack(); + os::Linux::manually_expand_stack(jt, addr); + osthread->clear_expanding_stack(); + } + + // initialize signal mask for this thread + // and save the caller's signal mask + os::Linux::hotspot_sigmask(thread); + + return true; +} + +void os::pd_start_thread(Thread* thread) { + OSThread * osthread = thread->osthread(); + assert(osthread->get_state() != INITIALIZED, "just checking"); + Monitor* sync_with_child = osthread->startThread_lock(); + MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag); + sync_with_child->notify(); +} + +// Free Linux resources related to the OSThread +void os::free_thread(OSThread* osthread) { + assert(osthread != NULL, "osthread not set"); + + if (Thread::current()->osthread() == osthread) { + // Restore caller's signal mask + sigset_t sigmask = osthread->caller_sigmask(); + pthread_sigmask(SIG_SETMASK, &sigmask, NULL); + } + + delete osthread; +} + +////////////////////////////////////////////////////////////////////////////// +// thread local storage + +int os::allocate_thread_local_storage() { + pthread_key_t key; + int rslt = pthread_key_create(&key, NULL); + assert(rslt == 0, "cannot allocate thread local storage"); + return (int)key; +} + +// Note: This is currently not used by VM, as we don't destroy TLS key +// on VM exit. +void os::free_thread_local_storage(int index) { + int rslt = pthread_key_delete((pthread_key_t)index); + assert(rslt == 0, "invalid index"); +} + +void os::thread_local_storage_at_put(int index, void* value) { + int rslt = pthread_setspecific((pthread_key_t)index, value); + assert(rslt == 0, "pthread_setspecific failed"); +} + +extern "C" Thread* get_thread() { + return ThreadLocalStorage::thread(); +} + +////////////////////////////////////////////////////////////////////////////// +// initial thread + +// Check if current thread is the initial thread, similar to Solaris thr_main. +bool os::Linux::is_initial_thread(void) { + char dummy; + // If called before init complete, thread stack bottom will be null. + // Can be called if fatal error occurs before initialization. + if (initial_thread_stack_bottom() == NULL) return false; + assert(initial_thread_stack_bottom() != NULL && + initial_thread_stack_size() != 0, + "os::init did not locate initial thread's stack region"); + if ((address)&dummy >= initial_thread_stack_bottom() && + (address)&dummy < initial_thread_stack_bottom() + initial_thread_stack_size()) + return true; + else return false; +} + +// Find the virtual memory area that contains addr +static bool find_vma(address addr, address* vma_low, address* vma_high) { + FILE *fp = fopen("/proc/self/maps", "r"); + if (fp) { + address low, high; + while (!feof(fp)) { + if (fscanf(fp, "%p-%p", &low, &high) == 2) { + if (low <= addr && addr < high) { + if (vma_low) *vma_low = low; + if (vma_high) *vma_high = high; + fclose (fp); + return true; + } + } + for (;;) { + int ch = fgetc(fp); + if (ch == EOF || ch == (int)'\n') break; + } + } + fclose(fp); + } + return false; +} + +// Locate initial thread stack. This special handling of initial thread stack +// is needed because pthread_getattr_np() on most (all?) Linux distros returns +// bogus value for initial thread. +void os::Linux::capture_initial_stack(size_t max_size) { + // stack size is the easy part, get it from RLIMIT_STACK + size_t stack_size; + struct rlimit rlim; + getrlimit(RLIMIT_STACK, &rlim); + stack_size = rlim.rlim_cur; + + // 6308388: a bug in ld.so will relocate its own .data section to the + // lower end of primordial stack; reduce ulimit -s value a little bit + // so we won't install guard page on ld.so's data section. + stack_size -= 2 * page_size(); + + // 4441425: avoid crash with "unlimited" stack size on SuSE 7.1 or Redhat + // 7.1, in both cases we will get 2G in return value. + // 4466587: glibc 2.2.x compiled w/o "--enable-kernel=2.4.0" (RH 7.0, + // SuSE 7.2, Debian) can not handle alternate signal stack correctly + // for initial thread if its stack size exceeds 6M. Cap it at 2M, + // in case other parts in glibc still assumes 2M max stack size. + // FIXME: alt signal stack is gone, maybe we can relax this constraint? +#ifndef IA64 + if (stack_size > 2 * K * K) stack_size = 2 * K * K; +#else + // Problem still exists RH7.2 (IA64 anyway) but 2MB is a little small + if (stack_size > 4 * K * K) stack_size = 4 * K * K; +#endif + + // Try to figure out where the stack base (top) is. This is harder. + // + // When an application is started, glibc saves the initial stack pointer in + // a global variable "__libc_stack_end", which is then used by system + // libraries. __libc_stack_end should be pretty close to stack top. The + // variable is available since the very early days. However, because it is + // a private interface, it could disappear in the future. + // + // Linux kernel saves start_stack information in /proc//stat. Similar + // to __libc_stack_end, it is very close to stack top, but isn't the real + // stack top. Note that /proc may not exist if VM is running as a chroot + // program, so reading /proc//stat could fail. Also the contents of + // /proc//stat could change in the future (though unlikely). + // + // We try __libc_stack_end first. If that doesn't work, look for + // /proc//stat. If neither of them works, we use current stack pointer + // as a hint, which should work well in most cases. + + uintptr_t stack_start; + + // try __libc_stack_end first + uintptr_t *p = (uintptr_t *)dlsym(RTLD_DEFAULT, "__libc_stack_end"); + if (p && *p) { + stack_start = *p; + } else { + // see if we can get the start_stack field from /proc/self/stat + FILE *fp; + int pid; + char state; + int ppid; + int pgrp; + int session; + int nr; + int tpgrp; + unsigned long flags; + unsigned long minflt; + unsigned long cminflt; + unsigned long majflt; + unsigned long cmajflt; + unsigned long utime; + unsigned long stime; + long cutime; + long cstime; + long prio; + long nice; + long junk; + long it_real; + uintptr_t start; + uintptr_t vsize; + uintptr_t rss; + unsigned long rsslim; + uintptr_t scodes; + uintptr_t ecode; + int i; + + // Figure what the primordial thread stack base is. Code is inspired + // by email from Hans Boehm. /proc/self/stat begins with current pid, + // followed by command name surrounded by parentheses, state, etc. + char stat[2048]; + int statlen; + + fp = fopen("/proc/self/stat", "r"); + if (fp) { + statlen = fread(stat, 1, 2047, fp); + stat[statlen] = '\0'; + fclose(fp); + + // Skip pid and the command string. Note that we could be dealing with + // weird command names, e.g. user could decide to rename java launcher + // to "java 1.4.2 :)", then the stat file would look like + // 1234 (java 1.4.2 :)) R ... ... + // We don't really need to know the command string, just find the last + // occurrence of ")" and then start parsing from there. See bug 4726580. + char * s = strrchr(stat, ')'); + + i = 0; + if (s) { + // Skip blank chars + do s++; while (isspace(*s)); + + /* 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 */ + /* 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 */ + i = sscanf(s, "%c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu", + &state, /* 3 %c */ + &ppid, /* 4 %d */ + &pgrp, /* 5 %d */ + &session, /* 6 %d */ + &nr, /* 7 %d */ + &tpgrp, /* 8 %d */ + &flags, /* 9 %lu */ + &minflt, /* 10 %lu */ + &cminflt, /* 11 %lu */ + &majflt, /* 12 %lu */ + &cmajflt, /* 13 %lu */ + &utime, /* 14 %lu */ + &stime, /* 15 %lu */ + &cutime, /* 16 %ld */ + &cstime, /* 17 %ld */ + &prio, /* 18 %ld */ + &nice, /* 19 %ld */ + &junk, /* 20 %ld */ + &it_real, /* 21 %ld */ + &start, /* 22 %lu */ + &vsize, /* 23 %lu */ + &rss, /* 24 %ld */ + &rsslim, /* 25 %lu */ + &scodes, /* 26 %lu */ + &ecode, /* 27 %lu */ + &stack_start); /* 28 %lu */ + } + + if (i != 28 - 2) { + assert(false, "Bad conversion from /proc/self/stat"); + // product mode - assume we are the initial thread, good luck in the + // embedded case. + warning("Can't detect initial thread stack location - bad conversion"); + stack_start = (uintptr_t) &rlim; + } + } else { + // For some reason we can't open /proc/self/stat (for example, running on + // FreeBSD with a Linux emulator, or inside chroot), this should work for + // most cases, so don't abort: + warning("Can't detect initial thread stack location - no /proc/self/stat"); + stack_start = (uintptr_t) &rlim; + } + } + + // Now we have a pointer (stack_start) very close to the stack top, the + // next thing to do is to figure out the exact location of stack top. We + // can find out the virtual memory area that contains stack_start by + // reading /proc/self/maps, it should be the last vma in /proc/self/maps, + // and its upper limit is the real stack top. (again, this would fail if + // running inside chroot, because /proc may not exist.) + + uintptr_t stack_top; + address low, high; + if (find_vma((address)stack_start, &low, &high)) { + // success, "high" is the true stack top. (ignore "low", because initial + // thread stack grows on demand, its real bottom is high - RLIMIT_STACK.) + stack_top = (uintptr_t)high; + } else { + // failed, likely because /proc/self/maps does not exist + warning("Can't detect initial thread stack location - find_vma failed"); + // best effort: stack_start is normally within a few pages below the real + // stack top, use it as stack top, and reduce stack size so we won't put + // guard page outside stack. + stack_top = stack_start; + stack_size -= 16 * page_size(); + } + + // stack_top could be partially down the page so align it + stack_top = align_size_up(stack_top, page_size()); + + if (max_size && stack_size > max_size) { + _initial_thread_stack_size = max_size; + } else { + _initial_thread_stack_size = stack_size; + } + + _initial_thread_stack_size = align_size_down(_initial_thread_stack_size, page_size()); + _initial_thread_stack_bottom = (address)stack_top - _initial_thread_stack_size; +} + +//////////////////////////////////////////////////////////////////////////////// +// time support + +// Time since start-up in seconds to a fine granularity. +// Used by VMSelfDestructTimer and the MemProfiler. +double os::elapsedTime() { + + return (double)(os::elapsed_counter()) * 0.000001; +} + +jlong os::elapsed_counter() { + timeval time; + int status = gettimeofday(&time, NULL); + return jlong(time.tv_sec) * 1000 * 1000 + jlong(time.tv_usec) - initial_time_count; +} + +jlong os::elapsed_frequency() { + return (1000 * 1000); +} + +jlong os::timeofday() { + timeval time; + int status = gettimeofday(&time, NULL); + assert(status != -1, "linux error"); + return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000); +} + +// Must return millis since Jan 1 1970 for JVM_CurrentTimeMillis +// _use_global_time is only set if CacheTimeMillis is true +jlong os::javaTimeMillis() { + return (_use_global_time ? read_global_time() : timeofday()); +} + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC (1) +#endif + +void os::Linux::clock_init() { + // we do dlopen's in this particular order due to bug in linux + // dynamical loader (see 6348968) leading to crash on exit + void* handle = dlopen("librt.so.1", RTLD_LAZY); + if (handle == NULL) { + handle = dlopen("librt.so", RTLD_LAZY); + } + + if (handle) { + int (*clock_getres_func)(clockid_t, struct timespec*) = + (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_getres"); + int (*clock_gettime_func)(clockid_t, struct timespec*) = + (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_gettime"); + if (clock_getres_func && clock_gettime_func) { + // See if monotonic clock is supported by the kernel. Note that some + // early implementations simply return kernel jiffies (updated every + // 1/100 or 1/1000 second). It would be bad to use such a low res clock + // for nano time (though the monotonic property is still nice to have). + // It's fixed in newer kernels, however clock_getres() still returns + // 1/HZ. We check if clock_getres() works, but will ignore its reported + // resolution for now. Hopefully as people move to new kernels, this + // won't be a problem. + struct timespec res; + struct timespec tp; + if (clock_getres_func (CLOCK_MONOTONIC, &res) == 0 && + clock_gettime_func(CLOCK_MONOTONIC, &tp) == 0) { + // yes, monotonic clock is supported + _clock_gettime = clock_gettime_func; + } else { + // close librt if there is no monotonic clock + dlclose(handle); + } + } + } +} + +#ifndef SYS_clock_getres + +#if defined(IA32) || defined(AMD64) +#define SYS_clock_getres IA32_ONLY(266) AMD64_ONLY(229) +#else +#error Value of SYS_clock_getres not known on this platform +#endif + +#endif + +#define sys_clock_getres(x,y) ::syscall(SYS_clock_getres, x, y) + +void os::Linux::fast_thread_clock_init() { + if (!UseLinuxPosixThreadCPUClocks) { + return; + } + clockid_t clockid; + struct timespec tp; + int (*pthread_getcpuclockid_func)(pthread_t, clockid_t *) = + (int(*)(pthread_t, clockid_t *)) dlsym(RTLD_DEFAULT, "pthread_getcpuclockid"); + + // Switch to using fast clocks for thread cpu time if + // the sys_clock_getres() returns 0 error code. + // Note, that some kernels may support the current thread + // clock (CLOCK_THREAD_CPUTIME_ID) but not the clocks + // returned by the pthread_getcpuclockid(). + // If the fast Posix clocks are supported then the sys_clock_getres() + // must return at least tp.tv_sec == 0 which means a resolution + // better than 1 sec. This is extra check for reliability. + + if(pthread_getcpuclockid_func && + pthread_getcpuclockid_func(_main_thread, &clockid) == 0 && + sys_clock_getres(clockid, &tp) == 0 && tp.tv_sec == 0) { + + _supports_fast_thread_cpu_time = true; + _pthread_getcpuclockid = pthread_getcpuclockid_func; + } +} + +jlong os::javaTimeNanos() { + if (Linux::supports_monotonic_clock()) { + struct timespec tp; + int status = Linux::clock_gettime(CLOCK_MONOTONIC, &tp); + assert(status == 0, "gettime error"); + jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec); + return result; + } else { + timeval time; + int status = gettimeofday(&time, NULL); + assert(status != -1, "linux error"); + jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec); + return 1000 * usecs; + } +} + +void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { + if (Linux::supports_monotonic_clock()) { + info_ptr->max_value = ALL_64_BITS; + + // CLOCK_MONOTONIC - amount of time since some arbitrary point in the past + info_ptr->may_skip_backward = false; // not subject to resetting or drifting + info_ptr->may_skip_forward = false; // not subject to resetting or drifting + } else { + // gettimeofday - based on time in seconds since the Epoch thus does not wrap + info_ptr->max_value = ALL_64_BITS; + + // gettimeofday is a real time clock so it skips + info_ptr->may_skip_backward = true; + info_ptr->may_skip_forward = true; + } + + info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time +} + +// Return the real, user, and system times in seconds from an +// arbitrary fixed point in the past. +bool os::getTimesSecs(double* process_real_time, + double* process_user_time, + double* process_system_time) { + struct tms ticks; + clock_t real_ticks = times(&ticks); + + if (real_ticks == (clock_t) (-1)) { + return false; + } else { + double ticks_per_second = (double) clock_tics_per_sec; + *process_user_time = ((double) ticks.tms_utime) / ticks_per_second; + *process_system_time = ((double) ticks.tms_stime) / ticks_per_second; + *process_real_time = ((double) real_ticks) / ticks_per_second; + + return true; + } +} + + +char * os::local_time_string(char *buf, size_t buflen) { + struct tm t; + time_t long_time; + time(&long_time); + localtime_r(&long_time, &t); + jio_snprintf(buf, buflen, "%d-%02d-%02d %02d:%02d:%02d", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec); + return buf; +} + +//////////////////////////////////////////////////////////////////////////////// +// runtime exit support + +// Note: os::shutdown() might be called very early during initialization, or +// called from signal handler. Before adding something to os::shutdown(), make +// sure it is async-safe and can handle partially initialized VM. +void os::shutdown() { + + // allow PerfMemory to attempt cleanup of any persistent resources + perfMemory_exit(); + + // needs to remove object in file system + AttachListener::abort(); + + // flush buffered output, finish log files + ostream_abort(); + + // Check for abort hook + abort_hook_t abort_hook = Arguments::abort_hook(); + if (abort_hook != NULL) { + abort_hook(); + } + +} + +// Note: os::abort() might be called very early during initialization, or +// called from signal handler. Before adding something to os::abort(), make +// sure it is async-safe and can handle partially initialized VM. +void os::abort(bool dump_core) { + os::shutdown(); + if (dump_core) { +#ifndef PRODUCT + fdStream out(defaultStream::output_fd()); + out.print_raw("Current thread is "); + char buf[16]; + jio_snprintf(buf, sizeof(buf), UINTX_FORMAT, os::current_thread_id()); + out.print_raw_cr(buf); + out.print_raw_cr("Dumping core ..."); +#endif + ::abort(); // dump core + } + + ::exit(1); +} + +// Die immediately, no exit hook, no abort hook, no cleanup. +void os::die() { + // _exit() on LinuxThreads only kills current thread + ::abort(); +} + +// unused on linux for now. +void os::set_error_file(const char *logfile) {} + +intx os::current_thread_id() { return (intx)pthread_self(); } +int os::current_process_id() { + + // Under the old linux thread library, linux gives each thread + // its own process id. Because of this each thread will return + // a different pid if this method were to return the result + // of getpid(2). Linux provides no api that returns the pid + // of the launcher thread for the vm. This implementation + // returns a unique pid, the pid of the launcher thread + // that starts the vm 'process'. + + // Under the NPTL, getpid() returns the same pid as the + // launcher thread rather than a unique pid per thread. + // Use gettid() if you want the old pre NPTL behaviour. + + // if you are looking for the result of a call to getpid() that + // returns a unique pid for the calling thread, then look at the + // OSThread::thread_id() method in osThread_linux.hpp file + + return (int)(_initial_pid ? _initial_pid : getpid()); +} + +// DLL functions + +const char* os::dll_file_extension() { return ".so"; } + +const char* os::get_temp_directory() { return "/tmp/"; } + +const char* os::get_current_directory(char *buf, int buflen) { + return getcwd(buf, buflen); +} + +// check if addr is inside libjvm[_g].so +bool os::address_is_in_vm(address addr) { + static address libjvm_base_addr; + Dl_info dlinfo; + + if (libjvm_base_addr == NULL) { + dladdr(CAST_FROM_FN_PTR(void *, os::address_is_in_vm), &dlinfo); + libjvm_base_addr = (address)dlinfo.dli_fbase; + assert(libjvm_base_addr !=NULL, "Cannot obtain base address for libjvm"); + } + + if (dladdr((void *)addr, &dlinfo)) { + if (libjvm_base_addr == (address)dlinfo.dli_fbase) return true; + } + + return false; +} + +bool os::dll_address_to_function_name(address addr, char *buf, + int buflen, int *offset) { + Dl_info dlinfo; + + if (dladdr((void*)addr, &dlinfo) && dlinfo.dli_sname != NULL) { + if (buf) jio_snprintf(buf, buflen, "%s", dlinfo.dli_sname); + if (offset) *offset = addr - (address)dlinfo.dli_saddr; + return true; + } else { + if (buf) buf[0] = '\0'; + if (offset) *offset = -1; + return false; + } +} + +struct _address_to_library_name { + address addr; // input : memory address + size_t buflen; // size of fname + char* fname; // output: library name + address base; // library base addr +}; + +static int address_to_library_name_callback(struct dl_phdr_info *info, + size_t size, void *data) { + int i; + bool found = false; + address libbase = NULL; + struct _address_to_library_name * d = (struct _address_to_library_name *)data; + + // iterate through all loadable segments + for (i = 0; i < info->dlpi_phnum; i++) { + address segbase = (address)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); + if (info->dlpi_phdr[i].p_type == PT_LOAD) { + // base address of a library is the lowest address of its loaded + // segments. + if (libbase == NULL || libbase > segbase) { + libbase = segbase; + } + // see if 'addr' is within current segment + if (segbase <= d->addr && + d->addr < segbase + info->dlpi_phdr[i].p_memsz) { + found = true; + } + } + } + + // dlpi_name is NULL or empty if the ELF file is executable, return 0 + // so dll_address_to_library_name() can fall through to use dladdr() which + // can figure out executable name from argv[0]. + if (found && info->dlpi_name && info->dlpi_name[0]) { + d->base = libbase; + if (d->fname) { + jio_snprintf(d->fname, d->buflen, "%s", info->dlpi_name); + } + return 1; + } + return 0; +} + +bool os::dll_address_to_library_name(address addr, char* buf, + int buflen, int* offset) { + Dl_info dlinfo; + struct _address_to_library_name data; + + // There is a bug in old glibc dladdr() implementation that it could resolve + // to wrong library name if the .so file has a base address != NULL. Here + // we iterate through the program headers of all loaded libraries to find + // out which library 'addr' really belongs to. This workaround can be + // removed once the minimum requirement for glibc is moved to 2.3.x. + data.addr = addr; + data.fname = buf; + data.buflen = buflen; + data.base = NULL; + int rslt = dl_iterate_phdr(address_to_library_name_callback, (void *)&data); + + if (rslt) { + // buf already contains library name + if (offset) *offset = addr - data.base; + return true; + } else if (dladdr((void*)addr, &dlinfo)){ + if (buf) jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); + if (offset) *offset = addr - (address)dlinfo.dli_fbase; + return true; + } else { + if (buf) buf[0] = '\0'; + if (offset) *offset = -1; + return false; + } +} + + // Loads .dll/.so and + // in case of error it checks if .dll/.so was built for the + // same architecture as Hotspot is running on + +void * os::dll_load(const char *filename, char *ebuf, int ebuflen) +{ + void * result= ::dlopen(filename, RTLD_LAZY); + if (result != NULL) { + // Successful loading + return result; + } + + Elf32_Ehdr elf_head; + + // Read system error message into ebuf + // It may or may not be overwritten below + ::strncpy(ebuf, ::dlerror(), ebuflen-1); + ebuf[ebuflen-1]='\0'; + int diag_msg_max_length=ebuflen-strlen(ebuf); + char* diag_msg_buf=ebuf+strlen(ebuf); + + if (diag_msg_max_length==0) { + // No more space in ebuf for additional diagnostics message + return NULL; + } + + + int file_descriptor= ::open(filename, O_RDONLY | O_NONBLOCK); + + if (file_descriptor < 0) { + // Can't open library, report dlerror() message + return NULL; + } + + bool failed_to_read_elf_head= + (sizeof(elf_head)!= + (::read(file_descriptor, &elf_head,sizeof(elf_head)))) ; + + ::close(file_descriptor); + if (failed_to_read_elf_head) { + // file i/o error - report dlerror() msg + return NULL; + } + + typedef struct { + Elf32_Half code; // Actual value as defined in elf.h + Elf32_Half compat_class; // Compatibility of archs at VM's sense + char elf_class; // 32 or 64 bit + char endianess; // MSB or LSB + char* name; // String representation + } arch_t; + + #ifndef EM_486 + #define EM_486 6 /* Intel 80486 */ + #endif + + static const arch_t arch_array[]={ + {EM_386, EM_386, ELFCLASS32, ELFDATA2LSB, (char*)"IA 32"}, + {EM_486, EM_386, ELFCLASS32, ELFDATA2LSB, (char*)"IA 32"}, + {EM_IA_64, EM_IA_64, ELFCLASS64, ELFDATA2LSB, (char*)"IA 64"}, + {EM_X86_64, EM_X86_64, ELFCLASS64, ELFDATA2LSB, (char*)"AMD 64"}, + {EM_SPARC, EM_SPARC, ELFCLASS32, ELFDATA2MSB, (char*)"Sparc 32"}, + {EM_SPARC32PLUS, EM_SPARC, ELFCLASS32, ELFDATA2MSB, (char*)"Sparc 32"}, + {EM_SPARCV9, EM_SPARCV9, ELFCLASS64, ELFDATA2MSB, (char*)"Sparc v9 64"}, + {EM_PPC, EM_PPC, ELFCLASS32, ELFDATA2MSB, (char*)"Power PC 32"}, + {EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64"} + }; + + #if (defined IA32) + static Elf32_Half running_arch_code=EM_386; + #elif (defined AMD64) + static Elf32_Half running_arch_code=EM_X86_64; + #elif (defined IA64) + static Elf32_Half running_arch_code=EM_IA_64; + #elif (defined __sparc) && (defined _LP64) + static Elf32_Half running_arch_code=EM_SPARCV9; + #elif (defined __sparc) && (!defined _LP64) + static Elf32_Half running_arch_code=EM_SPARC; + #elif (defined __powerpc64__) + static Elf32_Half running_arch_code=EM_PPC64; + #elif (defined __powerpc__) + static Elf32_Half running_arch_code=EM_PPC; + #else + #error Method os::dll_load requires that one of following is defined:\ + IA32, AMD64, IA64, __sparc, __powerpc__ + #endif + + // Identify compatability class for VM's architecture and library's architecture + // Obtain string descriptions for architectures + + arch_t lib_arch={elf_head.e_machine,0,elf_head.e_ident[EI_CLASS], elf_head.e_ident[EI_DATA], NULL}; + int running_arch_index=-1; + + for (unsigned int i=0 ; i < ARRAY_SIZE(arch_array) ; i++ ) { + if (running_arch_code == arch_array[i].code) { + running_arch_index = i; + } + if (lib_arch.code == arch_array[i].code) { + lib_arch.compat_class = arch_array[i].compat_class; + lib_arch.name = arch_array[i].name; + } + } + + assert(running_arch_index != -1, + "Didn't find running architecture code (running_arch_code) in arch_array"); + if (running_arch_index == -1) { + // Even though running architecture detection failed + // we may still continue with reporting dlerror() message + return NULL; + } + + if (lib_arch.endianess != arch_array[running_arch_index].endianess) { + ::snprintf(diag_msg_buf, diag_msg_max_length-1," (Possible cause: endianness mismatch)"); + return NULL; + } + + if (lib_arch.elf_class != arch_array[running_arch_index].elf_class) { + ::snprintf(diag_msg_buf, diag_msg_max_length-1," (Possible cause: architecture word width mismatch)"); + return NULL; + } + + if (lib_arch.compat_class != arch_array[running_arch_index].compat_class) { + if ( lib_arch.name!=NULL ) { + ::snprintf(diag_msg_buf, diag_msg_max_length-1, + " (Possible cause: can't load %s-bit .so on a %s-bit platform)", + lib_arch.name, arch_array[running_arch_index].name); + } else { + ::snprintf(diag_msg_buf, diag_msg_max_length-1, + " (Possible cause: can't load this .so (machine code=0x%x) on a %s-bit platform)", + lib_arch.code, + arch_array[running_arch_index].name); + } + } + + return NULL; +} + + + + +bool _print_ascii_file(const char* filename, outputStream* st) { + int fd = open(filename, O_RDONLY); + if (fd == -1) { + return false; + } + + char buf[32]; + int bytes; + while ((bytes = read(fd, buf, sizeof(buf))) > 0) { + st->print_raw(buf, bytes); + } + + close(fd); + + return true; +} + +void os::print_dll_info(outputStream *st) { + st->print_cr("Dynamic libraries:"); + + char fname[32]; + pid_t pid = os::Linux::gettid(); + + jio_snprintf(fname, sizeof(fname), "/proc/%d/maps", pid); + + if (!_print_ascii_file(fname, st)) { + st->print("Can not get library information for pid = %d\n", pid); + } +} + + +void os::print_os_info(outputStream* st) { + st->print("OS:"); + + // Try to identify popular distros. + // Most Linux distributions have /etc/XXX-release file, which contains + // the OS version string. Some have more than one /etc/XXX-release file + // (e.g. Mandrake has both /etc/mandrake-release and /etc/redhat-release.), + // so the order is important. + if (!_print_ascii_file("/etc/mandrake-release", st) && + !_print_ascii_file("/etc/sun-release", st) && + !_print_ascii_file("/etc/redhat-release", st) && + !_print_ascii_file("/etc/SuSE-release", st) && + !_print_ascii_file("/etc/turbolinux-release", st) && + !_print_ascii_file("/etc/gentoo-release", st) && + !_print_ascii_file("/etc/debian_version", st)) { + st->print("Linux"); + } + st->cr(); + + // kernel + st->print("uname:"); + struct utsname name; + uname(&name); + st->print(name.sysname); st->print(" "); + st->print(name.release); st->print(" "); + st->print(name.version); st->print(" "); + st->print(name.machine); + st->cr(); + + // Print warning if unsafe chroot environment detected + if (unsafe_chroot_detected) { + st->print("WARNING!! "); + st->print_cr(unstable_chroot_error); + } + + // libc, pthread + st->print("libc:"); + st->print(os::Linux::glibc_version()); st->print(" "); + st->print(os::Linux::libpthread_version()); st->print(" "); + if (os::Linux::is_LinuxThreads()) { + st->print("(%s stack)", os::Linux::is_floating_stack() ? "floating" : "fixed"); + } + st->cr(); + + // rlimit + st->print("rlimit:"); + struct rlimit rlim; + + st->print(" STACK "); + getrlimit(RLIMIT_STACK, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + + st->print(", CORE "); + getrlimit(RLIMIT_CORE, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + + st->print(", NPROC "); + getrlimit(RLIMIT_NPROC, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%d", rlim.rlim_cur); + + st->print(", NOFILE "); + getrlimit(RLIMIT_NOFILE, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%d", rlim.rlim_cur); + + st->print(", AS "); + getrlimit(RLIMIT_AS, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + st->cr(); + + // load average + st->print("load average:"); + double loadavg[3]; + os::loadavg(loadavg, 3); + st->print("%0.02f %0.02f %0.02f", loadavg[0], loadavg[1], loadavg[2]); + st->cr(); +} + +void os::print_memory_info(outputStream* st) { + + st->print("Memory:"); + st->print(" %dk page", os::vm_page_size()>>10); + + // values in struct sysinfo are "unsigned long" + struct sysinfo si; + sysinfo(&si); + + st->print(", physical " UINT64_FORMAT "k", + os::physical_memory() >> 10); + st->print("(" UINT64_FORMAT "k free)", + os::available_memory() >> 10); + st->print(", swap " UINT64_FORMAT "k", + ((jlong)si.totalswap * si.mem_unit) >> 10); + st->print("(" UINT64_FORMAT "k free)", + ((jlong)si.freeswap * si.mem_unit) >> 10); + st->cr(); +} + +// Taken from /usr/include/bits/siginfo.h Supposed to be architecture specific +// but they're the same for all the linux arch that we support +// and they're the same for solaris but there's no common place to put this. +const char *ill_names[] = { "ILL0", "ILL_ILLOPC", "ILL_ILLOPN", "ILL_ILLADR", + "ILL_ILLTRP", "ILL_PRVOPC", "ILL_PRVREG", + "ILL_COPROC", "ILL_BADSTK" }; + +const char *fpe_names[] = { "FPE0", "FPE_INTDIV", "FPE_INTOVF", "FPE_FLTDIV", + "FPE_FLTOVF", "FPE_FLTUND", "FPE_FLTRES", + "FPE_FLTINV", "FPE_FLTSUB", "FPE_FLTDEN" }; + +const char *segv_names[] = { "SEGV0", "SEGV_MAPERR", "SEGV_ACCERR" }; + +const char *bus_names[] = { "BUS0", "BUS_ADRALN", "BUS_ADRERR", "BUS_OBJERR" }; + +void os::print_siginfo(outputStream* st, void* siginfo) { + st->print("siginfo:"); + + const int buflen = 100; + char buf[buflen]; + siginfo_t *si = (siginfo_t*)siginfo; + st->print("si_signo=%s: ", os::exception_name(si->si_signo, buf, buflen)); + if (si->si_errno != 0 && strerror_r(si->si_errno, buf, buflen) == 0) { + st->print("si_errno=%s", buf); + } else { + st->print("si_errno=%d", si->si_errno); + } + const int c = si->si_code; + assert(c > 0, "unexpected si_code"); + switch (si->si_signo) { + case SIGILL: + st->print(", si_code=%d (%s)", c, c > 8 ? "" : ill_names[c]); + st->print(", si_addr=" PTR_FORMAT, si->si_addr); + break; + case SIGFPE: + st->print(", si_code=%d (%s)", c, c > 9 ? "" : fpe_names[c]); + st->print(", si_addr=" PTR_FORMAT, si->si_addr); + break; + case SIGSEGV: + st->print(", si_code=%d (%s)", c, c > 2 ? "" : segv_names[c]); + st->print(", si_addr=" PTR_FORMAT, si->si_addr); + break; + case SIGBUS: + st->print(", si_code=%d (%s)", c, c > 3 ? "" : bus_names[c]); + st->print(", si_addr=" PTR_FORMAT, si->si_addr); + break; + default: + st->print(", si_code=%d", si->si_code); + // no si_addr + } + + if ((si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && + UseSharedSpaces) { + FileMapInfo* mapinfo = FileMapInfo::current_info(); + if (mapinfo->is_in_shared_space(si->si_addr)) { + st->print("\n\nError accessing class data sharing archive." \ + " Mapped file inaccessible during execution, " \ + " possible disk/network problem."); + } + } + st->cr(); +} + + +static void print_signal_handler(outputStream* st, int sig, + char* buf, size_t buflen); + +void os::print_signal_handlers(outputStream* st, char* buf, size_t buflen) { + st->print_cr("Signal Handlers:"); + print_signal_handler(st, SIGSEGV, buf, buflen); + print_signal_handler(st, SIGBUS , buf, buflen); + print_signal_handler(st, SIGFPE , buf, buflen); + print_signal_handler(st, SIGPIPE, buf, buflen); + print_signal_handler(st, SIGXFSZ, buf, buflen); + print_signal_handler(st, SIGILL , buf, buflen); + print_signal_handler(st, INTERRUPT_SIGNAL, buf, buflen); + print_signal_handler(st, SR_signum, buf, buflen); + print_signal_handler(st, SHUTDOWN1_SIGNAL, buf, buflen); + print_signal_handler(st, SHUTDOWN2_SIGNAL , buf, buflen); + print_signal_handler(st, SHUTDOWN3_SIGNAL , buf, buflen); + print_signal_handler(st, BREAK_SIGNAL, buf, buflen); +} + +static char saved_jvm_path[MAXPATHLEN] = {0}; + +// Find the full path to the current module, libjvm.so or libjvm_g.so +void os::jvm_path(char *buf, jint len) { + // Error checking. + if (len < MAXPATHLEN) { + assert(false, "must use a large-enough buffer"); + buf[0] = '\0'; + return; + } + // Lazy resolve the path to current module. + if (saved_jvm_path[0] != 0) { + strcpy(buf, saved_jvm_path); + return; + } + + char dli_fname[MAXPATHLEN]; + bool ret = dll_address_to_library_name( + CAST_FROM_FN_PTR(address, os::jvm_path), + dli_fname, sizeof(dli_fname), NULL); + assert(ret != 0, "cannot locate libjvm"); + realpath(dli_fname, buf); + + if (strcmp(Arguments::sun_java_launcher(), "gamma") == 0) { + // Support for the gamma launcher. Typical value for buf is + // "/jre/lib///libjvm.so". If "/jre/lib/" appears at + // the right place in the string, then assume we are installed in a JDK and + // we're done. Otherwise, check for a JAVA_HOME environment variable and fix + // up the path so it looks like libjvm.so is installed there (append a + // fake suffix hotspot/libjvm.so). + const char *p = buf + strlen(buf) - 1; + for (int count = 0; p > buf && count < 5; ++count) { + for (--p; p > buf && *p != '/'; --p) + /* empty */ ; + } + + if (strncmp(p, "/jre/lib/", 9) != 0) { + // Look for JAVA_HOME in the environment. + char* java_home_var = ::getenv("JAVA_HOME"); + if (java_home_var != NULL && java_home_var[0] != 0) { + // Check the current module name "libjvm.so" or "libjvm_g.so". + p = strrchr(buf, '/'); + assert(strstr(p, "/libjvm") == p, "invalid library name"); + p = strstr(p, "_g") ? "_g" : ""; + + realpath(java_home_var, buf); + sprintf(buf + strlen(buf), "/jre/lib/%s", cpu_arch); + if (0 == access(buf, F_OK)) { + // Use current module name "libjvm[_g].so" instead of + // "libjvm"debug_only("_g")".so" since for fastdebug version + // we should have "libjvm.so" but debug_only("_g") adds "_g"! + // It is used when we are choosing the HPI library's name + // "libhpi[_g].so" in hpi::initialize_get_interface(). + sprintf(buf + strlen(buf), "/hotspot/libjvm%s.so", p); + } else { + // Go back to path of .so + realpath(dli_fname, buf); + } + } + } + } + + strcpy(saved_jvm_path, buf); +} + +void os::print_jni_name_prefix_on(outputStream* st, int args_size) { + // no prefix required, not even "_" +} + +void os::print_jni_name_suffix_on(outputStream* st, int args_size) { + // no suffix required +} + +//////////////////////////////////////////////////////////////////////////////// +// sun.misc.Signal support + +static volatile jint sigint_count = 0; + +static void +UserHandler(int sig, void *siginfo, void *context) { + // 4511530 - sem_post is serialized and handled by the manager thread. When + // the program is interrupted by Ctrl-C, SIGINT is sent to every thread. We + // don't want to flood the manager thread with sem_post requests. + if (sig == SIGINT && Atomic::add(1, &sigint_count) > 1) + return; + + // Ctrl-C is pressed during error reporting, likely because the error + // handler fails to abort. Let VM die immediately. + if (sig == SIGINT && is_error_reported()) { + os::die(); + } + + os::signal_notify(sig); +} + +void* os::user_handler() { + return CAST_FROM_FN_PTR(void*, UserHandler); +} + +extern "C" { + typedef void (*sa_handler_t)(int); + typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); +} + +void* os::signal(int signal_number, void* handler) { + struct sigaction sigAct, oldSigAct; + + sigfillset(&(sigAct.sa_mask)); + sigAct.sa_flags = SA_RESTART|SA_SIGINFO; + sigAct.sa_handler = CAST_TO_FN_PTR(sa_handler_t, handler); + + if (sigaction(signal_number, &sigAct, &oldSigAct)) { + // -1 means registration failed + return (void *)-1; + } + + return CAST_FROM_FN_PTR(void*, oldSigAct.sa_handler); +} + +void os::signal_raise(int signal_number) { + ::raise(signal_number); +} + +/* + * The following code is moved from os.cpp for making this + * code platform specific, which it is by its very nature. + */ + +// Will be modified when max signal is changed to be dynamic +int os::sigexitnum_pd() { + return NSIG; +} + +// a counter for each possible signal value +static volatile jint pending_signals[NSIG+1] = { 0 }; + +// Linux(POSIX) specific hand shaking semaphore. +static sem_t sig_sem; + +void os::signal_init_pd() { + // Initialize signal structures + ::memset((void*)pending_signals, 0, sizeof(pending_signals)); + + // Initialize signal semaphore + ::sem_init(&sig_sem, 0, 0); +} + +void os::signal_notify(int sig) { + Atomic::inc(&pending_signals[sig]); + ::sem_post(&sig_sem); +} + +static int check_pending_signals(bool wait) { + Atomic::store(0, &sigint_count); + for (;;) { + for (int i = 0; i < NSIG + 1; i++) { + jint n = pending_signals[i]; + if (n > 0 && n == Atomic::cmpxchg(n - 1, &pending_signals[i], n)) { + return i; + } + } + if (!wait) { + return -1; + } + JavaThread *thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + bool threadIsSuspended; + do { + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() + ::sem_wait(&sig_sem); + + // were we externally suspended while we were waiting? + threadIsSuspended = thread->handle_special_suspend_equivalent_condition(); + if (threadIsSuspended) { + // + // The semaphore has been incremented, but while we were waiting + // another thread suspended us. We don't want to continue running + // while suspended because that would surprise the thread that + // suspended us. + // + ::sem_post(&sig_sem); + + thread->java_suspend_self(); + } + } while (threadIsSuspended); + } +} + +int os::signal_lookup() { + return check_pending_signals(false); +} + +int os::signal_wait() { + return check_pending_signals(true); +} + +//////////////////////////////////////////////////////////////////////////////// +// Virtual Memory + +int os::vm_page_size() { + // Seems redundant as all get out + assert(os::Linux::page_size() != -1, "must call os::init"); + return os::Linux::page_size(); +} + +// Solaris allocates memory by pages. +int os::vm_allocation_granularity() { + assert(os::Linux::page_size() != -1, "must call os::init"); + return os::Linux::page_size(); +} + +// Rationale behind this function: +// current (Mon Apr 25 20:12:18 MSD 2005) oprofile drops samples without executable +// mapping for address (see lookup_dcookie() in the kernel module), thus we cannot get +// samples for JITted code. Here we create private executable mapping over the code cache +// and then we can use standard (well, almost, as mapping can change) way to provide +// info for the reporting script by storing timestamp and location of symbol +void linux_wrap_code(char* base, size_t size) { + static volatile jint cnt = 0; + + if (!UseOprofile) { + return; + } + + char buf[40]; + int num = Atomic::add(1, &cnt); + + sprintf(buf, "/tmp/hs-vm-%d-%d", os::current_process_id(), num); + unlink(buf); + + int fd = open(buf, O_CREAT | O_RDWR, S_IRWXU); + + if (fd != -1) { + off_t rv = lseek(fd, size-2, SEEK_SET); + if (rv != (off_t)-1) { + if (write(fd, "", 1) == 1) { + mmap(base, size, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_FIXED|MAP_NORESERVE, fd, 0); + } + } + close(fd); + unlink(buf); + } +} + +// NOTE: Linux kernel does not really reserve the pages for us. +// All it does is to check if there are enough free pages +// left at the time of mmap(). This could be a potential +// problem. +bool os::commit_memory(char* addr, size_t size) { + uintptr_t res = (uintptr_t) ::mmap(addr, size, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0); + return res != (uintptr_t) MAP_FAILED; +} + +bool os::commit_memory(char* addr, size_t size, size_t alignment_hint) { + return commit_memory(addr, size); +} + +void os::realign_memory(char *addr, size_t bytes, size_t alignment_hint) { } +void os::free_memory(char *addr, size_t bytes) { } +void os::numa_make_global(char *addr, size_t bytes) { } +void os::numa_make_local(char *addr, size_t bytes) { } +bool os::numa_topology_changed() { return false; } +size_t os::numa_get_groups_num() { return 1; } +int os::numa_get_group_id() { return 0; } +size_t os::numa_get_leaf_groups(int *ids, size_t size) { + if (size > 0) { + ids[0] = 0; + return 1; + } + return 0; +} + +bool os::get_page_info(char *start, page_info* info) { + return false; +} + +char *os::scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found) { + return end; +} + +bool os::uncommit_memory(char* addr, size_t size) { + return ::mmap(addr, size, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_FIXED|MAP_NORESERVE|MAP_ANONYMOUS, -1, 0) + != MAP_FAILED; +} + +static address _highest_vm_reserved_address = NULL; + +// If 'fixed' is true, anon_mmap() will attempt to reserve anonymous memory +// at 'requested_addr'. If there are existing memory mappings at the same +// location, however, they will be overwritten. If 'fixed' is false, +// 'requested_addr' is only treated as a hint, the return value may or +// may not start from the requested address. Unlike Linux mmap(), this +// function returns NULL to indicate failure. +static char* anon_mmap(char* requested_addr, size_t bytes, bool fixed) { + char * addr; + int flags; + + flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS; + if (fixed) { + assert((uintptr_t)requested_addr % os::Linux::page_size() == 0, "unaligned address"); + flags |= MAP_FIXED; + } + + addr = (char*)::mmap(requested_addr, bytes, PROT_READ|PROT_WRITE|PROT_EXEC, + flags, -1, 0); + + if (addr != MAP_FAILED) { + // anon_mmap() should only get called during VM initialization, + // don't need lock (actually we can skip locking even it can be called + // from multiple threads, because _highest_vm_reserved_address is just a + // hint about the upper limit of non-stack memory regions.) + if ((address)addr + bytes > _highest_vm_reserved_address) { + _highest_vm_reserved_address = (address)addr + bytes; + } + } + + return addr == MAP_FAILED ? NULL : addr; +} + +// Don't update _highest_vm_reserved_address, because there might be memory +// regions above addr + size. If so, releasing a memory region only creates +// a hole in the address space, it doesn't help prevent heap-stack collision. +// +static int anon_munmap(char * addr, size_t size) { + return ::munmap(addr, size) == 0; +} + +char* os::reserve_memory(size_t bytes, char* requested_addr, + size_t alignment_hint) { + return anon_mmap(requested_addr, bytes, (requested_addr != NULL)); +} + +bool os::release_memory(char* addr, size_t size) { + return anon_munmap(addr, size); +} + +static address highest_vm_reserved_address() { + return _highest_vm_reserved_address; +} + +static bool linux_mprotect(char* addr, size_t size, int prot) { + // Linux wants the mprotect address argument to be page aligned. + char* bottom = (char*)align_size_down((intptr_t)addr, os::Linux::page_size()); + + // According to SUSv3, mprotect() should only be used with mappings + // established by mmap(), and mmap() always maps whole pages. Unaligned + // 'addr' likely indicates problem in the VM (e.g. trying to change + // protection of malloc'ed or statically allocated memory). Check the + // caller if you hit this assert. + assert(addr == bottom, "sanity check"); + + size = align_size_up(pointer_delta(addr, bottom, 1) + size, os::Linux::page_size()); + return ::mprotect(bottom, size, prot) == 0; +} + +bool os::protect_memory(char* addr, size_t size) { + return linux_mprotect(addr, size, PROT_READ); +} + +bool os::guard_memory(char* addr, size_t size) { + return linux_mprotect(addr, size, PROT_NONE); +} + +bool os::unguard_memory(char* addr, size_t size) { + return linux_mprotect(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC); +} + +// Large page support + +static size_t _large_page_size = 0; + +bool os::large_page_init() { + if (!UseLargePages) return false; + + if (LargePageSizeInBytes) { + _large_page_size = LargePageSizeInBytes; + } else { + // large_page_size on Linux is used to round up heap size. x86 uses either + // 2M or 4M page, depending on whether PAE (Physical Address Extensions) + // mode is enabled. AMD64/EM64T uses 2M page in 64bit mode. IA64 can use + // page as large as 256M. + // + // Here we try to figure out page size by parsing /proc/meminfo and looking + // for a line with the following format: + // Hugepagesize: 2048 kB + // + // If we can't determine the value (e.g. /proc is not mounted, or the text + // format has been changed), we'll use the largest page size supported by + // the processor. + + _large_page_size = IA32_ONLY(4 * M) AMD64_ONLY(2 * M) IA64_ONLY(256 * M) SPARC_ONLY(4 * M); + + FILE *fp = fopen("/proc/meminfo", "r"); + if (fp) { + while (!feof(fp)) { + int x = 0; + char buf[16]; + if (fscanf(fp, "Hugepagesize: %d", &x) == 1) { + if (x && fgets(buf, sizeof(buf), fp) && strcmp(buf, " kB\n") == 0) { + _large_page_size = x * K; + break; + } + } else { + // skip to next line + for (;;) { + int ch = fgetc(fp); + if (ch == EOF || ch == (int)'\n') break; + } + } + } + fclose(fp); + } + } + + const size_t default_page_size = (size_t)Linux::page_size(); + if (_large_page_size > default_page_size) { + _page_sizes[0] = _large_page_size; + _page_sizes[1] = default_page_size; + _page_sizes[2] = 0; + } + + // Large page support is available on 2.6 or newer kernel, some vendors + // (e.g. Redhat) have backported it to their 2.4 based distributions. + // We optimistically assume the support is available. If later it turns out + // not true, VM will automatically switch to use regular page size. + return true; +} + +#ifndef SHM_HUGETLB +#define SHM_HUGETLB 04000 +#endif + +char* os::reserve_memory_special(size_t bytes) { + assert(UseLargePages, "only for large pages"); + + key_t key = IPC_PRIVATE; + char *addr; + + bool warn_on_failure = UseLargePages && + (!FLAG_IS_DEFAULT(UseLargePages) || + !FLAG_IS_DEFAULT(LargePageSizeInBytes) + ); + char msg[128]; + + // Create a large shared memory region to attach to based on size. + // Currently, size is the total size of the heap + int shmid = shmget(key, bytes, SHM_HUGETLB|IPC_CREAT|SHM_R|SHM_W); + if (shmid == -1) { + // Possible reasons for shmget failure: + // 1. shmmax is too small for Java heap. + // > check shmmax value: cat /proc/sys/kernel/shmmax + // > increase shmmax value: echo "0xffffffff" > /proc/sys/kernel/shmmax + // 2. not enough large page memory. + // > check available large pages: cat /proc/meminfo + // > increase amount of large pages: + // echo new_value > /proc/sys/vm/nr_hugepages + // Note 1: different Linux may use different name for this property, + // e.g. on Redhat AS-3 it is "hugetlb_pool". + // Note 2: it's possible there's enough physical memory available but + // they are so fragmented after a long run that they can't + // coalesce into large pages. Try to reserve large pages when + // the system is still "fresh". + if (warn_on_failure) { + jio_snprintf(msg, sizeof(msg), "Failed to reserve shared memory (errno = %d).", errno); + warning(msg); + } + return NULL; + } + + // attach to the region + addr = (char*)shmat(shmid, NULL, 0); + int err = errno; + + // Remove shmid. If shmat() is successful, the actual shared memory segment + // will be deleted when it's detached by shmdt() or when the process + // terminates. If shmat() is not successful this will remove the shared + // segment immediately. + shmctl(shmid, IPC_RMID, NULL); + + if ((intptr_t)addr == -1) { + if (warn_on_failure) { + jio_snprintf(msg, sizeof(msg), "Failed to attach shared memory (errno = %d).", err); + warning(msg); + } + return NULL; + } + + return addr; +} + +bool os::release_memory_special(char* base, size_t bytes) { + // detaching the SHM segment will also delete it, see reserve_memory_special() + int rslt = shmdt(base); + return rslt == 0; +} + +size_t os::large_page_size() { + return _large_page_size; +} + +// Linux does not support anonymous mmap with large page memory. The only way +// to reserve large page memory without file backing is through SysV shared +// memory API. The entire memory region is committed and pinned upfront. +// Hopefully this will change in the future... +bool os::can_commit_large_page_memory() { + return false; +} + +// Reserve memory at an arbitrary address, only if that area is +// available (and not reserved for something else). + +char* os::attempt_reserve_memory_at(size_t bytes, char* requested_addr) { + const int max_tries = 10; + char* base[max_tries]; + size_t size[max_tries]; + const size_t gap = 0x000000; + + // Assert only that the size is a multiple of the page size, since + // that's all that mmap requires, and since that's all we really know + // about at this low abstraction level. If we need higher alignment, + // we can either pass an alignment to this method or verify alignment + // in one of the methods further up the call chain. See bug 5044738. + assert(bytes % os::vm_page_size() == 0, "reserving unexpected size block"); + + // Repeatedly allocate blocks until the block is allocated at the + // right spot. Give up after max_tries. Note that reserve_memory() will + // automatically update _highest_vm_reserved_address if the call is + // successful. The variable tracks the highest memory address every reserved + // by JVM. It is used to detect heap-stack collision if running with + // fixed-stack LinuxThreads. Because here we may attempt to reserve more + // space than needed, it could confuse the collision detecting code. To + // solve the problem, save current _highest_vm_reserved_address and + // calculate the correct value before return. + address old_highest = _highest_vm_reserved_address; + + // Linux mmap allows caller to pass an address as hint; give it a try first, + // if kernel honors the hint then we can return immediately. + char * addr = anon_mmap(requested_addr, bytes, false); + if (addr == requested_addr) { + return requested_addr; + } + + if (addr != NULL) { + // mmap() is successful but it fails to reserve at the requested address + anon_munmap(addr, bytes); + } + + int i; + for (i = 0; i < max_tries; ++i) { + base[i] = reserve_memory(bytes); + + if (base[i] != NULL) { + // Is this the block we wanted? + if (base[i] == requested_addr) { + size[i] = bytes; + break; + } + + // Does this overlap the block we wanted? Give back the overlapped + // parts and try again. + + size_t top_overlap = requested_addr + (bytes + gap) - base[i]; + if (top_overlap >= 0 && top_overlap < bytes) { + unmap_memory(base[i], top_overlap); + base[i] += top_overlap; + size[i] = bytes - top_overlap; + } else { + size_t bottom_overlap = base[i] + bytes - requested_addr; + if (bottom_overlap >= 0 && bottom_overlap < bytes) { + unmap_memory(requested_addr, bottom_overlap); + size[i] = bytes - bottom_overlap; + } else { + size[i] = bytes; + } + } + } + } + + // Give back the unused reserved pieces. + + for (int j = 0; j < i; ++j) { + if (base[j] != NULL) { + unmap_memory(base[j], size[j]); + } + } + + if (i < max_tries) { + _highest_vm_reserved_address = MAX2(old_highest, (address)requested_addr + bytes); + return requested_addr; + } else { + _highest_vm_reserved_address = old_highest; + return NULL; + } +} + +size_t os::read(int fd, void *buf, unsigned int nBytes) { + return ::read(fd, buf, nBytes); +} + +// TODO-FIXME: reconcile Solaris' os::sleep with the linux variation. +// Solaris uses poll(), linux uses park(). +// Poll() is likely a better choice, assuming that Thread.interrupt() +// generates a SIGUSRx signal. Note that SIGUSR1 can interfere with +// SIGSEGV, see 4355769. + +const int NANOSECS_PER_MILLISECS = 1000000; + +int os::sleep(Thread* thread, jlong millis, bool interruptible) { + assert(thread == Thread::current(), "thread consistency check"); + + ParkEvent * const slp = thread->_SleepEvent ; + slp->reset() ; + OrderAccess::fence() ; + + if (interruptible) { + jlong prevtime = javaTimeNanos(); + + for (;;) { + if (os::is_interrupted(thread, true)) { + return OS_INTRPT; + } + + jlong newtime = javaTimeNanos(); + + if (newtime - prevtime < 0) { + // time moving backwards, should only happen if no monotonic clock + // not a guarantee() because JVM should not abort on kernel/glibc bugs + assert(!Linux::supports_monotonic_clock(), "time moving backwards"); + } else { + millis -= (newtime - prevtime) / NANOSECS_PER_MILLISECS; + } + + if(millis <= 0) { + return OS_OK; + } + + prevtime = newtime; + + { + assert(thread->is_Java_thread(), "sanity check"); + JavaThread *jt = (JavaThread *) thread; + ThreadBlockInVM tbivm(jt); + OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */); + + jt->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + slp->park(millis); + + // were we externally suspended while we were waiting? + jt->check_and_wait_while_suspended(); + } + } + } else { + OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); + jlong prevtime = javaTimeNanos(); + + for (;;) { + // It'd be nice to avoid the back-to-back javaTimeNanos() calls on + // the 1st iteration ... + jlong newtime = javaTimeNanos(); + + if (newtime - prevtime < 0) { + // time moving backwards, should only happen if no monotonic clock + // not a guarantee() because JVM should not abort on kernel/glibc bugs + assert(!Linux::supports_monotonic_clock(), "time moving backwards"); + } else { + millis -= (newtime - prevtime) / NANOSECS_PER_MILLISECS; + } + + if(millis <= 0) break ; + + prevtime = newtime; + slp->park(millis); + } + return OS_OK ; + } +} + +int os::naked_sleep() { + // %% make the sleep time an integer flag. for now use 1 millisec. + return os::sleep(Thread::current(), 1, false); +} + +// Sleep forever; naked call to OS-specific sleep; use with CAUTION +void os::infinite_sleep() { + while (true) { // sleep forever ... + ::sleep(100); // ... 100 seconds at a time + } +} + +// Used to convert frequent JVM_Yield() to nops +bool os::dont_yield() { + return DontYieldALot; +} + +void os::yield() { + sched_yield(); +} + +os::YieldResult os::NakedYield() { sched_yield(); return os::YIELD_UNKNOWN ;} + +void os::yield_all(int attempts) { + // Yields to all threads, including threads with lower priorities + // Threads on Linux are all with same priority. The Solaris style + // os::yield_all() with nanosleep(1ms) is not necessary. + sched_yield(); +} + +// Called from the tight loops to possibly influence time-sharing heuristics +void os::loop_breaker(int attempts) { + os::yield_all(attempts); +} + +//////////////////////////////////////////////////////////////////////////////// +// thread priority support + +// Note: Normal Linux applications are run with SCHED_OTHER policy. SCHED_OTHER +// only supports dynamic priority, static priority must be zero. For real-time +// applications, Linux supports SCHED_RR which allows static priority (1-99). +// However, for large multi-threaded applications, SCHED_RR is not only slower +// than SCHED_OTHER, but also very unstable (my volano tests hang hard 4 out +// of 5 runs - Sep 2005). +// +// The following code actually changes the niceness of kernel-thread/LWP. It +// has an assumption that setpriority() only modifies one kernel-thread/LWP, +// not the entire user process, and user level threads are 1:1 mapped to kernel +// threads. It has always been the case, but could change in the future. For +// this reason, the code should not be used as default (ThreadPriorityPolicy=0). +// It is only used when ThreadPriorityPolicy=1 and requires root privilege. + +int os::java_to_os_priority[MaxPriority + 1] = { + 19, // 0 Entry should never be used + + 4, // 1 MinPriority + 3, // 2 + 2, // 3 + + 1, // 4 + 0, // 5 NormPriority + -1, // 6 + + -2, // 7 + -3, // 8 + -4, // 9 NearMaxPriority + + -5 // 10 MaxPriority +}; + +static int prio_init() { + if (ThreadPriorityPolicy == 1) { + // Only root can raise thread priority. Don't allow ThreadPriorityPolicy=1 + // if effective uid is not root. Perhaps, a more elegant way of doing + // this is to test CAP_SYS_NICE capability, but that will require libcap.so + if (geteuid() != 0) { + if (!FLAG_IS_DEFAULT(ThreadPriorityPolicy)) { + warning("-XX:ThreadPriorityPolicy requires root privilege on Linux"); + } + ThreadPriorityPolicy = 0; + } + } + return 0; +} + +OSReturn os::set_native_priority(Thread* thread, int newpri) { + if ( !UseThreadPriorities || ThreadPriorityPolicy == 0 ) return OS_OK; + + int ret = setpriority(PRIO_PROCESS, thread->osthread()->thread_id(), newpri); + return (ret == 0) ? OS_OK : OS_ERR; +} + +OSReturn os::get_native_priority(const Thread* const thread, int *priority_ptr) { + if ( !UseThreadPriorities || ThreadPriorityPolicy == 0 ) { + *priority_ptr = java_to_os_priority[NormPriority]; + return OS_OK; + } + + errno = 0; + *priority_ptr = getpriority(PRIO_PROCESS, thread->osthread()->thread_id()); + return (*priority_ptr != -1 || errno == 0 ? OS_OK : OS_ERR); +} + +// Hint to the underlying OS that a task switch would not be good. +// Void return because it's a hint and can fail. +void os::hint_no_preempt() {} + +//////////////////////////////////////////////////////////////////////////////// +// suspend/resume support + +// the low-level signal-based suspend/resume support is a remnant from the +// old VM-suspension that used to be for java-suspension, safepoints etc, +// within hotspot. Now there is a single use-case for this: +// - calling get_thread_pc() on the VMThread by the flat-profiler task +// that runs in the watcher thread. +// The remaining code is greatly simplified from the more general suspension +// code that used to be used. +// +// The protocol is quite simple: +// - suspend: +// - sends a signal to the target thread +// - polls the suspend state of the osthread using a yield loop +// - target thread signal handler (SR_handler) sets suspend state +// and blocks in sigsuspend until continued +// - resume: +// - sets target osthread state to continue +// - sends signal to end the sigsuspend loop in the SR_handler +// +// Note that the SR_lock plays no role in this suspend/resume protocol. +// + +static void resume_clear_context(OSThread *osthread) { + osthread->set_ucontext(NULL); + osthread->set_siginfo(NULL); + + // notify the suspend action is completed, we have now resumed + osthread->sr.clear_suspended(); +} + +static void suspend_save_context(OSThread *osthread, siginfo_t* siginfo, ucontext_t* context) { + osthread->set_ucontext(context); + osthread->set_siginfo(siginfo); +} + +// +// Handler function invoked when a thread's execution is suspended or +// resumed. We have to be careful that only async-safe functions are +// called here (Note: most pthread functions are not async safe and +// should be avoided.) +// +// Note: sigwait() is a more natural fit than sigsuspend() from an +// interface point of view, but sigwait() prevents the signal hander +// from being run. libpthread would get very confused by not having +// its signal handlers run and prevents sigwait()'s use with the +// mutex granting granting signal. +// +// Currently only ever called on the VMThread +// +static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { + // Save and restore errno to avoid confusing native code with EINTR + // after sigsuspend. + int old_errno = errno; + + Thread* thread = Thread::current(); + OSThread* osthread = thread->osthread(); + assert(thread->is_VM_thread(), "Must be VMThread"); + // read current suspend action + int action = osthread->sr.suspend_action(); + if (action == SR_SUSPEND) { + suspend_save_context(osthread, siginfo, context); + + // Notify the suspend action is about to be completed. do_suspend() + // waits until SR_SUSPENDED is set and then returns. We will wait + // here for a resume signal and that completes the suspend-other + // action. do_suspend/do_resume is always called as a pair from + // the same thread - so there are no races + + // notify the caller + osthread->sr.set_suspended(); + + sigset_t suspend_set; // signals for sigsuspend() + + // get current set of blocked signals and unblock resume signal + pthread_sigmask(SIG_BLOCK, NULL, &suspend_set); + sigdelset(&suspend_set, SR_signum); + + // wait here until we are resumed + do { + sigsuspend(&suspend_set); + // ignore all returns until we get a resume signal + } while (osthread->sr.suspend_action() != SR_CONTINUE); + + resume_clear_context(osthread); + + } else { + assert(action == SR_CONTINUE, "unexpected sr action"); + // nothing special to do - just leave the handler + } + + errno = old_errno; +} + + +static int SR_initialize() { + struct sigaction act; + char *s; + /* Get signal number to use for suspend/resume */ + if ((s = ::getenv("_JAVA_SR_SIGNUM")) != 0) { + int sig = ::strtol(s, 0, 10); + if (sig > 0 || sig < _NSIG) { + SR_signum = sig; + } + } + + assert(SR_signum > SIGSEGV && SR_signum > SIGBUS, + "SR_signum must be greater than max(SIGSEGV, SIGBUS), see 4355769"); + + sigemptyset(&SR_sigset); + sigaddset(&SR_sigset, SR_signum); + + /* Set up signal handler for suspend/resume */ + act.sa_flags = SA_RESTART|SA_SIGINFO; + act.sa_handler = (void (*)(int)) SR_handler; + + // SR_signum is blocked by default. + // 4528190 - We also need to block pthread restart signal (32 on all + // supported Linux platforms). Note that LinuxThreads need to block + // this signal for all threads to work properly. So we don't have + // to use hard-coded signal number when setting up the mask. + pthread_sigmask(SIG_BLOCK, NULL, &act.sa_mask); + + if (sigaction(SR_signum, &act, 0) == -1) { + return -1; + } + + // Save signal flag + os::Linux::set_our_sigflags(SR_signum, act.sa_flags); + return 0; +} + +static int SR_finalize() { + return 0; +} + + +// returns true on success and false on error - really an error is fatal +// but this seems the normal response to library errors +static bool do_suspend(OSThread* osthread) { + // mark as suspended and send signal + osthread->sr.set_suspend_action(SR_SUSPEND); + int status = pthread_kill(osthread->pthread_id(), SR_signum); + assert_status(status == 0, status, "pthread_kill"); + + // check status and wait until notified of suspension + if (status == 0) { + for (int i = 0; !osthread->sr.is_suspended(); i++) { + os::yield_all(i); + } + osthread->sr.set_suspend_action(SR_NONE); + return true; + } + else { + osthread->sr.set_suspend_action(SR_NONE); + return false; + } +} + +static void do_resume(OSThread* osthread) { + assert(osthread->sr.is_suspended(), "thread should be suspended"); + osthread->sr.set_suspend_action(SR_CONTINUE); + + int status = pthread_kill(osthread->pthread_id(), SR_signum); + assert_status(status == 0, status, "pthread_kill"); + // check status and wait unit notified of resumption + if (status == 0) { + for (int i = 0; osthread->sr.is_suspended(); i++) { + os::yield_all(i); + } + } + osthread->sr.set_suspend_action(SR_NONE); +} + +//////////////////////////////////////////////////////////////////////////////// +// interrupt support + +void os::interrupt(Thread* thread) { + assert(Thread::current() == thread || Threads_lock->owned_by_self(), + "possibility of dangling Thread pointer"); + + OSThread* osthread = thread->osthread(); + + if (!osthread->interrupted()) { + osthread->set_interrupted(true); + // More than one thread can get here with the same value of osthread, + // resulting in multiple notifications. We do, however, want the store + // to interrupted() to be visible to other threads before we execute unpark(). + OrderAccess::fence(); + ParkEvent * const slp = thread->_SleepEvent ; + if (slp != NULL) slp->unpark() ; + } + + // For JSR166. Unpark even if interrupt status already was set + if (thread->is_Java_thread()) + ((JavaThread*)thread)->parker()->unpark(); + + ParkEvent * ev = thread->_ParkEvent ; + if (ev != NULL) ev->unpark() ; + +} + +bool os::is_interrupted(Thread* thread, bool clear_interrupted) { + assert(Thread::current() == thread || Threads_lock->owned_by_self(), + "possibility of dangling Thread pointer"); + + OSThread* osthread = thread->osthread(); + + bool interrupted = osthread->interrupted(); + + if (interrupted && clear_interrupted) { + osthread->set_interrupted(false); + // consider thread->_SleepEvent->reset() ... optional optimization + } + + return interrupted; +} + +/////////////////////////////////////////////////////////////////////////////////// +// signal handling (except suspend/resume) + +// This routine may be used by user applications as a "hook" to catch signals. +// The user-defined signal handler must pass unrecognized signals to this +// routine, and if it returns true (non-zero), then the signal handler must +// return immediately. If the flag "abort_if_unrecognized" is true, then this +// routine will never retun false (zero), but instead will execute a VM panic +// routine kill the process. +// +// If this routine returns false, it is OK to call it again. This allows +// the user-defined signal handler to perform checks either before or after +// the VM performs its own checks. Naturally, the user code would be making +// a serious error if it tried to handle an exception (such as a null check +// or breakpoint) that the VM was generating for its own correct operation. +// +// This routine may recognize any of the following kinds of signals: +// SIGBUS, SIGSEGV, SIGILL, SIGFPE, SIGQUIT, SIGPIPE, SIGXFSZ, SIGUSR1. +// It should be consulted by handlers for any of those signals. +// +// The caller of this routine must pass in the three arguments supplied +// to the function referred to in the "sa_sigaction" (not the "sa_handler") +// field of the structure passed to sigaction(). This routine assumes that +// the sa_flags field passed to sigaction() includes SA_SIGINFO and SA_RESTART. +// +// Note that the VM will print warnings if it detects conflicting signal +// handlers, unless invoked with the option "-XX:+AllowUserSignalHandlers". +// +extern "C" int +JVM_handle_linux_signal(int signo, siginfo_t* siginfo, + void* ucontext, int abort_if_unrecognized); + +void signalHandler(int sig, siginfo_t* info, void* uc) { + assert(info != NULL && uc != NULL, "it must be old kernel"); + JVM_handle_linux_signal(sig, info, uc, true); +} + + +// This boolean allows users to forward their own non-matching signals +// to JVM_handle_linux_signal, harmlessly. +bool os::Linux::signal_handlers_are_installed = false; + +// For signal-chaining +struct sigaction os::Linux::sigact[MAXSIGNUM]; +unsigned int os::Linux::sigs = 0; +bool os::Linux::libjsig_is_loaded = false; +typedef struct sigaction *(*get_signal_t)(int); +get_signal_t os::Linux::get_signal_action = NULL; + +struct sigaction* os::Linux::get_chained_signal_action(int sig) { + struct sigaction *actp = NULL; + + if (libjsig_is_loaded) { + // Retrieve the old signal handler from libjsig + actp = (*get_signal_action)(sig); + } + if (actp == NULL) { + // Retrieve the preinstalled signal handler from jvm + actp = get_preinstalled_handler(sig); + } + + return actp; +} + +static bool call_chained_handler(struct sigaction *actp, int sig, + siginfo_t *siginfo, void *context) { + // Call the old signal handler + if (actp->sa_handler == SIG_DFL) { + // It's more reasonable to let jvm treat it as an unexpected exception + // instead of taking the default action. + return false; + } else if (actp->sa_handler != SIG_IGN) { + if ((actp->sa_flags & SA_NODEFER) == 0) { + // automaticlly block the signal + sigaddset(&(actp->sa_mask), sig); + } + + sa_handler_t hand; + sa_sigaction_t sa; + bool siginfo_flag_set = (actp->sa_flags & SA_SIGINFO) != 0; + // retrieve the chained handler + if (siginfo_flag_set) { + sa = actp->sa_sigaction; + } else { + hand = actp->sa_handler; + } + + if ((actp->sa_flags & SA_RESETHAND) != 0) { + actp->sa_handler = SIG_DFL; + } + + // try to honor the signal mask + sigset_t oset; + pthread_sigmask(SIG_SETMASK, &(actp->sa_mask), &oset); + + // call into the chained handler + if (siginfo_flag_set) { + (*sa)(sig, siginfo, context); + } else { + (*hand)(sig); + } + + // restore the signal mask + pthread_sigmask(SIG_SETMASK, &oset, 0); + } + // Tell jvm's signal handler the signal is taken care of. + return true; +} + +bool os::Linux::chained_handler(int sig, siginfo_t* siginfo, void* context) { + bool chained = false; + // signal-chaining + if (UseSignalChaining) { + struct sigaction *actp = get_chained_signal_action(sig); + if (actp != NULL) { + chained = call_chained_handler(actp, sig, siginfo, context); + } + } + return chained; +} + +struct sigaction* os::Linux::get_preinstalled_handler(int sig) { + if ((( (unsigned int)1 << sig ) & sigs) != 0) { + return &sigact[sig]; + } + return NULL; +} + +void os::Linux::save_preinstalled_handler(int sig, struct sigaction& oldAct) { + assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); + sigact[sig] = oldAct; + sigs |= (unsigned int)1 << sig; +} + +// for diagnostic +int os::Linux::sigflags[MAXSIGNUM]; + +int os::Linux::get_our_sigflags(int sig) { + assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); + return sigflags[sig]; +} + +void os::Linux::set_our_sigflags(int sig, int flags) { + assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); + sigflags[sig] = flags; +} + +void os::Linux::set_signal_handler(int sig, bool set_installed) { + // Check for overwrite. + struct sigaction oldAct; + sigaction(sig, (struct sigaction*)NULL, &oldAct); + + void* oldhand = oldAct.sa_sigaction + ? CAST_FROM_FN_PTR(void*, oldAct.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oldAct.sa_handler); + if (oldhand != CAST_FROM_FN_PTR(void*, SIG_DFL) && + oldhand != CAST_FROM_FN_PTR(void*, SIG_IGN) && + oldhand != CAST_FROM_FN_PTR(void*, (sa_sigaction_t)signalHandler)) { + if (AllowUserSignalHandlers || !set_installed) { + // Do not overwrite; user takes responsibility to forward to us. + return; + } else if (UseSignalChaining) { + // save the old handler in jvm + save_preinstalled_handler(sig, oldAct); + // libjsig also interposes the sigaction() call below and saves the + // old sigaction on it own. + } else { + fatal2("Encountered unexpected pre-existing sigaction handler %#lx for signal %d.", (long)oldhand, sig); + } + } + + struct sigaction sigAct; + sigfillset(&(sigAct.sa_mask)); + sigAct.sa_handler = SIG_DFL; + if (!set_installed) { + sigAct.sa_flags = SA_SIGINFO|SA_RESTART; + } else { + sigAct.sa_sigaction = signalHandler; + sigAct.sa_flags = SA_SIGINFO|SA_RESTART; + } + // Save flags, which are set by ours + assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); + sigflags[sig] = sigAct.sa_flags; + + int ret = sigaction(sig, &sigAct, &oldAct); + assert(ret == 0, "check"); + + void* oldhand2 = oldAct.sa_sigaction + ? CAST_FROM_FN_PTR(void*, oldAct.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oldAct.sa_handler); + assert(oldhand2 == oldhand, "no concurrent signal handler installation"); +} + +// install signal handlers for signals that HotSpot needs to +// handle in order to support Java-level exception handling. + +void os::Linux::install_signal_handlers() { + if (!signal_handlers_are_installed) { + signal_handlers_are_installed = true; + + // signal-chaining + typedef void (*signal_setting_t)(); + signal_setting_t begin_signal_setting = NULL; + signal_setting_t end_signal_setting = NULL; + begin_signal_setting = CAST_TO_FN_PTR(signal_setting_t, + dlsym(RTLD_DEFAULT, "JVM_begin_signal_setting")); + if (begin_signal_setting != NULL) { + end_signal_setting = CAST_TO_FN_PTR(signal_setting_t, + dlsym(RTLD_DEFAULT, "JVM_end_signal_setting")); + get_signal_action = CAST_TO_FN_PTR(get_signal_t, + dlsym(RTLD_DEFAULT, "JVM_get_signal_action")); + libjsig_is_loaded = true; + assert(UseSignalChaining, "should enable signal-chaining"); + } + if (libjsig_is_loaded) { + // Tell libjsig jvm is setting signal handlers + (*begin_signal_setting)(); + } + + set_signal_handler(SIGSEGV, true); + set_signal_handler(SIGPIPE, true); + set_signal_handler(SIGBUS, true); + set_signal_handler(SIGILL, true); + set_signal_handler(SIGFPE, true); + set_signal_handler(SIGXFSZ, true); + + if (libjsig_is_loaded) { + // Tell libjsig jvm finishes setting signal handlers + (*end_signal_setting)(); + } + + // We don't activate signal checker if libjsig is in place, we trust ourselves + // and if UserSignalHandler is installed all bets are off + if (CheckJNICalls) { + if (libjsig_is_loaded) { + tty->print_cr("Info: libjsig is activated, all active signal checking is disabled"); + check_signals = false; + } + if (AllowUserSignalHandlers) { + tty->print_cr("Info: AllowUserSignalHandlers is activated, all active signal checking is disabled"); + check_signals = false; + } + } + } +} + +// This is the fastest way to get thread cpu time on Linux. +// Returns cpu time (user+sys) for any thread, not only for current. +// POSIX compliant clocks are implemented in the kernels 2.6.16+. +// It might work on 2.6.10+ with a special kernel/glibc patch. +// For reference, please, see IEEE Std 1003.1-2004: +// http://www.unix.org/single_unix_specification + +jlong os::Linux::fast_thread_cpu_time(clockid_t clockid) { + struct timespec tp; + int rc = os::Linux::clock_gettime(clockid, &tp); + assert(rc == 0, "clock_gettime is expected to return 0 code"); + + return (tp.tv_sec * SEC_IN_NANOSECS) + tp.tv_nsec; +} + +///// +// glibc on Linux platform uses non-documented flag +// to indicate, that some special sort of signal +// trampoline is used. +// We will never set this flag, and we should +// ignore this flag in our diagnostic +#ifdef SIGNIFICANT_SIGNAL_MASK +#undef SIGNIFICANT_SIGNAL_MASK +#endif +#define SIGNIFICANT_SIGNAL_MASK (~0x04000000) + +static const char* get_signal_handler_name(address handler, + char* buf, int buflen) { + int offset; + bool found = os::dll_address_to_library_name(handler, buf, buflen, &offset); + if (found) { + // skip directory names + const char *p1, *p2; + p1 = buf; + size_t len = strlen(os::file_separator()); + while ((p2 = strstr(p1, os::file_separator())) != NULL) p1 = p2 + len; + jio_snprintf(buf, buflen, "%s+0x%x", p1, offset); + } else { + jio_snprintf(buf, buflen, PTR_FORMAT, handler); + } + return buf; +} + +static void print_signal_handler(outputStream* st, int sig, + char* buf, size_t buflen) { + struct sigaction sa; + + sigaction(sig, NULL, &sa); + + // See comment for SIGNIFICANT_SIGNAL_MASK define + sa.sa_flags &= SIGNIFICANT_SIGNAL_MASK; + + st->print("%s: ", os::exception_name(sig, buf, buflen)); + + address handler = (sa.sa_flags & SA_SIGINFO) + ? CAST_FROM_FN_PTR(address, sa.sa_sigaction) + : CAST_FROM_FN_PTR(address, sa.sa_handler); + + if (handler == CAST_FROM_FN_PTR(address, SIG_DFL)) { + st->print("SIG_DFL"); + } else if (handler == CAST_FROM_FN_PTR(address, SIG_IGN)) { + st->print("SIG_IGN"); + } else { + st->print("[%s]", get_signal_handler_name(handler, buf, buflen)); + } + + st->print(", sa_mask[0]=" PTR32_FORMAT, *(uint32_t*)&sa.sa_mask); + + address rh = VMError::get_resetted_sighandler(sig); + // May be, handler was resetted by VMError? + if(rh != NULL) { + handler = rh; + sa.sa_flags = VMError::get_resetted_sigflags(sig) & SIGNIFICANT_SIGNAL_MASK; + } + + st->print(", sa_flags=" PTR32_FORMAT, sa.sa_flags); + + // Check: is it our handler? + if(handler == CAST_FROM_FN_PTR(address, (sa_sigaction_t)signalHandler) || + handler == CAST_FROM_FN_PTR(address, (sa_sigaction_t)SR_handler)) { + // It is our signal handler + // check for flags, reset system-used one! + if((int)sa.sa_flags != os::Linux::get_our_sigflags(sig)) { + st->print( + ", flags was changed from " PTR32_FORMAT ", consider using jsig library", + os::Linux::get_our_sigflags(sig)); + } + } + st->cr(); +} + + +#define DO_SIGNAL_CHECK(sig) \ + if (!sigismember(&check_signal_done, sig)) \ + os::Linux::check_signal_handler(sig) + +// This method is a periodic task to check for misbehaving JNI applications +// under CheckJNI, we can add any periodic checks here + +void os::run_periodic_checks() { + + if (check_signals == false) return; + + // SEGV and BUS if overridden could potentially prevent + // generation of hs*.log in the event of a crash, debugging + // such a case can be very challenging, so we absolutely + // check the following for a good measure: + DO_SIGNAL_CHECK(SIGSEGV); + DO_SIGNAL_CHECK(SIGILL); + DO_SIGNAL_CHECK(SIGFPE); + DO_SIGNAL_CHECK(SIGBUS); + DO_SIGNAL_CHECK(SIGPIPE); + DO_SIGNAL_CHECK(SIGXFSZ); + + + // ReduceSignalUsage allows the user to override these handlers + // see comments at the very top and jvm_solaris.h + if (!ReduceSignalUsage) { + DO_SIGNAL_CHECK(SHUTDOWN1_SIGNAL); + DO_SIGNAL_CHECK(SHUTDOWN2_SIGNAL); + DO_SIGNAL_CHECK(SHUTDOWN3_SIGNAL); + DO_SIGNAL_CHECK(BREAK_SIGNAL); + } + + DO_SIGNAL_CHECK(SR_signum); + DO_SIGNAL_CHECK(INTERRUPT_SIGNAL); +} + +typedef int (*os_sigaction_t)(int, const struct sigaction *, struct sigaction *); + +static os_sigaction_t os_sigaction = NULL; + +void os::Linux::check_signal_handler(int sig) { + char buf[O_BUFLEN]; + address jvmHandler = NULL; + + + struct sigaction act; + if (os_sigaction == NULL) { + // only trust the default sigaction, in case it has been interposed + os_sigaction = (os_sigaction_t)dlsym(RTLD_DEFAULT, "sigaction"); + if (os_sigaction == NULL) return; + } + + os_sigaction(sig, (struct sigaction*)NULL, &act); + + + act.sa_flags &= SIGNIFICANT_SIGNAL_MASK; + + address thisHandler = (act.sa_flags & SA_SIGINFO) + ? CAST_FROM_FN_PTR(address, act.sa_sigaction) + : CAST_FROM_FN_PTR(address, act.sa_handler) ; + + + switch(sig) { + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGPIPE: + case SIGILL: + case SIGXFSZ: + jvmHandler = CAST_FROM_FN_PTR(address, (sa_sigaction_t)signalHandler); + break; + + case SHUTDOWN1_SIGNAL: + case SHUTDOWN2_SIGNAL: + case SHUTDOWN3_SIGNAL: + case BREAK_SIGNAL: + jvmHandler = (address)user_handler(); + break; + + case INTERRUPT_SIGNAL: + jvmHandler = CAST_FROM_FN_PTR(address, SIG_DFL); + break; + + default: + if (sig == SR_signum) { + jvmHandler = CAST_FROM_FN_PTR(address, (sa_sigaction_t)SR_handler); + } else { + return; + } + break; + } + + if (thisHandler != jvmHandler) { + tty->print("Warning: %s handler ", exception_name(sig, buf, O_BUFLEN)); + tty->print("expected:%s", get_signal_handler_name(jvmHandler, buf, O_BUFLEN)); + tty->print_cr(" found:%s", get_signal_handler_name(thisHandler, buf, O_BUFLEN)); + // No need to check this sig any longer + sigaddset(&check_signal_done, sig); + } else if(os::Linux::get_our_sigflags(sig) != 0 && (int)act.sa_flags != os::Linux::get_our_sigflags(sig)) { + tty->print("Warning: %s handler flags ", exception_name(sig, buf, O_BUFLEN)); + tty->print("expected:" PTR32_FORMAT, os::Linux::get_our_sigflags(sig)); + tty->print_cr(" found:" PTR32_FORMAT, act.sa_flags); + // No need to check this sig any longer + sigaddset(&check_signal_done, sig); + } + + // Dump all the signal + if (sigismember(&check_signal_done, sig)) { + print_signal_handlers(tty, buf, O_BUFLEN); + } +} + +extern void report_error(char* file_name, int line_no, char* title, char* format, ...); + +extern bool signal_name(int signo, char* buf, size_t len); + +const char* os::exception_name(int exception_code, char* buf, size_t size) { + if (0 < exception_code && exception_code <= SIGRTMAX) { + // signal + if (!signal_name(exception_code, buf, size)) { + jio_snprintf(buf, size, "SIG%d", exception_code); + } + return buf; + } else { + return NULL; + } +} + +// this is called _before_ the most of global arguments have been parsed +void os::init(void) { + char dummy; /* used to get a guess on initial stack address */ +// first_hrtime = gethrtime(); + + // With LinuxThreads the JavaMain thread pid (primordial thread) + // is different than the pid of the java launcher thread. + // So, on Linux, the launcher thread pid is passed to the VM + // via the sun.java.launcher.pid property. + // Use this property instead of getpid() if it was correctly passed. + // See bug 6351349. + pid_t java_launcher_pid = (pid_t) Arguments::sun_java_launcher_pid(); + + _initial_pid = (java_launcher_pid > 0) ? java_launcher_pid : getpid(); + + clock_tics_per_sec = sysconf(_SC_CLK_TCK); + + init_random(1234567); + + ThreadCritical::initialize(); + + Linux::set_page_size(sysconf(_SC_PAGESIZE)); + if (Linux::page_size() == -1) { + fatal1("os_linux.cpp: os::init: sysconf failed (%s)", strerror(errno)); + } + init_page_sizes((size_t) Linux::page_size()); + + Linux::initialize_system_info(); + + // main_thread points to the aboriginal thread + Linux::_main_thread = pthread_self(); + + Linux::clock_init(); + initial_time_count = os::elapsed_counter(); +} + +// To install functions for atexit system call +extern "C" { + static void perfMemory_exit_helper() { + perfMemory_exit(); + } +} + +// this is called _after_ the global arguments have been parsed +jint os::init_2(void) +{ + Linux::fast_thread_clock_init(); + + // Allocate a single page and mark it as readable for safepoint polling + address polling_page = (address) ::mmap(NULL, Linux::page_size(), PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + guarantee( polling_page != MAP_FAILED, "os::init_2: failed to allocate polling page" ); + + os::set_polling_page( polling_page ); + +#ifndef PRODUCT + if(Verbose && PrintMiscellaneous) + tty->print("[SafePoint Polling address: " INTPTR_FORMAT "]\n", (intptr_t)polling_page); +#endif + + if (!UseMembar) { + address mem_serialize_page = (address) ::mmap(NULL, Linux::page_size(), PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + guarantee( mem_serialize_page != NULL, "mmap Failed for memory serialize page"); + os::set_memory_serialize_page( mem_serialize_page ); + +#ifndef PRODUCT + if(Verbose && PrintMiscellaneous) + tty->print("[Memory Serialize Page address: " INTPTR_FORMAT "]\n", (intptr_t)mem_serialize_page); +#endif + } + + FLAG_SET_DEFAULT(UseLargePages, os::large_page_init()); + + // initialize suspend/resume support - must do this before signal_sets_init() + if (SR_initialize() != 0) { + perror("SR_initialize failed"); + return JNI_ERR; + } + + Linux::signal_sets_init(); + Linux::install_signal_handlers(); + + size_t threadStackSizeInBytes = ThreadStackSize * K; + if (threadStackSizeInBytes != 0 && + threadStackSizeInBytes < Linux::min_stack_allowed) { + tty->print_cr("\nThe stack size specified is too small, " + "Specify at least %dk", + Linux::min_stack_allowed / K); + return JNI_ERR; + } + + // Make the stack size a multiple of the page size so that + // the yellow/red zones can be guarded. + JavaThread::set_stack_size_at_create(round_to(threadStackSizeInBytes, + vm_page_size())); + + Linux::capture_initial_stack(JavaThread::stack_size_at_create()); + + Linux::libpthread_init(); + if (PrintMiscellaneous && (Verbose || WizardMode)) { + tty->print_cr("[HotSpot is running with %s, %s(%s)]\n", + Linux::glibc_version(), Linux::libpthread_version(), + Linux::is_floating_stack() ? "floating stack" : "fixed stack"); + } + + if (MaxFDLimit) { + // set the number of file descriptors to max. print out error + // if getrlimit/setrlimit fails but continue regardless. + struct rlimit nbr_files; + int status = getrlimit(RLIMIT_NOFILE, &nbr_files); + if (status != 0) { + if (PrintMiscellaneous && (Verbose || WizardMode)) + perror("os::init_2 getrlimit failed"); + } else { + nbr_files.rlim_cur = nbr_files.rlim_max; + status = setrlimit(RLIMIT_NOFILE, &nbr_files); + if (status != 0) { + if (PrintMiscellaneous && (Verbose || WizardMode)) + perror("os::init_2 setrlimit failed"); + } + } + } + + // Initialize lock used to serialize thread creation (see os::create_thread) + Linux::set_createThread_lock(new Mutex(Mutex::leaf, "createThread_lock", false)); + + // Initialize HPI. + jint hpi_result = hpi::initialize(); + if (hpi_result != JNI_OK) { + tty->print_cr("There was an error trying to initialize the HPI library."); + return hpi_result; + } + + // at-exit methods are called in the reverse order of their registration. + // atexit functions are called on return from main or as a result of a + // call to exit(3C). There can be only 32 of these functions registered + // and atexit() does not set errno. + + if (PerfAllowAtExitRegistration) { + // only register atexit functions if PerfAllowAtExitRegistration is set. + // atexit functions can be delayed until process exit time, which + // can be problematic for embedded VM situations. Embedded VMs should + // call DestroyJavaVM() to assure that VM resources are released. + + // note: perfMemory_exit_helper atexit function may be removed in + // the future if the appropriate cleanup code can be added to the + // VM_Exit VMOperation's doit method. + if (atexit(perfMemory_exit_helper) != 0) { + warning("os::init2 atexit(perfMemory_exit_helper) failed"); + } + } + + // initialize thread priority policy + prio_init(); + + return JNI_OK; +} + +// Mark the polling page as unreadable +void os::make_polling_page_unreadable(void) { + if( !guard_memory((char*)_polling_page, Linux::page_size()) ) + fatal("Could not disable polling page"); +}; + +// Mark the polling page as readable +void os::make_polling_page_readable(void) { + if( !protect_memory((char *)_polling_page, Linux::page_size()) ) + fatal("Could not enable polling page"); +}; + +int os::active_processor_count() { + // Linux doesn't yet have a (official) notion of processor sets, + // so just return the number of online processors. + int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); + assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); + return online_cpus; +} + +bool os::distribute_processes(uint length, uint* distribution) { + // Not yet implemented. + return false; +} + +bool os::bind_to_processor(uint processor_id) { + // Not yet implemented. + return false; +} + +/// + +// Suspends the target using the signal mechanism and then grabs the PC before +// resuming the target. Used by the flat-profiler only +ExtendedPC os::get_thread_pc(Thread* thread) { + // Make sure that it is called by the watcher for the VMThread + assert(Thread::current()->is_Watcher_thread(), "Must be watcher"); + assert(thread->is_VM_thread(), "Can only be called for VMThread"); + + ExtendedPC epc; + + OSThread* osthread = thread->osthread(); + if (do_suspend(osthread)) { + if (osthread->ucontext() != NULL) { + epc = os::Linux::ucontext_get_pc(osthread->ucontext()); + } else { + // NULL context is unexpected, double-check this is the VMThread + guarantee(thread->is_VM_thread(), "can only be called for VMThread"); + } + do_resume(osthread); + } + // failure means pthread_kill failed for some reason - arguably this is + // a fatal problem, but such problems are ignored elsewhere + + return epc; +} + +int os::Linux::safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, const struct timespec *_abstime) +{ + if (is_NPTL()) { + return pthread_cond_timedwait(_cond, _mutex, _abstime); + } else { +#ifndef IA64 + // 6292965: LinuxThreads pthread_cond_timedwait() resets FPU control + // word back to default 64bit precision if condvar is signaled. Java + // wants 53bit precision. Save and restore current value. + int fpu = get_fpu_control_word(); +#endif // IA64 + int status = pthread_cond_timedwait(_cond, _mutex, _abstime); +#ifndef IA64 + set_fpu_control_word(fpu); +#endif // IA64 + return status; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// debug support + +#ifndef PRODUCT +static address same_page(address x, address y) { + int page_bits = -os::vm_page_size(); + if ((intptr_t(x) & page_bits) == (intptr_t(y) & page_bits)) + return x; + else if (x > y) + return (address)(intptr_t(y) | ~page_bits) + 1; + else + return (address)(intptr_t(y) & page_bits); +} + +bool os::find(address addr) { + Dl_info dlinfo; + memset(&dlinfo, 0, sizeof(dlinfo)); + if (dladdr(addr, &dlinfo)) { + tty->print(PTR_FORMAT ": ", addr); + if (dlinfo.dli_sname != NULL) { + tty->print("%s+%#x", dlinfo.dli_sname, + addr - (intptr_t)dlinfo.dli_saddr); + } else if (dlinfo.dli_fname) { + tty->print("", addr - (intptr_t)dlinfo.dli_fbase); + } else { + tty->print(""); + } + if (dlinfo.dli_fname) { + tty->print(" in %s", dlinfo.dli_fname); + } + if (dlinfo.dli_fbase) { + tty->print(" at " PTR_FORMAT, dlinfo.dli_fbase); + } + tty->cr(); + + if (Verbose) { + // decode some bytes around the PC + address begin = same_page(addr-40, addr); + address end = same_page(addr+40, addr); + address lowest = (address) dlinfo.dli_sname; + if (!lowest) lowest = (address) dlinfo.dli_fbase; + if (begin < lowest) begin = lowest; + Dl_info dlinfo2; + if (dladdr(end, &dlinfo2) && dlinfo2.dli_saddr != dlinfo.dli_saddr + && end > dlinfo2.dli_saddr && dlinfo2.dli_saddr > begin) + end = (address) dlinfo2.dli_saddr; + Disassembler::decode(begin, end); + } + return true; + } + return false; +} + +#endif + +//////////////////////////////////////////////////////////////////////////////// +// misc + +// This does not do anything on Linux. This is basically a hook for being +// able to use structured exception handling (thread-local exception filters) +// on, e.g., Win32. +void +os::os_exception_wrapper(java_call_t f, JavaValue* value, methodHandle* method, + JavaCallArguments* args, Thread* thread) { + f(value, method, args, thread); +} + +void os::print_statistics() { +} + +int os::message_box(const char* title, const char* message) { + int i; + fdStream err(defaultStream::error_fd()); + for (i = 0; i < 78; i++) err.print_raw("="); + err.cr(); + err.print_raw_cr(title); + for (i = 0; i < 78; i++) err.print_raw("-"); + err.cr(); + err.print_raw_cr(message); + for (i = 0; i < 78; i++) err.print_raw("="); + err.cr(); + + char buf[16]; + // Prevent process from exiting upon "read error" without consuming all CPU + while (::read(0, buf, sizeof(buf)) <= 0) { ::sleep(100); } + + return buf[0] == 'y' || buf[0] == 'Y'; +} + +int os::stat(const char *path, struct stat *sbuf) { + char pathbuf[MAX_PATH]; + if (strlen(path) > MAX_PATH - 1) { + errno = ENAMETOOLONG; + return -1; + } + hpi::native_path(strcpy(pathbuf, path)); + return ::stat(pathbuf, sbuf); +} + +bool os::check_heap(bool force) { + return true; +} + +int local_vsnprintf(char* buf, size_t count, const char* format, va_list args) { + return ::vsnprintf(buf, count, format, args); +} + +// Is a (classpath) directory empty? +bool os::dir_is_empty(const char* path) { + DIR *dir = NULL; + struct dirent *ptr; + + dir = opendir(path); + if (dir == NULL) return true; + + /* Scan the directory */ + bool result = true; + char buf[sizeof(struct dirent) + MAX_PATH]; + while (result && (ptr = ::readdir(dir)) != NULL) { + if (strcmp(ptr->d_name, ".") != 0 && strcmp(ptr->d_name, "..") != 0) { + result = false; + } + } + closedir(dir); + return result; +} + +// create binary file, rewriting existing file if required +int os::create_binary_file(const char* path, bool rewrite_existing) { + int oflags = O_WRONLY | O_CREAT; + if (!rewrite_existing) { + oflags |= O_EXCL; + } + return ::open64(path, oflags, S_IREAD | S_IWRITE); +} + +// return current position of file pointer +jlong os::current_file_offset(int fd) { + return (jlong)::lseek64(fd, (off64_t)0, SEEK_CUR); +} + +// move file pointer to the specified offset +jlong os::seek_to_file_offset(int fd, jlong offset) { + return (jlong)::lseek64(fd, (off64_t)offset, SEEK_SET); +} + +// Map a block of memory. +char* os::map_memory(int fd, const char* file_name, size_t file_offset, + char *addr, size_t bytes, bool read_only, + bool allow_exec) { + int prot; + int flags; + + if (read_only) { + prot = PROT_READ; + flags = MAP_SHARED; + } else { + prot = PROT_READ | PROT_WRITE; + flags = MAP_PRIVATE; + } + + if (allow_exec) { + prot |= PROT_EXEC; + } + + if (addr != NULL) { + flags |= MAP_FIXED; + } + + char* mapped_address = (char*)mmap(addr, (size_t)bytes, prot, flags, + fd, file_offset); + if (mapped_address == MAP_FAILED) { + return NULL; + } + return mapped_address; +} + + +// Remap a block of memory. +char* os::remap_memory(int fd, const char* file_name, size_t file_offset, + char *addr, size_t bytes, bool read_only, + bool allow_exec) { + // same as map_memory() on this OS + return os::map_memory(fd, file_name, file_offset, addr, bytes, read_only, + allow_exec); +} + + +// Unmap a block of memory. +bool os::unmap_memory(char* addr, size_t bytes) { + return munmap(addr, bytes) == 0; +} + +static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time); + +static clockid_t thread_cpu_clockid(Thread* thread) { + pthread_t tid = thread->osthread()->pthread_id(); + clockid_t clockid; + + // Get thread clockid + int rc = os::Linux::pthread_getcpuclockid(tid, &clockid); + assert(rc == 0, "pthread_getcpuclockid is expected to return 0 code"); + return clockid; +} + +// current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) +// are used by JVM M&M and JVMTI to get user+sys or user CPU time +// of a thread. +// +// current_thread_cpu_time() and thread_cpu_time(Thread*) returns +// the fast estimate available on the platform. + +jlong os::current_thread_cpu_time() { + if (os::Linux::supports_fast_thread_cpu_time()) { + return os::Linux::fast_thread_cpu_time(CLOCK_THREAD_CPUTIME_ID); + } else { + // return user + sys since the cost is the same + return slow_thread_cpu_time(Thread::current(), true /* user + sys */); + } +} + +jlong os::thread_cpu_time(Thread* thread) { + // consistent with what current_thread_cpu_time() returns + if (os::Linux::supports_fast_thread_cpu_time()) { + return os::Linux::fast_thread_cpu_time(thread_cpu_clockid(thread)); + } else { + return slow_thread_cpu_time(thread, true /* user + sys */); + } +} + +jlong os::current_thread_cpu_time(bool user_sys_cpu_time) { + if (user_sys_cpu_time && os::Linux::supports_fast_thread_cpu_time()) { + return os::Linux::fast_thread_cpu_time(CLOCK_THREAD_CPUTIME_ID); + } else { + return slow_thread_cpu_time(Thread::current(), user_sys_cpu_time); + } +} + +jlong os::thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { + if (user_sys_cpu_time && os::Linux::supports_fast_thread_cpu_time()) { + return os::Linux::fast_thread_cpu_time(thread_cpu_clockid(thread)); + } else { + return slow_thread_cpu_time(thread, user_sys_cpu_time); + } +} + +// +// -1 on error. +// + +static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { + static bool proc_pid_cpu_avail = true; + static bool proc_task_unchecked = true; + static const char *proc_stat_path = "/proc/%d/stat"; + pid_t tid = thread->osthread()->thread_id(); + int i; + char *s; + char stat[2048]; + int statlen; + char proc_name[64]; + int count; + long sys_time, user_time; + char string[64]; + int idummy; + long ldummy; + FILE *fp; + + // We first try accessing /proc//cpu since this is faster to + // process. If this file is not present (linux kernels 2.5 and above) + // then we open /proc//stat. + if ( proc_pid_cpu_avail ) { + sprintf(proc_name, "/proc/%d/cpu", tid); + fp = fopen(proc_name, "r"); + if ( fp != NULL ) { + count = fscanf( fp, "%s %lu %lu\n", string, &user_time, &sys_time); + fclose(fp); + if ( count != 3 ) return -1; + + if (user_sys_cpu_time) { + return ((jlong)sys_time + (jlong)user_time) * (1000000000 / clock_tics_per_sec); + } else { + return (jlong)user_time * (1000000000 / clock_tics_per_sec); + } + } + else proc_pid_cpu_avail = false; + } + + // The /proc//stat aggregates per-process usage on + // new Linux kernels 2.6+ where NPTL is supported. + // The /proc/self/task//stat still has the per-thread usage. + // See bug 6328462. + // There can be no directory /proc/self/task on kernels 2.4 with NPTL + // and possibly in some other cases, so we check its availability. + if (proc_task_unchecked && os::Linux::is_NPTL()) { + // This is executed only once + proc_task_unchecked = false; + fp = fopen("/proc/self/task", "r"); + if (fp != NULL) { + proc_stat_path = "/proc/self/task/%d/stat"; + fclose(fp); + } + } + + sprintf(proc_name, proc_stat_path, tid); + fp = fopen(proc_name, "r"); + if ( fp == NULL ) return -1; + statlen = fread(stat, 1, 2047, fp); + stat[statlen] = '\0'; + fclose(fp); + + // Skip pid and the command string. Note that we could be dealing with + // weird command names, e.g. user could decide to rename java launcher + // to "java 1.4.2 :)", then the stat file would look like + // 1234 (java 1.4.2 :)) R ... ... + // We don't really need to know the command string, just find the last + // occurrence of ")" and then start parsing from there. See bug 4726580. + s = strrchr(stat, ')'); + i = 0; + if (s == NULL ) return -1; + + // Skip blank chars + do s++; while (isspace(*s)); + + count = sscanf(s,"%c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu", + &idummy, &idummy, &idummy, &idummy, &idummy, &idummy, + &ldummy, &ldummy, &ldummy, &ldummy, &ldummy, + &user_time, &sys_time); + if ( count != 13 ) return -1; + if (user_sys_cpu_time) { + return ((jlong)sys_time + (jlong)user_time) * (1000000000 / clock_tics_per_sec); + } else { + return (jlong)user_time * (1000000000 / clock_tics_per_sec); + } +} + +void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { + info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + info_ptr->may_skip_backward = false; // elapsed time not wall time + info_ptr->may_skip_forward = false; // elapsed time not wall time + info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned +} + +void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { + info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + info_ptr->may_skip_backward = false; // elapsed time not wall time + info_ptr->may_skip_forward = false; // elapsed time not wall time + info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned +} + +bool os::is_thread_cpu_time_supported() { + return true; +} + +// System loadavg support. Returns -1 if load average cannot be obtained. +// Linux doesn't yet have a (official) notion of processor sets, +// so just return the system wide load average. +int os::loadavg(double loadavg[], int nelem) { + return ::getloadavg(loadavg, nelem); +} + +void os::pause() { + char filename[MAX_PATH]; + if (PauseAtStartupFile && PauseAtStartupFile[0]) { + jio_snprintf(filename, MAX_PATH, PauseAtStartupFile); + } else { + jio_snprintf(filename, MAX_PATH, "./vm.paused.%d", current_process_id()); + } + + int fd = ::open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd != -1) { + struct stat buf; + close(fd); + while (::stat(filename, &buf) == 0) { + (void)::poll(NULL, 0, 100); + } + } else { + jio_fprintf(stderr, + "Could not open pause file '%s', continuing immediately.\n", filename); + } +} + +extern "C" { + +/** + * NOTE: the following code is to keep the green threads code + * in the libjava.so happy. Once the green threads is removed, + * these code will no longer be needed. + */ +int +jdk_waitpid(pid_t pid, int* status, int options) { + return waitpid(pid, status, options); +} + +int +fork1() { + return fork(); +} + +int +jdk_sem_init(sem_t *sem, int pshared, unsigned int value) { + return sem_init(sem, pshared, value); +} + +int +jdk_sem_post(sem_t *sem) { + return sem_post(sem); +} + +int +jdk_sem_wait(sem_t *sem) { + return sem_wait(sem); +} + +int +jdk_pthread_sigmask(int how , const sigset_t* newmask, sigset_t* oldmask) { + return pthread_sigmask(how , newmask, oldmask); +} + +} + +// Refer to the comments in os_solaris.cpp park-unpark. +// +// Beware -- Some versions of NPTL embody a flaw where pthread_cond_timedwait() can +// hang indefinitely. For instance NPTL 0.60 on 2.4.21-4ELsmp is vulnerable. +// For specifics regarding the bug see GLIBC BUGID 261237 : +// http://www.mail-archive.com/debian-glibc@lists.debian.org/msg10837.html. +// Briefly, pthread_cond_timedwait() calls with an expiry time that's not in the future +// will either hang or corrupt the condvar, resulting in subsequent hangs if the condvar +// is used. (The simple C test-case provided in the GLIBC bug report manifests the +// hang). The JVM is vulernable via sleep(), Object.wait(timo), LockSupport.parkNanos() +// and monitorenter when we're using 1-0 locking. All those operations may result in +// calls to pthread_cond_timedwait(). Using LD_ASSUME_KERNEL to use an older version +// of libpthread avoids the problem, but isn't practical. +// +// Possible remedies: +// +// 1. Establish a minimum relative wait time. 50 to 100 msecs seems to work. +// This is palliative and probabilistic, however. If the thread is preempted +// between the call to compute_abstime() and pthread_cond_timedwait(), more +// than the minimum period may have passed, and the abstime may be stale (in the +// past) resultin in a hang. Using this technique reduces the odds of a hang +// but the JVM is still vulnerable, particularly on heavily loaded systems. +// +// 2. Modify park-unpark to use per-thread (per ParkEvent) pipe-pairs instead +// of the usual flag-condvar-mutex idiom. The write side of the pipe is set +// NDELAY. unpark() reduces to write(), park() reduces to read() and park(timo) +// reduces to poll()+read(). This works well, but consumes 2 FDs per extant +// thread. +// +// 3. Embargo pthread_cond_timedwait() and implement a native "chron" thread +// that manages timeouts. We'd emulate pthread_cond_timedwait() by enqueuing +// a timeout request to the chron thread and then blocking via pthread_cond_wait(). +// This also works well. In fact it avoids kernel-level scalability impediments +// on certain platforms that don't handle lots of active pthread_cond_timedwait() +// timers in a graceful fashion. +// +// 4. When the abstime value is in the past it appears that control returns +// correctly from pthread_cond_timedwait(), but the condvar is left corrupt. +// Subsequent timedwait/wait calls may hang indefinitely. Given that, we +// can avoid the problem by reinitializing the condvar -- by cond_destroy() +// followed by cond_init() -- after all calls to pthread_cond_timedwait(). +// It may be possible to avoid reinitialization by checking the return +// value from pthread_cond_timedwait(). In addition to reinitializing the +// condvar we must establish the invariant that cond_signal() is only called +// within critical sections protected by the adjunct mutex. This prevents +// cond_signal() from "seeing" a condvar that's in the midst of being +// reinitialized or that is corrupt. Sadly, this invariant obviates the +// desirable signal-after-unlock optimization that avoids futile context switching. +// +// I'm also concerned that some versions of NTPL might allocate an auxilliary +// structure when a condvar is used or initialized. cond_destroy() would +// release the helper structure. Our reinitialize-after-timedwait fix +// put excessive stress on malloc/free and locks protecting the c-heap. +// +// We currently use (4). See the WorkAroundNTPLTimedWaitHang flag. +// It may be possible to refine (4) by checking the kernel and NTPL verisons +// and only enabling the work-around for vulnerable environments. + +// utility to compute the abstime argument to timedwait: +// millis is the relative timeout time +// abstime will be the absolute timeout time +// TODO: replace compute_abstime() with unpackTime() + +static struct timespec* compute_abstime(timespec* abstime, jlong millis) { + if (millis < 0) millis = 0; + struct timeval now; + int status = gettimeofday(&now, NULL); + assert(status == 0, "gettimeofday"); + jlong seconds = millis / 1000; + millis %= 1000; + if (seconds > 50000000) { // see man cond_timedwait(3T) + seconds = 50000000; + } + abstime->tv_sec = now.tv_sec + seconds; + long usec = now.tv_usec + millis * 1000; + if (usec >= 1000000) { + abstime->tv_sec += 1; + usec -= 1000000; + } + abstime->tv_nsec = usec * 1000; + return abstime; +} + + +// Test-and-clear _Event, always leaves _Event set to 0, returns immediately. +// Conceptually TryPark() should be equivalent to park(0). + +int os::PlatformEvent::TryPark() { + for (;;) { + const int v = _Event ; + guarantee ((v == 0) || (v == 1), "invariant") ; + if (Atomic::cmpxchg (0, &_Event, v) == v) return v ; + } +} + +void os::PlatformEvent::park() { // AKA "down()" + // Invariant: Only the thread associated with the Event/PlatformEvent + // may call park(). + // TODO: assert that _Assoc != NULL or _Assoc == Self + int v ; + for (;;) { + v = _Event ; + if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ; + } + guarantee (v >= 0, "invariant") ; + if (v == 0) { + // Do this the hard way by blocking ... + int status = pthread_mutex_lock(_mutex); + assert_status(status == 0, status, "mutex_lock"); + guarantee (_nParked == 0, "invariant") ; + ++ _nParked ; + while (_Event < 0) { + status = pthread_cond_wait(_cond, _mutex); + // for some reason, under 2.7 lwp_cond_wait() may return ETIME ... + // Treat this the same as if the wait was interrupted + if (status == ETIME) { status = EINTR; } + assert_status(status == 0 || status == EINTR, status, "cond_wait"); + } + -- _nParked ; + + // In theory we could move the ST of 0 into _Event past the unlock(), + // but then we'd need a MEMBAR after the ST. + _Event = 0 ; + status = pthread_mutex_unlock(_mutex); + assert_status(status == 0, status, "mutex_unlock"); + } + guarantee (_Event >= 0, "invariant") ; +} + +int os::PlatformEvent::park(jlong millis) { + guarantee (_nParked == 0, "invariant") ; + + int v ; + for (;;) { + v = _Event ; + if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ; + } + guarantee (v >= 0, "invariant") ; + if (v != 0) return OS_OK ; + + // We do this the hard way, by blocking the thread. + // Consider enforcing a minimum timeout value. + struct timespec abst; + compute_abstime(&abst, millis); + + int ret = OS_TIMEOUT; + int status = pthread_mutex_lock(_mutex); + assert_status(status == 0, status, "mutex_lock"); + guarantee (_nParked == 0, "invariant") ; + ++_nParked ; + + // Object.wait(timo) will return because of + // (a) notification + // (b) timeout + // (c) thread.interrupt + // + // Thread.interrupt and object.notify{All} both call Event::set. + // That is, we treat thread.interrupt as a special case of notification. + // The underlying Solaris implementation, cond_timedwait, admits + // spurious/premature wakeups, but the JLS/JVM spec prevents the + // JVM from making those visible to Java code. As such, we must + // filter out spurious wakeups. We assume all ETIME returns are valid. + // + // TODO: properly differentiate simultaneous notify+interrupt. + // In that case, we should propagate the notify to another waiter. + + while (_Event < 0) { + status = os::Linux::safe_cond_timedwait(_cond, _mutex, &abst); + if (status != 0 && WorkAroundNPTLTimedWaitHang) { + pthread_cond_destroy (_cond); + pthread_cond_init (_cond, NULL) ; + } + assert_status(status == 0 || status == EINTR || + status == ETIME || status == ETIMEDOUT, + status, "cond_timedwait"); + if (!FilterSpuriousWakeups) break ; // previous semantics + if (status == ETIME || status == ETIMEDOUT) break ; + // We consume and ignore EINTR and spurious wakeups. + } + --_nParked ; + if (_Event >= 0) { + ret = OS_OK; + } + _Event = 0 ; + status = pthread_mutex_unlock(_mutex); + assert_status(status == 0, status, "mutex_unlock"); + assert (_nParked == 0, "invariant") ; + return ret; +} + +void os::PlatformEvent::unpark() { + int v, AnyWaiters ; + for (;;) { + v = _Event ; + if (v > 0) { + // The LD of _Event could have reordered or be satisfied + // by a read-aside from this processor's write buffer. + // To avoid problems execute a barrier and then + // ratify the value. + OrderAccess::fence() ; + if (_Event == v) return ; + continue ; + } + if (Atomic::cmpxchg (v+1, &_Event, v) == v) break ; + } + if (v < 0) { + // Wait for the thread associated with the event to vacate + int status = pthread_mutex_lock(_mutex); + assert_status(status == 0, status, "mutex_lock"); + AnyWaiters = _nParked ; + assert (AnyWaiters == 0 || AnyWaiters == 1, "invariant") ; + if (AnyWaiters != 0 && WorkAroundNPTLTimedWaitHang) { + AnyWaiters = 0 ; + pthread_cond_signal (_cond); + } + status = pthread_mutex_unlock(_mutex); + assert_status(status == 0, status, "mutex_unlock"); + if (AnyWaiters != 0) { + status = pthread_cond_signal(_cond); + assert_status(status == 0, status, "cond_signal"); + } + } + + // Note that we signal() _after dropping the lock for "immortal" Events. + // This is safe and avoids a common class of futile wakeups. In rare + // circumstances this can cause a thread to return prematurely from + // cond_{timed}wait() but the spurious wakeup is benign and the victim will + // simply re-test the condition and re-park itself. +} + + +// JSR166 +// ------------------------------------------------------- + +/* + * The solaris and linux implementations of park/unpark are fairly + * conservative for now, but can be improved. They currently use a + * mutex/condvar pair, plus a a count. + * Park decrements count if > 0, else does a condvar wait. Unpark + * sets count to 1 and signals condvar. Only one thread ever waits + * on the condvar. Contention seen when trying to park implies that someone + * is unparking you, so don't wait. And spurious returns are fine, so there + * is no need to track notifications. + */ + + +#define NANOSECS_PER_SEC 1000000000 +#define NANOSECS_PER_MILLISEC 1000000 +#define MAX_SECS 100000000 +/* + * This code is common to linux and solaris and will be moved to a + * common place in dolphin. + * + * The passed in time value is either a relative time in nanoseconds + * or an absolute time in milliseconds. Either way it has to be unpacked + * into suitable seconds and nanoseconds components and stored in the + * given timespec structure. + * Given time is a 64-bit value and the time_t used in the timespec is only + * a signed-32-bit value (except on 64-bit Linux) we have to watch for + * overflow if times way in the future are given. Further on Solaris versions + * prior to 10 there is a restriction (see cond_timedwait) that the specified + * number of seconds, in abstime, is less than current_time + 100,000,000. + * As it will be 28 years before "now + 100000000" will overflow we can + * ignore overflow and just impose a hard-limit on seconds using the value + * of "now + 100,000,000". This places a limit on the timeout of about 3.17 + * years from "now". + */ + +static void unpackTime(timespec* absTime, bool isAbsolute, jlong time) { + assert (time > 0, "convertTime"); + + struct timeval now; + int status = gettimeofday(&now, NULL); + assert(status == 0, "gettimeofday"); + + time_t max_secs = now.tv_sec + MAX_SECS; + + if (isAbsolute) { + jlong secs = time / 1000; + if (secs > max_secs) { + absTime->tv_sec = max_secs; + } + else { + absTime->tv_sec = secs; + } + absTime->tv_nsec = (time % 1000) * NANOSECS_PER_MILLISEC; + } + else { + jlong secs = time / NANOSECS_PER_SEC; + if (secs >= MAX_SECS) { + absTime->tv_sec = max_secs; + absTime->tv_nsec = 0; + } + else { + absTime->tv_sec = now.tv_sec + secs; + absTime->tv_nsec = (time % NANOSECS_PER_SEC) + now.tv_usec*1000; + if (absTime->tv_nsec >= NANOSECS_PER_SEC) { + absTime->tv_nsec -= NANOSECS_PER_SEC; + ++absTime->tv_sec; // note: this must be <= max_secs + } + } + } + assert(absTime->tv_sec >= 0, "tv_sec < 0"); + assert(absTime->tv_sec <= max_secs, "tv_sec > max_secs"); + assert(absTime->tv_nsec >= 0, "tv_nsec < 0"); + assert(absTime->tv_nsec < NANOSECS_PER_SEC, "tv_nsec >= nanos_per_sec"); +} + +void Parker::park(bool isAbsolute, jlong time) { + // Optional fast-path check: + // Return immediately if a permit is available. + if (_counter > 0) { + _counter = 0 ; + return ; + } + + Thread* thread = Thread::current(); + assert(thread->is_Java_thread(), "Must be JavaThread"); + JavaThread *jt = (JavaThread *)thread; + + // Optional optimization -- avoid state transitions if there's an interrupt pending. + // Check interrupt before trying to wait + if (Thread::is_interrupted(thread, false)) { + return; + } + + // Next, demultiplex/decode time arguments + timespec absTime; + if (time < 0) { // don't wait at all + return; + } + if (time > 0) { + unpackTime(&absTime, isAbsolute, time); + } + + + // Enter safepoint region + // Beware of deadlocks such as 6317397. + // The per-thread Parker:: mutex is a classic leaf-lock. + // In particular a thread must never block on the Threads_lock while + // holding the Parker:: mutex. If safepoints are pending both the + // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock. + ThreadBlockInVM tbivm(jt); + + // Don't wait if cannot get lock since interference arises from + // unblocking. Also. check interrupt before trying wait + if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) { + return; + } + + int status ; + if (_counter > 0) { // no wait needed + _counter = 0; + status = pthread_mutex_unlock(_mutex); + assert (status == 0, "invariant") ; + return; + } + +#ifdef ASSERT + // Don't catch signals while blocked; let the running threads have the signals. + // (This allows a debugger to break into the running thread.) + sigset_t oldsigs; + sigset_t* allowdebug_blocked = os::Linux::allowdebug_blocked_signals(); + pthread_sigmask(SIG_BLOCK, allowdebug_blocked, &oldsigs); +#endif + + OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); + jt->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() + + if (time == 0) { + status = pthread_cond_wait (_cond, _mutex) ; + } else { + status = os::Linux::safe_cond_timedwait (_cond, _mutex, &absTime) ; + if (status != 0 && WorkAroundNPTLTimedWaitHang) { + pthread_cond_destroy (_cond) ; + pthread_cond_init (_cond, NULL); + } + } + assert_status(status == 0 || status == EINTR || + status == ETIME || status == ETIMEDOUT, + status, "cond_timedwait"); + +#ifdef ASSERT + pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); +#endif + + _counter = 0 ; + status = pthread_mutex_unlock(_mutex) ; + assert_status(status == 0, status, "invariant") ; + // If externally suspended while waiting, re-suspend + if (jt->handle_special_suspend_equivalent_condition()) { + jt->java_suspend_self(); + } + +} + +void Parker::unpark() { + int s, status ; + status = pthread_mutex_lock(_mutex); + assert (status == 0, "invariant") ; + s = _counter; + _counter = 1; + if (s < 1) { + if (WorkAroundNPTLTimedWaitHang) { + status = pthread_cond_signal (_cond) ; + assert (status == 0, "invariant") ; + status = pthread_mutex_unlock(_mutex); + assert (status == 0, "invariant") ; + } else { + status = pthread_mutex_unlock(_mutex); + assert (status == 0, "invariant") ; + status = pthread_cond_signal (_cond) ; + assert (status == 0, "invariant") ; + } + } else { + pthread_mutex_unlock(_mutex); + assert (status == 0, "invariant") ; + } +} + + +extern char** environ; + +#ifndef __NR_fork +#define __NR_fork IA32_ONLY(2) IA64_ONLY(not defined) AMD64_ONLY(57) +#endif + +#ifndef __NR_execve +#define __NR_execve IA32_ONLY(11) IA64_ONLY(1033) AMD64_ONLY(59) +#endif + +// Run the specified command in a separate process. Return its exit value, +// or -1 on failure (e.g. can't fork a new process). +// Unlike system(), this function can be called from signal handler. It +// doesn't block SIGINT et al. +int os::fork_and_exec(char* cmd) { + char * argv[4]; + argv[0] = "sh"; + argv[1] = "-c"; + argv[2] = cmd; + argv[3] = NULL; + + // fork() in LinuxThreads/NPTL is not async-safe. It needs to run + // pthread_atfork handlers and reset pthread library. All we need is a + // separate process to execve. Make a direct syscall to fork process. + // On IA64 there's no fork syscall, we have to use fork() and hope for + // the best... + pid_t pid = NOT_IA64(syscall(__NR_fork);) + IA64_ONLY(fork();) + + if (pid < 0) { + // fork failed + return -1; + + } else if (pid == 0) { + // child process + + // execve() in LinuxThreads will call pthread_kill_other_threads_np() + // first to kill every thread on the thread list. Because this list is + // not reset by fork() (see notes above), execve() will instead kill + // every thread in the parent process. We know this is the only thread + // in the new process, so make a system call directly. + // IA64 should use normal execve() from glibc to match the glibc fork() + // above. + NOT_IA64(syscall(__NR_execve, "/bin/sh", argv, environ);) + IA64_ONLY(execve("/bin/sh", argv, environ);) + + // execve failed + _exit(-1); + + } else { + // copied from J2SE ..._waitForProcessExit() in UNIXProcess_md.c; we don't + // care about the actual exit code, for now. + + int status; + + // Wait for the child process to exit. This returns immediately if + // the child has already exited. */ + while (waitpid(pid, &status, 0) < 0) { + switch (errno) { + case ECHILD: return 0; + case EINTR: break; + default: return -1; + } + } + + if (WIFEXITED(status)) { + // The child exited normally; get its exit code. + return WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + // The child exited because of a signal + // The best value to return is 0x80 + signal number, + // because that is what all Unix shells do, and because + // it allows callers to distinguish between process exit and + // process death by signal. + return 0x80 + WTERMSIG(status); + } else { + // Unknown exit code; pass it through + return status; + } + } +} diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp new file mode 100644 index 00000000000..e3a204dc6cf --- /dev/null +++ b/hotspot/src/os/linux/vm/os_linux.hpp @@ -0,0 +1,286 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Linux_OS defines the interface to Linux operating systems + +/* pthread_getattr_np comes with LinuxThreads-0.9-7 on RedHat 7.1 */ +typedef int (*pthread_getattr_func_type) (pthread_t, pthread_attr_t *); + +class Linux { + friend class os; + + // For signal-chaining +#define MAXSIGNUM 32 + static struct sigaction sigact[MAXSIGNUM]; // saved preinstalled sigactions + static unsigned int sigs; // mask of signals that have + // preinstalled signal handlers + static bool libjsig_is_loaded; // libjsig that interposes sigaction(), + // __sigaction(), signal() is loaded + static struct sigaction *(*get_signal_action)(int); + static struct sigaction *get_preinstalled_handler(int); + static void save_preinstalled_handler(int, struct sigaction&); + + static void check_signal_handler(int sig); + + // For signal flags diagnostics + static int sigflags[MAXSIGNUM]; + + static int (*_clock_gettime)(clockid_t, struct timespec *); + static int (*_pthread_getcpuclockid)(pthread_t, clockid_t *); + + static address _initial_thread_stack_bottom; + static uintptr_t _initial_thread_stack_size; + + static char *_glibc_version; + static char *_libpthread_version; + + static bool _is_floating_stack; + static bool _is_NPTL; + static bool _supports_fast_thread_cpu_time; + + protected: + + static julong _physical_memory; + static pthread_t _main_thread; + static Mutex* _createThread_lock; + static int _page_size; + + static julong available_memory(); + static julong physical_memory() { return _physical_memory; } + static void initialize_system_info(); + + static void set_glibc_version(char *s) { _glibc_version = s; } + static void set_libpthread_version(char *s) { _libpthread_version = s; } + + static bool supports_variable_stack_size(); + + static void set_is_NPTL() { _is_NPTL = true; } + static void set_is_LinuxThreads() { _is_NPTL = false; } + static void set_is_floating_stack() { _is_floating_stack = true; } + + public: + + static void init_thread_fpu_state(); + static int get_fpu_control_word(); + static void set_fpu_control_word(int fpu_control); + static pthread_t main_thread(void) { return _main_thread; } + // returns kernel thread id (similar to LWP id on Solaris), which can be + // used to access /proc + static pid_t gettid(); + static void set_createThread_lock(Mutex* lk) { _createThread_lock = lk; } + static Mutex* createThread_lock(void) { return _createThread_lock; } + static void hotspot_sigmask(Thread* thread); + + static address initial_thread_stack_bottom(void) { return _initial_thread_stack_bottom; } + static uintptr_t initial_thread_stack_size(void) { return _initial_thread_stack_size; } + static bool is_initial_thread(void); + + static int page_size(void) { return _page_size; } + static void set_page_size(int val) { _page_size = val; } + + static address ucontext_get_pc(ucontext_t* uc); + static intptr_t* ucontext_get_sp(ucontext_t* uc); + static intptr_t* ucontext_get_fp(ucontext_t* uc); + + // For Analyzer Forte AsyncGetCallTrace profiling support: + // + // This interface should be declared in os_linux_i486.hpp, but + // that file provides extensions to the os class and not the + // Linux class. + static ExtendedPC fetch_frame_from_ucontext(Thread* thread, ucontext_t* uc, + intptr_t** ret_sp, intptr_t** ret_fp); + + // This boolean allows users to forward their own non-matching signals + // to JVM_handle_linux_signal, harmlessly. + static bool signal_handlers_are_installed; + + static int get_our_sigflags(int); + static void set_our_sigflags(int, int); + static void signal_sets_init(); + static void install_signal_handlers(); + static void set_signal_handler(int, bool); + static bool is_sig_ignored(int sig); + + static sigset_t* unblocked_signals(); + static sigset_t* vm_signals(); + static sigset_t* allowdebug_blocked_signals(); + + // For signal-chaining + static struct sigaction *get_chained_signal_action(int sig); + static bool chained_handler(int sig, siginfo_t* siginfo, void* context); + + // GNU libc and libpthread version strings + static char *glibc_version() { return _glibc_version; } + static char *libpthread_version() { return _libpthread_version; } + + // NPTL or LinuxThreads? + static bool is_LinuxThreads() { return !_is_NPTL; } + static bool is_NPTL() { return _is_NPTL; } + + // NPTL is always floating stack. LinuxThreads could be using floating + // stack or fixed stack. + static bool is_floating_stack() { return _is_floating_stack; } + + static void libpthread_init(); + + // Minimum stack size a thread can be created with (allowing + // the VM to completely create the thread and enter user code) + static size_t min_stack_allowed; + + // Return default stack size or guard size for the specified thread type + static size_t default_stack_size(os::ThreadType thr_type); + static size_t default_guard_size(os::ThreadType thr_type); + + static void capture_initial_stack(size_t max_size); + + // Stack overflow handling + static bool manually_expand_stack(JavaThread * t, address addr); + static int max_register_window_saves_before_flushing(); + + // Real-time clock functions + static void clock_init(void); + + // fast POSIX clocks support + static void fast_thread_clock_init(void); + + static bool supports_monotonic_clock() { + return _clock_gettime != NULL; + } + + static int clock_gettime(clockid_t clock_id, struct timespec *tp) { + return _clock_gettime ? _clock_gettime(clock_id, tp) : -1; + } + + static int pthread_getcpuclockid(pthread_t tid, clockid_t *clock_id) { + return _pthread_getcpuclockid ? _pthread_getcpuclockid(tid, clock_id) : -1; + } + + static bool supports_fast_thread_cpu_time() { + return _supports_fast_thread_cpu_time; + } + + static jlong fast_thread_cpu_time(clockid_t clockid); + + // Stack repair handling + + // none present + + // LinuxThreads work-around for 6292965 + static int safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, const struct timespec *_abstime); + + + // Linux suspend/resume support - this helper is a shadow of its former + // self now that low-level suspension is barely used, and old workarounds + // for LinuxThreads are no longer needed. + class SuspendResume { + private: + volatile int _suspend_action; + // values for suspend_action: + #define SR_NONE (0x00) + #define SR_SUSPEND (0x01) // suspend request + #define SR_CONTINUE (0x02) // resume request + + volatile jint _state; + // values for _state: + SR_NONE + #define SR_SUSPENDED (0x20) + public: + SuspendResume() { _suspend_action = SR_NONE; _state = SR_NONE; } + + int suspend_action() const { return _suspend_action; } + void set_suspend_action(int x) { _suspend_action = x; } + + // atomic updates for _state + void set_suspended() { + jint temp, temp2; + do { + temp = _state; + temp2 = Atomic::cmpxchg(temp | SR_SUSPENDED, &_state, temp); + } while (temp2 != temp); + } + void clear_suspended() { + jint temp, temp2; + do { + temp = _state; + temp2 = Atomic::cmpxchg(temp & ~SR_SUSPENDED, &_state, temp); + } while (temp2 != temp); + } + bool is_suspended() { return _state & SR_SUSPENDED; } + + #undef SR_SUSPENDED + }; +}; + + +class PlatformEvent : public CHeapObj { + private: + double CachePad [4] ; // increase odds that _mutex is sole occupant of cache line + volatile int _Event ; + volatile int _nParked ; + pthread_mutex_t _mutex [1] ; + pthread_cond_t _cond [1] ; + double PostPad [2] ; + Thread * _Assoc ; + + public: // TODO-FIXME: make dtor private + ~PlatformEvent() { guarantee (0, "invariant") ; } + + public: + PlatformEvent() { + int status; + status = pthread_cond_init (_cond, NULL); + assert_status(status == 0, status, "cond_init"); + status = pthread_mutex_init (_mutex, NULL); + assert_status(status == 0, status, "mutex_init"); + _Event = 0 ; + _nParked = 0 ; + _Assoc = NULL ; + } + + // Use caution with reset() and fired() -- they may require MEMBARs + void reset() { _Event = 0 ; } + int fired() { return _Event; } + void park () ; + void unpark () ; + int TryPark () ; + int park (jlong millis) ; + void SetAssociation (Thread * a) { _Assoc = a ; } +} ; + +class PlatformParker : public CHeapObj { + protected: + pthread_mutex_t _mutex [1] ; + pthread_cond_t _cond [1] ; + + public: // TODO-FIXME: make dtor private + ~PlatformParker() { guarantee (0, "invariant") ; } + + public: + PlatformParker() { + int status; + status = pthread_cond_init (_cond, NULL); + assert_status(status == 0, status, "cond_init"); + status = pthread_mutex_init (_mutex, NULL); + assert_status(status == 0, status, "mutex_init"); + } +} ; diff --git a/hotspot/src/os/linux/vm/os_linux.inline.hpp b/hotspot/src/os/linux/vm/os_linux.inline.hpp new file mode 100644 index 00000000000..55985cb3a34 --- /dev/null +++ b/hotspot/src/os/linux/vm/os_linux.inline.hpp @@ -0,0 +1,122 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void* os::thread_local_storage_at(int index) { + return pthread_getspecific((pthread_key_t)index); +} + +inline const char* os::file_separator() { + return "/"; +} + +inline const char* os::line_separator() { + return "\n"; +} + +inline const char* os::path_separator() { + return ":"; +} + +inline const char* os::jlong_format_specifier() { + return "%lld"; +} + +inline const char* os::julong_format_specifier() { + return "%llu"; +} + +// File names are case-sensitive on windows only +inline int os::file_name_strcmp(const char* s1, const char* s2) { + return strcmp(s1, s2); +} + +inline bool os::obsolete_option(const JavaVMOption *option) { + return false; +} + +inline bool os::uses_stack_guard_pages() { + return true; +} + +inline bool os::allocate_stack_guard_pages() { + assert(uses_stack_guard_pages(), "sanity check"); + return true; +} + + +// On Linux, reservations are made on a page by page basis, nothing to do. +inline void os::split_reserved_memory(char *base, size_t size, + size_t split, bool realloc) { +} + + +// Bang the shadow pages if they need to be touched to be mapped. +inline void os::bang_stack_shadow_pages() { +} + +inline DIR* os::opendir(const char* dirname) +{ + assert(dirname != NULL, "just checking"); + return ::opendir(dirname); +} + +inline int os::readdir_buf_size(const char *path) +{ + return NAME_MAX + sizeof(dirent) + 1; +} + +inline struct dirent* os::readdir(DIR* dirp, dirent *dbuf) +{ + dirent* p; + int status; + assert(dirp != NULL, "just checking"); + + // NOTE: Linux readdir_r (on RH 6.2 and 7.2 at least) is NOT like the POSIX + // version. Here is the doc for this function: + // http://www.gnu.org/manual/glibc-2.2.3/html_node/libc_262.html + + if((status = ::readdir_r(dirp, dbuf, &p)) != 0) { + errno = status; + return NULL; + } else + return p; +} + +inline int os::closedir(DIR *dirp) +{ + assert(dirp != NULL, "just checking"); + return ::closedir(dirp); +} + +// macros for restartable system calls + +#define RESTARTABLE(_cmd, _result) do { \ + _result = _cmd; \ + } while(((int)_result == OS_ERR) && (errno == EINTR)) + +#define RESTARTABLE_RETURN_INT(_cmd) do { \ + int _result; \ + RESTARTABLE(_cmd, _result); \ + return _result; \ +} while(false) diff --git a/hotspot/src/os/linux/vm/os_share_linux.hpp b/hotspot/src/os/linux/vm/os_share_linux.hpp new file mode 100644 index 00000000000..b1d9bc5c5ba --- /dev/null +++ b/hotspot/src/os/linux/vm/os_share_linux.hpp @@ -0,0 +1,32 @@ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// misc +void signalHandler(int, siginfo_t*, ucontext_t*); +void handle_unexpected_exception(Thread* thread, int sig, siginfo_t* info, address pc, address adjusted_pc); +#ifndef PRODUCT +void continue_with_dump(void); +#endif + +#define PROCFILE_LENGTH 128 diff --git a/hotspot/src/os/linux/vm/perfMemory_linux.cpp b/hotspot/src/os/linux/vm/perfMemory_linux.cpp new file mode 100644 index 00000000000..c56798c0187 --- /dev/null +++ b/hotspot/src/os/linux/vm/perfMemory_linux.cpp @@ -0,0 +1,1011 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_perfMemory_linux.cpp.incl" + +// put OS-includes here +# include +# include +# include +# include +# include +# include +# include +# include + +static char* backing_store_file_name = NULL; // name of the backing store + // file, if successfully created. + +// Standard Memory Implementation Details + +// create the PerfData memory region in standard memory. +// +static char* create_standard_memory(size_t size) { + + // allocate an aligned chuck of memory + char* mapAddress = os::reserve_memory(size); + + if (mapAddress == NULL) { + return NULL; + } + + // commit memory + if (!os::commit_memory(mapAddress, size)) { + if (PrintMiscellaneous && Verbose) { + warning("Could not commit PerfData memory\n"); + } + os::release_memory(mapAddress, size); + return NULL; + } + + return mapAddress; +} + +// delete the PerfData memory region +// +static void delete_standard_memory(char* addr, size_t size) { + + // there are no persistent external resources to cleanup for standard + // memory. since DestroyJavaVM does not support unloading of the JVM, + // cleanup of the memory resource is not performed. The memory will be + // reclaimed by the OS upon termination of the process. + // + return; +} + +// save the specified memory region to the given file +// +// Note: this function might be called from signal handler (by os::abort()), +// don't allocate heap memory. +// +static void save_memory_to_file(char* addr, size_t size) { + + const char* destfile = PerfMemory::get_perfdata_file_path(); + assert(destfile[0] != '\0', "invalid PerfData file path"); + + int result; + + RESTARTABLE(::open(destfile, O_CREAT|O_WRONLY|O_TRUNC, S_IREAD|S_IWRITE), + result);; + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("Could not create Perfdata save file: %s: %s\n", + destfile, strerror(errno)); + } + } else { + int fd = result; + + for (size_t remaining = size; remaining > 0;) { + + RESTARTABLE(::write(fd, addr, remaining), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("Could not write Perfdata save file: %s: %s\n", + destfile, strerror(errno)); + } + break; + } + + remaining -= (size_t)result; + addr += result; + } + + RESTARTABLE(::close(fd), result); + if (PrintMiscellaneous && Verbose) { + if (result == OS_ERR) { + warning("Could not close %s: %s\n", destfile, strerror(errno)); + } + } + } + FREE_C_HEAP_ARRAY(char, destfile); +} + + +// Shared Memory Implementation Details + +// Note: the solaris and linux shared memory implementation uses the mmap +// interface with a backing store file to implement named shared memory. +// Using the file system as the name space for shared memory allows a +// common name space to be supported across a variety of platforms. It +// also provides a name space that Java applications can deal with through +// simple file apis. +// +// The solaris and linux implementations store the backing store file in +// a user specific temporary directory located in the /tmp file system, +// which is always a local file system and is sometimes a RAM based file +// system. + +// return the user specific temporary directory name. +// +// the caller is expected to free the allocated memory. +// +static char* get_user_tmp_dir(const char* user) { + + const char* tmpdir = os::get_temp_directory(); + const char* perfdir = PERFDATA_NAME; + size_t nbytes = strlen(tmpdir) + strlen(perfdir) + strlen(user) + 2; + char* dirname = NEW_C_HEAP_ARRAY(char, nbytes); + + // construct the path name to user specific tmp directory + snprintf(dirname, nbytes, "%s%s_%s", tmpdir, perfdir, user); + + return dirname; +} + +// convert the given file name into a process id. if the file +// does not meet the file naming constraints, return 0. +// +static pid_t filename_to_pid(const char* filename) { + + // a filename that doesn't begin with a digit is not a + // candidate for conversion. + // + if (!isdigit(*filename)) { + return 0; + } + + // check if file name can be converted to an integer without + // any leftover characters. + // + char* remainder = NULL; + errno = 0; + pid_t pid = (pid_t)strtol(filename, &remainder, 10); + + if (errno != 0) { + return 0; + } + + // check for left over characters. If any, then the filename is + // not a candidate for conversion. + // + if (remainder != NULL && *remainder != '\0') { + return 0; + } + + // successful conversion, return the pid + return pid; +} + + +// check if the given path is considered a secure directory for +// the backing store files. Returns true if the directory exists +// and is considered a secure location. Returns false if the path +// is a symbolic link or if an error occured. +// +static bool is_directory_secure(const char* path) { + struct stat statbuf; + int result = 0; + + RESTARTABLE(::lstat(path, &statbuf), result); + if (result == OS_ERR) { + return false; + } + + // the path exists, now check it's mode + if (S_ISLNK(statbuf.st_mode) || !S_ISDIR(statbuf.st_mode)) { + // the path represents a link or some non-directory file type, + // which is not what we expected. declare it insecure. + // + return false; + } + else { + // we have an existing directory, check if the permissions are safe. + // + if ((statbuf.st_mode & (S_IWGRP|S_IWOTH)) != 0) { + // the directory is open for writing and could be subjected + // to a symlnk attack. declare it insecure. + // + return false; + } + } + return true; +} + + +// return the user name for the given user id +// +// the caller is expected to free the allocated memory. +// +static char* get_user_name(uid_t uid) { + + struct passwd pwent; + + // determine the max pwbuf size from sysconf, and hardcode + // a default if this not available through sysconf. + // + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize == -1) + bufsize = 1024; + + char* pwbuf = NEW_C_HEAP_ARRAY(char, bufsize); + + // POSIX interface to getpwuid_r is used on LINUX + struct passwd* p; + int result = getpwuid_r(uid, &pwent, pwbuf, (size_t)bufsize, &p); + + if (result != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') { + if (PrintMiscellaneous && Verbose) { + if (result != 0) { + warning("Could not retrieve passwd entry: %s\n", + strerror(result)); + } + else if (p == NULL) { + // this check is added to protect against an observed problem + // with getpwuid_r() on RedHat 9 where getpwuid_r returns 0, + // indicating success, but has p == NULL. This was observed when + // inserting a file descriptor exhaustion fault prior to the call + // getpwuid_r() call. In this case, error is set to the appropriate + // error condition, but this is undocumented behavior. This check + // is safe under any condition, but the use of errno in the output + // message may result in an erroneous message. + // Bug Id 89052 was opened with RedHat. + // + warning("Could not retrieve passwd entry: %s\n", + strerror(errno)); + } + else { + warning("Could not determine user name: %s\n", + p->pw_name == NULL ? "pw_name = NULL" : + "pw_name zero length"); + } + } + FREE_C_HEAP_ARRAY(char, pwbuf); + return NULL; + } + + char* user_name = NEW_C_HEAP_ARRAY(char, strlen(p->pw_name) + 1); + strcpy(user_name, p->pw_name); + + FREE_C_HEAP_ARRAY(char, pwbuf); + return user_name; +} + +// return the name of the user that owns the process identified by vmid. +// +// This method uses a slow directory search algorithm to find the backing +// store file for the specified vmid and returns the user name, as determined +// by the user name suffix of the hsperfdata_ directory name. +// +// the caller is expected to free the allocated memory. +// +static char* get_user_name_slow(int vmid, TRAPS) { + + // short circuit the directory search if the process doesn't even exist. + if (kill(vmid, 0) == OS_ERR) { + if (errno == ESRCH) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); + } + else /* EPERM */ { + THROW_MSG_0(vmSymbols::java_io_IOException(), strerror(errno)); + } + } + + // directory search + char* oldest_user = NULL; + time_t oldest_ctime = 0; + + const char* tmpdirname = os::get_temp_directory(); + + DIR* tmpdirp = os::opendir(tmpdirname); + + if (tmpdirp == NULL) { + return NULL; + } + + // for each entry in the directory that matches the pattern hsperfdata_*, + // open the directory and check if the file for the given vmid exists. + // The file with the expected name and the latest creation date is used + // to determine the user name for the process id. + // + struct dirent* dentry; + char* tdbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(tmpdirname)); + errno = 0; + while ((dentry = os::readdir(tmpdirp, (struct dirent *)tdbuf)) != NULL) { + + // check if the directory entry is a hsperfdata file + if (strncmp(dentry->d_name, PERFDATA_NAME, strlen(PERFDATA_NAME)) != 0) { + continue; + } + + char* usrdir_name = NEW_C_HEAP_ARRAY(char, + strlen(tmpdirname) + strlen(dentry->d_name) + 1); + strcpy(usrdir_name, tmpdirname); + strcat(usrdir_name, dentry->d_name); + + DIR* subdirp = os::opendir(usrdir_name); + + if (subdirp == NULL) { + FREE_C_HEAP_ARRAY(char, usrdir_name); + continue; + } + + // Since we don't create the backing store files in directories + // pointed to by symbolic links, we also don't follow them when + // looking for the files. We check for a symbolic link after the + // call to opendir in order to eliminate a small window where the + // symlink can be exploited. + // + if (!is_directory_secure(usrdir_name)) { + FREE_C_HEAP_ARRAY(char, usrdir_name); + os::closedir(subdirp); + continue; + } + + struct dirent* udentry; + char* udbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(usrdir_name)); + errno = 0; + while ((udentry = os::readdir(subdirp, (struct dirent *)udbuf)) != NULL) { + + if (filename_to_pid(udentry->d_name) == vmid) { + struct stat statbuf; + int result; + + char* filename = NEW_C_HEAP_ARRAY(char, + strlen(usrdir_name) + strlen(udentry->d_name) + 2); + + strcpy(filename, usrdir_name); + strcat(filename, "/"); + strcat(filename, udentry->d_name); + + // don't follow symbolic links for the file + RESTARTABLE(::lstat(filename, &statbuf), result); + if (result == OS_ERR) { + FREE_C_HEAP_ARRAY(char, filename); + continue; + } + + // skip over files that are not regular files. + if (!S_ISREG(statbuf.st_mode)) { + FREE_C_HEAP_ARRAY(char, filename); + continue; + } + + // compare and save filename with latest creation time + if (statbuf.st_size > 0 && statbuf.st_ctime > oldest_ctime) { + + if (statbuf.st_ctime > oldest_ctime) { + char* user = strchr(dentry->d_name, '_') + 1; + + if (oldest_user != NULL) FREE_C_HEAP_ARRAY(char, oldest_user); + oldest_user = NEW_C_HEAP_ARRAY(char, strlen(user)+1); + + strcpy(oldest_user, user); + oldest_ctime = statbuf.st_ctime; + } + } + + FREE_C_HEAP_ARRAY(char, filename); + } + } + os::closedir(subdirp); + FREE_C_HEAP_ARRAY(char, udbuf); + FREE_C_HEAP_ARRAY(char, usrdir_name); + } + os::closedir(tmpdirp); + FREE_C_HEAP_ARRAY(char, tdbuf); + + return(oldest_user); +} + +// return the name of the user that owns the JVM indicated by the given vmid. +// +static char* get_user_name(int vmid, TRAPS) { + return get_user_name_slow(vmid, CHECK_NULL); +} + +// return the file name of the backing store file for the named +// shared memory region for the given user name and vmid. +// +// the caller is expected to free the allocated memory. +// +static char* get_sharedmem_filename(const char* dirname, int vmid) { + + // add 2 for the file separator and a null terminator. + size_t nbytes = strlen(dirname) + UINT_CHARS + 2; + + char* name = NEW_C_HEAP_ARRAY(char, nbytes); + snprintf(name, nbytes, "%s/%d", dirname, vmid); + + return name; +} + + +// remove file +// +// this method removes the file specified by the given path +// +static void remove_file(const char* path) { + + int result; + + // if the file is a directory, the following unlink will fail. since + // we don't expect to find directories in the user temp directory, we + // won't try to handle this situation. even if accidentially or + // maliciously planted, the directory's presence won't hurt anything. + // + RESTARTABLE(::unlink(path), result); + if (PrintMiscellaneous && Verbose && result == OS_ERR) { + if (errno != ENOENT) { + warning("Could not unlink shared memory backing" + " store file %s : %s\n", path, strerror(errno)); + } + } +} + + +// remove file +// +// this method removes the file with the given file name in the +// named directory. +// +static void remove_file(const char* dirname, const char* filename) { + + size_t nbytes = strlen(dirname) + strlen(filename) + 2; + char* path = NEW_C_HEAP_ARRAY(char, nbytes); + + strcpy(path, dirname); + strcat(path, "/"); + strcat(path, filename); + + remove_file(path); + + FREE_C_HEAP_ARRAY(char, path); +} + + +// cleanup stale shared memory resources +// +// This method attempts to remove all stale shared memory files in +// the named user temporary directory. It scans the named directory +// for files matching the pattern ^$[0-9]*$. For each file found, the +// process id is extracted from the file name and a test is run to +// determine if the process is alive. If the process is not alive, +// any stale file resources are removed. +// +static void cleanup_sharedmem_resources(const char* dirname) { + + // open the user temp directory + DIR* dirp = os::opendir(dirname); + + if (dirp == NULL) { + // directory doesn't exist, so there is nothing to cleanup + return; + } + + if (!is_directory_secure(dirname)) { + // the directory is not a secure directory + return; + } + + // for each entry in the directory that matches the expected file + // name pattern, determine if the file resources are stale and if + // so, remove the file resources. Note, instrumented HotSpot processes + // for this user may start and/or terminate during this search and + // remove or create new files in this directory. The behavior of this + // loop under these conditions is dependent upon the implementation of + // opendir/readdir. + // + struct dirent* entry; + char* dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(dirname)); + errno = 0; + while ((entry = os::readdir(dirp, (struct dirent *)dbuf)) != NULL) { + + pid_t pid = filename_to_pid(entry->d_name); + + if (pid == 0) { + + if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { + + // attempt to remove all unexpected files, except "." and ".." + remove_file(dirname, entry->d_name); + } + + errno = 0; + continue; + } + + // we now have a file name that converts to a valid integer + // that could represent a process id . if this process id + // matches the current process id or the process is not running, + // then remove the stale file resources. + // + // process liveness is detected by sending signal number 0 to + // the process id (see kill(2)). if kill determines that the + // process does not exist, then the file resources are removed. + // if kill determines that that we don't have permission to + // signal the process, then the file resources are assumed to + // be stale and are removed because the resources for such a + // process should be in a different user specific directory. + // + if ((pid == os::current_process_id()) || + (kill(pid, 0) == OS_ERR && (errno == ESRCH || errno == EPERM))) { + + remove_file(dirname, entry->d_name); + } + errno = 0; + } + os::closedir(dirp); + FREE_C_HEAP_ARRAY(char, dbuf); +} + +// make the user specific temporary directory. Returns true if +// the directory exists and is secure upon return. Returns false +// if the directory exists but is either a symlink, is otherwise +// insecure, or if an error occurred. +// +static bool make_user_tmp_dir(const char* dirname) { + + // create the directory with 0755 permissions. note that the directory + // will be owned by euid::egid, which may not be the same as uid::gid. + // + if (mkdir(dirname, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) == OS_ERR) { + if (errno == EEXIST) { + // The directory already exists and was probably created by another + // JVM instance. However, this could also be the result of a + // deliberate symlink. Verify that the existing directory is safe. + // + if (!is_directory_secure(dirname)) { + // directory is not secure + if (PrintMiscellaneous && Verbose) { + warning("%s directory is insecure\n", dirname); + } + return false; + } + } + else { + // we encountered some other failure while attempting + // to create the directory + // + if (PrintMiscellaneous && Verbose) { + warning("could not create directory %s: %s\n", + dirname, strerror(errno)); + } + return false; + } + } + return true; +} + +// create the shared memory file resources +// +// This method creates the shared memory file with the given size +// This method also creates the user specific temporary directory, if +// it does not yet exist. +// +static int create_sharedmem_resources(const char* dirname, const char* filename, size_t size) { + + // make the user temporary directory + if (!make_user_tmp_dir(dirname)) { + // could not make/find the directory or the found directory + // was not secure + return -1; + } + + int result; + + RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("could not create file %s: %s\n", filename, strerror(errno)); + } + return -1; + } + + // save the file descriptor + int fd = result; + + // set the file size + RESTARTABLE(::ftruncate(fd, (off_t)size), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("could not set shared memory file size: %s\n", strerror(errno)); + } + RESTARTABLE(::close(fd), result); + return -1; + } + + return fd; +} + +// open the shared memory file for the given user and vmid. returns +// the file descriptor for the open file or -1 if the file could not +// be opened. +// +static int open_sharedmem_file(const char* filename, int oflags, TRAPS) { + + // open the file + int result; + RESTARTABLE(::open(filename, oflags), result); + if (result == OS_ERR) { + if (errno == ENOENT) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); + } + else if (errno == EACCES) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + "Permission denied"); + } + else { + THROW_MSG_0(vmSymbols::java_io_IOException(), strerror(errno)); + } + } + + return result; +} + +// create a named shared memory region. returns the address of the +// memory region on success or NULL on failure. A return value of +// NULL will ultimately disable the shared memory feature. +// +// On Solaris and Linux, the name space for shared memory objects +// is the file system name space. +// +// A monitoring application attaching to a JVM does not need to know +// the file system name of the shared memory object. However, it may +// be convenient for applications to discover the existence of newly +// created and terminating JVMs by watching the file system name space +// for files being created or removed. +// +static char* mmap_create_shared(size_t size) { + + int result; + int fd; + char* mapAddress; + + int vmid = os::current_process_id(); + + char* user_name = get_user_name(geteuid()); + + if (user_name == NULL) + return NULL; + + char* dirname = get_user_tmp_dir(user_name); + char* filename = get_sharedmem_filename(dirname, vmid); + + // cleanup any stale shared memory files + cleanup_sharedmem_resources(dirname); + + assert(((size > 0) && (size % os::vm_page_size() == 0)), + "unexpected PerfMemory region size"); + + fd = create_sharedmem_resources(dirname, filename, size); + + FREE_C_HEAP_ARRAY(char, user_name); + FREE_C_HEAP_ARRAY(char, dirname); + + if (fd == -1) { + FREE_C_HEAP_ARRAY(char, filename); + return NULL; + } + + mapAddress = (char*)::mmap((char*)0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + // attempt to close the file - restart it if it was interrupted, + // but ignore other failures + RESTARTABLE(::close(fd), result); + assert(result != OS_ERR, "could not close file"); + + if (mapAddress == MAP_FAILED) { + if (PrintMiscellaneous && Verbose) { + warning("mmap failed - %s\n", strerror(errno)); + } + remove_file(filename); + FREE_C_HEAP_ARRAY(char, filename); + return NULL; + } + + // save the file name for use in delete_shared_memory() + backing_store_file_name = filename; + + // clear the shared memory region + (void)::memset((void*) mapAddress, 0, size); + + return mapAddress; +} + +// release a named shared memory region +// +static void unmap_shared(char* addr, size_t bytes) { + os::release_memory(addr, bytes); +} + +// create the PerfData memory region in shared memory. +// +static char* create_shared_memory(size_t size) { + + // create the shared memory region. + return mmap_create_shared(size); +} + +// delete the shared PerfData memory region +// +static void delete_shared_memory(char* addr, size_t size) { + + // cleanup the persistent shared memory resources. since DestroyJavaVM does + // not support unloading of the JVM, unmapping of the memory resource is + // not performed. The memory will be reclaimed by the OS upon termination of + // the process. The backing store file is deleted from the file system. + + assert(!PerfDisableSharedMem, "shouldn't be here"); + + if (backing_store_file_name != NULL) { + remove_file(backing_store_file_name); + // Don't.. Free heap memory could deadlock os::abort() if it is called + // from signal handler. OS will reclaim the heap memory. + // FREE_C_HEAP_ARRAY(char, backing_store_file_name); + backing_store_file_name = NULL; + } +} + +// return the size of the file for the given file descriptor +// or 0 if it is not a valid size for a shared memory file +// +static size_t sharedmem_filesize(int fd, TRAPS) { + + struct stat statbuf; + int result; + + RESTARTABLE(::fstat(fd, &statbuf), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("fstat failed: %s\n", strerror(errno)); + } + THROW_MSG_0(vmSymbols::java_io_IOException(), + "Could not determine PerfMemory size"); + } + + if ((statbuf.st_size == 0) || + ((size_t)statbuf.st_size % os::vm_page_size() != 0)) { + THROW_MSG_0(vmSymbols::java_lang_Exception(), + "Invalid PerfMemory size"); + } + + return (size_t)statbuf.st_size; +} + +// attach to a named shared memory region. +// +static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemoryMode mode, char** addr, size_t* sizep, TRAPS) { + + char* mapAddress; + int result; + int fd; + size_t size; + const char* luser = NULL; + + int mmap_prot; + int file_flags; + + ResourceMark rm; + + // map the high level access mode to the appropriate permission + // constructs for the file and the shared memory mapping. + if (mode == PerfMemory::PERF_MODE_RO) { + mmap_prot = PROT_READ; + file_flags = O_RDONLY; + } + else if (mode == PerfMemory::PERF_MODE_RW) { +#ifdef LATER + mmap_prot = PROT_READ | PROT_WRITE; + file_flags = O_RDWR; +#else + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Unsupported access mode"); +#endif + } + else { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Illegal access mode"); + } + + if (user == NULL || strlen(user) == 0) { + luser = get_user_name(vmid, CHECK); + } + else { + luser = user; + } + + if (luser == NULL) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Could not map vmid to user Name"); + } + + char* dirname = get_user_tmp_dir(luser); + + // since we don't follow symbolic links when creating the backing + // store file, we don't follow them when attaching either. + // + if (!is_directory_secure(dirname)) { + FREE_C_HEAP_ARRAY(char, dirname); + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); + } + + char* filename = get_sharedmem_filename(dirname, vmid); + + // copy heap memory to resource memory. the open_sharedmem_file + // method below need to use the filename, but could throw an + // exception. using a resource array prevents the leak that + // would otherwise occur. + char* rfilename = NEW_RESOURCE_ARRAY(char, strlen(filename) + 1); + strcpy(rfilename, filename); + + // free the c heap resources that are no longer needed + if (luser != user) FREE_C_HEAP_ARRAY(char, luser); + FREE_C_HEAP_ARRAY(char, dirname); + FREE_C_HEAP_ARRAY(char, filename); + + // open the shared memory file for the give vmid + fd = open_sharedmem_file(rfilename, file_flags, CHECK); + assert(fd != OS_ERR, "unexpected value"); + + if (*sizep == 0) { + size = sharedmem_filesize(fd, CHECK); + assert(size != 0, "unexpected size"); + } + + mapAddress = (char*)::mmap((char*)0, size, mmap_prot, MAP_SHARED, fd, 0); + + // attempt to close the file - restart if it gets interrupted, + // but ignore other failures + RESTARTABLE(::close(fd), result); + assert(result != OS_ERR, "could not close file"); + + if (mapAddress == MAP_FAILED) { + if (PrintMiscellaneous && Verbose) { + warning("mmap failed: %s\n", strerror(errno)); + } + THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), + "Could not map PerfMemory"); + } + + *addr = mapAddress; + *sizep = size; + + if (PerfTraceMemOps) { + tty->print("mapped " SIZE_FORMAT " bytes for vmid %d at " + INTPTR_FORMAT "\n", size, vmid, (void*)mapAddress); + } +} + + + + +// create the PerfData memory region +// +// This method creates the memory region used to store performance +// data for the JVM. The memory may be created in standard or +// shared memory. +// +void PerfMemory::create_memory_region(size_t size) { + + if (PerfDisableSharedMem) { + // do not share the memory for the performance data. + _start = create_standard_memory(size); + } + else { + _start = create_shared_memory(size); + if (_start == NULL) { + + // creation of the shared memory region failed, attempt + // to create a contiguous, non-shared memory region instead. + // + if (PrintMiscellaneous && Verbose) { + warning("Reverting to non-shared PerfMemory region.\n"); + } + PerfDisableSharedMem = true; + _start = create_standard_memory(size); + } + } + + if (_start != NULL) _capacity = size; + +} + +// delete the PerfData memory region +// +// This method deletes the memory region used to store performance +// data for the JVM. The memory region indicated by the +// tuple will be inaccessible after a call to this method. +// +void PerfMemory::delete_memory_region() { + + assert((start() != NULL && capacity() > 0), "verify proper state"); + + // If user specifies PerfDataSaveFile, it will save the performance data + // to the specified file name no matter whether PerfDataSaveToFile is specified + // or not. In other word, -XX:PerfDataSaveFile=.. overrides flag + // -XX:+PerfDataSaveToFile. + if (PerfDataSaveToFile || PerfDataSaveFile != NULL) { + save_memory_to_file(start(), capacity()); + } + + if (PerfDisableSharedMem) { + delete_standard_memory(start(), capacity()); + } + else { + delete_shared_memory(start(), capacity()); + } +} + +// attach to the PerfData memory region for another JVM +// +// This method returns an tuple that points to +// a memory buffer that is kept reasonably synchronized with +// the PerfData memory region for the indicated JVM. This +// buffer may be kept in synchronization via shared memory +// or some other mechanism that keeps the buffer updated. +// +// If the JVM chooses not to support the attachability feature, +// this method should throw an UnsupportedOperation exception. +// +// This implementation utilizes named shared memory to map +// the indicated process's PerfData memory region into this JVMs +// address space. +// +void PerfMemory::attach(const char* user, int vmid, PerfMemoryMode mode, char** addrp, size_t* sizep, TRAPS) { + + if (vmid == 0 || vmid == os::current_process_id()) { + *addrp = start(); + *sizep = capacity(); + return; + } + + mmap_attach_shared(user, vmid, mode, addrp, sizep, CHECK); +} + +// detach from the PerfData memory region of another JVM +// +// This method detaches the PerfData memory region of another +// JVM, specified as an tuple of a buffer +// in this process's address space. This method may perform +// arbitrary actions to accomplish the detachment. The memory +// region specified by will be inaccessible after +// a call to this method. +// +// If the JVM chooses not to support the attachability feature, +// this method should throw an UnsupportedOperation exception. +// +// This implementation utilizes named shared memory to detach +// the indicated process's PerfData memory region from this +// process's address space. +// +void PerfMemory::detach(char* addr, size_t bytes, TRAPS) { + + assert(addr != 0, "address sanity check"); + assert(bytes > 0, "capacity sanity check"); + + if (PerfMemory::contains(addr) || PerfMemory::contains(addr + bytes - 1)) { + // prevent accidental detachment of this process's PerfMemory region + return; + } + + unmap_shared(addr, bytes); +} + +char* PerfMemory::backing_store_filename() { + return backing_store_file_name; +} diff --git a/hotspot/src/os/linux/vm/stubRoutines_linux.cpp b/hotspot/src/os/linux/vm/stubRoutines_linux.cpp new file mode 100644 index 00000000000..513b0f55dd3 --- /dev/null +++ b/hotspot/src/os/linux/vm/stubRoutines_linux.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubRoutines_linux.cpp.incl" diff --git a/hotspot/src/os/linux/vm/threadCritical_linux.cpp b/hotspot/src/os/linux/vm/threadCritical_linux.cpp new file mode 100644 index 00000000000..9d6bbc0d616 --- /dev/null +++ b/hotspot/src/os/linux/vm/threadCritical_linux.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_threadCritical_linux.cpp.incl" + +// put OS-includes here +# include + +// +// See threadCritical.hpp for details of this class. +// + +static pthread_t tc_owner = 0; +static pthread_mutex_t tc_mutex = PTHREAD_MUTEX_INITIALIZER; +static int tc_count = 0; + +void ThreadCritical::initialize() { +} + +void ThreadCritical::release() { +} + +ThreadCritical::ThreadCritical() { + pthread_t self = pthread_self(); + if (self != tc_owner) { + int ret = pthread_mutex_lock(&tc_mutex); + guarantee(ret == 0, "fatal error with pthread_mutex_lock()"); + assert(tc_count == 0, "Lock acquired with illegal reentry count."); + tc_owner = self; + } + tc_count++; +} + +ThreadCritical::~ThreadCritical() { + assert(tc_owner == pthread_self(), "must have correct owner"); + assert(tc_count > 0, "must have correct count"); + + tc_count--; + if (tc_count == 0) { + tc_owner = 0; + int ret = pthread_mutex_unlock(&tc_mutex); + guarantee(ret == 0, "fatal error with pthread_mutex_unlock()"); + } +} diff --git a/hotspot/src/os/linux/vm/thread_linux.inline.hpp b/hotspot/src/os/linux/vm/thread_linux.inline.hpp new file mode 100644 index 00000000000..59d93870b78 --- /dev/null +++ b/hotspot/src/os/linux/vm/thread_linux.inline.hpp @@ -0,0 +1,27 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Contains inlined functions for class Thread and ThreadLocalStorage + +inline void ThreadLocalStorage::pd_invalidate_all() {} // nothing to do diff --git a/hotspot/src/os/linux/vm/vmError_linux.cpp b/hotspot/src/os/linux/vm/vmError_linux.cpp new file mode 100644 index 00000000000..adf77357337 --- /dev/null +++ b/hotspot/src/os/linux/vm/vmError_linux.cpp @@ -0,0 +1,112 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vmError_linux.cpp.incl" + +#include +#include +#include +#include +#include + +void VMError::show_message_box(char *buf, int buflen) { + bool yes; + do { + error_string(buf, buflen); + int len = (int)strlen(buf); + char *p = &buf[len]; + + jio_snprintf(p, buflen - len, + "\n\n" + "Do you want to debug the problem?\n\n" + "To debug, run 'gdb /proc/%d/exe %d'; then switch to thread " INTX_FORMAT "\n" + "Enter 'yes' to launch gdb automatically (PATH must include gdb)\n" + "Otherwise, press RETURN to abort...", + os::current_process_id(), os::current_process_id(), + os::current_thread_id()); + + yes = os::message_box("Unexpected Error", buf); + + if (yes) { + // yes, user asked VM to launch debugger + jio_snprintf(buf, buflen, "gdb /proc/%d/exe %d", + os::current_process_id(), os::current_process_id()); + + os::fork_and_exec(buf); + yes = false; + } + } while (yes); +} + +// Space for our "saved" signal flags and handlers +static int resettedSigflags[2]; +static address resettedSighandler[2]; + +static void save_signal(int idx, int sig) +{ + struct sigaction sa; + sigaction(sig, NULL, &sa); + resettedSigflags[idx] = sa.sa_flags; + resettedSighandler[idx] = (sa.sa_flags & SA_SIGINFO) + ? CAST_FROM_FN_PTR(address, sa.sa_sigaction) + : CAST_FROM_FN_PTR(address, sa.sa_handler); +} + +int VMError::get_resetted_sigflags(int sig) { + if(SIGSEGV == sig) { + return resettedSigflags[0]; + } else if(SIGBUS == sig) { + return resettedSigflags[1]; + } + return -1; +} + +address VMError::get_resetted_sighandler(int sig) { + if(SIGSEGV == sig) { + return resettedSighandler[0]; + } else if(SIGBUS == sig) { + return resettedSighandler[1]; + } + return NULL; +} + +static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { + // unmask current signal + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); + sigprocmask(SIG_UNBLOCK, &newset, NULL); + + VMError err(NULL, sig, NULL, info, ucVoid); + err.report_and_die(); +} + +void VMError::reset_signal_handlers() { + // Save sigflags for resetted signals + save_signal(0, SIGSEGV); + save_signal(1, SIGBUS); + os::signal(SIGSEGV, CAST_FROM_FN_PTR(void *, crash_handler)); + os::signal(SIGBUS, CAST_FROM_FN_PTR(void *, crash_handler)); +} diff --git a/hotspot/src/os/linux/vm/vtune_linux.cpp b/hotspot/src/os/linux/vm/vtune_linux.cpp new file mode 100644 index 00000000000..0de83b04a17 --- /dev/null +++ b/hotspot/src/os/linux/vm/vtune_linux.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vtune_linux.cpp.incl" + +// empty implementation + +void VTune::start_GC() {} +void VTune::end_GC() {} +void VTune::start_class_load() {} +void VTune::end_class_load() {} +void VTune::exit() {} +void VTune::register_stub(const char* name, address start, address end) {} + +void VTune::create_nmethod(nmethod* nm) {} +void VTune::delete_nmethod(nmethod* nm) {} + +void vtune_init() {} + + +// Reconciliation History +// vtune_solaris.cpp 1.8 99/07/12 23:54:21 +// End diff --git a/hotspot/src/os/solaris/dtrace/generateJvmOffsets.cpp b/hotspot/src/os/solaris/dtrace/generateJvmOffsets.cpp new file mode 100644 index 00000000000..bceed658536 --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/generateJvmOffsets.cpp @@ -0,0 +1,272 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * This is to provide sanity check in jhelper.d which compares SCCS + * versions of generateJvmOffsets.cpp used to create and extract + * contents of __JvmOffsets[] table. + * The __JvmOffsets[] table is located in generated JvmOffsets.cpp. + * + * GENOFFS_SCCS_VER 34 + */ + +#include "generateJvmOffsets.h" + +/* A workaround for private and protected fields */ +#define private public +#define protected public + +#include +#include "incls/_precompiled.incl" +#include "incls/_vmStructs.cpp.incl" + +#ifdef COMPILER1 +#if defined(DEBUG) || defined(FASTDEBUG) + +/* + * To avoid the most part of potential link errors + * we link this program with -z nodefs . + * + * But for 'debug1' and 'fastdebug1' we still have to provide + * a particular workaround for the following symbols bellow. + * It will be good to find out a generic way in the future. + */ + +#pragma weak tty +#pragma weak CMSExpAvgFactor + +#if defined(i386) || defined(__i386) || defined(__amd64) +#pragma weak noreg +#endif /* i386 */ + +LIR_Opr LIR_OprFact::illegalOpr = (LIR_Opr) 0; + +address StubRoutines::_call_stub_return_address = NULL; + +StubQueue* AbstractInterpreter::_code = NULL; + +#endif /* defined(DEBUG) || defined(FASTDEBUG) */ +#endif /* COMPILER1 */ + +#define GEN_OFFS(Type,Name) \ + switch(gen_variant) { \ + case GEN_OFFSET: \ + printf("#define OFFSET_%-33s %d\n", \ + #Type #Name, offset_of(Type, Name)); \ + break; \ + case GEN_INDEX: \ + printf("#define IDX_OFFSET_%-33s %d\n", \ + #Type #Name, index++); \ + break; \ + case GEN_TABLE: \ + printf("\tOFFSET_%s,\n", #Type #Name); \ + break; \ + } + +#define GEN_SIZE(Type) \ + switch(gen_variant) { \ + case GEN_OFFSET: \ + printf("#define SIZE_%-35s %d\n", \ + #Type, sizeof(Type)); \ + break; \ + case GEN_INDEX: \ + printf("#define IDX_SIZE_%-35s %d\n", \ + #Type, index++); \ + break; \ + case GEN_TABLE: \ + printf("\tSIZE_%s,\n", #Type); \ + break; \ + } + +#define GEN_VALUE(String,Value) \ + switch(gen_variant) { \ + case GEN_OFFSET: \ + printf("#define %-40s %d\n", #String, Value); \ + break; \ + case GEN_INDEX: \ + printf("#define IDX_%-40s %d\n", #String, index++); \ + break; \ + case GEN_TABLE: \ + printf("\t" #String ",\n"); \ + break; \ + } + +void gen_prologue(GEN_variant gen_variant) { + const char *suffix; + + switch(gen_variant) { + case GEN_OFFSET: suffix = ".h"; break; + case GEN_INDEX: suffix = "Index.h"; break; + case GEN_TABLE: suffix = ".cpp"; break; + } + + printf("/*\n"); + printf(" * JvmOffsets%s !!!DO NOT EDIT!!! \n", suffix); + printf(" * The generateJvmOffsets program generates this file!\n"); + printf(" */\n\n"); + switch(gen_variant) { + + case GEN_OFFSET: + case GEN_INDEX: + break; + + case GEN_TABLE: + printf("#include \"JvmOffsets.h\"\n"); + printf("\n"); + printf("int __JvmOffsets[] = {\n"); + break; + } +} + +void gen_epilogue(GEN_variant gen_variant) { + if (gen_variant != GEN_TABLE) { + return; + } + printf("};\n\n"); + return; +} + +int generateJvmOffsets(GEN_variant gen_variant) { + int index = 0; /* It is used to generate JvmOffsetsIndex.h */ + int pointer_size = sizeof(void *); + int data_model = (pointer_size == 4) ? PR_MODEL_ILP32 : PR_MODEL_LP64; + + gen_prologue(gen_variant); + + GEN_VALUE(DATA_MODEL, data_model); + GEN_VALUE(POINTER_SIZE, pointer_size); +#if defined(TIERED) + GEN_VALUE(COMPILER, 3); +#elif COMPILER1 + GEN_VALUE(COMPILER, 1); +#elif COMPILER2 + GEN_VALUE(COMPILER, 2); +#else + GEN_VALUE(COMPILER, 0); +#endif // COMPILER1 && COMPILER2 + printf("\n"); + + GEN_OFFS(CollectedHeap, _reserved); + GEN_OFFS(MemRegion, _start); + GEN_OFFS(MemRegion, _word_size); + GEN_SIZE(HeapWord); + printf("\n"); + + GEN_OFFS(VMStructEntry, typeName); + GEN_OFFS(VMStructEntry, fieldName); + GEN_OFFS(VMStructEntry, address); + GEN_SIZE(VMStructEntry); + printf("\n"); + + GEN_VALUE(MAX_METHOD_CODE_SIZE, max_method_code_size); +#if defined(sparc) || defined(__sparc) + GEN_VALUE(OFFSET_interpreter_frame_method, 2 * pointer_size); /* L2 in saved window */ + GEN_VALUE(OFFSET_interpreter_frame_sender_sp, 13 * pointer_size); /* I5 in saved window */ + // Fake value for consistency. It is not going to be used. + GEN_VALUE(OFFSET_interpreter_frame_bcx_offset, 0xFFFF); +#elif defined(i386) || defined(__i386) || defined(__amd64) + GEN_VALUE(OFFSET_interpreter_frame_sender_sp, -1 * pointer_size); + GEN_VALUE(OFFSET_interpreter_frame_method, -3 * pointer_size); + GEN_VALUE(OFFSET_interpreter_frame_bcx_offset, -7 * pointer_size); +#endif + + GEN_OFFS(Klass, _name); + GEN_OFFS(constantPoolOopDesc, _pool_holder); + printf("\n"); + + GEN_VALUE(OFFSET_HeapBlockHeader_used, offset_of(HeapBlock::Header, _used)); + GEN_OFFS(oopDesc, _klass); + printf("\n"); + + GEN_VALUE(AccessFlags_NATIVE, JVM_ACC_NATIVE); + GEN_VALUE(constMethodOopDesc_has_linenumber_table, constMethodOopDesc::_has_linenumber_table); + GEN_OFFS(AccessFlags, _flags); + GEN_OFFS(symbolOopDesc, _length); + GEN_OFFS(symbolOopDesc, _body); + printf("\n"); + + GEN_OFFS(methodOopDesc, _constMethod); + GEN_OFFS(methodOopDesc, _constants); + GEN_OFFS(methodOopDesc, _access_flags); + printf("\n"); + + GEN_OFFS(constMethodOopDesc, _flags); + GEN_OFFS(constMethodOopDesc, _code_size); + GEN_OFFS(constMethodOopDesc, _name_index); + GEN_OFFS(constMethodOopDesc, _signature_index); + printf("\n"); + + GEN_OFFS(CodeHeap, _memory); + GEN_OFFS(CodeHeap, _segmap); + GEN_OFFS(CodeHeap, _log2_segment_size); + printf("\n"); + + GEN_OFFS(VirtualSpace, _low_boundary); + GEN_OFFS(VirtualSpace, _high_boundary); + GEN_OFFS(VirtualSpace, _low); + GEN_OFFS(VirtualSpace, _high); + printf("\n"); + + GEN_OFFS(CodeBlob, _name); + GEN_OFFS(CodeBlob, _header_size); + GEN_OFFS(CodeBlob, _instructions_offset); + GEN_OFFS(CodeBlob, _data_offset); + GEN_OFFS(CodeBlob, _oops_offset); + GEN_OFFS(CodeBlob, _oops_length); + GEN_OFFS(CodeBlob, _frame_size); + printf("\n"); + + GEN_OFFS(nmethod, _method); + GEN_OFFS(nmethod, _scopes_data_offset); + GEN_OFFS(nmethod, _scopes_pcs_offset); + GEN_OFFS(nmethod, _handler_table_offset); + GEN_OFFS(nmethod, _deoptimize_offset); + GEN_OFFS(nmethod, _orig_pc_offset); + + GEN_OFFS(PcDesc, _pc_offset); + GEN_OFFS(PcDesc, _scope_decode_offset); + + printf("\n"); + + GEN_VALUE(SIZE_HeapBlockHeader, sizeof(HeapBlock::Header)); + GEN_SIZE(oopDesc); + GEN_SIZE(constantPoolOopDesc); + printf("\n"); + + GEN_SIZE(PcDesc); + GEN_SIZE(methodOopDesc); + GEN_SIZE(constMethodOopDesc); + GEN_SIZE(nmethod); + GEN_SIZE(CodeBlob); + GEN_SIZE(BufferBlob); + GEN_SIZE(SingletonBlob); + GEN_SIZE(RuntimeStub); + GEN_SIZE(SafepointBlob); + + gen_epilogue(gen_variant); + printf("\n"); + + fflush(stdout); + return 0; +} diff --git a/hotspot/src/os/solaris/dtrace/generateJvmOffsets.h b/hotspot/src/os/solaris/dtrace/generateJvmOffsets.h new file mode 100644 index 00000000000..89309536cd9 --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/generateJvmOffsets.h @@ -0,0 +1,38 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include + +typedef enum GEN_variant { + GEN_OFFSET = 0, + GEN_INDEX = 1, + GEN_TABLE = 2 +} GEN_variant; + +extern "C" { + int generateJvmOffsets(GEN_variant gen_var); + void gen_prologue(GEN_variant gen_var); + void gen_epilogue(GEN_variant gen_var); +} diff --git a/hotspot/src/os/solaris/dtrace/generateJvmOffsetsMain.c b/hotspot/src/os/solaris/dtrace/generateJvmOffsetsMain.c new file mode 100644 index 00000000000..812ac5f9c71 --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/generateJvmOffsetsMain.c @@ -0,0 +1,53 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +#include "generateJvmOffsets.h" + +const char *HELP = + "HELP: generateJvmOffsets {-header | -index | -table} \n"; + +int main(int argc, const char *argv[]) { + GEN_variant gen_var; + + if (argc != 2) { + printf("%s", HELP); + return 1; + } + + if (0 == strcmp(argv[1], "-header")) { + gen_var = GEN_OFFSET; + } + else if (0 == strcmp(argv[1], "-index")) { + gen_var = GEN_INDEX; + } + else if (0 == strcmp(argv[1], "-table")) { + gen_var = GEN_TABLE; + } + else { + printf("%s", HELP); + return 1; + } + return generateJvmOffsets(gen_var); +} diff --git a/hotspot/src/os/solaris/dtrace/hotspot.d b/hotspot/src/os/solaris/dtrace/hotspot.d new file mode 100644 index 00000000000..057e5882689 --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/hotspot.d @@ -0,0 +1,69 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +provider hotspot { + probe class__loaded(char*, uintptr_t, void*, uintptr_t); + probe class__unloaded(char*, uintptr_t, void*, uintptr_t); + probe vm__init__begin(); + probe vm__init__end(); + probe vm__shutdown(); + probe gc__begin(uintptr_t); + probe gc__end(); + probe mem__pool__gc__begin( + char*, uintptr_t, char*, uintptr_t, + uintptr_t, uintptr_t, uintptr_t, uintptr_t); + probe mem__pool__gc__end( + char*, uintptr_t, char*, uintptr_t, + uintptr_t, uintptr_t, uintptr_t, uintptr_t); + probe thread__start(char*, uintptr_t, uintptr_t, uintptr_t, uintptr_t); + probe thread__stop(char*, uintptr_t, uintptr_t, uintptr_t, uintptr_t); + probe method__compile__begin( + char*, uintptr_t, char*, uintptr_t, char*, uintptr_t, char*, uintptr_t); + probe method__compile__end( + char*, uintptr_t, char*, uintptr_t, char*, uintptr_t, + char*, uintptr_t, uintptr_t); + probe compiled__method__load( + char*, uintptr_t, char*, uintptr_t, char*, uintptr_t, void*, uintptr_t); + probe compiled__method__unload( + char*, uintptr_t, char*, uintptr_t, char*, uintptr_t); + probe monitor__contended__enter(uintptr_t, uintptr_t, char*, uintptr_t); + probe monitor__contended__entered(uintptr_t, uintptr_t, char*, uintptr_t); + probe monitor__contended__exit(uintptr_t, uintptr_t, char*, uintptr_t); + probe monitor__wait(uintptr_t, uintptr_t, char*, uintptr_t, uintptr_t); + probe monitor__waited(uintptr_t, uintptr_t, char*, uintptr_t); + probe monitor__notify(uintptr_t, uintptr_t, char*, uintptr_t); + probe monitor__notifyAll(uintptr_t, uintptr_t, char*, uintptr_t); + + probe object__alloc(int, char*, uintptr_t, uintptr_t); + probe method__entry( + int, char*, int, char*, int, char*, int); + probe method__return( + int, char*, int, char*, int, char*, int); +}; + +#pragma D attributes Evolving/Evolving/Common provider hotspot provider +#pragma D attributes Private/Private/Unknown provider hotspot module +#pragma D attributes Private/Private/Unknown provider hotspot function +#pragma D attributes Evolving/Evolving/Common provider hotspot name +#pragma D attributes Evolving/Evolving/Common provider hotspot args diff --git a/hotspot/src/os/solaris/dtrace/hotspot_jni.d b/hotspot/src/os/solaris/dtrace/hotspot_jni.d new file mode 100644 index 00000000000..47f8fe2dea3 --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/hotspot_jni.d @@ -0,0 +1,506 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +provider hotspot_jni { + probe AllocObject__entry(void*, void*); + probe AllocObject__return(void*); + probe AttachCurrentThreadAsDaemon__entry(void*, void**, void*); + probe AttachCurrentThreadAsDaemon__return(uint32_t); + probe AttachCurrentThread__entry(void*, void**, void*); + probe AttachCurrentThread__return(uint32_t); + probe CallBooleanMethodA__entry(void*, void*, uintptr_t); + probe CallBooleanMethodA__return(uintptr_t); + probe CallBooleanMethod__entry(void*, void*, uintptr_t); + probe CallBooleanMethod__return(uintptr_t); + probe CallBooleanMethodV__entry(void*, void*, uintptr_t); + probe CallBooleanMethodV__return(uintptr_t); + probe CallByteMethodA__entry(void*, void*, uintptr_t); + probe CallByteMethodA__return(char); + probe CallByteMethod__entry(void*, void*, uintptr_t); + probe CallByteMethod__return(char); + probe CallByteMethodV__entry(void*, void*, uintptr_t); + probe CallByteMethodV__return(char); + probe CallCharMethodA__entry(void*, void*, uintptr_t); + probe CallCharMethodA__return(uint16_t); + probe CallCharMethod__entry(void*, void*, uintptr_t); + probe CallCharMethod__return(uint16_t); + probe CallCharMethodV__entry(void*, void*, uintptr_t); + probe CallCharMethodV__return(uint16_t); + probe CallDoubleMethodA__entry(void*, void*, uintptr_t); + probe CallDoubleMethodA__return(); + probe CallDoubleMethod__entry(void*, void*, uintptr_t); + probe CallDoubleMethod__return(); + probe CallDoubleMethodV__entry(void*, void*, uintptr_t); + probe CallDoubleMethodV__return(); + probe CallFloatMethodA__entry(void*, void*, uintptr_t); + probe CallFloatMethodA__return(); + probe CallFloatMethod__entry(void*, void*, uintptr_t); + probe CallFloatMethod__return(); + probe CallFloatMethodV__entry(void*, void*, uintptr_t); + probe CallFloatMethodV__return(); + probe CallIntMethodA__entry(void*, void*, uintptr_t); + probe CallIntMethodA__return(uint32_t); + probe CallIntMethod__entry(void*, void*, uintptr_t); + probe CallIntMethod__return(uint32_t); + probe CallIntMethodV__entry(void*, void*, uintptr_t); + probe CallIntMethodV__return(uint32_t); + probe CallLongMethodA__entry(void*, void*, uintptr_t); + probe CallLongMethodA__return(uintptr_t); + probe CallLongMethod__entry(void*, void*, uintptr_t); + probe CallLongMethod__return(uintptr_t); + probe CallLongMethodV__entry(void*, void*, uintptr_t); + probe CallLongMethodV__return(uintptr_t); + probe CallNonvirtualBooleanMethodA__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualBooleanMethodA__return(uintptr_t); + probe CallNonvirtualBooleanMethod__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualBooleanMethod__return(uintptr_t); + probe CallNonvirtualBooleanMethodV__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualBooleanMethodV__return(uintptr_t); + probe CallNonvirtualByteMethodA__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualByteMethodA__return(char); + probe CallNonvirtualByteMethod__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualByteMethod__return(char); + probe CallNonvirtualByteMethodV__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualByteMethodV__return(char); + probe CallNonvirtualCharMethodA__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualCharMethodA__return(uint16_t); + probe CallNonvirtualCharMethod__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualCharMethod__return(uint16_t); + probe CallNonvirtualCharMethodV__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualCharMethodV__return(uint16_t); + probe CallNonvirtualDoubleMethodA__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualDoubleMethodA__return(); + probe CallNonvirtualDoubleMethod__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualDoubleMethod__return(); + probe CallNonvirtualDoubleMethodV__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualDoubleMethodV__return(); + probe CallNonvirtualFloatMethodA__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualFloatMethodA__return(); + probe CallNonvirtualFloatMethod__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualFloatMethod__return(); + probe CallNonvirtualFloatMethodV__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualFloatMethodV__return(); + probe CallNonvirtualIntMethodA__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualIntMethodA__return(uint32_t); + probe CallNonvirtualIntMethod__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualIntMethod__return(uint32_t); + probe CallNonvirtualIntMethodV__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualIntMethodV__return(uint32_t); + probe CallNonvirtualLongMethodA__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualLongMethodA__return(uintptr_t); + probe CallNonvirtualLongMethod__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualLongMethod__return(uintptr_t); + probe CallNonvirtualLongMethodV__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualLongMethodV__return(uintptr_t); + probe CallNonvirtualObjectMethodA__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualObjectMethodA__return(void*); + probe CallNonvirtualObjectMethod__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualObjectMethod__return(void*); + probe CallNonvirtualObjectMethodV__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualObjectMethodV__return(void*); + probe CallNonvirtualShortMethodA__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualShortMethodA__return(uint16_t); + probe CallNonvirtualShortMethod__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualShortMethod__return(uint16_t); + probe CallNonvirtualShortMethodV__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualShortMethodV__return(uint16_t); + probe CallNonvirtualVoidMethodA__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualVoidMethodA__return(); + probe CallNonvirtualVoidMethod__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualVoidMethod__return(); + probe CallNonvirtualVoidMethodV__entry(void*, void*, void*, uintptr_t); + probe CallNonvirtualVoidMethodV__return(); + probe CallObjectMethodA__entry(void*, void*, uintptr_t); + probe CallObjectMethodA__return(void*); + probe CallObjectMethod__entry(void*, void*, uintptr_t); + probe CallObjectMethod__return(void*); + probe CallObjectMethodV__entry(void*, void*, uintptr_t); + probe CallObjectMethodV__return(void*); + probe CallShortMethodA__entry(void*, void*, uintptr_t); + probe CallShortMethodA__return(uint16_t); + probe CallShortMethod__entry(void*, void*, uintptr_t); + probe CallShortMethod__return(uint16_t); + probe CallShortMethodV__entry(void*, void*, uintptr_t); + probe CallShortMethodV__return(uint16_t); + probe CallStaticBooleanMethodA__entry(void*, void*, uintptr_t); + probe CallStaticBooleanMethodA__return(uintptr_t); + probe CallStaticBooleanMethod__entry(void*, void*, uintptr_t); + probe CallStaticBooleanMethod__return(uintptr_t); + probe CallStaticBooleanMethodV__entry(void*, void*, uintptr_t); + probe CallStaticBooleanMethodV__return(uintptr_t); + probe CallStaticByteMethodA__entry(void*, void*, uintptr_t); + probe CallStaticByteMethodA__return(char); + probe CallStaticByteMethod__entry(void*, void*, uintptr_t); + probe CallStaticByteMethod__return(char); + probe CallStaticByteMethodV__entry(void*, void*, uintptr_t); + probe CallStaticByteMethodV__return(char); + probe CallStaticCharMethodA__entry(void*, void*, uintptr_t); + probe CallStaticCharMethodA__return(uint16_t); + probe CallStaticCharMethod__entry(void*, void*, uintptr_t); + probe CallStaticCharMethod__return(uint16_t); + probe CallStaticCharMethodV__entry(void*, void*, uintptr_t); + probe CallStaticCharMethodV__return(uint16_t); + probe CallStaticDoubleMethodA__entry(void*, void*, uintptr_t); + probe CallStaticDoubleMethodA__return(); + probe CallStaticDoubleMethod__entry(void*, void*, uintptr_t); + probe CallStaticDoubleMethod__return(); + probe CallStaticDoubleMethodV__entry(void*, void*, uintptr_t); + probe CallStaticDoubleMethodV__return(); + probe CallStaticFloatMethodA__entry(void*, void*, uintptr_t); + probe CallStaticFloatMethodA__return(); + probe CallStaticFloatMethod__entry(void*, void*, uintptr_t); + probe CallStaticFloatMethod__return(); + probe CallStaticFloatMethodV__entry(void*, void*, uintptr_t); + probe CallStaticFloatMethodV__return(); + probe CallStaticIntMethodA__entry(void*, void*, uintptr_t); + probe CallStaticIntMethodA__return(uint32_t); + probe CallStaticIntMethod__entry(void*, void*, uintptr_t); + probe CallStaticIntMethod__return(uint32_t); + probe CallStaticIntMethodV__entry(void*, void*, uintptr_t); + probe CallStaticIntMethodV__return(uint32_t); + probe CallStaticLongMethodA__entry(void*, void*, uintptr_t); + probe CallStaticLongMethodA__return(uintptr_t); + probe CallStaticLongMethod__entry(void*, void*, uintptr_t); + probe CallStaticLongMethod__return(uintptr_t); + probe CallStaticLongMethodV__entry(void*, void*, uintptr_t); + probe CallStaticLongMethodV__return(uintptr_t); + probe CallStaticObjectMethodA__entry(void*, void*, uintptr_t); + probe CallStaticObjectMethodA__return(void*); + probe CallStaticObjectMethod__entry(void*, void*, uintptr_t); + probe CallStaticObjectMethod__return(void*); + probe CallStaticObjectMethodV__entry(void*, void*, uintptr_t); + probe CallStaticObjectMethodV__return(void*); + probe CallStaticShortMethodA__entry(void*, void*, uintptr_t); + probe CallStaticShortMethodA__return(uint16_t); + probe CallStaticShortMethod__entry(void*, void*, uintptr_t); + probe CallStaticShortMethod__return(uint16_t); + probe CallStaticShortMethodV__entry(void*, void*, uintptr_t); + probe CallStaticShortMethodV__return(uint16_t); + probe CallStaticVoidMethodA__entry(void*, void*, uintptr_t); + probe CallStaticVoidMethodA__return(); + probe CallStaticVoidMethod__entry(void*, void*, uintptr_t); + probe CallStaticVoidMethod__return(); + probe CallStaticVoidMethodV__entry(void*, void*, uintptr_t); + probe CallStaticVoidMethodV__return(); + probe CallVoidMethodA__entry(void*, void*, uintptr_t); + probe CallVoidMethodA__return(); + probe CallVoidMethod__entry(void*, void*, uintptr_t); + probe CallVoidMethod__return(); + probe CallVoidMethodV__entry(void*, void*, uintptr_t); + probe CallVoidMethodV__return(); + probe CreateJavaVM__entry(void**, void**, void*); + probe CreateJavaVM__return(uint32_t); + probe DefineClass__entry(void*, const char*, void*, char, uintptr_t); + probe DefineClass__return(void*); + probe DeleteGlobalRef__entry(void*, void*); + probe DeleteGlobalRef__return(); + probe DeleteLocalRef__entry(void*, void*); + probe DeleteLocalRef__return(); + probe DeleteWeakGlobalRef__entry(void*, void*); + probe DeleteWeakGlobalRef__return(); + probe DestroyJavaVM__entry(void*); + probe DestroyJavaVM__return(uint32_t); + probe DetachCurrentThread__entry(void*); + probe DetachCurrentThread__return(uint32_t); + probe EnsureLocalCapacity__entry(void*, uint32_t); + probe EnsureLocalCapacity__return(uint32_t); + probe ExceptionCheck__entry(void*); + probe ExceptionCheck__return(uintptr_t); + probe ExceptionClear__entry(void*); + probe ExceptionClear__return(); + probe ExceptionDescribe__entry(void*); + probe ExceptionDescribe__return(); + probe ExceptionOccurred__entry(void*); + probe ExceptionOccurred__return(void*); + probe FatalError__entry(void* env, const char*); + probe FindClass__entry(void*, const char*); + probe FindClass__return(void*); + probe FromReflectedField__entry(void*, void*); + probe FromReflectedField__return(uintptr_t); + probe FromReflectedMethod__entry(void*, void*); + probe FromReflectedMethod__return(uintptr_t); + probe GetArrayLength__entry(void*, void*); + probe GetArrayLength__return(uintptr_t); + probe GetBooleanArrayElements__entry(void*, void*, uintptr_t*); + probe GetBooleanArrayElements__return(uintptr_t*); + probe GetBooleanArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, uintptr_t*); + probe GetBooleanArrayRegion__return(); + probe GetBooleanField__entry(void*, void*, uintptr_t); + probe GetBooleanField__return(uintptr_t); + probe GetByteArrayElements__entry(void*, void*, uintptr_t*); + probe GetByteArrayElements__return(char*); + probe GetByteArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, char*); + probe GetByteArrayRegion__return(); + probe GetByteField__entry(void*, void*, uintptr_t); + probe GetByteField__return(char); + probe GetCharArrayElements__entry(void*, void*, uintptr_t*); + probe GetCharArrayElements__return(uint16_t*); + probe GetCharArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, uint16_t*); + probe GetCharArrayRegion__return(); + probe GetCharField__entry(void*, void*, uintptr_t); + probe GetCharField__return(uint16_t); + probe GetCreatedJavaVMs__entry(void**, uintptr_t, uintptr_t*); + probe GetCreatedJavaVMs__return(uintptr_t); + probe GetDefaultJavaVMInitArgs__entry(void*); + probe GetDefaultJavaVMInitArgs__return(uint32_t); + probe GetDirectBufferAddress__entry(void*, void*); + probe GetDirectBufferAddress__return(void*); + probe GetDirectBufferCapacity__entry(void*, void*); + probe GetDirectBufferCapacity__return(uintptr_t); + probe GetDoubleArrayElements__entry(void*, void*, uintptr_t*); + probe GetDoubleArrayElements__return(double*); + probe GetDoubleArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, double*); + probe GetDoubleArrayRegion__return(); + probe GetDoubleField__entry(void*, void*, uintptr_t); + probe GetDoubleField__return(); + probe GetEnv__entry(void*, void*, uint32_t); + probe GetEnv__return(uint32_t); + probe GetFieldID__entry(void*, void*, const char*, const char*); + probe GetFieldID__return(uintptr_t); + probe GetFloatArrayElements__entry(void*, void*, uintptr_t*); + probe GetFloatArrayElements__return(float*); + probe GetFloatArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, float*); + probe GetFloatArrayRegion__return(); + probe GetFloatField__entry(void*, void*, uintptr_t); + probe GetFloatField__return(); + probe GetIntArrayElements__entry(void*, void*, uintptr_t*); + probe GetIntArrayElements__return(uint32_t*); + probe GetIntArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, uint32_t*); + probe GetIntArrayRegion__return(); + probe GetIntField__entry(void*, void*, uintptr_t); + probe GetIntField__return(uint32_t); + probe GetJavaVM__entry(void*, void**); + probe GetJavaVM__return(uint32_t); + probe GetLongArrayElements__entry(void*, void*, uintptr_t*); + probe GetLongArrayElements__return(uintptr_t*); + probe GetLongArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, uintptr_t*); + probe GetLongArrayRegion__return(); + probe GetLongField__entry(void*, void*, uintptr_t); + probe GetLongField__return(uintptr_t); + probe GetMethodID__entry(void*, void*, const char*, const char*); + probe GetMethodID__return(uintptr_t); + probe GetObjectArrayElement__entry(void*, void*, uintptr_t); + probe GetObjectArrayElement__return(void*); + probe GetObjectClass__entry(void*, void*); + probe GetObjectClass__return(void*); + probe GetObjectField__entry(void*, void*, uintptr_t); + probe GetObjectField__return(void*); + probe GetObjectRefType__entry(void*, void*); + probe GetObjectRefType__return(void*); + probe GetPrimitiveArrayCritical__entry(void*, void*, uintptr_t*); + probe GetPrimitiveArrayCritical__return(void*); + probe GetShortArrayElements__entry(void*, void*, uintptr_t*); + probe GetShortArrayElements__return(uint16_t*); + probe GetShortArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, uint16_t*); + probe GetShortArrayRegion__return(); + probe GetShortField__entry(void*, void*, uintptr_t); + probe GetShortField__return(uint16_t); + probe GetStaticBooleanField__entry(void*, void*, uintptr_t); + probe GetStaticBooleanField__return(uintptr_t); + probe GetStaticByteField__entry(void*, void*, uintptr_t); + probe GetStaticByteField__return(char); + probe GetStaticCharField__entry(void*, void*, uintptr_t); + probe GetStaticCharField__return(uint16_t); + probe GetStaticDoubleField__entry(void*, void*, uintptr_t); + probe GetStaticDoubleField__return(); + probe GetStaticFieldID__entry(void*, void*, const char*, const char*); + probe GetStaticFieldID__return(uintptr_t); + probe GetStaticFloatField__entry(void*, void*, uintptr_t); + probe GetStaticFloatField__return(); + probe GetStaticIntField__entry(void*, void*, uintptr_t); + probe GetStaticIntField__return(uint32_t); + probe GetStaticLongField__entry(void*, void*, uintptr_t); + probe GetStaticLongField__return(uintptr_t); + probe GetStaticMethodID__entry(void*, void*, const char*, const char*); + probe GetStaticMethodID__return(uintptr_t); + probe GetStaticObjectField__entry(void*, void*, uintptr_t); + probe GetStaticObjectField__return(void*); + probe GetStaticShortField__entry(void*, void*, uintptr_t); + probe GetStaticShortField__return(uint16_t); + probe GetStringChars__entry(void*, void*, uintptr_t*); + probe GetStringChars__return(const uint16_t*); + probe GetStringCritical__entry(void*, void*, uintptr_t*); + probe GetStringCritical__return(const uint16_t*); + probe GetStringLength__entry(void*, void*); + probe GetStringLength__return(uintptr_t); + probe GetStringRegion__entry(void*, void*, uintptr_t, uintptr_t, uint16_t*); + probe GetStringRegion__return(); + probe GetStringUTFChars__entry(void*, void*, uintptr_t*); + probe GetStringUTFChars__return(const char*); + probe GetStringUTFLength__entry(void*, void*); + probe GetStringUTFLength__return(uintptr_t); + probe GetStringUTFRegion__entry(void*, void*, uintptr_t, uintptr_t, char*); + probe GetStringUTFRegion__return(); + probe GetSuperclass__entry(void*, void*); + probe GetSuperclass__return(void*); + probe GetVersion__entry(void*); + probe GetVersion__return(uint32_t); + probe IsAssignableFrom__entry(void*, void*, void*); + probe IsAssignableFrom__return(uintptr_t); + probe IsInstanceOf__entry(void*, void*, void*); + probe IsInstanceOf__return(uintptr_t); + probe IsSameObject__entry(void*, void*, void*); + probe IsSameObject__return(uintptr_t); + probe MonitorEnter__entry(void*, void*); + probe MonitorEnter__return(uint32_t); + probe MonitorExit__entry(void*, void*); + probe MonitorExit__return(uint32_t); + probe NewBooleanArray__entry(void*, uintptr_t); + probe NewBooleanArray__return(void*); + probe NewByteArray__entry(void*, uintptr_t); + probe NewByteArray__return(void*); + probe NewCharArray__entry(void*, uintptr_t); + probe NewCharArray__return(void*); + probe NewDirectByteBuffer__entry(void*, void*, uintptr_t); + probe NewDirectByteBuffer__return(void*); + probe NewDoubleArray__entry(void*, uintptr_t); + probe NewDoubleArray__return(void*); + probe NewFloatArray__entry(void*, uintptr_t); + probe NewFloatArray__return(void*); + probe NewGlobalRef__entry(void*, void*); + probe NewGlobalRef__return(void*); + probe NewIntArray__entry(void*, uintptr_t); + probe NewIntArray__return(void*); + probe NewLocalRef__entry(void*, void*); + probe NewLocalRef__return(void*); + probe NewLongArray__entry(void*, uintptr_t); + probe NewLongArray__return(void*); + probe NewObjectA__entry(void*, void*, uintptr_t); + probe NewObjectA__return(void*); + probe NewObjectArray__entry(void*, uintptr_t, void*, void*); + probe NewObjectArray__return(void*); + probe NewObject__entry(void*, void*, uintptr_t); + probe NewObject__return(void*); + probe NewObjectV__entry(void*, void*, uintptr_t); + probe NewObjectV__return(void*); + probe NewShortArray__entry(void*, uintptr_t); + probe NewShortArray__return(void*); + probe NewString__entry(void*, const uint16_t*, uintptr_t); + probe NewString__return(void*); + probe NewStringUTF__entry(void*, const char*); + probe NewStringUTF__return(void*); + probe NewWeakGlobalRef__entry(void*, void*); + probe NewWeakGlobalRef__return(void*); + probe PopLocalFrame__entry(void*, void*); + probe PopLocalFrame__return(void*); + probe PushLocalFrame__entry(void*, uint32_t); + probe PushLocalFrame__return(uint32_t); + probe RegisterNatives__entry(void*, void*, const void*, uint32_t); + probe RegisterNatives__return(uint32_t); + probe ReleaseBooleanArrayElements__entry(void*, void*, uintptr_t*, uint32_t); + probe ReleaseBooleanArrayElements__return(); + probe ReleaseByteArrayElements__entry(void*, void*, char*, uint32_t); + probe ReleaseByteArrayElements__return(); + probe ReleaseCharArrayElements__entry(void*, void*, uint16_t*, uint32_t); + probe ReleaseCharArrayElements__return(); + probe ReleaseDoubleArrayElements__entry(void*, void*, double*, uint32_t); + probe ReleaseDoubleArrayElements__return(); + probe ReleaseFloatArrayElements__entry(void*, void*, float*, uint32_t); + probe ReleaseFloatArrayElements__return(); + probe ReleaseIntArrayElements__entry(void*, void*, uint32_t*, uint32_t); + probe ReleaseIntArrayElements__return(); + probe ReleaseLongArrayElements__entry(void*, void*, uintptr_t*, uint32_t); + probe ReleaseLongArrayElements__return(); + probe ReleasePrimitiveArrayCritical__entry(void*, void*, void*, uint32_t); + probe ReleasePrimitiveArrayCritical__return(); + probe ReleaseShortArrayElements__entry(void*, void*, uint16_t*, uint32_t); + probe ReleaseShortArrayElements__return(); + probe ReleaseStringChars__entry(void*, void*, const uint16_t*); + probe ReleaseStringChars__return(); + probe ReleaseStringCritical__entry(void*, void*, const uint16_t*); + probe ReleaseStringCritical__return(); + probe ReleaseStringUTFChars__entry(void*, void*, const char*); + probe ReleaseStringUTFChars__return(); + probe SetBooleanArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, const uintptr_t*); + probe SetBooleanArrayRegion__return(); + probe SetBooleanField__entry(void*, void*, uintptr_t, uintptr_t); + probe SetBooleanField__return(); + probe SetByteArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, const char*); + probe SetByteArrayRegion__return(); + probe SetByteField__entry(void*, void*, uintptr_t, char); + probe SetByteField__return(); + probe SetCharArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, const uint16_t*); + probe SetCharArrayRegion__return(); + probe SetCharField__entry(void*, void*, uintptr_t, uint16_t); + probe SetCharField__return(); + probe SetDoubleArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, const double*); + probe SetDoubleArrayRegion__return(); + probe SetDoubleField__entry(void*, void*, uintptr_t); + probe SetDoubleField__return(); + probe SetFloatArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, const float*); + probe SetFloatArrayRegion__return(); + probe SetFloatField__entry(void*, void*, uintptr_t); + probe SetFloatField__return(); + probe SetIntArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, const uint32_t*); + probe SetIntArrayRegion__return(); + probe SetIntField__entry(void*, void*, uintptr_t, uint32_t); + probe SetIntField__return(); + probe SetLongArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, const uintptr_t*); + probe SetLongArrayRegion__return(); + probe SetLongField__entry(void*, void*, uintptr_t, uintptr_t); + probe SetLongField__return(); + probe SetObjectArrayElement__entry(void*, void*, uintptr_t, void*); + probe SetObjectArrayElement__return(); + probe SetObjectField__entry(void*, void*, uintptr_t, void*); + probe SetObjectField__return(); + probe SetShortArrayRegion__entry(void*, void*, uintptr_t, uintptr_t, const uint16_t*); + probe SetShortArrayRegion__return(); + probe SetShortField__entry(void*, void*, uintptr_t, uint16_t); + probe SetShortField__return(); + probe SetStaticBooleanField__entry(void*, void*, uintptr_t, uintptr_t); + probe SetStaticBooleanField__return(); + probe SetStaticByteField__entry(void*, void*, uintptr_t, char); + probe SetStaticByteField__return(); + probe SetStaticCharField__entry(void*, void*, uintptr_t, uint16_t); + probe SetStaticCharField__return(); + probe SetStaticDoubleField__entry(void*, void*, uintptr_t); + probe SetStaticDoubleField__return(); + probe SetStaticFloatField__entry(void*, void*, uintptr_t); + probe SetStaticFloatField__return(); + probe SetStaticIntField__entry(void*, void*, uintptr_t, uint32_t); + probe SetStaticIntField__return(); + probe SetStaticLongField__entry(void*, void*, uintptr_t, uintptr_t); + probe SetStaticLongField__return(); + probe SetStaticObjectField__entry(void*, void*, uintptr_t, void*); + probe SetStaticObjectField__return(); + probe SetStaticShortField__entry(void*, void*, uintptr_t, uint16_t); + probe SetStaticShortField__return(); + probe Throw__entry(void*, void*); + probe Throw__return(intptr_t); + probe ThrowNew__entry(void*, void*, const char*); + probe ThrowNew__return(intptr_t); + probe ToReflectedField__entry(void*, void*, uintptr_t, uintptr_t); + probe ToReflectedField__return(void*); + probe ToReflectedMethod__entry(void*, void*, uintptr_t, uintptr_t); + probe ToReflectedMethod__return(void*); + probe UnregisterNatives__entry(void*, void*); + probe UnregisterNatives__return(uint32_t); +}; + +#pragma D attributes Standard/Standard/Common provider hotspot_jni provider +#pragma D attributes Private/Private/Unknown provider hotspot_jni module +#pragma D attributes Private/Private/Unknown provider hotspot_jni function +#pragma D attributes Standard/Standard/Common provider hotspot_jni name +#pragma D attributes Evolving/Evolving/Common provider hotspot_jni args + diff --git a/hotspot/src/os/solaris/dtrace/hs_private.d b/hotspot/src/os/solaris/dtrace/hs_private.d new file mode 100644 index 00000000000..43baa3eb7a1 --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/hs_private.d @@ -0,0 +1,40 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +provider hs_private { + probe hashtable__new_entry(void*, uintptr_t, void*); + probe safepoint__begin(); + probe safepoint__end(); + probe cms__initmark__begin(); + probe cms__initmark__end(); + probe cms__remark__begin(); + probe cms__remark__end(); +}; + +#pragma D attributes Private/Private/Common provider hs_private provider +#pragma D attributes Private/Private/Unknown provider hs_private module +#pragma D attributes Private/Private/Unknown provider hs_private function +#pragma D attributes Private/Private/Common provider hs_private name +#pragma D attributes Private/Private/Common provider hs_private args + diff --git a/hotspot/src/os/solaris/dtrace/jhelper.d b/hotspot/src/os/solaris/dtrace/jhelper.d new file mode 100644 index 00000000000..ffbc7ad02ba --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/jhelper.d @@ -0,0 +1,411 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* This file is auto-generated */ +#include "JvmOffsetsIndex.h" + +#define DEBUG + +#ifdef DEBUG +#define MARK_LINE this->line = __LINE__ +#else +#define MARK_LINE +#endif + +#ifdef _LP64 +#define STACK_BIAS 0x7ff +#define pointer uint64_t +#else +#define STACK_BIAS 0 +#define pointer uint32_t +#endif + +extern pointer __JvmOffsets; + +extern pointer __1cJCodeCacheF_heap_; +extern pointer __1cIUniverseP_methodKlassObj_; +extern pointer __1cIUniverseO_collectedHeap_; + +extern pointer __1cHnmethodG__vtbl_; +extern pointer __1cKBufferBlobG__vtbl_; + +#define copyin_ptr(ADDR) *(pointer*) copyin((pointer) (ADDR), sizeof(pointer)) +#define copyin_uchar(ADDR) *(uchar_t*) copyin((pointer) (ADDR), sizeof(uchar_t)) +#define copyin_uint16(ADDR) *(uint16_t*) copyin((pointer) (ADDR), sizeof(uint16_t)) +#define copyin_uint32(ADDR) *(uint32_t*) copyin((pointer) (ADDR), sizeof(uint32_t)) +#define copyin_int32(ADDR) *(int32_t*) copyin((pointer) (ADDR), sizeof(int32_t)) + +#define SAME(x) x +#define copyin_offset(JVM_CONST) JVM_CONST = \ + copyin_int32(JvmOffsetsPtr + SAME(IDX_)JVM_CONST * sizeof(int32_t)) + +int init_done; + +dtrace:helper:ustack: +{ + MARK_LINE; + this->done = 0; + /* + * TBD: + * Here we initialize init_done, otherwise jhelper does not work. + * Therefore, copyin_offset() statements work multiple times now. + * There is a hope we could avoid it in the future, and so, + * this initialization can be removed. + */ + init_done = 0; + this->error = (char *) NULL; + this->result = (char *) NULL; + this->methodOop = 0; + this->codecache = 0; + this->klass = (pointer) NULL; + this->vtbl = (pointer) NULL; + this->suffix = '\0'; +} + +dtrace:helper:ustack: +{ + MARK_LINE; + /* Initialization of JvmOffsets constants */ + JvmOffsetsPtr = (pointer) &``__JvmOffsets; +} + +dtrace:helper:ustack: +/!init_done && !this->done/ +{ + MARK_LINE; + init_done = 1; + + copyin_offset(COMPILER); + copyin_offset(OFFSET_CollectedHeap_reserved); + copyin_offset(OFFSET_MemRegion_start); + copyin_offset(OFFSET_MemRegion_word_size); + copyin_offset(SIZE_HeapWord); + + copyin_offset(OFFSET_interpreter_frame_method); + copyin_offset(OFFSET_Klass_name); + copyin_offset(OFFSET_constantPoolOopDesc_pool_holder); + + copyin_offset(OFFSET_HeapBlockHeader_used); + copyin_offset(OFFSET_oopDesc_klass); + + copyin_offset(OFFSET_symbolOopDesc_length); + copyin_offset(OFFSET_symbolOopDesc_body); + + copyin_offset(OFFSET_methodOopDesc_constMethod); + copyin_offset(OFFSET_methodOopDesc_constants); + copyin_offset(OFFSET_constMethodOopDesc_name_index); + copyin_offset(OFFSET_constMethodOopDesc_signature_index); + + copyin_offset(OFFSET_CodeHeap_memory); + copyin_offset(OFFSET_CodeHeap_segmap); + copyin_offset(OFFSET_CodeHeap_log2_segment_size); + + copyin_offset(OFFSET_VirtualSpace_low); + copyin_offset(OFFSET_VirtualSpace_high); + + copyin_offset(OFFSET_CodeBlob_name); + + copyin_offset(OFFSET_nmethod_method); + copyin_offset(SIZE_HeapBlockHeader); + copyin_offset(SIZE_oopDesc); + copyin_offset(SIZE_constantPoolOopDesc); + + /* + * The PC to translate is in arg0. + */ + this->pc = arg0; + + /* + * The methodOopPtr is in %l2 on SPARC. This can be found at + * offset 8 from the frame pointer on 32-bit processes. + */ +#if defined(__sparc) + this->methodOopPtr = copyin_ptr(arg1 + 2 * sizeof(pointer) + STACK_BIAS); +#elif defined(__i386) || defined(__amd64) + this->methodOopPtr = copyin_ptr(arg1 + OFFSET_interpreter_frame_method); +#else +#error "Don't know architecture" +#endif + + this->Universe_methodKlassOop = copyin_ptr(&``__1cIUniverseP_methodKlassObj_); + this->CodeCache_heap_address = copyin_ptr(&``__1cJCodeCacheF_heap_); + + /* Reading volatile values */ + this->CodeCache_low = copyin_ptr(this->CodeCache_heap_address + + OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_low); + + this->CodeCache_high = copyin_ptr(this->CodeCache_heap_address + + OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_high); + + this->CodeCache_segmap_low = copyin_ptr(this->CodeCache_heap_address + + OFFSET_CodeHeap_segmap + OFFSET_VirtualSpace_low); + + this->CodeCache_segmap_high = copyin_ptr(this->CodeCache_heap_address + + OFFSET_CodeHeap_segmap + OFFSET_VirtualSpace_high); + + this->CodeHeap_log2_segment_size = copyin_uint32( + this->CodeCache_heap_address + OFFSET_CodeHeap_log2_segment_size); + + /* + * Get Java heap bounds + */ + this->Universe_collectedHeap = copyin_ptr(&``__1cIUniverseO_collectedHeap_); + this->heap_start = copyin_ptr(this->Universe_collectedHeap + + OFFSET_CollectedHeap_reserved + + OFFSET_MemRegion_start); + this->heap_size = SIZE_HeapWord * + copyin_ptr(this->Universe_collectedHeap + + OFFSET_CollectedHeap_reserved + + OFFSET_MemRegion_word_size + ); + this->heap_end = this->heap_start + this->heap_size; +} + +dtrace:helper:ustack: +/!this->done && +this->CodeCache_low <= this->pc && this->pc < this->CodeCache_high/ +{ + MARK_LINE; + this->codecache = 1; + + /* + * Find start. + */ + this->segment = (this->pc - this->CodeCache_low) >> + this->CodeHeap_log2_segment_size; + this->block = this->CodeCache_segmap_low; + this->tag = copyin_uchar(this->block + this->segment); + "second"; +} + +dtrace:helper:ustack: +/!this->done && this->codecache && this->tag > 0/ +{ + MARK_LINE; + this->tag = copyin_uchar(this->block + this->segment); + this->segment = this->segment - this->tag; +} + +dtrace:helper:ustack: +/!this->done && this->codecache && this->tag > 0/ +{ + MARK_LINE; + this->tag = copyin_uchar(this->block + this->segment); + this->segment = this->segment - this->tag; +} + +dtrace:helper:ustack: +/!this->done && this->codecache && this->tag > 0/ +{ + MARK_LINE; + this->tag = copyin_uchar(this->block + this->segment); + this->segment = this->segment - this->tag; +} + +dtrace:helper:ustack: +/!this->done && this->codecache && this->tag > 0/ +{ + MARK_LINE; + this->tag = copyin_uchar(this->block + this->segment); + this->segment = this->segment - this->tag; +} + +dtrace:helper:ustack: +/!this->done && this->codecache && this->tag > 0/ +{ + MARK_LINE; + this->tag = copyin_uchar(this->block + this->segment); + this->segment = this->segment - this->tag; +} + +dtrace:helper:ustack: +/!this->done && this->codecache && this->tag > 0/ +{ + MARK_LINE; + this->error = ""; + this->done = 1; +} + +dtrace:helper:ustack: +/!this->done && this->codecache/ +{ + MARK_LINE; + this->block = this->CodeCache_low + + (this->segment << this->CodeHeap_log2_segment_size); + this->used = copyin_uint32(this->block + OFFSET_HeapBlockHeader_used); +} + +dtrace:helper:ustack: +/!this->done && this->codecache && !this->used/ +{ + MARK_LINE; + this->error = ""; + this->done = 1; +} + +dtrace:helper:ustack: +/!this->done && this->codecache/ +{ + MARK_LINE; + this->start = this->block + SIZE_HeapBlockHeader; + this->vtbl = copyin_ptr(this->start); + + this->nmethod_vtbl = (pointer) &``__1cHnmethodG__vtbl_; + this->BufferBlob_vtbl = (pointer) &``__1cKBufferBlobG__vtbl_; +} + +dtrace:helper:ustack: +/!this->done && this->vtbl == this->nmethod_vtbl/ +{ + MARK_LINE; + this->methodOopPtr = copyin_ptr(this->start + OFFSET_nmethod_method); + this->suffix = '*'; + this->methodOop = 1; +} + +dtrace:helper:ustack: +/!this->done && this->vtbl == this->BufferBlob_vtbl/ +{ + MARK_LINE; + this->name = copyin_ptr(this->start + OFFSET_CodeBlob_name); +} + +dtrace:helper:ustack: +/!this->done && this->vtbl == this->BufferBlob_vtbl && +this->methodOopPtr > this->heap_start && this->methodOopPtr < this->heap_end/ +{ + MARK_LINE; + this->klass = copyin_ptr(this->methodOopPtr + OFFSET_oopDesc_klass); + this->methodOop = this->klass == this->Universe_methodKlassOop; + this->done = !this->methodOop; +} + +dtrace:helper:ustack: +/!this->done && !this->methodOop/ +{ + MARK_LINE; + this->name = copyin_ptr(this->start + OFFSET_CodeBlob_name); + this->result = this->name != 0 ? copyinstr(this->name) : ""; + this->done = 1; +} + +dtrace:helper:ustack: +/!this->done && this->methodOop/ +{ + MARK_LINE; + this->constMethod = copyin_ptr(this->methodOopPtr + + OFFSET_methodOopDesc_constMethod); + + this->nameIndex = copyin_uint16(this->constMethod + + OFFSET_constMethodOopDesc_name_index); + + this->signatureIndex = copyin_uint16(this->constMethod + + OFFSET_constMethodOopDesc_signature_index); + + this->constantPool = copyin_ptr(this->methodOopPtr + + OFFSET_methodOopDesc_constants); + + this->nameSymbol = copyin_ptr(this->constantPool + + this->nameIndex * sizeof (pointer) + SIZE_constantPoolOopDesc); + + this->nameSymbolLength = copyin_uint16(this->nameSymbol + + OFFSET_symbolOopDesc_length); + + this->signatureSymbol = copyin_ptr(this->constantPool + + this->signatureIndex * sizeof (pointer) + SIZE_constantPoolOopDesc); + + this->signatureSymbolLength = copyin_uint16(this->signatureSymbol + + OFFSET_symbolOopDesc_length); + + this->klassPtr = copyin_ptr(this->constantPool + + OFFSET_constantPoolOopDesc_pool_holder); + + this->klassSymbol = copyin_ptr(this->klassPtr + + OFFSET_Klass_name + SIZE_oopDesc); + + this->klassSymbolLength = copyin_uint16(this->klassSymbol + + OFFSET_symbolOopDesc_length); + + /* + * Enough for three strings, plus the '.', plus the trailing '\0'. + */ + this->result = (char *) alloca(this->klassSymbolLength + + this->nameSymbolLength + + this->signatureSymbolLength + 2 + 1); + + copyinto(this->klassSymbol + OFFSET_symbolOopDesc_body, + this->klassSymbolLength, this->result); + + /* + * Add the '.' between the class and the name. + */ + this->result[this->klassSymbolLength] = '.'; + + copyinto(this->nameSymbol + OFFSET_symbolOopDesc_body, + this->nameSymbolLength, + this->result + this->klassSymbolLength + 1); + + copyinto(this->signatureSymbol + OFFSET_symbolOopDesc_body, + this->signatureSymbolLength, + this->result + this->klassSymbolLength + + this->nameSymbolLength + 1); + + /* + * Now we need to add a trailing '\0' and possibly a tag character. + */ + this->result[this->klassSymbolLength + 1 + + this->nameSymbolLength + + this->signatureSymbolLength] = this->suffix; + this->result[this->klassSymbolLength + 2 + + this->nameSymbolLength + + this->signatureSymbolLength] = '\0'; + + this->done = 1; +} + +dtrace:helper:ustack: +/this->done && this->error == (char *) NULL/ +{ + this->result; +} + +dtrace:helper:ustack: +/this->done && this->error != (char *) NULL/ +{ + this->error; +} + +dtrace:helper:ustack: +/!this->done && this->codecache/ +{ + this->done = 1; + "error"; +} + + +dtrace:helper:ustack: +/!this->done/ +{ + NULL; +} diff --git a/hotspot/src/os/solaris/dtrace/jvm_dtrace.c b/hotspot/src/os/solaris/dtrace/jvm_dtrace.c new file mode 100644 index 00000000000..e1d4da195bc --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/jvm_dtrace.c @@ -0,0 +1,565 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "jvm_dtrace.h" + +// NOTE: These constants are used in JVM code as well. +// KEEP JVM CODE IN SYNC if you are going to change these... + +#define DTRACE_ALLOC_PROBES 0x1 +#define DTRACE_METHOD_PROBES 0x2 +#define DTRACE_MONITOR_PROBES 0x4 +#define DTRACE_ALL_PROBES -1 + +// generic error messages +#define JVM_ERR_OUT_OF_MEMORY "out of memory (native heap)" +#define JVM_ERR_INVALID_PARAM "invalid input parameter(s)" +#define JVM_ERR_NULL_PARAM "input paramater is NULL" + +// error messages for attach +#define JVM_ERR_CANT_OPEN_DOOR "cannot open door file" +#define JVM_ERR_CANT_CREATE_ATTACH_FILE "cannot create attach file" +#define JVM_ERR_DOOR_FILE_PERMISSION "door file is not secure" +#define JVM_ERR_CANT_SIGNAL "cannot send SIGQUIT to target" + +// error messages for enable probe +#define JVM_ERR_DOOR_CMD_SEND "door command send failed" +#define JVM_ERR_DOOR_CANT_READ_STATUS "cannot read door command status" +#define JVM_ERR_DOOR_CMD_STATUS "door command error status" + +// error message for detach +#define JVM_ERR_CANT_CLOSE_DOOR "cannot close door file" + +#define RESTARTABLE(_cmd, _result) do { \ + do { \ + _result = _cmd; \ + } while((_result == -1) && (errno == EINTR)); \ +} while(0) + +struct _jvm_t { + pid_t pid; + int door_fd; +}; + +static int libjvm_dtrace_debug; +static void print_debug(const char* fmt,...) { + if (libjvm_dtrace_debug) { + va_list alist; + va_start(alist, fmt); + fputs("libjvm_dtrace DEBUG: ", stderr); + vfprintf(stderr, fmt, alist); + va_end(alist); + } +} + +/* Key for thread local error message */ +static thread_key_t jvm_error_key; + +/* init function for this library */ +static void init_jvm_dtrace() { + /* check for env. var for debug mode */ + libjvm_dtrace_debug = getenv("LIBJVM_DTRACE_DEBUG") != NULL; + /* create key for thread local error message */ + if (thr_keycreate(&jvm_error_key, NULL) != 0) { + print_debug("can't create thread_key_t for jvm error key\n"); + // exit(1); ? + } +} + +#pragma init(init_jvm_dtrace) + +/* set thread local error message */ +static void set_jvm_error(const char* msg) { + thr_setspecific(jvm_error_key, (void*)msg); +} + +/* clear thread local error message */ +static void clear_jvm_error() { + thr_setspecific(jvm_error_key, NULL); +} + +/* file handling functions that can handle interrupt */ + +static int file_open(const char* path, int flag) { + int ret; + RESTARTABLE(open(path, flag), ret); + return ret; +} + +static int file_close(int fd) { + int ret; + RESTARTABLE(close(fd), ret); + return ret; +} + +static int file_read(int fd, char* buf, int len) { + int ret; + RESTARTABLE(read(fd, buf, len), ret); + return ret; +} + +/* send SIGQUIT signal to given process */ +static int send_sigquit(pid_t pid) { + int ret; + RESTARTABLE(kill(pid, SIGQUIT), ret); + return ret; +} + +/* called to check permissions on attach file */ +static int check_permission(const char* path) { + struct stat64 sb; + uid_t uid, gid; + int res; + + /* + * Check that the path is owned by the effective uid/gid of this + * process. Also check that group/other access is not allowed. + */ + uid = geteuid(); + gid = getegid(); + + res = stat64(path, &sb); + if (res != 0) { + print_debug("stat failed for %s\n", path); + return -1; + } + + if ((sb.st_uid != uid) || (sb.st_gid != gid) || + ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0)) { + print_debug("well-known file %s is not secure\n", path); + return -1; + } + return 0; +} + +#define ATTACH_FILE_PATTERN "/tmp/.attach_pid%d" + +/* fill-in the name of attach file name in given buffer */ +static void fill_attach_file_name(char* path, int len, pid_t pid) { + memset(path, 0, len); + sprintf(path, ATTACH_FILE_PATTERN, pid); +} + +#define DOOR_FILE_PATTERN "/tmp/.java_pid%d" + +/* open door file for the given JVM */ +static int open_door(pid_t pid) { + char path[PATH_MAX + 1]; + int fd; + + sprintf(path, DOOR_FILE_PATTERN, pid); + fd = file_open(path, O_RDONLY); + if (fd < 0) { + set_jvm_error(JVM_ERR_CANT_OPEN_DOOR); + print_debug("cannot open door file %s\n", path); + return -1; + } + print_debug("opened door file %s\n", path); + if (check_permission(path) != 0) { + set_jvm_error(JVM_ERR_DOOR_FILE_PERMISSION); + print_debug("check permission failed for %s\n", path); + file_close(fd); + fd = -1; + } + return fd; +} + +/* create attach file for given process */ +static int create_attach_file(pid_t pid) { + char path[PATH_MAX + 1]; + int fd; + fill_attach_file_name(path, sizeof(path), pid); + fd = file_open(path, O_CREAT | O_RDWR); + if (fd < 0) { + set_jvm_error(JVM_ERR_CANT_CREATE_ATTACH_FILE); + print_debug("cannot create file %s\n", path); + } else { + print_debug("created attach file %s\n", path); + } + return fd; +} + +/* delete attach file for given process */ +static void delete_attach_file(pid_t pid) { + char path[PATH_MAX + 1]; + fill_attach_file_name(path, sizeof(path), pid); + int res = unlink(path); + if (res) { + print_debug("cannot delete attach file %s\n", path); + } else { + print_debug("deleted attach file %s\n", path); + } +} + +/* attach to given JVM */ +jvm_t* jvm_attach(pid_t pid) { + jvm_t* jvm; + int door_fd, attach_fd, i; + + jvm = (jvm_t*) calloc(1, sizeof(jvm_t)); + if (jvm == NULL) { + set_jvm_error(JVM_ERR_OUT_OF_MEMORY); + print_debug("calloc failed in %s at %d\n", __FILE__, __LINE__); + return NULL; + } + jvm->pid = pid; + attach_fd = -1; + + door_fd = open_door(pid); + if (door_fd < 0) { + print_debug("trying to create attach file\n"); + if ((attach_fd = create_attach_file(pid)) < 0) { + goto quit; + } + + /* send QUIT signal to the target so that it will + * check for the attach file. + */ + if (send_sigquit(pid) != 0) { + set_jvm_error(JVM_ERR_CANT_SIGNAL); + print_debug("sending SIGQUIT failed\n"); + goto quit; + } + + /* give the target VM time to start the attach mechanism */ + do { + int res; + RESTARTABLE(poll(0, 0, 200), res); + door_fd = open_door(pid); + i++; + } while (i <= 50 && door_fd == -1); + if (door_fd < 0) { + print_debug("Unable to open door to process %d\n", pid); + goto quit; + } + } + +quit: + if (attach_fd >= 0) { + file_close(attach_fd); + delete_attach_file(jvm->pid); + } + if (door_fd >= 0) { + jvm->door_fd = door_fd; + clear_jvm_error(); + } else { + free(jvm); + jvm = NULL; + } + return jvm; +} + +/* return the last thread local error message */ +const char* jvm_get_last_error() { + const char* res = NULL; + thr_getspecific(jvm_error_key, (void**)&res); + return res; +} + +/* detach the givenb JVM */ +int jvm_detach(jvm_t* jvm) { + if (jvm) { + int res; + if (jvm->door_fd != -1) { + if (file_close(jvm->door_fd) != 0) { + set_jvm_error(JVM_ERR_CANT_CLOSE_DOOR); + res = -1; + } else { + clear_jvm_error(); + res = 0; + } + } + free(jvm); + return res; + } else { + set_jvm_error(JVM_ERR_NULL_PARAM); + print_debug("jvm_t* is NULL\n"); + return -1; + } +} + +/* + * A simple table to translate some known errors into reasonable + * error messages + */ +static struct { + int err; + const char* msg; +} const error_messages[] = { + { 100, "Bad request" }, + { 101, "Protocol mismatch" }, + { 102, "Resource failure" }, + { 103, "Internal error" }, + { 104, "Permission denied" }, +}; + +/* + * Lookup the given error code and return the appropriate + * message. If not found return NULL. + */ +static const char* translate_error(int err) { + int table_size = sizeof(error_messages) / sizeof(error_messages[0]); + int i; + + for (i=0; i\0\0 + */ + if (cstr == NULL) { + print_debug("command name is NULL\n"); + goto quit; + } + size = strlen(PROTOCOL_VERSION) + strlen(cstr) + 2; + buf = (char*)malloc(size); + if (buf != NULL) { + char* pos = buf; + strcpy(buf, PROTOCOL_VERSION); + pos += strlen(PROTOCOL_VERSION)+1; + strcpy(pos, cstr); + } else { + set_jvm_error(JVM_ERR_OUT_OF_MEMORY); + print_debug("malloc failed at %d in %s\n", __LINE__, __FILE__); + goto quit; + } + + /* + * Next we iterate over the arguments and extend the buffer + * to include them. + */ + for (i=0; idoor_fd, &door_args), rc); + + /* + * door_call failed + */ + if (rc == -1) { + print_debug("door_call failed\n"); + } else { + /* + * door_call succeeded but the call didn't return the the expected jint. + */ + if (door_args.data_size < sizeof(int)) { + print_debug("Enqueue error - reason unknown as result is truncated!"); + } else { + int* res = (int*)(door_args.data_ptr); + if (*res != 0) { + const char* msg = translate_error(*res); + if (msg == NULL) { + print_debug("Unable to enqueue command to target VM: %d\n", *res); + } else { + print_debug("Unable to enqueue command to target VM: %s\n", msg); + } + } else { + /* + * The door call should return a file descriptor to one end of + * a socket pair + */ + if ((door_args.desc_ptr != NULL) && + (door_args.desc_num == 1) && + (door_args.desc_ptr->d_attributes & DOOR_DESCRIPTOR)) { + result = door_args.desc_ptr->d_data.d_desc.d_descriptor; + } else { + print_debug("Reply from enqueue missing descriptor!\n"); + } + } + } + } + +quit: + if (buf) free(buf); + return result; +} + +/* read status code for a door command */ +static int read_status(int fd) { + char ch, buf[16]; + int index = 0; + + while (1) { + if (file_read(fd, &ch, sizeof(ch)) != sizeof(ch)) { + set_jvm_error(JVM_ERR_DOOR_CANT_READ_STATUS); + print_debug("door cmd status: read status failed\n"); + return -1; + } + buf[index++] = ch; + if (ch == '\n') { + buf[index - 1] = '\0'; + return atoi(buf); + } + if (index == sizeof(buf)) { + set_jvm_error(JVM_ERR_DOOR_CANT_READ_STATUS); + print_debug("door cmd status: read status overflow\n"); + return -1; + } + } +} + +static const char* ENABLE_DPROBES_CMD = "enabledprobes"; + +/* enable one or more DTrace probes for a given JVM */ +int jvm_enable_dtprobes(jvm_t* jvm, int num_probe_types, const char** probe_types) { + int fd, status = 0; + char ch; + const char* args[1]; + char buf[16]; + int probe_type = 0, index; + int count = 0; + + if (jvm == NULL) { + set_jvm_error(JVM_ERR_NULL_PARAM); + print_debug("jvm_t* is NULL\n"); + return -1; + } + + if (num_probe_types == 0 || probe_types == NULL || + probe_types[0] == NULL) { + set_jvm_error(JVM_ERR_INVALID_PARAM); + print_debug("invalid probe type argument(s)\n"); + return -1; + } + + for (index = 0; index < num_probe_types; index++) { + const char* p = probe_types[index]; + if (strcmp(p, JVM_DTPROBE_OBJECT_ALLOC) == 0) { + probe_type |= DTRACE_ALLOC_PROBES; + count++; + } else if (strcmp(p, JVM_DTPROBE_METHOD_ENTRY) == 0 || + strcmp(p, JVM_DTPROBE_METHOD_RETURN) == 0) { + probe_type |= DTRACE_METHOD_PROBES; + count++; + } else if (strcmp(p, JVM_DTPROBE_MONITOR_ENTER) == 0 || + strcmp(p, JVM_DTPROBE_MONITOR_ENTERED) == 0 || + strcmp(p, JVM_DTPROBE_MONITOR_EXIT) == 0 || + strcmp(p, JVM_DTPROBE_MONITOR_WAIT) == 0 || + strcmp(p, JVM_DTPROBE_MONITOR_WAITED) == 0 || + strcmp(p, JVM_DTPROBE_MONITOR_NOTIFY) == 0 || + strcmp(p, JVM_DTPROBE_MONITOR_NOTIFYALL) == 0) { + probe_type |= DTRACE_MONITOR_PROBES; + count++; + } else if (strcmp(p, JVM_DTPROBE_ALL) == 0) { + probe_type |= DTRACE_ALL_PROBES; + count++; + } + } + + if (count == 0) { + return count; + } + sprintf(buf, "%d", probe_type); + args[0] = buf; + + fd = enqueue_command(jvm, ENABLE_DPROBES_CMD, 1, args); + if (fd < 0) { + set_jvm_error(JVM_ERR_DOOR_CMD_SEND); + return -1; + } + + status = read_status(fd); + // non-zero status is error + if (status) { + set_jvm_error(JVM_ERR_DOOR_CMD_STATUS); + print_debug("%s command failed (status: %d) in target JVM\n", + ENABLE_DPROBES_CMD, status); + file_close(fd); + return -1; + } + // read from stream until EOF + while (file_read(fd, &ch, sizeof(ch)) == sizeof(ch)) { + if (libjvm_dtrace_debug) { + printf("%c", ch); + } + } + + file_close(fd); + clear_jvm_error(); + return count; +} diff --git a/hotspot/src/os/solaris/dtrace/jvm_dtrace.h b/hotspot/src/os/solaris/dtrace/jvm_dtrace.h new file mode 100644 index 00000000000..7ea916ab960 --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/jvm_dtrace.h @@ -0,0 +1,86 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _JVM_DTRACE_H_ +#define _JVM_DTRACE_H_ + +/* + * Interface to dynamically turn on probes in Hotspot JVM. Currently, + * this interface can be used to dynamically enable certain DTrace + * probe points that are costly to have "always on". + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + +struct _jvm_t; +typedef struct _jvm_t jvm_t; + + +/* Attach to the given JVM process. Returns NULL on failure. + jvm_get_last_error() returns last error message. */ +jvm_t* jvm_attach(pid_t pid); + +/* Returns the last error message from this library or NULL if none. */ +const char* jvm_get_last_error(); + +/* few well-known probe type constants for 'probe_types' param below */ + +#define JVM_DTPROBE_METHOD_ENTRY "method-entry" +#define JVM_DTPROBE_METHOD_RETURN "method-return" +#define JVM_DTPROBE_MONITOR_ENTER "monitor-contended-enter" +#define JVM_DTPROBE_MONITOR_ENTERED "monitor-contended-entered" +#define JVM_DTPROBE_MONITOR_EXIT "monitor-contended-exit" +#define JVM_DTPROBE_MONITOR_WAIT "monitor-wait" +#define JVM_DTPROBE_MONITOR_WAITED "monitor-waited" +#define JVM_DTPROBE_MONITOR_NOTIFY "monitor-notify" +#define JVM_DTPROBE_MONITOR_NOTIFYALL "monitor-notifyall" +#define JVM_DTPROBE_OBJECT_ALLOC "object-alloc" +#define JVM_DTPROBE_ALL "*" + +/* Enable the specified DTrace probes of given probe types on + * the specified JVM. Returns >= 0 on success, -1 on failure. + * On success, this returns number of probe_types enabled. + * On failure, jvm_get_last_error() returns the last error message. + */ +int jvm_enable_dtprobes(jvm_t* jvm, int num_probe_types, const char** probe_types); + +/* Note: There is no jvm_disable_dtprobes function. Probes are automatically + * disabled when there are no more clients requiring those probes. + */ + +/* Detach the given JVM. Returns 0 on success, -1 on failure. + * jvm_get_last_error() returns the last error message. + */ +int jvm_detach(jvm_t* jvm); + +#ifdef __cplusplus +} +#endif + +#endif /* _JVM_DTRACE_H_ */ diff --git a/hotspot/src/os/solaris/dtrace/libjvm_db.c b/hotspot/src/os/solaris/dtrace/libjvm_db.c new file mode 100644 index 00000000000..ac21ec23b19 --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/libjvm_db.c @@ -0,0 +1,1500 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include +#include +#include +#include +#include + +#include "libjvm_db.h" +#include "JvmOffsets.h" + +#define LIBJVM_SO "libjvm.so" + +#if defined(i386) || defined(__i386) || defined(__amd64) +#ifdef COMPILER2 +#define X86_COMPILER2 +#endif /* COMPILER2 */ +#endif /* i386 */ + +typedef struct { + short vf_cnt; /* number of recognized java vframes */ + short bci; /* current frame method byte code index */ + int line; /* current frame method source line */ + uint64_t new_fp; /* fp for the next frame */ + uint64_t new_pc; /* pc for the next frame */ + uint64_t new_sp; /* "raw" sp for the next frame (includes extension by interpreter/adapter */ + char locinf; /* indicates there is valid location info */ +} Jframe_t; + +int Jlookup_by_regs(jvm_agent_t* J, const prgregset_t regs, char *name, + size_t size, Jframe_t *jframe); + +int main(int arg) { return arg; } + +static int debug = 0; + +static void failed(int err, const char * file, int line) { + if (debug) { + fprintf(stderr, "failed %d at %s:%d\n", err, file, line); + } +} + +static void warn(const char * file, int line, const char * msg) { + if (debug) { + fprintf(stderr, "warning: %s at %s:%d\n", msg, file, line); + } +} + +static void warn1(const char * file, int line, const char * msg, intptr_t arg1) { + if (debug) { + fprintf(stderr, "warning: "); + fprintf(stderr, msg, arg1); + fprintf(stderr, " at %s:%d\n", file, line); + } +} + +#define CHECK_FAIL(err) \ + if (err != PS_OK) { failed(err, __FILE__, __LINE__); goto fail; } +#define WARN(msg) warn(__FILE__, __LINE__, msg) +#define WARN1(msg, arg1) warn1(__FILE__, __LINE__, msg, arg1) + +typedef struct VMStructEntry { + const char * typeName; /* The type name containing the given field (example: "Klass") */ + const char * fieldName; /* The field name within the type (example: "_name") */ + uint64_t address; /* Address of field; only used for static fields */ + /* ("offset" can not be reused because of apparent SparcWorks compiler bug */ + /* in generation of initializer data) */ +} VMStructEntry; + +/* Prototyping inlined methods */ + +int sprintf(char *s, const char *format, ...); + +#define SZ16 sizeof(int16_t) +#define SZ32 sizeof(int32_t) + +#define COMP_METHOD_SIGN '*' + +#define MAX_VFRAMES_CNT 256 + +typedef struct vframe { + uint64_t methodOop; + int32_t sender_decode_offset; + int32_t methodIdx; + int32_t bci; + int32_t line; +} Vframe_t; + +typedef struct frame { + uintptr_t fp; + uintptr_t pc; + uintptr_t sp; + uintptr_t sender_sp; // The unextended sp of the caller +} Frame_t; + +typedef struct Nmethod_t { + struct jvm_agent* J; + Jframe_t *jframe; + + uint64_t nm; /* _nmethod */ + uint64_t pc; + uint64_t pc_desc; + + int32_t orig_pc_offset; /* _orig_pc_offset */ + int32_t instrs_beg; /* _instructions_offset */ + int32_t instrs_end; + int32_t deopt_beg; /* _deoptimize_offset */ + int32_t scopes_data_beg; /* _scopes_data_offset */ + int32_t scopes_data_end; + int32_t oops_beg; /* _oops_offset */ + int32_t oops_len; /* _oops_length */ + int32_t scopes_pcs_beg; /* _scopes_pcs_offset */ + int32_t scopes_pcs_end; + + int vf_cnt; + Vframe_t vframes[MAX_VFRAMES_CNT]; +} Nmethod_t; + +struct jvm_agent { + struct ps_prochandle* P; + + uint64_t nmethod_vtbl; + uint64_t CodeBlob_vtbl; + uint64_t BufferBlob_vtbl; + uint64_t RuntimeStub_vtbl; + + uint64_t Universe_methodKlassObj_address; + uint64_t CodeCache_heap_address; + + /* Volatiles */ + uint64_t Universe_methodKlassObj; + uint64_t CodeCache_low; + uint64_t CodeCache_high; + uint64_t CodeCache_segmap_low; + uint64_t CodeCache_segmap_high; + + int32_t SIZE_CodeCache_log2_segment; + + uint64_t methodOopPtr; + uint64_t bcx; + + Nmethod_t *N; /*Inlined methods support */ + Frame_t prev_fr; + Frame_t curr_fr; +}; + + +static int +read_string(struct ps_prochandle *P, + char *buf, /* caller's buffer */ + size_t size, /* upper limit on bytes to read */ + uintptr_t addr) /* address in process */ +{ + int err = PS_OK; + while (size-- > 1 && err == PS_OK) { + err = ps_pread(P, addr, buf, 1); + if (*buf == '\0') { + return PS_OK; + } + addr += 1; + buf += 1; + } + return -1; +} + +static int read_pointer(jvm_agent_t* J, uint64_t base, uint64_t* ptr) { + int err = -1; + uint32_t ptr32; + + switch (DATA_MODEL) { + case PR_MODEL_LP64: + err = ps_pread(J->P, base, ptr, sizeof(uint64_t)); + break; + case PR_MODEL_ILP32: + err = ps_pread(J->P, base, &ptr32, sizeof(uint32_t)); + *ptr = ptr32; + break; + } + + return err; +} + +static int read_string_pointer(jvm_agent_t* J, uint64_t base, const char ** stringp) { + uint64_t ptr; + int err; + char buffer[1024]; + + *stringp = NULL; + err = read_pointer(J, base, &ptr); + CHECK_FAIL(err); + if (ptr != 0) { + err = read_string(J->P, buffer, sizeof(buffer), ptr); + CHECK_FAIL(err); + *stringp = strdup(buffer); + } + return PS_OK; + + fail: + return err; +} + +static int parse_vmstruct_entry(jvm_agent_t* J, uint64_t base, VMStructEntry* vmp) { + uint64_t ptr; + int err; + + err = read_string_pointer(J, base + OFFSET_VMStructEntrytypeName, &vmp->typeName); + CHECK_FAIL(err); + err = read_string_pointer(J, base + OFFSET_VMStructEntryfieldName, &vmp->fieldName); + CHECK_FAIL(err); + err = read_pointer(J, base + OFFSET_VMStructEntryaddress, &vmp->address); + CHECK_FAIL(err); + + return PS_OK; + + fail: + if (vmp->typeName != NULL) free((void*)vmp->typeName); + if (vmp->fieldName != NULL) free((void*)vmp->fieldName); + return err; +} + +static int parse_vmstructs(jvm_agent_t* J) { + VMStructEntry vmVar; + VMStructEntry* vmp = &vmVar; + uint64_t gHotSpotVMStructs; + psaddr_t sym_addr; + uint64_t base; + int err; + + err = ps_pglobal_lookup(J->P, LIBJVM_SO, "gHotSpotVMStructs", &sym_addr); + CHECK_FAIL(err); + err = read_pointer(J, sym_addr, &gHotSpotVMStructs); + CHECK_FAIL(err); + base = gHotSpotVMStructs; + + err = PS_OK; + while (err == PS_OK) { + memset(vmp, 0, sizeof(VMStructEntry)); + err = parse_vmstruct_entry(J, base, vmp); + if (err != PS_OK || vmp->typeName == NULL) { + break; + } + + if (vmp->typeName[0] == 'C' && strcmp("CodeCache", vmp->typeName) == 0) { + if (strcmp("_heap", vmp->fieldName) == 0) { + err = read_pointer(J, vmp->address, &J->CodeCache_heap_address); + } + } else if (vmp->typeName[0] == 'U' && strcmp("Universe", vmp->typeName) == 0) { + if (strcmp("_methodKlassObj", vmp->fieldName) == 0) { + J->Universe_methodKlassObj_address = vmp->address; + } + } + CHECK_FAIL(err); + + base += SIZE_VMStructEntry; + if (vmp->typeName != NULL) free((void*)vmp->typeName); + if (vmp->fieldName != NULL) free((void*)vmp->fieldName); + } + + return PS_OK; + + fail: + if (vmp->typeName != NULL) free((void*)vmp->typeName); + if (vmp->fieldName != NULL) free((void*)vmp->fieldName); + return -1; +} + +static int read_volatiles(jvm_agent_t* J) { + uint64_t ptr; + int err; + + err = read_pointer(J, J->Universe_methodKlassObj_address, &J->Universe_methodKlassObj); + CHECK_FAIL(err); + err = read_pointer(J, J->CodeCache_heap_address + OFFSET_CodeHeap_memory + + OFFSET_VirtualSpace_low, &J->CodeCache_low); + CHECK_FAIL(err); + err = read_pointer(J, J->CodeCache_heap_address + OFFSET_CodeHeap_memory + + OFFSET_VirtualSpace_high, &J->CodeCache_high); + CHECK_FAIL(err); + err = read_pointer(J, J->CodeCache_heap_address + OFFSET_CodeHeap_segmap + + OFFSET_VirtualSpace_low, &J->CodeCache_segmap_low); + CHECK_FAIL(err); + err = read_pointer(J, J->CodeCache_heap_address + OFFSET_CodeHeap_segmap + + OFFSET_VirtualSpace_high, &J->CodeCache_segmap_high); + CHECK_FAIL(err); + + err = ps_pread(J->P, J->CodeCache_heap_address + OFFSET_CodeHeap_log2_segment_size, + &J->SIZE_CodeCache_log2_segment, sizeof(J->SIZE_CodeCache_log2_segment)); + CHECK_FAIL(err); + + return PS_OK; + + fail: + return err; +} + + +static int codecache_contains(jvm_agent_t* J, uint64_t ptr) { + /* make sure the code cache is up to date */ + return (J->CodeCache_low <= ptr && ptr < J->CodeCache_high); +} + +static uint64_t segment_for(jvm_agent_t* J, uint64_t p) { + return (p - J->CodeCache_low) >> J->SIZE_CodeCache_log2_segment; +} + +static uint64_t block_at(jvm_agent_t* J, int i) { + return J->CodeCache_low + (i << J->SIZE_CodeCache_log2_segment); +} + +static int find_start(jvm_agent_t* J, uint64_t ptr, uint64_t *startp) { + int err; + + *startp = 0; + if (J->CodeCache_low <= ptr && ptr < J->CodeCache_high) { + int32_t used; + uint64_t segment = segment_for(J, ptr); + uint64_t block = J->CodeCache_segmap_low; + uint8_t tag; + err = ps_pread(J->P, block + segment, &tag, sizeof(tag)); + CHECK_FAIL(err); + if (tag == 0xff) + return PS_OK; + while (tag > 0) { + err = ps_pread(J->P, block + segment, &tag, sizeof(tag)); + CHECK_FAIL(err); + segment -= tag; + } + block = block_at(J, segment); + err = ps_pread(J->P, block + OFFSET_HeapBlockHeader_used, &used, sizeof(used)); + CHECK_FAIL(err); + if (used) { + *startp = block + SIZE_HeapBlockHeader; + } + } + return PS_OK; + + fail: + return -1; +} + +static int find_symbol(jvm_agent_t* J, const char *name, uint64_t* valuep) { + psaddr_t sym_addr; + int err; + + err = ps_pglobal_lookup(J->P, LIBJVM_SO, name, &sym_addr); + if (err != PS_OK) goto fail; + *valuep = sym_addr; + return PS_OK; + + fail: + return err; +} + +static int find_jlong_constant(jvm_agent_t* J, const char *name, uint64_t* valuep) { + psaddr_t sym_addr; + int err = ps_pglobal_lookup(J->P, LIBJVM_SO, name, &sym_addr); + if (err == PS_OK) { + err = ps_pread(J->P, sym_addr, valuep, sizeof(uint64_t)); + return err; + } + *valuep = -1; + return -1; +} + +jvm_agent_t *Jagent_create(struct ps_prochandle *P, int vers) { + jvm_agent_t* J; + int err; + + if (vers != JVM_DB_VERSION) { + errno = ENOTSUP; + return NULL; + } + + J = (jvm_agent_t*)calloc(sizeof(struct jvm_agent), 1); + + debug = getenv("LIBJVMDB_DEBUG") != NULL; + if (debug) debug = 3; + + if (debug) { + fprintf(stderr, "Jagent_create: debug=%d\n", debug); +#ifdef X86_COMPILER2 + fprintf(stderr, "Jagent_create: R_SP=%d, R_FP=%d, POINTER_SIZE=%d\n", R_SP, R_FP, POINTER_SIZE); +#endif /* X86_COMPILER2 */ + } + + J->P = P; + + // Initialize the initial previous frame + + J->prev_fr.fp = 0; + J->prev_fr.pc = 0; + J->prev_fr.sp = 0; + J->prev_fr.sender_sp = 0; + + err = find_symbol(J, "__1cHnmethodG__vtbl_", &J->nmethod_vtbl); + CHECK_FAIL(err); + err = find_symbol(J, "__1cKBufferBlobG__vtbl_", &J->BufferBlob_vtbl); + if (err != PS_OK) J->BufferBlob_vtbl = 0; + err = find_symbol(J, "__1cICodeBlobG__vtbl_", &J->CodeBlob_vtbl); + CHECK_FAIL(err); + err = find_symbol(J, "__1cLRuntimeStubG__vtbl_", &J->RuntimeStub_vtbl); + CHECK_FAIL(err); + + err = parse_vmstructs(J); + CHECK_FAIL(err); + err = read_volatiles(J); + CHECK_FAIL(err); + + return J; + + fail: + Jagent_destroy(J); + return NULL; +} + +void Jagent_destroy(jvm_agent_t *J) { + if (J != NULL) { + free(J); + } +} + +static int is_methodOop(jvm_agent_t* J, uint64_t methodOopPtr) { + uint64_t klass; + int err; + err = read_pointer(J, methodOopPtr + OFFSET_oopDesc_klass, &klass); + if (err != PS_OK) goto fail; + return klass == J->Universe_methodKlassObj; + + fail: + return 0; +} + +static int +name_for_methodOop(jvm_agent_t* J, uint64_t methodOopPtr, char * result, size_t size) +{ + short nameIndex; + short signatureIndex; + uint64_t constantPool; + uint64_t constMethod; + uint64_t nameSymbol; + uint64_t signatureSymbol; + uint64_t klassPtr; + uint64_t klassSymbol; + short klassSymbolLength; + short nameSymbolLength; + short signatureSymbolLength; + char * nameString = NULL; + char * klassString = NULL; + char * signatureString = NULL; + int err; + + err = read_pointer(J, methodOopPtr + OFFSET_methodOopDesc_constants, &constantPool); + CHECK_FAIL(err); + err = read_pointer(J, methodOopPtr + OFFSET_methodOopDesc_constMethod, &constMethod); + CHECK_FAIL(err); + + /* To get name string */ + err = ps_pread(J->P, constMethod + OFFSET_constMethodOopDesc_name_index, &nameIndex, 2); + CHECK_FAIL(err); + err = read_pointer(J, constantPool + nameIndex * POINTER_SIZE + SIZE_constantPoolOopDesc, &nameSymbol); + CHECK_FAIL(err); + err = ps_pread(J->P, nameSymbol + OFFSET_symbolOopDesc_length, &nameSymbolLength, 2); + CHECK_FAIL(err); + nameString = (char*)calloc(nameSymbolLength + 1, 1); + err = ps_pread(J->P, nameSymbol + OFFSET_symbolOopDesc_body, nameString, nameSymbolLength); + CHECK_FAIL(err); + + /* To get signature string */ + err = ps_pread(J->P, constMethod + OFFSET_constMethodOopDesc_signature_index, &signatureIndex, 2); + CHECK_FAIL(err); + err = read_pointer(J, constantPool + signatureIndex * POINTER_SIZE + SIZE_constantPoolOopDesc, &signatureSymbol); + CHECK_FAIL(err); + err = ps_pread(J->P, signatureSymbol + OFFSET_symbolOopDesc_length, &signatureSymbolLength, 2); + CHECK_FAIL(err); + signatureString = (char*)calloc(signatureSymbolLength + 1, 1); + err = ps_pread(J->P, signatureSymbol + OFFSET_symbolOopDesc_body, signatureString, signatureSymbolLength); + CHECK_FAIL(err); + + /* To get klass string */ + err = read_pointer(J, constantPool + OFFSET_constantPoolOopDesc_pool_holder, &klassPtr); + CHECK_FAIL(err); + err = read_pointer(J, klassPtr + OFFSET_Klass_name + SIZE_oopDesc, &klassSymbol); + CHECK_FAIL(err); + err = ps_pread(J->P, klassSymbol + OFFSET_symbolOopDesc_length, &klassSymbolLength, 2); + CHECK_FAIL(err); + klassString = (char*)calloc(klassSymbolLength + 1, 1); + err = ps_pread(J->P, klassSymbol + OFFSET_symbolOopDesc_body, klassString, klassSymbolLength); + CHECK_FAIL(err); + + result[0] = '\0'; + strncat(result, klassString, size); + size -= strlen(klassString); + strncat(result, ".", size); + size -= 1; + strncat(result, nameString, size); + size -= strlen(nameString); + strncat(result, signatureString, size); + + if (nameString != NULL) free(nameString); + if (klassString != NULL) free(klassString); + if (signatureString != NULL) free(signatureString); + + return PS_OK; + + fail: + if (debug) { + fprintf(stderr, "name_for_methodOop: FAIL \n\n"); + } + if (nameString != NULL) free(nameString); + if (klassString != NULL) free(klassString); + if (signatureString != NULL) free(signatureString); + return -1; +} + +static int nmethod_info(Nmethod_t *N) +{ + jvm_agent_t *J = N->J; + uint64_t nm = N->nm; + int32_t err; + + if (debug > 2 ) + fprintf(stderr, "\t nmethod_info: BEGIN \n"); + + /* Instructions */ + err = ps_pread(J->P, nm + OFFSET_CodeBlob_instructions_offset, &N->instrs_beg, SZ32); + CHECK_FAIL(err); + err = ps_pread(J->P, nm + OFFSET_CodeBlob_data_offset, &N->instrs_end, SZ32); + CHECK_FAIL(err); + err = ps_pread(J->P, nm + OFFSET_nmethod_deoptimize_offset, &N->deopt_beg, SZ32); + CHECK_FAIL(err); + err = ps_pread(J->P, nm + OFFSET_nmethod_orig_pc_offset, &N->orig_pc_offset, SZ32); + CHECK_FAIL(err); + + /* Oops */ + err = ps_pread(J->P, nm + OFFSET_CodeBlob_oops_offset, &N->oops_beg, SZ32); + CHECK_FAIL(err); + err = ps_pread(J->P, nm + OFFSET_CodeBlob_oops_length, &N->oops_len, SZ32); + CHECK_FAIL(err); + + /* scopes_pcs */ + err = ps_pread(J->P, nm + OFFSET_nmethod_scopes_pcs_offset, &N->scopes_pcs_beg, SZ32); + CHECK_FAIL(err); + err = ps_pread(J->P, nm + OFFSET_nmethod_handler_table_offset, &N->scopes_pcs_end, SZ32); + CHECK_FAIL(err); + + /* scopes_data */ + err = ps_pread(J->P, nm + OFFSET_nmethod_scopes_data_offset, &N->scopes_data_beg, SZ32); + CHECK_FAIL(err); + + if (debug > 2 ) { + N->scopes_data_end = N->scopes_pcs_beg; + + fprintf(stderr, "\t nmethod_info: instrs_beg: %#x, instrs_end: %#x\n", + N->instrs_beg, N->instrs_end); + + fprintf(stderr, "\t nmethod_info: deopt_beg: %#x \n", + N->deopt_beg); + + fprintf(stderr, "\t nmethod_info: orig_pc_offset: %#x \n", + N->orig_pc_offset); + + fprintf(stderr, "\t nmethod_info: oops_beg: %#x, oops_len: %#x\n", + N->oops_beg, N->oops_len); + + fprintf(stderr, "\t nmethod_info: scopes_data_beg: %#x, scopes_data_end: %#x\n", + N->scopes_data_beg, N->scopes_data_end); + + fprintf(stderr, "\t nmethod_info: scopes_pcs_beg: %#x, scopes_pcs_end: %#x\n", + N->scopes_pcs_beg, N->scopes_pcs_end); + + fprintf(stderr, "\t nmethod_info: END \n\n"); + } + return PS_OK; + + fail: + return err; +} + +static int +raw_read_int(jvm_agent_t* J, uint64_t *buffer, int32_t *val) +{ + int shift = 0; + int value = 0; + uint8_t ch = 0; + int32_t err; + int32_t sum; + // Constants for UNSIGNED5 coding of Pack200 + // see compressedStream.hpp + enum { + lg_H = 6, + H = 1<P, (*buffer)++, &ch, sizeof(uint8_t)); + CHECK_FAIL(err); + if (debug > 2) + fprintf(stderr, "\t\t\t raw_read_int: *buffer: %#llx, ch: %#x\n", *buffer, ch); + + sum = ch; + if ( sum >= L ) { + int32_t lg_H_i = lg_H; + // Read maximum of 5 total bytes (we've already read 1). + // See CompressedReadStream::read_int_mb + for ( i = 0; i < 4; i++) { + err = ps_pread(J->P, (*buffer)++, &ch, sizeof(uint8_t)); + CHECK_FAIL(err); + sum += ch << lg_H_i; + if (ch < L ) { + *val = sum; + return PS_OK; + } + lg_H_i += lg_H; + } + } + *val = sum; + return PS_OK; + + fail: + return err; +} + +static int +read_pair(jvm_agent_t* J, uint64_t *buffer, int32_t *bci, int32_t *line) +{ + uint8_t next = 0; + int32_t bci_delta; + int32_t line_delta; + int32_t err; + + if (debug > 2) + fprintf(stderr, "\t\t read_pair: BEGIN\n"); + + err = ps_pread(J->P, (*buffer)++, &next, sizeof(uint8_t)); + CHECK_FAIL(err); + + if (next == 0) { + if (debug > 2) + fprintf(stderr, "\t\t read_pair: END: next == 0\n"); + return 1; /* stream terminated */ + } + if (next == 0xFF) { + if (debug > 2) + fprintf(stderr, "\t\t read_pair: END: next == 0xFF\n"); + + /* Escape character, regular compression used */ + + err = raw_read_int(J, buffer, &bci_delta); + CHECK_FAIL(err); + + err = raw_read_int(J, buffer, &line_delta); + CHECK_FAIL(err); + + *bci += bci_delta; + *line += line_delta; + + if (debug > 2) { + fprintf(stderr, "\t\t read_pair: delta = (line %d: %d)\n", + line_delta, bci_delta); + fprintf(stderr, "\t\t read_pair: unpack= (line %d: %d)\n", + *line, *bci); + } + } else { + /* Single byte compression used */ + *bci += next >> 3; + *line += next & 0x7; + if (debug > 2) { + fprintf(stderr, "\t\t read_pair: delta = (line %d: %d)\n", + next & 0x7, next >> 3); + fprintf(stderr, "\t\t read_pair: unpack= (line %d: %d)\n", + *line, *bci); + } + } + if (debug > 2) + fprintf(stderr, "\t\t read_pair: END\n"); + return PS_OK; + + fail: + if (debug) + fprintf(stderr, "\t\t read_pair: FAIL\n"); + return err; +} + +static int +line_number_from_bci(jvm_agent_t* J, Vframe_t *vf) +{ + uint64_t buffer; + uint16_t code_size; + uint64_t code_end_delta; + uint64_t constMethod; + int8_t access_flags; + int32_t best_bci = 0; + int32_t stream_bci = 0; + int32_t stream_line = 0; + int32_t err; + + if (debug > 2) { + char name[256]; + err = name_for_methodOop(J, vf->methodOop, name, 256); + CHECK_FAIL(err); + fprintf(stderr, "\t line_number_from_bci: BEGIN, method name: %s, targ bci: %d\n", + name, vf->bci); + } + + err = read_pointer(J, vf->methodOop + OFFSET_methodOopDesc_constMethod, &constMethod); + CHECK_FAIL(err); + + vf->line = 0; + err = ps_pread(J->P, constMethod + OFFSET_constMethodOopDesc_flags, &access_flags, sizeof(int8_t)); + CHECK_FAIL(err); + + if (!(access_flags & constMethodOopDesc_has_linenumber_table)) { + if (debug > 2) + fprintf(stderr, "\t line_number_from_bci: END: !HAS_LINE_NUMBER_TABLE \n\n"); + return PS_OK; + } + + /* The line numbers are a short array of 2-tuples [start_pc, line_number]. + * Not necessarily sorted and not necessarily one-to-one. + */ + + err = ps_pread(J->P, constMethod + OFFSET_constMethodOopDesc_code_size, &code_size, SZ16); + CHECK_FAIL(err); + + /* inlined_table_start() */ + code_end_delta = (uint64_t) (access_flags & AccessFlags_NATIVE) ? 2*POINTER_SIZE : 0; + buffer = constMethod + (uint64_t) SIZE_constMethodOopDesc + (uint64_t) code_size + code_end_delta; + + if (debug > 2) { + fprintf(stderr, "\t\t line_number_from_bci: methodOop: %#llx, native: %d\n", + vf->methodOop, (access_flags & AccessFlags_NATIVE)); + fprintf(stderr, "\t\t line_number_from_bci: buffer: %#llx, code_size: %d\n", + buffer, (int) code_size); + } + + while (read_pair(J, &buffer, &stream_bci, &stream_line) == 0) { + if (stream_bci == vf->bci) { + /* perfect match */ + if (debug > 2) + fprintf(stderr, "\t line_number_from_bci: END: exact line: %ld \n\n", vf->line); + vf->line = stream_line; + return PS_OK; + } else { + /* update best_bci/line */ + if (stream_bci < vf->bci && stream_bci >= best_bci) { + best_bci = stream_bci; + vf->line = stream_line; + if (debug > 2) { + fprintf(stderr, "\t line_number_from_bci: best_bci: %ld, best_line: %ld\n", + best_bci, vf->line); + } + } + } + } + if (debug > 2) + fprintf(stderr, "\t line_number_from_bci: END: line: %ld \n\n", vf->line); + return PS_OK; + + fail: + if (debug) + fprintf(stderr, "\t line_number_from_bci: FAIL\n"); + return err; +} + +static int +get_real_pc(Nmethod_t *N, uint64_t pc_desc, uint64_t *real_pc) +{ + int32_t pc_offset; + int32_t err; + + err = ps_pread(N->J->P, pc_desc + OFFSET_PcDesc_pc_offset, &pc_offset, SZ32); + CHECK_FAIL(err); + + *real_pc = N->nm + N->instrs_beg + pc_offset; + if (debug > 2) { + fprintf(stderr, "\t\t get_real_pc: pc_offset: %lx, real_pc: %llx\n", + pc_offset, *real_pc); + } + return PS_OK; + + fail: + return err; +} + +/* Finds a PcDesc with real-pc equal to N->pc */ +static int pc_desc_at(Nmethod_t *N) +{ + uint64_t pc_diff; + int32_t offs; + int32_t err; + + if (debug > 2) + fprintf(stderr, "\t pc_desc_at: BEGIN\n"); + + N->vf_cnt = 0; + N->pc_desc = 0; + + for (offs = N->scopes_pcs_beg; offs < N->scopes_pcs_end; offs += SIZE_PcDesc) { + uint64_t pd; + uint64_t best_pc_diff = 16; /* some approximation */ + uint64_t real_pc = 0; + + pd = N->nm + offs; + err = get_real_pc(N, pd, &real_pc); + CHECK_FAIL(err); + + pc_diff = real_pc - N->pc; + + /* In general, this fragment should work */ + if (pc_diff == 0) { + N->pc_desc = pd; + if (debug) { + fprintf(stderr, "\t pc_desc_at: END: pc_desc: FOUND: %#lx \n\n", pd); + } + return PS_OK; + } + /* This fragment is to be able to find out an appropriate + * pc_desc entry even if pc_desc info is inaccurate. + */ + if (best_pc_diff > pc_diff && pc_diff > 0) { + best_pc_diff = pc_diff; + N->pc_desc = pd; + } + } + if (debug) { + fprintf(stderr, "\t pc_desc_at: END: pc_desc NOT FOUND"); + if (pc_diff < 20) + fprintf(stderr, ", best pc_diff: %d\n\n", pc_diff); + else + fprintf(stderr, "\n\n"); + } + return PS_OK; + + fail: + return err; +} + +static int +scope_desc_at(Nmethod_t *N, int32_t decode_offset, Vframe_t *vf) +{ + uint64_t buffer; + int32_t err; + + if (debug > 2) { + fprintf(stderr, "\t\t scope_desc_at: BEGIN \n"); + } + + buffer = N->nm + N->scopes_data_beg + decode_offset; + + err = raw_read_int(N->J, &buffer, &vf->sender_decode_offset); + CHECK_FAIL(err); + + err = raw_read_int(N->J, &buffer, &vf->methodIdx); + CHECK_FAIL(err); + + err = raw_read_int(N->J, &buffer, &vf->bci); + CHECK_FAIL(err); + + if (debug > 2) { + fprintf(stderr, "\t\t scope_desc_at: sender_decode_offset: %#x\n", + vf->sender_decode_offset); + fprintf(stderr, "\t\t scope_desc_at: methodIdx: %d\n", vf->methodIdx); + fprintf(stderr, "\t\t scope_desc_at: bci: %d\n", vf->bci); + + fprintf(stderr, "\t\t scope_desc_at: END \n\n"); + } + return PS_OK; + + fail: + return err; +} + +static int +scopeDesc_chain(Nmethod_t *N) +{ + int32_t decode_offset = 0; + int32_t err; + + if (debug > 2) + fprintf(stderr, "\t scopeDesc_chain: BEGIN\n"); + + err = ps_pread(N->J->P, N->pc_desc + OFFSET_PcDesc_scope_decode_offset, + &decode_offset, SZ32); + CHECK_FAIL(err); + + while (decode_offset > 0) { + if (debug > 2) + fprintf(stderr, "\t scopeDesc_chain: decode_offset: %#x\n", decode_offset); + + Vframe_t *vf = &N->vframes[N->vf_cnt]; + + err = scope_desc_at(N, decode_offset, vf); + CHECK_FAIL(err); + + if (vf->methodIdx > N->oops_len) { + fprintf(stderr, "\t scopeDesc_chain: (methodIdx > oops_len) !\n"); + return -1; + } + err = read_pointer(N->J, N->nm + N->oops_beg + (vf->methodIdx-1)*POINTER_SIZE, + &vf->methodOop); + CHECK_FAIL(err); + + if (vf->methodOop) { + N->vf_cnt++; + err = line_number_from_bci(N->J, vf); + CHECK_FAIL(err); + if (debug > 2) { + fprintf(stderr, "\t scopeDesc_chain: methodOop: %#8llx, line: %ld\n", + vf->methodOop, vf->line); + } + } + decode_offset = vf->sender_decode_offset; + } + if (debug > 2) + fprintf(stderr, "\t scopeDesc_chain: END \n\n"); + return PS_OK; + + fail: + if (debug) + fprintf(stderr, "\t scopeDesc_chain: FAIL \n\n"); + return err; +} + + +static int +name_for_nmethod(jvm_agent_t* J, + uint64_t nm, + uint64_t pc, + uint64_t methodOop, + char *result, + size_t size, + Jframe_t *jframe +) { + Nmethod_t *N; + Vframe_t *vf; + int32_t err; + int deoptimized = 0; + + if (debug) { + fprintf(stderr, "name_for_nmethod: BEGIN: nmethod: %#llx, pc: %#llx\n", nm, pc); + } + if (J->N == NULL) { + J->N = (Nmethod_t *) malloc(sizeof(Nmethod_t)); + } + memset(J->N, 0, sizeof(Nmethod_t)); /* Initial stat: all values are zeros */ + N = J->N; + N->J = J; + N->nm = nm; + N->pc = pc; + N->jframe = jframe; + + err = nmethod_info(N); + CHECK_FAIL(err); + if (debug) { + fprintf(stderr, "name_for_nmethod: pc: %#llx, deopt_pc: %#llx\n", + pc, N->nm + N->deopt_beg); + } + + /* check for a deoptimized frame */ + if ( pc == N->nm + N->deopt_beg) { + uint64_t base; + if (debug) { + fprintf(stderr, "name_for_nmethod: found deoptimized frame\n"); + } + if (J->prev_fr.sender_sp != 0) { + base = J->prev_fr.sender_sp + N->orig_pc_offset; + } else { + base = J->curr_fr.sp + N->orig_pc_offset; + } + err = read_pointer(J, base, &N->pc); + CHECK_FAIL(err); + if (debug) { + fprintf(stderr, "name_for_nmethod: found deoptimized frame converting pc from %#8llx to %#8llx\n", + pc, N->pc); + } + deoptimized = 1; + } + + err = pc_desc_at(N); + CHECK_FAIL(err); + + if (N->pc_desc > 0) { + jframe->locinf = 1; + err = scopeDesc_chain(N); + CHECK_FAIL(err); + } + result[0] = COMP_METHOD_SIGN; + vf = &N->vframes[0]; + if (N->vf_cnt > 0) { + jframe->vf_cnt = N->vf_cnt; + jframe->bci = vf->bci; + jframe->line = vf->line; + err = name_for_methodOop(J, N->vframes[0].methodOop, result+1, size-1); + CHECK_FAIL(err); + } else { + err = name_for_methodOop(J, methodOop, result+1, size-1); + CHECK_FAIL(err); + } + if (deoptimized) { + strncat(result + 1, " [deoptimized frame]; ", size-1); + } else { + strncat(result + 1, " [compiled] ", size-1); + } + if (debug) + fprintf(stderr, "name_for_nmethod: END: method name: %s, vf_cnt: %d\n\n", + result, N->vf_cnt); + return PS_OK; + + fail: + if (debug) + fprintf(stderr, "name_for_nmethod: FAIL \n\n"); + return err; +} + +int is_bci(intptr_t bcx) { + switch (DATA_MODEL) { + case PR_MODEL_LP64: + return ((uintptr_t) bcx) <= ((uintptr_t) MAX_METHOD_CODE_SIZE) ; + case PR_MODEL_ILP32: + default: + return 0 <= bcx && bcx <= MAX_METHOD_CODE_SIZE; + } +} + +static int +name_for_imethod(jvm_agent_t* J, + uint64_t bcx, + uint64_t methodOop, + char *result, + size_t size, + Jframe_t *jframe +) { + uint64_t bci; + uint64_t constMethod; + Vframe_t vframe = {0}; + Vframe_t *vf = &vframe; + int32_t err; + + err = read_pointer(J, methodOop + OFFSET_methodOopDesc_constMethod, &constMethod); + CHECK_FAIL(err); + + bci = is_bci(bcx) ? bcx : bcx - (constMethod + (uint64_t) SIZE_constMethodOopDesc); + + if (debug) + fprintf(stderr, "\t name_for_imethod: BEGIN: methodOop: %#llx\n", methodOop); + + err = name_for_methodOop(J, methodOop, result, size); + CHECK_FAIL(err); + if (debug) + fprintf(stderr, "\t name_for_imethod: method name: %s\n", result); + + if (bci > 0) { + vf->methodOop = methodOop; + vf->bci = bci; + err = line_number_from_bci(J, vf); + CHECK_FAIL(err); + } + jframe->bci = vf->bci; + jframe->line = vf->line; + jframe->locinf = 1; + + if (debug) { + fprintf(stderr, "\t name_for_imethod: END: bci: %d, line: %d\n\n", + vf->bci, vf->line); + } + return PS_OK; + + fail: + if (debug) + fprintf(stderr, "\t name_for_imethod: FAIL\n"); + return err; +} + +static int +name_for_codecache(jvm_agent_t* J, uint64_t fp, uint64_t pc, char * result, + size_t size, Jframe_t *jframe, int* is_interpreted) +{ + uint64_t start; + uint64_t vtbl; + int32_t err; + *is_interpreted = 0; + + result[0] = '\0'; + + err = find_start(J, pc, &start); + CHECK_FAIL(err); + + err = read_pointer(J, start, &vtbl); + CHECK_FAIL(err); + + if (vtbl == J->nmethod_vtbl) { + uint64_t methodOop; + + err = read_pointer(J, start + OFFSET_nmethod_method, &methodOop); + CHECK_FAIL(err); + + if (debug) { + fprintf(stderr, "name_for_codecache: start: %#8llx, pc: %#8llx, methodOop: %#8llx \n", + start, pc, methodOop); + } + err = name_for_nmethod(J, start, pc, methodOop, result, size, jframe); + CHECK_FAIL(err); + } else if (vtbl == J->BufferBlob_vtbl) { + const char * name; + + err = read_string_pointer(J, start + OFFSET_CodeBlob_name, &name); + + /* + * Temporary usage of string "Interpreter". + * We need some other way to distinguish "StubRoutines" + * and regular interpreted frames. + */ + if (err == PS_OK && strncmp(name, "Interpreter", 11) == 0) { + *is_interpreted = 1; + if (is_methodOop(J, J->methodOopPtr)) { + return name_for_imethod(J, J->bcx, J->methodOopPtr, result, size, jframe); + } + } + + if (err == PS_OK) { + strncpy(result, name, size); + free((void*)name); + } else { + strncpy(result, "", size); + } + /* return PS_OK; */ + } else { + const char * name; + + err = read_string_pointer(J, start + OFFSET_CodeBlob_name, &name); + if (err == PS_OK) { + strncpy(result, name, size); + free((void*)name); + } else { + strncpy(result, "", size); + WARN1("unknown CodeBlob: vtbl = 0x%x", vtbl); + } + } + result[size-1] = '\0'; + +#ifdef X86_COMPILER2 + if (vtbl != J->RuntimeStub_vtbl) { + uint64_t trial_pc; + int frame_size; + err = ps_pread(J->P, start + OFFSET_CodeBlob_frame_size, + &frame_size, SZ32); + CHECK_FAIL(err); + + // frame_size is in words, we want bytes. + frame_size *= POINTER_SIZE; /* word => byte conversion */ + + /* + Because c2 doesn't use FP as a framepointer the value of sp/fp we receive + in the initial entry to a set of stack frames containing server frames + will pretty much be nonsense. We can detect that nonsense by looking to + see if the PC we received is correct if we look at the expected storage + location in relation to the FP (ie. POINTER_SIZE(FP) ) + */ + + err = read_pointer(J, fp + POINTER_SIZE , &trial_pc); + if ( (err != PS_OK || trial_pc != pc) && frame_size > 0 ) { + // Either we couldn't even read at the "fp" or the pc didn't match + // both are sure clues that the fp is bogus. We no search the stack + // for a reasonable number of words trying to find the bogus fp + // and the current pc in adjacent words. The we will be able to + // deduce an approximation of the frame pointer and actually get + // the correct stack pointer. Which we can then unwind for the + // next frame. + int i; + uint64_t check; + uint64_t base = J->curr_fr.sp; + uint64_t prev_fp = 0; + for ( i = 0; i < frame_size * 5 ; i++, base += POINTER_SIZE ) { + err = read_pointer(J, base , &check); + CHECK_FAIL(err); + if (check == fp) { + base += POINTER_SIZE; + err = read_pointer(J, base , &check); + CHECK_FAIL(err); + if (check == pc) { + if (debug) { + fprintf(stderr, "name_for_codecache: found matching fp/pc combo at 0x%llx\n", base - POINTER_SIZE); + } + prev_fp = base - 2 * POINTER_SIZE; + break; + } + } + } + if ( prev_fp != 0 ) { + // real_sp is the sp we should have received for this frame + uint64_t real_sp = prev_fp + 2 * POINTER_SIZE; + // +POINTER_SIZE because callee owns the return address so caller's sp is +1 word + jframe->new_sp = real_sp + frame_size + POINTER_SIZE; + err = read_pointer(J, jframe->new_sp - POINTER_SIZE , &jframe->new_pc); + CHECK_FAIL(err); + err = read_pointer(J, jframe->new_sp - 2*POINTER_SIZE, &jframe->new_fp); + CHECK_FAIL(err); + return PS_OK; + } + } + + /* A prototype to workaround FP absence */ + /* + * frame_size can be 0 for StubRoutines (1) frame. + * In this case it should work with fp as usual. + */ + if (frame_size > 0) { + jframe->new_fp = J->prev_fr.fp + frame_size; + jframe->new_sp = jframe->new_fp + 2 * POINTER_SIZE; + } else { + memset(&J->curr_fr, 0, sizeof(Frame_t)); + err = read_pointer(J, fp, &jframe->new_fp); + CHECK_FAIL(err); + + err = read_pointer(J, jframe->new_fp + POINTER_SIZE, &jframe->new_pc); + CHECK_FAIL(err); + } + if (debug) { + fprintf(stderr, "name_for_codecache: %s, frame_size=%#lx\n", + result, frame_size); + fprintf(stderr, "name_for_codecache: prev_fr.fp=%#lx, fp=%#lx\n", + J->prev_fr.fp, jframe->new_fp); + } + } +#endif /* X86_COMPILER2 */ + + return PS_OK; + + fail: + return err; +} + +int Jget_vframe(jvm_agent_t* J, int vframe_no, + char *name, size_t size, Jframe_t *jframe) +{ + Nmethod_t *N = J->N; + Vframe_t *vf; + int32_t err; + + if (vframe_no >= N->vf_cnt) { + (void) sprintf(name, "Wrong inlinedMethod%1d()", vframe_no); + return -1; + } + vf = N->vframes + vframe_no; + name[0] = COMP_METHOD_SIGN; + err = name_for_methodOop(J, vf->methodOop, name + 1, size); + CHECK_FAIL(err); + + jframe->bci = vf->bci; + jframe->line = vf->line; + if (debug) { + fprintf(stderr, "\t Jget_vframe: method name: %s, line: %ld\n", + name, vf->line); + } + return PS_OK; + + fail: + if (debug) { + fprintf(stderr, "\t Jget_vframe: FAIL\n"); + } + return err; +} + +#define MAX_SYM_SIZE 256 + +int Jlookup_by_regs(jvm_agent_t* J, const prgregset_t regs, char *name, + size_t size, Jframe_t *jframe) { + uintptr_t fp; + uintptr_t pc; + /* arguments given to read_pointer need to be worst case sized */ + uint64_t methodOopPtr = 0; + uint64_t sender_sp; + uint64_t bcx = 0; + int is_interpreted = 0; + int result = PS_OK; + int err = PS_OK; + + if (J == NULL) { + return -1; + } + + jframe->vf_cnt = 1; + jframe->new_fp = 0; + jframe->new_pc = 0; + jframe->line = 0; + jframe->bci = 0; + jframe->locinf = 0; + + read_volatiles(J); + pc = (uintptr_t) regs[R_PC]; + J->curr_fr.pc = pc; + J->curr_fr.fp = regs[R_FP]; + J->curr_fr.sp = regs[R_SP]; + + if (debug) + fprintf(stderr, "Jlookup_by_regs: BEGINs: fp=%#lx, pc=%#lx\n", regs[R_FP], pc); + +#if defined(sparc) || defined(__sparc) + /* The following workaround is for SPARC. CALL instruction occupates 8 bytes. + * In the pcDesc structure return pc offset is recorded for CALL instructions. + * regs[R_PC] contains a CALL instruction pc offset. + */ + pc += 8; + bcx = (uintptr_t) regs[R_L1]; + methodOopPtr = (uintptr_t) regs[R_L2]; + sender_sp = regs[R_I5]; + if (debug > 2) { + fprintf(stderr, "\nregs[R_I1]=%lx, regs[R_I2]=%lx, regs[R_I5]=%lx, regs[R_L1]=%lx, regs[R_L2]=%lx\n", + regs[R_I1], regs[R_I2], regs[R_I5], regs[R_L1], regs[R_L2]); + } +#elif defined(i386) || defined(__i386) || defined(__amd64) + + fp = (uintptr_t) regs[R_FP]; + if (J->prev_fr.fp == 0) { +#ifdef X86_COMPILER2 + /* A workaround for top java frames */ + J->prev_fr.fp = (uintptr_t)(regs[R_SP] - 2 * POINTER_SIZE); +#else + J->prev_fr.fp = (uintptr_t)(regs[R_SP] - POINTER_SIZE); +#endif /* COMPILER2 */ + } + if (debug > 2) { + printf("Jlookup_by_regs: J->prev_fr.fp = %#lx\n", J->prev_fr.fp); + } + + if (read_pointer(J, fp + OFFSET_interpreter_frame_method, &methodOopPtr) != PS_OK) { + methodOopPtr = 0; + } + if (read_pointer(J, fp + OFFSET_interpreter_frame_sender_sp, &sender_sp) != PS_OK) { + sender_sp = 0; + } + if (read_pointer(J, fp + OFFSET_interpreter_frame_bcx_offset, &bcx) != PS_OK) { + bcx = 0; + } +#endif /* i386 */ + + J->methodOopPtr = methodOopPtr; + J->bcx = bcx; + + /* On x86 with C2 JVM: native frame may have wrong regs[R_FP] + * For example: JVM_SuspendThread frame poins to the top interpreted frame. + * If we call is_methodOop(J, methodOopPtr) before codecache_contains(J, pc) + * then we go over and omit both: nmethod and I2CAdapter frames. + * Note, that regs[R_PC] is always correct if frame defined correctly. + * So it is better to call codecache_contains(J, pc) from the beginning. + */ +#ifndef X86_COMPILER2 + if (is_methodOop(J, J->methodOopPtr)) { + result = name_for_imethod(J, bcx, J->methodOopPtr, name, size, jframe); + /* If the methodOopPtr is a method then this is highly likely to be + an interpreter frame */ + if (result >= 0) { + is_interpreted = 1; + } + } else +#endif /* ! X86_COMPILER2 */ + + if (codecache_contains(J, pc)) { + result = name_for_codecache(J, fp, pc, name, size, jframe, &is_interpreted); + } +#ifdef X86_COMPILER2 + else if (is_methodOop(J, J->methodOopPtr)) { + result = name_for_imethod(J, bcx, J->methodOopPtr, name, size, jframe); + /* If the methodOopPtr is a method then this is highly likely to be + an interpreter frame */ + if (result >= 0) { + is_interpreted = 1; + } + } +#endif /* X86_COMPILER2 */ + else { + if (debug) { + fprintf(stderr, "Jlookup_by_regs: END with -1\n\n"); + } + result = -1; + } + if (!is_interpreted) { + sender_sp = 0; + } + J->curr_fr.sender_sp = sender_sp; + +#ifdef X86_COMPILER2 + if (!J->curr_fr.fp) { + J->curr_fr.fp = (jframe->new_fp) ? jframe->new_fp : (uintptr_t)regs[R_FP]; + } + if (!jframe->new_pc && jframe->new_fp) { + // This seems dubious + read_pointer(J, jframe->new_fp + POINTER_SIZE, &jframe->new_pc); + CHECK_FAIL(err); + if (debug > 2) { + printf("Jlookup_by_regs: (update pc) jframe->new_fp: %#llx, jframe->new_pc: %#llx\n", + jframe->new_fp, jframe->new_pc); + } + } + +#endif /* X86_COMPILER2 */ + J->prev_fr = J->curr_fr; + + if (debug) + fprintf(stderr, "Jlookup_by_regs: END\n\n"); + + return result; + + fail: + return err; +} + +void update_gregs(prgregset_t gregs, Jframe_t jframe) { +#ifdef X86_COMPILER2 + if (debug > 0) { + fprintf(stderr, "update_gregs: before update sp = 0x%llx, fp = 0x%llx, pc = 0x%llx\n", gregs[R_SP], gregs[R_FP], gregs[R_PC]); + } + /* + * A workaround for java C2 frames with unconventional FP. + * may have to modify regset with new values for FP/PC/SP when needed. + */ + if (jframe.new_sp) { + *((uintptr_t *) &gregs[R_SP]) = (uintptr_t) jframe.new_sp; + } else { + // *((uintptr_t *) &gregs[R_SP]) = (uintptr_t) gregs[R_FP] + 2 * POINTER_SIZE; + } + + if (jframe.new_fp) { + *((uintptr_t *) &gregs[R_FP]) = (uintptr_t) jframe.new_fp; + } + if (jframe.new_pc) { + *((uintptr_t *) &gregs[R_PC]) = (uintptr_t) jframe.new_pc; + } + if (debug > 0) { + fprintf(stderr, "update_gregs: after update sp = 0x%llx, fp = 0x%llx, pc = 0x%llx\n", gregs[R_SP], gregs[R_FP], gregs[R_PC]); + } +#endif /* X86_COMPILER2 */ +} + +/* + * Iterates over java frames at current location given by 'gregs'. + * + * Returns -1 if no java frames are present or if an error is encountered. + * Returns the result of calling 'func' if the return value is non-zero. + * Returns 0 otherwise. + */ +int Jframe_iter(jvm_agent_t *J, prgregset_t gregs, java_stack_f *func, void* cld) { + char buf[MAX_SYM_SIZE + 1]; + Jframe_t jframe; + int i = 0, res; +#ifdef X86_COMPILER2 + if (debug > 0) { + fprintf(stderr, "Jframe_iter: Entry sp = 0x%llx, fp = 0x%llx, pc = 0x%llx\n", gregs[R_SP], gregs[R_FP], gregs[R_PC]); + } +#endif /* X86_COMPILER2 */ + + memset(&jframe, 0, sizeof(Jframe_t)); + memset(buf, 0, sizeof(buf)); + res = Jlookup_by_regs(J, gregs, buf, sizeof(buf), &jframe); + if (res != PS_OK) + return (-1); + + + res = func(cld, gregs, buf, (jframe.locinf)? jframe.bci : -1, + jframe.line, NULL); + if (res != 0) { + update_gregs(gregs, jframe); + return (res); + } + for (i = 1; i < jframe.vf_cnt; i++) { + Jget_vframe(J, i, buf, sizeof(buf), &jframe); + res = func(cld, gregs, buf, (jframe.locinf)? jframe.bci : -1, + jframe.line, NULL); + if (res != 0) { + update_gregs(gregs, jframe); + return (res); + } + } + update_gregs(gregs, jframe); + return (0); +} diff --git a/hotspot/src/os/solaris/dtrace/libjvm_db.h b/hotspot/src/os/solaris/dtrace/libjvm_db.h new file mode 100644 index 00000000000..b9958801f77 --- /dev/null +++ b/hotspot/src/os/solaris/dtrace/libjvm_db.h @@ -0,0 +1,63 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct jvm_agent jvm_agent_t; + +#define JVM_DB_VERSION 1 + +jvm_agent_t *Jagent_create(struct ps_prochandle *P, int vers); + +/* + * Called from Jframe_iter() for each java frame. If it returns 0, then + * Jframe_iter() proceeds to the next frame. Otherwise, the return value is + * immediately returned to the caller of Jframe_iter(). + * + * Parameters: + * 'cld' is client supplied data (to maintain iterator state, if any). + * 'name' is java method name. + * 'bci' is byte code index. it will be -1 if not available. + * 'line' is java source line number. it will be 0 if not available. + * 'handle' is an abstract client handle, reserved for future expansions + */ + +typedef int java_stack_f(void *cld, const prgregset_t regs, const char* name, int bci, int line, void *handle); + +/* + * Iterates over the java frames at the current location. Returns -1 if no java + * frames were found, or if there was some unrecoverable error. Otherwise, + * returns the last value returned from 'func'. + */ +int Jframe_iter(jvm_agent_t *agent, prgregset_t gregs, java_stack_f *func, void* cld); + +void Jagent_destroy(jvm_agent_t *J); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ diff --git a/hotspot/src/os/solaris/launcher/java.c b/hotspot/src/os/solaris/launcher/java.c new file mode 100644 index 00000000000..cb67cc0f088 --- /dev/null +++ b/hotspot/src/os/solaris/launcher/java.c @@ -0,0 +1,1841 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Gamma (Hotspot internal engineering test) launcher based on 1.6.0-b28 JDK, + * search "GAMMA" for gamma specific changes. + * + * GAMMA: gamma launcher is much simpler than regular java launcher in that + * JVM is either statically linked in or it is installed in the + * same directory where the launcher exists, so we don't have to + * worry about choosing the right JVM based on command line flag, jar + * file and/or ergonomics. Intead of removing unused logic from source + * they are commented out with #ifndef GAMMA, hopefully it'll be easier + * to maintain this file in sync with regular JDK launcher. + */ + +/* + * Shared source for 'java' command line tool. + * + * If JAVA_ARGS is defined, then acts as a launcher for applications. For + * instance, the JDK command line tools such as javac and javadoc (see + * makefiles for more details) are built with this program. Any arguments + * prefixed with '-J' will be passed directly to the 'java' command. + */ + +#ifdef GAMMA +# ifdef JAVA_ARGS +# error Do NOT define JAVA_ARGS when building gamma launcher +# endif +# if !defined(LINK_INTO_AOUT) && !defined(LINK_INTO_LIBJVM) +# error Either LINK_INTO_AOUT or LINK_INTO_LIBJVM must be defined +# endif +#endif + +/* + * One job of the launcher is to remove command line options which the + * vm does not understand and will not process. These options include + * options which select which style of vm is run (e.g. -client and + * -server) as well as options which select the data model to use. + * Additionally, for tools which invoke an underlying vm "-J-foo" + * options are turned into "-foo" options to the vm. This option + * filtering is handled in a number of places in the launcher, some of + * it in machine-dependent code. In this file, the function + * CheckJVMType removes vm style options and TranslateDashJArgs + * removes "-J" prefixes. On unix platforms, the + * CreateExecutionEnvironment function from the unix java_md.c file + * processes and removes -d options. However, in case + * CreateExecutionEnvironment does not need to exec because + * LD_LIBRARY_PATH is set acceptably and the data model does not need + * to be changed, ParseArguments will screen out the redundant -d + * options and prevent them from being passed to the vm; this is done + * by using the machine-dependent call + * RemovableMachineDependentOption. + */ + +#include +#include +#include + +#include +#include "java.h" + +#ifndef GAMMA +#include "manifest_info.h" +#include "version_comp.h" +#endif + +#ifndef FULL_VERSION +#define FULL_VERSION JDK_MAJOR_VERSION "." JDK_MINOR_VERSION +#endif + +/* + * The following environment variable is used to influence the behavior + * of the jre exec'd through the SelectVersion routine. The command line + * options which specify the version are not passed to the exec'd version, + * because that jre may be an older version which wouldn't recognize them. + * This environment variable is known to this (and later) version and serves + * to suppress the version selection code. This is not only for efficiency, + * but also for correctness, since any command line options have been + * removed which would cause any value found in the manifest to be used. + * This would be incorrect because the command line options are defined + * to take precedence. + * + * The value associated with this environment variable is the MainClass + * name from within the executable jar file (if any). This is strictly a + * performance enhancement to avoid re-reading the jar file manifest. + * + * A NOTE TO DEVELOPERS: For performance reasons it is important that + * the program image remain relatively small until after SelectVersion + * CreateExecutionEnvironment have finished their possibly recursive + * processing. Watch everything, but resist all temptations to use Java + * interfaces. + */ +#define ENV_ENTRY "_JAVA_VERSION_SET" + +static jboolean printVersion = JNI_FALSE; /* print and exit */ +static jboolean showVersion = JNI_FALSE; /* print but continue */ +static char *progname; +jboolean _launcher_debug = JNI_FALSE; + +/* + * List of VM options to be specified when the VM is created. + */ +static JavaVMOption *options; +static int numOptions, maxOptions; + +/* + * Prototypes for functions internal to launcher. + */ +static void AddOption(char *str, void *info); +static void SetClassPath(char *s); +static void SelectVersion(int argc, char **argv, char **main_class); +static jboolean ParseArguments(int *pargc, char ***pargv, char **pjarfile, + char **pclassname, int *pret); +static jboolean InitializeJVM(JavaVM **pvm, JNIEnv **penv, + InvocationFunctions *ifn); +static jstring NewPlatformString(JNIEnv *env, char *s); +static jobjectArray NewPlatformStringArray(JNIEnv *env, char **strv, int strc); +static jclass LoadClass(JNIEnv *env, char *name); +static jstring GetMainClassName(JNIEnv *env, char *jarname); +static void SetJavaCommandLineProp(char* classname, char* jarfile, int argc, char** argv); +#ifdef GAMMA +static void SetJavaLauncherProp(void); +#endif + +#ifdef JAVA_ARGS +static void TranslateDashJArgs(int *pargc, char ***pargv); +static jboolean AddApplicationOptions(void); +#endif + +static void PrintJavaVersion(JNIEnv *env); +static void PrintUsage(void); +static jint PrintXUsage(void); + +static void SetPaths(int argc, char **argv); + +/* Maximum supported entries from jvm.cfg. */ +#define INIT_MAX_KNOWN_VMS 10 +/* Values for vmdesc.flag */ +#define VM_UNKNOWN -1 +#define VM_KNOWN 0 +#define VM_ALIASED_TO 1 +#define VM_WARN 2 +#define VM_ERROR 3 +#define VM_IF_SERVER_CLASS 4 +#define VM_IGNORE 5 +struct vmdesc { + char *name; + int flag; + char *alias; + char *server_class; +}; +static struct vmdesc *knownVMs = NULL; +static int knownVMsCount = 0; +static int knownVMsLimit = 0; + +static void GrowKnownVMs(); +static int KnownVMIndex(const char* name); +static void FreeKnownVMs(); + +jboolean ServerClassMachine(); + +/* flag which if set suppresses error messages from the launcher */ +static int noExitErrorMessage = 0; + +/* + * Entry point. + */ +int +main(int argc, char ** argv) +{ + JavaVM *vm = 0; + JNIEnv *env = 0; + char *jarfile = 0; + char *classname = 0; + char *s = 0; + char *main_class = NULL; + jstring mainClassName; + jclass mainClass; + jmethodID mainID; + jobjectArray mainArgs; + int ret; + InvocationFunctions ifn; + jlong start, end; + char jrepath[MAXPATHLEN], jvmpath[MAXPATHLEN]; + char ** original_argv = argv; + + /* + * Error message to print or display; by default the message will + * only be displayed in a window. + */ + char * message = "Fatal exception occurred. Program will exit."; + jboolean messageDest = JNI_FALSE; + + if (getenv("_JAVA_LAUNCHER_DEBUG") != 0) { + _launcher_debug = JNI_TRUE; + printf("----_JAVA_LAUNCHER_DEBUG----\n"); + } + +#ifndef GAMMA + /* + * Make sure the specified version of the JRE is running. + * + * There are three things to note about the SelectVersion() routine: + * 1) If the version running isn't correct, this routine doesn't + * return (either the correct version has been exec'd or an error + * was issued). + * 2) Argc and Argv in this scope are *not* altered by this routine. + * It is the responsibility of subsequent code to ignore the + * arguments handled by this routine. + * 3) As a side-effect, the variable "main_class" is guaranteed to + * be set (if it should ever be set). This isn't exactly the + * poster child for structured programming, but it is a small + * price to pay for not processing a jar file operand twice. + * (Note: This side effect has been disabled. See comment on + * bugid 5030265 below.) + */ + SelectVersion(argc, argv, &main_class); +#endif /* ifndef GAMMA */ + + /* copy original argv */ + { + int i; + original_argv = (char**)MemAlloc(sizeof(char*)*(argc+1)); + for(i = 0; i < argc+1; i++) + original_argv[i] = argv[i]; + } + + CreateExecutionEnvironment(&argc, &argv, + jrepath, sizeof(jrepath), + jvmpath, sizeof(jvmpath), + original_argv); + ifn.CreateJavaVM = 0; + ifn.GetDefaultJavaVMInitArgs = 0; + + if (_launcher_debug) + start = CounterGet(); + if (!LoadJavaVM(jvmpath, &ifn)) { + exit(6); + } + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to LoadJavaVM\n", + (long)(jint)Counter2Micros(end-start)); + } + +#ifdef JAVA_ARGS /* javac, jar and friends. */ + progname = "java"; +#else /* java, oldjava, javaw and friends */ +#ifdef PROGNAME + progname = PROGNAME; +#else + progname = *argv; + if ((s = strrchr(progname, FILE_SEPARATOR)) != 0) { + progname = s + 1; + } +#endif /* PROGNAME */ +#endif /* JAVA_ARGS */ + ++argv; + --argc; + +#ifdef JAVA_ARGS + /* Preprocess wrapper arguments */ + TranslateDashJArgs(&argc, &argv); + if (!AddApplicationOptions()) { + exit(1); + } +#endif + + /* Set default CLASSPATH */ + if ((s = getenv("CLASSPATH")) == 0) { + s = "."; + } +#ifndef JAVA_ARGS + SetClassPath(s); +#endif + + /* + * Parse command line options; if the return value of + * ParseArguments is false, the program should exit. + */ + if (!ParseArguments(&argc, &argv, &jarfile, &classname, &ret)) { + exit(ret); + } + + /* Override class path if -jar flag was specified */ + if (jarfile != 0) { + SetClassPath(jarfile); + } + + /* set the -Dsun.java.command pseudo property */ + SetJavaCommandLineProp(classname, jarfile, argc, argv); + +#ifdef GAMMA + /* Set the -Dsun.java.launcher pseudo property */ + SetJavaLauncherProp(); +#endif + + /* + * Done with all command line processing and potential re-execs so + * clean up the environment. + */ + (void)UnsetEnv(ENV_ENTRY); + + /* Initialize the virtual machine */ + + if (_launcher_debug) + start = CounterGet(); + if (!InitializeJVM(&vm, &env, &ifn)) { + ReportErrorMessage("Could not create the Java virtual machine.", + JNI_TRUE); + exit(1); + } + + if (printVersion || showVersion) { + PrintJavaVersion(env); + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + goto leave; + } + if (printVersion) { + ret = 0; + message = NULL; + goto leave; + } + if (showVersion) { + fprintf(stderr, "\n"); + } + } + + /* If the user specified neither a class name nor a JAR file */ + if (jarfile == 0 && classname == 0) { + PrintUsage(); + message = NULL; + goto leave; + } + +#ifndef GAMMA + FreeKnownVMs(); /* after last possible PrintUsage() */ +#endif + + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to InitializeJVM\n", + (long)(jint)Counter2Micros(end-start)); + } + + /* At this stage, argc/argv have the applications' arguments */ + if (_launcher_debug) { + int i = 0; + printf("Main-Class is '%s'\n", classname ? classname : ""); + printf("Apps' argc is %d\n", argc); + for (; i < argc; i++) { + printf(" argv[%2d] = '%s'\n", i, argv[i]); + } + } + + ret = 1; + + /* + * Get the application's main class. + * + * See bugid 5030265. The Main-Class name has already been parsed + * from the manifest, but not parsed properly for UTF-8 support. + * Hence the code here ignores the value previously extracted and + * uses the pre-existing code to reextract the value. This is + * possibly an end of release cycle expedient. However, it has + * also been discovered that passing some character sets through + * the environment has "strange" behavior on some variants of + * Windows. Hence, maybe the manifest parsing code local to the + * launcher should never be enhanced. + * + * Hence, future work should either: + * 1) Correct the local parsing code and verify that the + * Main-Class attribute gets properly passed through + * all environments, + * 2) Remove the vestages of maintaining main_class through + * the environment (and remove these comments). + */ + if (jarfile != 0) { + mainClassName = GetMainClassName(env, jarfile); + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + goto leave; + } + if (mainClassName == NULL) { + const char * format = "Failed to load Main-Class manifest " + "attribute from\n%s"; + message = (char*)MemAlloc((strlen(format) + strlen(jarfile)) * + sizeof(char)); + sprintf(message, format, jarfile); + messageDest = JNI_TRUE; + goto leave; + } + classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0); + if (classname == NULL) { + ReportExceptionDescription(env); + goto leave; + } + mainClass = LoadClass(env, classname); + if(mainClass == NULL) { /* exception occured */ + ReportExceptionDescription(env); + message = "Could not find the main class. Program will exit."; + goto leave; + } + (*env)->ReleaseStringUTFChars(env, mainClassName, classname); + } else { + mainClassName = NewPlatformString(env, classname); + if (mainClassName == NULL) { + const char * format = "Failed to load Main Class: %s"; + message = (char *)MemAlloc((strlen(format) + strlen(classname)) * + sizeof(char) ); + sprintf(message, format, classname); + messageDest = JNI_TRUE; + goto leave; + } + classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0); + if (classname == NULL) { + ReportExceptionDescription(env); + goto leave; + } + mainClass = LoadClass(env, classname); + if(mainClass == NULL) { /* exception occured */ + ReportExceptionDescription(env); + message = "Could not find the main class. Program will exit."; + goto leave; + } + (*env)->ReleaseStringUTFChars(env, mainClassName, classname); + } + + /* Get the application's main method */ + mainID = (*env)->GetStaticMethodID(env, mainClass, "main", + "([Ljava/lang/String;)V"); + if (mainID == NULL) { + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + } else { + message = "No main method found in specified class."; + messageDest = JNI_TRUE; + } + goto leave; + } + + { /* Make sure the main method is public */ + jint mods; + jmethodID mid; + jobject obj = (*env)->ToReflectedMethod(env, mainClass, + mainID, JNI_TRUE); + + if( obj == NULL) { /* exception occurred */ + ReportExceptionDescription(env); + goto leave; + } + + mid = + (*env)->GetMethodID(env, + (*env)->GetObjectClass(env, obj), + "getModifiers", "()I"); + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + goto leave; + } + + mods = (*env)->CallIntMethod(env, obj, mid); + if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) ... */ + message = "Main method not public."; + messageDest = JNI_TRUE; + goto leave; + } + } + + /* Build argument array */ + mainArgs = NewPlatformStringArray(env, argv, argc); + if (mainArgs == NULL) { + ReportExceptionDescription(env); + goto leave; + } + + /* Invoke main method. */ + (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs); + + /* + * The launcher's exit code (in the absence of calls to + * System.exit) will be non-zero if main threw an exception. + */ + ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1; + + /* + * Detach the main thread so that it appears to have ended when + * the application's main method exits. This will invoke the + * uncaught exception handler machinery if main threw an + * exception. An uncaught exception handler cannot change the + * launcher's return code except by calling System.exit. + */ + if ((*vm)->DetachCurrentThread(vm) != 0) { + message = "Could not detach main thread."; + messageDest = JNI_TRUE; + ret = 1; + goto leave; + } + + message = NULL; + + leave: + /* + * Wait for all non-daemon threads to end, then destroy the VM. + * This will actually create a trivial new Java waiter thread + * named "DestroyJavaVM", but this will be seen as a different + * thread from the one that executed main, even though they are + * the same C thread. This allows mainThread.join() and + * mainThread.isAlive() to work as expected. + */ + (*vm)->DestroyJavaVM(vm); + + if(message != NULL && !noExitErrorMessage) + ReportErrorMessage(message, messageDest); + return ret; +} + + +#ifndef GAMMA +/* + * Checks the command line options to find which JVM type was + * specified. If no command line option was given for the JVM type, + * the default type is used. The environment variable + * JDK_ALTERNATE_VM and the command line option -XXaltjvm= are also + * checked as ways of specifying which JVM type to invoke. + */ +char * +CheckJvmType(int *pargc, char ***argv, jboolean speculative) { + int i, argi; + int argc; + char **newArgv; + int newArgvIdx = 0; + int isVMType; + int jvmidx = -1; + char *jvmtype = getenv("JDK_ALTERNATE_VM"); + + argc = *pargc; + + /* To make things simpler we always copy the argv array */ + newArgv = MemAlloc((argc + 1) * sizeof(char *)); + + /* The program name is always present */ + newArgv[newArgvIdx++] = (*argv)[0]; + + for (argi = 1; argi < argc; argi++) { + char *arg = (*argv)[argi]; + isVMType = 0; + +#ifdef JAVA_ARGS + if (arg[0] != '-') { + newArgv[newArgvIdx++] = arg; + continue; + } +#else + if (strcmp(arg, "-classpath") == 0 || + strcmp(arg, "-cp") == 0) { + newArgv[newArgvIdx++] = arg; + argi++; + if (argi < argc) { + newArgv[newArgvIdx++] = (*argv)[argi]; + } + continue; + } + if (arg[0] != '-') break; +#endif + + /* Did the user pass an explicit VM type? */ + i = KnownVMIndex(arg); + if (i >= 0) { + jvmtype = knownVMs[jvmidx = i].name + 1; /* skip the - */ + isVMType = 1; + *pargc = *pargc - 1; + } + + /* Did the user specify an "alternate" VM? */ + else if (strncmp(arg, "-XXaltjvm=", 10) == 0 || strncmp(arg, "-J-XXaltjvm=", 12) == 0) { + isVMType = 1; + jvmtype = arg+((arg[1]=='X')? 10 : 12); + jvmidx = -1; + } + + if (!isVMType) { + newArgv[newArgvIdx++] = arg; + } + } + + /* + * Finish copying the arguments if we aborted the above loop. + * NOTE that if we aborted via "break" then we did NOT copy the + * last argument above, and in addition argi will be less than + * argc. + */ + while (argi < argc) { + newArgv[newArgvIdx++] = (*argv)[argi]; + argi++; + } + + /* argv is null-terminated */ + newArgv[newArgvIdx] = 0; + + /* Copy back argv */ + *argv = newArgv; + *pargc = newArgvIdx; + + /* use the default VM type if not specified (no alias processing) */ + if (jvmtype == NULL) { + char* result = knownVMs[0].name+1; + /* Use a different VM type if we are on a server class machine? */ + if ((knownVMs[0].flag == VM_IF_SERVER_CLASS) && + (ServerClassMachine() == JNI_TRUE)) { + result = knownVMs[0].server_class+1; + } + if (_launcher_debug) { + printf("Default VM: %s\n", result); + } + return result; + } + + /* if using an alternate VM, no alias processing */ + if (jvmidx < 0) + return jvmtype; + + /* Resolve aliases first */ + { + int loopCount = 0; + while (knownVMs[jvmidx].flag == VM_ALIASED_TO) { + int nextIdx = KnownVMIndex(knownVMs[jvmidx].alias); + + if (loopCount > knownVMsCount) { + if (!speculative) { + ReportErrorMessage("Error: Corrupt jvm.cfg file; cycle in alias list.", + JNI_TRUE); + exit(1); + } else { + return "ERROR"; + /* break; */ + } + } + + if (nextIdx < 0) { + if (!speculative) { + ReportErrorMessage2("Error: Unable to resolve VM alias %s", + knownVMs[jvmidx].alias, JNI_TRUE); + exit(1); + } else { + return "ERROR"; + } + } + jvmidx = nextIdx; + jvmtype = knownVMs[jvmidx].name+1; + loopCount++; + } + } + + switch (knownVMs[jvmidx].flag) { + case VM_WARN: + if (!speculative) { + fprintf(stderr, "Warning: %s VM not supported; %s VM will be used\n", + jvmtype, knownVMs[0].name + 1); + } + /* fall through */ + case VM_IGNORE: + jvmtype = knownVMs[jvmidx=0].name + 1; + /* fall through */ + case VM_KNOWN: + break; + case VM_ERROR: + if (!speculative) { + ReportErrorMessage2("Error: %s VM not supported", jvmtype, JNI_TRUE); + exit(1); + } else { + return "ERROR"; + } + } + + return jvmtype; +} +#endif /* ifndef GAMMA */ + +/* + * Adds a new VM option with the given given name and value. + */ +static void +AddOption(char *str, void *info) +{ + /* + * Expand options array if needed to accommodate at least one more + * VM option. + */ + if (numOptions >= maxOptions) { + if (options == 0) { + maxOptions = 4; + options = MemAlloc(maxOptions * sizeof(JavaVMOption)); + } else { + JavaVMOption *tmp; + maxOptions *= 2; + tmp = MemAlloc(maxOptions * sizeof(JavaVMOption)); + memcpy(tmp, options, numOptions * sizeof(JavaVMOption)); + free(options); + options = tmp; + } + } + options[numOptions].optionString = str; + options[numOptions++].extraInfo = info; +} + +static void +SetClassPath(char *s) +{ + char *def = MemAlloc(strlen(s) + 40); + sprintf(def, "-Djava.class.path=%s", s); + AddOption(def, NULL); +} + +#ifndef GAMMA +/* + * The SelectVersion() routine ensures that an appropriate version of + * the JRE is running. The specification for the appropriate version + * is obtained from either the manifest of a jar file (preferred) or + * from command line options. + */ +static void +SelectVersion(int argc, char **argv, char **main_class) +{ + char *arg; + char **new_argv; + char **new_argp; + char *operand; + char *version = NULL; + char *jre = NULL; + int jarflag = 0; + int restrict_search = -1; /* -1 implies not known */ + manifest_info info; + char env_entry[MAXNAMELEN + 24] = ENV_ENTRY "="; + char *env_in; + int res; + + /* + * If the version has already been selected, set *main_class + * with the value passed through the environment (if any) and + * simply return. + */ + if ((env_in = getenv(ENV_ENTRY)) != NULL) { + if (*env_in != '\0') + *main_class = strdup(env_in); + return; + } + + /* + * Scan through the arguments for options relevant to multiple JRE + * support. For reference, the command line syntax is defined as: + * + * SYNOPSIS + * java [options] class [argument...] + * + * java [options] -jar file.jar [argument...] + * + * As the scan is performed, make a copy of the argument list with + * the version specification options (new to 1.5) removed, so that + * a version less than 1.5 can be exec'd. + */ + new_argv = MemAlloc((argc + 1) * sizeof(char*)); + new_argv[0] = argv[0]; + new_argp = &new_argv[1]; + argc--; + argv++; + while ((arg = *argv) != 0 && *arg == '-') { + if (strncmp(arg, "-version:", 9) == 0) { + version = arg + 9; + } else if (strcmp(arg, "-jre-restrict-search") == 0) { + restrict_search = 1; + } else if (strcmp(arg, "-no-jre-restrict-search") == 0) { + restrict_search = 0; + } else { + if (strcmp(arg, "-jar") == 0) + jarflag = 1; + /* deal with "unfortunate" classpath syntax */ + if ((strcmp(arg, "-classpath") == 0 || strcmp(arg, "-cp") == 0) && + (argc >= 2)) { + *new_argp++ = arg; + argc--; + argv++; + arg = *argv; + } + *new_argp++ = arg; + } + argc--; + argv++; + } + if (argc <= 0) { /* No operand? Possibly legit with -[full]version */ + operand = NULL; + } else { + argc--; + *new_argp++ = operand = *argv++; + } + while (argc-- > 0) /* Copy over [argument...] */ + *new_argp++ = *argv++; + *new_argp = NULL; + + /* + * If there is a jar file, read the manifest. If the jarfile can't be + * read, the manifest can't be read from the jar file, or the manifest + * is corrupt, issue the appropriate error messages and exit. + * + * Even if there isn't a jar file, construct a manifest_info structure + * containing the command line information. It's a convenient way to carry + * this data around. + */ + if (jarflag && operand) { + if ((res = parse_manifest(operand, &info)) != 0) { + if (res == -1) + ReportErrorMessage2("Unable to access jarfile %s", + operand, JNI_TRUE); + else + ReportErrorMessage2("Invalid or corrupt jarfile %s", + operand, JNI_TRUE); + exit(1); + } + } else { + info.manifest_version = NULL; + info.main_class = NULL; + info.jre_version = NULL; + info.jre_restrict_search = 0; + } + + /* + * The JRE-Version and JRE-Restrict-Search values (if any) from the + * manifest are overwritten by any specified on the command line. + */ + if (version != NULL) + info.jre_version = version; + if (restrict_search != -1) + info.jre_restrict_search = restrict_search; + + /* + * "Valid" returns (other than unrecoverable errors) follow. Set + * main_class as a side-effect of this routine. + */ + if (info.main_class != NULL) + *main_class = strdup(info.main_class); + + /* + * If no version selection information is found either on the command + * line or in the manifest, simply return. + */ + if (info.jre_version == NULL) { + free_manifest(); + free(new_argv); + return; + } + + /* + * Check for correct syntax of the version specification (JSR 56). + */ + if (!valid_version_string(info.jre_version)) { + ReportErrorMessage2("Syntax error in version specification \"%s\"", + info.jre_version, JNI_TRUE); + exit(1); + } + + /* + * Find the appropriate JVM on the system. Just to be as forgiving as + * possible, if the standard algorithms don't locate an appropriate + * jre, check to see if the one running will satisfy the requirements. + * This can happen on systems which haven't been set-up for multiple + * JRE support. + */ + jre = LocateJRE(&info); + if (_launcher_debug) + printf("JRE-Version = %s, JRE-Restrict-Search = %s Selected = %s\n", + (info.jre_version?info.jre_version:"null"), + (info.jre_restrict_search?"true":"false"), (jre?jre:"null")); + if (jre == NULL) { + if (acceptable_release(FULL_VERSION, info.jre_version)) { + free_manifest(); + free(new_argv); + return; + } else { + ReportErrorMessage2( + "Unable to locate JRE meeting specification \"%s\"", + info.jre_version, JNI_TRUE); + exit(1); + } + } + + /* + * If I'm not the chosen one, exec the chosen one. Returning from + * ExecJRE indicates that I am indeed the chosen one. + * + * The private environment variable _JAVA_VERSION_SET is used to + * prevent the chosen one from re-reading the manifest file and + * using the values found within to override the (potential) command + * line flags stripped from argv (because the target may not + * understand them). Passing the MainClass value is an optimization + * to avoid locating, expanding and parsing the manifest extra + * times. + */ + if (info.main_class != NULL) + (void)strcat(env_entry, info.main_class); + (void)putenv(env_entry); + ExecJRE(jre, new_argv); + free_manifest(); + free(new_argv); + return; +} +#endif /* ifndef GAMMA */ + +/* + * Parses command line arguments. Returns JNI_FALSE if launcher + * should exit without starting vm (e.g. certain version and usage + * options); returns JNI_TRUE if vm needs to be started to process + * given options. *pret (the launcher process return value) is set to + * 0 for a normal exit. + */ +static jboolean +ParseArguments(int *pargc, char ***pargv, char **pjarfile, + char **pclassname, int *pret) +{ + int argc = *pargc; + char **argv = *pargv; + jboolean jarflag = JNI_FALSE; + char *arg; + + *pret = 1; + while ((arg = *argv) != 0 && *arg == '-') { + argv++; --argc; + if (strcmp(arg, "-classpath") == 0 || strcmp(arg, "-cp") == 0) { + if (argc < 1) { + ReportErrorMessage2("%s requires class path specification", + arg, JNI_TRUE); + PrintUsage(); + return JNI_FALSE; + } + SetClassPath(*argv); + argv++; --argc; + } else if (strcmp(arg, "-jar") == 0) { + jarflag = JNI_TRUE; + } else if (strcmp(arg, "-help") == 0 || + strcmp(arg, "-h") == 0 || + strcmp(arg, "-?") == 0) { + PrintUsage(); + *pret = 0; + return JNI_FALSE; + } else if (strcmp(arg, "-version") == 0) { + printVersion = JNI_TRUE; + return JNI_TRUE; + } else if (strcmp(arg, "-showversion") == 0) { + showVersion = JNI_TRUE; + } else if (strcmp(arg, "-X") == 0) { + *pret = PrintXUsage(); + return JNI_FALSE; +/* + * The following case provide backward compatibility with old-style + * command line options. + */ + } else if (strcmp(arg, "-fullversion") == 0) { + fprintf(stderr, "%s full version \"%s\"\n", progname, + FULL_VERSION); + *pret = 0; + return JNI_FALSE; + } else if (strcmp(arg, "-verbosegc") == 0) { + AddOption("-verbose:gc", NULL); + } else if (strcmp(arg, "-t") == 0) { + AddOption("-Xt", NULL); + } else if (strcmp(arg, "-tm") == 0) { + AddOption("-Xtm", NULL); + } else if (strcmp(arg, "-debug") == 0) { + AddOption("-Xdebug", NULL); + } else if (strcmp(arg, "-noclassgc") == 0) { + AddOption("-Xnoclassgc", NULL); + } else if (strcmp(arg, "-Xfuture") == 0) { + AddOption("-Xverify:all", NULL); + } else if (strcmp(arg, "-verify") == 0) { + AddOption("-Xverify:all", NULL); + } else if (strcmp(arg, "-verifyremote") == 0) { + AddOption("-Xverify:remote", NULL); + } else if (strcmp(arg, "-noverify") == 0) { + AddOption("-Xverify:none", NULL); + } else if (strcmp(arg, "-XXsuppressExitMessage") == 0) { + noExitErrorMessage = 1; + } else if (strncmp(arg, "-prof", 5) == 0) { + char *p = arg + 5; + char *tmp = MemAlloc(strlen(arg) + 50); + if (*p) { + sprintf(tmp, "-Xrunhprof:cpu=old,file=%s", p + 1); + } else { + sprintf(tmp, "-Xrunhprof:cpu=old,file=java.prof"); + } + AddOption(tmp, NULL); + } else if (strncmp(arg, "-ss", 3) == 0 || + strncmp(arg, "-oss", 4) == 0 || + strncmp(arg, "-ms", 3) == 0 || + strncmp(arg, "-mx", 3) == 0) { + char *tmp = MemAlloc(strlen(arg) + 6); + sprintf(tmp, "-X%s", arg + 1); /* skip '-' */ + AddOption(tmp, NULL); + } else if (strcmp(arg, "-checksource") == 0 || + strcmp(arg, "-cs") == 0 || + strcmp(arg, "-noasyncgc") == 0) { + /* No longer supported */ + fprintf(stderr, + "Warning: %s option is no longer supported.\n", + arg); + } else if (strncmp(arg, "-version:", 9) == 0 || + strcmp(arg, "-no-jre-restrict-search") == 0 || + strcmp(arg, "-jre-restrict-search") == 0) { + ; /* Ignore machine independent options already handled */ + } else if (RemovableMachineDependentOption(arg) ) { + ; /* Do not pass option to vm. */ + } + else { + AddOption(arg, NULL); + } + } + + if (--argc >= 0) { + if (jarflag) { + *pjarfile = *argv++; + *pclassname = 0; + } else { + *pjarfile = 0; + *pclassname = *argv++; + } + *pargc = argc; + *pargv = argv; + } + + return JNI_TRUE; +} + +/* + * Initializes the Java Virtual Machine. Also frees options array when + * finished. + */ +static jboolean +InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn) +{ + JavaVMInitArgs args; + jint r; + + memset(&args, 0, sizeof(args)); + args.version = JNI_VERSION_1_2; + args.nOptions = numOptions; + args.options = options; + args.ignoreUnrecognized = JNI_FALSE; + + if (_launcher_debug) { + int i = 0; + printf("JavaVM args:\n "); + printf("version 0x%08lx, ", (long)args.version); + printf("ignoreUnrecognized is %s, ", + args.ignoreUnrecognized ? "JNI_TRUE" : "JNI_FALSE"); + printf("nOptions is %ld\n", (long)args.nOptions); + for (i = 0; i < numOptions; i++) + printf(" option[%2d] = '%s'\n", + i, args.options[i].optionString); + } + + r = ifn->CreateJavaVM(pvm, (void **)penv, &args); + free(options); + return r == JNI_OK; +} + + +#define NULL_CHECK0(e) if ((e) == 0) return 0 +#define NULL_CHECK(e) if ((e) == 0) return + +/* + * Returns a pointer to a block of at least 'size' bytes of memory. + * Prints error message and exits if the memory could not be allocated. + */ +void * +MemAlloc(size_t size) +{ + void *p = malloc(size); + if (p == 0) { + perror("malloc"); + exit(1); + } + return p; +} + +static jstring platformEncoding = NULL; +static jstring getPlatformEncoding(JNIEnv *env) { + if (platformEncoding == NULL) { + jstring propname = (*env)->NewStringUTF(env, "sun.jnu.encoding"); + if (propname) { + jclass cls; + jmethodID mid; + NULL_CHECK0 (cls = (*env)->FindClass(env, "java/lang/System")); + NULL_CHECK0 (mid = (*env)->GetStaticMethodID( + env, cls, + "getProperty", + "(Ljava/lang/String;)Ljava/lang/String;")); + platformEncoding = (*env)->CallStaticObjectMethod ( + env, cls, mid, propname); + } + } + return platformEncoding; +} + +static jboolean isEncodingSupported(JNIEnv *env, jstring enc) { + jclass cls; + jmethodID mid; + NULL_CHECK0 (cls = (*env)->FindClass(env, "java/nio/charset/Charset")); + NULL_CHECK0 (mid = (*env)->GetStaticMethodID( + env, cls, + "isSupported", + "(Ljava/lang/String;)Z")); + return (jboolean)(*env)->CallStaticObjectMethod (env, cls, mid, enc); +} + +/* + * Returns a new Java string object for the specified platform string. + */ +static jstring +NewPlatformString(JNIEnv *env, char *s) +{ + int len = (int)strlen(s); + jclass cls; + jmethodID mid; + jbyteArray ary; + jstring enc; + + if (s == NULL) + return 0; + enc = getPlatformEncoding(env); + + ary = (*env)->NewByteArray(env, len); + if (ary != 0) { + jstring str = 0; + (*env)->SetByteArrayRegion(env, ary, 0, len, (jbyte *)s); + if (!(*env)->ExceptionOccurred(env)) { +#ifdef GAMMA + /* We support running JVM with older JDK, so here we have to deal */ + /* with the case that sun.jnu.encoding is undefined (enc == NULL) */ + if (enc != NULL && isEncodingSupported(env, enc) == JNI_TRUE) { +#else + if (isEncodingSupported(env, enc) == JNI_TRUE) { +#endif + NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String")); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "", + "([BLjava/lang/String;)V")); + str = (*env)->NewObject(env, cls, mid, ary, enc); + } else { + /*If the encoding specified in sun.jnu.encoding is not + endorsed by "Charset.isSupported" we have to fall back + to use String(byte[]) explicitly here without specifying + the encoding name, in which the StringCoding class will + pickup the iso-8859-1 as the fallback converter for us. + */ + NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String")); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "", + "([B)V")); + str = (*env)->NewObject(env, cls, mid, ary); + } + (*env)->DeleteLocalRef(env, ary); + return str; + } + } + return 0; +} + +/* + * Returns a new array of Java string objects for the specified + * array of platform strings. + */ +static jobjectArray +NewPlatformStringArray(JNIEnv *env, char **strv, int strc) +{ + jarray cls; + jarray ary; + int i; + + NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String")); + NULL_CHECK0(ary = (*env)->NewObjectArray(env, strc, cls, 0)); + for (i = 0; i < strc; i++) { + jstring str = NewPlatformString(env, *strv++); + NULL_CHECK0(str); + (*env)->SetObjectArrayElement(env, ary, i, str); + (*env)->DeleteLocalRef(env, str); + } + return ary; +} + +/* + * Loads a class, convert the '.' to '/'. + */ +static jclass +LoadClass(JNIEnv *env, char *name) +{ + char *buf = MemAlloc(strlen(name) + 1); + char *s = buf, *t = name, c; + jclass cls; + jlong start, end; + + if (_launcher_debug) + start = CounterGet(); + + do { + c = *t++; + *s++ = (c == '.') ? '/' : c; + } while (c != '\0'); + cls = (*env)->FindClass(env, buf); + free(buf); + + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to load main class\n", + (long)(jint)Counter2Micros(end-start)); + printf("----_JAVA_LAUNCHER_DEBUG----\n"); + } + + return cls; +} + + +/* + * Returns the main class name for the specified jar file. + */ +static jstring +GetMainClassName(JNIEnv *env, char *jarname) +{ +#define MAIN_CLASS "Main-Class" + jclass cls; + jmethodID mid; + jobject jar, man, attr; + jstring str, result = 0; + + NULL_CHECK0(cls = (*env)->FindClass(env, "java/util/jar/JarFile")); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "", + "(Ljava/lang/String;)V")); + NULL_CHECK0(str = NewPlatformString(env, jarname)); + NULL_CHECK0(jar = (*env)->NewObject(env, cls, mid, str)); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "getManifest", + "()Ljava/util/jar/Manifest;")); + man = (*env)->CallObjectMethod(env, jar, mid); + if (man != 0) { + NULL_CHECK0(mid = (*env)->GetMethodID(env, + (*env)->GetObjectClass(env, man), + "getMainAttributes", + "()Ljava/util/jar/Attributes;")); + attr = (*env)->CallObjectMethod(env, man, mid); + if (attr != 0) { + NULL_CHECK0(mid = (*env)->GetMethodID(env, + (*env)->GetObjectClass(env, attr), + "getValue", + "(Ljava/lang/String;)Ljava/lang/String;")); + NULL_CHECK0(str = NewPlatformString(env, MAIN_CLASS)); + result = (*env)->CallObjectMethod(env, attr, mid, str); + } + } + return result; +} + +#ifdef JAVA_ARGS +static char *java_args[] = JAVA_ARGS; +static char *app_classpath[] = APP_CLASSPATH; + +/* + * For tools convert 'javac -J-ms32m' to 'java -ms32m ...' + */ +static void +TranslateDashJArgs(int *pargc, char ***pargv) +{ + const int NUM_ARGS = (sizeof(java_args) / sizeof(char *)); + int argc = *pargc; + char **argv = *pargv; + int nargc = argc + NUM_ARGS; + char **nargv = MemAlloc((nargc + 1) * sizeof(char *)); + int i; + + *pargc = nargc; + *pargv = nargv; + + /* Copy the VM arguments (i.e. prefixed with -J) */ + for (i = 0; i < NUM_ARGS; i++) { + char *arg = java_args[i]; + if (arg[0] == '-' && arg[1] == 'J') { + *nargv++ = arg + 2; + } + } + + for (i = 0; i < argc; i++) { + char *arg = argv[i]; + if (arg[0] == '-' && arg[1] == 'J') { + if (arg[2] == '\0') { + ReportErrorMessage("Error: the -J option should not be " + "followed by a space.", JNI_TRUE); + exit(1); + } + *nargv++ = arg + 2; + } + } + + /* Copy the rest of the arguments */ + for (i = 0; i < NUM_ARGS; i++) { + char *arg = java_args[i]; + if (arg[0] != '-' || arg[1] != 'J') { + *nargv++ = arg; + } + } + for (i = 0; i < argc; i++) { + char *arg = argv[i]; + if (arg[0] != '-' || arg[1] != 'J') { + *nargv++ = arg; + } + } + *nargv = 0; +} + +/* + * For our tools, we try to add 3 VM options: + * -Denv.class.path= + * -Dapplication.home= + * -Djava.class.path= + * is the user's setting of CLASSPATH -- for instance the user + * tells javac where to find binary classes through this environment + * variable. Notice that users will be able to compile against our + * tools classes (sun.tools.javac.Main) only if they explicitly add + * tools.jar to CLASSPATH. + * is the directory where the application is installed. + * is the classpath to where our apps' classfiles are. + */ +static jboolean +AddApplicationOptions() +{ + const int NUM_APP_CLASSPATH = (sizeof(app_classpath) / sizeof(char *)); + char *s, *envcp, *appcp, *apphome; + char home[MAXPATHLEN]; /* application home */ + char separator[] = { PATH_SEPARATOR, '\0' }; + int size, i; + int strlenHome; + + s = getenv("CLASSPATH"); + if (s) { + /* 40 for -Denv.class.path= */ + envcp = (char *)MemAlloc(strlen(s) + 40); + sprintf(envcp, "-Denv.class.path=%s", s); + AddOption(envcp, NULL); + } + + if (!GetApplicationHome(home, sizeof(home))) { + ReportErrorMessage("Can't determine application home", JNI_TRUE); + return JNI_FALSE; + } + + /* 40 for '-Dapplication.home=' */ + apphome = (char *)MemAlloc(strlen(home) + 40); + sprintf(apphome, "-Dapplication.home=%s", home); + AddOption(apphome, NULL); + + /* How big is the application's classpath? */ + size = 40; /* 40: "-Djava.class.path=" */ + strlenHome = (int)strlen(home); + for (i = 0; i < NUM_APP_CLASSPATH; i++) { + size += strlenHome + (int)strlen(app_classpath[i]) + 1; /* 1: separator */ + } + appcp = (char *)MemAlloc(size + 1); + strcpy(appcp, "-Djava.class.path="); + for (i = 0; i < NUM_APP_CLASSPATH; i++) { + strcat(appcp, home); /* c:\program files\myapp */ + strcat(appcp, app_classpath[i]); /* \lib\myapp.jar */ + strcat(appcp, separator); /* ; */ + } + appcp[strlen(appcp)-1] = '\0'; /* remove trailing path separator */ + AddOption(appcp, NULL); + return JNI_TRUE; +} +#endif + +/* + * inject the -Dsun.java.command pseudo property into the args structure + * this pseudo property is used in the HotSpot VM to expose the + * Java class name and arguments to the main method to the VM. The + * HotSpot VM uses this pseudo property to store the Java class name + * (or jar file name) and the arguments to the class's main method + * to the instrumentation memory region. The sun.java.command pseudo + * property is not exported by HotSpot to the Java layer. + */ +void +SetJavaCommandLineProp(char *classname, char *jarfile, + int argc, char **argv) +{ + + int i = 0; + size_t len = 0; + char* javaCommand = NULL; + char* dashDstr = "-Dsun.java.command="; + + if (classname == NULL && jarfile == NULL) { + /* unexpected, one of these should be set. just return without + * setting the property + */ + return; + } + + /* if the class name is not set, then use the jarfile name */ + if (classname == NULL) { + classname = jarfile; + } + + /* determine the amount of memory to allocate assuming + * the individual components will be space separated + */ + len = strlen(classname); + for (i = 0; i < argc; i++) { + len += strlen(argv[i]) + 1; + } + + /* allocate the memory */ + javaCommand = (char*) MemAlloc(len + strlen(dashDstr) + 1); + + /* build the -D string */ + *javaCommand = '\0'; + strcat(javaCommand, dashDstr); + strcat(javaCommand, classname); + + for (i = 0; i < argc; i++) { + /* the components of the string are space separated. In + * the case of embedded white space, the relationship of + * the white space separated components to their true + * positional arguments will be ambiguous. This issue may + * be addressed in a future release. + */ + strcat(javaCommand, " "); + strcat(javaCommand, argv[i]); + } + + AddOption(javaCommand, NULL); +} + +/* + * JVM wants to know launcher type, so tell it. + */ +#ifdef GAMMA +void SetJavaLauncherProp() { + AddOption("-Dsun.java.launcher=" LAUNCHER_TYPE, NULL); +} +#endif + +/* + * Prints the version information from the java.version and other properties. + */ +static void +PrintJavaVersion(JNIEnv *env) +{ + jclass ver; + jmethodID print; + + NULL_CHECK(ver = (*env)->FindClass(env, "sun/misc/Version")); + NULL_CHECK(print = (*env)->GetStaticMethodID(env, ver, "print", "()V")); + + (*env)->CallStaticVoidMethod(env, ver, print); +} + +/* + * Prints default usage message. + */ +static void +PrintUsage(void) +{ + int i; + + fprintf(stdout, + "Usage: %s [-options] class [args...]\n" + " (to execute a class)\n" + " or %s [-options] -jar jarfile [args...]\n" + " (to execute a jar file)\n" + "\n" + "where options include:\n", + progname, + progname); + +#ifndef GAMMA + PrintMachineDependentOptions(); + + if ((knownVMs[0].flag == VM_KNOWN) || + (knownVMs[0].flag == VM_IF_SERVER_CLASS)) { + fprintf(stdout, " %s\t to select the \"%s\" VM\n", + knownVMs[0].name, knownVMs[0].name+1); + } + for (i=1; i\n" +" -classpath \n" +" A %c separated list of directories, JAR archives,\n" +" and ZIP archives to search for class files.\n" +" -D=\n" +" set a system property\n" +" -verbose[:class|gc|jni]\n" +" enable verbose output\n" +" -version print product version and exit\n" +" -version:\n" +" require the specified version to run\n" +" -showversion print product version and continue\n" +" -jre-restrict-search | -jre-no-restrict-search\n" +" include/exclude user private JREs in the version search\n" +" -? -help print this help message\n" +" -X print help on non-standard options\n" +" -ea[:...|:]\n" +" -enableassertions[:...|:]\n" +" enable assertions\n" +" -da[:...|:]\n" +" -disableassertions[:...|:]\n" +" disable assertions\n" +" -esa | -enablesystemassertions\n" +" enable system assertions\n" +" -dsa | -disablesystemassertions\n" +" disable system assertions\n" +" -agentlib:[=]\n" +" load native agent library , e.g. -agentlib:hprof\n" +" see also, -agentlib:jdwp=help and -agentlib:hprof=help\n" +" -agentpath:[=]\n" +" load native agent library by full pathname\n" +" -javaagent:[=]\n" +" load Java programming language agent, see java.lang.instrument\n" + + ,PATH_SEPARATOR); +} + +/* + * Print usage message for -X options. + */ +static jint +PrintXUsage(void) +{ + char path[MAXPATHLEN]; + char buf[128]; + size_t n; + FILE *fp; + + GetXUsagePath(path, sizeof(path)); + fp = fopen(path, "r"); + if (fp == 0) { + fprintf(stderr, "Can't open %s\n", path); + return 1; + } + while ((n = fread(buf, 1, sizeof(buf), fp)) != 0) { + fwrite(buf, 1, n, stdout); + } + fclose(fp); + return 0; +} + +#ifndef GAMMA + +/* + * Read the jvm.cfg file and fill the knownJVMs[] array. + * + * The functionality of the jvm.cfg file is subject to change without + * notice and the mechanism will be removed in the future. + * + * The lexical structure of the jvm.cfg file is as follows: + * + * jvmcfg := { vmLine } + * vmLine := knownLine + * | aliasLine + * | warnLine + * | ignoreLine + * | errorLine + * | predicateLine + * | commentLine + * knownLine := flag "KNOWN" EOL + * warnLine := flag "WARN" EOL + * ignoreLine := flag "IGNORE" EOL + * errorLine := flag "ERROR" EOL + * aliasLine := flag "ALIASED_TO" flag EOL + * predicateLine := flag "IF_SERVER_CLASS" flag EOL + * commentLine := "#" text EOL + * flag := "-" identifier + * + * The semantics are that when someone specifies a flag on the command line: + * - if the flag appears on a knownLine, then the identifier is used as + * the name of the directory holding the JVM library (the name of the JVM). + * - if the flag appears as the first flag on an aliasLine, the identifier + * of the second flag is used as the name of the JVM. + * - if the flag appears on a warnLine, the identifier is used as the + * name of the JVM, but a warning is generated. + * - if the flag appears on an ignoreLine, the identifier is recognized as the + * name of a JVM, but the identifier is ignored and the default vm used + * - if the flag appears on an errorLine, an error is generated. + * - if the flag appears as the first flag on a predicateLine, and + * the machine on which you are running passes the predicate indicated, + * then the identifier of the second flag is used as the name of the JVM, + * otherwise the identifier of the first flag is used as the name of the JVM. + * If no flag is given on the command line, the first vmLine of the jvm.cfg + * file determines the name of the JVM. + * PredicateLines are only interpreted on first vmLine of a jvm.cfg file, + * since they only make sense if someone hasn't specified the name of the + * JVM on the command line. + * + * The intent of the jvm.cfg file is to allow several JVM libraries to + * be installed in different subdirectories of a single JRE installation, + * for space-savings and convenience in testing. + * The intent is explicitly not to provide a full aliasing or predicate + * mechanism. + */ +jint +ReadKnownVMs(const char *jrepath, char * arch, jboolean speculative) +{ + FILE *jvmCfg; + char jvmCfgName[MAXPATHLEN+20]; + char line[MAXPATHLEN+20]; + int cnt = 0; + int lineno = 0; + jlong start, end; + int vmType; + char *tmpPtr; + char *altVMName; + char *serverClassVMName; + static char *whiteSpace = " \t"; + if (_launcher_debug) { + start = CounterGet(); + } + + strcpy(jvmCfgName, jrepath); + strcat(jvmCfgName, FILESEP "lib" FILESEP); + strcat(jvmCfgName, arch); + strcat(jvmCfgName, FILESEP "jvm.cfg"); + + jvmCfg = fopen(jvmCfgName, "r"); + if (jvmCfg == NULL) { + if (!speculative) { + ReportErrorMessage2("Error: could not open `%s'", jvmCfgName, + JNI_TRUE); + exit(1); + } else { + return -1; + } + } + while (fgets(line, sizeof(line), jvmCfg) != NULL) { + vmType = VM_UNKNOWN; + lineno++; + if (line[0] == '#') + continue; + if (line[0] != '-') { + fprintf(stderr, "Warning: no leading - on line %d of `%s'\n", + lineno, jvmCfgName); + } + if (cnt >= knownVMsLimit) { + GrowKnownVMs(cnt); + } + line[strlen(line)-1] = '\0'; /* remove trailing newline */ + tmpPtr = line + strcspn(line, whiteSpace); + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing VM type on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + /* Null-terminate this string for strdup below */ + *tmpPtr++ = 0; + tmpPtr += strspn(tmpPtr, whiteSpace); + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing VM type on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + if (!strncmp(tmpPtr, "KNOWN", strlen("KNOWN"))) { + vmType = VM_KNOWN; + } else if (!strncmp(tmpPtr, "ALIASED_TO", strlen("ALIASED_TO"))) { + tmpPtr += strcspn(tmpPtr, whiteSpace); + if (*tmpPtr != 0) { + tmpPtr += strspn(tmpPtr, whiteSpace); + } + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing VM alias on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + /* Null terminate altVMName */ + altVMName = tmpPtr; + tmpPtr += strcspn(tmpPtr, whiteSpace); + *tmpPtr = 0; + vmType = VM_ALIASED_TO; + } + } else if (!strncmp(tmpPtr, "WARN", strlen("WARN"))) { + vmType = VM_WARN; + } else if (!strncmp(tmpPtr, "IGNORE", strlen("IGNORE"))) { + vmType = VM_IGNORE; + } else if (!strncmp(tmpPtr, "ERROR", strlen("ERROR"))) { + vmType = VM_ERROR; + } else if (!strncmp(tmpPtr, + "IF_SERVER_CLASS", + strlen("IF_SERVER_CLASS"))) { + tmpPtr += strcspn(tmpPtr, whiteSpace); + if (*tmpPtr != 0) { + tmpPtr += strspn(tmpPtr, whiteSpace); + } + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing server class VM on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + /* Null terminate server class VM name */ + serverClassVMName = tmpPtr; + tmpPtr += strcspn(tmpPtr, whiteSpace); + *tmpPtr = 0; + vmType = VM_IF_SERVER_CLASS; + } + } else { + fprintf(stderr, "Warning: unknown VM type on line %d of `%s'\n", + lineno, &jvmCfgName[0]); + vmType = VM_KNOWN; + } + } + } + + if (_launcher_debug) + printf("jvm.cfg[%d] = ->%s<-\n", cnt, line); + if (vmType != VM_UNKNOWN) { + knownVMs[cnt].name = strdup(line); + knownVMs[cnt].flag = vmType; + switch (vmType) { + default: + break; + case VM_ALIASED_TO: + knownVMs[cnt].alias = strdup(altVMName); + if (_launcher_debug) { + printf(" name: %s vmType: %s alias: %s\n", + knownVMs[cnt].name, "VM_ALIASED_TO", knownVMs[cnt].alias); + } + break; + case VM_IF_SERVER_CLASS: + knownVMs[cnt].server_class = strdup(serverClassVMName); + if (_launcher_debug) { + printf(" name: %s vmType: %s server_class: %s\n", + knownVMs[cnt].name, "VM_IF_SERVER_CLASS", knownVMs[cnt].server_class); + } + break; + } + cnt++; + } + } + fclose(jvmCfg); + knownVMsCount = cnt; + + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to parse jvm.cfg\n", + (long)(jint)Counter2Micros(end-start)); + } + + return cnt; +} + + +static void +GrowKnownVMs(int minimum) +{ + struct vmdesc* newKnownVMs; + int newMax; + + newMax = (knownVMsLimit == 0 ? INIT_MAX_KNOWN_VMS : (2 * knownVMsLimit)); + if (newMax <= minimum) { + newMax = minimum; + } + newKnownVMs = (struct vmdesc*) MemAlloc(newMax * sizeof(struct vmdesc)); + if (knownVMs != NULL) { + memcpy(newKnownVMs, knownVMs, knownVMsLimit * sizeof(struct vmdesc)); + } + free(knownVMs); + knownVMs = newKnownVMs; + knownVMsLimit = newMax; +} + + +/* Returns index of VM or -1 if not found */ +static int +KnownVMIndex(const char* name) +{ + int i; + if (strncmp(name, "-J", 2) == 0) name += 2; + for (i = 0; i < knownVMsCount; i++) { + if (!strcmp(name, knownVMs[i].name)) { + return i; + } + } + return -1; +} + +static void +FreeKnownVMs() +{ + int i; + for (i = 0; i < knownVMsCount; i++) { + free(knownVMs[i].name); + knownVMs[i].name = NULL; + } + free(knownVMs); +} + +#endif /* ifndef GAMMA */ diff --git a/hotspot/src/os/solaris/launcher/java.h b/hotspot/src/os/solaris/launcher/java.h new file mode 100644 index 00000000000..1e3321b7d4c --- /dev/null +++ b/hotspot/src/os/solaris/launcher/java.h @@ -0,0 +1,104 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Gamma (Hotspot internal engineering test) launcher based on 1.6.0-b28 JDK, + * search "GAMMA" for gamma specific changes. + */ + +#ifndef _JAVA_H_ +#define _JAVA_H_ + +/* + * Get system specific defines. + */ +#include "jni.h" +#include "java_md.h" + +/* + * Pointers to the needed JNI invocation API, initialized by LoadJavaVM. + */ +typedef jint (JNICALL *CreateJavaVM_t)(JavaVM **pvm, void **env, void *args); +typedef jint (JNICALL *GetDefaultJavaVMInitArgs_t)(void *args); + +typedef struct { + CreateJavaVM_t CreateJavaVM; + GetDefaultJavaVMInitArgs_t GetDefaultJavaVMInitArgs; +} InvocationFunctions; + +/* + * Prototypes for launcher functions in the system specific java_md.c. + */ + +jboolean +LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn); + +void +GetXUsagePath(char *buf, jint bufsize); + +jboolean +GetApplicationHome(char *buf, jint bufsize); + +const char * +GetArch(); + +void CreateExecutionEnvironment(int *_argc, + char ***_argv, + char jrepath[], + jint so_jrepath, + char jvmpath[], + jint so_jvmpath, + char **original_argv); + +/* + * Report an error message to stderr or a window as appropriate. The + * flag always is set to JNI_TRUE if message is to be reported to both + * strerr and windows and set to JNI_FALSE if the message should only + * be sent to a window. + */ +void ReportErrorMessage(char * message, jboolean always); +void ReportErrorMessage2(char * format, char * string, jboolean always); + +/* + * Report an exception which terminates the vm to stderr or a window + * as appropriate. + */ +void ReportExceptionDescription(JNIEnv * env); + +jboolean RemovableMachineDependentOption(char * option); +void PrintMachineDependentOptions(); + +/* + * Functions defined in java.c and used in java_md.c. + */ +jint ReadKnownVMs(const char *jrepath, char * arch, jboolean speculative); +char *CheckJvmType(int *argc, char ***argv, jboolean speculative); +void* MemAlloc(size_t size); + +/* + * Make launcher spit debug output. + */ +extern jboolean _launcher_debug; + +#endif /* _JAVA_H_ */ diff --git a/hotspot/src/os/solaris/launcher/java_md.c b/hotspot/src/os/solaris/launcher/java_md.c new file mode 100644 index 00000000000..09f8b89ea17 --- /dev/null +++ b/hotspot/src/os/solaris/launcher/java_md.c @@ -0,0 +1,1828 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Gamma (Hotspot internal engineering test) launcher based on 1.6.0-b28 JDK, + * search "GAMMA" for gamma specific changes. + */ + +#include "java.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GAMMA +#include "manifest_info.h" +#include "version_comp.h" +#endif + +#define JVM_DLL "libjvm.so" +#define JAVA_DLL "libjava.so" + +#ifndef GAMMA /* launcher.make defines ARCH */ + +/* + * If a processor / os combination has the ability to run binaries of + * two data models and cohabitation of jre/jdk bits with both data + * models is supported, then DUAL_MODE is defined. When DUAL_MODE is + * defined, the architecture names for the narrow and wide version of + * the architecture are defined in BIG_ARCH and SMALL_ARCH. Currently + * only Solaris on sparc/sparcv9 and i586/amd64 is DUAL_MODE; linux + * i586/amd64 could be defined as DUAL_MODE but that is not the + * current policy. + */ + +#ifdef _LP64 + +# ifdef ia64 +# define ARCH "ia64" +# elif defined(amd64) +# define ARCH "amd64" +# elif defined(__sparc) +# define ARCH "sparcv9" +# else +# define ARCH "unknown" /* unknown 64-bit architecture */ +# endif + +#else /* 32-bit data model */ + +# ifdef i586 +# define ARCH "i386" +# elif defined(__sparc) +# define ARCH "sparc" +# endif + +#endif /* _LP64 */ + +#ifdef __sun +# define DUAL_MODE +# ifdef __sparc +# define BIG_ARCH "sparcv9" +# define SMALL_ARCH "sparc" +# else +# define BIG_ARCH "amd64" +# define SMALL_ARCH "i386" +# endif +# include +# include +# include +#else +# ifndef ARCH +# include +# endif +#endif + +#endif /* ifndef GAMMA */ + +/* pointer to environment */ +extern char **environ; + +#ifndef GAMMA + +/* + * A collection of useful strings. One should think of these as #define + * entries, but actual strings can be more efficient (with many compilers). + */ +#ifdef __linux__ +static const char *system_dir = "/usr/java"; +static const char *user_dir = "/java"; +#else /* Solaris */ +static const char *system_dir = "/usr/jdk"; +static const char *user_dir = "/jdk"; +#endif + +#endif /* ifndef GAMMA */ + +/* + * Flowchart of launcher execs and options processing on unix + * + * The selection of the proper vm shared library to open depends on + * several classes of command line options, including vm "flavor" + * options (-client, -server) and the data model options, -d32 and + * -d64, as well as a version specification which may have come from + * the command line or from the manifest of an executable jar file. + * The vm selection options are not passed to the running + * virtual machine; they must be screened out by the launcher. + * + * The version specification (if any) is processed first by the + * platform independent routine SelectVersion. This may result in + * the exec of the specified launcher version. + * + * Typically, the launcher execs at least once to ensure a suitable + * LD_LIBRARY_PATH is in effect for the process. The first exec + * screens out all the data model options; leaving the choice of data + * model implicit in the binary selected to run. However, in case no + * exec is done, the data model options are screened out before the vm + * is invoked. + * + * incoming argv ------------------------------ + * | | + * \|/ | + * CheckJVMType | + * (removes -client, -server, etc.) | + * \|/ + * CreateExecutionEnvironment + * (removes -d32 and -d64, + * determines desired data model, + * sets up LD_LIBRARY_PATH, + * and exec's) + * | + * -------------------------------------------- + * | + * \|/ + * exec child 1 incoming argv ----------------- + * | | + * \|/ | + * CheckJVMType | + * (removes -client, -server, etc.) | + * | \|/ + * | CreateExecutionEnvironment + * | (verifies desired data model + * | is running and acceptable + * | LD_LIBRARY_PATH; + * | no-op in child) + * | + * \|/ + * TranslateDashJArgs... + * (Prepare to pass args to vm) + * | + * | + * | + * \|/ + * ParseArguments + * (ignores -d32 and -d64, + * processes version options, + * creates argument list for vm, + * etc.) + * + */ + +static char *SetExecname(char **argv); +static char * GetExecname(); +static jboolean GetJVMPath(const char *jrepath, const char *jvmtype, + char *jvmpath, jint jvmpathsize, char * arch); +static jboolean GetJREPath(char *path, jint pathsize, char * arch, jboolean speculative); + +const char * +GetArch() +{ + static char *arch = NULL; + static char buf[12]; + if (arch) { + return arch; + } + +#ifdef ARCH + strcpy(buf, ARCH); +#else + sysinfo(SI_ARCHITECTURE, buf, sizeof(buf)); +#endif + arch = buf; + return arch; +} + +void +CreateExecutionEnvironment(int *_argcp, + char ***_argvp, + char jrepath[], + jint so_jrepath, + char jvmpath[], + jint so_jvmpath, + char **original_argv) { + /* + * First, determine if we are running the desired data model. If we + * are running the desired data model, all the error messages + * associated with calling GetJREPath, ReadKnownVMs, etc. should be + * output. However, if we are not running the desired data model, + * some of the errors should be suppressed since it is more + * informative to issue an error message based on whether or not the + * os/processor combination has dual mode capabilities. + */ + + char *execname = NULL; + int original_argc = *_argcp; + jboolean jvmpathExists; + + /* Compute the name of the executable */ + execname = SetExecname(*_argvp); + +#ifndef GAMMA + /* Set the LD_LIBRARY_PATH environment variable, check data model + flags, and exec process, if needed */ + { + char *arch = (char *)GetArch(); /* like sparc or sparcv9 */ + char * jvmtype = NULL; + int argc = *_argcp; + char **argv = original_argv; + + char *runpath = NULL; /* existing effective LD_LIBRARY_PATH + setting */ + + int running = /* What data model is being ILP32 => + 32 bit vm; LP64 => 64 bit vm */ +#ifdef _LP64 + 64; +#else + 32; +#endif + + int wanted = running; /* What data mode is being + asked for? Current model is + fine unless another model + is asked for */ + + char* new_runpath = NULL; /* desired new LD_LIBRARY_PATH string */ + char* newpath = NULL; /* path on new LD_LIBRARY_PATH */ + char* lastslash = NULL; + + char** newenvp = NULL; /* current environment */ + + char** newargv = NULL; + int newargc = 0; +#ifdef __sun + char* dmpath = NULL; /* data model specific LD_LIBRARY_PATH, + Solaris only */ +#endif + + /* + * Starting in 1.5, all unix platforms accept the -d32 and -d64 + * options. On platforms where only one data-model is supported + * (e.g. ia-64 Linux), using the flag for the other data model is + * an error and will terminate the program. + */ + + { /* open new scope to declare local variables */ + int i; + + newargv = (char **)MemAlloc((argc+1) * sizeof(*newargv)); + newargv[newargc++] = argv[0]; + + /* scan for data model arguments and remove from argument list; + last occurrence determines desired data model */ + for (i=1; i < argc; i++) { + + if (strcmp(argv[i], "-J-d64") == 0 || strcmp(argv[i], "-d64") == 0) { + wanted = 64; + continue; + } + if (strcmp(argv[i], "-J-d32") == 0 || strcmp(argv[i], "-d32") == 0) { + wanted = 32; + continue; + } + newargv[newargc++] = argv[i]; + +#ifdef JAVA_ARGS + if (argv[i][0] != '-') + continue; +#else + if (strcmp(argv[i], "-classpath") == 0 || strcmp(argv[i], "-cp") == 0) { + i++; + if (i >= argc) break; + newargv[newargc++] = argv[i]; + continue; + } + if (argv[i][0] != '-') { i++; break; } +#endif + } + + /* copy rest of args [i .. argc) */ + while (i < argc) { + newargv[newargc++] = argv[i++]; + } + newargv[newargc] = NULL; + + /* + * newargv has all proper arguments here + */ + + argc = newargc; + argv = newargv; + } + + /* If the data model is not changing, it is an error if the + jvmpath does not exist */ + if (wanted == running) { + /* Find out where the JRE is that we will be using. */ + if (!GetJREPath(jrepath, so_jrepath, arch, JNI_FALSE) ) { + fprintf(stderr, "Error: could not find Java 2 Runtime Environment.\n"); + exit(2); + } + + /* Find the specified JVM type */ + if (ReadKnownVMs(jrepath, arch, JNI_FALSE) < 1) { + fprintf(stderr, "Error: no known VMs. (check for corrupt jvm.cfg file)\n"); + exit(1); + } + + jvmpath[0] = '\0'; + jvmtype = CheckJvmType(_argcp, _argvp, JNI_FALSE); + + if (!GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath, arch )) { + fprintf(stderr, "Error: no `%s' JVM at `%s'.\n", jvmtype, jvmpath); + exit(4); + } + } else { /* do the same speculatively or exit */ +#ifdef DUAL_MODE + if (running != wanted) { + /* Find out where the JRE is that we will be using. */ + if (!GetJREPath(jrepath, so_jrepath, ((wanted==64)?BIG_ARCH:SMALL_ARCH), JNI_TRUE)) { + goto EndDataModelSpeculate; + } + + /* + * Read in jvm.cfg for target data model and process vm + * selection options. + */ + if (ReadKnownVMs(jrepath, ((wanted==64)?BIG_ARCH:SMALL_ARCH), JNI_TRUE) < 1) { + goto EndDataModelSpeculate; + } + jvmpath[0] = '\0'; + jvmtype = CheckJvmType(_argcp, _argvp, JNI_TRUE); + /* exec child can do error checking on the existence of the path */ + jvmpathExists = GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath, + ((wanted==64)?BIG_ARCH:SMALL_ARCH)); + + } + EndDataModelSpeculate: /* give up and let other code report error message */ + ; +#else + fprintf(stderr, "Running a %d-bit JVM is not supported on this platform.\n", wanted); + exit(1); +#endif + } + + /* + * We will set the LD_LIBRARY_PATH as follows: + * + * o $JVMPATH (directory portion only) + * o $JRE/lib/$ARCH + * o $JRE/../lib/$ARCH + * + * followed by the user's previous effective LD_LIBRARY_PATH, if + * any. + */ + +#ifdef __sun + /* + * Starting in Solaris 7, ld.so.1 supports three LD_LIBRARY_PATH + * variables: + * + * 1. LD_LIBRARY_PATH -- used for 32 and 64 bit searches if + * data-model specific variables are not set. + * + * 2. LD_LIBRARY_PATH_64 -- overrides and replaces LD_LIBRARY_PATH + * for 64-bit binaries. + * + * 3. LD_LIBRARY_PATH_32 -- overrides and replaces LD_LIBRARY_PATH + * for 32-bit binaries. + * + * The vm uses LD_LIBRARY_PATH to set the java.library.path system + * property. To shield the vm from the complication of multiple + * LD_LIBRARY_PATH variables, if the appropriate data model + * specific variable is set, we will act as if LD_LIBRARY_PATH had + * the value of the data model specific variant and the data model + * specific variant will be unset. Note that the variable for the + * *wanted* data model must be used (if it is set), not simply the + * current running data model. + */ + + switch(wanted) { + case 0: + if(running == 32) { + dmpath = getenv("LD_LIBRARY_PATH_32"); + wanted = 32; + } + else { + dmpath = getenv("LD_LIBRARY_PATH_64"); + wanted = 64; + } + break; + + case 32: + dmpath = getenv("LD_LIBRARY_PATH_32"); + break; + + case 64: + dmpath = getenv("LD_LIBRARY_PATH_64"); + break; + + default: + fprintf(stderr, "Improper value at line %d.", __LINE__); + exit(1); /* unknown value in wanted */ + break; + } + + /* + * If dmpath is NULL, the relevant data model specific variable is + * not set and normal LD_LIBRARY_PATH should be used. + */ + if( dmpath == NULL) { + runpath = getenv("LD_LIBRARY_PATH"); + } + else { + runpath = dmpath; + } +#else + /* + * If not on Solaris, assume only a single LD_LIBRARY_PATH + * variable. + */ + runpath = getenv("LD_LIBRARY_PATH"); +#endif /* __sun */ + +#ifdef __linux + /* + * On linux, if a binary is running as sgid or suid, glibc sets + * LD_LIBRARY_PATH to the empty string for security purposes. (In + * contrast, on Solaris the LD_LIBRARY_PATH variable for a + * privileged binary does not lose its settings; but the dynamic + * linker does apply more scrutiny to the path.) The launcher uses + * the value of LD_LIBRARY_PATH to prevent an exec loop. + * Therefore, if we are running sgid or suid, this function's + * setting of LD_LIBRARY_PATH will be ineffective and we should + * return from the function now. Getting the right libraries to + * be found must be handled through other mechanisms. + */ + if((getgid() != getegid()) || (getuid() != geteuid()) ) { + return; + } +#endif + + /* runpath contains current effective LD_LIBRARY_PATH setting */ + + jvmpath = strdup(jvmpath); + new_runpath = MemAlloc( ((runpath!=NULL)?strlen(runpath):0) + + 2*strlen(jrepath) + 2*strlen(arch) + + strlen(jvmpath) + 52); + newpath = new_runpath + strlen("LD_LIBRARY_PATH="); + + + /* + * Create desired LD_LIBRARY_PATH value for target data model. + */ + { + /* remove the name of the .so from the JVM path */ + lastslash = strrchr(jvmpath, '/'); + if (lastslash) + *lastslash = '\0'; + + + /* jvmpath, ((running != wanted)?((wanted==64)?"/"BIG_ARCH:"/.."):""), */ + + sprintf(new_runpath, "LD_LIBRARY_PATH=" + "%s:" + "%s/lib/%s:" + "%s/../lib/%s", + jvmpath, +#ifdef DUAL_MODE + jrepath, ((wanted==64)?BIG_ARCH:SMALL_ARCH), + jrepath, ((wanted==64)?BIG_ARCH:SMALL_ARCH) +#else + jrepath, arch, + jrepath, arch +#endif + ); + + + /* + * Check to make sure that the prefix of the current path is the + * desired environment variable setting. + */ + if (runpath != NULL && + strncmp(newpath, runpath, strlen(newpath))==0 && + (runpath[strlen(newpath)] == 0 || runpath[strlen(newpath)] == ':') && + (running == wanted) /* data model does not have to be changed */ +#ifdef __sun + && (dmpath == NULL) /* data model specific variables not set */ +#endif + ) { + + return; + + } + } + + /* + * Place the desired environment setting onto the prefix of + * LD_LIBRARY_PATH. Note that this prevents any possible infinite + * loop of execv() because we test for the prefix, above. + */ + if (runpath != 0) { + strcat(new_runpath, ":"); + strcat(new_runpath, runpath); + } + + if( putenv(new_runpath) != 0) { + exit(1); /* problem allocating memory; LD_LIBRARY_PATH not set + properly */ + } + + /* + * Unix systems document that they look at LD_LIBRARY_PATH only + * once at startup, so we have to re-exec the current executable + * to get the changed environment variable to have an effect. + */ + +#ifdef __sun + /* + * If dmpath is not NULL, remove the data model specific string + * in the environment for the exec'ed child. + */ + + if( dmpath != NULL) + (void)UnsetEnv((wanted==32)?"LD_LIBRARY_PATH_32":"LD_LIBRARY_PATH_64"); +#endif + + newenvp = environ; + + { + char *newexec = execname; +#ifdef DUAL_MODE + /* + * If the data model is being changed, the path to the + * executable must be updated accordingly; the executable name + * and directory the executable resides in are separate. In the + * case of 32 => 64, the new bits are assumed to reside in, e.g. + * "olddir/BIGARCH/execname"; in the case of 64 => 32, + * the bits are assumed to be in "olddir/../execname". For example, + * + * olddir/sparcv9/execname + * olddir/amd64/execname + * + * for Solaris SPARC and Linux amd64, respectively. + */ + + if (running != wanted) { + char *oldexec = strcpy(MemAlloc(strlen(execname) + 1), execname); + char *olddir = oldexec; + char *oldbase = strrchr(oldexec, '/'); + + + newexec = MemAlloc(strlen(execname) + 20); + *oldbase++ = 0; + sprintf(newexec, "%s/%s/%s", olddir, + ((wanted==64) ? BIG_ARCH : ".."), oldbase); + argv[0] = newexec; + } +#endif + + execve(newexec, argv, newenvp); + perror("execve()"); + + fprintf(stderr, "Error trying to exec %s.\n", newexec); + fprintf(stderr, "Check if file exists and permissions are set correctly.\n"); + +#ifdef DUAL_MODE + if (running != wanted) { + fprintf(stderr, "Failed to start a %d-bit JVM process from a %d-bit JVM.\n", + wanted, running); +# ifdef __sun + +# ifdef __sparc + fprintf(stderr, "Verify all necessary J2SE components have been installed.\n" ); + fprintf(stderr, + "(Solaris SPARC 64-bit components must be installed after 32-bit components.)\n" ); +# else + fprintf(stderr, "Either 64-bit processes are not supported by this platform\n"); + fprintf(stderr, "or the 64-bit components have not been installed.\n"); +# endif + } +# endif +#endif + + } + + exit(1); + } + +#else /* ifndef GAMMA */ + + /* gamma launcher is simpler in that it doesn't handle VM flavors, data */ + /* model, LD_LIBRARY_PATH, etc. Assuming everything is set-up correctly */ + /* all we need to do here is to return correct path names. See also */ + /* GetJVMPath() and GetApplicationHome(). */ + + { char *arch = (char *)GetArch(); /* like sparc or sparcv9 */ + char *p; + + if (!GetJREPath(jrepath, so_jrepath, arch, JNI_FALSE) ) { + fprintf(stderr, "Error: could not find Java 2 Runtime Environment.\n"); + exit(2); + } + + if (!GetJVMPath(jrepath, NULL, jvmpath, so_jvmpath, arch )) { + fprintf(stderr, "Error: no JVM at `%s'.\n", jvmpath); + exit(4); + } + } + +#endif /* ifndef GAMMA */ +} + + +/* + * On Solaris VM choosing is done by the launcher (java.c). + */ +static jboolean +GetJVMPath(const char *jrepath, const char *jvmtype, + char *jvmpath, jint jvmpathsize, char * arch) +{ + struct stat s; + +#ifndef GAMMA + if (strchr(jvmtype, '/')) { + sprintf(jvmpath, "%s/" JVM_DLL, jvmtype); + } else { + sprintf(jvmpath, "%s/lib/%s/%s/" JVM_DLL, jrepath, arch, jvmtype); + } +#else + /* For gamma launcher, JVM is either built-in or in the same directory. */ + /* Either way we return "/libjvm.so" where is the */ + /* directory where gamma launcher is located. */ + + char *p; + + snprintf(jvmpath, jvmpathsize, "%s", GetExecname()); + p = strrchr(jvmpath, '/'); + if (p) { + /* replace executable name with libjvm.so */ + snprintf(p + 1, jvmpathsize - (p + 1 - jvmpath), "%s", JVM_DLL); + } else { + /* this case shouldn't happen */ + snprintf(jvmpath, jvmpathsize, "%s", JVM_DLL); + } +#endif + + if (_launcher_debug) + printf("Does `%s' exist ... ", jvmpath); + + if (stat(jvmpath, &s) == 0) { + if (_launcher_debug) + printf("yes.\n"); + return JNI_TRUE; + } else { + if (_launcher_debug) + printf("no.\n"); + return JNI_FALSE; + } +} + +/* + * Find path to JRE based on .exe's location or registry settings. + */ +static jboolean +GetJREPath(char *path, jint pathsize, char * arch, jboolean speculative) +{ + char libjava[MAXPATHLEN]; + + if (GetApplicationHome(path, pathsize)) { + /* Is JRE co-located with the application? */ + sprintf(libjava, "%s/lib/%s/" JAVA_DLL, path, arch); + if (access(libjava, F_OK) == 0) { + goto found; + } + + /* Does the app ship a private JRE in /jre directory? */ + sprintf(libjava, "%s/jre/lib/%s/" JAVA_DLL, path, arch); + if (access(libjava, F_OK) == 0) { + strcat(path, "/jre"); + goto found; + } + } + + if (!speculative) + fprintf(stderr, "Error: could not find " JAVA_DLL "\n"); + return JNI_FALSE; + + found: + if (_launcher_debug) + printf("JRE path is %s\n", path); + return JNI_TRUE; +} + +jboolean +LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) +{ +#ifdef GAMMA + /* JVM is directly linked with gamma launcher; no dlopen() */ + ifn->CreateJavaVM = JNI_CreateJavaVM; + ifn->GetDefaultJavaVMInitArgs = JNI_GetDefaultJavaVMInitArgs; + return JNI_TRUE; +#else + Dl_info dlinfo; + void *libjvm; + + if (_launcher_debug) { + printf("JVM path is %s\n", jvmpath); + } + + libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL); + if (libjvm == NULL) { +#if defined(__sparc) && !defined(_LP64) /* i.e. 32-bit sparc */ + FILE * fp; + Elf32_Ehdr elf_head; + int count; + int location; + + fp = fopen(jvmpath, "r"); + if(fp == NULL) + goto error; + + /* read in elf header */ + count = fread((void*)(&elf_head), sizeof(Elf32_Ehdr), 1, fp); + fclose(fp); + if(count < 1) + goto error; + + /* + * Check for running a server vm (compiled with -xarch=v8plus) + * on a stock v8 processor. In this case, the machine type in + * the elf header would not be included the architecture list + * provided by the isalist command, which is turn is gotten from + * sysinfo. This case cannot occur on 64-bit hardware and thus + * does not have to be checked for in binaries with an LP64 data + * model. + */ + if(elf_head.e_machine == EM_SPARC32PLUS) { + char buf[257]; /* recommended buffer size from sysinfo man + page */ + long length; + char* location; + + length = sysinfo(SI_ISALIST, buf, 257); + if(length > 0) { + location = strstr(buf, "sparcv8plus "); + if(location == NULL) { + fprintf(stderr, "SPARC V8 processor detected; Server compiler requires V9 or better.\n"); + fprintf(stderr, "Use Client compiler on V8 processors.\n"); + fprintf(stderr, "Could not create the Java virtual machine.\n"); + return JNI_FALSE; + } + } + } +#endif + fprintf(stderr, "dl failure on line %d", __LINE__); + goto error; + } + + ifn->CreateJavaVM = (CreateJavaVM_t) + dlsym(libjvm, "JNI_CreateJavaVM"); + if (ifn->CreateJavaVM == NULL) + goto error; + + ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t) + dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs"); + if (ifn->GetDefaultJavaVMInitArgs == NULL) + goto error; + + return JNI_TRUE; + +error: + fprintf(stderr, "Error: failed %s, because %s\n", jvmpath, dlerror()); + return JNI_FALSE; +#endif /* GAMMA */ +} + +/* + * Get the path to the file that has the usage message for -X options. + */ +void +GetXUsagePath(char *buf, jint bufsize) +{ + static const char Xusage_txt[] = "/Xusage.txt"; + Dl_info dlinfo; + + /* we use RTLD_NOW because of problems with ld.so.1 and green threads */ + dladdr(dlsym(dlopen(JVM_DLL, RTLD_NOW), "JNI_CreateJavaVM"), &dlinfo); + strncpy(buf, (char *)dlinfo.dli_fname, bufsize - sizeof(Xusage_txt)); + + buf[bufsize-1] = '\0'; + strcpy(strrchr(buf, '/'), Xusage_txt); +} + +/* + * If app is "/foo/bin/javac", or "/foo/bin/sparcv9/javac" then put + * "/foo" into buf. + */ +jboolean +GetApplicationHome(char *buf, jint bufsize) +{ +#ifdef __linux__ + char *execname = GetExecname(); + if (execname) { + strncpy(buf, execname, bufsize-1); + buf[bufsize-1] = '\0'; + } else { + return JNI_FALSE; + } +#else + Dl_info dlinfo; + + dladdr((void *)GetApplicationHome, &dlinfo); + if (realpath(dlinfo.dli_fname, buf) == NULL) { + fprintf(stderr, "Error: realpath(`%s') failed.\n", dlinfo.dli_fname); + return JNI_FALSE; + } +#endif + +#ifdef GAMMA + { + /* gamma launcher uses JAVA_HOME environment variable to find JDK/JRE */ + char* java_home_var = getenv("JAVA_HOME"); + if (java_home_var == NULL) { + printf("JAVA_HOME must point to a valid JDK/JRE to run gamma\n"); + return JNI_FALSE; + } + snprintf(buf, bufsize, "%s", java_home_var); + } +#else + if (strrchr(buf, '/') == 0) { + buf[0] = '\0'; + return JNI_FALSE; + } + *(strrchr(buf, '/')) = '\0'; /* executable file */ + if (strlen(buf) < 4 || strrchr(buf, '/') == 0) { + buf[0] = '\0'; + return JNI_FALSE; + } + if (strcmp("/bin", buf + strlen(buf) - 4) != 0) + *(strrchr(buf, '/')) = '\0'; /* sparcv9 or amd64 */ + if (strlen(buf) < 4 || strcmp("/bin", buf + strlen(buf) - 4) != 0) { + buf[0] = '\0'; + return JNI_FALSE; + } + *(strrchr(buf, '/')) = '\0'; /* bin */ +#endif /* GAMMA */ + + return JNI_TRUE; +} + + +/* + * Return true if the named program exists + */ +static int +ProgramExists(char *name) +{ + struct stat sb; + if (stat(name, &sb) != 0) return 0; + if (S_ISDIR(sb.st_mode)) return 0; + return (sb.st_mode & S_IEXEC) != 0; +} + + +/* + * Find a command in a directory, returning the path. + */ +static char * +Resolve(char *indir, char *cmd) +{ + char name[PATH_MAX + 2], *real; + + if ((strlen(indir) + strlen(cmd) + 1) > PATH_MAX) return 0; + sprintf(name, "%s%c%s", indir, FILE_SEPARATOR, cmd); + if (!ProgramExists(name)) return 0; + real = MemAlloc(PATH_MAX + 2); + if (!realpath(name, real)) + strcpy(real, name); + return real; +} + + +/* + * Find a path for the executable + */ +static char * +FindExecName(char *program) +{ + char cwdbuf[PATH_MAX+2]; + char *path; + char *tmp_path; + char *f; + char *result = NULL; + + /* absolute path? */ + if (*program == FILE_SEPARATOR || + (FILE_SEPARATOR=='\\' && strrchr(program, ':'))) + return Resolve("", program+1); + + /* relative path? */ + if (strrchr(program, FILE_SEPARATOR) != 0) { + char buf[PATH_MAX+2]; + return Resolve(getcwd(cwdbuf, sizeof(cwdbuf)), program); + } + + /* from search path? */ + path = getenv("PATH"); + if (!path || !*path) path = "."; + tmp_path = MemAlloc(strlen(path) + 2); + strcpy(tmp_path, path); + + for (f=tmp_path; *f && result==0; ) { + char *s = f; + while (*f && (*f != PATH_SEPARATOR)) ++f; + if (*f) *f++ = 0; + if (*s == FILE_SEPARATOR) + result = Resolve(s, program); + else { + /* relative path element */ + char dir[2*PATH_MAX]; + sprintf(dir, "%s%c%s", getcwd(cwdbuf, sizeof(cwdbuf)), + FILE_SEPARATOR, s); + result = Resolve(dir, program); + } + if (result != 0) break; + } + + free(tmp_path); + return result; +} + + +/* Store the name of the executable once computed */ +static char *execname = NULL; + +/* + * Compute the name of the executable + * + * In order to re-exec securely we need the absolute path of the + * executable. On Solaris getexecname(3c) may not return an absolute + * path so we use dladdr to get the filename of the executable and + * then use realpath to derive an absolute path. From Solaris 9 + * onwards the filename returned in DL_info structure from dladdr is + * an absolute pathname so technically realpath isn't required. + * On Linux we read the executable name from /proc/self/exe. + * As a fallback, and for platforms other than Solaris and Linux, + * we use FindExecName to compute the executable name. + */ +static char * +SetExecname(char **argv) +{ + char* exec_path = NULL; + + if (execname != NULL) /* Already determined */ + return (execname); + +#if defined(__sun) + { + Dl_info dlinfo; + if (dladdr((void*)&SetExecname, &dlinfo)) { + char *resolved = (char*)MemAlloc(PATH_MAX+1); + if (resolved != NULL) { + exec_path = realpath(dlinfo.dli_fname, resolved); + if (exec_path == NULL) { + free(resolved); + } + } + } + } +#elif defined(__linux__) + { + const char* self = "/proc/self/exe"; + char buf[PATH_MAX+1]; + int len = readlink(self, buf, PATH_MAX); + if (len >= 0) { + buf[len] = '\0'; /* readlink doesn't nul terminate */ + exec_path = strdup(buf); + } + } +#else /* !__sun && !__linux */ + { + /* Not implemented */ + } +#endif + + if (exec_path == NULL) { + exec_path = FindExecName(argv[0]); + } + execname = exec_path; + return exec_path; +} + +/* + * Return the name of the executable. Used in java_md.c to find the JRE area. + */ +static char * +GetExecname() { + return execname; +} + +void ReportErrorMessage(char * message, jboolean always) { + if (always) { + fprintf(stderr, "%s\n", message); + } +} + +void ReportErrorMessage2(char * format, char * string, jboolean always) { + if (always) { + fprintf(stderr, format, string); + fprintf(stderr, "\n"); + } +} + +void ReportExceptionDescription(JNIEnv * env) { + (*env)->ExceptionDescribe(env); +} + +/* + * Return JNI_TRUE for an option string that has no effect but should + * _not_ be passed on to the vm; return JNI_FALSE otherwise. On + * Solaris SPARC, this screening needs to be done if: + * 1) LD_LIBRARY_PATH does _not_ need to be reset and + * 2) -d32 or -d64 is passed to a binary with a matching data model + * (the exec in SetLibraryPath removes -d options and points the + * exec to the proper binary). When this exec is not done, these options + * would end up getting passed onto the vm. + */ +jboolean RemovableMachineDependentOption(char * option) { + /* + * Unconditionally remove both -d32 and -d64 options since only + * the last such options has an effect; e.g. + * java -d32 -d64 -d32 -version + * is equivalent to + * java -d32 -version + */ + + if( (strcmp(option, "-d32") == 0 ) || + (strcmp(option, "-d64") == 0 )) + return JNI_TRUE; + else + return JNI_FALSE; +} + +void PrintMachineDependentOptions() { + fprintf(stdout, + " -d32 use a 32-bit data model if available\n" + "\n" + " -d64 use a 64-bit data model if available\n"); + return; +} + +#ifndef GAMMA /* gamma launcher does not have ergonomics */ + +/* + * The following methods (down to ServerClassMachine()) answer + * the question about whether a machine is a "server-class" + * machine. A server-class machine is loosely defined as one + * with 2 or more processors and 2 gigabytes or more physical + * memory. The definition of a processor is a physical package, + * not a hyperthreaded chip masquerading as a multi-processor. + * The definition of memory is also somewhat fuzzy, since x86 + * machines seem not to report all the memory in their DIMMs, we + * think because of memory mapping of graphics cards, etc. + * + * This code is somewhat more confused with #ifdef's than we'd + * like because this file is used by both Solaris and Linux + * platforms, and so needs to be parameterized for SPARC and + * i586 hardware. The other Linux platforms (amd64 and ia64) + * don't even ask this question, because they only come with + * server JVMs. */ + +# define KB (1024UL) +# define MB (1024UL * KB) +# define GB (1024UL * MB) + +/* Compute physical memory by asking the OS */ +uint64_t +physical_memory(void) { + const uint64_t pages = (uint64_t) sysconf(_SC_PHYS_PAGES); + const uint64_t page_size = (uint64_t) sysconf(_SC_PAGESIZE); + const uint64_t result = pages * page_size; +# define UINT64_FORMAT "%" PRIu64 + + if (_launcher_debug) { + printf("pages: " UINT64_FORMAT + " page_size: " UINT64_FORMAT + " physical memory: " UINT64_FORMAT " (%.3fGB)\n", + pages, page_size, result, result / (double) GB); + } + return result; +} + +#if defined(__sun) && defined(__sparc) + +/* Methods for solaris-sparc: these are easy. */ + +/* Ask the OS how many processors there are. */ +unsigned long +physical_processors(void) { + const unsigned long sys_processors = sysconf(_SC_NPROCESSORS_CONF); + + if (_launcher_debug) { + printf("sysconf(_SC_NPROCESSORS_CONF): %lu\n", sys_processors); + } + return sys_processors; +} + +/* The solaris-sparc version of the "server-class" predicate. */ +jboolean +solaris_sparc_ServerClassMachine(void) { + jboolean result = JNI_FALSE; + /* How big is a server class machine? */ + const unsigned long server_processors = 2UL; + const uint64_t server_memory = 2UL * GB; + const uint64_t actual_memory = physical_memory(); + + /* Is this a server class machine? */ + if (actual_memory >= server_memory) { + const unsigned long actual_processors = physical_processors(); + if (actual_processors >= server_processors) { + result = JNI_TRUE; + } + } + if (_launcher_debug) { + printf("solaris_" ARCH "_ServerClassMachine: %s\n", + (result == JNI_TRUE ? "JNI_TRUE" : "JNI_FALSE")); + } + return result; +} + +#endif /* __sun && __sparc */ + +#if defined(__sun) && defined(i586) + +/* + * A utility method for asking the CPU about itself. + * There's a corresponding version of linux-i586 + * because the compilers are different. + */ +void +get_cpuid(uint32_t arg, + uint32_t* eaxp, + uint32_t* ebxp, + uint32_t* ecxp, + uint32_t* edxp) { +#ifdef _LP64 + asm( + /* rbx is a callee-saved register */ + " movq %rbx, %r11 \n" + /* rdx and rcx are 3rd and 4th argument registers */ + " movq %rdx, %r10 \n" + " movq %rcx, %r9 \n" + " movl %edi, %eax \n" + " cpuid \n" + " movl %eax, (%rsi)\n" + " movl %ebx, (%r10)\n" + " movl %ecx, (%r9) \n" + " movl %edx, (%r8) \n" + /* Restore rbx */ + " movq %r11, %rbx"); +#else + /* EBX is a callee-saved register */ + asm(" pushl %ebx"); + /* Need ESI for storing through arguments */ + asm(" pushl %esi"); + asm(" movl 8(%ebp), %eax \n" + " cpuid \n" + " movl 12(%ebp), %esi \n" + " movl %eax, (%esi) \n" + " movl 16(%ebp), %esi \n" + " movl %ebx, (%esi) \n" + " movl 20(%ebp), %esi \n" + " movl %ecx, (%esi) \n" + " movl 24(%ebp), %esi \n" + " movl %edx, (%esi) "); + /* Restore ESI and EBX */ + asm(" popl %esi"); + /* Restore EBX */ + asm(" popl %ebx"); +#endif +} + +#endif /* __sun && i586 */ + +#if defined(__linux__) && defined(i586) + +/* + * A utility method for asking the CPU about itself. + * There's a corresponding version of solaris-i586 + * because the compilers are different. + */ +void +get_cpuid(uint32_t arg, + uint32_t* eaxp, + uint32_t* ebxp, + uint32_t* ecxp, + uint32_t* edxp) { +#ifdef _LP64 + __asm__ volatile (/* Instructions */ + " movl %4, %%eax \n" + " cpuid \n" + " movl %%eax, (%0)\n" + " movl %%ebx, (%1)\n" + " movl %%ecx, (%2)\n" + " movl %%edx, (%3)\n" + : /* Outputs */ + : /* Inputs */ + "r" (eaxp), + "r" (ebxp), + "r" (ecxp), + "r" (edxp), + "r" (arg) + : /* Clobbers */ + "%rax", "%rbx", "%rcx", "%rdx", "memory" + ); +#else + uint32_t value_of_eax = 0; + uint32_t value_of_ebx = 0; + uint32_t value_of_ecx = 0; + uint32_t value_of_edx = 0; + __asm__ volatile (/* Instructions */ + /* ebx is callee-save, so push it */ + /* even though it's in the clobbers section */ + " pushl %%ebx \n" + " movl %4, %%eax \n" + " cpuid \n" + " movl %%eax, %0 \n" + " movl %%ebx, %1 \n" + " movl %%ecx, %2 \n" + " movl %%edx, %3 \n" + /* restore ebx */ + " popl %%ebx \n" + + : /* Outputs */ + "=m" (value_of_eax), + "=m" (value_of_ebx), + "=m" (value_of_ecx), + "=m" (value_of_edx) + : /* Inputs */ + "m" (arg) + : /* Clobbers */ + "%eax", "%ebx", "%ecx", "%edx" + ); + *eaxp = value_of_eax; + *ebxp = value_of_ebx; + *ecxp = value_of_ecx; + *edxp = value_of_edx; +#endif +} + +#endif /* __linux__ && i586 */ + +#ifdef i586 +/* + * Routines shared by solaris-i586 and linux-i586. + */ + +enum HyperThreadingSupport_enum { + hts_supported = 1, + hts_too_soon_to_tell = 0, + hts_not_supported = -1, + hts_not_pentium4 = -2, + hts_not_intel = -3 +}; +typedef enum HyperThreadingSupport_enum HyperThreadingSupport; + +/* Determine if hyperthreading is supported */ +HyperThreadingSupport +hyperthreading_support(void) { + HyperThreadingSupport result = hts_too_soon_to_tell; + /* Bits 11 through 8 is family processor id */ +# define FAMILY_ID_SHIFT 8 +# define FAMILY_ID_MASK 0xf + /* Bits 23 through 20 is extended family processor id */ +# define EXT_FAMILY_ID_SHIFT 20 +# define EXT_FAMILY_ID_MASK 0xf + /* Pentium 4 family processor id */ +# define PENTIUM4_FAMILY_ID 0xf + /* Bit 28 indicates Hyper-Threading Technology support */ +# define HT_BIT_SHIFT 28 +# define HT_BIT_MASK 1 + uint32_t vendor_id[3] = { 0U, 0U, 0U }; + uint32_t value_of_eax = 0U; + uint32_t value_of_edx = 0U; + uint32_t dummy = 0U; + + /* Yes, this is supposed to be [0], [2], [1] */ + get_cpuid(0, &dummy, &vendor_id[0], &vendor_id[2], &vendor_id[1]); + if (_launcher_debug) { + printf("vendor: %c %c %c %c %c %c %c %c %c %c %c %c \n", + ((vendor_id[0] >> 0) & 0xff), + ((vendor_id[0] >> 8) & 0xff), + ((vendor_id[0] >> 16) & 0xff), + ((vendor_id[0] >> 24) & 0xff), + ((vendor_id[1] >> 0) & 0xff), + ((vendor_id[1] >> 8) & 0xff), + ((vendor_id[1] >> 16) & 0xff), + ((vendor_id[1] >> 24) & 0xff), + ((vendor_id[2] >> 0) & 0xff), + ((vendor_id[2] >> 8) & 0xff), + ((vendor_id[2] >> 16) & 0xff), + ((vendor_id[2] >> 24) & 0xff)); + } + get_cpuid(1, &value_of_eax, &dummy, &dummy, &value_of_edx); + if (_launcher_debug) { + printf("value_of_eax: 0x%x value_of_edx: 0x%x\n", + value_of_eax, value_of_edx); + } + if ((((value_of_eax >> FAMILY_ID_SHIFT) & FAMILY_ID_MASK) == PENTIUM4_FAMILY_ID) || + (((value_of_eax >> EXT_FAMILY_ID_SHIFT) & EXT_FAMILY_ID_MASK) != 0)) { + if ((((vendor_id[0] >> 0) & 0xff) == 'G') && + (((vendor_id[0] >> 8) & 0xff) == 'e') && + (((vendor_id[0] >> 16) & 0xff) == 'n') && + (((vendor_id[0] >> 24) & 0xff) == 'u') && + (((vendor_id[1] >> 0) & 0xff) == 'i') && + (((vendor_id[1] >> 8) & 0xff) == 'n') && + (((vendor_id[1] >> 16) & 0xff) == 'e') && + (((vendor_id[1] >> 24) & 0xff) == 'I') && + (((vendor_id[2] >> 0) & 0xff) == 'n') && + (((vendor_id[2] >> 8) & 0xff) == 't') && + (((vendor_id[2] >> 16) & 0xff) == 'e') && + (((vendor_id[2] >> 24) & 0xff) == 'l')) { + if (((value_of_edx >> HT_BIT_SHIFT) & HT_BIT_MASK) == HT_BIT_MASK) { + if (_launcher_debug) { + printf("Hyperthreading supported\n"); + } + result = hts_supported; + } else { + if (_launcher_debug) { + printf("Hyperthreading not supported\n"); + } + result = hts_not_supported; + } + } else { + if (_launcher_debug) { + printf("Not GenuineIntel\n"); + } + result = hts_not_intel; + } + } else { + if (_launcher_debug) { + printf("not Pentium 4 or extended\n"); + } + result = hts_not_pentium4; + } + return result; +} + +/* Determine how many logical processors there are per CPU */ +unsigned int +logical_processors_per_package(void) { + /* + * After CPUID with EAX==1, register EBX bits 23 through 16 + * indicate the number of logical processors per package + */ +# define NUM_LOGICAL_SHIFT 16 +# define NUM_LOGICAL_MASK 0xff + unsigned int result = 1U; + const HyperThreadingSupport hyperthreading = hyperthreading_support(); + + if (hyperthreading == hts_supported) { + uint32_t value_of_ebx = 0U; + uint32_t dummy = 0U; + + get_cpuid(1, &dummy, &value_of_ebx, &dummy, &dummy); + result = (value_of_ebx >> NUM_LOGICAL_SHIFT) & NUM_LOGICAL_MASK; + if (_launcher_debug) { + printf("logical processors per package: %u\n", result); + } + } + return result; +} + +/* Compute the number of physical processors, not logical processors */ +unsigned long +physical_processors(void) { + const long sys_processors = sysconf(_SC_NPROCESSORS_CONF); + unsigned long result = sys_processors; + + if (_launcher_debug) { + printf("sysconf(_SC_NPROCESSORS_CONF): %lu\n", sys_processors); + } + if (sys_processors > 1) { + unsigned int logical_processors = logical_processors_per_package(); + if (logical_processors > 1) { + result = (unsigned long) sys_processors / logical_processors; + } + } + if (_launcher_debug) { + printf("physical processors: %lu\n", result); + } + return result; +} + +#endif /* i586 */ + +#if defined(__sun) && defined(i586) + +/* The definition of a server-class machine for solaris-i586/amd64 */ +jboolean +solaris_i586_ServerClassMachine(void) { + jboolean result = JNI_FALSE; + /* How big is a server class machine? */ + const unsigned long server_processors = 2UL; + const uint64_t server_memory = 2UL * GB; + /* + * We seem not to get our full complement of memory. + * We allow some part (1/8?) of the memory to be "missing", + * based on the sizes of DIMMs, and maybe graphics cards. + */ + const uint64_t missing_memory = 256UL * MB; + const uint64_t actual_memory = physical_memory(); + + /* Is this a server class machine? */ + if (actual_memory >= (server_memory - missing_memory)) { + const unsigned long actual_processors = physical_processors(); + if (actual_processors >= server_processors) { + result = JNI_TRUE; + } + } + if (_launcher_debug) { + printf("solaris_" ARCH "_ServerClassMachine: %s\n", + (result == JNI_TRUE ? "true" : "false")); + } + return result; +} + +#endif /* __sun && i586 */ + +#if defined(__linux__) && defined(i586) + +/* The definition of a server-class machine for linux-i586 */ +jboolean +linux_i586_ServerClassMachine(void) { + jboolean result = JNI_FALSE; + /* How big is a server class machine? */ + const unsigned long server_processors = 2UL; + const uint64_t server_memory = 2UL * GB; + /* + * We seem not to get our full complement of memory. + * We allow some part (1/8?) of the memory to be "missing", + * based on the sizes of DIMMs, and maybe graphics cards. + */ + const uint64_t missing_memory = 256UL * MB; + const uint64_t actual_memory = physical_memory(); + + /* Is this a server class machine? */ + if (actual_memory >= (server_memory - missing_memory)) { + const unsigned long actual_processors = physical_processors(); + if (actual_processors >= server_processors) { + result = JNI_TRUE; + } + } + if (_launcher_debug) { + printf("linux_" ARCH "_ServerClassMachine: %s\n", + (result == JNI_TRUE ? "true" : "false")); + } + return result; +} + +#endif /* __linux__ && i586 */ + +/* Dispatch to the platform-specific definition of "server-class" */ +jboolean +ServerClassMachine(void) { + jboolean result = JNI_FALSE; +#if defined(__sun) && defined(__sparc) + result = solaris_sparc_ServerClassMachine(); +#elif defined(__sun) && defined(i586) + result = solaris_i586_ServerClassMachine(); +#elif defined(__linux__) && defined(i586) + result = linux_i586_ServerClassMachine(); +#else + if (_launcher_debug) { + printf("ServerClassMachine: returns default value of %s\n", + (result == JNI_TRUE ? "true" : "false")); + } +#endif + return result; +} + +#endif /* ifndef GAMMA */ + +#ifndef GAMMA /* gamma launcher does not choose JDK/JRE/JVM */ + +/* + * Since using the file system as a registry is a bit risky, perform + * additional sanity checks on the identified directory to validate + * it as a valid jre/sdk. + * + * Return 0 if the tests fail; otherwise return non-zero (true). + * + * Note that checking for anything more than the existence of an + * executable object at bin/java relative to the path being checked + * will break the regression tests. + */ +static int +CheckSanity(char *path, char *dir) +{ + char buffer[PATH_MAX]; + + if (strlen(path) + strlen(dir) + 11 > PATH_MAX) + return (0); /* Silently reject "impossibly" long paths */ + + (void)strcat(strcat(strcat(strcpy(buffer, path), "/"), dir), "/bin/java"); + return ((access(buffer, X_OK) == 0) ? 1 : 0); +} + +/* + * Determine if there is an acceptable JRE in the directory dirname. + * Upon locating the "best" one, return a fully qualified path to + * it. "Best" is defined as the most advanced JRE meeting the + * constraints contained in the manifest_info. If no JRE in this + * directory meets the constraints, return NULL. + * + * Note that we don't check for errors in reading the directory + * (which would be done by checking errno). This is because it + * doesn't matter if we get an error reading the directory, or + * we just don't find anything interesting in the directory. We + * just return NULL in either case. + * + * The historical names of j2sdk and j2re were changed to jdk and + * jre respecively as part of the 1.5 rebranding effort. Since the + * former names are legacy on Linux, they must be recognized for + * all time. Fortunately, this is a minor cost. + */ +static char +*ProcessDir(manifest_info *info, char *dirname) +{ + DIR *dirp; + struct dirent *dp; + char *best = NULL; + int offset; + int best_offset = 0; + char *ret_str = NULL; + char buffer[PATH_MAX]; + + if ((dirp = opendir(dirname)) == NULL) + return (NULL); + + do { + if ((dp = readdir(dirp)) != NULL) { + offset = 0; + if ((strncmp(dp->d_name, "jre", 3) == 0) || + (strncmp(dp->d_name, "jdk", 3) == 0)) + offset = 3; + else if (strncmp(dp->d_name, "j2re", 4) == 0) + offset = 4; + else if (strncmp(dp->d_name, "j2sdk", 5) == 0) + offset = 5; + if (offset > 0) { + if ((acceptable_release(dp->d_name + offset, + info->jre_version)) && CheckSanity(dirname, dp->d_name)) + if ((best == NULL) || (exact_version_id( + dp->d_name + offset, best + best_offset) > 0)) { + if (best != NULL) + free(best); + best = strdup(dp->d_name); + best_offset = offset; + } + } + } + } while (dp != NULL); + (void) closedir(dirp); + if (best == NULL) + return (NULL); + else { + ret_str = MemAlloc(strlen(dirname) + strlen(best) + 2); + ret_str = strcat(strcat(strcpy(ret_str, dirname), "/"), best); + free(best); + return (ret_str); + } +} + +/* + * This is the global entry point. It examines the host for the optimal + * JRE to be used by scanning a set of directories. The set of directories + * is platform dependent and can be overridden by the environment + * variable JAVA_VERSION_PATH. + * + * This routine itself simply determines the set of appropriate + * directories before passing control onto ProcessDir(). + */ +char* +LocateJRE(manifest_info* info) +{ + char *path; + char *home; + char *target = NULL; + char *dp; + char *cp; + + /* + * Start by getting JAVA_VERSION_PATH + */ + if (info->jre_restrict_search) + path = strdup(system_dir); + else if ((path = getenv("JAVA_VERSION_PATH")) != NULL) + path = strdup(path); + else + if ((home = getenv("HOME")) != NULL) { + path = (char *)MemAlloc(strlen(home) + 13); + path = strcat(strcat(strcat(strcpy(path, home), + user_dir), ":"), system_dir); + } else + path = strdup(system_dir); + + /* + * Step through each directory on the path. Terminate the scan with + * the first directory with an acceptable JRE. + */ + cp = dp = path; + while (dp != NULL) { + cp = strchr(dp, (int)':'); + if (cp != NULL) + *cp = (char)NULL; + if ((target = ProcessDir(info, dp)) != NULL) + break; + dp = cp; + if (dp != NULL) + dp++; + } + free(path); + return (target); +} + +/* + * Given a path to a jre to execute, this routine checks if this process + * is indeed that jre. If not, it exec's that jre. + * + * We want to actually check the paths rather than just the version string + * built into the executable, so that given version specification (and + * JAVA_VERSION_PATH) will yield the exact same Java environment, regardless + * of the version of the arbitrary launcher we start with. + */ +void +ExecJRE(char *jre, char **argv) +{ + char wanted[PATH_MAX]; + char *execname; + char *progname; + + /* + * Resolve the real path to the directory containing the selected JRE. + */ + if (realpath(jre, wanted) == NULL) { + fprintf(stderr, "Unable to resolve %s\n", jre); + exit(1); + } + + /* + * Resolve the real path to the currently running launcher. + */ + execname = SetExecname(argv); + if (execname == NULL) { + fprintf(stderr, "Unable to resolve current executable\n"); + exit(1); + } + + /* + * If the path to the selected JRE directory is a match to the initial + * portion of the path to the currently executing JRE, we have a winner! + * If so, just return. + */ + if (strncmp(wanted, execname, strlen(wanted)) == 0) + return; /* I am the droid you were looking for */ + + /* + * If this isn't the selected version, exec the selected version. + */ +#ifdef JAVA_ARGS /* javac, jar and friends. */ + progname = "java"; +#else /* java, oldjava, javaw and friends */ +#ifdef PROGNAME + progname = PROGNAME; +#else + progname = *argv; + if ((s = strrchr(progname, FILE_SEPARATOR)) != 0) { + progname = s + 1; + } +#endif /* PROGNAME */ +#endif /* JAVA_ARGS */ + + /* + * This should never happen (because of the selection code in SelectJRE), + * but check for "impossibly" long path names just because buffer overruns + * can be so deadly. + */ + if (strlen(wanted) + strlen(progname) + 6 > PATH_MAX) { + fprintf(stderr, "Path length exceeds maximum length (PATH_MAX)\n"); + exit(1); + } + + /* + * Construct the path and exec it. + */ + (void)strcat(strcat(wanted, "/bin/"), progname); + argv[0] = progname; + if (_launcher_debug) { + int i; + printf("execv(\"%s\"", wanted); + for (i = 0; argv[i] != NULL; i++) + printf(", \"%s\"", argv[i]); + printf(")\n"); + } + execv(wanted, argv); + fprintf(stderr, "Exec of %s failed\n", wanted); + exit(1); +} + +#endif /* ifndef GAMMA */ + +/* + * "Borrowed" from Solaris 10 where the unsetenv() function is being added + * to libc thanks to SUSv3 (Standard Unix Specification, version 3). As + * such, in the fullness of time this will appear in libc on all relevant + * Solaris/Linux platforms and maybe even the Windows platform. At that + * time, this stub can be removed. + * + * This implementation removes the environment locking for multithreaded + * applications. (We don't have access to these mutexes within libc and + * the launcher isn't multithreaded.) Note that what remains is platform + * independent, because it only relies on attributes that a POSIX environment + * defines. + * + * Returns 0 on success, -1 on failure. + * + * Also removed was the setting of errno. The only value of errno set + * was EINVAL ("Invalid Argument"). + */ + +/* + * s1(environ) is name=value + * s2(name) is name(not the form of name=value). + * if names match, return value of 1, else return 0 + */ +static int +match_noeq(const char *s1, const char *s2) +{ + while (*s1 == *s2++) { + if (*s1++ == '=') + return (1); + } + if (*s1 == '=' && s2[-1] == '\0') + return (1); + return (0); +} + +/* + * added for SUSv3 standard + * + * Delete entry from environ. + * Do not free() memory! Other threads may be using it. + * Keep it around forever. + */ +static int +borrowed_unsetenv(const char *name) +{ + long idx; /* index into environ */ + + if (name == NULL || *name == '\0' || + strchr(name, '=') != NULL) { + return (-1); + } + + for (idx = 0; environ[idx] != NULL; idx++) { + if (match_noeq(environ[idx], name)) + break; + } + if (environ[idx] == NULL) { + /* name not found but still a success */ + return (0); + } + /* squeeze up one entry */ + do { + environ[idx] = environ[idx+1]; + } while (environ[++idx] != NULL); + + return (0); +} +/* --- End of "borrowed" code --- */ + +/* + * Wrapper for unsetenv() function. + */ +int +UnsetEnv(char *name) +{ + return(borrowed_unsetenv(name)); +} diff --git a/hotspot/src/os/solaris/launcher/java_md.h b/hotspot/src/os/solaris/launcher/java_md.h new file mode 100644 index 00000000000..c65b42e2a3f --- /dev/null +++ b/hotspot/src/os/solaris/launcher/java_md.h @@ -0,0 +1,79 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Gamma (Hotspot internal engineering test) launcher based on 1.6.0-b28 JDK, + * search "GAMMA" for gamma specific changes. + */ + +#ifndef JAVA_MD_H +#define JAVA_MD_H + +#include +#include +#include +#ifndef GAMMA +#include "manifest_info.h" +#endif + +#define PATH_SEPARATOR ':' +#define FILESEP "/" +#define FILE_SEPARATOR '/' +#ifndef MAXNAMELEN +#define MAXNAMELEN PATH_MAX +#endif + +#ifdef JAVA_ARGS +/* + * ApplicationHome is prepended to each of these entries; the resulting + * strings are concatenated (seperated by PATH_SEPARATOR) and used as the + * value of -cp option to the launcher. + */ +#ifndef APP_CLASSPATH +#define APP_CLASSPATH { "/lib/tools.jar", "/classes" } +#endif +#endif + +#ifdef HAVE_GETHRTIME +/* + * Support for doing cheap, accurate interval timing. + */ +#include +#define CounterGet() (gethrtime()/1000) +#define Counter2Micros(counts) (counts) +#else +#define CounterGet() (0) +#define Counter2Micros(counts) (1) +#endif /* HAVE_GETHRTIME */ + +/* + * Function prototypes. + */ +#ifndef GAMMA +char *LocateJRE(manifest_info* info); +void ExecJRE(char *jre, char **argv); +#endif +int UnsetEnv(char *name); + +#endif diff --git a/hotspot/src/os/solaris/vm/attachListener_solaris.cpp b/hotspot/src/os/solaris/vm/attachListener_solaris.cpp new file mode 100644 index 00000000000..8ef09b15c3a --- /dev/null +++ b/hotspot/src/os/solaris/vm/attachListener_solaris.cpp @@ -0,0 +1,682 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_attachListener_solaris.cpp.incl" + +#include +#include +#include +#include +#include +#include + +// stropts.h uses STR in stream ioctl defines +#undef STR +#include +#undef STR +#define STR(a) #a + +// The attach mechanism on Solaris is implemented using the Doors IPC +// mechanism. The first tool to attempt to attach causes the attach +// listener thread to startup. This thread creats a door that is +// associated with a function that enqueues an operation to the attach +// listener. The door is attached to a file in the file system so that +// client (tools) can locate it. To enqueue an operation to the VM the +// client calls through the door which invokes the enqueue function in +// this process. The credentials of the client are checked and if the +// effective uid matches this process then the operation is enqueued. +// When an operation completes the attach listener is required to send the +// operation result and any result data to the client. In this implementation +// the result is returned via a UNIX domain socket. A pair of connected +// sockets (socketpair) is created in the enqueue function and the file +// descriptor for one of the sockets is returned to the client as the +// return from the door call. The other end is retained in this process. +// When the operation completes the result is sent to the client and +// the socket is closed. + +// forward reference +class SolarisAttachOperation; + +class SolarisAttachListener: AllStatic { + private: + + // the path to which we attach the door file descriptor + static char _door_path[PATH_MAX+1]; + static volatile bool _has_door_path; + + // door descriptor returned by door_create + static int _door_descriptor; + + static void set_door_path(char* path) { + if (path == NULL) { + _has_door_path = false; + } else { + strncpy(_door_path, path, PATH_MAX); + _door_path[PATH_MAX] = '\0'; // ensure it's nul terminated + _has_door_path = true; + } + } + + static void set_door_descriptor(int dd) { _door_descriptor = dd; } + + // mutex to protect operation list + static mutex_t _mutex; + + // semaphore to wakeup listener thread + static sema_t _wakeup; + + static mutex_t* mutex() { return &_mutex; } + static sema_t* wakeup() { return &_wakeup; } + + // enqueued operation list + static SolarisAttachOperation* _head; + static SolarisAttachOperation* _tail; + + static SolarisAttachOperation* head() { return _head; } + static void set_head(SolarisAttachOperation* head) { _head = head; } + + static SolarisAttachOperation* tail() { return _tail; } + static void set_tail(SolarisAttachOperation* tail) { _tail = tail; } + + // create the door + static int create_door(); + + public: + enum { + ATTACH_PROTOCOL_VER = 1 // protocol version + }; + enum { + ATTACH_ERROR_BADREQUEST = 100, // error code returned by + ATTACH_ERROR_BADVERSION = 101, // the door call + ATTACH_ERROR_RESOURCE = 102, + ATTACH_ERROR_INTERNAL = 103, + ATTACH_ERROR_DENIED = 104 + }; + + // initialize the listener + static int init(); + + static bool has_door_path() { return _has_door_path; } + static char* door_path() { return _door_path; } + static int door_descriptor() { return _door_descriptor; } + + // enqueue an operation + static void enqueue(SolarisAttachOperation* op); + + // dequeue an operation + static SolarisAttachOperation* dequeue(); +}; + + +// SolarisAttachOperation is an AttachOperation that additionally encapsulates +// a socket connection to the requesting client/tool. SolarisAttachOperation +// can additionally be held in a linked list. + +class SolarisAttachOperation: public AttachOperation { + private: + friend class SolarisAttachListener; + + // connection to client + int _socket; + + // linked list support + SolarisAttachOperation* _next; + + SolarisAttachOperation* next() { return _next; } + void set_next(SolarisAttachOperation* next) { _next = next; } + + public: + void complete(jint res, bufferedStream* st); + + int socket() const { return _socket; } + void set_socket(int s) { _socket = s; } + + SolarisAttachOperation(char* name) : AttachOperation(name) { + set_socket(-1); + set_next(NULL); + } +}; + +// statics +char SolarisAttachListener::_door_path[PATH_MAX+1]; +volatile bool SolarisAttachListener::_has_door_path; +int SolarisAttachListener::_door_descriptor = -1; +mutex_t SolarisAttachListener::_mutex; +sema_t SolarisAttachListener::_wakeup; +SolarisAttachOperation* SolarisAttachListener::_head = NULL; +SolarisAttachOperation* SolarisAttachListener::_tail = NULL; + +// Supporting class to help split a buffer into individual components +class ArgumentIterator : public StackObj { + private: + char* _pos; + char* _end; + public: + ArgumentIterator(char* arg_buffer, size_t arg_size) { + _pos = arg_buffer; + _end = _pos + arg_size - 1; + } + char* next() { + if (*_pos == '\0') { + return NULL; + } + char* res = _pos; + char* next_pos = strchr(_pos, '\0'); + if (next_pos < _end) { + next_pos++; + } + _pos = next_pos; + return res; + } +}; + +// Calls from the door function to check that the client credentials +// match this process. Returns 0 if credentials okay, otherwise -1. +static int check_credentials() { + door_cred_t cred_info; + + // get client credentials + if (door_cred(&cred_info) == -1) { + return -1; // unable to get them + } + + // get our euid/eguid (probably could cache these) + uid_t euid = geteuid(); + gid_t egid = getegid(); + + // check that the effective uid/gid matches - discuss this with Jeff. + if (cred_info.dc_euid == euid && cred_info.dc_egid == egid) { + return 0; // okay + } else { + return -1; // denied + } +} + + +// Parses the argument buffer to create an AttachOperation that we should +// enqueue to the attach listener. +// The buffer is expected to be formatted as follows: +// 00000 +// where is the version number (must be "1"), is the command +// name ("load, "datadump", ...) and is an argument. +// +static SolarisAttachOperation* create_operation(char* argp, size_t arg_size, int* err) { + // assume bad request until parsed + *err = SolarisAttachListener::ATTACH_ERROR_BADREQUEST; + + if (arg_size < 2 || argp[arg_size-1] != '\0') { + return NULL; // no ver or not null terminated + } + + // Use supporting class to iterate over the buffer + ArgumentIterator args(argp, arg_size); + + // First check the protocol version + char* ver = args.next(); + if (ver == NULL) { + return NULL; + } + if (atoi(ver) != SolarisAttachListener::ATTACH_PROTOCOL_VER) { + *err = SolarisAttachListener::ATTACH_ERROR_BADVERSION; + return NULL; + } + + // Get command name and create the operation + char* name = args.next(); + if (name == NULL || strlen(name) > AttachOperation::name_length_max) { + return NULL; + } + SolarisAttachOperation* op = new SolarisAttachOperation(name); + + // Iterate over the arguments + for (int i=0; iset_arg(i, NULL); + } else { + if (strlen(arg) > AttachOperation::arg_length_max) { + delete op; + return NULL; + } + op->set_arg(i, arg); + } + } + + // return operation + *err = 0; + return op; +} + +// create special operation to indicate all clients have detached +static SolarisAttachOperation* create_detachall_operation() { + return new SolarisAttachOperation(AttachOperation::detachall_operation_name()); +} + +// This is door function which the client executes via a door_call. +extern "C" { + static void enqueue_proc(void* cookie, char* argp, size_t arg_size, + door_desc_t* dt, uint_t n_desc) + { + int return_fd = -1; + SolarisAttachOperation* op = NULL; + + // no listener + jint res = 0; + if (!AttachListener::is_initialized()) { + // how did we get here? + debug_only(warning("door_call when not enabled")); + res = (jint)SolarisAttachListener::ATTACH_ERROR_INTERNAL; + } + + // check client credentials + if (res == 0) { + if (check_credentials() != 0) { + res = (jint)SolarisAttachListener::ATTACH_ERROR_DENIED; + } + } + + // if we are stopped at ShowMessageBoxOnError then maybe we can + // load a diagnostic library + if (res == 0 && is_error_reported()) { + if (ShowMessageBoxOnError) { + // TBD - support loading of diagnostic library here + } + + // can't enqueue operation after fatal error + res = (jint)SolarisAttachListener::ATTACH_ERROR_RESOURCE; + } + + // create the operation + if (res == 0) { + int err; + op = create_operation(argp, arg_size, &err); + res = (op == NULL) ? (jint)err : 0; + } + + // create a pair of connected sockets. Store the file descriptor + // for one end in the operation and enqueue the operation. The + // file descriptor for the other end wil be returned to the client. + if (res == 0) { + int s[2]; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, s) < 0) { + delete op; + res = (jint)SolarisAttachListener::ATTACH_ERROR_RESOURCE; + } else { + op->set_socket(s[0]); + return_fd = s[1]; + SolarisAttachListener::enqueue(op); + } + } + + // Return 0 (success) + file descriptor, or non-0 (error) + if (res == 0) { + door_desc_t desc; + desc.d_attributes = DOOR_DESCRIPTOR; + desc.d_data.d_desc.d_descriptor = return_fd; + door_return((char*)&res, sizeof(res), &desc, 1); + } else { + door_return((char*)&res, sizeof(res), NULL, 0); + } + } +} + +// atexit hook to detach the door and remove the file +extern "C" { + static void listener_cleanup() { + static int cleanup_done; + if (!cleanup_done) { + cleanup_done = 1; + int dd = SolarisAttachListener::door_descriptor(); + if (dd >= 0) { + ::close(dd); + } + if (SolarisAttachListener::has_door_path()) { + char* path = SolarisAttachListener::door_path(); + ::fdetach(path); + ::unlink(path); + } + } + } +} + +// Create the door +int SolarisAttachListener::create_door() { + char door_path[PATH_MAX+1]; + int fd, res; + + // register exit function + ::atexit(listener_cleanup); + + // create the door descriptor + int dd = ::door_create(enqueue_proc, NULL, 0); + if (dd < 0) { + return -1; + } + + sprintf(door_path, "%s/.java_pid%d", os::get_temp_directory(), os::current_process_id()); + RESTARTABLE(::creat(door_path, S_IRUSR | S_IWUSR), fd); + + if (fd == -1) { + debug_only(warning("attempt to create %s failed", door_path)); + return -1; + } + assert(fd >= 0, "bad file descriptor"); + set_door_path(door_path); + RESTARTABLE(::close(fd), res); + + // attach the door descriptor to the file + if ((res = ::fattach(dd, door_path)) == -1) { + // if busy then detach and try again + if (errno == EBUSY) { + ::fdetach(door_path); + res = ::fattach(dd, door_path); + } + if (res == -1) { + ::door_revoke(dd); + dd = -1; + } + } + if (dd >= 0) { + set_door_descriptor(dd); + } else { + // unable to create door or attach it to the file + ::unlink(door_path); + set_door_path(NULL); + return -1; + } + + return 0; +} + +// Initialization - create the door, locks, and other initialization +int SolarisAttachListener::init() { + if (create_door()) { + return -1; + } + + int status = os::Solaris::mutex_init(&_mutex); + assert_status(status==0, status, "mutex_init"); + + status = ::sema_init(&_wakeup, 0, NULL, NULL); + assert_status(status==0, status, "sema_init"); + + set_head(NULL); + set_tail(NULL); + + return 0; +} + +// Dequeue an operation +SolarisAttachOperation* SolarisAttachListener::dequeue() { + for (;;) { + int res; + + // wait for somebody to enqueue something + while ((res = ::sema_wait(wakeup())) == EINTR) + ; + if (res) { + warning("sema_wait failed: %s", strerror(res)); + return NULL; + } + + // lock the list + res = os::Solaris::mutex_lock(mutex()); + assert(res == 0, "mutex_lock failed"); + + // remove the head of the list + SolarisAttachOperation* op = head(); + if (op != NULL) { + set_head(op->next()); + if (head() == NULL) { + set_tail(NULL); + } + } + + // unlock + os::Solaris::mutex_unlock(mutex()); + + // if we got an operation when return it. + if (op != NULL) { + return op; + } + } +} + +// Enqueue an operation +void SolarisAttachListener::enqueue(SolarisAttachOperation* op) { + // lock list + int res = os::Solaris::mutex_lock(mutex()); + assert(res == 0, "mutex_lock failed"); + + // enqueue at tail + op->set_next(NULL); + if (head() == NULL) { + set_head(op); + } else { + tail()->set_next(op); + } + set_tail(op); + + // wakeup the attach listener + RESTARTABLE(::sema_post(wakeup()), res); + assert(res == 0, "sema_post failed"); + + // unlock + os::Solaris::mutex_unlock(mutex()); +} + + +// support function - writes the (entire) buffer to a socket +static int write_fully(int s, char* buf, int len) { + do { + int n = ::write(s, buf, len); + if (n == -1) { + if (errno != EINTR) return -1; + } else { + buf += n; + len -= n; + } + } + while (len > 0); + return 0; +} + +// Complete an operation by sending the operation result and any result +// output to the client. At this time the socket is in blocking mode so +// potentially we can block if there is a lot of data and the client is +// non-responsive. For most operations this is a non-issue because the +// default send buffer is sufficient to buffer everything. In the future +// if there are operations that involves a very big reply then it the +// socket could be made non-blocking and a timeout could be used. + +void SolarisAttachOperation::complete(jint res, bufferedStream* st) { + if (this->socket() >= 0) { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + // write operation result + char msg[32]; + sprintf(msg, "%d\n", res); + int rc = write_fully(this->socket(), msg, strlen(msg)); + + // write any result data + if (rc == 0) { + write_fully(this->socket(), (char*) st->base(), st->size()); + ::shutdown(this->socket(), 2); + } + + // close socket and we're done + RESTARTABLE(::close(this->socket()), rc); + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); + } + delete this; +} + + +// AttachListener functions + +AttachOperation* AttachListener::dequeue() { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + AttachOperation* op = SolarisAttachListener::dequeue(); + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); + + return op; +} + +int AttachListener::pd_init() { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() + + int ret_code = SolarisAttachListener::init(); + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); + + return ret_code; +} + +// Attach Listener is started lazily except in the case when +// +ReduseSignalUsage is used +bool AttachListener::init_at_startup() { + if (ReduceSignalUsage) { + return true; + } else { + return false; + } +} + +// If the file .attach_pid exists in the working directory +// or /tmp then this is the trigger to start the attach mechanism +bool AttachListener::is_init_trigger() { + if (init_at_startup() || is_initialized()) { + return false; // initialized at startup or already initialized + } + char fn[32]; + sprintf(fn, ".attach_pid%d", os::current_process_id()); + int ret; + struct stat64 st; + RESTARTABLE(::stat64(fn, &st), ret); + if (ret == -1) { + sprintf(fn, "/tmp/.attach_pid%d", os::current_process_id()); + RESTARTABLE(::stat64(fn, &st), ret); + } + if (ret == 0) { + // simple check to avoid starting the attach mechanism when + // a bogus user creates the file + if (st.st_uid == geteuid()) { + init(); + return true; + } + } + return false; +} + +// if VM aborts then detach/cleanup +void AttachListener::abort() { + listener_cleanup(); +} + +void AttachListener::pd_data_dump() { + os::signal_notify(SIGQUIT); +} + +static jint enable_dprobes(AttachOperation* op, outputStream* out) { + const char* probe = op->arg(0); + if (probe == NULL || probe[0] == '\0') { + out->print_cr("No probe specified"); + return JNI_ERR; + } else { + int probe_typess = atoi(probe); + if (errno) { + out->print_cr("invalid probe type"); + return JNI_ERR; + } else { + DTrace::enable_dprobes(probe_typess); + return JNI_OK; + } + } +} + +// platform specific operations table +static AttachOperationFunctionInfo funcs[] = { + { "enabledprobes", enable_dprobes }, + { NULL, NULL } +}; + +AttachOperationFunctionInfo* AttachListener::pd_find_operation(const char* name) { + int i; + for (i = 0; funcs[i].name != NULL; i++) { + if (strcmp(funcs[i].name, name) == 0) { + return &funcs[i]; + } + } + return NULL; +} + +// Solaris specific global flag set. Currently, we support only +// changing ExtendedDTraceProbes flag. +jint AttachListener::pd_set_flag(AttachOperation* op, outputStream* out) { + const char* name = op->arg(0); + assert(name != NULL, "flag name should not be null"); + bool flag = true; + const char* arg1; + if ((arg1 = op->arg(1)) != NULL) { + flag = (atoi(arg1) != 0); + if (errno) { + out->print_cr("flag value has to be an integer"); + return JNI_ERR; + } + } + + if (strcmp(name, "ExtendedDTraceProbes") != 0) { + out->print_cr("flag '%s' cannot be changed", name); + return JNI_ERR; + } + + DTrace::set_extended_dprobes(flag); + return JNI_OK; +} + +void AttachListener::pd_detachall() { + DTrace::detach_all_clients(); +} diff --git a/hotspot/src/os/solaris/vm/c1_globals_solaris.hpp b/hotspot/src/os/solaris/vm/c1_globals_solaris.hpp new file mode 100644 index 00000000000..84dd36c19da --- /dev/null +++ b/hotspot/src/os/solaris/vm/c1_globals_solaris.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for operating system dependent flags used by the +// client compiler. (see c1_globals.hpp) +// diff --git a/hotspot/src/os/solaris/vm/c2_globals_solaris.hpp b/hotspot/src/os/solaris/vm/c2_globals_solaris.hpp new file mode 100644 index 00000000000..4102058ae77 --- /dev/null +++ b/hotspot/src/os/solaris/vm/c2_globals_solaris.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for operating system dependent flags used by the +// server compiler. (see c2_globals.hpp) +// diff --git a/hotspot/src/os/solaris/vm/chaitin_solaris.cpp b/hotspot/src/os/solaris/vm/chaitin_solaris.cpp new file mode 100644 index 00000000000..8e7067f6149 --- /dev/null +++ b/hotspot/src/os/solaris/vm/chaitin_solaris.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_chaitin_solaris.cpp.incl" + +void PhaseRegAlloc::pd_preallocate_hook() { + // no action +} + +#ifdef ASSERT +void PhaseRegAlloc::pd_postallocate_verify_hook() { + // no action +} +#endif + + +//Reconciliation History +// 1.1 99/02/12 15:35:26 chaitin_win32.cpp +// 1.2 99/02/18 15:38:56 chaitin_win32.cpp +// 1.4 99/03/09 10:37:48 chaitin_win32.cpp +// 1.6 99/03/25 11:07:44 chaitin_win32.cpp +// 1.8 99/06/22 16:38:58 chaitin_win32.cpp +//End diff --git a/hotspot/src/os/solaris/vm/globals_solaris.hpp b/hotspot/src/os/solaris/vm/globals_solaris.hpp new file mode 100644 index 00000000000..8f00adbe2a3 --- /dev/null +++ b/hotspot/src/os/solaris/vm/globals_solaris.hpp @@ -0,0 +1,48 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Defines Solaris specific flags. They are not available on other platforms. +// +#define RUNTIME_OS_FLAGS(develop, develop_pd, product, product_pd, diagnostic, notproduct) \ + \ + product(bool, UseISM, false, \ + "Use Intimate Shared Memory (Solaris Only)") \ + \ + product(bool, UsePermISM, false, \ + "Obsolete flag for compatibility (same as UseISM)") \ + \ + product(bool, UseMPSS, true, \ + "Use Multiple Page Size Support (Solaris 9 Only)") \ + \ + product(bool, UseExtendedFileIO, true, \ + "Enable workaround for limitations of stdio FILE structure") + +// +// Defines Solaris-specific default values. The flags are available on all +// platforms, but they may have different default values on other platforms. +// +define_pd_global(bool, UseLargePages, true); +define_pd_global(bool, UseOSErrorReporting, false); +define_pd_global(bool, UseThreadPriorities, false); diff --git a/hotspot/src/os/solaris/vm/hpi_solaris.cpp b/hotspot/src/os/solaris/vm/hpi_solaris.cpp new file mode 100644 index 00000000000..48478a5be66 --- /dev/null +++ b/hotspot/src/os/solaris/vm/hpi_solaris.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_hpi_solaris.cpp.incl" + +# include +# include + +typedef jint (JNICALL *init_t)(GetInterfaceFunc *, void *); + +void hpi::initialize_get_interface(vm_calls_t *callbacks) +{ + char buf[JVM_MAXPATHLEN]; + void *hpi_handle; + GetInterfaceFunc& getintf = _get_interface; + jint (JNICALL * DLL_Initialize)(GetInterfaceFunc *, void *); + + if (HPILibPath && HPILibPath[0]) { + strncpy(buf, HPILibPath, JVM_MAXPATHLEN - 1); + buf[JVM_MAXPATHLEN - 1] = '\0'; + } else { + const char *thread_type = "native_threads"; + + os::jvm_path(buf, JVM_MAXPATHLEN); + +#ifdef PRODUCT + const char * hpi_lib = "/libhpi.so"; +#else + char * ptr = strrchr(buf, '/'); + assert(strstr(ptr, "/libjvm") == ptr, "invalid library name"); + const char * hpi_lib = strstr(ptr, "_g") ? "/libhpi_g.so" : "/libhpi.so"; +#endif + + *(strrchr(buf, '/')) = '\0'; /* get rid of /libjvm.so */ + char* p = strrchr(buf, '/'); + if (p != NULL) p[1] = '\0'; /* get rid of hotspot */ + strcat(buf, thread_type); + strcat(buf, hpi_lib); + } + /* we use RTLD_NOW because of bug 4032715 */ + if (TraceHPI) tty->print_cr("Loading HPI %s ", buf); + hpi_handle = dlopen(buf, RTLD_NOW); + if (hpi_handle == NULL) { + if (TraceHPI) tty->print_cr("HPI dlopen failed: %s", dlerror()); + return; + } + DLL_Initialize = CAST_TO_FN_PTR(jint (JNICALL *)(GetInterfaceFunc *, void *), + dlsym(hpi_handle, "DLL_Initialize")); + if (TraceHPI && DLL_Initialize == NULL) tty->print_cr("HPI dlsym of DLL_Initialize failed: %s", dlerror()); + if (DLL_Initialize == NULL || + (*DLL_Initialize)(&getintf, callbacks) < 0) { + if (TraceHPI) tty->print_cr("HPI DLL_Initialize failed"); + return; + } + if (TraceHPI) tty->print_cr("HPI loaded successfully"); +} diff --git a/hotspot/src/os/solaris/vm/hpi_solaris.hpp b/hotspot/src/os/solaris/vm/hpi_solaris.hpp new file mode 100644 index 00000000000..cd5f2eab6ac --- /dev/null +++ b/hotspot/src/os/solaris/vm/hpi_solaris.hpp @@ -0,0 +1,245 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Parts of the HPI interface for which the HotSparc does not use the +// HPI (because the interruptible IO mechanims used are different). +// + +#include +#include +#include +#include +#include +#include + +// HPI_FileInterface + +// Many system calls can be interrupted by signals and must be restarted. +// Restart support was added without disturbing the extent of thread +// interruption support. + +inline int hpi::close(int fd) { + RESTARTABLE_RETURN_INT(::close(fd)); +} + +inline size_t hpi::read(int fd, void *buf, unsigned int nBytes) { + INTERRUPTIBLE_RETURN_INT(::read(fd, buf, nBytes), os::Solaris::clear_interrupted); +} + +inline size_t hpi::write(int fd, const void *buf, unsigned int nBytes) { + INTERRUPTIBLE_RETURN_INT(::write(fd, buf, nBytes), os::Solaris::clear_interrupted); +} + + +// HPI_SocketInterface + +inline int hpi::socket_close(int fd) { + RESTARTABLE_RETURN_INT(::close(fd)); +} + +inline int hpi::socket(int domain, int type, int protocol) { + return ::socket(domain, type, protocol); +} + +inline int hpi::recv(int fd, char *buf, int nBytes, int flags) { + INTERRUPTIBLE_RETURN_INT(::recv(fd, buf, nBytes, flags), os::Solaris::clear_interrupted); +} + +inline int hpi::send(int fd, char *buf, int nBytes, int flags) { + INTERRUPTIBLE_RETURN_INT(::send(fd, buf, nBytes, flags), os::Solaris::clear_interrupted); +} + +// As both poll and select can be interrupted by signals, we have to be +// prepared to restart the system call after updating the timeout, unless +// a poll() is done with timeout == -1, in which case we repeat with this +// "wait forever" value. + +inline int hpi::timeout(int fd, long timeout) { + int res; + struct timeval t; + julong prevtime, newtime; + static const char* aNull = 0; + + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN; + + gettimeofday(&t, &aNull); + prevtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; + + for(;;) { + INTERRUPTIBLE_NORESTART(::poll(&pfd, 1, timeout), res, os::Solaris::clear_interrupted); + if(res == OS_ERR && errno == EINTR) { + if(timeout != -1) { + gettimeofday(&t, &aNull); + newtime = ((julong)t.tv_sec * 1000) + t.tv_usec /1000; + timeout -= newtime - prevtime; + if(timeout <= 0) + return OS_OK; + prevtime = newtime; + } + } else + return res; + } +} + +inline int hpi::listen(int fd, int count) { + if (fd < 0) + return OS_ERR; + + return ::listen(fd, count); +} + +inline int +hpi::connect(int fd, struct sockaddr *him, int len) { + do { + int _result; + INTERRUPTIBLE_NORESTART(::connect(fd, him, len), _result, + os::Solaris::clear_interrupted); + + // Depending on when thread interruption is reset, _result could be + // one of two values when errno == EINTR + + if (((_result == OS_INTRPT) || (_result == OS_ERR)) && (errno == EINTR)) { + /* restarting a connect() changes its errno semantics */ + INTERRUPTIBLE(::connect(fd, him, len), _result, + os::Solaris::clear_interrupted); + /* undo these changes */ + if (_result == OS_ERR) { + if (errno == EALREADY) errno = EINPROGRESS; /* fall through */ + else if (errno == EISCONN) { errno = 0; return OS_OK; } + } + } + return _result; + } while(false); +} + +inline int hpi::accept(int fd, struct sockaddr *him, int *len) { + if (fd < 0) + return OS_ERR; + INTERRUPTIBLE_RETURN_INT((int)::accept(fd, him, (socklen_t*) len), os::Solaris::clear_interrupted); +} + +inline int hpi::recvfrom(int fd, char *buf, int nBytes, int flags, + sockaddr *from, int *fromlen) { + //%%note jvm_r11 + INTERRUPTIBLE_RETURN_INT((int)::recvfrom(fd, buf, nBytes, (unsigned int) flags, from, (socklen_t *)fromlen), os::Solaris::clear_interrupted); +} + +inline int hpi::sendto(int fd, char *buf, int len, int flags, + struct sockaddr *to, int tolen) { + //%%note jvm_r11 + INTERRUPTIBLE_RETURN_INT((int)::sendto(fd, buf, len, (unsigned int) flags, to, tolen),os::Solaris::clear_interrupted); +} + +inline int hpi::socket_available(int fd, jint *pbytes) { + if (fd < 0) + return OS_OK; + + int ret; + + RESTARTABLE(::ioctl(fd, FIONREAD, pbytes), ret); + + //%% note ioctl can return 0 when successful, JVM_SocketAvailable + // is expected to return 0 on failure and 1 on success to the jdk. + + return (ret == OS_ERR) ? 0 : 1; +} + + +/* +HPIDECL(socket_shutdown, "socket_shutdown", _socket, SocketShutdown, + int, "%d", + (int fd, int howto), + ("fd = %d, howto = %d", fd, howto), + (fd, howto)); + */ +inline int hpi::socket_shutdown(int fd, int howto){ + return ::shutdown(fd, howto); +} + +/* +HPIDECL(bind, "bind", _socket, Bind, + int, "%d", + (int fd, struct sockaddr *him, int len), + ("fd = %d, him = %p, len = %d", + fd, him, len), + (fd, him, len)); +*/ +inline int hpi::bind(int fd, struct sockaddr *him, int len){ + INTERRUPTIBLE_RETURN_INT_NORESTART(::bind(fd, him, len),os::Solaris::clear_interrupted); +} + +/* +HPIDECL(get_sock_name, "get_sock_name", _socket, GetSocketName, + int, "%d", + (int fd, struct sockaddr *him, int *len), + ("fd = %d, him = %p, len = %p", + fd, him, len), + (fd, him, len)); + */ +inline int hpi::get_sock_name(int fd, struct sockaddr *him, int *len){ + return ::getsockname(fd, him, (socklen_t*) len); +} + +/* +HPIDECL(get_host_name, "get_host_name", _socket, GetHostName, int, "%d", + (char *hostname, int namelen), + ("hostname = %p, namelen = %d", + hostname, namelen), + (hostname, namelen)); + */ +inline int hpi::get_host_name(char* name, int namelen){ + return ::gethostname(name, namelen); +} + +/* +HPIDECL(get_sock_opt, "get_sock_opt", _socket, SocketGetOption, int, "%d", + (int fd, int level, int optname, char *optval, int* optlen), + ("fd = %d, level = %d, optname = %d, optval = %p, optlen = %p", + fd, level, optname, optval, optlen), + (fd, level, optname, optval, optlen)); + */ +inline int hpi::get_sock_opt(int fd, int level, int optname, + char *optval, int* optlen){ + return ::getsockopt(fd, level, optname, optval, (socklen_t*) optlen); +} + +/* +HPIDECL(set_sock_opt, "set_sock_opt", _socket, SocketSetOption, int, "%d", + (int fd, int level, int optname, const char *optval, int optlen), + ("fd = %d, level = %d, optname = %d, optval = %p, optlen = %d", + fd, level, optname, optval, optlen), + (fd, level, optname, optval, optlen)); + */ +inline int hpi::set_sock_opt(int fd, int level, int optname, + const char *optval, int optlen){ + return ::setsockopt(fd, level, optname, optval, optlen); +} + +//Reconciliation History +// 1.3 98/10/21 18:17:14 hpi_win32.hpp +// 1.6 99/06/28 11:01:36 hpi_win32.hpp +//End diff --git a/hotspot/src/os/solaris/vm/interfaceSupport_solaris.hpp b/hotspot/src/os/solaris/vm/interfaceSupport_solaris.hpp new file mode 100644 index 00000000000..bc9a83b9df6 --- /dev/null +++ b/hotspot/src/os/solaris/vm/interfaceSupport_solaris.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Contains inlined functions for class InterfaceSupport + +static inline void serialize_memory(JavaThread *thread) { + os::write_memory_serialize_page(thread); +} diff --git a/hotspot/src/os/solaris/vm/jsig.c b/hotspot/src/os/solaris/vm/jsig.c new file mode 100644 index 00000000000..56b024487f5 --- /dev/null +++ b/hotspot/src/os/solaris/vm/jsig.c @@ -0,0 +1,273 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* CopyrightVersion 1.2 */ + +/* This is a special library that should be loaded before libc & + * libthread to interpose the signal handler installation functions: + * sigaction(), signal(), sigset(). + * Used for signal-chaining. See RFE 4381843. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "jvm_solaris.h" + +#define bool int +#define true 1 +#define false 0 + +static struct sigaction *sact = (struct sigaction *)NULL; /* saved signal handlers */ +static sigset_t jvmsigs; + +/* used to synchronize the installation of signal handlers */ +static mutex_t mutex = DEFAULTMUTEX; +static cond_t cond = DEFAULTCV; +static thread_t tid = 0; + +typedef void (*sa_handler_t)(int); +typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); +typedef sa_handler_t (*signal_t)(int, sa_handler_t); +typedef int (*sigaction_t)(int, const struct sigaction *, struct sigaction *); + +static signal_t os_signal = 0; /* os's version of signal()/sigset() */ +static sigaction_t os_sigaction = 0; /* os's version of sigaction() */ + +static bool jvm_signal_installing = false; +static bool jvm_signal_installed = false; + + +/* assume called within signal_lock */ +static void allocate_sact() { + size_t maxsignum; + maxsignum = SIGRTMAX; + if (sact == NULL) { + sact = (struct sigaction *)malloc((maxsignum+1) * (size_t)sizeof(struct sigaction)); + memset(sact, 0, (maxsignum+1) * (size_t)sizeof(struct sigaction)); + } + + if (sact == NULL) { + printf("%s\n", "libjsig.so unable to allocate memory"); + exit(0); + } + + sigemptyset(&jvmsigs); +} + +static void signal_lock() { + mutex_lock(&mutex); + /* When the jvm is installing its set of signal handlers, threads + * other than the jvm thread should wait */ + if (jvm_signal_installing) { + if (tid != thr_self()) { + cond_wait(&cond, &mutex); + } + } +} + +static void signal_unlock() { + mutex_unlock(&mutex); +} + +static sa_handler_t call_os_signal(int sig, sa_handler_t disp, + bool is_sigset) { + if (os_signal == NULL) { + if (!is_sigset) { + os_signal = (signal_t)dlsym(RTLD_NEXT, "signal"); + } else { + os_signal = (signal_t)dlsym(RTLD_NEXT, "sigset"); + } + if (os_signal == NULL) { + printf("%s\n", dlerror()); + exit(0); + } + } + return (*os_signal)(sig, disp); +} + +static void save_signal_handler(int sig, sa_handler_t disp, bool is_sigset) { + sigset_t set; + if (sact == NULL) { + allocate_sact(); + } + sact[sig].sa_handler = disp; + sigemptyset(&set); + sact[sig].sa_mask = set; + if (!is_sigset) { + sact[sig].sa_flags = SA_NODEFER; + if (sig != SIGILL && sig != SIGTRAP && sig != SIGPWR) { + sact[sig].sa_flags |= SA_RESETHAND; + } + } else { + sact[sig].sa_flags = 0; + } +} + +static sa_handler_t set_signal(int sig, sa_handler_t disp, bool is_sigset) { + sa_handler_t oldhandler; + bool sigblocked; + + signal_lock(); + if (sact == NULL) { + allocate_sact(); + } + + if (jvm_signal_installed && sigismember(&jvmsigs, sig)) { + /* jvm has installed its signal handler for this signal. */ + /* Save the handler. Don't really install it. */ + if (is_sigset) { + /* We won't honor the SIG_HOLD request to change the signal mask */ + sigblocked = sigismember(&(sact[sig].sa_mask), sig); + } + oldhandler = sact[sig].sa_handler; + save_signal_handler(sig, disp, is_sigset); + + if (is_sigset && sigblocked) { + oldhandler = SIG_HOLD; + } + + signal_unlock(); + return oldhandler; + } else if (jvm_signal_installing) { + /* jvm is installing its signal handlers. Install the new + * handlers and save the old ones. jvm uses sigaction(). + * Leave the piece here just in case. */ + oldhandler = call_os_signal(sig, disp, is_sigset); + save_signal_handler(sig, oldhandler, is_sigset); + + /* Record the signals used by jvm */ + sigaddset(&jvmsigs, sig); + + signal_unlock(); + return oldhandler; + } else { + /* jvm has no relation with this signal (yet). Install the + * the handler. */ + oldhandler = call_os_signal(sig, disp, is_sigset); + + signal_unlock(); + return oldhandler; + } +} + +sa_handler_t signal(int sig, sa_handler_t disp) { + return set_signal(sig, disp, false); +} + +sa_handler_t sigset(int sig, sa_handler_t disp) { + return set_signal(sig, disp, true); +} + +static int call_os_sigaction(int sig, const struct sigaction *act, + struct sigaction *oact) { + if (os_sigaction == NULL) { + os_sigaction = (sigaction_t)dlsym(RTLD_NEXT, "sigaction"); + if (os_sigaction == NULL) { + printf("%s\n", dlerror()); + exit(0); + } + } + return (*os_sigaction)(sig, act, oact); +} + +int sigaction(int sig, const struct sigaction *act, struct sigaction *oact) { + int res; + struct sigaction oldAct; + + signal_lock(); + + if (sact == NULL ) { + allocate_sact(); + } + if (jvm_signal_installed && sigismember(&jvmsigs, sig)) { + /* jvm has installed its signal handler for this signal. */ + /* Save the handler. Don't really install it. */ + if (oact != NULL) { + *oact = sact[sig]; + } + if (act != NULL) { + sact[sig] = *act; + } + + signal_unlock(); + return 0; + } else if (jvm_signal_installing) { + /* jvm is installing its signal handlers. Install the new + * handlers and save the old ones. */ + res = call_os_sigaction(sig, act, &oldAct); + sact[sig] = oldAct; + if (oact != NULL) { + *oact = oldAct; + } + + /* Record the signals used by jvm */ + sigaddset(&jvmsigs, sig); + + signal_unlock(); + return res; + } else { + /* jvm has no relation with this signal (yet). Install the + * the handler. */ + res = call_os_sigaction(sig, act, oact); + + signal_unlock(); + return res; + } +} + +/* The four functions for the jvm to call into */ +void JVM_begin_signal_setting() { + signal_lock(); + jvm_signal_installing = true; + tid = thr_self(); + signal_unlock(); +} + +void JVM_end_signal_setting() { + signal_lock(); + jvm_signal_installed = true; + jvm_signal_installing = false; + cond_broadcast(&cond); + signal_unlock(); +} + +struct sigaction *JVM_get_signal_action(int sig) { + if (sact == NULL) { + allocate_sact(); + } + /* Does race condition make sense here? */ + if (sigismember(&jvmsigs, sig)) { + return &sact[sig]; + } + return NULL; +} + +int JVM_get_libjsig_version() { + return JSIG_VERSION_1_4_1; +} diff --git a/hotspot/src/os/solaris/vm/jvm_solaris.cpp b/hotspot/src/os/solaris/vm/jvm_solaris.cpp new file mode 100644 index 00000000000..b1b79923520 --- /dev/null +++ b/hotspot/src/os/solaris/vm/jvm_solaris.cpp @@ -0,0 +1,148 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_jvm_solaris.cpp.incl" + +#include + +// sun.misc.Signal /////////////////////////////////////////////////////////// +// Signal code is mostly copied from classic vm, signals_md.c 1.4 98/08/23 +/* + * This function is included primarily as a debugging aid. If Java is + * running in a console window, then pressing will cause + * the current state of all active threads and monitors to be written + * to the console window. + */ + +JVM_ENTRY_NO_ENV(void*, JVM_RegisterSignal(jint sig, void* handler)) + // Copied from classic vm + // signals_md.c 1.4 98/08/23 + void* newHandler = handler == (void *)2 + ? os::user_handler() + : handler; + switch (sig) { + /* The following are already used by the VM. */ + case SIGFPE: + case SIGILL: + case SIGSEGV: + + /* The following signal is used by the VM to dump thread stacks unless + ReduceSignalUsage is set, in which case the user is allowed to set + his own _native_ handler for this signal; thus, in either case, + we do not allow JVM_RegisterSignal to change the handler. */ + case BREAK_SIGNAL: + return (void *)-1; + + /* The following signals are used for Shutdown Hooks support. However, if + ReduceSignalUsage (-Xrs) is set, Shutdown Hooks must be invoked via + System.exit(), Java is not allowed to use these signals, and the the + user is allowed to set his own _native_ handler for these signals and + invoke System.exit() as needed. Terminator.setup() is avoiding + registration of these signals when -Xrs is present. + - If the HUP signal is ignored (from the nohup) command, then Java + is not allowed to use this signal. + */ + case SHUTDOWN1_SIGNAL: + case SHUTDOWN2_SIGNAL: + case SHUTDOWN3_SIGNAL: + if (ReduceSignalUsage) return (void*)-1; + if (os::Solaris::is_sig_ignored(sig)) return (void*)1; + } + + /* Check parameterized signals. Don't allow sharing of our interrupt signal */ + if (sig == os::Solaris::SIGinterrupt()) { + return (void *)-1; + } + + void* oldHandler = os::signal(sig, newHandler); + if (oldHandler == os::user_handler()) { + return (void *)2; + } else { + return oldHandler; + } +JVM_END + + +JVM_ENTRY_NO_ENV(jboolean, JVM_RaiseSignal(jint sig)) + if (ReduceSignalUsage) { + // do not allow SHUTDOWN1_SIGNAL,SHUTDOWN2_SIGNAL,SHUTDOWN3_SIGNAL, + // BREAK_SIGNAL to be raised when ReduceSignalUsage is set, since + // no handler for them is actually registered in JVM or via + // JVM_RegisterSignal. + if (sig == SHUTDOWN1_SIGNAL || sig == SHUTDOWN2_SIGNAL || + sig == SHUTDOWN3_SIGNAL || sig == BREAK_SIGNAL) { + return JNI_FALSE; + } + } + else if ((sig == SHUTDOWN1_SIGNAL || sig == SHUTDOWN2_SIGNAL || + sig == SHUTDOWN3_SIGNAL) && os::Solaris::is_sig_ignored(sig)) { + // do not allow SHUTDOWN1_SIGNAL to be raised when SHUTDOWN1_SIGNAL + // is ignored, since no handler for them is actually registered in JVM + // or via JVM_RegisterSignal. + // This also applies for SHUTDOWN2_SIGNAL and SHUTDOWN3_SIGNAL + return JNI_FALSE; + } + + os::signal_raise(sig); + return JNI_TRUE; +JVM_END + + +/* + All the defined signal names for Solaris are defined by str2sig(). + + NOTE that not all of these names are accepted by our Java implementation + + Via an existing claim by the VM, sigaction restrictions, or + the "rules of Unix" some of these names will be rejected at runtime. + For example the VM sets up to handle USR1, sigaction returns EINVAL for + CANCEL, and Solaris simply doesn't allow catching of KILL. + + Here are the names currently accepted by a user of sun.misc.Signal with + 1.4.1 (ignoring potential interaction with use of chaining, etc): + + HUP, INT, TRAP, IOT, ABRT, EMT, BUS, SYS, PIPE, ALRM, TERM, USR2, + CLD, CHLD, PWR, WINCH, URG, POLL, IO, TSTP, CONT, TTIN, TTOU, VTALRM, + PROF, XCPU, XFSZ, FREEZE, THAW, LOST +*/ + +JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) + + int sig; + + /* return the named signal's number */ + + if(str2sig(name, &sig)) + return -1; + else + return sig; + +JVM_END + + +//Reconciliation History +// 1.4 98/10/07 13:39:41 jvm_win32.cpp +// 1.6 99/06/22 16:39:00 jvm_win32.cpp +//End diff --git a/hotspot/src/os/solaris/vm/jvm_solaris.h b/hotspot/src/os/solaris/vm/jvm_solaris.h new file mode 100644 index 00000000000..b7eae3c8238 --- /dev/null +++ b/hotspot/src/os/solaris/vm/jvm_solaris.h @@ -0,0 +1,100 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* +// HotSpot integration note: +// +// This is derived from the JDK classic file: +// "$JDK/src/solaris/javavm/export/jvm_md.h":15 (ver. 1.10 98/04/22) +// All local includes have been commented out. +*/ + + +#ifndef JVM_MD_H +#define JVM_MD_H + +/* + * This file is currently collecting system-specific dregs for the + * JNI conversion, which should be sorted out later. + */ + +#include /* For DIR */ +#include /* For MAXPATHLEN */ +#include /* For F_OK, R_OK, W_OK */ +#include /* for intptr_t types (64 Bit cleanliness) */ + +#define JNI_ONLOAD_SYMBOLS {"JNI_OnLoad"} +#define JNI_ONUNLOAD_SYMBOLS {"JNI_OnUnload"} +#define JVM_ONLOAD_SYMBOLS {"JVM_OnLoad"} +#define AGENT_ONLOAD_SYMBOLS {"Agent_OnLoad"} +#define AGENT_ONUNLOAD_SYMBOLS {"Agent_OnUnload"} +#define AGENT_ONATTACH_SYMBOLS {"Agent_OnAttach"} + +#define JNI_LIB_PREFIX "lib" +#define JNI_LIB_SUFFIX ".so" + +#define JVM_MAXPATHLEN MAXPATHLEN + +#define JVM_R_OK R_OK +#define JVM_W_OK W_OK +#define JVM_X_OK X_OK +#define JVM_F_OK F_OK + +/* + * File I/O + */ + +#include +#include +#include +#include + +/* O Flags */ + +#define JVM_O_RDONLY O_RDONLY +#define JVM_O_WRONLY O_WRONLY +#define JVM_O_RDWR O_RDWR +#define JVM_O_O_APPEND O_APPEND +#define JVM_O_EXCL O_EXCL +#define JVM_O_CREAT O_CREAT + + +/* Signal definitions */ + +#define BREAK_SIGNAL SIGQUIT /* Thread dumping support. */ +#define INTERRUPT_SIGNAL SIGUSR1 /* Interruptible I/O support. */ +#define ASYNC_SIGNAL SIGUSR2 /* Watcher & async err support. */ +#define SHUTDOWN1_SIGNAL SIGHUP /* Shutdown Hooks support. */ +#define SHUTDOWN2_SIGNAL SIGINT +#define SHUTDOWN3_SIGNAL SIGTERM +/* alternative signals used with -XX:+UseAltSigs (or for backward + compatibility with 1.2, -Xusealtsigs) flag. Chosen to be + unlikely to conflict with applications embedding the vm */ +#define ALT_INTERRUPT_SIGNAL (SIGRTMIN + SIGRTMAX)/2 /* alternate intio signal */ +#define ALT_ASYNC_SIGNAL ALT_INTERRUPT_SIGNAL+1 /* alternate async signal */ + +/* With 1.4.1 libjsig added versioning: used in os_solaris.cpp and jsig.c */ +#define JSIG_VERSION_1_4_1 0x30140100 + +#endif /* JVM_MD_H */ diff --git a/hotspot/src/os/solaris/vm/mutex_solaris.cpp b/hotspot/src/os/solaris/vm/mutex_solaris.cpp new file mode 100644 index 00000000000..38850102d20 --- /dev/null +++ b/hotspot/src/os/solaris/vm/mutex_solaris.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_mutex_solaris.cpp.incl" + +// Solaris-specific include, therefore not in includeDB_* +# include "os_share_solaris.hpp" + +// put OS-includes here +# include diff --git a/hotspot/src/os/solaris/vm/mutex_solaris.inline.hpp b/hotspot/src/os/solaris/vm/mutex_solaris.inline.hpp new file mode 100644 index 00000000000..2f5d3a8a448 --- /dev/null +++ b/hotspot/src/os/solaris/vm/mutex_solaris.inline.hpp @@ -0,0 +1,23 @@ +/* + * Copyright 1998-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ diff --git a/hotspot/src/os/solaris/vm/objectMonitor_solaris.cpp b/hotspot/src/os/solaris/vm/objectMonitor_solaris.cpp new file mode 100644 index 00000000000..155e7e40811 --- /dev/null +++ b/hotspot/src/os/solaris/vm/objectMonitor_solaris.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ diff --git a/hotspot/src/os/solaris/vm/objectMonitor_solaris.hpp b/hotspot/src/os/solaris/vm/objectMonitor_solaris.hpp new file mode 100644 index 00000000000..ce0ce5ee988 --- /dev/null +++ b/hotspot/src/os/solaris/vm/objectMonitor_solaris.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: diff --git a/hotspot/src/os/solaris/vm/objectMonitor_solaris.inline.hpp b/hotspot/src/os/solaris/vm/objectMonitor_solaris.inline.hpp new file mode 100644 index 00000000000..155e7e40811 --- /dev/null +++ b/hotspot/src/os/solaris/vm/objectMonitor_solaris.inline.hpp @@ -0,0 +1,23 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ diff --git a/hotspot/src/os/solaris/vm/osThread_solaris.cpp b/hotspot/src/os/solaris/vm/osThread_solaris.cpp new file mode 100644 index 00000000000..702e6d4e2d5 --- /dev/null +++ b/hotspot/src/os/solaris/vm/osThread_solaris.cpp @@ -0,0 +1,224 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// do not include precompiled header file +# include "incls/_osThread_solaris.cpp.incl" +# include + + // *************************************************************** + // Platform dependent initialization and cleanup + // *************************************************************** + +void OSThread::pd_initialize() { + _thread_id = 0; + sigemptyset(&_caller_sigmask); + + _current_callback = NULL; + _current_callback_lock = VM_Version::supports_compare_and_exchange() ? NULL + : new Mutex(Mutex::suspend_resume, "Callback_lock", true); + + _saved_interrupt_thread_state = _thread_new; + _vm_created_thread = false; +} + +void OSThread::pd_destroy() { +} + +// Synchronous interrupt support +// +// _current_callback == NULL no pending callback +// == 1 callback_in_progress +// == other value pointer to the pending callback +// + +// CAS on v8 is implemented by using a global atomic_memory_operation_lock, +// which is shared by other atomic functions. It is OK for normal uses, but +// dangerous if used after some thread is suspended or if used in signal +// handlers. Instead here we use a special per-thread lock to synchronize +// updating _current_callback if we are running on v8. Note in general trying +// to grab locks after a thread is suspended is not safe, but it is safe for +// updating _current_callback, because synchronous interrupt callbacks are +// currently only used in: +// 1. GetThreadPC_Callback - used by WatcherThread to profile VM thread +// There is no overlap between the callbacks, which means we won't try to +// grab a thread's sync lock after the thread has been suspended while holding +// the same lock. + +// used after a thread is suspended +static intptr_t compare_and_exchange_current_callback ( + intptr_t callback, intptr_t *addr, intptr_t compare_value, Mutex *sync) { + if (VM_Version::supports_compare_and_exchange()) { + return Atomic::cmpxchg_ptr(callback, addr, compare_value); + } else { + MutexLockerEx(sync, Mutex::_no_safepoint_check_flag); + if (*addr == compare_value) { + *addr = callback; + return compare_value; + } else { + return callback; + } + } +} + +// used in signal handler +static intptr_t exchange_current_callback(intptr_t callback, intptr_t *addr, Mutex *sync) { + if (VM_Version::supports_compare_and_exchange()) { + return Atomic::xchg_ptr(callback, addr); + } else { + MutexLockerEx(sync, Mutex::_no_safepoint_check_flag); + intptr_t cb = *addr; + *addr = callback; + return cb; + } +} + +// one interrupt at a time. spin if _current_callback != NULL +int OSThread::set_interrupt_callback(Sync_Interrupt_Callback * cb) { + int count = 0; + while (compare_and_exchange_current_callback( + (intptr_t)cb, (intptr_t *)&_current_callback, (intptr_t)NULL, _current_callback_lock) != NULL) { + while (_current_callback != NULL) { + count++; +#ifdef ASSERT + if ((WarnOnStalledSpinLock > 0) && + (count % WarnOnStalledSpinLock == 0)) { + warning("_current_callback seems to be stalled: %p", _current_callback); + } +#endif + os::yield_all(count); + } + } + return 0; +} + +// reset _current_callback, spin if _current_callback is callback_in_progress +void OSThread::remove_interrupt_callback(Sync_Interrupt_Callback * cb) { + int count = 0; + while (compare_and_exchange_current_callback( + (intptr_t)NULL, (intptr_t *)&_current_callback, (intptr_t)cb, _current_callback_lock) != (intptr_t)cb) { +#ifdef ASSERT + intptr_t p = (intptr_t)_current_callback; + assert(p == (intptr_t)callback_in_progress || + p == (intptr_t)cb, "wrong _current_callback value"); +#endif + while (_current_callback != cb) { + count++; +#ifdef ASSERT + if ((WarnOnStalledSpinLock > 0) && + (count % WarnOnStalledSpinLock == 0)) { + warning("_current_callback seems to be stalled: %p", _current_callback); + } +#endif + os::yield_all(count); + } + } +} + +void OSThread::do_interrupt_callbacks_at_interrupt(InterruptArguments *args) { + Sync_Interrupt_Callback * cb; + cb = (Sync_Interrupt_Callback *)exchange_current_callback( + (intptr_t)callback_in_progress, (intptr_t *)&_current_callback, _current_callback_lock); + + if (cb == NULL) { + // signal is delivered too late (thread is masking interrupt signal??). + // there is nothing we need to do because requesting thread has given up. + } else if ((intptr_t)cb == (intptr_t)callback_in_progress) { + fatal("invalid _current_callback state"); + } else { + assert(cb->target()->osthread() == this, "wrong target"); + cb->execute(args); + cb->leave_callback(); // notify the requester + } + + // restore original _current_callback value + intptr_t p; + p = exchange_current_callback((intptr_t)cb, (intptr_t *)&_current_callback, _current_callback_lock); + assert(p == (intptr_t)callback_in_progress, "just checking"); +} + +// Called by the requesting thread to send a signal to target thread and +// execute "this" callback from the signal handler. +int OSThread::Sync_Interrupt_Callback::interrupt(Thread * target, int timeout) { + // Let signals to the vm_thread go even if the Threads_lock is not acquired + assert(Threads_lock->owned_by_self() || (target == VMThread::vm_thread()), + "must have threads lock to call this"); + + OSThread * osthread = target->osthread(); + + // may block if target thread already has a pending callback + osthread->set_interrupt_callback(this); + + _target = target; + + int rslt = thr_kill(osthread->thread_id(), os::Solaris::SIGasync()); + assert(rslt == 0, "thr_kill != 0"); + + bool status = false; + jlong t1 = os::javaTimeMillis(); + { // don't use safepoint check because we might be the watcher thread. + MutexLockerEx ml(_sync, Mutex::_no_safepoint_check_flag); + while (!is_done()) { + status = _sync->wait(Mutex::_no_safepoint_check_flag, timeout); + + // status == true if timed out + if (status) break; + + // update timeout + jlong t2 = os::javaTimeMillis(); + timeout -= t2 - t1; + t1 = t2; + } + } + + // reset current_callback + osthread->remove_interrupt_callback(this); + + return status; +} + +void OSThread::Sync_Interrupt_Callback::leave_callback() { + if (!_sync->owned_by_self()) { + // notify requesting thread + MutexLockerEx ml(_sync, Mutex::_no_safepoint_check_flag); + _is_done = true; + _sync->notify_all(); + } else { + // Current thread is interrupted while it is holding the _sync lock, trying + // to grab it again will deadlock. The requester will timeout anyway, + // so just return. + _is_done = true; + } +} + +// copied from synchronizer.cpp + +void OSThread::handle_spinlock_contention(int tries) { + if (NoYieldsInMicrolock) return; + + if (tries > 10) { + os::yield_all(tries); // Yield to threads of any priority + } else if (tries > 5) { + os::yield(); // Yield to threads of same or higher priority + } +} diff --git a/hotspot/src/os/solaris/vm/osThread_solaris.hpp b/hotspot/src/os/solaris/vm/osThread_solaris.hpp new file mode 100644 index 00000000000..5981c73dfd0 --- /dev/null +++ b/hotspot/src/os/solaris/vm/osThread_solaris.hpp @@ -0,0 +1,151 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This is embedded via include into the class OSThread + + private: + + thread_t _thread_id; // Solaris thread id + unsigned int _lwp_id; // lwp ID, only used with bound threads + sigset_t _caller_sigmask; // Caller's signal mask + bool _vm_created_thread; // true if the VM create this thread + // false if primary thread or attached thread + public: + + thread_t thread_id() const { return _thread_id; } + + unsigned int lwp_id() const { return _lwp_id; } + + // Set and get state of _vm_created_thread flag + void set_vm_created() { _vm_created_thread = true; } + bool is_vm_created() { return _vm_created_thread; } + + // Methods to save/restore caller's signal mask + sigset_t caller_sigmask() const { return _caller_sigmask; } + void set_caller_sigmask(sigset_t sigmask) { _caller_sigmask = sigmask; } + +#ifndef PRODUCT + // Used for debugging, return a unique integer for each thread. + int thread_identifier() const { return _thread_id; } +#endif +#ifdef ASSERT + // On solaris reposition can fail in two ways: + // 1: a mismatched pc, because signal is delivered too late, target thread + // is resumed. + // 2: on a timeout where signal is lost, target thread is resumed. + bool valid_reposition_failure() { + // only 1 and 2 can happen and we can handle both of them + return true; + } +#endif + void set_thread_id(thread_t id) { _thread_id = id; } + void set_lwp_id(unsigned int id){ _lwp_id = id; } + + // *************************************************************** + // interrupt support. interrupts (using signals) are used to get + // the thread context (get_thread_pc), to set the thread context + // (set_thread_pc), and to implement java.lang.Thread.interrupt. + // *************************************************************** + + public: + + class InterruptArguments : StackObj { + private: + Thread* _thread; // the thread to signal was dispatched to + ucontext_t* _ucontext; // the machine context at the time of the signal + + public: + InterruptArguments(Thread* thread, ucontext_t* ucontext) { + _thread = thread; + _ucontext = ucontext; + } + + Thread* thread() const { return _thread; } + ucontext_t* ucontext() const { return _ucontext; } + }; + + // There are currently no asynchronous callbacks - and we'd better not + // support them in the future either, as they need to be deallocated from + // the interrupt handler, which is not safe; they also require locks to + // protect the callback queue. + + class Sync_Interrupt_Callback : private StackObj { + protected: + volatile bool _is_done; + Monitor* _sync; + Thread* _target; + public: + Sync_Interrupt_Callback(Monitor * sync) { + _is_done = false; _target = NULL; _sync = sync; + } + + bool is_done() const { return _is_done; } + Thread* target() const { return _target; } + + int interrupt(Thread * target, int timeout); + + // override to implement the callback. + virtual void execute(InterruptArguments *args) = 0; + + void leave_callback(); + }; + + private: + + Sync_Interrupt_Callback * volatile _current_callback; + enum { + callback_in_progress = 1 + }; + Mutex * _current_callback_lock; // only used on v8 + + public: + + int set_interrupt_callback (Sync_Interrupt_Callback * cb); + void remove_interrupt_callback(Sync_Interrupt_Callback * cb); + void OSThread::do_interrupt_callbacks_at_interrupt(InterruptArguments *args); + + // *************************************************************** + // java.lang.Thread.interrupt state. + // *************************************************************** + + private: + + JavaThreadState _saved_interrupt_thread_state; // the thread state before a system call -- restored afterward + + public: + + + JavaThreadState saved_interrupt_thread_state() { return _saved_interrupt_thread_state; } + void set_saved_interrupt_thread_state(JavaThreadState state) { _saved_interrupt_thread_state = state; } + + static void handle_spinlock_contention(int tries); // Used for thread local eden locking + + // *************************************************************** + // Platform dependent initialization and cleanup + // *************************************************************** + +private: + + void pd_initialize(); + void pd_destroy(); diff --git a/hotspot/src/os/solaris/vm/os_share_solaris.hpp b/hotspot/src/os/solaris/vm/os_share_solaris.hpp new file mode 100644 index 00000000000..502e7ae7ab1 --- /dev/null +++ b/hotspot/src/os/solaris/vm/os_share_solaris.hpp @@ -0,0 +1,67 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Defines the interfaces to Solaris operating systems that vary across platforms + + +// This is a simple callback that just fetches a PC for an interrupted thread. +// The thread need not be suspended and the fetched PC is just a hint. +// Returned PC and nPC are not necessarily consecutive. +// This one is currently used for profiling the VMThread ONLY! + +// Must be synchronous +class GetThreadPC_Callback : public OSThread::Sync_Interrupt_Callback { + private: + ExtendedPC _addr; + + public: + + GetThreadPC_Callback(Monitor *sync) : + OSThread::Sync_Interrupt_Callback(sync) { } + ExtendedPC addr() const { return _addr; } + + void set_addr(ExtendedPC addr) { _addr = addr; } + + void execute(OSThread::InterruptArguments *args); +}; + +// misc +extern "C" { + void signalHandler(int, siginfo_t*, void*); +} +void resolve_lwp_exit_calls(void); +void handle_unexpected_exception(Thread* thread, int sig, siginfo_t* info, address pc, address adjusted_pc); +#ifndef PRODUCT +void continue_with_dump(void); +#endif + +#if defined(__sparc) && defined(COMPILER2) +// For Sun Studio compiler implementation is in file +// src/os_cpu/solaris_sparc/vm/solaris_sparc.il +// For gcc implementation is in file +// src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp +extern "C" void _mark_fpu_nosave() ; +#endif + +#define PROCFILE_LENGTH 128 diff --git a/hotspot/src/os/solaris/vm/os_solaris.cpp b/hotspot/src/os/solaris/vm/os_solaris.cpp new file mode 100644 index 00000000000..e1fb6ff6204 --- /dev/null +++ b/hotspot/src/os/solaris/vm/os_solaris.cpp @@ -0,0 +1,5784 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// do not include precompiled header file +# include "incls/_os_solaris.cpp.incl" + +// put OS-includes here +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include // for elf Sym structure used by dladdr1 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# define _STRUCTURED_PROC 1 // this gets us the new structured proc interfaces of 5.6 & later +# include // see comment in + +#define MAX_PATH (2 * K) + +// for timer info max values which include all bits +#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) + +#ifdef _GNU_SOURCE +// See bug #6514594 +extern "C" int madvise(caddr_t, size_t, int); +extern "C" int memcntl(caddr_t addr, size_t len, int cmd, caddr_t arg, + int attr, int mask); +#endif //_GNU_SOURCE + +/* + MPSS Changes Start. + The JVM binary needs to be built and run on pre-Solaris 9 + systems, but the constants needed by MPSS are only in Solaris 9 + header files. They are textually replicated here to allow + building on earlier systems. Once building on Solaris 8 is + no longer a requirement, these #defines can be replaced by ordinary + system .h inclusion. + + In earlier versions of the JDK and Solaris, we used ISM for large pages. + But ISM requires shared memory to achieve this and thus has many caveats. + MPSS is a fully transparent and is a cleaner way to get large pages. + Although we still require keeping ISM for backward compatiblitiy as well as + giving the opportunity to use large pages on older systems it is + recommended that MPSS be used for Solaris 9 and above. + +*/ + +#ifndef MC_HAT_ADVISE + +struct memcntl_mha { + uint_t mha_cmd; /* command(s) */ + uint_t mha_flags; + size_t mha_pagesize; +}; +#define MC_HAT_ADVISE 7 /* advise hat map size */ +#define MHA_MAPSIZE_VA 0x1 /* set preferred page size */ +#define MAP_ALIGN 0x200 /* addr specifies alignment */ + +#endif +// MPSS Changes End. + + +// Here are some liblgrp types from sys/lgrp_user.h to be able to +// compile on older systems without this header file. + +#ifndef MADV_ACCESS_LWP +# define MADV_ACCESS_LWP 7 /* next LWP to access heavily */ +#endif +#ifndef MADV_ACCESS_MANY +# define MADV_ACCESS_MANY 8 /* many processes to access heavily */ +#endif + +// Some more macros from sys/mman.h that are not present in Solaris 8. + +#ifndef MAX_MEMINFO_CNT +/* + * info_req request type definitions for meminfo + * request types starting with MEMINFO_V are used for Virtual addresses + * and should not be mixed with MEMINFO_PLGRP which is targeted for Physical + * addresses + */ +# define MEMINFO_SHIFT 16 +# define MEMINFO_MASK (0xFF << MEMINFO_SHIFT) +# define MEMINFO_VPHYSICAL (0x01 << MEMINFO_SHIFT) /* get physical addr */ +# define MEMINFO_VLGRP (0x02 << MEMINFO_SHIFT) /* get lgroup */ +# define MEMINFO_VPAGESIZE (0x03 << MEMINFO_SHIFT) /* size of phys page */ +# define MEMINFO_VREPLCNT (0x04 << MEMINFO_SHIFT) /* no. of replica */ +# define MEMINFO_VREPL (0x05 << MEMINFO_SHIFT) /* physical replica */ +# define MEMINFO_VREPL_LGRP (0x06 << MEMINFO_SHIFT) /* lgrp of replica */ +# define MEMINFO_PLGRP (0x07 << MEMINFO_SHIFT) /* lgroup for paddr */ + +/* maximum number of addresses meminfo() can process at a time */ +# define MAX_MEMINFO_CNT 256 + +/* maximum number of request types */ +# define MAX_MEMINFO_REQ 31 +#endif + +// see thr_setprio(3T) for the basis of these numbers +#define MinimumPriority 0 +#define NormalPriority 64 +#define MaximumPriority 127 + +// Values for ThreadPriorityPolicy == 1 +int prio_policy1[MaxPriority+1] = { -99999, 0, 16, 32, 48, 64, + 80, 96, 112, 124, 127 }; + +// System parameters used internally +static clock_t clock_tics_per_sec = 100; + +// For diagnostics to print a message once. see run_periodic_checks +static bool check_addr0_done = false; +static sigset_t check_signal_done; +static bool check_signals = true; + +address os::Solaris::handler_start; // start pc of thr_sighndlrinfo +address os::Solaris::handler_end; // end pc of thr_sighndlrinfo + +address os::Solaris::_main_stack_base = NULL; // 4352906 workaround + + +// "default" initializers for missing libc APIs +extern "C" { + static int lwp_mutex_init(mutex_t *mx, int scope, void *arg) { memset(mx, 0, sizeof(mutex_t)); return 0; } + static int lwp_mutex_destroy(mutex_t *mx) { return 0; } + + static int lwp_cond_init(cond_t *cv, int scope, void *arg){ memset(cv, 0, sizeof(cond_t)); return 0; } + static int lwp_cond_destroy(cond_t *cv) { return 0; } +} + +// "default" initializers for pthread-based synchronization +extern "C" { + static int pthread_mutex_default_init(mutex_t *mx, int scope, void *arg) { memset(mx, 0, sizeof(mutex_t)); return 0; } + static int pthread_cond_default_init(cond_t *cv, int scope, void *arg){ memset(cv, 0, sizeof(cond_t)); return 0; } +} + +// Thread Local Storage +// This is common to all Solaris platforms so it is defined here, +// in this common file. +// The declarations are in the os_cpu threadLS*.hpp files. +// +// Static member initialization for TLS +Thread* ThreadLocalStorage::_get_thread_cache[ThreadLocalStorage::_pd_cache_size] = {NULL}; + +#ifndef PRODUCT +#define _PCT(n,d) ((100.0*(double)(n))/(double)(d)) + +int ThreadLocalStorage::_tcacheHit = 0; +int ThreadLocalStorage::_tcacheMiss = 0; + +void ThreadLocalStorage::print_statistics() { + int total = _tcacheMiss+_tcacheHit; + tty->print_cr("Thread cache hits %d misses %d total %d percent %f\n", + _tcacheHit, _tcacheMiss, total, _PCT(_tcacheHit, total)); +} +#undef _PCT +#endif // PRODUCT + +Thread* ThreadLocalStorage::get_thread_via_cache_slowly(uintptr_t raw_id, + int index) { + Thread *thread = get_thread_slow(); + if (thread != NULL) { + address sp = os::current_stack_pointer(); + guarantee(thread->_stack_base == NULL || + (sp <= thread->_stack_base && + sp >= thread->_stack_base - thread->_stack_size) || + is_error_reported(), + "sp must be inside of selected thread stack"); + + thread->_self_raw_id = raw_id; // mark for quick retrieval + _get_thread_cache[ index ] = thread; + } + return thread; +} + + +static const double all_zero[ sizeof(Thread) / sizeof(double) + 1 ] = {0}; +#define NO_CACHED_THREAD ((Thread*)all_zero) + +void ThreadLocalStorage::pd_set_thread(Thread* thread) { + + // Store the new value before updating the cache to prevent a race + // between get_thread_via_cache_slowly() and this store operation. + os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread); + + // Update thread cache with new thread if setting on thread create, + // or NO_CACHED_THREAD (zeroed) thread if resetting thread on exit. + uintptr_t raw = pd_raw_thread_id(); + int ix = pd_cache_index(raw); + _get_thread_cache[ix] = thread == NULL ? NO_CACHED_THREAD : thread; +} + +void ThreadLocalStorage::pd_init() { + for (int i = 0; i < _pd_cache_size; i++) { + _get_thread_cache[i] = NO_CACHED_THREAD; + } +} + +// Invalidate all the caches (happens to be the same as pd_init). +void ThreadLocalStorage::pd_invalidate_all() { pd_init(); } + +#undef NO_CACHED_THREAD + +// END Thread Local Storage + +static inline size_t adjust_stack_size(address base, size_t size) { + if ((ssize_t)size < 0) { + // 4759953: Compensate for ridiculous stack size. + size = max_intx; + } + if (size > (size_t)base) { + // 4812466: Make sure size doesn't allow the stack to wrap the address space. + size = (size_t)base; + } + return size; +} + +static inline stack_t get_stack_info() { + stack_t st; + int retval = thr_stksegment(&st); + st.ss_size = adjust_stack_size((address)st.ss_sp, st.ss_size); + assert(retval == 0, "incorrect return value from thr_stksegment"); + assert((address)&st < (address)st.ss_sp, "Invalid stack base returned"); + assert((address)&st > (address)st.ss_sp-st.ss_size, "Invalid stack size returned"); + return st; +} + +address os::current_stack_base() { + int r = thr_main() ; + guarantee (r == 0 || r == 1, "CR6501650 or CR6493689") ; + bool is_primordial_thread = r; + + // Workaround 4352906, avoid calls to thr_stksegment by + // thr_main after the first one (it looks like we trash + // some data, causing the value for ss_sp to be incorrect). + if (!is_primordial_thread || os::Solaris::_main_stack_base == NULL) { + stack_t st = get_stack_info(); + if (is_primordial_thread) { + // cache initial value of stack base + os::Solaris::_main_stack_base = (address)st.ss_sp; + } + return (address)st.ss_sp; + } else { + guarantee(os::Solaris::_main_stack_base != NULL, "Attempt to use null cached stack base"); + return os::Solaris::_main_stack_base; + } +} + +size_t os::current_stack_size() { + size_t size; + + int r = thr_main() ; + guarantee (r == 0 || r == 1, "CR6501650 or CR6493689") ; + if(!r) { + size = get_stack_info().ss_size; + } else { + struct rlimit limits; + getrlimit(RLIMIT_STACK, &limits); + size = adjust_stack_size(os::Solaris::_main_stack_base, (size_t)limits.rlim_cur); + } + // base may not be page aligned + address base = current_stack_base(); + address bottom = (address)align_size_up((intptr_t)(base - size), os::vm_page_size());; + return (size_t)(base - bottom); +} + +// interruptible infrastructure + +// setup_interruptible saves the thread state before going into an +// interruptible system call. +// The saved state is used to restore the thread to +// its former state whether or not an interrupt is received. +// Used by classloader os::read +// hpi calls skip this layer and stay in _thread_in_native + +void os::Solaris::setup_interruptible(JavaThread* thread) { + + JavaThreadState thread_state = thread->thread_state(); + + assert(thread_state != _thread_blocked, "Coming from the wrong thread"); + assert(thread_state != _thread_in_native, "Native threads skip setup_interruptible"); + OSThread* osthread = thread->osthread(); + osthread->set_saved_interrupt_thread_state(thread_state); + thread->frame_anchor()->make_walkable(thread); + ThreadStateTransition::transition(thread, thread_state, _thread_blocked); +} + +// Version of setup_interruptible() for threads that are already in +// _thread_blocked. Used by os_sleep(). +void os::Solaris::setup_interruptible_already_blocked(JavaThread* thread) { + thread->frame_anchor()->make_walkable(thread); +} + +JavaThread* os::Solaris::setup_interruptible() { + JavaThread* thread = (JavaThread*)ThreadLocalStorage::thread(); + setup_interruptible(thread); + return thread; +} + +void os::Solaris::try_enable_extended_io() { + typedef int (*enable_extended_FILE_stdio_t)(int, int); + + if (!UseExtendedFileIO) { + return; + } + + enable_extended_FILE_stdio_t enabler = + (enable_extended_FILE_stdio_t) dlsym(RTLD_DEFAULT, + "enable_extended_FILE_stdio"); + if (enabler) { + enabler(-1, -1); + } +} + + +#ifdef ASSERT + +JavaThread* os::Solaris::setup_interruptible_native() { + JavaThread* thread = (JavaThread*)ThreadLocalStorage::thread(); + JavaThreadState thread_state = thread->thread_state(); + assert(thread_state == _thread_in_native, "Assumed thread_in_native"); + return thread; +} + +void os::Solaris::cleanup_interruptible_native(JavaThread* thread) { + JavaThreadState thread_state = thread->thread_state(); + assert(thread_state == _thread_in_native, "Assumed thread_in_native"); +} +#endif + +// cleanup_interruptible reverses the effects of setup_interruptible +// setup_interruptible_already_blocked() does not need any cleanup. + +void os::Solaris::cleanup_interruptible(JavaThread* thread) { + OSThread* osthread = thread->osthread(); + + ThreadStateTransition::transition(thread, _thread_blocked, osthread->saved_interrupt_thread_state()); +} + +// I/O interruption related counters called in _INTERRUPTIBLE + +void os::Solaris::bump_interrupted_before_count() { + RuntimeService::record_interrupted_before_count(); +} + +void os::Solaris::bump_interrupted_during_count() { + RuntimeService::record_interrupted_during_count(); +} + +static int _processors_online = 0; + + jint os::Solaris::_os_thread_limit = 0; +volatile jint os::Solaris::_os_thread_count = 0; + +julong os::available_memory() { + return Solaris::available_memory(); +} + +julong os::Solaris::available_memory() { + return (julong)sysconf(_SC_AVPHYS_PAGES) * os::vm_page_size(); +} + +julong os::Solaris::_physical_memory = 0; + +julong os::physical_memory() { + return Solaris::physical_memory(); +} + +julong os::allocatable_physical_memory(julong size) { +#ifdef _LP64 + return size; +#else + julong result = MIN2(size, (julong)3835*M); + if (!is_allocatable(result)) { + // Memory allocations will be aligned but the alignment + // is not known at this point. Alignments will + // be at most to LargePageSizeInBytes. Protect + // allocations from alignments up to illegal + // values. If at this point 2G is illegal. + julong reasonable_size = (julong)2*G - 2 * LargePageSizeInBytes; + result = MIN2(size, reasonable_size); + } + return result; +#endif +} + +static hrtime_t first_hrtime = 0; +static const hrtime_t hrtime_hz = 1000*1000*1000; +const int LOCK_BUSY = 1; +const int LOCK_FREE = 0; +const int LOCK_INVALID = -1; +static volatile hrtime_t max_hrtime = 0; +static volatile int max_hrtime_lock = LOCK_FREE; // Update counter with LSB as lock-in-progress + + +void os::Solaris::initialize_system_info() { + _processor_count = sysconf(_SC_NPROCESSORS_CONF); + _processors_online = sysconf (_SC_NPROCESSORS_ONLN); + _physical_memory = (julong)sysconf(_SC_PHYS_PAGES) * (julong)sysconf(_SC_PAGESIZE); +} + +int os::active_processor_count() { + int online_cpus = sysconf(_SC_NPROCESSORS_ONLN); + pid_t pid = getpid(); + psetid_t pset = PS_NONE; + // Are we running in a processor set? + if (pset_bind(PS_QUERY, P_PID, pid, &pset) == 0) { + if (pset != PS_NONE) { + uint_t pset_cpus; + // Query number of cpus in processor set + if (pset_info(pset, NULL, &pset_cpus, NULL) == 0) { + assert(pset_cpus > 0 && pset_cpus <= online_cpus, "sanity check"); + _processors_online = pset_cpus; + return pset_cpus; + } + } + } + // Otherwise return number of online cpus + return online_cpus; +} + +static bool find_processors_in_pset(psetid_t pset, + processorid_t** id_array, + uint_t* id_length) { + bool result = false; + // Find the number of processors in the processor set. + if (pset_info(pset, NULL, id_length, NULL) == 0) { + // Make up an array to hold their ids. + *id_array = NEW_C_HEAP_ARRAY(processorid_t, *id_length); + // Fill in the array with their processor ids. + if (pset_info(pset, NULL, id_length, *id_array) == 0) { + result = true; + } + } + return result; +} + +// Callers of find_processors_online() must tolerate imprecise results -- +// the system configuration can change asynchronously because of DR +// or explicit psradm operations. +// +// We also need to take care that the loop (below) terminates as the +// number of processors online can change between the _SC_NPROCESSORS_ONLN +// request and the loop that builds the list of processor ids. Unfortunately +// there's no reliable way to determine the maximum valid processor id, +// so we use a manifest constant, MAX_PROCESSOR_ID, instead. See p_online +// man pages, which claim the processor id set is "sparse, but +// not too sparse". MAX_PROCESSOR_ID is used to ensure that we eventually +// exit the loop. +// +// In the future we'll be able to use sysconf(_SC_CPUID_MAX), but that's +// not available on S8.0. + +static bool find_processors_online(processorid_t** id_array, + uint* id_length) { + const processorid_t MAX_PROCESSOR_ID = 100000 ; + // Find the number of processors online. + *id_length = sysconf(_SC_NPROCESSORS_ONLN); + // Make up an array to hold their ids. + *id_array = NEW_C_HEAP_ARRAY(processorid_t, *id_length); + // Processors need not be numbered consecutively. + long found = 0; + processorid_t next = 0; + while (found < *id_length && next < MAX_PROCESSOR_ID) { + processor_info_t info; + if (processor_info(next, &info) == 0) { + // NB, PI_NOINTR processors are effectively online ... + if (info.pi_state == P_ONLINE || info.pi_state == P_NOINTR) { + (*id_array)[found] = next; + found += 1; + } + } + next += 1; + } + if (found < *id_length) { + // The loop above didn't identify the expected number of processors. + // We could always retry the operation, calling sysconf(_SC_NPROCESSORS_ONLN) + // and re-running the loop, above, but there's no guarantee of progress + // if the system configuration is in flux. Instead, we just return what + // we've got. Note that in the worst case find_processors_online() could + // return an empty set. (As a fall-back in the case of the empty set we + // could just return the ID of the current processor). + *id_length = found ; + } + + return true; +} + +static bool assign_distribution(processorid_t* id_array, + uint id_length, + uint* distribution, + uint distribution_length) { + // We assume we can assign processorid_t's to uint's. + assert(sizeof(processorid_t) == sizeof(uint), + "can't convert processorid_t to uint"); + // Quick check to see if we won't succeed. + if (id_length < distribution_length) { + return false; + } + // Assign processor ids to the distribution. + // Try to shuffle processors to distribute work across boards, + // assuming 4 processors per board. + const uint processors_per_board = ProcessDistributionStride; + // Find the maximum processor id. + processorid_t max_id = 0; + for (uint m = 0; m < id_length; m += 1) { + max_id = MAX2(max_id, id_array[m]); + } + // The next id, to limit loops. + const processorid_t limit_id = max_id + 1; + // Make up markers for available processors. + bool* available_id = NEW_C_HEAP_ARRAY(bool, limit_id); + for (uint c = 0; c < limit_id; c += 1) { + available_id[c] = false; + } + for (uint a = 0; a < id_length; a += 1) { + available_id[id_array[a]] = true; + } + // Step by "boards", then by "slot", copying to "assigned". + // NEEDS_CLEANUP: The assignment of processors should be stateful, + // remembering which processors have been assigned by + // previous calls, etc., so as to distribute several + // independent calls of this method. What we'd like is + // It would be nice to have an API that let us ask + // how many processes are bound to a processor, + // but we don't have that, either. + // In the short term, "board" is static so that + // subsequent distributions don't all start at board 0. + static uint board = 0; + uint assigned = 0; + // Until we've found enough processors .... + while (assigned < distribution_length) { + // ... find the next available processor in the board. + for (uint slot = 0; slot < processors_per_board; slot += 1) { + uint try_id = board * processors_per_board + slot; + if ((try_id < limit_id) && (available_id[try_id] == true)) { + distribution[assigned] = try_id; + available_id[try_id] = false; + assigned += 1; + break; + } + } + board += 1; + if (board * processors_per_board + 0 >= limit_id) { + board = 0; + } + } + if (available_id != NULL) { + FREE_C_HEAP_ARRAY(bool, available_id); + } + return true; +} + +bool os::distribute_processes(uint length, uint* distribution) { + bool result = false; + // Find the processor id's of all the available CPUs. + processorid_t* id_array = NULL; + uint id_length = 0; + // There are some races between querying information and using it, + // since processor sets can change dynamically. + psetid_t pset = PS_NONE; + // Are we running in a processor set? + if ((pset_bind(PS_QUERY, P_PID, P_MYID, &pset) == 0) && pset != PS_NONE) { + result = find_processors_in_pset(pset, &id_array, &id_length); + } else { + result = find_processors_online(&id_array, &id_length); + } + if (result == true) { + if (id_length >= length) { + result = assign_distribution(id_array, id_length, distribution, length); + } else { + result = false; + } + } + if (id_array != NULL) { + FREE_C_HEAP_ARRAY(processorid_t, id_array); + } + return result; +} + +bool os::bind_to_processor(uint processor_id) { + // We assume that a processorid_t can be stored in a uint. + assert(sizeof(uint) == sizeof(processorid_t), + "can't convert uint to processorid_t"); + int bind_result = + processor_bind(P_LWPID, // bind LWP. + P_MYID, // bind current LWP. + (processorid_t) processor_id, // id. + NULL); // don't return old binding. + return (bind_result == 0); +} + +bool os::getenv(const char* name, char* buffer, int len) { + char* val = ::getenv( name ); + if ( val == NULL + || strlen(val) + 1 > len ) { + if (len > 0) buffer[0] = 0; // return a null string + return false; + } + strcpy( buffer, val ); + return true; +} + + +// Return true if user is running as root. + +bool os::have_special_privileges() { + static bool init = false; + static bool privileges = false; + if (!init) { + privileges = (getuid() != geteuid()) || (getgid() != getegid()); + init = true; + } + return privileges; +} + + +static char* get_property(char* name, char* buffer, int buffer_size) { + if (os::getenv(name, buffer, buffer_size)) { + return buffer; + } + static char empty[] = ""; + return empty; +} + + +void os::init_system_properties_values() { + char arch[12]; + sysinfo(SI_ARCHITECTURE, arch, sizeof(arch)); + + // The next steps are taken in the product version: + // + // Obtain the JAVA_HOME value from the location of libjvm[_g].so. + // This library should be located at: + // /jre/lib//{client|server}/libjvm[_g].so. + // + // If "/jre/lib/" appears at the right place in the path, then we + // assume libjvm[_g].so is installed in a JDK and we use this path. + // + // Otherwise exit with message: "Could not create the Java virtual machine." + // + // The following extra steps are taken in the debugging version: + // + // If "/jre/lib/" does NOT appear at the right place in the path + // instead of exit check for $JAVA_HOME environment variable. + // + // If it is defined and we are able to locate $JAVA_HOME/jre/lib/, + // then we append a fake suffix "hotspot/libjvm[_g].so" to this path so + // it looks like libjvm[_g].so is installed there + // /jre/lib//hotspot/libjvm[_g].so. + // + // Otherwise exit. + // + // Important note: if the location of libjvm.so changes this + // code needs to be changed accordingly. + + // The next few definitions allow the code to be verbatim: +#define malloc(n) (char*)NEW_C_HEAP_ARRAY(char, (n)) +#define free(p) FREE_C_HEAP_ARRAY(char, p) +#define getenv(n) ::getenv(n) + +#define EXTENSIONS_DIR "/lib/ext" +#define ENDORSED_DIR "/lib/endorsed" +#define COMMON_DIR "/usr/jdk/packages" + + { + /* sysclasspath, java_home, dll_dir */ + { + char *home_path; + char *dll_path; + char *pslash; + char buf[MAXPATHLEN]; + os::jvm_path(buf, sizeof(buf)); + + // Found the full path to libjvm.so. + // Now cut the path to /jre if we can. + *(strrchr(buf, '/')) = '\0'; /* get rid of /libjvm.so */ + pslash = strrchr(buf, '/'); + if (pslash != NULL) + *pslash = '\0'; /* get rid of /{client|server|hotspot} */ + dll_path = malloc(strlen(buf) + 1); + if (dll_path == NULL) + return; + strcpy(dll_path, buf); + Arguments::set_dll_dir(dll_path); + + if (pslash != NULL) { + pslash = strrchr(buf, '/'); + if (pslash != NULL) { + *pslash = '\0'; /* get rid of / */ + pslash = strrchr(buf, '/'); + if (pslash != NULL) + *pslash = '\0'; /* get rid of /lib */ + } + } + + home_path = malloc(strlen(buf) + 1); + if (home_path == NULL) + return; + strcpy(home_path, buf); + Arguments::set_java_home(home_path); + + if (!set_boot_path('/', ':')) + return; + } + + /* + * Where to look for native libraries + */ + { + // Use dlinfo() to determine the correct java.library.path. + // + // If we're launched by the Java launcher, and the user + // does not set java.library.path explicitly on the commandline, + // the Java launcher sets LD_LIBRARY_PATH for us and unsets + // LD_LIBRARY_PATH_32 and LD_LIBRARY_PATH_64. In this case + // dlinfo returns LD_LIBRARY_PATH + crle settings (including + // /usr/lib), which is exactly what we want. + // + // If the user does set java.library.path, it completely + // overwrites this setting, and always has. + // + // If we're not launched by the Java launcher, we may + // get here with any/all of the LD_LIBRARY_PATH[_32|64] + // settings. Again, dlinfo does exactly what we want. + + Dl_serinfo _info, *info = &_info; + Dl_serpath *path; + char* library_path; + char *common_path; + int i; + + // determine search path count and required buffer size + if (dlinfo(RTLD_SELF, RTLD_DI_SERINFOSIZE, (void *)info) == -1) { + vm_exit_during_initialization("dlinfo SERINFOSIZE request", dlerror()); + } + + // allocate new buffer and initialize + info = (Dl_serinfo*)malloc(_info.dls_size); + if (info == NULL) { + vm_exit_out_of_memory(_info.dls_size, + "init_system_properties_values info"); + } + info->dls_size = _info.dls_size; + info->dls_cnt = _info.dls_cnt; + + // obtain search path information + if (dlinfo(RTLD_SELF, RTLD_DI_SERINFO, (void *)info) == -1) { + free(info); + vm_exit_during_initialization("dlinfo SERINFO request", dlerror()); + } + + path = &info->dls_serpath[0]; + + // Note: Due to a legacy implementation, most of the library path + // is set in the launcher. This was to accomodate linking restrictions + // on legacy Solaris implementations (which are no longer supported). + // Eventually, all the library path setting will be done here. + // + // However, to prevent the proliferation of improperly built native + // libraries, the new path component /usr/jdk/packages is added here. + + // Determine the actual CPU architecture. + char cpu_arch[12]; + sysinfo(SI_ARCHITECTURE, cpu_arch, sizeof(cpu_arch)); +#ifdef _LP64 + // If we are a 64-bit vm, perform the following translations: + // sparc -> sparcv9 + // i386 -> amd64 + if (strcmp(cpu_arch, "sparc") == 0) + strcat(cpu_arch, "v9"); + else if (strcmp(cpu_arch, "i386") == 0) + strcpy(cpu_arch, "amd64"); +#endif + + // Construct the invariant part of ld_library_path. Note that the + // space for the colon and the trailing null are provided by the + // nulls included by the sizeof operator. + size_t bufsize = sizeof(COMMON_DIR) + sizeof("/lib/") + strlen(cpu_arch); + common_path = malloc(bufsize); + if (common_path == NULL) { + free(info); + vm_exit_out_of_memory(bufsize, + "init_system_properties_values common_path"); + } + sprintf(common_path, COMMON_DIR "/lib/%s", cpu_arch); + + // struct size is more than sufficient for the path components obtained + // through the dlinfo() call, so only add additional space for the path + // components explicitly added here. + bufsize = info->dls_size + strlen(common_path); + library_path = malloc(bufsize); + if (library_path == NULL) { + free(info); + free(common_path); + vm_exit_out_of_memory(bufsize, + "init_system_properties_values library_path"); + } + library_path[0] = '\0'; + + // Construct the desired Java library path from the linker's library + // search path. + // + // For compatibility, it is optimal that we insert the additional path + // components specific to the Java VM after those components specified + // in LD_LIBRARY_PATH (if any) but before those added by the ld.so + // infrastructure. + if (info->dls_cnt == 0) { // Not sure this can happen, but allow for it + strcpy(library_path, common_path); + } else { + int inserted = 0; + for (i = 0; i < info->dls_cnt; i++, path++) { + uint_t flags = path->dls_flags & LA_SER_MASK; + if (((flags & LA_SER_LIBPATH) == 0) && !inserted) { + strcat(library_path, common_path); + strcat(library_path, os::path_separator()); + inserted = 1; + } + strcat(library_path, path->dls_name); + strcat(library_path, os::path_separator()); + } + // eliminate trailing path separator + library_path[strlen(library_path)-1] = '\0'; + } + + // happens before argument parsing - can't use a trace flag + // tty->print_raw("init_system_properties_values: native lib path: "); + // tty->print_raw_cr(library_path); + + // callee copies into its own buffer + Arguments::set_library_path(library_path); + + free(common_path); + free(library_path); + free(info); + } + + /* + * Extensions directories. + * + * Note that the space for the colon and the trailing null are provided + * by the nulls included by the sizeof operator (so actually one byte more + * than necessary is allocated). + */ + { + char *buf = (char *) malloc(strlen(Arguments::get_java_home()) + + sizeof(EXTENSIONS_DIR) + sizeof(COMMON_DIR) + + sizeof(EXTENSIONS_DIR)); + sprintf(buf, "%s" EXTENSIONS_DIR ":" COMMON_DIR EXTENSIONS_DIR, + Arguments::get_java_home()); + Arguments::set_ext_dirs(buf); + } + + /* Endorsed standards default directory. */ + { + char * buf = malloc(strlen(Arguments::get_java_home()) + sizeof(ENDORSED_DIR)); + sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home()); + Arguments::set_endorsed_dirs(buf); + } + } + +#undef malloc +#undef free +#undef getenv +#undef EXTENSIONS_DIR +#undef ENDORSED_DIR +#undef COMMON_DIR + +} + +void os::breakpoint() { + BREAKPOINT; +} + +bool os::obsolete_option(const JavaVMOption *option) +{ + if (!strncmp(option->optionString, "-Xt", 3)) { + return true; + } else if (!strncmp(option->optionString, "-Xtm", 4)) { + return true; + } else if (!strncmp(option->optionString, "-Xverifyheap", 12)) { + return true; + } else if (!strncmp(option->optionString, "-Xmaxjitcodesize", 16)) { + return true; + } + return false; +} + +bool os::Solaris::valid_stack_address(Thread* thread, address sp) { + address stackStart = (address)thread->stack_base(); + address stackEnd = (address)(stackStart - (address)thread->stack_size()); + if (sp < stackStart && sp >= stackEnd ) return true; + return false; +} + +extern "C" void breakpoint() { + // use debugger to set breakpoint here +} + +// Returns an estimate of the current stack pointer. Result must be guaranteed to +// point into the calling threads stack, and be no lower than the current stack +// pointer. +address os::current_stack_pointer() { + volatile int dummy; + address sp = (address)&dummy + 8; // %%%% need to confirm if this is right + return sp; +} + +static thread_t main_thread; + +// Thread start routine for all new Java threads +extern "C" void* java_start(void* thread_addr) { + // Try to randomize the cache line index of hot stack frames. + // This helps when threads of the same stack traces evict each other's + // cache lines. The threads can be either from the same JVM instance, or + // from different JVM instances. The benefit is especially true for + // processors with hyperthreading technology. + static int counter = 0; + int pid = os::current_process_id(); + alloca(((pid ^ counter++) & 7) * 128); + + int prio; + Thread* thread = (Thread*)thread_addr; + OSThread* osthr = thread->osthread(); + + osthr->set_lwp_id( _lwp_self() ); // Store lwp in case we are bound + thread->_schedctl = (void *) schedctl_init () ; + + if (UseNUMA) { + int lgrp_id = os::numa_get_group_id(); + if (lgrp_id != -1) { + thread->set_lgrp_id(lgrp_id); + } + } + + // If the creator called set priority before we started, + // we need to call set priority now that we have an lwp. + // Get the priority from libthread and set the priority + // for the new Solaris lwp. + if ( osthr->thread_id() != -1 ) { + if ( UseThreadPriorities ) { + thr_getprio(osthr->thread_id(), &prio); + if (ThreadPriorityVerbose) { + tty->print_cr("Starting Thread " INTPTR_FORMAT ", LWP is " INTPTR_FORMAT ", setting priority: %d\n", + osthr->thread_id(), osthr->lwp_id(), prio ); + } + os::set_native_priority(thread, prio); + } + } else if (ThreadPriorityVerbose) { + warning("Can't set priority in _start routine, thread id hasn't been set\n"); + } + + assert(osthr->get_state() == RUNNABLE, "invalid os thread state"); + + // initialize signal mask for this thread + os::Solaris::hotspot_sigmask(thread); + + thread->run(); + + // One less thread is executing + // When the VMThread gets here, the main thread may have already exited + // which frees the CodeHeap containing the Atomic::dec code + if (thread != VMThread::vm_thread() && VMThread::vm_thread() != NULL) { + Atomic::dec(&os::Solaris::_os_thread_count); + } + + if (UseDetachedThreads) { + thr_exit(NULL); + ShouldNotReachHere(); + } + return NULL; +} + +static OSThread* create_os_thread(Thread* thread, thread_t thread_id) { + // Allocate the OSThread object + OSThread* osthread = new OSThread(NULL, NULL); + if (osthread == NULL) return NULL; + + // Store info on the Solaris thread into the OSThread + osthread->set_thread_id(thread_id); + osthread->set_lwp_id(_lwp_self()); + thread->_schedctl = (void *) schedctl_init () ; + + if (UseNUMA) { + int lgrp_id = os::numa_get_group_id(); + if (lgrp_id != -1) { + thread->set_lgrp_id(lgrp_id); + } + } + + if ( ThreadPriorityVerbose ) { + tty->print_cr("In create_os_thread, Thread " INTPTR_FORMAT ", LWP is " INTPTR_FORMAT "\n", + osthread->thread_id(), osthread->lwp_id() ); + } + + // Initial thread state is INITIALIZED, not SUSPENDED + osthread->set_state(INITIALIZED); + + return osthread; +} + +void os::Solaris::hotspot_sigmask(Thread* thread) { + + //Save caller's signal mask + sigset_t sigmask; + thr_sigsetmask(SIG_SETMASK, NULL, &sigmask); + OSThread *osthread = thread->osthread(); + osthread->set_caller_sigmask(sigmask); + + thr_sigsetmask(SIG_UNBLOCK, os::Solaris::unblocked_signals(), NULL); + if (!ReduceSignalUsage) { + if (thread->is_VM_thread()) { + // Only the VM thread handles BREAK_SIGNAL ... + thr_sigsetmask(SIG_UNBLOCK, vm_signals(), NULL); + } else { + // ... all other threads block BREAK_SIGNAL + assert(!sigismember(vm_signals(), SIGINT), "SIGINT should not be blocked"); + thr_sigsetmask(SIG_BLOCK, vm_signals(), NULL); + } + } +} + +bool os::create_attached_thread(JavaThread* thread) { +#ifdef ASSERT + thread->verify_not_published(); +#endif + OSThread* osthread = create_os_thread(thread, thr_self()); + if (osthread == NULL) { + return false; + } + + // Initial thread state is RUNNABLE + osthread->set_state(RUNNABLE); + thread->set_osthread(osthread); + + // initialize signal mask for this thread + // and save the caller's signal mask + os::Solaris::hotspot_sigmask(thread); + + return true; +} + +bool os::create_main_thread(JavaThread* thread) { +#ifdef ASSERT + thread->verify_not_published(); +#endif + if (_starting_thread == NULL) { + _starting_thread = create_os_thread(thread, main_thread); + if (_starting_thread == NULL) { + return false; + } + } + + // The primodial thread is runnable from the start + _starting_thread->set_state(RUNNABLE); + + thread->set_osthread(_starting_thread); + + // initialize signal mask for this thread + // and save the caller's signal mask + os::Solaris::hotspot_sigmask(thread); + + return true; +} + +// _T2_libthread is true if we believe we are running with the newer +// SunSoft lwp/libthread.so (2.8 patch, 2.9 default) +bool os::Solaris::_T2_libthread = false; + +bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { + // Allocate the OSThread object + OSThread* osthread = new OSThread(NULL, NULL); + if (osthread == NULL) { + return false; + } + + if ( ThreadPriorityVerbose ) { + char *thrtyp; + switch ( thr_type ) { + case vm_thread: + thrtyp = (char *)"vm"; + break; + case cgc_thread: + thrtyp = (char *)"cgc"; + break; + case pgc_thread: + thrtyp = (char *)"pgc"; + break; + case java_thread: + thrtyp = (char *)"java"; + break; + case compiler_thread: + thrtyp = (char *)"compiler"; + break; + case watcher_thread: + thrtyp = (char *)"watcher"; + break; + default: + thrtyp = (char *)"unknown"; + break; + } + tty->print_cr("In create_thread, creating a %s thread\n", thrtyp); + } + + // Calculate stack size if it's not specified by caller. + if (stack_size == 0) { + // The default stack size 1M (2M for LP64). + stack_size = (BytesPerWord >> 2) * K * K; + + switch (thr_type) { + case os::java_thread: + // Java threads use ThreadStackSize which default value can be changed with the flag -Xss + if (JavaThread::stack_size_at_create() > 0) stack_size = JavaThread::stack_size_at_create(); + break; + case os::compiler_thread: + if (CompilerThreadStackSize > 0) { + stack_size = (size_t)(CompilerThreadStackSize * K); + break; + } // else fall through: + // use VMThreadStackSize if CompilerThreadStackSize is not defined + case os::vm_thread: + case os::pgc_thread: + case os::cgc_thread: + case os::watcher_thread: + if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K); + break; + } + } + stack_size = MAX2(stack_size, os::Solaris::min_stack_allowed); + + // Initial state is ALLOCATED but not INITIALIZED + osthread->set_state(ALLOCATED); + + if (os::Solaris::_os_thread_count > os::Solaris::_os_thread_limit) { + // We got lots of threads. Check if we still have some address space left. + // Need to be at least 5Mb of unreserved address space. We do check by + // trying to reserve some. + const size_t VirtualMemoryBangSize = 20*K*K; + char* mem = os::reserve_memory(VirtualMemoryBangSize); + if (mem == NULL) { + delete osthread; + return false; + } else { + // Release the memory again + os::release_memory(mem, VirtualMemoryBangSize); + } + } + + // Setup osthread because the child thread may need it. + thread->set_osthread(osthread); + + // Create the Solaris thread + // explicit THR_BOUND for T2_libthread case in case + // that assumption is not accurate, but our alternate signal stack + // handling is based on it which must have bound threads + thread_t tid = 0; + long flags = (UseDetachedThreads ? THR_DETACHED : 0) | THR_SUSPENDED + | ((UseBoundThreads || os::Solaris::T2_libthread() || + (thr_type == vm_thread) || + (thr_type == cgc_thread) || + (thr_type == pgc_thread) || + (thr_type == compiler_thread && BackgroundCompilation)) ? + THR_BOUND : 0); + int status; + + // 4376845 -- libthread/kernel don't provide enough LWPs to utilize all CPUs. + // + // On multiprocessors systems, libthread sometimes under-provisions our + // process with LWPs. On a 30-way systems, for instance, we could have + // 50 user-level threads in ready state and only 2 or 3 LWPs assigned + // to our process. This can result in under utilization of PEs. + // I suspect the problem is related to libthread's LWP + // pool management and to the kernel's SIGBLOCKING "last LWP parked" + // upcall policy. + // + // The following code is palliative -- it attempts to ensure that our + // process has sufficient LWPs to take advantage of multiple PEs. + // Proper long-term cures include using user-level threads bound to LWPs + // (THR_BOUND) or using LWP-based synchronization. Note that there is a + // slight timing window with respect to sampling _os_thread_count, but + // the race is benign. Also, we should periodically recompute + // _processors_online as the min of SC_NPROCESSORS_ONLN and the + // the number of PEs in our partition. You might be tempted to use + // THR_NEW_LWP here, but I'd recommend against it as that could + // result in undesirable growth of the libthread's LWP pool. + // The fix below isn't sufficient; for instance, it doesn't take into count + // LWPs parked on IO. It does, however, help certain CPU-bound benchmarks. + // + // Some pathologies this scheme doesn't handle: + // * Threads can block, releasing the LWPs. The LWPs can age out. + // When a large number of threads become ready again there aren't + // enough LWPs available to service them. This can occur when the + // number of ready threads oscillates. + // * LWPs/Threads park on IO, thus taking the LWP out of circulation. + // + // Finally, we should call thr_setconcurrency() periodically to refresh + // the LWP pool and thwart the LWP age-out mechanism. + // The "+3" term provides a little slop -- we want to slightly overprovision. + + if (AdjustConcurrency && os::Solaris::_os_thread_count < (_processors_online+3)) { + if (!(flags & THR_BOUND)) { + thr_setconcurrency (os::Solaris::_os_thread_count); // avoid starvation + } + } + // Although this doesn't hurt, we should warn of undefined behavior + // when using unbound T1 threads with schedctl(). This should never + // happen, as the compiler and VM threads are always created bound + DEBUG_ONLY( + if ((VMThreadHintNoPreempt || CompilerThreadHintNoPreempt) && + (!os::Solaris::T2_libthread() && (!(flags & THR_BOUND))) && + ((thr_type == vm_thread) || (thr_type == cgc_thread) || + (thr_type == pgc_thread) || (thr_type == compiler_thread && BackgroundCompilation))) { + warning("schedctl behavior undefined when Compiler/VM/GC Threads are Unbound"); + } + ); + + + // Mark that we don't have an lwp or thread id yet. + // In case we attempt to set the priority before the thread starts. + osthread->set_lwp_id(-1); + osthread->set_thread_id(-1); + + status = thr_create(NULL, stack_size, java_start, thread, flags, &tid); + if (status != 0) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + perror("os::create_thread"); + } + thread->set_osthread(NULL); + // Need to clean up stuff we've allocated so far + delete osthread; + return false; + } + + Atomic::inc(&os::Solaris::_os_thread_count); + + // Store info on the Solaris thread into the OSThread + osthread->set_thread_id(tid); + + // Remember that we created this thread so we can set priority on it + osthread->set_vm_created(); + + // Set the default thread priority otherwise use NormalPriority + + if ( UseThreadPriorities ) { + thr_setprio(tid, (DefaultThreadPriority == -1) ? + java_to_os_priority[NormPriority] : + DefaultThreadPriority); + } + + // Initial thread state is INITIALIZED, not SUSPENDED + osthread->set_state(INITIALIZED); + + // The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain + return true; +} + +/* defined for >= Solaris 10. This allows builds on earlier versions + * of Solaris to take advantage of the newly reserved Solaris JVM signals + * With SIGJVM1, SIGJVM2, INTERRUPT_SIGNAL is SIGJVM1, ASYNC_SIGNAL is SIGJVM2 + * and -XX:+UseAltSigs does nothing since these should have no conflict + */ +#if !defined(SIGJVM1) +#define SIGJVM1 39 +#define SIGJVM2 40 +#endif + +debug_only(static bool signal_sets_initialized = false); +static sigset_t unblocked_sigs, vm_sigs, allowdebug_blocked_sigs; +int os::Solaris::_SIGinterrupt = INTERRUPT_SIGNAL; +int os::Solaris::_SIGasync = ASYNC_SIGNAL; + +bool os::Solaris::is_sig_ignored(int sig) { + struct sigaction oact; + sigaction(sig, (struct sigaction*)NULL, &oact); + void* ohlr = oact.sa_sigaction ? CAST_FROM_FN_PTR(void*, oact.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oact.sa_handler); + if (ohlr == CAST_FROM_FN_PTR(void*, SIG_IGN)) + return true; + else + return false; +} + +// Note: SIGRTMIN is a macro that calls sysconf() so it will +// dynamically detect SIGRTMIN value for the system at runtime, not buildtime +static bool isJVM1available() { + return SIGJVM1 < SIGRTMIN; +} + +void os::Solaris::signal_sets_init() { + // Should also have an assertion stating we are still single-threaded. + assert(!signal_sets_initialized, "Already initialized"); + // Fill in signals that are necessarily unblocked for all threads in + // the VM. Currently, we unblock the following signals: + // SHUTDOWN{1,2,3}_SIGNAL: for shutdown hooks support (unless over-ridden + // by -Xrs (=ReduceSignalUsage)); + // BREAK_SIGNAL which is unblocked only by the VM thread and blocked by all + // other threads. The "ReduceSignalUsage" boolean tells us not to alter + // the dispositions or masks wrt these signals. + // Programs embedding the VM that want to use the above signals for their + // own purposes must, at this time, use the "-Xrs" option to prevent + // interference with shutdown hooks and BREAK_SIGNAL thread dumping. + // (See bug 4345157, and other related bugs). + // In reality, though, unblocking these signals is really a nop, since + // these signals are not blocked by default. + sigemptyset(&unblocked_sigs); + sigemptyset(&allowdebug_blocked_sigs); + sigaddset(&unblocked_sigs, SIGILL); + sigaddset(&unblocked_sigs, SIGSEGV); + sigaddset(&unblocked_sigs, SIGBUS); + sigaddset(&unblocked_sigs, SIGFPE); + + if (isJVM1available) { + os::Solaris::set_SIGinterrupt(SIGJVM1); + os::Solaris::set_SIGasync(SIGJVM2); + } else if (UseAltSigs) { + os::Solaris::set_SIGinterrupt(ALT_INTERRUPT_SIGNAL); + os::Solaris::set_SIGasync(ALT_ASYNC_SIGNAL); + } else { + os::Solaris::set_SIGinterrupt(INTERRUPT_SIGNAL); + os::Solaris::set_SIGasync(ASYNC_SIGNAL); + } + + sigaddset(&unblocked_sigs, os::Solaris::SIGinterrupt()); + sigaddset(&unblocked_sigs, os::Solaris::SIGasync()); + + if (!ReduceSignalUsage) { + if (!os::Solaris::is_sig_ignored(SHUTDOWN1_SIGNAL)) { + sigaddset(&unblocked_sigs, SHUTDOWN1_SIGNAL); + sigaddset(&allowdebug_blocked_sigs, SHUTDOWN1_SIGNAL); + } + if (!os::Solaris::is_sig_ignored(SHUTDOWN2_SIGNAL)) { + sigaddset(&unblocked_sigs, SHUTDOWN2_SIGNAL); + sigaddset(&allowdebug_blocked_sigs, SHUTDOWN2_SIGNAL); + } + if (!os::Solaris::is_sig_ignored(SHUTDOWN3_SIGNAL)) { + sigaddset(&unblocked_sigs, SHUTDOWN3_SIGNAL); + sigaddset(&allowdebug_blocked_sigs, SHUTDOWN3_SIGNAL); + } + } + // Fill in signals that are blocked by all but the VM thread. + sigemptyset(&vm_sigs); + if (!ReduceSignalUsage) + sigaddset(&vm_sigs, BREAK_SIGNAL); + debug_only(signal_sets_initialized = true); + + // For diagnostics only used in run_periodic_checks + sigemptyset(&check_signal_done); +} + +// These are signals that are unblocked while a thread is running Java. +// (For some reason, they get blocked by default.) +sigset_t* os::Solaris::unblocked_signals() { + assert(signal_sets_initialized, "Not initialized"); + return &unblocked_sigs; +} + +// These are the signals that are blocked while a (non-VM) thread is +// running Java. Only the VM thread handles these signals. +sigset_t* os::Solaris::vm_signals() { + assert(signal_sets_initialized, "Not initialized"); + return &vm_sigs; +} + +// These are signals that are blocked during cond_wait to allow debugger in +sigset_t* os::Solaris::allowdebug_blocked_signals() { + assert(signal_sets_initialized, "Not initialized"); + return &allowdebug_blocked_sigs; +} + +// First crack at OS-specific initialization, from inside the new thread. +void os::initialize_thread() { + int r = thr_main() ; + guarantee (r == 0 || r == 1, "CR6501650 or CR6493689") ; + if (r) { + JavaThread* jt = (JavaThread *)Thread::current(); + assert(jt != NULL,"Sanity check"); + size_t stack_size; + address base = jt->stack_base(); + if (Arguments::created_by_java_launcher()) { + // Use 2MB to allow for Solaris 7 64 bit mode. + stack_size = JavaThread::stack_size_at_create() == 0 + ? 2048*K : JavaThread::stack_size_at_create(); + + // There are rare cases when we may have already used more than + // the basic stack size allotment before this method is invoked. + // Attempt to allow for a normally sized java_stack. + size_t current_stack_offset = (size_t)(base - (address)&stack_size); + stack_size += ReservedSpace::page_align_size_down(current_stack_offset); + } else { + // 6269555: If we were not created by a Java launcher, i.e. if we are + // running embedded in a native application, treat the primordial thread + // as much like a native attached thread as possible. This means using + // the current stack size from thr_stksegment(), unless it is too large + // to reliably setup guard pages. A reasonable max size is 8MB. + size_t current_size = current_stack_size(); + // This should never happen, but just in case.... + if (current_size == 0) current_size = 2 * K * K; + stack_size = current_size > (8 * K * K) ? (8 * K * K) : current_size; + } + address bottom = (address)align_size_up((intptr_t)(base - stack_size), os::vm_page_size());; + stack_size = (size_t)(base - bottom); + + assert(stack_size > 0, "Stack size calculation problem"); + + if (stack_size > jt->stack_size()) { + NOT_PRODUCT( + struct rlimit limits; + getrlimit(RLIMIT_STACK, &limits); + size_t size = adjust_stack_size(base, (size_t)limits.rlim_cur); + assert(size >= jt->stack_size(), "Stack size problem in main thread"); + ) + tty->print_cr( + "Stack size of %d Kb exceeds current limit of %d Kb.\n" + "(Stack sizes are rounded up to a multiple of the system page size.)\n" + "See limit(1) to increase the stack size limit.", + stack_size / K, jt->stack_size() / K); + vm_exit(1); + } + assert(jt->stack_size() >= stack_size, + "Attempt to map more stack than was allocated"); + jt->set_stack_size(stack_size); + } + + // 5/22/01: Right now alternate signal stacks do not handle + // throwing stack overflow exceptions, see bug 4463178 + // Until a fix is found for this, T2 will NOT imply alternate signal + // stacks. + // If using T2 libthread threads, install an alternate signal stack. + // Because alternate stacks associate with LWPs on Solaris, + // see sigaltstack(2), if using UNBOUND threads, or if UseBoundThreads + // we prefer to explicitly stack bang. + // If not using T2 libthread, but using UseBoundThreads any threads + // (primordial thread, jni_attachCurrentThread) we do not create, + // probably are not bound, therefore they can not have an alternate + // signal stack. Since our stack banging code is generated and + // is shared across threads, all threads must be bound to allow + // using alternate signal stacks. The alternative is to interpose + // on _lwp_create to associate an alt sig stack with each LWP, + // and this could be a problem when the JVM is embedded. + // We would prefer to use alternate signal stacks with T2 + // Since there is currently no accurate way to detect T2 + // we do not. Assuming T2 when running T1 causes sig 11s or assertions + // on installing alternate signal stacks + + + // 05/09/03: removed alternate signal stack support for Solaris + // The alternate signal stack mechanism is no longer needed to + // handle stack overflow. This is now handled by allocating + // guard pages (red zone) and stackbanging. + // Initially the alternate signal stack mechanism was removed because + // it did not work with T1 llibthread. Alternate + // signal stacks MUST have all threads bound to lwps. Applications + // can create their own threads and attach them without their being + // bound under T1. This is frequently the case for the primordial thread. + // If we were ever to reenable this mechanism we would need to + // use the dynamic check for T2 libthread. + + os::Solaris::init_thread_fpu_state(); +} + + + +// Free Solaris resources related to the OSThread +void os::free_thread(OSThread* osthread) { + assert(osthread != NULL, "os::free_thread but osthread not set"); + + + // We are told to free resources of the argument thread, + // but we can only really operate on the current thread. + // The main thread must take the VMThread down synchronously + // before the main thread exits and frees up CodeHeap + guarantee((Thread::current()->osthread() == osthread + || (osthread == VMThread::vm_thread()->osthread())), "os::free_thread but not current thread"); + if (Thread::current()->osthread() == osthread) { + // Restore caller's signal mask + sigset_t sigmask = osthread->caller_sigmask(); + thr_sigsetmask(SIG_SETMASK, &sigmask, NULL); + } + delete osthread; +} + +void os::pd_start_thread(Thread* thread) { + int status = thr_continue(thread->osthread()->thread_id()); + assert_status(status == 0, status, "thr_continue failed"); +} + + +intx os::current_thread_id() { + return (intx)thr_self(); +} + +static pid_t _initial_pid = 0; + +int os::current_process_id() { + return (int)(_initial_pid ? _initial_pid : getpid()); +} + +int os::allocate_thread_local_storage() { + // %%% in Win32 this allocates a memory segment pointed to by a + // register. Dan Stein can implement a similar feature in + // Solaris. Alternatively, the VM can do the same thing + // explicitly: malloc some storage and keep the pointer in a + // register (which is part of the thread's context) (or keep it + // in TLS). + // %%% In current versions of Solaris, thr_self and TSD can + // be accessed via short sequences of displaced indirections. + // The value of thr_self is available as %g7(36). + // The value of thr_getspecific(k) is stored in %g7(12)(4)(k*4-4), + // assuming that the current thread already has a value bound to k. + // It may be worth experimenting with such access patterns, + // and later having the parameters formally exported from a Solaris + // interface. I think, however, that it will be faster to + // maintain the invariant that %g2 always contains the + // JavaThread in Java code, and have stubs simply + // treat %g2 as a caller-save register, preserving it in a %lN. + thread_key_t tk; + if (thr_keycreate( &tk, NULL ) ) + fatal1("os::allocate_thread_local_storage: thr_keycreate failed (%s)", strerror(errno)); + return int(tk); +} + +void os::free_thread_local_storage(int index) { + // %%% don't think we need anything here + // if ( pthread_key_delete((pthread_key_t) tk) ) + // fatal("os::free_thread_local_storage: pthread_key_delete failed"); +} + +#define SMALLINT 32 // libthread allocate for tsd_common is a version specific + // small number - point is NO swap space available +void os::thread_local_storage_at_put(int index, void* value) { + // %%% this is used only in threadLocalStorage.cpp + if (thr_setspecific((thread_key_t)index, value)) { + if (errno == ENOMEM) { + vm_exit_out_of_memory(SMALLINT, "thr_setspecific: out of swap space"); + } else { + fatal1("os::thread_local_storage_at_put: thr_setspecific failed (%s)", strerror(errno)); + } + } else { + ThreadLocalStorage::set_thread_in_slot ((Thread *) value) ; + } +} + +// This function could be called before TLS is initialized, for example, when +// VM receives an async signal or when VM causes a fatal error during +// initialization. Return NULL if thr_getspecific() fails. +void* os::thread_local_storage_at(int index) { + // %%% this is used only in threadLocalStorage.cpp + void* r = NULL; + return thr_getspecific((thread_key_t)index, &r) != 0 ? NULL : r; +} + + +const int NANOSECS_PER_MILLISECS = 1000000; +// gethrtime can move backwards if read from one cpu and then a different cpu +// getTimeNanos is guaranteed to not move backward on Solaris +// local spinloop created as faster for a CAS on an int than +// a CAS on a 64bit jlong. Also Atomic::cmpxchg for jlong is not +// supported on sparc v8 or pre supports_cx8 intel boxes. +// oldgetTimeNanos for systems which do not support CAS on 64bit jlong +// i.e. sparc v8 and pre supports_cx8 (i486) intel boxes +inline hrtime_t oldgetTimeNanos() { + int gotlock = LOCK_INVALID; + hrtime_t newtime = gethrtime(); + + for (;;) { +// grab lock for max_hrtime + int curlock = max_hrtime_lock; + if (curlock & LOCK_BUSY) continue; + if (gotlock = Atomic::cmpxchg(LOCK_BUSY, &max_hrtime_lock, LOCK_FREE) != LOCK_FREE) continue; + if (newtime > max_hrtime) { + max_hrtime = newtime; + } else { + newtime = max_hrtime; + } + // release lock + max_hrtime_lock = LOCK_FREE; + return newtime; + } +} +// gethrtime can move backwards if read from one cpu and then a different cpu +// getTimeNanos is guaranteed to not move backward on Solaris +inline hrtime_t getTimeNanos() { + if (VM_Version::supports_cx8()) { + bool retry = false; + hrtime_t newtime = gethrtime(); + hrtime_t oldmaxtime = max_hrtime; + hrtime_t retmaxtime = oldmaxtime; + while ((newtime > retmaxtime) && (retry == false || retmaxtime != oldmaxtime)) { + oldmaxtime = retmaxtime; + retmaxtime = Atomic::cmpxchg(newtime, (volatile jlong *)&max_hrtime, oldmaxtime); + retry = true; + } + return (newtime > retmaxtime) ? newtime : retmaxtime; + } else { + return oldgetTimeNanos(); + } +} + +// Time since start-up in seconds to a fine granularity. +// Used by VMSelfDestructTimer and the MemProfiler. +double os::elapsedTime() { + return (double)(getTimeNanos() - first_hrtime) / (double)hrtime_hz; +} + +jlong os::elapsed_counter() { + return (jlong)(getTimeNanos() - first_hrtime); +} + +jlong os::elapsed_frequency() { + return hrtime_hz; +} + +// Return the real, user, and system times in seconds from an +// arbitrary fixed point in the past. +bool os::getTimesSecs(double* process_real_time, + double* process_user_time, + double* process_system_time) { + struct tms ticks; + clock_t real_ticks = times(&ticks); + + if (real_ticks == (clock_t) (-1)) { + return false; + } else { + double ticks_per_second = (double) clock_tics_per_sec; + *process_user_time = ((double) ticks.tms_utime) / ticks_per_second; + *process_system_time = ((double) ticks.tms_stime) / ticks_per_second; + // For consistency return the real time from getTimeNanos() + // converted to seconds. + *process_real_time = ((double) getTimeNanos()) / ((double) NANOUNITS); + + return true; + } +} + +// Used internally for comparisons only +// getTimeMillis guaranteed to not move backwards on Solaris +jlong getTimeMillis() { + jlong nanotime = getTimeNanos(); + return (jlong)(nanotime / NANOSECS_PER_MILLISECS); +} + +jlong os::timeofday() { + timeval t; + if (gettimeofday( &t, NULL) == -1) + fatal1("timeofday: gettimeofday (%s)", strerror(errno)); + return jlong(t.tv_sec) * 1000 + jlong(t.tv_usec) / 1000; +} + +// Must return millis since Jan 1 1970 for JVM_CurrentTimeMillis +// _use_global_time is only set if CacheTimeMillis is true +jlong os::javaTimeMillis() { + return (_use_global_time ? read_global_time() : timeofday()); +} + +jlong os::javaTimeNanos() { + return (jlong)getTimeNanos(); +} + +void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { + info_ptr->max_value = ALL_64_BITS; // gethrtime() uses all 64 bits + info_ptr->may_skip_backward = false; // not subject to resetting or drifting + info_ptr->may_skip_forward = false; // not subject to resetting or drifting + info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time +} + +char * os::local_time_string(char *buf, size_t buflen) { + struct tm t; + time_t long_time; + time(&long_time); + localtime_r(&long_time, &t); + jio_snprintf(buf, buflen, "%d-%02d-%02d %02d:%02d:%02d", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec); + return buf; +} + +// Note: os::shutdown() might be called very early during initialization, or +// called from signal handler. Before adding something to os::shutdown(), make +// sure it is async-safe and can handle partially initialized VM. +void os::shutdown() { + + // allow PerfMemory to attempt cleanup of any persistent resources + perfMemory_exit(); + + // needs to remove object in file system + AttachListener::abort(); + + // flush buffered output, finish log files + ostream_abort(); + + // Check for abort hook + abort_hook_t abort_hook = Arguments::abort_hook(); + if (abort_hook != NULL) { + abort_hook(); + } +} + +// Note: os::abort() might be called very early during initialization, or +// called from signal handler. Before adding something to os::abort(), make +// sure it is async-safe and can handle partially initialized VM. +void os::abort(bool dump_core) { + os::shutdown(); + if (dump_core) { +#ifndef PRODUCT + fdStream out(defaultStream::output_fd()); + out.print_raw("Current thread is "); + char buf[16]; + jio_snprintf(buf, sizeof(buf), UINTX_FORMAT, os::current_thread_id()); + out.print_raw_cr(buf); + out.print_raw_cr("Dumping core ..."); +#endif + ::abort(); // dump core (for debugging) + } + + ::exit(1); +} + +// Die immediately, no exit hook, no abort hook, no cleanup. +void os::die() { + _exit(-1); +} + +// unused +void os::set_error_file(const char *logfile) {} + +// DLL functions + +const char* os::dll_file_extension() { return ".so"; } + +const char* os::get_temp_directory() { return "/tmp/"; } + +const char* os::get_current_directory(char *buf, int buflen) { + return getcwd(buf, buflen); +} + +// check if addr is inside libjvm[_g].so +bool os::address_is_in_vm(address addr) { + static address libjvm_base_addr; + Dl_info dlinfo; + + if (libjvm_base_addr == NULL) { + dladdr(CAST_FROM_FN_PTR(void *, os::address_is_in_vm), &dlinfo); + libjvm_base_addr = (address)dlinfo.dli_fbase; + assert(libjvm_base_addr !=NULL, "Cannot obtain base address for libjvm"); + } + + if (dladdr((void *)addr, &dlinfo)) { + if (libjvm_base_addr == (address)dlinfo.dli_fbase) return true; + } + + return false; +} + +typedef int (*dladdr1_func_type) (void *, Dl_info *, void **, int); +static dladdr1_func_type dladdr1_func = NULL; + +bool os::dll_address_to_function_name(address addr, char *buf, + int buflen, int * offset) { + Dl_info dlinfo; + + // dladdr1_func was initialized in os::init() + if (dladdr1_func){ + // yes, we have dladdr1 + + // Support for dladdr1 is checked at runtime; it may be + // available even if the vm is built on a machine that does + // not have dladdr1 support. Make sure there is a value for + // RTLD_DL_SYMENT. + #ifndef RTLD_DL_SYMENT + #define RTLD_DL_SYMENT 1 + #endif + Sym * info; + if (dladdr1_func((void *)addr, &dlinfo, (void **)&info, + RTLD_DL_SYMENT)) { + if (buf) jio_snprintf(buf, buflen, "%s", dlinfo.dli_sname); + if (offset) *offset = addr - (address)dlinfo.dli_saddr; + + // check if the returned symbol really covers addr + return ((char *)dlinfo.dli_saddr + info->st_size > (char *)addr); + } else { + if (buf) buf[0] = '\0'; + if (offset) *offset = -1; + return false; + } + } else { + // no, only dladdr is available + if(dladdr((void *)addr, &dlinfo)) { + if (buf) jio_snprintf(buf, buflen, dlinfo.dli_sname); + if (offset) *offset = addr - (address)dlinfo.dli_saddr; + return true; + } else { + if (buf) buf[0] = '\0'; + if (offset) *offset = -1; + return false; + } + } +} + +bool os::dll_address_to_library_name(address addr, char* buf, + int buflen, int* offset) { + Dl_info dlinfo; + + if (dladdr((void*)addr, &dlinfo)){ + if (buf) jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); + if (offset) *offset = addr - (address)dlinfo.dli_fbase; + return true; + } else { + if (buf) buf[0] = '\0'; + if (offset) *offset = -1; + return false; + } +} + +// Prints the names and full paths of all opened dynamic libraries +// for current process +void os::print_dll_info(outputStream * st) { + Dl_info dli; + void *handle; + Link_map *map; + Link_map *p; + + st->print_cr("Dynamic libraries:"); st->flush(); + + if (!dladdr(CAST_FROM_FN_PTR(void *, os::print_dll_info), &dli)) { + st->print_cr("Error: Cannot print dynamic libraries."); + return; + } + handle = dlopen(dli.dli_fname, RTLD_LAZY); + if (handle == NULL) { + st->print_cr("Error: Cannot print dynamic libraries."); + return; + } + dlinfo(handle, RTLD_DI_LINKMAP, &map); + if (map == NULL) { + st->print_cr("Error: Cannot print dynamic libraries."); + return; + } + + while (map->l_prev != NULL) + map = map->l_prev; + + while (map != NULL) { + st->print_cr(PTR_FORMAT " \t%s", map->l_addr, map->l_name); + map = map->l_next; + } + + dlclose(handle); +} + + // Loads .dll/.so and + // in case of error it checks if .dll/.so was built for the + // same architecture as Hotspot is running on + +void * os::dll_load(const char *filename, char *ebuf, int ebuflen) +{ + void * result= ::dlopen(filename, RTLD_LAZY); + if (result != NULL) { + // Successful loading + return result; + } + + Elf32_Ehdr elf_head; + + // Read system error message into ebuf + // It may or may not be overwritten below + ::strncpy(ebuf, ::dlerror(), ebuflen-1); + ebuf[ebuflen-1]='\0'; + int diag_msg_max_length=ebuflen-strlen(ebuf); + char* diag_msg_buf=ebuf+strlen(ebuf); + + if (diag_msg_max_length==0) { + // No more space in ebuf for additional diagnostics message + return NULL; + } + + + int file_descriptor= ::open(filename, O_RDONLY | O_NONBLOCK); + + if (file_descriptor < 0) { + // Can't open library, report dlerror() message + return NULL; + } + + bool failed_to_read_elf_head= + (sizeof(elf_head)!= + (::read(file_descriptor, &elf_head,sizeof(elf_head)))) ; + + ::close(file_descriptor); + if (failed_to_read_elf_head) { + // file i/o error - report dlerror() msg + return NULL; + } + + typedef struct { + Elf32_Half code; // Actual value as defined in elf.h + Elf32_Half compat_class; // Compatibility of archs at VM's sense + char elf_class; // 32 or 64 bit + char endianess; // MSB or LSB + char* name; // String representation + } arch_t; + + static const arch_t arch_array[]={ + {EM_386, EM_386, ELFCLASS32, ELFDATA2LSB, (char*)"IA 32"}, + {EM_486, EM_386, ELFCLASS32, ELFDATA2LSB, (char*)"IA 32"}, + {EM_IA_64, EM_IA_64, ELFCLASS64, ELFDATA2LSB, (char*)"IA 64"}, + {EM_X86_64, EM_X86_64, ELFCLASS64, ELFDATA2LSB, (char*)"AMD 64"}, + {EM_SPARC, EM_SPARC, ELFCLASS32, ELFDATA2MSB, (char*)"Sparc 32"}, + {EM_SPARC32PLUS, EM_SPARC, ELFCLASS32, ELFDATA2MSB, (char*)"Sparc 32"}, + {EM_SPARCV9, EM_SPARCV9, ELFCLASS64, ELFDATA2MSB, (char*)"Sparc v9 64"}, + {EM_PPC, EM_PPC, ELFCLASS32, ELFDATA2MSB, (char*)"Power PC 32"}, + {EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64"} + }; + + #if (defined IA32) + static Elf32_Half running_arch_code=EM_386; + #elif (defined AMD64) + static Elf32_Half running_arch_code=EM_X86_64; + #elif (defined IA64) + static Elf32_Half running_arch_code=EM_IA_64; + #elif (defined __sparc) && (defined _LP64) + static Elf32_Half running_arch_code=EM_SPARCV9; + #elif (defined __sparc) && (!defined _LP64) + static Elf32_Half running_arch_code=EM_SPARC; + #elif (defined __powerpc64__) + static Elf32_Half running_arch_code=EM_PPC64; + #elif (defined __powerpc__) + static Elf32_Half running_arch_code=EM_PPC; + #else + #error Method os::dll_load requires that one of following is defined:\ + IA32, AMD64, IA64, __sparc, __powerpc__ + #endif + + // Identify compatability class for VM's architecture and library's architecture + // Obtain string descriptions for architectures + + arch_t lib_arch={elf_head.e_machine,0,elf_head.e_ident[EI_CLASS], elf_head.e_ident[EI_DATA], NULL}; + int running_arch_index=-1; + + for (unsigned int i=0 ; i < ARRAY_SIZE(arch_array) ; i++ ) { + if (running_arch_code == arch_array[i].code) { + running_arch_index = i; + } + if (lib_arch.code == arch_array[i].code) { + lib_arch.compat_class = arch_array[i].compat_class; + lib_arch.name = arch_array[i].name; + } + } + + assert(running_arch_index != -1, + "Didn't find running architecture code (running_arch_code) in arch_array"); + if (running_arch_index == -1) { + // Even though running architecture detection failed + // we may still continue with reporting dlerror() message + return NULL; + } + + if (lib_arch.endianess != arch_array[running_arch_index].endianess) { + ::snprintf(diag_msg_buf, diag_msg_max_length-1," (Possible cause: endianness mismatch)"); + return NULL; + } + + if (lib_arch.elf_class != arch_array[running_arch_index].elf_class) { + ::snprintf(diag_msg_buf, diag_msg_max_length-1," (Possible cause: architecture word width mismatch)"); + return NULL; + } + + if (lib_arch.compat_class != arch_array[running_arch_index].compat_class) { + if ( lib_arch.name!=NULL ) { + ::snprintf(diag_msg_buf, diag_msg_max_length-1, + " (Possible cause: can't load %s-bit .so on a %s-bit platform)", + lib_arch.name, arch_array[running_arch_index].name); + } else { + ::snprintf(diag_msg_buf, diag_msg_max_length-1, + " (Possible cause: can't load this .so (machine code=0x%x) on a %s-bit platform)", + lib_arch.code, + arch_array[running_arch_index].name); + } + } + + return NULL; +} + + + +bool _print_ascii_file(const char* filename, outputStream* st) { + int fd = open(filename, O_RDONLY); + if (fd == -1) { + return false; + } + + char buf[32]; + int bytes; + while ((bytes = read(fd, buf, sizeof(buf))) > 0) { + st->print_raw(buf, bytes); + } + + close(fd); + + return true; +} + +void os::print_os_info(outputStream* st) { + st->print("OS:"); + + if (!_print_ascii_file("/etc/release", st)) { + st->print("Solaris"); + } + st->cr(); + + // kernel + st->print("uname:"); + struct utsname name; + uname(&name); + st->print(name.sysname); st->print(" "); + st->print(name.release); st->print(" "); + st->print(name.version); st->print(" "); + st->print(name.machine); + + // libthread + if (os::Solaris::T2_libthread()) st->print(" (T2 libthread)"); + else st->print(" (T1 libthread)"); + st->cr(); + + // rlimit + st->print("rlimit:"); + struct rlimit rlim; + + st->print(" STACK "); + getrlimit(RLIMIT_STACK, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + + st->print(", CORE "); + getrlimit(RLIMIT_CORE, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + + st->print(", NOFILE "); + getrlimit(RLIMIT_NOFILE, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%d", rlim.rlim_cur); + + st->print(", AS "); + getrlimit(RLIMIT_AS, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + st->cr(); + + // load average + st->print("load average:"); + double loadavg[3]; + os::loadavg(loadavg, 3); + st->print("%0.02f %0.02f %0.02f", loadavg[0], loadavg[1], loadavg[2]); + st->cr(); +} + + +static bool check_addr0(outputStream* st) { + jboolean status = false; + int fd = open("/proc/self/map",O_RDONLY); + if (fd >= 0) { + prmap_t p; + while(read(fd, &p, sizeof(p)) > 0) { + if (p.pr_vaddr == 0x0) { + st->print("Warning: Address: 0x%x, Size: %dK, ",p.pr_vaddr, p.pr_size/1024, p.pr_mapname); + st->print("Mapped file: %s, ", p.pr_mapname[0] == '\0' ? "None" : p.pr_mapname); + st->print("Access:"); + st->print("%s",(p.pr_mflags & MA_READ) ? "r" : "-"); + st->print("%s",(p.pr_mflags & MA_WRITE) ? "w" : "-"); + st->print("%s",(p.pr_mflags & MA_EXEC) ? "x" : "-"); + st->cr(); + status = true; + } + close(fd); + } + } + return status; +} + +void os::print_memory_info(outputStream* st) { + st->print("Memory:"); + st->print(" %dk page", os::vm_page_size()>>10); + st->print(", physical " UINT64_FORMAT "k", os::physical_memory()>>10); + st->print("(" UINT64_FORMAT "k free)", os::available_memory() >> 10); + st->cr(); + (void) check_addr0(st); +} + +// Taken from /usr/include/sys/machsig.h Supposed to be architecture specific +// but they're the same for all the solaris architectures that we support. +const char *ill_names[] = { "ILL0", "ILL_ILLOPC", "ILL_ILLOPN", "ILL_ILLADR", + "ILL_ILLTRP", "ILL_PRVOPC", "ILL_PRVREG", + "ILL_COPROC", "ILL_BADSTK" }; + +const char *fpe_names[] = { "FPE0", "FPE_INTDIV", "FPE_INTOVF", "FPE_FLTDIV", + "FPE_FLTOVF", "FPE_FLTUND", "FPE_FLTRES", + "FPE_FLTINV", "FPE_FLTSUB" }; + +const char *segv_names[] = { "SEGV0", "SEGV_MAPERR", "SEGV_ACCERR" }; + +const char *bus_names[] = { "BUS0", "BUS_ADRALN", "BUS_ADRERR", "BUS_OBJERR" }; + +void os::print_siginfo(outputStream* st, void* siginfo) { + st->print("siginfo:"); + + const int buflen = 100; + char buf[buflen]; + siginfo_t *si = (siginfo_t*)siginfo; + st->print("si_signo=%s: ", os::exception_name(si->si_signo, buf, buflen)); + char *err = strerror(si->si_errno); + if (si->si_errno != 0 && err != NULL) { + st->print("si_errno=%s", err); + } else { + st->print("si_errno=%d", si->si_errno); + } + const int c = si->si_code; + assert(c > 0, "unexpected si_code"); + switch (si->si_signo) { + case SIGILL: + st->print(", si_code=%d (%s)", c, c > 8 ? "" : ill_names[c]); + st->print(", si_addr=" PTR_FORMAT, si->si_addr); + break; + case SIGFPE: + st->print(", si_code=%d (%s)", c, c > 9 ? "" : fpe_names[c]); + st->print(", si_addr=" PTR_FORMAT, si->si_addr); + break; + case SIGSEGV: + st->print(", si_code=%d (%s)", c, c > 2 ? "" : segv_names[c]); + st->print(", si_addr=" PTR_FORMAT, si->si_addr); + break; + case SIGBUS: + st->print(", si_code=%d (%s)", c, c > 3 ? "" : bus_names[c]); + st->print(", si_addr=" PTR_FORMAT, si->si_addr); + break; + default: + st->print(", si_code=%d", si->si_code); + // no si_addr + } + + if ((si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && + UseSharedSpaces) { + FileMapInfo* mapinfo = FileMapInfo::current_info(); + if (mapinfo->is_in_shared_space(si->si_addr)) { + st->print("\n\nError accessing class data sharing archive." \ + " Mapped file inaccessible during execution, " \ + " possible disk/network problem."); + } + } + st->cr(); +} + +// Moved from whole group, because we need them here for diagnostic +// prints. +#define OLDMAXSIGNUM 32 +static int Maxsignum = 0; +static int *ourSigFlags = NULL; + +extern "C" void sigINTRHandler(int, siginfo_t*, void*); + +int os::Solaris::get_our_sigflags(int sig) { + assert(ourSigFlags!=NULL, "signal data structure not initialized"); + assert(sig > 0 && sig < Maxsignum, "vm signal out of expected range"); + return ourSigFlags[sig]; +} + +void os::Solaris::set_our_sigflags(int sig, int flags) { + assert(ourSigFlags!=NULL, "signal data structure not initialized"); + assert(sig > 0 && sig < Maxsignum, "vm signal out of expected range"); + ourSigFlags[sig] = flags; +} + + +static const char* get_signal_handler_name(address handler, + char* buf, int buflen) { + int offset; + bool found = os::dll_address_to_library_name(handler, buf, buflen, &offset); + if (found) { + // skip directory names + const char *p1, *p2; + p1 = buf; + size_t len = strlen(os::file_separator()); + while ((p2 = strstr(p1, os::file_separator())) != NULL) p1 = p2 + len; + jio_snprintf(buf, buflen, "%s+0x%x", p1, offset); + } else { + jio_snprintf(buf, buflen, PTR_FORMAT, handler); + } + return buf; +} + +static void print_signal_handler(outputStream* st, int sig, + char* buf, size_t buflen) { + struct sigaction sa; + + sigaction(sig, NULL, &sa); + + st->print("%s: ", os::exception_name(sig, buf, buflen)); + + address handler = (sa.sa_flags & SA_SIGINFO) + ? CAST_FROM_FN_PTR(address, sa.sa_sigaction) + : CAST_FROM_FN_PTR(address, sa.sa_handler); + + if (handler == CAST_FROM_FN_PTR(address, SIG_DFL)) { + st->print("SIG_DFL"); + } else if (handler == CAST_FROM_FN_PTR(address, SIG_IGN)) { + st->print("SIG_IGN"); + } else { + st->print("[%s]", get_signal_handler_name(handler, buf, buflen)); + } + + st->print(", sa_mask[0]=" PTR32_FORMAT, *(uint32_t*)&sa.sa_mask); + + address rh = VMError::get_resetted_sighandler(sig); + // May be, handler was resetted by VMError? + if(rh != NULL) { + handler = rh; + sa.sa_flags = VMError::get_resetted_sigflags(sig); + } + + st->print(", sa_flags=" PTR32_FORMAT, sa.sa_flags); + + // Check: is it our handler? + if(handler == CAST_FROM_FN_PTR(address, signalHandler) || + handler == CAST_FROM_FN_PTR(address, sigINTRHandler)) { + // It is our signal handler + // check for flags + if(sa.sa_flags != os::Solaris::get_our_sigflags(sig)) { + st->print( + ", flags was changed from " PTR32_FORMAT ", consider using jsig library", + os::Solaris::get_our_sigflags(sig)); + } + } + st->cr(); +} + +void os::print_signal_handlers(outputStream* st, char* buf, size_t buflen) { + st->print_cr("Signal Handlers:"); + print_signal_handler(st, SIGSEGV, buf, buflen); + print_signal_handler(st, SIGBUS , buf, buflen); + print_signal_handler(st, SIGFPE , buf, buflen); + print_signal_handler(st, SIGPIPE, buf, buflen); + print_signal_handler(st, SIGXFSZ, buf, buflen); + print_signal_handler(st, SIGILL , buf, buflen); + print_signal_handler(st, INTERRUPT_SIGNAL, buf, buflen); + print_signal_handler(st, ASYNC_SIGNAL, buf, buflen); + print_signal_handler(st, BREAK_SIGNAL, buf, buflen); + print_signal_handler(st, SHUTDOWN1_SIGNAL , buf, buflen); + print_signal_handler(st, SHUTDOWN2_SIGNAL , buf, buflen); + print_signal_handler(st, SHUTDOWN3_SIGNAL, buf, buflen); + print_signal_handler(st, os::Solaris::SIGinterrupt(), buf, buflen); + print_signal_handler(st, os::Solaris::SIGasync(), buf, buflen); +} + +static char saved_jvm_path[MAXPATHLEN] = { 0 }; + +// Find the full path to the current module, libjvm.so or libjvm_g.so +void os::jvm_path(char *buf, jint buflen) { + // Error checking. + if (buflen < MAXPATHLEN) { + assert(false, "must use a large-enough buffer"); + buf[0] = '\0'; + return; + } + // Lazy resolve the path to current module. + if (saved_jvm_path[0] != 0) { + strcpy(buf, saved_jvm_path); + return; + } + + Dl_info dlinfo; + int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo); + assert(ret != 0, "cannot locate libjvm"); + realpath((char *)dlinfo.dli_fname, buf); + + if (strcmp(Arguments::sun_java_launcher(), "gamma") == 0) { + // Support for the gamma launcher. Typical value for buf is + // "/jre/lib///libjvm.so". If "/jre/lib/" appears at + // the right place in the string, then assume we are installed in a JDK and + // we're done. Otherwise, check for a JAVA_HOME environment variable and fix + // up the path so it looks like libjvm.so is installed there (append a + // fake suffix hotspot/libjvm.so). + const char *p = buf + strlen(buf) - 1; + for (int count = 0; p > buf && count < 5; ++count) { + for (--p; p > buf && *p != '/'; --p) + /* empty */ ; + } + + if (strncmp(p, "/jre/lib/", 9) != 0) { + // Look for JAVA_HOME in the environment. + char* java_home_var = ::getenv("JAVA_HOME"); + if (java_home_var != NULL && java_home_var[0] != 0) { + char cpu_arch[12]; + sysinfo(SI_ARCHITECTURE, cpu_arch, sizeof(cpu_arch)); +#ifdef _LP64 + // If we are on sparc running a 64-bit vm, look in jre/lib/sparcv9. + if (strcmp(cpu_arch, "sparc") == 0) { + strcat(cpu_arch, "v9"); + } else if (strcmp(cpu_arch, "i386") == 0) { + strcpy(cpu_arch, "amd64"); + } +#endif + // Check the current module name "libjvm.so" or "libjvm_g.so". + p = strrchr(buf, '/'); + assert(strstr(p, "/libjvm") == p, "invalid library name"); + p = strstr(p, "_g") ? "_g" : ""; + + realpath(java_home_var, buf); + sprintf(buf + strlen(buf), "/jre/lib/%s", cpu_arch); + if (0 == access(buf, F_OK)) { + // Use current module name "libjvm[_g].so" instead of + // "libjvm"debug_only("_g")".so" since for fastdebug version + // we should have "libjvm.so" but debug_only("_g") adds "_g"! + // It is used when we are choosing the HPI library's name + // "libhpi[_g].so" in hpi::initialize_get_interface(). + sprintf(buf + strlen(buf), "/hotspot/libjvm%s.so", p); + } else { + // Go back to path of .so + realpath((char *)dlinfo.dli_fname, buf); + } + } + } + } + + strcpy(saved_jvm_path, buf); +} + + +void os::print_jni_name_prefix_on(outputStream* st, int args_size) { + // no prefix required, not even "_" +} + + +void os::print_jni_name_suffix_on(outputStream* st, int args_size) { + // no suffix required +} + + +// sun.misc.Signal + +extern "C" { + static void UserHandler(int sig, void *siginfo, void *context) { + // Ctrl-C is pressed during error reporting, likely because the error + // handler fails to abort. Let VM die immediately. + if (sig == SIGINT && is_error_reported()) { + os::die(); + } + + os::signal_notify(sig); + // We do not need to reinstate the signal handler each time... + } +} + +void* os::user_handler() { + return CAST_FROM_FN_PTR(void*, UserHandler); +} + +extern "C" { + typedef void (*sa_handler_t)(int); + typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); +} + +void* os::signal(int signal_number, void* handler) { + struct sigaction sigAct, oldSigAct; + sigfillset(&(sigAct.sa_mask)); + sigAct.sa_flags = SA_RESTART & ~SA_RESETHAND; + sigAct.sa_handler = CAST_TO_FN_PTR(sa_handler_t, handler); + + if (sigaction(signal_number, &sigAct, &oldSigAct)) + // -1 means registration failed + return (void *)-1; + + return CAST_FROM_FN_PTR(void*, oldSigAct.sa_handler); +} + +void os::signal_raise(int signal_number) { + raise(signal_number); +} + +/* + * The following code is moved from os.cpp for making this + * code platform specific, which it is by its very nature. + */ + +// a counter for each possible signal value +static int Sigexit = 0; +static int Maxlibjsigsigs; +static jint *pending_signals = NULL; +static int *preinstalled_sigs = NULL; +static struct sigaction *chainedsigactions = NULL; +static sema_t sig_sem; +typedef int (*version_getting_t)(); +version_getting_t os::Solaris::get_libjsig_version = NULL; +static int libjsigversion = NULL; + +int os::sigexitnum_pd() { + assert(Sigexit > 0, "signal memory not yet initialized"); + return Sigexit; +} + +void os::Solaris::init_signal_mem() { + // Initialize signal structures + Maxsignum = SIGRTMAX; + Sigexit = Maxsignum+1; + assert(Maxsignum >0, "Unable to obtain max signal number"); + + Maxlibjsigsigs = Maxsignum; + + // pending_signals has one int per signal + // The additional signal is for SIGEXIT - exit signal to signal_thread + pending_signals = (jint *)os::malloc(sizeof(jint) * (Sigexit+1)); + memset(pending_signals, 0, (sizeof(jint) * (Sigexit+1))); + + if (UseSignalChaining) { + chainedsigactions = (struct sigaction *)malloc(sizeof(struct sigaction) + * (Maxsignum + 1)); + memset(chainedsigactions, 0, (sizeof(struct sigaction) * (Maxsignum + 1))); + preinstalled_sigs = (int *)os::malloc(sizeof(int) * (Maxsignum + 1)); + memset(preinstalled_sigs, 0, (sizeof(int) * (Maxsignum + 1))); + } + ourSigFlags = (int*)malloc(sizeof(int) * (Maxsignum + 1 )); + memset(ourSigFlags, 0, sizeof(int) * (Maxsignum + 1)); +} + +void os::signal_init_pd() { + int ret; + + ret = ::sema_init(&sig_sem, 0, NULL, NULL); + assert(ret == 0, "sema_init() failed"); +} + +void os::signal_notify(int signal_number) { + int ret; + + Atomic::inc(&pending_signals[signal_number]); + ret = ::sema_post(&sig_sem); + assert(ret == 0, "sema_post() failed"); +} + +static int check_pending_signals(bool wait_for_signal) { + int ret; + while (true) { + for (int i = 0; i < Sigexit + 1; i++) { + jint n = pending_signals[i]; + if (n > 0 && n == Atomic::cmpxchg(n - 1, &pending_signals[i], n)) { + return i; + } + } + if (!wait_for_signal) { + return -1; + } + JavaThread *thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + bool threadIsSuspended; + do { + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() + while((ret = ::sema_wait(&sig_sem)) == EINTR) + ; + assert(ret == 0, "sema_wait() failed"); + + // were we externally suspended while we were waiting? + threadIsSuspended = thread->handle_special_suspend_equivalent_condition(); + if (threadIsSuspended) { + // + // The semaphore has been incremented, but while we were waiting + // another thread suspended us. We don't want to continue running + // while suspended because that would surprise the thread that + // suspended us. + // + ret = ::sema_post(&sig_sem); + assert(ret == 0, "sema_post() failed"); + + thread->java_suspend_self(); + } + } while (threadIsSuspended); + } +} + +int os::signal_lookup() { + return check_pending_signals(false); +} + +int os::signal_wait() { + return check_pending_signals(true); +} + +//////////////////////////////////////////////////////////////////////////////// +// Virtual Memory + +static int page_size = -1; + +// The mmap MAP_ALIGN flag is supported on Solaris 9 and later. init_2() will +// clear this var if support is not available. +static bool has_map_align = true; + +int os::vm_page_size() { + assert(page_size != -1, "must call os::init"); + return page_size; +} + +// Solaris allocates memory by pages. +int os::vm_allocation_granularity() { + assert(page_size != -1, "must call os::init"); + return page_size; +} + +bool os::commit_memory(char* addr, size_t bytes) { + size_t size = bytes; + return + NULL != Solaris::mmap_chunk(addr, size, MAP_PRIVATE|MAP_FIXED, + PROT_READ | PROT_WRITE | PROT_EXEC); +} + +bool os::commit_memory(char* addr, size_t bytes, size_t alignment_hint) { + if (commit_memory(addr, bytes)) { + if (UseMPSS && alignment_hint > (size_t)vm_page_size()) { + // If the large page size has been set and the VM + // is using large pages, use the large page size + // if it is smaller than the alignment hint. This is + // a case where the VM wants to use a larger alignment size + // for its own reasons but still want to use large pages + // (which is what matters to setting the mpss range. + size_t page_size = 0; + if (large_page_size() < alignment_hint) { + assert(UseLargePages, "Expected to be here for large page use only"); + page_size = large_page_size(); + } else { + // If the alignment hint is less than the large page + // size, the VM wants a particular alignment (thus the hint) + // for internal reasons. Try to set the mpss range using + // the alignment_hint. + page_size = alignment_hint; + } + // Since this is a hint, ignore any failures. + (void)Solaris::set_mpss_range(addr, bytes, page_size); + } + return true; + } + return false; +} + +// Uncommit the pages in a specified region. +void os::free_memory(char* addr, size_t bytes) { + if (madvise(addr, bytes, MADV_FREE) < 0) { + debug_only(warning("MADV_FREE failed.")); + return; + } +} + +// Change the page size in a given range. +void os::realign_memory(char *addr, size_t bytes, size_t alignment_hint) { + assert((intptr_t)addr % alignment_hint == 0, "Address should be aligned."); + assert((intptr_t)(addr + bytes) % alignment_hint == 0, "End should be aligned."); + Solaris::set_mpss_range(addr, bytes, alignment_hint); +} + +// Tell the OS to make the range local to the first-touching LWP +void os::numa_make_local(char *addr, size_t bytes) { + assert((intptr_t)addr % os::vm_page_size() == 0, "Address should be page-aligned."); + if (madvise(addr, bytes, MADV_ACCESS_LWP) < 0) { + debug_only(warning("MADV_ACCESS_LWP failed.")); + } +} + +// Tell the OS that this range would be accessed from different LWPs. +void os::numa_make_global(char *addr, size_t bytes) { + assert((intptr_t)addr % os::vm_page_size() == 0, "Address should be page-aligned."); + if (madvise(addr, bytes, MADV_ACCESS_MANY) < 0) { + debug_only(warning("MADV_ACCESS_MANY failed.")); + } +} + +// Get the number of the locality groups. +size_t os::numa_get_groups_num() { + size_t n = Solaris::lgrp_nlgrps(Solaris::lgrp_cookie()); + return n != -1 ? n : 1; +} + +// Get a list of leaf locality groups. A leaf lgroup is group that +// doesn't have any children. Typical leaf group is a CPU or a CPU/memory +// board. An LWP is assigned to one of these groups upon creation. +size_t os::numa_get_leaf_groups(int *ids, size_t size) { + if ((ids[0] = Solaris::lgrp_root(Solaris::lgrp_cookie())) == -1) { + ids[0] = 0; + return 1; + } + int result_size = 0, top = 1, bottom = 0, cur = 0; + for (int k = 0; k < size; k++) { + int r = Solaris::lgrp_children(Solaris::lgrp_cookie(), ids[cur], + (Solaris::lgrp_id_t*)&ids[top], size - top); + if (r == -1) { + ids[0] = 0; + return 1; + } + if (!r) { + assert (bottom <= cur, "Sanity check"); + ids[bottom++] = ids[cur]; + } + top += r; + cur++; + } + return bottom; +} + +// Detect the topology change. Typically happens during CPU pluggin-unplugging. +bool os::numa_topology_changed() { + int is_stale = Solaris::lgrp_cookie_stale(Solaris::lgrp_cookie()); + if (is_stale != -1 && is_stale) { + Solaris::lgrp_fini(Solaris::lgrp_cookie()); + Solaris::lgrp_cookie_t c = Solaris::lgrp_init(Solaris::LGRP_VIEW_CALLER); + assert(c != 0, "Failure to initialize LGRP API"); + Solaris::set_lgrp_cookie(c); + return true; + } + return false; +} + +// Get the group id of the current LWP. +int os::numa_get_group_id() { + int lgrp_id = os::Solaris::lgrp_home(P_LWPID, P_MYID); + if (lgrp_id == -1) { + return 0; + } + return lgrp_id; +} + +// Request information about the page. +bool os::get_page_info(char *start, page_info* info) { + const uint_t info_types[] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE }; + uint64_t addr = (uintptr_t)start; + uint64_t outdata[2]; + uint_t validity = 0; + + if (os::Solaris::meminfo(&addr, 1, info_types, 2, outdata, &validity) < 0) { + return false; + } + + info->size = 0; + info->lgrp_id = -1; + + if ((validity & 1) != 0) { + if ((validity & 2) != 0) { + info->lgrp_id = outdata[0]; + } + if ((validity & 4) != 0) { + info->size = outdata[1]; + } + return true; + } + return false; +} + +// Scan the pages from start to end until a page different than +// the one described in the info parameter is encountered. +char *os::scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found) { + const uint_t info_types[] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE }; + const size_t types = sizeof(info_types) / sizeof(info_types[0]); + uint64_t addrs[MAX_MEMINFO_CNT], outdata[types * MAX_MEMINFO_CNT]; + uint_t validity[MAX_MEMINFO_CNT]; + + size_t page_size = MAX2((size_t)os::vm_page_size(), page_expected->size); + uint64_t p = (uint64_t)start; + while (p < (uint64_t)end) { + addrs[0] = p; + size_t addrs_count = 1; + while (addrs_count < MAX_MEMINFO_CNT && addrs[addrs_count - 1] < (uint64_t)end) { + addrs[addrs_count] = addrs[addrs_count - 1] + page_size; + addrs_count++; + } + + if (os::Solaris::meminfo(addrs, addrs_count, info_types, types, outdata, validity) < 0) { + return NULL; + } + + size_t i = 0; + for (; i < addrs_count; i++) { + if ((validity[i] & 1) != 0) { + if ((validity[i] & 4) != 0) { + if (outdata[types * i + 1] != page_expected->size) { + break; + } + } else + if (page_expected->size != 0) { + break; + } + + if ((validity[i] & 2) != 0 && page_expected->lgrp_id > 0) { + if (outdata[types * i] != page_expected->lgrp_id) { + break; + } + } + } else { + return NULL; + } + } + + if (i != addrs_count) { + if ((validity[i] & 2) != 0) { + page_found->lgrp_id = outdata[types * i]; + } else { + page_found->lgrp_id = -1; + } + if ((validity[i] & 4) != 0) { + page_found->size = outdata[types * i + 1]; + } else { + page_found->size = 0; + } + return (char*)addrs[i]; + } + + p = addrs[addrs_count - 1] + page_size; + } + return end; +} + +bool os::uncommit_memory(char* addr, size_t bytes) { + size_t size = bytes; + // Map uncommitted pages PROT_NONE so we fail early if we touch an + // uncommitted page. Otherwise, the read/write might succeed if we + // have enough swap space to back the physical page. + return + NULL != Solaris::mmap_chunk(addr, size, + MAP_PRIVATE|MAP_FIXED|MAP_NORESERVE, + PROT_NONE); +} + +char* os::Solaris::mmap_chunk(char *addr, size_t size, int flags, int prot) { + char *b = (char *)mmap(addr, size, prot, flags, os::Solaris::_dev_zero_fd, 0); + + if (b == MAP_FAILED) { + return NULL; + } + return b; +} + +char* +os::reserve_memory(size_t bytes, char* requested_addr, size_t alignment_hint) { + char* addr = NULL; + int flags; + + flags = MAP_PRIVATE | MAP_NORESERVE; + if (requested_addr != NULL) { + flags |= MAP_FIXED; + addr = requested_addr; + } else if (has_map_align && alignment_hint > (size_t) vm_page_size()) { + flags |= MAP_ALIGN; + addr = (char*) alignment_hint; + } + + // Map uncommitted pages PROT_NONE so we fail early if we touch an + // uncommitted page. Otherwise, the read/write might succeed if we + // have enough swap space to back the physical page. + addr = Solaris::mmap_chunk(addr, bytes, flags, PROT_NONE); + + guarantee(requested_addr == NULL || requested_addr == addr, + "OS failed to return requested mmap address."); + + return addr; +} + +// Reserve memory at an arbitrary address, only if that area is +// available (and not reserved for something else). + +char* os::attempt_reserve_memory_at(size_t bytes, char* requested_addr) { + const int max_tries = 10; + char* base[max_tries]; + size_t size[max_tries]; + + // Solaris adds a gap between mmap'ed regions. The size of the gap + // is dependent on the requested size and the MMU. Our initial gap + // value here is just a guess and will be corrected later. + bool had_top_overlap = false; + bool have_adjusted_gap = false; + size_t gap = 0x400000; + + // Assert only that the size is a multiple of the page size, since + // that's all that mmap requires, and since that's all we really know + // about at this low abstraction level. If we need higher alignment, + // we can either pass an alignment to this method or verify alignment + // in one of the methods further up the call chain. See bug 5044738. + assert(bytes % os::vm_page_size() == 0, "reserving unexpected size block"); + + // Repeatedly allocate blocks until the block is allocated at the + // right spot. Give up after max_tries. + int i; + for (i = 0; i < max_tries; ++i) { + base[i] = reserve_memory(bytes); + + if (base[i] != NULL) { + // Is this the block we wanted? + if (base[i] == requested_addr) { + size[i] = bytes; + break; + } + + // check that the gap value is right + if (had_top_overlap && !have_adjusted_gap) { + size_t actual_gap = base[i-1] - base[i] - bytes; + if (gap != actual_gap) { + // adjust the gap value and retry the last 2 allocations + assert(i > 0, "gap adjustment code problem"); + have_adjusted_gap = true; // adjust the gap only once, just in case + gap = actual_gap; + if (PrintMiscellaneous && Verbose) { + warning("attempt_reserve_memory_at: adjusted gap to 0x%lx", gap); + } + unmap_memory(base[i], bytes); + unmap_memory(base[i-1], size[i-1]); + i-=2; + continue; + } + } + + // Does this overlap the block we wanted? Give back the overlapped + // parts and try again. + // + // There is still a bug in this code: if top_overlap == bytes, + // the overlap is offset from requested region by the value of gap. + // In this case giving back the overlapped part will not work, + // because we'll give back the entire block at base[i] and + // therefore the subsequent allocation will not generate a new gap. + // This could be fixed with a new algorithm that used larger + // or variable size chunks to find the requested region - + // but such a change would introduce additional complications. + // It's rare enough that the planets align for this bug, + // so we'll just wait for a fix for 6204603/5003415 which + // will provide a mmap flag to allow us to avoid this business. + + size_t top_overlap = requested_addr + (bytes + gap) - base[i]; + if (top_overlap >= 0 && top_overlap < bytes) { + had_top_overlap = true; + unmap_memory(base[i], top_overlap); + base[i] += top_overlap; + size[i] = bytes - top_overlap; + } else { + size_t bottom_overlap = base[i] + bytes - requested_addr; + if (bottom_overlap >= 0 && bottom_overlap < bytes) { + if (PrintMiscellaneous && Verbose && bottom_overlap == 0) { + warning("attempt_reserve_memory_at: possible alignment bug"); + } + unmap_memory(requested_addr, bottom_overlap); + size[i] = bytes - bottom_overlap; + } else { + size[i] = bytes; + } + } + } + } + + // Give back the unused reserved pieces. + + for (int j = 0; j < i; ++j) { + if (base[j] != NULL) { + unmap_memory(base[j], size[j]); + } + } + + return (i < max_tries) ? requested_addr : NULL; +} + +bool os::release_memory(char* addr, size_t bytes) { + size_t size = bytes; + return munmap(addr, size) == 0; +} + +static bool solaris_mprotect(char* addr, size_t bytes, int prot) { + assert(addr == (char*)align_size_down((uintptr_t)addr, os::vm_page_size()), + "addr must be page aligned"); + int retVal = mprotect(addr, bytes, prot); + return retVal == 0; +} + +// Protect memory (make it read-only. (Used to pass readonly pages through +// JNI GetArrayElements with empty arrays.) +bool os::protect_memory(char* addr, size_t bytes) { + return solaris_mprotect(addr, bytes, PROT_READ); +} + +// guard_memory and unguard_memory only happens within stack guard pages. +// Since ISM pertains only to the heap, guard and unguard memory should not +/// happen with an ISM region. +bool os::guard_memory(char* addr, size_t bytes) { + return solaris_mprotect(addr, bytes, PROT_NONE); +} + +bool os::unguard_memory(char* addr, size_t bytes) { + return solaris_mprotect(addr, bytes, PROT_READ|PROT_WRITE|PROT_EXEC); +} + +// Large page support + +// UseLargePages is the master flag to enable/disable large page memory. +// UseMPSS and UseISM are supported for compatibility reasons. Their combined +// effects can be described in the following table: +// +// UseLargePages UseMPSS UseISM +// false * * => UseLargePages is the master switch, turning +// it off will turn off both UseMPSS and +// UseISM. VM will not use large page memory +// regardless the settings of UseMPSS/UseISM. +// true false false => Unless future Solaris provides other +// mechanism to use large page memory, this +// combination is equivalent to -UseLargePages, +// VM will not use large page memory +// true true false => JVM will use MPSS for large page memory. +// This is the default behavior. +// true false true => JVM will use ISM for large page memory. +// true true true => JVM will use ISM if it is available. +// Otherwise, JVM will fall back to MPSS. +// Becaues ISM is now available on all +// supported Solaris versions, this combination +// is equivalent to +UseISM -UseMPSS. + +typedef int (*getpagesizes_func_type) (size_t[], int); +static size_t _large_page_size = 0; + +bool os::Solaris::ism_sanity_check(bool warn, size_t * page_size) { + // x86 uses either 2M or 4M page, depending on whether PAE (Physical Address + // Extensions) mode is enabled. AMD64/EM64T uses 2M page in 64bit mode. Sparc + // can support multiple page sizes. + + // Don't bother to probe page size because getpagesizes() comes with MPSS. + // ISM is only recommended on old Solaris where there is no MPSS support. + // Simply choose a conservative value as default. + *page_size = LargePageSizeInBytes ? LargePageSizeInBytes : + SPARC_ONLY(4 * M) IA32_ONLY(4 * M) AMD64_ONLY(2 * M); + + // ISM is available on all supported Solaris versions + return true; +} + +// Insertion sort for small arrays (descending order). +static void insertion_sort_descending(size_t* array, int len) { + for (int i = 0; i < len; i++) { + size_t val = array[i]; + for (size_t key = i; key > 0 && array[key - 1] < val; --key) { + size_t tmp = array[key]; + array[key] = array[key - 1]; + array[key - 1] = tmp; + } + } +} + +bool os::Solaris::mpss_sanity_check(bool warn, size_t * page_size) { + getpagesizes_func_type getpagesizes_func = + CAST_TO_FN_PTR(getpagesizes_func_type, dlsym(RTLD_DEFAULT, "getpagesizes")); + if (getpagesizes_func == NULL) { + if (warn) { + warning("MPSS is not supported by the operating system."); + } + return false; + } + + const unsigned int usable_count = VM_Version::page_size_count(); + if (usable_count == 1) { + return false; + } + + // Fill the array of page sizes. + int n = getpagesizes_func(_page_sizes, page_sizes_max); + assert(n > 0, "Solaris bug?"); + if (n == page_sizes_max) { + // Add a sentinel value (necessary only if the array was completely filled + // since it is static (zeroed at initialization)). + _page_sizes[--n] = 0; + DEBUG_ONLY(warning("increase the size of the os::_page_sizes array.");) + } + assert(_page_sizes[n] == 0, "missing sentinel"); + + if (n == 1) return false; // Only one page size available. + + // Skip sizes larger than 4M (or LargePageSizeInBytes if it was set) and + // select up to usable_count elements. First sort the array, find the first + // acceptable value, then copy the usable sizes to the top of the array and + // trim the rest. Make sure to include the default page size :-). + // + // A better policy could get rid of the 4M limit by taking the sizes of the + // important VM memory regions (java heap and possibly the code cache) into + // account. + insertion_sort_descending(_page_sizes, n); + const size_t size_limit = + FLAG_IS_DEFAULT(LargePageSizeInBytes) ? 4 * M : LargePageSizeInBytes; + int beg; + for (beg = 0; beg < n && _page_sizes[beg] > size_limit; ++beg) /* empty */ ; + const int end = MIN2((int)usable_count, n) - 1; + for (int cur = 0; cur < end; ++cur, ++beg) { + _page_sizes[cur] = _page_sizes[beg]; + } + _page_sizes[end] = vm_page_size(); + _page_sizes[end + 1] = 0; + + if (_page_sizes[end] > _page_sizes[end - 1]) { + // Default page size is not the smallest; sort again. + insertion_sort_descending(_page_sizes, end + 1); + } + *page_size = _page_sizes[0]; + + return true; +} + +bool os::large_page_init() { + if (!UseLargePages) { + UseISM = false; + UseMPSS = false; + return false; + } + + // print a warning if any large page related flag is specified on command line + bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) || + !FLAG_IS_DEFAULT(UseISM) || + !FLAG_IS_DEFAULT(UseMPSS) || + !FLAG_IS_DEFAULT(LargePageSizeInBytes); + UseISM = UseISM && + Solaris::ism_sanity_check(warn_on_failure, &_large_page_size); + if (UseISM) { + // ISM disables MPSS to be compatible with old JDK behavior + UseMPSS = false; + } + + UseMPSS = UseMPSS && + Solaris::mpss_sanity_check(warn_on_failure, &_large_page_size); + + UseLargePages = UseISM || UseMPSS; + return UseLargePages; +} + +bool os::Solaris::set_mpss_range(caddr_t start, size_t bytes, size_t align) { + // Signal to OS that we want large pages for addresses + // from addr, addr + bytes + struct memcntl_mha mpss_struct; + mpss_struct.mha_cmd = MHA_MAPSIZE_VA; + mpss_struct.mha_pagesize = align; + mpss_struct.mha_flags = 0; + if (memcntl(start, bytes, MC_HAT_ADVISE, + (caddr_t) &mpss_struct, 0, 0) < 0) { + debug_only(warning("Attempt to use MPSS failed.")); + return false; + } + return true; +} + +char* os::reserve_memory_special(size_t bytes) { + assert(UseLargePages && UseISM, "only for ISM large pages"); + + size_t size = bytes; + char* retAddr = NULL; + int shmid; + key_t ismKey; + + bool warn_on_failure = UseISM && + (!FLAG_IS_DEFAULT(UseLargePages) || + !FLAG_IS_DEFAULT(UseISM) || + !FLAG_IS_DEFAULT(LargePageSizeInBytes) + ); + char msg[128]; + + ismKey = IPC_PRIVATE; + + // Create a large shared memory region to attach to based on size. + // Currently, size is the total size of the heap + shmid = shmget(ismKey, size, SHM_R | SHM_W | IPC_CREAT); + if (shmid == -1){ + if (warn_on_failure) { + jio_snprintf(msg, sizeof(msg), "Failed to reserve shared memory (errno = %d).", errno); + warning(msg); + } + return NULL; + } + + // Attach to the region + retAddr = (char *) shmat(shmid, 0, SHM_SHARE_MMU | SHM_R | SHM_W); + int err = errno; + + // Remove shmid. If shmat() is successful, the actual shared memory segment + // will be deleted when it's detached by shmdt() or when the process + // terminates. If shmat() is not successful this will remove the shared + // segment immediately. + shmctl(shmid, IPC_RMID, NULL); + + if (retAddr == (char *) -1) { + if (warn_on_failure) { + jio_snprintf(msg, sizeof(msg), "Failed to attach shared memory (errno = %d).", err); + warning(msg); + } + return NULL; + } + + return retAddr; +} + +bool os::release_memory_special(char* base, size_t bytes) { + // detaching the SHM segment will also delete it, see reserve_memory_special() + int rslt = shmdt(base); + return rslt == 0; +} + +size_t os::large_page_size() { + return _large_page_size; +} + +// MPSS allows application to commit large page memory on demand; with ISM +// the entire memory region must be allocated as shared memory. +bool os::can_commit_large_page_memory() { + return UseISM ? false : true; +} + +static int os_sleep(jlong millis, bool interruptible) { + const jlong limit = INT_MAX; + jlong prevtime; + int res; + + while (millis > limit) { + if ((res = os_sleep(limit, interruptible)) != OS_OK) + return res; + millis -= limit; + } + + // Restart interrupted polls with new parameters until the proper delay + // has been completed. + + prevtime = getTimeMillis(); + + while (millis > 0) { + jlong newtime; + + if (!interruptible) { + // Following assert fails for os::yield_all: + // assert(!thread->is_Java_thread(), "must not be java thread"); + res = poll(NULL, 0, millis); + } else { + JavaThread *jt = JavaThread::current(); + + INTERRUPTIBLE_NORESTART_VM_ALWAYS(poll(NULL, 0, millis), res, jt, + os::Solaris::clear_interrupted); + } + + // INTERRUPTIBLE_NORESTART_VM_ALWAYS returns res == OS_INTRPT for + // thread.Interrupt. + + if((res == OS_ERR) && (errno == EINTR)) { + newtime = getTimeMillis(); + assert(newtime >= prevtime, "time moving backwards"); + /* Doing prevtime and newtime in microseconds doesn't help precision, + and trying to round up to avoid lost milliseconds can result in a + too-short delay. */ + millis -= newtime - prevtime; + if(millis <= 0) + return OS_OK; + prevtime = newtime; + } else + return res; + } + + return OS_OK; +} + +// Read calls from inside the vm need to perform state transitions +size_t os::read(int fd, void *buf, unsigned int nBytes) { + INTERRUPTIBLE_RETURN_INT_VM(::read(fd, buf, nBytes), os::Solaris::clear_interrupted); +} + +int os::sleep(Thread* thread, jlong millis, bool interruptible) { + assert(thread == Thread::current(), "thread consistency check"); + + // TODO-FIXME: this should be removed. + // On Solaris machines (especially 2.5.1) we found that sometimes the VM gets into a live lock + // situation with a JavaThread being starved out of a lwp. The kernel doesn't seem to generate + // a SIGWAITING signal which would enable the threads library to create a new lwp for the starving + // thread. We suspect that because the Watcher thread keeps waking up at periodic intervals the kernel + // is fooled into believing that the system is making progress. In the code below we block the + // the watcher thread while safepoint is in progress so that it would not appear as though the + // system is making progress. + if (!Solaris::T2_libthread() && + thread->is_Watcher_thread() && SafepointSynchronize::is_synchronizing() && !Arguments::has_profile()) { + // We now try to acquire the threads lock. Since this lock is held by the VM thread during + // the entire safepoint, the watcher thread will line up here during the safepoint. + Threads_lock->lock_without_safepoint_check(); + Threads_lock->unlock(); + } + + if (thread->is_Java_thread()) { + // This is a JavaThread so we honor the _thread_blocked protocol + // even for sleeps of 0 milliseconds. This was originally done + // as a workaround for bug 4338139. However, now we also do it + // to honor the suspend-equivalent protocol. + + JavaThread *jt = (JavaThread *) thread; + ThreadBlockInVM tbivm(jt); + + jt->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + int ret_code; + if (millis <= 0) { + thr_yield(); + ret_code = 0; + } else { + // The original sleep() implementation did not create an + // OSThreadWaitState helper for sleeps of 0 milliseconds. + // I'm preserving that decision for now. + OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */); + + ret_code = os_sleep(millis, interruptible); + } + + // were we externally suspended while we were waiting? + jt->check_and_wait_while_suspended(); + + return ret_code; + } + + // non-JavaThread from this point on: + + if (millis <= 0) { + thr_yield(); + return 0; + } + + OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); + + return os_sleep(millis, interruptible); +} + +int os::naked_sleep() { + // %% make the sleep time an integer flag. for now use 1 millisec. + return os_sleep(1, false); +} + +// Sleep forever; naked call to OS-specific sleep; use with CAUTION +void os::infinite_sleep() { + while (true) { // sleep forever ... + ::sleep(100); // ... 100 seconds at a time + } +} + +// Used to convert frequent JVM_Yield() to nops +bool os::dont_yield() { + if (DontYieldALot) { + static hrtime_t last_time = 0; + hrtime_t diff = getTimeNanos() - last_time; + + if (diff < DontYieldALotInterval * 1000000) + return true; + + last_time += diff; + + return false; + } + else { + return false; + } +} + +// Caveat: Solaris os::yield() causes a thread-state transition whereas +// the linux and win32 implementations do not. This should be checked. + +void os::yield() { + // Yields to all threads with same or greater priority + os::sleep(Thread::current(), 0, false); +} + +// Note that yield semantics are defined by the scheduling class to which +// the thread currently belongs. Typically, yield will _not yield to +// other equal or higher priority threads that reside on the dispatch queues +// of other CPUs. + +os::YieldResult os::NakedYield() { thr_yield(); return os::YIELD_UNKNOWN; } + + +// On Solaris we found that yield_all doesn't always yield to all other threads. +// There have been cases where there is a thread ready to execute but it doesn't +// get an lwp as the VM thread continues to spin with sleeps of 1 millisecond. +// The 1 millisecond wait doesn't seem long enough for the kernel to issue a +// SIGWAITING signal which will cause a new lwp to be created. So we count the +// number of times yield_all is called in the one loop and increase the sleep +// time after 8 attempts. If this fails too we increase the concurrency level +// so that the starving thread would get an lwp + +void os::yield_all(int attempts) { + // Yields to all threads, including threads with lower priorities + if (attempts == 0) { + os::sleep(Thread::current(), 1, false); + } else { + int iterations = attempts % 30; + if (iterations == 0 && !os::Solaris::T2_libthread()) { + // thr_setconcurrency and _getconcurrency make sense only under T1. + int noofLWPS = thr_getconcurrency(); + if (noofLWPS < (Threads::number_of_threads() + 2)) { + thr_setconcurrency(thr_getconcurrency() + 1); + } + } else if (iterations < 25) { + os::sleep(Thread::current(), 1, false); + } else { + os::sleep(Thread::current(), 10, false); + } + } +} + +// Called from the tight loops to possibly influence time-sharing heuristics +void os::loop_breaker(int attempts) { + os::yield_all(attempts); +} + + +// Interface for setting lwp priorities. If we are using T2 libthread, +// which forces the use of BoundThreads or we manually set UseBoundThreads, +// all of our threads will be assigned to real lwp's. Using the thr_setprio +// function is meaningless in this mode so we must adjust the real lwp's priority +// The routines below implement the getting and setting of lwp priorities. +// +// Note: There are three priority scales used on Solaris. Java priotities +// which range from 1 to 10, libthread "thr_setprio" scale which range +// from 0 to 127, and the current scheduling class of the process we +// are running in. This is typically from -60 to +60. +// The setting of the lwp priorities in done after a call to thr_setprio +// so Java priorities are mapped to libthread priorities and we map from +// the latter to lwp priorities. We don't keep priorities stored in +// Java priorities since some of our worker threads want to set priorities +// higher than all Java threads. +// +// For related information: +// (1) man -s 2 priocntl +// (2) man -s 4 priocntl +// (3) man dispadmin +// = librt.so +// = libthread/common/rtsched.c - thrp_setlwpprio(). +// = ps -cL ... to validate priority. +// = sched_get_priority_min and _max +// pthread_create +// sched_setparam +// pthread_setschedparam +// +// Assumptions: +// + We assume that all threads in the process belong to the same +// scheduling class. IE. an homogenous process. +// + Must be root or in IA group to change change "interactive" attribute. +// Priocntl() will fail silently. The only indication of failure is when +// we read-back the value and notice that it hasn't changed. +// + Interactive threads enter the runq at the head, non-interactive at the tail. +// + For RT, change timeslice as well. Invariant: +// constant "priority integral" +// Konst == TimeSlice * (60-Priority) +// Given a priority, compute appropriate timeslice. +// + Higher numerical values have higher priority. + +// sched class attributes +typedef struct { + int schedPolicy; // classID + int maxPrio; + int minPrio; +} SchedInfo; + + +static SchedInfo tsLimits, iaLimits, rtLimits; + +#ifdef ASSERT +static int ReadBackValidate = 1; +#endif +static int myClass = 0; +static int myMin = 0; +static int myMax = 0; +static int myCur = 0; +static bool priocntl_enable = false; + + +// Call the version of priocntl suitable for all supported versions +// of Solaris. We need to call through this wrapper so that we can +// build on Solaris 9 and run on Solaris 8, 9 and 10. +// +// This code should be removed if we ever stop supporting Solaris 8 +// and earlier releases. + +static long priocntl_stub(int pcver, idtype_t idtype, id_t id, int cmd, caddr_t arg); +typedef long (*priocntl_type)(int pcver, idtype_t idtype, id_t id, int cmd, caddr_t arg); +static priocntl_type priocntl_ptr = priocntl_stub; + +// Stub to set the value of the real pointer, and then call the real +// function. + +static long priocntl_stub(int pcver, idtype_t idtype, id_t id, int cmd, caddr_t arg) { + // Try Solaris 8- name only. + priocntl_type tmp = (priocntl_type)dlsym(RTLD_DEFAULT, "__priocntl"); + guarantee(tmp != NULL, "priocntl function not found."); + priocntl_ptr = tmp; + return (*priocntl_ptr)(PC_VERSION, idtype, id, cmd, arg); +} + + +// lwp_priocntl_init +// +// Try to determine the priority scale for our process. +// +// Return errno or 0 if OK. +// +static +int lwp_priocntl_init () +{ + int rslt; + pcinfo_t ClassInfo; + pcparms_t ParmInfo; + int i; + + if (!UseThreadPriorities) return 0; + + // We are using Bound threads, we need to determine our priority ranges + if (os::Solaris::T2_libthread() || UseBoundThreads) { + // If ThreadPriorityPolicy is 1, switch tables + if (ThreadPriorityPolicy == 1) { + for (i = 0 ; i < MaxPriority+1; i++) + os::java_to_os_priority[i] = prio_policy1[i]; + } + } + // Not using Bound Threads, set to ThreadPolicy 1 + else { + for ( i = 0 ; i < MaxPriority+1; i++ ) { + os::java_to_os_priority[i] = prio_policy1[i]; + } + return 0; + } + + + // Get IDs for a set of well-known scheduling classes. + // TODO-FIXME: GETCLINFO returns the current # of classes in the + // the system. We should have a loop that iterates over the + // classID values, which are known to be "small" integers. + + strcpy(ClassInfo.pc_clname, "TS"); + ClassInfo.pc_cid = -1; + rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); + if (rslt < 0) return errno; + assert(ClassInfo.pc_cid != -1, "cid for TS class is -1"); + tsLimits.schedPolicy = ClassInfo.pc_cid; + tsLimits.maxPrio = ((tsinfo_t*)ClassInfo.pc_clinfo)->ts_maxupri; + tsLimits.minPrio = -tsLimits.maxPrio; + + strcpy(ClassInfo.pc_clname, "IA"); + ClassInfo.pc_cid = -1; + rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); + if (rslt < 0) return errno; + assert(ClassInfo.pc_cid != -1, "cid for IA class is -1"); + iaLimits.schedPolicy = ClassInfo.pc_cid; + iaLimits.maxPrio = ((iainfo_t*)ClassInfo.pc_clinfo)->ia_maxupri; + iaLimits.minPrio = -iaLimits.maxPrio; + + strcpy(ClassInfo.pc_clname, "RT"); + ClassInfo.pc_cid = -1; + rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); + if (rslt < 0) return errno; + assert(ClassInfo.pc_cid != -1, "cid for RT class is -1"); + rtLimits.schedPolicy = ClassInfo.pc_cid; + rtLimits.maxPrio = ((rtinfo_t*)ClassInfo.pc_clinfo)->rt_maxpri; + rtLimits.minPrio = 0; + + + // Query our "current" scheduling class. + // This will normally be IA,TS or, rarely, RT. + memset (&ParmInfo, 0, sizeof(ParmInfo)); + ParmInfo.pc_cid = PC_CLNULL; + rslt = (*priocntl_ptr) (PC_VERSION, P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo ); + if ( rslt < 0 ) return errno; + myClass = ParmInfo.pc_cid; + + // We now know our scheduling classId, get specific information + // the class. + ClassInfo.pc_cid = myClass; + ClassInfo.pc_clname[0] = 0; + rslt = (*priocntl_ptr) (PC_VERSION, (idtype)0, 0, PC_GETCLINFO, (caddr_t)&ClassInfo ); + if ( rslt < 0 ) return errno; + + if (ThreadPriorityVerbose) + tty->print_cr ("lwp_priocntl_init: Class=%d(%s)...", myClass, ClassInfo.pc_clname); + + memset(&ParmInfo, 0, sizeof(pcparms_t)); + ParmInfo.pc_cid = PC_CLNULL; + rslt = (*priocntl_ptr)(PC_VERSION, P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo); + if (rslt < 0) return errno; + + if (ParmInfo.pc_cid == rtLimits.schedPolicy) { + myMin = rtLimits.minPrio; + myMax = rtLimits.maxPrio; + } else if (ParmInfo.pc_cid == iaLimits.schedPolicy) { + iaparms_t *iaInfo = (iaparms_t*)ParmInfo.pc_clparms; + myMin = iaLimits.minPrio; + myMax = iaLimits.maxPrio; + myMax = MIN2(myMax, (int)iaInfo->ia_uprilim); // clamp - restrict + } else if (ParmInfo.pc_cid == tsLimits.schedPolicy) { + tsparms_t *tsInfo = (tsparms_t*)ParmInfo.pc_clparms; + myMin = tsLimits.minPrio; + myMax = tsLimits.maxPrio; + myMax = MIN2(myMax, (int)tsInfo->ts_uprilim); // clamp - restrict + } else { + // No clue - punt + if (ThreadPriorityVerbose) + tty->print_cr ("Unknown scheduling class: %s ... \n", ClassInfo.pc_clname); + return EINVAL; // no clue, punt + } + + if (ThreadPriorityVerbose) + tty->print_cr ("Thread priority Range: [%d..%d]\n", myMin, myMax); + + priocntl_enable = true; // Enable changing priorities + return 0; +} + +#define IAPRI(x) ((iaparms_t *)((x).pc_clparms)) +#define RTPRI(x) ((rtparms_t *)((x).pc_clparms)) +#define TSPRI(x) ((tsparms_t *)((x).pc_clparms)) + + +// scale_to_lwp_priority +// +// Convert from the libthread "thr_setprio" scale to our current +// lwp scheduling class scale. +// +static +int scale_to_lwp_priority (int rMin, int rMax, int x) +{ + int v; + + if (x == 127) return rMax; // avoid round-down + v = (((x*(rMax-rMin)))/128)+rMin; + return v; +} + + +// set_lwp_priority +// +// Set the priority of the lwp. This call should only be made +// when using bound threads (T2 threads are bound by default). +// +int set_lwp_priority (int ThreadID, int lwpid, int newPrio ) +{ + int rslt; + int Actual, Expected, prv; + pcparms_t ParmInfo; // for GET-SET +#ifdef ASSERT + pcparms_t ReadBack; // for readback +#endif + + // Set priority via PC_GETPARMS, update, PC_SETPARMS + // Query current values. + // TODO: accelerate this by eliminating the PC_GETPARMS call. + // Cache "pcparms_t" in global ParmCache. + // TODO: elide set-to-same-value + + // If something went wrong on init, don't change priorities. + if ( !priocntl_enable ) { + if (ThreadPriorityVerbose) + tty->print_cr("Trying to set priority but init failed, ignoring"); + return EINVAL; + } + + + // If lwp hasn't started yet, just return + // the _start routine will call us again. + if ( lwpid <= 0 ) { + if (ThreadPriorityVerbose) { + tty->print_cr ("deferring the set_lwp_priority of thread " INTPTR_FORMAT " to %d, lwpid not set", + ThreadID, newPrio); + } + return 0; + } + + if (ThreadPriorityVerbose) { + tty->print_cr ("set_lwp_priority(" INTPTR_FORMAT "@" INTPTR_FORMAT " %d) ", + ThreadID, lwpid, newPrio); + } + + memset(&ParmInfo, 0, sizeof(pcparms_t)); + ParmInfo.pc_cid = PC_CLNULL; + rslt = (*priocntl_ptr)(PC_VERSION, P_LWPID, lwpid, PC_GETPARMS, (caddr_t)&ParmInfo); + if (rslt < 0) return errno; + + if (ParmInfo.pc_cid == rtLimits.schedPolicy) { + rtparms_t *rtInfo = (rtparms_t*)ParmInfo.pc_clparms; + rtInfo->rt_pri = scale_to_lwp_priority (rtLimits.minPrio, rtLimits.maxPrio, newPrio); + rtInfo->rt_tqsecs = RT_NOCHANGE; + rtInfo->rt_tqnsecs = RT_NOCHANGE; + if (ThreadPriorityVerbose) { + tty->print_cr("RT: %d->%d\n", newPrio, rtInfo->rt_pri); + } + } else if (ParmInfo.pc_cid == iaLimits.schedPolicy) { + iaparms_t *iaInfo = (iaparms_t*)ParmInfo.pc_clparms; + int maxClamped = MIN2(iaLimits.maxPrio, (int)iaInfo->ia_uprilim); + iaInfo->ia_upri = scale_to_lwp_priority(iaLimits.minPrio, maxClamped, newPrio); + iaInfo->ia_uprilim = IA_NOCHANGE; + iaInfo->ia_nice = IA_NOCHANGE; + iaInfo->ia_mode = IA_NOCHANGE; + if (ThreadPriorityVerbose) { + tty->print_cr ("IA: [%d...%d] %d->%d\n", + iaLimits.minPrio, maxClamped, newPrio, iaInfo->ia_upri); + } + } else if (ParmInfo.pc_cid == tsLimits.schedPolicy) { + tsparms_t *tsInfo = (tsparms_t*)ParmInfo.pc_clparms; + int maxClamped = MIN2(tsLimits.maxPrio, (int)tsInfo->ts_uprilim); + prv = tsInfo->ts_upri; + tsInfo->ts_upri = scale_to_lwp_priority(tsLimits.minPrio, maxClamped, newPrio); + tsInfo->ts_uprilim = IA_NOCHANGE; + if (ThreadPriorityVerbose) { + tty->print_cr ("TS: %d [%d...%d] %d->%d\n", + prv, tsLimits.minPrio, maxClamped, newPrio, tsInfo->ts_upri); + } + if (prv == tsInfo->ts_upri) return 0; + } else { + if ( ThreadPriorityVerbose ) { + tty->print_cr ("Unknown scheduling class\n"); + } + return EINVAL; // no clue, punt + } + + rslt = (*priocntl_ptr)(PC_VERSION, P_LWPID, lwpid, PC_SETPARMS, (caddr_t)&ParmInfo); + if (ThreadPriorityVerbose && rslt) { + tty->print_cr ("PC_SETPARMS ->%d %d\n", rslt, errno); + } + if (rslt < 0) return errno; + +#ifdef ASSERT + // Sanity check: read back what we just attempted to set. + // In theory it could have changed in the interim ... + // + // The priocntl system call is tricky. + // Sometimes it'll validate the priority value argument and + // return EINVAL if unhappy. At other times it fails silently. + // Readbacks are prudent. + + if (!ReadBackValidate) return 0; + + memset(&ReadBack, 0, sizeof(pcparms_t)); + ReadBack.pc_cid = PC_CLNULL; + rslt = (*priocntl_ptr)(PC_VERSION, P_LWPID, lwpid, PC_GETPARMS, (caddr_t)&ReadBack); + assert(rslt >= 0, "priocntl failed"); + Actual = Expected = 0xBAD; + assert(ParmInfo.pc_cid == ReadBack.pc_cid, "cid's don't match"); + if (ParmInfo.pc_cid == rtLimits.schedPolicy) { + Actual = RTPRI(ReadBack)->rt_pri; + Expected = RTPRI(ParmInfo)->rt_pri; + } else if (ParmInfo.pc_cid == iaLimits.schedPolicy) { + Actual = IAPRI(ReadBack)->ia_upri; + Expected = IAPRI(ParmInfo)->ia_upri; + } else if (ParmInfo.pc_cid == tsLimits.schedPolicy) { + Actual = TSPRI(ReadBack)->ts_upri; + Expected = TSPRI(ParmInfo)->ts_upri; + } else { + if ( ThreadPriorityVerbose ) { + tty->print_cr("set_lwp_priority: unexpected class in readback: %d\n", ParmInfo.pc_cid); + } + } + + if (Actual != Expected) { + if ( ThreadPriorityVerbose ) { + tty->print_cr ("set_lwp_priority(%d %d) Class=%d: actual=%d vs expected=%d\n", + lwpid, newPrio, ReadBack.pc_cid, Actual, Expected); + } + } +#endif + + return 0; +} + + + +// Solaris only gives access to 128 real priorities at a time, +// so we expand Java's ten to fill this range. This would be better +// if we dynamically adjusted relative priorities. +// +// The ThreadPriorityPolicy option allows us to select 2 different +// priority scales. +// +// ThreadPriorityPolicy=0 +// Since the Solaris' default priority is MaximumPriority, we do not +// set a priority lower than Max unless a priority lower than +// NormPriority is requested. +// +// ThreadPriorityPolicy=1 +// This mode causes the priority table to get filled with +// linear values. NormPriority get's mapped to 50% of the +// Maximum priority an so on. This will cause VM threads +// to get unfair treatment against other Solaris processes +// which do not explicitly alter their thread priorities. +// + + +int os::java_to_os_priority[MaxPriority + 1] = { + -99999, // 0 Entry should never be used + + 0, // 1 MinPriority + 32, // 2 + 64, // 3 + + 96, // 4 + 127, // 5 NormPriority + 127, // 6 + + 127, // 7 + 127, // 8 + 127, // 9 NearMaxPriority + + 127 // 10 MaxPriority +}; + + +OSReturn os::set_native_priority(Thread* thread, int newpri) { + assert(newpri >= MinimumPriority && newpri <= MaximumPriority, "bad priority mapping"); + if ( !UseThreadPriorities ) return OS_OK; + int status = thr_setprio(thread->osthread()->thread_id(), newpri); + if ( os::Solaris::T2_libthread() || (UseBoundThreads && thread->osthread()->is_vm_created()) ) + status |= (set_lwp_priority (thread->osthread()->thread_id(), + thread->osthread()->lwp_id(), newpri )); + return (status == 0) ? OS_OK : OS_ERR; +} + + +OSReturn os::get_native_priority(const Thread* const thread, int *priority_ptr) { + int p; + if ( !UseThreadPriorities ) { + *priority_ptr = NormalPriority; + return OS_OK; + } + int status = thr_getprio(thread->osthread()->thread_id(), &p); + if (status != 0) { + return OS_ERR; + } + *priority_ptr = p; + return OS_OK; +} + + +// Hint to the underlying OS that a task switch would not be good. +// Void return because it's a hint and can fail. +void os::hint_no_preempt() { + schedctl_start(schedctl_init()); +} + +void os::interrupt(Thread* thread) { + assert(Thread::current() == thread || Threads_lock->owned_by_self(), "possibility of dangling Thread pointer"); + + OSThread* osthread = thread->osthread(); + + int isInterrupted = osthread->interrupted(); + if (!isInterrupted) { + osthread->set_interrupted(true); + OrderAccess::fence(); + // os::sleep() is implemented with either poll (NULL,0,timeout) or + // by parking on _SleepEvent. If the former, thr_kill will unwedge + // the sleeper by SIGINTR, otherwise the unpark() will wake the sleeper. + ParkEvent * const slp = thread->_SleepEvent ; + if (slp != NULL) slp->unpark() ; + } + + // For JSR166: unpark after setting status but before thr_kill -dl + if (thread->is_Java_thread()) { + ((JavaThread*)thread)->parker()->unpark(); + } + + // Handle interruptible wait() ... + ParkEvent * const ev = thread->_ParkEvent ; + if (ev != NULL) ev->unpark() ; + + // When events are used everywhere for os::sleep, then this thr_kill + // will only be needed if UseVMInterruptibleIO is true. + + if (!isInterrupted) { + int status = thr_kill(osthread->thread_id(), os::Solaris::SIGinterrupt()); + assert_status(status == 0, status, "thr_kill"); + + // Bump thread interruption counter + RuntimeService::record_thread_interrupt_signaled_count(); + } +} + + +bool os::is_interrupted(Thread* thread, bool clear_interrupted) { + assert(Thread::current() == thread || Threads_lock->owned_by_self(), "possibility of dangling Thread pointer"); + + OSThread* osthread = thread->osthread(); + + bool res = osthread->interrupted(); + + // NOTE that since there is no "lock" around these two operations, + // there is the possibility that the interrupted flag will be + // "false" but that the interrupt event will be set. This is + // intentional. The effect of this is that Object.wait() will appear + // to have a spurious wakeup, which is not harmful, and the + // possibility is so rare that it is not worth the added complexity + // to add yet another lock. It has also been recommended not to put + // the interrupted flag into the os::Solaris::Event structure, + // because it hides the issue. + if (res && clear_interrupted) { + osthread->set_interrupted(false); + } + return res; +} + + +void os::print_statistics() { +} + +int os::message_box(const char* title, const char* message) { + int i; + fdStream err(defaultStream::error_fd()); + for (i = 0; i < 78; i++) err.print_raw("="); + err.cr(); + err.print_raw_cr(title); + for (i = 0; i < 78; i++) err.print_raw("-"); + err.cr(); + err.print_raw_cr(message); + for (i = 0; i < 78; i++) err.print_raw("="); + err.cr(); + + char buf[16]; + // Prevent process from exiting upon "read error" without consuming all CPU + while (::read(0, buf, sizeof(buf)) <= 0) { ::sleep(100); } + + return buf[0] == 'y' || buf[0] == 'Y'; +} + +// A lightweight implementation that does not suspend the target thread and +// thus returns only a hint. Used for profiling only! +ExtendedPC os::get_thread_pc(Thread* thread) { + // Make sure that it is called by the watcher and the Threads lock is owned. + assert(Thread::current()->is_Watcher_thread(), "Must be watcher and own Threads_lock"); + // For now, is only used to profile the VM Thread + assert(thread->is_VM_thread(), "Can only be called for VMThread"); + ExtendedPC epc; + + GetThreadPC_Callback cb(ProfileVM_lock); + OSThread *osthread = thread->osthread(); + const int time_to_wait = 400; // 400ms wait for initial response + int status = cb.interrupt(thread, time_to_wait); + + if (cb.is_done() ) { + epc = cb.addr(); + } else { + DEBUG_ONLY(tty->print_cr("Failed to get pc for thread: %d got %d status", + osthread->thread_id(), status);); + // epc is already NULL + } + return epc; +} + + +// This does not do anything on Solaris. This is basically a hook for being +// able to use structured exception handling (thread-local exception filters) on, e.g., Win32. +void os::os_exception_wrapper(java_call_t f, JavaValue* value, methodHandle* method, JavaCallArguments* args, Thread* thread) { + f(value, method, args, thread); +} + +// This routine may be used by user applications as a "hook" to catch signals. +// The user-defined signal handler must pass unrecognized signals to this +// routine, and if it returns true (non-zero), then the signal handler must +// return immediately. If the flag "abort_if_unrecognized" is true, then this +// routine will never retun false (zero), but instead will execute a VM panic +// routine kill the process. +// +// If this routine returns false, it is OK to call it again. This allows +// the user-defined signal handler to perform checks either before or after +// the VM performs its own checks. Naturally, the user code would be making +// a serious error if it tried to handle an exception (such as a null check +// or breakpoint) that the VM was generating for its own correct operation. +// +// This routine may recognize any of the following kinds of signals: +// SIGBUS, SIGSEGV, SIGILL, SIGFPE, BREAK_SIGNAL, SIGPIPE, SIGXFSZ, +// os::Solaris::SIGasync +// It should be consulted by handlers for any of those signals. +// It explicitly does not recognize os::Solaris::SIGinterrupt +// +// The caller of this routine must pass in the three arguments supplied +// to the function referred to in the "sa_sigaction" (not the "sa_handler") +// field of the structure passed to sigaction(). This routine assumes that +// the sa_flags field passed to sigaction() includes SA_SIGINFO and SA_RESTART. +// +// Note that the VM will print warnings if it detects conflicting signal +// handlers, unless invoked with the option "-XX:+AllowUserSignalHandlers". +// +extern "C" int JVM_handle_solaris_signal(int signo, siginfo_t* siginfo, void* ucontext, int abort_if_unrecognized); + + +void signalHandler(int sig, siginfo_t* info, void* ucVoid) { + JVM_handle_solaris_signal(sig, info, ucVoid, true); +} + +/* Do not delete - if guarantee is ever removed, a signal handler (even empty) + is needed to provoke threads blocked on IO to return an EINTR + Note: this explicitly does NOT call JVM_handle_solaris_signal and + does NOT participate in signal chaining due to requirement for + NOT setting SA_RESTART to make EINTR work. */ +extern "C" void sigINTRHandler(int sig, siginfo_t* info, void* ucVoid) { + if (UseSignalChaining) { + struct sigaction *actp = os::Solaris::get_chained_signal_action(sig); + if (actp && actp->sa_handler) { + vm_exit_during_initialization("Signal chaining detected for VM interrupt signal, try -XX:+UseAltSigs"); + } + } +} + +// This boolean allows users to forward their own non-matching signals +// to JVM_handle_solaris_signal, harmlessly. +bool os::Solaris::signal_handlers_are_installed = false; + +// For signal-chaining +bool os::Solaris::libjsig_is_loaded = false; +typedef struct sigaction *(*get_signal_t)(int); +get_signal_t os::Solaris::get_signal_action = NULL; + +struct sigaction* os::Solaris::get_chained_signal_action(int sig) { + struct sigaction *actp = NULL; + + if ((libjsig_is_loaded) && (sig <= Maxlibjsigsigs)) { + // Retrieve the old signal handler from libjsig + actp = (*get_signal_action)(sig); + } + if (actp == NULL) { + // Retrieve the preinstalled signal handler from jvm + actp = get_preinstalled_handler(sig); + } + + return actp; +} + +static bool call_chained_handler(struct sigaction *actp, int sig, + siginfo_t *siginfo, void *context) { + // Call the old signal handler + if (actp->sa_handler == SIG_DFL) { + // It's more reasonable to let jvm treat it as an unexpected exception + // instead of taking the default action. + return false; + } else if (actp->sa_handler != SIG_IGN) { + if ((actp->sa_flags & SA_NODEFER) == 0) { + // automaticlly block the signal + sigaddset(&(actp->sa_mask), sig); + } + + sa_handler_t hand; + sa_sigaction_t sa; + bool siginfo_flag_set = (actp->sa_flags & SA_SIGINFO) != 0; + // retrieve the chained handler + if (siginfo_flag_set) { + sa = actp->sa_sigaction; + } else { + hand = actp->sa_handler; + } + + if ((actp->sa_flags & SA_RESETHAND) != 0) { + actp->sa_handler = SIG_DFL; + } + + // try to honor the signal mask + sigset_t oset; + thr_sigsetmask(SIG_SETMASK, &(actp->sa_mask), &oset); + + // call into the chained handler + if (siginfo_flag_set) { + (*sa)(sig, siginfo, context); + } else { + (*hand)(sig); + } + + // restore the signal mask + thr_sigsetmask(SIG_SETMASK, &oset, 0); + } + // Tell jvm's signal handler the signal is taken care of. + return true; +} + +bool os::Solaris::chained_handler(int sig, siginfo_t* siginfo, void* context) { + bool chained = false; + // signal-chaining + if (UseSignalChaining) { + struct sigaction *actp = get_chained_signal_action(sig); + if (actp != NULL) { + chained = call_chained_handler(actp, sig, siginfo, context); + } + } + return chained; +} + +struct sigaction* os::Solaris::get_preinstalled_handler(int sig) { + assert((chainedsigactions != (struct sigaction *)NULL) && (preinstalled_sigs != (int *)NULL) , "signals not yet initialized"); + if (preinstalled_sigs[sig] != 0) { + return &chainedsigactions[sig]; + } + return NULL; +} + +void os::Solaris::save_preinstalled_handler(int sig, struct sigaction& oldAct) { + + assert(sig > 0 && sig <= Maxsignum, "vm signal out of expected range"); + assert((chainedsigactions != (struct sigaction *)NULL) && (preinstalled_sigs != (int *)NULL) , "signals not yet initialized"); + chainedsigactions[sig] = oldAct; + preinstalled_sigs[sig] = 1; +} + +void os::Solaris::set_signal_handler(int sig, bool set_installed, bool oktochain) { + // Check for overwrite. + struct sigaction oldAct; + sigaction(sig, (struct sigaction*)NULL, &oldAct); + void* oldhand = oldAct.sa_sigaction ? CAST_FROM_FN_PTR(void*, oldAct.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oldAct.sa_handler); + if (oldhand != CAST_FROM_FN_PTR(void*, SIG_DFL) && + oldhand != CAST_FROM_FN_PTR(void*, SIG_IGN) && + oldhand != CAST_FROM_FN_PTR(void*, signalHandler)) { + if (AllowUserSignalHandlers || !set_installed) { + // Do not overwrite; user takes responsibility to forward to us. + return; + } else if (UseSignalChaining) { + if (oktochain) { + // save the old handler in jvm + save_preinstalled_handler(sig, oldAct); + } else { + vm_exit_during_initialization("Signal chaining not allowed for VM interrupt signal, try -XX:+UseAltSigs."); + } + // libjsig also interposes the sigaction() call below and saves the + // old sigaction on it own. + } else { + fatal2("Encountered unexpected pre-existing sigaction handler %#lx for signal %d.", (long)oldhand, sig); + } + } + + struct sigaction sigAct; + sigfillset(&(sigAct.sa_mask)); + sigAct.sa_handler = SIG_DFL; + + sigAct.sa_sigaction = signalHandler; + // Handle SIGSEGV on alternate signal stack if + // not using stack banging + if (!UseStackBanging && sig == SIGSEGV) { + sigAct.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK; + // Interruptible i/o requires SA_RESTART cleared so EINTR + // is returned instead of restarting system calls + } else if (sig == os::Solaris::SIGinterrupt()) { + sigemptyset(&sigAct.sa_mask); + sigAct.sa_handler = NULL; + sigAct.sa_flags = SA_SIGINFO; + sigAct.sa_sigaction = sigINTRHandler; + } else { + sigAct.sa_flags = SA_SIGINFO | SA_RESTART; + } + os::Solaris::set_our_sigflags(sig, sigAct.sa_flags); + + sigaction(sig, &sigAct, &oldAct); + + void* oldhand2 = oldAct.sa_sigaction ? CAST_FROM_FN_PTR(void*, oldAct.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oldAct.sa_handler); + assert(oldhand2 == oldhand, "no concurrent signal handler installation"); +} + + +#define DO_SIGNAL_CHECK(sig) \ + if (!sigismember(&check_signal_done, sig)) \ + os::Solaris::check_signal_handler(sig) + +// This method is a periodic task to check for misbehaving JNI applications +// under CheckJNI, we can add any periodic checks here + +void os::run_periodic_checks() { + // A big source of grief is hijacking virt. addr 0x0 on Solaris, + // thereby preventing a NULL checks. + if(!check_addr0_done) check_addr0_done = check_addr0(tty); + + if (check_signals == false) return; + + // SEGV and BUS if overridden could potentially prevent + // generation of hs*.log in the event of a crash, debugging + // such a case can be very challenging, so we absolutely + // check for the following for a good measure: + DO_SIGNAL_CHECK(SIGSEGV); + DO_SIGNAL_CHECK(SIGILL); + DO_SIGNAL_CHECK(SIGFPE); + DO_SIGNAL_CHECK(SIGBUS); + DO_SIGNAL_CHECK(SIGPIPE); + DO_SIGNAL_CHECK(SIGXFSZ); + + // ReduceSignalUsage allows the user to override these handlers + // see comments at the very top and jvm_solaris.h + if (!ReduceSignalUsage) { + DO_SIGNAL_CHECK(SHUTDOWN1_SIGNAL); + DO_SIGNAL_CHECK(SHUTDOWN2_SIGNAL); + DO_SIGNAL_CHECK(SHUTDOWN3_SIGNAL); + DO_SIGNAL_CHECK(BREAK_SIGNAL); + } + + // See comments above for using JVM1/JVM2 and UseAltSigs + DO_SIGNAL_CHECK(os::Solaris::SIGinterrupt()); + DO_SIGNAL_CHECK(os::Solaris::SIGasync()); + +} + +typedef int (*os_sigaction_t)(int, const struct sigaction *, struct sigaction *); + +static os_sigaction_t os_sigaction = NULL; + +void os::Solaris::check_signal_handler(int sig) { + char buf[O_BUFLEN]; + address jvmHandler = NULL; + + struct sigaction act; + if (os_sigaction == NULL) { + // only trust the default sigaction, in case it has been interposed + os_sigaction = (os_sigaction_t)dlsym(RTLD_DEFAULT, "sigaction"); + if (os_sigaction == NULL) return; + } + + os_sigaction(sig, (struct sigaction*)NULL, &act); + + address thisHandler = (act.sa_flags & SA_SIGINFO) + ? CAST_FROM_FN_PTR(address, act.sa_sigaction) + : CAST_FROM_FN_PTR(address, act.sa_handler) ; + + + switch(sig) { + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGPIPE: + case SIGXFSZ: + case SIGILL: + jvmHandler = CAST_FROM_FN_PTR(address, signalHandler); + break; + + case SHUTDOWN1_SIGNAL: + case SHUTDOWN2_SIGNAL: + case SHUTDOWN3_SIGNAL: + case BREAK_SIGNAL: + jvmHandler = (address)user_handler(); + break; + + default: + int intrsig = os::Solaris::SIGinterrupt(); + int asynsig = os::Solaris::SIGasync(); + + if (sig == intrsig) { + jvmHandler = CAST_FROM_FN_PTR(address, sigINTRHandler); + } else if (sig == asynsig) { + jvmHandler = CAST_FROM_FN_PTR(address, signalHandler); + } else { + return; + } + break; + } + + + if (thisHandler != jvmHandler) { + tty->print("Warning: %s handler ", exception_name(sig, buf, O_BUFLEN)); + tty->print("expected:%s", get_signal_handler_name(jvmHandler, buf, O_BUFLEN)); + tty->print_cr(" found:%s", get_signal_handler_name(thisHandler, buf, O_BUFLEN)); + // No need to check this sig any longer + sigaddset(&check_signal_done, sig); + } else if(os::Solaris::get_our_sigflags(sig) != 0 && act.sa_flags != os::Solaris::get_our_sigflags(sig)) { + tty->print("Warning: %s handler flags ", exception_name(sig, buf, O_BUFLEN)); + tty->print("expected:" PTR32_FORMAT, os::Solaris::get_our_sigflags(sig)); + tty->print_cr(" found:" PTR32_FORMAT, act.sa_flags); + // No need to check this sig any longer + sigaddset(&check_signal_done, sig); + } + + // Print all the signal handler state + if (sigismember(&check_signal_done, sig)) { + print_signal_handlers(tty, buf, O_BUFLEN); + } + +} + +void os::Solaris::install_signal_handlers() { + bool libjsigdone = false; + signal_handlers_are_installed = true; + + // signal-chaining + typedef void (*signal_setting_t)(); + signal_setting_t begin_signal_setting = NULL; + signal_setting_t end_signal_setting = NULL; + begin_signal_setting = CAST_TO_FN_PTR(signal_setting_t, + dlsym(RTLD_DEFAULT, "JVM_begin_signal_setting")); + if (begin_signal_setting != NULL) { + end_signal_setting = CAST_TO_FN_PTR(signal_setting_t, + dlsym(RTLD_DEFAULT, "JVM_end_signal_setting")); + get_signal_action = CAST_TO_FN_PTR(get_signal_t, + dlsym(RTLD_DEFAULT, "JVM_get_signal_action")); + get_libjsig_version = CAST_TO_FN_PTR(version_getting_t, + dlsym(RTLD_DEFAULT, "JVM_get_libjsig_version")); + libjsig_is_loaded = true; + if (os::Solaris::get_libjsig_version != NULL) { + libjsigversion = (*os::Solaris::get_libjsig_version)(); + } + assert(UseSignalChaining, "should enable signal-chaining"); + } + if (libjsig_is_loaded) { + // Tell libjsig jvm is setting signal handlers + (*begin_signal_setting)(); + } + + set_signal_handler(SIGSEGV, true, true); + set_signal_handler(SIGPIPE, true, true); + set_signal_handler(SIGXFSZ, true, true); + set_signal_handler(SIGBUS, true, true); + set_signal_handler(SIGILL, true, true); + set_signal_handler(SIGFPE, true, true); + + + if (os::Solaris::SIGinterrupt() > OLDMAXSIGNUM || os::Solaris::SIGasync() > OLDMAXSIGNUM) { + + // Pre-1.4.1 Libjsig limited to signal chaining signals <= 32 so + // can not register overridable signals which might be > 32 + if (libjsig_is_loaded && libjsigversion <= JSIG_VERSION_1_4_1) { + // Tell libjsig jvm has finished setting signal handlers + (*end_signal_setting)(); + libjsigdone = true; + } + } + + // Never ok to chain our SIGinterrupt + set_signal_handler(os::Solaris::SIGinterrupt(), true, false); + set_signal_handler(os::Solaris::SIGasync(), true, true); + + if (libjsig_is_loaded && !libjsigdone) { + // Tell libjsig jvm finishes setting signal handlers + (*end_signal_setting)(); + } + + // We don't activate signal checker if libjsig is in place, we trust ourselves + // and if UserSignalHandler is installed all bets are off + if (CheckJNICalls) { + if (libjsig_is_loaded) { + tty->print_cr("Info: libjsig is activated, all active signal checking is disabled"); + check_signals = false; + } + if (AllowUserSignalHandlers) { + tty->print_cr("Info: AllowUserSignalHandlers is activated, all active signal checking is disabled"); + check_signals = false; + } + } +} + + +void report_error(const char* file_name, int line_no, const char* title, const char* format, ...); + +const char * signames[] = { + "SIG0", + "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", + "SIGABRT", "SIGEMT", "SIGFPE", "SIGKILL", "SIGBUS", + "SIGSEGV", "SIGSYS", "SIGPIPE", "SIGALRM", "SIGTERM", + "SIGUSR1", "SIGUSR2", "SIGCLD", "SIGPWR", "SIGWINCH", + "SIGURG", "SIGPOLL", "SIGSTOP", "SIGTSTP", "SIGCONT", + "SIGTTIN", "SIGTTOU", "SIGVTALRM", "SIGPROF", "SIGXCPU", + "SIGXFSZ", "SIGWAITING", "SIGLWP", "SIGFREEZE", "SIGTHAW", + "SIGCANCEL", "SIGLOST" +}; + +const char* os::exception_name(int exception_code, char* buf, size_t size) { + if (0 < exception_code && exception_code <= SIGRTMAX) { + // signal + if (exception_code < sizeof(signames)/sizeof(const char*)) { + jio_snprintf(buf, size, "%s", signames[exception_code]); + } else { + jio_snprintf(buf, size, "SIG%d", exception_code); + } + return buf; + } else { + return NULL; + } +} + +// (Static) wrappers for the new libthread API +int_fnP_thread_t_iP_uP_stack_tP_gregset_t os::Solaris::_thr_getstate; +int_fnP_thread_t_i_gregset_t os::Solaris::_thr_setstate; +int_fnP_thread_t_i os::Solaris::_thr_setmutator; +int_fnP_thread_t os::Solaris::_thr_suspend_mutator; +int_fnP_thread_t os::Solaris::_thr_continue_mutator; + +// (Static) wrappers for the liblgrp API +os::Solaris::lgrp_home_func_t os::Solaris::_lgrp_home; +os::Solaris::lgrp_init_func_t os::Solaris::_lgrp_init; +os::Solaris::lgrp_fini_func_t os::Solaris::_lgrp_fini; +os::Solaris::lgrp_root_func_t os::Solaris::_lgrp_root; +os::Solaris::lgrp_children_func_t os::Solaris::_lgrp_children; +os::Solaris::lgrp_nlgrps_func_t os::Solaris::_lgrp_nlgrps; +os::Solaris::lgrp_cookie_stale_func_t os::Solaris::_lgrp_cookie_stale; +os::Solaris::lgrp_cookie_t os::Solaris::_lgrp_cookie = 0; + +// (Static) wrapper for meminfo() call. +os::Solaris::meminfo_func_t os::Solaris::_meminfo = 0; + +static address resolve_symbol(const char *name) { + address addr; + + addr = (address) dlsym(RTLD_DEFAULT, name); + if(addr == NULL) { + // RTLD_DEFAULT was not defined on some early versions of 2.5.1 + addr = (address) dlsym(RTLD_NEXT, name); + if(addr == NULL) { + fatal(dlerror()); + } + } + return addr; +} + + + +// isT2_libthread() +// +// Routine to determine if we are currently using the new T2 libthread. +// +// We determine if we are using T2 by reading /proc/self/lstatus and +// looking for a thread with the ASLWP bit set. If we find this status +// bit set, we must assume that we are NOT using T2. The T2 team +// has approved this algorithm. +// +// We need to determine if we are running with the new T2 libthread +// since setting native thread priorities is handled differently +// when using this library. All threads created using T2 are bound +// threads. Calling thr_setprio is meaningless in this case. +// +bool isT2_libthread() { + int i, rslt; + static prheader_t * lwpArray = NULL; + static int lwpSize = 0; + static int lwpFile = -1; + lwpstatus_t * that; + int aslwpcount; + char lwpName [128]; + bool isT2 = false; + +#define ADR(x) ((uintptr_t)(x)) +#define LWPINDEX(ary,ix) ((lwpstatus_t *)(((ary)->pr_entsize * (ix)) + (ADR((ary) + 1)))) + + aslwpcount = 0; + lwpSize = 16*1024; + lwpArray = ( prheader_t *)NEW_C_HEAP_ARRAY (char, lwpSize); + lwpFile = open ("/proc/self/lstatus", O_RDONLY, 0); + if (lwpArray == NULL) { + if ( ThreadPriorityVerbose ) warning ("Couldn't allocate T2 Check array\n"); + return(isT2); + } + if (lwpFile < 0) { + if ( ThreadPriorityVerbose ) warning ("Couldn't open /proc/self/lstatus\n"); + return(isT2); + } + for (;;) { + lseek (lwpFile, 0, SEEK_SET); + rslt = read (lwpFile, lwpArray, lwpSize); + if ((lwpArray->pr_nent * lwpArray->pr_entsize) <= lwpSize) { + break; + } + FREE_C_HEAP_ARRAY(char, lwpArray); + lwpSize = lwpArray->pr_nent * lwpArray->pr_entsize; + lwpArray = ( prheader_t *)NEW_C_HEAP_ARRAY (char, lwpSize); + if (lwpArray == NULL) { + if ( ThreadPriorityVerbose ) warning ("Couldn't allocate T2 Check array\n"); + return(isT2); + } + } + + // We got a good snapshot - now iterate over the list. + for (i = 0; i < lwpArray->pr_nent; i++ ) { + that = LWPINDEX(lwpArray,i); + if (that->pr_flags & PR_ASLWP) { + aslwpcount++; + } + } + if ( aslwpcount == 0 ) isT2 = true; + + FREE_C_HEAP_ARRAY(char, lwpArray); + close (lwpFile); + if ( ThreadPriorityVerbose ) { + if ( isT2 ) tty->print_cr("We are running with a T2 libthread\n"); + else tty->print_cr("We are not running with a T2 libthread\n"); + } + return (isT2); +} + + +void os::Solaris::libthread_init() { + address func = (address)dlsym(RTLD_DEFAULT, "_thr_suspend_allmutators"); + + // Determine if we are running with the new T2 libthread + os::Solaris::set_T2_libthread(isT2_libthread()); + + lwp_priocntl_init(); + + // RTLD_DEFAULT was not defined on some early versions of 5.5.1 + if(func == NULL) { + func = (address) dlsym(RTLD_NEXT, "_thr_suspend_allmutators"); + // Guarantee that this VM is running on an new enough OS (5.6 or + // later) that it will have a new enough libthread.so. + guarantee(func != NULL, "libthread.so is too old."); + } + + // Initialize the new libthread getstate API wrappers + func = resolve_symbol("thr_getstate"); + os::Solaris::set_thr_getstate(CAST_TO_FN_PTR(int_fnP_thread_t_iP_uP_stack_tP_gregset_t, func)); + + func = resolve_symbol("thr_setstate"); + os::Solaris::set_thr_setstate(CAST_TO_FN_PTR(int_fnP_thread_t_i_gregset_t, func)); + + func = resolve_symbol("thr_setmutator"); + os::Solaris::set_thr_setmutator(CAST_TO_FN_PTR(int_fnP_thread_t_i, func)); + + func = resolve_symbol("thr_suspend_mutator"); + os::Solaris::set_thr_suspend_mutator(CAST_TO_FN_PTR(int_fnP_thread_t, func)); + + func = resolve_symbol("thr_continue_mutator"); + os::Solaris::set_thr_continue_mutator(CAST_TO_FN_PTR(int_fnP_thread_t, func)); + + int size; + void (*handler_info_func)(address *, int *); + handler_info_func = CAST_TO_FN_PTR(void (*)(address *, int *), resolve_symbol("thr_sighndlrinfo")); + handler_info_func(&handler_start, &size); + handler_end = handler_start + size; +} + + +int_fnP_mutex_tP os::Solaris::_mutex_lock; +int_fnP_mutex_tP os::Solaris::_mutex_trylock; +int_fnP_mutex_tP os::Solaris::_mutex_unlock; +int_fnP_mutex_tP_i_vP os::Solaris::_mutex_init; +int_fnP_mutex_tP os::Solaris::_mutex_destroy; +int os::Solaris::_mutex_scope = USYNC_THREAD; + +int_fnP_cond_tP_mutex_tP_timestruc_tP os::Solaris::_cond_timedwait; +int_fnP_cond_tP_mutex_tP os::Solaris::_cond_wait; +int_fnP_cond_tP os::Solaris::_cond_signal; +int_fnP_cond_tP os::Solaris::_cond_broadcast; +int_fnP_cond_tP_i_vP os::Solaris::_cond_init; +int_fnP_cond_tP os::Solaris::_cond_destroy; +int os::Solaris::_cond_scope = USYNC_THREAD; + +void os::Solaris::synchronization_init() { + if(UseLWPSynchronization) { + os::Solaris::set_mutex_lock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("_lwp_mutex_lock"))); + os::Solaris::set_mutex_trylock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("_lwp_mutex_trylock"))); + os::Solaris::set_mutex_unlock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("_lwp_mutex_unlock"))); + os::Solaris::set_mutex_init(lwp_mutex_init); + os::Solaris::set_mutex_destroy(lwp_mutex_destroy); + os::Solaris::set_mutex_scope(USYNC_THREAD); + + os::Solaris::set_cond_timedwait(CAST_TO_FN_PTR(int_fnP_cond_tP_mutex_tP_timestruc_tP, resolve_symbol("_lwp_cond_timedwait"))); + os::Solaris::set_cond_wait(CAST_TO_FN_PTR(int_fnP_cond_tP_mutex_tP, resolve_symbol("_lwp_cond_wait"))); + os::Solaris::set_cond_signal(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("_lwp_cond_signal"))); + os::Solaris::set_cond_broadcast(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("_lwp_cond_broadcast"))); + os::Solaris::set_cond_init(lwp_cond_init); + os::Solaris::set_cond_destroy(lwp_cond_destroy); + os::Solaris::set_cond_scope(USYNC_THREAD); + } + else { + os::Solaris::set_mutex_scope(USYNC_THREAD); + os::Solaris::set_cond_scope(USYNC_THREAD); + + if(UsePthreads) { + os::Solaris::set_mutex_lock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("pthread_mutex_lock"))); + os::Solaris::set_mutex_trylock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("pthread_mutex_trylock"))); + os::Solaris::set_mutex_unlock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("pthread_mutex_unlock"))); + os::Solaris::set_mutex_init(pthread_mutex_default_init); + os::Solaris::set_mutex_destroy(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("pthread_mutex_destroy"))); + + os::Solaris::set_cond_timedwait(CAST_TO_FN_PTR(int_fnP_cond_tP_mutex_tP_timestruc_tP, resolve_symbol("pthread_cond_timedwait"))); + os::Solaris::set_cond_wait(CAST_TO_FN_PTR(int_fnP_cond_tP_mutex_tP, resolve_symbol("pthread_cond_wait"))); + os::Solaris::set_cond_signal(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("pthread_cond_signal"))); + os::Solaris::set_cond_broadcast(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("pthread_cond_broadcast"))); + os::Solaris::set_cond_init(pthread_cond_default_init); + os::Solaris::set_cond_destroy(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("pthread_cond_destroy"))); + } + else { + os::Solaris::set_mutex_lock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("mutex_lock"))); + os::Solaris::set_mutex_trylock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("mutex_trylock"))); + os::Solaris::set_mutex_unlock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("mutex_unlock"))); + os::Solaris::set_mutex_init(::mutex_init); + os::Solaris::set_mutex_destroy(::mutex_destroy); + + os::Solaris::set_cond_timedwait(CAST_TO_FN_PTR(int_fnP_cond_tP_mutex_tP_timestruc_tP, resolve_symbol("cond_timedwait"))); + os::Solaris::set_cond_wait(CAST_TO_FN_PTR(int_fnP_cond_tP_mutex_tP, resolve_symbol("cond_wait"))); + os::Solaris::set_cond_signal(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("cond_signal"))); + os::Solaris::set_cond_broadcast(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("cond_broadcast"))); + os::Solaris::set_cond_init(::cond_init); + os::Solaris::set_cond_destroy(::cond_destroy); + } + } +} + +void os::Solaris::liblgrp_init() { + void *handle = dlopen("liblgrp.so", RTLD_LAZY); + if (handle != NULL) { + os::Solaris::set_lgrp_home(CAST_TO_FN_PTR(lgrp_home_func_t, dlsym(handle, "lgrp_home"))); + os::Solaris::set_lgrp_init(CAST_TO_FN_PTR(lgrp_init_func_t, dlsym(handle, "lgrp_init"))); + os::Solaris::set_lgrp_fini(CAST_TO_FN_PTR(lgrp_fini_func_t, dlsym(handle, "lgrp_fini"))); + os::Solaris::set_lgrp_root(CAST_TO_FN_PTR(lgrp_root_func_t, dlsym(handle, "lgrp_root"))); + os::Solaris::set_lgrp_children(CAST_TO_FN_PTR(lgrp_children_func_t, dlsym(handle, "lgrp_children"))); + os::Solaris::set_lgrp_nlgrps(CAST_TO_FN_PTR(lgrp_nlgrps_func_t, dlsym(handle, "lgrp_nlgrps"))); + os::Solaris::set_lgrp_cookie_stale(CAST_TO_FN_PTR(lgrp_cookie_stale_func_t, + dlsym(handle, "lgrp_cookie_stale"))); + + lgrp_cookie_t c = lgrp_init(LGRP_VIEW_CALLER); + set_lgrp_cookie(c); + } else { + warning("your OS does not support NUMA"); + } +} + +void os::Solaris::misc_sym_init() { + address func = (address)dlsym(RTLD_DEFAULT, "meminfo"); + if(func == NULL) { + func = (address) dlsym(RTLD_NEXT, "meminfo"); + } + if (func != NULL) { + os::Solaris::set_meminfo(CAST_TO_FN_PTR(meminfo_func_t, func)); + } +} + +// Symbol doesn't exist in Solaris 8 pset.h +#ifndef PS_MYID +#define PS_MYID -3 +#endif + +// int pset_getloadavg(psetid_t pset, double loadavg[], int nelem); +typedef long (*pset_getloadavg_type)(psetid_t pset, double loadavg[], int nelem); +static pset_getloadavg_type pset_getloadavg_ptr = NULL; + +void init_pset_getloadavg_ptr(void) { + pset_getloadavg_ptr = + (pset_getloadavg_type)dlsym(RTLD_DEFAULT, "pset_getloadavg"); + if (PrintMiscellaneous && Verbose && pset_getloadavg_ptr == NULL) { + warning("pset_getloadavg function not found"); + } +} + +int os::Solaris::_dev_zero_fd = -1; + +// this is called _before_ the global arguments have been parsed +void os::init(void) { + _initial_pid = getpid(); + + max_hrtime = first_hrtime = gethrtime(); + + init_random(1234567); + + page_size = sysconf(_SC_PAGESIZE); + if (page_size == -1) + fatal1("os_solaris.cpp: os::init: sysconf failed (%s)", strerror(errno)); + init_page_sizes((size_t) page_size); + + Solaris::initialize_system_info(); + + int fd = open("/dev/zero", O_RDWR); + if (fd < 0) { + fatal1("os::init: cannot open /dev/zero (%s)", strerror(errno)); + } else { + Solaris::set_dev_zero_fd(fd); + + // Close on exec, child won't inherit. + fcntl(fd, F_SETFD, FD_CLOEXEC); + } + + clock_tics_per_sec = CLK_TCK; + + // check if dladdr1() exists; dladdr1 can provide more information than + // dladdr for os::dll_address_to_function_name. It comes with SunOS 5.9 + // and is available on linker patches for 5.7 and 5.8. + // libdl.so must have been loaded, this call is just an entry lookup + void * hdl = dlopen("libdl.so", RTLD_NOW); + if (hdl) + dladdr1_func = CAST_TO_FN_PTR(dladdr1_func_type, dlsym(hdl, "dladdr1")); + + // (Solaris only) this switches to calls that actually do locking. + ThreadCritical::initialize(); + + main_thread = thr_self(); + + // Constant minimum stack size allowed. It must be at least + // the minimum of what the OS supports (thr_min_stack()), and + // enough to allow the thread to get to user bytecode execution. + Solaris::min_stack_allowed = MAX2(thr_min_stack(), Solaris::min_stack_allowed); + // If the pagesize of the VM is greater than 8K determine the appropriate + // number of initial guard pages. The user can change this with the + // command line arguments, if needed. + if (vm_page_size() > 8*K) { + StackYellowPages = 1; + StackRedPages = 1; + StackShadowPages = round_to((StackShadowPages*8*K), vm_page_size()) / vm_page_size(); + } +} + +// To install functions for atexit system call +extern "C" { + static void perfMemory_exit_helper() { + perfMemory_exit(); + } +} + +// this is called _after_ the global arguments have been parsed +jint os::init_2(void) { + // try to enable extended file IO ASAP, see 6431278 + os::Solaris::try_enable_extended_io(); + + // Allocate a single page and mark it as readable for safepoint polling. Also + // use this first mmap call to check support for MAP_ALIGN. + address polling_page = (address)Solaris::mmap_chunk((char*)page_size, + page_size, + MAP_PRIVATE | MAP_ALIGN, + PROT_READ); + if (polling_page == NULL) { + has_map_align = false; + polling_page = (address)Solaris::mmap_chunk(NULL, page_size, MAP_PRIVATE, + PROT_READ); + } + + os::set_polling_page(polling_page); + +#ifndef PRODUCT + if( Verbose && PrintMiscellaneous ) + tty->print("[SafePoint Polling address: " INTPTR_FORMAT "]\n", (intptr_t)polling_page); +#endif + + if (!UseMembar) { + address mem_serialize_page = (address)Solaris::mmap_chunk( NULL, page_size, MAP_PRIVATE, PROT_READ | PROT_WRITE ); + guarantee( mem_serialize_page != NULL, "mmap Failed for memory serialize page"); + os::set_memory_serialize_page( mem_serialize_page ); + +#ifndef PRODUCT + if(Verbose && PrintMiscellaneous) + tty->print("[Memory Serialize Page address: " INTPTR_FORMAT "]\n", (intptr_t)mem_serialize_page); +#endif +} + + FLAG_SET_DEFAULT(UseLargePages, os::large_page_init()); + + // Check minimum allowable stack size for thread creation and to initialize + // the java system classes, including StackOverflowError - depends on page + // size. Add a page for compiler2 recursion in main thread. + // Add in BytesPerWord times page size to account for VM stack during + // class initialization depending on 32 or 64 bit VM. + guarantee((Solaris::min_stack_allowed >= + (StackYellowPages+StackRedPages+StackShadowPages+BytesPerWord + COMPILER2_PRESENT(+1)) * page_size), + "need to increase Solaris::min_stack_allowed on this platform"); + + size_t threadStackSizeInBytes = ThreadStackSize * K; + if (threadStackSizeInBytes != 0 && + threadStackSizeInBytes < Solaris::min_stack_allowed) { + tty->print_cr("\nThe stack size specified is too small, Specify at least %dk", + Solaris::min_stack_allowed/K); + return JNI_ERR; + } + + // For 64kbps there will be a 64kb page size, which makes + // the usable default stack size quite a bit less. Increase the + // stack for 64kb (or any > than 8kb) pages, this increases + // virtual memory fragmentation (since we're not creating the + // stack on a power of 2 boundary. The real fix for this + // should be to fix the guard page mechanism. + + if (vm_page_size() > 8*K) { + threadStackSizeInBytes = (threadStackSizeInBytes != 0) + ? threadStackSizeInBytes + + ((StackYellowPages + StackRedPages) * vm_page_size()) + : 0; + ThreadStackSize = threadStackSizeInBytes/K; + } + + // Make the stack size a multiple of the page size so that + // the yellow/red zones can be guarded. + JavaThread::set_stack_size_at_create(round_to(threadStackSizeInBytes, + vm_page_size())); + + Solaris::libthread_init(); + if (UseNUMA) { + Solaris::liblgrp_init(); + } + Solaris::misc_sym_init(); + Solaris::signal_sets_init(); + Solaris::init_signal_mem(); + Solaris::install_signal_handlers(); + + if (libjsigversion < JSIG_VERSION_1_4_1) { + Maxlibjsigsigs = OLDMAXSIGNUM; + } + + // initialize synchronization primitives to use either thread or + // lwp synchronization (controlled by UseLWPSynchronization) + Solaris::synchronization_init(); + + if (MaxFDLimit) { + // set the number of file descriptors to max. print out error + // if getrlimit/setrlimit fails but continue regardless. + struct rlimit nbr_files; + int status = getrlimit(RLIMIT_NOFILE, &nbr_files); + if (status != 0) { + if (PrintMiscellaneous && (Verbose || WizardMode)) + perror("os::init_2 getrlimit failed"); + } else { + nbr_files.rlim_cur = nbr_files.rlim_max; + status = setrlimit(RLIMIT_NOFILE, &nbr_files); + if (status != 0) { + if (PrintMiscellaneous && (Verbose || WizardMode)) + perror("os::init_2 setrlimit failed"); + } + } + } + + // Initialize HPI. + jint hpi_result = hpi::initialize(); + if (hpi_result != JNI_OK) { + tty->print_cr("There was an error trying to initialize the HPI library."); + return hpi_result; + } + + // Calculate theoretical max. size of Threads to guard gainst + // artifical out-of-memory situations, where all available address- + // space has been reserved by thread stacks. Default stack size is 1Mb. + size_t pre_thread_stack_size = (JavaThread::stack_size_at_create()) ? + JavaThread::stack_size_at_create() : (1*K*K); + assert(pre_thread_stack_size != 0, "Must have a stack"); + // Solaris has a maximum of 4Gb of user programs. Calculate the thread limit when + // we should start doing Virtual Memory banging. Currently when the threads will + // have used all but 200Mb of space. + size_t max_address_space = ((unsigned int)4 * K * K * K) - (200 * K * K); + Solaris::_os_thread_limit = max_address_space / pre_thread_stack_size; + + // at-exit methods are called in the reverse order of their registration. + // In Solaris 7 and earlier, atexit functions are called on return from + // main or as a result of a call to exit(3C). There can be only 32 of + // these functions registered and atexit() does not set errno. In Solaris + // 8 and later, there is no limit to the number of functions registered + // and atexit() sets errno. In addition, in Solaris 8 and later, atexit + // functions are called upon dlclose(3DL) in addition to return from main + // and exit(3C). + + if (PerfAllowAtExitRegistration) { + // only register atexit functions if PerfAllowAtExitRegistration is set. + // atexit functions can be delayed until process exit time, which + // can be problematic for embedded VM situations. Embedded VMs should + // call DestroyJavaVM() to assure that VM resources are released. + + // note: perfMemory_exit_helper atexit function may be removed in + // the future if the appropriate cleanup code can be added to the + // VM_Exit VMOperation's doit method. + if (atexit(perfMemory_exit_helper) != 0) { + warning("os::init2 atexit(perfMemory_exit_helper) failed"); + } + } + + // Init pset_loadavg function pointer + init_pset_getloadavg_ptr(); + + return JNI_OK; +} + + +// Mark the polling page as unreadable +void os::make_polling_page_unreadable(void) { + if( mprotect((char *)_polling_page, page_size, PROT_NONE) != 0 ) + fatal("Could not disable polling page"); +}; + +// Mark the polling page as readable +void os::make_polling_page_readable(void) { + if( mprotect((char *)_polling_page, page_size, PROT_READ) != 0 ) + fatal("Could not enable polling page"); +}; + +// OS interface. + +int os::stat(const char *path, struct stat *sbuf) { + char pathbuf[MAX_PATH]; + if (strlen(path) > MAX_PATH - 1) { + errno = ENAMETOOLONG; + return -1; + } + hpi::native_path(strcpy(pathbuf, path)); + return ::stat(pathbuf, sbuf); +} + + +bool os::check_heap(bool force) { return true; } + +typedef int (*vsnprintf_t)(char* buf, size_t count, const char* fmt, va_list argptr); +static vsnprintf_t sol_vsnprintf = NULL; + +int local_vsnprintf(char* buf, size_t count, const char* fmt, va_list argptr) { + if (!sol_vsnprintf) { + //search for the named symbol in the objects that were loaded after libjvm + void* where = RTLD_NEXT; + if ((sol_vsnprintf = CAST_TO_FN_PTR(vsnprintf_t, dlsym(where, "__vsnprintf"))) == NULL) + sol_vsnprintf = CAST_TO_FN_PTR(vsnprintf_t, dlsym(where, "vsnprintf")); + if (!sol_vsnprintf){ + //search for the named symbol in the objects that were loaded before libjvm + where = RTLD_DEFAULT; + if ((sol_vsnprintf = CAST_TO_FN_PTR(vsnprintf_t, dlsym(where, "__vsnprintf"))) == NULL) + sol_vsnprintf = CAST_TO_FN_PTR(vsnprintf_t, dlsym(where, "vsnprintf")); + assert(sol_vsnprintf != NULL, "vsnprintf not found"); + } + } + return (*sol_vsnprintf)(buf, count, fmt, argptr); +} + + +// Is a (classpath) directory empty? +bool os::dir_is_empty(const char* path) { + DIR *dir = NULL; + struct dirent *ptr; + + dir = opendir(path); + if (dir == NULL) return true; + + /* Scan the directory */ + bool result = true; + char buf[sizeof(struct dirent) + MAX_PATH]; + struct dirent *dbuf = (struct dirent *) buf; + while (result && (ptr = readdir(dir, dbuf)) != NULL) { + if (strcmp(ptr->d_name, ".") != 0 && strcmp(ptr->d_name, "..") != 0) { + result = false; + } + } + closedir(dir); + return result; +} + +// create binary file, rewriting existing file if required +int os::create_binary_file(const char* path, bool rewrite_existing) { + int oflags = O_WRONLY | O_CREAT; + if (!rewrite_existing) { + oflags |= O_EXCL; + } + return ::open64(path, oflags, S_IREAD | S_IWRITE); +} + +// return current position of file pointer +jlong os::current_file_offset(int fd) { + return (jlong)::lseek64(fd, (off64_t)0, SEEK_CUR); +} + +// move file pointer to the specified offset +jlong os::seek_to_file_offset(int fd, jlong offset) { + return (jlong)::lseek64(fd, (off64_t)offset, SEEK_SET); +} + +// Map a block of memory. +char* os::map_memory(int fd, const char* file_name, size_t file_offset, + char *addr, size_t bytes, bool read_only, + bool allow_exec) { + int prot; + int flags; + + if (read_only) { + prot = PROT_READ; + flags = MAP_SHARED; + } else { + prot = PROT_READ | PROT_WRITE; + flags = MAP_PRIVATE; + } + + if (allow_exec) { + prot |= PROT_EXEC; + } + + if (addr != NULL) { + flags |= MAP_FIXED; + } + + char* mapped_address = (char*)mmap(addr, (size_t)bytes, prot, flags, + fd, file_offset); + if (mapped_address == MAP_FAILED) { + return NULL; + } + return mapped_address; +} + + +// Remap a block of memory. +char* os::remap_memory(int fd, const char* file_name, size_t file_offset, + char *addr, size_t bytes, bool read_only, + bool allow_exec) { + // same as map_memory() on this OS + return os::map_memory(fd, file_name, file_offset, addr, bytes, read_only, + allow_exec); +} + + +// Unmap a block of memory. +bool os::unmap_memory(char* addr, size_t bytes) { + return munmap(addr, bytes) == 0; +} + +void os::pause() { + char filename[MAX_PATH]; + if (PauseAtStartupFile && PauseAtStartupFile[0]) { + jio_snprintf(filename, MAX_PATH, PauseAtStartupFile); + } else { + jio_snprintf(filename, MAX_PATH, "./vm.paused.%d", current_process_id()); + } + + int fd = ::open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd != -1) { + struct stat buf; + close(fd); + while (::stat(filename, &buf) == 0) { + (void)::poll(NULL, 0, 100); + } + } else { + jio_fprintf(stderr, + "Could not open pause file '%s', continuing immediately.\n", filename); + } +} + +#ifndef PRODUCT +#ifdef INTERPOSE_ON_SYSTEM_SYNCH_FUNCTIONS +// Turn this on if you need to trace synch operations. +// Set RECORD_SYNCH_LIMIT to a large-enough value, +// and call record_synch_enable and record_synch_disable +// around the computation of interest. + +void record_synch(char* name, bool returning); // defined below + +class RecordSynch { + char* _name; + public: + RecordSynch(char* name) :_name(name) + { record_synch(_name, false); } + ~RecordSynch() { record_synch(_name, true); } +}; + +#define CHECK_SYNCH_OP(ret, name, params, args, inner) \ +extern "C" ret name params { \ + typedef ret name##_t params; \ + static name##_t* implem = NULL; \ + static int callcount = 0; \ + if (implem == NULL) { \ + implem = (name##_t*) dlsym(RTLD_NEXT, #name); \ + if (implem == NULL) fatal(dlerror()); \ + } \ + ++callcount; \ + RecordSynch _rs(#name); \ + inner; \ + return implem args; \ +} +// in dbx, examine callcounts this way: +// for n in $(eval whereis callcount | awk '{print $2}'); do print $n; done + +#define CHECK_POINTER_OK(p) \ + (Universe::perm_gen() == NULL || !Universe::is_reserved_heap((oop)(p))) +#define CHECK_MU \ + if (!CHECK_POINTER_OK(mu)) fatal("Mutex must be in C heap only."); +#define CHECK_CV \ + if (!CHECK_POINTER_OK(cv)) fatal("Condvar must be in C heap only."); +#define CHECK_P(p) \ + if (!CHECK_POINTER_OK(p)) fatal(false, "Pointer must be in C heap only."); + +#define CHECK_MUTEX(mutex_op) \ +CHECK_SYNCH_OP(int, mutex_op, (mutex_t *mu), (mu), CHECK_MU); + +CHECK_MUTEX( mutex_lock) +CHECK_MUTEX( _mutex_lock) +CHECK_MUTEX( mutex_unlock) +CHECK_MUTEX(_mutex_unlock) +CHECK_MUTEX( mutex_trylock) +CHECK_MUTEX(_mutex_trylock) + +#define CHECK_COND(cond_op) \ +CHECK_SYNCH_OP(int, cond_op, (cond_t *cv, mutex_t *mu), (cv, mu), CHECK_MU;CHECK_CV); + +CHECK_COND( cond_wait); +CHECK_COND(_cond_wait); +CHECK_COND(_cond_wait_cancel); + +#define CHECK_COND2(cond_op) \ +CHECK_SYNCH_OP(int, cond_op, (cond_t *cv, mutex_t *mu, timestruc_t* ts), (cv, mu, ts), CHECK_MU;CHECK_CV); + +CHECK_COND2( cond_timedwait); +CHECK_COND2(_cond_timedwait); +CHECK_COND2(_cond_timedwait_cancel); + +// do the _lwp_* versions too +#define mutex_t lwp_mutex_t +#define cond_t lwp_cond_t +CHECK_MUTEX( _lwp_mutex_lock) +CHECK_MUTEX( _lwp_mutex_unlock) +CHECK_MUTEX( _lwp_mutex_trylock) +CHECK_MUTEX( __lwp_mutex_lock) +CHECK_MUTEX( __lwp_mutex_unlock) +CHECK_MUTEX( __lwp_mutex_trylock) +CHECK_MUTEX(___lwp_mutex_lock) +CHECK_MUTEX(___lwp_mutex_unlock) + +CHECK_COND( _lwp_cond_wait); +CHECK_COND( __lwp_cond_wait); +CHECK_COND(___lwp_cond_wait); + +CHECK_COND2( _lwp_cond_timedwait); +CHECK_COND2( __lwp_cond_timedwait); +#undef mutex_t +#undef cond_t + +CHECK_SYNCH_OP(int, _lwp_suspend2, (int lwp, int *n), (lwp, n), 0); +CHECK_SYNCH_OP(int,__lwp_suspend2, (int lwp, int *n), (lwp, n), 0); +CHECK_SYNCH_OP(int, _lwp_kill, (int lwp, int n), (lwp, n), 0); +CHECK_SYNCH_OP(int,__lwp_kill, (int lwp, int n), (lwp, n), 0); +CHECK_SYNCH_OP(int, _lwp_sema_wait, (lwp_sema_t* p), (p), CHECK_P(p)); +CHECK_SYNCH_OP(int,__lwp_sema_wait, (lwp_sema_t* p), (p), CHECK_P(p)); +CHECK_SYNCH_OP(int, _lwp_cond_broadcast, (lwp_cond_t* cv), (cv), CHECK_CV); +CHECK_SYNCH_OP(int,__lwp_cond_broadcast, (lwp_cond_t* cv), (cv), CHECK_CV); + + +// recording machinery: + +enum { RECORD_SYNCH_LIMIT = 200 }; +char* record_synch_name[RECORD_SYNCH_LIMIT]; +void* record_synch_arg0ptr[RECORD_SYNCH_LIMIT]; +bool record_synch_returning[RECORD_SYNCH_LIMIT]; +thread_t record_synch_thread[RECORD_SYNCH_LIMIT]; +int record_synch_count = 0; +bool record_synch_enabled = false; + +// in dbx, examine recorded data this way: +// for n in name arg0ptr returning thread; do print record_synch_$n[0..record_synch_count-1]; done + +void record_synch(char* name, bool returning) { + if (record_synch_enabled) { + if (record_synch_count < RECORD_SYNCH_LIMIT) { + record_synch_name[record_synch_count] = name; + record_synch_returning[record_synch_count] = returning; + record_synch_thread[record_synch_count] = thr_self(); + record_synch_arg0ptr[record_synch_count] = &name; + record_synch_count++; + } + // put more checking code here: + // ... + } +} + +void record_synch_enable() { + // start collecting trace data, if not already doing so + if (!record_synch_enabled) record_synch_count = 0; + record_synch_enabled = true; +} + +void record_synch_disable() { + // stop collecting trace data + record_synch_enabled = false; +} + +#endif // INTERPOSE_ON_SYSTEM_SYNCH_FUNCTIONS +#endif // PRODUCT + +const intptr_t thr_time_off = (intptr_t)(&((prusage_t *)(NULL))->pr_utime); +const intptr_t thr_time_size = (intptr_t)(&((prusage_t *)(NULL))->pr_ttime) - + (intptr_t)(&((prusage_t *)(NULL))->pr_utime); + + +// JVMTI & JVM monitoring and management support +// The thread_cpu_time() and current_thread_cpu_time() are only +// supported if is_thread_cpu_time_supported() returns true. +// They are not supported on Solaris T1. + +// current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) +// are used by JVM M&M and JVMTI to get user+sys or user CPU time +// of a thread. +// +// current_thread_cpu_time() and thread_cpu_time(Thread *) +// returns the fast estimate available on the platform. + +// hrtime_t gethrvtime() return value includes +// user time but does not include system time +jlong os::current_thread_cpu_time() { + return (jlong) gethrvtime(); +} + +jlong os::thread_cpu_time(Thread *thread) { + // return user level CPU time only to be consistent with + // what current_thread_cpu_time returns. + // thread_cpu_time_info() must be changed if this changes + return os::thread_cpu_time(thread, false /* user time only */); +} + +jlong os::current_thread_cpu_time(bool user_sys_cpu_time) { + if (user_sys_cpu_time) { + return os::thread_cpu_time(Thread::current(), user_sys_cpu_time); + } else { + return os::current_thread_cpu_time(); + } +} + +jlong os::thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { + char proc_name[64]; + int count; + prusage_t prusage; + jlong lwp_time; + int fd; + + sprintf(proc_name, "/proc/%d/lwp/%d/lwpusage", + getpid(), + thread->osthread()->lwp_id()); + fd = open(proc_name, O_RDONLY); + if ( fd == -1 ) return -1; + + do { + count = pread(fd, + (void *)&prusage.pr_utime, + thr_time_size, + thr_time_off); + } while (count < 0 && errno == EINTR); + close(fd); + if ( count < 0 ) return -1; + + if (user_sys_cpu_time) { + // user + system CPU time + lwp_time = (((jlong)prusage.pr_stime.tv_sec + + (jlong)prusage.pr_utime.tv_sec) * (jlong)1000000000) + + (jlong)prusage.pr_stime.tv_nsec + + (jlong)prusage.pr_utime.tv_nsec; + } else { + // user level CPU time only + lwp_time = ((jlong)prusage.pr_utime.tv_sec * (jlong)1000000000) + + (jlong)prusage.pr_utime.tv_nsec; + } + + return(lwp_time); +} + +void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { + info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + info_ptr->may_skip_backward = false; // elapsed time not wall time + info_ptr->may_skip_forward = false; // elapsed time not wall time + info_ptr->kind = JVMTI_TIMER_USER_CPU; // only user time is returned +} + +void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { + info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + info_ptr->may_skip_backward = false; // elapsed time not wall time + info_ptr->may_skip_forward = false; // elapsed time not wall time + info_ptr->kind = JVMTI_TIMER_USER_CPU; // only user time is returned +} + +bool os::is_thread_cpu_time_supported() { + if ( os::Solaris::T2_libthread() || UseBoundThreads ) { + return true; + } else { + return false; + } +} + +// System loadavg support. Returns -1 if load average cannot be obtained. +// Return the load average for our processor set if the primitive exists +// (Solaris 9 and later). Otherwise just return system wide loadavg. +int os::loadavg(double loadavg[], int nelem) { + if (pset_getloadavg_ptr != NULL) { + return (*pset_getloadavg_ptr)(PS_MYID, loadavg, nelem); + } else { + return ::getloadavg(loadavg, nelem); + } +} + +//--------------------------------------------------------------------------------- +#ifndef PRODUCT + +static address same_page(address x, address y) { + intptr_t page_bits = -os::vm_page_size(); + if ((intptr_t(x) & page_bits) == (intptr_t(y) & page_bits)) + return x; + else if (x > y) + return (address)(intptr_t(y) | ~page_bits) + 1; + else + return (address)(intptr_t(y) & page_bits); +} + +bool os::find(address addr) { + Dl_info dlinfo; + memset(&dlinfo, 0, sizeof(dlinfo)); + if (dladdr(addr, &dlinfo)) { +#ifdef _LP64 + tty->print("0x%016lx: ", addr); +#else + tty->print("0x%08x: ", addr); +#endif + if (dlinfo.dli_sname != NULL) + tty->print("%s+%#lx", dlinfo.dli_sname, addr-(intptr_t)dlinfo.dli_saddr); + else if (dlinfo.dli_fname) + tty->print("", addr-(intptr_t)dlinfo.dli_fbase); + else + tty->print(""); + if (dlinfo.dli_fname) tty->print(" in %s", dlinfo.dli_fname); +#ifdef _LP64 + if (dlinfo.dli_fbase) tty->print(" at 0x%016lx", dlinfo.dli_fbase); +#else + if (dlinfo.dli_fbase) tty->print(" at 0x%08x", dlinfo.dli_fbase); +#endif + tty->cr(); + + if (Verbose) { + // decode some bytes around the PC + address begin = same_page(addr-40, addr); + address end = same_page(addr+40, addr); + address lowest = (address) dlinfo.dli_sname; + if (!lowest) lowest = (address) dlinfo.dli_fbase; + if (begin < lowest) begin = lowest; + Dl_info dlinfo2; + if (dladdr(end, &dlinfo2) && dlinfo2.dli_saddr != dlinfo.dli_saddr + && end > dlinfo2.dli_saddr && dlinfo2.dli_saddr > begin) + end = (address) dlinfo2.dli_saddr; + Disassembler::decode(begin, end); + } + return true; + } + return false; +} + +#endif + + +// Following function has been added to support HotSparc's libjvm.so running +// under Solaris production JDK 1.2.2 / 1.3.0. These came from +// src/solaris/hpi/native_threads in the EVM codebase. +// +// NOTE: This is no longer needed in the 1.3.1 and 1.4 production release +// libraries and should thus be removed. We will leave it behind for a while +// until we no longer want to able to run on top of 1.3.0 Solaris production +// JDK. See 4341971. + +#define STACK_SLACK 0x800 + +extern "C" { + intptr_t sysThreadAvailableStackWithSlack() { + stack_t st; + intptr_t retval, stack_top; + retval = thr_stksegment(&st); + assert(retval == 0, "incorrect return value from thr_stksegment"); + assert((address)&st < (address)st.ss_sp, "Invalid stack base returned"); + assert((address)&st > (address)st.ss_sp-st.ss_size, "Invalid stack size returned"); + stack_top=(intptr_t)st.ss_sp-st.ss_size; + return ((intptr_t)&stack_top - stack_top - STACK_SLACK); + } +} + +// Just to get the Kernel build to link on solaris for testing. + +extern "C" { +class ASGCT_CallTrace; +void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) + KERNEL_RETURN; +} + + +// ObjectMonitor park-unpark infrastructure ... +// +// We implement Solaris and Linux PlatformEvents with the +// obvious condvar-mutex-flag triple. +// Another alternative that works quite well is pipes: +// Each PlatformEvent consists of a pipe-pair. +// The thread associated with the PlatformEvent +// calls park(), which reads from the input end of the pipe. +// Unpark() writes into the other end of the pipe. +// The write-side of the pipe must be set NDELAY. +// Unfortunately pipes consume a large # of handles. +// Native solaris lwp_park() and lwp_unpark() work nicely, too. +// Using pipes for the 1st few threads might be workable, however. +// +// park() is permitted to return spuriously. +// Callers of park() should wrap the call to park() in +// an appropriate loop. A litmus test for the correct +// usage of park is the following: if park() were modified +// to immediately return 0 your code should still work, +// albeit degenerating to a spin loop. +// +// An interesting optimization for park() is to use a trylock() +// to attempt to acquire the mutex. If the trylock() fails +// then we know that a concurrent unpark() operation is in-progress. +// in that case the park() code could simply set _count to 0 +// and return immediately. The subsequent park() operation *might* +// return immediately. That's harmless as the caller of park() is +// expected to loop. By using trylock() we will have avoided a +// avoided a context switch caused by contention on the per-thread mutex. +// +// TODO-FIXME: +// 1. Reconcile Doug's JSR166 j.u.c park-unpark with the +// objectmonitor implementation. +// 2. Collapse the JSR166 parker event, and the +// objectmonitor ParkEvent into a single "Event" construct. +// 3. In park() and unpark() add: +// assert (Thread::current() == AssociatedWith). +// 4. add spurious wakeup injection on a -XX:EarlyParkReturn=N switch. +// 1-out-of-N park() operations will return immediately. +// +// _Event transitions in park() +// -1 => -1 : illegal +// 1 => 0 : pass - return immediately +// 0 => -1 : block +// +// _Event serves as a restricted-range semaphore. +// +// Another possible encoding of _Event would be with +// explicit "PARKED" == 01b and "SIGNALED" == 10b bits. +// +// TODO-FIXME: add DTRACE probes for: +// 1. Tx parks +// 2. Ty unparks Tx +// 3. Tx resumes from park + + +// value determined through experimentation +#define ROUNDINGFIX 11 + +// utility to compute the abstime argument to timedwait. +// TODO-FIXME: switch from compute_abstime() to unpackTime(). + +static timestruc_t* compute_abstime(timestruc_t* abstime, jlong millis) { + // millis is the relative timeout time + // abstime will be the absolute timeout time + if (millis < 0) millis = 0; + struct timeval now; + int status = gettimeofday(&now, NULL); + assert(status == 0, "gettimeofday"); + jlong seconds = millis / 1000; + jlong max_wait_period; + + if (UseLWPSynchronization) { + // forward port of fix for 4275818 (not sleeping long enough) + // There was a bug in Solaris 6, 7 and pre-patch 5 of 8 where + // _lwp_cond_timedwait() used a round_down algorithm rather + // than a round_up. For millis less than our roundfactor + // it rounded down to 0 which doesn't meet the spec. + // For millis > roundfactor we may return a bit sooner, but + // since we can not accurately identify the patch level and + // this has already been fixed in Solaris 9 and 8 we will + // leave it alone rather than always rounding down. + + if (millis > 0 && millis < ROUNDINGFIX) millis = ROUNDINGFIX; + // It appears that when we go directly through Solaris _lwp_cond_timedwait() + // the acceptable max time threshold is smaller than for libthread on 2.5.1 and 2.6 + max_wait_period = 21000000; + } else { + max_wait_period = 50000000; + } + millis %= 1000; + if (seconds > max_wait_period) { // see man cond_timedwait(3T) + seconds = max_wait_period; + } + abstime->tv_sec = now.tv_sec + seconds; + long usec = now.tv_usec + millis * 1000; + if (usec >= 1000000) { + abstime->tv_sec += 1; + usec -= 1000000; + } + abstime->tv_nsec = usec * 1000; + return abstime; +} + +// Test-and-clear _Event, always leaves _Event set to 0, returns immediately. +// Conceptually TryPark() should be equivalent to park(0). + +int os::PlatformEvent::TryPark() { + for (;;) { + const int v = _Event ; + guarantee ((v == 0) || (v == 1), "invariant") ; + if (Atomic::cmpxchg (0, &_Event, v) == v) return v ; + } +} + +void os::PlatformEvent::park() { // AKA: down() + // Invariant: Only the thread associated with the Event/PlatformEvent + // may call park(). + int v ; + for (;;) { + v = _Event ; + if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ; + } + guarantee (v >= 0, "invariant") ; + if (v == 0) { + // Do this the hard way by blocking ... + // See http://monaco.sfbay/detail.jsf?cr=5094058. + // TODO-FIXME: for Solaris SPARC set fprs.FEF=0 prior to parking. + // Only for SPARC >= V8PlusA +#if defined(__sparc) && defined(COMPILER2) + if (ClearFPUAtPark) { _mark_fpu_nosave() ; } +#endif + int status = os::Solaris::mutex_lock(_mutex); + assert_status(status == 0, status, "mutex_lock"); + guarantee (_nParked == 0, "invariant") ; + ++ _nParked ; + while (_Event < 0) { + // for some reason, under 2.7 lwp_cond_wait() may return ETIME ... + // Treat this the same as if the wait was interrupted + // With usr/lib/lwp going to kernel, always handle ETIME + status = os::Solaris::cond_wait(_cond, _mutex); + if (status == ETIME) status = EINTR ; + assert_status(status == 0 || status == EINTR, status, "cond_wait"); + } + -- _nParked ; + _Event = 0 ; + status = os::Solaris::mutex_unlock(_mutex); + assert_status(status == 0, status, "mutex_unlock"); + } +} + +int os::PlatformEvent::park(jlong millis) { + guarantee (_nParked == 0, "invariant") ; + int v ; + for (;;) { + v = _Event ; + if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ; + } + guarantee (v >= 0, "invariant") ; + if (v != 0) return OS_OK ; + + int ret = OS_TIMEOUT; + timestruc_t abst; + compute_abstime (&abst, millis); + + // See http://monaco.sfbay/detail.jsf?cr=5094058. + // For Solaris SPARC set fprs.FEF=0 prior to parking. + // Only for SPARC >= V8PlusA +#if defined(__sparc) && defined(COMPILER2) + if (ClearFPUAtPark) { _mark_fpu_nosave() ; } +#endif + int status = os::Solaris::mutex_lock(_mutex); + assert_status(status == 0, status, "mutex_lock"); + guarantee (_nParked == 0, "invariant") ; + ++ _nParked ; + while (_Event < 0) { + int status = os::Solaris::cond_timedwait(_cond, _mutex, &abst); + assert_status(status == 0 || status == EINTR || + status == ETIME || status == ETIMEDOUT, + status, "cond_timedwait"); + if (!FilterSpuriousWakeups) break ; // previous semantics + if (status == ETIME || status == ETIMEDOUT) break ; + // We consume and ignore EINTR and spurious wakeups. + } + -- _nParked ; + if (_Event >= 0) ret = OS_OK ; + _Event = 0 ; + status = os::Solaris::mutex_unlock(_mutex); + assert_status(status == 0, status, "mutex_unlock"); + return ret; +} + +void os::PlatformEvent::unpark() { + int v, AnyWaiters; + + // Increment _Event. + // Another acceptable implementation would be to simply swap 1 + // into _Event: + // if (Swap (&_Event, 1) < 0) { + // mutex_lock (_mutex) ; AnyWaiters = nParked; mutex_unlock (_mutex) ; + // if (AnyWaiters) cond_signal (_cond) ; + // } + + for (;;) { + v = _Event ; + if (v > 0) { + // The LD of _Event could have reordered or be satisfied + // by a read-aside from this processor's write buffer. + // To avoid problems execute a barrier and then + // ratify the value. A degenerate CAS() would also work. + // Viz., CAS (v+0, &_Event, v) == v). + OrderAccess::fence() ; + if (_Event == v) return ; + continue ; + } + if (Atomic::cmpxchg (v+1, &_Event, v) == v) break ; + } + + // If the thread associated with the event was parked, wake it. + if (v < 0) { + int status ; + // Wait for the thread assoc with the PlatformEvent to vacate. + status = os::Solaris::mutex_lock(_mutex); + assert_status(status == 0, status, "mutex_lock"); + AnyWaiters = _nParked ; + status = os::Solaris::mutex_unlock(_mutex); + assert_status(status == 0, status, "mutex_unlock"); + guarantee (AnyWaiters == 0 || AnyWaiters == 1, "invariant") ; + if (AnyWaiters != 0) { + // We intentional signal *after* dropping the lock + // to avoid a common class of futile wakeups. + status = os::Solaris::cond_signal(_cond); + assert_status(status == 0, status, "cond_signal"); + } + } +} + +// JSR166 +// ------------------------------------------------------- + +/* + * The solaris and linux implementations of park/unpark are fairly + * conservative for now, but can be improved. They currently use a + * mutex/condvar pair, plus _counter. + * Park decrements _counter if > 0, else does a condvar wait. Unpark + * sets count to 1 and signals condvar. Only one thread ever waits + * on the condvar. Contention seen when trying to park implies that someone + * is unparking you, so don't wait. And spurious returns are fine, so there + * is no need to track notifications. + */ + +#define NANOSECS_PER_SEC 1000000000 +#define NANOSECS_PER_MILLISEC 1000000 +#define MAX_SECS 100000000 + +/* + * This code is common to linux and solaris and will be moved to a + * common place in dolphin. + * + * The passed in time value is either a relative time in nanoseconds + * or an absolute time in milliseconds. Either way it has to be unpacked + * into suitable seconds and nanoseconds components and stored in the + * given timespec structure. + * Given time is a 64-bit value and the time_t used in the timespec is only + * a signed-32-bit value (except on 64-bit Linux) we have to watch for + * overflow if times way in the future are given. Further on Solaris versions + * prior to 10 there is a restriction (see cond_timedwait) that the specified + * number of seconds, in abstime, is less than current_time + 100,000,000. + * As it will be 28 years before "now + 100000000" will overflow we can + * ignore overflow and just impose a hard-limit on seconds using the value + * of "now + 100,000,000". This places a limit on the timeout of about 3.17 + * years from "now". + */ +static void unpackTime(timespec* absTime, bool isAbsolute, jlong time) { + assert (time > 0, "convertTime"); + + struct timeval now; + int status = gettimeofday(&now, NULL); + assert(status == 0, "gettimeofday"); + + time_t max_secs = now.tv_sec + MAX_SECS; + + if (isAbsolute) { + jlong secs = time / 1000; + if (secs > max_secs) { + absTime->tv_sec = max_secs; + } + else { + absTime->tv_sec = secs; + } + absTime->tv_nsec = (time % 1000) * NANOSECS_PER_MILLISEC; + } + else { + jlong secs = time / NANOSECS_PER_SEC; + if (secs >= MAX_SECS) { + absTime->tv_sec = max_secs; + absTime->tv_nsec = 0; + } + else { + absTime->tv_sec = now.tv_sec + secs; + absTime->tv_nsec = (time % NANOSECS_PER_SEC) + now.tv_usec*1000; + if (absTime->tv_nsec >= NANOSECS_PER_SEC) { + absTime->tv_nsec -= NANOSECS_PER_SEC; + ++absTime->tv_sec; // note: this must be <= max_secs + } + } + } + assert(absTime->tv_sec >= 0, "tv_sec < 0"); + assert(absTime->tv_sec <= max_secs, "tv_sec > max_secs"); + assert(absTime->tv_nsec >= 0, "tv_nsec < 0"); + assert(absTime->tv_nsec < NANOSECS_PER_SEC, "tv_nsec >= nanos_per_sec"); +} + +void Parker::park(bool isAbsolute, jlong time) { + + // Optional fast-path check: + // Return immediately if a permit is available. + if (_counter > 0) { + _counter = 0 ; + return ; + } + + // Optional fast-exit: Check interrupt before trying to wait + Thread* thread = Thread::current(); + assert(thread->is_Java_thread(), "Must be JavaThread"); + JavaThread *jt = (JavaThread *)thread; + if (Thread::is_interrupted(thread, false)) { + return; + } + + // First, demultiplex/decode time arguments + timespec absTime; + if (time < 0) { // don't wait at all + return; + } + if (time > 0) { + // Warning: this code might be exposed to the old Solaris time + // round-down bugs. Grep "roundingFix" for details. + unpackTime(&absTime, isAbsolute, time); + } + + // Enter safepoint region + // Beware of deadlocks such as 6317397. + // The per-thread Parker:: _mutex is a classic leaf-lock. + // In particular a thread must never block on the Threads_lock while + // holding the Parker:: mutex. If safepoints are pending both the + // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock. + ThreadBlockInVM tbivm(jt); + + // Don't wait if cannot get lock since interference arises from + // unblocking. Also. check interrupt before trying wait + if (Thread::is_interrupted(thread, false) || + os::Solaris::mutex_trylock(_mutex) != 0) { + return; + } + + int status ; + + if (_counter > 0) { // no wait needed + _counter = 0; + status = os::Solaris::mutex_unlock(_mutex); + assert (status == 0, "invariant") ; + return; + } + +#ifdef ASSERT + // Don't catch signals while blocked; let the running threads have the signals. + // (This allows a debugger to break into the running thread.) + sigset_t oldsigs; + sigset_t* allowdebug_blocked = os::Solaris::allowdebug_blocked_signals(); + thr_sigsetmask(SIG_BLOCK, allowdebug_blocked, &oldsigs); +#endif + + OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); + jt->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() + + // Do this the hard way by blocking ... + // See http://monaco.sfbay/detail.jsf?cr=5094058. + // TODO-FIXME: for Solaris SPARC set fprs.FEF=0 prior to parking. + // Only for SPARC >= V8PlusA +#if defined(__sparc) && defined(COMPILER2) + if (ClearFPUAtPark) { _mark_fpu_nosave() ; } +#endif + + if (time == 0) { + status = os::Solaris::cond_wait (_cond, _mutex) ; + } else { + status = os::Solaris::cond_timedwait (_cond, _mutex, &absTime); + } + // Note that an untimed cond_wait() can sometimes return ETIME on older + // versions of the Solaris. + assert_status(status == 0 || status == EINTR || + status == ETIME || status == ETIMEDOUT, + status, "cond_timedwait"); + +#ifdef ASSERT + thr_sigsetmask(SIG_SETMASK, &oldsigs, NULL); +#endif + _counter = 0 ; + status = os::Solaris::mutex_unlock(_mutex); + assert_status(status == 0, status, "mutex_unlock") ; + + // If externally suspended while waiting, re-suspend + if (jt->handle_special_suspend_equivalent_condition()) { + jt->java_suspend_self(); + } + +} + +void Parker::unpark() { + int s, status ; + status = os::Solaris::mutex_lock (_mutex) ; + assert (status == 0, "invariant") ; + s = _counter; + _counter = 1; + status = os::Solaris::mutex_unlock (_mutex) ; + assert (status == 0, "invariant") ; + + if (s < 1) { + status = os::Solaris::cond_signal (_cond) ; + assert (status == 0, "invariant") ; + } +} + +extern char** environ; + +// Run the specified command in a separate process. Return its exit value, +// or -1 on failure (e.g. can't fork a new process). +// Unlike system(), this function can be called from signal handler. It +// doesn't block SIGINT et al. +int os::fork_and_exec(char* cmd) { + char * argv[4]; + argv[0] = (char *)"sh"; + argv[1] = (char *)"-c"; + argv[2] = cmd; + argv[3] = NULL; + + // fork is async-safe, fork1 is not so can't use in signal handler + pid_t pid; + Thread* t = ThreadLocalStorage::get_thread_slow(); + if (t != NULL && t->is_inside_signal_handler()) { + pid = fork(); + } else { + pid = fork1(); + } + + if (pid < 0) { + // fork failed + warning("fork failed: %s", strerror(errno)); + return -1; + + } else if (pid == 0) { + // child process + + // try to be consistent with system(), which uses "/usr/bin/sh" on Solaris + execve("/usr/bin/sh", argv, environ); + + // execve failed + _exit(-1); + + } else { + // copied from J2SE ..._waitForProcessExit() in UNIXProcess_md.c; we don't + // care about the actual exit code, for now. + + int status; + + // Wait for the child process to exit. This returns immediately if + // the child has already exited. */ + while (waitpid(pid, &status, 0) < 0) { + switch (errno) { + case ECHILD: return 0; + case EINTR: break; + default: return -1; + } + } + + if (WIFEXITED(status)) { + // The child exited normally; get its exit code. + return WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + // The child exited because of a signal + // The best value to return is 0x80 + signal number, + // because that is what all Unix shells do, and because + // it allows callers to distinguish between process exit and + // process death by signal. + return 0x80 + WTERMSIG(status); + } else { + // Unknown exit code; pass it through + return status; + } + } +} diff --git a/hotspot/src/os/solaris/vm/os_solaris.hpp b/hotspot/src/os/solaris/vm/os_solaris.hpp new file mode 100644 index 00000000000..201730ee1a7 --- /dev/null +++ b/hotspot/src/os/solaris/vm/os_solaris.hpp @@ -0,0 +1,377 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Solaris_OS defines the interface to Solaris operating systems + +class Solaris { + friend class os; + + private: + + // Support for "new" libthread APIs for getting & setting thread context (2.8) + #define TRS_VALID 0 + #define TRS_NONVOLATILE 1 + #define TRS_LWPID 2 + #define TRS_INVALID 3 + + // _T2_libthread is true if we believe we are running with the newer + // SunSoft lib/lwp/libthread: default Solaris 9, available Solaris 8 + // which is a lightweight libthread that also supports all T1 + static bool _T2_libthread; + // These refer to new libthread interface functions + // They get intialized if we dynamically detect new libthread + static int_fnP_thread_t_iP_uP_stack_tP_gregset_t _thr_getstate; + static int_fnP_thread_t_i_gregset_t _thr_setstate; + static int_fnP_thread_t_i _thr_setmutator; + static int_fnP_thread_t _thr_suspend_mutator; + static int_fnP_thread_t _thr_continue_mutator; + // libthread_init sets the above, if the new functionality is detected + + // initialized to libthread or lwp synchronization primitives depending on UseLWPSychronization + static int_fnP_mutex_tP _mutex_lock; + static int_fnP_mutex_tP _mutex_trylock; + static int_fnP_mutex_tP _mutex_unlock; + static int_fnP_mutex_tP_i_vP _mutex_init; + static int_fnP_mutex_tP _mutex_destroy; + static int _mutex_scope; + + static int_fnP_cond_tP_mutex_tP_timestruc_tP _cond_timedwait; + static int_fnP_cond_tP_mutex_tP _cond_wait; + static int_fnP_cond_tP _cond_signal; + static int_fnP_cond_tP _cond_broadcast; + static int_fnP_cond_tP_i_vP _cond_init; + static int_fnP_cond_tP _cond_destroy; + static int _cond_scope; + + typedef uintptr_t lgrp_cookie_t; + typedef id_t lgrp_id_t; + typedef enum lgrp_view { + LGRP_VIEW_CALLER, /* what's available to the caller */ + LGRP_VIEW_OS /* what's available to operating system */ + } lgrp_view_t; + + typedef lgrp_id_t (*lgrp_home_func_t)(idtype_t idtype, id_t id); + typedef lgrp_cookie_t (*lgrp_init_func_t)(lgrp_view_t view); + typedef int (*lgrp_fini_func_t)(lgrp_cookie_t cookie); + typedef lgrp_id_t (*lgrp_root_func_t)(lgrp_cookie_t cookie); + typedef int (*lgrp_children_func_t)(lgrp_cookie_t cookie, lgrp_id_t parent, + lgrp_id_t *lgrp_array, uint_t lgrp_array_size); + typedef int (*lgrp_nlgrps_func_t)(lgrp_cookie_t cookie); + typedef int (*lgrp_cookie_stale_func_t)(lgrp_cookie_t cookie); + typedef int (*meminfo_func_t)(const uint64_t inaddr[], int addr_count, + const uint_t info_req[], int info_count, + uint64_t outdata[], uint_t validity[]); + + static lgrp_home_func_t _lgrp_home; + static lgrp_init_func_t _lgrp_init; + static lgrp_fini_func_t _lgrp_fini; + static lgrp_root_func_t _lgrp_root; + static lgrp_children_func_t _lgrp_children; + static lgrp_nlgrps_func_t _lgrp_nlgrps; + static lgrp_cookie_stale_func_t _lgrp_cookie_stale; + static lgrp_cookie_t _lgrp_cookie; + + static meminfo_func_t _meminfo; + + // Large Page Support--mpss. + static bool set_mpss_range(caddr_t start, size_t bytes, size_t align); + + static void init_thread_fpu_state(void); + + static void try_enable_extended_io(); + + // For signal-chaining + static unsigned long sigs; // mask of signals that have + // preinstalled signal handlers + static struct sigaction *(*get_signal_action)(int); + static struct sigaction *get_preinstalled_handler(int); + static int (*get_libjsig_version)(); + static void save_preinstalled_handler(int, struct sigaction&); + static void check_signal_handler(int sig); + + // For overridable signals + static int _SIGinterrupt; // user-overridable INTERRUPT_SIGNAL + static int _SIGasync; // user-overridable ASYNC_SIGNAL + static void set_SIGinterrupt(int newsig) { _SIGinterrupt = newsig; } + static void set_SIGasync(int newsig) { _SIGasync = newsig; } + + + public: + // Large Page Support--ISM. + static bool largepage_range(char* addr, size_t size); + + static int SIGinterrupt() { return _SIGinterrupt; } + static int SIGasync() { return _SIGasync; } + static address handler_start, handler_end; // start and end pc of thr_sighndlrinfo + + static bool valid_stack_address(Thread* thread, address sp); + static bool valid_ucontext(Thread* thread, ucontext_t* valid, ucontext_t* suspect); + static ucontext_t* get_valid_uc_in_signal_handler(Thread* thread, + ucontext_t* uc); + + static ExtendedPC ucontext_get_ExtendedPC(ucontext_t* uc); + static intptr_t* ucontext_get_sp(ucontext_t* uc); + // ucontext_get_fp() is only used by Solaris X86 (see note below) + static intptr_t* ucontext_get_fp(ucontext_t* uc); + + // For Analyzer Forte AsyncGetCallTrace profiling support: + // Parameter ret_fp is only used by Solaris X86. + // + // We should have different declarations of this interface in + // os_solaris_i486.hpp and os_solaris_sparc.hpp, but that file + // provides extensions to the os class and not the Solaris class. + static ExtendedPC fetch_frame_from_ucontext(Thread* thread, ucontext_t* uc, + intptr_t** ret_sp, intptr_t** ret_fp); + + static void hotspot_sigmask(Thread* thread); + + protected: + // Solaris-specific interface goes here + static julong available_memory(); + static julong physical_memory() { return _physical_memory; } + static julong _physical_memory; + static void initialize_system_info(); + static int _dev_zero_fd; + static int get_dev_zero_fd() { return _dev_zero_fd; } + static void set_dev_zero_fd(int fd) { _dev_zero_fd = fd; } + static char* mmap_chunk(char *addr, size_t size, int flags, int prot); + static bool mpss_sanity_check(bool warn, size_t * page_size); + static bool ism_sanity_check (bool warn, size_t * page_size); + + // Workaround for 4352906. thr_stksegment sometimes returns + // a bad value for the primordial thread's stack base when + // it is called more than one time. + // Workaround is to cache the initial value to avoid further + // calls to thr_stksegment. + // It appears that someone (Hotspot?) is trashing the user's + // proc_t structure (note that this is a system struct). + static address _main_stack_base; + + public: + static void libthread_init(); + static void synchronization_init(); + static void liblgrp_init(); + // Load miscellaneous symbols. + static void misc_sym_init(); + // This boolean allows users to forward their own non-matching signals + // to JVM_handle_solaris_signal, harmlessly. + static bool signal_handlers_are_installed; + + static void signal_sets_init(); + static void install_signal_handlers(); + static void set_signal_handler(int sig, bool set_installed, bool oktochain); + static void init_signal_mem(); + static bool is_sig_ignored(int sig); + static void set_our_sigflags(int, int); + static int get_our_sigflags(int); + + // For signal-chaining + static bool libjsig_is_loaded; // libjsig that interposes sigaction(), + // signal(), sigset() is loaded + static struct sigaction *get_chained_signal_action(int sig); + static bool chained_handler(int sig, siginfo_t *siginfo, void *context); + + // The following allow us to link against both the old and new libthread (2.8) + // and exploit the new libthread functionality if available. + + static bool T2_libthread() { return _T2_libthread; } + static void set_T2_libthread(bool T2_libthread) { _T2_libthread = T2_libthread; } + + static int thr_getstate(thread_t tid, int *flag, unsigned *lwp, stack_t *ss, gregset_t rs) + { return _thr_getstate(tid, flag, lwp, ss, rs); } + static void set_thr_getstate(int_fnP_thread_t_iP_uP_stack_tP_gregset_t func) + { _thr_getstate = func; } + + static int thr_setstate(thread_t tid, int flag, gregset_t rs) { return _thr_setstate(tid, flag, rs); } + static void set_thr_setstate(int_fnP_thread_t_i_gregset_t func) { _thr_setstate = func; } + + static int thr_setmutator(thread_t tid, int enabled) { return _thr_setmutator(tid, enabled); } + static void set_thr_setmutator(int_fnP_thread_t_i func) { _thr_setmutator = func; } + + static int thr_suspend_mutator(thread_t tid) { return _thr_suspend_mutator(tid); } + static void set_thr_suspend_mutator(int_fnP_thread_t func) { _thr_suspend_mutator = func; } + + static int thr_continue_mutator(thread_t tid) { return _thr_continue_mutator(tid); } + static void set_thr_continue_mutator(int_fnP_thread_t func) { _thr_continue_mutator = func; } + + // Allows us to switch between lwp and thread -based synchronization + static int mutex_lock(mutex_t *mx) { return _mutex_lock(mx); } + static int mutex_trylock(mutex_t *mx) { return _mutex_trylock(mx); } + static int mutex_unlock(mutex_t *mx) { return _mutex_unlock(mx); } + static int mutex_init(mutex_t *mx) { return _mutex_init(mx, os::Solaris::mutex_scope(), NULL); } + static int mutex_destroy(mutex_t *mx) { return _mutex_destroy(mx); } + static int mutex_scope() { return _mutex_scope; } + + static void set_mutex_lock(int_fnP_mutex_tP func) { _mutex_lock = func; } + static void set_mutex_trylock(int_fnP_mutex_tP func) { _mutex_trylock = func; } + static void set_mutex_unlock(int_fnP_mutex_tP func) { _mutex_unlock = func; } + static void set_mutex_init(int_fnP_mutex_tP_i_vP func) { _mutex_init = func; } + static void set_mutex_destroy(int_fnP_mutex_tP func) { _mutex_destroy = func; } + static void set_mutex_scope(int scope) { _mutex_scope = scope; } + + static int cond_timedwait(cond_t *cv, mutex_t *mx, timestruc_t *abst) + { return _cond_timedwait(cv, mx, abst); } + static int cond_wait(cond_t *cv, mutex_t *mx) { return _cond_wait(cv, mx); } + static int cond_signal(cond_t *cv) { return _cond_signal(cv); } + static int cond_broadcast(cond_t *cv) { return _cond_broadcast(cv); } + static int cond_init(cond_t *cv) { return _cond_init(cv, os::Solaris::cond_scope(), NULL); } + static int cond_destroy(cond_t *cv) { return _cond_destroy(cv); } + static int cond_scope() { return _cond_scope; } + + static void set_cond_timedwait(int_fnP_cond_tP_mutex_tP_timestruc_tP func) + { _cond_timedwait = func; } + static void set_cond_wait(int_fnP_cond_tP_mutex_tP func) { _cond_wait = func; } + static void set_cond_signal(int_fnP_cond_tP func) { _cond_signal = func; } + static void set_cond_broadcast(int_fnP_cond_tP func) { _cond_broadcast = func; } + static void set_cond_init(int_fnP_cond_tP_i_vP func) { _cond_init = func; } + static void set_cond_destroy(int_fnP_cond_tP func) { _cond_destroy = func; } + static void set_cond_scope(int scope) { _cond_scope = scope; } + + static void set_lgrp_home(lgrp_home_func_t func) { _lgrp_home = func; } + static void set_lgrp_init(lgrp_init_func_t func) { _lgrp_init = func; } + static void set_lgrp_fini(lgrp_fini_func_t func) { _lgrp_fini = func; } + static void set_lgrp_root(lgrp_root_func_t func) { _lgrp_root = func; } + static void set_lgrp_children(lgrp_children_func_t func) { _lgrp_children = func; } + static void set_lgrp_nlgrps(lgrp_nlgrps_func_t func) { _lgrp_nlgrps = func; } + static void set_lgrp_cookie_stale(lgrp_cookie_stale_func_t func) { _lgrp_cookie_stale = func; } + static void set_lgrp_cookie(lgrp_cookie_t cookie) { _lgrp_cookie = cookie; } + + static id_t lgrp_home(idtype_t type, id_t id) { return _lgrp_home != NULL ? _lgrp_home(type, id) : -1; } + static lgrp_cookie_t lgrp_init(lgrp_view_t view) { return _lgrp_init != NULL ? _lgrp_init(view) : 0; } + static int lgrp_fini(lgrp_cookie_t cookie) { return _lgrp_fini != NULL ? _lgrp_fini(cookie) : -1; } + static lgrp_id_t lgrp_root(lgrp_cookie_t cookie) { return _lgrp_root != NULL ? _lgrp_root(cookie) : -1; }; + static int lgrp_children(lgrp_cookie_t cookie, lgrp_id_t parent, + lgrp_id_t *lgrp_array, uint_t lgrp_array_size) { + return _lgrp_children != NULL ? _lgrp_children(cookie, parent, lgrp_array, lgrp_array_size) : -1; + } + static int lgrp_nlgrps(lgrp_cookie_t cookie) { return _lgrp_nlgrps != NULL ? _lgrp_nlgrps(cookie) : -1; } + static int lgrp_cookie_stale(lgrp_cookie_t cookie) { + return _lgrp_cookie_stale != NULL ? _lgrp_cookie_stale(cookie) : -1; + } + static lgrp_cookie_t lgrp_cookie() { return _lgrp_cookie; } + + static void set_meminfo(meminfo_func_t func) { _meminfo = func; } + static int meminfo (const uint64_t inaddr[], int addr_count, + const uint_t info_req[], int info_count, + uint64_t outdata[], uint_t validity[]) { + return _meminfo != NULL ? _meminfo(inaddr, addr_count, info_req, info_count, + outdata, validity) : -1; + } + + enum { + clear_interrupted = true + }; + static void setup_interruptible(JavaThread* thread); + static void setup_interruptible_already_blocked(JavaThread* thread); + static JavaThread* setup_interruptible(); + static void cleanup_interruptible(JavaThread* thread); + + // perf counter incrementers used by _INTERRUPTIBLE + + static void bump_interrupted_before_count(); + static void bump_interrupted_during_count(); + +#ifdef ASSERT + static JavaThread* setup_interruptible_native(); + static void cleanup_interruptible_native(JavaThread* thread); +#endif + + static sigset_t* unblocked_signals(); + static sigset_t* vm_signals(); + static sigset_t* allowdebug_blocked_signals(); + + // %%% Following should be promoted to os.hpp: + // Trace number of created threads + static jint _os_thread_limit; + static volatile jint _os_thread_count; + + // Minimum stack size a thread can be created with (allowing + // the VM to completely create the thread and enter user code) + + static size_t min_stack_allowed; + + // Stack overflow handling + + static int max_register_window_saves_before_flushing(); + + // Stack repair handling + + // none present + +}; + +class PlatformEvent : public CHeapObj { + private: + double CachePad [4] ; // increase odds that _mutex is sole occupant of cache line + volatile int _Event ; + int _nParked ; + int _pipev [2] ; + mutex_t _mutex [1] ; + cond_t _cond [1] ; + double PostPad [2] ; + + protected: + // Defining a protected ctor effectively gives us an abstract base class. + // That is, a PlatformEvent can never be instantiated "naked" but only + // as a part of a ParkEvent (recall that ParkEvent extends PlatformEvent). + // TODO-FIXME: make dtor private + ~PlatformEvent() { guarantee (0, "invariant") ; } + PlatformEvent() { + int status; + status = os::Solaris::cond_init(_cond); + assert_status(status == 0, status, "cond_init"); + status = os::Solaris::mutex_init(_mutex); + assert_status(status == 0, status, "mutex_init"); + _Event = 0 ; + _nParked = 0 ; + _pipev[0] = _pipev[1] = -1 ; + } + + public: + // Exercise caution using reset() and fired() -- they may require MEMBARs + void reset() { _Event = 0 ; } + int fired() { return _Event; } + void park () ; + int park (jlong millis) ; + int TryPark () ; + void unpark () ; +} ; + +class PlatformParker : public CHeapObj { + protected: + mutex_t _mutex [1] ; + cond_t _cond [1] ; + + public: // TODO-FIXME: make dtor private + ~PlatformParker() { guarantee (0, "invariant") ; } + + public: + PlatformParker() { + int status; + status = os::Solaris::cond_init(_cond); + assert_status(status == 0, status, "cond_init"); + status = os::Solaris::mutex_init(_mutex); + assert_status(status == 0, status, "mutex_init"); + } +} ; diff --git a/hotspot/src/os/solaris/vm/os_solaris.inline.hpp b/hotspot/src/os/solaris/vm/os_solaris.inline.hpp new file mode 100644 index 00000000000..f75b50d15b2 --- /dev/null +++ b/hotspot/src/os/solaris/vm/os_solaris.inline.hpp @@ -0,0 +1,206 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline const char* os::file_separator() { return "/"; } +inline const char* os::line_separator() { return "\n"; } +inline const char* os::path_separator() { return ":"; } + +inline const char* os::jlong_format_specifier() { return "%lld"; } +inline const char* os::julong_format_specifier() { return "%llu"; } + +// File names are case-sensitive on windows only +inline int os::file_name_strcmp(const char* s1, const char* s2) { + return strcmp(s1, s2); +} + +inline bool os::uses_stack_guard_pages() { + return true; +} + +inline bool os::allocate_stack_guard_pages() { + assert(uses_stack_guard_pages(), "sanity check"); + int r = thr_main() ; + guarantee (r == 0 || r == 1, "CR6501650 or CR6493689") ; + return r; +} + + +// On Solaris, reservations are made on a page by page basis, nothing to do. +inline void os::split_reserved_memory(char *base, size_t size, + size_t split, bool realloc) { +} + + +// Bang the shadow pages if they need to be touched to be mapped. +inline void os::bang_stack_shadow_pages() { +} + +inline DIR* os::opendir(const char* dirname) +{ + assert(dirname != NULL, "just checking"); + return ::opendir(dirname); +} + +inline int os::readdir_buf_size(const char *path) +{ + int size = pathconf(path, _PC_NAME_MAX); + return (size < 0 ? MAXPATHLEN : size) + sizeof(dirent) + 1; +} + +inline struct dirent* os::readdir(DIR* dirp, dirent* dbuf) +{ + assert(dirp != NULL, "just checking"); +#if defined(_LP64) || defined(_GNU_SOURCE) + dirent* p; + int status; + + if((status = ::readdir_r(dirp, dbuf, &p)) != 0) { + errno = status; + return NULL; + } else + return p; +#else // defined(_LP64) || defined(_GNU_SOURCE) + return ::readdir_r(dirp, dbuf); +#endif // defined(_LP64) || defined(_GNU_SOURCE) +} + +inline int os::closedir(DIR *dirp) +{ + assert(dirp != NULL, "just checking"); + return ::closedir(dirp); +} + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// macros for interruptible io and system calls and system call restarting + +#define _INTERRUPTIBLE(_setup, _cmd, _result, _thread, _clear, _before, _after, _int_enable) \ +do { \ + _setup; \ + _before; \ + OSThread* _osthread = _thread->osthread(); \ + if (_int_enable && _thread->has_last_Java_frame()) { \ + /* this is java interruptible io stuff */ \ + if (os::is_interrupted(_thread, _clear)) { \ + os::Solaris::bump_interrupted_before_count(); \ + _result = OS_INTRPT; \ + } else { \ + /* _cmd always expands to an assignment to _result */ \ + if ((_cmd) < 0 && errno == EINTR \ + && os::is_interrupted(_thread, _clear)) { \ + os::Solaris::bump_interrupted_during_count(); \ + _result = OS_INTRPT; \ + } \ + } \ + } else { \ + /* this is normal blocking io stuff */ \ + _cmd; \ + } \ + _after; \ +} while(false) + +// Interruptible io support + restarting of interrupted system calls + +#ifndef ASSERT + +#define INTERRUPTIBLE(_cmd, _result, _clear) do { \ + _INTERRUPTIBLE( JavaThread* _thread = (JavaThread*)ThreadLocalStorage::thread(),_result = _cmd, _result, _thread, _clear, , , UseVMInterruptibleIO); \ +} while((_result == OS_ERR) && (errno == EINTR)) + +#else + +// This adds an assertion that it is only called from thread_in_native +// The call overhead is skipped for performance in product mode +#define INTERRUPTIBLE(_cmd, _result, _clear) do { \ + _INTERRUPTIBLE(JavaThread* _thread = os::Solaris::setup_interruptible_native(), _result = _cmd, _result, _thread, _clear, , os::Solaris::cleanup_interruptible_native(_thread), UseVMInterruptibleIO ); \ +} while((_result == OS_ERR) && (errno == EINTR)) + +#endif + +// Used for calls from _thread_in_vm, not from _thread_in_native +#define INTERRUPTIBLE_VM(_cmd, _result, _clear) do { \ + _INTERRUPTIBLE(JavaThread* _thread = os::Solaris::setup_interruptible(), _result = _cmd, _result, _thread, _clear, , os::Solaris::cleanup_interruptible(_thread), UseVMInterruptibleIO ); \ +} while((_result == OS_ERR) && (errno == EINTR)) + +/* Use NORESTART when the system call cannot return EINTR, when something other + than a system call is being invoked, or when the caller must do EINTR + handling. */ + +#ifndef ASSERT + +#define INTERRUPTIBLE_NORESTART(_cmd, _result, _clear) \ + _INTERRUPTIBLE( JavaThread* _thread = (JavaThread*)ThreadLocalStorage::thread(),_result = _cmd, _result, _thread, _clear, , , UseVMInterruptibleIO) + +#else + +// This adds an assertion that it is only called from thread_in_native +// The call overhead is skipped for performance in product mode +#define INTERRUPTIBLE_NORESTART(_cmd, _result, _clear) \ + _INTERRUPTIBLE(JavaThread* _thread = os::Solaris::setup_interruptible_native(), _result = _cmd, _result, _thread, _clear, , os::Solaris::cleanup_interruptible_native(_thread), UseVMInterruptibleIO ) + +#endif + +// Don't attend to UseVMInterruptibleIO. Always allow interruption. +// Also assumes that it is called from the _thread_blocked state. +// Used by os_sleep(). + +#define INTERRUPTIBLE_NORESTART_VM_ALWAYS(_cmd, _result, _thread, _clear) \ + _INTERRUPTIBLE(os::Solaris::setup_interruptible_already_blocked(_thread), _result = _cmd, _result, _thread, _clear, , , true ) + +#define INTERRUPTIBLE_RETURN_INT(_cmd, _clear) do { \ + int _result; \ + do { \ + INTERRUPTIBLE(_cmd, _result, _clear); \ + } while((_result == OS_ERR) && (errno == EINTR)); \ + return _result; \ +} while(false) + +#define INTERRUPTIBLE_RETURN_INT_VM(_cmd, _clear) do { \ + int _result; \ + do { \ + INTERRUPTIBLE_VM(_cmd, _result, _clear); \ + } while((_result == OS_ERR) && (errno == EINTR)); \ + return _result; \ +} while(false) + +#define INTERRUPTIBLE_RETURN_INT_NORESTART(_cmd, _clear) do { \ + int _result; \ + INTERRUPTIBLE_NORESTART(_cmd, _result, _clear); \ + return _result; \ +} while(false) + +/* Use the RESTARTABLE macros when interruptible io is not needed */ + +#define RESTARTABLE(_cmd, _result) do { \ + do { \ + _result = _cmd; \ + } while((_result == OS_ERR) && (errno == EINTR)); \ +} while(false) + +#define RESTARTABLE_RETURN_INT(_cmd) do { \ + int _result; \ + RESTARTABLE(_cmd, _result); \ + return _result; \ +} while(false) diff --git a/hotspot/src/os/solaris/vm/perfMemory_solaris.cpp b/hotspot/src/os/solaris/vm/perfMemory_solaris.cpp new file mode 100644 index 00000000000..4626d7299b4 --- /dev/null +++ b/hotspot/src/os/solaris/vm/perfMemory_solaris.cpp @@ -0,0 +1,1048 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_perfMemory_solaris.cpp.incl" + +// put OS-includes here +# include +# include +# include +# include +# include +# include +# include +# include +# include + + +static char* backing_store_file_name = NULL; // name of the backing store + // file, if successfully created. + +// Standard Memory Implementation Details + +// create the PerfData memory region in standard memory. +// +static char* create_standard_memory(size_t size) { + + // allocate an aligned chuck of memory + char* mapAddress = os::reserve_memory(size); + + if (mapAddress == NULL) { + return NULL; + } + + // commit memory + if (!os::commit_memory(mapAddress, size)) { + if (PrintMiscellaneous && Verbose) { + warning("Could not commit PerfData memory\n"); + } + os::release_memory(mapAddress, size); + return NULL; + } + + return mapAddress; +} + +// delete the PerfData memory region +// +static void delete_standard_memory(char* addr, size_t size) { + + // there are no persistent external resources to cleanup for standard + // memory. since DestroyJavaVM does not support unloading of the JVM, + // cleanup of the memory resource is not performed. The memory will be + // reclaimed by the OS upon termination of the process. + // + return; +} + +// save the specified memory region to the given file +// +// Note: this function might be called from signal handler (by os::abort()), +// don't allocate heap memory. +// +static void save_memory_to_file(char* addr, size_t size) { + + const char* destfile = PerfMemory::get_perfdata_file_path(); + assert(destfile[0] != '\0', "invalid PerfData file path"); + + int result; + + RESTARTABLE(::open(destfile, O_CREAT|O_WRONLY|O_TRUNC, S_IREAD|S_IWRITE), + result);; + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("Could not create Perfdata save file: %s: %s\n", + destfile, strerror(errno)); + } + } else { + + int fd = result; + + for (size_t remaining = size; remaining > 0;) { + + RESTARTABLE(::write(fd, addr, remaining), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("Could not write Perfdata save file: %s: %s\n", + destfile, strerror(errno)); + } + break; + } + remaining -= (size_t)result; + addr += result; + } + + RESTARTABLE(::close(fd), result); + if (PrintMiscellaneous && Verbose) { + if (result == OS_ERR) { + warning("Could not close %s: %s\n", destfile, strerror(errno)); + } + } + } + FREE_C_HEAP_ARRAY(char, destfile); +} + + +// Shared Memory Implementation Details + +// Note: the solaris and linux shared memory implementation uses the mmap +// interface with a backing store file to implement named shared memory. +// Using the file system as the name space for shared memory allows a +// common name space to be supported across a variety of platforms. It +// also provides a name space that Java applications can deal with through +// simple file apis. +// +// The solaris and linux implementations store the backing store file in +// a user specific temporary directory located in the /tmp file system, +// which is always a local file system and is sometimes a RAM based file +// system. + +// return the user specific temporary directory name. +// +// the caller is expected to free the allocated memory. +// +static char* get_user_tmp_dir(const char* user) { + + const char* tmpdir = os::get_temp_directory(); + const char* perfdir = PERFDATA_NAME; + size_t nbytes = strlen(tmpdir) + strlen(perfdir) + strlen(user) + 2; + char* dirname = NEW_C_HEAP_ARRAY(char, nbytes); + + // construct the path name to user specific tmp directory + snprintf(dirname, nbytes, "%s%s_%s", tmpdir, perfdir, user); + + return dirname; +} + +// convert the given file name into a process id. if the file +// does not meet the file naming constraints, return 0. +// +static pid_t filename_to_pid(const char* filename) { + + // a filename that doesn't begin with a digit is not a + // candidate for conversion. + // + if (!isdigit(*filename)) { + return 0; + } + + // check if file name can be converted to an integer without + // any leftover characters. + // + char* remainder = NULL; + errno = 0; + pid_t pid = (pid_t)strtol(filename, &remainder, 10); + + if (errno != 0) { + return 0; + } + + // check for left over characters. If any, then the filename is + // not a candidate for conversion. + // + if (remainder != NULL && *remainder != '\0') { + return 0; + } + + // successful conversion, return the pid + return pid; +} + + +// check if the given path is considered a secure directory for +// the backing store files. Returns true if the directory exists +// and is considered a secure location. Returns false if the path +// is a symbolic link or if an error occured. +// +static bool is_directory_secure(const char* path) { + struct stat statbuf; + int result = 0; + + RESTARTABLE(::lstat(path, &statbuf), result); + if (result == OS_ERR) { + return false; + } + + // the path exists, now check it's mode + if (S_ISLNK(statbuf.st_mode) || !S_ISDIR(statbuf.st_mode)) { + // the path represents a link or some non-directory file type, + // which is not what we expected. declare it insecure. + // + return false; + } + else { + // we have an existing directory, check if the permissions are safe. + // + if ((statbuf.st_mode & (S_IWGRP|S_IWOTH)) != 0) { + // the directory is open for writing and could be subjected + // to a symlnk attack. declare it insecure. + // + return false; + } + } + return true; +} + + +// return the user name for the given user id +// +// the caller is expected to free the allocated memory. +// +static char* get_user_name(uid_t uid) { + + struct passwd pwent; + + // determine the max pwbuf size from sysconf, and hardcode + // a default if this not available through sysconf. + // + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize == -1) + bufsize = 1024; + + char* pwbuf = NEW_C_HEAP_ARRAY(char, bufsize); + +#ifdef _GNU_SOURCE + struct passwd* p = NULL; + int result = getpwuid_r(uid, &pwent, pwbuf, (size_t)bufsize, &p); +#else // _GNU_SOURCE + struct passwd* p = getpwuid_r(uid, &pwent, pwbuf, (int)bufsize); +#endif // _GNU_SOURCE + + if (p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') { + if (PrintMiscellaneous && Verbose) { + if (p == NULL) { + warning("Could not retrieve passwd entry: %s\n", + strerror(errno)); + } + else { + warning("Could not determine user name: %s\n", + p->pw_name == NULL ? "pw_name = NULL" : + "pw_name zero length"); + } + } + FREE_C_HEAP_ARRAY(char, pwbuf); + return NULL; + } + + char* user_name = NEW_C_HEAP_ARRAY(char, strlen(p->pw_name) + 1); + strcpy(user_name, p->pw_name); + + FREE_C_HEAP_ARRAY(char, pwbuf); + return user_name; +} + +// return the name of the user that owns the process identified by vmid. +// +// This method uses a slow directory search algorithm to find the backing +// store file for the specified vmid and returns the user name, as determined +// by the user name suffix of the hsperfdata_ directory name. +// +// the caller is expected to free the allocated memory. +// +static char* get_user_name_slow(int vmid, TRAPS) { + + // short circuit the directory search if the process doesn't even exist. + if (kill(vmid, 0) == OS_ERR) { + if (errno == ESRCH) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); + } + else /* EPERM */ { + THROW_MSG_0(vmSymbols::java_io_IOException(), strerror(errno)); + } + } + + // directory search + char* oldest_user = NULL; + time_t oldest_ctime = 0; + + const char* tmpdirname = os::get_temp_directory(); + + DIR* tmpdirp = os::opendir(tmpdirname); + + if (tmpdirp == NULL) { + return NULL; + } + + // for each entry in the directory that matches the pattern hsperfdata_*, + // open the directory and check if the file for the given vmid exists. + // The file with the expected name and the latest creation date is used + // to determine the user name for the process id. + // + struct dirent* dentry; + char* tdbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(tmpdirname)); + errno = 0; + while ((dentry = os::readdir(tmpdirp, (struct dirent *)tdbuf)) != NULL) { + + // check if the directory entry is a hsperfdata file + if (strncmp(dentry->d_name, PERFDATA_NAME, strlen(PERFDATA_NAME)) != 0) { + continue; + } + + char* usrdir_name = NEW_C_HEAP_ARRAY(char, + strlen(tmpdirname) + strlen(dentry->d_name) + 1); + strcpy(usrdir_name, tmpdirname); + strcat(usrdir_name, dentry->d_name); + + DIR* subdirp = os::opendir(usrdir_name); + + if (subdirp == NULL) { + FREE_C_HEAP_ARRAY(char, usrdir_name); + continue; + } + + // Since we don't create the backing store files in directories + // pointed to by symbolic links, we also don't follow them when + // looking for the files. We check for a symbolic link after the + // call to opendir in order to eliminate a small window where the + // symlink can be exploited. + // + if (!is_directory_secure(usrdir_name)) { + FREE_C_HEAP_ARRAY(char, usrdir_name); + os::closedir(subdirp); + continue; + } + + struct dirent* udentry; + char* udbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(usrdir_name)); + errno = 0; + while ((udentry = os::readdir(subdirp, (struct dirent *)udbuf)) != NULL) { + + if (filename_to_pid(udentry->d_name) == vmid) { + struct stat statbuf; + int result; + + char* filename = NEW_C_HEAP_ARRAY(char, + strlen(usrdir_name) + strlen(udentry->d_name) + 2); + + strcpy(filename, usrdir_name); + strcat(filename, "/"); + strcat(filename, udentry->d_name); + + // don't follow symbolic links for the file + RESTARTABLE(::lstat(filename, &statbuf), result); + if (result == OS_ERR) { + FREE_C_HEAP_ARRAY(char, filename); + continue; + } + + // skip over files that are not regular files. + if (!S_ISREG(statbuf.st_mode)) { + FREE_C_HEAP_ARRAY(char, filename); + continue; + } + + // compare and save filename with latest creation time + if (statbuf.st_size > 0 && statbuf.st_ctime > oldest_ctime) { + + if (statbuf.st_ctime > oldest_ctime) { + char* user = strchr(dentry->d_name, '_') + 1; + + if (oldest_user != NULL) FREE_C_HEAP_ARRAY(char, oldest_user); + oldest_user = NEW_C_HEAP_ARRAY(char, strlen(user)+1); + + strcpy(oldest_user, user); + oldest_ctime = statbuf.st_ctime; + } + } + + FREE_C_HEAP_ARRAY(char, filename); + } + } + os::closedir(subdirp); + FREE_C_HEAP_ARRAY(char, udbuf); + FREE_C_HEAP_ARRAY(char, usrdir_name); + } + os::closedir(tmpdirp); + FREE_C_HEAP_ARRAY(char, tdbuf); + + return(oldest_user); +} + +// return the name of the user that owns the JVM indicated by the given vmid. +// +static char* get_user_name(int vmid, TRAPS) { + + char psinfo_name[PATH_MAX]; + int result; + + snprintf(psinfo_name, PATH_MAX, "/proc/%d/psinfo", vmid); + + RESTARTABLE(::open(psinfo_name, O_RDONLY), result); + + if (result != OS_ERR) { + int fd = result; + + psinfo_t psinfo; + char* addr = (char*)&psinfo; + + for (size_t remaining = sizeof(psinfo_t); remaining > 0;) { + + RESTARTABLE(::read(fd, addr, remaining), result); + if (result == OS_ERR) { + THROW_MSG_0(vmSymbols::java_io_IOException(), "Read error"); + } + remaining-=result; + addr+=result; + } + + RESTARTABLE(::close(fd), result); + + // get the user name for the effective user id of the process + char* user_name = get_user_name(psinfo.pr_euid); + + return user_name; + } + + if (result == OS_ERR && errno == EACCES) { + + // In this case, the psinfo file for the process id existed, + // but we didn't have permission to access it. + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + strerror(errno)); + } + + // at this point, we don't know if the process id itself doesn't + // exist or if the psinfo file doesn't exit. If the psinfo file + // doesn't exist, then we are running on Solaris 2.5.1 or earlier. + // since the structured procfs and old procfs interfaces can't be + // mixed, we attempt to find the file through a directory search. + + return get_user_name_slow(vmid, CHECK_NULL); +} + +// return the file name of the backing store file for the named +// shared memory region for the given user name and vmid. +// +// the caller is expected to free the allocated memory. +// +static char* get_sharedmem_filename(const char* dirname, int vmid) { + + // add 2 for the file separator and a NULL terminator. + size_t nbytes = strlen(dirname) + UINT_CHARS + 2; + + char* name = NEW_C_HEAP_ARRAY(char, nbytes); + snprintf(name, nbytes, "%s/%d", dirname, vmid); + + return name; +} + + +// remove file +// +// this method removes the file specified by the given path +// +static void remove_file(const char* path) { + + int result; + + // if the file is a directory, the following unlink will fail. since + // we don't expect to find directories in the user temp directory, we + // won't try to handle this situation. even if accidentially or + // maliciously planted, the directory's presence won't hurt anything. + // + RESTARTABLE(::unlink(path), result); + if (PrintMiscellaneous && Verbose && result == OS_ERR) { + if (errno != ENOENT) { + warning("Could not unlink shared memory backing" + " store file %s : %s\n", path, strerror(errno)); + } + } +} + + +// remove file +// +// this method removes the file with the given file name in the +// named directory. +// +static void remove_file(const char* dirname, const char* filename) { + + size_t nbytes = strlen(dirname) + strlen(filename) + 2; + char* path = NEW_C_HEAP_ARRAY(char, nbytes); + + strcpy(path, dirname); + strcat(path, "/"); + strcat(path, filename); + + remove_file(path); + + FREE_C_HEAP_ARRAY(char, path); +} + + +// cleanup stale shared memory resources +// +// This method attempts to remove all stale shared memory files in +// the named user temporary directory. It scans the named directory +// for files matching the pattern ^$[0-9]*$. For each file found, the +// process id is extracted from the file name and a test is run to +// determine if the process is alive. If the process is not alive, +// any stale file resources are removed. +// +static void cleanup_sharedmem_resources(const char* dirname) { + + // open the user temp directory + DIR* dirp = os::opendir(dirname); + + if (dirp == NULL) { + // directory doesn't exist, so there is nothing to cleanup + return; + } + + if (!is_directory_secure(dirname)) { + // the directory is not a secure directory + return; + } + + // for each entry in the directory that matches the expected file + // name pattern, determine if the file resources are stale and if + // so, remove the file resources. Note, instrumented HotSpot processes + // for this user may start and/or terminate during this search and + // remove or create new files in this directory. The behavior of this + // loop under these conditions is dependent upon the implementation of + // opendir/readdir. + // + struct dirent* entry; + char* dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(dirname)); + errno = 0; + while ((entry = os::readdir(dirp, (struct dirent *)dbuf)) != NULL) { + + pid_t pid = filename_to_pid(entry->d_name); + + if (pid == 0) { + + if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { + + // attempt to remove all unexpected files, except "." and ".." + remove_file(dirname, entry->d_name); + } + + errno = 0; + continue; + } + + // we now have a file name that converts to a valid integer + // that could represent a process id . if this process id + // matches the current process id or the process is not running, + // then remove the stale file resources. + // + // process liveness is detected by sending signal number 0 to + // the process id (see kill(2)). if kill determines that the + // process does not exist, then the file resources are removed. + // if kill determines that that we don't have permission to + // signal the process, then the file resources are assumed to + // be stale and are removed because the resources for such a + // process should be in a different user specific directory. + // + if ((pid == os::current_process_id()) || + (kill(pid, 0) == OS_ERR && (errno == ESRCH || errno == EPERM))) { + + remove_file(dirname, entry->d_name); + } + errno = 0; + } + os::closedir(dirp); + FREE_C_HEAP_ARRAY(char, dbuf); +} + +// make the user specific temporary directory. Returns true if +// the directory exists and is secure upon return. Returns false +// if the directory exists but is either a symlink, is otherwise +// insecure, or if an error occurred. +// +static bool make_user_tmp_dir(const char* dirname) { + + // create the directory with 0755 permissions. note that the directory + // will be owned by euid::egid, which may not be the same as uid::gid. + // + if (mkdir(dirname, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) == OS_ERR) { + if (errno == EEXIST) { + // The directory already exists and was probably created by another + // JVM instance. However, this could also be the result of a + // deliberate symlink. Verify that the existing directory is safe. + // + if (!is_directory_secure(dirname)) { + // directory is not secure + if (PrintMiscellaneous && Verbose) { + warning("%s directory is insecure\n", dirname); + } + return false; + } + } + else { + // we encountered some other failure while attempting + // to create the directory + // + if (PrintMiscellaneous && Verbose) { + warning("could not create directory %s: %s\n", + dirname, strerror(errno)); + } + return false; + } + } + return true; +} + +// create the shared memory file resources +// +// This method creates the shared memory file with the given size +// This method also creates the user specific temporary directory, if +// it does not yet exist. +// +static int create_sharedmem_resources(const char* dirname, const char* filename, size_t size) { + + // make the user temporary directory + if (!make_user_tmp_dir(dirname)) { + // could not make/find the directory or the found directory + // was not secure + return -1; + } + + int result; + + RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("could not create file %s: %s\n", filename, strerror(errno)); + } + return -1; + } + + // save the file descriptor + int fd = result; + + // set the file size + RESTARTABLE(::ftruncate(fd, (off_t)size), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("could not set shared memory file size: %s\n", strerror(errno)); + } + RESTARTABLE(::close(fd), result); + return -1; + } + + return fd; +} + +// open the shared memory file for the given user and vmid. returns +// the file descriptor for the open file or -1 if the file could not +// be opened. +// +static int open_sharedmem_file(const char* filename, int oflags, TRAPS) { + + // open the file + int result; + RESTARTABLE(::open(filename, oflags), result); + if (result == OS_ERR) { + if (errno == ENOENT) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); + } + else if (errno == EACCES) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + "Permission denied"); + } + else { + THROW_MSG_0(vmSymbols::java_io_IOException(), strerror(errno)); + } + } + + return result; +} + +// create a named shared memory region. returns the address of the +// memory region on success or NULL on failure. A return value of +// NULL will ultimately disable the shared memory feature. +// +// On Solaris and Linux, the name space for shared memory objects +// is the file system name space. +// +// A monitoring application attaching to a JVM does not need to know +// the file system name of the shared memory object. However, it may +// be convenient for applications to discover the existence of newly +// created and terminating JVMs by watching the file system name space +// for files being created or removed. +// +static char* mmap_create_shared(size_t size) { + + int result; + int fd; + char* mapAddress; + + int vmid = os::current_process_id(); + + char* user_name = get_user_name(geteuid()); + + if (user_name == NULL) + return NULL; + + char* dirname = get_user_tmp_dir(user_name); + char* filename = get_sharedmem_filename(dirname, vmid); + + // cleanup any stale shared memory files + cleanup_sharedmem_resources(dirname); + + assert(((size > 0) && (size % os::vm_page_size() == 0)), + "unexpected PerfMemory region size"); + + fd = create_sharedmem_resources(dirname, filename, size); + + FREE_C_HEAP_ARRAY(char, user_name); + FREE_C_HEAP_ARRAY(char, dirname); + + if (fd == -1) { + FREE_C_HEAP_ARRAY(char, filename); + return NULL; + } + + mapAddress = (char*)::mmap((char*)0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + // attempt to close the file - restart it if it was interrupted, + // but ignore other failures + RESTARTABLE(::close(fd), result); + assert(result != OS_ERR, "could not close file"); + + if (mapAddress == MAP_FAILED) { + if (PrintMiscellaneous && Verbose) { + warning("mmap failed - %s\n", strerror(errno)); + } + remove_file(filename); + FREE_C_HEAP_ARRAY(char, filename); + return NULL; + } + + // save the file name for use in delete_shared_memory() + backing_store_file_name = filename; + + // clear the shared memory region + (void)::memset((void*) mapAddress, 0, size); + + return mapAddress; +} + +// release a named shared memory region +// +static void unmap_shared(char* addr, size_t bytes) { + os::release_memory(addr, bytes); +} + +// create the PerfData memory region in shared memory. +// +static char* create_shared_memory(size_t size) { + + // create the shared memory region. + return mmap_create_shared(size); +} + +// delete the shared PerfData memory region +// +static void delete_shared_memory(char* addr, size_t size) { + + // cleanup the persistent shared memory resources. since DestroyJavaVM does + // not support unloading of the JVM, unmapping of the memory resource is + // not performed. The memory will be reclaimed by the OS upon termination of + // the process. The backing store file is deleted from the file system. + + assert(!PerfDisableSharedMem, "shouldn't be here"); + + if (backing_store_file_name != NULL) { + remove_file(backing_store_file_name); + // Don't.. Free heap memory could deadlock os::abort() if it is called + // from signal handler. OS will reclaim the heap memory. + // FREE_C_HEAP_ARRAY(char, backing_store_file_name); + backing_store_file_name = NULL; + } +} + +// return the size of the file for the given file descriptor +// or 0 if it is not a valid size for a shared memory file +// +static size_t sharedmem_filesize(int fd, TRAPS) { + + struct stat statbuf; + int result; + + RESTARTABLE(::fstat(fd, &statbuf), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("fstat failed: %s\n", strerror(errno)); + } + THROW_MSG_0(vmSymbols::java_io_IOException(), + "Could not determine PerfMemory size"); + } + + if ((statbuf.st_size == 0) || + ((size_t)statbuf.st_size % os::vm_page_size() != 0)) { + THROW_MSG_0(vmSymbols::java_lang_Exception(), + "Invalid PerfMemory size"); + } + + return (size_t)statbuf.st_size; +} + +// attach to a named shared memory region. +// +static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemoryMode mode, char** addr, size_t* sizep, TRAPS) { + + char* mapAddress; + int result; + int fd; + size_t size; + const char* luser = NULL; + + int mmap_prot; + int file_flags; + + ResourceMark rm; + + // map the high level access mode to the appropriate permission + // constructs for the file and the shared memory mapping. + if (mode == PerfMemory::PERF_MODE_RO) { + mmap_prot = PROT_READ; + file_flags = O_RDONLY; + } + else if (mode == PerfMemory::PERF_MODE_RW) { +#ifdef LATER + mmap_prot = PROT_READ | PROT_WRITE; + file_flags = O_RDWR; +#else + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Unsupported access mode"); +#endif + } + else { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Illegal access mode"); + } + + if (user == NULL || strlen(user) == 0) { + luser = get_user_name(vmid, CHECK); + } + else { + luser = user; + } + + if (luser == NULL) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Could not map vmid to user Name"); + } + + char* dirname = get_user_tmp_dir(luser); + + // since we don't follow symbolic links when creating the backing + // store file, we don't follow them when attaching either. + // + if (!is_directory_secure(dirname)) { + FREE_C_HEAP_ARRAY(char, dirname); + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); + } + + char* filename = get_sharedmem_filename(dirname, vmid); + + // copy heap memory to resource memory. the open_sharedmem_file + // method below need to use the filename, but could throw an + // exception. using a resource array prevents the leak that + // would otherwise occur. + char* rfilename = NEW_RESOURCE_ARRAY(char, strlen(filename) + 1); + strcpy(rfilename, filename); + + // free the c heap resources that are no longer needed + if (luser != user) FREE_C_HEAP_ARRAY(char, luser); + FREE_C_HEAP_ARRAY(char, dirname); + FREE_C_HEAP_ARRAY(char, filename); + + // open the shared memory file for the give vmid + fd = open_sharedmem_file(rfilename, file_flags, CHECK); + assert(fd != OS_ERR, "unexpected value"); + + if (*sizep == 0) { + size = sharedmem_filesize(fd, CHECK); + assert(size != 0, "unexpected size"); + } + + mapAddress = (char*)::mmap((char*)0, size, mmap_prot, MAP_SHARED, fd, 0); + + // attempt to close the file - restart if it gets interrupted, + // but ignore other failures + RESTARTABLE(::close(fd), result); + assert(result != OS_ERR, "could not close file"); + + if (mapAddress == MAP_FAILED) { + if (PrintMiscellaneous && Verbose) { + warning("mmap failed: %s\n", strerror(errno)); + } + THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), + "Could not map PerfMemory"); + } + + *addr = mapAddress; + *sizep = size; + + if (PerfTraceMemOps) { + tty->print("mapped " SIZE_FORMAT " bytes for vmid %d at " + INTPTR_FORMAT "\n", size, vmid, (void*)mapAddress); + } +} + + + + +// create the PerfData memory region +// +// This method creates the memory region used to store performance +// data for the JVM. The memory may be created in standard or +// shared memory. +// +void PerfMemory::create_memory_region(size_t size) { + + if (PerfDisableSharedMem) { + // do not share the memory for the performance data. + _start = create_standard_memory(size); + } + else { + _start = create_shared_memory(size); + if (_start == NULL) { + + // creation of the shared memory region failed, attempt + // to create a contiguous, non-shared memory region instead. + // + if (PrintMiscellaneous && Verbose) { + warning("Reverting to non-shared PerfMemory region.\n"); + } + PerfDisableSharedMem = true; + _start = create_standard_memory(size); + } + } + + if (_start != NULL) _capacity = size; + +} + +// delete the PerfData memory region +// +// This method deletes the memory region used to store performance +// data for the JVM. The memory region indicated by the +// tuple will be inaccessible after a call to this method. +// +void PerfMemory::delete_memory_region() { + + assert((start() != NULL && capacity() > 0), "verify proper state"); + + // If user specifies PerfDataSaveFile, it will save the performance data + // to the specified file name no matter whether PerfDataSaveToFile is specified + // or not. In other word, -XX:PerfDataSaveFile=.. overrides flag + // -XX:+PerfDataSaveToFile. + if (PerfDataSaveToFile || PerfDataSaveFile != NULL) { + save_memory_to_file(start(), capacity()); + } + + if (PerfDisableSharedMem) { + delete_standard_memory(start(), capacity()); + } + else { + delete_shared_memory(start(), capacity()); + } +} + +// attach to the PerfData memory region for another JVM +// +// This method returns an tuple that points to +// a memory buffer that is kept reasonably synchronized with +// the PerfData memory region for the indicated JVM. This +// buffer may be kept in synchronization via shared memory +// or some other mechanism that keeps the buffer updated. +// +// If the JVM chooses not to support the attachability feature, +// this method should throw an UnsupportedOperation exception. +// +// This implementation utilizes named shared memory to map +// the indicated process's PerfData memory region into this JVMs +// address space. +// +void PerfMemory::attach(const char* user, int vmid, PerfMemoryMode mode, char** addrp, size_t* sizep, TRAPS) { + + if (vmid == 0 || vmid == os::current_process_id()) { + *addrp = start(); + *sizep = capacity(); + return; + } + + mmap_attach_shared(user, vmid, mode, addrp, sizep, CHECK); +} + +// detach from the PerfData memory region of another JVM +// +// This method detaches the PerfData memory region of another +// JVM, specified as an tuple of a buffer +// in this process's address space. This method may perform +// arbitrary actions to accomplish the detachment. The memory +// region specified by will be inaccessible after +// a call to this method. +// +// If the JVM chooses not to support the attachability feature, +// this method should throw an UnsupportedOperation exception. +// +// This implementation utilizes named shared memory to detach +// the indicated process's PerfData memory region from this +// process's address space. +// +void PerfMemory::detach(char* addr, size_t bytes, TRAPS) { + + assert(addr != 0, "address sanity check"); + assert(bytes > 0, "capacity sanity check"); + + if (PerfMemory::contains(addr) || PerfMemory::contains(addr + bytes - 1)) { + // prevent accidental detachment of this process's PerfMemory region + return; + } + + unmap_shared(addr, bytes); +} + +char* PerfMemory::backing_store_filename() { + return backing_store_file_name; +} diff --git a/hotspot/src/os/solaris/vm/stubRoutines_solaris.cpp b/hotspot/src/os/solaris/vm/stubRoutines_solaris.cpp new file mode 100644 index 00000000000..602f2c1e6bb --- /dev/null +++ b/hotspot/src/os/solaris/vm/stubRoutines_solaris.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubRoutines_solaris.cpp.incl" diff --git a/hotspot/src/os/solaris/vm/threadCritical_solaris.cpp b/hotspot/src/os/solaris/vm/threadCritical_solaris.cpp new file mode 100644 index 00000000000..58ef055e510 --- /dev/null +++ b/hotspot/src/os/solaris/vm/threadCritical_solaris.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_threadCritical_solaris.cpp.incl" + +// OS-includes here +#include +#include + +// +// See threadCritical.hpp for details of this class. +// +// For some reason, we don't do locking until the +// os::init() call completes. I'm not sure why this +// is, and have left it that way for now. This should +// be reviewed later. + +static mutex_t global_mut; +static thread_t global_mut_owner = -1; +static int global_mut_count = 0; +static bool initialized = false; + +ThreadCritical::ThreadCritical() { + if (initialized) { + thread_t owner = thr_self(); + if (global_mut_owner != owner) { + if (os::Solaris::mutex_lock(&global_mut)) + fatal1("ThreadCritical::ThreadCritical: mutex_lock failed (%s)", strerror(errno)); + assert(global_mut_count == 0, "must have clean count"); + assert(global_mut_owner == -1, "must have clean owner"); + } + global_mut_owner = owner; + ++global_mut_count; + } else { + assert (Threads::number_of_threads() == 0, "valid only during initialization"); + } +} + +ThreadCritical::~ThreadCritical() { + if (initialized) { + assert(global_mut_owner == thr_self(), "must have correct owner"); + assert(global_mut_count > 0, "must have correct count"); + --global_mut_count; + if (global_mut_count == 0) { + global_mut_owner = -1; + if (os::Solaris::mutex_unlock(&global_mut)) + fatal1("ThreadCritical::~ThreadCritical: mutex_unlock failed (%s)", strerror(errno)); + } + } else { + assert (Threads::number_of_threads() == 0, "valid only during initialization"); + } +} + +void ThreadCritical::initialize() { + // This method is called at the end of os::init(). Until + // then, we don't do real locking. + initialized = true; +} + +void ThreadCritical::release() { +} diff --git a/hotspot/src/os/solaris/vm/thread_solaris.inline.hpp b/hotspot/src/os/solaris/vm/thread_solaris.inline.hpp new file mode 100644 index 00000000000..5b7920eab9c --- /dev/null +++ b/hotspot/src/os/solaris/vm/thread_solaris.inline.hpp @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Thread::current is "hot" it's called > 128K times in the 1st 500 msecs of +// startup. +// ThreadLocalStorage::thread is warm -- it's called > 16K times in the same +// period. Thread::current() now calls ThreadLocalStorage::thread() directly. +// For SPARC, to avoid excessive register window spill-fill faults, +// we aggressively inline these routines. + +inline Thread* ThreadLocalStorage::thread() { + // don't use specialized code if +UseMallocOnly -- may confuse Purify et al. + debug_only(if (UseMallocOnly) return get_thread_slow();); + + uintptr_t raw = pd_raw_thread_id(); + int ix = pd_cache_index(raw); + Thread *Candidate = ThreadLocalStorage::_get_thread_cache[ix]; + if (Candidate->_self_raw_id == raw) { + // hit + return Candidate; + } else { + return ThreadLocalStorage::get_thread_via_cache_slowly(raw, ix); + } +} diff --git a/hotspot/src/os/solaris/vm/vmError_solaris.cpp b/hotspot/src/os/solaris/vm/vmError_solaris.cpp new file mode 100644 index 00000000000..956c7683185 --- /dev/null +++ b/hotspot/src/os/solaris/vm/vmError_solaris.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vmError_solaris.cpp.incl" + +#include +#include +#include + +void VMError::show_message_box(char *buf, int buflen) { + bool yes; + do { + error_string(buf, buflen); + int len = (int)strlen(buf); + char *p = &buf[len]; + + jio_snprintf(p, buflen - len, + "\n\n" + "Do you want to debug the problem?\n\n" + "To debug, run 'dbx - %d'; then switch to thread " INTX_FORMAT "\n" + "Enter 'yes' to launch dbx automatically (PATH must include dbx)\n" + "Otherwise, press RETURN to abort...", + os::current_process_id(), os::current_thread_id()); + + yes = os::message_box("Unexpected Error", buf); + + if (yes) { + // yes, user asked VM to launch debugger + jio_snprintf(buf, buflen, "dbx - %d", os::current_process_id()); + + os::fork_and_exec(buf); + yes = false; + } + } while (yes); +} + +// Space for our "saved" signal flags and handlers +static int resettedSigflags[2]; +static address resettedSighandler[2]; + +static void save_signal(int idx, int sig) +{ + struct sigaction sa; + sigaction(sig, NULL, &sa); + resettedSigflags[idx] = sa.sa_flags; + resettedSighandler[idx] = (sa.sa_flags & SA_SIGINFO) + ? CAST_FROM_FN_PTR(address, sa.sa_sigaction) + : CAST_FROM_FN_PTR(address, sa.sa_handler); +} + +int VMError::get_resetted_sigflags(int sig) { + if(SIGSEGV == sig) { + return resettedSigflags[0]; + } else if(SIGBUS == sig) { + return resettedSigflags[1]; + } + return -1; +} + +address VMError::get_resetted_sighandler(int sig) { + if(SIGSEGV == sig) { + return resettedSighandler[0]; + } else if(SIGBUS == sig) { + return resettedSighandler[1]; + } + return NULL; +} + +static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { + // unmask current signal + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); + sigprocmask(SIG_UNBLOCK, &newset, NULL); + + VMError err(NULL, sig, NULL, info, ucVoid); + err.report_and_die(); +} + +void VMError::reset_signal_handlers() { + // Save sigflags for resetted signals + save_signal(0, SIGSEGV); + save_signal(1, SIGBUS); + os::signal(SIGSEGV, CAST_FROM_FN_PTR(void *, crash_handler)); + os::signal(SIGBUS, CAST_FROM_FN_PTR(void *, crash_handler)); +} diff --git a/hotspot/src/os/solaris/vm/vtune_solaris.cpp b/hotspot/src/os/solaris/vm/vtune_solaris.cpp new file mode 100644 index 00000000000..b215dddf13c --- /dev/null +++ b/hotspot/src/os/solaris/vm/vtune_solaris.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vtune_solaris.cpp.incl" + +// empty implementation + +void VTune::start_GC() {} +void VTune::end_GC() {} +void VTune::start_class_load() {} +void VTune::end_class_load() {} +void VTune::exit() {} +void VTune::register_stub(const char* name, address start, address end) {} + +void VTune::create_nmethod(nmethod* nm) {} +void VTune::delete_nmethod(nmethod* nm) {} + +void vtune_init() {} diff --git a/hotspot/src/os/windows/vm/attachListener_windows.cpp b/hotspot/src/os/windows/vm/attachListener_windows.cpp new file mode 100644 index 00000000000..43e4262b6d9 --- /dev/null +++ b/hotspot/src/os/windows/vm/attachListener_windows.cpp @@ -0,0 +1,400 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_attachListener_windows.cpp.incl" + +#include +#include // SIGBREAK + +// The AttachListener thread services a queue of operations. It blocks in the dequeue +// function until an operation is enqueued. A client enqueues an operation by creating +// a thread in this process using the Win32 CreateRemoteThread function. That thread +// executes a small stub generated by the client. The stub invokes the +// JVM_EnqueueOperation function which checks the operation parameters and enqueues +// the operation to the queue serviced by the attach listener. The thread created by +// the client is a native thread and is restricted to a single page of stack. To keep +// it simple operations are pre-allocated at initialization time. An enqueue thus +// takes a preallocated operation, populates the operation parameters, adds it to +// queue and wakes up the attach listener. +// +// When an operation has completed the attach listener is required to send the +// operation result and any result data to the client. In this implementation the +// client is a pipe server. In the enqueue operation it provides the name of pipe +// to this process. When the operation is completed this process opens the pipe and +// sends the result and output back to the client. Note that writing to the pipe +// (and flushing the output) is a blocking operation. This means that a non-responsive +// client could potentially hang the attach listener thread indefinitely. In that +// case no new operations would be executed but the VM would continue as normal. +// As only suitably privileged processes can open this process we concluded that +// this wasn't worth worrying about. + + +// forward reference +class Win32AttachOperation; + + +class Win32AttachListener: AllStatic { + private: + enum { + preallocate_count = 4 // number of preallocated operations + }; + + // protects the preallocated list and the operation list + static HANDLE _mutex; + + // head of preallocated operations list + static Win32AttachOperation* _avail; + + // head and tail of enqueue operations list + static Win32AttachOperation* _head; + static Win32AttachOperation* _tail; + + + static Win32AttachOperation* head() { return _head; } + static void set_head(Win32AttachOperation* head) { _head = head; } + + static Win32AttachOperation* tail() { return _tail; } + static void set_tail(Win32AttachOperation* tail) { _tail = tail; } + + + // used to wakeup the listener + static HANDLE _wakeup; + static HANDLE wakeup() { return _wakeup; } + + public: + enum { + ATTACH_ERROR_DISABLED = 100, // error codes + ATTACH_ERROR_RESOURCE = 101, + ATTACH_ERROR_ILLEGALARG = 102, + ATTACH_ERROR_INTERNAL = 103 + }; + + static int init(); + static HANDLE mutex() { return _mutex; } + + static Win32AttachOperation* available() { return _avail; } + static void set_available(Win32AttachOperation* avail) { _avail = avail; } + + // enqueue an operation to the end of the list + static int enqueue(char* cmd, char* arg1, char* arg2, char* arg3, char* pipename); + + // dequeue an operation from from head of the list + static Win32AttachOperation* dequeue(); +}; + +// statics +HANDLE Win32AttachListener::_mutex; +HANDLE Win32AttachListener::_wakeup; +Win32AttachOperation* Win32AttachListener::_avail; +Win32AttachOperation* Win32AttachListener::_head; +Win32AttachOperation* Win32AttachListener::_tail; + + +// Win32AttachOperation is an AttachOperation that additionally encapsulates the name +// of a pipe which is used to send the operation reply/output to the client. +// Win32AttachOperation can also be linked in a list. + +class Win32AttachOperation: public AttachOperation { + private: + friend class Win32AttachListener; + + enum { + pipe_name_max = 256 // maximum pipe name + }; + + char _pipe[pipe_name_max+1]; + + const char* pipe() const { return _pipe; } + void set_pipe(const char* pipe) { + assert(strlen(pipe) <= pipe_name_max, "execeds maximum length of pipe name"); + strcpy(_pipe, pipe); + } + + HANDLE open_pipe(); + static BOOL write_pipe(HANDLE hPipe, char* buf, int len); + + Win32AttachOperation* _next; + + Win32AttachOperation* next() const { return _next; } + void set_next(Win32AttachOperation* next) { _next = next; } + + // noarg constructor as operation is preallocated + Win32AttachOperation() : AttachOperation("") { + set_pipe(""); + set_next(NULL); + } + + public: + void Win32AttachOperation::complete(jint result, bufferedStream* result_stream); +}; + + +// preallocate the required number of operations +int Win32AttachListener::init() { + _mutex = (void*)::CreateMutex(NULL, FALSE, NULL); + guarantee(_mutex != (HANDLE)NULL, "mutex creation failed"); + + _wakeup = ::CreateSemaphore(NULL, 0, 1, NULL); + guarantee(_wakeup != (HANDLE)NULL, "semaphore creation failed"); + + set_head(NULL); + set_tail(NULL); + + // preallocate a few operations + set_available(NULL); + for (int i=0; iset_next(available()); + set_available(op); + } + + return 0; +} + +// Enqueue an operation. This is called from a native thread that is not attached to VM. +// Also we need to be careful not to execute anything that results in more than a 4k stack. +// +int Win32AttachListener::enqueue(char* cmd, char* arg0, char* arg1, char* arg2, char* pipename) { + // listener not running + if (!AttachListener::is_initialized()) { + return ATTACH_ERROR_DISABLED; + } + + // check that all paramteres to the operation + if (strlen(cmd) > AttachOperation::name_length_max) return ATTACH_ERROR_ILLEGALARG; + if (strlen(arg0) > AttachOperation::arg_length_max) return ATTACH_ERROR_ILLEGALARG; + if (strlen(arg0) > AttachOperation::arg_length_max) return ATTACH_ERROR_ILLEGALARG; + if (strlen(pipename) > Win32AttachOperation::pipe_name_max) return ATTACH_ERROR_ILLEGALARG; + + // check for a well-formed pipename + if (strstr(pipename, "\\\\.\\pipe\\") != pipename) return ATTACH_ERROR_ILLEGALARG; + + // grab the lock for the list + DWORD res = ::WaitForSingleObject(mutex(), INFINITE); + if (res != WAIT_OBJECT_0) { + return ATTACH_ERROR_INTERNAL; + } + + // try to get an operation from the available list + Win32AttachOperation* op = available(); + if (op != NULL) { + set_available(op->next()); + + // add to end (tail) of list + op->set_next(NULL); + if (tail() == NULL) { + set_head(op); + } else { + tail()->set_next(op); + } + set_tail(op); + + op->set_name(cmd); + op->set_arg(0, arg0); + op->set_arg(1, arg1); + op->set_arg(2, arg2); + op->set_pipe(pipename); + + // wakeup the thread waiting for operations + ::ReleaseSemaphore(wakeup(), 1, NULL); + } + ::ReleaseMutex(mutex()); + + return (op != NULL) ? 0 : ATTACH_ERROR_RESOURCE; +} + + +// dequeue the operation from the head of the operation list. If +Win32AttachOperation* Win32AttachListener::dequeue() { + for (;;) { + DWORD res = ::WaitForSingleObject(wakeup(), INFINITE); + guarantee(res == WAIT_OBJECT_0, "wait failed"); + + res = ::WaitForSingleObject(mutex(), INFINITE); + guarantee(res == WAIT_OBJECT_0, "wait failed"); + + Win32AttachOperation* op = head(); + if (op != NULL) { + set_head(op->next()); + if (head() == NULL) { // list is empty + set_tail(NULL); + } + } + ::ReleaseMutex(mutex()); + + if (op != NULL) { + return op; + } + } +} + + +// open the pipe to the client +HANDLE Win32AttachOperation::open_pipe() { + HANDLE hPipe; + + hPipe = ::CreateFile( pipe(), // pipe name + GENERIC_WRITE, // write only + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + NULL); // no template file + + if (hPipe != INVALID_HANDLE_VALUE) { + // shouldn't happen as there is a pipe created per operation + if (::GetLastError() == ERROR_PIPE_BUSY) { + return INVALID_HANDLE_VALUE; + } + } + return hPipe; +} + +// write to the pipe +BOOL Win32AttachOperation::write_pipe(HANDLE hPipe, char* buf, int len) { + do { + DWORD nwrote; + + BOOL fSuccess = WriteFile( hPipe, // pipe handle + (LPCVOID)buf, // message + (DWORD)len, // message length + &nwrote, // bytes written + NULL); // not overlapped + if (!fSuccess) { + return fSuccess; + } + buf += nwrote; + len -= nwrote; + } + while (len > 0); + return TRUE; +} + +// Complete the operation: +// - open the pipe to the client +// - write the operation result (a jint) +// - write the operation output (the result stream) +// +void Win32AttachOperation::complete(jint result, bufferedStream* result_stream) { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + HANDLE hPipe = open_pipe(); + if (hPipe != INVALID_HANDLE_VALUE) { + BOOL fSuccess; + + char msg[32]; + sprintf(msg, "%d\n", result); + + fSuccess = write_pipe(hPipe, msg, (int)strlen(msg)); + if (fSuccess) { + write_pipe(hPipe, (char*) result_stream->base(), (int)(result_stream->size())); + } + + // Need to flush buffers + FlushFileBuffers(hPipe); + CloseHandle(hPipe); + } + + DWORD res = ::WaitForSingleObject(Win32AttachListener::mutex(), INFINITE); + if (res == WAIT_OBJECT_0) { + + // put the operation back on the available list + set_next(Win32AttachListener::available()); + Win32AttachListener::set_available(this); + + ::ReleaseMutex(Win32AttachListener::mutex()); + } + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); +} + + +// AttachOperation functions + +AttachOperation* AttachListener::dequeue() { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + AttachOperation* op = Win32AttachListener::dequeue(); + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); + + return op; +} + +int AttachListener::pd_init() { + return Win32AttachListener::init(); +} + +// always startup on Windows NT/2000/XP +bool AttachListener::init_at_startup() { + return os::win32::is_nt(); +} + +// no trigger mechanism on Windows to start Attach Listener lazily +bool AttachListener::is_init_trigger() { + return false; +} + +void AttachListener::abort() { + // nothing to do +} + +void AttachListener::pd_data_dump() { + os::signal_notify(SIGBREAK); +} + +AttachOperationFunctionInfo* AttachListener::pd_find_operation(const char* n) { + return NULL; +} + +jint AttachListener::pd_set_flag(AttachOperation* op, outputStream* out) { + out->print_cr("flag '%s' cannot be changed", op->arg(0)); + return JNI_ERR; +} + +void AttachListener::pd_detachall() { + // do nothing for now +} + +// Native thread started by remote client executes this. +extern "C" { + JNIEXPORT jint JNICALL + JVM_EnqueueOperation(char* cmd, char* arg0, char* arg1, char* arg2, char* pipename) { + return (jint)Win32AttachListener::enqueue(cmd, arg0, arg1, arg2, pipename); + } + +} // extern diff --git a/hotspot/src/os/windows/vm/c1_globals_windows.hpp b/hotspot/src/os/windows/vm/c1_globals_windows.hpp new file mode 100644 index 00000000000..84dd36c19da --- /dev/null +++ b/hotspot/src/os/windows/vm/c1_globals_windows.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for operating system dependent flags used by the +// client compiler. (see c1_globals.hpp) +// diff --git a/hotspot/src/os/windows/vm/c2_globals_windows.hpp b/hotspot/src/os/windows/vm/c2_globals_windows.hpp new file mode 100644 index 00000000000..4102058ae77 --- /dev/null +++ b/hotspot/src/os/windows/vm/c2_globals_windows.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for operating system dependent flags used by the +// server compiler. (see c2_globals.hpp) +// diff --git a/hotspot/src/os/windows/vm/chaitin_windows.cpp b/hotspot/src/os/windows/vm/chaitin_windows.cpp new file mode 100644 index 00000000000..dafd164b437 --- /dev/null +++ b/hotspot/src/os/windows/vm/chaitin_windows.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_chaitin_windows.cpp.incl" + +// Disallow the use of the frame pointer (EBP) for implicit null exceptions +// on win95/98. If we do not do this, the OS gets confused and gives a stack +// error. +void PhaseRegAlloc::pd_preallocate_hook() { +#ifndef _WIN64 + if (ImplicitNullChecks && !os::win32::is_nt()) { + for (uint block_num=1; block_num<_cfg._num_blocks; block_num++) { + Block *block = _cfg._blocks[block_num]; + + Node *block_end = block->end(); + if (block_end->is_MachNullCheck() && + block_end->as_Mach()->ideal_Opcode() != Op_Con) { + // The last instruction in the block is an implicit null check. + // Fix its input so that it does not load into the frame pointer. + _matcher.pd_implicit_null_fixup(block_end->in(1)->as_Mach(), + block_end->as_MachNullCheck()->_vidx); + } + } + } +#else + // WIN64==itanium on XP +#endif +} + +#ifdef ASSERT +// Verify that no implicit null check uses the frame pointer (EBP) as +// its register on win95/98. Use of the frame pointer in an implicit +// null check confuses the OS, yielding a stack error. +void PhaseRegAlloc::pd_postallocate_verify_hook() { +#ifndef _WIN64 + if (ImplicitNullChecks && !os::win32::is_nt()) { + for (uint block_num=1; block_num<_cfg._num_blocks; block_num++) { + Block *block = _cfg._blocks[block_num]; + + Node *block_end = block->_nodes[block->_nodes.size()-1]; + if (block_end->is_MachNullCheck() && block_end->as_Mach()->ideal_Opcode() != Op_Con) { + // The last instruction in the block is an implicit + // null check. Verify that this instruction does not + // use the frame pointer. + int reg = get_reg_first(block_end->in(1)->in(block_end->as_MachNullCheck()->_vidx)); + assert(reg != EBP_num, + "implicit null check using frame pointer on win95/98"); + } + } + } +#else + // WIN64==itanium on XP +#endif +} +#endif diff --git a/hotspot/src/os/windows/vm/globals_windows.hpp b/hotspot/src/os/windows/vm/globals_windows.hpp new file mode 100644 index 00000000000..228c69c84c0 --- /dev/null +++ b/hotspot/src/os/windows/vm/globals_windows.hpp @@ -0,0 +1,41 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Defines Windows specific flags. They are not available on other platforms. +// +#define RUNTIME_OS_FLAGS(develop, develop_pd, product, product_pd, \ + diagnostic, notproduct) \ + \ + product(bool, UseUTCFileTimestamp, true, \ + "Adjust the timestamp returned from stat() to be UTC") + + +// +// Defines Windows-specific default values. The flags are available on all +// platforms, but they may have different default values on other platforms. +// +define_pd_global(bool, UseLargePages, false); +define_pd_global(bool, UseOSErrorReporting, false); // for now. +define_pd_global(bool, UseThreadPriorities, true) ; diff --git a/hotspot/src/os/windows/vm/hpi_windows.cpp b/hotspot/src/os/windows/vm/hpi_windows.cpp new file mode 100644 index 00000000000..697a4eb8034 --- /dev/null +++ b/hotspot/src/os/windows/vm/hpi_windows.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_hpi_windows.cpp.incl" + +typedef jint (JNICALL *init_t)(GetInterfaceFunc *, void *); + +void hpi::initialize_get_interface(vm_calls_t *callbacks) +{ + // Build name of HPI. + char lib_name[JVM_MAXPATHLEN]; + + if (HPILibPath && HPILibPath[0]) { + strncpy(lib_name, HPILibPath, JVM_MAXPATHLEN - 1); + lib_name[JVM_MAXPATHLEN - 1] = '\0'; + } else { + os::jvm_path(lib_name, sizeof lib_name); + +#ifdef PRODUCT + const char *hpi_lib = "\\hpi.dll"; +#else + char *ptr = strrchr(lib_name, '\\'); + // On Win98 GetModuleFileName() returns the path in the upper case. + assert(_strnicmp(ptr, "\\jvm",4) == 0, "invalid library name"); + const char *hpi_lib = (_strnicmp(ptr, "\\jvm_g",6) == 0) ? "\\hpi_g.dll" : "\\hpi.dll"; +#endif + + *(::strrchr(lib_name, '\\')) = '\0'; /* get rid of "\\jvm.dll" */ + char *p = ::strrchr(lib_name, '\\'); + if (p != NULL) *p = '\0'; /* get rid of "\\hotspot" */ + strcat(lib_name, hpi_lib); + } + + // Load it. + if (TraceHPI) tty->print_cr("Loading HPI %s ", lib_name); + HINSTANCE lib_handle = LoadLibrary(lib_name); + if (lib_handle == NULL) { + if (TraceHPI) tty->print_cr("LoadLibrary failed, code = %d", GetLastError()); + return; + } + + // Find hpi initializer. + init_t initer = (init_t)GetProcAddress(lib_handle, "DLL_Initialize"); + if (initer == NULL) { + if (TraceHPI) tty->print_cr("GetProcAddress failed, errcode = %d", GetLastError()); + return; + } + + // Call initializer. + jint init_result = (*initer)(&_get_interface, callbacks); + if (init_result < 0) { + if (TraceHPI) tty->print_cr("DLL_Initialize failed, returned %ld", init_result); + return; + } + + if (TraceHPI) tty->print_cr("success"); + return; +} diff --git a/hotspot/src/os/windows/vm/hpi_windows.hpp b/hotspot/src/os/windows/vm/hpi_windows.hpp new file mode 100644 index 00000000000..1398950c85b --- /dev/null +++ b/hotspot/src/os/windows/vm/hpi_windows.hpp @@ -0,0 +1,166 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Win32 delegates these to the HPI. Solaris provides its own +// implementation without using the HPI (for Interrupitble I/O). + +// HPI_FileInterface + +HPIDECL(close, "close", _file, Close, int, "%d", + (int fd), + ("fd = %d", fd), + (fd)); + +HPIDECL(read, "read", _file, Read, size_t, "%ld", + (int fd, void *buf, unsigned int nBytes), + ("fd = %d, buf = %p, nBytes = %u", fd, buf, nBytes), + (fd, buf, nBytes)); + +HPIDECL(write, "write", _file, Write, size_t, "%ld", + (int fd, const void *buf, unsigned int nBytes), + ("fd = %d, buf = %p, nBytes = %u", fd, buf, nBytes), + (fd, buf, nBytes)); + + +// HPI_SocketInterface + +HPIDECL(socket_close, "socket_close", _socket, Close, int, "%d", + (int fd), + ("fd = %d", fd), + (fd)); + +HPIDECL(socket_available, "socket_available", _socket, Available, + int, "%d", + (int fd, jint *pbytes), + ("fd = %d, pbytes = %p", fd, pbytes), + (fd, pbytes)); + +HPIDECL(socket, "socket", _socket, Socket, int, "%d", + (int domain, int type, int protocol), + ("domain = %d, type = %d, protocol = %d", domain, type, protocol), + (domain, type, protocol)); + +HPIDECL(listen, "listen", _socket, Listen, int, "%d", + (int fd, int count), + ("fd = %d, count = %d", fd, count), + (fd, count)); + +HPIDECL(connect, "connect", _socket, Connect, int, "%d", + (int fd, struct sockaddr *him, int len), + ("fd = %d, him = %p, len = %d", fd, him, len), + (fd, him, len)); + +HPIDECL(accept, "accept", _socket, Accept, int, "%d", + (int fd, struct sockaddr *him, int *len), + ("fd = %d, him = %p, len = %p", fd, him, len), + (fd, him, len)); + +HPIDECL(sendto, "sendto", _socket, SendTo, int, "%d", + (int fd, char *buf, int len, int flags, + struct sockaddr *to, int tolen), + ("fd = %d, buf = %p, len = %d, flags = %d, to = %p, tolen = %d", + fd, buf, len, flags, to, tolen), + (fd, buf, len, flags, to, tolen)); + +HPIDECL(recvfrom, "recvfrom", _socket, RecvFrom, int, "%d", + (int fd, char *buf, int nbytes, int flags, + struct sockaddr *from, int *fromlen), + ("fd = %d, buf = %p, len = %d, flags = %d, frm = %p, frmlen = %d", + fd, buf, nbytes, flags, from, fromlen), + (fd, buf, nbytes, flags, from, fromlen)); + +HPIDECL(recv, "recv", _socket, Recv, int, "%d", + (int fd, char *buf, int nBytes, int flags), + ("fd = %d, buf = %p, nBytes = %d, flags = %d", + fd, buf, nBytes, flags), + (fd, buf, nBytes, flags)); + +HPIDECL(send, "send", _socket, Send, int, "%d", + (int fd, char *buf, int nBytes, int flags), + ("fd = %d, buf = %p, nBytes = %d, flags = %d", + fd, buf, nBytes, flags), + (fd, buf, nBytes, flags)); + +HPIDECL(timeout, "timeout", _socket, Timeout, int, "%d", + (int fd, long timeout), + ("fd = %d, timeout = %ld", fd, timeout), + (fd, timeout)); + +HPIDECL(get_host_by_name, "get_host_by_name", _socket, GetHostByName, + struct hostent *, "(struct hostent *)%p", + (char *name), + ("%s", name), + (name)); + +HPIDECL(socket_shutdown, "socket_shutdown", _socket, SocketShutdown, + int, "%d", + (int fd, int howto), + ("fd = %d, howto = %d", fd, howto), + (fd, howto)); + +HPIDECL(bind, "bind", _socket, Bind, + int, "%d", + (int fd, struct sockaddr *him, int len), + ("fd = %d, him = %p, len = %d", + fd, him, len), + (fd, him, len)); + +HPIDECL(get_sock_name, "get_sock_name", _socket, GetSocketName, + int, "%d", + (int fd, struct sockaddr *him, int *len), + ("fd = %d, him = %p, len = %p", + fd, him, len), + (fd, him, len)); + +HPIDECL(get_host_name, "get_host_name", _socket, GetHostName, int, "%d", + (char *hostname, int namelen), + ("hostname = %p, namelen = %d", + hostname, namelen), + (hostname, namelen)); + +HPIDECL(get_host_by_addr, "get_host_by_addr", _socket, GetHostByAddr, + struct hostent *, "(struct hostent *)%p", + (const char* name, int len, int type), + ("name = %p, len = %d, type = %d", + name, len, type), + (name, len, type)); + +HPIDECL(get_sock_opt, "get_sock_opt", _socket, SocketGetOption, int, "%d", + (int fd, int level, int optname, char *optval, int* optlen), + ("fd = %d, level = %d, optname = %d, optval = %p, optlen = %p", + fd, level, optname, optval, optlen), + (fd, level, optname, optval, optlen)); + +HPIDECL(set_sock_opt, "set_sock_opt", _socket, SocketSetOption, int, "%d", + (int fd, int level, int optname, const char *optval, int optlen), + ("fd = %d, level = %d, optname = %d, optval = %p, optlen = %d", + fd, level, optname, optval, optlen), + (fd, level, optname, optval, optlen)); + +HPIDECL(get_proto_by_name, "get_proto_by_name", _socket, GetProtoByName, + struct protoent *, "(struct protoent *)%p", + (char* name), + ("name = %p", + name), + (name)); diff --git a/hotspot/src/os/windows/vm/interfaceSupport_windows.hpp b/hotspot/src/os/windows/vm/interfaceSupport_windows.hpp new file mode 100644 index 00000000000..89c1ea79b12 --- /dev/null +++ b/hotspot/src/os/windows/vm/interfaceSupport_windows.hpp @@ -0,0 +1,41 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Contains inlined functions for class InterfaceSupport + +static inline void serialize_memory(JavaThread *thread) { + // due to chained nature of SEH handlers we have to be sure + // that our handler is always last handler before an attempt to write + // into serialization page - it can fault if we access this page + // right in the middle of protect/unprotect sequence by remote + // membar logic. + // __try/__except are very lightweight operations (only several + // instructions not affecting control flow directly on x86) + // so we can use it here, on very time critical path + __try { + os::write_memory_serialize_page(thread); + } __except (os::win32:: + serialize_fault_filter((_EXCEPTION_POINTERS*)_exception_info())) + {} +} diff --git a/hotspot/src/os/windows/vm/jvm_windows.cpp b/hotspot/src/os/windows/vm/jvm_windows.cpp new file mode 100644 index 00000000000..01e256df24d --- /dev/null +++ b/hotspot/src/os/windows/vm/jvm_windows.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_jvm_windows.cpp.incl" + +#include + +JVM_LEAF(void*, JVM_GetThreadInterruptEvent()) + return Thread::current()->osthread()->interrupt_event(); +JVM_END + +// sun.misc.Signal /////////////////////////////////////////////////////////// +// Signal code is mostly copied from classic vm, signals_md.c 1.4 98/08/23 +/* + * This function is included primarily as a debugging aid. If Java is + * running in a console window, then pressing will cause + * the current state of all active threads and monitors to be written + * to the console window. + */ + +JVM_ENTRY_NO_ENV(void*, JVM_RegisterSignal(jint sig, void* handler)) + // Copied from classic vm + // signals_md.c 1.4 98/08/23 + void* newHandler = handler == (void *)2 + ? os::user_handler() + : handler; + switch (sig) { + case SIGFPE: + return (void *)-1; /* already used by VM */ + case SIGBREAK: + if (!ReduceSignalUsage) return (void *)-1; + + /* The following signals are used for Shutdown Hooks support. However, if + ReduceSignalUsage (-Xrs) is set, Shutdown Hooks must be invoked via + System.exit(), Java is not allowed to use these signals, and the the + user is allowed to set his own _native_ handler for these signals and + invoke System.exit() as needed. Terminator.setup() is avoiding + registration of these signals when -Xrs is present. */ + case SHUTDOWN1_SIGNAL: + case SHUTDOWN2_SIGNAL: + if (ReduceSignalUsage) return (void*)-1; + } + + void* oldHandler = os::signal(sig, newHandler); + if (oldHandler == os::user_handler()) { + return (void *)2; + } else { + return oldHandler; + } +JVM_END + + +JVM_ENTRY_NO_ENV(jboolean, JVM_RaiseSignal(jint sig)) + if (ReduceSignalUsage) { + // do not allow SHUTDOWN1_SIGNAL,SHUTDOWN2_SIGNAL,BREAK_SIGNAL + // to be raised when ReduceSignalUsage is set, since no handler + // for them is actually registered in JVM or via JVM_RegisterSignal. + if (sig == SHUTDOWN1_SIGNAL || sig == SHUTDOWN2_SIGNAL || + sig == SIGBREAK) { + return JNI_FALSE; + } + } + os::signal_raise(sig); + return JNI_TRUE; +JVM_END + + +/* + All the defined signal names for Windows. + + NOTE that not all of these names are accepted by FindSignal! + + For various reasons some of these may be rejected at runtime. + + Here are the names currently accepted by a user of sun.misc.Signal with + 1.4.1 (ignoring potential interaction with use of chaining, etc): + + (LIST TBD) + +*/ +struct siglabel { + char *name; + int number; +}; + +struct siglabel siglabels[] = + /* derived from version 6.0 VC98/include/signal.h */ + {"ABRT", SIGABRT, /* abnormal termination triggered by abort cl */ + "FPE", SIGFPE, /* floating point exception */ + "SEGV", SIGSEGV, /* segment violation */ + "INT", SIGINT, /* interrupt */ + "TERM", SIGTERM, /* software term signal from kill */ + "BREAK", SIGBREAK, /* Ctrl-Break sequence */ + "ILL", SIGILL}; /* illegal instruction */ + +JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) + /* find and return the named signal's number */ + + for(int i=0;i +// #include +// #include + + +// #include "jni.h" + +#define JNI_ONLOAD_SYMBOLS {"_JNI_OnLoad@8", "JNI_OnLoad"} +#define JNI_ONUNLOAD_SYMBOLS {"_JNI_OnUnload@8", "JNI_OnUnload"} +#define JVM_ONLOAD_SYMBOLS {"_JVM_OnLoad@12", "JVM_OnLoad"} +#define AGENT_ONLOAD_SYMBOLS {"_Agent_OnLoad@12", "Agent_OnLoad"} +#define AGENT_ONUNLOAD_SYMBOLS {"_Agent_OnUnload@4", "Agent_OnUnload"} +#define AGENT_ONATTACH_SYMBOLS {"_Agent_OnAttach@12", "Agent_OnAttach"} + +#define JNI_LIB_PREFIX "" +#define JNI_LIB_SUFFIX ".dll" + +struct dirent { + char d_name[MAX_PATH]; +}; + +typedef struct { + struct dirent dirent; + char *path; + HANDLE handle; + WIN32_FIND_DATA find_data; +} DIR; + +#include + +#define JVM_MAXPATHLEN _MAX_PATH + +#define JVM_R_OK 4 +#define JVM_W_OK 2 +#define JVM_X_OK 1 +#define JVM_F_OK 0 + +#ifdef __cplusplus +extern "C" { +#endif + +JNIEXPORT void * JNICALL +JVM_GetThreadInterruptEvent(); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +/* + * File I/O + */ + +// #include +// #include +// #include +// #include + +/* O Flags */ + +#define JVM_O_RDONLY O_RDONLY +#define JVM_O_WRONLY O_WRONLY +#define JVM_O_RDWR O_RDWR +#define JVM_O_O_APPEND O_APPEND +#define JVM_O_EXCL O_EXCL +#define JVM_O_CREAT O_CREAT +#define JVM_O_DELETE O_TEMPORARY + +/* Signals */ + +#define JVM_SIGINT SIGINT +#define JVM_SIGTERM SIGTERM + +#define SHUTDOWN1_SIGNAL SIGINT /* Shutdown Hooks support. */ +#define SHUTDOWN2_SIGNAL SIGTERM + +#endif /* !_JAVASOFT_JVM_MD_H_ */ diff --git a/hotspot/src/os/windows/vm/mutex_windows.cpp b/hotspot/src/os/windows/vm/mutex_windows.cpp new file mode 100644 index 00000000000..51bacfa6a65 --- /dev/null +++ b/hotspot/src/os/windows/vm/mutex_windows.cpp @@ -0,0 +1,29 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_mutex_windows.cpp.incl" + +// put OS-includes here +# include diff --git a/hotspot/src/os/windows/vm/mutex_windows.inline.hpp b/hotspot/src/os/windows/vm/mutex_windows.inline.hpp new file mode 100644 index 00000000000..b140fce7e1a --- /dev/null +++ b/hotspot/src/os/windows/vm/mutex_windows.inline.hpp @@ -0,0 +1,23 @@ +/* + * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ diff --git a/hotspot/src/os/windows/vm/objectMonitor_windows.cpp b/hotspot/src/os/windows/vm/objectMonitor_windows.cpp new file mode 100644 index 00000000000..4b146226d01 --- /dev/null +++ b/hotspot/src/os/windows/vm/objectMonitor_windows.cpp @@ -0,0 +1,25 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" diff --git a/hotspot/src/os/windows/vm/objectMonitor_windows.hpp b/hotspot/src/os/windows/vm/objectMonitor_windows.hpp new file mode 100644 index 00000000000..ce0ce5ee988 --- /dev/null +++ b/hotspot/src/os/windows/vm/objectMonitor_windows.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: diff --git a/hotspot/src/os/windows/vm/objectMonitor_windows.inline.hpp b/hotspot/src/os/windows/vm/objectMonitor_windows.inline.hpp new file mode 100644 index 00000000000..155e7e40811 --- /dev/null +++ b/hotspot/src/os/windows/vm/objectMonitor_windows.inline.hpp @@ -0,0 +1,23 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ diff --git a/hotspot/src/os/windows/vm/osThread_windows.cpp b/hotspot/src/os/windows/vm/osThread_windows.cpp new file mode 100644 index 00000000000..15ccd714282 --- /dev/null +++ b/hotspot/src/os/windows/vm/osThread_windows.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 1998-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_osThread_windows.cpp.incl" + +void OSThread::pd_initialize() { + set_thread_handle(NULL); + set_thread_id(NULL); + set_interrupt_event(NULL); +} + +// TODO: this is not well encapsulated; creation and deletion of the +// interrupt_event are done in os_win32.cpp, create_thread and +// free_thread. Should follow pattern of Linux/Solaris code here. +void OSThread::pd_destroy() { +} diff --git a/hotspot/src/os/windows/vm/osThread_windows.hpp b/hotspot/src/os/windows/vm/osThread_windows.hpp new file mode 100644 index 00000000000..1c1af657b55 --- /dev/null +++ b/hotspot/src/os/windows/vm/osThread_windows.hpp @@ -0,0 +1,66 @@ +/* + * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +typedef void* HANDLE; + + private: + // Win32-specific thread information + HANDLE _thread_handle; // Win32 thread handle + unsigned long _thread_id; // Win32 thread id + HANDLE _interrupt_event; // Event signalled on thread interrupt + ThreadState _last_state; + + public: + // The following will only apply in the Win32 implementation, and should only + // be visible in the concrete class, not this which should be an abstract base class + HANDLE thread_handle() const { return _thread_handle; } + void set_thread_handle(HANDLE handle) { _thread_handle = handle; } + HANDLE interrupt_event() const { return _interrupt_event; } + void set_interrupt_event(HANDLE interrupt_event) { _interrupt_event = interrupt_event; } + + unsigned long thread_id() const { return _thread_id; } +#ifndef PRODUCT + // Used for debugging, return a unique integer for each thread. + int thread_identifier() const { return _thread_id; } +#endif +#ifdef ASSERT + // We expect no reposition failures so kill vm if we get one + // + bool valid_reposition_failure() { + return false; + } +#endif // ASSERT + void set_thread_id(unsigned long thread_id) { _thread_id = thread_id; } + + bool is_try_mutex_enter() { return false; } + + // This is a temporary fix for the thread states during + // suspend/resume until we throw away OSThread completely. + // NEEDS_CLEANUP + void set_last_state(ThreadState state) { _last_state = state; } + ThreadState get_last_state() { return _last_state; } + + private: + void pd_initialize(); + void pd_destroy(); diff --git a/hotspot/src/os/windows/vm/os_share_windows.hpp b/hotspot/src/os/windows/vm/os_share_windows.hpp new file mode 100644 index 00000000000..a39b49fa2b6 --- /dev/null +++ b/hotspot/src/os/windows/vm/os_share_windows.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Defines the interfaces to Windows operating system that vary across platforms diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp new file mode 100644 index 00000000000..7f229338590 --- /dev/null +++ b/hotspot/src/os/windows/vm/os_windows.cpp @@ -0,0 +1,3872 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifdef _WIN64 +// Must be at least Windows 2000 or XP to use VectoredExceptions +#define _WIN32_WINNT 0x500 +#endif + +// do not include precompiled header file +# include "incls/_os_windows.cpp.incl" + +#ifdef _DEBUG +#include +#endif + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include // For _beginthreadex(), _endthreadex() +#include // For os::dll_address_to_function_name + +/* for enumerating dll libraries */ +#include +#include + +// for timer info max values which include all bits +#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) + +// For DLL loading/load error detection +// Values of PE COFF +#define IMAGE_FILE_PTR_TO_SIGNATURE 0x3c +#define IMAGE_FILE_SIGNATURE_LENGTH 4 + +static HANDLE main_process; +static HANDLE main_thread; +static int main_thread_id; + +static FILETIME process_creation_time; +static FILETIME process_exit_time; +static FILETIME process_user_time; +static FILETIME process_kernel_time; + +#ifdef _WIN64 +PVOID topLevelVectoredExceptionHandler = NULL; +#endif + +#ifdef _M_IA64 +#define __CPU__ ia64 +#elif _M_AMD64 +#define __CPU__ amd64 +#else +#define __CPU__ i486 +#endif + +// save DLL module handle, used by GetModuleFileName + +HINSTANCE vm_lib_handle; +static int getLastErrorString(char *buf, size_t len); + +BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) { + switch (reason) { + case DLL_PROCESS_ATTACH: + vm_lib_handle = hinst; + if(ForceTimeHighResolution) + timeBeginPeriod(1L); + break; + case DLL_PROCESS_DETACH: + if(ForceTimeHighResolution) + timeEndPeriod(1L); +#ifdef _WIN64 + if (topLevelVectoredExceptionHandler != NULL) { + RemoveVectoredExceptionHandler(topLevelVectoredExceptionHandler); + topLevelVectoredExceptionHandler = NULL; + } +#endif + break; + default: + break; + } + return true; +} + +static inline double fileTimeAsDouble(FILETIME* time) { + const double high = (double) ((unsigned int) ~0); + const double split = 10000000.0; + double result = (time->dwLowDateTime / split) + + time->dwHighDateTime * (high/split); + return result; +} + +// Implementation of os + +bool os::getenv(const char* name, char* buffer, int len) { + int result = GetEnvironmentVariable(name, buffer, len); + return result > 0 && result < len; +} + + +// No setuid programs under Windows. +bool os::have_special_privileges() { + return false; +} + + +// This method is a periodic task to check for misbehaving JNI applications +// under CheckJNI, we can add any periodic checks here. +// For Windows at the moment does nothing +void os::run_periodic_checks() { + return; +} + +#ifndef _WIN64 +LONG WINAPI Handle_FLT_Exception(struct _EXCEPTION_POINTERS* exceptionInfo); +#endif +void os::init_system_properties_values() { + /* sysclasspath, java_home, dll_dir */ + { + char *home_path; + char *dll_path; + char *pslash; + char *bin = "\\bin"; + char home_dir[MAX_PATH]; + + if (!getenv("_ALT_JAVA_HOME_DIR", home_dir, MAX_PATH)) { + os::jvm_path(home_dir, sizeof(home_dir)); + // Found the full path to jvm[_g].dll. + // Now cut the path to /jre if we can. + *(strrchr(home_dir, '\\')) = '\0'; /* get rid of \jvm.dll */ + pslash = strrchr(home_dir, '\\'); + if (pslash != NULL) { + *pslash = '\0'; /* get rid of \{client|server} */ + pslash = strrchr(home_dir, '\\'); + if (pslash != NULL) + *pslash = '\0'; /* get rid of \bin */ + } + } + + home_path = NEW_C_HEAP_ARRAY(char, strlen(home_dir) + 1); + if (home_path == NULL) + return; + strcpy(home_path, home_dir); + Arguments::set_java_home(home_path); + + dll_path = NEW_C_HEAP_ARRAY(char, strlen(home_dir) + strlen(bin) + 1); + if (dll_path == NULL) + return; + strcpy(dll_path, home_dir); + strcat(dll_path, bin); + Arguments::set_dll_dir(dll_path); + + if (!set_boot_path('\\', ';')) + return; + } + + /* library_path */ + #define EXT_DIR "\\lib\\ext" + #define BIN_DIR "\\bin" + #define PACKAGE_DIR "\\Sun\\Java" + { + /* Win32 library search order (See the documentation for LoadLibrary): + * + * 1. The directory from which application is loaded. + * 2. The current directory + * 3. The system wide Java Extensions directory (Java only) + * 4. System directory (GetSystemDirectory) + * 5. Windows directory (GetWindowsDirectory) + * 6. The PATH environment variable + */ + + char *library_path; + char tmp[MAX_PATH]; + char *path_str = ::getenv("PATH"); + + library_path = NEW_C_HEAP_ARRAY(char, MAX_PATH * 5 + sizeof(PACKAGE_DIR) + + sizeof(BIN_DIR) + (path_str ? strlen(path_str) : 0) + 10); + + library_path[0] = '\0'; + + GetModuleFileName(NULL, tmp, sizeof(tmp)); + *(strrchr(tmp, '\\')) = '\0'; + strcat(library_path, tmp); + + strcat(library_path, ";."); + + GetWindowsDirectory(tmp, sizeof(tmp)); + strcat(library_path, ";"); + strcat(library_path, tmp); + strcat(library_path, PACKAGE_DIR BIN_DIR); + + GetSystemDirectory(tmp, sizeof(tmp)); + strcat(library_path, ";"); + strcat(library_path, tmp); + + GetWindowsDirectory(tmp, sizeof(tmp)); + strcat(library_path, ";"); + strcat(library_path, tmp); + + if (path_str) { + strcat(library_path, ";"); + strcat(library_path, path_str); + } + + Arguments::set_library_path(library_path); + FREE_C_HEAP_ARRAY(char, library_path); + } + + /* Default extensions directory */ + { + char path[MAX_PATH]; + char buf[2 * MAX_PATH + 2 * sizeof(EXT_DIR) + sizeof(PACKAGE_DIR) + 1]; + GetWindowsDirectory(path, MAX_PATH); + sprintf(buf, "%s%s;%s%s%s", Arguments::get_java_home(), EXT_DIR, + path, PACKAGE_DIR, EXT_DIR); + Arguments::set_ext_dirs(buf); + } + #undef EXT_DIR + #undef BIN_DIR + #undef PACKAGE_DIR + + /* Default endorsed standards directory. */ + { + #define ENDORSED_DIR "\\lib\\endorsed" + size_t len = strlen(Arguments::get_java_home()) + sizeof(ENDORSED_DIR); + char * buf = NEW_C_HEAP_ARRAY(char, len); + sprintf(buf, "%s%s", Arguments::get_java_home(), ENDORSED_DIR); + Arguments::set_endorsed_dirs(buf); + #undef ENDORSED_DIR + } + +#ifndef _WIN64 + SetUnhandledExceptionFilter(Handle_FLT_Exception); +#endif + + // Done + return; +} + +void os::breakpoint() { + DebugBreak(); +} + +// Invoked from the BREAKPOINT Macro +extern "C" void breakpoint() { + os::breakpoint(); +} + +// Returns an estimate of the current stack pointer. Result must be guaranteed +// to point into the calling threads stack, and be no lower than the current +// stack pointer. + +address os::current_stack_pointer() { + int dummy; + address sp = (address)&dummy; + return sp; +} + +// os::current_stack_base() +// +// Returns the base of the stack, which is the stack's +// starting address. This function must be called +// while running on the stack of the thread being queried. + +address os::current_stack_base() { + MEMORY_BASIC_INFORMATION minfo; + address stack_bottom; + size_t stack_size; + + VirtualQuery(&minfo, &minfo, sizeof(minfo)); + stack_bottom = (address)minfo.AllocationBase; + stack_size = minfo.RegionSize; + + // Add up the sizes of all the regions with the same + // AllocationBase. + while( 1 ) + { + VirtualQuery(stack_bottom+stack_size, &minfo, sizeof(minfo)); + if ( stack_bottom == (address)minfo.AllocationBase ) + stack_size += minfo.RegionSize; + else + break; + } + +#ifdef _M_IA64 + // IA64 has memory and register stacks + stack_size = stack_size / 2; +#endif + return stack_bottom + stack_size; +} + +size_t os::current_stack_size() { + size_t sz; + MEMORY_BASIC_INFORMATION minfo; + VirtualQuery(&minfo, &minfo, sizeof(minfo)); + sz = (size_t)os::current_stack_base() - (size_t)minfo.AllocationBase; + return sz; +} + + +LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo); + +// Thread start routine for all new Java threads +static unsigned __stdcall java_start(Thread* thread) { + // Try to randomize the cache line index of hot stack frames. + // This helps when threads of the same stack traces evict each other's + // cache lines. The threads can be either from the same JVM instance, or + // from different JVM instances. The benefit is especially true for + // processors with hyperthreading technology. + static int counter = 0; + int pid = os::current_process_id(); + _alloca(((pid ^ counter++) & 7) * 128); + + OSThread* osthr = thread->osthread(); + assert(osthr->get_state() == RUNNABLE, "invalid os thread state"); + + if (UseNUMA) { + int lgrp_id = os::numa_get_group_id(); + if (lgrp_id != -1) { + thread->set_lgrp_id(lgrp_id); + } + } + + + if (UseVectoredExceptions) { + // If we are using vectored exception we don't need to set a SEH + thread->run(); + } + else { + // Install a win32 structured exception handler around every thread created + // by VM, so VM can genrate error dump when an exception occurred in non- + // Java thread (e.g. VM thread). + __try { + thread->run(); + } __except(topLevelExceptionFilter( + (_EXCEPTION_POINTERS*)_exception_info())) { + // Nothing to do. + } + } + + // One less thread is executing + // When the VMThread gets here, the main thread may have already exited + // which frees the CodeHeap containing the Atomic::add code + if (thread != VMThread::vm_thread() && VMThread::vm_thread() != NULL) { + Atomic::dec_ptr((intptr_t*)&os::win32::_os_thread_count); + } + + return 0; +} + +static OSThread* create_os_thread(Thread* thread, HANDLE thread_handle, int thread_id) { + // Allocate the OSThread object + OSThread* osthread = new OSThread(NULL, NULL); + if (osthread == NULL) return NULL; + + // Initialize support for Java interrupts + HANDLE interrupt_event = CreateEvent(NULL, true, false, NULL); + if (interrupt_event == NULL) { + delete osthread; + return NULL; + } + osthread->set_interrupt_event(interrupt_event); + + // Store info on the Win32 thread into the OSThread + osthread->set_thread_handle(thread_handle); + osthread->set_thread_id(thread_id); + + if (UseNUMA) { + int lgrp_id = os::numa_get_group_id(); + if (lgrp_id != -1) { + thread->set_lgrp_id(lgrp_id); + } + } + + // Initial thread state is INITIALIZED, not SUSPENDED + osthread->set_state(INITIALIZED); + + return osthread; +} + + +bool os::create_attached_thread(JavaThread* thread) { +#ifdef ASSERT + thread->verify_not_published(); +#endif + HANDLE thread_h; + if (!DuplicateHandle(main_process, GetCurrentThread(), GetCurrentProcess(), + &thread_h, THREAD_ALL_ACCESS, false, 0)) { + fatal("DuplicateHandle failed\n"); + } + OSThread* osthread = create_os_thread(thread, thread_h, + (int)current_thread_id()); + if (osthread == NULL) { + return false; + } + + // Initial thread state is RUNNABLE + osthread->set_state(RUNNABLE); + + thread->set_osthread(osthread); + return true; +} + +bool os::create_main_thread(JavaThread* thread) { +#ifdef ASSERT + thread->verify_not_published(); +#endif + if (_starting_thread == NULL) { + _starting_thread = create_os_thread(thread, main_thread, main_thread_id); + if (_starting_thread == NULL) { + return false; + } + } + + // The primordial thread is runnable from the start) + _starting_thread->set_state(RUNNABLE); + + thread->set_osthread(_starting_thread); + return true; +} + +// Allocate and initialize a new OSThread +bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { + unsigned thread_id; + + // Allocate the OSThread object + OSThread* osthread = new OSThread(NULL, NULL); + if (osthread == NULL) { + return false; + } + + // Initialize support for Java interrupts + HANDLE interrupt_event = CreateEvent(NULL, true, false, NULL); + if (interrupt_event == NULL) { + delete osthread; + return NULL; + } + osthread->set_interrupt_event(interrupt_event); + osthread->set_interrupted(false); + + thread->set_osthread(osthread); + + if (stack_size == 0) { + switch (thr_type) { + case os::java_thread: + // Java threads use ThreadStackSize which default value can be changed with the flag -Xss + if (JavaThread::stack_size_at_create() > 0) + stack_size = JavaThread::stack_size_at_create(); + break; + case os::compiler_thread: + if (CompilerThreadStackSize > 0) { + stack_size = (size_t)(CompilerThreadStackSize * K); + break; + } // else fall through: + // use VMThreadStackSize if CompilerThreadStackSize is not defined + case os::vm_thread: + case os::pgc_thread: + case os::cgc_thread: + case os::watcher_thread: + if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K); + break; + } + } + + // Create the Win32 thread + // + // Contrary to what MSDN document says, "stack_size" in _beginthreadex() + // does not specify stack size. Instead, it specifies the size of + // initially committed space. The stack size is determined by + // PE header in the executable. If the committed "stack_size" is larger + // than default value in the PE header, the stack is rounded up to the + // nearest multiple of 1MB. For example if the launcher has default + // stack size of 320k, specifying any size less than 320k does not + // affect the actual stack size at all, it only affects the initial + // commitment. On the other hand, specifying 'stack_size' larger than + // default value may cause significant increase in memory usage, because + // not only the stack space will be rounded up to MB, but also the + // entire space is committed upfront. + // + // Finally Windows XP added a new flag 'STACK_SIZE_PARAM_IS_A_RESERVATION' + // for CreateThread() that can treat 'stack_size' as stack size. However we + // are not supposed to call CreateThread() directly according to MSDN + // document because JVM uses C runtime library. The good news is that the + // flag appears to work with _beginthredex() as well. + +#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION +#define STACK_SIZE_PARAM_IS_A_RESERVATION (0x10000) +#endif + + HANDLE thread_handle = + (HANDLE)_beginthreadex(NULL, + (unsigned)stack_size, + (unsigned (__stdcall *)(void*)) java_start, + thread, + CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, + &thread_id); + if (thread_handle == NULL) { + // perhaps STACK_SIZE_PARAM_IS_A_RESERVATION is not supported, try again + // without the flag. + thread_handle = + (HANDLE)_beginthreadex(NULL, + (unsigned)stack_size, + (unsigned (__stdcall *)(void*)) java_start, + thread, + CREATE_SUSPENDED, + &thread_id); + } + if (thread_handle == NULL) { + // Need to clean up stuff we've allocated so far + CloseHandle(osthread->interrupt_event()); + thread->set_osthread(NULL); + delete osthread; + return NULL; + } + + Atomic::inc_ptr((intptr_t*)&os::win32::_os_thread_count); + + // Store info on the Win32 thread into the OSThread + osthread->set_thread_handle(thread_handle); + osthread->set_thread_id(thread_id); + + // Initial thread state is INITIALIZED, not SUSPENDED + osthread->set_state(INITIALIZED); + + // The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain + return true; +} + + +// Free Win32 resources related to the OSThread +void os::free_thread(OSThread* osthread) { + assert(osthread != NULL, "osthread not set"); + CloseHandle(osthread->thread_handle()); + CloseHandle(osthread->interrupt_event()); + delete osthread; +} + + +static int has_performance_count = 0; +static jlong first_filetime; +static jlong initial_performance_count; +static jlong performance_frequency; + + +jlong as_long(LARGE_INTEGER x) { + jlong result = 0; // initialization to avoid warning + set_high(&result, x.HighPart); + set_low(&result, x.LowPart); + return result; +} + + +jlong os::elapsed_counter() { + LARGE_INTEGER count; + if (has_performance_count) { + QueryPerformanceCounter(&count); + return as_long(count) - initial_performance_count; + } else { + FILETIME wt; + GetSystemTimeAsFileTime(&wt); + return (jlong_from(wt.dwHighDateTime, wt.dwLowDateTime) - first_filetime); + } +} + + +jlong os::elapsed_frequency() { + if (has_performance_count) { + return performance_frequency; + } else { + // the FILETIME time is the number of 100-nanosecond intervals since January 1,1601. + return 10000000; + } +} + + +julong os::available_memory() { + return win32::available_memory(); +} + +julong os::win32::available_memory() { + // FIXME: GlobalMemoryStatus() may return incorrect value if total memory + // is larger than 4GB + MEMORYSTATUS ms; + GlobalMemoryStatus(&ms); + + return (julong)ms.dwAvailPhys; +} + +julong os::physical_memory() { + return win32::physical_memory(); +} + +julong os::allocatable_physical_memory(julong size) { + return MIN2(size, (julong)1400*M); +} + +// VC6 lacks DWORD_PTR +#if _MSC_VER < 1300 +typedef UINT_PTR DWORD_PTR; +#endif + +int os::active_processor_count() { + DWORD_PTR lpProcessAffinityMask = 0; + DWORD_PTR lpSystemAffinityMask = 0; + int proc_count = processor_count(); + if (proc_count <= sizeof(UINT_PTR) * BitsPerByte && + GetProcessAffinityMask(GetCurrentProcess(), &lpProcessAffinityMask, &lpSystemAffinityMask)) { + // Nof active processors is number of bits in process affinity mask + int bitcount = 0; + while (lpProcessAffinityMask != 0) { + lpProcessAffinityMask = lpProcessAffinityMask & (lpProcessAffinityMask-1); + bitcount++; + } + return bitcount; + } else { + return proc_count; + } +} + +bool os::distribute_processes(uint length, uint* distribution) { + // Not yet implemented. + return false; +} + +bool os::bind_to_processor(uint processor_id) { + // Not yet implemented. + return false; +} + +static void initialize_performance_counter() { + LARGE_INTEGER count; + if (QueryPerformanceFrequency(&count)) { + has_performance_count = 1; + performance_frequency = as_long(count); + QueryPerformanceCounter(&count); + initial_performance_count = as_long(count); + } else { + has_performance_count = 0; + FILETIME wt; + GetSystemTimeAsFileTime(&wt); + first_filetime = jlong_from(wt.dwHighDateTime, wt.dwLowDateTime); + } +} + + +double os::elapsedTime() { + return (double) elapsed_counter() / (double) elapsed_frequency(); +} + + +// Windows format: +// The FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601. +// Java format: +// Java standards require the number of milliseconds since 1/1/1970 + +// Constant offset - calculated using offset() +static jlong _offset = 116444736000000000; +// Fake time counter for reproducible results when debugging +static jlong fake_time = 0; + +#ifdef ASSERT +// Just to be safe, recalculate the offset in debug mode +static jlong _calculated_offset = 0; +static int _has_calculated_offset = 0; + +jlong offset() { + if (_has_calculated_offset) return _calculated_offset; + SYSTEMTIME java_origin; + java_origin.wYear = 1970; + java_origin.wMonth = 1; + java_origin.wDayOfWeek = 0; // ignored + java_origin.wDay = 1; + java_origin.wHour = 0; + java_origin.wMinute = 0; + java_origin.wSecond = 0; + java_origin.wMilliseconds = 0; + FILETIME jot; + if (!SystemTimeToFileTime(&java_origin, &jot)) { + fatal1("Error = %d\nWindows error", GetLastError()); + } + _calculated_offset = jlong_from(jot.dwHighDateTime, jot.dwLowDateTime); + _has_calculated_offset = 1; + assert(_calculated_offset == _offset, "Calculated and constant time offsets must be equal"); + return _calculated_offset; +} +#else +jlong offset() { + return _offset; +} +#endif + +jlong windows_to_java_time(FILETIME wt) { + jlong a = jlong_from(wt.dwHighDateTime, wt.dwLowDateTime); + return (a - offset()) / 10000; +} + +FILETIME java_to_windows_time(jlong l) { + jlong a = (l * 10000) + offset(); + FILETIME result; + result.dwHighDateTime = high(a); + result.dwLowDateTime = low(a); + return result; +} + +jlong os::timeofday() { + FILETIME wt; + GetSystemTimeAsFileTime(&wt); + return windows_to_java_time(wt); +} + + +// Must return millis since Jan 1 1970 for JVM_CurrentTimeMillis +// _use_global_time is only set if CacheTimeMillis is true +jlong os::javaTimeMillis() { + if (UseFakeTimers) { + return fake_time++; + } else { + return (_use_global_time ? read_global_time() : timeofday()); + } +} + +#define NANOS_PER_SEC CONST64(1000000000) +#define NANOS_PER_MILLISEC 1000000 +jlong os::javaTimeNanos() { + if (!has_performance_count) { + return javaTimeMillis() * NANOS_PER_MILLISEC; // the best we can do. + } else { + LARGE_INTEGER current_count; + QueryPerformanceCounter(¤t_count); + double current = as_long(current_count); + double freq = performance_frequency; + jlong time = (jlong)((current/freq) * NANOS_PER_SEC); + return time; + } +} + +void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { + if (!has_performance_count) { + // javaTimeMillis() doesn't have much percision, + // but it is not going to wrap -- so all 64 bits + info_ptr->max_value = ALL_64_BITS; + + // this is a wall clock timer, so may skip + info_ptr->may_skip_backward = true; + info_ptr->may_skip_forward = true; + } else { + jlong freq = performance_frequency; + if (freq < NANOS_PER_SEC) { + // the performance counter is 64 bits and we will + // be multiplying it -- so no wrap in 64 bits + info_ptr->max_value = ALL_64_BITS; + } else if (freq > NANOS_PER_SEC) { + // use the max value the counter can reach to + // determine the max value which could be returned + julong max_counter = (julong)ALL_64_BITS; + info_ptr->max_value = (jlong)(max_counter / (freq / NANOS_PER_SEC)); + } else { + // the performance counter is 64 bits and we will + // be using it directly -- so no wrap in 64 bits + info_ptr->max_value = ALL_64_BITS; + } + + // using a counter, so no skipping + info_ptr->may_skip_backward = false; + info_ptr->may_skip_forward = false; + } + info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time +} + +char* os::local_time_string(char *buf, size_t buflen) { + SYSTEMTIME st; + GetLocalTime(&st); + jio_snprintf(buf, buflen, "%d-%02d-%02d %02d:%02d:%02d", + st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); + return buf; +} + +bool os::getTimesSecs(double* process_real_time, + double* process_user_time, + double* process_system_time) { + HANDLE h_process = GetCurrentProcess(); + FILETIME create_time, exit_time, kernel_time, user_time; + BOOL result = GetProcessTimes(h_process, + &create_time, + &exit_time, + &kernel_time, + &user_time); + if (result != 0) { + FILETIME wt; + GetSystemTimeAsFileTime(&wt); + jlong rtc_millis = windows_to_java_time(wt); + jlong user_millis = windows_to_java_time(user_time); + jlong system_millis = windows_to_java_time(kernel_time); + *process_real_time = ((double) rtc_millis) / ((double) MILLIUNITS); + *process_user_time = ((double) user_millis) / ((double) MILLIUNITS); + *process_system_time = ((double) system_millis) / ((double) MILLIUNITS); + return true; + } else { + return false; + } +} + +void os::shutdown() { + + // allow PerfMemory to attempt cleanup of any persistent resources + perfMemory_exit(); + + // flush buffered output, finish log files + ostream_abort(); + + // Check for abort hook + abort_hook_t abort_hook = Arguments::abort_hook(); + if (abort_hook != NULL) { + abort_hook(); + } +} + +void os::abort(bool dump_core) +{ + os::shutdown(); + // no core dump on Windows + ::exit(1); +} + +// Die immediately, no exit hook, no abort hook, no cleanup. +void os::die() { + _exit(-1); +} + +// Directory routines copied from src/win32/native/java/io/dirent_md.c +// * dirent_md.c 1.15 00/02/02 +// +// The declarations for DIR and struct dirent are in jvm_win32.h. + +/* Caller must have already run dirname through JVM_NativePath, which removes + duplicate slashes and converts all instances of '/' into '\\'. */ + +DIR * +os::opendir(const char *dirname) +{ + assert(dirname != NULL, "just checking"); // hotspot change + DIR *dirp = (DIR *)malloc(sizeof(DIR)); + DWORD fattr; // hotspot change + char alt_dirname[4] = { 0, 0, 0, 0 }; + + if (dirp == 0) { + errno = ENOMEM; + return 0; + } + + /* + * Win32 accepts "\" in its POSIX stat(), but refuses to treat it + * as a directory in FindFirstFile(). We detect this case here and + * prepend the current drive name. + */ + if (dirname[1] == '\0' && dirname[0] == '\\') { + alt_dirname[0] = _getdrive() + 'A' - 1; + alt_dirname[1] = ':'; + alt_dirname[2] = '\\'; + alt_dirname[3] = '\0'; + dirname = alt_dirname; + } + + dirp->path = (char *)malloc(strlen(dirname) + 5); + if (dirp->path == 0) { + free(dirp); + errno = ENOMEM; + return 0; + } + strcpy(dirp->path, dirname); + + fattr = GetFileAttributes(dirp->path); + if (fattr == 0xffffffff) { + free(dirp->path); + free(dirp); + errno = ENOENT; + return 0; + } else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) { + free(dirp->path); + free(dirp); + errno = ENOTDIR; + return 0; + } + + /* Append "*.*", or possibly "\\*.*", to path */ + if (dirp->path[1] == ':' + && (dirp->path[2] == '\0' + || (dirp->path[2] == '\\' && dirp->path[3] == '\0'))) { + /* No '\\' needed for cases like "Z:" or "Z:\" */ + strcat(dirp->path, "*.*"); + } else { + strcat(dirp->path, "\\*.*"); + } + + dirp->handle = FindFirstFile(dirp->path, &dirp->find_data); + if (dirp->handle == INVALID_HANDLE_VALUE) { + if (GetLastError() != ERROR_FILE_NOT_FOUND) { + free(dirp->path); + free(dirp); + errno = EACCES; + return 0; + } + } + return dirp; +} + +/* parameter dbuf unused on Windows */ + +struct dirent * +os::readdir(DIR *dirp, dirent *dbuf) +{ + assert(dirp != NULL, "just checking"); // hotspot change + if (dirp->handle == INVALID_HANDLE_VALUE) { + return 0; + } + + strcpy(dirp->dirent.d_name, dirp->find_data.cFileName); + + if (!FindNextFile(dirp->handle, &dirp->find_data)) { + if (GetLastError() == ERROR_INVALID_HANDLE) { + errno = EBADF; + return 0; + } + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } + + return &dirp->dirent; +} + +int +os::closedir(DIR *dirp) +{ + assert(dirp != NULL, "just checking"); // hotspot change + if (dirp->handle != INVALID_HANDLE_VALUE) { + if (!FindClose(dirp->handle)) { + errno = EBADF; + return -1; + } + dirp->handle = INVALID_HANDLE_VALUE; + } + free(dirp->path); + free(dirp); + return 0; +} + +const char* os::dll_file_extension() { return ".dll"; } + +const char * os::get_temp_directory() +{ + static char path_buf[MAX_PATH]; + if (GetTempPath(MAX_PATH, path_buf)>0) + return path_buf; + else{ + path_buf[0]='\0'; + return path_buf; + } +} + +// Needs to be in os specific directory because windows requires another +// header file +const char* os::get_current_directory(char *buf, int buflen) { + return _getcwd(buf, buflen); +} + +//----------------------------------------------------------- +// Helper functions for fatal error handler + +// The following library functions are resolved dynamically at runtime: + +// PSAPI functions, for Windows NT, 2000, XP + +// psapi.h doesn't come with Visual Studio 6; it can be downloaded as Platform +// SDK from Microsoft. Here are the definitions copied from psapi.h +typedef struct _MODULEINFO { + LPVOID lpBaseOfDll; + DWORD SizeOfImage; + LPVOID EntryPoint; +} MODULEINFO, *LPMODULEINFO; + +static BOOL (WINAPI *_EnumProcessModules) ( HANDLE, HMODULE *, DWORD, LPDWORD ); +static DWORD (WINAPI *_GetModuleFileNameEx) ( HANDLE, HMODULE, LPTSTR, DWORD ); +static BOOL (WINAPI *_GetModuleInformation)( HANDLE, HMODULE, LPMODULEINFO, DWORD ); + +// ToolHelp Functions, for Windows 95, 98 and ME + +static HANDLE(WINAPI *_CreateToolhelp32Snapshot)(DWORD,DWORD) ; +static BOOL (WINAPI *_Module32First) (HANDLE,LPMODULEENTRY32) ; +static BOOL (WINAPI *_Module32Next) (HANDLE,LPMODULEENTRY32) ; + +bool _has_psapi; +bool _psapi_init = false; +bool _has_toolhelp; + +static bool _init_psapi() { + HINSTANCE psapi = LoadLibrary( "PSAPI.DLL" ) ; + if( psapi == NULL ) return false ; + + _EnumProcessModules = CAST_TO_FN_PTR( + BOOL(WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD), + GetProcAddress(psapi, "EnumProcessModules")) ; + _GetModuleFileNameEx = CAST_TO_FN_PTR( + DWORD (WINAPI *)(HANDLE, HMODULE, LPTSTR, DWORD), + GetProcAddress(psapi, "GetModuleFileNameExA")); + _GetModuleInformation = CAST_TO_FN_PTR( + BOOL (WINAPI *)(HANDLE, HMODULE, LPMODULEINFO, DWORD), + GetProcAddress(psapi, "GetModuleInformation")); + + _has_psapi = (_EnumProcessModules && _GetModuleFileNameEx && _GetModuleInformation); + _psapi_init = true; + return _has_psapi; +} + +static bool _init_toolhelp() { + HINSTANCE kernel32 = LoadLibrary("Kernel32.DLL") ; + if (kernel32 == NULL) return false ; + + _CreateToolhelp32Snapshot = CAST_TO_FN_PTR( + HANDLE(WINAPI *)(DWORD,DWORD), + GetProcAddress(kernel32, "CreateToolhelp32Snapshot")); + _Module32First = CAST_TO_FN_PTR( + BOOL(WINAPI *)(HANDLE,LPMODULEENTRY32), + GetProcAddress(kernel32, "Module32First" )); + _Module32Next = CAST_TO_FN_PTR( + BOOL(WINAPI *)(HANDLE,LPMODULEENTRY32), + GetProcAddress(kernel32, "Module32Next" )); + + _has_toolhelp = (_CreateToolhelp32Snapshot && _Module32First && _Module32Next); + return _has_toolhelp; +} + +#ifdef _WIN64 +// Helper routine which returns true if address in +// within the NTDLL address space. +// +static bool _addr_in_ntdll( address addr ) +{ + HMODULE hmod; + MODULEINFO minfo; + + hmod = GetModuleHandle("NTDLL.DLL"); + if ( hmod == NULL ) return false; + if ( !_GetModuleInformation( GetCurrentProcess(), hmod, + &minfo, sizeof(MODULEINFO)) ) + return false; + + if ( (addr >= minfo.lpBaseOfDll) && + (addr < (address)((uintptr_t)minfo.lpBaseOfDll + (uintptr_t)minfo.SizeOfImage))) + return true; + else + return false; +} +#endif + + +// Enumerate all modules for a given process ID +// +// Notice that Windows 95/98/Me and Windows NT/2000/XP have +// different API for doing this. We use PSAPI.DLL on NT based +// Windows and ToolHelp on 95/98/Me. + +// Callback function that is called by enumerate_modules() on +// every DLL module. +// Input parameters: +// int pid, +// char* module_file_name, +// address module_base_addr, +// unsigned module_size, +// void* param +typedef int (*EnumModulesCallbackFunc)(int, char *, address, unsigned, void *); + +// enumerate_modules for Windows NT, using PSAPI +static int _enumerate_modules_winnt( int pid, EnumModulesCallbackFunc func, void * param) +{ + HANDLE hProcess ; + +# define MAX_NUM_MODULES 128 + HMODULE modules[MAX_NUM_MODULES]; + static char filename[ MAX_PATH ]; + int result = 0; + + if (!_has_psapi && (_psapi_init || !_init_psapi())) return 0; + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, pid ) ; + if (hProcess == NULL) return 0; + + DWORD size_needed; + if (!_EnumProcessModules(hProcess, modules, + sizeof(modules), &size_needed)) { + CloseHandle( hProcess ); + return 0; + } + + // number of modules that are currently loaded + int num_modules = size_needed / sizeof(HMODULE); + + for (int i = 0; i < MIN2(num_modules, MAX_NUM_MODULES); i++) { + // Get Full pathname: + if(!_GetModuleFileNameEx(hProcess, modules[i], + filename, sizeof(filename))) { + filename[0] = '\0'; + } + + MODULEINFO modinfo; + if (!_GetModuleInformation(hProcess, modules[i], + &modinfo, sizeof(modinfo))) { + modinfo.lpBaseOfDll = NULL; + modinfo.SizeOfImage = 0; + } + + // Invoke callback function + result = func(pid, filename, (address)modinfo.lpBaseOfDll, + modinfo.SizeOfImage, param); + if (result) break; + } + + CloseHandle( hProcess ) ; + return result; +} + + +// enumerate_modules for Windows 95/98/ME, using TOOLHELP +static int _enumerate_modules_windows( int pid, EnumModulesCallbackFunc func, void *param) +{ + HANDLE hSnapShot ; + static MODULEENTRY32 modentry ; + int result = 0; + + if (!_has_toolhelp) return 0; + + // Get a handle to a Toolhelp snapshot of the system + hSnapShot = _CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid ) ; + if( hSnapShot == INVALID_HANDLE_VALUE ) { + return FALSE ; + } + + // iterate through all modules + modentry.dwSize = sizeof(MODULEENTRY32) ; + bool not_done = _Module32First( hSnapShot, &modentry ) != 0; + + while( not_done ) { + // invoke the callback + result=func(pid, modentry.szExePath, (address)modentry.modBaseAddr, + modentry.modBaseSize, param); + if (result) break; + + modentry.dwSize = sizeof(MODULEENTRY32) ; + not_done = _Module32Next( hSnapShot, &modentry ) != 0; + } + + CloseHandle(hSnapShot); + return result; +} + +int enumerate_modules( int pid, EnumModulesCallbackFunc func, void * param ) +{ + // Get current process ID if caller doesn't provide it. + if (!pid) pid = os::current_process_id(); + + if (os::win32::is_nt()) return _enumerate_modules_winnt (pid, func, param); + else return _enumerate_modules_windows(pid, func, param); +} + +struct _modinfo { + address addr; + char* full_path; // point to a char buffer + int buflen; // size of the buffer + address base_addr; +}; + +static int _locate_module_by_addr(int pid, char * mod_fname, address base_addr, + unsigned size, void * param) { + struct _modinfo *pmod = (struct _modinfo *)param; + if (!pmod) return -1; + + if (base_addr <= pmod->addr && + base_addr+size > pmod->addr) { + // if a buffer is provided, copy path name to the buffer + if (pmod->full_path) { + jio_snprintf(pmod->full_path, pmod->buflen, "%s", mod_fname); + } + pmod->base_addr = base_addr; + return 1; + } + return 0; +} + +bool os::dll_address_to_library_name(address addr, char* buf, + int buflen, int* offset) { +// NOTE: the reason we don't use SymGetModuleInfo() is it doesn't always +// return the full path to the DLL file, sometimes it returns path +// to the corresponding PDB file (debug info); sometimes it only +// returns partial path, which makes life painful. + + struct _modinfo mi; + mi.addr = addr; + mi.full_path = buf; + mi.buflen = buflen; + int pid = os::current_process_id(); + if (enumerate_modules(pid, _locate_module_by_addr, (void *)&mi)) { + // buf already contains path name + if (offset) *offset = addr - mi.base_addr; + return true; + } else { + if (buf) buf[0] = '\0'; + if (offset) *offset = -1; + return false; + } +} + +bool os::dll_address_to_function_name(address addr, char *buf, + int buflen, int *offset) { + // Unimplemented on Windows - in order to use SymGetSymFromAddr(), + // we need to initialize imagehlp/dbghelp, then load symbol table + // for every module. That's too much work to do after a fatal error. + // For an example on how to implement this function, see 1.4.2. + if (offset) *offset = -1; + if (buf) buf[0] = '\0'; + return false; +} + +// save the start and end address of jvm.dll into param[0] and param[1] +static int _locate_jvm_dll(int pid, char* mod_fname, address base_addr, + unsigned size, void * param) { + if (!param) return -1; + + if (base_addr <= (address)_locate_jvm_dll && + base_addr+size > (address)_locate_jvm_dll) { + ((address*)param)[0] = base_addr; + ((address*)param)[1] = base_addr + size; + return 1; + } + return 0; +} + +address vm_lib_location[2]; // start and end address of jvm.dll + +// check if addr is inside jvm.dll +bool os::address_is_in_vm(address addr) { + if (!vm_lib_location[0] || !vm_lib_location[1]) { + int pid = os::current_process_id(); + if (!enumerate_modules(pid, _locate_jvm_dll, (void *)vm_lib_location)) { + assert(false, "Can't find jvm module."); + return false; + } + } + + return (vm_lib_location[0] <= addr) && (addr < vm_lib_location[1]); +} + +// print module info; param is outputStream* +static int _print_module(int pid, char* fname, address base, + unsigned size, void* param) { + if (!param) return -1; + + outputStream* st = (outputStream*)param; + + address end_addr = base + size; + st->print(PTR_FORMAT " - " PTR_FORMAT " \t%s\n", base, end_addr, fname); + return 0; +} + +// Loads .dll/.so and +// in case of error it checks if .dll/.so was built for the +// same architecture as Hotspot is running on +void * os::dll_load(const char *name, char *ebuf, int ebuflen) +{ + void * result = LoadLibrary(name); + if (result != NULL) + { + return result; + } + + long errcode = GetLastError(); + if (errcode == ERROR_MOD_NOT_FOUND) { + strncpy(ebuf, "Can't find dependent libraries", ebuflen-1); + ebuf[ebuflen-1]='\0'; + return NULL; + } + + // Parsing dll below + // If we can read dll-info and find that dll was built + // for an architecture other than Hotspot is running in + // - then print to buffer "DLL was built for a different architecture" + // else call getLastErrorString to obtain system error message + + // Read system error message into ebuf + // It may or may not be overwritten below (in the for loop and just above) + getLastErrorString(ebuf, (size_t) ebuflen); + ebuf[ebuflen-1]='\0'; + int file_descriptor=::open(name, O_RDONLY | O_BINARY, 0); + if (file_descriptor<0) + { + return NULL; + } + + uint32_t signature_offset; + uint16_t lib_arch=0; + bool failed_to_get_lib_arch= + ( + //Go to position 3c in the dll + (os::seek_to_file_offset(file_descriptor,IMAGE_FILE_PTR_TO_SIGNATURE)<0) + || + // Read loacation of signature + (sizeof(signature_offset)!= + (os::read(file_descriptor, (void*)&signature_offset,sizeof(signature_offset)))) + || + //Go to COFF File Header in dll + //that is located after"signature" (4 bytes long) + (os::seek_to_file_offset(file_descriptor, + signature_offset+IMAGE_FILE_SIGNATURE_LENGTH)<0) + || + //Read field that contains code of architecture + // that dll was build for + (sizeof(lib_arch)!= + (os::read(file_descriptor, (void*)&lib_arch,sizeof(lib_arch)))) + ); + + ::close(file_descriptor); + if (failed_to_get_lib_arch) + { + // file i/o error - report getLastErrorString(...) msg + return NULL; + } + + typedef struct + { + uint16_t arch_code; + char* arch_name; + } arch_t; + + static const arch_t arch_array[]={ + {IMAGE_FILE_MACHINE_I386, (char*)"IA 32"}, + {IMAGE_FILE_MACHINE_AMD64, (char*)"AMD 64"}, + {IMAGE_FILE_MACHINE_IA64, (char*)"IA 64"} + }; + #if (defined _M_IA64) + static const uint16_t running_arch=IMAGE_FILE_MACHINE_IA64; + #elif (defined _M_AMD64) + static const uint16_t running_arch=IMAGE_FILE_MACHINE_AMD64; + #elif (defined _M_IX86) + static const uint16_t running_arch=IMAGE_FILE_MACHINE_I386; + #else + #error Method os::dll_load requires that one of following \ + is defined :_M_IA64,_M_AMD64 or _M_IX86 + #endif + + + // Obtain a string for printf operation + // lib_arch_str shall contain string what platform this .dll was built for + // running_arch_str shall string contain what platform Hotspot was built for + char *running_arch_str=NULL,*lib_arch_str=NULL; + for (unsigned int i=0;iprint_cr("Dynamic libraries:"); + enumerate_modules(pid, _print_module, (void *)st); +} + +void os::print_os_info(outputStream* st) { + st->print("OS:"); + + OSVERSIONINFOEX osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + if (!GetVersionEx((OSVERSIONINFO *)&osvi)) { + st->print_cr("N/A"); + return; + } + + int os_vers = osvi.dwMajorVersion * 1000 + osvi.dwMinorVersion; + + if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) { + switch (os_vers) { + case 3051: st->print(" Windows NT 3.51"); break; + case 4000: st->print(" Windows NT 4.0"); break; + case 5000: st->print(" Windows 2000"); break; + case 5001: st->print(" Windows XP"); break; + case 5002: st->print(" Windows Server 2003 family"); break; + case 6000: st->print(" Windows Vista"); break; + default: // future windows, print out its major and minor versions + st->print(" Windows NT %d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion); + } + } else { + switch (os_vers) { + case 4000: st->print(" Windows 95"); break; + case 4010: st->print(" Windows 98"); break; + case 4090: st->print(" Windows Me"); break; + default: // future windows, print out its major and minor versions + st->print(" Windows %d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion); + } + } + + st->print(" Build %d", osvi.dwBuildNumber); + st->print(" %s", osvi.szCSDVersion); // service pack + st->cr(); +} + +void os::print_memory_info(outputStream* st) { + st->print("Memory:"); + st->print(" %dk page", os::vm_page_size()>>10); + + // FIXME: GlobalMemoryStatus() may return incorrect value if total memory + // is larger than 4GB + MEMORYSTATUS ms; + GlobalMemoryStatus(&ms); + + st->print(", physical %uk", os::physical_memory() >> 10); + st->print("(%uk free)", os::available_memory() >> 10); + + st->print(", swap %uk", ms.dwTotalPageFile >> 10); + st->print("(%uk free)", ms.dwAvailPageFile >> 10); + st->cr(); +} + +void os::print_siginfo(outputStream *st, void *siginfo) { + EXCEPTION_RECORD* er = (EXCEPTION_RECORD*)siginfo; + st->print("siginfo:"); + st->print(" ExceptionCode=0x%x", er->ExceptionCode); + + if (er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && + er->NumberParameters >= 2) { + switch (er->ExceptionInformation[0]) { + case 0: st->print(", reading address"); break; + case 1: st->print(", writing address"); break; + default: st->print(", ExceptionInformation=" INTPTR_FORMAT, + er->ExceptionInformation[0]); + } + st->print(" " INTPTR_FORMAT, er->ExceptionInformation[1]); + } else if (er->ExceptionCode == EXCEPTION_IN_PAGE_ERROR && + er->NumberParameters >= 2 && UseSharedSpaces) { + FileMapInfo* mapinfo = FileMapInfo::current_info(); + if (mapinfo->is_in_shared_space((void*)er->ExceptionInformation[1])) { + st->print("\n\nError accessing class data sharing archive." \ + " Mapped file inaccessible during execution, " \ + " possible disk/network problem."); + } + } else { + int num = er->NumberParameters; + if (num > 0) { + st->print(", ExceptionInformation="); + for (int i = 0; i < num; i++) { + st->print(INTPTR_FORMAT " ", er->ExceptionInformation[i]); + } + } + } + st->cr(); +} + +void os::print_signal_handlers(outputStream* st, char* buf, size_t buflen) { + // do nothing +} + +static char saved_jvm_path[MAX_PATH] = {0}; + +// Find the full path to the current module, jvm.dll or jvm_g.dll +void os::jvm_path(char *buf, jint buflen) { + // Error checking. + if (buflen < MAX_PATH) { + assert(false, "must use a large-enough buffer"); + buf[0] = '\0'; + return; + } + // Lazy resolve the path to current module. + if (saved_jvm_path[0] != 0) { + strcpy(buf, saved_jvm_path); + return; + } + + GetModuleFileName(vm_lib_handle, buf, buflen); + strcpy(saved_jvm_path, buf); +} + + +void os::print_jni_name_prefix_on(outputStream* st, int args_size) { +#ifndef _WIN64 + st->print("_"); +#endif +} + + +void os::print_jni_name_suffix_on(outputStream* st, int args_size) { +#ifndef _WIN64 + st->print("@%d", args_size * sizeof(int)); +#endif +} + +// sun.misc.Signal +// NOTE that this is a workaround for an apparent kernel bug where if +// a signal handler for SIGBREAK is installed then that signal handler +// takes priority over the console control handler for CTRL_CLOSE_EVENT. +// See bug 4416763. +static void (*sigbreakHandler)(int) = NULL; + +static void UserHandler(int sig, void *siginfo, void *context) { + os::signal_notify(sig); + // We need to reinstate the signal handler each time... + os::signal(sig, (void*)UserHandler); +} + +void* os::user_handler() { + return (void*) UserHandler; +} + +void* os::signal(int signal_number, void* handler) { + if ((signal_number == SIGBREAK) && (!ReduceSignalUsage)) { + void (*oldHandler)(int) = sigbreakHandler; + sigbreakHandler = (void (*)(int)) handler; + return (void*) oldHandler; + } else { + return (void*)::signal(signal_number, (void (*)(int))handler); + } +} + +void os::signal_raise(int signal_number) { + raise(signal_number); +} + +// The Win32 C runtime library maps all console control events other than ^C +// into SIGBREAK, which makes it impossible to distinguish ^BREAK from close, +// logoff, and shutdown events. We therefore install our own console handler +// that raises SIGTERM for the latter cases. +// +static BOOL WINAPI consoleHandler(DWORD event) { + switch(event) { + case CTRL_C_EVENT: + if (is_error_reported()) { + // Ctrl-C is pressed during error reporting, likely because the error + // handler fails to abort. Let VM die immediately. + os::die(); + } + + os::signal_raise(SIGINT); + return TRUE; + break; + case CTRL_BREAK_EVENT: + if (sigbreakHandler != NULL) { + (*sigbreakHandler)(SIGBREAK); + } + return TRUE; + break; + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + os::signal_raise(SIGTERM); + return TRUE; + break; + default: + break; + } + return FALSE; +} + +/* + * The following code is moved from os.cpp for making this + * code platform specific, which it is by its very nature. + */ + +// Return maximum OS signal used + 1 for internal use only +// Used as exit signal for signal_thread +int os::sigexitnum_pd(){ + return NSIG; +} + +// a counter for each possible signal value, including signal_thread exit signal +static volatile jint pending_signals[NSIG+1] = { 0 }; +static HANDLE sig_sem; + +void os::signal_init_pd() { + // Initialize signal structures + memset((void*)pending_signals, 0, sizeof(pending_signals)); + + sig_sem = ::CreateSemaphore(NULL, 0, NSIG+1, NULL); + + // Programs embedding the VM do not want it to attempt to receive + // events like CTRL_LOGOFF_EVENT, which are used to implement the + // shutdown hooks mechanism introduced in 1.3. For example, when + // the VM is run as part of a Windows NT service (i.e., a servlet + // engine in a web server), the correct behavior is for any console + // control handler to return FALSE, not TRUE, because the OS's + // "final" handler for such events allows the process to continue if + // it is a service (while terminating it if it is not a service). + // To make this behavior uniform and the mechanism simpler, we + // completely disable the VM's usage of these console events if -Xrs + // (=ReduceSignalUsage) is specified. This means, for example, that + // the CTRL-BREAK thread dump mechanism is also disabled in this + // case. See bugs 4323062, 4345157, and related bugs. + + if (!ReduceSignalUsage) { + // Add a CTRL-C handler + SetConsoleCtrlHandler(consoleHandler, TRUE); + } +} + +void os::signal_notify(int signal_number) { + BOOL ret; + + Atomic::inc(&pending_signals[signal_number]); + ret = ::ReleaseSemaphore(sig_sem, 1, NULL); + assert(ret != 0, "ReleaseSemaphore() failed"); +} + +static int check_pending_signals(bool wait_for_signal) { + DWORD ret; + while (true) { + for (int i = 0; i < NSIG + 1; i++) { + jint n = pending_signals[i]; + if (n > 0 && n == Atomic::cmpxchg(n - 1, &pending_signals[i], n)) { + return i; + } + } + if (!wait_for_signal) { + return -1; + } + + JavaThread *thread = JavaThread::current(); + + ThreadBlockInVM tbivm(thread); + + bool threadIsSuspended; + do { + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() + ret = ::WaitForSingleObject(sig_sem, INFINITE); + assert(ret == WAIT_OBJECT_0, "WaitForSingleObject() failed"); + + // were we externally suspended while we were waiting? + threadIsSuspended = thread->handle_special_suspend_equivalent_condition(); + if (threadIsSuspended) { + // + // The semaphore has been incremented, but while we were waiting + // another thread suspended us. We don't want to continue running + // while suspended because that would surprise the thread that + // suspended us. + // + ret = ::ReleaseSemaphore(sig_sem, 1, NULL); + assert(ret != 0, "ReleaseSemaphore() failed"); + + thread->java_suspend_self(); + } + } while (threadIsSuspended); + } +} + +int os::signal_lookup() { + return check_pending_signals(false); +} + +int os::signal_wait() { + return check_pending_signals(true); +} + +// Implicit OS exception handling + +LONG Handle_Exception(struct _EXCEPTION_POINTERS* exceptionInfo, address handler) { + JavaThread* thread = JavaThread::current(); + // Save pc in thread +#ifdef _M_IA64 + thread->set_saved_exception_pc((address)exceptionInfo->ContextRecord->StIIP); + // Set pc to handler + exceptionInfo->ContextRecord->StIIP = (DWORD64)handler; +#elif _M_AMD64 + thread->set_saved_exception_pc((address)exceptionInfo->ContextRecord->Rip); + // Set pc to handler + exceptionInfo->ContextRecord->Rip = (DWORD64)handler; +#else + thread->set_saved_exception_pc((address)exceptionInfo->ContextRecord->Eip); + // Set pc to handler + exceptionInfo->ContextRecord->Eip = (LONG)handler; +#endif + + // Continue the execution + return EXCEPTION_CONTINUE_EXECUTION; +} + + +// Used for PostMortemDump +extern "C" void safepoints(); +extern "C" void find(int x); +extern "C" void events(); + +// According to Windows API documentation, an illegal instruction sequence should generate +// the 0xC000001C exception code. However, real world experience shows that occasionnaly +// the execution of an illegal instruction can generate the exception code 0xC000001E. This +// seems to be an undocumented feature of Win NT 4.0 (and probably other Windows systems). + +#define EXCEPTION_ILLEGAL_INSTRUCTION_2 0xC000001E + +// From "Execution Protection in the Windows Operating System" draft 0.35 +// Once a system header becomes available, the "real" define should be +// included or copied here. +#define EXCEPTION_INFO_EXEC_VIOLATION 0x08 + +#define def_excpt(val) #val, val + +struct siglabel { + char *name; + int number; +}; + +struct siglabel exceptlabels[] = { + def_excpt(EXCEPTION_ACCESS_VIOLATION), + def_excpt(EXCEPTION_DATATYPE_MISALIGNMENT), + def_excpt(EXCEPTION_BREAKPOINT), + def_excpt(EXCEPTION_SINGLE_STEP), + def_excpt(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), + def_excpt(EXCEPTION_FLT_DENORMAL_OPERAND), + def_excpt(EXCEPTION_FLT_DIVIDE_BY_ZERO), + def_excpt(EXCEPTION_FLT_INEXACT_RESULT), + def_excpt(EXCEPTION_FLT_INVALID_OPERATION), + def_excpt(EXCEPTION_FLT_OVERFLOW), + def_excpt(EXCEPTION_FLT_STACK_CHECK), + def_excpt(EXCEPTION_FLT_UNDERFLOW), + def_excpt(EXCEPTION_INT_DIVIDE_BY_ZERO), + def_excpt(EXCEPTION_INT_OVERFLOW), + def_excpt(EXCEPTION_PRIV_INSTRUCTION), + def_excpt(EXCEPTION_IN_PAGE_ERROR), + def_excpt(EXCEPTION_ILLEGAL_INSTRUCTION), + def_excpt(EXCEPTION_ILLEGAL_INSTRUCTION_2), + def_excpt(EXCEPTION_NONCONTINUABLE_EXCEPTION), + def_excpt(EXCEPTION_STACK_OVERFLOW), + def_excpt(EXCEPTION_INVALID_DISPOSITION), + def_excpt(EXCEPTION_GUARD_PAGE), + def_excpt(EXCEPTION_INVALID_HANDLE), + NULL, 0 +}; + +const char* os::exception_name(int exception_code, char *buf, size_t size) { + for (int i = 0; exceptlabels[i].name != NULL; i++) { + if (exceptlabels[i].number == exception_code) { + jio_snprintf(buf, size, "%s", exceptlabels[i].name); + return buf; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +LONG Handle_IDiv_Exception(struct _EXCEPTION_POINTERS* exceptionInfo) { + // handle exception caused by idiv; should only happen for -MinInt/-1 + // (division by zero is handled explicitly) +#ifdef _M_IA64 + assert(0, "Fix Handle_IDiv_Exception"); +#elif _M_AMD64 + PCONTEXT ctx = exceptionInfo->ContextRecord; + address pc = (address)ctx->Rip; + NOT_PRODUCT(Events::log("idiv overflow exception at " INTPTR_FORMAT , pc)); + assert(pc[0] == 0xF7, "not an idiv opcode"); + assert((pc[1] & ~0x7) == 0xF8, "cannot handle non-register operands"); + assert(ctx->Rax == min_jint, "unexpected idiv exception"); + // set correct result values and continue after idiv instruction + ctx->Rip = (DWORD)pc + 2; // idiv reg, reg is 2 bytes + ctx->Rax = (DWORD)min_jint; // result + ctx->Rdx = (DWORD)0; // remainder + // Continue the execution +#else + PCONTEXT ctx = exceptionInfo->ContextRecord; + address pc = (address)ctx->Eip; + NOT_PRODUCT(Events::log("idiv overflow exception at " INTPTR_FORMAT , pc)); + assert(pc[0] == 0xF7, "not an idiv opcode"); + assert((pc[1] & ~0x7) == 0xF8, "cannot handle non-register operands"); + assert(ctx->Eax == min_jint, "unexpected idiv exception"); + // set correct result values and continue after idiv instruction + ctx->Eip = (DWORD)pc + 2; // idiv reg, reg is 2 bytes + ctx->Eax = (DWORD)min_jint; // result + ctx->Edx = (DWORD)0; // remainder + // Continue the execution +#endif + return EXCEPTION_CONTINUE_EXECUTION; +} + +#ifndef _WIN64 +//----------------------------------------------------------------------------- +LONG WINAPI Handle_FLT_Exception(struct _EXCEPTION_POINTERS* exceptionInfo) { + // handle exception caused by native mothod modifying control word + PCONTEXT ctx = exceptionInfo->ContextRecord; + DWORD exception_code = exceptionInfo->ExceptionRecord->ExceptionCode; + + switch (exception_code) { + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + jint fp_control_word = (* (jint*) StubRoutines::addr_fpu_cntrl_wrd_std()); + if (fp_control_word != ctx->FloatSave.ControlWord) { + // Restore FPCW and mask out FLT exceptions + ctx->FloatSave.ControlWord = fp_control_word | 0xffffffc0; + // Mask out pending FLT exceptions + ctx->FloatSave.StatusWord &= 0xffffff00; + return EXCEPTION_CONTINUE_EXECUTION; + } + } + return EXCEPTION_CONTINUE_SEARCH; +} +#else //_WIN64 +/* + On Windows, the mxcsr control bits are non-volatile across calls + See also CR 6192333 + If EXCEPTION_FLT_* happened after some native method modified + mxcsr - it is not a jvm fault. + However should we decide to restore of mxcsr after a faulty + native method we can uncomment following code + jint MxCsr = INITIAL_MXCSR; + // we can't use StubRoutines::addr_mxcsr_std() + // because in Win64 mxcsr is not saved there + if (MxCsr != ctx->MxCsr) { + ctx->MxCsr = MxCsr; + return EXCEPTION_CONTINUE_EXECUTION; + } + +*/ +#endif //_WIN64 + + +// Fatal error reporting is single threaded so we can make this a +// static and preallocated. If it's more than MAX_PATH silently ignore +// it. +static char saved_error_file[MAX_PATH] = {0}; + +void os::set_error_file(const char *logfile) { + if (strlen(logfile) <= MAX_PATH) { + strncpy(saved_error_file, logfile, MAX_PATH); + } +} + +static inline void report_error(Thread* t, DWORD exception_code, + address addr, void* siginfo, void* context) { + VMError err(t, exception_code, addr, siginfo, context); + err.report_and_die(); + + // If UseOsErrorReporting, this will return here and save the error file + // somewhere where we can find it in the minidump. +} + +//----------------------------------------------------------------------------- +LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { + if (InterceptOSException) return EXCEPTION_CONTINUE_SEARCH; + DWORD exception_code = exceptionInfo->ExceptionRecord->ExceptionCode; +#ifdef _M_IA64 + address pc = (address) exceptionInfo->ContextRecord->StIIP; +#elif _M_AMD64 + address pc = (address) exceptionInfo->ContextRecord->Rip; +#else + address pc = (address) exceptionInfo->ContextRecord->Eip; +#endif + Thread* t = ThreadLocalStorage::get_thread_slow(); // slow & steady + +#ifndef _WIN64 + // Execution protection violation - win32 running on AMD64 only + // Handled first to avoid misdiagnosis as a "normal" access violation; + // This is safe to do because we have a new/unique ExceptionInformation + // code for this condition. + if (exception_code == EXCEPTION_ACCESS_VIOLATION) { + PEXCEPTION_RECORD exceptionRecord = exceptionInfo->ExceptionRecord; + int exception_subcode = (int) exceptionRecord->ExceptionInformation[0]; + address addr = (address) exceptionRecord->ExceptionInformation[1]; + + if (exception_subcode == EXCEPTION_INFO_EXEC_VIOLATION) { + int page_size = os::vm_page_size(); + + // Make sure the pc and the faulting address are sane. + // + // If an instruction spans a page boundary, and the page containing + // the beginning of the instruction is executable but the following + // page is not, the pc and the faulting address might be slightly + // different - we still want to unguard the 2nd page in this case. + // + // 15 bytes seems to be a (very) safe value for max instruction size. + bool pc_is_near_addr = + (pointer_delta((void*) addr, (void*) pc, sizeof(char)) < 15); + bool instr_spans_page_boundary = + (align_size_down((intptr_t) pc ^ (intptr_t) addr, + (intptr_t) page_size) > 0); + + if (pc == addr || (pc_is_near_addr && instr_spans_page_boundary)) { + static volatile address last_addr = + (address) os::non_memory_address_word(); + + // In conservative mode, don't unguard unless the address is in the VM + if (UnguardOnExecutionViolation > 0 && addr != last_addr && + (UnguardOnExecutionViolation > 1 || os::address_is_in_vm(addr))) { + + // Unguard and retry + address page_start = + (address) align_size_down((intptr_t) addr, (intptr_t) page_size); + bool res = os::unguard_memory((char*) page_start, page_size); + + if (PrintMiscellaneous && Verbose) { + char buf[256]; + jio_snprintf(buf, sizeof(buf), "Execution protection violation " + "at " INTPTR_FORMAT + ", unguarding " INTPTR_FORMAT ": %s", addr, + page_start, (res ? "success" : strerror(errno))); + tty->print_raw_cr(buf); + } + + // Set last_addr so if we fault again at the same address, we don't + // end up in an endless loop. + // + // There are two potential complications here. Two threads trapping + // at the same address at the same time could cause one of the + // threads to think it already unguarded, and abort the VM. Likely + // very rare. + // + // The other race involves two threads alternately trapping at + // different addresses and failing to unguard the page, resulting in + // an endless loop. This condition is probably even more unlikely + // than the first. + // + // Although both cases could be avoided by using locks or thread + // local last_addr, these solutions are unnecessary complication: + // this handler is a best-effort safety net, not a complete solution. + // It is disabled by default and should only be used as a workaround + // in case we missed any no-execute-unsafe VM code. + + last_addr = addr; + + return EXCEPTION_CONTINUE_EXECUTION; + } + } + + // Last unguard failed or not unguarding + tty->print_raw_cr("Execution protection violation"); + report_error(t, exception_code, addr, exceptionInfo->ExceptionRecord, + exceptionInfo->ContextRecord); + return EXCEPTION_CONTINUE_SEARCH; + } + } +#endif // _WIN64 + + // Check to see if we caught the safepoint code in the + // process of write protecting the memory serialization page. + // It write enables the page immediately after protecting it + // so just return. + if ( exception_code == EXCEPTION_ACCESS_VIOLATION ) { + JavaThread* thread = (JavaThread*) t; + PEXCEPTION_RECORD exceptionRecord = exceptionInfo->ExceptionRecord; + address addr = (address) exceptionRecord->ExceptionInformation[1]; + if ( os::is_memory_serialize_page(thread, addr) ) { + // Block current thread until the memory serialize page permission restored. + os::block_on_serialize_page_trap(); + return EXCEPTION_CONTINUE_EXECUTION; + } + } + + + if (t != NULL && t->is_Java_thread()) { + JavaThread* thread = (JavaThread*) t; + bool in_java = thread->thread_state() == _thread_in_Java; + + // Handle potential stack overflows up front. + if (exception_code == EXCEPTION_STACK_OVERFLOW) { + if (os::uses_stack_guard_pages()) { +#ifdef _M_IA64 + // + // If it's a legal stack address continue, Windows will map it in. + // + PEXCEPTION_RECORD exceptionRecord = exceptionInfo->ExceptionRecord; + address addr = (address) exceptionRecord->ExceptionInformation[1]; + if (addr > thread->stack_yellow_zone_base() && addr < thread->stack_base() ) + return EXCEPTION_CONTINUE_EXECUTION; + + // The register save area is the same size as the memory stack + // and starts at the page just above the start of the memory stack. + // If we get a fault in this area, we've run out of register + // stack. If we are in java, try throwing a stack overflow exception. + if (addr > thread->stack_base() && + addr <= (thread->stack_base()+thread->stack_size()) ) { + char buf[256]; + jio_snprintf(buf, sizeof(buf), + "Register stack overflow, addr:%p, stack_base:%p\n", + addr, thread->stack_base() ); + tty->print_raw_cr(buf); + // If not in java code, return and hope for the best. + return in_java ? Handle_Exception(exceptionInfo, + SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW)) + : EXCEPTION_CONTINUE_EXECUTION; + } +#endif + if (thread->stack_yellow_zone_enabled()) { + // Yellow zone violation. The o/s has unprotected the first yellow + // zone page for us. Note: must call disable_stack_yellow_zone to + // update the enabled status, even if the zone contains only one page. + thread->disable_stack_yellow_zone(); + // If not in java code, return and hope for the best. + return in_java ? Handle_Exception(exceptionInfo, + SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW)) + : EXCEPTION_CONTINUE_EXECUTION; + } else { + // Fatal red zone violation. + thread->disable_stack_red_zone(); + tty->print_raw_cr("An unrecoverable stack overflow has occurred."); + report_error(t, exception_code, pc, exceptionInfo->ExceptionRecord, + exceptionInfo->ContextRecord); + return EXCEPTION_CONTINUE_SEARCH; + } + } else if (in_java) { + // JVM-managed guard pages cannot be used on win95/98. The o/s provides + // a one-time-only guard page, which it has released to us. The next + // stack overflow on this thread will result in an ACCESS_VIOLATION. + return Handle_Exception(exceptionInfo, + SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW)); + } else { + // Can only return and hope for the best. Further stack growth will + // result in an ACCESS_VIOLATION. + return EXCEPTION_CONTINUE_EXECUTION; + } + } else if (exception_code == EXCEPTION_ACCESS_VIOLATION) { + // Either stack overflow or null pointer exception. + if (in_java) { + PEXCEPTION_RECORD exceptionRecord = exceptionInfo->ExceptionRecord; + address addr = (address) exceptionRecord->ExceptionInformation[1]; + address stack_end = thread->stack_base() - thread->stack_size(); + if (addr < stack_end && addr >= stack_end - os::vm_page_size()) { + // Stack overflow. + assert(!os::uses_stack_guard_pages(), + "should be caught by red zone code above."); + return Handle_Exception(exceptionInfo, + SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW)); + } + // + // Check for safepoint polling and implicit null + // We only expect null pointers in the stubs (vtable) + // the rest are checked explicitly now. + // + CodeBlob* cb = CodeCache::find_blob(pc); + if (cb != NULL) { + if (os::is_poll_address(addr)) { + address stub = SharedRuntime::get_poll_stub(pc); + return Handle_Exception(exceptionInfo, stub); + } + } + { +#ifdef _WIN64 + // + // If it's a legal stack address map the entire region in + // + PEXCEPTION_RECORD exceptionRecord = exceptionInfo->ExceptionRecord; + address addr = (address) exceptionRecord->ExceptionInformation[1]; + if (addr > thread->stack_yellow_zone_base() && addr < thread->stack_base() ) { + addr = (address)((uintptr_t)addr & + (~((uintptr_t)os::vm_page_size() - (uintptr_t)1))); + os::commit_memory( (char *)addr, thread->stack_base() - addr ); + return EXCEPTION_CONTINUE_EXECUTION; + } + else +#endif + { + // Null pointer exception. +#ifdef _M_IA64 + // We catch register stack overflows in compiled code by doing + // an explicit compare and executing a st8(G0, G0) if the + // BSP enters into our guard area. We test for the overflow + // condition and fall into the normal null pointer exception + // code if BSP hasn't overflowed. + if ( in_java ) { + if(thread->register_stack_overflow()) { + assert((address)exceptionInfo->ContextRecord->IntS3 == + thread->register_stack_limit(), + "GR7 doesn't contain register_stack_limit"); + // Disable the yellow zone which sets the state that + // we've got a stack overflow problem. + if (thread->stack_yellow_zone_enabled()) { + thread->disable_stack_yellow_zone(); + } + // Give us some room to process the exception + thread->disable_register_stack_guard(); + // Update GR7 with the new limit so we can continue running + // compiled code. + exceptionInfo->ContextRecord->IntS3 = + (ULONGLONG)thread->register_stack_limit(); + return Handle_Exception(exceptionInfo, + SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW)); + } else { + // + // Check for implicit null + // We only expect null pointers in the stubs (vtable) + // the rest are checked explicitly now. + // + CodeBlob* cb = CodeCache::find_blob(pc); + if (cb != NULL) { + if (VtableStubs::stub_containing(pc) != NULL) { + if (((uintptr_t)addr) < os::vm_page_size() ) { + // an access to the first page of VM--assume it is a null pointer + return Handle_Exception(exceptionInfo, + SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL)); + } + } + } + } + } // in_java + + // IA64 doesn't use implicit null checking yet. So we shouldn't + // get here. + tty->print_raw_cr("Access violation, possible null pointer exception"); + report_error(t, exception_code, pc, exceptionInfo->ExceptionRecord, + exceptionInfo->ContextRecord); + return EXCEPTION_CONTINUE_SEARCH; +#else /* !IA64 */ + + // Windows 98 reports faulting addresses incorrectly + if (!MacroAssembler::needs_explicit_null_check((intptr_t)addr) || + !os::win32::is_nt()) { + return Handle_Exception(exceptionInfo, + SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL)); + } + report_error(t, exception_code, pc, exceptionInfo->ExceptionRecord, + exceptionInfo->ContextRecord); + return EXCEPTION_CONTINUE_SEARCH; +#endif + } + } + } + +#ifdef _WIN64 + // Special care for fast JNI field accessors. + // jni_fast_GetField can trap at certain pc's if a GC kicks + // in and the heap gets shrunk before the field access. + if (exception_code == EXCEPTION_ACCESS_VIOLATION) { + address addr = JNI_FastGetField::find_slowcase_pc(pc); + if (addr != (address)-1) { + return Handle_Exception(exceptionInfo, addr); + } + } +#endif + +#ifdef _WIN64 + // Windows will sometimes generate an access violation + // when we call malloc. Since we use VectoredExceptions + // on 64 bit platforms, we see this exception. We must + // pass this exception on so Windows can recover. + // We check to see if the pc of the fault is in NTDLL.DLL + // if so, we pass control on to Windows for handling. + if (UseVectoredExceptions && _addr_in_ntdll(pc)) return EXCEPTION_CONTINUE_SEARCH; +#endif + + // Stack overflow or null pointer exception in native code. + report_error(t, exception_code, pc, exceptionInfo->ExceptionRecord, + exceptionInfo->ContextRecord); + return EXCEPTION_CONTINUE_SEARCH; + } + + if (in_java) { + switch (exception_code) { + case EXCEPTION_INT_DIVIDE_BY_ZERO: + return Handle_Exception(exceptionInfo, SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_DIVIDE_BY_ZERO)); + + case EXCEPTION_INT_OVERFLOW: + return Handle_IDiv_Exception(exceptionInfo); + + } // switch + } +#ifndef _WIN64 + if ((thread->thread_state() == _thread_in_Java) || + (thread->thread_state() == _thread_in_native) ) + { + LONG result=Handle_FLT_Exception(exceptionInfo); + if (result==EXCEPTION_CONTINUE_EXECUTION) return result; + } +#endif //_WIN64 + } + + if (exception_code != EXCEPTION_BREAKPOINT) { +#ifndef _WIN64 + report_error(t, exception_code, pc, exceptionInfo->ExceptionRecord, + exceptionInfo->ContextRecord); +#else + // Itanium Windows uses a VectoredExceptionHandler + // Which means that C++ programatic exception handlers (try/except) + // will get here. Continue the search for the right except block if + // the exception code is not a fatal code. + switch ( exception_code ) { + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_STACK_OVERFLOW: + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_ILLEGAL_INSTRUCTION_2: + case EXCEPTION_INT_OVERFLOW: + case EXCEPTION_INT_DIVIDE_BY_ZERO: + { report_error(t, exception_code, pc, exceptionInfo->ExceptionRecord, + exceptionInfo->ContextRecord); + } + break; + default: + break; + } +#endif + } + return EXCEPTION_CONTINUE_SEARCH; +} + +#ifndef _WIN64 +// Special care for fast JNI accessors. +// jni_fast_GetField can trap at certain pc's if a GC kicks in and +// the heap gets shrunk before the field access. +// Need to install our own structured exception handler since native code may +// install its own. +LONG WINAPI fastJNIAccessorExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { + DWORD exception_code = exceptionInfo->ExceptionRecord->ExceptionCode; + if (exception_code == EXCEPTION_ACCESS_VIOLATION) { + address pc = (address) exceptionInfo->ContextRecord->Eip; + address addr = JNI_FastGetField::find_slowcase_pc(pc); + if (addr != (address)-1) { + return Handle_Exception(exceptionInfo, addr); + } + } + return EXCEPTION_CONTINUE_SEARCH; +} + +#define DEFINE_FAST_GETFIELD(Return,Fieldname,Result) \ +Return JNICALL jni_fast_Get##Result##Field_wrapper(JNIEnv *env, jobject obj, jfieldID fieldID) { \ + __try { \ + return (*JNI_FastGetField::jni_fast_Get##Result##Field_fp)(env, obj, fieldID); \ + } __except(fastJNIAccessorExceptionFilter((_EXCEPTION_POINTERS*)_exception_info())) { \ + } \ + return 0; \ +} + +DEFINE_FAST_GETFIELD(jboolean, bool, Boolean) +DEFINE_FAST_GETFIELD(jbyte, byte, Byte) +DEFINE_FAST_GETFIELD(jchar, char, Char) +DEFINE_FAST_GETFIELD(jshort, short, Short) +DEFINE_FAST_GETFIELD(jint, int, Int) +DEFINE_FAST_GETFIELD(jlong, long, Long) +DEFINE_FAST_GETFIELD(jfloat, float, Float) +DEFINE_FAST_GETFIELD(jdouble, double, Double) + +address os::win32::fast_jni_accessor_wrapper(BasicType type) { + switch (type) { + case T_BOOLEAN: return (address)jni_fast_GetBooleanField_wrapper; + case T_BYTE: return (address)jni_fast_GetByteField_wrapper; + case T_CHAR: return (address)jni_fast_GetCharField_wrapper; + case T_SHORT: return (address)jni_fast_GetShortField_wrapper; + case T_INT: return (address)jni_fast_GetIntField_wrapper; + case T_LONG: return (address)jni_fast_GetLongField_wrapper; + case T_FLOAT: return (address)jni_fast_GetFloatField_wrapper; + case T_DOUBLE: return (address)jni_fast_GetDoubleField_wrapper; + default: ShouldNotReachHere(); + } + return (address)-1; +} +#endif + +// Virtual Memory + +int os::vm_page_size() { return os::win32::vm_page_size(); } +int os::vm_allocation_granularity() { + return os::win32::vm_allocation_granularity(); +} + +// Windows large page support is available on Windows 2003. In order to use +// large page memory, the administrator must first assign additional privilege +// to the user: +// + select Control Panel -> Administrative Tools -> Local Security Policy +// + select Local Policies -> User Rights Assignment +// + double click "Lock pages in memory", add users and/or groups +// + reboot +// Note the above steps are needed for administrator as well, as administrators +// by default do not have the privilege to lock pages in memory. +// +// Note about Windows 2003: although the API supports committing large page +// memory on a page-by-page basis and VirtualAlloc() returns success under this +// scenario, I found through experiment it only uses large page if the entire +// memory region is reserved and committed in a single VirtualAlloc() call. +// This makes Windows large page support more or less like Solaris ISM, in +// that the entire heap must be committed upfront. This probably will change +// in the future, if so the code below needs to be revisited. + +#ifndef MEM_LARGE_PAGES +#define MEM_LARGE_PAGES 0x20000000 +#endif + +// GetLargePageMinimum is only available on Windows 2003. The other functions +// are available on NT but not on Windows 98/Me. We have to resolve them at +// runtime. +typedef SIZE_T (WINAPI *GetLargePageMinimum_func_type) (void); +typedef BOOL (WINAPI *AdjustTokenPrivileges_func_type) + (HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); +typedef BOOL (WINAPI *OpenProcessToken_func_type) (HANDLE, DWORD, PHANDLE); +typedef BOOL (WINAPI *LookupPrivilegeValue_func_type) (LPCTSTR, LPCTSTR, PLUID); + +static GetLargePageMinimum_func_type _GetLargePageMinimum; +static AdjustTokenPrivileges_func_type _AdjustTokenPrivileges; +static OpenProcessToken_func_type _OpenProcessToken; +static LookupPrivilegeValue_func_type _LookupPrivilegeValue; + +static HINSTANCE _kernel32; +static HINSTANCE _advapi32; +static HANDLE _hProcess; +static HANDLE _hToken; + +static size_t _large_page_size = 0; + +static bool resolve_functions_for_large_page_init() { + _kernel32 = LoadLibrary("kernel32.dll"); + if (_kernel32 == NULL) return false; + + _GetLargePageMinimum = CAST_TO_FN_PTR(GetLargePageMinimum_func_type, + GetProcAddress(_kernel32, "GetLargePageMinimum")); + if (_GetLargePageMinimum == NULL) return false; + + _advapi32 = LoadLibrary("advapi32.dll"); + if (_advapi32 == NULL) return false; + + _AdjustTokenPrivileges = CAST_TO_FN_PTR(AdjustTokenPrivileges_func_type, + GetProcAddress(_advapi32, "AdjustTokenPrivileges")); + _OpenProcessToken = CAST_TO_FN_PTR(OpenProcessToken_func_type, + GetProcAddress(_advapi32, "OpenProcessToken")); + _LookupPrivilegeValue = CAST_TO_FN_PTR(LookupPrivilegeValue_func_type, + GetProcAddress(_advapi32, "LookupPrivilegeValueA")); + return _AdjustTokenPrivileges != NULL && + _OpenProcessToken != NULL && + _LookupPrivilegeValue != NULL; +} + +static bool request_lock_memory_privilege() { + _hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, + os::current_process_id()); + + LUID luid; + if (_hProcess != NULL && + _OpenProcessToken(_hProcess, TOKEN_ADJUST_PRIVILEGES, &_hToken) && + _LookupPrivilegeValue(NULL, "SeLockMemoryPrivilege", &luid)) { + + TOKEN_PRIVILEGES tp; + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + // AdjustTokenPrivileges() may return TRUE even when it couldn't change the + // privilege. Check GetLastError() too. See MSDN document. + if (_AdjustTokenPrivileges(_hToken, false, &tp, sizeof(tp), NULL, NULL) && + (GetLastError() == ERROR_SUCCESS)) { + return true; + } + } + + return false; +} + +static void cleanup_after_large_page_init() { + _GetLargePageMinimum = NULL; + _AdjustTokenPrivileges = NULL; + _OpenProcessToken = NULL; + _LookupPrivilegeValue = NULL; + if (_kernel32) FreeLibrary(_kernel32); + _kernel32 = NULL; + if (_advapi32) FreeLibrary(_advapi32); + _advapi32 = NULL; + if (_hProcess) CloseHandle(_hProcess); + _hProcess = NULL; + if (_hToken) CloseHandle(_hToken); + _hToken = NULL; +} + +bool os::large_page_init() { + if (!UseLargePages) return false; + + // print a warning if any large page related flag is specified on command line + bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) || + !FLAG_IS_DEFAULT(LargePageSizeInBytes); + bool success = false; + +# define WARN(msg) if (warn_on_failure) { warning(msg); } + if (resolve_functions_for_large_page_init()) { + if (request_lock_memory_privilege()) { + size_t s = _GetLargePageMinimum(); + if (s) { +#if defined(IA32) || defined(AMD64) + if (s > 4*M || LargePageSizeInBytes > 4*M) { + WARN("JVM cannot use large pages bigger than 4mb."); + } else { +#endif + if (LargePageSizeInBytes && LargePageSizeInBytes % s == 0) { + _large_page_size = LargePageSizeInBytes; + } else { + _large_page_size = s; + } + success = true; +#if defined(IA32) || defined(AMD64) + } +#endif + } else { + WARN("Large page is not supported by the processor."); + } + } else { + WARN("JVM cannot use large page memory because it does not have enough privilege to lock pages in memory."); + } + } else { + WARN("Large page is not supported by the operating system."); + } +#undef WARN + + const size_t default_page_size = (size_t) vm_page_size(); + if (success && _large_page_size > default_page_size) { + _page_sizes[0] = _large_page_size; + _page_sizes[1] = default_page_size; + _page_sizes[2] = 0; + } + + cleanup_after_large_page_init(); + return success; +} + +// On win32, one cannot release just a part of reserved memory, it's an +// all or nothing deal. When we split a reservation, we must break the +// reservation into two reservations. +void os::split_reserved_memory(char *base, size_t size, size_t split, + bool realloc) { + if (size > 0) { + release_memory(base, size); + if (realloc) { + reserve_memory(split, base); + } + if (size != split) { + reserve_memory(size - split, base + split); + } + } +} + +char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint) { + assert((size_t)addr % os::vm_allocation_granularity() == 0, + "reserve alignment"); + assert(bytes % os::vm_allocation_granularity() == 0, "reserve block size"); + char* res = (char*)VirtualAlloc(addr, bytes, MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + assert(res == NULL || addr == NULL || addr == res, + "Unexpected address from reserve."); + return res; +} + +// Reserve memory at an arbitrary address, only if that area is +// available (and not reserved for something else). +char* os::attempt_reserve_memory_at(size_t bytes, char* requested_addr) { + // Windows os::reserve_memory() fails of the requested address range is + // not avilable. + return reserve_memory(bytes, requested_addr); +} + +size_t os::large_page_size() { + return _large_page_size; +} + +bool os::can_commit_large_page_memory() { + // Windows only uses large page memory when the entire region is reserved + // and committed in a single VirtualAlloc() call. This may change in the + // future, but with Windows 2003 it's not possible to commit on demand. + return false; +} + +char* os::reserve_memory_special(size_t bytes) { + DWORD flag = MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES; + char * res = (char *)VirtualAlloc(NULL, bytes, flag, PAGE_READWRITE); + return res; +} + +bool os::release_memory_special(char* base, size_t bytes) { + return release_memory(base, bytes); +} + +void os::print_statistics() { +} + +bool os::commit_memory(char* addr, size_t bytes) { + if (bytes == 0) { + // Don't bother the OS with noops. + return true; + } + assert((size_t) addr % os::vm_page_size() == 0, "commit on page boundaries"); + assert(bytes % os::vm_page_size() == 0, "commit in page-sized chunks"); + // Don't attempt to print anything if the OS call fails. We're + // probably low on resources, so the print itself may cause crashes. + return VirtualAlloc(addr, bytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE) != NULL; +} + +bool os::commit_memory(char* addr, size_t size, size_t alignment_hint) { + return commit_memory(addr, size); +} + +bool os::uncommit_memory(char* addr, size_t bytes) { + if (bytes == 0) { + // Don't bother the OS with noops. + return true; + } + assert((size_t) addr % os::vm_page_size() == 0, "uncommit on page boundaries"); + assert(bytes % os::vm_page_size() == 0, "uncommit in page-sized chunks"); + return VirtualFree(addr, bytes, MEM_DECOMMIT) != 0; +} + +bool os::release_memory(char* addr, size_t bytes) { + return VirtualFree(addr, 0, MEM_RELEASE) != 0; +} + +bool os::protect_memory(char* addr, size_t bytes) { + DWORD old_status; + return VirtualProtect(addr, bytes, PAGE_READONLY, &old_status) != 0; +} + +bool os::guard_memory(char* addr, size_t bytes) { + DWORD old_status; + return VirtualProtect(addr, bytes, PAGE_EXECUTE_READWRITE | PAGE_GUARD, &old_status) != 0; +} + +bool os::unguard_memory(char* addr, size_t bytes) { + DWORD old_status; + return VirtualProtect(addr, bytes, PAGE_EXECUTE_READWRITE, &old_status) != 0; +} + +void os::realign_memory(char *addr, size_t bytes, size_t alignment_hint) { } +void os::free_memory(char *addr, size_t bytes) { } +void os::numa_make_global(char *addr, size_t bytes) { } +void os::numa_make_local(char *addr, size_t bytes) { } +bool os::numa_topology_changed() { return false; } +size_t os::numa_get_groups_num() { return 1; } +int os::numa_get_group_id() { return 0; } +size_t os::numa_get_leaf_groups(int *ids, size_t size) { + if (size > 0) { + ids[0] = 0; + return 1; + } + return 0; +} + +bool os::get_page_info(char *start, page_info* info) { + return false; +} + +char *os::scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found) { + return end; +} + +char* os::non_memory_address_word() { + // Must never look like an address returned by reserve_memory, + // even in its subfields (as defined by the CPU immediate fields, + // if the CPU splits constants across multiple instructions). + return (char*)-1; +} + +#define MAX_ERROR_COUNT 100 +#define SYS_THREAD_ERROR 0xffffffffUL + +void os::pd_start_thread(Thread* thread) { + DWORD ret = ResumeThread(thread->osthread()->thread_handle()); + // Returns previous suspend state: + // 0: Thread was not suspended + // 1: Thread is running now + // >1: Thread is still suspended. + assert(ret != SYS_THREAD_ERROR, "StartThread failed"); // should propagate back +} + +size_t os::read(int fd, void *buf, unsigned int nBytes) { + return ::read(fd, buf, nBytes); +} + +class HighResolutionInterval { + // The default timer resolution seems to be 10 milliseconds. + // (Where is this written down?) + // If someone wants to sleep for only a fraction of the default, + // then we set the timer resolution down to 1 millisecond for + // the duration of their interval. + // We carefully set the resolution back, since otherwise we + // seem to incur an overhead (3%?) that we don't need. + // CONSIDER: if ms is small, say 3, then we should run with a high resolution time. + // Buf if ms is large, say 500, or 503, we should avoid the call to timeBeginPeriod(). + // Alternatively, we could compute the relative error (503/500 = .6%) and only use + // timeBeginPeriod() if the relative error exceeded some threshold. + // timeBeginPeriod() has been linked to problems with clock drift on win32 systems and + // to decreased efficiency related to increased timer "tick" rates. We want to minimize + // (a) calls to timeBeginPeriod() and timeEndPeriod() and (b) time spent with high + // resolution timers running. +private: + jlong resolution; +public: + HighResolutionInterval(jlong ms) { + resolution = ms % 10L; + if (resolution != 0) { + MMRESULT result = timeBeginPeriod(1L); + } + } + ~HighResolutionInterval() { + if (resolution != 0) { + MMRESULT result = timeEndPeriod(1L); + } + resolution = 0L; + } +}; + +int os::sleep(Thread* thread, jlong ms, bool interruptable) { + jlong limit = (jlong) MAXDWORD; + + while(ms > limit) { + int res; + if ((res = sleep(thread, limit, interruptable)) != OS_TIMEOUT) + return res; + ms -= limit; + } + + assert(thread == Thread::current(), "thread consistency check"); + OSThread* osthread = thread->osthread(); + OSThreadWaitState osts(osthread, false /* not Object.wait() */); + int result; + if (interruptable) { + assert(thread->is_Java_thread(), "must be java thread"); + JavaThread *jt = (JavaThread *) thread; + ThreadBlockInVM tbivm(jt); + + jt->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + HANDLE events[1]; + events[0] = osthread->interrupt_event(); + HighResolutionInterval *phri=NULL; + if(!ForceTimeHighResolution) + phri = new HighResolutionInterval( ms ); + if (WaitForMultipleObjects(1, events, FALSE, (DWORD)ms) == WAIT_TIMEOUT) { + result = OS_TIMEOUT; + } else { + ResetEvent(osthread->interrupt_event()); + osthread->set_interrupted(false); + result = OS_INTRPT; + } + delete phri; //if it is NULL, harmless + + // were we externally suspended while we were waiting? + jt->check_and_wait_while_suspended(); + } else { + assert(!thread->is_Java_thread(), "must not be java thread"); + Sleep((long) ms); + result = OS_TIMEOUT; + } + return result; +} + +// Sleep forever; naked call to OS-specific sleep; use with CAUTION +void os::infinite_sleep() { + while (true) { // sleep forever ... + Sleep(100000); // ... 100 seconds at a time + } +} + +typedef BOOL (WINAPI * STTSignature)(void) ; + +os::YieldResult os::NakedYield() { + // Use either SwitchToThread() or Sleep(0) + // Consider passing back the return value from SwitchToThread(). + // We use GetProcAddress() as ancient Win9X versions of windows doen't support SwitchToThread. + // In that case we revert to Sleep(0). + static volatile STTSignature stt = (STTSignature) 1 ; + + if (stt == ((STTSignature) 1)) { + stt = (STTSignature) ::GetProcAddress (LoadLibrary ("Kernel32.dll"), "SwitchToThread") ; + // It's OK if threads race during initialization as the operation above is idempotent. + } + if (stt != NULL) { + return (*stt)() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ; + } else { + Sleep (0) ; + } + return os::YIELD_UNKNOWN ; +} + +void os::yield() { os::NakedYield(); } + +void os::yield_all(int attempts) { + // Yields to all threads, including threads with lower priorities + Sleep(1); +} + +// Win32 only gives you access to seven real priorities at a time, +// so we compress Java's ten down to seven. It would be better +// if we dynamically adjusted relative priorities. + +int os::java_to_os_priority[MaxPriority + 1] = { + THREAD_PRIORITY_IDLE, // 0 Entry should never be used + THREAD_PRIORITY_LOWEST, // 1 MinPriority + THREAD_PRIORITY_LOWEST, // 2 + THREAD_PRIORITY_BELOW_NORMAL, // 3 + THREAD_PRIORITY_BELOW_NORMAL, // 4 + THREAD_PRIORITY_NORMAL, // 5 NormPriority + THREAD_PRIORITY_NORMAL, // 6 + THREAD_PRIORITY_ABOVE_NORMAL, // 7 + THREAD_PRIORITY_ABOVE_NORMAL, // 8 + THREAD_PRIORITY_HIGHEST, // 9 NearMaxPriority + THREAD_PRIORITY_HIGHEST // 10 MaxPriority +}; + +int prio_policy1[MaxPriority + 1] = { + THREAD_PRIORITY_IDLE, // 0 Entry should never be used + THREAD_PRIORITY_LOWEST, // 1 MinPriority + THREAD_PRIORITY_LOWEST, // 2 + THREAD_PRIORITY_BELOW_NORMAL, // 3 + THREAD_PRIORITY_BELOW_NORMAL, // 4 + THREAD_PRIORITY_NORMAL, // 5 NormPriority + THREAD_PRIORITY_ABOVE_NORMAL, // 6 + THREAD_PRIORITY_ABOVE_NORMAL, // 7 + THREAD_PRIORITY_HIGHEST, // 8 + THREAD_PRIORITY_HIGHEST, // 9 NearMaxPriority + THREAD_PRIORITY_TIME_CRITICAL // 10 MaxPriority +}; + +static int prio_init() { + // If ThreadPriorityPolicy is 1, switch tables + if (ThreadPriorityPolicy == 1) { + int i; + for (i = 0; i < MaxPriority + 1; i++) { + os::java_to_os_priority[i] = prio_policy1[i]; + } + } + return 0; +} + +OSReturn os::set_native_priority(Thread* thread, int priority) { + if (!UseThreadPriorities) return OS_OK; + bool ret = SetThreadPriority(thread->osthread()->thread_handle(), priority) != 0; + return ret ? OS_OK : OS_ERR; +} + +OSReturn os::get_native_priority(const Thread* const thread, int* priority_ptr) { + if ( !UseThreadPriorities ) { + *priority_ptr = java_to_os_priority[NormPriority]; + return OS_OK; + } + int os_prio = GetThreadPriority(thread->osthread()->thread_handle()); + if (os_prio == THREAD_PRIORITY_ERROR_RETURN) { + assert(false, "GetThreadPriority failed"); + return OS_ERR; + } + *priority_ptr = os_prio; + return OS_OK; +} + + +// Hint to the underlying OS that a task switch would not be good. +// Void return because it's a hint and can fail. +void os::hint_no_preempt() {} + +void os::interrupt(Thread* thread) { + assert(!thread->is_Java_thread() || Thread::current() == thread || Threads_lock->owned_by_self(), + "possibility of dangling Thread pointer"); + + OSThread* osthread = thread->osthread(); + osthread->set_interrupted(true); + // More than one thread can get here with the same value of osthread, + // resulting in multiple notifications. We do, however, want the store + // to interrupted() to be visible to other threads before we post + // the interrupt event. + OrderAccess::release(); + SetEvent(osthread->interrupt_event()); + // For JSR166: unpark after setting status + if (thread->is_Java_thread()) + ((JavaThread*)thread)->parker()->unpark(); + + ParkEvent * ev = thread->_ParkEvent ; + if (ev != NULL) ev->unpark() ; + +} + + +bool os::is_interrupted(Thread* thread, bool clear_interrupted) { + assert(!thread->is_Java_thread() || Thread::current() == thread || Threads_lock->owned_by_self(), + "possibility of dangling Thread pointer"); + + OSThread* osthread = thread->osthread(); + bool interrupted; + interrupted = osthread->interrupted(); + if (clear_interrupted == true) { + osthread->set_interrupted(false); + ResetEvent(osthread->interrupt_event()); + } // Otherwise leave the interrupted state alone + + return interrupted; +} + +// Get's a pc (hint) for a running thread. Currently used only for profiling. +ExtendedPC os::get_thread_pc(Thread* thread) { + CONTEXT context; + context.ContextFlags = CONTEXT_CONTROL; + HANDLE handle = thread->osthread()->thread_handle(); +#ifdef _M_IA64 + assert(0, "Fix get_thread_pc"); + return ExtendedPC(NULL); +#else + if (GetThreadContext(handle, &context)) { +#ifdef _M_AMD64 + return ExtendedPC((address) context.Rip); +#else + return ExtendedPC((address) context.Eip); +#endif + } else { + return ExtendedPC(NULL); + } +#endif +} + +// GetCurrentThreadId() returns DWORD +intx os::current_thread_id() { return GetCurrentThreadId(); } + +static int _initial_pid = 0; + +int os::current_process_id() +{ + return (_initial_pid ? _initial_pid : _getpid()); +} + +int os::win32::_vm_page_size = 0; +int os::win32::_vm_allocation_granularity = 0; +int os::win32::_processor_type = 0; +// Processor level is not available on non-NT systems, use vm_version instead +int os::win32::_processor_level = 0; +julong os::win32::_physical_memory = 0; +size_t os::win32::_default_stack_size = 0; + + intx os::win32::_os_thread_limit = 0; +volatile intx os::win32::_os_thread_count = 0; + +bool os::win32::_is_nt = false; + + +void os::win32::initialize_system_info() { + SYSTEM_INFO si; + GetSystemInfo(&si); + _vm_page_size = si.dwPageSize; + _vm_allocation_granularity = si.dwAllocationGranularity; + _processor_type = si.dwProcessorType; + _processor_level = si.wProcessorLevel; + _processor_count = si.dwNumberOfProcessors; + + MEMORYSTATUS ms; + // also returns dwAvailPhys (free physical memory bytes), dwTotalVirtual, dwAvailVirtual, + // dwMemoryLoad (% of memory in use) + GlobalMemoryStatus(&ms); + _physical_memory = ms.dwTotalPhys; + + OSVERSIONINFO oi; + oi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&oi); + switch(oi.dwPlatformId) { + case VER_PLATFORM_WIN32_WINDOWS: _is_nt = false; break; + case VER_PLATFORM_WIN32_NT: _is_nt = true; break; + default: fatal("Unknown platform"); + } + + _default_stack_size = os::current_stack_size(); + assert(_default_stack_size > (size_t) _vm_page_size, "invalid stack size"); + assert((_default_stack_size & (_vm_page_size - 1)) == 0, + "stack size not a multiple of page size"); + + initialize_performance_counter(); + + // Win95/Win98 scheduler bug work-around. The Win95/98 scheduler is + // known to deadlock the system, if the VM issues to thread operations with + // a too high frequency, e.g., such as changing the priorities. + // The 6000 seems to work well - no deadlocks has been notices on the test + // programs that we have seen experience this problem. + if (!os::win32::is_nt()) { + StarvationMonitorInterval = 6000; + } +} + + +void os::win32::setmode_streams() { + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + _setmode(_fileno(stderr), _O_BINARY); +} + + +int os::message_box(const char* title, const char* message) { + int result = MessageBox(NULL, message, title, + MB_YESNO | MB_ICONERROR | MB_SYSTEMMODAL | MB_DEFAULT_DESKTOP_ONLY); + return result == IDYES; +} + +int os::allocate_thread_local_storage() { + return TlsAlloc(); +} + + +void os::free_thread_local_storage(int index) { + TlsFree(index); +} + + +void os::thread_local_storage_at_put(int index, void* value) { + TlsSetValue(index, value); + assert(thread_local_storage_at(index) == value, "Just checking"); +} + + +void* os::thread_local_storage_at(int index) { + return TlsGetValue(index); +} + + +#ifndef PRODUCT +#ifndef _WIN64 +// Helpers to check whether NX protection is enabled +int nx_exception_filter(_EXCEPTION_POINTERS *pex) { + if (pex->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && + pex->ExceptionRecord->NumberParameters > 0 && + pex->ExceptionRecord->ExceptionInformation[0] == + EXCEPTION_INFO_EXEC_VIOLATION) { + return EXCEPTION_EXECUTE_HANDLER; + } + return EXCEPTION_CONTINUE_SEARCH; +} + +void nx_check_protection() { + // If NX is enabled we'll get an exception calling into code on the stack + char code[] = { (char)0xC3 }; // ret + void *code_ptr = (void *)code; + __try { + __asm call code_ptr + } __except(nx_exception_filter((_EXCEPTION_POINTERS*)_exception_info())) { + tty->print_raw_cr("NX protection detected."); + } +} +#endif // _WIN64 +#endif // PRODUCT + +// this is called _before_ the global arguments have been parsed +void os::init(void) { + _initial_pid = _getpid(); + + init_random(1234567); + + win32::initialize_system_info(); + win32::setmode_streams(); + init_page_sizes((size_t) win32::vm_page_size()); + + // For better scalability on MP systems (must be called after initialize_system_info) +#ifndef PRODUCT + if (is_MP()) { + NoYieldsInMicrolock = true; + } +#endif + // Initialize main_process and main_thread + main_process = GetCurrentProcess(); // Remember main_process is a pseudo handle + if (!DuplicateHandle(main_process, GetCurrentThread(), main_process, + &main_thread, THREAD_ALL_ACCESS, false, 0)) { + fatal("DuplicateHandle failed\n"); + } + main_thread_id = (int) GetCurrentThreadId(); +} + +// To install functions for atexit processing +extern "C" { + static void perfMemory_exit_helper() { + perfMemory_exit(); + } +} + + +// this is called _after_ the global arguments have been parsed +jint os::init_2(void) { + // Allocate a single page and mark it as readable for safepoint polling + address polling_page = (address)VirtualAlloc(NULL, os::vm_page_size(), MEM_RESERVE, PAGE_READONLY); + guarantee( polling_page != NULL, "Reserve Failed for polling page"); + + address return_page = (address)VirtualAlloc(polling_page, os::vm_page_size(), MEM_COMMIT, PAGE_READONLY); + guarantee( return_page != NULL, "Commit Failed for polling page"); + + os::set_polling_page( polling_page ); + +#ifndef PRODUCT + if( Verbose && PrintMiscellaneous ) + tty->print("[SafePoint Polling address: " INTPTR_FORMAT "]\n", (intptr_t)polling_page); +#endif + + if (!UseMembar) { + address mem_serialize_page = (address)VirtualAlloc(NULL, os::vm_page_size(), MEM_RESERVE, PAGE_EXECUTE_READWRITE); + guarantee( mem_serialize_page != NULL, "Reserve Failed for memory serialize page"); + + return_page = (address)VirtualAlloc(mem_serialize_page, os::vm_page_size(), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + guarantee( return_page != NULL, "Commit Failed for memory serialize page"); + + os::set_memory_serialize_page( mem_serialize_page ); + +#ifndef PRODUCT + if(Verbose && PrintMiscellaneous) + tty->print("[Memory Serialize Page address: " INTPTR_FORMAT "]\n", (intptr_t)mem_serialize_page); +#endif +} + + FLAG_SET_DEFAULT(UseLargePages, os::large_page_init()); + + // Setup Windows Exceptions + + // On Itanium systems, Structured Exception Handling does not + // work since stack frames must be walkable by the OS. Since + // much of our code is dynamically generated, and we do not have + // proper unwind .xdata sections, the system simply exits + // rather than delivering the exception. To work around + // this we use VectorExceptions instead. +#ifdef _WIN64 + if (UseVectoredExceptions) { + topLevelVectoredExceptionHandler = AddVectoredExceptionHandler( 1, topLevelExceptionFilter); + } +#endif + + // for debugging float code generation bugs + if (ForceFloatExceptions) { +#ifndef _WIN64 + static long fp_control_word = 0; + __asm { fstcw fp_control_word } + // see Intel PPro Manual, Vol. 2, p 7-16 + const long precision = 0x20; + const long underflow = 0x10; + const long overflow = 0x08; + const long zero_div = 0x04; + const long denorm = 0x02; + const long invalid = 0x01; + fp_control_word |= invalid; + __asm { fldcw fp_control_word } +#endif + } + + // Initialize HPI. + jint hpi_result = hpi::initialize(); + if (hpi_result != JNI_OK) { return hpi_result; } + + // If stack_commit_size is 0, windows will reserve the default size, + // but only commit a small portion of it. + size_t stack_commit_size = round_to(ThreadStackSize*K, os::vm_page_size()); + size_t default_reserve_size = os::win32::default_stack_size(); + size_t actual_reserve_size = stack_commit_size; + if (stack_commit_size < default_reserve_size) { + // If stack_commit_size == 0, we want this too + actual_reserve_size = default_reserve_size; + } + + JavaThread::set_stack_size_at_create(stack_commit_size); + + // Calculate theoretical max. size of Threads to guard gainst artifical + // out-of-memory situations, where all available address-space has been + // reserved by thread stacks. + assert(actual_reserve_size != 0, "Must have a stack"); + + // Calculate the thread limit when we should start doing Virtual Memory + // banging. Currently when the threads will have used all but 200Mb of space. + // + // TODO: consider performing a similar calculation for commit size instead + // as reserve size, since on a 64-bit platform we'll run into that more + // often than running out of virtual memory space. We can use the + // lower value of the two calculations as the os_thread_limit. + size_t max_address_space = ((size_t)1 << (BitsPerOop - 1)) - (200 * K * K); + win32::_os_thread_limit = (intx)(max_address_space / actual_reserve_size); + + // at exit methods are called in the reverse order of their registration. + // there is no limit to the number of functions registered. atexit does + // not set errno. + + if (PerfAllowAtExitRegistration) { + // only register atexit functions if PerfAllowAtExitRegistration is set. + // atexit functions can be delayed until process exit time, which + // can be problematic for embedded VM situations. Embedded VMs should + // call DestroyJavaVM() to assure that VM resources are released. + + // note: perfMemory_exit_helper atexit function may be removed in + // the future if the appropriate cleanup code can be added to the + // VM_Exit VMOperation's doit method. + if (atexit(perfMemory_exit_helper) != 0) { + warning("os::init_2 atexit(perfMemory_exit_helper) failed"); + } + } + + // initialize PSAPI or ToolHelp for fatal error handler + if (win32::is_nt()) _init_psapi(); + else _init_toolhelp(); + +#ifndef _WIN64 + // Print something if NX is enabled (win32 on AMD64) + NOT_PRODUCT(if (PrintMiscellaneous && Verbose) nx_check_protection()); +#endif + + // initialize thread priority policy + prio_init(); + + return JNI_OK; +} + + +// Mark the polling page as unreadable +void os::make_polling_page_unreadable(void) { + DWORD old_status; + if( !VirtualProtect((char *)_polling_page, os::vm_page_size(), PAGE_NOACCESS, &old_status) ) + fatal("Could not disable polling page"); +}; + +// Mark the polling page as readable +void os::make_polling_page_readable(void) { + DWORD old_status; + if( !VirtualProtect((char *)_polling_page, os::vm_page_size(), PAGE_READONLY, &old_status) ) + fatal("Could not enable polling page"); +}; + + +int os::stat(const char *path, struct stat *sbuf) { + char pathbuf[MAX_PATH]; + if (strlen(path) > MAX_PATH - 1) { + errno = ENAMETOOLONG; + return -1; + } + hpi::native_path(strcpy(pathbuf, path)); + int ret = ::stat(pathbuf, sbuf); + if (sbuf != NULL && UseUTCFileTimestamp) { + // Fix for 6539723. st_mtime returned from stat() is dependent on + // the system timezone and so can return different values for the + // same file if/when daylight savings time changes. This adjustment + // makes sure the same timestamp is returned regardless of the TZ. + // + // See: + // http://msdn.microsoft.com/library/ + // default.asp?url=/library/en-us/sysinfo/base/ + // time_zone_information_str.asp + // and + // http://msdn.microsoft.com/library/default.asp?url= + // /library/en-us/sysinfo/base/settimezoneinformation.asp + // + // NOTE: there is a insidious bug here: If the timezone is changed + // after the call to stat() but before 'GetTimeZoneInformation()', then + // the adjustment we do here will be wrong and we'll return the wrong + // value (which will likely end up creating an invalid class data + // archive). Absent a better API for this, or some time zone locking + // mechanism, we'll have to live with this risk. + TIME_ZONE_INFORMATION tz; + DWORD tzid = GetTimeZoneInformation(&tz); + int daylightBias = + (tzid == TIME_ZONE_ID_DAYLIGHT) ? tz.DaylightBias : tz.StandardBias; + sbuf->st_mtime += (tz.Bias + daylightBias) * 60; + } + return ret; +} + + +#define FT2INT64(ft) \ + ((jlong)((jlong)(ft).dwHighDateTime << 32 | (julong)(ft).dwLowDateTime)) + + +// current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) +// are used by JVM M&M and JVMTI to get user+sys or user CPU time +// of a thread. +// +// current_thread_cpu_time() and thread_cpu_time(Thread*) returns +// the fast estimate available on the platform. + +// current_thread_cpu_time() is not optimized for Windows yet +jlong os::current_thread_cpu_time() { + // return user + sys since the cost is the same + return os::thread_cpu_time(Thread::current(), true /* user+sys */); +} + +jlong os::thread_cpu_time(Thread* thread) { + // consistent with what current_thread_cpu_time() returns. + return os::thread_cpu_time(thread, true /* user+sys */); +} + +jlong os::current_thread_cpu_time(bool user_sys_cpu_time) { + return os::thread_cpu_time(Thread::current(), user_sys_cpu_time); +} + +jlong os::thread_cpu_time(Thread* thread, bool user_sys_cpu_time) { + // This code is copy from clasic VM -> hpi::sysThreadCPUTime + // If this function changes, os::is_thread_cpu_time_supported() should too + if (os::win32::is_nt()) { + FILETIME CreationTime; + FILETIME ExitTime; + FILETIME KernelTime; + FILETIME UserTime; + + if ( GetThreadTimes(thread->osthread()->thread_handle(), + &CreationTime, &ExitTime, &KernelTime, &UserTime) == 0) + return -1; + else + if (user_sys_cpu_time) { + return (FT2INT64(UserTime) + FT2INT64(KernelTime)) * 100; + } else { + return FT2INT64(UserTime) * 100; + } + } else { + return (jlong) timeGetTime() * 1000000; + } +} + +void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { + info_ptr->max_value = ALL_64_BITS; // the max value -- all 64 bits + info_ptr->may_skip_backward = false; // GetThreadTimes returns absolute time + info_ptr->may_skip_forward = false; // GetThreadTimes returns absolute time + info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned +} + +void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { + info_ptr->max_value = ALL_64_BITS; // the max value -- all 64 bits + info_ptr->may_skip_backward = false; // GetThreadTimes returns absolute time + info_ptr->may_skip_forward = false; // GetThreadTimes returns absolute time + info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned +} + +bool os::is_thread_cpu_time_supported() { + // see os::thread_cpu_time + if (os::win32::is_nt()) { + FILETIME CreationTime; + FILETIME ExitTime; + FILETIME KernelTime; + FILETIME UserTime; + + if ( GetThreadTimes(GetCurrentThread(), + &CreationTime, &ExitTime, &KernelTime, &UserTime) == 0) + return false; + else + return true; + } else { + return false; + } +} + +// Windows does't provide a loadavg primitive so this is stubbed out for now. +// It does have primitives (PDH API) to get CPU usage and run queue length. +// "\\Processor(_Total)\\% Processor Time", "\\System\\Processor Queue Length" +// If we wanted to implement loadavg on Windows, we have a few options: +// +// a) Query CPU usage and run queue length and "fake" an answer by +// returning the CPU usage if it's under 100%, and the run queue +// length otherwise. It turns out that querying is pretty slow +// on Windows, on the order of 200 microseconds on a fast machine. +// Note that on the Windows the CPU usage value is the % usage +// since the last time the API was called (and the first call +// returns 100%), so we'd have to deal with that as well. +// +// b) Sample the "fake" answer using a sampling thread and store +// the answer in a global variable. The call to loadavg would +// just return the value of the global, avoiding the slow query. +// +// c) Sample a better answer using exponential decay to smooth the +// value. This is basically the algorithm used by UNIX kernels. +// +// Note that sampling thread starvation could affect both (b) and (c). +int os::loadavg(double loadavg[], int nelem) { + return -1; +} + + +// DontYieldALot=false by default: dutifully perform all yields as requested by JVM_Yield() +bool os::dont_yield() { + return DontYieldALot; +} + +// Is a (classpath) directory empty? +bool os::dir_is_empty(const char* path) { + WIN32_FIND_DATA fd; + HANDLE f = FindFirstFile(path, &fd); + if (f == INVALID_HANDLE_VALUE) { + return true; + } + FindClose(f); + return false; +} + +// create binary file, rewriting existing file if required +int os::create_binary_file(const char* path, bool rewrite_existing) { + int oflags = _O_CREAT | _O_WRONLY | _O_BINARY; + if (!rewrite_existing) { + oflags |= _O_EXCL; + } + return ::open(path, oflags, _S_IREAD | _S_IWRITE); +} + +// return current position of file pointer +jlong os::current_file_offset(int fd) { + return (jlong)::_lseeki64(fd, (__int64)0L, SEEK_CUR); +} + +// move file pointer to the specified offset +jlong os::seek_to_file_offset(int fd, jlong offset) { + return (jlong)::_lseeki64(fd, (__int64)offset, SEEK_SET); +} + + +// Map a block of memory. +char* os::map_memory(int fd, const char* file_name, size_t file_offset, + char *addr, size_t bytes, bool read_only, + bool allow_exec) { + HANDLE hFile; + char* base; + + hFile = CreateFile(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == NULL) { + if (PrintMiscellaneous && Verbose) { + DWORD err = GetLastError(); + tty->print_cr("CreateFile() failed: GetLastError->%ld."); + } + return NULL; + } + + if (allow_exec) { + // CreateFileMapping/MapViewOfFileEx can't map executable memory + // unless it comes from a PE image (which the shared archive is not.) + // Even VirtualProtect refuses to give execute access to mapped memory + // that was not previously executable. + // + // Instead, stick the executable region in anonymous memory. Yuck. + // Penalty is that ~4 pages will not be shareable - in the future + // we might consider DLLizing the shared archive with a proper PE + // header so that mapping executable + sharing is possible. + + base = (char*) VirtualAlloc(addr, bytes, MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + if (base == NULL) { + if (PrintMiscellaneous && Verbose) { + DWORD err = GetLastError(); + tty->print_cr("VirtualAlloc() failed: GetLastError->%ld.", err); + } + CloseHandle(hFile); + return NULL; + } + + DWORD bytes_read; + OVERLAPPED overlapped; + overlapped.Offset = (DWORD)file_offset; + overlapped.OffsetHigh = 0; + overlapped.hEvent = NULL; + // ReadFile guarantees that if the return value is true, the requested + // number of bytes were read before returning. + bool res = ReadFile(hFile, base, (DWORD)bytes, &bytes_read, &overlapped) != 0; + if (!res) { + if (PrintMiscellaneous && Verbose) { + DWORD err = GetLastError(); + tty->print_cr("ReadFile() failed: GetLastError->%ld.", err); + } + release_memory(base, bytes); + CloseHandle(hFile); + return NULL; + } + } else { + HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, + NULL /*file_name*/); + if (hMap == NULL) { + if (PrintMiscellaneous && Verbose) { + DWORD err = GetLastError(); + tty->print_cr("CreateFileMapping() failed: GetLastError->%ld."); + } + CloseHandle(hFile); + return NULL; + } + + DWORD access = read_only ? FILE_MAP_READ : FILE_MAP_COPY; + base = (char*)MapViewOfFileEx(hMap, access, 0, (DWORD)file_offset, + (DWORD)bytes, addr); + if (base == NULL) { + if (PrintMiscellaneous && Verbose) { + DWORD err = GetLastError(); + tty->print_cr("MapViewOfFileEx() failed: GetLastError->%ld.", err); + } + CloseHandle(hMap); + CloseHandle(hFile); + return NULL; + } + + if (CloseHandle(hMap) == 0) { + if (PrintMiscellaneous && Verbose) { + DWORD err = GetLastError(); + tty->print_cr("CloseHandle(hMap) failed: GetLastError->%ld.", err); + } + CloseHandle(hFile); + return base; + } + } + + if (allow_exec) { + DWORD old_protect; + DWORD exec_access = read_only ? PAGE_EXECUTE_READ : PAGE_EXECUTE_READWRITE; + bool res = VirtualProtect(base, bytes, exec_access, &old_protect) != 0; + + if (!res) { + if (PrintMiscellaneous && Verbose) { + DWORD err = GetLastError(); + tty->print_cr("VirtualProtect() failed: GetLastError->%ld.", err); + } + // Don't consider this a hard error, on IA32 even if the + // VirtualProtect fails, we should still be able to execute + CloseHandle(hFile); + return base; + } + } + + if (CloseHandle(hFile) == 0) { + if (PrintMiscellaneous && Verbose) { + DWORD err = GetLastError(); + tty->print_cr("CloseHandle(hFile) failed: GetLastError->%ld.", err); + } + return base; + } + + return base; +} + + +// Remap a block of memory. +char* os::remap_memory(int fd, const char* file_name, size_t file_offset, + char *addr, size_t bytes, bool read_only, + bool allow_exec) { + // This OS does not allow existing memory maps to be remapped so we + // have to unmap the memory before we remap it. + if (!os::unmap_memory(addr, bytes)) { + return NULL; + } + + // There is a very small theoretical window between the unmap_memory() + // call above and the map_memory() call below where a thread in native + // code may be able to access an address that is no longer mapped. + + return os::map_memory(fd, file_name, file_offset, addr, bytes, read_only, + allow_exec); +} + + +// Unmap a block of memory. +// Returns true=success, otherwise false. + +bool os::unmap_memory(char* addr, size_t bytes) { + BOOL result = UnmapViewOfFile(addr); + if (result == 0) { + if (PrintMiscellaneous && Verbose) { + DWORD err = GetLastError(); + tty->print_cr("UnmapViewOfFile() failed: GetLastError->%ld.", err); + } + return false; + } + return true; +} + +void os::pause() { + char filename[MAX_PATH]; + if (PauseAtStartupFile && PauseAtStartupFile[0]) { + jio_snprintf(filename, MAX_PATH, PauseAtStartupFile); + } else { + jio_snprintf(filename, MAX_PATH, "./vm.paused.%d", current_process_id()); + } + + int fd = ::open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd != -1) { + struct stat buf; + close(fd); + while (::stat(filename, &buf) == 0) { + Sleep(100); + } + } else { + jio_fprintf(stderr, + "Could not open pause file '%s', continuing immediately.\n", filename); + } +} + +// An Event wraps a win32 "CreateEvent" kernel handle. +// +// We have a number of choices regarding "CreateEvent" win32 handle leakage: +// +// 1: When a thread dies return the Event to the EventFreeList, clear the ParkHandle +// field, and call CloseHandle() on the win32 event handle. Unpark() would +// need to be modified to tolerate finding a NULL (invalid) win32 event handle. +// In addition, an unpark() operation might fetch the handle field, but the +// event could recycle between the fetch and the SetEvent() operation. +// SetEvent() would either fail because the handle was invalid, or inadvertently work, +// as the win32 handle value had been recycled. In an ideal world calling SetEvent() +// on an stale but recycled handle would be harmless, but in practice this might +// confuse other non-Sun code, so it's not a viable approach. +// +// 2: Once a win32 event handle is associated with an Event, it remains associated +// with the Event. The event handle is never closed. This could be construed +// as handle leakage, but only up to the maximum # of threads that have been extant +// at any one time. This shouldn't be an issue, as windows platforms typically +// permit a process to have hundreds of thousands of open handles. +// +// 3: Same as (1), but periodically, at stop-the-world time, rundown the EventFreeList +// and release unused handles. +// +// 4: Add a CRITICAL_SECTION to the Event to protect LD+SetEvent from LD;ST(null);CloseHandle. +// It's not clear, however, that we wouldn't be trading one type of leak for another. +// +// 5. Use an RCU-like mechanism (Read-Copy Update). +// Or perhaps something similar to Maged Michael's "Hazard pointers". +// +// We use (2). +// +// TODO-FIXME: +// 1. Reconcile Doug's JSR166 j.u.c park-unpark with the objectmonitor implementation. +// 2. Consider wrapping the WaitForSingleObject(Ex) calls in SEH try/finally blocks +// to recover from (or at least detect) the dreaded Windows 841176 bug. +// 3. Collapse the interrupt_event, the JSR166 parker event, and the objectmonitor ParkEvent +// into a single win32 CreateEvent() handle. +// +// _Event transitions in park() +// -1 => -1 : illegal +// 1 => 0 : pass - return immediately +// 0 => -1 : block +// +// _Event serves as a restricted-range semaphore : +// -1 : thread is blocked +// 0 : neutral - thread is running or ready +// 1 : signaled - thread is running or ready +// +// Another possible encoding of _Event would be +// with explicit "PARKED" and "SIGNALED" bits. + +int os::PlatformEvent::park (jlong Millis) { + guarantee (_ParkHandle != NULL , "Invariant") ; + guarantee (Millis > 0 , "Invariant") ; + int v ; + + // CONSIDER: defer assigning a CreateEvent() handle to the Event until + // the initial park() operation. + + for (;;) { + v = _Event ; + if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ; + } + guarantee ((v == 0) || (v == 1), "invariant") ; + if (v != 0) return OS_OK ; + + // Do this the hard way by blocking ... + // TODO: consider a brief spin here, gated on the success of recent + // spin attempts by this thread. + // + // We decompose long timeouts into series of shorter timed waits. + // Evidently large timo values passed in WaitForSingleObject() are problematic on some + // versions of Windows. See EventWait() for details. This may be superstition. Or not. + // We trust the WAIT_TIMEOUT indication and don't track the elapsed wait time + // with os::javaTimeNanos(). Furthermore, we assume that spurious returns from + // ::WaitForSingleObject() caused by latent ::setEvent() operations will tend + // to happen early in the wait interval. Specifically, after a spurious wakeup (rv == + // WAIT_OBJECT_0 but _Event is still < 0) we don't bother to recompute Millis to compensate + // for the already waited time. This policy does not admit any new outcomes. + // In the future, however, we might want to track the accumulated wait time and + // adjust Millis accordingly if we encounter a spurious wakeup. + + const int MAXTIMEOUT = 0x10000000 ; + DWORD rv = WAIT_TIMEOUT ; + while (_Event < 0 && Millis > 0) { + DWORD prd = Millis ; // set prd = MAX (Millis, MAXTIMEOUT) + if (Millis > MAXTIMEOUT) { + prd = MAXTIMEOUT ; + } + rv = ::WaitForSingleObject (_ParkHandle, prd) ; + assert (rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT, "WaitForSingleObject failed") ; + if (rv == WAIT_TIMEOUT) { + Millis -= prd ; + } + } + v = _Event ; + _Event = 0 ; + OrderAccess::fence() ; + // If we encounter a nearly simultanous timeout expiry and unpark() + // we return OS_OK indicating we awoke via unpark(). + // Implementor's license -- returning OS_TIMEOUT would be equally valid, however. + return (v >= 0) ? OS_OK : OS_TIMEOUT ; +} + +void os::PlatformEvent::park () { + guarantee (_ParkHandle != NULL, "Invariant") ; + // Invariant: Only the thread associated with the Event/PlatformEvent + // may call park(). + int v ; + for (;;) { + v = _Event ; + if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ; + } + guarantee ((v == 0) || (v == 1), "invariant") ; + if (v != 0) return ; + + // Do this the hard way by blocking ... + // TODO: consider a brief spin here, gated on the success of recent + // spin attempts by this thread. + while (_Event < 0) { + DWORD rv = ::WaitForSingleObject (_ParkHandle, INFINITE) ; + assert (rv == WAIT_OBJECT_0, "WaitForSingleObject failed") ; + } + + // Usually we'll find _Event == 0 at this point, but as + // an optional optimization we clear it, just in case can + // multiple unpark() operations drove _Event up to 1. + _Event = 0 ; + OrderAccess::fence() ; + guarantee (_Event >= 0, "invariant") ; +} + +void os::PlatformEvent::unpark() { + guarantee (_ParkHandle != NULL, "Invariant") ; + int v ; + for (;;) { + v = _Event ; // Increment _Event if it's < 1. + if (v > 0) { + // If it's already signaled just return. + // The LD of _Event could have reordered or be satisfied + // by a read-aside from this processor's write buffer. + // To avoid problems execute a barrier and then + // ratify the value. A degenerate CAS() would also work. + // Viz., CAS (v+0, &_Event, v) == v). + OrderAccess::fence() ; + if (_Event == v) return ; + continue ; + } + if (Atomic::cmpxchg (v+1, &_Event, v) == v) break ; + } + if (v < 0) { + ::SetEvent (_ParkHandle) ; + } +} + + +// JSR166 +// ------------------------------------------------------- + +/* + * The Windows implementation of Park is very straightforward: Basic + * operations on Win32 Events turn out to have the right semantics to + * use them directly. We opportunistically resuse the event inherited + * from Monitor. + */ + + +void Parker::park(bool isAbsolute, jlong time) { + guarantee (_ParkEvent != NULL, "invariant") ; + // First, demultiplex/decode time arguments + if (time < 0) { // don't wait + return; + } + else if (time == 0) { + time = INFINITE; + } + else if (isAbsolute) { + time -= os::javaTimeMillis(); // convert to relative time + if (time <= 0) // already elapsed + return; + } + else { // relative + time /= 1000000; // Must coarsen from nanos to millis + if (time == 0) // Wait for the minimal time unit if zero + time = 1; + } + + JavaThread* thread = (JavaThread*)(Thread::current()); + assert(thread->is_Java_thread(), "Must be JavaThread"); + JavaThread *jt = (JavaThread *)thread; + + // Don't wait if interrupted or already triggered + if (Thread::is_interrupted(thread, false) || + WaitForSingleObject(_ParkEvent, 0) == WAIT_OBJECT_0) { + ResetEvent(_ParkEvent); + return; + } + else { + ThreadBlockInVM tbivm(jt); + OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); + jt->set_suspend_equivalent(); + + WaitForSingleObject(_ParkEvent, time); + ResetEvent(_ParkEvent); + + // If externally suspended while waiting, re-suspend + if (jt->handle_special_suspend_equivalent_condition()) { + jt->java_suspend_self(); + } + } +} + +void Parker::unpark() { + guarantee (_ParkEvent != NULL, "invariant") ; + SetEvent(_ParkEvent); +} + +// Run the specified command in a separate process. Return its exit value, +// or -1 on failure (e.g. can't create a new process). +int os::fork_and_exec(char* cmd) { + STARTUPINFO si; + PROCESS_INFORMATION pi; + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + memset(&pi, 0, sizeof(pi)); + BOOL rslt = CreateProcess(NULL, // executable name - use command line + cmd, // command line + NULL, // process security attribute + NULL, // thread security attribute + TRUE, // inherits system handles + 0, // no creation flags + NULL, // use parent's environment block + NULL, // use parent's starting directory + &si, // (in) startup information + &pi); // (out) process information + + if (rslt) { + // Wait until child process exits. + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD exit_code; + GetExitCodeProcess(pi.hProcess, &exit_code); + + // Close process and thread handles. + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return (int)exit_code; + } else { + return -1; + } +} + +//-------------------------------------------------------------------------------------------------- +// Non-product code + +static int mallocDebugIntervalCounter = 0; +static int mallocDebugCounter = 0; +bool os::check_heap(bool force) { + if (++mallocDebugCounter < MallocVerifyStart && !force) return true; + if (++mallocDebugIntervalCounter >= MallocVerifyInterval || force) { + // Note: HeapValidate executes two hardware breakpoints when it finds something + // wrong; at these points, eax contains the address of the offending block (I think). + // To get to the exlicit error message(s) below, just continue twice. + HANDLE heap = GetProcessHeap(); + { HeapLock(heap); + PROCESS_HEAP_ENTRY phe; + phe.lpData = NULL; + while (HeapWalk(heap, &phe) != 0) { + if ((phe.wFlags & PROCESS_HEAP_ENTRY_BUSY) && + !HeapValidate(heap, 0, phe.lpData)) { + tty->print_cr("C heap has been corrupted (time: %d allocations)", mallocDebugCounter); + tty->print_cr("corrupted block near address %#x, length %d", phe.lpData, phe.cbData); + fatal("corrupted C heap"); + } + } + int err = GetLastError(); + if (err != ERROR_NO_MORE_ITEMS && err != ERROR_CALL_NOT_IMPLEMENTED) { + fatal1("heap walk aborted with error %d", err); + } + HeapUnlock(heap); + } + mallocDebugIntervalCounter = 0; + } + return true; +} + + +#ifndef PRODUCT +bool os::find(address addr) { + // Nothing yet + return false; +} +#endif + +LONG WINAPI os::win32::serialize_fault_filter(struct _EXCEPTION_POINTERS* e) { + DWORD exception_code = e->ExceptionRecord->ExceptionCode; + + if ( exception_code == EXCEPTION_ACCESS_VIOLATION ) { + JavaThread* thread = (JavaThread*)ThreadLocalStorage::get_thread_slow(); + PEXCEPTION_RECORD exceptionRecord = e->ExceptionRecord; + address addr = (address) exceptionRecord->ExceptionInformation[1]; + + if (os::is_memory_serialize_page(thread, addr)) + return EXCEPTION_CONTINUE_EXECUTION; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +static int getLastErrorString(char *buf, size_t len) +{ + long errval; + + if ((errval = GetLastError()) != 0) + { + /* DOS error */ + size_t n = (size_t)FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errval, + 0, + buf, + (DWORD)len, + NULL); + if (n > 3) { + /* Drop final '.', CR, LF */ + if (buf[n - 1] == '\n') n--; + if (buf[n - 1] == '\r') n--; + if (buf[n - 1] == '.') n--; + buf[n] = '\0'; + } + return (int)n; + } + + if (errno != 0) + { + /* C runtime error that has no corresponding DOS error code */ + const char *s = strerror(errno); + size_t n = strlen(s); + if (n >= len) n = len - 1; + strncpy(buf, s, n); + buf[n] = '\0'; + return (int)n; + } + return 0; +} diff --git a/hotspot/src/os/windows/vm/os_windows.hpp b/hotspot/src/os/windows/vm/os_windows.hpp new file mode 100644 index 00000000000..8a66e3c6374 --- /dev/null +++ b/hotspot/src/os/windows/vm/os_windows.hpp @@ -0,0 +1,122 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Win32_OS defines the interface to windows operating systems + +class win32 { + + protected: + static int _vm_page_size; + static int _vm_allocation_granularity; + static int _processor_type; + static int _processor_level; + static julong _physical_memory; + static size_t _default_stack_size; + static bool _is_nt; + + public: + // Windows-specific interface: + static void initialize_system_info(); + static void setmode_streams(); + + // Processor info as provided by NT + static int processor_type() { return _processor_type; } + // Processor level may not be accurate on non-NT systems + static int processor_level() { + assert(is_nt(), "use vm_version instead"); + return _processor_level; + } + static julong available_memory(); + static julong physical_memory() { return _physical_memory; } + + public: + // Generic interface: + + // Trace number of created threads + static intx _os_thread_limit; + static volatile intx _os_thread_count; + + // Tells whether the platform is NT or Windown95 + static bool is_nt() { return _is_nt; } + + // Returns the byte size of a virtual memory page + static int vm_page_size() { return _vm_page_size; } + + // Returns the size in bytes of memory blocks which can be allocated. + static int vm_allocation_granularity() { return _vm_allocation_granularity; } + + // Read the headers for the executable that started the current process into + // the structure passed in (see winnt.h). + static void read_executable_headers(PIMAGE_NT_HEADERS); + + // Default stack size for the current process. + static size_t default_stack_size() { return _default_stack_size; } + +#ifndef _WIN64 + // A wrapper to install a structured exception handler for fast JNI accesors. + static address fast_jni_accessor_wrapper(BasicType); +#endif + + // filter function to ignore faults on serializations page + static LONG WINAPI serialize_fault_filter(struct _EXCEPTION_POINTERS* e); +}; + +class PlatformEvent : public CHeapObj { + private: + double CachePad [4] ; // increase odds that _Event is sole occupant of cache line + volatile int _Event ; + HANDLE _ParkHandle ; + + public: // TODO-FIXME: make dtor private + ~PlatformEvent() { guarantee (0, "invariant") ; } + + public: + PlatformEvent() { + _Event = 0 ; + _ParkHandle = CreateEvent (NULL, false, false, NULL) ; + guarantee (_ParkHandle != NULL, "invariant") ; + } + + // Exercise caution using reset() and fired() - they may require MEMBARs + void reset() { _Event = 0 ; } + int fired() { return _Event; } + void park () ; + void unpark () ; + int park (jlong millis) ; +} ; + + + +class PlatformParker : public CHeapObj { + protected: + HANDLE _ParkEvent ; + + public: + ~PlatformParker () { guarantee (0, "invariant") ; } + PlatformParker () { + _ParkEvent = CreateEvent (NULL, true, false, NULL) ; + guarantee (_ParkEvent != NULL, "invariant") ; + } + +} ; diff --git a/hotspot/src/os/windows/vm/os_windows.inline.hpp b/hotspot/src/os/windows/vm/os_windows.inline.hpp new file mode 100644 index 00000000000..393a0181f38 --- /dev/null +++ b/hotspot/src/os/windows/vm/os_windows.inline.hpp @@ -0,0 +1,71 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline const char* os::file_separator() { return "\\"; } +inline const char* os::line_separator() { return "\r\n"; } +inline const char* os::path_separator() { return ";"; } + +inline const char* os::jlong_format_specifier() { return "%I64d"; } +inline const char* os::julong_format_specifier() { return "%I64u"; } + +// File names are case-insensitive on windows only +inline int os::file_name_strcmp(const char* s, const char* t) { + return _stricmp(s, t); +} + +// Used to improve time-sharing on some systems +inline void os::loop_breaker(int attempts) {} + +inline bool os::obsolete_option(const JavaVMOption *option) { + return false; +} + +inline bool os::uses_stack_guard_pages() { + return os::win32::is_nt(); +} + +inline bool os::allocate_stack_guard_pages() { + assert(uses_stack_guard_pages(), "sanity check"); + return true; +} + +inline int os::readdir_buf_size(const char *path) +{ + /* As Windows doesn't use the directory entry buffer passed to + os::readdir() this can be as short as possible */ + + return 1; +} + +// Bang the shadow pages if they need to be touched to be mapped. +inline void os::bang_stack_shadow_pages() { + // Write to each page of our new frame to force OS mapping. + // If we decrement stack pointer more than one page + // the OS may not map an intervening page into our space + // and may fault on a memory access to interior of our frame. + address sp = current_stack_pointer(); + for (int pages = 1; pages <= StackShadowPages; pages++) { + *((int *)(sp - (pages * vm_page_size()))) = 0; + } +} diff --git a/hotspot/src/os/windows/vm/perfMemory_windows.cpp b/hotspot/src/os/windows/vm/perfMemory_windows.cpp new file mode 100644 index 00000000000..42a45ee6527 --- /dev/null +++ b/hotspot/src/os/windows/vm/perfMemory_windows.cpp @@ -0,0 +1,1776 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_perfMemory_windows.cpp.incl" + +#include +#include +#include +#include +#include + +typedef BOOL (WINAPI *SetSecurityDescriptorControlFnPtr)( + IN PSECURITY_DESCRIPTOR pSecurityDescriptor, + IN SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, + IN SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet); + +// Standard Memory Implementation Details + +// create the PerfData memory region in standard memory. +// +static char* create_standard_memory(size_t size) { + + // allocate an aligned chuck of memory + char* mapAddress = os::reserve_memory(size); + + if (mapAddress == NULL) { + return NULL; + } + + // commit memory + if (!os::commit_memory(mapAddress, size)) { + if (PrintMiscellaneous && Verbose) { + warning("Could not commit PerfData memory\n"); + } + os::release_memory(mapAddress, size); + return NULL; + } + + return mapAddress; +} + +// delete the PerfData memory region +// +static void delete_standard_memory(char* addr, size_t size) { + + // there are no persistent external resources to cleanup for standard + // memory. since DestroyJavaVM does not support unloading of the JVM, + // cleanup of the memory resource is not performed. The memory will be + // reclaimed by the OS upon termination of the process. + // + return; + +} + +// save the specified memory region to the given file +// +static void save_memory_to_file(char* addr, size_t size) { + + const char* destfile = PerfMemory::get_perfdata_file_path(); + assert(destfile[0] != '\0', "invalid Perfdata file path"); + + int fd = ::_open(destfile, _O_BINARY|_O_CREAT|_O_WRONLY|_O_TRUNC, + _S_IREAD|_S_IWRITE); + + if (fd == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("Could not create Perfdata save file: %s: %s\n", + destfile, strerror(errno)); + } + } else { + for (size_t remaining = size; remaining > 0;) { + + int nbytes = ::_write(fd, addr, (unsigned int)remaining); + if (nbytes == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("Could not write Perfdata save file: %s: %s\n", + destfile, strerror(errno)); + } + break; + } + + remaining -= (size_t)nbytes; + addr += nbytes; + } + + int result = ::_close(fd); + if (PrintMiscellaneous && Verbose) { + if (result == OS_ERR) { + warning("Could not close %s: %s\n", destfile, strerror(errno)); + } + } + } + + FREE_C_HEAP_ARRAY(char, destfile); +} + +// Shared Memory Implementation Details + +// Note: the win32 shared memory implementation uses two objects to represent +// the shared memory: a windows kernel based file mapping object and a backing +// store file. On windows, the name space for shared memory is a kernel +// based name space that is disjoint from other win32 name spaces. Since Java +// is unaware of this name space, a parallel file system based name space is +// maintained, which provides a common file system based shared memory name +// space across the supported platforms and one that Java apps can deal with +// through simple file apis. +// +// For performance and resource cleanup reasons, it is recommended that the +// user specific directory and the backing store file be stored in either a +// RAM based file system or a local disk based file system. Network based +// file systems are not recommended for performance reasons. In addition, +// use of SMB network based file systems may result in unsuccesful cleanup +// of the disk based resource on exit of the VM. The Windows TMP and TEMP +// environement variables, as used by the GetTempPath() Win32 API (see +// os::get_temp_directory() in os_win32.cpp), control the location of the +// user specific directory and the shared memory backing store file. + +static HANDLE sharedmem_fileMapHandle = NULL; +static HANDLE sharedmem_fileHandle = INVALID_HANDLE_VALUE; +static char* sharedmem_fileName = NULL; + +// return the user specific temporary directory name. +// +// the caller is expected to free the allocated memory. +// +static char* get_user_tmp_dir(const char* user) { + + const char* tmpdir = os::get_temp_directory(); + const char* perfdir = PERFDATA_NAME; + size_t nbytes = strlen(tmpdir) + strlen(perfdir) + strlen(user) + 2; + char* dirname = NEW_C_HEAP_ARRAY(char, nbytes); + + // construct the path name to user specific tmp directory + _snprintf(dirname, nbytes, "%s%s_%s", tmpdir, perfdir, user); + + return dirname; +} + +// convert the given file name into a process id. if the file +// does not meet the file naming constraints, return 0. +// +static int filename_to_pid(const char* filename) { + + // a filename that doesn't begin with a digit is not a + // candidate for conversion. + // + if (!isdigit(*filename)) { + return 0; + } + + // check if file name can be converted to an integer without + // any leftover characters. + // + char* remainder = NULL; + errno = 0; + int pid = (int)strtol(filename, &remainder, 10); + + if (errno != 0) { + return 0; + } + + // check for left over characters. If any, then the filename is + // not a candidate for conversion. + // + if (remainder != NULL && *remainder != '\0') { + return 0; + } + + // successful conversion, return the pid + return pid; +} + +// check if the given path is considered a secure directory for +// the backing store files. Returns true if the directory exists +// and is considered a secure location. Returns false if the path +// is a symbolic link or if an error occured. +// +static bool is_directory_secure(const char* path) { + + DWORD fa; + + fa = GetFileAttributes(path); + if (fa == 0xFFFFFFFF) { + DWORD lasterror = GetLastError(); + if (lasterror == ERROR_FILE_NOT_FOUND) { + return false; + } + else { + // unexpected error, declare the path insecure + if (PrintMiscellaneous && Verbose) { + warning("could not get attributes for file %s: ", + " lasterror = %d\n", path, lasterror); + } + return false; + } + } + + if (fa & FILE_ATTRIBUTE_REPARSE_POINT) { + // we don't accept any redirection for the user specific directory + // so declare the path insecure. This may be too conservative, + // as some types of reparse points might be acceptable, but it + // is probably more secure to avoid these conditions. + // + if (PrintMiscellaneous && Verbose) { + warning("%s is a reparse point\n", path); + } + return false; + } + + if (fa & FILE_ATTRIBUTE_DIRECTORY) { + // this is the expected case. Since windows supports symbolic + // links to directories only, not to files, there is no need + // to check for open write permissions on the directory. If the + // directory has open write permissions, any files deposited that + // are not expected will be removed by the cleanup code. + // + return true; + } + else { + // this is either a regular file or some other type of file, + // any of which are unexpected and therefore insecure. + // + if (PrintMiscellaneous && Verbose) { + warning("%s is not a directory, file attributes = " + INTPTR_FORMAT "\n", path, fa); + } + return false; + } +} + +// return the user name for the owner of this process +// +// the caller is expected to free the allocated memory. +// +static char* get_user_name() { + + /* get the user name. This code is adapted from code found in + * the jdk in src/windows/native/java/lang/java_props_md.c + * java_props_md.c 1.29 02/02/06. According to the original + * source, the call to GetUserName is avoided because of a resulting + * increase in footprint of 100K. + */ + char* user = getenv("USERNAME"); + char buf[UNLEN+1]; + DWORD buflen = sizeof(buf); + if (user == NULL || strlen(user) == 0) { + if (GetUserName(buf, &buflen)) { + user = buf; + } + else { + return NULL; + } + } + + char* user_name = NEW_C_HEAP_ARRAY(char, strlen(user)+1); + strcpy(user_name, user); + + return user_name; +} + +// return the name of the user that owns the process identified by vmid. +// +// This method uses a slow directory search algorithm to find the backing +// store file for the specified vmid and returns the user name, as determined +// by the user name suffix of the hsperfdata_ directory name. +// +// the caller is expected to free the allocated memory. +// +static char* get_user_name_slow(int vmid) { + + // directory search + char* oldest_user = NULL; + time_t oldest_ctime = 0; + + const char* tmpdirname = os::get_temp_directory(); + + DIR* tmpdirp = os::opendir(tmpdirname); + + if (tmpdirp == NULL) { + return NULL; + } + + // for each entry in the directory that matches the pattern hsperfdata_*, + // open the directory and check if the file for the given vmid exists. + // The file with the expected name and the latest creation date is used + // to determine the user name for the process id. + // + struct dirent* dentry; + char* tdbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(tmpdirname)); + errno = 0; + while ((dentry = os::readdir(tmpdirp, (struct dirent *)tdbuf)) != NULL) { + + // check if the directory entry is a hsperfdata file + if (strncmp(dentry->d_name, PERFDATA_NAME, strlen(PERFDATA_NAME)) != 0) { + continue; + } + + char* usrdir_name = NEW_C_HEAP_ARRAY(char, + strlen(tmpdirname) + strlen(dentry->d_name) + 1); + strcpy(usrdir_name, tmpdirname); + strcat(usrdir_name, dentry->d_name); + + DIR* subdirp = os::opendir(usrdir_name); + + if (subdirp == NULL) { + FREE_C_HEAP_ARRAY(char, usrdir_name); + continue; + } + + // Since we don't create the backing store files in directories + // pointed to by symbolic links, we also don't follow them when + // looking for the files. We check for a symbolic link after the + // call to opendir in order to eliminate a small window where the + // symlink can be exploited. + // + if (!is_directory_secure(usrdir_name)) { + FREE_C_HEAP_ARRAY(char, usrdir_name); + os::closedir(subdirp); + continue; + } + + struct dirent* udentry; + char* udbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(usrdir_name)); + errno = 0; + while ((udentry = os::readdir(subdirp, (struct dirent *)udbuf)) != NULL) { + + if (filename_to_pid(udentry->d_name) == vmid) { + struct stat statbuf; + + char* filename = NEW_C_HEAP_ARRAY(char, + strlen(usrdir_name) + strlen(udentry->d_name) + 2); + + strcpy(filename, usrdir_name); + strcat(filename, "\\"); + strcat(filename, udentry->d_name); + + if (::stat(filename, &statbuf) == OS_ERR) { + FREE_C_HEAP_ARRAY(char, filename); + continue; + } + + // skip over files that are not regular files. + if ((statbuf.st_mode & S_IFMT) != S_IFREG) { + FREE_C_HEAP_ARRAY(char, filename); + continue; + } + + // compare and save filename with latest creation time + if (statbuf.st_size > 0 && statbuf.st_ctime > oldest_ctime) { + + if (statbuf.st_ctime > oldest_ctime) { + char* user = strchr(dentry->d_name, '_') + 1; + + if (oldest_user != NULL) FREE_C_HEAP_ARRAY(char, oldest_user); + oldest_user = NEW_C_HEAP_ARRAY(char, strlen(user)+1); + + strcpy(oldest_user, user); + oldest_ctime = statbuf.st_ctime; + } + } + + FREE_C_HEAP_ARRAY(char, filename); + } + } + os::closedir(subdirp); + FREE_C_HEAP_ARRAY(char, udbuf); + FREE_C_HEAP_ARRAY(char, usrdir_name); + } + os::closedir(tmpdirp); + FREE_C_HEAP_ARRAY(char, tdbuf); + + return(oldest_user); +} + +// return the name of the user that owns the process identified by vmid. +// +// note: this method should only be used via the Perf native methods. +// There are various costs to this method and limiting its use to the +// Perf native methods limits the impact to monitoring applications only. +// +static char* get_user_name(int vmid) { + + // A fast implementation is not provided at this time. It's possible + // to provide a fast process id to user name mapping function using + // the win32 apis, but the default ACL for the process object only + // allows processes with the same owner SID to acquire the process + // handle (via OpenProcess(PROCESS_QUERY_INFORMATION)). It's possible + // to have the JVM change the ACL for the process object to allow arbitrary + // users to access the process handle and the process security token. + // The security ramifications need to be studied before providing this + // mechanism. + // + return get_user_name_slow(vmid); +} + +// return the name of the shared memory file mapping object for the +// named shared memory region for the given user name and vmid. +// +// The file mapping object's name is not the file name. It is a name +// in a separate name space. +// +// the caller is expected to free the allocated memory. +// +static char *get_sharedmem_objectname(const char* user, int vmid) { + + // construct file mapping object's name, add 3 for two '_' and a + // null terminator. + int nbytes = (int)strlen(PERFDATA_NAME) + (int)strlen(user) + 3; + + // the id is converted to an unsigned value here because win32 allows + // negative process ids. However, OpenFileMapping API complains + // about a name containing a '-' characters. + // + nbytes += UINT_CHARS; + char* name = NEW_C_HEAP_ARRAY(char, nbytes); + _snprintf(name, nbytes, "%s_%s_%u", PERFDATA_NAME, user, vmid); + + return name; +} + +// return the file name of the backing store file for the named +// shared memory region for the given user name and vmid. +// +// the caller is expected to free the allocated memory. +// +static char* get_sharedmem_filename(const char* dirname, int vmid) { + + // add 2 for the file separator and a null terminator. + size_t nbytes = strlen(dirname) + UINT_CHARS + 2; + + char* name = NEW_C_HEAP_ARRAY(char, nbytes); + _snprintf(name, nbytes, "%s\\%d", dirname, vmid); + + return name; +} + +// remove file +// +// this method removes the file with the given file name. +// +// Note: if the indicated file is on an SMB network file system, this +// method may be unsuccessful in removing the file. +// +static void remove_file(const char* dirname, const char* filename) { + + size_t nbytes = strlen(dirname) + strlen(filename) + 2; + char* path = NEW_C_HEAP_ARRAY(char, nbytes); + + strcpy(path, dirname); + strcat(path, "\\"); + strcat(path, filename); + + if (::unlink(path) == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + if (errno != ENOENT) { + warning("Could not unlink shared memory backing" + " store file %s : %s\n", path, strerror(errno)); + } + } + } + + FREE_C_HEAP_ARRAY(char, path); +} + +// returns true if the process represented by pid is alive, otherwise +// returns false. the validity of the result is only accurate if the +// target process is owned by the same principal that owns this process. +// this method should not be used if to test the status of an otherwise +// arbitrary process unless it is know that this process has the appropriate +// privileges to guarantee a result valid. +// +static bool is_alive(int pid) { + + HANDLE ph = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (ph == NULL) { + // the process does not exist. + if (PrintMiscellaneous && Verbose) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_INVALID_PARAMETER) { + warning("OpenProcess failed: %d\n", GetLastError()); + } + } + return false; + } + + DWORD exit_status; + if (!GetExitCodeProcess(ph, &exit_status)) { + if (PrintMiscellaneous && Verbose) { + warning("GetExitCodeProcess failed: %d\n", GetLastError()); + } + CloseHandle(ph); + return false; + } + + CloseHandle(ph); + return (exit_status == STILL_ACTIVE) ? true : false; +} + +// check if the file system is considered secure for the backing store files +// +static bool is_filesystem_secure(const char* path) { + + char root_path[MAX_PATH]; + char fs_type[MAX_PATH]; + + if (PerfBypassFileSystemCheck) { + if (PrintMiscellaneous && Verbose) { + warning("bypassing file system criteria checks for %s\n", path); + } + return true; + } + + char* first_colon = strchr((char *)path, ':'); + if (first_colon == NULL) { + if (PrintMiscellaneous && Verbose) { + warning("expected device specifier in path: %s\n", path); + } + return false; + } + + size_t len = (size_t)(first_colon - path); + assert(len + 2 <= MAX_PATH, "unexpected device specifier length"); + strncpy(root_path, path, len + 1); + root_path[len + 1] = '\\'; + root_path[len + 2] = '\0'; + + // check that we have something like "C:\" or "AA:\" + assert(strlen(root_path) >= 3, "device specifier too short"); + assert(strchr(root_path, ':') != NULL, "bad device specifier format"); + assert(strchr(root_path, '\\') != NULL, "bad device specifier format"); + + DWORD maxpath; + DWORD flags; + + if (!GetVolumeInformation(root_path, NULL, 0, NULL, &maxpath, + &flags, fs_type, MAX_PATH)) { + // we can't get information about the volume, so assume unsafe. + if (PrintMiscellaneous && Verbose) { + warning("could not get device information for %s: " + " path = %s: lasterror = %d\n", + root_path, path, GetLastError()); + } + return false; + } + + if ((flags & FS_PERSISTENT_ACLS) == 0) { + // file system doesn't support ACLs, declare file system unsafe + if (PrintMiscellaneous && Verbose) { + warning("file system type %s on device %s does not support" + " ACLs\n", fs_type, root_path); + } + return false; + } + + if ((flags & FS_VOL_IS_COMPRESSED) != 0) { + // file system is compressed, declare file system unsafe + if (PrintMiscellaneous && Verbose) { + warning("file system type %s on device %s is compressed\n", + fs_type, root_path); + } + return false; + } + + return true; +} + +// cleanup stale shared memory resources +// +// This method attempts to remove all stale shared memory files in +// the named user temporary directory. It scans the named directory +// for files matching the pattern ^$[0-9]*$. For each file found, the +// process id is extracted from the file name and a test is run to +// determine if the process is alive. If the process is not alive, +// any stale file resources are removed. +// +static void cleanup_sharedmem_resources(const char* dirname) { + + // open the user temp directory + DIR* dirp = os::opendir(dirname); + + if (dirp == NULL) { + // directory doesn't exist, so there is nothing to cleanup + return; + } + + if (!is_directory_secure(dirname)) { + // the directory is not secure, don't attempt any cleanup + return; + } + + // for each entry in the directory that matches the expected file + // name pattern, determine if the file resources are stale and if + // so, remove the file resources. Note, instrumented HotSpot processes + // for this user may start and/or terminate during this search and + // remove or create new files in this directory. The behavior of this + // loop under these conditions is dependent upon the implementation of + // opendir/readdir. + // + struct dirent* entry; + char* dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(dirname)); + errno = 0; + while ((entry = os::readdir(dirp, (struct dirent *)dbuf)) != NULL) { + + int pid = filename_to_pid(entry->d_name); + + if (pid == 0) { + + if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { + + // attempt to remove all unexpected files, except "." and ".." + remove_file(dirname, entry->d_name); + } + + errno = 0; + continue; + } + + // we now have a file name that converts to a valid integer + // that could represent a process id . if this process id + // matches the current process id or the process is not running, + // then remove the stale file resources. + // + // process liveness is detected by checking the exit status + // of the process. if the process id is valid and the exit status + // indicates that it is still running, the file file resources + // are not removed. If the process id is invalid, or if we don't + // have permissions to check the process status, or if the process + // id is valid and the process has terminated, the the file resources + // are assumed to be stale and are removed. + // + if (pid == os::current_process_id() || !is_alive(pid)) { + + // we can only remove the file resources. Any mapped views + // of the file can only be unmapped by the processes that + // opened those views and the file mapping object will not + // get removed until all views are unmapped. + // + remove_file(dirname, entry->d_name); + } + errno = 0; + } + os::closedir(dirp); + FREE_C_HEAP_ARRAY(char, dbuf); +} + +// create a file mapping object with the requested name, and size +// from the file represented by the given Handle object +// +static HANDLE create_file_mapping(const char* name, HANDLE fh, LPSECURITY_ATTRIBUTES fsa, size_t size) { + + DWORD lowSize = (DWORD)size; + DWORD highSize = 0; + HANDLE fmh = NULL; + + // Create a file mapping object with the given name. This function + // will grow the file to the specified size. + // + fmh = CreateFileMapping( + fh, /* HANDLE file handle for backing store */ + fsa, /* LPSECURITY_ATTRIBUTES Not inheritable */ + PAGE_READWRITE, /* DWORD protections */ + highSize, /* DWORD High word of max size */ + lowSize, /* DWORD Low word of max size */ + name); /* LPCTSTR name for object */ + + if (fmh == NULL) { + if (PrintMiscellaneous && Verbose) { + warning("CreateFileMapping failed, lasterror = %d\n", GetLastError()); + } + return NULL; + } + + if (GetLastError() == ERROR_ALREADY_EXISTS) { + + // a stale file mapping object was encountered. This object may be + // owned by this or some other user and cannot be removed until + // the other processes either exit or close their mapping objects + // and/or mapped views of this mapping object. + // + if (PrintMiscellaneous && Verbose) { + warning("file mapping already exists, lasterror = %d\n", GetLastError()); + } + + CloseHandle(fmh); + return NULL; + } + + return fmh; +} + + +// method to free the given security descriptor and the contained +// access control list. +// +static void free_security_desc(PSECURITY_DESCRIPTOR pSD) { + + BOOL success, exists, isdefault; + PACL pACL; + + if (pSD != NULL) { + + // get the access control list from the security descriptor + success = GetSecurityDescriptorDacl(pSD, &exists, &pACL, &isdefault); + + // if an ACL existed and it was not a default acl, then it must + // be an ACL we enlisted. free the resources. + // + if (success && exists && pACL != NULL && !isdefault) { + FREE_C_HEAP_ARRAY(char, pACL); + } + + // free the security descriptor + FREE_C_HEAP_ARRAY(char, pSD); + } +} + +// method to free up a security attributes structure and any +// contained security descriptors and ACL +// +static void free_security_attr(LPSECURITY_ATTRIBUTES lpSA) { + + if (lpSA != NULL) { + // free the contained security descriptor and the ACL + free_security_desc(lpSA->lpSecurityDescriptor); + lpSA->lpSecurityDescriptor = NULL; + + // free the security attributes structure + FREE_C_HEAP_ARRAY(char, lpSA); + } +} + +// get the user SID for the process indicated by the process handle +// +static PSID get_user_sid(HANDLE hProcess) { + + HANDLE hAccessToken; + PTOKEN_USER token_buf = NULL; + DWORD rsize = 0; + + if (hProcess == NULL) { + return NULL; + } + + // get the process token + if (!OpenProcessToken(hProcess, TOKEN_READ, &hAccessToken)) { + if (PrintMiscellaneous && Verbose) { + warning("OpenProcessToken failure: lasterror = %d \n", GetLastError()); + } + return NULL; + } + + // determine the size of the token structured needed to retrieve + // the user token information from the access token. + // + if (!GetTokenInformation(hAccessToken, TokenUser, NULL, rsize, &rsize)) { + DWORD lasterror = GetLastError(); + if (lasterror != ERROR_INSUFFICIENT_BUFFER) { + if (PrintMiscellaneous && Verbose) { + warning("GetTokenInformation failure: lasterror = %d," + " rsize = %d\n", lasterror, rsize); + } + CloseHandle(hAccessToken); + return NULL; + } + } + + token_buf = (PTOKEN_USER) NEW_C_HEAP_ARRAY(char, rsize); + + // get the user token information + if (!GetTokenInformation(hAccessToken, TokenUser, token_buf, rsize, &rsize)) { + if (PrintMiscellaneous && Verbose) { + warning("GetTokenInformation failure: lasterror = %d," + " rsize = %d\n", GetLastError(), rsize); + } + FREE_C_HEAP_ARRAY(char, token_buf); + CloseHandle(hAccessToken); + return NULL; + } + + DWORD nbytes = GetLengthSid(token_buf->User.Sid); + PSID pSID = NEW_C_HEAP_ARRAY(char, nbytes); + + if (!CopySid(nbytes, pSID, token_buf->User.Sid)) { + if (PrintMiscellaneous && Verbose) { + warning("GetTokenInformation failure: lasterror = %d," + " rsize = %d\n", GetLastError(), rsize); + } + FREE_C_HEAP_ARRAY(char, token_buf); + FREE_C_HEAP_ARRAY(char, pSID); + CloseHandle(hAccessToken); + return NULL; + } + + // close the access token. + CloseHandle(hAccessToken); + FREE_C_HEAP_ARRAY(char, token_buf); + + return pSID; +} + +// structure used to consolidate access control entry information +// +typedef struct ace_data { + PSID pSid; // SID of the ACE + DWORD mask; // mask for the ACE +} ace_data_t; + + +// method to add an allow access control entry with the access rights +// indicated in mask for the principal indicated in SID to the given +// security descriptor. Much of the DACL handling was adapted from +// the example provided here: +// http://support.microsoft.com/kb/102102/EN-US/ +// + +static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, + ace_data_t aces[], int ace_count) { + PACL newACL = NULL; + PACL oldACL = NULL; + + if (pSD == NULL) { + return false; + } + + BOOL exists, isdefault; + + // retrieve any existing access control list. + if (!GetSecurityDescriptorDacl(pSD, &exists, &oldACL, &isdefault)) { + if (PrintMiscellaneous && Verbose) { + warning("GetSecurityDescriptor failure: lasterror = %d \n", + GetLastError()); + } + return false; + } + + // get the size of the DACL + ACL_SIZE_INFORMATION aclinfo; + + // GetSecurityDescriptorDacl may return true value for exists (lpbDaclPresent) + // while oldACL is NULL for some case. + if (oldACL == NULL) { + exists = FALSE; + } + + if (exists) { + if (!GetAclInformation(oldACL, &aclinfo, + sizeof(ACL_SIZE_INFORMATION), + AclSizeInformation)) { + if (PrintMiscellaneous && Verbose) { + warning("GetAclInformation failure: lasterror = %d \n", GetLastError()); + return false; + } + } + } else { + aclinfo.AceCount = 0; // assume NULL DACL + aclinfo.AclBytesFree = 0; + aclinfo.AclBytesInUse = sizeof(ACL); + } + + // compute the size needed for the new ACL + // initial size of ACL is sum of the following: + // * size of ACL structure. + // * size of each ACE structure that ACL is to contain minus the sid + // sidStart member (DWORD) of the ACE. + // * length of the SID that each ACE is to contain. + DWORD newACLsize = aclinfo.AclBytesInUse + + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) * ace_count; + for (int i = 0; i < ace_count; i++) { + newACLsize += GetLengthSid(aces[i].pSid); + } + + // create the new ACL + newACL = (PACL) NEW_C_HEAP_ARRAY(char, newACLsize); + + if (!InitializeAcl(newACL, newACLsize, ACL_REVISION)) { + if (PrintMiscellaneous && Verbose) { + warning("InitializeAcl failure: lasterror = %d \n", GetLastError()); + } + FREE_C_HEAP_ARRAY(char, newACL); + return false; + } + + unsigned int ace_index = 0; + // copy any existing ACEs from the old ACL (if any) to the new ACL. + if (aclinfo.AceCount != 0) { + while (ace_index < aclinfo.AceCount) { + LPVOID ace; + if (!GetAce(oldACL, ace_index, &ace)) { + if (PrintMiscellaneous && Verbose) { + warning("InitializeAcl failure: lasterror = %d \n", GetLastError()); + } + FREE_C_HEAP_ARRAY(char, newACL); + return false; + } + if (((ACCESS_ALLOWED_ACE *)ace)->Header.AceFlags && INHERITED_ACE) { + // this is an inherited, allowed ACE; break from loop so we can + // add the new access allowed, non-inherited ACE in the correct + // position, immediately following all non-inherited ACEs. + break; + } + + // determine if the SID of this ACE matches any of the SIDs + // for which we plan to set ACEs. + int matches = 0; + for (int i = 0; i < ace_count; i++) { + if (EqualSid(aces[i].pSid, &(((ACCESS_ALLOWED_ACE *)ace)->SidStart))) { + matches++; + break; + } + } + + // if there are no SID matches, then add this existing ACE to the new ACL + if (matches == 0) { + if (!AddAce(newACL, ACL_REVISION, MAXDWORD, ace, + ((PACE_HEADER)ace)->AceSize)) { + if (PrintMiscellaneous && Verbose) { + warning("AddAce failure: lasterror = %d \n", GetLastError()); + } + FREE_C_HEAP_ARRAY(char, newACL); + return false; + } + } + ace_index++; + } + } + + // add the passed-in access control entries to the new ACL + for (int i = 0; i < ace_count; i++) { + if (!AddAccessAllowedAce(newACL, ACL_REVISION, + aces[i].mask, aces[i].pSid)) { + if (PrintMiscellaneous && Verbose) { + warning("AddAccessAllowedAce failure: lasterror = %d \n", + GetLastError()); + } + FREE_C_HEAP_ARRAY(char, newACL); + return false; + } + } + + // now copy the rest of the inherited ACEs from the old ACL + if (aclinfo.AceCount != 0) { + // picking up at ace_index, where we left off in the + // previous ace_index loop + while (ace_index < aclinfo.AceCount) { + LPVOID ace; + if (!GetAce(oldACL, ace_index, &ace)) { + if (PrintMiscellaneous && Verbose) { + warning("InitializeAcl failure: lasterror = %d \n", GetLastError()); + } + FREE_C_HEAP_ARRAY(char, newACL); + return false; + } + if (!AddAce(newACL, ACL_REVISION, MAXDWORD, ace, + ((PACE_HEADER)ace)->AceSize)) { + if (PrintMiscellaneous && Verbose) { + warning("AddAce failure: lasterror = %d \n", GetLastError()); + } + FREE_C_HEAP_ARRAY(char, newACL); + return false; + } + ace_index++; + } + } + + // add the new ACL to the security descriptor. + if (!SetSecurityDescriptorDacl(pSD, TRUE, newACL, FALSE)) { + if (PrintMiscellaneous && Verbose) { + warning("SetSecurityDescriptorDacl failure:" + " lasterror = %d \n", GetLastError()); + } + FREE_C_HEAP_ARRAY(char, newACL); + return false; + } + + // if running on windows 2000 or later, set the automatic inheritence + // control flags. + SetSecurityDescriptorControlFnPtr _SetSecurityDescriptorControl; + _SetSecurityDescriptorControl = (SetSecurityDescriptorControlFnPtr) + GetProcAddress(GetModuleHandle(TEXT("advapi32.dll")), + "SetSecurityDescriptorControl"); + + if (_SetSecurityDescriptorControl != NULL) { + // We do not want to further propogate inherited DACLs, so making them + // protected prevents that. + if (!_SetSecurityDescriptorControl(pSD, SE_DACL_PROTECTED, + SE_DACL_PROTECTED)) { + if (PrintMiscellaneous && Verbose) { + warning("SetSecurityDescriptorControl failure:" + " lasterror = %d \n", GetLastError()); + } + FREE_C_HEAP_ARRAY(char, newACL); + return false; + } + } + // Note, the security descriptor maintains a reference to the newACL, not + // a copy of it. Therefore, the newACL is not freed here. It is freed when + // the security descriptor containing its reference is freed. + // + return true; +} + +// method to create a security attributes structure, which contains a +// security descriptor and an access control list comprised of 0 or more +// access control entries. The method take an array of ace_data structures +// that indicate the ACE to be added to the security descriptor. +// +// the caller must free the resources associated with the security +// attributes structure created by this method by calling the +// free_security_attr() method. +// +static LPSECURITY_ATTRIBUTES make_security_attr(ace_data_t aces[], int count) { + + // allocate space for a security descriptor + PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR) + NEW_C_HEAP_ARRAY(char, SECURITY_DESCRIPTOR_MIN_LENGTH); + + // initialize the security descriptor + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { + if (PrintMiscellaneous && Verbose) { + warning("InitializeSecurityDescriptor failure: " + "lasterror = %d \n", GetLastError()); + } + free_security_desc(pSD); + return NULL; + } + + // add the access control entries + if (!add_allow_aces(pSD, aces, count)) { + free_security_desc(pSD); + return NULL; + } + + // allocate and initialize the security attributes structure and + // return it to the caller. + // + LPSECURITY_ATTRIBUTES lpSA = (LPSECURITY_ATTRIBUTES) + NEW_C_HEAP_ARRAY(char, sizeof(SECURITY_ATTRIBUTES)); + lpSA->nLength = sizeof(SECURITY_ATTRIBUTES); + lpSA->lpSecurityDescriptor = pSD; + lpSA->bInheritHandle = FALSE; + + return(lpSA); +} + +// method to create a security attributes structure with a restrictive +// access control list that creates a set access rights for the user/owner +// of the securable object and a separate set access rights for everyone else. +// also provides for full access rights for the administrator group. +// +// the caller must free the resources associated with the security +// attributes structure created by this method by calling the +// free_security_attr() method. +// + +static LPSECURITY_ATTRIBUTES make_user_everybody_admin_security_attr( + DWORD umask, DWORD emask, DWORD amask) { + + ace_data_t aces[3]; + + // initialize the user ace data + aces[0].pSid = get_user_sid(GetCurrentProcess()); + aces[0].mask = umask; + + // get the well known SID for BUILTIN\Administrators + PSID administratorsSid = NULL; + SID_IDENTIFIER_AUTHORITY SIDAuthAdministrators = SECURITY_NT_AUTHORITY; + + if (!AllocateAndInitializeSid( &SIDAuthAdministrators, 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, &administratorsSid)) { + + if (PrintMiscellaneous && Verbose) { + warning("AllocateAndInitializeSid failure: " + "lasterror = %d \n", GetLastError()); + } + return NULL; + } + + // initialize the ace data for administrator group + aces[1].pSid = administratorsSid; + aces[1].mask = amask; + + // get the well known SID for the universal Everybody + PSID everybodySid = NULL; + SID_IDENTIFIER_AUTHORITY SIDAuthEverybody = SECURITY_WORLD_SID_AUTHORITY; + + if (!AllocateAndInitializeSid( &SIDAuthEverybody, 1, SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, &everybodySid)) { + + if (PrintMiscellaneous && Verbose) { + warning("AllocateAndInitializeSid failure: " + "lasterror = %d \n", GetLastError()); + } + return NULL; + } + + // initialize the ace data for everybody else. + aces[2].pSid = everybodySid; + aces[2].mask = emask; + + // create a security attributes structure with access control + // entries as initialized above. + LPSECURITY_ATTRIBUTES lpSA = make_security_attr(aces, 3); + FREE_C_HEAP_ARRAY(char, aces[0].pSid); + FreeSid(everybodySid); + FreeSid(administratorsSid); + return(lpSA); +} + + +// method to create the security attributes structure for restricting +// access to the user temporary directory. +// +// the caller must free the resources associated with the security +// attributes structure created by this method by calling the +// free_security_attr() method. +// +static LPSECURITY_ATTRIBUTES make_tmpdir_security_attr() { + + // create full access rights for the user/owner of the directory + // and read-only access rights for everybody else. This is + // effectively equivalent to UNIX 755 permissions on a directory. + // + DWORD umask = STANDARD_RIGHTS_REQUIRED | FILE_ALL_ACCESS; + DWORD emask = GENERIC_READ | FILE_LIST_DIRECTORY | FILE_TRAVERSE; + DWORD amask = STANDARD_RIGHTS_ALL | FILE_ALL_ACCESS; + + return make_user_everybody_admin_security_attr(umask, emask, amask); +} + +// method to create the security attributes structure for restricting +// access to the shared memory backing store file. +// +// the caller must free the resources associated with the security +// attributes structure created by this method by calling the +// free_security_attr() method. +// +static LPSECURITY_ATTRIBUTES make_file_security_attr() { + + // create extensive access rights for the user/owner of the file + // and attribute read-only access rights for everybody else. This + // is effectively equivalent to UNIX 600 permissions on a file. + // + DWORD umask = STANDARD_RIGHTS_ALL | FILE_ALL_ACCESS; + DWORD emask = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | + FILE_READ_EA | FILE_LIST_DIRECTORY | FILE_TRAVERSE; + DWORD amask = STANDARD_RIGHTS_ALL | FILE_ALL_ACCESS; + + return make_user_everybody_admin_security_attr(umask, emask, amask); +} + +// method to create the security attributes structure for restricting +// access to the name shared memory file mapping object. +// +// the caller must free the resources associated with the security +// attributes structure created by this method by calling the +// free_security_attr() method. +// +static LPSECURITY_ATTRIBUTES make_smo_security_attr() { + + // create extensive access rights for the user/owner of the shared + // memory object and attribute read-only access rights for everybody + // else. This is effectively equivalent to UNIX 600 permissions on + // on the shared memory object. + // + DWORD umask = STANDARD_RIGHTS_REQUIRED | FILE_MAP_ALL_ACCESS; + DWORD emask = STANDARD_RIGHTS_READ; // attributes only + DWORD amask = STANDARD_RIGHTS_ALL | FILE_MAP_ALL_ACCESS; + + return make_user_everybody_admin_security_attr(umask, emask, amask); +} + +// make the user specific temporary directory +// +static bool make_user_tmp_dir(const char* dirname) { + + + LPSECURITY_ATTRIBUTES pDirSA = make_tmpdir_security_attr(); + if (pDirSA == NULL) { + return false; + } + + + // create the directory with the given security attributes + if (!CreateDirectory(dirname, pDirSA)) { + DWORD lasterror = GetLastError(); + if (lasterror == ERROR_ALREADY_EXISTS) { + // The directory already exists and was probably created by another + // JVM instance. However, this could also be the result of a + // deliberate symlink. Verify that the existing directory is safe. + // + if (!is_directory_secure(dirname)) { + // directory is not secure + if (PrintMiscellaneous && Verbose) { + warning("%s directory is insecure\n", dirname); + } + return false; + } + // The administrator should be able to delete this directory. + // But the directory created by previous version of JVM may not + // have permission for administrators to delete this directory. + // So add full permission to the administrator. Also setting new + // DACLs might fix the corrupted the DACLs. + SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION; + if (!SetFileSecurity(dirname, secInfo, pDirSA->lpSecurityDescriptor)) { + if (PrintMiscellaneous && Verbose) { + lasterror = GetLastError(); + warning("SetFileSecurity failed for %s directory. lasterror %d \n", + dirname, lasterror); + } + } + } + else { + if (PrintMiscellaneous && Verbose) { + warning("CreateDirectory failed: %d\n", GetLastError()); + } + return false; + } + } + + // free the security attributes structure + free_security_attr(pDirSA); + + return true; +} + +// create the shared memory resources +// +// This function creates the shared memory resources. This includes +// the backing store file and the file mapping shared memory object. +// +static HANDLE create_sharedmem_resources(const char* dirname, const char* filename, const char* objectname, size_t size) { + + HANDLE fh = INVALID_HANDLE_VALUE; + HANDLE fmh = NULL; + + + // create the security attributes for the backing store file + LPSECURITY_ATTRIBUTES lpFileSA = make_file_security_attr(); + if (lpFileSA == NULL) { + return NULL; + } + + // create the security attributes for the shared memory object + LPSECURITY_ATTRIBUTES lpSmoSA = make_smo_security_attr(); + if (lpSmoSA == NULL) { + free_security_attr(lpFileSA); + return NULL; + } + + // create the user temporary directory + if (!make_user_tmp_dir(dirname)) { + // could not make/find the directory or the found directory + // was not secure + return NULL; + } + + // Create the file - the FILE_FLAG_DELETE_ON_CLOSE flag allows the + // file to be deleted by the last process that closes its handle to + // the file. This is important as the apis do not allow a terminating + // JVM being monitored by another process to remove the file name. + // + // the FILE_SHARE_DELETE share mode is valid only in winnt + // + fh = CreateFile( + filename, /* LPCTSTR file name */ + + GENERIC_READ|GENERIC_WRITE, /* DWORD desired access */ + + (os::win32::is_nt() ? FILE_SHARE_DELETE : 0)| + FILE_SHARE_READ, /* DWORD share mode, future READONLY + * open operations allowed + */ + lpFileSA, /* LPSECURITY security attributes */ + CREATE_ALWAYS, /* DWORD creation disposition + * create file, if it already + * exists, overwrite it. + */ + FILE_FLAG_DELETE_ON_CLOSE, /* DWORD flags and attributes */ + + NULL); /* HANDLE template file access */ + + free_security_attr(lpFileSA); + + if (fh == INVALID_HANDLE_VALUE) { + DWORD lasterror = GetLastError(); + if (PrintMiscellaneous && Verbose) { + warning("could not create file %s: %d\n", filename, lasterror); + } + return NULL; + } + + // try to create the file mapping + fmh = create_file_mapping(objectname, fh, lpSmoSA, size); + + free_security_attr(lpSmoSA); + + if (fmh == NULL) { + // closing the file handle here will decrement the reference count + // on the file. When all processes accessing the file close their + // handle to it, the reference count will decrement to 0 and the + // OS will delete the file. These semantics are requested by the + // FILE_FLAG_DELETE_ON_CLOSE flag in CreateFile call above. + CloseHandle(fh); + fh = NULL; + return NULL; + } + + // the file has been successfully created and the file mapping + // object has been created. + sharedmem_fileHandle = fh; + sharedmem_fileName = strdup(filename); + + return fmh; +} + +// open the shared memory object for the given vmid. +// +static HANDLE open_sharedmem_object(const char* objectname, DWORD ofm_access, TRAPS) { + + HANDLE fmh; + + // open the file mapping with the requested mode + fmh = OpenFileMapping( + ofm_access, /* DWORD access mode */ + FALSE, /* BOOL inherit flag - Do not allow inherit */ + objectname); /* name for object */ + + if (fmh == NULL) { + if (PrintMiscellaneous && Verbose) { + warning("OpenFileMapping failed for shared memory object %s:" + " lasterror = %d\n", objectname, GetLastError()); + } + THROW_MSG_(vmSymbols::java_lang_Exception(), + "Could not open PerfMemory", INVALID_HANDLE_VALUE); + } + + return fmh;; +} + +// create a named shared memory region +// +// On Win32, a named shared memory object has a name space that +// is independent of the file system name space. Shared memory object, +// or more precisely, file mapping objects, provide no mechanism to +// inquire the size of the memory region. There is also no api to +// enumerate the memory regions for various processes. +// +// This implementation utilizes the shared memory name space in parallel +// with the file system name space. This allows us to determine the +// size of the shared memory region from the size of the file and it +// allows us to provide a common, file system based name space for +// shared memory across platforms. +// +static char* mapping_create_shared(size_t size) { + + void *mapAddress; + int vmid = os::current_process_id(); + + // get the name of the user associated with this process + char* user = get_user_name(); + + if (user == NULL) { + return NULL; + } + + // construct the name of the user specific temporary directory + char* dirname = get_user_tmp_dir(user); + + // check that the file system is secure - i.e. it supports ACLs. + if (!is_filesystem_secure(dirname)) { + return NULL; + } + + // create the names of the backing store files and for the + // share memory object. + // + char* filename = get_sharedmem_filename(dirname, vmid); + char* objectname = get_sharedmem_objectname(user, vmid); + + // cleanup any stale shared memory resources + cleanup_sharedmem_resources(dirname); + + assert(((size != 0) && (size % os::vm_page_size() == 0)), + "unexpected PerfMemry region size"); + + FREE_C_HEAP_ARRAY(char, user); + + // create the shared memory resources + sharedmem_fileMapHandle = + create_sharedmem_resources(dirname, filename, objectname, size); + + FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(char, objectname); + FREE_C_HEAP_ARRAY(char, dirname); + + if (sharedmem_fileMapHandle == NULL) { + return NULL; + } + + // map the file into the address space + mapAddress = MapViewOfFile( + sharedmem_fileMapHandle, /* HANDLE = file mapping object */ + FILE_MAP_ALL_ACCESS, /* DWORD access flags */ + 0, /* DWORD High word of offset */ + 0, /* DWORD Low word of offset */ + (DWORD)size); /* DWORD Number of bytes to map */ + + if (mapAddress == NULL) { + if (PrintMiscellaneous && Verbose) { + warning("MapViewOfFile failed, lasterror = %d\n", GetLastError()); + } + CloseHandle(sharedmem_fileMapHandle); + sharedmem_fileMapHandle = NULL; + return NULL; + } + + // clear the shared memory region + (void)memset(mapAddress, '\0', size); + + return (char*) mapAddress; +} + +// this method deletes the file mapping object. +// +static void delete_file_mapping(char* addr, size_t size) { + + // cleanup the persistent shared memory resources. since DestroyJavaVM does + // not support unloading of the JVM, unmapping of the memory resource is not + // performed. The memory will be reclaimed by the OS upon termination of all + // processes mapping the resource. The file mapping handle and the file + // handle are closed here to expedite the remove of the file by the OS. The + // file is not removed directly because it was created with + // FILE_FLAG_DELETE_ON_CLOSE semantics and any attempt to remove it would + // be unsuccessful. + + // close the fileMapHandle. the file mapping will still be retained + // by the OS as long as any other JVM processes has an open file mapping + // handle or a mapped view of the file. + // + if (sharedmem_fileMapHandle != NULL) { + CloseHandle(sharedmem_fileMapHandle); + sharedmem_fileMapHandle = NULL; + } + + // close the file handle. This will decrement the reference count on the + // backing store file. When the reference count decrements to 0, the OS + // will delete the file. These semantics apply because the file was + // created with the FILE_FLAG_DELETE_ON_CLOSE flag. + // + if (sharedmem_fileHandle != INVALID_HANDLE_VALUE) { + CloseHandle(sharedmem_fileHandle); + sharedmem_fileHandle = INVALID_HANDLE_VALUE; + } +} + +// this method determines the size of the shared memory file +// +static size_t sharedmem_filesize(const char* filename, TRAPS) { + + struct stat statbuf; + + // get the file size + // + // on win95/98/me, _stat returns a file size of 0 bytes, but on + // winnt/2k the appropriate file size is returned. support for + // the sharable aspects of performance counters was abandonded + // on the non-nt win32 platforms due to this and other api + // inconsistencies + // + if (::stat(filename, &statbuf) == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("stat %s failed: %s\n", filename, strerror(errno)); + } + THROW_MSG_0(vmSymbols::java_io_IOException(), + "Could not determine PerfMemory size"); + } + + if ((statbuf.st_size == 0) || (statbuf.st_size % os::vm_page_size() != 0)) { + if (PrintMiscellaneous && Verbose) { + warning("unexpected file size: size = " SIZE_FORMAT "\n", + statbuf.st_size); + } + THROW_MSG_0(vmSymbols::java_lang_Exception(), + "Invalid PerfMemory size"); + } + + return statbuf.st_size; +} + +// this method opens a file mapping object and maps the object +// into the address space of the process +// +static void open_file_mapping(const char* user, int vmid, + PerfMemory::PerfMemoryMode mode, + char** addrp, size_t* sizep, TRAPS) { + + ResourceMark rm; + + void *mapAddress = 0; + size_t size; + HANDLE fmh; + DWORD ofm_access; + DWORD mv_access; + const char* luser = NULL; + + if (mode == PerfMemory::PERF_MODE_RO) { + ofm_access = FILE_MAP_READ; + mv_access = FILE_MAP_READ; + } + else if (mode == PerfMemory::PERF_MODE_RW) { +#ifdef LATER + ofm_access = FILE_MAP_READ | FILE_MAP_WRITE; + mv_access = FILE_MAP_READ | FILE_MAP_WRITE; +#else + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Unsupported access mode"); +#endif + } + else { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Illegal access mode"); + } + + // if a user name wasn't specified, then find the user name for + // the owner of the target vm. + if (user == NULL || strlen(user) == 0) { + luser = get_user_name(vmid); + } + else { + luser = user; + } + + if (luser == NULL) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Could not map vmid to user name"); + } + + // get the names for the resources for the target vm + char* dirname = get_user_tmp_dir(luser); + + // since we don't follow symbolic links when creating the backing + // store file, we also don't following them when attaching + // + if (!is_directory_secure(dirname)) { + FREE_C_HEAP_ARRAY(char, dirname); + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); + } + + char* filename = get_sharedmem_filename(dirname, vmid); + char* objectname = get_sharedmem_objectname(luser, vmid); + + // copy heap memory to resource memory. the objectname and + // filename are passed to methods that may throw exceptions. + // using resource arrays for these names prevents the leaks + // that would otherwise occur. + // + char* rfilename = NEW_RESOURCE_ARRAY(char, strlen(filename) + 1); + char* robjectname = NEW_RESOURCE_ARRAY(char, strlen(objectname) + 1); + strcpy(rfilename, filename); + strcpy(robjectname, objectname); + + // free the c heap resources that are no longer needed + if (luser != user) FREE_C_HEAP_ARRAY(char, luser); + FREE_C_HEAP_ARRAY(char, dirname); + FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(char, objectname); + + if (*sizep == 0) { + size = sharedmem_filesize(rfilename, CHECK); + assert(size != 0, "unexpected size"); + } + + // Open the file mapping object with the given name + fmh = open_sharedmem_object(robjectname, ofm_access, CHECK); + + assert(fmh != INVALID_HANDLE_VALUE, "unexpected handle value"); + + // map the entire file into the address space + mapAddress = MapViewOfFile( + fmh, /* HANDLE Handle of file mapping object */ + mv_access, /* DWORD access flags */ + 0, /* DWORD High word of offset */ + 0, /* DWORD Low word of offset */ + size); /* DWORD Number of bytes to map */ + + if (mapAddress == NULL) { + if (PrintMiscellaneous && Verbose) { + warning("MapViewOfFile failed, lasterror = %d\n", GetLastError()); + } + CloseHandle(fmh); + THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), + "Could not map PerfMemory"); + } + + *addrp = (char*)mapAddress; + *sizep = size; + + // File mapping object can be closed at this time without + // invalidating the mapped view of the file + CloseHandle(fmh); + + if (PerfTraceMemOps) { + tty->print("mapped " SIZE_FORMAT " bytes for vmid %d at " + INTPTR_FORMAT "\n", size, vmid, mapAddress); + } +} + +// this method unmaps the the mapped view of the the +// file mapping object. +// +static void remove_file_mapping(char* addr) { + + // the file mapping object was closed in open_file_mapping() + // after the file map view was created. We only need to + // unmap the file view here. + UnmapViewOfFile(addr); +} + +// create the PerfData memory region in shared memory. +static char* create_shared_memory(size_t size) { + + return mapping_create_shared(size); +} + +// release a named, shared memory region +// +void delete_shared_memory(char* addr, size_t size) { + + delete_file_mapping(addr, size); +} + + + + +// create the PerfData memory region +// +// This method creates the memory region used to store performance +// data for the JVM. The memory may be created in standard or +// shared memory. +// +void PerfMemory::create_memory_region(size_t size) { + + if (PerfDisableSharedMem || !os::win32::is_nt()) { + // do not share the memory for the performance data. + PerfDisableSharedMem = true; + _start = create_standard_memory(size); + } + else { + _start = create_shared_memory(size); + if (_start == NULL) { + + // creation of the shared memory region failed, attempt + // to create a contiguous, non-shared memory region instead. + // + if (PrintMiscellaneous && Verbose) { + warning("Reverting to non-shared PerfMemory region.\n"); + } + PerfDisableSharedMem = true; + _start = create_standard_memory(size); + } + } + + if (_start != NULL) _capacity = size; + +} + +// delete the PerfData memory region +// +// This method deletes the memory region used to store performance +// data for the JVM. The memory region indicated by the +// tuple will be inaccessible after a call to this method. +// +void PerfMemory::delete_memory_region() { + + assert((start() != NULL && capacity() > 0), "verify proper state"); + + // If user specifies PerfDataSaveFile, it will save the performance data + // to the specified file name no matter whether PerfDataSaveToFile is specified + // or not. In other word, -XX:PerfDataSaveFile=.. overrides flag + // -XX:+PerfDataSaveToFile. + if (PerfDataSaveToFile || PerfDataSaveFile != NULL) { + save_memory_to_file(start(), capacity()); + } + + if (PerfDisableSharedMem) { + delete_standard_memory(start(), capacity()); + } + else { + delete_shared_memory(start(), capacity()); + } +} + +// attach to the PerfData memory region for another JVM +// +// This method returns an tuple that points to +// a memory buffer that is kept reasonably synchronized with +// the PerfData memory region for the indicated JVM. This +// buffer may be kept in synchronization via shared memory +// or some other mechanism that keeps the buffer updated. +// +// If the JVM chooses not to support the attachability feature, +// this method should throw an UnsupportedOperation exception. +// +// This implementation utilizes named shared memory to map +// the indicated process's PerfData memory region into this JVMs +// address space. +// +void PerfMemory::attach(const char* user, int vmid, PerfMemoryMode mode, + char** addrp, size_t* sizep, TRAPS) { + + if (vmid == 0 || vmid == os::current_process_id()) { + *addrp = start(); + *sizep = capacity(); + return; + } + + open_file_mapping(user, vmid, mode, addrp, sizep, CHECK); +} + +// detach from the PerfData memory region of another JVM +// +// This method detaches the PerfData memory region of another +// JVM, specified as an tuple of a buffer +// in this process's address space. This method may perform +// arbitrary actions to accomplish the detachment. The memory +// region specified by will be inaccessible after +// a call to this method. +// +// If the JVM chooses not to support the attachability feature, +// this method should throw an UnsupportedOperation exception. +// +// This implementation utilizes named shared memory to detach +// the indicated process's PerfData memory region from this +// process's address space. +// +void PerfMemory::detach(char* addr, size_t bytes, TRAPS) { + + assert(addr != 0, "address sanity check"); + assert(bytes > 0, "capacity sanity check"); + + if (PerfMemory::contains(addr) || PerfMemory::contains(addr + bytes - 1)) { + // prevent accidental detachment of this process's PerfMemory region + return; + } + + remove_file_mapping(addr); +} + +char* PerfMemory::backing_store_filename() { + return sharedmem_fileName; +} diff --git a/hotspot/src/os/windows/vm/stubRoutines_windows.cpp b/hotspot/src/os/windows/vm/stubRoutines_windows.cpp new file mode 100644 index 00000000000..d8d2b340e6c --- /dev/null +++ b/hotspot/src/os/windows/vm/stubRoutines_windows.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubRoutines_windows.cpp.incl" diff --git a/hotspot/src/os/windows/vm/threadCritical_windows.cpp b/hotspot/src/os/windows/vm/threadCritical_windows.cpp new file mode 100644 index 00000000000..239a3a1df43 --- /dev/null +++ b/hotspot/src/os/windows/vm/threadCritical_windows.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_threadCritical_windows.cpp.incl" + +// OS-includes here +# include +# include + +// +// See threadCritical.hpp for details of this class. +// + +static bool initialized = false; +static volatile jint lock_count = -1; +static HANDLE lock_event; +static DWORD lock_owner = -1; + +// +// Note that Microsoft's critical region code contains a race +// condition, and is not suitable for use. A thread holding the +// critical section cannot safely suspend a thread attempting +// to enter the critical region. The failure mode is that both +// threads are permanently suspended. +// +// I experiemented with the use of ordinary windows mutex objects +// and found them ~30 times slower than the critical region code. +// + +void ThreadCritical::initialize() { +} + +void ThreadCritical::release() { + assert(lock_owner == -1, "Mutex being deleted while owned."); + assert(lock_count == -1, "Mutex being deleted while recursively locked"); + assert(lock_event != NULL, "Sanity check"); + CloseHandle(lock_event); +} + +ThreadCritical::ThreadCritical() { + DWORD current_thread = GetCurrentThreadId(); + + if (lock_owner != current_thread) { + // Grab the lock before doing anything. + while (Atomic::cmpxchg(0, &lock_count, -1) != -1) { + if (initialized) { + DWORD ret = WaitForSingleObject(lock_event, INFINITE); + assert(ret == WAIT_OBJECT_0, "unexpected return value from WaitForSingleObject"); + } + } + + // Make sure the event object is allocated. + if (!initialized) { + // Locking will not work correctly unless this is autoreset. + lock_event = CreateEvent(NULL, false, false, NULL); + initialized = true; + } + + assert(lock_owner == -1, "Lock acquired illegally."); + lock_owner = current_thread; + } else { + // Atomicity isn't required. Bump the recursion count. + lock_count++; + } + + assert(lock_owner == GetCurrentThreadId(), "Lock acquired illegally."); +} + +ThreadCritical::~ThreadCritical() { + assert(lock_owner == GetCurrentThreadId(), "unlock attempt by wrong thread"); + assert(lock_count >= 0, "Attempt to unlock when already unlocked"); + + if (lock_count == 0) { + // We're going to unlock + lock_owner = -1; + lock_count = -1; + // No lost wakeups, lock_event stays signaled until reset. + DWORD ret = SetEvent(lock_event); + assert(ret != 0, "unexpected return value from SetEvent"); + } else { + // Just unwinding a recursive lock; + lock_count--; + } +} diff --git a/hotspot/src/os/windows/vm/thread_windows.inline.hpp b/hotspot/src/os/windows/vm/thread_windows.inline.hpp new file mode 100644 index 00000000000..2413ad3b30d --- /dev/null +++ b/hotspot/src/os/windows/vm/thread_windows.inline.hpp @@ -0,0 +1,27 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Contains inlined functions for class Thread and ThreadLocalStorage + +inline void ThreadLocalStorage::pd_invalidate_all() { return; } diff --git a/hotspot/src/os/windows/vm/version.rc b/hotspot/src/os/windows/vm/version.rc new file mode 100644 index 00000000000..68d7e13ef2d --- /dev/null +++ b/hotspot/src/os/windows/vm/version.rc @@ -0,0 +1,72 @@ +// +// Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +#include "winresrc.h" + +// Need 2 defines so macro argument to XSTR will get expanded before quoting. +#define XSTR(x) STR(x) +#define STR(x) #x + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION HS_VER + PRODUCTVERSION JDK_VER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + // FILEOS 0x4 is Win32, 0x40004 is Win32 NT only + FILEOS 0x4L + // FILETYPE should be 0x1 for .exe and 0x2 for .dll + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", XSTR(HS_COMPANY) "\0" + VALUE "FileDescription", XSTR(HS_FILEDESC) "\0" + VALUE "FileVersion", XSTR(HS_DOTVER) "\0" + VALUE "Full Version", XSTR(HS_BUILD_ID) "\0" + VALUE "InternalName", XSTR(HS_INTERNAL_NAME) "\0" + VALUE "LegalCopyright", XSTR(HS_COPYRIGHT) "\0" + VALUE "OriginalFilename", XSTR(HS_FNAME) "\0" + VALUE "ProductName", XSTR(HS_NAME) "\0" + VALUE "ProductVersion", XSTR(JDK_DOTVER) "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/hotspot/src/os/windows/vm/vmError_windows.cpp b/hotspot/src/os/windows/vm/vmError_windows.cpp new file mode 100644 index 00000000000..07ae2f406f6 --- /dev/null +++ b/hotspot/src/os/windows/vm/vmError_windows.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vmError_windows.cpp.incl" + + +void VMError::show_message_box(char *buf, int buflen) { + bool yes; + do { + error_string(buf, buflen); + int len = (int)strlen(buf); + char *p = &buf[len]; + + jio_snprintf(p, buflen - len, + "\n\n" + "Do you want to debug the problem?\n\n" + "To debug, attach Visual Studio to process %d; then switch to thread 0x%x\n" + "Select 'Yes' to launch Visual Studio automatically (PATH must include msdev)\n" + "Otherwise, select 'No' to abort...", + os::current_process_id(), os::current_thread_id()); + + yes = os::message_box("Unexpected Error", buf) != 0; + + if (yes) { + // yes, user asked VM to launch debugger + // + // os::breakpoint() calls DebugBreak(), which causes a breakpoint + // exception. If VM is running inside a debugger, the debugger will + // catch the exception. Otherwise, the breakpoint exception will reach + // the default windows exception handler, which can spawn a debugger and + // automatically attach to the dying VM. + os::breakpoint(); + yes = false; + } + } while (yes); +} + +int VMError::get_resetted_sigflags(int sig) { + return -1; +} + +address VMError::get_resetted_sighandler(int sig) { + return NULL; +} + +LONG WINAPI crash_handler(struct _EXCEPTION_POINTERS* exceptionInfo) { + DWORD exception_code = exceptionInfo->ExceptionRecord->ExceptionCode; + VMError err(NULL, exception_code, NULL, + exceptionInfo->ExceptionRecord, exceptionInfo->ContextRecord); + err.report_and_die(); + return EXCEPTION_CONTINUE_SEARCH; +} + +void VMError::reset_signal_handlers() { + SetUnhandledExceptionFilter(crash_handler); +} diff --git a/hotspot/src/os/windows/vm/vtune_windows.cpp b/hotspot/src/os/windows/vm/vtune_windows.cpp new file mode 100644 index 00000000000..40870893d1e --- /dev/null +++ b/hotspot/src/os/windows/vm/vtune_windows.cpp @@ -0,0 +1,290 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vtune_windows.cpp.incl" + +static int current_method_ID = 0; + +// ------------- iJITProf.h ------------------- +// defined by Intel -- do not change + +#include "windows.h" + +extern "C" { + enum iJITP_Event { + ExceptionOccurred_S, // Java exception + ExceptionOccurred_IDS, + + Shutdown, // VM exit + + ThreadCreate, // threads + ThreadDestroy, + ThreadSwitch, + + ClassLoadStart, // class loading + ClassLoadEnd, + + GCStart, // GC + GCEnd, + + NMethodCreate = 13, // nmethod creation + NMethodDelete + + // rest of event types omitted (call profiling not supported yet) + }; + + // version number -- 0 if VTune not installed + int WINAPI iJitP_VersionNumber(); + + enum iJITP_ModeFlags { + NoNotification = 0x0, // don't call vtune + NotifyNMethodCreate = 0x1, // notify NMethod_Create + NotifyNMethodDelete = 0x2, // notify NMethod_Create + NotifyMethodEnter = 0x4, // method entry + NotifyMethodExit = 0x8, // method exit + NotifyShutdown = 0x10, // VM exit + NotifyGC = 0x20, // GC + }; + + // call back function type + typedef void (WINAPI *ModeChangedFn)(iJITP_ModeFlags flags); + + // ------------- VTune method interfaces ---------------------- + typedef void (WINAPI *RegisterCallbackFn)(ModeChangedFn fn); // register callback + typedef int (WINAPI *NotifyEventFn)(iJITP_Event, void* event_data); + + // specific event data structures + + // data for NMethodCreate + + struct VTuneObj { // base class for allocation + // (can't use CHeapObj -- has vtable ptr) + void* operator new(size_t size) { return os::malloc(size); } + void operator delete(void* p) { fatal("never delete VTune data"); } + }; + + struct LineNumberInfo : VTuneObj { // PC-to-line number mapping + unsigned long offset; // byte offset from start of method + unsigned long line_num; // corresponding line number + }; + + struct MethodLoadInfo : VTuneObj { + unsigned long methodID; // unique method ID + const char* name; // method name + unsigned long instr_start; // start address + unsigned long instr_size; // length in bytes + unsigned long line_number_size; // size of line number table + LineNumberInfo* line_number_table; // line number mapping + unsigned long classID; // unique class ID + char* class_file_name; // fully qualified class file name + char* source_file_name; // fully qualified source file name + + MethodLoadInfo(nmethod* nm); // for real nmethods + MethodLoadInfo(const char* vm_name, address start, address end); + // for "nmethods" like stubs, interpreter, etc + + }; + + // data for NMethodDelete + struct MethodInfo : VTuneObj { + unsigned long methodID; // unique method ID + unsigned long classID; // (added for convenience -- not part of Intel interface) + + MethodInfo(methodOop m); + }; +}; + +MethodInfo::MethodInfo(methodOop m) { + // just give it a new ID -- we're not compiling methods twice (usually) + // (and even if we did, one might want to see the two versions separately) + methodID = ++current_method_ID; +} + +MethodLoadInfo::MethodLoadInfo(const char* vm_name, address start, address end) { + classID = 0; + methodID = ++current_method_ID; + name = vm_name; + instr_start = (unsigned long)start; + instr_size = end - start; + line_number_size = 0; + line_number_table = NULL; + class_file_name = source_file_name = "HotSpot JVM"; +} + +MethodLoadInfo::MethodLoadInfo(nmethod* nm) { + methodOop m = nm->method(); + MethodInfo info(m); + classID = info.classID; + methodID = info.methodID; + name = strdup(m->name()->as_C_string()); + instr_start = (unsigned long)nm->instructions_begin(); + instr_size = nm->code_size(); + line_number_size = 0; + line_number_table = NULL; + klassOop kl = m->method_holder(); + char* class_name = Klass::cast(kl)->name()->as_C_string(); + char* file_name = NEW_C_HEAP_ARRAY(char, strlen(class_name) + 1); + strcpy(file_name, class_name); + class_file_name = file_name; + char* src_name = NEW_C_HEAP_ARRAY(char, strlen(class_name) + strlen(".java") + 1); + strcpy(src_name, class_name); + strcat(src_name, ".java"); + source_file_name = src_name; +} + +// --------------------- DLL loading functions ------------------------ + +#define DLLNAME "iJitProf.dll" + +static HINSTANCE load_lib(char* name) { + HINSTANCE lib = NULL; + HKEY hk; + + // try to get VTune directory from the registry + if (RegOpenKey(HKEY_CURRENT_USER, "Software\\VB and VBA Program Settings\\VTune\\StartUp", &hk) == ERROR_SUCCESS) { + for (int i = 0; true; i++) { + char szName[MAX_PATH + 1]; + char szVal [MAX_PATH + 1]; + DWORD cbName, cbVal; + + cbName = cbVal = MAX_PATH + 1; + if (RegEnumValue(hk, i, szName, &cbName, NULL, NULL, (LPBYTE)szVal, &cbVal) == ERROR_SUCCESS) { + // get VTune directory + if (!strcmp(szName, name)) { + char*p = szVal; + while (*p == ' ') p++; // trim + char* q = p + strlen(p) - 1; + while (*q == ' ') *(q--) = '\0'; + + // chdir to the VTune dir + GetCurrentDirectory(MAX_PATH + 1, szName); + SetCurrentDirectory(p); + // load lib + lib = LoadLibrary(strcat(strcat(p, "\\"), DLLNAME)); + if (lib != NULL && WizardMode) tty->print_cr("*loaded VTune DLL %s", p); + // restore current dir + SetCurrentDirectory(szName); + break; + } + } else { + break; + } + } + } + return lib; +} + +static RegisterCallbackFn iJIT_RegisterCallback = NULL; +static NotifyEventFn iJIT_NotifyEvent = NULL; + +static bool load_iJIT_funcs() { + // first try to load from PATH + HINSTANCE lib = LoadLibrary(DLLNAME); + if (lib != NULL && WizardMode) tty->print_cr("*loaded VTune DLL %s via PATH", DLLNAME); + + // if not successful, try to look in the VTUNE directory + if (lib == NULL) lib = load_lib("VTUNEDIR30"); + if (lib == NULL) lib = load_lib("VTUNEDIR25"); + if (lib == NULL) lib = load_lib("VTUNEDIR"); + + if (lib == NULL) return false; // unsuccessful + + // try to load the functions + iJIT_RegisterCallback = (RegisterCallbackFn)GetProcAddress(lib, "iJIT_RegisterCallback"); + iJIT_NotifyEvent = (NotifyEventFn) GetProcAddress(lib, "iJIT_NotifyEvent"); + + if (!iJIT_RegisterCallback) tty->print_cr("*couldn't find VTune entry point iJIT_RegisterCallback"); + if (!iJIT_NotifyEvent) tty->print_cr("*couldn't find VTune entry point iJIT_NotifyEvent"); + return iJIT_RegisterCallback != NULL && iJIT_NotifyEvent != NULL; +} + +// --------------------- VTune class ------------------------ + +static bool active = false; +static int flags = 0; + +void VTune::start_GC() { + if (active && (flags & NotifyGC)) iJIT_NotifyEvent(GCStart, NULL); +} + +void VTune::end_GC() { + if (active && (flags & NotifyGC)) iJIT_NotifyEvent(GCEnd, NULL); +} + +void VTune::start_class_load() { + // not yet implemented in VTune +} + +void VTune::end_class_load() { + // not yet implemented in VTune +} + +void VTune::exit() { + if (active && (flags & NotifyShutdown)) iJIT_NotifyEvent(Shutdown, NULL); +} + +void VTune::register_stub(const char* name, address start, address end) { + if (flags & NotifyNMethodCreate) { + MethodLoadInfo* info = new MethodLoadInfo(name, start, end); + if (PrintMiscellaneous && WizardMode && Verbose) { + tty->print_cr("NMethodCreate %s (%d): %#x..%#x", info->name, info->methodID, + info->instr_start, info->instr_start + info->instr_size); + } + iJIT_NotifyEvent(NMethodCreate, info); + } +} + +void VTune::create_nmethod(nmethod* nm) { + if (flags & NotifyNMethodCreate) { + MethodLoadInfo* info = new MethodLoadInfo(nm); + if (PrintMiscellaneous && WizardMode && Verbose) { + tty->print_cr("NMethodCreate %s (%d): %#x..%#x", info->name, info->methodID, + info->instr_start, info->instr_start + info->instr_size); + } + iJIT_NotifyEvent(NMethodCreate, info); + } +} + +void VTune::delete_nmethod(nmethod* nm) { + if (flags & NotifyNMethodDelete) { + MethodInfo* info = new MethodInfo(nm->method()); + iJIT_NotifyEvent(NMethodDelete, info); + } +} + +static void set_flags(int new_flags) { + flags = new_flags; + // if (WizardMode) tty->print_cr("*new VTune flags: %#x", flags); +} + +void vtune_init() { + if (!UseVTune) return; + active = load_iJIT_funcs(); + if (active) { + iJIT_RegisterCallback((ModeChangedFn)set_flags); + } else { + assert(flags == 0, "flags shouldn't be set"); + } +} diff --git a/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_32.cpp b/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_32.cpp new file mode 100644 index 00000000000..e35442d61b5 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_32.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler_linux_x86_32.cpp.incl" + +void MacroAssembler::int3() { + call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); +} + +void MacroAssembler::get_thread(Register thread) { + movl(thread, rsp); + shrl(thread, PAGE_SHIFT); + + ExternalAddress tls_base((address)ThreadLocalStorage::sp_map_addr()); + Address index(noreg, thread, Address::times_4); + ArrayAddress tls(tls_base, index); + + movptr(thread, tls); +} + +bool MacroAssembler::needs_explicit_null_check(int offset) { + // Linux kernel guarantees that the first page is always unmapped. Don't + // assume anything more than that. + bool offset_in_first_page = 0 <= offset && offset < os::vm_page_size(); + return !offset_in_first_page; +} diff --git a/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_64.cpp b/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_64.cpp new file mode 100644 index 00000000000..02ad9e4bdd8 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_64.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler_linux_x86_64.cpp.incl" + +void MacroAssembler::int3() { + call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); +} + +void MacroAssembler::get_thread(Register thread) { + // call pthread_getspecific + // void * pthread_getspecific(pthread_key_t key); + if (thread != rax) { + pushq(rax); + } + pushq(rdi); + pushq(rsi); + pushq(rdx); + pushq(rcx); + pushq(r8); + pushq(r9); + pushq(r10); + // XXX + movq(r10, rsp); + andq(rsp, -16); + pushq(r10); + pushq(r11); + + movl(rdi, ThreadLocalStorage::thread_index()); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, pthread_getspecific))); + + popq(r11); + popq(rsp); + popq(r10); + popq(r9); + popq(r8); + popq(rcx); + popq(rdx); + popq(rsi); + popq(rdi); + if (thread != rax) { + movq(thread, rax); + popq(rax); + } +} + +// NOTE: since the linux kernel resides at the low end of +// user address space, no null pointer check is needed. +bool MacroAssembler::needs_explicit_null_check(int offset) { + return offset < 0 || offset >= 0x100000; +} diff --git a/hotspot/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp b/hotspot/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp new file mode 100644 index 00000000000..e31da3977dc --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp @@ -0,0 +1,195 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Implementation of class atomic + +inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, jint* dest) { *dest = store_value; } +inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, void* dest) { *(void**)dest = store_value; } + +inline void Atomic::store (jbyte store_value, volatile jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, volatile jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, volatile jint* dest) { *dest = store_value; } +inline void Atomic::store_ptr(intptr_t store_value, volatile intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, volatile void* dest) { *(void* volatile *)dest = store_value; } + + +// Adding a lock prefix to an instruction on MP machine +#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: " + +inline jint Atomic::add (jint add_value, volatile jint* dest) { + jint addend = add_value; + int mp = os::is_MP(); + __asm__ volatile ( LOCK_IF_MP(%3) "xaddl %0,(%2)" + : "=r" (addend) + : "0" (addend), "r" (dest), "r" (mp) + : "cc", "memory"); + return addend + add_value; +} + +inline void Atomic::inc (volatile jint* dest) { + int mp = os::is_MP(); + __asm__ volatile (LOCK_IF_MP(%1) "addl $1,(%0)" : + : "r" (dest), "r" (mp) : "cc", "memory"); +} + +inline void Atomic::inc_ptr(volatile void* dest) { + inc_ptr((volatile intptr_t*)dest); +} + +inline void Atomic::dec (volatile jint* dest) { + int mp = os::is_MP(); + __asm__ volatile (LOCK_IF_MP(%1) "subl $1,(%0)" : + : "r" (dest), "r" (mp) : "cc", "memory"); +} + +inline void Atomic::dec_ptr(volatile void* dest) { + dec_ptr((volatile intptr_t*)dest); +} + +inline jint Atomic::xchg (jint exchange_value, volatile jint* dest) { + __asm__ volatile ( "xchgl (%2),%0" + : "=r" (exchange_value) + : "0" (exchange_value), "r" (dest) + : "memory"); + return exchange_value; +} + +inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { + return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest); +} + + +inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { + int mp = os::is_MP(); + __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)" + : "=a" (exchange_value) + : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp) + : "cc", "memory"); + return exchange_value; +} + +extern "C" { + // defined in linux_x86.s + jlong _Atomic_cmpxchg_long(jlong, volatile jlong*, jlong, bool); +} + +#ifdef AMD64 +inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } +inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { + intptr_t addend = add_value; + bool mp = os::is_MP(); + __asm__ __volatile__ (LOCK_IF_MP(%3) "xaddq %0,(%2)" + : "=r" (addend) + : "0" (addend), "r" (dest), "r" (mp) + : "cc", "memory"); + return addend + add_value; +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)add_ptr(add_value, (volatile intptr_t*)dest); +} + +inline void Atomic::inc_ptr(volatile intptr_t* dest) { + bool mp = os::is_MP(); + __asm__ __volatile__ (LOCK_IF_MP(%1) "addq $1,(%0)" + : + : "r" (dest), "r" (mp) + : "cc", "memory"); +} + +inline void Atomic::dec_ptr(volatile intptr_t* dest) { + bool mp = os::is_MP(); + __asm__ __volatile__ (LOCK_IF_MP(%1) "subq $1,(%0)" + : + : "r" (dest), "r" (mp) + : "cc", "memory"); +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { + __asm__ __volatile__ ("xchgq (%2),%0" + : "=r" (exchange_value) + : "0" (exchange_value), "r" (dest) + : "memory"); + return exchange_value; +} + +inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) { + bool mp = os::is_MP(); + __asm__ __volatile__ (LOCK_IF_MP(%4) "cmpxchgq %1,(%3)" + : "=a" (exchange_value) + : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp) + : "cc", "memory"); + return exchange_value; +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { + return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value); +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value); +} + +#else +//inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } +//inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { + return (intptr_t)Atomic::add((jint)add_value, (volatile jint*)dest); +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)Atomic::add((jint)add_value, (volatile jint*)dest); +} + + +inline void Atomic::inc_ptr(volatile intptr_t* dest) { + inc((volatile jint*)dest); +} + +inline void Atomic::dec_ptr(volatile intptr_t* dest) { + dec((volatile jint*)dest); +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { + return (intptr_t)xchg((jint)exchange_value, (volatile jint*)dest); +} + +inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) { + return _Atomic_cmpxchg_long(exchange_value, dest, compare_value, os::is_MP()); +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { + return (intptr_t)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value); +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value); +} +#endif // AMD64 diff --git a/hotspot/src/os_cpu/linux_x86/vm/bytes_linux_x86.inline.hpp b/hotspot/src/os_cpu/linux_x86/vm/bytes_linux_x86.inline.hpp new file mode 100644 index 00000000000..1368a4c9c66 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/bytes_linux_x86.inline.hpp @@ -0,0 +1,74 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include + +// Efficient swapping of data bytes from Java byte +// ordering to native byte ordering and vice versa. +inline u2 Bytes::swap_u2(u2 x) { +#ifdef AMD64 + return bswap_16(x); +#else + u2 ret; + __asm__ __volatile__ ( + "movw %0, %%ax;" + "xchg %%al, %%ah;" + "movw %%ax, %0" + :"=r" (ret) // output : register 0 => ret + :"0" (x) // input : x => register 0 + :"ax", "0" // clobbered registers + ); + return ret; +#endif // AMD64 +} + +inline u4 Bytes::swap_u4(u4 x) { +#ifdef AMD64 + return bswap_32(x); +#else + u4 ret; + __asm__ __volatile__ ( + "bswap %0" + :"=r" (ret) // output : register 0 => ret + :"0" (x) // input : x => register 0 + :"0" // clobbered register + ); + return ret; +#endif // AMD64 +} + +#ifdef AMD64 +inline u8 Bytes::swap_u8(u8 x) { + return bswap_64(x); +} +#else +// Helper function for swap_u8 +inline u8 Bytes::swap_u8_base(u4 x, u4 y) { + return (((u8)swap_u4(x))<<32) | swap_u4(y); +} + +inline u8 Bytes::swap_u8(u8 x) { + return swap_u8_base(*(u4*)&x, *(((u4*)&x)+1)); +} +#endif // !AMD64 diff --git a/hotspot/src/os_cpu/linux_x86/vm/copy_linux_x86.inline.hpp b/hotspot/src/os_cpu/linux_x86/vm/copy_linux_x86.inline.hpp new file mode 100644 index 00000000000..b5b7cd27470 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/copy_linux_x86.inline.hpp @@ -0,0 +1,361 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +static void pd_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + (void)memmove(to, from, count * HeapWordSize); +#else + // Same as pd_aligned_conjoint_words, except includes a zero-count check. + intx temp; + __asm__ volatile(" testl %6,%6 ;" + " jz 7f ;" + " cmpl %4,%5 ;" + " leal -4(%4,%6,4),%3;" + " jbe 1f ;" + " cmpl %7,%5 ;" + " jbe 4f ;" + "1: cmpl $32,%6 ;" + " ja 3f ;" + " subl %4,%1 ;" + "2: movl (%4),%3 ;" + " movl %7,(%5,%4,1) ;" + " addl $4,%0 ;" + " subl $1,%2 ;" + " jnz 2b ;" + " jmp 7f ;" + "3: rep; smovl ;" + " jmp 7f ;" + "4: cmpl $32,%2 ;" + " movl %7,%0 ;" + " leal -4(%5,%6,4),%1;" + " ja 6f ;" + " subl %4,%1 ;" + "5: movl (%4),%3 ;" + " movl %7,(%5,%4,1) ;" + " subl $4,%0 ;" + " subl $1,%2 ;" + " jnz 5b ;" + " jmp 7f ;" + "6: std ;" + " rep; smovl ;" + " cld ;" + "7: nop " + : "=S" (from), "=D" (to), "=c" (count), "=r" (temp) + : "0" (from), "1" (to), "2" (count), "3" (temp) + : "memory", "flags"); +#endif // AMD64 +} + +static void pd_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + switch (count) { + case 8: to[7] = from[7]; + case 7: to[6] = from[6]; + case 6: to[5] = from[5]; + case 5: to[4] = from[4]; + case 4: to[3] = from[3]; + case 3: to[2] = from[2]; + case 2: to[1] = from[1]; + case 1: to[0] = from[0]; + case 0: break; + default: + (void)memcpy(to, from, count * HeapWordSize); + break; + } +#else + // Same as pd_aligned_disjoint_words, except includes a zero-count check. + intx temp; + __asm__ volatile(" testl %6,%6 ;" + " jz 3f ;" + " cmpl $32,%6 ;" + " ja 2f ;" + " subl %4,%1 ;" + "1: movl (%4),%3 ;" + " movl %7,(%5,%4,1);" + " addl $4,%0 ;" + " subl $1,%2 ;" + " jnz 1b ;" + " jmp 3f ;" + "2: rep; smovl ;" + "3: nop " + : "=S" (from), "=D" (to), "=c" (count), "=r" (temp) + : "0" (from), "1" (to), "2" (count), "3" (temp) + : "memory", "cc"); +#endif // AMD64 +} + +static void pd_disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + switch (count) { + case 8: to[7] = from[7]; + case 7: to[6] = from[6]; + case 6: to[5] = from[5]; + case 5: to[4] = from[4]; + case 4: to[3] = from[3]; + case 3: to[2] = from[2]; + case 2: to[1] = from[1]; + case 1: to[0] = from[0]; + case 0: break; + default: + while (count-- > 0) { + *to++ = *from++; + } + break; + } +#else + // pd_disjoint_words is word-atomic in this implementation. + pd_disjoint_words(from, to, count); +#endif // AMD64 +} + +static void pd_aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + (void)memmove(to, from, count * HeapWordSize); +#else + // Same as pd_conjoint_words, except no zero-count check. + intx temp; + __asm__ volatile(" cmpl %4,%5 ;" + " leal -4(%4,%6,4),%3;" + " jbe 1f ;" + " cmpl %7,%5 ;" + " jbe 4f ;" + "1: cmpl $32,%6 ;" + " ja 3f ;" + " subl %4,%1 ;" + "2: movl (%4),%3 ;" + " movl %7,(%5,%4,1) ;" + " addl $4,%0 ;" + " subl $1,%2 ;" + " jnz 2b ;" + " jmp 7f ;" + "3: rep; smovl ;" + " jmp 7f ;" + "4: cmpl $32,%2 ;" + " movl %7,%0 ;" + " leal -4(%5,%6,4),%1;" + " ja 6f ;" + " subl %4,%1 ;" + "5: movl (%4),%3 ;" + " movl %7,(%5,%4,1) ;" + " subl $4,%0 ;" + " subl $1,%2 ;" + " jnz 5b ;" + " jmp 7f ;" + "6: std ;" + " rep; smovl ;" + " cld ;" + "7: nop " + : "=S" (from), "=D" (to), "=c" (count), "=r" (temp) + : "0" (from), "1" (to), "2" (count), "3" (temp) + : "memory", "flags"); +#endif // AMD64 +} + +static void pd_aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + pd_disjoint_words(from, to, count); +#else + // Same as pd_disjoint_words, except no zero-count check. + intx temp; + __asm__ volatile(" cmpl $32,%6 ;" + " ja 2f ;" + " subl %4,%1 ;" + "1: movl (%4),%3 ;" + " movl %7,(%5,%4,1);" + " addl $4,%0 ;" + " subl $1,%2 ;" + " jnz 1b ;" + " jmp 3f ;" + "2: rep; smovl ;" + "3: nop " + : "=S" (from), "=D" (to), "=c" (count), "=r" (temp) + : "0" (from), "1" (to), "2" (count), "3" (temp) + : "memory", "cc"); +#endif // AMD64 +} + +static void pd_conjoint_bytes(void* from, void* to, size_t count) { +#ifdef AMD64 + (void)memmove(to, from, count); +#else + intx temp; + __asm__ volatile(" testl %6,%6 ;" + " jz 13f ;" + " cmpl %4,%5 ;" + " leal -1(%4,%6),%3 ;" + " jbe 1f ;" + " cmpl %7,%5 ;" + " jbe 8f ;" + "1: cmpl $3,%6 ;" + " jbe 6f ;" + " movl %6,%3 ;" + " movl $4,%2 ;" + " subl %4,%2 ;" + " andl $3,%2 ;" + " jz 2f ;" + " subl %6,%3 ;" + " rep; smovb ;" + "2: movl %7,%2 ;" + " shrl $2,%2 ;" + " jz 5f ;" + " cmpl $32,%2 ;" + " ja 4f ;" + " subl %4,%1 ;" + "3: movl (%4),%%edx ;" + " movl %%edx,(%5,%4,1);" + " addl $4,%0 ;" + " subl $1,%2 ;" + " jnz 3b ;" + " addl %4,%1 ;" + " jmp 5f ;" + "4: rep; smovl ;" + "5: movl %7,%2 ;" + " andl $3,%2 ;" + " jz 13f ;" + "6: xorl %7,%3 ;" + "7: movb (%4,%7,1),%%dl ;" + " movb %%dl,(%5,%7,1) ;" + " addl $1,%3 ;" + " subl $1,%2 ;" + " jnz 7b ;" + " jmp 13f ;" + "8: std ;" + " cmpl $12,%2 ;" + " ja 9f ;" + " movl %7,%0 ;" + " leal -1(%6,%5),%1 ;" + " jmp 11f ;" + "9: xchgl %3,%2 ;" + " movl %6,%0 ;" + " addl $1,%2 ;" + " leal -1(%7,%5),%1 ;" + " andl $3,%2 ;" + " jz 10f ;" + " subl %6,%3 ;" + " rep; smovb ;" + "10: movl %7,%2 ;" + " subl $3,%0 ;" + " shrl $2,%2 ;" + " subl $3,%1 ;" + " rep; smovl ;" + " andl $3,%3 ;" + " jz 12f ;" + " movl %7,%2 ;" + " addl $3,%0 ;" + " addl $3,%1 ;" + "11: rep; smovb ;" + "12: cld ;" + "13: nop ;" + : "=S" (from), "=D" (to), "=c" (count), "=r" (temp) + : "0" (from), "1" (to), "2" (count), "3" (temp) + : "memory", "flags", "%edx"); +#endif // AMD64 +} + +static void pd_conjoint_bytes_atomic(void* from, void* to, size_t count) { + pd_conjoint_bytes(from, to, count); +} + +static void pd_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { + _Copy_conjoint_jshorts_atomic(from, to, count); +} + +static void pd_conjoint_jints_atomic(jint* from, jint* to, size_t count) { +#ifdef AMD64 + _Copy_conjoint_jints_atomic(from, to, count); +#else + assert(HeapWordSize == BytesPerInt, "heapwords and jints must be the same size"); + // pd_conjoint_words is word-atomic in this implementation. + pd_conjoint_words((HeapWord*)from, (HeapWord*)to, count); +#endif // AMD64 +} + +static void pd_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { +#ifdef AMD64 + _Copy_conjoint_jlongs_atomic(from, to, count); +#else + // Guarantee use of fild/fistp or xmm regs via some asm code, because compilers won't. + if (from > to) { + while (count-- > 0) { + __asm__ volatile("fildll (%0); fistpll (%1)" + : + : "r" (from), "r" (to) + : "memory" ); + ++from; + ++to; + } + } else { + while (count-- > 0) { + __asm__ volatile("fildll (%0,%2,8); fistpll (%1,%2,8)" + : + : "r" (from), "r" (to), "r" (count) + : "memory" ); + } + } +#endif // AMD64 +} + +static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) { +#ifdef AMD64 + assert(BytesPerLong == BytesPerOop, "jlongs and oops must be the same size"); + _Copy_conjoint_jlongs_atomic((jlong*)from, (jlong*)to, count); +#else + assert(HeapWordSize == BytesPerOop, "heapwords and oops must be the same size"); + // pd_conjoint_words is word-atomic in this implementation. + pd_conjoint_words((HeapWord*)from, (HeapWord*)to, count); +#endif // AMD64 +} + +static void pd_arrayof_conjoint_bytes(HeapWord* from, HeapWord* to, size_t count) { + _Copy_arrayof_conjoint_bytes(from, to, count); +} + +static void pd_arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count) { + _Copy_arrayof_conjoint_jshorts(from, to, count); +} + +static void pd_arrayof_conjoint_jints(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + _Copy_arrayof_conjoint_jints(from, to, count); +#else + pd_conjoint_jints_atomic((jint*)from, (jint*)to, count); +#endif // AMD64 +} + +static void pd_arrayof_conjoint_jlongs(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + _Copy_arrayof_conjoint_jlongs(from, to, count); +#else + pd_conjoint_jlongs_atomic((jlong*)from, (jlong*)to, count); +#endif // AMD64 +} + +static void pd_arrayof_conjoint_oops(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + assert(BytesPerLong == BytesPerOop, "jlongs and oops must be the same size"); + _Copy_arrayof_conjoint_jlongs(from, to, count); +#else + pd_conjoint_oops_atomic((oop*)from, (oop*)to, count); +#endif // AMD64 +} diff --git a/hotspot/src/os_cpu/linux_x86/vm/globals_linux_x86.hpp b/hotspot/src/os_cpu/linux_x86/vm/globals_linux_x86.hpp new file mode 100644 index 00000000000..317993ce58f --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/globals_linux_x86.hpp @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for platform dependent flags used by the runtime system. +// (see globals.hpp) +// +define_pd_global(bool, DontYieldALot, false); +#ifdef AMD64 +define_pd_global(intx, ThreadStackSize, 1024); // 0 => use system default +define_pd_global(intx, VMThreadStackSize, 1024); +#else +// ThreadStackSize 320 allows TaggedStackInterpreter and a couple of test cases +// to run while keeping the number of threads that can be created high. +// System default ThreadStackSize appears to be 512 which is too big. +define_pd_global(intx, ThreadStackSize, 320); +define_pd_global(intx, VMThreadStackSize, 512); +#endif // AMD64 + +define_pd_global(intx, CompilerThreadStackSize, 0); +define_pd_global(intx, SurvivorRatio, 8); + +define_pd_global(uintx, JVMInvokeMethodSlack, 8192); + +// Only used on 64 bit Windows platforms +define_pd_global(bool, UseVectoredExceptions, false); diff --git a/hotspot/src/os_cpu/linux_x86/vm/linux_x86_32.ad b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_32.ad new file mode 100644 index 00000000000..ec4725a1cbc --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_32.ad @@ -0,0 +1,160 @@ +// +// Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// X86 Linux Architecture Description File + +//----------OS-DEPENDENT ENCODING BLOCK----------------------------------------------------- +// This block specifies the encoding classes used by the compiler to output +// byte streams. Encoding classes generate functions which are called by +// Machine Instruction Nodes in order to generate the bit encoding of the +// instruction. Operands specify their base encoding interface with the +// interface keyword. There are currently supported four interfaces, +// REG_INTER, CONST_INTER, MEMORY_INTER, & COND_INTER. REG_INTER causes an +// operand to generate a function which returns its register number when +// queried. CONST_INTER causes an operand to generate a function which +// returns the value of the constant when queried. MEMORY_INTER causes an +// operand to generate four functions which return the Base Register, the +// Index Register, the Scale Value, and the Offset Value of the operand when +// queried. COND_INTER causes an operand to generate six functions which +// return the encoding code (ie - encoding bits for the instruction) +// associated with each basic boolean condition for a conditional instruction. +// Instructions specify two basic values for encoding. They use the +// ins_encode keyword to specify their encoding class (which must be one of +// the class names specified in the encoding block), and they use the +// opcode keyword to specify, in order, their primary, secondary, and +// tertiary opcode. Only the opcode sections which a particular instruction +// needs for encoding need to be specified. +encode %{ + // Build emit functions for each basic byte or larger field in the intel + // encoding scheme (opcode, rm, sib, immediate), and call them from C++ + // code in the enc_class source block. Emit functions will live in the + // main source block for now. In future, we can generalize this by + // adding a syntax that specifies the sizes of fields in an order, + // so that the adlc can build the emit functions automagically + + enc_class linux_tlsencode (eRegP dst) %{ + Register dstReg = as_Register($dst$$reg); + MacroAssembler* masm = new MacroAssembler(&cbuf); + masm->get_thread(dstReg); + %} + + enc_class linux_breakpoint %{ + MacroAssembler* masm = new MacroAssembler(&cbuf); + masm->call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); + %} + + enc_class call_epilog %{ + if( VerifyStackAtCalls ) { + // Check that stack depth is unchanged: find majik cookie on stack + int framesize = ra_->reg2offset_unchecked(OptoReg::add(ra_->_matcher._old_SP,-3*VMRegImpl::slots_per_word)); + if(framesize >= 128) { + emit_opcode(cbuf, 0x81); // cmp [esp+0],0xbadb1ood + emit_d8(cbuf,0xBC); + emit_d8(cbuf,0x24); + emit_d32(cbuf,framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } + else { + emit_opcode(cbuf, 0x81); // cmp [esp+0],0xbadb1ood + emit_d8(cbuf,0x7C); + emit_d8(cbuf,0x24); + emit_d8(cbuf,framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } + // jmp EQ around INT3 + // QQQ TODO + const int jump_around = 5; // size of call to breakpoint, 1 for CC + emit_opcode(cbuf,0x74); + emit_d8(cbuf, jump_around); + // QQQ temporary + emit_break(cbuf); + // Die if stack mismatch + // emit_opcode(cbuf,0xCC); + } + %} + +%} + +// INSTRUCTIONS -- Platform dependent + +//----------OS and Locking Instructions---------------------------------------- + +// This name is KNOWN by the ADLC and cannot be changed. +// The ADLC forces a 'TypeRawPtr::BOTTOM' output type +// for this guy. +instruct tlsLoadP(eAXRegP dst, eFlagsReg cr) %{ + match(Set dst (ThreadLocal)); + effect(DEF dst, KILL cr); + + format %{ "MOV EAX, Thread::current()" %} + ins_encode( linux_tlsencode(dst) ); + ins_pipe( ialu_reg_fat ); +%} + +instruct TLS(eAXRegP dst) %{ + match(Set dst (ThreadLocal)); + + expand %{ + tlsLoadP(dst); + %} +%} + +// Die now +instruct ShouldNotReachHere( ) +%{ + match(Halt); + + // Use the following format syntax + format %{ "INT3 ; ShouldNotReachHere" %} + // QQQ TODO for now call breakpoint + // opcode(0xCC); + // ins_encode(Opc); + ins_encode(linux_breakpoint); + ins_pipe( pipe_slow ); +%} + + + +// Platform dependent source + +source %{ + +// emit an interrupt that is caught by the debugger +void emit_break(CodeBuffer &cbuf) { + + // Debugger doesn't really catch this but best we can do so far QQQ + MacroAssembler* masm = new MacroAssembler(&cbuf); + masm->call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); +} + +void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + emit_break(cbuf); +} + + +uint MachBreakpointNode::size(PhaseRegAlloc *ra_) const { + return 5; +} + +%} diff --git a/hotspot/src/os_cpu/linux_x86/vm/linux_x86_32.s b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_32.s new file mode 100644 index 00000000000..260822d37b2 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_32.s @@ -0,0 +1,652 @@ +# +# Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + + + # NOTE WELL! The _Copy functions are called directly + # from server-compiler-generated code via CallLeafNoFP, + # which means that they *must* either not use floating + # point or use it in the same manner as does the server + # compiler. + + .globl _Copy_conjoint_bytes + .globl _Copy_arrayof_conjoint_bytes + .globl _Copy_conjoint_jshorts_atomic + .globl _Copy_arrayof_conjoint_jshorts + .globl _Copy_conjoint_jints_atomic + .globl _Copy_arrayof_conjoint_jints + .globl _Copy_conjoint_jlongs_atomic + .globl _mmx_Copy_arrayof_conjoint_jshorts + + .globl _Atomic_cmpxchg_long + + .text + + .globl SafeFetch32, Fetch32PFI, Fetch32Resume + .globl SafeFetchN + ## TODO: avoid exposing Fetch32PFI and Fetch32Resume. + ## Instead, the signal handler would call a new SafeFetchTriage(FaultingEIP) + ## routine to vet the address. If the address is the faulting LD then + ## SafeFetchTriage() would return the resume-at EIP, otherwise null. + .type SafeFetch32,@function + .p2align 4,,15 +SafeFetch32: +SafeFetchN: + movl 0x8(%esp), %eax + movl 0x4(%esp), %ecx +Fetch32PFI: + movl (%ecx), %eax +Fetch32Resume: + ret + + + .globl SpinPause + .type SpinPause,@function + .p2align 4,,15 +SpinPause: + rep + nop + movl $1, %eax + ret + + # Support for void Copy::conjoint_bytes(void* from, + # void* to, + # size_t count) + .p2align 4,,15 + .type _Copy_conjoint_bytes,@function +_Copy_conjoint_bytes: + pushl %esi + movl 4+12(%esp),%ecx # count + pushl %edi + movl 8+ 4(%esp),%esi # from + movl 8+ 8(%esp),%edi # to + cmpl %esi,%edi + leal -1(%esi,%ecx),%eax # from + count - 1 + jbe cb_CopyRight + cmpl %eax,%edi + jbe cb_CopyLeft + # copy from low to high +cb_CopyRight: + cmpl $3,%ecx + jbe 5f # <= 3 bytes + # align source address at dword address boundary + movl %ecx,%eax # original count + movl $4,%ecx + subl %esi,%ecx + andl $3,%ecx # prefix byte count + jz 1f # no prefix + subl %ecx,%eax # byte count less prefix + # copy prefix + subl %esi,%edi +0: movb (%esi),%dl + movb %dl,(%edi,%esi,1) + addl $1,%esi + subl $1,%ecx + jnz 0b + addl %esi,%edi +1: movl %eax,%ecx # byte count less prefix + shrl $2,%ecx # dword count + jz 4f # no dwords to move + cmpl $32,%ecx + jbe 2f # <= 32 dwords + # copy aligned dwords + rep; smovl + jmp 4f + # copy aligned dwords +2: subl %esi,%edi + .p2align 4,,15 +3: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 3b + addl %esi,%edi +4: movl %eax,%ecx # byte count less prefix + andl $3,%ecx # suffix byte count + jz 7f # no suffix + # copy suffix +5: xorl %eax,%eax +6: movb (%esi,%eax,1),%dl + movb %dl,(%edi,%eax,1) + addl $1,%eax + subl $1,%ecx + jnz 6b +7: popl %edi + popl %esi + ret + # copy from high to low +cb_CopyLeft: + std + leal -4(%edi,%ecx),%edi # to + count - 4 + movl %eax,%esi # from + count - 1 + movl %ecx,%eax + subl $3,%esi # from + count - 4 + cmpl $3,%ecx + jbe 5f # <= 3 bytes +1: shrl $2,%ecx # dword count + jz 4f # no dwords to move + cmpl $32,%ecx + ja 3f # > 32 dwords + # copy dwords, aligned or not + subl %esi,%edi + .p2align 4,,15 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + subl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 4f + # copy dwords, aligned or not +3: rep; smovl +4: movl %eax,%ecx # byte count + andl $3,%ecx # suffix byte count + jz 7f # no suffix + # copy suffix +5: subl %esi,%edi + addl $3,%esi +6: movb (%esi),%dl + movb %dl,(%edi,%esi,1) + subl $1,%esi + subl $1,%ecx + jnz 6b +7: cld + popl %edi + popl %esi + ret + + # Support for void Copy::arrayof_conjoint_bytes(void* from, + # void* to, + # size_t count) + # + # Same as _Copy_conjoint_bytes, except no source alignment check. + .p2align 4,,15 + .type _Copy_arrayof_conjoint_bytes,@function +_Copy_arrayof_conjoint_bytes: + pushl %esi + movl 4+12(%esp),%ecx # count + pushl %edi + movl 8+ 4(%esp),%esi # from + movl 8+ 8(%esp),%edi # to + cmpl %esi,%edi + leal -1(%esi,%ecx),%eax # from + count - 1 + jbe acb_CopyRight + cmpl %eax,%edi + jbe acb_CopyLeft + # copy from low to high +acb_CopyRight: + cmpl $3,%ecx + jbe 5f +1: movl %ecx,%eax + shrl $2,%ecx + jz 4f + cmpl $32,%ecx + ja 3f + # copy aligned dwords + subl %esi,%edi + .p2align 4,,15 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 4f + # copy aligned dwords +3: rep; smovl +4: movl %eax,%ecx + andl $3,%ecx + jz 7f + # copy suffix +5: xorl %eax,%eax +6: movb (%esi,%eax,1),%dl + movb %dl,(%edi,%eax,1) + addl $1,%eax + subl $1,%ecx + jnz 6b +7: popl %edi + popl %esi + ret +acb_CopyLeft: + std + leal -4(%edi,%ecx),%edi # to + count - 4 + movl %eax,%esi # from + count - 1 + movl %ecx,%eax + subl $3,%esi # from + count - 4 + cmpl $3,%ecx + jbe 5f +1: shrl $2,%ecx + jz 4f + cmpl $32,%ecx + jbe 2f # <= 32 dwords + rep; smovl + jmp 4f + .=.+8 +2: subl %esi,%edi + .p2align 4,,15 +3: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + subl $4,%esi + subl $1,%ecx + jnz 3b + addl %esi,%edi +4: movl %eax,%ecx + andl $3,%ecx + jz 7f +5: subl %esi,%edi + addl $3,%esi +6: movb (%esi),%dl + movb %dl,(%edi,%esi,1) + subl $1,%esi + subl $1,%ecx + jnz 6b +7: cld + popl %edi + popl %esi + ret + + # Support for void Copy::conjoint_jshorts_atomic(void* from, + # void* to, + # size_t count) + .p2align 4,,15 + .type _Copy_conjoint_jshorts_atomic,@function +_Copy_conjoint_jshorts_atomic: + pushl %esi + movl 4+12(%esp),%ecx # count + pushl %edi + movl 8+ 4(%esp),%esi # from + movl 8+ 8(%esp),%edi # to + cmpl %esi,%edi + leal -2(%esi,%ecx,2),%eax # from + count*2 - 2 + jbe cs_CopyRight + cmpl %eax,%edi + jbe cs_CopyLeft + # copy from low to high +cs_CopyRight: + # align source address at dword address boundary + movl %esi,%eax # original from + andl $3,%eax # either 0 or 2 + jz 1f # no prefix + # copy prefix + movw (%esi),%dx + movw %dx,(%edi) + addl %eax,%esi # %eax == 2 + addl %eax,%edi + subl $1,%ecx +1: movl %ecx,%eax # word count less prefix + sarl %ecx # dword count + jz 4f # no dwords to move + cmpl $32,%ecx + jbe 2f # <= 32 dwords + # copy aligned dwords + rep; smovl + jmp 4f + # copy aligned dwords +2: subl %esi,%edi + .p2align 4,,15 +3: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 3b + addl %esi,%edi +4: andl $1,%eax # suffix count + jz 5f # no suffix + # copy suffix + movw (%esi),%dx + movw %dx,(%edi) +5: popl %edi + popl %esi + ret + # copy from high to low +cs_CopyLeft: + std + leal -4(%edi,%ecx,2),%edi # to + count*2 - 4 + movl %eax,%esi # from + count*2 - 2 + movl %ecx,%eax + subl $2,%esi # from + count*2 - 4 +1: sarl %ecx # dword count + jz 4f # no dwords to move + cmpl $32,%ecx + ja 3f # > 32 dwords + subl %esi,%edi + .p2align 4,,15 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + subl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 4f +3: rep; smovl +4: andl $1,%eax # suffix count + jz 5f # no suffix + # copy suffix + addl $2,%esi + addl $2,%edi + movw (%esi),%dx + movw %dx,(%edi) +5: cld + popl %edi + popl %esi + ret + + # Support for void Copy::arrayof_conjoint_jshorts(void* from, + # void* to, + # size_t count) + .p2align 4,,15 + .type _Copy_arrayof_conjoint_jshorts,@function +_Copy_arrayof_conjoint_jshorts: + pushl %esi + movl 4+12(%esp),%ecx # count + pushl %edi + movl 8+ 4(%esp),%esi # from + movl 8+ 8(%esp),%edi # to + cmpl %esi,%edi + leal -2(%esi,%ecx,2),%eax # from + count*2 - 2 + jbe acs_CopyRight + cmpl %eax,%edi + jbe acs_CopyLeft +acs_CopyRight: + movl %ecx,%eax # word count + sarl %ecx # dword count + jz 4f # no dwords to move + cmpl $32,%ecx + jbe 2f # <= 32 dwords + # copy aligned dwords + rep; smovl + jmp 4f + # copy aligned dwords + .=.+5 +2: subl %esi,%edi + .p2align 4,,15 +3: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 3b + addl %esi,%edi +4: andl $1,%eax # suffix count + jz 5f # no suffix + # copy suffix + movw (%esi),%dx + movw %dx,(%edi) +5: popl %edi + popl %esi + ret +acs_CopyLeft: + std + leal -4(%edi,%ecx,2),%edi # to + count*2 - 4 + movl %eax,%esi # from + count*2 - 2 + movl %ecx,%eax + subl $2,%esi # from + count*2 - 4 + sarl %ecx # dword count + jz 4f # no dwords to move + cmpl $32,%ecx + ja 3f # > 32 dwords + subl %esi,%edi + .p2align 4,,15 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + subl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 4f +3: rep; smovl +4: andl $1,%eax # suffix count + jz 5f # no suffix + # copy suffix + addl $2,%esi + addl $2,%edi + movw (%esi),%dx + movw %dx,(%edi) +5: cld + popl %edi + popl %esi + ret + + # Support for void Copy::conjoint_jints_atomic(void* from, + # void* to, + # size_t count) + # Equivalent to + # arrayof_conjoint_jints + .p2align 4,,15 + .type _Copy_conjoint_jints_atomic,@function + .type _Copy_arrayof_conjoint_jints,@function +_Copy_conjoint_jints_atomic: +_Copy_arrayof_conjoint_jints: + pushl %esi + movl 4+12(%esp),%ecx # count + pushl %edi + movl 8+ 4(%esp),%esi # from + movl 8+ 8(%esp),%edi # to + cmpl %esi,%edi + leal -4(%esi,%ecx,4),%eax # from + count*4 - 4 + jbe ci_CopyRight + cmpl %eax,%edi + jbe ci_CopyLeft +ci_CopyRight: + cmpl $32,%ecx + jbe 2f # <= 32 dwords + rep; smovl + popl %edi + popl %esi + ret + .=.+10 +2: subl %esi,%edi + .p2align 4,,15 +3: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 3b + popl %edi + popl %esi + ret +ci_CopyLeft: + std + leal -4(%edi,%ecx,4),%edi # to + count*4 - 4 + cmpl $32,%ecx + ja 3f # > 32 dwords + subl %eax,%edi # eax == from + count*4 - 4 + .p2align 4,,15 +2: movl (%eax),%edx + movl %edx,(%edi,%eax,1) + subl $4,%eax + subl $1,%ecx + jnz 2b + cld + popl %edi + popl %esi + ret +3: movl %eax,%esi # from + count*4 - 4 + rep; smovl + cld + popl %edi + popl %esi + ret + + # Support for void Copy::conjoint_jlongs_atomic(jlong* from, + # jlong* to, + # size_t count) + # + # 32-bit + # + # count treated as signed + # + # if (from > to) { + # while (--count >= 0) { + # *to++ = *from++; + # } + # } else { + # while (--count >= 0) { + # to[count] = from[count]; + # } + # } + .p2align 4,,15 + .type _Copy_conjoint_jlongs_atomic,@function +_Copy_conjoint_jlongs_atomic: + movl 4+8(%esp),%ecx # count + movl 4+0(%esp),%eax # from + movl 4+4(%esp),%edx # to + cmpl %eax,%edx + jae cla_CopyLeft +cla_CopyRight: + subl %eax,%edx + jmp 2f + .p2align 4,,15 +1: fildll (%eax) + fistpll (%edx,%eax,1) + addl $8,%eax +2: subl $1,%ecx + jge 1b + ret + .p2align 4,,15 +3: fildll (%eax,%ecx,8) + fistpll (%edx,%ecx,8) +cla_CopyLeft: + subl $1,%ecx + jge 3b + ret + + # Support for void Copy::arrayof_conjoint_jshorts(void* from, + # void* to, + # size_t count) + .p2align 4,,15 + .type _mmx_Copy_arrayof_conjoint_jshorts,@function +_mmx_Copy_arrayof_conjoint_jshorts: + pushl %esi + movl 4+12(%esp),%ecx + pushl %edi + movl 8+ 4(%esp),%esi + movl 8+ 8(%esp),%edi + cmpl %esi,%edi + leal -2(%esi,%ecx,2),%eax + jbe mmx_acs_CopyRight + cmpl %eax,%edi + jbe mmx_acs_CopyLeft +mmx_acs_CopyRight: + movl %ecx,%eax + sarl %ecx + je 5f + cmpl $33,%ecx + jae 3f +1: subl %esi,%edi + .p2align 4,,15 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 5f +3: smovl # align to 8 bytes, we know we are 4 byte aligned to start + subl $1,%ecx +4: .p2align 4,,15 + movq 0(%esi),%mm0 + addl $64,%edi + movq 8(%esi),%mm1 + subl $16,%ecx + movq 16(%esi),%mm2 + movq %mm0,-64(%edi) + movq 24(%esi),%mm0 + movq %mm1,-56(%edi) + movq 32(%esi),%mm1 + movq %mm2,-48(%edi) + movq 40(%esi),%mm2 + movq %mm0,-40(%edi) + movq 48(%esi),%mm0 + movq %mm1,-32(%edi) + movq 56(%esi),%mm1 + movq %mm2,-24(%edi) + movq %mm0,-16(%edi) + addl $64,%esi + movq %mm1,-8(%edi) + cmpl $16,%ecx + jge 4b + emms + testl %ecx,%ecx + ja 1b +5: andl $1,%eax + je 7f +6: movw (%esi),%dx + movw %dx,(%edi) +7: popl %edi + popl %esi + ret +mmx_acs_CopyLeft: + std + leal -4(%edi,%ecx,2),%edi + movl %eax,%esi + movl %ecx,%eax + subl $2,%esi + sarl %ecx + je 4f + cmpl $32,%ecx + ja 3f + subl %esi,%edi + .p2align 4,,15 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + subl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 4f +3: rep; smovl +4: andl $1,%eax + je 6f + addl $2,%esi + addl $2,%edi +5: movw (%esi),%dx + movw %dx,(%edi) +6: cld + popl %edi + popl %esi + ret + + + # Support for jlong Atomic::cmpxchg(jlong exchange_value, + # volatile jlong* dest, + # jlong compare_value, + # bool is_MP) + # + .p2align 4,,15 + .type _Atomic_cmpxchg_long,@function +_Atomic_cmpxchg_long: + # 8(%esp) : return PC + pushl %ebx # 4(%esp) : old %ebx + pushl %edi # 0(%esp) : old %edi + movl 12(%esp), %ebx # 12(%esp) : exchange_value (low) + movl 16(%esp), %ecx # 16(%esp) : exchange_value (high) + movl 24(%esp), %eax # 24(%esp) : compare_value (low) + movl 28(%esp), %edx # 28(%esp) : compare_value (high) + movl 20(%esp), %edi # 20(%esp) : dest + cmpl $0, 32(%esp) # 32(%esp) : is_MP + je 1f + lock +1: cmpxchg8b (%edi) + popl %edi + popl %ebx + ret + diff --git a/hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.ad b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.ad new file mode 100644 index 00000000000..f45a1ce9adb --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.ad @@ -0,0 +1,173 @@ +// +// Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// AMD64 Linux Architecture Description File + +//----------OS-DEPENDENT ENCODING BLOCK---------------------------------------- +// This block specifies the encoding classes used by the compiler to +// output byte streams. Encoding classes generate functions which are +// called by Machine Instruction Nodes in order to generate the bit +// encoding of the instruction. Operands specify their base encoding +// interface with the interface keyword. There are currently +// supported four interfaces, REG_INTER, CONST_INTER, MEMORY_INTER, & +// COND_INTER. REG_INTER causes an operand to generate a function +// which returns its register number when queried. CONST_INTER causes +// an operand to generate a function which returns the value of the +// constant when queried. MEMORY_INTER causes an operand to generate +// four functions which return the Base Register, the Index Register, +// the Scale Value, and the Offset Value of the operand when queried. +// COND_INTER causes an operand to generate six functions which return +// the encoding code (ie - encoding bits for the instruction) +// associated with each basic boolean condition for a conditional +// instruction. Instructions specify two basic values for encoding. +// They use the ins_encode keyword to specify their encoding class +// (which must be one of the class names specified in the encoding +// block), and they use the opcode keyword to specify, in order, their +// primary, secondary, and tertiary opcode. Only the opcode sections +// which a particular instruction needs for encoding need to be +// specified. +encode %{ + // Build emit functions for each basic byte or larger field in the intel + // encoding scheme (opcode, rm, sib, immediate), and call them from C++ + // code in the enc_class source block. Emit functions will live in the + // main source block for now. In future, we can generalize this by + // adding a syntax that specifies the sizes of fields in an order, + // so that the adlc can build the emit functions automagically + + enc_class Java_To_Runtime(method meth) + %{ + // No relocation needed + + // movq r10, + emit_opcode(cbuf, Assembler::REX_WB); + emit_opcode(cbuf, 0xB8 | (R10_enc - 8)); + emit_d64(cbuf, (int64_t) $meth$$method); + + // call (r10) + emit_opcode(cbuf, Assembler::REX_B); + emit_opcode(cbuf, 0xFF); + emit_opcode(cbuf, 0xD0 | (R10_enc - 8)); + %} + + enc_class linux_breakpoint + %{ + MacroAssembler* masm = new MacroAssembler(&cbuf); + masm->call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); + %} + + enc_class call_epilog + %{ + if (VerifyStackAtCalls) { + // Check that stack depth is unchanged: find majik cookie on stack + int framesize = + ra_->reg2offset_unchecked(OptoReg::add(ra_->_matcher._old_SP, -3*VMRegImpl::slots_per_word)); + if (framesize) { + if (framesize < 0x80) { + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x81); // cmpq [rsp+0],0xbadb1ood + emit_d8(cbuf, 0x7C); + emit_d8(cbuf, 0x24); + emit_d8(cbuf, framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } else { + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x81); // cmpq [rsp+0],0xbadb1ood + emit_d8(cbuf, 0xBC); + emit_d8(cbuf, 0x24); + emit_d32(cbuf, framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } + } + // jmp EQ around INT3 + // QQQ TODO + const int jump_around = 5; // size of call to breakpoint, 1 for CC + emit_opcode(cbuf, 0x74); + emit_d8(cbuf, jump_around); + // QQQ temporary + emit_break(cbuf); + // Die if stack mismatch + // emit_opcode(cbuf,0xCC); + } + %} + +%} + +// INSTRUCTIONS -- Platform dependent + +//----------OS and Locking Instructions---------------------------------------- + +// This name is KNOWN by the ADLC and cannot be changed. +// The ADLC forces a 'TypeRawPtr::BOTTOM' output type +// for this guy. +instruct tlsLoadP(r15_RegP dst) +%{ + match(Set dst (ThreadLocal)); + effect(DEF dst); + + size(0); + format %{ "# TLS is in R15" %} + ins_encode( /*empty encoding*/ ); + ins_pipe(ialu_reg_reg); +%} + +// Die now +instruct ShouldNotReachHere() +%{ + match(Halt); + + // Use the following format syntax + format %{ "int3\t# ShouldNotReachHere" %} + // QQQ TODO for now call breakpoint + // opcode(0xCC); + // ins_encode(Opc); + ins_encode(linux_breakpoint); + ins_pipe(pipe_slow); +%} + + +// Platform dependent source + +source +%{ + +int MachCallRuntimeNode::ret_addr_offset() { + return 13; // movq r10,#addr; callq (r10) +} + +// emit an interrupt that is caught by the debugger +void emit_break(CodeBuffer& cbuf) { + // Debugger doesn't really catch this but best we can do so far QQQ + MacroAssembler* masm = new MacroAssembler(&cbuf); + masm->call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); +} + +void MachBreakpointNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { + emit_break(cbuf); +} + +uint MachBreakpointNode::size(PhaseRegAlloc* ra_) const { + return 5; +} + +%} diff --git a/hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.s b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.s new file mode 100644 index 00000000000..35f602f6b88 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.s @@ -0,0 +1,402 @@ +# +# Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + + + # NOTE WELL! The _Copy functions are called directly + # from server-compiler-generated code via CallLeafNoFP, + # which means that they *must* either not use floating + # point or use it in the same manner as does the server + # compiler. + + .globl _Copy_arrayof_conjoint_bytes + .globl _Copy_arrayof_conjoint_jshorts + .globl _Copy_conjoint_jshorts_atomic + .globl _Copy_arrayof_conjoint_jints + .globl _Copy_conjoint_jints_atomic + .globl _Copy_arrayof_conjoint_jlongs + .globl _Copy_conjoint_jlongs_atomic + + .text + + .globl SafeFetch32, Fetch32PFI, Fetch32Resume + .align 16 + .type SafeFetch32,@function + // Prototype: int SafeFetch32 (int * Adr, int ErrValue) +SafeFetch32: + movl %esi, %eax +Fetch32PFI: + movl (%rdi), %eax +Fetch32Resume: + ret + + .globl SafeFetchN, FetchNPFI, FetchNResume + .align 16 + .type SafeFetchN,@function + // Prototype: intptr_t SafeFetchN (intptr_t * Adr, intptr_t ErrValue) +SafeFetchN: + movq %rsi, %rax +FetchNPFI: + movq (%rdi), %rax +FetchNResume: + ret + + .globl SpinPause + .align 16 + .type SpinPause,@function +SpinPause: + rep + nop + movq $1, %rax + ret + + # Support for void Copy::arrayof_conjoint_bytes(void* from, + # void* to, + # size_t count) + # rdi - from + # rsi - to + # rdx - count, treated as ssize_t + # + .p2align 4,,15 + .type _Copy_arrayof_conjoint_bytes,@function +_Copy_arrayof_conjoint_bytes: + movq %rdx,%r8 # byte count + shrq $3,%rdx # qword count + cmpq %rdi,%rsi + leaq -1(%rdi,%r8,1),%rax # from + bcount*1 - 1 + jbe acb_CopyRight + cmpq %rax,%rsi + jbe acb_CopyLeft +acb_CopyRight: + leaq -8(%rdi,%rdx,8),%rax # from + qcount*8 - 8 + leaq -8(%rsi,%rdx,8),%rcx # to + qcount*8 - 8 + negq %rdx + jmp 7f + .p2align 4,,15 +1: movq 8(%rax,%rdx,8),%rsi + movq %rsi,8(%rcx,%rdx,8) + addq $1,%rdx + jnz 1b +2: testq $4,%r8 # check for trailing dword + jz 3f + movl 8(%rax),%esi # copy trailing dword + movl %esi,8(%rcx) + addq $4,%rax + addq $4,%rcx # original %rsi is trashed, so we + # can't use it as a base register +3: testq $2,%r8 # check for trailing word + jz 4f + movw 8(%rax),%si # copy trailing word + movw %si,8(%rcx) + addq $2,%rcx +4: testq $1,%r8 # check for trailing byte + jz 5f + movb -1(%rdi,%r8,1),%al # copy trailing byte + movb %al,8(%rcx) +5: ret + .p2align 4,,15 +6: movq -24(%rax,%rdx,8),%rsi + movq %rsi,-24(%rcx,%rdx,8) + movq -16(%rax,%rdx,8),%rsi + movq %rsi,-16(%rcx,%rdx,8) + movq -8(%rax,%rdx,8),%rsi + movq %rsi,-8(%rcx,%rdx,8) + movq (%rax,%rdx,8),%rsi + movq %rsi,(%rcx,%rdx,8) +7: addq $4,%rdx + jle 6b + subq $4,%rdx + jl 1b + jmp 2b +acb_CopyLeft: + testq $1,%r8 # check for trailing byte + jz 1f + movb -1(%rdi,%r8,1),%cl # copy trailing byte + movb %cl,-1(%rsi,%r8,1) + subq $1,%r8 # adjust for possible trailing word +1: testq $2,%r8 # check for trailing word + jz 2f + movw -2(%rdi,%r8,1),%cx # copy trailing word + movw %cx,-2(%rsi,%r8,1) +2: testq $4,%r8 # check for trailing dword + jz 5f + movl (%rdi,%rdx,8),%ecx # copy trailing dword + movl %ecx,(%rsi,%rdx,8) + jmp 5f + .p2align 4,,15 +3: movq -8(%rdi,%rdx,8),%rcx + movq %rcx,-8(%rsi,%rdx,8) + subq $1,%rdx + jnz 3b + ret + .p2align 4,,15 +4: movq 24(%rdi,%rdx,8),%rcx + movq %rcx,24(%rsi,%rdx,8) + movq 16(%rdi,%rdx,8),%rcx + movq %rcx,16(%rsi,%rdx,8) + movq 8(%rdi,%rdx,8),%rcx + movq %rcx,8(%rsi,%rdx,8) + movq (%rdi,%rdx,8),%rcx + movq %rcx,(%rsi,%rdx,8) +5: subq $4,%rdx + jge 4b + addq $4,%rdx + jg 3b + ret + + # Support for void Copy::arrayof_conjoint_jshorts(void* from, + # void* to, + # size_t count) + # Equivalent to + # conjoint_jshorts_atomic + # + # If 'from' and/or 'to' are aligned on 4- or 2-byte boundaries, we + # let the hardware handle it. The tow or four words within dwords + # or qwords that span cache line boundaries will still be loaded + # and stored atomically. + # + # rdi - from + # rsi - to + # rdx - count, treated as ssize_t + # + .p2align 4,,15 + .type _Copy_arrayof_conjoint_jshorts,@function + .type _Copy_conjoint_jshorts_atomic,@function +_Copy_arrayof_conjoint_jshorts: +_Copy_conjoint_jshorts_atomic: + movq %rdx,%r8 # word count + shrq $2,%rdx # qword count + cmpq %rdi,%rsi + leaq -2(%rdi,%r8,2),%rax # from + wcount*2 - 2 + jbe acs_CopyRight + cmpq %rax,%rsi + jbe acs_CopyLeft +acs_CopyRight: + leaq -8(%rdi,%rdx,8),%rax # from + qcount*8 - 8 + leaq -8(%rsi,%rdx,8),%rcx # to + qcount*8 - 8 + negq %rdx + jmp 6f +1: movq 8(%rax,%rdx,8),%rsi + movq %rsi,8(%rcx,%rdx,8) + addq $1,%rdx + jnz 1b +2: testq $2,%r8 # check for trailing dword + jz 3f + movl 8(%rax),%esi # copy trailing dword + movl %esi,8(%rcx) + addq $4,%rcx # original %rsi is trashed, so we + # can't use it as a base register +3: testq $1,%r8 # check for trailing word + jz 4f + movw -2(%rdi,%r8,2),%si # copy trailing word + movw %si,8(%rcx) +4: ret + .p2align 4,,15 +5: movq -24(%rax,%rdx,8),%rsi + movq %rsi,-24(%rcx,%rdx,8) + movq -16(%rax,%rdx,8),%rsi + movq %rsi,-16(%rcx,%rdx,8) + movq -8(%rax,%rdx,8),%rsi + movq %rsi,-8(%rcx,%rdx,8) + movq (%rax,%rdx,8),%rsi + movq %rsi,(%rcx,%rdx,8) +6: addq $4,%rdx + jle 5b + subq $4,%rdx + jl 1b + jmp 2b +acs_CopyLeft: + testq $1,%r8 # check for trailing word + jz 1f + movw -2(%rdi,%r8,2),%cx # copy trailing word + movw %cx,-2(%rsi,%r8,2) +1: testq $2,%r8 # check for trailing dword + jz 4f + movl (%rdi,%rdx,8),%ecx # copy trailing dword + movl %ecx,(%rsi,%rdx,8) + jmp 4f +2: movq -8(%rdi,%rdx,8),%rcx + movq %rcx,-8(%rsi,%rdx,8) + subq $1,%rdx + jnz 2b + ret + .p2align 4,,15 +3: movq 24(%rdi,%rdx,8),%rcx + movq %rcx,24(%rsi,%rdx,8) + movq 16(%rdi,%rdx,8),%rcx + movq %rcx,16(%rsi,%rdx,8) + movq 8(%rdi,%rdx,8),%rcx + movq %rcx,8(%rsi,%rdx,8) + movq (%rdi,%rdx,8),%rcx + movq %rcx,(%rsi,%rdx,8) +4: subq $4,%rdx + jge 3b + addq $4,%rdx + jg 2b + ret + + # Support for void Copy::arrayof_conjoint_jints(jint* from, + # jint* to, + # size_t count) + # Equivalent to + # conjoint_jints_atomic + # + # If 'from' and/or 'to' are aligned on 4-byte boundaries, we let + # the hardware handle it. The two dwords within qwords that span + # cache line boundaries will still be loaded and stored atomically. + # + # rdi - from + # rsi - to + # rdx - count, treated as ssize_t + # + .p2align 4,,15 + .type _Copy_arrayof_conjoint_jints,@function + .type _Copy_conjoint_jints_atomic,@function +_Copy_arrayof_conjoint_jints: +_Copy_conjoint_jints_atomic: + movq %rdx,%r8 # dword count + shrq %rdx # qword count + cmpq %rdi,%rsi + leaq -4(%rdi,%r8,4),%rax # from + dcount*4 - 4 + jbe aci_CopyRight + cmpq %rax,%rsi + jbe aci_CopyLeft +aci_CopyRight: + leaq -8(%rdi,%rdx,8),%rax # from + qcount*8 - 8 + leaq -8(%rsi,%rdx,8),%rcx # to + qcount*8 - 8 + negq %rdx + jmp 5f + .p2align 4,,15 +1: movq 8(%rax,%rdx,8),%rsi + movq %rsi,8(%rcx,%rdx,8) + addq $1,%rdx + jnz 1b +2: testq $1,%r8 # check for trailing dword + jz 3f + movl 8(%rax),%esi # copy trailing dword + movl %esi,8(%rcx) +3: ret + .p2align 4,,15 +4: movq -24(%rax,%rdx,8),%rsi + movq %rsi,-24(%rcx,%rdx,8) + movq -16(%rax,%rdx,8),%rsi + movq %rsi,-16(%rcx,%rdx,8) + movq -8(%rax,%rdx,8),%rsi + movq %rsi,-8(%rcx,%rdx,8) + movq (%rax,%rdx,8),%rsi + movq %rsi,(%rcx,%rdx,8) +5: addq $4,%rdx + jle 4b + subq $4,%rdx + jl 1b + jmp 2b +aci_CopyLeft: + testq $1,%r8 # check for trailing dword + jz 3f + movl -4(%rdi,%r8,4),%ecx # copy trailing dword + movl %ecx,-4(%rsi,%r8,4) + jmp 3f +1: movq -8(%rdi,%rdx,8),%rcx + movq %rcx,-8(%rsi,%rdx,8) + subq $1,%rdx + jnz 1b + ret + .p2align 4,,15 +2: movq 24(%rdi,%rdx,8),%rcx + movq %rcx,24(%rsi,%rdx,8) + movq 16(%rdi,%rdx,8),%rcx + movq %rcx,16(%rsi,%rdx,8) + movq 8(%rdi,%rdx,8),%rcx + movq %rcx,8(%rsi,%rdx,8) + movq (%rdi,%rdx,8),%rcx + movq %rcx,(%rsi,%rdx,8) +3: subq $4,%rdx + jge 2b + addq $4,%rdx + jg 1b + ret + + # Support for void Copy::arrayof_conjoint_jlongs(jlong* from, + # jlong* to, + # size_t count) + # Equivalent to + # conjoint_jlongs_atomic + # arrayof_conjoint_oops + # conjoint_oops_atomic + # + # rdi - from + # rsi - to + # rdx - count, treated as ssize_t + # + .p2align 4,,15 + .type _Copy_arrayof_conjoint_jlongs,@function + .type _Copy_conjoint_jlongs_atomic,@function +_Copy_arrayof_conjoint_jlongs: +_Copy_conjoint_jlongs_atomic: + cmpq %rdi,%rsi + leaq -8(%rdi,%rdx,8),%rax # from + count*8 - 8 + jbe acl_CopyRight + cmpq %rax,%rsi + jbe acl_CopyLeft +acl_CopyRight: + leaq -8(%rsi,%rdx,8),%rcx # to + count*8 - 8 + negq %rdx + jmp 3f +1: movq 8(%rax,%rdx,8),%rsi + movq %rsi,8(%rcx,%rdx,8) + addq $1,%rdx + jnz 1b + ret + .p2align 4,,15 +2: movq -24(%rax,%rdx,8),%rsi + movq %rsi,-24(%rcx,%rdx,8) + movq -16(%rax,%rdx,8),%rsi + movq %rsi,-16(%rcx,%rdx,8) + movq -8(%rax,%rdx,8),%rsi + movq %rsi,-8(%rcx,%rdx,8) + movq (%rax,%rdx,8),%rsi + movq %rsi,(%rcx,%rdx,8) +3: addq $4,%rdx + jle 2b + subq $4,%rdx + jl 1b + ret +4: movq -8(%rdi,%rdx,8),%rcx + movq %rcx,-8(%rsi,%rdx,8) + subq $1,%rdx + jnz 4b + ret + .p2align 4,,15 +5: movq 24(%rdi,%rdx,8),%rcx + movq %rcx,24(%rsi,%rdx,8) + movq 16(%rdi,%rdx,8),%rcx + movq %rcx,16(%rsi,%rdx,8) + movq 8(%rdi,%rdx,8),%rcx + movq %rcx,8(%rsi,%rdx,8) + movq (%rdi,%rdx,8),%rcx + movq %rcx,(%rsi,%rdx,8) +acl_CopyLeft: + subq $4,%rdx + jge 5b + addq $4,%rdx + jg 4b + ret diff --git a/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp b/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp new file mode 100644 index 00000000000..2b5f3ec0ac5 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp @@ -0,0 +1,203 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Implementation of class OrderAccess. + +inline void OrderAccess::loadload() { acquire(); } +inline void OrderAccess::storestore() { release(); } +inline void OrderAccess::loadstore() { acquire(); } +inline void OrderAccess::storeload() { fence(); } + +inline void OrderAccess::acquire() { + volatile intptr_t dummy; +#ifdef AMD64 + __asm__ volatile ("movq 0(%%rsp), %0" : "=r" (dummy) : : "memory"); +#else + __asm__ volatile ("movl 0(%%esp),%0" : "=r" (dummy) : : "memory"); +#endif // AMD64 +} + +inline void OrderAccess::release() { + dummy = 0; +} + +inline void OrderAccess::fence() { + if (os::is_MP()) { +#ifdef AMD64 + __asm__ __volatile__ ("mfence":::"memory"); +#else + __asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory"); +#endif // AMD64 + } +} + +inline jbyte OrderAccess::load_acquire(volatile jbyte* p) { return *p; } +inline jshort OrderAccess::load_acquire(volatile jshort* p) { return *p; } +inline jint OrderAccess::load_acquire(volatile jint* p) { return *p; } +inline jlong OrderAccess::load_acquire(volatile jlong* p) { return *p; } +inline jubyte OrderAccess::load_acquire(volatile jubyte* p) { return *p; } +inline jushort OrderAccess::load_acquire(volatile jushort* p) { return *p; } +inline juint OrderAccess::load_acquire(volatile juint* p) { return *p; } +inline julong OrderAccess::load_acquire(volatile julong* p) { return *p; } +inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { return *p; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return *p; } + +inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return *p; } +inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return *(void* volatile *)p; } +inline void* OrderAccess::load_ptr_acquire(const volatile void* p) { return *(void* const volatile *)p; } + +inline void OrderAccess::release_store(volatile jbyte* p, jbyte v) { *p = v; } +inline void OrderAccess::release_store(volatile jshort* p, jshort v) { *p = v; } +inline void OrderAccess::release_store(volatile jint* p, jint v) { *p = v; } +inline void OrderAccess::release_store(volatile jlong* p, jlong v) { *p = v; } +inline void OrderAccess::release_store(volatile jubyte* p, jubyte v) { *p = v; } +inline void OrderAccess::release_store(volatile jushort* p, jushort v) { *p = v; } +inline void OrderAccess::release_store(volatile juint* p, juint v) { *p = v; } +inline void OrderAccess::release_store(volatile julong* p, julong v) { *p = v; } +inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { *p = v; } + +inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { *p = v; } +inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { *(void* volatile *)p = v; } + +inline void OrderAccess::store_fence(jbyte* p, jbyte v) { + __asm__ volatile ( "xchgb (%2),%0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +} +inline void OrderAccess::store_fence(jshort* p, jshort v) { + __asm__ volatile ( "xchgw (%2),%0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +} +inline void OrderAccess::store_fence(jint* p, jint v) { + __asm__ volatile ( "xchgl (%2),%0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +} + +inline void OrderAccess::store_fence(jlong* p, jlong v) { +#ifdef AMD64 + __asm__ __volatile__ ("xchgq (%2), %0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +#else + *p = v; fence(); +#endif // AMD64 +} + +// AMD64 copied the bodies for the the signed version. 32bit did this. As long as the +// compiler does the inlining this is simpler. +inline void OrderAccess::store_fence(jubyte* p, jubyte v) { store_fence((jbyte*)p, (jbyte)v); } +inline void OrderAccess::store_fence(jushort* p, jushort v) { store_fence((jshort*)p, (jshort)v); } +inline void OrderAccess::store_fence(juint* p, juint v) { store_fence((jint*)p, (jint)v); } +inline void OrderAccess::store_fence(julong* p, julong v) { store_fence((jlong*)p, (jlong)v); } +inline void OrderAccess::store_fence(jfloat* p, jfloat v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jdouble* p, jdouble v) { *p = v; fence(); } + +inline void OrderAccess::store_ptr_fence(intptr_t* p, intptr_t v) { +#ifdef AMD64 + __asm__ __volatile__ ("xchgq (%2), %0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +#else + store_fence((jint*)p, (jint)v); +#endif // AMD64 +} + +inline void OrderAccess::store_ptr_fence(void** p, void* v) { +#ifdef AMD64 + __asm__ __volatile__ ("xchgq (%2), %0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +#else + store_fence((jint*)p, (jint)v); +#endif // AMD64 +} + +// Must duplicate definitions instead of calling store_fence because we don't want to cast away volatile. +inline void OrderAccess::release_store_fence(volatile jbyte* p, jbyte v) { + __asm__ volatile ( "xchgb (%2),%0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +} +inline void OrderAccess::release_store_fence(volatile jshort* p, jshort v) { + __asm__ volatile ( "xchgw (%2),%0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +} +inline void OrderAccess::release_store_fence(volatile jint* p, jint v) { + __asm__ volatile ( "xchgl (%2),%0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +} + +inline void OrderAccess::release_store_fence(volatile jlong* p, jlong v) { +#ifdef AMD64 + __asm__ __volatile__ ( "xchgq (%2), %0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +#else + *p = v; fence(); +#endif // AMD64 +} + +inline void OrderAccess::release_store_fence(volatile jubyte* p, jubyte v) { release_store_fence((volatile jbyte*)p, (jbyte)v); } +inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) { release_store_fence((volatile jshort*)p, (jshort)v); } +inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { release_store_fence((volatile jint*)p, (jint)v); } +inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { release_store_fence((volatile jlong*)p, (jlong)v); } + +inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { *p = v; fence(); } + +inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { +#ifdef AMD64 + __asm__ __volatile__ ( "xchgq (%2), %0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +#else + release_store_fence((volatile jint*)p, (jint)v); +#endif // AMD64 +} +inline void OrderAccess::release_store_ptr_fence(volatile void* p, void* v) { +#ifdef AMD64 + __asm__ __volatile__ ( "xchgq (%2), %0" + : "=r" (v) + : "0" (v), "r" (p) + : "memory"); +#else + release_store_fence((volatile jint*)p, (jint)v); +#endif // AMD64 +} diff --git a/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp new file mode 100644 index 00000000000..972ee017c40 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp @@ -0,0 +1,782 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// do not include precompiled header file +# include "incls/_os_linux_x86.cpp.incl" + +// put OS-includes here +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +#ifdef AMD64 +#define REG_SP REG_RSP +#define REG_PC REG_RIP +#define REG_FP REG_RBP +#define SPELL_REG_SP "rsp" +#define SPELL_REG_FP "rbp" +#else +#define REG_SP REG_UESP +#define REG_PC REG_EIP +#define REG_FP REG_EBP +#define SPELL_REG_SP "esp" +#define SPELL_REG_FP "ebp" +#endif // AMD64 + +address os::current_stack_pointer() { + register void *esp __asm__ (SPELL_REG_SP); + return (address) esp; +} + +char* os::non_memory_address_word() { + // Must never look like an address returned by reserve_memory, + // even in its subfields (as defined by the CPU immediate fields, + // if the CPU splits constants across multiple instructions). + + return (char*) -1; +} + +void os::initialize_thread() { +// Nothing to do. +} + +address os::Linux::ucontext_get_pc(ucontext_t * uc) { + return (address)uc->uc_mcontext.gregs[REG_PC]; +} + +intptr_t* os::Linux::ucontext_get_sp(ucontext_t * uc) { + return (intptr_t*)uc->uc_mcontext.gregs[REG_SP]; +} + +intptr_t* os::Linux::ucontext_get_fp(ucontext_t * uc) { + return (intptr_t*)uc->uc_mcontext.gregs[REG_FP]; +} + +// For Forte Analyzer AsyncGetCallTrace profiling support - thread +// is currently interrupted by SIGPROF. +// os::Solaris::fetch_frame_from_ucontext() tries to skip nested signal +// frames. Currently we don't do that on Linux, so it's the same as +// os::fetch_frame_from_context(). +ExtendedPC os::Linux::fetch_frame_from_ucontext(Thread* thread, + ucontext_t* uc, intptr_t** ret_sp, intptr_t** ret_fp) { + + assert(thread != NULL, "just checking"); + assert(ret_sp != NULL, "just checking"); + assert(ret_fp != NULL, "just checking"); + + return os::fetch_frame_from_context(uc, ret_sp, ret_fp); +} + +ExtendedPC os::fetch_frame_from_context(void* ucVoid, + intptr_t** ret_sp, intptr_t** ret_fp) { + + ExtendedPC epc; + ucontext_t* uc = (ucontext_t*)ucVoid; + + if (uc != NULL) { + epc = ExtendedPC(os::Linux::ucontext_get_pc(uc)); + if (ret_sp) *ret_sp = os::Linux::ucontext_get_sp(uc); + if (ret_fp) *ret_fp = os::Linux::ucontext_get_fp(uc); + } else { + // construct empty ExtendedPC for return value checking + epc = ExtendedPC(NULL); + if (ret_sp) *ret_sp = (intptr_t *)NULL; + if (ret_fp) *ret_fp = (intptr_t *)NULL; + } + + return epc; +} + +frame os::fetch_frame_from_context(void* ucVoid) { + intptr_t* sp; + intptr_t* fp; + ExtendedPC epc = fetch_frame_from_context(ucVoid, &sp, &fp); + return frame(sp, fp, epc.pc()); +} + +// By default, gcc always save frame pointer (%ebp/%rbp) on stack. It may get +// turned off by -fomit-frame-pointer, +frame os::get_sender_for_C_frame(frame* fr) { + return frame(fr->sender_sp(), fr->link(), fr->sender_pc()); +} + +intptr_t* _get_previous_fp() { + register intptr_t **ebp __asm__ (SPELL_REG_FP); + return (intptr_t*) *ebp; // we want what it points to. +} + + +frame os::current_frame() { + intptr_t* fp = _get_previous_fp(); + frame myframe((intptr_t*)os::current_stack_pointer(), + (intptr_t*)fp, + CAST_FROM_FN_PTR(address, os::current_frame)); + if (os::is_first_C_frame(&myframe)) { + // stack is not walkable + return frame(NULL, NULL, NULL); + } else { + return os::get_sender_for_C_frame(&myframe); + } +} + + +// Utility functions + +julong os::allocatable_physical_memory(julong size) { +#ifdef AMD64 + return size; +#else + julong result = MIN2(size, (julong)3800*M); + if (!is_allocatable(result)) { + // See comments under solaris for alignment considerations + julong reasonable_size = (julong)2*G - 2 * os::vm_page_size(); + result = MIN2(size, reasonable_size); + } + return result; +#endif // AMD64 +} + +// From IA32 System Programming Guide +enum { + trap_page_fault = 0xE +}; + +extern "C" void Fetch32PFI () ; +extern "C" void Fetch32Resume () ; +#ifdef AMD64 +extern "C" void FetchNPFI () ; +extern "C" void FetchNResume () ; +#endif // AMD64 + +extern "C" int +JVM_handle_linux_signal(int sig, + siginfo_t* info, + void* ucVoid, + int abort_if_unrecognized) { + ucontext_t* uc = (ucontext_t*) ucVoid; + + Thread* t = ThreadLocalStorage::get_thread_slow(); + + SignalHandlerMark shm(t); + + // Note: it's not uncommon that JNI code uses signal/sigset to install + // then restore certain signal handler (e.g. to temporarily block SIGPIPE, + // or have a SIGILL handler when detecting CPU type). When that happens, + // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To + // avoid unnecessary crash when libjsig is not preloaded, try handle signals + // that do not require siginfo/ucontext first. + + if (sig == SIGPIPE || sig == SIGXFSZ) { + // allow chained handler to go first + if (os::Linux::chained_handler(sig, info, ucVoid)) { + return true; + } else { + if (PrintMiscellaneous && (WizardMode || Verbose)) { + char buf[64]; + warning("Ignoring %s - see bugs 4229104 or 646499219", + os::exception_name(sig, buf, sizeof(buf))); + } + return true; + } + } + + JavaThread* thread = NULL; + VMThread* vmthread = NULL; + if (os::Linux::signal_handlers_are_installed) { + if (t != NULL ){ + if(t->is_Java_thread()) { + thread = (JavaThread*)t; + } + else if(t->is_VM_thread()){ + vmthread = (VMThread *)t; + } + } + } +/* + NOTE: does not seem to work on linux. + if (info == NULL || info->si_code <= 0 || info->si_code == SI_NOINFO) { + // can't decode this kind of signal + info = NULL; + } else { + assert(sig == info->si_signo, "bad siginfo"); + } +*/ + // decide if this trap can be handled by a stub + address stub = NULL; + + address pc = NULL; + + //%note os_trap_1 + if (info != NULL && uc != NULL && thread != NULL) { + pc = (address) os::Linux::ucontext_get_pc(uc); + + if (pc == (address) Fetch32PFI) { + uc->uc_mcontext.gregs[REG_PC] = intptr_t(Fetch32Resume) ; + return 1 ; + } +#ifdef AMD64 + if (pc == (address) FetchNPFI) { + uc->uc_mcontext.gregs[REG_PC] = intptr_t (FetchNResume) ; + return 1 ; + } +#endif // AMD64 + + // Handle ALL stack overflow variations here + if (sig == SIGSEGV) { + address addr = (address) info->si_addr; + + // check if fault address is within thread stack + if (addr < thread->stack_base() && + addr >= thread->stack_base() - thread->stack_size()) { + // stack overflow + if (thread->in_stack_yellow_zone(addr)) { + thread->disable_stack_yellow_zone(); + if (thread->thread_state() == _thread_in_Java) { + // Throw a stack overflow exception. Guard pages will be reenabled + // while unwinding the stack. + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); + } else { + // Thread was in the vm or native code. Return and try to finish. + return 1; + } + } else if (thread->in_stack_red_zone(addr)) { + // Fatal red zone violation. Disable the guard pages and fall through + // to handle_unexpected_exception way down below. + thread->disable_stack_red_zone(); + tty->print_raw_cr("An irrecoverable stack overflow has occurred."); + } else { + // Accessing stack address below sp may cause SEGV if current + // thread has MAP_GROWSDOWN stack. This should only happen when + // current thread was created by user code with MAP_GROWSDOWN flag + // and then attached to VM. See notes in os_linux.cpp. + if (thread->osthread()->expanding_stack() == 0) { + thread->osthread()->set_expanding_stack(); + if (os::Linux::manually_expand_stack(thread, addr)) { + thread->osthread()->clear_expanding_stack(); + return 1; + } + thread->osthread()->clear_expanding_stack(); + } else { + fatal("recursive segv. expanding stack."); + } + } + } + } + + if (thread->thread_state() == _thread_in_Java) { + // Java thread running in Java code => find exception handler if any + // a fault inside compiled code, the interpreter, or a stub + + if (sig == SIGSEGV && os::is_poll_address((address)info->si_addr)) { + stub = SharedRuntime::get_poll_stub(pc); + } else if (sig == SIGBUS /* && info->si_code == BUS_OBJERR */) { + // BugId 4454115: A read from a MappedByteBuffer can fault + // here if the underlying file has been truncated. + // Do not crash the VM in such a case. + CodeBlob* cb = CodeCache::find_blob_unsafe(pc); + nmethod* nm = cb->is_nmethod() ? (nmethod*)cb : NULL; + if (nm != NULL && nm->has_unsafe_access()) { + stub = StubRoutines::handler_for_unsafe_access(); + } + } + else + +#ifdef AMD64 + if (sig == SIGFPE && + (info->si_code == FPE_INTDIV || info->si_code == FPE_FLTDIV)) { + stub = + SharedRuntime:: + continuation_for_implicit_exception(thread, + pc, + SharedRuntime:: + IMPLICIT_DIVIDE_BY_ZERO); +#else + if (sig == SIGFPE /* && info->si_code == FPE_INTDIV */) { + // HACK: si_code does not work on linux 2.2.12-20!!! + int op = pc[0]; + if (op == 0xDB) { + // FIST + // TODO: The encoding of D2I in i486.ad can cause an exception + // prior to the fist instruction if there was an invalid operation + // pending. We want to dismiss that exception. From the win_32 + // side it also seems that if it really was the fist causing + // the exception that we do the d2i by hand with different + // rounding. Seems kind of weird. + // NOTE: that we take the exception at the NEXT floating point instruction. + assert(pc[0] == 0xDB, "not a FIST opcode"); + assert(pc[1] == 0x14, "not a FIST opcode"); + assert(pc[2] == 0x24, "not a FIST opcode"); + return true; + } else if (op == 0xF7) { + // IDIV + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_DIVIDE_BY_ZERO); + } else { + // TODO: handle more cases if we are using other x86 instructions + // that can generate SIGFPE signal on linux. + tty->print_cr("unknown opcode 0x%X with SIGFPE.", op); + fatal("please update this code."); + } +#endif // AMD64 + } else if (sig == SIGSEGV && + !MacroAssembler::needs_explicit_null_check((intptr_t)info->si_addr)) { + // Determination of interpreter/vtable stub/compiled code null exception + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); + } + } else if (thread->thread_state() == _thread_in_vm && + sig == SIGBUS && /* info->si_code == BUS_OBJERR && */ + thread->doing_unsafe_access()) { + stub = StubRoutines::handler_for_unsafe_access(); + } + + // jni_fast_GetField can trap at certain pc's if a GC kicks in + // and the heap gets shrunk before the field access. + if ((sig == SIGSEGV) || (sig == SIGBUS)) { + address addr = JNI_FastGetField::find_slowcase_pc(pc); + if (addr != (address)-1) { + stub = addr; + } + } + + // Check to see if we caught the safepoint code in the + // process of write protecting the memory serialization page. + // It write enables the page immediately after protecting it + // so we can just return to retry the write. + if ((sig == SIGSEGV) && + os::is_memory_serialize_page(thread, (address) info->si_addr)) { + // Block current thread until the memory serialize page permission restored. + os::block_on_serialize_page_trap(); + return true; + } + } + +#ifndef AMD64 + // Execution protection violation + // + // This should be kept as the last step in the triage. We don't + // have a dedicated trap number for a no-execute fault, so be + // conservative and allow other handlers the first shot. + // + // Note: We don't test that info->si_code == SEGV_ACCERR here. + // this si_code is so generic that it is almost meaningless; and + // the si_code for this condition may change in the future. + // Furthermore, a false-positive should be harmless. + if (UnguardOnExecutionViolation > 0 && + (sig == SIGSEGV || sig == SIGBUS) && + uc->uc_mcontext.gregs[REG_TRAPNO] == trap_page_fault) { + int page_size = os::vm_page_size(); + address addr = (address) info->si_addr; + address pc = os::Linux::ucontext_get_pc(uc); + // Make sure the pc and the faulting address are sane. + // + // If an instruction spans a page boundary, and the page containing + // the beginning of the instruction is executable but the following + // page is not, the pc and the faulting address might be slightly + // different - we still want to unguard the 2nd page in this case. + // + // 15 bytes seems to be a (very) safe value for max instruction size. + bool pc_is_near_addr = + (pointer_delta((void*) addr, (void*) pc, sizeof(char)) < 15); + bool instr_spans_page_boundary = + (align_size_down((intptr_t) pc ^ (intptr_t) addr, + (intptr_t) page_size) > 0); + + if (pc == addr || (pc_is_near_addr && instr_spans_page_boundary)) { + static volatile address last_addr = + (address) os::non_memory_address_word(); + + // In conservative mode, don't unguard unless the address is in the VM + if (addr != last_addr && + (UnguardOnExecutionViolation > 1 || os::address_is_in_vm(addr))) { + + // Unguard and retry + address page_start = + (address) align_size_down((intptr_t) addr, (intptr_t) page_size); + bool res = os::unguard_memory((char*) page_start, page_size); + + if (PrintMiscellaneous && Verbose) { + char buf[256]; + jio_snprintf(buf, sizeof(buf), "Execution protection violation " + "at " INTPTR_FORMAT + ", unguarding " INTPTR_FORMAT ": %s, errno=%d", addr, + page_start, (res ? "success" : "failed"), errno); + tty->print_raw_cr(buf); + } + stub = pc; + + // Set last_addr so if we fault again at the same address, we don't end + // up in an endless loop. + // + // There are two potential complications here. Two threads trapping at + // the same address at the same time could cause one of the threads to + // think it already unguarded, and abort the VM. Likely very rare. + // + // The other race involves two threads alternately trapping at + // different addresses and failing to unguard the page, resulting in + // an endless loop. This condition is probably even more unlikely than + // the first. + // + // Although both cases could be avoided by using locks or thread local + // last_addr, these solutions are unnecessary complication: this + // handler is a best-effort safety net, not a complete solution. It is + // disabled by default and should only be used as a workaround in case + // we missed any no-execute-unsafe VM code. + + last_addr = addr; + } + } + } +#endif // !AMD64 + + if (stub != NULL) { + // save all thread context in case we need to restore it + if (thread != NULL) thread->set_saved_exception_pc(pc); + + uc->uc_mcontext.gregs[REG_PC] = (greg_t)stub; + return true; + } + + // signal-chaining + if (os::Linux::chained_handler(sig, info, ucVoid)) { + return true; + } + + if (!abort_if_unrecognized) { + // caller wants another chance, so give it to him + return false; + } + + if (pc == NULL && uc != NULL) { + pc = os::Linux::ucontext_get_pc(uc); + } + + // unmask current signal + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); + sigprocmask(SIG_UNBLOCK, &newset, NULL); + + VMError err(t, sig, pc, info, ucVoid); + err.report_and_die(); + + ShouldNotReachHere(); +} + +void os::Linux::init_thread_fpu_state(void) { +#ifndef AMD64 + // set fpu to 53 bit precision + set_fpu_control_word(0x27f); +#endif // !AMD64 +} + +int os::Linux::get_fpu_control_word(void) { +#ifdef AMD64 + return 0; +#else + int fpu_control; + _FPU_GETCW(fpu_control); + return fpu_control & 0xffff; +#endif // AMD64 +} + +void os::Linux::set_fpu_control_word(int fpu_control) { +#ifndef AMD64 + _FPU_SETCW(fpu_control); +#endif // !AMD64 +} + +// Check that the linux kernel version is 2.4 or higher since earlier +// versions do not support SSE without patches. +bool os::supports_sse() { +#ifdef AMD64 + return true; +#else + struct utsname uts; + if( uname(&uts) != 0 ) return false; // uname fails? + char *minor_string; + int major = strtol(uts.release,&minor_string,10); + int minor = strtol(minor_string+1,NULL,10); + bool result = (major > 2 || (major==2 && minor >= 4)); +#ifndef PRODUCT + if (PrintMiscellaneous && Verbose) { + tty->print("OS version is %d.%d, which %s support SSE/SSE2\n", + major,minor, result ? "DOES" : "does NOT"); + } +#endif + return result; +#endif // AMD64 +} + +bool os::is_allocatable(size_t bytes) { +#ifdef AMD64 + // unused on amd64? + return true; +#else + + if (bytes < 2 * G) { + return true; + } + + char* addr = reserve_memory(bytes, NULL); + + if (addr != NULL) { + release_memory(addr, bytes); + } + + return addr != NULL; +#endif // AMD64 +} + +//////////////////////////////////////////////////////////////////////////////// +// thread stack + +#ifdef AMD64 +size_t os::Linux::min_stack_allowed = 64 * K; + +// amd64: pthread on amd64 is always in floating stack mode +bool os::Linux::supports_variable_stack_size() { return true; } +#else +size_t os::Linux::min_stack_allowed = (48 DEBUG_ONLY(+4))*K; + +#define GET_GS() ({int gs; __asm__ volatile("movw %%gs, %w0":"=q"(gs)); gs&0xffff;}) + +// Test if pthread library can support variable thread stack size. LinuxThreads +// in fixed stack mode allocates 2M fixed slot for each thread. LinuxThreads +// in floating stack mode and NPTL support variable stack size. +bool os::Linux::supports_variable_stack_size() { + if (os::Linux::is_NPTL()) { + // NPTL, yes + return true; + + } else { + // Note: We can't control default stack size when creating a thread. + // If we use non-default stack size (pthread_attr_setstacksize), both + // floating stack and non-floating stack LinuxThreads will return the + // same value. This makes it impossible to implement this function by + // detecting thread stack size directly. + // + // An alternative approach is to check %gs. Fixed-stack LinuxThreads + // do not use %gs, so its value is 0. Floating-stack LinuxThreads use + // %gs (either as LDT selector or GDT selector, depending on kernel) + // to access thread specific data. + // + // Note that %gs is a reserved glibc register since early 2001, so + // applications are not allowed to change its value (Ulrich Drepper from + // Redhat confirmed that all known offenders have been modified to use + // either %fs or TSD). In the worst case scenario, when VM is embedded in + // a native application that plays with %gs, we might see non-zero %gs + // even LinuxThreads is running in fixed stack mode. As the result, we'll + // return true and skip _thread_safety_check(), so we may not be able to + // detect stack-heap collisions. But otherwise it's harmless. + // + return (GET_GS() != 0); + } +} +#endif // AMD64 + +// return default stack size for thr_type +size_t os::Linux::default_stack_size(os::ThreadType thr_type) { + // default stack size (compiler thread needs larger stack) +#ifdef AMD64 + size_t s = (thr_type == os::compiler_thread ? 4 * M : 1 * M); +#else + size_t s = (thr_type == os::compiler_thread ? 2 * M : 512 * K); +#endif // AMD64 + return s; +} + +size_t os::Linux::default_guard_size(os::ThreadType thr_type) { + // Creating guard page is very expensive. Java thread has HotSpot + // guard page, only enable glibc guard page for non-Java threads. + return (thr_type == java_thread ? 0 : page_size()); +} + +// Java thread: +// +// Low memory addresses +// +------------------------+ +// | |\ JavaThread created by VM does not have glibc +// | glibc guard page | - guard, attached Java thread usually has +// | |/ 1 page glibc guard. +// P1 +------------------------+ Thread::stack_base() - Thread::stack_size() +// | |\ +// | HotSpot Guard Pages | - red and yellow pages +// | |/ +// +------------------------+ JavaThread::stack_yellow_zone_base() +// | |\ +// | Normal Stack | - +// | |/ +// P2 +------------------------+ Thread::stack_base() +// +// Non-Java thread: +// +// Low memory addresses +// +------------------------+ +// | |\ +// | glibc guard page | - usually 1 page +// | |/ +// P1 +------------------------+ Thread::stack_base() - Thread::stack_size() +// | |\ +// | Normal Stack | - +// | |/ +// P2 +------------------------+ Thread::stack_base() +// +// ** P1 (aka bottom) and size ( P2 = P1 - size) are the address and stack size returned from +// pthread_attr_getstack() + +static void current_stack_region(address * bottom, size_t * size) { + if (os::Linux::is_initial_thread()) { + // initial thread needs special handling because pthread_getattr_np() + // may return bogus value. + *bottom = os::Linux::initial_thread_stack_bottom(); + *size = os::Linux::initial_thread_stack_size(); + } else { + pthread_attr_t attr; + + int rslt = pthread_getattr_np(pthread_self(), &attr); + + // JVM needs to know exact stack location, abort if it fails + if (rslt != 0) { + if (rslt == ENOMEM) { + vm_exit_out_of_memory(0, "pthread_getattr_np"); + } else { + fatal1("pthread_getattr_np failed with errno = %d", rslt); + } + } + + if (pthread_attr_getstack(&attr, (void **)bottom, size) != 0) { + fatal("Can not locate current stack attributes!"); + } + + pthread_attr_destroy(&attr); + + } + assert(os::current_stack_pointer() >= *bottom && + os::current_stack_pointer() < *bottom + *size, "just checking"); +} + +address os::current_stack_base() { + address bottom; + size_t size; + current_stack_region(&bottom, &size); + return (bottom + size); +} + +size_t os::current_stack_size() { + // stack size includes normal stack and HotSpot guard pages + address bottom; + size_t size; + current_stack_region(&bottom, &size); + return size; +} + +///////////////////////////////////////////////////////////////////////////// +// helper functions for fatal error handler + +void os::print_context(outputStream *st, void *context) { + if (context == NULL) return; + + ucontext_t *uc = (ucontext_t*)context; + st->print_cr("Registers:"); +#ifdef AMD64 + st->print( "RAX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RAX]); + st->print(", RBX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RBX]); + st->print(", RCX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RCX]); + st->print(", RDX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RDX]); + st->cr(); + st->print( "RSP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RSP]); + st->print(", RBP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RBP]); + st->print(", RSI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RSI]); + st->print(", RDI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RDI]); + st->cr(); + st->print( "R8 =" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R8]); + st->print(", R9 =" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R9]); + st->print(", R10=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R10]); + st->print(", R11=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R11]); + st->cr(); + st->print( "R12=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R12]); + st->print(", R13=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R13]); + st->print(", R14=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R14]); + st->print(", R15=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R15]); + st->cr(); + st->print( "RIP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RIP]); + st->print(", EFL=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EFL]); + st->print(", CSGSFS=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_CSGSFS]); + st->print(", ERR=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_ERR]); + st->cr(); + st->print(" TRAPNO=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_TRAPNO]); +#else + st->print( "EAX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EAX]); + st->print(", EBX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EBX]); + st->print(", ECX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_ECX]); + st->print(", EDX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EDX]); + st->cr(); + st->print( "ESP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_UESP]); + st->print(", EBP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EBP]); + st->print(", ESI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_ESI]); + st->print(", EDI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EDI]); + st->cr(); + st->print( "EIP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EIP]); + st->print(", CR2=" INTPTR_FORMAT, uc->uc_mcontext.cr2); + st->print(", EFLAGS=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EFL]); +#endif // AMD64 + st->cr(); + st->cr(); + + intptr_t *sp = (intptr_t *)os::Linux::ucontext_get_sp(uc); + st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", sp); + print_hex_dump(st, (address)sp, (address)(sp + 8*sizeof(intptr_t)), sizeof(intptr_t)); + st->cr(); + + // Note: it may be unsafe to inspect memory near pc. For example, pc may + // point to garbage if entry point in an nmethod is corrupted. Leave + // this at the end, and hope for the best. + address pc = os::Linux::ucontext_get_pc(uc); + st->print_cr("Instructions: (pc=" PTR_FORMAT ")", pc); + print_hex_dump(st, pc - 16, pc + 16, sizeof(char)); +} + +void os::setup_fpu() { +#ifndef AMD64 + address fpu_cntrl = StubRoutines::addr_fpu_cntrl_wrd_std(); + __asm__ volatile ( "fldcw (%0)" : + : "r" (fpu_cntrl) : "memory"); +#endif // !AMD64 +} diff --git a/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.hpp b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.hpp new file mode 100644 index 00000000000..f8780638455 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.hpp @@ -0,0 +1,32 @@ +/* + * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + static void setup_fpu(); + static bool supports_sse(); + + static bool is_allocatable(size_t bytes); + + // Used to register dynamic code cache area with the OS + // Note: Currently only used in 64 bit Windows implementations + static bool register_code_area(char *low, char *high) { return true; } diff --git a/hotspot/src/os_cpu/linux_x86/vm/prefetch_linux_x86.inline.hpp b/hotspot/src/os_cpu/linux_x86/vm/prefetch_linux_x86.inline.hpp new file mode 100644 index 00000000000..ba932e6b277 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/prefetch_linux_x86.inline.hpp @@ -0,0 +1,40 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +inline void Prefetch::read (void *loc, intx interval) { +#ifdef AMD64 + __asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval)); +#endif // AMD64 +} + +inline void Prefetch::write(void *loc, intx interval) { +#ifdef AMD64 + + // Do not use the 3dnow prefetchw instruction. It isn't supported on em64t. + // __asm__ ("prefetchw (%0,%1,1)" : : "r" (loc), "r" (interval)); + __asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval)); + +#endif // AMD64 +} diff --git a/hotspot/src/os_cpu/linux_x86/vm/threadLS_linux_x86.cpp b/hotspot/src/os_cpu/linux_x86/vm/threadLS_linux_x86.cpp new file mode 100644 index 00000000000..a7ada996df0 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/threadLS_linux_x86.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_threadLS_linux_x86.cpp.incl" + +// Map stack pointer (%esp) to thread pointer for faster TLS access +// +// Here we use a flat table for better performance. Getting current thread +// is down to one memory access (read _sp_map[%esp>>12]) in generated code +// and two in runtime code (-fPIC code needs an extra load for _sp_map). +// +// This code assumes stack page is not shared by different threads. It works +// in 32-bit VM when page size is 4K (or a multiple of 4K, if that matters). +// +// Notice that _sp_map is allocated in the bss segment, which is ZFOD +// (zero-fill-on-demand). While it reserves 4M address space upfront, +// actual memory pages are committed on demand. +// +// If an application creates and destroys a lot of threads, usually the +// stack space freed by a thread will soon get reused by new thread +// (this is especially true in NPTL or LinuxThreads in fixed-stack mode). +// No memory page in _sp_map is wasted. +// +// However, it's still possible that we might end up populating & +// committing a large fraction of the 4M table over time, but the actual +// amount of live data in the table could be quite small. The max wastage +// is less than 4M bytes. If it becomes an issue, we could use madvise() +// with MADV_DONTNEED to reclaim unused (i.e. all-zero) pages in _sp_map. +// MADV_DONTNEED on Linux keeps the virtual memory mapping, but zaps the +// physical memory page (i.e. similar to MADV_FREE on Solaris). + +#ifndef AMD64 +Thread* ThreadLocalStorage::_sp_map[1UL << (SP_BITLENGTH - PAGE_SHIFT)]; +#endif // !AMD64 + +void ThreadLocalStorage::generate_code_for_get_thread() { + // nothing we can do here for user-level thread +} + +void ThreadLocalStorage::pd_init() { +#ifndef AMD64 + assert(align_size_down(os::vm_page_size(), PAGE_SIZE) == os::vm_page_size(), + "page size must be multiple of PAGE_SIZE"); +#endif // !AMD64 +} + +void ThreadLocalStorage::pd_set_thread(Thread* thread) { + os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread); + +#ifndef AMD64 + address stack_top = os::current_stack_base(); + size_t stack_size = os::current_stack_size(); + + for (address p = stack_top - stack_size; p < stack_top; p += PAGE_SIZE) { + // pd_set_thread() is called with non-NULL value when a new thread is + // created/attached, or with NULL value when a thread is about to exit. + // If both "thread" and the corresponding _sp_map[] entry are non-NULL, + // they should have the same value. Otherwise it might indicate that the + // stack page is shared by multiple threads. However, a more likely cause + // for this assertion to fail is that an attached thread exited without + // detaching itself from VM, which is a program error and could cause VM + // to crash. + assert(thread == NULL || _sp_map[(uintptr_t)p >> PAGE_SHIFT] == NULL || + thread == _sp_map[(uintptr_t)p >> PAGE_SHIFT], + "thread exited without detaching from VM??"); + _sp_map[(uintptr_t)p >> PAGE_SHIFT] = thread; + } +#endif // !AMD64 +} diff --git a/hotspot/src/os_cpu/linux_x86/vm/threadLS_linux_x86.hpp b/hotspot/src/os_cpu/linux_x86/vm/threadLS_linux_x86.hpp new file mode 100644 index 00000000000..9bfe9064a63 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/threadLS_linux_x86.hpp @@ -0,0 +1,49 @@ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // Processor dependent parts of ThreadLocalStorage + +#ifndef AMD64 + // map stack pointer to thread pointer - see notes in threadLS_linux_x86.cpp + #define SP_BITLENGTH 32 + #define PAGE_SHIFT 12 + #define PAGE_SIZE (1UL << PAGE_SHIFT) + static Thread* _sp_map[1UL << (SP_BITLENGTH - PAGE_SHIFT)]; +#endif // !AMD64 + +public: + +#ifndef AMD64 + static Thread** sp_map_addr() { return _sp_map; } +#endif // !AMD64 + + static Thread* thread() { +#ifdef AMD64 + return (Thread*) os::thread_local_storage_at(thread_index()); +#else + uintptr_t sp; + __asm__ volatile ("movl %%esp, %0" : "=r" (sp)); + return _sp_map[sp >> PAGE_SHIFT]; +#endif // AMD64 + } diff --git a/hotspot/src/os_cpu/linux_x86/vm/thread_linux_x86.cpp b/hotspot/src/os_cpu/linux_x86/vm/thread_linux_x86.cpp new file mode 100644 index 00000000000..d623e0e5204 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/thread_linux_x86.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_thread_linux_x86.cpp.incl" + +// For Forte Analyzer AsyncGetCallTrace profiling support - thread is +// currently interrupted by SIGPROF +bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, + void* ucontext, bool isInJava) { + + assert(Thread::current() == this, "caller must be current thread"); + assert(this->is_Java_thread(), "must be JavaThread"); + + JavaThread* jt = (JavaThread *)this; + + // If we have a last_Java_frame, then we should use it even if + // isInJava == true. It should be more reliable than ucontext info. + if (jt->has_last_Java_frame()) { + *fr_addr = jt->pd_last_frame(); + return true; + } + + // At this point, we don't have a last_Java_frame, so + // we try to glean some information out of the ucontext + // if we were running Java code when SIGPROF came in. + if (isInJava) { + ucontext_t* uc = (ucontext_t*) ucontext; + + intptr_t* ret_fp; + intptr_t* ret_sp; + ExtendedPC addr = os::Linux::fetch_frame_from_ucontext(this, uc, + &ret_sp, &ret_fp); + if (addr.pc() == NULL || ret_sp == NULL ) { + // ucontext wasn't useful + return false; + } + + frame ret_frame(ret_sp, ret_fp, addr.pc()); + if (!ret_frame.safe_for_sender(jt)) { +#ifdef COMPILER2 + // C2 uses ebp as a general register see if NULL fp helps + frame ret_frame2(ret_sp, NULL, addr.pc()); + if (!ret_frame2.safe_for_sender(jt)) { + // nothing else to try if the frame isn't good + return false; + } + ret_frame = ret_frame2; +#else + // nothing else to try if the frame isn't good + return false; +#endif /* COMPILER2 */ + } + *fr_addr = ret_frame; + return true; + } + + // nothing else to try + return false; +} diff --git a/hotspot/src/os_cpu/linux_x86/vm/thread_linux_x86.hpp b/hotspot/src/os_cpu/linux_x86/vm/thread_linux_x86.hpp new file mode 100644 index 00000000000..3911b51a342 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/thread_linux_x86.hpp @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: + void pd_initialize() { + _anchor.clear(); + } + + frame pd_last_frame() { + assert(has_last_Java_frame(), "must have last_Java_sp() when suspended"); + if (_anchor.last_Java_pc() != NULL) { + return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp(), _anchor.last_Java_pc()); + } else { + // This will pick up pc from sp + return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp()); + } + } + + public: + // Mutators are highly dangerous.... + intptr_t* last_Java_fp() { return _anchor.last_Java_fp(); } + void set_last_Java_fp(intptr_t* fp) { _anchor.set_last_Java_fp(fp); } + + void set_base_of_stack_pointer(intptr_t* base_sp) { + } + + static ByteSize last_Java_fp_offset() { + return byte_offset_of(JavaThread, _anchor) + JavaFrameAnchor::last_Java_fp_offset(); + } + + intptr_t* base_of_stack_pointer() { + return NULL; + } + void record_base_of_stack_pointer() { + } + + bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, + bool isInJava); + + // These routines are only used on cpu architectures that + // have separate register stacks (Itanium). + static bool register_stack_overflow() { return false; } + static void enable_register_stack_guard() {} + static void disable_register_stack_guard() {} diff --git a/hotspot/src/os_cpu/linux_x86/vm/vmStructs_linux_x86.hpp b/hotspot/src/os_cpu/linux_x86/vm/vmStructs_linux_x86.hpp new file mode 100644 index 00000000000..17996f4e203 --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/vmStructs_linux_x86.hpp @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// These are the OS and CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, pid_t) \ + nonstatic_field(OSThread, _pthread_id, pthread_t) \ + /* This must be the last entry, and must be present */ \ + last_entry() + + +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ + \ + /**********************/ \ + /* Posix Thread IDs */ \ + /**********************/ \ + \ + declare_integer_type(pid_t) \ + declare_unsigned_integer_type(pthread_t) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() diff --git a/hotspot/src/os_cpu/linux_x86/vm/vm_version_linux_x86.cpp b/hotspot/src/os_cpu/linux_x86/vm/vm_version_linux_x86.cpp new file mode 100644 index 00000000000..647b264746f --- /dev/null +++ b/hotspot/src/os_cpu/linux_x86/vm/vm_version_linux_x86.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vm_version_linux_x86.cpp.incl" diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/assembler_solaris_sparc.cpp b/hotspot/src/os_cpu/solaris_sparc/vm/assembler_solaris_sparc.cpp new file mode 100644 index 00000000000..8e18dde9c31 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/assembler_solaris_sparc.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler_solaris_sparc.cpp.incl" + +#include // For trap numbers +#include // For V8 compatibility + +bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { + // The first page of virtual addresses is unmapped on SPARC. + // Thus, any access the VM makes through a null pointer with an offset of + // less than 4K will get a recognizable SIGSEGV, which the signal handler + // will transform into a NullPointerException. + // (Actually, the first 64K or so is unmapped, but it's simpler + // to depend only on the first 4K or so.) + + bool offset_in_first_page = 0 <= offset && offset < os::vm_page_size(); + return !offset_in_first_page; +} + +void MacroAssembler::read_ccr_trap(Register ccr_save) { + // Execute a trap to get the PSR, mask and shift + // to get the condition codes. + get_psr_trap(); + nop(); + set(PSR_ICC, ccr_save); + and3(O0, ccr_save, ccr_save); + srl(ccr_save, PSR_ICC_SHIFT, ccr_save); +} + +void MacroAssembler::write_ccr_trap(Register ccr_save, Register scratch1, Register scratch2) { + // Execute a trap to get the PSR, shift back + // the condition codes, mask the condition codes + // back into and PSR and trap to write back the + // PSR. + sll(ccr_save, PSR_ICC_SHIFT, scratch2); + get_psr_trap(); + nop(); + set(~PSR_ICC, scratch1); + and3(O0, scratch1, O0); + or3(O0, scratch2, O0); + set_psr_trap(); + nop(); +} + +void MacroAssembler::flush_windows_trap() { trap(ST_FLUSH_WINDOWS); } +void MacroAssembler::clean_windows_trap() { trap(ST_CLEAN_WINDOWS); } +void MacroAssembler::get_psr_trap() { trap(ST_GETPSR); } +void MacroAssembler::set_psr_trap() { trap(ST_SETPSR); } diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/atomic_solaris_sparc.inline.hpp b/hotspot/src/os_cpu/solaris_sparc/vm/atomic_solaris_sparc.inline.hpp new file mode 100644 index 00000000000..e3ee2bc23ba --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/atomic_solaris_sparc.inline.hpp @@ -0,0 +1,342 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Implementation of class atomic + +inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, jint* dest) { *dest = store_value; } +inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } +inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, void* dest) { *(void**)dest = store_value; } + +inline void Atomic::store (jbyte store_value, volatile jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, volatile jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, volatile jint* dest) { *dest = store_value; } +inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } +inline void Atomic::store_ptr(intptr_t store_value, volatile intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, volatile void* dest) { *(void* volatile *)dest = store_value; } + +inline void Atomic::inc (volatile jint* dest) { (void)add (1, dest); } +inline void Atomic::inc_ptr(volatile intptr_t* dest) { (void)add_ptr(1, dest); } +inline void Atomic::inc_ptr(volatile void* dest) { (void)add_ptr(1, dest); } + +inline void Atomic::dec (volatile jint* dest) { (void)add (-1, dest); } +inline void Atomic::dec_ptr(volatile intptr_t* dest) { (void)add_ptr(-1, dest); } +inline void Atomic::dec_ptr(volatile void* dest) { (void)add_ptr(-1, dest); } + +#ifdef _GNU_SOURCE + +inline jint Atomic::add (jint add_value, volatile jint* dest) { + intptr_t rv; + __asm__ volatile( + "1: \n\t" + " ld [%2], %%o2\n\t" + " add %1, %%o2, %%o3\n\t" + " cas [%2], %%o2, %%o3\n\t" + " cmp %%o2, %%o3\n\t" + " bne 1b\n\t" + " nop\n\t" + " add %1, %%o2, %0\n\t" + : "=r" (rv) + : "r" (add_value), "r" (dest) + : "memory", "o2", "o3"); + return rv; +} + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { + intptr_t rv; +#ifdef _LP64 + __asm__ volatile( + "1: \n\t" + " ldx [%2], %%o2\n\t" + " add %0, %%o2, %%o3\n\t" + " casx [%2], %%o2, %%o3\n\t" + " cmp %%o2, %%o3\n\t" + " bne %%xcc, 1b\n\t" + " nop\n\t" + " add %0, %%o2, %0\n\t" + : "=r" (rv) + : "r" (add_value), "r" (dest) + : "memory", "o2", "o3"); +#else //_LP64 + __asm__ volatile( + "1: \n\t" + " ld [%2], %%o2\n\t" + " add %1, %%o2, %%o3\n\t" + " cas [%2], %%o2, %%o3\n\t" + " cmp %%o2, %%o3\n\t" + " bne 1b\n\t" + " nop\n\t" + " add %1, %%o2, %0\n\t" + : "=r" (rv) + : "r" (add_value), "r" (dest) + : "memory", "o2", "o3"); +#endif // _LP64 + return rv; +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)add_ptr((intptr_t)add_value, (volatile intptr_t*)dest); +} + + +inline jint Atomic::xchg (jint exchange_value, volatile jint* dest) { + intptr_t rv = exchange_value; + __asm__ volatile( + " swap [%2],%1\n\t" + : "=r" (rv) + : "0" (exchange_value) /* we use same register as for return value */, "r" (dest) + : "memory"); + return rv; +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { + intptr_t rv = exchange_value; +#ifdef _LP64 + __asm__ volatile( + "1:\n\t" + " mov %1, %%o3\n\t" + " ldx [%2], %%o2\n\t" + " casx [%2], %%o2, %%o3\n\t" + " cmp %%o2, %%o3\n\t" + " bne %%xcc, 1b\n\t" + " nop\n\t" + " mov %%o2, %0\n\t" + : "=r" (rv) + : "r" (exchange_value), "r" (dest) + : "memory", "o2", "o3"); +#else //_LP64 + __asm__ volatile( + "swap [%2],%1\n\t" + : "=r" (rv) + : "0" (exchange_value) /* we use same register as for return value */, "r" (dest) + : "memory"); +#endif // _LP64 + return rv; +} + +inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { + return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest); +} + + +inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { + jint rv; + __asm__ volatile( + " cas [%2], %3, %0" + : "=r" (rv) + : "0" (exchange_value), "r" (dest), "r" (compare_value) + : "memory"); + return rv; +} + +inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) { +#ifdef _LP64 + jlong rv; + __asm__ volatile( + " casx [%2], %3, %0" + : "=r" (rv) + : "0" (exchange_value), "r" (dest), "r" (compare_value) + : "memory"); + return rv; +#else //_LP64 + assert(VM_Version::v9_instructions_work(), "cas only supported on v9"); + volatile jlong_accessor evl, cvl, rv; + evl.long_value = exchange_value; + cvl.long_value = compare_value; + + __asm__ volatile( + " sllx %2, 32, %2\n\t" + " srl %3, 0, %3\n\t" + " or %2, %3, %2\n\t" + " sllx %5, 32, %5\n\t" + " srl %6, 0, %6\n\t" + " or %5, %6, %5\n\t" + " casx [%4], %5, %2\n\t" + " srl %2, 0, %1\n\t" + " srlx %2, 32, %0\n\t" + : "=r" (rv.words[0]), "=r" (rv.words[1]) + : "r" (evl.words[0]), "r" (evl.words[1]), "r" (dest), "r" (cvl.words[0]), "r" (cvl.words[1]) + : "memory"); + + return rv.long_value; +#endif //_LP64 +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { + intptr_t rv; +#ifdef _LP64 + __asm__ volatile( + " casx [%2], %3, %0" + : "=r" (rv) + : "0" (exchange_value), "r" (dest), "r" (compare_value) + : "memory"); +#else //_LP64 + __asm__ volatile( + " cas [%2], %3, %0" + : "=r" (rv) + : "0" (exchange_value), "r" (dest), "r" (compare_value) + : "memory"); +#endif // _LP64 + return rv; +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value); +} + +#else // _GNU_SOURCE + +#if defined(COMPILER2) || defined(_LP64) + +// This is the interface to the atomic instructions in solaris_sparc.il. +// It's very messy because we need to support v8 and these instructions +// are illegal there. When sparc v8 is dropped, we can drop out lots of +// this code. Also compiler2 does not support v8 so the conditional code +// omits the instruction set check. + +extern "C" jint _Atomic_swap32(jint exchange_value, volatile jint* dest); +extern "C" intptr_t _Atomic_swap64(intptr_t exchange_value, volatile intptr_t* dest); + +extern "C" jint _Atomic_cas32(jint exchange_value, volatile jint* dest, jint compare_value); +extern "C" intptr_t _Atomic_cas64(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value); +extern "C" jlong _Atomic_casl (jlong exchange_value, volatile jlong* dest, jlong compare_value); + +extern "C" jint _Atomic_add32(jint inc, volatile jint* dest); +extern "C" intptr_t _Atomic_add64(intptr_t add_value, volatile intptr_t* dest); + + +inline jint Atomic::add (jint add_value, volatile jint* dest) { + return _Atomic_add32(add_value, dest); +} + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { +#ifdef _LP64 + return _Atomic_add64(add_value, dest); +#else //_LP64 + return _Atomic_add32(add_value, dest); +#endif // _LP64 +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)add_ptr((intptr_t)add_value, (volatile intptr_t*)dest); +} + + +inline jint Atomic::xchg (jint exchange_value, volatile jint* dest) { + return _Atomic_swap32(exchange_value, dest); +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { +#ifdef _LP64 + return _Atomic_swap64(exchange_value, dest); +#else // _LP64 + return _Atomic_swap32(exchange_value, dest); +#endif // _LP64 +} + +inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { + return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest); +} + + +inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { + return _Atomic_cas32(exchange_value, dest, compare_value); +} + +inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) { +#ifdef _LP64 + // Return 64 bit value in %o0 + return _Atomic_cas64((intptr_t)exchange_value, (intptr_t *)dest, (intptr_t)compare_value); +#else // _LP64 + assert (VM_Version::v9_instructions_work(), "only supported on v9"); + // Return 64 bit value in %o0,%o1 by hand + return _Atomic_casl(exchange_value, dest, compare_value); +#endif // _LP64 +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { +#ifdef _LP64 + return _Atomic_cas64(exchange_value, dest, compare_value); +#else // _LP64 + return _Atomic_cas32(exchange_value, dest, compare_value); +#endif // _LP64 +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value); +} + + +#else // _LP64 || COMPILER2 + + +// 32-bit compiler1 only + +inline jint Atomic::add (jint add_value, volatile jint* dest) { + return (*os::atomic_add_func)(add_value, dest); +} + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { + return (intptr_t)add((jint)add_value, (volatile jint*)dest); +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)add((jint)add_value, (volatile jint*)dest); +} + + +inline jint Atomic::xchg (jint exchange_value, volatile jint* dest) { + return (*os::atomic_xchg_func)(exchange_value, dest); +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { + return (intptr_t)xchg((jint)exchange_value, (volatile jint*)dest); +} + +inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { + return (void*)xchg((jint)exchange_value, (volatile jint*)dest); +} + + +inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { + return (*os::atomic_cmpxchg_func)(exchange_value, dest, compare_value); +} + +inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) { + return (*os::atomic_cmpxchg_long_func)(exchange_value, dest, compare_value); +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { + return (intptr_t)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value); +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value); +} + +#endif // _LP64 || COMPILER2 + +#endif // _GNU_SOURCE diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/globals_solaris_sparc.hpp b/hotspot/src/os_cpu/solaris_sparc/vm/globals_solaris_sparc.hpp new file mode 100644 index 00000000000..c0bf513235e --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/globals_solaris_sparc.hpp @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for platform dependent flags used by the runtime system. +// (see globals.hpp) +// + +define_pd_global(uintx, JVMInvokeMethodSlack, 12288); +define_pd_global(intx, CompilerThreadStackSize, 0); + +// Only used on 64 bit Windows platforms +define_pd_global(bool, UseVectoredExceptions, false); diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/orderAccess_solaris_sparc.inline.hpp b/hotspot/src/os_cpu/solaris_sparc/vm/orderAccess_solaris_sparc.inline.hpp new file mode 100644 index 00000000000..c80d89157e4 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/orderAccess_solaris_sparc.inline.hpp @@ -0,0 +1,136 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Implementation of class OrderAccess. + +// Assume TSO. + +// In solaris_sparc.il +extern "C" void _OrderAccess_acquire(); +extern "C" void _OrderAccess_fence(); + +inline void OrderAccess::loadload() { acquire(); } +inline void OrderAccess::storestore() { release(); } +inline void OrderAccess::loadstore() { acquire(); } +inline void OrderAccess::storeload() { fence(); } + +#ifdef _GNU_SOURCE + +inline void OrderAccess::acquire() { + __asm__ volatile ("nop" : : :); +} + +inline void OrderAccess::release() { + jint* dummy = (jint*)&dummy; + __asm__ volatile("stw %%g0, [%0]" : : "r" (dummy) : "memory"); +} + +inline void OrderAccess::fence() { + __asm__ volatile ("membar #StoreLoad" : : :); +} + +#else // _GNU_SOURCE + +inline void OrderAccess::acquire() { + _OrderAccess_acquire(); +} + +inline void OrderAccess::release() { + dummy = 0; +} + +#if defined(COMPILER2) || defined(_LP64) + +inline void OrderAccess::fence() { + _OrderAccess_fence(); +} + +#else // defined(COMPILER2) || defined(_LP64) + +inline void OrderAccess::fence() { + if (os::is_MP()) { + (*os::fence_func)(); + } +} + +#endif // defined(COMPILER2) || defined(_LP64) + +#endif // _GNU_SOURCE + +inline jbyte OrderAccess::load_acquire(volatile jbyte* p) { return *p; } +inline jshort OrderAccess::load_acquire(volatile jshort* p) { return *p; } +inline jint OrderAccess::load_acquire(volatile jint* p) { return *p; } +inline jlong OrderAccess::load_acquire(volatile jlong* p) { return *p; } +inline jubyte OrderAccess::load_acquire(volatile jubyte* p) { return *p; } +inline jushort OrderAccess::load_acquire(volatile jushort* p) { return *p; } +inline juint OrderAccess::load_acquire(volatile juint* p) { return *p; } +inline julong OrderAccess::load_acquire(volatile julong* p) { return *p; } +inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { return *p; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return *p; } + +inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return *p; } +inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return *(void* volatile *)p; } +inline void* OrderAccess::load_ptr_acquire(const volatile void* p) { return *(void* const volatile *)p; } + +inline void OrderAccess::release_store(volatile jbyte* p, jbyte v) { *p = v; } +inline void OrderAccess::release_store(volatile jshort* p, jshort v) { *p = v; } +inline void OrderAccess::release_store(volatile jint* p, jint v) { *p = v; } +inline void OrderAccess::release_store(volatile jlong* p, jlong v) { *p = v; } +inline void OrderAccess::release_store(volatile jubyte* p, jubyte v) { *p = v; } +inline void OrderAccess::release_store(volatile jushort* p, jushort v) { *p = v; } +inline void OrderAccess::release_store(volatile juint* p, juint v) { *p = v; } +inline void OrderAccess::release_store(volatile julong* p, julong v) { *p = v; } +inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { *p = v; } + +inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { *p = v; } +inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { *(void* volatile *)p = v; } + +inline void OrderAccess::store_fence(jbyte* p, jbyte v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jshort* p, jshort v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jint* p, jint v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jlong* p, jlong v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jubyte* p, jubyte v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jushort* p, jushort v) { *p = v; fence(); } +inline void OrderAccess::store_fence(juint* p, juint v) { *p = v; fence(); } +inline void OrderAccess::store_fence(julong* p, julong v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jfloat* p, jfloat v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jdouble* p, jdouble v) { *p = v; fence(); } + +inline void OrderAccess::store_ptr_fence(intptr_t* p, intptr_t v) { *p = v; fence(); } +inline void OrderAccess::store_ptr_fence(void** p, void* v) { *p = v; fence(); } + +inline void OrderAccess::release_store_fence(volatile jbyte* p, jbyte v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jshort* p, jshort v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jint* p, jint v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jlong* p, jlong v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jubyte* p, jubyte v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { *p = v; fence(); } + +inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { *p = v; fence(); } +inline void OrderAccess::release_store_ptr_fence(volatile void* p, void* v) { *(void* volatile *)p = v; fence(); } diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp b/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp new file mode 100644 index 00000000000..012c170a980 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp @@ -0,0 +1,712 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// do not include precompiled header file + +# include // needed first to avoid name collision for "std" with SC 5.0 + +# include "incls/_os_solaris_sparc.cpp.incl" + +// put OS-includes here +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# define _STRUCTURED_PROC 1 // this gets us the new structured proc interfaces of 5.6 & later +# include // see comment in + +#define MAX_PATH (2 * K) + +// Minimum stack size for the VM. It's easier to document a constant +// but it's different for x86 and sparc because the page sizes are different. +#ifdef _LP64 +size_t os::Solaris::min_stack_allowed = 128*K; +#else +size_t os::Solaris::min_stack_allowed = 96*K; +#endif + +int os::Solaris::max_register_window_saves_before_flushing() { + // We should detect this at run time. For now, filling + // in with a constant. + return 8; +} + +static void handle_unflushed_register_windows(gwindows_t *win) { + int restore_count = win->wbcnt; + int i; + + for(i=0; ispbuf[i]) + STACK_BIAS; + address reg_win = (address)&win->wbuf[i]; + memcpy(sp,reg_win,sizeof(struct rwindow)); + } +} + +char* os::non_memory_address_word() { + // Must never look like an address returned by reserve_memory, + // even in its subfields (as defined by the CPU immediate fields, + // if the CPU splits constants across multiple instructions). + // On SPARC, 0 != %hi(any real address), because there is no + // allocation in the first 1Kb of the virtual address space. + return (char*) 0; +} + +// Validate a ucontext retrieved from walking a uc_link of a ucontext. +// There are issues with libthread giving out uc_links for different threads +// on the same uc_link chain and bad or circular links. +// +bool os::Solaris::valid_ucontext(Thread* thread, ucontext_t* valid, ucontext_t* suspect) { + if (valid >= suspect || + valid->uc_stack.ss_flags != suspect->uc_stack.ss_flags || + valid->uc_stack.ss_sp != suspect->uc_stack.ss_sp || + valid->uc_stack.ss_size != suspect->uc_stack.ss_size) { + DEBUG_ONLY(tty->print_cr("valid_ucontext: failed test 1");) + return false; + } + + if (thread->is_Java_thread()) { + if (!valid_stack_address(thread, (address)suspect)) { + DEBUG_ONLY(tty->print_cr("valid_ucontext: uc_link not in thread stack");) + return false; + } + address _sp = (address)((intptr_t)suspect->uc_mcontext.gregs[REG_SP] + STACK_BIAS); + if (!valid_stack_address(thread, _sp) || + !frame::is_valid_stack_pointer(((JavaThread*)thread)->base_of_stack_pointer(), (intptr_t*)_sp)) { + DEBUG_ONLY(tty->print_cr("valid_ucontext: stackpointer not in thread stack");) + return false; + } + } + return true; +} + +// We will only follow one level of uc_link since there are libthread +// issues with ucontext linking and it is better to be safe and just +// let caller retry later. +ucontext_t* os::Solaris::get_valid_uc_in_signal_handler(Thread *thread, + ucontext_t *uc) { + + ucontext_t *retuc = NULL; + + // Sometimes the topmost register windows are not properly flushed. + // i.e., if the kernel would have needed to take a page fault + if (uc != NULL && uc->uc_mcontext.gwins != NULL) { + ::handle_unflushed_register_windows(uc->uc_mcontext.gwins); + } + + if (uc != NULL) { + if (uc->uc_link == NULL) { + // cannot validate without uc_link so accept current ucontext + retuc = uc; + } else if (os::Solaris::valid_ucontext(thread, uc, uc->uc_link)) { + // first ucontext is valid so try the next one + uc = uc->uc_link; + if (uc->uc_link == NULL) { + // cannot validate without uc_link so accept current ucontext + retuc = uc; + } else if (os::Solaris::valid_ucontext(thread, uc, uc->uc_link)) { + // the ucontext one level down is also valid so return it + retuc = uc; + } + } + } + return retuc; +} + +// Assumes ucontext is valid +ExtendedPC os::Solaris::ucontext_get_ExtendedPC(ucontext_t *uc) { + address pc = (address)uc->uc_mcontext.gregs[REG_PC]; + // set npc to zero to avoid using it for safepoint, good for profiling only + return ExtendedPC(pc); +} + +// Assumes ucontext is valid +intptr_t* os::Solaris::ucontext_get_sp(ucontext_t *uc) { + return (intptr_t*)((intptr_t)uc->uc_mcontext.gregs[REG_SP] + STACK_BIAS); +} + +// Solaris X86 only +intptr_t* os::Solaris::ucontext_get_fp(ucontext_t *uc) { + ShouldNotReachHere(); + return NULL; +} + +// For Forte Analyzer AsyncGetCallTrace profiling support - thread +// is currently interrupted by SIGPROF. +// +// ret_fp parameter is only used by Solaris X86. +// +// The difference between this and os::fetch_frame_from_context() is that +// here we try to skip nested signal frames. +ExtendedPC os::Solaris::fetch_frame_from_ucontext(Thread* thread, + ucontext_t* uc, intptr_t** ret_sp, intptr_t** ret_fp) { + + assert(thread != NULL, "just checking"); + assert(ret_sp != NULL, "just checking"); + assert(ret_fp == NULL, "just checking"); + + ucontext_t *luc = os::Solaris::get_valid_uc_in_signal_handler(thread, uc); + + return os::fetch_frame_from_context(luc, ret_sp, ret_fp); +} + + +// ret_fp parameter is only used by Solaris X86. +ExtendedPC os::fetch_frame_from_context(void* ucVoid, + intptr_t** ret_sp, intptr_t** ret_fp) { + + ExtendedPC epc; + ucontext_t *uc = (ucontext_t*)ucVoid; + + if (uc != NULL) { + epc = os::Solaris::ucontext_get_ExtendedPC(uc); + if (ret_sp) *ret_sp = os::Solaris::ucontext_get_sp(uc); + } else { + // construct empty ExtendedPC for return value checking + epc = ExtendedPC(NULL); + if (ret_sp) *ret_sp = (intptr_t *)NULL; + } + + return epc; +} + +frame os::fetch_frame_from_context(void* ucVoid) { + intptr_t* sp; + intptr_t* fp; + ExtendedPC epc = fetch_frame_from_context(ucVoid, &sp, &fp); + return frame(sp, frame::unpatchable, epc.pc()); +} + +frame os::get_sender_for_C_frame(frame* fr) { + return frame(fr->sender_sp(), frame::unpatchable, fr->sender_pc()); +} + +frame os::current_frame() { + intptr_t* sp = StubRoutines::Sparc::flush_callers_register_windows_func()(); + frame myframe(sp, frame::unpatchable, + CAST_FROM_FN_PTR(address, os::current_frame)); + if (os::is_first_C_frame(&myframe)) { + // stack is not walkable + return frame(NULL, NULL, NULL); + } else { + return os::get_sender_for_C_frame(&myframe); + } +} + + +void GetThreadPC_Callback::execute(OSThread::InterruptArguments *args) { + Thread* thread = args->thread(); + ucontext_t* uc = args->ucontext(); + intptr_t* sp; + + assert(ProfileVM && thread->is_VM_thread(), "just checking"); + + // Skip the mcontext corruption verification. If if occasionally + // things get corrupt, it is ok for profiling - we will just get an unresolved + // function name + ExtendedPC new_addr((address)uc->uc_mcontext.gregs[REG_PC]); + _addr = new_addr; +} + + +static int threadgetstate(thread_t tid, int *flags, lwpid_t *lwp, stack_t *ss, gregset_t rs, lwpstatus_t *lwpstatus) { + char lwpstatusfile[PROCFILE_LENGTH]; + int lwpfd, err; + + if (err = os::Solaris::thr_getstate(tid, flags, lwp, ss, rs)) + return (err); + if (*flags == TRS_LWPID) { + sprintf(lwpstatusfile, "/proc/%d/lwp/%d/lwpstatus", getpid(), + *lwp); + if ((lwpfd = open(lwpstatusfile, O_RDONLY)) < 0) { + perror("thr_mutator_status: open lwpstatus"); + return (EINVAL); + } + if (pread(lwpfd, lwpstatus, sizeof (lwpstatus_t), (off_t)0) != + sizeof (lwpstatus_t)) { + perror("thr_mutator_status: read lwpstatus"); + (void) close(lwpfd); + return (EINVAL); + } + (void) close(lwpfd); + } + return (0); +} + + +bool os::is_allocatable(size_t bytes) { +#ifdef _LP64 + return true; +#else + return (bytes <= (size_t)3835*M); +#endif +} + +extern "C" void Fetch32PFI () ; +extern "C" void Fetch32Resume () ; +extern "C" void FetchNPFI () ; +extern "C" void FetchNResume () ; + +extern "C" int JVM_handle_solaris_signal(int signo, siginfo_t* siginfo, void* ucontext, int abort_if_unrecognized); + +int JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrecognized) { + ucontext_t* uc = (ucontext_t*) ucVoid; + + Thread* t = ThreadLocalStorage::get_thread_slow(); + + SignalHandlerMark shm(t); + + if(sig == SIGPIPE || sig == SIGXFSZ) { + if (os::Solaris::chained_handler(sig, info, ucVoid)) { + return true; + } else { + if (PrintMiscellaneous && (WizardMode || Verbose)) { + char buf[64]; + warning("Ignoring %s - see 4229104 or 6499219", + os::exception_name(sig, buf, sizeof(buf))); + + } + return true; + } + } + + JavaThread* thread = NULL; + VMThread* vmthread = NULL; + if (os::Solaris::signal_handlers_are_installed) { + if (t != NULL ){ + if(t->is_Java_thread()) { + thread = (JavaThread*)t; + } + else if(t->is_VM_thread()){ + vmthread = (VMThread *)t; + } + } + } + + guarantee(sig != os::Solaris::SIGinterrupt(), "Can not chain VM interrupt signal, try -XX:+UseAltSigs"); + + if (sig == os::Solaris::SIGasync()) { + if (thread) { + OSThread::InterruptArguments args(thread, uc); + thread->osthread()->do_interrupt_callbacks_at_interrupt(&args); + return true; + } else if (vmthread) { + OSThread::InterruptArguments args(vmthread, uc); + vmthread->osthread()->do_interrupt_callbacks_at_interrupt(&args); + return true; + } else if (os::Solaris::chained_handler(sig, info, ucVoid)) { + return true; + } else { + // If os::Solaris::SIGasync not chained, and this is a non-vm and + // non-java thread + return true; + } + } + + if (info == NULL || info->si_code <= 0 || info->si_code == SI_NOINFO) { + // can't decode this kind of signal + info = NULL; + } else { + assert(sig == info->si_signo, "bad siginfo"); + } + + // decide if this trap can be handled by a stub + address stub = NULL; + + address pc = NULL; + address npc = NULL; + + //%note os_trap_1 + if (info != NULL && uc != NULL && thread != NULL) { + // factor me: getPCfromContext + pc = (address) uc->uc_mcontext.gregs[REG_PC]; + npc = (address) uc->uc_mcontext.gregs[REG_nPC]; + + // SafeFetch() support + // Implemented with either a fixed set of addresses such + // as Fetch32*, or with Thread._OnTrap. + if (uc->uc_mcontext.gregs[REG_PC] == intptr_t(Fetch32PFI)) { + uc->uc_mcontext.gregs [REG_PC] = intptr_t(Fetch32Resume) ; + uc->uc_mcontext.gregs [REG_nPC] = intptr_t(Fetch32Resume) + 4 ; + return true ; + } + if (uc->uc_mcontext.gregs[REG_PC] == intptr_t(FetchNPFI)) { + uc->uc_mcontext.gregs [REG_PC] = intptr_t(FetchNResume) ; + uc->uc_mcontext.gregs [REG_nPC] = intptr_t(FetchNResume) + 4 ; + return true ; + } + + // Handle ALL stack overflow variations here + if (sig == SIGSEGV && info->si_code == SEGV_ACCERR) { + address addr = (address) info->si_addr; + if (thread->in_stack_yellow_zone(addr)) { + thread->disable_stack_yellow_zone(); + // Sometimes the register windows are not properly flushed. + if(uc->uc_mcontext.gwins != NULL) { + ::handle_unflushed_register_windows(uc->uc_mcontext.gwins); + } + if (thread->thread_state() == _thread_in_Java) { + // Throw a stack overflow exception. Guard pages will be reenabled + // while unwinding the stack. + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); + } else { + // Thread was in the vm or native code. Return and try to finish. + return true; + } + } else if (thread->in_stack_red_zone(addr)) { + // Fatal red zone violation. Disable the guard pages and fall through + // to handle_unexpected_exception way down below. + thread->disable_stack_red_zone(); + tty->print_raw_cr("An irrecoverable stack overflow has occurred."); + // Sometimes the register windows are not properly flushed. + if(uc->uc_mcontext.gwins != NULL) { + ::handle_unflushed_register_windows(uc->uc_mcontext.gwins); + } + } + } + + + if (thread->thread_state() == _thread_in_vm) { + if (sig == SIGBUS && info->si_code == BUS_OBJERR && thread->doing_unsafe_access()) { + stub = StubRoutines::handler_for_unsafe_access(); + } + } + + else if (thread->thread_state() == _thread_in_Java) { + // Java thread running in Java code => find exception handler if any + // a fault inside compiled code, the interpreter, or a stub + + // Support Safepoint Polling + if ( sig == SIGSEGV && (address)info->si_addr == os::get_polling_page() ) { + stub = SharedRuntime::get_poll_stub(pc); + } + + // Not needed on x86 solaris because verify_oops doesn't generate + // SEGV/BUS like sparc does. + if ( (sig == SIGSEGV || sig == SIGBUS) + && pc >= MacroAssembler::_verify_oop_implicit_branch[0] + && pc < MacroAssembler::_verify_oop_implicit_branch[1] ) { + stub = MacroAssembler::_verify_oop_implicit_branch[2]; + warning("fixed up memory fault in +VerifyOops at address " INTPTR_FORMAT, info->si_addr); + } + + // This is not factored because on x86 solaris the patching for + // zombies does not generate a SEGV. + else if (sig == SIGSEGV && nativeInstruction_at(pc)->is_zombie()) { + // zombie method (ld [%g0],%o7 instruction) + stub = SharedRuntime::get_handle_wrong_method_stub(); + + // At the stub it needs to look like a call from the caller of this + // method (not a call from the segv site). + pc = (address)uc->uc_mcontext.gregs[REG_O7]; + } + else if (sig == SIGBUS && info->si_code == BUS_OBJERR) { + // BugId 4454115: A read from a MappedByteBuffer can fault + // here if the underlying file has been truncated. + // Do not crash the VM in such a case. + CodeBlob* cb = CodeCache::find_blob_unsafe(pc); + nmethod* nm = cb->is_nmethod() ? (nmethod*)cb : NULL; + if (nm != NULL && nm->has_unsafe_access()) { + stub = StubRoutines::handler_for_unsafe_access(); + } + } + + else if (sig == SIGFPE && info->si_code == FPE_INTDIV) { + // integer divide by zero + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_DIVIDE_BY_ZERO); + } + else if (sig == SIGFPE && info->si_code == FPE_FLTDIV) { + // floating-point divide by zero + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_DIVIDE_BY_ZERO); + } +#ifdef COMPILER2 + else if (sig == SIGILL && nativeInstruction_at(pc)->is_ic_miss_trap()) { +#ifdef ASSERT + #ifdef TIERED + CodeBlob* cb = CodeCache::find_blob_unsafe(pc); + assert(cb->is_compiled_by_c2(), "Wrong compiler"); + #endif // TIERED +#endif // ASSERT + // Inline cache missed and user trap "Tne G0+ST_RESERVED_FOR_USER_0+2" taken. + stub = SharedRuntime::get_ic_miss_stub(); + // At the stub it needs to look like a call from the caller of this + // method (not a call from the segv site). + pc = (address)uc->uc_mcontext.gregs[REG_O7]; + } +#endif // COMPILER2 + + else if (sig == SIGSEGV && info->si_code > 0 && !MacroAssembler::needs_explicit_null_check((intptr_t)info->si_addr)) { + // Determination of interpreter/vtable stub/compiled code null exception + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); + } + } + + // jni_fast_GetField can trap at certain pc's if a GC kicks in + // and the heap gets shrunk before the field access. + if ((sig == SIGSEGV) || (sig == SIGBUS)) { + address addr = JNI_FastGetField::find_slowcase_pc(pc); + if (addr != (address)-1) { + stub = addr; + } + } + + // Check to see if we caught the safepoint code in the + // process of write protecting the memory serialization page. + // It write enables the page immediately after protecting it + // so just return. + if ((sig == SIGSEGV) && + os::is_memory_serialize_page(thread, (address)info->si_addr)) { + // Block current thread until the memory serialize page permission restored. + os::block_on_serialize_page_trap(); + return true; + } + } + + if (stub != NULL) { + // save all thread context in case we need to restore it + + thread->set_saved_exception_pc(pc); + thread->set_saved_exception_npc(npc); + + // simulate a branch to the stub (a "call" in the safepoint stub case) + // factor me: setPC + uc->uc_mcontext.gregs[REG_PC ] = (greg_t)stub; + uc->uc_mcontext.gregs[REG_nPC] = (greg_t)(stub + 4); + +#ifndef PRODUCT + if (TraceJumps) thread->record_jump(stub, NULL, __FILE__, __LINE__); +#endif /* PRODUCT */ + + return true; + } + + // signal-chaining + if (os::Solaris::chained_handler(sig, info, ucVoid)) { + return true; + } + + if (!abort_if_unrecognized) { + // caller wants another chance, so give it to him + return false; + } + + if (!os::Solaris::libjsig_is_loaded) { + struct sigaction oldAct; + sigaction(sig, (struct sigaction *)0, &oldAct); + if (oldAct.sa_sigaction != signalHandler) { + void* sighand = oldAct.sa_sigaction ? CAST_FROM_FN_PTR(void*, oldAct.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oldAct.sa_handler); + warning("Unexpected Signal %d occured under user-defined signal handler " INTPTR_FORMAT, sig, (intptr_t)sighand); + } + } + + if (pc == NULL && uc != NULL) { + pc = (address) uc->uc_mcontext.gregs[REG_PC]; + } + + // unmask current signal + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); + sigprocmask(SIG_UNBLOCK, &newset, NULL); + + VMError err(t, sig, pc, info, ucVoid); + err.report_and_die(); + + ShouldNotReachHere(); +} + +void os::print_context(outputStream *st, void *context) { + if (context == NULL) return; + + ucontext_t *uc = (ucontext_t*)context; + st->print_cr("Registers:"); + + st->print_cr(" O0=" INTPTR_FORMAT " O1=" INTPTR_FORMAT + " O2=" INTPTR_FORMAT " O3=" INTPTR_FORMAT, + uc->uc_mcontext.gregs[REG_O0], + uc->uc_mcontext.gregs[REG_O1], + uc->uc_mcontext.gregs[REG_O2], + uc->uc_mcontext.gregs[REG_O3]); + st->print_cr(" O4=" INTPTR_FORMAT " O5=" INTPTR_FORMAT + " O6=" INTPTR_FORMAT " O7=" INTPTR_FORMAT, + uc->uc_mcontext.gregs[REG_O4], + uc->uc_mcontext.gregs[REG_O5], + uc->uc_mcontext.gregs[REG_O6], + uc->uc_mcontext.gregs[REG_O7]); + + st->print_cr(" G1=" INTPTR_FORMAT " G2=" INTPTR_FORMAT + " G3=" INTPTR_FORMAT " G4=" INTPTR_FORMAT, + uc->uc_mcontext.gregs[REG_G1], + uc->uc_mcontext.gregs[REG_G2], + uc->uc_mcontext.gregs[REG_G3], + uc->uc_mcontext.gregs[REG_G4]); + st->print_cr(" G5=" INTPTR_FORMAT " G6=" INTPTR_FORMAT + " G7=" INTPTR_FORMAT " Y=" INTPTR_FORMAT, + uc->uc_mcontext.gregs[REG_G5], + uc->uc_mcontext.gregs[REG_G6], + uc->uc_mcontext.gregs[REG_G7], + uc->uc_mcontext.gregs[REG_Y]); + + st->print_cr(" PC=" INTPTR_FORMAT " nPC=" INTPTR_FORMAT, + uc->uc_mcontext.gregs[REG_PC], + uc->uc_mcontext.gregs[REG_nPC]); + st->cr(); + st->cr(); + + intptr_t *sp = (intptr_t *)os::Solaris::ucontext_get_sp(uc); + st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", sp); + print_hex_dump(st, (address)sp, (address)(sp + 32), sizeof(intptr_t)); + st->cr(); + + // Note: it may be unsafe to inspect memory near pc. For example, pc may + // point to garbage if entry point in an nmethod is corrupted. Leave + // this at the end, and hope for the best. + ExtendedPC epc = os::Solaris::ucontext_get_ExtendedPC(uc); + address pc = epc.pc(); + st->print_cr("Instructions: (pc=" PTR_FORMAT ")", pc); + print_hex_dump(st, pc - 16, pc + 16, sizeof(char)); +} + +void os::Solaris::init_thread_fpu_state(void) { + // Nothing needed on Sparc. +} + +#if !defined(COMPILER2) && !defined(_LP64) + +// These routines are the initial value of atomic_xchg_entry(), +// atomic_cmpxchg_entry(), atomic_add_entry() and fence_entry() +// until initialization is complete. +// TODO - remove when the VM drops support for V8. + +typedef jint xchg_func_t (jint, volatile jint*); +typedef jint cmpxchg_func_t (jint, volatile jint*, jint); +typedef jlong cmpxchg_long_func_t(jlong, volatile jlong*, jlong); +typedef jint add_func_t (jint, volatile jint*); +typedef void fence_func_t (); + +jint os::atomic_xchg_bootstrap(jint exchange_value, volatile jint* dest) { + // try to use the stub: + xchg_func_t* func = CAST_TO_FN_PTR(xchg_func_t*, StubRoutines::atomic_xchg_entry()); + + if (func != NULL) { + os::atomic_xchg_func = func; + return (*func)(exchange_value, dest); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + jint old_value = *dest; + *dest = exchange_value; + return old_value; +} + +jint os::atomic_cmpxchg_bootstrap(jint exchange_value, volatile jint* dest, jint compare_value) { + // try to use the stub: + cmpxchg_func_t* func = CAST_TO_FN_PTR(cmpxchg_func_t*, StubRoutines::atomic_cmpxchg_entry()); + + if (func != NULL) { + os::atomic_cmpxchg_func = func; + return (*func)(exchange_value, dest, compare_value); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + jint old_value = *dest; + if (old_value == compare_value) + *dest = exchange_value; + return old_value; +} + +jlong os::atomic_cmpxchg_long_bootstrap(jlong exchange_value, volatile jlong* dest, jlong compare_value) { + // try to use the stub: + cmpxchg_long_func_t* func = CAST_TO_FN_PTR(cmpxchg_long_func_t*, StubRoutines::atomic_cmpxchg_long_entry()); + + if (func != NULL) { + os::atomic_cmpxchg_long_func = func; + return (*func)(exchange_value, dest, compare_value); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + jlong old_value = *dest; + if (old_value == compare_value) + *dest = exchange_value; + return old_value; +} + +jint os::atomic_add_bootstrap(jint add_value, volatile jint* dest) { + // try to use the stub: + add_func_t* func = CAST_TO_FN_PTR(add_func_t*, StubRoutines::atomic_add_entry()); + + if (func != NULL) { + os::atomic_add_func = func; + return (*func)(add_value, dest); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + return (*dest) += add_value; +} + +void os::fence_bootstrap() { + // try to use the stub: + fence_func_t* func = CAST_TO_FN_PTR(fence_func_t*, StubRoutines::fence_entry()); + + if (func != NULL) { + os::fence_func = func; + (*func)(); + return; + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + // don't have to do anything for a single thread +} + +xchg_func_t* os::atomic_xchg_func = os::atomic_xchg_bootstrap; +cmpxchg_func_t* os::atomic_cmpxchg_func = os::atomic_cmpxchg_bootstrap; +cmpxchg_long_func_t* os::atomic_cmpxchg_long_func = os::atomic_cmpxchg_long_bootstrap; +add_func_t* os::atomic_add_func = os::atomic_add_bootstrap; +fence_func_t* os::fence_func = os::fence_bootstrap; + +#endif // !_LP64 && !COMPILER2 + +#if defined(__sparc) && defined(COMPILER2) && defined(_GNU_SOURCE) + // See file build/solaris/makefiles/$compiler.make + // For compiler1 the architecture is v8 and frps isn't present in v8 + extern "C" void _mark_fpu_nosave() { + __asm__ __volatile__ ("wr %%g0, 0, %%fprs \n\t" : : :); + } +#endif //defined(__sparc) && defined(COMPILER2) diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.hpp b/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.hpp new file mode 100644 index 00000000000..f522b038507 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.hpp @@ -0,0 +1,46 @@ +/* + * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // + // NOTE: we are back in class os here, not Solaris + // + static jint (*atomic_xchg_func) (jint, volatile jint*); + static jint (*atomic_cmpxchg_func) (jint, volatile jint*, jint); + static jlong (*atomic_cmpxchg_long_func)(jlong, volatile jlong*, jlong); + static jint (*atomic_add_func) (jint, volatile jint*); + static void (*fence_func) (); + + static jint atomic_xchg_bootstrap (jint, volatile jint*); + static jint atomic_cmpxchg_bootstrap (jint, volatile jint*, jint); + static jlong atomic_cmpxchg_long_bootstrap(jlong, volatile jlong*, jlong); + static jint atomic_add_bootstrap (jint, volatile jint*); + static void fence_bootstrap (); + + static void setup_fpu() {} + + static bool is_allocatable(size_t bytes); + + // Used to register dynamic code cache area with the OS + // Note: Currently only used in 64 bit Windows implementations + static bool register_code_area(char *low, char *high) { return true; } diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/prefetch_solaris_sparc.inline.hpp b/hotspot/src/os_cpu/solaris_sparc/vm/prefetch_solaris_sparc.inline.hpp new file mode 100644 index 00000000000..7c9adecb4b0 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/prefetch_solaris_sparc.inline.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#if defined(COMPILER2) || defined(_LP64) + +// For Sun Studio inplementation is in solaris_sparc.il +// For gcc inplementation is just below +extern "C" void _Prefetch_read (void *loc, intx interval); +extern "C" void _Prefetch_write(void *loc, intx interval); + +inline void Prefetch::read(void *loc, intx interval) { + _Prefetch_read(loc, interval); +} + +inline void Prefetch::write(void *loc, intx interval) { + _Prefetch_write(loc, interval); +} + +#ifdef _GNU_SOURCE +extern "C" { + inline void _Prefetch_read (void *loc, intx interval) { + __asm__ volatile + ("prefetch [%0+%1], 0" : : "r" (loc), "r" (interval) : "memory" ); + } + inline void _Prefetch_write(void *loc, intx interval) { + __asm__ volatile + ("prefetch [%0+%1], 2" : : "r" (loc), "r" (interval) : "memory" ); + } +} +#endif // _GNU_SOURCE + +#else // defined(COMPILER2) || defined(_LP64) + +inline void Prefetch::read (void *loc, intx interval) {} +inline void Prefetch::write(void *loc, intx interval) {} + +#endif // defined(COMPILER2) || defined(_LP64) diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.ad b/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.ad new file mode 100644 index 00000000000..f46de2d3139 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.ad @@ -0,0 +1,27 @@ +// +// Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// + +// +// + +// SPARC Solaris Architecture Description File diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.il b/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.il new file mode 100644 index 00000000000..39eb45c0f3d --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.il @@ -0,0 +1,297 @@ +// +// Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + + // Get the raw thread ID from %g7 + + .inline _raw_thread_id, 0 + .register %g7,#scratch + .volatile + mov %g7, %o0 + .nonvolatile + .end + + + // Clear SPARC fprs.FEF DU and DL bits -- + // allows the kernel to avoid saving FPU state at context-switch time. + // Use for state-transition points (into _thread_blocked) or when + // parking. + + .inline _mark_fpu_nosave, 0 + .volatile + wr %g0, 0, %fprs + .nonvolatile + .end + + // Support for jint Atomic::xchg(jint exchange_value, volatile jint* dest). + // + // Arguments: + // exchange_value: O0 + // dest: O1 + // + // Results: + // O0: the value previously stored in dest + + .inline _Atomic_swap32, 2 + .volatile + swap [%o1],%o0 + .nonvolatile + .end + + + // Support for intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t * dest). + // + // 64-bit + // + // Arguments: + // exchange_value: O0 + // dest: O1 + // + // Results: + // O0: the value previously stored in dest + + .inline _Atomic_swap64, 2 + .volatile + 1: + mov %o0, %o3 + ldx [%o1], %o2 + casx [%o1], %o2, %o3 + cmp %o2, %o3 + bne %xcc, 1b + nop + mov %o2, %o0 + .nonvolatile + .end + + + // Support for jint Atomic::cmpxchg(jint exchange_value, + // volatile jint* dest, + // jint compare_value) + // + // Arguments: + // exchange_value: O0 + // dest: O1 + // compare_value: O2 + // + // Results: + // O0: the value previously stored in dest + + .inline _Atomic_cas32, 3 + .volatile + cas [%o1], %o2, %o0 + .nonvolatile + .end + + + // Support for intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, + // volatile intptr_t* dest, + // intptr_t compare_value) + // + // 64-bit + // + // Arguments: + // exchange_value: O0 + // dest: O1 + // compare_value: O2 + // + // Results: + // O0: the value previously stored in dest + + .inline _Atomic_cas64, 3 + .volatile + casx [%o1], %o2, %o0 + .nonvolatile + .end + + + // Support for jlong Atomic::cmpxchg(jlong exchange_value, + // volatile jlong* dest, + // jlong compare_value) + // + // 32-bit calling conventions + // + // Arguments: + // exchange_value: O1:O0 + // dest: O2 + // compare_value: O4:O3 + // + // Results: + // O1:O0: the value previously stored in dest + + .inline _Atomic_casl, 3 + .volatile + sllx %o0, 32, %o0 + srl %o1, 0, %o1 + or %o0,%o1,%o0 + sllx %o3, 32, %o3 + srl %o4, 0, %o4 + or %o3,%o4,%o3 + casx [%o2], %o3, %o0 + srl %o0, 0, %o1 + srlx %o0, 32, %o0 + .nonvolatile + .end + + + // Support for jint Atomic::add(jint add_value, volatile jint* dest). + // + // Arguments: + // add_value: O0 (e.g., +1 or -1) + // dest: O1 + // + // Results: + // O0: the new value stored in dest + // + // Overwrites O3 + + .inline _Atomic_add32, 2 + .volatile + 2: + ld [%o1], %o2 + add %o0, %o2, %o3 + cas [%o1], %o2, %o3 + cmp %o2, %o3 + bne 2b + nop + add %o0, %o2, %o0 + .nonvolatile + .end + + + // Support for intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) + // + // 64-bit + // + // Arguments: + // add_value: O0 (e.g., +1 or -1) + // dest: O1 + // + // Results: + // O0: the new value stored in dest + // + // Overwrites O3 + + .inline _Atomic_add64, 2 + .volatile + 3: + ldx [%o1], %o2 + add %o0, %o2, %o3 + casx [%o1], %o2, %o3 + cmp %o2, %o3 + bne %xcc, 3b + nop + add %o0, %o2, %o0 + .nonvolatile + .end + + + // Support for void OrderAccess::acquire() + // The method is intentionally empty. + // It exists for the sole purpose of generating + // a C/C++ sequence point over which the compiler won't + // reorder code. + + .inline _OrderAccess_acquire,0 + .volatile + .nonvolatile + .end + + + // Support for void OrderAccess::fence() + + .inline _OrderAccess_fence,0 + .volatile + membar #StoreLoad + .nonvolatile + .end + + + // Support for void Prefetch::read(void *loc, intx interval) + // + // Prefetch for several reads. + + .inline _Prefetch_read, 2 + .volatile + prefetch [%o0+%o1], 0 + .nonvolatile + .end + + + // Support for void Prefetch::write(void *loc, intx interval) + // + // Prefetch for several writes. + + .inline _Prefetch_write, 2 + .volatile + prefetch [%o0+%o1], 2 + .nonvolatile + .end + + + // Support for void Copy::conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) + // + // 32-bit + // + // Arguments: + // from: O0 + // to: O1 + // count: O2 treated as signed + // + // Clobbers: + // long_value: O2, O3 + // count: O4 + // + // if (from > to) { + // while (--count >= 0) { + // *to++ = *from++; + // } + // } else { + // while (--count >= 0) { + // to[count] = from[count]; + // } + // } + .inline _Copy_conjoint_jlongs_atomic, 3 + .volatile + cmp %o0, %o1 + bleu 4f + sll %o2, 3, %o4 + ba 2f + 1: + subcc %o4, 8, %o4 + std %o2, [%o1] + add %o0, 8, %o0 + add %o1, 8, %o1 + 2: + bge,a 1b + ldd [%o0], %o2 + ba 5f + nop + 3: + std %o2, [%o1+%o4] + 4: + subcc %o4, 8, %o4 + bge,a 3b + ldd [%o0+%o4], %o2 + 5: + .nonvolatile + .end diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.s b/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.s new file mode 100644 index 00000000000..f243c3fb9e5 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.s @@ -0,0 +1,120 @@ +!! +!! Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. +!! DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +!! +!! This code is free software; you can redistribute it and/or modify it +!! under the terms of the GNU General Public License version 2 only, as +!! published by the Free Software Foundation. +!! +!! This code is distributed in the hope that it will be useful, but WITHOUT +!! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +!! FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +!! version 2 for more details (a copy is included in the LICENSE file that +!! accompanied this code). +!! +!! You should have received a copy of the GNU General Public License version +!! 2 along with this work; if not, write to the Free Software Foundation, +!! Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +!! +!! Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +!! CA 95054 USA or visit www.sun.com if you need additional information or +!! have any questions. +!! + + !! Prototype: int SafeFetch32 (int * adr, int ErrValue) + !! The "ld" at Fetch32 is potentially faulting instruction. + !! If the instruction traps the trap handler will arrange + !! for control to resume at Fetch32Resume. + !! By convention with the trap handler we ensure there is a non-CTI + !! instruction in the trap shadow. + !! + !! The reader might be tempted to move this service to .il. + !! Don't. Sun's CC back-end reads and optimize code emitted + !! by the .il "call", in some cases optimizing the code, completely eliding it, + !! or by moving the code from the "call site". + + + .globl SafeFetch32 + .align 32 + .global Fetch32PFI, Fetch32Resume +SafeFetch32: + mov %o0, %g1 + mov %o1, %o0 +Fetch32PFI: + ld [%g1], %o0 !! <-- Potentially faulting instruction +Fetch32Resume: + nop + retl + nop + + .globl SafeFetchN + .align 32 + .globl FetchNPFI, FetchNResume +SafeFetchN: + mov %o0, %g1 + mov %o1, %o0 +FetchNPFI: + ldn [%g1], %o0 +FetchNResume: + nop + retl + nop + + !! Possibilities: + !! -- membar + !! -- CAS (SP + BIAS, G0, G0) + !! -- wr %g0, %asi + + .global SpinPause + .align 32 +SpinPause: + retl + mov %g0, %o0 + + + + .globl _Copy_conjoint_jlongs_atomic + .align 32 + .global _Copy_conjoint_jlongs_atomic + _Copy_conjoint_jlongs_atomic: + cmp %o0, %o1 + bleu 4f + sll %o2, 3, %o4 + ba 2f + 1: + subcc %o4, 8, %o4 + std %o2, [%o1] + add %o0, 8, %o0 + add %o1, 8, %o1 + 2: + bge,a 1b + ldd [%o0], %o2 + ba 5f + nop + 3: + std %o2, [%o1+%o4] + 4: + subcc %o4, 8, %o4 + bge,a 3b + ldd [%o0+%o4], %o2 + 5: + retl + nop + + + + .globl _raw_thread_id + .align 32 + _raw_thread_id: + retl + mov %g7, %o0 + + + .globl _flush_reg_windows + .align 32 + _flush_reg_windows: + ta 0x03 + retl + mov %fp, %o0 + + diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/threadLS_solaris_sparc.cpp b/hotspot/src/os_cpu/solaris_sparc/vm/threadLS_solaris_sparc.cpp new file mode 100644 index 00000000000..fc79f1ba70c --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/threadLS_solaris_sparc.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Provides an entry point we can link against and +// a buffer we can emit code into. The buffer is +// filled by ThreadLocalStorage::generate_code_for_get_thread +// and called from ThreadLocalStorage::thread() + +#include "incls/_precompiled.incl" +#include "incls/_threadLS_solaris_sparc.cpp.incl" +#include + +// The portable TLS mechanism (get_thread_via_cache) is enough on SPARC. +// There is no need for hand-assembling a special function. +void ThreadLocalStorage::generate_code_for_get_thread() { +} + +void ThreadLocalStorage::set_thread_in_slot (Thread * self) {} + +extern "C" Thread* get_thread() { + return ThreadLocalStorage::thread(); +} diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/threadLS_solaris_sparc.hpp b/hotspot/src/os_cpu/solaris_sparc/vm/threadLS_solaris_sparc.hpp new file mode 100644 index 00000000000..4ce50873d42 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/threadLS_solaris_sparc.hpp @@ -0,0 +1,66 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +public: + // Java Thread - force inlining + static inline Thread* thread() ; + +private: + static Thread* _get_thread_cache[]; // index by [(raw_id>>9)^(raw_id>>20) % _pd_cache_size] + static Thread* get_thread_via_cache_slowly(uintptr_t raw_id, int index); + + NOT_PRODUCT(static int _tcacheHit;) + NOT_PRODUCT(static int _tcacheMiss;) + +public: + + // Print cache hit/miss statistics + static void print_statistics() PRODUCT_RETURN; + + enum Constants { + _pd_cache_size = 256*2 // projected typical # of threads * 2 + }; + + static void set_thread_in_slot (Thread *) ; + + static uintptr_t pd_raw_thread_id() { + return _raw_thread_id(); + } + + static int pd_cache_index(uintptr_t raw_id) { + // Hash function: From email from Dave: + // The hash function deserves an explanation. %g7 points to libthread's + // "thread" structure. On T1 the thread structure is allocated on the + // user's stack (yes, really!) so the ">>20" handles T1 where the JVM's + // stack size is usually >= 1Mb. The ">>9" is for T2 where Roger allocates + // globs of thread blocks contiguously. The "9" has to do with the + // expected size of the T2 thread structure. If these constants are wrong + // the worst thing that'll happen is that the hit rate for heavily threaded + // apps won't be as good as it could be. If you want to burn another + // shift+xor you could mix together _all of the %g7 bits to form the hash, + // but I think that's excessive. Making the change above changed the + // T$ miss rate on SpecJBB (on a 16X system) from about 3% to imperceptible. + uintptr_t ix = (int) (((raw_id >> 9) ^ (raw_id >> 20)) % _pd_cache_size); + return ix; + } diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/thread_solaris_sparc.cpp b/hotspot/src/os_cpu/solaris_sparc/vm/thread_solaris_sparc.cpp new file mode 100644 index 00000000000..9a4118bba05 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/thread_solaris_sparc.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_thread_solaris_sparc.cpp.incl" + +// For Forte Analyzer AsyncGetCallTrace profiling support - thread is +// currently interrupted by SIGPROF +// +// NOTE: On Solaris, register windows are flushed in the signal handler +// except for possibly the top frame. +// +bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, + void* ucontext, bool isInJava) { + + assert(Thread::current() == this, "caller must be current thread"); + assert(this->is_Java_thread(), "must be JavaThread"); + + JavaThread* jt = (JavaThread *)this; + + if (!isInJava) { + // make_walkable flushes register windows and grabs last_Java_pc + // which can not be done if the ucontext sp matches last_Java_sp + // stack walking utilities assume last_Java_pc set if marked flushed + jt->frame_anchor()->make_walkable(jt); + } + + // If we have a walkable last_Java_frame, then we should use it + // even if isInJava == true. It should be more reliable than + // ucontext info. + if (jt->has_last_Java_frame() && jt->frame_anchor()->walkable()) { +#if 0 + // This sanity check may not be needed with the new frame + // walking code. Remove it for now. + if (!jt->frame_anchor()->post_Java_state_is_pc() + && frame::next_younger_sp_or_null(last_Java_sp(), + jt->frame_anchor()->post_Java_sp()) == NULL) { + // the anchor contains an SP, but the frame is not walkable + // because post_Java_sp isn't valid relative to last_Java_sp + return false; + } +#endif + *fr_addr = jt->pd_last_frame(); + return true; + } + + ucontext_t* uc = (ucontext_t*) ucontext; + + // At this point, we don't have a walkable last_Java_frame, so + // we try to glean some information out of the ucontext. + intptr_t* ret_sp; + ExtendedPC addr = os::Solaris::fetch_frame_from_ucontext(this, uc, + &ret_sp, NULL /* ret_fp only used on Solaris X86 */); + if (addr.pc() == NULL || ret_sp == NULL) { + // ucontext wasn't useful + return false; + } + + // we were running Java code when SIGPROF came in + if (isInJava) { + // If we have a last_Java_sp, then the SIGPROF signal caught us + // right when we were transitioning from _thread_in_Java to a new + // JavaThreadState. We use last_Java_sp instead of the sp from + // the ucontext since it should be more reliable. + if (jt->has_last_Java_frame()) { + ret_sp = jt->last_Java_sp(); + } + // Implied else: we don't have a last_Java_sp so we use what we + // got from the ucontext. + + frame ret_frame(ret_sp, frame::unpatchable, addr.pc()); + if (!ret_frame.safe_for_sender(jt)) { + // nothing else to try if the frame isn't good + return false; + } + *fr_addr = ret_frame; + return true; + } + + // At this point, we know we weren't running Java code. We might + // have a last_Java_sp, but we don't have a walkable frame. + // However, we might still be able to construct something useful + // if the thread was running native code. + if (jt->has_last_Java_frame()) { + assert(!jt->frame_anchor()->walkable(), "case covered above"); + + if (jt->thread_state() == _thread_in_native) { + frame ret_frame(jt->last_Java_sp(), frame::unpatchable, addr.pc()); + if (!ret_frame.safe_for_sender(jt)) { + // nothing else to try if the frame isn't good + return false; + } + *fr_addr = ret_frame; + return true; + } + } + + // nothing else to try + return false; +} diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/thread_solaris_sparc.hpp b/hotspot/src/os_cpu/solaris_sparc/vm/thread_solaris_sparc.hpp new file mode 100644 index 00000000000..003d3152e18 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/thread_solaris_sparc.hpp @@ -0,0 +1,97 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +private: + + void pd_initialize() { + _anchor.clear(); + _base_of_stack_pointer = NULL; + } + + frame pd_last_frame() { + assert(has_last_Java_frame(), "must have last_Java_sp() when suspended"); + assert(_anchor.walkable(), "thread has not dumped its register windows yet"); + + assert(_anchor.last_Java_pc() != NULL, "Ack no pc!"); + return frame(last_Java_sp(), frame::unpatchable, _anchor.last_Java_pc()); + } + + // Sometimes the trap handler needs to record both PC and NPC. + // This is a SPARC-specific companion to Thread::set_saved_exception_pc. + address _saved_exception_npc; + + // In polling_page_safepoint_handler_blob(s) we have to tail call other + // blobs without blowing any registers. A tail call requires some + // register to jump with and we can't blow any registers, so it must + // be restored in the delay slot. 'restore' cannot be used as it + // will chop the heads off of 64-bit %o registers in the 32-bit + // build. Instead we reload the registers using G2_thread and this + // location. Must be 64bits in the 32-bit LION build. + jdouble _o_reg_temps[6]; + + // a stack pointer older than any java frame stack pointer. It is + // used to validate stack pointers in frame::next_younger_sp (it + // provides the upper bound in the range check). This is necessary + // on Solaris/SPARC since the ucontext passed to a signal handler is + // sometimes corrupt and we need a way to check the extracted sp. + intptr_t* _base_of_stack_pointer; + +public: + + static int o_reg_temps_offset_in_bytes() { return offset_of(JavaThread, _o_reg_temps); } + +#ifndef _LP64 + address o_reg_temps(int i) { return (address)&_o_reg_temps[i]; } +#endif + + static int saved_exception_npc_offset_in_bytes() { return offset_of(JavaThread,_saved_exception_npc); } + + address saved_exception_npc() { return _saved_exception_npc; } + void set_saved_exception_npc(address a) { _saved_exception_npc = a; } + + +public: + + intptr_t* base_of_stack_pointer() { return _base_of_stack_pointer; } + + void set_base_of_stack_pointer(intptr_t* base_sp) { + _base_of_stack_pointer = base_sp; + } + + void record_base_of_stack_pointer() { + intptr_t *sp = (intptr_t *)(((intptr_t)StubRoutines::Sparc::flush_callers_register_windows_func()())); + intptr_t *ysp; + while((ysp = (intptr_t*)sp[FP->sp_offset_in_saved_window()]) != NULL) { + sp = (intptr_t *)((intptr_t)ysp + STACK_BIAS); + } + _base_of_stack_pointer = sp; + } + + bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, + bool isInJava); + + // These routines are only used on cpu architectures that + // have separate register stacks (Itanium). + static bool register_stack_overflow() { return false; } + static void enable_register_stack_guard() {} + static void disable_register_stack_guard() {} diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/vmStructs_solaris_sparc.hpp b/hotspot/src/os_cpu/solaris_sparc/vm/vmStructs_solaris_sparc.hpp new file mode 100644 index 00000000000..4159509f862 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/vmStructs_solaris_sparc.hpp @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// These are the OS and CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + \ + nonstatic_field(JavaThread, _base_of_stack_pointer, intptr_t*) \ + nonstatic_field(OSThread, _thread_id, thread_t) \ + /* This must be the last entry, and must be present */ \ + last_entry() + + +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ + \ + /**********************/ \ + /* Solaris Thread IDs */ \ + /**********************/ \ + \ + declare_unsigned_integer_type(thread_t) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + + +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /************************/ \ + /* JavaThread constants */ \ + /************************/ \ + \ + declare_constant(JavaFrameAnchor::flushed) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp b/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp new file mode 100644 index 00000000000..5f2ec21027e --- /dev/null +++ b/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp @@ -0,0 +1,82 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vm_version_solaris_sparc.cpp.incl" + +# include + +int VM_Version::platform_features(int features) { + // We determine what sort of hardware we have via sysinfo(SI_ISALIST, ...). + // This isn't the best of all possible ways because there's not enough + // detail in the isa list it returns, but it's a bit less arcane than + // generating assembly code and an illegal instruction handler. We used + // to generate a getpsr trap, but that's even more arcane. + // + // Another possibility would be to use sysinfo(SI_PLATFORM, ...), but + // that would require more knowledge here than is wise. + + // isalist spec via 'man isalist' as of 01-Aug-2001 + + char tmp; + size_t bufsize = sysinfo(SI_ISALIST, &tmp, 1); + char* buf = (char*)malloc(bufsize); + + if (buf != NULL) { + if (sysinfo(SI_ISALIST, buf, bufsize) == bufsize) { + // Figure out what kind of sparc we have + char *sparc_string = strstr(buf, "sparc"); + if (sparc_string != NULL) { features |= v8_instructions_m; + if (sparc_string[5] == 'v') { + if (sparc_string[6] == '8') { + if (sparc_string[7] == '-') features |= hardware_int_muldiv_m; + else if (sparc_string[7] == 'p') features |= generic_v9_m; + else features |= generic_v8_m; + } else if (sparc_string[6] == '9') features |= generic_v9_m; + } + } + + // Check for visualization instructions + char *vis = strstr(buf, "vis"); + if (vis != NULL) { features |= vis1_instructions_m; + if (vis[3] == '2') features |= vis2_instructions_m; + } + } + free(buf); + } + + bufsize = sysinfo(SI_MACHINE, &tmp, 1); + buf = (char*)malloc(bufsize); + + if (buf != NULL) { + if (sysinfo(SI_MACHINE, buf, bufsize) == bufsize) { + if (strstr(buf, "sun4v") != NULL) { + features |= sun4v_m; + } + } + free(buf); + } + + return features; +} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_32.cpp b/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_32.cpp new file mode 100644 index 00000000000..2f877bfce47 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_32.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler_solaris_x86_32.cpp.incl" + + +void MacroAssembler::int3() { + pushl(rax); + pushl(rdx); + pushl(rcx); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); + popl(rcx); + popl(rdx); + popl(rax); +} + +void MacroAssembler::get_thread(Register thread) { + + // Try to emit a Solaris-specific fast TSD/TLS accessor. + ThreadLocalStorage::pd_tlsAccessMode tlsMode = ThreadLocalStorage::pd_getTlsAccessMode () ; + if (tlsMode == ThreadLocalStorage::pd_tlsAccessIndirect) { // T1 + // Use thread as a temporary: mov r, gs:[0]; mov r, [r+tlsOffset] + emit_byte (Assembler::GS_segment) ; + // ExternalAddress doesn't work because it can't take NULL + AddressLiteral null(0, relocInfo::none); + movptr (thread, null); + movl (thread, Address(thread, ThreadLocalStorage::pd_getTlsOffset())) ; + return ; + } else + if (tlsMode == ThreadLocalStorage::pd_tlsAccessDirect) { // T2 + // mov r, gs:[tlsOffset] + emit_byte (Assembler::GS_segment) ; + AddressLiteral tls((address)ThreadLocalStorage::pd_getTlsOffset(), relocInfo::none); + movptr (thread, tls); + return ; + } + + // slow call to of thr_getspecific + // int thr_getspecific(thread_key_t key, void **value); + // Consider using pthread_getspecific instead. + + pushl(0); // allocate space for return value + if (thread != rax) pushl(rax); // save rax, if caller still wants it + pushl(rcx); // save caller save + pushl(rdx); // save caller save + if (thread != rax) { + leal(thread, Address(rsp, 3 * sizeof(int))); // address of return value + } else { + leal(thread, Address(rsp, 2 * sizeof(int))); // address of return value + } + pushl(thread); // and pass the address + pushl(ThreadLocalStorage::thread_index()); // the key + call(RuntimeAddress(CAST_FROM_FN_PTR(address, thr_getspecific))); + increment(rsp, 2 * wordSize); + popl(rdx); + popl(rcx); + if (thread != rax) popl(rax); + popl(thread); +} + +bool MacroAssembler::needs_explicit_null_check(int offset) { + // Identical to Sparc/Solaris code + bool offset_in_first_page = 0 <= offset && offset < os::vm_page_size(); + return !offset_in_first_page; +} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_64.cpp b/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_64.cpp new file mode 100644 index 00000000000..b6c210f93b8 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/assembler_solaris_x86_64.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler_solaris_x86_64.cpp.incl" + +void MacroAssembler::int3() { + call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); +} + +void MacroAssembler::get_thread(Register thread) { + // Try to emit a Solaris-specific fast TSD/TLS accessor. + ThreadLocalStorage::pd_tlsAccessMode tlsMode = ThreadLocalStorage::pd_getTlsAccessMode(); + if (tlsMode == ThreadLocalStorage::pd_tlsAccessIndirect) { // T1 + // Use thread as a temporary: mov r, fs:[0]; mov r, [r+tlsOffset] + emit_byte(Assembler::FS_segment); + movq(thread, Address(NULL, relocInfo::none)); + movq(thread, Address(thread, ThreadLocalStorage::pd_getTlsOffset())); + return; + } else if (tlsMode == ThreadLocalStorage::pd_tlsAccessDirect) { // T2 + // mov r, fs:[tlsOffset] + emit_byte(Assembler::FS_segment); + ExternalAddress tls_off((address) ThreadLocalStorage::pd_getTlsOffset()); + movptr(thread, tls_off); + return; + } + + // slow call to of thr_getspecific + // int thr_getspecific(thread_key_t key, void **value); + // Consider using pthread_getspecific instead. + + if (thread != rax) { + pushq(rax); + } + pushq(0); // space for return value + pushq(rdi); + pushq(rsi); + leaq(rsi, Address(rsp, 16)); // pass return value address + pushq(rdx); + pushq(rcx); + pushq(r8); + pushq(r9); + pushq(r10); + // XXX + movq(r10, rsp); + andq(rsp, -16); + pushq(r10); + pushq(r11); + + movl(rdi, ThreadLocalStorage::thread_index()); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, thr_getspecific))); + + popq(r11); + popq(rsp); + popq(r10); + popq(r9); + popq(r8); + popq(rcx); + popq(rdx); + popq(rsi); + popq(rdi); + popq(thread); // load return value + if (thread != rax) { + popq(rax); + } +} + +bool MacroAssembler::needs_explicit_null_check(int offset) { + // Identical to Sparc/Solaris code + bool offset_in_first_page = 0 <= offset && offset < os::vm_page_size(); + return !offset_in_first_page; +} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/atomic_solaris_x86.inline.hpp b/hotspot/src/os_cpu/solaris_x86/vm/atomic_solaris_x86.inline.hpp new file mode 100644 index 00000000000..469e0180062 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/atomic_solaris_x86.inline.hpp @@ -0,0 +1,230 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, jint* dest) { *dest = store_value; } + + +inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, void* dest) { *(void**)dest = store_value; } + +inline void Atomic::store (jbyte store_value, volatile jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, volatile jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, volatile jint* dest) { *dest = store_value; } +inline void Atomic::store_ptr(intptr_t store_value, volatile intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, volatile void* dest) { *(void* volatile *)dest = store_value; } + +inline void Atomic::inc (volatile jint* dest) { (void)add (1, dest); } +inline void Atomic::inc_ptr(volatile intptr_t* dest) { (void)add_ptr(1, dest); } +inline void Atomic::inc_ptr(volatile void* dest) { (void)add_ptr(1, dest); } + +inline void Atomic::dec (volatile jint* dest) { (void)add (-1, dest); } +inline void Atomic::dec_ptr(volatile intptr_t* dest) { (void)add_ptr(-1, dest); } +inline void Atomic::dec_ptr(volatile void* dest) { (void)add_ptr(-1, dest); } + +// For Sun Studio - implementation is in solaris_x86_[32/64].il. +// For gcc - implementation is just below. + +extern "C" jint _Atomic_add(jint add_value, volatile jint* dest, int mp); +extern "C" jint _Atomic_xchg(jint exchange_value, volatile jint* dest); +extern "C" jint _Atomic_cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value, int mp); +extern "C" jlong _Atomic_cmpxchg_long(jlong exchange_value, volatile jlong* dest, jlong compare_value, int mp); + +inline jint Atomic::add (jint add_value, volatile jint* dest) { + return _Atomic_add(add_value, dest, (int) os::is_MP()); +} + +inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { + return _Atomic_cmpxchg(exchange_value, dest, compare_value, (int) os::is_MP()); +} + +inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) { + return _Atomic_cmpxchg_long(exchange_value, dest, compare_value, (int) os::is_MP()); +} + + +#ifdef AMD64 +inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } +inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } +extern "C" jlong _Atomic_add_long(jlong add_value, volatile jlong* dest, int mp); +extern "C" jlong _Atomic_xchg_long(jlong exchange_value, volatile jlong* dest); + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { + return (intptr_t)_Atomic_add_long((jlong)add_value, (volatile jlong*)dest, (int) os::is_MP()); +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)_Atomic_add_long((jlong)add_value, (volatile jlong*)dest, (int) os::is_MP()); +} + +inline jint Atomic::xchg (jint exchange_value, volatile jint* dest) { + return _Atomic_xchg(exchange_value, dest); +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { + return (intptr_t)_Atomic_xchg_long((jlong)exchange_value, (volatile jlong*)dest); +} + +inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { + return (void*)_Atomic_xchg_long((jlong)exchange_value, (volatile jlong*)dest); +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { + return (intptr_t)_Atomic_cmpxchg_long((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, (int) os::is_MP()); +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)_Atomic_cmpxchg_long((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, (int) os::is_MP()); +} + +#else // !AMD64 + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { + return (intptr_t)add((jint)add_value, (volatile jint*)dest); +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)add((jint)add_value, (volatile jint*)dest); +} + +inline jint Atomic::xchg (jint exchange_value, volatile jint* dest) { + // We noticed a CC5.5 bug (4894807), so keep calling the stub just to be safe. + // Will use the inline template version after 4894807 is fixed. + // return _Atomic_xchg(exchange_value, dest); + return (*os::atomic_xchg_func)(exchange_value, dest); +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { + return (intptr_t)xchg((jint)exchange_value, (volatile jint*)dest); +} + +inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { + return (void*)xchg((jint)exchange_value, (volatile jint*)dest); +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { + return (intptr_t)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value); +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value); +} +#endif // AMD64 + +#ifdef _GNU_SOURCE +// Add a lock prefix to an instruction on an MP machine +#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: " + +extern "C" { + inline jint _Atomic_add(jint add_value, volatile jint* dest, int mp) { + jint addend = add_value; + __asm__ volatile ( LOCK_IF_MP(%3) "xaddl %0,(%2)" + : "=r" (addend) + : "0" (addend), "r" (dest), "r" (mp) + : "cc", "memory"); + return addend + add_value; + } + +#ifdef AMD64 + inline jlong _Atomic_add_long(jlong add_value, volatile jlong* dest, int mp) { + intptr_t addend = add_value; + __asm__ __volatile__ (LOCK_IF_MP(%3) "xaddq %0,(%2)" + : "=r" (addend) + : "0" (addend), "r" (dest), "r" (mp) + : "cc", "memory"); + return addend + add_value; + } + + inline jlong _Atomic_xchg_long(jlong exchange_value, volatile jlong* dest) { + __asm__ __volatile__ ("xchgq (%2),%0" + : "=r" (exchange_value) + : "0" (exchange_value), "r" (dest) + : "memory"); + return exchange_value; + } + +#endif // AMD64 + + inline jint _Atomic_xchg(jint exchange_value, volatile jint* dest) { + + // 32bit version originally did nothing!! + + __asm__ __volatile__ ("xchgl (%2),%0" + : "=r" (exchange_value) + : "0" (exchange_value), "r" (dest) + : "memory"); + return exchange_value; + } + + inline jint _Atomic_cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value, int mp) { + __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)" + : "=a" (exchange_value) + : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp) + : "cc", "memory"); + return exchange_value; + } + + // This is the interface to the atomic instruction in solaris_i486.s. + jlong _Atomic_cmpxchg_long_gcc(jlong exchange_value, volatile jlong* dest, jlong compare_value, int mp); + + inline jlong _Atomic_cmpxchg_long(jlong exchange_value, volatile jlong* dest, jlong compare_value, int mp) { +#ifdef AMD64 + __asm__ __volatile__ (LOCK_IF_MP(%4) "cmpxchgq %1,(%3)" + : "=a" (exchange_value) + : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp) + : "cc", "memory"); + return exchange_value; +#else + return _Atomic_cmpxchg_long_gcc(exchange_value, dest, compare_value, os::is_MP()); + + #if 0 + // The code below does not work presumably because of the bug in gcc + // The error message says: + // can't find a register in class BREG while reloading asm + // However I want to save this code and later replace _Atomic_cmpxchg_long_gcc + // with such inline asm code: + + volatile jlong_accessor evl, cvl, rv; + evl.long_value = exchange_value; + cvl.long_value = compare_value; + int mp = os::is_MP(); + + __asm__ volatile ("cmp $0, %%esi\n\t" + "je 1f \n\t" + "lock\n\t" + "1: cmpxchg8b (%%edi)\n\t" + : "=a"(cvl.words[0]), "=d"(cvl.words[1]) + : "a"(cvl.words[0]), "d"(cvl.words[1]), + "b"(evl.words[0]), "c"(evl.words[1]), + "D"(dest), "S"(mp) + : "cc", "memory"); + return cvl.long_value; + #endif // if 0 +#endif // AMD64 + } +} +#undef LOCK_IF_MP + +#endif // _GNU_SOURCE diff --git a/hotspot/src/os_cpu/solaris_x86/vm/bytes_solaris_x86.inline.hpp b/hotspot/src/os_cpu/solaris_x86/vm/bytes_solaris_x86.inline.hpp new file mode 100644 index 00000000000..919570279da --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/bytes_solaris_x86.inline.hpp @@ -0,0 +1,111 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// For Sun Studio - implementation is in solaris_i486.il. +// For gcc - implementation is just below. +extern "C" u2 _raw_swap_u2(u2 x); +extern "C" u4 _raw_swap_u4(u4 x); +#ifdef AMD64 +extern "C" u8 _raw_swap_u8(u8 x); +#else +extern "C" u8 _raw_swap_u8(u4 x, u4 y); +#endif // AMD64 + +// Efficient swapping of data bytes from Java byte +// ordering to native byte ordering and vice versa. +inline u2 Bytes::swap_u2(u2 x) { + return _raw_swap_u2(x); +} + +inline u4 Bytes::swap_u4(u4 x) { + return _raw_swap_u4(x); +} + +inline u8 Bytes::swap_u8(u8 x) { +#ifdef AMD64 + return _raw_swap_u8(x); +#else + return swap_u8_base(*(u4*)&x, *(((u4*)&x)+1)); +#endif // AMD64 + +} + +#ifndef AMD64 +// Helper function for swap_u8 +inline u8 Bytes::swap_u8_base(u4 x, u4 y) { + return _raw_swap_u8(x, y); +} +#endif // !AMD64 + + +#ifdef _GNU_SOURCE + +extern "C" { +#ifdef AMD64 + inline u2 _raw_swap_u2(u2 x) { + register unsigned short int __dest; + __asm__ ("rorw $8, %w0": "=r" (__dest): "0" (x): "cc"); + return __dest; + } + inline u4 _raw_swap_u4(u4 x) { + register unsigned int __dest; + __asm__ ("bswap %0" : "=r" (__dest) : "0" (x)); + return __dest; + } + inline u8 _raw_swap_u8(u8 x) { + register unsigned long __dest; + __asm__ ("bswap %q0" : "=r" (__dest) : "0" (x)); + return __dest; + } +#else + inline u2 _raw_swap_u2(u2 x) { + u2 ret; + __asm__ __volatile__ ( + "movw %0, %%ax;" + "xchg %%al, %%ah;" + "movw %%ax, %0" + :"=r" (ret) // output : register 0 => ret + :"0" (x) // input : x => register 0 + :"ax", "0" // clobbered registers + ); + return ret; + } + + inline u4 _raw_swap_u4(u4 x) { + u4 ret; + __asm__ __volatile__ ( + "bswap %0" + :"=r" (ret) // output : register 0 => ret + :"0" (x) // input : x => register 0 + :"0" // clobbered register + ); + return ret; + } + + inline u8 _raw_swap_u8(u4 x, u4 y) { + return (((u8)_raw_swap_u4(x))<<32) | _raw_swap_u4(y); + } +#endif // AMD64 +} +#endif //_GNU_SOURCE diff --git a/hotspot/src/os_cpu/solaris_x86/vm/copy_solaris_x86.inline.hpp b/hotspot/src/os_cpu/solaris_x86/vm/copy_solaris_x86.inline.hpp new file mode 100644 index 00000000000..97c31e1fa0e --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/copy_solaris_x86.inline.hpp @@ -0,0 +1,138 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +static void pd_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + (void)memmove(to, from, count * HeapWordSize); +} + +static void pd_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { +#ifndef AMD64 + (void)memcpy(to, from, count * HeapWordSize); +#else + switch (count) { + case 8: to[7] = from[7]; + case 7: to[6] = from[6]; + case 6: to[5] = from[5]; + case 5: to[4] = from[4]; + case 4: to[3] = from[3]; + case 3: to[2] = from[2]; + case 2: to[1] = from[1]; + case 1: to[0] = from[0]; + case 0: break; + default: + (void)memcpy(to, from, count * HeapWordSize); + break; + } +#endif // AMD64 +} + +static void pd_disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count) { + switch (count) { + case 8: to[7] = from[7]; + case 7: to[6] = from[6]; + case 6: to[5] = from[5]; + case 5: to[4] = from[4]; + case 4: to[3] = from[3]; + case 3: to[2] = from[2]; + case 2: to[1] = from[1]; + case 1: to[0] = from[0]; + case 0: break; + default: while (count-- > 0) { + *to++ = *from++; + } + break; + } +} + +static void pd_aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + (void)memmove(to, from, count * HeapWordSize); +} + +static void pd_aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { + pd_disjoint_words(from, to, count); +} + +static void pd_conjoint_bytes(void* from, void* to, size_t count) { +#ifdef AMD64 + (void)memmove(to, from, count); +#else + _Copy_conjoint_bytes(from, to, count); +#endif // AMD64 +} + +static void pd_conjoint_bytes_atomic(void* from, void* to, size_t count) { + pd_conjoint_bytes(from, to, count); +} + +static void pd_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { + _Copy_conjoint_jshorts_atomic(from, to, count); +} + +static void pd_conjoint_jints_atomic(jint* from, jint* to, size_t count) { + _Copy_conjoint_jints_atomic(from, to, count); +} + +static void pd_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { + // Guarantee use of fild/fistp or xmm regs via some asm code, because compilers won't. + _Copy_conjoint_jlongs_atomic(from, to, count); +} + +static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) { +#ifdef AMD64 + assert(BytesPerLong == BytesPerOop, "jlongs and oops must be the same size"); + _Copy_conjoint_jlongs_atomic((jlong*)from, (jlong*)to, count); +#else + _Copy_conjoint_jints_atomic((jint*)from, (jint*)to, count); +#endif // AMD64 +} + +static void pd_arrayof_conjoint_bytes(HeapWord* from, HeapWord* to, size_t count) { + _Copy_arrayof_conjoint_bytes(from, to, count); +} + +static void pd_arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count) { + _Copy_arrayof_conjoint_jshorts(from, to, count); +} + +static void pd_arrayof_conjoint_jints(HeapWord* from, HeapWord* to, size_t count) { + _Copy_arrayof_conjoint_jints(from, to, count); +} + +static void pd_arrayof_conjoint_jlongs(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + _Copy_arrayof_conjoint_jlongs(from, to, count); +#else + pd_conjoint_jlongs_atomic((jlong*)from, (jlong*)to, count); +#endif // AMD64 +} + +static void pd_arrayof_conjoint_oops(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + assert(BytesPerLong == BytesPerOop, "jlongs and oops must be the same size"); + _Copy_arrayof_conjoint_jlongs(from, to, count); +#else + assert(BytesPerInt == BytesPerOop, "jints and oops must be the same size"); + _Copy_arrayof_conjoint_jints(from, to, count); +#endif // AMD64 +} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/globals_solaris_x86.hpp b/hotspot/src/os_cpu/solaris_x86/vm/globals_solaris_x86.hpp new file mode 100644 index 00000000000..aef1d7314d3 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/globals_solaris_x86.hpp @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for platform dependent flags used by the runtime system. +// (see globals.hpp) +// +define_pd_global(bool, DontYieldALot, true); // Determined in the design center +#ifdef AMD64 +define_pd_global(intx, ThreadStackSize, 1024); // 0 => use system default +define_pd_global(intx, VMThreadStackSize, 1024); +define_pd_global(intx, SurvivorRatio, 6); +define_pd_global(uintx, JVMInvokeMethodSlack, 8*K); +#else +// UseStackBanging is not pd +// define_pd_global(bool, UseStackBanging, true); + +// ThreadStackSize 320 allows TaggedStackInterpreter and a couple of test cases +// to run while keeping the number of threads that can be created high. +define_pd_global(intx, ThreadStackSize, 320); +define_pd_global(intx, VMThreadStackSize, 256); +define_pd_global(intx, SurvivorRatio, 8); +define_pd_global(uintx, JVMInvokeMethodSlack, 10*K); +#endif // AMD64 + +define_pd_global(intx, CompilerThreadStackSize, 0); + +// Only used on 64 bit Windows platforms +define_pd_global(bool, UseVectoredExceptions, false); diff --git a/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp b/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp new file mode 100644 index 00000000000..ed8486f746f --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp @@ -0,0 +1,130 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Implementation of class OrderAccess. + +// For Sun Studio - implementation is in solaris_i486.il. +// For gcc - implementation is just below. +extern "C" void _OrderAccess_acquire(); +extern "C" void _OrderAccess_fence(); + +inline void OrderAccess::loadload() { acquire(); } +inline void OrderAccess::storestore() { release(); } +inline void OrderAccess::loadstore() { acquire(); } +inline void OrderAccess::storeload() { fence(); } + +inline void OrderAccess::acquire() { + _OrderAccess_acquire(); + +} + +inline void OrderAccess::release() { + dummy = 0; +} + +inline void OrderAccess::fence() { + if (os::is_MP()) { + _OrderAccess_fence(); + } +} + +#ifdef _GNU_SOURCE + +extern "C" { + inline void _OrderAccess_acquire() { + volatile intptr_t dummy; +#ifdef AMD64 + __asm__ volatile ("movq 0(%%rsp), %0" : "=r" (dummy) : : "memory"); +#else + __asm__ volatile ("movl 0(%%esp),%0" : "=r" (dummy) : : "memory"); +#endif // AMD64 + } + inline void _OrderAccess_fence() { +#ifdef AMD64 + __asm__ __volatile__ ("mfence":::"memory"); +#else + __asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory"); +#endif // AMD64 + } + +} + +#endif // GNU_SOURCE + +inline jbyte OrderAccess::load_acquire(volatile jbyte* p) { return *p; } +inline jshort OrderAccess::load_acquire(volatile jshort* p) { return *p; } +inline jint OrderAccess::load_acquire(volatile jint* p) { return *p; } +inline jlong OrderAccess::load_acquire(volatile jlong* p) { return *p; } +inline jubyte OrderAccess::load_acquire(volatile jubyte* p) { return *p; } +inline jushort OrderAccess::load_acquire(volatile jushort* p) { return *p; } +inline juint OrderAccess::load_acquire(volatile juint* p) { return *p; } +inline julong OrderAccess::load_acquire(volatile julong* p) { return *p; } +inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { return *p; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return *p; } + +inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return *p; } +inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return *(void* volatile *)p; } +inline void* OrderAccess::load_ptr_acquire(const volatile void* p) { return *(void* const volatile *)p; } + +inline void OrderAccess::release_store(volatile jbyte* p, jbyte v) { *p = v; } +inline void OrderAccess::release_store(volatile jshort* p, jshort v) { *p = v; } +inline void OrderAccess::release_store(volatile jint* p, jint v) { *p = v; } +inline void OrderAccess::release_store(volatile jlong* p, jlong v) { *p = v; } +inline void OrderAccess::release_store(volatile jubyte* p, jubyte v) { *p = v; } +inline void OrderAccess::release_store(volatile jushort* p, jushort v) { *p = v; } +inline void OrderAccess::release_store(volatile juint* p, juint v) { *p = v; } +inline void OrderAccess::release_store(volatile julong* p, julong v) { *p = v; } +inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { *p = v; } + +inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { *p = v; } +inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { *(void* volatile *)p = v; } + +inline void OrderAccess::store_fence(jbyte* p, jbyte v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jshort* p, jshort v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jint* p, jint v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jlong* p, jlong v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jubyte* p, jubyte v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jushort* p, jushort v) { *p = v; fence(); } +inline void OrderAccess::store_fence(juint* p, juint v) { *p = v; fence(); } +inline void OrderAccess::store_fence(julong* p, julong v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jfloat* p, jfloat v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jdouble* p, jdouble v) { *p = v; fence(); } + +inline void OrderAccess::store_ptr_fence(intptr_t* p, intptr_t v) { *p = v; fence(); } +inline void OrderAccess::store_ptr_fence(void** p, void* v) { *p = v; fence(); } + +inline void OrderAccess::release_store_fence(volatile jbyte* p, jbyte v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jshort* p, jshort v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jint* p, jint v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jlong* p, jlong v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jubyte* p, jubyte v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { *p = v; fence(); } + +inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { *p = v; fence(); } +inline void OrderAccess::release_store_ptr_fence(volatile void* p, void* v) { *(void* volatile *)p = v; fence(); } diff --git a/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp b/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp new file mode 100644 index 00000000000..0fcf224df44 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp @@ -0,0 +1,878 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// do not include precompiled header file +# include "incls/_os_solaris_x86.cpp.incl" + +// put OS-includes here +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include // see comment in + +#ifndef AMD64 +// QQQ seems useless at this point +# define _STRUCTURED_PROC 1 // this gets us the new structured proc interfaces of 5.6 & later +#endif // AMD64 +# include // see comment in + + +#define MAX_PATH (2 * K) + +// Minimum stack size for the VM. It's easier to document a constant value +// but it's different for x86 and sparc because the page sizes are different. +#ifdef AMD64 +size_t os::Solaris::min_stack_allowed = 224*K; +#define REG_SP REG_RSP +#define REG_PC REG_RIP +#define REG_FP REG_RBP +#else +size_t os::Solaris::min_stack_allowed = 64*K; +#define REG_SP UESP +#define REG_PC EIP +#define REG_FP EBP +// 4900493 counter to prevent runaway LDTR refresh attempt + +static volatile int ldtr_refresh = 0; +// the libthread instruction that faults because of the stale LDTR + +static const unsigned char movlfs[] = { 0x8e, 0xe0 // movl %eax,%fs + }; +#endif // AMD64 + +char* os::non_memory_address_word() { + // Must never look like an address returned by reserve_memory, + // even in its subfields (as defined by the CPU immediate fields, + // if the CPU splits constants across multiple instructions). + return (char*) -1; +} + +// +// Validate a ucontext retrieved from walking a uc_link of a ucontext. +// There are issues with libthread giving out uc_links for different threads +// on the same uc_link chain and bad or circular links. +// +bool os::Solaris::valid_ucontext(Thread* thread, ucontext_t* valid, ucontext_t* suspect) { + if (valid >= suspect || + valid->uc_stack.ss_flags != suspect->uc_stack.ss_flags || + valid->uc_stack.ss_sp != suspect->uc_stack.ss_sp || + valid->uc_stack.ss_size != suspect->uc_stack.ss_size) { + DEBUG_ONLY(tty->print_cr("valid_ucontext: failed test 1");) + return false; + } + + if (thread->is_Java_thread()) { + if (!valid_stack_address(thread, (address)suspect)) { + DEBUG_ONLY(tty->print_cr("valid_ucontext: uc_link not in thread stack");) + return false; + } + if (!valid_stack_address(thread, (address) suspect->uc_mcontext.gregs[REG_SP])) { + DEBUG_ONLY(tty->print_cr("valid_ucontext: stackpointer not in thread stack");) + return false; + } + } + return true; +} + +// We will only follow one level of uc_link since there are libthread +// issues with ucontext linking and it is better to be safe and just +// let caller retry later. +ucontext_t* os::Solaris::get_valid_uc_in_signal_handler(Thread *thread, + ucontext_t *uc) { + + ucontext_t *retuc = NULL; + + if (uc != NULL) { + if (uc->uc_link == NULL) { + // cannot validate without uc_link so accept current ucontext + retuc = uc; + } else if (os::Solaris::valid_ucontext(thread, uc, uc->uc_link)) { + // first ucontext is valid so try the next one + uc = uc->uc_link; + if (uc->uc_link == NULL) { + // cannot validate without uc_link so accept current ucontext + retuc = uc; + } else if (os::Solaris::valid_ucontext(thread, uc, uc->uc_link)) { + // the ucontext one level down is also valid so return it + retuc = uc; + } + } + } + return retuc; +} + +// Assumes ucontext is valid +ExtendedPC os::Solaris::ucontext_get_ExtendedPC(ucontext_t *uc) { + return ExtendedPC((address)uc->uc_mcontext.gregs[REG_PC]); +} + +// Assumes ucontext is valid +intptr_t* os::Solaris::ucontext_get_sp(ucontext_t *uc) { + return (intptr_t*)uc->uc_mcontext.gregs[REG_SP]; +} + +// Assumes ucontext is valid +intptr_t* os::Solaris::ucontext_get_fp(ucontext_t *uc) { + return (intptr_t*)uc->uc_mcontext.gregs[REG_FP]; +} + +// For Forte Analyzer AsyncGetCallTrace profiling support - thread +// is currently interrupted by SIGPROF. +// +// The difference between this and os::fetch_frame_from_context() is that +// here we try to skip nested signal frames. +ExtendedPC os::Solaris::fetch_frame_from_ucontext(Thread* thread, + ucontext_t* uc, intptr_t** ret_sp, intptr_t** ret_fp) { + + assert(thread != NULL, "just checking"); + assert(ret_sp != NULL, "just checking"); + assert(ret_fp != NULL, "just checking"); + + ucontext_t *luc = os::Solaris::get_valid_uc_in_signal_handler(thread, uc); + return os::fetch_frame_from_context(luc, ret_sp, ret_fp); +} + +ExtendedPC os::fetch_frame_from_context(void* ucVoid, + intptr_t** ret_sp, intptr_t** ret_fp) { + + ExtendedPC epc; + ucontext_t *uc = (ucontext_t*)ucVoid; + + if (uc != NULL) { + epc = os::Solaris::ucontext_get_ExtendedPC(uc); + if (ret_sp) *ret_sp = os::Solaris::ucontext_get_sp(uc); + if (ret_fp) *ret_fp = os::Solaris::ucontext_get_fp(uc); + } else { + // construct empty ExtendedPC for return value checking + epc = ExtendedPC(NULL); + if (ret_sp) *ret_sp = (intptr_t *)NULL; + if (ret_fp) *ret_fp = (intptr_t *)NULL; + } + + return epc; +} + +frame os::fetch_frame_from_context(void* ucVoid) { + intptr_t* sp; + intptr_t* fp; + ExtendedPC epc = fetch_frame_from_context(ucVoid, &sp, &fp); + return frame(sp, fp, epc.pc()); +} + +frame os::get_sender_for_C_frame(frame* fr) { + return frame(fr->sender_sp(), fr->link(), fr->sender_pc()); +} + +extern "C" intptr_t *_get_previous_fp(); // in .il file. + +frame os::current_frame() { + intptr_t* fp = _get_previous_fp(); + frame myframe((intptr_t*)os::current_stack_pointer(), + (intptr_t*)fp, + CAST_FROM_FN_PTR(address, os::current_frame)); + if (os::is_first_C_frame(&myframe)) { + // stack is not walkable + return frame(NULL, NULL, NULL); + } else { + return os::get_sender_for_C_frame(&myframe); + } +} + +// This is a simple callback that just fetches a PC for an interrupted thread. +// The thread need not be suspended and the fetched PC is just a hint. +// This one is currently used for profiling the VMThread ONLY! + +// Must be synchronous +void GetThreadPC_Callback::execute(OSThread::InterruptArguments *args) { + Thread* thread = args->thread(); + ucontext_t* uc = args->ucontext(); + intptr_t* sp; + + assert(ProfileVM && thread->is_VM_thread(), "just checking"); + + ExtendedPC new_addr((address)uc->uc_mcontext.gregs[REG_PC]); + _addr = new_addr; +} + +static int threadgetstate(thread_t tid, int *flags, lwpid_t *lwp, stack_t *ss, gregset_t rs, lwpstatus_t *lwpstatus) { + char lwpstatusfile[PROCFILE_LENGTH]; + int lwpfd, err; + + if (err = os::Solaris::thr_getstate(tid, flags, lwp, ss, rs)) + return (err); + if (*flags == TRS_LWPID) { + sprintf(lwpstatusfile, "/proc/%d/lwp/%d/lwpstatus", getpid(), + *lwp); + if ((lwpfd = open(lwpstatusfile, O_RDONLY)) < 0) { + perror("thr_mutator_status: open lwpstatus"); + return (EINVAL); + } + if (pread(lwpfd, lwpstatus, sizeof (lwpstatus_t), (off_t)0) != + sizeof (lwpstatus_t)) { + perror("thr_mutator_status: read lwpstatus"); + (void) close(lwpfd); + return (EINVAL); + } + (void) close(lwpfd); + } + return (0); +} + +#ifndef AMD64 + +// Detecting SSE support by OS +// From solaris_i486.s +extern "C" bool sse_check(); +extern "C" bool sse_unavailable(); + +enum { SSE_UNKNOWN, SSE_NOT_SUPPORTED, SSE_SUPPORTED}; +static int sse_status = SSE_UNKNOWN; + + +static void check_for_sse_support() { + if (!VM_Version::supports_sse()) { + sse_status = SSE_NOT_SUPPORTED; + return; + } + // looking for _sse_hw in libc.so, if it does not exist or + // the value (int) is 0, OS has no support for SSE + int *sse_hwp; + void *h; + + if ((h=dlopen("/usr/lib/libc.so", RTLD_LAZY)) == NULL) { + //open failed, presume no support for SSE + sse_status = SSE_NOT_SUPPORTED; + return; + } + if ((sse_hwp = (int *)dlsym(h, "_sse_hw")) == NULL) { + sse_status = SSE_NOT_SUPPORTED; + } else if (*sse_hwp == 0) { + sse_status = SSE_NOT_SUPPORTED; + } + dlclose(h); + + if (sse_status == SSE_UNKNOWN) { + bool (*try_sse)() = (bool (*)())sse_check; + sse_status = (*try_sse)() ? SSE_SUPPORTED : SSE_NOT_SUPPORTED; + } + +} + +bool os::supports_sse() { + if (sse_status == SSE_UNKNOWN) + check_for_sse_support(); + return sse_status == SSE_SUPPORTED; +} + +#endif // AMD64 + +bool os::is_allocatable(size_t bytes) { +#ifdef AMD64 + return true; +#else + + if (bytes < 2 * G) { + return true; + } + + char* addr = reserve_memory(bytes, NULL); + + if (addr != NULL) { + release_memory(addr, bytes); + } + + return addr != NULL; +#endif // AMD64 + +} + +extern "C" int JVM_handle_solaris_signal(int signo, siginfo_t* siginfo, void* ucontext, int abort_if_unrecognized); + +extern "C" void Fetch32PFI () ; +extern "C" void Fetch32Resume () ; +#ifdef AMD64 +extern "C" void FetchNPFI () ; +extern "C" void FetchNResume () ; +#endif // AMD64 + +int JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrecognized) { + ucontext_t* uc = (ucontext_t*) ucVoid; + +#ifndef AMD64 + if (sig == SIGILL && info->si_addr == (caddr_t)sse_check) { + // the SSE instruction faulted. supports_sse() need return false. + uc->uc_mcontext.gregs[EIP] = (greg_t)sse_unavailable; + return true; + } +#endif // !AMD64 + + Thread* t = ThreadLocalStorage::get_thread_slow(); // slow & steady + + SignalHandlerMark shm(t); + + if(sig == SIGPIPE || sig == SIGXFSZ) { + if (os::Solaris::chained_handler(sig, info, ucVoid)) { + return true; + } else { + if (PrintMiscellaneous && (WizardMode || Verbose)) { + char buf[64]; + warning("Ignoring %s - see 4229104 or 6499219", + os::exception_name(sig, buf, sizeof(buf))); + + } + return true; + } + } + + JavaThread* thread = NULL; + VMThread* vmthread = NULL; + + if (os::Solaris::signal_handlers_are_installed) { + if (t != NULL ){ + if(t->is_Java_thread()) { + thread = (JavaThread*)t; + } + else if(t->is_VM_thread()){ + vmthread = (VMThread *)t; + } + } + } + + guarantee(sig != os::Solaris::SIGinterrupt(), "Can not chain VM interrupt signal, try -XX:+UseAltSigs"); + + if (sig == os::Solaris::SIGasync()) { + if(thread){ + OSThread::InterruptArguments args(thread, uc); + thread->osthread()->do_interrupt_callbacks_at_interrupt(&args); + return true; + } + else if(vmthread){ + OSThread::InterruptArguments args(vmthread, uc); + vmthread->osthread()->do_interrupt_callbacks_at_interrupt(&args); + return true; + } else if (os::Solaris::chained_handler(sig, info, ucVoid)) { + return true; + } else { + // If os::Solaris::SIGasync not chained, and this is a non-vm and + // non-java thread + return true; + } + } + + if (info == NULL || info->si_code <= 0 || info->si_code == SI_NOINFO) { + // can't decode this kind of signal + info = NULL; + } else { + assert(sig == info->si_signo, "bad siginfo"); + } + + // decide if this trap can be handled by a stub + address stub = NULL; + + address pc = NULL; + + //%note os_trap_1 + if (info != NULL && uc != NULL && thread != NULL) { + // factor me: getPCfromContext + pc = (address) uc->uc_mcontext.gregs[REG_PC]; + + // SafeFetch32() support + if (pc == (address) Fetch32PFI) { + uc->uc_mcontext.gregs[REG_PC] = intptr_t(Fetch32Resume) ; + return true ; + } +#ifdef AMD64 + if (pc == (address) FetchNPFI) { + uc->uc_mcontext.gregs [REG_PC] = intptr_t(FetchNResume) ; + return true ; + } +#endif // AMD64 + + // Handle ALL stack overflow variations here + if (sig == SIGSEGV && info->si_code == SEGV_ACCERR) { + address addr = (address) info->si_addr; + if (thread->in_stack_yellow_zone(addr)) { + thread->disable_stack_yellow_zone(); + if (thread->thread_state() == _thread_in_Java) { + // Throw a stack overflow exception. Guard pages will be reenabled + // while unwinding the stack. + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); + } else { + // Thread was in the vm or native code. Return and try to finish. + return true; + } + } else if (thread->in_stack_red_zone(addr)) { + // Fatal red zone violation. Disable the guard pages and fall through + // to handle_unexpected_exception way down below. + thread->disable_stack_red_zone(); + tty->print_raw_cr("An irrecoverable stack overflow has occurred."); + } + } + + if (thread->thread_state() == _thread_in_vm) { + if (sig == SIGBUS && info->si_code == BUS_OBJERR && thread->doing_unsafe_access()) { + stub = StubRoutines::handler_for_unsafe_access(); + } + } + + if (thread->thread_state() == _thread_in_Java) { + // Support Safepoint Polling + if ( sig == SIGSEGV && os::is_poll_address((address)info->si_addr)) { + stub = SharedRuntime::get_poll_stub(pc); + } + else if (sig == SIGBUS && info->si_code == BUS_OBJERR) { + // BugId 4454115: A read from a MappedByteBuffer can fault + // here if the underlying file has been truncated. + // Do not crash the VM in such a case. + CodeBlob* cb = CodeCache::find_blob_unsafe(pc); + nmethod* nm = cb->is_nmethod() ? (nmethod*)cb : NULL; + if (nm != NULL && nm->has_unsafe_access()) { + stub = StubRoutines::handler_for_unsafe_access(); + } + } + else + if (sig == SIGFPE && info->si_code == FPE_INTDIV) { + // integer divide by zero + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_DIVIDE_BY_ZERO); + } +#ifndef AMD64 + else if (sig == SIGFPE && info->si_code == FPE_FLTDIV) { + // floating-point divide by zero + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_DIVIDE_BY_ZERO); + } + else if (sig == SIGFPE && info->si_code == FPE_FLTINV) { + // The encoding of D2I in i486.ad can cause an exception prior + // to the fist instruction if there was an invalid operation + // pending. We want to dismiss that exception. From the win_32 + // side it also seems that if it really was the fist causing + // the exception that we do the d2i by hand with different + // rounding. Seems kind of weird. QQQ TODO + // Note that we take the exception at the NEXT floating point instruction. + if (pc[0] == 0xDB) { + assert(pc[0] == 0xDB, "not a FIST opcode"); + assert(pc[1] == 0x14, "not a FIST opcode"); + assert(pc[2] == 0x24, "not a FIST opcode"); + return true; + } else { + assert(pc[-3] == 0xDB, "not an flt invalid opcode"); + assert(pc[-2] == 0x14, "not an flt invalid opcode"); + assert(pc[-1] == 0x24, "not an flt invalid opcode"); + } + } + else if (sig == SIGFPE ) { + tty->print_cr("caught SIGFPE, info 0x%x.", info->si_code); + } +#endif // !AMD64 + + // QQQ It doesn't seem that we need to do this on x86 because we should be able + // to return properly from the handler without this extra stuff on the back side. + + else if (sig == SIGSEGV && info->si_code > 0 && !MacroAssembler::needs_explicit_null_check((intptr_t)info->si_addr)) { + // Determination of interpreter/vtable stub/compiled code null exception + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); + } + } + + // jni_fast_GetField can trap at certain pc's if a GC kicks in + // and the heap gets shrunk before the field access. + if ((sig == SIGSEGV) || (sig == SIGBUS)) { + address addr = JNI_FastGetField::find_slowcase_pc(pc); + if (addr != (address)-1) { + stub = addr; + } + } + + // Check to see if we caught the safepoint code in the + // process of write protecting the memory serialization page. + // It write enables the page immediately after protecting it + // so we can just return to retry the write. + if ((sig == SIGSEGV) && + os::is_memory_serialize_page(thread, (address)info->si_addr)) { + // Block current thread until the memory serialize page permission restored. + os::block_on_serialize_page_trap(); + return true; + } + } + + // Execution protection violation + // + // Preventative code for future versions of Solaris which may + // enable execution protection when running the 32-bit VM on AMD64. + // + // This should be kept as the last step in the triage. We don't + // have a dedicated trap number for a no-execute fault, so be + // conservative and allow other handlers the first shot. + // + // Note: We don't test that info->si_code == SEGV_ACCERR here. + // this si_code is so generic that it is almost meaningless; and + // the si_code for this condition may change in the future. + // Furthermore, a false-positive should be harmless. + if (UnguardOnExecutionViolation > 0 && + (sig == SIGSEGV || sig == SIGBUS) && + uc->uc_mcontext.gregs[TRAPNO] == T_PGFLT) { // page fault + int page_size = os::vm_page_size(); + address addr = (address) info->si_addr; + address pc = (address) uc->uc_mcontext.gregs[REG_PC]; + // Make sure the pc and the faulting address are sane. + // + // If an instruction spans a page boundary, and the page containing + // the beginning of the instruction is executable but the following + // page is not, the pc and the faulting address might be slightly + // different - we still want to unguard the 2nd page in this case. + // + // 15 bytes seems to be a (very) safe value for max instruction size. + bool pc_is_near_addr = + (pointer_delta((void*) addr, (void*) pc, sizeof(char)) < 15); + bool instr_spans_page_boundary = + (align_size_down((intptr_t) pc ^ (intptr_t) addr, + (intptr_t) page_size) > 0); + + if (pc == addr || (pc_is_near_addr && instr_spans_page_boundary)) { + static volatile address last_addr = + (address) os::non_memory_address_word(); + + // In conservative mode, don't unguard unless the address is in the VM + if (addr != last_addr && + (UnguardOnExecutionViolation > 1 || os::address_is_in_vm(addr))) { + + // Unguard and retry + address page_start = + (address) align_size_down((intptr_t) addr, (intptr_t) page_size); + bool res = os::unguard_memory((char*) page_start, page_size); + + if (PrintMiscellaneous && Verbose) { + char buf[256]; + jio_snprintf(buf, sizeof(buf), "Execution protection violation " + "at " INTPTR_FORMAT + ", unguarding " INTPTR_FORMAT ": %s, errno=%d", addr, + page_start, (res ? "success" : "failed"), errno); + tty->print_raw_cr(buf); + } + stub = pc; + + // Set last_addr so if we fault again at the same address, we don't end + // up in an endless loop. + // + // There are two potential complications here. Two threads trapping at + // the same address at the same time could cause one of the threads to + // think it already unguarded, and abort the VM. Likely very rare. + // + // The other race involves two threads alternately trapping at + // different addresses and failing to unguard the page, resulting in + // an endless loop. This condition is probably even more unlikely than + // the first. + // + // Although both cases could be avoided by using locks or thread local + // last_addr, these solutions are unnecessary complication: this + // handler is a best-effort safety net, not a complete solution. It is + // disabled by default and should only be used as a workaround in case + // we missed any no-execute-unsafe VM code. + + last_addr = addr; + } + } + } + + if (stub != NULL) { + // save all thread context in case we need to restore it + + if (thread != NULL) thread->set_saved_exception_pc(pc); + // 12/02/99: On Sparc it appears that the full context is also saved + // but as yet, no one looks at or restores that saved context + // factor me: setPC + uc->uc_mcontext.gregs[REG_PC] = (greg_t)stub; + return true; + } + + // signal-chaining + if (os::Solaris::chained_handler(sig, info, ucVoid)) { + return true; + } + +#ifndef AMD64 + // Workaround (bug 4900493) for Solaris kernel bug 4966651. + // Handle an undefined selector caused by an attempt to assign + // fs in libthread getipriptr(). With the current libthread design every 512 + // thread creations the LDT for a private thread data structure is extended + // and thre is a hazard that and another thread attempting a thread creation + // will use a stale LDTR that doesn't reflect the structure's growth, + // causing a GP fault. + // Enforce the probable limit of passes through here to guard against an + // infinite loop if some other move to fs caused the GP fault. Note that + // this loop counter is ultimately a heuristic as it is possible for + // more than one thread to generate this fault at a time in an MP system. + // In the case of the loop count being exceeded or if the poll fails + // just fall through to a fatal error. + // If there is some other source of T_GPFLT traps and the text at EIP is + // unreadable this code will loop infinitely until the stack is exausted. + // The key to diagnosis in this case is to look for the bottom signal handler + // frame. + + if(! IgnoreLibthreadGPFault) { + if (sig == SIGSEGV && uc->uc_mcontext.gregs[TRAPNO] == T_GPFLT) { + const unsigned char *p = + (unsigned const char *) uc->uc_mcontext.gregs[EIP]; + + // Expected instruction? + + if(p[0] == movlfs[0] && p[1] == movlfs[1]) { + + Atomic::inc(&ldtr_refresh); + + // Infinite loop? + + if(ldtr_refresh < ((2 << 16) / PAGESIZE)) { + + // No, force scheduling to get a fresh view of the LDTR + + if(poll(NULL, 0, 10) == 0) { + + // Retry the move + + return false; + } + } + } + } + } +#endif // !AMD64 + + if (!abort_if_unrecognized) { + // caller wants another chance, so give it to him + return false; + } + + if (!os::Solaris::libjsig_is_loaded) { + struct sigaction oldAct; + sigaction(sig, (struct sigaction *)0, &oldAct); + if (oldAct.sa_sigaction != signalHandler) { + void* sighand = oldAct.sa_sigaction ? CAST_FROM_FN_PTR(void*, oldAct.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oldAct.sa_handler); + warning("Unexpected Signal %d occured under user-defined signal handler %#lx", sig, (long)sighand); + } + } + + if (pc == NULL && uc != NULL) { + pc = (address) uc->uc_mcontext.gregs[REG_PC]; + } + + // unmask current signal + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); + sigprocmask(SIG_UNBLOCK, &newset, NULL); + + VMError err(t, sig, pc, info, ucVoid); + err.report_and_die(); + + ShouldNotReachHere(); +} + +void os::print_context(outputStream *st, void *context) { + if (context == NULL) return; + + ucontext_t *uc = (ucontext_t*)context; + st->print_cr("Registers:"); +#ifdef AMD64 + st->print( "RAX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RAX]); + st->print(", RBX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RBX]); + st->print(", RCX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RCX]); + st->print(", RDX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RDX]); + st->cr(); + st->print( "RSP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RSP]); + st->print(", RBP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RBP]); + st->print(", RSI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RSI]); + st->print(", RDI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RDI]); + st->cr(); + st->print(", R8=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R8]); + st->print(", R9=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R9]); + st->print(", R10=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R10]); + st->print(", R11=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R11]); + st->print(", R12=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R12]); + st->print(", R13=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R13]); + st->print(", R14=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R14]); + st->print(", R15=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R15]); + st->cr(); + st->print( "RIP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RIP]); + st->print(", RFLAGS=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RFL]); +#else + st->print( "EAX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[EAX]); + st->print(", EBX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[EBX]); + st->print(", ECX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[ECX]); + st->print(", EDX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[EDX]); + st->cr(); + st->print( "ESP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[UESP]); + st->print(", EBP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[EBP]); + st->print(", ESI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[ESI]); + st->print(", EDI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[EDI]); + st->cr(); + st->print( "EIP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[EIP]); + st->print(", EFLAGS=" INTPTR_FORMAT, uc->uc_mcontext.gregs[EFL]); +#endif // AMD64 + st->cr(); + st->cr(); + + intptr_t *sp = (intptr_t *)os::Solaris::ucontext_get_sp(uc); + st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", sp); + print_hex_dump(st, (address)sp, (address)(sp + 8*sizeof(intptr_t)), sizeof(intptr_t)); + st->cr(); + + // Note: it may be unsafe to inspect memory near pc. For example, pc may + // point to garbage if entry point in an nmethod is corrupted. Leave + // this at the end, and hope for the best. + ExtendedPC epc = os::Solaris::ucontext_get_ExtendedPC(uc); + address pc = epc.pc(); + st->print_cr("Instructions: (pc=" PTR_FORMAT ")", pc); + print_hex_dump(st, pc - 16, pc + 16, sizeof(char)); +} + +#ifdef AMD64 +void os::Solaris::init_thread_fpu_state(void) { + // Nothing to do +} +#else +// From solaris_i486.s +extern "C" void fixcw(); + +void os::Solaris::init_thread_fpu_state(void) { + // Set fpu to 53 bit precision. This happens too early to use a stub. + fixcw(); +} + +// These routines are the initial value of atomic_xchg_entry(), +// atomic_cmpxchg_entry(), atomic_inc_entry() and fence_entry() +// until initialization is complete. +// TODO - replace with .il implementation when compiler supports it. + +typedef jint xchg_func_t (jint, volatile jint*); +typedef jint cmpxchg_func_t (jint, volatile jint*, jint); +typedef jlong cmpxchg_long_func_t(jlong, volatile jlong*, jlong); +typedef jint add_func_t (jint, volatile jint*); +typedef void fence_func_t (); + +jint os::atomic_xchg_bootstrap(jint exchange_value, volatile jint* dest) { + // try to use the stub: + xchg_func_t* func = CAST_TO_FN_PTR(xchg_func_t*, StubRoutines::atomic_xchg_entry()); + + if (func != NULL) { + os::atomic_xchg_func = func; + return (*func)(exchange_value, dest); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + jint old_value = *dest; + *dest = exchange_value; + return old_value; +} + +jint os::atomic_cmpxchg_bootstrap(jint exchange_value, volatile jint* dest, jint compare_value) { + // try to use the stub: + cmpxchg_func_t* func = CAST_TO_FN_PTR(cmpxchg_func_t*, StubRoutines::atomic_cmpxchg_entry()); + + if (func != NULL) { + os::atomic_cmpxchg_func = func; + return (*func)(exchange_value, dest, compare_value); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + jint old_value = *dest; + if (old_value == compare_value) + *dest = exchange_value; + return old_value; +} + +jlong os::atomic_cmpxchg_long_bootstrap(jlong exchange_value, volatile jlong* dest, jlong compare_value) { + // try to use the stub: + cmpxchg_long_func_t* func = CAST_TO_FN_PTR(cmpxchg_long_func_t*, StubRoutines::atomic_cmpxchg_long_entry()); + + if (func != NULL) { + os::atomic_cmpxchg_long_func = func; + return (*func)(exchange_value, dest, compare_value); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + jlong old_value = *dest; + if (old_value == compare_value) + *dest = exchange_value; + return old_value; +} + +jint os::atomic_add_bootstrap(jint add_value, volatile jint* dest) { + // try to use the stub: + add_func_t* func = CAST_TO_FN_PTR(add_func_t*, StubRoutines::atomic_add_entry()); + + if (func != NULL) { + os::atomic_add_func = func; + return (*func)(add_value, dest); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + return (*dest) += add_value; +} + +void os::fence_bootstrap() { + // try to use the stub: + fence_func_t* func = CAST_TO_FN_PTR(fence_func_t*, StubRoutines::fence_entry()); + + if (func != NULL) { + os::fence_func = func; + (*func)(); + return; + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + // don't have to do anything for a single thread +} + +xchg_func_t* os::atomic_xchg_func = os::atomic_xchg_bootstrap; +cmpxchg_func_t* os::atomic_cmpxchg_func = os::atomic_cmpxchg_bootstrap; +cmpxchg_long_func_t* os::atomic_cmpxchg_long_func = os::atomic_cmpxchg_long_bootstrap; +add_func_t* os::atomic_add_func = os::atomic_add_bootstrap; +fence_func_t* os::fence_func = os::fence_bootstrap; + +extern "C" _solaris_raw_setup_fpu(address ptr); +void os::setup_fpu() { + address fpu_cntrl = StubRoutines::addr_fpu_cntrl_wrd_std(); + _solaris_raw_setup_fpu(fpu_cntrl); +} +#endif // AMD64 diff --git a/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.hpp b/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.hpp new file mode 100644 index 00000000000..c7f1de37c6b --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // + // NOTE: we are back in class os here, not Solaris + // +#ifdef AMD64 + static void setup_fpu() {} +#else + static jint (*atomic_xchg_func) (jint, volatile jint*); + static jint (*atomic_cmpxchg_func) (jint, volatile jint*, jint); + static jlong (*atomic_cmpxchg_long_func)(jlong, volatile jlong*, jlong); + static jint (*atomic_add_func) (jint, volatile jint*); + static void (*fence_func) (); + + static jint atomic_xchg_bootstrap (jint, volatile jint*); + static jint atomic_cmpxchg_bootstrap (jint, volatile jint*, jint); + static jlong atomic_cmpxchg_long_bootstrap(jlong, volatile jlong*, jlong); + static jint atomic_add_bootstrap (jint, volatile jint*); + static void fence_bootstrap (); + + static void setup_fpu(); + static bool supports_sse(); +#endif // AMD64 + + static bool is_allocatable(size_t bytes); + + // Used to register dynamic code cache area with the OS + // Note: Currently only used in 64 bit Windows implementations + static bool register_code_area(char *low, char *high) { return true; } diff --git a/hotspot/src/os_cpu/solaris_x86/vm/prefetch_solaris_x86.inline.hpp b/hotspot/src/os_cpu/solaris_x86/vm/prefetch_solaris_x86.inline.hpp new file mode 100644 index 00000000000..e5ea02ce639 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/prefetch_solaris_x86.inline.hpp @@ -0,0 +1,41 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +extern "C" { + void _Prefetch_read (void *loc, intx interval); + void _Prefetch_write(void *loc, intx interval); +} + +inline void Prefetch::read (void *loc, intx interval) { +#ifdef AMD64 + _Prefetch_read(loc, interval); +#endif // AMD64 +} + +// Use of this method should be gated by VM_Version::has_prefetchw. +inline void Prefetch::write(void *loc, intx interval) { +#ifdef AMD64 + _Prefetch_write(loc, interval); +#endif // AMD64 +} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.ad b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.ad new file mode 100644 index 00000000000..0409fb55d8b --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.ad @@ -0,0 +1,167 @@ +// +// Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// X86 Solaris Architecture Description File + +//----------OS-DEPENDENT ENCODING BLOCK----------------------------------------------------- +// This block specifies the encoding classes used by the compiler to output +// byte streams. Encoding classes generate functions which are called by +// Machine Instruction Nodes in order to generate the bit encoding of the +// instruction. Operands specify their base encoding interface with the +// interface keyword. There are currently supported four interfaces, +// REG_INTER, CONST_INTER, MEMORY_INTER, & COND_INTER. REG_INTER causes an +// operand to generate a function which returns its register number when +// queried. CONST_INTER causes an operand to generate a function which +// returns the value of the constant when queried. MEMORY_INTER causes an +// operand to generate four functions which return the Base Register, the +// Index Register, the Scale Value, and the Offset Value of the operand when +// queried. COND_INTER causes an operand to generate six functions which +// return the encoding code (ie - encoding bits for the instruction) +// associated with each basic boolean condition for a conditional instruction. +// Instructions specify two basic values for encoding. They use the +// ins_encode keyword to specify their encoding class (which must be one of +// the class names specified in the encoding block), and they use the +// opcode keyword to specify, in order, their primary, secondary, and +// tertiary opcode. Only the opcode sections which a particular instruction +// needs for encoding need to be specified. +encode %{ + // Build emit functions for each basic byte or larger field in the intel + // encoding scheme (opcode, rm, sib, immediate), and call them from C++ + // code in the enc_class source block. Emit functions will live in the + // main source block for now. In future, we can generalize this by + // adding a syntax that specifies the sizes of fields in an order, + // so that the adlc can build the emit functions automagically + + enc_class solaris_tlsencode (eRegP dst) %{ + Register dstReg = as_Register($dst$$reg); + MacroAssembler* masm = new MacroAssembler(&cbuf); + masm->get_thread(dstReg); + %} + + enc_class solaris_breakpoint %{ + MacroAssembler* masm = new MacroAssembler(&cbuf); + // Really need to fix this + masm->pushl(rax); + masm->pushl(rcx); + masm->pushl(rdx); + masm->call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); + masm->popl(rdx); + masm->popl(rcx); + masm->popl(rax); + %} + + enc_class call_epilog %{ + if( VerifyStackAtCalls ) { + // Check that stack depth is unchanged: find majik cookie on stack + int framesize = ra_->reg2offset_unchecked(OptoReg::add(ra_->_matcher._old_SP,-3*VMRegImpl::slots_per_word)); + if(framesize >= 128) { + emit_opcode(cbuf, 0x81); // cmp [esp+0],0xbadb1ood + emit_d8(cbuf,0xBC); + emit_d8(cbuf,0x24); + emit_d32(cbuf,framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } + else { + emit_opcode(cbuf, 0x81); // cmp [esp+0],0xbadb1ood + emit_d8(cbuf,0x7C); + emit_d8(cbuf,0x24); + emit_d8(cbuf,framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } + // jmp EQ around INT3 + // QQQ TODO + const int jump_around = 11; // size of call to breakpoint (and register preserve), 1 for CC + emit_opcode(cbuf,0x74); + emit_d8(cbuf, jump_around); + // QQQ temporary + emit_break(cbuf); + // Die if stack mismatch + // emit_opcode(cbuf,0xCC); + } + %} + +%} + +// INSTRUCTIONS -- Platform dependent + +//----------OS and Locking Instructions---------------------------------------- + +// This name is KNOWN by the ADLC and cannot be changed. +// The ADLC forces a 'TypeRawPtr::BOTTOM' output type +// for this guy. +instruct tlsLoadP(eAXRegP dst, eFlagsReg cr) %{ + match(Set dst (ThreadLocal)); + effect(DEF dst, KILL cr); + + format %{ "MOV EAX, Thread::current()" %} + ins_encode( solaris_tlsencode(dst) ); + ins_pipe( ialu_reg_fat ); +%} + +instruct TLS(eAXRegP dst) %{ + match(Set dst (ThreadLocal)); + + expand %{ + tlsLoadP(dst); + %} +%} + +// Die now +instruct ShouldNotReachHere( ) +%{ + match(Halt); + + // Use the following format syntax + format %{ "INT3 ; ShouldNotReachHere" %} + // QQQ TODO for now call breakpoint + // opcode(0xCC); + // ins_encode(Opc); + ins_encode(solaris_breakpoint); + ins_pipe( pipe_slow ); +%} + + + +// Platform dependent source + +source %{ + +// emit an interrupt that is caught by the debugger +void emit_break(CodeBuffer &cbuf) { + + // Debugger doesn't really catch this but best we can do so far QQQ + MacroAssembler* masm = new MacroAssembler(&cbuf); + masm->call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); +} + +void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + emit_break(cbuf); +} + + +uint MachBreakpointNode::size(PhaseRegAlloc *ra_) const { + return 5; +} + +%} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.il b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.il new file mode 100644 index 00000000000..49ae97729fe --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.il @@ -0,0 +1,129 @@ +// +// Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + + + // Support for u8 os::setup_fpu() + .inline _solaris_raw_setup_fpu,1 + movl 0(%esp), %eax + fldcw (%eax) + .end + + // The argument size of each inline directive is ignored by the compiler + // and is set to 0 for compatibility reason. + + // Get the raw thread ID from %gs:0 + .inline _raw_thread_id,0 + movl %gs:0, %eax + .end + + // Get callers fp + .inline _get_previous_fp,0 + movl %ebp, %eax + movl %eax, %eax + .end + + // Support for jint Atomic::add(jint inc, volatile jint* dest) + // An additional bool (os::is_MP()) is passed as the last argument. + .inline _Atomic_add,3 + movl 0(%esp), %eax // inc + movl 4(%esp), %edx // dest + movl %eax, %ecx + cmpl $0, 8(%esp) // MP test + je 1f + lock +1: xaddl %eax, (%edx) + addl %ecx, %eax + .end + + // Support for jint Atomic::xchg(jint exchange_value, volatile jint* dest). + .inline _Atomic_xchg,2 + movl 0(%esp), %eax // exchange_value + movl 4(%esp), %ecx // dest + xchgl (%ecx), %eax + .end + + // Support for jint Atomic::cmpxchg(jint exchange_value, + // volatile jint *dest, + // jint compare_value) + // An additional bool (os::is_MP()) is passed as the last argument. + .inline _Atomic_cmpxchg,4 + movl 8(%esp), %eax // compare_value + movl 0(%esp), %ecx // exchange_value + movl 4(%esp), %edx // dest + cmp $0, 12(%esp) // MP test + je 1f + lock +1: cmpxchgl %ecx, (%edx) + .end + + // Support for jlong Atomic::cmpxchg(jlong exchange_value, + // volatile jlong* dest, + // jlong compare_value) + // An additional bool (os::is_MP()) is passed as the last argument. + .inline _Atomic_cmpxchg_long,6 + pushl %ebx + pushl %edi + movl 20(%esp), %eax // compare_value (low) + movl 24(%esp), %edx // compare_value (high) + movl 16(%esp), %edi // dest + movl 8(%esp), %ebx // exchange_value (low) + movl 12(%esp), %ecx // exchange_high (high) + cmp $0, 28(%esp) // MP test + je 1f + lock +1: cmpxchg8b (%edi) + popl %edi + popl %ebx + .end + + // Support for OrderAccess::acquire() + .inline _OrderAccess_acquire,0 + movl 0(%esp), %eax + .end + + // Support for OrderAccess::fence() + .inline _OrderAccess_fence,0 + lock + addl $0, (%esp) + .end + + // Support for u2 Bytes::swap_u2(u2 x) + .inline _raw_swap_u2,1 + movl 0(%esp), %eax + xchgb %al, %ah + .end + + // Support for u4 Bytes::swap_u4(u4 x) + .inline _raw_swap_u4,1 + movl 0(%esp), %eax + bswap %eax + .end + + // Support for u8 Bytes::swap_u8_base(u4 x, u4 y) + .inline _raw_swap_u8,2 + movl 4(%esp), %eax // y + movl 0(%esp), %edx // x + bswap %eax + bswap %edx + .end diff --git a/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.s b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.s new file mode 100644 index 00000000000..fc9a849705e --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.s @@ -0,0 +1,676 @@ +// +// Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// + + .globl fixcw + .globl sse_check + .globl sse_unavailable + .globl gs_load + .globl gs_thread + .globl _Atomic_cmpxchg_long_gcc + + // NOTE WELL! The _Copy functions are called directly + // from server-compiler-generated code via CallLeafNoFP, + // which means that they *must* either not use floating + // point or use it in the same manner as does the server + // compiler. + + .globl _Copy_conjoint_bytes + .globl _Copy_arrayof_conjoint_bytes + .globl _Copy_conjoint_jshorts_atomic + .globl _Copy_arrayof_conjoint_jshorts + .globl _Copy_conjoint_jints_atomic + .globl _Copy_arrayof_conjoint_jints + .globl _Copy_conjoint_jlongs_atomic + .globl _mmx_Copy_arrayof_conjoint_jshorts + + .section .text,"ax" + +/ Support for void os::Solaris::init_thread_fpu_state() in os_solaris_i486.cpp +/ Set fpu to 53 bit precision. This happens too early to use a stub. + .align 16 +fixcw: + pushl $0x27f + fldcw 0(%esp) + popl %eax + ret + + .align 16 + .globl SafeFetch32 + .globl SafeFetchN + .globl Fetch32PFI, Fetch32Resume +SafeFetch32: +SafeFetchN: + movl 0x8(%esp), %eax + movl 0x4(%esp), %ecx +Fetch32PFI: + movl (%ecx), %eax +Fetch32Resume: + ret + + + .align 16 + .globl SpinPause +SpinPause: + rep + nop + movl $1, %eax + ret + + +/ Test SSE availability, used by os_solaris_i486.cpp + .align 16 +sse_check: + / Fault if SSE not available + xorps %xmm0,%xmm0 + / No fault + movl $1,%eax + ret + / Signal handler continues here if SSE is not available +sse_unavailable: + xorl %eax,%eax + ret + +/ Fast thread accessors, used by threadLS_solaris_i486.cpp + .align 16 +gs_load: + movl 4(%esp),%ecx + movl %gs:(%ecx),%eax + ret + + .align 16 +gs_thread: + movl %gs:0x0,%eax + ret + + / Support for void Copy::conjoint_bytes(void* from, + / void* to, + / size_t count) + .align 16 +_Copy_conjoint_bytes: + pushl %esi + movl 4+12(%esp),%ecx / count + pushl %edi + movl 8+ 4(%esp),%esi / from + movl 8+ 8(%esp),%edi / to + cmpl %esi,%edi + leal -1(%esi,%ecx),%eax / from + count - 1 + jbe cb_CopyRight + cmpl %eax,%edi + jbe cb_CopyLeft + / copy from low to high +cb_CopyRight: + cmpl $3,%ecx + jbe 5f / <= 3 bytes + / align source address at dword address boundary + movl %ecx,%eax / original count + movl $4,%ecx + subl %esi,%ecx + andl $3,%ecx / prefix byte count + jz 1f / no prefix + subl %ecx,%eax / byte count less prefix + / copy prefix + subl %esi,%edi +0: movb (%esi),%dl + movb %dl,(%edi,%esi,1) + addl $1,%esi + subl $1,%ecx + jnz 0b + addl %esi,%edi +1: movl %eax,%ecx / byte count less prefix + shrl $2,%ecx / dword count + jz 4f / no dwords to move + cmpl $32,%ecx + jbe 2f / <= 32 dwords + / copy aligned dwords + rep; smovl + jmp 4f + / copy aligned dwords +2: subl %esi,%edi + .align 16 +3: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 3b + addl %esi,%edi +4: movl %eax,%ecx / byte count less prefix + andl $3,%ecx / suffix byte count + jz 7f / no suffix + / copy suffix +5: xorl %eax,%eax +6: movb (%esi,%eax,1),%dl + movb %dl,(%edi,%eax,1) + addl $1,%eax + subl $1,%ecx + jnz 6b +7: popl %edi + popl %esi + ret + / copy from high to low +cb_CopyLeft: + std + leal -4(%edi,%ecx),%edi / to + count - 4 + movl %eax,%esi / from + count - 1 + movl %ecx,%eax + subl $3,%esi / from + count - 4 + cmpl $3,%ecx + jbe 5f / <= 3 bytes +1: shrl $2,%ecx / dword count + jz 4f / no dwords to move + cmpl $32,%ecx + ja 3f / > 32 dwords + / copy dwords, aligned or not + subl %esi,%edi + .align 16 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + subl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 4f + / copy dwords, aligned or not +3: rep; smovl +4: movl %eax,%ecx / byte count + andl $3,%ecx / suffix byte count + jz 7f / no suffix + / copy suffix +5: subl %esi,%edi + addl $3,%esi +6: movb (%esi),%dl + movb %dl,(%edi,%esi,1) + subl $1,%esi + subl $1,%ecx + jnz 6b +7: cld + popl %edi + popl %esi + ret + + / Support for void Copy::arrayof_conjoint_bytes(void* from, + / void* to, + / size_t count) + / + / Same as _Copy_conjoint_bytes, except no source alignment check. + .align 16 +_Copy_arrayof_conjoint_bytes: + pushl %esi + movl 4+12(%esp),%ecx / count + pushl %edi + movl 8+ 4(%esp),%esi / from + movl 8+ 8(%esp),%edi / to + cmpl %esi,%edi + leal -1(%esi,%ecx),%eax / from + count - 1 + jbe acb_CopyRight + cmpl %eax,%edi + jbe acb_CopyLeft + / copy from low to high +acb_CopyRight: + cmpl $3,%ecx + jbe 5f +1: movl %ecx,%eax + shrl $2,%ecx + jz 4f + cmpl $32,%ecx + ja 3f + / copy aligned dwords + subl %esi,%edi + .align 16 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 4f + / copy aligned dwords +3: rep; smovl +4: movl %eax,%ecx + andl $3,%ecx + jz 7f + / copy suffix +5: xorl %eax,%eax +6: movb (%esi,%eax,1),%dl + movb %dl,(%edi,%eax,1) + addl $1,%eax + subl $1,%ecx + jnz 6b +7: popl %edi + popl %esi + ret +acb_CopyLeft: + std + leal -4(%edi,%ecx),%edi / to + count - 4 + movl %eax,%esi / from + count - 1 + movl %ecx,%eax + subl $3,%esi / from + count - 4 + cmpl $3,%ecx + jbe 5f +1: shrl $2,%ecx + jz 4f + cmpl $32,%ecx + jbe 2f / <= 32 dwords + rep; smovl + jmp 4f + .=.+8 +2: subl %esi,%edi + .align 16 +3: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + subl $4,%esi + subl $1,%ecx + jnz 3b + addl %esi,%edi +4: movl %eax,%ecx + andl $3,%ecx + jz 7f +5: subl %esi,%edi + addl $3,%esi +6: movb (%esi),%dl + movb %dl,(%edi,%esi,1) + subl $1,%esi + subl $1,%ecx + jnz 6b +7: cld + popl %edi + popl %esi + ret + + / Support for void Copy::conjoint_jshorts_atomic(void* from, + / void* to, + / size_t count) + .align 16 +_Copy_conjoint_jshorts_atomic: + pushl %esi + movl 4+12(%esp),%ecx / count + pushl %edi + movl 8+ 4(%esp),%esi / from + movl 8+ 8(%esp),%edi / to + cmpl %esi,%edi + leal -2(%esi,%ecx,2),%eax / from + count*2 - 2 + jbe cs_CopyRight + cmpl %eax,%edi + jbe cs_CopyLeft + / copy from low to high +cs_CopyRight: + / align source address at dword address boundary + movl %esi,%eax / original from + andl $3,%eax / either 0 or 2 + jz 1f / no prefix + / copy prefix + movw (%esi),%dx + movw %dx,(%edi) + addl %eax,%esi / %eax == 2 + addl %eax,%edi + subl $1,%ecx +1: movl %ecx,%eax / word count less prefix + sarl %ecx / dword count + jz 4f / no dwords to move + cmpl $32,%ecx + jbe 2f / <= 32 dwords + / copy aligned dwords + rep; smovl + jmp 4f + / copy aligned dwords +2: subl %esi,%edi + .align 16 +3: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 3b + addl %esi,%edi +4: andl $1,%eax / suffix count + jz 5f / no suffix + / copy suffix + movw (%esi),%dx + movw %dx,(%edi) +5: popl %edi + popl %esi + ret + / copy from high to low +cs_CopyLeft: + std + leal -4(%edi,%ecx,2),%edi / to + count*2 - 4 + movl %eax,%esi / from + count*2 - 2 + movl %ecx,%eax + subl $2,%esi / from + count*2 - 4 +1: sarl %ecx / dword count + jz 4f / no dwords to move + cmpl $32,%ecx + ja 3f / > 32 dwords + subl %esi,%edi + .align 16 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + subl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 4f +3: rep; smovl +4: andl $1,%eax / suffix count + jz 5f / no suffix + / copy suffix + addl $2,%esi + addl $2,%edi + movw (%esi),%dx + movw %dx,(%edi) +5: cld + popl %edi + popl %esi + ret + + / Support for void Copy::arrayof_conjoint_jshorts(void* from, + / void* to, + / size_t count) + .align 16 +_Copy_arrayof_conjoint_jshorts: + pushl %esi + movl 4+12(%esp),%ecx / count + pushl %edi + movl 8+ 4(%esp),%esi / from + movl 8+ 8(%esp),%edi / to + cmpl %esi,%edi + leal -2(%esi,%ecx,2),%eax / from + count*2 - 2 + jbe acs_CopyRight + cmpl %eax,%edi + jbe acs_CopyLeft +acs_CopyRight: + movl %ecx,%eax / word count + sarl %ecx / dword count + jz 4f / no dwords to move + cmpl $32,%ecx + jbe 2f / <= 32 dwords + / copy aligned dwords + rep; smovl + jmp 4f + / copy aligned dwords + .=.+5 +2: subl %esi,%edi + .align 16 +3: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 3b + addl %esi,%edi +4: andl $1,%eax / suffix count + jz 5f / no suffix + / copy suffix + movw (%esi),%dx + movw %dx,(%edi) +5: popl %edi + popl %esi + ret +acs_CopyLeft: + std + leal -4(%edi,%ecx,2),%edi / to + count*2 - 4 + movl %eax,%esi / from + count*2 - 2 + movl %ecx,%eax + subl $2,%esi / from + count*2 - 4 + sarl %ecx / dword count + jz 4f / no dwords to move + cmpl $32,%ecx + ja 3f / > 32 dwords + subl %esi,%edi + .align 16 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + subl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 4f +3: rep; smovl +4: andl $1,%eax / suffix count + jz 5f / no suffix + / copy suffix + addl $2,%esi + addl $2,%edi + movw (%esi),%dx + movw %dx,(%edi) +5: cld + popl %edi + popl %esi + ret + + / Support for void Copy::conjoint_jints_atomic(void* from, + / void* to, + / size_t count) + / Equivalent to + / arrayof_conjoint_jints + .align 16 +_Copy_conjoint_jints_atomic: +_Copy_arrayof_conjoint_jints: + pushl %esi + movl 4+12(%esp),%ecx / count + pushl %edi + movl 8+ 4(%esp),%esi / from + movl 8+ 8(%esp),%edi / to + cmpl %esi,%edi + leal -4(%esi,%ecx,4),%eax / from + count*4 - 4 + jbe ci_CopyRight + cmpl %eax,%edi + jbe ci_CopyLeft +ci_CopyRight: + cmpl $32,%ecx + jbe 2f / <= 32 dwords + rep; smovl + popl %edi + popl %esi + ret + .=.+10 +2: subl %esi,%edi + .align 16 +3: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 3b + popl %edi + popl %esi + ret +ci_CopyLeft: + std + leal -4(%edi,%ecx,4),%edi / to + count*4 - 4 + cmpl $32,%ecx + ja 3f / > 32 dwords + subl %eax,%edi / eax == from + count*4 - 4 + .align 16 +2: movl (%eax),%edx + movl %edx,(%edi,%eax,1) + subl $4,%eax + subl $1,%ecx + jnz 2b + cld + popl %edi + popl %esi + ret +3: movl %eax,%esi / from + count*4 - 4 + rep; smovl + cld + popl %edi + popl %esi + ret + + / Support for void Copy::conjoint_jlongs_atomic(jlong* from, + / jlong* to, + / size_t count) + / + / 32-bit + / + / count treated as signed + / + / if (from > to) { + / while (--count >= 0) { + / *to++ = *from++; + / } + / } else { + / while (--count >= 0) { + / to[count] = from[count]; + / } + / } + .align 16 +_Copy_conjoint_jlongs_atomic: + movl 4+8(%esp),%ecx / count + movl 4+0(%esp),%eax / from + movl 4+4(%esp),%edx / to + cmpl %eax,%edx + jae cla_CopyLeft +cla_CopyRight: + subl %eax,%edx + jmp 2f + .align 16 +1: fildll (%eax) + fistpll (%edx,%eax,1) + addl $8,%eax +2: subl $1,%ecx + jge 1b + ret + .align 16 +3: fildll (%eax,%ecx,8) + fistpll (%edx,%ecx,8) +cla_CopyLeft: + subl $1,%ecx + jge 3b + ret + + / Support for void Copy::arrayof_conjoint_jshorts(void* from, + / void* to, + / size_t count) + .align 16 +_mmx_Copy_arrayof_conjoint_jshorts: + pushl %esi + movl 4+12(%esp),%ecx + pushl %edi + movl 8+ 4(%esp),%esi + movl 8+ 8(%esp),%edi + cmpl %esi,%edi + leal -2(%esi,%ecx,2),%eax + jbe mmx_acs_CopyRight + cmpl %eax,%edi + jbe mmx_acs_CopyLeft +mmx_acs_CopyRight: + movl %ecx,%eax + sarl %ecx + je 5f + cmpl $33,%ecx + jae 3f +1: subl %esi,%edi + .align 16 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + addl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 5f +3: smovl / align to 8 bytes, we know we are 4 byte aligned to start + subl $1,%ecx +4: .align 16 + movq 0(%esi),%mm0 + addl $64,%edi + movq 8(%esi),%mm1 + subl $16,%ecx + movq 16(%esi),%mm2 + movq %mm0,-64(%edi) + movq 24(%esi),%mm0 + movq %mm1,-56(%edi) + movq 32(%esi),%mm1 + movq %mm2,-48(%edi) + movq 40(%esi),%mm2 + movq %mm0,-40(%edi) + movq 48(%esi),%mm0 + movq %mm1,-32(%edi) + movq 56(%esi),%mm1 + movq %mm2,-24(%edi) + movq %mm0,-16(%edi) + addl $64,%esi + movq %mm1,-8(%edi) + cmpl $16,%ecx + jge 4b + emms + testl %ecx,%ecx + ja 1b +5: andl $1,%eax + je 7f +6: movw (%esi),%dx + movw %dx,(%edi) +7: popl %edi + popl %esi + ret +mmx_acs_CopyLeft: + std + leal -4(%edi,%ecx,2),%edi + movl %eax,%esi + movl %ecx,%eax + subl $2,%esi + sarl %ecx + je 4f + cmpl $32,%ecx + ja 3f + subl %esi,%edi + .align 16 +2: movl (%esi),%edx + movl %edx,(%edi,%esi,1) + subl $4,%esi + subl $1,%ecx + jnz 2b + addl %esi,%edi + jmp 4f +3: rep; smovl +4: andl $1,%eax + je 6f + addl $2,%esi + addl $2,%edi +5: movw (%esi),%dx + movw %dx,(%edi) +6: cld + popl %edi + popl %esi + ret + + + / Support for jlong Atomic::cmpxchg(jlong exchange_value, + / volatile jlong* dest, + / jlong compare_value, + / bool is_MP) + / Used only for Solaris/gcc builds + .align 16 +_Atomic_cmpxchg_long_gcc: + / 8(%esp) : return PC + pushl %ebx / 4(%esp) : old %ebx + pushl %edi / 0(%esp) : old %edi + movl 12(%esp), %ebx / 12(%esp) : exchange_value (low) + movl 16(%esp), %ecx / 16(%esp) : exchange_value (high) + movl 24(%esp), %eax / 24(%esp) : compare_value (low) + movl 28(%esp), %edx / 28(%esp) : compare_value (high) + movl 20(%esp), %edi / 20(%esp) : dest + cmpl $0, 32(%esp) / 32(%esp) : is_MP + je 1f + lock +1: cmpxchg8b (%edi) + popl %edi + popl %ebx + ret diff --git a/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.ad b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.ad new file mode 100644 index 00000000000..34ebda4e3df --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.ad @@ -0,0 +1,186 @@ +// +// Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// AMD64 Solaris Architecture Description File + +//----------OS-DEPENDENT ENCODING BLOCK---------------------------------------- +// This block specifies the encoding classes used by the compiler to +// output byte streams. Encoding classes generate functions which are +// called by Machine Instruction Nodes in order to generate the bit +// encoding of the instruction. Operands specify their base encoding +// interface with the interface keyword. There are currently +// supported four interfaces, REG_INTER, CONST_INTER, MEMORY_INTER, & +// COND_INTER. REG_INTER causes an operand to generate a function +// which returns its register number when queried. CONST_INTER causes +// an operand to generate a function which returns the value of the +// constant when queried. MEMORY_INTER causes an operand to generate +// four functions which return the Base Register, the Index Register, +// the Scale Value, and the Offset Value of the operand when queried. +// COND_INTER causes an operand to generate six functions which return +// the encoding code (ie - encoding bits for the instruction) +// associated with each basic boolean condition for a conditional +// instruction. Instructions specify two basic values for encoding. +// They use the ins_encode keyword to specify their encoding class +// (which must be one of the class names specified in the encoding +// block), and they use the opcode keyword to specify, in order, their +// primary, secondary, and tertiary opcode. Only the opcode sections +// which a particular instruction needs for encoding need to be +// specified. +encode %{ + // Build emit functions for each basic byte or larger field in the intel + // encoding scheme (opcode, rm, sib, immediate), and call them from C++ + // code in the enc_class source block. Emit functions will live in the + // main source block for now. In future, we can generalize this by + // adding a syntax that specifies the sizes of fields in an order, + // so that the adlc can build the emit functions automagically + + enc_class Java_To_Runtime(method meth) + %{ + // No relocation needed + + // movq r10, + emit_opcode(cbuf, Assembler::REX_WB); + emit_opcode(cbuf, 0xB8 | (R10_enc - 8)); + emit_d64(cbuf, (int64_t) $meth$$method); + + // call (r10) + emit_opcode(cbuf, Assembler::REX_B); + emit_opcode(cbuf, 0xFF); + emit_opcode(cbuf, 0xD0 | (R10_enc - 8)); + %} + + enc_class solaris_breakpoint + %{ + MacroAssembler* masm = new MacroAssembler(&cbuf); + masm->call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); + %} + + enc_class call_epilog + %{ + if (VerifyStackAtCalls) { + // Check that stack depth is unchanged: find majik cookie on stack + int framesize = + ra_->reg2offset_unchecked(OptoReg::add(ra_->_matcher._old_SP, -3*VMRegImpl::slots_per_word)); + if (framesize) { + if (framesize < 0x80) { + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x81); // cmpq [rsp+0],0xbadb1ood + emit_d8(cbuf, 0x7C); + emit_d8(cbuf, 0x24); + emit_d8(cbuf, framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } else { + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x81); // cmpq [rsp+0],0xbadb1ood + emit_d8(cbuf, 0xBC); + emit_d8(cbuf, 0x24); + emit_d32(cbuf, framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } + } + // jmp EQ around INT3 + // QQQ TODO + const int jump_around = 5; // size of call to breakpoint, 1 for CC + emit_opcode(cbuf, 0x74); + emit_d8(cbuf, jump_around); + // QQQ temporary + emit_break(cbuf); + // Die if stack mismatch + // emit_opcode(cbuf,0xCC); + } + %} + + enc_class post_call_verify_mxcsr %{ + MacroAssembler masm(&cbuf); + if (RestoreMXCSROnJNICalls) { + masm.ldmxcsr(ExternalAddress(StubRoutines::amd64::mxcsr_std())); + } + else if (CheckJNICalls) { + masm.call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::amd64::verify_mxcsr_entry()))); + } + %} +%} + +// INSTRUCTIONS -- Platform dependent + +//----------OS and Locking Instructions---------------------------------------- + +// This name is KNOWN by the ADLC and cannot be changed. +// The ADLC forces a 'TypeRawPtr::BOTTOM' output type +// for this guy. +instruct tlsLoadP(r15_RegP dst) +%{ + match(Set dst (ThreadLocal)); + effect(DEF dst); + + size(0); + format %{ "# TLS is in R15" %} + ins_encode( /*empty encoding*/ ); + ins_pipe(ialu_reg_reg); +%} + +// Die now +instruct ShouldNotReachHere() +%{ + match(Halt); + + // Use the following format syntax + format %{ "int3\t# ShouldNotReachHere" %} + // QQQ TODO for now call breakpoint + // opcode(0xCC); + // ins_encode(Opc); + ins_encode(solaris_breakpoint); + ins_pipe(pipe_slow); +%} + + +// Platform dependent source + +source +%{ + +int MachCallRuntimeNode::ret_addr_offset() +{ + return 13; // movq r10,#addr; callq (r10) +} + +// emit an interrupt that is caught by the debugger +void emit_break(CodeBuffer& cbuf) +{ + // Debugger doesn't really catch this but best we can do so far QQQ + MacroAssembler* masm = new MacroAssembler(&cbuf); + masm->call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); +} + +void MachBreakpointNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const +{ + emit_break(cbuf); +} + +uint MachBreakpointNode::size(PhaseRegAlloc* ra_) const +{ + return 5; +} + +%} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.il b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.il new file mode 100644 index 00000000000..89ed2c87b68 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.il @@ -0,0 +1,136 @@ +// +// Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + + // The argument size of each inline directive is ignored by the compiler + // and is set to the number of arguments as documentation. + + // Get the raw thread ID from %gs:0 + .inline _raw_thread_id,0 + movq %fs:0, %rax + .end + + // Get the frame pointer from previous frame. + .inline _get_previous_fp,0 + movq %rbp, %rax + movq %rax, %rax + .end + + // Support for jint Atomic::add(jint add_value, volatile jint* dest) + // An additional bool (os::is_MP()) is passed as the last argument. + .inline _Atomic_add,3 + movl %edi, %eax // save add_value for return + testl %edx, %edx // MP test + je 1f + lock +1: xaddl %edi, (%rsi) + addl %edi, %eax + .end + + // Support for jlong Atomic::add(jlong add_value, volatile jlong* dest) + // An additional bool (os::is_MP()) is passed as the last argument. + .inline _Atomic_add_long,3 + movq %rdi, %rax // save add_value for return + testq %rdx, %rdx // MP test + je 1f + lock +1: xaddq %rdi, (%rsi) + addq %rdi, %rax + .end + + // Support for jint Atomic::xchg(jint exchange_value, volatile jint* dest). + .inline _Atomic_xchg,2 + xchgl (%rsi), %edi + movl %edi, %eax + .end + + // Support for jlong Atomic::xchg(jlong exchange_value, volatile jlong* dest). + .inline _Atomic_xchg_long,2 + xchgq (%rsi), %rdi + movq %rdi, %rax + .end + + // Support for jint Atomic::cmpxchg(jint exchange_value, + // volatile jint *dest, + // jint compare_value) + // An additional bool (os::is_MP()) is passed as the last argument. + .inline _Atomic_cmpxchg,4 + movl %edx, %eax // compare_value + testl %ecx, %ecx // MP test + je 1f + lock +1: cmpxchgl %edi, (%rsi) + .end + + // Support for jlong Atomic::cmpxchg(jlong exchange_value, + // volatile jlong* dest, + // jlong compare_value) + // An additional bool (os::is_MP()) is passed as the last argument. + .inline _Atomic_cmpxchg_long,6 + movq %rdx, %rax // compare_value + testq %rcx, %rcx // MP test + je 1f + lock +1: cmpxchgq %rdi, (%rsi) + .end + + // Support for OrderAccess::acquire() + .inline _OrderAccess_acquire,0 + movl 0(%rsp), %eax + .end + + // Support for OrderAccess::fence() + .inline _OrderAccess_fence,0 + lock + addl $0, (%rsp) + .end + + // Support for u2 Bytes::swap_u2(u2 x) + .inline _raw_swap_u2,1 + movw %di, %ax + rorw $8, %ax + .end + + // Support for u4 Bytes::swap_u4(u4 x) + .inline _raw_swap_u4,1 + movl %edi, %eax + bswapl %eax + .end + + // Support for u8 Bytes::swap_u8(u8 x) + .inline _raw_swap_u8,1 + movq %rdi, %rax + bswapq %rax + .end + + // Support for void Prefetch::read + .inline _Prefetch_read,2 + prefetcht0 (%rdi, %rsi, 1) + .end + + // Support for void Prefetch::write + // We use prefetcht0 because em64t doesn't support prefetchw. + // prefetchw is a 3dnow instruction. + .inline _Prefetch_write,2 + prefetcht0 (%rdi, %rsi, 1) + .end diff --git a/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.s b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.s new file mode 100644 index 00000000000..5c737d0b995 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.s @@ -0,0 +1,406 @@ +/ +/ Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. +/ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +/ +/ This code is free software; you can redistribute it and/or modify it +/ under the terms of the GNU General Public License version 2 only, as +/ published by the Free Software Foundation. +/ +/ This code is distributed in the hope that it will be useful, but WITHOUT +/ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +/ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +/ version 2 for more details (a copy is included in the LICENSE file that +/ accompanied this code). +/ +/ You should have received a copy of the GNU General Public License version +/ 2 along with this work; if not, write to the Free Software Foundation, +/ Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +/ +/ Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +/ CA 95054 USA or visit www.sun.com if you need additional information or +/ have any questions. +/ + + .globl fs_load + .globl fs_thread + + // NOTE WELL! The _Copy functions are called directly + // from server-compiler-generated code via CallLeafNoFP, + // which means that they *must* either not use floating + // point or use it in the same manner as does the server + // compiler. + + .globl _Copy_arrayof_conjoint_bytes + .globl _Copy_conjoint_jshorts_atomic + .globl _Copy_arrayof_conjoint_jshorts + .globl _Copy_conjoint_jints_atomic + .globl _Copy_arrayof_conjoint_jints + .globl _Copy_conjoint_jlongs_atomic + .globl _Copy_arrayof_conjoint_jlongs + + .section .text,"ax" + + / Fast thread accessors, used by threadLS_solaris_amd64.cpp + .align 16 +fs_load: + movq %fs:(%rdi),%rax + ret + + .align 16 +fs_thread: + movq %fs:0x0,%rax + ret + + .globl SafeFetch32, Fetch32PFI, Fetch32Resume + .align 16 + // Prototype: int SafeFetch32 (int * Adr, int ErrValue) +SafeFetch32: + movl %esi, %eax +Fetch32PFI: + movl (%rdi), %eax +Fetch32Resume: + ret + + .globl SafeFetchN, FetchNPFI, FetchNResume + .align 16 + // Prototype: intptr_t SafeFetchN (intptr_t * Adr, intptr_t ErrValue) +SafeFetchN: + movq %rsi, %rax +FetchNPFI: + movq (%rdi), %rax +FetchNResume: + ret + + .globl SpinPause + .align 16 +SpinPause: + rep + nop + movq $1, %rax + ret + + + / Support for void Copy::arrayof_conjoint_bytes(void* from, + / void* to, + / size_t count) + / rdi - from + / rsi - to + / rdx - count, treated as ssize_t + / + .align 16 +_Copy_arrayof_conjoint_bytes: + movq %rdx,%r8 / byte count + shrq $3,%rdx / qword count + cmpq %rdi,%rsi + leaq -1(%rdi,%r8,1),%rax / from + bcount*1 - 1 + jbe acb_CopyRight + cmpq %rax,%rsi + jbe acb_CopyLeft +acb_CopyRight: + leaq -8(%rdi,%rdx,8),%rax / from + qcount*8 - 8 + leaq -8(%rsi,%rdx,8),%rcx / to + qcount*8 - 8 + negq %rdx + jmp 7f + .align 16 +1: movq 8(%rax,%rdx,8),%rsi + movq %rsi,8(%rcx,%rdx,8) + addq $1,%rdx + jnz 1b +2: testq $4,%r8 / check for trailing dword + jz 3f + movl 8(%rax),%esi / copy trailing dword + movl %esi,8(%rcx) + addq $4,%rax + addq $4,%rcx / original %rsi is trashed, so we + / can't use it as a base register +3: testq $2,%r8 / check for trailing word + jz 4f + movw 8(%rax),%si / copy trailing word + movw %si,8(%rcx) + addq $2,%rcx +4: testq $1,%r8 / check for trailing byte + jz 5f + movb -1(%rdi,%r8,1),%al / copy trailing byte + movb %al,8(%rcx) +5: ret + .align 16 +6: movq -24(%rax,%rdx,8),%rsi + movq %rsi,-24(%rcx,%rdx,8) + movq -16(%rax,%rdx,8),%rsi + movq %rsi,-16(%rcx,%rdx,8) + movq -8(%rax,%rdx,8),%rsi + movq %rsi,-8(%rcx,%rdx,8) + movq (%rax,%rdx,8),%rsi + movq %rsi,(%rcx,%rdx,8) +7: addq $4,%rdx + jle 6b + subq $4,%rdx + jl 1b + jmp 2b +acb_CopyLeft: + testq $1,%r8 / check for trailing byte + jz 1f + movb -1(%rdi,%r8,1),%cl / copy trailing byte + movb %cl,-1(%rsi,%r8,1) + subq $1,%r8 / adjust for possible trailing word +1: testq $2,%r8 / check for trailing word + jz 2f + movw -2(%rdi,%r8,1),%cx / copy trailing word + movw %cx,-2(%rsi,%r8,1) +2: testq $4,%r8 / check for trailing dword + jz 5f + movl (%rdi,%rdx,8),%ecx / copy trailing dword + movl %ecx,(%rsi,%rdx,8) + jmp 5f + .align 16 +3: movq -8(%rdi,%rdx,8),%rcx + movq %rcx,-8(%rsi,%rdx,8) + subq $1,%rdx + jnz 3b + ret + .align 16 +4: movq 24(%rdi,%rdx,8),%rcx + movq %rcx,24(%rsi,%rdx,8) + movq 16(%rdi,%rdx,8),%rcx + movq %rcx,16(%rsi,%rdx,8) + movq 8(%rdi,%rdx,8),%rcx + movq %rcx,8(%rsi,%rdx,8) + movq (%rdi,%rdx,8),%rcx + movq %rcx,(%rsi,%rdx,8) +5: subq $4,%rdx + jge 4b + addq $4,%rdx + jg 3b + ret + + / Support for void Copy::arrayof_conjoint_jshorts(void* from, + / void* to, + / size_t count) + / Equivalent to + / conjoint_jshorts_atomic + / + / If 'from' and/or 'to' are aligned on 4- or 2-byte boundaries, we + / let the hardware handle it. The tow or four words within dwords + / or qwords that span cache line boundaries will still be loaded + / and stored atomically. + / + / rdi - from + / rsi - to + / rdx - count, treated as ssize_t + / + .align 16 +_Copy_arrayof_conjoint_jshorts: +_Copy_conjoint_jshorts_atomic: + movq %rdx,%r8 / word count + shrq $2,%rdx / qword count + cmpq %rdi,%rsi + leaq -2(%rdi,%r8,2),%rax / from + wcount*2 - 2 + jbe acs_CopyRight + cmpq %rax,%rsi + jbe acs_CopyLeft +acs_CopyRight: + leaq -8(%rdi,%rdx,8),%rax / from + qcount*8 - 8 + leaq -8(%rsi,%rdx,8),%rcx / to + qcount*8 - 8 + negq %rdx + jmp 6f +1: movq 8(%rax,%rdx,8),%rsi + movq %rsi,8(%rcx,%rdx,8) + addq $1,%rdx + jnz 1b +2: testq $2,%r8 / check for trailing dword + jz 3f + movl 8(%rax),%esi / copy trailing dword + movl %esi,8(%rcx) + addq $4,%rcx / original %rsi is trashed, so we + / can't use it as a base register +3: testq $1,%r8 / check for trailing word + jz 4f + movw -2(%rdi,%r8,2),%si / copy trailing word + movw %si,8(%rcx) +4: ret + .align 16 +5: movq -24(%rax,%rdx,8),%rsi + movq %rsi,-24(%rcx,%rdx,8) + movq -16(%rax,%rdx,8),%rsi + movq %rsi,-16(%rcx,%rdx,8) + movq -8(%rax,%rdx,8),%rsi + movq %rsi,-8(%rcx,%rdx,8) + movq (%rax,%rdx,8),%rsi + movq %rsi,(%rcx,%rdx,8) +6: addq $4,%rdx + jle 5b + subq $4,%rdx + jl 1b + jmp 2b +acs_CopyLeft: + testq $1,%r8 / check for trailing word + jz 1f + movw -2(%rdi,%r8,2),%cx / copy trailing word + movw %cx,-2(%rsi,%r8,2) +1: testq $2,%r8 / check for trailing dword + jz 4f + movl (%rdi,%rdx,8),%ecx / copy trailing dword + movl %ecx,(%rsi,%rdx,8) + jmp 4f +2: movq -8(%rdi,%rdx,8),%rcx + movq %rcx,-8(%rsi,%rdx,8) + subq $1,%rdx + jnz 2b + ret + .align 16 +3: movq 24(%rdi,%rdx,8),%rcx + movq %rcx,24(%rsi,%rdx,8) + movq 16(%rdi,%rdx,8),%rcx + movq %rcx,16(%rsi,%rdx,8) + movq 8(%rdi,%rdx,8),%rcx + movq %rcx,8(%rsi,%rdx,8) + movq (%rdi,%rdx,8),%rcx + movq %rcx,(%rsi,%rdx,8) +4: subq $4,%rdx + jge 3b + addq $4,%rdx + jg 2b + ret + + / Support for void Copy::arrayof_conjoint_jints(jint* from, + / jint* to, + / size_t count) + / Equivalent to + / conjoint_jints_atomic + / + / If 'from' and/or 'to' are aligned on 4-byte boundaries, we let + / the hardware handle it. The two dwords within qwords that span + / cache line boundaries will still be loaded and stored atomically. + / + / rdi - from + / rsi - to + / rdx - count, treated as ssize_t + / + .align 16 +_Copy_arrayof_conjoint_jints: +_Copy_conjoint_jints_atomic: + movq %rdx,%r8 / dword count + shrq %rdx / qword count + cmpq %rdi,%rsi + leaq -4(%rdi,%r8,4),%rax / from + dcount*4 - 4 + jbe aci_CopyRight + cmpq %rax,%rsi + jbe aci_CopyLeft +aci_CopyRight: + leaq -8(%rdi,%rdx,8),%rax / from + qcount*8 - 8 + leaq -8(%rsi,%rdx,8),%rcx / to + qcount*8 - 8 + negq %rdx + jmp 5f + .align 16 +1: movq 8(%rax,%rdx,8),%rsi + movq %rsi,8(%rcx,%rdx,8) + addq $1,%rdx + jnz 1b +2: testq $1,%r8 / check for trailing dword + jz 3f + movl 8(%rax),%esi / copy trailing dword + movl %esi,8(%rcx) +3: ret + .align 16 +4: movq -24(%rax,%rdx,8),%rsi + movq %rsi,-24(%rcx,%rdx,8) + movq -16(%rax,%rdx,8),%rsi + movq %rsi,-16(%rcx,%rdx,8) + movq -8(%rax,%rdx,8),%rsi + movq %rsi,-8(%rcx,%rdx,8) + movq (%rax,%rdx,8),%rsi + movq %rsi,(%rcx,%rdx,8) +5: addq $4,%rdx + jle 4b + subq $4,%rdx + jl 1b + jmp 2b +aci_CopyLeft: + testq $1,%r8 / check for trailing dword + jz 3f + movl -4(%rdi,%r8,4),%ecx / copy trailing dword + movl %ecx,-4(%rsi,%r8,4) + jmp 3f +1: movq -8(%rdi,%rdx,8),%rcx + movq %rcx,-8(%rsi,%rdx,8) + subq $1,%rdx + jnz 1b + ret + .align 16 +2: movq 24(%rdi,%rdx,8),%rcx + movq %rcx,24(%rsi,%rdx,8) + movq 16(%rdi,%rdx,8),%rcx + movq %rcx,16(%rsi,%rdx,8) + movq 8(%rdi,%rdx,8),%rcx + movq %rcx,8(%rsi,%rdx,8) + movq (%rdi,%rdx,8),%rcx + movq %rcx,(%rsi,%rdx,8) +3: subq $4,%rdx + jge 2b + addq $4,%rdx + jg 1b + ret + + / Support for void Copy::arrayof_conjoint_jlongs(jlong* from, + / jlong* to, + / size_t count) + / Equivalent to + / conjoint_jlongs_atomic + / arrayof_conjoint_oops + / conjoint_oops_atomic + / + / rdi - from + / rsi - to + / rdx - count, treated as ssize_t + / + .align 16 +_Copy_arrayof_conjoint_jlongs: +_Copy_conjoint_jlongs_atomic: + cmpq %rdi,%rsi + leaq -8(%rdi,%rdx,8),%rax / from + count*8 - 8 + jbe acl_CopyRight + cmpq %rax,%rsi + jbe acl_CopyLeft +acl_CopyRight: + leaq -8(%rsi,%rdx,8),%rcx / to + count*8 - 8 + negq %rdx + jmp 3f +1: movq 8(%rax,%rdx,8),%rsi + movq %rsi,8(%rcx,%rdx,8) + addq $1,%rdx + jnz 1b + ret + .align 16 +2: movq -24(%rax,%rdx,8),%rsi + movq %rsi,-24(%rcx,%rdx,8) + movq -16(%rax,%rdx,8),%rsi + movq %rsi,-16(%rcx,%rdx,8) + movq -8(%rax,%rdx,8),%rsi + movq %rsi,-8(%rcx,%rdx,8) + movq (%rax,%rdx,8),%rsi + movq %rsi,(%rcx,%rdx,8) +3: addq $4,%rdx + jle 2b + subq $4,%rdx + jl 1b + ret +4: movq -8(%rdi,%rdx,8),%rcx + movq %rcx,-8(%rsi,%rdx,8) + subq $1,%rdx + jnz 4b + ret + .align 16 +5: movq 24(%rdi,%rdx,8),%rcx + movq %rcx,24(%rsi,%rdx,8) + movq 16(%rdi,%rdx,8),%rcx + movq %rcx,16(%rsi,%rdx,8) + movq 8(%rdi,%rdx,8),%rcx + movq %rcx,8(%rsi,%rdx,8) + movq (%rdi,%rdx,8),%rcx + movq %rcx,(%rsi,%rdx,8) +acl_CopyLeft: + subq $4,%rdx + jge 5b + addq $4,%rdx + jg 4b + ret diff --git a/hotspot/src/os_cpu/solaris_x86/vm/threadLS_solaris_x86.cpp b/hotspot/src/os_cpu/solaris_x86/vm/threadLS_solaris_x86.cpp new file mode 100644 index 00000000000..f28d960674a --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/threadLS_solaris_x86.cpp @@ -0,0 +1,191 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_threadLS_solaris_x86.cpp.incl" + +#ifdef AMD64 +extern "C" Thread* fs_load(ptrdiff_t tlsOffset); +extern "C" intptr_t fs_thread(); +#else +// From solaris_i486.s +extern "C" Thread* gs_load(ptrdiff_t tlsOffset); +extern "C" intptr_t gs_thread(); +#endif // AMD64 + +// tlsMode encoding: +// +// pd_tlsAccessUndefined : uninitialized +// pd_tlsAccessSlow : not available +// pd_tlsAccessIndirect : +// old-style indirect access - present in "T1" libthread. +// use thr_slot_sync_allocate() to attempt to allocate a slot. +// pd_tlsAccessDirect : +// new-style direct access - present in late-model "T2" libthread. +// Allocate the offset (slot) via _thr_slot_offset() or by +// defining an IE- or LE-mode TLS/TSD slot in the launcher and then passing +// that offset into libjvm.so. +// See http://sac.eng/Archives/CaseLog/arc/PSARC/2003/159/. +// +// Note that we have a capability gap - some early model T2 forms +// (e.g., unpatched S9) have neither _thr_slot_sync_allocate() nor +// _thr_slot_offset(). In that case we revert to the usual +// thr_getspecific accessor. +// + +static ThreadLocalStorage::pd_tlsAccessMode tlsMode = ThreadLocalStorage::pd_tlsAccessUndefined ; +static ptrdiff_t tlsOffset = 0 ; +static thread_key_t tlsKey ; + +typedef int (*TSSA_Entry) (ptrdiff_t *, int, int) ; +typedef ptrdiff_t (*TSO_Entry) (int) ; + +ThreadLocalStorage::pd_tlsAccessMode ThreadLocalStorage::pd_getTlsAccessMode () +{ + guarantee (tlsMode != pd_tlsAccessUndefined, "tlsMode not set") ; + return tlsMode ; +} + +ptrdiff_t ThreadLocalStorage::pd_getTlsOffset () { + guarantee (tlsMode != pd_tlsAccessUndefined, "tlsMode not set") ; + return tlsOffset ; +} + +// TODO: Consider the following improvements: +// +// 1. Convert from thr_*specific* to pthread_*specific*. +// The pthread_ forms are slightly faster. Also, the +// pthread_ forms have a pthread_key_delete() API which +// would aid in clean JVM shutdown and the eventual goal +// of permitting a JVM to reinstantiate itself withing a process. +// +// 2. See ThreadLocalStorage::init(). We end up allocating +// two TLS keys during VM startup. That's benign, but we could collapse +// down to one key without too much trouble. +// +// 3. MacroAssembler::get_thread() currently emits calls to thr_getspecific(). +// Modify get_thread() to call Thread::current() instead. +// +// 4. Thread::current() currently uses a cache keyed by %gs:[0]. +// (The JVM has PSARC permission to use %g7/%gs:[0] +// as an opaque temporally unique thread identifier). +// For C++ access to a thread's reflexive "self" pointer we +// should consider using one of the following: +// a. a radix tree keyed by %esp - as in EVM. +// This requires two loads (the 2nd dependent on the 1st), but +// is easily inlined and doesn't require a "miss" slow path. +// b. a fast TLS/TSD slot allocated by _thr_slot_offset +// or _thr_slot_sync_allocate. +// +// 5. 'generate_code_for_get_thread' is a misnomer. +// We should change it to something more general like +// pd_ThreadSelf_Init(), for instance. +// + +static void AllocateTLSOffset () +{ + int rslt ; + TSSA_Entry tssa ; + TSO_Entry tso ; + ptrdiff_t off ; + + guarantee (tlsMode == ThreadLocalStorage::pd_tlsAccessUndefined, "tlsMode not set") ; + tlsMode = ThreadLocalStorage::pd_tlsAccessSlow ; + tlsOffset = 0 ; +#ifndef AMD64 + + tssa = (TSSA_Entry) dlsym (RTLD_DEFAULT, "thr_slot_sync_allocate") ; + if (tssa != NULL) { + off = -1 ; + rslt = (*tssa)(&off, NULL, NULL) ; // (off,dtor,darg) + if (off != -1) { + tlsOffset = off ; + tlsMode = ThreadLocalStorage::pd_tlsAccessIndirect ; + return ; + } + } + + rslt = thr_keycreate (&tlsKey, NULL) ; + if (rslt != 0) { + tlsMode = ThreadLocalStorage::pd_tlsAccessSlow ; // revert to slow mode + return ; + } + + tso = (TSO_Entry) dlsym (RTLD_DEFAULT, "_thr_slot_offset") ; + if (tso != NULL) { + off = (*tso)(tlsKey) ; + if (off >= 0) { + tlsOffset = off ; + tlsMode = ThreadLocalStorage::pd_tlsAccessDirect ; + return ; + } + } + + // Failure: Too bad ... we've allocated a TLS slot we don't need and there's + // no provision in the ABI for returning the slot. + // + // If we didn't find a slot then then: + // 1. We might be on liblwp. + // 2. We might be on T2 libthread, but all "fast" slots are already + // consumed + // 3. We might be on T1, and all TSD (thr_slot_sync_allocate) slots are + // consumed. + // 4. We might be on T2 libthread, but it's be re-architected + // so that fast slots are no longer g7-relative. + // + + tlsMode = ThreadLocalStorage::pd_tlsAccessSlow ; + return ; +#endif // AMD64 +} + +void ThreadLocalStorage::generate_code_for_get_thread() { + AllocateTLSOffset() ; +} + +void ThreadLocalStorage::set_thread_in_slot(Thread *thread) { + guarantee (tlsMode != pd_tlsAccessUndefined, "tlsMode not set") ; + if (tlsMode == pd_tlsAccessIndirect) { +#ifdef AMD64 + intptr_t tbase = fs_thread(); +#else + intptr_t tbase = gs_thread(); +#endif // AMD64 + *((Thread**) (tbase + tlsOffset)) = thread ; + } else + if (tlsMode == pd_tlsAccessDirect) { + thr_setspecific (tlsKey, (void *) thread) ; + // set with thr_setspecific and then readback with gs_load to validate. +#ifdef AMD64 + guarantee (thread == fs_load(tlsOffset), "tls readback failure") ; +#else + guarantee (thread == gs_load(tlsOffset), "tls readback failure") ; +#endif // AMD64 + } +} + + +extern "C" Thread* get_thread() { + return ThreadLocalStorage::thread(); +} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/threadLS_solaris_x86.hpp b/hotspot/src/os_cpu/solaris_x86/vm/threadLS_solaris_x86.hpp new file mode 100644 index 00000000000..4495f98cf4c --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/threadLS_solaris_x86.hpp @@ -0,0 +1,80 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Processor dependent parts of ThreadLocalStorage + +private: + static Thread* _get_thread_cache[]; // index by [(raw_id>>9)^(raw_id>>20) % _pd_cache_size] + static Thread* get_thread_via_cache_slowly(uintptr_t raw_id, int index); + + NOT_PRODUCT(static int _tcacheHit;) + NOT_PRODUCT(static int _tcacheMiss;) + +public: + // Cache hit/miss statistics + static void print_statistics() PRODUCT_RETURN; + + enum Constants { +#ifdef AMD64 + _pd_cache_size = 256*2 // projected typical # of threads * 2 +#else + _pd_cache_size = 128*2 // projected typical # of threads * 2 +#endif // AMD64 + }; + + enum pd_tlsAccessMode { + pd_tlsAccessUndefined = -1, + pd_tlsAccessSlow = 0, + pd_tlsAccessIndirect = 1, + pd_tlsAccessDirect = 2 + } ; + + static void set_thread_in_slot (Thread *) ; + + static pd_tlsAccessMode pd_getTlsAccessMode () ; + static ptrdiff_t pd_getTlsOffset () ; + + static uintptr_t pd_raw_thread_id() { +#ifdef _GNU_SOURCE +#ifdef AMD64 + uintptr_t rv; + __asm__ __volatile__ ("movq %%fs:0, %0" : "=r"(rv)); + return rv; +#else + return gs_thread(); +#endif // AMD64 +#else //_GNU_SOURCE + return _raw_thread_id(); +#endif //_GNU_SOURCE + } + + static int pd_cache_index(uintptr_t raw_id) { + // Copied from the sparc version. Dave said it should also work fine + // for solx86. + int ix = (int) (((raw_id >> 9) ^ (raw_id >> 20)) % _pd_cache_size); + return ix; + } + + // Java Thread + static inline Thread* thread(); diff --git a/hotspot/src/os_cpu/solaris_x86/vm/thread_solaris_x86.cpp b/hotspot/src/os_cpu/solaris_x86/vm/thread_solaris_x86.cpp new file mode 100644 index 00000000000..eab73273efa --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/thread_solaris_x86.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_thread_solaris_x86.cpp.incl" + +// For Forte Analyzer AsyncGetCallTrace profiling support - thread is +// currently interrupted by SIGPROF +bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, + void* ucontext, bool isInJava) { + + assert(Thread::current() == this, "caller must be current thread"); + assert(this->is_Java_thread(), "must be JavaThread"); + + JavaThread* jt = (JavaThread *)this; + + // If we have a last_Java_frame, then we should use it even if + // isInJava == true. It should be more reliable than ucontext info. + if (jt->has_last_Java_frame()) { + *fr_addr = jt->pd_last_frame(); + return true; + } + + // At this point, we don't have a last_Java_frame, so + // we try to glean some information out of the ucontext + // if we were running Java code when SIGPROF came in. + if (isInJava) { + ucontext_t* uc = (ucontext_t*) ucontext; + + intptr_t* ret_fp; + intptr_t* ret_sp; + ExtendedPC addr = os::Solaris::fetch_frame_from_ucontext(this, uc, + &ret_sp, &ret_fp); + if (addr.pc() == NULL || ret_sp == NULL ) { + // ucontext wasn't useful + return false; + } + + frame ret_frame(ret_sp, ret_fp, addr.pc()); + if (!ret_frame.safe_for_sender(jt)) { +#ifdef COMPILER2 + frame ret_frame2(ret_sp, NULL, addr.pc()); + if (!ret_frame2.safe_for_sender(jt)) { + // nothing else to try if the frame isn't good + return false; + } + ret_frame = ret_frame2; +#else + // nothing else to try if the frame isn't good + return false; +#endif /* COMPILER2 */ + } + *fr_addr = ret_frame; + return true; + } + + // nothing else to try + return false; +} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/thread_solaris_x86.hpp b/hotspot/src/os_cpu/solaris_x86/vm/thread_solaris_x86.hpp new file mode 100644 index 00000000000..647ff46b81d --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/thread_solaris_x86.hpp @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: + void pd_initialize() { _anchor.clear(); } + + frame pd_last_frame() { + assert(has_last_Java_frame(), "must have last_Java_sp() when suspended"); + if (_anchor.last_Java_pc() != NULL) { + return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp(), _anchor.last_Java_pc()); + } else { + // This will pick up pc from sp + return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp()); + } + } + + public: + // Mutators are highly dangerous.... + intptr_t* last_Java_fp() { return _anchor.last_Java_fp(); } + void set_last_Java_fp(intptr_t* fp) { _anchor.set_last_Java_fp(fp); } + + void set_base_of_stack_pointer(intptr_t* base_sp) {} + + static ByteSize last_Java_fp_offset() { + return byte_offset_of(JavaThread, _anchor) + JavaFrameAnchor::last_Java_fp_offset(); + } + + intptr_t* base_of_stack_pointer() { return NULL; } + void record_base_of_stack_pointer() {} + + bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, + bool isInJava); + + // These routines are only used on cpu architectures that + // have separate register stacks (Itanium). + static bool register_stack_overflow() { return false; } + static void enable_register_stack_guard() {} + static void disable_register_stack_guard() {} diff --git a/hotspot/src/os_cpu/solaris_x86/vm/vmStructs_solaris_x86.hpp b/hotspot/src/os_cpu/solaris_x86/vm/vmStructs_solaris_x86.hpp new file mode 100644 index 00000000000..fcc2911b467 --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/vmStructs_solaris_x86.hpp @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// These are the OS and CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + \ + nonstatic_field(OSThread, _thread_id, thread_t) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ + \ + /**********************/ \ + /* Solaris Thread IDs */ \ + /**********************/ \ + \ + declare_unsigned_integer_type(thread_t) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() diff --git a/hotspot/src/os_cpu/solaris_x86/vm/vm_version_solaris_x86.cpp b/hotspot/src/os_cpu/solaris_x86/vm/vm_version_solaris_x86.cpp new file mode 100644 index 00000000000..4bf58d2505c --- /dev/null +++ b/hotspot/src/os_cpu/solaris_x86/vm/vm_version_solaris_x86.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vm_version_solaris_x86.cpp.incl" diff --git a/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_32.cpp b/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_32.cpp new file mode 100644 index 00000000000..dd90d704fea --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_32.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler_windows_x86_32.cpp.incl" + + +void MacroAssembler::int3() { + emit_byte(0xCC); +} + +// The current scheme to accelerate access to the thread +// pointer is to store the current thread in the os_exception_wrapper +// and reference the current thread from stubs and compiled code +// via the FS register. FS[0] contains a pointer to the structured +// exception block which is actually a stack address. The first time +// we call the os exception wrapper, we calculate and store the +// offset from this exception block and use that offset here. +// +// The last mechanism we used was problematic in that the +// the offset we had hard coded in the VM kept changing as Microsoft +// evolved the OS. +// +// Warning: This mechanism assumes that we only attempt to get the +// thread when we are nested below a call wrapper. +// +// movl reg, fs:[0] Get exeception pointer +// movl reg, [reg + thread_ptr_offset] Load thread +// +void MacroAssembler::get_thread(Register thread) { + // can't use ExternalAddress because it can't take NULL + AddressLiteral null(0, relocInfo::none); + + prefix(FS_segment); + movptr(thread, null); + assert(ThreadLocalStorage::get_thread_ptr_offset() != 0, + "Thread Pointer Offset has not been initialized"); + movl(thread, Address(thread, ThreadLocalStorage::get_thread_ptr_offset())); +} + +bool MacroAssembler::needs_explicit_null_check(int offset) { + return offset < 0 || (int)os::vm_page_size() <= offset; +} diff --git a/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_64.cpp b/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_64.cpp new file mode 100644 index 00000000000..c0211d0afd8 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/assembler_windows_x86_64.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler_windows_x86_64.cpp.incl" + + +void MacroAssembler::int3() { + emit_byte(0xCC); +} + +// call (Thread*)TlsGetValue(thread_index()); +void MacroAssembler::get_thread(Register thread) { + if (thread != rax) { + pushq(rax); + } + pushq(rdi); + pushq(rsi); + pushq(rdx); + pushq(rcx); + pushq(r8); + pushq(r9); + pushq(r10); + // XXX + movq(r10, rsp); + andq(rsp, -16); + pushq(r10); + pushq(r11); + + movl(c_rarg0, ThreadLocalStorage::thread_index()); + call(RuntimeAddress((address)TlsGetValue)); + + popq(r11); + popq(rsp); + popq(r10); + popq(r9); + popq(r8); + popq(rcx); + popq(rdx); + popq(rsi); + popq(rdi); + if (thread != rax) { + movq(thread, rax); + popq(rax); + } +} + +bool MacroAssembler::needs_explicit_null_check(int offset) { + return offset < 0 || (int)os::vm_page_size() <= offset; +} diff --git a/hotspot/src/os_cpu/windows_x86/vm/atomic_windows_x86.inline.hpp b/hotspot/src/os_cpu/windows_x86/vm/atomic_windows_x86.inline.hpp new file mode 100644 index 00000000000..cb85f306b3c --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/atomic_windows_x86.inline.hpp @@ -0,0 +1,251 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The following alternative implementations are needed because +// Windows 95 doesn't support (some of) the corresponding Windows NT +// calls. Furthermore, these versions allow inlining in the caller. +// (More precisely: The documentation for InterlockedExchange says +// it is supported for Windows 95. However, when single-stepping +// through the assembly code we cannot step into the routine and +// when looking at the routine address we see only garbage code. +// Better safe then sorry!). Was bug 7/31/98 (gri). +// +// Performance note: On uniprocessors, the 'lock' prefixes are not +// necessary (and expensive). We should generate separate cases if +// this becomes a performance problem. + +#pragma warning(disable: 4035) // Disables warnings reporting missing return statement + +inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, jint* dest) { *dest = store_value; } + +inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, void* dest) { *(void**)dest = store_value; } + +inline void Atomic::store (jbyte store_value, volatile jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, volatile jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, volatile jint* dest) { *dest = store_value; } + + +inline void Atomic::store_ptr(intptr_t store_value, volatile intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, volatile void* dest) { *(void* volatile *)dest = store_value; } + +// Adding a lock prefix to an instruction on MP machine +// VC++ doesn't like the lock prefix to be on a single line +// so we can't insert a label after the lock prefix. +// By emitting a lock prefix, we can define a label after it. +#define LOCK_IF_MP(mp) __asm cmp mp, 0 \ + __asm je L0 \ + __asm _emit 0xF0 \ + __asm L0: + +#ifdef AMD64 +inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } +inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } + +inline jint Atomic::add (jint add_value, volatile jint* dest) { + return (jint)(*os::atomic_add_func)(add_value, dest); +} + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { + return (intptr_t)(*os::atomic_add_ptr_func)(add_value, dest); +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)(*os::atomic_add_ptr_func)(add_value, (volatile intptr_t*)dest); +} + +inline void Atomic::inc (volatile jint* dest) { + (void)add (1, dest); +} + +inline void Atomic::inc_ptr(volatile intptr_t* dest) { + (void)add_ptr(1, dest); +} + +inline void Atomic::inc_ptr(volatile void* dest) { + (void)add_ptr(1, dest); +} + +inline void Atomic::dec (volatile jint* dest) { + (void)add (-1, dest); +} + +inline void Atomic::dec_ptr(volatile intptr_t* dest) { + (void)add_ptr(-1, dest); +} + +inline void Atomic::dec_ptr(volatile void* dest) { + (void)add_ptr(-1, dest); +} + +inline jint Atomic::xchg (jint exchange_value, volatile jint* dest) { + return (jint)(*os::atomic_xchg_func)(exchange_value, dest); +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { + return (intptr_t)(os::atomic_xchg_ptr_func)(exchange_value, dest); +} + +inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { + return (void *)(os::atomic_xchg_ptr_func)((intptr_t)exchange_value, (volatile intptr_t*)dest); +} + +inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { + return (*os::atomic_cmpxchg_func)(exchange_value, dest, compare_value); +} + +inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) { + return (*os::atomic_cmpxchg_long_func)(exchange_value, dest, compare_value); +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { + return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value); +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value); +} + +#else // !AMD64 + +//inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } +//inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } +inline jint Atomic::add (jint add_value, volatile jint* dest) { + int mp = os::is_MP(); + __asm { + mov edx, dest; + mov eax, add_value; + mov ecx, eax; + LOCK_IF_MP(mp) + xadd dword ptr [edx], eax; + add eax, ecx; + } +} + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { + return (intptr_t)add((jint)add_value, (volatile jint*)dest); +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)add((jint)add_value, (volatile jint*)dest); +} + +inline void Atomic::inc (volatile jint* dest) { + // alternative for InterlockedIncrement + int mp = os::is_MP(); + __asm { + mov edx, dest; + LOCK_IF_MP(mp) + add dword ptr [edx], 1; + } +} + +inline void Atomic::inc_ptr(volatile intptr_t* dest) { + inc((volatile jint*)dest); +} + +inline void Atomic::inc_ptr(volatile void* dest) { + inc((volatile jint*)dest); +} + +inline void Atomic::dec (volatile jint* dest) { + // alternative for InterlockedDecrement + int mp = os::is_MP(); + __asm { + mov edx, dest; + LOCK_IF_MP(mp) + sub dword ptr [edx], 1; + } +} + +inline void Atomic::dec_ptr(volatile intptr_t* dest) { + dec((volatile jint*)dest); +} + +inline void Atomic::dec_ptr(volatile void* dest) { + dec((volatile jint*)dest); +} + +inline jint Atomic::xchg (jint exchange_value, volatile jint* dest) { + // alternative for InterlockedExchange + __asm { + mov eax, exchange_value; + mov ecx, dest; + xchg eax, dword ptr [ecx]; + } +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { + return (intptr_t)xchg((jint)exchange_value, (volatile jint*)dest); +} + +inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { + return (void*)xchg((jint)exchange_value, (volatile jint*)dest); +} + +inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { + // alternative for InterlockedCompareExchange + int mp = os::is_MP(); + __asm { + mov edx, dest + mov ecx, exchange_value + mov eax, compare_value + LOCK_IF_MP(mp) + cmpxchg dword ptr [edx], ecx + } +} + +inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) { + int mp = os::is_MP(); + jint ex_lo = (jint)exchange_value; + jint ex_hi = *( ((jint*)&exchange_value) + 1 ); + jint cmp_lo = (jint)compare_value; + jint cmp_hi = *( ((jint*)&compare_value) + 1 ); + __asm { + push ebx + push edi + mov eax, cmp_lo + mov edx, cmp_hi + mov edi, dest + mov ebx, ex_lo + mov ecx, ex_hi + LOCK_IF_MP(mp) + cmpxchg8b qword ptr [edi] + pop edi + pop ebx + } +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { + return (intptr_t)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value); +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value); +} +#endif // AMD64 + +#pragma warning(default: 4035) // Enables warnings reporting missing return statement diff --git a/hotspot/src/os_cpu/windows_x86/vm/bytes_windows_x86.inline.hpp b/hotspot/src/os_cpu/windows_x86/vm/bytes_windows_x86.inline.hpp new file mode 100644 index 00000000000..345bfbd3802 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/bytes_windows_x86.inline.hpp @@ -0,0 +1,82 @@ +/* + * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#pragma warning(disable: 4035) // Disable warning 4035: no return value + +// Efficient swapping of data bytes from Java byte +// ordering to native byte ordering and vice versa. +inline u2 Bytes::swap_u2(u2 x) { +#ifdef AMD64 + address p = (address) &x; + return ( (u2(p[0]) << 8 ) | ( u2(p[1])) ); +#else + __asm { + mov ax, x + xchg al, ah + } + // no return statement needed, result is already in ax + // compiler warning C4035 disabled via warning pragma +#endif // AMD64 +} + +inline u4 Bytes::swap_u4(u4 x) { +#ifdef AMD64 + address p = (address) &x; + return ( (u4(p[0]) << 24) | (u4(p[1]) << 16) | (u4(p[2]) << 8) | u4(p[3])) ; +#else + __asm { + mov eax, x + bswap eax + } + // no return statement needed, result is already in eax + // compiler warning C4035 disabled via warning pragma +#endif // AMD64 +} + +#ifdef AMD64 +inline u8 Bytes::swap_u8(u8 x) { + address p = (address) &x; + return ( (u8(p[0]) << 56) | (u8(p[1]) << 48) | (u8(p[2]) << 40) | (u8(p[3]) << 32) | + (u8(p[4]) << 24) | (u8(p[5]) << 16) | (u8(p[6]) << 8) | u8(p[7])) ; +} + +#else +// Helper function for swap_u8 +inline u8 Bytes::swap_u8_base(u4 x, u4 y) { + __asm { + mov eax, y + mov edx, x + bswap eax + bswap edx + } + // no return statement needed, result is already in edx:eax + // compiler warning C4035 disabled via warning pragma +} + +inline u8 Bytes::swap_u8(u8 x) { + return swap_u8_base(*(u4*)&x, *(((u4*)&x)+1)); +} +#endif // AMD64 + +#pragma warning(default: 4035) // Enable warning 4035: no return value diff --git a/hotspot/src/os_cpu/windows_x86/vm/copy_windows_x86.inline.hpp b/hotspot/src/os_cpu/windows_x86/vm/copy_windows_x86.inline.hpp new file mode 100644 index 00000000000..2d6c0519569 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/copy_windows_x86.inline.hpp @@ -0,0 +1,166 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +static void pd_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + (void)memmove(to, from, count * HeapWordSize); +} + +static void pd_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + switch (count) { + case 8: to[7] = from[7]; + case 7: to[6] = from[6]; + case 6: to[5] = from[5]; + case 5: to[4] = from[4]; + case 4: to[3] = from[3]; + case 3: to[2] = from[2]; + case 2: to[1] = from[1]; + case 1: to[0] = from[0]; + case 0: break; + default: + (void)memcpy(to, from, count * HeapWordSize); + break; + } +#else + (void)memcpy(to, from, count * HeapWordSize); +#endif // AMD64 +} + +static void pd_disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count) { + switch (count) { + case 8: to[7] = from[7]; + case 7: to[6] = from[6]; + case 6: to[5] = from[5]; + case 5: to[4] = from[4]; + case 4: to[3] = from[3]; + case 3: to[2] = from[2]; + case 2: to[1] = from[1]; + case 1: to[0] = from[0]; + case 0: break; + default: while (count-- > 0) { + *to++ = *from++; + } + break; + } +} + +static void pd_aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + (void)memmove(to, from, count * HeapWordSize); +} + +static void pd_aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { + pd_disjoint_words(from, to, count); +} + +static void pd_conjoint_bytes(void* from, void* to, size_t count) { + (void)memmove(to, from, count); +} + +static void pd_conjoint_bytes_atomic(void* from, void* to, size_t count) { + pd_conjoint_bytes(from, to, count); +} + +static void pd_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { + // FIXME + (void)memmove(to, from, count << LogBytesPerShort); +} + +static void pd_conjoint_jints_atomic(jint* from, jint* to, size_t count) { + // FIXME + (void)memmove(to, from, count << LogBytesPerInt); +} + +static void pd_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { +#ifdef AMD64 + assert(BytesPerLong == BytesPerOop, "jlongs and oops must be the same size"); + pd_conjoint_oops_atomic((oop*)from, (oop*)to, count); +#else + // Guarantee use of fild/fistp or xmm regs via some asm code, because compilers won't. + __asm { + mov eax, from; + mov edx, to; + mov ecx, count; + cmp eax, edx; + jbe downtest; + jmp uptest; + up: + fild qword ptr [eax]; + fistp qword ptr [edx]; + add eax, 8; + add edx, 8; + uptest: + sub ecx, 1; + jge up; + jmp done; + down: + fild qword ptr [eax][ecx*8]; + fistp qword ptr [edx][ecx*8]; + downtest: + sub ecx, 1; + jge down; + done:; + } +#endif // AMD64 +} + +static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) { + // Do better than this: inline memmove body NEEDS CLEANUP + if (from > to) { + while (count-- > 0) { + // Copy forwards + *to++ = *from++; + } + } else { + from += count - 1; + to += count - 1; + while (count-- > 0) { + // Copy backwards + *to-- = *from--; + } + } +} + +static void pd_arrayof_conjoint_bytes(HeapWord* from, HeapWord* to, size_t count) { +#ifdef AMD64 + pd_conjoint_bytes_atomic(from, to, count); +#else + pd_conjoint_bytes(from, to, count); +#endif // AMD64 +} + +static void pd_arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_jshorts_atomic((jshort*)from, (jshort*)to, count); +} + +static void pd_arrayof_conjoint_jints(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_jints_atomic((jint*)from, (jint*)to, count); +} + +static void pd_arrayof_conjoint_jlongs(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_jlongs_atomic((jlong*)from, (jlong*)to, count); +} + +static void pd_arrayof_conjoint_oops(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_oops_atomic((oop*)from, (oop*)to, count); +} diff --git a/hotspot/src/os_cpu/windows_x86/vm/globals_windows_x86.hpp b/hotspot/src/os_cpu/windows_x86/vm/globals_windows_x86.hpp new file mode 100644 index 00000000000..7c578df700c --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/globals_windows_x86.hpp @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Sets the default values for platform dependent flags used by the runtime system. +// (see globals.hpp) +// +define_pd_global(bool, DontYieldALot, false); + +// Default stack size on Windows is determined by the executable (java.exe +// has a default value of 320K/1MB [32bit/64bit]). Depending on Windows version, changing +// ThreadStackSize to non-zero may have significant impact on memory usage. +// See comments in os_windows.cpp. +define_pd_global(intx, ThreadStackSize, 0); // 0 => use system default +define_pd_global(intx, VMThreadStackSize, 0); // 0 => use system default + +define_pd_global(intx, SurvivorRatio, 8); + +#ifdef ASSERT +define_pd_global(intx, CompilerThreadStackSize, 1024); +#else +define_pd_global(intx, CompilerThreadStackSize, 0); +#endif + +define_pd_global(uintx, JVMInvokeMethodSlack, 8192); + +// Only used on 64 bit Windows platforms +define_pd_global(bool, UseVectoredExceptions, false); diff --git a/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp b/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp new file mode 100644 index 00000000000..b0a98bb0bab --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp @@ -0,0 +1,210 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#pragma warning(disable: 4035) // Disables warnings reporting missing return statement + +// Implementation of class OrderAccess. + +inline void OrderAccess::loadload() { acquire(); } +inline void OrderAccess::storestore() { release(); } +inline void OrderAccess::loadstore() { acquire(); } +inline void OrderAccess::storeload() { fence(); } + +inline void OrderAccess::acquire() { +#ifndef AMD64 + __asm { + mov eax, dword ptr [esp]; + } +#endif // !AMD64 +} + +inline void OrderAccess::release() { + // A volatile store has release semantics. + dummy = 0; +} + +inline void OrderAccess::fence() { +#ifdef AMD64 + (*os::fence_func)(); +#else + if (os::is_MP()) { + __asm { + lock add dword ptr [esp], 0; + } + } +#endif // AMD64 +} + +inline jbyte OrderAccess::load_acquire(volatile jbyte* p) { return *p; } +inline jshort OrderAccess::load_acquire(volatile jshort* p) { return *p; } +inline jint OrderAccess::load_acquire(volatile jint* p) { return *p; } +inline jlong OrderAccess::load_acquire(volatile jlong* p) { return *p; } +inline jubyte OrderAccess::load_acquire(volatile jubyte* p) { return *p; } +inline jushort OrderAccess::load_acquire(volatile jushort* p) { return *p; } +inline juint OrderAccess::load_acquire(volatile juint* p) { return *p; } +inline julong OrderAccess::load_acquire(volatile julong* p) { return *p; } +inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { return *p; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return *p; } + +inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return *p; } +inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return *(void* volatile *)p; } +inline void* OrderAccess::load_ptr_acquire(const volatile void* p) { return *(void* const volatile *)p; } + +inline void OrderAccess::release_store(volatile jbyte* p, jbyte v) { *p = v; } +inline void OrderAccess::release_store(volatile jshort* p, jshort v) { *p = v; } +inline void OrderAccess::release_store(volatile jint* p, jint v) { *p = v; } +inline void OrderAccess::release_store(volatile jlong* p, jlong v) { *p = v; } +inline void OrderAccess::release_store(volatile jubyte* p, jubyte v) { *p = v; } +inline void OrderAccess::release_store(volatile jushort* p, jushort v) { *p = v; } +inline void OrderAccess::release_store(volatile juint* p, juint v) { *p = v; } +inline void OrderAccess::release_store(volatile julong* p, julong v) { *p = v; } +inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { *p = v; } + +inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { *p = v; } +inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { *(void* volatile *)p = v; } + +inline void OrderAccess::store_fence(jbyte* p, jbyte v) { +#ifdef AMD64 + *p = v; fence(); +#else + __asm { + mov edx, p; + mov al, v; + xchg al, byte ptr [edx]; + } +#endif // AMD64 +} + +inline void OrderAccess::store_fence(jshort* p, jshort v) { +#ifdef AMD64 + *p = v; fence(); +#else + __asm { + mov edx, p; + mov ax, v; + xchg ax, word ptr [edx]; + } +#endif // AMD64 +} + +inline void OrderAccess::store_fence(jint* p, jint v) { +#ifdef AMD64 + *p = v; fence(); +#else + __asm { + mov edx, p; + mov eax, v; + xchg eax, dword ptr [edx]; + } +#endif // AMD64 +} + +inline void OrderAccess::store_fence(jlong* p, jlong v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jubyte* p, jubyte v) { store_fence((jbyte*)p, (jbyte)v); } +inline void OrderAccess::store_fence(jushort* p, jushort v) { store_fence((jshort*)p, (jshort)v); } +inline void OrderAccess::store_fence(juint* p, juint v) { store_fence((jint*)p, (jint)v); } +inline void OrderAccess::store_fence(julong* p, julong v) { store_fence((jlong*)p, (jlong)v); } +inline void OrderAccess::store_fence(jfloat* p, jfloat v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jdouble* p, jdouble v) { *p = v; fence(); } + +inline void OrderAccess::store_ptr_fence(intptr_t* p, intptr_t v) { +#ifdef AMD64 + *p = v; fence(); +#else + store_fence((jint*)p, (jint)v); +#endif // AMD64 +} + +inline void OrderAccess::store_ptr_fence(void** p, void* v) { +#ifdef AMD64 + *p = v; fence(); +#else + store_fence((jint*)p, (jint)v); +#endif // AMD64 +} + +// Must duplicate definitions instead of calling store_fence because we don't want to cast away volatile. +inline void OrderAccess::release_store_fence(volatile jbyte* p, jbyte v) { +#ifdef AMD64 + *p = v; fence(); +#else + __asm { + mov edx, p; + mov al, v; + xchg al, byte ptr [edx]; + } +#endif // AMD64 +} + +inline void OrderAccess::release_store_fence(volatile jshort* p, jshort v) { +#ifdef AMD64 + *p = v; fence(); +#else + __asm { + mov edx, p; + mov ax, v; + xchg ax, word ptr [edx]; + } +#endif // AMD64 +} + +inline void OrderAccess::release_store_fence(volatile jint* p, jint v) { +#ifdef AMD64 + *p = v; fence(); +#else + __asm { + mov edx, p; + mov eax, v; + xchg eax, dword ptr [edx]; + } +#endif // AMD64 +} + +inline void OrderAccess::release_store_fence(volatile jlong* p, jlong v) { *p = v; fence(); } + +inline void OrderAccess::release_store_fence(volatile jubyte* p, jubyte v) { release_store_fence((volatile jbyte*)p, (jbyte)v); } +inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) { release_store_fence((volatile jshort*)p, (jshort)v); } +inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { release_store_fence((volatile jint*)p, (jint)v); } +inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { release_store_fence((volatile jlong*)p, (jlong)v); } +inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { *p = v; fence(); } + +inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { +#ifdef AMD64 + *p = v; fence(); +#else + release_store_fence((volatile jint*)p, (jint)v); +#endif // AMD64 +} + +inline void OrderAccess::release_store_ptr_fence(volatile void* p, void* v) { +#ifdef AMD64 + *(void* volatile *)p = v; fence(); +#else + release_store_fence((volatile jint*)p, (jint)v); +#endif // AMD64 +} + +#pragma warning(default: 4035) // Enables warnings reporting missing return statement diff --git a/hotspot/src/os_cpu/windows_x86/vm/os_windows_x86.cpp b/hotspot/src/os_cpu/windows_x86/vm/os_windows_x86.cpp new file mode 100644 index 00000000000..a2504045915 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/os_windows_x86.cpp @@ -0,0 +1,478 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// do not include precompiled header file +# include "incls/_os_windows_x86.cpp.incl" +# include "unwind_windows_x86.hpp" +#undef REG_SP +#undef REG_FP +#undef REG_PC +#ifdef AMD64 +#define REG_SP Rsp +#define REG_FP Rbp +#define REG_PC Rip +#else +#define REG_SP Esp +#define REG_FP Ebp +#define REG_PC Eip +#endif // AMD64 + +extern LONG WINAPI topLevelExceptionFilter(_EXCEPTION_POINTERS* ); + +// Install a win32 structured exception handler around thread. +void os::os_exception_wrapper(java_call_t f, JavaValue* value, methodHandle* method, JavaCallArguments* args, Thread* thread) { + __try { + +#ifndef AMD64 + // We store the current thread in this wrapperthread location + // and determine how far away this address is from the structured + // execption pointer that FS:[0] points to. This get_thread + // code can then get the thread pointer via FS. + // + // Warning: This routine must NEVER be inlined since we'd end up with + // multiple offsets. + // + volatile Thread* wrapperthread = thread; + + if ( ThreadLocalStorage::get_thread_ptr_offset() == 0 ) { + int thread_ptr_offset; + __asm { + lea eax, dword ptr wrapperthread; + sub eax, dword ptr FS:[0H]; + mov thread_ptr_offset, eax + }; + ThreadLocalStorage::set_thread_ptr_offset(thread_ptr_offset); + } +#ifdef ASSERT + // Verify that the offset hasn't changed since we initally captured + // it. This might happen if we accidentally ended up with an + // inlined version of this routine. + else { + int test_thread_ptr_offset; + __asm { + lea eax, dword ptr wrapperthread; + sub eax, dword ptr FS:[0H]; + mov test_thread_ptr_offset, eax + }; + assert(test_thread_ptr_offset == ThreadLocalStorage::get_thread_ptr_offset(), + "thread pointer offset from SEH changed"); + } +#endif // ASSERT +#endif // !AMD64 + + f(value, method, args, thread); + } __except(topLevelExceptionFilter((_EXCEPTION_POINTERS*)_exception_info())) { + // Nothing to do. + } +} + +#ifdef AMD64 + +// This is the language specific handler for exceptions +// originating from dynamically generated code. +// We call the standard structured exception handler +// We only expect Continued Execution since we cannot unwind +// from generated code. +LONG HandleExceptionFromCodeCache( + IN PEXCEPTION_RECORD ExceptionRecord, + IN ULONG64 EstablisherFrame, + IN OUT PCONTEXT ContextRecord, + IN OUT PDISPATCHER_CONTEXT DispatcherContext) { + EXCEPTION_POINTERS ep; + LONG result; + + ep.ExceptionRecord = ExceptionRecord; + ep.ContextRecord = ContextRecord; + + result = topLevelExceptionFilter(&ep); + + // We better only get a CONTINUE_EXECUTION from our handler + // since we don't have unwind information registered. + + guarantee( result == EXCEPTION_CONTINUE_EXECUTION, + "Unexpected result from topLevelExceptionFilter"); + + return(ExceptionContinueExecution); +} + + +// Structure containing the Windows Data Structures required +// to register our Code Cache exception handler. +// We put these in the CodeCache since the API requires +// all addresses in these structures are relative to the Code +// area registered with RtlAddFunctionTable. +typedef struct { + char ExceptionHandlerInstr[16]; // jmp HandleExceptionFromCodeCache + RUNTIME_FUNCTION rt; + UNWIND_INFO_EH_ONLY unw; +} DynamicCodeData, *pDynamicCodeData; + +#endif // AMD64 +// +// Register our CodeCache area with the OS so it will dispatch exceptions +// to our topLevelExceptionFilter when we take an exception in our +// dynamically generated code. +// +// Arguments: low and high are the address of the full reserved +// codeCache area +// +bool os::register_code_area(char *low, char *high) { +#ifdef AMD64 + + ResourceMark rm; + + pDynamicCodeData pDCD; + PRUNTIME_FUNCTION prt; + PUNWIND_INFO_EH_ONLY punwind; + + // If we are using Vectored Exceptions we don't need this registration + if (UseVectoredExceptions) return true; + + BufferBlob* b = BufferBlob::create("CodeCache Exception Handler", sizeof (DynamicCodeData)); + CodeBuffer cb(b->instructions_begin(), b->instructions_size()); + MacroAssembler* masm = new MacroAssembler(&cb); + pDCD = (pDynamicCodeData) masm->pc(); + + masm->jump(ExternalAddress((address)&HandleExceptionFromCodeCache)); + masm->flush(); + + // Create an Unwind Structure specifying no unwind info + // other than an Exception Handler + punwind = &pDCD->unw; + punwind->Version = 1; + punwind->Flags = UNW_FLAG_EHANDLER; + punwind->SizeOfProlog = 0; + punwind->CountOfCodes = 0; + punwind->FrameRegister = 0; + punwind->FrameOffset = 0; + punwind->ExceptionHandler = (char *)(&(pDCD->ExceptionHandlerInstr[0])) - + (char*)low; + punwind->ExceptionData[0] = 0; + + // This structure describes the covered dynamic code area. + // Addresses are relative to the beginning on the code cache area + prt = &pDCD->rt; + prt->BeginAddress = 0; + prt->EndAddress = (ULONG)(high - low); + prt->UnwindData = ((char *)punwind - low); + + guarantee(RtlAddFunctionTable(prt, 1, (ULONGLONG)low), + "Failed to register Dynamic Code Exception Handler with RtlAddFunctionTable"); + +#endif // AMD64 + return true; +} + +void os::initialize_thread() { +// Nothing to do. +} + +// Atomics and Stub Functions + +typedef jint xchg_func_t (jint, volatile jint*); +typedef intptr_t xchg_ptr_func_t (intptr_t, volatile intptr_t*); +typedef jint cmpxchg_func_t (jint, volatile jint*, jint); +typedef jlong cmpxchg_long_func_t (jlong, volatile jlong*, jlong); +typedef jint add_func_t (jint, volatile jint*); +typedef intptr_t add_ptr_func_t (intptr_t, volatile intptr_t*); +typedef void fence_func_t (); + +#ifdef AMD64 + +jint os::atomic_xchg_bootstrap(jint exchange_value, volatile jint* dest) { + // try to use the stub: + xchg_func_t* func = CAST_TO_FN_PTR(xchg_func_t*, StubRoutines::atomic_xchg_entry()); + + if (func != NULL) { + os::atomic_xchg_func = func; + return (*func)(exchange_value, dest); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + jint old_value = *dest; + *dest = exchange_value; + return old_value; +} + +intptr_t os::atomic_xchg_ptr_bootstrap(intptr_t exchange_value, volatile intptr_t* dest) { + // try to use the stub: + xchg_ptr_func_t* func = CAST_TO_FN_PTR(xchg_ptr_func_t*, StubRoutines::atomic_xchg_ptr_entry()); + + if (func != NULL) { + os::atomic_xchg_ptr_func = func; + return (*func)(exchange_value, dest); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + intptr_t old_value = *dest; + *dest = exchange_value; + return old_value; +} + + +jint os::atomic_cmpxchg_bootstrap(jint exchange_value, volatile jint* dest, jint compare_value) { + // try to use the stub: + cmpxchg_func_t* func = CAST_TO_FN_PTR(cmpxchg_func_t*, StubRoutines::atomic_cmpxchg_entry()); + + if (func != NULL) { + os::atomic_cmpxchg_func = func; + return (*func)(exchange_value, dest, compare_value); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + jint old_value = *dest; + if (old_value == compare_value) + *dest = exchange_value; + return old_value; +} +#endif // AMD64 + +jlong os::atomic_cmpxchg_long_bootstrap(jlong exchange_value, volatile jlong* dest, jlong compare_value) { + // try to use the stub: + cmpxchg_long_func_t* func = CAST_TO_FN_PTR(cmpxchg_long_func_t*, StubRoutines::atomic_cmpxchg_long_entry()); + + if (func != NULL) { + os::atomic_cmpxchg_long_func = func; + return (*func)(exchange_value, dest, compare_value); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + jlong old_value = *dest; + if (old_value == compare_value) + *dest = exchange_value; + return old_value; +} + +#ifdef AMD64 + +jint os::atomic_add_bootstrap(jint add_value, volatile jint* dest) { + // try to use the stub: + add_func_t* func = CAST_TO_FN_PTR(add_func_t*, StubRoutines::atomic_add_entry()); + + if (func != NULL) { + os::atomic_add_func = func; + return (*func)(add_value, dest); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + return (*dest) += add_value; +} + +intptr_t os::atomic_add_ptr_bootstrap(intptr_t add_value, volatile intptr_t* dest) { + // try to use the stub: + add_ptr_func_t* func = CAST_TO_FN_PTR(add_ptr_func_t*, StubRoutines::atomic_add_ptr_entry()); + + if (func != NULL) { + os::atomic_add_ptr_func = func; + return (*func)(add_value, dest); + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + return (*dest) += add_value; +} + +void os::fence_bootstrap() { + // try to use the stub: + fence_func_t* func = CAST_TO_FN_PTR(fence_func_t*, StubRoutines::fence_entry()); + + if (func != NULL) { + os::fence_func = func; + (*func)(); + return; + } + assert(Threads::number_of_threads() == 0, "for bootstrap only"); + + // don't have to do anything for a single thread +} + + +xchg_func_t* os::atomic_xchg_func = os::atomic_xchg_bootstrap; +xchg_ptr_func_t* os::atomic_xchg_ptr_func = os::atomic_xchg_ptr_bootstrap; +cmpxchg_func_t* os::atomic_cmpxchg_func = os::atomic_cmpxchg_bootstrap; +add_func_t* os::atomic_add_func = os::atomic_add_bootstrap; +add_ptr_func_t* os::atomic_add_ptr_func = os::atomic_add_ptr_bootstrap; +fence_func_t* os::fence_func = os::fence_bootstrap; + +#endif // AMD64 + +cmpxchg_long_func_t* os::atomic_cmpxchg_long_func = os::atomic_cmpxchg_long_bootstrap; + +ExtendedPC os::fetch_frame_from_context(void* ucVoid, + intptr_t** ret_sp, intptr_t** ret_fp) { + + ExtendedPC epc; + CONTEXT* uc = (CONTEXT*)ucVoid; + + if (uc != NULL) { + epc = ExtendedPC((address)uc->REG_PC); + if (ret_sp) *ret_sp = (intptr_t*)uc->REG_SP; + if (ret_fp) *ret_fp = (intptr_t*)uc->REG_FP; + } else { + // construct empty ExtendedPC for return value checking + epc = ExtendedPC(NULL); + if (ret_sp) *ret_sp = (intptr_t *)NULL; + if (ret_fp) *ret_fp = (intptr_t *)NULL; + } + + return epc; +} + +frame os::fetch_frame_from_context(void* ucVoid) { + intptr_t* sp; + intptr_t* fp; + ExtendedPC epc = fetch_frame_from_context(ucVoid, &sp, &fp); + return frame(sp, fp, epc.pc()); +} + +// VC++ does not save frame pointer on stack in optimized build. It +// can be turned off by /Oy-. If we really want to walk C frames, +// we can use the StackWalk() API. +frame os::get_sender_for_C_frame(frame* fr) { + return frame(fr->sender_sp(), fr->link(), fr->sender_pc()); +} + + +#ifndef AMD64 +intptr_t* _get_previous_fp() { + intptr_t **frameptr; + __asm { + mov frameptr, ebp + }; + return *frameptr; +} +#endif // !AMD64 + +frame os::current_frame() { + +#ifdef AMD64 + // apparently _asm not supported on windows amd64 + typedef intptr_t* get_fp_func (); + get_fp_func* func = CAST_TO_FN_PTR(get_fp_func*, + StubRoutines::amd64::get_previous_fp_entry()); + if (func == NULL) return frame(NULL, NULL, NULL); + intptr_t* fp = (*func)(); +#else + intptr_t* fp = _get_previous_fp(); +#endif // AMD64 + + frame myframe((intptr_t*)os::current_stack_pointer(), + (intptr_t*)fp, + CAST_FROM_FN_PTR(address, os::current_frame)); + if (os::is_first_C_frame(&myframe)) { + // stack is not walkable + return frame(NULL, NULL, NULL); + } else { + return os::get_sender_for_C_frame(&myframe); + } +} + +void os::print_context(outputStream *st, void *context) { + if (context == NULL) return; + + CONTEXT* uc = (CONTEXT*)context; + + st->print_cr("Registers:"); +#ifdef AMD64 + st->print( "EAX=" INTPTR_FORMAT, uc->Rax); + st->print(", EBX=" INTPTR_FORMAT, uc->Rbx); + st->print(", ECX=" INTPTR_FORMAT, uc->Rcx); + st->print(", EDX=" INTPTR_FORMAT, uc->Rdx); + st->cr(); + st->print( "ESP=" INTPTR_FORMAT, uc->Rsp); + st->print(", EBP=" INTPTR_FORMAT, uc->Rbp); + st->print(", ESI=" INTPTR_FORMAT, uc->Rsi); + st->print(", EDI=" INTPTR_FORMAT, uc->Rdi); + st->cr(); + st->print( "EIP=" INTPTR_FORMAT, uc->Rip); + st->print(", EFLAGS=" INTPTR_FORMAT, uc->EFlags); +#else + st->print( "EAX=" INTPTR_FORMAT, uc->Eax); + st->print(", EBX=" INTPTR_FORMAT, uc->Ebx); + st->print(", ECX=" INTPTR_FORMAT, uc->Ecx); + st->print(", EDX=" INTPTR_FORMAT, uc->Edx); + st->cr(); + st->print( "ESP=" INTPTR_FORMAT, uc->Esp); + st->print(", EBP=" INTPTR_FORMAT, uc->Ebp); + st->print(", ESI=" INTPTR_FORMAT, uc->Esi); + st->print(", EDI=" INTPTR_FORMAT, uc->Edi); + st->cr(); + st->print( "EIP=" INTPTR_FORMAT, uc->Eip); + st->print(", EFLAGS=" INTPTR_FORMAT, uc->EFlags); +#endif // AMD64 + st->cr(); + st->cr(); + + intptr_t *sp = (intptr_t *)uc->REG_SP; + st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", sp); + print_hex_dump(st, (address)sp, (address)(sp + 32), sizeof(intptr_t)); + st->cr(); + + // Note: it may be unsafe to inspect memory near pc. For example, pc may + // point to garbage if entry point in an nmethod is corrupted. Leave + // this at the end, and hope for the best. + address pc = (address)uc->REG_PC; + st->print_cr("Instructions: (pc=" PTR_FORMAT ")", pc); + print_hex_dump(st, pc - 16, pc + 16, sizeof(char)); + st->cr(); +} + +extern "C" int SafeFetch32 (int * adr, int Err) { + int rv = Err ; + _try { + rv = *((volatile int *) adr) ; + } __except(EXCEPTION_EXECUTE_HANDLER) { + } + return rv ; +} + +extern "C" intptr_t SafeFetchN (intptr_t * adr, intptr_t Err) { + intptr_t rv = Err ; + _try { + rv = *((volatile intptr_t *) adr) ; + } __except(EXCEPTION_EXECUTE_HANDLER) { + } + return rv ; +} + +extern "C" int SpinPause () { +#ifdef AMD64 + return 0 ; +#else + // pause == rep:nop + // On systems that don't support pause a rep:nop + // is executed as a nop. The rep: prefix is ignored. + _asm { + pause ; + }; + return 1 ; +#endif // AMD64 +} + + +void os::setup_fpu() { +#ifndef AMD64 + int fpu_cntrl_word = StubRoutines::fpu_cntrl_wrd_std(); + __asm fldcw fpu_cntrl_word; +#endif // !AMD64 +} diff --git a/hotspot/src/os_cpu/windows_x86/vm/os_windows_x86.hpp b/hotspot/src/os_cpu/windows_x86/vm/os_windows_x86.hpp new file mode 100644 index 00000000000..d7578101677 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/os_windows_x86.hpp @@ -0,0 +1,63 @@ +/* + * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // + // NOTE: we are back in class os here, not win32 + // +#ifdef AMD64 + static jint (*atomic_xchg_func) (jint, volatile jint*); + static intptr_t (*atomic_xchg_ptr_func) (intptr_t, volatile intptr_t*); + + static jint (*atomic_cmpxchg_func) (jint, volatile jint*, jint); + static jlong (*atomic_cmpxchg_long_func) (jlong, volatile jlong*, jlong); + + static jint (*atomic_add_func) (jint, volatile jint*); + static intptr_t (*atomic_add_ptr_func) (intptr_t, volatile intptr_t*); + + static void (*fence_func) (); + + + static jint atomic_xchg_bootstrap (jint, volatile jint*); + static intptr_t atomic_xchg_ptr_bootstrap (intptr_t, volatile intptr_t*); + + static jint atomic_cmpxchg_bootstrap (jint, volatile jint*, jint); +#else + + static jlong (*atomic_cmpxchg_long_func) (jlong, volatile jlong*, jlong); + +#endif // AMD64 + + static jlong atomic_cmpxchg_long_bootstrap(jlong, volatile jlong*, jlong); + +#ifdef AMD64 + static jint atomic_add_bootstrap (jint, volatile jint*); + static intptr_t atomic_add_ptr_bootstrap (intptr_t, volatile intptr_t*); + + static void fence_bootstrap (); +#endif // AMD64 + + static void setup_fpu(); + static bool supports_sse() { return true; } + + static bool register_code_area(char *low, char *high); diff --git a/hotspot/src/os_cpu/windows_x86/vm/prefetch_windows_x86.inline.hpp b/hotspot/src/os_cpu/windows_x86/vm/prefetch_windows_x86.inline.hpp new file mode 100644 index 00000000000..d40d3418a8f --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/prefetch_windows_x86.inline.hpp @@ -0,0 +1,26 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void Prefetch::read (void *loc, intx interval) {} +inline void Prefetch::write(void *loc, intx interval) {} diff --git a/hotspot/src/os_cpu/windows_x86/vm/threadLS_windows_x86.cpp b/hotspot/src/os_cpu/windows_x86/vm/threadLS_windows_x86.cpp new file mode 100644 index 00000000000..c46f115d36a --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/threadLS_windows_x86.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Provides an entry point we can link against and +// a buffer we can emit code into. The buffer is +// filled by ThreadLocalStorage::generate_code_for_get_thread +// and called from ThreadLocalStorage::thread() + +#include "incls/_precompiled.incl" +#include "incls/_threadLS_windows_x86.cpp.incl" + +int ThreadLocalStorage::_thread_ptr_offset = 0; + +static void call_wrapper_dummy() {} + +// We need to call the os_exception_wrapper once so that it sets +// up the offset from FS of the thread pointer. +void ThreadLocalStorage::generate_code_for_get_thread() { + os::os_exception_wrapper( (java_call_t)call_wrapper_dummy, + NULL, NULL, NULL, NULL); +} + +void ThreadLocalStorage::pd_init() { } + +void ThreadLocalStorage::pd_set_thread(Thread* thread) { + os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread); +} diff --git a/hotspot/src/os_cpu/windows_x86/vm/threadLS_windows_x86.hpp b/hotspot/src/os_cpu/windows_x86/vm/threadLS_windows_x86.hpp new file mode 100644 index 00000000000..e295abe22e8 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/threadLS_windows_x86.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Processor dependent parts of ThreadLocalStorage + +protected: + + static int _thread_ptr_offset; + +public: + + // Java Thread + static inline Thread* thread() { + return (Thread*)TlsGetValue(thread_index()); + } + + static inline Thread* get_thread() { + return (Thread*)TlsGetValue(thread_index()); + } + + static inline void set_thread_ptr_offset( int offset ) { _thread_ptr_offset = offset; } + + static inline int get_thread_ptr_offset() { return _thread_ptr_offset; } diff --git a/hotspot/src/os_cpu/windows_x86/vm/thread_windows_x86.cpp b/hotspot/src/os_cpu/windows_x86/vm/thread_windows_x86.cpp new file mode 100644 index 00000000000..a11a746afa1 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/thread_windows_x86.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_thread_windows_x86.cpp.incl" + +// For Forte Analyzer AsyncGetCallTrace profiling support - thread is +// currently interrupted by SIGPROF +bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, + void* ucontext, bool isInJava) { + + assert(Thread::current() == this, "caller must be current thread"); + assert(this->is_Java_thread(), "must be JavaThread"); + + JavaThread* jt = (JavaThread *)this; + + // If we have a last_Java_frame, then we should use it even if + // isInJava == true. It should be more reliable than CONTEXT info. + if (jt->has_last_Java_frame()) { + *fr_addr = jt->pd_last_frame(); + return true; + } + + // At this point, we don't have a last_Java_frame, so + // we try to glean some information out of the CONTEXT + // if we were running Java code when SIGPROF came in. + if (isInJava) { + CONTEXT* uc = (CONTEXT*)ucontext; + +#ifdef AMD64 + intptr_t* ret_fp = (intptr_t*) uc->Rbp; + intptr_t* ret_sp = (intptr_t*) uc->Rsp; + ExtendedPC addr = ExtendedPC((address)uc->Rip); +#else + intptr_t* ret_fp = (intptr_t*) uc->Ebp; + intptr_t* ret_sp = (intptr_t*) uc->Esp; + ExtendedPC addr = ExtendedPC((address)uc->Eip); +#endif // AMD64 + if (addr.pc() == NULL || ret_sp == NULL ) { + // CONTEXT wasn't useful + return false; + } + + frame ret_frame(ret_sp, ret_fp, addr.pc()); + if (!ret_frame.safe_for_sender(jt)) { +#ifdef COMPILER2 + // C2 uses ebp as a general register see if NULL fp helps + frame ret_frame2(ret_sp, NULL, addr.pc()); + if (!ret_frame2.safe_for_sender(jt)) { + // nothing else to try if the frame isn't good + return false; + } + ret_frame = ret_frame2; +#else + // nothing else to try if the frame isn't good + return false; +#endif /* COMPILER2 */ + } + *fr_addr = ret_frame; + return true; + } + + // nothing else to try + return false; +} diff --git a/hotspot/src/os_cpu/windows_x86/vm/thread_windows_x86.hpp b/hotspot/src/os_cpu/windows_x86/vm/thread_windows_x86.hpp new file mode 100644 index 00000000000..8e61fdd92e9 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/thread_windows_x86.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: + void pd_initialize() { + _anchor.clear(); + } + + frame pd_last_frame() { + assert(has_last_Java_frame(), "must have last_Java_sp() when suspended"); + if (_anchor.last_Java_pc() != NULL) { + return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp(), _anchor.last_Java_pc()); + } else { + // This will pick up pc from sp + return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp()); + } + } + + public: + // Mutators are highly dangerous.... + intptr_t* last_Java_fp() { return _anchor.last_Java_fp(); } + void set_last_Java_fp(intptr_t* fp) { _anchor.set_last_Java_fp(fp); } + + void set_base_of_stack_pointer(intptr_t* base_sp) {} + + + static ByteSize last_Java_fp_offset() { + return byte_offset_of(JavaThread, _anchor) + JavaFrameAnchor::last_Java_fp_offset(); + } + + intptr_t* base_of_stack_pointer() { return NULL; } + void record_base_of_stack_pointer() {} + + bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, + bool isInJava); + + // These routines are only used on cpu architectures that + // have separate register stacks (Itanium). + static bool register_stack_overflow() { return false; } + static void enable_register_stack_guard() {} + static void disable_register_stack_guard() {} diff --git a/hotspot/src/os_cpu/windows_x86/vm/unwind_windows_x86.hpp b/hotspot/src/os_cpu/windows_x86/vm/unwind_windows_x86.hpp new file mode 100644 index 00000000000..1e8ed078d54 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/unwind_windows_x86.hpp @@ -0,0 +1,78 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +#ifdef AMD64 +typedef unsigned char UBYTE; + +#define UNW_FLAG_EHANDLER 0x01 +#define UNW_FLAG_UHANDLER 0x02 +#define UNW_FLAG_CHAININFO 0x04 + +// This structure is used to define an UNWIND_INFO that +// only has an ExceptionHandler. There are no UnwindCodes +// declared. +typedef struct _UNWIND_INFO_EH_ONLY { + UBYTE Version : 3; + UBYTE Flags : 5; + UBYTE SizeOfProlog; + UBYTE CountOfCodes; + UBYTE FrameRegister : 4; + UBYTE FrameOffset : 4; + union { + OPTIONAL ULONG ExceptionHandler; + OPTIONAL ULONG FunctionEntry; + }; + OPTIONAL ULONG ExceptionData[1]; +} UNWIND_INFO_EH_ONLY, *PUNWIND_INFO_EH_ONLY; + + +/* +typedef struct _RUNTIME_FUNCTION { + ULONG BeginAddress; + ULONG EndAddress; + ULONG UnwindData; +} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION; +*/ + +typedef struct _DISPATCHER_CONTEXT { + ULONG64 ControlPc; + ULONG64 ImageBase; + PRUNTIME_FUNCTION FunctionEntry; + ULONG64 EstablisherFrame; + ULONG64 TargetIp; + PCONTEXT ContextRecord; +// PEXCEPTION_ROUTINE LanguageHandler; + char * LanguageHandler; // double dependency problem + PVOID HandlerData; +} DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT; + +typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) ( + IN PEXCEPTION_RECORD ExceptionRecord, + IN ULONG64 EstablisherFrame, + IN OUT PCONTEXT ContextRecord, + IN OUT PDISPATCHER_CONTEXT DispatcherContext +); + +#endif // AMD64 diff --git a/hotspot/src/os_cpu/windows_x86/vm/vmStructs_windows_x86.hpp b/hotspot/src/os_cpu/windows_x86/vm/vmStructs_windows_x86.hpp new file mode 100644 index 00000000000..bf2c029b42a --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/vmStructs_windows_x86.hpp @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// These are the OS and CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + \ + nonstatic_field(OSThread, _thread_id, unsigned long) \ + unchecked_nonstatic_field(OSThread, _thread_handle, sizeof(HANDLE)) /* NOTE: no type */ \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() diff --git a/hotspot/src/os_cpu/windows_x86/vm/vm_version_windows_x86.cpp b/hotspot/src/os_cpu/windows_x86/vm/vm_version_windows_x86.cpp new file mode 100644 index 00000000000..c3abce485a6 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/vm_version_windows_x86.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vm_version_windows_x86.cpp.incl" diff --git a/hotspot/src/os_cpu/windows_x86/vm/windows_x86_32.ad b/hotspot/src/os_cpu/windows_x86/vm/windows_x86_32.ad new file mode 100644 index 00000000000..83a5421c14a --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/windows_x86_32.ad @@ -0,0 +1,158 @@ +// +// Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// X86 Win32 Architecture Description File + +//----------OS-DEPENDENT ENCODING BLOCK----------------------------------------------------- +// This block specifies the encoding classes used by the compiler to output +// byte streams. Encoding classes generate functions which are called by +// Machine Instruction Nodes in order to generate the bit encoding of the +// instruction. Operands specify their base encoding interface with the +// interface keyword. There are currently supported four interfaces, +// REG_INTER, CONST_INTER, MEMORY_INTER, & COND_INTER. REG_INTER causes an +// operand to generate a function which returns its register number when +// queried. CONST_INTER causes an operand to generate a function which +// returns the value of the constant when queried. MEMORY_INTER causes an +// operand to generate four functions which return the Base Register, the +// Index Register, the Scale Value, and the Offset Value of the operand when +// queried. COND_INTER causes an operand to generate six functions which +// return the encoding code (ie - encoding bits for the instruction) +// associated with each basic boolean condition for a conditional instruction. +// Instructions specify two basic values for encoding. They use the +// ins_encode keyword to specify their encoding class (which must be one of +// the class names specified in the encoding block), and they use the +// opcode keyword to specify, in order, their primary, secondary, and +// tertiary opcode. Only the opcode sections which a particular instruction +// needs for encoding need to be specified. +encode %{ + // Build emit functions for each basic byte or larger field in the intel + // encoding scheme (opcode, rm, sib, immediate), and call them from C++ + // code in the enc_class source block. Emit functions will live in the + // main source block for now. In future, we can generalize this by + // adding a syntax that specifies the sizes of fields in an order, + // so that the adlc can build the emit functions automagically + + enc_class tlsencode (eRegP dst, eRegP src) %{ + emit_rm(cbuf, 0x2, $dst$$reg, $src$$reg); + emit_d32(cbuf, ThreadLocalStorage::get_thread_ptr_offset() ); + %} + + enc_class call_epilog %{ + if( VerifyStackAtCalls ) { + // Check that stack depth is unchanged: find majik cookie on stack + int framesize = ra_->reg2offset_unchecked(OptoReg::add(ra_->_matcher._old_SP,-3*VMRegImpl::slots_per_word)); + if(framesize >= 128) { + emit_opcode(cbuf, 0x81); // cmp [esp+0],0xbadb1ood + emit_d8(cbuf,0xBC); + emit_d8(cbuf,0x24); + emit_d32(cbuf,framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } + else { + emit_opcode(cbuf, 0x81); // cmp [esp+0],0xbadb1ood + emit_d8(cbuf,0x7C); + emit_d8(cbuf,0x24); + emit_d8(cbuf,framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } + // jmp EQ around INT3 + emit_opcode(cbuf,0x74); + emit_d8(cbuf,1); + // Die if stack mismatch + emit_opcode(cbuf,0xCC); + } + %} + +%} + +// INSTRUCTIONS -- Platform dependent + + +//----------OS and Locking Instructions---------------------------------------- + +// The prefix of this name is KNOWN by the ADLC and cannot be changed. +instruct tlsLoadP_prefixLoadP(eRegP t1) %{ + effect(DEF t1); + + format %{ "MOV $t1,FS:[0x00] "%} + opcode(0x8B, 0x64); + ins_encode(OpcS, OpcP, conmemref(t1)); + ins_pipe( ialu_reg_fat ); +%} + +// This name is KNOWN by the ADLC and cannot be changed. +// The ADLC forces a 'TypeRawPtr::BOTTOM' output type +// for this guy. +// %%% Should do this with a clause like: bottom_type(TypeRawPtr::BOTTOM); +instruct tlsLoadP(eRegP dst, eRegP t1) %{ + effect(DEF dst, USE t1); + + format %{ "MOV $dst,[$t1 + TLS::thread_ptr_offset()]" %} + opcode(0x8B); + ins_encode(OpcP, tlsencode(dst, t1)); + ins_pipe( ialu_reg_reg_fat ); +%} + +instruct TLS(eRegP dst) %{ + match(Set dst (ThreadLocal)); + expand %{ + eRegP t1; + tlsLoadP_prefixLoadP(t1); + tlsLoadP(dst, t1); + %} +%} + +// Die now +instruct ShouldNotReachHere( ) +%{ + match(Halt); + // Use the following format syntax + format %{ "INT3 ; ShouldNotReachHere" %} + opcode(0xCC); + ins_encode(OpcP); + ins_pipe( pipe_slow ); +%} + +// +// Platform dependent source +// +source %{ + +// emit an interrupt that is caught by the debugger +void emit_break(CodeBuffer &cbuf) { + *(cbuf.code_end()) = (unsigned char)(0xcc); + cbuf.set_code_end(cbuf.code_end() + 1); +} + +void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + emit_break(cbuf); +} + + +uint MachBreakpointNode::size(PhaseRegAlloc *ra_) const { + return 1; +} + + +%} diff --git a/hotspot/src/os_cpu/windows_x86/vm/windows_x86_64.ad b/hotspot/src/os_cpu/windows_x86/vm/windows_x86_64.ad new file mode 100644 index 00000000000..0ef5a15bf17 --- /dev/null +++ b/hotspot/src/os_cpu/windows_x86/vm/windows_x86_64.ad @@ -0,0 +1,159 @@ +// +// Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// AMD64 Win32 Architecture Description File + +//----------OS-DEPENDENT ENCODING BLOCK----------------------------------------------------- +// This block specifies the encoding classes used by the compiler to output +// byte streams. Encoding classes generate functions which are called by +// Machine Instruction Nodes in order to generate the bit encoding of the +// instruction. Operands specify their base encoding interface with the +// interface keyword. There are currently supported four interfaces, +// REG_INTER, CONST_INTER, MEMORY_INTER, & COND_INTER. REG_INTER causes an +// operand to generate a function which returns its register number when +// queried. CONST_INTER causes an operand to generate a function which +// returns the value of the constant when queried. MEMORY_INTER causes an +// operand to generate four functions which return the Base Register, the +// Index Register, the Scale Value, and the Offset Value of the operand when +// queried. COND_INTER causes an operand to generate six functions which +// return the encoding code (ie - encoding bits for the instruction) +// associated with each basic boolean condition for a conditional instruction. +// Instructions specify two basic values for encoding. They use the +// ins_encode keyword to specify their encoding class (which must be one of +// the class names specified in the encoding block), and they use the +// opcode keyword to specify, in order, their primary, secondary, and +// tertiary opcode. Only the opcode sections which a particular instruction +// needs for encoding need to be specified. +encode %{ + // Build emit functions for each basic byte or larger field in the intel + // encoding scheme (opcode, rm, sib, immediate), and call them from C++ + // code in the enc_class source block. Emit functions will live in the + // main source block for now. In future, we can generalize this by + // adding a syntax that specifies the sizes of fields in an order, + // so that the adlc can build the emit functions automagically + + enc_class Java_To_Runtime (method meth) %{ // CALL Java_To_Runtime + // No relocation needed + + // movq r10, + emit_opcode(cbuf, Assembler::REX_WB); + emit_opcode(cbuf, 0xB8 | (R10_enc - 8)); + emit_d64(cbuf, (int64_t) $meth$$method); + + // call (r10) + emit_opcode(cbuf, Assembler::REX_B); + emit_opcode(cbuf, 0xFF); + emit_opcode(cbuf, 0xD0 | (R10_enc - 8)); + %} + + enc_class call_epilog %{ + if (VerifyStackAtCalls) { + // Check that stack depth is unchanged: find majik cookie on stack + int framesize = + ra_->reg2offset_unchecked(OptoReg::add(ra_->_matcher._old_SP, -3*VMRegImpl::slots_per_word)); + if (framesize) { + if (framesize < 0x80) { + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x81); // cmpq [rsp+0],0xbadb1ood + emit_d8(cbuf, 0x7C); + emit_d8(cbuf, 0x24); + emit_d8(cbuf, framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } else { + emit_opcode(cbuf, Assembler::REX_W); + emit_opcode(cbuf, 0x81); // cmpq [rsp+0],0xbadb1ood + emit_d8(cbuf, 0xBC); + emit_d8(cbuf, 0x24); + emit_d32(cbuf, framesize); // Find majik cookie from ESP + emit_d32(cbuf, 0xbadb100d); + } + } + // jmp EQ around INT3 + // QQQ TODO + const int jump_around = 5; // size of call to breakpoint, 1 for CC + emit_opcode(cbuf, 0x74); + emit_d8(cbuf, jump_around); + // QQQ temporary + emit_break(cbuf); + // Die if stack mismatch + // emit_opcode(cbuf,0xCC); + } + %} +%} + +// INSTRUCTIONS -- Platform dependent + + +//----------OS and Locking Instructions---------------------------------------- + +// This name is KNOWN by the ADLC and cannot be changed. +// The ADLC forces a 'TypeRawPtr::BOTTOM' output type +// for this guy. +instruct tlsLoadP(r15_RegP dst) +%{ + match(Set dst (ThreadLocal)); + effect(DEF dst); + + size(0); + format %{ "# TLS is in R15" %} + ins_encode( /*empty encoding*/ ); + ins_pipe(ialu_reg_reg); +%} + +// Die now +instruct ShouldNotReachHere( ) +%{ + match(Halt); + // Use the following format syntax + format %{ "INT3 ; ShouldNotReachHere" %} + opcode(0xCC); + ins_encode(OpcP); + ins_pipe( pipe_slow ); +%} + +// +// Platform dependent source +// +source %{ + +int MachCallRuntimeNode::ret_addr_offset() +{ + return 13; // movq r10,#addr; callq (r10) +} + +// emit an interrupt that is caught by the debugger +void emit_break(CodeBuffer &cbuf) { + *(cbuf.code_end()) = (unsigned char)(0xcc); + cbuf.set_code_end(cbuf.code_end() + 1); +} + +void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + emit_break(cbuf); +} + +uint MachBreakpointNode::size(PhaseRegAlloc *ra_) const { + return 1; +} + +%} diff --git a/hotspot/src/share/tools/MakeDeps/ArgsParser.java b/hotspot/src/share/tools/MakeDeps/ArgsParser.java new file mode 100644 index 00000000000..8856c30f3a0 --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/ArgsParser.java @@ -0,0 +1,85 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ArgIterator { + String[] args; + int i; + ArgIterator(String[] args) { + this.args = args; + this.i = 0; + } + String get() { return args[i]; } + boolean hasMore() { return args != null && i < args.length; } + boolean next() { return ++i < args.length; } +} + +abstract class ArgHandler { + public abstract void handle(ArgIterator it); + +} + +class ArgRule { + String arg; + ArgHandler handler; + ArgRule(String arg, ArgHandler handler) { + this.arg = arg; + this.handler = handler; + } + + boolean process(ArgIterator it) { + if (match(it.get(), arg)) { + handler.handle(it); + return true; + } + return false; + } + boolean match(String rule_pattern, String arg) { + return arg.equals(rule_pattern); + } +} + +class ArgsParser { + ArgsParser(String[] args, + ArgRule[] rules, + ArgHandler defaulter) { + ArgIterator ai = new ArgIterator(args); + while (ai.hasMore()) { + boolean processed = false; + for (int i=0; i.hpp os_.hpp but only + // recorded if the platform file was seen. + private FileList platformFiles; + private FileList outerFiles; + private FileList indivIncludes; + private FileList grandInclude; // the results for the grand include file + private long threshold; + private int nOuterFiles; + private int nPrecompiledFiles; + private boolean missingOk; + private Platform plat; + /** These allow you to specify files not in the include database + which are prepended and appended to the file list, allowing + you to have well-known functions at the start and end of the + text segment (allows us to find out in a portable fashion + whether the current PC is in VM code or not upon a crash) */ + private String firstFile; + private String lastFile; + + public Database(Platform plat, long t) { + this.plat = plat; + macros = new MacroDefinitions(); + allFiles = new FileList("allFiles", plat); + platformFiles = new FileList("platformFiles", plat); + outerFiles = new FileList("outerFiles", plat); + indivIncludes = new FileList("IndivIncludes", plat); + grandInclude = new FileList(plat.getGIFileTemplate().nameOfList(), plat); + + threshold = t; + nOuterFiles = 0; + nPrecompiledFiles = 0; + missingOk = false; + firstFile = null; + lastFile = null; + }; + + public FileList getAllFiles() { + return allFiles; + } + + public Iterator getMacros() { + return macros.getMacros(); + } + + public void canBeMissing() { + missingOk = true; + } + + public boolean hfileIsInGrandInclude(FileList hfile, FileList cfile) { + return ((hfile.getCount() >= threshold) && (cfile.getUseGrandInclude())); + } + + /** These allow you to specify files not in the include database + which are prepended and appended to the file list, allowing + you to have well-known functions at the start and end of the + text segment (allows us to find out in a portable fashion + whether the current PC is in VM code or not upon a crash) */ + public void setFirstFile(String fileName) { + firstFile = fileName; + } + + public void setLastFile(String fileName) { + lastFile = fileName; + } + + public void get(String platFileName, String dbFileName) + throws FileFormatException, IOException, FileNotFoundException { + macros.readFrom(platFileName, missingOk); + + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(dbFileName)); + } catch (FileNotFoundException e) { + if (missingOk) { + return; + } else { + throw(e); + } + } + System.out.println("\treading database: " + dbFileName); + String line; + int lineNo = 0; + do { + line = reader.readLine(); + lineNo++; + if (line != null) { + StreamTokenizer tokenizer = + new StreamTokenizer(new StringReader(line)); + tokenizer.slashSlashComments(true); + tokenizer.wordChars('_', '_'); + tokenizer.wordChars('<', '>'); + // NOTE: if we didn't have to do this line by line, + // we could trivially recognize C-style comments as + // well. + // tokenizer.slashStarComments(true); + int numTok = 0; + int res; + String unexpandedIncluder = null; + String unexpandedIncludee = null; + do { + res = tokenizer.nextToken(); + if (res != StreamTokenizer.TT_EOF) { + if (numTok == 0) { + unexpandedIncluder = tokenizer.sval; + } else if (numTok == 1) { + unexpandedIncludee = tokenizer.sval; + } else { + throw new FileFormatException( + "invalid line: \"" + line + + "\". Error position: line " + lineNo + ); + } + numTok++; + } + } while (res != StreamTokenizer.TT_EOF); + + if ((numTok != 0) && (numTok != 2)) { + throw new FileFormatException( + "invalid line: \"" + line + + "\". Error position: line " + lineNo + ); + } + + if (numTok == 2) { + // Non-empty line + String includer = macros.expand(unexpandedIncluder); + String includee = macros.expand(unexpandedIncludee); + + if (includee.equals(plat.generatePlatformDependentInclude())) { + MacroDefinitions localExpander = macros.copy(); + MacroDefinitions localExpander2 = macros.copy(); + localExpander.setAllMacroBodiesTo("pd"); + localExpander2.setAllMacroBodiesTo(""); + + // unexpanded_includer e.g. thread_.hpp + // thread_solaris_i486.hpp -> _thread_pd.hpp.incl + + FileName pdName = + plat.getInclFileTemplate().copyStem( + localExpander.expand(unexpandedIncluder) + ); + + // derive generic name from platform specific name + // e.g. os_.hpp => os.hpp. We enforce the + // restriction (imperfectly) noted in includeDB_core + // that platform specific files will have an underscore + // preceding the macro invocation. + + // First expand macro as null string. + + String newIncluder_temp = + localExpander2.expand(unexpandedIncluder); + + // Now find "_." and remove the underscore. + + String newIncluder = ""; + + int len = newIncluder_temp.length(); + int count = 0; + + for ( int i = 0; i < len - 1 ; i++ ) { + if (newIncluder_temp.charAt(i) == '_' && newIncluder_temp.charAt(i+1) == '.') { + count++; + } else { + newIncluder += newIncluder_temp.charAt(i); + } + } + newIncluder += newIncluder_temp.charAt(len-1); + + if (count != 1) { + throw new FileFormatException( + "Unexpected filename format for platform dependent file.\nline: \"" + line + + "\".\nError position: line " + lineNo + ); + } + + FileList p = allFiles.listForFile(includer); + p.setPlatformDependentInclude(pdName.dirPreStemSuff()); + + // Add an implicit dependency on platform + // specific file for the generic file + + p = platformFiles.listForFile(newIncluder); + + // if this list is empty then this is 1st + // occurance of a platform dependent file and + // we need a new version of the include file. + // Otherwise we just append to the current + // file. + + PrintWriter pdFile = + new PrintWriter( + new FileWriter(pdName.dirPreStemSuff(), + !p.isEmpty()) + ); + pdFile.println("# include \"" + includer + "\""); + pdFile.close(); + + // Add the platform specific file to the list + // for this generic file. + + FileList q = allFiles.listForFile(includer); + p.addIfAbsent(q); + } else { + FileList p = allFiles.listForFile(includer); + if (isOuterFile(includer)) + outerFiles.addIfAbsent(p); + + if (includee.equals(plat.noGrandInclude())) { + p.setUseGrandInclude(false); + } else { + FileList q = allFiles.listForFile(includee); + p.addIfAbsent(q); + } + } + } + } + } while (line != null); + reader.close(); + + // Keep allFiles in well-known order so we can easily determine + // whether the known files are the same + allFiles.sortByName(); + + // Add first and last files differently to prevent a mistake + // in ordering in the include databases from breaking the + // error reporting in the VM. + if (firstFile != null) { + FileList p = allFiles.listForFile(firstFile); + allFiles.setFirstFile(p); + outerFiles.setFirstFile(p); + } + + if (lastFile != null) { + FileList p = allFiles.listForFile(lastFile); + allFiles.setLastFile(p); + outerFiles.setLastFile(p); + } + } + + public void compute() { + System.out.println("\tcomputing closures\n"); + // build both indiv and grand results + for (Iterator iter = outerFiles.iterator(); iter.hasNext(); ) { + indivIncludes.add(((FileList) iter.next()).doCFile()); + ++nOuterFiles; + } + + if (!plat.haveGrandInclude()) + return; // nothing in grand include + + // count how many times each include is included & add em to grand + for (Iterator iter = indivIncludes.iterator(); iter.hasNext(); ) { + FileList indivInclude = (FileList) iter.next(); + if (!indivInclude.getUseGrandInclude()) { + continue; // do not bump count if my files cannot be + // in grand include + } + indivInclude.doFiles(grandInclude); // put em on + // grand_include list + for (Iterator incListIter = indivInclude.iterator(); + incListIter.hasNext(); ) { + ((FileList) incListIter.next()).incrementCount(); + } + } + } + + // Not sure this is necessary in Java + public void verify() { + for (Iterator iter = indivIncludes.iterator(); iter.hasNext(); ) { + if (iter.next() == null) { + plat.abort(); + } + } + } + + public void put() throws IOException { + writeIndividualIncludes(); + + if (plat.haveGrandInclude()) + writeGrandInclude(); + + writeGrandUnixMakefile(); + } + + private void writeIndividualIncludes() throws IOException { + System.out.println("\twriting individual include files\n"); + + for (Iterator iter = indivIncludes.iterator(); iter.hasNext(); ) { + FileList list = (FileList) iter.next(); + System.out.println("\tcreating " + list.getName()); + list.putInclFile(this); + } + } + + private void writeGrandInclude() throws IOException { + System.out.println("\twriting grand include file\n"); + PrintWriter inclFile = + new PrintWriter(new FileWriter(plat.getGIFileTemplate().dirPreStemSuff())); + plat.writeGIPragma(inclFile); + for (Iterator iter = grandInclude.iterator(); iter.hasNext(); ) { + FileList list = (FileList) iter.next(); + if (list.getCount() >= threshold) { + inclFile.println("# include \"" + + plat.getGIFileTemplate().getInvDir() + + list.getName() + + "\""); + nPrecompiledFiles += 1; + } + } + inclFile.println(); + inclFile.close(); + } + + private void writeGrandUnixMakefile() throws IOException { + if (!plat.writeDeps()) + return; + + System.out.println("\twriting dependencies file\n"); + PrintWriter gd = + new PrintWriter(new FileWriter( + plat.getGDFileTemplate().dirPreStemSuff()) + ); + gd.println("# generated by makeDeps"); + gd.println(); + + + // HACK ALERT. The compilation of ad_ files is very slow. + // We want to start compiling them as early as possible. The compilation + // order on unix is dependant on the order we emit files here. + // By sorting the output before emitting it, we expect + // that ad_ will be compiled early. + boolean shouldSortObjFiles = true; + + if (shouldSortObjFiles) { + ArrayList sortList = new ArrayList(); + + // We need to preserve the ordering of the first and last items + // in outerFiles. + int size = outerFiles.size() - 1; + String firstName = removeSuffixFrom(((FileList)outerFiles.get(0)).getName()); + String lastName = removeSuffixFrom(((FileList)outerFiles.get(size)).getName()); + + for (int i=1; i 0) { + // write Precompiled_Files = ... + gd.println("Precompiled_Files = \\"); + for (Iterator iter = grandInclude.iterator(); iter.hasNext(); ) { + FileList list = (FileList) iter.next(); + gd.println(list.getName() + " \\"); + } + gd.println(); + gd.println(); + } + + gd.println("DTraced_Files = \\"); + for (Iterator iter = outerFiles.iterator(); iter.hasNext(); ) { + FileList anOuterFile = (FileList) iter.next(); + + if (anOuterFile.hasListForFile("dtrace.hpp")) { + String stemName = removeSuffixFrom(anOuterFile.getName()); + gd.println(stemName + plat.objFileSuffix() + " \\"); + } + } + gd.println(); + gd.println(); + + { + // write each dependency + + for (Iterator iter = indivIncludes.iterator(); iter.hasNext(); ) { + + FileList anII = (FileList) iter.next(); + + String stemName = removeSuffixFrom(anII.getName()); + String inclFileName = + plat.getInclFileTemplate().copyStem(anII.getName()). + preStemSuff(); + + gd.println(stemName + plat.objFileSuffix() + " " + + stemName + plat.asmFileSuffix() + ": \\"); + + printDependentOn(gd, anII.getName()); + // this gets the include file that includes all that + // this file needs (first level) since nested includes + // are skipped to avoid cycles. + printDependentOn(gd, inclFileName); + + if ( plat.haveGrandInclude() ) { + printDependentOn(gd, + plat.getGIFileTemplate().preStemSuff()); + } + + for (Iterator iiIter = anII.iterator(); iiIter.hasNext(); ) { + FileList hfile = (FileList) iiIter.next(); + if (!hfileIsInGrandInclude(hfile, anII) || + plat.writeDependenciesOnHFilesFromGI()) { + printDependentOn(gd, hfile.getName()); + } + if (platformFiles.hasListForFile(hfile.getName())) { + FileList p = + platformFiles.listForFile(hfile.getName());; + for (Iterator hiIter = p.iterator(); + hiIter.hasNext(); ) { + FileList hi2 = (FileList) hiIter.next(); + if (!hfileIsInGrandInclude(hi2, p)) { + printDependentOn(gd, hi2.getName()); + } + } + } + } + + if (plat.includeGIDependencies() + && nPrecompiledFiles > 0 + && anII.getUseGrandInclude()) { + gd.println(" $(Precompiled_Files) \\"); + } + gd.println(); + gd.println(); + } + } + + gd.close(); + } + + public void putDiffs(Database previous) throws IOException { + System.out.println("\tupdating output files\n"); + + if (!indivIncludes.compareLists(previous.indivIncludes) + || !grandInclude.compareLists(previous.grandInclude)) { + System.out.println("The order of .c or .s has changed, or " + + "the grand include file has changed."); + put(); + return; + } + + Iterator curIter = indivIncludes.iterator(); + Iterator prevIter = previous.indivIncludes.iterator(); + + try { + while (curIter.hasNext()) { + FileList newCFileList = (FileList) curIter.next(); + FileList prevCFileList = (FileList) prevIter.next(); + if (!newCFileList.compareLists(prevCFileList)) { + System.out.println("\tupdating " + newCFileList.getName()); + newCFileList.putInclFile(this); + } + } + } + catch (Exception e) { + throw new InternalError("assertion failure: cur and prev " + + "database lists changed unexpectedly."); + } + + writeGrandUnixMakefile(); + } + + private void printDependentOn(PrintWriter gd, String name) { + gd.print(" "); + gd.print(plat.dependentPrefix() + name); + } + + private boolean isOuterFile(String s) { + int len = s.length(); + String[] suffixes = plat.outerSuffixes(); + for (int i = 0; i < suffixes.length; i++) { + String suffix = suffixes[i]; + int suffLen = suffix.length(); + if ((len >= suffLen) && + (plat.fileNameStringEquality(s.substring(len - suffLen), + suffix))) { + return true; + } + } + return false; + } + + private String removeSuffixFrom(String s) { + int idx = s.lastIndexOf('.'); + if (idx <= 0) + plat.abort(); + return s.substring(0, idx); + } +} diff --git a/hotspot/src/share/tools/MakeDeps/DirectoryTree.java b/hotspot/src/share/tools/MakeDeps/DirectoryTree.java new file mode 100644 index 00000000000..ea9040f5e9b --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/DirectoryTree.java @@ -0,0 +1,257 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/** Encapsulates a notion of a directory tree. Designed to allow fast + querying of full paths for unique filenames in the hierarchy. */ + +import java.io.*; +import java.util.*; + +public class DirectoryTree { + + /** The root of the read directoryTree */ + private Node rootNode; + + /** Subdirs to ignore; Vector of Strings */ + private Vector subdirsToIgnore; + + /** This maps file names to Lists of nodes. */ + private Hashtable nameToNodeListTable; + + /** Output "."'s as directories are read. Defaults to false. */ + private boolean verbose; + + public DirectoryTree() { + subdirsToIgnore = new Vector(); + verbose = false; + } + + /** Takes an absolute path to the root directory of this + DirectoryTree. Throws IllegalArgumentException if the given + string represents a plain file or nonexistent directory. */ + + public DirectoryTree(String baseDirectory) { + this(); + readDirectory(baseDirectory); + } + + public void addSubdirToIgnore(String subdir) { + subdirsToIgnore.add(subdir); + } + + /** Output "."'s to System.out as directories are read. Defaults + to false. */ + public void setVerbose(boolean newValue) { + verbose = newValue; + } + + public boolean getVerbose() { + return verbose; + } + + public String getRootNodeName() { + return rootNode.getName(); + } + + /** Takes an absolute path to the root directory of this + DirectoryTree. Throws IllegalArgumentException if the given + string represents a plain file or nonexistent directory. */ + + public void readDirectory(String baseDirectory) + throws IllegalArgumentException { + File root = new File(baseDirectory); + if (!root.isDirectory()) { + throw new IllegalArgumentException("baseDirectory \"" + + baseDirectory + + "\" does not exist or " + + "is not a directory"); + } + try { + root = root.getCanonicalFile(); + } + catch (IOException e) { + throw new RuntimeException(e.toString()); + } + rootNode = new Node(root); + readDirectory(rootNode, root); + } + + /** Queries the DirectoryTree for a file or directory name. Takes + only the name of the file or directory itself (i.e., no parent + directory information should be in the passed name). Returns a + List of DirectoryTreeNodes specifying the full paths of all of + the files or directories of this name in the DirectoryTree. + Returns null if the directory tree has not been read from disk + yet or if the file was not found in the tree. */ + public List findFile(String name) { + if (rootNode == null) { + return null; + } + + if (nameToNodeListTable == null) { + nameToNodeListTable = new Hashtable(); + try { + buildNameToNodeListTable(rootNode); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + return (List) nameToNodeListTable.get(name); + } + + private void buildNameToNodeListTable(Node curNode) + throws IOException { + String fullName = curNode.getName(); + String parent = curNode.getParent(); + String separator = System.getProperty("file.separator"); + + if (parent != null) { + if (!fullName.startsWith(parent)) { + throw new RuntimeException( + "Internal error: parent of file name \"" + fullName + + "\" does not match file name \"" + parent + "\"" + ); + } + + int len = parent.length(); + if (!parent.endsWith(separator)) { + len += separator.length(); + } + + String fileName = fullName.substring(len); + + if (fileName == null) { + throw new RuntimeException( + "Internal error: file name was empty" + ); + } + + List nodeList = (List) nameToNodeListTable.get(fileName); + if (nodeList == null) { + nodeList = new Vector(); + nameToNodeListTable.put(fileName, nodeList); + } + + nodeList.add(curNode); + } else { + if (curNode != rootNode) { + throw new RuntimeException( + "Internal error: parent of file + \"" + fullName + "\"" + + " was null" + ); + } + } + + if (curNode.isDirectory()) { + Iterator iter = curNode.getChildren(); + if (iter != null) { + while (iter.hasNext()) { + buildNameToNodeListTable((Node) iter.next()); + } + } + } + } + + /** Reads all of the files in the given directory and adds them as + children of the directory tree node. Requires that the passed + node represents a directory. */ + + private void readDirectory(Node parentNode, File parentDir) { + File[] children = parentDir.listFiles(); + if (children == null) + return; + if (verbose) { + System.out.print("."); + System.out.flush(); + } + for (int i = 0; i < children.length; i++) { + File child = children[i]; + children[i] = null; + boolean isDir = child.isDirectory(); + boolean mustSkip = false; + if (isDir) { + for (Iterator iter = subdirsToIgnore.iterator(); + iter.hasNext(); ) { + if (child.getName().equals((String) iter.next())) { + mustSkip = true; + break; + } + } + } + if (!mustSkip) { + Node childNode = new Node(child); + parentNode.addChild(childNode); + if (isDir) { + readDirectory(childNode, child); + } + } + } + } + + private class Node implements DirectoryTreeNode { + private File file; + private Vector children; + + /** file must be a canonical file */ + Node(File file) { + this.file = file; + children = new Vector(); + } + + public boolean isFile() { + return file.isFile(); + } + + public boolean isDirectory() { + return file.isDirectory(); + } + + public String getName() { + return file.getPath(); + } + + public String getParent() { + return file.getParent(); + } + + public void addChild(Node n) { + children.add(n); + } + + public Iterator getChildren() throws IllegalArgumentException { + return children.iterator(); + } + + public int getNumChildren() throws IllegalArgumentException { + return children.size(); + } + + public DirectoryTreeNode getChild(int i) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + return (DirectoryTreeNode) children.get(i); + } + } +} diff --git a/hotspot/src/share/tools/MakeDeps/DirectoryTreeNode.java b/hotspot/src/share/tools/MakeDeps/DirectoryTreeNode.java new file mode 100644 index 00000000000..96eade615e3 --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/DirectoryTreeNode.java @@ -0,0 +1,36 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.util.*; + +public interface DirectoryTreeNode { + public boolean isFile(); + public boolean isDirectory(); + public String getName(); + public String getParent(); + public Iterator getChildren() throws IllegalArgumentException; + public int getNumChildren() throws IllegalArgumentException; + public DirectoryTreeNode getChild(int i) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException; +} diff --git a/hotspot/src/share/tools/MakeDeps/FileFormatException.java b/hotspot/src/share/tools/MakeDeps/FileFormatException.java new file mode 100644 index 00000000000..87f4c8b29ec --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/FileFormatException.java @@ -0,0 +1,33 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +public class FileFormatException extends Exception { + public FileFormatException() { + super(); + } + + public FileFormatException(String s) { + super(s); + } +} diff --git a/hotspot/src/share/tools/MakeDeps/FileList.java b/hotspot/src/share/tools/MakeDeps/FileList.java new file mode 100644 index 00000000000..1905cbb6cf1 --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/FileList.java @@ -0,0 +1,263 @@ +/* + * Copyright 1999-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.io.*; +import java.util.*; + +/** This class implements the java.util.List interface as well as + providing functionality specific to keeping track of lists of + files. See the documentation for the Database class to see how + these are used. Each FileList must only contain other FileLists + (although that is not currently enforced in the mutators). */ + +public class FileList extends Vector { + private String name; // (also the file name) + private boolean beenHere; + private boolean mayBeCycle; + private boolean isCycle; + /** Put in list because a file can refuse to */ + private boolean useGrandInclude; + private String platformDependentInclude; + private int count; + private Platform plat; + + public FileList(String n, Platform plat) { + super(); + this.plat = plat; + beenHere = mayBeCycle = isCycle = false; + platformDependentInclude = null; + name = n; + count = 0; + useGrandInclude = plat.haveGrandInclude(); + } + + // Change definition of equality from AbstractList so remove() works properly + public boolean equals(Object o) { + return ((Object) this) == o; + } + + // Necessary accessors + public String getName() { + return name; + } + + public void setPlatformDependentInclude(String arg) { + platformDependentInclude = arg; + } + + public String getPlatformDependentInclude() { + return platformDependentInclude; + } + + public boolean getUseGrandInclude() { + return useGrandInclude; + } + + public void setUseGrandInclude(boolean arg) { + useGrandInclude = arg; + } + + public void incrementCount() { + count++; + } + + public int getCount() { + return count; + } + + public FileList listForFile(String fileName) { + for (Iterator iter = iterator(); iter.hasNext(); ) { + FileList fl = (FileList) iter.next(); + if (plat.fileNameStringEquality(fl.name, fileName)) { + plat.fileNamePortabilityCheck(fl.name, fileName); + return fl; + } + } + plat.fileNamePortabilityCheck(fileName); + FileList newList = new FileList(fileName, plat); + add(newList); + return newList; + } + + public boolean hasListForFile(String fileName) { + for (Iterator iter = iterator(); iter.hasNext(); ) { + FileList fl = (FileList) iter.next(); + if (plat.fileNameStringEquality(fl.name, fileName)) { + plat.fileNamePortabilityCheck(fl.name, fileName); + return true; + } + } + return false; + } + + public boolean compareLists(FileList s) { + Iterator myIter = iterator(); + Iterator hisIter = s.iterator(); + + while (myIter.hasNext() && + hisIter.hasNext()) { + // crude: order dependent + FileList myElement = (FileList) myIter.next(); + FileList hisElement = (FileList) hisIter.next(); + if (!plat.fileNameStringEquality(myElement.name, + hisElement.name)) { + return false; + } + } + + if (myIter.hasNext() != hisIter.hasNext()) { + // One ended earlier + return false; + } + + return true; + } + + public void addIfAbsent(FileList s) { + for (Iterator iter = iterator(); iter.hasNext(); ) { + if (iter.next() == s) { + return; + } + } + add(s); + } + + public void sortByName() { + Collections.sort(this, new Comparator() { + public int compare(Object o1, Object o2) { + FileList fl1 = (FileList) o1; + FileList fl2 = (FileList) o2; + return fl1.getName().compareTo(fl2.getName()); + } + }); + } + + public void setFirstFile(FileList s) { + // Remove the file list if it's already here + remove(s); + add(0, s); + } + + public void setLastFile(FileList s) { + // Remove the file list if it's already here + remove(s); + add(s); + } + + public boolean doFiles(FileList s) { + boolean result = true; + for (Iterator iter = iterator(); iter.hasNext(); ) { + FileList h = (FileList) iter.next(); + if (h.platformDependentInclude != null) { + System.err.println("Error: the source for " + + h.platformDependentInclude + + " is " + h.name + "."); + System.err.println("\tIt shouldn't be included directly by " + + name + "."); + h.platformDependentInclude = null; // report once per file + result = false; + } + h.doHFile(s); + } + return result; + } + + public void traceCycle(FileList s) { + if (isCycle) // already traced + return; + isCycle = true; + System.err.println("\ttracing cycle for " + name); + // FIXME: must return status in caller routine + // exitCode = 1; + for (Iterator iter = iterator(); iter.hasNext(); ) { + FileList q = (FileList) iter.next(); + if (q.mayBeCycle) { + if (s == q) { + plat.fatalError("\tend of cycle for " + s.getName()); + } else { + q.traceCycle(s); + } + } + } + } + + public void doHFile(FileList s) { + if (beenHere) { + if (mayBeCycle) { + traceCycle(this); + } + return; + } + beenHere = true; + mayBeCycle = true; + doFiles(s); + mayBeCycle = false; + s.add(this); + } + + public FileList doCFile() { + FileList s = new FileList(name, plat); + s.useGrandInclude = useGrandInclude; // propagate this + doFiles(s); + for (Iterator iter = s.iterator(); iter.hasNext(); ) { + FileList l = (FileList) iter.next(); + l.beenHere = false; + } + return s; + } + + /** if .h file is included thresh times, put it in the grand + include file */ + public void putInclFile(Database db) + throws IOException { + boolean needline = true; + FileName inclName = plat.getInclFileTemplate().copyStem(name); + PrintWriter inclFile = + new PrintWriter(new FileWriter(inclName.dirPreStemSuff())); + if (plat.haveGrandInclude() && plat.includeGIInEachIncl()) { + inclFile.println("# include \"" + + plat.getGIFileTemplate().dirPreStemAltSuff() + + "\""); + needline = false; + } + for (Iterator iter = iterator(); iter.hasNext(); ) { + FileList hfile = (FileList) iter.next(); + if (!db.hfileIsInGrandInclude(hfile, this)) { + inclFile.println("# include \"" + + plat.getInclFileTemplate().getInvDir() + + hfile.name + + "\""); + needline = false; + } + } + + // Solaris C++ in strict mode warns about empty files + + if(needline) { + inclFile.println(); + } + + inclFile.close(); + } +} diff --git a/hotspot/src/share/tools/MakeDeps/FileName.java b/hotspot/src/share/tools/MakeDeps/FileName.java new file mode 100644 index 00000000000..773fdc84ebb --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/FileName.java @@ -0,0 +1,119 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +public class FileName { + private String dir; + private String prefix; + private String stem; + private String suffix; + private String inverseDir; + private String altSuffix; + + private String dpss; + private String psa; + private String dpsa; + private String pss; + + private Platform plat; + + /** None of the passed strings may be null. */ + + public FileName(Platform plat, String dir, String prefix, + String stem, String suffix, + String inverseDir, String altSuffix) { + if ((dir == null) || + (prefix == null) || + (stem == null) || + (suffix == null) || + (inverseDir == null) || + (altSuffix == null)) { + throw new NullPointerException("All arguments must be non-null"); + } + + this.plat = plat; + + this.dir = dir; + this.prefix = prefix; + this.stem = stem; + this.suffix = suffix; + this.inverseDir = inverseDir; + this.altSuffix = altSuffix; + + pss = prefix + stem + suffix; + dpss = dir + prefix + stem + suffix; + psa = prefix + stem + altSuffix; + dpsa = dir + prefix + stem + altSuffix; + + checkLength(plat); + } + + public void checkLength(Platform p) { + int len; + String s; + int suffLen = suffix.length(); + int altSuffLen = altSuffix.length(); + if (suffLen >= altSuffLen) { + len = suffLen; + s = suffix; + } else { + len = altSuffLen; + s = altSuffix; + } + len += prefix.length() + stem.length(); + int lim = p.fileNameLengthLimit(); + if (len > lim) { + p.fatalError(prefix + stem + s + " is too long: " + + len + " >= " + lim); + } + } + + public String dirPreStemSuff() { + return dpss; + } + + public String preStemSuff() { + return pss; + } + + public String dirPreStemAltSuff() { + return dpsa; + } + + public String preStemAltSuff() { + return psa; + } + + public FileName copyStem(String newStem) { + return new FileName(plat, dir, prefix, newStem, + suffix, inverseDir, altSuffix); + } + + String nameOfList() { + return stem; + } + + String getInvDir() { + return inverseDir; + } +} diff --git a/hotspot/src/share/tools/MakeDeps/Macro.java b/hotspot/src/share/tools/MakeDeps/Macro.java new file mode 100644 index 00000000000..4b4b948272a --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/Macro.java @@ -0,0 +1,28 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +public class Macro { + public String name; + public String contents; +} diff --git a/hotspot/src/share/tools/MakeDeps/MacroDefinitions.java b/hotspot/src/share/tools/MakeDeps/MacroDefinitions.java new file mode 100644 index 00000000000..c4452d8e723 --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/MacroDefinitions.java @@ -0,0 +1,256 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.io.*; +import java.util.*; + +public class MacroDefinitions { + private Vector macros; + + public MacroDefinitions() { + macros = new Vector(); + } + + private String lookup(String name) throws NoSuchElementException { + for (Iterator iter = macros.iterator(); iter.hasNext(); ) { + Macro macro = (Macro) iter.next(); + if (macro.name.equals(name)) { + return macro.contents; + } + } + throw new NoSuchElementException(name); + } + + public void addMacro(String name, String contents) { + Macro macro = new Macro(); + macro.name = name; + macro.contents = contents; + macros.add(macro); + } + + private boolean lineIsEmpty(String s) { + for (int i = 0; i < s.length(); i++) { + if (!Character.isWhitespace(s.charAt(i))) { + return false; + } + } + return true; + } + + public void readFrom(String fileName, boolean missingOk) + throws FileNotFoundException, FileFormatException, IOException { + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(fileName)); + } catch (FileNotFoundException e) { + if (missingOk) { + return; + } else { + throw(e); + } + } + String line; + do { + line = reader.readLine(); + if (line != null) { + // This had to be rewritten (compare to Database.java) + // because the Solaris platform file has been + // repurposed and now contains "macros" with spaces in + // them. + + if ((!line.startsWith("//")) && + (!lineIsEmpty(line))) { + int nameBegin = -1; + int nameEnd = -1; + boolean gotEquals = false; + int contentsBegin = -1; + int contentsEnd = -1; + + int i = 0; + // Scan forward for beginning of name + while (i < line.length()) { + if (!Character.isWhitespace(line.charAt(i))) { + break; + } + i++; + } + nameBegin = i; + + // Scan forward for end of name + while (i < line.length()) { + if (Character.isWhitespace(line.charAt(i))) { + break; + } + i++; + } + nameEnd = i; + + // Scan forward for equals sign + while (i < line.length()) { + if (line.charAt(i) == '=') { + gotEquals = true; + break; + } + i++; + } + + // Scan forward for start of contents + i++; + while (i < line.length()) { + if (!Character.isWhitespace(line.charAt(i))) { + break; + } + i++; + } + contentsBegin = i; + + // Scan *backward* for end of contents + i = line.length() - 1; + while (i >= 0) { + if (!Character.isWhitespace(line.charAt(i))) { + break; + } + } + contentsEnd = i+1; + + // Now do consistency check + if (!((nameBegin < nameEnd) && + (nameEnd < contentsBegin) && + (contentsBegin < contentsEnd) && + (gotEquals == true))) { + throw new FileFormatException( + "Expected \"macroname = value\", " + + "but found: " + line + ); + } + + String name = line.substring(nameBegin, nameEnd); + String contents = line.substring(contentsBegin, + contentsEnd); + addMacro(name, contents); + } + } + } while (line != null); + reader.close(); + } + + /** Throws IllegalArgumentException if passed token is illegally + formatted */ + public String expand(String token) + throws IllegalArgumentException { + // the token may contain one or more 's + + String out = ""; + + // emacs lingo + int mark = 0; + int point = 0; + + int len = token.length(); + + if (len == 0) + return out; + + do { + // Scan "point" forward until hitting either the end of + // the string or the beginning of a macro + if (token.charAt(point) == '<') { + // Append (point - mark) to out + if ((point - mark) != 0) { + out += token.substring(mark, point); + } + mark = point + 1; + // Scan forward from point for right bracket + point++; + while ((point < len) && + (token.charAt(point) != '>')) { + point++; + } + if (point == len) { + throw new IllegalArgumentException( + "Could not find right angle-bracket in token " + token + ); + } + String name = token.substring(mark, point); + if (name == null) { + throw new IllegalArgumentException( + "Empty macro in token " + token + ); + } + try { + String contents = lookup(name); + out += contents; + point++; + mark = point; + } catch (NoSuchElementException e) { + throw new IllegalArgumentException( + "Unknown macro " + name + " in token " + token + ); + } + } else { + point++; + } + } while (point != len); + + if (mark != point) { + out += token.substring(mark, point); + } + + return out; + } + + public MacroDefinitions copy() { + MacroDefinitions ret = new MacroDefinitions(); + for (Iterator iter = macros.iterator(); + iter.hasNext(); ) { + Macro orig = (Macro) iter.next(); + Macro macro = new Macro(); + macro.name = orig.name; + macro.contents = orig.contents; + ret.macros.add(macro); + } + return ret; + } + + public void setAllMacroBodiesTo(String s) { + for (Iterator iter = macros.iterator(); + iter.hasNext(); ) { + Macro macro = (Macro) iter.next(); + macro.contents = s; + } + } + + /** This returns an Iterator of Macros. You should not mutate the + returned Macro objects or use the Iterator to remove + macros. */ + public Iterator getMacros() { + return macros.iterator(); + } + + private void error(String text) throws FileFormatException { + throw new FileFormatException( + "Expected \"macroname = value\", but found: " + text + ); + } +} diff --git a/hotspot/src/share/tools/MakeDeps/MakeDeps.java b/hotspot/src/share/tools/MakeDeps/MakeDeps.java new file mode 100644 index 00000000000..c4bf218d669 --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/MakeDeps.java @@ -0,0 +1,236 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This program reads an include file database. +// The database should cover each self .c and .h file, +// but not files in /usr/include +// The database consists of pairs of nonblank words, where the first word is +// the filename that needs to include the file named by the second word. +// For each .c file, this program generates a fooIncludes.h file that +// the .c file may include to include all the needed files in the right order. +// It also generates a foo.dep file to include in the makefile. +// Finally it detects cycles, and can work with two files, an old and a new one. +// To incrementally write out only needed files after a small change. +// +// Based on a suggestion by Roland Conybeare, algorithm suggested by Craig +// Chambers, written by David Ungar, 3/1/89. +// Added PREFIX, {DEP/INC}_DIR, smaller dep output 10/92 -Urs + +// Add something for precompiled headers + +// To handle different platforms, I am introducing a platform file. +// The platform file contains lines like: +// os = svr4 +// +// Then, when processing the includeDB file, a token such as +// gets replaced by svr4. -- dmu 3/25/97 + +// Modified to centralize Dependencies to speed up make -- dmu 5/97 + +public class MakeDeps { + + public static void usage() { + System.out.println("usage:"); + System.out.println("\tmakeDeps platform-name platform-file database-file [MakeDeps args] [platform args]"); + System.out.println("\tmakeDeps diffs platform-name old-platform-file old-database-file new-platform-file new-database-file [MakeDeps args] [platform args]"); + System.out.println("where platform-name is the name of a platform MakeDeps supports"); + System.out.println("(currently \"WinGammaPlatform\" or \"UnixPlatform\")"); + System.out.println("MakeDeps options:"); + System.out.println(" -firstFile [filename]: Specify the first file in link order (i.e.,"); + System.out.println(" to have a well-known function at the start of the output file)"); + System.out.println(" -lastFile [filename]: Specify the last file in link order (i.e.,"); + System.out.println(" to have a well-known function at the end of the output file)"); + System.err.println("WinGammaPlatform platform-specific options:"); + System.err.println(" -sourceBase "); + System.err.println(" -dspFileName "); + System.err.println(" -envVar "); + System.err.println(" -dllLoc "); + System.err.println(" If any of the above are specified, "+ + "they must all be."); + System.err.println(" Additional, optional arguments, which can be " + + "specified multiple times:"); + System.err.println(" -absoluteInclude "); + System.err.println(" -relativeInclude "); + System.err.println(" -define "); + System.err.println(" -perFileLine "); + System.err.println(" -conditionalPerFileLine "); + System.err.println(" (NOTE: To work around a bug in nmake, where " + + "you can't have a '#' character in a quoted " + + "string, all of the lines outputted have \"#\"" + + "prepended)"); + System.err.println(" -startAt "); + System.err.println(" -ignoreFile "); + System.err.println(" -additionalFile "); + System.err.println(" -additionalGeneratedFile " + + ""); + System.err.println(" -prelink :"); + System.err.println(" Generate a set of prelink commands for the given BUILD"); + System.err.println(" (\"Debug\" or \"Release\"). The prelink description and commands"); + System.err.println(" are both quoted strings."); + System.err.println(" Default includes: \".\""); + System.err.println(" Default defines: WIN32, _WINDOWS, \"HOTSPOT_BUILD_USER=$(USERNAME)\""); + } + + public static void main(String[] args) { + try { + if (args.length < 3) { + usage(); + System.exit(1); + } + + int argc = 0; + boolean diffMode = false; + if (args[argc].equals("diffs")) { + diffMode = true; + ++argc; + } + + String platformName = args[argc++]; + Class platformClass = Class.forName(platformName); + + String plat1 = null; + String db1 = null; + String plat2 = null; + String db2 = null; + + String firstFile = null; + String lastFile = null; + + int numOptionalArgs = + (diffMode ? (args.length - 6) : (args.length - 3)); + if (numOptionalArgs < 0) { + usage(); + System.exit(1); + } + + plat1 = args[argc++]; + db1 = args[argc++]; + + if (diffMode) { + plat2 = args[argc++]; + db2 = args[argc++]; + } + + // argc now points at start of optional arguments, if any + + try { + boolean gotOne = true; + while (gotOne && (argc < args.length - 1)) { + gotOne = false; + String arg = args[argc]; + if (arg.equals("-firstFile")) { + firstFile = args[argc + 1]; + argc += 2; + gotOne = true; + } else if (arg.equals("-lastFile")) { + lastFile = args[argc + 1]; + argc += 2; + gotOne = true; + } + } + } + catch (Exception e) { + e.printStackTrace(); + usage(); + System.exit(1); + } + + Platform platform = (Platform) platformClass.newInstance(); + platform.setupFileTemplates(); + long t = platform.defaultGrandIncludeThreshold(); + + String[] platformArgs = null; + int numPlatformArgs = args.length - argc; + if (numPlatformArgs > 0) { + platformArgs = new String[numPlatformArgs]; + int offset = argc; + while (argc < args.length) { + platformArgs[argc - offset] = args[argc]; + ++argc; + } + } + + // If you want to change the threshold, change the default + // "grand include" threshold in Platform.java, or override + // it in the platform-specific file like UnixPlatform.java + + Database previous = new Database(platform, t); + Database current = new Database(platform, t); + + previous.canBeMissing(); + + if (firstFile != null) { + previous.setFirstFile(firstFile); + current.setFirstFile(firstFile); + } + if (lastFile != null) { + previous.setLastFile(lastFile); + current.setLastFile(lastFile); + } + + if (diffMode) { + System.out.println("Old database:"); + previous.get(plat1, db1); + previous.compute(); + System.out.println("New database:"); + current.get(plat2, db2); + current.compute(); + System.out.println("Deltas:"); + current.putDiffs(previous); + } else { + System.out.println("New database:"); + current.get(plat1, db1); + current.compute(); + current.put(); + } + + if (platformArgs != null) { + // Allow the platform to write platform-specific files + platform.writePlatformSpecificFiles(previous, current, + platformArgs); + } + } + catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/hotspot/src/share/tools/MakeDeps/MetroWerksMacPlatform.java b/hotspot/src/share/tools/MakeDeps/MetroWerksMacPlatform.java new file mode 100644 index 00000000000..0ee6dab5f3e --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/MetroWerksMacPlatform.java @@ -0,0 +1,70 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.io.*; + +public class MetroWerksMacPlatform extends Platform { + public void setupFileTemplates() { + inclFileTemplate = new FileName(this, + ":incls:", "_", "", ".incl", "", "" + ); + giFileTemplate = new FileName(this, + "", "", "precompiledHeader", ".pch", "", "" + ); + gdFileTemplate = dummyFileTemplate; + } + + private static String[] suffixes = { ".cpp", ".c", ".s" }; + + public String[] outerSuffixes() { + return suffixes; + } + + public boolean includeGIInEachIncl() { + return true; + } + + public int defaultGrandIncludeThreshold() { + return 150; + } + + public void writeGIPragma(PrintWriter out) { + out.println("#pragma precompile_target \"" + + giFileTemplate.preStemAltSuff() + + "\""); + out.println(); + } + + public String objFileSuffix() { + throw new RuntimeException("Unimplemented in original makeDeps"); + } + + public String asmFileSuffix() { + throw new RuntimeException("Unimplemented in original makeDeps"); + } + + public String dependentPrefix() { + throw new RuntimeException("Unimplemented in original makeDeps"); + } +} diff --git a/hotspot/src/share/tools/MakeDeps/Platform.java b/hotspot/src/share/tools/MakeDeps/Platform.java new file mode 100644 index 00000000000..a587d5222a7 --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/Platform.java @@ -0,0 +1,185 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/** Defines what must be specified for each platform. This class must + have a no-arg constructor. */ + +import java.io.*; + +public abstract class Platform { + /** file name templates capture naming conventions */ + protected FileName dummyFileTemplate = + new FileName(this, "", "", "", "", "", ""); + + // The next three must be instantiated in subclasses' constructors + + /** An incl file is produced per .c file and contains all the + includes it needs */ + protected FileName inclFileTemplate; + + /** A GI (grand-include) file has any file used more than N times + for precompiled headers */ + protected FileName giFileTemplate; + + /** A GD (grand-dependencies) file that tells Unix make all the + .o's needed for linking and the include dependencies */ + protected FileName gdFileTemplate; + + // Accessors + public FileName getInclFileTemplate() { + return inclFileTemplate; + } + + public FileName getGIFileTemplate() { + return giFileTemplate; + } + + public FileName getGDFileTemplate() { + return gdFileTemplate; + } + + // an incl file is the file included by each.c file that includes + // all needed header files + + public abstract void setupFileTemplates(); + public abstract String[] outerSuffixes(); + + /** empty file name -> no grand include file */ + public boolean haveGrandInclude() { + return (giFileTemplate.nameOfList().length() > 0); + } + + public boolean writeDeps() { + return (gdFileTemplate.nameOfList().length() > 0); + } + + /**

A gi file is the grand-include file. It includes in one + file any file that is included more than a certain number of + times.

+ +

It is used for precompiled header files.

+ +

It has a source name, that is the file that this program + generates, and a compiled name; that is the file that is + included by other files.

+ +

Some platforms have this program actually explictly + include the preprocessed gi file-- see includeGIInEachIncl(). +

+ +

Also, some platforms need a pragma in the GI file.

*/ + public boolean includeGIInEachIncl() { + return false; + } + + /** For some platforms, e.g. Solaris, include the grand-include + dependencies in the makefile. For others, e.g. Windows, do + not. */ + public boolean includeGIDependencies() { + return false; + } + + /** Should C/C++ source file be dependent on a file included + into the grand-include file. */ + public boolean writeDependenciesOnHFilesFromGI() { + return false; + } + + /** Default implementation does nothing */ + public void writeGIPragma(PrintWriter out) { + } + + /** A line with a filename and the noGrandInclude string means + that this file cannot use the precompiled header. */ + public String noGrandInclude() { + return "no_precompiled_headers"; + } + + /** A line with a filename and the + generatePlatformDependentInclude means that an include file + for the header file must be generated. This file generated include + file is directly included by the non-platform dependent include file + (e.g os.hpp includes _os_pd.hpp.incl. So while we notice files that + are directly dependent on non-platform dependent files from the database + we must infer the dependence on platform specific files to generate correct + dependences on the platform specific files. */ + public String generatePlatformDependentInclude() { + return "generate_platform_dependent_include"; + } + + /** Prefix and suffix strings for emitting Makefile rules */ + public abstract String objFileSuffix(); + public abstract String asmFileSuffix(); + public abstract String dependentPrefix(); + + // Exit routines: + + /** Abort means an internal error */ + public void abort() { + throw new RuntimeException("Internal error"); + } + + /** fatalError is used by clients to stop the system */ + public void fatalError(String msg) { + System.err.println(msg); + System.exit(1); + } + + /** Default implementation performs case-sensitive comparison */ + public boolean fileNameStringEquality(String s1, String s2) { + return s1.equals(s2); + } + + public void fileNamePortabilityCheck(String name) { + if (Character.isUpperCase(name.charAt(0))) { + fatalError("Error: for the sake of portability we have chosen\n" + + "to avoid files starting with an uppercase letter.\n" + + "Please rename " + name + "."); + } + } + + public void fileNamePortabilityCheck(String name, String matchingName) { + if (!name.equals(matchingName)) { + fatalError("Error: file " + name + " also appears as " + + matchingName + ". Case must be consistent for " + + "portability."); + } + } + + /** max is 31 on mac, so warn */ + public int fileNameLengthLimit() { + return 45; + } + + public int defaultGrandIncludeThreshold() { + return 30; + } + + /** Not very general, but this is a way to get platform-specific + files to be written. Default implementation does nothing. */ + public void writePlatformSpecificFiles(Database previousDB, + Database currentDB, String[] args) + throws IllegalArgumentException, IOException { + } +} diff --git a/hotspot/src/share/tools/MakeDeps/UnixPlatform.java b/hotspot/src/share/tools/MakeDeps/UnixPlatform.java new file mode 100644 index 00000000000..1c4d834464a --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/UnixPlatform.java @@ -0,0 +1,80 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +public class UnixPlatform extends Platform { + public void setupFileTemplates() { + inclFileTemplate = new FileName(this, + "incls/", "_", "", ".incl", "", "" + ); + giFileTemplate = new FileName(this, + "incls/", "", "_precompiled", ".incl", "", "" + ); + gdFileTemplate = new FileName(this, + "", "", "Dependencies", "", "", "" + ); + } + + private static String[] suffixes = { ".cpp", ".c", ".s" }; + + public String[] outerSuffixes() { + return suffixes; + } + + public String objFileSuffix() { + return ".o"; + } + + public String asmFileSuffix() { + return ".i"; + } + + public String dependentPrefix() { + return ""; + } + + /** Do not change this; unless you fix things so precompiled + header files get translated into make dependencies. - Ungar */ + public int defaultGrandIncludeThreshold() { + if (System.getProperty("USE_PRECOMPILED_HEADER") != null) + return 30; + else + return 1 << 30; + } + + /** For Unix make, include the dependencies for precompiled header + files. */ + public boolean includeGIDependencies() { + return false; + } + + /** Should C/C++ source file be dependent on a file included + into the grand-include file. + On Unix with precompiled headers we don't want each file to be + dependent on grand-include file. Instead each C/C++ source file + is depended on each own set of files, and recompiled only when + files from this set are changed. */ + public boolean writeDependenciesOnHFilesFromGI() { + return System.getProperty("USE_PRECOMPILED_HEADER") != null; + } +} diff --git a/hotspot/src/share/tools/MakeDeps/Util.java b/hotspot/src/share/tools/MakeDeps/Util.java new file mode 100644 index 00000000000..af93e94a1d1 --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/Util.java @@ -0,0 +1,88 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.util.*; +import java.io.File; + +public class Util { + static String join(String padder, Vector v) { + return join(padder, v, false); + } + + static String join(String padder, Vector v, boolean quoted) { + StringBuffer sb = new StringBuffer(); + + for (Iterator iter = v.iterator(); iter.hasNext(); ) { + if (quoted) { + sb.append('"'); + } + sb.append((String)iter.next()); + if (quoted) { + sb.append('"'); + } + if (iter.hasNext()) sb.append(padder); + } + + return sb.toString(); + } + + static String join(String padder, String v[]) { + StringBuffer sb = new StringBuffer(); + + for (int i=0; i"); + System.err.println(" -projectFileName "); + System.err.println(" If any of the above are specified, "+ + "they must all be."); + System.err.println(" Additional, optional arguments, which can be " + + "specified multiple times:"); + System.err.println(" -absoluteInclude "); + System.err.println(" -relativeInclude "); + System.err.println(" -define "); + System.err.println(" -startAt "); + System.err.println(" -additionalFile "); + System.err.println(" -additionalGeneratedFile " + + ""); + throw new IllegalArgumentException(); + } + + + public void addPerFileLine(Hashtable table, + String fileName, + String line) { + Vector v = (Vector) table.get(fileName); + if (v != null) { + v.add(line); + } else { + v = new Vector(); + v.add(line); + table.put(fileName, v); + } + } + + protected static class PerFileCondData { + public String releaseString; + public String debugString; + } + + protected void addConditionalPerFileLine(Hashtable table, + String fileName, + String releaseLine, + String debugLine) { + PerFileCondData data = new PerFileCondData(); + data.releaseString = releaseLine; + data.debugString = debugLine; + Vector v = (Vector) table.get(fileName); + if (v != null) { + v.add(data); + } else { + v = new Vector(); + v.add(data); + table.put(fileName, v); + } + } + + protected static class PrelinkCommandData { + String description; + String commands; + } + + protected void addPrelinkCommand(Hashtable table, + String build, + String description, + String commands) { + PrelinkCommandData data = new PrelinkCommandData(); + data.description = description; + data.commands = commands; + table.put(build, data); + } + + public boolean findString(Vector v, String s) { + for (Iterator iter = v.iterator(); iter.hasNext(); ) { + if (((String) iter.next()).equals(s)) { + return true; + } + } + + return false; + } + + /* This returns a String containing the full path to the passed + file name, or null if an error occurred. If the file was not + found or was a duplicate and couldn't be resolved using the + preferred paths, the file name is added to the appropriate + Vector of Strings. */ + private String findFileInDirectory(String fileName, + DirectoryTree directory, + Vector preferredPaths, + Vector filesNotFound, + Vector filesDuplicate) { + List locationsInTree = directory.findFile(fileName); + int rootNameLength = directory.getRootNodeName().length(); + String name = null; + if ((locationsInTree == null) || + (locationsInTree.size() == 0)) { + filesNotFound.add(fileName); + } else if (locationsInTree.size() > 1) { + // We shouldn't have duplicate file names in our workspace. + System.err.println(); + System.err.println("There are multiple files named as: " + fileName); + System.exit(-1); + // The following code could be safely removed if we don't need duplicate + // file names. + + // Iterate through them, trying to find one with a + // preferred path + search: + { + for (Iterator locIter = locationsInTree.iterator(); + locIter.hasNext(); ) { + DirectoryTreeNode node = + (DirectoryTreeNode) locIter.next(); + String tmpName = node.getName(); + for (Iterator prefIter = preferredPaths.iterator(); + prefIter.hasNext(); ) { + // We need to make sure the preferred path is + // found from the file path not including the root node name. + if (tmpName.indexOf((String)prefIter.next(), + rootNameLength) != -1) { + name = tmpName; + break search; + } + } + } + } + + if (name == null) { + filesDuplicate.add(fileName); + } + } else { + name = ((DirectoryTreeNode) locationsInTree.get(0)).getName(); + } + + return name; + } + + protected boolean databaseAllFilesEqual(Database previousDB, + Database currentDB) { + Iterator i1 = previousDB.getAllFiles().iterator(); + Iterator i2 = currentDB.getAllFiles().iterator(); + + while (i1.hasNext() && i2.hasNext()) { + FileList fl1 = (FileList) i1.next(); + FileList fl2 = (FileList) i2.next(); + if (!fl1.getName().equals(fl2.getName())) { + return false; + } + } + + if (i1.hasNext() != i2.hasNext()) { + // Different lengths + return false; + } + + return true; + } + + protected String envVarPrefixedFileName(String fileName, + int sourceBaseLen, + DirectoryTree tree, + Vector preferredPaths, + Vector filesNotFound, + Vector filesDuplicate) { + String fullName = findFileInDirectory(fileName, + tree, + preferredPaths, + filesNotFound, + filesDuplicate); + return fullName; + } + + String getProjectName(String fullPath, String extension) + throws IllegalArgumentException, IOException { + File file = new File(fullPath).getCanonicalFile(); + fullPath = file.getCanonicalPath(); + String parent = file.getParent(); + + if (!fullPath.endsWith(extension)) { + throw new IllegalArgumentException("project file name \"" + + fullPath + + "\" does not end in "+extension); + } + + if ((parent != null) && + (!fullPath.startsWith(parent))) { + throw new RuntimeException( + "Internal error: parent of file name \"" + parent + + "\" does not match file name \"" + fullPath + "\"" + ); + } + + int len = parent.length(); + if (!parent.endsWith(Util.sep)) { + len += Util.sep.length(); + } + + int end = fullPath.length() - extension.length(); + + if (len == end) { + throw new RuntimeException( + "Internal error: file name was empty" + ); + } + + return fullPath.substring(len, end); + } + + protected abstract String getProjectExt(); + + public void writePlatformSpecificFiles(Database previousDB, + Database currentDB, String[] args) + throws IllegalArgumentException, IOException { + + parseArguments(args); + + String projectFileName = BuildConfig.getFieldString(null, "ProjectFileName"); + String ext = getProjectExt(); + + // Compare contents of allFiles of previousDB and includeDB. + // If these haven't changed, then skip writing the .vcproj file. + if (false && databaseAllFilesEqual(previousDB, currentDB) && + new File(projectFileName).exists()) { + System.out.println( + " Databases unchanged; skipping overwrite of "+ext+" file." + ); + return; + } + + String projectName = getProjectName(projectFileName, ext); + + writeProjectFile(projectFileName, projectName, createAllConfigs()); + } + + protected void writePrologue(String[] args) { + System.err.println("WinGammaPlatform platform-specific arguments:"); + for (int i = 0; i < args.length; i++) { + System.err.print(args[i] + " "); + } + System.err.println(); + } + + + void setInclFileTemplate(FileName val) { + this.inclFileTemplate = val; + } + + void setGIFileTemplate(FileName val) { + this.giFileTemplate = val; + } + + + void parseArguments(String[] args) { + new ArgsParser(args, + new ArgRule[] + { + new HsArgRule("-sourceBase", + "SourceBase", + " (Did you set the HotSpotWorkSpace environment variable?)", + HsArgHandler.STRING + ), + + new HsArgRule("-buildBase", + "BuildBase", + " (Did you set the HotSpotBuildSpace environment variable?)", + HsArgHandler.STRING + ), + + new HsArgRule("-projectFileName", + "ProjectFileName", + null, + HsArgHandler.STRING + ), + + new HsArgRule("-jdkTargetRoot", + "JdkTargetRoot", + " (Did you set the HotSpotJDKDist environment variable?)", + HsArgHandler.STRING + ), + + new HsArgRule("-compiler", + "CompilerVersion", + " (Did you set the VcVersion correctly?)", + HsArgHandler.STRING + ), + + new HsArgRule("-platform", + "Platform", + null, + HsArgHandler.STRING + ), + + new HsArgRule("-absoluteInclude", + "AbsoluteInclude", + null, + HsArgHandler.VECTOR + ), + + new HsArgRule("-relativeInclude", + "RelativeInclude", + null, + HsArgHandler.VECTOR + ), + + new HsArgRule("-define", + "Define", + null, + HsArgHandler.VECTOR + ), + + new HsArgRule("-useToGeneratePch", + "UseToGeneratePch", + null, + HsArgHandler.STRING + ), + + new ArgRuleSpecific("-perFileLine", + new HsArgHandler() { + public void handle(ArgIterator it) { + String cfg = getCfg(it.get()); + if (nextNotKey(it)) { + String fileName = it.get(); + if (nextNotKey(it)) { + String line = it.get(); + BuildConfig.putFieldHash(cfg, "PerFileLine", fileName, line); + it.next(); + return; + } + } + empty(null, "** Error: wrong number of args to -perFileLine"); + } + } + ), + + new ArgRuleSpecific("-conditionalPerFileLine", + new HsArgHandler() { + public void handle(ArgIterator it) { + String cfg = getCfg(it.get()); + if (nextNotKey(it)) { + String fileName = it.get(); + if (nextNotKey(it)) { + String productLine = it.get(); + if (nextNotKey(it)) { + String debugLine = it.get(); + BuildConfig.putFieldHash(cfg+"_debug", "CondPerFileLine", + fileName, debugLine); + BuildConfig.putFieldHash(cfg+"_product", "CondPerFileLine", + fileName, productLine); + it.next(); + return; + } + } + } + + empty(null, "** Error: wrong number of args to -conditionalPerFileLine"); + } + } + ), + + new HsArgRule("-disablePch", + "DisablePch", + null, + HsArgHandler.HASH + ), + + new ArgRule("-startAt", + new HsArgHandler() { + public void handle(ArgIterator it) { + if (BuildConfig.getField(null, "StartAt") != null) { + empty(null, "** Error: multiple -startAt"); + } + if (nextNotKey(it)) { + BuildConfig.putField(null, "StartAt", it.get()); + it.next(); + } else { + empty("-startAt", null); + } + } + } + ), + + new HsArgRule("-ignoreFile", + "IgnoreFile", + null, + HsArgHandler.HASH + ), + + new HsArgRule("-additionalFile", + "AdditionalFile", + null, + HsArgHandler.VECTOR + ), + + new ArgRuleSpecific("-additionalGeneratedFile", + new HsArgHandler() { + public void handle(ArgIterator it) { + String cfg = getCfg(it.get()); + if (nextNotKey(it)) { + String dir = it.get(); + if (nextNotKey(it)) { + String fileName = it.get(); + // we ignore files that we know are generated, so we coudn't + // find them in sources + BuildConfig.putFieldHash(cfg, "IgnoreFile", fileName, "1"); + BuildConfig.putFieldHash(cfg, "AdditionalGeneratedFile", + Util.normalize(dir + Util.sep + fileName), + fileName); + it.next(); + return; + } + } + empty(null, "** Error: wrong number of args to -additionalGeneratedFile"); + } + } + ), + + new HsArgRule("-includeDB", + "IncludeDB", + null, + HsArgHandler.STRING + ), + + new ArgRule("-prelink", + new HsArgHandler() { + public void handle(ArgIterator it) { + if (nextNotKey(it)) { + String build = it.get(); + if (nextNotKey(it)) { + String description = it.get(); + if (nextNotKey(it)) { + String command = it.get(); + BuildConfig.putField(null, "PrelinkDescription", description); + BuildConfig.putField(null, "PrelinkCommand", command); + it.next(); + return; + } + } + } + + empty(null, "** Error: wrong number of args to -prelink"); + } + } + ) + }, + new ArgHandler() { + public void handle(ArgIterator it) { + + throw new RuntimeException("Arg Parser: unrecognized option "+it.get()); + } + } + ); + if (BuildConfig.getField(null, "SourceBase") == null || + BuildConfig.getField(null, "BuildBase") == null || + BuildConfig.getField(null, "ProjectFileName") == null || + BuildConfig.getField(null, "CompilerVersion") == null) { + usage(); + } + + if (BuildConfig.getField(null, "UseToGeneratePch") == null) { + throw new RuntimeException("ERROR: need to specify one file to compute PCH, with -useToGeneratePch flag"); + } + + BuildConfig.putField(null, "PlatformObject", this); + } + + Vector createAllConfigs() { + Vector allConfigs = new Vector(); + + allConfigs.add(new C1DebugConfig()); + + boolean b = true; + if (b) { + allConfigs.add(new C1FastDebugConfig()); + allConfigs.add(new C1ProductConfig()); + + allConfigs.add(new C2DebugConfig()); + allConfigs.add(new C2FastDebugConfig()); + allConfigs.add(new C2ProductConfig()); + + allConfigs.add(new TieredDebugConfig()); + allConfigs.add(new TieredFastDebugConfig()); + allConfigs.add(new TieredProductConfig()); + + allConfigs.add(new CoreDebugConfig()); + allConfigs.add(new CoreFastDebugConfig()); + allConfigs.add(new CoreProductConfig()); + + allConfigs.add(new KernelDebugConfig()); + allConfigs.add(new KernelFastDebugConfig()); + allConfigs.add(new KernelProductConfig()); + } + + return allConfigs; + } + + class FileAttribute { + int numConfigs; + Vector configs; + String shortName; + boolean noPch, pchRoot; + + FileAttribute(String shortName, BuildConfig cfg, int numConfigs) { + this.shortName = shortName; + this.noPch = (cfg.lookupHashFieldInContext("DisablePch", shortName) != null); + this.pchRoot = shortName.equals(BuildConfig.getFieldString(null, "UseToGeneratePch")); + this.numConfigs = numConfigs; + + configs = new Vector(); + add(cfg.get("Name")); + } + + void add(String confName) { + configs.add(confName); + + // if presented in all configs + if (configs.size() == numConfigs) { + configs = null; + } + } + } + + class FileInfo implements Comparable { + String full; + FileAttribute attr; + + FileInfo(String full, FileAttribute attr) { + this.full = full; + this.attr = attr; + } + + public int compareTo(Object o) { + FileInfo oo = (FileInfo)o; + // Don't squelch identical short file names where the full + // paths are different + if (!attr.shortName.equals(oo.attr.shortName)) + return attr.shortName.compareTo(oo.attr.shortName); + return full.compareTo(oo.full); + } + + boolean isHeader() { + return attr.shortName.endsWith(".h") || attr.shortName.endsWith(".hpp"); + } + } + + + TreeSet sortFiles(Hashtable allFiles) { + TreeSet rv = new TreeSet(); + Enumeration e = allFiles.keys(); + while (e.hasMoreElements()) { + String fullPath = (String)e.nextElement(); + rv.add(new FileInfo(fullPath, (FileAttribute)allFiles.get(fullPath))); + } + return rv; + } + + Hashtable computeAttributedFiles(Vector allConfigs) { + Hashtable ht = new Hashtable(); + int numConfigs = allConfigs.size(); + + for (Iterator i = allConfigs.iterator(); i.hasNext(); ) { + BuildConfig bc = (BuildConfig)i.next(); + Hashtable confFiles = (Hashtable)bc.getSpecificField("AllFilesHash"); + String confName = bc.get("Name"); + + for (Enumeration e=confFiles.keys(); e.hasMoreElements(); ) { + String filePath = (String)e.nextElement(); + FileAttribute fa = (FileAttribute)ht.get(filePath); + + if (fa == null) { + fa = new FileAttribute((String)confFiles.get(filePath), bc, numConfigs); + ht.put(filePath, fa); + } else { + fa.add(confName); + } + } + } + + return ht; + } + + Hashtable computeAttributedFiles(BuildConfig bc) { + Hashtable ht = new Hashtable(); + Hashtable confFiles = (Hashtable)bc.getSpecificField("AllFilesHash"); + + for (Enumeration e = confFiles.keys(); e.hasMoreElements(); ) { + String filePath = (String)e.nextElement(); + ht.put(filePath, new FileAttribute((String)confFiles.get(filePath), bc, 1)); + } + + return ht; + } + + PrintWriter printWriter; + + public void writeProjectFile(String projectFileName, String projectName, + Vector allConfigs) throws IOException { + throw new RuntimeException("use compiler version specific version"); + } +} diff --git a/hotspot/src/share/tools/MakeDeps/WinGammaPlatformVC6.java b/hotspot/src/share/tools/MakeDeps/WinGammaPlatformVC6.java new file mode 100644 index 00000000000..3a296fef9ed --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/WinGammaPlatformVC6.java @@ -0,0 +1,291 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.io.*; +import java.util.*; + +public class WinGammaPlatformVC6 extends WinGammaPlatform { + public void writeProjectFile(String projectFileName, String projectName, + Vector allConfigs) throws IOException { + Vector allConfigNames = new Vector(); + + printWriter = new PrintWriter(new FileWriter(projectFileName)); + String cfg = ((BuildConfig)allConfigs.get(0)).get("Name"); + + printWriter.println("# Microsoft Developer Studio Project File - Name=\"" + projectName + "\" - Package Owner=<4>"); + printWriter.println("# Microsoft Developer Studio Generated Build File, Format Version 6.00"); + printWriter.println("# ** DO NOT EDIT **"); + printWriter.println(""); + printWriter.println("# TARGTYPE \"Win32 (x86) Dynamic-Link Library\" 0x0102"); + printWriter.println("CFG=" + cfg); + printWriter.println(""); + + printWriter.println("!MESSAGE This is not a valid makefile. To build this project using NMAKE,"); + printWriter.println("!MESSAGE use the Export Makefile command and run"); + printWriter.println("!MESSAGE "); + printWriter.println("!MESSAGE NMAKE /f \"" + projectName + ".mak\"."); + printWriter.println("!MESSAGE "); + printWriter.println("!MESSAGE You can specify a configuration when running NMAKE"); + printWriter.println("!MESSAGE by defining the macro CFG on the command line. For example:"); + printWriter.println("!MESSAGE "); + printWriter.println("!MESSAGE NMAKE /f \"" + projectName + ".mak\" CFG=\"" + cfg + "\""); + printWriter.println("!MESSAGE "); + printWriter.println("!MESSAGE Possible choices for configuration are:"); + printWriter.println("!MESSAGE "); + for (Iterator i = allConfigs.iterator(); i.hasNext(); ) { + String name = ((BuildConfig)i.next()).get("Name"); + printWriter.println("!MESSAGE \""+ name + "\" (based on \"Win32 (x86) Dynamic-Link Library\")"); + allConfigNames.add(name); + } + printWriter.println("!MESSAGE "); + printWriter.println(""); + + printWriter.println("# Begin Project"); + printWriter.println("# PROP AllowPerConfigDependencies 0"); + printWriter.println("# PROP Scc_ProjName \"\""); + printWriter.println("# PROP Scc_LocalPath \"\""); + printWriter.println("CPP=cl.exe"); + printWriter.println("MTL=midl.exe"); + printWriter.println("RSC=rc.exe"); + + + String keyword = "!IF"; + for (Iterator i = allConfigs.iterator(); i.hasNext(); ) { + BuildConfig bcfg = (BuildConfig)i.next(); + printWriter.println(keyword + " \"$(CFG)\" == \"" + bcfg.get("Name") + "\""); + writeConfigHeader(bcfg); + keyword = "!ELSEIF"; + if (!i.hasNext()) printWriter.println("!ENDIF"); + } + + + TreeSet sortedFiles = sortFiles(computeAttributedFiles(allConfigs)); + + printWriter.println("# Begin Target"); + + for (Iterator i = allConfigs.iterator(); i.hasNext(); ) { + printWriter.println("# Name \"" + ((BuildConfig)i.next()).get("Name") + "\""); + } + printWriter.println("# Begin Group \"Header Files\""); + printWriter.println("# PROP Default_Filter \"h;hpp;hxx;hm;inl;fi;fd\""); + + Iterator i = sortedFiles.iterator(); + + while (i.hasNext()) { + FileInfo fi = (FileInfo)i.next(); + + // skip sources + if (!fi.isHeader()) { + continue; + } + + printFile(fi, allConfigNames); + } + printWriter.println("# End Group"); + printWriter.println(""); + + printWriter.println("# Begin Group \"Source Files\""); + printWriter.println("# PROP Default_Filter \"cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90\""); + + i = sortedFiles.iterator(); + while (i.hasNext()) { + FileInfo fi = (FileInfo)i.next(); + + // skip headers + if (fi.isHeader()) { + continue; + } + + printFile(fi, allConfigNames); + } + printWriter.println("# End Group"); + printWriter.println(""); + + + printWriter.println("# Begin Group \"Resource Files\""); + printWriter.println("# PROP Default_Filter \"ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe\""); + printWriter.println("# End Group"); + printWriter.println(""); + printWriter.println("# End Target"); + + printWriter.println("# End Project"); + + printWriter.close(); + } + + + void printFile(FileInfo fi, Vector allConfigNames) { + printWriter.println("# Begin Source File"); + printWriter.println(""); + printWriter.println("SOURCE=\"" + fi.full + "\""); + FileAttribute attr = fi.attr; + + if (attr.noPch) { + printWriter.println("# SUBTRACT CPP /YX /Yc /Yu"); + } + + if (attr.pchRoot) { + printWriter.println("# ADD CPP /Yc\"incls/_precompiled.incl\""); + } + if (attr.configs != null) { + String keyword = "!IF"; + for (Iterator j=allConfigNames.iterator(); j.hasNext();) { + String cfg = (String)j.next(); + if (!attr.configs.contains(cfg)) { + printWriter.println(keyword+" \"$(CFG)\" == \"" + cfg +"\""); + printWriter.println("# PROP BASE Exclude_From_Build 1"); + printWriter.println("# PROP Exclude_From_Build 1"); + keyword = "!ELSEIF"; + } + } + printWriter.println("!ENDIF"); + } + + printWriter.println("# End Source File"); + } + + void writeConfigHeader(BuildConfig cfg) { + printWriter.println("# Begin Special Build Tool"); + printWriter.println("SOURCE=\"$(InputPath)\""); + printWriter.println("PreLink_Desc=" + BuildConfig.getFieldString(null, "PrelinkDescription")); + printWriter.println("PreLink_Cmds=" + + cfg.expandFormat(BuildConfig.getFieldString(null, "PrelinkCommand"))); + printWriter.println("# End Special Build Tool"); + printWriter.println(""); + + for (Iterator i = cfg.getV("CompilerFlags").iterator(); i.hasNext(); ) { + printWriter.println("# "+(String)i.next()); + } + + + printWriter.println("LINK32=link.exe"); + + for (Iterator i = cfg.getV("LinkerFlags").iterator(); i.hasNext(); ) { + printWriter.println("# "+(String)i.next()); + } + + printWriter.println("ADD BASE MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32"); + printWriter.println("ADD MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32"); + printWriter.println("ADD BASE RSC /l 0x409 /d \"_DEBUG\""); + printWriter.println("ADD RSC /l 0x409 /d \"_DEBUG\""); + printWriter.println("BSC32=bscmake.exe"); + printWriter.println("ADD BASE BSC32 /nologo"); + printWriter.println("ADD BSC32 /nologo"); + printWriter.println(""); + } + + protected String getProjectExt() { + return ".dsp"; + } +} + + +class CompilerInterfaceVC6 extends CompilerInterface { + Vector getBaseCompilerFlags(Vector defines, Vector includes, String outDir) { + Vector rv = new Vector(); + + rv.add("PROP BASE Use_MFC 0"); + rv.add("PROP Use_MFC 0"); + rv.add("ADD CPP /nologo /MT /W3 /WX /GX /YX /Fr /FD /c"); + rv.add("PROP BASE Output_Dir \""+outDir+"\""); + rv.add("PROP Output_Dir \""+outDir+"\""); + rv.add("PROP BASE Intermediate_Dir \""+outDir+"\""); + rv.add("PROP Intermediate_Dir \""+outDir+"\""); + rv.add("PROP BASE Target_Dir \"\""); + rv.add("PROP Target_Dir \"\""); + rv.add("ADD BASE CPP "+Util.prefixed_join(" /I ", includes, true)); + rv.add("ADD CPP "+Util.prefixed_join(" /I ", includes, true)); + rv.add("ADD BASE CPP "+Util.prefixed_join(" /D ", defines, true)); + rv.add("ADD CPP "+Util.prefixed_join(" /D ", defines, true)); + rv.add("ADD CPP /Yu\"incls/_precompiled.incl\""); + + return rv; + } + + Vector getBaseLinkerFlags(String outDir, String outDll) { + Vector rv = new Vector(); + + rv.add("PROP Ignore_Export_Lib 0"); + rv.add("ADD BASE CPP /MD"); + rv.add("ADD CPP /MD"); + rv.add("ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib " + + " advapi32.lib shell32.lib ole32.lib oleaut32.lib winmm.lib"); + rv.add("ADD LINK32 /out:\""+outDll+"\" "+ + " /nologo /subsystem:windows /machine:I386" + + " /nologo /base:\"0x8000000\" /subsystem:windows /dll" + + " /export:JNI_GetDefaultJavaVMInitArgs /export:JNI_CreateJavaVM /export:JNI_GetCreatedJavaVMs "+ + " /export:jio_snprintf /export:jio_printf /export:jio_fprintf /export:jio_vfprintf "+ + " /export:jio_vsnprintf "); + rv.add("SUBTRACT LINK32 /pdb:none /map"); + + return rv; + } + + Vector getDebugCompilerFlags(String opt) { + Vector rv = new Vector(); + + rv.add("ADD BASE CPP /Gm /Zi /O"+opt); + + return rv; + } + + Vector getDebugLinkerFlags() { + Vector rv = new Vector(); + + rv.add("PROP BASE Use_Debug_Libraries 1"); + rv.add("PROP Use_Debug_Libraries 1"); + rv.add("ADD LINK32 /debug"); + + return rv; + } + + Vector getProductCompilerFlags() { + Vector rv = new Vector(); + + rv.add("ADD CPP /O"+getOptFlag()); + + return rv; + } + + Vector getProductLinkerFlags() { + Vector rv = new Vector(); + + rv.add("PROP BASE Use_Debug_Libraries 0"); + rv.add("PROP Use_Debug_Libraries 0"); + + return rv; + } + + String getOptFlag() { + return "2"; + } + + String getNoOptFlag() { + return "d"; + } + + String makeCfgName(String flavourBuild) { + return "vm - "+ Util.os + " " + flavourBuild; + } +} diff --git a/hotspot/src/share/tools/MakeDeps/WinGammaPlatformVC7.java b/hotspot/src/share/tools/MakeDeps/WinGammaPlatformVC7.java new file mode 100644 index 00000000000..82159a41835 --- /dev/null +++ b/hotspot/src/share/tools/MakeDeps/WinGammaPlatformVC7.java @@ -0,0 +1,647 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.io.*; +import java.util.*; + +public class WinGammaPlatformVC7 extends WinGammaPlatform { + + public void writeProjectFile(String projectFileName, String projectName, + Vector allConfigs) throws IOException { + System.out.println(); + System.out.println(" Writing .vcproj file..."); + // If we got this far without an error, we're safe to actually + // write the .vcproj file + printWriter = new PrintWriter(new FileWriter(projectFileName)); + + printWriter.println(""); + startTag( + "VisualStudioProject", + new String[] { + "ProjectType", "Visual C++", + "Version", "7.10", + "Name", projectName, + "ProjectGUID", "{8822CB5C-1C41-41C2-8493-9F6E1994338B}", + "SccProjectName", "", + "SccLocalPath", "" + } + ); + + startTag("Platforms", null); + tag("Platform", new String[] {"Name", Util.os}); + endTag("Platforms"); + + startTag("Configurations", null); + + for (Iterator i = allConfigs.iterator(); i.hasNext(); ) { + writeConfiguration((BuildConfig)i.next()); + } + + endTag("Configurations"); + + tag("References", null); + + writeFiles(allConfigs); + + tag("Globals", null); + + endTag("VisualStudioProject"); + printWriter.close(); + + System.out.println(" Done."); + } + + + abstract class NameFilter { + protected String fname; + + abstract boolean match(FileInfo fi); + + String filterString() { return ""; } + String name() { return this.fname;} + } + + class DirectoryFilter extends NameFilter { + String dir; + int baseLen, dirLen; + + DirectoryFilter(String dir, String sbase) { + this.dir = dir; + this.baseLen = sbase.length(); + this.dirLen = dir.length(); + this.fname = dir; + } + + DirectoryFilter(String fname, String dir, String sbase) { + this.dir = dir; + this.baseLen = sbase.length(); + this.dirLen = dir.length(); + this.fname = fname; + } + + + boolean match(FileInfo fi) { + return fi.full.regionMatches(true, baseLen, dir, 0, dirLen); + } + } + + class TypeFilter extends NameFilter { + String[] exts; + + TypeFilter(String fname, String[] exts) { + this.fname = fname; + this.exts = exts; + } + + boolean match(FileInfo fi) { + for (int i=0; i"); + } else { + //doIndent(); + printWriter.println(">"); + } + } + + void startTag(String name, String[] attrs) { + startTagPrim(name, attrs, false); + } + + void startTagV(String name, Vector attrs) { + String s[] = new String [attrs.size()]; + for (int i=0; i"); + } + + void tag(String name, String[] attrs) { + startTagPrim(name, attrs, true); + } + + void tagV(String name, Vector attrs) { + String s[] = new String [attrs.size()]; + for (int i=0; i + set search path for bootstrap classes and resources + -Xbootclasspath/a: + append to end of bootstrap class path + -Xbootclasspath/p: + prepend in front of bootstrap class path + -Xnoclassgc disable class garbage collection + -Xincgc enable incremental garbage collection + -Xloggc: log GC status to a file with time stamps + -Xbatch disable background compilation + -Xms set initial Java heap size + -Xmx set maximum Java heap size + -Xss set java thread stack size + -Xprof output cpu profiling data + -Xfuture enable strictest checks, anticipating future default + -Xrs reduce use of OS signals by Java/VM (see documentation) + -Xcheck:jni perform additional checks for JNI functions + -Xshare:off do not attempt to use shared class data + -Xshare:auto use shared class data if possible (default) + -Xshare:on require using shared class data, otherwise fail. + +The -X options are non-standard and subject to change without notice. diff --git a/hotspot/src/share/vm/adlc/Doc/Syntax.doc b/hotspot/src/share/vm/adlc/Doc/Syntax.doc new file mode 100644 index 00000000000..ade893410c3 --- /dev/null +++ b/hotspot/src/share/vm/adlc/Doc/Syntax.doc @@ -0,0 +1,350 @@ +# +# Copyright 1997-1998 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +JavaSoft HotSpot Architecture Description Language Syntax Specification + +Version 0.4 - September 19, 1997 + +A. Introduction + +This document specifies the syntax and associated semantics for the JavaSoft +HotSpot Architecture Description Language. This language is used to describe +the architecture of a processor, and is the input to the ADL Compiler. The +ADL Compiler compiles an ADL file into code which is incorporated into the +Optimizing Just In Time Compiler (OJIT) to generate efficient and correct code +for the target architecture. The ADL describes three bassic different types +of architectural features. It describes the instruction set (and associated +operands) of the target architecture. It describes the register set of the +target architecture along with relevant information for the register allocator. +Finally, it describes the architecture's pipeline for scheduling purposes. +The ADL is used to create an architecture description file for a target +architecture. The architecture description file along with some additional +target specific oracles, written in C++, represent the principal effort in +porting the OJIT to a new target architecture. + + +B. Example Syntax + + 1. Instruction/Operand Syntax for Matching and Encoding + +// Create a cost attribute for all operands, and specify the default value +op_attrib op_cost(10); + +// Create a cost attribute for all instruction, and specify a default value +ins_attrib ins_cost(100); + +// example operand form +operand x_reg(REG_NUM rnum) +%{ + constraint(IS_RCLASS(rnum, RC_X_REG)); + + match(VREG) %{ rnum = new_VR(); %} // block after rule is constructor + + encode %{ return rnum; %} // encode rules are required + + // this operand has no op_cost entry because it uses the default value +%} + +// example instruction form +instruct add_accum_reg(x_reg dst, reg src) +%{ + match(SET dst (PLUS dst src)); // no block = use default constructor + + encode // rule is body of a C++ function + %{ + return pentium_encode_add_accum_reg(rnum); + %} + + ins_cost(200); // this instruction is more costly than the default +%} + + 2. Register Set Description Syntax for Allocation + +reg_def AX(SOE, 1); // declare register AX, mark it save on entry with index 0 +reg_def BX(SOC); // declare register BX, and mark it save on call + +reg_class X_REG(AX, BX); // form a matcher register class of X_REG + // these are used for constraints, etc. + +alloc_class class1(AX, BX); // form an allocation class of registers + // used by the register allocator for seperate + // allocation of target register classes + + 3. Pipeline Syntax for Scheduling + + +C. Keywords + +1. Matching/Encoding + a. instruct - indicates a machine instruction entry + b. operand - indicates a machine operand entry + c. opclass - indicates a class of machine operands + d. source - indicates a block of C++ source code + e. op_attrib - indicates an optional attribute for all operands + f. ins_attrib - indicates an optional attribute for all instructions + g. match - indicates a matching rule for an operand/instruction + h. encode - indicates an encoding rule for an operand/instruction + i. predicate - indicates a predicate for matching operand/instruction + *j. constraint - indicates a constraint on the value of an operand + *k. effect - describes the dataflow effect of an operand which + is not part of a match rule + *l. expand - indicates a single instruction for matching which + expands to multiple instructions for output + *m. rewrite - indicates a single instruction for matching which + gets rewritten to one or more instructions after + allocation depending upon the registers allocated + *n. format - indicates a format rule for an operand/instruction + o. construct - indicates a default constructor for an operand + + [NOTE: * indicates a feature which will not be in first release ] + +2. Register + *a. register - indicates an architecture register definition section + *b. reg_def - indicates a register declaration + *b. reg_class - indicates a class (list) of registers for matching + *c. alloc_class - indicates a class (list) of registers for allocation + +3. Pipeline + *a. pipeline - indicates an architecture pipeline definition section + *b. resource - indicates a pipeline resource used by instructions + *c. pipe_desc - indicates the relevant stages of the pipeline + *d. pipe_class - indicates a description of the pipeline behavior + for a group of instructions + *e. ins_pipe - indicates what pipeline class an instruction is in + + +D. Delimiters + + 1. Comments + a. /* ... */ (like C code) + b. // ... EOL (like C++ code) + c. Comments must be preceeded by whitespace + + 2. Blocks + a. %{ ... %} (% just distinguishes from C++ syntax) + b. Must be whitespace before and after block delimiters + + 3. Terminators + a. ; (standard statement terminator) + b. %} (block terminator) + c. EOF (file terminator) + + 4. Each statement must start on a seperate line + + 5. Identifiers cannot contain: (){}%;,"/\ + +E. Instruction Form: instruct instr1(oper1 dst, oper2 src) %{ ... %} + + 1. Identifier (scope of all instruction names is global in ADL file) + 2. Operands + a. Specified in argument style: ( , ...) + b. Type must be the name of an Operand Form + c. Name is a locally scoped name, used for substitution, etc. + 3. Match Rule: match(SET dst (PLUS dst src)); + a. Parenthesized Inorder Binary Tree: [lft = left; rgh = right] + (root root->lft (root->rgh (root->rgh->lft root->rgh->rgh))) + b. Interior nodes in tree are operators (nodes) in abstract IR + c. Leaves are operands from instruction operand list + d. Assignment operation and destination, which are implicit + in the abstract IR, must be specified in the match rule. + 4. Encode Rule: encode %{ return CONST; %} + a. Block form must contain C++ code which constitutes the + body of a C++ function which takes no arguments, and + returns an integer. + b. Local names (operand names) are can be used as substitution + symbols in the code. + 5. Attribute (ins_attrib): ins_cost(37); + a. Identifier (must be name defined as instruction attribute) + b. Argument must be a constant value or a C++ expression which + evaluates to a constant at compile time. + *6. Effect: effect(src, OP_KILL); + a. Arguments must be the name of an operand and one of the + pre-defined effect type symbols: + OP_DEF, OP_USE, OP_KILL, OP_USE_DEF, OP_DEF_USE, OP_USE_KILL + *7. Expand: + a. Parameters for the new instructions must be the name of + an expand rule temporary operand or must match the local + operand name in both the instruction being expanded and + the new instruction being generated. + + instruct convI2B( xRegI dst, eRegI src ) %{ + match(Set dst (Conv2B src)); + + expand %{ + eFlagsReg cr; + loadZero(dst); + testI_zero(cr,src); + set_nz(dst,cr); + %} + %} + // Move zero into a register without setting flags + instruct loadZero(eRegI dst) %{ + effect( DEF dst ); + format %{ "MOV $dst,0" %} + opcode(0xB8); // +rd + ins_encode( LdI0(dst) ); + %} + + *8. Rewrite + 9. Format: format(add_X $src, $dst); | format %{ ... %} + a. Argument form takes a text string, possibly containing + some substitution symbols, which will be printed out + to the assembly language file. + b. The block form takes valid C++ code which forms the body + of a function which takes no arguments, and returns a + pointer to a string to print out to the assembly file. + + Mentions of a literal register r in a or b must be of + the form r_enc or r_num. The form r_enc refers to the + encoding of the register in an instruction. The form + r_num refers to the number of the register. While + r_num is unique, two different registers may have the + same r_enc, as, for example, an integer and a floating + point register. + + +F. Operand Form: operand x_reg(REG_T rall) %{ ... %} + 1. Identifier (scope of all operand names is global in ADL file) + 2. Components + a. Specified in argument style: ( , ...) + b. Type must be a predefined Component Type + c. Name is a locally scoped name, used for substitution, etc. + 3. Match: (VREG) + a. Parenthesized Inorder Binary Tree: [lft = left; rgh = right] + (root root->lft (root->rgh (root->rgh->lft root->rgh->rgh))) + b. Interior nodes in tree are operators (nodes) in abstract IR + c. Leaves are components from operand component list + d. Block following tree is the body of a C++ function taking + no arguments and returning no value, which assigns values + to the components of the operand at match time. + 4. Encode: encode %{ return CONST; %} + a. Block form must contain C++ code which constitutes the + body of a C++ function which takes no arguments, and + returns an integer. + b. Local names (operand names) are can be used as substitution + symbols in the code. + 5. Attribute (op_attrib): op_cost(5); + a. Identifier (must be name defined as operand attribute) + b. Argument must be a constant value or a C++ expression which + evaluates to a constant at compile time. + 6. Predicate: predicate(0 <= src < 256); + a. Argument must be a valid C++ expression which evaluates + to either TRUE of FALSE at run time. + *7. Constraint: constraint(IS_RCLASS(dst, RC_X_CLASS)); + a. Arguments must contain only predefined constraint + functions on values defined in the AD file. + b. Multiple arguments can be chained together logically + with "&&". + 8. Construct: construct %{ ... %} + a. Block must be a valid C++ function body which takes no + arguments, and returns no values. + b. Purpose of block is to assign values to the elements + of an operand which is constructed outside the matching + process. + c. This block is logically identical to the constructor + block in a match rule. + 9. Format: format(add_X $src, $dst); | format %{ ... %} + a. Argument form takes a text string, possibly containing + some substitution symbols, which will be printed out + to the assembly language file. + b. The block form takes valid C++ code which forms the body + of a function which takes no arguments, and returns a + pointer to a string to print out to the assembly file. + + Mentions of a literal register r in a or b must be of + the form r_enc or r_num. The form r_enc refers to the + encoding of the register in an instruction. The form + r_num refers to the number of the register. While + r_num is unique, two different registers may have the + same r_enc, as, for example, an integer and a floating + point register. + +G. Operand Class Form: opclass memory( direct, indirect, ind_offset); + +H. Attribute Form (keywords ins_atrib & op_attrib): ins_attrib ins_cost(10); + 1. Identifier (scope of all attribute names is global in ADL file) + 2. Argument must be a valid C++ expression which evaluates to a + constant at compile time, and specifies the default value of + this attribute if attribute definition is not included in an + operand/instruction. + +I. Source Form: source %{ ... %} + 1. Source Block + a. All source blocks are delimited by "%{" and "%}". + b. All source blocks are copied verbatim into the + C++ output file, and must be valid C++ code. + + Mentions of a literal register r in this code must + be of the form r_enc or r_num. The form r_enc + refers to the encoding of the register in an + instruction. The form r_num refers to the number of + the register. While r_num is unique, two different + registers may have the same r_enc, as, for example, + an integer and a floating point register. + + +J. *Register Form: register %{ ... %} + 1. Block contains architecture specific information for allocation + 2. Reg_def: reg_def reg_AX(1); + a. Identifier is name by which register will be referenced + throughout the rest of the AD, and the allocator and + back-end. + b. Argument is the Save on Entry index (where 0 means that + the register is Save on Call). This is used by the + frame management routines for generating register saves + and restores. + 3. Reg_class: reg_class x_regs(reg_AX, reg_BX, reg_CX, reg_DX); + a. Identifier is the name of the class used throughout the + instruction selector. + b. Arguments are a list of register names in the class. + 4. Alloc_class: alloc_class x_alloc(reg_AX, reg_BX, reg_CX, reg_DX); + a. Identifier is the name of the class used throughout the + register allocator. + b. Arguments are a list of register names in the class. + + +K. *Pipeline Form: pipeline %{ ... %} + 1. Block contains architecture specific information for scheduling + 2. Resource: resource(ialu1); + a. Argument is the name of the resource. + 3. Pipe_desc: pipe_desc(Address, Access, Read, Execute); + a. Arguments are names of relevant phases of the pipeline. + b. If ALL instructions behave identically in a pipeline + phase, it does not need to be specified. (This is typically + true for pre-fetch, fetch, and decode pipeline stages.) + c. There must be one name per cycle consumed in the + pipeline, even if there is no instruction which has + significant behavior in that stage (for instance, extra + stages inserted for load and store instructions which + are just cycles which pass waiting for the completion + of the memory operation). + 4. Pipe_class: pipe_class pipe_normal(dagen; ; membus; ialu1, ialu2); + a. Identifier names the class for use in ins_pipe statements + b. Arguments are a list of stages, separated by ;'s which + contain comma separated lists of resource names. + c. There must be an entry for each stage defined in the + pipe_desc statement, (even if it is empty as in the example) + and entries are associated with stages by the order of + stage declarations in the pipe_desc statement. + diff --git a/hotspot/src/share/vm/adlc/Test/i486.ad b/hotspot/src/share/vm/adlc/Test/i486.ad new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hotspot/src/share/vm/adlc/adlc.hpp b/hotspot/src/share/vm/adlc/adlc.hpp new file mode 100644 index 00000000000..fc81ee00a5e --- /dev/null +++ b/hotspot/src/share/vm/adlc/adlc.hpp @@ -0,0 +1,106 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Standard include file for ADLC parser +// + +// standard library constants +#include "stdio.h" +#include "stdlib.h" +#if _MSC_VER >= 1300 // Visual C++ 7.0 or later +#include +#else +#include +#endif +#include "string.h" +#include "ctype.h" +#include "stdarg.h" +#include + +#if _MSC_VER >= 1300 +using namespace std; +#endif + +// make sure the MSC_VER and _MSC_VER settings make sense +#if _MSC_VER != MSC_VER && (_MSC_VER != 1400 || MSC_VER != 1399) +#error "Something is wrong with the detection of MSC_VER in the makefiles" +#endif + +#if _MSC_VER >= 1400 && !defined(_WIN64) +#define strdup _strdup +#endif + +/* Make sure that we have the intptr_t and uintptr_t definitions */ +#ifdef _WIN32 +#ifndef _INTPTR_T_DEFINED +#ifdef _WIN64 +typedef __int64 intptr_t; +#else +typedef int intptr_t; +#endif +#define _INTPTR_T_DEFINED +#endif + +#ifndef _UINTPTR_T_DEFINED +#ifdef _WIN64 +typedef unsigned __int64 uintptr_t; +#else +typedef unsigned int uintptr_t; +#endif +#define _UINTPTR_T_DEFINED +#endif +#endif // _WIN32 + +#ifdef LINUX + #include +#endif // LINUX + +// Macros +#define uint32 unsigned int +#define uint unsigned int + +// Macros +// Debugging note: Put a breakpoint on "abort". +#define assert(cond, msg) { if (!(cond)) { fprintf(stderr, "assert fails %s %d: %s\n", __FILE__, __LINE__, msg); abort(); }} +#define max(a, b) (((a)>(b)) ? (a) : (b)) + +// VM components +#include "opcodes.hpp" + +// ADLC components +#include "arena.hpp" +#include "adlcVMDeps.hpp" +#include "filebuff.hpp" +#include "dict2.hpp" +#include "forms.hpp" +#include "formsopt.hpp" +#include "formssel.hpp" +#include "archDesc.hpp" +#include "adlparse.hpp" + +// globally define ArchDesc for convenience. Alternatively every form +// could have a backpointer to the AD but it's too complicated to pass +// it everywhere it needs to be available. +extern ArchDesc* globalAD; diff --git a/hotspot/src/share/vm/adlc/adlparse.cpp b/hotspot/src/share/vm/adlc/adlparse.cpp new file mode 100644 index 00000000000..6ebef89f27f --- /dev/null +++ b/hotspot/src/share/vm/adlc/adlparse.cpp @@ -0,0 +1,4565 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ADLPARSE.CPP - Architecture Description Language Parser +// Authors: Chris Vick and Mike Paleczny +#include "adlc.hpp" + +//----------------------------ADLParser---------------------------------------- +// Create a new ADL parser +ADLParser::ADLParser(FileBuff& buffer, ArchDesc& archDesc) + : _buf(buffer), _AD(archDesc), + _globalNames(archDesc.globalNames()) { + _AD._syntax_errs = _AD._semantic_errs = 0; // No errors so far this file + _AD._warnings = 0; // No warnings either + _linenum = 0; // Will increment to first line + _curline = _ptr = NULL; // No pointers into buffer yet + + _preproc_depth = 0; + _preproc_not_taken = 0; + + // Delimit command-line definitions from in-file definitions: + _AD._preproc_list.add_signal(); +} + +//------------------------------~ADLParser------------------------------------- +// Delete an ADL parser. +ADLParser::~ADLParser() { + if (!_AD._quiet_mode) + fprintf(stderr,"---------------------------- Errors and Warnings ----------------------------\n"); +#ifndef ASSERT + fprintf(stderr, "**************************************************************\n"); + fprintf(stderr, "***** WARNING: ASSERT is undefined, assertions disabled. *****\n"); + fprintf(stderr, "**************************************************************\n"); +#endif + if( _AD._syntax_errs + _AD._semantic_errs + _AD._warnings == 0 ) { + if (!_AD._quiet_mode) + fprintf(stderr,"No errors or warnings to report from phase-1 parse.\n" ); + } + else { + if( _AD._syntax_errs ) { // Any syntax errors? + fprintf(stderr,"%s: Found %d syntax error", _buf._fp->_name, _AD._syntax_errs); + if( _AD._syntax_errs > 1 ) fprintf(stderr,"s.\n\n"); + else fprintf(stderr,".\n\n"); + } + if( _AD._semantic_errs ) { // Any semantic errors? + fprintf(stderr,"%s: Found %d semantic error", _buf._fp->_name, _AD._semantic_errs); + if( _AD._semantic_errs > 1 ) fprintf(stderr,"s.\n\n"); + else fprintf(stderr,".\n\n"); + } + if( _AD._warnings ) { // Any warnings? + fprintf(stderr,"%s: Found %d warning", _buf._fp->_name, _AD._warnings); + if( _AD._warnings > 1 ) fprintf(stderr,"s.\n\n"); + else fprintf(stderr,".\n\n"); + } + } + if (!_AD._quiet_mode) + fprintf(stderr,"-----------------------------------------------------------------------------\n"); + _AD._TotalLines += _linenum-1; // -1 for overshoot in "nextline" routine + + // Write out information we have stored + // // UNIXism == fsync(stderr); +} + +//------------------------------parse------------------------------------------ +// Each top-level keyword should appear as the first non-whitespace on a line. +// +void ADLParser::parse() { + char *ident; + + // Iterate over the lines in the file buffer parsing Level 1 objects + for( next_line(); _curline != NULL; next_line()) { + _ptr = _curline; // Reset ptr to start of new line + skipws(); // Skip any leading whitespace + ident = get_ident(); // Get first token + if (ident == NULL) { // Empty line + continue; // Get the next line + } + if (!strcmp(ident, "instruct")) instr_parse(); + else if (!strcmp(ident, "operand")) oper_parse(); + else if (!strcmp(ident, "opclass")) opclass_parse(); + else if (!strcmp(ident, "ins_attrib")) ins_attr_parse(); + else if (!strcmp(ident, "op_attrib")) op_attr_parse(); + else if (!strcmp(ident, "source")) source_parse(); + else if (!strcmp(ident, "source_hpp")) source_hpp_parse(); + else if (!strcmp(ident, "register")) reg_parse(); + else if (!strcmp(ident, "frame")) frame_parse(); + else if (!strcmp(ident, "encode")) encode_parse(); + else if (!strcmp(ident, "pipeline")) pipe_parse(); + else if (!strcmp(ident, "definitions")) definitions_parse(); + else if (!strcmp(ident, "peephole")) peep_parse(); + else if (!strcmp(ident, "#define")) preproc_define(); + else if (!strcmp(ident, "#undef")) preproc_undef(); + else { + parse_err(SYNERR, "expected one of - instruct, operand, ins_attrib, op_attrib, source, register, pipeline, encode\n Found %s",ident); + } + } + + // Done with parsing, check consistency. + + if (_preproc_depth != 0) { + parse_err(SYNERR, "End of file inside #ifdef"); + } + + // AttributeForms ins_cost and op_cost must be defined for default behaviour + if (_globalNames[AttributeForm::_ins_cost] == NULL) { + parse_err(SEMERR, "Did not declare 'ins_cost' attribute"); + } + if (_globalNames[AttributeForm::_ins_pc_relative] == NULL) { + parse_err(SEMERR, "Did not declare 'ins_pc_relative' attribute"); + } + if (_globalNames[AttributeForm::_op_cost] == NULL) { + parse_err(SEMERR, "Did not declare 'op_cost' attribute"); + } +} + +// ******************** Private Level 1 Parse Functions ******************** +//------------------------------instr_parse------------------------------------ +// Parse the contents of an instruction definition, build the InstructForm to +// represent that instruction, and add it to the InstructForm list. +void ADLParser::instr_parse(void) { + char *ident; + InstructForm *instr; + MatchRule *rule; + int match_rules_cnt = 0; + + // First get the name of the instruction + if( (ident = get_unique_ident(_globalNames,"instruction")) == NULL ) + return; + instr = new InstructForm(ident); // Create new instruction form + instr->_linenum = _linenum; + _globalNames.Insert(ident, instr); // Add name to the name table + // Debugging Stuff + if (_AD._adl_debug > 1) + fprintf(stderr,"Parsing Instruction Form %s\n", ident); + + // Then get the operands + skipws(); + if (_curchar != '(') { + parse_err(SYNERR, "missing '(' in instruct definition\n"); + } + // Parse the operand list + else get_oplist(instr->_parameters, instr->_localNames); + skipws(); // Skip leading whitespace + // Check for block delimiter + if ( (_curchar != '%') + || ( next_char(), (_curchar != '{')) ) { + parse_err(SYNERR, "missing '%{' in instruction definition\n"); + return; + } + next_char(); // Maintain the invariant + do { + ident = get_ident(); // Grab next identifier + if (ident == NULL) { + parse_err(SYNERR, "keyword identifier expected at %c\n", _curchar); + continue; + } + if (!strcmp(ident, "predicate")) instr->_predicate = pred_parse(); + else if (!strcmp(ident, "match")) { + // Allow one instruction have several match rules. + rule = instr->_matrule; + if (rule == NULL) { + // This is first match rule encountered + rule = match_parse(instr->_localNames); + if (rule) { + instr->_matrule = rule; + // Special case the treatment of Control instructions. + if( instr->is_ideal_control() ) { + // Control instructions return a special result, 'Universe' + rule->_result = "Universe"; + } + // Check for commutative operations with tree operands. + matchrule_clone_and_swap(rule, instr->_ident, match_rules_cnt); + } + } else { + // Find the end of the match rule list + while (rule->_next != NULL) + rule = rule->_next; + // Add the new match rule to the list + rule->_next = match_parse(instr->_localNames); + if (rule->_next) { + rule = rule->_next; + if( instr->is_ideal_control() ) { + parse_err(SYNERR, "unique match rule expected for %s\n", rule->_name); + return; + } + assert(match_rules_cnt < 100," too many match rule clones"); + char* buf = (char*) malloc(strlen(instr->_ident) + 4); + sprintf(buf, "%s_%d", instr->_ident, match_rules_cnt++); + rule->_result = buf; + // Check for commutative operations with tree operands. + matchrule_clone_and_swap(rule, instr->_ident, match_rules_cnt); + } + } + } + else if (!strcmp(ident, "encode")) { + parse_err(SYNERR, "Instructions specify ins_encode, not encode\n"); + } + else if (!strcmp(ident, "ins_encode")) + instr->_insencode = ins_encode_parse(*instr); + else if (!strcmp(ident, "opcode")) instr->_opcode = opcode_parse(instr); + else if (!strcmp(ident, "size")) instr->_size = size_parse(instr); + else if (!strcmp(ident, "effect")) effect_parse(instr); + else if (!strcmp(ident, "expand")) instr->_exprule = expand_parse(instr); + else if (!strcmp(ident, "rewrite")) instr->_rewrule = rewrite_parse(); + else if (!strcmp(ident, "constraint")) { + parse_err(SYNERR, "Instructions do not specify a constraint\n"); + } + else if (!strcmp(ident, "construct")) { + parse_err(SYNERR, "Instructions do not specify a construct\n"); + } + else if (!strcmp(ident, "format")) instr->_format = format_parse(); + else if (!strcmp(ident, "interface")) { + parse_err(SYNERR, "Instructions do not specify an interface\n"); + } + else if (!strcmp(ident, "ins_pipe")) ins_pipe_parse(*instr); + else { // Done with staticly defined parts of instruction definition + // Check identifier to see if it is the name of an attribute + const Form *form = _globalNames[ident]; + AttributeForm *attr = form ? form->is_attribute() : NULL; + if( attr && (attr->_atype == INS_ATTR) ) { + // Insert the new attribute into the linked list. + Attribute *temp = attr_parse(ident); + temp->_next = instr->_attribs; + instr->_attribs = temp; + } else { + parse_err(SYNERR, "expected one of:\n predicate, match, encode, or the name of an instruction attribute at %s\n", ident); + } + } + skipws(); + } while(_curchar != '%'); + next_char(); + if (_curchar != '}') { + parse_err(SYNERR, "missing '%}' in instruction definition\n"); + return; + } + // Check for "Set" form of chain rule + adjust_set_rule(instr); + if (_AD._pipeline ) { + if( instr->expands() ) { + if( instr->_ins_pipe ) + parse_err(WARN, "ins_pipe and expand rule both specified for instruction \"%s\"; ins_pipe will be unused\n", instr->_ident); + } else { + if( !instr->_ins_pipe ) + parse_err(WARN, "No ins_pipe specified for instruction \"%s\"\n", instr->_ident); + } + } + // Add instruction to tail of instruction list + _AD.addForm(instr); + + // Create instruction form for each additional match rule + rule = instr->_matrule; + if (rule != NULL) { + rule = rule->_next; + while (rule != NULL) { + ident = (char*)rule->_result; + InstructForm *clone = new InstructForm(ident, instr, rule); // Create new instruction form + _globalNames.Insert(ident, clone); // Add name to the name table + // Debugging Stuff + if (_AD._adl_debug > 1) + fprintf(stderr,"Parsing Instruction Form %s\n", ident); + // Check for "Set" form of chain rule + adjust_set_rule(clone); + // Add instruction to tail of instruction list + _AD.addForm(clone); + rule = rule->_next; + clone->_matrule->_next = NULL; // One match rule per clone + } + } +} + +//------------------------------matchrule_clone_and_swap----------------------- +// Check for commutative operations with subtree operands, +// create clones and swap operands. +void ADLParser::matchrule_clone_and_swap(MatchRule* rule, const char* instr_ident, int& match_rules_cnt) { + // Check for commutative operations with tree operands. + int count = 0; + rule->count_commutative_op(count); + if (count > 0) { + // Clone match rule and swap commutative operation's operands. + rule->swap_commutative_op(instr_ident, count, match_rules_cnt); + } +} + +//------------------------------adjust_set_rule-------------------------------- +// Check for "Set" form of chain rule +void ADLParser::adjust_set_rule(InstructForm *instr) { + if (instr->_matrule == NULL || instr->_matrule->_rChild == NULL) return; + const char *rch = instr->_matrule->_rChild->_opType; + const Form *frm = _globalNames[rch]; + if( (! strcmp(instr->_matrule->_opType,"Set")) && + frm && frm->is_operand() && (! frm->ideal_only()) ) { + // Previous implementation, which missed leaP*, but worked for loadCon* + unsigned position = 0; + const char *result = NULL; + const char *name = NULL; + const char *optype = NULL; + MatchNode *right = instr->_matrule->_rChild; + if (right->base_operand(position, _globalNames, result, name, optype)) { + position = 1; + const char *result2 = NULL; + const char *name2 = NULL; + const char *optype2 = NULL; + // Can not have additional base operands in right side of match! + if ( ! right->base_operand( position, _globalNames, result2, name2, optype2) ) { + assert( instr->_predicate == NULL, "ADLC does not support instruction chain rules with predicates"); + // Chain from input _ideal_operand_type_, + // Needed for shared roots of match-trees + ChainList *lst = (ChainList *)_AD._chainRules[optype]; + if (lst == NULL) { + lst = new ChainList(); + _AD._chainRules.Insert(optype, lst); + } + if (!lst->search(instr->_matrule->_lChild->_opType)) { + const char *cost = instr->cost(); + if (cost == NULL) { + cost = ((AttributeForm*)_globalNames[AttributeForm::_ins_cost])->_attrdef; + } + // The ADLC does not support chaining from the ideal operand type + // of a predicated user-defined operand + if( frm->is_operand() == NULL || frm->is_operand()->_predicate == NULL ) { + lst->insert(instr->_matrule->_lChild->_opType,cost,instr->_ident); + } + } + // Chain from input _user_defined_operand_type_, + lst = (ChainList *)_AD._chainRules[result]; + if (lst == NULL) { + lst = new ChainList(); + _AD._chainRules.Insert(result, lst); + } + if (!lst->search(instr->_matrule->_lChild->_opType)) { + const char *cost = instr->cost(); + if (cost == NULL) { + cost = ((AttributeForm*)_globalNames[AttributeForm::_ins_cost])->_attrdef; + } + // It is safe to chain from the top-level user-defined operand even + // if it has a predicate, since the predicate is checked before + // the user-defined type is available. + lst->insert(instr->_matrule->_lChild->_opType,cost,instr->_ident); + } + } else { + // May have instruction chain rule if root of right-tree is an ideal + OperandForm *rightOp = _globalNames[right->_opType]->is_operand(); + if( rightOp ) { + const Form *rightRoot = _globalNames[rightOp->_matrule->_opType]; + if( rightRoot && rightRoot->ideal_only() ) { + const char *chain_op = NULL; + if( rightRoot->is_instruction() ) + chain_op = rightOp->_ident; + if( chain_op ) { + // Look-up the operation in chain rule table + ChainList *lst = (ChainList *)_AD._chainRules[chain_op]; + if (lst == NULL) { + lst = new ChainList(); + _AD._chainRules.Insert(chain_op, lst); + } + // if (!lst->search(instr->_matrule->_lChild->_opType)) { + const char *cost = instr->cost(); + if (cost == NULL) { + cost = ((AttributeForm*)_globalNames[AttributeForm::_ins_cost])->_attrdef; + } + // This chains from a top-level operand whose predicate, if any, + // has been checked. + lst->insert(instr->_matrule->_lChild->_opType,cost,instr->_ident); + // } + } + } + } + } // end chain rule from right-tree's ideal root + } + } +} + + +//------------------------------oper_parse------------------------------------- +void ADLParser::oper_parse(void) { + char *ident; + OperandForm *oper; + AttributeForm *attr; + MatchRule *rule; + + // First get the name of the operand + skipws(); + if( (ident = get_unique_ident(_globalNames,"operand")) == NULL ) + return; + oper = new OperandForm(ident); // Create new operand form + oper->_linenum = _linenum; + _globalNames.Insert(ident, oper); // Add name to the name table + + // Debugging Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Parsing Operand Form %s\n", ident); + + // Get the component operands + skipws(); + if (_curchar != '(') { + parse_err(SYNERR, "missing '(' in operand definition\n"); + return; + } + else get_oplist(oper->_parameters, oper->_localNames); // Parse the component operand list + skipws(); + // Check for block delimiter + if ((_curchar != '%') || (*(_ptr+1) != '{')) { // If not open block + parse_err(SYNERR, "missing '%c{' in operand definition\n","%"); + return; + } + next_char(); next_char(); // Skip over "%{" symbol + do { + ident = get_ident(); // Grab next identifier + if (ident == NULL) { + parse_err(SYNERR, "keyword identifier expected at %c\n", _curchar); + continue; + } + if (!strcmp(ident, "predicate")) oper->_predicate = pred_parse(); + else if (!strcmp(ident, "match")) { + // Find the end of the match rule list + rule = oper->_matrule; + if (rule) { + while (rule->_next) rule = rule->_next; + // Add the new match rule to the list + rule->_next = match_parse(oper->_localNames); + if (rule->_next) { + rule->_next->_result = oper->_ident; + } + } + else { + // This is first match rule encountered + oper->_matrule = match_parse(oper->_localNames); + if (oper->_matrule) { + oper->_matrule->_result = oper->_ident; + } + } + } + else if (!strcmp(ident, "encode")) oper->_interface = interface_parse(); + else if (!strcmp(ident, "ins_encode")) { + parse_err(SYNERR, "Operands specify 'encode', not 'ins_encode'\n"); + } + else if (!strcmp(ident, "opcode")) { + parse_err(SYNERR, "Operands do not specify an opcode\n"); + } + else if (!strcmp(ident, "effect")) { + parse_err(SYNERR, "Operands do not specify an effect\n"); + } + else if (!strcmp(ident, "expand")) { + parse_err(SYNERR, "Operands do not specify an expand\n"); + } + else if (!strcmp(ident, "rewrite")) { + parse_err(SYNERR, "Operands do not specify a rewrite\n"); + } + else if (!strcmp(ident, "constraint"))oper->_constraint= constraint_parse(); + else if (!strcmp(ident, "construct")) oper->_construct = construct_parse(); + else if (!strcmp(ident, "format")) oper->_format = format_parse(); + else if (!strcmp(ident, "interface")) oper->_interface = interface_parse(); + // Check identifier to see if it is the name of an attribute + else if (((attr = _globalNames[ident]->is_attribute()) != NULL) && + (attr->_atype == OP_ATTR)) oper->_attribs = attr_parse(ident); + else { + parse_err(SYNERR, "expected one of - constraint, predicate, match, encode, format, construct, or the name of a defined operand attribute at %s\n", ident); + } + skipws(); + } while(_curchar != '%'); + next_char(); + if (_curchar != '}') { + parse_err(SYNERR, "missing '%}' in operand definition\n"); + return; + } + // Add operand to tail of operand list + _AD.addForm(oper); +} + +//------------------------------opclass_parse---------------------------------- +// Operand Classes are a block with a comma delimited list of operand names +void ADLParser::opclass_parse(void) { + char *ident; + OpClassForm *opc; + OperandForm *opForm; + + // First get the name of the operand class + skipws(); + if( (ident = get_unique_ident(_globalNames,"opclass")) == NULL ) + return; + opc = new OpClassForm(ident); // Create new operand class form + _globalNames.Insert(ident, opc); // Add name to the name table + + // Debugging Stuff + if (_AD._adl_debug > 1) + fprintf(stderr,"Parsing Operand Class Form %s\n", ident); + + // Get the list of operands + skipws(); + if (_curchar != '(') { + parse_err(SYNERR, "missing '(' in operand definition\n"); + return; + } + do { + next_char(); // Skip past open paren or comma + ident = get_ident(); // Grab next identifier + if (ident == NULL) { + parse_err(SYNERR, "keyword identifier expected at %c\n", _curchar); + continue; + } + // Check identifier to see if it is the name of an operand + const Form *form = _globalNames[ident]; + opForm = form ? form->is_operand() : NULL; + if ( opForm ) { + opc->_oplst.addName(ident); // Add operand to opclass list + opForm->_classes.addName(opc->_ident);// Add opclass to operand list + } + else { + parse_err(SYNERR, "expected name of a defined operand at %s\n", ident); + } + skipws(); // skip trailing whitespace + } while (_curchar == ','); // Check for the comma + // Check for closing ')' + if (_curchar != ')') { + parse_err(SYNERR, "missing ')' or ',' in opclass definition\n"); + return; + } + next_char(); // Consume the ')' + skipws(); + // Check for closing ';' + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' in opclass definition\n"); + return; + } + next_char(); // Consume the ';' + // Add operand to tail of operand list + _AD.addForm(opc); +} + +//------------------------------ins_attr_parse--------------------------------- +void ADLParser::ins_attr_parse(void) { + char *ident; + char *aexpr; + AttributeForm *attrib; + + // get name for the instruction attribute + skipws(); // Skip leading whitespace + if( (ident = get_unique_ident(_globalNames,"inst_attrib")) == NULL ) + return; + // Debugging Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Parsing Ins_Attribute Form %s\n", ident); + + // Get default value of the instruction attribute + skipws(); // Skip whitespace + if ((aexpr = get_paren_expr("attribute default expression string")) == NULL) { + parse_err(SYNERR, "missing '(' in ins_attrib definition\n"); + return; + } + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Attribute Expression: %s\n", aexpr); + + // Check for terminator + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' in ins_attrib definition\n"); + return; + } + next_char(); // Advance past the ';' + + // Construct the attribute, record global name, and store in ArchDesc + attrib = new AttributeForm(ident, INS_ATTR, aexpr); + _globalNames.Insert(ident, attrib); // Add name to the name table + _AD.addForm(attrib); +} + +//------------------------------op_attr_parse---------------------------------- +void ADLParser::op_attr_parse(void) { + char *ident; + char *aexpr; + AttributeForm *attrib; + + // get name for the operand attribute + skipws(); // Skip leading whitespace + if( (ident = get_unique_ident(_globalNames,"op_attrib")) == NULL ) + return; + // Debugging Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Parsing Op_Attribute Form %s\n", ident); + + // Get default value of the instruction attribute + skipws(); // Skip whitespace + if ((aexpr = get_paren_expr("attribute default expression string")) == NULL) { + parse_err(SYNERR, "missing '(' in op_attrib definition\n"); + return; + } + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Attribute Expression: %s\n", aexpr); + + // Check for terminator + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' in op_attrib definition\n"); + return; + } + next_char(); // Advance past the ';' + + // Construct the attribute, record global name, and store in ArchDesc + attrib = new AttributeForm(ident, OP_ATTR, aexpr); + _globalNames.Insert(ident, attrib); + _AD.addForm(attrib); +} + +//------------------------------definitions_parse----------------------------------- +void ADLParser::definitions_parse(void) { + skipws(); // Skip leading whitespace + if (_curchar == '%' && *(_ptr+1) == '{') { + next_char(); next_char(); // Skip "%{" + skipws(); + while (_curchar != '%' && *(_ptr+1) != '}') { + // Process each definition until finding closing string "%}" + char *token = get_ident(); + if (token == NULL) { + parse_err(SYNERR, "missing identifier inside definitions block.\n"); + return; + } + if (strcmp(token,"int_def")==0) { int_def_parse(); } + // if (strcmp(token,"str_def")==0) { str_def_parse(); } + skipws(); + } + } + else { + parse_err(SYNERR, "Missing %%{ ... %%} block after definitions keyword.\n"); + return; + } +} + +//------------------------------int_def_parse---------------------------------- +// Parse Example: +// int_def MEMORY_REF_COST ( 200, DEFAULT_COST * 2); +// ( , ); +// +void ADLParser::int_def_parse(void) { + char *name = NULL; // Name of definition + char *value = NULL; // its value, + int int_value = -1; // positive values only + char *description = NULL; // textual description + + // Get definition name + skipws(); // Skip whitespace + name = get_ident(); + if (name == NULL) { + parse_err(SYNERR, "missing definition name after int_def\n"); + return; + } + + // Check for value of int_def dname( integer_value [, string_expression ] ) + skipws(); + if (_curchar == '(') { + + // Parse the integer value. + next_char(); + value = get_ident(); + if (value == NULL) { + parse_err(SYNERR, "missing value in int_def\n"); + return; + } + if( !is_int_token(value, int_value) ) { + parse_err(SYNERR, "value in int_def is not recognized as integer\n"); + return; + } + skipws(); + + // Check for description + if (_curchar == ',') { + next_char(); // skip ',' + + description = get_expr("int_def description", ")"); + if (description == NULL) { + parse_err(SYNERR, "invalid or missing description in int_def\n"); + return; + } + trim(description); + } + + if (_curchar != ')') { + parse_err(SYNERR, "missing ')' in register definition statement\n"); + return; + } + next_char(); + } + + // Check for closing ';' + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' after int_def\n"); + return; + } + next_char(); // move past ';' + + // Debug Stuff + if (_AD._adl_debug > 1) { + fprintf(stderr,"int_def: %s ( %s, %s )\n", name, + (value), (description ? description : "")); + } + + // Record new definition. + Expr *expr = new Expr(name, description, int_value, int_value); + const Expr *old_expr = _AD.globalDefs().define(name, expr); + if (old_expr != NULL) { + parse_err(SYNERR, "Duplicate definition\n"); + return; + } + + return; +} + + +//------------------------------source_parse----------------------------------- +void ADLParser::source_parse(void) { + SourceForm *source; // Encode class for instruction/operand + char *rule = NULL; // String representation of encode rule + + skipws(); // Skip leading whitespace + if ( (rule = find_cpp_block("source block")) == NULL ) { + parse_err(SYNERR, "incorrect or missing block for 'source'.\n"); + return; + } + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Source Form: %s\n", rule); + + source = new SourceForm(rule); // Build new Source object + _AD.addForm(source); + // skipws(); +} + +//------------------------------source_hpp_parse------------------------------- +// Parse a source_hpp %{ ... %} block. +// The code gets stuck into the ad_.hpp file. +// If the source_hpp block appears before the register block in the AD +// file, it goes up at the very top of the ad_.hpp file, so that +// it can be used by register encodings, etc. Otherwise, it goes towards +// the bottom, where it's useful as a global definition to *.cpp files. +void ADLParser::source_hpp_parse(void) { + char *rule = NULL; // String representation of encode rule + + skipws(); // Skip leading whitespace + if ( (rule = find_cpp_block("source_hpp block")) == NULL ) { + parse_err(SYNERR, "incorrect or missing block for 'source_hpp'.\n"); + return; + } + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Header Form: %s\n", rule); + + if (_AD.get_registers() == NULL) { + // Very early in the file, before reg_defs, we collect pre-headers. + PreHeaderForm* pre_header = new PreHeaderForm(rule); + _AD.addForm(pre_header); + } else { + // Normally, we collect header info, placed at the bottom of the hpp file. + HeaderForm* header = new HeaderForm(rule); + _AD.addForm(header); + } +} + +//------------------------------reg_parse-------------------------------------- +void ADLParser::reg_parse(void) { + + // Create the RegisterForm for the architecture description. + RegisterForm *regBlock = new RegisterForm(); // Build new Source object + regBlock->_linenum = _linenum; + _AD.addForm(regBlock); + + skipws(); // Skip leading whitespace + if (_curchar == '%' && *(_ptr+1) == '{') { + next_char(); next_char(); // Skip "%{" + skipws(); + while (_curchar != '%' && *(_ptr+1) != '}') { + char *token = get_ident(); + if (token == NULL) { + parse_err(SYNERR, "missing identifier inside register block.\n"); + return; + } + if (strcmp(token,"reg_def")==0) { reg_def_parse(); } + if (strcmp(token,"reg_class")==0) { reg_class_parse(); } + if (strcmp(token,"alloc_class")==0) { alloc_class_parse(); } + skipws(); + } + } + else { + parse_err(SYNERR, "Missing %c{ ... %c} block after register keyword.\n",'%','%'); + return; + } + + // Add reg_class spill_regs + regBlock->addSpillRegClass(); +} + +//------------------------------encode_parse----------------------------------- +void ADLParser::encode_parse(void) { + EncodeForm *encBlock; // Information about instruction/operand encoding + char *desc = NULL; // String representation of encode rule + + _AD.getForm(&encBlock); + if ( encBlock == NULL) { + // Create the EncodeForm for the architecture description. + encBlock = new EncodeForm(); // Build new Source object + _AD.addForm(encBlock); + } + + skipws(); // Skip leading whitespace + if (_curchar == '%' && *(_ptr+1) == '{') { + next_char(); next_char(); // Skip "%{" + skipws(); + while (_curchar != '%' && *(_ptr+1) != '}') { + char *token = get_ident(); + if (token == NULL) { + parse_err(SYNERR, "missing identifier inside encoding block.\n"); + return; + } + if (strcmp(token,"enc_class")==0) { enc_class_parse(); } + skipws(); + } + } + else { + parse_err(SYNERR, "Missing %c{ ... %c} block after encode keyword.\n",'%','%'); + return; + } +} + +//------------------------------enc_class_parse-------------------------------- +void ADLParser::enc_class_parse(void) { + char *ec_name; // Name of encoding class being defined + + // Get encoding class name + skipws(); // Skip whitespace + ec_name = get_ident(); + if (ec_name == NULL) { + parse_err(SYNERR, "missing encoding class name after encode.\n"); + return; + } + + EncClass *encoding = _AD._encode->add_EncClass(ec_name); + encoding->_linenum = _linenum; + + skipws(); // Skip leading whitespace + // Check for optional parameter list + if (_curchar == '(') { + do { + char *pType = NULL; // parameter type + char *pName = NULL; // parameter name + + next_char(); // skip open paren & comma characters + skipws(); + if (_curchar == ')') break; + + // Get parameter type + pType = get_ident(); + if (pType == NULL) { + parse_err(SYNERR, "parameter type expected at %c\n", _curchar); + return; + } + + skipws(); + // Get parameter name + pName = get_ident(); + if (pName == NULL) { + parse_err(SYNERR, "parameter name expected at %c\n", _curchar); + return; + } + + // Record parameter type and name + encoding->add_parameter( pType, pName ); + + skipws(); + } while(_curchar == ','); + + if (_curchar != ')') parse_err(SYNERR, "missing ')'\n"); + else { + next_char(); // Skip ')' + } + } // Done with parameter list + + skipws(); + // Check for block starting delimiters + if ((_curchar != '%') || (*(_ptr+1) != '{')) { // If not open block + parse_err(SYNERR, "missing '%c{' in enc_class definition\n", '%'); + return; + } + next_char(); // Skip '%' + next_char(); // Skip '{' + + enc_class_parse_block(encoding, ec_name); +} + + +void ADLParser::enc_class_parse_block(EncClass* encoding, char* ec_name) { + skipws_no_preproc(); // Skip leading whitespace + // Prepend location descriptor, for debugging; cf. ADLParser::find_cpp_block + if (_AD._adlocation_debug) { + const char* file = _AD._ADL_file._name; + int line = _linenum; + char* location = (char *)malloc(strlen(file) + 100); + sprintf(location, "#line %d \"%s\"\n", line, file); + encoding->add_code(location); + } + + // Collect the parts of the encode description + // (1) strings that are passed through to output + // (2) replacement/substitution variable, preceeded by a '$' + while ( (_curchar != '%') && (*(_ptr+1) != '}') ) { + + // (1) + // Check if there is a string to pass through to output + char *start = _ptr; // Record start of the next string + while ((_curchar != '$') && ((_curchar != '%') || (*(_ptr+1) != '}')) ) { + // If at the start of a comment, skip past it + if( (_curchar == '/') && ((*(_ptr+1) == '/') || (*(_ptr+1) == '*')) ) { + skipws_no_preproc(); + } else { + // ELSE advance to the next character, or start of the next line + next_char_or_line(); + } + } + // If a string was found, terminate it and record in EncClass + if ( start != _ptr ) { + *_ptr = '\0'; // Terminate the string + encoding->add_code(start); + } + + // (2) + // If we are at a replacement variable, + // copy it and record in EncClass + if ( _curchar == '$' ) { + // Found replacement Variable + char *rep_var = get_rep_var_ident_dup(); + // Add flag to _strings list indicating we should check _rep_vars + encoding->add_rep_var(rep_var); + } + } // end while part of format description + next_char(); // Skip '%' + next_char(); // Skip '}' + + skipws(); + + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"EncodingClass Form: %s\n", ec_name); +} + +//------------------------------frame_parse----------------------------------- +void ADLParser::frame_parse(void) { + FrameForm *frame; // Information about stack-frame layout + char *desc = NULL; // String representation of frame + + skipws(); // Skip leading whitespace + + frame = new FrameForm(); // Build new Frame object + // Check for open block sequence + skipws(); // Skip leading whitespace + if (_curchar == '%' && *(_ptr+1) == '{') { + next_char(); next_char(); // Skip "%{" + skipws(); + while (_curchar != '%' && *(_ptr+1) != '}') { + char *token = get_ident(); + if (token == NULL) { + parse_err(SYNERR, "missing identifier inside frame block.\n"); + return; + } + if (strcmp(token,"stack_direction")==0) { + stack_dir_parse(frame); + } + if (strcmp(token,"sync_stack_slots")==0) { + sync_stack_slots_parse(frame); + } + if (strcmp(token,"frame_pointer")==0) { + frame_pointer_parse(frame, false); + } + if (strcmp(token,"interpreter_frame_pointer")==0) { + interpreter_frame_pointer_parse(frame, false); + // Add reg_class interpreter_frame_pointer_reg + if( _AD._register != NULL ) { + RegClass *reg_class = _AD._register->addRegClass("interpreter_frame_pointer_reg"); + char *interpreter_frame_pointer_reg = frame->_interpreter_frame_pointer_reg; + if( interpreter_frame_pointer_reg != NULL ) { + RegDef *regDef = _AD._register->getRegDef(interpreter_frame_pointer_reg); + reg_class->addReg(regDef); // add regDef to regClass + } + } + } + if (strcmp(token,"inline_cache_reg")==0) { + inline_cache_parse(frame, false); + // Add reg_class inline_cache_reg + if( _AD._register != NULL ) { + RegClass *reg_class = _AD._register->addRegClass("inline_cache_reg"); + char *inline_cache_reg = frame->_inline_cache_reg; + if( inline_cache_reg != NULL ) { + RegDef *regDef = _AD._register->getRegDef(inline_cache_reg); + reg_class->addReg(regDef); // add regDef to regClass + } + } + } + if (strcmp(token,"compiler_method_oop_reg")==0) { + parse_err(WARN, "Using obsolete Token, compiler_method_oop_reg"); + skipws(); + } + if (strcmp(token,"interpreter_method_oop_reg")==0) { + interpreter_method_oop_parse(frame, false); + // Add reg_class interpreter_method_oop_reg + if( _AD._register != NULL ) { + RegClass *reg_class = _AD._register->addRegClass("interpreter_method_oop_reg"); + char *method_oop_reg = frame->_interpreter_method_oop_reg; + if( method_oop_reg != NULL ) { + RegDef *regDef = _AD._register->getRegDef(method_oop_reg); + reg_class->addReg(regDef); // add regDef to regClass + } + } + } + if (strcmp(token,"cisc_spilling_operand_name")==0) { + cisc_spilling_operand_name_parse(frame, false); + } + if (strcmp(token,"stack_alignment")==0) { + stack_alignment_parse(frame); + } + if (strcmp(token,"return_addr")==0) { + return_addr_parse(frame, false); + } + if (strcmp(token,"in_preserve_stack_slots")==0) { + preserve_stack_parse(frame); + } + if (strcmp(token,"out_preserve_stack_slots")==0) { + parse_err(WARN, "Using obsolete token, out_preserve_stack_slots"); + skipws(); + } + if (strcmp(token,"varargs_C_out_slots_killed")==0) { + frame->_varargs_C_out_slots_killed = parse_one_arg("varargs C out slots killed"); + } + if (strcmp(token,"calling_convention")==0) { + frame->_calling_convention = calling_convention_parse(); + } + if (strcmp(token,"return_value")==0) { + frame->_return_value = return_value_parse(); + } + if (strcmp(token,"c_frame_pointer")==0) { + frame_pointer_parse(frame, true); + } + if (strcmp(token,"c_return_addr")==0) { + return_addr_parse(frame, true); + } + if (strcmp(token,"c_calling_convention")==0) { + frame->_c_calling_convention = calling_convention_parse(); + } + if (strcmp(token,"c_return_value")==0) { + frame->_c_return_value = return_value_parse(); + } + + skipws(); + } + } + else { + parse_err(SYNERR, "Missing %c{ ... %c} block after encode keyword.\n",'%','%'); + return; + } + // All Java versions are required, native versions are optional + if(frame->_frame_pointer == NULL) { + parse_err(SYNERR, "missing frame pointer definition in frame section.\n"); + return; + } + // !!!!! !!!!! + // if(frame->_interpreter_frame_ptr_reg == NULL) { + // parse_err(SYNERR, "missing interpreter frame pointer definition in frame section.\n"); + // return; + // } + if(frame->_alignment == NULL) { + parse_err(SYNERR, "missing alignment definition in frame section.\n"); + return; + } + if(frame->_return_addr == NULL) { + parse_err(SYNERR, "missing return address location in frame section.\n"); + return; + } + if(frame->_in_preserve_slots == NULL) { + parse_err(SYNERR, "missing stack slot preservation definition in frame section.\n"); + return; + } + if(frame->_varargs_C_out_slots_killed == NULL) { + parse_err(SYNERR, "missing varargs C out slots killed definition in frame section.\n"); + return; + } + if(frame->_calling_convention == NULL) { + parse_err(SYNERR, "missing calling convention definition in frame section.\n"); + return; + } + if(frame->_return_value == NULL) { + parse_err(SYNERR, "missing return value definition in frame section.\n"); + return; + } + // Fill natives in identically with the Java versions if not present. + if(frame->_c_frame_pointer == NULL) { + frame->_c_frame_pointer = frame->_frame_pointer; + } + if(frame->_c_return_addr == NULL) { + frame->_c_return_addr = frame->_return_addr; + frame->_c_return_addr_loc = frame->_return_addr_loc; + } + if(frame->_c_calling_convention == NULL) { + frame->_c_calling_convention = frame->_calling_convention; + } + if(frame->_c_return_value == NULL) { + frame->_c_return_value = frame->_return_value; + } + + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Frame Form: %s\n", desc); + + // Create the EncodeForm for the architecture description. + _AD.addForm(frame); + // skipws(); +} + +//------------------------------stack_dir_parse-------------------------------- +void ADLParser::stack_dir_parse(FrameForm *frame) { + char *direction = parse_one_arg("stack direction entry"); + if (strcmp(direction, "TOWARDS_LOW") == 0) { + frame->_direction = false; + } + else if (strcmp(direction, "TOWARDS_HIGH") == 0) { + frame->_direction = true; + } + else { + parse_err(SYNERR, "invalid value inside stack direction entry.\n"); + return; + } +} + +//------------------------------sync_stack_slots_parse------------------------- +void ADLParser::sync_stack_slots_parse(FrameForm *frame) { + // Assign value into frame form + frame->_sync_stack_slots = parse_one_arg("sync stack slots entry"); +} + +//------------------------------frame_pointer_parse---------------------------- +void ADLParser::frame_pointer_parse(FrameForm *frame, bool native) { + char *frame_pointer = parse_one_arg("frame pointer entry"); + // Assign value into frame form + if (native) { frame->_c_frame_pointer = frame_pointer; } + else { frame->_frame_pointer = frame_pointer; } +} + +//------------------------------interpreter_frame_pointer_parse---------------------------- +void ADLParser::interpreter_frame_pointer_parse(FrameForm *frame, bool native) { + frame->_interpreter_frame_pointer_reg = parse_one_arg("interpreter frame pointer entry"); +} + +//------------------------------inline_cache_parse----------------------------- +void ADLParser::inline_cache_parse(FrameForm *frame, bool native) { + frame->_inline_cache_reg = parse_one_arg("inline cache reg entry"); +} + +//------------------------------interpreter_method_oop_parse------------------ +void ADLParser::interpreter_method_oop_parse(FrameForm *frame, bool native) { + frame->_interpreter_method_oop_reg = parse_one_arg("method oop reg entry"); +} + +//------------------------------cisc_spilling_operand_parse--------------------- +void ADLParser::cisc_spilling_operand_name_parse(FrameForm *frame, bool native) { + frame->_cisc_spilling_operand_name = parse_one_arg("cisc spilling operand name"); +} + +//------------------------------stack_alignment_parse-------------------------- +void ADLParser::stack_alignment_parse(FrameForm *frame) { + char *alignment = parse_one_arg("stack alignment entry"); + // Assign value into frame + frame->_alignment = alignment; +} + +//------------------------------parse_one_arg------------------------------- +char *ADLParser::parse_one_arg(const char *description) { + char *token = NULL; + if(_curchar == '(') { + next_char(); + skipws(); + token = get_expr(description, ")"); + if (token == NULL) { + parse_err(SYNERR, "missing value inside %s.\n", description); + return NULL; + } + next_char(); // skip the close paren + if(_curchar != ';') { // check for semi-colon + parse_err(SYNERR, "missing %c in.\n", ';', description); + return NULL; + } + next_char(); // skip the semi-colon + } + else { + parse_err(SYNERR, "Missing %c in.\n", '(', description); + return NULL; + } + + trim(token); + return token; +} + +//------------------------------return_addr_parse------------------------------ +void ADLParser::return_addr_parse(FrameForm *frame, bool native) { + bool in_register = true; + if(_curchar == '(') { + next_char(); + skipws(); + char *token = get_ident(); + if (token == NULL) { + parse_err(SYNERR, "missing value inside return address entry.\n"); + return; + } + // check for valid values for stack/register + if (strcmp(token, "REG") == 0) { + in_register = true; + } + else if (strcmp(token, "STACK") == 0) { + in_register = false; + } + else { + parse_err(SYNERR, "invalid value inside return_address entry.\n"); + return; + } + if (native) { frame->_c_return_addr_loc = in_register; } + else { frame->_return_addr_loc = in_register; } + + // Parse expression that specifies register or stack position + skipws(); + char *token2 = get_expr("return address entry", ")"); + if (token2 == NULL) { + parse_err(SYNERR, "missing value inside return address entry.\n"); + return; + } + next_char(); // skip the close paren + if (native) { frame->_c_return_addr = token2; } + else { frame->_return_addr = token2; } + + if(_curchar != ';') { // check for semi-colon + parse_err(SYNERR, "missing %c in return address entry.\n", ';'); + return; + } + next_char(); // skip the semi-colon + } + else { + parse_err(SYNERR, "Missing %c in return_address entry.\n", '('); + } +} + +//------------------------------preserve_stack_parse--------------------------- +void ADLParser::preserve_stack_parse(FrameForm *frame) { + if(_curchar == '(') { + char *token = get_paren_expr("preserve_stack_slots"); + frame->_in_preserve_slots = token; + + if(_curchar != ';') { // check for semi-colon + parse_err(SYNERR, "missing %c in preserve stack slot entry.\n", ';'); + return; + } + next_char(); // skip the semi-colon + } + else { + parse_err(SYNERR, "Missing %c in preserve stack slot entry.\n", '('); + } +} + +//------------------------------calling_convention_parse----------------------- +char *ADLParser::calling_convention_parse() { + char *desc = NULL; // String representation of calling_convention + + skipws(); // Skip leading whitespace + if ( (desc = find_cpp_block("calling convention block")) == NULL ) { + parse_err(SYNERR, "incorrect or missing block for 'calling_convention'.\n"); + } + return desc; +} + +//------------------------------return_value_parse----------------------------- +char *ADLParser::return_value_parse() { + char *desc = NULL; // String representation of calling_convention + + skipws(); // Skip leading whitespace + if ( (desc = find_cpp_block("return value block")) == NULL ) { + parse_err(SYNERR, "incorrect or missing block for 'return_value'.\n"); + } + return desc; +} + +//------------------------------ins_pipe_parse--------------------------------- +void ADLParser::ins_pipe_parse(InstructForm &instr) { + char * ident; + + skipws(); + if ( _curchar != '(' ) { // Check for delimiter + parse_err(SYNERR, "missing \"(\" in ins_pipe definition\n"); + return; + } + + next_char(); + ident = get_ident(); // Grab next identifier + + if (ident == NULL) { + parse_err(SYNERR, "keyword identifier expected at %c\n", _curchar); + return; + } + + skipws(); + if ( _curchar != ')' ) { // Check for delimiter + parse_err(SYNERR, "missing \")\" in ins_pipe definition\n"); + return; + } + + next_char(); // skip the close paren + if(_curchar != ';') { // check for semi-colon + parse_err(SYNERR, "missing %c in return value entry.\n", ';'); + return; + } + next_char(); // skip the semi-colon + + // Check ident for validity + if (_AD._pipeline && !_AD._pipeline->_classlist.search(ident)) { + parse_err(SYNERR, "\"%s\" is not a valid pipeline class\n", ident); + return; + } + + // Add this instruction to the list in the pipeline class + _AD._pipeline->_classdict[ident]->is_pipeclass()->_instructs.addName(instr._ident); + + // Set the name of the pipeline class in the instruction + instr._ins_pipe = ident; + return; +} + +//------------------------------pipe_parse------------------------------------- +void ADLParser::pipe_parse(void) { + PipelineForm *pipeline; // Encode class for instruction/operand + char * ident; + + pipeline = new PipelineForm(); // Build new Source object + _AD.addForm(pipeline); + + skipws(); // Skip leading whitespace + // Check for block delimiter + if ( (_curchar != '%') + || ( next_char(), (_curchar != '{')) ) { + parse_err(SYNERR, "missing '%{' in pipeline definition\n"); + return; + } + next_char(); // Maintain the invariant + do { + ident = get_ident(); // Grab next identifier + if (ident == NULL) { + parse_err(SYNERR, "keyword identifier expected at %c\n", _curchar); + continue; + } + if (!strcmp(ident, "resources" )) resource_parse(*pipeline); + else if (!strcmp(ident, "pipe_desc" )) pipe_desc_parse(*pipeline); + else if (!strcmp(ident, "pipe_class")) pipe_class_parse(*pipeline); + else if (!strcmp(ident, "define")) { + skipws(); + if ( (_curchar != '%') + || ( next_char(), (_curchar != '{')) ) { + parse_err(SYNERR, "expected '%{'\n"); + return; + } + next_char(); skipws(); + + char *node_class = get_ident(); + if (node_class == NULL) { + parse_err(SYNERR, "expected identifier, found \"%c\"\n", _curchar); + return; + } + + skipws(); + if (_curchar != ',' && _curchar != '=') { + parse_err(SYNERR, "expected `=`, found '%c'\n", _curchar); + break; + } + next_char(); skipws(); + + char *pipe_class = get_ident(); + if (pipe_class == NULL) { + parse_err(SYNERR, "expected identifier, found \"%c\"\n", _curchar); + return; + } + if (_curchar != ';' ) { + parse_err(SYNERR, "expected `;`, found '%c'\n", _curchar); + break; + } + next_char(); // Skip over semi-colon + + skipws(); + if ( (_curchar != '%') + || ( next_char(), (_curchar != '}')) ) { + parse_err(SYNERR, "expected '%%}', found \"%c\"\n", _curchar); + } + next_char(); + + // Check ident for validity + if (_AD._pipeline && !_AD._pipeline->_classlist.search(pipe_class)) { + parse_err(SYNERR, "\"%s\" is not a valid pipeline class\n", pipe_class); + return; + } + + // Add this machine node to the list in the pipeline class + _AD._pipeline->_classdict[pipe_class]->is_pipeclass()->_instructs.addName(node_class); + + MachNodeForm *machnode = new MachNodeForm(node_class); // Create new machnode form + machnode->_machnode_pipe = pipe_class; + + _AD.addForm(machnode); + } + else if (!strcmp(ident, "attributes")) { + bool vsi_seen = false, bhds_seen = false; + + skipws(); + if ( (_curchar != '%') + || ( next_char(), (_curchar != '{')) ) { + parse_err(SYNERR, "expected '%{'\n"); + return; + } + next_char(); skipws(); + + while (_curchar != '%') { + ident = get_ident(); + if (ident == NULL) + break; + + if (!strcmp(ident, "variable_size_instructions")) { + skipws(); + if (_curchar == ';') { + next_char(); skipws(); + } + + pipeline->_variableSizeInstrs = true; + vsi_seen = true; + continue; + } + + if (!strcmp(ident, "fixed_size_instructions")) { + skipws(); + if (_curchar == ';') { + next_char(); skipws(); + } + + pipeline->_variableSizeInstrs = false; + vsi_seen = true; + continue; + } + + if (!strcmp(ident, "branch_has_delay_slot")) { + skipws(); + if (_curchar == ';') { + next_char(); skipws(); + } + + pipeline->_branchHasDelaySlot = true; + bhds_seen = true; + continue; + } + + if (!strcmp(ident, "max_instructions_per_bundle")) { + skipws(); + if (_curchar != '=') { + parse_err(SYNERR, "expected `=`\n"); + break; + } + + next_char(); skipws(); + pipeline->_maxInstrsPerBundle = get_int(); + skipws(); + + if (_curchar == ';') { + next_char(); skipws(); + } + + continue; + } + + if (!strcmp(ident, "max_bundles_per_cycle")) { + skipws(); + if (_curchar != '=') { + parse_err(SYNERR, "expected `=`\n"); + break; + } + + next_char(); skipws(); + pipeline->_maxBundlesPerCycle = get_int(); + skipws(); + + if (_curchar == ';') { + next_char(); skipws(); + } + + continue; + } + + if (!strcmp(ident, "instruction_unit_size")) { + skipws(); + if (_curchar != '=') { + parse_err(SYNERR, "expected `=`, found '%c'\n", _curchar); + break; + } + + next_char(); skipws(); + pipeline->_instrUnitSize = get_int(); + skipws(); + + if (_curchar == ';') { + next_char(); skipws(); + } + + continue; + } + + if (!strcmp(ident, "bundle_unit_size")) { + skipws(); + if (_curchar != '=') { + parse_err(SYNERR, "expected `=`, found '%c'\n", _curchar); + break; + } + + next_char(); skipws(); + pipeline->_bundleUnitSize = get_int(); + skipws(); + + if (_curchar == ';') { + next_char(); skipws(); + } + + continue; + } + + if (!strcmp(ident, "instruction_fetch_unit_size")) { + skipws(); + if (_curchar != '=') { + parse_err(SYNERR, "expected `=`, found '%c'\n", _curchar); + break; + } + + next_char(); skipws(); + pipeline->_instrFetchUnitSize = get_int(); + skipws(); + + if (_curchar == ';') { + next_char(); skipws(); + } + + continue; + } + + if (!strcmp(ident, "instruction_fetch_units")) { + skipws(); + if (_curchar != '=') { + parse_err(SYNERR, "expected `=`, found '%c'\n", _curchar); + break; + } + + next_char(); skipws(); + pipeline->_instrFetchUnits = get_int(); + skipws(); + + if (_curchar == ';') { + next_char(); skipws(); + } + + continue; + } + + if (!strcmp(ident, "nops")) { + skipws(); + if (_curchar != '(') { + parse_err(SYNERR, "expected `(`, found '%c'\n", _curchar); + break; + } + + next_char(); skipws(); + + while (_curchar != ')') { + ident = get_ident(); + if (ident == NULL) { + parse_err(SYNERR, "expected identifier for nop instruction, found '%c'\n", _curchar); + break; + } + + pipeline->_noplist.addName(ident); + pipeline->_nopcnt++; + skipws(); + + if (_curchar == ',') { + next_char(); skipws(); + } + } + + next_char(); skipws(); + + if (_curchar == ';') { + next_char(); skipws(); + } + + continue; + } + + parse_err(SYNERR, "unknown specifier \"%s\"\n", ident); + } + + if ( (_curchar != '%') + || ( next_char(), (_curchar != '}')) ) { + parse_err(SYNERR, "expected '%}', found \"%c\"\n", _curchar); + } + next_char(); skipws(); + + if (pipeline->_maxInstrsPerBundle == 0) + parse_err(SYNERR, "\"max_instructions_per_bundle\" unspecified\n"); + if (pipeline->_instrUnitSize == 0 && pipeline->_bundleUnitSize == 0) + parse_err(SYNERR, "\"instruction_unit_size\" and \"bundle_unit_size\" unspecified\n"); + if (pipeline->_instrFetchUnitSize == 0) + parse_err(SYNERR, "\"instruction_fetch_unit_size\" unspecified\n"); + if (pipeline->_instrFetchUnits == 0) + parse_err(SYNERR, "\"instruction_fetch_units\" unspecified\n"); + if (!vsi_seen) + parse_err(SYNERR, "\"variable_size_instruction\" or \"fixed_size_instruction\" unspecified\n"); + } + else { // Done with staticly defined parts of instruction definition + parse_err(SYNERR, "expected one of \"resources\", \"pipe_desc\", \"pipe_class\", found \"%s\"\n", ident); + return; + } + skipws(); + if (_curchar == ';') + skipws(); + } while(_curchar != '%'); + + next_char(); + if (_curchar != '}') { + parse_err(SYNERR, "missing \"%}\" in pipeline definition\n"); + return; + } + + next_char(); +} + +//------------------------------resource_parse---------------------------- +void ADLParser::resource_parse(PipelineForm &pipeline) { + ResourceForm *resource; + char * ident; + char * expr; + unsigned mask; + pipeline._rescount = 0; + + skipws(); // Skip leading whitespace + + if (_curchar != '(') { + parse_err(SYNERR, "missing \"(\" in resource definition\n"); + return; + } + + do { + next_char(); // Skip "(" or "," + ident = get_ident(); // Grab next identifier + + if (ident == NULL) { + parse_err(SYNERR, "keyword identifier expected at \"%c\"\n", _curchar); + return; + } + skipws(); + + if (_curchar != '=') { + mask = (1 << pipeline._rescount++); + } + else { + next_char(); skipws(); + expr = get_ident(); // Grab next identifier + if (expr == NULL) { + parse_err(SYNERR, "keyword identifier expected at \"%c\"\n", _curchar); + return; + } + resource = (ResourceForm *) pipeline._resdict[expr]; + if (resource == NULL) { + parse_err(SYNERR, "resource \"%s\" is not defined\n", expr); + return; + } + mask = resource->mask(); + + skipws(); + while (_curchar == '|') { + next_char(); skipws(); + + expr = get_ident(); // Grab next identifier + if (expr == NULL) { + parse_err(SYNERR, "keyword identifier expected at \"%c\"\n", _curchar); + return; + } + + resource = (ResourceForm *) pipeline._resdict[expr]; // Look up the value + if (resource == NULL) { + parse_err(SYNERR, "resource \"%s\" is not defined\n", expr); + return; + } + + mask |= resource->mask(); + skipws(); + } + } + + resource = new ResourceForm(mask); + + pipeline._resdict.Insert(ident, resource); + pipeline._reslist.addName(ident); + } while (_curchar == ','); + + if (_curchar != ')') { + parse_err(SYNERR, "\")\" expected at \"%c\"\n", _curchar); + return; + } + + next_char(); // Skip ")" + if (_curchar == ';') + next_char(); // Skip ";" +} + +//------------------------------resource_parse---------------------------- +void ADLParser::pipe_desc_parse(PipelineForm &pipeline) { + char * ident; + + skipws(); // Skip leading whitespace + + if (_curchar != '(') { + parse_err(SYNERR, "missing \"(\" in pipe_desc definition\n"); + return; + } + + do { + next_char(); // Skip "(" or "," + ident = get_ident(); // Grab next identifier + if (ident == NULL) { + parse_err(SYNERR, "keyword identifier expected at \"%c\"\n", _curchar); + return; + } + + // Add the name to the list + pipeline._stages.addName(ident); + pipeline._stagecnt++; + + skipws(); + } while (_curchar == ','); + + if (_curchar != ')') { + parse_err(SYNERR, "\")\" expected at \"%c\"\n", _curchar); + return; + } + + next_char(); // Skip ")" + if (_curchar == ';') + next_char(); // Skip ";" +} + +//------------------------------pipe_class_parse-------------------------- +void ADLParser::pipe_class_parse(PipelineForm &pipeline) { + PipeClassForm *pipe_class; + char * ident; + char * stage; + char * read_or_write; + int is_write; + int is_read; + OperandForm *oper; + + skipws(); // Skip leading whitespace + + ident = get_ident(); // Grab next identifier + + if (ident == NULL) { + parse_err(SYNERR, "keyword identifier expected at \"%c\"\n", _curchar); + return; + } + + // Create a record for the pipe_class + pipe_class = new PipeClassForm(ident, ++pipeline._classcnt); + pipeline._classdict.Insert(ident, pipe_class); + pipeline._classlist.addName(ident); + + // Then get the operands + skipws(); + if (_curchar != '(') { + parse_err(SYNERR, "missing \"(\" in pipe_class definition\n"); + } + // Parse the operand list + else get_oplist(pipe_class->_parameters, pipe_class->_localNames); + skipws(); // Skip leading whitespace + // Check for block delimiter + if ( (_curchar != '%') + || ( next_char(), (_curchar != '{')) ) { + parse_err(SYNERR, "missing \"%{\" in pipe_class definition\n"); + return; + } + next_char(); + + do { + ident = get_ident(); // Grab next identifier + if (ident == NULL) { + parse_err(SYNERR, "keyword identifier expected at \"%c\"\n", _curchar); + continue; + } + skipws(); + + if (!strcmp(ident, "fixed_latency")) { + skipws(); + if (_curchar != '(') { + parse_err(SYNERR, "missing \"(\" in latency definition\n"); + return; + } + next_char(); skipws(); + if( !isdigit(_curchar) ) { + parse_err(SYNERR, "number expected for \"%c\" in latency definition\n", _curchar); + return; + } + int fixed_latency = get_int(); + skipws(); + if (_curchar != ')') { + parse_err(SYNERR, "missing \")\" in latency definition\n"); + return; + } + next_char(); skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing \";\" in latency definition\n"); + return; + } + + pipe_class->setFixedLatency(fixed_latency); + next_char(); skipws(); + continue; + } + + if (!strcmp(ident, "zero_instructions") || + !strcmp(ident, "no_instructions")) { + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing \";\" in latency definition\n"); + return; + } + + pipe_class->setInstructionCount(0); + next_char(); skipws(); + continue; + } + + if (!strcmp(ident, "one_instruction_with_delay_slot") || + !strcmp(ident, "single_instruction_with_delay_slot")) { + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing \";\" in latency definition\n"); + return; + } + + pipe_class->setInstructionCount(1); + pipe_class->setBranchDelay(true); + next_char(); skipws(); + continue; + } + + if (!strcmp(ident, "one_instruction") || + !strcmp(ident, "single_instruction")) { + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing \";\" in latency definition\n"); + return; + } + + pipe_class->setInstructionCount(1); + next_char(); skipws(); + continue; + } + + if (!strcmp(ident, "instructions_in_first_bundle") || + !strcmp(ident, "instruction_count")) { + skipws(); + + int number_of_instructions = 1; + + if (_curchar != '(') { + parse_err(SYNERR, "\"(\" expected at \"%c\"\n", _curchar); + continue; + } + + next_char(); skipws(); + number_of_instructions = get_int(); + + skipws(); + if (_curchar != ')') { + parse_err(SYNERR, "\")\" expected at \"%c\"\n", _curchar); + continue; + } + + next_char(); skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing \";\" in latency definition\n"); + return; + } + + pipe_class->setInstructionCount(number_of_instructions); + next_char(); skipws(); + continue; + } + + if (!strcmp(ident, "multiple_bundles")) { + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing \";\" after multiple bundles\n"); + return; + } + + pipe_class->setMultipleBundles(true); + next_char(); skipws(); + continue; + } + + if (!strcmp(ident, "has_delay_slot")) { + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing \";\" after \"has_delay_slot\"\n"); + return; + } + + pipe_class->setBranchDelay(true); + next_char(); skipws(); + continue; + } + + if (!strcmp(ident, "force_serialization")) { + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing \";\" after \"force_serialization\"\n"); + return; + } + + pipe_class->setForceSerialization(true); + next_char(); skipws(); + continue; + } + + if (!strcmp(ident, "may_have_no_code")) { + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing \";\" after \"may_have_no_code\"\n"); + return; + } + + pipe_class->setMayHaveNoCode(true); + next_char(); skipws(); + continue; + } + + const Form *parm = pipe_class->_localNames[ident]; + if (parm != NULL) { + oper = parm->is_operand(); + if (oper == NULL && !parm->is_opclass()) { + parse_err(SYNERR, "operand name expected at %s\n", ident); + continue; + } + + if (_curchar != ':') { + parse_err(SYNERR, "\":\" expected at \"%c\"\n", _curchar); + continue; + } + next_char(); skipws(); + stage = get_ident(); + if (stage == NULL) { + parse_err(SYNERR, "pipeline stage identifier expected at \"%c\"\n", _curchar); + continue; + } + + skipws(); + if (_curchar != '(') { + parse_err(SYNERR, "\"(\" expected at \"%c\"\n", _curchar); + continue; + } + + next_char(); + read_or_write = get_ident(); + if (read_or_write == NULL) { + parse_err(SYNERR, "\"read\" or \"write\" expected at \"%c\"\n", _curchar); + continue; + } + + is_read = strcmp(read_or_write, "read") == 0; + is_write = strcmp(read_or_write, "write") == 0; + if (!is_read && !is_write) { + parse_err(SYNERR, "\"read\" or \"write\" expected at \"%c\"\n", _curchar); + continue; + } + + skipws(); + if (_curchar != ')') { + parse_err(SYNERR, "\")\" expected at \"%c\"\n", _curchar); + continue; + } + + next_char(); skipws(); + int more_instrs = 0; + if (_curchar == '+') { + next_char(); skipws(); + if (_curchar < '0' || _curchar > '9') { + parse_err(SYNERR, " expected at \"%c\"\n", _curchar); + continue; + } + while (_curchar >= '0' && _curchar <= '9') { + more_instrs *= 10; + more_instrs += _curchar - '0'; + next_char(); + } + skipws(); + } + + PipeClassOperandForm *pipe_operand = new PipeClassOperandForm(stage, is_write, more_instrs); + pipe_class->_localUsage.Insert(ident, pipe_operand); + + if (_curchar == '%') + continue; + + if (_curchar != ';') { + parse_err(SYNERR, "\";\" expected at \"%c\"\n", _curchar); + continue; + } + next_char(); skipws(); + continue; + } + + // Scan for Resource Specifier + const Form *res = pipeline._resdict[ident]; + if (res != NULL) { + int cyclecnt = 1; + if (_curchar != ':') { + parse_err(SYNERR, "\":\" expected at \"%c\"\n", _curchar); + continue; + } + next_char(); skipws(); + stage = get_ident(); + if (stage == NULL) { + parse_err(SYNERR, "pipeline stage identifier expected at \"%c\"\n", _curchar); + continue; + } + + skipws(); + if (_curchar == '(') { + next_char(); + cyclecnt = get_int(); + + skipws(); + if (_curchar != ')') { + parse_err(SYNERR, "\")\" expected at \"%c\"\n", _curchar); + continue; + } + + next_char(); skipws(); + } + + PipeClassResourceForm *resource = new PipeClassResourceForm(ident, stage, cyclecnt); + int stagenum = pipeline._stages.index(stage); + if (pipeline._maxcycleused < (stagenum+cyclecnt)) + pipeline._maxcycleused = (stagenum+cyclecnt); + pipe_class->_resUsage.addForm(resource); + + if (_curchar == '%') + continue; + + if (_curchar != ';') { + parse_err(SYNERR, "\";\" expected at \"%c\"\n", _curchar); + continue; + } + next_char(); skipws(); + continue; + } + + parse_err(SYNERR, "resource expected at \"%s\"\n", ident); + return; + } while(_curchar != '%'); + + next_char(); + if (_curchar != '}') { + parse_err(SYNERR, "missing \"%}\" in pipe_class definition\n"); + return; + } + + next_char(); +} + +//------------------------------peep_parse------------------------------------- +void ADLParser::peep_parse(void) { + Peephole *peep; // Pointer to current peephole rule form + char *desc = NULL; // String representation of rule + + skipws(); // Skip leading whitespace + + peep = new Peephole(); // Build new Peephole object + // Check for open block sequence + skipws(); // Skip leading whitespace + if (_curchar == '%' && *(_ptr+1) == '{') { + next_char(); next_char(); // Skip "%{" + skipws(); + while (_curchar != '%' && *(_ptr+1) != '}') { + char *token = get_ident(); + if (token == NULL) { + parse_err(SYNERR, "missing identifier inside peephole rule.\n"); + return; + } + // check for legal subsections of peephole rule + if (strcmp(token,"peepmatch")==0) { + peep_match_parse(*peep); } + else if (strcmp(token,"peepconstraint")==0) { + peep_constraint_parse(*peep); } + else if (strcmp(token,"peepreplace")==0) { + peep_replace_parse(*peep); } + else { + parse_err(SYNERR, "expected peepmatch, peepconstraint, or peepreplace for identifier %s.\n", token); + } + skipws(); + } + } + else { + parse_err(SYNERR, "Missing %%{ ... %%} block after peephole keyword.\n"); + return; + } + next_char(); // Skip past '%' + next_char(); // Skip past '}' +} + +// ******************** Private Level 2 Parse Functions ******************** +//------------------------------constraint_parse------------------------------ +Constraint *ADLParser::constraint_parse(void) { + char *func; + char *arg; + + // Check for constraint expression + skipws(); + if (_curchar != '(') { + parse_err(SYNERR, "missing constraint expression, (...)\n"); + return NULL; + } + next_char(); // Skip past '(' + + // Get constraint function + skipws(); + func = get_ident(); + if (func == NULL) { + parse_err(SYNERR, "missing function in constraint expression.\n"); + return NULL; + } + if (strcmp(func,"ALLOC_IN_RC")==0 + || strcmp(func,"IS_R_CLASS")==0) { + // Check for '(' before argument + skipws(); + if (_curchar != '(') { + parse_err(SYNERR, "missing '(' for constraint function's argument.\n"); + return NULL; + } + next_char(); + + // Get it's argument + skipws(); + arg = get_ident(); + if (arg == NULL) { + parse_err(SYNERR, "missing argument for constraint function %s\n",func); + return NULL; + } + // Check for ')' after argument + skipws(); + if (_curchar != ')') { + parse_err(SYNERR, "missing ')' after constraint function argument %s\n",arg); + return NULL; + } + next_char(); + } else { + parse_err(SYNERR, "Invalid constraint function %s\n",func); + return NULL; + } + + // Check for closing paren and ';' + skipws(); + if (_curchar != ')') { + parse_err(SYNERR, "Missing ')' for constraint function %s\n",func); + return NULL; + } + next_char(); + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "Missing ';' after constraint.\n"); + return NULL; + } + next_char(); + + // Create new "Constraint" + Constraint *constraint = new Constraint(func,arg); + return constraint; +} + +//------------------------------constr_parse----------------------------------- +ConstructRule *ADLParser::construct_parse(void) { + return NULL; +} + + +//------------------------------reg_def_parse---------------------------------- +void ADLParser::reg_def_parse(void) { + char *rname; // Name of register being defined + + // Get register name + skipws(); // Skip whitespace + rname = get_ident(); + if (rname == NULL) { + parse_err(SYNERR, "missing register name after reg_def\n"); + return; + } + + // Check for definition of register calling convention (save on call, ...), + // register save type, and register encoding value. + skipws(); + char *callconv = NULL; + char *c_conv = NULL; + char *idealtype = NULL; + char *encoding = NULL; + char *concrete = NULL; + if (_curchar == '(') { + next_char(); + callconv = get_ident(); + // Parse the internal calling convention, must be NS, SOC, SOE, or AS. + if (callconv == NULL) { + parse_err(SYNERR, "missing register calling convention value\n"); + return; + } + if(strcmp(callconv, "SOC") && strcmp(callconv,"SOE") && + strcmp(callconv, "NS") && strcmp(callconv, "AS")) { + parse_err(SYNERR, "invalid value for register calling convention\n"); + } + skipws(); + if (_curchar != ',') { + parse_err(SYNERR, "missing comma in register definition statement\n"); + return; + } + next_char(); + + // Parse the native calling convention, must be NS, SOC, SOE, AS + c_conv = get_ident(); + if (c_conv == NULL) { + parse_err(SYNERR, "missing register native calling convention value\n"); + return; + } + if(strcmp(c_conv, "SOC") && strcmp(c_conv,"SOE") && + strcmp(c_conv, "NS") && strcmp(c_conv, "AS")) { + parse_err(SYNERR, "invalid value for register calling convention\n"); + } + skipws(); + if (_curchar != ',') { + parse_err(SYNERR, "missing comma in register definition statement\n"); + return; + } + next_char(); + skipws(); + + // Parse the ideal save type + idealtype = get_ident(); + if (idealtype == NULL) { + parse_err(SYNERR, "missing register save type value\n"); + return; + } + skipws(); + if (_curchar != ',') { + parse_err(SYNERR, "missing comma in register definition statement\n"); + return; + } + next_char(); + skipws(); + + // Parse the encoding value + encoding = get_expr("encoding", ","); + if (encoding == NULL) { + parse_err(SYNERR, "missing register encoding value\n"); + return; + } + trim(encoding); + if (_curchar != ',') { + parse_err(SYNERR, "missing comma in register definition statement\n"); + return; + } + next_char(); + skipws(); + // Parse the concrete name type + // concrete = get_ident(); + concrete = get_expr("concrete", ")"); + if (concrete == NULL) { + parse_err(SYNERR, "missing vm register name value\n"); + return; + } + + if (_curchar != ')') { + parse_err(SYNERR, "missing ')' in register definition statement\n"); + return; + } + next_char(); + } + + // Check for closing ';' + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' after reg_def\n"); + return; + } + next_char(); // move past ';' + + // Debug Stuff + if (_AD._adl_debug > 1) { + fprintf(stderr,"Register Definition: %s ( %s, %s %s )\n", rname, + (callconv ? callconv : ""), (c_conv ? c_conv : ""), concrete); + } + + // Record new register definition. + _AD._register->addRegDef(rname, callconv, c_conv, idealtype, encoding, concrete); + return; +} + +//------------------------------reg_class_parse-------------------------------- +void ADLParser::reg_class_parse(void) { + char *cname; // Name of register class being defined + + // Get register class name + skipws(); // Skip leading whitespace + cname = get_ident(); + if (cname == NULL) { + parse_err(SYNERR, "missing register class name after 'reg_class'\n"); + return; + } + // Debug Stuff + if (_AD._adl_debug >1) fprintf(stderr,"Register Class: %s\n", cname); + + RegClass *reg_class = _AD._register->addRegClass(cname); + + // Collect registers in class + skipws(); + if (_curchar == '(') { + next_char(); // Skip '(' + skipws(); + while (_curchar != ')') { + char *rname = get_ident(); + if (rname==NULL) { + parse_err(SYNERR, "missing identifier inside reg_class list.\n"); + return; + } + RegDef *regDef = _AD._register->getRegDef(rname); + reg_class->addReg(regDef); // add regDef to regClass + + // Check for ',' and position to next token. + skipws(); + if (_curchar == ',') { + next_char(); // Skip trailing ',' + skipws(); + } + } + next_char(); // Skip closing ')' + } + + // Check for terminating ';' + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' at end of reg_class definition.\n"); + return; + } + next_char(); // Skip trailing ';' + + // Check RegClass size, must be <= 32 registers in class. + + return; +} + +//------------------------------alloc_class_parse------------------------------ +void ADLParser::alloc_class_parse(void) { + char *name; // Name of allocation class being defined + + // Get allocation class name + skipws(); // Skip leading whitespace + name = get_ident(); + if (name == NULL) { + parse_err(SYNERR, "missing allocation class name after 'reg_class'\n"); + return; + } + // Debug Stuff + if (_AD._adl_debug >1) fprintf(stderr,"Allocation Class: %s\n", name); + + AllocClass *alloc_class = _AD._register->addAllocClass(name); + + // Collect registers in class + skipws(); + if (_curchar == '(') { + next_char(); // Skip '(' + skipws(); + while (_curchar != ')') { + char *rname = get_ident(); + if (rname==NULL) { + parse_err(SYNERR, "missing identifier inside reg_class list.\n"); + return; + } + // Check if name is a RegDef + RegDef *regDef = _AD._register->getRegDef(rname); + if (regDef) { + alloc_class->addReg(regDef); // add regDef to allocClass + } else { + + // name must be a RegDef or a RegClass + parse_err(SYNERR, "name %s should be a previously defined reg_def.\n", rname); + return; + } + + // Check for ',' and position to next token. + skipws(); + if (_curchar == ',') { + next_char(); // Skip trailing ',' + skipws(); + } + } + next_char(); // Skip closing ')' + } + + // Check for terminating ';' + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' at end of reg_class definition.\n"); + return; + } + next_char(); // Skip trailing ';' + + return; +} + +//------------------------------peep_match_child_parse------------------------- +InstructForm *ADLParser::peep_match_child_parse(PeepMatch &match, int parent, int &position, int input){ + char *token = NULL; + int lparen = 0; // keep track of parenthesis nesting depth + int rparen = 0; // position of instruction at this depth + InstructForm *inst_seen = NULL; + InstructForm *child_seen = NULL; + + // Walk the match tree, + // Record + while ( lparen >= rparen ) { + skipws(); + // Left paren signals start of an input, collect with recursive call + if (_curchar == '(') { + ++lparen; + next_char(); + child_seen = peep_match_child_parse(match, parent, position, rparen); + } + // Right paren signals end of an input, may be more + else if (_curchar == ')') { + ++rparen; + if( rparen == lparen ) { // IF rparen matches an lparen I've seen + next_char(); // move past ')' + } else { // ELSE leave ')' for parent + assert( rparen == lparen + 1, "Should only see one extra ')'"); + // if an instruction was not specified for this paren-pair + if( ! inst_seen ) { // record signal entry + match.add_instruction( parent, position, NameList::_signal, input ); + ++position; + } + // ++input; // TEMPORARY + return inst_seen; + } + } + // if no parens, then check for instruction name + // This instruction is the parent of a sub-tree + else if ((token = get_ident_dup()) != NULL) { + const Form *form = _AD._globalNames[token]; + if (form) { + InstructForm *inst = form->is_instruction(); + // Record the first instruction at this level + if( inst_seen == NULL ) { + inst_seen = inst; + } + if (inst) { + match.add_instruction( parent, position, token, input ); + parent = position; + ++position; + } else { + parse_err(SYNERR, "instruction name expected at identifier %s.\n", + token); + return inst_seen; + } + } + else { + parse_err(SYNERR, "missing identifier in peepmatch rule.\n"); + return NULL; + } + } + else { + parse_err(SYNERR, "missing identifier in peepmatch rule.\n"); + return NULL; + } + + } // end while + + assert( false, "ShouldNotReachHere();"); + return NULL; +} + +//------------------------------peep_match_parse------------------------------- +// Syntax for a peepmatch rule +// +// peepmatch ( root_instr_name [(instruction subtree)] [,(instruction subtree)]* ); +// +void ADLParser::peep_match_parse(Peephole &peep) { + + skipws(); + // Check the structure of the rule + // Check for open paren + if (_curchar != '(') { + parse_err(SYNERR, "missing '(' at start of peepmatch rule.\n"); + return; + } + next_char(); // skip '(' + + // Construct PeepMatch and parse the peepmatch rule. + PeepMatch *match = new PeepMatch(_ptr); + int parent = -1; // parent of root + int position = 0; // zero-based positions + int input = 0; // input position in parent's operands + InstructForm *root= peep_match_child_parse( *match, parent, position, input); + if( root == NULL ) { + parse_err(SYNERR, "missing instruction-name at start of peepmatch.\n"); + return; + } + + if( _curchar != ')' ) { + parse_err(SYNERR, "missing ')' at end of peepmatch.\n"); + return; + } + next_char(); // skip ')' + + // Check for closing semicolon + skipws(); + if( _curchar != ';' ) { + parse_err(SYNERR, "missing ';' at end of peepmatch.\n"); + return; + } + next_char(); // skip ';' + + // Store match into peep, and store peep into instruction + peep.add_match(match); + root->append_peephole(&peep); +} + +//------------------------------peep_constraint_parse-------------------------- +// Syntax for a peepconstraint rule +// A parenthesized list of relations between operands in peepmatch subtree +// +// peepconstraint %{ +// (instruction_number.operand_name +// relational_op +// instruction_number.operand_name OR register_name +// [, ...] ); +// +// // instruction numbers are zero-based using topological order in peepmatch +// +void ADLParser::peep_constraint_parse(Peephole &peep) { + + skipws(); + // Check the structure of the rule + // Check for open paren + if (_curchar != '(') { + parse_err(SYNERR, "missing '(' at start of peepconstraint rule.\n"); + return; + } + else { + next_char(); // Skip '(' + } + + // Check for a constraint + skipws(); + while( _curchar != ')' ) { + // Get information on the left instruction and its operand + // left-instructions's number + intptr_t left_inst = get_int(); + // Left-instruction's operand + skipws(); + if( _curchar != '.' ) { + parse_err(SYNERR, "missing '.' in peepconstraint after instruction number.\n"); + return; + } + next_char(); // Skip '.' + char *left_op = get_ident_dup(); + + skipws(); + // Collect relational operator + char *relation = get_relation_dup(); + + skipws(); + // Get information on the right instruction and its operand + intptr_t right_inst; // Right-instructions's number + if( isdigit(_curchar) ) { + right_inst = get_int(); + // Right-instruction's operand + skipws(); + if( _curchar != '.' ) { + parse_err(SYNERR, "missing '.' in peepconstraint after instruction number.\n"); + return; + } + next_char(); // Skip '.' + } else { + right_inst = -1; // Flag as being a register constraint + } + + char *right_op = get_ident_dup(); + + // Construct the next PeepConstraint + PeepConstraint *constraint = new PeepConstraint( left_inst, left_op, + relation, + right_inst, right_op ); + // And append it to the list for this peephole rule + peep.append_constraint( constraint ); + + // Check for another constraint, or end of rule + skipws(); + if( _curchar == ',' ) { + next_char(); // Skip ',' + skipws(); + } + else if( _curchar != ')' ) { + parse_err(SYNERR, "expected ',' or ')' after peephole constraint.\n"); + return; + } + } // end while( processing constraints ) + next_char(); // Skip ')' + + // Check for terminating ';' + skipws(); + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' at end of peepconstraint.\n"); + return; + } + next_char(); // Skip trailing ';' +} + + +//------------------------------peep_replace_parse----------------------------- +// Syntax for a peepreplace rule +// root instruction name followed by a +// parenthesized list of whitespace separated instruction.operand specifiers +// +// peepreplace ( instr_name ( [instruction_number.operand_name]* ) ); +// +// +void ADLParser::peep_replace_parse(Peephole &peep) { + int lparen = 0; // keep track of parenthesis nesting depth + int rparen = 0; // keep track of parenthesis nesting depth + int icount = 0; // count of instructions in rule for naming + char *str = NULL; + char *token = NULL; + + skipws(); + // Check for open paren + if (_curchar != '(') { + parse_err(SYNERR, "missing '(' at start of peepreplace rule.\n"); + return; + } + else { + lparen++; + next_char(); + } + + // Check for root instruction + char *inst = get_ident_dup(); + const Form *form = _AD._globalNames[inst]; + if( form == NULL || form->is_instruction() == NULL ) { + parse_err(SYNERR, "Instruction name expected at start of peepreplace.\n"); + return; + } + + // Store string representation of rule into replace + PeepReplace *replace = new PeepReplace(str); + replace->add_instruction( inst ); + + skipws(); + // Start of root's operand-list + if (_curchar != '(') { + parse_err(SYNERR, "missing '(' at peepreplace root's operand-list.\n"); + return; + } + else { + lparen++; + next_char(); + } + + skipws(); + // Get the list of operands + while( _curchar != ')' ) { + // Get information on an instruction and its operand + // instructions's number + int inst_num = get_int(); + // Left-instruction's operand + skipws(); + if( _curchar != '.' ) { + parse_err(SYNERR, "missing '.' in peepreplace after instruction number.\n"); + return; + } + next_char(); // Skip '.' + char *inst_op = get_ident_dup(); + if( inst_op == NULL ) { + parse_err(SYNERR, "missing operand identifier in peepreplace.\n"); + return; + } + + // Record this operand's position in peepmatch + replace->add_operand( inst_num, inst_op ); + skipws(); + } + + // Check for the end of operands list + skipws(); + assert( _curchar == ')', "While loop should have advanced to ')'."); + next_char(); // Skip ')' + + skipws(); + // Check for end of peepreplace + if( _curchar != ')' ) { + parse_err(SYNERR, "missing ')' at end of peepmatch.\n"); + parse_err(SYNERR, "Support one replacement instruction.\n"); + return; + } + next_char(); // Skip ')' + + // Check for closing semicolon + skipws(); + if( _curchar != ';' ) { + parse_err(SYNERR, "missing ';' at end of peepreplace.\n"); + return; + } + next_char(); // skip ';' + + // Store replace into peep + peep.add_replace( replace ); +} + +//------------------------------pred_parse------------------------------------- +Predicate *ADLParser::pred_parse(void) { + Predicate *predicate; // Predicate class for operand + char *rule = NULL; // String representation of predicate + + skipws(); // Skip leading whitespace + if ( (rule = get_paren_expr("pred expression")) == NULL ) { + parse_err(SYNERR, "incorrect or missing expression for 'predicate'\n"); + return NULL; + } + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Predicate: %s\n", rule); + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' in predicate definition\n"); + return NULL; + } + next_char(); // Point after the terminator + + predicate = new Predicate(rule); // Build new predicate object + skipws(); + return predicate; +} + + +//------------------------------ins_encode_parse_block------------------------- +// Parse the block form of ins_encode. See ins_encode_parse for more details +InsEncode *ADLParser::ins_encode_parse_block(InstructForm &inst) { + // Create a new encoding name based on the name of the instruction + // definition, which should be unique. + const char * prefix = "__enc_"; + char* ec_name = (char*)malloc(strlen(inst._ident) + strlen(prefix) + 1); + sprintf(ec_name, "%s%s", prefix, inst._ident); + + assert(_AD._encode->encClass(ec_name) == NULL, "shouldn't already exist"); + EncClass *encoding = _AD._encode->add_EncClass(ec_name); + encoding->_linenum = _linenum; + + // synthesize the arguments list for the enc_class from the + // arguments to the instruct definition. + const char * param = NULL; + inst._parameters.reset(); + while ((param = inst._parameters.iter()) != NULL) { + OperandForm *opForm = (OperandForm*)inst._localNames[param]; + encoding->add_parameter(opForm->_ident, param); + } + + // Add the prologue to create the MacroAssembler + encoding->add_code("\n" + " // Define a MacroAssembler instance for use by the encoding. The\n" + " // name is chosen to match the __ idiom used for assembly in other\n" + " // parts of hotspot and assumes the existence of the standard\n" + " // #define __ _masm.\n" + " MacroAssembler _masm(&cbuf);\n"); + + // Parse the following %{ }% block + enc_class_parse_block(encoding, ec_name); + + // Build an encoding rule which invokes the encoding rule we just + // created, passing all arguments that we received. + InsEncode *encrule = new InsEncode(); // Encode class for instruction + NameAndList *params = encrule->add_encode(ec_name); + inst._parameters.reset(); + while ((param = inst._parameters.iter()) != NULL) { + params->add_entry(param); + } + + return encrule; +} + + +//------------------------------ins_encode_parse------------------------------- +// Encode rules have the form +// ins_encode( encode_class_name(parameter_list), ... ); +// +// The "encode_class_name" must be defined in the encode section +// The parameter list contains $names that are locals. +// +// Alternatively it can be written like this: +// +// ins_encode %{ +// ... // body +// %} +// +// which synthesizes a new encoding class taking the same arguments as +// the InstructForm, and automatically prefixes the definition with: +// +// MacroAssembler masm(&cbuf);\n"); +// +// making it more compact to take advantage of the MacroAssembler and +// placing the assembly closer to it's use by instructions. +InsEncode *ADLParser::ins_encode_parse(InstructForm &inst) { + + // Parse encode class name + skipws(); // Skip whitespace + if (_curchar != '(') { + // Check for ins_encode %{ form + if ((_curchar == '%') && (*(_ptr+1) == '{')) { + next_char(); // Skip '%' + next_char(); // Skip '{' + + // Parse the block form of ins_encode + return ins_encode_parse_block(inst); + } + + parse_err(SYNERR, "missing '%%{' or '(' in ins_encode definition\n"); + return NULL; + } + next_char(); // move past '(' + skipws(); + + InsEncode *encrule = new InsEncode(); // Encode class for instruction + encrule->_linenum = _linenum; + char *ec_name = NULL; // String representation of encode rule + // identifier is optional. + while (_curchar != ')') { + ec_name = get_ident(); + if (ec_name == NULL) { + parse_err(SYNERR, "Invalid encode class name after 'ins_encode('.\n"); + return NULL; + } + // Check that encoding is defined in the encode section + EncClass *encode_class = _AD._encode->encClass(ec_name); + if (encode_class == NULL) { + // Like to defer checking these till later... + // parse_err(WARN, "Using an undefined encode class '%s' in 'ins_encode'.\n", ec_name); + } + + // Get list for encode method's parameters + NameAndList *params = encrule->add_encode(ec_name); + + // Parse the parameters to this encode method. + skipws(); + if ( _curchar == '(' ) { + next_char(); // move past '(' for parameters + + // Parse the encode method's parameters + while (_curchar != ')') { + char *param = get_ident_or_literal_constant("encoding operand"); + if ( param != NULL ) { + // Found a parameter: + // Check it is a local name, add it to the list, then check for more + // New: allow hex constants as parameters to an encode method. + // New: allow parenthesized expressions as parameters. + // New: allow "primary", "secondary", "tertiary" as parameters. + // New: allow user-defined register name as parameter + if ( (inst._localNames[param] == NULL) && + !ADLParser::is_literal_constant(param) && + (Opcode::as_opcode_type(param) == Opcode::NOT_AN_OPCODE) && + ((_AD._register == NULL ) || (_AD._register->getRegDef(param) == NULL)) ) { + parse_err(SYNERR, "Using non-locally defined parameter %s for encoding %s.\n", param, ec_name); + return NULL; + } + params->add_entry(param); + + skipws(); + if (_curchar == ',' ) { + // More parameters to come + next_char(); // move past ',' between parameters + skipws(); // Skip to next parameter + } + else if (_curchar == ')') { + // Done with parameter list + } + else { + // Only ',' or ')' are valid after a parameter name + parse_err(SYNERR, "expected ',' or ')' after parameter %s.\n", + ec_name); + return NULL; + } + + } else { + skipws(); + // Did not find a parameter + if (_curchar == ',') { + parse_err(SYNERR, "Expected encode parameter before ',' in encoding %s.\n", ec_name); + return NULL; + } + if (_curchar != ')') { + parse_err(SYNERR, "Expected ')' after encode parameters.\n"); + return NULL; + } + } + } // WHILE loop collecting parameters + next_char(); // move past ')' at end of parameters + } // done with parameter list for encoding + + // Check for ',' or ')' after encoding + skipws(); // move to character after parameters + if ( _curchar == ',' ) { + // Found a ',' + next_char(); // move past ',' between encode methods + skipws(); + } + else if ( _curchar != ')' ) { + // If not a ',' then only a ')' is allowed + parse_err(SYNERR, "Expected ')' after encoding %s.\n", ec_name); + return NULL; + } + + // Check for ',' separating parameters + // if ( _curchar != ',' && _curchar != ')' ) { + // parse_err(SYNERR, "expected ',' or ')' after encode method inside ins_encode.\n"); + // return NULL; + // } + + } // done parsing ins_encode methods and their parameters + if (_curchar != ')') { + parse_err(SYNERR, "Missing ')' at end of ins_encode description.\n"); + return NULL; + } + next_char(); // move past ')' + skipws(); // Skip leading whitespace + + if ( _curchar != ';' ) { + parse_err(SYNERR, "Missing ';' at end of ins_encode.\n"); + return NULL; + } + next_char(); // move past ';' + skipws(); // be friendly to oper_parse() + + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Instruction Encode: %s\n", ec_name); + + return encrule; +} + + +//------------------------------size_parse----------------------------------- +char* ADLParser::size_parse(InstructForm *instr) { + char* sizeOfInstr = NULL; + + // Get value of the instruction's size + skipws(); + + // Parse size + sizeOfInstr = get_paren_expr("size expression"); + if (sizeOfInstr == NULL) { + parse_err(SYNERR, "size of opcode expected at %c\n", _curchar); + return NULL; + } + + skipws(); + + // Check for terminator + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' in ins_attrib definition\n"); + return NULL; + } + next_char(); // Advance past the ';' + skipws(); // necessary for instr_parse() + + // Debug Stuff + if (_AD._adl_debug > 1) { + if (sizeOfInstr != NULL) { + fprintf(stderr,"size of opcode: %s\n", sizeOfInstr); + } + } + + return sizeOfInstr; +} + + +//------------------------------opcode_parse----------------------------------- +Opcode * ADLParser::opcode_parse(InstructForm *instr) { + char *primary = NULL; + char *secondary = NULL; + char *tertiary = NULL; + + char *val = NULL; + Opcode *opcode = NULL; + + // Get value of the instruction's opcode + skipws(); + if (_curchar != '(') { // Check for parenthesized operand list + parse_err(SYNERR, "missing '(' in expand instruction declaration\n"); + return NULL; + } + next_char(); // skip open paren + skipws(); + if (_curchar != ')') { + // Parse primary, secondary, and tertiary opcodes, if provided. + if ( ((primary = get_ident_or_literal_constant("primary opcode")) == NULL) ) { + parse_err(SYNERR, "primary hex opcode expected at %c\n", _curchar); + return NULL; + } + skipws(); + if (_curchar == ',') { + next_char(); + skipws(); + // Parse secondary opcode + if ( ((secondary = get_ident_or_literal_constant("secondary opcode")) == NULL) ) { + parse_err(SYNERR, "secondary hex opcode expected at %c\n", _curchar); + return NULL; + } + skipws(); + if (_curchar == ',') { + next_char(); + skipws(); + // Parse tertiary opcode + if ( ((tertiary = get_ident_or_literal_constant("tertiary opcode")) == NULL) ) { + parse_err(SYNERR,"tertiary hex opcode expected at %c\n", _curchar); + return NULL; + } + skipws(); + } + } + skipws(); + if (_curchar != ')') { + parse_err(SYNERR, "Missing ')' in opcode description\n"); + return NULL; + } + } + next_char(); // Skip ')' + skipws(); + // Check for terminator + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' in ins_attrib definition\n"); + return NULL; + } + next_char(); // Advance past the ';' + skipws(); // necessary for instr_parse() + + // Debug Stuff + if (_AD._adl_debug > 1) { + if (primary != NULL) fprintf(stderr,"primary opcode: %s\n", primary); + if (secondary != NULL) fprintf(stderr,"secondary opcode: %s\n", secondary); + if (tertiary != NULL) fprintf(stderr,"tertiary opcode: %s\n", tertiary); + } + + // Generate new object and return + opcode = new Opcode(primary, secondary, tertiary); + return opcode; +} + + +//------------------------------interface_parse-------------------------------- +Interface *ADLParser::interface_parse(void) { + char *iface_name = NULL; // Name of interface class being used + char *iface_code = NULL; // Describe components of this class + + // Get interface class name + skipws(); // Skip whitespace + if (_curchar != '(') { + parse_err(SYNERR, "Missing '(' at start of interface description.\n"); + return NULL; + } + next_char(); // move past '(' + skipws(); + iface_name = get_ident(); + if (iface_name == NULL) { + parse_err(SYNERR, "missing interface name after 'interface'.\n"); + return NULL; + } + skipws(); + if (_curchar != ')') { + parse_err(SYNERR, "Missing ')' after name of interface.\n"); + return NULL; + } + next_char(); // move past ')' + + // Get details of the interface, + // for the type of interface indicated by iface_name. + Interface *inter = NULL; + skipws(); + if ( _curchar != ';' ) { + if ( strcmp(iface_name,"MEMORY_INTER") == 0 ) { + inter = mem_interface_parse(); + } + else if ( strcmp(iface_name,"COND_INTER") == 0 ) { + inter = cond_interface_parse(); + } + // The parse routines consume the "%}" + + // Check for probable extra ';' after defining block. + if ( _curchar == ';' ) { + parse_err(SYNERR, "Extra ';' after defining interface block.\n"); + next_char(); // Skip ';' + return NULL; + } + } else { + next_char(); // move past ';' + + // Create appropriate interface object + if ( strcmp(iface_name,"REG_INTER") == 0 ) { + inter = new RegInterface(); + } + else if ( strcmp(iface_name,"CONST_INTER") == 0 ) { + inter = new ConstInterface(); + } + } + skipws(); // be friendly to oper_parse() + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Interface Form: %s\n", iface_name); + + // Create appropriate interface object and return. + return inter; +} + + +//------------------------------mem_interface_parse---------------------------- +Interface *ADLParser::mem_interface_parse(void) { + // Fields for MemInterface + char *base = NULL; + char *index = NULL; + char *scale = NULL; + char *disp = NULL; + + if (_curchar != '%') { + parse_err(SYNERR, "Missing '%{' for 'interface' block.\n"); + return NULL; + } + next_char(); // Skip '%' + if (_curchar != '{') { + parse_err(SYNERR, "Missing '%{' for 'interface' block.\n"); + return NULL; + } + next_char(); // Skip '{' + skipws(); + do { + char *field = get_ident(); + if (field == NULL) { + parse_err(SYNERR, "Expected keyword, base|index|scale|disp, or '%}' ending interface.\n"); + return NULL; + } + if ( strcmp(field,"base") == 0 ) { + base = interface_field_parse(); + } + else if ( strcmp(field,"index") == 0 ) { + index = interface_field_parse(); + } + else if ( strcmp(field,"scale") == 0 ) { + scale = interface_field_parse(); + } + else if ( strcmp(field,"disp") == 0 ) { + disp = interface_field_parse(); + } + else { + parse_err(SYNERR, "Expected keyword, base|index|scale|disp, or '%}' ending interface.\n"); + return NULL; + } + } while( _curchar != '%' ); + next_char(); // Skip '%' + if ( _curchar != '}' ) { + parse_err(SYNERR, "Missing '%}' for 'interface' block.\n"); + return NULL; + } + next_char(); // Skip '}' + + // Construct desired object and return + Interface *inter = new MemInterface(base, index, scale, disp); + return inter; +} + + +//------------------------------cond_interface_parse--------------------------- +Interface *ADLParser::cond_interface_parse(void) { + char *equal; + char *not_equal; + char *less; + char *greater_equal; + char *less_equal; + char *greater; + + if (_curchar != '%') { + parse_err(SYNERR, "Missing '%{' for 'cond_interface' block.\n"); + return NULL; + } + next_char(); // Skip '%' + if (_curchar != '{') { + parse_err(SYNERR, "Missing '%{' for 'cond_interface' block.\n"); + return NULL; + } + next_char(); // Skip '{' + skipws(); + do { + char *field = get_ident(); + if (field == NULL) { + parse_err(SYNERR, "Expected keyword, base|index|scale|disp, or '%}' ending interface.\n"); + return NULL; + } + if ( strcmp(field,"equal") == 0 ) { + equal = interface_field_parse(); + } + else if ( strcmp(field,"not_equal") == 0 ) { + not_equal = interface_field_parse(); + } + else if ( strcmp(field,"less") == 0 ) { + less = interface_field_parse(); + } + else if ( strcmp(field,"greater_equal") == 0 ) { + greater_equal = interface_field_parse(); + } + else if ( strcmp(field,"less_equal") == 0 ) { + less_equal = interface_field_parse(); + } + else if ( strcmp(field,"greater") == 0 ) { + greater = interface_field_parse(); + } + else { + parse_err(SYNERR, "Expected keyword, base|index|scale|disp, or '%}' ending interface.\n"); + return NULL; + } + } while( _curchar != '%' ); + next_char(); // Skip '%' + if ( _curchar != '}' ) { + parse_err(SYNERR, "Missing '%}' for 'interface' block.\n"); + return NULL; + } + next_char(); // Skip '}' + + // Construct desired object and return + Interface *inter = new CondInterface(equal, not_equal, less, greater_equal, + less_equal, greater); + return inter; +} + + +//------------------------------interface_field_parse-------------------------- +char *ADLParser::interface_field_parse(void) { + char *iface_field = NULL; + + // Get interface field + skipws(); // Skip whitespace + if (_curchar != '(') { + parse_err(SYNERR, "Missing '(' at start of interface field.\n"); + return NULL; + } + next_char(); // move past '(' + skipws(); + if ( _curchar != '0' && _curchar != '$' ) { + parse_err(SYNERR, "missing or invalid interface field contents.\n"); + return NULL; + } + iface_field = get_rep_var_ident(); + if (iface_field == NULL) { + parse_err(SYNERR, "missing or invalid interface field contents.\n"); + return NULL; + } + skipws(); + if (_curchar != ')') { + parse_err(SYNERR, "Missing ')' after interface field.\n"); + return NULL; + } + next_char(); // move past ')' + skipws(); + if ( _curchar != ';' ) { + parse_err(SYNERR, "Missing ';' at end of interface field.\n"); + return NULL; + } + next_char(); // move past ';' + skipws(); // be friendly to interface_parse() + + return iface_field; +} + + +//------------------------------match_parse------------------------------------ +MatchRule *ADLParser::match_parse(FormDict &operands) { + MatchRule *match; // Match Rule class for instruction/operand + char *cnstr = NULL; // Code for constructor + int depth = 0; // Counter for matching parentheses + int numleaves = 0; // Counter for number of leaves in rule + + // Parse the match rule tree + MatchNode *mnode = matchNode_parse(operands, depth, numleaves, true); + + // Either there is a block with a constructor, or a ';' here + skipws(); // Skip whitespace + if ( _curchar == ';' ) { // Semicolon is valid terminator + cnstr = NULL; // no constructor for this form + next_char(); // Move past the ';', replaced with '\0' + } + else if ((cnstr = find_cpp_block("match constructor")) == NULL ) { + parse_err(SYNERR, "invalid construction of match rule\n" + "Missing ';' or invalid '%{' and '%}' constructor\n"); + return NULL; // No MatchRule to return + } + if (_AD._adl_debug > 1) + if (cnstr) fprintf(stderr,"Match Constructor: %s\n", cnstr); + // Build new MatchRule object + match = new MatchRule(_AD, mnode, depth, cnstr, numleaves); + skipws(); // Skip any trailing whitespace + return match; // Return MatchRule object +} + +//------------------------------format_parse----------------------------------- +FormatRule* ADLParser::format_parse(void) { + char *desc = NULL; + FormatRule *format = (new FormatRule(desc)); + + // Without expression form, MUST have a code block; + skipws(); // Skip whitespace + if ( _curchar == ';' ) { // Semicolon is valid terminator + desc = NULL; // no constructor for this form + next_char(); // Move past the ';', replaced with '\0' + } + else if ( _curchar == '%' && *(_ptr+1) == '{') { + next_char(); // Move past the '%' + next_char(); // Move past the '{' + + skipws(); + // Check for the opening '"' inside the format description + if ( _curchar == '"' ) { + next_char(); // Move past the initial '"' + if( _curchar == '"' ) { // Handle empty format string case + *_ptr = '\0'; // Terminate empty string + format->_strings.addName(_ptr); + } + + // Collect the parts of the format description + // (1) strings that are passed through to tty->print + // (2) replacement/substitution variable, preceeded by a '$' + // (3) multi-token ANSIY C style strings + while ( true ) { + if ( _curchar == '%' || _curchar == '\n' ) { + if ( _curchar != '"' ) { + parse_err(SYNERR, "missing '\"' at end of format block"); + return NULL; + } + } + + // (1) + // Check if there is a string to pass through to output + char *start = _ptr; // Record start of the next string + while ((_curchar != '$') && (_curchar != '"') && (_curchar != '%') && (_curchar != '\n')) { + if (_curchar == '\\') next_char(); // superquote + if (_curchar == '\n') parse_err(SYNERR, "newline in string"); // unimplemented! + next_char(); + } + // If a string was found, terminate it and record in FormatRule + if ( start != _ptr ) { + *_ptr = '\0'; // Terminate the string + format->_strings.addName(start); + } + + // (2) + // If we are at a replacement variable, + // copy it and record in FormatRule + if ( _curchar == '$' ) { + next_char(); // Move past the '$' + char* rep_var = get_ident(); // Nil terminate the variable name + rep_var = strdup(rep_var);// Copy the string + *_ptr = _curchar; // and replace Nil with original character + format->_rep_vars.addName(rep_var); + // Add flag to _strings list indicating we should check _rep_vars + format->_strings.addName(NameList::_signal); + } + + // (3) + // Allow very long strings to be broken up, + // using the ANSI C syntax "foo\n" "bar" + if ( _curchar == '"') { + next_char(); // Move past the '"' + skipws(); // Skip white space before next string token + if ( _curchar != '"') { + break; + } else { + // Found one. Skip both " and the whitespace in between. + next_char(); + } + } + } // end while part of format description + + // Check for closing '"' and '%}' in format description + skipws(); // Move to closing '%}' + if ( _curchar != '%' ) { + parse_err(SYNERR, "non-blank characters between closing '\"' and '%' in format"); + return NULL; + } + } // Done with format description inside + + skipws(); + // Past format description, at '%' + if ( _curchar != '%' || *(_ptr+1) != '}' ) { + parse_err(SYNERR, "missing '%}' at end of format block"); + return NULL; + } + next_char(); // Move past the '%' + next_char(); // Move past the '}' + } + else { // parameter list alone must terminate with a ';' + parse_err(SYNERR, "missing ';' after Format expression"); + return NULL; + } + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Format Rule: %s\n", desc); + + skipws(); + return format; +} + + +//------------------------------effect_parse----------------------------------- +void ADLParser::effect_parse(InstructForm *instr) { + char* desc = NULL; + + skipws(); // Skip whitespace + if (_curchar != '(') { + parse_err(SYNERR, "missing '(' in effect definition\n"); + return; + } + // Get list of effect-operand pairs and insert into dictionary + else get_effectlist(instr->_effects, instr->_localNames); + + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Effect description: %s\n", desc); + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' in Effect definition\n"); + } + next_char(); // Skip ';' + +} + +//------------------------------expand_parse----------------------------------- +ExpandRule* ADLParser::expand_parse(InstructForm *instr) { + char *ident, *ident2; + OperandForm *oper; + InstructForm *ins; + NameAndList *instr_and_operands = NULL; + ExpandRule *exp = new ExpandRule(); + + // Expand is a block containing an ordered list of instructions, each of + // which has an ordered list of operands. + // Check for block delimiter + skipws(); // Skip leading whitespace + if ((_curchar != '%') + || (next_char(), (_curchar != '{')) ) { // If not open block + parse_err(SYNERR, "missing '%{' in expand definition\n"); + return(NULL); + } + next_char(); // Maintain the invariant + do { + ident = get_ident(); // Grab next identifier + if (ident == NULL) { + parse_err(SYNERR, "identifier expected at %c\n", _curchar); + continue; + } // Check that you have a valid instruction + const Form *form = _globalNames[ident]; + ins = form ? form->is_instruction() : NULL; + if (ins == NULL) { + // This is a new operand + oper = form ? form->is_operand() : NULL; + if (oper == NULL) { + parse_err(SYNERR, "instruction/operand name expected at %s\n", ident); + continue; + } + // Throw the operand on the _newopers list + skipws(); + ident = get_unique_ident(instr->_localNames,"Operand"); + if (ident == NULL) { + parse_err(SYNERR, "identifier expected at %c\n", _curchar); + continue; + } + exp->_newopers.addName(ident); + // Add new operand to LocalNames + instr->_localNames.Insert(ident, oper); + // Grab any constructor code and save as a string + char *c = NULL; + skipws(); + if (_curchar == '%') { // Need a constructor for the operand + c = find_cpp_block("Operand Constructor"); + if (c == NULL) { + parse_err(SYNERR, "Invalid code block for operand constructor\n", _curchar); + continue; + } + // Add constructor to _newopconst Dict + exp->_newopconst.Insert(ident, c); + } + else if (_curchar != ';') { // If no constructor, need a ; + parse_err(SYNERR, "Missing ; in expand rule operand declaration\n"); + continue; + } + else next_char(); // Skip the ; + skipws(); + } + else { + // Add instruction to list + instr_and_operands = new NameAndList(ident); + // Grab operands, build nameList of them, and then put into dictionary + skipws(); + if (_curchar != '(') { // Check for parenthesized operand list + parse_err(SYNERR, "missing '(' in expand instruction declaration\n"); + continue; + } + do { + next_char(); // skip open paren & comma characters + skipws(); + if (_curchar == ')') break; + ident2 = get_ident(); + skipws(); + if (ident2 == NULL) { + parse_err(SYNERR, "identifier expected at %c\n", _curchar); + continue; + } // Check that you have a valid operand + const Form *form = instr->_localNames[ident2]; + if (!form) { + parse_err(SYNERR, "operand name expected at %s\n", ident2); + continue; + } + oper = form->is_operand(); + if (oper == NULL && !form->is_opclass()) { + parse_err(SYNERR, "operand name expected at %s\n", ident2); + continue; + } // Add operand to list + instr_and_operands->add_entry(ident2); + } while(_curchar == ','); + if (_curchar != ')') { + parse_err(SYNERR, "missing ')'in expand instruction declaration\n"); + continue; + } + next_char(); + if (_curchar != ';') { + parse_err(SYNERR, "missing ';'in expand instruction declaration\n"); + continue; + } + next_char(); + + // Record both instruction name and its operand list + exp->add_instruction(instr_and_operands); + + skipws(); + } + + } while(_curchar != '%'); + next_char(); + if (_curchar != '}') { + parse_err(SYNERR, "missing '%}' in expand rule definition\n"); + return(NULL); + } + next_char(); + + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Expand Rule:\n"); + + skipws(); + return (exp); +} + +//------------------------------rewrite_parse---------------------------------- +RewriteRule* ADLParser::rewrite_parse(void) { + char* params = NULL; + char* desc = NULL; + + + // This feature targeted for second generation description language. + + skipws(); // Skip whitespace + // Get parameters for rewrite + if ((params = get_paren_expr("rewrite parameters")) == NULL) { + parse_err(SYNERR, "missing '(' in rewrite rule\n"); + return NULL; + } + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Rewrite parameters: %s\n", params); + + // For now, grab entire block; + skipws(); + if ( (desc = find_cpp_block("rewrite block")) == NULL ) { + parse_err(SYNERR, "incorrect or missing block for 'rewrite'.\n"); + return NULL; + } + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Rewrite Rule: %s\n", desc); + + skipws(); + return (new RewriteRule(params,desc)); +} + +//------------------------------attr_parse------------------------------------- +Attribute *ADLParser::attr_parse(char* ident) { + Attribute *attrib; // Attribute class + char *cost = NULL; // String representation of cost attribute + + skipws(); // Skip leading whitespace + if ( (cost = get_paren_expr("attribute")) == NULL ) { + parse_err(SYNERR, "incorrect or missing expression for 'attribute'\n"); + return NULL; + } + // Debug Stuff + if (_AD._adl_debug > 1) fprintf(stderr,"Attribute: %s\n", cost); + if (_curchar != ';') { + parse_err(SYNERR, "missing ';' in attribute definition\n"); + return NULL; + } + next_char(); // Point after the terminator + + skipws(); + attrib = new Attribute(ident,cost,INS_ATTR); // Build new predicate object + return attrib; +} + + +//------------------------------matchNode_parse-------------------------------- +MatchNode *ADLParser::matchNode_parse(FormDict &operands, int &depth, int &numleaves, bool atroot) { + // Count depth of parenthesis nesting for both left and right children + int lParens = depth; + int rParens = depth; + + // MatchNode objects for left, right, and root of subtree. + MatchNode *lChild = NULL; + MatchNode *rChild = NULL; + char *token; // Identifier which may be opcode or operand + + // Match expression starts with a '(' + if (cur_char() != '(') + return NULL; + + next_char(); // advance past '(' + + // Parse the opcode + token = get_ident(); // Get identifier, opcode + if (token == NULL) { + parse_err(SYNERR, "missing opcode in match expression\n"); + return NULL; + } + + // Take note if we see one of a few special operations - those that are + // treated differently on different architectures in the sense that on + // one architecture there is a match rule and on another there isn't (so + // a call will eventually be generated). + + for (int i = _last_machine_leaf + 1; i < _last_opcode; i++) { + if (strcmp(token, NodeClassNames[i]) == 0) { + _AD.has_match_rule(i, true); + } + } + + // Lookup the root value in the operands dict to perform substitution + const char *result = NULL; // Result type will be filled in later + const char *name = token; // local name associated with this node + const char *operation = token; // remember valid operation for later + const Form *form = operands[token]; + OpClassForm *opcForm = form ? form->is_opclass() : NULL; + if (opcForm != NULL) { + // If this token is an entry in the local names table, record its type + if (!opcForm->ideal_only()) { + operation = opcForm->_ident; + result = operation; // Operands result in their own type + } + // Otherwise it is an ideal type, and so, has no local name + else name = NULL; + } + + // Parse the operands + skipws(); + if (cur_char() != ')') { + + // Parse the left child + if (strcmp(operation,"Set")) + lChild = matchChild_parse(operands, lParens, numleaves, false); + else + lChild = matchChild_parse(operands, lParens, numleaves, true); + + skipws(); + if (cur_char() != ')' ) { + if(strcmp(operation, "Set")) + rChild = matchChild_parse(operands,rParens,numleaves,false); + else + rChild = matchChild_parse(operands,rParens,numleaves,true); + } + } + + // Check for required ')' + skipws(); + if (cur_char() != ')') { + parse_err(SYNERR, "missing ')' in match expression\n"); + return NULL; + } + next_char(); // skip the ')' + + MatchNode* mroot = new MatchNode(_AD,result,name,operation,lChild,rChild); + + // If not the root, reduce this subtree to an internal operand + if (!atroot) { + mroot->build_internalop(); + } + // depth is greater of left and right paths. + depth = (lParens > rParens) ? lParens : rParens; + + return mroot; +} + + +//------------------------------matchChild_parse------------------------------- +MatchNode *ADLParser::matchChild_parse(FormDict &operands, int &parens, int &numleaves, bool atroot) { + MatchNode *child = NULL; + const char *result = NULL; + const char *token = NULL; + const char *opType = NULL; + + if (cur_char() == '(') { // child is an operation + ++parens; + child = matchNode_parse(operands, parens, numleaves, atroot); + } + else { // child is an operand + token = get_ident(); + const Form *form = operands[token]; + OpClassForm *opcForm = form ? form->is_opclass() : NULL; + if (opcForm != NULL) { + opType = opcForm->_ident; + result = opcForm->_ident; // an operand's result matches its type + } else { + parse_err(SYNERR, "undefined operand %s in match rule\n", token); + return NULL; + } + + if (opType == NULL) { + parse_err(SYNERR, "missing type for argument '%s'\n", token); + } + + child = new MatchNode(_AD, result, token, opType); + ++numleaves; + } + + return child; +} + + + +// ******************** Private Utility Functions ************************* + + +char* ADLParser::find_cpp_block(const char* description) { + char *next; // Pointer for finding block delimiters + char* cppBlock = NULL; // Beginning of C++ code block + + if (_curchar == '%') { // Encoding is a C++ expression + next_char(); + if (_curchar != '{') { + parse_err(SYNERR, "missing '{' in %s \n", description); + return NULL; + } + next_char(); // Skip block delimiter + skipws_no_preproc(); // Skip leading whitespace + cppBlock = _ptr; // Point to start of expression + const char* file = _AD._ADL_file._name; + int line = _linenum; + next = _ptr + 1; + while(((_curchar != '%') || (*next != '}')) && (_curchar != '\0')) { + next_char_or_line(); + next = _ptr+1; // Maintain the next pointer + } // Grab string + if (_curchar == '\0') { + parse_err(SYNERR, "invalid termination of %s \n", description); + return NULL; + } + *_ptr = '\0'; // Terminate string + _ptr += 2; // Skip block delimiter + _curchar = *_ptr; // Maintain invariant + + // Prepend location descriptor, for debugging. + char* location = (char *)malloc(strlen(file) + 100); + *location = '\0'; + if (_AD._adlocation_debug) + sprintf(location, "#line %d \"%s\"\n", line, file); + char* result = (char *)malloc(strlen(location) + strlen(cppBlock) + 1); + strcpy(result, location); + strcat(result, cppBlock); + cppBlock = result; + free(location); + } + + return cppBlock; +} + +// Move to the closing token of the expression we are currently at, +// as defined by stop_chars. Match parens and quotes. +char* ADLParser::get_expr(const char *desc, const char *stop_chars) { + char* expr = NULL; + int paren = 0; + + expr = _ptr; + while (paren > 0 || !strchr(stop_chars, _curchar)) { + if (_curchar == '(') { // Down level of nesting + paren++; // Bump the parenthesis counter + next_char(); // maintain the invariant + } + else if (_curchar == ')') { // Up one level of nesting + if (paren == 0) { + // Paren underflow: We didn't encounter the required stop-char. + parse_err(SYNERR, "too many )'s, did not find %s after %s\n", + stop_chars, desc); + return NULL; + } + paren--; // Drop the parenthesis counter + next_char(); // Maintain the invariant + } + else if (_curchar == '"' || _curchar == '\'') { + int qchar = _curchar; + while (true) { + next_char(); + if (_curchar == qchar) { next_char(); break; } + if (_curchar == '\\') next_char(); // superquote + if (_curchar == '\n' || _curchar == '\0') { + parse_err(SYNERR, "newline in string in %s\n", desc); + return NULL; + } + } + } + else if (_curchar == '%' && (_ptr[1] == '{' || _ptr[1] == '}')) { + // Make sure we do not stray into the next ADLC-level form. + parse_err(SYNERR, "unexpected %%%c in %s\n", _ptr[1], desc); + return NULL; + } + else if (_curchar == '\0') { + parse_err(SYNERR, "unexpected EOF in %s\n", desc); + return NULL; + } + else { + // Always walk over whitespace, comments, preprocessor directives, etc. + char* pre_skip_ptr = _ptr; + skipws(); + // If the parser declined to make progress on whitespace, + // skip the next character, which is therefore NOT whitespace. + if (pre_skip_ptr == _ptr) { + next_char(); + } else if (pre_skip_ptr+strlen(pre_skip_ptr) != _ptr+strlen(_ptr)) { + parse_err(SYNERR, "unimplemented: preprocessor must not elide subexpression in %s", desc); + } + } + } + + assert(strchr(stop_chars, _curchar), "non-null return must be at stop-char"); + *_ptr = '\0'; // Replace ')' or other stop-char with '\0' + return expr; +} + +// Helper function around get_expr +// Sets _curchar to '(' so that get_paren_expr will search for a matching ')' +char *ADLParser::get_paren_expr(const char *description) { + if (_curchar != '(') // Escape if not valid starting position + return NULL; + next_char(); // Skip the required initial paren. + char *token2 = get_expr(description, ")"); + if (_curchar == ')') + next_char(); // Skip required final paren. + return token2; +} + +//------------------------------get_ident_common------------------------------- +// Looks for an identifier in the buffer, and turns it into a null terminated +// string(still inside the file buffer). Returns a pointer to the string or +// NULL if some other token is found instead. +char *ADLParser::get_ident_common(bool do_preproc) { + register char c; + char *start; // Pointer to start of token + char *end; // Pointer to end of token + + if( _curline == NULL ) // Return NULL at EOF. + return NULL; + + skipws_common(do_preproc); // Skip whitespace before identifier + start = end = _ptr; // Start points at first character + end--; // unwind end by one to prepare for loop + do { + end++; // Increment end pointer + c = *end; // Grab character to test + } while ( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) + || ((c >= '0') && (c <= '9')) + || ((c == '_')) || ((c == ':')) || ((c == '#')) ); + if (start == end) { // We popped out on the first try + parse_err(SYNERR, "identifier expected at %c\n", c); + start = NULL; + } + else { + _curchar = c; // Save the first character of next token + *end = '\0'; // NULL terminate the string in place + } + _ptr = end; // Reset _ptr to point to next char after token + + // Make sure we do not try to use #defined identifiers. If start is + // NULL an error was already reported. + if (do_preproc && start != NULL) { + const char* def = _AD.get_preproc_def(start); + if (def != NULL && strcmp(def, start)) { + const char* def2 = _AD.get_preproc_def(def); + if (def2 != NULL && strcmp(def2, def)) { + parse_err(SYNERR, "unimplemented: using %s defined as %s => %s", + start, def, def2); + } + start = strdup(def); + } + } + + return start; // Pointer to token in filebuf +} + +//------------------------------get_ident_dup---------------------------------- +// Looks for an identifier in the buffer, and returns a duplicate +// or NULL if some other token is found instead. +char *ADLParser::get_ident_dup(void) { + char *ident = get_ident(); + + // Duplicate an identifier before returning and restore string. + if( ident != NULL ) { + ident = strdup(ident); // Copy the string + *_ptr = _curchar; // and replace Nil with original character + } + + return ident; +} + +//----------------------get_ident_or_literal_constant-------------------------- +// Looks for an identifier in the buffer, or a parenthesized expression. +char *ADLParser::get_ident_or_literal_constant(const char* description) { + char* param = NULL; + skipws(); + if (_curchar == '(') { + // Grab a constant expression. + param = get_paren_expr(description); + if (param[0] != '(') { + char* buf = (char*) malloc(strlen(param) + 3); + sprintf(buf, "(%s)", param); + param = buf; + } + assert(is_literal_constant(param), + "expr must be recognizable as a constant"); + } else { + param = get_ident(); + } + return param; +} + +//------------------------------get_rep_var_ident----------------------------- +// Do NOT duplicate, +// Leave nil terminator in buffer +// Preserve initial '$'(s) in string +char *ADLParser::get_rep_var_ident(void) { + // Remember starting point + char *rep_var = _ptr; + + // Check for replacement variable indicator '$' and pass if present + if ( _curchar == '$' ) { + next_char(); + } + // Check for a subfield indicator, a second '$', and pass if present + if ( _curchar == '$' ) { + next_char(); + } + + // Check for a control indicator, a third '$': + if ( _curchar == '$' ) { + next_char(); + } + + // Check for more than three '$'s in sequence, SYNERR + if( _curchar == '$' ) { + parse_err(SYNERR, "Replacement variables and field specifiers can not start with '$$$$'"); + next_char(); + return NULL; + } + + // Nil terminate the variable name following the '$' + char *rep_var_name = get_ident(); + assert( rep_var_name != NULL, + "Missing identifier after replacement variable indicator '$'"); + + return rep_var; +} + + + +//------------------------------get_rep_var_ident_dup------------------------- +// Return the next replacement variable identifier, skipping first '$' +// given a pointer into a line of the buffer. +// Null terminates string, still inside the file buffer, +// Returns a pointer to a copy of the string, or NULL on failure +char *ADLParser::get_rep_var_ident_dup(void) { + if( _curchar != '$' ) return NULL; + + next_char(); // Move past the '$' + char *rep_var = _ptr; // Remember starting point + + // Check for a subfield indicator, a second '$': + if ( _curchar == '$' ) { + next_char(); + } + + // Check for a control indicator, a third '$': + if ( _curchar == '$' ) { + next_char(); + } + + // Check for more than three '$'s in sequence, SYNERR + if( _curchar == '$' ) { + parse_err(SYNERR, "Replacement variables and field specifiers can not start with '$$$$'"); + next_char(); + return NULL; + } + + // Nil terminate the variable name following the '$' + char *rep_var_name = get_ident(); + assert( rep_var_name != NULL, + "Missing identifier after replacement variable indicator '$'"); + rep_var = strdup(rep_var); // Copy the string + *_ptr = _curchar; // and replace Nil with original character + + return rep_var; +} + + +//------------------------------get_unique_ident------------------------------ +// Looks for an identifier in the buffer, terminates it with a NULL, +// and checks that it is unique +char *ADLParser::get_unique_ident(FormDict& dict, const char* nameDescription){ + char* ident = get_ident(); + + if (ident == NULL) { + parse_err(SYNERR, "missing %s identifier at %c\n", nameDescription, _curchar); + } + else { + if (dict[ident] != NULL) { + parse_err(SYNERR, "duplicate name %s for %s\n", ident, nameDescription); + ident = NULL; + } + } + + return ident; +} + + +//------------------------------get_int---------------------------------------- +// Looks for a character string integer in the buffer, and turns it into an int +// invokes a parse_err if the next token is not an integer. +// This routine does not leave the integer null-terminated. +int ADLParser::get_int(void) { + register char c; + char *start; // Pointer to start of token + char *end; // Pointer to end of token + int result; // Storage for integer result + + if( _curline == NULL ) // Return NULL at EOF. + return NULL; + + skipws(); // Skip whitespace before identifier + start = end = _ptr; // Start points at first character + c = *end; // Grab character to test + while ((c >= '0') && (c <= '9') + || ((c == '-') && (end == start))) { + end++; // Increment end pointer + c = *end; // Grab character to test + } + if (start == end) { // We popped out on the first try + parse_err(SYNERR, "integer expected at %c\n", c); + result = 0; + } + else { + _curchar = c; // Save the first character of next token + *end = '\0'; // NULL terminate the string in place + result = atoi(start); // Convert the string to an integer + *end = _curchar; // Restore buffer to original condition + } + + // Reset _ptr to next char after token + _ptr = end; + + return result; // integer +} + + +//------------------------------get_relation_dup------------------------------ +// Looks for a relational operator in the buffer +// invokes a parse_err if the next token is not a relation +// This routine creates a duplicate of the string in the buffer. +char *ADLParser::get_relation_dup(void) { + char *result = NULL; // relational operator being returned + + if( _curline == NULL ) // Return NULL at EOF. + return NULL; + + skipws(); // Skip whitespace before relation + char *start = _ptr; // Store start of relational operator + char first = *_ptr; // the first character + if( (first == '=') || (first == '!') || (first == '<') || (first == '>') ) { + next_char(); + char second = *_ptr; // the second character + if( (second == '=') ) { + next_char(); + char tmp = *_ptr; + *_ptr = '\0'; // NULL terminate + result = strdup(start); // Duplicate the string + *_ptr = tmp; // restore buffer + } else { + parse_err(SYNERR, "relational operator expected at %s\n", _ptr); + } + } else { + parse_err(SYNERR, "relational operator expected at %s\n", _ptr); + } + + return result; +} + + + +//------------------------------get_oplist------------------------------------- +// Looks for identifier pairs where first must be the name of an operand, and +// second must be a name unique in the scope of this instruction. Stores the +// names with a pointer to the OpClassForm of their type in a local name table. +void ADLParser::get_oplist(NameList ¶meters, FormDict &operands) { + OpClassForm *opclass = NULL; + char *ident = NULL; + + do { + next_char(); // skip open paren & comma characters + skipws(); + if (_curchar == ')') break; + + // Get operand type, and check it against global name table + ident = get_ident(); + if (ident == NULL) { + parse_err(SYNERR, "optype identifier expected at %c\n", _curchar); + return; + } + else { + const Form *form = _globalNames[ident]; + if( form == NULL ) { + parse_err(SYNERR, "undefined operand type %s\n", ident); + return; + } + + // Check for valid operand type + OpClassForm *opc = form->is_opclass(); + OperandForm *oper = form->is_operand(); + if((oper == NULL) && (opc == NULL)) { + parse_err(SYNERR, "identifier %s not operand type\n", ident); + return; + } + opclass = opc; + } + // Debugging Stuff + if (_AD._adl_debug > 1) fprintf(stderr, "\tOperand Type: %s\t", ident); + + // Get name of operand and add it to local name table + if( (ident = get_unique_ident(operands, "operand")) == NULL) { + return; + } + // Parameter names must not be global names. + if( _globalNames[ident] != NULL ) { + parse_err(SYNERR, "Reuse of global name %s as operand.\n",ident); + return; + } + operands.Insert(ident, opclass); + parameters.addName(ident); + + // Debugging Stuff + if (_AD._adl_debug > 1) fprintf(stderr, "\tOperand Name: %s\n", ident); + skipws(); + } while(_curchar == ','); + + if (_curchar != ')') parse_err(SYNERR, "missing ')'\n"); + else { + next_char(); // set current character position past the close paren + } +} + + +//------------------------------get_effectlist--------------------------------- +// Looks for identifier pairs where first must be the name of a pre-defined, +// effect, and the second must be the name of an operand defined in the +// operand list of this instruction. Stores the names with a pointer to the +// effect form in a local effects table. +void ADLParser::get_effectlist(FormDict &effects, FormDict &operands) { + OperandForm *opForm; + Effect *eForm; + char *ident; + + do { + next_char(); // skip open paren & comma characters + skipws(); + if (_curchar == ')') break; + + // Get effect type, and check it against global name table + ident = get_ident(); + if (ident == NULL) { + parse_err(SYNERR, "effect type identifier expected at %c\n", _curchar); + return; + } + else { + // Check for valid effect type + const Form *form = _globalNames[ident]; + if( form == NULL ) { + parse_err(SYNERR, "undefined effect type %s\n", ident); + return; + } + else { + if( (eForm = form->is_effect()) == NULL) { + parse_err(SYNERR, "identifier %s not effect type\n", ident); + return; + } + } + } + // Debugging Stuff + if (_AD._adl_debug > 1) fprintf(stderr, "\tEffect Type: %s\t", ident); + skipws(); + // Get name of operand and check that it is in the local name table + if( (ident = get_unique_ident(effects, "effect")) == NULL) { + parse_err(SYNERR, "missing operand identifier in effect list\n"); + return; + } + const Form *form = operands[ident]; + opForm = form ? form->is_operand() : NULL; + if( opForm == NULL ) { + if( form && form->is_opclass() ) { + const char* cname = form->is_opclass()->_ident; + parse_err(SYNERR, "operand classes are illegal in effect lists (found %s %s)\n", cname, ident); + } else { + parse_err(SYNERR, "undefined operand %s in effect list\n", ident); + } + return; + } + // Add the pair to the effects table + effects.Insert(ident, eForm); + // Debugging Stuff + if (_AD._adl_debug > 1) fprintf(stderr, "\tOperand Name: %s\n", ident); + skipws(); + } while(_curchar == ','); + + if (_curchar != ')') parse_err(SYNERR, "missing ')'\n"); + else { + next_char(); // set current character position past the close paren + } +} + + +//------------------------------preproc_define--------------------------------- +// A "#define" keyword has been seen, so parse the rest of the line. +void ADLParser::preproc_define(void) { + char* flag = get_ident_no_preproc(); + skipws_no_preproc(); + // only #define x y is supported for now + char* def = get_ident_no_preproc(); + _AD.set_preproc_def(flag, def); + skipws_no_preproc(); + if (_curchar != '\n') { + parse_err(SYNERR, "non-identifier in preprocessor definition\n"); + } +} + +//------------------------------preproc_undef---------------------------------- +// An "#undef" keyword has been seen, so parse the rest of the line. +void ADLParser::preproc_undef(void) { + char* flag = get_ident_no_preproc(); + skipws_no_preproc(); + ensure_end_of_line(); + _AD.set_preproc_def(flag, NULL); +} + + + +//------------------------------parse_err-------------------------------------- +// Issue a parser error message, and skip to the end of the current line +void ADLParser::parse_err(int flag, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + if (flag == 1) + _AD._syntax_errs += _AD.emit_msg(0, flag, _linenum, fmt, args); + else if (flag == 2) + _AD._semantic_errs += _AD.emit_msg(0, flag, _linenum, fmt, args); + else + _AD._warnings += _AD.emit_msg(0, flag, _linenum, fmt, args); + + int error_char = _curchar; + char* error_ptr = _ptr+1; + for(;*_ptr != '\n'; _ptr++) ; // Skip to the end of the current line + _curchar = '\n'; + va_end(args); + _AD._no_output = 1; + + if (flag == 1) { + char* error_tail = strchr(error_ptr, '\n'); + char tem = *error_ptr; + error_ptr[-1] = '\0'; + char* error_head = error_ptr-1; + while (error_head > _curline && *error_head) --error_head; + if (error_tail) *error_tail = '\0'; + fprintf(stderr, "Error Context: %s>>>%c<<<%s\n", + error_head, error_char, error_ptr); + if (error_tail) *error_tail = '\n'; + error_ptr[-1] = tem; + } +} + +//---------------------------ensure_start_of_line------------------------------ +// A preprocessor directive has been encountered. Be sure it has fallen at +// the begining of a line, or else report an error. +void ADLParser::ensure_start_of_line(void) { + assert( _ptr >= _curline && _ptr < _curline+strlen(_curline), + "Must be able to find which line we are in" ); + + for (char *s = _curline; s < _ptr; s++) { + if (*s > ' ') { + parse_err(SYNERR, "'%c' must be at beginning of line\n", _curchar); + break; + } + } +} + +//---------------------------ensure_end_of_line-------------------------------- +// A preprocessor directive has been parsed. Be sure there is no trailing +// garbage at the end of this line. Set the scan point to the beginning of +// the next line. +void ADLParser::ensure_end_of_line(void) { + skipws_no_preproc(); + if (_curchar != '\n' && _curchar != '\0') { + parse_err(SYNERR, "garbage char '%c' at end of line\n", _curchar); + } else { + next_char_or_line(); + } +} + +//---------------------------handle_preproc------------------------------------ +// The '#' character introducing a preprocessor directive has been found. +// Parse the whole directive name (e.g., #define, #endif) and take appropriate +// action. If we are in an "untaken" span of text, simply keep track of +// #ifdef nesting structure, so we can find out when to start taking text +// again. (In this state, we "sort of support" C's #if directives, enough +// to disregard their associated #else and #endif lines.) If we are in a +// "taken" span of text, there are two cases: "#define" and "#undef" +// directives are preserved and passed up to the caller, which eventually +// passes control to the top-level parser loop, which handles #define and +// #undef directly. (This prevents these directives from occurring in +// arbitrary positions in the AD file--we require better structure than C.) +// In the other case, and #ifdef, #ifndef, #else, or #endif is silently +// processed as whitespace, with the "taken" state of the text correctly +// updated. This routine returns "false" exactly in the case of a "taken" +// #define or #undef, which tells the caller that a preprocessor token +// has appeared which must be handled explicitly by the parse loop. +bool ADLParser::handle_preproc_token() { + assert(*_ptr == '#', "must be at start of preproc"); + ensure_start_of_line(); + next_char(); + skipws_no_preproc(); + char* start_ident = _ptr; + char* ident = (_curchar == '\n') ? NULL : get_ident_no_preproc(); + if (ident == NULL) { + parse_err(SYNERR, "expected preprocessor command, got end of line\n"); + } else if (!strcmp(ident, "ifdef") || + !strcmp(ident, "ifndef")) { + char* flag = get_ident_no_preproc(); + ensure_end_of_line(); + // Test the identifier only if we are already in taken code: + bool flag_def = preproc_taken() && (_AD.get_preproc_def(flag) != NULL); + bool now_taken = !strcmp(ident, "ifdef") ? flag_def : !flag_def; + begin_if_def(now_taken); + } else if (!strcmp(ident, "if")) { + if (preproc_taken()) + parse_err(SYNERR, "unimplemented: #%s %s", ident, _ptr+1); + next_line(); + // Intelligently skip this nested C preprocessor directive: + begin_if_def(true); + } else if (!strcmp(ident, "else")) { + ensure_end_of_line(); + invert_if_def(); + } else if (!strcmp(ident, "endif")) { + ensure_end_of_line(); + end_if_def(); + } else if (preproc_taken()) { + // pass this token up to the main parser as "#define" or "#undef" + _ptr = start_ident; + _curchar = *--_ptr; + if( _curchar != '#' ) { + parse_err(SYNERR, "no space allowed after # in #define or #undef"); + assert(_curchar == '#', "no space allowed after # in #define or #undef"); + } + return false; + } + return true; +} + +//---------------------------skipws_common------------------------------------- +// Skip whitespace, including comments and newlines, while keeping an accurate +// line count. +// Maybe handle certain preprocessor constructs: #ifdef, #ifndef, #else, #endif +void ADLParser::skipws_common(bool do_preproc) { + char *start = _ptr; + char *next = _ptr + 1; + + if (*_ptr == '\0') { + // Check for string terminator + if (_curchar > ' ') return; + if (_curchar == '\n') { + if (!do_preproc) return; // let caller handle the newline + next_line(); + _ptr = _curline; next = _ptr + 1; + } + else if (_curchar == '#' || + (_curchar == '/' && (*next == '/' || *next == '*'))) { + parse_err(SYNERR, "unimplemented: comment token in a funny place"); + } + } + while(_curline != NULL) { // Check for end of file + if (*_ptr == '\n') { // keep proper track of new lines + if (!do_preproc) break; // let caller handle the newline + next_line(); + _ptr = _curline; next = _ptr + 1; + } + else if ((*_ptr == '/') && (*next == '/')) // C++ comment + do { _ptr++; next++; } while(*_ptr != '\n'); // So go to end of line + else if ((*_ptr == '/') && (*next == '*')) { // C comment + _ptr++; next++; + do { + _ptr++; next++; + if (*_ptr == '\n') { // keep proper track of new lines + next_line(); // skip newlines within comments + if (_curline == NULL) { // check for end of file + parse_err(SYNERR, "end-of-file detected inside comment\n"); + break; + } + _ptr = _curline; next = _ptr + 1; + } + } while(!((*_ptr == '*') && (*next == '/'))); // Go to end of comment + _ptr = ++next; next++; // increment _ptr past comment end + } + else if (do_preproc && *_ptr == '#') { + // Note that this calls skipws_common(false) recursively! + bool preproc_handled = handle_preproc_token(); + if (!preproc_handled) { + if (preproc_taken()) { + return; // short circuit + } + ++_ptr; // skip the preprocessor character + } + next = _ptr+1; + } else if(*_ptr > ' ' && !(do_preproc && !preproc_taken())) { + break; + } + else if (*_ptr == '"' || *_ptr == '\'') { + assert(do_preproc, "only skip strings if doing preproc"); + // skip untaken quoted string + int qchar = *_ptr; + while (true) { + ++_ptr; + if (*_ptr == qchar) { ++_ptr; break; } + if (*_ptr == '\\') ++_ptr; + if (*_ptr == '\n' || *_ptr == '\0') { + parse_err(SYNERR, "newline in string"); + break; + } + } + next = _ptr + 1; + } + else { ++_ptr; ++next; } + } + if( _curline != NULL ) // at end of file _curchar isn't valid + _curchar = *_ptr; // reset _curchar to maintain invariant +} + +//---------------------------cur_char----------------------------------------- +char ADLParser::cur_char() { + return (_curchar); +} + +//---------------------------next_char----------------------------------------- +void ADLParser::next_char() { + _curchar = *++_ptr; + // if ( _curchar == '\n' ) { + // next_line(); + // } +} + +//---------------------------next_char_or_line--------------------------------- +void ADLParser::next_char_or_line() { + if ( _curchar != '\n' ) { + _curchar = *++_ptr; + } else { + next_line(); + _ptr = _curline; + _curchar = *_ptr; // maintain invariant + } +} + +//---------------------------next_line----------------------------------------- +void ADLParser::next_line() { + _curline = _buf.get_line(); _linenum++; +} + +//-------------------------is_literal_constant--------------------------------- +bool ADLParser::is_literal_constant(const char *param) { + if (param[0] == 0) return false; // null string + if (param[0] == '(') return true; // parenthesized expression + if (param[0] == '0' && (param[1] == 'x' || param[1] == 'X')) { + // Make sure it's a hex constant. + int i = 2; + do { + if( !ADLParser::is_hex_digit(*(param+i)) ) return false; + ++i; + } while( *(param+i) != 0 ); + return true; + } + return false; +} + +//---------------------------is_hex_digit-------------------------------------- +bool ADLParser::is_hex_digit(char digit) { + return ((digit >= '0') && (digit <= '9')) + ||((digit >= 'a') && (digit <= 'f')) + ||((digit >= 'A') && (digit <= 'F')); +} + +//---------------------------is_int_token-------------------------------------- +bool ADLParser::is_int_token(const char* token, int& intval) { + const char* cp = token; + while (*cp != '\0' && *cp <= ' ') cp++; + if (*cp == '-') cp++; + int ndigit = 0; + while (*cp >= '0' && *cp <= '9') { cp++; ndigit++; } + while (*cp != '\0' && *cp <= ' ') cp++; + if (ndigit == 0 || *cp != '\0') { + return false; + } + intval = atoi(token); + return true; +} + +//-------------------------------trim------------------------------------------ +void ADLParser::trim(char* &token) { + while (*token <= ' ') token++; + char* end = token + strlen(token); + while (end > token && *(end-1) <= ' ') --end; + *end = '\0'; +} diff --git a/hotspot/src/share/vm/adlc/adlparse.hpp b/hotspot/src/share/vm/adlc/adlparse.hpp new file mode 100644 index 00000000000..853a104f7cd --- /dev/null +++ b/hotspot/src/share/vm/adlc/adlparse.hpp @@ -0,0 +1,272 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ADLPARSE.HPP - Definitions for Architecture Description Language Parser +// Authors: Chris Vick and Mike Paleczny + +// Class List +class Form; +// ***** Top Level, 1, classes ***** +class InstructForm; +class OperandForm; +class OpClassForm; +class AttributeForm; +class RegisterForm; +class PipelineForm; +class SourceForm; +class Peephole; +// ***** Level 2 classes ***** +class Component; +class Predicate; +class MatchRule; +class Encode; +class Attribute; +class Effect; +class ExpandRule; +class RewriteRule; +class Constraint; +class ConstructRule; +// ***** Register Section ***** +class RegDef; +class RegClass; +class AllocClass; +class ResourceForm; +// ***** Pipeline Section ***** +class PipeDesc; +class PipeClass; +class RegList; +// ***** Peephole Section ***** +class PeepMatch; +class PeepConstraint; +class PeepReplace; + +// class ostream; // ostream is a typedef in some systems + +extern char *toUpper(const char *str); + +//---------------------------ADLParser----------------------------------------- +class ADLParser { +protected: + char *_curline; // Start of current line + char *_ptr; // Pointer into current location in File Buffer + int _linenum; // Count of line numbers seen so far + char _curchar; // Current character from buffer + FormDict &_globalNames; // Global names + + enum { _preproc_limit = 20 }; + int _preproc_depth; // How deep are we into ifdefs? + int _preproc_not_taken; // How deep in not-taken ifdefs? + bool _preproc_taken[_preproc_limit]; // Are we taking this ifdef level? + bool _preproc_else[_preproc_limit]; // Did this level have an else yet? + + // ***** Level 1 Parse functions ***** + void instr_parse(void); // Parse instruction definitions + void oper_parse(void); // Parse operand definitions + void opclass_parse(void); // Parse operand class definitions + void ins_attr_parse(void); // Parse instruction attrubute definitions + void op_attr_parse(void); // Parse operand attrubute definitions + void source_parse(void); // Parse source section + void source_hpp_parse(void); // Parse source_hpp section + void reg_parse(void); // Parse register section + void encode_parse(void); // Parse encoding section + void frame_parse(void); // Parse frame section + void pipe_parse(void); // Parse pipeline section + void definitions_parse(void); // Parse definitions section + void peep_parse(void); // Parse peephole rule definitions + void preproc_define(void); // Parse a #define statement + void preproc_undef(void); // Parse an #undef statement + + // Helper functions for instr_parse(). + void adjust_set_rule(InstructForm *instr); + void matchrule_clone_and_swap(MatchRule *rule, const char* instr_ident, int& match_rules_cnt); + + // ***** Level 2 Parse functions ***** + // Parse the components of the encode section + void enc_class_parse(void); // Parse encoding class definition + void enc_class_parse_block(EncClass* encoding, char* ec_name); + + // Parse the components of the frame section + void stack_dir_parse(FrameForm *frame); // Parse the stack direction entry + void sync_stack_slots_parse(FrameForm *frame); + void frame_pointer_parse(FrameForm *frame, bool native); + void interpreter_frame_pointer_parse(FrameForm *frame, bool native); + void inline_cache_parse(FrameForm *frame, bool native); + void interpreter_arg_ptr_parse(FrameForm *frame, bool native); + void interpreter_method_oop_parse(FrameForm *frame, bool native); + void cisc_spilling_operand_name_parse(FrameForm *frame, bool native); + void stack_alignment_parse(FrameForm *frame); + void return_addr_parse(FrameForm *frame, bool native); + void preserve_stack_parse(FrameForm *frame); + char *calling_convention_parse(); + char *return_value_parse(); + + // Parse components of the register section + void reg_def_parse(void); // Parse register definition + void reg_class_parse(void); // Parse register class definition + void alloc_class_parse(void); // Parse allocation class definition + + // Parse components of the definition section + void int_def_parse(void); // Parse an integer definition + + // Parse components of a pipeline rule + void resource_parse(PipelineForm &pipe); // Parse resource definition + void pipe_desc_parse(PipelineForm &pipe); // Parse pipeline description definition + void pipe_class_parse(PipelineForm &pipe); // Parse pipeline class definition + + // Parse components of a peephole rule + void peep_match_parse(Peephole &peep); // Parse the peephole match rule + void peep_constraint_parse(Peephole &peep);// Parse the peephole constraints + void peep_replace_parse(Peephole &peep); // Parse peephole replacement rule + + // Parse the peep match rule tree + InstructForm *peep_match_child_parse(PeepMatch &match, int parent, int &position, int input); + + // Parse components of an operand and/or instruction form + Predicate *pred_parse(void); // Parse predicate rule + // Parse match rule, and internal nodes + MatchRule *match_parse(FormDict &operands); + MatchNode *matchNode_parse(FormDict &operands, int &depth, + int &numleaves, bool atroot); + MatchNode *matchChild_parse(FormDict &operands, int &depth, + int &numleaves, bool atroot); + + Attribute *attr_parse(char *ident);// Parse instr/operand attribute rule + // Parse instruction encode rule + InsEncode *ins_encode_parse(InstructForm &inst); + InsEncode *ins_encode_parse_block(InstructForm &inst); + Opcode *opcode_parse(InstructForm *insr); // Parse instruction opcode + char *size_parse(InstructForm *insr); // Parse instruction size + Interface *interface_parse(); // Parse operand interface rule + Interface *mem_interface_parse(); // Parse memory interface rule + Interface *cond_interface_parse(); // Parse conditional interface rule + char *interface_field_parse();// Parse field contents + + FormatRule *format_parse(void); // Parse format rule + void effect_parse(InstructForm *instr); // Parse effect rule + ExpandRule *expand_parse(InstructForm *instr); // Parse expand rule + RewriteRule *rewrite_parse(void); // Parse rewrite rule + Constraint *constraint_parse(void); // Parse constraint rule + ConstructRule *construct_parse(void); // Parse construct rule + void ins_pipe_parse(InstructForm &instr); // Parse ins_pipe rule + + // ***** Preprocessor functions ***** + void begin_if_def(bool taken) { + assert(_preproc_depth < _preproc_limit, "#ifdef nesting limit"); + int ppn = _preproc_depth++; + _preproc_taken[ppn] = taken; + // Invariant: _preproc_not_taken = SUM !_preproc_taken[0.._preproc_depth) + if (!_preproc_taken[ppn]) _preproc_not_taken += 1; + _preproc_else[ppn] = false; + } + void invert_if_def() { + assert(_preproc_depth > 0, "#ifdef matching"); + int ppn = _preproc_depth - 1; + assert(!_preproc_else[ppn], "multiple #else lines"); + _preproc_else[ppn] = true; + if (!_preproc_taken[ppn]) _preproc_not_taken -= 1; + _preproc_taken[ppn] = !_preproc_taken[ppn]; + if (!_preproc_taken[ppn]) _preproc_not_taken += 1; + } + void end_if_def() { + assert(_preproc_depth > 0, "#ifdef matching"); + int ppn = --_preproc_depth; + if (!_preproc_taken[ppn]) _preproc_not_taken -= 1; + } + bool preproc_taken() { + // Return true only if there is no directive hiding this text position. + return _preproc_not_taken == 0; + } + // Handle a '#' token. Return true if it disappeared. + bool handle_preproc_token(); + + // ***** Utility Functions for ADL Parser ****** + + // Parse one string argument inside parens: '(' string ')' ';' + char *parse_one_arg(const char *description); + + // Return the next identifier given a pointer into a line of the buffer. + char *get_ident() { return get_ident_common(true); } + char *get_ident_no_preproc() { return get_ident_common(false); } + char *get_ident_common(bool do_preproc); // Grab it from the file buffer + char *get_ident_dup(void); // Grab a duplicate of the identifier + char *get_ident_or_literal_constant(const char* description); + // Grab unique identifier from file buffer + char *get_unique_ident(FormDict &dict, const char *nameDescription); + // Return the next replacement variable identifier + char *get_rep_var_ident(void); + // Skip first '$' and make a duplicate of the string + char *get_rep_var_ident_dup(void); + // Return the next token given as a signed integer. + int get_int(void); + // Return the next token, a relational operator { ==, !=, <=, >= } + char *get_relation_dup(void); + + void get_oplist(NameList ¶meters, FormDict &operands);// Parse type-operand pairs + void get_effectlist(FormDict &effects, FormDict &operands); // Parse effect-operand pairs + // Return the contents of a parenthesized expression. + // Requires initial '(' and consumes final ')', which is replaced by '\0'. + char *get_paren_expr(const char *description); + // Return expression up to next stop-char, which terminator replaces. + // Does not require initial '('. Does not consume final stop-char. + // Final stop-char is left in _curchar, but is also is replaced by '\0'. + char *get_expr(const char *description, const char *stop_chars); + char *find_cpp_block(const char *description); // Parse a C++ code block + // Issue parser error message & go to EOL + void parse_err(int flag, const char *fmt, ...); + + // Return pointer to current character + inline char cur_char(void); + // Advance to next character, assign this to _curchar + inline void next_char(void); + inline void next_char_or_line(void); + // Advance File Buffer to next line, updating _curline + inline void next_line(void); + // Issue an error if we are not at the beginning of a line (exc. whitespace). + void ensure_start_of_line(void); + // Issue an error if we are not at the end of a line (exc. whitespace). + void ensure_end_of_line(void); + // Skip whitespace, leaving ptr pointing to first non-whitespace character + // Also handle preprocessor constructs like "#ifdef". + void skipws() { skipws_common(true); } + // Skip comments and spaces but not newlines or preprocessor constructs. + void skipws_no_preproc() { skipws_common(false); } + void skipws_common(bool do_preproc); + + FileBuff &_buf; // File buffer to be parsed + ArchDesc &_AD; // Architecture Description being built + +public: + + ADLParser(FileBuff &buf, ArchDesc &archDesc); // Create new ADLParser object + ~ADLParser(); // Destroy ADLParser object + + void parse(void); // Do the parsing & build forms lists + + int getlines( ) { return _linenum; } + + static bool is_literal_constant(const char *hex_string); + static bool is_hex_digit(char digit); + static bool is_int_token(const char* token, int& intval); + static void trim(char* &token); // trim leading & trailing spaces +}; diff --git a/hotspot/src/share/vm/adlc/archDesc.cpp b/hotspot/src/share/vm/adlc/archDesc.cpp new file mode 100644 index 00000000000..0e9088e8f45 --- /dev/null +++ b/hotspot/src/share/vm/adlc/archDesc.cpp @@ -0,0 +1,1131 @@ +// +// Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + + +// archDesc.cpp - Internal format for architecture definition +#include "adlc.hpp" + +static FILE *errfile = stderr; + +//--------------------------- utility functions ----------------------------- +inline char toUpper(char lower) { + return (('a' <= lower && lower <= 'z') ? (lower + ('A'-'a')) : lower); +} +char *toUpper(const char *str) { + char *upper = new char[strlen(str)+1]; + char *result = upper; + const char *end = str + strlen(str); + for (; str < end; ++str, ++upper) { + *upper = toUpper(*str); + } + *upper = '\0'; + return result; +} + +// Utilities to characterize effect statements +static bool is_def(int usedef) { + switch(usedef) { + case Component::DEF: + case Component::USE_DEF: return true; break; + } + return false; +} + +static bool is_use(int usedef) { + switch(usedef) { + case Component::USE: + case Component::USE_DEF: + case Component::USE_KILL: return true; break; + } + return false; +} + +static bool is_kill(int usedef) { + switch(usedef) { + case Component::KILL: + case Component::USE_KILL: return true; break; + } + return false; +} + +//---------------------------ChainList Methods------------------------------- +ChainList::ChainList() { +} + +void ChainList::insert(const char *name, const char *cost, const char *rule) { + _name.addName(name); + _cost.addName(cost); + _rule.addName(rule); +} + +bool ChainList::search(const char *name) { + return _name.search(name); +} + +void ChainList::reset() { + _name.reset(); + _cost.reset(); + _rule.reset(); +} + +bool ChainList::iter(const char * &name, const char * &cost, const char * &rule) { + bool notDone = false; + const char *n = _name.iter(); + const char *c = _cost.iter(); + const char *r = _rule.iter(); + + if (n && c && r) { + notDone = true; + name = n; + cost = c; + rule = r; + } + + return notDone; +} + +void ChainList::dump() { + output(stderr); +} + +void ChainList::output(FILE *fp) { + fprintf(fp, "\nChain Rules: output resets iterator\n"); + const char *cost = NULL; + const char *name = NULL; + const char *rule = NULL; + bool chains_exist = false; + for(reset(); (iter(name,cost,rule)) == true; ) { + fprintf(fp, "Chain to <%s> at cost #%s using %s_rule\n",name, cost ? cost : "0", rule); + // // Check for transitive chain rules + // Form *form = (Form *)_globalNames[rule]; + // if (form->is_instruction()) { + // // chain_rule(fp, indent, name, cost, rule); + // chain_rule(fp, indent, name, cost, rule); + // } + } + reset(); + if( ! chains_exist ) { + fprintf(fp, "No entries in this ChainList\n"); + } +} + + +//---------------------------MatchList Methods------------------------------- +bool MatchList::search(const char *opc, const char *res, const char *lch, + const char *rch, Predicate *pr) { + bool tmp = false; + if ((res == _resultStr) || (res && _resultStr && !strcmp(res, _resultStr))) { + if ((lch == _lchild) || (lch && _lchild && !strcmp(lch, _lchild))) { + if ((rch == _rchild) || (rch && _rchild && !strcmp(rch, _rchild))) { + char * predStr = get_pred(); + char * prStr = pr?pr->_pred:NULL; + if ((prStr == predStr) || (prStr && predStr && !strcmp(prStr, predStr))) { + return true; + } + } + } + } + if (_next) { + tmp = _next->search(opc, res, lch, rch, pr); + } + return tmp; +} + + +void MatchList::dump() { + output(stderr); +} + +void MatchList::output(FILE *fp) { + fprintf(fp, "\nMatchList output is Unimplemented();\n"); +} + + +//---------------------------ArchDesc Constructor and Destructor------------- + +ArchDesc::ArchDesc() + : _globalNames(cmpstr,hashstr, Form::arena), + _globalDefs(cmpstr,hashstr, Form::arena), + _preproc_table(cmpstr,hashstr, Form::arena), + _idealIndex(cmpstr,hashstr, Form::arena), + _internalOps(cmpstr,hashstr, Form::arena), + _internalMatch(cmpstr,hashstr, Form::arena), + _chainRules(cmpstr,hashstr, Form::arena), + _cisc_spill_operand(NULL) { + + // Initialize the opcode to MatchList table with NULLs + for( int i=0; i<_last_opcode; ++i ) { + _mlistab[i] = NULL; + } + + // Set-up the global tables + initKeywords(_globalNames); // Initialize the Name Table with keywords + + // Prime user-defined types with predefined types: Set, RegI, RegF, ... + initBaseOpTypes(); + + // Initialize flags & counters + _TotalLines = 0; + _no_output = 0; + _quiet_mode = 0; + _disable_warnings = 0; + _dfa_debug = 0; + _dfa_small = 0; + _adl_debug = 0; + _adlocation_debug = 0; + _internalOpCounter = 0; + _cisc_spill_debug = false; + _short_branch_debug = false; + + // Initialize match rule flags + for (int i = 0; i < _last_opcode; i++) { + _has_match_rule[i] = false; + } + + // Error/Warning Counts + _syntax_errs = 0; + _semantic_errs = 0; + _warnings = 0; + _internal_errs = 0; + + // Initialize I/O Files + _ADL_file._name = NULL; _ADL_file._fp = NULL; + // Machine dependent output files + _DFA_file._name = "dfa_i486.cpp"; _DFA_file._fp = NULL; + _HPP_file._name = "ad_i486.hpp"; _HPP_file._fp = NULL; + _CPP_file._name = "ad_i486.cpp"; _CPP_file._fp = NULL; + _bug_file._name = "bugs.out"; _bug_file._fp = NULL; + + // Initialize Register & Pipeline Form Pointers + _register = NULL; + _encode = NULL; + _pipeline = NULL; +} + +ArchDesc::~ArchDesc() { + // Clean-up and quit + +} + +//---------------------------ArchDesc methods: Public ---------------------- +// Store forms according to type +void ArchDesc::addForm(PreHeaderForm *ptr) { _pre_header.addForm(ptr); }; +void ArchDesc::addForm(HeaderForm *ptr) { _header.addForm(ptr); }; +void ArchDesc::addForm(SourceForm *ptr) { _source.addForm(ptr); }; +void ArchDesc::addForm(EncodeForm *ptr) { _encode = ptr; }; +void ArchDesc::addForm(InstructForm *ptr) { _instructions.addForm(ptr); }; +void ArchDesc::addForm(MachNodeForm *ptr) { _machnodes.addForm(ptr); }; +void ArchDesc::addForm(OperandForm *ptr) { _operands.addForm(ptr); }; +void ArchDesc::addForm(OpClassForm *ptr) { _opclass.addForm(ptr); }; +void ArchDesc::addForm(AttributeForm *ptr) { _attributes.addForm(ptr); }; +void ArchDesc::addForm(RegisterForm *ptr) { _register = ptr; }; +void ArchDesc::addForm(FrameForm *ptr) { _frame = ptr; }; +void ArchDesc::addForm(PipelineForm *ptr) { _pipeline = ptr; }; + +// Build MatchList array and construct MatchLists +void ArchDesc::generateMatchLists() { + // Call inspection routines to populate array + inspectOperands(); + inspectInstructions(); +} + +// Build MatchList structures for operands +void ArchDesc::inspectOperands() { + + // Iterate through all operands + _operands.reset(); + OperandForm *op; + for( ; (op = (OperandForm*)_operands.iter()) != NULL;) { + // Construct list of top-level operands (components) + op->build_components(); + + // Ensure that match field is defined. + if ( op->_matrule == NULL ) continue; + + // Type check match rules + check_optype(op->_matrule); + + // Construct chain rules + build_chain_rule(op); + + MatchRule &mrule = *op->_matrule; + Predicate *pred = op->_predicate; + + // Grab the machine type of the operand + const char *rootOp = op->_ident; + mrule._machType = rootOp; + + // Check for special cases + if (strcmp(rootOp,"Universe")==0) continue; + if (strcmp(rootOp,"label")==0) continue; + // !!!!! !!!!! + assert( strcmp(rootOp,"sReg") != 0, "Disable untyped 'sReg'"); + if (strcmp(rootOp,"sRegI")==0) continue; + if (strcmp(rootOp,"sRegP")==0) continue; + if (strcmp(rootOp,"sRegF")==0) continue; + if (strcmp(rootOp,"sRegD")==0) continue; + if (strcmp(rootOp,"sRegL")==0) continue; + + // Cost for this match + const char *costStr = op->cost(); + const char *defaultCost = + ((AttributeForm*)_globalNames[AttributeForm::_op_cost])->_attrdef; + const char *cost = costStr? costStr : defaultCost; + + // Find result type for match. + const char *result = op->reduce_result(); + bool has_root = false; + + // Construct a MatchList for this entry + buildMatchList(op->_matrule, result, rootOp, pred, cost); + } +} + +// Build MatchList structures for instructions +void ArchDesc::inspectInstructions() { + + // Iterate through all instructions + _instructions.reset(); + InstructForm *instr; + for( ; (instr = (InstructForm*)_instructions.iter()) != NULL; ) { + // Construct list of top-level operands (components) + instr->build_components(); + + // Ensure that match field is defined. + if ( instr->_matrule == NULL ) continue; + + MatchRule &mrule = *instr->_matrule; + Predicate *pred = instr->build_predicate(); + + // Grab the machine type of the operand + const char *rootOp = instr->_ident; + mrule._machType = rootOp; + + // Cost for this match + const char *costStr = instr->cost(); + const char *defaultCost = + ((AttributeForm*)_globalNames[AttributeForm::_ins_cost])->_attrdef; + const char *cost = costStr? costStr : defaultCost; + + // Find result type for match + const char *result = instr->reduce_result(); + + Attribute *attr = instr->_attribs; + while (attr != NULL) { + if (strcmp(attr->_ident,"ins_short_branch") == 0 && + attr->int_val(*this) != 0) { + instr->set_short_branch(true); + } else if (strcmp(attr->_ident,"ins_alignment") == 0 && + attr->int_val(*this) != 0) { + instr->set_alignment(attr->int_val(*this)); + } + attr = (Attribute *)attr->_next; + } + + if (!instr->is_short_branch()) { + buildMatchList(instr->_matrule, result, mrule._machType, pred, cost); + } + } +} + +static int setsResult(MatchRule &mrule) { + if (strcmp(mrule._name,"Set") == 0) return 1; + return 0; +} + +const char *ArchDesc::getMatchListIndex(MatchRule &mrule) { + if (setsResult(mrule)) { + // right child + return mrule._rChild->_opType; + } else { + // first entry + return mrule._opType; + } +} + + +//------------------------------result of reduction---------------------------- + + +//------------------------------left reduction--------------------------------- +// Return the left reduction associated with an internal name +const char *ArchDesc::reduceLeft(char *internalName) { + const char *left = NULL; + MatchNode *mnode = (MatchNode*)_internalMatch[internalName]; + if (mnode->_lChild) { + mnode = mnode->_lChild; + left = mnode->_internalop ? mnode->_internalop : mnode->_opType; + } + return left; +} + + +//------------------------------right reduction-------------------------------- +const char *ArchDesc::reduceRight(char *internalName) { + const char *right = NULL; + MatchNode *mnode = (MatchNode*)_internalMatch[internalName]; + if (mnode->_rChild) { + mnode = mnode->_rChild; + right = mnode->_internalop ? mnode->_internalop : mnode->_opType; + } + return right; +} + + +//------------------------------check_optype----------------------------------- +void ArchDesc::check_optype(MatchRule *mrule) { + MatchRule *rule = mrule; + + // !!!!! + // // Cycle through the list of match rules + // while(mrule) { + // // Check for a filled in type field + // if (mrule->_opType == NULL) { + // const Form *form = operands[_result]; + // OpClassForm *opcForm = form ? form->is_opclass() : NULL; + // assert(opcForm != NULL, "Match Rule contains invalid operand name."); + // } + // char *opType = opcForm->_ident; + // } +} + +//------------------------------add_chain_rule_entry-------------------------- +void ArchDesc::add_chain_rule_entry(const char *src, const char *cost, + const char *result) { + // Look-up the operation in chain rule table + ChainList *lst = (ChainList *)_chainRules[src]; + if (lst == NULL) { + lst = new ChainList(); + _chainRules.Insert(src, lst); + } + if (!lst->search(result)) { + if (cost == NULL) { + cost = ((AttributeForm*)_globalNames[AttributeForm::_op_cost])->_attrdef; + } + lst->insert(result, cost, result); + } +} + +//------------------------------build_chain_rule------------------------------- +void ArchDesc::build_chain_rule(OperandForm *oper) { + MatchRule *rule; + + // Check for chain rules here + // If this is only a chain rule + if ((oper->_matrule) && (oper->_matrule->_lChild == NULL) && + (oper->_matrule->_rChild == NULL)) { + + const Form *form = _globalNames[oper->_matrule->_opType]; + if ((form) && form->is_operand() && + (form->ideal_only() == false)) { + add_chain_rule_entry(oper->_matrule->_opType, oper->cost(), oper->_ident); + } + // Check for additional chain rules + if (oper->_matrule->_next) { + rule = oper->_matrule; + do { + rule = rule->_next; + // Any extra match rules after the first must be chain rules + const Form *form = _globalNames[rule->_opType]; + if ((form) && form->is_operand() && + (form->ideal_only() == false)) { + add_chain_rule_entry(rule->_opType, oper->cost(), oper->_ident); + } + } while(rule->_next != NULL); + } + } + else if ((oper->_matrule) && (oper->_matrule->_next)) { + // Regardles of whether the first matchrule is a chain rule, check the list + rule = oper->_matrule; + do { + rule = rule->_next; + // Any extra match rules after the first must be chain rules + const Form *form = _globalNames[rule->_opType]; + if ((form) && form->is_operand() && + (form->ideal_only() == false)) { + assert( oper->cost(), "This case expects NULL cost, not default cost"); + add_chain_rule_entry(rule->_opType, oper->cost(), oper->_ident); + } + } while(rule->_next != NULL); + } + +} + +//------------------------------buildMatchList--------------------------------- +// operands and instructions provide the result +void ArchDesc::buildMatchList(MatchRule *mrule, const char *resultStr, + const char *rootOp, Predicate *pred, + const char *cost) { + const char *leftstr, *rightstr; + MatchNode *mnode; + + leftstr = rightstr = NULL; + // Check for chain rule, and do not generate a match list for it + if ( mrule->is_chain_rule(_globalNames) ) { + return; + } + + // Identify index position among ideal operands + intptr_t index = _last_opcode; + const char *indexStr = getMatchListIndex(*mrule); + index = (intptr_t)_idealIndex[indexStr]; + if (index == 0) { + fprintf(stderr, "Ideal node missing: %s\n", indexStr); + assert(index != 0, "Failed lookup of ideal node\n"); + } + + // Check that this will be placed appropriately in the DFA + if (index >= _last_opcode) { + fprintf(stderr, "Invalid match rule %s <-- ( %s )\n", + resultStr ? resultStr : " ", + rootOp ? rootOp : " "); + assert(index < _last_opcode, "Matching item not in ideal graph\n"); + return; + } + + + // Walk the MatchRule, generating MatchList entries for each level + // of the rule (each nesting of parentheses) + // Check for "Set" + if (!strcmp(mrule->_opType, "Set")) { + mnode = mrule->_rChild; + buildMList(mnode, rootOp, resultStr, pred, cost); + return; + } + // Build MatchLists for children + // Check each child for an internal operand name, and use that name + // for the parent's matchlist entry if it exists + mnode = mrule->_lChild; + if (mnode) { + buildMList(mnode, NULL, NULL, NULL, NULL); + leftstr = mnode->_internalop ? mnode->_internalop : mnode->_opType; + } + mnode = mrule->_rChild; + if (mnode) { + buildMList(mnode, NULL, NULL, NULL, NULL); + rightstr = mnode->_internalop ? mnode->_internalop : mnode->_opType; + } + // Search for an identical matchlist entry already on the list + if ((_mlistab[index] == NULL) || + (_mlistab[index] && + !_mlistab[index]->search(rootOp, resultStr, leftstr, rightstr, pred))) { + // Place this match rule at front of list + MatchList *mList = + new MatchList(_mlistab[index], pred, cost, + rootOp, resultStr, leftstr, rightstr); + _mlistab[index] = mList; + } +} + +// Recursive call for construction of match lists +void ArchDesc::buildMList(MatchNode *node, const char *rootOp, + const char *resultOp, Predicate *pred, + const char *cost) { + const char *leftstr, *rightstr; + const char *resultop; + const char *opcode; + MatchNode *mnode; + Form *form; + + leftstr = rightstr = NULL; + // Do not process leaves of the Match Tree if they are not ideal + if ((node) && (node->_lChild == NULL) && (node->_rChild == NULL) && + ((form = (Form *)_globalNames[node->_opType]) != NULL) && + (!form->ideal_only())) { + return; + } + + // Identify index position among ideal operands + intptr_t index = _last_opcode; + const char *indexStr = node ? node->_opType : (char *) " "; + index = (intptr_t)_idealIndex[indexStr]; + if (index == 0) { + fprintf(stderr, "error: operand \"%s\" not found\n", indexStr); + assert(0, "fatal error"); + } + + // Build MatchLists for children + // Check each child for an internal operand name, and use that name + // for the parent's matchlist entry if it exists + mnode = node->_lChild; + if (mnode) { + buildMList(mnode, NULL, NULL, NULL, NULL); + leftstr = mnode->_internalop ? mnode->_internalop : mnode->_opType; + } + mnode = node->_rChild; + if (mnode) { + buildMList(mnode, NULL, NULL, NULL, NULL); + rightstr = mnode->_internalop ? mnode->_internalop : mnode->_opType; + } + // Grab the string for the opcode of this list entry + if (rootOp == NULL) { + opcode = (node->_internalop) ? node->_internalop : node->_opType; + } else { + opcode = rootOp; + } + // Grab the string for the result of this list entry + if (resultOp == NULL) { + resultop = (node->_internalop) ? node->_internalop : node->_opType; + } + else resultop = resultOp; + // Search for an identical matchlist entry already on the list + if ((_mlistab[index] == NULL) || (_mlistab[index] && + !_mlistab[index]->search(opcode, resultop, leftstr, rightstr, pred))) { + // Place this match rule at front of list + MatchList *mList = + new MatchList(_mlistab[index],pred,cost, + opcode, resultop, leftstr, rightstr); + _mlistab[index] = mList; + } +} + +// Count number of OperandForms defined +int ArchDesc::operandFormCount() { + // Only interested in ones with non-NULL match rule + int count = 0; _operands.reset(); + OperandForm *cur; + for( ; (cur = (OperandForm*)_operands.iter()) != NULL; ) { + if (cur->_matrule != NULL) ++count; + }; + return count; +} + +// Count number of OpClassForms defined +int ArchDesc::opclassFormCount() { + // Only interested in ones with non-NULL match rule + int count = 0; _operands.reset(); + OpClassForm *cur; + for( ; (cur = (OpClassForm*)_opclass.iter()) != NULL; ) { + ++count; + }; + return count; +} + +// Count number of InstructForms defined +int ArchDesc::instructFormCount() { + // Only interested in ones with non-NULL match rule + int count = 0; _instructions.reset(); + InstructForm *cur; + for( ; (cur = (InstructForm*)_instructions.iter()) != NULL; ) { + if (cur->_matrule != NULL) ++count; + }; + return count; +} + + +//------------------------------get_preproc_def-------------------------------- +// Return the textual binding for a given CPP flag name. +// Return NULL if there is no binding, or it has been #undef-ed. +char* ArchDesc::get_preproc_def(const char* flag) { + SourceForm* deff = (SourceForm*) _preproc_table[flag]; + return (deff == NULL) ? NULL : deff->_code; +} + + +//------------------------------set_preproc_def-------------------------------- +// Change or create a textual binding for a given CPP flag name. +// Giving NULL means the flag name is to be #undef-ed. +// In any case, _preproc_list collects all names either #defined or #undef-ed. +void ArchDesc::set_preproc_def(const char* flag, const char* def) { + SourceForm* deff = (SourceForm*) _preproc_table[flag]; + if (deff == NULL) { + deff = new SourceForm(NULL); + _preproc_table.Insert(flag, deff); + _preproc_list.addName(flag); // this supports iteration + } + deff->_code = (char*) def; +} + + +bool ArchDesc::verify() { + + if (_register) + assert( _register->verify(), "Register declarations failed verification"); + if (!_quiet_mode) + fprintf(stderr,"\n"); + // fprintf(stderr,"---------------------------- Verify Operands ---------------\n"); + // _operands.verify(); + // fprintf(stderr,"\n"); + // fprintf(stderr,"---------------------------- Verify Operand Classes --------\n"); + // _opclass.verify(); + // fprintf(stderr,"\n"); + // fprintf(stderr,"---------------------------- Verify Attributes ------------\n"); + // _attributes.verify(); + // fprintf(stderr,"\n"); + if (!_quiet_mode) + fprintf(stderr,"---------------------------- Verify Instructions ----------------------------\n"); + _instructions.verify(); + if (!_quiet_mode) + fprintf(stderr,"\n"); + // if ( _encode ) { + // fprintf(stderr,"---------------------------- Verify Encodings --------------\n"); + // _encode->verify(); + // } + + //if (_pipeline) _pipeline->verify(); + + return true; +} + + +void ArchDesc::dump() { + _pre_header.dump(); + _header.dump(); + _source.dump(); + if (_register) _register->dump(); + fprintf(stderr,"\n"); + fprintf(stderr,"------------------ Dump Operands ---------------------\n"); + _operands.dump(); + fprintf(stderr,"\n"); + fprintf(stderr,"------------------ Dump Operand Classes --------------\n"); + _opclass.dump(); + fprintf(stderr,"\n"); + fprintf(stderr,"------------------ Dump Attributes ------------------\n"); + _attributes.dump(); + fprintf(stderr,"\n"); + fprintf(stderr,"------------------ Dump Instructions -----------------\n"); + _instructions.dump(); + if ( _encode ) { + fprintf(stderr,"------------------ Dump Encodings --------------------\n"); + _encode->dump(); + } + if (_pipeline) _pipeline->dump(); +} + + +//------------------------------init_keywords---------------------------------- +// Load the kewords into the global name table +void ArchDesc::initKeywords(FormDict& names) { + // Insert keyword strings into Global Name Table. Keywords have a NULL value + // field for quick easy identification when checking identifiers. + names.Insert("instruct", NULL); + names.Insert("operand", NULL); + names.Insert("attribute", NULL); + names.Insert("source", NULL); + names.Insert("register", NULL); + names.Insert("pipeline", NULL); + names.Insert("constraint", NULL); + names.Insert("predicate", NULL); + names.Insert("encode", NULL); + names.Insert("enc_class", NULL); + names.Insert("interface", NULL); + names.Insert("opcode", NULL); + names.Insert("ins_encode", NULL); + names.Insert("match", NULL); + names.Insert("effect", NULL); + names.Insert("expand", NULL); + names.Insert("rewrite", NULL); + names.Insert("reg_def", NULL); + names.Insert("reg_class", NULL); + names.Insert("alloc_class", NULL); + names.Insert("resource", NULL); + names.Insert("pipe_class", NULL); + names.Insert("pipe_desc", NULL); +} + + +//------------------------------internal_err---------------------------------- +// Issue a parser error message, and skip to the end of the current line +void ArchDesc::internal_err(const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + _internal_errs += emit_msg(0, INTERNAL_ERR, 0, fmt, args); + va_end(args); + + _no_output = 1; +} + +//------------------------------syntax_err---------------------------------- +// Issue a parser error message, and skip to the end of the current line +void ArchDesc::syntax_err(int lineno, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + _internal_errs += emit_msg(0, SYNERR, lineno, fmt, args); + va_end(args); + + _no_output = 1; +} + +//------------------------------emit_msg--------------------------------------- +// Emit a user message, typically a warning or error +int ArchDesc::emit_msg(int quiet, int flag, int line, const char *fmt, + va_list args) { + static int last_lineno = -1; + int i; + const char *pref; + + switch(flag) { + case 0: pref = "Warning: "; break; + case 1: pref = "Syntax Error: "; break; + case 2: pref = "Semantic Error: "; break; + case 3: pref = "Internal Error: "; break; + default: assert(0, ""); break; + } + + if (line == last_lineno) return 0; + last_lineno = line; + + if (!quiet) { /* no output if in quiet mode */ + i = fprintf(errfile, "%s(%d) ", _ADL_file._name, line); + while (i++ <= 15) fputc(' ', errfile); + fprintf(errfile, "%-8s:", pref); + vfprintf(errfile, fmt, args); + fprintf(errfile, "\n"); } + return 1; +} + + +// --------------------------------------------------------------------------- +//--------Utilities to build mappings for machine registers ------------------ +// --------------------------------------------------------------------------- + +// Construct the name of the register mask. +static const char *getRegMask(const char *reg_class_name) { + if( reg_class_name == NULL ) return "RegMask::Empty"; + + if (strcmp(reg_class_name,"Universe")==0) { + return "RegMask::Empty"; + } else if (strcmp(reg_class_name,"stack_slots")==0) { + return "(Compile::current()->FIRST_STACK_mask())"; + } else { + char *rc_name = toUpper(reg_class_name); + const char *mask = "_mask"; + int length = (int)strlen(rc_name) + (int)strlen(mask) + 3; + char *regMask = new char[length]; + sprintf(regMask,"%s%s", rc_name, mask); + return regMask; + } +} + +// Convert a register class name to its register mask. +const char *ArchDesc::reg_class_to_reg_mask(const char *rc_name) { + const char *reg_mask = "RegMask::Empty"; + + if( _register ) { + RegClass *reg_class = _register->getRegClass(rc_name); + if (reg_class == NULL) { + syntax_err(0, "Use of an undefined register class %s", rc_name); + return reg_mask; + } + + // Construct the name of the register mask. + reg_mask = getRegMask(rc_name); + } + + return reg_mask; +} + + +// Obtain the name of the RegMask for an OperandForm +const char *ArchDesc::reg_mask(OperandForm &opForm) { + const char *regMask = "RegMask::Empty"; + + // Check constraints on result's register class + const char *result_class = opForm.constrained_reg_class(); + if (!result_class) opForm.dump(); + assert( result_class, "Resulting register class was not defined for operand"); + regMask = reg_class_to_reg_mask( result_class ); + + return regMask; +} + +// Obtain the name of the RegMask for an InstructForm +const char *ArchDesc::reg_mask(InstructForm &inForm) { + const char *result = inForm.reduce_result(); + assert( result, + "Did not find result operand or RegMask for this instruction"); + + // Instructions producing 'Universe' use RegMask::Empty + if( strcmp(result,"Universe")==0 ) { + return "RegMask::Empty"; + } + + // Lookup this result operand and get its register class + Form *form = (Form*)_globalNames[result]; + assert( form, "Result operand must be defined"); + OperandForm *oper = form->is_operand(); + assert( oper, "Result must be an OperandForm"); + return reg_mask( *oper ); +} + + +// Obtain the STACK_OR_reg_mask name for an OperandForm +char *ArchDesc::stack_or_reg_mask(OperandForm &opForm) { + // name of cisc_spillable version + const char *reg_mask_name = reg_mask(opForm); + assert( reg_mask_name != NULL, "called with incorrect opForm"); + + const char *stack_or = "STACK_OR_"; + int length = (int)strlen(stack_or) + (int)strlen(reg_mask_name) + 1; + char *result = new char[length]; + sprintf(result,"%s%s", stack_or, reg_mask_name); + + return result; +} + +// Record that the register class must generate a stack_or_reg_mask +void ArchDesc::set_stack_or_reg(const char *reg_class_name) { + if( _register ) { + RegClass *reg_class = _register->getRegClass(reg_class_name); + reg_class->_stack_or_reg = true; + } +} + + +// Return the type signature for the ideal operation +const char *ArchDesc::getIdealType(const char *idealOp) { + // Find last character in idealOp, it specifies the type + char last_char = 0; + const char *ptr = idealOp; + for( ; *ptr != '\0'; ++ptr) { + last_char = *ptr; + } + + // !!!!! + switch( last_char ) { + case 'I': return "TypeInt::INT"; + case 'P': return "TypePtr::BOTTOM"; + case 'F': return "Type::FLOAT"; + case 'D': return "Type::DOUBLE"; + case 'L': return "TypeLong::LONG"; + case 's': return "TypeInt::CC /*flags*/"; + default: + return NULL; + // !!!!! + // internal_err("Ideal type %s with unrecognized type\n",idealOp); + break; + } + + return NULL; +} + + + +OperandForm *ArchDesc::constructOperand(const char *ident, + bool ideal_only) { + OperandForm *opForm = new OperandForm(ident, ideal_only); + _globalNames.Insert(ident, opForm); + addForm(opForm); + + return opForm; +} + + +// Import predefined base types: Set = 1, RegI, RegP, ... +void ArchDesc::initBaseOpTypes() { + // Create OperandForm and assign type for each opcode. + for (int i = 1; i < _last_machine_leaf; ++i) { + char *ident = (char *)NodeClassNames[i]; + constructOperand(ident, true); + } + // Create InstructForm and assign type for each ideal instruction. + for ( int j = _last_machine_leaf+1; j < _last_opcode; ++j) { + char *ident = (char *)NodeClassNames[j]; + if(!strcmp(ident, "ConI") || !strcmp(ident, "ConP") || + !strcmp(ident, "ConF") || !strcmp(ident, "ConD") || + !strcmp(ident, "ConL") || !strcmp(ident, "Con" ) || + !strcmp(ident, "Bool") ) { + constructOperand(ident, true); + } + else { + InstructForm *insForm = new InstructForm(ident, true); + // insForm->_opcode = nextUserOpType(ident); + _globalNames.Insert(ident,insForm); + addForm(insForm); + } + } + + { OperandForm *opForm; + // Create operand type "Universe" for return instructions. + const char *ident = "Universe"; + opForm = constructOperand(ident, false); + + // Create operand type "label" for branch targets + ident = "label"; + opForm = constructOperand(ident, false); + + // !!!!! Update - when adding a new sReg/stackSlot type + // Create operand types "sReg[IPFDL]" for stack slot registers + opForm = constructOperand("sRegI", false); + opForm->_constraint = new Constraint("ALLOC_IN_RC", "stack_slots"); + opForm = constructOperand("sRegP", false); + opForm->_constraint = new Constraint("ALLOC_IN_RC", "stack_slots"); + opForm = constructOperand("sRegF", false); + opForm->_constraint = new Constraint("ALLOC_IN_RC", "stack_slots"); + opForm = constructOperand("sRegD", false); + opForm->_constraint = new Constraint("ALLOC_IN_RC", "stack_slots"); + opForm = constructOperand("sRegL", false); + opForm->_constraint = new Constraint("ALLOC_IN_RC", "stack_slots"); + + // Create operand type "method" for call targets + ident = "method"; + opForm = constructOperand(ident, false); + } + + // Create Effect Forms for each of the legal effects + // USE, DEF, USE_DEF, KILL, USE_KILL + { + const char *ident = "USE"; + Effect *eForm = new Effect(ident); + _globalNames.Insert(ident, eForm); + ident = "DEF"; + eForm = new Effect(ident); + _globalNames.Insert(ident, eForm); + ident = "USE_DEF"; + eForm = new Effect(ident); + _globalNames.Insert(ident, eForm); + ident = "KILL"; + eForm = new Effect(ident); + _globalNames.Insert(ident, eForm); + ident = "USE_KILL"; + eForm = new Effect(ident); + _globalNames.Insert(ident, eForm); + ident = "TEMP"; + eForm = new Effect(ident); + _globalNames.Insert(ident, eForm); + } + + // + // Build mapping from ideal names to ideal indices + int idealIndex = 0; + for (idealIndex = 1; idealIndex < _last_machine_leaf; ++idealIndex) { + const char *idealName = NodeClassNames[idealIndex]; + _idealIndex.Insert((void*)idealName, (void*)idealIndex); + } + for ( idealIndex = _last_machine_leaf+1; + idealIndex < _last_opcode; ++idealIndex) { + const char *idealName = NodeClassNames[idealIndex]; + _idealIndex.Insert((void*)idealName, (void*)idealIndex); + } + +} + + +//---------------------------addSUNcopyright------------------------------- +// output SUN copyright info +void ArchDesc::addSunCopyright(char* legal, int size, FILE *fp) { + fwrite(legal, size, 1, fp); + fprintf(fp,"\n"); + fprintf(fp,"// Machine Generated File. Do Not Edit!\n"); + fprintf(fp,"\n"); +} + +//---------------------------machineDependentIncludes-------------------------- +// output #include declarations for machine specific files +void ArchDesc::machineDependentIncludes(ADLFILE &adlfile) { + const char *basename = adlfile._name; + const char *cp; + for (cp = basename; *cp; cp++) + if (*cp == '/') basename = cp+1; + + // Build #include lines + fprintf(adlfile._fp, "\n"); + fprintf(adlfile._fp, "#include \"incls/_precompiled.incl\"\n"); + fprintf(adlfile._fp, "#include \"incls/_%s.incl\"\n",basename); + fprintf(adlfile._fp, "\n"); + +} + + +//---------------------------addPreprocessorChecks----------------------------- +// Output C preprocessor code to verify the backend compilation environment. +// The idea is to force code produced by "adlc -DHS64" to be compiled by a +// command of the form "CC ... -DHS64 ...", so that any #ifdefs in the source +// blocks select C code that is consistent with adlc's selections of AD code. +void ArchDesc::addPreprocessorChecks(FILE *fp) { + const char* flag; + _preproc_list.reset(); + if (_preproc_list.count() > 0 && !_preproc_list.current_is_signal()) { + fprintf(fp, "// Check consistency of C++ compilation with ADLC options:\n"); + } + for (_preproc_list.reset(); (flag = _preproc_list.iter()) != NULL; ) { + if (_preproc_list.current_is_signal()) break; + char* def = get_preproc_def(flag); + fprintf(fp, "// Check adlc "); + if (def) + fprintf(fp, "-D%s=%s\n", flag, def); + else fprintf(fp, "-U%s\n", flag); + fprintf(fp, "#%s %s\n", + def ? "ifndef" : "ifdef", flag); + fprintf(fp, "# error \"%s %s be defined\"\n", + flag, def ? "must" : "must not"); + fprintf(fp, "#endif // %s\n", flag); + } +} + + +// Convert operand name into enum name +const char *ArchDesc::machOperEnum(const char *opName) { + return ArchDesc::getMachOperEnum(opName); +} + +// Convert operand name into enum name +const char *ArchDesc::getMachOperEnum(const char *opName) { + return (opName ? toUpper(opName) : opName); +} + +//---------------------------buildMustCloneMap----------------------------- +// Flag cases when machine needs cloned values or instructions +void ArchDesc::buildMustCloneMap(FILE *fp_hpp, FILE *fp_cpp) { + // Build external declarations for mappings + fprintf(fp_hpp, "// Mapping from machine-independent opcode to boolean\n"); + fprintf(fp_hpp, "// Flag cases where machine needs cloned values or instructions\n"); + fprintf(fp_hpp, "extern const char must_clone[];\n"); + fprintf(fp_hpp, "\n"); + + // Build mapping from ideal names to ideal indices + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "// Mapping from machine-independent opcode to boolean\n"); + fprintf(fp_cpp, "const char must_clone[] = {\n"); + for (int idealIndex = 0; idealIndex < _last_opcode; ++idealIndex) { + int must_clone = 0; + const char *idealName = NodeClassNames[idealIndex]; + // Previously selected constants for cloning + // !!!!! + // These are the current machine-dependent clones + if ( strcmp(idealName,"CmpI") == 0 + || strcmp(idealName,"CmpU") == 0 + || strcmp(idealName,"CmpP") == 0 + || strcmp(idealName,"CmpL") == 0 + || strcmp(idealName,"CmpD") == 0 + || strcmp(idealName,"CmpF") == 0 + || strcmp(idealName,"FastLock") == 0 + || strcmp(idealName,"FastUnlock") == 0 + || strcmp(idealName,"Bool") == 0 + || strcmp(idealName,"Binary") == 0 ) { + // Removed ConI from the must_clone list. CPUs that cannot use + // large constants as immediates manifest the constant as an + // instruction. The must_clone flag prevents the constant from + // floating up out of loops. + must_clone = 1; + } + fprintf(fp_cpp, " %d%s // %s: %d\n", must_clone, + (idealIndex != (_last_opcode - 1)) ? "," : " // no trailing comma", + idealName, idealIndex); + } + // Finish defining table + fprintf(fp_cpp, "};\n"); +} diff --git a/hotspot/src/share/vm/adlc/archDesc.hpp b/hotspot/src/share/vm/adlc/archDesc.hpp new file mode 100644 index 00000000000..0d32a5dd017 --- /dev/null +++ b/hotspot/src/share/vm/adlc/archDesc.hpp @@ -0,0 +1,389 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Definitions for Error Flags +#define WARN 0 +#define SYNERR 1 +#define SEMERR 2 +#define INTERNAL_ERR 3 + +// Minimal declarations for include files +class OutputMap; +class ProductionState; +class Expr; + +// STRUCTURE FOR HANDLING INPUT AND OUTPUT FILES +typedef BufferedFile ADLFILE; + +//---------------------------ChainList----------------------------------------- +class ChainList { + NameList _name; + NameList _cost; + NameList _rule; + +public: + void insert(const char *name, const char *cost, const char *rule); + bool search(const char *name); + + void reset(); + bool iter(const char * &name, const char * &cost, const char * &rule); + + void dump(); + void output(FILE *fp); + + ChainList(); + ~ChainList(); +}; + +//---------------------------MatchList----------------------------------------- +class MatchList { +private: + MatchList *_next; + Predicate *_pred; // Predicate which applies to this match rule + const char *_cost; + +public: + const char *_opcode; + const char *_resultStr; + const char *_lchild; + const char *_rchild; + + MatchList(MatchList *nxt, Predicate *prd): _next(nxt), _pred(prd), _cost(NULL){ + _resultStr = _lchild = _rchild = _opcode = NULL; } + + MatchList(MatchList *nxt, Predicate *prd, const char *cost, + const char *opcode, const char *resultStr, const char *lchild, + const char *rchild) + : _next(nxt), _pred(prd), _cost(cost), _opcode(opcode), + _resultStr(resultStr), _lchild(lchild), _rchild(rchild) { } + + MatchList *get_next(void) { return _next; } + char *get_pred(void) { return (_pred?_pred->_pred:NULL); } + Predicate *get_pred_obj(void) { return _pred; } + const char *get_cost(void) { return _cost == NULL ? "0" :_cost; } + bool search(const char *opc, const char *res, const char *lch, + const char *rch, Predicate *pr); + + void dump(); + void output(FILE *fp); +}; + +//---------------------------ArchDesc------------------------------------------ +class ArchDesc { +private: + FormDict _globalNames; // Global names + Dict _idealIndex; // Map ideal names to index in enumeration + ExprDict _globalDefs; // Global definitions, #defines + int _internalOpCounter; // Internal Operand Counter + + FormList _header; // List of Source Code Forms for hpp file + FormList _pre_header; // ditto for the very top of the hpp file + FormList _source; // List of Source Code Forms for output + FormList _instructions; // List of Instruction Forms for output + FormList _machnodes; // List of Node Classes (special for pipelining) + FormList _operands; // List of Operand Forms for output + FormList _opclass; // List of Operand Class Forms for output + FormList _attributes; // List of Attribute Forms for parsing + RegisterForm *_register; // Only one Register Form allowed + FrameForm *_frame; // Describe stack-frame layout + EncodeForm *_encode; // Only one Encode Form allowed + PipelineForm *_pipeline; // Pipeline Form for output + + bool _has_match_rule[_last_opcode]; // found AD rule for ideal node in .ad + + MatchList *_mlistab[_last_opcode]; // Array of MatchLists + + // The Architecture Description identifies which user-defined operand can be used + // to access [stack_pointer + offset] + OperandForm *_cisc_spill_operand; + + // Methods for outputting the DFA + void gen_match(FILE *fp, MatchList &mlist, ProductionState &status, Dict &operands_chained_from); + void chain_rule(FILE *fp, const char *indent, const char *ideal, + const Expr *icost, const char *irule, + Dict &operands_chained_from, ProductionState &status); + void chain_rule_c(FILE *fp, char *indent, char *ideal, char *irule); // %%%%% TODO: remove this + void expand_opclass(FILE *fp, const char *indent, const Expr *cost, + const char *result_type, ProductionState &status); + Expr *calc_cost(FILE *fp, const char *spaces, MatchList &mList, ProductionState &status); + void prune_matchlist(Dict &minimize, MatchList &mlist); + + // Helper function that outputs code to generate an instruction in MachNodeGenerator + void buildMachNode(FILE *fp_cpp, InstructForm *inst, const char *indent); + +public: + ArchDesc(); + ~ArchDesc(); + + // Option flags which control miscellaneous behaviors throughout the code + int _TotalLines; // Line Counter + int _no_output; // Flag to disable output of DFA, etc. + int _quiet_mode; // Do not output banner messages, etc. + int _disable_warnings; // Do not output warning messages + int _dfa_debug; // Debug Flag for generated DFA + int _dfa_small; // Debug Flag for generated DFA + int _adl_debug; // Debug Flag for ADLC + int _adlocation_debug; // Debug Flag to use ad file locations + bool _cisc_spill_debug; // Debug Flag to see cisc-spill-instructions + bool _short_branch_debug; // Debug Flag to see short branch instructions + + // Error/Warning Counts + int _syntax_errs; // Count of syntax errors + int _semantic_errs; // Count of semantic errors + int _warnings; // Count warnings + int _internal_errs; // Count of internal errors + + // Accessor for private data. + void has_match_rule(int opc, const bool b) { _has_match_rule[opc] = b; } + + // I/O Files + ADLFILE _ADL_file; // Input Architecture Description File + // Machine dependent files, built from architecture definition + ADLFILE _DFA_file; // File for definition of Matcher::DFA + ADLFILE _HPP_file; // File for ArchNode class declarations + ADLFILE _CPP_file; // File for ArchNode class defintions + ADLFILE _CPP_CLONE_file; // File for MachNode/Oper clone defintions + ADLFILE _CPP_EXPAND_file; // File for MachNode expand methods + ADLFILE _CPP_FORMAT_file; // File for MachNode/Oper format defintions + ADLFILE _CPP_GEN_file; // File for MachNode/Oper generator methods + ADLFILE _CPP_MISC_file; // File for miscellaneous MachNode/Oper tables & methods + ADLFILE _CPP_PEEPHOLE_file; // File for MachNode peephole methods + ADLFILE _CPP_PIPELINE_file; // File for MachNode pipeline defintions + ADLFILE _VM_file; // File for constants needed in VM code + ADLFILE _bug_file; // DFA debugging file + + // I/O helper methods + int open_file(bool required, ADLFILE & adf, const char *action); + void close_file(int delete_out, ADLFILE & adf); + int open_files(void); + void close_files(int delete_out); + + Dict _chainRules; // Maps user operand names to ChainRules + Dict _internalOps; // Maps match strings to internal operand names + NameList _internalOpNames; // List internal operand names + Dict _internalMatch; // Map internal name to its MatchNode + + NameList _preproc_list; // Preprocessor flag names + FormDict _preproc_table;// Preprocessor flag bindings + char* get_preproc_def(const char* flag); + void set_preproc_def(const char* flag, const char* def); + + FormDict& globalNames() {return _globalNames;} // map global names to forms + void initKeywords(FormDict& globals); // Add keywords to global name table + + ExprDict& globalDefs() {return _globalDefs;} // map global names to expressions + + OperandForm *constructOperand(const char *ident, bool ideal_only); + void initBaseOpTypes(); // Import predefined base types. + + void addForm(PreHeaderForm *ptr); // Add objects to pre-header list + void addForm(HeaderForm *ptr); // Add objects to header list + void addForm(SourceForm *ptr); // Add objects to source list + void addForm(EncodeForm *ptr); // Add objects to encode list + void addForm(InstructForm *ptr); // Add objects to the instruct list + void addForm(OperandForm *ptr); // Add objects to the operand list + void addForm(OpClassForm *ptr); // Add objects to the opclasss list + void addForm(AttributeForm *ptr); // Add objects to the attributes list + void addForm(RegisterForm *ptr); // Add objects to the register list + void addForm(FrameForm *ptr); // Add objects to the frame list + void addForm(PipelineForm *ptr); // Add objects to the pipeline list + void addForm(MachNodeForm *ptr); // Add objects to the machnode list + + int operandFormCount(); // Count number of OperandForms defined + int opclassFormCount(); // Count number of OpClassForms defined + int instructFormCount(); // Count number of InstructForms defined + + inline void getForm(EncodeForm **ptr) { *ptr = _encode; } + + bool verify(); + void dump(); + + // Helper utility that gets MatchList components from inside MatchRule + void check_optype(MatchRule *mrule); + void build_chain_rule(OperandForm *oper); + void add_chain_rule_entry(const char *src, const char *cost, + const char *result); + const char *getMatchListIndex(MatchRule &mrule); + void generateMatchLists(); // Build MatchList array and populate it + void inspectOperands(); // Build MatchLists for all operands + void inspectOpClasses(); // Build MatchLists for all operands + void inspectInstructions(); // Build MatchLists for all operands + void buildDFA(FILE *fp); // Driver for constructing the DFA + void gen_dfa_state_body(FILE *fp, Dict &minmize, ProductionState &status, Dict &chained, int i); // Driver for constructing the DFA state bodies + + // Helper utilities to generate reduction maps for internal operands + const char *reduceLeft (char *internalName); + const char *reduceRight(char *internalName); + + // Build enumerations, (1) dense operand index, (2) operands and opcodes + const char *machOperEnum(const char *opName); // create dense index names using static function + static const char *getMachOperEnum(const char *opName);// create dense index name + void buildMachOperEnum(FILE *fp_hpp);// dense enumeration for operands + void buildMachOpcodesEnum(FILE *fp_hpp);// enumeration for MachOpers & MachNodes + + // Helper utilities to generate Register Masks + RegisterForm *get_registers() { return _register; } + const char *reg_mask(OperandForm &opForm); + const char *reg_mask(InstructForm &instForm); + const char *reg_class_to_reg_mask(const char *reg_class); + char *stack_or_reg_mask(OperandForm &opForm); // name of cisc_spillable version + // This register class should also generate a stack_or_reg_mask + void set_stack_or_reg(const char *reg_class_name); // for cisc-spillable reg classes + // Generate an enumeration of register mask names and the RegMask objects. + void declare_register_masks(FILE *fp_cpp); + void build_register_masks(FILE *fp_cpp); + // Generate enumeration of machine register numbers + void buildMachRegisterNumbers(FILE *fp_hpp); + // Generate enumeration of machine register encodings + void buildMachRegisterEncodes(FILE *fp_hpp); + // Generate Regsiter Size Array + void declareRegSizes(FILE *fp_hpp); + // Generate Pipeline Class information + void declare_pipe_classes(FILE *fp_hpp); + // Generate Pipeline definitions + void build_pipeline_enums(FILE *fp_cpp); + // Generate Pipeline Class information + void build_pipe_classes(FILE *fp_cpp); + + // Declare and define mappings from rules to result and input types + void build_map(OutputMap &map); + void buildReduceMaps(FILE *fp_hpp, FILE *fp_cpp); + // build flags for signaling that our machine needs this instruction cloned + void buildMustCloneMap(FILE *fp_hpp, FILE *fp_cpp); + + // output SUN copyright info + void addSunCopyright(char* legal, int size, FILE *fp); + // output #include declarations for machine specific files + void machineDependentIncludes(ADLFILE &adlfile); + // Output C preprocessor code to verify the backend compilation environment. + void addPreprocessorChecks(FILE *fp); + // Output C source and header (source_hpp) blocks. + void addPreHeaderBlocks(FILE *fp_hpp); + void addHeaderBlocks(FILE *fp_hpp); + void addSourceBlocks(FILE *fp_cpp); + void generate_adlc_verification(FILE *fp_cpp); + + // output declaration of class State + void defineStateClass(FILE *fp); + + // Generator for MachOper objects given integer type + void buildMachOperGenerator(FILE *fp_cpp); + // Generator for MachNode objects given integer type + void buildMachNodeGenerator(FILE *fp_cpp); + + // Generator for Expand methods for instructions with expand rules + void defineExpand(FILE *fp, InstructForm *node); + // Generator for Peephole methods for instructions with peephole rules + void definePeephole(FILE *fp, InstructForm *node); + // Generator for Size methods for instructions + void defineSize(FILE *fp, InstructForm &node); + // Generator for Emit methods for instructions + void defineEmit(FILE *fp, InstructForm &node); + // Define a MachOper encode method + void define_oper_interface(FILE *fp, OperandForm &oper, FormDict &globals, + const char *name, const char *encoding); + + // Methods to construct the MachNode class hierarchy + // Return the type signature for the ideal operation + const char *getIdealType(const char *idealOp); + // Declare and define the classes derived from MachOper and MachNode + void declareClasses(FILE *fp_hpp); + void defineClasses(FILE *fp_cpp); + + // Emit an ADLC message + void internal_err( const char *fmt, ...); + void syntax_err ( int lineno, const char *fmt, ...); + int emit_msg(int quiet, int flag, int linenum, const char *fmt, + va_list args); + + // Generator for has_match_rule methods + void buildInstructMatchCheck(FILE *fp_cpp) const; + + // Generator for Frame Methods + void buildFrameMethods(FILE *fp_cpp); + + // Generate CISC_spilling oracle and MachNode::cisc_spill() methods + void build_cisc_spill_instructions(FILE *fp_hpp, FILE *fp_cpp); + void identify_cisc_spill_instructions(); + void identify_short_branches(); + void identify_unique_operands(); + void set_cisc_spill_operand(OperandForm *opForm) { _cisc_spill_operand = opForm; } + OperandForm *cisc_spill_operand() { return _cisc_spill_operand; } + bool can_cisc_spill() { return _cisc_spill_operand != NULL; } + + +protected: + // build MatchList from MatchRule + void buildMatchList(MatchRule *mrule, const char *resultStr, + const char *rootOp, Predicate *pred, const char *cost); + + void buildMList(MatchNode *node, const char *rootOp, const char *resultOp, + Predicate *pred, const char *cost); + + friend class ADLParser; + +}; + + +// -------------------------------- maps ------------------------------------ + +// Base class for generating a mapping from rule number to value. +// Used with ArchDesc::build_map() for all maps except "enum MachOperands" +// A derived class defines the appropriate output for a specific mapping. +class OutputMap { +protected: + FILE *_hpp; + FILE *_cpp; + FormDict &_globals; + ArchDesc &_AD; +public: + OutputMap (FILE *decl_file, FILE *def_file, FormDict &globals, ArchDesc &AD) + : _hpp(decl_file), _cpp(def_file), _globals(globals), _AD(AD) {}; + // Access files used by this routine + FILE *decl_file() { return _hpp; } + FILE *def_file() { return _cpp; } + // Positions in iteration that derived class will be told about + enum position { BEGIN_OPERANDS, + BEGIN_OPCLASSES, + BEGIN_INTERNALS, + BEGIN_INSTRUCTIONS, + BEGIN_INST_CHAIN_RULES, + END_INST_CHAIN_RULES, + BEGIN_REMATERIALIZE, + END_REMATERIALIZE, + END_INSTRUCTIONS + }; + // Output routines specific to the derived class + virtual void declaration() {} + virtual void definition() {} + virtual void closing() { fprintf(_cpp, "};\n"); } + virtual void map(OperandForm &oper) { } + virtual void map(OpClassForm &opc) { } + virtual void map(char *internal_name) { } + // Allow enum-MachOperands to turn-off instructions + virtual bool do_instructions() { return true; } + virtual void map(InstructForm &inst) { } + // Allow derived class to output name and position specific info + virtual void record_position(OutputMap::position place, int index) {} +}; diff --git a/hotspot/src/share/vm/adlc/arena.cpp b/hotspot/src/share/vm/adlc/arena.cpp new file mode 100644 index 00000000000..928ecfe14a5 --- /dev/null +++ b/hotspot/src/share/vm/adlc/arena.cpp @@ -0,0 +1,172 @@ +/* + * Copyright 1998-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "adlc.hpp" + +void* Chunk::operator new(size_t requested_size, size_t length) { + return CHeapObj::operator new(requested_size + length); +} + +void Chunk::operator delete(void* p, size_t length) { + CHeapObj::operator delete(p); +} + +Chunk::Chunk(size_t length) { + _next = NULL; // Chain on the linked list + _len = length; // Save actual size +} + +//------------------------------chop------------------------------------------- +void Chunk::chop() { + Chunk *k = this; + while( k ) { + Chunk *tmp = k->_next; + // clear out this chunk (to detect allocation bugs) + memset(k, 0xBAADBABE, k->_len); + free(k); // Free chunk (was malloc'd) + k = tmp; + } +} + +void Chunk::next_chop() { + _next->chop(); + _next = NULL; +} + +//------------------------------Arena------------------------------------------ +Arena::Arena( size_t init_size ) { + init_size = (init_size+3) & ~3; + _first = _chunk = new (init_size) Chunk(init_size); + _hwm = _chunk->bottom(); // Save the cached hwm, max + _max = _chunk->top(); + set_size_in_bytes(init_size); +} + +Arena::Arena() { + _first = _chunk = new (Chunk::init_size) Chunk(Chunk::init_size); + _hwm = _chunk->bottom(); // Save the cached hwm, max + _max = _chunk->top(); + set_size_in_bytes(Chunk::init_size); +} + +Arena::Arena( Arena *a ) +: _chunk(a->_chunk), _hwm(a->_hwm), _max(a->_max), _first(a->_first) { + set_size_in_bytes(a->size_in_bytes()); +} + +//------------------------------used------------------------------------------- +// Total of all Chunks in arena +size_t Arena::used() const { + size_t sum = _chunk->_len - (_max-_hwm); // Size leftover in this Chunk + register Chunk *k = _first; + while( k != _chunk) { // Whilst have Chunks in a row + sum += k->_len; // Total size of this Chunk + k = k->_next; // Bump along to next Chunk + } + return sum; // Return total consumed space. +} + +//------------------------------grow------------------------------------------- +// Grow a new Chunk +void* Arena::grow( size_t x ) { + // Get minimal required size. Either real big, or even bigger for giant objs + size_t len = max(x, Chunk::size); + + register Chunk *k = _chunk; // Get filled-up chunk address + _chunk = new (len) Chunk(len); + + if( k ) k->_next = _chunk; // Append new chunk to end of linked list + else _first = _chunk; + _hwm = _chunk->bottom(); // Save the cached hwm, max + _max = _chunk->top(); + set_size_in_bytes(size_in_bytes() + len); + void* result = _hwm; + _hwm += x; + return result; +} + +//------------------------------calloc----------------------------------------- +// Allocate zeroed storage in Arena +void *Arena::Acalloc( size_t items, size_t x ) { + size_t z = items*x; // Total size needed + void *ptr = Amalloc(z); // Get space + memset( ptr, 0, z ); // Zap space + return ptr; // Return space +} + +//------------------------------realloc---------------------------------------- +// Reallocate storage in Arena. +void *Arena::Arealloc( void *old_ptr, size_t old_size, size_t new_size ) { + char *c_old = (char*)old_ptr; // Handy name + // Stupid fast special case + if( new_size <= old_size ) { // Shrink in-place + if( c_old+old_size == _hwm) // Attempt to free the excess bytes + _hwm = c_old+new_size; // Adjust hwm + return c_old; + } + + // See if we can resize in-place + if( (c_old+old_size == _hwm) && // Adjusting recent thing + (c_old+new_size <= _max) ) { // Still fits where it sits + _hwm = c_old+new_size; // Adjust hwm + return c_old; // Return old pointer + } + + // Oops, got to relocate guts + void *new_ptr = Amalloc(new_size); + memcpy( new_ptr, c_old, old_size ); + Afree(c_old,old_size); // Mostly done to keep stats accurate + return new_ptr; +} + +//------------------------------reset------------------------------------------ +// Reset this Arena to empty, and return this Arenas guts in a new Arena. +Arena *Arena::reset(void) { + Arena *a = new Arena(this); // New empty arena + _first = _chunk = NULL; // Normal, new-arena initialization + _hwm = _max = NULL; + return a; // Return Arena with guts +} + +//------------------------------contains--------------------------------------- +// Determine if pointer belongs to this Arena or not. +bool Arena::contains( const void *ptr ) const { + if( (void*)_chunk->bottom() <= ptr && ptr < (void*)_hwm ) + return true; // Check for in this chunk + for( Chunk *c = _first; c; c = c->_next ) + if( (void*)c->bottom() <= ptr && ptr < (void*)c->top()) + return true; // Check for every chunk in Arena + return false; // Not in any Chunk, so not in Arena +} + +//----------------------------------------------------------------------------- +// CHeapObj + +void* CHeapObj::operator new(size_t size){ + return (void *) malloc(size); +} + +void CHeapObj::operator delete(void* p){ + free(p); +} diff --git a/hotspot/src/share/vm/adlc/arena.hpp b/hotspot/src/share/vm/adlc/arena.hpp new file mode 100644 index 00000000000..96246d9ef96 --- /dev/null +++ b/hotspot/src/share/vm/adlc/arena.hpp @@ -0,0 +1,157 @@ +/* + * Copyright 1998-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// All classes in the virtual machine must be subclassed +// by one of the following allocation classes: +// +// +// For objects allocated in the C-heap (managed by: free & malloc). +// - CHeapObj +// +// +// For embedded objects. +// - ValueObj +// +// For classes used as name spaces. +// - AllStatic +// + +class CHeapObj { + public: + void* operator new(size_t size); + void operator delete(void* p); + void* new_array(size_t size); +}; + + +// Base class for objects used as value objects. +// Calling new or delete will result in fatal error. + +class ValueObj { + public: + void* operator new(size_t size); + void operator delete(void* p); +}; + +// Base class for classes that constitute name spaces. + +class AllStatic { + public: + void* operator new(size_t size); + void operator delete(void* p); +}; + + +//------------------------------Chunk------------------------------------------ +// Linked list of raw memory chunks +class Chunk: public CHeapObj { + public: + void* operator new(size_t size, size_t length); + void operator delete(void* p, size_t length); + Chunk(size_t length); + + enum { + init_size = 1*1024, // Size of first chunk + size = 32*1024 // Default size of an Arena chunk (following the first) + }; + Chunk* _next; // Next Chunk in list + size_t _len; // Size of this Chunk + + void chop(); // Chop this chunk + void next_chop(); // Chop next chunk + + // Boundaries of data area (possibly unused) + char* bottom() const { return ((char*) this) + sizeof(Chunk); } + char* top() const { return bottom() + _len; } +}; + + +//------------------------------Arena------------------------------------------ +// Fast allocation of memory +class Arena: public CHeapObj { +protected: + friend class ResourceMark; + friend class HandleMark; + friend class NoHandleMark; + Chunk *_first; // First chunk + Chunk *_chunk; // current chunk + char *_hwm, *_max; // High water mark and max in current chunk + void* grow(size_t x); // Get a new Chunk of at least size x + size_t _size_in_bytes; // Size of arena (used for memory usage tracing) +public: + Arena(); + Arena(size_t init_size); + Arena(Arena *old); + ~Arena() { _first->chop(); } + char* hwm() const { return _hwm; } + + // Fast allocate in the arena. Common case is: pointer test + increment. + void* Amalloc(size_t x) { +#ifdef _LP64 + x = (x + (8-1)) & ((unsigned)(-8)); +#else + x = (x + (4-1)) & ((unsigned)(-4)); +#endif + if (_hwm + x > _max) { + return grow(x); + } else { + char *old = _hwm; + _hwm += x; + return old; + } + } + // Further assume size is padded out to words + // Warning: in LP64, Amalloc_4 is really Amalloc_8 + void *Amalloc_4(size_t x) { + assert( (x&(sizeof(char*)-1)) == 0, "misaligned size" ); + if (_hwm + x > _max) { + return grow(x); + } else { + char *old = _hwm; + _hwm += x; + return old; + } + } + + // Fast delete in area. Common case is: NOP (except for storage reclaimed) + void Afree(void *ptr, size_t size) { + if (((char*)ptr) + size == _hwm) _hwm = (char*)ptr; + } + + void *Acalloc( size_t items, size_t x ); + void *Arealloc( void *old_ptr, size_t old_size, size_t new_size ); + + // Reset this Arena to empty, and return this Arenas guts in a new Arena. + Arena *reset(void); + + // Determine if pointer belongs to this Arena or not. + bool contains( const void *ptr ) const; + + // Total of all chunks in use (not thread-safe) + size_t used() const; + + // Total # of bytes used + size_t size_in_bytes() const { return _size_in_bytes; } + void set_size_in_bytes(size_t size) { _size_in_bytes = size; } +}; diff --git a/hotspot/src/share/vm/adlc/dfa.cpp b/hotspot/src/share/vm/adlc/dfa.cpp new file mode 100644 index 00000000000..36d56062cfd --- /dev/null +++ b/hotspot/src/share/vm/adlc/dfa.cpp @@ -0,0 +1,1021 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// DFA.CPP - Method definitions for outputting the matcher DFA from ADLC +#include "adlc.hpp" + +//---------------------------Switches for debugging output--------------------- +static bool debug_output = false; +static bool debug_output1 = false; // top level chain rules + +//---------------------------Access to internals of class State---------------- +static const char *sLeft = "_kids[0]"; +static const char *sRight = "_kids[1]"; + +//---------------------------DFA productions----------------------------------- +static const char *dfa_production = "DFA_PRODUCTION"; +static const char *dfa_production_set_valid = "DFA_PRODUCTION__SET_VALID"; + +//---------------------------Production State---------------------------------- +static const char *knownInvalid = "knownInvalid"; // The result does NOT have a rule defined +static const char *knownValid = "knownValid"; // The result must be produced by a rule +static const char *unknownValid = "unknownValid"; // Unknown (probably due to a child or predicate constraint) + +static const char *noConstraint = "noConstraint"; // No constraints seen so far +static const char *hasConstraint = "hasConstraint"; // Within the first constraint + + +//------------------------------Production------------------------------------ +// Track the status of productions for a particular result +class Production { +public: + const char *_result; + const char *_constraint; + const char *_valid; + Expr *_cost_lb; // Cost lower bound for this production + Expr *_cost_ub; // Cost upper bound for this production + +public: + Production(const char *result, const char *constraint, const char *valid); + ~Production() {}; + + void initialize(); // reset to be an empty container + + const char *valid() const { return _valid; } + Expr *cost_lb() const { return (Expr *)_cost_lb; } + Expr *cost_ub() const { return (Expr *)_cost_ub; } + + void print(); +}; + + +//------------------------------ProductionState-------------------------------- +// Track the status of all production rule results +// Reset for each root opcode (e.g., Op_RegI, Op_AddI, ...) +class ProductionState { +private: + Dict _production; // map result of production, char*, to information or NULL + const char *_constraint; + +public: + // cmpstr does string comparisions. hashstr computes a key. + ProductionState(Arena *arena) : _production(cmpstr, hashstr, arena) { initialize(); }; + ~ProductionState() { }; + + void initialize(); // reset local and dictionary state + + const char *constraint(); + void set_constraint(const char *constraint); // currently working inside of constraints + + const char *valid(const char *result); // unknownValid, or status for this production + void set_valid(const char *result); // if not constrained, set status to knownValid + + Expr *cost_lb(const char *result); + Expr *cost_ub(const char *result); + void set_cost_bounds(const char *result, const Expr *cost, bool has_state_check, bool has_cost_check); + + // Return the Production associated with the result, + // or create a new Production and insert it into the dictionary. + Production *getProduction(const char *result); + + void print(); + +private: + // Disable public use of constructor, copy-ctor, ... + ProductionState( ) : _production(cmpstr, hashstr, Form::arena) { assert( false, "NotImplemented"); }; + ProductionState( const ProductionState & ) : _production(cmpstr, hashstr, Form::arena) { assert( false, "NotImplemented"); }; // Deep-copy +}; + + +//---------------------------Helper Functions---------------------------------- +// cost_check template: +// 1) if (STATE__NOT_YET_VALID(EBXREGI) || _cost[EBXREGI] > c) { +// 2) DFA_PRODUCTION__SET_VALID(EBXREGI, cmovI_memu_rule, c) +// 3) } +// +static void cost_check(FILE *fp, const char *spaces, + const char *arrayIdx, const Expr *cost, const char *rule, ProductionState &status) { + bool state_check = false; // true if this production needs to check validity + bool cost_check = false; // true if this production needs to check cost + bool cost_is_above_upper_bound = false; // true if this production is unnecessary due to high cost + bool cost_is_below_lower_bound = false; // true if this production replaces a higher cost production + + // Get information about this production + const Expr *previous_ub = status.cost_ub(arrayIdx); + if( !previous_ub->is_unknown() ) { + if( previous_ub->less_than_or_equal(cost) ) { + cost_is_above_upper_bound = true; + if( debug_output ) { fprintf(fp, "// Previous rule with lower cost than: %s === %s_rule costs %s\n", arrayIdx, rule, cost->as_string()); } + } + } + + const Expr *previous_lb = status.cost_lb(arrayIdx); + if( !previous_lb->is_unknown() ) { + if( cost->less_than_or_equal(previous_lb) ) { + cost_is_below_lower_bound = true; + if( debug_output ) { fprintf(fp, "// Previous rule with higher cost\n"); } + } + } + + // line 1) + // Check for validity and compare to other match costs + const char *validity_check = status.valid(arrayIdx); + if( validity_check == unknownValid ) { + fprintf(fp, "%sif (STATE__NOT_YET_VALID(%s) || _cost[%s] > %s) {\n", spaces, arrayIdx, arrayIdx, cost->as_string()); + state_check = true; + cost_check = true; + } + else if( validity_check == knownInvalid ) { + if( debug_output ) { fprintf(fp, "%s// %s KNOWN_INVALID \n", spaces, arrayIdx); } + } + else if( validity_check == knownValid ) { + if( cost_is_above_upper_bound ) { + // production cost is known to be too high. + return; + } else if( cost_is_below_lower_bound ) { + // production will unconditionally overwrite a previous production that had higher cost + } else { + fprintf(fp, "%sif ( /* %s KNOWN_VALID || */ _cost[%s] > %s) {\n", spaces, arrayIdx, arrayIdx, cost->as_string()); + cost_check = true; + } + } + + // line 2) + // no need to set State vector if our state is knownValid + const char *production = (validity_check == knownValid) ? dfa_production : dfa_production_set_valid; + fprintf(fp, "%s %s(%s, %s_rule, %s)", spaces, production, arrayIdx, rule, cost->as_string() ); + if( validity_check == knownValid ) { + if( cost_is_below_lower_bound ) { fprintf(fp, "\t // overwrites higher cost rule"); } + } + fprintf(fp, "\n"); + + // line 3) + if( cost_check || state_check ) { + fprintf(fp, "%s}\n", spaces); + } + + status.set_cost_bounds(arrayIdx, cost, state_check, cost_check); + + // Update ProductionState + if( validity_check != knownValid ) { + // set State vector if not previously known + status.set_valid(arrayIdx); + } +} + + +//---------------------------child_test---------------------------------------- +// Example: +// STATE__VALID_CHILD(_kids[0], FOO) && STATE__VALID_CHILD(_kids[1], BAR) +// Macro equivalent to: _kids[0]->valid(FOO) && _kids[1]->valid(BAR) +// +static void child_test(FILE *fp, MatchList &mList) { + if( mList._lchild ) // If left child, check it + fprintf(fp, "STATE__VALID_CHILD(_kids[0], %s)", ArchDesc::getMachOperEnum(mList._lchild)); + if( mList._lchild && mList._rchild ) // If both, add the "&&" + fprintf(fp, " && " ); + if( mList._rchild ) // If right child, check it + fprintf(fp, "STATE__VALID_CHILD(_kids[1], %s)", ArchDesc::getMachOperEnum(mList._rchild)); +} + +//---------------------------calc_cost----------------------------------------- +// Example: +// unsigned int c = _kids[0]->_cost[FOO] + _kids[1]->_cost[BAR] + 5; +// +Expr *ArchDesc::calc_cost(FILE *fp, const char *spaces, MatchList &mList, ProductionState &status) { + fprintf(fp, "%sunsigned int c = ", spaces); + Expr *c = new Expr("0"); + if (mList._lchild ) { // If left child, add it in + sprintf(Expr::buffer(), "_kids[0]->_cost[%s]", ArchDesc::getMachOperEnum(mList._lchild)); + c->add(Expr::buffer()); +} + if (mList._rchild) { // If right child, add it in + sprintf(Expr::buffer(), "_kids[1]->_cost[%s]", ArchDesc::getMachOperEnum(mList._rchild)); + c->add(Expr::buffer()); + } + // Add in cost of this rule + const char *mList_cost = mList.get_cost(); + c->add(mList_cost, *this); + + fprintf(fp, "%s;\n", c->as_string()); + c->set_external_name("c"); + return c; +} + + +//---------------------------gen_match----------------------------------------- +void ArchDesc::gen_match(FILE *fp, MatchList &mList, ProductionState &status, Dict &operands_chained_from) { + const char *spaces4 = " "; + const char *spaces6 = " "; + + fprintf(fp, "%s", spaces4); + // Only generate child tests if this is not a leaf node + bool has_child_constraints = mList._lchild || mList._rchild; + const char *predicate_test = mList.get_pred(); + if( has_child_constraints || predicate_test ) { + // Open the child-and-predicate-test braces + fprintf(fp, "if( "); + status.set_constraint(hasConstraint); + child_test(fp, mList); + // Only generate predicate test if one exists for this match + if( predicate_test ) { + if( has_child_constraints ) { fprintf(fp," &&\n"); } + fprintf(fp, "%s %s", spaces6, predicate_test); + } + // End of outer tests + fprintf(fp," ) "); + } else { + // No child or predicate test needed + status.set_constraint(noConstraint); + } + + // End of outer tests + fprintf(fp,"{\n"); + + // Calculate cost of this match + const Expr *cost = calc_cost(fp, spaces6, mList, status); + // Check against other match costs, and update cost & rule vectors + cost_check(fp, spaces6, ArchDesc::getMachOperEnum(mList._resultStr), cost, mList._opcode, status); + + // If this is a member of an operand class, update the class cost & rule + expand_opclass( fp, spaces6, cost, mList._resultStr, status); + + // Check if this rule should be used to generate the chains as well. + const char *rule = /* set rule to "Invalid" for internal operands */ + strcmp(mList._opcode,mList._resultStr) ? mList._opcode : "Invalid"; + + // If this rule produces an operand which has associated chain rules, + // update the operands with the chain rule + this rule cost & this rule. + chain_rule(fp, spaces6, mList._resultStr, cost, rule, operands_chained_from, status); + + // Close the child-and-predicate-test braces + fprintf(fp, " }\n"); + +} + + +//---------------------------expand_opclass------------------------------------ +// Chain from one result_type to all other members of its operand class +void ArchDesc::expand_opclass(FILE *fp, const char *indent, const Expr *cost, + const char *result_type, ProductionState &status) { + const Form *form = _globalNames[result_type]; + OperandForm *op = form ? form->is_operand() : NULL; + if( op && op->_classes.count() > 0 ) { + if( debug_output ) { fprintf(fp, "// expand operand classes for operand: %s \n", (char *)op->_ident ); } // %%%%% Explanation + // Iterate through all operand classes which include this operand + op->_classes.reset(); + const char *oclass; + // Expr *cCost = new Expr(cost); + while( (oclass = op->_classes.iter()) != NULL ) + // Check against other match costs, and update cost & rule vectors + cost_check(fp, indent, ArchDesc::getMachOperEnum(oclass), cost, result_type, status); + } +} + +//---------------------------chain_rule---------------------------------------- +// Starting at 'operand', check if we know how to automatically generate other results +void ArchDesc::chain_rule(FILE *fp, const char *indent, const char *operand, + const Expr *icost, const char *irule, Dict &operands_chained_from, ProductionState &status) { + + // Check if we have already generated chains from this starting point + if( operands_chained_from[operand] != NULL ) { + return; + } else { + operands_chained_from.Insert( operand, operand); + } + if( debug_output ) { fprintf(fp, "// chain rules starting from: %s and %s \n", (char *)operand, (char *)irule); } // %%%%% Explanation + + ChainList *lst = (ChainList *)_chainRules[operand]; + if (lst) { + // printf("\nChain from <%s> at cost #%s\n",operand, icost ? icost : "_"); + const char *result, *cost, *rule; + for(lst->reset(); (lst->iter(result,cost,rule)) == true; ) { + // Do not generate operands that are already available + if( operands_chained_from[result] != NULL ) { + continue; + } else { + // Compute the cost for previous match + chain_rule_cost + // total_cost = icost + cost; + Expr *total_cost = icost->clone(); // icost + cost + total_cost->add(cost, *this); + + // Check for transitive chain rules + Form *form = (Form *)_globalNames[rule]; + if ( ! form->is_instruction()) { + // printf(" result=%s cost=%s rule=%s\n", result, total_cost, rule); + // Check against other match costs, and update cost & rule vectors + const char *reduce_rule = strcmp(irule,"Invalid") ? irule : rule; + cost_check(fp, indent, ArchDesc::getMachOperEnum(result), total_cost, reduce_rule, status); + chain_rule(fp, indent, result, total_cost, irule, operands_chained_from, status); + } else { + // printf(" result=%s cost=%s rule=%s\n", result, total_cost, rule); + // Check against other match costs, and update cost & rule vectors + cost_check(fp, indent, ArchDesc::getMachOperEnum(result), total_cost, rule, status); + chain_rule(fp, indent, result, total_cost, rule, operands_chained_from, status); + } + + // If this is a member of an operand class, update class cost & rule + expand_opclass( fp, indent, total_cost, result, status ); + } + } + } +} + +//---------------------------prune_matchlist----------------------------------- +// Check for duplicate entries in a matchlist, and prune out the higher cost +// entry. +void ArchDesc::prune_matchlist(Dict &minimize, MatchList &mlist) { + +} + +//---------------------------buildDFA------------------------------------------ +// DFA is a large switch with case statements for each ideal opcode encountered +// in any match rule in the ad file. Each case has a series of if's to handle +// the match or fail decisions. The matches test the cost function of that +// rule, and prune any cases which are higher cost for the same reduction. +// In order to generate the DFA we walk the table of ideal opcode/MatchList +// pairs generated by the ADLC front end to build the contents of the case +// statements (a series of if statements). +void ArchDesc::buildDFA(FILE* fp) { + int i; + // Remember operands that are the starting points for chain rules. + // Prevent cycles by checking if we have already generated chain. + Dict operands_chained_from(cmpstr, hashstr, Form::arena); + + // Hash inputs to match rules so that final DFA contains only one entry for + // each match pattern which is the low cost entry. + Dict minimize(cmpstr, hashstr, Form::arena); + + // Track status of dfa for each resulting production + // reset for each ideal root. + ProductionState status(Form::arena); + + // Output the start of the DFA method into the output file + + fprintf(fp, "\n"); + fprintf(fp, "//------------------------- Source -----------------------------------------\n"); + // Do not put random source code into the DFA. + // If there are constants which need sharing, put them in "source_hpp" forms. + // _source.output(fp); + fprintf(fp, "\n"); + fprintf(fp, "//------------------------- Attributes -------------------------------------\n"); + _attributes.output(fp); + fprintf(fp, "\n"); + fprintf(fp, "//------------------------- Macros -----------------------------------------\n"); + // #define DFA_PRODUCTION(result, rule, cost)\ + // _cost[ (result) ] = cost; _rule[ (result) ] = rule; + fprintf(fp, "#define %s(result, rule, cost)\\\n", dfa_production); + fprintf(fp, " _cost[ (result) ] = cost; _rule[ (result) ] = rule;\n"); + fprintf(fp, "\n"); + + // #define DFA_PRODUCTION__SET_VALID(result, rule, cost)\ + // DFA_PRODUCTION( (result), (rule), (cost) ); STATE__SET_VALID( (result) ); + fprintf(fp, "#define %s(result, rule, cost)\\\n", dfa_production_set_valid); + fprintf(fp, " %s( (result), (rule), (cost) ); STATE__SET_VALID( (result) );\n", dfa_production); + fprintf(fp, "\n"); + + fprintf(fp, "//------------------------- DFA --------------------------------------------\n"); + + fprintf(fp, +"// DFA is a large switch with case statements for each ideal opcode encountered\n" +"// in any match rule in the ad file. Each case has a series of if's to handle\n" +"// the match or fail decisions. The matches test the cost function of that\n" +"// rule, and prune any cases which are higher cost for the same reduction.\n" +"// In order to generate the DFA we walk the table of ideal opcode/MatchList\n" +"// pairs generated by the ADLC front end to build the contents of the case\n" +"// statements (a series of if statements).\n" +); + fprintf(fp, "\n"); + fprintf(fp, "\n"); + if (_dfa_small) { + // Now build the individual routines just like the switch entries in large version + // Iterate over the table of MatchLists, start at first valid opcode of 1 + for (i = 1; i < _last_opcode; i++) { + if (_mlistab[i] == NULL) continue; + // Generate the routine header statement for this opcode + fprintf(fp, "void State::_sub_Op_%s(const Node *n){\n", NodeClassNames[i]); + // Generate body. Shared for both inline and out-of-line version + gen_dfa_state_body(fp, minimize, status, operands_chained_from, i); + // End of routine + fprintf(fp, "}\n"); + } + } + fprintf(fp, "bool State::DFA"); + fprintf(fp, "(int opcode, const Node *n) {\n"); + fprintf(fp, " switch(opcode) {\n"); + + // Iterate over the table of MatchLists, start at first valid opcode of 1 + for (i = 1; i < _last_opcode; i++) { + if (_mlistab[i] == NULL) continue; + // Generate the case statement for this opcode + if (_dfa_small) { + fprintf(fp, " case Op_%s: { _sub_Op_%s(n);\n", NodeClassNames[i], NodeClassNames[i]); + } else { + fprintf(fp, " case Op_%s: {\n", NodeClassNames[i]); + // Walk the list, compacting it + gen_dfa_state_body(fp, minimize, status, operands_chained_from, i); + } + // Print the "break" + fprintf(fp, " break;\n"); + fprintf(fp, " }\n"); + } + + // Generate the default case for switch(opcode) + fprintf(fp, " \n"); + fprintf(fp, " default:\n"); + fprintf(fp, " tty->print(\"Default case invoked for: \\n\");\n"); + fprintf(fp, " tty->print(\" opcode = %cd, \\\"%cs\\\"\\n\", opcode, NodeClassNames[opcode]);\n", '%', '%'); + fprintf(fp, " return false;\n"); + fprintf(fp, " }\n"); + + // Return status, indicating a successful match. + fprintf(fp, " return true;\n"); + // Generate the closing brace for method Matcher::DFA + fprintf(fp, "}\n"); + Expr::check_buffers(); +} + + +class dfa_shared_preds { + enum { count = 2 }; + + static bool _found[count]; + static const char* _type [count]; + static const char* _var [count]; + static const char* _pred [count]; + + static void check_index(int index) { assert( 0 <= index && index < count, "Invalid index"); } + + // Confirm that this is a separate sub-expression. + // Only need to catch common cases like " ... && shared ..." + // and avoid hazardous ones like "...->shared" + static bool valid_loc(char *pred, char *shared) { + // start of predicate is valid + if( shared == pred ) return true; + + // Check previous character and recurse if needed + char *prev = shared - 1; + char c = *prev; + switch( c ) { + case ' ': + return dfa_shared_preds::valid_loc(pred, prev); + case '!': + case '(': + case '<': + case '=': + return true; + case '|': + if( prev != pred && *(prev-1) == '|' ) return true; + case '&': + if( prev != pred && *(prev-1) == '&' ) return true; + default: + return false; + } + + return false; + } + +public: + + static bool found(int index){ check_index(index); return _found[index]; } + static void set_found(int index, bool val) { check_index(index); _found[index] = val; } + static void reset_found() { + for( int i = 0; i < count; ++i ) { _found[i] = false; } + }; + + static const char* type(int index) { check_index(index); return _type[index]; } + static const char* var (int index) { check_index(index); return _var [index]; } + static const char* pred(int index) { check_index(index); return _pred[index]; } + + // Check each predicate in the MatchList for common sub-expressions + static void cse_matchlist(MatchList *matchList) { + for( MatchList *mList = matchList; mList != NULL; mList = mList->get_next() ) { + Predicate* predicate = mList->get_pred_obj(); + char* pred = mList->get_pred(); + if( pred != NULL ) { + for(int index = 0; index < count; ++index ) { + const char *shared_pred = dfa_shared_preds::pred(index); + const char *shared_pred_var = dfa_shared_preds::var(index); + bool result = dfa_shared_preds::cse_predicate(predicate, shared_pred, shared_pred_var); + if( result ) dfa_shared_preds::set_found(index, true); + } + } + } + } + + // If the Predicate contains a common sub-expression, replace the Predicate's + // string with one that uses the variable name. + static bool cse_predicate(Predicate* predicate, const char *shared_pred, const char *shared_pred_var) { + bool result = false; + char *pred = predicate->_pred; + if( pred != NULL ) { + char *new_pred = pred; + for( char *shared_pred_loc = strstr(new_pred, shared_pred); + shared_pred_loc != NULL && dfa_shared_preds::valid_loc(new_pred,shared_pred_loc); + shared_pred_loc = strstr(new_pred, shared_pred) ) { + // Do not modify the original predicate string, it is shared + if( new_pred == pred ) { + new_pred = strdup(pred); + shared_pred_loc = strstr(new_pred, shared_pred); + } + // Replace shared_pred with variable name + strncpy(shared_pred_loc, shared_pred_var, strlen(shared_pred_var)); + } + // Install new predicate + if( new_pred != pred ) { + predicate->_pred = new_pred; + result = true; + } + } + return result; + } + + // Output the hoisted common sub-expression if we found it in predicates + static void generate_cse(FILE *fp) { + for(int j = 0; j < count; ++j ) { + if( dfa_shared_preds::found(j) ) { + const char *shared_pred_type = dfa_shared_preds::type(j); + const char *shared_pred_var = dfa_shared_preds::var(j); + const char *shared_pred = dfa_shared_preds::pred(j); + fprintf(fp, " %s %s = %s;\n", shared_pred_type, shared_pred_var, shared_pred); + } + } + } +}; +// shared predicates, _var and _pred entry should be the same length +bool dfa_shared_preds::_found[dfa_shared_preds::count] = { false, false }; +const char* dfa_shared_preds::_type[dfa_shared_preds::count] = { "int", "bool" }; +const char* dfa_shared_preds::_var [dfa_shared_preds::count] = { "_n_get_int__", "Compile__current____select_24_bit_instr__" }; +const char* dfa_shared_preds::_pred[dfa_shared_preds::count] = { "n->get_int()", "Compile::current()->select_24_bit_instr()" }; + + +void ArchDesc::gen_dfa_state_body(FILE* fp, Dict &minimize, ProductionState &status, Dict &operands_chained_from, int i) { + // Start the body of each Op_XXX sub-dfa with a clean state. + status.initialize(); + + // Walk the list, compacting it + MatchList* mList = _mlistab[i]; + do { + // Hash each entry using inputs as key and pointer as data. + // If there is already an entry, keep the one with lower cost, and + // remove the other one from the list. + prune_matchlist(minimize, *mList); + // Iterate + mList = mList->get_next(); + } while(mList != NULL); + + // Hoist previously specified common sub-expressions out of predicates + dfa_shared_preds::reset_found(); + dfa_shared_preds::cse_matchlist(_mlistab[i]); + dfa_shared_preds::generate_cse(fp); + + mList = _mlistab[i]; + + // Walk the list again, generating code + do { + // Each match can generate its own chains + operands_chained_from.Clear(); + gen_match(fp, *mList, status, operands_chained_from); + mList = mList->get_next(); + } while(mList != NULL); + // Fill in any chain rules which add instructions + // These can generate their own chains as well. + operands_chained_from.Clear(); // + if( debug_output1 ) { fprintf(fp, "// top level chain rules for: %s \n", (char *)NodeClassNames[i]); } // %%%%% Explanation + const Expr *zeroCost = new Expr("0"); + chain_rule(fp, " ", (char *)NodeClassNames[i], zeroCost, "Invalid", + operands_chained_from, status); +} + + + +//------------------------------Expr------------------------------------------ +Expr *Expr::_unknown_expr = NULL; +char Expr::string_buffer[STRING_BUFFER_LENGTH]; +char Expr::external_buffer[STRING_BUFFER_LENGTH]; +bool Expr::_init_buffers = Expr::init_buffers(); + +Expr::Expr() { + _external_name = NULL; + _expr = "Invalid_Expr"; + _min_value = Expr::Max; + _max_value = Expr::Zero; +} +Expr::Expr(const char *cost) { + _external_name = NULL; + + int intval = 0; + if( cost == NULL ) { + _expr = "0"; + _min_value = Expr::Zero; + _max_value = Expr::Zero; + } + else if( ADLParser::is_int_token(cost, intval) ) { + _expr = cost; + _min_value = intval; + _max_value = intval; + } + else { + assert( strcmp(cost,"0") != 0, "Recognize string zero as an int"); + _expr = cost; + _min_value = Expr::Zero; + _max_value = Expr::Max; + } +} + +Expr::Expr(const char *name, const char *expression, int min_value, int max_value) { + _external_name = name; + _expr = expression ? expression : name; + _min_value = min_value; + _max_value = max_value; + assert(_min_value >= 0 && _min_value <= Expr::Max, "value out of range"); + assert(_max_value >= 0 && _max_value <= Expr::Max, "value out of range"); +} + +Expr *Expr::clone() const { + Expr *cost = new Expr(); + cost->_external_name = _external_name; + cost->_expr = _expr; + cost->_min_value = _min_value; + cost->_max_value = _max_value; + + return cost; +} + +void Expr::add(const Expr *c) { + // Do not update fields until all computation is complete + const char *external = compute_external(this, c); + const char *expr = compute_expr(this, c); + int min_value = compute_min (this, c); + int max_value = compute_max (this, c); + + _external_name = external; + _expr = expr; + _min_value = min_value; + _max_value = max_value; +} + +void Expr::add(const char *c) { + Expr *cost = new Expr(c); + add(cost); +} + +void Expr::add(const char *c, ArchDesc &AD) { + const Expr *e = AD.globalDefs()[c]; + if( e != NULL ) { + // use the value of 'c' defined in .ad + add(e); + } else { + Expr *cost = new Expr(c); + add(cost); + } +} + +const char *Expr::compute_external(const Expr *c1, const Expr *c2) { + const char * result = NULL; + + // Preserve use of external name which has a zero value + if( c1->_external_name != NULL ) { + sprintf( string_buffer, "%s", c1->as_string()); + if( !c2->is_zero() ) { + strcat( string_buffer, "+"); + strcat( string_buffer, c2->as_string()); + } + result = strdup(string_buffer); + } + else if( c2->_external_name != NULL ) { + if( !c1->is_zero() ) { + sprintf( string_buffer, "%s", c1->as_string()); + strcat( string_buffer, " + "); + } else { + string_buffer[0] = '\0'; + } + strcat( string_buffer, c2->_external_name ); + result = strdup(string_buffer); + } + return result; +} + +const char *Expr::compute_expr(const Expr *c1, const Expr *c2) { + if( !c1->is_zero() ) { + sprintf( string_buffer, "%s", c1->_expr); + if( !c2->is_zero() ) { + strcat( string_buffer, "+"); + strcat( string_buffer, c2->_expr); + } + } + else if( !c2->is_zero() ) { + sprintf( string_buffer, "%s", c2->_expr); + } + else { + sprintf( string_buffer, "0"); + } + char *cost = strdup(string_buffer); + + return cost; +} + +int Expr::compute_min(const Expr *c1, const Expr *c2) { + int result = c1->_min_value + c2->_min_value; + assert( result >= 0, "Invalid cost computation"); + + return result; +} + +int Expr::compute_max(const Expr *c1, const Expr *c2) { + int result = c1->_max_value + c2->_max_value; + if( result < 0 ) { // check for overflow + result = Expr::Max; + } + + return result; +} + +void Expr::print() const { + if( _external_name != NULL ) { + printf(" %s == (%s) === [%d, %d]\n", _external_name, _expr, _min_value, _max_value); + } else { + printf(" %s === [%d, %d]\n", _expr, _min_value, _max_value); + } +} + +void Expr::print_define(FILE *fp) const { + assert( _external_name != NULL, "definition does not have a name"); + assert( _min_value == _max_value, "Expect user definitions to have constant value"); + fprintf(fp, "#define %s (%s) \n", _external_name, _expr); + fprintf(fp, "// value == %d \n", _min_value); +} + +void Expr::print_assert(FILE *fp) const { + assert( _external_name != NULL, "definition does not have a name"); + assert( _min_value == _max_value, "Expect user definitions to have constant value"); + fprintf(fp, " assert( %s == %d, \"Expect (%s) to equal %d\");\n", _external_name, _min_value, _expr, _min_value); +} + +Expr *Expr::get_unknown() { + if( Expr::_unknown_expr == NULL ) { + Expr::_unknown_expr = new Expr(); + } + + return Expr::_unknown_expr; +} + +bool Expr::init_buffers() { + // Fill buffers with 0 + for( int i = 0; i < STRING_BUFFER_LENGTH; ++i ) { + external_buffer[i] = '\0'; + string_buffer[i] = '\0'; + } + + return true; +} + +bool Expr::check_buffers() { + // returns 'true' if buffer use may have overflowed + bool ok = true; + for( int i = STRING_BUFFER_LENGTH - 100; i < STRING_BUFFER_LENGTH; ++i) { + if( external_buffer[i] != '\0' || string_buffer[i] != '\0' ) { + ok = false; + assert( false, "Expr:: Buffer overflow"); + } + } + + return ok; +} + + +//------------------------------ExprDict--------------------------------------- +// Constructor +ExprDict::ExprDict( CmpKey cmp, Hash hash, Arena *arena ) + : _expr(cmp, hash, arena), _defines() { +} +ExprDict::~ExprDict() { +} + +// Return # of name-Expr pairs in dict +int ExprDict::Size(void) const { + return _expr.Size(); +} + +// define inserts the given key-value pair into the dictionary, +// and records the name in order for later output, ... +const Expr *ExprDict::define(const char *name, Expr *expr) { + const Expr *old_expr = (*this)[name]; + assert(old_expr == NULL, "Implementation does not support redefinition"); + + _expr.Insert(name, expr); + _defines.addName(name); + + return old_expr; +} + +// Insert inserts the given key-value pair into the dictionary. The prior +// value of the key is returned; NULL if the key was not previously defined. +const Expr *ExprDict::Insert(const char *name, Expr *expr) { + return (Expr*)_expr.Insert((void*)name, (void*)expr); +} + +// Finds the value of a given key; or NULL if not found. +// The dictionary is NOT changed. +const Expr *ExprDict::operator [](const char *name) const { + return (Expr*)_expr[name]; +} + +void ExprDict::print_defines(FILE *fp) { + fprintf(fp, "\n"); + const char *name = NULL; + for( _defines.reset(); (name = _defines.iter()) != NULL; ) { + const Expr *expr = (const Expr*)_expr[name]; + assert( expr != NULL, "name in ExprDict without matching Expr in dictionary"); + expr->print_define(fp); + } +} +void ExprDict::print_asserts(FILE *fp) { + fprintf(fp, "\n"); + fprintf(fp, " // Following assertions generated from definition section\n"); + const char *name = NULL; + for( _defines.reset(); (name = _defines.iter()) != NULL; ) { + const Expr *expr = (const Expr*)_expr[name]; + assert( expr != NULL, "name in ExprDict without matching Expr in dictionary"); + expr->print_assert(fp); + } +} + +// Print out the dictionary contents as key-value pairs +static void dumpekey(const void* key) { fprintf(stdout, "%s", key); } +static void dumpexpr(const void* expr) { fflush(stdout); ((Expr*)expr)->print(); } + +void ExprDict::dump() { + _expr.print(dumpekey, dumpexpr); +} + + +//------------------------------ExprDict::private------------------------------ +// Disable public use of constructor, copy-ctor, operator =, operator == +ExprDict::ExprDict( ) : _expr(cmpkey,hashkey), _defines() { + assert( false, "NotImplemented"); +} +ExprDict::ExprDict( const ExprDict & ) : _expr(cmpkey,hashkey), _defines() { + assert( false, "NotImplemented"); +} +ExprDict &ExprDict::operator =( const ExprDict &rhs) { + assert( false, "NotImplemented"); + _expr = rhs._expr; + return *this; +} +// == compares two dictionaries; they must have the same keys (their keys +// must match using CmpKey) and they must have the same values (pointer +// comparison). If so 1 is returned, if not 0 is returned. +bool ExprDict::operator ==(const ExprDict &d) const { + assert( false, "NotImplemented"); + return false; +} + + +//------------------------------Production------------------------------------- +Production::Production(const char *result, const char *constraint, const char *valid) { + initialize(); + _result = result; + _constraint = constraint; + _valid = valid; +} + +void Production::initialize() { + _result = NULL; + _constraint = NULL; + _valid = knownInvalid; + _cost_lb = Expr::get_unknown(); + _cost_ub = Expr::get_unknown(); +} + +void Production::print() { + printf("%s", (_result == NULL ? "NULL" : _result ) ); + printf("%s", (_constraint == NULL ? "NULL" : _constraint ) ); + printf("%s", (_valid == NULL ? "NULL" : _valid ) ); + _cost_lb->print(); + _cost_ub->print(); +} + + +//------------------------------ProductionState-------------------------------- +void ProductionState::initialize() { + _constraint = noConstraint; + + // reset each Production currently in the dictionary + DictI iter( &_production ); + const void *x, *y = NULL; + for( ; iter.test(); ++iter) { + x = iter._key; + y = iter._value; + Production *p = (Production*)y; + if( p != NULL ) { + p->initialize(); + } + } +} + +Production *ProductionState::getProduction(const char *result) { + Production *p = (Production *)_production[result]; + if( p == NULL ) { + p = new Production(result, _constraint, knownInvalid); + _production.Insert(result, p); + } + + return p; +} + +void ProductionState::set_constraint(const char *constraint) { + _constraint = constraint; +} + +const char *ProductionState::valid(const char *result) { + return getProduction(result)->valid(); +} + +void ProductionState::set_valid(const char *result) { + Production *p = getProduction(result); + + // Update valid as allowed by current constraints + if( _constraint == noConstraint ) { + p->_valid = knownValid; + } else { + if( p->_valid != knownValid ) { + p->_valid = unknownValid; + } + } +} + +Expr *ProductionState::cost_lb(const char *result) { + return getProduction(result)->cost_lb(); +} + +Expr *ProductionState::cost_ub(const char *result) { + return getProduction(result)->cost_ub(); +} + +void ProductionState::set_cost_bounds(const char *result, const Expr *cost, bool has_state_check, bool has_cost_check) { + Production *p = getProduction(result); + + if( p->_valid == knownInvalid ) { + // Our cost bounds are not unknown, just not defined. + p->_cost_lb = cost->clone(); + p->_cost_ub = cost->clone(); + } else if (has_state_check || _constraint != noConstraint) { + // The production is protected by a condition, so + // the cost bounds may expand. + // _cost_lb = min(cost, _cost_lb) + if( cost->less_than_or_equal(p->_cost_lb) ) { + p->_cost_lb = cost->clone(); + } + // _cost_ub = max(cost, _cost_ub) + if( p->_cost_ub->less_than_or_equal(cost) ) { + p->_cost_ub = cost->clone(); + } + } else if (has_cost_check) { + // The production has no condition check, but does + // have a cost check that could reduce the upper + // and/or lower bound. + // _cost_lb = min(cost, _cost_lb) + if( cost->less_than_or_equal(p->_cost_lb) ) { + p->_cost_lb = cost->clone(); + } + // _cost_ub = min(cost, _cost_ub) + if( cost->less_than_or_equal(p->_cost_ub) ) { + p->_cost_ub = cost->clone(); + } + } else { + // The costs are unconditionally set. + p->_cost_lb = cost->clone(); + p->_cost_ub = cost->clone(); + } + +} + +// Print out the dictionary contents as key-value pairs +static void print_key (const void* key) { fprintf(stdout, "%s", key); } +static void print_production(const void* production) { fflush(stdout); ((Production*)production)->print(); } + +void ProductionState::print() { + _production.print(print_key, print_production); +} diff --git a/hotspot/src/share/vm/adlc/dict2.cpp b/hotspot/src/share/vm/adlc/dict2.cpp new file mode 100644 index 00000000000..640fad4af11 --- /dev/null +++ b/hotspot/src/share/vm/adlc/dict2.cpp @@ -0,0 +1,353 @@ +/* + * Copyright 1998-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Dictionaries - An Abstract Data Type + +#include "adlc.hpp" + +// #include "dict.hpp" + + +//------------------------------data----------------------------------------- +// String hash tables +#define MAXID 20 +static char initflag = 0; // True after 1st initialization +static char shft[MAXID] = {1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6}; +static short xsum[MAXID + 1]; + +//------------------------------bucket--------------------------------------- +class bucket { +public: + int _cnt, _max; // Size of bucket + const void **_keyvals; // Array of keys and values +}; + +//------------------------------Dict----------------------------------------- +// The dictionary is kept has a hash table. The hash table is a even power +// of two, for nice modulo operations. Each bucket in the hash table points +// to a linear list of key-value pairs; each key & value is just a (void *). +// The list starts with a count. A hash lookup finds the list head, then a +// simple linear scan finds the key. If the table gets too full, it's +// doubled in size; the total amount of EXTRA times all hash functions are +// computed for the doubling is no more than the current size - thus the +// doubling in size costs no more than a constant factor in speed. +Dict::Dict(CmpKey initcmp, Hash inithash) : _hash(inithash), _cmp(initcmp), _arena(NULL) { + init(); +} + +Dict::Dict(CmpKey initcmp, Hash inithash, Arena *arena) : _hash(inithash), _cmp(initcmp), _arena(arena) { + init(); +} + +void Dict::init() { + int i; + + // Precompute table of null character hashes + if( !initflag ) { // Not initializated yet? + xsum[0] = (1<Amalloc_4(sizeof(bucket)*_size); + memset(_bin,0,sizeof(bucket)*_size); +} + +//------------------------------~Dict------------------------------------------ +// Delete an existing dictionary. +Dict::~Dict() { +} + +//------------------------------Clear---------------------------------------- +// Zap to empty; ready for re-use +void Dict::Clear() { + _cnt = 0; // Empty contents + for( int i=0; i<_size; i++ ) + _bin[i]._cnt = 0; // Empty buckets, but leave allocated + // Leave _size & _bin alone, under the assumption that dictionary will + // grow to this size again. +} + +//------------------------------doubhash--------------------------------------- +// Double hash table size. If can't do so, just suffer. If can, then run +// thru old hash table, moving things to new table. Note that since hash +// table doubled, exactly 1 new bit is exposed in the mask - so everything +// in the old table ends up on 1 of two lists in the new table; a hi and a +// lo list depending on the value of the bit. +void Dict::doubhash(void) { + int oldsize = _size; + _size <<= 1; // Double in size + _bin = (bucket*)_arena->Arealloc( _bin, sizeof(bucket)*oldsize, sizeof(bucket)*_size ); + memset( &_bin[oldsize], 0, oldsize*sizeof(bucket) ); + // Rehash things to spread into new table + for( int i=0; i < oldsize; i++) { // For complete OLD table do + bucket *b = &_bin[i]; // Handy shortcut for _bin[i] + if( !b->_keyvals ) continue; // Skip empties fast + + bucket *nb = &_bin[i+oldsize]; // New bucket shortcut + int j = b->_max; // Trim new bucket to nearest power of 2 + while( j > b->_cnt ) j >>= 1; // above old bucket _cnt + if( !j ) j = 1; // Handle zero-sized buckets + nb->_max = j<<1; + // Allocate worst case space for key-value pairs + nb->_keyvals = (const void**)_arena->Amalloc_4( sizeof(void *)*nb->_max*2 ); + int nbcnt = 0; + + for( j=0; j_cnt; j++ ) { // Rehash all keys in this bucket + const void *key = b->_keyvals[j+j]; + if( (_hash( key ) & (_size-1)) != i ) { // Moving to hi bucket? + nb->_keyvals[nbcnt+nbcnt] = key; + nb->_keyvals[nbcnt+nbcnt+1] = b->_keyvals[j+j+1]; + nb->_cnt = nbcnt = nbcnt+1; + b->_cnt--; // Remove key/value from lo bucket + b->_keyvals[j+j ] = b->_keyvals[b->_cnt+b->_cnt ]; + b->_keyvals[j+j+1] = b->_keyvals[b->_cnt+b->_cnt+1]; + j--; // Hash compacted element also + } + } // End of for all key-value pairs in bucket + } // End of for all buckets + + +} + +//------------------------------Dict----------------------------------------- +// Deep copy a dictionary. +Dict::Dict( const Dict &d ) : _size(d._size), _cnt(d._cnt), _hash(d._hash),_cmp(d._cmp), _arena(d._arena) { + _bin = (bucket*)_arena->Amalloc_4(sizeof(bucket)*_size); + memcpy( _bin, d._bin, sizeof(bucket)*_size ); + for( int i=0; i<_size; i++ ) { + if( !_bin[i]._keyvals ) continue; + _bin[i]._keyvals=(const void**)_arena->Amalloc_4( sizeof(void *)*_bin[i]._max*2); + memcpy( _bin[i]._keyvals, d._bin[i]._keyvals,_bin[i]._cnt*2*sizeof(void*)); + } +} + +//------------------------------Dict----------------------------------------- +// Deep copy a dictionary. +Dict &Dict::operator =( const Dict &d ) { + if( _size < d._size ) { // If must have more buckets + _arena = d._arena; + _bin = (bucket*)_arena->Arealloc( _bin, sizeof(bucket)*_size, sizeof(bucket)*d._size ); + memset( &_bin[_size], 0, (d._size-_size)*sizeof(bucket) ); + _size = d._size; + } + for( int i=0; i<_size; i++ ) // All buckets are empty + _bin[i]._cnt = 0; // But leave bucket allocations alone + _cnt = d._cnt; + *(Hash*)(&_hash) = d._hash; + *(CmpKey*)(&_cmp) = d._cmp; + for(int k=0; k<_size; k++ ) { + bucket *b = &d._bin[k]; // Shortcut to source bucket + for( int j=0; j_cnt; j++ ) + Insert( b->_keyvals[j+j], b->_keyvals[j+j+1] ); + } + return *this; +} + +//------------------------------Insert--------------------------------------- +// Insert or replace a key/value pair in the given dictionary. If the +// dictionary is too full, it's size is doubled. The prior value being +// replaced is returned (NULL if this is a 1st insertion of that key). If +// an old value is found, it's swapped with the prior key-value pair on the +// list. This moves a commonly searched-for value towards the list head. +const void *Dict::Insert(const void *key, const void *val) { + int hash = _hash( key ); // Get hash key + int i = hash & (_size-1); // Get hash key, corrected for size + bucket *b = &_bin[i]; // Handy shortcut + for( int j=0; j_cnt; j++ ) + if( !_cmp(key,b->_keyvals[j+j]) ) { + const void *prior = b->_keyvals[j+j+1]; + b->_keyvals[j+j ] = key; // Insert current key-value + b->_keyvals[j+j+1] = val; + return prior; // Return prior + } + + if( ++_cnt > _size ) { // Hash table is full + doubhash(); // Grow whole table if too full + i = hash & (_size-1); // Rehash + b = &_bin[i]; // Handy shortcut + } + if( b->_cnt == b->_max ) { // Must grow bucket? + if( !b->_keyvals ) { + b->_max = 2; // Initial bucket size + b->_keyvals = (const void**)_arena->Amalloc_4( sizeof(void *)*b->_max*2 ); + } else { + b->_keyvals = (const void**)_arena->Arealloc( b->_keyvals, sizeof(void *)*b->_max*2, sizeof(void *)*b->_max*4 ); + b->_max <<= 1; // Double bucket + } + } + b->_keyvals[b->_cnt+b->_cnt ] = key; + b->_keyvals[b->_cnt+b->_cnt+1] = val; + b->_cnt++; + return NULL; // Nothing found prior +} + +//------------------------------Delete--------------------------------------- +// Find & remove a value from dictionary. Return old value. +const void *Dict::Delete(void *key) { + int i = _hash( key ) & (_size-1); // Get hash key, corrected for size + bucket *b = &_bin[i]; // Handy shortcut + for( int j=0; j_cnt; j++ ) + if( !_cmp(key,b->_keyvals[j+j]) ) { + const void *prior = b->_keyvals[j+j+1]; + b->_cnt--; // Remove key/value from lo bucket + b->_keyvals[j+j ] = b->_keyvals[b->_cnt+b->_cnt ]; + b->_keyvals[j+j+1] = b->_keyvals[b->_cnt+b->_cnt+1]; + _cnt--; // One less thing in table + return prior; + } + return NULL; +} + +//------------------------------FindDict------------------------------------- +// Find a key-value pair in the given dictionary. If not found, return NULL. +// If found, move key-value pair towards head of list. +const void *Dict::operator [](const void *key) const { + int i = _hash( key ) & (_size-1); // Get hash key, corrected for size + bucket *b = &_bin[i]; // Handy shortcut + for( int j=0; j_cnt; j++ ) + if( !_cmp(key,b->_keyvals[j+j]) ) + return b->_keyvals[j+j+1]; + return NULL; +} + +//------------------------------CmpDict-------------------------------------- +// CmpDict compares two dictionaries; they must have the same keys (their +// keys must match using CmpKey) and they must have the same values (pointer +// comparison). If so 1 is returned, if not 0 is returned. +int Dict::operator ==(const Dict &d2) const { + if( _cnt != d2._cnt ) return 0; + if( _hash != d2._hash ) return 0; + if( _cmp != d2._cmp ) return 0; + for( int i=0; i < _size; i++) { // For complete hash table do + bucket *b = &_bin[i]; // Handy shortcut + if( b->_cnt != d2._bin[i]._cnt ) return 0; + if( memcmp(b->_keyvals, d2._bin[i]._keyvals, b->_cnt*2*sizeof(void*) ) ) + return 0; // Key-value pairs must match + } + return 1; // All match, is OK +} + + +//------------------------------print---------------------------------------- +static void printvoid(const void* x) { printf("%p", x); } +void Dict::print() { + print(printvoid, printvoid); +} +void Dict::print(PrintKeyOrValue print_key, PrintKeyOrValue print_value) { + for( int i=0; i < _size; i++) { // For complete hash table do + bucket *b = &_bin[i]; // Handy shortcut + for( int j=0; j_cnt; j++ ) { + print_key( b->_keyvals[j+j ]); + printf(" -> "); + print_value(b->_keyvals[j+j+1]); + printf("\n"); + } + } +} + +//------------------------------Hashing Functions---------------------------- +// Convert string to hash key. This algorithm implements a universal hash +// function with the multipliers frozen (ok, so it's not universal). The +// multipliers (and allowable characters) are all odd, so the resultant sum +// is odd - guarenteed not divisible by any power of two, so the hash tables +// can be any power of two with good results. Also, I choose multipliers +// that have only 2 bits set (the low is always set to be odd) so +// multiplication requires only shifts and adds. Characters are required to +// be in the range 0-127 (I double & add 1 to force oddness). Keys are +// limited to MAXID characters in length. Experimental evidence on 150K of +// C text shows excellent spreading of values for any size hash table. +int hashstr(const void *t) { + register char c, k = 0; + register int sum = 0; + register const char *s = (const char *)t; + + while( ((c = s[k]) != '\0') && (k < MAXID-1) ) { // Get characters till nul + c = (c<<1)+1; // Characters are always odd! + sum += c + (c<> 1); // Hash key, un-modulo'd table size +} + +//------------------------------hashptr-------------------------------------- +// Slimey cheap hash function; no guarenteed performance. Better than the +// default for pointers, especially on MS-DOS machines. +int hashptr(const void *key) { +#ifdef __TURBOC__ + return (int)((intptr_t)key >> 16); +#else // __TURBOC__ + return (int)((intptr_t)key >> 2); +#endif +} + +// Slimey cheap hash function; no guarenteed performance. +int hashkey(const void *key) { + return (int)((intptr_t)key); +} + +//------------------------------Key Comparator Functions--------------------- +int cmpstr(const void *k1, const void *k2) { + return strcmp((const char *)k1,(const char *)k2); +} + +// Slimey cheap key comparator. +int cmpkey(const void *key1, const void *key2) { + return (int)((intptr_t)key1 - (intptr_t)key2); +} + +//============================================================================= +//------------------------------reset------------------------------------------ +// Create an iterator and initialize the first variables. +void DictI::reset( const Dict *dict ) { + _d = dict; // The dictionary + _i = (int)-1; // Before the first bin + _j = 0; // Nothing left in the current bin + ++(*this); // Step to first real value +} + +//------------------------------next------------------------------------------- +// Find the next key-value pair in the dictionary, or return a NULL key and +// value. +void DictI::operator ++(void) { + if( _j-- ) { // Still working in current bin? + _key = _d->_bin[_i]._keyvals[_j+_j]; + _value = _d->_bin[_i]._keyvals[_j+_j+1]; + return; + } + + while( ++_i < _d->_size ) { // Else scan for non-zero bucket + _j = _d->_bin[_i]._cnt; + if( !_j ) continue; + _j--; + _key = _d->_bin[_i]._keyvals[_j+_j]; + _value = _d->_bin[_i]._keyvals[_j+_j+1]; + return; + } + _key = _value = NULL; +} diff --git a/hotspot/src/share/vm/adlc/dict2.hpp b/hotspot/src/share/vm/adlc/dict2.hpp new file mode 100644 index 00000000000..41a1b19d167 --- /dev/null +++ b/hotspot/src/share/vm/adlc/dict2.hpp @@ -0,0 +1,120 @@ +/* + * Copyright 1998-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _DICT_ +#define _DICT_ +// Dictionaries - An Abstract Data Type + + +class Dict; + +// These dictionaries define a key-value mapping. They can be inserted to, +// searched or deleted from. They grow and shrink as needed. The key is a +// pointer to something (or anything which can be stored in a pointer). A +// key comparison routine determines if two keys are equal or not. A hash +// function can be provided; if it's not provided the key itself is used +// instead. A nice string hash function is included. +typedef int (*CmpKey)(const void *key1, const void *key2); +typedef int (*Hash)(const void *key); +typedef void (*PrintKeyOrValue)(const void *key_or_value); +typedef void (*FuncDict)(const void *key, const void *val, Dict *d); + +class Dict { // Dictionary structure + private: + class Arena *_arena; // Where to draw storage from + class bucket *_bin; // Hash table is array of buckets + int _size; // Size (# of slots) in hash table + int _cnt; // Number of key-value pairs in hash table + const Hash _hash; // Hashing function + const CmpKey _cmp; // Key comparison function + void doubhash( void ); // Double hash table size + + public: + friend class DictI; // Friendly iterator function + + // cmp is a key comparision routine. hash is a routine to hash a key. + Dict( CmpKey cmp, Hash hash ); + Dict( CmpKey cmp, Hash hash, Arena *arena ); + void init(); + ~Dict(); + + Dict( const Dict & ); // Deep-copy guts + Dict &operator =( const Dict & ); + + // Zap to empty; ready for re-use + void Clear(); + + // Return # of key-value pairs in dict + int Size(void) const { return _cnt; } + + // Insert inserts the given key-value pair into the dictionary. The prior + // value of the key is returned; NULL if the key was not previously defined. + const void *Insert(const void *key, const void *val); // A new key-value + const void *Delete(void *key); // Delete & return old + + // Find finds the value of a given key; or NULL if not found. + // The dictionary is NOT changed. + const void *operator [](const void *key) const; // Do a lookup + + // == compares two dictionaries; they must have the same keys (their keys + // must match using CmpKey) and they must have the same values (pointer + // comparison). If so 1 is returned, if not 0 is returned. + int operator ==(const Dict &d) const; // Compare dictionaries for equal + + // Print out the dictionary contents as key-value pairs + void print(); + void print(PrintKeyOrValue print_key, PrintKeyOrValue print_value); +}; + +// Hashing functions +int hashstr(const void *s); // Nice string hash +// Slimey cheap hash function; no guarenteed performance. Better than the +// default for pointers, especially on MS-DOS machines. +int hashptr(const void *key); +// Slimey cheap hash function; no guarenteed performance. +int hashkey(const void *key); + +// Key comparators +int cmpstr(const void *k1, const void *k2); +// Slimey cheap key comparator. +int cmpkey(const void *key1, const void *key2); + +//------------------------------Iteration-------------------------------------- +// The class of dictionary iterators. Fails in the presences of modifications +// to the dictionary during iteration (including searches). +// Usage: for( DictI i(dict); i.test(); ++i ) { body = i.key; body = i.value;} +class DictI { + private: + const Dict *_d; // Dictionary being iterated over + int _i; // Counter over the bins + int _j; // Counter inside each bin + public: + const void *_key, *_value; // Easy access to the key-value pair + DictI( const Dict *d ) {reset(d);}; // Create a new iterator + void reset( const Dict *dict ); // Reset existing iterator + void operator ++(void); // Increment iterator + int test(void) { return _i<_d->_size;} // Test for end of iteration +}; + +#endif // _DICT_ diff --git a/hotspot/src/share/vm/adlc/filebuff.cpp b/hotspot/src/share/vm/adlc/filebuff.cpp new file mode 100644 index 00000000000..502354edcf0 --- /dev/null +++ b/hotspot/src/share/vm/adlc/filebuff.cpp @@ -0,0 +1,296 @@ +/* + * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// FILEBUFF.CPP - Routines for handling a parser file buffer +#include "adlc.hpp" + +//------------------------------FileBuff--------------------------------------- +// Create a new parsing buffer +FileBuff::FileBuff( BufferedFile *fptr, ArchDesc& archDesc) : _fp(fptr), _AD(archDesc) { + _err = fseek(_fp->_fp, 0, SEEK_END); // Seek to end of file + if (_err) { + file_error(SEMERR, 0, "File seek error reading input file"); + exit(1); // Exit on seek error + } + _filepos = ftell(_fp->_fp); // Find offset of end of file + _bufferSize = _filepos + 5; // Filepos points to last char, so add padding + _err = fseek(_fp->_fp, 0, SEEK_SET); // Reset to beginning of file + if (_err) { + file_error(SEMERR, 0, "File seek error reading input file\n"); + exit(1); // Exit on seek error + } + _filepos = ftell(_fp->_fp); // Reset current file position + + _bigbuf = new char[_bufferSize]; // Create buffer to hold text for parser + if( !_bigbuf ) { + file_error(SEMERR, 0, "Buffer allocation failed\n"); + exit(1); // Exit on allocation failure + } + *_bigbuf = '\n'; // Lead with a sentinal newline + _buf = _bigbuf+1; // Skip sentinal + _bufmax = _buf; // Buffer is empty + _bufeol = _bigbuf; // _bufeol points at sentinal + _filepos = -1; // filepos is in sync with _bufeol + _bufoff = _offset = 0L; // Offset at file start + + _bufmax += fread(_buf, 1, _bufferSize-2, _fp->_fp); // Fill buffer & set end value + if (_bufmax == _buf) { + file_error(SEMERR, 0, "File read error, no input read\n"); + exit(1); // Exit on read error + } + *_bufmax = '\n'; // End with a sentinal new-line + *(_bufmax+1) = '\0'; // Then end with a sentinal NULL +} + +//------------------------------~FileBuff-------------------------------------- +// Nuke the FileBuff +FileBuff::~FileBuff() { + delete _bigbuf; +} + +//------------------------------get_line---------------------------------------- +char *FileBuff::get_line(void) { + char *retval; + + // Check for end of file & return NULL + if (_bufeol >= _bufmax) return NULL; + + retval = ++_bufeol; // return character following end of previous line + if (*retval == '\0') return NULL; // Check for EOF sentinal + // Search for newline character which must end each line + for(_filepos++; *_bufeol != '\n'; _bufeol++) + _filepos++; // keep filepos in sync with _bufeol + // _bufeol & filepos point at end of current line, so return pointer to start + return retval; +} + +//------------------------------FileBuffRegion--------------------------------- +// Create a new region in a FileBuff. +FileBuffRegion::FileBuffRegion( FileBuff* bufr, int soln, int ln, + int off, int len) +: _bfr(bufr), _sol(soln), _line(ln), _offset(off), _length(len) { + _next = NULL; // No chained regions +} + +//------------------------------~FileBuffRegion-------------------------------- +// Delete the entire linked list of buffer regions. +FileBuffRegion::~FileBuffRegion() { + if( _next ) delete _next; +} + +//------------------------------copy------------------------------------------- +// Deep copy a FileBuffRegion +FileBuffRegion *FileBuffRegion::copy() { + if( !this ) return NULL; // The empty buffer region + FileBuffRegion *br = new FileBuffRegion(_bfr,_sol,_line,_offset,_length); + if( _next ) br->_next = _next->copy(); + return br; +} + +//------------------------------merge------------------------------------------ +// Merge another buffer region into this buffer region. Make overlapping areas +// become a single region. Remove (delete) the input FileBuffRegion. +// Since the buffer regions are sorted by file offset, this is a varient of a +// "sorted-merge" running in linear time. +FileBuffRegion *FileBuffRegion::merge( FileBuffRegion *br ) { + if( !br ) return this; // Merging nothing + if( !this ) return br; // Merging into nothing + + assert( _bfr == br->_bfr, "" ); // Check for pointer-equivalent buffers + + if( _offset < br->_offset ) { // "this" starts before "br" + if( _offset+_length < br->_offset ) { // "this" ends before "br" + if( _next ) _next->merge( br ); // Merge with remainder of list + else _next = br; // No more in this list; just append. + } else { // Regions overlap. + int l = br->_offset + br->_length - _offset; + if( l > _length ) _length = l; // Pick larger region + FileBuffRegion *nr = br->_next; // Get rest of region + br->_next = NULL; // Remove indication of rest of region + delete br; // Delete this region (it's been subsumed). + if( nr ) merge( nr ); // Merge with rest of region + } // End of if regions overlap or not. + } else { // "this" starts after "br" + if( br->_offset+br->_length < _offset ) { // "br" ends before "this" + FileBuffRegion *nr = new FileBuffRegion(_bfr,_sol,_line,_offset,_length); + nr->_next = _next; // Structure copy "this" guy to "nr" + *this = *br; // Structure copy "br" over "this". + br->_next = NULL; // Remove indication of rest of region + delete br; // Delete this region (it's been copied) + merge( nr ); // Finish merging + } else { // Regions overlap. + int l = _offset + _length - br->_offset; + if( l > _length ) _length = l; // Pick larger region + _offset = br->_offset; // Start with earlier region + _sol = br->_sol; // Also use earlier line start + _line = br->_line; // Also use earlier line + FileBuffRegion *nr = br->_next; // Get rest of region + br->_next = NULL; // Remove indication of rest of region + delete br; // Delete this region (it's been subsumed). + if( nr ) merge( nr ); // Merge with rest of region + } // End of if regions overlap or not. + } + return this; +} + +//------------------------------expandtab-------------------------------------- +static int expandtab( ostream &os, int off, char c, char fill1, char fill2 ) { + if( c == '\t' ) { // Tab? + do os << fill1; // Expand the tab; Output space + while( (++off) & 7 ); // Expand to tab stop + } else { // Normal character + os << fill2; // Display normal character + off++; // Increment "cursor" offset + } + return off; +} + +//------------------------------printline-------------------------------------- +// Print and highlite a region of a line. Return the amount of highliting left +// to do (i.e. highlite length minus length of line). +static int printline( ostream& os, const char *fname, int line, + const char *_sol, int skip, int len ) { + + // Display the entire tab-expanded line + os << fname << ":" << line << ": "; + const char *t = strchr(_sol,'\n')+1; // End of line + int off = 0; // Cursor offset for tab expansion + const char *s = _sol; // Nice string pointer + while( t-s ) { // Display whole line + char c = *s++; // Get next character to display + off = expandtab(os,off,c,' ',c); + } + + // Display the tab-expanded skippings before underlining. + os << fname << ":" << line << ": "; + off = 0; // Cursor offset for tab expansion + s = _sol; // Restart string pointer + + // Start underlining. + if( skip != -1 ) { // The no-start-indicating flag + const char *u = _sol+skip; // Amount to skip + while( u-s ) // Display skipped part + off = expandtab(os,off,*s++,' ',' '); + os << '^'; // Start region + off++; // Moved cursor + len--; // 1 less char to do + if( *s++ == '\t' ) // Starting character is a tab? + off = expandtab(os,off,'\t','-','^'); + } + + // Long region doesn't end on this line + int llen = (int)(t-s); // Length of line, minus what's already done + if( len > llen ) { // Doing entire rest of line? + while( t-s ) // Display rest of line + off = expandtab(os,off,*s++,'-','-'); + os << '\n'; // EOL + return len-llen; // Return what's not yet done. + } + + // Region does end on this line. This code fails subtly if the region ends + // in a tab character. + int i; + for( i=1; iprint(os); // Print region + brp = brp->_next; // Chain to next + } + return os; // Return final stream +} + +//------------------------------print------------------------------------------ +// Print the FileBuffRegion to a stream. FileBuffRegions are printed with the +// filename and line number to the left, and complete text lines to the right. +// Selected portions (portions of a line actually in the FileBuffRegion are +// underlined. Ellipses are used for long multi-line regions. +//void FileBuffRegion::print( std::ostream& os ) { +void FileBuffRegion::print( ostream& os ) { + if( !this ) return; // Nothing to print + char *s = _bfr->get_line(); + int skip = (int)(_offset - _sol); // Amount to skip to start of data + int len = printline( os, _bfr->_fp->_name, _line, s, skip, _length ); + + if( !len ) return; // All done; exit + + // Here we require at least 2 lines + int off1 = _length - len + skip; // Length of line 1 + int off2 = off1 + _sol; // Offset to start of line 2 + char *s2 = _bfr->get_line(); // Start of line 2 + char *s3 = strchr( s2, '\n' )+1; // Start of line 3 (unread) + if( len <= (s3-s2) ) { // It all fits on the next line + printline( os, _bfr->_fp->_name, _line+1, s2, -1, len ); // Print&underline + return; + } + + // Here we require at least 3 lines + int off3 = off2 + (int)(s3-s2); // Offset to start of line 3 + s3 = _bfr->get_line(); // Start of line 3 (read) + const char *s4 = strchr( s3, '\n' )+1;// Start of line 4 (unread) + if( len < (s4-s3) ) { // It all fits on the next 2 lines + s2 = _bfr->get_line(); + len = printline( os, _bfr->_fp->_name, _line+1, s2, -1, len ); // Line 2 + s3 = _bfr->get_line(); + printline( os, _bfr->_fp->_name, _line+2, s3, -1, len ); // Line 3 + return; + } + + // Here we require at least 4 lines. + // Print only the 1st and last line, with ellipses in middle. + os << "...\n"; // The ellipses + int cline = _line+1; // Skipped 2 lines + do { // Do until find last line + len -= (int)(s3-s2); // Remove length of line + cline++; // Next line + s2 = _bfr->get_line(); // Get next line from end of this line + s3 = strchr( s2, '\n' ) + 1;// Get end of next line + } while( len > (s3-s2) ); // Repeat until last line + printline( os, _bfr->_fp->_name, cline, s2, -1, len ); // Print & underline +} + +//------------------------------file_error------------------------------------- +void FileBuff::file_error(int flag, int linenum, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + switch (flag) { + case 0: _AD._warnings += _AD.emit_msg(0, flag, linenum, fmt, args); + case 1: _AD._syntax_errs += _AD.emit_msg(0, flag, linenum, fmt, args); + case 2: _AD._semantic_errs += _AD.emit_msg(0, flag, linenum, fmt, args); + default: assert(0, ""); break; + } + va_end(args); + _AD._no_output = 1; +} diff --git a/hotspot/src/share/vm/adlc/filebuff.hpp b/hotspot/src/share/vm/adlc/filebuff.hpp new file mode 100644 index 00000000000..fa84e09bd2a --- /dev/null +++ b/hotspot/src/share/vm/adlc/filebuff.hpp @@ -0,0 +1,103 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// FILEBUFF.HPP - Definitions for parser file buffering routines + +#if _MSC_VER >= 1300 // Visual C++ 7.0 or later +#include +#else +#include +#endif + +// STRUCTURE FOR HANDLING INPUT AND OUTPUT FILES +typedef struct { + const char *_name; + FILE *_fp; +} BufferedFile; + +class ArchDesc; + +//------------------------------FileBuff-------------------------------------- +// This class defines a nicely behaved buffer of text. Entire file of text +// is read into buffer at creation, with sentinals at start and end. +class FileBuff { + friend class FileBuffRegion; + private: + long _bufferSize; // Size of text holding buffer. + long _offset; // Expected filepointer offset. + long _bufoff; // Start of buffer file offset + + char *_buf; // The buffer itself. + char *_bigbuf; // The buffer plus sentinals; actual heap area + char *_bufmax; // A pointer to the buffer end sentinal + char *_bufeol; // A pointer to the last complete line end + + int _err; // Error flag for file seek/read operations + long _filepos; // Current offset from start of file + + ArchDesc& _AD; // Reference to Architecture Description + + // Error reporting function + void file_error(int flag, int linenum, const char *fmt, ...); + + public: + const BufferedFile *_fp; // File to be buffered + + FileBuff(BufferedFile *fp, ArchDesc& archDesc); // Constructor + ~FileBuff(); // Destructor + + // This returns a pointer to the start of the current line in the buffer, + // and increments bufeol and filepos to point at the end of that line. + char *get_line(void); + + // This converts a pointer into the buffer to a file offset. It only works + // when the pointer is valid (i.e. just obtained from getline()). + int getoff(const char *s) { return _bufoff+(int)(s-_buf); } +}; + +//------------------------------FileBuffRegion--------------------------------- +// A buffer region is really a region of some file, specified as a linked list +// of offsets and lengths. These regions can be merged; overlapping regions +// will coalesce. +class FileBuffRegion { + public: // Workaround dev-studio friend/private bug + FileBuffRegion *_next; // Linked list of regions sorted by offset. + private: + FileBuff *_bfr; // The Buffer of the file + int _offset, _length; // The file area + int _sol; // Start of line where the file area starts + int _line; // First line of region + + public: + FileBuffRegion(FileBuff*, int sol, int line, int offset, int len); + ~FileBuffRegion(); + + FileBuffRegion *copy(); // Deep copy + FileBuffRegion *merge(FileBuffRegion*); // Merge 2 regions; delete input + +// void print(std::ostream&); +// friend std::ostream& operator<< (std::ostream&, FileBuffRegion&); + void print(ostream&); + friend ostream& operator<< (ostream&, FileBuffRegion&); +}; diff --git a/hotspot/src/share/vm/adlc/forms.cpp b/hotspot/src/share/vm/adlc/forms.cpp new file mode 100644 index 00000000000..d51c7d1f95a --- /dev/null +++ b/hotspot/src/share/vm/adlc/forms.cpp @@ -0,0 +1,385 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// FORMS.CPP - Definitions for ADL Parser Generic & Utility Forms Classes +#include "adlc.hpp" + +//------------------------------Static Initializers---------------------------- +// allocate arena used by forms +Arena *Form::arena = Form::generate_arena(); // = Form::generate_arena(); +Arena *Form::generate_arena() { + return (new Arena); +} + +//------------------------------NameList--------------------------------------- +// reserved user-defined string +const char *NameList::_signal = "$$SIGNAL$$"; + +// Constructor and Destructor +NameList::NameList() : _cur(0), _max(4), _iter(0), _justReset(true) { + _names = (const char**)malloc(_max*sizeof(char*)); +} +NameList::~NameList() { + // The following free is a double-free, and crashes the program: + //free(_names); // not owner of strings +} + +void NameList::addName(const char *name) { + if (_cur == _max) _names =(const char**)realloc(_names,(_max *=2)*sizeof(char*)); + _names[_cur++] = name; +} + +void NameList::add_signal() { + addName( _signal ); +} +void NameList::clear() { + _cur = 0; + _iter = 0; + _justReset = true; + // _max = 4; Already allocated +} + +int NameList::count() const { return _cur; } + +void NameList::reset() { _iter = 0; _justReset = true;} +const char *NameList::iter() { + if (_justReset) {_justReset=false; return (_iter < _cur ? _names[_iter] : NULL);} + else return (_iter <_cur-1 ? _names[++_iter] : NULL); +} +const char *NameList::current() { return (_iter < _cur ? _names[_iter] : NULL); } + +// Return 'true' if current entry is signal +bool NameList::current_is_signal() { + const char *entry = current(); + return is_signal(entry); +} + +// Return true if entry is a signal +bool NameList::is_signal(const char *entry) { + return ( (strcmp(entry,NameList::_signal) == 0) ? true : false); +} + +// Search for a name in the list +bool NameList::search(const char *name) { + const char *entry; + for(reset(); (entry = iter()) != NULL; ) { + if(!strcmp(entry,name)) return true; + } + return false; +} + +// Return index of name in list +int NameList::index(const char *name) { + int cnt = 0; + const char *entry; + for(reset(); (entry = iter()) != NULL; ) { + if(!strcmp(entry,name)) return cnt; + cnt++; + } + return Not_in_list; +} + +// Return name at index in list +const char *NameList::name(intptr_t index) { + return ( index < _cur ? _names[index] : NULL); +} + +void NameList::dump() { output(stderr); } + +void NameList::output(FILE *fp) { + fprintf(fp, "\n"); + + // Run iteration over all entries, independent of position of iterator. + const char *name = NULL; + int iter = 0; + bool justReset = true; + + while( ( name = (justReset ? + (justReset=false, (iter < _cur ? _names[iter] : NULL)) : + (iter < _cur-1 ? _names[++iter] : NULL)) ) + != NULL ) { + fprintf( fp, " %s,\n", name); + } + fprintf(fp, "\n"); +} + +//------------------------------NameAndList------------------------------------ +// Storage for a name and an associated list of names +NameAndList::NameAndList(char *name) : _name(name) { +} +NameAndList::~NameAndList() { +} + +// Add to entries in list +void NameAndList::add_entry(const char *entry) { + _list.addName(entry); +} + +// Access the name and its associated list. +const char *NameAndList::name() const { return _name; } +void NameAndList::reset() { _list.reset(); } +const char *NameAndList::iter() { return _list.iter(); } + +// Return the "index" entry in the list, zero-based +const char *NameAndList::operator[](int index) { + assert( index >= 0, "Internal Error(): index less than 0."); + + _list.reset(); + const char *entry = _list.iter(); + // Iterate further if it isn't at index 0. + for ( int position = 0; position != index; ++position ) { + entry = _list.iter(); + } + + return entry; +} + + +void NameAndList::dump() { output(stderr); } +void NameAndList::output(FILE *fp) { + fprintf(fp, "\n"); + + // Output the Name + fprintf(fp, "Name == %s", (_name ? _name : "") ); + + // Output the associated list of names + const char *name; + fprintf(fp, " ("); + for (reset(); (name = iter()) != NULL;) { + fprintf(fp, " %s,\n", name); + } + fprintf(fp, ")"); + fprintf(fp, "\n"); +} + +//------------------------------Form------------------------------------------- +OpClassForm *Form::is_opclass() const { + return NULL; +} + +OperandForm *Form::is_operand() const { + return NULL; +} + +InstructForm *Form::is_instruction() const { + return NULL; +} + +MachNodeForm *Form::is_machnode() const { + return NULL; +} + +AttributeForm *Form::is_attribute() const { + return NULL; +} + +Effect *Form::is_effect() const { + return NULL; +} + +ResourceForm *Form::is_resource() const { + return NULL; +} + +PipeClassForm *Form::is_pipeclass() const { + return NULL; +} + +Form::DataType Form::ideal_to_const_type(const char *name) const { + if( name == NULL ) { return Form::none; } + + if (strcmp(name,"ConI")==0) return Form::idealI; + if (strcmp(name,"ConP")==0) return Form::idealP; + if (strcmp(name,"ConL")==0) return Form::idealL; + if (strcmp(name,"ConF")==0) return Form::idealF; + if (strcmp(name,"ConD")==0) return Form::idealD; + if (strcmp(name,"Bool")==0) return Form::idealI; + + return Form::none; +} + +Form::DataType Form::ideal_to_sReg_type(const char *name) const { + if( name == NULL ) { return Form::none; } + + if (strcmp(name,"sRegI")==0) return Form::idealI; + if (strcmp(name,"sRegP")==0) return Form::idealP; + if (strcmp(name,"sRegF")==0) return Form::idealF; + if (strcmp(name,"sRegD")==0) return Form::idealD; + if (strcmp(name,"sRegL")==0) return Form::idealL; + return Form::none; +} + +Form::DataType Form::ideal_to_Reg_type(const char *name) const { + if( name == NULL ) { return Form::none; } + + if (strcmp(name,"RegI")==0) return Form::idealI; + if (strcmp(name,"RegP")==0) return Form::idealP; + if (strcmp(name,"RegF")==0) return Form::idealF; + if (strcmp(name,"RegD")==0) return Form::idealD; + if (strcmp(name,"RegL")==0) return Form::idealL; + + return Form::none; +} + +// True if 'opType', an ideal name, loads or stores. +Form::DataType Form::is_load_from_memory(const char *opType) const { + if( strcmp(opType,"LoadB")==0 ) return Form::idealB; + if( strcmp(opType,"LoadC")==0 ) return Form::idealC; + if( strcmp(opType,"LoadD")==0 ) return Form::idealD; + if( strcmp(opType,"LoadD_unaligned")==0 ) return Form::idealD; + if( strcmp(opType,"LoadF")==0 ) return Form::idealF; + if( strcmp(opType,"LoadI")==0 ) return Form::idealI; + if( strcmp(opType,"LoadKlass")==0 ) return Form::idealP; + if( strcmp(opType,"LoadL")==0 ) return Form::idealL; + if( strcmp(opType,"LoadL_unaligned")==0 ) return Form::idealL; + if( strcmp(opType,"LoadPLocked")==0 ) return Form::idealP; + if( strcmp(opType,"LoadLLocked")==0 ) return Form::idealL; + if( strcmp(opType,"LoadP")==0 ) return Form::idealP; + if( strcmp(opType,"LoadRange")==0 ) return Form::idealI; + if( strcmp(opType,"LoadS")==0 ) return Form::idealS; + if( strcmp(opType,"Load16B")==0 ) return Form::idealB; + if( strcmp(opType,"Load8B")==0 ) return Form::idealB; + if( strcmp(opType,"Load4B")==0 ) return Form::idealB; + if( strcmp(opType,"Load8C")==0 ) return Form::idealC; + if( strcmp(opType,"Load4C")==0 ) return Form::idealC; + if( strcmp(opType,"Load2C")==0 ) return Form::idealC; + if( strcmp(opType,"Load8S")==0 ) return Form::idealS; + if( strcmp(opType,"Load4S")==0 ) return Form::idealS; + if( strcmp(opType,"Load2S")==0 ) return Form::idealS; + if( strcmp(opType,"Load2D")==0 ) return Form::idealD; + if( strcmp(opType,"Load4F")==0 ) return Form::idealF; + if( strcmp(opType,"Load2F")==0 ) return Form::idealF; + if( strcmp(opType,"Load4I")==0 ) return Form::idealI; + if( strcmp(opType,"Load2I")==0 ) return Form::idealI; + if( strcmp(opType,"Load2L")==0 ) return Form::idealL; + assert( strcmp(opType,"Load") != 0, "Must type Loads" ); + return Form::none; +} + +Form::DataType Form::is_store_to_memory(const char *opType) const { + if( strcmp(opType,"StoreB")==0) return Form::idealB; + if( strcmp(opType,"StoreCM")==0) return Form::idealB; + if( strcmp(opType,"StoreC")==0) return Form::idealC; + if( strcmp(opType,"StoreD")==0) return Form::idealD; + if( strcmp(opType,"StoreF")==0) return Form::idealF; + if( strcmp(opType,"StoreI")==0) return Form::idealI; + if( strcmp(opType,"StoreL")==0) return Form::idealL; + if( strcmp(opType,"StoreP")==0) return Form::idealP; + if( strcmp(opType,"Store16B")==0) return Form::idealB; + if( strcmp(opType,"Store8B")==0) return Form::idealB; + if( strcmp(opType,"Store4B")==0) return Form::idealB; + if( strcmp(opType,"Store8C")==0) return Form::idealC; + if( strcmp(opType,"Store4C")==0) return Form::idealC; + if( strcmp(opType,"Store2C")==0) return Form::idealC; + if( strcmp(opType,"Store2D")==0) return Form::idealD; + if( strcmp(opType,"Store4F")==0) return Form::idealF; + if( strcmp(opType,"Store2F")==0) return Form::idealF; + if( strcmp(opType,"Store4I")==0) return Form::idealI; + if( strcmp(opType,"Store2I")==0) return Form::idealI; + if( strcmp(opType,"Store2L")==0) return Form::idealL; + assert( strcmp(opType,"Store") != 0, "Must type Stores" ); + return Form::none; +} + +Form::InterfaceType Form::interface_type(FormDict &globals) const { + return Form::no_interface; +} + +//------------------------------FormList--------------------------------------- +// Destructor +FormList::~FormList() { + // // This list may not own its elements + // Form *cur = _root; + // Form *next = NULL; + // for( ; (cur = next) != NULL; ) { + // next = (Form *)cur->_next; + // delete cur; + // } +}; + +//------------------------------FormDict--------------------------------------- +// Constructor +FormDict::FormDict( CmpKey cmp, Hash hash, Arena *arena ) + : _form(cmp, hash, arena) { +} +FormDict::~FormDict() { +} + +// Return # of name-Form pairs in dict +int FormDict::Size(void) const { + return _form.Size(); +} + +// Insert inserts the given key-value pair into the dictionary. The prior +// value of the key is returned; NULL if the key was not previously defined. +const Form *FormDict::Insert(const char *name, Form *form) { + return (Form*)_form.Insert((void*)name, (void*)form); +} + +// Finds the value of a given key; or NULL if not found. +// The dictionary is NOT changed. +const Form *FormDict::operator [](const char *name) const { + return (Form*)_form[name]; +} + +//------------------------------FormDict::private------------------------------ +// Disable public use of constructor, copy-ctor, operator =, operator == +FormDict::FormDict( ) : _form(cmpkey,hashkey) { + assert( false, "NotImplemented"); +} +FormDict::FormDict( const FormDict & fd) : _form(fd._form) { +} +FormDict &FormDict::operator =( const FormDict &rhs) { + assert( false, "NotImplemented"); + _form = rhs._form; + return *this; +} +// == compares two dictionaries; they must have the same keys (their keys +// must match using CmpKey) and they must have the same values (pointer +// comparison). If so 1 is returned, if not 0 is returned. +bool FormDict::operator ==(const FormDict &d) const { + assert( false, "NotImplemented"); + return false; +} + +// Print out the dictionary contents as key-value pairs +static void dumpkey (const void* key) { fprintf(stdout, "%s", key); } +static void dumpform(const void* form) { fflush(stdout); ((Form*)form)->dump(); } + +void FormDict::dump() { + _form.print(dumpkey, dumpform); +} + +//------------------------------SourceForm------------------------------------- +SourceForm::SourceForm(char* code) : _code(code) { }; // Constructor +SourceForm::~SourceForm() { +} + +void SourceForm::dump() { // Debug printer + output(stderr); +} + +void SourceForm::output(FILE *fp) { + fprintf(fp,"\n//%s\n%s\n",classname(),(_code?_code:"")); +} diff --git a/hotspot/src/share/vm/adlc/forms.hpp b/hotspot/src/share/vm/adlc/forms.hpp new file mode 100644 index 00000000000..e4f8233926a --- /dev/null +++ b/hotspot/src/share/vm/adlc/forms.hpp @@ -0,0 +1,586 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// FORMS.HPP - ADL Parser Generic and Utility Forms Classes + +#define TRUE 1 +#define FALSE 0 + +// DEFINITIONS OF LEGAL ATTRIBUTE TYPES +#define INS_ATTR 0 +#define OP_ATTR 1 + +// DEFINITIONS OF LEGAL CONSTRAINT TYPES + +// Class List +class Form; +class InstructForm; +class MachNodeForm; +class OperandForm; +class OpClassForm; +class AttributeForm; +class RegisterForm; +class PipelineForm; +class SourceForm; +class EncodeForm; +class Component; +class Constraint; +class Predicate; +class MatchRule; +class Attribute; +class Effect; +class ExpandRule; +class RewriteRule; +class ConstructRule; +class FormatRule; +class Peephole; +class EncClass; +class Interface; +class RegInterface; +class ConstInterface; +class MemInterface; +class CondInterface; +class Opcode; +class InsEncode; +class RegDef; +class RegClass; +class AllocClass; +class ResourceForm; +class PipeClassForm; +class PeepMatch; +class PeepConstraint; +class PeepReplace; +class MatchList; + +class ArchDesc; + +//------------------------------FormDict--------------------------------------- +// Dictionary containing Forms, and objects derived from forms +class FormDict { +private: + Dict _form; // map names, char*, to their Form* or NULL + + // Disable public use of constructor, copy-ctor, operator =, operator == + FormDict( ); + FormDict &operator =( const FormDict & ); + // == compares two dictionaries; they must have the same keys (their keys + // must match using CmpKey) and they must have the same values (pointer + // comparison). If so 1 is returned, if not 0 is returned. + bool operator ==(const FormDict &d) const; // Compare dictionaries for equal + +public: + // cmp is a key comparision routine. hash is a routine to hash a key. + // FormDict( CmpKey cmp, Hash hash ); + FormDict( CmpKey cmp, Hash hash, Arena *arena ); + FormDict( const FormDict & fd ); // Deep-copy guts + ~FormDict(); + + // Return # of key-value pairs in dict + int Size(void) const; + + // Insert inserts the given key-value pair into the dictionary. The prior + // value of the key is returned; NULL if the key was not previously defined. + const Form *Insert(const char *name, Form *form); // A new key-value + + // Find finds the value of a given key; or NULL if not found. + // The dictionary is NOT changed. + const Form *operator [](const char *name) const; // Do a lookup + + void dump(); +}; + +// ***** Master Class for ADL Parser Forms ***** +//------------------------------Form------------------------------------------- +class Form { +public: + static Arena *arena; // arena used by forms +private: + static Arena *generate_arena(); // allocate arena used by forms + +protected: + int _ftype; // Indicator for derived class type + +public: + // Public Data + Form *_next; // Next pointer for form lists + long _linenum; // Line number for debugging + + // Dynamic type check for common forms. + virtual OpClassForm *is_opclass() const; + virtual OperandForm *is_operand() const; + virtual InstructForm *is_instruction() const; + virtual MachNodeForm *is_machnode() const; + virtual AttributeForm *is_attribute() const; + virtual Effect *is_effect() const; + virtual ResourceForm *is_resource() const; + virtual PipeClassForm *is_pipeclass() const; + + // Check if this form is an operand usable for cisc-spilling + virtual bool is_cisc_reg(FormDict &globals) const { return false; } + virtual bool is_cisc_mem(FormDict &globals) const { return false; } + + // Public Methods + Form(int formType=0, int line=0) + : _next(NULL), _linenum(line), _ftype(formType) { }; + ~Form() {}; + + virtual bool ideal_only() const { + assert(0,"Check of ideal status on non-instruction/operand form.\n"); + return FALSE; + } + + // Check constraints after parsing + virtual bool verify() { return true; } + + virtual void dump() { output(stderr); } // Debug printer + // Write info to output files + virtual void output(FILE *fp) { fprintf(fp,"Form Output"); } + +public: + // ADLC types, match the last character on ideal operands and instructions + enum DataType { + none = 0, // Not a simple type + idealI = 1, // Integer type + idealP = 2, // Pointer types, oop(s) + idealL = 3, // Long type + idealF = 4, // Float type + idealD = 5, // Double type + idealB = 6, // Byte type + idealC = 7, // Char type + idealS = 8 // String type + }; + // Convert ideal name to a DataType, return DataType::none if not a 'ConX' + Form::DataType ideal_to_const_type(const char *ideal_type_name) const; + // Convert ideal name to a DataType, return DataType::none if not a 'sRegX + Form::DataType ideal_to_sReg_type(const char *name) const; + // Convert ideal name to a DataType, return DataType::none if not a 'RegX + Form::DataType ideal_to_Reg_type(const char *name) const; + + // Convert ideal name to a DataType, return DataType::none if not a 'LoadX + Form::DataType is_load_from_memory(const char *opType) const; + // Convert ideal name to a DataType, return DataType::none if not a 'StoreX + Form::DataType is_store_to_memory(const char *opType) const; + + // ADLC call types, matched with ideal world + enum CallType { + invalid_type = 0, // invalid call type + JAVA_STATIC = 1, // monomorphic entry + JAVA_DYNAMIC = 2, // possibly megamorphic, inline cache call + JAVA_COMPILED = 3, // callee will be compiled java + JAVA_INTERP = 4, // callee will be executed by interpreter + JAVA_NATIVE = 5, // native entrypoint + JAVA_RUNTIME = 6, // runtime entrypoint + JAVA_LEAF = 7 // calling leaf + }; + + // Interface types for operands and operand classes + enum InterfaceType { + no_interface = 0, // unknown or inconsistent interface type + constant_interface = 1, // interface to constants + register_interface = 2, // interface to registers + memory_interface = 3, // interface to memory + conditional_interface = 4 // interface for condition codes + }; + virtual Form::InterfaceType interface_type(FormDict &globals) const; + + enum CiscSpillInfo { + Not_cisc_spillable = AdlcVMDeps::Not_cisc_spillable, + Maybe_cisc_spillable = 0, + Is_cisc_spillable = 1 + // ... + }; + + // LEGAL FORM TYPES + enum { + INS, + OPER, + OPCLASS, + SRC, + ADEF, + REG, + PIPE, + CNST, + PRED, + ATTR, + MAT, + ENC, + FOR, + EXP, + REW, + EFF, + RDEF, + RCL, + ACL, + RES, + PCL, + PDEF, + REGL, + RESL, + STAL, + COMP, + PEEP, + RESO + }; + +}; + +//------------------------------FormList--------------------------------------- +class FormList { +private: + Form *_root; + Form *_tail; + Form *_cur; + int _justReset; // Set immediately after reset + Form *_cur2; // Nested iterator + int _justReset2; + +public: + void addForm(Form * entry) { + if (_tail==NULL) { _root = _tail = _cur = entry;} + else { _tail->_next = entry; _tail = entry;} + }; + Form * current() { return _cur; }; + Form * iter() { if (_justReset) _justReset = 0; + else if (_cur) _cur = _cur->_next; + return _cur;}; + void reset() { if (_root) {_cur = _root; _justReset = 1;} }; + + // Second iterator, state is internal + Form * current2(){ return _cur2; }; + Form * iter2() { if (_justReset2) _justReset2 = 0; + else if (_cur2) _cur2 = _cur2->_next; + return _cur2;}; + void reset2() { if (_root) {_cur2 = _root; _justReset2 = 1;} }; + + int count() { + int count = 0; reset(); + for( Form *cur; (cur = iter()) != NULL; ) { ++count; }; + return count; + } + + void dump() { + reset(); + Form *cur; + for(; (cur = iter()) != NULL; ) { + cur->dump(); + }; + } + + bool verify() { + bool verified = true; + + reset(); + Form *cur; + for(; (cur = iter()) != NULL; ) { + if ( ! cur->verify() ) verified = false; + }; + + return verified; + } + + void output(FILE* fp) { + reset(); + Form *cur; + for( ; (cur = iter()) != NULL; ) { + cur->output(fp); + }; + } + + FormList() { _justReset = 1; _justReset2 = 1; _root = NULL; _tail = NULL; _cur = NULL; _cur2 = NULL;}; + ~FormList(); +}; + +//------------------------------NameList--------------------------------------- +// Extendable list of pointers, +class NameList { + friend class PreserveIter; + +private: + int _cur; // Insert next entry here; count of entries + int _max; // Number of spaces allocated + const char **_names; // Array of names + +protected: + int _iter; // position during iteration + bool _justReset; // Set immediately after reset + + +public: + static const char *_signal; // reserved user-defined string + enum { Not_in_list = -1 }; + + void addName(const char *name); + void add_signal(); + void clear(); // Remove all entries + + int count() const; + + void reset(); // Reset iteration + const char *iter(); // after reset(), first element : else next + const char *current(); // return current element in iteration. + + bool current_is_signal(); // Return 'true' if current entry is signal + bool is_signal(const char *entry); // Return true if entry is a signal + + bool search(const char *); // Search for a name in the list + int index(const char *); // Return index of name in list + const char *name (intptr_t index);// Return name at index in list + + void dump(); // output to stderr + void output(FILE *fp); // Output list of names to 'fp' + + NameList(); + ~NameList(); +}; + + +// Convenience class to preserve iteration state since iterators are +// internal instead of being external. +class PreserveIter { + private: + NameList* _list; + int _iter; + bool _justReset; + + public: + PreserveIter(NameList* nl) { + _list = nl; + _iter = _list->_iter; + _justReset = _list->_justReset; + } + ~PreserveIter() { + _list->_iter = _iter; + _list->_justReset = _justReset; + } + +}; + + +//------------------------------NameAndList------------------------------------ +// Storage for a name and an associated list of names +class NameAndList { +private: + const char *_name; + NameList _list; + +public: + NameAndList(char *name); + ~NameAndList(); + + // Add to entries in list + void add_entry(const char *entry); + + // Access the name and its associated list. + const char *name() const; + void reset(); + const char *iter(); + + int count() { return _list.count(); } + + // Return the "index" entry in the list, zero-based + const char *operator[](int index); + + + void dump(); // output to stderr + void output(FILE *fp); // Output list of names to 'fp' +}; + +//------------------------------ComponentList--------------------------------- +// Component lists always have match rule operands first, followed by parameter +// operands which do not appear in the match list (in order of declaration). +class ComponentList : private NameList { +private: + int _matchcnt; // Count of match rule operands + +public: + + // This is a batch program. (And I have a destructor bug!) + void operator delete( void *ptr ) {} + + void insert(Component *component, bool mflag); + void insert(const char *name, const char *opType, int usedef, bool mflag); + + int count(); + int match_count() { return _matchcnt; } // Get count of match rule opers + + Component *iter(); // after reset(), first element : else next + Component *match_iter(); // after reset(), first element : else next + Component *post_match_iter(); // after reset(), first element : else next + void reset(); // Reset iteration + Component *current(); // return current element in iteration. + + // Return element at "position", else NULL + Component *operator[](int position); + Component *at(int position) { return (*this)[position]; } + + // Return first component having this name. + const Component *search(const char *name); + + // Return number of USEs + number of DEFs + int num_operands(); + // Return zero-based position in list; -1 if not in list. + int operand_position(const char *name, int usedef); + // Find position for this name, regardless of use/def information + int operand_position(const char *name); + // Find position for this name when looked up for output via "format" + int operand_position_format(const char *name); + // Find position for the Label when looked up for output via "format" + int label_position(); + // Find position for the Method when looked up for output via "format" + int method_position(); + + void dump(); // output to stderr + void output(FILE *fp); // Output list of names to 'fp' + + ComponentList(); + ~ComponentList(); +}; + +//------------------------------SourceForm------------------------------------- +class SourceForm : public Form { +private: + +public: + // Public Data + char *_code; // Buffer for storing code text + + // Public Methods + SourceForm(char* code); + ~SourceForm(); + + virtual const char* classname() { return "SourceForm"; } + + void dump(); // Debug printer + void output(FILE *fp); // Write output files +}; + +class HeaderForm : public SourceForm { +public: + HeaderForm(char* code) : SourceForm(code) { } + + virtual const char* classname() { return "HeaderForm"; } +}; + +class PreHeaderForm : public SourceForm { +public: + PreHeaderForm(char* code) : SourceForm(code) { } + + virtual const char* classname() { return "PreHeaderForm"; } +}; + + + + +//------------------------------Expr------------------------------------------ +#define STRING_BUFFER_LENGTH 2048 +// class Expr represents integer expressions containing constants and addition +// Value must be in range zero through maximum positive integer. 32bits. +// Expected use: instruction and operand costs +class Expr { +public: + enum { + Zero = 0, + Max = 0x7fffffff + }; + const char *_external_name; // if !NULL, then print this instead of _expr + const char *_expr; + int _min_value; + int _max_value; + + Expr(); + Expr(const char *cost); + Expr(const char *name, const char *expression, int min_value, int max_value); + Expr *clone() const; + + bool is_unknown() const { return (this == Expr::get_unknown()); } + bool is_zero() const { return (_min_value == Expr::Zero && _max_value == Expr::Zero); } + bool less_than_or_equal(const Expr *c) const { return (_max_value <= c->_min_value); } + + void add(const Expr *c); + void add(const char *c); + void add(const char *c, ArchDesc &AD); // check if 'c' is defined in .ad + void set_external_name(const char *name) { _external_name = name; } + + const char *as_string() const { return (_external_name != NULL ? _external_name : _expr); } + void print() const; + void print_define(FILE *fp) const; + void print_assert(FILE *fp) const; + + static Expr *get_unknown(); // Returns pointer to shared unknown cost instance + + static char *buffer() { return &external_buffer[0]; } + static bool init_buffers(); // Fill buffers with 0 + static bool check_buffers(); // if buffer use may have overflowed, assert + +private: + static Expr *_unknown_expr; + static char string_buffer[STRING_BUFFER_LENGTH]; + static char external_buffer[STRING_BUFFER_LENGTH]; + static bool _init_buffers; + const char *compute_expr(const Expr *c1, const Expr *c2); // cost as string after adding 'c1' and 'c2' + int compute_min (const Expr *c1, const Expr *c2); // minimum after adding 'c1' and 'c2' + int compute_max (const Expr *c1, const Expr *c2); // maximum after adding 'c1' and 'c2' + const char *compute_external(const Expr *c1, const Expr *c2); // external name after adding 'c1' and 'c2' +}; + +//------------------------------ExprDict--------------------------------------- +// Dictionary containing Exprs +class ExprDict { +private: + Dict _expr; // map names, char*, to their Expr* or NULL + NameList _defines; // record the order of definitions entered with define call + + // Disable public use of constructor, copy-ctor, operator =, operator == + ExprDict( ); + ExprDict( const ExprDict & ); // Deep-copy guts + ExprDict &operator =( const ExprDict & ); + // == compares two dictionaries; they must have the same keys (their keys + // must match using CmpKey) and they must have the same values (pointer + // comparison). If so 1 is returned, if not 0 is returned. + bool operator ==(const ExprDict &d) const; // Compare dictionaries for equal + +public: + // cmp is a key comparision routine. hash is a routine to hash a key. + ExprDict( CmpKey cmp, Hash hash, Arena *arena ); + ~ExprDict(); + + // Return # of key-value pairs in dict + int Size(void) const; + + // define inserts the given key-value pair into the dictionary, + // and records the name in order for later output, ... + const Expr *define(const char *name, Expr *expr); + + // Insert inserts the given key-value pair into the dictionary. The prior + // value of the key is returned; NULL if the key was not previously defined. + const Expr *Insert(const char *name, Expr *expr); // A new key-value + + // Find finds the value of a given key; or NULL if not found. + // The dictionary is NOT changed. + const Expr *operator [](const char *name) const; // Do a lookup + + void print_defines(FILE *fp); + void print_asserts(FILE *fp); + void dump(); +}; diff --git a/hotspot/src/share/vm/adlc/formsopt.cpp b/hotspot/src/share/vm/adlc/formsopt.cpp new file mode 100644 index 00000000000..df1f912e9a6 --- /dev/null +++ b/hotspot/src/share/vm/adlc/formsopt.cpp @@ -0,0 +1,724 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// FORMS.CPP - Definitions for ADL Parser Forms Classes +#include "adlc.hpp" + +//==============================Register Allocation============================ +int RegisterForm::_reg_ctr = 0; + +//------------------------------RegisterForm----------------------------------- +// Constructor +RegisterForm::RegisterForm() + : _regDef(cmpstr,hashstr, Form::arena), + _regClass(cmpstr,hashstr, Form::arena), + _allocClass(cmpstr,hashstr, Form::arena) { +} +RegisterForm::~RegisterForm() { +} + +// record a new register definition +void RegisterForm::addRegDef(char *name, char *callingConv, char *c_conv, + char *idealtype, char *encoding, char* concrete) { + RegDef *regDef = new RegDef(name, callingConv, c_conv, idealtype, encoding, concrete); + _rdefs.addName(name); + _regDef.Insert(name,regDef); +} + +// record a new register class +RegClass *RegisterForm::addRegClass(const char *className) { + RegClass *regClass = new RegClass(className); + _rclasses.addName(className); + _regClass.Insert(className,regClass); + return regClass; +} + +// record a new register class +AllocClass *RegisterForm::addAllocClass(char *className) { + AllocClass *allocClass = new AllocClass(className); + _aclasses.addName(className); + _allocClass.Insert(className,allocClass); + return allocClass; +} + +// Called after parsing the Register block. Record the register class +// for spill-slots/regs. +void RegisterForm::addSpillRegClass() { + // Stack slots start at the next available even register number. + _reg_ctr = (_reg_ctr+1) & ~1; + const char *rc_name = "stack_slots"; + RegClass *reg_class = new RegClass(rc_name); + reg_class->_stack_or_reg = true; + _rclasses.addName(rc_name); + _regClass.Insert(rc_name,reg_class); +} + + +// Provide iteration over all register definitions +// in the order used by the register allocator +void RegisterForm::reset_RegDefs() { + _current_ac = NULL; + _aclasses.reset(); +} + +RegDef *RegisterForm::iter_RegDefs() { + // Check if we need to get the next AllocClass + if ( _current_ac == NULL ) { + const char *ac_name = _aclasses.iter(); + if( ac_name == NULL ) return NULL; // No more allocation classes + _current_ac = (AllocClass*)_allocClass[ac_name]; + _current_ac->_regDefs.reset(); + assert( _current_ac != NULL, "Name must match an allocation class"); + } + + const char *rd_name = _current_ac->_regDefs.iter(); + if( rd_name == NULL ) { + // At end of this allocation class, check the next + _current_ac = NULL; + return iter_RegDefs(); + } + RegDef *reg_def = (RegDef*)_current_ac->_regDef[rd_name]; + assert( reg_def != NULL, "Name must match a register definition"); + return reg_def; +} + +// return the register definition with name 'regName' +RegDef *RegisterForm::getRegDef(const char *regName) { + RegDef *regDef = (RegDef*)_regDef[regName]; + return regDef; +} + +// return the register class with name 'className' +RegClass *RegisterForm::getRegClass(const char *className) { + RegClass *regClass = (RegClass*)_regClass[className]; + return regClass; +} + + +// Check that register classes are compatible with chunks +bool RegisterForm::verify() { + bool valid = true; + + // Verify Register Classes + // check that each register class contains registers from one chunk + const char *rc_name = NULL; + _rclasses.reset(); + while ( (rc_name = _rclasses.iter()) != NULL ) { + // Check the chunk value for all registers in this class + RegClass *reg_class = getRegClass(rc_name); + assert( reg_class != NULL, "InternalError() no matching register class"); + } // end of RegClasses + + // Verify that every register has been placed into an allocation class + RegDef *reg_def = NULL; + reset_RegDefs(); + uint num_register_zero = 0; + while ( (reg_def = iter_RegDefs()) != NULL ) { + if( reg_def->register_num() == 0 ) ++num_register_zero; + } + if( num_register_zero > 1 ) { + fprintf(stderr, + "ERROR: More than one register has been assigned register-number 0.\n" + "Probably because a register has not been entered into an allocation class.\n"); + } + + return valid; +} + +// Compute RegMask size +int RegisterForm::RegMask_Size() { + // Need at least this many words + int words_for_regs = (_reg_ctr + 31)>>5; + // Add a few for incoming & outgoing arguments to calls. + // Round up to the next doubleword size. + return (words_for_regs + 2 + 1) & ~1; +} + +void RegisterForm::dump() { // Debug printer + output(stderr); +} + +void RegisterForm::output(FILE *fp) { // Write info to output files + const char *name; + fprintf(fp,"\n"); + fprintf(fp,"-------------------- Dump RegisterForm --------------------\n"); + for(_rdefs.reset(); (name = _rdefs.iter()) != NULL;) { + ((RegDef*)_regDef[name])->output(fp); + } + fprintf(fp,"\n"); + for (_rclasses.reset(); (name = _rclasses.iter()) != NULL;) { + ((RegClass*)_regClass[name])->output(fp); + } + fprintf(fp,"\n"); + for (_aclasses.reset(); (name = _aclasses.iter()) != NULL;) { + ((AllocClass*)_allocClass[name])->output(fp); + } + fprintf(fp,"-------------------- end RegisterForm --------------------\n"); +} + +//------------------------------RegDef----------------------------------------- +// Constructor +RegDef::RegDef(char *regname, char *callconv, char *c_conv, char * idealtype, char * encode, char * concrete) + : _regname(regname), _callconv(callconv), _c_conv(c_conv), + _idealtype(idealtype), + _register_encode(encode), + _concrete(concrete), + _register_num(0) { + + // Chunk and register mask are determined by the register number + // _register_num is set when registers are added to an allocation class +} +RegDef::~RegDef() { // Destructor +} + +void RegDef::set_register_num(uint32 register_num) { + _register_num = register_num; +} + +// Bit pattern used for generating machine code +const char* RegDef::register_encode() const { + return _register_encode; +} + +// Register number used in machine-independent code +uint32 RegDef::register_num() const { + return _register_num; +} + +void RegDef::dump() { + output(stderr); +} + +void RegDef::output(FILE *fp) { // Write info to output files + fprintf(fp,"RegDef: %s (%s) encode as %s using number %d\n", + _regname, (_callconv?_callconv:""), _register_encode, _register_num); + fprintf(fp,"\n"); +} + + +//------------------------------RegClass--------------------------------------- +// Construct a register class into which registers will be inserted +RegClass::RegClass(const char *classid) : _stack_or_reg(false), _classid(classid), _regDef(cmpstr,hashstr, Form::arena) { +} + +// record a register in this class +void RegClass::addReg(RegDef *regDef) { + _regDefs.addName(regDef->_regname); + _regDef.Insert((void*)regDef->_regname, regDef); +} + +// Number of registers in class +uint RegClass::size() const { + return _regDef.Size(); +} + +const RegDef *RegClass::get_RegDef(const char *rd_name) const { + return (const RegDef*)_regDef[rd_name]; +} + +void RegClass::reset() { + _regDefs.reset(); +} + +const char *RegClass::rd_name_iter() { + return _regDefs.iter(); +} + +RegDef *RegClass::RegDef_iter() { + const char *rd_name = rd_name_iter(); + RegDef *reg_def = rd_name ? (RegDef*)_regDef[rd_name] : NULL; + return reg_def; +} + +const RegDef* RegClass::find_first_elem() { + const RegDef* first = NULL; + const RegDef* def = NULL; + + reset(); + while ((def = RegDef_iter()) != NULL) { + if (first == NULL || def->register_num() < first->register_num()) { + first = def; + } + } + + assert(first != NULL, "empty mask?"); + return first;; +} + +// Collect all the registers in this register-word. One bit per register. +int RegClass::regs_in_word( int wordnum, bool stack_also ) { + int word = 0; + const char *name; + for(_regDefs.reset(); (name = _regDefs.iter()) != NULL;) { + int rnum = ((RegDef*)_regDef[name])->register_num(); + if( (rnum >> 5) == wordnum ) + word |= (1L<<(rnum&31)); + } + if( stack_also ) { + // Now also collect stack bits + for( int i = 0; i < 32; i++ ) + if( wordnum*32+i >= RegisterForm::_reg_ctr ) + word |= (1L<output(fp); + } + fprintf(fp,"--- done with entries for reg_class %s\n\n",_classid); +} + + +//------------------------------AllocClass------------------------------------- +AllocClass::AllocClass(char *classid) : _classid(classid), _regDef(cmpstr,hashstr, Form::arena) { +} + +// record a register in this class +void AllocClass::addReg(RegDef *regDef) { + assert( regDef != NULL, "Can not add a NULL to an allocation class"); + regDef->set_register_num( RegisterForm::_reg_ctr++ ); + // Add regDef to this allocation class + _regDefs.addName(regDef->_regname); + _regDef.Insert((void*)regDef->_regname, regDef); +} + +void AllocClass::dump() { + output(stderr); +} + +void AllocClass::output(FILE *fp) { // Write info to output files + fprintf(fp,"AllocClass: %s \n",_classid); + const char *name; + for(_regDefs.reset(); (name = _regDefs.iter()) != NULL;) { + ((RegDef*)_regDef[name])->output(fp); + } + fprintf(fp,"--- done with entries for alloc_class %s\n\n",_classid); +} + +//==============================Frame Handling================================= +//------------------------------FrameForm-------------------------------------- +FrameForm::FrameForm() { + _frame_pointer = NULL; + _c_frame_pointer = NULL; + _alignment = NULL; + _return_addr = NULL; + _c_return_addr = NULL; + _in_preserve_slots = NULL; + _varargs_C_out_slots_killed = NULL; + _calling_convention = NULL; + _c_calling_convention = NULL; + _return_value = NULL; + _c_return_value = NULL; + _interpreter_frame_pointer_reg = NULL; +} + +FrameForm::~FrameForm() { +} + +void FrameForm::dump() { + output(stderr); +} + +void FrameForm::output(FILE *fp) { // Write info to output files + fprintf(fp,"\nFrame:\n"); +} + +//==============================Scheduling===================================== +//------------------------------PipelineForm----------------------------------- +PipelineForm::PipelineForm() + : _reslist () + , _resdict (cmpstr, hashstr, Form::arena) + , _classdict (cmpstr, hashstr, Form::arena) + , _rescount (0) + , _maxcycleused (0) + , _stages () + , _stagecnt (0) + , _classlist () + , _classcnt (0) + , _noplist () + , _nopcnt (0) + , _variableSizeInstrs (false) + , _branchHasDelaySlot (false) + , _maxInstrsPerBundle (0) + , _maxBundlesPerCycle (1) + , _instrUnitSize (0) + , _bundleUnitSize (0) + , _instrFetchUnitSize (0) + , _instrFetchUnits (0) { +} +PipelineForm::~PipelineForm() { +} + +void PipelineForm::dump() { + output(stderr); +} + +void PipelineForm::output(FILE *fp) { // Write info to output files + const char *res; + const char *stage; + const char *cls; + const char *nop; + int count = 0; + + fprintf(fp,"\nPipeline:"); + if (_variableSizeInstrs) + if (_instrUnitSize > 0) + fprintf(fp," variable-sized instructions in %d byte units", _instrUnitSize); + else + fprintf(fp," variable-sized instructions"); + else + if (_instrUnitSize > 0) + fprintf(fp," fixed-sized instructions of %d bytes", _instrUnitSize); + else if (_bundleUnitSize > 0) + fprintf(fp," fixed-sized bundles of %d bytes", _bundleUnitSize); + else + fprintf(fp," fixed-sized instructions"); + if (_branchHasDelaySlot) + fprintf(fp,", branch has delay slot"); + if (_maxInstrsPerBundle > 0) + fprintf(fp,", max of %d instruction%s in parallel", + _maxInstrsPerBundle, _maxInstrsPerBundle > 1 ? "s" : ""); + if (_maxBundlesPerCycle > 0) + fprintf(fp,", max of %d bundle%s in parallel", + _maxBundlesPerCycle, _maxBundlesPerCycle > 1 ? "s" : ""); + if (_instrFetchUnitSize > 0 && _instrFetchUnits) + fprintf(fp, ", fetch %d x % d bytes per cycle", _instrFetchUnits, _instrFetchUnitSize); + + fprintf(fp,"\nResource:"); + for ( _reslist.reset(); (res = _reslist.iter()) != NULL; ) + fprintf(fp," %s(0x%08x)", res, _resdict[res]->is_resource()->mask()); + fprintf(fp,"\n"); + + fprintf(fp,"\nDescription:\n"); + for ( _stages.reset(); (stage = _stages.iter()) != NULL; ) + fprintf(fp," %s(%d)", stage, count++); + fprintf(fp,"\n"); + + fprintf(fp,"\nClasses:\n"); + for ( _classlist.reset(); (cls = _classlist.iter()) != NULL; ) + _classdict[cls]->is_pipeclass()->output(fp); + + fprintf(fp,"\nNop Instructions:"); + for ( _noplist.reset(); (nop = _noplist.iter()) != NULL; ) + fprintf(fp, " \"%s\"", nop); + fprintf(fp,"\n"); +} + + +//------------------------------ResourceForm----------------------------------- +ResourceForm::ResourceForm(unsigned resmask) +: _resmask(resmask) { +} +ResourceForm::~ResourceForm() { +} + +ResourceForm *ResourceForm::is_resource() const { + return (ResourceForm *)(this); +} + +void ResourceForm::dump() { + output(stderr); +} + +void ResourceForm::output(FILE *fp) { // Write info to output files + fprintf(fp, "resource: 0x%08x;\n", mask()); +} + + +//------------------------------PipeClassOperandForm---------------------------------- + +void PipeClassOperandForm::dump() { + output(stderr); +} + +void PipeClassOperandForm::output(FILE *fp) { // Write info to output files + fprintf(stderr,"PipeClassOperandForm: %s", _stage); + fflush(stderr); + if (_more_instrs > 0) + fprintf(stderr,"+%d", _more_instrs); + fprintf(stderr," (%s)\n", _iswrite ? "write" : "read"); + fflush(stderr); + fprintf(fp,"PipeClassOperandForm: %s", _stage); + if (_more_instrs > 0) + fprintf(fp,"+%d", _more_instrs); + fprintf(fp," (%s)\n", _iswrite ? "write" : "read"); +} + + +//------------------------------PipeClassResourceForm---------------------------------- + +void PipeClassResourceForm::dump() { + output(stderr); +} + +void PipeClassResourceForm::output(FILE *fp) { // Write info to output files + fprintf(fp,"PipeClassResourceForm: %s at stage %s for %d cycles\n", + _resource, _stage, _cycles); +} + + +//------------------------------PipeClassForm---------------------------------- +PipeClassForm::PipeClassForm(const char *id, int num) + : _ident(id) + , _num(num) + , _localNames(cmpstr, hashstr, Form::arena) + , _localUsage(cmpstr, hashstr, Form::arena) + , _has_fixed_latency(0) + , _fixed_latency(0) + , _instruction_count(0) + , _has_multiple_bundles(false) + , _has_branch_delay_slot(false) + , _force_serialization(false) + , _may_have_no_code(false) { +} + +PipeClassForm::~PipeClassForm() { +} + +PipeClassForm *PipeClassForm::is_pipeclass() const { + return (PipeClassForm *)(this); +} + +void PipeClassForm::dump() { + output(stderr); +} + +void PipeClassForm::output(FILE *fp) { // Write info to output files + fprintf(fp,"PipeClassForm: #%03d", _num); + if (_ident) + fprintf(fp," \"%s\":", _ident); + if (_has_fixed_latency) + fprintf(fp," latency %d", _fixed_latency); + if (_force_serialization) + fprintf(fp, ", force serialization"); + if (_may_have_no_code) + fprintf(fp, ", may have no code"); + fprintf(fp, ", %d instruction%s\n", InstructionCount(), InstructionCount() != 1 ? "s" : ""); +} + + +//==============================Peephole Optimization========================== +int Peephole::_peephole_counter = 0; +//------------------------------Peephole--------------------------------------- +Peephole::Peephole() : _match(NULL), _constraint(NULL), _replace(NULL), _next(NULL) { + _peephole_number = _peephole_counter++; +} +Peephole::~Peephole() { +} + +// Append a peephole rule with the same root instruction +void Peephole::append_peephole(Peephole *next_peephole) { + if( _next == NULL ) { + _next = next_peephole; + } else { + _next->append_peephole( next_peephole ); + } +} + +// Store the components of this peephole rule +void Peephole::add_match(PeepMatch *match) { + assert( _match == NULL, "fatal()" ); + _match = match; +} + +void Peephole::append_constraint(PeepConstraint *next_constraint) { + if( _constraint == NULL ) { + _constraint = next_constraint; + } else { + _constraint->append( next_constraint ); + } +} + +void Peephole::add_replace(PeepReplace *replace) { + assert( _replace == NULL, "fatal()" ); + _replace = replace; +} + +// class Peephole accessor methods are in the declaration. + + +void Peephole::dump() { + output(stderr); +} + +void Peephole::output(FILE *fp) { // Write info to output files + fprintf(fp,"Peephole:\n"); + if( _match != NULL ) _match->output(fp); + if( _constraint != NULL ) _constraint->output(fp); + if( _replace != NULL ) _replace->output(fp); + // Output the next entry + if( _next ) _next->output(fp); +} + +//------------------------------PeepMatch-------------------------------------- +PeepMatch::PeepMatch(char *rule) : _max_position(0), _rule(rule) { +} +PeepMatch::~PeepMatch() { +} + + +// Insert info into the match-rule +void PeepMatch::add_instruction(int parent, int position, const char *name, + int input) { + if( position > _max_position ) _max_position = position; + + _parent.addName((char *)parent); + _position.addName((char *)position); + _instrs.addName(name); + _input.addName((char *)input); +} + +// Access info about instructions in the peep-match rule +int PeepMatch::max_position() { + return _max_position; +} + +const char *PeepMatch::instruction_name(intptr_t position) { + return _instrs.name(position); +} + +// Iterate through all info on matched instructions +void PeepMatch::reset() { + _parent.reset(); + _position.reset(); + _instrs.reset(); + _input.reset(); +} + +void PeepMatch::next_instruction( intptr_t &parent, intptr_t &position, const char * &name, intptr_t &input ){ + parent = (intptr_t)_parent.iter(); + position = (intptr_t)_position.iter(); + name = _instrs.iter(); + input = (intptr_t)_input.iter(); +} + +// 'true' if current position in iteration is a placeholder, not matched. +bool PeepMatch::is_placeholder() { + return _instrs.current_is_signal(); +} + + +void PeepMatch::dump() { + output(stderr); +} + +void PeepMatch::output(FILE *fp) { // Write info to output files + fprintf(fp,"PeepMatch:\n"); +} + +//------------------------------PeepConstraint--------------------------------- +PeepConstraint::PeepConstraint(intptr_t left_inst, char *left_op, char *relation, + intptr_t right_inst, char *right_op) + : _left_inst(left_inst), _left_op(left_op), _relation(relation), + _right_inst(right_inst), _right_op(right_op), _next(NULL) {} +PeepConstraint::~PeepConstraint() { +} + +// Check if constraints use instruction at position +bool PeepConstraint::constrains_instruction(intptr_t position) { + // Check local instruction constraints + if( _left_inst == position ) return true; + if( _right_inst == position ) return true; + + // Check remaining constraints in list + if( _next == NULL ) return false; + else return _next->constrains_instruction(position); +} + +// Add another constraint +void PeepConstraint::append(PeepConstraint *next_constraint) { + if( _next == NULL ) { + _next = next_constraint; + } else { + _next->append( next_constraint ); + } +} + +// Access the next constraint in the list +PeepConstraint *PeepConstraint::next() { + return _next; +} + + +void PeepConstraint::dump() { + output(stderr); +} + +void PeepConstraint::output(FILE *fp) { // Write info to output files + fprintf(fp,"PeepConstraint:\n"); +} + +//------------------------------PeepReplace------------------------------------ +PeepReplace::PeepReplace(char *rule) : _rule(rule) { +} +PeepReplace::~PeepReplace() { +} + +// Add contents of peepreplace +void PeepReplace::add_instruction(char *root) { + _instruction.addName(root); + _operand_inst_num.add_signal(); + _operand_op_name.add_signal(); +} +void PeepReplace::add_operand( int inst_num, char *inst_operand ) { + _instruction.add_signal(); + _operand_inst_num.addName((char*)inst_num); + _operand_op_name.addName(inst_operand); +} + +// Access contents of peepreplace +void PeepReplace::reset() { + _instruction.reset(); + _operand_inst_num.reset(); + _operand_op_name.reset(); +} +void PeepReplace::next_instruction(const char * &inst){ + inst = _instruction.iter(); + intptr_t inst_num = (intptr_t)_operand_inst_num.iter(); + const char *inst_operand = _operand_op_name.iter(); +} +void PeepReplace::next_operand( intptr_t &inst_num, const char * &inst_operand ) { + const char *inst = _instruction.iter(); + inst_num = (intptr_t)_operand_inst_num.iter(); + inst_operand = _operand_op_name.iter(); +} + + + +void PeepReplace::dump() { + output(stderr); +} + +void PeepReplace::output(FILE *fp) { // Write info to output files + fprintf(fp,"PeepReplace:\n"); +} diff --git a/hotspot/src/share/vm/adlc/formsopt.hpp b/hotspot/src/share/vm/adlc/formsopt.hpp new file mode 100644 index 00000000000..627dc4de035 --- /dev/null +++ b/hotspot/src/share/vm/adlc/formsopt.hpp @@ -0,0 +1,548 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// FORMSOPT.HPP - ADL Parser Target Specific Optimization Forms Classes + +// Class List +class Form; +class InstructForm; +class OperandForm; +class OpClassForm; +class AttributeForm; +class RegisterForm; +class PipelineForm; +class SourceForm; +class EncodeForm; +class Component; +class Constraint; +class Predicate; +class MatchRule; +class Attribute; +class Effect; +class ExpandRule; +class RewriteRule; +class ConstructRule; +class FormatRule; +class Peephole; +class PeepMatch; +class PeepConstraint; +class EncClass; +class Interface; +class RegInterface; +class ConstInterface; +class MemInterface; +class CondInterface; +class Opcode; +class InsEncode; +class RegDef; +class RegClass; +class AllocClass; +class ResourceForm; +class PipeClassForm; +class PipeClassOperandForm; +class PipeClassResourceForm; +class PeepMatch; +class PeepConstraint; +class PeepReplace; +class MatchList; + +class ArchDesc; + +//==============================Register Allocation============================ +//------------------------------RegisterForm----------------------------------- +class RegisterForm : public Form { +private: + AllocClass *_current_ac; // State used by iter_RegDefs() + +public: + // Public Data + NameList _rdefs; // List of register definition names + Dict _regDef; // map register name to RegDef* + + NameList _rclasses; // List of register class names + Dict _regClass; // map register class name to RegClass* + + NameList _aclasses; // List of allocation class names + Dict _allocClass; // Dictionary of allocation classes + + static int _reg_ctr; // Register counter + static int RegMask_Size(); // Compute RegMask size + + // Public Methods + RegisterForm(); + ~RegisterForm(); + + void addRegDef(char *regName, char *callingConv, char *c_conv, + char * idealtype, char *encoding, char* concreteName); + RegClass *addRegClass(const char *className); + AllocClass *addAllocClass(char *allocName); + void addSpillRegClass(); + + // Provide iteration over all register definitions + // in the order used by the register allocator + void reset_RegDefs(); + RegDef *iter_RegDefs(); + RegDef *getRegDef (const char *regName); + + RegClass *getRegClass(const char *className); + + // Return register mask, compressed chunk and register # + uint reg_mask(char *register_class); + + // Check that register classes are compatible with chunks + bool verify(); + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------RegDef----------------------------------------- +class RegDef : public Form { +public: + // Public Data + const char *_regname; // ADLC (Opto) Register name + const char *_callconv; // Calling convention + const char *_c_conv; // Native calling convention, 'C' + const char *_idealtype; // Ideal Type for register save/restore + const char *_concrete; // concrete register name + +private: + const char *_register_encode; // The register encoding + // The chunk and register mask bits define info for register allocation + uint32 _register_num; // Which register am I + +public: + // Public Methods + RegDef(char *regname, char *callconv, char *c_conv, + char *idealtype, char *encoding, char *concrete); + ~RegDef(); // Destructor + + // Interface to define/redefine the register number + void set_register_num(uint32 new_register_num); + + // Bit pattern used for generating machine code + const char *register_encode() const; + // Register number used in machine-independent code + uint32 register_num() const; + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------RegClass--------------------------------------- +class RegClass : public Form { +public: + // Public Data + const char *_classid; // Name of class + NameList _regDefs; // List of registers in class + Dict _regDef; // Dictionary of registers in class + bool _stack_or_reg; // Allowed on any stack slot + + // Public Methods + RegClass(const char *classid);// Constructor + + void addReg(RegDef *regDef); // Add a register to this class + + uint size() const; // Number of registers in class + int regs_in_word( int wordnum, bool stack_also ); + + const RegDef *get_RegDef(const char *regDef_name) const; + + // Returns the lowest numbered register in the mask. + const RegDef* find_first_elem(); + + // Iteration support + void reset(); // Reset the following two iterators + RegDef *RegDef_iter(); // which move jointly, + const char *rd_name_iter(); // invoking either advances both. + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------AllocClass------------------------------------- +class AllocClass : public Form { +private: + +public: + // Public Data + char *_classid; // Name of class + NameList _regDefs; // List of registers in class + Dict _regDef; // Dictionary of registers in class + + // Public Methods + AllocClass(char *classid); // Constructor + + void addReg(RegDef *regDef); // Add a register to this class + uint size() {return _regDef.Size();} // Number of registers in class + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + + +//==============================Frame Handling================================ +//------------------------------FrameForm------------------------------------- +class FrameForm : public Form { +private: + +public: + // Public Data + bool _direction; // Direction of stack growth + char *_sync_stack_slots; + char *_inline_cache_reg; + char *_interpreter_method_oop_reg; + char *_interpreter_frame_pointer_reg; + char *_cisc_spilling_operand_name; + char *_frame_pointer; + char *_c_frame_pointer; + char *_alignment; + bool _return_addr_loc; + bool _c_return_addr_loc; + char *_return_addr; + char *_c_return_addr; + char *_in_preserve_slots; + char *_varargs_C_out_slots_killed; + char *_calling_convention; + char *_c_calling_convention; + char *_return_value; + char *_c_return_value; + + // Public Methods + FrameForm(); + ~FrameForm(); + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + + +//==============================Scheduling===================================== +//------------------------------PipelineForm----------------------------------- +class PipelineForm : public Form { +private: + +public: + // Public Data + NameList _reslist; // List of pipeline resources + FormDict _resdict; // Resource Name -> ResourceForm mapping + int _rescount; // Number of resources (ignores OR cases) + int _maxcycleused; // Largest cycle used relative to beginning of instruction + + NameList _stages; // List of pipeline stages on architecture + int _stagecnt; // Number of stages listed + + NameList _classlist; // List of pipeline classes + FormDict _classdict; // Class Name -> PipeClassForm mapping + int _classcnt; // Number of classes + + NameList _noplist; // List of NOP instructions + int _nopcnt; // Number of nop instructions + + bool _variableSizeInstrs; // Indicates if this architecture has variable sized instructions + bool _branchHasDelaySlot; // Indicates that branches have delay slot instructions + int _maxInstrsPerBundle; // Indicates the maximum number of instructions for ILP + int _maxBundlesPerCycle; // Indicates the maximum number of bundles for ILP + int _instrUnitSize; // The minimum instruction unit size, in bytes + int _bundleUnitSize; // The bundle unit size, in bytes + int _instrFetchUnitSize; // The size of the I-fetch unit, in bytes [must be power of 2] + int _instrFetchUnits; // The number of I-fetch units processed per cycle + + // Public Methods + PipelineForm(); + ~PipelineForm(); + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------ResourceForm----------------------------------- +class ResourceForm : public Form { +public: + unsigned mask() const { return _resmask; }; + +private: + // Public Data + unsigned _resmask; // Resource Mask (OR of resource specifier bits) + +public: + + // Virtual Methods + virtual ResourceForm *is_resource() const; + + // Public Methods + ResourceForm(unsigned resmask); // Constructor + ~ResourceForm(); // Destructor + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------PipeClassOperandForm----------------------------- +class PipeClassOperandForm : public Form { +private: + +public: + // Public Data + const char *_stage; // Name of Stage + unsigned _iswrite; // Read or Write + unsigned _more_instrs; // Additional Instructions + + // Public Methods + PipeClassOperandForm(const char *stage, unsigned iswrite, unsigned more_instrs) + : _stage(stage) + , _iswrite(iswrite) + , _more_instrs(more_instrs) + {}; + + ~PipeClassOperandForm() {}; // Destructor + + bool isWrite() const { return _iswrite != 0; } + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------PipeClassResourceForm-------------------------- +class PipeClassResourceForm : public Form { +private: + +public: + // Public Data + const char *_resource; // Resource + const char *_stage; // Stage the resource is used in + int _cycles; // Number of cycles the resource is used + + // Public Methods + PipeClassResourceForm(const char *resource, const char *stage, int cycles) + // Constructor + : _resource(resource) + , _stage(stage) + , _cycles(cycles) + {}; + + ~PipeClassResourceForm() {}; // Destructor + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------PipeClassForm---------------------------------- +class PipeClassForm : public Form { +private: + +public: + + // Public Data + const char *_ident; // Name of class + int _num; // Used in name of MachNode subclass + NameList _parameters; // Locally defined names + FormDict _localNames; // Table of operands & their types + FormDict _localUsage; // Table of operand usage + FormList _resUsage; // List of resource usage + NameList _instructs; // List of instructions and machine nodes that use this pipeline class + bool _has_fixed_latency; // Always takes this number of cycles + int _fixed_latency; // Always takes this number of cycles + int _instruction_count; // Number of instructions in first bundle + bool _has_multiple_bundles; // Indicates if 1 or multiple bundles + bool _has_branch_delay_slot; // Has branch delay slot as last instruction + bool _force_serialization; // This node serializes relative to surrounding nodes + bool _may_have_no_code; // This node may generate no code based on register allocation + + // Virtual Methods + virtual PipeClassForm *is_pipeclass() const; + + // Public Methods + PipeClassForm(const char *id, int num); + // Constructor + ~PipeClassForm(); // Destructor + + bool hasFixedLatency() { return _has_fixed_latency; } + int fixedLatency() { return _fixed_latency; } + + void setFixedLatency(int fixed_latency) { _has_fixed_latency = 1; _fixed_latency = fixed_latency; } + + void setInstructionCount(int i) { _instruction_count = i; } + void setMultipleBundles(bool b) { _has_multiple_bundles = b; } + void setBranchDelay(bool s) { _has_branch_delay_slot = s; } + void setForceSerialization(bool s) { _force_serialization = s; } + void setMayHaveNoCode(bool s) { _may_have_no_code = s; } + + int InstructionCount() const { return _instruction_count; } + bool hasMultipleBundles() const { return _has_multiple_bundles; } + bool hasBranchDelay() const { return _has_branch_delay_slot; } + bool forceSerialization() const { return _force_serialization; } + bool mayHaveNoCode() const { return _may_have_no_code; } + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + + +//==============================Peephole Optimization========================== +//------------------------------Peephole--------------------------------------- +class Peephole : public Form { +private: + static int _peephole_counter;// Incremented by each peephole rule parsed + int _peephole_number;// Remember my order in architecture description + PeepMatch *_match; // Instruction pattern to match + PeepConstraint *_constraint; // List of additional constraints + PeepReplace *_replace; // Instruction pattern to substitute in + + Peephole *_next; + +public: + // Public Methods + Peephole(); + ~Peephole(); + + // Append a peephole rule with the same root instruction + void append_peephole(Peephole *next_peephole); + + // Store the components of this peephole rule + void add_match(PeepMatch *only_one_match); + void append_constraint(PeepConstraint *next_constraint); + void add_replace(PeepReplace *only_one_replacement); + + // Access the components of this peephole rule + int peephole_number() { return _peephole_number; } + PeepMatch *match() { return _match; } + PeepConstraint *constraints() { return _constraint; } + PeepReplace *replacement() { return _replace; } + Peephole *next() { return _next; } + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + + +class PeepMatch : public Form { +private: + char *_rule; + // NameList _depth; // Depth of this instruction + NameList _parent; + NameList _position; + NameList _instrs; // List of instructions in match rule + NameList _input; // input position in parent's instruction + int _max_position; + +public: + // Public Methods + PeepMatch(char *rule); + ~PeepMatch(); + + // Insert info into the match-rule + void add_instruction(int parent, int position, const char *name, int input); + + // Access info about instructions in the peep-match rule + int max_position(); + const char *instruction_name(intptr_t position); + // Iterate through all info on matched instructions + void reset(); + void next_instruction( intptr_t &parent, intptr_t &position, const char * &name, intptr_t &input ); + // 'true' if current position in iteration is a placeholder, not matched. + bool is_placeholder(); + + void dump(); + void output(FILE *fp); +}; + + +class PeepConstraint : public Form { +private: + PeepConstraint *_next; // Additional constraints ANDed together + +public: + const intptr_t _left_inst; + const char *_left_op; + const char *_relation; + const intptr_t _right_inst; + const char *_right_op; + +public: + // Public Methods + PeepConstraint(intptr_t left_inst, char *left_op, char *relation, + intptr_t right_inst, char *right_op); + ~PeepConstraint(); + + // Check if constraints use instruction at position + bool constrains_instruction(intptr_t position); + + // Add another constraint + void append(PeepConstraint *next_peep_constraint); + // Access the next constraint in the list + PeepConstraint *next(); + + void dump(); + void output(FILE *fp); +}; + + +class PeepReplace : public Form { +private: + char *_rule; + NameList _instruction; + NameList _operand_inst_num; + NameList _operand_op_name; + +public: + + // Public Methods + PeepReplace(char *rule); + ~PeepReplace(); + + // Add contents of peepreplace + void add_instruction(char *root); + void add_operand( int inst_num, char *inst_operand ); + + // Access contents of peepreplace + void reset(); + void next_instruction(const char * &root); + void next_operand( intptr_t &inst_num, const char * &inst_operand ); + + // Utilities + void dump(); + void output(FILE *fp); +}; + + +class PeepChild : public Form { +public: + const int _inst_num; // Number of instruction (-1 if only named) + const char *_inst_op; // Instruction's operand, NULL if number == -1 + const char *_inst_name; // Name of the instruction + +public: + PeepChild(char *inst_name) + : _inst_num(-1), _inst_op(NULL), _inst_name(inst_name) {}; + PeepChild(int inst_num, char *inst_op, char *inst_name) + : _inst_num(inst_num), _inst_op(inst_op), _inst_name(inst_name) {}; + ~PeepChild(); + + bool use_leaf_operand() { return _inst_num != -1; }; + bool generate_an_instruction() { return _inst_num == -1; } + + void dump(); + void output(FILE *fp); +}; diff --git a/hotspot/src/share/vm/adlc/formssel.cpp b/hotspot/src/share/vm/adlc/formssel.cpp new file mode 100644 index 00000000000..a65882497f0 --- /dev/null +++ b/hotspot/src/share/vm/adlc/formssel.cpp @@ -0,0 +1,3999 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// FORMS.CPP - Definitions for ADL Parser Forms Classes +#include "adlc.hpp" + +//==============================Instructions=================================== +//------------------------------InstructForm----------------------------------- +InstructForm::InstructForm(const char *id, bool ideal_only) + : _ident(id), _ideal_only(ideal_only), + _localNames(cmpstr, hashstr, Form::arena), + _effects(cmpstr, hashstr, Form::arena) { + _ftype = Form::INS; + + _matrule = NULL; + _insencode = NULL; + _opcode = NULL; + _size = NULL; + _attribs = NULL; + _predicate = NULL; + _exprule = NULL; + _rewrule = NULL; + _format = NULL; + _peephole = NULL; + _ins_pipe = NULL; + _uniq_idx = NULL; + _num_uniq = 0; + _cisc_spill_operand = Not_cisc_spillable;// Which operand may cisc-spill + _cisc_spill_alternate = NULL; // possible cisc replacement + _cisc_reg_mask_name = NULL; + _is_cisc_alternate = false; + _is_short_branch = false; + _short_branch_form = NULL; + _alignment = 1; +} + +InstructForm::InstructForm(const char *id, InstructForm *instr, MatchRule *rule) + : _ident(id), _ideal_only(false), + _localNames(instr->_localNames), + _effects(instr->_effects) { + _ftype = Form::INS; + + _matrule = rule; + _insencode = instr->_insencode; + _opcode = instr->_opcode; + _size = instr->_size; + _attribs = instr->_attribs; + _predicate = instr->_predicate; + _exprule = instr->_exprule; + _rewrule = instr->_rewrule; + _format = instr->_format; + _peephole = instr->_peephole; + _ins_pipe = instr->_ins_pipe; + _uniq_idx = instr->_uniq_idx; + _num_uniq = instr->_num_uniq; + _cisc_spill_operand = Not_cisc_spillable;// Which operand may cisc-spill + _cisc_spill_alternate = NULL; // possible cisc replacement + _cisc_reg_mask_name = NULL; + _is_cisc_alternate = false; + _is_short_branch = false; + _short_branch_form = NULL; + _alignment = 1; + // Copy parameters + const char *name; + instr->_parameters.reset(); + for (; (name = instr->_parameters.iter()) != NULL;) + _parameters.addName(name); +} + +InstructForm::~InstructForm() { +} + +InstructForm *InstructForm::is_instruction() const { + return (InstructForm*)this; +} + +bool InstructForm::ideal_only() const { + return _ideal_only; +} + +bool InstructForm::sets_result() const { + return (_matrule != NULL && _matrule->sets_result()); +} + +bool InstructForm::needs_projections() { + _components.reset(); + for( Component *comp; (comp = _components.iter()) != NULL; ) { + if (comp->isa(Component::KILL)) { + return true; + } + } + return false; +} + + +bool InstructForm::has_temps() { + if (_matrule) { + // Examine each component to see if it is a TEMP + _components.reset(); + // Skip the first component, if already handled as (SET dst (...)) + Component *comp = NULL; + if (sets_result()) comp = _components.iter(); + while ((comp = _components.iter()) != NULL) { + if (comp->isa(Component::TEMP)) { + return true; + } + } + } + + return false; +} + +uint InstructForm::num_defs_or_kills() { + uint defs_or_kills = 0; + + _components.reset(); + for( Component *comp; (comp = _components.iter()) != NULL; ) { + if( comp->isa(Component::DEF) || comp->isa(Component::KILL) ) { + ++defs_or_kills; + } + } + + return defs_or_kills; +} + +// This instruction has an expand rule? +bool InstructForm::expands() const { + return ( _exprule != NULL ); +} + +// This instruction has a peephole rule? +Peephole *InstructForm::peepholes() const { + return _peephole; +} + +// This instruction has a peephole rule? +void InstructForm::append_peephole(Peephole *peephole) { + if( _peephole == NULL ) { + _peephole = peephole; + } else { + _peephole->append_peephole(peephole); + } +} + + +// ideal opcode enumeration +const char *InstructForm::ideal_Opcode( FormDict &globalNames ) const { + if( !_matrule ) return "Node"; // Something weird + // Chain rules do not really have ideal Opcodes; use their source + // operand ideal Opcode instead. + if( is_simple_chain_rule(globalNames) ) { + const char *src = _matrule->_rChild->_opType; + OperandForm *src_op = globalNames[src]->is_operand(); + assert( src_op, "Not operand class of chain rule" ); + if( !src_op->_matrule ) return "Node"; + return src_op->_matrule->_opType; + } + // Operand chain rules do not really have ideal Opcodes + if( _matrule->is_chain_rule(globalNames) ) + return "Node"; + return strcmp(_matrule->_opType,"Set") + ? _matrule->_opType + : _matrule->_rChild->_opType; +} + +// Recursive check on all operands' match rules in my match rule +bool InstructForm::is_pinned(FormDict &globals) { + if ( ! _matrule) return false; + + int index = 0; + if (_matrule->find_type("Goto", index)) return true; + if (_matrule->find_type("If", index)) return true; + if (_matrule->find_type("CountedLoopEnd",index)) return true; + if (_matrule->find_type("Return", index)) return true; + if (_matrule->find_type("Rethrow", index)) return true; + if (_matrule->find_type("TailCall", index)) return true; + if (_matrule->find_type("TailJump", index)) return true; + if (_matrule->find_type("Halt", index)) return true; + if (_matrule->find_type("Jump", index)) return true; + + return is_parm(globals); +} + +// Recursive check on all operands' match rules in my match rule +bool InstructForm::is_projection(FormDict &globals) { + if ( ! _matrule) return false; + + int index = 0; + if (_matrule->find_type("Goto", index)) return true; + if (_matrule->find_type("Return", index)) return true; + if (_matrule->find_type("Rethrow", index)) return true; + if (_matrule->find_type("TailCall",index)) return true; + if (_matrule->find_type("TailJump",index)) return true; + if (_matrule->find_type("Halt", index)) return true; + + return false; +} + +// Recursive check on all operands' match rules in my match rule +bool InstructForm::is_parm(FormDict &globals) { + if ( ! _matrule) return false; + + int index = 0; + if (_matrule->find_type("Parm",index)) return true; + + return false; +} + + +// Return 'true' if this instruction matches an ideal 'Copy*' node +int InstructForm::is_ideal_copy() const { + return _matrule ? _matrule->is_ideal_copy() : 0; +} + +// Return 'true' if this instruction is too complex to rematerialize. +int InstructForm::is_expensive() const { + // We can prove it is cheap if it has an empty encoding. + // This helps with platform-specific nops like ThreadLocal and RoundFloat. + if (is_empty_encoding()) + return 0; + + if (is_tls_instruction()) + return 1; + + if (_matrule == NULL) return 0; + + return _matrule->is_expensive(); +} + +// Has an empty encoding if _size is a constant zero or there +// are no ins_encode tokens. +int InstructForm::is_empty_encoding() const { + if (_insencode != NULL) { + _insencode->reset(); + if (_insencode->encode_class_iter() == NULL) { + return 1; + } + } + if (_size != NULL && strcmp(_size, "0") == 0) { + return 1; + } + return 0; +} + +int InstructForm::is_tls_instruction() const { + if (_ident != NULL && + ( ! strcmp( _ident,"tlsLoadP") || + ! strncmp(_ident,"tlsLoadP_",9)) ) { + return 1; + } + + if (_matrule != NULL && _insencode != NULL) { + const char* opType = _matrule->_opType; + if (strcmp(opType, "Set")==0) + opType = _matrule->_rChild->_opType; + if (strcmp(opType,"ThreadLocal")==0) { + fprintf(stderr, "Warning: ThreadLocal instruction %s should be named 'tlsLoadP_*'\n", + (_ident == NULL ? "NULL" : _ident)); + return 1; + } + } + + return 0; +} + + +// Return 'true' if this instruction matches an ideal 'Copy*' node +bool InstructForm::is_ideal_unlock() const { + return _matrule ? _matrule->is_ideal_unlock() : false; +} + +bool InstructForm::is_ideal_call_leaf() const { + return _matrule ? _matrule->is_ideal_call_leaf() : false; +} + +// Return 'true' if this instruction matches an ideal 'If' node +bool InstructForm::is_ideal_if() const { + if( _matrule == NULL ) return false; + + return _matrule->is_ideal_if(); +} + +// Return 'true' if this instruction matches an ideal 'FastLock' node +bool InstructForm::is_ideal_fastlock() const { + if( _matrule == NULL ) return false; + + return _matrule->is_ideal_fastlock(); +} + +// Return 'true' if this instruction matches an ideal 'MemBarXXX' node +bool InstructForm::is_ideal_membar() const { + if( _matrule == NULL ) return false; + + return _matrule->is_ideal_membar(); +} + +// Return 'true' if this instruction matches an ideal 'LoadPC' node +bool InstructForm::is_ideal_loadPC() const { + if( _matrule == NULL ) return false; + + return _matrule->is_ideal_loadPC(); +} + +// Return 'true' if this instruction matches an ideal 'Box' node +bool InstructForm::is_ideal_box() const { + if( _matrule == NULL ) return false; + + return _matrule->is_ideal_box(); +} + +// Return 'true' if this instruction matches an ideal 'Goto' node +bool InstructForm::is_ideal_goto() const { + if( _matrule == NULL ) return false; + + return _matrule->is_ideal_goto(); +} + +// Return 'true' if this instruction matches an ideal 'Jump' node +bool InstructForm::is_ideal_jump() const { + if( _matrule == NULL ) return false; + + return _matrule->is_ideal_jump(); +} + +// Return 'true' if instruction matches ideal 'If' | 'Goto' | +// 'CountedLoopEnd' | 'Jump' +bool InstructForm::is_ideal_branch() const { + if( _matrule == NULL ) return false; + + return _matrule->is_ideal_if() || _matrule->is_ideal_goto() || _matrule->is_ideal_jump(); +} + + +// Return 'true' if this instruction matches an ideal 'Return' node +bool InstructForm::is_ideal_return() const { + if( _matrule == NULL ) return false; + + // Check MatchRule to see if the first entry is the ideal "Return" node + int index = 0; + if (_matrule->find_type("Return",index)) return true; + if (_matrule->find_type("Rethrow",index)) return true; + if (_matrule->find_type("TailCall",index)) return true; + if (_matrule->find_type("TailJump",index)) return true; + + return false; +} + +// Return 'true' if this instruction matches an ideal 'Halt' node +bool InstructForm::is_ideal_halt() const { + int index = 0; + return _matrule && _matrule->find_type("Halt",index); +} + +// Return 'true' if this instruction matches an ideal 'SafePoint' node +bool InstructForm::is_ideal_safepoint() const { + int index = 0; + return _matrule && _matrule->find_type("SafePoint",index); +} + +// Return 'true' if this instruction matches an ideal 'Nop' node +bool InstructForm::is_ideal_nop() const { + return _ident && _ident[0] == 'N' && _ident[1] == 'o' && _ident[2] == 'p' && _ident[3] == '_'; +} + +bool InstructForm::is_ideal_control() const { + if ( ! _matrule) return false; + + return is_ideal_return() || is_ideal_branch() || is_ideal_halt(); +} + +// Return 'true' if this instruction matches an ideal 'Call' node +Form::CallType InstructForm::is_ideal_call() const { + if( _matrule == NULL ) return Form::invalid_type; + + // Check MatchRule to see if the first entry is the ideal "Call" node + int idx = 0; + if(_matrule->find_type("CallStaticJava",idx)) return Form::JAVA_STATIC; + idx = 0; + if(_matrule->find_type("Lock",idx)) return Form::JAVA_STATIC; + idx = 0; + if(_matrule->find_type("Unlock",idx)) return Form::JAVA_STATIC; + idx = 0; + if(_matrule->find_type("CallDynamicJava",idx)) return Form::JAVA_DYNAMIC; + idx = 0; + if(_matrule->find_type("CallRuntime",idx)) return Form::JAVA_RUNTIME; + idx = 0; + if(_matrule->find_type("CallLeaf",idx)) return Form::JAVA_LEAF; + idx = 0; + if(_matrule->find_type("CallLeafNoFP",idx)) return Form::JAVA_LEAF; + idx = 0; + + return Form::invalid_type; +} + +// Return 'true' if this instruction matches an ideal 'Load?' node +Form::DataType InstructForm::is_ideal_load() const { + if( _matrule == NULL ) return Form::none; + + return _matrule->is_ideal_load(); +} + +// Return 'true' if this instruction matches an ideal 'Load?' node +Form::DataType InstructForm::is_ideal_store() const { + if( _matrule == NULL ) return Form::none; + + return _matrule->is_ideal_store(); +} + +// Return the input register that must match the output register +// If this is not required, return 0 +uint InstructForm::two_address(FormDict &globals) { + uint matching_input = 0; + if(_components.count() == 0) return 0; + + _components.reset(); + Component *comp = _components.iter(); + // Check if there is a DEF + if( comp->isa(Component::DEF) ) { + // Check that this is a register + const char *def_type = comp->_type; + const Form *form = globals[def_type]; + OperandForm *op = form->is_operand(); + if( op ) { + if( op->constrained_reg_class() != NULL && + op->interface_type(globals) == Form::register_interface ) { + // Remember the local name for equality test later + const char *def_name = comp->_name; + // Check if a component has the same name and is a USE + do { + if( comp->isa(Component::USE) && strcmp(comp->_name,def_name)==0 ) { + return operand_position_format(def_name); + } + } while( (comp = _components.iter()) != NULL); + } + } + } + + return 0; +} + + +// when chaining a constant to an instruction, returns 'true' and sets opType +Form::DataType InstructForm::is_chain_of_constant(FormDict &globals) { + const char *dummy = NULL; + const char *dummy2 = NULL; + return is_chain_of_constant(globals, dummy, dummy2); +} +Form::DataType InstructForm::is_chain_of_constant(FormDict &globals, + const char * &opTypeParam) { + const char *result = NULL; + + return is_chain_of_constant(globals, opTypeParam, result); +} + +Form::DataType InstructForm::is_chain_of_constant(FormDict &globals, + const char * &opTypeParam, const char * &resultParam) { + Form::DataType data_type = Form::none; + if ( ! _matrule) return data_type; + + // !!!!! + // The source of the chain rule is 'position = 1' + uint position = 1; + const char *result = NULL; + const char *name = NULL; + const char *opType = NULL; + // Here base_operand is looking for an ideal type to be returned (opType). + if ( _matrule->is_chain_rule(globals) + && _matrule->base_operand(position, globals, result, name, opType) ) { + data_type = ideal_to_const_type(opType); + + // if it isn't an ideal constant type, just return + if ( data_type == Form::none ) return data_type; + + // Ideal constant types also adjust the opType parameter. + resultParam = result; + opTypeParam = opType; + return data_type; + } + + return data_type; +} + +// Check if a simple chain rule +bool InstructForm::is_simple_chain_rule(FormDict &globals) const { + if( _matrule && _matrule->sets_result() + && _matrule->_rChild->_lChild == NULL + && globals[_matrule->_rChild->_opType] + && globals[_matrule->_rChild->_opType]->is_opclass() ) { + return true; + } + return false; +} + +// check for structural rematerialization +bool InstructForm::rematerialize(FormDict &globals, RegisterForm *registers ) { + bool rematerialize = false; + + Form::DataType data_type = is_chain_of_constant(globals); + if( data_type != Form::none ) + rematerialize = true; + + // Constants + if( _components.count() == 1 && _components[0]->is(Component::USE_DEF) ) + rematerialize = true; + + // Pseudo-constants (values easily available to the runtime) + if (is_empty_encoding() && is_tls_instruction()) + rematerialize = true; + + // 1-input, 1-output, such as copies or increments. + if( _components.count() == 2 && + _components[0]->is(Component::DEF) && + _components[1]->isa(Component::USE) ) + rematerialize = true; + + // Check for an ideal 'Load?' and eliminate rematerialize option + if ( is_ideal_load() != Form::none || // Ideal load? Do not rematerialize + is_ideal_copy() != Form::none || // Ideal copy? Do not rematerialize + is_expensive() != Form::none) { // Expensive? Do not rematerialize + rematerialize = false; + } + + // Always rematerialize the flags. They are more expensive to save & + // restore than to recompute (and possibly spill the compare's inputs). + if( _components.count() >= 1 ) { + Component *c = _components[0]; + const Form *form = globals[c->_type]; + OperandForm *opform = form->is_operand(); + if( opform ) { + // Avoid the special stack_slots register classes + const char *rc_name = opform->constrained_reg_class(); + if( rc_name ) { + if( strcmp(rc_name,"stack_slots") ) { + // Check for ideal_type of RegFlags + const char *type = opform->ideal_type( globals, registers ); + if( !strcmp(type,"RegFlags") ) + rematerialize = true; + } else + rematerialize = false; // Do not rematerialize things target stk + } + } + } + + return rematerialize; +} + +// loads from memory, so must check for anti-dependence +bool InstructForm::needs_anti_dependence_check(FormDict &globals) const { + // Machine independent loads must be checked for anti-dependences + if( is_ideal_load() != Form::none ) return true; + + // !!!!! !!!!! !!!!! + // TEMPORARY + // if( is_simple_chain_rule(globals) ) return false; + + // String-compare uses many memorys edges, but writes none + if( _matrule && _matrule->_rChild && + strcmp(_matrule->_rChild->_opType,"StrComp")==0 ) + return true; + + // Check if instruction has a USE of a memory operand class, but no defs + bool USE_of_memory = false; + bool DEF_of_memory = false; + Component *comp = NULL; + ComponentList &components = (ComponentList &)_components; + + components.reset(); + while( (comp = components.iter()) != NULL ) { + const Form *form = globals[comp->_type]; + if( !form ) continue; + OpClassForm *op = form->is_opclass(); + if( !op ) continue; + if( form->interface_type(globals) == Form::memory_interface ) { + if( comp->isa(Component::USE) ) USE_of_memory = true; + if( comp->isa(Component::DEF) ) { + OperandForm *oper = form->is_operand(); + if( oper && oper->is_user_name_for_sReg() ) { + // Stack slots are unaliased memory handled by allocator + oper = oper; // debug stopping point !!!!! + } else { + DEF_of_memory = true; + } + } + } + } + return (USE_of_memory && !DEF_of_memory); +} + + +bool InstructForm::is_wide_memory_kill(FormDict &globals) const { + if( _matrule == NULL ) return false; + if( !_matrule->_opType ) return false; + + if( strcmp(_matrule->_opType,"MemBarRelease") == 0 ) return true; + if( strcmp(_matrule->_opType,"MemBarAcquire") == 0 ) return true; + + return false; +} + +int InstructForm::memory_operand(FormDict &globals) const { + // Machine independent loads must be checked for anti-dependences + // Check if instruction has a USE of a memory operand class, or a def. + int USE_of_memory = 0; + int DEF_of_memory = 0; + const char* last_memory_DEF = NULL; // to test DEF/USE pairing in asserts + Component *unique = NULL; + Component *comp = NULL; + ComponentList &components = (ComponentList &)_components; + + components.reset(); + while( (comp = components.iter()) != NULL ) { + const Form *form = globals[comp->_type]; + if( !form ) continue; + OpClassForm *op = form->is_opclass(); + if( !op ) continue; + if( op->stack_slots_only(globals) ) continue; + if( form->interface_type(globals) == Form::memory_interface ) { + if( comp->isa(Component::DEF) ) { + last_memory_DEF = comp->_name; + DEF_of_memory++; + unique = comp; + } else if( comp->isa(Component::USE) ) { + if( last_memory_DEF != NULL ) { + assert(0 == strcmp(last_memory_DEF, comp->_name), "every memory DEF is followed by a USE of the same name"); + last_memory_DEF = NULL; + } + USE_of_memory++; + if (DEF_of_memory == 0) // defs take precedence + unique = comp; + } else { + assert(last_memory_DEF == NULL, "unpaired memory DEF"); + } + } + } + assert(last_memory_DEF == NULL, "unpaired memory DEF"); + assert(USE_of_memory >= DEF_of_memory, "unpaired memory DEF"); + USE_of_memory -= DEF_of_memory; // treat paired DEF/USE as one occurrence + if( (USE_of_memory + DEF_of_memory) > 0 ) { + if( is_simple_chain_rule(globals) ) { + //fprintf(stderr, "Warning: chain rule is not really a memory user.\n"); + //((InstructForm*)this)->dump(); + // Preceding code prints nothing on sparc and these insns on intel: + // leaP8 leaP32 leaPIdxOff leaPIdxScale leaPIdxScaleOff leaP8 leaP32 + // leaPIdxOff leaPIdxScale leaPIdxScaleOff + return NO_MEMORY_OPERAND; + } + + if( DEF_of_memory == 1 ) { + assert(unique != NULL, ""); + if( USE_of_memory == 0 ) { + // unique def, no uses + } else { + // // unique def, some uses + // // must return bottom unless all uses match def + // unique = NULL; + } + } else if( DEF_of_memory > 0 ) { + // multiple defs, don't care about uses + unique = NULL; + } else if( USE_of_memory == 1) { + // unique use, no defs + assert(unique != NULL, ""); + } else if( USE_of_memory > 0 ) { + // multiple uses, no defs + unique = NULL; + } else { + assert(false, "bad case analysis"); + } + // process the unique DEF or USE, if there is one + if( unique == NULL ) { + return MANY_MEMORY_OPERANDS; + } else { + int pos = components.operand_position(unique->_name); + if( unique->isa(Component::DEF) ) { + pos += 1; // get corresponding USE from DEF + } + assert(pos >= 1, "I was just looking at it!"); + return pos; + } + } + + // missed the memory op?? + if( true ) { // %%% should not be necessary + if( is_ideal_store() != Form::none ) { + fprintf(stderr, "Warning: cannot find memory opnd in instr.\n"); + ((InstructForm*)this)->dump(); + // pretend it has multiple defs and uses + return MANY_MEMORY_OPERANDS; + } + if( is_ideal_load() != Form::none ) { + fprintf(stderr, "Warning: cannot find memory opnd in instr.\n"); + ((InstructForm*)this)->dump(); + // pretend it has multiple uses and no defs + return MANY_MEMORY_OPERANDS; + } + } + + return NO_MEMORY_OPERAND; +} + + +// This instruction captures the machine-independent bottom_type +// Expected use is for pointer vs oop determination for LoadP +bool InstructForm::captures_bottom_type() const { + if( _matrule && _matrule->_rChild && + (!strcmp(_matrule->_rChild->_opType,"CastPP") || // new result type + !strcmp(_matrule->_rChild->_opType,"CastX2P") || // new result type + !strcmp(_matrule->_rChild->_opType,"CreateEx") || // type of exception + !strcmp(_matrule->_rChild->_opType,"CheckCastPP")) ) return true; + else if ( is_ideal_load() == Form::idealP ) return true; + else if ( is_ideal_store() != Form::none ) return true; + + return false; +} + + +// Access instr_cost attribute or return NULL. +const char* InstructForm::cost() { + for (Attribute* cur = _attribs; cur != NULL; cur = (Attribute*)cur->_next) { + if( strcmp(cur->_ident,AttributeForm::_ins_cost) == 0 ) { + return cur->_val; + } + } + return NULL; +} + +// Return count of top-level operands. +uint InstructForm::num_opnds() { + int num_opnds = _components.num_operands(); + + // Need special handling for matching some ideal nodes + // i.e. Matching a return node + /* + if( _matrule ) { + if( strcmp(_matrule->_opType,"Return" )==0 || + strcmp(_matrule->_opType,"Halt" )==0 ) + return 3; + } + */ + return num_opnds; +} + +// Return count of unmatched operands. +uint InstructForm::num_post_match_opnds() { + uint num_post_match_opnds = _components.count(); + uint num_match_opnds = _components.match_count(); + num_post_match_opnds = num_post_match_opnds - num_match_opnds; + + return num_post_match_opnds; +} + +// Return the number of leaves below this complex operand +uint InstructForm::num_consts(FormDict &globals) const { + if ( ! _matrule) return 0; + + // This is a recursive invocation on all operands in the matchrule + return _matrule->num_consts(globals); +} + +// Constants in match rule with specified type +uint InstructForm::num_consts(FormDict &globals, Form::DataType type) const { + if ( ! _matrule) return 0; + + // This is a recursive invocation on all operands in the matchrule + return _matrule->num_consts(globals, type); +} + + +// Return the register class associated with 'leaf'. +const char *InstructForm::out_reg_class(FormDict &globals) { + assert( false, "InstructForm::out_reg_class(FormDict &globals); Not Implemented"); + + return NULL; +} + + + +// Lookup the starting position of inputs we are interested in wrt. ideal nodes +uint InstructForm::oper_input_base(FormDict &globals) { + if( !_matrule ) return 1; // Skip control for most nodes + + // Need special handling for matching some ideal nodes + // i.e. Matching a return node + if( strcmp(_matrule->_opType,"Return" )==0 || + strcmp(_matrule->_opType,"Rethrow" )==0 || + strcmp(_matrule->_opType,"TailCall" )==0 || + strcmp(_matrule->_opType,"TailJump" )==0 || + strcmp(_matrule->_opType,"SafePoint" )==0 || + strcmp(_matrule->_opType,"Halt" )==0 ) + return AdlcVMDeps::Parms; // Skip the machine-state edges + + if( _matrule->_rChild && + strcmp(_matrule->_rChild->_opType,"StrComp")==0 ) { + // String compare takes 1 control and 4 memory edges. + return 5; + } + + // Check for handling of 'Memory' input/edge in the ideal world. + // The AD file writer is shielded from knowledge of these edges. + int base = 1; // Skip control + base += _matrule->needs_ideal_memory_edge(globals); + + // Also skip the base-oop value for uses of derived oops. + // The AD file writer is shielded from knowledge of these edges. + base += needs_base_oop_edge(globals); + + return base; +} + +// Implementation does not modify state of internal structures +void InstructForm::build_components() { + // Add top-level operands to the components + if (_matrule) _matrule->append_components(_localNames, _components); + + // Add parameters that "do not appear in match rule". + bool has_temp = false; + const char *name; + const char *kill_name = NULL; + for (_parameters.reset(); (name = _parameters.iter()) != NULL;) { + OperandForm *opForm = (OperandForm*)_localNames[name]; + + const Form *form = _effects[name]; + Effect *e = form ? form->is_effect() : NULL; + if (e != NULL) { + has_temp |= e->is(Component::TEMP); + + // KILLs must be declared after any TEMPs because TEMPs are real + // uses so their operand numbering must directly follow the real + // inputs from the match rule. Fixing the numbering seems + // complex so simply enforce the restriction during parse. + if (kill_name != NULL && + e->isa(Component::TEMP) && !e->isa(Component::DEF)) { + OperandForm* kill = (OperandForm*)_localNames[kill_name]; + globalAD->syntax_err(_linenum, "%s: %s %s must be at the end of the argument list\n", + _ident, kill->_ident, kill_name); + } else if (e->isa(Component::KILL)) { + kill_name = name; + } + + // TEMPs are real uses and need to be among the first parameters + // listed, otherwise the numbering of operands and inputs gets + // screwy, so enforce this restriction during parse. + if (kill_name != NULL && + e->isa(Component::TEMP) && !e->isa(Component::DEF)) { + OperandForm* kill = (OperandForm*)_localNames[kill_name]; + globalAD->syntax_err(_linenum, "%s: %s %s must follow %s %s in the argument list\n", + _ident, kill->_ident, kill_name, opForm->_ident, name); + } else if (e->isa(Component::KILL)) { + kill_name = name; + } + } + + const Component *component = _components.search(name); + if ( component == NULL ) { + if (e) { + _components.insert(name, opForm->_ident, e->_use_def, false); + component = _components.search(name); + if (component->isa(Component::USE) && !component->isa(Component::TEMP) && _matrule) { + const Form *form = globalAD->globalNames()[component->_type]; + assert( form, "component type must be a defined form"); + OperandForm *op = form->is_operand(); + if (op->_interface && op->_interface->is_RegInterface()) { + globalAD->syntax_err(_linenum, "%s: illegal USE of non-input: %s %s\n", + _ident, opForm->_ident, name); + } + } + } else { + // This would be a nice warning but it triggers in a few places in a benign way + // if (_matrule != NULL && !expands()) { + // globalAD->syntax_err(_linenum, "%s: %s %s not mentioned in effect or match rule\n", + // _ident, opForm->_ident, name); + // } + _components.insert(name, opForm->_ident, Component::INVALID, false); + } + } + else if (e) { + // Component was found in the list + // Check if there is a new effect that requires an extra component. + // This happens when adding 'USE' to a component that is not yet one. + if ((!component->isa( Component::USE) && ((e->_use_def & Component::USE) != 0))) { + if (component->isa(Component::USE) && _matrule) { + const Form *form = globalAD->globalNames()[component->_type]; + assert( form, "component type must be a defined form"); + OperandForm *op = form->is_operand(); + if (op->_interface && op->_interface->is_RegInterface()) { + globalAD->syntax_err(_linenum, "%s: illegal USE of non-input: %s %s\n", + _ident, opForm->_ident, name); + } + } + _components.insert(name, opForm->_ident, e->_use_def, false); + } else { + Component *comp = (Component*)component; + comp->promote_use_def_info(e->_use_def); + } + // Component positions are zero based. + int pos = _components.operand_position(name); + assert( ! (component->isa(Component::DEF) && (pos >= 1)), + "Component::DEF can only occur in the first position"); + } + } + + // Resolving the interactions between expand rules and TEMPs would + // be complex so simply disallow it. + if (_matrule == NULL && has_temp) { + globalAD->syntax_err(_linenum, "%s: TEMPs without match rule isn't supported\n", _ident); + } + + return; +} + +// Return zero-based position in component list; -1 if not in list. +int InstructForm::operand_position(const char *name, int usedef) { + return unique_opnds_idx(_components.operand_position(name, usedef)); +} + +int InstructForm::operand_position_format(const char *name) { + return unique_opnds_idx(_components.operand_position_format(name)); +} + +// Return zero-based position in component list; -1 if not in list. +int InstructForm::label_position() { + return unique_opnds_idx(_components.label_position()); +} + +int InstructForm::method_position() { + return unique_opnds_idx(_components.method_position()); +} + +// Return number of relocation entries needed for this instruction. +uint InstructForm::reloc(FormDict &globals) { + uint reloc_entries = 0; + // Check for "Call" nodes + if ( is_ideal_call() ) ++reloc_entries; + if ( is_ideal_return() ) ++reloc_entries; + if ( is_ideal_safepoint() ) ++reloc_entries; + + + // Check if operands MAYBE oop pointers, by checking for ConP elements + // Proceed through the leaves of the match-tree and check for ConPs + if ( _matrule != NULL ) { + uint position = 0; + const char *result = NULL; + const char *name = NULL; + const char *opType = NULL; + while (_matrule->base_operand(position, globals, result, name, opType)) { + if ( strcmp(opType,"ConP") == 0 ) { +#ifdef SPARC + reloc_entries += 2; // 1 for sethi + 1 for setlo +#else + ++reloc_entries; +#endif + } + ++position; + } + } + + // Above is only a conservative estimate + // because it did not check contents of operand classes. + // !!!!! !!!!! + // Add 1 to reloc info for each operand class in the component list. + Component *comp; + _components.reset(); + while ( (comp = _components.iter()) != NULL ) { + const Form *form = globals[comp->_type]; + assert( form, "Did not find component's type in global names"); + const OpClassForm *opc = form->is_opclass(); + const OperandForm *oper = form->is_operand(); + if ( opc && (oper == NULL) ) { + ++reloc_entries; + } else if ( oper ) { + // floats and doubles loaded out of method's constant pool require reloc info + Form::DataType type = oper->is_base_constant(globals); + if ( (type == Form::idealF) || (type == Form::idealD) ) { + ++reloc_entries; + } + } + } + + // Float and Double constants may come from the CodeBuffer table + // and require relocatable addresses for access + // !!!!! + // Check for any component being an immediate float or double. + Form::DataType data_type = is_chain_of_constant(globals); + if( data_type==idealD || data_type==idealF ) { +#ifdef SPARC + // sparc required more relocation entries for floating constants + // (expires 9/98) + reloc_entries += 6; +#else + reloc_entries++; +#endif + } + + return reloc_entries; +} + +// Utility function defined in archDesc.cpp +extern bool is_def(int usedef); + +// Return the result of reducing an instruction +const char *InstructForm::reduce_result() { + const char* result = "Universe"; // default + _components.reset(); + Component *comp = _components.iter(); + if (comp != NULL && comp->isa(Component::DEF)) { + result = comp->_type; + // Override this if the rule is a store operation: + if (_matrule && _matrule->_rChild && + is_store_to_memory(_matrule->_rChild->_opType)) + result = "Universe"; + } + return result; +} + +// Return the name of the operand on the right hand side of the binary match +// Return NULL if there is no right hand side +const char *InstructForm::reduce_right(FormDict &globals) const { + if( _matrule == NULL ) return NULL; + return _matrule->reduce_right(globals); +} + +// Similar for left +const char *InstructForm::reduce_left(FormDict &globals) const { + if( _matrule == NULL ) return NULL; + return _matrule->reduce_left(globals); +} + + +// Base class for this instruction, MachNode except for calls +const char *InstructForm::mach_base_class() const { + if( is_ideal_call() == Form::JAVA_STATIC ) { + return "MachCallStaticJavaNode"; + } + else if( is_ideal_call() == Form::JAVA_DYNAMIC ) { + return "MachCallDynamicJavaNode"; + } + else if( is_ideal_call() == Form::JAVA_RUNTIME ) { + return "MachCallRuntimeNode"; + } + else if( is_ideal_call() == Form::JAVA_LEAF ) { + return "MachCallLeafNode"; + } + else if (is_ideal_return()) { + return "MachReturnNode"; + } + else if (is_ideal_halt()) { + return "MachHaltNode"; + } + else if (is_ideal_safepoint()) { + return "MachSafePointNode"; + } + else if (is_ideal_if()) { + return "MachIfNode"; + } + else if (is_ideal_fastlock()) { + return "MachFastLockNode"; + } + else if (is_ideal_nop()) { + return "MachNopNode"; + } + else if (captures_bottom_type()) { + return "MachTypeNode"; + } else { + return "MachNode"; + } + assert( false, "ShouldNotReachHere()"); + return NULL; +} + +// Compare the instruction predicates for textual equality +bool equivalent_predicates( const InstructForm *instr1, const InstructForm *instr2 ) { + const Predicate *pred1 = instr1->_predicate; + const Predicate *pred2 = instr2->_predicate; + if( pred1 == NULL && pred2 == NULL ) { + // no predicates means they are identical + return true; + } + if( pred1 != NULL && pred2 != NULL ) { + // compare the predicates + const char *str1 = pred1->_pred; + const char *str2 = pred2->_pred; + if( (str1 == NULL && str2 == NULL) + || (str1 != NULL && str2 != NULL && strcmp(str1,str2) == 0) ) { + return true; + } + } + + return false; +} + +// Check if this instruction can cisc-spill to 'alternate' +bool InstructForm::cisc_spills_to(ArchDesc &AD, InstructForm *instr) { + assert( _matrule != NULL && instr->_matrule != NULL, "must have match rules"); + // Do not replace if a cisc-version has been found. + if( cisc_spill_operand() != Not_cisc_spillable ) return false; + + int cisc_spill_operand = Maybe_cisc_spillable; + char *result = NULL; + char *result2 = NULL; + const char *op_name = NULL; + const char *reg_type = NULL; + FormDict &globals = AD.globalNames(); + cisc_spill_operand = _matrule->cisc_spill_match(globals, AD.get_registers(), instr->_matrule, op_name, reg_type); + if( (cisc_spill_operand != Not_cisc_spillable) && (op_name != NULL) && equivalent_predicates(this, instr) ) { + cisc_spill_operand = operand_position(op_name, Component::USE); + int def_oper = operand_position(op_name, Component::DEF); + if( def_oper == NameList::Not_in_list && instr->num_opnds() == num_opnds()) { + // Do not support cisc-spilling for destination operands and + // make sure they have the same number of operands. + _cisc_spill_alternate = instr; + instr->set_cisc_alternate(true); + if( AD._cisc_spill_debug ) { + fprintf(stderr, "Instruction %s cisc-spills-to %s\n", _ident, instr->_ident); + fprintf(stderr, " using operand %s %s at index %d\n", reg_type, op_name, cisc_spill_operand); + } + // Record that a stack-version of the reg_mask is needed + // !!!!! + OperandForm *oper = (OperandForm*)(globals[reg_type]->is_operand()); + assert( oper != NULL, "cisc-spilling non operand"); + const char *reg_class_name = oper->constrained_reg_class(); + AD.set_stack_or_reg(reg_class_name); + const char *reg_mask_name = AD.reg_mask(*oper); + set_cisc_reg_mask_name(reg_mask_name); + const char *stack_or_reg_mask_name = AD.stack_or_reg_mask(*oper); + } else { + cisc_spill_operand = Not_cisc_spillable; + } + } else { + cisc_spill_operand = Not_cisc_spillable; + } + + set_cisc_spill_operand(cisc_spill_operand); + return (cisc_spill_operand != Not_cisc_spillable); +} + +// Check to see if this instruction can be replaced with the short branch +// instruction `short-branch' +bool InstructForm::check_branch_variant(ArchDesc &AD, InstructForm *short_branch) { + if (_matrule != NULL && + this != short_branch && // Don't match myself + !is_short_branch() && // Don't match another short branch variant + reduce_result() != NULL && + strcmp(reduce_result(), short_branch->reduce_result()) == 0 && + _matrule->equivalent(AD.globalNames(), short_branch->_matrule)) { + // The instructions are equivalent. + if (AD._short_branch_debug) { + fprintf(stderr, "Instruction %s has short form %s\n", _ident, short_branch->_ident); + } + _short_branch_form = short_branch; + return true; + } + return false; +} + + +// --------------------------- FILE *output_routines +// +// Generate the format call for the replacement variable +void InstructForm::rep_var_format(FILE *fp, const char *rep_var) { + // Find replacement variable's type + const Form *form = _localNames[rep_var]; + if (form == NULL) { + fprintf(stderr, "unknown replacement variable in format statement: '%s'\n", rep_var); + assert(false, "ShouldNotReachHere()"); + } + OpClassForm *opc = form->is_opclass(); + assert( opc, "replacement variable was not found in local names"); + // Lookup the index position of the replacement variable + int idx = operand_position_format(rep_var); + if ( idx == -1 ) { + assert( strcmp(opc->_ident,"label")==0, "Unimplemented"); + assert( false, "ShouldNotReachHere()"); + } + + if (is_noninput_operand(idx)) { + // This component isn't in the input array. Print out the static + // name of the register. + OperandForm* oper = form->is_operand(); + if (oper != NULL && oper->is_bound_register()) { + const RegDef* first = oper->get_RegClass()->find_first_elem(); + fprintf(fp, " tty->print(\"%s\");\n", first->_regname); + } else { + globalAD->syntax_err(_linenum, "In %s can't find format for %s %s", _ident, opc->_ident, rep_var); + } + } else { + // Output the format call for this operand + fprintf(fp,"opnd_array(%d)->",idx); + if (idx == 0) + fprintf(fp,"int_format(ra, this, st); // %s\n", rep_var); + else + fprintf(fp,"ext_format(ra, this,idx%d, st); // %s\n", idx, rep_var ); + } +} + +// Seach through operands to determine parameters unique positions. +void InstructForm::set_unique_opnds() { + uint* uniq_idx = NULL; + uint nopnds = num_opnds(); + uint num_uniq = nopnds; + uint i; + if ( nopnds > 0 ) { + // Allocate index array with reserve. + uniq_idx = (uint*) malloc(sizeof(uint)*(nopnds + 2)); + for( i = 0; i < nopnds+2; i++ ) { + uniq_idx[i] = i; + } + } + // Do it only if there is a match rule and no expand rule. With an + // expand rule it is done by creating new mach node in Expand() + // method. + if ( nopnds > 0 && _matrule != NULL && _exprule == NULL ) { + const char *name; + uint count; + bool has_dupl_use = false; + + _parameters.reset(); + while( (name = _parameters.iter()) != NULL ) { + count = 0; + uint position = 0; + uint uniq_position = 0; + _components.reset(); + Component *comp = NULL; + if( sets_result() ) { + comp = _components.iter(); + position++; + } + // The next code is copied from the method operand_position(). + for (; (comp = _components.iter()) != NULL; ++position) { + // When the first component is not a DEF, + // leave space for the result operand! + if ( position==0 && (! comp->isa(Component::DEF)) ) { + ++position; + } + if( strcmp(name, comp->_name)==0 ) { + if( ++count > 1 ) { + uniq_idx[position] = uniq_position; + has_dupl_use = true; + } else { + uniq_position = position; + } + } + if( comp->isa(Component::DEF) + && comp->isa(Component::USE) ) { + ++position; + if( position != 1 ) + --position; // only use two slots for the 1st USE_DEF + } + } + } + if( has_dupl_use ) { + for( i = 1; i < nopnds; i++ ) + if( i != uniq_idx[i] ) + break; + int j = i; + for( ; i < nopnds; i++ ) + if( i == uniq_idx[i] ) + uniq_idx[i] = j++; + num_uniq = j; + } + } + _uniq_idx = uniq_idx; + _num_uniq = num_uniq; +} + +// Generate index values needed for determing the operand position +void InstructForm::index_temps(FILE *fp, FormDict &globals, const char *prefix, const char *receiver) { + uint idx = 0; // position of operand in match rule + int cur_num_opnds = num_opnds(); + + // Compute the index into vector of operand pointers: + // idx0=0 is used to indicate that info comes from this same node, not from input edge. + // idx1 starts at oper_input_base() + if ( cur_num_opnds >= 1 ) { + fprintf(fp," // Start at oper_input_base() and count operands\n"); + fprintf(fp," unsigned %sidx0 = %d;\n", prefix, oper_input_base(globals)); + fprintf(fp," unsigned %sidx1 = %d;\n", prefix, oper_input_base(globals)); + + // Generate starting points for other unique operands if they exist + for ( idx = 2; idx < num_unique_opnds(); ++idx ) { + if( *receiver == 0 ) { + fprintf(fp," unsigned %sidx%d = %sidx%d + opnd_array(%d)->num_edges();\n", + prefix, idx, prefix, idx-1, idx-1 ); + } else { + fprintf(fp," unsigned %sidx%d = %sidx%d + %s_opnds[%d]->num_edges();\n", + prefix, idx, prefix, idx-1, receiver, idx-1 ); + } + } + } + if( *receiver != 0 ) { + // This value is used by generate_peepreplace when copying a node. + // Don't emit it in other cases since it can hide bugs with the + // use invalid idx's. + fprintf(fp," unsigned %sidx%d = %sreq(); \n", prefix, idx, receiver); + } + +} + +// --------------------------- +bool InstructForm::verify() { + // !!!!! !!!!! + // Check that a "label" operand occurs last in the operand list, if present + return true; +} + +void InstructForm::dump() { + output(stderr); +} + +void InstructForm::output(FILE *fp) { + fprintf(fp,"\nInstruction: %s\n", (_ident?_ident:"")); + if (_matrule) _matrule->output(fp); + if (_insencode) _insencode->output(fp); + if (_opcode) _opcode->output(fp); + if (_attribs) _attribs->output(fp); + if (_predicate) _predicate->output(fp); + if (_effects.Size()) { + fprintf(fp,"Effects\n"); + _effects.dump(); + } + if (_exprule) _exprule->output(fp); + if (_rewrule) _rewrule->output(fp); + if (_format) _format->output(fp); + if (_peephole) _peephole->output(fp); +} + +void MachNodeForm::dump() { + output(stderr); +} + +void MachNodeForm::output(FILE *fp) { + fprintf(fp,"\nMachNode: %s\n", (_ident?_ident:"")); +} + +//------------------------------build_predicate-------------------------------- +// Build instruction predicates. If the user uses the same operand name +// twice, we need to check that the operands are pointer-eequivalent in +// the DFA during the labeling process. +Predicate *InstructForm::build_predicate() { + char buf[1024], *s=buf; + Dict names(cmpstr,hashstr,Form::arena); // Map Names to counts + + MatchNode *mnode = + strcmp(_matrule->_opType, "Set") ? _matrule : _matrule->_rChild; + mnode->count_instr_names(names); + + uint first = 1; + // Start with the predicate supplied in the .ad file. + if( _predicate ) { + if( first ) first=0; + strcpy(s,"("); s += strlen(s); + strcpy(s,_predicate->_pred); + s += strlen(s); + strcpy(s,")"); s += strlen(s); + } + for( DictI i(&names); i.test(); ++i ) { + uintptr_t cnt = (uintptr_t)i._value; + if( cnt > 1 ) { // Need a predicate at all? + assert( cnt == 2, "Unimplemented" ); + // Handle many pairs + if( first ) first=0; + else { // All tests must pass, so use '&&' + strcpy(s," && "); + s += strlen(s); + } + // Add predicate to working buffer + sprintf(s,"/*%s*/(",(char*)i._key); + s += strlen(s); + mnode->build_instr_pred(s,(char*)i._key,0); + s += strlen(s); + strcpy(s," == "); s += strlen(s); + mnode->build_instr_pred(s,(char*)i._key,1); + s += strlen(s); + strcpy(s,")"); s += strlen(s); + } + } + if( s == buf ) s = NULL; + else { + assert( strlen(buf) < sizeof(buf), "String buffer overflow" ); + s = strdup(buf); + } + return new Predicate(s); +} + +//------------------------------EncodeForm------------------------------------- +// Constructor +EncodeForm::EncodeForm() + : _encClass(cmpstr,hashstr, Form::arena) { +} +EncodeForm::~EncodeForm() { +} + +// record a new register class +EncClass *EncodeForm::add_EncClass(const char *className) { + EncClass *encClass = new EncClass(className); + _eclasses.addName(className); + _encClass.Insert(className,encClass); + return encClass; +} + +// Lookup the function body for an encoding class +EncClass *EncodeForm::encClass(const char *className) { + assert( className != NULL, "Must provide a defined encoding name"); + + EncClass *encClass = (EncClass*)_encClass[className]; + return encClass; +} + +// Lookup the function body for an encoding class +const char *EncodeForm::encClassBody(const char *className) { + if( className == NULL ) return NULL; + + EncClass *encClass = (EncClass*)_encClass[className]; + assert( encClass != NULL, "Encode Class is missing."); + encClass->_code.reset(); + const char *code = (const char*)encClass->_code.iter(); + assert( code != NULL, "Found an empty encode class body."); + + return code; +} + +// Lookup the function body for an encoding class +const char *EncodeForm::encClassPrototype(const char *className) { + assert( className != NULL, "Encode class name must be non NULL."); + + return className; +} + +void EncodeForm::dump() { // Debug printer + output(stderr); +} + +void EncodeForm::output(FILE *fp) { // Write info to output files + const char *name; + fprintf(fp,"\n"); + fprintf(fp,"-------------------- Dump EncodeForm --------------------\n"); + for (_eclasses.reset(); (name = _eclasses.iter()) != NULL;) { + ((EncClass*)_encClass[name])->output(fp); + } + fprintf(fp,"-------------------- end EncodeForm --------------------\n"); +} +//------------------------------EncClass--------------------------------------- +EncClass::EncClass(const char *name) + : _localNames(cmpstr,hashstr, Form::arena), _name(name) { +} +EncClass::~EncClass() { +} + +// Add a parameter pair +void EncClass::add_parameter(const char *parameter_type, const char *parameter_name) { + _parameter_type.addName( parameter_type ); + _parameter_name.addName( parameter_name ); +} + +// Verify operand types in parameter list +bool EncClass::check_parameter_types(FormDict &globals) { + // !!!!! + return false; +} + +// Add the decomposed "code" sections of an encoding's code-block +void EncClass::add_code(const char *code) { + _code.addName(code); +} + +// Add the decomposed "replacement variables" of an encoding's code-block +void EncClass::add_rep_var(char *replacement_var) { + _code.addName(NameList::_signal); + _rep_vars.addName(replacement_var); +} + +// Lookup the function body for an encoding class +int EncClass::rep_var_index(const char *rep_var) { + uint position = 0; + const char *name = NULL; + + _parameter_name.reset(); + while ( (name = _parameter_name.iter()) != NULL ) { + if ( strcmp(rep_var,name) == 0 ) return position; + ++position; + } + + return -1; +} + +// Check after parsing +bool EncClass::verify() { + // 1!!!! + // Check that each replacement variable, '$name' in architecture description + // is actually a local variable for this encode class, or a reserved name + // "primary, secondary, tertiary" + return true; +} + +void EncClass::dump() { + output(stderr); +} + +// Write info to output files +void EncClass::output(FILE *fp) { + fprintf(fp,"EncClass: %s", (_name ? _name : "")); + + // Output the parameter list + _parameter_type.reset(); + _parameter_name.reset(); + const char *type = _parameter_type.iter(); + const char *name = _parameter_name.iter(); + fprintf(fp, " ( "); + for ( ; (type != NULL) && (name != NULL); + (type = _parameter_type.iter()), (name = _parameter_name.iter()) ) { + fprintf(fp, " %s %s,", type, name); + } + fprintf(fp, " ) "); + + // Output the code block + _code.reset(); + _rep_vars.reset(); + const char *code; + while ( (code = _code.iter()) != NULL ) { + if ( _code.is_signal(code) ) { + // A replacement variable + const char *rep_var = _rep_vars.iter(); + fprintf(fp,"($%s)", rep_var); + } else { + // A section of code + fprintf(fp,"%s", code); + } + } + +} + +//------------------------------Opcode----------------------------------------- +Opcode::Opcode(char *primary, char *secondary, char *tertiary) + : _primary(primary), _secondary(secondary), _tertiary(tertiary) { +} + +Opcode::~Opcode() { +} + +Opcode::opcode_type Opcode::as_opcode_type(const char *param) { + if( strcmp(param,"primary") == 0 ) { + return Opcode::PRIMARY; + } + else if( strcmp(param,"secondary") == 0 ) { + return Opcode::SECONDARY; + } + else if( strcmp(param,"tertiary") == 0 ) { + return Opcode::TERTIARY; + } + return Opcode::NOT_AN_OPCODE; +} + +void Opcode::print_opcode(FILE *fp, Opcode::opcode_type desired_opcode) { + // Default values previously provided by MachNode::primary()... + const char *description = "default_opcode()"; + const char *value = "-1"; + // Check if user provided any opcode definitions + if( this != NULL ) { + // Update 'value' if user provided a definition in the instruction + switch (desired_opcode) { + case PRIMARY: + description = "primary()"; + if( _primary != NULL) { value = _primary; } + break; + case SECONDARY: + description = "secondary()"; + if( _secondary != NULL ) { value = _secondary; } + break; + case TERTIARY: + description = "tertiary()"; + if( _tertiary != NULL ) { value = _tertiary; } + break; + default: + assert( false, "ShouldNotReachHere();"); + break; + } + } + fprintf(fp, "(%s /*%s*/)", value, description); +} + +void Opcode::dump() { + output(stderr); +} + +// Write info to output files +void Opcode::output(FILE *fp) { + if (_primary != NULL) fprintf(fp,"Primary opcode: %s\n", _primary); + if (_secondary != NULL) fprintf(fp,"Secondary opcode: %s\n", _secondary); + if (_tertiary != NULL) fprintf(fp,"Tertiary opcode: %s\n", _tertiary); +} + +//------------------------------InsEncode-------------------------------------- +InsEncode::InsEncode() { +} +InsEncode::~InsEncode() { +} + +// Add "encode class name" and its parameters +NameAndList *InsEncode::add_encode(char *encoding) { + assert( encoding != NULL, "Must provide name for encoding"); + + // add_parameter(NameList::_signal); + NameAndList *encode = new NameAndList(encoding); + _encoding.addName((char*)encode); + + return encode; +} + +// Access the list of encodings +void InsEncode::reset() { + _encoding.reset(); + // _parameter.reset(); +} +const char* InsEncode::encode_class_iter() { + NameAndList *encode_class = (NameAndList*)_encoding.iter(); + return ( encode_class != NULL ? encode_class->name() : NULL ); +} +// Obtain parameter name from zero based index +const char *InsEncode::rep_var_name(InstructForm &inst, uint param_no) { + NameAndList *params = (NameAndList*)_encoding.current(); + assert( params != NULL, "Internal Error"); + const char *param = (*params)[param_no]; + + // Remove '$' if parser placed it there. + return ( param != NULL && *param == '$') ? (param+1) : param; +} + +void InsEncode::dump() { + output(stderr); +} + +// Write info to output files +void InsEncode::output(FILE *fp) { + NameAndList *encoding = NULL; + const char *parameter = NULL; + + fprintf(fp,"InsEncode: "); + _encoding.reset(); + + while ( (encoding = (NameAndList*)_encoding.iter()) != 0 ) { + // Output the encoding being used + fprintf(fp,"%s(", encoding->name() ); + + // Output its parameter list, if any + bool first_param = true; + encoding->reset(); + while ( (parameter = encoding->iter()) != 0 ) { + // Output the ',' between parameters + if ( ! first_param ) fprintf(fp,", "); + first_param = false; + // Output the parameter + fprintf(fp,"%s", parameter); + } // done with parameters + fprintf(fp,") "); + } // done with encodings + + fprintf(fp,"\n"); +} + +//------------------------------Effect----------------------------------------- +static int effect_lookup(const char *name) { + if(!strcmp(name, "USE")) return Component::USE; + if(!strcmp(name, "DEF")) return Component::DEF; + if(!strcmp(name, "USE_DEF")) return Component::USE_DEF; + if(!strcmp(name, "KILL")) return Component::KILL; + if(!strcmp(name, "USE_KILL")) return Component::USE_KILL; + if(!strcmp(name, "TEMP")) return Component::TEMP; + if(!strcmp(name, "INVALID")) return Component::INVALID; + assert( false,"Invalid effect name specified\n"); + return Component::INVALID; +} + +Effect::Effect(const char *name) : _name(name), _use_def(effect_lookup(name)) { + _ftype = Form::EFF; +} +Effect::~Effect() { +} + +// Dynamic type check +Effect *Effect::is_effect() const { + return (Effect*)this; +} + + +// True if this component is equal to the parameter. +bool Effect::is(int use_def_kill_enum) const { + return (_use_def == use_def_kill_enum ? true : false); +} +// True if this component is used/def'd/kill'd as the parameter suggests. +bool Effect::isa(int use_def_kill_enum) const { + return (_use_def & use_def_kill_enum) == use_def_kill_enum; +} + +void Effect::dump() { + output(stderr); +} + +void Effect::output(FILE *fp) { // Write info to output files + fprintf(fp,"Effect: %s\n", (_name?_name:"")); +} + +//------------------------------ExpandRule------------------------------------- +ExpandRule::ExpandRule() : _expand_instrs(), + _newopconst(cmpstr, hashstr, Form::arena) { + _ftype = Form::EXP; +} + +ExpandRule::~ExpandRule() { // Destructor +} + +void ExpandRule::add_instruction(NameAndList *instruction_name_and_operand_list) { + _expand_instrs.addName((char*)instruction_name_and_operand_list); +} + +void ExpandRule::reset_instructions() { + _expand_instrs.reset(); +} + +NameAndList* ExpandRule::iter_instructions() { + return (NameAndList*)_expand_instrs.iter(); +} + + +void ExpandRule::dump() { + output(stderr); +} + +void ExpandRule::output(FILE *fp) { // Write info to output files + NameAndList *expand_instr = NULL; + const char *opid = NULL; + + fprintf(fp,"\nExpand Rule:\n"); + + // Iterate over the instructions 'node' expands into + for(reset_instructions(); (expand_instr = iter_instructions()) != NULL; ) { + fprintf(fp,"%s(", expand_instr->name()); + + // iterate over the operand list + for( expand_instr->reset(); (opid = expand_instr->iter()) != NULL; ) { + fprintf(fp,"%s ", opid); + } + fprintf(fp,");\n"); + } +} + +//------------------------------RewriteRule------------------------------------ +RewriteRule::RewriteRule(char* params, char* block) + : _tempParams(params), _tempBlock(block) { }; // Constructor +RewriteRule::~RewriteRule() { // Destructor +} + +void RewriteRule::dump() { + output(stderr); +} + +void RewriteRule::output(FILE *fp) { // Write info to output files + fprintf(fp,"\nRewrite Rule:\n%s\n%s\n", + (_tempParams?_tempParams:""), + (_tempBlock?_tempBlock:"")); +} + + +//==============================MachNodes====================================== +//------------------------------MachNodeForm----------------------------------- +MachNodeForm::MachNodeForm(char *id) + : _ident(id) { +} + +MachNodeForm::~MachNodeForm() { +} + +MachNodeForm *MachNodeForm::is_machnode() const { + return (MachNodeForm*)this; +} + +//==============================Operand Classes================================ +//------------------------------OpClassForm------------------------------------ +OpClassForm::OpClassForm(const char* id) : _ident(id) { + _ftype = Form::OPCLASS; +} + +OpClassForm::~OpClassForm() { +} + +bool OpClassForm::ideal_only() const { return 0; } + +OpClassForm *OpClassForm::is_opclass() const { + return (OpClassForm*)this; +} + +Form::InterfaceType OpClassForm::interface_type(FormDict &globals) const { + if( _oplst.count() == 0 ) return Form::no_interface; + + // Check that my operands have the same interface type + Form::InterfaceType interface; + bool first = true; + NameList &op_list = (NameList &)_oplst; + op_list.reset(); + const char *op_name; + while( (op_name = op_list.iter()) != NULL ) { + const Form *form = globals[op_name]; + OperandForm *operand = form->is_operand(); + assert( operand, "Entry in operand class that is not an operand"); + if( first ) { + first = false; + interface = operand->interface_type(globals); + } else { + interface = (interface == operand->interface_type(globals) ? interface : Form::no_interface); + } + } + return interface; +} + +bool OpClassForm::stack_slots_only(FormDict &globals) const { + if( _oplst.count() == 0 ) return false; // how? + + NameList &op_list = (NameList &)_oplst; + op_list.reset(); + const char *op_name; + while( (op_name = op_list.iter()) != NULL ) { + const Form *form = globals[op_name]; + OperandForm *operand = form->is_operand(); + assert( operand, "Entry in operand class that is not an operand"); + if( !operand->stack_slots_only(globals) ) return false; + } + return true; +} + + +void OpClassForm::dump() { + output(stderr); +} + +void OpClassForm::output(FILE *fp) { + const char *name; + fprintf(fp,"\nOperand Class: %s\n", (_ident?_ident:"")); + fprintf(fp,"\nCount = %d\n", _oplst.count()); + for(_oplst.reset(); (name = _oplst.iter()) != NULL;) { + fprintf(fp,"%s, ",name); + } + fprintf(fp,"\n"); +} + + +//==============================Operands======================================= +//------------------------------OperandForm------------------------------------ +OperandForm::OperandForm(const char* id) + : OpClassForm(id), _ideal_only(false), + _localNames(cmpstr, hashstr, Form::arena) { + _ftype = Form::OPER; + + _matrule = NULL; + _interface = NULL; + _attribs = NULL; + _predicate = NULL; + _constraint= NULL; + _construct = NULL; + _format = NULL; +} +OperandForm::OperandForm(const char* id, bool ideal_only) + : OpClassForm(id), _ideal_only(ideal_only), + _localNames(cmpstr, hashstr, Form::arena) { + _ftype = Form::OPER; + + _matrule = NULL; + _interface = NULL; + _attribs = NULL; + _predicate = NULL; + _constraint= NULL; + _construct = NULL; + _format = NULL; +} +OperandForm::~OperandForm() { +} + + +OperandForm *OperandForm::is_operand() const { + return (OperandForm*)this; +} + +bool OperandForm::ideal_only() const { + return _ideal_only; +} + +Form::InterfaceType OperandForm::interface_type(FormDict &globals) const { + if( _interface == NULL ) return Form::no_interface; + + return _interface->interface_type(globals); +} + + +bool OperandForm::stack_slots_only(FormDict &globals) const { + if( _constraint == NULL ) return false; + return _constraint->stack_slots_only(); +} + + +// Access op_cost attribute or return NULL. +const char* OperandForm::cost() { + for (Attribute* cur = _attribs; cur != NULL; cur = (Attribute*)cur->_next) { + if( strcmp(cur->_ident,AttributeForm::_op_cost) == 0 ) { + return cur->_val; + } + } + return NULL; +} + +// Return the number of leaves below this complex operand +uint OperandForm::num_leaves() const { + if ( ! _matrule) return 0; + + int num_leaves = _matrule->_numleaves; + return num_leaves; +} + +// Return the number of constants contained within this complex operand +uint OperandForm::num_consts(FormDict &globals) const { + if ( ! _matrule) return 0; + + // This is a recursive invocation on all operands in the matchrule + return _matrule->num_consts(globals); +} + +// Return the number of constants in match rule with specified type +uint OperandForm::num_consts(FormDict &globals, Form::DataType type) const { + if ( ! _matrule) return 0; + + // This is a recursive invocation on all operands in the matchrule + return _matrule->num_consts(globals, type); +} + +// Return the number of pointer constants contained within this complex operand +uint OperandForm::num_const_ptrs(FormDict &globals) const { + if ( ! _matrule) return 0; + + // This is a recursive invocation on all operands in the matchrule + return _matrule->num_const_ptrs(globals); +} + +uint OperandForm::num_edges(FormDict &globals) const { + uint edges = 0; + uint leaves = num_leaves(); + uint consts = num_consts(globals); + + // If we are matching a constant directly, there are no leaves. + edges = ( leaves > consts ) ? leaves - consts : 0; + + // !!!!! + // Special case operands that do not have a corresponding ideal node. + if( (edges == 0) && (consts == 0) ) { + if( constrained_reg_class() != NULL ) { + edges = 1; + } else { + if( _matrule + && (_matrule->_lChild == NULL) && (_matrule->_rChild == NULL) ) { + const Form *form = globals[_matrule->_opType]; + OperandForm *oper = form ? form->is_operand() : NULL; + if( oper ) { + return oper->num_edges(globals); + } + } + } + } + + return edges; +} + + +// Check if this operand is usable for cisc-spilling +bool OperandForm::is_cisc_reg(FormDict &globals) const { + const char *ideal = ideal_type(globals); + bool is_cisc_reg = (ideal && (ideal_to_Reg_type(ideal) != none)); + return is_cisc_reg; +} + +bool OpClassForm::is_cisc_mem(FormDict &globals) const { + Form::InterfaceType my_interface = interface_type(globals); + return (my_interface == memory_interface); +} + + +// node matches ideal 'Bool' +bool OperandForm::is_ideal_bool() const { + if( _matrule == NULL ) return false; + + return _matrule->is_ideal_bool(); +} + +// Require user's name for an sRegX to be stackSlotX +Form::DataType OperandForm::is_user_name_for_sReg() const { + DataType data_type = none; + if( _ident != NULL ) { + if( strcmp(_ident,"stackSlotI") == 0 ) data_type = Form::idealI; + else if( strcmp(_ident,"stackSlotP") == 0 ) data_type = Form::idealP; + else if( strcmp(_ident,"stackSlotD") == 0 ) data_type = Form::idealD; + else if( strcmp(_ident,"stackSlotF") == 0 ) data_type = Form::idealF; + else if( strcmp(_ident,"stackSlotL") == 0 ) data_type = Form::idealL; + } + assert((data_type == none) || (_matrule == NULL), "No match-rule for stackSlotX"); + + return data_type; +} + + +// Return ideal type, if there is a single ideal type for this operand +const char *OperandForm::ideal_type(FormDict &globals, RegisterForm *registers) const { + const char *type = NULL; + if (ideal_only()) type = _ident; + else if( _matrule == NULL ) { + // Check for condition code register + const char *rc_name = constrained_reg_class(); + // !!!!! + if (rc_name == NULL) return NULL; + // !!!!! !!!!! + // Check constraints on result's register class + if( registers ) { + RegClass *reg_class = registers->getRegClass(rc_name); + assert( reg_class != NULL, "Register class is not defined"); + + // Check for ideal type of entries in register class, all are the same type + reg_class->reset(); + RegDef *reg_def = reg_class->RegDef_iter(); + assert( reg_def != NULL, "No entries in register class"); + assert( reg_def->_idealtype != NULL, "Did not define ideal type for register"); + // Return substring that names the register's ideal type + type = reg_def->_idealtype + 3; + assert( *(reg_def->_idealtype + 0) == 'O', "Expect Op_ prefix"); + assert( *(reg_def->_idealtype + 1) == 'p', "Expect Op_ prefix"); + assert( *(reg_def->_idealtype + 2) == '_', "Expect Op_ prefix"); + } + } + else if( _matrule->_lChild == NULL && _matrule->_rChild == NULL ) { + // This operand matches a single type, at the top level. + // Check for ideal type + type = _matrule->_opType; + if( strcmp(type,"Bool") == 0 ) + return "Bool"; + // transitive lookup + const Form *frm = globals[type]; + OperandForm *op = frm->is_operand(); + type = op->ideal_type(globals, registers); + } + return type; +} + + +// If there is a single ideal type for this interface field, return it. +const char *OperandForm::interface_ideal_type(FormDict &globals, + const char *field) const { + const char *ideal_type = NULL; + const char *value = NULL; + + // Check if "field" is valid for this operand's interface + if ( ! is_interface_field(field, value) ) return ideal_type; + + // !!!!! !!!!! !!!!! + // If a valid field has a constant value, identify "ConI" or "ConP" or ... + + // Else, lookup type of field's replacement variable + + return ideal_type; +} + + +RegClass* OperandForm::get_RegClass() const { + if (_interface && !_interface->is_RegInterface()) return NULL; + return globalAD->get_registers()->getRegClass(constrained_reg_class()); +} + + +bool OperandForm::is_bound_register() const { + RegClass *reg_class = get_RegClass(); + if (reg_class == NULL) return false; + + const char * name = ideal_type(globalAD->globalNames()); + if (name == NULL) return false; + + int size = 0; + if (strcmp(name,"RegFlags")==0) size = 1; + if (strcmp(name,"RegI")==0) size = 1; + if (strcmp(name,"RegF")==0) size = 1; + if (strcmp(name,"RegD")==0) size = 2; + if (strcmp(name,"RegL")==0) size = 2; + if (strcmp(name,"RegP")==0) size = globalAD->get_preproc_def("_LP64") ? 2 : 1; + if (size == 0) return false; + return size == reg_class->size(); +} + + +// Check if this is a valid field for this operand, +// Return 'true' if valid, and set the value to the string the user provided. +bool OperandForm::is_interface_field(const char *field, + const char * &value) const { + return false; +} + + +// Return register class name if a constraint specifies the register class. +const char *OperandForm::constrained_reg_class() const { + const char *reg_class = NULL; + if ( _constraint ) { + // !!!!! + Constraint *constraint = _constraint; + if ( strcmp(_constraint->_func,"ALLOC_IN_RC") == 0 ) { + reg_class = _constraint->_arg; + } + } + + return reg_class; +} + + +// Return the register class associated with 'leaf'. +const char *OperandForm::in_reg_class(uint leaf, FormDict &globals) { + const char *reg_class = NULL; // "RegMask::Empty"; + + if((_matrule == NULL) || (_matrule->is_chain_rule(globals))) { + reg_class = constrained_reg_class(); + return reg_class; + } + const char *result = NULL; + const char *name = NULL; + const char *type = NULL; + // iterate through all base operands + // until we reach the register that corresponds to "leaf" + // This function is not looking for an ideal type. It needs the first + // level user type associated with the leaf. + for(uint idx = 0;_matrule->base_operand(idx,globals,result,name,type);++idx) { + const Form *form = (_localNames[name] ? _localNames[name] : globals[result]); + OperandForm *oper = form ? form->is_operand() : NULL; + if( oper ) { + reg_class = oper->constrained_reg_class(); + if( reg_class ) { + reg_class = reg_class; + } else { + // ShouldNotReachHere(); + } + } else { + // ShouldNotReachHere(); + } + + // Increment our target leaf position if current leaf is not a candidate. + if( reg_class == NULL) ++leaf; + // Exit the loop with the value of reg_class when at the correct index + if( idx == leaf ) break; + // May iterate through all base operands if reg_class for 'leaf' is NULL + } + return reg_class; +} + + +// Recursive call to construct list of top-level operands. +// Implementation does not modify state of internal structures +void OperandForm::build_components() { + if (_matrule) _matrule->append_components(_localNames, _components); + + // Add parameters that "do not appear in match rule". + const char *name; + for (_parameters.reset(); (name = _parameters.iter()) != NULL;) { + OperandForm *opForm = (OperandForm*)_localNames[name]; + + if ( _components.operand_position(name) == -1 ) { + _components.insert(name, opForm->_ident, Component::INVALID, false); + } + } + + return; +} + +int OperandForm::operand_position(const char *name, int usedef) { + return _components.operand_position(name, usedef); +} + + +// Return zero-based position in component list, only counting constants; +// Return -1 if not in list. +int OperandForm::constant_position(FormDict &globals, const Component *last) { + // Iterate through components and count constants preceeding 'constant' + uint position = 0; + Component *comp; + _components.reset(); + while( (comp = _components.iter()) != NULL && (comp != last) ) { + // Special case for operands that take a single user-defined operand + // Skip the initial definition in the component list. + if( strcmp(comp->_name,this->_ident) == 0 ) continue; + + const char *type = comp->_type; + // Lookup operand form for replacement variable's type + const Form *form = globals[type]; + assert( form != NULL, "Component's type not found"); + OperandForm *oper = form ? form->is_operand() : NULL; + if( oper ) { + if( oper->_matrule->is_base_constant(globals) != Form::none ) { + ++position; + } + } + } + + // Check for being passed a component that was not in the list + if( comp != last ) position = -1; + + return position; +} +// Provide position of constant by "name" +int OperandForm::constant_position(FormDict &globals, const char *name) { + const Component *comp = _components.search(name); + int idx = constant_position( globals, comp ); + + return idx; +} + + +// Return zero-based position in component list, only counting constants; +// Return -1 if not in list. +int OperandForm::register_position(FormDict &globals, const char *reg_name) { + // Iterate through components and count registers preceeding 'last' + uint position = 0; + Component *comp; + _components.reset(); + while( (comp = _components.iter()) != NULL + && (strcmp(comp->_name,reg_name) != 0) ) { + // Special case for operands that take a single user-defined operand + // Skip the initial definition in the component list. + if( strcmp(comp->_name,this->_ident) == 0 ) continue; + + const char *type = comp->_type; + // Lookup operand form for component's type + const Form *form = globals[type]; + assert( form != NULL, "Component's type not found"); + OperandForm *oper = form ? form->is_operand() : NULL; + if( oper ) { + if( oper->_matrule->is_base_register(globals) ) { + ++position; + } + } + } + + return position; +} + + +const char *OperandForm::reduce_result() const { + return _ident; +} +// Return the name of the operand on the right hand side of the binary match +// Return NULL if there is no right hand side +const char *OperandForm::reduce_right(FormDict &globals) const { + return ( _matrule ? _matrule->reduce_right(globals) : NULL ); +} + +// Similar for left +const char *OperandForm::reduce_left(FormDict &globals) const { + return ( _matrule ? _matrule->reduce_left(globals) : NULL ); +} + + +// --------------------------- FILE *output_routines +// +// Output code for disp_is_oop, if true. +void OperandForm::disp_is_oop(FILE *fp, FormDict &globals) { + // Check it is a memory interface with a non-user-constant disp field + if ( this->_interface == NULL ) return; + MemInterface *mem_interface = this->_interface->is_MemInterface(); + if ( mem_interface == NULL ) return; + const char *disp = mem_interface->_disp; + if ( *disp != '$' ) return; + + // Lookup replacement variable in operand's component list + const char *rep_var = disp + 1; + const Component *comp = this->_components.search(rep_var); + assert( comp != NULL, "Replacement variable not found in components"); + // Lookup operand form for replacement variable's type + const char *type = comp->_type; + Form *form = (Form*)globals[type]; + assert( form != NULL, "Replacement variable's type not found"); + OperandForm *op = form->is_operand(); + assert( op, "Memory Interface 'disp' can only emit an operand form"); + // Check if this is a ConP, which may require relocation + if ( op->is_base_constant(globals) == Form::idealP ) { + // Find the constant's index: _c0, _c1, _c2, ... , _cN + uint idx = op->constant_position( globals, rep_var); + fprintf(fp," virtual bool disp_is_oop() const {", _ident); + fprintf(fp, " return _c%d->isa_oop_ptr();", idx); + fprintf(fp, " }\n"); + } +} + +// Generate code for internal and external format methods +// +// internal access to reg# node->_idx +// access to subsumed constant _c0, _c1, +void OperandForm::int_format(FILE *fp, FormDict &globals, uint index) { + Form::DataType dtype; + if (_matrule && (_matrule->is_base_register(globals) || + strcmp(ideal_type(globalAD->globalNames()), "RegFlags") == 0)) { + // !!!!! !!!!! + fprintf(fp, "{ char reg_str[128];\n"); + fprintf(fp," ra->dump_register(node,reg_str);\n"); + fprintf(fp," tty->print(\"%cs\",reg_str);\n",'%'); + fprintf(fp," }\n"); + } else if (_matrule && (dtype = _matrule->is_base_constant(globals)) != Form::none) { + format_constant( fp, index, dtype ); + } else if (ideal_to_sReg_type(_ident) != Form::none) { + // Special format for Stack Slot Register + fprintf(fp, "{ char reg_str[128];\n"); + fprintf(fp," ra->dump_register(node,reg_str);\n"); + fprintf(fp," tty->print(\"%cs\",reg_str);\n",'%'); + fprintf(fp," }\n"); + } else { + fprintf(fp,"tty->print(\"No format defined for %s\n\");\n", _ident); + fflush(fp); + fprintf(stderr,"No format defined for %s\n", _ident); + dump(); + assert( false,"Internal error:\n output_internal_operand() attempting to output other than a Register or Constant"); + } +} + +// Similar to "int_format" but for cases where data is external to operand +// external access to reg# node->in(idx)->_idx, +void OperandForm::ext_format(FILE *fp, FormDict &globals, uint index) { + Form::DataType dtype; + if (_matrule && (_matrule->is_base_register(globals) || + strcmp(ideal_type(globalAD->globalNames()), "RegFlags") == 0)) { + fprintf(fp, "{ char reg_str[128];\n"); + fprintf(fp," ra->dump_register(node->in(idx"); + if ( index != 0 ) fprintf(fp, "+%d",index); + fprintf(fp, "),reg_str);\n"); + fprintf(fp," tty->print(\"%cs\",reg_str);\n",'%'); + fprintf(fp," }\n"); + } else if (_matrule && (dtype = _matrule->is_base_constant(globals)) != Form::none) { + format_constant( fp, index, dtype ); + } else if (ideal_to_sReg_type(_ident) != Form::none) { + // Special format for Stack Slot Register + fprintf(fp, "{ char reg_str[128];\n"); + fprintf(fp," ra->dump_register(node->in(idx"); + if ( index != 0 ) fprintf(fp, "+%d",index); + fprintf(fp, "),reg_str);\n"); + fprintf(fp," tty->print(\"%cs\",reg_str);\n",'%'); + fprintf(fp," }\n"); + } else { + fprintf(fp,"tty->print(\"No format defined for %s\n\");\n", _ident); + assert( false,"Internal error:\n output_external_operand() attempting to output other than a Register or Constant"); + } +} + +void OperandForm::format_constant(FILE *fp, uint const_index, uint const_type) { + switch(const_type) { + case Form::idealI: fprintf(fp,"st->print(\"#%%d\", _c%d);\n", const_index); break; + case Form::idealP: fprintf(fp,"_c%d->dump_on(st);\n", const_index); break; + case Form::idealL: fprintf(fp,"st->print(\"#%%lld\", _c%d);\n", const_index); break; + case Form::idealF: fprintf(fp,"st->print(\"#%%f\", _c%d);\n", const_index); break; + case Form::idealD: fprintf(fp,"st->print(\"#%%f\", _c%d);\n", const_index); break; + default: + assert( false, "ShouldNotReachHere()"); + } +} + +// Return the operand form corresponding to the given index, else NULL. +OperandForm *OperandForm::constant_operand(FormDict &globals, + uint index) { + // !!!!! + // Check behavior on complex operands + uint n_consts = num_consts(globals); + if( n_consts > 0 ) { + uint i = 0; + const char *type; + Component *comp; + _components.reset(); + if ((comp = _components.iter()) == NULL) { + assert(n_consts == 1, "Bad component list detected.\n"); + // Current operand is THE operand + if ( index == 0 ) { + return this; + } + } // end if NULL + else { + // Skip the first component, it can not be a DEF of a constant + do { + type = comp->base_type(globals); + // Check that "type" is a 'ConI', 'ConP', ... + if ( ideal_to_const_type(type) != Form::none ) { + // When at correct component, get corresponding Operand + if ( index == 0 ) { + return globals[comp->_type]->is_operand(); + } + // Decrement number of constants to go + --index; + } + } while((comp = _components.iter()) != NULL); + } + } + + // Did not find a constant for this index. + return NULL; +} + +// If this operand has a single ideal type, return its type +Form::DataType OperandForm::simple_type(FormDict &globals) const { + const char *type_name = ideal_type(globals); + Form::DataType type = type_name ? ideal_to_const_type( type_name ) + : Form::none; + return type; +} + +Form::DataType OperandForm::is_base_constant(FormDict &globals) const { + if ( _matrule == NULL ) return Form::none; + + return _matrule->is_base_constant(globals); +} + +// "true" if this operand is a simple type that is swallowed +bool OperandForm::swallowed(FormDict &globals) const { + Form::DataType type = simple_type(globals); + if( type != Form::none ) { + return true; + } + + return false; +} + +// Output code to access the value of the index'th constant +void OperandForm::access_constant(FILE *fp, FormDict &globals, + uint const_index) { + OperandForm *oper = constant_operand(globals, const_index); + assert( oper, "Index exceeds number of constants in operand"); + Form::DataType dtype = oper->is_base_constant(globals); + + switch(dtype) { + case idealI: fprintf(fp,"_c%d", const_index); break; + case idealP: fprintf(fp,"_c%d->get_con()",const_index); break; + case idealL: fprintf(fp,"_c%d", const_index); break; + case idealF: fprintf(fp,"_c%d", const_index); break; + case idealD: fprintf(fp,"_c%d", const_index); break; + default: + assert( false, "ShouldNotReachHere()"); + } +} + + +void OperandForm::dump() { + output(stderr); +} + +void OperandForm::output(FILE *fp) { + fprintf(fp,"\nOperand: %s\n", (_ident?_ident:"")); + if (_matrule) _matrule->dump(); + if (_interface) _interface->dump(); + if (_attribs) _attribs->dump(); + if (_predicate) _predicate->dump(); + if (_constraint) _constraint->dump(); + if (_construct) _construct->dump(); + if (_format) _format->dump(); +} + +//------------------------------Constraint------------------------------------- +Constraint::Constraint(const char *func, const char *arg) + : _func(func), _arg(arg) { +} +Constraint::~Constraint() { /* not owner of char* */ +} + +bool Constraint::stack_slots_only() const { + return strcmp(_func, "ALLOC_IN_RC") == 0 + && strcmp(_arg, "stack_slots") == 0; +} + +void Constraint::dump() { + output(stderr); +} + +void Constraint::output(FILE *fp) { // Write info to output files + assert((_func != NULL && _arg != NULL),"missing constraint function or arg"); + fprintf(fp,"Constraint: %s ( %s )\n", _func, _arg); +} + +//------------------------------Predicate-------------------------------------- +Predicate::Predicate(char *pr) + : _pred(pr) { +} +Predicate::~Predicate() { +} + +void Predicate::dump() { + output(stderr); +} + +void Predicate::output(FILE *fp) { + fprintf(fp,"Predicate"); // Write to output files +} +//------------------------------Interface-------------------------------------- +Interface::Interface(const char *name) : _name(name) { +} +Interface::~Interface() { +} + +Form::InterfaceType Interface::interface_type(FormDict &globals) const { + Interface *thsi = (Interface*)this; + if ( thsi->is_RegInterface() ) return Form::register_interface; + if ( thsi->is_MemInterface() ) return Form::memory_interface; + if ( thsi->is_ConstInterface() ) return Form::constant_interface; + if ( thsi->is_CondInterface() ) return Form::conditional_interface; + + return Form::no_interface; +} + +RegInterface *Interface::is_RegInterface() { + if ( strcmp(_name,"REG_INTER") != 0 ) + return NULL; + return (RegInterface*)this; +} +MemInterface *Interface::is_MemInterface() { + if ( strcmp(_name,"MEMORY_INTER") != 0 ) return NULL; + return (MemInterface*)this; +} +ConstInterface *Interface::is_ConstInterface() { + if ( strcmp(_name,"CONST_INTER") != 0 ) return NULL; + return (ConstInterface*)this; +} +CondInterface *Interface::is_CondInterface() { + if ( strcmp(_name,"COND_INTER") != 0 ) return NULL; + return (CondInterface*)this; +} + + +void Interface::dump() { + output(stderr); +} + +// Write info to output files +void Interface::output(FILE *fp) { + fprintf(fp,"Interface: %s\n", (_name ? _name : "") ); +} + +//------------------------------RegInterface----------------------------------- +RegInterface::RegInterface() : Interface("REG_INTER") { +} +RegInterface::~RegInterface() { +} + +void RegInterface::dump() { + output(stderr); +} + +// Write info to output files +void RegInterface::output(FILE *fp) { + Interface::output(fp); +} + +//------------------------------ConstInterface--------------------------------- +ConstInterface::ConstInterface() : Interface("CONST_INTER") { +} +ConstInterface::~ConstInterface() { +} + +void ConstInterface::dump() { + output(stderr); +} + +// Write info to output files +void ConstInterface::output(FILE *fp) { + Interface::output(fp); +} + +//------------------------------MemInterface----------------------------------- +MemInterface::MemInterface(char *base, char *index, char *scale, char *disp) + : Interface("MEMORY_INTER"), _base(base), _index(index), _scale(scale), _disp(disp) { +} +MemInterface::~MemInterface() { + // not owner of any character arrays +} + +void MemInterface::dump() { + output(stderr); +} + +// Write info to output files +void MemInterface::output(FILE *fp) { + Interface::output(fp); + if ( _base != NULL ) fprintf(fp," base == %s\n", _base); + if ( _index != NULL ) fprintf(fp," index == %s\n", _index); + if ( _scale != NULL ) fprintf(fp," scale == %s\n", _scale); + if ( _disp != NULL ) fprintf(fp," disp == %s\n", _disp); + // fprintf(fp,"\n"); +} + +//------------------------------CondInterface---------------------------------- +CondInterface::CondInterface(char *equal, char *not_equal, + char *less, char *greater_equal, + char *less_equal, char *greater) + : Interface("COND_INTER"), + _equal(equal), _not_equal(not_equal), + _less(less), _greater_equal(greater_equal), + _less_equal(less_equal), _greater(greater) { + // +} +CondInterface::~CondInterface() { + // not owner of any character arrays +} + +void CondInterface::dump() { + output(stderr); +} + +// Write info to output files +void CondInterface::output(FILE *fp) { + Interface::output(fp); + if ( _equal != NULL ) fprintf(fp," equal == %s\n", _equal); + if ( _not_equal != NULL ) fprintf(fp," not_equal == %s\n", _not_equal); + if ( _less != NULL ) fprintf(fp," less == %s\n", _less); + if ( _greater_equal != NULL ) fprintf(fp," greater_equal == %s\n", _greater_equal); + if ( _less_equal != NULL ) fprintf(fp," less_equal == %s\n", _less_equal); + if ( _greater != NULL ) fprintf(fp," greater == %s\n", _greater); + // fprintf(fp,"\n"); +} + +//------------------------------ConstructRule---------------------------------- +ConstructRule::ConstructRule(char *cnstr) + : _construct(cnstr) { +} +ConstructRule::~ConstructRule() { +} + +void ConstructRule::dump() { + output(stderr); +} + +void ConstructRule::output(FILE *fp) { + fprintf(fp,"\nConstruct Rule\n"); // Write to output files +} + + +//==============================Shared Forms=================================== +//------------------------------AttributeForm---------------------------------- +int AttributeForm::_insId = 0; // start counter at 0 +int AttributeForm::_opId = 0; // start counter at 0 +const char* AttributeForm::_ins_cost = "ins_cost"; // required name +const char* AttributeForm::_ins_pc_relative = "ins_pc_relative"; +const char* AttributeForm::_op_cost = "op_cost"; // required name + +AttributeForm::AttributeForm(char *attr, int type, char *attrdef) + : Form(Form::ATTR), _attrname(attr), _atype(type), _attrdef(attrdef) { + if (type==OP_ATTR) { + id = ++_opId; + } + else if (type==INS_ATTR) { + id = ++_insId; + } + else assert( false,""); +} +AttributeForm::~AttributeForm() { +} + +// Dynamic type check +AttributeForm *AttributeForm::is_attribute() const { + return (AttributeForm*)this; +} + + +// inlined // int AttributeForm::type() { return id;} + +void AttributeForm::dump() { + output(stderr); +} + +void AttributeForm::output(FILE *fp) { + if( _attrname && _attrdef ) { + fprintf(fp,"\n// AttributeForm \nstatic const int %s = %s;\n", + _attrname, _attrdef); + } + else { + fprintf(fp,"\n// AttributeForm missing name %s or definition %s\n", + (_attrname?_attrname:""), (_attrdef?_attrdef:"") ); + } +} + +//------------------------------Component-------------------------------------- +Component::Component(const char *name, const char *type, int usedef) + : _name(name), _type(type), _usedef(usedef) { + _ftype = Form::COMP; +} +Component::~Component() { +} + +// True if this component is equal to the parameter. +bool Component::is(int use_def_kill_enum) const { + return (_usedef == use_def_kill_enum ? true : false); +} +// True if this component is used/def'd/kill'd as the parameter suggests. +bool Component::isa(int use_def_kill_enum) const { + return (_usedef & use_def_kill_enum) == use_def_kill_enum; +} + +// Extend this component with additional use/def/kill behavior +int Component::promote_use_def_info(int new_use_def) { + _usedef |= new_use_def; + + return _usedef; +} + +// Check the base type of this component, if it has one +const char *Component::base_type(FormDict &globals) { + const Form *frm = globals[_type]; + if (frm == NULL) return NULL; + OperandForm *op = frm->is_operand(); + if (op == NULL) return NULL; + if (op->ideal_only()) return op->_ident; + return (char *)op->ideal_type(globals); +} + +void Component::dump() { + output(stderr); +} + +void Component::output(FILE *fp) { + fprintf(fp,"Component:"); // Write to output files + fprintf(fp, " name = %s", _name); + fprintf(fp, ", type = %s", _type); + const char * usedef = "Undefined Use/Def info"; + switch (_usedef) { + case USE: usedef = "USE"; break; + case USE_DEF: usedef = "USE_DEF"; break; + case USE_KILL: usedef = "USE_KILL"; break; + case KILL: usedef = "KILL"; break; + case TEMP: usedef = "TEMP"; break; + case DEF: usedef = "DEF"; break; + default: assert(false, "unknown effect"); + } + fprintf(fp, ", use/def = %s\n", usedef); +} + + +//------------------------------ComponentList--------------------------------- +ComponentList::ComponentList() : NameList(), _matchcnt(0) { +} +ComponentList::~ComponentList() { + // // This list may not own its elements if copied via assignment + // Component *component; + // for (reset(); (component = iter()) != NULL;) { + // delete component; + // } +} + +void ComponentList::insert(Component *component, bool mflag) { + NameList::addName((char *)component); + if(mflag) _matchcnt++; +} +void ComponentList::insert(const char *name, const char *opType, int usedef, + bool mflag) { + Component * component = new Component(name, opType, usedef); + insert(component, mflag); +} +Component *ComponentList::current() { return (Component*)NameList::current(); } +Component *ComponentList::iter() { return (Component*)NameList::iter(); } +Component *ComponentList::match_iter() { + if(_iter < _matchcnt) return (Component*)NameList::iter(); + return NULL; +} +Component *ComponentList::post_match_iter() { + Component *comp = iter(); + // At end of list? + if ( comp == NULL ) { + return comp; + } + // In post-match components? + if (_iter > match_count()-1) { + return comp; + } + + return post_match_iter(); +} + +void ComponentList::reset() { NameList::reset(); } +int ComponentList::count() { return NameList::count(); } + +Component *ComponentList::operator[](int position) { + // Shortcut complete iteration if there are not enough entries + if (position >= count()) return NULL; + + int index = 0; + Component *component = NULL; + for (reset(); (component = iter()) != NULL;) { + if (index == position) { + return component; + } + ++index; + } + + return NULL; +} + +const Component *ComponentList::search(const char *name) { + PreserveIter pi(this); + reset(); + for( Component *comp = NULL; ((comp = iter()) != NULL); ) { + if( strcmp(comp->_name,name) == 0 ) return comp; + } + + return NULL; +} + +// Return number of USEs + number of DEFs +// When there are no components, or the first component is a USE, +// then we add '1' to hold a space for the 'result' operand. +int ComponentList::num_operands() { + PreserveIter pi(this); + uint count = 1; // result operand + uint position = 0; + + Component *component = NULL; + for( reset(); (component = iter()) != NULL; ++position ) { + if( component->isa(Component::USE) || + ( position == 0 && (! component->isa(Component::DEF))) ) { + ++count; + } + } + + return count; +} + +// Return zero-based position in list; -1 if not in list. +// if parameter 'usedef' is ::USE, it will match USE, USE_DEF, ... +int ComponentList::operand_position(const char *name, int usedef) { + PreserveIter pi(this); + int position = 0; + int num_opnds = num_operands(); + Component *component; + Component* preceding_non_use = NULL; + Component* first_def = NULL; + for (reset(); (component = iter()) != NULL; ++position) { + // When the first component is not a DEF, + // leave space for the result operand! + if ( position==0 && (! component->isa(Component::DEF)) ) { + ++position; + ++num_opnds; + } + if (strcmp(name, component->_name)==0 && (component->isa(usedef))) { + // When the first entry in the component list is a DEF and a USE + // Treat them as being separate, a DEF first, then a USE + if( position==0 + && usedef==Component::USE && component->isa(Component::DEF) ) { + assert(position+1 < num_opnds, "advertised index in bounds"); + return position+1; + } else { + if( preceding_non_use && strcmp(component->_name, preceding_non_use->_name) ) { + fprintf(stderr, "the name '%s' should not precede the name '%s'\n", preceding_non_use->_name, name); + } + if( position >= num_opnds ) { + fprintf(stderr, "the name '%s' is too late in its name list\n", name); + } + assert(position < num_opnds, "advertised index in bounds"); + return position; + } + } + if( component->isa(Component::DEF) + && component->isa(Component::USE) ) { + ++position; + if( position != 1 ) --position; // only use two slots for the 1st USE_DEF + } + if( component->isa(Component::DEF) && !first_def ) { + first_def = component; + } + if( !component->isa(Component::USE) && component != first_def ) { + preceding_non_use = component; + } else if( preceding_non_use && !strcmp(component->_name, preceding_non_use->_name) ) { + preceding_non_use = NULL; + } + } + return Not_in_list; +} + +// Find position for this name, regardless of use/def information +int ComponentList::operand_position(const char *name) { + PreserveIter pi(this); + int position = 0; + Component *component; + for (reset(); (component = iter()) != NULL; ++position) { + // When the first component is not a DEF, + // leave space for the result operand! + if ( position==0 && (! component->isa(Component::DEF)) ) { + ++position; + } + if (strcmp(name, component->_name)==0) { + return position; + } + if( component->isa(Component::DEF) + && component->isa(Component::USE) ) { + ++position; + if( position != 1 ) --position; // only use two slots for the 1st USE_DEF + } + } + return Not_in_list; +} + +int ComponentList::operand_position_format(const char *name) { + PreserveIter pi(this); + int first_position = operand_position(name); + int use_position = operand_position(name, Component::USE); + + return ((first_position < use_position) ? use_position : first_position); +} + +int ComponentList::label_position() { + PreserveIter pi(this); + int position = 0; + reset(); + for( Component *comp; (comp = iter()) != NULL; ++position) { + // When the first component is not a DEF, + // leave space for the result operand! + if ( position==0 && (! comp->isa(Component::DEF)) ) { + ++position; + } + if (strcmp(comp->_type, "label")==0) { + return position; + } + if( comp->isa(Component::DEF) + && comp->isa(Component::USE) ) { + ++position; + if( position != 1 ) --position; // only use two slots for the 1st USE_DEF + } + } + + return -1; +} + +int ComponentList::method_position() { + PreserveIter pi(this); + int position = 0; + reset(); + for( Component *comp; (comp = iter()) != NULL; ++position) { + // When the first component is not a DEF, + // leave space for the result operand! + if ( position==0 && (! comp->isa(Component::DEF)) ) { + ++position; + } + if (strcmp(comp->_type, "method")==0) { + return position; + } + if( comp->isa(Component::DEF) + && comp->isa(Component::USE) ) { + ++position; + if( position != 1 ) --position; // only use two slots for the 1st USE_DEF + } + } + + return -1; +} + +void ComponentList::dump() { output(stderr); } + +void ComponentList::output(FILE *fp) { + PreserveIter pi(this); + fprintf(fp, "\n"); + Component *component; + for (reset(); (component = iter()) != NULL;) { + component->output(fp); + } + fprintf(fp, "\n"); +} + +//------------------------------MatchNode-------------------------------------- +MatchNode::MatchNode(ArchDesc &ad, const char *result, const char *mexpr, + const char *opType, MatchNode *lChild, MatchNode *rChild) + : _AD(ad), _result(result), _name(mexpr), _opType(opType), + _lChild(lChild), _rChild(rChild), _internalop(0), _numleaves(0), + _commutative_id(0) { + _numleaves = (lChild ? lChild->_numleaves : 0) + + (rChild ? rChild->_numleaves : 0); +} + +MatchNode::MatchNode(ArchDesc &ad, MatchNode& mnode) + : _AD(ad), _result(mnode._result), _name(mnode._name), + _opType(mnode._opType), _lChild(mnode._lChild), _rChild(mnode._rChild), + _internalop(0), _numleaves(mnode._numleaves), + _commutative_id(mnode._commutative_id) { +} + +MatchNode::MatchNode(ArchDesc &ad, MatchNode& mnode, int clone) + : _AD(ad), _result(mnode._result), _name(mnode._name), + _opType(mnode._opType), + _internalop(0), _numleaves(mnode._numleaves), + _commutative_id(mnode._commutative_id) { + if (mnode._lChild) { + _lChild = new MatchNode(ad, *mnode._lChild, clone); + } else { + _lChild = NULL; + } + if (mnode._rChild) { + _rChild = new MatchNode(ad, *mnode._rChild, clone); + } else { + _rChild = NULL; + } +} + +MatchNode::~MatchNode() { + // // This node may not own its children if copied via assignment + // if( _lChild ) delete _lChild; + // if( _rChild ) delete _rChild; +} + +bool MatchNode::find_type(const char *type, int &position) const { + if ( (_lChild != NULL) && (_lChild->find_type(type, position)) ) return true; + if ( (_rChild != NULL) && (_rChild->find_type(type, position)) ) return true; + + if (strcmp(type,_opType)==0) { + return true; + } else { + ++position; + } + return false; +} + +// Recursive call collecting info on top-level operands, not transitive. +// Implementation does not modify state of internal structures. +void MatchNode::append_components(FormDict &locals, ComponentList &components, + bool deflag) const { + int usedef = deflag ? Component::DEF : Component::USE; + FormDict &globals = _AD.globalNames(); + + assert (_name != NULL, "MatchNode::build_components encountered empty node\n"); + // Base case + if (_lChild==NULL && _rChild==NULL) { + // If _opType is not an operation, do not build a component for it ##### + const Form *f = globals[_opType]; + if( f != NULL ) { + // Add non-ideals that are operands, operand-classes, + if( ! f->ideal_only() + && (f->is_opclass() || f->is_operand()) ) { + components.insert(_name, _opType, usedef, true); + } + } + return; + } + // Promote results of "Set" to DEF + bool def_flag = (!strcmp(_opType, "Set")) ? true : false; + if (_lChild) _lChild->append_components(locals, components, def_flag); + def_flag = false; // only applies to component immediately following 'Set' + if (_rChild) _rChild->append_components(locals, components, def_flag); +} + +// Find the n'th base-operand in the match node, +// recursively investigates match rules of user-defined operands. +// +// Implementation does not modify state of internal structures since they +// can be shared. +bool MatchNode::base_operand(uint &position, FormDict &globals, + const char * &result, const char * &name, + const char * &opType) const { + assert (_name != NULL, "MatchNode::base_operand encountered empty node\n"); + // Base case + if (_lChild==NULL && _rChild==NULL) { + // Check for special case: "Universe", "label" + if (strcmp(_opType,"Universe") == 0 || strcmp(_opType,"label")==0 ) { + if (position == 0) { + result = _result; + name = _name; + opType = _opType; + return 1; + } else { + -- position; + return 0; + } + } + + const Form *form = globals[_opType]; + MatchNode *matchNode = NULL; + // Check for user-defined type + if (form) { + // User operand or instruction? + OperandForm *opForm = form->is_operand(); + InstructForm *inForm = form->is_instruction(); + if ( opForm ) { + matchNode = (MatchNode*)opForm->_matrule; + } else if ( inForm ) { + matchNode = (MatchNode*)inForm->_matrule; + } + } + // if this is user-defined, recurse on match rule + // User-defined operand and instruction forms have a match-rule. + if (matchNode) { + return (matchNode->base_operand(position,globals,result,name,opType)); + } else { + // Either not a form, or a system-defined form (no match rule). + if (position==0) { + result = _result; + name = _name; + opType = _opType; + return 1; + } else { + --position; + return 0; + } + } + + } else { + // Examine the left child and right child as well + if (_lChild) { + if (_lChild->base_operand(position, globals, result, name, opType)) + return 1; + } + + if (_rChild) { + if (_rChild->base_operand(position, globals, result, name, opType)) + return 1; + } + } + + return 0; +} + +// Recursive call on all operands' match rules in my match rule. +uint MatchNode::num_consts(FormDict &globals) const { + uint index = 0; + uint num_consts = 0; + const char *result; + const char *name; + const char *opType; + + for (uint position = index; + base_operand(position,globals,result,name,opType); position = index) { + ++index; + if( ideal_to_const_type(opType) ) num_consts++; + } + + return num_consts; +} + +// Recursive call on all operands' match rules in my match rule. +// Constants in match rule subtree with specified type +uint MatchNode::num_consts(FormDict &globals, Form::DataType type) const { + uint index = 0; + uint num_consts = 0; + const char *result; + const char *name; + const char *opType; + + for (uint position = index; + base_operand(position,globals,result,name,opType); position = index) { + ++index; + if( ideal_to_const_type(opType) == type ) num_consts++; + } + + return num_consts; +} + +// Recursive call on all operands' match rules in my match rule. +uint MatchNode::num_const_ptrs(FormDict &globals) const { + return num_consts( globals, Form::idealP ); +} + +bool MatchNode::sets_result() const { + return ( (strcmp(_name,"Set") == 0) ? true : false ); +} + +const char *MatchNode::reduce_right(FormDict &globals) const { + // If there is no right reduction, return NULL. + const char *rightStr = NULL; + + // If we are a "Set", start from the right child. + const MatchNode *const mnode = sets_result() ? + (const MatchNode *const)this->_rChild : + (const MatchNode *const)this; + + // If our right child exists, it is the right reduction + if ( mnode->_rChild ) { + rightStr = mnode->_rChild->_internalop ? mnode->_rChild->_internalop + : mnode->_rChild->_opType; + } + // Else, May be simple chain rule: (Set dst operand_form), rightStr=NULL; + return rightStr; +} + +const char *MatchNode::reduce_left(FormDict &globals) const { + // If there is no left reduction, return NULL. + const char *leftStr = NULL; + + // If we are a "Set", start from the right child. + const MatchNode *const mnode = sets_result() ? + (const MatchNode *const)this->_rChild : + (const MatchNode *const)this; + + // If our left child exists, it is the left reduction + if ( mnode->_lChild ) { + leftStr = mnode->_lChild->_internalop ? mnode->_lChild->_internalop + : mnode->_lChild->_opType; + } else { + // May be simple chain rule: (Set dst operand_form_source) + if ( sets_result() ) { + OperandForm *oper = globals[mnode->_opType]->is_operand(); + if( oper ) { + leftStr = mnode->_opType; + } + } + } + return leftStr; +} + +//------------------------------count_instr_names------------------------------ +// Count occurrences of operands names in the leaves of the instruction +// match rule. +void MatchNode::count_instr_names( Dict &names ) { + if( !this ) return; + if( _lChild ) _lChild->count_instr_names(names); + if( _rChild ) _rChild->count_instr_names(names); + if( !_lChild && !_rChild ) { + uintptr_t cnt = (uintptr_t)names[_name]; + cnt++; // One more name found + names.Insert(_name,(void*)cnt); + } +} + +//------------------------------build_instr_pred------------------------------- +// Build a path to 'name' in buf. Actually only build if cnt is zero, so we +// can skip some leading instances of 'name'. +int MatchNode::build_instr_pred( char *buf, const char *name, int cnt ) { + if( _lChild ) { + if( !cnt ) strcpy( buf, "_kids[0]->" ); + cnt = _lChild->build_instr_pred( buf+strlen(buf), name, cnt ); + if( cnt < 0 ) return cnt; // Found it, all done + } + if( _rChild ) { + if( !cnt ) strcpy( buf, "_kids[1]->" ); + cnt = _rChild->build_instr_pred( buf+strlen(buf), name, cnt ); + if( cnt < 0 ) return cnt; // Found it, all done + } + if( !_lChild && !_rChild ) { // Found a leaf + // Wrong name? Give up... + if( strcmp(name,_name) ) return cnt; + if( !cnt ) strcpy(buf,"_leaf"); + return cnt-1; + } + return cnt; +} + + +//------------------------------build_internalop------------------------------- +// Build string representation of subtree +void MatchNode::build_internalop( ) { + char *iop, *subtree; + const char *lstr, *rstr; + // Build string representation of subtree + // Operation lchildType rchildType + int len = (int)strlen(_opType) + 4; + lstr = (_lChild) ? ((_lChild->_internalop) ? + _lChild->_internalop : _lChild->_opType) : ""; + rstr = (_rChild) ? ((_rChild->_internalop) ? + _rChild->_internalop : _rChild->_opType) : ""; + len += (int)strlen(lstr) + (int)strlen(rstr); + subtree = (char *)malloc(len); + sprintf(subtree,"_%s_%s_%s", _opType, lstr, rstr); + // Hash the subtree string in _internalOps; if a name exists, use it + iop = (char *)_AD._internalOps[subtree]; + // Else create a unique name, and add it to the hash table + if (iop == NULL) { + iop = subtree; + _AD._internalOps.Insert(subtree, iop); + _AD._internalOpNames.addName(iop); + _AD._internalMatch.Insert(iop, this); + } + // Add the internal operand name to the MatchNode + _internalop = iop; + _result = iop; +} + + +void MatchNode::dump() { + output(stderr); +} + +void MatchNode::output(FILE *fp) { + if (_lChild==0 && _rChild==0) { + fprintf(fp," %s",_name); // operand + } + else { + fprintf(fp," (%s ",_name); // " (opcodeName " + if(_lChild) _lChild->output(fp); // left operand + if(_rChild) _rChild->output(fp); // right operand + fprintf(fp,")"); // ")" + } +} + +int MatchNode::needs_ideal_memory_edge(FormDict &globals) const { + static const char *needs_ideal_memory_list[] = { + "StoreI","StoreL","StoreP","StoreD","StoreF" , + "StoreB","StoreC","Store" ,"StoreFP", + "LoadI" ,"LoadL", "LoadP" ,"LoadD" ,"LoadF" , + "LoadB" ,"LoadC" ,"LoadS" ,"Load" , + "Store4I","Store2I","Store2L","Store2D","Store4F","Store2F","Store16B", + "Store8B","Store4B","Store8C","Store4C","Store2C", + "Load4I" ,"Load2I" ,"Load2L" ,"Load2D" ,"Load4F" ,"Load2F" ,"Load16B" , + "Load8B" ,"Load4B" ,"Load8C" ,"Load4C" ,"Load2C" ,"Load8S", "Load4S","Load2S", + "LoadRange", "LoadKlass", "LoadL_unaligned", "LoadD_unaligned", + "LoadPLocked", "LoadLLocked", + "StorePConditional", "StoreLConditional", + "CompareAndSwapI", "CompareAndSwapL", "CompareAndSwapP", + "StoreCM", + "ClearArray" + }; + int cnt = sizeof(needs_ideal_memory_list)/sizeof(char*); + if( strcmp(_opType,"PrefetchRead")==0 || strcmp(_opType,"PrefetchWrite")==0 ) + return 1; + if( _lChild ) { + const char *opType = _lChild->_opType; + for( int i=0; ineeds_ideal_memory_edge(globals) ) + return 1; + } + if( _rChild ) { + const char *opType = _rChild->_opType; + for( int i=0; ineeds_ideal_memory_edge(globals) ) + return 1; + } + + return 0; +} + +// TRUE if defines a derived oop, and so needs a base oop edge present +// post-matching. +int MatchNode::needs_base_oop_edge() const { + if( !strcmp(_opType,"AddP") ) return 1; + if( strcmp(_opType,"Set") ) return 0; + return !strcmp(_rChild->_opType,"AddP"); +} + +int InstructForm::needs_base_oop_edge(FormDict &globals) const { + if( is_simple_chain_rule(globals) ) { + const char *src = _matrule->_rChild->_opType; + OperandForm *src_op = globals[src]->is_operand(); + assert( src_op, "Not operand class of chain rule" ); + return src_op->_matrule ? src_op->_matrule->needs_base_oop_edge() : 0; + } // Else check instruction + + return _matrule ? _matrule->needs_base_oop_edge() : 0; +} + + +//-------------------------cisc spilling methods------------------------------- +// helper routines and methods for detecting cisc-spilling instructions +//-------------------------cisc_spill_merge------------------------------------ +int MatchNode::cisc_spill_merge(int left_spillable, int right_spillable) { + int cisc_spillable = Maybe_cisc_spillable; + + // Combine results of left and right checks + if( (left_spillable == Maybe_cisc_spillable) && (right_spillable == Maybe_cisc_spillable) ) { + // neither side is spillable, nor prevents cisc spilling + cisc_spillable = Maybe_cisc_spillable; + } + else if( (left_spillable == Maybe_cisc_spillable) && (right_spillable > Maybe_cisc_spillable) ) { + // right side is spillable + cisc_spillable = right_spillable; + } + else if( (right_spillable == Maybe_cisc_spillable) && (left_spillable > Maybe_cisc_spillable) ) { + // left side is spillable + cisc_spillable = left_spillable; + } + else if( (left_spillable == Not_cisc_spillable) || (right_spillable == Not_cisc_spillable) ) { + // left or right prevents cisc spilling this instruction + cisc_spillable = Not_cisc_spillable; + } + else { + // Only allow one to spill + cisc_spillable = Not_cisc_spillable; + } + + return cisc_spillable; +} + +//-------------------------root_ops_match-------------------------------------- +bool static root_ops_match(FormDict &globals, const char *op1, const char *op2) { + // Base Case: check that the current operands/operations match + assert( op1, "Must have op's name"); + assert( op2, "Must have op's name"); + const Form *form1 = globals[op1]; + const Form *form2 = globals[op2]; + + return (form1 == form2); +} + +//-------------------------cisc_spill_match------------------------------------ +// Recursively check two MatchRules for legal conversion via cisc-spilling +int MatchNode::cisc_spill_match(FormDict &globals, RegisterForm *registers, MatchNode *mRule2, const char * &operand, const char * ®_type) { + int cisc_spillable = Maybe_cisc_spillable; + int left_spillable = Maybe_cisc_spillable; + int right_spillable = Maybe_cisc_spillable; + + // Check that each has same number of operands at this level + if( (_lChild && !(mRule2->_lChild)) || (_rChild && !(mRule2->_rChild)) ) + return Not_cisc_spillable; + + // Base Case: check that the current operands/operations match + // or are CISC spillable + assert( _opType, "Must have _opType"); + assert( mRule2->_opType, "Must have _opType"); + const Form *form = globals[_opType]; + const Form *form2 = globals[mRule2->_opType]; + if( form == form2 ) { + cisc_spillable = Maybe_cisc_spillable; + } else { + const InstructForm *form2_inst = form2 ? form2->is_instruction() : NULL; + const char *name_left = mRule2->_lChild ? mRule2->_lChild->_opType : NULL; + const char *name_right = mRule2->_rChild ? mRule2->_rChild->_opType : NULL; + // Detect reg vs (loadX memory) + if( form->is_cisc_reg(globals) + && form2_inst + && (is_load_from_memory(mRule2->_opType) != Form::none) // reg vs. (load memory) + && (name_left != NULL) // NOT (load) + && (name_right == NULL) ) { // NOT (load memory foo) + const Form *form2_left = name_left ? globals[name_left] : NULL; + if( form2_left && form2_left->is_cisc_mem(globals) ) { + cisc_spillable = Is_cisc_spillable; + operand = _name; + reg_type = _result; + return Is_cisc_spillable; + } else { + cisc_spillable = Not_cisc_spillable; + } + } + // Detect reg vs memory + else if( form->is_cisc_reg(globals) && form2->is_cisc_mem(globals) ) { + cisc_spillable = Is_cisc_spillable; + operand = _name; + reg_type = _result; + return Is_cisc_spillable; + } else { + cisc_spillable = Not_cisc_spillable; + } + } + + // If cisc is still possible, check rest of tree + if( cisc_spillable == Maybe_cisc_spillable ) { + // Check that each has same number of operands at this level + if( (_lChild && !(mRule2->_lChild)) || (_rChild && !(mRule2->_rChild)) ) return Not_cisc_spillable; + + // Check left operands + if( (_lChild == NULL) && (mRule2->_lChild == NULL) ) { + left_spillable = Maybe_cisc_spillable; + } else { + left_spillable = _lChild->cisc_spill_match(globals, registers, mRule2->_lChild, operand, reg_type); + } + + // Check right operands + if( (_rChild == NULL) && (mRule2->_rChild == NULL) ) { + right_spillable = Maybe_cisc_spillable; + } else { + right_spillable = _rChild->cisc_spill_match(globals, registers, mRule2->_rChild, operand, reg_type); + } + + // Combine results of left and right checks + cisc_spillable = cisc_spill_merge(left_spillable, right_spillable); + } + + return cisc_spillable; +} + +//---------------------------cisc_spill_match---------------------------------- +// Recursively check two MatchRules for legal conversion via cisc-spilling +// This method handles the root of Match tree, +// general recursive checks done in MatchNode +int MatchRule::cisc_spill_match(FormDict &globals, RegisterForm *registers, + MatchRule *mRule2, const char * &operand, + const char * ®_type) { + int cisc_spillable = Maybe_cisc_spillable; + int left_spillable = Maybe_cisc_spillable; + int right_spillable = Maybe_cisc_spillable; + + // Check that each sets a result + if( !(sets_result() && mRule2->sets_result()) ) return Not_cisc_spillable; + // Check that each has same number of operands at this level + if( (_lChild && !(mRule2->_lChild)) || (_rChild && !(mRule2->_rChild)) ) return Not_cisc_spillable; + + // Check left operands: at root, must be target of 'Set' + if( (_lChild == NULL) || (mRule2->_lChild == NULL) ) { + left_spillable = Not_cisc_spillable; + } else { + // Do not support cisc-spilling instruction's target location + if( root_ops_match(globals, _lChild->_opType, mRule2->_lChild->_opType) ) { + left_spillable = Maybe_cisc_spillable; + } else { + left_spillable = Not_cisc_spillable; + } + } + + // Check right operands: recursive walk to identify reg->mem operand + if( (_rChild == NULL) && (mRule2->_rChild == NULL) ) { + right_spillable = Maybe_cisc_spillable; + } else { + right_spillable = _rChild->cisc_spill_match(globals, registers, mRule2->_rChild, operand, reg_type); + } + + // Combine results of left and right checks + cisc_spillable = cisc_spill_merge(left_spillable, right_spillable); + + return cisc_spillable; +} + +//----------------------------- equivalent ------------------------------------ +// Recursively check to see if two match rules are equivalent. +// This rule handles the root. +bool MatchRule::equivalent(FormDict &globals, MatchRule *mRule2) { + // Check that each sets a result + if (sets_result() != mRule2->sets_result()) { + return false; + } + + // Check that the current operands/operations match + assert( _opType, "Must have _opType"); + assert( mRule2->_opType, "Must have _opType"); + const Form *form = globals[_opType]; + const Form *form2 = globals[mRule2->_opType]; + if( form != form2 ) { + return false; + } + + if (_lChild ) { + if( !_lChild->equivalent(globals, mRule2->_lChild) ) + return false; + } else if (mRule2->_lChild) { + return false; // I have NULL left child, mRule2 has non-NULL left child. + } + + if (_rChild ) { + if( !_rChild->equivalent(globals, mRule2->_rChild) ) + return false; + } else if (mRule2->_rChild) { + return false; // I have NULL right child, mRule2 has non-NULL right child. + } + + // We've made it through the gauntlet. + return true; +} + +//----------------------------- equivalent ------------------------------------ +// Recursively check to see if two match rules are equivalent. +// This rule handles the operands. +bool MatchNode::equivalent(FormDict &globals, MatchNode *mNode2) { + if( !mNode2 ) + return false; + + // Check that the current operands/operations match + assert( _opType, "Must have _opType"); + assert( mNode2->_opType, "Must have _opType"); + const Form *form = globals[_opType]; + const Form *form2 = globals[mNode2->_opType]; + return (form == form2); +} + +//-------------------------- has_commutative_op ------------------------------- +// Recursively check for commutative operations with subtree operands +// which could be swapped. +void MatchNode::count_commutative_op(int& count) { + static const char *commut_op_list[] = { + "AddI","AddL","AddF","AddD", + "AndI","AndL", + "MaxI","MinI", + "MulI","MulL","MulF","MulD", + "OrI" ,"OrL" , + "XorI","XorL" + }; + int cnt = sizeof(commut_op_list)/sizeof(char*); + + if( _lChild && _rChild && (_lChild->_lChild || _rChild->_lChild) ) { + // Don't swap if right operand is an immediate constant. + bool is_const = false; + if( _rChild->_lChild == NULL && _rChild->_rChild == NULL ) { + FormDict &globals = _AD.globalNames(); + const Form *form = globals[_rChild->_opType]; + if ( form ) { + OperandForm *oper = form->is_operand(); + if( oper && oper->interface_type(globals) == Form::constant_interface ) + is_const = true; + } + } + if( !is_const ) { + for( int i=0; i 0 + break; + } + } + } + } + if( _lChild ) + _lChild->count_commutative_op(count); + if( _rChild ) + _rChild->count_commutative_op(count); +} + +//-------------------------- swap_commutative_op ------------------------------ +// Recursively swap specified commutative operation with subtree operands. +void MatchNode::swap_commutative_op(bool atroot, int id) { + if( _commutative_id == id ) { // id should be > 0 + assert(_lChild && _rChild && (_lChild->_lChild || _rChild->_lChild ), + "not swappable operation"); + MatchNode* tmp = _lChild; + _lChild = _rChild; + _rChild = tmp; + // Don't exit here since we need to build internalop. + } + + bool is_set = ( strcmp(_opType, "Set") == 0 ); + if( _lChild ) + _lChild->swap_commutative_op(is_set, id); + if( _rChild ) + _rChild->swap_commutative_op(is_set, id); + + // If not the root, reduce this subtree to an internal operand + if( !atroot && (_lChild || _rChild) ) { + build_internalop(); + } +} + +//-------------------------- swap_commutative_op ------------------------------ +// Recursively swap specified commutative operation with subtree operands. +void MatchRule::swap_commutative_op(const char* instr_ident, int count, int& match_rules_cnt) { + assert(match_rules_cnt < 100," too many match rule clones"); + // Clone + MatchRule* clone = new MatchRule(_AD, this); + // Swap operands of commutative operation + ((MatchNode*)clone)->swap_commutative_op(true, count); + char* buf = (char*) malloc(strlen(instr_ident) + 4); + sprintf(buf, "%s_%d", instr_ident, match_rules_cnt++); + clone->_result = buf; + + clone->_next = this->_next; + this-> _next = clone; + if( (--count) > 0 ) { + this-> swap_commutative_op(instr_ident, count, match_rules_cnt); + clone->swap_commutative_op(instr_ident, count, match_rules_cnt); + } +} + +//------------------------------MatchRule-------------------------------------- +MatchRule::MatchRule(ArchDesc &ad) + : MatchNode(ad), _depth(0), _construct(NULL), _numchilds(0) { + _next = NULL; +} + +MatchRule::MatchRule(ArchDesc &ad, MatchRule* mRule) + : MatchNode(ad, *mRule, 0), _depth(mRule->_depth), + _construct(mRule->_construct), _numchilds(mRule->_numchilds) { + _next = NULL; +} + +MatchRule::MatchRule(ArchDesc &ad, MatchNode* mroot, int depth, char *cnstr, + int numleaves) + : MatchNode(ad,*mroot), _depth(depth), _construct(cnstr), + _numchilds(0) { + _next = NULL; + mroot->_lChild = NULL; + mroot->_rChild = NULL; + delete mroot; + _numleaves = numleaves; + _numchilds = (_lChild ? 1 : 0) + (_rChild ? 1 : 0); +} +MatchRule::~MatchRule() { +} + +// Recursive call collecting info on top-level operands, not transitive. +// Implementation does not modify state of internal structures. +void MatchRule::append_components(FormDict &locals, ComponentList &components) const { + assert (_name != NULL, "MatchNode::build_components encountered empty node\n"); + + MatchNode::append_components(locals, components, + false /* not necessarily a def */); +} + +// Recursive call on all operands' match rules in my match rule. +// Implementation does not modify state of internal structures since they +// can be shared. +// The MatchNode that is called first treats its +bool MatchRule::base_operand(uint &position0, FormDict &globals, + const char *&result, const char * &name, + const char * &opType)const{ + uint position = position0; + + return (MatchNode::base_operand( position, globals, result, name, opType)); +} + + +bool MatchRule::is_base_register(FormDict &globals) const { + uint position = 1; + const char *result = NULL; + const char *name = NULL; + const char *opType = NULL; + if (!base_operand(position, globals, result, name, opType)) { + position = 0; + if( base_operand(position, globals, result, name, opType) && + (strcmp(opType,"RegI")==0 || + strcmp(opType,"RegP")==0 || + strcmp(opType,"RegL")==0 || + strcmp(opType,"RegF")==0 || + strcmp(opType,"RegD")==0 || + strcmp(opType,"Reg" )==0) ) { + return 1; + } + } + return 0; +} + +Form::DataType MatchRule::is_base_constant(FormDict &globals) const { + uint position = 1; + const char *result = NULL; + const char *name = NULL; + const char *opType = NULL; + if (!base_operand(position, globals, result, name, opType)) { + position = 0; + if (base_operand(position, globals, result, name, opType)) { + return ideal_to_const_type(opType); + } + } + return Form::none; +} + +bool MatchRule::is_chain_rule(FormDict &globals) const { + + // Check for chain rule, and do not generate a match list for it + if ((_lChild == NULL) && (_rChild == NULL) ) { + const Form *form = globals[_opType]; + // If this is ideal, then it is a base match, not a chain rule. + if ( form && form->is_operand() && (!form->ideal_only())) { + return true; + } + } + // Check for "Set" form of chain rule, and do not generate a match list + if (_rChild) { + const char *rch = _rChild->_opType; + const Form *form = globals[rch]; + if ((!strcmp(_opType,"Set") && + ((form) && form->is_operand()))) { + return true; + } + } + return false; +} + +int MatchRule::is_ideal_copy() const { + if( _rChild ) { + const char *opType = _rChild->_opType; + if( strcmp(opType,"CastII")==0 ) + return 1; + // Do not treat *CastPP this way, because it + // may transfer a raw pointer to an oop. + // If the register allocator were to coalesce this + // into a single LRG, the GC maps would be incorrect. + //if( strcmp(opType,"CastPP")==0 ) + // return 1; + //if( strcmp(opType,"CheckCastPP")==0 ) + // return 1; + // + // Do not treat CastX2P or CastP2X this way, because + // raw pointers and int types are treated differently + // when saving local & stack info for safepoints in + // Output(). + //if( strcmp(opType,"CastX2P")==0 ) + // return 1; + //if( strcmp(opType,"CastP2X")==0 ) + // return 1; + } + if( is_chain_rule(_AD.globalNames()) && + _lChild && strncmp(_lChild->_opType,"stackSlot",9)==0 ) + return 1; + return 0; +} + + +int MatchRule::is_expensive() const { + if( _rChild ) { + const char *opType = _rChild->_opType; + if( strcmp(opType,"AtanD")==0 || + strcmp(opType,"CosD")==0 || + strcmp(opType,"DivD")==0 || + strcmp(opType,"DivF")==0 || + strcmp(opType,"DivI")==0 || + strcmp(opType,"ExpD")==0 || + strcmp(opType,"LogD")==0 || + strcmp(opType,"Log10D")==0 || + strcmp(opType,"ModD")==0 || + strcmp(opType,"ModF")==0 || + strcmp(opType,"ModI")==0 || + strcmp(opType,"PowD")==0 || + strcmp(opType,"SinD")==0 || + strcmp(opType,"SqrtD")==0 || + strcmp(opType,"TanD")==0 || + strcmp(opType,"ConvD2F")==0 || + strcmp(opType,"ConvD2I")==0 || + strcmp(opType,"ConvD2L")==0 || + strcmp(opType,"ConvF2D")==0 || + strcmp(opType,"ConvF2I")==0 || + strcmp(opType,"ConvF2L")==0 || + strcmp(opType,"ConvI2D")==0 || + strcmp(opType,"ConvI2F")==0 || + strcmp(opType,"ConvI2L")==0 || + strcmp(opType,"ConvL2D")==0 || + strcmp(opType,"ConvL2F")==0 || + strcmp(opType,"ConvL2I")==0 || + strcmp(opType,"RoundDouble")==0 || + strcmp(opType,"RoundFloat")==0 || + strcmp(opType,"ReverseBytesI")==0 || + strcmp(opType,"ReverseBytesL")==0 || + strcmp(opType,"Replicate16B")==0 || + strcmp(opType,"Replicate8B")==0 || + strcmp(opType,"Replicate4B")==0 || + strcmp(opType,"Replicate8C")==0 || + strcmp(opType,"Replicate4C")==0 || + strcmp(opType,"Replicate8S")==0 || + strcmp(opType,"Replicate4S")==0 || + strcmp(opType,"Replicate4I")==0 || + strcmp(opType,"Replicate2I")==0 || + strcmp(opType,"Replicate2L")==0 || + strcmp(opType,"Replicate4F")==0 || + strcmp(opType,"Replicate2F")==0 || + strcmp(opType,"Replicate2D")==0 || + 0 /* 0 to line up columns nicely */ ) + return 1; + } + return 0; +} + +bool MatchRule::is_ideal_unlock() const { + if( !_opType ) return false; + return !strcmp(_opType,"Unlock") || !strcmp(_opType,"FastUnlock"); +} + + +bool MatchRule::is_ideal_call_leaf() const { + if( !_opType ) return false; + return !strcmp(_opType,"CallLeaf") || + !strcmp(_opType,"CallLeafNoFP"); +} + + +bool MatchRule::is_ideal_if() const { + if( !_opType ) return false; + return + !strcmp(_opType,"If" ) || + !strcmp(_opType,"CountedLoopEnd"); +} + +bool MatchRule::is_ideal_fastlock() const { + if ( _opType && (strcmp(_opType,"Set") == 0) && _rChild ) { + return (strcmp(_rChild->_opType,"FastLock") == 0); + } + return false; +} + +bool MatchRule::is_ideal_membar() const { + if( !_opType ) return false; + return + !strcmp(_opType,"MemBarAcquire" ) || + !strcmp(_opType,"MemBarRelease" ) || + !strcmp(_opType,"MemBarVolatile" ) || + !strcmp(_opType,"MemBarCPUOrder" ) ; +} + +bool MatchRule::is_ideal_loadPC() const { + if ( _opType && (strcmp(_opType,"Set") == 0) && _rChild ) { + return (strcmp(_rChild->_opType,"LoadPC") == 0); + } + return false; +} + +bool MatchRule::is_ideal_box() const { + if ( _opType && (strcmp(_opType,"Set") == 0) && _rChild ) { + return (strcmp(_rChild->_opType,"Box") == 0); + } + return false; +} + +bool MatchRule::is_ideal_goto() const { + bool ideal_goto = false; + + if( _opType && (strcmp(_opType,"Goto") == 0) ) { + ideal_goto = true; + } + return ideal_goto; +} + +bool MatchRule::is_ideal_jump() const { + if( _opType ) { + if( !strcmp(_opType,"Jump") ) + return true; + } + return false; +} + +bool MatchRule::is_ideal_bool() const { + if( _opType ) { + if( !strcmp(_opType,"Bool") ) + return true; + } + return false; +} + + +Form::DataType MatchRule::is_ideal_load() const { + Form::DataType ideal_load = Form::none; + + if ( _opType && (strcmp(_opType,"Set") == 0) && _rChild ) { + const char *opType = _rChild->_opType; + ideal_load = is_load_from_memory(opType); + } + + return ideal_load; +} + + +Form::DataType MatchRule::is_ideal_store() const { + Form::DataType ideal_store = Form::none; + + if ( _opType && (strcmp(_opType,"Set") == 0) && _rChild ) { + const char *opType = _rChild->_opType; + ideal_store = is_store_to_memory(opType); + } + + return ideal_store; +} + + +void MatchRule::dump() { + output(stderr); +} + +void MatchRule::output(FILE *fp) { + fprintf(fp,"MatchRule: ( %s",_name); + if (_lChild) _lChild->output(fp); + if (_rChild) _rChild->output(fp); + fprintf(fp," )\n"); + fprintf(fp," nesting depth = %d\n", _depth); + if (_result) fprintf(fp," Result Type = %s", _result); + fprintf(fp,"\n"); +} + +//------------------------------Attribute-------------------------------------- +Attribute::Attribute(char *id, char* val, int type) + : _ident(id), _val(val), _atype(type) { +} +Attribute::~Attribute() { +} + +int Attribute::int_val(ArchDesc &ad) { + // Make sure it is an integer constant: + int result = 0; + if (!_val || !ADLParser::is_int_token(_val, result)) { + ad.syntax_err(0, "Attribute %s must have an integer value: %s", + _ident, _val ? _val : ""); + } + return result; +} + +void Attribute::dump() { + output(stderr); +} // Debug printer + +// Write to output files +void Attribute::output(FILE *fp) { + fprintf(fp,"Attribute: %s %s\n", (_ident?_ident:""), (_val?_val:"")); +} + +//------------------------------FormatRule---------------------------------- +FormatRule::FormatRule(char *temp) + : _temp(temp) { +} +FormatRule::~FormatRule() { +} + +void FormatRule::dump() { + output(stderr); +} + +// Write to output files +void FormatRule::output(FILE *fp) { + fprintf(fp,"\nFormat Rule: \n%s", (_temp?_temp:"")); + fprintf(fp,"\n"); +} diff --git a/hotspot/src/share/vm/adlc/formssel.hpp b/hotspot/src/share/vm/adlc/formssel.hpp new file mode 100644 index 00000000000..8ed2885c890 --- /dev/null +++ b/hotspot/src/share/vm/adlc/formssel.hpp @@ -0,0 +1,1045 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// FORMSSEL.HPP - ADL Parser Instruction Selection Forms Classes + +// Class List +class Form; +class InstructForm; +class MachNodeForm; +class OperandForm; +class OpClassForm; +class AttributeForm; +class RegisterForm; +class PipelineForm; +class SourceForm; +class EncodeForm; +class Component; +class Constraint; +class Predicate; +class MatchRule; +class Attribute; +class Effect; +class ExpandRule; +class RewriteRule; +class ConstructRule; +class FormatRule; +class Peephole; +class EncClass; +class Interface; +class RegInterface; +class ConstInterface; +class MemInterface; +class CondInterface; +class Opcode; +class InsEncode; +class RegDef; +class RegClass; +class AllocClass; +class ResourceForm; +class PipeDesc; +class PipeClass; +class PeepMatch; +class PeepConstraint; +class PeepReplace; +class MatchList; + +class ArchDesc; + +//==============================Instructions=================================== +//------------------------------InstructForm----------------------------------- +class InstructForm : public Form { +private: + bool _ideal_only; // Not a user-defined instruction + // Members used for tracking CISC-spilling + uint _cisc_spill_operand;// Which operand may cisc-spill + void set_cisc_spill_operand(uint op_index) { _cisc_spill_operand = op_index; } + bool _is_cisc_alternate; + InstructForm *_cisc_spill_alternate;// cisc possible replacement + const char *_cisc_reg_mask_name; + InstructForm *_short_branch_form; + bool _is_short_branch; + uint _alignment; + +public: + // Public Data + const char *_ident; // Name of this instruction + NameList _parameters; // Locally defined names + FormDict _localNames; // Table of operands & their types + MatchRule *_matrule; // Matching rule for this instruction + Opcode *_opcode; // Encoding of the opcode for instruction + char *_size; // Size of instruction + InsEncode *_insencode; // Encoding class instruction belongs to + Attribute *_attribs; // List of Attribute rules + Predicate *_predicate; // Predicate test for this instruction + FormDict _effects; // Dictionary of effect rules + ExpandRule *_exprule; // Expand rule for this instruction + RewriteRule *_rewrule; // Rewrite rule for this instruction + FormatRule *_format; // Format for assembly generation + Peephole *_peephole; // List of peephole rules for instruction + const char *_ins_pipe; // Instruction Scheduline description class + + uint *_uniq_idx; // Indexes of unique operands + uint _num_uniq; // Number of unique operands + ComponentList _components; // List of Components matches MachNode's + // operand structure + + // Public Methods + InstructForm(const char *id, bool ideal_only = false); + InstructForm(const char *id, InstructForm *instr, MatchRule *rule); + ~InstructForm(); + + // Dynamic type check + virtual InstructForm *is_instruction() const; + + virtual bool ideal_only() const; + + // This instruction sets a result + virtual bool sets_result() const; + // This instruction needs projections for additional DEFs or KILLs + virtual bool needs_projections(); + // This instruction needs extra nodes for temporary inputs + virtual bool has_temps(); + // This instruction defines or kills more than one object + virtual uint num_defs_or_kills(); + // This instruction has an expand rule? + virtual bool expands() const ; + // Return this instruction's first peephole rule, or NULL + virtual Peephole *peepholes() const; + // Add a peephole rule to this instruction + virtual void append_peephole(Peephole *peep); + + virtual bool is_pinned(FormDict &globals); // should be pinned inside block + virtual bool is_projection(FormDict &globals); // node requires projection + virtual bool is_parm(FormDict &globals); // node matches ideal 'Parm' + // ideal opcode enumeration + virtual const char *ideal_Opcode(FormDict &globals) const; + virtual int is_expensive() const; // node matches ideal 'CosD' + virtual int is_empty_encoding() const; // _size=0 and/or _insencode empty + virtual int is_tls_instruction() const; // tlsLoadP rule or ideal ThreadLocal + virtual int is_ideal_copy() const; // node matches ideal 'Copy*' + virtual bool is_ideal_unlock() const; // node matches ideal 'Unlock' + virtual bool is_ideal_call_leaf() const; // node matches ideal 'CallLeaf' + virtual bool is_ideal_if() const; // node matches ideal 'If' + virtual bool is_ideal_fastlock() const; // node matches 'FastLock' + virtual bool is_ideal_membar() const; // node matches ideal 'MemBarXXX' + virtual bool is_ideal_loadPC() const; // node matches ideal 'LoadPC' + virtual bool is_ideal_box() const; // node matches ideal 'Box' + virtual bool is_ideal_goto() const; // node matches ideal 'Goto' + virtual bool is_ideal_branch() const; // "" 'If' | 'Goto' | 'LoopEnd' | 'Jump' + virtual bool is_ideal_jump() const; // node matches ideal 'Jump' + virtual bool is_ideal_return() const; // node matches ideal 'Return' + virtual bool is_ideal_halt() const; // node matches ideal 'Halt' + virtual bool is_ideal_safepoint() const; // node matches 'SafePoint' + virtual bool is_ideal_nop() const; // node matches 'Nop' + virtual bool is_ideal_control() const; // control node + + virtual Form::CallType is_ideal_call() const; // matches ideal 'Call' + virtual Form::DataType is_ideal_load() const; // node matches ideal 'LoadXNode' + virtual Form::DataType is_ideal_store() const;// node matches ideal 'StoreXNode' + bool is_ideal_mem() const { return is_ideal_load() != Form::none || is_ideal_store() != Form::none; } + virtual uint two_address(FormDict &globals); // output reg must match input reg + // when chaining a constant to an instruction, return 'true' and set opType + virtual Form::DataType is_chain_of_constant(FormDict &globals); + virtual Form::DataType is_chain_of_constant(FormDict &globals, const char * &opType); + virtual Form::DataType is_chain_of_constant(FormDict &globals, const char * &opType, const char * &result_type); + + // Check if a simple chain rule + virtual bool is_simple_chain_rule(FormDict &globals) const; + + // check for structural rematerialization + virtual bool rematerialize(FormDict &globals, RegisterForm *registers); + + // loads from memory, so must check for anti-dependence + virtual bool needs_anti_dependence_check(FormDict &globals) const; + virtual int memory_operand(FormDict &globals) const; + bool is_wide_memory_kill(FormDict &globals) const; + + enum memory_operand_type { + NO_MEMORY_OPERAND = -1, + MANY_MEMORY_OPERANDS = 999999 + }; + + + // This instruction captures the machine-independent bottom_type + // Expected use is for pointer vs oop determination for LoadP + virtual bool captures_bottom_type() const; + + virtual const char *cost(); // Access ins_cost attribute + virtual uint num_opnds(); // Count of num_opnds for MachNode class + virtual uint num_post_match_opnds(); + virtual uint num_consts(FormDict &globals) const;// Constants in match rule + // Constants in match rule with specified type + virtual uint num_consts(FormDict &globals, Form::DataType type) const; + + // Return the register class associated with 'leaf'. + virtual const char *out_reg_class(FormDict &globals); + + // number of ideal node inputs to skip + virtual uint oper_input_base(FormDict &globals); + + // Does this instruction need a base-oop edge? + int needs_base_oop_edge(FormDict &globals) const; + + // Build instruction predicates. If the user uses the same operand name + // twice, we need to check that the operands are pointer-eequivalent in + // the DFA during the labeling process. + Predicate *build_predicate(); + + virtual void build_components(); // top-level operands + // Return zero-based position in component list; -1 if not in list. + virtual int operand_position(const char *name, int usedef); + virtual int operand_position_format(const char *name); + + // Return zero-based position in component list; -1 if not in list. + virtual int label_position(); + virtual int method_position(); + // Return number of relocation entries needed for this instruction. + virtual uint reloc(FormDict &globals); + + const char *reduce_result(); + // Return the name of the operand on the right hand side of the binary match + // Return NULL if there is no right hand side + const char *reduce_right(FormDict &globals) const; + const char *reduce_left(FormDict &globals) const; + + // Base class for this instruction, MachNode except for calls + virtual const char *mach_base_class() const; + + // Check if this instruction can cisc-spill to 'alternate' + bool cisc_spills_to(ArchDesc &AD, InstructForm *alternate); + InstructForm *cisc_spill_alternate() { return _cisc_spill_alternate; } + uint cisc_spill_operand() const { return _cisc_spill_operand; } + bool is_cisc_alternate() const { return _is_cisc_alternate; } + void set_cisc_alternate(bool val) { _is_cisc_alternate = val; } + const char *cisc_reg_mask_name() const { return _cisc_reg_mask_name; } + void set_cisc_reg_mask_name(const char *rm_name) { _cisc_reg_mask_name = rm_name; } + // Output cisc-method prototypes and method bodies + void declare_cisc_version(ArchDesc &AD, FILE *fp_cpp); + bool define_cisc_version (ArchDesc &AD, FILE *fp_cpp); + + bool check_branch_variant(ArchDesc &AD, InstructForm *short_branch); + + bool is_short_branch() { return _is_short_branch; } + void set_short_branch(bool val) { _is_short_branch = val; } + + InstructForm *short_branch_form() { return _short_branch_form; } + bool has_short_branch_form() { return _short_branch_form != NULL; } + // Output short branch prototypes and method bodies + void declare_short_branch_methods(FILE *fp_cpp); + bool define_short_branch_methods(FILE *fp_cpp); + + uint alignment() { return _alignment; } + void set_alignment(uint val) { _alignment = val; } + + // Seach through operands to determine operands unique positions. + void set_unique_opnds(); + uint num_unique_opnds() { return _num_uniq; } + uint unique_opnds_idx(int idx) { + if( _uniq_idx != NULL && idx > 0 ) + return _uniq_idx[idx]; + else + return idx; + } + + // Operands which are only KILLs aren't part of the input array and + // require special handling in some cases. Their position in this + // operand list is higher than the number of unique operands. + bool is_noninput_operand(uint idx) { + return (idx >= num_unique_opnds()); + } + + // --------------------------- FILE *output_routines + // + // Generate the format call for the replacement variable + void rep_var_format(FILE *fp, const char *rep_var); + // Generate index values needed for determing the operand position + void index_temps (FILE *fp, FormDict &globals, const char *prefix = "", const char *receiver = ""); + // --------------------------- + + virtual bool verify(); // Check consistency after parsing + + virtual void dump(); // Debug printer + virtual void output(FILE *fp); // Write to output files +}; + +//------------------------------EncodeForm------------------------------------- +class EncodeForm : public Form { +private: + +public: + // Public Data + NameList _eclasses; // List of encode class names + Dict _encClass; // Map encode class names to EncClass objects + + // Public Methods + EncodeForm(); + ~EncodeForm(); + + EncClass *add_EncClass(const char *className); + EncClass *encClass(const char *className); + + const char *encClassPrototype(const char *className); + const char *encClassBody(const char *className); + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------EncClass--------------------------------------- +class EncClass : public Form { +public: + // NameList for parameter type and name + NameList _parameter_type; + NameList _parameter_name; + + // Breakdown the encoding into strings separated by $replacement_variables + // There is an entry in _strings, perhaps NULL, that precedes each _rep_vars + NameList _code; // Strings passed through to tty->print + NameList _rep_vars; // replacement variables + + NameList _parameters; // Locally defined names + FormDict _localNames; // Table of components & their types + +public: + // Public Data + const char *_name; // encoding class name + + // Public Methods + EncClass(const char *name); + ~EncClass(); + + // --------------------------- Parameters + // Add a parameter pair + void add_parameter(const char *parameter_type, const char *parameter_name); + // Verify operand types in parameter list + bool check_parameter_types(FormDict &globals); + // Obtain the zero-based index corresponding to a replacement variable + int rep_var_index(const char *rep_var); + int num_args() { return _parameter_name.count(); } + + // --------------------------- Code Block + // Add code + void add_code(const char *string_preceeding_replacement_var); + // Add a replacement variable or one of its subfields + // Subfields are stored with a leading '$' + void add_rep_var(char *replacement_var); + + bool verify(); + void dump(); + void output(FILE *fp); +}; + +//------------------------------MachNode--------------------------------------- +class MachNodeForm: public Form { +private: + +public: + char *_ident; // Name of this instruction + const char *_machnode_pipe; // Instruction Scheduline description class + + // Public Methods + MachNodeForm(char *id); + ~MachNodeForm(); + + virtual MachNodeForm *is_machnode() const; + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------Opcode----------------------------------------- +class Opcode : public Form { +private: + +public: + // Public Data + // Strings representing instruction opcodes, user defines placement in emit + char *_primary; + char *_secondary; + char *_tertiary; + + enum opcode_type { + NOT_AN_OPCODE = -1, + PRIMARY = 1, + SECONDARY = 2, + TERTIARY = 3 + }; + + // Public Methods + Opcode(char *primary, char *secondary, char *tertiary); + ~Opcode(); + + static Opcode::opcode_type as_opcode_type(const char *designator); + + void dump(); + void output(FILE *fp); + + // --------------------------- FILE *output_routines + void print_opcode(FILE *fp, Opcode::opcode_type desired_opcode); +}; + +//------------------------------InsEncode-------------------------------------- +class InsEncode : public Form { +private: + // Public Data (access directly only for reads) + // The encodings can only have the values predefined by the ADLC: + // blank, RegReg, RegMem, MemReg, ... + NameList _encoding; + // NameList _parameter; + // The parameters for each encoding are preceeded by a NameList::_signal + // and follow the parameters for the previous encoding. + + // char *_encode; // Type of instruction encoding + +public: + // Public Methods + InsEncode(); + ~InsEncode(); + + // Add "encode class name" and its parameters + NameAndList *add_encode(char *encode_method_name); + // Parameters are added to the returned "NameAndList" by the parser + + // Access the list of encodings + void reset(); + const char *encode_class_iter(); + + // Returns the number of arguments to the current encoding in the iteration + int current_encoding_num_args() { + return ((NameAndList*)_encoding.current())->count(); + } + + // --------------------------- Parameters + // The following call depends upon position within encode_class_iteration + // + // Obtain parameter name from zero based index + const char *rep_var_name(InstructForm &inst, uint param_no); + // --------------------------- + + void dump(); + void output(FILE *fp); +}; + +//------------------------------Effect----------------------------------------- +class Effect : public Form { +private: + +public: + // Public Data + const char *_name; // Pre-defined name for effect + int _use_def; // Enumeration value of effect + + // Public Methods + Effect(const char *name); // Constructor + ~Effect(); // Destructor + + // Dynamic type check + virtual Effect *is_effect() const; + + // Return 'true' if this use def info equals the parameter + bool is(int use_def_kill_enum) const; + // Return 'true' if this use def info is a superset of parameter + bool isa(int use_def_kill_enum) const; + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------ExpandRule------------------------------------- +class ExpandRule : public Form { +private: + NameList _expand_instrs; // ordered list of instructions and operands + +public: + // Public Data + NameList _newopers; // List of newly created operands + Dict _newopconst; // Map new operands to their constructors + + void add_instruction(NameAndList *instruction_name_and_operand_list); + void reset_instructions(); + NameAndList *iter_instructions(); + + // Public Methods + ExpandRule(); // Constructor + ~ExpandRule(); // Destructor + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------RewriteRule------------------------------------ +class RewriteRule : public Form { +private: + +public: + // Public Data + SourceForm *_condition; // Rewrite condition code + InstructForm *_instrs; // List of instructions to expand to + OperandForm *_opers; // List of operands generated by expand + char *_tempParams; // Hold string until parser is finished. + char *_tempBlock; // Hold string until parser is finished. + + // Public Methods + RewriteRule(char* params, char* block) ; + ~RewriteRule(); // Destructor + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + + +//==============================Operands======================================= +//------------------------------OpClassForm------------------------------------ +class OpClassForm : public Form { +public: + // Public Data + const char *_ident; // Name of this operand + NameList _oplst; // List of operand forms included in class + + // Public Methods + OpClassForm(const char *id); + ~OpClassForm(); + + // dynamic type check + virtual OpClassForm *is_opclass() const; + virtual Form::InterfaceType interface_type(FormDict &globals) const; + virtual bool stack_slots_only(FormDict &globals) const; + + virtual bool is_cisc_mem(FormDict &globals) const; + + + // Min and Max opcodes of operands in this operand class + int _minCode; + int _maxCode; + + virtual bool ideal_only() const; + virtual void dump(); // Debug printer + virtual void output(FILE *fp); // Write to output files +}; + +//------------------------------OperandForm------------------------------------ +class OperandForm : public OpClassForm { +private: + bool _ideal_only; // Not a user-defined instruction + +public: + // Public Data + NameList _parameters; // Locally defined names + FormDict _localNames; // Table of components & their types + MatchRule *_matrule; // Matching rule for this operand + Interface *_interface; // Encoding interface for this operand + Attribute *_attribs; // List of Attribute rules + Predicate *_predicate; // Predicate test for this operand + Constraint *_constraint; // Constraint Rule for this operand + ConstructRule *_construct; // Construction of operand data after matching + FormatRule *_format; // Format for assembly generation + NameList _classes; // List of opclasses which contain this oper + + ComponentList _components; // + + // Public Methods + OperandForm(const char *id); + OperandForm(const char *id, bool ideal_only); + ~OperandForm(); + + // Dynamic type check + virtual OperandForm *is_operand() const; + + virtual bool ideal_only() const; + virtual Form::InterfaceType interface_type(FormDict &globals) const; + virtual bool stack_slots_only(FormDict &globals) const; + + virtual const char *cost(); // Access ins_cost attribute + virtual uint num_leaves() const;// Leaves in complex operand + // Constants in operands' match rules + virtual uint num_consts(FormDict &globals) const; + // Constants in operand's match rule with specified type + virtual uint num_consts(FormDict &globals, Form::DataType type) const; + // Pointer Constants in operands' match rules + virtual uint num_const_ptrs(FormDict &globals) const; + // The number of input edges in the machine world == num_leaves - num_consts + virtual uint num_edges(FormDict &globals) const; + + // Check if this operand is usable for cisc-spilling + virtual bool is_cisc_reg(FormDict &globals) const; + + // node matches ideal 'Bool', grab condition codes from the ideal world + virtual bool is_ideal_bool() const; + + // Has an integer constant suitable for spill offsets + bool has_conI(FormDict &globals) const { + return (num_consts(globals,idealI) == 1) && !is_ideal_bool(); } + bool has_conL(FormDict &globals) const { + return (num_consts(globals,idealL) == 1) && !is_ideal_bool(); } + + // Node is user-defined operand for an sRegX + virtual Form::DataType is_user_name_for_sReg() const; + + // Return ideal type, if there is a single ideal type for this operand + virtual const char *ideal_type(FormDict &globals, RegisterForm *registers = NULL) const; + // If there is a single ideal type for this interface field, return it. + virtual const char *interface_ideal_type(FormDict &globals, + const char *field_name) const; + + // Return true if this operand represents a bound register class + bool is_bound_register() const; + + // Return the Register class for this operand. Returns NULL if + // operand isn't a register form. + RegClass* get_RegClass() const; + + virtual bool is_interface_field(const char *field_name, + const char * &value) const; + + // If this operand has a single ideal type, return its type + virtual Form::DataType simple_type(FormDict &globals) const; + // If this operand is an ideal constant, return its type + virtual Form::DataType is_base_constant(FormDict &globals) const; + + // "true" if this operand is a simple type that is swallowed + virtual bool swallowed(FormDict &globals) const; + + // Return register class name if a constraint specifies the register class. + virtual const char *constrained_reg_class() const; + // Return the register class associated with 'leaf'. + virtual const char *in_reg_class(uint leaf, FormDict &globals); + + // Build component list from MatchRule and operand's parameter list + virtual void build_components(); // top-level operands + + // Return zero-based position in component list; -1 if not in list. + virtual int operand_position(const char *name, int usedef); + + // Return zero-based position in component list; -1 if not in list. + virtual int constant_position(FormDict &globals, const Component *comp); + virtual int constant_position(FormDict &globals, const char *local_name); + // Return the operand form corresponding to the given index, else NULL. + virtual OperandForm *constant_operand(FormDict &globals, uint const_index); + + // Return zero-based position in component list; -1 if not in list. + virtual int register_position(FormDict &globals, const char *regname); + + const char *reduce_result() const; + // Return the name of the operand on the right hand side of the binary match + // Return NULL if there is no right hand side + const char *reduce_right(FormDict &globals) const; + const char *reduce_left(FormDict &globals) const; + + + // --------------------------- FILE *output_routines + // + // Output code for disp_is_oop, if true. + void disp_is_oop(FILE *fp, FormDict &globals); + // Generate code for internal and external format methods + void int_format(FILE *fp, FormDict &globals, uint index); + void ext_format(FILE *fp, FormDict &globals, uint index); + void format_constant(FILE *fp, uint con_index, uint con_type); + // Output code to access the value of the index'th constant + void access_constant(FILE *fp, FormDict &globals, + uint con_index); + // --------------------------- + + + virtual void dump(); // Debug printer + virtual void output(FILE *fp); // Write to output files +}; + +//------------------------------Constraint------------------------------------- +class Constraint : public Form { +private: + +public: + const char *_func; // Constraint function + const char *_arg; // function's argument + + // Public Methods + Constraint(const char *func, const char *arg); // Constructor + ~Constraint(); + + bool stack_slots_only() const; + + void dump(); // Debug printer + void output(FILE *fp); // Write info to output files +}; + +//------------------------------Predicate-------------------------------------- +class Predicate : public Form { +private: + +public: + // Public Data + char *_pred; // C++ source string for predicate + + // Public Methods + Predicate(char *pr); + ~Predicate(); + + void dump(); + void output(FILE *fp); +}; + +//------------------------------Interface-------------------------------------- +class Interface : public Form { +private: + +public: + // Public Data + const char *_name; // String representing the interface name + + // Public Methods + Interface(const char *name); + ~Interface(); + + virtual Form::InterfaceType interface_type(FormDict &globals) const; + + RegInterface *is_RegInterface(); + MemInterface *is_MemInterface(); + ConstInterface *is_ConstInterface(); + CondInterface *is_CondInterface(); + + + void dump(); + void output(FILE *fp); +}; + +//------------------------------RegInterface----------------------------------- +class RegInterface : public Interface { +private: + +public: + // Public Methods + RegInterface(); + ~RegInterface(); + + void dump(); + void output(FILE *fp); +}; + +//------------------------------ConstInterface--------------------------------- +class ConstInterface : public Interface { +private: + +public: + // Public Methods + ConstInterface(); + ~ConstInterface(); + + void dump(); + void output(FILE *fp); +}; + +//------------------------------MemInterface----------------------------------- +class MemInterface : public Interface { +private: + +public: + // Public Data + char *_base; // Base address + char *_index; // index + char *_scale; // scaling factor + char *_disp; // displacement + + // Public Methods + MemInterface(char *base, char *index, char *scale, char *disp); + ~MemInterface(); + + void dump(); + void output(FILE *fp); +}; + +//------------------------------CondInterface---------------------------------- +class CondInterface : public Interface { +private: + +public: + const char *_equal; + const char *_not_equal; + const char *_less; + const char *_greater_equal; + const char *_less_equal; + const char *_greater; + + // Public Methods + CondInterface(char *equal, char *not_equal, char *less, char *greater_equal, + char *less_equal, char *greater); + ~CondInterface(); + + void dump(); + void output(FILE *fp); +}; + +//------------------------------ConstructRule---------------------------------- +class ConstructRule : public Form { +private: + +public: + // Public Data + char *_expr; // String representing the match expression + char *_construct; // String representing C++ constructor code + + // Public Methods + ConstructRule(char *cnstr); + ~ConstructRule(); + + void dump(); + void output(FILE *fp); +}; + + +//==============================Shared========================================= +//------------------------------AttributeForm---------------------------------- +class AttributeForm : public Form { +private: + // counters for unique instruction or operand ID + static int _insId; // user-defined machine instruction types + static int _opId; // user-defined operand types + + int id; // hold type for this object + +public: + // Public Data + char *_attrname; // Name of attribute + int _atype; // Either INS_ATTR or OP_ATTR + char *_attrdef; // C++ source which evaluates to constant + + // Public Methods + AttributeForm(char *attr, int type, char *attrdef); + ~AttributeForm(); + + // Dynamic type check + virtual AttributeForm *is_attribute() const; + + int type() { return id;} // return this object's "id" + + static const char* _ins_cost; // "ins_cost" + static const char* _ins_pc_relative; // "ins_pc_relative" + static const char* _op_cost; // "op_cost" + + void dump(); // Debug printer + void output(FILE *fp); // Write output files +}; + +//------------------------------Component-------------------------------------- +class Component : public Form { +private: + +public: + // Public Data + const char *_name; // Name of this component + const char *_type; // Type of this component + int _usedef; // Value of component + + // Public Methods + Component(const char *name, const char *type, int usedef); + ~Component(); + + + // Return 'true' if this use def info equals the parameter + bool is(int use_def_kill_enum) const; + // Return 'true' if this use def info is a superset of parameter + bool isa(int use_def_kill_enum) const; + int promote_use_def_info(int new_use_def); + const char *base_type(FormDict &globals); + // Form::DataType is_base_constant(FormDict &globals); + + void dump(); // Debug printer + void output(FILE *fp); // Write to output files + +public: + // Implementation depends upon working bit intersection and union. + enum use_def_enum { + INVALID = 0x0, + USE = 0x1, + DEF = 0x2, USE_DEF = 0x3, + KILL = 0x4, USE_KILL = 0x5, + SYNTHETIC = 0x8, + TEMP = USE | SYNTHETIC + }; +}; + + +//------------------------------MatchNode-------------------------------------- +class MatchNode : public Form { +private: + +public: + // Public Data + const char *_result; // The name of the output of this node + const char *_name; // The name that appeared in the match rule + const char *_opType; // The Operation/Type matched + MatchNode *_lChild; // Left child in expression tree + MatchNode *_rChild; // Right child in expression tree + int _numleaves; // Sum of numleaves for all direct children + ArchDesc &_AD; // Reference to ArchDesc object + char *_internalop; // String representing internal operand + int _commutative_id; // id of commutative operation + + // Public Methods + MatchNode(ArchDesc &ad, const char *result = 0, const char *expr = 0, + const char *opType=0, MatchNode *lChild=NULL, + MatchNode *rChild=NULL); + MatchNode(ArchDesc &ad, MatchNode& mNode); // Shallow copy constructor; + MatchNode(ArchDesc &ad, MatchNode& mNode, int clone); // Construct clone + ~MatchNode(); + + // return 0 if not found: + // return 1 if found and position is incremented by operand offset in rule + bool find_name(const char *str, int &position) const; + bool find_type(const char *str, int &position) const; + void append_components(FormDict &locals, ComponentList &components, + bool def_flag) const; + bool base_operand(uint &position, FormDict &globals, + const char * &result, const char * &name, + const char * &opType) const; + // recursive count on operands + uint num_consts(FormDict &globals) const; + uint num_const_ptrs(FormDict &globals) const; + // recursive count of constants with specified type + uint num_consts(FormDict &globals, Form::DataType type) const; + // uint num_consts() const; // Local inspection only + int needs_ideal_memory_edge(FormDict &globals) const; + int needs_base_oop_edge() const; + + // Help build instruction predicates. Search for operand names. + void count_instr_names( Dict &names ); + int build_instr_pred( char *buf, const char *name, int cnt ); + void build_internalop( ); + + // Return the name of the operands associated with reducing to this operand: + // The result type, plus the left and right sides of the binary match + // Return NULL if there is no left or right hand side + bool sets_result() const; // rule "Set"s result of match + const char *reduce_right(FormDict &globals) const; + const char *reduce_left (FormDict &globals) const; + + // Recursive version of check in MatchRule + int cisc_spill_match(FormDict &globals, RegisterForm *registers, + MatchNode *mRule2, const char * &operand, + const char * ®_type); + int cisc_spill_merge(int left_result, int right_result); + + bool equivalent(FormDict &globals, MatchNode *mNode2); + + void count_commutative_op(int& count); + void swap_commutative_op(bool atroot, int count); + + void dump(); + void output(FILE *fp); +}; + +//------------------------------MatchRule-------------------------------------- +class MatchRule : public MatchNode { +private: + +public: + // Public Data + const char *_machType; // Machine type index + int _depth; // Expression tree depth + char *_construct; // String representing C++ constructor code + int _numchilds; // Number of direct children + MatchRule *_next; // Pointer to next match rule + + // Public Methods + MatchRule(ArchDesc &ad); + MatchRule(ArchDesc &ad, MatchRule* mRule); // Shallow copy constructor; + MatchRule(ArchDesc &ad, MatchNode* mroot, int depth, char* construct, int numleaves); + ~MatchRule(); + + void append_components(FormDict &locals, ComponentList &components) const; + // Recursive call on all operands' match rules in my match rule. + bool base_operand(uint &position, FormDict &globals, + const char * &result, const char * &name, + const char * &opType) const; + + + bool is_base_register(FormDict &globals) const; + Form::DataType is_base_constant(FormDict &globals) const; + bool is_chain_rule(FormDict &globals) const; + int is_ideal_copy() const; + int is_expensive() const; // node matches ideal 'CosD' + bool is_ideal_unlock() const; + bool is_ideal_call_leaf() const; + bool is_ideal_if() const; // node matches ideal 'If' + bool is_ideal_fastlock() const; // node matches ideal 'FastLock' + bool is_ideal_jump() const; // node matches ideal 'Jump' + bool is_ideal_membar() const; // node matches ideal 'MemBarXXX' + bool is_ideal_loadPC() const; // node matches ideal 'LoadPC' + bool is_ideal_box() const; // node matches ideal 'Box' + bool is_ideal_goto() const; // node matches ideal 'Goto' + bool is_ideal_loopEnd() const; // node matches ideal 'LoopEnd' + bool is_ideal_bool() const; // node matches ideal 'Bool' + Form::DataType is_ideal_load() const;// node matches ideal 'LoadXNode' + Form::DataType is_ideal_store() const;// node matches ideal 'StoreXNode' + + // Check if 'mRule2' is a cisc-spill variant of this MatchRule + int cisc_spill_match(FormDict &globals, RegisterForm *registers, + MatchRule *mRule2, const char * &operand, + const char * ®_type); + + // Check if 'mRule2' is equivalent to this MatchRule + bool equivalent(FormDict &globals, MatchRule *mRule2); + + void swap_commutative_op(const char* instr_ident, int count, int& match_rules_cnt); + + void dump(); + void output(FILE *fp); +}; + +//------------------------------Attribute-------------------------------------- +class Attribute : public Form { +private: + +public: + // Public Data + char *_ident; // Name of predefined attribute + char *_val; // C++ source which evaluates to constant + int _atype; // Either INS_ATTR or OP_ATTR + int int_val(ArchDesc &ad); // Return atoi(_val), ensuring syntax. + + // Public Methods + Attribute(char *id, char* val, int type); + ~Attribute(); + + void dump(); + void output(FILE *fp); +}; + +//------------------------------FormatRule------------------------------------- +class FormatRule : public Form { +private: + +public: + // Public Data + // There is an entry in _strings, perhaps NULL, that precedes each _rep_vars + NameList _strings; // Strings passed through to tty->print + NameList _rep_vars; // replacement variables + char *_temp; // String representing the assembly code + + // Public Methods + FormatRule(char *temp); + ~FormatRule(); + + void dump(); + void output(FILE *fp); +}; diff --git a/hotspot/src/share/vm/adlc/main.cpp b/hotspot/src/share/vm/adlc/main.cpp new file mode 100644 index 00000000000..c5b405d0208 --- /dev/null +++ b/hotspot/src/share/vm/adlc/main.cpp @@ -0,0 +1,442 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// MAIN.CPP - Entry point for the Architecture Description Language Compiler +#include "adlc.hpp" + +//------------------------------Prototypes------------------------------------- +static void usage(ArchDesc& AD); // Print usage message and exit +static char *strip_ext(char *fname); // Strip off name extension +static char *base_plus_suffix(const char* base, const char *suffix);// New concatenated string +static char *prefix_plus_base_plus_suffix(const char* prefix, const char* base, const char *suffix);// New concatenated string +static int get_legal_text(FileBuff &fbuf, char **legal_text); // Get pointer to legal text + +ArchDesc* globalAD = NULL; // global reference to Architecture Description object + +//------------------------------main------------------------------------------- +int main(int argc, char *argv[]) +{ + ArchDesc AD; // Architecture Description object + globalAD = &AD; + + // ResourceMark mark; + ADLParser *ADL_Parse; // ADL Parser object to parse AD file + + // Check for proper arguments + if( argc == 1 ) usage(AD); // No arguments? Then print usage + + // Read command line arguments and file names + for( int i = 1; i < argc; i++ ) { // For all arguments + register char *s = argv[i]; // Get option/filename + + if( *s++ == '-' ) { // It's a flag? (not a filename) + if( !*s ) { // Stand-alone `-' means stdin + //********** INSERT CODE HERE ********** + } else while (*s != '\0') { // While have flags on option + switch (*s++) { // Handle flag + case 'd': // Debug flag + AD._dfa_debug += 1; // Set Debug Flag + break; + case 'g': // Debug ad location flag + AD._adlocation_debug += 1; // Set Debug ad location Flag + break; + case 'o': // No Output Flag + AD._no_output ^= 1; // Toggle no_output flag + break; + case 'q': // Quiet Mode Flag + AD._quiet_mode ^= 1; // Toggle quiet_mode flag + break; + case 'w': // Disable Warnings Flag + AD._disable_warnings ^= 1; // Toggle disable_warnings flag + break; + case 'T': // Option to make DFA as many subroutine calls. + AD._dfa_small += 1; // Set Mode Flag + break; + case 'c': { // Set C++ Output file name + AD._CPP_file._name = s; + const char *base = strip_ext(strdup(s)); + AD._CPP_CLONE_file._name = base_plus_suffix(base,"_clone.cpp"); + AD._CPP_EXPAND_file._name = base_plus_suffix(base,"_expand.cpp"); + AD._CPP_FORMAT_file._name = base_plus_suffix(base,"_format.cpp"); + AD._CPP_GEN_file._name = base_plus_suffix(base,"_gen.cpp"); + AD._CPP_MISC_file._name = base_plus_suffix(base,"_misc.cpp"); + AD._CPP_PEEPHOLE_file._name = base_plus_suffix(base,"_peephole.cpp"); + AD._CPP_PIPELINE_file._name = base_plus_suffix(base,"_pipeline.cpp"); + s += strlen(s); + break; + } + case 'h': // Set C++ Output file name + AD._HPP_file._name = s; s += strlen(s); + break; + case 'v': // Set C++ Output file name + AD._VM_file._name = s; s += strlen(s); + break; + case 'a': // Set C++ Output file name + AD._DFA_file._name = s; + AD._bug_file._name = s; + s += strlen(s); + break; + case '#': // Special internal debug flag + AD._adl_debug++; // Increment internal debug level + break; + case 's': // Output which instructions are cisc-spillable + AD._cisc_spill_debug = true; + break; + case 'D': // Flag Definition + { + char* flag = s; + s += strlen(s); + char* def = strchr(flag, '='); + if (def == NULL) def = (char*)"1"; + else *def++ = '\0'; + AD.set_preproc_def(flag, def); + } + break; + case 'U': // Flag Un-Definition + { + char* flag = s; + s += strlen(s); + AD.set_preproc_def(flag, NULL); + } + break; + default: // Unknown option + usage(AD); // So print usage and exit + } // End of switch on options... + } // End of while have options... + + } else { // Not an option; must be a filename + AD._ADL_file._name = argv[i]; // Set the input filename + + // // Files for storage, based on input file name + const char *base = strip_ext(strdup(argv[i])); + char *temp = base_plus_suffix("dfa_",base); + AD._DFA_file._name = base_plus_suffix(temp,".cpp"); + delete temp; + temp = base_plus_suffix("ad_",base); + AD._CPP_file._name = base_plus_suffix(temp,".cpp"); + AD._CPP_CLONE_file._name = base_plus_suffix(temp,"_clone.cpp"); + AD._CPP_EXPAND_file._name = base_plus_suffix(temp,"_expand.cpp"); + AD._CPP_FORMAT_file._name = base_plus_suffix(temp,"_format.cpp"); + AD._CPP_GEN_file._name = base_plus_suffix(temp,"_gen.cpp"); + AD._CPP_MISC_file._name = base_plus_suffix(temp,"_misc.cpp"); + AD._CPP_PEEPHOLE_file._name = base_plus_suffix(temp,"_peephole.cpp"); + AD._CPP_PIPELINE_file._name = base_plus_suffix(temp,"_pipeline.cpp"); + AD._HPP_file._name = base_plus_suffix(temp,".hpp"); + delete temp; + temp = base_plus_suffix("adGlobals_",base); + AD._VM_file._name = base_plus_suffix(temp,".hpp"); + delete temp; + temp = base_plus_suffix("bugs_",base); + AD._bug_file._name = base_plus_suffix(temp,".out"); + delete temp; + } // End of files vs options... + } // End of while have command line arguments + + // Open files used to store the matcher and its components + if (AD.open_files() == 0) return 1; // Open all input/output files + + // Build the File Buffer, Parse the input, & Generate Code + FileBuff ADL_Buf(&AD._ADL_file, AD); // Create a file buffer for input file + + // Get pointer to legal text at the beginning of AD file. + // It will be used in generated ad files. + char* legal_text; + int legal_sz = get_legal_text(ADL_Buf, &legal_text); + + ADL_Parse = new ADLParser(ADL_Buf, AD); // Create a parser to parse the buffer + ADL_Parse->parse(); // Parse buffer & build description lists + + if( AD._dfa_debug >= 1 ) { // For higher debug settings, print dump + AD.dump(); + } + + delete ADL_Parse; // Delete parser + + // Verify that the results of the parse are consistent + AD.verify(); + + // Prepare to generate the result files: + AD.generateMatchLists(); + AD.identify_unique_operands(); + AD.identify_cisc_spill_instructions(); + AD.identify_short_branches(); + // Make sure every file starts with a copyright: + AD.addSunCopyright(legal_text, legal_sz, AD._HPP_file._fp); // .hpp + AD.addSunCopyright(legal_text, legal_sz, AD._CPP_file._fp); // .cpp + AD.addSunCopyright(legal_text, legal_sz, AD._CPP_CLONE_file._fp); // .cpp + AD.addSunCopyright(legal_text, legal_sz, AD._CPP_EXPAND_file._fp); // .cpp + AD.addSunCopyright(legal_text, legal_sz, AD._CPP_FORMAT_file._fp); // .cpp + AD.addSunCopyright(legal_text, legal_sz, AD._CPP_GEN_file._fp); // .cpp + AD.addSunCopyright(legal_text, legal_sz, AD._CPP_MISC_file._fp); // .cpp + AD.addSunCopyright(legal_text, legal_sz, AD._CPP_PEEPHOLE_file._fp); // .cpp + AD.addSunCopyright(legal_text, legal_sz, AD._CPP_PIPELINE_file._fp); // .cpp + AD.addSunCopyright(legal_text, legal_sz, AD._VM_file._fp); // .hpp + AD.addSunCopyright(legal_text, legal_sz, AD._DFA_file._fp); // .cpp + // Make sure each .cpp file starts with include lines: + // files declaring and defining generators for Mach* Objects (hpp,cpp) + AD.machineDependentIncludes(AD._CPP_file); // .cpp + AD.machineDependentIncludes(AD._CPP_CLONE_file); // .cpp + AD.machineDependentIncludes(AD._CPP_EXPAND_file); // .cpp + AD.machineDependentIncludes(AD._CPP_FORMAT_file); // .cpp + AD.machineDependentIncludes(AD._CPP_GEN_file); // .cpp + AD.machineDependentIncludes(AD._CPP_MISC_file); // .cpp + AD.machineDependentIncludes(AD._CPP_PEEPHOLE_file); // .cpp + AD.machineDependentIncludes(AD._CPP_PIPELINE_file); // .cpp + // Generate the result files: + // enumerations, class definitions, object generators, and the DFA + // file containing enumeration of machine operands & instructions (hpp) + AD.addPreHeaderBlocks(AD._HPP_file._fp); // .hpp + AD.buildMachOperEnum(AD._HPP_file._fp); // .hpp + AD.buildMachOpcodesEnum(AD._HPP_file._fp); // .hpp + AD.buildMachRegisterNumbers(AD._VM_file._fp); // VM file + AD.buildMachRegisterEncodes(AD._HPP_file._fp); // .hpp file + AD.declareRegSizes(AD._HPP_file._fp); // .hpp + AD.build_pipeline_enums(AD._HPP_file._fp); // .hpp + // output definition of class "State" + AD.defineStateClass(AD._HPP_file._fp); // .hpp + // file declaring the Mach* classes derived from MachOper and MachNode + AD.declareClasses(AD._HPP_file._fp); + // declare and define maps: in the .hpp and .cpp files respectively + AD.addSourceBlocks(AD._CPP_file._fp); // .cpp + AD.addHeaderBlocks(AD._HPP_file._fp); // .hpp + AD.buildReduceMaps(AD._HPP_file._fp, AD._CPP_file._fp); + AD.buildMustCloneMap(AD._HPP_file._fp, AD._CPP_file._fp); + // build CISC_spilling oracle and MachNode::cisc_spill() methods + AD.build_cisc_spill_instructions(AD._HPP_file._fp, AD._CPP_file._fp); + // define methods for machine dependent State, MachOper, and MachNode classes + AD.defineClasses(AD._CPP_file._fp); + AD.buildMachOperGenerator(AD._CPP_GEN_file._fp);// .cpp + AD.buildMachNodeGenerator(AD._CPP_GEN_file._fp);// .cpp + // define methods for machine dependent instruction matching + AD.buildInstructMatchCheck(AD._CPP_file._fp); // .cpp + // define methods for machine dependent frame management + AD.buildFrameMethods(AD._CPP_file._fp); // .cpp + + // do this last: + AD.addPreprocessorChecks(AD._CPP_file._fp); // .cpp + AD.addPreprocessorChecks(AD._CPP_CLONE_file._fp); // .cpp + AD.addPreprocessorChecks(AD._CPP_EXPAND_file._fp); // .cpp + AD.addPreprocessorChecks(AD._CPP_FORMAT_file._fp); // .cpp + AD.addPreprocessorChecks(AD._CPP_GEN_file._fp); // .cpp + AD.addPreprocessorChecks(AD._CPP_MISC_file._fp); // .cpp + AD.addPreprocessorChecks(AD._CPP_PEEPHOLE_file._fp); // .cpp + AD.addPreprocessorChecks(AD._CPP_PIPELINE_file._fp); // .cpp + + // define the finite automata that selects lowest cost production + AD.machineDependentIncludes(AD._DFA_file); // .cpp + AD.buildDFA(AD._DFA_file._fp); + + AD.close_files(0); // Close all input/output files + + // Final printout and statistics + // cout << program; + + if( AD._dfa_debug & 2 ) { // For higher debug settings, print timing info + // Timer t_stop; + // Timer t_total = t_stop - t_start; // Total running time + // cerr << "\n---Architecture Description Totals---\n"; + // cerr << ", Total lines: " << TotalLines; + // float l = TotalLines; + // cerr << "\nTotal Compilation Time: " << t_total << "\n"; + // float ft = (float)t_total; + // if( ft > 0.0 ) fprintf(stderr,"Lines/sec: %#5.2f\n", l/ft); + } + return (AD._syntax_errs + AD._semantic_errs + AD._internal_errs); // Bye Bye!! +} + +//------------------------------usage------------------------------------------ +static void usage(ArchDesc& AD) +{ + printf("Architecture Description Language Compiler\n\n"); + printf("Usage: adl [-doqw] [-Dflag[=def]] [-Uflag] [-cFILENAME] [-hFILENAME] [-aDFAFILE] ADLFILE\n"); + printf(" d produce DFA debugging info\n"); + printf(" o no output produced, syntax and semantic checking only\n"); + printf(" q quiet mode, supresses all non-essential messages\n"); + printf(" w suppress warning messages\n"); + printf(" c specify CPP file name (default: %s)\n", AD._CPP_file._name); + printf(" h specify HPP file name (default: %s)\n", AD._HPP_file._name); + printf(" a specify DFA output file name\n"); + printf("\n"); +} + +//------------------------------open_file------------------------------------ +int ArchDesc::open_file(bool required, ADLFILE & ADF, const char *action) +{ + if (required && + (ADF._fp = fopen(ADF._name, action)) == NULL) { + printf("ERROR: Cannot open file for %s: %s\n", action, ADF._name); + close_files(1); + return 0; + } + return 1; +} + +//------------------------------open_files------------------------------------- +int ArchDesc::open_files(void) +{ + if (_ADL_file._name == NULL) + { printf("ERROR: No ADL input file specified\n"); return 0; } + + if (!open_file(true , _ADL_file, "r")) { return 0; } + if (!open_file(!_no_output, _DFA_file, "w")) { return 0; } + if (!open_file(!_no_output, _HPP_file, "w")) { return 0; } + if (!open_file(!_no_output, _CPP_file, "w")) { return 0; } + if (!open_file(!_no_output, _CPP_CLONE_file, "w")) { return 0; } + if (!open_file(!_no_output, _CPP_EXPAND_file, "w")) { return 0; } + if (!open_file(!_no_output, _CPP_FORMAT_file, "w")) { return 0; } + if (!open_file(!_no_output, _CPP_GEN_file, "w")) { return 0; } + if (!open_file(!_no_output, _CPP_MISC_file, "w")) { return 0; } + if (!open_file(!_no_output, _CPP_PEEPHOLE_file, "w")) { return 0; } + if (!open_file(!_no_output, _CPP_PIPELINE_file, "w")) { return 0; } + if (!open_file(!_no_output, _VM_file , "w")) { return 0; } + if (!open_file(_dfa_debug != 0, _bug_file, "w")) { return 0; } + + return 1; +} + +//------------------------------close_file------------------------------------ +void ArchDesc::close_file(int delete_out, ADLFILE& ADF) +{ + if (ADF._fp) { + fclose(ADF._fp); + if (delete_out) remove(ADF._name); + } +} + +//------------------------------close_files------------------------------------ +void ArchDesc::close_files(int delete_out) +{ + if (_ADL_file._fp) fclose(_ADL_file._fp); + + close_file(delete_out, _CPP_file); + close_file(delete_out, _CPP_CLONE_file); + close_file(delete_out, _CPP_EXPAND_file); + close_file(delete_out, _CPP_FORMAT_file); + close_file(delete_out, _CPP_GEN_file); + close_file(delete_out, _CPP_MISC_file); + close_file(delete_out, _CPP_PEEPHOLE_file); + close_file(delete_out, _CPP_PIPELINE_file); + close_file(delete_out, _HPP_file); + close_file(delete_out, _DFA_file); + close_file(delete_out, _bug_file); + + if (!_quiet_mode) { + printf("\n"); + if (_no_output || delete_out) { + if (_ADL_file._name) printf("%s: ", _ADL_file._name); + printf("No output produced"); + } + else { + if (_ADL_file._name) printf("%s --> ", _ADL_file._name); + printf("%s, %s, %s, %s, %s, %s, %s, %s, %s", + _CPP_file._name, + _CPP_CLONE_file._name, + _CPP_EXPAND_file._name, + _CPP_FORMAT_file._name, + _CPP_GEN_file._name, + _CPP_MISC_file._name, + _CPP_PEEPHOLE_file._name, + _CPP_PIPELINE_file._name, + _HPP_file._name, _DFA_file._name); + } + printf("\n"); + } +} + +//------------------------------strip_ext-------------------------------------- +static char *strip_ext(char *fname) +{ + char *ep; + + if (fname) { + ep = fname + strlen(fname) - 1; // start at last character and look for '.' + while (ep >= fname && *ep != '.') --ep; + if (*ep == '.') *ep = '\0'; // truncate string at '.' + } + return fname; +} + +//------------------------------strip_path_and_ext------------------------------ +static char *strip_path_and_ext(char *fname) +{ + char *ep; + char *sp; + + if (fname) { + for (sp = fname; *sp; sp++) + if (*sp == '/') fname = sp+1; + ep = fname; // start at first character and look for '.' + while (ep <= (fname + strlen(fname) - 1) && *ep != '.') ep++; + if (*ep == '.') *ep = '\0'; // truncate string at '.' + } + return fname; +} + +//------------------------------base_plus_suffix------------------------------- +// New concatenated string +static char *base_plus_suffix(const char* base, const char *suffix) +{ + int len = (int)strlen(base) + (int)strlen(suffix) + 1; + + char* fname = new char[len]; + sprintf(fname,"%s%s",base,suffix); + return fname; +} + + +//------------------------------prefix_plus_base_plus_suffix------------------- +// New concatenated string +static char *prefix_plus_base_plus_suffix(const char* prefix, const char* base, const char *suffix) +{ + int len = (int)strlen(prefix) + (int)strlen(base) + (int)strlen(suffix) + 1; + + char* fname = new char[len]; + sprintf(fname,"%s%s%s",prefix,base,suffix); + return fname; +} + +//------------------------------get_legal_text--------------------------------- +// Get pointer to legal text at the beginning of AD file. +// This code assumes that a legal text starts at the beginning of .ad files, +// is commented by "//" at each line and ends with empty line. +// +int get_legal_text(FileBuff &fbuf, char **legal_text) +{ + char* legal_start = fbuf.get_line(); + assert(legal_start[0] == '/' && legal_start[1] == '/', "Incorrect header of AD file"); + char* legal_end = fbuf.get_line(); + assert(strncmp(legal_end, "// Copyright", 12) == 0, "Incorrect header of AD file"); + while(legal_end[0] == '/' && legal_end[1] == '/') { + legal_end = fbuf.get_line(); + } + *legal_text = legal_start; + return (legal_end - legal_start); +} + +// VS2005 has its own definition, identical to this one. +#if !defined(_WIN32) || defined(_WIN64) || _MSC_VER < 1400 +void *operator new( size_t size, int, const char *, int ) { + return ::operator new( size ); +} +#endif diff --git a/hotspot/src/share/vm/adlc/output_c.cpp b/hotspot/src/share/vm/adlc/output_c.cpp new file mode 100644 index 00000000000..037e129d1b2 --- /dev/null +++ b/hotspot/src/share/vm/adlc/output_c.cpp @@ -0,0 +1,4049 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// output_c.cpp - Class CPP file output routines for architecture definition + +#include "adlc.hpp" + +// Utilities to characterize effect statements +static bool is_def(int usedef) { + switch(usedef) { + case Component::DEF: + case Component::USE_DEF: return true; break; + } + return false; +} + +static bool is_use(int usedef) { + switch(usedef) { + case Component::USE: + case Component::USE_DEF: + case Component::USE_KILL: return true; break; + } + return false; +} + +static bool is_kill(int usedef) { + switch(usedef) { + case Component::KILL: + case Component::USE_KILL: return true; break; + } + return false; +} + +// Define an array containing the machine register names, strings. +static void defineRegNames(FILE *fp, RegisterForm *registers) { + if (registers) { + fprintf(fp,"\n"); + fprintf(fp,"// An array of character pointers to machine register names.\n"); + fprintf(fp,"const char *Matcher::regName[REG_COUNT] = {\n"); + + // Output the register name for each register in the allocation classes + RegDef *reg_def = NULL; + RegDef *next = NULL; + registers->reset_RegDefs(); + for( reg_def = registers->iter_RegDefs(); reg_def != NULL; reg_def = next ) { + next = registers->iter_RegDefs(); + const char *comma = (next != NULL) ? "," : " // no trailing comma"; + fprintf(fp," \"%s\"%s\n", + reg_def->_regname, comma ); + } + + // Finish defining enumeration + fprintf(fp,"};\n"); + + fprintf(fp,"\n"); + fprintf(fp,"// An array of character pointers to machine register names.\n"); + fprintf(fp,"const VMReg OptoReg::opto2vm[REG_COUNT] = {\n"); + reg_def = NULL; + next = NULL; + registers->reset_RegDefs(); + for( reg_def = registers->iter_RegDefs(); reg_def != NULL; reg_def = next ) { + next = registers->iter_RegDefs(); + const char *comma = (next != NULL) ? "," : " // no trailing comma"; + fprintf(fp,"\t%s%s\n", reg_def->_concrete, comma ); + } + // Finish defining array + fprintf(fp,"\t};\n"); + fprintf(fp,"\n"); + + fprintf(fp," OptoReg::Name OptoReg::vm2opto[ConcreteRegisterImpl::number_of_registers];\n"); + + } +} + +// Define an array containing the machine register encoding values +static void defineRegEncodes(FILE *fp, RegisterForm *registers) { + if (registers) { + fprintf(fp,"\n"); + fprintf(fp,"// An array of the machine register encode values\n"); + fprintf(fp,"const unsigned char Matcher::_regEncode[REG_COUNT] = {\n"); + + // Output the register encoding for each register in the allocation classes + RegDef *reg_def = NULL; + RegDef *next = NULL; + registers->reset_RegDefs(); + for( reg_def = registers->iter_RegDefs(); reg_def != NULL; reg_def = next ) { + next = registers->iter_RegDefs(); + const char* register_encode = reg_def->register_encode(); + const char *comma = (next != NULL) ? "," : " // no trailing comma"; + int encval; + if (!ADLParser::is_int_token(register_encode, encval)) { + fprintf(fp," %s%s // %s\n", + register_encode, comma, reg_def->_regname ); + } else { + // Output known constants in hex char format (backward compatibility). + assert(encval < 256, "Exceeded supported width for register encoding"); + fprintf(fp," (unsigned char)'\\x%X'%s // %s\n", + encval, comma, reg_def->_regname ); + } + } + // Finish defining enumeration + fprintf(fp,"};\n"); + + } // Done defining array +} + +// Output an enumeration of register class names +static void defineRegClassEnum(FILE *fp, RegisterForm *registers) { + if (registers) { + // Output an enumeration of register class names + fprintf(fp,"\n"); + fprintf(fp,"// Enumeration of register class names\n"); + fprintf(fp, "enum machRegisterClass {\n"); + registers->_rclasses.reset(); + for( const char *class_name = NULL; + (class_name = registers->_rclasses.iter()) != NULL; ) { + fprintf(fp," %s,\n", toUpper( class_name )); + } + // Finish defining enumeration + fprintf(fp, " _last_Mach_Reg_Class\n"); + fprintf(fp, "};\n"); + } +} + +// Declare an enumeration of user-defined register classes +// and a list of register masks, one for each class. +void ArchDesc::declare_register_masks(FILE *fp_hpp) { + const char *rc_name; + + if( _register ) { + // Build enumeration of user-defined register classes. + defineRegClassEnum(fp_hpp, _register); + + // Generate a list of register masks, one for each class. + fprintf(fp_hpp,"\n"); + fprintf(fp_hpp,"// Register masks, one for each register class.\n"); + _register->_rclasses.reset(); + for( rc_name = NULL; + (rc_name = _register->_rclasses.iter()) != NULL; ) { + const char *prefix = ""; + RegClass *reg_class = _register->getRegClass(rc_name); + assert( reg_class, "Using an undefined register class"); + + int len = RegisterForm::RegMask_Size(); + fprintf(fp_hpp, "extern const RegMask %s%s_mask;\n", prefix, toUpper( rc_name ) ); + + if( reg_class->_stack_or_reg ) { + fprintf(fp_hpp, "extern const RegMask %sSTACK_OR_%s_mask;\n", prefix, toUpper( rc_name ) ); + } + } + } +} + +// Generate an enumeration of user-defined register classes +// and a list of register masks, one for each class. +void ArchDesc::build_register_masks(FILE *fp_cpp) { + const char *rc_name; + + if( _register ) { + // Generate a list of register masks, one for each class. + fprintf(fp_cpp,"\n"); + fprintf(fp_cpp,"// Register masks, one for each register class.\n"); + _register->_rclasses.reset(); + for( rc_name = NULL; + (rc_name = _register->_rclasses.iter()) != NULL; ) { + const char *prefix = ""; + RegClass *reg_class = _register->getRegClass(rc_name); + assert( reg_class, "Using an undefined register class"); + + int len = RegisterForm::RegMask_Size(); + fprintf(fp_cpp, "const RegMask %s%s_mask(", prefix, toUpper( rc_name ) ); + { int i; + for( i = 0; i < len-1; i++ ) + fprintf(fp_cpp," 0x%x,",reg_class->regs_in_word(i,false)); + fprintf(fp_cpp," 0x%x );\n",reg_class->regs_in_word(i,false)); + } + + if( reg_class->_stack_or_reg ) { + int i; + fprintf(fp_cpp, "const RegMask %sSTACK_OR_%s_mask(", prefix, toUpper( rc_name ) ); + for( i = 0; i < len-1; i++ ) + fprintf(fp_cpp," 0x%x,",reg_class->regs_in_word(i,true)); + fprintf(fp_cpp," 0x%x );\n",reg_class->regs_in_word(i,true)); + } + } + } +} + +// Compute an index for an array in the pipeline_reads_NNN arrays +static int pipeline_reads_initializer(FILE *fp_cpp, NameList &pipeline_reads, PipeClassForm *pipeclass) +{ + int templen = 1; + int paramcount = 0; + const char *paramname; + + if (pipeclass->_parameters.count() == 0) + return -1; + + pipeclass->_parameters.reset(); + paramname = pipeclass->_parameters.iter(); + const PipeClassOperandForm *pipeopnd = + (const PipeClassOperandForm *)pipeclass->_localUsage[paramname]; + if (pipeopnd && !pipeopnd->isWrite() && strcmp(pipeopnd->_stage, "Universal")) + pipeclass->_parameters.reset(); + + while ( (paramname = pipeclass->_parameters.iter()) != NULL ) { + const PipeClassOperandForm *pipeopnd = + (const PipeClassOperandForm *)pipeclass->_localUsage[paramname]; + + if (pipeopnd) + templen += 10 + (int)strlen(pipeopnd->_stage); + else + templen += 19; + + paramcount++; + } + + // See if the count is zero + if (paramcount == 0) { + return -1; + } + + char *operand_stages = new char [templen]; + operand_stages[0] = 0; + int i = 0; + templen = 0; + + pipeclass->_parameters.reset(); + paramname = pipeclass->_parameters.iter(); + pipeopnd = (const PipeClassOperandForm *)pipeclass->_localUsage[paramname]; + if (pipeopnd && !pipeopnd->isWrite() && strcmp(pipeopnd->_stage, "Universal")) + pipeclass->_parameters.reset(); + + while ( (paramname = pipeclass->_parameters.iter()) != NULL ) { + const PipeClassOperandForm *pipeopnd = + (const PipeClassOperandForm *)pipeclass->_localUsage[paramname]; + templen += sprintf(&operand_stages[templen], " stage_%s%c\n", + pipeopnd ? pipeopnd->_stage : "undefined", + (++i < paramcount ? ',' : ' ') ); + } + + // See if the same string is in the table + int ndx = pipeline_reads.index(operand_stages); + + // No, add it to the table + if (ndx < 0) { + pipeline_reads.addName(operand_stages); + ndx = pipeline_reads.index(operand_stages); + + fprintf(fp_cpp, "static const enum machPipelineStages pipeline_reads_%03d[%d] = {\n%s};\n\n", + ndx+1, paramcount, operand_stages); + } + else + delete [] operand_stages; + + return (ndx); +} + +// Compute an index for an array in the pipeline_res_stages_NNN arrays +static int pipeline_res_stages_initializer( + FILE *fp_cpp, + PipelineForm *pipeline, + NameList &pipeline_res_stages, + PipeClassForm *pipeclass) +{ + const PipeClassResourceForm *piperesource; + int * res_stages = new int [pipeline->_rescount]; + int i; + + for (i = 0; i < pipeline->_rescount; i++) + res_stages[i] = 0; + + for (pipeclass->_resUsage.reset(); + (piperesource = (const PipeClassResourceForm *)pipeclass->_resUsage.iter()) != NULL; ) { + int used_mask = pipeline->_resdict[piperesource->_resource]->is_resource()->mask(); + for (i = 0; i < pipeline->_rescount; i++) + if ((1 << i) & used_mask) { + int stage = pipeline->_stages.index(piperesource->_stage); + if (res_stages[i] < stage+1) + res_stages[i] = stage+1; + } + } + + // Compute the length needed for the resource list + int commentlen = 0; + int max_stage = 0; + for (i = 0; i < pipeline->_rescount; i++) { + if (res_stages[i] == 0) { + if (max_stage < 9) + max_stage = 9; + } + else { + int stagelen = (int)strlen(pipeline->_stages.name(res_stages[i]-1)); + if (max_stage < stagelen) + max_stage = stagelen; + } + + commentlen += (int)strlen(pipeline->_reslist.name(i)); + } + + int templen = 1 + commentlen + pipeline->_rescount * (max_stage + 14); + + // Allocate space for the resource list + char * resource_stages = new char [templen]; + + templen = 0; + for (i = 0; i < pipeline->_rescount; i++) { + const char * const resname = + res_stages[i] == 0 ? "undefined" : pipeline->_stages.name(res_stages[i]-1); + + templen += sprintf(&resource_stages[templen], " stage_%s%-*s // %s\n", + resname, max_stage - (int)strlen(resname) + 1, + (i < pipeline->_rescount-1) ? "," : "", + pipeline->_reslist.name(i)); + } + + // See if the same string is in the table + int ndx = pipeline_res_stages.index(resource_stages); + + // No, add it to the table + if (ndx < 0) { + pipeline_res_stages.addName(resource_stages); + ndx = pipeline_res_stages.index(resource_stages); + + fprintf(fp_cpp, "static const enum machPipelineStages pipeline_res_stages_%03d[%d] = {\n%s};\n\n", + ndx+1, pipeline->_rescount, resource_stages); + } + else + delete [] resource_stages; + + delete [] res_stages; + + return (ndx); +} + +// Compute an index for an array in the pipeline_res_cycles_NNN arrays +static int pipeline_res_cycles_initializer( + FILE *fp_cpp, + PipelineForm *pipeline, + NameList &pipeline_res_cycles, + PipeClassForm *pipeclass) +{ + const PipeClassResourceForm *piperesource; + int * res_cycles = new int [pipeline->_rescount]; + int i; + + for (i = 0; i < pipeline->_rescount; i++) + res_cycles[i] = 0; + + for (pipeclass->_resUsage.reset(); + (piperesource = (const PipeClassResourceForm *)pipeclass->_resUsage.iter()) != NULL; ) { + int used_mask = pipeline->_resdict[piperesource->_resource]->is_resource()->mask(); + for (i = 0; i < pipeline->_rescount; i++) + if ((1 << i) & used_mask) { + int cycles = piperesource->_cycles; + if (res_cycles[i] < cycles) + res_cycles[i] = cycles; + } + } + + // Pre-compute the string length + int templen; + int cyclelen = 0, commentlen = 0; + int max_cycles = 0; + char temp[32]; + + for (i = 0; i < pipeline->_rescount; i++) { + if (max_cycles < res_cycles[i]) + max_cycles = res_cycles[i]; + templen = sprintf(temp, "%d", res_cycles[i]); + if (cyclelen < templen) + cyclelen = templen; + commentlen += (int)strlen(pipeline->_reslist.name(i)); + } + + templen = 1 + commentlen + (cyclelen + 8) * pipeline->_rescount; + + // Allocate space for the resource list + char * resource_cycles = new char [templen]; + + templen = 0; + + for (i = 0; i < pipeline->_rescount; i++) { + templen += sprintf(&resource_cycles[templen], " %*d%c // %s\n", + cyclelen, res_cycles[i], (i < pipeline->_rescount-1) ? ',' : ' ', pipeline->_reslist.name(i)); + } + + // See if the same string is in the table + int ndx = pipeline_res_cycles.index(resource_cycles); + + // No, add it to the table + if (ndx < 0) { + pipeline_res_cycles.addName(resource_cycles); + ndx = pipeline_res_cycles.index(resource_cycles); + + fprintf(fp_cpp, "static const uint pipeline_res_cycles_%03d[%d] = {\n%s};\n\n", + ndx+1, pipeline->_rescount, resource_cycles); + } + else + delete [] resource_cycles; + + delete [] res_cycles; + + return (ndx); +} + +//typedef unsigned long long uint64_t; + +// Compute an index for an array in the pipeline_res_mask_NNN arrays +static int pipeline_res_mask_initializer( + FILE *fp_cpp, + PipelineForm *pipeline, + NameList &pipeline_res_mask, + NameList &pipeline_res_args, + PipeClassForm *pipeclass) +{ + const PipeClassResourceForm *piperesource; + const uint rescount = pipeline->_rescount; + const uint maxcycleused = pipeline->_maxcycleused; + const uint cyclemasksize = (maxcycleused + 31) >> 5; + + int i, j; + int element_count = 0; + uint *res_mask = new uint [cyclemasksize]; + uint resources_used = 0; + uint resources_used_exclusively = 0; + + for (pipeclass->_resUsage.reset(); + (piperesource = (const PipeClassResourceForm *)pipeclass->_resUsage.iter()) != NULL; ) + element_count++; + + // Pre-compute the string length + int templen; + int commentlen = 0; + int max_cycles = 0; + + int cyclelen = ((maxcycleused + 3) >> 2); + int masklen = (rescount + 3) >> 2; + + int cycledigit = 0; + for (i = maxcycleused; i > 0; i /= 10) + cycledigit++; + + int maskdigit = 0; + for (i = rescount; i > 0; i /= 10) + maskdigit++; + + static const char * pipeline_use_cycle_mask = "Pipeline_Use_Cycle_Mask"; + static const char * pipeline_use_element = "Pipeline_Use_Element"; + + templen = 1 + + (int)(strlen(pipeline_use_cycle_mask) + (int)strlen(pipeline_use_element) + + (cyclemasksize * 12) + masklen + (cycledigit * 2) + 30) * element_count; + + // Allocate space for the resource list + char * resource_mask = new char [templen]; + char * last_comma = NULL; + + templen = 0; + + for (pipeclass->_resUsage.reset(); + (piperesource = (const PipeClassResourceForm *)pipeclass->_resUsage.iter()) != NULL; ) { + int used_mask = pipeline->_resdict[piperesource->_resource]->is_resource()->mask(); + + if (!used_mask) + fprintf(stderr, "*** used_mask is 0 ***\n"); + + resources_used |= used_mask; + + uint lb, ub; + + for (lb = 0; (used_mask & (1 << lb)) == 0; lb++); + for (ub = 31; (used_mask & (1 << ub)) == 0; ub--); + + if (lb == ub) + resources_used_exclusively |= used_mask; + + int formatlen = + sprintf(&resource_mask[templen], " %s(0x%0*x, %*d, %*d, %s %s(", + pipeline_use_element, + masklen, used_mask, + cycledigit, lb, cycledigit, ub, + ((used_mask & (used_mask-1)) != 0) ? "true, " : "false,", + pipeline_use_cycle_mask); + + templen += formatlen; + + memset(res_mask, 0, cyclemasksize * sizeof(uint)); + + int cycles = piperesource->_cycles; + uint stage = pipeline->_stages.index(piperesource->_stage); + uint upper_limit = stage+cycles-1; + uint lower_limit = stage-1; + uint upper_idx = upper_limit >> 5; + uint lower_idx = lower_limit >> 5; + uint upper_position = upper_limit & 0x1f; + uint lower_position = lower_limit & 0x1f; + + uint mask = (((uint)1) << upper_position) - 1; + + while ( upper_idx > lower_idx ) { + res_mask[upper_idx--] |= mask; + mask = (uint)-1; + } + + mask -= (((uint)1) << lower_position) - 1; + res_mask[upper_idx] |= mask; + + for (j = cyclemasksize-1; j >= 0; j--) { + formatlen = + sprintf(&resource_mask[templen], "0x%08x%s", res_mask[j], j > 0 ? ", " : ""); + templen += formatlen; + } + + resource_mask[templen++] = ')'; + resource_mask[templen++] = ')'; + last_comma = &resource_mask[templen]; + resource_mask[templen++] = ','; + resource_mask[templen++] = '\n'; + } + + resource_mask[templen] = 0; + if (last_comma) + last_comma[0] = ' '; + + // See if the same string is in the table + int ndx = pipeline_res_mask.index(resource_mask); + + // No, add it to the table + if (ndx < 0) { + pipeline_res_mask.addName(resource_mask); + ndx = pipeline_res_mask.index(resource_mask); + + if (strlen(resource_mask) > 0) + fprintf(fp_cpp, "static const Pipeline_Use_Element pipeline_res_mask_%03d[%d] = {\n%s};\n\n", + ndx+1, element_count, resource_mask); + + char * args = new char [9 + 2*masklen + maskdigit]; + + sprintf(args, "0x%0*x, 0x%0*x, %*d", + masklen, resources_used, + masklen, resources_used_exclusively, + maskdigit, element_count); + + pipeline_res_args.addName(args); + } + else + delete [] resource_mask; + + delete [] res_mask; +//delete [] res_masks; + + return (ndx); +} + +void ArchDesc::build_pipe_classes(FILE *fp_cpp) { + const char *classname; + const char *resourcename; + int resourcenamelen = 0; + NameList pipeline_reads; + NameList pipeline_res_stages; + NameList pipeline_res_cycles; + NameList pipeline_res_masks; + NameList pipeline_res_args; + const int default_latency = 1; + const int non_operand_latency = 0; + const int node_latency = 0; + + if (!_pipeline) { + fprintf(fp_cpp, "uint Node::latency(uint i) const {\n"); + fprintf(fp_cpp, " // assert(false, \"pipeline functionality is not defined\");\n"); + fprintf(fp_cpp, " return %d;\n", non_operand_latency); + fprintf(fp_cpp, "}\n"); + return; + } + + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "//------------------Pipeline Methods-----------------------------------------\n"); + fprintf(fp_cpp, "#ifndef PRODUCT\n"); + fprintf(fp_cpp, "const char * Pipeline::stageName(uint s) {\n"); + fprintf(fp_cpp, " static const char * const _stage_names[] = {\n"); + fprintf(fp_cpp, " \"undefined\""); + + for (int s = 0; s < _pipeline->_stagecnt; s++) + fprintf(fp_cpp, ", \"%s\"", _pipeline->_stages.name(s)); + + fprintf(fp_cpp, "\n };\n\n"); + fprintf(fp_cpp, " return (s <= %d ? _stage_names[s] : \"???\");\n", + _pipeline->_stagecnt); + fprintf(fp_cpp, "}\n"); + fprintf(fp_cpp, "#endif\n\n"); + + fprintf(fp_cpp, "uint Pipeline::functional_unit_latency(uint start, const Pipeline *pred) const {\n"); + fprintf(fp_cpp, " // See if the functional units overlap\n"); +#if 0 + fprintf(fp_cpp, "\n#ifndef PRODUCT\n"); + fprintf(fp_cpp, " if (TraceOptoOutput) {\n"); + fprintf(fp_cpp, " tty->print(\"# functional_unit_latency: start == %%d, this->exclusively == 0x%%03x, pred->exclusively == 0x%%03x\\n\", start, resourcesUsedExclusively(), pred->resourcesUsedExclusively());\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, "#endif\n\n"); +#endif + fprintf(fp_cpp, " uint mask = resourcesUsedExclusively() & pred->resourcesUsedExclusively();\n"); + fprintf(fp_cpp, " if (mask == 0)\n return (start);\n\n"); +#if 0 + fprintf(fp_cpp, "\n#ifndef PRODUCT\n"); + fprintf(fp_cpp, " if (TraceOptoOutput) {\n"); + fprintf(fp_cpp, " tty->print(\"# functional_unit_latency: mask == 0x%%x\\n\", mask);\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, "#endif\n\n"); +#endif + fprintf(fp_cpp, " for (uint i = 0; i < pred->resourceUseCount(); i++) {\n"); + fprintf(fp_cpp, " const Pipeline_Use_Element *predUse = pred->resourceUseElement(i);\n"); + fprintf(fp_cpp, " if (predUse->multiple())\n"); + fprintf(fp_cpp, " continue;\n\n"); + fprintf(fp_cpp, " for (uint j = 0; j < resourceUseCount(); j++) {\n"); + fprintf(fp_cpp, " const Pipeline_Use_Element *currUse = resourceUseElement(j);\n"); + fprintf(fp_cpp, " if (currUse->multiple())\n"); + fprintf(fp_cpp, " continue;\n\n"); + fprintf(fp_cpp, " if (predUse->used() & currUse->used()) {\n"); + fprintf(fp_cpp, " Pipeline_Use_Cycle_Mask x = predUse->mask();\n"); + fprintf(fp_cpp, " Pipeline_Use_Cycle_Mask y = currUse->mask();\n\n"); + fprintf(fp_cpp, " for ( y <<= start; x.overlaps(y); start++ )\n"); + fprintf(fp_cpp, " y <<= 1;\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " }\n\n"); + fprintf(fp_cpp, " // There is the potential for overlap\n"); + fprintf(fp_cpp, " return (start);\n"); + fprintf(fp_cpp, "}\n\n"); + fprintf(fp_cpp, "// The following two routines assume that the root Pipeline_Use entity\n"); + fprintf(fp_cpp, "// consists of exactly 1 element for each functional unit\n"); + fprintf(fp_cpp, "// start is relative to the current cycle; used for latency-based info\n"); + fprintf(fp_cpp, "uint Pipeline_Use::full_latency(uint delay, const Pipeline_Use &pred) const {\n"); + fprintf(fp_cpp, " for (uint i = 0; i < pred._count; i++) {\n"); + fprintf(fp_cpp, " const Pipeline_Use_Element *predUse = pred.element(i);\n"); + fprintf(fp_cpp, " if (predUse->_multiple) {\n"); + fprintf(fp_cpp, " uint min_delay = %d;\n", + _pipeline->_maxcycleused+1); + fprintf(fp_cpp, " // Multiple possible functional units, choose first unused one\n"); + fprintf(fp_cpp, " for (uint j = predUse->_lb; j <= predUse->_ub; j++) {\n"); + fprintf(fp_cpp, " const Pipeline_Use_Element *currUse = element(j);\n"); + fprintf(fp_cpp, " uint curr_delay = delay;\n"); + fprintf(fp_cpp, " if (predUse->_used & currUse->_used) {\n"); + fprintf(fp_cpp, " Pipeline_Use_Cycle_Mask x = predUse->_mask;\n"); + fprintf(fp_cpp, " Pipeline_Use_Cycle_Mask y = currUse->_mask;\n\n"); + fprintf(fp_cpp, " for ( y <<= curr_delay; x.overlaps(y); curr_delay++ )\n"); + fprintf(fp_cpp, " y <<= 1;\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " if (min_delay > curr_delay)\n min_delay = curr_delay;\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " if (delay < min_delay)\n delay = min_delay;\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " else {\n"); + fprintf(fp_cpp, " for (uint j = predUse->_lb; j <= predUse->_ub; j++) {\n"); + fprintf(fp_cpp, " const Pipeline_Use_Element *currUse = element(j);\n"); + fprintf(fp_cpp, " if (predUse->_used & currUse->_used) {\n"); + fprintf(fp_cpp, " Pipeline_Use_Cycle_Mask x = predUse->_mask;\n"); + fprintf(fp_cpp, " Pipeline_Use_Cycle_Mask y = currUse->_mask;\n\n"); + fprintf(fp_cpp, " for ( y <<= delay; x.overlaps(y); delay++ )\n"); + fprintf(fp_cpp, " y <<= 1;\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " }\n\n"); + fprintf(fp_cpp, " return (delay);\n"); + fprintf(fp_cpp, "}\n\n"); + fprintf(fp_cpp, "void Pipeline_Use::add_usage(const Pipeline_Use &pred) {\n"); + fprintf(fp_cpp, " for (uint i = 0; i < pred._count; i++) {\n"); + fprintf(fp_cpp, " const Pipeline_Use_Element *predUse = pred.element(i);\n"); + fprintf(fp_cpp, " if (predUse->_multiple) {\n"); + fprintf(fp_cpp, " // Multiple possible functional units, choose first unused one\n"); + fprintf(fp_cpp, " for (uint j = predUse->_lb; j <= predUse->_ub; j++) {\n"); + fprintf(fp_cpp, " Pipeline_Use_Element *currUse = element(j);\n"); + fprintf(fp_cpp, " if ( !predUse->_mask.overlaps(currUse->_mask) ) {\n"); + fprintf(fp_cpp, " currUse->_used |= (1 << j);\n"); + fprintf(fp_cpp, " _resources_used |= (1 << j);\n"); + fprintf(fp_cpp, " currUse->_mask.Or(predUse->_mask);\n"); + fprintf(fp_cpp, " break;\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " else {\n"); + fprintf(fp_cpp, " for (uint j = predUse->_lb; j <= predUse->_ub; j++) {\n"); + fprintf(fp_cpp, " Pipeline_Use_Element *currUse = element(j);\n"); + fprintf(fp_cpp, " currUse->_used |= (1 << j);\n"); + fprintf(fp_cpp, " _resources_used |= (1 << j);\n"); + fprintf(fp_cpp, " currUse->_mask.Or(predUse->_mask);\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, "}\n\n"); + + fprintf(fp_cpp, "uint Pipeline::operand_latency(uint opnd, const Pipeline *pred) const {\n"); + fprintf(fp_cpp, " int const default_latency = 1;\n"); + fprintf(fp_cpp, "\n"); +#if 0 + fprintf(fp_cpp, "#ifndef PRODUCT\n"); + fprintf(fp_cpp, " if (TraceOptoOutput) {\n"); + fprintf(fp_cpp, " tty->print(\"# operand_latency(%%d), _read_stage_count = %%d\\n\", opnd, _read_stage_count);\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, "#endif\n\n"); +#endif + fprintf(fp_cpp, " assert(this, \"NULL pipeline info\")\n"); + fprintf(fp_cpp, " assert(pred, \"NULL predecessor pipline info\")\n\n"); + fprintf(fp_cpp, " if (pred->hasFixedLatency())\n return (pred->fixedLatency());\n\n"); + fprintf(fp_cpp, " // If this is not an operand, then assume a dependence with 0 latency\n"); + fprintf(fp_cpp, " if (opnd > _read_stage_count)\n return (0);\n\n"); + fprintf(fp_cpp, " uint writeStage = pred->_write_stage;\n"); + fprintf(fp_cpp, " uint readStage = _read_stages[opnd-1];\n"); +#if 0 + fprintf(fp_cpp, "\n#ifndef PRODUCT\n"); + fprintf(fp_cpp, " if (TraceOptoOutput) {\n"); + fprintf(fp_cpp, " tty->print(\"# operand_latency: writeStage=%%s readStage=%%s, opnd=%%d\\n\", stageName(writeStage), stageName(readStage), opnd);\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, "#endif\n\n"); +#endif + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, " if (writeStage == stage_undefined || readStage == stage_undefined)\n"); + fprintf(fp_cpp, " return (default_latency);\n"); + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, " int delta = writeStage - readStage;\n"); + fprintf(fp_cpp, " if (delta < 0) delta = 0;\n\n"); +#if 0 + fprintf(fp_cpp, "\n#ifndef PRODUCT\n"); + fprintf(fp_cpp, " if (TraceOptoOutput) {\n"); + fprintf(fp_cpp, " tty->print(\"# operand_latency: delta=%%d\\n\", delta);\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, "#endif\n\n"); +#endif + fprintf(fp_cpp, " return (delta);\n"); + fprintf(fp_cpp, "}\n\n"); + + if (!_pipeline) + /* Do Nothing */; + + else if (_pipeline->_maxcycleused <= +#ifdef SPARC + 64 +#else + 32 +#endif + ) { + fprintf(fp_cpp, "Pipeline_Use_Cycle_Mask operator&(const Pipeline_Use_Cycle_Mask &in1, const Pipeline_Use_Cycle_Mask &in2) {\n"); + fprintf(fp_cpp, " return Pipeline_Use_Cycle_Mask(in1._mask & in2._mask);\n"); + fprintf(fp_cpp, "}\n\n"); + fprintf(fp_cpp, "Pipeline_Use_Cycle_Mask operator|(const Pipeline_Use_Cycle_Mask &in1, const Pipeline_Use_Cycle_Mask &in2) {\n"); + fprintf(fp_cpp, " return Pipeline_Use_Cycle_Mask(in1._mask | in2._mask);\n"); + fprintf(fp_cpp, "}\n\n"); + } + else { + uint l; + uint masklen = (_pipeline->_maxcycleused + 31) >> 5; + fprintf(fp_cpp, "Pipeline_Use_Cycle_Mask operator&(const Pipeline_Use_Cycle_Mask &in1, const Pipeline_Use_Cycle_Mask &in2) {\n"); + fprintf(fp_cpp, " return Pipeline_Use_Cycle_Mask("); + for (l = 1; l <= masklen; l++) + fprintf(fp_cpp, "in1._mask%d & in2._mask%d%s\n", l, l, l < masklen ? ", " : ""); + fprintf(fp_cpp, ");\n"); + fprintf(fp_cpp, "}\n\n"); + fprintf(fp_cpp, "Pipeline_Use_Cycle_Mask operator|(const Pipeline_Use_Cycle_Mask &in1, const Pipeline_Use_Cycle_Mask &in2) {\n"); + fprintf(fp_cpp, " return Pipeline_Use_Cycle_Mask("); + for (l = 1; l <= masklen; l++) + fprintf(fp_cpp, "in1._mask%d | in2._mask%d%s", l, l, l < masklen ? ", " : ""); + fprintf(fp_cpp, ");\n"); + fprintf(fp_cpp, "}\n\n"); + fprintf(fp_cpp, "void Pipeline_Use_Cycle_Mask::Or(const Pipeline_Use_Cycle_Mask &in2) {\n "); + for (l = 1; l <= masklen; l++) + fprintf(fp_cpp, " _mask%d |= in2._mask%d;", l, l); + fprintf(fp_cpp, "\n}\n\n"); + } + + /* Get the length of all the resource names */ + for (_pipeline->_reslist.reset(), resourcenamelen = 0; + (resourcename = _pipeline->_reslist.iter()) != NULL; + resourcenamelen += (int)strlen(resourcename)); + + // Create the pipeline class description + + fprintf(fp_cpp, "static const Pipeline pipeline_class_Zero_Instructions(0, 0, true, 0, 0, false, false, false, false, NULL, NULL, NULL, Pipeline_Use(0, 0, 0, NULL));\n\n"); + fprintf(fp_cpp, "static const Pipeline pipeline_class_Unknown_Instructions(0, 0, true, 0, 0, false, true, true, false, NULL, NULL, NULL, Pipeline_Use(0, 0, 0, NULL));\n\n"); + + fprintf(fp_cpp, "const Pipeline_Use_Element Pipeline_Use::elaborated_elements[%d] = {\n", _pipeline->_rescount); + for (int i1 = 0; i1 < _pipeline->_rescount; i1++) { + fprintf(fp_cpp, " Pipeline_Use_Element(0, %d, %d, false, Pipeline_Use_Cycle_Mask(", i1, i1); + uint masklen = (_pipeline->_maxcycleused + 31) >> 5; + for (int i2 = masklen-1; i2 >= 0; i2--) + fprintf(fp_cpp, "0%s", i2 > 0 ? ", " : ""); + fprintf(fp_cpp, "))%s\n", i1 < (_pipeline->_rescount-1) ? "," : ""); + } + fprintf(fp_cpp, "};\n\n"); + + fprintf(fp_cpp, "const Pipeline_Use Pipeline_Use::elaborated_use(0, 0, %d, (Pipeline_Use_Element *)&elaborated_elements[0]);\n\n", + _pipeline->_rescount); + + for (_pipeline->_classlist.reset(); (classname = _pipeline->_classlist.iter()) != NULL; ) { + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "// Pipeline Class \"%s\"\n", classname); + PipeClassForm *pipeclass = _pipeline->_classdict[classname]->is_pipeclass(); + int maxWriteStage = -1; + int maxMoreInstrs = 0; + int paramcount = 0; + int i = 0; + const char *paramname; + int resource_count = (_pipeline->_rescount + 3) >> 2; + + // Scan the operands, looking for last output stage and number of inputs + for (pipeclass->_parameters.reset(); (paramname = pipeclass->_parameters.iter()) != NULL; ) { + const PipeClassOperandForm *pipeopnd = + (const PipeClassOperandForm *)pipeclass->_localUsage[paramname]; + if (pipeopnd) { + if (pipeopnd->_iswrite) { + int stagenum = _pipeline->_stages.index(pipeopnd->_stage); + int moreinsts = pipeopnd->_more_instrs; + if ((maxWriteStage+maxMoreInstrs) < (stagenum+moreinsts)) { + maxWriteStage = stagenum; + maxMoreInstrs = moreinsts; + } + } + } + + if (i++ > 0 || (pipeopnd && !pipeopnd->isWrite())) + paramcount++; + } + + // Create the list of stages for the operands that are read + // Note that we will build a NameList to reduce the number of copies + + int pipeline_reads_index = pipeline_reads_initializer(fp_cpp, pipeline_reads, pipeclass); + + int pipeline_res_stages_index = pipeline_res_stages_initializer( + fp_cpp, _pipeline, pipeline_res_stages, pipeclass); + + int pipeline_res_cycles_index = pipeline_res_cycles_initializer( + fp_cpp, _pipeline, pipeline_res_cycles, pipeclass); + + int pipeline_res_mask_index = pipeline_res_mask_initializer( + fp_cpp, _pipeline, pipeline_res_masks, pipeline_res_args, pipeclass); + +#if 0 + // Process the Resources + const PipeClassResourceForm *piperesource; + + unsigned resources_used = 0; + unsigned exclusive_resources_used = 0; + unsigned resource_groups = 0; + for (pipeclass->_resUsage.reset(); + (piperesource = (const PipeClassResourceForm *)pipeclass->_resUsage.iter()) != NULL; ) { + int used_mask = _pipeline->_resdict[piperesource->_resource]->is_resource()->mask(); + if (used_mask) + resource_groups++; + resources_used |= used_mask; + if ((used_mask & (used_mask-1)) == 0) + exclusive_resources_used |= used_mask; + } + + if (resource_groups > 0) { + fprintf(fp_cpp, "static const uint pipeline_res_or_masks_%03d[%d] = {", + pipeclass->_num, resource_groups); + for (pipeclass->_resUsage.reset(), i = 1; + (piperesource = (const PipeClassResourceForm *)pipeclass->_resUsage.iter()) != NULL; + i++ ) { + int used_mask = _pipeline->_resdict[piperesource->_resource]->is_resource()->mask(); + if (used_mask) { + fprintf(fp_cpp, " 0x%0*x%c", resource_count, used_mask, i < (int)resource_groups ? ',' : ' '); + } + } + fprintf(fp_cpp, "};\n\n"); + } +#endif + + // Create the pipeline class description + fprintf(fp_cpp, "static const Pipeline pipeline_class_%03d(", + pipeclass->_num); + if (maxWriteStage < 0) + fprintf(fp_cpp, "(uint)stage_undefined"); + else if (maxMoreInstrs == 0) + fprintf(fp_cpp, "(uint)stage_%s", _pipeline->_stages.name(maxWriteStage)); + else + fprintf(fp_cpp, "((uint)stage_%s)+%d", _pipeline->_stages.name(maxWriteStage), maxMoreInstrs); + fprintf(fp_cpp, ", %d, %s, %d, %d, %s, %s, %s, %s,\n", + paramcount, + pipeclass->hasFixedLatency() ? "true" : "false", + pipeclass->fixedLatency(), + pipeclass->InstructionCount(), + pipeclass->hasBranchDelay() ? "true" : "false", + pipeclass->hasMultipleBundles() ? "true" : "false", + pipeclass->forceSerialization() ? "true" : "false", + pipeclass->mayHaveNoCode() ? "true" : "false" ); + if (paramcount > 0) { + fprintf(fp_cpp, "\n (enum machPipelineStages * const) pipeline_reads_%03d,\n ", + pipeline_reads_index+1); + } + else + fprintf(fp_cpp, " NULL,"); + fprintf(fp_cpp, " (enum machPipelineStages * const) pipeline_res_stages_%03d,\n", + pipeline_res_stages_index+1); + fprintf(fp_cpp, " (uint * const) pipeline_res_cycles_%03d,\n", + pipeline_res_cycles_index+1); + fprintf(fp_cpp, " Pipeline_Use(%s, (Pipeline_Use_Element *)", + pipeline_res_args.name(pipeline_res_mask_index)); + if (strlen(pipeline_res_masks.name(pipeline_res_mask_index)) > 0) + fprintf(fp_cpp, "&pipeline_res_mask_%03d[0]", + pipeline_res_mask_index+1); + else + fprintf(fp_cpp, "NULL"); + fprintf(fp_cpp, "));\n"); + } + + // Generate the Node::latency method if _pipeline defined + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "//------------------Inter-Instruction Latency--------------------------------\n"); + fprintf(fp_cpp, "uint Node::latency(uint i) {\n"); + if (_pipeline) { +#if 0 + fprintf(fp_cpp, "#ifndef PRODUCT\n"); + fprintf(fp_cpp, " if (TraceOptoOutput) {\n"); + fprintf(fp_cpp, " tty->print(\"# %%4d->latency(%%d)\\n\", _idx, i);\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, "#endif\n"); +#endif + fprintf(fp_cpp, " uint j;\n"); + fprintf(fp_cpp, " // verify in legal range for inputs\n"); + fprintf(fp_cpp, " assert(i < len(), \"index not in range\");\n\n"); + fprintf(fp_cpp, " // verify input is not null\n"); + fprintf(fp_cpp, " Node *pred = in(i);\n"); + fprintf(fp_cpp, " if (!pred)\n return %d;\n\n", + non_operand_latency); + fprintf(fp_cpp, " if (pred->is_Proj())\n pred = pred->in(0);\n\n"); + fprintf(fp_cpp, " // if either node does not have pipeline info, use default\n"); + fprintf(fp_cpp, " const Pipeline *predpipe = pred->pipeline();\n"); + fprintf(fp_cpp, " assert(predpipe, \"no predecessor pipeline info\");\n\n"); + fprintf(fp_cpp, " if (predpipe->hasFixedLatency())\n return predpipe->fixedLatency();\n\n"); + fprintf(fp_cpp, " const Pipeline *currpipe = pipeline();\n"); + fprintf(fp_cpp, " assert(currpipe, \"no pipeline info\");\n\n"); + fprintf(fp_cpp, " if (!is_Mach())\n return %d;\n\n", + node_latency); + fprintf(fp_cpp, " const MachNode *m = as_Mach();\n"); + fprintf(fp_cpp, " j = m->oper_input_base();\n"); + fprintf(fp_cpp, " if (i < j)\n return currpipe->functional_unit_latency(%d, predpipe);\n\n", + non_operand_latency); + fprintf(fp_cpp, " // determine which operand this is in\n"); + fprintf(fp_cpp, " uint n = m->num_opnds();\n"); + fprintf(fp_cpp, " int delta = %d;\n\n", + non_operand_latency); + fprintf(fp_cpp, " uint k;\n"); + fprintf(fp_cpp, " for (k = 1; k < n; k++) {\n"); + fprintf(fp_cpp, " j += m->_opnds[k]->num_edges();\n"); + fprintf(fp_cpp, " if (i < j)\n"); + fprintf(fp_cpp, " break;\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, " if (k < n)\n"); + fprintf(fp_cpp, " delta = currpipe->operand_latency(k,predpipe);\n\n"); + fprintf(fp_cpp, " return currpipe->functional_unit_latency(delta, predpipe);\n"); + } + else { + fprintf(fp_cpp, " // assert(false, \"pipeline functionality is not defined\");\n"); + fprintf(fp_cpp, " return %d;\n", + non_operand_latency); + } + fprintf(fp_cpp, "}\n\n"); + + // Output the list of nop nodes + fprintf(fp_cpp, "// Descriptions for emitting different functional unit nops\n"); + const char *nop; + int nopcnt = 0; + for ( _pipeline->_noplist.reset(); (nop = _pipeline->_noplist.iter()) != NULL; nopcnt++ ); + + fprintf(fp_cpp, "void Bundle::initialize_nops(MachNode * nop_list[%d], Compile *C) {\n", nopcnt); + int i = 0; + for ( _pipeline->_noplist.reset(); (nop = _pipeline->_noplist.iter()) != NULL; i++ ) { + fprintf(fp_cpp, " nop_list[%d] = (MachNode *) new (C) %sNode();\n", i, nop); + } + fprintf(fp_cpp, "};\n\n"); + fprintf(fp_cpp, "#ifndef PRODUCT\n"); + fprintf(fp_cpp, "void Bundle::dump() const {\n"); + fprintf(fp_cpp, " static const char * bundle_flags[] = {\n"); + fprintf(fp_cpp, " \"\",\n"); + fprintf(fp_cpp, " \"use nop delay\",\n"); + fprintf(fp_cpp, " \"use unconditional delay\",\n"); + fprintf(fp_cpp, " \"use conditional delay\",\n"); + fprintf(fp_cpp, " \"used in conditional delay\",\n"); + fprintf(fp_cpp, " \"used in unconditional delay\",\n"); + fprintf(fp_cpp, " \"used in all conditional delays\",\n"); + fprintf(fp_cpp, " };\n\n"); + + fprintf(fp_cpp, " static const char *resource_names[%d] = {", _pipeline->_rescount); + for (i = 0; i < _pipeline->_rescount; i++) + fprintf(fp_cpp, " \"%s\"%c", _pipeline->_reslist.name(i), i < _pipeline->_rescount-1 ? ',' : ' '); + fprintf(fp_cpp, "};\n\n"); + + // See if the same string is in the table + fprintf(fp_cpp, " bool needs_comma = false;\n\n"); + fprintf(fp_cpp, " if (_flags) {\n"); + fprintf(fp_cpp, " tty->print(\"%%s\", bundle_flags[_flags]);\n"); + fprintf(fp_cpp, " needs_comma = true;\n"); + fprintf(fp_cpp, " };\n"); + fprintf(fp_cpp, " if (instr_count()) {\n"); + fprintf(fp_cpp, " tty->print(\"%%s%%d instr%%s\", needs_comma ? \", \" : \"\", instr_count(), instr_count() != 1 ? \"s\" : \"\");\n"); + fprintf(fp_cpp, " needs_comma = true;\n"); + fprintf(fp_cpp, " };\n"); + fprintf(fp_cpp, " uint r = resources_used();\n"); + fprintf(fp_cpp, " if (r) {\n"); + fprintf(fp_cpp, " tty->print(\"%%sresource%%s:\", needs_comma ? \", \" : \"\", (r & (r-1)) != 0 ? \"s\" : \"\");\n"); + fprintf(fp_cpp, " for (uint i = 0; i < %d; i++)\n", _pipeline->_rescount); + fprintf(fp_cpp, " if ((r & (1 << i)) != 0)\n"); + fprintf(fp_cpp, " tty->print(\" %%s\", resource_names[i]);\n"); + fprintf(fp_cpp, " needs_comma = true;\n"); + fprintf(fp_cpp, " };\n"); + fprintf(fp_cpp, " tty->print(\"\\n\");\n"); + fprintf(fp_cpp, "}\n"); + fprintf(fp_cpp, "#endif\n"); +} + +// --------------------------------------------------------------------------- +//------------------------------Utilities to build Instruction Classes-------- +// --------------------------------------------------------------------------- + +static void defineOut_RegMask(FILE *fp, const char *node, const char *regMask) { + fprintf(fp,"const RegMask &%sNode::out_RegMask() const { return (%s); }\n", + node, regMask); +} + +// Scan the peepmatch and output a test for each instruction +static void check_peepmatch_instruction_tree(FILE *fp, PeepMatch *pmatch, PeepConstraint *pconstraint) { + intptr_t parent = -1; + intptr_t inst_position = 0; + const char *inst_name = NULL; + intptr_t input = 0; + fprintf(fp, " // Check instruction sub-tree\n"); + pmatch->reset(); + for( pmatch->next_instruction( parent, inst_position, inst_name, input ); + inst_name != NULL; + pmatch->next_instruction( parent, inst_position, inst_name, input ) ) { + // If this is not a placeholder + if( ! pmatch->is_placeholder() ) { + // Define temporaries 'inst#', based on parent and parent's input index + if( parent != -1 ) { // root was initialized + fprintf(fp, " inst%ld = inst%ld->in(%ld);\n", + inst_position, parent, input); + } + + // When not the root + // Test we have the correct instruction by comparing the rule + if( parent != -1 ) { + fprintf(fp, " matches = matches && ( inst%ld->rule() == %s_rule );", + inst_position, inst_name); + } + } else { + // Check that user did not try to constrain a placeholder + assert( ! pconstraint->constrains_instruction(inst_position), + "fatal(): Can not constrain a placeholder instruction"); + } + } +} + +static void print_block_index(FILE *fp, intptr_t inst_position) { + assert( inst_position >= 0, "Instruction number less than zero"); + fprintf(fp, "block_index"); + if( inst_position != 0 ) { + fprintf(fp, " - %ld", inst_position); + } +} + +// Scan the peepmatch and output a test for each instruction +static void check_peepmatch_instruction_sequence(FILE *fp, PeepMatch *pmatch, PeepConstraint *pconstraint) { + intptr_t parent = -1; + intptr_t inst_position = 0; + const char *inst_name = NULL; + intptr_t input = 0; + fprintf(fp, " // Check instruction sub-tree\n"); + pmatch->reset(); + for( pmatch->next_instruction( parent, inst_position, inst_name, input ); + inst_name != NULL; + pmatch->next_instruction( parent, inst_position, inst_name, input ) ) { + // If this is not a placeholder + if( ! pmatch->is_placeholder() ) { + // Define temporaries 'inst#', based on parent and parent's input index + if( parent != -1 ) { // root was initialized + fprintf(fp, " // Identify previous instruction if inside this block\n"); + fprintf(fp, " if( "); + print_block_index(fp, inst_position); + fprintf(fp, " > 0 ) {\n Node *n = block->_nodes.at("); + print_block_index(fp, inst_position); + fprintf(fp, ");\n inst%ld = (n->is_Mach()) ? ", inst_position); + fprintf(fp, "n->as_Mach() : NULL;\n }\n"); + } + + // When not the root + // Test we have the correct instruction by comparing the rule. + if( parent != -1 ) { + fprintf(fp, " matches = matches && (inst%ld != NULL) && (inst%ld->rule() == %s_rule);\n", + inst_position, inst_position, inst_name); + } + } else { + // Check that user did not try to constrain a placeholder + assert( ! pconstraint->constrains_instruction(inst_position), + "fatal(): Can not constrain a placeholder instruction"); + } + } +} + +// Build mapping for register indices, num_edges to input +static void build_instruction_index_mapping( FILE *fp, FormDict &globals, PeepMatch *pmatch ) { + intptr_t parent = -1; + intptr_t inst_position = 0; + const char *inst_name = NULL; + intptr_t input = 0; + fprintf(fp, " // Build map to register info\n"); + pmatch->reset(); + for( pmatch->next_instruction( parent, inst_position, inst_name, input ); + inst_name != NULL; + pmatch->next_instruction( parent, inst_position, inst_name, input ) ) { + // If this is not a placeholder + if( ! pmatch->is_placeholder() ) { + // Define temporaries 'inst#', based on self's inst_position + InstructForm *inst = globals[inst_name]->is_instruction(); + if( inst != NULL ) { + char inst_prefix[] = "instXXXX_"; + sprintf(inst_prefix, "inst%ld_", inst_position); + char receiver[] = "instXXXX->"; + sprintf(receiver, "inst%ld->", inst_position); + inst->index_temps( fp, globals, inst_prefix, receiver ); + } + } + } +} + +// Generate tests for the constraints +static void check_peepconstraints(FILE *fp, FormDict &globals, PeepMatch *pmatch, PeepConstraint *pconstraint) { + fprintf(fp, "\n"); + fprintf(fp, " // Check constraints on sub-tree-leaves\n"); + + // Build mapping from num_edges to local variables + build_instruction_index_mapping( fp, globals, pmatch ); + + // Build constraint tests + if( pconstraint != NULL ) { + fprintf(fp, " matches = matches &&"); + bool first_constraint = true; + while( pconstraint != NULL ) { + // indentation and connecting '&&' + const char *indentation = " "; + fprintf(fp, "\n%s%s", indentation, (!first_constraint ? "&& " : " ")); + + // Only have '==' relation implemented + if( strcmp(pconstraint->_relation,"==") != 0 ) { + assert( false, "Unimplemented()" ); + } + + // LEFT + intptr_t left_index = pconstraint->_left_inst; + const char *left_op = pconstraint->_left_op; + // Access info on the instructions whose operands are compared + InstructForm *inst_left = globals[pmatch->instruction_name(left_index)]->is_instruction(); + assert( inst_left, "Parser should guaranty this is an instruction"); + int left_op_base = inst_left->oper_input_base(globals); + // Access info on the operands being compared + int left_op_index = inst_left->operand_position(left_op, Component::USE); + if( left_op_index == -1 ) { + left_op_index = inst_left->operand_position(left_op, Component::DEF); + if( left_op_index == -1 ) { + left_op_index = inst_left->operand_position(left_op, Component::USE_DEF); + } + } + assert( left_op_index != NameList::Not_in_list, "Did not find operand in instruction"); + ComponentList components_left = inst_left->_components; + const char *left_comp_type = components_left.at(left_op_index)->_type; + OpClassForm *left_opclass = globals[left_comp_type]->is_opclass(); + Form::InterfaceType left_interface_type = left_opclass->interface_type(globals); + + + // RIGHT + int right_op_index = -1; + intptr_t right_index = pconstraint->_right_inst; + const char *right_op = pconstraint->_right_op; + if( right_index != -1 ) { // Match operand + // Access info on the instructions whose operands are compared + InstructForm *inst_right = globals[pmatch->instruction_name(right_index)]->is_instruction(); + assert( inst_right, "Parser should guaranty this is an instruction"); + int right_op_base = inst_right->oper_input_base(globals); + // Access info on the operands being compared + right_op_index = inst_right->operand_position(right_op, Component::USE); + if( right_op_index == -1 ) { + right_op_index = inst_right->operand_position(right_op, Component::DEF); + if( right_op_index == -1 ) { + right_op_index = inst_right->operand_position(right_op, Component::USE_DEF); + } + } + assert( right_op_index != NameList::Not_in_list, "Did not find operand in instruction"); + ComponentList components_right = inst_right->_components; + const char *right_comp_type = components_right.at(right_op_index)->_type; + OpClassForm *right_opclass = globals[right_comp_type]->is_opclass(); + Form::InterfaceType right_interface_type = right_opclass->interface_type(globals); + assert( right_interface_type == left_interface_type, "Both must be same interface"); + + } else { // Else match register + // assert( false, "should be a register" ); + } + + // + // Check for equivalence + // + // fprintf(fp, "phase->eqv( "); + // fprintf(fp, "inst%d->in(%d+%d) /* %s */, inst%d->in(%d+%d) /* %s */", + // left_index, left_op_base, left_op_index, left_op, + // right_index, right_op_base, right_op_index, right_op ); + // fprintf(fp, ")"); + // + switch( left_interface_type ) { + case Form::register_interface: { + // Check that they are allocated to the same register + // Need parameter for index position if not result operand + char left_reg_index[] = ",instXXXX_idxXXXX"; + if( left_op_index != 0 ) { + assert( (left_index <= 9999) && (left_op_index <= 9999), "exceed string size"); + // Must have index into operands + sprintf(left_reg_index,",inst%d_idx%d", left_index, left_op_index); + } else { + strcpy(left_reg_index, ""); + } + fprintf(fp, "(inst%d->_opnds[%d]->reg(ra_,inst%d%s) /* %d.%s */", + left_index, left_op_index, left_index, left_reg_index, left_index, left_op ); + fprintf(fp, " == "); + + if( right_index != -1 ) { + char right_reg_index[18] = ",instXXXX_idxXXXX"; + if( right_op_index != 0 ) { + assert( (right_index <= 9999) && (right_op_index <= 9999), "exceed string size"); + // Must have index into operands + sprintf(right_reg_index,",inst%d_idx%d", right_index, right_op_index); + } else { + strcpy(right_reg_index, ""); + } + fprintf(fp, "/* %d.%s */ inst%d->_opnds[%d]->reg(ra_,inst%d%s)", + right_index, right_op, right_index, right_op_index, right_index, right_reg_index ); + } else { + fprintf(fp, "%s_enc", right_op ); + } + fprintf(fp,")"); + break; + } + case Form::constant_interface: { + // Compare the '->constant()' values + fprintf(fp, "(inst%d->_opnds[%d]->constant() /* %d.%s */", + left_index, left_op_index, left_index, left_op ); + fprintf(fp, " == "); + fprintf(fp, "/* %d.%s */ inst%d->_opnds[%d]->constant())", + right_index, right_op, right_index, right_op_index ); + break; + } + case Form::memory_interface: { + // Compare 'base', 'index', 'scale', and 'disp' + // base + fprintf(fp, "( \n"); + fprintf(fp, " (inst%d->_opnds[%d]->base(ra_,inst%d,inst%d_idx%d) /* %d.%s$$base */", + left_index, left_op_index, left_index, left_index, left_op_index, left_index, left_op ); + fprintf(fp, " == "); + fprintf(fp, "/* %d.%s$$base */ inst%d->_opnds[%d]->base(ra_,inst%d,inst%d_idx%d)) &&\n", + right_index, right_op, right_index, right_op_index, right_index, right_index, right_op_index ); + // index + fprintf(fp, " (inst%d->_opnds[%d]->index(ra_,inst%d,inst%d_idx%d) /* %d.%s$$index */", + left_index, left_op_index, left_index, left_index, left_op_index, left_index, left_op ); + fprintf(fp, " == "); + fprintf(fp, "/* %d.%s$$index */ inst%d->_opnds[%d]->index(ra_,inst%d,inst%d_idx%d)) &&\n", + right_index, right_op, right_index, right_op_index, right_index, right_index, right_op_index ); + // scale + fprintf(fp, " (inst%d->_opnds[%d]->scale() /* %d.%s$$scale */", + left_index, left_op_index, left_index, left_op ); + fprintf(fp, " == "); + fprintf(fp, "/* %d.%s$$scale */ inst%d->_opnds[%d]->scale()) &&\n", + right_index, right_op, right_index, right_op_index ); + // disp + fprintf(fp, " (inst%d->_opnds[%d]->disp(ra_,inst%d,inst%d_idx%d) /* %d.%s$$disp */", + left_index, left_op_index, left_index, left_index, left_op_index, left_index, left_op ); + fprintf(fp, " == "); + fprintf(fp, "/* %d.%s$$disp */ inst%d->_opnds[%d]->disp(ra_,inst%d,inst%d_idx%d))\n", + right_index, right_op, right_index, right_op_index, right_index, right_index, right_op_index ); + fprintf(fp, ") \n"); + break; + } + case Form::conditional_interface: { + // Compare the condition code being tested + assert( false, "Unimplemented()" ); + break; + } + default: { + assert( false, "ShouldNotReachHere()" ); + break; + } + } + + // Advance to next constraint + pconstraint = pconstraint->next(); + first_constraint = false; + } + + fprintf(fp, ";\n"); + } +} + +// // EXPERIMENTAL -- TEMPORARY code +// static Form::DataType get_operand_type(FormDict &globals, InstructForm *instr, const char *op_name ) { +// int op_index = instr->operand_position(op_name, Component::USE); +// if( op_index == -1 ) { +// op_index = instr->operand_position(op_name, Component::DEF); +// if( op_index == -1 ) { +// op_index = instr->operand_position(op_name, Component::USE_DEF); +// } +// } +// assert( op_index != NameList::Not_in_list, "Did not find operand in instruction"); +// +// ComponentList components_right = instr->_components; +// char *right_comp_type = components_right.at(op_index)->_type; +// OpClassForm *right_opclass = globals[right_comp_type]->is_opclass(); +// Form::InterfaceType right_interface_type = right_opclass->interface_type(globals); +// +// return; +// } + +// Construct the new sub-tree +static void generate_peepreplace( FILE *fp, FormDict &globals, PeepMatch *pmatch, PeepConstraint *pconstraint, PeepReplace *preplace, int max_position ) { + fprintf(fp, " // IF instructions and constraints matched\n"); + fprintf(fp, " if( matches ) {\n"); + fprintf(fp, " // generate the new sub-tree\n"); + fprintf(fp, " assert( true, \"Debug stopping point\");\n"); + if( preplace != NULL ) { + // Get the root of the new sub-tree + const char *root_inst = NULL; + preplace->next_instruction(root_inst); + InstructForm *root_form = globals[root_inst]->is_instruction(); + assert( root_form != NULL, "Replacement instruction was not previously defined"); + fprintf(fp, " %sNode *root = new (C) %sNode();\n", root_inst, root_inst); + + intptr_t inst_num; + const char *op_name; + int opnds_index = 0; // define result operand + // Then install the use-operands for the new sub-tree + // preplace->reset(); // reset breaks iteration + for( preplace->next_operand( inst_num, op_name ); + op_name != NULL; + preplace->next_operand( inst_num, op_name ) ) { + InstructForm *inst_form; + inst_form = globals[pmatch->instruction_name(inst_num)]->is_instruction(); + assert( inst_form, "Parser should guaranty this is an instruction"); + int op_base = inst_form->oper_input_base(globals); + int inst_op_num = inst_form->operand_position(op_name, Component::USE); + if( inst_op_num == NameList::Not_in_list ) + inst_op_num = inst_form->operand_position(op_name, Component::USE_DEF); + assert( inst_op_num != NameList::Not_in_list, "Did not find operand as USE"); + // find the name of the OperandForm from the local name + const Form *form = inst_form->_localNames[op_name]; + OperandForm *op_form = form->is_operand(); + if( opnds_index == 0 ) { + // Initial setup of new instruction + fprintf(fp, " // ----- Initial setup -----\n"); + // + // Add control edge for this node + fprintf(fp, " root->add_req(_in[0]); // control edge\n"); + // Add unmatched edges from root of match tree + int op_base = root_form->oper_input_base(globals); + for( int unmatched_edge = 1; unmatched_edge < op_base; ++unmatched_edge ) { + fprintf(fp, " root->add_req(inst%ld->in(%d)); // unmatched ideal edge\n", + inst_num, unmatched_edge); + } + // If new instruction captures bottom type + if( root_form->captures_bottom_type() ) { + // Get bottom type from instruction whose result we are replacing + fprintf(fp, " root->_bottom_type = inst%ld->bottom_type();\n", inst_num); + } + // Define result register and result operand + fprintf(fp, " ra_->add_reference(root, inst%ld);\n", inst_num); + fprintf(fp, " ra_->set_oop (root, ra_->is_oop(inst%ld));\n", inst_num); + fprintf(fp, " ra_->set_pair(root->_idx, ra_->get_reg_second(inst%ld), ra_->get_reg_first(inst%ld));\n", inst_num, inst_num); + fprintf(fp, " root->_opnds[0] = inst%ld->_opnds[0]->clone(C); // result\n", inst_num); + fprintf(fp, " // ----- Done with initial setup -----\n"); + } else { + if( (op_form == NULL) || (op_form->is_base_constant(globals) == Form::none) ) { + // Do not have ideal edges for constants after matching + fprintf(fp, " for( unsigned x%d = inst%ld_idx%d; x%d < inst%ld_idx%d; x%d++ )\n", + inst_op_num, inst_num, inst_op_num, + inst_op_num, inst_num, inst_op_num+1, inst_op_num ); + fprintf(fp, " root->add_req( inst%ld->in(x%d) );\n", + inst_num, inst_op_num ); + } else { + fprintf(fp, " // no ideal edge for constants after matching\n"); + } + fprintf(fp, " root->_opnds[%d] = inst%ld->_opnds[%d]->clone(C);\n", + opnds_index, inst_num, inst_op_num ); + } + ++opnds_index; + } + }else { + // Replacing subtree with empty-tree + assert( false, "ShouldNotReachHere();"); + } + + // Return the new sub-tree + fprintf(fp, " deleted = %d;\n", max_position+1 /*zero to one based*/); + fprintf(fp, " return root; // return new root;\n"); + fprintf(fp, " }\n"); +} + + +// Define the Peephole method for an instruction node +void ArchDesc::definePeephole(FILE *fp, InstructForm *node) { + // Generate Peephole function header + fprintf(fp, "MachNode *%sNode::peephole( Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted, Compile* C ) {\n", node->_ident); + fprintf(fp, " bool matches = true;\n"); + + // Identify the maximum instruction position, + // generate temporaries that hold current instruction + // + // MachNode *inst0 = NULL; + // ... + // MachNode *instMAX = NULL; + // + int max_position = 0; + Peephole *peep; + for( peep = node->peepholes(); peep != NULL; peep = peep->next() ) { + PeepMatch *pmatch = peep->match(); + assert( pmatch != NULL, "fatal(), missing peepmatch rule"); + if( max_position < pmatch->max_position() ) max_position = pmatch->max_position(); + } + for( int i = 0; i <= max_position; ++i ) { + if( i == 0 ) { + fprintf(fp, " MachNode *inst0 = this;\n", i); + } else { + fprintf(fp, " MachNode *inst%d = NULL;\n", i); + } + } + + // For each peephole rule in architecture description + // Construct a test for the desired instruction sub-tree + // then check the constraints + // If these match, Generate the new subtree + for( peep = node->peepholes(); peep != NULL; peep = peep->next() ) { + int peephole_number = peep->peephole_number(); + PeepMatch *pmatch = peep->match(); + PeepConstraint *pconstraint = peep->constraints(); + PeepReplace *preplace = peep->replacement(); + + // Root of this peephole is the current MachNode + assert( true, // %%name?%% strcmp( node->_ident, pmatch->name(0) ) == 0, + "root of PeepMatch does not match instruction"); + + // Make each peephole rule individually selectable + fprintf(fp, " if( (OptoPeepholeAt == -1) || (OptoPeepholeAt==%d) ) {\n", peephole_number); + fprintf(fp, " matches = true;\n"); + // Scan the peepmatch and output a test for each instruction + check_peepmatch_instruction_sequence( fp, pmatch, pconstraint ); + + // Check constraints and build replacement inside scope + fprintf(fp, " // If instruction subtree matches\n"); + fprintf(fp, " if( matches ) {\n"); + + // Generate tests for the constraints + check_peepconstraints( fp, _globalNames, pmatch, pconstraint ); + + // Construct the new sub-tree + generate_peepreplace( fp, _globalNames, pmatch, pconstraint, preplace, max_position ); + + // End of scope for this peephole's constraints + fprintf(fp, " }\n"); + // Closing brace '}' to make each peephole rule individually selectable + fprintf(fp, " } // end of peephole rule #%d\n", peephole_number); + fprintf(fp, "\n"); + } + + fprintf(fp, " return NULL; // No peephole rules matched\n"); + fprintf(fp, "}\n"); + fprintf(fp, "\n"); +} + +// Define the Expand method for an instruction node +void ArchDesc::defineExpand(FILE *fp, InstructForm *node) { + unsigned cnt = 0; // Count nodes we have expand into + unsigned i; + + // Generate Expand function header + fprintf(fp,"MachNode *%sNode::Expand(State *state, Node_List &proj_list) {\n", node->_ident); + fprintf(fp,"Compile* C = Compile::current();\n"); + // Generate expand code + if( node->expands() ) { + const char *opid; + int new_pos, exp_pos; + const char *new_id = NULL; + const Form *frm = NULL; + InstructForm *new_inst = NULL; + OperandForm *new_oper = NULL; + unsigned numo = node->num_opnds() + + node->_exprule->_newopers.count(); + + // If necessary, generate any operands created in expand rule + if (node->_exprule->_newopers.count()) { + for(node->_exprule->_newopers.reset(); + (new_id = node->_exprule->_newopers.iter()) != NULL; cnt++) { + frm = node->_localNames[new_id]; + assert(frm, "Invalid entry in new operands list of expand rule"); + new_oper = frm->is_operand(); + char *tmp = (char *)node->_exprule->_newopconst[new_id]; + if (tmp == NULL) { + fprintf(fp," MachOper *op%d = new (C) %sOper();\n", + cnt, new_oper->_ident); + } + else { + fprintf(fp," MachOper *op%d = new (C) %sOper(%s);\n", + cnt, new_oper->_ident, tmp); + } + } + } + cnt = 0; + // Generate the temps to use for DAG building + for(i = 0; i < numo; i++) { + if (i < node->num_opnds()) { + fprintf(fp," MachNode *tmp%d = this;\n", i); + } + else { + fprintf(fp," MachNode *tmp%d = NULL;\n", i); + } + } + // Build mapping from num_edges to local variables + fprintf(fp," unsigned num0 = 0;\n"); + for( i = 1; i < node->num_opnds(); i++ ) { + fprintf(fp," unsigned num%d = opnd_array(%d)->num_edges();\n",i,i); + } + + // Build a mapping from operand index to input edges + fprintf(fp," unsigned idx0 = oper_input_base();\n"); + for( i = 0; i < node->num_opnds(); i++ ) { + fprintf(fp," unsigned idx%d = idx%d + num%d;\n", + i+1,i,i); + } + + // Declare variable to hold root of expansion + fprintf(fp," MachNode *result = NULL;\n"); + + // Iterate over the instructions 'node' expands into + ExpandRule *expand = node->_exprule; + NameAndList *expand_instr = NULL; + for(expand->reset_instructions(); + (expand_instr = expand->iter_instructions()) != NULL; cnt++) { + new_id = expand_instr->name(); + + InstructForm* expand_instruction = (InstructForm*)globalAD->globalNames()[new_id]; + if (expand_instruction->has_temps()) { + globalAD->syntax_err(node->_linenum, "In %s: expand rules using instructs with TEMPs aren't supported: %s", + node->_ident, new_id); + } + + // Build the node for the instruction + fprintf(fp,"\n %sNode *n%d = new (C) %sNode();\n", new_id, cnt, new_id); + // Add control edge for this node + fprintf(fp," n%d->add_req(_in[0]);\n", cnt); + // Build the operand for the value this node defines. + Form *form = (Form*)_globalNames[new_id]; + assert( form, "'new_id' must be a defined form name"); + // Grab the InstructForm for the new instruction + new_inst = form->is_instruction(); + assert( new_inst, "'new_id' must be an instruction name"); + if( node->is_ideal_if() && new_inst->is_ideal_if() ) { + fprintf(fp, " ((MachIfNode*)n%d)->_prob = _prob;\n",cnt); + fprintf(fp, " ((MachIfNode*)n%d)->_fcnt = _fcnt;\n",cnt); + } + + if( node->is_ideal_fastlock() && new_inst->is_ideal_fastlock() ) { + fprintf(fp, " ((MachFastLockNode*)n%d)->_counters = _counters;\n",cnt); + } + + const char *resultOper = new_inst->reduce_result(); + fprintf(fp," n%d->set_opnd_array(0, state->MachOperGenerator( %s, C ));\n", + cnt, machOperEnum(resultOper)); + + // get the formal operand NameList + NameList *formal_lst = &new_inst->_parameters; + formal_lst->reset(); + + // Handle any memory operand + int memory_operand = new_inst->memory_operand(_globalNames); + if( memory_operand != InstructForm::NO_MEMORY_OPERAND ) { + int node_mem_op = node->memory_operand(_globalNames); + assert( node_mem_op != InstructForm::NO_MEMORY_OPERAND, + "expand rule member needs memory but top-level inst doesn't have any" ); + // Copy memory edge + fprintf(fp," n%d->add_req(_in[1]);\t// Add memory edge\n", cnt); + } + + // Iterate over the new instruction's operands + for( expand_instr->reset(); (opid = expand_instr->iter()) != NULL; ) { + // Use 'parameter' at current position in list of new instruction's formals + // instead of 'opid' when looking up info internal to new_inst + const char *parameter = formal_lst->iter(); + // Check for an operand which is created in the expand rule + if ((exp_pos = node->_exprule->_newopers.index(opid)) != -1) { + new_pos = new_inst->operand_position(parameter,Component::USE); + exp_pos += node->num_opnds(); + // If there is no use of the created operand, just skip it + if (new_pos != -1) { + //Copy the operand from the original made above + fprintf(fp," n%d->set_opnd_array(%d, op%d->clone(C)); // %s\n", + cnt, new_pos, exp_pos-node->num_opnds(), opid); + // Check for who defines this operand & add edge if needed + fprintf(fp," if(tmp%d != NULL)\n", exp_pos); + fprintf(fp," n%d->add_req(tmp%d);\n", cnt, exp_pos); + } + } + else { + // Use operand name to get an index into instruction component list + // ins = (InstructForm *) _globalNames[new_id]; + exp_pos = node->operand_position_format(opid); + assert(exp_pos != -1, "Bad expand rule"); + + new_pos = new_inst->operand_position(parameter,Component::USE); + if (new_pos != -1) { + // Copy the operand from the ExpandNode to the new node + fprintf(fp," n%d->set_opnd_array(%d, opnd_array(%d)->clone(C)); // %s\n", + cnt, new_pos, exp_pos, opid); + // For each operand add appropriate input edges by looking at tmp's + fprintf(fp," if(tmp%d == this) {\n", exp_pos); + // Grab corresponding edges from ExpandNode and insert them here + fprintf(fp," for(unsigned i = 0; i < num%d; i++) {\n", exp_pos); + fprintf(fp," n%d->add_req(_in[i + idx%d]);\n", cnt, exp_pos); + fprintf(fp," }\n"); + fprintf(fp," }\n"); + // This value is generated by one of the new instructions + fprintf(fp," else n%d->add_req(tmp%d);\n", cnt, exp_pos); + } + } + + // Update the DAG tmp's for values defined by this instruction + int new_def_pos = new_inst->operand_position(parameter,Component::DEF); + Effect *eform = (Effect *)new_inst->_effects[parameter]; + // If this operand is a definition in either an effects rule + // or a match rule + if((eform) && (is_def(eform->_use_def))) { + // Update the temp associated with this operand + fprintf(fp," tmp%d = n%d;\n", exp_pos, cnt); + } + else if( new_def_pos != -1 ) { + // Instruction defines a value but user did not declare it + // in the 'effect' clause + fprintf(fp," tmp%d = n%d;\n", exp_pos, cnt); + } + } // done iterating over a new instruction's operands + + // Invoke Expand() for the newly created instruction. + fprintf(fp," result = n%d->Expand( state, proj_list );\n", cnt); + assert( !new_inst->expands(), "Do not have complete support for recursive expansion"); + } // done iterating over new instructions + fprintf(fp,"\n"); + } // done generating expand rule + + else if( node->_matrule != NULL ) { + // Remove duplicated operands and inputs which use the same name. + // Seach through match operands for the same name usage. + uint cur_num_opnds = node->num_opnds(); + if( cur_num_opnds > 1 && cur_num_opnds != node->num_unique_opnds() ) { + Component *comp = NULL; + // Build mapping from num_edges to local variables + fprintf(fp," unsigned num0 = 0;\n"); + for( i = 1; i < cur_num_opnds; i++ ) { + fprintf(fp," unsigned num%d = opnd_array(%d)->num_edges();\n",i,i); + } + // Build a mapping from operand index to input edges + fprintf(fp," unsigned idx0 = oper_input_base();\n"); + for( i = 0; i < cur_num_opnds; i++ ) { + fprintf(fp," unsigned idx%d = idx%d + num%d;\n", + i+1,i,i); + } + + uint new_num_opnds = 1; + node->_components.reset(); + // Skip first unique operands. + for( i = 1; i < cur_num_opnds; i++ ) { + comp = node->_components.iter(); + if( (int)i != node->unique_opnds_idx(i) ) { + break; + } + new_num_opnds++; + } + // Replace not unique operands with next unique operands. + for( ; i < cur_num_opnds; i++ ) { + comp = node->_components.iter(); + int j = node->unique_opnds_idx(i); + // unique_opnds_idx(i) is unique if unique_opnds_idx(j) is not unique. + if( j != node->unique_opnds_idx(j) ) { + fprintf(fp," set_opnd_array(%d, opnd_array(%d)->clone(C)); // %s\n", + new_num_opnds, i, comp->_name); + // delete not unique edges here + fprintf(fp," for(unsigned i = 0; i < num%d; i++) {\n", i); + fprintf(fp," set_req(i + idx%d, _in[i + idx%d]);\n", new_num_opnds, i); + fprintf(fp," }\n"); + fprintf(fp," num%d = num%d;\n", new_num_opnds, i); + fprintf(fp," idx%d = idx%d + num%d;\n", new_num_opnds+1, new_num_opnds, new_num_opnds); + new_num_opnds++; + } + } + // delete the rest of edges + fprintf(fp," for(int i = idx%d - 1; i >= (int)idx%d; i--) {\n", cur_num_opnds, new_num_opnds); + fprintf(fp," del_req(i);\n", i); + fprintf(fp," }\n"); + fprintf(fp," _num_opnds = %d;\n", new_num_opnds); + } + } + + + // Generate projections for instruction's additional DEFs and KILLs + if( ! node->expands() && (node->needs_projections() || node->has_temps())) { + // Get string representing the MachNode that projections point at + const char *machNode = "this"; + // Generate the projections + fprintf(fp," // Add projection edges for additional defs or kills\n"); + + // Examine each component to see if it is a DEF or KILL + node->_components.reset(); + // Skip the first component, if already handled as (SET dst (...)) + Component *comp = NULL; + // For kills, the choice of projection numbers is arbitrary + int proj_no = 1; + bool declared_def = false; + bool declared_kill = false; + + while( (comp = node->_components.iter()) != NULL ) { + // Lookup register class associated with operand type + Form *form = (Form*)_globalNames[comp->_type]; + assert( form, "component type must be a defined form"); + OperandForm *op = form->is_operand(); + + if (comp->is(Component::TEMP)) { + fprintf(fp, " // TEMP %s\n", comp->_name); + if (!declared_def) { + // Define the variable "def" to hold new MachProjNodes + fprintf(fp, " MachTempNode *def;\n"); + declared_def = true; + } + if (op && op->_interface && op->_interface->is_RegInterface()) { + fprintf(fp," def = new (C) MachTempNode(state->MachOperGenerator( %s, C ));\n", + machOperEnum(op->_ident)); + fprintf(fp," add_req(def);\n"); + int idx = node->operand_position_format(comp->_name); + fprintf(fp," set_opnd_array(%d, state->MachOperGenerator( %s, C ));\n", + idx, machOperEnum(op->_ident)); + } else { + assert(false, "can't have temps which aren't registers"); + } + } else if (comp->isa(Component::KILL)) { + fprintf(fp, " // DEF/KILL %s\n", comp->_name); + + if (!declared_kill) { + // Define the variable "kill" to hold new MachProjNodes + fprintf(fp, " MachProjNode *kill;\n"); + declared_kill = true; + } + + assert( op, "Support additional KILLS for base operands"); + const char *regmask = reg_mask(*op); + const char *ideal_type = op->ideal_type(_globalNames, _register); + + if (!op->is_bound_register()) { + syntax_err(node->_linenum, "In %s only bound registers can be killed: %s %s\n", + node->_ident, comp->_type, comp->_name); + } + + fprintf(fp," kill = "); + fprintf(fp,"new (C, 1) MachProjNode( %s, %d, (%s), Op_%s );\n", + machNode, proj_no++, regmask, ideal_type); + fprintf(fp," proj_list.push(kill);\n"); + } + } + } + + fprintf(fp,"\n"); + if( node->expands() ) { + fprintf(fp," return result;\n",cnt-1); + } else { + fprintf(fp," return this;\n"); + } + fprintf(fp,"}\n"); + fprintf(fp,"\n"); +} + + +//------------------------------Emit Routines---------------------------------- +// Special classes and routines for defining node emit routines which output +// target specific instruction object encodings. +// Define the ___Node::emit() routine +// +// (1) void ___Node::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { +// (2) // ... encoding defined by user +// (3) +// (4) } +// + +class DefineEmitState { +private: + enum reloc_format { RELOC_NONE = -1, + RELOC_IMMEDIATE = 0, + RELOC_DISP = 1, + RELOC_CALL_DISP = 2 }; + enum literal_status{ LITERAL_NOT_SEEN = 0, + LITERAL_SEEN = 1, + LITERAL_ACCESSED = 2, + LITERAL_OUTPUT = 3 }; + // Temporaries that describe current operand + bool _cleared; + OpClassForm *_opclass; + OperandForm *_operand; + int _operand_idx; + const char *_local_name; + const char *_operand_name; + bool _doing_disp; + bool _doing_constant; + Form::DataType _constant_type; + DefineEmitState::literal_status _constant_status; + DefineEmitState::literal_status _reg_status; + bool _doing_emit8; + bool _doing_emit_d32; + bool _doing_emit_d16; + bool _doing_emit_hi; + bool _doing_emit_lo; + bool _may_reloc; + bool _must_reloc; + reloc_format _reloc_form; + const char * _reloc_type; + bool _processing_noninput; + + NameList _strings_to_emit; + + // Stable state, set by constructor + ArchDesc &_AD; + FILE *_fp; + EncClass &_encoding; + InsEncode &_ins_encode; + InstructForm &_inst; + +public: + DefineEmitState(FILE *fp, ArchDesc &AD, EncClass &encoding, + InsEncode &ins_encode, InstructForm &inst) + : _AD(AD), _fp(fp), _encoding(encoding), _ins_encode(ins_encode), _inst(inst) { + clear(); + } + + void clear() { + _cleared = true; + _opclass = NULL; + _operand = NULL; + _operand_idx = 0; + _local_name = ""; + _operand_name = ""; + _doing_disp = false; + _doing_constant= false; + _constant_type = Form::none; + _constant_status = LITERAL_NOT_SEEN; + _reg_status = LITERAL_NOT_SEEN; + _doing_emit8 = false; + _doing_emit_d32= false; + _doing_emit_d16= false; + _doing_emit_hi = false; + _doing_emit_lo = false; + _may_reloc = false; + _must_reloc = false; + _reloc_form = RELOC_NONE; + _reloc_type = AdlcVMDeps::none_reloc_type(); + _strings_to_emit.clear(); + } + + // Track necessary state when identifying a replacement variable + void update_state(const char *rep_var) { + // A replacement variable or one of its subfields + // Obtain replacement variable from list + if ( (*rep_var) != '$' ) { + // A replacement variable, '$' prefix + // check_rep_var( rep_var ); + if ( Opcode::as_opcode_type(rep_var) != Opcode::NOT_AN_OPCODE ) { + // No state needed. + assert( _opclass == NULL, + "'primary', 'secondary' and 'tertiary' don't follow operand."); + } else { + // Lookup its position in parameter list + int param_no = _encoding.rep_var_index(rep_var); + if ( param_no == -1 ) { + _AD.syntax_err( _encoding._linenum, + "Replacement variable %s not found in enc_class %s.\n", + rep_var, _encoding._name); + } + + // Lookup the corresponding ins_encode parameter + const char *inst_rep_var = _ins_encode.rep_var_name(_inst, param_no); + if (inst_rep_var == NULL) { + _AD.syntax_err( _ins_encode._linenum, + "Parameter %s not passed to enc_class %s from instruct %s.\n", + rep_var, _encoding._name, _inst._ident); + } + + // Check if instruction's actual parameter is a local name in the instruction + const Form *local = _inst._localNames[inst_rep_var]; + OpClassForm *opc = (local != NULL) ? local->is_opclass() : NULL; + // Note: assert removed to allow constant and symbolic parameters + // assert( opc, "replacement variable was not found in local names"); + // Lookup the index position iff the replacement variable is a localName + int idx = (opc != NULL) ? _inst.operand_position_format(inst_rep_var) : -1; + + if ( idx != -1 ) { + // This is a local in the instruction + // Update local state info. + _opclass = opc; + _operand_idx = idx; + _local_name = rep_var; + _operand_name = inst_rep_var; + + // !!!!! + // Do not support consecutive operands. + assert( _operand == NULL, "Unimplemented()"); + _operand = opc->is_operand(); + } + else if( ADLParser::is_literal_constant(inst_rep_var) ) { + // Instruction provided a constant expression + // Check later that encoding specifies $$$constant to resolve as constant + _constant_status = LITERAL_SEEN; + } + else if( Opcode::as_opcode_type(inst_rep_var) != Opcode::NOT_AN_OPCODE ) { + // Instruction provided an opcode: "primary", "secondary", "tertiary" + // Check later that encoding specifies $$$constant to resolve as constant + _constant_status = LITERAL_SEEN; + } + else if((_AD.get_registers() != NULL ) && (_AD.get_registers()->getRegDef(inst_rep_var) != NULL)) { + // Instruction provided a literal register name for this parameter + // Check that encoding specifies $$$reg to resolve.as register. + _reg_status = LITERAL_SEEN; + } + else { + // Check for unimplemented functionality before hard failure + assert( strcmp(opc->_ident,"label")==0, "Unimplemented() Label"); + assert( false, "ShouldNotReachHere()"); + } + } // done checking which operand this is. + } else { + // + // A subfield variable, '$$' prefix + // Check for fields that may require relocation information. + // Then check that literal register parameters are accessed with 'reg' or 'constant' + // + if ( strcmp(rep_var,"$disp") == 0 ) { + _doing_disp = true; + assert( _opclass, "Must use operand or operand class before '$disp'"); + if( _operand == NULL ) { + // Only have an operand class, generate run-time check for relocation + _may_reloc = true; + _reloc_form = RELOC_DISP; + _reloc_type = AdlcVMDeps::oop_reloc_type(); + } else { + // Do precise check on operand: is it a ConP or not + // + // Check interface for value of displacement + assert( ( _operand->_interface != NULL ), + "$disp can only follow memory interface operand"); + MemInterface *mem_interface= _operand->_interface->is_MemInterface(); + assert( mem_interface != NULL, + "$disp can only follow memory interface operand"); + const char *disp = mem_interface->_disp; + + if( disp != NULL && (*disp == '$') ) { + // MemInterface::disp contains a replacement variable, + // Check if this matches a ConP + // + // Lookup replacement variable, in operand's component list + const char *rep_var_name = disp + 1; // Skip '$' + const Component *comp = _operand->_components.search(rep_var_name); + assert( comp != NULL,"Replacement variable not found in components"); + const char *type = comp->_type; + // Lookup operand form for replacement variable's type + const Form *form = _AD.globalNames()[type]; + assert( form != NULL, "Replacement variable's type not found"); + OperandForm *op = form->is_operand(); + assert( op, "Attempting to emit a non-register or non-constant"); + // Check if this is a constant + if (op->_matrule && op->_matrule->is_base_constant(_AD.globalNames())) { + // Check which constant this name maps to: _c0, _c1, ..., _cn + // const int idx = _operand.constant_position(_AD.globalNames(), comp); + // assert( idx != -1, "Constant component not found in operand"); + Form::DataType dtype = op->is_base_constant(_AD.globalNames()); + if ( dtype == Form::idealP ) { + _may_reloc = true; + // No longer true that idealP is always an oop + _reloc_form = RELOC_DISP; + _reloc_type = AdlcVMDeps::oop_reloc_type(); + } + } + + else if( _operand->is_user_name_for_sReg() != Form::none ) { + // The only non-constant allowed access to disp is an operand sRegX in a stackSlotX + assert( op->ideal_to_sReg_type(type) != Form::none, "StackSlots access displacements using 'sRegs'"); + _may_reloc = false; + } else { + assert( false, "fatal(); Only stackSlots can access a non-constant using 'disp'"); + } + } + } // finished with precise check of operand for relocation. + } // finished with subfield variable + else if ( strcmp(rep_var,"$constant") == 0 ) { + _doing_constant = true; + if ( _constant_status == LITERAL_NOT_SEEN ) { + // Check operand for type of constant + assert( _operand, "Must use operand before '$$constant'"); + Form::DataType dtype = _operand->is_base_constant(_AD.globalNames()); + _constant_type = dtype; + if ( dtype == Form::idealP ) { + _may_reloc = true; + // No longer true that idealP is always an oop + // // _must_reloc = true; + _reloc_form = RELOC_IMMEDIATE; + _reloc_type = AdlcVMDeps::oop_reloc_type(); + } else { + // No relocation information needed + } + } else { + // User-provided literals may not require relocation information !!!!! + assert( _constant_status == LITERAL_SEEN, "Must know we are processing a user-provided literal"); + } + } + else if ( strcmp(rep_var,"$label") == 0 ) { + // Calls containing labels require relocation + if ( _inst.is_ideal_call() ) { + _may_reloc = true; + // !!!!! !!!!! + _reloc_type = AdlcVMDeps::none_reloc_type(); + } + } + + // literal register parameter must be accessed as a 'reg' field. + if ( _reg_status != LITERAL_NOT_SEEN ) { + assert( _reg_status == LITERAL_SEEN, "Must have seen register literal before now"); + if (strcmp(rep_var,"$reg") == 0 || reg_conversion(rep_var) != NULL) { + _reg_status = LITERAL_ACCESSED; + } else { + assert( false, "invalid access to literal register parameter"); + } + } + // literal constant parameters must be accessed as a 'constant' field + if ( _constant_status != LITERAL_NOT_SEEN ) { + assert( _constant_status == LITERAL_SEEN, "Must have seen constant literal before now"); + if( strcmp(rep_var,"$constant") == 0 ) { + _constant_status = LITERAL_ACCESSED; + } else { + assert( false, "invalid access to literal constant parameter"); + } + } + } // end replacement and/or subfield + + } + + void add_rep_var(const char *rep_var) { + // Handle subfield and replacement variables. + if ( ( *rep_var == '$' ) && ( *(rep_var+1) == '$' ) ) { + // Check for emit prefix, '$$emit32' + assert( _cleared, "Can not nest $$$emit32"); + if ( strcmp(rep_var,"$$emit32") == 0 ) { + _doing_emit_d32 = true; + } + else if ( strcmp(rep_var,"$$emit16") == 0 ) { + _doing_emit_d16 = true; + } + else if ( strcmp(rep_var,"$$emit_hi") == 0 ) { + _doing_emit_hi = true; + } + else if ( strcmp(rep_var,"$$emit_lo") == 0 ) { + _doing_emit_lo = true; + } + else if ( strcmp(rep_var,"$$emit8") == 0 ) { + _doing_emit8 = true; + } + else { + _AD.syntax_err(_encoding._linenum, "Unsupported $$operation '%s'\n",rep_var); + assert( false, "fatal();"); + } + } + else { + // Update state for replacement variables + update_state( rep_var ); + _strings_to_emit.addName(rep_var); + } + _cleared = false; + } + + void emit_replacement() { + // A replacement variable or one of its subfields + // Obtain replacement variable from list + // const char *ec_rep_var = encoding->_rep_vars.iter(); + const char *rep_var; + _strings_to_emit.reset(); + while ( (rep_var = _strings_to_emit.iter()) != NULL ) { + + if ( (*rep_var) == '$' ) { + // A subfield variable, '$$' prefix + emit_field( rep_var ); + } else { + // A replacement variable, '$' prefix + emit_rep_var( rep_var ); + } // end replacement and/or subfield + } + } + + void emit_reloc_type(const char* type) { + fprintf(_fp, "%s", type) + ; + } + + + void gen_emit_x_reloc(const char *d32_lo_hi ) { + fprintf(_fp,"emit_%s_reloc(cbuf, ", d32_lo_hi ); + emit_replacement(); fprintf(_fp,", "); + emit_reloc_type( _reloc_type ); fprintf(_fp,", "); + fprintf(_fp, "%d", _reloc_form);fprintf(_fp, ");"); + } + + + void emit() { + // + // "emit_d32_reloc(" or "emit_hi_reloc" or "emit_lo_reloc" + // + // Emit the function name when generating an emit function + if ( _doing_emit_d32 || _doing_emit_hi || _doing_emit_lo ) { + const char *d32_hi_lo = _doing_emit_d32 ? "d32" : (_doing_emit_hi ? "hi" : "lo"); + // In general, relocatable isn't known at compiler compile time. + // Check results of prior scan + if ( ! _may_reloc ) { + // Definitely don't need relocation information + fprintf( _fp, "emit_%s(cbuf, ", d32_hi_lo ); + emit_replacement(); fprintf(_fp, ")"); + } + else if ( _must_reloc ) { + // Must emit relocation information + gen_emit_x_reloc( d32_hi_lo ); + } + else { + // Emit RUNTIME CHECK to see if value needs relocation info + // If emitting a relocatable address, use 'emit_d32_reloc' + const char *disp_constant = _doing_disp ? "disp" : _doing_constant ? "constant" : "INVALID"; + assert( (_doing_disp || _doing_constant) + && !(_doing_disp && _doing_constant), + "Must be emitting either a displacement or a constant"); + fprintf(_fp,"\n"); + fprintf(_fp,"if ( opnd_array(%d)->%s_is_oop() ) {\n", + _operand_idx, disp_constant); + fprintf(_fp," "); + gen_emit_x_reloc( d32_hi_lo ); fprintf(_fp,"\n"); + fprintf(_fp,"} else {\n"); + fprintf(_fp," emit_%s(cbuf, ", d32_hi_lo); + emit_replacement(); fprintf(_fp, ");\n"); fprintf(_fp,"}"); + } + } + else if ( _doing_emit_d16 ) { + // Relocation of 16-bit values is not supported + fprintf(_fp,"emit_d16(cbuf, "); + emit_replacement(); fprintf(_fp, ")"); + // No relocation done for 16-bit values + } + else if ( _doing_emit8 ) { + // Relocation of 8-bit values is not supported + fprintf(_fp,"emit_d8(cbuf, "); + emit_replacement(); fprintf(_fp, ")"); + // No relocation done for 8-bit values + } + else { + // Not an emit# command, just output the replacement string. + emit_replacement(); + } + + // Get ready for next state collection. + clear(); + } + +private: + + // recognizes names which represent MacroAssembler register types + // and return the conversion function to build them from OptoReg + const char* reg_conversion(const char* rep_var) { + if (strcmp(rep_var,"$Register") == 0) return "as_Register"; + if (strcmp(rep_var,"$FloatRegister") == 0) return "as_FloatRegister"; +#if defined(IA32) || defined(AMD64) + if (strcmp(rep_var,"$XMMRegister") == 0) return "as_XMMRegister"; +#endif + return NULL; + } + + void emit_field(const char *rep_var) { + const char* reg_convert = reg_conversion(rep_var); + + // A subfield variable, '$$subfield' + if ( strcmp(rep_var, "$reg") == 0 || reg_convert != NULL) { + // $reg form or the $Register MacroAssembler type conversions + assert( _operand_idx != -1, + "Must use this subfield after operand"); + if( _reg_status == LITERAL_NOT_SEEN ) { + if (_processing_noninput) { + const Form *local = _inst._localNames[_operand_name]; + OperandForm *oper = local->is_operand(); + const RegDef* first = oper->get_RegClass()->find_first_elem(); + if (reg_convert != NULL) { + fprintf(_fp, "%s(%s_enc)", reg_convert, first->_regname); + } else { + fprintf(_fp, "%s_enc", first->_regname); + } + } else { + fprintf(_fp,"->%s(ra_,this", reg_convert != NULL ? reg_convert : "reg"); + // Add parameter for index position, if not result operand + if( _operand_idx != 0 ) fprintf(_fp,",idx%d", _operand_idx); + fprintf(_fp,")"); + } + } else { + assert( _reg_status == LITERAL_OUTPUT, "should have output register literal in emit_rep_var"); + // Register literal has already been sent to output file, nothing more needed + } + } + else if ( strcmp(rep_var,"$base") == 0 ) { + assert( _operand_idx != -1, + "Must use this subfield after operand"); + assert( ! _may_reloc, "UnImplemented()"); + fprintf(_fp,"->base(ra_,this,idx%d)", _operand_idx); + } + else if ( strcmp(rep_var,"$index") == 0 ) { + assert( _operand_idx != -1, + "Must use this subfield after operand"); + assert( ! _may_reloc, "UnImplemented()"); + fprintf(_fp,"->index(ra_,this,idx%d)", _operand_idx); + } + else if ( strcmp(rep_var,"$scale") == 0 ) { + assert( ! _may_reloc, "UnImplemented()"); + fprintf(_fp,"->scale()"); + } + else if ( strcmp(rep_var,"$cmpcode") == 0 ) { + assert( ! _may_reloc, "UnImplemented()"); + fprintf(_fp,"->ccode()"); + } + else if ( strcmp(rep_var,"$constant") == 0 ) { + if( _constant_status == LITERAL_NOT_SEEN ) { + if ( _constant_type == Form::idealD ) { + fprintf(_fp,"->constantD()"); + } else if ( _constant_type == Form::idealF ) { + fprintf(_fp,"->constantF()"); + } else if ( _constant_type == Form::idealL ) { + fprintf(_fp,"->constantL()"); + } else { + fprintf(_fp,"->constant()"); + } + } else { + assert( _constant_status == LITERAL_OUTPUT, "should have output constant literal in emit_rep_var"); + // Cosntant literal has already been sent to output file, nothing more needed + } + } + else if ( strcmp(rep_var,"$disp") == 0 ) { + Form::DataType stack_type = _operand ? _operand->is_user_name_for_sReg() : Form::none; + if( _operand && _operand_idx==0 && stack_type != Form::none ) { + fprintf(_fp,"->disp(ra_,this,0)"); + } else { + fprintf(_fp,"->disp(ra_,this,idx%d)", _operand_idx); + } + } + else if ( strcmp(rep_var,"$label") == 0 ) { + fprintf(_fp,"->label()"); + } + else if ( strcmp(rep_var,"$method") == 0 ) { + fprintf(_fp,"->method()"); + } + else { + printf("emit_field: %s\n",rep_var); + assert( false, "UnImplemented()"); + } + } + + + void emit_rep_var(const char *rep_var) { + _processing_noninput = false; + // A replacement variable, originally '$' + if ( Opcode::as_opcode_type(rep_var) != Opcode::NOT_AN_OPCODE ) { + _inst._opcode->print_opcode(_fp, Opcode::as_opcode_type(rep_var) ); + } + else { + // Lookup its position in parameter list + int param_no = _encoding.rep_var_index(rep_var); + if ( param_no == -1 ) { + _AD.syntax_err( _encoding._linenum, + "Replacement variable %s not found in enc_class %s.\n", + rep_var, _encoding._name); + } + // Lookup the corresponding ins_encode parameter + const char *inst_rep_var = _ins_encode.rep_var_name(_inst, param_no); + + // Check if instruction's actual parameter is a local name in the instruction + const Form *local = _inst._localNames[inst_rep_var]; + OpClassForm *opc = (local != NULL) ? local->is_opclass() : NULL; + // Note: assert removed to allow constant and symbolic parameters + // assert( opc, "replacement variable was not found in local names"); + // Lookup the index position iff the replacement variable is a localName + int idx = (opc != NULL) ? _inst.operand_position_format(inst_rep_var) : -1; + if( idx != -1 ) { + if (_inst.is_noninput_operand(idx)) { + // This operand isn't a normal input so printing it is done + // specially. + _processing_noninput = true; + } else { + // Output the emit code for this operand + fprintf(_fp,"opnd_array(%d)",idx); + } + assert( _operand == opc->is_operand(), + "Previous emit $operand does not match current"); + } + else if( ADLParser::is_literal_constant(inst_rep_var) ) { + // else check if it is a constant expression + // Removed following assert to allow primitive C types as arguments to encodings + // assert( _constant_status == LITERAL_ACCESSED, "Must be processing a literal constant parameter"); + fprintf(_fp,"(%s)", inst_rep_var); + _constant_status = LITERAL_OUTPUT; + } + else if( Opcode::as_opcode_type(inst_rep_var) != Opcode::NOT_AN_OPCODE ) { + // else check if "primary", "secondary", "tertiary" + assert( _constant_status == LITERAL_ACCESSED, "Must be processing a literal constant parameter"); + _inst._opcode->print_opcode(_fp, Opcode::as_opcode_type(inst_rep_var) ); + _constant_status = LITERAL_OUTPUT; + } + else if((_AD.get_registers() != NULL ) && (_AD.get_registers()->getRegDef(inst_rep_var) != NULL)) { + // Instruction provided a literal register name for this parameter + // Check that encoding specifies $$$reg to resolve.as register. + assert( _reg_status == LITERAL_ACCESSED, "Must be processing a literal register parameter"); + fprintf(_fp,"(%s_enc)", inst_rep_var); + _reg_status = LITERAL_OUTPUT; + } + else { + // Check for unimplemented functionality before hard failure + assert( strcmp(opc->_ident,"label")==0, "Unimplemented() Label"); + assert( false, "ShouldNotReachHere()"); + } + // all done + } + } + +}; // end class DefineEmitState + + +void ArchDesc::defineSize(FILE *fp, InstructForm &inst) { + + //(1) + // Output instruction's emit prototype + fprintf(fp,"uint %sNode::size(PhaseRegAlloc *ra_) const {\n", + inst._ident); + + //(2) + // Print the size + fprintf(fp, " return (VerifyOops ? MachNode::size(ra_) : %s);\n", inst._size); + + // (3) and (4) + fprintf(fp,"}\n"); +} + +void ArchDesc::defineEmit(FILE *fp, InstructForm &inst) { + InsEncode *ins_encode = inst._insencode; + + // (1) + // Output instruction's emit prototype + fprintf(fp,"void %sNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {\n", + inst._ident); + + // If user did not define an encode section, + // provide stub that does not generate any machine code. + if( (_encode == NULL) || (ins_encode == NULL) ) { + fprintf(fp, " // User did not define an encode section.\n"); + fprintf(fp,"}\n"); + return; + } + + // Save current instruction's starting address (helps with relocation). + fprintf( fp, " cbuf.set_inst_mark();\n"); + + // // // idx0 is only needed for syntactic purposes and only by "storeSSI" + // fprintf( fp, " unsigned idx0 = 0;\n"); + + // Output each operand's offset into the array of registers. + inst.index_temps( fp, _globalNames ); + + // Output this instruction's encodings + const char *ec_name; + bool user_defined = false; + ins_encode->reset(); + while ( (ec_name = ins_encode->encode_class_iter()) != NULL ) { + fprintf(fp, " {"); + // Output user-defined encoding + user_defined = true; + + const char *ec_code = NULL; + const char *ec_rep_var = NULL; + EncClass *encoding = _encode->encClass(ec_name); + if (encoding == NULL) { + fprintf(stderr, "User did not define contents of this encode_class: %s\n", ec_name); + abort(); + } + + if (ins_encode->current_encoding_num_args() != encoding->num_args()) { + globalAD->syntax_err(ins_encode->_linenum, "In %s: passing %d arguments to %s but expecting %d", + inst._ident, ins_encode->current_encoding_num_args(), + ec_name, encoding->num_args()); + } + + DefineEmitState pending(fp, *this, *encoding, *ins_encode, inst ); + encoding->_code.reset(); + encoding->_rep_vars.reset(); + // Process list of user-defined strings, + // and occurrences of replacement variables. + // Replacement Vars are pushed into a list and then output + while ( (ec_code = encoding->_code.iter()) != NULL ) { + if ( ! encoding->_code.is_signal( ec_code ) ) { + // Emit pending code + pending.emit(); + pending.clear(); + // Emit this code section + fprintf(fp,"%s", ec_code); + } else { + // A replacement variable or one of its subfields + // Obtain replacement variable from list + ec_rep_var = encoding->_rep_vars.iter(); + pending.add_rep_var(ec_rep_var); + } + } + // Emit pending code + pending.emit(); + pending.clear(); + fprintf(fp, "}\n"); + } // end while instruction's encodings + + // Check if user stated which encoding to user + if ( user_defined == false ) { + fprintf(fp, " // User did not define which encode class to use.\n"); + } + + // (3) and (4) + fprintf(fp,"}\n"); +} + +// --------------------------------------------------------------------------- +//--------Utilities to build MachOper and MachNode derived Classes------------ +// --------------------------------------------------------------------------- + +//------------------------------Utilities to build Operand Classes------------ +static void defineIn_RegMask(FILE *fp, FormDict &globals, OperandForm &oper) { + uint num_edges = oper.num_edges(globals); + if( num_edges != 0 ) { + // Method header + fprintf(fp, "const RegMask *%sOper::in_RegMask(int index) const {\n", + oper._ident); + + // Assert that the index is in range. + fprintf(fp, " assert(0 <= index && index < %d, \"index out of range\");\n", + num_edges); + + // Figure out if all RegMasks are the same. + const char* first_reg_class = oper.in_reg_class(0, globals); + bool all_same = true; + assert(first_reg_class != NULL, "did not find register mask"); + + for (uint index = 1; all_same && index < num_edges; index++) { + const char* some_reg_class = oper.in_reg_class(index, globals); + assert(some_reg_class != NULL, "did not find register mask"); + if (strcmp(first_reg_class, some_reg_class) != 0) { + all_same = false; + } + } + + if (all_same) { + // Return the sole RegMask. + if (strcmp(first_reg_class, "stack_slots") == 0) { + fprintf(fp," return &(Compile::current()->FIRST_STACK_mask());\n"); + } else { + fprintf(fp," return &%s_mask;\n", toUpper(first_reg_class)); + } + } else { + // Build a switch statement to return the desired mask. + fprintf(fp," switch (index) {\n"); + + for (uint index = 0; index < num_edges; index++) { + const char *reg_class = oper.in_reg_class(index, globals); + assert(reg_class != NULL, "did not find register mask"); + if( !strcmp(reg_class, "stack_slots") ) { + fprintf(fp, " case %d: return &(Compile::current()->FIRST_STACK_mask());\n", index); + } else { + fprintf(fp, " case %d: return &%s_mask;\n", index, toUpper(reg_class)); + } + } + fprintf(fp," }\n"); + fprintf(fp," ShouldNotReachHere();\n"); + fprintf(fp," return NULL;\n"); + } + + // Method close + fprintf(fp, "}\n\n"); + } +} + +// generate code to create a clone for a class derived from MachOper +// +// (0) MachOper *MachOperXOper::clone(Compile* C) const { +// (1) return new (C) MachXOper( _ccode, _c0, _c1, ..., _cn); +// (2) } +// +static void defineClone(FILE *fp, FormDict &globalNames, OperandForm &oper) { + fprintf(fp,"MachOper *%sOper::clone(Compile* C) const {\n", oper._ident); + // Check for constants that need to be copied over + const int num_consts = oper.num_consts(globalNames); + const bool is_ideal_bool = oper.is_ideal_bool(); + if( (num_consts > 0) ) { + fprintf(fp," return new (C) %sOper(", oper._ident); + // generate parameters for constants + int i = 0; + fprintf(fp,"_c%d", i); + for( i = 1; i < num_consts; ++i) { + fprintf(fp,", _c%d", i); + } + // finish line (1) + fprintf(fp,");\n"); + } + else { + assert( num_consts == 0, "Currently support zero or one constant per operand clone function"); + fprintf(fp," return new (C) %sOper();\n", oper._ident); + } + // finish method + fprintf(fp,"}\n"); +} + +static void define_hash(FILE *fp, char *operand) { + fprintf(fp,"uint %sOper::hash() const { return 5; }\n", operand); +} + +static void define_cmp(FILE *fp, char *operand) { + fprintf(fp,"uint %sOper::cmp( const MachOper &oper ) const { return opcode() == oper.opcode(); }\n", operand); +} + + +// Helper functions for bug 4796752, abstracted with minimal modification +// from define_oper_interface() +OperandForm *rep_var_to_operand(const char *encoding, OperandForm &oper, FormDict &globals) { + OperandForm *op = NULL; + // Check for replacement variable + if( *encoding == '$' ) { + // Replacement variable + const char *rep_var = encoding + 1; + // Lookup replacement variable, rep_var, in operand's component list + const Component *comp = oper._components.search(rep_var); + assert( comp != NULL, "Replacement variable not found in components"); + // Lookup operand form for replacement variable's type + const char *type = comp->_type; + Form *form = (Form*)globals[type]; + assert( form != NULL, "Replacement variable's type not found"); + op = form->is_operand(); + assert( op, "Attempting to emit a non-register or non-constant"); + } + + return op; +} + +int rep_var_to_constant_index(const char *encoding, OperandForm &oper, FormDict &globals) { + int idx = -1; + // Check for replacement variable + if( *encoding == '$' ) { + // Replacement variable + const char *rep_var = encoding + 1; + // Lookup replacement variable, rep_var, in operand's component list + const Component *comp = oper._components.search(rep_var); + assert( comp != NULL, "Replacement variable not found in components"); + // Lookup operand form for replacement variable's type + const char *type = comp->_type; + Form *form = (Form*)globals[type]; + assert( form != NULL, "Replacement variable's type not found"); + OperandForm *op = form->is_operand(); + assert( op, "Attempting to emit a non-register or non-constant"); + // Check that this is a constant and find constant's index: + if (op->_matrule && op->_matrule->is_base_constant(globals)) { + idx = oper.constant_position(globals, comp); + } + } + + return idx; +} + +bool is_regI(const char *encoding, OperandForm &oper, FormDict &globals ) { + bool is_regI = false; + + OperandForm *op = rep_var_to_operand(encoding, oper, globals); + if( op != NULL ) { + // Check that this is a register + if ( (op->_matrule && op->_matrule->is_base_register(globals)) ) { + // Register + const char* ideal = op->ideal_type(globals); + is_regI = (ideal && (op->ideal_to_Reg_type(ideal) == Form::idealI)); + } + } + + return is_regI; +} + +bool is_conP(const char *encoding, OperandForm &oper, FormDict &globals ) { + bool is_conP = false; + + OperandForm *op = rep_var_to_operand(encoding, oper, globals); + if( op != NULL ) { + // Check that this is a constant pointer + if (op->_matrule && op->_matrule->is_base_constant(globals)) { + // Constant + Form::DataType dtype = op->is_base_constant(globals); + is_conP = (dtype == Form::idealP); + } + } + + return is_conP; +} + + +// Define a MachOper interface methods +void ArchDesc::define_oper_interface(FILE *fp, OperandForm &oper, FormDict &globals, + const char *name, const char *encoding) { + bool emit_position = false; + int position = -1; + + fprintf(fp," virtual int %s", name); + // Generate access method for base, index, scale, disp, ... + if( (strcmp(name,"base") == 0) || (strcmp(name,"index") == 0) ) { + fprintf(fp,"(PhaseRegAlloc *ra_, const Node *node, int idx) const { \n"); + emit_position = true; + } else if ( (strcmp(name,"disp") == 0) ) { + fprintf(fp,"(PhaseRegAlloc *ra_, const Node *node, int idx) const { \n"); + } else { + fprintf(fp,"() const { "); + } + + // Check for hexadecimal value OR replacement variable + if( *encoding == '$' ) { + // Replacement variable + const char *rep_var = encoding + 1; + fprintf(fp,"// Replacement variable: %s\n", encoding+1); + // Lookup replacement variable, rep_var, in operand's component list + const Component *comp = oper._components.search(rep_var); + assert( comp != NULL, "Replacement variable not found in components"); + // Lookup operand form for replacement variable's type + const char *type = comp->_type; + Form *form = (Form*)globals[type]; + assert( form != NULL, "Replacement variable's type not found"); + OperandForm *op = form->is_operand(); + assert( op, "Attempting to emit a non-register or non-constant"); + // Check that this is a register or a constant and generate code: + if ( (op->_matrule && op->_matrule->is_base_register(globals)) ) { + // Register + int idx_offset = oper.register_position( globals, rep_var); + position = idx_offset; + fprintf(fp," return (int)ra_->get_encode(node->in(idx"); + if ( idx_offset > 0 ) fprintf(fp, "+%d",idx_offset); + fprintf(fp,"));\n"); + } else if ( op->ideal_to_sReg_type(op->_ident) != Form::none ) { + // StackSlot for an sReg comes either from input node or from self, when idx==0 + fprintf(fp," if( idx != 0 ) {\n"); + fprintf(fp," // Access register number for input operand\n"); + fprintf(fp," return ra_->reg2offset(ra_->get_reg_first(node->in(idx)));/* sReg */\n"); + fprintf(fp," }\n"); + fprintf(fp," // Access register number from myself\n"); + fprintf(fp," return ra_->reg2offset(ra_->get_reg_first(node));/* sReg */\n"); + } else if (op->_matrule && op->_matrule->is_base_constant(globals)) { + // Constant + // Check which constant this name maps to: _c0, _c1, ..., _cn + const int idx = oper.constant_position(globals, comp); + assert( idx != -1, "Constant component not found in operand"); + // Output code for this constant, type dependent. + fprintf(fp," return (int)" ); + oper.access_constant(fp, globals, (uint)idx /* , const_type */); + fprintf(fp,";\n"); + } else { + assert( false, "Attempting to emit a non-register or non-constant"); + } + } + else if( *encoding == '0' && *(encoding+1) == 'x' ) { + // Hex value + fprintf(fp,"return %s;", encoding); + } else { + assert( false, "Do not support octal or decimal encode constants"); + } + fprintf(fp," }\n"); + + if( emit_position && (position != -1) && (oper.num_edges(globals) > 0) ) { + fprintf(fp," virtual int %s_position() const { return %d; }\n", name, position); + MemInterface *mem_interface = oper._interface->is_MemInterface(); + const char *base = mem_interface->_base; + const char *disp = mem_interface->_disp; + if( emit_position && (strcmp(name,"base") == 0) + && base != NULL && is_regI(base, oper, globals) + && disp != NULL && is_conP(disp, oper, globals) ) { + // Found a memory access using a constant pointer for a displacement + // and a base register containing an integer offset. + // In this case the base and disp are reversed with respect to what + // is expected by MachNode::get_base_and_disp() and MachNode::adr_type(). + // Provide a non-NULL return for disp_as_type() that will allow adr_type() + // to correctly compute the access type for alias analysis. + // + // See BugId 4796752, operand indOffset32X in i486.ad + int idx = rep_var_to_constant_index(disp, oper, globals); + fprintf(fp," virtual const TypePtr *disp_as_type() const { return _c%d; }\n", idx); + } + } +} + +// +// Construct the method to copy _idx, inputs and operands to new node. +static void define_fill_new_machnode(bool used, FILE *fp_cpp) { + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "// Copy _idx, inputs and operands to new node\n"); + fprintf(fp_cpp, "void MachNode::fill_new_machnode( MachNode* node, Compile* C) const {\n"); + if( !used ) { + fprintf(fp_cpp, " // This architecture does not have cisc or short branch instructions\n"); + fprintf(fp_cpp, " ShouldNotCallThis();\n"); + fprintf(fp_cpp, "}\n"); + } else { + // New node must use same node index for access through allocator's tables + fprintf(fp_cpp, " // New node must use same node index\n"); + fprintf(fp_cpp, " node->set_idx( _idx );\n"); + // Copy machine-independent inputs + fprintf(fp_cpp, " // Copy machine-independent inputs\n"); + fprintf(fp_cpp, " for( uint j = 0; j < req(); j++ ) {\n"); + fprintf(fp_cpp, " node->add_req(in(j));\n"); + fprintf(fp_cpp, " }\n"); + // Copy machine operands to new MachNode + fprintf(fp_cpp, " // Copy my operands, except for cisc position\n"); + fprintf(fp_cpp, " int nopnds = num_opnds();\n"); + fprintf(fp_cpp, " assert( node->num_opnds() == (uint)nopnds, \"Must have same number of operands\");\n"); + fprintf(fp_cpp, " MachOper **to = node->_opnds;\n"); + fprintf(fp_cpp, " for( int i = 0; i < nopnds; i++ ) {\n"); + fprintf(fp_cpp, " if( i != cisc_operand() ) \n"); + fprintf(fp_cpp, " to[i] = _opnds[i]->clone(C);\n"); + fprintf(fp_cpp, " }\n"); + fprintf(fp_cpp, "}\n"); + } + fprintf(fp_cpp, "\n"); +} + +//------------------------------defineClasses---------------------------------- +// Define members of MachNode and MachOper classes based on +// operand and instruction lists +void ArchDesc::defineClasses(FILE *fp) { + + // Define the contents of an array containing the machine register names + defineRegNames(fp, _register); + // Define an array containing the machine register encoding values + defineRegEncodes(fp, _register); + // Generate an enumeration of user-defined register classes + // and a list of register masks, one for each class. + // Only define the RegMask value objects in the expand file. + // Declare each as an extern const RegMask ...; in ad_.hpp + declare_register_masks(_HPP_file._fp); + // build_register_masks(fp); + build_register_masks(_CPP_EXPAND_file._fp); + // Define the pipe_classes + build_pipe_classes(_CPP_PIPELINE_file._fp); + + // Generate Machine Classes for each operand defined in AD file + fprintf(fp,"\n"); + fprintf(fp,"\n"); + fprintf(fp,"//------------------Define classes derived from MachOper---------------------\n"); + // Iterate through all operands + _operands.reset(); + OperandForm *oper; + for( ; (oper = (OperandForm*)_operands.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( oper->ideal_only() ) continue; + // !!!!! + // The declaration of labelOper is in machine-independent file: machnode + if ( strcmp(oper->_ident,"label") == 0 ) { + defineIn_RegMask(_CPP_MISC_file._fp, _globalNames, *oper); + + fprintf(fp,"MachOper *%sOper::clone(Compile* C) const {\n", oper->_ident); + fprintf(fp," return new (C) %sOper(_label, _block_num);\n", oper->_ident); + fprintf(fp,"}\n"); + + fprintf(fp,"uint %sOper::opcode() const { return %s; }\n", + oper->_ident, machOperEnum(oper->_ident)); + // // Currently all XXXOper::Hash() methods are identical (990820) + // define_hash(fp, oper->_ident); + // // Currently all XXXOper::Cmp() methods are identical (990820) + // define_cmp(fp, oper->_ident); + fprintf(fp,"\n"); + + continue; + } + + // The declaration of methodOper is in machine-independent file: machnode + if ( strcmp(oper->_ident,"method") == 0 ) { + defineIn_RegMask(_CPP_MISC_file._fp, _globalNames, *oper); + + fprintf(fp,"MachOper *%sOper::clone(Compile* C) const {\n", oper->_ident); + fprintf(fp," return new (C) %sOper(_method);\n", oper->_ident); + fprintf(fp,"}\n"); + + fprintf(fp,"uint %sOper::opcode() const { return %s; }\n", + oper->_ident, machOperEnum(oper->_ident)); + // // Currently all XXXOper::Hash() methods are identical (990820) + // define_hash(fp, oper->_ident); + // // Currently all XXXOper::Cmp() methods are identical (990820) + // define_cmp(fp, oper->_ident); + fprintf(fp,"\n"); + + continue; + } + + defineIn_RegMask(fp, _globalNames, *oper); + defineClone(_CPP_CLONE_file._fp, _globalNames, *oper); + // // Currently all XXXOper::Hash() methods are identical (990820) + // define_hash(fp, oper->_ident); + // // Currently all XXXOper::Cmp() methods are identical (990820) + // define_cmp(fp, oper->_ident); + + // side-call to generate output that used to be in the header file: + extern void gen_oper_format(FILE *fp, FormDict &globals, OperandForm &oper, bool for_c_file); + gen_oper_format(_CPP_FORMAT_file._fp, _globalNames, *oper, true); + + } + + + // Generate Machine Classes for each instruction defined in AD file + fprintf(fp,"//------------------Define members for classes derived from MachNode----------\n"); + // Output the definitions for out_RegMask() // & kill_RegMask() + _instructions.reset(); + InstructForm *instr; + MachNodeForm *machnode; + for( ; (instr = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( instr->ideal_only() ) continue; + + defineOut_RegMask(_CPP_MISC_file._fp, instr->_ident, reg_mask(*instr)); + } + + bool used = false; + // Output the definitions for expand rules & peephole rules + _instructions.reset(); + for( ; (instr = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( instr->ideal_only() ) continue; + // If there are multiple defs/kills, or an explicit expand rule, build rule + if( instr->expands() || instr->needs_projections() || + instr->has_temps() || + instr->_matrule != NULL && + instr->num_opnds() != instr->num_unique_opnds() ) + defineExpand(_CPP_EXPAND_file._fp, instr); + // If there is an explicit peephole rule, build it + if ( instr->peepholes() ) + definePeephole(_CPP_PEEPHOLE_file._fp, instr); + + // Output code to convert to the cisc version, if applicable + used |= instr->define_cisc_version(*this, fp); + + // Output code to convert to the short branch version, if applicable + used |= instr->define_short_branch_methods(fp); + } + + // Construct the method called by cisc_version() to copy inputs and operands. + define_fill_new_machnode(used, fp); + + // Output the definitions for labels + _instructions.reset(); + while( (instr = (InstructForm*)_instructions.iter()) != NULL ) { + // Ensure this is a machine-world instruction + if ( instr->ideal_only() ) continue; + + // Access the fields for operand Label + int label_position = instr->label_position(); + if( label_position != -1 ) { + // Set the label + fprintf(fp,"void %sNode::label_set( Label& label, uint block_num ) {\n", instr->_ident); + fprintf(fp," labelOper* oper = (labelOper*)(opnd_array(%d));\n", + label_position ); + fprintf(fp," oper->_label = &label;\n"); + fprintf(fp," oper->_block_num = block_num;\n"); + fprintf(fp,"}\n"); + } + } + + // Output the definitions for methods + _instructions.reset(); + while( (instr = (InstructForm*)_instructions.iter()) != NULL ) { + // Ensure this is a machine-world instruction + if ( instr->ideal_only() ) continue; + + // Access the fields for operand Label + int method_position = instr->method_position(); + if( method_position != -1 ) { + // Access the method's address + fprintf(fp,"void %sNode::method_set( intptr_t method ) {\n", instr->_ident); + fprintf(fp," ((methodOper*)opnd_array(%d))->_method = method;\n", + method_position ); + fprintf(fp,"}\n"); + fprintf(fp,"\n"); + } + } + + // Define this instruction's number of relocation entries, base is '0' + _instructions.reset(); + while( (instr = (InstructForm*)_instructions.iter()) != NULL ) { + // Output the definition for number of relocation entries + uint reloc_size = instr->reloc(_globalNames); + if ( reloc_size != 0 ) { + fprintf(fp,"int %sNode::reloc() const {\n", instr->_ident); + fprintf(fp, " return %d;\n", reloc_size ); + fprintf(fp,"}\n"); + fprintf(fp,"\n"); + } + } + fprintf(fp,"\n"); + + // Output the definitions for code generation + // + // address ___Node::emit(address ptr, PhaseRegAlloc *ra_) const { + // // ... encoding defined by user + // return ptr; + // } + // + _instructions.reset(); + for( ; (instr = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( instr->ideal_only() ) continue; + + if (instr->_insencode) defineEmit(fp, *instr); + if (instr->_size) defineSize(fp, *instr); + + // side-call to generate output that used to be in the header file: + extern void gen_inst_format(FILE *fp, FormDict &globals, InstructForm &oper, bool for_c_file); + gen_inst_format(_CPP_FORMAT_file._fp, _globalNames, *instr, true); + } + + // Output the definitions for alias analysis + _instructions.reset(); + for( ; (instr = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( instr->ideal_only() ) continue; + + // Analyze machine instructions that either USE or DEF memory. + int memory_operand = instr->memory_operand(_globalNames); + // Some guys kill all of memory + if ( instr->is_wide_memory_kill(_globalNames) ) { + memory_operand = InstructForm::MANY_MEMORY_OPERANDS; + } + + if ( memory_operand != InstructForm::NO_MEMORY_OPERAND ) { + if( memory_operand == InstructForm::MANY_MEMORY_OPERANDS ) { + fprintf(fp,"const TypePtr *%sNode::adr_type() const { return TypePtr::BOTTOM; }\n", instr->_ident); + fprintf(fp,"const MachOper* %sNode::memory_operand() const { return (MachOper*)-1; }\n", instr->_ident); + } else { + fprintf(fp,"const MachOper* %sNode::memory_operand() const { return _opnds[%d]; }\n", instr->_ident, memory_operand); + } + } + } + + // Get the length of the longest identifier + int max_ident_len = 0; + _instructions.reset(); + + for ( ; (instr = (InstructForm*)_instructions.iter()) != NULL; ) { + if (instr->_ins_pipe && _pipeline->_classlist.search(instr->_ins_pipe)) { + int ident_len = (int)strlen(instr->_ident); + if( max_ident_len < ident_len ) + max_ident_len = ident_len; + } + } + + // Emit specifically for Node(s) + fprintf(_CPP_PIPELINE_file._fp, "const Pipeline * %*s::pipeline_class() { return %s; }\n", + max_ident_len, "Node", _pipeline ? "(&pipeline_class_Zero_Instructions)" : "NULL"); + fprintf(_CPP_PIPELINE_file._fp, "const Pipeline * %*s::pipeline() const { return %s; }\n", + max_ident_len, "Node", _pipeline ? "(&pipeline_class_Zero_Instructions)" : "NULL"); + fprintf(_CPP_PIPELINE_file._fp, "\n"); + + fprintf(_CPP_PIPELINE_file._fp, "const Pipeline * %*s::pipeline_class() { return %s; }\n", + max_ident_len, "MachNode", _pipeline ? "(&pipeline_class_Unknown_Instructions)" : "NULL"); + fprintf(_CPP_PIPELINE_file._fp, "const Pipeline * %*s::pipeline() const { return pipeline_class(); }\n", + max_ident_len, "MachNode"); + fprintf(_CPP_PIPELINE_file._fp, "\n"); + + // Output the definitions for machine node specific pipeline data + _machnodes.reset(); + + for ( ; (machnode = (MachNodeForm*)_machnodes.iter()) != NULL; ) { + fprintf(_CPP_PIPELINE_file._fp, "const Pipeline * %sNode::pipeline() const { return (&pipeline_class_%03d); }\n", + machnode->_ident, ((class PipeClassForm *)_pipeline->_classdict[machnode->_machnode_pipe])->_num); + } + + fprintf(_CPP_PIPELINE_file._fp, "\n"); + + // Output the definitions for instruction pipeline static data references + _instructions.reset(); + + for ( ; (instr = (InstructForm*)_instructions.iter()) != NULL; ) { + if (instr->_ins_pipe && _pipeline->_classlist.search(instr->_ins_pipe)) { + fprintf(_CPP_PIPELINE_file._fp, "\n"); + fprintf(_CPP_PIPELINE_file._fp, "const Pipeline * %*sNode::pipeline_class() { return (&pipeline_class_%03d); }\n", + max_ident_len, instr->_ident, ((class PipeClassForm *)_pipeline->_classdict[instr->_ins_pipe])->_num); + fprintf(_CPP_PIPELINE_file._fp, "const Pipeline * %*sNode::pipeline() const { return (&pipeline_class_%03d); }\n", + max_ident_len, instr->_ident, ((class PipeClassForm *)_pipeline->_classdict[instr->_ins_pipe])->_num); + } + } +} + + +// -------------------------------- maps ------------------------------------ + +// Information needed to generate the ReduceOp mapping for the DFA +class OutputReduceOp : public OutputMap { +public: + OutputReduceOp(FILE *hpp, FILE *cpp, FormDict &globals, ArchDesc &AD) + : OutputMap(hpp, cpp, globals, AD) {}; + + void declaration() { fprintf(_hpp, "extern const int reduceOp[];\n"); } + void definition() { fprintf(_cpp, "const int reduceOp[] = {\n"); } + void closing() { fprintf(_cpp, " 0 // no trailing comma\n"); + OutputMap::closing(); + } + void map(OpClassForm &opc) { + const char *reduce = opc._ident; + if( reduce ) fprintf(_cpp, " %s_rule", reduce); + else fprintf(_cpp, " 0"); + } + void map(OperandForm &oper) { + // Most operands without match rules, e.g. eFlagsReg, do not have a result operand + const char *reduce = (oper._matrule ? oper.reduce_result() : NULL); + // operand stackSlot does not have a match rule, but produces a stackSlot + if( oper.is_user_name_for_sReg() != Form::none ) reduce = oper.reduce_result(); + if( reduce ) fprintf(_cpp, " %s_rule", reduce); + else fprintf(_cpp, " 0"); + } + void map(InstructForm &inst) { + const char *reduce = (inst._matrule ? inst.reduce_result() : NULL); + if( reduce ) fprintf(_cpp, " %s_rule", reduce); + else fprintf(_cpp, " 0"); + } + void map(char *reduce) { + if( reduce ) fprintf(_cpp, " %s_rule", reduce); + else fprintf(_cpp, " 0"); + } +}; + +// Information needed to generate the LeftOp mapping for the DFA +class OutputLeftOp : public OutputMap { +public: + OutputLeftOp(FILE *hpp, FILE *cpp, FormDict &globals, ArchDesc &AD) + : OutputMap(hpp, cpp, globals, AD) {}; + + void declaration() { fprintf(_hpp, "extern const int leftOp[];\n"); } + void definition() { fprintf(_cpp, "const int leftOp[] = {\n"); } + void closing() { fprintf(_cpp, " 0 // no trailing comma\n"); + OutputMap::closing(); + } + void map(OpClassForm &opc) { fprintf(_cpp, " 0"); } + void map(OperandForm &oper) { + const char *reduce = oper.reduce_left(_globals); + if( reduce ) fprintf(_cpp, " %s_rule", reduce); + else fprintf(_cpp, " 0"); + } + void map(char *name) { + const char *reduce = _AD.reduceLeft(name); + if( reduce ) fprintf(_cpp, " %s_rule", reduce); + else fprintf(_cpp, " 0"); + } + void map(InstructForm &inst) { + const char *reduce = inst.reduce_left(_globals); + if( reduce ) fprintf(_cpp, " %s_rule", reduce); + else fprintf(_cpp, " 0"); + } +}; + + +// Information needed to generate the RightOp mapping for the DFA +class OutputRightOp : public OutputMap { +public: + OutputRightOp(FILE *hpp, FILE *cpp, FormDict &globals, ArchDesc &AD) + : OutputMap(hpp, cpp, globals, AD) {}; + + void declaration() { fprintf(_hpp, "extern const int rightOp[];\n"); } + void definition() { fprintf(_cpp, "const int rightOp[] = {\n"); } + void closing() { fprintf(_cpp, " 0 // no trailing comma\n"); + OutputMap::closing(); + } + void map(OpClassForm &opc) { fprintf(_cpp, " 0"); } + void map(OperandForm &oper) { + const char *reduce = oper.reduce_right(_globals); + if( reduce ) fprintf(_cpp, " %s_rule", reduce); + else fprintf(_cpp, " 0"); + } + void map(char *name) { + const char *reduce = _AD.reduceRight(name); + if( reduce ) fprintf(_cpp, " %s_rule", reduce); + else fprintf(_cpp, " 0"); + } + void map(InstructForm &inst) { + const char *reduce = inst.reduce_right(_globals); + if( reduce ) fprintf(_cpp, " %s_rule", reduce); + else fprintf(_cpp, " 0"); + } +}; + + +// Information needed to generate the Rule names for the DFA +class OutputRuleName : public OutputMap { +public: + OutputRuleName(FILE *hpp, FILE *cpp, FormDict &globals, ArchDesc &AD) + : OutputMap(hpp, cpp, globals, AD) {}; + + void declaration() { fprintf(_hpp, "extern const char *ruleName[];\n"); } + void definition() { fprintf(_cpp, "const char *ruleName[] = {\n"); } + void closing() { fprintf(_cpp, " \"no trailing comma\"\n"); + OutputMap::closing(); + } + void map(OpClassForm &opc) { fprintf(_cpp, " \"%s\"", _AD.machOperEnum(opc._ident) ); } + void map(OperandForm &oper) { fprintf(_cpp, " \"%s\"", _AD.machOperEnum(oper._ident) ); } + void map(char *name) { fprintf(_cpp, " \"%s\"", name ? name : "0"); } + void map(InstructForm &inst){ fprintf(_cpp, " \"%s\"", inst._ident ? inst._ident : "0"); } +}; + + +// Information needed to generate the swallowed mapping for the DFA +class OutputSwallowed : public OutputMap { +public: + OutputSwallowed(FILE *hpp, FILE *cpp, FormDict &globals, ArchDesc &AD) + : OutputMap(hpp, cpp, globals, AD) {}; + + void declaration() { fprintf(_hpp, "extern const bool swallowed[];\n"); } + void definition() { fprintf(_cpp, "const bool swallowed[] = {\n"); } + void closing() { fprintf(_cpp, " false // no trailing comma\n"); + OutputMap::closing(); + } + void map(OperandForm &oper) { // Generate the entry for this opcode + const char *swallowed = oper.swallowed(_globals) ? "true" : "false"; + fprintf(_cpp, " %s", swallowed); + } + void map(OpClassForm &opc) { fprintf(_cpp, " false"); } + void map(char *name) { fprintf(_cpp, " false"); } + void map(InstructForm &inst){ fprintf(_cpp, " false"); } +}; + + +// Information needed to generate the decision array for instruction chain rule +class OutputInstChainRule : public OutputMap { +public: + OutputInstChainRule(FILE *hpp, FILE *cpp, FormDict &globals, ArchDesc &AD) + : OutputMap(hpp, cpp, globals, AD) {}; + + void declaration() { fprintf(_hpp, "extern const bool instruction_chain_rule[];\n"); } + void definition() { fprintf(_cpp, "const bool instruction_chain_rule[] = {\n"); } + void closing() { fprintf(_cpp, " false // no trailing comma\n"); + OutputMap::closing(); + } + void map(OpClassForm &opc) { fprintf(_cpp, " false"); } + void map(OperandForm &oper) { fprintf(_cpp, " false"); } + void map(char *name) { fprintf(_cpp, " false"); } + void map(InstructForm &inst) { // Check for simple chain rule + const char *chain = inst.is_simple_chain_rule(_globals) ? "true" : "false"; + fprintf(_cpp, " %s", chain); + } +}; + + +//---------------------------build_map------------------------------------ +// Build mapping from enumeration for densely packed operands +// TO result and child types. +void ArchDesc::build_map(OutputMap &map) { + FILE *fp_hpp = map.decl_file(); + FILE *fp_cpp = map.def_file(); + int idx = 0; + OperandForm *op; + OpClassForm *opc; + InstructForm *inst; + + // Construct this mapping + map.declaration(); + fprintf(fp_cpp,"\n"); + map.definition(); + + // Output the mapping for operands + map.record_position(OutputMap::BEGIN_OPERANDS, idx ); + _operands.reset(); + for(; (op = (OperandForm*)_operands.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( op->ideal_only() ) continue; + + // Generate the entry for this opcode + map.map(*op); fprintf(fp_cpp, ", // %d\n", idx); + ++idx; + }; + fprintf(fp_cpp, " // last operand\n"); + + // Place all user-defined operand classes into the mapping + map.record_position(OutputMap::BEGIN_OPCLASSES, idx ); + _opclass.reset(); + for(; (opc = (OpClassForm*)_opclass.iter()) != NULL; ) { + map.map(*opc); fprintf(fp_cpp, ", // %d\n", idx); + ++idx; + }; + fprintf(fp_cpp, " // last operand class\n"); + + // Place all internally defined operands into the mapping + map.record_position(OutputMap::BEGIN_INTERNALS, idx ); + _internalOpNames.reset(); + char *name = NULL; + for(; (name = (char *)_internalOpNames.iter()) != NULL; ) { + map.map(name); fprintf(fp_cpp, ", // %d\n", idx); + ++idx; + }; + fprintf(fp_cpp, " // last internally defined operand\n"); + + // Place all user-defined instructions into the mapping + if( map.do_instructions() ) { + map.record_position(OutputMap::BEGIN_INSTRUCTIONS, idx ); + // Output all simple instruction chain rules first + map.record_position(OutputMap::BEGIN_INST_CHAIN_RULES, idx ); + { + _instructions.reset(); + for(; (inst = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( inst->ideal_only() ) continue; + if ( ! inst->is_simple_chain_rule(_globalNames) ) continue; + if ( inst->rematerialize(_globalNames, get_registers()) ) continue; + + map.map(*inst); fprintf(fp_cpp, ", // %d\n", idx); + ++idx; + }; + map.record_position(OutputMap::BEGIN_REMATERIALIZE, idx ); + _instructions.reset(); + for(; (inst = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( inst->ideal_only() ) continue; + if ( ! inst->is_simple_chain_rule(_globalNames) ) continue; + if ( ! inst->rematerialize(_globalNames, get_registers()) ) continue; + + map.map(*inst); fprintf(fp_cpp, ", // %d\n", idx); + ++idx; + }; + map.record_position(OutputMap::END_INST_CHAIN_RULES, idx ); + } + // Output all instructions that are NOT simple chain rules + { + _instructions.reset(); + for(; (inst = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( inst->ideal_only() ) continue; + if ( inst->is_simple_chain_rule(_globalNames) ) continue; + if ( ! inst->rematerialize(_globalNames, get_registers()) ) continue; + + map.map(*inst); fprintf(fp_cpp, ", // %d\n", idx); + ++idx; + }; + map.record_position(OutputMap::END_REMATERIALIZE, idx ); + _instructions.reset(); + for(; (inst = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( inst->ideal_only() ) continue; + if ( inst->is_simple_chain_rule(_globalNames) ) continue; + if ( inst->rematerialize(_globalNames, get_registers()) ) continue; + + map.map(*inst); fprintf(fp_cpp, ", // %d\n", idx); + ++idx; + }; + } + fprintf(fp_cpp, " // last instruction\n"); + map.record_position(OutputMap::END_INSTRUCTIONS, idx ); + } + // Finish defining table + map.closing(); +}; + + +// Helper function for buildReduceMaps +char reg_save_policy(const char *calling_convention) { + char callconv; + + if (!strcmp(calling_convention, "NS")) callconv = 'N'; + else if (!strcmp(calling_convention, "SOE")) callconv = 'E'; + else if (!strcmp(calling_convention, "SOC")) callconv = 'C'; + else if (!strcmp(calling_convention, "AS")) callconv = 'A'; + else callconv = 'Z'; + + return callconv; +} + +//---------------------------generate_assertion_checks------------------- +void ArchDesc::generate_adlc_verification(FILE *fp_cpp) { + fprintf(fp_cpp, "\n"); + + fprintf(fp_cpp, "#ifndef PRODUCT\n"); + fprintf(fp_cpp, "void Compile::adlc_verification() {\n"); + globalDefs().print_asserts(fp_cpp); + fprintf(fp_cpp, "}\n"); + fprintf(fp_cpp, "#endif\n"); + fprintf(fp_cpp, "\n"); +} + +//---------------------------addSourceBlocks----------------------------- +void ArchDesc::addSourceBlocks(FILE *fp_cpp) { + if (_source.count() > 0) + _source.output(fp_cpp); + + generate_adlc_verification(fp_cpp); +} +//---------------------------addHeaderBlocks----------------------------- +void ArchDesc::addHeaderBlocks(FILE *fp_hpp) { + if (_header.count() > 0) + _header.output(fp_hpp); +} +//-------------------------addPreHeaderBlocks---------------------------- +void ArchDesc::addPreHeaderBlocks(FILE *fp_hpp) { + // Output #defines from definition block + globalDefs().print_defines(fp_hpp); + + if (_pre_header.count() > 0) + _pre_header.output(fp_hpp); +} + +//---------------------------buildReduceMaps----------------------------- +// Build mapping from enumeration for densely packed operands +// TO result and child types. +void ArchDesc::buildReduceMaps(FILE *fp_hpp, FILE *fp_cpp) { + RegDef *rdef; + RegDef *next; + + // The emit bodies currently require functions defined in the source block. + + // Build external declarations for mappings + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, "extern const char register_save_policy[];\n"); + fprintf(fp_hpp, "extern const char c_reg_save_policy[];\n"); + fprintf(fp_hpp, "extern const int register_save_type[];\n"); + fprintf(fp_hpp, "\n"); + + // Construct Save-Policy array + fprintf(fp_cpp, "// Map from machine-independent register number to register_save_policy\n"); + fprintf(fp_cpp, "const char register_save_policy[] = {\n"); + _register->reset_RegDefs(); + for( rdef = _register->iter_RegDefs(); rdef != NULL; rdef = next ) { + next = _register->iter_RegDefs(); + char policy = reg_save_policy(rdef->_callconv); + const char *comma = (next != NULL) ? "," : " // no trailing comma"; + fprintf(fp_cpp, " '%c'%s\n", policy, comma); + } + fprintf(fp_cpp, "};\n\n"); + + // Construct Native Save-Policy array + fprintf(fp_cpp, "// Map from machine-independent register number to c_reg_save_policy\n"); + fprintf(fp_cpp, "const char c_reg_save_policy[] = {\n"); + _register->reset_RegDefs(); + for( rdef = _register->iter_RegDefs(); rdef != NULL; rdef = next ) { + next = _register->iter_RegDefs(); + char policy = reg_save_policy(rdef->_c_conv); + const char *comma = (next != NULL) ? "," : " // no trailing comma"; + fprintf(fp_cpp, " '%c'%s\n", policy, comma); + } + fprintf(fp_cpp, "};\n\n"); + + // Construct Register Save Type array + fprintf(fp_cpp, "// Map from machine-independent register number to register_save_type\n"); + fprintf(fp_cpp, "const int register_save_type[] = {\n"); + _register->reset_RegDefs(); + for( rdef = _register->iter_RegDefs(); rdef != NULL; rdef = next ) { + next = _register->iter_RegDefs(); + const char *comma = (next != NULL) ? "," : " // no trailing comma"; + fprintf(fp_cpp, " %s%s\n", rdef->_idealtype, comma); + } + fprintf(fp_cpp, "};\n\n"); + + // Construct the table for reduceOp + OutputReduceOp output_reduce_op(fp_hpp, fp_cpp, _globalNames, *this); + build_map(output_reduce_op); + // Construct the table for leftOp + OutputLeftOp output_left_op(fp_hpp, fp_cpp, _globalNames, *this); + build_map(output_left_op); + // Construct the table for rightOp + OutputRightOp output_right_op(fp_hpp, fp_cpp, _globalNames, *this); + build_map(output_right_op); + // Construct the table of rule names + OutputRuleName output_rule_name(fp_hpp, fp_cpp, _globalNames, *this); + build_map(output_rule_name); + // Construct the boolean table for subsumed operands + OutputSwallowed output_swallowed(fp_hpp, fp_cpp, _globalNames, *this); + build_map(output_swallowed); + // // // Preserve in case we decide to use this table instead of another + //// Construct the boolean table for instruction chain rules + //OutputInstChainRule output_inst_chain(fp_hpp, fp_cpp, _globalNames, *this); + //build_map(output_inst_chain); + +} + + +//---------------------------buildMachOperGenerator--------------------------- + +// Recurse through match tree, building path through corresponding state tree, +// Until we reach the constant we are looking for. +static void path_to_constant(FILE *fp, FormDict &globals, + MatchNode *mnode, uint idx) { + if ( ! mnode) return; + + unsigned position = 0; + const char *result = NULL; + const char *name = NULL; + const char *optype = NULL; + + // Base Case: access constant in ideal node linked to current state node + // Each type of constant has its own access function + if ( (mnode->_lChild == NULL) && (mnode->_rChild == NULL) + && mnode->base_operand(position, globals, result, name, optype) ) { + if ( strcmp(optype,"ConI") == 0 ) { + fprintf(fp, "_leaf->get_int()"); + } else if ( (strcmp(optype,"ConP") == 0) ) { + fprintf(fp, "_leaf->bottom_type()->is_ptr()"); + } else if ( (strcmp(optype,"ConF") == 0) ) { + fprintf(fp, "_leaf->getf()"); + } else if ( (strcmp(optype,"ConD") == 0) ) { + fprintf(fp, "_leaf->getd()"); + } else if ( (strcmp(optype,"ConL") == 0) ) { + fprintf(fp, "_leaf->get_long()"); + } else if ( (strcmp(optype,"Con")==0) ) { + // !!!!! - Update if adding a machine-independent constant type + fprintf(fp, "_leaf->get_int()"); + assert( false, "Unsupported constant type, pointer or indefinite"); + } else if ( (strcmp(optype,"Bool") == 0) ) { + fprintf(fp, "_leaf->as_Bool()->_test._test"); + } else { + assert( false, "Unsupported constant type"); + } + return; + } + + // If constant is in left child, build path and recurse + uint lConsts = (mnode->_lChild) ? (mnode->_lChild->num_consts(globals) ) : 0; + uint rConsts = (mnode->_rChild) ? (mnode->_rChild->num_consts(globals) ) : 0; + if ( (mnode->_lChild) && (lConsts > idx) ) { + fprintf(fp, "_kids[0]->"); + path_to_constant(fp, globals, mnode->_lChild, idx); + return; + } + // If constant is in right child, build path and recurse + if ( (mnode->_rChild) && (rConsts > (idx - lConsts) ) ) { + idx = idx - lConsts; + fprintf(fp, "_kids[1]->"); + path_to_constant(fp, globals, mnode->_rChild, idx); + return; + } + assert( false, "ShouldNotReachHere()"); +} + +// Generate code that is executed when generating a specific Machine Operand +static void genMachOperCase(FILE *fp, FormDict &globalNames, ArchDesc &AD, + OperandForm &op) { + const char *opName = op._ident; + const char *opEnumName = AD.machOperEnum(opName); + uint num_consts = op.num_consts(globalNames); + + // Generate the case statement for this opcode + fprintf(fp, " case %s:", opEnumName); + fprintf(fp, "\n return new (C) %sOper(", opName); + // Access parameters for constructor from the stat object + // + // Build access to condition code value + if ( (num_consts > 0) ) { + uint i = 0; + path_to_constant(fp, globalNames, op._matrule, i); + for ( i = 1; i < num_consts; ++i ) { + fprintf(fp, ", "); + path_to_constant(fp, globalNames, op._matrule, i); + } + } + fprintf(fp, " );\n"); +} + + +// Build switch to invoke "new" MachNode or MachOper +void ArchDesc::buildMachOperGenerator(FILE *fp_cpp) { + int idx = 0; + + // Build switch to invoke 'new' for a specific MachOper + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, + "//------------------------- MachOper Generator ---------------\n"); + fprintf(fp_cpp, + "// A switch statement on the dense-packed user-defined type system\n" + "// that invokes 'new' on the corresponding class constructor.\n"); + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "MachOper *State::MachOperGenerator"); + fprintf(fp_cpp, "(int opcode, Compile* C)"); + fprintf(fp_cpp, "{\n"); + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, " switch(opcode) {\n"); + + // Place all user-defined operands into the mapping + _operands.reset(); + int opIndex = 0; + OperandForm *op; + for( ; (op = (OperandForm*)_operands.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( op->ideal_only() ) continue; + + genMachOperCase(fp_cpp, _globalNames, *this, *op); + }; + + // Do not iterate over operand classes for the operand generator!!! + + // Place all internal operands into the mapping + _internalOpNames.reset(); + const char *iopn; + for( ; (iopn = _internalOpNames.iter()) != NULL; ) { + const char *opEnumName = machOperEnum(iopn); + // Generate the case statement for this opcode + fprintf(fp_cpp, " case %s:", opEnumName); + fprintf(fp_cpp, " return NULL;\n"); + }; + + // Generate the default case for switch(opcode) + fprintf(fp_cpp, " \n"); + fprintf(fp_cpp, " default:\n"); + fprintf(fp_cpp, " fprintf(stderr, \"Default MachOper Generator invoked for: \\n\");\n"); + fprintf(fp_cpp, " fprintf(stderr, \" opcode = %cd\\n\", opcode);\n", '%'); + fprintf(fp_cpp, " break;\n"); + fprintf(fp_cpp, " }\n"); + + // Generate the closing for method Matcher::MachOperGenerator + fprintf(fp_cpp, " return NULL;\n"); + fprintf(fp_cpp, "};\n"); +} + + +//---------------------------buildMachNode------------------------------------- +// Build a new MachNode, for MachNodeGenerator or cisc-spilling +void ArchDesc::buildMachNode(FILE *fp_cpp, InstructForm *inst, const char *indent) { + const char *opType = NULL; + const char *opClass = inst->_ident; + + // Create the MachNode object + fprintf(fp_cpp, "%s %sNode *node = new (C) %sNode();\n",indent, opClass,opClass); + + if ( (inst->num_post_match_opnds() != 0) ) { + // Instruction that contains operands which are not in match rule. + // + // Check if the first post-match component may be an interesting def + bool dont_care = false; + ComponentList &comp_list = inst->_components; + Component *comp = NULL; + comp_list.reset(); + if ( comp_list.match_iter() != NULL ) dont_care = true; + + // Insert operands that are not in match-rule. + // Only insert a DEF if the do_care flag is set + comp_list.reset(); + while ( comp = comp_list.post_match_iter() ) { + // Check if we don't care about DEFs or KILLs that are not USEs + if ( dont_care && (! comp->isa(Component::USE)) ) { + continue; + } + dont_care = true; + // For each operand not in the match rule, call MachOperGenerator + // with the enum for the opcode that needs to be built + // and the node just built, the parent of the operand. + ComponentList clist = inst->_components; + int index = clist.operand_position(comp->_name, comp->_usedef); + const char *opcode = machOperEnum(comp->_type); + const char *parent = "node"; + fprintf(fp_cpp, "%s node->set_opnd_array(%d, ", indent, index); + fprintf(fp_cpp, "MachOperGenerator(%s, C));\n", opcode); + } + } + else if ( inst->is_chain_of_constant(_globalNames, opType) ) { + // An instruction that chains from a constant! + // In this case, we need to subsume the constant into the node + // at operand position, oper_input_base(). + // + // Fill in the constant + fprintf(fp_cpp, "%s node->_opnd_array[%d] = ", indent, + inst->oper_input_base(_globalNames)); + // ##### + // Check for multiple constants and then fill them in. + // Just like MachOperGenerator + const char *opName = inst->_matrule->_rChild->_opType; + fprintf(fp_cpp, "new (C) %sOper(", opName); + // Grab operand form + OperandForm *op = (_globalNames[opName])->is_operand(); + // Look up the number of constants + uint num_consts = op->num_consts(_globalNames); + if ( (num_consts > 0) ) { + uint i = 0; + path_to_constant(fp_cpp, _globalNames, op->_matrule, i); + for ( i = 1; i < num_consts; ++i ) { + fprintf(fp_cpp, ", "); + path_to_constant(fp_cpp, _globalNames, op->_matrule, i); + } + } + fprintf(fp_cpp, " );\n"); + // ##### + } + + // Fill in the bottom_type where requested + if ( inst->captures_bottom_type() ) { + fprintf(fp_cpp, "%s node->_bottom_type = _leaf->bottom_type();\n", indent); + } + if( inst->is_ideal_if() ) { + fprintf(fp_cpp, "%s node->_prob = _leaf->as_If()->_prob;\n", indent); + fprintf(fp_cpp, "%s node->_fcnt = _leaf->as_If()->_fcnt;\n", indent); + } + if( inst->is_ideal_fastlock() ) { + fprintf(fp_cpp, "%s node->_counters = _leaf->as_FastLock()->counters();\n", indent); + } + +} + +//---------------------------declare_cisc_version------------------------------ +// Build CISC version of this instruction +void InstructForm::declare_cisc_version(ArchDesc &AD, FILE *fp_hpp) { + if( AD.can_cisc_spill() ) { + InstructForm *inst_cisc = cisc_spill_alternate(); + if (inst_cisc != NULL) { + fprintf(fp_hpp, " virtual int cisc_operand() const { return %d; }\n", cisc_spill_operand()); + fprintf(fp_hpp, " virtual MachNode *cisc_version(int offset, Compile* C);\n"); + fprintf(fp_hpp, " virtual void use_cisc_RegMask();\n"); + fprintf(fp_hpp, " virtual const RegMask *cisc_RegMask() const { return _cisc_RegMask; }\n"); + } + } +} + +//---------------------------define_cisc_version------------------------------- +// Build CISC version of this instruction +bool InstructForm::define_cisc_version(ArchDesc &AD, FILE *fp_cpp) { + InstructForm *inst_cisc = this->cisc_spill_alternate(); + if( AD.can_cisc_spill() && (inst_cisc != NULL) ) { + const char *name = inst_cisc->_ident; + assert( inst_cisc->num_opnds() == this->num_opnds(), "Must have same number of operands"); + OperandForm *cisc_oper = AD.cisc_spill_operand(); + assert( cisc_oper != NULL, "insanity check"); + const char *cisc_oper_name = cisc_oper->_ident; + assert( cisc_oper_name != NULL, "insanity check"); + // + // Set the correct reg_mask_or_stack for the cisc operand + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "void %sNode::use_cisc_RegMask() {\n", this->_ident); + // Lookup the correct reg_mask_or_stack + const char *reg_mask_name = cisc_reg_mask_name(); + fprintf(fp_cpp, " _cisc_RegMask = &STACK_OR_%s;\n", reg_mask_name); + fprintf(fp_cpp, "}\n"); + // + // Construct CISC version of this instruction + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "// Build CISC version of this instruction\n"); + fprintf(fp_cpp, "MachNode *%sNode::cisc_version( int offset, Compile* C ) {\n", this->_ident); + // Create the MachNode object + fprintf(fp_cpp, " %sNode *node = new (C) %sNode();\n", name, name); + // Fill in the bottom_type where requested + if ( this->captures_bottom_type() ) { + fprintf(fp_cpp, " node->_bottom_type = bottom_type();\n"); + } + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, " // Copy _idx, inputs and operands to new node\n"); + fprintf(fp_cpp, " fill_new_machnode(node, C);\n"); + // Construct operand to access [stack_pointer + offset] + fprintf(fp_cpp, " // Construct operand to access [stack_pointer + offset]\n"); + fprintf(fp_cpp, " node->set_opnd_array(cisc_operand(), new (C) %sOper(offset));\n", cisc_oper_name); + fprintf(fp_cpp, "\n"); + + // Return result and exit scope + fprintf(fp_cpp, " return node;\n"); + fprintf(fp_cpp, "}\n"); + fprintf(fp_cpp, "\n"); + return true; + } + return false; +} + +//---------------------------declare_short_branch_methods---------------------- +// Build prototypes for short branch methods +void InstructForm::declare_short_branch_methods(FILE *fp_hpp) { + if (has_short_branch_form()) { + fprintf(fp_hpp, " virtual MachNode *short_branch_version(Compile* C);\n"); + } +} + +//---------------------------define_short_branch_methods----------------------- +// Build definitions for short branch methods +bool InstructForm::define_short_branch_methods(FILE *fp_cpp) { + if (has_short_branch_form()) { + InstructForm *short_branch = short_branch_form(); + const char *name = short_branch->_ident; + + // Construct short_branch_version() method. + fprintf(fp_cpp, "// Build short branch version of this instruction\n"); + fprintf(fp_cpp, "MachNode *%sNode::short_branch_version(Compile* C) {\n", this->_ident); + // Create the MachNode object + fprintf(fp_cpp, " %sNode *node = new (C) %sNode();\n", name, name); + if( is_ideal_if() ) { + fprintf(fp_cpp, " node->_prob = _prob;\n"); + fprintf(fp_cpp, " node->_fcnt = _fcnt;\n"); + } + // Fill in the bottom_type where requested + if ( this->captures_bottom_type() ) { + fprintf(fp_cpp, " node->_bottom_type = bottom_type();\n"); + } + + fprintf(fp_cpp, "\n"); + // Short branch version must use same node index for access + // through allocator's tables + fprintf(fp_cpp, " // Copy _idx, inputs and operands to new node\n"); + fprintf(fp_cpp, " fill_new_machnode(node, C);\n"); + + // Return result and exit scope + fprintf(fp_cpp, " return node;\n"); + fprintf(fp_cpp, "}\n"); + fprintf(fp_cpp,"\n"); + return true; + } + return false; +} + + +//---------------------------buildMachNodeGenerator---------------------------- +// Build switch to invoke appropriate "new" MachNode for an opcode +void ArchDesc::buildMachNodeGenerator(FILE *fp_cpp) { + + // Build switch to invoke 'new' for a specific MachNode + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, + "//------------------------- MachNode Generator ---------------\n"); + fprintf(fp_cpp, + "// A switch statement on the dense-packed user-defined type system\n" + "// that invokes 'new' on the corresponding class constructor.\n"); + fprintf(fp_cpp, "\n"); + fprintf(fp_cpp, "MachNode *State::MachNodeGenerator"); + fprintf(fp_cpp, "(int opcode, Compile* C)"); + fprintf(fp_cpp, "{\n"); + fprintf(fp_cpp, " switch(opcode) {\n"); + + // Provide constructor for all user-defined instructions + _instructions.reset(); + int opIndex = operandFormCount(); + InstructForm *inst; + for( ; (inst = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure that matrule is defined. + if ( inst->_matrule == NULL ) continue; + + int opcode = opIndex++; + const char *opClass = inst->_ident; + char *opType = NULL; + + // Generate the case statement for this instruction + fprintf(fp_cpp, " case %s_rule:", opClass); + + // Start local scope + fprintf(fp_cpp, " {\n"); + // Generate code to construct the new MachNode + buildMachNode(fp_cpp, inst, " "); + // Return result and exit scope + fprintf(fp_cpp, " return node;\n"); + fprintf(fp_cpp, " }\n"); + } + + // Generate the default case for switch(opcode) + fprintf(fp_cpp, " \n"); + fprintf(fp_cpp, " default:\n"); + fprintf(fp_cpp, " fprintf(stderr, \"Default MachNode Generator invoked for: \\n\");\n"); + fprintf(fp_cpp, " fprintf(stderr, \" opcode = %cd\\n\", opcode);\n", '%'); + fprintf(fp_cpp, " break;\n"); + fprintf(fp_cpp, " };\n"); + + // Generate the closing for method Matcher::MachNodeGenerator + fprintf(fp_cpp, " return NULL;\n"); + fprintf(fp_cpp, "}\n"); +} + + +//---------------------------buildInstructMatchCheck-------------------------- +// Output the method to Matcher which checks whether or not a specific +// instruction has a matching rule for the host architecture. +void ArchDesc::buildInstructMatchCheck(FILE *fp_cpp) const { + fprintf(fp_cpp, "\n\n"); + fprintf(fp_cpp, "const bool Matcher::has_match_rule(int opcode) {\n"); + fprintf(fp_cpp, " assert(_last_machine_leaf < opcode && opcode < _last_opcode, \"opcode in range\");\n"); + fprintf(fp_cpp, " return _hasMatchRule[opcode];\n"); + fprintf(fp_cpp, "}\n\n"); + + fprintf(fp_cpp, "const bool Matcher::_hasMatchRule[_last_opcode] = {\n"); + int i; + for (i = 0; i < _last_opcode - 1; i++) { + fprintf(fp_cpp, " %-5s, // %s\n", + _has_match_rule[i] ? "true" : "false", + NodeClassNames[i]); + } + fprintf(fp_cpp, " %-5s // %s\n", + _has_match_rule[i] ? "true" : "false", + NodeClassNames[i]); + fprintf(fp_cpp, "};\n"); +} + +//---------------------------buildFrameMethods--------------------------------- +// Output the methods to Matcher which specify frame behavior +void ArchDesc::buildFrameMethods(FILE *fp_cpp) { + fprintf(fp_cpp,"\n\n"); + // Stack Direction + fprintf(fp_cpp,"bool Matcher::stack_direction() const { return %s; }\n\n", + _frame->_direction ? "true" : "false"); + // Sync Stack Slots + fprintf(fp_cpp,"int Compile::sync_stack_slots() const { return %s; }\n\n", + _frame->_sync_stack_slots); + // Java Stack Alignment + fprintf(fp_cpp,"uint Matcher::stack_alignment_in_bytes() { return %s; }\n\n", + _frame->_alignment); + // Java Return Address Location + fprintf(fp_cpp,"OptoReg::Name Matcher::return_addr() const {"); + if (_frame->_return_addr_loc) { + fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n", + _frame->_return_addr); + } + else { + fprintf(fp_cpp," return OptoReg::stack2reg(%s); }\n\n", + _frame->_return_addr); + } + // Java Stack Slot Preservation + fprintf(fp_cpp,"uint Compile::in_preserve_stack_slots() "); + fprintf(fp_cpp,"{ return %s; }\n\n", _frame->_in_preserve_slots); + // Top Of Stack Slot Preservation, for both Java and C + fprintf(fp_cpp,"uint Compile::out_preserve_stack_slots() "); + fprintf(fp_cpp,"{ return SharedRuntime::out_preserve_stack_slots(); }\n\n"); + // varargs C out slots killed + fprintf(fp_cpp,"uint Compile::varargs_C_out_slots_killed() const "); + fprintf(fp_cpp,"{ return %s; }\n\n", _frame->_varargs_C_out_slots_killed); + // Java Argument Position + fprintf(fp_cpp,"void Matcher::calling_convention(BasicType *sig_bt, VMRegPair *regs, uint length, bool is_outgoing) {\n"); + fprintf(fp_cpp,"%s\n", _frame->_calling_convention); + fprintf(fp_cpp,"}\n\n"); + // Native Argument Position + fprintf(fp_cpp,"void Matcher::c_calling_convention(BasicType *sig_bt, VMRegPair *regs, uint length) {\n"); + fprintf(fp_cpp,"%s\n", _frame->_c_calling_convention); + fprintf(fp_cpp,"}\n\n"); + // Java Return Value Location + fprintf(fp_cpp,"OptoRegPair Matcher::return_value(int ideal_reg, bool is_outgoing) {\n"); + fprintf(fp_cpp,"%s\n", _frame->_return_value); + fprintf(fp_cpp,"}\n\n"); + // Native Return Value Location + fprintf(fp_cpp,"OptoRegPair Matcher::c_return_value(int ideal_reg, bool is_outgoing) {\n"); + fprintf(fp_cpp,"%s\n", _frame->_c_return_value); + fprintf(fp_cpp,"}\n\n"); + + // Inline Cache Register, mask definition, and encoding + fprintf(fp_cpp,"OptoReg::Name Matcher::inline_cache_reg() {"); + fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n", + _frame->_inline_cache_reg); + fprintf(fp_cpp,"const RegMask &Matcher::inline_cache_reg_mask() {"); + fprintf(fp_cpp," return INLINE_CACHE_REG_mask; }\n\n"); + fprintf(fp_cpp,"int Matcher::inline_cache_reg_encode() {"); + fprintf(fp_cpp," return _regEncode[inline_cache_reg()]; }\n\n"); + + // Interpreter's Method Oop Register, mask definition, and encoding + fprintf(fp_cpp,"OptoReg::Name Matcher::interpreter_method_oop_reg() {"); + fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n", + _frame->_interpreter_method_oop_reg); + fprintf(fp_cpp,"const RegMask &Matcher::interpreter_method_oop_reg_mask() {"); + fprintf(fp_cpp," return INTERPRETER_METHOD_OOP_REG_mask; }\n\n"); + fprintf(fp_cpp,"int Matcher::interpreter_method_oop_reg_encode() {"); + fprintf(fp_cpp," return _regEncode[interpreter_method_oop_reg()]; }\n\n"); + + // Interpreter's Frame Pointer Register, mask definition, and encoding + fprintf(fp_cpp,"OptoReg::Name Matcher::interpreter_frame_pointer_reg() {"); + if (_frame->_interpreter_frame_pointer_reg == NULL) + fprintf(fp_cpp," return OptoReg::Bad; }\n\n"); + else + fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n", + _frame->_interpreter_frame_pointer_reg); + fprintf(fp_cpp,"const RegMask &Matcher::interpreter_frame_pointer_reg_mask() {"); + if (_frame->_interpreter_frame_pointer_reg == NULL) + fprintf(fp_cpp," static RegMask dummy; return dummy; }\n\n"); + else + fprintf(fp_cpp," return INTERPRETER_FRAME_POINTER_REG_mask; }\n\n"); + + // Frame Pointer definition + /* CNC - I can not contemplate having a different frame pointer between + Java and native code; makes my head hurt to think about it. + fprintf(fp_cpp,"OptoReg::Name Matcher::frame_pointer() const {"); + fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n", + _frame->_frame_pointer); + */ + // (Native) Frame Pointer definition + fprintf(fp_cpp,"OptoReg::Name Matcher::c_frame_pointer() const {"); + fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n", + _frame->_frame_pointer); + + // Number of callee-save + always-save registers for calling convention + fprintf(fp_cpp, "// Number of callee-save + always-save registers\n"); + fprintf(fp_cpp, "int Matcher::number_of_saved_registers() {\n"); + RegDef *rdef; + int nof_saved_registers = 0; + _register->reset_RegDefs(); + while( (rdef = _register->iter_RegDefs()) != NULL ) { + if( !strcmp(rdef->_callconv, "SOE") || !strcmp(rdef->_callconv, "AS") ) + ++nof_saved_registers; + } + fprintf(fp_cpp, " return %d;\n", nof_saved_registers); + fprintf(fp_cpp, "};\n\n"); +} + + + + +static int PrintAdlcCisc = 0; +//---------------------------identify_cisc_spilling---------------------------- +// Get info for the CISC_oracle and MachNode::cisc_version() +void ArchDesc::identify_cisc_spill_instructions() { + + // Find the user-defined operand for cisc-spilling + if( _frame->_cisc_spilling_operand_name != NULL ) { + const Form *form = _globalNames[_frame->_cisc_spilling_operand_name]; + OperandForm *oper = form ? form->is_operand() : NULL; + // Verify the user's suggestion + if( oper != NULL ) { + // Ensure that match field is defined. + if ( oper->_matrule != NULL ) { + MatchRule &mrule = *oper->_matrule; + if( strcmp(mrule._opType,"AddP") == 0 ) { + MatchNode *left = mrule._lChild; + MatchNode *right= mrule._rChild; + if( left != NULL && right != NULL ) { + const Form *left_op = _globalNames[left->_opType]->is_operand(); + const Form *right_op = _globalNames[right->_opType]->is_operand(); + if( (left_op != NULL && right_op != NULL) + && (left_op->interface_type(_globalNames) == Form::register_interface) + && (right_op->interface_type(_globalNames) == Form::constant_interface) ) { + // Successfully verified operand + set_cisc_spill_operand( oper ); + if( _cisc_spill_debug ) { + fprintf(stderr, "\n\nVerified CISC-spill operand %s\n\n", oper->_ident); + } + } + } + } + } + } + } + + if( cisc_spill_operand() != NULL ) { + // N^2 comparison of instructions looking for a cisc-spilling version + _instructions.reset(); + InstructForm *instr; + for( ; (instr = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure that match field is defined. + if ( instr->_matrule == NULL ) continue; + + MatchRule &mrule = *instr->_matrule; + Predicate *pred = instr->build_predicate(); + + // Grab the machine type of the operand + const char *rootOp = instr->_ident; + mrule._machType = rootOp; + + // Find result type for match + const char *result = instr->reduce_result(); + + if( PrintAdlcCisc ) fprintf(stderr, " new instruction %s \n", instr->_ident ? instr->_ident : " "); + bool found_cisc_alternate = false; + _instructions.reset2(); + InstructForm *instr2; + for( ; !found_cisc_alternate && (instr2 = (InstructForm*)_instructions.iter2()) != NULL; ) { + // Ensure that match field is defined. + if( PrintAdlcCisc ) fprintf(stderr, " instr2 == %s \n", instr2->_ident ? instr2->_ident : " "); + if ( instr2->_matrule != NULL + && (instr != instr2 ) // Skip self + && (instr2->reduce_result() != NULL) // want same result + && (strcmp(result, instr2->reduce_result()) == 0)) { + MatchRule &mrule2 = *instr2->_matrule; + Predicate *pred2 = instr2->build_predicate(); + found_cisc_alternate = instr->cisc_spills_to(*this, instr2); + } + } + } + } +} + +//---------------------------build_cisc_spilling------------------------------- +// Get info for the CISC_oracle and MachNode::cisc_version() +void ArchDesc::build_cisc_spill_instructions(FILE *fp_hpp, FILE *fp_cpp) { + // Output the table for cisc spilling + fprintf(fp_cpp, "// The following instructions can cisc-spill\n"); + _instructions.reset(); + InstructForm *inst = NULL; + for(; (inst = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( inst->ideal_only() ) continue; + const char *inst_name = inst->_ident; + int operand = inst->cisc_spill_operand(); + if( operand != AdlcVMDeps::Not_cisc_spillable ) { + InstructForm *inst2 = inst->cisc_spill_alternate(); + fprintf(fp_cpp, "// %s can cisc-spill operand %d to %s\n", inst->_ident, operand, inst2->_ident); + } + } + fprintf(fp_cpp, "\n\n"); +} + +//---------------------------identify_short_branches---------------------------- +// Get info for our short branch replacement oracle. +void ArchDesc::identify_short_branches() { + // Walk over all instructions, checking to see if they match a short + // branching alternate. + _instructions.reset(); + InstructForm *instr; + while( (instr = (InstructForm*)_instructions.iter()) != NULL ) { + // The instruction must have a match rule. + if (instr->_matrule != NULL && + instr->is_short_branch()) { + + _instructions.reset2(); + InstructForm *instr2; + while( (instr2 = (InstructForm*)_instructions.iter2()) != NULL ) { + instr2->check_branch_variant(*this, instr); + } + } + } +} + + +//---------------------------identify_unique_operands--------------------------- +// Identify unique operands. +void ArchDesc::identify_unique_operands() { + // Walk over all instructions. + _instructions.reset(); + InstructForm *instr; + while( (instr = (InstructForm*)_instructions.iter()) != NULL ) { + // Ensure this is a machine-world instruction + if (!instr->ideal_only()) { + instr->set_unique_opnds(); + } + } +} diff --git a/hotspot/src/share/vm/adlc/output_h.cpp b/hotspot/src/share/vm/adlc/output_h.cpp new file mode 100644 index 00000000000..5429e57d954 --- /dev/null +++ b/hotspot/src/share/vm/adlc/output_h.cpp @@ -0,0 +1,2102 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// output_h.cpp - Class HPP file output routines for architecture definition +#include "adlc.hpp" + + +// Generate the #define that describes the number of registers. +static void defineRegCount(FILE *fp, RegisterForm *registers) { + if (registers) { + int regCount = AdlcVMDeps::Physical + registers->_rdefs.count(); + fprintf(fp,"\n"); + fprintf(fp,"// the number of reserved registers + machine registers.\n"); + fprintf(fp,"#define REG_COUNT %d\n", regCount); + } +} + +// Output enumeration of machine register numbers +// (1) +// // Enumerate machine registers starting after reserved regs. +// // in the order of occurrence in the register block. +// enum MachRegisterNumbers { +// EAX_num = 0, +// ... +// _last_Mach_Reg +// } +void ArchDesc::buildMachRegisterNumbers(FILE *fp_hpp) { + if (_register) { + RegDef *reg_def = NULL; + + // Output a #define for the number of machine registers + defineRegCount(fp_hpp, _register); + + // Count all the Save_On_Entry and Always_Save registers + int saved_on_entry = 0; + int c_saved_on_entry = 0; + _register->reset_RegDefs(); + while( (reg_def = _register->iter_RegDefs()) != NULL ) { + if( strcmp(reg_def->_callconv,"SOE") == 0 || + strcmp(reg_def->_callconv,"AS") == 0 ) ++saved_on_entry; + if( strcmp(reg_def->_c_conv,"SOE") == 0 || + strcmp(reg_def->_c_conv,"AS") == 0 ) ++c_saved_on_entry; + } + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, "// the number of save_on_entry + always_saved registers.\n"); + fprintf(fp_hpp, "#define MAX_SAVED_ON_ENTRY_REG_COUNT %d\n", max(saved_on_entry,c_saved_on_entry)); + fprintf(fp_hpp, "#define SAVED_ON_ENTRY_REG_COUNT %d\n", saved_on_entry); + fprintf(fp_hpp, "#define C_SAVED_ON_ENTRY_REG_COUNT %d\n", c_saved_on_entry); + + // (1) + // Build definition for enumeration of register numbers + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, "// Enumerate machine register numbers starting after reserved regs.\n"); + fprintf(fp_hpp, "// in the order of occurrence in the register block.\n"); + fprintf(fp_hpp, "enum MachRegisterNumbers {\n"); + + // Output the register number for each register in the allocation classes + _register->reset_RegDefs(); + int i = 0; + while( (reg_def = _register->iter_RegDefs()) != NULL ) { + fprintf(fp_hpp," %s_num,\t\t// %d\n", reg_def->_regname, i++); + } + // Finish defining enumeration + fprintf(fp_hpp, " _last_Mach_Reg\t// %d\n", i); + fprintf(fp_hpp, "};\n"); + } + + fprintf(fp_hpp, "\n// Size of register-mask in ints\n"); + fprintf(fp_hpp, "#define RM_SIZE %d\n",RegisterForm::RegMask_Size()); + fprintf(fp_hpp, "// Unroll factor for loops over the data in a RegMask\n"); + fprintf(fp_hpp, "#define FORALL_BODY "); + int len = RegisterForm::RegMask_Size(); + for( int i = 0; i < len; i++ ) + fprintf(fp_hpp, "BODY(%d) ",i); + fprintf(fp_hpp, "\n\n"); + + fprintf(fp_hpp,"class RegMask;\n"); + // All RegMasks are declared "extern const ..." in ad_.hpp + // fprintf(fp_hpp,"extern RegMask STACK_OR_STACK_SLOTS_mask;\n\n"); +} + + +// Output enumeration of machine register encodings +// (2) +// // Enumerate machine registers starting after reserved regs. +// // in the order of occurrence in the alloc_class(es). +// enum MachRegisterEncodes { +// EAX_enc = 0x00, +// ... +// } +void ArchDesc::buildMachRegisterEncodes(FILE *fp_hpp) { + if (_register) { + RegDef *reg_def = NULL; + RegDef *reg_def_next = NULL; + + // (2) + // Build definition for enumeration of encode values + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, "// Enumerate machine registers starting after reserved regs.\n"); + fprintf(fp_hpp, "// in the order of occurrence in the alloc_class(es).\n"); + fprintf(fp_hpp, "enum MachRegisterEncodes {\n"); + + // Output the register encoding for each register in the allocation classes + _register->reset_RegDefs(); + reg_def_next = _register->iter_RegDefs(); + while( (reg_def = reg_def_next) != NULL ) { + reg_def_next = _register->iter_RegDefs(); + fprintf(fp_hpp," %s_enc = %s%s\n", + reg_def->_regname, reg_def->register_encode(), reg_def_next == NULL? "" : "," ); + } + // Finish defining enumeration + fprintf(fp_hpp, "};\n"); + + } // Done with register form +} + + +// Declare an array containing the machine register names, strings. +static void declareRegNames(FILE *fp, RegisterForm *registers) { + if (registers) { +// fprintf(fp,"\n"); +// fprintf(fp,"// An array of character pointers to machine register names.\n"); +// fprintf(fp,"extern const char *regName[];\n"); + } +} + +// Declare an array containing the machine register sizes in 32-bit words. +void ArchDesc::declareRegSizes(FILE *fp) { +// regSize[] is not used +} + +// Declare an array containing the machine register encoding values +static void declareRegEncodes(FILE *fp, RegisterForm *registers) { + if (registers) { + // // // + // fprintf(fp,"\n"); + // fprintf(fp,"// An array containing the machine register encode values\n"); + // fprintf(fp,"extern const char regEncode[];\n"); + } +} + + +// --------------------------------------------------------------------------- +//------------------------------Utilities to build Instruction Classes-------- +// --------------------------------------------------------------------------- +static void out_RegMask(FILE *fp) { + fprintf(fp," virtual const RegMask &out_RegMask() const;\n"); +} + +// --------------------------------------------------------------------------- +//--------Utilities to build MachOper and MachNode derived Classes------------ +// --------------------------------------------------------------------------- + +//------------------------------Utilities to build Operand Classes------------ +static void in_RegMask(FILE *fp) { + fprintf(fp," virtual const RegMask *in_RegMask(int index) const;\n"); +} + +static void declare_hash(FILE *fp) { + fprintf(fp," virtual uint hash() const;\n"); +} + +static void declare_cmp(FILE *fp) { + fprintf(fp," virtual uint cmp( const MachOper &oper ) const;\n"); +} + +static void declareConstStorage(FILE *fp, FormDict &globals, OperandForm *oper) { + int i = 0; + Component *comp; + + if (oper->num_consts(globals) == 0) return; + // Iterate over the component list looking for constants + oper->_components.reset(); + if ((comp = oper->_components.iter()) == NULL) { + assert(oper->num_consts(globals) == 1, "Bad component list detected.\n"); + const char *type = oper->ideal_type(globals); + if (!strcmp(type, "ConI")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp," int32 _c%d;\n", i); + } + else if (!strcmp(type, "ConP")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp," const TypePtr *_c%d;\n", i); + } + else if (!strcmp(type, "ConL")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp," jlong _c%d;\n", i); + } + else if (!strcmp(type, "ConF")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp," jfloat _c%d;\n", i); + } + else if (!strcmp(type, "ConD")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp," jdouble _c%d;\n", i); + } + else if (!strcmp(type, "Bool")) { + fprintf(fp,"private:\n"); + fprintf(fp," BoolTest::mask _c%d;\n", i); + fprintf(fp,"public:\n"); + } + else { + assert(0, "Non-constant operand lacks component list."); + } + } // end if NULL + else { + oper->_components.reset(); + while ((comp = oper->_components.iter()) != NULL) { + if (!strcmp(comp->base_type(globals), "ConI")) { + fprintf(fp," jint _c%d;\n", i); + i++; + } + else if (!strcmp(comp->base_type(globals), "ConP")) { + fprintf(fp," const TypePtr *_c%d;\n", i); + i++; + } + else if (!strcmp(comp->base_type(globals), "ConL")) { + fprintf(fp," jlong _c%d;\n", i); + i++; + } + else if (!strcmp(comp->base_type(globals), "ConF")) { + fprintf(fp," jfloat _c%d;\n", i); + i++; + } + else if (!strcmp(comp->base_type(globals), "ConD")) { + fprintf(fp," jdouble _c%d;\n", i); + i++; + } + } + } +} + +// Declare constructor. +// Parameters start with condition code, then all other constants +// +// (0) public: +// (1) MachXOper(int32 ccode, int32 c0, int32 c1, ..., int32 cn) +// (2) : _ccode(ccode), _c0(c0), _c1(c1), ..., _cn(cn) { } +// +static void defineConstructor(FILE *fp, const char *name, uint num_consts, + ComponentList &lst, bool is_ideal_bool, + Form::DataType constant_type, FormDict &globals) { + fprintf(fp,"public:\n"); + // generate line (1) + fprintf(fp," %sOper(", name); + if( num_consts == 0 ) { + fprintf(fp,") {}\n"); + return; + } + + // generate parameters for constants + uint i = 0; + Component *comp; + lst.reset(); + if ((comp = lst.iter()) == NULL) { + assert(num_consts == 1, "Bad component list detected.\n"); + switch( constant_type ) { + case Form::idealI : { + fprintf(fp,is_ideal_bool ? "BoolTest::mask c%d" : "int32 c%d", i); + break; + } + case Form::idealP : { fprintf(fp,"const TypePtr *c%d", i); break; } + case Form::idealL : { fprintf(fp,"jlong c%d", i); break; } + case Form::idealF : { fprintf(fp,"jfloat c%d", i); break; } + case Form::idealD : { fprintf(fp,"jdouble c%d", i); break; } + default: + assert(!is_ideal_bool, "Non-constant operand lacks component list."); + break; + } + } // end if NULL + else { + lst.reset(); + while((comp = lst.iter()) != NULL) { + if (!strcmp(comp->base_type(globals), "ConI")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp,"int32 c%d", i); + i++; + } + else if (!strcmp(comp->base_type(globals), "ConP")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp,"const TypePtr *c%d", i); + i++; + } + else if (!strcmp(comp->base_type(globals), "ConL")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp,"jlong c%d", i); + i++; + } + else if (!strcmp(comp->base_type(globals), "ConF")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp,"jfloat c%d", i); + i++; + } + else if (!strcmp(comp->base_type(globals), "ConD")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp,"jdouble c%d", i); + i++; + } + else if (!strcmp(comp->base_type(globals), "Bool")) { + if (i > 0) fprintf(fp,", "); + fprintf(fp,"BoolTest::mask c%d", i); + i++; + } + } + } + // finish line (1) and start line (2) + fprintf(fp,") : "); + // generate initializers for constants + i = 0; + fprintf(fp,"_c%d(c%d)", i, i); + for( i = 1; i < num_consts; ++i) { + fprintf(fp,", _c%d(c%d)", i, i); + } + // The body for the constructor is empty + fprintf(fp," {}\n"); +} + +// --------------------------------------------------------------------------- +// Utilities to generate format rules for machine operands and instructions +// --------------------------------------------------------------------------- + +// Generate the format rule for condition codes +static void defineCCodeDump(FILE *fp, int i) { + fprintf(fp, " if( _c%d == BoolTest::eq ) st->print(\"eq\");\n",i); + fprintf(fp, " else if( _c%d == BoolTest::ne ) st->print(\"ne\");\n",i); + fprintf(fp, " else if( _c%d == BoolTest::le ) st->print(\"le\");\n",i); + fprintf(fp, " else if( _c%d == BoolTest::ge ) st->print(\"ge\");\n",i); + fprintf(fp, " else if( _c%d == BoolTest::lt ) st->print(\"lt\");\n",i); + fprintf(fp, " else if( _c%d == BoolTest::gt ) st->print(\"gt\");\n",i); +} + +// Output code that dumps constant values, increment "i" if type is constant +static uint dump_spec_constant(FILE *fp, const char *ideal_type, uint i) { + if (!strcmp(ideal_type, "ConI")) { + fprintf(fp," st->print(\"#%%d\", _c%d);\n", i); + ++i; + } + else if (!strcmp(ideal_type, "ConP")) { + fprintf(fp," _c%d->dump_on(st);\n", i); + ++i; + } + else if (!strcmp(ideal_type, "ConL")) { + fprintf(fp," st->print(\"#\" INT64_FORMAT, _c%d);\n", i); + ++i; + } + else if (!strcmp(ideal_type, "ConF")) { + fprintf(fp," st->print(\"#%%f\", _c%d);\n", i); + ++i; + } + else if (!strcmp(ideal_type, "ConD")) { + fprintf(fp," st->print(\"#%%f\", _c%d);\n", i); + ++i; + } + else if (!strcmp(ideal_type, "Bool")) { + defineCCodeDump(fp,i); + ++i; + } + + return i; +} + +// Generate the format rule for an operand +void gen_oper_format(FILE *fp, FormDict &globals, OperandForm &oper, bool for_c_file = false) { + if (!for_c_file) { + // invoked after output #ifndef PRODUCT to ad_.hpp + // compile the bodies separately, to cut down on recompilations + fprintf(fp," virtual void int_format(PhaseRegAlloc *ra, const MachNode *node, outputStream *st) const;\n"); + fprintf(fp," virtual void ext_format(PhaseRegAlloc *ra, const MachNode *node, int idx, outputStream *st) const;\n"); + return; + } + + // Local pointer indicates remaining part of format rule + uint idx = 0; // position of operand in match rule + + // Generate internal format function, used when stored locally + fprintf(fp, "\n#ifndef PRODUCT\n"); + fprintf(fp,"void %sOper::int_format(PhaseRegAlloc *ra, const MachNode *node, outputStream *st) const {\n", oper._ident); + // Generate the user-defined portion of the format + if (oper._format) { + if ( oper._format->_strings.count() != 0 ) { + // No initialization code for int_format + + // Build the format from the entries in strings and rep_vars + const char *string = NULL; + oper._format->_rep_vars.reset(); + oper._format->_strings.reset(); + while ( (string = oper._format->_strings.iter()) != NULL ) { + fprintf(fp," "); + + // Check if this is a standard string or a replacement variable + if ( string != NameList::_signal ) { + // Normal string + // Pass through to st->print + fprintf(fp,"st->print(\"%s\");\n", string); + } else { + // Replacement variable + const char *rep_var = oper._format->_rep_vars.iter(); + // Check that it is a local name, and an operand + OperandForm *op = oper._localNames[rep_var]->is_operand(); + assert( op, "replacement variable was not found in local names"); + // Get index if register or constant + if ( op->_matrule && op->_matrule->is_base_register(globals) ) { + idx = oper.register_position( globals, rep_var); + } + else if (op->_matrule && op->_matrule->is_base_constant(globals)) { + idx = oper.constant_position( globals, rep_var); + } else { + idx = 0; + } + + // output invocation of "$..."s format function + if ( op != NULL ) op->int_format(fp, globals, idx); + + if ( idx == -1 ) { + fprintf(stderr, + "Using a name, %s, that isn't in match rule\n", rep_var); + assert( strcmp(op->_ident,"label")==0, "Unimplemented"); + } + } // Done with a replacement variable + } // Done with all format strings + } else { + // Default formats for base operands (RegI, RegP, ConI, ConP, ...) + oper.int_format(fp, globals, 0); + } + + } else { // oper._format == NULL + // Provide a few special case formats where the AD writer cannot. + if ( strcmp(oper._ident,"Universe")==0 ) { + fprintf(fp, " st->print(\"$$univ\");\n"); + } + // labelOper::int_format is defined in ad_<...>.cpp + } + // ALWAYS! Provide a special case output for condition codes. + if( oper.is_ideal_bool() ) { + defineCCodeDump(fp,0); + } + fprintf(fp,"}\n"); + + // Generate external format function, when data is stored externally + fprintf(fp,"void %sOper::ext_format(PhaseRegAlloc *ra, const MachNode *node, int idx, outputStream *st) const {\n", oper._ident); + // Generate the user-defined portion of the format + if (oper._format) { + if ( oper._format->_strings.count() != 0 ) { + + // Check for a replacement string "$..." + if ( oper._format->_rep_vars.count() != 0 ) { + // Initialization code for ext_format + } + + // Build the format from the entries in strings and rep_vars + const char *string = NULL; + oper._format->_rep_vars.reset(); + oper._format->_strings.reset(); + while ( (string = oper._format->_strings.iter()) != NULL ) { + fprintf(fp," "); + + // Check if this is a standard string or a replacement variable + if ( string != NameList::_signal ) { + // Normal string + // Pass through to st->print + fprintf(fp,"st->print(\"%s\");\n", string); + } else { + // Replacement variable + const char *rep_var = oper._format->_rep_vars.iter(); + // Check that it is a local name, and an operand + OperandForm *op = oper._localNames[rep_var]->is_operand(); + assert( op, "replacement variable was not found in local names"); + // Get index if register or constant + if ( op->_matrule && op->_matrule->is_base_register(globals) ) { + idx = oper.register_position( globals, rep_var); + } + else if (op->_matrule && op->_matrule->is_base_constant(globals)) { + idx = oper.constant_position( globals, rep_var); + } else { + idx = 0; + } + // output invocation of "$..."s format function + if ( op != NULL ) op->ext_format(fp, globals, idx); + + // Lookup the index position of the replacement variable + idx = oper._components.operand_position_format(rep_var); + if ( idx == -1 ) { + fprintf(stderr, + "Using a name, %s, that isn't in match rule\n", rep_var); + assert( strcmp(op->_ident,"label")==0, "Unimplemented"); + } + } // Done with a replacement variable + } // Done with all format strings + + } else { + // Default formats for base operands (RegI, RegP, ConI, ConP, ...) + oper.ext_format(fp, globals, 0); + } + } else { // oper._format == NULL + // Provide a few special case formats where the AD writer cannot. + if ( strcmp(oper._ident,"Universe")==0 ) { + fprintf(fp, " st->print(\"$$univ\");\n"); + } + // labelOper::ext_format is defined in ad_<...>.cpp + } + // ALWAYS! Provide a special case output for condition codes. + if( oper.is_ideal_bool() ) { + defineCCodeDump(fp,0); + } + fprintf(fp, "}\n"); + fprintf(fp, "#endif\n"); +} + + +// Generate the format rule for an instruction +void gen_inst_format(FILE *fp, FormDict &globals, InstructForm &inst, bool for_c_file = false) { + if (!for_c_file) { + // compile the bodies separately, to cut down on recompilations + // #ifndef PRODUCT region generated by caller + fprintf(fp," virtual void format(PhaseRegAlloc *ra, outputStream *st) const;\n"); + return; + } + + // Define the format function + fprintf(fp, "#ifndef PRODUCT\n"); + fprintf(fp, "void %sNode::format(PhaseRegAlloc *ra, outputStream *st) const {\n", inst._ident); + + // Generate the user-defined portion of the format + if( inst._format ) { + // If there are replacement variables, + // Generate index values needed for determing the operand position + if( inst._format->_rep_vars.count() ) + inst.index_temps(fp, globals); + + // Build the format from the entries in strings and rep_vars + const char *string = NULL; + inst._format->_rep_vars.reset(); + inst._format->_strings.reset(); + while( (string = inst._format->_strings.iter()) != NULL ) { + fprintf(fp," "); + // Check if this is a standard string or a replacement variable + if( string != NameList::_signal ) // Normal string. Pass through. + fprintf(fp,"st->print(\"%s\");\n", string); + else // Replacement variable + inst.rep_var_format( fp, inst._format->_rep_vars.iter() ); + } // Done with all format strings + } // Done generating the user-defined portion of the format + + // Add call debug info automatically + Form::CallType call_type = inst.is_ideal_call(); + if( call_type != Form::invalid_type ) { + switch( call_type ) { + case Form::JAVA_DYNAMIC: + fprintf(fp," _method->print_short_name();\n"); + break; + case Form::JAVA_STATIC: + fprintf(fp," if( _method ) _method->print_short_name(st); else st->print(\" wrapper for: %%s\", _name);\n"); + fprintf(fp," if( !_method ) dump_trap_args(st);\n"); + break; + case Form::JAVA_COMPILED: + case Form::JAVA_INTERP: + break; + case Form::JAVA_RUNTIME: + case Form::JAVA_LEAF: + case Form::JAVA_NATIVE: + fprintf(fp," st->print(\" %%s\", _name);"); + break; + default: + assert(0,"ShouldNotReacHere"); + } + fprintf(fp, " st->print_cr(\"\");\n" ); + fprintf(fp, " if (_jvms) _jvms->format(ra, this, st); else st->print_cr(\" No JVM State Info\");\n" ); + fprintf(fp, " st->print(\" # \");\n" ); + fprintf(fp, " if( _jvms ) _oop_map->print_on(st);\n"); + } + else if(inst.is_ideal_safepoint()) { + fprintf(fp, " st->print(\"\");\n" ); + fprintf(fp, " if (_jvms) _jvms->format(ra, this, st); else st->print_cr(\" No JVM State Info\");\n" ); + fprintf(fp, " st->print(\" # \");\n" ); + fprintf(fp, " if( _jvms ) _oop_map->print_on(st);\n"); + } + else if( inst.is_ideal_if() ) { + fprintf(fp, " st->print(\" P=%%f C=%%f\",_prob,_fcnt);\n" ); + } + else if( inst.is_ideal_mem() ) { + // Print out the field name if available to improve readability + fprintf(fp, " if (ra->C->alias_type(adr_type())->field() != NULL) {\n"); + fprintf(fp, " st->print(\" ! Field \");\n"); + fprintf(fp, " if( ra->C->alias_type(adr_type())->is_volatile() )\n"); + fprintf(fp, " st->print(\" Volatile\");\n"); + fprintf(fp, " ra->C->alias_type(adr_type())->field()->holder()->name()->print_symbol_on(st);\n"); + fprintf(fp, " st->print(\".\");\n"); + fprintf(fp, " ra->C->alias_type(adr_type())->field()->name()->print_symbol_on(st);\n"); + fprintf(fp, " } else\n"); + // Make sure 'Volatile' gets printed out + fprintf(fp, " if( ra->C->alias_type(adr_type())->is_volatile() )\n"); + fprintf(fp, " st->print(\" Volatile!\");\n"); + } + + // Complete the definition of the format function + fprintf(fp, " }\n#endif\n"); +} + +static bool is_non_constant(char* x) { + // Tells whether the string (part of an operator interface) is non-constant. + // Simply detect whether there is an occurrence of a formal parameter, + // which will always begin with '$'. + return strchr(x, '$') == 0; +} + +void ArchDesc::declare_pipe_classes(FILE *fp_hpp) { + if (!_pipeline) + return; + + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, "// Pipeline_Use_Cycle_Mask Class\n"); + fprintf(fp_hpp, "class Pipeline_Use_Cycle_Mask {\n"); + + if (_pipeline->_maxcycleused <= +#ifdef SPARC + 64 +#else + 32 +#endif + ) { + fprintf(fp_hpp, "protected:\n"); + fprintf(fp_hpp, " %s _mask;\n\n", _pipeline->_maxcycleused <= 32 ? "uint" : "uint64_t" ); + fprintf(fp_hpp, "public:\n"); + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask() : _mask(0) {}\n\n"); + if (_pipeline->_maxcycleused <= 32) + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask(uint mask) : _mask(mask) {}\n\n"); + else { + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask(uint mask1, uint mask2) : _mask((((uint64_t)mask1) << 32) | mask2) {}\n\n"); + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask(uint64_t mask) : _mask(mask) {}\n\n"); + } + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask& operator=(const Pipeline_Use_Cycle_Mask &in) {\n"); + fprintf(fp_hpp, " _mask = in._mask;\n"); + fprintf(fp_hpp, " return *this;\n"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " bool overlaps(const Pipeline_Use_Cycle_Mask &in2) const {\n"); + fprintf(fp_hpp, " return ((_mask & in2._mask) != 0);\n"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask& operator<<=(int n) {\n"); + fprintf(fp_hpp, " _mask <<= n;\n"); + fprintf(fp_hpp, " return *this;\n"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " void Or(const Pipeline_Use_Cycle_Mask &in2) {\n"); + fprintf(fp_hpp, " _mask |= in2._mask;\n"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " friend Pipeline_Use_Cycle_Mask operator&(const Pipeline_Use_Cycle_Mask &, const Pipeline_Use_Cycle_Mask &);\n"); + fprintf(fp_hpp, " friend Pipeline_Use_Cycle_Mask operator|(const Pipeline_Use_Cycle_Mask &, const Pipeline_Use_Cycle_Mask &);\n\n"); + } + else { + fprintf(fp_hpp, "protected:\n"); + uint masklen = (_pipeline->_maxcycleused + 31) >> 5; + uint l; + fprintf(fp_hpp, " uint "); + for (l = 1; l <= masklen; l++) + fprintf(fp_hpp, "_mask%d%s", l, l < masklen ? ", " : ";\n\n"); + fprintf(fp_hpp, "public:\n"); + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask() : "); + for (l = 1; l <= masklen; l++) + fprintf(fp_hpp, "_mask%d(0)%s", l, l < masklen ? ", " : " {}\n\n"); + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask("); + for (l = 1; l <= masklen; l++) + fprintf(fp_hpp, "uint mask%d%s", l, l < masklen ? ", " : ") : "); + for (l = 1; l <= masklen; l++) + fprintf(fp_hpp, "_mask%d(mask%d)%s", l, l, l < masklen ? ", " : " {}\n\n"); + + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask& operator=(const Pipeline_Use_Cycle_Mask &in) {\n"); + for (l = 1; l <= masklen; l++) + fprintf(fp_hpp, " _mask%d = in._mask%d;\n", l, l); + fprintf(fp_hpp, " return *this;\n"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask intersect(const Pipeline_Use_Cycle_Mask &in2) {\n"); + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask out;\n"); + for (l = 1; l <= masklen; l++) + fprintf(fp_hpp, " out._mask%d = _mask%d & in2._mask%d;\n", l, l, l); + fprintf(fp_hpp, " return out;\n"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " bool overlaps(const Pipeline_Use_Cycle_Mask &in2) const {\n"); + fprintf(fp_hpp, " return ("); + for (l = 1; l <= masklen; l++) + fprintf(fp_hpp, "((_mask%d & in2._mask%d) != 0)%s", l, l, l < masklen ? " || " : ""); + fprintf(fp_hpp, ") ? true : false;\n"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask& operator<<=(int n) {\n"); + fprintf(fp_hpp, " if (n >= 32)\n"); + fprintf(fp_hpp, " do {\n "); + for (l = masklen; l > 1; l--) + fprintf(fp_hpp, " _mask%d = _mask%d;", l, l-1); + fprintf(fp_hpp, " _mask%d = 0;\n", 1); + fprintf(fp_hpp, " } while ((n -= 32) >= 32);\n\n"); + fprintf(fp_hpp, " if (n > 0) {\n"); + fprintf(fp_hpp, " uint m = 32 - n;\n"); + fprintf(fp_hpp, " uint mask = (1 << n) - 1;\n"); + fprintf(fp_hpp, " uint temp%d = mask & (_mask%d >> m); _mask%d <<= n;\n", 2, 1, 1); + for (l = 2; l < masklen; l++) { + fprintf(fp_hpp, " uint temp%d = mask & (_mask%d >> m); _mask%d <<= n; _mask%d |= temp%d;\n", l+1, l, l, l, l); + } + fprintf(fp_hpp, " _mask%d <<= n; _mask%d |= temp%d;\n", masklen, masklen, masklen); + fprintf(fp_hpp, " }\n"); + + fprintf(fp_hpp, " return *this;\n"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " void Or(const Pipeline_Use_Cycle_Mask &);\n\n"); + fprintf(fp_hpp, " friend Pipeline_Use_Cycle_Mask operator&(const Pipeline_Use_Cycle_Mask &, const Pipeline_Use_Cycle_Mask &);\n"); + fprintf(fp_hpp, " friend Pipeline_Use_Cycle_Mask operator|(const Pipeline_Use_Cycle_Mask &, const Pipeline_Use_Cycle_Mask &);\n\n"); + } + + fprintf(fp_hpp, " friend class Pipeline_Use;\n\n"); + fprintf(fp_hpp, " friend class Pipeline_Use_Element;\n\n"); + fprintf(fp_hpp, "};\n\n"); + + uint rescount = 0; + const char *resource; + + for ( _pipeline->_reslist.reset(); (resource = _pipeline->_reslist.iter()) != NULL; ) { + int mask = _pipeline->_resdict[resource]->is_resource()->mask(); + if ((mask & (mask-1)) == 0) + rescount++; + } + + fprintf(fp_hpp, "// Pipeline_Use_Element Class\n"); + fprintf(fp_hpp, "class Pipeline_Use_Element {\n"); + fprintf(fp_hpp, "protected:\n"); + fprintf(fp_hpp, " // Mask of used functional units\n"); + fprintf(fp_hpp, " uint _used;\n\n"); + fprintf(fp_hpp, " // Lower and upper bound of functional unit number range\n"); + fprintf(fp_hpp, " uint _lb, _ub;\n\n"); + fprintf(fp_hpp, " // Indicates multiple functionals units available\n"); + fprintf(fp_hpp, " bool _multiple;\n\n"); + fprintf(fp_hpp, " // Mask of specific used cycles\n"); + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask _mask;\n\n"); + fprintf(fp_hpp, "public:\n"); + fprintf(fp_hpp, " Pipeline_Use_Element() {}\n\n"); + fprintf(fp_hpp, " Pipeline_Use_Element(uint used, uint lb, uint ub, bool multiple, Pipeline_Use_Cycle_Mask mask)\n"); + fprintf(fp_hpp, " : _used(used), _lb(lb), _ub(ub), _multiple(multiple), _mask(mask) {}\n\n"); + fprintf(fp_hpp, " uint used() const { return _used; }\n\n"); + fprintf(fp_hpp, " uint lowerBound() const { return _lb; }\n\n"); + fprintf(fp_hpp, " uint upperBound() const { return _ub; }\n\n"); + fprintf(fp_hpp, " bool multiple() const { return _multiple; }\n\n"); + fprintf(fp_hpp, " Pipeline_Use_Cycle_Mask mask() const { return _mask; }\n\n"); + fprintf(fp_hpp, " bool overlaps(const Pipeline_Use_Element &in2) const {\n"); + fprintf(fp_hpp, " return ((_used & in2._used) != 0 && _mask.overlaps(in2._mask));\n"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " void step(uint cycles) {\n"); + fprintf(fp_hpp, " _used = 0;\n"); + fprintf(fp_hpp, " _mask <<= cycles;\n"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " friend class Pipeline_Use;\n"); + fprintf(fp_hpp, "};\n\n"); + + fprintf(fp_hpp, "// Pipeline_Use Class\n"); + fprintf(fp_hpp, "class Pipeline_Use {\n"); + fprintf(fp_hpp, "protected:\n"); + fprintf(fp_hpp, " // These resources can be used\n"); + fprintf(fp_hpp, " uint _resources_used;\n\n"); + fprintf(fp_hpp, " // These resources are used; excludes multiple choice functional units\n"); + fprintf(fp_hpp, " uint _resources_used_exclusively;\n\n"); + fprintf(fp_hpp, " // Number of elements\n"); + fprintf(fp_hpp, " uint _count;\n\n"); + fprintf(fp_hpp, " // This is the array of Pipeline_Use_Elements\n"); + fprintf(fp_hpp, " Pipeline_Use_Element * _elements;\n\n"); + fprintf(fp_hpp, "public:\n"); + fprintf(fp_hpp, " Pipeline_Use(uint resources_used, uint resources_used_exclusively, uint count, Pipeline_Use_Element *elements)\n"); + fprintf(fp_hpp, " : _resources_used(resources_used)\n"); + fprintf(fp_hpp, " , _resources_used_exclusively(resources_used_exclusively)\n"); + fprintf(fp_hpp, " , _count(count)\n"); + fprintf(fp_hpp, " , _elements(elements)\n"); + fprintf(fp_hpp, " {}\n\n"); + fprintf(fp_hpp, " uint resourcesUsed() const { return _resources_used; }\n\n"); + fprintf(fp_hpp, " uint resourcesUsedExclusively() const { return _resources_used_exclusively; }\n\n"); + fprintf(fp_hpp, " uint count() const { return _count; }\n\n"); + fprintf(fp_hpp, " Pipeline_Use_Element * element(uint i) const { return &_elements[i]; }\n\n"); + fprintf(fp_hpp, " uint full_latency(uint delay, const Pipeline_Use &pred) const;\n\n"); + fprintf(fp_hpp, " void add_usage(const Pipeline_Use &pred);\n\n"); + fprintf(fp_hpp, " void reset() {\n"); + fprintf(fp_hpp, " _resources_used = _resources_used_exclusively = 0;\n"); + fprintf(fp_hpp, " };\n\n"); + fprintf(fp_hpp, " void step(uint cycles) {\n"); + fprintf(fp_hpp, " reset();\n"); + fprintf(fp_hpp, " for (uint i = 0; i < %d; i++)\n", + rescount); + fprintf(fp_hpp, " (&_elements[i])->step(cycles);\n"); + fprintf(fp_hpp, " };\n\n"); + fprintf(fp_hpp, " static const Pipeline_Use elaborated_use;\n"); + fprintf(fp_hpp, " static const Pipeline_Use_Element elaborated_elements[%d];\n\n", + rescount); + fprintf(fp_hpp, " friend class Pipeline;\n"); + fprintf(fp_hpp, "};\n\n"); + + fprintf(fp_hpp, "// Pipeline Class\n"); + fprintf(fp_hpp, "class Pipeline {\n"); + fprintf(fp_hpp, "public:\n"); + + fprintf(fp_hpp, " static bool enabled() { return %s; }\n\n", + _pipeline ? "true" : "false" ); + + assert( _pipeline->_maxInstrsPerBundle && + ( _pipeline->_instrUnitSize || _pipeline->_bundleUnitSize) && + _pipeline->_instrFetchUnitSize && + _pipeline->_instrFetchUnits, + "unspecified pipeline architecture units"); + + uint unitSize = _pipeline->_instrUnitSize ? _pipeline->_instrUnitSize : _pipeline->_bundleUnitSize; + + fprintf(fp_hpp, " enum {\n"); + fprintf(fp_hpp, " _variable_size_instructions = %d,\n", + _pipeline->_variableSizeInstrs ? 1 : 0); + fprintf(fp_hpp, " _fixed_size_instructions = %d,\n", + _pipeline->_variableSizeInstrs ? 0 : 1); + fprintf(fp_hpp, " _branch_has_delay_slot = %d,\n", + _pipeline->_branchHasDelaySlot ? 1 : 0); + fprintf(fp_hpp, " _max_instrs_per_bundle = %d,\n", + _pipeline->_maxInstrsPerBundle); + fprintf(fp_hpp, " _max_bundles_per_cycle = %d,\n", + _pipeline->_maxBundlesPerCycle); + fprintf(fp_hpp, " _max_instrs_per_cycle = %d\n", + _pipeline->_maxBundlesPerCycle * _pipeline->_maxInstrsPerBundle); + fprintf(fp_hpp, " };\n\n"); + + fprintf(fp_hpp, " static bool instr_has_unit_size() { return %s; }\n\n", + _pipeline->_instrUnitSize != 0 ? "true" : "false" ); + if( _pipeline->_bundleUnitSize != 0 ) + if( _pipeline->_instrUnitSize != 0 ) + fprintf(fp_hpp, "// Individual Instructions may be bundled together by the hardware\n\n"); + else + fprintf(fp_hpp, "// Instructions exist only in bundles\n\n"); + else + fprintf(fp_hpp, "// Bundling is not supported\n\n"); + if( _pipeline->_instrUnitSize != 0 ) + fprintf(fp_hpp, " // Size of an instruction\n"); + else + fprintf(fp_hpp, " // Size of an individual instruction does not exist - unsupported\n"); + fprintf(fp_hpp, " static uint instr_unit_size() {"); + if( _pipeline->_instrUnitSize == 0 ) + fprintf(fp_hpp, " assert( false, \"Instructions are only in bundles\" );"); + fprintf(fp_hpp, " return %d; };\n\n", _pipeline->_instrUnitSize); + + if( _pipeline->_bundleUnitSize != 0 ) + fprintf(fp_hpp, " // Size of a bundle\n"); + else + fprintf(fp_hpp, " // Bundles do not exist - unsupported\n"); + fprintf(fp_hpp, " static uint bundle_unit_size() {"); + if( _pipeline->_bundleUnitSize == 0 ) + fprintf(fp_hpp, " assert( false, \"Bundles are not supported\" );"); + fprintf(fp_hpp, " return %d; };\n\n", _pipeline->_bundleUnitSize); + + fprintf(fp_hpp, " static bool requires_bundling() { return %s; }\n\n", + _pipeline->_bundleUnitSize != 0 && _pipeline->_instrUnitSize == 0 ? "true" : "false" ); + + fprintf(fp_hpp, "private:\n"); + fprintf(fp_hpp, " Pipeline(); // Not a legal constructor\n"); + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, " const unsigned char _read_stage_count;\n"); + fprintf(fp_hpp, " const unsigned char _write_stage;\n"); + fprintf(fp_hpp, " const unsigned char _fixed_latency;\n"); + fprintf(fp_hpp, " const unsigned char _instruction_count;\n"); + fprintf(fp_hpp, " const bool _has_fixed_latency;\n"); + fprintf(fp_hpp, " const bool _has_branch_delay;\n"); + fprintf(fp_hpp, " const bool _has_multiple_bundles;\n"); + fprintf(fp_hpp, " const bool _force_serialization;\n"); + fprintf(fp_hpp, " const bool _may_have_no_code;\n"); + fprintf(fp_hpp, " const enum machPipelineStages * const _read_stages;\n"); + fprintf(fp_hpp, " const enum machPipelineStages * const _resource_stage;\n"); + fprintf(fp_hpp, " const uint * const _resource_cycles;\n"); + fprintf(fp_hpp, " const Pipeline_Use _resource_use;\n"); + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, "public:\n"); + fprintf(fp_hpp, " Pipeline(uint write_stage,\n"); + fprintf(fp_hpp, " uint count,\n"); + fprintf(fp_hpp, " bool has_fixed_latency,\n"); + fprintf(fp_hpp, " uint fixed_latency,\n"); + fprintf(fp_hpp, " uint instruction_count,\n"); + fprintf(fp_hpp, " bool has_branch_delay,\n"); + fprintf(fp_hpp, " bool has_multiple_bundles,\n"); + fprintf(fp_hpp, " bool force_serialization,\n"); + fprintf(fp_hpp, " bool may_have_no_code,\n"); + fprintf(fp_hpp, " enum machPipelineStages * const dst,\n"); + fprintf(fp_hpp, " enum machPipelineStages * const stage,\n"); + fprintf(fp_hpp, " uint * const cycles,\n"); + fprintf(fp_hpp, " Pipeline_Use resource_use)\n"); + fprintf(fp_hpp, " : _write_stage(write_stage)\n"); + fprintf(fp_hpp, " , _read_stage_count(count)\n"); + fprintf(fp_hpp, " , _has_fixed_latency(has_fixed_latency)\n"); + fprintf(fp_hpp, " , _fixed_latency(fixed_latency)\n"); + fprintf(fp_hpp, " , _read_stages(dst)\n"); + fprintf(fp_hpp, " , _resource_stage(stage)\n"); + fprintf(fp_hpp, " , _resource_cycles(cycles)\n"); + fprintf(fp_hpp, " , _resource_use(resource_use)\n"); + fprintf(fp_hpp, " , _instruction_count(instruction_count)\n"); + fprintf(fp_hpp, " , _has_branch_delay(has_branch_delay)\n"); + fprintf(fp_hpp, " , _has_multiple_bundles(has_multiple_bundles)\n"); + fprintf(fp_hpp, " , _force_serialization(force_serialization)\n"); + fprintf(fp_hpp, " , _may_have_no_code(may_have_no_code)\n"); + fprintf(fp_hpp, " {};\n"); + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, " uint writeStage() const {\n"); + fprintf(fp_hpp, " return (_write_stage);\n"); + fprintf(fp_hpp, " }\n"); + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, " enum machPipelineStages readStage(int ndx) const {\n"); + fprintf(fp_hpp, " return (ndx < _read_stage_count ? _read_stages[ndx] : stage_undefined);"); + fprintf(fp_hpp, " }\n\n"); + fprintf(fp_hpp, " uint resourcesUsed() const {\n"); + fprintf(fp_hpp, " return _resource_use.resourcesUsed();\n }\n\n"); + fprintf(fp_hpp, " uint resourcesUsedExclusively() const {\n"); + fprintf(fp_hpp, " return _resource_use.resourcesUsedExclusively();\n }\n\n"); + fprintf(fp_hpp, " bool hasFixedLatency() const {\n"); + fprintf(fp_hpp, " return (_has_fixed_latency);\n }\n\n"); + fprintf(fp_hpp, " uint fixedLatency() const {\n"); + fprintf(fp_hpp, " return (_fixed_latency);\n }\n\n"); + fprintf(fp_hpp, " uint functional_unit_latency(uint start, const Pipeline *pred) const;\n\n"); + fprintf(fp_hpp, " uint operand_latency(uint opnd, const Pipeline *pred) const;\n\n"); + fprintf(fp_hpp, " const Pipeline_Use& resourceUse() const {\n"); + fprintf(fp_hpp, " return (_resource_use); }\n\n"); + fprintf(fp_hpp, " const Pipeline_Use_Element * resourceUseElement(uint i) const {\n"); + fprintf(fp_hpp, " return (&_resource_use._elements[i]); }\n\n"); + fprintf(fp_hpp, " uint resourceUseCount() const {\n"); + fprintf(fp_hpp, " return (_resource_use._count); }\n\n"); + fprintf(fp_hpp, " uint instructionCount() const {\n"); + fprintf(fp_hpp, " return (_instruction_count); }\n\n"); + fprintf(fp_hpp, " bool hasBranchDelay() const {\n"); + fprintf(fp_hpp, " return (_has_branch_delay); }\n\n"); + fprintf(fp_hpp, " bool hasMultipleBundles() const {\n"); + fprintf(fp_hpp, " return (_has_multiple_bundles); }\n\n"); + fprintf(fp_hpp, " bool forceSerialization() const {\n"); + fprintf(fp_hpp, " return (_force_serialization); }\n\n"); + fprintf(fp_hpp, " bool mayHaveNoCode() const {\n"); + fprintf(fp_hpp, " return (_may_have_no_code); }\n\n"); + fprintf(fp_hpp, "//const Pipeline_Use_Cycle_Mask& resourceUseMask(int resource) const {\n"); + fprintf(fp_hpp, "// return (_resource_use_masks[resource]); }\n\n"); + fprintf(fp_hpp, "\n#ifndef PRODUCT\n"); + fprintf(fp_hpp, " static const char * stageName(uint i);\n"); + fprintf(fp_hpp, "#endif\n"); + fprintf(fp_hpp, "};\n\n"); + + fprintf(fp_hpp, "// Bundle class\n"); + fprintf(fp_hpp, "class Bundle {\n"); + + uint mshift = 0; + for (uint msize = _pipeline->_maxInstrsPerBundle * _pipeline->_maxBundlesPerCycle; msize != 0; msize >>= 1) + mshift++; + + uint rshift = rescount; + + fprintf(fp_hpp, "protected:\n"); + fprintf(fp_hpp, " enum {\n"); + fprintf(fp_hpp, " _unused_delay = 0x%x,\n", 0); + fprintf(fp_hpp, " _use_nop_delay = 0x%x,\n", 1); + fprintf(fp_hpp, " _use_unconditional_delay = 0x%x,\n", 2); + fprintf(fp_hpp, " _use_conditional_delay = 0x%x,\n", 3); + fprintf(fp_hpp, " _used_in_conditional_delay = 0x%x,\n", 4); + fprintf(fp_hpp, " _used_in_unconditional_delay = 0x%x,\n", 5); + fprintf(fp_hpp, " _used_in_all_conditional_delays = 0x%x,\n", 6); + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, " _use_delay = 0x%x,\n", 3); + fprintf(fp_hpp, " _used_in_delay = 0x%x\n", 4); + fprintf(fp_hpp, " };\n\n"); + fprintf(fp_hpp, " uint _flags : 3,\n"); + fprintf(fp_hpp, " _starts_bundle : 1,\n"); + fprintf(fp_hpp, " _instr_count : %d,\n", mshift); + fprintf(fp_hpp, " _resources_used : %d;\n", rshift); + fprintf(fp_hpp, "public:\n"); + fprintf(fp_hpp, " Bundle() : _flags(_unused_delay), _starts_bundle(0), _instr_count(0), _resources_used(0) {}\n\n"); + fprintf(fp_hpp, " void set_instr_count(uint i) { _instr_count = i; }\n"); + fprintf(fp_hpp, " void set_resources_used(uint i) { _resources_used = i; }\n"); + fprintf(fp_hpp, " void clear_usage() { _flags = _unused_delay; }\n"); + fprintf(fp_hpp, " void set_starts_bundle() { _starts_bundle = true; }\n"); + + fprintf(fp_hpp, " uint flags() const { return (_flags); }\n"); + fprintf(fp_hpp, " uint instr_count() const { return (_instr_count); }\n"); + fprintf(fp_hpp, " uint resources_used() const { return (_resources_used); }\n"); + fprintf(fp_hpp, " bool starts_bundle() const { return (_starts_bundle != 0); }\n"); + + fprintf(fp_hpp, " void set_use_nop_delay() { _flags = _use_nop_delay; }\n"); + fprintf(fp_hpp, " void set_use_unconditional_delay() { _flags = _use_unconditional_delay; }\n"); + fprintf(fp_hpp, " void set_use_conditional_delay() { _flags = _use_conditional_delay; }\n"); + fprintf(fp_hpp, " void set_used_in_unconditional_delay() { _flags = _used_in_unconditional_delay; }\n"); + fprintf(fp_hpp, " void set_used_in_conditional_delay() { _flags = _used_in_conditional_delay; }\n"); + fprintf(fp_hpp, " void set_used_in_all_conditional_delays() { _flags = _used_in_all_conditional_delays; }\n"); + + fprintf(fp_hpp, " bool use_nop_delay() { return (_flags == _use_nop_delay); }\n"); + fprintf(fp_hpp, " bool use_unconditional_delay() { return (_flags == _use_unconditional_delay); }\n"); + fprintf(fp_hpp, " bool use_conditional_delay() { return (_flags == _use_conditional_delay); }\n"); + fprintf(fp_hpp, " bool used_in_unconditional_delay() { return (_flags == _used_in_unconditional_delay); }\n"); + fprintf(fp_hpp, " bool used_in_conditional_delay() { return (_flags == _used_in_conditional_delay); }\n"); + fprintf(fp_hpp, " bool used_in_all_conditional_delays() { return (_flags == _used_in_all_conditional_delays); }\n"); + fprintf(fp_hpp, " bool use_delay() { return ((_flags & _use_delay) != 0); }\n"); + fprintf(fp_hpp, " bool used_in_delay() { return ((_flags & _used_in_delay) != 0); }\n\n"); + + fprintf(fp_hpp, " enum {\n"); + fprintf(fp_hpp, " _nop_count = %d\n", + _pipeline->_nopcnt); + fprintf(fp_hpp, " };\n\n"); + fprintf(fp_hpp, " static void initialize_nops(MachNode *nop_list[%d], Compile* C);\n\n", + _pipeline->_nopcnt); + fprintf(fp_hpp, "#ifndef PRODUCT\n"); + fprintf(fp_hpp, " void dump() const;\n"); + fprintf(fp_hpp, "#endif\n"); + fprintf(fp_hpp, "};\n\n"); + +// const char *classname; +// for (_pipeline->_classlist.reset(); (classname = _pipeline->_classlist.iter()) != NULL; ) { +// PipeClassForm *pipeclass = _pipeline->_classdict[classname]->is_pipeclass(); +// fprintf(fp_hpp, "// Pipeline Class Instance for \"%s\"\n", classname); +// } +} + +//------------------------------declareClasses--------------------------------- +// Construct the class hierarchy of MachNode classes from the instruction & +// operand lists +void ArchDesc::declareClasses(FILE *fp) { + + // Declare an array containing the machine register names, strings. + declareRegNames(fp, _register); + + // Declare an array containing the machine register encoding values + declareRegEncodes(fp, _register); + + // Generate declarations for the total number of operands + fprintf(fp,"\n"); + fprintf(fp,"// Total number of operands defined in architecture definition\n"); + int num_operands = 0; + OperandForm *op; + for (_operands.reset(); (op = (OperandForm*)_operands.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if (op->ideal_only()) continue; + + ++num_operands; + } + int first_operand_class = num_operands; + OpClassForm *opc; + for (_opclass.reset(); (opc = (OpClassForm*)_opclass.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if (opc->ideal_only()) continue; + + ++num_operands; + } + fprintf(fp,"#define FIRST_OPERAND_CLASS %d\n", first_operand_class); + fprintf(fp,"#define NUM_OPERANDS %d\n", num_operands); + fprintf(fp,"\n"); + // Generate declarations for the total number of instructions + fprintf(fp,"// Total number of instructions defined in architecture definition\n"); + fprintf(fp,"#define NUM_INSTRUCTIONS %d\n",instructFormCount()); + + + // Generate Machine Classes for each operand defined in AD file + fprintf(fp,"\n"); + fprintf(fp,"//----------------------------Declare classes derived from MachOper----------\n"); + // Iterate through all operands + _operands.reset(); + OperandForm *oper; + for( ; (oper = (OperandForm*)_operands.iter()) != NULL;) { + // Ensure this is a machine-world instruction + if (oper->ideal_only() ) continue; + // The declaration of labelOper is in machine-independent file: machnode + if ( strcmp(oper->_ident,"label") == 0 ) continue; + // The declaration of methodOper is in machine-independent file: machnode + if ( strcmp(oper->_ident,"method") == 0 ) continue; + + // Build class definition for this operand + fprintf(fp,"\n"); + fprintf(fp,"class %sOper : public MachOper { \n",oper->_ident); + fprintf(fp,"private:\n"); + // Operand definitions that depend upon number of input edges + { + uint num_edges = oper->num_edges(_globalNames); + if( num_edges != 1 ) { // Use MachOper::num_edges() {return 1;} + fprintf(fp," virtual uint num_edges() const { return %d; }\n", + num_edges ); + } + if( num_edges > 0 ) { + in_RegMask(fp); + } + } + + // Support storing constants inside the MachOper + declareConstStorage(fp,_globalNames,oper); + + // Support storage of the condition codes + if( oper->is_ideal_bool() ) { + fprintf(fp," virtual int ccode() const { \n"); + fprintf(fp," switch (_c0) {\n"); + fprintf(fp," case BoolTest::eq : return equal();\n"); + fprintf(fp," case BoolTest::gt : return greater();\n"); + fprintf(fp," case BoolTest::lt : return less();\n"); + fprintf(fp," case BoolTest::ne : return not_equal();\n"); + fprintf(fp," case BoolTest::le : return less_equal();\n"); + fprintf(fp," case BoolTest::ge : return greater_equal();\n"); + fprintf(fp," default : ShouldNotReachHere(); return 0;\n"); + fprintf(fp," }\n"); + fprintf(fp," };\n"); + } + + // Support storage of the condition codes + if( oper->is_ideal_bool() ) { + fprintf(fp," virtual void negate() { \n"); + fprintf(fp," _c0 = (BoolTest::mask)((int)_c0^0x4); \n"); + fprintf(fp," };\n"); + } + + // Declare constructor. + // Parameters start with condition code, then all other constants + // + // (1) MachXOper(int32 ccode, int32 c0, int32 c1, ..., int32 cn) + // (2) : _ccode(ccode), _c0(c0), _c1(c1), ..., _cn(cn) { } + // + Form::DataType constant_type = oper->simple_type(_globalNames); + defineConstructor(fp, oper->_ident, oper->num_consts(_globalNames), + oper->_components, oper->is_ideal_bool(), + constant_type, _globalNames); + + // Clone function + fprintf(fp," virtual MachOper *clone(Compile* C) const;\n"); + + // Support setting a spill offset into a constant operand. + // We only support setting an 'int' offset, while in the + // LP64 build spill offsets are added with an AddP which + // requires a long constant. Thus we don't support spilling + // in frames larger than 4Gig. + if( oper->has_conI(_globalNames) || + oper->has_conL(_globalNames) ) + fprintf(fp, " virtual void set_con( jint c0 ) { _c0 = c0; }\n"); + + // virtual functions for encoding and format + // fprintf(fp," virtual void encode() const {\n %s }\n", + // (oper->_encrule)?(oper->_encrule->_encrule):""); + // Check the interface type, and generate the correct query functions + // encoding queries based upon MEMORY_INTER, REG_INTER, CONST_INTER. + + fprintf(fp," virtual uint opcode() const { return %s; }\n", + machOperEnum(oper->_ident)); + + // virtual function to look up ideal return type of machine instruction + // + // (1) virtual const Type *type() const { return .....; } + // + if ((oper->_matrule) && (oper->_matrule->_lChild == NULL) && + (oper->_matrule->_rChild == NULL)) { + unsigned int position = 0; + const char *opret, *opname, *optype; + oper->_matrule->base_operand(position,_globalNames,opret,opname,optype); + fprintf(fp," virtual const Type *type() const {"); + const char *type = getIdealType(optype); + if( type != NULL ) { + Form::DataType data_type = oper->is_base_constant(_globalNames); + // Check if we are an ideal pointer type + if( data_type == Form::idealP ) { + // Return the ideal type we already have: + fprintf(fp," return _c0;"); + } else { + // Return the appropriate bottom type + fprintf(fp," return %s;", getIdealType(optype)); + } + } else { + fprintf(fp," ShouldNotCallThis(); return Type::BOTTOM;"); + } + fprintf(fp," }\n"); + } else { + // Check for user-defined stack slots, based upon sRegX + Form::DataType data_type = oper->is_user_name_for_sReg(); + if( data_type != Form::none ){ + const char *type = NULL; + switch( data_type ) { + case Form::idealI: type = "TypeInt::INT"; break; + case Form::idealP: type = "TypePtr::BOTTOM";break; + case Form::idealF: type = "Type::FLOAT"; break; + case Form::idealD: type = "Type::DOUBLE"; break; + case Form::idealL: type = "TypeLong::LONG"; break; + case Form::none: // fall through + default: + assert( false, "No support for this type of stackSlot"); + } + fprintf(fp," virtual const Type *type() const { return %s; } // stackSlotX\n", type); + } + } + + + // + // virtual functions for defining the encoding interface. + // + // Access the linearized ideal register mask, + // map to physical register encoding + if ( oper->_matrule && oper->_matrule->is_base_register(_globalNames) ) { + // Just use the default virtual 'reg' call + } else if ( oper->ideal_to_sReg_type(oper->_ident) != Form::none ) { + // Special handling for operand 'sReg', a Stack Slot Register. + // Map linearized ideal register mask to stack slot number + fprintf(fp," virtual int reg(PhaseRegAlloc *ra_, const Node *node) const {\n"); + fprintf(fp," return (int)OptoReg::reg2stack(ra_->get_reg_first(node));/* sReg */\n"); + fprintf(fp," }\n"); + fprintf(fp," virtual int reg(PhaseRegAlloc *ra_, const Node *node, int idx) const {\n"); + fprintf(fp," return (int)OptoReg::reg2stack(ra_->get_reg_first(node->in(idx)));/* sReg */\n"); + fprintf(fp," }\n"); + } + + // Output the operand specific access functions used by an enc_class + // These are only defined when we want to override the default virtual func + if (oper->_interface != NULL) { + fprintf(fp,"\n"); + // Check if it is a Memory Interface + if ( oper->_interface->is_MemInterface() != NULL ) { + MemInterface *mem_interface = oper->_interface->is_MemInterface(); + const char *base = mem_interface->_base; + if( base != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "base", base); + } + char *index = mem_interface->_index; + if( index != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "index", index); + } + const char *scale = mem_interface->_scale; + if( scale != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "scale", scale); + } + const char *disp = mem_interface->_disp; + if( disp != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "disp", disp); + oper->disp_is_oop(fp, _globalNames); + } + if( oper->stack_slots_only(_globalNames) ) { + // should not call this: + fprintf(fp," virtual int constant_disp() const { return Type::OffsetBot; }"); + } else if ( disp != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "constant_disp", disp); + } + } // end Memory Interface + // Check if it is a Conditional Interface + else if (oper->_interface->is_CondInterface() != NULL) { + CondInterface *cInterface = oper->_interface->is_CondInterface(); + const char *equal = cInterface->_equal; + if( equal != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "equal", equal); + } + const char *not_equal = cInterface->_not_equal; + if( not_equal != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "not_equal", not_equal); + } + const char *less = cInterface->_less; + if( less != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "less", less); + } + const char *greater_equal = cInterface->_greater_equal; + if( greater_equal != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "greater_equal", greater_equal); + } + const char *less_equal = cInterface->_less_equal; + if( less_equal != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "less_equal", less_equal); + } + const char *greater = cInterface->_greater; + if( greater != NULL ) { + define_oper_interface(fp, *oper, _globalNames, "greater", greater); + } + } // end Conditional Interface + // Check if it is a Constant Interface + else if (oper->_interface->is_ConstInterface() != NULL ) { + assert( oper->num_consts(_globalNames) == 1, + "Must have one constant when using CONST_INTER encoding"); + if (!strcmp(oper->ideal_type(_globalNames), "ConI")) { + // Access the locally stored constant + fprintf(fp," virtual intptr_t constant() const {"); + fprintf(fp, " return (intptr_t)_c0;"); + fprintf(fp," }\n"); + } + else if (!strcmp(oper->ideal_type(_globalNames), "ConP")) { + // Access the locally stored constant + fprintf(fp," virtual intptr_t constant() const {"); + fprintf(fp, " return _c0->get_con();"); + fprintf(fp, " }\n"); + // Generate query to determine if this pointer is an oop + fprintf(fp," virtual bool constant_is_oop() const {"); + fprintf(fp, " return _c0->isa_oop_ptr();"); + fprintf(fp, " }\n"); + } + else if (!strcmp(oper->ideal_type(_globalNames), "ConL")) { + fprintf(fp," virtual intptr_t constant() const {"); + // We don't support addressing modes with > 4Gig offsets. + // Truncate to int. + fprintf(fp, " return (intptr_t)_c0;"); + fprintf(fp, " }\n"); + fprintf(fp," virtual jlong constantL() const {"); + fprintf(fp, " return _c0;"); + fprintf(fp, " }\n"); + } + else if (!strcmp(oper->ideal_type(_globalNames), "ConF")) { + fprintf(fp," virtual intptr_t constant() const {"); + fprintf(fp, " ShouldNotReachHere(); return 0; "); + fprintf(fp, " }\n"); + fprintf(fp," virtual jfloat constantF() const {"); + fprintf(fp, " return (jfloat)_c0;"); + fprintf(fp, " }\n"); + } + else if (!strcmp(oper->ideal_type(_globalNames), "ConD")) { + fprintf(fp," virtual intptr_t constant() const {"); + fprintf(fp, " ShouldNotReachHere(); return 0; "); + fprintf(fp, " }\n"); + fprintf(fp," virtual jdouble constantD() const {"); + fprintf(fp, " return _c0;"); + fprintf(fp, " }\n"); + } + } + else if (oper->_interface->is_RegInterface() != NULL) { + // make sure that a fixed format string isn't used for an + // operand which might be assiged to multiple registers. + // Otherwise the opto assembly output could be misleading. + if (oper->_format->_strings.count() != 0 && !oper->is_bound_register()) { + syntax_err(oper->_linenum, + "Only bound registers can have fixed formats: %s\n", + oper->_ident); + } + } + else { + assert( false, "ShouldNotReachHere();"); + } + } + + fprintf(fp,"\n"); + // // Currently all XXXOper::hash() methods are identical (990820) + // declare_hash(fp); + // // Currently all XXXOper::Cmp() methods are identical (990820) + // declare_cmp(fp); + + // Do not place dump_spec() and Name() into PRODUCT code + // int_format and ext_format are not needed in PRODUCT code either + fprintf(fp, "#ifndef PRODUCT\n"); + + // Declare int_format() and ext_format() + gen_oper_format(fp, _globalNames, *oper); + + // Machine independent print functionality for debugging + // IF we have constants, create a dump_spec function for the derived class + // + // (1) virtual void dump_spec() const { + // (2) st->print("#%d", _c#); // Constant != ConP + // OR _c#->dump_on(st); // Type ConP + // ... + // (3) } + uint num_consts = oper->num_consts(_globalNames); + if( num_consts > 0 ) { + // line (1) + fprintf(fp, " virtual void dump_spec(outputStream *st) const {\n"); + // generate format string for st->print + // Iterate over the component list & spit out the right thing + uint i = 0; + const char *type = oper->ideal_type(_globalNames); + Component *comp; + oper->_components.reset(); + if ((comp = oper->_components.iter()) == NULL) { + assert(num_consts == 1, "Bad component list detected.\n"); + i = dump_spec_constant( fp, type, i ); + // Check that type actually matched + assert( i != 0, "Non-constant operand lacks component list."); + } // end if NULL + else { + // line (2) + // dump all components + oper->_components.reset(); + while((comp = oper->_components.iter()) != NULL) { + type = comp->base_type(_globalNames); + i = dump_spec_constant( fp, type, i ); + } + } + // finish line (3) + fprintf(fp," }\n"); + } + + fprintf(fp," virtual const char *Name() const { return \"%s\";}\n", + oper->_ident); + + fprintf(fp,"#endif\n"); + + // Close definition of this XxxMachOper + fprintf(fp,"};\n"); + } + + + // Generate Machine Classes for each instruction defined in AD file + fprintf(fp,"\n"); + fprintf(fp,"//----------------------------Declare classes for Pipelines-----------------\n"); + declare_pipe_classes(fp); + + // Generate Machine Classes for each instruction defined in AD file + fprintf(fp,"\n"); + fprintf(fp,"//----------------------------Declare classes derived from MachNode----------\n"); + _instructions.reset(); + InstructForm *instr; + for( ; (instr = (InstructForm*)_instructions.iter()) != NULL; ) { + // Ensure this is a machine-world instruction + if ( instr->ideal_only() ) continue; + + // Build class definition for this instruction + fprintf(fp,"\n"); + fprintf(fp,"class %sNode : public %s { \n", + instr->_ident, instr->mach_base_class() ); + fprintf(fp,"private:\n"); + fprintf(fp," MachOper *_opnd_array[%d];\n", instr->num_opnds() ); + if ( instr->is_ideal_jump() ) { + fprintf(fp, " GrowableArray _index2label;\n"); + } + fprintf(fp,"public:\n"); + fprintf(fp," MachOper *opnd_array(uint operand_index) const { assert(operand_index < _num_opnds, \"invalid _opnd_array index\"); return _opnd_array[operand_index]; }\n"); + fprintf(fp," void set_opnd_array(uint operand_index, MachOper *operand) { assert(operand_index < _num_opnds, \"invalid _opnd_array index\"); _opnd_array[operand_index] = operand; }\n"); + fprintf(fp,"private:\n"); + if ( instr->is_ideal_jump() ) { + fprintf(fp," virtual void add_case_label(int index_num, Label* blockLabel) {\n"); + fprintf(fp," _index2label.at_put_grow(index_num, blockLabel);}\n"); + } + if( can_cisc_spill() && (instr->cisc_spill_alternate() != NULL) ) { + fprintf(fp," const RegMask *_cisc_RegMask;\n"); + } + + out_RegMask(fp); // output register mask + fprintf(fp," virtual uint rule() const { return %s_rule; }\n", + instr->_ident); + + // If this instruction contains a labelOper + // Declare Node::methods that set operand Label's contents + int label_position = instr->label_position(); + if( label_position != -1 ) { + // Set the label, stored in labelOper::_branch_label + fprintf(fp," virtual void label_set( Label& label, uint block_num );\n"); + } + + // If this instruction contains a methodOper + // Declare Node::methods that set operand method's contents + int method_position = instr->method_position(); + if( method_position != -1 ) { + // Set the address method, stored in methodOper::_method + fprintf(fp," virtual void method_set( intptr_t method );\n"); + } + + // virtual functions for attributes + // + // Each instruction attribute results in a virtual call of same name. + // The ins_cost is not handled here. + Attribute *attr = instr->_attribs; + bool is_pc_relative = false; + while (attr != NULL) { + if (strcmp(attr->_ident,"ins_cost") && + strcmp(attr->_ident,"ins_pc_relative")) { + fprintf(fp," int %s() const { return %s; }\n", + attr->_ident, attr->_val); + } + // Check value for ins_pc_relative, and if it is true (1), set the flag + if (!strcmp(attr->_ident,"ins_pc_relative") && attr->int_val(*this) != 0) + is_pc_relative = true; + attr = (Attribute *)attr->_next; + } + + // virtual functions for encode and format + // + // Output the opcode function and the encode function here using the + // encoding class information in the _insencode slot. + if ( instr->_insencode ) { + fprintf(fp," virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const;\n"); + } + + // virtual function for getting the size of an instruction + if ( instr->_size ) { + fprintf(fp," virtual uint size(PhaseRegAlloc *ra_) const;\n"); + } + + // Return the top-level ideal opcode. + // Use MachNode::ideal_Opcode() for nodes based on MachNode class + // if the ideal_Opcode == Op_Node. + if ( strcmp("Node", instr->ideal_Opcode(_globalNames)) != 0 || + strcmp("MachNode", instr->mach_base_class()) != 0 ) { + fprintf(fp," virtual int ideal_Opcode() const { return Op_%s; }\n", + instr->ideal_Opcode(_globalNames) ); + } + + // Allow machine-independent optimization, invert the sense of the IF test + if( instr->is_ideal_if() ) { + fprintf(fp," virtual void negate() { \n"); + // Identify which operand contains the negate(able) ideal condition code + int idx = 0; + instr->_components.reset(); + for( Component *comp; (comp = instr->_components.iter()) != NULL; ) { + // Check that component is an operand + Form *form = (Form*)_globalNames[comp->_type]; + OperandForm *opForm = form ? form->is_operand() : NULL; + if( opForm == NULL ) continue; + + // Lookup the position of the operand in the instruction. + if( opForm->is_ideal_bool() ) { + idx = instr->operand_position(comp->_name, comp->_usedef); + assert( idx != NameList::Not_in_list, "Did not find component in list that contained it."); + break; + } + } + fprintf(fp," opnd_array(%d)->negate();\n", idx); + fprintf(fp," _prob = 1.0f - _prob;\n"); + fprintf(fp," };\n"); + } + + + // Identify which input register matches the input register. + uint matching_input = instr->two_address(_globalNames); + + // Generate the method if it returns != 0 otherwise use MachNode::two_adr() + if( matching_input != 0 ) { + fprintf(fp," virtual uint two_adr() const "); + fprintf(fp,"{ return oper_input_base()"); + for( uint i = 2; i <= matching_input; i++ ) + fprintf(fp," + opnd_array(%d)->num_edges()",i-1); + fprintf(fp,"; }\n"); + } + + // Declare cisc_version, if applicable + // MachNode *cisc_version( int offset /* ,... */ ); + instr->declare_cisc_version(*this, fp); + + // If there is an explicit peephole rule, build it + if ( instr->peepholes() != NULL ) { + fprintf(fp," virtual MachNode *peephole(Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted, Compile *C);\n"); + } + + // Output the declaration for number of relocation entries + if ( instr->reloc(_globalNames) != 0 ) { + fprintf(fp," virtual int reloc() const;\n"); + } + + if (instr->alignment() != 1) { + fprintf(fp," virtual int alignment_required() const { return %d; }\n", instr->alignment()); + fprintf(fp," virtual int compute_padding(int current_offset) const;\n"); + } + + // Starting point for inputs matcher wants. + // Use MachNode::oper_input_base() for nodes based on MachNode class + // if the base == 1. + if ( instr->oper_input_base(_globalNames) != 1 || + strcmp("MachNode", instr->mach_base_class()) != 0 ) { + fprintf(fp," virtual uint oper_input_base() const { return %d; }\n", + instr->oper_input_base(_globalNames)); + } + + // Make the constructor and following methods 'public:' + fprintf(fp,"public:\n"); + + // Constructor + if ( instr->is_ideal_jump() ) { + fprintf(fp," %sNode() : _index2label(MinJumpTableSize*2) { ", instr->_ident); + } else { + fprintf(fp," %sNode() { ", instr->_ident); + if( can_cisc_spill() && (instr->cisc_spill_alternate() != NULL) ) { + fprintf(fp,"_cisc_RegMask = NULL; "); + } + } + + fprintf(fp," _num_opnds = %d; _opnds = _opnd_array; ", instr->num_opnds()); + + bool node_flags_set = false; + // flag: if this instruction matches an ideal 'Goto' node + if ( instr->is_ideal_goto() ) { + fprintf(fp,"init_flags(Flag_is_Goto"); + node_flags_set = true; + } + + // flag: if this instruction matches an ideal 'Copy*' node + if ( instr->is_ideal_copy() != 0 ) { + if ( node_flags_set ) { + fprintf(fp," | Flag_is_Copy"); + } else { + fprintf(fp,"init_flags(Flag_is_Copy"); + node_flags_set = true; + } + } + + // Is an instruction is a constant? If so, get its type + Form::DataType data_type; + const char *opType = NULL; + const char *result = NULL; + data_type = instr->is_chain_of_constant(_globalNames, opType, result); + // Check if this instruction is a constant + if ( data_type != Form::none ) { + if ( node_flags_set ) { + fprintf(fp," | Flag_is_Con"); + } else { + fprintf(fp,"init_flags(Flag_is_Con"); + node_flags_set = true; + } + } + + // flag: if instruction matches 'If' | 'Goto' | 'CountedLoopEnd | 'Jump' + if ( instr->is_ideal_branch() ) { + if ( node_flags_set ) { + fprintf(fp," | Flag_is_Branch"); + } else { + fprintf(fp,"init_flags(Flag_is_Branch"); + node_flags_set = true; + } + } + + // flag: if this instruction is cisc alternate + if ( can_cisc_spill() && instr->is_cisc_alternate() ) { + if ( node_flags_set ) { + fprintf(fp," | Flag_is_cisc_alternate"); + } else { + fprintf(fp,"init_flags(Flag_is_cisc_alternate"); + node_flags_set = true; + } + } + + // flag: if this instruction is pc relative + if ( is_pc_relative ) { + if ( node_flags_set ) { + fprintf(fp," | Flag_is_pc_relative"); + } else { + fprintf(fp,"init_flags(Flag_is_pc_relative"); + node_flags_set = true; + } + } + + // flag: if this instruction has short branch form + if ( instr->has_short_branch_form() ) { + if ( node_flags_set ) { + fprintf(fp," | Flag_may_be_short_branch"); + } else { + fprintf(fp,"init_flags(Flag_may_be_short_branch"); + node_flags_set = true; + } + } + + // Check if machine instructions that USE memory, but do not DEF memory, + // depend upon a node that defines memory in machine-independent graph. + if ( instr->needs_anti_dependence_check(_globalNames) ) { + if ( node_flags_set ) { + fprintf(fp," | Flag_needs_anti_dependence_check"); + } else { + fprintf(fp,"init_flags(Flag_needs_anti_dependence_check"); + node_flags_set = true; + } + } + + if ( node_flags_set ) { + fprintf(fp,"); "); + } + + if (instr->is_ideal_unlock() || instr->is_ideal_call_leaf()) { + fprintf(fp,"clear_flag(Flag_is_safepoint_node); "); + } + + fprintf(fp,"}\n"); + + // size_of, used by base class's clone to obtain the correct size. + fprintf(fp," virtual uint size_of() const {"); + fprintf(fp, " return sizeof(%sNode);", instr->_ident); + fprintf(fp, " }\n"); + + // Virtual methods which are only generated to override base class + if( instr->expands() || instr->needs_projections() || + instr->has_temps() || + instr->_matrule != NULL && + instr->num_opnds() != instr->num_unique_opnds() ) { + fprintf(fp," virtual MachNode *Expand(State *state, Node_List &proj_list);\n"); + } + + if (instr->is_pinned(_globalNames)) { + fprintf(fp," virtual bool pinned() const { return "); + if (instr->is_parm(_globalNames)) { + fprintf(fp,"_in[0]->pinned();"); + } else { + fprintf(fp,"true;"); + } + fprintf(fp," }\n"); + } + if (instr->is_projection(_globalNames)) { + fprintf(fp," virtual const Node *is_block_proj() const { return this; }\n"); + } + if ( instr->num_post_match_opnds() != 0 + || instr->is_chain_of_constant(_globalNames) ) { + fprintf(fp," friend MachNode *State::MachNodeGenerator(int opcode, Compile* C);\n"); + } + if ( instr->rematerialize(_globalNames, get_registers()) ) { + fprintf(fp," // Rematerialize %s\n", instr->_ident); + } + + // Declare short branch methods, if applicable + instr->declare_short_branch_methods(fp); + + // Instructions containing a constant that will be entered into the + // float/double table redefine the base virtual function +#ifdef SPARC + // Sparc doubles entries in the constant table require more space for + // alignment. (expires 9/98) + int table_entries = (3 * instr->num_consts( _globalNames, Form::idealD )) + + instr->num_consts( _globalNames, Form::idealF ); +#else + int table_entries = instr->num_consts( _globalNames, Form::idealD ) + + instr->num_consts( _globalNames, Form::idealF ); +#endif + if( table_entries != 0 ) { + fprintf(fp," virtual int const_size() const {"); + fprintf(fp, " return %d;", table_entries); + fprintf(fp, " }\n"); + } + + + // See if there is an "ins_pipe" declaration for this instruction + if (instr->_ins_pipe) { + fprintf(fp," static const Pipeline *pipeline_class();\n"); + fprintf(fp," virtual const Pipeline *pipeline() const;\n"); + } + + // Generate virtual function for MachNodeX::bottom_type when necessary + // + // Note on accuracy: Pointer-types of machine nodes need to be accurate, + // or else alias analysis on the matched graph may produce bad code. + // Moreover, the aliasing decisions made on machine-node graph must be + // no less accurate than those made on the ideal graph, or else the graph + // may fail to schedule. (Reason: Memory ops which are reordered in + // the ideal graph might look interdependent in the machine graph, + // thereby removing degrees of scheduling freedom that the optimizer + // assumed would be available.) + // + // %%% We should handle many of these cases with an explicit ADL clause: + // instruct foo() %{ ... bottom_type(TypeRawPtr::BOTTOM); ... %} + if( data_type != Form::none ) { + // A constant's bottom_type returns a Type containing its constant value + + // !!!!! + // Convert all ints, floats, ... to machine-independent TypeXs + // as is done for pointers + // + // Construct appropriate constant type containing the constant value. + fprintf(fp," virtual const class Type *bottom_type() const{\n"); + switch( data_type ) { + case Form::idealI: + fprintf(fp," return TypeInt::make(opnd_array(1)->constant());\n"); + break; + case Form::idealP: + fprintf(fp," return opnd_array(1)->type();\n",result); + break; + case Form::idealD: + fprintf(fp," return TypeD::make(opnd_array(1)->constantD());\n"); + break; + case Form::idealF: + fprintf(fp," return TypeF::make(opnd_array(1)->constantF());\n"); + break; + case Form::idealL: + fprintf(fp," return TypeLong::make(opnd_array(1)->constantL());\n"); + break; + default: + assert( false, "Unimplemented()" ); + break; + } + fprintf(fp," };\n"); + } +/* else if ( instr->_matrule && instr->_matrule->_rChild && + ( strcmp("ConvF2I",instr->_matrule->_rChild->_opType)==0 + || strcmp("ConvD2I",instr->_matrule->_rChild->_opType)==0 ) ) { + // !!!!! !!!!! + // Provide explicit bottom type for conversions to int + // On Intel the result operand is a stackSlot, untyped. + fprintf(fp," virtual const class Type *bottom_type() const{"); + fprintf(fp, " return TypeInt::INT;"); + fprintf(fp, " };\n"); + }*/ + else if( instr->is_ideal_copy() && + !strcmp(instr->_matrule->_lChild->_opType,"stackSlotP") ) { + // !!!!! + // Special hack for ideal Copy of pointer. Bottom type is oop or not depending on input. + fprintf(fp," const Type *bottom_type() const { return in(1)->bottom_type(); } // Copy?\n"); + } + else if( instr->is_ideal_loadPC() ) { + // LoadPCNode provides the return address of a call to native code. + // Define its bottom type to be TypeRawPtr::BOTTOM instead of TypePtr::BOTTOM + // since it is a pointer to an internal VM location and must have a zero offset. + // Allocation detects derived pointers, in part, by their non-zero offsets. + fprintf(fp," const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } // LoadPC?\n"); + } + else if( instr->is_ideal_box() ) { + // BoxNode provides the address of a stack slot. + // Define its bottom type to be TypeRawPtr::BOTTOM instead of TypePtr::BOTTOM + // This prevent s insert_anti_dependencies from complaining. It will + // complain if it see that the pointer base is TypePtr::BOTTOM since + // it doesn't understand what that might alias. + fprintf(fp," const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } // Box?\n"); + } + else if( instr->_matrule && instr->_matrule->_rChild && !strcmp(instr->_matrule->_rChild->_opType,"CMoveP") ) { + int offset = 1; + // Special special hack to see if the Cmp? has been incorporated in the conditional move + MatchNode *rl = instr->_matrule->_rChild->_lChild; + if( rl && !strcmp(rl->_opType, "Binary") ) { + MatchNode *rlr = rl->_rChild; + if (rlr && strncmp(rlr->_opType, "Cmp", 3) == 0) + offset = 2; + } + // Special hack for ideal CMoveP; ideal type depends on inputs + fprintf(fp," const Type *bottom_type() const { const Type *t = in(oper_input_base()+%d)->bottom_type(); return (req() <= oper_input_base()+%d) ? t : t->meet(in(oper_input_base()+%d)->bottom_type()); } // CMoveP\n", + offset, offset+1, offset+1); + } + else if( instr->needs_base_oop_edge(_globalNames) ) { + // Special hack for ideal AddP. Bottom type is an oop IFF it has a + // legal base-pointer input. Otherwise it is NOT an oop. + fprintf(fp," const Type *bottom_type() const { return AddPNode::mach_bottom_type(this); } // AddP\n"); + } + else if (instr->is_tls_instruction()) { + // Special hack for tlsLoadP + fprintf(fp," const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } // tlsLoadP\n"); + } + else if ( instr->is_ideal_if() ) { + fprintf(fp," const Type *bottom_type() const { return TypeTuple::IFBOTH; } // matched IfNode\n"); + } + else if ( instr->is_ideal_membar() ) { + fprintf(fp," const Type *bottom_type() const { return TypeTuple::MEMBAR; } // matched MemBar\n"); + } + + // Check where 'ideal_type' must be customized + /* + if ( instr->_matrule && instr->_matrule->_rChild && + ( strcmp("ConvF2I",instr->_matrule->_rChild->_opType)==0 + || strcmp("ConvD2I",instr->_matrule->_rChild->_opType)==0 ) ) { + fprintf(fp," virtual uint ideal_reg() const { return Compile::current()->matcher()->base2reg[Type::Int]; }\n"); + }*/ + + // Analyze machine instructions that either USE or DEF memory. + int memory_operand = instr->memory_operand(_globalNames); + // Some guys kill all of memory + if ( instr->is_wide_memory_kill(_globalNames) ) { + memory_operand = InstructForm::MANY_MEMORY_OPERANDS; + } + if ( memory_operand != InstructForm::NO_MEMORY_OPERAND ) { + if( memory_operand == InstructForm::MANY_MEMORY_OPERANDS ) { + fprintf(fp," virtual const TypePtr *adr_type() const;\n"); + } + fprintf(fp," virtual const MachOper *memory_operand() const;\n"); + } + + fprintf(fp, "#ifndef PRODUCT\n"); + + // virtual function for generating the user's assembler output + gen_inst_format(fp, _globalNames,*instr); + + // Machine independent print functionality for debugging + fprintf(fp," virtual const char *Name() const { return \"%s\";}\n", + instr->_ident); + + fprintf(fp, "#endif\n"); + + // Close definition of this XxxMachNode + fprintf(fp,"};\n"); + }; + +} + +void ArchDesc::defineStateClass(FILE *fp) { + static const char *state__valid = "_valid[((uint)index) >> 5] & (0x1 << (((uint)index) & 0x0001F))"; + static const char *state__set_valid= "_valid[((uint)index) >> 5] |= (0x1 << (((uint)index) & 0x0001F))"; + + fprintf(fp,"\n"); + fprintf(fp,"// MACROS to inline and constant fold State::valid(index)...\n"); + fprintf(fp,"// when given a constant 'index' in dfa_.cpp\n"); + fprintf(fp,"// uint word = index >> 5; // Shift out bit position\n"); + fprintf(fp,"// uint bitpos = index & 0x0001F; // Mask off word bits\n"); + fprintf(fp,"#define STATE__VALID(index) "); + fprintf(fp," (%s)\n", state__valid); + fprintf(fp,"\n"); + fprintf(fp,"#define STATE__NOT_YET_VALID(index) "); + fprintf(fp," ( (%s) == 0 )\n", state__valid); + fprintf(fp,"\n"); + fprintf(fp,"#define STATE__VALID_CHILD(state,index) "); + fprintf(fp," ( state && (state->%s) )\n", state__valid); + fprintf(fp,"\n"); + fprintf(fp,"#define STATE__SET_VALID(index) "); + fprintf(fp," (%s)\n", state__set_valid); + fprintf(fp,"\n"); + fprintf(fp, + "//---------------------------State-------------------------------------------\n"); + fprintf(fp,"// State contains an integral cost vector, indexed by machine operand opcodes,\n"); + fprintf(fp,"// a rule vector consisting of machine operand/instruction opcodes, and also\n"); + fprintf(fp,"// indexed by machine operand opcodes, pointers to the children in the label\n"); + fprintf(fp,"// tree generated by the Label routines in ideal nodes (currently limited to\n"); + fprintf(fp,"// two for convenience, but this could change).\n"); + fprintf(fp,"class State : public ResourceObj {\n"); + fprintf(fp,"public:\n"); + fprintf(fp," int _id; // State identifier\n"); + fprintf(fp," Node *_leaf; // Ideal (non-machine-node) leaf of match tree\n"); + fprintf(fp," State *_kids[2]; // Children of state node in label tree\n"); + fprintf(fp," unsigned int _cost[_LAST_MACH_OPER]; // Cost vector, indexed by operand opcodes\n"); + fprintf(fp," unsigned int _rule[_LAST_MACH_OPER]; // Rule vector, indexed by operand opcodes\n"); + fprintf(fp," unsigned int _valid[(_LAST_MACH_OPER/32)+1]; // Bit Map of valid Cost/Rule entries\n"); + fprintf(fp,"\n"); + fprintf(fp," State(void); // Constructor\n"); + fprintf(fp," DEBUG_ONLY( ~State(void); ) // Destructor\n"); + fprintf(fp,"\n"); + fprintf(fp," // Methods created by ADLC and invoked by Reduce\n"); + fprintf(fp," MachOper *MachOperGenerator( int opcode, Compile* C );\n"); + fprintf(fp," MachNode *MachNodeGenerator( int opcode, Compile* C );\n"); + fprintf(fp,"\n"); + fprintf(fp," // Assign a state to a node, definition of method produced by ADLC\n"); + fprintf(fp," bool DFA( int opcode, const Node *ideal );\n"); + fprintf(fp,"\n"); + fprintf(fp," // Access function for _valid bit vector\n"); + fprintf(fp," bool valid(uint index) {\n"); + fprintf(fp," return( STATE__VALID(index) != 0 );\n"); + fprintf(fp," }\n"); + fprintf(fp,"\n"); + fprintf(fp," // Set function for _valid bit vector\n"); + fprintf(fp," void set_valid(uint index) {\n"); + fprintf(fp," STATE__SET_VALID(index);\n"); + fprintf(fp," }\n"); + fprintf(fp,"\n"); + fprintf(fp,"#ifndef PRODUCT\n"); + fprintf(fp," void dump(); // Debugging prints\n"); + fprintf(fp," void dump(int depth);\n"); + fprintf(fp,"#endif\n"); + if (_dfa_small) { + // Generate the routine name we'll need + for (int i = 1; i < _last_opcode; i++) { + if (_mlistab[i] == NULL) continue; + fprintf(fp, " void _sub_Op_%s(const Node *n);\n", NodeClassNames[i]); + } + } + fprintf(fp,"};\n"); + fprintf(fp,"\n"); + fprintf(fp,"\n"); + +} + + +//---------------------------buildMachOperEnum--------------------------------- +// Build enumeration for densely packed operands. +// This enumeration is used to index into the arrays in the State objects +// that indicate cost and a successfull rule match. + +// Information needed to generate the ReduceOp mapping for the DFA +class OutputMachOperands : public OutputMap { +public: + OutputMachOperands(FILE *hpp, FILE *cpp, FormDict &globals, ArchDesc &AD) + : OutputMap(hpp, cpp, globals, AD) {}; + + void declaration() { } + void definition() { fprintf(_cpp, "enum MachOperands {\n"); } + void closing() { fprintf(_cpp, " _LAST_MACH_OPER\n"); + OutputMap::closing(); + } + void map(OpClassForm &opc) { fprintf(_cpp, " %s", _AD.machOperEnum(opc._ident) ); } + void map(OperandForm &oper) { fprintf(_cpp, " %s", _AD.machOperEnum(oper._ident) ); } + void map(char *name) { fprintf(_cpp, " %s", _AD.machOperEnum(name)); } + + bool do_instructions() { return false; } + void map(InstructForm &inst){ assert( false, "ShouldNotCallThis()"); } +}; + + +void ArchDesc::buildMachOperEnum(FILE *fp_hpp) { + // Construct the table for MachOpcodes + OutputMachOperands output_mach_operands(fp_hpp, fp_hpp, _globalNames, *this); + build_map(output_mach_operands); +} + + +//---------------------------buildMachEnum---------------------------------- +// Build enumeration for all MachOpers and all MachNodes + +// Information needed to generate the ReduceOp mapping for the DFA +class OutputMachOpcodes : public OutputMap { + int begin_inst_chain_rule; + int end_inst_chain_rule; + int begin_rematerialize; + int end_rematerialize; + int end_instructions; +public: + OutputMachOpcodes(FILE *hpp, FILE *cpp, FormDict &globals, ArchDesc &AD) + : OutputMap(hpp, cpp, globals, AD), + begin_inst_chain_rule(-1), end_inst_chain_rule(-1), end_instructions(-1) + {}; + + void declaration() { } + void definition() { fprintf(_cpp, "enum MachOpcodes {\n"); } + void closing() { + if( begin_inst_chain_rule != -1 ) + fprintf(_cpp, " _BEGIN_INST_CHAIN_RULE = %d,\n", begin_inst_chain_rule); + if( end_inst_chain_rule != -1 ) + fprintf(_cpp, " _END_INST_CHAIN_RULE = %d,\n", end_inst_chain_rule); + if( begin_rematerialize != -1 ) + fprintf(_cpp, " _BEGIN_REMATERIALIZE = %d,\n", begin_rematerialize); + if( end_rematerialize != -1 ) + fprintf(_cpp, " _END_REMATERIALIZE = %d,\n", end_rematerialize); + // always execute since do_instructions() is true, and avoids trailing comma + fprintf(_cpp, " _last_Mach_Node = %d \n", end_instructions); + OutputMap::closing(); + } + void map(OpClassForm &opc) { fprintf(_cpp, " %s_rule", opc._ident ); } + void map(OperandForm &oper) { fprintf(_cpp, " %s_rule", oper._ident ); } + void map(char *name) { if (name) fprintf(_cpp, " %s_rule", name); + else fprintf(_cpp, " 0"); } + void map(InstructForm &inst) {fprintf(_cpp, " %s_rule", inst._ident ); } + + void record_position(OutputMap::position place, int idx ) { + switch(place) { + case OutputMap::BEGIN_INST_CHAIN_RULES : + begin_inst_chain_rule = idx; + break; + case OutputMap::END_INST_CHAIN_RULES : + end_inst_chain_rule = idx; + break; + case OutputMap::BEGIN_REMATERIALIZE : + begin_rematerialize = idx; + break; + case OutputMap::END_REMATERIALIZE : + end_rematerialize = idx; + break; + case OutputMap::END_INSTRUCTIONS : + end_instructions = idx; + break; + default: + break; + } + } +}; + + +void ArchDesc::buildMachOpcodesEnum(FILE *fp_hpp) { + // Construct the table for MachOpcodes + OutputMachOpcodes output_mach_opcodes(fp_hpp, fp_hpp, _globalNames, *this); + build_map(output_mach_opcodes); +} + + +// Generate an enumeration of the pipeline states, and both +// the functional units (resources) and the masks for +// specifying resources +void ArchDesc::build_pipeline_enums(FILE *fp_hpp) { + int stagelen = (int)strlen("undefined"); + int stagenum = 0; + + if (_pipeline) { // Find max enum string length + const char *stage; + for ( _pipeline->_stages.reset(); (stage = _pipeline->_stages.iter()) != NULL; ) { + int len = (int)strlen(stage); + if (stagelen < len) stagelen = len; + } + } + + // Generate a list of stages + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, "// Pipeline Stages\n"); + fprintf(fp_hpp, "enum machPipelineStages {\n"); + fprintf(fp_hpp, " stage_%-*s = 0,\n", stagelen, "undefined"); + + if( _pipeline ) { + const char *stage; + for ( _pipeline->_stages.reset(); (stage = _pipeline->_stages.iter()) != NULL; ) + fprintf(fp_hpp, " stage_%-*s = %d,\n", stagelen, stage, ++stagenum); + } + + fprintf(fp_hpp, " stage_%-*s = %d\n", stagelen, "count", stagenum); + fprintf(fp_hpp, "};\n"); + + fprintf(fp_hpp, "\n"); + fprintf(fp_hpp, "// Pipeline Resources\n"); + fprintf(fp_hpp, "enum machPipelineResources {\n"); + int rescount = 0; + + if( _pipeline ) { + const char *resource; + int reslen = 0; + + // Generate a list of resources, and masks + for ( _pipeline->_reslist.reset(); (resource = _pipeline->_reslist.iter()) != NULL; ) { + int len = (int)strlen(resource); + if (reslen < len) + reslen = len; + } + + for ( _pipeline->_reslist.reset(); (resource = _pipeline->_reslist.iter()) != NULL; ) { + const ResourceForm *resform = _pipeline->_resdict[resource]->is_resource(); + int mask = resform->mask(); + if ((mask & (mask-1)) == 0) + fprintf(fp_hpp, " resource_%-*s = %d,\n", reslen, resource, rescount++); + } + fprintf(fp_hpp, "\n"); + for ( _pipeline->_reslist.reset(); (resource = _pipeline->_reslist.iter()) != NULL; ) { + const ResourceForm *resform = _pipeline->_resdict[resource]->is_resource(); + fprintf(fp_hpp, " res_mask_%-*s = 0x%08x,\n", reslen, resource, resform->mask()); + } + fprintf(fp_hpp, "\n"); + } + fprintf(fp_hpp, " resource_count = %d\n", rescount); + fprintf(fp_hpp, "};\n"); +} diff --git a/hotspot/src/share/vm/asm/assembler.cpp b/hotspot/src/share/vm/asm/assembler.cpp new file mode 100644 index 00000000000..62c9d232f72 --- /dev/null +++ b/hotspot/src/share/vm/asm/assembler.cpp @@ -0,0 +1,273 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler.cpp.incl" + + +// Implementation of AbstractAssembler +// +// The AbstractAssembler is generating code into a CodeBuffer. To make code generation faster, +// the assembler keeps a copy of the code buffers boundaries & modifies them when +// emitting bytes rather than using the code buffers accessor functions all the time. +// The code buffer is updated via set_code_end(...) after emiting a whole instruction. + +AbstractAssembler::AbstractAssembler(CodeBuffer* code) { + if (code == NULL) return; + CodeSection* cs = code->insts(); + cs->clear_mark(); // new assembler kills old mark + _code_section = cs; + _code_begin = cs->start(); + _code_limit = cs->limit(); + _code_pos = cs->end(); + _oop_recorder= code->oop_recorder(); + if (_code_begin == NULL) { + vm_exit_out_of_memory1(0, "CodeCache: no room for %s", code->name()); + } +} + +void AbstractAssembler::set_code_section(CodeSection* cs) { + assert(cs->outer() == code_section()->outer(), "sanity"); + assert(cs->is_allocated(), "need to pre-allocate this section"); + cs->clear_mark(); // new assembly into this section kills old mark + _code_section = cs; + _code_begin = cs->start(); + _code_limit = cs->limit(); + _code_pos = cs->end(); +} + +// Inform CodeBuffer that incoming code and relocation will be for stubs +address AbstractAssembler::start_a_stub(int required_space) { + CodeBuffer* cb = code(); + CodeSection* cs = cb->stubs(); + assert(_code_section == cb->insts(), "not in insts?"); + sync(); + if (cs->maybe_expand_to_ensure_remaining(required_space) + && cb->blob() == NULL) { + return NULL; + } + set_code_section(cs); + return pc(); +} + +// Inform CodeBuffer that incoming code and relocation will be code +// Should not be called if start_a_stub() returned NULL +void AbstractAssembler::end_a_stub() { + assert(_code_section == code()->stubs(), "not in stubs?"); + sync(); + set_code_section(code()->insts()); +} + +// Inform CodeBuffer that incoming code and relocation will be for stubs +address AbstractAssembler::start_a_const(int required_space, int required_align) { + CodeBuffer* cb = code(); + CodeSection* cs = cb->consts(); + assert(_code_section == cb->insts(), "not in insts?"); + sync(); + address end = cs->end(); + int pad = -(intptr_t)end & (required_align-1); + if (cs->maybe_expand_to_ensure_remaining(pad + required_space)) { + if (cb->blob() == NULL) return NULL; + end = cs->end(); // refresh pointer + } + if (pad > 0) { + while (--pad >= 0) { *end++ = 0; } + cs->set_end(end); + } + set_code_section(cs); + return end; +} + +// Inform CodeBuffer that incoming code and relocation will be code +// Should not be called if start_a_const() returned NULL +void AbstractAssembler::end_a_const() { + assert(_code_section == code()->consts(), "not in consts?"); + sync(); + set_code_section(code()->insts()); +} + + +void AbstractAssembler::flush() { + sync(); + ICache::invalidate_range(addr_at(0), offset()); +} + + +void AbstractAssembler::a_byte(int x) { + emit_byte(x); +} + + +void AbstractAssembler::a_long(jint x) { + emit_long(x); +} + +// Labels refer to positions in the (to be) generated code. There are bound +// and unbound +// +// Bound labels refer to known positions in the already generated code. +// offset() is the position the label refers to. +// +// Unbound labels refer to unknown positions in the code to be generated; it +// may contain a list of unresolved displacements that refer to it +#ifndef PRODUCT +void AbstractAssembler::print(Label& L) { + if (L.is_bound()) { + tty->print_cr("bound label to %d|%d", L.loc_pos(), L.loc_sect()); + } else if (L.is_unbound()) { + L.print_instructions((MacroAssembler*)this); + } else { + tty->print_cr("label in inconsistent state (loc = %d)", L.loc()); + } +} +#endif // PRODUCT + + +void AbstractAssembler::bind(Label& L) { + if (L.is_bound()) { + // Assembler can bind a label more than once to the same place. + guarantee(L.loc() == locator(), "attempt to redefine label"); + return; + } + L.bind_loc(locator()); + L.patch_instructions((MacroAssembler*)this); +} + +void AbstractAssembler::generate_stack_overflow_check( int frame_size_in_bytes) { + if (UseStackBanging) { + // Each code entry causes one stack bang n pages down the stack where n + // is configurable by StackBangPages. The setting depends on the maximum + // depth of VM call stack or native before going back into java code, + // since only java code can raise a stack overflow exception using the + // stack banging mechanism. The VM and native code does not detect stack + // overflow. + // The code in JavaCalls::call() checks that there is at least n pages + // available, so all entry code needs to do is bang once for the end of + // this shadow zone. + // The entry code may need to bang additional pages if the framesize + // is greater than a page. + + const int page_size = os::vm_page_size(); + int bang_end = StackShadowPages*page_size; + + // This is how far the previous frame's stack banging extended. + const int bang_end_safe = bang_end; + + if (frame_size_in_bytes > page_size) { + bang_end += frame_size_in_bytes; + } + + int bang_offset = bang_end_safe; + while (bang_offset <= bang_end) { + // Need at least one stack bang at end of shadow zone. + bang_stack_with_offset(bang_offset); + bang_offset += page_size; + } + } // end (UseStackBanging) +} + +void Label::add_patch_at(CodeBuffer* cb, int branch_loc) { + assert(_loc == -1, "Label is unbound"); + if (_patch_index < PatchCacheSize) { + _patches[_patch_index] = branch_loc; + } else { + if (_patch_overflow == NULL) { + _patch_overflow = cb->create_patch_overflow(); + } + _patch_overflow->push(branch_loc); + } + ++_patch_index; +} + +void Label::patch_instructions(MacroAssembler* masm) { + assert(is_bound(), "Label is bound"); + CodeBuffer* cb = masm->code(); + int target_sect = CodeBuffer::locator_sect(loc()); + address target = cb->locator_address(loc()); + while (_patch_index > 0) { + --_patch_index; + int branch_loc; + if (_patch_index >= PatchCacheSize) { + branch_loc = _patch_overflow->pop(); + } else { + branch_loc = _patches[_patch_index]; + } + int branch_sect = CodeBuffer::locator_sect(branch_loc); + address branch = cb->locator_address(branch_loc); + if (branch_sect == CodeBuffer::SECT_CONSTS) { + // The thing to patch is a constant word. + *(address*)branch = target; + continue; + } + +#ifdef ASSERT + // Cross-section branches only work if the + // intermediate section boundaries are frozen. + if (target_sect != branch_sect) { + for (int n = MIN2(target_sect, branch_sect), + nlimit = (target_sect + branch_sect) - n; + n < nlimit; n++) { + CodeSection* cs = cb->code_section(n); + assert(cs->is_frozen(), "cross-section branch needs stable offsets"); + } + } +#endif //ASSERT + + // Push the target offset into the branch instruction. + masm->pd_patch_instruction(branch, target); + } +} + + +void AbstractAssembler::block_comment(const char* comment) { + if (sect() == CodeBuffer::SECT_INSTS) { + code_section()->outer()->block_comment(offset(), comment); + } +} + + +#ifndef PRODUCT +void Label::print_instructions(MacroAssembler* masm) const { + CodeBuffer* cb = masm->code(); + for (int i = 0; i < _patch_index; ++i) { + int branch_loc; + if (i >= PatchCacheSize) { + branch_loc = _patch_overflow->at(i - PatchCacheSize); + } else { + branch_loc = _patches[i]; + } + int branch_pos = CodeBuffer::locator_pos(branch_loc); + int branch_sect = CodeBuffer::locator_sect(branch_loc); + address branch = cb->locator_address(branch_loc); + tty->print_cr("unbound label"); + tty->print("@ %d|%d ", branch_pos, branch_sect); + if (branch_sect == CodeBuffer::SECT_CONSTS) { + tty->print_cr(PTR_FORMAT, *(address*)branch); + continue; + } + masm->pd_print_patched_instruction(branch); + tty->cr(); + } +} +#endif // ndef PRODUCT diff --git a/hotspot/src/share/vm/asm/assembler.hpp b/hotspot/src/share/vm/asm/assembler.hpp new file mode 100644 index 00000000000..7af6e5dffa1 --- /dev/null +++ b/hotspot/src/share/vm/asm/assembler.hpp @@ -0,0 +1,309 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file contains platform-independant assembler declarations. + +class CodeBuffer; +class MacroAssembler; +class AbstractAssembler; +class Label; + +/** + * Labels represent destinations for control transfer instructions. Such + * instructions can accept a Label as their target argument. A Label is + * bound to the current location in the code stream by calling the + * MacroAssembler's 'bind' method, which in turn calls the Label's 'bind' + * method. A Label may be referenced by an instruction before it's bound + * (i.e., 'forward referenced'). 'bind' stores the current code offset + * in the Label object. + * + * If an instruction references a bound Label, the offset field(s) within + * the instruction are immediately filled in based on the Label's code + * offset. If an instruction references an unbound label, that + * instruction is put on a list of instructions that must be patched + * (i.e., 'resolved') when the Label is bound. + * + * 'bind' will call the platform-specific 'patch_instruction' method to + * fill in the offset field(s) for each unresolved instruction (if there + * are any). 'patch_instruction' lives in one of the + * cpu//vm/assembler_* files. + * + * Instead of using a linked list of unresolved instructions, a Label has + * an array of unresolved instruction code offsets. _patch_index + * contains the total number of forward references. If the Label's array + * overflows (i.e., _patch_index grows larger than the array size), a + * GrowableArray is allocated to hold the remaining offsets. (The cache + * size is 4 for now, which handles over 99.5% of the cases) + * + * Labels may only be used within a single CodeSection. If you need + * to create references between code sections, use explicit relocations. + */ +class Label VALUE_OBJ_CLASS_SPEC { + private: + enum { PatchCacheSize = 4 }; + + // _loc encodes both the binding state (via its sign) + // and the binding locator (via its value) of a label. + // + // _loc >= 0 bound label, loc() encodes the target (jump) position + // _loc == -1 unbound label + int _loc; + + // References to instructions that jump to this unresolved label. + // These instructions need to be patched when the label is bound + // using the platform-specific patchInstruction() method. + // + // To avoid having to allocate from the C-heap each time, we provide + // a local cache and use the overflow only if we exceed the local cache + int _patches[PatchCacheSize]; + int _patch_index; + GrowableArray* _patch_overflow; + + Label(const Label&) { ShouldNotReachHere(); } + + public: + + /** + * After binding, be sure 'patch_instructions' is called later to link + */ + void bind_loc(int loc) { + assert(loc >= 0, "illegal locator"); + assert(_loc == -1, "already bound"); + _loc = loc; + } + void bind_loc(int pos, int sect); // = bind_loc(locator(pos, sect)) + +#ifndef PRODUCT + // Iterates over all unresolved instructions for printing + void print_instructions(MacroAssembler* masm) const; +#endif // PRODUCT + + /** + * Returns the position of the the Label in the code buffer + * The position is a 'locator', which encodes both offset and section. + */ + int loc() const { + assert(_loc >= 0, "unbound label"); + return _loc; + } + int loc_pos() const; // == locator_pos(loc()) + int loc_sect() const; // == locator_sect(loc()) + + bool is_bound() const { return _loc >= 0; } + bool is_unbound() const { return _loc == -1 && _patch_index > 0; } + bool is_unused() const { return _loc == -1 && _patch_index == 0; } + + /** + * Adds a reference to an unresolved displacement instruction to + * this unbound label + * + * @param cb the code buffer being patched + * @param branch_loc the locator of the branch instruction in the code buffer + */ + void add_patch_at(CodeBuffer* cb, int branch_loc); + + /** + * Iterate over the list of patches, resolving the instructions + * Call patch_instruction on each 'branch_loc' value + */ + void patch_instructions(MacroAssembler* masm); + + void init() { + _loc = -1; + _patch_index = 0; + _patch_overflow = NULL; + } + + Label() { + init(); + } +}; + + +// The Abstract Assembler: Pure assembler doing NO optimizations on the +// instruction level; i.e., what you write is what you get. +// The Assembler is generating code into a CodeBuffer. +class AbstractAssembler : public ResourceObj { + friend class Label; + + protected: + CodeSection* _code_section; // section within the code buffer + address _code_begin; // first byte of code buffer + address _code_limit; // first byte after code buffer + address _code_pos; // current code generation position + OopRecorder* _oop_recorder; // support for relocInfo::oop_type + + // Code emission & accessing + address addr_at(int pos) const { return _code_begin + pos; } + + // This routine is called with a label is used for an address. + // Labels and displacements truck in offsets, but target must return a PC. + address target(Label& L); // return _code_section->target(L) + + bool is8bit(int x) const { return -0x80 <= x && x < 0x80; } + bool isByte(int x) const { return 0 <= x && x < 0x100; } + bool isShiftCount(int x) const { return 0 <= x && x < 32; } + + void emit_byte(int x); // emit a single byte + void emit_word(int x); // emit a 16-bit word (not a wordSize word!) + void emit_long(jint x); // emit a 32-bit word (not a longSize word!) + void emit_address(address x); // emit an address (not a longSize word!) + + // Instruction boundaries (required when emitting relocatable values). + class InstructionMark: public StackObj { + private: + AbstractAssembler* _assm; + + public: + InstructionMark(AbstractAssembler* assm) : _assm(assm) { + assert(assm->inst_mark() == NULL, "overlapping instructions"); + _assm->set_inst_mark(); + } + ~InstructionMark() { + _assm->clear_inst_mark(); + } + }; + friend class InstructionMark; + #ifdef ASSERT + // Make it return true on platforms which need to verify + // instruction boundaries for some operations. + inline static bool pd_check_instruction_mark(); + #endif + + // Label functions + void print(Label& L); + + public: + + // Creation + AbstractAssembler(CodeBuffer* code); + + // save end pointer back to code buf. + void sync(); + + // ensure buf contains all code (call this before using/copying the code) + void flush(); + + // Accessors + CodeBuffer* code() const; // _code_section->outer() + CodeSection* code_section() const { return _code_section; } + int sect() const; // return _code_section->index() + address pc() const { return _code_pos; } + int offset() const { return _code_pos - _code_begin; } + int locator() const; // CodeBuffer::locator(offset(), sect()) + OopRecorder* oop_recorder() const { return _oop_recorder; } + void set_oop_recorder(OopRecorder* r) { _oop_recorder = r; } + + address inst_mark() const; + void set_inst_mark(); + void clear_inst_mark(); + + // Constants in code + void a_byte(int x); + void a_long(jint x); + void relocate(RelocationHolder const& rspec, int format = 0); + void relocate( relocInfo::relocType rtype, int format = 0) { + if (rtype != relocInfo::none) + relocate(Relocation::spec_simple(rtype), format); + } + + static int code_fill_byte(); // used to pad out odd-sized code buffers + + // Associate a comment with the current offset. It will be printed + // along with the disassembly when printing nmethods. Currently + // only supported in the instruction section of the code buffer. + void block_comment(const char* comment); + + // Label functions + void bind(Label& L); // binds an unbound label L to the current code position + + // Move to a different section in the same code buffer. + void set_code_section(CodeSection* cs); + + // Inform assembler when generating stub code and relocation info + address start_a_stub(int required_space); + void end_a_stub(); + // Ditto for constants. + address start_a_const(int required_space, int required_align = sizeof(double)); + void end_a_const(); + + // fp constants support + address double_constant(jdouble c) { + address ptr = start_a_const(sizeof(c), sizeof(c)); + if (ptr != NULL) { + *(jdouble*)ptr = c; + _code_pos = ptr + sizeof(c); + end_a_const(); + } + return ptr; + } + address float_constant(jfloat c) { + address ptr = start_a_const(sizeof(c), sizeof(c)); + if (ptr != NULL) { + *(jfloat*)ptr = c; + _code_pos = ptr + sizeof(c); + end_a_const(); + } + return ptr; + } + address address_constant(address c, RelocationHolder const& rspec) { + address ptr = start_a_const(sizeof(c), sizeof(c)); + if (ptr != NULL) { + relocate(rspec); + *(address*)ptr = c; + _code_pos = ptr + sizeof(c); + end_a_const(); + } + return ptr; + } + inline address address_constant(Label& L); + inline address address_table_constant(GrowableArray label); + + // Bang stack to trigger StackOverflowError at a safe location + // implementation delegates to machine-specific bang_stack_with_offset + void generate_stack_overflow_check( int frame_size_in_bytes ); + virtual void bang_stack_with_offset(int offset) = 0; + + + /** + * A platform-dependent method to patch a jump instruction that refers + * to this label. + * + * @param branch the location of the instruction to patch + * @param masm the assembler which generated the branch + */ + void pd_patch_instruction(address branch, address target); + +#ifndef PRODUCT + /** + * Platform-dependent method of printing an instruction that needs to be + * patched. + * + * @param branch the instruction to be patched in the buffer. + */ + static void pd_print_patched_instruction(address branch); +#endif // PRODUCT +}; + +#include "incls/_assembler_pd.hpp.incl" diff --git a/hotspot/src/share/vm/asm/assembler.inline.hpp b/hotspot/src/share/vm/asm/assembler.inline.hpp new file mode 100644 index 00000000000..1d9f4876ce3 --- /dev/null +++ b/hotspot/src/share/vm/asm/assembler.inline.hpp @@ -0,0 +1,135 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void AbstractAssembler::sync() { + CodeSection* cs = code_section(); + guarantee(cs->start() == _code_begin, "must not shift code buffer"); + cs->set_end(_code_pos); +} + +inline void AbstractAssembler::emit_byte(int x) { + assert(isByte(x), "not a byte"); + *(unsigned char*)_code_pos = (unsigned char)x; + _code_pos += sizeof(unsigned char); + sync(); +} + + +inline void AbstractAssembler::emit_word(int x) { + *(short*)_code_pos = (short)x; + _code_pos += sizeof(short); + sync(); +} + + +inline void AbstractAssembler::emit_long(jint x) { + *(jint*)_code_pos = x; + _code_pos += sizeof(jint); + sync(); +} + +inline void AbstractAssembler::emit_address(address x) { + *(address*)_code_pos = x; + _code_pos += sizeof(address); + sync(); +} + +inline address AbstractAssembler::inst_mark() const { + return code_section()->mark(); +} + + +inline void AbstractAssembler::set_inst_mark() { + code_section()->set_mark(); +} + + +inline void AbstractAssembler::clear_inst_mark() { + code_section()->clear_mark(); +} + + +inline void AbstractAssembler::relocate(RelocationHolder const& rspec, int format) { + assert(!pd_check_instruction_mark() + || inst_mark() == NULL || inst_mark() == _code_pos, + "call relocate() between instructions"); + code_section()->relocate(_code_pos, rspec, format); +} + + +inline CodeBuffer* AbstractAssembler::code() const { + return code_section()->outer(); +} + +inline int AbstractAssembler::sect() const { + return code_section()->index(); +} + +inline int AbstractAssembler::locator() const { + return CodeBuffer::locator(offset(), sect()); +} + +inline address AbstractAssembler::target(Label& L) { + return code_section()->target(L, pc()); +} + +inline int Label::loc_pos() const { + return CodeBuffer::locator_pos(loc()); +} + +inline int Label::loc_sect() const { + return CodeBuffer::locator_sect(loc()); +} + +inline void Label::bind_loc(int pos, int sect) { + bind_loc(CodeBuffer::locator(pos, sect)); +} + +address AbstractAssembler::address_constant(Label& L) { + address c = NULL; + address ptr = start_a_const(sizeof(c), sizeof(c)); + if (ptr != NULL) { + relocate(Relocation::spec_simple(relocInfo::internal_word_type)); + *(address*)ptr = c = code_section()->target(L, ptr); + _code_pos = ptr + sizeof(c); + end_a_const(); + } + return ptr; +} + +address AbstractAssembler::address_table_constant(GrowableArray labels) { + int addressSize = sizeof(address); + int sizeLabel = addressSize * labels.length(); + address ptr = start_a_const(sizeLabel, addressSize); + + if (ptr != NULL) { + address *labelLoc = (address*)ptr; + for (int i=0; i < labels.length(); i++) { + emit_address(code_section()->target(*labels.at(i), (address)&labelLoc[i])); + code_section()->relocate((address)&labelLoc[i], relocInfo::internal_word_type); + } + end_a_const(); + } + return ptr; +} diff --git a/hotspot/src/share/vm/asm/codeBuffer.cpp b/hotspot/src/share/vm/asm/codeBuffer.cpp new file mode 100644 index 00000000000..a8b19abb3a7 --- /dev/null +++ b/hotspot/src/share/vm/asm/codeBuffer.cpp @@ -0,0 +1,1023 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_codeBuffer.cpp.incl" + +// The structure of a CodeSection: +// +// _start -> +----------------+ +// | machine code...| +// _end -> |----------------| +// | | +// | (empty) | +// | | +// | | +// +----------------+ +// _limit -> | | +// +// _locs_start -> +----------------+ +// |reloc records...| +// |----------------| +// _locs_end -> | | +// | | +// | (empty) | +// | | +// | | +// +----------------+ +// _locs_limit -> | | +// The _end (resp. _limit) pointer refers to the first +// unused (resp. unallocated) byte. + +// The structure of the CodeBuffer while code is being accumulated: +// +// _total_start -> \ +// _insts._start -> +----------------+ +// | | +// | Code | +// | | +// _stubs._start -> |----------------| +// | | +// | Stubs | (also handlers for deopt/exception) +// | | +// _consts._start -> |----------------| +// | | +// | Constants | +// | | +// +----------------+ +// + _total_size -> | | +// +// When the code and relocations are copied to the code cache, +// the empty parts of each section are removed, and everything +// is copied into contiguous locations. + +typedef CodeBuffer::csize_t csize_t; // file-local definition + +// external buffer, in a predefined CodeBlob or other buffer area +// Important: The code_start must be taken exactly, and not realigned. +CodeBuffer::CodeBuffer(address code_start, csize_t code_size) { + assert(code_start != NULL, "sanity"); + initialize_misc("static buffer"); + initialize(code_start, code_size); + assert(verify_section_allocation(), "initial use of buffer OK"); +} + +void CodeBuffer::initialize(csize_t code_size, csize_t locs_size) { + // Compute maximal alignment. + int align = _insts.alignment(); + // Always allow for empty slop around each section. + int slop = (int) CodeSection::end_slop(); + + assert(blob() == NULL, "only once"); + set_blob(BufferBlob::create(_name, code_size + (align+slop) * (SECT_LIMIT+1))); + if (blob() == NULL) { + // The assembler constructor will throw a fatal on an empty CodeBuffer. + return; // caller must test this + } + + // Set up various pointers into the blob. + initialize(_total_start, _total_size); + + assert((uintptr_t)code_begin() % CodeEntryAlignment == 0, "instruction start not code entry aligned"); + + pd_initialize(); + + if (locs_size != 0) { + _insts.initialize_locs(locs_size / sizeof(relocInfo)); + } + + assert(verify_section_allocation(), "initial use of blob is OK"); +} + + +CodeBuffer::~CodeBuffer() { + // If we allocate our code buffer from the CodeCache + // via a BufferBlob, and it's not permanent, then + // free the BufferBlob. + // The rest of the memory will be freed when the ResourceObj + // is released. + assert(verify_section_allocation(), "final storage configuration still OK"); + for (CodeBuffer* cb = this; cb != NULL; cb = cb->before_expand()) { + // Previous incarnations of this buffer are held live, so that internal + // addresses constructed before expansions will not be confused. + cb->free_blob(); + } +#ifdef ASSERT + Copy::fill_to_bytes(this, sizeof(*this), badResourceValue); +#endif +} + +void CodeBuffer::initialize_oop_recorder(OopRecorder* r) { + assert(_oop_recorder == &_default_oop_recorder && _default_oop_recorder.is_unused(), "do this once"); + DEBUG_ONLY(_default_oop_recorder.oop_size()); // force unused OR to be frozen + _oop_recorder = r; +} + +void CodeBuffer::initialize_section_size(CodeSection* cs, csize_t size) { + assert(cs != &_insts, "insts is the memory provider, not the consumer"); +#ifdef ASSERT + for (int n = (int)SECT_INSTS+1; n < (int)SECT_LIMIT; n++) { + CodeSection* prevCS = code_section(n); + if (prevCS == cs) break; + assert(!prevCS->is_allocated(), "section allocation must be in reverse order"); + } +#endif + csize_t slop = CodeSection::end_slop(); // margin between sections + int align = cs->alignment(); + assert(is_power_of_2(align), "sanity"); + address start = _insts._start; + address limit = _insts._limit; + address middle = limit - size; + middle -= (intptr_t)middle & (align-1); // align the division point downward + guarantee(middle - slop > start, "need enough space to divide up"); + _insts._limit = middle - slop; // subtract desired space, plus slop + cs->initialize(middle, limit - middle); + assert(cs->start() == middle, "sanity"); + assert(cs->limit() == limit, "sanity"); + // give it some relocations to start with, if the main section has them + if (_insts.has_locs()) cs->initialize_locs(1); +} + +void CodeBuffer::freeze_section(CodeSection* cs) { + CodeSection* next_cs = (cs == consts())? NULL: code_section(cs->index()+1); + csize_t frozen_size = cs->size(); + if (next_cs != NULL) { + frozen_size = next_cs->align_at_start(frozen_size); + } + address old_limit = cs->limit(); + address new_limit = cs->start() + frozen_size; + relocInfo* old_locs_limit = cs->locs_limit(); + relocInfo* new_locs_limit = cs->locs_end(); + // Patch the limits. + cs->_limit = new_limit; + cs->_locs_limit = new_locs_limit; + cs->_frozen = true; + if (!next_cs->is_allocated() && !next_cs->is_frozen()) { + // Give remaining buffer space to the following section. + next_cs->initialize(new_limit, old_limit - new_limit); + next_cs->initialize_shared_locs(new_locs_limit, + old_locs_limit - new_locs_limit); + } +} + +void CodeBuffer::set_blob(BufferBlob* blob) { + _blob = blob; + if (blob != NULL) { + address start = blob->instructions_begin(); + address end = blob->instructions_end(); + // Round up the starting address. + int align = _insts.alignment(); + start += (-(intptr_t)start) & (align-1); + _total_start = start; + _total_size = end - start; + } else { + #ifdef ASSERT + // Clean out dangling pointers. + _total_start = badAddress; + _insts._start = _insts._end = badAddress; + _stubs._start = _stubs._end = badAddress; + _consts._start = _consts._end = badAddress; + #endif //ASSERT + } +} + +void CodeBuffer::free_blob() { + if (_blob != NULL) { + BufferBlob::free(_blob); + set_blob(NULL); + } +} + +const char* CodeBuffer::code_section_name(int n) { +#ifdef PRODUCT + return NULL; +#else //PRODUCT + switch (n) { + case SECT_INSTS: return "insts"; + case SECT_STUBS: return "stubs"; + case SECT_CONSTS: return "consts"; + default: return NULL; + } +#endif //PRODUCT +} + +int CodeBuffer::section_index_of(address addr) const { + for (int n = 0; n < (int)SECT_LIMIT; n++) { + const CodeSection* cs = code_section(n); + if (cs->allocates(addr)) return n; + } + return SECT_NONE; +} + +int CodeBuffer::locator(address addr) const { + for (int n = 0; n < (int)SECT_LIMIT; n++) { + const CodeSection* cs = code_section(n); + if (cs->allocates(addr)) { + return locator(addr - cs->start(), n); + } + } + return -1; +} + +address CodeBuffer::locator_address(int locator) const { + if (locator < 0) return NULL; + address start = code_section(locator_sect(locator))->start(); + return start + locator_pos(locator); +} + +address CodeBuffer::decode_begin() { + address begin = _insts.start(); + if (_decode_begin != NULL && _decode_begin > begin) + begin = _decode_begin; + return begin; +} + + +GrowableArray* CodeBuffer::create_patch_overflow() { + if (_overflow_arena == NULL) { + _overflow_arena = new Arena(); + } + return new (_overflow_arena) GrowableArray(_overflow_arena, 8, 0, 0); +} + + +// Helper function for managing labels and their target addresses. +// Returns a sensible address, and if it is not the label's final +// address, notes the dependency (at 'branch_pc') on the label. +address CodeSection::target(Label& L, address branch_pc) { + if (L.is_bound()) { + int loc = L.loc(); + if (index() == CodeBuffer::locator_sect(loc)) { + return start() + CodeBuffer::locator_pos(loc); + } else { + return outer()->locator_address(loc); + } + } else { + assert(allocates2(branch_pc), "sanity"); + address base = start(); + int patch_loc = CodeBuffer::locator(branch_pc - base, index()); + L.add_patch_at(outer(), patch_loc); + + // Need to return a pc, doesn't matter what it is since it will be + // replaced during resolution later. + // (Don't return NULL or badAddress, since branches shouldn't overflow.) + return base; + } +} + +void CodeSection::relocate(address at, RelocationHolder const& spec, int format) { + Relocation* reloc = spec.reloc(); + relocInfo::relocType rtype = (relocInfo::relocType) reloc->type(); + if (rtype == relocInfo::none) return; + + // The assertion below has been adjusted, to also work for + // relocation for fixup. Sometimes we want to put relocation + // information for the next instruction, since it will be patched + // with a call. + assert(start() <= at && at <= end()+1, + "cannot relocate data outside code boundaries"); + + if (!has_locs()) { + // no space for relocation information provided => code cannot be + // relocated. Make sure that relocate is only called with rtypes + // that can be ignored for this kind of code. + assert(rtype == relocInfo::none || + rtype == relocInfo::runtime_call_type || + rtype == relocInfo::internal_word_type|| + rtype == relocInfo::section_word_type || + rtype == relocInfo::external_word_type, + "code needs relocation information"); + // leave behind an indication that we attempted a relocation + DEBUG_ONLY(_locs_start = _locs_limit = (relocInfo*)badAddress); + return; + } + + // Advance the point, noting the offset we'll have to record. + csize_t offset = at - locs_point(); + set_locs_point(at); + + // Test for a couple of overflow conditions; maybe expand the buffer. + relocInfo* end = locs_end(); + relocInfo* req = end + relocInfo::length_limit; + // Check for (potential) overflow + if (req >= locs_limit() || offset >= relocInfo::offset_limit()) { + req += (uint)offset / (uint)relocInfo::offset_limit(); + if (req >= locs_limit()) { + // Allocate or reallocate. + expand_locs(locs_count() + (req - end)); + // reload pointer + end = locs_end(); + } + } + + // If the offset is giant, emit filler relocs, of type 'none', but + // each carrying the largest possible offset, to advance the locs_point. + while (offset >= relocInfo::offset_limit()) { + assert(end < locs_limit(), "adjust previous paragraph of code"); + *end++ = filler_relocInfo(); + offset -= filler_relocInfo().addr_offset(); + } + + // If it's a simple reloc with no data, we'll just write (rtype | offset). + (*end) = relocInfo(rtype, offset, format); + + // If it has data, insert the prefix, as (data_prefix_tag | data1), data2. + end->initialize(this, reloc); +} + +void CodeSection::initialize_locs(int locs_capacity) { + assert(_locs_start == NULL, "only one locs init step, please"); + // Apply a priori lower limits to relocation size: + csize_t min_locs = MAX2(size() / 16, (csize_t)4); + if (locs_capacity < min_locs) locs_capacity = min_locs; + relocInfo* locs_start = NEW_RESOURCE_ARRAY(relocInfo, locs_capacity); + _locs_start = locs_start; + _locs_end = locs_start; + _locs_limit = locs_start + locs_capacity; + _locs_own = true; +} + +void CodeSection::initialize_shared_locs(relocInfo* buf, int length) { + assert(_locs_start == NULL, "do this before locs are allocated"); + // Internal invariant: locs buf must be fully aligned. + // See copy_relocations_to() below. + while ((uintptr_t)buf % HeapWordSize != 0 && length > 0) { + ++buf; --length; + } + if (length > 0) { + _locs_start = buf; + _locs_end = buf; + _locs_limit = buf + length; + _locs_own = false; + } +} + +void CodeSection::initialize_locs_from(const CodeSection* source_cs) { + int lcount = source_cs->locs_count(); + if (lcount != 0) { + initialize_shared_locs(source_cs->locs_start(), lcount); + _locs_end = _locs_limit = _locs_start + lcount; + assert(is_allocated(), "must have copied code already"); + set_locs_point(start() + source_cs->locs_point_off()); + } + assert(this->locs_count() == source_cs->locs_count(), "sanity"); +} + +void CodeSection::expand_locs(int new_capacity) { + if (_locs_start == NULL) { + initialize_locs(new_capacity); + return; + } else { + int old_count = locs_count(); + int old_capacity = locs_capacity(); + if (new_capacity < old_capacity * 2) + new_capacity = old_capacity * 2; + relocInfo* locs_start; + if (_locs_own) { + locs_start = REALLOC_RESOURCE_ARRAY(relocInfo, _locs_start, old_capacity, new_capacity); + } else { + locs_start = NEW_RESOURCE_ARRAY(relocInfo, new_capacity); + Copy::conjoint_bytes(_locs_start, locs_start, old_capacity * sizeof(relocInfo)); + _locs_own = true; + } + _locs_start = locs_start; + _locs_end = locs_start + old_count; + _locs_limit = locs_start + new_capacity; + } +} + + +/// Support for emitting the code to its final location. +/// The pattern is the same for all functions. +/// We iterate over all the sections, padding each to alignment. + +csize_t CodeBuffer::total_code_size() const { + csize_t code_size_so_far = 0; + for (int n = 0; n < (int)SECT_LIMIT; n++) { + const CodeSection* cs = code_section(n); + if (cs->is_empty()) continue; // skip trivial section + code_size_so_far = cs->align_at_start(code_size_so_far); + code_size_so_far += cs->size(); + } + return code_size_so_far; +} + +void CodeBuffer::compute_final_layout(CodeBuffer* dest) const { + address buf = dest->_total_start; + csize_t buf_offset = 0; + assert(dest->_total_size >= total_code_size(), "must be big enough"); + + { + // not sure why this is here, but why not... + int alignSize = MAX2((intx) sizeof(jdouble), CodeEntryAlignment); + assert( (dest->_total_start - _insts.start()) % alignSize == 0, "copy must preserve alignment"); + } + + const CodeSection* prev_cs = NULL; + CodeSection* prev_dest_cs = NULL; + for (int n = 0; n < (int)SECT_LIMIT; n++) { + // figure compact layout of each section + const CodeSection* cs = code_section(n); + address cstart = cs->start(); + address cend = cs->end(); + csize_t csize = cend - cstart; + + CodeSection* dest_cs = dest->code_section(n); + if (!cs->is_empty()) { + // Compute initial padding; assign it to the previous non-empty guy. + // Cf. figure_expanded_capacities. + csize_t padding = cs->align_at_start(buf_offset) - buf_offset; + if (padding != 0) { + buf_offset += padding; + assert(prev_dest_cs != NULL, "sanity"); + prev_dest_cs->_limit += padding; + } + #ifdef ASSERT + if (prev_cs != NULL && prev_cs->is_frozen() && n < SECT_CONSTS) { + // Make sure the ends still match up. + // This is important because a branch in a frozen section + // might target code in a following section, via a Label, + // and without a relocation record. See Label::patch_instructions. + address dest_start = buf+buf_offset; + csize_t start2start = cs->start() - prev_cs->start(); + csize_t dest_start2start = dest_start - prev_dest_cs->start(); + assert(start2start == dest_start2start, "cannot stretch frozen sect"); + } + #endif //ASSERT + prev_dest_cs = dest_cs; + prev_cs = cs; + } + + debug_only(dest_cs->_start = NULL); // defeat double-initialization assert + dest_cs->initialize(buf+buf_offset, csize); + dest_cs->set_end(buf+buf_offset+csize); + assert(dest_cs->is_allocated(), "must always be allocated"); + assert(cs->is_empty() == dest_cs->is_empty(), "sanity"); + + buf_offset += csize; + } + + // Done calculating sections; did it come out to the right end? + assert(buf_offset == total_code_size(), "sanity"); + assert(dest->verify_section_allocation(), "final configuration works"); +} + +csize_t CodeBuffer::total_offset_of(address addr) const { + csize_t code_size_so_far = 0; + for (int n = 0; n < (int)SECT_LIMIT; n++) { + const CodeSection* cs = code_section(n); + if (!cs->is_empty()) { + code_size_so_far = cs->align_at_start(code_size_so_far); + } + if (cs->contains2(addr)) { + return code_size_so_far + (addr - cs->start()); + } + code_size_so_far += cs->size(); + } +#ifndef PRODUCT + tty->print_cr("Dangling address " PTR_FORMAT " in:", addr); + ((CodeBuffer*)this)->print(); +#endif + ShouldNotReachHere(); + return -1; +} + +csize_t CodeBuffer::total_relocation_size() const { + csize_t lsize = copy_relocations_to(NULL); // dry run only + csize_t csize = total_code_size(); + csize_t total = RelocIterator::locs_and_index_size(csize, lsize); + return (csize_t) align_size_up(total, HeapWordSize); +} + +csize_t CodeBuffer::copy_relocations_to(CodeBlob* dest) const { + address buf = NULL; + csize_t buf_offset = 0; + csize_t buf_limit = 0; + if (dest != NULL) { + buf = (address)dest->relocation_begin(); + buf_limit = (address)dest->relocation_end() - buf; + assert((uintptr_t)buf % HeapWordSize == 0, "buf must be fully aligned"); + assert(buf_limit % HeapWordSize == 0, "buf must be evenly sized"); + } + // if dest == NULL, this is just the sizing pass + + csize_t code_end_so_far = 0; + csize_t code_point_so_far = 0; + for (int n = 0; n < (int)SECT_LIMIT; n++) { + // pull relocs out of each section + const CodeSection* cs = code_section(n); + assert(!(cs->is_empty() && cs->locs_count() > 0), "sanity"); + if (cs->is_empty()) continue; // skip trivial section + relocInfo* lstart = cs->locs_start(); + relocInfo* lend = cs->locs_end(); + csize_t lsize = (csize_t)( (address)lend - (address)lstart ); + csize_t csize = cs->size(); + code_end_so_far = cs->align_at_start(code_end_so_far); + + if (lsize > 0) { + // Figure out how to advance the combined relocation point + // first to the beginning of this section. + // We'll insert one or more filler relocs to span that gap. + // (Don't bother to improve this by editing the first reloc's offset.) + csize_t new_code_point = code_end_so_far; + for (csize_t jump; + code_point_so_far < new_code_point; + code_point_so_far += jump) { + jump = new_code_point - code_point_so_far; + relocInfo filler = filler_relocInfo(); + if (jump >= filler.addr_offset()) { + jump = filler.addr_offset(); + } else { // else shrink the filler to fit + filler = relocInfo(relocInfo::none, jump); + } + if (buf != NULL) { + assert(buf_offset + (csize_t)sizeof(filler) <= buf_limit, "filler in bounds"); + *(relocInfo*)(buf+buf_offset) = filler; + } + buf_offset += sizeof(filler); + } + + // Update code point and end to skip past this section: + csize_t last_code_point = code_end_so_far + cs->locs_point_off(); + assert(code_point_so_far <= last_code_point, "sanity"); + code_point_so_far = last_code_point; // advance past this guy's relocs + } + code_end_so_far += csize; // advance past this guy's instructions too + + // Done with filler; emit the real relocations: + if (buf != NULL && lsize != 0) { + assert(buf_offset + lsize <= buf_limit, "target in bounds"); + assert((uintptr_t)lstart % HeapWordSize == 0, "sane start"); + if (buf_offset % HeapWordSize == 0) { + // Use wordwise copies if possible: + Copy::disjoint_words((HeapWord*)lstart, + (HeapWord*)(buf+buf_offset), + (lsize + HeapWordSize-1) / HeapWordSize); + } else { + Copy::conjoint_bytes(lstart, buf+buf_offset, lsize); + } + } + buf_offset += lsize; + } + + // Align end of relocation info in target. + while (buf_offset % HeapWordSize != 0) { + if (buf != NULL) { + relocInfo padding = relocInfo(relocInfo::none, 0); + assert(buf_offset + (csize_t)sizeof(padding) <= buf_limit, "padding in bounds"); + *(relocInfo*)(buf+buf_offset) = padding; + } + buf_offset += sizeof(relocInfo); + } + + assert(code_end_so_far == total_code_size(), "sanity"); + + // Account for index: + if (buf != NULL) { + RelocIterator::create_index(dest->relocation_begin(), + buf_offset / sizeof(relocInfo), + dest->relocation_end()); + } + + return buf_offset; +} + +void CodeBuffer::copy_code_to(CodeBlob* dest_blob) { +#ifndef PRODUCT + if (PrintNMethods && (WizardMode || Verbose)) { + tty->print("done with CodeBuffer:"); + ((CodeBuffer*)this)->print(); + } +#endif //PRODUCT + + CodeBuffer dest(dest_blob->instructions_begin(), + dest_blob->instructions_size()); + assert(dest_blob->instructions_size() >= total_code_size(), "good sizing"); + this->compute_final_layout(&dest); + relocate_code_to(&dest); + + // transfer comments from buffer to blob + dest_blob->set_comments(_comments); + + // Done moving code bytes; were they the right size? + assert(round_to(dest.total_code_size(), oopSize) == dest_blob->instructions_size(), "sanity"); + + // Flush generated code + ICache::invalidate_range(dest_blob->instructions_begin(), + dest_blob->instructions_size()); +} + +// Move all my code into another code buffer. +// Consult applicable relocs to repair embedded addresses. +void CodeBuffer::relocate_code_to(CodeBuffer* dest) const { + DEBUG_ONLY(address dest_end = dest->_total_start + dest->_total_size); + for (int n = 0; n < (int)SECT_LIMIT; n++) { + // pull code out of each section + const CodeSection* cs = code_section(n); + if (cs->is_empty()) continue; // skip trivial section + CodeSection* dest_cs = dest->code_section(n); + assert(cs->size() == dest_cs->size(), "sanity"); + csize_t usize = dest_cs->size(); + csize_t wsize = align_size_up(usize, HeapWordSize); + assert(dest_cs->start() + wsize <= dest_end, "no overflow"); + // Copy the code as aligned machine words. + // This may also include an uninitialized partial word at the end. + Copy::disjoint_words((HeapWord*)cs->start(), + (HeapWord*)dest_cs->start(), + wsize / HeapWordSize); + + if (dest->blob() == NULL) { + // Destination is a final resting place, not just another buffer. + // Normalize uninitialized bytes in the final padding. + Copy::fill_to_bytes(dest_cs->end(), dest_cs->remaining(), + Assembler::code_fill_byte()); + } + + assert(cs->locs_start() != (relocInfo*)badAddress, + "this section carries no reloc storage, but reloc was attempted"); + + // Make the new code copy use the old copy's relocations: + dest_cs->initialize_locs_from(cs); + + { // Repair the pc relative information in the code after the move + RelocIterator iter(dest_cs); + while (iter.next()) { + iter.reloc()->fix_relocation_after_move(this, dest); + } + } + } +} + +csize_t CodeBuffer::figure_expanded_capacities(CodeSection* which_cs, + csize_t amount, + csize_t* new_capacity) { + csize_t new_total_cap = 0; + + int prev_n = -1; + for (int n = 0; n < (int)SECT_LIMIT; n++) { + const CodeSection* sect = code_section(n); + + if (!sect->is_empty()) { + // Compute initial padding; assign it to the previous non-empty guy. + // Cf. compute_final_layout. + csize_t padding = sect->align_at_start(new_total_cap) - new_total_cap; + if (padding != 0) { + new_total_cap += padding; + assert(prev_n >= 0, "sanity"); + new_capacity[prev_n] += padding; + } + prev_n = n; + } + + csize_t exp = sect->size(); // 100% increase + if ((uint)exp < 4*K) exp = 4*K; // minimum initial increase + if (sect == which_cs) { + if (exp < amount) exp = amount; + if (StressCodeBuffers) exp = amount; // expand only slightly + } else if (n == SECT_INSTS) { + // scale down inst increases to a more modest 25% + exp = 4*K + ((exp - 4*K) >> 2); + if (StressCodeBuffers) exp = amount / 2; // expand only slightly + } else if (sect->is_empty()) { + // do not grow an empty secondary section + exp = 0; + } + // Allow for inter-section slop: + exp += CodeSection::end_slop(); + csize_t new_cap = sect->size() + exp; + if (new_cap < sect->capacity()) { + // No need to expand after all. + new_cap = sect->capacity(); + } + new_capacity[n] = new_cap; + new_total_cap += new_cap; + } + + return new_total_cap; +} + +void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { +#ifndef PRODUCT + if (PrintNMethods && (WizardMode || Verbose)) { + tty->print("expanding CodeBuffer:"); + this->print(); + } + + if (StressCodeBuffers && blob() != NULL) { + static int expand_count = 0; + if (expand_count >= 0) expand_count += 1; + if (expand_count > 100 && is_power_of_2(expand_count)) { + tty->print_cr("StressCodeBuffers: have expanded %d times", expand_count); + // simulate an occasional allocation failure: + free_blob(); + } + } +#endif //PRODUCT + + // Resizing must be allowed + { + if (blob() == NULL) return; // caller must check for blob == NULL + for (int n = 0; n < (int)SECT_LIMIT; n++) { + guarantee(!code_section(n)->is_frozen(), "resizing not allowed when frozen"); + } + } + + // Figure new capacity for each section. + csize_t new_capacity[SECT_LIMIT]; + csize_t new_total_cap + = figure_expanded_capacities(which_cs, amount, new_capacity); + + // Create a new (temporary) code buffer to hold all the new data + CodeBuffer cb(name(), new_total_cap, 0); + if (cb.blob() == NULL) { + // Failed to allocate in code cache. + free_blob(); + return; + } + + // Create an old code buffer to remember which addresses used to go where. + // This will be useful when we do final assembly into the code cache, + // because we will need to know how to warp any internal address that + // has been created at any time in this CodeBuffer's past. + CodeBuffer* bxp = new CodeBuffer(_total_start, _total_size); + bxp->take_over_code_from(this); // remember the old undersized blob + DEBUG_ONLY(this->_blob = NULL); // silence a later assert + bxp->_before_expand = this->_before_expand; + this->_before_expand = bxp; + + // Give each section its required (expanded) capacity. + for (int n = (int)SECT_LIMIT-1; n >= SECT_INSTS; n--) { + CodeSection* cb_sect = cb.code_section(n); + CodeSection* this_sect = code_section(n); + if (new_capacity[n] == 0) continue; // already nulled out + if (n > SECT_INSTS) { + cb.initialize_section_size(cb_sect, new_capacity[n]); + } + assert(cb_sect->capacity() >= new_capacity[n], "big enough"); + address cb_start = cb_sect->start(); + cb_sect->set_end(cb_start + this_sect->size()); + if (this_sect->mark() == NULL) { + cb_sect->clear_mark(); + } else { + cb_sect->set_mark(cb_start + this_sect->mark_off()); + } + } + + // Move all the code and relocations to the new blob: + relocate_code_to(&cb); + + // Copy the temporary code buffer into the current code buffer. + // Basically, do {*this = cb}, except for some control information. + this->take_over_code_from(&cb); + cb.set_blob(NULL); + + // Zap the old code buffer contents, to avoid mistakenly using them. + debug_only(Copy::fill_to_bytes(bxp->_total_start, bxp->_total_size, + badCodeHeapFreeVal)); + + _decode_begin = NULL; // sanity + + // Make certain that the new sections are all snugly inside the new blob. + assert(verify_section_allocation(), "expanded allocation is ship-shape"); + +#ifndef PRODUCT + if (PrintNMethods && (WizardMode || Verbose)) { + tty->print("expanded CodeBuffer:"); + this->print(); + } +#endif //PRODUCT +} + +void CodeBuffer::take_over_code_from(CodeBuffer* cb) { + // Must already have disposed of the old blob somehow. + assert(blob() == NULL, "must be empty"); +#ifdef ASSERT + +#endif + // Take the new blob away from cb. + set_blob(cb->blob()); + // Take over all the section pointers. + for (int n = 0; n < (int)SECT_LIMIT; n++) { + CodeSection* cb_sect = cb->code_section(n); + CodeSection* this_sect = code_section(n); + this_sect->take_over_code_from(cb_sect); + } + _overflow_arena = cb->_overflow_arena; + // Make sure the old cb won't try to use it or free it. + DEBUG_ONLY(cb->_blob = (BufferBlob*)badAddress); +} + +#ifdef ASSERT +bool CodeBuffer::verify_section_allocation() { + address tstart = _total_start; + if (tstart == badAddress) return true; // smashed by set_blob(NULL) + address tend = tstart + _total_size; + if (_blob != NULL) { + assert(tstart >= _blob->instructions_begin(), "sanity"); + assert(tend <= _blob->instructions_end(), "sanity"); + } + address tcheck = tstart; // advancing pointer to verify disjointness + for (int n = 0; n < (int)SECT_LIMIT; n++) { + CodeSection* sect = code_section(n); + if (!sect->is_allocated()) continue; + assert(sect->start() >= tcheck, "sanity"); + tcheck = sect->start(); + assert((intptr_t)tcheck % sect->alignment() == 0 + || sect->is_empty() || _blob == NULL, + "start is aligned"); + assert(sect->end() >= tcheck, "sanity"); + assert(sect->end() <= tend, "sanity"); + } + return true; +} +#endif //ASSERT + +#ifndef PRODUCT + +void CodeSection::dump() { + address ptr = start(); + for (csize_t step; ptr < end(); ptr += step) { + step = end() - ptr; + if (step > jintSize * 4) step = jintSize * 4; + tty->print(PTR_FORMAT ": ", ptr); + while (step > 0) { + tty->print(" " PTR32_FORMAT, *(jint*)ptr); + ptr += jintSize; + } + tty->cr(); + } +} + + +void CodeSection::decode() { + Disassembler::decode(start(), end()); +} + + +void CodeBuffer::block_comment(intptr_t offset, const char * comment) { + _comments.add_comment(offset, comment); +} + + +class CodeComment: public CHeapObj { + private: + friend class CodeComments; + intptr_t _offset; + const char * _comment; + CodeComment* _next; + + ~CodeComment() { + assert(_next == NULL, "wrong interface for freeing list"); + os::free((void*)_comment); + } + + public: + CodeComment(intptr_t offset, const char * comment) { + _offset = offset; + _comment = os::strdup(comment); + _next = NULL; + } + + intptr_t offset() const { return _offset; } + const char * comment() const { return _comment; } + CodeComment* next() { return _next; } + + void set_next(CodeComment* next) { _next = next; } + + CodeComment* find(intptr_t offset) { + CodeComment* a = this; + while (a != NULL && a->_offset != offset) { + a = a->_next; + } + return a; + } +}; + + +void CodeComments::add_comment(intptr_t offset, const char * comment) { + CodeComment* c = new CodeComment(offset, comment); + CodeComment* insert = NULL; + if (_comments != NULL) { + CodeComment* c = _comments->find(offset); + insert = c; + while (c && c->offset() == offset) { + insert = c; + c = c->next(); + } + } + if (insert) { + // insert after comments with same offset + c->set_next(insert->next()); + insert->set_next(c); + } else { + c->set_next(_comments); + _comments = c; + } +} + + +void CodeComments::assign(CodeComments& other) { + assert(_comments == NULL, "don't overwrite old value"); + _comments = other._comments; +} + + +void CodeComments::print_block_comment(outputStream* stream, intptr_t offset) { + if (_comments != NULL) { + CodeComment* c = _comments->find(offset); + while (c && c->offset() == offset) { + stream->print(" ;; "); + stream->print_cr(c->comment()); + c = c->next(); + } + } +} + + +void CodeComments::free() { + CodeComment* n = _comments; + while (n) { + // unlink the node from the list saving a pointer to the next + CodeComment* p = n->_next; + n->_next = NULL; + delete n; + n = p; + } + _comments = NULL; +} + + + +void CodeBuffer::decode() { + Disassembler::decode(decode_begin(), code_end()); + _decode_begin = code_end(); +} + + +void CodeBuffer::skip_decode() { + _decode_begin = code_end(); +} + + +void CodeBuffer::decode_all() { + for (int n = 0; n < (int)SECT_LIMIT; n++) { + // dump contents of each section + CodeSection* cs = code_section(n); + tty->print_cr("! %s:", code_section_name(n)); + if (cs != consts()) + cs->decode(); + else + cs->dump(); + } +} + + +void CodeSection::print(const char* name) { + csize_t locs_size = locs_end() - locs_start(); + tty->print_cr(" %7s.code = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d)%s", + name, start(), end(), limit(), size(), capacity(), + is_frozen()? " [frozen]": ""); + tty->print_cr(" %7s.locs = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d) point=%d", + name, locs_start(), locs_end(), locs_limit(), locs_size, locs_capacity(), locs_point_off()); + if (PrintRelocations) { + RelocIterator iter(this); + iter.print(); + } +} + +void CodeBuffer::print() { + if (this == NULL) { + tty->print_cr("NULL CodeBuffer pointer"); + return; + } + + tty->print_cr("CodeBuffer:"); + for (int n = 0; n < (int)SECT_LIMIT; n++) { + // print each section + CodeSection* cs = code_section(n); + cs->print(code_section_name(n)); + } +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/asm/codeBuffer.hpp b/hotspot/src/share/vm/asm/codeBuffer.hpp new file mode 100644 index 00000000000..96a983c300c --- /dev/null +++ b/hotspot/src/share/vm/asm/codeBuffer.hpp @@ -0,0 +1,542 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class CodeComments; +class AbstractAssembler; +class MacroAssembler; +class PhaseCFG; +class Compile; +class BufferBlob; +class CodeBuffer; + +class CodeOffsets: public StackObj { +public: + enum Entries { Entry, + Verified_Entry, + Frame_Complete, // Offset in the code where the frame setup is (for forte stackwalks) is complete + OSR_Entry, + Exceptions, // Offset where exception handler lives + Deopt, // Offset where deopt handler lives + max_Entries }; + + // special value to note codeBlobs where profile (forte) stack walking is + // always dangerous and suspect. + + enum { frame_never_safe = -1 }; + +private: + int _values[max_Entries]; + +public: + CodeOffsets() { + _values[Entry] = 0; + _values[Verified_Entry] = 0; + _values[Frame_Complete] = frame_never_safe; + _values[OSR_Entry] = 0; + _values[Exceptions] = -1; + _values[Deopt] = -1; + } + + int value(Entries e) { return _values[e]; } + void set_value(Entries e, int val) { _values[e] = val; } +}; + +// This class represents a stream of code and associated relocations. +// There are a few in each CodeBuffer. +// They are filled concurrently, and concatenated at the end. +class CodeSection VALUE_OBJ_CLASS_SPEC { + friend class CodeBuffer; + public: + typedef int csize_t; // code size type; would be size_t except for history + + private: + address _start; // first byte of contents (instructions) + address _mark; // user mark, usually an instruction beginning + address _end; // current end address + address _limit; // last possible (allocated) end address + relocInfo* _locs_start; // first byte of relocation information + relocInfo* _locs_end; // first byte after relocation information + relocInfo* _locs_limit; // first byte after relocation information buf + address _locs_point; // last relocated position (grows upward) + bool _locs_own; // did I allocate the locs myself? + bool _frozen; // no more expansion of this section + char _index; // my section number (SECT_INST, etc.) + CodeBuffer* _outer; // enclosing CodeBuffer + + // (Note: _locs_point used to be called _last_reloc_offset.) + + CodeSection() { + _start = NULL; + _mark = NULL; + _end = NULL; + _limit = NULL; + _locs_start = NULL; + _locs_end = NULL; + _locs_limit = NULL; + _locs_point = NULL; + _locs_own = false; + _frozen = false; + debug_only(_index = -1); + debug_only(_outer = (CodeBuffer*)badAddress); + } + + void initialize_outer(CodeBuffer* outer, int index) { + _outer = outer; + _index = index; + } + + void initialize(address start, csize_t size = 0) { + assert(_start == NULL, "only one init step, please"); + _start = start; + _mark = NULL; + _end = start; + + _limit = start + size; + _locs_point = start; + } + + void initialize_locs(int locs_capacity); + void expand_locs(int new_capacity); + void initialize_locs_from(const CodeSection* source_cs); + + // helper for CodeBuffer::expand() + void take_over_code_from(CodeSection* cs) { + _start = cs->_start; + _mark = cs->_mark; + _end = cs->_end; + _limit = cs->_limit; + _locs_point = cs->_locs_point; + } + + public: + address start() const { return _start; } + address mark() const { return _mark; } + address end() const { return _end; } + address limit() const { return _limit; } + csize_t size() const { return (csize_t)(_end - _start); } + csize_t mark_off() const { assert(_mark != NULL, "not an offset"); + return (csize_t)(_mark - _start); } + csize_t capacity() const { return (csize_t)(_limit - _start); } + csize_t remaining() const { return (csize_t)(_limit - _end); } + + relocInfo* locs_start() const { return _locs_start; } + relocInfo* locs_end() const { return _locs_end; } + int locs_count() const { return (int)(_locs_end - _locs_start); } + relocInfo* locs_limit() const { return _locs_limit; } + address locs_point() const { return _locs_point; } + csize_t locs_point_off() const{ return (csize_t)(_locs_point - _start); } + csize_t locs_capacity() const { return (csize_t)(_locs_limit - _locs_start); } + csize_t locs_remaining()const { return (csize_t)(_locs_limit - _locs_end); } + + int index() const { return _index; } + bool is_allocated() const { return _start != NULL; } + bool is_empty() const { return _start == _end; } + bool is_frozen() const { return _frozen; } + bool has_locs() const { return _locs_end != NULL; } + + CodeBuffer* outer() const { return _outer; } + + // is a given address in this section? (2nd version is end-inclusive) + bool contains(address pc) const { return pc >= _start && pc < _end; } + bool contains2(address pc) const { return pc >= _start && pc <= _end; } + bool allocates(address pc) const { return pc >= _start && pc < _limit; } + bool allocates2(address pc) const { return pc >= _start && pc <= _limit; } + + void set_end(address pc) { assert(allocates2(pc),""); _end = pc; } + void set_mark(address pc) { assert(contains2(pc),"not in codeBuffer"); + _mark = pc; } + void set_mark_off(int offset) { assert(contains2(offset+_start),"not in codeBuffer"); + _mark = offset + _start; } + void set_mark() { _mark = _end; } + void clear_mark() { _mark = NULL; } + + void set_locs_end(relocInfo* p) { + assert(p <= locs_limit(), "locs data fits in allocated buffer"); + _locs_end = p; + } + void set_locs_point(address pc) { + assert(pc >= locs_point(), "relocation addr may not decrease"); + assert(allocates2(pc), "relocation addr must be in this section"); + _locs_point = pc; + } + + // Share a scratch buffer for relocinfo. (Hacky; saves a resource allocation.) + void initialize_shared_locs(relocInfo* buf, int length); + + // Manage labels and their addresses. + address target(Label& L, address branch_pc); + + // Emit a relocation. + void relocate(address at, RelocationHolder const& rspec, int format = 0); + void relocate(address at, relocInfo::relocType rtype, int format = 0) { + if (rtype != relocInfo::none) + relocate(at, Relocation::spec_simple(rtype), format); + } + + // alignment requirement for starting offset + // Requirements are that the instruction area and the + // stubs area must start on CodeEntryAlignment, and + // the ctable on sizeof(jdouble) + int alignment() const { return MAX2((int)sizeof(jdouble), (int)CodeEntryAlignment); } + + // Slop between sections, used only when allocating temporary BufferBlob buffers. + static csize_t end_slop() { return MAX2((int)sizeof(jdouble), (int)CodeEntryAlignment); } + + csize_t align_at_start(csize_t off) const { return (csize_t) align_size_up(off, alignment()); } + + // Mark a section frozen. Assign its remaining space to + // the following section. It will never expand after this point. + inline void freeze(); // { _outer->freeze_section(this); } + + // Ensure there's enough space left in the current section. + // Return true if there was an expansion. + bool maybe_expand_to_ensure_remaining(csize_t amount); + +#ifndef PRODUCT + void decode(); + void dump(); + void print(const char* name); +#endif //PRODUCT +}; + +class CodeComment; +class CodeComments VALUE_OBJ_CLASS_SPEC { +private: +#ifndef PRODUCT + CodeComment* _comments; +#endif + +public: + CodeComments() { +#ifndef PRODUCT + _comments = NULL; +#endif + } + + void add_comment(intptr_t offset, const char * comment) PRODUCT_RETURN; + void print_block_comment(outputStream* stream, intptr_t offset) PRODUCT_RETURN; + void assign(CodeComments& other) PRODUCT_RETURN; + void free() PRODUCT_RETURN; +}; + + +// A CodeBuffer describes a memory space into which assembly +// code is generated. This memory space usually occupies the +// interior of a single BufferBlob, but in some cases it may be +// an arbitrary span of memory, even outside the code cache. +// +// A code buffer comes in two variants: +// +// (1) A CodeBuffer referring to an already allocated piece of memory: +// This is used to direct 'static' code generation (e.g. for interpreter +// or stubroutine generation, etc.). This code comes with NO relocation +// information. +// +// (2) A CodeBuffer referring to a piece of memory allocated when the +// CodeBuffer is allocated. This is used for nmethod generation. +// +// The memory can be divided up into several parts called sections. +// Each section independently accumulates code (or data) an relocations. +// Sections can grow (at the expense of a reallocation of the BufferBlob +// and recopying of all active sections). When the buffered code is finally +// written to an nmethod (or other CodeBlob), the contents (code, data, +// and relocations) of the sections are padded to an alignment and concatenated. +// Instructions and data in one section can contain relocatable references to +// addresses in a sibling section. + +class CodeBuffer: public StackObj { + friend class CodeSection; + + private: + // CodeBuffers must be allocated on the stack except for a single + // special case during expansion which is handled internally. This + // is done to guarantee proper cleanup of resources. + void* operator new(size_t size) { return ResourceObj::operator new(size); } + void operator delete(void* p) { ResourceObj::operator delete(p); } + + public: + typedef int csize_t; // code size type; would be size_t except for history + enum { + // Here is the list of all possible sections, in order of ascending address. + SECT_INSTS, // Executable instructions. + SECT_STUBS, // Outbound trampolines for supporting call sites. + SECT_CONSTS, // Non-instruction data: Floats, jump tables, etc. + SECT_LIMIT, SECT_NONE = -1 + }; + + private: + enum { + sect_bits = 2, // assert (SECT_LIMIT <= (1<index() == n || !cs->is_allocated(), "sanity"); + return cs; + } + const CodeSection* code_section(int n) const { // yucky const stuff + return ((CodeBuffer*)this)->code_section(n); + } + static const char* code_section_name(int n); + int section_index_of(address addr) const; + bool contains(address addr) const { + // handy for debugging + return section_index_of(addr) > SECT_NONE; + } + + // A stable mapping between 'locators' (small ints) and addresses. + static int locator_pos(int locator) { return locator >> sect_bits; } + static int locator_sect(int locator) { return locator & sect_mask; } + static int locator(int pos, int sect) { return (pos << sect_bits) | sect; } + int locator(address addr) const; + address locator_address(int locator) const; + + // Properties + const char* name() const { return _name; } + CodeBuffer* before_expand() const { return _before_expand; } + BufferBlob* blob() const { return _blob; } + void set_blob(BufferBlob* blob); + void free_blob(); // Free the blob, if we own one. + + // Properties relative to the insts section: + address code_begin() const { return _insts.start(); } + address code_end() const { return _insts.end(); } + void set_code_end(address end) { _insts.set_end(end); } + address code_limit() const { return _insts.limit(); } + address inst_mark() const { return _insts.mark(); } + void set_inst_mark() { _insts.set_mark(); } + void clear_inst_mark() { _insts.clear_mark(); } + + // is there anything in the buffer other than the current section? + bool is_pure() const { return code_size() == total_code_size(); } + + // size in bytes of output so far in the insts sections + csize_t code_size() const { return _insts.size(); } + + // same as code_size(), except that it asserts there is no non-code here + csize_t pure_code_size() const { assert(is_pure(), "no non-code"); + return code_size(); } + // capacity in bytes of the insts sections + csize_t code_capacity() const { return _insts.capacity(); } + + // number of bytes remaining in the insts section + csize_t code_remaining() const { return _insts.remaining(); } + + // is a given address in the insts section? (2nd version is end-inclusive) + bool code_contains(address pc) const { return _insts.contains(pc); } + bool code_contains2(address pc) const { return _insts.contains2(pc); } + + // allocated size of code in all sections, when aligned and concatenated + // (this is the eventual state of the code in its final CodeBlob) + csize_t total_code_size() const; + + // combined offset (relative to start of insts) of given address, + // as eventually found in the final CodeBlob + csize_t total_offset_of(address addr) const; + + // allocated size of all relocation data, including index, rounded up + csize_t total_relocation_size() const; + + // allocated size of any and all recorded oops + csize_t total_oop_size() const { + OopRecorder* recorder = oop_recorder(); + return (recorder == NULL)? 0: recorder->oop_size(); + } + + // Configuration functions, called immediately after the CB is constructed. + // The section sizes are subtracted from the original insts section. + // Note: Call them in reverse section order, because each steals from insts. + void initialize_consts_size(csize_t size) { initialize_section_size(&_consts, size); } + void initialize_stubs_size(csize_t size) { initialize_section_size(&_stubs, size); } + // Override default oop recorder. + void initialize_oop_recorder(OopRecorder* r); + + OopRecorder* oop_recorder() const { return _oop_recorder; } + CodeComments& comments() { return _comments; } + + // Code generation + void relocate(address at, RelocationHolder const& rspec, int format = 0) { + _insts.relocate(at, rspec, format); + } + void relocate(address at, relocInfo::relocType rtype, int format = 0) { + _insts.relocate(at, rtype, format); + } + + // Management of overflow storage for binding of Labels. + GrowableArray* create_patch_overflow(); + + // NMethod generation + void copy_code_and_locs_to(CodeBlob* blob) { + assert(blob != NULL, "sane"); + copy_relocations_to(blob); + copy_code_to(blob); + } + void copy_oops_to(CodeBlob* blob) { + if (!oop_recorder()->is_unused()) { + oop_recorder()->copy_to(blob); + } + } + + // Transform an address from the code in this code buffer to a specified code buffer + address transform_address(const CodeBuffer &cb, address addr) const; + + void block_comment(intptr_t offset, const char * comment) PRODUCT_RETURN; + +#ifndef PRODUCT + public: + // Printing / Decoding + // decodes from decode_begin() to code_end() and sets decode_begin to end + void decode(); + void decode_all(); // decodes all the code + void skip_decode(); // sets decode_begin to code_end(); + void print(); +#endif + + + // The following header contains architecture-specific implementations + #include "incls/_codeBuffer_pd.hpp.incl" +}; + + +inline void CodeSection::freeze() { + _outer->freeze_section(this); +} + +inline bool CodeSection::maybe_expand_to_ensure_remaining(csize_t amount) { + if (remaining() < amount) { _outer->expand(this, amount); return true; } + return false; +} diff --git a/hotspot/src/share/vm/asm/register.cpp b/hotspot/src/share/vm/asm/register.cpp new file mode 100644 index 00000000000..0765701b40d --- /dev/null +++ b/hotspot/src/share/vm/asm/register.cpp @@ -0,0 +1,29 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_register.cpp.incl" + + +// Intentionally left blank diff --git a/hotspot/src/share/vm/asm/register.hpp b/hotspot/src/share/vm/asm/register.hpp new file mode 100644 index 00000000000..526b6e5e0d1 --- /dev/null +++ b/hotspot/src/share/vm/asm/register.hpp @@ -0,0 +1,211 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Use AbstractRegister as shortcut +class AbstractRegisterImpl; +typedef AbstractRegisterImpl* AbstractRegister; + + +// The super class for platform specific registers. Instead of using value objects, +// registers are implemented as pointers. Subclassing is used so all registers can +// use the debugging suport below. No virtual functions are used for efficiency. +// They are canonicalized; i.e., registers are equal if their pointers are equal, +// and vice versa. A concrete implementation may just map the register onto 'this'. + +class AbstractRegisterImpl { + protected: + int value() const { return (int)(intx)this; } +}; + + +// +// Macros for use in defining Register instances. We'd like to be +// able to simply define const instances of the RegisterImpl* for each +// of the registers needed on a system in a header file. However many +// compilers don't handle this very well and end up producing a +// private definition in every file which includes the header file. +// Along with the static constructors necessary for initialization it +// can consume a significant amount of space in the result library. +// +// The following macros allow us to declare the instance in a .hpp and +// produce an enumeration value which has the same number. Then in a +// .cpp the the register instance can be defined using the enumeration +// value. This avoids the use of static constructors and multiple +// definitions per .cpp. In addition #defines for the register can be +// produced so that the constant registers can be inlined. These +// macros should not be used inside other macros, because you may get +// multiple evaluations of the macros which can give bad results. +// +// Here are some example uses and expansions. Note that the macro +// invocation is terminated with a ;. +// +// CONSTANT_REGISTER_DECLARATION(Register, G0, 0); +// +// extern const Register G0 ; +// enum { G0_RegisterEnumValue = 0 } ; +// +// REGISTER_DECLARATION(Register, Gmethod, G5); +// +// extern const Register Gmethod ; +// enum { Gmethod_RegisterEnumValue = G5_RegisterEnumValue } ; +// +// REGISTER_DEFINITION(Register, G0); +// +// const Register G0 = ( ( Register ) G0_RegisterEnumValue ) ; +// + +#define AS_REGISTER(type,name) ((type)name##_##type##EnumValue) + +#define CONSTANT_REGISTER_DECLARATION(type, name, value) \ +extern const type name; \ +enum { name##_##type##EnumValue = (value) } + +#define REGISTER_DECLARATION(type, name, value) \ +extern const type name; \ +enum { name##_##type##EnumValue = value##_##type##EnumValue } + +#define REGISTER_DEFINITION(type, name) \ +const type name = ((type)name##_##type##EnumValue) + + + +// Debugging support + +inline void assert_different_registers( + AbstractRegister a, + AbstractRegister b +) { + assert( + a != b, + "registers must be different" + ); +} + + +inline void assert_different_registers( + AbstractRegister a, + AbstractRegister b, + AbstractRegister c +) { + assert( + a != b && a != c + && b != c, + "registers must be different" + ); +} + + +inline void assert_different_registers( + AbstractRegister a, + AbstractRegister b, + AbstractRegister c, + AbstractRegister d +) { + assert( + a != b && a != c && a != d + && b != c && b != d + && c != d, + "registers must be different" + ); +} + + +inline void assert_different_registers( + AbstractRegister a, + AbstractRegister b, + AbstractRegister c, + AbstractRegister d, + AbstractRegister e +) { + assert( + a != b && a != c && a != d && a != e + && b != c && b != d && b != e + && c != d && c != e + && d != e, + "registers must be different" + ); +} + + +inline void assert_different_registers( + AbstractRegister a, + AbstractRegister b, + AbstractRegister c, + AbstractRegister d, + AbstractRegister e, + AbstractRegister f +) { + assert( + a != b && a != c && a != d && a != e && a != f + && b != c && b != d && b != e && b != f + && c != d && c != e && c != f + && d != e && d != f + && e != f, + "registers must be different" + ); +} + + +inline void assert_different_registers( + AbstractRegister a, + AbstractRegister b, + AbstractRegister c, + AbstractRegister d, + AbstractRegister e, + AbstractRegister f, + AbstractRegister g +) { + assert( + a != b && a != c && a != d && a != e && a != f && a != g + && b != c && b != d && b != e && b != f && b != g + && c != d && c != e && c != f && c != g + && d != e && d != f && d != g + && e != f && e != g + && f != g, + "registers must be different" + ); +} + + +inline void assert_different_registers( + AbstractRegister a, + AbstractRegister b, + AbstractRegister c, + AbstractRegister d, + AbstractRegister e, + AbstractRegister f, + AbstractRegister g, + AbstractRegister h +) { + assert( + a != b && a != c && a != d && a != e && a != f && a != g && a != h + && b != c && b != d && b != e && b != f && b != g && b != h + && c != d && c != e && c != f && c != g && c != h + && d != e && d != f && d != g && d != h + && e != f && e != g && e != h + && f != g && f != h + && g != h, + "registers must be different" + ); +} diff --git a/hotspot/src/share/vm/c1/c1_CFGPrinter.cpp b/hotspot/src/share/vm/c1/c1_CFGPrinter.cpp new file mode 100644 index 00000000000..a07e07e423f --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_CFGPrinter.cpp @@ -0,0 +1,378 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_CFGPrinter.cpp.incl" + +#ifndef PRODUCT + + +class CFGPrinterOutput : public CHeapObj { + private: + outputStream* _output; + + Compilation* _compilation; + bool _do_print_HIR; + bool _do_print_LIR; + + class PrintBlockClosure: public BlockClosure { + void block_do(BlockBegin* block) { if (block != NULL) CFGPrinter::output()->print_block(block); } + }; + + + outputStream* output() { assert(_output != NULL, ""); return _output; } + + void inc_indent(); + void dec_indent(); + void print(const char* format, ...); + void print_begin(const char* tag); + void print_end(const char* tag); + + char* method_name(ciMethod* method, bool short_name = false); + + public: + CFGPrinterOutput(); + + void set_compilation(Compilation* compilation) { _compilation = compilation; } + void set_print_flags(bool do_print_HIR, bool do_print_LIR) { _do_print_HIR = do_print_HIR; _do_print_LIR = do_print_LIR; } + + void print_compilation(); + void print_intervals(IntervalList* intervals, const char* name); + + void print_state(BlockBegin* block); + void print_operand(Value instr); + void print_HIR(Value instr); + void print_HIR(BlockBegin* block); + void print_LIR(BlockBegin* block); + void print_block(BlockBegin* block); + void print_cfg(BlockList* blocks, const char* name); + void print_cfg(IR* blocks, const char* name); +}; + +CFGPrinterOutput* CFGPrinter::_output = NULL; + + + + +void CFGPrinter::print_compilation(Compilation* compilation) { + if (_output == NULL) { + _output = new CFGPrinterOutput(); + } + output()->set_compilation(compilation); + output()->print_compilation(); +} + +void CFGPrinter::print_cfg(BlockList* blocks, const char* name, bool do_print_HIR, bool do_print_LIR) { + output()->set_print_flags(do_print_HIR, do_print_LIR); + output()->print_cfg(blocks, name); +} + +void CFGPrinter::print_cfg(IR* blocks, const char* name, bool do_print_HIR, bool do_print_LIR) { + output()->set_print_flags(do_print_HIR, do_print_LIR); + output()->print_cfg(blocks, name); +} + + +void CFGPrinter::print_intervals(IntervalList* intervals, const char* name) { + output()->print_intervals(intervals, name); +} + + + +CFGPrinterOutput::CFGPrinterOutput() + : _output(new(ResourceObj::C_HEAP) fileStream("output.cfg")) +{ +} + + + +void CFGPrinterOutput::inc_indent() { + output()->inc(); + output()->inc(); +} + +void CFGPrinterOutput::dec_indent() { + output()->dec(); + output()->dec(); +} + +void CFGPrinterOutput::print(const char* format, ...) { + output()->indent(); + + va_list ap; + va_start(ap, format); + output()->vprint_cr(format, ap); + va_end(ap); +} + +void CFGPrinterOutput::print_begin(const char* tag) { + output()->indent(); + output()->print_cr("begin_%s", tag); + inc_indent(); +} + +void CFGPrinterOutput::print_end(const char* tag) { + dec_indent(); + output()->indent(); + output()->print_cr("end_%s", tag); +} + + +char* CFGPrinterOutput::method_name(ciMethod* method, bool short_name) { + stringStream name; + if (short_name) { + method->print_short_name(&name); + } else { + method->print_name(&name); + } + return name.as_string(); + +} + + +void CFGPrinterOutput::print_compilation() { + print_begin("compilation"); + + print("name \"%s\"", method_name(_compilation->method(), true)); + print("method \"%s\"", method_name(_compilation->method())); + print("date "INT64_FORMAT, os::javaTimeMillis()); + + print_end("compilation"); +} + + + + + +void CFGPrinterOutput::print_state(BlockBegin* block) { + print_begin("states"); + + InstructionPrinter ip(true, output()); + + ValueStack* state = block->state(); + int index; + Value value; + + if (state->stack_size() > 0) { + print_begin("stack"); + print("size %d", state->stack_size()); + + for_each_stack_value(state, index, value) { + ip.print_phi(index, value, block); + print_operand(value); + output()->cr(); + } + + print_end("stack"); + } + + if (state->locks_size() > 0) { + print_begin("locks"); + print("size %d", state->locks_size()); + + for_each_lock_value(state, index, value) { + ip.print_phi(index, value, block); + print_operand(value); + output()->cr(); + } + print_end("locks"); + } + + for_each_state(state) { + print_begin("locals"); + print("size %d", state->locals_size()); + print("method \"%s\"", method_name(state->scope()->method())); + + for_each_local_value(state, index, value) { + ip.print_phi(index, value, block); + print_operand(value); + output()->cr(); + } + print_end("locals"); + } + + print_end("states"); +} + + +void CFGPrinterOutput::print_operand(Value instr) { + if (instr->operand()->is_virtual()) { + output()->print(" \""); + instr->operand()->print(output()); + output()->print("\" "); + } +} + +void CFGPrinterOutput::print_HIR(Value instr) { + InstructionPrinter ip(true, output()); + + if (instr->is_pinned()) { + output()->put('.'); + } + output()->print("%d %d ", instr->bci(), instr->use_count()); + + print_operand(instr); + + ip.print_temp(instr); + output()->print(" "); + ip.print_instr(instr); + + output()->print_cr(" <|@"); +} + +void CFGPrinterOutput::print_HIR(BlockBegin* block) { + print_begin("HIR"); + + Value cur = block->next(); + while (cur != NULL) { + print_HIR(cur); + cur = cur->next(); + } + + print_end("HIR"); +} + +void CFGPrinterOutput::print_LIR(BlockBegin* block) { + print_begin("LIR"); + + for (int i = 0; i < block->lir()->length(); i++) { + block->lir()->at(i)->print_on(output()); + output()->print_cr(" <|@ "); + } + + print_end("LIR"); +} + + +void CFGPrinterOutput::print_block(BlockBegin* block) { + print_begin("block"); + + print("name \"B%d\"", block->block_id()); + + print("from_bci %d", block->bci()); + print("to_bci %d", (block->end() == NULL ? -1 : block->end()->bci())); + + output()->indent(); + output()->print("predecessors "); + int i; + for (i = 0; i < block->number_of_preds(); i++) { + output()->print("\"B%d\" ", block->pred_at(i)->block_id()); + } + output()->cr(); + + output()->indent(); + output()->print("successors "); + for (i = 0; i < block->number_of_sux(); i++) { + output()->print("\"B%d\" ", block->sux_at(i)->block_id()); + } + output()->cr(); + + output()->indent(); + output()->print("xhandlers"); + for (i = 0; i < block->number_of_exception_handlers(); i++) { + output()->print("\"B%d\" ", block->exception_handler_at(i)->block_id()); + } + output()->cr(); + + output()->indent(); + output()->print("flags "); + if (block->is_set(BlockBegin::std_entry_flag)) output()->print("\"std\" "); + if (block->is_set(BlockBegin::osr_entry_flag)) output()->print("\"osr\" "); + if (block->is_set(BlockBegin::exception_entry_flag)) output()->print("\"ex\" "); + if (block->is_set(BlockBegin::subroutine_entry_flag)) output()->print("\"sr\" "); + if (block->is_set(BlockBegin::backward_branch_target_flag)) output()->print("\"bb\" "); + if (block->is_set(BlockBegin::parser_loop_header_flag)) output()->print("\"plh\" "); + if (block->is_set(BlockBegin::critical_edge_split_flag)) output()->print("\"ces\" "); + if (block->is_set(BlockBegin::linear_scan_loop_header_flag)) output()->print("\"llh\" "); + if (block->is_set(BlockBegin::linear_scan_loop_end_flag)) output()->print("\"lle\" "); + output()->cr(); + + if (block->dominator() != NULL) { + print("dominator \"B%d\"", block->dominator()->block_id()); + } + if (block->loop_index() != -1) { + print("loop_index %d", block->loop_index()); + print("loop_depth %d", block->loop_depth()); + } + + if (block->first_lir_instruction_id() != -1) { + print("first_lir_id %d", block->first_lir_instruction_id()); + print("last_lir_id %d", block->last_lir_instruction_id()); + } + + if (_do_print_HIR) { + print_state(block); + print_HIR(block); + } + + if (_do_print_LIR) { + print_LIR(block); + } + + print_end("block"); +} + + + +void CFGPrinterOutput::print_cfg(BlockList* blocks, const char* name) { + print_begin("cfg"); + print("name \"%s\"", name); + + PrintBlockClosure print_block; + blocks->iterate_forward(&print_block); + + print_end("cfg"); + output()->flush(); +} + +void CFGPrinterOutput::print_cfg(IR* blocks, const char* name) { + print_begin("cfg"); + print("name \"%s\"", name); + + PrintBlockClosure print_block; + blocks->iterate_preorder(&print_block); + + print_end("cfg"); + output()->flush(); +} + + + + +void CFGPrinterOutput::print_intervals(IntervalList* intervals, const char* name) { + print_begin("intervals"); + print("name \"%s\"", name); + + for (int i = 0; i < intervals->length(); i++) { + if (intervals->at(i) != NULL) { + intervals->at(i)->print(output()); + } + } + + print_end("intervals"); + output()->flush(); +} + + +#endif diff --git a/hotspot/src/share/vm/c1/c1_CFGPrinter.hpp b/hotspot/src/share/vm/c1/c1_CFGPrinter.hpp new file mode 100644 index 00000000000..a0c7d21c168 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_CFGPrinter.hpp @@ -0,0 +1,46 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef PRODUCT + +// This is a utility class used for recording the results of a +// compilation for later analysis. + +class CFGPrinterOutput; +class IntervalList; + +class CFGPrinter : public AllStatic { +private: + static CFGPrinterOutput* _output; +public: + static CFGPrinterOutput* output() { assert(_output != NULL, ""); return _output; } + + + static void print_compilation(Compilation* compilation); + static void print_cfg(BlockList* blocks, const char* name, bool do_print_HIR, bool do_print_LIR); + static void print_cfg(IR* blocks, const char* name, bool do_print_HIR, bool do_print_LIR); + static void print_intervals(IntervalList* intervals, const char* name); +}; + +#endif diff --git a/hotspot/src/share/vm/c1/c1_Canonicalizer.cpp b/hotspot/src/share/vm/c1/c1_Canonicalizer.cpp new file mode 100644 index 00000000000..8efd3b58009 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Canonicalizer.cpp @@ -0,0 +1,877 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_Canonicalizer.cpp.incl" + + +static void do_print_value(Value* vp) { + (*vp)->print_line(); +} + +void Canonicalizer::set_canonical(Value x) { + assert(x != NULL, "value must exist"); + // Note: we can not currently substitute root nodes which show up in + // the instruction stream (because the instruction list is embedded + // in the instructions). + if (canonical() != x) { + if (PrintCanonicalization) { + canonical()->input_values_do(do_print_value); + canonical()->print_line(); + tty->print_cr("canonicalized to:"); + x->input_values_do(do_print_value); + x->print_line(); + tty->cr(); + } + assert(_canonical->type()->tag() == x->type()->tag(), "types must match"); + _canonical = x; + } +} + + +void Canonicalizer::move_const_to_right(Op2* x) { + if (x->x()->type()->is_constant() && x->is_commutative()) x->swap_operands(); +} + + +void Canonicalizer::do_Op2(Op2* x) { + if (x->x() == x->y()) { + switch (x->op()) { + case Bytecodes::_isub: set_constant(0); return; + case Bytecodes::_lsub: set_constant(jlong_cast(0)); return; + case Bytecodes::_iand: // fall through + case Bytecodes::_land: // fall through + case Bytecodes::_ior: // fall through + case Bytecodes::_lor : set_canonical(x->x()); return; + case Bytecodes::_ixor: set_constant(0); return; + case Bytecodes::_lxor: set_constant(jlong_cast(0)); return; + } + } + + if (x->x()->type()->is_constant() && x->y()->type()->is_constant()) { + // do constant folding for selected operations + switch (x->type()->tag()) { + case intTag: + { jint a = x->x()->type()->as_IntConstant()->value(); + jint b = x->y()->type()->as_IntConstant()->value(); + switch (x->op()) { + case Bytecodes::_iadd: set_constant(a + b); return; + case Bytecodes::_isub: set_constant(a - b); return; + case Bytecodes::_imul: set_constant(a * b); return; + case Bytecodes::_idiv: + if (b != 0) { + if (a == min_jint && b == -1) { + set_constant(min_jint); + } else { + set_constant(a / b); + } + return; + } + break; + case Bytecodes::_irem: + if (b != 0) { + if (a == min_jint && b == -1) { + set_constant(0); + } else { + set_constant(a % b); + } + return; + } + break; + case Bytecodes::_iand: set_constant(a & b); return; + case Bytecodes::_ior : set_constant(a | b); return; + case Bytecodes::_ixor: set_constant(a ^ b); return; + } + } + break; + case longTag: + { jlong a = x->x()->type()->as_LongConstant()->value(); + jlong b = x->y()->type()->as_LongConstant()->value(); + switch (x->op()) { + case Bytecodes::_ladd: set_constant(a + b); return; + case Bytecodes::_lsub: set_constant(a - b); return; + case Bytecodes::_lmul: set_constant(a * b); return; + case Bytecodes::_ldiv: + if (b != 0) { + set_constant(SharedRuntime::ldiv(b, a)); + return; + } + break; + case Bytecodes::_lrem: + if (b != 0) { + set_constant(SharedRuntime::lrem(b, a)); + return; + } + break; + case Bytecodes::_land: set_constant(a & b); return; + case Bytecodes::_lor : set_constant(a | b); return; + case Bytecodes::_lxor: set_constant(a ^ b); return; + } + } + break; + // other cases not implemented (must be extremely careful with floats & doubles!) + } + } + // make sure constant is on the right side, if any + move_const_to_right(x); + + if (x->y()->type()->is_constant()) { + // do constant folding for selected operations + switch (x->type()->tag()) { + case intTag: + if (x->y()->type()->as_IntConstant()->value() == 0) { + switch (x->op()) { + case Bytecodes::_iadd: set_canonical(x->x()); return; + case Bytecodes::_isub: set_canonical(x->x()); return; + case Bytecodes::_imul: set_constant(0); return; + // Note: for div and rem, make sure that C semantics + // corresponds to Java semantics! + case Bytecodes::_iand: set_constant(0); return; + case Bytecodes::_ior : set_canonical(x->x()); return; + } + } + break; + case longTag: + if (x->y()->type()->as_LongConstant()->value() == (jlong)0) { + switch (x->op()) { + case Bytecodes::_ladd: set_canonical(x->x()); return; + case Bytecodes::_lsub: set_canonical(x->x()); return; + case Bytecodes::_lmul: set_constant((jlong)0); return; + // Note: for div and rem, make sure that C semantics + // corresponds to Java semantics! + case Bytecodes::_land: set_constant((jlong)0); return; + case Bytecodes::_lor : set_canonical(x->x()); return; + } + } + break; + } + } +} + + +void Canonicalizer::do_Phi (Phi* x) {} +void Canonicalizer::do_Constant (Constant* x) {} +void Canonicalizer::do_Local (Local* x) {} +void Canonicalizer::do_LoadField (LoadField* x) {} + +// checks if v is in the block that is currently processed by +// GraphBuilder. This is the only block that has not BlockEnd yet. +static bool in_current_block(Value v) { + int max_distance = 4; + while (max_distance > 0 && v != NULL && v->as_BlockEnd() == NULL) { + v = v->next(); + max_distance--; + } + return v == NULL; +} + +void Canonicalizer::do_StoreField (StoreField* x) { + // If a value is going to be stored into a field or array some of + // the conversions emitted by javac are unneeded because the fields + // are packed to their natural size. + Convert* conv = x->value()->as_Convert(); + if (conv) { + Value value = NULL; + BasicType type = x->field()->type()->basic_type(); + switch (conv->op()) { + case Bytecodes::_i2b: if (type == T_BYTE) value = conv->value(); break; + case Bytecodes::_i2s: if (type == T_SHORT || type == T_BYTE) value = conv->value(); break; + case Bytecodes::_i2c: if (type == T_CHAR || type == T_BYTE) value = conv->value(); break; + } + // limit this optimization to current block + if (value != NULL && in_current_block(conv)) { + set_canonical(new StoreField(x->obj(), x->offset(), x->field(), value, x->is_static(), + x->lock_stack(), x->state_before(), x->is_loaded(), x->is_initialized())); + return; + } + } + +} + +void Canonicalizer::do_ArrayLength (ArrayLength* x) { + NewArray* array = x->array()->as_NewArray(); + if (array != NULL && array->length() != NULL) { + Constant* length = array->length()->as_Constant(); + if (length != NULL) { + // do not use the Constant itself, but create a new Constant + // with same value Otherwise a Constant is live over multiple + // blocks without being registered in a state array. + assert(length->type()->as_IntConstant() != NULL, "array length must be integer"); + set_constant(length->type()->as_IntConstant()->value()); + } + } else { + LoadField* lf = x->array()->as_LoadField(); + if (lf != NULL && lf->field()->is_constant()) { + ciObject* c = lf->field()->constant_value().as_object(); + if (c->is_array()) { + ciArray* array = (ciArray*) c; + set_constant(array->length()); + } + } + } +} + +void Canonicalizer::do_LoadIndexed (LoadIndexed* x) {} +void Canonicalizer::do_StoreIndexed (StoreIndexed* x) { + // If a value is going to be stored into a field or array some of + // the conversions emitted by javac are unneeded because the fields + // are packed to their natural size. + Convert* conv = x->value()->as_Convert(); + if (conv) { + Value value = NULL; + BasicType type = x->elt_type(); + switch (conv->op()) { + case Bytecodes::_i2b: if (type == T_BYTE) value = conv->value(); break; + case Bytecodes::_i2s: if (type == T_SHORT || type == T_BYTE) value = conv->value(); break; + case Bytecodes::_i2c: if (type == T_CHAR || type == T_BYTE) value = conv->value(); break; + } + // limit this optimization to current block + if (value != NULL && in_current_block(conv)) { + set_canonical(new StoreIndexed(x->array(), x->index(), x->length(), + x->elt_type(), value, x->lock_stack())); + return; + } + } + + +} + + +void Canonicalizer::do_NegateOp(NegateOp* x) { + ValueType* t = x->x()->type(); + if (t->is_constant()) { + switch (t->tag()) { + case intTag : set_constant(-t->as_IntConstant ()->value()); return; + case longTag : set_constant(-t->as_LongConstant ()->value()); return; + case floatTag : set_constant(-t->as_FloatConstant ()->value()); return; + case doubleTag: set_constant(-t->as_DoubleConstant()->value()); return; + default : ShouldNotReachHere(); + } + } +} + + +void Canonicalizer::do_ArithmeticOp (ArithmeticOp* x) { do_Op2(x); } + + +void Canonicalizer::do_ShiftOp (ShiftOp* x) { + ValueType* t = x->x()->type(); + ValueType* t2 = x->y()->type(); + if (t->is_constant()) { + switch (t->tag()) { + case intTag : if (t->as_IntConstant()->value() == 0) { set_constant(0); return; } break; + case longTag : if (t->as_LongConstant()->value() == (jlong)0) { set_constant(jlong_cast(0)); return; } break; + default : ShouldNotReachHere(); + } + if (t2->is_constant()) { + if (t->tag() == intTag) { + int value = t->as_IntConstant()->value(); + int shift = t2->as_IntConstant()->value() & 31; + jint mask = ~(~0 << (32 - shift)); + if (shift == 0) mask = ~0; + switch (x->op()) { + case Bytecodes::_ishl: set_constant(value << shift); return; + case Bytecodes::_ishr: set_constant(value >> shift); return; + case Bytecodes::_iushr: set_constant((value >> shift) & mask); return; + } + } else if (t->tag() == longTag) { + jlong value = t->as_LongConstant()->value(); + int shift = t2->as_IntConstant()->value() & 63; + jlong mask = ~(~jlong_cast(0) << (64 - shift)); + if (shift == 0) mask = ~jlong_cast(0); + switch (x->op()) { + case Bytecodes::_lshl: set_constant(value << shift); return; + case Bytecodes::_lshr: set_constant(value >> shift); return; + case Bytecodes::_lushr: set_constant((value >> shift) & mask); return; + } + } + } + } + if (t2->is_constant()) { + switch (t2->tag()) { + case intTag : if (t2->as_IntConstant()->value() == 0) set_canonical(x->x()); return; + case longTag : if (t2->as_IntConstant()->value() == 0) set_canonical(x->x()); return; + default : ShouldNotReachHere(); + } + } +} + + +void Canonicalizer::do_LogicOp (LogicOp* x) { do_Op2(x); } +void Canonicalizer::do_CompareOp (CompareOp* x) { + if (x->x() == x->y()) { + switch (x->x()->type()->tag()) { + case longTag: set_constant(0); break; + case floatTag: { + FloatConstant* fc = x->x()->type()->as_FloatConstant(); + if (fc) { + if (g_isnan(fc->value())) { + set_constant(x->op() == Bytecodes::_fcmpl ? -1 : 1); + } else { + set_constant(0); + } + } + break; + } + case doubleTag: { + DoubleConstant* dc = x->x()->type()->as_DoubleConstant(); + if (dc) { + if (g_isnan(dc->value())) { + set_constant(x->op() == Bytecodes::_dcmpl ? -1 : 1); + } else { + set_constant(0); + } + } + break; + } + } + } else if (x->x()->type()->is_constant() && x->y()->type()->is_constant()) { + switch (x->x()->type()->tag()) { + case longTag: { + jlong vx = x->x()->type()->as_LongConstant()->value(); + jlong vy = x->y()->type()->as_LongConstant()->value(); + if (vx == vy) + set_constant(0); + else if (vx < vy) + set_constant(-1); + else + set_constant(1); + break; + } + + case floatTag: { + float vx = x->x()->type()->as_FloatConstant()->value(); + float vy = x->y()->type()->as_FloatConstant()->value(); + if (g_isnan(vx) || g_isnan(vy)) + set_constant(x->op() == Bytecodes::_fcmpl ? -1 : 1); + else if (vx == vy) + set_constant(0); + else if (vx < vy) + set_constant(-1); + else + set_constant(1); + break; + } + + case doubleTag: { + double vx = x->x()->type()->as_DoubleConstant()->value(); + double vy = x->y()->type()->as_DoubleConstant()->value(); + if (g_isnan(vx) || g_isnan(vy)) + set_constant(x->op() == Bytecodes::_dcmpl ? -1 : 1); + else if (vx == vy) + set_constant(0); + else if (vx < vy) + set_constant(-1); + else + set_constant(1); + break; + } + } + + } +} + + +void Canonicalizer::do_IfInstanceOf(IfInstanceOf* x) {} + +void Canonicalizer::do_IfOp(IfOp* x) { + // Caution: do not use do_Op2(x) here for now since + // we map the condition to the op for now! + move_const_to_right(x); +} + + +void Canonicalizer::do_Intrinsic (Intrinsic* x) { + switch (x->id()) { + case vmIntrinsics::_floatToRawIntBits : { + FloatConstant* c = x->argument_at(0)->type()->as_FloatConstant(); + if (c != NULL) { + JavaValue v; + v.set_jfloat(c->value()); + set_constant(v.get_jint()); + } + break; + } + case vmIntrinsics::_intBitsToFloat : { + IntConstant* c = x->argument_at(0)->type()->as_IntConstant(); + if (c != NULL) { + JavaValue v; + v.set_jint(c->value()); + set_constant(v.get_jfloat()); + } + break; + } + case vmIntrinsics::_doubleToRawLongBits : { + DoubleConstant* c = x->argument_at(0)->type()->as_DoubleConstant(); + if (c != NULL) { + JavaValue v; + v.set_jdouble(c->value()); + set_constant(v.get_jlong()); + } + break; + } + case vmIntrinsics::_longBitsToDouble : { + LongConstant* c = x->argument_at(0)->type()->as_LongConstant(); + if (c != NULL) { + JavaValue v; + v.set_jlong(c->value()); + set_constant(v.get_jdouble()); + } + break; + } + } +} + +void Canonicalizer::do_Convert (Convert* x) { + if (x->value()->type()->is_constant()) { + switch (x->op()) { + case Bytecodes::_i2b: set_constant((int)((x->value()->type()->as_IntConstant()->value() << 24) >> 24)); break; + case Bytecodes::_i2s: set_constant((int)((x->value()->type()->as_IntConstant()->value() << 16) >> 16)); break; + case Bytecodes::_i2c: set_constant((int)(x->value()->type()->as_IntConstant()->value() & ((1<<16)-1))); break; + case Bytecodes::_i2l: set_constant((jlong)(x->value()->type()->as_IntConstant()->value())); break; + case Bytecodes::_i2f: set_constant((float)(x->value()->type()->as_IntConstant()->value())); break; + case Bytecodes::_i2d: set_constant((double)(x->value()->type()->as_IntConstant()->value())); break; + case Bytecodes::_l2i: set_constant((int)(x->value()->type()->as_LongConstant()->value())); break; + case Bytecodes::_l2f: set_constant(SharedRuntime::l2f(x->value()->type()->as_LongConstant()->value())); break; + case Bytecodes::_l2d: set_constant(SharedRuntime::l2d(x->value()->type()->as_LongConstant()->value())); break; + case Bytecodes::_f2d: set_constant((double)(x->value()->type()->as_FloatConstant()->value())); break; + case Bytecodes::_f2i: set_constant(SharedRuntime::f2i(x->value()->type()->as_FloatConstant()->value())); break; + case Bytecodes::_f2l: set_constant(SharedRuntime::f2l(x->value()->type()->as_FloatConstant()->value())); break; + case Bytecodes::_d2f: set_constant((float)(x->value()->type()->as_DoubleConstant()->value())); break; + case Bytecodes::_d2i: set_constant(SharedRuntime::d2i(x->value()->type()->as_DoubleConstant()->value())); break; + case Bytecodes::_d2l: set_constant(SharedRuntime::d2l(x->value()->type()->as_DoubleConstant()->value())); break; + default: + ShouldNotReachHere(); + } + } + + Value value = x->value(); + BasicType type = T_ILLEGAL; + LoadField* lf = value->as_LoadField(); + if (lf) { + type = lf->field_type(); + } else { + LoadIndexed* li = value->as_LoadIndexed(); + if (li) { + type = li->elt_type(); + } else { + Convert* conv = value->as_Convert(); + if (conv) { + switch (conv->op()) { + case Bytecodes::_i2b: type = T_BYTE; break; + case Bytecodes::_i2s: type = T_SHORT; break; + case Bytecodes::_i2c: type = T_CHAR; break; + } + } + } + } + if (type != T_ILLEGAL) { + switch (x->op()) { + case Bytecodes::_i2b: if (type == T_BYTE) set_canonical(x->value()); break; + case Bytecodes::_i2s: if (type == T_SHORT || type == T_BYTE) set_canonical(x->value()); break; + case Bytecodes::_i2c: if (type == T_CHAR) set_canonical(x->value()); break; + } + } else { + Op2* op2 = x->value()->as_Op2(); + if (op2 && op2->op() == Bytecodes::_iand && op2->y()->type()->is_constant()) { + jint safebits = 0; + jint mask = op2->y()->type()->as_IntConstant()->value(); + switch (x->op()) { + case Bytecodes::_i2b: safebits = 0x7f; break; + case Bytecodes::_i2s: safebits = 0x7fff; break; + case Bytecodes::_i2c: safebits = 0xffff; break; + } + // When casting a masked integer to a smaller signed type, if + // the mask doesn't include the sign bit the cast isn't needed. + if (safebits && (mask & ~safebits) == 0) { + set_canonical(x->value()); + } + } + } + +} + +void Canonicalizer::do_NullCheck (NullCheck* x) { + if (x->obj()->as_NewArray() != NULL || x->obj()->as_NewInstance() != NULL) { + set_canonical(x->obj()); + } else { + Constant* con = x->obj()->as_Constant(); + if (con) { + ObjectType* c = con->type()->as_ObjectType(); + if (c && c->is_loaded()) { + ObjectConstant* oc = c->as_ObjectConstant(); + if (!oc || !oc->value()->is_null_object()) { + set_canonical(con); + } + } + } + } +} + +void Canonicalizer::do_Invoke (Invoke* x) {} +void Canonicalizer::do_NewInstance (NewInstance* x) {} +void Canonicalizer::do_NewTypeArray (NewTypeArray* x) {} +void Canonicalizer::do_NewObjectArray (NewObjectArray* x) {} +void Canonicalizer::do_NewMultiArray (NewMultiArray* x) {} +void Canonicalizer::do_CheckCast (CheckCast* x) { + if (x->klass()->is_loaded()) { + Value obj = x->obj(); + ciType* klass = obj->exact_type(); + if (klass == NULL) klass = obj->declared_type(); + if (klass != NULL && klass->is_loaded() && klass->is_subtype_of(x->klass())) { + set_canonical(obj); + return; + } + // checkcast of null returns null + if (obj->as_Constant() && obj->type()->as_ObjectType()->constant_value()->is_null_object()) { + set_canonical(obj); + } + } +} +void Canonicalizer::do_InstanceOf (InstanceOf* x) { + if (x->klass()->is_loaded()) { + Value obj = x->obj(); + ciType* exact = obj->exact_type(); + if (exact != NULL && exact->is_loaded() && (obj->as_NewInstance() || obj->as_NewArray())) { + set_constant(exact->is_subtype_of(x->klass()) ? 1 : 0); + return; + } + // instanceof null returns false + if (obj->as_Constant() && obj->type()->as_ObjectType()->constant_value()->is_null_object()) { + set_constant(0); + } + } + +} +void Canonicalizer::do_MonitorEnter (MonitorEnter* x) {} +void Canonicalizer::do_MonitorExit (MonitorExit* x) {} +void Canonicalizer::do_BlockBegin (BlockBegin* x) {} +void Canonicalizer::do_Goto (Goto* x) {} + + +static bool is_true(jlong x, If::Condition cond, jlong y) { + switch (cond) { + case If::eql: return x == y; + case If::neq: return x != y; + case If::lss: return x < y; + case If::leq: return x <= y; + case If::gtr: return x > y; + case If::geq: return x >= y; + } + ShouldNotReachHere(); + return false; +} + + +void Canonicalizer::do_If(If* x) { + // move const to right + if (x->x()->type()->is_constant()) x->swap_operands(); + // simplify + const Value l = x->x(); ValueType* lt = l->type(); + const Value r = x->y(); ValueType* rt = r->type(); + + if (l == r && !lt->is_float_kind()) { + // pattern: If (a cond a) => simplify to Goto + BlockBegin* sux; + switch (x->cond()) { + case If::eql: sux = x->sux_for(true); break; + case If::neq: sux = x->sux_for(false); break; + case If::lss: sux = x->sux_for(false); break; + case If::leq: sux = x->sux_for(true); break; + case If::gtr: sux = x->sux_for(false); break; + case If::geq: sux = x->sux_for(true); break; + } + // If is a safepoint then the debug information should come from the state_before of the If. + set_canonical(new Goto(sux, x->state_before(), x->is_safepoint())); + return; + } + + if (lt->is_constant() && rt->is_constant()) { + if (x->x()->as_Constant() != NULL) { + // pattern: If (lc cond rc) => simplify to: Goto + BlockBegin* sux = x->x()->as_Constant()->compare(x->cond(), x->y(), + x->sux_for(true), + x->sux_for(false)); + if (sux != NULL) { + // If is a safepoint then the debug information should come from the state_before of the If. + set_canonical(new Goto(sux, x->state_before(), x->is_safepoint())); + } + } + } else if (rt->as_IntConstant() != NULL) { + // pattern: If (l cond rc) => investigate further + const jint rc = rt->as_IntConstant()->value(); + if (l->as_CompareOp() != NULL) { + // pattern: If ((a cmp b) cond rc) => simplify to: If (x cond y) or: Goto + CompareOp* cmp = l->as_CompareOp(); + bool unordered_is_less = cmp->op() == Bytecodes::_fcmpl || cmp->op() == Bytecodes::_dcmpl; + BlockBegin* lss_sux = x->sux_for(is_true(-1, x->cond(), rc)); // successor for a < b + BlockBegin* eql_sux = x->sux_for(is_true( 0, x->cond(), rc)); // successor for a = b + BlockBegin* gtr_sux = x->sux_for(is_true(+1, x->cond(), rc)); // successor for a > b + BlockBegin* nan_sux = unordered_is_less ? lss_sux : gtr_sux ; // successor for unordered + // Note: At this point all successors (lss_sux, eql_sux, gtr_sux, nan_sux) are + // equal to x->tsux() or x->fsux(). Furthermore, nan_sux equals either + // lss_sux or gtr_sux. + if (lss_sux == eql_sux && eql_sux == gtr_sux) { + // all successors identical => simplify to: Goto + set_canonical(new Goto(lss_sux, x->state_before(), x->is_safepoint())); + } else { + // two successors differ and two successors are the same => simplify to: If (x cmp y) + // determine new condition & successors + If::Condition cond; + BlockBegin* tsux = NULL; + BlockBegin* fsux = NULL; + if (lss_sux == eql_sux) { cond = If::leq; tsux = lss_sux; fsux = gtr_sux; } + else if (lss_sux == gtr_sux) { cond = If::neq; tsux = lss_sux; fsux = eql_sux; } + else if (eql_sux == gtr_sux) { cond = If::geq; tsux = eql_sux; fsux = lss_sux; } + else { ShouldNotReachHere(); } + If* canon = new If(cmp->x(), cond, nan_sux == tsux, cmp->y(), tsux, fsux, cmp->state_before(), x->is_safepoint()); + if (cmp->x() == cmp->y()) { + do_If(canon); + } else { + set_canonical(canon); + set_bci(cmp->bci()); + } + } + } else if (l->as_InstanceOf() != NULL) { + // NOTE: Code permanently disabled for now since it leaves the old InstanceOf + // instruction in the graph (it is pinned). Need to fix this at some point. + return; + // pattern: If ((obj instanceof klass) cond rc) => simplify to: IfInstanceOf or: Goto + InstanceOf* inst = l->as_InstanceOf(); + BlockBegin* is_inst_sux = x->sux_for(is_true(1, x->cond(), rc)); // successor for instanceof == 1 + BlockBegin* no_inst_sux = x->sux_for(is_true(0, x->cond(), rc)); // successor for instanceof == 0 + if (is_inst_sux == no_inst_sux && inst->is_loaded()) { + // both successors identical and klass is loaded => simplify to: Goto + set_canonical(new Goto(is_inst_sux, x->state_before(), x->is_safepoint())); + } else { + // successors differ => simplify to: IfInstanceOf + set_canonical(new IfInstanceOf(inst->klass(), inst->obj(), true, inst->bci(), is_inst_sux, no_inst_sux)); + } + } + } else if (rt == objectNull && (l->as_NewInstance() || l->as_NewArray())) { + if (x->cond() == Instruction::eql) { + set_canonical(new Goto(x->fsux(), x->state_before(), x->is_safepoint())); + } else { + assert(x->cond() == Instruction::neq, "only other valid case"); + set_canonical(new Goto(x->tsux(), x->state_before(), x->is_safepoint())); + } + } +} + + +void Canonicalizer::do_TableSwitch(TableSwitch* x) { + if (x->tag()->type()->is_constant()) { + int v = x->tag()->type()->as_IntConstant()->value(); + BlockBegin* sux = x->default_sux(); + if (v >= x->lo_key() && v <= x->hi_key()) { + sux = x->sux_at(v - x->lo_key()); + } + set_canonical(new Goto(sux, x->state_before(), x->is_safepoint())); + } else if (x->number_of_sux() == 1) { + // NOTE: Code permanently disabled for now since the switch statement's + // tag expression may produce side-effects in which case it must + // be executed. + return; + // simplify to Goto + set_canonical(new Goto(x->default_sux(), x->state_before(), x->is_safepoint())); + } else if (x->number_of_sux() == 2) { + // NOTE: Code permanently disabled for now since it produces two new nodes + // (Constant & If) and the Canonicalizer cannot return them correctly + // yet. For now we copied the corresponding code directly into the + // GraphBuilder (i.e., we should never reach here). + return; + // simplify to If + assert(x->lo_key() == x->hi_key(), "keys must be the same"); + Constant* key = new Constant(new IntConstant(x->lo_key())); + set_canonical(new If(x->tag(), If::eql, true, key, x->sux_at(0), x->default_sux(), x->state_before(), x->is_safepoint())); + } +} + + +void Canonicalizer::do_LookupSwitch(LookupSwitch* x) { + if (x->tag()->type()->is_constant()) { + int v = x->tag()->type()->as_IntConstant()->value(); + BlockBegin* sux = x->default_sux(); + for (int i = 0; i < x->length(); i++) { + if (v == x->key_at(i)) { + sux = x->sux_at(i); + } + } + set_canonical(new Goto(sux, x->state_before(), x->is_safepoint())); + } else if (x->number_of_sux() == 1) { + // NOTE: Code permanently disabled for now since the switch statement's + // tag expression may produce side-effects in which case it must + // be executed. + return; + // simplify to Goto + set_canonical(new Goto(x->default_sux(), x->state_before(), x->is_safepoint())); + } else if (x->number_of_sux() == 2) { + // NOTE: Code permanently disabled for now since it produces two new nodes + // (Constant & If) and the Canonicalizer cannot return them correctly + // yet. For now we copied the corresponding code directly into the + // GraphBuilder (i.e., we should never reach here). + return; + // simplify to If + assert(x->length() == 1, "length must be the same"); + Constant* key = new Constant(new IntConstant(x->key_at(0))); + set_canonical(new If(x->tag(), If::eql, true, key, x->sux_at(0), x->default_sux(), x->state_before(), x->is_safepoint())); + } +} + + +void Canonicalizer::do_Return (Return* x) {} +void Canonicalizer::do_Throw (Throw* x) {} +void Canonicalizer::do_Base (Base* x) {} +void Canonicalizer::do_OsrEntry (OsrEntry* x) {} +void Canonicalizer::do_ExceptionObject(ExceptionObject* x) {} + +static bool match_index_and_scale(Instruction* instr, + Instruction** index, + int* log2_scale, + Instruction** instr_to_unpin) { + *instr_to_unpin = NULL; + + // Skip conversion ops + Convert* convert = instr->as_Convert(); + if (convert != NULL) { + instr = convert->value(); + } + + ShiftOp* shift = instr->as_ShiftOp(); + if (shift != NULL) { + if (shift->is_pinned()) { + *instr_to_unpin = shift; + } + // Constant shift value? + Constant* con = shift->y()->as_Constant(); + if (con == NULL) return false; + // Well-known type and value? + IntConstant* val = con->type()->as_IntConstant(); + if (val == NULL) return false; + if (shift->x()->type() != intType) return false; + *index = shift->x(); + int tmp_scale = val->value(); + if (tmp_scale >= 0 && tmp_scale < 4) { + *log2_scale = tmp_scale; + return true; + } else { + return false; + } + } + + ArithmeticOp* arith = instr->as_ArithmeticOp(); + if (arith != NULL) { + if (arith->is_pinned()) { + *instr_to_unpin = arith; + } + // Check for integer multiply + if (arith->op() == Bytecodes::_imul) { + // See if either arg is a known constant + Constant* con = arith->x()->as_Constant(); + if (con != NULL) { + *index = arith->y(); + } else { + con = arith->y()->as_Constant(); + if (con == NULL) return false; + *index = arith->x(); + } + if ((*index)->type() != intType) return false; + // Well-known type and value? + IntConstant* val = con->type()->as_IntConstant(); + if (val == NULL) return false; + switch (val->value()) { + case 1: *log2_scale = 0; return true; + case 2: *log2_scale = 1; return true; + case 4: *log2_scale = 2; return true; + case 8: *log2_scale = 3; return true; + default: return false; + } + } + } + + // Unknown instruction sequence; don't touch it + return false; +} + + +static bool match(UnsafeRawOp* x, + Instruction** base, + Instruction** index, + int* log2_scale) { + Instruction* instr_to_unpin = NULL; + ArithmeticOp* root = x->base()->as_ArithmeticOp(); + if (root == NULL) return false; + // Limit ourselves to addition for now + if (root->op() != Bytecodes::_ladd) return false; + // Try to find shift or scale op + if (match_index_and_scale(root->y(), index, log2_scale, &instr_to_unpin)) { + *base = root->x(); + } else if (match_index_and_scale(root->x(), index, log2_scale, &instr_to_unpin)) { + *base = root->y(); + } else if (root->y()->as_Convert() != NULL) { + Convert* convert = root->y()->as_Convert(); + if (convert->op() == Bytecodes::_i2l && convert->value()->type() == intType) { + // pick base and index, setting scale at 1 + *base = root->x(); + *index = convert->value(); + *log2_scale = 0; + } else { + return false; + } + } else { + // doesn't match any expected sequences + return false; + } + + // If the value is pinned then it will be always be computed so + // there's no profit to reshaping the expression. + return !root->is_pinned(); +} + + +void Canonicalizer::do_UnsafeRawOp(UnsafeRawOp* x) { + Instruction* base = NULL; + Instruction* index = NULL; + int log2_scale; + + if (match(x, &base, &index, &log2_scale)) { + x->set_base(base); + x->set_index(index); + x->set_log2_scale(log2_scale); + if (PrintUnsafeOptimization) { + tty->print_cr("Canonicalizer: UnsafeRawOp id %d: base = id %d, index = id %d, log2_scale = %d", + x->id(), x->base()->id(), x->index()->id(), x->log2_scale()); + } + } +} + +void Canonicalizer::do_RoundFP(RoundFP* x) {} +void Canonicalizer::do_UnsafeGetRaw(UnsafeGetRaw* x) { if (OptimizeUnsafes) do_UnsafeRawOp(x); } +void Canonicalizer::do_UnsafePutRaw(UnsafePutRaw* x) { if (OptimizeUnsafes) do_UnsafeRawOp(x); } +void Canonicalizer::do_UnsafeGetObject(UnsafeGetObject* x) {} +void Canonicalizer::do_UnsafePutObject(UnsafePutObject* x) {} +void Canonicalizer::do_UnsafePrefetchRead (UnsafePrefetchRead* x) {} +void Canonicalizer::do_UnsafePrefetchWrite(UnsafePrefetchWrite* x) {} +void Canonicalizer::do_ProfileCall(ProfileCall* x) {} +void Canonicalizer::do_ProfileCounter(ProfileCounter* x) {} diff --git a/hotspot/src/share/vm/c1/c1_Canonicalizer.hpp b/hotspot/src/share/vm/c1/c1_Canonicalizer.hpp new file mode 100644 index 00000000000..f63abaec34a --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Canonicalizer.hpp @@ -0,0 +1,96 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Canonicalizer: InstructionVisitor { + private: + Instruction* _canonical; + int _bci; + + void set_canonical(Value x); + void set_bci(int bci) { _bci = bci; } + void set_constant(jint x) { set_canonical(new Constant(new IntConstant(x))); } + void set_constant(jlong x) { set_canonical(new Constant(new LongConstant(x))); } + void set_constant(jfloat x) { set_canonical(new Constant(new FloatConstant(x))); } + void set_constant(jdouble x) { set_canonical(new Constant(new DoubleConstant(x))); } + void move_const_to_right(Op2* x); + void do_Op2(Op2* x); + void do_UnsafeRawOp(UnsafeRawOp* x); + + void unsafe_raw_match(UnsafeRawOp* x, + Instruction** base, + Instruction** index, + int* scale); + + public: + Canonicalizer(Value x, int bci) { _canonical = x; _bci = bci; if (CanonicalizeNodes) x->visit(this); } + Value canonical() const { return _canonical; } + int bci() const { return _bci; } + + virtual void do_Phi (Phi* x); + virtual void do_Constant (Constant* x); + virtual void do_Local (Local* x); + virtual void do_LoadField (LoadField* x); + virtual void do_StoreField (StoreField* x); + virtual void do_ArrayLength (ArrayLength* x); + virtual void do_LoadIndexed (LoadIndexed* x); + virtual void do_StoreIndexed (StoreIndexed* x); + virtual void do_NegateOp (NegateOp* x); + virtual void do_ArithmeticOp (ArithmeticOp* x); + virtual void do_ShiftOp (ShiftOp* x); + virtual void do_LogicOp (LogicOp* x); + virtual void do_CompareOp (CompareOp* x); + virtual void do_IfOp (IfOp* x); + virtual void do_IfInstanceOf (IfInstanceOf* x); + virtual void do_Convert (Convert* x); + virtual void do_NullCheck (NullCheck* x); + virtual void do_Invoke (Invoke* x); + virtual void do_NewInstance (NewInstance* x); + virtual void do_NewTypeArray (NewTypeArray* x); + virtual void do_NewObjectArray (NewObjectArray* x); + virtual void do_NewMultiArray (NewMultiArray* x); + virtual void do_CheckCast (CheckCast* x); + virtual void do_InstanceOf (InstanceOf* x); + virtual void do_MonitorEnter (MonitorEnter* x); + virtual void do_MonitorExit (MonitorExit* x); + virtual void do_Intrinsic (Intrinsic* x); + virtual void do_BlockBegin (BlockBegin* x); + virtual void do_Goto (Goto* x); + virtual void do_If (If* x); + virtual void do_TableSwitch (TableSwitch* x); + virtual void do_LookupSwitch (LookupSwitch* x); + virtual void do_Return (Return* x); + virtual void do_Throw (Throw* x); + virtual void do_Base (Base* x); + virtual void do_OsrEntry (OsrEntry* x); + virtual void do_ExceptionObject(ExceptionObject* x); + virtual void do_RoundFP (RoundFP* x); + virtual void do_UnsafeGetRaw (UnsafeGetRaw* x); + virtual void do_UnsafePutRaw (UnsafePutRaw* x); + virtual void do_UnsafeGetObject(UnsafeGetObject* x); + virtual void do_UnsafePutObject(UnsafePutObject* x); + virtual void do_UnsafePrefetchRead (UnsafePrefetchRead* x); + virtual void do_UnsafePrefetchWrite(UnsafePrefetchWrite* x); + virtual void do_ProfileCall (ProfileCall* x); + virtual void do_ProfileCounter (ProfileCounter* x); +}; diff --git a/hotspot/src/share/vm/c1/c1_CodeStubs.hpp b/hotspot/src/share/vm/c1/c1_CodeStubs.hpp new file mode 100644 index 00000000000..9009e0d8c72 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_CodeStubs.hpp @@ -0,0 +1,484 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class CodeEmitInfo; +class LIR_Assembler; +class LIR_OpVisitState; + +// CodeStubs are little 'out-of-line' pieces of code that +// usually handle slow cases of operations. All code stubs +// are collected and code is emitted at the end of the +// nmethod. + +class CodeStub: public CompilationResourceObj { + protected: + Label _entry; // label at the stub entry point + Label _continuation; // label where stub continues, if any + + public: + CodeStub() {} + + // code generation + void assert_no_unbound_labels() { assert(!_entry.is_unbound() && !_continuation.is_unbound(), "unbound label"); } + virtual void emit_code(LIR_Assembler* e) = 0; + virtual CodeEmitInfo* info() const { return NULL; } + virtual bool is_exception_throw_stub() const { return false; } + virtual bool is_range_check_stub() const { return false; } + virtual bool is_divbyzero_stub() const { return false; } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const = 0; +#endif + + // label access + Label* entry() { return &_entry; } + Label* continuation() { return &_continuation; } + // for LIR + virtual void visit(LIR_OpVisitState* visit) { +#ifndef PRODUCT + if (LIRTracePeephole && Verbose) { + tty->print("no visitor for "); + print_name(tty); + tty->cr(); + } +#endif + } +}; + + +define_array(CodeStubArray, CodeStub*) +define_stack(_CodeStubList, CodeStubArray) + +class CodeStubList: public _CodeStubList { + public: + CodeStubList(): _CodeStubList() {} + + void append(CodeStub* stub) { + if (!contains(stub)) { + _CodeStubList::append(stub); + } + } +}; + +#ifdef TIERED +class CounterOverflowStub: public CodeStub { + private: + CodeEmitInfo* _info; + int _bci; + +public: + CounterOverflowStub(CodeEmitInfo* info, int bci) : _info(info), _bci(bci) { + } + + virtual void emit_code(LIR_Assembler* e); + + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(_info); + } + +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("CounterOverflowStub"); } +#endif // PRODUCT + +}; +#endif // TIERED + +class ConversionStub: public CodeStub { + private: + Bytecodes::Code _bytecode; + LIR_Opr _input; + LIR_Opr _result; + + static float float_zero; + static double double_zero; + public: + ConversionStub(Bytecodes::Code bytecode, LIR_Opr input, LIR_Opr result) + : _bytecode(bytecode), _input(input), _result(result) { + } + + Bytecodes::Code bytecode() { return _bytecode; } + LIR_Opr input() { return _input; } + LIR_Opr result() { return _result; } + + virtual void emit_code(LIR_Assembler* e); + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(); + visitor->do_input(_input); + visitor->do_output(_result); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("ConversionStub"); } +#endif // PRODUCT +}; + + +// Throws ArrayIndexOutOfBoundsException by default but can be +// configured to throw IndexOutOfBoundsException in constructor +class RangeCheckStub: public CodeStub { + private: + CodeEmitInfo* _info; + LIR_Opr _index; + bool _throw_index_out_of_bounds_exception; + + public: + RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, bool throw_index_out_of_bounds_exception = false); + virtual void emit_code(LIR_Assembler* e); + virtual CodeEmitInfo* info() const { return _info; } + virtual bool is_exception_throw_stub() const { return true; } + virtual bool is_range_check_stub() const { return true; } + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(_info); + visitor->do_input(_index); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("RangeCheckStub"); } +#endif // PRODUCT +}; + + +class DivByZeroStub: public CodeStub { + private: + CodeEmitInfo* _info; + int _offset; + + public: + DivByZeroStub(CodeEmitInfo* info) + : _info(info), _offset(-1) { + } + DivByZeroStub(int offset, CodeEmitInfo* info) + : _info(info), _offset(offset) { + } + virtual void emit_code(LIR_Assembler* e); + virtual CodeEmitInfo* info() const { return _info; } + virtual bool is_exception_throw_stub() const { return true; } + virtual bool is_divbyzero_stub() const { return true; } + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(_info); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("DivByZeroStub"); } +#endif // PRODUCT +}; + + +class ImplicitNullCheckStub: public CodeStub { + private: + CodeEmitInfo* _info; + int _offset; + + public: + ImplicitNullCheckStub(int offset, CodeEmitInfo* info) + : _offset(offset), _info(info) { + } + virtual void emit_code(LIR_Assembler* e); + virtual CodeEmitInfo* info() const { return _info; } + virtual bool is_exception_throw_stub() const { return true; } + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(_info); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("ImplicitNullCheckStub"); } +#endif // PRODUCT +}; + + +class NewInstanceStub: public CodeStub { + private: + ciInstanceKlass* _klass; + LIR_Opr _klass_reg; + LIR_Opr _result; + CodeEmitInfo* _info; + Runtime1::StubID _stub_id; + + public: + NewInstanceStub(LIR_Opr klass_reg, LIR_Opr result, ciInstanceKlass* klass, CodeEmitInfo* info, Runtime1::StubID stub_id); + virtual void emit_code(LIR_Assembler* e); + virtual CodeEmitInfo* info() const { return _info; } + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(_info); + visitor->do_input(_klass_reg); + visitor->do_output(_result); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("NewInstanceStub"); } +#endif // PRODUCT +}; + + +class NewTypeArrayStub: public CodeStub { + private: + LIR_Opr _klass_reg; + LIR_Opr _length; + LIR_Opr _result; + CodeEmitInfo* _info; + + public: + NewTypeArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, CodeEmitInfo* info); + virtual void emit_code(LIR_Assembler* e); + virtual CodeEmitInfo* info() const { return _info; } + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(_info); + visitor->do_input(_klass_reg); + visitor->do_input(_length); + assert(_result->is_valid(), "must be valid"); visitor->do_output(_result); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("NewTypeArrayStub"); } +#endif // PRODUCT +}; + + +class NewObjectArrayStub: public CodeStub { + private: + LIR_Opr _klass_reg; + LIR_Opr _length; + LIR_Opr _result; + CodeEmitInfo* _info; + + public: + NewObjectArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, CodeEmitInfo* info); + virtual void emit_code(LIR_Assembler* e); + virtual CodeEmitInfo* info() const { return _info; } + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(_info); + visitor->do_input(_klass_reg); + visitor->do_input(_length); + assert(_result->is_valid(), "must be valid"); visitor->do_output(_result); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("NewObjectArrayStub"); } +#endif // PRODUCT +}; + + +class MonitorAccessStub: public CodeStub { + protected: + LIR_Opr _obj_reg; + LIR_Opr _lock_reg; + + public: + MonitorAccessStub(LIR_Opr obj_reg, LIR_Opr lock_reg) { + _obj_reg = obj_reg; + _lock_reg = lock_reg; + } + +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("MonitorAccessStub"); } +#endif // PRODUCT +}; + + +class MonitorEnterStub: public MonitorAccessStub { + private: + CodeEmitInfo* _info; + + public: + MonitorEnterStub(LIR_Opr obj_reg, LIR_Opr lock_reg, CodeEmitInfo* info); + + virtual void emit_code(LIR_Assembler* e); + virtual CodeEmitInfo* info() const { return _info; } + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_input(_obj_reg); + visitor->do_input(_lock_reg); + visitor->do_slow_case(_info); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("MonitorEnterStub"); } +#endif // PRODUCT +}; + + +class MonitorExitStub: public MonitorAccessStub { + private: + bool _compute_lock; + int _monitor_ix; + + public: + MonitorExitStub(LIR_Opr lock_reg, bool compute_lock, int monitor_ix) + : MonitorAccessStub(LIR_OprFact::illegalOpr, lock_reg), + _compute_lock(compute_lock), _monitor_ix(monitor_ix) { } + virtual void emit_code(LIR_Assembler* e); + virtual void visit(LIR_OpVisitState* visitor) { + assert(_obj_reg->is_illegal(), "unused"); + if (_compute_lock) { + visitor->do_temp(_lock_reg); + } else { + visitor->do_input(_lock_reg); + } + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("MonitorExitStub"); } +#endif // PRODUCT +}; + + +class PatchingStub: public CodeStub { + public: + enum PatchID { + access_field_id, + load_klass_id + }; + enum constants { + patch_info_size = 3 + }; + private: + PatchID _id; + address _pc_start; + int _bytes_to_copy; + Label _patched_code_entry; + Label _patch_site_entry; + Label _patch_site_continuation; + Register _obj; + CodeEmitInfo* _info; + int _oop_index; // index of the patchable oop in nmethod oop table if needed + static int _patch_info_offset; + + void align_patch_site(MacroAssembler* masm); + + public: + static int patch_info_offset() { return _patch_info_offset; } + + PatchingStub(MacroAssembler* masm, PatchID id, int oop_index = -1): + _id(id) + , _info(NULL) + , _oop_index(oop_index) { + if (os::is_MP()) { + // force alignment of patch sites on MP hardware so we + // can guarantee atomic writes to the patch site. + align_patch_site(masm); + } + _pc_start = masm->pc(); + masm->bind(_patch_site_entry); + } + + void install(MacroAssembler* masm, LIR_PatchCode patch_code, Register obj, CodeEmitInfo* info) { + _info = info; + _obj = obj; + masm->bind(_patch_site_continuation); + _bytes_to_copy = masm->pc() - pc_start(); + if (_id == PatchingStub::access_field_id) { + // embed a fixed offset to handle long patches which need to be offset by a word. + // the patching code will just add the field offset field to this offset so + // that we can refernce either the high or low word of a double word field. + int field_offset = 0; + switch (patch_code) { + case lir_patch_low: field_offset = lo_word_offset_in_bytes; break; + case lir_patch_high: field_offset = hi_word_offset_in_bytes; break; + case lir_patch_normal: field_offset = 0; break; + default: ShouldNotReachHere(); + } + NativeMovRegMem* n_move = nativeMovRegMem_at(pc_start()); + n_move->set_offset(field_offset); + } else if (_id == load_klass_id) { + assert(_obj != noreg, "must have register object for load_klass"); +#ifdef ASSERT + // verify that we're pointing at a NativeMovConstReg + nativeMovConstReg_at(pc_start()); +#endif + } else { + ShouldNotReachHere(); + } + assert(_bytes_to_copy <= (masm->pc() - pc_start()), "not enough bytes"); + } + + address pc_start() const { return _pc_start; } + PatchID id() const { return _id; } + + virtual void emit_code(LIR_Assembler* e); + virtual CodeEmitInfo* info() const { return _info; } + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(_info); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("PatchingStub"); } +#endif // PRODUCT +}; + + +class SimpleExceptionStub: public CodeStub { + private: + LIR_Opr _obj; + Runtime1::StubID _stub; + CodeEmitInfo* _info; + + public: + SimpleExceptionStub(Runtime1::StubID stub, LIR_Opr obj, CodeEmitInfo* info): + _obj(obj), _info(info), _stub(stub) { + } + + virtual void emit_code(LIR_Assembler* e); + virtual CodeEmitInfo* info() const { return _info; } + virtual bool is_exception_throw_stub() const { return true; } + virtual void visit(LIR_OpVisitState* visitor) { + if (_obj->is_valid()) visitor->do_input(_obj); + visitor->do_slow_case(_info); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("SimpleExceptionStub"); } +#endif // PRODUCT +}; + + + +class ArrayStoreExceptionStub: public CodeStub { + private: + CodeEmitInfo* _info; + + public: + ArrayStoreExceptionStub(CodeEmitInfo* info); + virtual void emit_code(LIR_Assembler* emit); + virtual CodeEmitInfo* info() const { return _info; } + virtual bool is_exception_throw_stub() const { return true; } + virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(_info); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("ArrayStoreExceptionStub"); } +#endif // PRODUCT +}; + + +class ArrayCopyStub: public CodeStub { + private: + LIR_OpArrayCopy* _op; + + public: + ArrayCopyStub(LIR_OpArrayCopy* op): _op(op) { } + + LIR_Opr src() const { return _op->src(); } + LIR_Opr src_pos() const { return _op->src_pos(); } + LIR_Opr dst() const { return _op->dst(); } + LIR_Opr dst_pos() const { return _op->dst_pos(); } + LIR_Opr length() const { return _op->length(); } + LIR_Opr tmp() const { return _op->tmp(); } + + virtual void emit_code(LIR_Assembler* e); + virtual CodeEmitInfo* info() const { return _op->info(); } + virtual void visit(LIR_OpVisitState* visitor) { + // don't pass in the code emit info since it's processed in the fast path + visitor->do_slow_case(); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("ArrayCopyStub"); } +#endif // PRODUCT +}; diff --git a/hotspot/src/share/vm/c1/c1_Compilation.cpp b/hotspot/src/share/vm/c1/c1_Compilation.cpp new file mode 100644 index 00000000000..77bf0e1e71e --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Compilation.cpp @@ -0,0 +1,526 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_Compilation.cpp.incl" + + +typedef enum { + _t_compile, + _t_setup, + _t_optimizeIR, + _t_buildIR, + _t_emit_lir, + _t_linearScan, + _t_lirGeneration, + _t_lir_schedule, + _t_codeemit, + _t_codeinstall, + max_phase_timers +} TimerName; + +static const char * timer_name[] = { + "compile", + "setup", + "optimizeIR", + "buildIR", + "emit_lir", + "linearScan", + "lirGeneration", + "lir_schedule", + "codeemit", + "codeinstall" +}; + +static elapsedTimer timers[max_phase_timers]; +static int totalInstructionNodes = 0; + +class PhaseTraceTime: public TraceTime { + private: + JavaThread* _thread; + + public: + PhaseTraceTime(TimerName timer): + TraceTime("", &timers[timer], CITime || CITimeEach, Verbose) { + } +}; + +Arena* Compilation::_arena = NULL; +Compilation* Compilation::_compilation = NULL; + +// Implementation of Compilation + + +#ifndef PRODUCT + +void Compilation::maybe_print_current_instruction() { + if (_current_instruction != NULL && _last_instruction_printed != _current_instruction) { + _last_instruction_printed = _current_instruction; + _current_instruction->print_line(); + } +} +#endif // PRODUCT + + +DebugInformationRecorder* Compilation::debug_info_recorder() const { + return _env->debug_info(); +} + + +Dependencies* Compilation::dependency_recorder() const { + return _env->dependencies(); +} + + +void Compilation::initialize() { + // Use an oop recorder bound to the CI environment. + // (The default oop recorder is ignorant of the CI.) + OopRecorder* ooprec = new OopRecorder(_env->arena()); + _env->set_oop_recorder(ooprec); + _env->set_debug_info(new DebugInformationRecorder(ooprec)); + debug_info_recorder()->set_oopmaps(new OopMapSet()); + _env->set_dependencies(new Dependencies(_env)); +} + + +void Compilation::build_hir() { + CHECK_BAILOUT(); + + // setup ir + _hir = new IR(this, method(), osr_bci()); + if (!_hir->is_valid()) { + bailout("invalid parsing"); + return; + } + +#ifndef PRODUCT + if (PrintCFGToFile) { + CFGPrinter::print_cfg(_hir, "After Generation of HIR", true, false); + } +#endif + +#ifndef PRODUCT + if (PrintCFG || PrintCFG0) { tty->print_cr("CFG after parsing"); _hir->print(true); } + if (PrintIR || PrintIR0 ) { tty->print_cr("IR after parsing"); _hir->print(false); } +#endif + + _hir->verify(); + + if (UseC1Optimizations) { + NEEDS_CLEANUP + // optimization + PhaseTraceTime timeit(_t_optimizeIR); + + _hir->optimize(); + } + + _hir->verify(); + + _hir->split_critical_edges(); + +#ifndef PRODUCT + if (PrintCFG || PrintCFG1) { tty->print_cr("CFG after optimizations"); _hir->print(true); } + if (PrintIR || PrintIR1 ) { tty->print_cr("IR after optimizations"); _hir->print(false); } +#endif + + _hir->verify(); + + // compute block ordering for code generation + // the control flow must not be changed from here on + _hir->compute_code(); + + if (UseGlobalValueNumbering) { + ResourceMark rm; + int instructions = Instruction::number_of_instructions(); + GlobalValueNumbering gvn(_hir); + assert(instructions == Instruction::number_of_instructions(), + "shouldn't have created an instructions"); + } + + // compute use counts after global value numbering + _hir->compute_use_counts(); + +#ifndef PRODUCT + if (PrintCFG || PrintCFG2) { tty->print_cr("CFG before code generation"); _hir->code()->print(true); } + if (PrintIR || PrintIR2 ) { tty->print_cr("IR before code generation"); _hir->code()->print(false, true); } +#endif + + _hir->verify(); +} + + +void Compilation::emit_lir() { + CHECK_BAILOUT(); + + LIRGenerator gen(this, method()); + { + PhaseTraceTime timeit(_t_lirGeneration); + hir()->iterate_linear_scan_order(&gen); + } + + CHECK_BAILOUT(); + + { + PhaseTraceTime timeit(_t_linearScan); + + LinearScan* allocator = new LinearScan(hir(), &gen, frame_map()); + set_allocator(allocator); + // Assign physical registers to LIR operands using a linear scan algorithm. + allocator->do_linear_scan(); + CHECK_BAILOUT(); + + _max_spills = allocator->max_spills(); + } + + if (BailoutAfterLIR) { + if (PrintLIR && !bailed_out()) { + print_LIR(hir()->code()); + } + bailout("Bailing out because of -XX:+BailoutAfterLIR"); + } +} + + +void Compilation::emit_code_epilog(LIR_Assembler* assembler) { + CHECK_BAILOUT(); + + // generate code or slow cases + assembler->emit_slow_case_stubs(); + CHECK_BAILOUT(); + + // generate exception adapters + assembler->emit_exception_entries(exception_info_list()); + CHECK_BAILOUT(); + + // generate code for exception handler + assembler->emit_exception_handler(); + CHECK_BAILOUT(); + assembler->emit_deopt_handler(); + CHECK_BAILOUT(); + + // done + masm()->flush(); +} + + +int Compilation::emit_code_body() { + // emit code + Runtime1::setup_code_buffer(code(), allocator()->num_calls()); + code()->initialize_oop_recorder(env()->oop_recorder()); + + _masm = new C1_MacroAssembler(code()); + _masm->set_oop_recorder(env()->oop_recorder()); + + LIR_Assembler lir_asm(this); + + lir_asm.emit_code(hir()->code()); + CHECK_BAILOUT_(0); + + emit_code_epilog(&lir_asm); + CHECK_BAILOUT_(0); + + generate_exception_handler_table(); + +#ifndef PRODUCT + if (PrintExceptionHandlers && Verbose) { + exception_handler_table()->print(); + } +#endif /* PRODUCT */ + + return frame_map()->framesize(); +} + + +int Compilation::compile_java_method() { + assert(!method()->is_native(), "should not reach here"); + + if (BailoutOnExceptionHandlers) { + if (method()->has_exception_handlers()) { + bailout("linear scan can't handle exception handlers"); + } + } + + CHECK_BAILOUT_(no_frame_size); + + { + PhaseTraceTime timeit(_t_buildIR); + build_hir(); + } + if (BailoutAfterHIR) { + BAILOUT_("Bailing out because of -XX:+BailoutAfterHIR", no_frame_size); + } + + + { + PhaseTraceTime timeit(_t_emit_lir); + + _frame_map = new FrameMap(method(), hir()->number_of_locks(), MAX2(4, hir()->max_stack())); + emit_lir(); + } + CHECK_BAILOUT_(no_frame_size); + + { + PhaseTraceTime timeit(_t_codeemit); + return emit_code_body(); + } +} + +void Compilation::install_code(int frame_size) { + // frame_size is in 32-bit words so adjust it intptr_t words + assert(frame_size == frame_map()->framesize(), "must match"); + assert(in_bytes(frame_map()->framesize_in_bytes()) % sizeof(intptr_t) == 0, "must be at least pointer aligned"); + _env->register_method( + method(), + osr_bci(), + &_offsets, + in_bytes(_frame_map->sp_offset_for_orig_pc()), + code(), + in_bytes(frame_map()->framesize_in_bytes()) / sizeof(intptr_t), + debug_info_recorder()->_oopmaps, + exception_handler_table(), + implicit_exception_table(), + compiler(), + _env->comp_level(), + needs_debug_information(), + has_unsafe_access() + ); +} + + +void Compilation::compile_method() { + // setup compilation + initialize(); + + if (!method()->can_be_compiled()) { + // Prevent race condition 6328518. + // This can happen if the method is obsolete or breakpointed. + bailout("Bailing out because method is not compilable"); + return; + } + + if (JvmtiExport::can_hotswap_or_post_breakpoint()) { + // We can assert evol_method because method->can_be_compiled is true. + dependency_recorder()->assert_evol_method(method()); + } + + if (method()->break_at_execute()) { + BREAKPOINT; + } + +#ifndef PRODUCT + if (PrintCFGToFile) { + CFGPrinter::print_compilation(this); + } +#endif + + // compile method + int frame_size = compile_java_method(); + + // bailout if method couldn't be compiled + // Note: make sure we mark the method as not compilable! + CHECK_BAILOUT(); + + if (InstallMethods) { + // install code + PhaseTraceTime timeit(_t_codeinstall); + install_code(frame_size); + } + totalInstructionNodes += Instruction::number_of_instructions(); +} + + +void Compilation::generate_exception_handler_table() { + // Generate an ExceptionHandlerTable from the exception handler + // information accumulated during the compilation. + ExceptionInfoList* info_list = exception_info_list(); + + if (info_list->length() == 0) { + return; + } + + // allocate some arrays for use by the collection code. + const int num_handlers = 5; + GrowableArray* bcis = new GrowableArray(num_handlers); + GrowableArray* scope_depths = new GrowableArray(num_handlers); + GrowableArray* pcos = new GrowableArray(num_handlers); + + for (int i = 0; i < info_list->length(); i++) { + ExceptionInfo* info = info_list->at(i); + XHandlers* handlers = info->exception_handlers(); + + // empty the arrays + bcis->trunc_to(0); + scope_depths->trunc_to(0); + pcos->trunc_to(0); + + for (int i = 0; i < handlers->length(); i++) { + XHandler* handler = handlers->handler_at(i); + assert(handler->entry_pco() != -1, "must have been generated"); + + int e = bcis->find(handler->handler_bci()); + if (e >= 0 && scope_depths->at(e) == handler->scope_count()) { + // two different handlers are declared to dispatch to the same + // catch bci. During parsing we created edges for each + // handler but we really only need one. The exception handler + // table will also get unhappy if we try to declare both since + // it's nonsensical. Just skip this handler. + continue; + } + + bcis->append(handler->handler_bci()); + if (handler->handler_bci() == -1) { + // insert a wildcard handler at scope depth 0 so that the + // exception lookup logic with find it. + scope_depths->append(0); + } else { + scope_depths->append(handler->scope_count()); + } + pcos->append(handler->entry_pco()); + + // stop processing once we hit a catch any + if (handler->is_catch_all()) { + assert(i == handlers->length() - 1, "catch all must be last handler"); + } + } + exception_handler_table()->add_subtable(info->pco(), bcis, scope_depths, pcos); + } +} + + +Compilation::Compilation(AbstractCompiler* compiler, ciEnv* env, ciMethod* method, int osr_bci) +: _compiler(compiler) +, _env(env) +, _method(method) +, _osr_bci(osr_bci) +, _hir(NULL) +, _max_spills(-1) +, _frame_map(NULL) +, _masm(NULL) +, _has_exception_handlers(false) +, _has_fpu_code(true) // pessimistic assumption +, _has_unsafe_access(false) +, _bailout_msg(NULL) +, _exception_info_list(NULL) +, _allocator(NULL) +, _code(Runtime1::get_buffer_blob()->instructions_begin(), + Runtime1::get_buffer_blob()->instructions_size()) +, _current_instruction(NULL) +#ifndef PRODUCT +, _last_instruction_printed(NULL) +#endif // PRODUCT +{ + PhaseTraceTime timeit(_t_compile); + + assert(_arena == NULL, "shouldn't only one instance of Compilation in existence at a time"); + _arena = Thread::current()->resource_area(); + _compilation = this; + _needs_debug_information = JvmtiExport::can_examine_or_deopt_anywhere() || + JavaMonitorsInStackTrace || AlwaysEmitDebugInfo || DeoptimizeALot; + _exception_info_list = new ExceptionInfoList(); + _implicit_exception_table.set_size(0); + compile_method(); +} + +Compilation::~Compilation() { + _arena = NULL; + _compilation = NULL; +} + + +void Compilation::add_exception_handlers_for_pco(int pco, XHandlers* exception_handlers) { +#ifndef PRODUCT + if (PrintExceptionHandlers && Verbose) { + tty->print_cr(" added exception scope for pco %d", pco); + } +#endif + // Note: we do not have program counters for these exception handlers yet + exception_info_list()->push(new ExceptionInfo(pco, exception_handlers)); +} + + +void Compilation::notice_inlined_method(ciMethod* method) { + _env->notice_inlined_method(method); +} + + +void Compilation::bailout(const char* msg) { + assert(msg != NULL, "bailout message must exist"); + if (!bailed_out()) { + // keep first bailout message + if (PrintBailouts) tty->print_cr("compilation bailout: %s", msg); + _bailout_msg = msg; + } +} + + +void Compilation::print_timers() { + // tty->print_cr(" Native methods : %6.3f s, Average : %2.3f", CompileBroker::_t_native_compilation.seconds(), CompileBroker::_t_native_compilation.seconds() / CompileBroker::_total_native_compile_count); + float total = timers[_t_setup].seconds() + timers[_t_buildIR].seconds() + timers[_t_emit_lir].seconds() + timers[_t_lir_schedule].seconds() + timers[_t_codeemit].seconds() + timers[_t_codeinstall].seconds(); + + + tty->print_cr(" Detailed C1 Timings"); + tty->print_cr(" Setup time: %6.3f s (%4.1f%%)", timers[_t_setup].seconds(), (timers[_t_setup].seconds() / total) * 100.0); + tty->print_cr(" Build IR: %6.3f s (%4.1f%%)", timers[_t_buildIR].seconds(), (timers[_t_buildIR].seconds() / total) * 100.0); + tty->print_cr(" Optimize: %6.3f s (%4.1f%%)", timers[_t_optimizeIR].seconds(), (timers[_t_optimizeIR].seconds() / total) * 100.0); + tty->print_cr(" Emit LIR: %6.3f s (%4.1f%%)", timers[_t_emit_lir].seconds(), (timers[_t_emit_lir].seconds() / total) * 100.0); + tty->print_cr(" LIR Gen: %6.3f s (%4.1f%%)", timers[_t_lirGeneration].seconds(), (timers[_t_lirGeneration].seconds() / total) * 100.0); + tty->print_cr(" Linear Scan: %6.3f s (%4.1f%%)", timers[_t_linearScan].seconds(), (timers[_t_linearScan].seconds() / total) * 100.0); + NOT_PRODUCT(LinearScan::print_timers(timers[_t_linearScan].seconds())); + tty->print_cr(" LIR Schedule: %6.3f s (%4.1f%%)", timers[_t_lir_schedule].seconds(), (timers[_t_lir_schedule].seconds() / total) * 100.0); + tty->print_cr(" Code Emission: %6.3f s (%4.1f%%)", timers[_t_codeemit].seconds(), (timers[_t_codeemit].seconds() / total) * 100.0); + tty->print_cr(" Code Installation: %6.3f s (%4.1f%%)", timers[_t_codeinstall].seconds(), (timers[_t_codeinstall].seconds() / total) * 100.0); + tty->print_cr(" Instruction Nodes: %6d nodes", totalInstructionNodes); + + NOT_PRODUCT(LinearScan::print_statistics()); +} + + +#ifndef PRODUCT +void Compilation::compile_only_this_method() { + ResourceMark rm; + fileStream stream(fopen("c1_compile_only", "wt")); + stream.print_cr("# c1 compile only directives"); + compile_only_this_scope(&stream, hir()->top_scope()); +} + + +void Compilation::compile_only_this_scope(outputStream* st, IRScope* scope) { + st->print("CompileOnly="); + scope->method()->holder()->name()->print_symbol_on(st); + st->print("."); + scope->method()->name()->print_symbol_on(st); + st->cr(); +} + + +void Compilation::exclude_this_method() { + fileStream stream(fopen(".hotspot_compiler", "at")); + stream.print("exclude "); + method()->holder()->name()->print_symbol_on(&stream); + stream.print(" "); + method()->name()->print_symbol_on(&stream); + stream.cr(); + stream.cr(); +} +#endif diff --git a/hotspot/src/share/vm/c1/c1_Compilation.hpp b/hotspot/src/share/vm/c1/c1_Compilation.hpp new file mode 100644 index 00000000000..c0332491bde --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Compilation.hpp @@ -0,0 +1,234 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class BlockBegin; +class CompilationResourceObj; +class XHandlers; +class ExceptionInfo; +class DebugInformationRecorder; +class FrameMap; +class IR; +class IRScope; +class Instruction; +class LinearScan; +class OopMap; +class LIR_Emitter; +class LIR_Assembler; +class CodeEmitInfo; +class ciEnv; +class ciMethod; +class ValueStack; +class LIR_OprDesc; +class C1_MacroAssembler; +class CFGPrinter; +typedef LIR_OprDesc* LIR_Opr; + + +define_array(BasicTypeArray, BasicType) +define_stack(BasicTypeList, BasicTypeArray) + +define_array(ExceptionInfoArray, ExceptionInfo*) +define_stack(ExceptionInfoList, ExceptionInfoArray) + +class Compilation: public StackObj { + friend class CompilationResourceObj; + private: + + static Arena* _arena; + static Arena* arena() { return _arena; } + + static Compilation* _compilation; + + private: + // compilation specifics + AbstractCompiler* _compiler; + ciEnv* _env; + ciMethod* _method; + int _osr_bci; + IR* _hir; + int _max_spills; + FrameMap* _frame_map; + C1_MacroAssembler* _masm; + bool _needs_debug_information; + bool _has_exception_handlers; + bool _has_fpu_code; + bool _has_unsafe_access; + const char* _bailout_msg; + ExceptionInfoList* _exception_info_list; + ExceptionHandlerTable _exception_handler_table; + ImplicitExceptionTable _implicit_exception_table; + LinearScan* _allocator; + CodeOffsets _offsets; + CodeBuffer _code; + + // compilation helpers + void initialize(); + void build_hir(); + void emit_lir(); + + void emit_code_epilog(LIR_Assembler* assembler); + int emit_code_body(); + + int compile_java_method(); + void install_code(int frame_size); + void compile_method(); + + void generate_exception_handler_table(); + + ExceptionInfoList* exception_info_list() const { return _exception_info_list; } + ExceptionHandlerTable* exception_handler_table() { return &_exception_handler_table; } + + LinearScan* allocator() { return _allocator; } + void set_allocator(LinearScan* allocator) { _allocator = allocator; } + + Instruction* _current_instruction; // the instruction currently being processed +#ifndef PRODUCT + Instruction* _last_instruction_printed; // the last instruction printed during traversal +#endif // PRODUCT + + public: + // creation + Compilation(AbstractCompiler* compiler, ciEnv* env, ciMethod* method, int osr_bci); + ~Compilation(); + + static Compilation* current_compilation() { return _compilation; } + + // accessors + ciEnv* env() const { return _env; } + AbstractCompiler* compiler() const { return _compiler; } + bool needs_debug_information() const { return _needs_debug_information; } + bool has_exception_handlers() const { return _has_exception_handlers; } + bool has_fpu_code() const { return _has_fpu_code; } + bool has_unsafe_access() const { return _has_unsafe_access; } + ciMethod* method() const { return _method; } + int osr_bci() const { return _osr_bci; } + bool is_osr_compile() const { return osr_bci() >= 0; } + IR* hir() const { return _hir; } + int max_spills() const { return _max_spills; } + FrameMap* frame_map() const { return _frame_map; } + CodeBuffer* code() { return &_code; } + C1_MacroAssembler* masm() const { return _masm; } + CodeOffsets* offsets() { return &_offsets; } + + // setters + void set_needs_debug_information(bool f) { _needs_debug_information = f; } + void set_has_exception_handlers(bool f) { _has_exception_handlers = f; } + void set_has_fpu_code(bool f) { _has_fpu_code = f; } + void set_has_unsafe_access(bool f) { _has_unsafe_access = f; } + // Add a set of exception handlers covering the given PC offset + void add_exception_handlers_for_pco(int pco, XHandlers* exception_handlers); + // Statistics gathering + void notice_inlined_method(ciMethod* method); + + DebugInformationRecorder* debug_info_recorder() const; // = _env->debug_info(); + Dependencies* dependency_recorder() const; // = _env->dependencies() + ImplicitExceptionTable* implicit_exception_table() { return &_implicit_exception_table; } + + Instruction* current_instruction() const { return _current_instruction; } + Instruction* set_current_instruction(Instruction* instr) { + Instruction* previous = _current_instruction; + _current_instruction = instr; + return previous; + } + +#ifndef PRODUCT + void maybe_print_current_instruction(); +#endif // PRODUCT + + // error handling + void bailout(const char* msg); + bool bailed_out() const { return _bailout_msg != NULL; } + const char* bailout_msg() const { return _bailout_msg; } + + // timers + static void print_timers(); + +#ifndef PRODUCT + // debugging support. + // produces a file named c1compileonly in the current directory with + // directives to compile only the current method and it's inlines. + // The file can be passed to the command line option -XX:Flags= + void compile_only_this_method(); + void compile_only_this_scope(outputStream* st, IRScope* scope); + void exclude_this_method(); +#endif // PRODUCT +}; + + +// Macro definitions for unified bailout-support +// The methods bailout() and bailed_out() are present in all classes +// that might bailout, but forward all calls to Compilation +#define BAILOUT(msg) { bailout(msg); return; } +#define BAILOUT_(msg, res) { bailout(msg); return res; } + +#define CHECK_BAILOUT() { if (bailed_out()) return; } +#define CHECK_BAILOUT_(res) { if (bailed_out()) return res; } + + +class InstructionMark: public StackObj { + private: + Compilation* _compilation; + Instruction* _previous; + + public: + InstructionMark(Compilation* compilation, Instruction* instr) { + _compilation = compilation; + _previous = _compilation->set_current_instruction(instr); + } + ~InstructionMark() { + _compilation->set_current_instruction(_previous); + } +}; + + +//---------------------------------------------------------------------- +// Base class for objects allocated by the compiler in the compilation arena +class CompilationResourceObj ALLOCATION_SUPER_CLASS_SPEC { + public: + void* operator new(size_t size) { return Compilation::arena()->Amalloc(size); } + void operator delete(void* p) {} // nothing to do +}; + + +//---------------------------------------------------------------------- +// Class for aggregating exception handler information. + +// Effectively extends XHandlers class with PC offset of +// potentially exception-throwing instruction. +// This class is used at the end of the compilation to build the +// ExceptionHandlerTable. +class ExceptionInfo: public CompilationResourceObj { + private: + int _pco; // PC of potentially exception-throwing instruction + XHandlers* _exception_handlers; // flat list of exception handlers covering this PC + + public: + ExceptionInfo(int pco, XHandlers* exception_handlers) + : _pco(pco) + , _exception_handlers(exception_handlers) + { } + + int pco() { return _pco; } + XHandlers* exception_handlers() { return _exception_handlers; } +}; diff --git a/hotspot/src/share/vm/c1/c1_Compiler.cpp b/hotspot/src/share/vm/c1/c1_Compiler.cpp new file mode 100644 index 00000000000..711b3c990f9 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Compiler.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_Compiler.cpp.incl" + +volatile int Compiler::_runtimes = uninitialized; + +volatile bool Compiler::_compiling = false; + + +Compiler::Compiler() { +} + + +Compiler::~Compiler() { + Unimplemented(); +} + + +void Compiler::initialize() { + if (_runtimes != initialized) { + initialize_runtimes( Runtime1::initialize, &_runtimes); + } + mark_initialized(); +} + + +void Compiler::compile_method(ciEnv* env, ciMethod* method, int entry_bci) { + + if (!is_initialized()) { + initialize(); + } + // invoke compilation +#ifdef TIERED + // We are thread in native here... + CompilerThread* thread = CompilerThread::current(); + { + ThreadInVMfromNative tv(thread); + MutexLocker only_one (C1_lock, thread); + while ( _compiling) { + C1_lock->wait(); + } + _compiling = true; + } +#endif // TIERED + { + // We are nested here because we need for the destructor + // of Compilation to occur before we release the any + // competing compiler thread + ResourceMark rm; + Compilation c(this, env, method, entry_bci); + } +#ifdef TIERED + { + ThreadInVMfromNative tv(thread); + MutexLocker only_one (C1_lock, thread); + _compiling = false; + C1_lock->notify(); + } +#endif // TIERED +} + + +void Compiler::print_timers() { + Compilation::print_timers(); +} diff --git a/hotspot/src/share/vm/c1/c1_Compiler.hpp b/hotspot/src/share/vm/c1/c1_Compiler.hpp new file mode 100644 index 00000000000..2bfbac196c3 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Compiler.hpp @@ -0,0 +1,67 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// There is one instance of the Compiler per CompilerThread. + +class Compiler: public AbstractCompiler { + + private: + + // Tracks whether runtime has been initialized + static volatile int _runtimes; + + // In tiered it is possible for multiple threads to want to do compilation + // only one can enter c1 at a time + static volatile bool _compiling; + + public: + // Creation + Compiler(); + ~Compiler(); + + // Name of this compiler + virtual const char* name() { return "C1"; } + +#ifdef TIERED + virtual bool is_c1() { return true; }; +#endif // TIERED + + + // Missing feature tests + virtual bool supports_native() { return true; } + virtual bool supports_osr () { return true; } + + // Customization + virtual bool needs_adapters () { return false; } + virtual bool needs_stubs () { return false; } + + // Initialization + virtual void initialize(); + + // Compilation entry point for methods + virtual void compile_method(ciEnv* env, ciMethod* target, int entry_bci); + + // Print compilation timers and statistics + virtual void print_timers(); +}; diff --git a/hotspot/src/share/vm/c1/c1_Defs.cpp b/hotspot/src/share/vm/c1/c1_Defs.cpp new file mode 100644 index 00000000000..1d5941e27ff --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Defs.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_Defs.cpp.incl" diff --git a/hotspot/src/share/vm/c1/c1_Defs.hpp b/hotspot/src/share/vm/c1/c1_Defs.hpp new file mode 100644 index 00000000000..06eeb3ec343 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Defs.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// set frame size and return address offset to these values in blobs +// (if the compiled frame uses ebp as link pointer on IA; otherwise, +// the frame size must be fixed) +enum { + no_frame_size = -1 +}; + + +# include "incls/_c1_Defs_pd.hpp.incl" + +// native word offsets from memory address +enum { + lo_word_offset_in_bytes = pd_lo_word_offset_in_bytes, + hi_word_offset_in_bytes = pd_hi_word_offset_in_bytes +}; + + +// the processor may require explicit rounding operations to implement the strictFP mode +enum { + strict_fp_requires_explicit_rounding = pd_strict_fp_requires_explicit_rounding +}; + + +// for debug info: a float value in a register may be saved in double precision by runtime stubs +enum { + float_saved_as_double = pd_float_saved_as_double +}; diff --git a/hotspot/src/share/vm/c1/c1_FpuStackSim.hpp b/hotspot/src/share/vm/c1/c1_FpuStackSim.hpp new file mode 100644 index 00000000000..dea999be524 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_FpuStackSim.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Provides location for forward declaration of this class, which is +// only implemented on Intel +class FpuStackSim; + +# include "incls/_c1_FpuStackSim_pd.hpp.incl" // platform dependent declarations diff --git a/hotspot/src/share/vm/c1/c1_FrameMap.cpp b/hotspot/src/share/vm/c1/c1_FrameMap.cpp new file mode 100644 index 00000000000..31e29bed07f --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_FrameMap.cpp @@ -0,0 +1,350 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_FrameMap.cpp.incl" + + + +//----------------------------------------------------- + +// Convert method signature into an array of BasicTypes for the arguments +BasicTypeArray* FrameMap::signature_type_array_for(const ciMethod* method) { + ciSignature* sig = method->signature(); + BasicTypeList* sta = new BasicTypeList(method->arg_size()); + // add receiver, if any + if (!method->is_static()) sta->append(T_OBJECT); + // add remaining arguments + for (int i = 0; i < sig->count(); i++) { + ciType* type = sig->type_at(i); + BasicType t = type->basic_type(); + if (t == T_ARRAY) { + t = T_OBJECT; + } + sta->append(t); + } + // done + return sta; +} + + +CallingConvention* FrameMap::java_calling_convention(const BasicTypeArray* signature, bool outgoing) { + // compute the size of the arguments first. The signature array + // that java_calling_convention takes includes a T_VOID after double + // work items but our signatures do not. + int i; + int sizeargs = 0; + for (i = 0; i < signature->length(); i++) { + sizeargs += type2size[signature->at(i)]; + } + + BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sizeargs); + VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, sizeargs); + int sig_index = 0; + for (i = 0; i < sizeargs; i++, sig_index++) { + sig_bt[i] = signature->at(sig_index); + if (sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) { + sig_bt[i + 1] = T_VOID; + i++; + } + } + + intptr_t out_preserve = SharedRuntime::java_calling_convention(sig_bt, regs, sizeargs, outgoing); + LIR_OprList* args = new LIR_OprList(signature->length()); + for (i = 0; i < sizeargs;) { + BasicType t = sig_bt[i]; + assert(t != T_VOID, "should be skipping these"); + + LIR_Opr opr = map_to_opr(t, regs + i, outgoing); + args->append(opr); + if (opr->is_address()) { + LIR_Address* addr = opr->as_address_ptr(); + assert(addr->disp() == (int)addr->disp(), "out of range value"); + out_preserve = MAX2(out_preserve, (intptr_t)addr->disp() / 4); + } + i += type2size[t]; + } + assert(args->length() == signature->length(), "size mismatch"); + out_preserve += SharedRuntime::out_preserve_stack_slots(); + + if (outgoing) { + // update the space reserved for arguments. + update_reserved_argument_area_size(out_preserve); + } + return new CallingConvention(args, out_preserve); +} + + +CallingConvention* FrameMap::c_calling_convention(const BasicTypeArray* signature) { + // compute the size of the arguments first. The signature array + // that java_calling_convention takes includes a T_VOID after double + // work items but our signatures do not. + int i; + int sizeargs = 0; + for (i = 0; i < signature->length(); i++) { + sizeargs += type2size[signature->at(i)]; + } + + BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sizeargs); + VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, sizeargs); + int sig_index = 0; + for (i = 0; i < sizeargs; i++, sig_index++) { + sig_bt[i] = signature->at(sig_index); + if (sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) { + sig_bt[i + 1] = T_VOID; + i++; + } + } + + intptr_t out_preserve = SharedRuntime::c_calling_convention(sig_bt, regs, sizeargs); + LIR_OprList* args = new LIR_OprList(signature->length()); + for (i = 0; i < sizeargs;) { + BasicType t = sig_bt[i]; + assert(t != T_VOID, "should be skipping these"); + + // C calls are always outgoing + bool outgoing = true; + LIR_Opr opr = map_to_opr(t, regs + i, outgoing); + // they might be of different types if for instance floating point + // values are passed in cpu registers, but the sizes must match. + assert(type2size[opr->type()] == type2size[t], "type mismatch"); + args->append(opr); + if (opr->is_address()) { + LIR_Address* addr = opr->as_address_ptr(); + out_preserve = MAX2(out_preserve, (intptr_t)addr->disp() / 4); + } + i += type2size[t]; + } + assert(args->length() == signature->length(), "size mismatch"); + out_preserve += SharedRuntime::out_preserve_stack_slots(); + update_reserved_argument_area_size(out_preserve); + return new CallingConvention(args, out_preserve); +} + + +//-------------------------------------------------------- +// FrameMap +//-------------------------------------------------------- + +bool FrameMap::_init_done = false; +Register FrameMap::_cpu_rnr2reg [FrameMap::nof_cpu_regs]; +int FrameMap::_cpu_reg2rnr [FrameMap::nof_cpu_regs]; + + +FrameMap::FrameMap(ciMethod* method, int monitors, int reserved_argument_area_size) { + if (!_init_done) init(); + + _framesize = -1; + _num_spills = -1; + + assert(monitors >= 0, "not set"); + _num_monitors = monitors; + assert(reserved_argument_area_size >= 0, "not set"); + _reserved_argument_area_size = MAX2(4, reserved_argument_area_size) * BytesPerWord; + + _argcount = method->arg_size(); + _argument_locations = new intArray(_argcount, -1); + _incoming_arguments = java_calling_convention(signature_type_array_for(method), false); + _oop_map_arg_count = _incoming_arguments->reserved_stack_slots(); + + int java_index = 0; + for (int i = 0; i < _incoming_arguments->length(); i++) { + LIR_Opr opr = _incoming_arguments->at(i); + if (opr->is_address()) { + LIR_Address* address = opr->as_address_ptr(); + _argument_locations->at_put(java_index, address->disp() - STACK_BIAS); + _incoming_arguments->args()->at_put(i, LIR_OprFact::stack(java_index, as_BasicType(as_ValueType(address->type())))); + } + java_index += type2size[opr->type()]; + } + +} + + +bool FrameMap::finalize_frame(int nof_slots) { + assert(nof_slots >= 0, "must be positive"); + assert(_num_spills == -1, "can only be set once"); + _num_spills = nof_slots; + assert(_framesize == -1, "should only be calculated once"); + _framesize = round_to(in_bytes(sp_offset_for_monitor_base(0)) + + _num_monitors * sizeof(BasicObjectLock) + + sizeof(intptr_t) + // offset of deopt orig pc + frame_pad_in_bytes, + StackAlignmentInBytes) / 4; + int java_index = 0; + for (int i = 0; i < _incoming_arguments->length(); i++) { + LIR_Opr opr = _incoming_arguments->at(i); + if (opr->is_stack()) { + _argument_locations->at_put(java_index, in_bytes(framesize_in_bytes()) + + _argument_locations->at(java_index)); + } + java_index += type2size[opr->type()]; + } + // make sure it's expressible on the platform + return validate_frame(); +} + +VMReg FrameMap::sp_offset2vmreg(ByteSize offset) const { + int offset_in_bytes = in_bytes(offset); + assert(offset_in_bytes % 4 == 0, "must be multiple of 4 bytes"); + assert(offset_in_bytes / 4 < framesize() + oop_map_arg_count(), "out of range"); + return VMRegImpl::stack2reg(offset_in_bytes / 4); +} + + +bool FrameMap::location_for_sp_offset(ByteSize byte_offset_from_sp, + Location::Type loc_type, + Location* loc) const { + int offset = in_bytes(byte_offset_from_sp); + assert(offset >= 0, "incorrect offset"); + if (!Location::legal_offset_in_bytes(offset)) { + return false; + } + Location tmp_loc = Location::new_stk_loc(loc_type, offset); + *loc = tmp_loc; + return true; +} + + +bool FrameMap::locations_for_slot (int index, Location::Type loc_type, + Location* loc, Location* second) const { + ByteSize offset_from_sp = sp_offset_for_slot(index); + if (!location_for_sp_offset(offset_from_sp, loc_type, loc)) { + return false; + } + if (second != NULL) { + // two word item + offset_from_sp = offset_from_sp + in_ByteSize(4); + return location_for_sp_offset(offset_from_sp, loc_type, second); + } + return true; +} + +////////////////////// +// Public accessors // +////////////////////// + + +ByteSize FrameMap::sp_offset_for_slot(const int index) const { + if (index < argcount()) { + int offset = _argument_locations->at(index); + assert(offset != -1, "not a memory argument"); + assert(offset >= framesize() * 4, "argument inside of frame"); + return in_ByteSize(offset); + } + ByteSize offset = sp_offset_for_spill(index - argcount()); + assert(in_bytes(offset) < framesize() * 4, "spill outside of frame"); + return offset; +} + + +ByteSize FrameMap::sp_offset_for_double_slot(const int index) const { + ByteSize offset = sp_offset_for_slot(index); + if (index >= argcount()) { + assert(in_bytes(offset) + 4 < framesize() * 4, "spill outside of frame"); + } + return offset; +} + + +ByteSize FrameMap::sp_offset_for_spill(const int index) const { + assert(index >= 0 && index < _num_spills, "out of range"); + int offset = round_to(first_available_sp_in_frame + _reserved_argument_area_size, sizeof(double)) + + index * spill_slot_size_in_bytes; + return in_ByteSize(offset); +} + +ByteSize FrameMap::sp_offset_for_monitor_base(const int index) const { + int end_of_spills = round_to(first_available_sp_in_frame + _reserved_argument_area_size, sizeof(double)) + + _num_spills * spill_slot_size_in_bytes; + int offset = round_to(end_of_spills, HeapWordSize) + index * sizeof(BasicObjectLock); + return in_ByteSize(offset); +} + +ByteSize FrameMap::sp_offset_for_monitor_lock(int index) const { + check_monitor_index(index); + return sp_offset_for_monitor_base(index) + in_ByteSize(BasicObjectLock::lock_offset_in_bytes());; +} + +ByteSize FrameMap::sp_offset_for_monitor_object(int index) const { + check_monitor_index(index); + return sp_offset_for_monitor_base(index) + in_ByteSize(BasicObjectLock::obj_offset_in_bytes()); +} + +void FrameMap::print_frame_layout() const { + int svar; + tty->print_cr("#####################################"); + tty->print_cr("Frame size in words %d", framesize()); + + if( _num_monitors > 0) { + tty->print_cr("monitor [0]:%d | [%2d]:%d", + in_bytes(sp_offset_for_monitor_base(0)), + in_bytes(sp_offset_for_monitor_base(_num_monitors))); + } + if( _num_spills > 0) { + svar = _num_spills - 1; + if(svar == 0) + tty->print_cr("spill [0]:%d", in_bytes(sp_offset_for_spill(0))); + else + tty->print_cr("spill [0]:%d | [%2d]:%d", in_bytes(sp_offset_for_spill(0)), + svar, + in_bytes(sp_offset_for_spill(svar))); + } +} + + +// For OopMaps, map a local variable or spill index to an VMReg. +// This is the offset from sp() in the frame of the slot for the index, +// skewed by SharedInfo::stack0 to indicate a stack location (vs.a register.) +// +// C ABI size + +// framesize + framesize + +// stack0 stack0 stack0 0 <- VMReg->value() +// | | | | +// ..........|..............|..............|.............| +// 0 1 2 3 | | 4 5 6 ...... | <- local indices +// ^ ^ sp() +// | | +// arguments non-argument locals + + +VMReg FrameMap::regname(LIR_Opr opr) const { + if (opr->is_single_cpu()) { + assert(!opr->is_virtual(), "should not see virtual registers here"); + return opr->as_register()->as_VMReg(); + } else if (opr->is_single_stack()) { + return sp_offset2vmreg(sp_offset_for_slot(opr->single_stack_ix())); + } else if (opr->is_address()) { + LIR_Address* addr = opr->as_address_ptr(); + assert(addr->base() == stack_pointer(), "sp based addressing only"); + return sp_offset2vmreg(in_ByteSize(addr->index()->as_jint())); + } + ShouldNotReachHere(); + return VMRegImpl::Bad(); +} + + + + +// ------------ extra spill slots --------------- diff --git a/hotspot/src/share/vm/c1/c1_FrameMap.hpp b/hotspot/src/share/vm/c1/c1_FrameMap.hpp new file mode 100644 index 00000000000..c8dd5643bf5 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_FrameMap.hpp @@ -0,0 +1,266 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ciMethod; +class CallingConvention; +class BasicTypeArray; +class BasicTypeList; + +//-------------------------------------------------------- +// FrameMap +//-------------------------------------------------------- + +// This class is responsible of mapping items (locals, monitors, spill +// slots and registers to their frame location +// +// The monitors are specified by a consecutive index, although each monitor entry +// occupies two words. The monitor_index is 0.._num_monitors +// The spill index is similar to local index; it is in range 0..(open) +// +// The CPU registers are mapped using a fixed table; register with number 0 +// is the most used one. + + +// stack grow direction --> SP +// +----------+---+----------+-------+------------------------+-----+ +// |arguments | x | monitors | spill | reserved argument area | ABI | +// +----------+---+----------+-------+------------------------+-----+ +// +// x = ABI area (SPARC) or return adress and link (i486) +// ABI = ABI area (SPARC) or nothing (i486) + + +class LIR_OprDesc; +typedef LIR_OprDesc* LIR_Opr; + + +class FrameMap : public CompilationResourceObj { + public: + enum { + nof_cpu_regs = pd_nof_cpu_regs_frame_map, + nof_fpu_regs = pd_nof_fpu_regs_frame_map, + + nof_cpu_regs_reg_alloc = pd_nof_cpu_regs_reg_alloc, + nof_fpu_regs_reg_alloc = pd_nof_fpu_regs_reg_alloc, + + nof_caller_save_cpu_regs = pd_nof_caller_save_cpu_regs_frame_map, + nof_caller_save_fpu_regs = pd_nof_caller_save_fpu_regs_frame_map, + + spill_slot_size_in_bytes = 4 + }; + +# include "incls/_c1_FrameMap_pd.hpp.incl" // platform dependent declarations + + friend class LIR_OprDesc; + + private: + static bool _init_done; + static Register _cpu_rnr2reg [nof_cpu_regs]; + static int _cpu_reg2rnr [nof_cpu_regs]; + + static LIR_Opr _caller_save_cpu_regs [nof_caller_save_cpu_regs]; + static LIR_Opr _caller_save_fpu_regs [nof_caller_save_fpu_regs]; + + int _framesize; + int _argcount; + int _num_monitors; + int _num_spills; + int _reserved_argument_area_size; + int _oop_map_arg_count; + + CallingConvention* _incoming_arguments; + intArray* _argument_locations; + + void check_spill_index (int spill_index) const { assert(spill_index >= 0, "bad index"); } + void check_monitor_index (int monitor_index) const { assert(monitor_index >= 0 && + monitor_index < _num_monitors, "bad index"); } + + static Register cpu_rnr2reg (int rnr) { + assert(_init_done, "tables not initialized"); + debug_only(cpu_range_check(rnr);) + return _cpu_rnr2reg[rnr]; + } + + static int cpu_reg2rnr (Register reg) { + assert(_init_done, "tables not initialized"); + debug_only(cpu_range_check(reg->encoding());) + return _cpu_reg2rnr[reg->encoding()]; + } + + static void map_register(int rnr, Register reg) { + debug_only(cpu_range_check(rnr);) + debug_only(cpu_range_check(reg->encoding());) + _cpu_rnr2reg[rnr] = reg; + _cpu_reg2rnr[reg->encoding()] = rnr; + } + + void update_reserved_argument_area_size (int size) { + assert(size >= 0, "check"); + _reserved_argument_area_size = MAX2(_reserved_argument_area_size, size); + } + + protected: +#ifndef PRODUCT + static void cpu_range_check (int rnr) { assert(0 <= rnr && rnr < nof_cpu_regs, "cpu register number is too big"); } + static void fpu_range_check (int rnr) { assert(0 <= rnr && rnr < nof_fpu_regs, "fpu register number is too big"); } +#endif + + + ByteSize sp_offset_for_monitor_base(const int idx) const; + + Address make_new_address(ByteSize sp_offset) const; + + ByteSize sp_offset_for_slot(const int idx) const; + ByteSize sp_offset_for_double_slot(const int idx) const; + ByteSize sp_offset_for_spill(const int idx) const; + ByteSize sp_offset_for_monitor_lock(int monitor_index) const; + ByteSize sp_offset_for_monitor_object(int monitor_index) const; + + VMReg sp_offset2vmreg(ByteSize offset) const; + + // platform dependent hook used to check that frame is properly + // addressable on the platform. Used by sparc to verify that all + // stack addresses are expressable in a simm13. + bool validate_frame(); + + static LIR_Opr map_to_opr(BasicType type, VMRegPair* reg, bool incoming); + + public: + // Opr representing the stack_pointer on this platform + static LIR_Opr stack_pointer(); + + static BasicTypeArray* signature_type_array_for(const ciMethod* method); + static BasicTypeArray* signature_type_array_for(const char * signature); + + // for outgoing calls, these also update the reserved area to + // include space for arguments and any ABI area. + CallingConvention* c_calling_convention (const BasicTypeArray* signature); + CallingConvention* java_calling_convention (const BasicTypeArray* signature, bool outgoing); + + // deopt support + ByteSize sp_offset_for_orig_pc() { return sp_offset_for_monitor_base(_num_monitors); } + + static LIR_Opr as_opr(Register r) { + return LIR_OprFact::single_cpu(cpu_reg2rnr(r)); + } + static LIR_Opr as_oop_opr(Register r) { + return LIR_OprFact::single_cpu_oop(cpu_reg2rnr(r)); + } + + FrameMap(ciMethod* method, int monitors, int reserved_argument_area_size); + bool finalize_frame(int nof_slots); + + int reserved_argument_area_size () const { return _reserved_argument_area_size; } + int framesize () const { assert(_framesize != -1, "hasn't been calculated"); return _framesize; } + ByteSize framesize_in_bytes () const { return in_ByteSize(framesize() * 4); } + int num_monitors () const { return _num_monitors; } + int num_spills () const { assert(_num_spills >= 0, "not set"); return _num_spills; } + int argcount () const { assert(_argcount >= 0, "not set"); return _argcount; } + + int oop_map_arg_count() const { return _oop_map_arg_count; } + + CallingConvention* incoming_arguments() const { return _incoming_arguments; } + + // convenience routines + Address address_for_slot(int index, int sp_adjust = 0) const { + return make_new_address(sp_offset_for_slot(index) + in_ByteSize(sp_adjust)); + } + Address address_for_double_slot(int index, int sp_adjust = 0) const { + return make_new_address(sp_offset_for_double_slot(index) + in_ByteSize(sp_adjust)); + } + Address address_for_monitor_lock(int monitor_index) const { + return make_new_address(sp_offset_for_monitor_lock(monitor_index)); + } + Address address_for_monitor_object(int monitor_index) const { + return make_new_address(sp_offset_for_monitor_object(monitor_index)); + } + + void print_frame_layout() const; + + // Creates Location describing desired slot and returns it via pointer + // to Location object. Returns true if the stack frame offset was legal + // (as defined by Location::legal_offset_in_bytes()), false otherwise. + // Do not use the returned location if this returns false. + bool location_for_sp_offset(ByteSize byte_offset_from_sp, + Location::Type loc_type, Location* loc) const; + + bool location_for_monitor_lock (int monitor_index, Location* loc) const { + return location_for_sp_offset(sp_offset_for_monitor_lock(monitor_index), Location::normal, loc); + } + bool location_for_monitor_object(int monitor_index, Location* loc) const { + return location_for_sp_offset(sp_offset_for_monitor_object(monitor_index), Location::oop, loc); + } + bool locations_for_slot (int index, Location::Type loc_type, + Location* loc, Location* second = NULL) const; + + VMReg slot_regname(int index) const { + return sp_offset2vmreg(sp_offset_for_slot(index)); + } + VMReg monitor_object_regname(int monitor_index) const { + return sp_offset2vmreg(sp_offset_for_monitor_object(monitor_index)); + } + VMReg regname(LIR_Opr opr) const; + + static LIR_Opr caller_save_cpu_reg_at(int i) { + assert(i >= 0 && i < nof_caller_save_cpu_regs, "out of bounds"); + return _caller_save_cpu_regs[i]; + } + + static LIR_Opr caller_save_fpu_reg_at(int i) { + assert(i >= 0 && i < nof_caller_save_fpu_regs, "out of bounds"); + return _caller_save_fpu_regs[i]; + } + + static void init(); +}; + +// CallingConvention +//-------------------------------------------------------- + +class CallingConvention: public ResourceObj { + private: + LIR_OprList* _args; + int _reserved_stack_slots; + + public: + CallingConvention (LIR_OprList* args, int reserved_stack_slots) + : _args(args) + , _reserved_stack_slots(reserved_stack_slots) {} + + LIR_OprList* args() { return _args; } + + LIR_Opr at(int i) const { return _args->at(i); } + int length() const { return _args->length(); } + + // Indicates number of real frame slots used by arguments passed on stack. + int reserved_stack_slots() const { return _reserved_stack_slots; } + +#ifndef PRODUCT + void print () const { + for (int i = 0; i < length(); i++) { + at(i)->print(); + } + } +#endif // PRODUCT +}; diff --git a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp new file mode 100644 index 00000000000..efa25e3915f --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp @@ -0,0 +1,3835 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_GraphBuilder.cpp.incl" + +class BlockListBuilder VALUE_OBJ_CLASS_SPEC { + private: + Compilation* _compilation; + IRScope* _scope; + + BlockList _blocks; // internal list of all blocks + BlockList* _bci2block; // mapping from bci to blocks for GraphBuilder + + // fields used by mark_loops + BitMap _active; // for iteration of control flow graph + BitMap _visited; // for iteration of control flow graph + intArray _loop_map; // caches the information if a block is contained in a loop + int _next_loop_index; // next free loop number + int _next_block_number; // for reverse postorder numbering of blocks + + // accessors + Compilation* compilation() const { return _compilation; } + IRScope* scope() const { return _scope; } + ciMethod* method() const { return scope()->method(); } + XHandlers* xhandlers() const { return scope()->xhandlers(); } + + // unified bailout support + void bailout(const char* msg) const { compilation()->bailout(msg); } + bool bailed_out() const { return compilation()->bailed_out(); } + + // helper functions + BlockBegin* make_block_at(int bci, BlockBegin* predecessor); + void handle_exceptions(BlockBegin* current, int cur_bci); + void handle_jsr(BlockBegin* current, int sr_bci, int next_bci); + void store_one(BlockBegin* current, int local); + void store_two(BlockBegin* current, int local); + void set_entries(int osr_bci); + void set_leaders(); + + void make_loop_header(BlockBegin* block); + void mark_loops(); + int mark_loops(BlockBegin* b, bool in_subroutine); + + // debugging +#ifndef PRODUCT + void print(); +#endif + + public: + // creation + BlockListBuilder(Compilation* compilation, IRScope* scope, int osr_bci); + + // accessors for GraphBuilder + BlockList* bci2block() const { return _bci2block; } +}; + + +// Implementation of BlockListBuilder + +BlockListBuilder::BlockListBuilder(Compilation* compilation, IRScope* scope, int osr_bci) + : _compilation(compilation) + , _scope(scope) + , _blocks(16) + , _bci2block(new BlockList(scope->method()->code_size(), NULL)) + , _next_block_number(0) + , _active() // size not known yet + , _visited() // size not known yet + , _next_loop_index(0) + , _loop_map() // size not known yet +{ + set_entries(osr_bci); + set_leaders(); + CHECK_BAILOUT(); + + mark_loops(); + NOT_PRODUCT(if (PrintInitialBlockList) print()); + +#ifndef PRODUCT + if (PrintCFGToFile) { + stringStream title; + title.print("BlockListBuilder "); + scope->method()->print_name(&title); + CFGPrinter::print_cfg(_bci2block, title.as_string(), false, false); + } +#endif +} + + +void BlockListBuilder::set_entries(int osr_bci) { + // generate start blocks + BlockBegin* std_entry = make_block_at(0, NULL); + if (scope()->caller() == NULL) { + std_entry->set(BlockBegin::std_entry_flag); + } + if (osr_bci != -1) { + BlockBegin* osr_entry = make_block_at(osr_bci, NULL); + osr_entry->set(BlockBegin::osr_entry_flag); + } + + // generate exception entry blocks + XHandlers* list = xhandlers(); + const int n = list->length(); + for (int i = 0; i < n; i++) { + XHandler* h = list->handler_at(i); + BlockBegin* entry = make_block_at(h->handler_bci(), NULL); + entry->set(BlockBegin::exception_entry_flag); + h->set_entry_block(entry); + } +} + + +BlockBegin* BlockListBuilder::make_block_at(int cur_bci, BlockBegin* predecessor) { + assert(method()->bci_block_start().at(cur_bci), "wrong block starts of MethodLivenessAnalyzer"); + + BlockBegin* block = _bci2block->at(cur_bci); + if (block == NULL) { + block = new BlockBegin(cur_bci); + block->init_stores_to_locals(method()->max_locals()); + _bci2block->at_put(cur_bci, block); + _blocks.append(block); + + assert(predecessor == NULL || predecessor->bci() < cur_bci, "targets for backward branches must already exist"); + } + + if (predecessor != NULL) { + if (block->is_set(BlockBegin::exception_entry_flag)) { + BAILOUT_("Exception handler can be reached by both normal and exceptional control flow", block); + } + + predecessor->add_successor(block); + block->increment_total_preds(); + } + + return block; +} + + +inline void BlockListBuilder::store_one(BlockBegin* current, int local) { + current->stores_to_locals().set_bit(local); +} +inline void BlockListBuilder::store_two(BlockBegin* current, int local) { + store_one(current, local); + store_one(current, local + 1); +} + + +void BlockListBuilder::handle_exceptions(BlockBegin* current, int cur_bci) { + // Draws edges from a block to its exception handlers + XHandlers* list = xhandlers(); + const int n = list->length(); + + for (int i = 0; i < n; i++) { + XHandler* h = list->handler_at(i); + + if (h->covers(cur_bci)) { + BlockBegin* entry = h->entry_block(); + assert(entry != NULL && entry == _bci2block->at(h->handler_bci()), "entry must be set"); + assert(entry->is_set(BlockBegin::exception_entry_flag), "flag must be set"); + + // add each exception handler only once + if (!current->is_successor(entry)) { + current->add_successor(entry); + entry->increment_total_preds(); + } + + // stop when reaching catchall + if (h->catch_type() == 0) break; + } + } +} + +void BlockListBuilder::handle_jsr(BlockBegin* current, int sr_bci, int next_bci) { + // start a new block after jsr-bytecode and link this block into cfg + make_block_at(next_bci, current); + + // start a new block at the subroutine entry at mark it with special flag + BlockBegin* sr_block = make_block_at(sr_bci, current); + if (!sr_block->is_set(BlockBegin::subroutine_entry_flag)) { + sr_block->set(BlockBegin::subroutine_entry_flag); + } +} + + +void BlockListBuilder::set_leaders() { + bool has_xhandlers = xhandlers()->has_handlers(); + BlockBegin* current = NULL; + + // The information which bci starts a new block simplifies the analysis + // Without it, backward branches could jump to a bci where no block was created + // during bytecode iteration. This would require the creation of a new block at the + // branch target and a modification of the successor lists. + BitMap bci_block_start = method()->bci_block_start(); + + ciBytecodeStream s(method()); + while (s.next() != ciBytecodeStream::EOBC()) { + int cur_bci = s.cur_bci(); + + if (bci_block_start.at(cur_bci)) { + current = make_block_at(cur_bci, current); + } + assert(current != NULL, "must have current block"); + + if (has_xhandlers && GraphBuilder::can_trap(method(), s.cur_bc())) { + handle_exceptions(current, cur_bci); + } + + switch (s.cur_bc()) { + // track stores to local variables for selective creation of phi functions + case Bytecodes::_iinc: store_one(current, s.get_index()); break; + case Bytecodes::_istore: store_one(current, s.get_index()); break; + case Bytecodes::_lstore: store_two(current, s.get_index()); break; + case Bytecodes::_fstore: store_one(current, s.get_index()); break; + case Bytecodes::_dstore: store_two(current, s.get_index()); break; + case Bytecodes::_astore: store_one(current, s.get_index()); break; + case Bytecodes::_istore_0: store_one(current, 0); break; + case Bytecodes::_istore_1: store_one(current, 1); break; + case Bytecodes::_istore_2: store_one(current, 2); break; + case Bytecodes::_istore_3: store_one(current, 3); break; + case Bytecodes::_lstore_0: store_two(current, 0); break; + case Bytecodes::_lstore_1: store_two(current, 1); break; + case Bytecodes::_lstore_2: store_two(current, 2); break; + case Bytecodes::_lstore_3: store_two(current, 3); break; + case Bytecodes::_fstore_0: store_one(current, 0); break; + case Bytecodes::_fstore_1: store_one(current, 1); break; + case Bytecodes::_fstore_2: store_one(current, 2); break; + case Bytecodes::_fstore_3: store_one(current, 3); break; + case Bytecodes::_dstore_0: store_two(current, 0); break; + case Bytecodes::_dstore_1: store_two(current, 1); break; + case Bytecodes::_dstore_2: store_two(current, 2); break; + case Bytecodes::_dstore_3: store_two(current, 3); break; + case Bytecodes::_astore_0: store_one(current, 0); break; + case Bytecodes::_astore_1: store_one(current, 1); break; + case Bytecodes::_astore_2: store_one(current, 2); break; + case Bytecodes::_astore_3: store_one(current, 3); break; + + // track bytecodes that affect the control flow + case Bytecodes::_athrow: // fall through + case Bytecodes::_ret: // fall through + case Bytecodes::_ireturn: // fall through + case Bytecodes::_lreturn: // fall through + case Bytecodes::_freturn: // fall through + case Bytecodes::_dreturn: // fall through + case Bytecodes::_areturn: // fall through + case Bytecodes::_return: + current = NULL; + break; + + case Bytecodes::_ifeq: // fall through + case Bytecodes::_ifne: // fall through + case Bytecodes::_iflt: // fall through + case Bytecodes::_ifge: // fall through + case Bytecodes::_ifgt: // fall through + case Bytecodes::_ifle: // fall through + case Bytecodes::_if_icmpeq: // fall through + case Bytecodes::_if_icmpne: // fall through + case Bytecodes::_if_icmplt: // fall through + case Bytecodes::_if_icmpge: // fall through + case Bytecodes::_if_icmpgt: // fall through + case Bytecodes::_if_icmple: // fall through + case Bytecodes::_if_acmpeq: // fall through + case Bytecodes::_if_acmpne: // fall through + case Bytecodes::_ifnull: // fall through + case Bytecodes::_ifnonnull: + make_block_at(s.next_bci(), current); + make_block_at(s.get_dest(), current); + current = NULL; + break; + + case Bytecodes::_goto: + make_block_at(s.get_dest(), current); + current = NULL; + break; + + case Bytecodes::_goto_w: + make_block_at(s.get_far_dest(), current); + current = NULL; + break; + + case Bytecodes::_jsr: + handle_jsr(current, s.get_dest(), s.next_bci()); + current = NULL; + break; + + case Bytecodes::_jsr_w: + handle_jsr(current, s.get_far_dest(), s.next_bci()); + current = NULL; + break; + + case Bytecodes::_tableswitch: { + // set block for each case + Bytecode_tableswitch *switch_ = Bytecode_tableswitch_at(s.cur_bcp()); + int l = switch_->length(); + for (int i = 0; i < l; i++) { + make_block_at(cur_bci + switch_->dest_offset_at(i), current); + } + make_block_at(cur_bci + switch_->default_offset(), current); + current = NULL; + break; + } + + case Bytecodes::_lookupswitch: { + // set block for each case + Bytecode_lookupswitch *switch_ = Bytecode_lookupswitch_at(s.cur_bcp()); + int l = switch_->number_of_pairs(); + for (int i = 0; i < l; i++) { + make_block_at(cur_bci + switch_->pair_at(i)->offset(), current); + } + make_block_at(cur_bci + switch_->default_offset(), current); + current = NULL; + break; + } + } + } +} + + +void BlockListBuilder::mark_loops() { + ResourceMark rm; + + _active = BitMap(BlockBegin::number_of_blocks()); _active.clear(); + _visited = BitMap(BlockBegin::number_of_blocks()); _visited.clear(); + _loop_map = intArray(BlockBegin::number_of_blocks(), 0); + _next_loop_index = 0; + _next_block_number = _blocks.length(); + + // recursively iterate the control flow graph + mark_loops(_bci2block->at(0), false); + assert(_next_block_number >= 0, "invalid block numbers"); +} + +void BlockListBuilder::make_loop_header(BlockBegin* block) { + if (block->is_set(BlockBegin::exception_entry_flag)) { + // exception edges may look like loops but don't mark them as such + // since it screws up block ordering. + return; + } + if (!block->is_set(BlockBegin::parser_loop_header_flag)) { + block->set(BlockBegin::parser_loop_header_flag); + + assert(_loop_map.at(block->block_id()) == 0, "must not be set yet"); + assert(0 <= _next_loop_index && _next_loop_index < BitsPerInt, "_next_loop_index is used as a bit-index in integer"); + _loop_map.at_put(block->block_id(), 1 << _next_loop_index); + if (_next_loop_index < 31) _next_loop_index++; + } else { + // block already marked as loop header + assert(is_power_of_2(_loop_map.at(block->block_id())), "exactly one bit must be set"); + } +} + +int BlockListBuilder::mark_loops(BlockBegin* block, bool in_subroutine) { + int block_id = block->block_id(); + + if (_visited.at(block_id)) { + if (_active.at(block_id)) { + // reached block via backward branch + make_loop_header(block); + } + // return cached loop information for this block + return _loop_map.at(block_id); + } + + if (block->is_set(BlockBegin::subroutine_entry_flag)) { + in_subroutine = true; + } + + // set active and visited bits before successors are processed + _visited.set_bit(block_id); + _active.set_bit(block_id); + + intptr_t loop_state = 0; + for (int i = block->number_of_sux() - 1; i >= 0; i--) { + // recursively process all successors + loop_state |= mark_loops(block->sux_at(i), in_subroutine); + } + + // clear active-bit after all successors are processed + _active.clear_bit(block_id); + + // reverse-post-order numbering of all blocks + block->set_depth_first_number(_next_block_number); + _next_block_number--; + + if (loop_state != 0 || in_subroutine ) { + // block is contained at least in one loop, so phi functions are necessary + // phi functions are also necessary for all locals stored in a subroutine + scope()->requires_phi_function().set_union(block->stores_to_locals()); + } + + if (block->is_set(BlockBegin::parser_loop_header_flag)) { + int header_loop_state = _loop_map.at(block_id); + assert(is_power_of_2((unsigned)header_loop_state), "exactly one bit must be set"); + + // If the highest bit is set (i.e. when integer value is negative), the method + // has 32 or more loops. This bit is never cleared because it is used for multiple loops + if (header_loop_state >= 0) { + clear_bits(loop_state, header_loop_state); + } + } + + // cache and return loop information for this block + _loop_map.at_put(block_id, loop_state); + return loop_state; +} + + +#ifndef PRODUCT + +int compare_depth_first(BlockBegin** a, BlockBegin** b) { + return (*a)->depth_first_number() - (*b)->depth_first_number(); +} + +void BlockListBuilder::print() { + tty->print("----- initial block list of BlockListBuilder for method "); + method()->print_short_name(); + tty->cr(); + + // better readability if blocks are sorted in processing order + _blocks.sort(compare_depth_first); + + for (int i = 0; i < _blocks.length(); i++) { + BlockBegin* cur = _blocks.at(i); + tty->print("%4d: B%-4d bci: %-4d preds: %-4d ", cur->depth_first_number(), cur->block_id(), cur->bci(), cur->total_preds()); + + tty->print(cur->is_set(BlockBegin::std_entry_flag) ? " std" : " "); + tty->print(cur->is_set(BlockBegin::osr_entry_flag) ? " osr" : " "); + tty->print(cur->is_set(BlockBegin::exception_entry_flag) ? " ex" : " "); + tty->print(cur->is_set(BlockBegin::subroutine_entry_flag) ? " sr" : " "); + tty->print(cur->is_set(BlockBegin::parser_loop_header_flag) ? " lh" : " "); + + if (cur->number_of_sux() > 0) { + tty->print(" sux: "); + for (int j = 0; j < cur->number_of_sux(); j++) { + BlockBegin* sux = cur->sux_at(j); + tty->print("B%d ", sux->block_id()); + } + } + tty->cr(); + } +} + +#endif + + +// A simple growable array of Values indexed by ciFields +class FieldBuffer: public CompilationResourceObj { + private: + GrowableArray _values; + + public: + FieldBuffer() {} + + void kill() { + _values.trunc_to(0); + } + + Value at(ciField* field) { + assert(field->holder()->is_loaded(), "must be a loaded field"); + int offset = field->offset(); + if (offset < _values.length()) { + return _values.at(offset); + } else { + return NULL; + } + } + + void at_put(ciField* field, Value value) { + assert(field->holder()->is_loaded(), "must be a loaded field"); + int offset = field->offset(); + _values.at_put_grow(offset, value, NULL); + } + +}; + + +// MemoryBuffer is fairly simple model of the current state of memory. +// It partitions memory into several pieces. The first piece is +// generic memory where little is known about the owner of the memory. +// This is conceptually represented by the tuple which says +// that the field F of object O has value V. This is flattened so +// that F is represented by the offset of the field and the parallel +// arrays _objects and _values are used for O and V. Loads of O.F can +// simply use V. Newly allocated objects are kept in a separate list +// along with a parallel array for each object which represents the +// current value of its fields. Stores of the default value to fields +// which have never been stored to before are eliminated since they +// are redundant. Once newly allocated objects are stored into +// another object or they are passed out of the current compile they +// are treated like generic memory. + +class MemoryBuffer: public CompilationResourceObj { + private: + FieldBuffer _values; + GrowableArray _objects; + GrowableArray _newobjects; + GrowableArray _fields; + + public: + MemoryBuffer() {} + + StoreField* store(StoreField* st) { + if (!EliminateFieldAccess) { + return st; + } + + Value object = st->obj(); + Value value = st->value(); + ciField* field = st->field(); + if (field->holder()->is_loaded()) { + int offset = field->offset(); + int index = _newobjects.find(object); + if (index != -1) { + // newly allocated object with no other stores performed on this field + FieldBuffer* buf = _fields.at(index); + if (buf->at(field) == NULL && is_default_value(value)) { +#ifndef PRODUCT + if (PrintIRDuringConstruction && Verbose) { + tty->print_cr("Eliminated store for object %d:", index); + st->print_line(); + } +#endif + return NULL; + } else { + buf->at_put(field, value); + } + } else { + _objects.at_put_grow(offset, object, NULL); + _values.at_put(field, value); + } + + store_value(value); + } else { + // if we held onto field names we could alias based on names but + // we don't know what's being stored to so kill it all. + kill(); + } + return st; + } + + + // return true if this value correspond to the default value of a field. + bool is_default_value(Value value) { + Constant* con = value->as_Constant(); + if (con) { + switch (con->type()->tag()) { + case intTag: return con->type()->as_IntConstant()->value() == 0; + case longTag: return con->type()->as_LongConstant()->value() == 0; + case floatTag: return jint_cast(con->type()->as_FloatConstant()->value()) == 0; + case doubleTag: return jlong_cast(con->type()->as_DoubleConstant()->value()) == jlong_cast(0); + case objectTag: return con->type() == objectNull; + default: ShouldNotReachHere(); + } + } + return false; + } + + + // return either the actual value of a load or the load itself + Value load(LoadField* load) { + if (!EliminateFieldAccess) { + return load; + } + + if (RoundFPResults && UseSSE < 2 && load->type()->is_float_kind()) { + // can't skip load since value might get rounded as a side effect + return load; + } + + ciField* field = load->field(); + Value object = load->obj(); + if (field->holder()->is_loaded() && !field->is_volatile()) { + int offset = field->offset(); + Value result = NULL; + int index = _newobjects.find(object); + if (index != -1) { + result = _fields.at(index)->at(field); + } else if (_objects.at_grow(offset, NULL) == object) { + result = _values.at(field); + } + if (result != NULL) { +#ifndef PRODUCT + if (PrintIRDuringConstruction && Verbose) { + tty->print_cr("Eliminated load: "); + load->print_line(); + } +#endif + assert(result->type()->tag() == load->type()->tag(), "wrong types"); + return result; + } + } + return load; + } + + // Record this newly allocated object + void new_instance(NewInstance* object) { + int index = _newobjects.length(); + _newobjects.append(object); + if (_fields.at_grow(index, NULL) == NULL) { + _fields.at_put(index, new FieldBuffer()); + } else { + _fields.at(index)->kill(); + } + } + + void store_value(Value value) { + int index = _newobjects.find(value); + if (index != -1) { + // stored a newly allocated object into another object. + // Assume we've lost track of it as separate slice of memory. + // We could do better by keeping track of whether individual + // fields could alias each other. + _newobjects.remove_at(index); + // pull out the field info and store it at the end up the list + // of field info list to be reused later. + _fields.append(_fields.at(index)); + _fields.remove_at(index); + } + } + + void kill() { + _newobjects.trunc_to(0); + _objects.trunc_to(0); + _values.kill(); + } +}; + + +// Implementation of GraphBuilder's ScopeData + +GraphBuilder::ScopeData::ScopeData(ScopeData* parent) + : _parent(parent) + , _bci2block(NULL) + , _scope(NULL) + , _has_handler(false) + , _stream(NULL) + , _work_list(NULL) + , _parsing_jsr(false) + , _jsr_xhandlers(NULL) + , _caller_stack_size(-1) + , _continuation(NULL) + , _continuation_state(NULL) + , _num_returns(0) + , _cleanup_block(NULL) + , _cleanup_return_prev(NULL) + , _cleanup_state(NULL) +{ + if (parent != NULL) { + _max_inline_size = (intx) ((float) NestedInliningSizeRatio * (float) parent->max_inline_size() / 100.0f); + } else { + _max_inline_size = MaxInlineSize; + } + if (_max_inline_size < MaxTrivialSize) { + _max_inline_size = MaxTrivialSize; + } +} + + +void GraphBuilder::kill_field(ciField* field) { + if (UseLocalValueNumbering) { + vmap()->kill_field(field); + } +} + + +void GraphBuilder::kill_array(Value value) { + if (UseLocalValueNumbering) { + vmap()->kill_array(value->type()); + } + _memory->store_value(value); +} + + +void GraphBuilder::kill_all() { + if (UseLocalValueNumbering) { + vmap()->kill_all(); + } + _memory->kill(); +} + + +BlockBegin* GraphBuilder::ScopeData::block_at(int bci) { + if (parsing_jsr()) { + // It is necessary to clone all blocks associated with a + // subroutine, including those for exception handlers in the scope + // of the method containing the jsr (because those exception + // handlers may contain ret instructions in some cases). + BlockBegin* block = bci2block()->at(bci); + if (block != NULL && block == parent()->bci2block()->at(bci)) { + BlockBegin* new_block = new BlockBegin(block->bci()); +#ifndef PRODUCT + if (PrintInitialBlockList) { + tty->print_cr("CFG: cloned block %d (bci %d) as block %d for jsr", + block->block_id(), block->bci(), new_block->block_id()); + } +#endif + // copy data from cloned blocked + new_block->set_depth_first_number(block->depth_first_number()); + if (block->is_set(BlockBegin::parser_loop_header_flag)) new_block->set(BlockBegin::parser_loop_header_flag); + // Preserve certain flags for assertion checking + if (block->is_set(BlockBegin::subroutine_entry_flag)) new_block->set(BlockBegin::subroutine_entry_flag); + if (block->is_set(BlockBegin::exception_entry_flag)) new_block->set(BlockBegin::exception_entry_flag); + + // copy was_visited_flag to allow early detection of bailouts + // if a block that is used in a jsr has already been visited before, + // it is shared between the normal control flow and a subroutine + // BlockBegin::try_merge returns false when the flag is set, this leads + // to a compilation bailout + if (block->is_set(BlockBegin::was_visited_flag)) new_block->set(BlockBegin::was_visited_flag); + + bci2block()->at_put(bci, new_block); + block = new_block; + } + return block; + } else { + return bci2block()->at(bci); + } +} + + +XHandlers* GraphBuilder::ScopeData::xhandlers() const { + if (_jsr_xhandlers == NULL) { + assert(!parsing_jsr(), ""); + return scope()->xhandlers(); + } + assert(parsing_jsr(), ""); + return _jsr_xhandlers; +} + + +void GraphBuilder::ScopeData::set_scope(IRScope* scope) { + _scope = scope; + bool parent_has_handler = false; + if (parent() != NULL) { + parent_has_handler = parent()->has_handler(); + } + _has_handler = parent_has_handler || scope->xhandlers()->has_handlers(); +} + + +void GraphBuilder::ScopeData::set_inline_cleanup_info(BlockBegin* block, + Instruction* return_prev, + ValueStack* return_state) { + _cleanup_block = block; + _cleanup_return_prev = return_prev; + _cleanup_state = return_state; +} + + +void GraphBuilder::ScopeData::add_to_work_list(BlockBegin* block) { + if (_work_list == NULL) { + _work_list = new BlockList(); + } + + if (!block->is_set(BlockBegin::is_on_work_list_flag)) { + // Do not start parsing the continuation block while in a + // sub-scope + if (parsing_jsr()) { + if (block == jsr_continuation()) { + return; + } + } else { + if (block == continuation()) { + return; + } + } + block->set(BlockBegin::is_on_work_list_flag); + _work_list->push(block); + + sort_top_into_worklist(_work_list, block); + } +} + + +void GraphBuilder::sort_top_into_worklist(BlockList* worklist, BlockBegin* top) { + assert(worklist->top() == top, ""); + // sort block descending into work list + const int dfn = top->depth_first_number(); + assert(dfn != -1, "unknown depth first number"); + int i = worklist->length()-2; + while (i >= 0) { + BlockBegin* b = worklist->at(i); + if (b->depth_first_number() < dfn) { + worklist->at_put(i+1, b); + } else { + break; + } + i --; + } + if (i >= -1) worklist->at_put(i + 1, top); +} + +int GraphBuilder::ScopeData::caller_stack_size() const { + ValueStack* state = scope()->caller_state(); + if (state == NULL) { + return 0; + } + return state->stack_size(); +} + + +BlockBegin* GraphBuilder::ScopeData::remove_from_work_list() { + if (is_work_list_empty()) { + return NULL; + } + return _work_list->pop(); +} + + +bool GraphBuilder::ScopeData::is_work_list_empty() const { + return (_work_list == NULL || _work_list->length() == 0); +} + + +void GraphBuilder::ScopeData::setup_jsr_xhandlers() { + assert(parsing_jsr(), ""); + // clone all the exception handlers from the scope + XHandlers* handlers = new XHandlers(scope()->xhandlers()); + const int n = handlers->length(); + for (int i = 0; i < n; i++) { + // The XHandlers need to be adjusted to dispatch to the cloned + // handler block instead of the default one but the synthetic + // unlocker needs to be handled specially. The synthetic unlocker + // should be left alone since there can be only one and all code + // should dispatch to the same one. + XHandler* h = handlers->handler_at(i); + if (h->handler_bci() != SynchronizationEntryBCI) { + h->set_entry_block(block_at(h->handler_bci())); + } else { + assert(h->entry_block()->is_set(BlockBegin::default_exception_handler_flag), + "should be the synthetic unlock block"); + } + } + _jsr_xhandlers = handlers; +} + + +int GraphBuilder::ScopeData::num_returns() { + if (parsing_jsr()) { + return parent()->num_returns(); + } + return _num_returns; +} + + +void GraphBuilder::ScopeData::incr_num_returns() { + if (parsing_jsr()) { + parent()->incr_num_returns(); + } else { + ++_num_returns; + } +} + + +// Implementation of GraphBuilder + +#define INLINE_BAILOUT(msg) { inline_bailout(msg); return false; } + + +void GraphBuilder::load_constant() { + ciConstant con = stream()->get_constant(); + if (con.basic_type() == T_ILLEGAL) { + BAILOUT("could not resolve a constant"); + } else { + ValueType* t = illegalType; + ValueStack* patch_state = NULL; + switch (con.basic_type()) { + case T_BOOLEAN: t = new IntConstant (con.as_boolean()); break; + case T_BYTE : t = new IntConstant (con.as_byte ()); break; + case T_CHAR : t = new IntConstant (con.as_char ()); break; + case T_SHORT : t = new IntConstant (con.as_short ()); break; + case T_INT : t = new IntConstant (con.as_int ()); break; + case T_LONG : t = new LongConstant (con.as_long ()); break; + case T_FLOAT : t = new FloatConstant (con.as_float ()); break; + case T_DOUBLE : t = new DoubleConstant (con.as_double ()); break; + case T_ARRAY : t = new ArrayConstant (con.as_object ()->as_array ()); break; + case T_OBJECT : + { + ciObject* obj = con.as_object(); + if (obj->is_klass()) { + ciKlass* klass = obj->as_klass(); + if (!klass->is_loaded() || PatchALot) { + patch_state = state()->copy(); + t = new ObjectConstant(obj); + } else { + t = new InstanceConstant(klass->java_mirror()); + } + } else { + t = new InstanceConstant(obj->as_instance()); + } + break; + } + default : ShouldNotReachHere(); + } + Value x; + if (patch_state != NULL) { + x = new Constant(t, patch_state); + } else { + x = new Constant(t); + } + push(t, append(x)); + } +} + + +void GraphBuilder::load_local(ValueType* type, int index) { + Value x = state()->load_local(index); + push(type, x); +} + + +void GraphBuilder::store_local(ValueType* type, int index) { + Value x = pop(type); + store_local(state(), x, type, index); +} + + +void GraphBuilder::store_local(ValueStack* state, Value x, ValueType* type, int index) { + if (parsing_jsr()) { + // We need to do additional tracking of the location of the return + // address for jsrs since we don't handle arbitrary jsr/ret + // constructs. Here we are figuring out in which circumstances we + // need to bail out. + if (x->type()->is_address()) { + scope_data()->set_jsr_return_address_local(index); + + // Also check parent jsrs (if any) at this time to see whether + // they are using this local. We don't handle skipping over a + // ret. + for (ScopeData* cur_scope_data = scope_data()->parent(); + cur_scope_data != NULL && cur_scope_data->parsing_jsr() && cur_scope_data->scope() == scope(); + cur_scope_data = cur_scope_data->parent()) { + if (cur_scope_data->jsr_return_address_local() == index) { + BAILOUT("subroutine overwrites return address from previous subroutine"); + } + } + } else if (index == scope_data()->jsr_return_address_local()) { + scope_data()->set_jsr_return_address_local(-1); + } + } + + state->store_local(index, round_fp(x)); +} + + +void GraphBuilder::load_indexed(BasicType type) { + Value index = ipop(); + Value array = apop(); + Value length = NULL; + if (CSEArrayLength || + (array->as_AccessField() && array->as_AccessField()->field()->is_constant()) || + (array->as_NewArray() && array->as_NewArray()->length() && array->as_NewArray()->length()->type()->is_constant())) { + length = append(new ArrayLength(array, lock_stack())); + } + push(as_ValueType(type), append(new LoadIndexed(array, index, length, type, lock_stack()))); +} + + +void GraphBuilder::store_indexed(BasicType type) { + Value value = pop(as_ValueType(type)); + Value index = ipop(); + Value array = apop(); + Value length = NULL; + if (CSEArrayLength || + (array->as_AccessField() && array->as_AccessField()->field()->is_constant()) || + (array->as_NewArray() && array->as_NewArray()->length() && array->as_NewArray()->length()->type()->is_constant())) { + length = append(new ArrayLength(array, lock_stack())); + } + StoreIndexed* result = new StoreIndexed(array, index, length, type, value, lock_stack()); + kill_array(value); // invalidate all CSEs that are memory accesses of the same type + append(result); +} + + +void GraphBuilder::stack_op(Bytecodes::Code code) { + switch (code) { + case Bytecodes::_pop: + { state()->raw_pop(); + } + break; + case Bytecodes::_pop2: + { state()->raw_pop(); + state()->raw_pop(); + } + break; + case Bytecodes::_dup: + { Value w = state()->raw_pop(); + state()->raw_push(w); + state()->raw_push(w); + } + break; + case Bytecodes::_dup_x1: + { Value w1 = state()->raw_pop(); + Value w2 = state()->raw_pop(); + state()->raw_push(w1); + state()->raw_push(w2); + state()->raw_push(w1); + } + break; + case Bytecodes::_dup_x2: + { Value w1 = state()->raw_pop(); + Value w2 = state()->raw_pop(); + Value w3 = state()->raw_pop(); + state()->raw_push(w1); + state()->raw_push(w3); + state()->raw_push(w2); + state()->raw_push(w1); + } + break; + case Bytecodes::_dup2: + { Value w1 = state()->raw_pop(); + Value w2 = state()->raw_pop(); + state()->raw_push(w2); + state()->raw_push(w1); + state()->raw_push(w2); + state()->raw_push(w1); + } + break; + case Bytecodes::_dup2_x1: + { Value w1 = state()->raw_pop(); + Value w2 = state()->raw_pop(); + Value w3 = state()->raw_pop(); + state()->raw_push(w2); + state()->raw_push(w1); + state()->raw_push(w3); + state()->raw_push(w2); + state()->raw_push(w1); + } + break; + case Bytecodes::_dup2_x2: + { Value w1 = state()->raw_pop(); + Value w2 = state()->raw_pop(); + Value w3 = state()->raw_pop(); + Value w4 = state()->raw_pop(); + state()->raw_push(w2); + state()->raw_push(w1); + state()->raw_push(w4); + state()->raw_push(w3); + state()->raw_push(w2); + state()->raw_push(w1); + } + break; + case Bytecodes::_swap: + { Value w1 = state()->raw_pop(); + Value w2 = state()->raw_pop(); + state()->raw_push(w1); + state()->raw_push(w2); + } + break; + default: + ShouldNotReachHere(); + break; + } +} + + +void GraphBuilder::arithmetic_op(ValueType* type, Bytecodes::Code code, ValueStack* stack) { + Value y = pop(type); + Value x = pop(type); + // NOTE: strictfp can be queried from current method since we don't + // inline methods with differing strictfp bits + Value res = new ArithmeticOp(code, x, y, method()->is_strict(), stack); + // Note: currently single-precision floating-point rounding on Intel is handled at the LIRGenerator level + res = append(res); + if (method()->is_strict()) { + res = round_fp(res); + } + push(type, res); +} + + +void GraphBuilder::negate_op(ValueType* type) { + push(type, append(new NegateOp(pop(type)))); +} + + +void GraphBuilder::shift_op(ValueType* type, Bytecodes::Code code) { + Value s = ipop(); + Value x = pop(type); + // try to simplify + // Note: This code should go into the canonicalizer as soon as it can + // can handle canonicalized forms that contain more than one node. + if (CanonicalizeNodes && code == Bytecodes::_iushr) { + // pattern: x >>> s + IntConstant* s1 = s->type()->as_IntConstant(); + if (s1 != NULL) { + // pattern: x >>> s1, with s1 constant + ShiftOp* l = x->as_ShiftOp(); + if (l != NULL && l->op() == Bytecodes::_ishl) { + // pattern: (a << b) >>> s1 + IntConstant* s0 = l->y()->type()->as_IntConstant(); + if (s0 != NULL) { + // pattern: (a << s0) >>> s1 + const int s0c = s0->value() & 0x1F; // only the low 5 bits are significant for shifts + const int s1c = s1->value() & 0x1F; // only the low 5 bits are significant for shifts + if (s0c == s1c) { + if (s0c == 0) { + // pattern: (a << 0) >>> 0 => simplify to: a + ipush(l->x()); + } else { + // pattern: (a << s0c) >>> s0c => simplify to: a & m, with m constant + assert(0 < s0c && s0c < BitsPerInt, "adjust code below to handle corner cases"); + const int m = (1 << (BitsPerInt - s0c)) - 1; + Value s = append(new Constant(new IntConstant(m))); + ipush(append(new LogicOp(Bytecodes::_iand, l->x(), s))); + } + return; + } + } + } + } + } + // could not simplify + push(type, append(new ShiftOp(code, x, s))); +} + + +void GraphBuilder::logic_op(ValueType* type, Bytecodes::Code code) { + Value y = pop(type); + Value x = pop(type); + push(type, append(new LogicOp(code, x, y))); +} + + +void GraphBuilder::compare_op(ValueType* type, Bytecodes::Code code) { + ValueStack* state_before = state()->copy(); + Value y = pop(type); + Value x = pop(type); + ipush(append(new CompareOp(code, x, y, state_before))); +} + + +void GraphBuilder::convert(Bytecodes::Code op, BasicType from, BasicType to) { + push(as_ValueType(to), append(new Convert(op, pop(as_ValueType(from)), as_ValueType(to)))); +} + + +void GraphBuilder::increment() { + int index = stream()->get_index(); + int delta = stream()->is_wide() ? (signed short)Bytes::get_Java_u2(stream()->cur_bcp() + 4) : (signed char)(stream()->cur_bcp()[2]); + load_local(intType, index); + ipush(append(new Constant(new IntConstant(delta)))); + arithmetic_op(intType, Bytecodes::_iadd); + store_local(intType, index); +} + + +void GraphBuilder::_goto(int from_bci, int to_bci) { + profile_bci(from_bci); + append(new Goto(block_at(to_bci), to_bci <= from_bci)); +} + + +void GraphBuilder::if_node(Value x, If::Condition cond, Value y, ValueStack* state_before) { + BlockBegin* tsux = block_at(stream()->get_dest()); + BlockBegin* fsux = block_at(stream()->next_bci()); + bool is_bb = tsux->bci() < stream()->cur_bci() || fsux->bci() < stream()->cur_bci(); + If* if_node = append(new If(x, cond, false, y, tsux, fsux, is_bb ? state_before : NULL, is_bb))->as_If(); + if (profile_branches() && (if_node != NULL)) { + if_node->set_profiled_method(method()); + if_node->set_profiled_bci(bci()); + if_node->set_should_profile(true); + } +} + + +void GraphBuilder::if_zero(ValueType* type, If::Condition cond) { + Value y = append(new Constant(intZero)); + ValueStack* state_before = state()->copy(); + Value x = ipop(); + if_node(x, cond, y, state_before); +} + + +void GraphBuilder::if_null(ValueType* type, If::Condition cond) { + Value y = append(new Constant(objectNull)); + ValueStack* state_before = state()->copy(); + Value x = apop(); + if_node(x, cond, y, state_before); +} + + +void GraphBuilder::if_same(ValueType* type, If::Condition cond) { + ValueStack* state_before = state()->copy(); + Value y = pop(type); + Value x = pop(type); + if_node(x, cond, y, state_before); +} + + +void GraphBuilder::jsr(int dest) { + // We only handle well-formed jsrs (those which are "block-structured"). + // If the bytecodes are strange (jumping out of a jsr block) then we + // might end up trying to re-parse a block containing a jsr which + // has already been activated. Watch for this case and bail out. + for (ScopeData* cur_scope_data = scope_data(); + cur_scope_data != NULL && cur_scope_data->parsing_jsr() && cur_scope_data->scope() == scope(); + cur_scope_data = cur_scope_data->parent()) { + if (cur_scope_data->jsr_entry_bci() == dest) { + BAILOUT("too-complicated jsr/ret structure"); + } + } + + push(addressType, append(new Constant(new AddressConstant(next_bci())))); + if (!try_inline_jsr(dest)) { + return; // bailed out while parsing and inlining subroutine + } +} + + +void GraphBuilder::ret(int local_index) { + if (!parsing_jsr()) BAILOUT("ret encountered while not parsing subroutine"); + + if (local_index != scope_data()->jsr_return_address_local()) { + BAILOUT("can not handle complicated jsr/ret constructs"); + } + + // Rets simply become (NON-SAFEPOINT) gotos to the jsr continuation + append(new Goto(scope_data()->jsr_continuation(), false)); +} + + +void GraphBuilder::table_switch() { + Bytecode_tableswitch* switch_ = Bytecode_tableswitch_at(method()->code() + bci()); + const int l = switch_->length(); + if (CanonicalizeNodes && l == 1) { + // total of 2 successors => use If instead of switch + // Note: This code should go into the canonicalizer as soon as it can + // can handle canonicalized forms that contain more than one node. + Value key = append(new Constant(new IntConstant(switch_->low_key()))); + BlockBegin* tsux = block_at(bci() + switch_->dest_offset_at(0)); + BlockBegin* fsux = block_at(bci() + switch_->default_offset()); + bool is_bb = tsux->bci() < bci() || fsux->bci() < bci(); + ValueStack* state_before = is_bb ? state() : NULL; + append(new If(ipop(), If::eql, true, key, tsux, fsux, state_before, is_bb)); + } else { + // collect successors + BlockList* sux = new BlockList(l + 1, NULL); + int i; + bool has_bb = false; + for (i = 0; i < l; i++) { + sux->at_put(i, block_at(bci() + switch_->dest_offset_at(i))); + if (switch_->dest_offset_at(i) < 0) has_bb = true; + } + // add default successor + sux->at_put(i, block_at(bci() + switch_->default_offset())); + ValueStack* state_before = has_bb ? state() : NULL; + append(new TableSwitch(ipop(), sux, switch_->low_key(), state_before, has_bb)); + } +} + + +void GraphBuilder::lookup_switch() { + Bytecode_lookupswitch* switch_ = Bytecode_lookupswitch_at(method()->code() + bci()); + const int l = switch_->number_of_pairs(); + if (CanonicalizeNodes && l == 1) { + // total of 2 successors => use If instead of switch + // Note: This code should go into the canonicalizer as soon as it can + // can handle canonicalized forms that contain more than one node. + // simplify to If + LookupswitchPair* pair = switch_->pair_at(0); + Value key = append(new Constant(new IntConstant(pair->match()))); + BlockBegin* tsux = block_at(bci() + pair->offset()); + BlockBegin* fsux = block_at(bci() + switch_->default_offset()); + bool is_bb = tsux->bci() < bci() || fsux->bci() < bci(); + ValueStack* state_before = is_bb ? state() : NULL; + append(new If(ipop(), If::eql, true, key, tsux, fsux, state_before, is_bb)); + } else { + // collect successors & keys + BlockList* sux = new BlockList(l + 1, NULL); + intArray* keys = new intArray(l, 0); + int i; + bool has_bb = false; + for (i = 0; i < l; i++) { + LookupswitchPair* pair = switch_->pair_at(i); + if (pair->offset() < 0) has_bb = true; + sux->at_put(i, block_at(bci() + pair->offset())); + keys->at_put(i, pair->match()); + } + // add default successor + sux->at_put(i, block_at(bci() + switch_->default_offset())); + ValueStack* state_before = has_bb ? state() : NULL; + append(new LookupSwitch(ipop(), sux, keys, state_before, has_bb)); + } +} + +void GraphBuilder::call_register_finalizer() { + // If the receiver requires finalization then emit code to perform + // the registration on return. + + // Gather some type information about the receiver + Value receiver = state()->load_local(0); + assert(receiver != NULL, "must have a receiver"); + ciType* declared_type = receiver->declared_type(); + ciType* exact_type = receiver->exact_type(); + if (exact_type == NULL && + receiver->as_Local() && + receiver->as_Local()->java_index() == 0) { + ciInstanceKlass* ik = compilation()->method()->holder(); + if (ik->is_final()) { + exact_type = ik; + } else if (UseCHA && !(ik->has_subklass() || ik->is_interface())) { + // test class is leaf class + compilation()->dependency_recorder()->assert_leaf_type(ik); + exact_type = ik; + } else { + declared_type = ik; + } + } + + // see if we know statically that registration isn't required + bool needs_check = true; + if (exact_type != NULL) { + needs_check = exact_type->as_instance_klass()->has_finalizer(); + } else if (declared_type != NULL) { + ciInstanceKlass* ik = declared_type->as_instance_klass(); + if (!Dependencies::has_finalizable_subclass(ik)) { + compilation()->dependency_recorder()->assert_has_no_finalizable_subclasses(ik); + needs_check = false; + } + } + + if (needs_check) { + // Perform the registration of finalizable objects. + load_local(objectType, 0); + append_split(new Intrinsic(voidType, vmIntrinsics::_Object_init, + state()->pop_arguments(1), + true, lock_stack(), true)); + } +} + + +void GraphBuilder::method_return(Value x) { + if (RegisterFinalizersAtInit && + method()->intrinsic_id() == vmIntrinsics::_Object_init) { + call_register_finalizer(); + } + + // Check to see whether we are inlining. If so, Return + // instructions become Gotos to the continuation point. + if (continuation() != NULL) { + assert(!method()->is_synchronized() || InlineSynchronizedMethods, "can not inline synchronized methods yet"); + + // If the inlined method is synchronized, the monitor must be + // released before we jump to the continuation block. + if (method()->is_synchronized()) { + int i = state()->caller_state()->locks_size(); + assert(state()->locks_size() == i + 1, "receiver must be locked here"); + monitorexit(state()->lock_at(i), SynchronizationEntryBCI); + } + + state()->truncate_stack(caller_stack_size()); + if (x != NULL) { + state()->push(x->type(), x); + } + Goto* goto_callee = new Goto(continuation(), false); + + // See whether this is the first return; if so, store off some + // of the state for later examination + if (num_returns() == 0) { + set_inline_cleanup_info(_block, _last, state()); + } + + // State at end of inlined method is the state of the caller + // without the method parameters on stack, including the + // return value, if any, of the inlined method on operand stack. + set_state(scope_data()->continuation_state()->copy()); + if (x) { + state()->push(x->type(), x); + } + + // The current bci() is in the wrong scope, so use the bci() of + // the continuation point. + append_with_bci(goto_callee, scope_data()->continuation()->bci()); + incr_num_returns(); + + return; + } + + state()->truncate_stack(0); + if (method()->is_synchronized()) { + // perform the unlocking before exiting the method + Value receiver; + if (!method()->is_static()) { + receiver = _initial_state->local_at(0); + } else { + receiver = append(new Constant(new ClassConstant(method()->holder()))); + } + append_split(new MonitorExit(receiver, state()->unlock())); + } + + append(new Return(x)); +} + + +void GraphBuilder::access_field(Bytecodes::Code code) { + bool will_link; + ciField* field = stream()->get_field(will_link); + ciInstanceKlass* holder = field->holder(); + BasicType field_type = field->type()->basic_type(); + ValueType* type = as_ValueType(field_type); + // call will_link again to determine if the field is valid. + const bool is_loaded = holder->is_loaded() && + field->will_link(method()->holder(), code); + const bool is_initialized = is_loaded && holder->is_initialized(); + + ValueStack* state_copy = NULL; + if (!is_initialized || PatchALot) { + // save state before instruction for debug info when + // deoptimization happens during patching + state_copy = state()->copy(); + } + + Value obj = NULL; + if (code == Bytecodes::_getstatic || code == Bytecodes::_putstatic) { + // commoning of class constants should only occur if the class is + // fully initialized and resolved in this constant pool. The will_link test + // above essentially checks if this class is resolved in this constant pool + // so, the is_initialized flag should be suffiect. + if (state_copy != NULL) { + // build a patching constant + obj = new Constant(new ClassConstant(holder), state_copy); + } else { + obj = new Constant(new ClassConstant(holder)); + } + } + + + const int offset = is_loaded ? field->offset() : -1; + switch (code) { + case Bytecodes::_getstatic: { + // check for compile-time constants, i.e., initialized static final fields + Instruction* constant = NULL; + if (field->is_constant() && !PatchALot) { + ciConstant field_val = field->constant_value(); + BasicType field_type = field_val.basic_type(); + switch (field_type) { + case T_ARRAY: + case T_OBJECT: + if (field_val.as_object()->has_encoding()) { + constant = new Constant(as_ValueType(field_val)); + } + break; + + default: + constant = new Constant(as_ValueType(field_val)); + } + } + if (constant != NULL) { + push(type, append(constant)); + state_copy = NULL; // Not a potential deoptimization point (see set_state_before logic below) + } else { + push(type, append(new LoadField(append(obj), offset, field, true, + lock_stack(), state_copy, is_loaded, is_initialized))); + } + break; + } + case Bytecodes::_putstatic: + { Value val = pop(type); + append(new StoreField(append(obj), offset, field, val, true, lock_stack(), state_copy, is_loaded, is_initialized)); + if (UseLocalValueNumbering) { + vmap()->kill_field(field); // invalidate all CSEs that are memory accesses + } + } + break; + case Bytecodes::_getfield : + { + LoadField* load = new LoadField(apop(), offset, field, false, lock_stack(), state_copy, is_loaded, true); + Value replacement = is_loaded ? _memory->load(load) : load; + if (replacement != load) { + assert(replacement->bci() != -99 || replacement->as_Phi() || replacement->as_Local(), + "should already by linked"); + push(type, replacement); + } else { + push(type, append(load)); + } + break; + } + + case Bytecodes::_putfield : + { Value val = pop(type); + StoreField* store = new StoreField(apop(), offset, field, val, false, lock_stack(), state_copy, is_loaded, true); + if (is_loaded) store = _memory->store(store); + if (store != NULL) { + append(store); + kill_field(field); // invalidate all CSEs that are accesses of this field + } + } + break; + default : + ShouldNotReachHere(); + break; + } +} + + +Dependencies* GraphBuilder::dependency_recorder() const { + assert(DeoptC1, "need debug information"); + compilation()->set_needs_debug_information(true); + return compilation()->dependency_recorder(); +} + + +void GraphBuilder::invoke(Bytecodes::Code code) { + bool will_link; + ciMethod* target = stream()->get_method(will_link); + // we have to make sure the argument size (incl. the receiver) + // is correct for compilation (the call would fail later during + // linkage anyway) - was bug (gri 7/28/99) + if (target->is_loaded() && target->is_static() != (code == Bytecodes::_invokestatic)) BAILOUT("will cause link error"); + ciInstanceKlass* klass = target->holder(); + + // check if CHA possible: if so, change the code to invoke_special + ciInstanceKlass* calling_klass = method()->holder(); + ciKlass* holder = stream()->get_declared_method_holder(); + ciInstanceKlass* callee_holder = ciEnv::get_instance_klass_for_declared_method_holder(holder); + ciInstanceKlass* actual_recv = callee_holder; + + // some methods are obviously bindable without any type checks so + // convert them directly to an invokespecial. + if (target->is_loaded() && !target->is_abstract() && + target->can_be_statically_bound() && code == Bytecodes::_invokevirtual) { + code = Bytecodes::_invokespecial; + } + + // NEEDS_CLEANUP + // I've added the target-is_loaded() test below but I don't really understand + // how klass->is_loaded() can be true and yet target->is_loaded() is false. + // this happened while running the JCK invokevirtual tests under doit. TKR + ciMethod* cha_monomorphic_target = NULL; + ciMethod* exact_target = NULL; + if (UseCHA && DeoptC1 && klass->is_loaded() && target->is_loaded()) { + Value receiver = NULL; + ciInstanceKlass* receiver_klass = NULL; + bool type_is_exact = false; + // try to find a precise receiver type + if (will_link && !target->is_static()) { + int index = state()->stack_size() - (target->arg_size_no_receiver() + 1); + receiver = state()->stack_at(index); + ciType* type = receiver->exact_type(); + if (type != NULL && type->is_loaded() && + type->is_instance_klass() && !type->as_instance_klass()->is_interface()) { + receiver_klass = (ciInstanceKlass*) type; + type_is_exact = true; + } + if (type == NULL) { + type = receiver->declared_type(); + if (type != NULL && type->is_loaded() && + type->is_instance_klass() && !type->as_instance_klass()->is_interface()) { + receiver_klass = (ciInstanceKlass*) type; + if (receiver_klass->is_leaf_type() && !receiver_klass->is_final()) { + // Insert a dependency on this type since + // find_monomorphic_target may assume it's already done. + dependency_recorder()->assert_leaf_type(receiver_klass); + type_is_exact = true; + } + } + } + } + if (receiver_klass != NULL && type_is_exact && + receiver_klass->is_loaded() && code != Bytecodes::_invokespecial) { + // If we have the exact receiver type we can bind directly to + // the method to call. + exact_target = target->resolve_invoke(calling_klass, receiver_klass); + if (exact_target != NULL) { + target = exact_target; + code = Bytecodes::_invokespecial; + } + } + if (receiver_klass != NULL && + receiver_klass->is_subtype_of(actual_recv) && + actual_recv->is_initialized()) { + actual_recv = receiver_klass; + } + + if ((code == Bytecodes::_invokevirtual && callee_holder->is_initialized()) || + (code == Bytecodes::_invokeinterface && callee_holder->is_initialized() && !actual_recv->is_interface())) { + // Use CHA on the receiver to select a more precise method. + cha_monomorphic_target = target->find_monomorphic_target(calling_klass, callee_holder, actual_recv); + } else if (code == Bytecodes::_invokeinterface && callee_holder->is_loaded() && receiver != NULL) { + // if there is only one implementor of this interface then we + // may be able bind this invoke directly to the implementing + // klass but we need both a dependence on the single interface + // and on the method we bind to. Additionally since all we know + // about the receiver type is the it's supposed to implement the + // interface we have to insert a check that it's the class we + // expect. Interface types are not checked by the verifier so + // they are roughly equivalent to Object. + ciInstanceKlass* singleton = NULL; + if (target->holder()->nof_implementors() == 1) { + singleton = target->holder()->implementor(0); + } + if (singleton) { + cha_monomorphic_target = target->find_monomorphic_target(calling_klass, target->holder(), singleton); + if (cha_monomorphic_target != NULL) { + // If CHA is able to bind this invoke then update the class + // to match that class, otherwise klass will refer to the + // interface. + klass = cha_monomorphic_target->holder(); + actual_recv = target->holder(); + + // insert a check it's really the expected class. + CheckCast* c = new CheckCast(klass, receiver, NULL); + c->set_incompatible_class_change_check(); + c->set_direct_compare(klass->is_final()); + append_split(c); + } + } + } + } + + if (cha_monomorphic_target != NULL) { + if (cha_monomorphic_target->is_abstract()) { + // Do not optimize for abstract methods + cha_monomorphic_target = NULL; + } + } + + if (cha_monomorphic_target != NULL) { + if (!(target->is_final_method())) { + // If we inlined because CHA revealed only a single target method, + // then we are dependent on that target method not getting overridden + // by dynamic class loading. Be sure to test the "static" receiver + // dest_method here, as opposed to the actual receiver, which may + // falsely lead us to believe that the receiver is final or private. + dependency_recorder()->assert_unique_concrete_method(actual_recv, cha_monomorphic_target); + } + code = Bytecodes::_invokespecial; + } + // check if we could do inlining + if (!PatchALot && Inline && klass->is_loaded() && + (klass->is_initialized() || klass->is_interface() && target->holder()->is_initialized()) + && target->will_link(klass, callee_holder, code)) { + // callee is known => check if we have static binding + assert(target->is_loaded(), "callee must be known"); + if (code == Bytecodes::_invokestatic + || code == Bytecodes::_invokespecial + || code == Bytecodes::_invokevirtual && target->is_final_method() + ) { + // static binding => check if callee is ok + ciMethod* inline_target = (cha_monomorphic_target != NULL) + ? cha_monomorphic_target + : target; + bool res = try_inline(inline_target, (cha_monomorphic_target != NULL) || (exact_target != NULL)); + CHECK_BAILOUT(); + +#ifndef PRODUCT + // printing + if (PrintInlining && !res) { + // if it was successfully inlined, then it was already printed. + print_inline_result(inline_target, res); + } +#endif + clear_inline_bailout(); + if (res) { + // Register dependence if JVMTI has either breakpoint + // setting or hotswapping of methods capabilities since they may + // cause deoptimization. + if (JvmtiExport::can_hotswap_or_post_breakpoint()) { + dependency_recorder()->assert_evol_method(inline_target); + } + return; + } + } + } + // If we attempted an inline which did not succeed because of a + // bailout during construction of the callee graph, the entire + // compilation has to be aborted. This is fairly rare and currently + // seems to only occur for jasm-generated classes which contain + // jsr/ret pairs which are not associated with finally clauses and + // do not have exception handlers in the containing method, and are + // therefore not caught early enough to abort the inlining without + // corrupting the graph. (We currently bail out with a non-empty + // stack at a ret in these situations.) + CHECK_BAILOUT(); + + // inlining not successful => standard invoke + bool is_static = code == Bytecodes::_invokestatic; + ValueType* result_type = as_ValueType(target->return_type()); + Values* args = state()->pop_arguments(target->arg_size_no_receiver()); + Value recv = is_static ? NULL : apop(); + bool is_loaded = target->is_loaded(); + int vtable_index = methodOopDesc::invalid_vtable_index; + +#ifdef SPARC + // Currently only supported on Sparc. + // The UseInlineCaches only controls dispatch to invokevirtuals for + // loaded classes which we weren't able to statically bind. + if (!UseInlineCaches && is_loaded && code == Bytecodes::_invokevirtual + && !target->can_be_statically_bound()) { + // Find a vtable index if one is available + vtable_index = target->resolve_vtable_index(calling_klass, callee_holder); + } +#endif + + if (recv != NULL && + (code == Bytecodes::_invokespecial || + !is_loaded || target->is_final() || + profile_calls())) { + // invokespecial always needs a NULL check. invokevirtual where + // the target is final or where it's not known that whether the + // target is final requires a NULL check. Otherwise normal + // invokevirtual will perform the null check during the lookup + // logic or the unverified entry point. Profiling of calls + // requires that the null check is performed in all cases. + null_check(recv); + } + + if (profile_calls()) { + assert(cha_monomorphic_target == NULL || exact_target == NULL, "both can not be set"); + ciKlass* target_klass = NULL; + if (cha_monomorphic_target != NULL) { + target_klass = cha_monomorphic_target->holder(); + } else if (exact_target != NULL) { + target_klass = exact_target->holder(); + } + profile_call(recv, target_klass); + } + + Invoke* result = new Invoke(code, result_type, recv, args, vtable_index, target); + // push result + append_split(result); + + if (result_type != voidType) { + if (method()->is_strict()) { + push(result_type, round_fp(result)); + } else { + push(result_type, result); + } + } +} + + +void GraphBuilder::new_instance(int klass_index) { + bool will_link; + ciKlass* klass = stream()->get_klass(will_link); + assert(klass->is_instance_klass(), "must be an instance klass"); + NewInstance* new_instance = new NewInstance(klass->as_instance_klass()); + _memory->new_instance(new_instance); + apush(append_split(new_instance)); +} + + +void GraphBuilder::new_type_array() { + apush(append_split(new NewTypeArray(ipop(), (BasicType)stream()->get_index()))); +} + + +void GraphBuilder::new_object_array() { + bool will_link; + ciKlass* klass = stream()->get_klass(will_link); + ValueStack* state_before = !klass->is_loaded() || PatchALot ? state()->copy() : NULL; + NewArray* n = new NewObjectArray(klass, ipop(), state_before); + apush(append_split(n)); +} + + +bool GraphBuilder::direct_compare(ciKlass* k) { + if (k->is_loaded() && k->is_instance_klass() && !UseSlowPath) { + ciInstanceKlass* ik = k->as_instance_klass(); + if (ik->is_final()) { + return true; + } else { + if (DeoptC1 && UseCHA && !(ik->has_subklass() || ik->is_interface())) { + // test class is leaf class + dependency_recorder()->assert_leaf_type(ik); + return true; + } + } + } + return false; +} + + +void GraphBuilder::check_cast(int klass_index) { + bool will_link; + ciKlass* klass = stream()->get_klass(will_link); + ValueStack* state_before = !klass->is_loaded() || PatchALot ? state()->copy() : NULL; + CheckCast* c = new CheckCast(klass, apop(), state_before); + apush(append_split(c)); + c->set_direct_compare(direct_compare(klass)); + if (profile_checkcasts()) { + c->set_profiled_method(method()); + c->set_profiled_bci(bci()); + c->set_should_profile(true); + } +} + + +void GraphBuilder::instance_of(int klass_index) { + bool will_link; + ciKlass* klass = stream()->get_klass(will_link); + ValueStack* state_before = !klass->is_loaded() || PatchALot ? state()->copy() : NULL; + InstanceOf* i = new InstanceOf(klass, apop(), state_before); + ipush(append_split(i)); + i->set_direct_compare(direct_compare(klass)); +} + + +void GraphBuilder::monitorenter(Value x, int bci) { + // save state before locking in case of deoptimization after a NullPointerException + ValueStack* lock_stack_before = lock_stack(); + append_with_bci(new MonitorEnter(x, state()->lock(scope(), x), lock_stack_before), bci); + kill_all(); +} + + +void GraphBuilder::monitorexit(Value x, int bci) { + // Note: the comment below is only relevant for the case where we do + // not deoptimize due to asynchronous exceptions (!(DeoptC1 && + // DeoptOnAsyncException), which is not used anymore) + + // Note: Potentially, the monitor state in an exception handler + // can be wrong due to wrong 'initialization' of the handler + // via a wrong asynchronous exception path. This can happen, + // if the exception handler range for asynchronous exceptions + // is too long (see also java bug 4327029, and comment in + // GraphBuilder::handle_exception()). This may cause 'under- + // flow' of the monitor stack => bailout instead. + if (state()->locks_size() < 1) BAILOUT("monitor stack underflow"); + append_with_bci(new MonitorExit(x, state()->unlock()), bci); + kill_all(); +} + + +void GraphBuilder::new_multi_array(int dimensions) { + bool will_link; + ciKlass* klass = stream()->get_klass(will_link); + ValueStack* state_before = !klass->is_loaded() || PatchALot ? state()->copy() : NULL; + + Values* dims = new Values(dimensions, NULL); + // fill in all dimensions + int i = dimensions; + while (i-- > 0) dims->at_put(i, ipop()); + // create array + NewArray* n = new NewMultiArray(klass, dims, state_before); + apush(append_split(n)); +} + + +void GraphBuilder::throw_op(int bci) { + // We require that the debug info for a Throw be the "state before" + // the Throw (i.e., exception oop is still on TOS) + ValueStack* state_before = state()->copy(); + Throw* t = new Throw(apop(), state_before); + append_with_bci(t, bci); +} + + +Value GraphBuilder::round_fp(Value fp_value) { + // no rounding needed if SSE2 is used + if (RoundFPResults && UseSSE < 2) { + // Must currently insert rounding node for doubleword values that + // are results of expressions (i.e., not loads from memory or + // constants) + if (fp_value->type()->tag() == doubleTag && + fp_value->as_Constant() == NULL && + fp_value->as_Local() == NULL && // method parameters need no rounding + fp_value->as_RoundFP() == NULL) { + return append(new RoundFP(fp_value)); + } + } + return fp_value; +} + + +Instruction* GraphBuilder::append_with_bci(Instruction* instr, int bci) { + Canonicalizer canon(instr, bci); + Instruction* i1 = canon.canonical(); + if (i1->bci() != -99) { + // Canonicalizer returned an instruction which was already + // appended so simply return it. + return i1; + } else if (UseLocalValueNumbering) { + // Lookup the instruction in the ValueMap and add it to the map if + // it's not found. + Instruction* i2 = vmap()->find_insert(i1); + if (i2 != i1) { + // found an entry in the value map, so just return it. + assert(i2->bci() != -1, "should already be linked"); + return i2; + } + } + + if (i1->as_Phi() == NULL && i1->as_Local() == NULL) { + // i1 was not eliminated => append it + assert(i1->next() == NULL, "shouldn't already be linked"); + _last = _last->set_next(i1, canon.bci()); + if (++_instruction_count >= InstructionCountCutoff + && !bailed_out()) { + // set the bailout state but complete normal processing. We + // might do a little more work before noticing the bailout so we + // want processing to continue normally until it's noticed. + bailout("Method and/or inlining is too large"); + } + +#ifndef PRODUCT + if (PrintIRDuringConstruction) { + InstructionPrinter ip; + ip.print_line(i1); + if (Verbose) { + state()->print(); + } + } +#endif + assert(_last == i1, "adjust code below"); + StateSplit* s = i1->as_StateSplit(); + if (s != NULL && i1->as_BlockEnd() == NULL) { + // Continue CSE across certain intrinsics + Intrinsic* intrinsic = s->as_Intrinsic(); + if (UseLocalValueNumbering) { + if (intrinsic == NULL || !intrinsic->preserves_state()) { + vmap()->kill_all(); // for now, hopefully we need this only for calls eventually + } + } + if (EliminateFieldAccess) { + if (s->as_Invoke() != NULL || (intrinsic && !intrinsic->preserves_state())) { + _memory->kill(); + } + } + s->set_state(state()->copy()); + } + // set up exception handlers for this instruction if necessary + if (i1->can_trap()) { + assert(exception_state() != NULL || !has_handler(), "must have setup exception state"); + i1->set_exception_handlers(handle_exception(bci)); + } + } + return i1; +} + + +Instruction* GraphBuilder::append(Instruction* instr) { + assert(instr->as_StateSplit() == NULL || instr->as_BlockEnd() != NULL, "wrong append used"); + return append_with_bci(instr, bci()); +} + + +Instruction* GraphBuilder::append_split(StateSplit* instr) { + return append_with_bci(instr, bci()); +} + + +void GraphBuilder::null_check(Value value) { + if (value->as_NewArray() != NULL || value->as_NewInstance() != NULL) { + return; + } else { + Constant* con = value->as_Constant(); + if (con) { + ObjectType* c = con->type()->as_ObjectType(); + if (c && c->is_loaded()) { + ObjectConstant* oc = c->as_ObjectConstant(); + if (!oc || !oc->value()->is_null_object()) { + return; + } + } + } + } + append(new NullCheck(value, lock_stack())); +} + + + +XHandlers* GraphBuilder::handle_exception(int cur_bci) { + // fast path if it is guaranteed that no exception handlers are present + if (!has_handler()) { + // TODO: check if return NULL is possible (avoids empty lists) + return new XHandlers(); + } + + XHandlers* exception_handlers = new XHandlers(); + ScopeData* cur_scope_data = scope_data(); + ValueStack* s = exception_state(); + int scope_count = 0; + + assert(s != NULL, "exception state must be set"); + do { + assert(cur_scope_data->scope() == s->scope(), "scopes do not match"); + assert(cur_bci == SynchronizationEntryBCI || cur_bci == cur_scope_data->stream()->cur_bci(), "invalid bci"); + + // join with all potential exception handlers + XHandlers* list = cur_scope_data->xhandlers(); + const int n = list->length(); + for (int i = 0; i < n; i++) { + XHandler* h = list->handler_at(i); + if (h->covers(cur_bci)) { + // h is a potential exception handler => join it + compilation()->set_has_exception_handlers(true); + + BlockBegin* entry = h->entry_block(); + if (entry == block()) { + // It's acceptable for an exception handler to cover itself + // but we don't handle that in the parser currently. It's + // very rare so we bailout instead of trying to handle it. + BAILOUT_("exception handler covers itself", exception_handlers); + } + assert(entry->bci() == h->handler_bci(), "must match"); + assert(entry->bci() == -1 || entry == cur_scope_data->block_at(entry->bci()), "blocks must correspond"); + + // previously this was a BAILOUT, but this is not necessary + // now because asynchronous exceptions are not handled this way. + assert(entry->state() == NULL || s->locks_size() == entry->state()->locks_size(), "locks do not match"); + + // xhandler start with an empty expression stack + s->truncate_stack(cur_scope_data->caller_stack_size()); + + // Note: Usually this join must work. However, very + // complicated jsr-ret structures where we don't ret from + // the subroutine can cause the objects on the monitor + // stacks to not match because blocks can be parsed twice. + // The only test case we've seen so far which exhibits this + // problem is caught by the infinite recursion test in + // GraphBuilder::jsr() if the join doesn't work. + if (!entry->try_merge(s)) { + BAILOUT_("error while joining with exception handler, prob. due to complicated jsr/rets", exception_handlers); + } + + // add current state for correct handling of phi functions at begin of xhandler + int phi_operand = entry->add_exception_state(s); + + // add entry to the list of xhandlers of this block + _block->add_exception_handler(entry); + + // add back-edge from xhandler entry to this block + if (!entry->is_predecessor(_block)) { + entry->add_predecessor(_block); + } + + // clone XHandler because phi_operand and scope_count can not be shared + XHandler* new_xhandler = new XHandler(h); + new_xhandler->set_phi_operand(phi_operand); + new_xhandler->set_scope_count(scope_count); + exception_handlers->append(new_xhandler); + + // fill in exception handler subgraph lazily + assert(!entry->is_set(BlockBegin::was_visited_flag), "entry must not be visited yet"); + cur_scope_data->add_to_work_list(entry); + + // stop when reaching catchall + if (h->catch_type() == 0) { + return exception_handlers; + } + } + } + + // Set up iteration for next time. + // If parsing a jsr, do not grab exception handlers from the + // parent scopes for this method (already got them, and they + // needed to be cloned) + if (cur_scope_data->parsing_jsr()) { + IRScope* tmp_scope = cur_scope_data->scope(); + while (cur_scope_data->parent() != NULL && + cur_scope_data->parent()->scope() == tmp_scope) { + cur_scope_data = cur_scope_data->parent(); + } + } + if (cur_scope_data != NULL) { + if (cur_scope_data->parent() != NULL) { + // must use pop_scope instead of caller_state to preserve all monitors + s = s->pop_scope(); + } + cur_bci = cur_scope_data->scope()->caller_bci(); + cur_scope_data = cur_scope_data->parent(); + scope_count++; + } + } while (cur_scope_data != NULL); + + return exception_handlers; +} + + +// Helper class for simplifying Phis. +class PhiSimplifier : public BlockClosure { + private: + bool _has_substitutions; + Value simplify(Value v); + + public: + PhiSimplifier(BlockBegin* start) : _has_substitutions(false) { + start->iterate_preorder(this); + if (_has_substitutions) { + SubstitutionResolver sr(start); + } + } + void block_do(BlockBegin* b); + bool has_substitutions() const { return _has_substitutions; } +}; + + +Value PhiSimplifier::simplify(Value v) { + Phi* phi = v->as_Phi(); + + if (phi == NULL) { + // no phi function + return v; + } else if (v->has_subst()) { + // already substituted; subst can be phi itself -> simplify + return simplify(v->subst()); + } else if (phi->is_set(Phi::cannot_simplify)) { + // already tried to simplify phi before + return phi; + } else if (phi->is_set(Phi::visited)) { + // break cycles in phi functions + return phi; + } else if (phi->type()->is_illegal()) { + // illegal phi functions are ignored anyway + return phi; + + } else { + // mark phi function as processed to break cycles in phi functions + phi->set(Phi::visited); + + // simplify x = [y, x] and x = [y, y] to y + Value subst = NULL; + int opd_count = phi->operand_count(); + for (int i = 0; i < opd_count; i++) { + Value opd = phi->operand_at(i); + assert(opd != NULL, "Operand must exist!"); + + if (opd->type()->is_illegal()) { + // if one operand is illegal, the entire phi function is illegal + phi->make_illegal(); + phi->clear(Phi::visited); + return phi; + } + + Value new_opd = simplify(opd); + assert(new_opd != NULL, "Simplified operand must exist!"); + + if (new_opd != phi && new_opd != subst) { + if (subst == NULL) { + subst = new_opd; + } else { + // no simplification possible + phi->set(Phi::cannot_simplify); + phi->clear(Phi::visited); + return phi; + } + } + } + + // sucessfully simplified phi function + assert(subst != NULL, "illegal phi function"); + _has_substitutions = true; + phi->clear(Phi::visited); + phi->set_subst(subst); + +#ifndef PRODUCT + if (PrintPhiFunctions) { + tty->print_cr("simplified phi function %c%d to %c%d (Block B%d)", phi->type()->tchar(), phi->id(), subst->type()->tchar(), subst->id(), phi->block()->block_id()); + } +#endif + + return subst; + } +} + + +void PhiSimplifier::block_do(BlockBegin* b) { + for_each_phi_fun(b, phi, + simplify(phi); + ); + +#ifdef ASSERT + for_each_phi_fun(b, phi, + assert(phi->operand_count() != 1 || phi->subst() != phi, "missed trivial simplification"); + ); + + ValueStack* state = b->state()->caller_state(); + int index; + Value value; + for_each_state(state) { + for_each_local_value(state, index, value) { + Phi* phi = value->as_Phi(); + assert(phi == NULL || phi->block() != b, "must not have phi function to simplify in caller state"); + } + } +#endif +} + +// This method is called after all blocks are filled with HIR instructions +// It eliminates all Phi functions of the form x = [y, y] and x = [y, x] +void GraphBuilder::eliminate_redundant_phis(BlockBegin* start) { + PhiSimplifier simplifier(start); +} + + +void GraphBuilder::connect_to_end(BlockBegin* beg) { + // setup iteration + kill_all(); + _block = beg; + _state = beg->state()->copy(); + _last = beg; + iterate_bytecodes_for_block(beg->bci()); +} + + +BlockEnd* GraphBuilder::iterate_bytecodes_for_block(int bci) { +#ifndef PRODUCT + if (PrintIRDuringConstruction) { + tty->cr(); + InstructionPrinter ip; + ip.print_instr(_block); tty->cr(); + ip.print_stack(_block->state()); tty->cr(); + ip.print_inline_level(_block); + ip.print_head(); + tty->print_cr("locals size: %d stack size: %d", state()->locals_size(), state()->stack_size()); + } +#endif + _skip_block = false; + assert(state() != NULL, "ValueStack missing!"); + ciBytecodeStream s(method()); + s.reset_to_bci(bci); + int prev_bci = bci; + scope_data()->set_stream(&s); + // iterate + Bytecodes::Code code = Bytecodes::_illegal; + bool push_exception = false; + + if (block()->is_set(BlockBegin::exception_entry_flag) && block()->next() == NULL) { + // first thing in the exception entry block should be the exception object. + push_exception = true; + } + + while (!bailed_out() && last()->as_BlockEnd() == NULL && + (code = stream()->next()) != ciBytecodeStream::EOBC() && + (block_at(s.cur_bci()) == NULL || block_at(s.cur_bci()) == block())) { + + if (has_handler() && can_trap(method(), code)) { + // copy the state because it is modified before handle_exception is called + set_exception_state(state()->copy()); + } else { + // handle_exception is not called for this bytecode + set_exception_state(NULL); + } + + // Check for active jsr during OSR compilation + if (compilation()->is_osr_compile() + && scope()->is_top_scope() + && parsing_jsr() + && s.cur_bci() == compilation()->osr_bci()) { + bailout("OSR not supported while a jsr is active"); + } + + if (push_exception) { + apush(append(new ExceptionObject())); + push_exception = false; + } + + // handle bytecode + switch (code) { + case Bytecodes::_nop : /* nothing to do */ break; + case Bytecodes::_aconst_null : apush(append(new Constant(objectNull ))); break; + case Bytecodes::_iconst_m1 : ipush(append(new Constant(new IntConstant (-1)))); break; + case Bytecodes::_iconst_0 : ipush(append(new Constant(intZero ))); break; + case Bytecodes::_iconst_1 : ipush(append(new Constant(intOne ))); break; + case Bytecodes::_iconst_2 : ipush(append(new Constant(new IntConstant ( 2)))); break; + case Bytecodes::_iconst_3 : ipush(append(new Constant(new IntConstant ( 3)))); break; + case Bytecodes::_iconst_4 : ipush(append(new Constant(new IntConstant ( 4)))); break; + case Bytecodes::_iconst_5 : ipush(append(new Constant(new IntConstant ( 5)))); break; + case Bytecodes::_lconst_0 : lpush(append(new Constant(new LongConstant ( 0)))); break; + case Bytecodes::_lconst_1 : lpush(append(new Constant(new LongConstant ( 1)))); break; + case Bytecodes::_fconst_0 : fpush(append(new Constant(new FloatConstant ( 0)))); break; + case Bytecodes::_fconst_1 : fpush(append(new Constant(new FloatConstant ( 1)))); break; + case Bytecodes::_fconst_2 : fpush(append(new Constant(new FloatConstant ( 2)))); break; + case Bytecodes::_dconst_0 : dpush(append(new Constant(new DoubleConstant( 0)))); break; + case Bytecodes::_dconst_1 : dpush(append(new Constant(new DoubleConstant( 1)))); break; + case Bytecodes::_bipush : ipush(append(new Constant(new IntConstant(((signed char*)s.cur_bcp())[1])))); break; + case Bytecodes::_sipush : ipush(append(new Constant(new IntConstant((short)Bytes::get_Java_u2(s.cur_bcp()+1))))); break; + case Bytecodes::_ldc : // fall through + case Bytecodes::_ldc_w : // fall through + case Bytecodes::_ldc2_w : load_constant(); break; + case Bytecodes::_iload : load_local(intType , s.get_index()); break; + case Bytecodes::_lload : load_local(longType , s.get_index()); break; + case Bytecodes::_fload : load_local(floatType , s.get_index()); break; + case Bytecodes::_dload : load_local(doubleType , s.get_index()); break; + case Bytecodes::_aload : load_local(instanceType, s.get_index()); break; + case Bytecodes::_iload_0 : load_local(intType , 0); break; + case Bytecodes::_iload_1 : load_local(intType , 1); break; + case Bytecodes::_iload_2 : load_local(intType , 2); break; + case Bytecodes::_iload_3 : load_local(intType , 3); break; + case Bytecodes::_lload_0 : load_local(longType , 0); break; + case Bytecodes::_lload_1 : load_local(longType , 1); break; + case Bytecodes::_lload_2 : load_local(longType , 2); break; + case Bytecodes::_lload_3 : load_local(longType , 3); break; + case Bytecodes::_fload_0 : load_local(floatType , 0); break; + case Bytecodes::_fload_1 : load_local(floatType , 1); break; + case Bytecodes::_fload_2 : load_local(floatType , 2); break; + case Bytecodes::_fload_3 : load_local(floatType , 3); break; + case Bytecodes::_dload_0 : load_local(doubleType, 0); break; + case Bytecodes::_dload_1 : load_local(doubleType, 1); break; + case Bytecodes::_dload_2 : load_local(doubleType, 2); break; + case Bytecodes::_dload_3 : load_local(doubleType, 3); break; + case Bytecodes::_aload_0 : load_local(objectType, 0); break; + case Bytecodes::_aload_1 : load_local(objectType, 1); break; + case Bytecodes::_aload_2 : load_local(objectType, 2); break; + case Bytecodes::_aload_3 : load_local(objectType, 3); break; + case Bytecodes::_iaload : load_indexed(T_INT ); break; + case Bytecodes::_laload : load_indexed(T_LONG ); break; + case Bytecodes::_faload : load_indexed(T_FLOAT ); break; + case Bytecodes::_daload : load_indexed(T_DOUBLE); break; + case Bytecodes::_aaload : load_indexed(T_OBJECT); break; + case Bytecodes::_baload : load_indexed(T_BYTE ); break; + case Bytecodes::_caload : load_indexed(T_CHAR ); break; + case Bytecodes::_saload : load_indexed(T_SHORT ); break; + case Bytecodes::_istore : store_local(intType , s.get_index()); break; + case Bytecodes::_lstore : store_local(longType , s.get_index()); break; + case Bytecodes::_fstore : store_local(floatType , s.get_index()); break; + case Bytecodes::_dstore : store_local(doubleType, s.get_index()); break; + case Bytecodes::_astore : store_local(objectType, s.get_index()); break; + case Bytecodes::_istore_0 : store_local(intType , 0); break; + case Bytecodes::_istore_1 : store_local(intType , 1); break; + case Bytecodes::_istore_2 : store_local(intType , 2); break; + case Bytecodes::_istore_3 : store_local(intType , 3); break; + case Bytecodes::_lstore_0 : store_local(longType , 0); break; + case Bytecodes::_lstore_1 : store_local(longType , 1); break; + case Bytecodes::_lstore_2 : store_local(longType , 2); break; + case Bytecodes::_lstore_3 : store_local(longType , 3); break; + case Bytecodes::_fstore_0 : store_local(floatType , 0); break; + case Bytecodes::_fstore_1 : store_local(floatType , 1); break; + case Bytecodes::_fstore_2 : store_local(floatType , 2); break; + case Bytecodes::_fstore_3 : store_local(floatType , 3); break; + case Bytecodes::_dstore_0 : store_local(doubleType, 0); break; + case Bytecodes::_dstore_1 : store_local(doubleType, 1); break; + case Bytecodes::_dstore_2 : store_local(doubleType, 2); break; + case Bytecodes::_dstore_3 : store_local(doubleType, 3); break; + case Bytecodes::_astore_0 : store_local(objectType, 0); break; + case Bytecodes::_astore_1 : store_local(objectType, 1); break; + case Bytecodes::_astore_2 : store_local(objectType, 2); break; + case Bytecodes::_astore_3 : store_local(objectType, 3); break; + case Bytecodes::_iastore : store_indexed(T_INT ); break; + case Bytecodes::_lastore : store_indexed(T_LONG ); break; + case Bytecodes::_fastore : store_indexed(T_FLOAT ); break; + case Bytecodes::_dastore : store_indexed(T_DOUBLE); break; + case Bytecodes::_aastore : store_indexed(T_OBJECT); break; + case Bytecodes::_bastore : store_indexed(T_BYTE ); break; + case Bytecodes::_castore : store_indexed(T_CHAR ); break; + case Bytecodes::_sastore : store_indexed(T_SHORT ); break; + case Bytecodes::_pop : // fall through + case Bytecodes::_pop2 : // fall through + case Bytecodes::_dup : // fall through + case Bytecodes::_dup_x1 : // fall through + case Bytecodes::_dup_x2 : // fall through + case Bytecodes::_dup2 : // fall through + case Bytecodes::_dup2_x1 : // fall through + case Bytecodes::_dup2_x2 : // fall through + case Bytecodes::_swap : stack_op(code); break; + case Bytecodes::_iadd : arithmetic_op(intType , code); break; + case Bytecodes::_ladd : arithmetic_op(longType , code); break; + case Bytecodes::_fadd : arithmetic_op(floatType , code); break; + case Bytecodes::_dadd : arithmetic_op(doubleType, code); break; + case Bytecodes::_isub : arithmetic_op(intType , code); break; + case Bytecodes::_lsub : arithmetic_op(longType , code); break; + case Bytecodes::_fsub : arithmetic_op(floatType , code); break; + case Bytecodes::_dsub : arithmetic_op(doubleType, code); break; + case Bytecodes::_imul : arithmetic_op(intType , code); break; + case Bytecodes::_lmul : arithmetic_op(longType , code); break; + case Bytecodes::_fmul : arithmetic_op(floatType , code); break; + case Bytecodes::_dmul : arithmetic_op(doubleType, code); break; + case Bytecodes::_idiv : arithmetic_op(intType , code, lock_stack()); break; + case Bytecodes::_ldiv : arithmetic_op(longType , code, lock_stack()); break; + case Bytecodes::_fdiv : arithmetic_op(floatType , code); break; + case Bytecodes::_ddiv : arithmetic_op(doubleType, code); break; + case Bytecodes::_irem : arithmetic_op(intType , code, lock_stack()); break; + case Bytecodes::_lrem : arithmetic_op(longType , code, lock_stack()); break; + case Bytecodes::_frem : arithmetic_op(floatType , code); break; + case Bytecodes::_drem : arithmetic_op(doubleType, code); break; + case Bytecodes::_ineg : negate_op(intType ); break; + case Bytecodes::_lneg : negate_op(longType ); break; + case Bytecodes::_fneg : negate_op(floatType ); break; + case Bytecodes::_dneg : negate_op(doubleType); break; + case Bytecodes::_ishl : shift_op(intType , code); break; + case Bytecodes::_lshl : shift_op(longType, code); break; + case Bytecodes::_ishr : shift_op(intType , code); break; + case Bytecodes::_lshr : shift_op(longType, code); break; + case Bytecodes::_iushr : shift_op(intType , code); break; + case Bytecodes::_lushr : shift_op(longType, code); break; + case Bytecodes::_iand : logic_op(intType , code); break; + case Bytecodes::_land : logic_op(longType, code); break; + case Bytecodes::_ior : logic_op(intType , code); break; + case Bytecodes::_lor : logic_op(longType, code); break; + case Bytecodes::_ixor : logic_op(intType , code); break; + case Bytecodes::_lxor : logic_op(longType, code); break; + case Bytecodes::_iinc : increment(); break; + case Bytecodes::_i2l : convert(code, T_INT , T_LONG ); break; + case Bytecodes::_i2f : convert(code, T_INT , T_FLOAT ); break; + case Bytecodes::_i2d : convert(code, T_INT , T_DOUBLE); break; + case Bytecodes::_l2i : convert(code, T_LONG , T_INT ); break; + case Bytecodes::_l2f : convert(code, T_LONG , T_FLOAT ); break; + case Bytecodes::_l2d : convert(code, T_LONG , T_DOUBLE); break; + case Bytecodes::_f2i : convert(code, T_FLOAT , T_INT ); break; + case Bytecodes::_f2l : convert(code, T_FLOAT , T_LONG ); break; + case Bytecodes::_f2d : convert(code, T_FLOAT , T_DOUBLE); break; + case Bytecodes::_d2i : convert(code, T_DOUBLE, T_INT ); break; + case Bytecodes::_d2l : convert(code, T_DOUBLE, T_LONG ); break; + case Bytecodes::_d2f : convert(code, T_DOUBLE, T_FLOAT ); break; + case Bytecodes::_i2b : convert(code, T_INT , T_BYTE ); break; + case Bytecodes::_i2c : convert(code, T_INT , T_CHAR ); break; + case Bytecodes::_i2s : convert(code, T_INT , T_SHORT ); break; + case Bytecodes::_lcmp : compare_op(longType , code); break; + case Bytecodes::_fcmpl : compare_op(floatType , code); break; + case Bytecodes::_fcmpg : compare_op(floatType , code); break; + case Bytecodes::_dcmpl : compare_op(doubleType, code); break; + case Bytecodes::_dcmpg : compare_op(doubleType, code); break; + case Bytecodes::_ifeq : if_zero(intType , If::eql); break; + case Bytecodes::_ifne : if_zero(intType , If::neq); break; + case Bytecodes::_iflt : if_zero(intType , If::lss); break; + case Bytecodes::_ifge : if_zero(intType , If::geq); break; + case Bytecodes::_ifgt : if_zero(intType , If::gtr); break; + case Bytecodes::_ifle : if_zero(intType , If::leq); break; + case Bytecodes::_if_icmpeq : if_same(intType , If::eql); break; + case Bytecodes::_if_icmpne : if_same(intType , If::neq); break; + case Bytecodes::_if_icmplt : if_same(intType , If::lss); break; + case Bytecodes::_if_icmpge : if_same(intType , If::geq); break; + case Bytecodes::_if_icmpgt : if_same(intType , If::gtr); break; + case Bytecodes::_if_icmple : if_same(intType , If::leq); break; + case Bytecodes::_if_acmpeq : if_same(objectType, If::eql); break; + case Bytecodes::_if_acmpne : if_same(objectType, If::neq); break; + case Bytecodes::_goto : _goto(s.cur_bci(), s.get_dest()); break; + case Bytecodes::_jsr : jsr(s.get_dest()); break; + case Bytecodes::_ret : ret(s.get_index()); break; + case Bytecodes::_tableswitch : table_switch(); break; + case Bytecodes::_lookupswitch : lookup_switch(); break; + case Bytecodes::_ireturn : method_return(ipop()); break; + case Bytecodes::_lreturn : method_return(lpop()); break; + case Bytecodes::_freturn : method_return(fpop()); break; + case Bytecodes::_dreturn : method_return(dpop()); break; + case Bytecodes::_areturn : method_return(apop()); break; + case Bytecodes::_return : method_return(NULL ); break; + case Bytecodes::_getstatic : // fall through + case Bytecodes::_putstatic : // fall through + case Bytecodes::_getfield : // fall through + case Bytecodes::_putfield : access_field(code); break; + case Bytecodes::_invokevirtual : // fall through + case Bytecodes::_invokespecial : // fall through + case Bytecodes::_invokestatic : // fall through + case Bytecodes::_invokeinterface: invoke(code); break; + case Bytecodes::_xxxunusedxxx : ShouldNotReachHere(); break; + case Bytecodes::_new : new_instance(s.get_index_big()); break; + case Bytecodes::_newarray : new_type_array(); break; + case Bytecodes::_anewarray : new_object_array(); break; + case Bytecodes::_arraylength : ipush(append(new ArrayLength(apop(), lock_stack()))); break; + case Bytecodes::_athrow : throw_op(s.cur_bci()); break; + case Bytecodes::_checkcast : check_cast(s.get_index_big()); break; + case Bytecodes::_instanceof : instance_of(s.get_index_big()); break; + // Note: we do not have special handling for the monitorenter bytecode if DeoptC1 && DeoptOnAsyncException + case Bytecodes::_monitorenter : monitorenter(apop(), s.cur_bci()); break; + case Bytecodes::_monitorexit : monitorexit (apop(), s.cur_bci()); break; + case Bytecodes::_wide : ShouldNotReachHere(); break; + case Bytecodes::_multianewarray : new_multi_array(s.cur_bcp()[3]); break; + case Bytecodes::_ifnull : if_null(objectType, If::eql); break; + case Bytecodes::_ifnonnull : if_null(objectType, If::neq); break; + case Bytecodes::_goto_w : _goto(s.cur_bci(), s.get_far_dest()); break; + case Bytecodes::_jsr_w : jsr(s.get_far_dest()); break; + case Bytecodes::_breakpoint : BAILOUT_("concurrent setting of breakpoint", NULL); + default : ShouldNotReachHere(); break; + } + // save current bci to setup Goto at the end + prev_bci = s.cur_bci(); + } + CHECK_BAILOUT_(NULL); + // stop processing of this block (see try_inline_full) + if (_skip_block) { + _skip_block = false; + assert(_last && _last->as_BlockEnd(), ""); + return _last->as_BlockEnd(); + } + // if there are any, check if last instruction is a BlockEnd instruction + BlockEnd* end = last()->as_BlockEnd(); + if (end == NULL) { + // all blocks must end with a BlockEnd instruction => add a Goto + end = new Goto(block_at(s.cur_bci()), false); + _last = _last->set_next(end, prev_bci); + } + assert(end == last()->as_BlockEnd(), "inconsistency"); + + // if the method terminates, we don't need the stack anymore + if (end->as_Return() != NULL) { + state()->clear_stack(); + } else if (end->as_Throw() != NULL) { + // May have exception handler in caller scopes + state()->truncate_stack(scope()->lock_stack_size()); + } + + // connect to begin & set state + // NOTE that inlining may have changed the block we are parsing + block()->set_end(end); + end->set_state(state()); + // propagate state + for (int i = end->number_of_sux() - 1; i >= 0; i--) { + BlockBegin* sux = end->sux_at(i); + assert(sux->is_predecessor(block()), "predecessor missing"); + // be careful, bailout if bytecodes are strange + if (!sux->try_merge(state())) BAILOUT_("block join failed", NULL); + scope_data()->add_to_work_list(end->sux_at(i)); + } + + scope_data()->set_stream(NULL); + + // done + return end; +} + + +void GraphBuilder::iterate_all_blocks(bool start_in_current_block_for_inlining) { + do { + if (start_in_current_block_for_inlining && !bailed_out()) { + iterate_bytecodes_for_block(0); + start_in_current_block_for_inlining = false; + } else { + BlockBegin* b; + while ((b = scope_data()->remove_from_work_list()) != NULL) { + if (!b->is_set(BlockBegin::was_visited_flag)) { + if (b->is_set(BlockBegin::osr_entry_flag)) { + // we're about to parse the osr entry block, so make sure + // we setup the OSR edge leading into this block so that + // Phis get setup correctly. + setup_osr_entry_block(); + // this is no longer the osr entry block, so clear it. + b->clear(BlockBegin::osr_entry_flag); + } + b->set(BlockBegin::was_visited_flag); + connect_to_end(b); + } + } + } + } while (!bailed_out() && !scope_data()->is_work_list_empty()); +} + + +bool GraphBuilder::_is_initialized = false; +bool GraphBuilder::_can_trap [Bytecodes::number_of_java_codes]; +bool GraphBuilder::_is_async[Bytecodes::number_of_java_codes]; + +void GraphBuilder::initialize() { + // make sure initialization happens only once (need a + // lock here, if we allow the compiler to be re-entrant) + if (is_initialized()) return; + _is_initialized = true; + + // the following bytecodes are assumed to potentially + // throw exceptions in compiled code - note that e.g. + // monitorexit & the return bytecodes do not throw + // exceptions since monitor pairing proved that they + // succeed (if monitor pairing succeeded) + Bytecodes::Code can_trap_list[] = + { Bytecodes::_ldc + , Bytecodes::_ldc_w + , Bytecodes::_ldc2_w + , Bytecodes::_iaload + , Bytecodes::_laload + , Bytecodes::_faload + , Bytecodes::_daload + , Bytecodes::_aaload + , Bytecodes::_baload + , Bytecodes::_caload + , Bytecodes::_saload + , Bytecodes::_iastore + , Bytecodes::_lastore + , Bytecodes::_fastore + , Bytecodes::_dastore + , Bytecodes::_aastore + , Bytecodes::_bastore + , Bytecodes::_castore + , Bytecodes::_sastore + , Bytecodes::_idiv + , Bytecodes::_ldiv + , Bytecodes::_irem + , Bytecodes::_lrem + , Bytecodes::_getstatic + , Bytecodes::_putstatic + , Bytecodes::_getfield + , Bytecodes::_putfield + , Bytecodes::_invokevirtual + , Bytecodes::_invokespecial + , Bytecodes::_invokestatic + , Bytecodes::_invokeinterface + , Bytecodes::_new + , Bytecodes::_newarray + , Bytecodes::_anewarray + , Bytecodes::_arraylength + , Bytecodes::_athrow + , Bytecodes::_checkcast + , Bytecodes::_instanceof + , Bytecodes::_monitorenter + , Bytecodes::_multianewarray + }; + + // the following bytecodes are assumed to potentially + // throw asynchronous exceptions in compiled code due + // to safepoints (note: these entries could be merged + // with the can_trap_list - however, we need to know + // which ones are asynchronous for now - see also the + // comment in GraphBuilder::handle_exception) + Bytecodes::Code is_async_list[] = + { Bytecodes::_ifeq + , Bytecodes::_ifne + , Bytecodes::_iflt + , Bytecodes::_ifge + , Bytecodes::_ifgt + , Bytecodes::_ifle + , Bytecodes::_if_icmpeq + , Bytecodes::_if_icmpne + , Bytecodes::_if_icmplt + , Bytecodes::_if_icmpge + , Bytecodes::_if_icmpgt + , Bytecodes::_if_icmple + , Bytecodes::_if_acmpeq + , Bytecodes::_if_acmpne + , Bytecodes::_goto + , Bytecodes::_jsr + , Bytecodes::_ret + , Bytecodes::_tableswitch + , Bytecodes::_lookupswitch + , Bytecodes::_ireturn + , Bytecodes::_lreturn + , Bytecodes::_freturn + , Bytecodes::_dreturn + , Bytecodes::_areturn + , Bytecodes::_return + , Bytecodes::_ifnull + , Bytecodes::_ifnonnull + , Bytecodes::_goto_w + , Bytecodes::_jsr_w + }; + + // inititialize trap tables + for (int i = 0; i < Bytecodes::number_of_java_codes; i++) { + _can_trap[i] = false; + _is_async[i] = false; + } + // set standard trap info + for (uint j = 0; j < ARRAY_SIZE(can_trap_list); j++) { + _can_trap[can_trap_list[j]] = true; + } + + // We now deoptimize if an asynchronous exception is thrown. This + // considerably cleans up corner case issues related to javac's + // incorrect exception handler ranges for async exceptions and + // allows us to precisely analyze the types of exceptions from + // certain bytecodes. + if (!(DeoptC1 && DeoptOnAsyncException)) { + // set asynchronous trap info + for (uint k = 0; k < ARRAY_SIZE(is_async_list); k++) { + assert(!_can_trap[is_async_list[k]], "can_trap_list and is_async_list should be disjoint"); + _can_trap[is_async_list[k]] = true; + _is_async[is_async_list[k]] = true; + } + } +} + + +BlockBegin* GraphBuilder::header_block(BlockBegin* entry, BlockBegin::Flag f, ValueStack* state) { + assert(entry->is_set(f), "entry/flag mismatch"); + // create header block + BlockBegin* h = new BlockBegin(entry->bci()); + h->set_depth_first_number(0); + + Value l = h; + if (profile_branches()) { + // Increment the invocation count on entry to the method. We + // can't use profile_invocation here because append isn't setup to + // work properly at this point. The instruction have to be + // appended to the instruction stream by hand. + Value m = new Constant(new ObjectConstant(compilation()->method())); + h->set_next(m, 0); + Value p = new ProfileCounter(m, methodOopDesc::interpreter_invocation_counter_offset_in_bytes(), 1); + m->set_next(p, 0); + l = p; + } + + BlockEnd* g = new Goto(entry, false); + l->set_next(g, entry->bci()); + h->set_end(g); + h->set(f); + // setup header block end state + ValueStack* s = state->copy(); // can use copy since stack is empty (=> no phis) + assert(s->stack_is_empty(), "must have empty stack at entry point"); + g->set_state(s); + return h; +} + + + +BlockBegin* GraphBuilder::setup_start_block(int osr_bci, BlockBegin* std_entry, BlockBegin* osr_entry, ValueStack* state) { + BlockBegin* start = new BlockBegin(0); + + // This code eliminates the empty start block at the beginning of + // each method. Previously, each method started with the + // start-block created below, and this block was followed by the + // header block that was always empty. This header block is only + // necesary if std_entry is also a backward branch target because + // then phi functions may be necessary in the header block. It's + // also necessary when profiling so that there's a single block that + // can increment the interpreter_invocation_count. + BlockBegin* new_header_block; + if (std_entry->number_of_preds() == 0 && !profile_branches()) { + new_header_block = std_entry; + } else { + new_header_block = header_block(std_entry, BlockBegin::std_entry_flag, state); + } + + // setup start block (root for the IR graph) + Base* base = + new Base( + new_header_block, + osr_entry + ); + start->set_next(base, 0); + start->set_end(base); + // create & setup state for start block + start->set_state(state->copy()); + base->set_state(state->copy()); + + if (base->std_entry()->state() == NULL) { + // setup states for header blocks + base->std_entry()->merge(state); + } + + assert(base->std_entry()->state() != NULL, ""); + return start; +} + + +void GraphBuilder::setup_osr_entry_block() { + assert(compilation()->is_osr_compile(), "only for osrs"); + + int osr_bci = compilation()->osr_bci(); + ciBytecodeStream s(method()); + s.reset_to_bci(osr_bci); + s.next(); + scope_data()->set_stream(&s); + + // create a new block to be the osr setup code + _osr_entry = new BlockBegin(osr_bci); + _osr_entry->set(BlockBegin::osr_entry_flag); + _osr_entry->set_depth_first_number(0); + BlockBegin* target = bci2block()->at(osr_bci); + assert(target != NULL && target->is_set(BlockBegin::osr_entry_flag), "must be there"); + // the osr entry has no values for locals + ValueStack* state = target->state()->copy(); + _osr_entry->set_state(state); + + kill_all(); + _block = _osr_entry; + _state = _osr_entry->state()->copy(); + _last = _osr_entry; + Value e = append(new OsrEntry()); + e->set_needs_null_check(false); + + // OSR buffer is + // + // locals[nlocals-1..0] + // monitors[number_of_locks-1..0] + // + // locals is a direct copy of the interpreter frame so in the osr buffer + // so first slot in the local array is the last local from the interpreter + // and last slot is local[0] (receiver) from the interpreter + // + // Similarly with locks. The first lock slot in the osr buffer is the nth lock + // from the interpreter frame, the nth lock slot in the osr buffer is 0th lock + // in the interpreter frame (the method lock if a sync method) + + // Initialize monitors in the compiled activation. + + int index; + Value local; + + // find all the locals that the interpreter thinks contain live oops + const BitMap live_oops = method()->live_local_oops_at_bci(osr_bci); + + // compute the offset into the locals so that we can treat the buffer + // as if the locals were still in the interpreter frame + int locals_offset = BytesPerWord * (method()->max_locals() - 1); + for_each_local_value(state, index, local) { + int offset = locals_offset - (index + local->type()->size() - 1) * BytesPerWord; + Value get; + if (local->type()->is_object_kind() && !live_oops.at(index)) { + // The interpreter thinks this local is dead but the compiler + // doesn't so pretend that the interpreter passed in null. + get = append(new Constant(objectNull)); + } else { + get = append(new UnsafeGetRaw(as_BasicType(local->type()), e, + append(new Constant(new IntConstant(offset))), + 0, + true)); + } + _state->store_local(index, get); + } + + // the storage for the OSR buffer is freed manually in the LIRGenerator. + + assert(state->caller_state() == NULL, "should be top scope"); + state->clear_locals(); + Goto* g = new Goto(target, false); + g->set_state(_state->copy()); + append(g); + _osr_entry->set_end(g); + target->merge(_osr_entry->end()->state()); + + scope_data()->set_stream(NULL); +} + + +ValueStack* GraphBuilder::state_at_entry() { + ValueStack* state = new ValueStack(scope(), method()->max_locals(), method()->max_stack()); + + // Set up locals for receiver + int idx = 0; + if (!method()->is_static()) { + // we should always see the receiver + state->store_local(idx, new Local(objectType, idx)); + idx = 1; + } + + // Set up locals for incoming arguments + ciSignature* sig = method()->signature(); + for (int i = 0; i < sig->count(); i++) { + ciType* type = sig->type_at(i); + BasicType basic_type = type->basic_type(); + // don't allow T_ARRAY to propagate into locals types + if (basic_type == T_ARRAY) basic_type = T_OBJECT; + ValueType* vt = as_ValueType(basic_type); + state->store_local(idx, new Local(vt, idx)); + idx += type->size(); + } + + // lock synchronized method + if (method()->is_synchronized()) { + state->lock(scope(), NULL); + } + + return state; +} + + +GraphBuilder::GraphBuilder(Compilation* compilation, IRScope* scope) + : _scope_data(NULL) + , _exception_state(NULL) + , _instruction_count(0) + , _osr_entry(NULL) + , _memory(new MemoryBuffer()) + , _compilation(compilation) + , _inline_bailout_msg(NULL) +{ + int osr_bci = compilation->osr_bci(); + + // determine entry points and bci2block mapping + BlockListBuilder blm(compilation, scope, osr_bci); + CHECK_BAILOUT(); + + BlockList* bci2block = blm.bci2block(); + BlockBegin* start_block = bci2block->at(0); + + assert(is_initialized(), "GraphBuilder must have been initialized"); + push_root_scope(scope, bci2block, start_block); + + // setup state for std entry + _initial_state = state_at_entry(); + start_block->merge(_initial_state); + + BlockBegin* sync_handler = NULL; + if (method()->is_synchronized() || DTraceMethodProbes) { + // setup an exception handler to do the unlocking and/or notification + sync_handler = new BlockBegin(-1); + sync_handler->set(BlockBegin::exception_entry_flag); + sync_handler->set(BlockBegin::is_on_work_list_flag); + sync_handler->set(BlockBegin::default_exception_handler_flag); + + ciExceptionHandler* desc = new ciExceptionHandler(method()->holder(), 0, method()->code_size(), -1, 0); + XHandler* h = new XHandler(desc); + h->set_entry_block(sync_handler); + scope_data()->xhandlers()->append(h); + scope_data()->set_has_handler(); + } + + // complete graph + _vmap = new ValueMap(); + scope->compute_lock_stack_size(); + switch (scope->method()->intrinsic_id()) { + case vmIntrinsics::_dabs : // fall through + case vmIntrinsics::_dsqrt : // fall through + case vmIntrinsics::_dsin : // fall through + case vmIntrinsics::_dcos : // fall through + case vmIntrinsics::_dtan : // fall through + case vmIntrinsics::_dlog : // fall through + case vmIntrinsics::_dlog10 : // fall through + { + // Compiles where the root method is an intrinsic need a special + // compilation environment because the bytecodes for the method + // shouldn't be parsed during the compilation, only the special + // Intrinsic node should be emitted. If this isn't done the the + // code for the inlined version will be different than the root + // compiled version which could lead to monotonicity problems on + // intel. + + // Set up a stream so that appending instructions works properly. + ciBytecodeStream s(scope->method()); + s.reset_to_bci(0); + scope_data()->set_stream(&s); + s.next(); + + // setup the initial block state + _block = start_block; + _state = start_block->state()->copy(); + _last = start_block; + load_local(doubleType, 0); + + // Emit the intrinsic node. + bool result = try_inline_intrinsics(scope->method()); + if (!result) BAILOUT("failed to inline intrinsic"); + method_return(dpop()); + + // connect the begin and end blocks and we're all done. + BlockEnd* end = last()->as_BlockEnd(); + block()->set_end(end); + end->set_state(state()); + break; + } + default: + scope_data()->add_to_work_list(start_block); + iterate_all_blocks(); + break; + } + CHECK_BAILOUT(); + + if (sync_handler && sync_handler->state() != NULL) { + Value lock = NULL; + if (method()->is_synchronized()) { + lock = method()->is_static() ? new Constant(new InstanceConstant(method()->holder()->java_mirror())) : + _initial_state->local_at(0); + + sync_handler->state()->unlock(); + sync_handler->state()->lock(scope, lock); + + } + fill_sync_handler(lock, sync_handler, true); + } + + _start = setup_start_block(osr_bci, start_block, _osr_entry, _initial_state); + + eliminate_redundant_phis(_start); + + NOT_PRODUCT(if (PrintValueNumbering && Verbose) print_stats()); + // for osr compile, bailout if some requirements are not fulfilled + if (osr_bci != -1) { + BlockBegin* osr_block = blm.bci2block()->at(osr_bci); + assert(osr_block->is_set(BlockBegin::was_visited_flag),"osr entry must have been visited for osr compile"); + + // check if osr entry point has empty stack - we cannot handle non-empty stacks at osr entry points + if (!osr_block->state()->stack_is_empty()) { + BAILOUT("stack not empty at OSR entry point"); + } + } +#ifndef PRODUCT + if (PrintCompilation && Verbose) tty->print_cr("Created %d Instructions", _instruction_count); +#endif +} + + +ValueStack* GraphBuilder::lock_stack() { + // return a new ValueStack representing just the current lock stack + // (for debug info at safepoints in exception throwing or handling) + ValueStack* new_stack = state()->copy_locks(); + return new_stack; +} + + +int GraphBuilder::recursive_inline_level(ciMethod* cur_callee) const { + int recur_level = 0; + for (IRScope* s = scope(); s != NULL; s = s->caller()) { + if (s->method() == cur_callee) { + ++recur_level; + } + } + return recur_level; +} + + +bool GraphBuilder::try_inline(ciMethod* callee, bool holder_known) { + // Clear out any existing inline bailout condition + clear_inline_bailout(); + + if (callee->should_exclude()) { + // callee is excluded + INLINE_BAILOUT("excluded by CompilerOracle") + } else if (!callee->can_be_compiled()) { + // callee is not compilable (prob. has breakpoints) + INLINE_BAILOUT("not compilable") + } else if (callee->intrinsic_id() != vmIntrinsics::_none && try_inline_intrinsics(callee)) { + // intrinsics can be native or not + return true; + } else if (callee->is_native()) { + // non-intrinsic natives cannot be inlined + INLINE_BAILOUT("non-intrinsic native") + } else if (callee->is_abstract()) { + INLINE_BAILOUT("abstract") + } else { + return try_inline_full(callee, holder_known); + } +} + + +bool GraphBuilder::try_inline_intrinsics(ciMethod* callee) { + if (!InlineNatives ) INLINE_BAILOUT("intrinsic method inlining disabled"); + if (callee->is_synchronized()) INLINE_BAILOUT("intrinsic method is synchronized"); + // callee seems like a good candidate + // determine id + bool preserves_state = false; + bool cantrap = true; + vmIntrinsics::ID id = callee->intrinsic_id(); + switch (id) { + case vmIntrinsics::_arraycopy : + if (!InlineArrayCopy) return false; + break; + + case vmIntrinsics::_currentTimeMillis: + case vmIntrinsics::_nanoTime: + preserves_state = true; + cantrap = false; + break; + + case vmIntrinsics::_floatToRawIntBits : + case vmIntrinsics::_intBitsToFloat : + case vmIntrinsics::_doubleToRawLongBits : + case vmIntrinsics::_longBitsToDouble : + if (!InlineMathNatives) return false; + preserves_state = true; + cantrap = false; + break; + + case vmIntrinsics::_getClass : + if (!InlineClassNatives) return false; + preserves_state = true; + break; + + case vmIntrinsics::_currentThread : + if (!InlineThreadNatives) return false; + preserves_state = true; + cantrap = false; + break; + + case vmIntrinsics::_dabs : // fall through + case vmIntrinsics::_dsqrt : // fall through + case vmIntrinsics::_dsin : // fall through + case vmIntrinsics::_dcos : // fall through + case vmIntrinsics::_dtan : // fall through + case vmIntrinsics::_dlog : // fall through + case vmIntrinsics::_dlog10 : // fall through + if (!InlineMathNatives) return false; + cantrap = false; + preserves_state = true; + break; + + // sun/misc/AtomicLong.attemptUpdate + case vmIntrinsics::_attemptUpdate : + if (!VM_Version::supports_cx8()) return false; + if (!InlineAtomicLong) return false; + preserves_state = true; + break; + + // Use special nodes for Unsafe instructions so we can more easily + // perform an address-mode optimization on the raw variants + case vmIntrinsics::_getObject : return append_unsafe_get_obj(callee, T_OBJECT, false); + case vmIntrinsics::_getBoolean: return append_unsafe_get_obj(callee, T_BOOLEAN, false); + case vmIntrinsics::_getByte : return append_unsafe_get_obj(callee, T_BYTE, false); + case vmIntrinsics::_getShort : return append_unsafe_get_obj(callee, T_SHORT, false); + case vmIntrinsics::_getChar : return append_unsafe_get_obj(callee, T_CHAR, false); + case vmIntrinsics::_getInt : return append_unsafe_get_obj(callee, T_INT, false); + case vmIntrinsics::_getLong : return append_unsafe_get_obj(callee, T_LONG, false); + case vmIntrinsics::_getFloat : return append_unsafe_get_obj(callee, T_FLOAT, false); + case vmIntrinsics::_getDouble : return append_unsafe_get_obj(callee, T_DOUBLE, false); + + case vmIntrinsics::_putObject : return append_unsafe_put_obj(callee, T_OBJECT, false); + case vmIntrinsics::_putBoolean: return append_unsafe_put_obj(callee, T_BOOLEAN, false); + case vmIntrinsics::_putByte : return append_unsafe_put_obj(callee, T_BYTE, false); + case vmIntrinsics::_putShort : return append_unsafe_put_obj(callee, T_SHORT, false); + case vmIntrinsics::_putChar : return append_unsafe_put_obj(callee, T_CHAR, false); + case vmIntrinsics::_putInt : return append_unsafe_put_obj(callee, T_INT, false); + case vmIntrinsics::_putLong : return append_unsafe_put_obj(callee, T_LONG, false); + case vmIntrinsics::_putFloat : return append_unsafe_put_obj(callee, T_FLOAT, false); + case vmIntrinsics::_putDouble : return append_unsafe_put_obj(callee, T_DOUBLE, false); + + case vmIntrinsics::_getObjectVolatile : return append_unsafe_get_obj(callee, T_OBJECT, true); + case vmIntrinsics::_getBooleanVolatile: return append_unsafe_get_obj(callee, T_BOOLEAN, true); + case vmIntrinsics::_getByteVolatile : return append_unsafe_get_obj(callee, T_BYTE, true); + case vmIntrinsics::_getShortVolatile : return append_unsafe_get_obj(callee, T_SHORT, true); + case vmIntrinsics::_getCharVolatile : return append_unsafe_get_obj(callee, T_CHAR, true); + case vmIntrinsics::_getIntVolatile : return append_unsafe_get_obj(callee, T_INT, true); + case vmIntrinsics::_getLongVolatile : return append_unsafe_get_obj(callee, T_LONG, true); + case vmIntrinsics::_getFloatVolatile : return append_unsafe_get_obj(callee, T_FLOAT, true); + case vmIntrinsics::_getDoubleVolatile : return append_unsafe_get_obj(callee, T_DOUBLE, true); + + case vmIntrinsics::_putObjectVolatile : return append_unsafe_put_obj(callee, T_OBJECT, true); + case vmIntrinsics::_putBooleanVolatile: return append_unsafe_put_obj(callee, T_BOOLEAN, true); + case vmIntrinsics::_putByteVolatile : return append_unsafe_put_obj(callee, T_BYTE, true); + case vmIntrinsics::_putShortVolatile : return append_unsafe_put_obj(callee, T_SHORT, true); + case vmIntrinsics::_putCharVolatile : return append_unsafe_put_obj(callee, T_CHAR, true); + case vmIntrinsics::_putIntVolatile : return append_unsafe_put_obj(callee, T_INT, true); + case vmIntrinsics::_putLongVolatile : return append_unsafe_put_obj(callee, T_LONG, true); + case vmIntrinsics::_putFloatVolatile : return append_unsafe_put_obj(callee, T_FLOAT, true); + case vmIntrinsics::_putDoubleVolatile : return append_unsafe_put_obj(callee, T_DOUBLE, true); + + case vmIntrinsics::_getByte_raw : return append_unsafe_get_raw(callee, T_BYTE); + case vmIntrinsics::_getShort_raw : return append_unsafe_get_raw(callee, T_SHORT); + case vmIntrinsics::_getChar_raw : return append_unsafe_get_raw(callee, T_CHAR); + case vmIntrinsics::_getInt_raw : return append_unsafe_get_raw(callee, T_INT); + case vmIntrinsics::_getLong_raw : return append_unsafe_get_raw(callee, T_LONG); + case vmIntrinsics::_getFloat_raw : return append_unsafe_get_raw(callee, T_FLOAT); + case vmIntrinsics::_getDouble_raw : return append_unsafe_get_raw(callee, T_DOUBLE); + + case vmIntrinsics::_putByte_raw : return append_unsafe_put_raw(callee, T_BYTE); + case vmIntrinsics::_putShort_raw : return append_unsafe_put_raw(callee, T_SHORT); + case vmIntrinsics::_putChar_raw : return append_unsafe_put_raw(callee, T_CHAR); + case vmIntrinsics::_putInt_raw : return append_unsafe_put_raw(callee, T_INT); + case vmIntrinsics::_putLong_raw : return append_unsafe_put_raw(callee, T_LONG); + case vmIntrinsics::_putFloat_raw : return append_unsafe_put_raw(callee, T_FLOAT); + case vmIntrinsics::_putDouble_raw : return append_unsafe_put_raw(callee, T_DOUBLE); + + case vmIntrinsics::_prefetchRead : return append_unsafe_prefetch(callee, false, false); + case vmIntrinsics::_prefetchWrite : return append_unsafe_prefetch(callee, false, true); + case vmIntrinsics::_prefetchReadStatic : return append_unsafe_prefetch(callee, true, false); + case vmIntrinsics::_prefetchWriteStatic : return append_unsafe_prefetch(callee, true, true); + + case vmIntrinsics::_checkIndex : + if (!InlineNIOCheckIndex) return false; + preserves_state = true; + break; + case vmIntrinsics::_putOrderedObject : return append_unsafe_put_obj(callee, T_OBJECT, true); + case vmIntrinsics::_putOrderedInt : return append_unsafe_put_obj(callee, T_INT, true); + case vmIntrinsics::_putOrderedLong : return append_unsafe_put_obj(callee, T_LONG, true); + + case vmIntrinsics::_compareAndSwapLong: + if (!VM_Version::supports_cx8()) return false; + // fall through + case vmIntrinsics::_compareAndSwapInt: + case vmIntrinsics::_compareAndSwapObject: + append_unsafe_CAS(callee); + return true; + + default : return false; // do not inline + } + // create intrinsic node + const bool has_receiver = !callee->is_static(); + ValueType* result_type = as_ValueType(callee->return_type()); + + Values* args = state()->pop_arguments(callee->arg_size()); + ValueStack* locks = lock_stack(); + if (profile_calls()) { + // Don't profile in the special case where the root method + // is the intrinsic + if (callee != method()) { + Value recv = NULL; + if (has_receiver) { + recv = args->at(0); + null_check(recv); + } + profile_call(recv, NULL); + } + } + + Intrinsic* result = new Intrinsic(result_type, id, args, has_receiver, lock_stack(), + preserves_state, cantrap); + // append instruction & push result + Value value = append_split(result); + if (result_type != voidType) push(result_type, value); + +#ifndef PRODUCT + // printing + if (PrintInlining) { + print_inline_result(callee, true); + } +#endif + + // done + return true; +} + + +bool GraphBuilder::try_inline_jsr(int jsr_dest_bci) { + // Introduce a new callee continuation point - all Ret instructions + // will be replaced with Gotos to this point. + BlockBegin* cont = block_at(next_bci()); + assert(cont != NULL, "continuation must exist (BlockListBuilder starts a new block after a jsr"); + + // Note: can not assign state to continuation yet, as we have to + // pick up the state from the Ret instructions. + + // Push callee scope + push_scope_for_jsr(cont, jsr_dest_bci); + + // Temporarily set up bytecode stream so we can append instructions + // (only using the bci of this stream) + scope_data()->set_stream(scope_data()->parent()->stream()); + + BlockBegin* jsr_start_block = block_at(jsr_dest_bci); + assert(jsr_start_block != NULL, "jsr start block must exist"); + assert(!jsr_start_block->is_set(BlockBegin::was_visited_flag), "should not have visited jsr yet"); + Goto* goto_sub = new Goto(jsr_start_block, false); + goto_sub->set_state(state()); + // Must copy state to avoid wrong sharing when parsing bytecodes + assert(jsr_start_block->state() == NULL, "should have fresh jsr starting block"); + jsr_start_block->set_state(state()->copy()); + append(goto_sub); + _block->set_end(goto_sub); + _last = _block = jsr_start_block; + + // Clear out bytecode stream + scope_data()->set_stream(NULL); + + scope_data()->add_to_work_list(jsr_start_block); + + // Ready to resume parsing in subroutine + iterate_all_blocks(); + + // If we bailed out during parsing, return immediately (this is bad news) + CHECK_BAILOUT_(false); + + // Detect whether the continuation can actually be reached. If not, + // it has not had state set by the join() operations in + // iterate_bytecodes_for_block()/ret() and we should not touch the + // iteration state. The calling activation of + // iterate_bytecodes_for_block will then complete normally. + if (cont->state() != NULL) { + if (!cont->is_set(BlockBegin::was_visited_flag)) { + // add continuation to work list instead of parsing it immediately + scope_data()->parent()->add_to_work_list(cont); + } + } + + assert(jsr_continuation() == cont, "continuation must not have changed"); + assert(!jsr_continuation()->is_set(BlockBegin::was_visited_flag) || + jsr_continuation()->is_set(BlockBegin::parser_loop_header_flag), + "continuation can only be visited in case of backward branches"); + assert(_last && _last->as_BlockEnd(), "block must have end"); + + // continuation is in work list, so end iteration of current block + _skip_block = true; + pop_scope_for_jsr(); + + return true; +} + + +// Inline the entry of a synchronized method as a monitor enter and +// register the exception handler which releases the monitor if an +// exception is thrown within the callee. Note that the monitor enter +// cannot throw an exception itself, because the receiver is +// guaranteed to be non-null by the explicit null check at the +// beginning of inlining. +void GraphBuilder::inline_sync_entry(Value lock, BlockBegin* sync_handler) { + assert(lock != NULL && sync_handler != NULL, "lock or handler missing"); + + set_exception_state(state()->copy()); + monitorenter(lock, SynchronizationEntryBCI); + assert(_last->as_MonitorEnter() != NULL, "monitor enter expected"); + _last->set_needs_null_check(false); + + sync_handler->set(BlockBegin::exception_entry_flag); + sync_handler->set(BlockBegin::is_on_work_list_flag); + + ciExceptionHandler* desc = new ciExceptionHandler(method()->holder(), 0, method()->code_size(), -1, 0); + XHandler* h = new XHandler(desc); + h->set_entry_block(sync_handler); + scope_data()->xhandlers()->append(h); + scope_data()->set_has_handler(); +} + + +// If an exception is thrown and not handled within an inlined +// synchronized method, the monitor must be released before the +// exception is rethrown in the outer scope. Generate the appropriate +// instructions here. +void GraphBuilder::fill_sync_handler(Value lock, BlockBegin* sync_handler, bool default_handler) { + BlockBegin* orig_block = _block; + ValueStack* orig_state = _state; + Instruction* orig_last = _last; + _last = _block = sync_handler; + _state = sync_handler->state()->copy(); + + assert(sync_handler != NULL, "handler missing"); + assert(!sync_handler->is_set(BlockBegin::was_visited_flag), "is visited here"); + + assert(lock != NULL || default_handler, "lock or handler missing"); + + XHandler* h = scope_data()->xhandlers()->remove_last(); + assert(h->entry_block() == sync_handler, "corrupt list of handlers"); + + block()->set(BlockBegin::was_visited_flag); + Value exception = append_with_bci(new ExceptionObject(), SynchronizationEntryBCI); + assert(exception->is_pinned(), "must be"); + + int bci = SynchronizationEntryBCI; + if (lock) { + assert(state()->locks_size() > 0 && state()->lock_at(state()->locks_size() - 1) == lock, "lock is missing"); + if (lock->bci() == -99) { + lock = append_with_bci(lock, -1); + } + + // exit the monitor in the context of the synchronized method + monitorexit(lock, SynchronizationEntryBCI); + + // exit the context of the synchronized method + if (!default_handler) { + pop_scope(); + _state = _state->copy(); + bci = _state->scope()->caller_bci(); + _state = _state->pop_scope()->copy(); + } + } + + // perform the throw as if at the the call site + apush(exception); + + set_exception_state(state()->copy()); + throw_op(bci); + + BlockEnd* end = last()->as_BlockEnd(); + block()->set_end(end); + end->set_state(state()); + + _block = orig_block; + _state = orig_state; + _last = orig_last; +} + + +bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known) { + assert(!callee->is_native(), "callee must not be native"); + + // first perform tests of things it's not possible to inline + if (callee->has_exception_handlers() && + !InlineMethodsWithExceptionHandlers) INLINE_BAILOUT("callee has exception handlers"); + if (callee->is_synchronized() && + !InlineSynchronizedMethods ) INLINE_BAILOUT("callee is synchronized"); + if (!callee->holder()->is_initialized()) INLINE_BAILOUT("callee's klass not initialized yet"); + if (!callee->has_balanced_monitors()) INLINE_BAILOUT("callee's monitors do not match"); + + // Proper inlining of methods with jsrs requires a little more work. + if (callee->has_jsrs() ) INLINE_BAILOUT("jsrs not handled properly by inliner yet"); + + // now perform tests that are based on flag settings + if (inline_level() > MaxInlineLevel ) INLINE_BAILOUT("too-deep inlining"); + if (recursive_inline_level(callee) > MaxRecursiveInlineLevel) INLINE_BAILOUT("too-deep recursive inlining"); + if (callee->code_size() > max_inline_size() ) INLINE_BAILOUT("callee is too large"); + + // don't inline throwable methods unless the inlining tree is rooted in a throwable class + if (callee->name() == ciSymbol::object_initializer_name() && + callee->holder()->is_subclass_of(ciEnv::current()->Throwable_klass())) { + // Throwable constructor call + IRScope* top = scope(); + while (top->caller() != NULL) { + top = top->caller(); + } + if (!top->method()->holder()->is_subclass_of(ciEnv::current()->Throwable_klass())) { + INLINE_BAILOUT("don't inline Throwable constructors"); + } + } + + // When SSE2 is used on intel, then no special handling is needed + // for strictfp because the enum-constant is fixed at compile time, + // the check for UseSSE2 is needed here + if (strict_fp_requires_explicit_rounding && UseSSE < 2 && method()->is_strict() != callee->is_strict()) { + INLINE_BAILOUT("caller and callee have different strict fp requirements"); + } + + if (compilation()->env()->num_inlined_bytecodes() > DesiredMethodLimit) { + INLINE_BAILOUT("total inlining greater than DesiredMethodLimit"); + } + +#ifndef PRODUCT + // printing + if (PrintInlining) { + print_inline_result(callee, true); + } +#endif + + // NOTE: Bailouts from this point on, which occur at the + // GraphBuilder level, do not cause bailout just of the inlining but + // in fact of the entire compilation. + + BlockBegin* orig_block = block(); + + const int args_base = state()->stack_size() - callee->arg_size(); + assert(args_base >= 0, "stack underflow during inlining"); + + // Insert null check if necessary + Value recv = NULL; + if (code() != Bytecodes::_invokestatic) { + // note: null check must happen even if first instruction of callee does + // an implicit null check since the callee is in a different scope + // and we must make sure exception handling does the right thing + assert(!callee->is_static(), "callee must not be static"); + assert(callee->arg_size() > 0, "must have at least a receiver"); + recv = state()->stack_at(args_base); + null_check(recv); + } + + if (profile_inlined_calls()) { + profile_call(recv, holder_known ? callee->holder() : NULL); + } + + profile_invocation(callee); + + // Introduce a new callee continuation point - if the callee has + // more than one return instruction or the return does not allow + // fall-through of control flow, all return instructions of the + // callee will need to be replaced by Goto's pointing to this + // continuation point. + BlockBegin* cont = block_at(next_bci()); + bool continuation_existed = true; + if (cont == NULL) { + cont = new BlockBegin(next_bci()); + // low number so that continuation gets parsed as early as possible + cont->set_depth_first_number(0); +#ifndef PRODUCT + if (PrintInitialBlockList) { + tty->print_cr("CFG: created block %d (bci %d) as continuation for inline at bci %d", + cont->block_id(), cont->bci(), bci()); + } +#endif + continuation_existed = false; + } + // Record number of predecessors of continuation block before + // inlining, to detect if inlined method has edges to its + // continuation after inlining. + int continuation_preds = cont->number_of_preds(); + + // Push callee scope + push_scope(callee, cont); + + // the BlockListBuilder for the callee could have bailed out + CHECK_BAILOUT_(false); + + // Temporarily set up bytecode stream so we can append instructions + // (only using the bci of this stream) + scope_data()->set_stream(scope_data()->parent()->stream()); + + // Pass parameters into callee state: add assignments + // note: this will also ensure that all arguments are computed before being passed + ValueStack* callee_state = state(); + ValueStack* caller_state = scope()->caller_state(); + { int i = args_base; + while (i < caller_state->stack_size()) { + const int par_no = i - args_base; + Value arg = caller_state->stack_at_inc(i); + // NOTE: take base() of arg->type() to avoid problems storing + // constants + store_local(callee_state, arg, arg->type()->base(), par_no); + } + } + + // Remove args from stack. + // Note that we preserve locals state in case we can use it later + // (see use of pop_scope() below) + caller_state->truncate_stack(args_base); + callee_state->truncate_stack(args_base); + + // Setup state that is used at returns form the inlined method. + // This is essentially the state of the continuation block, + // but without the return value on stack, if any, this will + // be pushed at the return instruction (see method_return). + scope_data()->set_continuation_state(caller_state->copy()); + + // Compute lock stack size for callee scope now that args have been passed + scope()->compute_lock_stack_size(); + + Value lock; + BlockBegin* sync_handler; + + // Inline the locking of the receiver if the callee is synchronized + if (callee->is_synchronized()) { + lock = callee->is_static() ? append(new Constant(new InstanceConstant(callee->holder()->java_mirror()))) + : state()->local_at(0); + sync_handler = new BlockBegin(-1); + inline_sync_entry(lock, sync_handler); + + // recompute the lock stack size + scope()->compute_lock_stack_size(); + } + + + BlockBegin* callee_start_block = block_at(0); + if (callee_start_block != NULL) { + assert(callee_start_block->is_set(BlockBegin::parser_loop_header_flag), "must be loop header"); + Goto* goto_callee = new Goto(callee_start_block, false); + goto_callee->set_state(state()); + // The state for this goto is in the scope of the callee, so use + // the entry bci for the callee instead of the call site bci. + append_with_bci(goto_callee, 0); + _block->set_end(goto_callee); + callee_start_block->merge(callee_state); + + _last = _block = callee_start_block; + + scope_data()->add_to_work_list(callee_start_block); + } + + // Clear out bytecode stream + scope_data()->set_stream(NULL); + + // Ready to resume parsing in callee (either in the same block we + // were in before or in the callee's start block) + iterate_all_blocks(callee_start_block == NULL); + + // If we bailed out during parsing, return immediately (this is bad news) + if (bailed_out()) return false; + + // iterate_all_blocks theoretically traverses in random order; in + // practice, we have only traversed the continuation if we are + // inlining into a subroutine + assert(continuation_existed || + !continuation()->is_set(BlockBegin::was_visited_flag), + "continuation should not have been parsed yet if we created it"); + + // If we bailed out during parsing, return immediately (this is bad news) + CHECK_BAILOUT_(false); + + // At this point we are almost ready to return and resume parsing of + // the caller back in the GraphBuilder. The only thing we want to do + // first is an optimization: during parsing of the callee we + // generated at least one Goto to the continuation block. If we + // generated exactly one, and if the inlined method spanned exactly + // one block (and we didn't have to Goto its entry), then we snip + // off the Goto to the continuation, allowing control to fall + // through back into the caller block and effectively performing + // block merging. This allows load elimination and CSE to take place + // across multiple callee scopes if they are relatively simple, and + // is currently essential to making inlining profitable. + if ( num_returns() == 1 + && block() == orig_block + && block() == inline_cleanup_block()) { + _last = inline_cleanup_return_prev(); + _state = inline_cleanup_state()->pop_scope(); + } else if (continuation_preds == cont->number_of_preds()) { + // Inlining caused that the instructions after the invoke in the + // caller are not reachable any more. So skip filling this block + // with instructions! + assert (cont == continuation(), ""); + assert(_last && _last->as_BlockEnd(), ""); + _skip_block = true; + } else { + // Resume parsing in continuation block unless it was already parsed. + // Note that if we don't change _last here, iteration in + // iterate_bytecodes_for_block will stop when we return. + if (!continuation()->is_set(BlockBegin::was_visited_flag)) { + // add continuation to work list instead of parsing it immediately + assert(_last && _last->as_BlockEnd(), ""); + scope_data()->parent()->add_to_work_list(continuation()); + _skip_block = true; + } + } + + // Fill the exception handler for synchronized methods with instructions + if (callee->is_synchronized() && sync_handler->state() != NULL) { + fill_sync_handler(lock, sync_handler); + } else { + pop_scope(); + } + + compilation()->notice_inlined_method(callee); + + return true; +} + + +void GraphBuilder::inline_bailout(const char* msg) { + assert(msg != NULL, "inline bailout msg must exist"); + _inline_bailout_msg = msg; +} + + +void GraphBuilder::clear_inline_bailout() { + _inline_bailout_msg = NULL; +} + + +void GraphBuilder::push_root_scope(IRScope* scope, BlockList* bci2block, BlockBegin* start) { + ScopeData* data = new ScopeData(NULL); + data->set_scope(scope); + data->set_bci2block(bci2block); + _scope_data = data; + _block = start; +} + + +void GraphBuilder::push_scope(ciMethod* callee, BlockBegin* continuation) { + IRScope* callee_scope = new IRScope(compilation(), scope(), bci(), callee, -1, false); + scope()->add_callee(callee_scope); + + BlockListBuilder blb(compilation(), callee_scope, -1); + CHECK_BAILOUT(); + + if (!blb.bci2block()->at(0)->is_set(BlockBegin::parser_loop_header_flag)) { + // this scope can be inlined directly into the caller so remove + // the block at bci 0. + blb.bci2block()->at_put(0, NULL); + } + + callee_scope->set_caller_state(state()); + set_state(state()->push_scope(callee_scope)); + + ScopeData* data = new ScopeData(scope_data()); + data->set_scope(callee_scope); + data->set_bci2block(blb.bci2block()); + data->set_continuation(continuation); + _scope_data = data; +} + + +void GraphBuilder::push_scope_for_jsr(BlockBegin* jsr_continuation, int jsr_dest_bci) { + ScopeData* data = new ScopeData(scope_data()); + data->set_parsing_jsr(); + data->set_jsr_entry_bci(jsr_dest_bci); + data->set_jsr_return_address_local(-1); + // Must clone bci2block list as we will be mutating it in order to + // properly clone all blocks in jsr region as well as exception + // handlers containing rets + BlockList* new_bci2block = new BlockList(bci2block()->length()); + new_bci2block->push_all(bci2block()); + data->set_bci2block(new_bci2block); + data->set_scope(scope()); + data->setup_jsr_xhandlers(); + data->set_continuation(continuation()); + if (continuation() != NULL) { + assert(continuation_state() != NULL, ""); + data->set_continuation_state(continuation_state()->copy()); + } + data->set_jsr_continuation(jsr_continuation); + _scope_data = data; +} + + +void GraphBuilder::pop_scope() { + int number_of_locks = scope()->number_of_locks(); + _scope_data = scope_data()->parent(); + // accumulate minimum number of monitor slots to be reserved + scope()->set_min_number_of_locks(number_of_locks); +} + + +void GraphBuilder::pop_scope_for_jsr() { + _scope_data = scope_data()->parent(); +} + +bool GraphBuilder::append_unsafe_get_obj(ciMethod* callee, BasicType t, bool is_volatile) { + if (InlineUnsafeOps) { + Values* args = state()->pop_arguments(callee->arg_size()); + null_check(args->at(0)); + Instruction* offset = args->at(2); +#ifndef _LP64 + offset = append(new Convert(Bytecodes::_l2i, offset, as_ValueType(T_INT))); +#endif + Instruction* op = append(new UnsafeGetObject(t, args->at(1), offset, is_volatile)); + push(op->type(), op); + compilation()->set_has_unsafe_access(true); + } + return InlineUnsafeOps; +} + + +bool GraphBuilder::append_unsafe_put_obj(ciMethod* callee, BasicType t, bool is_volatile) { + if (InlineUnsafeOps) { + Values* args = state()->pop_arguments(callee->arg_size()); + null_check(args->at(0)); + Instruction* offset = args->at(2); +#ifndef _LP64 + offset = append(new Convert(Bytecodes::_l2i, offset, as_ValueType(T_INT))); +#endif + Instruction* op = append(new UnsafePutObject(t, args->at(1), offset, args->at(3), is_volatile)); + compilation()->set_has_unsafe_access(true); + kill_all(); + } + return InlineUnsafeOps; +} + + +bool GraphBuilder::append_unsafe_get_raw(ciMethod* callee, BasicType t) { + if (InlineUnsafeOps) { + Values* args = state()->pop_arguments(callee->arg_size()); + null_check(args->at(0)); + Instruction* op = append(new UnsafeGetRaw(t, args->at(1), false)); + push(op->type(), op); + compilation()->set_has_unsafe_access(true); + } + return InlineUnsafeOps; +} + + +bool GraphBuilder::append_unsafe_put_raw(ciMethod* callee, BasicType t) { + if (InlineUnsafeOps) { + Values* args = state()->pop_arguments(callee->arg_size()); + null_check(args->at(0)); + Instruction* op = append(new UnsafePutRaw(t, args->at(1), args->at(2))); + compilation()->set_has_unsafe_access(true); + } + return InlineUnsafeOps; +} + + +bool GraphBuilder::append_unsafe_prefetch(ciMethod* callee, bool is_static, bool is_store) { + if (InlineUnsafeOps) { + Values* args = state()->pop_arguments(callee->arg_size()); + int obj_arg_index = 1; // Assume non-static case + if (is_static) { + obj_arg_index = 0; + } else { + null_check(args->at(0)); + } + Instruction* offset = args->at(obj_arg_index + 1); +#ifndef _LP64 + offset = append(new Convert(Bytecodes::_l2i, offset, as_ValueType(T_INT))); +#endif + Instruction* op = is_store ? append(new UnsafePrefetchWrite(args->at(obj_arg_index), offset)) + : append(new UnsafePrefetchRead (args->at(obj_arg_index), offset)); + compilation()->set_has_unsafe_access(true); + } + return InlineUnsafeOps; +} + + +void GraphBuilder::append_unsafe_CAS(ciMethod* callee) { + ValueType* result_type = as_ValueType(callee->return_type()); + assert(result_type->is_int(), "int result"); + Values* args = state()->pop_arguments(callee->arg_size()); + + // Pop off some args to speically handle, then push back + Value newval = args->pop(); + Value cmpval = args->pop(); + Value offset = args->pop(); + Value src = args->pop(); + Value unsafe_obj = args->pop(); + + // Separately handle the unsafe arg. It is not needed for code + // generation, but must be null checked + null_check(unsafe_obj); + +#ifndef _LP64 + offset = append(new Convert(Bytecodes::_l2i, offset, as_ValueType(T_INT))); +#endif + + args->push(src); + args->push(offset); + args->push(cmpval); + args->push(newval); + + // An unsafe CAS can alias with other field accesses, but we don't + // know which ones so mark the state as no preserved. This will + // cause CSE to invalidate memory across it. + bool preserves_state = false; + Intrinsic* result = new Intrinsic(result_type, callee->intrinsic_id(), args, false, lock_stack(), preserves_state); + append_split(result); + push(result_type, result); + compilation()->set_has_unsafe_access(true); +} + + +#ifndef PRODUCT +void GraphBuilder::print_inline_result(ciMethod* callee, bool res) { + const char sync_char = callee->is_synchronized() ? 's' : ' '; + const char exception_char = callee->has_exception_handlers() ? '!' : ' '; + const char monitors_char = callee->has_monitor_bytecodes() ? 'm' : ' '; + tty->print(" %c%c%c ", sync_char, exception_char, monitors_char); + for (int i = 0; i < scope()->level(); i++) tty->print(" "); + if (res) { + tty->print(" "); + } else { + tty->print("- "); + } + tty->print("@ %d ", bci()); + callee->print_short_name(); + tty->print(" (%d bytes)", callee->code_size()); + if (_inline_bailout_msg) { + tty->print(" %s", _inline_bailout_msg); + } + tty->cr(); + + if (res && CIPrintMethodCodes) { + callee->print_codes(); + } +} + + +void GraphBuilder::print_stats() { + vmap()->print(); +} +#endif // PRODUCT + + +void GraphBuilder::profile_call(Value recv, ciKlass* known_holder) { + append(new ProfileCall(method(), bci(), recv, known_holder)); +} + + +void GraphBuilder::profile_invocation(ciMethod* callee) { + if (profile_calls()) { + // increment the interpreter_invocation_count for the inlinee + Value m = append(new Constant(new ObjectConstant(callee))); + append(new ProfileCounter(m, methodOopDesc::interpreter_invocation_counter_offset_in_bytes(), 1)); + } +} + + +void GraphBuilder::profile_bci(int bci) { + if (profile_branches()) { + ciMethodData* md = method()->method_data(); + if (md == NULL) { + BAILOUT("out of memory building methodDataOop"); + } + ciProfileData* data = md->bci_to_data(bci); + assert(data != NULL && data->is_JumpData(), "need JumpData for goto"); + Value mdo = append(new Constant(new ObjectConstant(md))); + append(new ProfileCounter(mdo, md->byte_offset_of_slot(data, JumpData::taken_offset()), 1)); + } +} diff --git a/hotspot/src/share/vm/c1/c1_GraphBuilder.hpp b/hotspot/src/share/vm/c1/c1_GraphBuilder.hpp new file mode 100644 index 00000000000..46786748b5e --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.hpp @@ -0,0 +1,390 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class MemoryBuffer; + +class GraphBuilder VALUE_OBJ_CLASS_SPEC { + private: + // Per-scope data. These are pushed and popped as we descend into + // inlined methods. Currently in order to generate good code in the + // inliner we have to attempt to inline methods directly into the + // basic block we are parsing; this adds complexity. + class ScopeData: public CompilationResourceObj { + private: + ScopeData* _parent; + // bci-to-block mapping + BlockList* _bci2block; + // Scope + IRScope* _scope; + // Whether this scope or any parent scope has exception handlers + bool _has_handler; + // The bytecodes + ciBytecodeStream* _stream; + + // Work list + BlockList* _work_list; + + // Maximum inline size for this scope + intx _max_inline_size; + // Expression stack depth at point where inline occurred + int _caller_stack_size; + + // The continuation point for the inline. Currently only used in + // multi-block inlines, but eventually would like to use this for + // all inlines for uniformity and simplicity; in this case would + // get the continuation point from the BlockList instead of + // fabricating it anew because Invokes would be considered to be + // BlockEnds. + BlockBegin* _continuation; + + // Without return value of inlined method on stack + ValueStack* _continuation_state; + + // Was this ScopeData created only for the parsing and inlining of + // a jsr? + bool _parsing_jsr; + // We track the destination bci of the jsr only to determine + // bailout conditions, since we only handle a subset of all of the + // possible jsr-ret control structures. Recursive invocations of a + // jsr are disallowed by the verifier. + int _jsr_entry_bci; + // We need to track the local variable in which the return address + // was stored to ensure we can handle inlining the jsr, because we + // don't handle arbitrary jsr/ret constructs. + int _jsr_ret_addr_local; + // If we are parsing a jsr, the continuation point for rets + BlockBegin* _jsr_continuation; + // Cloned XHandlers for jsr-related ScopeDatas + XHandlers* _jsr_xhandlers; + + // Number of returns seen in this scope + int _num_returns; + + // In order to generate profitable code for inlining, we currently + // have to perform an optimization for single-block inlined + // methods where we continue parsing into the same block. This + // allows us to perform CSE across inlined scopes and to avoid + // storing parameters to the stack. Having a global register + // allocator and being able to perform global CSE would allow this + // code to be removed and thereby simplify the inliner. + BlockBegin* _cleanup_block; // The block to which the return was added + Instruction* _cleanup_return_prev; // Instruction before return instruction + ValueStack* _cleanup_state; // State of that block (not yet pinned) + + public: + ScopeData(ScopeData* parent); + + ScopeData* parent() const { return _parent; } + + BlockList* bci2block() const { return _bci2block; } + void set_bci2block(BlockList* bci2block) { _bci2block = bci2block; } + + // NOTE: this has a different effect when parsing jsrs + BlockBegin* block_at(int bci); + + IRScope* scope() const { return _scope; } + // Has side-effect of setting has_handler flag + void set_scope(IRScope* scope); + + // Whether this or any parent scope has exception handlers + bool has_handler() const { return _has_handler; } + void set_has_handler() { _has_handler = true; } + + // Exception handlers list to be used for this scope + XHandlers* xhandlers() const; + + // How to get a block to be parsed + void add_to_work_list(BlockBegin* block); + // How to remove the next block to be parsed; returns NULL if none left + BlockBegin* remove_from_work_list(); + // Indicates parse is over + bool is_work_list_empty() const; + + ciBytecodeStream* stream() { return _stream; } + void set_stream(ciBytecodeStream* stream) { _stream = stream; } + + intx max_inline_size() const { return _max_inline_size; } + int caller_stack_size() const; + + BlockBegin* continuation() const { return _continuation; } + void set_continuation(BlockBegin* cont) { _continuation = cont; } + + ValueStack* continuation_state() const { return _continuation_state; } + void set_continuation_state(ValueStack* s) { _continuation_state = s; } + + // Indicates whether this ScopeData was pushed only for the + // parsing and inlining of a jsr + bool parsing_jsr() const { return _parsing_jsr; } + void set_parsing_jsr() { _parsing_jsr = true; } + int jsr_entry_bci() const { return _jsr_entry_bci; } + void set_jsr_entry_bci(int bci) { _jsr_entry_bci = bci; } + void set_jsr_return_address_local(int local_no){ _jsr_ret_addr_local = local_no; } + int jsr_return_address_local() const { return _jsr_ret_addr_local; } + // Must be called after scope is set up for jsr ScopeData + void setup_jsr_xhandlers(); + + // The jsr continuation is only used when parsing_jsr is true, and + // is different from the "normal" continuation since we can end up + // doing a return (rather than a ret) from within a subroutine + BlockBegin* jsr_continuation() const { return _jsr_continuation; } + void set_jsr_continuation(BlockBegin* cont) { _jsr_continuation = cont; } + + int num_returns(); + void incr_num_returns(); + + void set_inline_cleanup_info(BlockBegin* block, + Instruction* return_prev, + ValueStack* return_state); + BlockBegin* inline_cleanup_block() const { return _cleanup_block; } + Instruction* inline_cleanup_return_prev() const{ return _cleanup_return_prev; } + ValueStack* inline_cleanup_state() const { return _cleanup_state; } + }; + + // for all GraphBuilders + static bool _is_initialized; // true if trap tables were initialized, false otherwise + static bool _can_trap[Bytecodes::number_of_java_codes]; + static bool _is_async[Bytecodes::number_of_java_codes]; + + // for each instance of GraphBuilder + ScopeData* _scope_data; // Per-scope data; used for inlining + Compilation* _compilation; // the current compilation + ValueMap* _vmap; // the map of values encountered (for CSE) + MemoryBuffer* _memory; + const char* _inline_bailout_msg; // non-null if most recent inline attempt failed + int _instruction_count; // for bailing out in pathological jsr/ret cases + BlockBegin* _start; // the start block + BlockBegin* _osr_entry; // the osr entry block block + ValueStack* _initial_state; // The state for the start block + + // for each call to connect_to_end; can also be set by inliner + BlockBegin* _block; // the current block + ValueStack* _state; // the current execution state + ValueStack* _exception_state; // state that will be used by handle_exception + Instruction* _last; // the last instruction added + bool _skip_block; // skip processing of the rest of this block + + // accessors + ScopeData* scope_data() const { return _scope_data; } + Compilation* compilation() const { return _compilation; } + BlockList* bci2block() const { return scope_data()->bci2block(); } + ValueMap* vmap() const { assert(UseLocalValueNumbering, "should not access otherwise"); return _vmap; } + bool has_handler() const { return scope_data()->has_handler(); } + + BlockBegin* block() const { return _block; } + ValueStack* state() const { return _state; } + void set_state(ValueStack* state) { _state = state; } + IRScope* scope() const { return scope_data()->scope(); } + ValueStack* exception_state() const { return _exception_state; } + void set_exception_state(ValueStack* s) { _exception_state = s; } + ciMethod* method() const { return scope()->method(); } + ciBytecodeStream* stream() const { return scope_data()->stream(); } + Instruction* last() const { return _last; } + Bytecodes::Code code() const { return stream()->cur_bc(); } + int bci() const { return stream()->cur_bci(); } + int next_bci() const { return stream()->next_bci(); } + + // unified bailout support + void bailout(const char* msg) const { compilation()->bailout(msg); } + bool bailed_out() const { return compilation()->bailed_out(); } + + // stack manipulation helpers + void ipush(Value t) const { state()->ipush(t); } + void lpush(Value t) const { state()->lpush(t); } + void fpush(Value t) const { state()->fpush(t); } + void dpush(Value t) const { state()->dpush(t); } + void apush(Value t) const { state()->apush(t); } + void push(ValueType* type, Value t) const { state()-> push(type, t); } + + Value ipop() { return state()->ipop(); } + Value lpop() { return state()->lpop(); } + Value fpop() { return state()->fpop(); } + Value dpop() { return state()->dpop(); } + Value apop() { return state()->apop(); } + Value pop(ValueType* type) { return state()-> pop(type); } + + // instruction helpers + void load_constant(); + void load_local(ValueType* type, int index); + void store_local(ValueType* type, int index); + void store_local(ValueStack* state, Value value, ValueType* type, int index); + void load_indexed (BasicType type); + void store_indexed(BasicType type); + void stack_op(Bytecodes::Code code); + void arithmetic_op(ValueType* type, Bytecodes::Code code, ValueStack* lock_stack = NULL); + void negate_op(ValueType* type); + void shift_op(ValueType* type, Bytecodes::Code code); + void logic_op(ValueType* type, Bytecodes::Code code); + void compare_op(ValueType* type, Bytecodes::Code code); + void convert(Bytecodes::Code op, BasicType from, BasicType to); + void increment(); + void _goto(int from_bci, int to_bci); + void if_node(Value x, If::Condition cond, Value y, ValueStack* stack_before); + void if_zero(ValueType* type, If::Condition cond); + void if_null(ValueType* type, If::Condition cond); + void if_same(ValueType* type, If::Condition cond); + void jsr(int dest); + void ret(int local_index); + void table_switch(); + void lookup_switch(); + void method_return(Value x); + void call_register_finalizer(); + void access_field(Bytecodes::Code code); + void invoke(Bytecodes::Code code); + void new_instance(int klass_index); + void new_type_array(); + void new_object_array(); + void check_cast(int klass_index); + void instance_of(int klass_index); + void monitorenter(Value x, int bci); + void monitorexit(Value x, int bci); + void new_multi_array(int dimensions); + void throw_op(int bci); + Value round_fp(Value fp_value); + + // stack/code manipulation helpers + Instruction* append_with_bci(Instruction* instr, int bci); + Instruction* append(Instruction* instr); + Instruction* append_split(StateSplit* instr); + + // other helpers + static bool is_initialized() { return _is_initialized; } + static bool is_async(Bytecodes::Code code) { + assert(0 <= code && code < Bytecodes::number_of_java_codes, "illegal bytecode"); + return _is_async[code]; + } + BlockBegin* block_at(int bci) { return scope_data()->block_at(bci); } + XHandlers* handle_exception(int bci); + void connect_to_end(BlockBegin* beg); + void null_check(Value value); + void eliminate_redundant_phis(BlockBegin* start); + BlockEnd* iterate_bytecodes_for_block(int bci); + void iterate_all_blocks(bool start_in_current_block_for_inlining = false); + Dependencies* dependency_recorder() const; // = compilation()->dependencies() + bool direct_compare(ciKlass* k); + + void kill_field(ciField* field); + void kill_array(Value value); + void kill_all(); + + ValueStack* lock_stack(); + + // + // Inlining support + // + + // accessors + bool parsing_jsr() const { return scope_data()->parsing_jsr(); } + BlockBegin* continuation() const { return scope_data()->continuation(); } + ValueStack* continuation_state() const { return scope_data()->continuation_state(); } + BlockBegin* jsr_continuation() const { return scope_data()->jsr_continuation(); } + int caller_stack_size() const { return scope_data()->caller_stack_size(); } + void set_continuation(BlockBegin* continuation) { scope_data()->set_continuation(continuation); } + void set_inline_cleanup_info(BlockBegin* block, + Instruction* return_prev, + ValueStack* return_state) { scope_data()->set_inline_cleanup_info(block, + return_prev, + return_state); } + BlockBegin* inline_cleanup_block() const { return scope_data()->inline_cleanup_block(); } + Instruction* inline_cleanup_return_prev() const { return scope_data()->inline_cleanup_return_prev(); } + ValueStack* inline_cleanup_state() const { return scope_data()->inline_cleanup_state(); } + void incr_num_returns() { scope_data()->incr_num_returns(); } + int num_returns() const { return scope_data()->num_returns(); } + intx max_inline_size() const { return scope_data()->max_inline_size(); } + int inline_level() const { return scope()->level(); } + int recursive_inline_level(ciMethod* callee) const; + + // inlining of synchronized methods + void inline_sync_entry(Value lock, BlockBegin* sync_handler); + void fill_sync_handler(Value lock, BlockBegin* sync_handler, bool default_handler = false); + + // inliners + bool try_inline(ciMethod* callee, bool holder_known); + bool try_inline_intrinsics(ciMethod* callee); + bool try_inline_full (ciMethod* callee, bool holder_known); + bool try_inline_jsr(int jsr_dest_bci); + + // helpers + void inline_bailout(const char* msg); + BlockBegin* header_block(BlockBegin* entry, BlockBegin::Flag f, ValueStack* state); + BlockBegin* setup_start_block(int osr_bci, BlockBegin* std_entry, BlockBegin* osr_entry, ValueStack* init_state); + void setup_osr_entry_block(); + void clear_inline_bailout(); + ValueStack* state_at_entry(); + void push_root_scope(IRScope* scope, BlockList* bci2block, BlockBegin* start); + void push_scope(ciMethod* callee, BlockBegin* continuation); + void push_scope_for_jsr(BlockBegin* jsr_continuation, int jsr_dest_bci); + void pop_scope(); + void pop_scope_for_jsr(); + + bool append_unsafe_get_obj(ciMethod* callee, BasicType t, bool is_volatile); + bool append_unsafe_put_obj(ciMethod* callee, BasicType t, bool is_volatile); + bool append_unsafe_get_raw(ciMethod* callee, BasicType t); + bool append_unsafe_put_raw(ciMethod* callee, BasicType t); + bool append_unsafe_prefetch(ciMethod* callee, bool is_store, bool is_static); + void append_unsafe_CAS(ciMethod* callee); + + NOT_PRODUCT(void print_inline_result(ciMethod* callee, bool res);) + + // methodDataOop profiling helpers + void profile_call(Value recv, ciKlass* predicted_holder); + void profile_invocation(ciMethod* method); + void profile_bci(int bci); + + // Helpers for generation of profile information + bool profile_branches() { + return _compilation->env()->comp_level() == CompLevel_fast_compile && + Tier1UpdateMethodData && Tier1ProfileBranches; + } + bool profile_calls() { + return _compilation->env()->comp_level() == CompLevel_fast_compile && + Tier1UpdateMethodData && Tier1ProfileCalls; + } + bool profile_inlined_calls() { + return profile_calls() && Tier1ProfileInlinedCalls; + } + bool profile_checkcasts() { + return _compilation->env()->comp_level() == CompLevel_fast_compile && + Tier1UpdateMethodData && Tier1ProfileCheckcasts; + } + + public: + NOT_PRODUCT(void print_stats();) + + // initialization + static void initialize(); + + // public + static bool can_trap(ciMethod* method, Bytecodes::Code code) { + assert(0 <= code && code < Bytecodes::number_of_java_codes, "illegal bytecode"); + if (_can_trap[code]) return true; + // special handling for finalizer registration + return code == Bytecodes::_return && method->intrinsic_id() == vmIntrinsics::_Object_init; + } + + // creation + GraphBuilder(Compilation* compilation, IRScope* scope); + static void sort_top_into_worklist(BlockList* worklist, BlockBegin* top); + + BlockBegin* start() const { return _start; } +}; diff --git a/hotspot/src/share/vm/c1/c1_IR.cpp b/hotspot/src/share/vm/c1/c1_IR.cpp new file mode 100644 index 00000000000..005114b46e2 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_IR.cpp @@ -0,0 +1,1323 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_IR.cpp.incl" + + +// Implementation of XHandlers +// +// Note: This code could eventually go away if we are +// just using the ciExceptionHandlerStream. + +XHandlers::XHandlers(ciMethod* method) : _list(method->exception_table_length()) { + ciExceptionHandlerStream s(method); + while (!s.is_done()) { + _list.append(new XHandler(s.handler())); + s.next(); + } + assert(s.count() == method->exception_table_length(), "exception table lengths inconsistent"); +} + +// deep copy of all XHandler contained in list +XHandlers::XHandlers(XHandlers* other) : + _list(other->length()) +{ + for (int i = 0; i < other->length(); i++) { + _list.append(new XHandler(other->handler_at(i))); + } +} + +// Returns whether a particular exception type can be caught. Also +// returns true if klass is unloaded or any exception handler +// classes are unloaded. type_is_exact indicates whether the throw +// is known to be exactly that class or it might throw a subtype. +bool XHandlers::could_catch(ciInstanceKlass* klass, bool type_is_exact) const { + // the type is unknown so be conservative + if (!klass->is_loaded()) { + return true; + } + + for (int i = 0; i < length(); i++) { + XHandler* handler = handler_at(i); + if (handler->is_catch_all()) { + // catch of ANY + return true; + } + ciInstanceKlass* handler_klass = handler->catch_klass(); + // if it's unknown it might be catchable + if (!handler_klass->is_loaded()) { + return true; + } + // if the throw type is definitely a subtype of the catch type + // then it can be caught. + if (klass->is_subtype_of(handler_klass)) { + return true; + } + if (!type_is_exact) { + // If the type isn't exactly known then it can also be caught by + // catch statements where the inexact type is a subtype of the + // catch type. + // given: foo extends bar extends Exception + // throw bar can be caught by catch foo, catch bar, and catch + // Exception, however it can't be caught by any handlers without + // bar in its type hierarchy. + if (handler_klass->is_subtype_of(klass)) { + return true; + } + } + } + + return false; +} + + +bool XHandlers::equals(XHandlers* others) const { + if (others == NULL) return false; + if (length() != others->length()) return false; + + for (int i = 0; i < length(); i++) { + if (!handler_at(i)->equals(others->handler_at(i))) return false; + } + return true; +} + +bool XHandler::equals(XHandler* other) const { + assert(entry_pco() != -1 && other->entry_pco() != -1, "must have entry_pco"); + + if (entry_pco() != other->entry_pco()) return false; + if (scope_count() != other->scope_count()) return false; + if (_desc != other->_desc) return false; + + assert(entry_block() == other->entry_block(), "entry_block must be equal when entry_pco is equal"); + return true; +} + + +// Implementation of IRScope + +BlockBegin* IRScope::header_block(BlockBegin* entry, BlockBegin::Flag f, ValueStack* state) { + if (entry == NULL) return NULL; + assert(entry->is_set(f), "entry/flag mismatch"); + // create header block + BlockBegin* h = new BlockBegin(entry->bci()); + BlockEnd* g = new Goto(entry, false); + h->set_next(g, entry->bci()); + h->set_end(g); + h->set(f); + // setup header block end state + ValueStack* s = state->copy(); // can use copy since stack is empty (=> no phis) + assert(s->stack_is_empty(), "must have empty stack at entry point"); + g->set_state(s); + return h; +} + + +BlockBegin* IRScope::build_graph(Compilation* compilation, int osr_bci) { + GraphBuilder gm(compilation, this); + NOT_PRODUCT(if (PrintValueNumbering && Verbose) gm.print_stats()); + if (compilation->bailed_out()) return NULL; + return gm.start(); +} + + +IRScope::IRScope(Compilation* compilation, IRScope* caller, int caller_bci, ciMethod* method, int osr_bci, bool create_graph) +: _callees(2) +, _compilation(compilation) +, _lock_stack_size(-1) +, _requires_phi_function(method->max_locals()) +{ + _caller = caller; + _caller_bci = caller == NULL ? -1 : caller_bci; + _caller_state = NULL; // Must be set later if needed + _level = caller == NULL ? 0 : caller->level() + 1; + _method = method; + _xhandlers = new XHandlers(method); + _number_of_locks = 0; + _monitor_pairing_ok = method->has_balanced_monitors(); + _start = NULL; + + if (osr_bci == -1) { + _requires_phi_function.clear(); + } else { + // selective creation of phi functions is not possibel in osr-methods + _requires_phi_function.set_range(0, method->max_locals()); + } + + assert(method->holder()->is_loaded() , "method holder must be loaded"); + + // build graph if monitor pairing is ok + if (create_graph && monitor_pairing_ok()) _start = build_graph(compilation, osr_bci); +} + + +int IRScope::max_stack() const { + int my_max = method()->max_stack(); + int callee_max = 0; + for (int i = 0; i < number_of_callees(); i++) { + callee_max = MAX2(callee_max, callee_no(i)->max_stack()); + } + return my_max + callee_max; +} + + +void IRScope::compute_lock_stack_size() { + if (!InlineMethodsWithExceptionHandlers) { + _lock_stack_size = 0; + return; + } + + // Figure out whether we have to preserve expression stack elements + // for parent scopes, and if so, how many + IRScope* cur_scope = this; + while (cur_scope != NULL && !cur_scope->xhandlers()->has_handlers()) { + cur_scope = cur_scope->caller(); + } + _lock_stack_size = (cur_scope == NULL ? 0 : + (cur_scope->caller_state() == NULL ? 0 : + cur_scope->caller_state()->stack_size())); +} + +int IRScope::top_scope_bci() const { + assert(!is_top_scope(), "no correct answer for top scope possible"); + const IRScope* scope = this; + while (!scope->caller()->is_top_scope()) { + scope = scope->caller(); + } + return scope->caller_bci(); +} + + + +// Implementation of CodeEmitInfo + +// Stack must be NON-null +CodeEmitInfo::CodeEmitInfo(int bci, ValueStack* stack, XHandlers* exception_handlers) + : _scope(stack->scope()) + , _bci(bci) + , _scope_debug_info(NULL) + , _oop_map(NULL) + , _stack(stack) + , _exception_handlers(exception_handlers) + , _next(NULL) + , _id(-1) { + assert(_stack != NULL, "must be non null"); + assert(_bci == SynchronizationEntryBCI || Bytecodes::is_defined(scope()->method()->java_code_at_bci(_bci)), "make sure bci points at a real bytecode"); +} + + +CodeEmitInfo::CodeEmitInfo(CodeEmitInfo* info, bool lock_stack_only) + : _scope(info->_scope) + , _exception_handlers(NULL) + , _bci(info->_bci) + , _scope_debug_info(NULL) + , _oop_map(NULL) { + if (lock_stack_only) { + if (info->_stack != NULL) { + _stack = info->_stack->copy_locks(); + } else { + _stack = NULL; + } + } else { + _stack = info->_stack; + } + + // deep copy of exception handlers + if (info->_exception_handlers != NULL) { + _exception_handlers = new XHandlers(info->_exception_handlers); + } +} + + +void CodeEmitInfo::record_debug_info(DebugInformationRecorder* recorder, int pc_offset) { + // record the safepoint before recording the debug info for enclosing scopes + recorder->add_safepoint(pc_offset, _oop_map->deep_copy()); + _scope_debug_info->record_debug_info(recorder, pc_offset); + recorder->end_safepoint(pc_offset); +} + + +void CodeEmitInfo::add_register_oop(LIR_Opr opr) { + assert(_oop_map != NULL, "oop map must already exist"); + assert(opr->is_single_cpu(), "should not call otherwise"); + + int frame_size = frame_map()->framesize(); + int arg_count = frame_map()->oop_map_arg_count(); + VMReg name = frame_map()->regname(opr); + _oop_map->set_oop(name); +} + + + + +// Implementation of IR + +IR::IR(Compilation* compilation, ciMethod* method, int osr_bci) : + _locals_size(in_WordSize(-1)) + , _num_loops(0) { + // initialize data structures + ValueType::initialize(); + Instruction::initialize(); + BlockBegin::initialize(); + GraphBuilder::initialize(); + // setup IR fields + _compilation = compilation; + _top_scope = new IRScope(compilation, NULL, -1, method, osr_bci, true); + _code = NULL; +} + + +void IR::optimize() { + Optimizer opt(this); + if (DoCEE) { + opt.eliminate_conditional_expressions(); +#ifndef PRODUCT + if (PrintCFG || PrintCFG1) { tty->print_cr("CFG after CEE"); print(true); } + if (PrintIR || PrintIR1 ) { tty->print_cr("IR after CEE"); print(false); } +#endif + } + if (EliminateBlocks) { + opt.eliminate_blocks(); +#ifndef PRODUCT + if (PrintCFG || PrintCFG1) { tty->print_cr("CFG after block elimination"); print(true); } + if (PrintIR || PrintIR1 ) { tty->print_cr("IR after block elimination"); print(false); } +#endif + } + if (EliminateNullChecks) { + opt.eliminate_null_checks(); +#ifndef PRODUCT + if (PrintCFG || PrintCFG1) { tty->print_cr("CFG after null check elimination"); print(true); } + if (PrintIR || PrintIR1 ) { tty->print_cr("IR after null check elimination"); print(false); } +#endif + } +} + + +static int sort_pairs(BlockPair** a, BlockPair** b) { + if ((*a)->from() == (*b)->from()) { + return (*a)->to()->block_id() - (*b)->to()->block_id(); + } else { + return (*a)->from()->block_id() - (*b)->from()->block_id(); + } +} + + +class CriticalEdgeFinder: public BlockClosure { + BlockPairList blocks; + IR* _ir; + + public: + CriticalEdgeFinder(IR* ir): _ir(ir) {} + void block_do(BlockBegin* bb) { + BlockEnd* be = bb->end(); + int nos = be->number_of_sux(); + if (nos >= 2) { + for (int i = 0; i < nos; i++) { + BlockBegin* sux = be->sux_at(i); + if (sux->number_of_preds() >= 2) { + blocks.append(new BlockPair(bb, sux)); + } + } + } + } + + void split_edges() { + BlockPair* last_pair = NULL; + blocks.sort(sort_pairs); + for (int i = 0; i < blocks.length(); i++) { + BlockPair* pair = blocks.at(i); + if (last_pair != NULL && pair->is_same(last_pair)) continue; + BlockBegin* from = pair->from(); + BlockBegin* to = pair->to(); + BlockBegin* split = from->insert_block_between(to); +#ifndef PRODUCT + if ((PrintIR || PrintIR1) && Verbose) { + tty->print_cr("Split critical edge B%d -> B%d (new block B%d)", + from->block_id(), to->block_id(), split->block_id()); + } +#endif + last_pair = pair; + } + } +}; + +void IR::split_critical_edges() { + CriticalEdgeFinder cef(this); + + iterate_preorder(&cef); + cef.split_edges(); +} + + +class UseCountComputer: public AllStatic { + private: + static void update_use_count(Value* n) { + // Local instructions and Phis for expression stack values at the + // start of basic blocks are not added to the instruction list + if ((*n)->bci() == -99 && (*n)->as_Local() == NULL && + (*n)->as_Phi() == NULL) { + assert(false, "a node was not appended to the graph"); + Compilation::current_compilation()->bailout("a node was not appended to the graph"); + } + // use n's input if not visited before + if (!(*n)->is_pinned() && !(*n)->has_uses()) { + // note: a) if the instruction is pinned, it will be handled by compute_use_count + // b) if the instruction has uses, it was touched before + // => in both cases we don't need to update n's values + uses_do(n); + } + // use n + (*n)->_use_count++; + } + + static Values* worklist; + static int depth; + enum { + max_recurse_depth = 20 + }; + + static void uses_do(Value* n) { + depth++; + if (depth > max_recurse_depth) { + // don't allow the traversal to recurse too deeply + worklist->push(*n); + } else { + (*n)->input_values_do(update_use_count); + // special handling for some instructions + if ((*n)->as_BlockEnd() != NULL) { + // note on BlockEnd: + // must 'use' the stack only if the method doesn't + // terminate, however, in those cases stack is empty + (*n)->state_values_do(update_use_count); + } + } + depth--; + } + + static void basic_compute_use_count(BlockBegin* b) { + depth = 0; + // process all pinned nodes as the roots of expression trees + for (Instruction* n = b; n != NULL; n = n->next()) { + if (n->is_pinned()) uses_do(&n); + } + assert(depth == 0, "should have counted back down"); + + // now process any unpinned nodes which recursed too deeply + while (worklist->length() > 0) { + Value t = worklist->pop(); + if (!t->is_pinned()) { + // compute the use count + uses_do(&t); + + // pin the instruction so that LIRGenerator doesn't recurse + // too deeply during it's evaluation. + t->pin(); + } + } + assert(depth == 0, "should have counted back down"); + } + + public: + static void compute(BlockList* blocks) { + worklist = new Values(); + blocks->blocks_do(basic_compute_use_count); + worklist = NULL; + } +}; + + +Values* UseCountComputer::worklist = NULL; +int UseCountComputer::depth = 0; + +// helper macro for short definition of trace-output inside code +#ifndef PRODUCT + #define TRACE_LINEAR_SCAN(level, code) \ + if (TraceLinearScanLevel >= level) { \ + code; \ + } +#else + #define TRACE_LINEAR_SCAN(level, code) +#endif + +class ComputeLinearScanOrder : public StackObj { + private: + int _max_block_id; // the highest block_id of a block + int _num_blocks; // total number of blocks (smaller than _max_block_id) + int _num_loops; // total number of loops + bool _iterative_dominators;// method requires iterative computation of dominatiors + + BlockList* _linear_scan_order; // the resulting list of blocks in correct order + + BitMap _visited_blocks; // used for recursive processing of blocks + BitMap _active_blocks; // used for recursive processing of blocks + BitMap _dominator_blocks; // temproary BitMap used for computation of dominator + intArray _forward_branches; // number of incoming forward branches for each block + BlockList _loop_end_blocks; // list of all loop end blocks collected during count_edges + BitMap2D _loop_map; // two-dimensional bit set: a bit is set if a block is contained in a loop + BlockList _work_list; // temporary list (used in mark_loops and compute_order) + + // accessors for _visited_blocks and _active_blocks + void init_visited() { _active_blocks.clear(); _visited_blocks.clear(); } + bool is_visited(BlockBegin* b) const { return _visited_blocks.at(b->block_id()); } + bool is_active(BlockBegin* b) const { return _active_blocks.at(b->block_id()); } + void set_visited(BlockBegin* b) { assert(!is_visited(b), "already set"); _visited_blocks.set_bit(b->block_id()); } + void set_active(BlockBegin* b) { assert(!is_active(b), "already set"); _active_blocks.set_bit(b->block_id()); } + void clear_active(BlockBegin* b) { assert(is_active(b), "not already"); _active_blocks.clear_bit(b->block_id()); } + + // accessors for _forward_branches + void inc_forward_branches(BlockBegin* b) { _forward_branches.at_put(b->block_id(), _forward_branches.at(b->block_id()) + 1); } + int dec_forward_branches(BlockBegin* b) { _forward_branches.at_put(b->block_id(), _forward_branches.at(b->block_id()) - 1); return _forward_branches.at(b->block_id()); } + + // accessors for _loop_map + bool is_block_in_loop (int loop_idx, BlockBegin* b) const { return _loop_map.at(loop_idx, b->block_id()); } + void set_block_in_loop (int loop_idx, BlockBegin* b) { _loop_map.set_bit(loop_idx, b->block_id()); } + void clear_block_in_loop(int loop_idx, int block_id) { _loop_map.clear_bit(loop_idx, block_id); } + + // count edges between blocks + void count_edges(BlockBegin* cur, BlockBegin* parent); + + // loop detection + void mark_loops(); + void clear_non_natural_loops(BlockBegin* start_block); + void assign_loop_depth(BlockBegin* start_block); + + // computation of final block order + BlockBegin* common_dominator(BlockBegin* a, BlockBegin* b); + void compute_dominator(BlockBegin* cur, BlockBegin* parent); + int compute_weight(BlockBegin* cur); + bool ready_for_processing(BlockBegin* cur); + void sort_into_work_list(BlockBegin* b); + void append_block(BlockBegin* cur); + void compute_order(BlockBegin* start_block); + + // fixup of dominators for non-natural loops + bool compute_dominators_iter(); + void compute_dominators(); + + // debug functions + NOT_PRODUCT(void print_blocks();) + DEBUG_ONLY(void verify();) + + public: + ComputeLinearScanOrder(BlockBegin* start_block); + + // accessors for final result + BlockList* linear_scan_order() const { return _linear_scan_order; } + int num_loops() const { return _num_loops; } +}; + + +ComputeLinearScanOrder::ComputeLinearScanOrder(BlockBegin* start_block) : + _max_block_id(BlockBegin::number_of_blocks()), + _num_blocks(0), + _num_loops(0), + _iterative_dominators(false), + _visited_blocks(_max_block_id), + _active_blocks(_max_block_id), + _dominator_blocks(_max_block_id), + _forward_branches(_max_block_id, 0), + _loop_end_blocks(8), + _work_list(8), + _linear_scan_order(NULL), // initialized later with correct size + _loop_map(0, 0) // initialized later with correct size +{ + TRACE_LINEAR_SCAN(2, "***** computing linear-scan block order"); + + init_visited(); + count_edges(start_block, NULL); + + if (_num_loops > 0) { + mark_loops(); + clear_non_natural_loops(start_block); + assign_loop_depth(start_block); + } + + compute_order(start_block); + compute_dominators(); + + NOT_PRODUCT(print_blocks()); + DEBUG_ONLY(verify()); +} + + +// Traverse the CFG: +// * count total number of blocks +// * count all incoming edges and backward incoming edges +// * number loop header blocks +// * create a list with all loop end blocks +void ComputeLinearScanOrder::count_edges(BlockBegin* cur, BlockBegin* parent) { + TRACE_LINEAR_SCAN(3, tty->print_cr("Enter count_edges for block B%d coming from B%d", cur->block_id(), parent != NULL ? parent->block_id() : -1)); + assert(cur->dominator() == NULL, "dominator already initialized"); + + if (is_active(cur)) { + TRACE_LINEAR_SCAN(3, tty->print_cr("backward branch")); + assert(is_visited(cur), "block must be visisted when block is active"); + assert(parent != NULL, "must have parent"); + assert(parent->number_of_sux() == 1, "loop end blocks must have one successor (critical edges are split)"); + + cur->set(BlockBegin::linear_scan_loop_header_flag); + cur->set(BlockBegin::backward_branch_target_flag); + + parent->set(BlockBegin::linear_scan_loop_end_flag); + _loop_end_blocks.append(parent); + return; + } + + // increment number of incoming forward branches + inc_forward_branches(cur); + + if (is_visited(cur)) { + TRACE_LINEAR_SCAN(3, tty->print_cr("block already visited")); + return; + } + + _num_blocks++; + set_visited(cur); + set_active(cur); + + // recursive call for all successors + int i; + for (i = cur->number_of_sux() - 1; i >= 0; i--) { + count_edges(cur->sux_at(i), cur); + } + for (i = cur->number_of_exception_handlers() - 1; i >= 0; i--) { + count_edges(cur->exception_handler_at(i), cur); + } + + clear_active(cur); + + // Each loop has a unique number. + // When multiple loops are nested, assign_loop_depth assumes that the + // innermost loop has the lowest number. This is guaranteed by setting + // the loop number after the recursive calls for the successors above + // have returned. + if (cur->is_set(BlockBegin::linear_scan_loop_header_flag)) { + assert(cur->loop_index() == -1, "cannot set loop-index twice"); + TRACE_LINEAR_SCAN(3, tty->print_cr("Block B%d is loop header of loop %d", cur->block_id(), _num_loops)); + + cur->set_loop_index(_num_loops); + _num_loops++; + } + + TRACE_LINEAR_SCAN(3, tty->print_cr("Finished count_edges for block B%d", cur->block_id())); +} + + +void ComputeLinearScanOrder::mark_loops() { + TRACE_LINEAR_SCAN(3, tty->print_cr("----- marking loops")); + + _loop_map = BitMap2D(_num_loops, _max_block_id); + _loop_map.clear(); + + for (int i = _loop_end_blocks.length() - 1; i >= 0; i--) { + BlockBegin* loop_end = _loop_end_blocks.at(i); + BlockBegin* loop_start = loop_end->sux_at(0); + int loop_idx = loop_start->loop_index(); + + TRACE_LINEAR_SCAN(3, tty->print_cr("Processing loop from B%d to B%d (loop %d):", loop_start->block_id(), loop_end->block_id(), loop_idx)); + assert(loop_end->is_set(BlockBegin::linear_scan_loop_end_flag), "loop end flag must be set"); + assert(loop_end->number_of_sux() == 1, "incorrect number of successors"); + assert(loop_start->is_set(BlockBegin::linear_scan_loop_header_flag), "loop header flag must be set"); + assert(loop_idx >= 0 && loop_idx < _num_loops, "loop index not set"); + assert(_work_list.is_empty(), "work list must be empty before processing"); + + // add the end-block of the loop to the working list + _work_list.push(loop_end); + set_block_in_loop(loop_idx, loop_end); + do { + BlockBegin* cur = _work_list.pop(); + + TRACE_LINEAR_SCAN(3, tty->print_cr(" processing B%d", cur->block_id())); + assert(is_block_in_loop(loop_idx, cur), "bit in loop map must be set when block is in work list"); + + // recursive processing of all predecessors ends when start block of loop is reached + if (cur != loop_start && !cur->is_set(BlockBegin::osr_entry_flag)) { + for (int j = cur->number_of_preds() - 1; j >= 0; j--) { + BlockBegin* pred = cur->pred_at(j); + + if (!is_block_in_loop(loop_idx, pred) /*&& !pred->is_set(BlockBeginosr_entry_flag)*/) { + // this predecessor has not been processed yet, so add it to work list + TRACE_LINEAR_SCAN(3, tty->print_cr(" pushing B%d", pred->block_id())); + _work_list.push(pred); + set_block_in_loop(loop_idx, pred); + } + } + } + } while (!_work_list.is_empty()); + } +} + + +// check for non-natural loops (loops where the loop header does not dominate +// all other loop blocks = loops with mulitple entries). +// such loops are ignored +void ComputeLinearScanOrder::clear_non_natural_loops(BlockBegin* start_block) { + for (int i = _num_loops - 1; i >= 0; i--) { + if (is_block_in_loop(i, start_block)) { + // loop i contains the entry block of the method + // -> this is not a natural loop, so ignore it + TRACE_LINEAR_SCAN(2, tty->print_cr("Loop %d is non-natural, so it is ignored", i)); + + for (int block_id = _max_block_id - 1; block_id >= 0; block_id--) { + clear_block_in_loop(i, block_id); + } + _iterative_dominators = true; + } + } +} + +void ComputeLinearScanOrder::assign_loop_depth(BlockBegin* start_block) { + TRACE_LINEAR_SCAN(3, "----- computing loop-depth and weight"); + init_visited(); + + assert(_work_list.is_empty(), "work list must be empty before processing"); + _work_list.append(start_block); + + do { + BlockBegin* cur = _work_list.pop(); + + if (!is_visited(cur)) { + set_visited(cur); + TRACE_LINEAR_SCAN(4, tty->print_cr("Computing loop depth for block B%d", cur->block_id())); + + // compute loop-depth and loop-index for the block + assert(cur->loop_depth() == 0, "cannot set loop-depth twice"); + int i; + int loop_depth = 0; + int min_loop_idx = -1; + for (i = _num_loops - 1; i >= 0; i--) { + if (is_block_in_loop(i, cur)) { + loop_depth++; + min_loop_idx = i; + } + } + cur->set_loop_depth(loop_depth); + cur->set_loop_index(min_loop_idx); + + // append all unvisited successors to work list + for (i = cur->number_of_sux() - 1; i >= 0; i--) { + _work_list.append(cur->sux_at(i)); + } + for (i = cur->number_of_exception_handlers() - 1; i >= 0; i--) { + _work_list.append(cur->exception_handler_at(i)); + } + } + } while (!_work_list.is_empty()); +} + + +BlockBegin* ComputeLinearScanOrder::common_dominator(BlockBegin* a, BlockBegin* b) { + assert(a != NULL && b != NULL, "must have input blocks"); + + _dominator_blocks.clear(); + while (a != NULL) { + _dominator_blocks.set_bit(a->block_id()); + assert(a->dominator() != NULL || a == _linear_scan_order->at(0), "dominator must be initialized"); + a = a->dominator(); + } + while (b != NULL && !_dominator_blocks.at(b->block_id())) { + assert(b->dominator() != NULL || b == _linear_scan_order->at(0), "dominator must be initialized"); + b = b->dominator(); + } + + assert(b != NULL, "could not find dominator"); + return b; +} + +void ComputeLinearScanOrder::compute_dominator(BlockBegin* cur, BlockBegin* parent) { + if (cur->dominator() == NULL) { + TRACE_LINEAR_SCAN(4, tty->print_cr("DOM: initializing dominator of B%d to B%d", cur->block_id(), parent->block_id())); + cur->set_dominator(parent); + + } else if (!(cur->is_set(BlockBegin::linear_scan_loop_header_flag) && parent->is_set(BlockBegin::linear_scan_loop_end_flag))) { + TRACE_LINEAR_SCAN(4, tty->print_cr("DOM: computing dominator of B%d: common dominator of B%d and B%d is B%d", cur->block_id(), parent->block_id(), cur->dominator()->block_id(), common_dominator(cur->dominator(), parent)->block_id())); + assert(cur->number_of_preds() > 1, ""); + cur->set_dominator(common_dominator(cur->dominator(), parent)); + } +} + + +int ComputeLinearScanOrder::compute_weight(BlockBegin* cur) { + BlockBegin* single_sux = NULL; + if (cur->number_of_sux() == 1) { + single_sux = cur->sux_at(0); + } + + // limit loop-depth to 15 bit (only for security reason, it will never be so big) + int weight = (cur->loop_depth() & 0x7FFF) << 16; + + // general macro for short definition of weight flags + // the first instance of INC_WEIGHT_IF has the highest priority + int cur_bit = 15; + #define INC_WEIGHT_IF(condition) if ((condition)) { weight |= (1 << cur_bit); } cur_bit--; + + // this is necessery for the (very rare) case that two successing blocks have + // the same loop depth, but a different loop index (can happen for endless loops + // with exception handlers) + INC_WEIGHT_IF(!cur->is_set(BlockBegin::linear_scan_loop_header_flag)); + + // loop end blocks (blocks that end with a backward branch) are added + // after all other blocks of the loop. + INC_WEIGHT_IF(!cur->is_set(BlockBegin::linear_scan_loop_end_flag)); + + // critical edge split blocks are prefered because than they have a bigger + // proability to be completely empty + INC_WEIGHT_IF(cur->is_set(BlockBegin::critical_edge_split_flag)); + + // exceptions should not be thrown in normal control flow, so these blocks + // are added as late as possible + INC_WEIGHT_IF(cur->end()->as_Throw() == NULL && (single_sux == NULL || single_sux->end()->as_Throw() == NULL)); + INC_WEIGHT_IF(cur->end()->as_Return() == NULL && (single_sux == NULL || single_sux->end()->as_Return() == NULL)); + + // exceptions handlers are added as late as possible + INC_WEIGHT_IF(!cur->is_set(BlockBegin::exception_entry_flag)); + + // guarantee that weight is > 0 + weight |= 1; + + #undef INC_WEIGHT_IF + assert(cur_bit >= 0, "too many flags"); + assert(weight > 0, "weight cannot become negative"); + + return weight; +} + +bool ComputeLinearScanOrder::ready_for_processing(BlockBegin* cur) { + // Discount the edge just traveled. + // When the number drops to zero, all forward branches were processed + if (dec_forward_branches(cur) != 0) { + return false; + } + + assert(_linear_scan_order->index_of(cur) == -1, "block already processed (block can be ready only once)"); + assert(_work_list.index_of(cur) == -1, "block already in work-list (block can be ready only once)"); + return true; +} + +void ComputeLinearScanOrder::sort_into_work_list(BlockBegin* cur) { + assert(_work_list.index_of(cur) == -1, "block already in work list"); + + int cur_weight = compute_weight(cur); + + // the linear_scan_number is used to cache the weight of a block + cur->set_linear_scan_number(cur_weight); + +#ifndef PRODUCT + if (StressLinearScan) { + _work_list.insert_before(0, cur); + return; + } +#endif + + _work_list.append(NULL); // provide space for new element + + int insert_idx = _work_list.length() - 1; + while (insert_idx > 0 && _work_list.at(insert_idx - 1)->linear_scan_number() > cur_weight) { + _work_list.at_put(insert_idx, _work_list.at(insert_idx - 1)); + insert_idx--; + } + _work_list.at_put(insert_idx, cur); + + TRACE_LINEAR_SCAN(3, tty->print_cr("Sorted B%d into worklist. new worklist:", cur->block_id())); + TRACE_LINEAR_SCAN(3, for (int i = 0; i < _work_list.length(); i++) tty->print_cr("%8d B%2d weight:%6x", i, _work_list.at(i)->block_id(), _work_list.at(i)->linear_scan_number())); + +#ifdef ASSERT + for (int i = 0; i < _work_list.length(); i++) { + assert(_work_list.at(i)->linear_scan_number() > 0, "weight not set"); + assert(i == 0 || _work_list.at(i - 1)->linear_scan_number() <= _work_list.at(i)->linear_scan_number(), "incorrect order in worklist"); + } +#endif +} + +void ComputeLinearScanOrder::append_block(BlockBegin* cur) { + TRACE_LINEAR_SCAN(3, tty->print_cr("appending block B%d (weight 0x%6x) to linear-scan order", cur->block_id(), cur->linear_scan_number())); + assert(_linear_scan_order->index_of(cur) == -1, "cannot add the same block twice"); + + // currently, the linear scan order and code emit order are equal. + // therefore the linear_scan_number and the weight of a block must also + // be equal. + cur->set_linear_scan_number(_linear_scan_order->length()); + _linear_scan_order->append(cur); +} + +void ComputeLinearScanOrder::compute_order(BlockBegin* start_block) { + TRACE_LINEAR_SCAN(3, "----- computing final block order"); + + // the start block is always the first block in the linear scan order + _linear_scan_order = new BlockList(_num_blocks); + append_block(start_block); + + assert(start_block->end()->as_Base() != NULL, "start block must end with Base-instruction"); + BlockBegin* std_entry = ((Base*)start_block->end())->std_entry(); + BlockBegin* osr_entry = ((Base*)start_block->end())->osr_entry(); + + BlockBegin* sux_of_osr_entry = NULL; + if (osr_entry != NULL) { + // special handling for osr entry: + // ignore the edge between the osr entry and its successor for processing + // the osr entry block is added manually below + assert(osr_entry->number_of_sux() == 1, "osr entry must have exactly one successor"); + assert(osr_entry->sux_at(0)->number_of_preds() >= 2, "sucessor of osr entry must have two predecessors (otherwise it is not present in normal control flow"); + + sux_of_osr_entry = osr_entry->sux_at(0); + dec_forward_branches(sux_of_osr_entry); + + compute_dominator(osr_entry, start_block); + _iterative_dominators = true; + } + compute_dominator(std_entry, start_block); + + // start processing with standard entry block + assert(_work_list.is_empty(), "list must be empty before processing"); + + if (ready_for_processing(std_entry)) { + sort_into_work_list(std_entry); + } else { + assert(false, "the std_entry must be ready for processing (otherwise, the method has no start block)"); + } + + do { + BlockBegin* cur = _work_list.pop(); + + if (cur == sux_of_osr_entry) { + // the osr entry block is ignored in normal processing, it is never added to the + // work list. Instead, it is added as late as possible manually here. + append_block(osr_entry); + compute_dominator(cur, osr_entry); + } + append_block(cur); + + int i; + int num_sux = cur->number_of_sux(); + // changed loop order to get "intuitive" order of if- and else-blocks + for (i = 0; i < num_sux; i++) { + BlockBegin* sux = cur->sux_at(i); + compute_dominator(sux, cur); + if (ready_for_processing(sux)) { + sort_into_work_list(sux); + } + } + num_sux = cur->number_of_exception_handlers(); + for (i = 0; i < num_sux; i++) { + BlockBegin* sux = cur->exception_handler_at(i); + compute_dominator(sux, cur); + if (ready_for_processing(sux)) { + sort_into_work_list(sux); + } + } + } while (_work_list.length() > 0); +} + + +bool ComputeLinearScanOrder::compute_dominators_iter() { + bool changed = false; + int num_blocks = _linear_scan_order->length(); + + assert(_linear_scan_order->at(0)->dominator() == NULL, "must not have dominator"); + assert(_linear_scan_order->at(0)->number_of_preds() == 0, "must not have predecessors"); + for (int i = 1; i < num_blocks; i++) { + BlockBegin* block = _linear_scan_order->at(i); + + BlockBegin* dominator = block->pred_at(0); + int num_preds = block->number_of_preds(); + for (int i = 1; i < num_preds; i++) { + dominator = common_dominator(dominator, block->pred_at(i)); + } + + if (dominator != block->dominator()) { + TRACE_LINEAR_SCAN(4, tty->print_cr("DOM: updating dominator of B%d from B%d to B%d", block->block_id(), block->dominator()->block_id(), dominator->block_id())); + + block->set_dominator(dominator); + changed = true; + } + } + return changed; +} + +void ComputeLinearScanOrder::compute_dominators() { + TRACE_LINEAR_SCAN(3, tty->print_cr("----- computing dominators (iterative computation reqired: %d)", _iterative_dominators)); + + // iterative computation of dominators is only required for methods with non-natural loops + // and OSR-methods. For all other methods, the dominators computed when generating the + // linear scan block order are correct. + if (_iterative_dominators) { + do { + TRACE_LINEAR_SCAN(1, tty->print_cr("DOM: next iteration of fix-point calculation")); + } while (compute_dominators_iter()); + } + + // check that dominators are correct + assert(!compute_dominators_iter(), "fix point not reached"); +} + + +#ifndef PRODUCT +void ComputeLinearScanOrder::print_blocks() { + if (TraceLinearScanLevel >= 2) { + tty->print_cr("----- loop information:"); + for (int block_idx = 0; block_idx < _linear_scan_order->length(); block_idx++) { + BlockBegin* cur = _linear_scan_order->at(block_idx); + + tty->print("%4d: B%2d: ", cur->linear_scan_number(), cur->block_id()); + for (int loop_idx = 0; loop_idx < _num_loops; loop_idx++) { + tty->print ("%d ", is_block_in_loop(loop_idx, cur)); + } + tty->print_cr(" -> loop_index: %2d, loop_depth: %2d", cur->loop_index(), cur->loop_depth()); + } + } + + if (TraceLinearScanLevel >= 1) { + tty->print_cr("----- linear-scan block order:"); + for (int block_idx = 0; block_idx < _linear_scan_order->length(); block_idx++) { + BlockBegin* cur = _linear_scan_order->at(block_idx); + tty->print("%4d: B%2d loop: %2d depth: %2d", cur->linear_scan_number(), cur->block_id(), cur->loop_index(), cur->loop_depth()); + + tty->print(cur->is_set(BlockBegin::exception_entry_flag) ? " ex" : " "); + tty->print(cur->is_set(BlockBegin::critical_edge_split_flag) ? " ce" : " "); + tty->print(cur->is_set(BlockBegin::linear_scan_loop_header_flag) ? " lh" : " "); + tty->print(cur->is_set(BlockBegin::linear_scan_loop_end_flag) ? " le" : " "); + + if (cur->dominator() != NULL) { + tty->print(" dom: B%d ", cur->dominator()->block_id()); + } else { + tty->print(" dom: NULL "); + } + + if (cur->number_of_preds() > 0) { + tty->print(" preds: "); + for (int j = 0; j < cur->number_of_preds(); j++) { + BlockBegin* pred = cur->pred_at(j); + tty->print("B%d ", pred->block_id()); + } + } + if (cur->number_of_sux() > 0) { + tty->print(" sux: "); + for (int j = 0; j < cur->number_of_sux(); j++) { + BlockBegin* sux = cur->sux_at(j); + tty->print("B%d ", sux->block_id()); + } + } + if (cur->number_of_exception_handlers() > 0) { + tty->print(" ex: "); + for (int j = 0; j < cur->number_of_exception_handlers(); j++) { + BlockBegin* ex = cur->exception_handler_at(j); + tty->print("B%d ", ex->block_id()); + } + } + tty->cr(); + } + } +} +#endif + +#ifdef ASSERT +void ComputeLinearScanOrder::verify() { + assert(_linear_scan_order->length() == _num_blocks, "wrong number of blocks in list"); + + if (StressLinearScan) { + // blocks are scrambled when StressLinearScan is used + return; + } + + // check that all successors of a block have a higher linear-scan-number + // and that all predecessors of a block have a lower linear-scan-number + // (only backward branches of loops are ignored) + int i; + for (i = 0; i < _linear_scan_order->length(); i++) { + BlockBegin* cur = _linear_scan_order->at(i); + + assert(cur->linear_scan_number() == i, "incorrect linear_scan_number"); + assert(cur->linear_scan_number() >= 0 && cur->linear_scan_number() == _linear_scan_order->index_of(cur), "incorrect linear_scan_number"); + + int j; + for (j = cur->number_of_sux() - 1; j >= 0; j--) { + BlockBegin* sux = cur->sux_at(j); + + assert(sux->linear_scan_number() >= 0 && sux->linear_scan_number() == _linear_scan_order->index_of(sux), "incorrect linear_scan_number"); + if (!cur->is_set(BlockBegin::linear_scan_loop_end_flag)) { + assert(cur->linear_scan_number() < sux->linear_scan_number(), "invalid order"); + } + if (cur->loop_depth() == sux->loop_depth()) { + assert(cur->loop_index() == sux->loop_index() || sux->is_set(BlockBegin::linear_scan_loop_header_flag), "successing blocks with same loop depth must have same loop index"); + } + } + + for (j = cur->number_of_preds() - 1; j >= 0; j--) { + BlockBegin* pred = cur->pred_at(j); + + assert(pred->linear_scan_number() >= 0 && pred->linear_scan_number() == _linear_scan_order->index_of(pred), "incorrect linear_scan_number"); + if (!cur->is_set(BlockBegin::linear_scan_loop_header_flag)) { + assert(cur->linear_scan_number() > pred->linear_scan_number(), "invalid order"); + } + if (cur->loop_depth() == pred->loop_depth()) { + assert(cur->loop_index() == pred->loop_index() || cur->is_set(BlockBegin::linear_scan_loop_header_flag), "successing blocks with same loop depth must have same loop index"); + } + + assert(cur->dominator()->linear_scan_number() <= cur->pred_at(j)->linear_scan_number(), "dominator must be before predecessors"); + } + + // check dominator + if (i == 0) { + assert(cur->dominator() == NULL, "first block has no dominator"); + } else { + assert(cur->dominator() != NULL, "all but first block must have dominator"); + } + assert(cur->number_of_preds() != 1 || cur->dominator() == cur->pred_at(0), "Single predecessor must also be dominator"); + } + + // check that all loops are continuous + for (int loop_idx = 0; loop_idx < _num_loops; loop_idx++) { + int block_idx = 0; + assert(!is_block_in_loop(loop_idx, _linear_scan_order->at(block_idx)), "the first block must not be present in any loop"); + + // skip blocks before the loop + while (block_idx < _num_blocks && !is_block_in_loop(loop_idx, _linear_scan_order->at(block_idx))) { + block_idx++; + } + // skip blocks of loop + while (block_idx < _num_blocks && is_block_in_loop(loop_idx, _linear_scan_order->at(block_idx))) { + block_idx++; + } + // after the first non-loop block, there must not be another loop-block + while (block_idx < _num_blocks) { + assert(!is_block_in_loop(loop_idx, _linear_scan_order->at(block_idx)), "loop not continuous in linear-scan order"); + block_idx++; + } + } +} +#endif + + +void IR::compute_code() { + assert(is_valid(), "IR must be valid"); + + ComputeLinearScanOrder compute_order(start()); + _num_loops = compute_order.num_loops(); + _code = compute_order.linear_scan_order(); +} + + +void IR::compute_use_counts() { + // make sure all values coming out of this block get evaluated. + int num_blocks = _code->length(); + for (int i = 0; i < num_blocks; i++) { + _code->at(i)->end()->state()->pin_stack_for_linear_scan(); + } + + // compute use counts + UseCountComputer::compute(_code); +} + + +void IR::iterate_preorder(BlockClosure* closure) { + assert(is_valid(), "IR must be valid"); + start()->iterate_preorder(closure); +} + + +void IR::iterate_postorder(BlockClosure* closure) { + assert(is_valid(), "IR must be valid"); + start()->iterate_postorder(closure); +} + +void IR::iterate_linear_scan_order(BlockClosure* closure) { + linear_scan_order()->iterate_forward(closure); +} + + +#ifndef PRODUCT +class BlockPrinter: public BlockClosure { + private: + InstructionPrinter* _ip; + bool _cfg_only; + bool _live_only; + + public: + BlockPrinter(InstructionPrinter* ip, bool cfg_only, bool live_only = false) { + _ip = ip; + _cfg_only = cfg_only; + _live_only = live_only; + } + + virtual void block_do(BlockBegin* block) { + if (_cfg_only) { + _ip->print_instr(block); tty->cr(); + } else { + block->print_block(*_ip, _live_only); + } + } +}; + + +void IR::print(BlockBegin* start, bool cfg_only, bool live_only) { + ttyLocker ttyl; + InstructionPrinter ip(!cfg_only); + BlockPrinter bp(&ip, cfg_only, live_only); + start->iterate_preorder(&bp); + tty->cr(); +} + +void IR::print(bool cfg_only, bool live_only) { + if (is_valid()) { + print(start(), cfg_only, live_only); + } else { + tty->print_cr("invalid IR"); + } +} + + +define_array(BlockListArray, BlockList*) +define_stack(BlockListList, BlockListArray) + +class PredecessorValidator : public BlockClosure { + private: + BlockListList* _predecessors; + BlockList* _blocks; + + static int cmp(BlockBegin** a, BlockBegin** b) { + return (*a)->block_id() - (*b)->block_id(); + } + + public: + PredecessorValidator(IR* hir) { + ResourceMark rm; + _predecessors = new BlockListList(BlockBegin::number_of_blocks(), NULL); + _blocks = new BlockList(); + + int i; + hir->start()->iterate_preorder(this); + if (hir->code() != NULL) { + assert(hir->code()->length() == _blocks->length(), "must match"); + for (i = 0; i < _blocks->length(); i++) { + assert(hir->code()->contains(_blocks->at(i)), "should be in both lists"); + } + } + + for (i = 0; i < _blocks->length(); i++) { + BlockBegin* block = _blocks->at(i); + BlockList* preds = _predecessors->at(block->block_id()); + if (preds == NULL) { + assert(block->number_of_preds() == 0, "should be the same"); + continue; + } + + // clone the pred list so we can mutate it + BlockList* pred_copy = new BlockList(); + int j; + for (j = 0; j < block->number_of_preds(); j++) { + pred_copy->append(block->pred_at(j)); + } + // sort them in the same order + preds->sort(cmp); + pred_copy->sort(cmp); + int length = MIN2(preds->length(), block->number_of_preds()); + for (j = 0; j < block->number_of_preds(); j++) { + assert(preds->at(j) == pred_copy->at(j), "must match"); + } + + assert(preds->length() == block->number_of_preds(), "should be the same"); + } + } + + virtual void block_do(BlockBegin* block) { + _blocks->append(block); + BlockEnd* be = block->end(); + int n = be->number_of_sux(); + int i; + for (i = 0; i < n; i++) { + BlockBegin* sux = be->sux_at(i); + assert(!sux->is_set(BlockBegin::exception_entry_flag), "must not be xhandler"); + + BlockList* preds = _predecessors->at_grow(sux->block_id(), NULL); + if (preds == NULL) { + preds = new BlockList(); + _predecessors->at_put(sux->block_id(), preds); + } + preds->append(block); + } + + n = block->number_of_exception_handlers(); + for (i = 0; i < n; i++) { + BlockBegin* sux = block->exception_handler_at(i); + assert(sux->is_set(BlockBegin::exception_entry_flag), "must be xhandler"); + + BlockList* preds = _predecessors->at_grow(sux->block_id(), NULL); + if (preds == NULL) { + preds = new BlockList(); + _predecessors->at_put(sux->block_id(), preds); + } + preds->append(block); + } + } +}; + +void IR::verify() { +#ifdef ASSERT + PredecessorValidator pv(this); +#endif +} + +#endif // PRODUCT + +void SubstitutionResolver::substitute(Value* v) { + Value v0 = *v; + if (v0) { + Value vs = v0->subst(); + if (vs != v0) { + *v = v0->subst(); + } + } +} + +#ifdef ASSERT +void check_substitute(Value* v) { + Value v0 = *v; + if (v0) { + Value vs = v0->subst(); + assert(vs == v0, "missed substitution"); + } +} +#endif + + +void SubstitutionResolver::block_do(BlockBegin* block) { + Instruction* last = NULL; + for (Instruction* n = block; n != NULL;) { + n->values_do(substitute); + // need to remove this instruction from the instruction stream + if (n->subst() != n) { + assert(last != NULL, "must have last"); + last->set_next(n->next(), n->next()->bci()); + } else { + last = n; + } + n = last->next(); + } + +#ifdef ASSERT + if (block->state()) block->state()->values_do(check_substitute); + block->block_values_do(check_substitute); + if (block->end() && block->end()->state()) block->end()->state()->values_do(check_substitute); +#endif +} diff --git a/hotspot/src/share/vm/c1/c1_IR.hpp b/hotspot/src/share/vm/c1/c1_IR.hpp new file mode 100644 index 00000000000..ecd5b4ba522 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_IR.hpp @@ -0,0 +1,376 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// An XHandler is a C1 internal description for an exception handler + +class XHandler: public CompilationResourceObj { + private: + ciExceptionHandler* _desc; + + BlockBegin* _entry_block; // Entry block of xhandler + LIR_List* _entry_code; // LIR-operations that must be executed before jumping to entry_block + int _entry_pco; // pco where entry_code (or entry_block if no entry_code) starts + int _phi_operand; // For resolving of phi functions at begin of entry_block + int _scope_count; // for filling ExceptionRangeEntry::scope_count + +#ifdef ASSERT + int _lir_op_id; // op_id of the LIR-operation throwing to this handler +#endif + + public: + // creation + XHandler(ciExceptionHandler* desc) + : _desc(desc) + , _entry_block(NULL) + , _entry_code(NULL) + , _entry_pco(-1) + , _phi_operand(-1) + , _scope_count(-1) +#ifdef ASSERT + , _lir_op_id(-1) +#endif + { } + + XHandler(XHandler* other) + : _desc(other->_desc) + , _entry_block(other->_entry_block) + , _entry_code(other->_entry_code) + , _entry_pco(other->_entry_pco) + , _phi_operand(other->_phi_operand) + , _scope_count(other->_scope_count) +#ifdef ASSERT + , _lir_op_id(other->_lir_op_id) +#endif + { } + + // accessors for data of ciExceptionHandler + int beg_bci() const { return _desc->start(); } + int end_bci() const { return _desc->limit(); } + int handler_bci() const { return _desc->handler_bci(); } + bool is_catch_all() const { return _desc->is_catch_all(); } + int catch_type() const { return _desc->catch_klass_index(); } + ciInstanceKlass* catch_klass() const { return _desc->catch_klass(); } + bool covers(int bci) const { return beg_bci() <= bci && bci < end_bci(); } + + // accessors for additional fields + BlockBegin* entry_block() const { return _entry_block; } + LIR_List* entry_code() const { return _entry_code; } + int entry_pco() const { return _entry_pco; } + int phi_operand() const { assert(_phi_operand != -1, "not set"); return _phi_operand; } + int scope_count() const { assert(_scope_count != -1, "not set"); return _scope_count; } + DEBUG_ONLY(int lir_op_id() const { return _lir_op_id; }); + + void set_entry_block(BlockBegin* entry_block) { + assert(entry_block->is_set(BlockBegin::exception_entry_flag), "must be an exception handler entry"); + assert(entry_block->bci() == handler_bci(), "bci's must correspond"); + _entry_block = entry_block; + } + void set_entry_code(LIR_List* entry_code) { _entry_code = entry_code; } + void set_entry_pco(int entry_pco) { _entry_pco = entry_pco; } + void set_phi_operand(int phi_operand) { _phi_operand = phi_operand; } + void set_scope_count(int scope_count) { _scope_count = scope_count; } + DEBUG_ONLY(void set_lir_op_id(int lir_op_id) { _lir_op_id = lir_op_id; }); + + bool equals(XHandler* other) const; +}; + +define_array(_XHandlerArray, XHandler*) +define_stack(_XHandlerList, _XHandlerArray) + + +// XHandlers is the C1 internal list of exception handlers for a method +class XHandlers: public CompilationResourceObj { + private: + _XHandlerList _list; + + public: + // creation + XHandlers() : _list() { } + XHandlers(ciMethod* method); + XHandlers(XHandlers* other); + + // accessors + int length() const { return _list.length(); } + XHandler* handler_at(int i) const { return _list.at(i); } + bool has_handlers() const { return _list.length() > 0; } + void append(XHandler* h) { _list.append(h); } + XHandler* remove_last() { return _list.pop(); } + + bool could_catch(ciInstanceKlass* klass, bool type_is_exact) const; + bool equals(XHandlers* others) const; +}; + + +class IRScope; +define_array(IRScopeArray, IRScope*) +define_stack(IRScopeList, IRScopeArray) + +class Compilation; +class IRScope: public CompilationResourceObj { + private: + // hierarchy + Compilation* _compilation; // the current compilation + IRScope* _caller; // the caller scope, or NULL + int _caller_bci; // the caller bci of the corresponding (inlined) invoke, or < 0 + ValueStack* _caller_state; // the caller state, or NULL + int _level; // the inlining level + ciMethod* _method; // the corresponding method + IRScopeList _callees; // the inlined method scopes + + // graph + XHandlers* _xhandlers; // the exception handlers + int _number_of_locks; // the number of monitor lock slots needed + bool _monitor_pairing_ok; // the monitor pairing info + BlockBegin* _start; // the start block, successsors are method entries + + // lock stack management + int _lock_stack_size; // number of expression stack elements which, if present, + // must be spilled to the stack because of exception + // handling inside inlined methods + + BitMap _requires_phi_function; // bit is set if phi functions at loop headers are necessary for a local variable + + // helper functions + BlockBegin* header_block(BlockBegin* entry, BlockBegin::Flag f, ValueStack* state); + BlockBegin* build_graph(Compilation* compilation, int osr_bci); + + public: + // creation + IRScope(Compilation* compilation, IRScope* caller, int caller_bci, ciMethod* method, int osr_bci, bool create_graph = false); + + // accessors + Compilation* compilation() const { return _compilation; } + IRScope* caller() const { return _caller; } + int caller_bci() const { return _caller_bci; } + ValueStack* caller_state() const { return _caller_state; } + int level() const { return _level; } + ciMethod* method() const { return _method; } + int max_stack() const; // NOTE: expensive + int lock_stack_size() const { + assert(_lock_stack_size != -1, "uninitialized"); + return _lock_stack_size; + } + BitMap& requires_phi_function() { return _requires_phi_function; } + + // mutators + // Needed because caller state is not ready at time of IRScope construction + void set_caller_state(ValueStack* state) { _caller_state = state; } + // Needed because caller state changes after IRScope construction. + // Computes number of expression stack elements whose state must be + // preserved in the case of an exception; these may be seen by + // caller scopes. Zero when inlining of methods containing exception + // handlers is disabled, otherwise a conservative approximation. + void compute_lock_stack_size(); + + // hierarchy + bool is_top_scope() const { return _caller == NULL; } + void add_callee(IRScope* callee) { _callees.append(callee); } + int number_of_callees() const { return _callees.length(); } + IRScope* callee_no(int i) const { return _callees.at(i); } + int top_scope_bci() const; + + // accessors, graph + bool is_valid() const { return start() != NULL; } + XHandlers* xhandlers() const { return _xhandlers; } + int number_of_locks() const { return _number_of_locks; } + void set_min_number_of_locks(int n) { if (n > _number_of_locks) _number_of_locks = n; } + bool monitor_pairing_ok() const { return _monitor_pairing_ok; } + BlockBegin* start() const { return _start; } +}; + + +// +// IRScopeDebugInfo records the debug information for a particular IRScope +// in a particular CodeEmitInfo. This allows the information to be computed +// once early enough for the OopMap to be available to the LIR and also to be +// reemited for different pcs using the same CodeEmitInfo without recomputing +// everything. +// + +class IRScopeDebugInfo: public CompilationResourceObj { + private: + IRScope* _scope; + int _bci; + GrowableArray* _locals; + GrowableArray* _expressions; + GrowableArray* _monitors; + IRScopeDebugInfo* _caller; + + public: + IRScopeDebugInfo(IRScope* scope, + int bci, + GrowableArray* locals, + GrowableArray* expressions, + GrowableArray* monitors, + IRScopeDebugInfo* caller): + _scope(scope) + , _locals(locals) + , _bci(bci) + , _expressions(expressions) + , _monitors(monitors) + , _caller(caller) {} + + + IRScope* scope() { return _scope; } + int bci() { return _bci; } + GrowableArray* locals() { return _locals; } + GrowableArray* expressions() { return _expressions; } + GrowableArray* monitors() { return _monitors; } + IRScopeDebugInfo* caller() { return _caller; } + + void record_debug_info(DebugInformationRecorder* recorder, int pc_offset) { + if (caller() != NULL) { + // Order is significant: Must record caller first. + caller()->record_debug_info(recorder, pc_offset); + } + DebugToken* locvals = recorder->create_scope_values(locals()); + DebugToken* expvals = recorder->create_scope_values(expressions()); + DebugToken* monvals = recorder->create_monitor_values(monitors()); + recorder->describe_scope(pc_offset, scope()->method(), bci(), locvals, expvals, monvals); + } +}; + + +class CodeEmitInfo: public CompilationResourceObj { + friend class LinearScan; + private: + IRScopeDebugInfo* _scope_debug_info; + IRScope* _scope; + XHandlers* _exception_handlers; + OopMap* _oop_map; + ValueStack* _stack; // used by deoptimization (contains also monitors + int _bci; + CodeEmitInfo* _next; + int _id; + + FrameMap* frame_map() const { return scope()->compilation()->frame_map(); } + Compilation* compilation() const { return scope()->compilation(); } + + public: + + // use scope from ValueStack + CodeEmitInfo(int bci, ValueStack* stack, XHandlers* exception_handlers); + + // used by natives + CodeEmitInfo(IRScope* scope, int bci) + : _scope(scope) + , _bci(bci) + , _oop_map(NULL) + , _scope_debug_info(NULL) + , _stack(NULL) + , _exception_handlers(NULL) + , _next(NULL) + , _id(-1) { + } + + // make a copy + CodeEmitInfo(CodeEmitInfo* info, bool lock_stack_only = false); + + // accessors + OopMap* oop_map() { return _oop_map; } + ciMethod* method() const { return _scope->method(); } + IRScope* scope() const { return _scope; } + XHandlers* exception_handlers() const { return _exception_handlers; } + ValueStack* stack() const { return _stack; } + int bci() const { return _bci; } + + void add_register_oop(LIR_Opr opr); + void record_debug_info(DebugInformationRecorder* recorder, int pc_offset); + + CodeEmitInfo* next() const { return _next; } + void set_next(CodeEmitInfo* next) { _next = next; } + + int id() const { return _id; } + void set_id(int id) { _id = id; } +}; + + +class IR: public CompilationResourceObj { + private: + Compilation* _compilation; // the current compilation + IRScope* _top_scope; // the root of the scope hierarchy + WordSize _locals_size; // the space required for all locals + int _num_loops; // Total number of loops + BlockList* _code; // the blocks in code generation order w/ use counts + + public: + // creation + IR(Compilation* compilation, ciMethod* method, int osr_bci); + + // accessors + bool is_valid() const { return top_scope()->is_valid(); } + Compilation* compilation() const { return _compilation; } + IRScope* top_scope() const { return _top_scope; } + int number_of_locks() const { return top_scope()->number_of_locks(); } + ciMethod* method() const { return top_scope()->method(); } + BlockBegin* start() const { return top_scope()->start(); } + BlockBegin* std_entry() const { return start()->end()->as_Base()->std_entry(); } + BlockBegin* osr_entry() const { return start()->end()->as_Base()->osr_entry(); } + WordSize locals_size() const { return _locals_size; } + int locals_size_in_words() const { return in_words(_locals_size); } + BlockList* code() const { return _code; } + int num_loops() const { return _num_loops; } + int max_stack() const { return top_scope()->max_stack(); } // expensive + + // ir manipulation + void optimize(); + void compute_predecessors(); + void split_critical_edges(); + void compute_code(); + void compute_use_counts(); + + // The linear-scan order and the code emission order are equal, but + // this may change in future + BlockList* linear_scan_order() { assert(_code != NULL, "not computed"); return _code; } + + // iteration + void iterate_preorder (BlockClosure* closure); + void iterate_postorder (BlockClosure* closure); + void iterate_linear_scan_order(BlockClosure* closure); + + // debugging + static void print(BlockBegin* start, bool cfg_only, bool live_only = false) PRODUCT_RETURN; + void print(bool cfg_only, bool live_only = false) PRODUCT_RETURN; + void verify() PRODUCT_RETURN; +}; + + +// Globally do instruction substitution and remove substituted +// instructions from the instruction list. +// + +class SubstitutionResolver: public BlockClosure { + static void substitute(Value* v); + + public: + SubstitutionResolver(IR* hir) { + hir->iterate_preorder(this); + } + + SubstitutionResolver(BlockBegin* block) { + block->iterate_preorder(this); + } + + virtual void block_do(BlockBegin* block); +}; diff --git a/hotspot/src/share/vm/c1/c1_Instruction.cpp b/hotspot/src/share/vm/c1/c1_Instruction.cpp new file mode 100644 index 00000000000..4ac089fa337 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Instruction.cpp @@ -0,0 +1,1006 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_Instruction.cpp.incl" + + +// Implementation of Instruction + + +int Instruction::_next_id = 0; + +#ifdef ASSERT +void Instruction::create_hi_word() { + assert(type()->is_double_word() && _hi_word == NULL, "only double word has high word"); + _hi_word = new HiWord(this); +} +#endif + +Instruction::Condition Instruction::mirror(Condition cond) { + switch (cond) { + case eql: return eql; + case neq: return neq; + case lss: return gtr; + case leq: return geq; + case gtr: return lss; + case geq: return leq; + } + ShouldNotReachHere(); + return eql; +} + + +Instruction::Condition Instruction::negate(Condition cond) { + switch (cond) { + case eql: return neq; + case neq: return eql; + case lss: return geq; + case leq: return gtr; + case gtr: return leq; + case geq: return lss; + } + ShouldNotReachHere(); + return eql; +} + + +Instruction* Instruction::prev(BlockBegin* block) { + Instruction* p = NULL; + Instruction* q = block; + while (q != this) { + assert(q != NULL, "this is not in the block's instruction list"); + p = q; q = q->next(); + } + return p; +} + + +#ifndef PRODUCT +void Instruction::print() { + InstructionPrinter ip; + print(ip); +} + + +void Instruction::print_line() { + InstructionPrinter ip; + ip.print_line(this); +} + + +void Instruction::print(InstructionPrinter& ip) { + ip.print_head(); + ip.print_line(this); + tty->cr(); +} +#endif // PRODUCT + + +// perform constant and interval tests on index value +bool AccessIndexed::compute_needs_range_check() { + Constant* clength = length()->as_Constant(); + Constant* cindex = index()->as_Constant(); + if (clength && cindex) { + IntConstant* l = clength->type()->as_IntConstant(); + IntConstant* i = cindex->type()->as_IntConstant(); + if (l && i && i->value() < l->value() && i->value() >= 0) { + return false; + } + } + return true; +} + + +ciType* LoadIndexed::exact_type() const { + ciType* array_type = array()->exact_type(); + if (array_type == NULL) { + return NULL; + } + assert(array_type->is_array_klass(), "what else?"); + ciArrayKlass* ak = (ciArrayKlass*)array_type; + + if (ak->element_type()->is_instance_klass()) { + ciInstanceKlass* ik = (ciInstanceKlass*)ak->element_type(); + if (ik->is_loaded() && ik->is_final()) { + return ik; + } + } + return NULL; +} + + +ciType* LoadIndexed::declared_type() const { + ciType* array_type = array()->declared_type(); + if (array_type == NULL) { + return NULL; + } + assert(array_type->is_array_klass(), "what else?"); + ciArrayKlass* ak = (ciArrayKlass*)array_type; + return ak->element_type(); +} + + +ciType* LoadField::declared_type() const { + return field()->type(); +} + + +ciType* LoadField::exact_type() const { + ciType* type = declared_type(); + // for primitive arrays, the declared type is the exact type + if (type->is_type_array_klass()) { + return type; + } + if (type->is_instance_klass()) { + ciInstanceKlass* ik = (ciInstanceKlass*)type; + if (ik->is_loaded() && ik->is_final()) { + return type; + } + } + return NULL; +} + + +ciType* NewTypeArray::exact_type() const { + return ciTypeArrayKlass::make(elt_type()); +} + + +ciType* NewObjectArray::exact_type() const { + return ciObjArrayKlass::make(klass()); +} + + +ciType* NewInstance::exact_type() const { + return klass(); +} + + +ciType* CheckCast::declared_type() const { + return klass(); +} + +ciType* CheckCast::exact_type() const { + if (klass()->is_instance_klass()) { + ciInstanceKlass* ik = (ciInstanceKlass*)klass(); + if (ik->is_loaded() && ik->is_final()) { + return ik; + } + } + return NULL; +} + + +void ArithmeticOp::other_values_do(void f(Value*)) { + if (lock_stack() != NULL) lock_stack()->values_do(f); +} + +void NullCheck::other_values_do(void f(Value*)) { + lock_stack()->values_do(f); +} + +void AccessArray::other_values_do(void f(Value*)) { + if (lock_stack() != NULL) lock_stack()->values_do(f); +} + + +// Implementation of AccessField + +void AccessField::other_values_do(void f(Value*)) { + if (state_before() != NULL) state_before()->values_do(f); + if (lock_stack() != NULL) lock_stack()->values_do(f); +} + + +// Implementation of StoreIndexed + +IRScope* StoreIndexed::scope() const { + return lock_stack()->scope(); +} + + +// Implementation of ArithmeticOp + +bool ArithmeticOp::is_commutative() const { + switch (op()) { + case Bytecodes::_iadd: // fall through + case Bytecodes::_ladd: // fall through + case Bytecodes::_fadd: // fall through + case Bytecodes::_dadd: // fall through + case Bytecodes::_imul: // fall through + case Bytecodes::_lmul: // fall through + case Bytecodes::_fmul: // fall through + case Bytecodes::_dmul: return true; + } + return false; +} + + +bool ArithmeticOp::can_trap() const { + switch (op()) { + case Bytecodes::_idiv: // fall through + case Bytecodes::_ldiv: // fall through + case Bytecodes::_irem: // fall through + case Bytecodes::_lrem: return true; + } + return false; +} + + +// Implementation of LogicOp + +bool LogicOp::is_commutative() const { +#ifdef ASSERT + switch (op()) { + case Bytecodes::_iand: // fall through + case Bytecodes::_land: // fall through + case Bytecodes::_ior : // fall through + case Bytecodes::_lor : // fall through + case Bytecodes::_ixor: // fall through + case Bytecodes::_lxor: break; + default : ShouldNotReachHere(); + } +#endif + // all LogicOps are commutative + return true; +} + + +// Implementation of CompareOp + +void CompareOp::other_values_do(void f(Value*)) { + if (state_before() != NULL) state_before()->values_do(f); +} + + +// Implementation of IfOp + +bool IfOp::is_commutative() const { + return cond() == eql || cond() == neq; +} + + +// Implementation of StateSplit + +void StateSplit::substitute(BlockList& list, BlockBegin* old_block, BlockBegin* new_block) { + NOT_PRODUCT(bool assigned = false;) + for (int i = 0; i < list.length(); i++) { + BlockBegin** b = list.adr_at(i); + if (*b == old_block) { + *b = new_block; + NOT_PRODUCT(assigned = true;) + } + } + assert(assigned == true, "should have assigned at least once"); +} + + +IRScope* StateSplit::scope() const { + return _state->scope(); +} + + +void StateSplit::state_values_do(void f(Value*)) { + if (state() != NULL) state()->values_do(f); +} + + +void BlockBegin::state_values_do(void f(Value*)) { + StateSplit::state_values_do(f); + + if (is_set(BlockBegin::exception_entry_flag)) { + for (int i = 0; i < number_of_exception_states(); i++) { + exception_state_at(i)->values_do(f); + } + } +} + + +void MonitorEnter::state_values_do(void f(Value*)) { + StateSplit::state_values_do(f); + _lock_stack_before->values_do(f); +} + + +void Intrinsic::state_values_do(void f(Value*)) { + StateSplit::state_values_do(f); + if (lock_stack() != NULL) lock_stack()->values_do(f); +} + + +// Implementation of Invoke + + +Invoke::Invoke(Bytecodes::Code code, ValueType* result_type, Value recv, Values* args, + int vtable_index, ciMethod* target) + : StateSplit(result_type) + , _code(code) + , _recv(recv) + , _args(args) + , _vtable_index(vtable_index) + , _target(target) +{ + set_flag(TargetIsLoadedFlag, target->is_loaded()); + set_flag(TargetIsFinalFlag, target_is_loaded() && target->is_final_method()); + set_flag(TargetIsStrictfpFlag, target_is_loaded() && target->is_strict()); + + assert(args != NULL, "args must exist"); +#ifdef ASSERT + values_do(assert_value); +#endif // ASSERT + + // provide an initial guess of signature size. + _signature = new BasicTypeList(number_of_arguments() + (has_receiver() ? 1 : 0)); + if (has_receiver()) { + _signature->append(as_BasicType(receiver()->type())); + } + for (int i = 0; i < number_of_arguments(); i++) { + ValueType* t = argument_at(i)->type(); + BasicType bt = as_BasicType(t); + _signature->append(bt); + } +} + + +// Implementation of Contant +intx Constant::hash() const { + if (_state == NULL) { + switch (type()->tag()) { + case intTag: + return HASH2(name(), type()->as_IntConstant()->value()); + case longTag: + { + jlong temp = type()->as_LongConstant()->value(); + return HASH3(name(), high(temp), low(temp)); + } + case floatTag: + return HASH2(name(), jint_cast(type()->as_FloatConstant()->value())); + case doubleTag: + { + jlong temp = jlong_cast(type()->as_DoubleConstant()->value()); + return HASH3(name(), high(temp), low(temp)); + } + case objectTag: + assert(type()->as_ObjectType()->is_loaded(), "can't handle unloaded values"); + return HASH2(name(), type()->as_ObjectType()->constant_value()); + } + } + return 0; +} + +bool Constant::is_equal(Value v) const { + if (v->as_Constant() == NULL) return false; + + switch (type()->tag()) { + case intTag: + { + IntConstant* t1 = type()->as_IntConstant(); + IntConstant* t2 = v->type()->as_IntConstant(); + return (t1 != NULL && t2 != NULL && + t1->value() == t2->value()); + } + case longTag: + { + LongConstant* t1 = type()->as_LongConstant(); + LongConstant* t2 = v->type()->as_LongConstant(); + return (t1 != NULL && t2 != NULL && + t1->value() == t2->value()); + } + case floatTag: + { + FloatConstant* t1 = type()->as_FloatConstant(); + FloatConstant* t2 = v->type()->as_FloatConstant(); + return (t1 != NULL && t2 != NULL && + jint_cast(t1->value()) == jint_cast(t2->value())); + } + case doubleTag: + { + DoubleConstant* t1 = type()->as_DoubleConstant(); + DoubleConstant* t2 = v->type()->as_DoubleConstant(); + return (t1 != NULL && t2 != NULL && + jlong_cast(t1->value()) == jlong_cast(t2->value())); + } + case objectTag: + { + ObjectType* t1 = type()->as_ObjectType(); + ObjectType* t2 = v->type()->as_ObjectType(); + return (t1 != NULL && t2 != NULL && + t1->is_loaded() && t2->is_loaded() && + t1->constant_value() == t2->constant_value()); + } + } + return false; +} + + +BlockBegin* Constant::compare(Instruction::Condition cond, Value right, + BlockBegin* true_sux, BlockBegin* false_sux) { + Constant* rc = right->as_Constant(); + // other is not a constant + if (rc == NULL) return NULL; + + ValueType* lt = type(); + ValueType* rt = rc->type(); + // different types + if (lt->base() != rt->base()) return NULL; + switch (lt->tag()) { + case intTag: { + int x = lt->as_IntConstant()->value(); + int y = rt->as_IntConstant()->value(); + switch (cond) { + case If::eql: return x == y ? true_sux : false_sux; + case If::neq: return x != y ? true_sux : false_sux; + case If::lss: return x < y ? true_sux : false_sux; + case If::leq: return x <= y ? true_sux : false_sux; + case If::gtr: return x > y ? true_sux : false_sux; + case If::geq: return x >= y ? true_sux : false_sux; + } + break; + } + case longTag: { + jlong x = lt->as_LongConstant()->value(); + jlong y = rt->as_LongConstant()->value(); + switch (cond) { + case If::eql: return x == y ? true_sux : false_sux; + case If::neq: return x != y ? true_sux : false_sux; + case If::lss: return x < y ? true_sux : false_sux; + case If::leq: return x <= y ? true_sux : false_sux; + case If::gtr: return x > y ? true_sux : false_sux; + case If::geq: return x >= y ? true_sux : false_sux; + } + break; + } + case objectTag: { + ciObject* xvalue = lt->as_ObjectType()->constant_value(); + ciObject* yvalue = rt->as_ObjectType()->constant_value(); + assert(xvalue != NULL && yvalue != NULL, "not constants"); + if (xvalue->is_loaded() && yvalue->is_loaded()) { + switch (cond) { + case If::eql: return xvalue == yvalue ? true_sux : false_sux; + case If::neq: return xvalue != yvalue ? true_sux : false_sux; + } + } + break; + } + } + return NULL; +} + + +void Constant::other_values_do(void f(Value*)) { + if (state() != NULL) state()->values_do(f); +} + + +// Implementation of NewArray + +void NewArray::other_values_do(void f(Value*)) { + if (state_before() != NULL) state_before()->values_do(f); +} + + +// Implementation of TypeCheck + +void TypeCheck::other_values_do(void f(Value*)) { + if (state_before() != NULL) state_before()->values_do(f); +} + + +// Implementation of BlockBegin + +int BlockBegin::_next_block_id = 0; + + +void BlockBegin::set_end(BlockEnd* end) { + assert(end != NULL, "should not reset block end to NULL"); + BlockEnd* old_end = _end; + if (end == old_end) { + return; + } + // Must make the predecessors/successors match up with the + // BlockEnd's notion. + int i, n; + if (old_end != NULL) { + // disconnect from the old end + old_end->set_begin(NULL); + + // disconnect this block from it's current successors + for (i = 0; i < _successors.length(); i++) { + _successors.at(i)->remove_predecessor(this); + } + } + _end = end; + + _successors.clear(); + // Now reset successors list based on BlockEnd + n = end->number_of_sux(); + for (i = 0; i < n; i++) { + BlockBegin* sux = end->sux_at(i); + _successors.append(sux); + sux->_predecessors.append(this); + } + _end->set_begin(this); +} + + +void BlockBegin::disconnect_edge(BlockBegin* from, BlockBegin* to) { + // disconnect any edges between from and to +#ifndef PRODUCT + if (PrintIR && Verbose) { + tty->print_cr("Disconnected edge B%d -> B%d", from->block_id(), to->block_id()); + } +#endif + for (int s = 0; s < from->number_of_sux();) { + BlockBegin* sux = from->sux_at(s); + if (sux == to) { + int index = sux->_predecessors.index_of(from); + if (index >= 0) { + sux->_predecessors.remove_at(index); + } + from->_successors.remove_at(s); + } else { + s++; + } + } +} + + +void BlockBegin::disconnect_from_graph() { + // disconnect this block from all other blocks + for (int p = 0; p < number_of_preds(); p++) { + pred_at(p)->remove_successor(this); + } + for (int s = 0; s < number_of_sux(); s++) { + sux_at(s)->remove_predecessor(this); + } +} + +void BlockBegin::substitute_sux(BlockBegin* old_sux, BlockBegin* new_sux) { + // modify predecessors before substituting successors + for (int i = 0; i < number_of_sux(); i++) { + if (sux_at(i) == old_sux) { + // remove old predecessor before adding new predecessor + // otherwise there is a dead predecessor in the list + new_sux->remove_predecessor(old_sux); + new_sux->add_predecessor(this); + } + } + old_sux->remove_predecessor(this); + end()->substitute_sux(old_sux, new_sux); +} + + + +// In general it is not possible to calculate a value for the field "depth_first_number" +// of the inserted block, without recomputing the values of the other blocks +// in the CFG. Therefore the value of "depth_first_number" in BlockBegin becomes meaningless. +BlockBegin* BlockBegin::insert_block_between(BlockBegin* sux) { + // Try to make the bci close to a block with a single pred or sux, + // since this make the block layout algorithm work better. + int bci = -1; + if (sux->number_of_preds() == 1) { + bci = sux->bci(); + } else { + bci = end()->bci(); + } + + BlockBegin* new_sux = new BlockBegin(bci); + + // mark this block (special treatment when block order is computed) + new_sux->set(critical_edge_split_flag); + + // This goto is not a safepoint. + Goto* e = new Goto(sux, false); + new_sux->set_next(e, bci); + new_sux->set_end(e); + // setup states + ValueStack* s = end()->state(); + new_sux->set_state(s->copy()); + e->set_state(s->copy()); + assert(new_sux->state()->locals_size() == s->locals_size(), "local size mismatch!"); + assert(new_sux->state()->stack_size() == s->stack_size(), "stack size mismatch!"); + assert(new_sux->state()->locks_size() == s->locks_size(), "locks size mismatch!"); + + // link predecessor to new block + end()->substitute_sux(sux, new_sux); + + // The ordering needs to be the same, so remove the link that the + // set_end call above added and substitute the new_sux for this + // block. + sux->remove_predecessor(new_sux); + + // the successor could be the target of a switch so it might have + // multiple copies of this predecessor, so substitute the new_sux + // for the first and delete the rest. + bool assigned = false; + BlockList& list = sux->_predecessors; + for (int i = 0; i < list.length(); i++) { + BlockBegin** b = list.adr_at(i); + if (*b == this) { + if (assigned) { + list.remove_at(i); + // reprocess this index + i--; + } else { + assigned = true; + *b = new_sux; + } + // link the new block back to it's predecessors. + new_sux->add_predecessor(this); + } + } + assert(assigned == true, "should have assigned at least once"); + return new_sux; +} + + +void BlockBegin::remove_successor(BlockBegin* pred) { + int idx; + while ((idx = _successors.index_of(pred)) >= 0) { + _successors.remove_at(idx); + } +} + + +void BlockBegin::add_predecessor(BlockBegin* pred) { + _predecessors.append(pred); +} + + +void BlockBegin::remove_predecessor(BlockBegin* pred) { + int idx; + while ((idx = _predecessors.index_of(pred)) >= 0) { + _predecessors.remove_at(idx); + } +} + + +void BlockBegin::add_exception_handler(BlockBegin* b) { + assert(b != NULL && (b->is_set(exception_entry_flag)), "exception handler must exist"); + // add only if not in the list already + if (!_exception_handlers.contains(b)) _exception_handlers.append(b); +} + +int BlockBegin::add_exception_state(ValueStack* state) { + assert(is_set(exception_entry_flag), "only for xhandlers"); + if (_exception_states == NULL) { + _exception_states = new ValueStackStack(4); + } + _exception_states->append(state); + return _exception_states->length() - 1; +} + + +void BlockBegin::iterate_preorder(boolArray& mark, BlockClosure* closure) { + if (!mark.at(block_id())) { + mark.at_put(block_id(), true); + closure->block_do(this); + BlockEnd* e = end(); // must do this after block_do because block_do may change it! + { for (int i = number_of_exception_handlers() - 1; i >= 0; i--) exception_handler_at(i)->iterate_preorder(mark, closure); } + { for (int i = e->number_of_sux () - 1; i >= 0; i--) e->sux_at (i)->iterate_preorder(mark, closure); } + } +} + + +void BlockBegin::iterate_postorder(boolArray& mark, BlockClosure* closure) { + if (!mark.at(block_id())) { + mark.at_put(block_id(), true); + BlockEnd* e = end(); + { for (int i = number_of_exception_handlers() - 1; i >= 0; i--) exception_handler_at(i)->iterate_postorder(mark, closure); } + { for (int i = e->number_of_sux () - 1; i >= 0; i--) e->sux_at (i)->iterate_postorder(mark, closure); } + closure->block_do(this); + } +} + + +void BlockBegin::iterate_preorder(BlockClosure* closure) { + boolArray mark(number_of_blocks(), false); + iterate_preorder(mark, closure); +} + + +void BlockBegin::iterate_postorder(BlockClosure* closure) { + boolArray mark(number_of_blocks(), false); + iterate_postorder(mark, closure); +} + + +void BlockBegin::block_values_do(void f(Value*)) { + for (Instruction* n = this; n != NULL; n = n->next()) n->values_do(f); +} + + +#ifndef PRODUCT + #define TRACE_PHI(code) if (PrintPhiFunctions) { code; } +#else + #define TRACE_PHI(coce) +#endif + + +bool BlockBegin::try_merge(ValueStack* new_state) { + TRACE_PHI(tty->print_cr("********** try_merge for block B%d", block_id())); + + // local variables used for state iteration + int index; + Value new_value, existing_value; + + ValueStack* existing_state = state(); + if (existing_state == NULL) { + TRACE_PHI(tty->print_cr("first call of try_merge for this block")); + + if (is_set(BlockBegin::was_visited_flag)) { + // this actually happens for complicated jsr/ret structures + return false; // BAILOUT in caller + } + + // copy state because it is altered + new_state = new_state->copy(); + + // Use method liveness to invalidate dead locals + MethodLivenessResult liveness = new_state->scope()->method()->liveness_at_bci(bci()); + if (liveness.is_valid()) { + assert((int)liveness.size() == new_state->locals_size(), "error in use of liveness"); + + for_each_local_value(new_state, index, new_value) { + if (!liveness.at(index) || new_value->type()->is_illegal()) { + new_state->invalidate_local(index); + TRACE_PHI(tty->print_cr("invalidating dead local %d", index)); + } + } + } + + if (is_set(BlockBegin::parser_loop_header_flag)) { + TRACE_PHI(tty->print_cr("loop header block, initializing phi functions")); + + for_each_stack_value(new_state, index, new_value) { + new_state->setup_phi_for_stack(this, index); + TRACE_PHI(tty->print_cr("creating phi-function %c%d for stack %d", new_state->stack_at(index)->type()->tchar(), new_state->stack_at(index)->id(), index)); + } + + BitMap requires_phi_function = new_state->scope()->requires_phi_function(); + + for_each_local_value(new_state, index, new_value) { + bool requires_phi = requires_phi_function.at(index) || (new_value->type()->is_double_word() && requires_phi_function.at(index + 1)); + if (requires_phi || !SelectivePhiFunctions) { + new_state->setup_phi_for_local(this, index); + TRACE_PHI(tty->print_cr("creating phi-function %c%d for local %d", new_state->local_at(index)->type()->tchar(), new_state->local_at(index)->id(), index)); + } + } + } + + // initialize state of block + set_state(new_state); + + } else if (existing_state->is_same_across_scopes(new_state)) { + TRACE_PHI(tty->print_cr("exisiting state found")); + + // Inlining may cause the local state not to match up, so walk up + // the new state until we get to the same scope as the + // existing and then start processing from there. + while (existing_state->scope() != new_state->scope()) { + new_state = new_state->caller_state(); + assert(new_state != NULL, "could not match up scopes"); + + assert(false, "check if this is necessary"); + } + + assert(existing_state->scope() == new_state->scope(), "not matching"); + assert(existing_state->locals_size() == new_state->locals_size(), "not matching"); + assert(existing_state->stack_size() == new_state->stack_size(), "not matching"); + + if (is_set(BlockBegin::was_visited_flag)) { + TRACE_PHI(tty->print_cr("loop header block, phis must be present")); + + if (!is_set(BlockBegin::parser_loop_header_flag)) { + // this actually happens for complicated jsr/ret structures + return false; // BAILOUT in caller + } + + for_each_local_value(existing_state, index, existing_value) { + Value new_value = new_state->local_at(index); + if (new_value == NULL || new_value->type()->tag() != existing_value->type()->tag()) { + // The old code invalidated the phi function here + // Because dead locals are replaced with NULL, this is a very rare case now, so simply bail out + return false; // BAILOUT in caller + } + } + +#ifdef ASSERT + // check that all necessary phi functions are present + for_each_stack_value(existing_state, index, existing_value) { + assert(existing_value->as_Phi() != NULL && existing_value->as_Phi()->block() == this, "phi function required"); + } + for_each_local_value(existing_state, index, existing_value) { + assert(existing_value == new_state->local_at(index) || (existing_value->as_Phi() != NULL && existing_value->as_Phi()->as_Phi()->block() == this), "phi function required"); + } +#endif + + } else { + TRACE_PHI(tty->print_cr("creating phi functions on demand")); + + // create necessary phi functions for stack + for_each_stack_value(existing_state, index, existing_value) { + Value new_value = new_state->stack_at(index); + Phi* existing_phi = existing_value->as_Phi(); + + if (new_value != existing_value && (existing_phi == NULL || existing_phi->block() != this)) { + existing_state->setup_phi_for_stack(this, index); + TRACE_PHI(tty->print_cr("creating phi-function %c%d for stack %d", existing_state->stack_at(index)->type()->tchar(), existing_state->stack_at(index)->id(), index)); + } + } + + // create necessary phi functions for locals + for_each_local_value(existing_state, index, existing_value) { + Value new_value = new_state->local_at(index); + Phi* existing_phi = existing_value->as_Phi(); + + if (new_value == NULL || new_value->type()->tag() != existing_value->type()->tag()) { + existing_state->invalidate_local(index); + TRACE_PHI(tty->print_cr("invalidating local %d because of type mismatch", index)); + } else if (new_value != existing_value && (existing_phi == NULL || existing_phi->block() != this)) { + existing_state->setup_phi_for_local(this, index); + TRACE_PHI(tty->print_cr("creating phi-function %c%d for local %d", existing_state->local_at(index)->type()->tchar(), existing_state->local_at(index)->id(), index)); + } + } + } + + assert(existing_state->caller_state() == new_state->caller_state(), "caller states must be equal"); + + } else { + assert(false, "stack or locks not matching (invalid bytecodes)"); + return false; + } + + TRACE_PHI(tty->print_cr("********** try_merge for block B%d successful", block_id())); + + return true; +} + + +#ifndef PRODUCT +void BlockBegin::print_block() { + InstructionPrinter ip; + print_block(ip, false); +} + + +void BlockBegin::print_block(InstructionPrinter& ip, bool live_only) { + ip.print_instr(this); tty->cr(); + ip.print_stack(this->state()); tty->cr(); + ip.print_inline_level(this); + ip.print_head(); + for (Instruction* n = next(); n != NULL; n = n->next()) { + if (!live_only || n->is_pinned() || n->use_count() > 0) { + ip.print_line(n); + } + } + tty->cr(); +} +#endif // PRODUCT + + +// Implementation of BlockList + +void BlockList::iterate_forward (BlockClosure* closure) { + const int l = length(); + for (int i = 0; i < l; i++) closure->block_do(at(i)); +} + + +void BlockList::iterate_backward(BlockClosure* closure) { + for (int i = length() - 1; i >= 0; i--) closure->block_do(at(i)); +} + + +void BlockList::blocks_do(void f(BlockBegin*)) { + for (int i = length() - 1; i >= 0; i--) f(at(i)); +} + + +void BlockList::values_do(void f(Value*)) { + for (int i = length() - 1; i >= 0; i--) at(i)->block_values_do(f); +} + + +#ifndef PRODUCT +void BlockList::print(bool cfg_only, bool live_only) { + InstructionPrinter ip; + for (int i = 0; i < length(); i++) { + BlockBegin* block = at(i); + if (cfg_only) { + ip.print_instr(block); tty->cr(); + } else { + block->print_block(ip, live_only); + } + } +} +#endif // PRODUCT + + +// Implementation of BlockEnd + +void BlockEnd::set_begin(BlockBegin* begin) { + BlockList* sux = NULL; + if (begin != NULL) { + sux = begin->successors(); + } else if (_begin != NULL) { + // copy our sux list + BlockList* sux = new BlockList(_begin->number_of_sux()); + for (int i = 0; i < _begin->number_of_sux(); i++) { + sux->append(_begin->sux_at(i)); + } + } + _sux = sux; + _begin = begin; +} + + +void BlockEnd::substitute_sux(BlockBegin* old_sux, BlockBegin* new_sux) { + substitute(*_sux, old_sux, new_sux); +} + + +void BlockEnd::other_values_do(void f(Value*)) { + if (state_before() != NULL) state_before()->values_do(f); +} + + +// Implementation of Phi + +// Normal phi functions take their operands from the last instruction of the +// predecessor. Special handling is needed for xhanlder entries because there +// the state of arbitrary instructions are needed. + +Value Phi::operand_at(int i) const { + ValueStack* state; + if (_block->is_set(BlockBegin::exception_entry_flag)) { + state = _block->exception_state_at(i); + } else { + state = _block->pred_at(i)->end()->state(); + } + assert(state != NULL, ""); + + if (is_local()) { + return state->local_at(local_index()); + } else { + return state->stack_at(stack_index()); + } +} + + +int Phi::operand_count() const { + if (_block->is_set(BlockBegin::exception_entry_flag)) { + return _block->number_of_exception_states(); + } else { + return _block->number_of_preds(); + } +} + + +// Implementation of Throw + +void Throw::state_values_do(void f(Value*)) { + BlockEnd::state_values_do(f); +} diff --git a/hotspot/src/share/vm/c1/c1_Instruction.hpp b/hotspot/src/share/vm/c1/c1_Instruction.hpp new file mode 100644 index 00000000000..86087b65f44 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Instruction.hpp @@ -0,0 +1,2291 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Predefined classes +class ciField; +class ValueStack; +class InstructionPrinter; +class IRScope; +class LIR_OprDesc; +typedef LIR_OprDesc* LIR_Opr; + + +// Instruction class hierarchy +// +// All leaf classes in the class hierarchy are concrete classes +// (i.e., are instantiated). All other classes are abstract and +// serve factoring. + +class Instruction; +class HiWord; +class Phi; +class Local; +class Constant; +class AccessField; +class LoadField; +class StoreField; +class AccessArray; +class ArrayLength; +class AccessIndexed; +class LoadIndexed; +class StoreIndexed; +class NegateOp; +class Op2; +class ArithmeticOp; +class ShiftOp; +class LogicOp; +class CompareOp; +class IfOp; +class Convert; +class NullCheck; +class OsrEntry; +class ExceptionObject; +class StateSplit; +class Invoke; +class NewInstance; +class NewArray; +class NewTypeArray; +class NewObjectArray; +class NewMultiArray; +class TypeCheck; +class CheckCast; +class InstanceOf; +class AccessMonitor; +class MonitorEnter; +class MonitorExit; +class Intrinsic; +class BlockBegin; +class BlockEnd; +class Goto; +class If; +class IfInstanceOf; +class Switch; +class TableSwitch; +class LookupSwitch; +class Return; +class Throw; +class Base; +class RoundFP; +class UnsafeOp; +class UnsafeRawOp; +class UnsafeGetRaw; +class UnsafePutRaw; +class UnsafeObjectOp; +class UnsafeGetObject; +class UnsafePutObject; +class UnsafePrefetch; +class UnsafePrefetchRead; +class UnsafePrefetchWrite; +class ProfileCall; +class ProfileCounter; + +// A Value is a reference to the instruction creating the value +typedef Instruction* Value; +define_array(ValueArray, Value) +define_stack(Values, ValueArray) + +define_array(ValueStackArray, ValueStack*) +define_stack(ValueStackStack, ValueStackArray) + +// BlockClosure is the base class for block traversal/iteration. + +class BlockClosure: public CompilationResourceObj { + public: + virtual void block_do(BlockBegin* block) = 0; +}; + + +// Some array and list classes +define_array(BlockBeginArray, BlockBegin*) +define_stack(_BlockList, BlockBeginArray) + +class BlockList: public _BlockList { + public: + BlockList(): _BlockList() {} + BlockList(const int size): _BlockList(size) {} + BlockList(const int size, BlockBegin* init): _BlockList(size, init) {} + + void iterate_forward(BlockClosure* closure); + void iterate_backward(BlockClosure* closure); + void blocks_do(void f(BlockBegin*)); + void values_do(void f(Value*)); + void print(bool cfg_only = false, bool live_only = false) PRODUCT_RETURN; +}; + + +// InstructionVisitors provide type-based dispatch for instructions. +// For each concrete Instruction class X, a virtual function do_X is +// provided. Functionality that needs to be implemented for all classes +// (e.g., printing, code generation) is factored out into a specialised +// visitor instead of added to the Instruction classes itself. + +class InstructionVisitor: public StackObj { + public: + void do_HiWord (HiWord* x) { ShouldNotReachHere(); } + virtual void do_Phi (Phi* x) = 0; + virtual void do_Local (Local* x) = 0; + virtual void do_Constant (Constant* x) = 0; + virtual void do_LoadField (LoadField* x) = 0; + virtual void do_StoreField (StoreField* x) = 0; + virtual void do_ArrayLength (ArrayLength* x) = 0; + virtual void do_LoadIndexed (LoadIndexed* x) = 0; + virtual void do_StoreIndexed (StoreIndexed* x) = 0; + virtual void do_NegateOp (NegateOp* x) = 0; + virtual void do_ArithmeticOp (ArithmeticOp* x) = 0; + virtual void do_ShiftOp (ShiftOp* x) = 0; + virtual void do_LogicOp (LogicOp* x) = 0; + virtual void do_CompareOp (CompareOp* x) = 0; + virtual void do_IfOp (IfOp* x) = 0; + virtual void do_Convert (Convert* x) = 0; + virtual void do_NullCheck (NullCheck* x) = 0; + virtual void do_Invoke (Invoke* x) = 0; + virtual void do_NewInstance (NewInstance* x) = 0; + virtual void do_NewTypeArray (NewTypeArray* x) = 0; + virtual void do_NewObjectArray (NewObjectArray* x) = 0; + virtual void do_NewMultiArray (NewMultiArray* x) = 0; + virtual void do_CheckCast (CheckCast* x) = 0; + virtual void do_InstanceOf (InstanceOf* x) = 0; + virtual void do_MonitorEnter (MonitorEnter* x) = 0; + virtual void do_MonitorExit (MonitorExit* x) = 0; + virtual void do_Intrinsic (Intrinsic* x) = 0; + virtual void do_BlockBegin (BlockBegin* x) = 0; + virtual void do_Goto (Goto* x) = 0; + virtual void do_If (If* x) = 0; + virtual void do_IfInstanceOf (IfInstanceOf* x) = 0; + virtual void do_TableSwitch (TableSwitch* x) = 0; + virtual void do_LookupSwitch (LookupSwitch* x) = 0; + virtual void do_Return (Return* x) = 0; + virtual void do_Throw (Throw* x) = 0; + virtual void do_Base (Base* x) = 0; + virtual void do_OsrEntry (OsrEntry* x) = 0; + virtual void do_ExceptionObject(ExceptionObject* x) = 0; + virtual void do_RoundFP (RoundFP* x) = 0; + virtual void do_UnsafeGetRaw (UnsafeGetRaw* x) = 0; + virtual void do_UnsafePutRaw (UnsafePutRaw* x) = 0; + virtual void do_UnsafeGetObject(UnsafeGetObject* x) = 0; + virtual void do_UnsafePutObject(UnsafePutObject* x) = 0; + virtual void do_UnsafePrefetchRead (UnsafePrefetchRead* x) = 0; + virtual void do_UnsafePrefetchWrite(UnsafePrefetchWrite* x) = 0; + virtual void do_ProfileCall (ProfileCall* x) = 0; + virtual void do_ProfileCounter (ProfileCounter* x) = 0; +}; + + +// Hashing support +// +// Note: This hash functions affect the performance +// of ValueMap - make changes carefully! + +#define HASH1(x1 ) ((intx)(x1)) +#define HASH2(x1, x2 ) ((HASH1(x1 ) << 7) ^ HASH1(x2)) +#define HASH3(x1, x2, x3 ) ((HASH2(x1, x2 ) << 7) ^ HASH1(x3)) +#define HASH4(x1, x2, x3, x4) ((HASH3(x1, x2, x3) << 7) ^ HASH1(x4)) + + +// The following macros are used to implement instruction-specific hashing. +// By default, each instruction implements hash() and is_equal(Value), used +// for value numbering/common subexpression elimination. The default imple- +// mentation disables value numbering. Each instruction which can be value- +// numbered, should define corresponding hash() and is_equal(Value) functions +// via the macros below. The f arguments specify all the values/op codes, etc. +// that need to be identical for two instructions to be identical. +// +// Note: The default implementation of hash() returns 0 in order to indicate +// that the instruction should not be considered for value numbering. +// The currently used hash functions do not guarantee that never a 0 +// is produced. While this is still correct, it may be a performance +// bug (no value numbering for that node). However, this situation is +// so unlikely, that we are not going to handle it specially. + +#define HASHING1(class_name, enabled, f1) \ + virtual intx hash() const { \ + return (enabled) ? HASH2(name(), f1) : 0; \ + } \ + virtual bool is_equal(Value v) const { \ + if (!(enabled) ) return false; \ + class_name* _v = v->as_##class_name(); \ + if (_v == NULL ) return false; \ + if (f1 != _v->f1) return false; \ + return true; \ + } \ + + +#define HASHING2(class_name, enabled, f1, f2) \ + virtual intx hash() const { \ + return (enabled) ? HASH3(name(), f1, f2) : 0; \ + } \ + virtual bool is_equal(Value v) const { \ + if (!(enabled) ) return false; \ + class_name* _v = v->as_##class_name(); \ + if (_v == NULL ) return false; \ + if (f1 != _v->f1) return false; \ + if (f2 != _v->f2) return false; \ + return true; \ + } \ + + +#define HASHING3(class_name, enabled, f1, f2, f3) \ + virtual intx hash() const { \ + return (enabled) ? HASH4(name(), f1, f2, f3) : 0; \ + } \ + virtual bool is_equal(Value v) const { \ + if (!(enabled) ) return false; \ + class_name* _v = v->as_##class_name(); \ + if (_v == NULL ) return false; \ + if (f1 != _v->f1) return false; \ + if (f2 != _v->f2) return false; \ + if (f3 != _v->f3) return false; \ + return true; \ + } \ + + +// The mother of all instructions... + +class Instruction: public CompilationResourceObj { + private: + static int _next_id; // the node counter + + int _id; // the unique instruction id + int _bci; // the instruction bci + int _use_count; // the number of instructions refering to this value (w/o prev/next); only roots can have use count = 0 or > 1 + int _pin_state; // set of PinReason describing the reason for pinning + ValueType* _type; // the instruction value type + Instruction* _next; // the next instruction if any (NULL for BlockEnd instructions) + Instruction* _subst; // the substitution instruction if any + LIR_Opr _operand; // LIR specific information + unsigned int _flags; // Flag bits + + XHandlers* _exception_handlers; // Flat list of exception handlers covering this instruction + +#ifdef ASSERT + HiWord* _hi_word; +#endif + + friend class UseCountComputer; + + protected: + void set_bci(int bci) { assert(bci == SynchronizationEntryBCI || bci >= 0, "illegal bci"); _bci = bci; } + void set_type(ValueType* type) { + assert(type != NULL, "type must exist"); + _type = type; + } + + public: + enum InstructionFlag { + NeedsNullCheckFlag = 0, + CanTrapFlag, + DirectCompareFlag, + IsEliminatedFlag, + IsInitializedFlag, + IsLoadedFlag, + IsSafepointFlag, + IsStaticFlag, + IsStrictfpFlag, + NeedsStoreCheckFlag, + NeedsWriteBarrierFlag, + PreservesStateFlag, + TargetIsFinalFlag, + TargetIsLoadedFlag, + TargetIsStrictfpFlag, + UnorderedIsTrueFlag, + NeedsPatchingFlag, + ThrowIncompatibleClassChangeErrorFlag, + ProfileMDOFlag, + InstructionLastFlag + }; + + public: + bool check_flag(InstructionFlag id) const { return (_flags & (1 << id)) != 0; } + void set_flag(InstructionFlag id, bool f) { _flags = f ? (_flags | (1 << id)) : (_flags & ~(1 << id)); }; + + // 'globally' used condition values + enum Condition { + eql, neq, lss, leq, gtr, geq + }; + + // Instructions may be pinned for many reasons and under certain conditions + // with enough knowledge it's possible to safely unpin them. + enum PinReason { + PinUnknown = 1 << 0 + , PinExplicitNullCheck = 1 << 3 + , PinStackForStateSplit= 1 << 12 + , PinStateSplitConstructor= 1 << 13 + , PinGlobalValueNumbering= 1 << 14 + }; + + static Condition mirror(Condition cond); + static Condition negate(Condition cond); + + // initialization + static void initialize() { _next_id = 0; } + static int number_of_instructions() { return _next_id; } + + // creation + Instruction(ValueType* type, bool type_is_constant = false, bool create_hi = true) + : _id(_next_id++) + , _bci(-99) + , _use_count(0) + , _pin_state(0) + , _type(type) + , _next(NULL) + , _subst(NULL) + , _flags(0) + , _operand(LIR_OprFact::illegalOpr) + , _exception_handlers(NULL) +#ifdef ASSERT + , _hi_word(NULL) +#endif + { + assert(type != NULL && (!type->is_constant() || type_is_constant), "type must exist"); +#ifdef ASSERT + if (create_hi && type->is_double_word()) { + create_hi_word(); + } +#endif + } + + // accessors + int id() const { return _id; } + int bci() const { return _bci; } + int use_count() const { return _use_count; } + int pin_state() const { return _pin_state; } + bool is_pinned() const { return _pin_state != 0 || PinAllInstructions; } + ValueType* type() const { return _type; } + Instruction* prev(BlockBegin* block); // use carefully, expensive operation + Instruction* next() const { return _next; } + bool has_subst() const { return _subst != NULL; } + Instruction* subst() { return _subst == NULL ? this : _subst->subst(); } + LIR_Opr operand() const { return _operand; } + + void set_needs_null_check(bool f) { set_flag(NeedsNullCheckFlag, f); } + bool needs_null_check() const { return check_flag(NeedsNullCheckFlag); } + + bool has_uses() const { return use_count() > 0; } + bool is_root() const { return is_pinned() || use_count() > 1; } + XHandlers* exception_handlers() const { return _exception_handlers; } + + // manipulation + void pin(PinReason reason) { _pin_state |= reason; } + void pin() { _pin_state |= PinUnknown; } + // DANGEROUS: only used by EliminateStores + void unpin(PinReason reason) { assert((reason & PinUnknown) == 0, "can't unpin unknown state"); _pin_state &= ~reason; } + virtual void set_lock_stack(ValueStack* l) { /* do nothing*/ } + virtual ValueStack* lock_stack() const { return NULL; } + + Instruction* set_next(Instruction* next, int bci) { + if (next != NULL) { + assert(as_BlockEnd() == NULL, "BlockEnd instructions must have no next"); + assert(next->as_Phi() == NULL && next->as_Local() == NULL, "shouldn't link these instructions into list"); + next->set_bci(bci); + } + _next = next; + return next; + } + + void set_subst(Instruction* subst) { + assert(subst == NULL || + type()->base() == subst->type()->base() || + subst->type()->base() == illegalType, "type can't change"); + _subst = subst; + } + void set_exception_handlers(XHandlers *xhandlers) { _exception_handlers = xhandlers; } + +#ifdef ASSERT + // HiWord is used for debugging and is allocated early to avoid + // allocation at inconvenient points + HiWord* hi_word() { return _hi_word; } + void create_hi_word(); +#endif + + + // machine-specifics + void set_operand(LIR_Opr operand) { assert(operand != LIR_OprFact::illegalOpr, "operand must exist"); _operand = operand; } + void clear_operand() { _operand = LIR_OprFact::illegalOpr; } + + // generic + virtual Instruction* as_Instruction() { return this; } // to satisfy HASHING1 macro + virtual HiWord* as_HiWord() { return NULL; } + virtual Phi* as_Phi() { return NULL; } + virtual Local* as_Local() { return NULL; } + virtual Constant* as_Constant() { return NULL; } + virtual AccessField* as_AccessField() { return NULL; } + virtual LoadField* as_LoadField() { return NULL; } + virtual StoreField* as_StoreField() { return NULL; } + virtual AccessArray* as_AccessArray() { return NULL; } + virtual ArrayLength* as_ArrayLength() { return NULL; } + virtual AccessIndexed* as_AccessIndexed() { return NULL; } + virtual LoadIndexed* as_LoadIndexed() { return NULL; } + virtual StoreIndexed* as_StoreIndexed() { return NULL; } + virtual NegateOp* as_NegateOp() { return NULL; } + virtual Op2* as_Op2() { return NULL; } + virtual ArithmeticOp* as_ArithmeticOp() { return NULL; } + virtual ShiftOp* as_ShiftOp() { return NULL; } + virtual LogicOp* as_LogicOp() { return NULL; } + virtual CompareOp* as_CompareOp() { return NULL; } + virtual IfOp* as_IfOp() { return NULL; } + virtual Convert* as_Convert() { return NULL; } + virtual NullCheck* as_NullCheck() { return NULL; } + virtual OsrEntry* as_OsrEntry() { return NULL; } + virtual StateSplit* as_StateSplit() { return NULL; } + virtual Invoke* as_Invoke() { return NULL; } + virtual NewInstance* as_NewInstance() { return NULL; } + virtual NewArray* as_NewArray() { return NULL; } + virtual NewTypeArray* as_NewTypeArray() { return NULL; } + virtual NewObjectArray* as_NewObjectArray() { return NULL; } + virtual NewMultiArray* as_NewMultiArray() { return NULL; } + virtual TypeCheck* as_TypeCheck() { return NULL; } + virtual CheckCast* as_CheckCast() { return NULL; } + virtual InstanceOf* as_InstanceOf() { return NULL; } + virtual AccessMonitor* as_AccessMonitor() { return NULL; } + virtual MonitorEnter* as_MonitorEnter() { return NULL; } + virtual MonitorExit* as_MonitorExit() { return NULL; } + virtual Intrinsic* as_Intrinsic() { return NULL; } + virtual BlockBegin* as_BlockBegin() { return NULL; } + virtual BlockEnd* as_BlockEnd() { return NULL; } + virtual Goto* as_Goto() { return NULL; } + virtual If* as_If() { return NULL; } + virtual IfInstanceOf* as_IfInstanceOf() { return NULL; } + virtual TableSwitch* as_TableSwitch() { return NULL; } + virtual LookupSwitch* as_LookupSwitch() { return NULL; } + virtual Return* as_Return() { return NULL; } + virtual Throw* as_Throw() { return NULL; } + virtual Base* as_Base() { return NULL; } + virtual RoundFP* as_RoundFP() { return NULL; } + virtual ExceptionObject* as_ExceptionObject() { return NULL; } + virtual UnsafeOp* as_UnsafeOp() { return NULL; } + + virtual void visit(InstructionVisitor* v) = 0; + + virtual bool can_trap() const { return false; } + + virtual void input_values_do(void f(Value*)) = 0; + virtual void state_values_do(void f(Value*)) { /* usually no state - override on demand */ } + virtual void other_values_do(void f(Value*)) { /* usually no other - override on demand */ } + void values_do(void f(Value*)) { input_values_do(f); state_values_do(f); other_values_do(f); } + + virtual ciType* exact_type() const { return NULL; } + virtual ciType* declared_type() const { return NULL; } + + // hashing + virtual const char* name() const = 0; + HASHING1(Instruction, false, id()) // hashing disabled by default + + // debugging + void print() PRODUCT_RETURN; + void print_line() PRODUCT_RETURN; + void print(InstructionPrinter& ip) PRODUCT_RETURN; +}; + + +// The following macros are used to define base (i.e., non-leaf) +// and leaf instruction classes. They define class-name related +// generic functionality in one place. + +#define BASE(class_name, super_class_name) \ + class class_name: public super_class_name { \ + public: \ + virtual class_name* as_##class_name() { return this; } \ + + +#define LEAF(class_name, super_class_name) \ + BASE(class_name, super_class_name) \ + public: \ + virtual const char* name() const { return #class_name; } \ + virtual void visit(InstructionVisitor* v) { v->do_##class_name(this); } \ + + +// Debugging support + +#ifdef ASSERT + static void assert_value(Value* x) { assert((*x) != NULL, "value must exist"); } + #define ASSERT_VALUES values_do(assert_value); +#else + #define ASSERT_VALUES +#endif // ASSERT + + +// A HiWord occupies the 'high word' of a 2-word +// expression stack entry. Hi & lo words must be +// paired on the expression stack (otherwise the +// bytecode sequence is illegal). Note that 'hi' +// refers to the IR expression stack format and +// does *not* imply a machine word ordering. No +// HiWords are used in optimized mode for speed, +// but NULL pointers are used instead. + +LEAF(HiWord, Instruction) + private: + Value _lo_word; + + public: + // creation + HiWord(Value lo_word) + : Instruction(illegalType, false, false), + _lo_word(lo_word) { + // hi-words are also allowed for illegal lo-words + assert(lo_word->type()->is_double_word() || lo_word->type()->is_illegal(), + "HiWord must be used for 2-word values only"); + } + + // accessors + Value lo_word() const { return _lo_word->subst(); } + + // for invalidating of HiWords + void make_illegal() { set_type(illegalType); } + + // generic + virtual void input_values_do(void f(Value*)) { ShouldNotReachHere(); } +}; + + +// A Phi is a phi function in the sense of SSA form. It stands for +// the value of a local variable at the beginning of a join block. +// A Phi consists of n operands, one for every incoming branch. + +LEAF(Phi, Instruction) + private: + BlockBegin* _block; // the block to which the phi function belongs + int _pf_flags; // the flags of the phi function + int _index; // to value on operand stack (index < 0) or to local + public: + // creation + Phi(ValueType* type, BlockBegin* b, int index) + : Instruction(type->base()) + , _pf_flags(0) + , _block(b) + , _index(index) + { + if (type->is_illegal()) { + make_illegal(); + } + } + + // flags + enum Flag { + no_flag = 0, + visited = 1 << 0, + cannot_simplify = 1 << 1 + }; + + // accessors + bool is_local() const { return _index >= 0; } + bool is_on_stack() const { return !is_local(); } + int local_index() const { assert(is_local(), ""); return _index; } + int stack_index() const { assert(is_on_stack(), ""); return -(_index+1); } + + Value operand_at(int i) const; + int operand_count() const; + + BlockBegin* block() const { return _block; } + + void set(Flag f) { _pf_flags |= f; } + void clear(Flag f) { _pf_flags &= ~f; } + bool is_set(Flag f) const { return (_pf_flags & f) != 0; } + + // Invalidates phis corresponding to merges of locals of two different types + // (these should never be referenced, otherwise the bytecodes are illegal) + void make_illegal() { + set(cannot_simplify); + set_type(illegalType); + } + + bool is_illegal() const { + return type()->is_illegal(); + } + + // generic + virtual void input_values_do(void f(Value*)) { + } +}; + + +// A local is a placeholder for an incoming argument to a function call. +LEAF(Local, Instruction) + private: + int _java_index; // the local index within the method to which the local belongs + public: + // creation + Local(ValueType* type, int index) + : Instruction(type) + , _java_index(index) + {} + + // accessors + int java_index() const { return _java_index; } + + // generic + virtual void input_values_do(void f(Value*)) { /* no values */ } +}; + + +LEAF(Constant, Instruction) + ValueStack* _state; + + public: + // creation + Constant(ValueType* type): + Instruction(type, true) + , _state(NULL) { + assert(type->is_constant(), "must be a constant"); + } + + Constant(ValueType* type, ValueStack* state): + Instruction(type, true) + , _state(state) { + assert(state != NULL, "only used for constants which need patching"); + assert(type->is_constant(), "must be a constant"); + // since it's patching it needs to be pinned + pin(); + } + + ValueStack* state() const { return _state; } + + // generic + virtual bool can_trap() const { return state() != NULL; } + virtual void input_values_do(void f(Value*)) { /* no values */ } + virtual void other_values_do(void f(Value*)); + + virtual intx hash() const; + virtual bool is_equal(Value v) const; + + virtual BlockBegin* compare(Instruction::Condition condition, Value right, + BlockBegin* true_sux, BlockBegin* false_sux); +}; + + +BASE(AccessField, Instruction) + private: + Value _obj; + int _offset; + ciField* _field; + ValueStack* _state_before; // state is set only for unloaded or uninitialized fields + ValueStack* _lock_stack; // contains lock and scope information + NullCheck* _explicit_null_check; // For explicit null check elimination + + public: + // creation + AccessField(Value obj, int offset, ciField* field, bool is_static, ValueStack* lock_stack, + ValueStack* state_before, bool is_loaded, bool is_initialized) + : Instruction(as_ValueType(field->type()->basic_type())) + , _obj(obj) + , _offset(offset) + , _field(field) + , _lock_stack(lock_stack) + , _state_before(state_before) + , _explicit_null_check(NULL) + { + set_needs_null_check(!is_static); + set_flag(IsLoadedFlag, is_loaded); + set_flag(IsInitializedFlag, is_initialized); + set_flag(IsStaticFlag, is_static); + ASSERT_VALUES + if (!is_loaded || (PatchALot && !field->is_volatile())) { + // need to patch if the holder wasn't loaded or we're testing + // using PatchALot. Don't allow PatchALot for fields which are + // known to be volatile they aren't patchable. + set_flag(NeedsPatchingFlag, true); + } + // pin of all instructions with memory access + pin(); + } + + // accessors + Value obj() const { return _obj; } + int offset() const { return _offset; } + ciField* field() const { return _field; } + BasicType field_type() const { return _field->type()->basic_type(); } + bool is_static() const { return check_flag(IsStaticFlag); } + bool is_loaded() const { return check_flag(IsLoadedFlag); } + bool is_initialized() const { return check_flag(IsInitializedFlag); } + ValueStack* state_before() const { return _state_before; } + ValueStack* lock_stack() const { return _lock_stack; } + NullCheck* explicit_null_check() const { return _explicit_null_check; } + bool needs_patching() const { return check_flag(NeedsPatchingFlag); } + + // manipulation + void set_lock_stack(ValueStack* l) { _lock_stack = l; } + // Under certain circumstances, if a previous NullCheck instruction + // proved the target object non-null, we can eliminate the explicit + // null check and do an implicit one, simply specifying the debug + // information from the NullCheck. This field should only be consulted + // if needs_null_check() is true. + void set_explicit_null_check(NullCheck* check) { _explicit_null_check = check; } + + // generic + virtual bool can_trap() const { return needs_null_check() || needs_patching(); } + virtual void input_values_do(void f(Value*)) { f(&_obj); } + virtual void other_values_do(void f(Value*)); +}; + + +LEAF(LoadField, AccessField) + public: + // creation + LoadField(Value obj, int offset, ciField* field, bool is_static, ValueStack* lock_stack, + ValueStack* state_before, bool is_loaded, bool is_initialized) + : AccessField(obj, offset, field, is_static, lock_stack, state_before, is_loaded, is_initialized) + {} + + ciType* declared_type() const; + ciType* exact_type() const; + + // generic + HASHING2(LoadField, is_loaded() && !field()->is_volatile(), obj()->subst(), offset()) // cannot be eliminated if not yet loaded or if volatile +}; + + +LEAF(StoreField, AccessField) + private: + Value _value; + + public: + // creation + StoreField(Value obj, int offset, ciField* field, Value value, bool is_static, ValueStack* lock_stack, + ValueStack* state_before, bool is_loaded, bool is_initialized) + : AccessField(obj, offset, field, is_static, lock_stack, state_before, is_loaded, is_initialized) + , _value(value) + { + set_flag(NeedsWriteBarrierFlag, as_ValueType(field_type())->is_object()); + ASSERT_VALUES + pin(); + } + + // accessors + Value value() const { return _value; } + bool needs_write_barrier() const { return check_flag(NeedsWriteBarrierFlag); } + + // generic + virtual void input_values_do(void f(Value*)) { AccessField::input_values_do(f); f(&_value); } +}; + + +BASE(AccessArray, Instruction) + private: + Value _array; + ValueStack* _lock_stack; + + public: + // creation + AccessArray(ValueType* type, Value array, ValueStack* lock_stack) + : Instruction(type) + , _array(array) + , _lock_stack(lock_stack) { + set_needs_null_check(true); + ASSERT_VALUES + pin(); // instruction with side effect (null exception or range check throwing) + } + + Value array() const { return _array; } + ValueStack* lock_stack() const { return _lock_stack; } + + // setters + void set_lock_stack(ValueStack* l) { _lock_stack = l; } + + // generic + virtual bool can_trap() const { return needs_null_check(); } + virtual void input_values_do(void f(Value*)) { f(&_array); } + virtual void other_values_do(void f(Value*)); +}; + + +LEAF(ArrayLength, AccessArray) + private: + NullCheck* _explicit_null_check; // For explicit null check elimination + + public: + // creation + ArrayLength(Value array, ValueStack* lock_stack) + : AccessArray(intType, array, lock_stack) + , _explicit_null_check(NULL) {} + + // accessors + NullCheck* explicit_null_check() const { return _explicit_null_check; } + + // setters + // See LoadField::set_explicit_null_check for documentation + void set_explicit_null_check(NullCheck* check) { _explicit_null_check = check; } + + // generic + HASHING1(ArrayLength, true, array()->subst()) +}; + + +BASE(AccessIndexed, AccessArray) + private: + Value _index; + Value _length; + BasicType _elt_type; + + public: + // creation + AccessIndexed(Value array, Value index, Value length, BasicType elt_type, ValueStack* lock_stack) + : AccessArray(as_ValueType(elt_type), array, lock_stack) + , _index(index) + , _length(length) + , _elt_type(elt_type) + { + ASSERT_VALUES + } + + // accessors + Value index() const { return _index; } + Value length() const { return _length; } + BasicType elt_type() const { return _elt_type; } + + // perform elimination of range checks involving constants + bool compute_needs_range_check(); + + // generic + virtual void input_values_do(void f(Value*)) { AccessArray::input_values_do(f); f(&_index); if (_length != NULL) f(&_length); } +}; + + +LEAF(LoadIndexed, AccessIndexed) + private: + NullCheck* _explicit_null_check; // For explicit null check elimination + + public: + // creation + LoadIndexed(Value array, Value index, Value length, BasicType elt_type, ValueStack* lock_stack) + : AccessIndexed(array, index, length, elt_type, lock_stack) + , _explicit_null_check(NULL) {} + + // accessors + NullCheck* explicit_null_check() const { return _explicit_null_check; } + + // setters + // See LoadField::set_explicit_null_check for documentation + void set_explicit_null_check(NullCheck* check) { _explicit_null_check = check; } + + ciType* exact_type() const; + ciType* declared_type() const; + + // generic + HASHING2(LoadIndexed, true, array()->subst(), index()->subst()) +}; + + +LEAF(StoreIndexed, AccessIndexed) + private: + Value _value; + + public: + // creation + StoreIndexed(Value array, Value index, Value length, BasicType elt_type, Value value, ValueStack* lock_stack) + : AccessIndexed(array, index, length, elt_type, lock_stack) + , _value(value) + { + set_flag(NeedsWriteBarrierFlag, (as_ValueType(elt_type)->is_object())); + set_flag(NeedsStoreCheckFlag, (as_ValueType(elt_type)->is_object())); + ASSERT_VALUES + pin(); + } + + // accessors + Value value() const { return _value; } + IRScope* scope() const; // the state's scope + bool needs_write_barrier() const { return check_flag(NeedsWriteBarrierFlag); } + bool needs_store_check() const { return check_flag(NeedsStoreCheckFlag); } + + // generic + virtual void input_values_do(void f(Value*)) { AccessIndexed::input_values_do(f); f(&_value); } +}; + + +LEAF(NegateOp, Instruction) + private: + Value _x; + + public: + // creation + NegateOp(Value x) : Instruction(x->type()->base()), _x(x) { + ASSERT_VALUES + } + + // accessors + Value x() const { return _x; } + + // generic + virtual void input_values_do(void f(Value*)) { f(&_x); } +}; + + +BASE(Op2, Instruction) + private: + Bytecodes::Code _op; + Value _x; + Value _y; + + public: + // creation + Op2(ValueType* type, Bytecodes::Code op, Value x, Value y) : Instruction(type), _op(op), _x(x), _y(y) { + ASSERT_VALUES + } + + // accessors + Bytecodes::Code op() const { return _op; } + Value x() const { return _x; } + Value y() const { return _y; } + + // manipulators + void swap_operands() { + assert(is_commutative(), "operation must be commutative"); + Value t = _x; _x = _y; _y = t; + } + + // generic + virtual bool is_commutative() const { return false; } + virtual void input_values_do(void f(Value*)) { f(&_x); f(&_y); } +}; + + +LEAF(ArithmeticOp, Op2) + private: + ValueStack* _lock_stack; // used only for division operations + public: + // creation + ArithmeticOp(Bytecodes::Code op, Value x, Value y, bool is_strictfp, ValueStack* lock_stack) + : Op2(x->type()->meet(y->type()), op, x, y) + , _lock_stack(lock_stack) { + set_flag(IsStrictfpFlag, is_strictfp); + if (can_trap()) pin(); + } + + // accessors + ValueStack* lock_stack() const { return _lock_stack; } + bool is_strictfp() const { return check_flag(IsStrictfpFlag); } + + // setters + void set_lock_stack(ValueStack* l) { _lock_stack = l; } + + // generic + virtual bool is_commutative() const; + virtual bool can_trap() const; + virtual void other_values_do(void f(Value*)); + HASHING3(Op2, true, op(), x()->subst(), y()->subst()) +}; + + +LEAF(ShiftOp, Op2) + public: + // creation + ShiftOp(Bytecodes::Code op, Value x, Value s) : Op2(x->type()->base(), op, x, s) {} + + // generic + HASHING3(Op2, true, op(), x()->subst(), y()->subst()) +}; + + +LEAF(LogicOp, Op2) + public: + // creation + LogicOp(Bytecodes::Code op, Value x, Value y) : Op2(x->type()->meet(y->type()), op, x, y) {} + + // generic + virtual bool is_commutative() const; + HASHING3(Op2, true, op(), x()->subst(), y()->subst()) +}; + + +LEAF(CompareOp, Op2) + private: + ValueStack* _state_before; // for deoptimization, when canonicalizing + public: + // creation + CompareOp(Bytecodes::Code op, Value x, Value y, ValueStack* state_before) + : Op2(intType, op, x, y) + , _state_before(state_before) + {} + + // accessors + ValueStack* state_before() const { return _state_before; } + + // generic + HASHING3(Op2, true, op(), x()->subst(), y()->subst()) + virtual void other_values_do(void f(Value*)); +}; + + +LEAF(IfOp, Op2) + private: + Value _tval; + Value _fval; + + public: + // creation + IfOp(Value x, Condition cond, Value y, Value tval, Value fval) + : Op2(tval->type()->meet(fval->type()), (Bytecodes::Code)cond, x, y) + , _tval(tval) + , _fval(fval) + { + ASSERT_VALUES + assert(tval->type()->tag() == fval->type()->tag(), "types must match"); + } + + // accessors + virtual bool is_commutative() const; + Bytecodes::Code op() const { ShouldNotCallThis(); return Bytecodes::_illegal; } + Condition cond() const { return (Condition)Op2::op(); } + Value tval() const { return _tval; } + Value fval() const { return _fval; } + + // generic + virtual void input_values_do(void f(Value*)) { Op2::input_values_do(f); f(&_tval); f(&_fval); } +}; + + +LEAF(Convert, Instruction) + private: + Bytecodes::Code _op; + Value _value; + + public: + // creation + Convert(Bytecodes::Code op, Value value, ValueType* to_type) : Instruction(to_type), _op(op), _value(value) { + ASSERT_VALUES + } + + // accessors + Bytecodes::Code op() const { return _op; } + Value value() const { return _value; } + + // generic + virtual void input_values_do(void f(Value*)) { f(&_value); } + HASHING2(Convert, true, op(), value()->subst()) +}; + + +LEAF(NullCheck, Instruction) + private: + Value _obj; + ValueStack* _lock_stack; + + public: + // creation + NullCheck(Value obj, ValueStack* lock_stack) : Instruction(obj->type()->base()), _obj(obj), _lock_stack(lock_stack) { + ASSERT_VALUES + set_can_trap(true); + assert(_obj->type()->is_object(), "null check must be applied to objects only"); + pin(Instruction::PinExplicitNullCheck); + } + + // accessors + Value obj() const { return _obj; } + ValueStack* lock_stack() const { return _lock_stack; } + + // setters + void set_lock_stack(ValueStack* l) { _lock_stack = l; } + void set_can_trap(bool can_trap) { set_flag(CanTrapFlag, can_trap); } + + // generic + virtual bool can_trap() const { return check_flag(CanTrapFlag); /* null-check elimination sets to false */ } + virtual void input_values_do(void f(Value*)) { f(&_obj); } + virtual void other_values_do(void f(Value*)); + HASHING1(NullCheck, true, obj()->subst()) +}; + + +BASE(StateSplit, Instruction) + private: + ValueStack* _state; + + protected: + static void substitute(BlockList& list, BlockBegin* old_block, BlockBegin* new_block); + + public: + // creation + StateSplit(ValueType* type) : Instruction(type), _state(NULL) { + pin(PinStateSplitConstructor); + } + + // accessors + ValueStack* state() const { return _state; } + IRScope* scope() const; // the state's scope + + // manipulation + void set_state(ValueStack* state) { _state = state; } + + // generic + virtual void input_values_do(void f(Value*)) { /* no values */ } + virtual void state_values_do(void f(Value*)); +}; + + +LEAF(Invoke, StateSplit) + private: + Bytecodes::Code _code; + Value _recv; + Values* _args; + BasicTypeList* _signature; + int _vtable_index; + ciMethod* _target; + + public: + // creation + Invoke(Bytecodes::Code code, ValueType* result_type, Value recv, Values* args, + int vtable_index, ciMethod* target); + + // accessors + Bytecodes::Code code() const { return _code; } + Value receiver() const { return _recv; } + bool has_receiver() const { return receiver() != NULL; } + int number_of_arguments() const { return _args->length(); } + Value argument_at(int i) const { return _args->at(i); } + int vtable_index() const { return _vtable_index; } + BasicTypeList* signature() const { return _signature; } + ciMethod* target() const { return _target; } + + // Returns false if target is not loaded + bool target_is_final() const { return check_flag(TargetIsFinalFlag); } + bool target_is_loaded() const { return check_flag(TargetIsLoadedFlag); } + // Returns false if target is not loaded + bool target_is_strictfp() const { return check_flag(TargetIsStrictfpFlag); } + + // generic + virtual bool can_trap() const { return true; } + virtual void input_values_do(void f(Value*)) { + StateSplit::input_values_do(f); + if (has_receiver()) f(&_recv); + for (int i = 0; i < _args->length(); i++) f(_args->adr_at(i)); + } +}; + + +LEAF(NewInstance, StateSplit) + private: + ciInstanceKlass* _klass; + + public: + // creation + NewInstance(ciInstanceKlass* klass) : StateSplit(instanceType), _klass(klass) {} + + // accessors + ciInstanceKlass* klass() const { return _klass; } + + // generic + virtual bool can_trap() const { return true; } + ciType* exact_type() const; +}; + + +BASE(NewArray, StateSplit) + private: + Value _length; + ValueStack* _state_before; + + public: + // creation + NewArray(Value length, ValueStack* state_before) : StateSplit(objectType), _length(length), _state_before(state_before) { + // Do not ASSERT_VALUES since length is NULL for NewMultiArray + } + + // accessors + ValueStack* state_before() const { return _state_before; } + Value length() const { return _length; } + + // generic + virtual bool can_trap() const { return true; } + virtual void input_values_do(void f(Value*)) { StateSplit::input_values_do(f); f(&_length); } + virtual void other_values_do(void f(Value*)); +}; + + +LEAF(NewTypeArray, NewArray) + private: + BasicType _elt_type; + + public: + // creation + NewTypeArray(Value length, BasicType elt_type) : NewArray(length, NULL), _elt_type(elt_type) {} + + // accessors + BasicType elt_type() const { return _elt_type; } + ciType* exact_type() const; +}; + + +LEAF(NewObjectArray, NewArray) + private: + ciKlass* _klass; + + public: + // creation + NewObjectArray(ciKlass* klass, Value length, ValueStack* state_before) : NewArray(length, state_before), _klass(klass) {} + + // accessors + ciKlass* klass() const { return _klass; } + ciType* exact_type() const; +}; + + +LEAF(NewMultiArray, NewArray) + private: + ciKlass* _klass; + Values* _dims; + + public: + // creation + NewMultiArray(ciKlass* klass, Values* dims, ValueStack* state_before) : NewArray(NULL, state_before), _klass(klass), _dims(dims) { + ASSERT_VALUES + } + + // accessors + ciKlass* klass() const { return _klass; } + Values* dims() const { return _dims; } + int rank() const { return dims()->length(); } + + // generic + virtual void input_values_do(void f(Value*)) { + // NOTE: we do not call NewArray::input_values_do since "length" + // is meaningless for a multi-dimensional array; passing the + // zeroth element down to NewArray as its length is a bad idea + // since there will be a copy in the "dims" array which doesn't + // get updated, and the value must not be traversed twice. Was bug + // - kbr 4/10/2001 + StateSplit::input_values_do(f); + for (int i = 0; i < _dims->length(); i++) f(_dims->adr_at(i)); + } +}; + + +BASE(TypeCheck, StateSplit) + private: + ciKlass* _klass; + Value _obj; + ValueStack* _state_before; + + public: + // creation + TypeCheck(ciKlass* klass, Value obj, ValueType* type, ValueStack* state_before) : StateSplit(type), _klass(klass), _obj(obj), _state_before(state_before) { + ASSERT_VALUES + set_direct_compare(false); + } + + // accessors + ValueStack* state_before() const { return _state_before; } + ciKlass* klass() const { return _klass; } + Value obj() const { return _obj; } + bool is_loaded() const { return klass() != NULL; } + bool direct_compare() const { return check_flag(DirectCompareFlag); } + + // manipulation + void set_direct_compare(bool flag) { set_flag(DirectCompareFlag, flag); } + + // generic + virtual bool can_trap() const { return true; } + virtual void input_values_do(void f(Value*)) { StateSplit::input_values_do(f); f(&_obj); } + virtual void other_values_do(void f(Value*)); +}; + + +LEAF(CheckCast, TypeCheck) + private: + ciMethod* _profiled_method; + int _profiled_bci; + + public: + // creation + CheckCast(ciKlass* klass, Value obj, ValueStack* state_before) + : TypeCheck(klass, obj, objectType, state_before) + , _profiled_method(NULL) + , _profiled_bci(0) {} + + void set_incompatible_class_change_check() { + set_flag(ThrowIncompatibleClassChangeErrorFlag, true); + } + bool is_incompatible_class_change_check() const { + return check_flag(ThrowIncompatibleClassChangeErrorFlag); + } + + // Helpers for methodDataOop profiling + void set_should_profile(bool value) { set_flag(ProfileMDOFlag, value); } + void set_profiled_method(ciMethod* method) { _profiled_method = method; } + void set_profiled_bci(int bci) { _profiled_bci = bci; } + bool should_profile() const { return check_flag(ProfileMDOFlag); } + ciMethod* profiled_method() const { return _profiled_method; } + int profiled_bci() const { return _profiled_bci; } + + ciType* declared_type() const; + ciType* exact_type() const; + +}; + + +LEAF(InstanceOf, TypeCheck) + public: + // creation + InstanceOf(ciKlass* klass, Value obj, ValueStack* state_before) : TypeCheck(klass, obj, intType, state_before) {} +}; + + +BASE(AccessMonitor, StateSplit) + private: + Value _obj; + int _monitor_no; + + public: + // creation + AccessMonitor(Value obj, int monitor_no) + : StateSplit(illegalType) + , _obj(obj) + , _monitor_no(monitor_no) + { + set_needs_null_check(true); + ASSERT_VALUES + } + + // accessors + Value obj() const { return _obj; } + int monitor_no() const { return _monitor_no; } + + // generic + virtual void input_values_do(void f(Value*)) { StateSplit::input_values_do(f); f(&_obj); } +}; + + +LEAF(MonitorEnter, AccessMonitor) + private: + ValueStack* _lock_stack_before; + + public: + // creation + MonitorEnter(Value obj, int monitor_no, ValueStack* lock_stack_before) + : AccessMonitor(obj, monitor_no) + , _lock_stack_before(lock_stack_before) + { + ASSERT_VALUES + } + + // accessors + ValueStack* lock_stack_before() const { return _lock_stack_before; } + virtual void state_values_do(void f(Value*)); + + // generic + virtual bool can_trap() const { return true; } +}; + + +LEAF(MonitorExit, AccessMonitor) + public: + // creation + MonitorExit(Value obj, int monitor_no) : AccessMonitor(obj, monitor_no) {} +}; + + +LEAF(Intrinsic, StateSplit) + private: + vmIntrinsics::ID _id; + Values* _args; + ValueStack* _lock_stack; + Value _recv; + + public: + // preserves_state can be set to true for Intrinsics + // which are guaranteed to preserve register state across any slow + // cases; setting it to true does not mean that the Intrinsic can + // not trap, only that if we continue execution in the same basic + // block after the Intrinsic, all of the registers are intact. This + // allows load elimination and common expression elimination to be + // performed across the Intrinsic. The default value is false. + Intrinsic(ValueType* type, + vmIntrinsics::ID id, + Values* args, + bool has_receiver, + ValueStack* lock_stack, + bool preserves_state, + bool cantrap = true) + : StateSplit(type) + , _id(id) + , _args(args) + , _lock_stack(lock_stack) + , _recv(NULL) + { + assert(args != NULL, "args must exist"); + ASSERT_VALUES + set_flag(PreservesStateFlag, preserves_state); + set_flag(CanTrapFlag, cantrap); + if (has_receiver) { + _recv = argument_at(0); + } + set_needs_null_check(has_receiver); + + // some intrinsics can't trap, so don't force them to be pinned + if (!can_trap()) { + unpin(PinStateSplitConstructor); + } + } + + // accessors + vmIntrinsics::ID id() const { return _id; } + int number_of_arguments() const { return _args->length(); } + Value argument_at(int i) const { return _args->at(i); } + ValueStack* lock_stack() const { return _lock_stack; } + + bool has_receiver() const { return (_recv != NULL); } + Value receiver() const { assert(has_receiver(), "must have receiver"); return _recv; } + bool preserves_state() const { return check_flag(PreservesStateFlag); } + + // generic + virtual bool can_trap() const { return check_flag(CanTrapFlag); } + virtual void input_values_do(void f(Value*)) { + StateSplit::input_values_do(f); + for (int i = 0; i < _args->length(); i++) f(_args->adr_at(i)); + } + virtual void state_values_do(void f(Value*)); + +}; + + +class LIR_List; + +LEAF(BlockBegin, StateSplit) + private: + static int _next_block_id; // the block counter + + int _block_id; // the unique block id + int _depth_first_number; // number of this block in a depth-first ordering + int _linear_scan_number; // number of this block in linear-scan ordering + int _loop_depth; // the loop nesting level of this block + int _loop_index; // number of the innermost loop of this block + int _flags; // the flags associated with this block + + // fields used by BlockListBuilder + int _total_preds; // number of predecessors found by BlockListBuilder + BitMap _stores_to_locals; // bit is set when a local variable is stored in the block + + // SSA specific fields: (factor out later) + BlockList _successors; // the successors of this block + BlockList _predecessors; // the predecessors of this block + BlockBegin* _dominator; // the dominator of this block + // SSA specific ends + BlockEnd* _end; // the last instruction of this block + BlockList _exception_handlers; // the exception handlers potentially invoked by this block + ValueStackStack* _exception_states; // only for xhandler entries: states of all instructions that have an edge to this xhandler + int _exception_handler_pco; // if this block is the start of an exception handler, + // this records the PC offset in the assembly code of the + // first instruction in this block + Label _label; // the label associated with this block + LIR_List* _lir; // the low level intermediate representation for this block + + BitMap _live_in; // set of live LIR_Opr registers at entry to this block + BitMap _live_out; // set of live LIR_Opr registers at exit from this block + BitMap _live_gen; // set of registers used before any redefinition in this block + BitMap _live_kill; // set of registers defined in this block + + BitMap _fpu_register_usage; + intArray* _fpu_stack_state; // For x86 FPU code generation with UseLinearScan + int _first_lir_instruction_id; // ID of first LIR instruction in this block + int _last_lir_instruction_id; // ID of last LIR instruction in this block + + void iterate_preorder (boolArray& mark, BlockClosure* closure); + void iterate_postorder(boolArray& mark, BlockClosure* closure); + + friend class SuxAndWeightAdjuster; + + public: + // initialization/counting + static void initialize() { _next_block_id = 0; } + static int number_of_blocks() { return _next_block_id; } + + // creation + BlockBegin(int bci) + : StateSplit(illegalType) + , _block_id(_next_block_id++) + , _depth_first_number(-1) + , _linear_scan_number(-1) + , _loop_depth(0) + , _flags(0) + , _dominator(NULL) + , _end(NULL) + , _predecessors(2) + , _successors(2) + , _exception_handlers(1) + , _exception_states(NULL) + , _exception_handler_pco(-1) + , _lir(NULL) + , _loop_index(-1) + , _live_in() + , _live_out() + , _live_gen() + , _live_kill() + , _fpu_register_usage() + , _fpu_stack_state(NULL) + , _first_lir_instruction_id(-1) + , _last_lir_instruction_id(-1) + , _total_preds(0) + , _stores_to_locals() + { + set_bci(bci); + } + + // accessors + int block_id() const { return _block_id; } + BlockList* successors() { return &_successors; } + BlockBegin* dominator() const { return _dominator; } + int loop_depth() const { return _loop_depth; } + int depth_first_number() const { return _depth_first_number; } + int linear_scan_number() const { return _linear_scan_number; } + BlockEnd* end() const { return _end; } + Label* label() { return &_label; } + LIR_List* lir() const { return _lir; } + int exception_handler_pco() const { return _exception_handler_pco; } + BitMap& live_in() { return _live_in; } + BitMap& live_out() { return _live_out; } + BitMap& live_gen() { return _live_gen; } + BitMap& live_kill() { return _live_kill; } + BitMap& fpu_register_usage() { return _fpu_register_usage; } + intArray* fpu_stack_state() const { return _fpu_stack_state; } + int first_lir_instruction_id() const { return _first_lir_instruction_id; } + int last_lir_instruction_id() const { return _last_lir_instruction_id; } + int total_preds() const { return _total_preds; } + BitMap& stores_to_locals() { return _stores_to_locals; } + + // manipulation + void set_bci(int bci) { Instruction::set_bci(bci); } + void set_dominator(BlockBegin* dom) { _dominator = dom; } + void set_loop_depth(int d) { _loop_depth = d; } + void set_depth_first_number(int dfn) { _depth_first_number = dfn; } + void set_linear_scan_number(int lsn) { _linear_scan_number = lsn; } + void set_end(BlockEnd* end); + void disconnect_from_graph(); + static void disconnect_edge(BlockBegin* from, BlockBegin* to); + BlockBegin* insert_block_between(BlockBegin* sux); + void substitute_sux(BlockBegin* old_sux, BlockBegin* new_sux); + void set_lir(LIR_List* lir) { _lir = lir; } + void set_exception_handler_pco(int pco) { _exception_handler_pco = pco; } + void set_live_in (BitMap map) { _live_in = map; } + void set_live_out (BitMap map) { _live_out = map; } + void set_live_gen (BitMap map) { _live_gen = map; } + void set_live_kill (BitMap map) { _live_kill = map; } + void set_fpu_register_usage(BitMap map) { _fpu_register_usage = map; } + void set_fpu_stack_state(intArray* state) { _fpu_stack_state = state; } + void set_first_lir_instruction_id(int id) { _first_lir_instruction_id = id; } + void set_last_lir_instruction_id(int id) { _last_lir_instruction_id = id; } + void increment_total_preds(int n = 1) { _total_preds += n; } + void init_stores_to_locals(int locals_count) { _stores_to_locals = BitMap(locals_count); _stores_to_locals.clear(); } + + // generic + virtual void state_values_do(void f(Value*)); + + // successors and predecessors + int number_of_sux() const; + BlockBegin* sux_at(int i) const; + void add_successor(BlockBegin* sux); + void remove_successor(BlockBegin* pred); + bool is_successor(BlockBegin* sux) const { return _successors.contains(sux); } + + void add_predecessor(BlockBegin* pred); + void remove_predecessor(BlockBegin* pred); + bool is_predecessor(BlockBegin* pred) const { return _predecessors.contains(pred); } + int number_of_preds() const { return _predecessors.length(); } + BlockBegin* pred_at(int i) const { return _predecessors[i]; } + + // exception handlers potentially invoked by this block + void add_exception_handler(BlockBegin* b); + bool is_exception_handler(BlockBegin* b) const { return _exception_handlers.contains(b); } + int number_of_exception_handlers() const { return _exception_handlers.length(); } + BlockBegin* exception_handler_at(int i) const { return _exception_handlers.at(i); } + + // states of the instructions that have an edge to this exception handler + int number_of_exception_states() { assert(is_set(exception_entry_flag), "only for xhandlers"); return _exception_states == NULL ? 0 : _exception_states->length(); } + ValueStack* exception_state_at(int idx) const { assert(is_set(exception_entry_flag), "only for xhandlers"); return _exception_states->at(idx); } + int add_exception_state(ValueStack* state); + + // flags + enum Flag { + no_flag = 0, + std_entry_flag = 1 << 0, + osr_entry_flag = 1 << 1, + exception_entry_flag = 1 << 2, + subroutine_entry_flag = 1 << 3, + backward_branch_target_flag = 1 << 4, + is_on_work_list_flag = 1 << 5, + was_visited_flag = 1 << 6, + default_exception_handler_flag = 1 << 8, // identify block which represents the default exception handler + parser_loop_header_flag = 1 << 9, // set by parser to identify blocks where phi functions can not be created on demand + critical_edge_split_flag = 1 << 10, // set for all blocks that are introduced when critical edges are split + linear_scan_loop_header_flag = 1 << 11, // set during loop-detection for LinearScan + linear_scan_loop_end_flag = 1 << 12 // set during loop-detection for LinearScan + }; + + void set(Flag f) { _flags |= f; } + void clear(Flag f) { _flags &= ~f; } + bool is_set(Flag f) const { return (_flags & f) != 0; } + bool is_entry_block() const { + const int entry_mask = std_entry_flag | osr_entry_flag | exception_entry_flag; + return (_flags & entry_mask) != 0; + } + + // iteration + void iterate_preorder (BlockClosure* closure); + void iterate_postorder (BlockClosure* closure); + + void block_values_do(void f(Value*)); + + // loops + void set_loop_index(int ix) { _loop_index = ix; } + int loop_index() const { return _loop_index; } + + // merging + bool try_merge(ValueStack* state); // try to merge states at block begin + void merge(ValueStack* state) { bool b = try_merge(state); assert(b, "merge failed"); } + + // debugging + void print_block() PRODUCT_RETURN; + void print_block(InstructionPrinter& ip, bool live_only = false) PRODUCT_RETURN; +}; + + +BASE(BlockEnd, StateSplit) + private: + BlockBegin* _begin; + BlockList* _sux; + ValueStack* _state_before; + + protected: + BlockList* sux() const { return _sux; } + + void set_sux(BlockList* sux) { +#ifdef ASSERT + assert(sux != NULL, "sux must exist"); + for (int i = sux->length() - 1; i >= 0; i--) assert(sux->at(i) != NULL, "sux must exist"); +#endif + _sux = sux; + } + + public: + // creation + BlockEnd(ValueType* type, ValueStack* state_before, bool is_safepoint) + : StateSplit(type) + , _begin(NULL) + , _sux(NULL) + , _state_before(state_before) { + set_flag(IsSafepointFlag, is_safepoint); + } + + // accessors + ValueStack* state_before() const { return _state_before; } + bool is_safepoint() const { return check_flag(IsSafepointFlag); } + BlockBegin* begin() const { return _begin; } + + // manipulation + void set_begin(BlockBegin* begin); + + // generic + virtual void other_values_do(void f(Value*)); + + // successors + int number_of_sux() const { return _sux != NULL ? _sux->length() : 0; } + BlockBegin* sux_at(int i) const { return _sux->at(i); } + BlockBegin* default_sux() const { return sux_at(number_of_sux() - 1); } + BlockBegin** addr_sux_at(int i) const { return _sux->adr_at(i); } + int sux_index(BlockBegin* sux) const { return _sux->find(sux); } + void substitute_sux(BlockBegin* old_sux, BlockBegin* new_sux); +}; + + +LEAF(Goto, BlockEnd) + public: + // creation + Goto(BlockBegin* sux, ValueStack* state_before, bool is_safepoint = false) : BlockEnd(illegalType, state_before, is_safepoint) { + BlockList* s = new BlockList(1); + s->append(sux); + set_sux(s); + } + + Goto(BlockBegin* sux, bool is_safepoint) : BlockEnd(illegalType, NULL, is_safepoint) { + BlockList* s = new BlockList(1); + s->append(sux); + set_sux(s); + } + +}; + + +LEAF(If, BlockEnd) + private: + Value _x; + Condition _cond; + Value _y; + ciMethod* _profiled_method; + int _profiled_bci; // Canonicalizer may alter bci of If node + public: + // creation + // unordered_is_true is valid for float/double compares only + If(Value x, Condition cond, bool unordered_is_true, Value y, BlockBegin* tsux, BlockBegin* fsux, ValueStack* state_before, bool is_safepoint) + : BlockEnd(illegalType, state_before, is_safepoint) + , _x(x) + , _cond(cond) + , _y(y) + , _profiled_method(NULL) + , _profiled_bci(0) + { + ASSERT_VALUES + set_flag(UnorderedIsTrueFlag, unordered_is_true); + assert(x->type()->tag() == y->type()->tag(), "types must match"); + BlockList* s = new BlockList(2); + s->append(tsux); + s->append(fsux); + set_sux(s); + } + + // accessors + Value x() const { return _x; } + Condition cond() const { return _cond; } + bool unordered_is_true() const { return check_flag(UnorderedIsTrueFlag); } + Value y() const { return _y; } + BlockBegin* sux_for(bool is_true) const { return sux_at(is_true ? 0 : 1); } + BlockBegin* tsux() const { return sux_for(true); } + BlockBegin* fsux() const { return sux_for(false); } + BlockBegin* usux() const { return sux_for(unordered_is_true()); } + bool should_profile() const { return check_flag(ProfileMDOFlag); } + ciMethod* profiled_method() const { return _profiled_method; } // set only for profiled branches + int profiled_bci() const { return _profiled_bci; } // set only for profiled branches + + // manipulation + void swap_operands() { + Value t = _x; _x = _y; _y = t; + _cond = mirror(_cond); + } + + void swap_sux() { + assert(number_of_sux() == 2, "wrong number of successors"); + BlockList* s = sux(); + BlockBegin* t = s->at(0); s->at_put(0, s->at(1)); s->at_put(1, t); + _cond = negate(_cond); + set_flag(UnorderedIsTrueFlag, !check_flag(UnorderedIsTrueFlag)); + } + + void set_should_profile(bool value) { set_flag(ProfileMDOFlag, value); } + void set_profiled_method(ciMethod* method) { _profiled_method = method; } + void set_profiled_bci(int bci) { _profiled_bci = bci; } + + // generic + virtual void input_values_do(void f(Value*)) { BlockEnd::input_values_do(f); f(&_x); f(&_y); } +}; + + +LEAF(IfInstanceOf, BlockEnd) + private: + ciKlass* _klass; + Value _obj; + bool _test_is_instance; // jump if instance + int _instanceof_bci; + + public: + IfInstanceOf(ciKlass* klass, Value obj, bool test_is_instance, int instanceof_bci, BlockBegin* tsux, BlockBegin* fsux) + : BlockEnd(illegalType, NULL, false) // temporary set to false + , _klass(klass) + , _obj(obj) + , _test_is_instance(test_is_instance) + , _instanceof_bci(instanceof_bci) + { + ASSERT_VALUES + assert(instanceof_bci >= 0, "illegal bci"); + BlockList* s = new BlockList(2); + s->append(tsux); + s->append(fsux); + set_sux(s); + } + + // accessors + // + // Note 1: If test_is_instance() is true, IfInstanceOf tests if obj *is* an + // instance of klass; otherwise it tests if it is *not* and instance + // of klass. + // + // Note 2: IfInstanceOf instructions are created by combining an InstanceOf + // and an If instruction. The IfInstanceOf bci() corresponds to the + // bci that the If would have had; the (this->) instanceof_bci() is + // the bci of the original InstanceOf instruction. + ciKlass* klass() const { return _klass; } + Value obj() const { return _obj; } + int instanceof_bci() const { return _instanceof_bci; } + bool test_is_instance() const { return _test_is_instance; } + BlockBegin* sux_for(bool is_true) const { return sux_at(is_true ? 0 : 1); } + BlockBegin* tsux() const { return sux_for(true); } + BlockBegin* fsux() const { return sux_for(false); } + + // manipulation + void swap_sux() { + assert(number_of_sux() == 2, "wrong number of successors"); + BlockList* s = sux(); + BlockBegin* t = s->at(0); s->at_put(0, s->at(1)); s->at_put(1, t); + _test_is_instance = !_test_is_instance; + } + + // generic + virtual void input_values_do(void f(Value*)) { BlockEnd::input_values_do(f); f(&_obj); } +}; + + +BASE(Switch, BlockEnd) + private: + Value _tag; + + public: + // creation + Switch(Value tag, BlockList* sux, ValueStack* state_before, bool is_safepoint) + : BlockEnd(illegalType, state_before, is_safepoint) + , _tag(tag) { + ASSERT_VALUES + set_sux(sux); + } + + // accessors + Value tag() const { return _tag; } + int length() const { return number_of_sux() - 1; } + + // generic + virtual void input_values_do(void f(Value*)) { BlockEnd::input_values_do(f); f(&_tag); } +}; + + +LEAF(TableSwitch, Switch) + private: + int _lo_key; + + public: + // creation + TableSwitch(Value tag, BlockList* sux, int lo_key, ValueStack* state_before, bool is_safepoint) + : Switch(tag, sux, state_before, is_safepoint) + , _lo_key(lo_key) {} + + // accessors + int lo_key() const { return _lo_key; } + int hi_key() const { return _lo_key + length() - 1; } +}; + + +LEAF(LookupSwitch, Switch) + private: + intArray* _keys; + + public: + // creation + LookupSwitch(Value tag, BlockList* sux, intArray* keys, ValueStack* state_before, bool is_safepoint) + : Switch(tag, sux, state_before, is_safepoint) + , _keys(keys) { + assert(keys != NULL, "keys must exist"); + assert(keys->length() == length(), "sux & keys have incompatible lengths"); + } + + // accessors + int key_at(int i) const { return _keys->at(i); } +}; + + +LEAF(Return, BlockEnd) + private: + Value _result; + + public: + // creation + Return(Value result) : + BlockEnd(result == NULL ? voidType : result->type()->base(), NULL, true), + _result(result) {} + + // accessors + Value result() const { return _result; } + bool has_result() const { return result() != NULL; } + + // generic + virtual void input_values_do(void f(Value*)) { + BlockEnd::input_values_do(f); + if (has_result()) f(&_result); + } +}; + + +LEAF(Throw, BlockEnd) + private: + Value _exception; + + public: + // creation + Throw(Value exception, ValueStack* state_before) : BlockEnd(illegalType, state_before, true), _exception(exception) { + ASSERT_VALUES + } + + // accessors + Value exception() const { return _exception; } + + // generic + virtual bool can_trap() const { return true; } + virtual void input_values_do(void f(Value*)) { BlockEnd::input_values_do(f); f(&_exception); } + virtual void state_values_do(void f(Value*)); +}; + + +LEAF(Base, BlockEnd) + public: + // creation + Base(BlockBegin* std_entry, BlockBegin* osr_entry) : BlockEnd(illegalType, NULL, false) { + assert(std_entry->is_set(BlockBegin::std_entry_flag), "std entry must be flagged"); + assert(osr_entry == NULL || osr_entry->is_set(BlockBegin::osr_entry_flag), "osr entry must be flagged"); + BlockList* s = new BlockList(2); + if (osr_entry != NULL) s->append(osr_entry); + s->append(std_entry); // must be default sux! + set_sux(s); + } + + // accessors + BlockBegin* std_entry() const { return default_sux(); } + BlockBegin* osr_entry() const { return number_of_sux() < 2 ? NULL : sux_at(0); } +}; + + +LEAF(OsrEntry, Instruction) + public: + // creation +#ifdef _LP64 + OsrEntry() : Instruction(longType, false) { pin(); } +#else + OsrEntry() : Instruction(intType, false) { pin(); } +#endif + + // generic + virtual void input_values_do(void f(Value*)) { } +}; + + +// Models the incoming exception at a catch site +LEAF(ExceptionObject, Instruction) + public: + // creation + ExceptionObject() : Instruction(objectType, false) { + pin(); + } + + // generic + virtual void input_values_do(void f(Value*)) { } +}; + + +// Models needed rounding for floating-point values on Intel. +// Currently only used to represent rounding of double-precision +// values stored into local variables, but could be used to model +// intermediate rounding of single-precision values as well. +LEAF(RoundFP, Instruction) + private: + Value _input; // floating-point value to be rounded + + public: + RoundFP(Value input) + : Instruction(input->type()) // Note: should not be used for constants + , _input(input) + { + ASSERT_VALUES + } + + // accessors + Value input() const { return _input; } + + // generic + virtual void input_values_do(void f(Value*)) { f(&_input); } +}; + + +BASE(UnsafeOp, Instruction) + private: + BasicType _basic_type; // ValueType can not express byte-sized integers + + protected: + // creation + UnsafeOp(BasicType basic_type, bool is_put) + : Instruction(is_put ? voidType : as_ValueType(basic_type)) + , _basic_type(basic_type) + { + //Note: Unsafe ops are not not guaranteed to throw NPE. + // Convservatively, Unsafe operations must be pinned though we could be + // looser about this if we wanted to.. + pin(); + } + + public: + // accessors + BasicType basic_type() { return _basic_type; } + + // generic + virtual void input_values_do(void f(Value*)) { } + virtual void other_values_do(void f(Value*)) { } +}; + + +BASE(UnsafeRawOp, UnsafeOp) + private: + Value _base; // Base address (a Java long) + Value _index; // Index if computed by optimizer; initialized to NULL + int _log2_scale; // Scale factor: 0, 1, 2, or 3. + // Indicates log2 of number of bytes (1, 2, 4, or 8) + // to scale index by. + + protected: + UnsafeRawOp(BasicType basic_type, Value addr, bool is_put) + : UnsafeOp(basic_type, is_put) + , _base(addr) + , _index(NULL) + , _log2_scale(0) + { + // Can not use ASSERT_VALUES because index may be NULL + assert(addr != NULL && addr->type()->is_long(), "just checking"); + } + + UnsafeRawOp(BasicType basic_type, Value base, Value index, int log2_scale, bool is_put) + : UnsafeOp(basic_type, is_put) + , _base(base) + , _index(index) + , _log2_scale(log2_scale) + { + } + + public: + // accessors + Value base() { return _base; } + Value index() { return _index; } + bool has_index() { return (_index != NULL); } + int log2_scale() { return _log2_scale; } + + // setters + void set_base (Value base) { _base = base; } + void set_index(Value index) { _index = index; } + void set_log2_scale(int log2_scale) { _log2_scale = log2_scale; } + + // generic + virtual void input_values_do(void f(Value*)) { UnsafeOp::input_values_do(f); + f(&_base); + if (has_index()) f(&_index); } +}; + + +LEAF(UnsafeGetRaw, UnsafeRawOp) + private: + bool _may_be_unaligned; // For OSREntry + + public: + UnsafeGetRaw(BasicType basic_type, Value addr, bool may_be_unaligned) + : UnsafeRawOp(basic_type, addr, false) { + _may_be_unaligned = may_be_unaligned; + } + + UnsafeGetRaw(BasicType basic_type, Value base, Value index, int log2_scale, bool may_be_unaligned) + : UnsafeRawOp(basic_type, base, index, log2_scale, false) { + _may_be_unaligned = may_be_unaligned; + } + + bool may_be_unaligned() { return _may_be_unaligned; } +}; + + +LEAF(UnsafePutRaw, UnsafeRawOp) + private: + Value _value; // Value to be stored + + public: + UnsafePutRaw(BasicType basic_type, Value addr, Value value) + : UnsafeRawOp(basic_type, addr, true) + , _value(value) + { + assert(value != NULL, "just checking"); + ASSERT_VALUES + } + + UnsafePutRaw(BasicType basic_type, Value base, Value index, int log2_scale, Value value) + : UnsafeRawOp(basic_type, base, index, log2_scale, true) + , _value(value) + { + assert(value != NULL, "just checking"); + ASSERT_VALUES + } + + // accessors + Value value() { return _value; } + + // generic + virtual void input_values_do(void f(Value*)) { UnsafeRawOp::input_values_do(f); + f(&_value); } +}; + + +BASE(UnsafeObjectOp, UnsafeOp) + private: + Value _object; // Object to be fetched from or mutated + Value _offset; // Offset within object + bool _is_volatile; // true if volatile - dl/JSR166 + public: + UnsafeObjectOp(BasicType basic_type, Value object, Value offset, bool is_put, bool is_volatile) + : UnsafeOp(basic_type, is_put), _object(object), _offset(offset), _is_volatile(is_volatile) + { + } + + // accessors + Value object() { return _object; } + Value offset() { return _offset; } + bool is_volatile() { return _is_volatile; } + // generic + virtual void input_values_do(void f(Value*)) { UnsafeOp::input_values_do(f); + f(&_object); + f(&_offset); } +}; + + +LEAF(UnsafeGetObject, UnsafeObjectOp) + public: + UnsafeGetObject(BasicType basic_type, Value object, Value offset, bool is_volatile) + : UnsafeObjectOp(basic_type, object, offset, false, is_volatile) + { + ASSERT_VALUES + } +}; + + +LEAF(UnsafePutObject, UnsafeObjectOp) + private: + Value _value; // Value to be stored + public: + UnsafePutObject(BasicType basic_type, Value object, Value offset, Value value, bool is_volatile) + : UnsafeObjectOp(basic_type, object, offset, true, is_volatile) + , _value(value) + { + ASSERT_VALUES + } + + // accessors + Value value() { return _value; } + + // generic + virtual void input_values_do(void f(Value*)) { UnsafeObjectOp::input_values_do(f); + f(&_value); } +}; + + +BASE(UnsafePrefetch, UnsafeObjectOp) + public: + UnsafePrefetch(Value object, Value offset) + : UnsafeObjectOp(T_VOID, object, offset, false, false) + { + } +}; + + +LEAF(UnsafePrefetchRead, UnsafePrefetch) + public: + UnsafePrefetchRead(Value object, Value offset) + : UnsafePrefetch(object, offset) + { + ASSERT_VALUES + } +}; + + +LEAF(UnsafePrefetchWrite, UnsafePrefetch) + public: + UnsafePrefetchWrite(Value object, Value offset) + : UnsafePrefetch(object, offset) + { + ASSERT_VALUES + } +}; + + +LEAF(ProfileCall, Instruction) + private: + ciMethod* _method; + int _bci_of_invoke; + Value _recv; + ciKlass* _known_holder; + + public: + ProfileCall(ciMethod* method, int bci, Value recv, ciKlass* known_holder) + : Instruction(voidType) + , _method(method) + , _bci_of_invoke(bci) + , _recv(recv) + , _known_holder(known_holder) + { + // The ProfileCall has side-effects and must occur precisely where located + pin(); + } + + ciMethod* method() { return _method; } + int bci_of_invoke() { return _bci_of_invoke; } + Value recv() { return _recv; } + ciKlass* known_holder() { return _known_holder; } + + virtual void input_values_do(void f(Value*)) { if (_recv != NULL) f(&_recv); } +}; + + +// +// Simple node representing a counter update generally used for updating MDOs +// +LEAF(ProfileCounter, Instruction) + private: + Value _mdo; + int _offset; + int _increment; + + public: + ProfileCounter(Value mdo, int offset, int increment = 1) + : Instruction(voidType) + , _mdo(mdo) + , _offset(offset) + , _increment(increment) + { + // The ProfileCounter has side-effects and must occur precisely where located + pin(); + } + + Value mdo() { return _mdo; } + int offset() { return _offset; } + int increment() { return _increment; } + + virtual void input_values_do(void f(Value*)) { f(&_mdo); } +}; + + +class BlockPair: public CompilationResourceObj { + private: + BlockBegin* _from; + BlockBegin* _to; + public: + BlockPair(BlockBegin* from, BlockBegin* to): _from(from), _to(to) {} + BlockBegin* from() const { return _from; } + BlockBegin* to() const { return _to; } + bool is_same(BlockBegin* from, BlockBegin* to) const { return _from == from && _to == to; } + bool is_same(BlockPair* p) const { return _from == p->from() && _to == p->to(); } + void set_to(BlockBegin* b) { _to = b; } + void set_from(BlockBegin* b) { _from = b; } +}; + + +define_array(BlockPairArray, BlockPair*) +define_stack(BlockPairList, BlockPairArray) + + +inline int BlockBegin::number_of_sux() const { assert(_end == NULL || _end->number_of_sux() == _successors.length(), "mismatch"); return _successors.length(); } +inline BlockBegin* BlockBegin::sux_at(int i) const { assert(_end == NULL || _end->sux_at(i) == _successors.at(i), "mismatch"); return _successors.at(i); } +inline void BlockBegin::add_successor(BlockBegin* sux) { assert(_end == NULL, "Would create mismatch with successors of BlockEnd"); _successors.append(sux); } + +#undef ASSERT_VALUES diff --git a/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp new file mode 100644 index 00000000000..0388385a8b0 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp @@ -0,0 +1,850 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_InstructionPrinter.cpp.incl" + + +#ifndef PRODUCT + +const char* InstructionPrinter::basic_type_name(BasicType type) { + switch (type) { + case T_BOOLEAN: return "boolean"; + case T_BYTE : return "byte"; + case T_CHAR : return "char"; + case T_SHORT : return "short"; + case T_INT : return "int"; + case T_LONG : return "long"; + case T_FLOAT : return "float"; + case T_DOUBLE : return "double"; + case T_ARRAY : return "array"; + case T_OBJECT : return "object"; + default : return "???"; + } +} + + +const char* InstructionPrinter::cond_name(If::Condition cond) { + switch (cond) { + case If::eql: return "=="; + case If::neq: return "!="; + case If::lss: return "<"; + case If::leq: return "<="; + case If::gtr: return ">"; + case If::geq: return ">="; + } + ShouldNotReachHere(); + return NULL; +} + + +const char* InstructionPrinter::op_name(Bytecodes::Code op) { + switch (op) { + // arithmetic ops + case Bytecodes::_iadd : // fall through + case Bytecodes::_ladd : // fall through + case Bytecodes::_fadd : // fall through + case Bytecodes::_dadd : return "+"; + case Bytecodes::_isub : // fall through + case Bytecodes::_lsub : // fall through + case Bytecodes::_fsub : // fall through + case Bytecodes::_dsub : return "-"; + case Bytecodes::_imul : // fall through + case Bytecodes::_lmul : // fall through + case Bytecodes::_fmul : // fall through + case Bytecodes::_dmul : return "*"; + case Bytecodes::_idiv : // fall through + case Bytecodes::_ldiv : // fall through + case Bytecodes::_fdiv : // fall through + case Bytecodes::_ddiv : return "/"; + case Bytecodes::_irem : // fall through + case Bytecodes::_lrem : // fall through + case Bytecodes::_frem : // fall through + case Bytecodes::_drem : return "%"; + // shift ops + case Bytecodes::_ishl : // fall through + case Bytecodes::_lshl : return "<<"; + case Bytecodes::_ishr : // fall through + case Bytecodes::_lshr : return ">>"; + case Bytecodes::_iushr: // fall through + case Bytecodes::_lushr: return ">>>"; + // logic ops + case Bytecodes::_iand : // fall through + case Bytecodes::_land : return "&"; + case Bytecodes::_ior : // fall through + case Bytecodes::_lor : return "|"; + case Bytecodes::_ixor : // fall through + case Bytecodes::_lxor : return "^"; + } + return Bytecodes::name(op); +} + + +bool InstructionPrinter::is_illegal_phi(Value v) { + Phi* phi = v ? v->as_Phi() : NULL; + if (phi && phi->is_illegal()) { + return true; + } + return false; +} + + +bool InstructionPrinter::is_phi_of_block(Value v, BlockBegin* b) { + Phi* phi = v ? v->as_Phi() : NULL; + return phi && phi->block() == b; +} + + +void InstructionPrinter::print_klass(ciKlass* klass) { + klass->name()->print_symbol_on(output()); +} + + +void InstructionPrinter::print_object(Value obj) { + ValueType* type = obj->type(); + if (type->as_ObjectConstant() != NULL) { + ciObject* value = type->as_ObjectConstant()->value(); + if (value->is_null_object()) { + output()->print("null"); + } else if (!value->is_loaded()) { + output()->print("", value); + } else if (value->is_method()) { + ciMethod* m = (ciMethod*)value; + output()->print("", m->holder()->name()->as_utf8(), m->name()->as_utf8()); + } else { + output()->print("", value->encoding()); + } + } else if (type->as_InstanceConstant() != NULL) { + output()->print("", type->as_InstanceConstant()->value()->encoding()); + } else if (type->as_ArrayConstant() != NULL) { + output()->print("", type->as_ArrayConstant()->value()->encoding()); + } else if (type->as_ClassConstant() != NULL) { + ciInstanceKlass* klass = type->as_ClassConstant()->value(); + if (!klass->is_loaded()) { + output()->print(" "); + } + output()->print("class "); + print_klass(klass); + } else { + output()->print("???"); + } +} + + +void InstructionPrinter::print_temp(Value value) { + output()->print("%c%d", value->type()->tchar(), value->id()); +} + + +void InstructionPrinter::print_field(AccessField* field) { + print_value(field->obj()); + output()->print("._%d", field->offset()); +} + + +void InstructionPrinter::print_indexed(AccessIndexed* indexed) { + print_value(indexed->array()); + output()->put('['); + print_value(indexed->index()); + output()->put(']'); +} + + +void InstructionPrinter::print_monitor(AccessMonitor* monitor) { + output()->print("monitor[%d](", monitor->monitor_no()); + print_value(monitor->obj()); + output()->put(')'); +} + + +void InstructionPrinter::print_op2(Op2* instr) { + print_value(instr->x()); + output()->print(" %s ", op_name(instr->op())); + print_value(instr->y()); +} + + +void InstructionPrinter::print_value(Value value) { + if (value == NULL) { + output()->print("NULL"); + } else { + print_temp(value); + } +} + + +void InstructionPrinter::print_instr(Instruction* instr) { + instr->visit(this); +} + + +void InstructionPrinter::print_stack(ValueStack* stack) { + int start_position = output()->position(); + if (stack->stack_is_empty()) { + output()->print("empty stack"); + } else { + output()->print("stack ["); + for (int i = 0; i < stack->stack_size();) { + if (i > 0) output()->print(", "); + output()->print("%d:", i); + Value value = stack->stack_at_inc(i); + print_value(value); + Phi* phi = value->as_Phi(); + if (phi != NULL) { + if (phi->operand()->is_valid()) { + output()->print(" "); + phi->operand()->print(output()); + } + } + } + output()->put(']'); + } + if (!stack->no_active_locks()) { + // print out the lines on the line below this + // one at the same indentation level. + output()->cr(); + fill_to(start_position, ' '); + output()->print("locks ["); + for (int i = i = 0; i < stack->locks_size(); i++) { + Value t = stack->lock_at(i); + if (i > 0) output()->print(", "); + output()->print("%d:", i); + if (t == NULL) { + // synchronized methods push null on the lock stack + output()->print("this"); + } else { + print_value(t); + } + } + output()->print("]"); + } +} + + +void InstructionPrinter::print_inline_level(BlockBegin* block) { + output()->print_cr("inlining depth %d", block->scope()->level()); +} + + +void InstructionPrinter::print_unsafe_op(UnsafeOp* op, const char* name) { + output()->print(name); + output()->print(".("); +} + +void InstructionPrinter::print_unsafe_raw_op(UnsafeRawOp* op, const char* name) { + print_unsafe_op(op, name); + output()->print("base "); + print_value(op->base()); + if (op->has_index()) { + output()->print(", index "); print_value(op->index()); + output()->print(", log2_scale %d", op->log2_scale()); + } +} + + +void InstructionPrinter::print_unsafe_object_op(UnsafeObjectOp* op, const char* name) { + print_unsafe_op(op, name); + print_value(op->object()); + output()->print(", "); + print_value(op->offset()); +} + + +void InstructionPrinter::print_phi(int i, Value v, BlockBegin* b) { + Phi* phi = v->as_Phi(); + output()->print("%2d ", i); + print_value(v); + // print phi operands + if (phi && phi->block() == b) { + output()->print(" ["); + for (int j = 0; j < phi->operand_count(); j ++) { + output()->print(" "); + Value opd = phi->operand_at(j); + if (opd) print_value(opd); + else output()->print("NULL"); + } + output()->print("] "); + } + print_alias(v); +} + + +void InstructionPrinter::print_alias(Value v) { + if (v != v->subst()) { + output()->print("alias "); print_value(v->subst()); + } +} + + +void InstructionPrinter::fill_to(int pos, char filler) { + while (output()->position() < pos) output()->put(filler); +} + + +void InstructionPrinter::print_head() { + const char filler = '_'; + fill_to(bci_pos , filler); output()->print("bci" ); + fill_to(use_pos , filler); output()->print("use" ); + fill_to(temp_pos , filler); output()->print("tid" ); + fill_to(instr_pos, filler); output()->print("instr"); + fill_to(end_pos , filler); + output()->cr(); +} + + +void InstructionPrinter::print_line(Instruction* instr) { + // print instruction data on one line + if (instr->is_pinned()) output()->put('.'); + fill_to(bci_pos ); output()->print("%d", instr->bci()); + fill_to(use_pos ); output()->print("%d", instr->use_count()); + fill_to(temp_pos ); print_temp(instr); + fill_to(instr_pos); print_instr(instr); + output()->cr(); + // add a line for StateSplit instructions w/ non-empty stacks + // (make it robust so we can print incomplete instructions) + StateSplit* split = instr->as_StateSplit(); + if (split != NULL && split->state() != NULL && !split->state()->stack_is_empty()) { + fill_to(instr_pos); print_stack(split->state()); + output()->cr(); + } +} + + +void InstructionPrinter::do_Phi(Phi* x) { + output()->print("phi function"); // make that more detailed later + if (x->is_illegal()) + output()->print(" (illegal)"); +} + + +void InstructionPrinter::do_Local(Local* x) { + output()->print("local[index %d]", x->java_index()); +} + + +void InstructionPrinter::do_Constant(Constant* x) { + ValueType* t = x->type(); + switch (t->tag()) { + case intTag : output()->print("%d" , t->as_IntConstant ()->value()); break; + case longTag : output()->print(os::jlong_format_specifier(), t->as_LongConstant()->value()); output()->print("L"); break; + case floatTag : output()->print("%g" , t->as_FloatConstant ()->value()); break; + case doubleTag : output()->print("%gD" , t->as_DoubleConstant()->value()); break; + case objectTag : print_object(x); break; + case addressTag: output()->print("bci:%d", t->as_AddressConstant()->value()); break; + default : output()->print("???"); break; + } +} + + +void InstructionPrinter::do_LoadField(LoadField* x) { + print_field(x); + output()->print(" (%c)", type2char(x->field()->type()->basic_type())); +} + + +void InstructionPrinter::do_StoreField(StoreField* x) { + print_field(x); + output()->print(" := "); + print_value(x->value()); + output()->print(" (%c)", type2char(x->field()->type()->basic_type())); +} + + +void InstructionPrinter::do_ArrayLength(ArrayLength* x) { + print_value(x->array()); + output()->print(".length"); +} + + +void InstructionPrinter::do_LoadIndexed(LoadIndexed* x) { + print_indexed(x); + output()->print(" (%c)", type2char(x->elt_type())); +} + + +void InstructionPrinter::do_StoreIndexed(StoreIndexed* x) { + print_indexed(x); + output()->print(" := "); + print_value(x->value()); + output()->print(" (%c)", type2char(x->elt_type())); +} + +void InstructionPrinter::do_NegateOp(NegateOp* x) { + output()->put('-'); + print_value(x->x()); +} + + +void InstructionPrinter::do_ArithmeticOp(ArithmeticOp* x) { + print_op2(x); +} + + +void InstructionPrinter::do_ShiftOp(ShiftOp* x) { + print_op2(x); +} + + +void InstructionPrinter::do_LogicOp(LogicOp* x) { + print_op2(x); +} + + +void InstructionPrinter::do_CompareOp(CompareOp* x) { + print_op2(x); +} + + +void InstructionPrinter::do_IfOp(IfOp* x) { + print_value(x->x()); + output()->print(" %s ", cond_name(x->cond())); + print_value(x->y()); + output()->print(" ? "); + print_value(x->tval()); + output()->print(" : "); + print_value(x->fval()); +} + + +void InstructionPrinter::do_Convert(Convert* x) { + output()->print("%s(", Bytecodes::name(x->op())); + print_value(x->value()); + output()->put(')'); +} + + +void InstructionPrinter::do_NullCheck(NullCheck* x) { + output()->print("null_check("); + print_value(x->obj()); + output()->put(')'); + if (!x->can_trap()) { + output()->print(" (eliminated)"); + } +} + + +void InstructionPrinter::do_Invoke(Invoke* x) { + if (x->receiver() != NULL) { + print_value(x->receiver()); + output()->print("."); + } + + output()->print("%s(", Bytecodes::name(x->code())); + for (int i = 0; i < x->number_of_arguments(); i++) { + if (i > 0) output()->print(", "); + print_value(x->argument_at(i)); + } + output()->print_cr(")"); + fill_to(instr_pos); + output()->print("%s.%s%s", + x->target()->holder()->name()->as_utf8(), + x->target()->name()->as_utf8(), + x->target()->signature()->as_symbol()->as_utf8()); +} + + +void InstructionPrinter::do_NewInstance(NewInstance* x) { + output()->print("new instance "); + print_klass(x->klass()); +} + + +void InstructionPrinter::do_NewTypeArray(NewTypeArray* x) { + output()->print("new %s array [", basic_type_name(x->elt_type())); + print_value(x->length()); + output()->put(']'); +} + + +void InstructionPrinter::do_NewObjectArray(NewObjectArray* x) { + output()->print("new object array ["); + print_value(x->length()); + output()->print("] "); + print_klass(x->klass()); +} + + +void InstructionPrinter::do_NewMultiArray(NewMultiArray* x) { + output()->print("new multi array ["); + Values* dims = x->dims(); + for (int i = 0; i < dims->length(); i++) { + if (i > 0) output()->print(", "); + print_value(dims->at(i)); + } + output()->print("] "); + print_klass(x->klass()); +} + + +void InstructionPrinter::do_MonitorEnter(MonitorEnter* x) { + output()->print("enter "); + print_monitor(x); +} + + +void InstructionPrinter::do_MonitorExit(MonitorExit* x) { + output()->print("exit "); + print_monitor(x); +} + + +void InstructionPrinter::do_Intrinsic(Intrinsic* x) { + const char* name = vmIntrinsics::name_at(x->id()); + if (name[0] == '_') name++; // strip leading bug from _hashCode, etc. + const char* kname = vmSymbols::name_for(vmIntrinsics::class_for(x->id())); + if (strchr(name, '_') == NULL) { + kname = NULL; + } else { + const char* kptr = strrchr(kname, '/'); + if (kptr != NULL) kname = kptr + 1; + } + if (kname == NULL) + output()->print("%s(", name); + else + output()->print("%s.%s(", kname, name); + for (int i = 0; i < x->number_of_arguments(); i++) { + if (i > 0) output()->print(", "); + print_value(x->argument_at(i)); + } + output()->put(')'); +} + + +void InstructionPrinter::do_BlockBegin(BlockBegin* x) { + // print block id + BlockEnd* end = x->end(); + output()->print("B%d ", x->block_id()); + + // print flags + bool printed_flag = false; + if (x->is_set(BlockBegin::std_entry_flag)) { + if (!printed_flag) output()->print("("); + output()->print("S"); printed_flag = true; + } + if (x->is_set(BlockBegin::osr_entry_flag)) { + if (!printed_flag) output()->print("("); + output()->print("O"); printed_flag = true; + } + if (x->is_set(BlockBegin::exception_entry_flag)) { + if (!printed_flag) output()->print("("); + output()->print("E"); printed_flag = true; + } + if (x->is_set(BlockBegin::subroutine_entry_flag)) { + if (!printed_flag) output()->print("("); + output()->print("s"); printed_flag = true; + } + if (x->is_set(BlockBegin::parser_loop_header_flag)) { + if (!printed_flag) output()->print("("); + output()->print("LH"); printed_flag = true; + } + if (x->is_set(BlockBegin::backward_branch_target_flag)) { + if (!printed_flag) output()->print("("); + output()->print("b"); printed_flag = true; + } + if (x->is_set(BlockBegin::was_visited_flag)) { + if (!printed_flag) output()->print("("); + output()->print("V"); printed_flag = true; + } + if (printed_flag) output()->print(") "); + + // print block bci range + output()->print("[%d, %d]", x->bci(), (end == NULL ? -1 : end->bci())); + + // print block successors + if (end != NULL && end->number_of_sux() > 0) { + output()->print(" ->"); + for (int i = 0; i < end->number_of_sux(); i++) { + output()->print(" B%d", end->sux_at(i)->block_id()); + } + } + // print exception handlers + if (x->number_of_exception_handlers() > 0) { + output()->print(" (xhandlers "); + for (int i = 0; i < x->number_of_exception_handlers(); i++) { + if (i > 0) output()->print(" "); + output()->print("B%d", x->exception_handler_at(i)->block_id()); + } + output()->put(')'); + } + + // print dominator block + if (x->dominator() != NULL) { + output()->print(" dom B%d", x->dominator()->block_id()); + } + + // print predecessors and successors + if (x->successors()->length() > 0) { + output()->print(" sux:"); + for (int i = 0; i < x->successors()->length(); i ++) { + output()->print(" B%d", x->successors()->at(i)->block_id()); + } + } + + if (x->number_of_preds() > 0) { + output()->print(" pred:"); + for (int i = 0; i < x->number_of_preds(); i ++) { + output()->print(" B%d", x->pred_at(i)->block_id()); + } + } + + if (!_print_phis) { + return; + } + + // print phi functions + bool has_phis_in_locals = false; + bool has_phis_on_stack = false; + + if (x->end() && x->end()->state()) { + ValueStack* state = x->state(); + + int i = 0; + while (!has_phis_on_stack && i < state->stack_size()) { + Value v = state->stack_at_inc(i); + has_phis_on_stack = is_phi_of_block(v, x); + } + + do { + for (i = 0; !has_phis_in_locals && i < state->locals_size();) { + Value v = state->local_at(i); + has_phis_in_locals = is_phi_of_block(v, x); + // also ignore illegal HiWords + if (v && !v->type()->is_illegal()) i += v->type()->size(); else i ++; + } + state = state->caller_state(); + } while (state != NULL); + + } + + // print values in locals + if (has_phis_in_locals) { + output()->cr(); output()->print_cr("Locals:"); + + ValueStack* state = x->state(); + do { + for (int i = 0; i < state->locals_size();) { + Value v = state->local_at(i); + if (v) { + print_phi(i, v, x); output()->cr(); + // also ignore illegal HiWords + i += (v->type()->is_illegal() ? 1 : v->type()->size()); + } else { + i ++; + } + } + output()->cr(); + state = state->caller_state(); + } while (state != NULL); + } + + // print values on stack + if (has_phis_on_stack) { + output()->print_cr("Stack:"); + int i = 0; + while (i < x->state()->stack_size()) { + int o = i; + Value v = x->state()->stack_at_inc(i); + if (v) { + print_phi(o, v, x); output()->cr(); + } + } + } +} + + +void InstructionPrinter::do_CheckCast(CheckCast* x) { + output()->print("checkcast("); + print_value(x->obj()); + output()->print(") "); + print_klass(x->klass()); +} + + +void InstructionPrinter::do_InstanceOf(InstanceOf* x) { + output()->print("instanceof("); + print_value(x->obj()); + output()->print(") "); + print_klass(x->klass()); +} + + +void InstructionPrinter::do_Goto(Goto* x) { + output()->print("goto B%d", x->default_sux()->block_id()); + if (x->is_safepoint()) output()->print(" (safepoint)"); +} + + +void InstructionPrinter::do_If(If* x) { + output()->print("if "); + print_value(x->x()); + output()->print(" %s ", cond_name(x->cond())); + print_value(x->y()); + output()->print(" then B%d else B%d", x->sux_at(0)->block_id(), x->sux_at(1)->block_id()); + if (x->is_safepoint()) output()->print(" (safepoint)"); +} + + +void InstructionPrinter::do_IfInstanceOf(IfInstanceOf* x) { + output()->print(""); +} + + +void InstructionPrinter::do_TableSwitch(TableSwitch* x) { + output()->print("tableswitch "); + if (x->is_safepoint()) output()->print("(safepoint) "); + print_value(x->tag()); + output()->cr(); + int l = x->length(); + for (int i = 0; i < l; i++) { + fill_to(instr_pos); + output()->print_cr("case %5d: B%d", x->lo_key() + i, x->sux_at(i)->block_id()); + } + fill_to(instr_pos); + output()->print("default : B%d", x->default_sux()->block_id()); +} + + +void InstructionPrinter::do_LookupSwitch(LookupSwitch* x) { + output()->print("lookupswitch "); + if (x->is_safepoint()) output()->print("(safepoint) "); + print_value(x->tag()); + output()->cr(); + int l = x->length(); + for (int i = 0; i < l; i++) { + fill_to(instr_pos); + output()->print_cr("case %5d: B%d", x->key_at(i), x->sux_at(i)->block_id()); + } + fill_to(instr_pos); + output()->print("default : B%d", x->default_sux()->block_id()); +} + + +void InstructionPrinter::do_Return(Return* x) { + if (x->result() == NULL) { + output()->print("return"); + } else { + output()->print("%creturn ", x->type()->tchar()); + print_value(x->result()); + } +} + + +void InstructionPrinter::do_Throw(Throw* x) { + output()->print("throw "); + print_value(x->exception()); +} + + +void InstructionPrinter::do_Base(Base* x) { + output()->print("std entry B%d", x->std_entry()->block_id()); + if (x->number_of_sux() > 1) { + output()->print(" osr entry B%d", x->osr_entry()->block_id()); + } +} + + +void InstructionPrinter::do_OsrEntry(OsrEntry* x) { + output()->print("osr entry"); +} + + +void InstructionPrinter::do_ExceptionObject(ExceptionObject* x) { + output()->print("incoming exception"); +} + + +void InstructionPrinter::do_RoundFP(RoundFP* x) { + output()->print("round_fp "); + print_value(x->input()); +} + + +void InstructionPrinter::do_UnsafeGetRaw(UnsafeGetRaw* x) { + print_unsafe_raw_op(x, "UnsafeGetRaw"); + output()->put(')'); +} + + +void InstructionPrinter::do_UnsafePutRaw(UnsafePutRaw* x) { + print_unsafe_raw_op(x, "UnsafePutRaw"); + output()->print(", value "); + print_value(x->value()); + output()->put(')'); +} + + +void InstructionPrinter::do_UnsafeGetObject(UnsafeGetObject* x) { + print_unsafe_object_op(x, "UnsafeGetObject"); + output()->put(')'); +} + + +void InstructionPrinter::do_UnsafePutObject(UnsafePutObject* x) { + print_unsafe_object_op(x, "UnsafePutObject"); + output()->print(", value "); + print_value(x->value()); + output()->put(')'); +} + + +void InstructionPrinter::do_UnsafePrefetchRead(UnsafePrefetchRead* x) { + print_unsafe_object_op(x, "UnsafePrefetchRead"); + output()->put(')'); +} + + +void InstructionPrinter::do_UnsafePrefetchWrite(UnsafePrefetchWrite* x) { + print_unsafe_object_op(x, "UnsafePrefetchWrite"); + output()->put(')'); +} + + +void InstructionPrinter::do_ProfileCall(ProfileCall* x) { + output()->print("profile "); + print_value(x->recv()); + output()->print(" %s.%s", x->method()->holder()->name()->as_utf8(), x->method()->name()->as_utf8()); + if (x->known_holder() != NULL) { + output()->print(", "); + print_klass(x->known_holder()); + } + output()->put(')'); +} + + +void InstructionPrinter::do_ProfileCounter(ProfileCounter* x) { + + ObjectConstant* oc = x->mdo()->type()->as_ObjectConstant(); + if (oc != NULL && oc->value()->is_method() && + x->offset() == methodOopDesc::interpreter_invocation_counter_offset_in_bytes()) { + print_value(x->mdo()); + output()->print(".interpreter_invocation_count += %d", x->increment()); + } else { + output()->print("counter ["); + print_value(x->mdo()); + output()->print(" + %d] += %d", x->offset(), x->increment()); + } +} + + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/c1/c1_InstructionPrinter.hpp b/hotspot/src/share/vm/c1/c1_InstructionPrinter.hpp new file mode 100644 index 00000000000..f0d0392817e --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_InstructionPrinter.hpp @@ -0,0 +1,128 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef PRODUCT +class InstructionPrinter: public InstructionVisitor { + private: + outputStream* _output; + bool _print_phis; + + enum LayoutConstants { + bci_pos = 2, + use_pos = 7, + temp_pos = 12, + instr_pos = 19, + end_pos = 60 + }; + + bool is_illegal_phi(Value v); + + public: + InstructionPrinter(bool print_phis = true, outputStream* output = tty) + : _print_phis(print_phis) + , _output(output) + {} + + outputStream* output() { return _output; } + + // helpers + static const char* basic_type_name(BasicType type); + static const char* cond_name(If::Condition cond); + static const char* op_name(Bytecodes::Code op); + bool is_phi_of_block(Value v, BlockBegin* b); + + // type-specific print functions + void print_klass(ciKlass* klass); + void print_object(Value obj); + + // generic print functions + void print_temp(Value value); + void print_field(AccessField* field); + void print_indexed(AccessIndexed* indexed); + void print_monitor(AccessMonitor* monitor); + void print_op2(Op2* instr); + void print_value(Value value); + void print_instr(Instruction* instr); + void print_stack(ValueStack* stack); + void print_inline_level(BlockBegin* block); + void print_unsafe_op(UnsafeOp* op, const char* name); + void print_unsafe_raw_op(UnsafeRawOp* op, const char* name); + void print_unsafe_object_op(UnsafeObjectOp* op, const char* name); + void print_phi(int i, Value v, BlockBegin* b); + void print_alias(Value v); + + // line printing of instructions + void fill_to(int pos, char filler = ' '); + void print_head(); + void print_line(Instruction* instr); + + // visitor functionality + virtual void do_Phi (Phi* x); + virtual void do_Local (Local* x); + virtual void do_Constant (Constant* x); + virtual void do_LoadField (LoadField* x); + virtual void do_StoreField (StoreField* x); + virtual void do_ArrayLength (ArrayLength* x); + virtual void do_LoadIndexed (LoadIndexed* x); + virtual void do_StoreIndexed (StoreIndexed* x); + virtual void do_NegateOp (NegateOp* x); + virtual void do_ArithmeticOp (ArithmeticOp* x); + virtual void do_ShiftOp (ShiftOp* x); + virtual void do_LogicOp (LogicOp* x); + virtual void do_CompareOp (CompareOp* x); + virtual void do_IfOp (IfOp* x); + virtual void do_Convert (Convert* x); + virtual void do_NullCheck (NullCheck* x); + virtual void do_Invoke (Invoke* x); + virtual void do_NewInstance (NewInstance* x); + virtual void do_NewTypeArray (NewTypeArray* x); + virtual void do_NewObjectArray (NewObjectArray* x); + virtual void do_NewMultiArray (NewMultiArray* x); + virtual void do_CheckCast (CheckCast* x); + virtual void do_InstanceOf (InstanceOf* x); + virtual void do_MonitorEnter (MonitorEnter* x); + virtual void do_MonitorExit (MonitorExit* x); + virtual void do_Intrinsic (Intrinsic* x); + virtual void do_BlockBegin (BlockBegin* x); + virtual void do_Goto (Goto* x); + virtual void do_If (If* x); + virtual void do_IfInstanceOf (IfInstanceOf* x); + virtual void do_TableSwitch (TableSwitch* x); + virtual void do_LookupSwitch (LookupSwitch* x); + virtual void do_Return (Return* x); + virtual void do_Throw (Throw* x); + virtual void do_Base (Base* x); + virtual void do_OsrEntry (OsrEntry* x); + virtual void do_ExceptionObject(ExceptionObject* x); + virtual void do_RoundFP (RoundFP* x); + virtual void do_UnsafeGetRaw (UnsafeGetRaw* x); + virtual void do_UnsafePutRaw (UnsafePutRaw* x); + virtual void do_UnsafeGetObject(UnsafeGetObject* x); + virtual void do_UnsafePutObject(UnsafePutObject* x); + virtual void do_UnsafePrefetchRead (UnsafePrefetchRead* x); + virtual void do_UnsafePrefetchWrite(UnsafePrefetchWrite* x); + virtual void do_ProfileCall (ProfileCall* x); + virtual void do_ProfileCounter (ProfileCounter* x); +}; +#endif // PRODUCT diff --git a/hotspot/src/share/vm/c1/c1_LIR.cpp b/hotspot/src/share/vm/c1/c1_LIR.cpp new file mode 100644 index 00000000000..60e25387a71 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_LIR.cpp @@ -0,0 +1,1892 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_LIR.cpp.incl" + +Register LIR_OprDesc::as_register() const { + return FrameMap::cpu_rnr2reg(cpu_regnr()); +} + +Register LIR_OprDesc::as_register_lo() const { + return FrameMap::cpu_rnr2reg(cpu_regnrLo()); +} + +Register LIR_OprDesc::as_register_hi() const { + return FrameMap::cpu_rnr2reg(cpu_regnrHi()); +} + +#ifdef IA32 + +XMMRegister LIR_OprDesc::as_xmm_float_reg() const { + return FrameMap::nr2xmmreg(xmm_regnr()); +} + +XMMRegister LIR_OprDesc::as_xmm_double_reg() const { + assert(xmm_regnrLo() == xmm_regnrHi(), "assumed in calculation"); + return FrameMap::nr2xmmreg(xmm_regnrLo()); +} + +#endif + + +#ifdef SPARC + +FloatRegister LIR_OprDesc::as_float_reg() const { + return FrameMap::nr2floatreg(fpu_regnr()); +} + +FloatRegister LIR_OprDesc::as_double_reg() const { + return FrameMap::nr2floatreg(fpu_regnrHi()); +} + +#endif + +LIR_Opr LIR_OprFact::illegalOpr = LIR_OprFact::illegal(); + +LIR_Opr LIR_OprFact::value_type(ValueType* type) { + ValueTag tag = type->tag(); + switch (tag) { + case objectTag : { + ClassConstant* c = type->as_ClassConstant(); + if (c != NULL && !c->value()->is_loaded()) { + return LIR_OprFact::oopConst(NULL); + } else { + return LIR_OprFact::oopConst(type->as_ObjectType()->encoding()); + } + } + case addressTag: return LIR_OprFact::intConst(type->as_AddressConstant()->value()); + case intTag : return LIR_OprFact::intConst(type->as_IntConstant()->value()); + case floatTag : return LIR_OprFact::floatConst(type->as_FloatConstant()->value()); + case longTag : return LIR_OprFact::longConst(type->as_LongConstant()->value()); + case doubleTag : return LIR_OprFact::doubleConst(type->as_DoubleConstant()->value()); + default: ShouldNotReachHere(); + } +} + + +LIR_Opr LIR_OprFact::dummy_value_type(ValueType* type) { + switch (type->tag()) { + case objectTag: return LIR_OprFact::oopConst(NULL); + case addressTag: + case intTag: return LIR_OprFact::intConst(0); + case floatTag: return LIR_OprFact::floatConst(0.0); + case longTag: return LIR_OprFact::longConst(0); + case doubleTag: return LIR_OprFact::doubleConst(0.0); + default: ShouldNotReachHere(); + } + return illegalOpr; +} + + + +//--------------------------------------------------- + + +LIR_Address::Scale LIR_Address::scale(BasicType type) { + int elem_size = type2aelembytes[type]; + switch (elem_size) { + case 1: return LIR_Address::times_1; + case 2: return LIR_Address::times_2; + case 4: return LIR_Address::times_4; + case 8: return LIR_Address::times_8; + } + ShouldNotReachHere(); + return LIR_Address::times_1; +} + + +#ifndef PRODUCT +void LIR_Address::verify() const { +#ifdef SPARC + assert(scale() == times_1, "Scaled addressing mode not available on SPARC and should not be used"); + assert(disp() == 0 || index()->is_illegal(), "can't have both"); +#endif +#ifdef _LP64 + assert(base()->is_cpu_register(), "wrong base operand"); + assert(index()->is_illegal() || index()->is_double_cpu(), "wrong index operand"); + assert(base()->type() == T_OBJECT || base()->type() == T_LONG, + "wrong type for addresses"); +#else + assert(base()->is_single_cpu(), "wrong base operand"); + assert(index()->is_illegal() || index()->is_single_cpu(), "wrong index operand"); + assert(base()->type() == T_OBJECT || base()->type() == T_INT, + "wrong type for addresses"); +#endif +} +#endif + + +//--------------------------------------------------- + +char LIR_OprDesc::type_char(BasicType t) { + switch (t) { + case T_ARRAY: + t = T_OBJECT; + case T_BOOLEAN: + case T_CHAR: + case T_FLOAT: + case T_DOUBLE: + case T_BYTE: + case T_SHORT: + case T_INT: + case T_LONG: + case T_OBJECT: + case T_ADDRESS: + case T_VOID: + return ::type2char(t); + + case T_ILLEGAL: + return '?'; + + default: + ShouldNotReachHere(); + } +} + +#ifndef PRODUCT +void LIR_OprDesc::validate_type() const { + +#ifdef ASSERT + if (!is_pointer() && !is_illegal()) { + switch (as_BasicType(type_field())) { + case T_LONG: + assert((kind_field() == cpu_register || kind_field() == stack_value) && size_field() == double_size, "must match"); + break; + case T_FLOAT: + assert((kind_field() == fpu_register || kind_field() == stack_value) && size_field() == single_size, "must match"); + break; + case T_DOUBLE: + assert((kind_field() == fpu_register || kind_field() == stack_value) && size_field() == double_size, "must match"); + break; + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + case T_OBJECT: + case T_ARRAY: + assert((kind_field() == cpu_register || kind_field() == stack_value) && size_field() == single_size, "must match"); + break; + + case T_ILLEGAL: + // XXX TKR also means unknown right now + // assert(is_illegal(), "must match"); + break; + + default: + ShouldNotReachHere(); + } + } +#endif + +} +#endif // PRODUCT + + +bool LIR_OprDesc::is_oop() const { + if (is_pointer()) { + return pointer()->is_oop_pointer(); + } else { + OprType t= type_field(); + assert(t != unknown_type, "not set"); + return t == object_type; + } +} + + + +void LIR_Op2::verify() const { +#ifdef ASSERT + switch (code()) { + case lir_cmove: + break; + + default: + assert(!result_opr()->is_register() || !result_opr()->is_oop_register(), + "can't produce oops from arith"); + } + + if (TwoOperandLIRForm) { + switch (code()) { + case lir_add: + case lir_sub: + case lir_mul: + case lir_mul_strictfp: + case lir_div: + case lir_div_strictfp: + case lir_rem: + case lir_logic_and: + case lir_logic_or: + case lir_logic_xor: + case lir_shl: + case lir_shr: + assert(in_opr1() == result_opr(), "opr1 and result must match"); + assert(in_opr1()->is_valid() && in_opr2()->is_valid(), "must be valid"); + break; + + // special handling for lir_ushr because of write barriers + case lir_ushr: + assert(in_opr1() == result_opr() || in_opr2()->is_constant(), "opr1 and result must match or shift count is constant"); + assert(in_opr1()->is_valid() && in_opr2()->is_valid(), "must be valid"); + break; + + } + } +#endif +} + + +LIR_OpBranch::LIR_OpBranch(LIR_Condition cond, BasicType type, BlockBegin* block) + : LIR_Op(lir_branch, LIR_OprFact::illegalOpr, (CodeEmitInfo*)NULL) + , _cond(cond) + , _type(type) + , _label(block->label()) + , _block(block) + , _ublock(NULL) + , _stub(NULL) { +} + +LIR_OpBranch::LIR_OpBranch(LIR_Condition cond, BasicType type, CodeStub* stub) : + LIR_Op(lir_branch, LIR_OprFact::illegalOpr, (CodeEmitInfo*)NULL) + , _cond(cond) + , _type(type) + , _label(stub->entry()) + , _block(NULL) + , _ublock(NULL) + , _stub(stub) { +} + +LIR_OpBranch::LIR_OpBranch(LIR_Condition cond, BasicType type, BlockBegin* block, BlockBegin* ublock) + : LIR_Op(lir_cond_float_branch, LIR_OprFact::illegalOpr, (CodeEmitInfo*)NULL) + , _cond(cond) + , _type(type) + , _label(block->label()) + , _block(block) + , _ublock(ublock) + , _stub(NULL) +{ +} + +void LIR_OpBranch::change_block(BlockBegin* b) { + assert(_block != NULL, "must have old block"); + assert(_block->label() == label(), "must be equal"); + + _block = b; + _label = b->label(); +} + +void LIR_OpBranch::change_ublock(BlockBegin* b) { + assert(_ublock != NULL, "must have old block"); + _ublock = b; +} + +void LIR_OpBranch::negate_cond() { + switch (_cond) { + case lir_cond_equal: _cond = lir_cond_notEqual; break; + case lir_cond_notEqual: _cond = lir_cond_equal; break; + case lir_cond_less: _cond = lir_cond_greaterEqual; break; + case lir_cond_lessEqual: _cond = lir_cond_greater; break; + case lir_cond_greaterEqual: _cond = lir_cond_less; break; + case lir_cond_greater: _cond = lir_cond_lessEqual; break; + default: ShouldNotReachHere(); + } +} + + +LIR_OpTypeCheck::LIR_OpTypeCheck(LIR_Code code, LIR_Opr result, LIR_Opr object, ciKlass* klass, + LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, + bool fast_check, CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, + CodeStub* stub, + ciMethod* profiled_method, + int profiled_bci) + : LIR_Op(code, result, NULL) + , _object(object) + , _array(LIR_OprFact::illegalOpr) + , _klass(klass) + , _tmp1(tmp1) + , _tmp2(tmp2) + , _tmp3(tmp3) + , _fast_check(fast_check) + , _stub(stub) + , _info_for_patch(info_for_patch) + , _info_for_exception(info_for_exception) + , _profiled_method(profiled_method) + , _profiled_bci(profiled_bci) { + if (code == lir_checkcast) { + assert(info_for_exception != NULL, "checkcast throws exceptions"); + } else if (code == lir_instanceof) { + assert(info_for_exception == NULL, "instanceof throws no exceptions"); + } else { + ShouldNotReachHere(); + } +} + + + +LIR_OpTypeCheck::LIR_OpTypeCheck(LIR_Code code, LIR_Opr object, LIR_Opr array, LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, CodeEmitInfo* info_for_exception, ciMethod* profiled_method, int profiled_bci) + : LIR_Op(code, LIR_OprFact::illegalOpr, NULL) + , _object(object) + , _array(array) + , _klass(NULL) + , _tmp1(tmp1) + , _tmp2(tmp2) + , _tmp3(tmp3) + , _fast_check(false) + , _stub(NULL) + , _info_for_patch(NULL) + , _info_for_exception(info_for_exception) + , _profiled_method(profiled_method) + , _profiled_bci(profiled_bci) { + if (code == lir_store_check) { + _stub = new ArrayStoreExceptionStub(info_for_exception); + assert(info_for_exception != NULL, "store_check throws exceptions"); + } else { + ShouldNotReachHere(); + } +} + + +LIR_OpArrayCopy::LIR_OpArrayCopy(LIR_Opr src, LIR_Opr src_pos, LIR_Opr dst, LIR_Opr dst_pos, LIR_Opr length, + LIR_Opr tmp, ciArrayKlass* expected_type, int flags, CodeEmitInfo* info) + : LIR_Op(lir_arraycopy, LIR_OprFact::illegalOpr, info) + , _tmp(tmp) + , _src(src) + , _src_pos(src_pos) + , _dst(dst) + , _dst_pos(dst_pos) + , _flags(flags) + , _expected_type(expected_type) + , _length(length) { + _stub = new ArrayCopyStub(this); +} + + +//-------------------verify-------------------------- + +void LIR_Op1::verify() const { + switch(code()) { + case lir_move: + assert(in_opr()->is_valid() && result_opr()->is_valid(), "must be"); + break; + case lir_null_check: + assert(in_opr()->is_register(), "must be"); + break; + case lir_return: + assert(in_opr()->is_register() || in_opr()->is_illegal(), "must be"); + break; + } +} + +void LIR_OpRTCall::verify() const { + assert(strcmp(Runtime1::name_for_address(addr()), "") != 0, "unknown function"); +} + +//-------------------visits-------------------------- + +// complete rework of LIR instruction visitor. +// The virtual calls for each instruction type is replaced by a big +// switch that adds the operands for each instruction + +void LIR_OpVisitState::visit(LIR_Op* op) { + // copy information from the LIR_Op + reset(); + set_op(op); + + switch (op->code()) { + +// LIR_Op0 + case lir_word_align: // result and info always invalid + case lir_backwardbranch_target: // result and info always invalid + case lir_build_frame: // result and info always invalid + case lir_fpop_raw: // result and info always invalid + case lir_24bit_FPU: // result and info always invalid + case lir_reset_FPU: // result and info always invalid + case lir_breakpoint: // result and info always invalid + case lir_membar: // result and info always invalid + case lir_membar_acquire: // result and info always invalid + case lir_membar_release: // result and info always invalid + { + assert(op->as_Op0() != NULL, "must be"); + assert(op->_info == NULL, "info not used by this instruction"); + assert(op->_result->is_illegal(), "not used"); + break; + } + + case lir_nop: // may have info, result always invalid + case lir_std_entry: // may have result, info always invalid + case lir_osr_entry: // may have result, info always invalid + case lir_get_thread: // may have result, info always invalid + { + assert(op->as_Op0() != NULL, "must be"); + if (op->_info != NULL) do_info(op->_info); + if (op->_result->is_valid()) do_output(op->_result); + break; + } + + +// LIR_OpLabel + case lir_label: // result and info always invalid + { + assert(op->as_OpLabel() != NULL, "must be"); + assert(op->_info == NULL, "info not used by this instruction"); + assert(op->_result->is_illegal(), "not used"); + break; + } + + +// LIR_Op1 + case lir_fxch: // input always valid, result and info always invalid + case lir_fld: // input always valid, result and info always invalid + case lir_ffree: // input always valid, result and info always invalid + case lir_push: // input always valid, result and info always invalid + case lir_pop: // input always valid, result and info always invalid + case lir_return: // input always valid, result and info always invalid + case lir_leal: // input and result always valid, info always invalid + case lir_neg: // input and result always valid, info always invalid + case lir_monaddr: // input and result always valid, info always invalid + case lir_null_check: // input and info always valid, result always invalid + case lir_move: // input and result always valid, may have info + case lir_prefetchr: // input always valid, result and info always invalid + case lir_prefetchw: // input always valid, result and info always invalid + { + assert(op->as_Op1() != NULL, "must be"); + LIR_Op1* op1 = (LIR_Op1*)op; + + if (op1->_info) do_info(op1->_info); + if (op1->_opr->is_valid()) do_input(op1->_opr); + if (op1->_result->is_valid()) do_output(op1->_result); + + break; + } + + case lir_safepoint: + { + assert(op->as_Op1() != NULL, "must be"); + LIR_Op1* op1 = (LIR_Op1*)op; + + assert(op1->_info != NULL, ""); do_info(op1->_info); + if (op1->_opr->is_valid()) do_temp(op1->_opr); // safepoints on SPARC need temporary register + assert(op1->_result->is_illegal(), "safepoint does not produce value"); + + break; + } + +// LIR_OpConvert; + case lir_convert: // input and result always valid, info always invalid + { + assert(op->as_OpConvert() != NULL, "must be"); + LIR_OpConvert* opConvert = (LIR_OpConvert*)op; + + assert(opConvert->_info == NULL, "must be"); + if (opConvert->_opr->is_valid()) do_input(opConvert->_opr); + if (opConvert->_result->is_valid()) do_output(opConvert->_result); + do_stub(opConvert->_stub); + + break; + } + +// LIR_OpBranch; + case lir_branch: // may have info, input and result register always invalid + case lir_cond_float_branch: // may have info, input and result register always invalid + { + assert(op->as_OpBranch() != NULL, "must be"); + LIR_OpBranch* opBranch = (LIR_OpBranch*)op; + + if (opBranch->_info != NULL) do_info(opBranch->_info); + assert(opBranch->_result->is_illegal(), "not used"); + if (opBranch->_stub != NULL) opBranch->stub()->visit(this); + + break; + } + + +// LIR_OpAllocObj + case lir_alloc_object: + { + assert(op->as_OpAllocObj() != NULL, "must be"); + LIR_OpAllocObj* opAllocObj = (LIR_OpAllocObj*)op; + + if (opAllocObj->_info) do_info(opAllocObj->_info); + if (opAllocObj->_opr->is_valid()) do_input(opAllocObj->_opr); + if (opAllocObj->_tmp1->is_valid()) do_temp(opAllocObj->_tmp1); + if (opAllocObj->_tmp2->is_valid()) do_temp(opAllocObj->_tmp2); + if (opAllocObj->_tmp3->is_valid()) do_temp(opAllocObj->_tmp3); + if (opAllocObj->_tmp4->is_valid()) do_temp(opAllocObj->_tmp4); + if (opAllocObj->_result->is_valid()) do_output(opAllocObj->_result); + do_stub(opAllocObj->_stub); + break; + } + + +// LIR_OpRoundFP; + case lir_roundfp: { + assert(op->as_OpRoundFP() != NULL, "must be"); + LIR_OpRoundFP* opRoundFP = (LIR_OpRoundFP*)op; + + assert(op->_info == NULL, "info not used by this instruction"); + assert(opRoundFP->_tmp->is_illegal(), "not used"); + do_input(opRoundFP->_opr); + do_output(opRoundFP->_result); + + break; + } + + +// LIR_Op2 + case lir_cmp: + case lir_cmp_l2i: + case lir_ucmp_fd2i: + case lir_cmp_fd2i: + case lir_add: + case lir_sub: + case lir_mul: + case lir_div: + case lir_rem: + case lir_sqrt: + case lir_abs: + case lir_log: + case lir_log10: + case lir_logic_and: + case lir_logic_or: + case lir_logic_xor: + case lir_shl: + case lir_shr: + case lir_ushr: + { + assert(op->as_Op2() != NULL, "must be"); + LIR_Op2* op2 = (LIR_Op2*)op; + + if (op2->_info) do_info(op2->_info); + if (op2->_opr1->is_valid()) do_input(op2->_opr1); + if (op2->_opr2->is_valid()) do_input(op2->_opr2); + if (op2->_tmp->is_valid()) do_temp(op2->_tmp); + if (op2->_result->is_valid()) do_output(op2->_result); + + break; + } + + // special handling for cmove: right input operand must not be equal + // to the result operand, otherwise the backend fails + case lir_cmove: + { + assert(op->as_Op2() != NULL, "must be"); + LIR_Op2* op2 = (LIR_Op2*)op; + + assert(op2->_info == NULL && op2->_tmp->is_illegal(), "not used"); + assert(op2->_opr1->is_valid() && op2->_opr2->is_valid() && op2->_result->is_valid(), "used"); + + do_input(op2->_opr1); + do_input(op2->_opr2); + do_temp(op2->_opr2); + do_output(op2->_result); + + break; + } + + // vspecial handling for strict operations: register input operands + // as temp to guarantee that they do not overlap with other + // registers + case lir_mul_strictfp: + case lir_div_strictfp: + { + assert(op->as_Op2() != NULL, "must be"); + LIR_Op2* op2 = (LIR_Op2*)op; + + assert(op2->_info == NULL, "not used"); + assert(op2->_opr1->is_valid(), "used"); + assert(op2->_opr2->is_valid(), "used"); + assert(op2->_result->is_valid(), "used"); + + do_input(op2->_opr1); do_temp(op2->_opr1); + do_input(op2->_opr2); do_temp(op2->_opr2); + if (op2->_tmp->is_valid()) do_temp(op2->_tmp); + do_output(op2->_result); + + break; + } + + case lir_throw: + case lir_unwind: { + assert(op->as_Op2() != NULL, "must be"); + LIR_Op2* op2 = (LIR_Op2*)op; + + if (op2->_info) do_info(op2->_info); + if (op2->_opr1->is_valid()) do_temp(op2->_opr1); + if (op2->_opr2->is_valid()) do_input(op2->_opr2); // exception object is input parameter + assert(op2->_result->is_illegal(), "no result"); + + break; + } + + + case lir_tan: + case lir_sin: + case lir_cos: { + assert(op->as_Op2() != NULL, "must be"); + LIR_Op2* op2 = (LIR_Op2*)op; + + // sin and cos need two temporary fpu stack slots, so register + // two temp operands. Register input operand as temp to + // guarantee that they do not overlap + assert(op2->_info == NULL, "not used"); + assert(op2->_opr1->is_valid(), "used"); + do_input(op2->_opr1); do_temp(op2->_opr1); + + if (op2->_opr2->is_valid()) do_temp(op2->_opr2); + if (op2->_tmp->is_valid()) do_temp(op2->_tmp); + if (op2->_result->is_valid()) do_output(op2->_result); + + break; + } + + +// LIR_Op3 + case lir_idiv: + case lir_irem: { + assert(op->as_Op3() != NULL, "must be"); + LIR_Op3* op3= (LIR_Op3*)op; + + if (op3->_info) do_info(op3->_info); + if (op3->_opr1->is_valid()) do_input(op3->_opr1); + + // second operand is input and temp, so ensure that second operand + // and third operand get not the same register + if (op3->_opr2->is_valid()) do_input(op3->_opr2); + if (op3->_opr2->is_valid()) do_temp(op3->_opr2); + if (op3->_opr3->is_valid()) do_temp(op3->_opr3); + + if (op3->_result->is_valid()) do_output(op3->_result); + + break; + } + + +// LIR_OpJavaCall + case lir_static_call: + case lir_optvirtual_call: + case lir_icvirtual_call: + case lir_virtual_call: { + assert(op->as_OpJavaCall() != NULL, "must be"); + LIR_OpJavaCall* opJavaCall = (LIR_OpJavaCall*)op; + + if (opJavaCall->_receiver->is_valid()) do_input(opJavaCall->_receiver); + + // only visit register parameters + int n = opJavaCall->_arguments->length(); + for (int i = 0; i < n; i++) { + if (!opJavaCall->_arguments->at(i)->is_pointer()) { + do_input(*opJavaCall->_arguments->adr_at(i)); + } + } + + if (opJavaCall->_info) do_info(opJavaCall->_info); + do_call(); + if (opJavaCall->_result->is_valid()) do_output(opJavaCall->_result); + + break; + } + + +// LIR_OpRTCall + case lir_rtcall: { + assert(op->as_OpRTCall() != NULL, "must be"); + LIR_OpRTCall* opRTCall = (LIR_OpRTCall*)op; + + // only visit register parameters + int n = opRTCall->_arguments->length(); + for (int i = 0; i < n; i++) { + if (!opRTCall->_arguments->at(i)->is_pointer()) { + do_input(*opRTCall->_arguments->adr_at(i)); + } + } + if (opRTCall->_info) do_info(opRTCall->_info); + if (opRTCall->_tmp->is_valid()) do_temp(opRTCall->_tmp); + do_call(); + if (opRTCall->_result->is_valid()) do_output(opRTCall->_result); + + break; + } + + +// LIR_OpArrayCopy + case lir_arraycopy: { + assert(op->as_OpArrayCopy() != NULL, "must be"); + LIR_OpArrayCopy* opArrayCopy = (LIR_OpArrayCopy*)op; + + assert(opArrayCopy->_result->is_illegal(), "unused"); + assert(opArrayCopy->_src->is_valid(), "used"); do_input(opArrayCopy->_src); do_temp(opArrayCopy->_src); + assert(opArrayCopy->_src_pos->is_valid(), "used"); do_input(opArrayCopy->_src_pos); do_temp(opArrayCopy->_src_pos); + assert(opArrayCopy->_dst->is_valid(), "used"); do_input(opArrayCopy->_dst); do_temp(opArrayCopy->_dst); + assert(opArrayCopy->_dst_pos->is_valid(), "used"); do_input(opArrayCopy->_dst_pos); do_temp(opArrayCopy->_dst_pos); + assert(opArrayCopy->_length->is_valid(), "used"); do_input(opArrayCopy->_length); do_temp(opArrayCopy->_length); + assert(opArrayCopy->_tmp->is_valid(), "used"); do_temp(opArrayCopy->_tmp); + if (opArrayCopy->_info) do_info(opArrayCopy->_info); + + // the implementation of arraycopy always has a call into the runtime + do_call(); + + break; + } + + +// LIR_OpLock + case lir_lock: + case lir_unlock: { + assert(op->as_OpLock() != NULL, "must be"); + LIR_OpLock* opLock = (LIR_OpLock*)op; + + if (opLock->_info) do_info(opLock->_info); + + // TODO: check if these operands really have to be temp + // (or if input is sufficient). This may have influence on the oop map! + assert(opLock->_lock->is_valid(), "used"); do_temp(opLock->_lock); + assert(opLock->_hdr->is_valid(), "used"); do_temp(opLock->_hdr); + assert(opLock->_obj->is_valid(), "used"); do_temp(opLock->_obj); + + if (opLock->_scratch->is_valid()) do_temp(opLock->_scratch); + assert(opLock->_result->is_illegal(), "unused"); + + do_stub(opLock->_stub); + + break; + } + + +// LIR_OpDelay + case lir_delay_slot: { + assert(op->as_OpDelay() != NULL, "must be"); + LIR_OpDelay* opDelay = (LIR_OpDelay*)op; + + visit(opDelay->delay_op()); + break; + } + +// LIR_OpTypeCheck + case lir_instanceof: + case lir_checkcast: + case lir_store_check: { + assert(op->as_OpTypeCheck() != NULL, "must be"); + LIR_OpTypeCheck* opTypeCheck = (LIR_OpTypeCheck*)op; + + if (opTypeCheck->_info_for_exception) do_info(opTypeCheck->_info_for_exception); + if (opTypeCheck->_info_for_patch) do_info(opTypeCheck->_info_for_patch); + if (opTypeCheck->_object->is_valid()) do_input(opTypeCheck->_object); + if (opTypeCheck->_array->is_valid()) do_input(opTypeCheck->_array); + if (opTypeCheck->_tmp1->is_valid()) do_temp(opTypeCheck->_tmp1); + if (opTypeCheck->_tmp2->is_valid()) do_temp(opTypeCheck->_tmp2); + if (opTypeCheck->_tmp3->is_valid()) do_temp(opTypeCheck->_tmp3); + if (opTypeCheck->_result->is_valid()) do_output(opTypeCheck->_result); + do_stub(opTypeCheck->_stub); + break; + } + +// LIR_OpCompareAndSwap + case lir_cas_long: + case lir_cas_obj: + case lir_cas_int: { + assert(op->as_OpCompareAndSwap() != NULL, "must be"); + LIR_OpCompareAndSwap* opCompareAndSwap = (LIR_OpCompareAndSwap*)op; + + if (opCompareAndSwap->_info) do_info(opCompareAndSwap->_info); + if (opCompareAndSwap->_addr->is_valid()) do_input(opCompareAndSwap->_addr); + if (opCompareAndSwap->_cmp_value->is_valid()) do_input(opCompareAndSwap->_cmp_value); + if (opCompareAndSwap->_new_value->is_valid()) do_input(opCompareAndSwap->_new_value); + if (opCompareAndSwap->_tmp1->is_valid()) do_temp(opCompareAndSwap->_tmp1); + if (opCompareAndSwap->_tmp2->is_valid()) do_temp(opCompareAndSwap->_tmp2); + if (opCompareAndSwap->_result->is_valid()) do_output(opCompareAndSwap->_result); + + break; + } + + +// LIR_OpAllocArray; + case lir_alloc_array: { + assert(op->as_OpAllocArray() != NULL, "must be"); + LIR_OpAllocArray* opAllocArray = (LIR_OpAllocArray*)op; + + if (opAllocArray->_info) do_info(opAllocArray->_info); + if (opAllocArray->_klass->is_valid()) do_input(opAllocArray->_klass); do_temp(opAllocArray->_klass); + if (opAllocArray->_len->is_valid()) do_input(opAllocArray->_len); do_temp(opAllocArray->_len); + if (opAllocArray->_tmp1->is_valid()) do_temp(opAllocArray->_tmp1); + if (opAllocArray->_tmp2->is_valid()) do_temp(opAllocArray->_tmp2); + if (opAllocArray->_tmp3->is_valid()) do_temp(opAllocArray->_tmp3); + if (opAllocArray->_tmp4->is_valid()) do_temp(opAllocArray->_tmp4); + if (opAllocArray->_result->is_valid()) do_output(opAllocArray->_result); + do_stub(opAllocArray->_stub); + break; + } + +// LIR_OpProfileCall: + case lir_profile_call: { + assert(op->as_OpProfileCall() != NULL, "must be"); + LIR_OpProfileCall* opProfileCall = (LIR_OpProfileCall*)op; + + if (opProfileCall->_recv->is_valid()) do_temp(opProfileCall->_recv); + assert(opProfileCall->_mdo->is_valid(), "used"); do_temp(opProfileCall->_mdo); + assert(opProfileCall->_tmp1->is_valid(), "used"); do_temp(opProfileCall->_tmp1); + break; + } + + default: + ShouldNotReachHere(); + } +} + + +void LIR_OpVisitState::do_stub(CodeStub* stub) { + if (stub != NULL) { + stub->visit(this); + } +} + +XHandlers* LIR_OpVisitState::all_xhandler() { + XHandlers* result = NULL; + + int i; + for (i = 0; i < info_count(); i++) { + if (info_at(i)->exception_handlers() != NULL) { + result = info_at(i)->exception_handlers(); + break; + } + } + +#ifdef ASSERT + for (i = 0; i < info_count(); i++) { + assert(info_at(i)->exception_handlers() == NULL || + info_at(i)->exception_handlers() == result, + "only one xhandler list allowed per LIR-operation"); + } +#endif + + if (result != NULL) { + return result; + } else { + return new XHandlers(); + } + + return result; +} + + +#ifdef ASSERT +bool LIR_OpVisitState::no_operands(LIR_Op* op) { + visit(op); + + return opr_count(inputMode) == 0 && + opr_count(outputMode) == 0 && + opr_count(tempMode) == 0 && + info_count() == 0 && + !has_call() && + !has_slow_case(); +} +#endif + +//--------------------------------------------------- + + +void LIR_OpJavaCall::emit_code(LIR_Assembler* masm) { + masm->emit_call(this); +} + +void LIR_OpRTCall::emit_code(LIR_Assembler* masm) { + masm->emit_rtcall(this); +} + +void LIR_OpLabel::emit_code(LIR_Assembler* masm) { + masm->emit_opLabel(this); +} + +void LIR_OpArrayCopy::emit_code(LIR_Assembler* masm) { + masm->emit_arraycopy(this); + masm->emit_code_stub(stub()); +} + +void LIR_Op0::emit_code(LIR_Assembler* masm) { + masm->emit_op0(this); +} + +void LIR_Op1::emit_code(LIR_Assembler* masm) { + masm->emit_op1(this); +} + +void LIR_OpAllocObj::emit_code(LIR_Assembler* masm) { + masm->emit_alloc_obj(this); + masm->emit_code_stub(stub()); +} + +void LIR_OpBranch::emit_code(LIR_Assembler* masm) { + masm->emit_opBranch(this); + if (stub()) { + masm->emit_code_stub(stub()); + } +} + +void LIR_OpConvert::emit_code(LIR_Assembler* masm) { + masm->emit_opConvert(this); + if (stub() != NULL) { + masm->emit_code_stub(stub()); + } +} + +void LIR_Op2::emit_code(LIR_Assembler* masm) { + masm->emit_op2(this); +} + +void LIR_OpAllocArray::emit_code(LIR_Assembler* masm) { + masm->emit_alloc_array(this); + masm->emit_code_stub(stub()); +} + +void LIR_OpTypeCheck::emit_code(LIR_Assembler* masm) { + masm->emit_opTypeCheck(this); + if (stub()) { + masm->emit_code_stub(stub()); + } +} + +void LIR_OpCompareAndSwap::emit_code(LIR_Assembler* masm) { + masm->emit_compare_and_swap(this); +} + +void LIR_Op3::emit_code(LIR_Assembler* masm) { + masm->emit_op3(this); +} + +void LIR_OpLock::emit_code(LIR_Assembler* masm) { + masm->emit_lock(this); + if (stub()) { + masm->emit_code_stub(stub()); + } +} + + +void LIR_OpDelay::emit_code(LIR_Assembler* masm) { + masm->emit_delay(this); +} + + +void LIR_OpProfileCall::emit_code(LIR_Assembler* masm) { + masm->emit_profile_call(this); +} + + +// LIR_List +LIR_List::LIR_List(Compilation* compilation, BlockBegin* block) + : _operations(8) + , _compilation(compilation) +#ifndef PRODUCT + , _block(block) +#endif +#ifdef ASSERT + , _file(NULL) + , _line(0) +#endif +{ } + + +#ifdef ASSERT +void LIR_List::set_file_and_line(const char * file, int line) { + const char * f = strrchr(file, '/'); + if (f == NULL) f = strrchr(file, '\\'); + if (f == NULL) { + f = file; + } else { + f++; + } + _file = f; + _line = line; +} +#endif + + +void LIR_List::append(LIR_InsertionBuffer* buffer) { + assert(this == buffer->lir_list(), "wrong lir list"); + const int n = _operations.length(); + + if (buffer->number_of_ops() > 0) { + // increase size of instructions list + _operations.at_grow(n + buffer->number_of_ops() - 1, NULL); + // insert ops from buffer into instructions list + int op_index = buffer->number_of_ops() - 1; + int ip_index = buffer->number_of_insertion_points() - 1; + int from_index = n - 1; + int to_index = _operations.length() - 1; + for (; ip_index >= 0; ip_index --) { + int index = buffer->index_at(ip_index); + // make room after insertion point + while (index < from_index) { + _operations.at_put(to_index --, _operations.at(from_index --)); + } + // insert ops from buffer + for (int i = buffer->count_at(ip_index); i > 0; i --) { + _operations.at_put(to_index --, buffer->op_at(op_index --)); + } + } + } + + buffer->finish(); +} + + +void LIR_List::oop2reg_patch(jobject o, LIR_Opr reg, CodeEmitInfo* info) { + append(new LIR_Op1(lir_move, LIR_OprFact::oopConst(o), reg, T_OBJECT, lir_patch_normal, info)); +} + + +void LIR_List::load(LIR_Address* addr, LIR_Opr src, CodeEmitInfo* info, LIR_PatchCode patch_code) { + append(new LIR_Op1( + lir_move, + LIR_OprFact::address(addr), + src, + addr->type(), + patch_code, + info)); +} + + +void LIR_List::volatile_load_mem_reg(LIR_Address* address, LIR_Opr dst, CodeEmitInfo* info, LIR_PatchCode patch_code) { + append(new LIR_Op1( + lir_move, + LIR_OprFact::address(address), + dst, + address->type(), + patch_code, + info, lir_move_volatile)); +} + +void LIR_List::volatile_load_unsafe_reg(LIR_Opr base, LIR_Opr offset, LIR_Opr dst, BasicType type, CodeEmitInfo* info, LIR_PatchCode patch_code) { + append(new LIR_Op1( + lir_move, + LIR_OprFact::address(new LIR_Address(base, offset, type)), + dst, + type, + patch_code, + info, lir_move_volatile)); +} + + +void LIR_List::prefetch(LIR_Address* addr, bool is_store) { + append(new LIR_Op1( + is_store ? lir_prefetchw : lir_prefetchr, + LIR_OprFact::address(addr))); +} + + +void LIR_List::store_mem_int(jint v, LIR_Opr base, int offset_in_bytes, BasicType type, CodeEmitInfo* info, LIR_PatchCode patch_code) { + append(new LIR_Op1( + lir_move, + LIR_OprFact::intConst(v), + LIR_OprFact::address(new LIR_Address(base, offset_in_bytes, type)), + type, + patch_code, + info)); +} + + +void LIR_List::store_mem_oop(jobject o, LIR_Opr base, int offset_in_bytes, BasicType type, CodeEmitInfo* info, LIR_PatchCode patch_code) { + append(new LIR_Op1( + lir_move, + LIR_OprFact::oopConst(o), + LIR_OprFact::address(new LIR_Address(base, offset_in_bytes, type)), + type, + patch_code, + info)); +} + + +void LIR_List::store(LIR_Opr src, LIR_Address* addr, CodeEmitInfo* info, LIR_PatchCode patch_code) { + append(new LIR_Op1( + lir_move, + src, + LIR_OprFact::address(addr), + addr->type(), + patch_code, + info)); +} + + +void LIR_List::volatile_store_mem_reg(LIR_Opr src, LIR_Address* addr, CodeEmitInfo* info, LIR_PatchCode patch_code) { + append(new LIR_Op1( + lir_move, + src, + LIR_OprFact::address(addr), + addr->type(), + patch_code, + info, + lir_move_volatile)); +} + +void LIR_List::volatile_store_unsafe_reg(LIR_Opr src, LIR_Opr base, LIR_Opr offset, BasicType type, CodeEmitInfo* info, LIR_PatchCode patch_code) { + append(new LIR_Op1( + lir_move, + src, + LIR_OprFact::address(new LIR_Address(base, offset, type)), + type, + patch_code, + info, lir_move_volatile)); +} + + +void LIR_List::idiv(LIR_Opr left, LIR_Opr right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info) { + append(new LIR_Op3( + lir_idiv, + left, + right, + tmp, + res, + info)); +} + + +void LIR_List::idiv(LIR_Opr left, int right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info) { + append(new LIR_Op3( + lir_idiv, + left, + LIR_OprFact::intConst(right), + tmp, + res, + info)); +} + + +void LIR_List::irem(LIR_Opr left, LIR_Opr right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info) { + append(new LIR_Op3( + lir_irem, + left, + right, + tmp, + res, + info)); +} + + +void LIR_List::irem(LIR_Opr left, int right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info) { + append(new LIR_Op3( + lir_irem, + left, + LIR_OprFact::intConst(right), + tmp, + res, + info)); +} + + +void LIR_List::cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int c, CodeEmitInfo* info) { + append(new LIR_Op2( + lir_cmp, + condition, + LIR_OprFact::address(new LIR_Address(base, disp, T_INT)), + LIR_OprFact::intConst(c), + info)); +} + + +void LIR_List::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Address* addr, CodeEmitInfo* info) { + append(new LIR_Op2( + lir_cmp, + condition, + reg, + LIR_OprFact::address(addr), + info)); +} + +void LIR_List::allocate_object(LIR_Opr dst, LIR_Opr t1, LIR_Opr t2, LIR_Opr t3, LIR_Opr t4, + int header_size, int object_size, LIR_Opr klass, bool init_check, CodeStub* stub) { + append(new LIR_OpAllocObj( + klass, + dst, + t1, + t2, + t3, + t4, + header_size, + object_size, + init_check, + stub)); +} + +void LIR_List::allocate_array(LIR_Opr dst, LIR_Opr len, LIR_Opr t1,LIR_Opr t2, LIR_Opr t3,LIR_Opr t4, BasicType type, LIR_Opr klass, CodeStub* stub) { + append(new LIR_OpAllocArray( + klass, + len, + dst, + t1, + t2, + t3, + t4, + type, + stub)); +} + +void LIR_List::shift_left(LIR_Opr value, LIR_Opr count, LIR_Opr dst, LIR_Opr tmp) { + append(new LIR_Op2( + lir_shl, + value, + count, + dst, + tmp)); +} + +void LIR_List::shift_right(LIR_Opr value, LIR_Opr count, LIR_Opr dst, LIR_Opr tmp) { + append(new LIR_Op2( + lir_shr, + value, + count, + dst, + tmp)); +} + + +void LIR_List::unsigned_shift_right(LIR_Opr value, LIR_Opr count, LIR_Opr dst, LIR_Opr tmp) { + append(new LIR_Op2( + lir_ushr, + value, + count, + dst, + tmp)); +} + +void LIR_List::fcmp2int(LIR_Opr left, LIR_Opr right, LIR_Opr dst, bool is_unordered_less) { + append(new LIR_Op2(is_unordered_less ? lir_ucmp_fd2i : lir_cmp_fd2i, + left, + right, + dst)); +} + +void LIR_List::lock_object(LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub, CodeEmitInfo* info) { + append(new LIR_OpLock( + lir_lock, + hdr, + obj, + lock, + scratch, + stub, + info)); +} + +void LIR_List::unlock_object(LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, CodeStub* stub) { + append(new LIR_OpLock( + lir_unlock, + hdr, + obj, + lock, + LIR_OprFact::illegalOpr, + stub, + NULL)); +} + + +void check_LIR() { + // cannot do the proper checking as PRODUCT and other modes return different results + // guarantee(sizeof(LIR_OprDesc) == wordSize, "may not have a v-table"); +} + + + +void LIR_List::checkcast (LIR_Opr result, LIR_Opr object, ciKlass* klass, + LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check, + CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub, + ciMethod* profiled_method, int profiled_bci) { + append(new LIR_OpTypeCheck(lir_checkcast, result, object, klass, + tmp1, tmp2, tmp3, fast_check, info_for_exception, info_for_patch, stub, + profiled_method, profiled_bci)); +} + + +void LIR_List::instanceof(LIR_Opr result, LIR_Opr object, ciKlass* klass, LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check, CodeEmitInfo* info_for_patch) { + append(new LIR_OpTypeCheck(lir_instanceof, result, object, klass, tmp1, tmp2, tmp3, fast_check, NULL, info_for_patch, NULL, NULL, 0)); +} + + +void LIR_List::store_check(LIR_Opr object, LIR_Opr array, LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, CodeEmitInfo* info_for_exception) { + append(new LIR_OpTypeCheck(lir_store_check, object, array, tmp1, tmp2, tmp3, info_for_exception, NULL, 0)); +} + + +void LIR_List::cas_long(LIR_Opr addr, LIR_Opr cmp_value, LIR_Opr new_value, LIR_Opr t1, LIR_Opr t2) { + // Compare and swap produces condition code "zero" if contents_of(addr) == cmp_value, + // implying successful swap of new_value into addr + append(new LIR_OpCompareAndSwap(lir_cas_long, addr, cmp_value, new_value, t1, t2)); +} + +void LIR_List::cas_obj(LIR_Opr addr, LIR_Opr cmp_value, LIR_Opr new_value, LIR_Opr t1, LIR_Opr t2) { + // Compare and swap produces condition code "zero" if contents_of(addr) == cmp_value, + // implying successful swap of new_value into addr + append(new LIR_OpCompareAndSwap(lir_cas_obj, addr, cmp_value, new_value, t1, t2)); +} + +void LIR_List::cas_int(LIR_Opr addr, LIR_Opr cmp_value, LIR_Opr new_value, LIR_Opr t1, LIR_Opr t2) { + // Compare and swap produces condition code "zero" if contents_of(addr) == cmp_value, + // implying successful swap of new_value into addr + append(new LIR_OpCompareAndSwap(lir_cas_int, addr, cmp_value, new_value, t1, t2)); +} + + +#ifdef PRODUCT + +void print_LIR(BlockList* blocks) { +} + +#else +// LIR_OprDesc +void LIR_OprDesc::print() const { + print(tty); +} + +void LIR_OprDesc::print(outputStream* out) const { + if (is_illegal()) { + return; + } + + out->print("["); + if (is_pointer()) { + pointer()->print_value_on(out); + } else if (is_single_stack()) { + out->print("stack:%d", single_stack_ix()); + } else if (is_double_stack()) { + out->print("dbl_stack:%d",double_stack_ix()); + } else if (is_virtual()) { + out->print("R%d", vreg_number()); + } else if (is_single_cpu()) { + out->print(as_register()->name()); + } else if (is_double_cpu()) { + out->print(as_register_hi()->name()); + out->print(as_register_lo()->name()); +#ifdef IA32 + } else if (is_single_xmm()) { + out->print(as_xmm_float_reg()->name()); + } else if (is_double_xmm()) { + out->print(as_xmm_double_reg()->name()); + } else if (is_single_fpu()) { + out->print("fpu%d", fpu_regnr()); + } else if (is_double_fpu()) { + out->print("fpu%d", fpu_regnrLo()); +#else + } else if (is_single_fpu()) { + out->print(as_float_reg()->name()); + } else if (is_double_fpu()) { + out->print(as_double_reg()->name()); +#endif + + } else if (is_illegal()) { + out->print("-"); + } else { + out->print("Unknown Operand"); + } + if (!is_illegal()) { + out->print("|%c", type_char()); + } + if (is_register() && is_last_use()) { + out->print("(last_use)"); + } + out->print("]"); +} + + +// LIR_Address +void LIR_Const::print_value_on(outputStream* out) const { + switch (type()) { + case T_INT: out->print("int:%d", as_jint()); break; + case T_LONG: out->print("lng:%lld", as_jlong()); break; + case T_FLOAT: out->print("flt:%f", as_jfloat()); break; + case T_DOUBLE: out->print("dbl:%f", as_jdouble()); break; + case T_OBJECT: out->print("obj:0x%x", as_jobject()); break; + default: out->print("%3d:0x%x",type(), as_jdouble()); break; + } +} + +// LIR_Address +void LIR_Address::print_value_on(outputStream* out) const { + out->print("Base:"); _base->print(out); + if (!_index->is_illegal()) { + out->print(" Index:"); _index->print(out); + switch (scale()) { + case times_1: break; + case times_2: out->print(" * 2"); break; + case times_4: out->print(" * 4"); break; + case times_8: out->print(" * 8"); break; + } + } + out->print(" Disp: %d", _disp); +} + +// debug output of block header without InstructionPrinter +// (because phi functions are not necessary for LIR) +static void print_block(BlockBegin* x) { + // print block id + BlockEnd* end = x->end(); + tty->print("B%d ", x->block_id()); + + // print flags + if (x->is_set(BlockBegin::std_entry_flag)) tty->print("std "); + if (x->is_set(BlockBegin::osr_entry_flag)) tty->print("osr "); + if (x->is_set(BlockBegin::exception_entry_flag)) tty->print("ex "); + if (x->is_set(BlockBegin::subroutine_entry_flag)) tty->print("jsr "); + if (x->is_set(BlockBegin::backward_branch_target_flag)) tty->print("bb "); + if (x->is_set(BlockBegin::linear_scan_loop_header_flag)) tty->print("lh "); + if (x->is_set(BlockBegin::linear_scan_loop_end_flag)) tty->print("le "); + + // print block bci range + tty->print("[%d, %d] ", x->bci(), (end == NULL ? -1 : end->bci())); + + // print predecessors and successors + if (x->number_of_preds() > 0) { + tty->print("preds: "); + for (int i = 0; i < x->number_of_preds(); i ++) { + tty->print("B%d ", x->pred_at(i)->block_id()); + } + } + + if (x->number_of_sux() > 0) { + tty->print("sux: "); + for (int i = 0; i < x->number_of_sux(); i ++) { + tty->print("B%d ", x->sux_at(i)->block_id()); + } + } + + // print exception handlers + if (x->number_of_exception_handlers() > 0) { + tty->print("xhandler: "); + for (int i = 0; i < x->number_of_exception_handlers(); i++) { + tty->print("B%d ", x->exception_handler_at(i)->block_id()); + } + } + + tty->cr(); +} + +void print_LIR(BlockList* blocks) { + tty->print_cr("LIR:"); + int i; + for (i = 0; i < blocks->length(); i++) { + BlockBegin* bb = blocks->at(i); + print_block(bb); + tty->print("__id_Instruction___________________________________________"); tty->cr(); + bb->lir()->print_instructions(); + } +} + +void LIR_List::print_instructions() { + for (int i = 0; i < _operations.length(); i++) { + _operations.at(i)->print(); tty->cr(); + } + tty->cr(); +} + +// LIR_Ops printing routines +// LIR_Op +void LIR_Op::print_on(outputStream* out) const { + if (id() != -1 || PrintCFGToFile) { + out->print("%4d ", id()); + } else { + out->print(" "); + } + out->print(name()); out->print(" "); + print_instr(out); + if (info() != NULL) out->print(" [bci:%d]", info()->bci()); +#ifdef ASSERT + if (Verbose && _file != NULL) { + out->print(" (%s:%d)", _file, _line); + } +#endif +} + +const char * LIR_Op::name() const { + const char* s = NULL; + switch(code()) { + // LIR_Op0 + case lir_membar: s = "membar"; break; + case lir_membar_acquire: s = "membar_acquire"; break; + case lir_membar_release: s = "membar_release"; break; + case lir_word_align: s = "word_align"; break; + case lir_label: s = "label"; break; + case lir_nop: s = "nop"; break; + case lir_backwardbranch_target: s = "backbranch"; break; + case lir_std_entry: s = "std_entry"; break; + case lir_osr_entry: s = "osr_entry"; break; + case lir_build_frame: s = "build_frm"; break; + case lir_fpop_raw: s = "fpop_raw"; break; + case lir_24bit_FPU: s = "24bit_FPU"; break; + case lir_reset_FPU: s = "reset_FPU"; break; + case lir_breakpoint: s = "breakpoint"; break; + case lir_get_thread: s = "get_thread"; break; + // LIR_Op1 + case lir_fxch: s = "fxch"; break; + case lir_fld: s = "fld"; break; + case lir_ffree: s = "ffree"; break; + case lir_push: s = "push"; break; + case lir_pop: s = "pop"; break; + case lir_null_check: s = "null_check"; break; + case lir_return: s = "return"; break; + case lir_safepoint: s = "safepoint"; break; + case lir_neg: s = "neg"; break; + case lir_leal: s = "leal"; break; + case lir_branch: s = "branch"; break; + case lir_cond_float_branch: s = "flt_cond_br"; break; + case lir_move: s = "move"; break; + case lir_roundfp: s = "roundfp"; break; + case lir_rtcall: s = "rtcall"; break; + case lir_throw: s = "throw"; break; + case lir_unwind: s = "unwind"; break; + case lir_convert: s = "convert"; break; + case lir_alloc_object: s = "alloc_obj"; break; + case lir_monaddr: s = "mon_addr"; break; + // LIR_Op2 + case lir_cmp: s = "cmp"; break; + case lir_cmp_l2i: s = "cmp_l2i"; break; + case lir_ucmp_fd2i: s = "ucomp_fd2i"; break; + case lir_cmp_fd2i: s = "comp_fd2i"; break; + case lir_cmove: s = "cmove"; break; + case lir_add: s = "add"; break; + case lir_sub: s = "sub"; break; + case lir_mul: s = "mul"; break; + case lir_mul_strictfp: s = "mul_strictfp"; break; + case lir_div: s = "div"; break; + case lir_div_strictfp: s = "div_strictfp"; break; + case lir_rem: s = "rem"; break; + case lir_abs: s = "abs"; break; + case lir_sqrt: s = "sqrt"; break; + case lir_sin: s = "sin"; break; + case lir_cos: s = "cos"; break; + case lir_tan: s = "tan"; break; + case lir_log: s = "log"; break; + case lir_log10: s = "log10"; break; + case lir_logic_and: s = "logic_and"; break; + case lir_logic_or: s = "logic_or"; break; + case lir_logic_xor: s = "logic_xor"; break; + case lir_shl: s = "shift_left"; break; + case lir_shr: s = "shift_right"; break; + case lir_ushr: s = "ushift_right"; break; + case lir_alloc_array: s = "alloc_array"; break; + // LIR_Op3 + case lir_idiv: s = "idiv"; break; + case lir_irem: s = "irem"; break; + // LIR_OpJavaCall + case lir_static_call: s = "static"; break; + case lir_optvirtual_call: s = "optvirtual"; break; + case lir_icvirtual_call: s = "icvirtual"; break; + case lir_virtual_call: s = "virtual"; break; + // LIR_OpArrayCopy + case lir_arraycopy: s = "arraycopy"; break; + // LIR_OpLock + case lir_lock: s = "lock"; break; + case lir_unlock: s = "unlock"; break; + // LIR_OpDelay + case lir_delay_slot: s = "delay"; break; + // LIR_OpTypeCheck + case lir_instanceof: s = "instanceof"; break; + case lir_checkcast: s = "checkcast"; break; + case lir_store_check: s = "store_check"; break; + // LIR_OpCompareAndSwap + case lir_cas_long: s = "cas_long"; break; + case lir_cas_obj: s = "cas_obj"; break; + case lir_cas_int: s = "cas_int"; break; + // LIR_OpProfileCall + case lir_profile_call: s = "profile_call"; break; + + case lir_none: ShouldNotReachHere();break; + default: s = "illegal_op"; break; + } + return s; +} + +// LIR_OpJavaCall +void LIR_OpJavaCall::print_instr(outputStream* out) const { + out->print("call: "); + out->print("[addr: 0x%x]", address()); + if (receiver()->is_valid()) { + out->print(" [recv: "); receiver()->print(out); out->print("]"); + } + if (result_opr()->is_valid()) { + out->print(" [result: "); result_opr()->print(out); out->print("]"); + } +} + +// LIR_OpLabel +void LIR_OpLabel::print_instr(outputStream* out) const { + out->print("[label:0x%x]", _label); +} + +// LIR_OpArrayCopy +void LIR_OpArrayCopy::print_instr(outputStream* out) const { + src()->print(out); out->print(" "); + src_pos()->print(out); out->print(" "); + dst()->print(out); out->print(" "); + dst_pos()->print(out); out->print(" "); + length()->print(out); out->print(" "); + tmp()->print(out); out->print(" "); +} + +// LIR_OpCompareAndSwap +void LIR_OpCompareAndSwap::print_instr(outputStream* out) const { + addr()->print(out); out->print(" "); + cmp_value()->print(out); out->print(" "); + new_value()->print(out); out->print(" "); + tmp1()->print(out); out->print(" "); + tmp2()->print(out); out->print(" "); + +} + +// LIR_Op0 +void LIR_Op0::print_instr(outputStream* out) const { + result_opr()->print(out); +} + +// LIR_Op1 +const char * LIR_Op1::name() const { + if (code() == lir_move) { + switch (move_kind()) { + case lir_move_normal: + return "move"; + case lir_move_unaligned: + return "unaligned move"; + case lir_move_volatile: + return "volatile_move"; + default: + ShouldNotReachHere(); + return "illegal_op"; + } + } else { + return LIR_Op::name(); + } +} + + +void LIR_Op1::print_instr(outputStream* out) const { + _opr->print(out); out->print(" "); + result_opr()->print(out); out->print(" "); + print_patch_code(out, patch_code()); +} + + +// LIR_Op1 +void LIR_OpRTCall::print_instr(outputStream* out) const { + intx a = (intx)addr(); + out->print(Runtime1::name_for_address(addr())); + out->print(" "); + tmp()->print(out); +} + +void LIR_Op1::print_patch_code(outputStream* out, LIR_PatchCode code) { + switch(code) { + case lir_patch_none: break; + case lir_patch_low: out->print("[patch_low]"); break; + case lir_patch_high: out->print("[patch_high]"); break; + case lir_patch_normal: out->print("[patch_normal]"); break; + default: ShouldNotReachHere(); + } +} + +// LIR_OpBranch +void LIR_OpBranch::print_instr(outputStream* out) const { + print_condition(out, cond()); out->print(" "); + if (block() != NULL) { + out->print("[B%d] ", block()->block_id()); + } else if (stub() != NULL) { + out->print("["); + stub()->print_name(out); + out->print(": 0x%x]", stub()); + if (stub()->info() != NULL) out->print(" [bci:%d]", stub()->info()->bci()); + } else { + out->print("[label:0x%x] ", label()); + } + if (ublock() != NULL) { + out->print("unordered: [B%d] ", ublock()->block_id()); + } +} + +void LIR_Op::print_condition(outputStream* out, LIR_Condition cond) { + switch(cond) { + case lir_cond_equal: out->print("[EQ]"); break; + case lir_cond_notEqual: out->print("[NE]"); break; + case lir_cond_less: out->print("[LT]"); break; + case lir_cond_lessEqual: out->print("[LE]"); break; + case lir_cond_greaterEqual: out->print("[GE]"); break; + case lir_cond_greater: out->print("[GT]"); break; + case lir_cond_belowEqual: out->print("[BE]"); break; + case lir_cond_aboveEqual: out->print("[AE]"); break; + case lir_cond_always: out->print("[AL]"); break; + default: out->print("[%d]",cond); break; + } +} + +// LIR_OpConvert +void LIR_OpConvert::print_instr(outputStream* out) const { + print_bytecode(out, bytecode()); + in_opr()->print(out); out->print(" "); + result_opr()->print(out); out->print(" "); +} + +void LIR_OpConvert::print_bytecode(outputStream* out, Bytecodes::Code code) { + switch(code) { + case Bytecodes::_d2f: out->print("[d2f] "); break; + case Bytecodes::_d2i: out->print("[d2i] "); break; + case Bytecodes::_d2l: out->print("[d2l] "); break; + case Bytecodes::_f2d: out->print("[f2d] "); break; + case Bytecodes::_f2i: out->print("[f2i] "); break; + case Bytecodes::_f2l: out->print("[f2l] "); break; + case Bytecodes::_i2b: out->print("[i2b] "); break; + case Bytecodes::_i2c: out->print("[i2c] "); break; + case Bytecodes::_i2d: out->print("[i2d] "); break; + case Bytecodes::_i2f: out->print("[i2f] "); break; + case Bytecodes::_i2l: out->print("[i2l] "); break; + case Bytecodes::_i2s: out->print("[i2s] "); break; + case Bytecodes::_l2i: out->print("[l2i] "); break; + case Bytecodes::_l2f: out->print("[l2f] "); break; + case Bytecodes::_l2d: out->print("[l2d] "); break; + default: + out->print("[?%d]",code); + break; + } +} + +void LIR_OpAllocObj::print_instr(outputStream* out) const { + klass()->print(out); out->print(" "); + obj()->print(out); out->print(" "); + tmp1()->print(out); out->print(" "); + tmp2()->print(out); out->print(" "); + tmp3()->print(out); out->print(" "); + tmp4()->print(out); out->print(" "); + out->print("[hdr:%d]", header_size()); out->print(" "); + out->print("[obj:%d]", object_size()); out->print(" "); + out->print("[lbl:0x%x]", stub()->entry()); +} + +void LIR_OpRoundFP::print_instr(outputStream* out) const { + _opr->print(out); out->print(" "); + tmp()->print(out); out->print(" "); + result_opr()->print(out); out->print(" "); +} + +// LIR_Op2 +void LIR_Op2::print_instr(outputStream* out) const { + if (code() == lir_cmove) { + print_condition(out, condition()); out->print(" "); + } + in_opr1()->print(out); out->print(" "); + in_opr2()->print(out); out->print(" "); + if (tmp_opr()->is_valid()) { tmp_opr()->print(out); out->print(" "); } + result_opr()->print(out); +} + +void LIR_OpAllocArray::print_instr(outputStream* out) const { + klass()->print(out); out->print(" "); + len()->print(out); out->print(" "); + obj()->print(out); out->print(" "); + tmp1()->print(out); out->print(" "); + tmp2()->print(out); out->print(" "); + tmp3()->print(out); out->print(" "); + tmp4()->print(out); out->print(" "); + out->print("[type:0x%x]", type()); out->print(" "); + out->print("[label:0x%x]", stub()->entry()); +} + + +void LIR_OpTypeCheck::print_instr(outputStream* out) const { + object()->print(out); out->print(" "); + if (code() == lir_store_check) { + array()->print(out); out->print(" "); + } + if (code() != lir_store_check) { + klass()->print_name_on(out); out->print(" "); + if (fast_check()) out->print("fast_check "); + } + tmp1()->print(out); out->print(" "); + tmp2()->print(out); out->print(" "); + tmp3()->print(out); out->print(" "); + result_opr()->print(out); out->print(" "); + if (info_for_exception() != NULL) out->print(" [bci:%d]", info_for_exception()->bci()); +} + + +// LIR_Op3 +void LIR_Op3::print_instr(outputStream* out) const { + in_opr1()->print(out); out->print(" "); + in_opr2()->print(out); out->print(" "); + in_opr3()->print(out); out->print(" "); + result_opr()->print(out); +} + + +void LIR_OpLock::print_instr(outputStream* out) const { + hdr_opr()->print(out); out->print(" "); + obj_opr()->print(out); out->print(" "); + lock_opr()->print(out); out->print(" "); + if (_scratch->is_valid()) { + _scratch->print(out); out->print(" "); + } + out->print("[lbl:0x%x]", stub()->entry()); +} + + +void LIR_OpDelay::print_instr(outputStream* out) const { + _op->print_on(out); +} + + +// LIR_OpProfileCall +void LIR_OpProfileCall::print_instr(outputStream* out) const { + profiled_method()->name()->print_symbol_on(out); + out->print("."); + profiled_method()->holder()->name()->print_symbol_on(out); + out->print(" @ %d ", profiled_bci()); + mdo()->print(out); out->print(" "); + recv()->print(out); out->print(" "); + tmp1()->print(out); out->print(" "); +} + + +#endif // PRODUCT + +// Implementation of LIR_InsertionBuffer + +void LIR_InsertionBuffer::append(int index, LIR_Op* op) { + assert(_index_and_count.length() % 2 == 0, "must have a count for each index"); + + int i = number_of_insertion_points() - 1; + if (i < 0 || index_at(i) < index) { + append_new(index, 1); + } else { + assert(index_at(i) == index, "can append LIR_Ops in ascending order only"); + assert(count_at(i) > 0, "check"); + set_count_at(i, count_at(i) + 1); + } + _ops.push(op); + + DEBUG_ONLY(verify()); +} + +#ifdef ASSERT +void LIR_InsertionBuffer::verify() { + int sum = 0; + int prev_idx = -1; + + for (int i = 0; i < number_of_insertion_points(); i++) { + assert(prev_idx < index_at(i), "index must be ordered ascending"); + sum += count_at(i); + } + assert(sum == number_of_ops(), "wrong total sum"); +} +#endif diff --git a/hotspot/src/share/vm/c1/c1_LIR.hpp b/hotspot/src/share/vm/c1/c1_LIR.hpp new file mode 100644 index 00000000000..6612604ca66 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_LIR.hpp @@ -0,0 +1,2034 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class BlockBegin; +class BlockList; +class LIR_Assembler; +class CodeEmitInfo; +class CodeStub; +class CodeStubList; +class ArrayCopyStub; +class LIR_Op; +class ciType; +class ValueType; +class LIR_OpVisitState; +class FpuStackSim; + +//--------------------------------------------------------------------- +// LIR Operands +// LIR_OprDesc +// LIR_OprPtr +// LIR_Const +// LIR_Address +//--------------------------------------------------------------------- +class LIR_OprDesc; +class LIR_OprPtr; +class LIR_Const; +class LIR_Address; +class LIR_OprVisitor; + + +typedef LIR_OprDesc* LIR_Opr; +typedef int RegNr; + +define_array(LIR_OprArray, LIR_Opr) +define_stack(LIR_OprList, LIR_OprArray) + +define_array(LIR_OprRefArray, LIR_Opr*) +define_stack(LIR_OprRefList, LIR_OprRefArray) + +define_array(CodeEmitInfoArray, CodeEmitInfo*) +define_stack(CodeEmitInfoList, CodeEmitInfoArray) + +define_array(LIR_OpArray, LIR_Op*) +define_stack(LIR_OpList, LIR_OpArray) + +// define LIR_OprPtr early so LIR_OprDesc can refer to it +class LIR_OprPtr: public CompilationResourceObj { + public: + bool is_oop_pointer() const { return (type() == T_OBJECT); } + bool is_float_kind() const { BasicType t = type(); return (t == T_FLOAT) || (t == T_DOUBLE); } + + virtual LIR_Const* as_constant() { return NULL; } + virtual LIR_Address* as_address() { return NULL; } + virtual BasicType type() const = 0; + virtual void print_value_on(outputStream* out) const = 0; +}; + + + +// LIR constants +class LIR_Const: public LIR_OprPtr { + private: + JavaValue _value; + + void type_check(BasicType t) const { assert(type() == t, "type check"); } + void type_check(BasicType t1, BasicType t2) const { assert(type() == t1 || type() == t2, "type check"); } + + public: + LIR_Const(jint i) { _value.set_type(T_INT); _value.set_jint(i); } + LIR_Const(jlong l) { _value.set_type(T_LONG); _value.set_jlong(l); } + LIR_Const(jfloat f) { _value.set_type(T_FLOAT); _value.set_jfloat(f); } + LIR_Const(jdouble d) { _value.set_type(T_DOUBLE); _value.set_jdouble(d); } + LIR_Const(jobject o) { _value.set_type(T_OBJECT); _value.set_jobject(o); } + LIR_Const(void* p) { +#ifdef _LP64 + assert(sizeof(jlong) >= sizeof(p), "too small");; + _value.set_type(T_LONG); _value.set_jlong((jlong)p); +#else + assert(sizeof(jint) >= sizeof(p), "too small");; + _value.set_type(T_INT); _value.set_jint((jint)p); +#endif + } + + virtual BasicType type() const { return _value.get_type(); } + virtual LIR_Const* as_constant() { return this; } + + jint as_jint() const { type_check(T_INT ); return _value.get_jint(); } + jlong as_jlong() const { type_check(T_LONG ); return _value.get_jlong(); } + jfloat as_jfloat() const { type_check(T_FLOAT ); return _value.get_jfloat(); } + jdouble as_jdouble() const { type_check(T_DOUBLE); return _value.get_jdouble(); } + jobject as_jobject() const { type_check(T_OBJECT); return _value.get_jobject(); } + jint as_jint_lo() const { type_check(T_LONG ); return low(_value.get_jlong()); } + jint as_jint_hi() const { type_check(T_LONG ); return high(_value.get_jlong()); } + +#ifdef _LP64 + address as_pointer() const { type_check(T_LONG ); return (address)_value.get_jlong(); } +#else + address as_pointer() const { type_check(T_INT ); return (address)_value.get_jint(); } +#endif + + + jint as_jint_bits() const { type_check(T_FLOAT, T_INT); return _value.get_jint(); } + jint as_jint_lo_bits() const { + if (type() == T_DOUBLE) { + return low(jlong_cast(_value.get_jdouble())); + } else { + return as_jint_lo(); + } + } + jint as_jint_hi_bits() const { + if (type() == T_DOUBLE) { + return high(jlong_cast(_value.get_jdouble())); + } else { + return as_jint_hi(); + } + } + + virtual void print_value_on(outputStream* out) const PRODUCT_RETURN; + + + bool is_zero_float() { + jfloat f = as_jfloat(); + jfloat ok = 0.0f; + return jint_cast(f) == jint_cast(ok); + } + + bool is_one_float() { + jfloat f = as_jfloat(); + return !g_isnan(f) && g_isfinite(f) && f == 1.0; + } + + bool is_zero_double() { + jdouble d = as_jdouble(); + jdouble ok = 0.0; + return jlong_cast(d) == jlong_cast(ok); + } + + bool is_one_double() { + jdouble d = as_jdouble(); + return !g_isnan(d) && g_isfinite(d) && d == 1.0; + } +}; + + +//---------------------LIR Operand descriptor------------------------------------ +// +// The class LIR_OprDesc represents a LIR instruction operand; +// it can be a register (ALU/FPU), stack location or a constant; +// Constants and addresses are represented as resource area allocated +// structures (see above). +// Registers and stack locations are inlined into the this pointer +// (see value function). + +class LIR_OprDesc: public CompilationResourceObj { + public: + // value structure: + // data opr-type opr-kind + // +--------------+-------+-------+ + // [max...........|7 6 5 4|3 2 1 0] + // ^ + // is_pointer bit + // + // lowest bit cleared, means it is a structure pointer + // we need 4 bits to represent types + + private: + friend class LIR_OprFact; + + // Conversion + intptr_t value() const { return (intptr_t) this; } + + bool check_value_mask(intptr_t mask, intptr_t masked_value) const { + return (value() & mask) == masked_value; + } + + enum OprKind { + pointer_value = 0 + , stack_value = 1 + , cpu_register = 3 + , fpu_register = 5 + , illegal_value = 7 + }; + + enum OprBits { + pointer_bits = 1 + , kind_bits = 3 + , type_bits = 4 + , size_bits = 2 + , destroys_bits = 1 + , virtual_bits = 1 + , is_xmm_bits = 1 + , last_use_bits = 1 + , is_fpu_stack_offset_bits = 1 // used in assertion checking on x86 for FPU stack slot allocation + , non_data_bits = kind_bits + type_bits + size_bits + destroys_bits + last_use_bits + + is_fpu_stack_offset_bits + virtual_bits + is_xmm_bits + , data_bits = BitsPerInt - non_data_bits + , reg_bits = data_bits / 2 // for two registers in one value encoding + }; + + enum OprShift { + kind_shift = 0 + , type_shift = kind_shift + kind_bits + , size_shift = type_shift + type_bits + , destroys_shift = size_shift + size_bits + , last_use_shift = destroys_shift + destroys_bits + , is_fpu_stack_offset_shift = last_use_shift + last_use_bits + , virtual_shift = is_fpu_stack_offset_shift + is_fpu_stack_offset_bits + , is_xmm_shift = virtual_shift + virtual_bits + , data_shift = is_xmm_shift + is_xmm_bits + , reg1_shift = data_shift + , reg2_shift = data_shift + reg_bits + + }; + + enum OprSize { + single_size = 0 << size_shift + , double_size = 1 << size_shift + }; + + enum OprMask { + kind_mask = right_n_bits(kind_bits) + , type_mask = right_n_bits(type_bits) << type_shift + , size_mask = right_n_bits(size_bits) << size_shift + , last_use_mask = right_n_bits(last_use_bits) << last_use_shift + , is_fpu_stack_offset_mask = right_n_bits(is_fpu_stack_offset_bits) << is_fpu_stack_offset_shift + , virtual_mask = right_n_bits(virtual_bits) << virtual_shift + , is_xmm_mask = right_n_bits(is_xmm_bits) << is_xmm_shift + , pointer_mask = right_n_bits(pointer_bits) + , lower_reg_mask = right_n_bits(reg_bits) + , no_type_mask = (int)(~(type_mask | last_use_mask | is_fpu_stack_offset_mask)) + }; + + uintptr_t data() const { return value() >> data_shift; } + int lo_reg_half() const { return data() & lower_reg_mask; } + int hi_reg_half() const { return (data() >> reg_bits) & lower_reg_mask; } + OprKind kind_field() const { return (OprKind)(value() & kind_mask); } + OprSize size_field() const { return (OprSize)(value() & size_mask); } + + static char type_char(BasicType t); + + public: + enum { + vreg_base = ConcreteRegisterImpl::number_of_registers, + vreg_max = (1 << data_bits) - 1 + }; + + static inline LIR_Opr illegalOpr(); + + enum OprType { + unknown_type = 0 << type_shift // means: not set (catch uninitialized types) + , int_type = 1 << type_shift + , long_type = 2 << type_shift + , object_type = 3 << type_shift + , pointer_type = 4 << type_shift + , float_type = 5 << type_shift + , double_type = 6 << type_shift + }; + friend OprType as_OprType(BasicType t); + friend BasicType as_BasicType(OprType t); + + OprType type_field_valid() const { assert(is_register() || is_stack(), "should not be called otherwise"); return (OprType)(value() & type_mask); } + OprType type_field() const { return is_illegal() ? unknown_type : (OprType)(value() & type_mask); } + + static OprSize size_for(BasicType t) { + switch (t) { + case T_LONG: + case T_DOUBLE: + return double_size; + break; + + case T_FLOAT: + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + case T_OBJECT: + case T_ARRAY: + return single_size; + break; + + default: + ShouldNotReachHere(); + } + } + + + void validate_type() const PRODUCT_RETURN; + + BasicType type() const { + if (is_pointer()) { + return pointer()->type(); + } + return as_BasicType(type_field()); + } + + + ValueType* value_type() const { return as_ValueType(type()); } + + char type_char() const { return type_char((is_pointer()) ? pointer()->type() : type()); } + + bool is_equal(LIR_Opr opr) const { return this == opr; } + // checks whether types are same + bool is_same_type(LIR_Opr opr) const { + assert(type_field() != unknown_type && + opr->type_field() != unknown_type, "shouldn't see unknown_type"); + return type_field() == opr->type_field(); + } + bool is_same_register(LIR_Opr opr) { + return (is_register() && opr->is_register() && + kind_field() == opr->kind_field() && + (value() & no_type_mask) == (opr->value() & no_type_mask)); + } + + bool is_pointer() const { return check_value_mask(pointer_mask, pointer_value); } + bool is_illegal() const { return kind_field() == illegal_value; } + bool is_valid() const { return kind_field() != illegal_value; } + + bool is_register() const { return is_cpu_register() || is_fpu_register(); } + bool is_virtual() const { return is_virtual_cpu() || is_virtual_fpu(); } + + bool is_constant() const { return is_pointer() && pointer()->as_constant() != NULL; } + bool is_address() const { return is_pointer() && pointer()->as_address() != NULL; } + + bool is_float_kind() const { return is_pointer() ? pointer()->is_float_kind() : (kind_field() == fpu_register); } + bool is_oop() const; + + // semantic for fpu- and xmm-registers: + // * is_float and is_double return true for xmm_registers + // (so is_single_fpu and is_single_xmm are true) + // * So you must always check for is_???_xmm prior to is_???_fpu to + // distinguish between fpu- and xmm-registers + + bool is_stack() const { validate_type(); return check_value_mask(kind_mask, stack_value); } + bool is_single_stack() const { validate_type(); return check_value_mask(kind_mask | size_mask, stack_value | single_size); } + bool is_double_stack() const { validate_type(); return check_value_mask(kind_mask | size_mask, stack_value | double_size); } + + bool is_cpu_register() const { validate_type(); return check_value_mask(kind_mask, cpu_register); } + bool is_virtual_cpu() const { validate_type(); return check_value_mask(kind_mask | virtual_mask, cpu_register | virtual_mask); } + bool is_fixed_cpu() const { validate_type(); return check_value_mask(kind_mask | virtual_mask, cpu_register); } + bool is_single_cpu() const { validate_type(); return check_value_mask(kind_mask | size_mask, cpu_register | single_size); } + bool is_double_cpu() const { validate_type(); return check_value_mask(kind_mask | size_mask, cpu_register | double_size); } + + bool is_fpu_register() const { validate_type(); return check_value_mask(kind_mask, fpu_register); } + bool is_virtual_fpu() const { validate_type(); return check_value_mask(kind_mask | virtual_mask, fpu_register | virtual_mask); } + bool is_fixed_fpu() const { validate_type(); return check_value_mask(kind_mask | virtual_mask, fpu_register); } + bool is_single_fpu() const { validate_type(); return check_value_mask(kind_mask | size_mask, fpu_register | single_size); } + bool is_double_fpu() const { validate_type(); return check_value_mask(kind_mask | size_mask, fpu_register | double_size); } + + bool is_xmm_register() const { validate_type(); return check_value_mask(kind_mask | is_xmm_mask, fpu_register | is_xmm_mask); } + bool is_single_xmm() const { validate_type(); return check_value_mask(kind_mask | size_mask | is_xmm_mask, fpu_register | single_size | is_xmm_mask); } + bool is_double_xmm() const { validate_type(); return check_value_mask(kind_mask | size_mask | is_xmm_mask, fpu_register | double_size | is_xmm_mask); } + + // fast accessor functions for special bits that do not work for pointers + // (in this functions, the check for is_pointer() is omitted) + bool is_single_word() const { assert(is_register() || is_stack(), "type check"); return check_value_mask(size_mask, single_size); } + bool is_double_word() const { assert(is_register() || is_stack(), "type check"); return check_value_mask(size_mask, double_size); } + bool is_virtual_register() const { assert(is_register(), "type check"); return check_value_mask(virtual_mask, virtual_mask); } + bool is_oop_register() const { assert(is_register() || is_stack(), "type check"); return type_field_valid() == object_type; } + BasicType type_register() const { assert(is_register() || is_stack(), "type check"); return as_BasicType(type_field_valid()); } + + bool is_last_use() const { assert(is_register(), "only works for registers"); return (value() & last_use_mask) != 0; } + bool is_fpu_stack_offset() const { assert(is_register(), "only works for registers"); return (value() & is_fpu_stack_offset_mask) != 0; } + LIR_Opr make_last_use() { assert(is_register(), "only works for registers"); return (LIR_Opr)(value() | last_use_mask); } + LIR_Opr make_fpu_stack_offset() { assert(is_register(), "only works for registers"); return (LIR_Opr)(value() | is_fpu_stack_offset_mask); } + + + int single_stack_ix() const { assert(is_single_stack() && !is_virtual(), "type check"); return (int)data(); } + int double_stack_ix() const { assert(is_double_stack() && !is_virtual(), "type check"); return (int)data(); } + RegNr cpu_regnr() const { assert(is_single_cpu() && !is_virtual(), "type check"); return (RegNr)data(); } + RegNr cpu_regnrLo() const { assert(is_double_cpu() && !is_virtual(), "type check"); return (RegNr)lo_reg_half(); } + RegNr cpu_regnrHi() const { assert(is_double_cpu() && !is_virtual(), "type check"); return (RegNr)hi_reg_half(); } + RegNr fpu_regnr() const { assert(is_single_fpu() && !is_virtual(), "type check"); return (RegNr)data(); } + RegNr fpu_regnrLo() const { assert(is_double_fpu() && !is_virtual(), "type check"); return (RegNr)lo_reg_half(); } + RegNr fpu_regnrHi() const { assert(is_double_fpu() && !is_virtual(), "type check"); return (RegNr)hi_reg_half(); } + RegNr xmm_regnr() const { assert(is_single_xmm() && !is_virtual(), "type check"); return (RegNr)data(); } + RegNr xmm_regnrLo() const { assert(is_double_xmm() && !is_virtual(), "type check"); return (RegNr)lo_reg_half(); } + RegNr xmm_regnrHi() const { assert(is_double_xmm() && !is_virtual(), "type check"); return (RegNr)hi_reg_half(); } + int vreg_number() const { assert(is_virtual(), "type check"); return (RegNr)data(); } + + LIR_OprPtr* pointer() const { assert(is_pointer(), "type check"); return (LIR_OprPtr*)this; } + LIR_Const* as_constant_ptr() const { return pointer()->as_constant(); } + LIR_Address* as_address_ptr() const { return pointer()->as_address(); } + + Register as_register() const; + Register as_register_lo() const; + Register as_register_hi() const; + + Register as_pointer_register() { +#ifdef _LP64 + if (is_double_cpu()) { + assert(as_register_lo() == as_register_hi(), "should be a single register"); + return as_register_lo(); + } +#endif + return as_register(); + } + +#ifdef IA32 + XMMRegister as_xmm_float_reg() const; + XMMRegister as_xmm_double_reg() const; + // for compatibility with RInfo + int fpu () const { return lo_reg_half(); } +#endif + +#ifdef SPARC + FloatRegister as_float_reg () const; + FloatRegister as_double_reg () const; +#endif + + jint as_jint() const { return as_constant_ptr()->as_jint(); } + jlong as_jlong() const { return as_constant_ptr()->as_jlong(); } + jfloat as_jfloat() const { return as_constant_ptr()->as_jfloat(); } + jdouble as_jdouble() const { return as_constant_ptr()->as_jdouble(); } + jobject as_jobject() const { return as_constant_ptr()->as_jobject(); } + + void print() const PRODUCT_RETURN; + void print(outputStream* out) const PRODUCT_RETURN; +}; + + +inline LIR_OprDesc::OprType as_OprType(BasicType type) { + switch (type) { + case T_INT: return LIR_OprDesc::int_type; + case T_LONG: return LIR_OprDesc::long_type; + case T_FLOAT: return LIR_OprDesc::float_type; + case T_DOUBLE: return LIR_OprDesc::double_type; + case T_OBJECT: + case T_ARRAY: return LIR_OprDesc::object_type; + case T_ILLEGAL: // fall through + default: ShouldNotReachHere(); return LIR_OprDesc::unknown_type; + } +} + +inline BasicType as_BasicType(LIR_OprDesc::OprType t) { + switch (t) { + case LIR_OprDesc::int_type: return T_INT; + case LIR_OprDesc::long_type: return T_LONG; + case LIR_OprDesc::float_type: return T_FLOAT; + case LIR_OprDesc::double_type: return T_DOUBLE; + case LIR_OprDesc::object_type: return T_OBJECT; + case LIR_OprDesc::unknown_type: // fall through + default: ShouldNotReachHere(); return T_ILLEGAL; + } +} + + +// LIR_Address +class LIR_Address: public LIR_OprPtr { + friend class LIR_OpVisitState; + + public: + // NOTE: currently these must be the log2 of the scale factor (and + // must also be equivalent to the ScaleFactor enum in + // assembler_i486.hpp) + enum Scale { + times_1 = 0, + times_2 = 1, + times_4 = 2, + times_8 = 3 + }; + + private: + LIR_Opr _base; + LIR_Opr _index; + Scale _scale; + intx _disp; + BasicType _type; + + public: + LIR_Address(LIR_Opr base, LIR_Opr index, BasicType type): + _base(base) + , _index(index) + , _scale(times_1) + , _type(type) + , _disp(0) { verify(); } + + LIR_Address(LIR_Opr base, int disp, BasicType type): + _base(base) + , _index(LIR_OprDesc::illegalOpr()) + , _scale(times_1) + , _type(type) + , _disp(disp) { verify(); } + +#ifdef IA32 + LIR_Address(LIR_Opr base, LIR_Opr index, Scale scale, int disp, BasicType type): + _base(base) + , _index(index) + , _scale(scale) + , _type(type) + , _disp(disp) { verify(); } +#endif + + LIR_Opr base() const { return _base; } + LIR_Opr index() const { return _index; } + Scale scale() const { return _scale; } + intx disp() const { return _disp; } + + bool equals(LIR_Address* other) const { return base() == other->base() && index() == other->index() && disp() == other->disp() && scale() == other->scale(); } + + virtual LIR_Address* as_address() { return this; } + virtual BasicType type() const { return _type; } + virtual void print_value_on(outputStream* out) const PRODUCT_RETURN; + + void verify() const PRODUCT_RETURN; + + static Scale scale(BasicType type); +}; + + +// operand factory +class LIR_OprFact: public AllStatic { + public: + + static LIR_Opr illegalOpr; + + static LIR_Opr single_cpu(int reg) { return (LIR_Opr)((reg << LIR_OprDesc::reg1_shift) | LIR_OprDesc::int_type | LIR_OprDesc::cpu_register | LIR_OprDesc::single_size); } + static LIR_Opr single_cpu_oop(int reg) { return (LIR_Opr)((reg << LIR_OprDesc::reg1_shift) | LIR_OprDesc::object_type | LIR_OprDesc::cpu_register | LIR_OprDesc::single_size); } + static LIR_Opr double_cpu(int reg1, int reg2) { return (LIR_Opr)((reg1 << LIR_OprDesc::reg1_shift) | (reg2 << LIR_OprDesc::reg2_shift) | LIR_OprDesc::long_type | LIR_OprDesc::cpu_register | LIR_OprDesc::double_size); } + + static LIR_Opr single_fpu(int reg) { return (LIR_Opr)((reg << LIR_OprDesc::reg1_shift) | LIR_OprDesc::float_type | LIR_OprDesc::fpu_register | LIR_OprDesc::single_size); } + +#ifdef SPARC + static LIR_Opr double_fpu(int reg1, int reg2) { return (LIR_Opr)((reg1 << LIR_OprDesc::reg1_shift) | (reg2 << LIR_OprDesc::reg2_shift) | LIR_OprDesc::double_type | LIR_OprDesc::fpu_register | LIR_OprDesc::double_size); } +#endif +#ifdef IA32 + static LIR_Opr double_fpu(int reg) { return (LIR_Opr)((reg << LIR_OprDesc::reg1_shift) | (reg << LIR_OprDesc::reg2_shift) | LIR_OprDesc::double_type | LIR_OprDesc::fpu_register | LIR_OprDesc::double_size); } + static LIR_Opr single_xmm(int reg) { return (LIR_Opr)((reg << LIR_OprDesc::reg1_shift) | LIR_OprDesc::float_type | LIR_OprDesc::fpu_register | LIR_OprDesc::single_size | LIR_OprDesc::is_xmm_mask); } + static LIR_Opr double_xmm(int reg) { return (LIR_Opr)((reg << LIR_OprDesc::reg1_shift) | (reg << LIR_OprDesc::reg2_shift) | LIR_OprDesc::double_type | LIR_OprDesc::fpu_register | LIR_OprDesc::double_size | LIR_OprDesc::is_xmm_mask); } +#endif + + + static LIR_Opr virtual_register(int index, BasicType type) { + LIR_Opr res; + switch (type) { + case T_OBJECT: // fall through + case T_ARRAY: res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::object_type | LIR_OprDesc::cpu_register | LIR_OprDesc::single_size | LIR_OprDesc::virtual_mask); break; + case T_INT: res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::int_type | LIR_OprDesc::cpu_register | LIR_OprDesc::single_size | LIR_OprDesc::virtual_mask); break; + case T_LONG: res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::long_type | LIR_OprDesc::cpu_register | LIR_OprDesc::double_size | LIR_OprDesc::virtual_mask); break; + case T_FLOAT: res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::float_type | LIR_OprDesc::fpu_register | LIR_OprDesc::single_size | LIR_OprDesc::virtual_mask); break; + case T_DOUBLE: res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::double_type | LIR_OprDesc::fpu_register | LIR_OprDesc::double_size | LIR_OprDesc::virtual_mask); break; + + default: ShouldNotReachHere(); res = illegalOpr; + } + +#ifdef ASSERT + res->validate_type(); + assert(res->vreg_number() == index, "conversion check"); + assert(index >= LIR_OprDesc::vreg_base, "must start at vreg_base"); + assert(index <= (max_jint >> LIR_OprDesc::data_shift), "index is too big"); + + // old-style calculation; check if old and new method are equal + LIR_OprDesc::OprType t = as_OprType(type); + LIR_Opr old_res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | t | + ((type == T_FLOAT || type == T_DOUBLE) ? LIR_OprDesc::fpu_register : LIR_OprDesc::cpu_register) | + LIR_OprDesc::size_for(type) | LIR_OprDesc::virtual_mask); + assert(res == old_res, "old and new method not equal"); +#endif + + return res; + } + + // 'index' is computed by FrameMap::local_stack_pos(index); do not use other parameters as + // the index is platform independent; a double stack useing indeces 2 and 3 has always + // index 2. + static LIR_Opr stack(int index, BasicType type) { + LIR_Opr res; + switch (type) { + case T_OBJECT: // fall through + case T_ARRAY: res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::object_type | LIR_OprDesc::stack_value | LIR_OprDesc::single_size); break; + case T_INT: res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::int_type | LIR_OprDesc::stack_value | LIR_OprDesc::single_size); break; + case T_LONG: res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::long_type | LIR_OprDesc::stack_value | LIR_OprDesc::double_size); break; + case T_FLOAT: res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::float_type | LIR_OprDesc::stack_value | LIR_OprDesc::single_size); break; + case T_DOUBLE: res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::double_type | LIR_OprDesc::stack_value | LIR_OprDesc::double_size); break; + + default: ShouldNotReachHere(); res = illegalOpr; + } + +#ifdef ASSERT + assert(index >= 0, "index must be positive"); + assert(index <= (max_jint >> LIR_OprDesc::data_shift), "index is too big"); + + LIR_Opr old_res = (LIR_Opr)((index << LIR_OprDesc::data_shift) | LIR_OprDesc::stack_value | as_OprType(type) | LIR_OprDesc::size_for(type)); + assert(res == old_res, "old and new method not equal"); +#endif + + return res; + } + + static LIR_Opr intConst(jint i) { return (LIR_Opr)(new LIR_Const(i)); } + static LIR_Opr longConst(jlong l) { return (LIR_Opr)(new LIR_Const(l)); } + static LIR_Opr floatConst(jfloat f) { return (LIR_Opr)(new LIR_Const(f)); } + static LIR_Opr doubleConst(jdouble d) { return (LIR_Opr)(new LIR_Const(d)); } + static LIR_Opr oopConst(jobject o) { return (LIR_Opr)(new LIR_Const(o)); } + static LIR_Opr address(LIR_Address* a) { return (LIR_Opr)a; } + static LIR_Opr intptrConst(void* p) { return (LIR_Opr)(new LIR_Const(p)); } + static LIR_Opr intptrConst(intptr_t v) { return (LIR_Opr)(new LIR_Const((void*)v)); } + static LIR_Opr illegal() { return (LIR_Opr)-1; } + + static LIR_Opr value_type(ValueType* type); + static LIR_Opr dummy_value_type(ValueType* type); +}; + + +//------------------------------------------------------------------------------- +// LIR Instructions +//------------------------------------------------------------------------------- +// +// Note: +// - every instruction has a result operand +// - every instruction has an CodeEmitInfo operand (can be revisited later) +// - every instruction has a LIR_OpCode operand +// - LIR_OpN, means an instruction that has N input operands +// +// class hierarchy: +// +class LIR_Op; +class LIR_Op0; +class LIR_OpLabel; +class LIR_Op1; +class LIR_OpBranch; +class LIR_OpConvert; +class LIR_OpAllocObj; +class LIR_OpRoundFP; +class LIR_Op2; +class LIR_OpDelay; +class LIR_Op3; +class LIR_OpAllocArray; +class LIR_OpCall; +class LIR_OpJavaCall; +class LIR_OpRTCall; +class LIR_OpArrayCopy; +class LIR_OpLock; +class LIR_OpTypeCheck; +class LIR_OpCompareAndSwap; +class LIR_OpProfileCall; + + +// LIR operation codes +enum LIR_Code { + lir_none + , begin_op0 + , lir_word_align + , lir_label + , lir_nop + , lir_backwardbranch_target + , lir_std_entry + , lir_osr_entry + , lir_build_frame + , lir_fpop_raw + , lir_24bit_FPU + , lir_reset_FPU + , lir_breakpoint + , lir_rtcall + , lir_membar + , lir_membar_acquire + , lir_membar_release + , lir_get_thread + , end_op0 + , begin_op1 + , lir_fxch + , lir_fld + , lir_ffree + , lir_push + , lir_pop + , lir_null_check + , lir_return + , lir_leal + , lir_neg + , lir_branch + , lir_cond_float_branch + , lir_move + , lir_prefetchr + , lir_prefetchw + , lir_convert + , lir_alloc_object + , lir_monaddr + , lir_roundfp + , lir_safepoint + , end_op1 + , begin_op2 + , lir_cmp + , lir_cmp_l2i + , lir_ucmp_fd2i + , lir_cmp_fd2i + , lir_cmove + , lir_add + , lir_sub + , lir_mul + , lir_mul_strictfp + , lir_div + , lir_div_strictfp + , lir_rem + , lir_sqrt + , lir_abs + , lir_sin + , lir_cos + , lir_tan + , lir_log + , lir_log10 + , lir_logic_and + , lir_logic_or + , lir_logic_xor + , lir_shl + , lir_shr + , lir_ushr + , lir_alloc_array + , lir_throw + , lir_unwind + , lir_compare_to + , end_op2 + , begin_op3 + , lir_idiv + , lir_irem + , end_op3 + , begin_opJavaCall + , lir_static_call + , lir_optvirtual_call + , lir_icvirtual_call + , lir_virtual_call + , end_opJavaCall + , begin_opArrayCopy + , lir_arraycopy + , end_opArrayCopy + , begin_opLock + , lir_lock + , lir_unlock + , end_opLock + , begin_delay_slot + , lir_delay_slot + , end_delay_slot + , begin_opTypeCheck + , lir_instanceof + , lir_checkcast + , lir_store_check + , end_opTypeCheck + , begin_opCompareAndSwap + , lir_cas_long + , lir_cas_obj + , lir_cas_int + , end_opCompareAndSwap + , begin_opMDOProfile + , lir_profile_call + , end_opMDOProfile +}; + + +enum LIR_Condition { + lir_cond_equal + , lir_cond_notEqual + , lir_cond_less + , lir_cond_lessEqual + , lir_cond_greaterEqual + , lir_cond_greater + , lir_cond_belowEqual + , lir_cond_aboveEqual + , lir_cond_always + , lir_cond_unknown = -1 +}; + + +enum LIR_PatchCode { + lir_patch_none, + lir_patch_low, + lir_patch_high, + lir_patch_normal +}; + + +enum LIR_MoveKind { + lir_move_normal, + lir_move_volatile, + lir_move_unaligned, + lir_move_max_flag +}; + + +// -------------------------------------------------- +// LIR_Op +// -------------------------------------------------- +class LIR_Op: public CompilationResourceObj { + friend class LIR_OpVisitState; + +#ifdef ASSERT + private: + const char * _file; + int _line; +#endif + + protected: + LIR_Opr _result; + unsigned short _code; + unsigned short _flags; + CodeEmitInfo* _info; + int _id; // value id for register allocation + int _fpu_pop_count; + Instruction* _source; // for debugging + + static void print_condition(outputStream* out, LIR_Condition cond) PRODUCT_RETURN; + + protected: + static bool is_in_range(LIR_Code test, LIR_Code start, LIR_Code end) { return start < test && test < end; } + + public: + LIR_Op() + : _result(LIR_OprFact::illegalOpr) + , _code(lir_none) + , _flags(0) + , _info(NULL) +#ifdef ASSERT + , _file(NULL) + , _line(0) +#endif + , _fpu_pop_count(0) + , _source(NULL) + , _id(-1) {} + + LIR_Op(LIR_Code code, LIR_Opr result, CodeEmitInfo* info) + : _result(result) + , _code(code) + , _flags(0) + , _info(info) +#ifdef ASSERT + , _file(NULL) + , _line(0) +#endif + , _fpu_pop_count(0) + , _source(NULL) + , _id(-1) {} + + CodeEmitInfo* info() const { return _info; } + LIR_Code code() const { return (LIR_Code)_code; } + LIR_Opr result_opr() const { return _result; } + void set_result_opr(LIR_Opr opr) { _result = opr; } + +#ifdef ASSERT + void set_file_and_line(const char * file, int line) { + _file = file; + _line = line; + } +#endif + + virtual const char * name() const PRODUCT_RETURN0; + + int id() const { return _id; } + void set_id(int id) { _id = id; } + + // FPU stack simulation helpers -- only used on Intel + void set_fpu_pop_count(int count) { assert(count >= 0 && count <= 1, "currently only 0 and 1 are valid"); _fpu_pop_count = count; } + int fpu_pop_count() const { return _fpu_pop_count; } + bool pop_fpu_stack() { return _fpu_pop_count > 0; } + + Instruction* source() const { return _source; } + void set_source(Instruction* ins) { _source = ins; } + + virtual void emit_code(LIR_Assembler* masm) = 0; + virtual void print_instr(outputStream* out) const = 0; + virtual void print_on(outputStream* st) const PRODUCT_RETURN; + + virtual LIR_OpCall* as_OpCall() { return NULL; } + virtual LIR_OpJavaCall* as_OpJavaCall() { return NULL; } + virtual LIR_OpLabel* as_OpLabel() { return NULL; } + virtual LIR_OpDelay* as_OpDelay() { return NULL; } + virtual LIR_OpLock* as_OpLock() { return NULL; } + virtual LIR_OpAllocArray* as_OpAllocArray() { return NULL; } + virtual LIR_OpAllocObj* as_OpAllocObj() { return NULL; } + virtual LIR_OpRoundFP* as_OpRoundFP() { return NULL; } + virtual LIR_OpBranch* as_OpBranch() { return NULL; } + virtual LIR_OpRTCall* as_OpRTCall() { return NULL; } + virtual LIR_OpConvert* as_OpConvert() { return NULL; } + virtual LIR_Op0* as_Op0() { return NULL; } + virtual LIR_Op1* as_Op1() { return NULL; } + virtual LIR_Op2* as_Op2() { return NULL; } + virtual LIR_Op3* as_Op3() { return NULL; } + virtual LIR_OpArrayCopy* as_OpArrayCopy() { return NULL; } + virtual LIR_OpTypeCheck* as_OpTypeCheck() { return NULL; } + virtual LIR_OpCompareAndSwap* as_OpCompareAndSwap() { return NULL; } + virtual LIR_OpProfileCall* as_OpProfileCall() { return NULL; } + + virtual void verify() const {} +}; + +// for calls +class LIR_OpCall: public LIR_Op { + friend class LIR_OpVisitState; + + protected: + address _addr; + LIR_OprList* _arguments; + protected: + LIR_OpCall(LIR_Code code, address addr, LIR_Opr result, + LIR_OprList* arguments, CodeEmitInfo* info = NULL) + : LIR_Op(code, result, info) + , _arguments(arguments) + , _addr(addr) {} + + public: + address addr() const { return _addr; } + const LIR_OprList* arguments() const { return _arguments; } + virtual LIR_OpCall* as_OpCall() { return this; } +}; + + +// -------------------------------------------------- +// LIR_OpJavaCall +// -------------------------------------------------- +class LIR_OpJavaCall: public LIR_OpCall { + friend class LIR_OpVisitState; + + private: + ciMethod* _method; + LIR_Opr _receiver; + + public: + LIR_OpJavaCall(LIR_Code code, ciMethod* method, + LIR_Opr receiver, LIR_Opr result, + address addr, LIR_OprList* arguments, + CodeEmitInfo* info) + : LIR_OpCall(code, addr, result, arguments, info) + , _receiver(receiver) + , _method(method) { assert(is_in_range(code, begin_opJavaCall, end_opJavaCall), "code check"); } + + LIR_OpJavaCall(LIR_Code code, ciMethod* method, + LIR_Opr receiver, LIR_Opr result, intptr_t vtable_offset, + LIR_OprList* arguments, CodeEmitInfo* info) + : LIR_OpCall(code, (address)vtable_offset, result, arguments, info) + , _receiver(receiver) + , _method(method) { assert(is_in_range(code, begin_opJavaCall, end_opJavaCall), "code check"); } + + LIR_Opr receiver() const { return _receiver; } + ciMethod* method() const { return _method; } + + intptr_t vtable_offset() const { + assert(_code == lir_virtual_call, "only have vtable for real vcall"); + return (intptr_t) addr(); + } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpJavaCall* as_OpJavaCall() { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + +// -------------------------------------------------- +// LIR_OpLabel +// -------------------------------------------------- +// Location where a branch can continue +class LIR_OpLabel: public LIR_Op { + friend class LIR_OpVisitState; + + private: + Label* _label; + public: + LIR_OpLabel(Label* lbl) + : LIR_Op(lir_label, LIR_OprFact::illegalOpr, NULL) + , _label(lbl) {} + Label* label() const { return _label; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpLabel* as_OpLabel() { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + +// LIR_OpArrayCopy +class LIR_OpArrayCopy: public LIR_Op { + friend class LIR_OpVisitState; + + private: + ArrayCopyStub* _stub; + LIR_Opr _src; + LIR_Opr _src_pos; + LIR_Opr _dst; + LIR_Opr _dst_pos; + LIR_Opr _length; + LIR_Opr _tmp; + ciArrayKlass* _expected_type; + int _flags; + +public: + enum Flags { + src_null_check = 1 << 0, + dst_null_check = 1 << 1, + src_pos_positive_check = 1 << 2, + dst_pos_positive_check = 1 << 3, + length_positive_check = 1 << 4, + src_range_check = 1 << 5, + dst_range_check = 1 << 6, + type_check = 1 << 7, + all_flags = (1 << 8) - 1 + }; + + LIR_OpArrayCopy(LIR_Opr src, LIR_Opr src_pos, LIR_Opr dst, LIR_Opr dst_pos, LIR_Opr length, LIR_Opr tmp, + ciArrayKlass* expected_type, int flags, CodeEmitInfo* info); + + LIR_Opr src() const { return _src; } + LIR_Opr src_pos() const { return _src_pos; } + LIR_Opr dst() const { return _dst; } + LIR_Opr dst_pos() const { return _dst_pos; } + LIR_Opr length() const { return _length; } + LIR_Opr tmp() const { return _tmp; } + int flags() const { return _flags; } + ciArrayKlass* expected_type() const { return _expected_type; } + ArrayCopyStub* stub() const { return _stub; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpArrayCopy* as_OpArrayCopy() { return this; } + void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + + +// -------------------------------------------------- +// LIR_Op0 +// -------------------------------------------------- +class LIR_Op0: public LIR_Op { + friend class LIR_OpVisitState; + + public: + LIR_Op0(LIR_Code code) + : LIR_Op(code, LIR_OprFact::illegalOpr, NULL) { assert(is_in_range(code, begin_op0, end_op0), "code check"); } + LIR_Op0(LIR_Code code, LIR_Opr result, CodeEmitInfo* info = NULL) + : LIR_Op(code, result, info) { assert(is_in_range(code, begin_op0, end_op0), "code check"); } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_Op0* as_Op0() { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + + +// -------------------------------------------------- +// LIR_Op1 +// -------------------------------------------------- + +class LIR_Op1: public LIR_Op { + friend class LIR_OpVisitState; + + protected: + LIR_Opr _opr; // input operand + BasicType _type; // Operand types + LIR_PatchCode _patch; // only required with patchin (NEEDS_CLEANUP: do we want a special instruction for patching?) + + static void print_patch_code(outputStream* out, LIR_PatchCode code); + + void set_kind(LIR_MoveKind kind) { + assert(code() == lir_move, "must be"); + _flags = kind; + } + + public: + LIR_Op1(LIR_Code code, LIR_Opr opr, LIR_Opr result = LIR_OprFact::illegalOpr, BasicType type = T_ILLEGAL, LIR_PatchCode patch = lir_patch_none, CodeEmitInfo* info = NULL) + : LIR_Op(code, result, info) + , _opr(opr) + , _patch(patch) + , _type(type) { assert(is_in_range(code, begin_op1, end_op1), "code check"); } + + LIR_Op1(LIR_Code code, LIR_Opr opr, LIR_Opr result, BasicType type, LIR_PatchCode patch, CodeEmitInfo* info, LIR_MoveKind kind) + : LIR_Op(code, result, info) + , _opr(opr) + , _patch(patch) + , _type(type) { + assert(code == lir_move, "must be"); + set_kind(kind); + } + + LIR_Op1(LIR_Code code, LIR_Opr opr, CodeEmitInfo* info) + : LIR_Op(code, LIR_OprFact::illegalOpr, info) + , _opr(opr) + , _patch(lir_patch_none) + , _type(T_ILLEGAL) { assert(is_in_range(code, begin_op1, end_op1), "code check"); } + + LIR_Opr in_opr() const { return _opr; } + LIR_PatchCode patch_code() const { return _patch; } + BasicType type() const { return _type; } + + LIR_MoveKind move_kind() const { + assert(code() == lir_move, "must be"); + return (LIR_MoveKind)_flags; + } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_Op1* as_Op1() { return this; } + virtual const char * name() const PRODUCT_RETURN0; + + void set_in_opr(LIR_Opr opr) { _opr = opr; } + + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; + virtual void verify() const; +}; + + +// for runtime calls +class LIR_OpRTCall: public LIR_OpCall { + friend class LIR_OpVisitState; + + private: + LIR_Opr _tmp; + public: + LIR_OpRTCall(address addr, LIR_Opr tmp, + LIR_Opr result, LIR_OprList* arguments, CodeEmitInfo* info = NULL) + : LIR_OpCall(lir_rtcall, addr, result, arguments, info) + , _tmp(tmp) {} + + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpRTCall* as_OpRTCall() { return this; } + + LIR_Opr tmp() const { return _tmp; } + + virtual void verify() const; +}; + + +class LIR_OpBranch: public LIR_Op { + friend class LIR_OpVisitState; + + private: + LIR_Condition _cond; + BasicType _type; + Label* _label; + BlockBegin* _block; // if this is a branch to a block, this is the block + BlockBegin* _ublock; // if this is a float-branch, this is the unorderd block + CodeStub* _stub; // if this is a branch to a stub, this is the stub + + public: + LIR_OpBranch(LIR_Condition cond, Label* lbl) + : LIR_Op(lir_branch, LIR_OprFact::illegalOpr, (CodeEmitInfo*) NULL) + , _cond(cond) + , _label(lbl) + , _block(NULL) + , _ublock(NULL) + , _stub(NULL) { } + + LIR_OpBranch(LIR_Condition cond, BasicType type, BlockBegin* block); + LIR_OpBranch(LIR_Condition cond, BasicType type, CodeStub* stub); + + // for unordered comparisons + LIR_OpBranch(LIR_Condition cond, BasicType type, BlockBegin* block, BlockBegin* ublock); + + LIR_Condition cond() const { return _cond; } + BasicType type() const { return _type; } + Label* label() const { return _label; } + BlockBegin* block() const { return _block; } + BlockBegin* ublock() const { return _ublock; } + CodeStub* stub() const { return _stub; } + + void change_block(BlockBegin* b); + void change_ublock(BlockBegin* b); + void negate_cond(); + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpBranch* as_OpBranch() { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + + +class ConversionStub; + +class LIR_OpConvert: public LIR_Op1 { + friend class LIR_OpVisitState; + + private: + Bytecodes::Code _bytecode; + ConversionStub* _stub; + + public: + LIR_OpConvert(Bytecodes::Code code, LIR_Opr opr, LIR_Opr result, ConversionStub* stub) + : LIR_Op1(lir_convert, opr, result) + , _stub(stub) + , _bytecode(code) {} + + Bytecodes::Code bytecode() const { return _bytecode; } + ConversionStub* stub() const { return _stub; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpConvert* as_OpConvert() { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; + + static void print_bytecode(outputStream* out, Bytecodes::Code code) PRODUCT_RETURN; +}; + + +// LIR_OpAllocObj +class LIR_OpAllocObj : public LIR_Op1 { + friend class LIR_OpVisitState; + + private: + LIR_Opr _tmp1; + LIR_Opr _tmp2; + LIR_Opr _tmp3; + LIR_Opr _tmp4; + int _hdr_size; + int _obj_size; + CodeStub* _stub; + bool _init_check; + + public: + LIR_OpAllocObj(LIR_Opr klass, LIR_Opr result, + LIR_Opr t1, LIR_Opr t2, LIR_Opr t3, LIR_Opr t4, + int hdr_size, int obj_size, bool init_check, CodeStub* stub) + : LIR_Op1(lir_alloc_object, klass, result) + , _tmp1(t1) + , _tmp2(t2) + , _tmp3(t3) + , _tmp4(t4) + , _hdr_size(hdr_size) + , _obj_size(obj_size) + , _init_check(init_check) + , _stub(stub) { } + + LIR_Opr klass() const { return in_opr(); } + LIR_Opr obj() const { return result_opr(); } + LIR_Opr tmp1() const { return _tmp1; } + LIR_Opr tmp2() const { return _tmp2; } + LIR_Opr tmp3() const { return _tmp3; } + LIR_Opr tmp4() const { return _tmp4; } + int header_size() const { return _hdr_size; } + int object_size() const { return _obj_size; } + bool init_check() const { return _init_check; } + CodeStub* stub() const { return _stub; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpAllocObj * as_OpAllocObj () { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + + +// LIR_OpRoundFP +class LIR_OpRoundFP : public LIR_Op1 { + friend class LIR_OpVisitState; + + private: + LIR_Opr _tmp; + + public: + LIR_OpRoundFP(LIR_Opr reg, LIR_Opr stack_loc_temp, LIR_Opr result) + : LIR_Op1(lir_roundfp, reg, result) + , _tmp(stack_loc_temp) {} + + LIR_Opr tmp() const { return _tmp; } + virtual LIR_OpRoundFP* as_OpRoundFP() { return this; } + void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + +// LIR_OpTypeCheck +class LIR_OpTypeCheck: public LIR_Op { + friend class LIR_OpVisitState; + + private: + LIR_Opr _object; + LIR_Opr _array; + ciKlass* _klass; + LIR_Opr _tmp1; + LIR_Opr _tmp2; + LIR_Opr _tmp3; + bool _fast_check; + CodeEmitInfo* _info_for_patch; + CodeEmitInfo* _info_for_exception; + CodeStub* _stub; + // Helpers for Tier1UpdateMethodData + ciMethod* _profiled_method; + int _profiled_bci; + +public: + LIR_OpTypeCheck(LIR_Code code, LIR_Opr result, LIR_Opr object, ciKlass* klass, + LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check, + CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub, + ciMethod* profiled_method, int profiled_bci); + LIR_OpTypeCheck(LIR_Code code, LIR_Opr object, LIR_Opr array, + LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, CodeEmitInfo* info_for_exception, + ciMethod* profiled_method, int profiled_bci); + + LIR_Opr object() const { return _object; } + LIR_Opr array() const { assert(code() == lir_store_check, "not valid"); return _array; } + LIR_Opr tmp1() const { return _tmp1; } + LIR_Opr tmp2() const { return _tmp2; } + LIR_Opr tmp3() const { return _tmp3; } + ciKlass* klass() const { assert(code() == lir_instanceof || code() == lir_checkcast, "not valid"); return _klass; } + bool fast_check() const { assert(code() == lir_instanceof || code() == lir_checkcast, "not valid"); return _fast_check; } + CodeEmitInfo* info_for_patch() const { return _info_for_patch; } + CodeEmitInfo* info_for_exception() const { return _info_for_exception; } + CodeStub* stub() const { return _stub; } + + // methodDataOop profiling + ciMethod* profiled_method() { return _profiled_method; } + int profiled_bci() { return _profiled_bci; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpTypeCheck* as_OpTypeCheck() { return this; } + void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + +// LIR_Op2 +class LIR_Op2: public LIR_Op { + friend class LIR_OpVisitState; + + int _fpu_stack_size; // for sin/cos implementation on Intel + + protected: + LIR_Opr _opr1; + LIR_Opr _opr2; + BasicType _type; + LIR_Opr _tmp; + LIR_Condition _condition; + + void verify() const; + + public: + LIR_Op2(LIR_Code code, LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, CodeEmitInfo* info = NULL) + : LIR_Op(code, LIR_OprFact::illegalOpr, info) + , _opr1(opr1) + , _opr2(opr2) + , _type(T_ILLEGAL) + , _condition(condition) + , _fpu_stack_size(0) + , _tmp(LIR_OprFact::illegalOpr) { + assert(code == lir_cmp, "code check"); + } + + LIR_Op2(LIR_Code code, LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr result) + : LIR_Op(code, result, NULL) + , _opr1(opr1) + , _opr2(opr2) + , _type(T_ILLEGAL) + , _condition(condition) + , _fpu_stack_size(0) + , _tmp(LIR_OprFact::illegalOpr) { + assert(code == lir_cmove, "code check"); + } + + LIR_Op2(LIR_Code code, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr result = LIR_OprFact::illegalOpr, + CodeEmitInfo* info = NULL, BasicType type = T_ILLEGAL) + : LIR_Op(code, result, info) + , _opr1(opr1) + , _opr2(opr2) + , _type(type) + , _condition(lir_cond_unknown) + , _fpu_stack_size(0) + , _tmp(LIR_OprFact::illegalOpr) { + assert(code != lir_cmp && is_in_range(code, begin_op2, end_op2), "code check"); + } + + LIR_Op2(LIR_Code code, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr result, LIR_Opr tmp) + : LIR_Op(code, result, NULL) + , _opr1(opr1) + , _opr2(opr2) + , _type(T_ILLEGAL) + , _condition(lir_cond_unknown) + , _fpu_stack_size(0) + , _tmp(tmp) { + assert(code != lir_cmp && is_in_range(code, begin_op2, end_op2), "code check"); + } + + LIR_Opr in_opr1() const { return _opr1; } + LIR_Opr in_opr2() const { return _opr2; } + BasicType type() const { return _type; } + LIR_Opr tmp_opr() const { return _tmp; } + LIR_Condition condition() const { + assert(code() == lir_cmp || code() == lir_cmove, "only valid for cmp and cmove"); return _condition; + } + + void set_fpu_stack_size(int size) { _fpu_stack_size = size; } + int fpu_stack_size() const { return _fpu_stack_size; } + + void set_in_opr1(LIR_Opr opr) { _opr1 = opr; } + void set_in_opr2(LIR_Opr opr) { _opr2 = opr; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_Op2* as_Op2() { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + +class LIR_OpAllocArray : public LIR_Op { + friend class LIR_OpVisitState; + + private: + LIR_Opr _klass; + LIR_Opr _len; + LIR_Opr _tmp1; + LIR_Opr _tmp2; + LIR_Opr _tmp3; + LIR_Opr _tmp4; + BasicType _type; + CodeStub* _stub; + + public: + LIR_OpAllocArray(LIR_Opr klass, LIR_Opr len, LIR_Opr result, LIR_Opr t1, LIR_Opr t2, LIR_Opr t3, LIR_Opr t4, BasicType type, CodeStub* stub) + : LIR_Op(lir_alloc_array, result, NULL) + , _klass(klass) + , _len(len) + , _tmp1(t1) + , _tmp2(t2) + , _tmp3(t3) + , _tmp4(t4) + , _type(type) + , _stub(stub) {} + + LIR_Opr klass() const { return _klass; } + LIR_Opr len() const { return _len; } + LIR_Opr obj() const { return result_opr(); } + LIR_Opr tmp1() const { return _tmp1; } + LIR_Opr tmp2() const { return _tmp2; } + LIR_Opr tmp3() const { return _tmp3; } + LIR_Opr tmp4() const { return _tmp4; } + BasicType type() const { return _type; } + CodeStub* stub() const { return _stub; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpAllocArray * as_OpAllocArray () { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + + +class LIR_Op3: public LIR_Op { + friend class LIR_OpVisitState; + + private: + LIR_Opr _opr1; + LIR_Opr _opr2; + LIR_Opr _opr3; + public: + LIR_Op3(LIR_Code code, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr opr3, LIR_Opr result, CodeEmitInfo* info = NULL) + : LIR_Op(code, result, info) + , _opr1(opr1) + , _opr2(opr2) + , _opr3(opr3) { assert(is_in_range(code, begin_op3, end_op3), "code check"); } + LIR_Opr in_opr1() const { return _opr1; } + LIR_Opr in_opr2() const { return _opr2; } + LIR_Opr in_opr3() const { return _opr3; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_Op3* as_Op3() { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + + +//-------------------------------- +class LabelObj: public CompilationResourceObj { + private: + Label _label; + public: + LabelObj() {} + Label* label() { return &_label; } +}; + + +class LIR_OpLock: public LIR_Op { + friend class LIR_OpVisitState; + + private: + LIR_Opr _hdr; + LIR_Opr _obj; + LIR_Opr _lock; + LIR_Opr _scratch; + CodeStub* _stub; + public: + LIR_OpLock(LIR_Code code, LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub, CodeEmitInfo* info) + : LIR_Op(code, LIR_OprFact::illegalOpr, info) + , _hdr(hdr) + , _obj(obj) + , _lock(lock) + , _scratch(scratch) + , _stub(stub) {} + + LIR_Opr hdr_opr() const { return _hdr; } + LIR_Opr obj_opr() const { return _obj; } + LIR_Opr lock_opr() const { return _lock; } + LIR_Opr scratch_opr() const { return _scratch; } + CodeStub* stub() const { return _stub; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpLock* as_OpLock() { return this; } + void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + + +class LIR_OpDelay: public LIR_Op { + friend class LIR_OpVisitState; + + private: + LIR_Op* _op; + + public: + LIR_OpDelay(LIR_Op* op, CodeEmitInfo* info): + LIR_Op(lir_delay_slot, LIR_OprFact::illegalOpr, info), + _op(op) { + assert(op->code() == lir_nop || LIRFillDelaySlots, "should be filling with nops"); + } + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpDelay* as_OpDelay() { return this; } + void print_instr(outputStream* out) const PRODUCT_RETURN; + LIR_Op* delay_op() const { return _op; } + CodeEmitInfo* call_info() const { return info(); } +}; + + +// LIR_OpCompareAndSwap +class LIR_OpCompareAndSwap : public LIR_Op { + friend class LIR_OpVisitState; + + private: + LIR_Opr _addr; + LIR_Opr _cmp_value; + LIR_Opr _new_value; + LIR_Opr _tmp1; + LIR_Opr _tmp2; + + public: + LIR_OpCompareAndSwap(LIR_Code code, LIR_Opr addr, LIR_Opr cmp_value, LIR_Opr new_value, LIR_Opr t1, LIR_Opr t2) + : LIR_Op(code, LIR_OprFact::illegalOpr, NULL) // no result, no info + , _addr(addr) + , _cmp_value(cmp_value) + , _new_value(new_value) + , _tmp1(t1) + , _tmp2(t2) { } + + LIR_Opr addr() const { return _addr; } + LIR_Opr cmp_value() const { return _cmp_value; } + LIR_Opr new_value() const { return _new_value; } + LIR_Opr tmp1() const { return _tmp1; } + LIR_Opr tmp2() const { return _tmp2; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpCompareAndSwap * as_OpCompareAndSwap () { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + +// LIR_OpProfileCall +class LIR_OpProfileCall : public LIR_Op { + friend class LIR_OpVisitState; + + private: + ciMethod* _profiled_method; + int _profiled_bci; + LIR_Opr _mdo; + LIR_Opr _recv; + LIR_Opr _tmp1; + ciKlass* _known_holder; + + public: + // Destroys recv + LIR_OpProfileCall(LIR_Code code, ciMethod* profiled_method, int profiled_bci, LIR_Opr mdo, LIR_Opr recv, LIR_Opr t1, ciKlass* known_holder) + : LIR_Op(code, LIR_OprFact::illegalOpr, NULL) // no result, no info + , _profiled_method(profiled_method) + , _profiled_bci(profiled_bci) + , _mdo(mdo) + , _recv(recv) + , _tmp1(t1) + , _known_holder(known_holder) { } + + ciMethod* profiled_method() const { return _profiled_method; } + int profiled_bci() const { return _profiled_bci; } + LIR_Opr mdo() const { return _mdo; } + LIR_Opr recv() const { return _recv; } + LIR_Opr tmp1() const { return _tmp1; } + ciKlass* known_holder() const { return _known_holder; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpProfileCall* as_OpProfileCall() { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + + +class LIR_InsertionBuffer; + +//--------------------------------LIR_List--------------------------------------------------- +// Maintains a list of LIR instructions (one instance of LIR_List per basic block) +// The LIR instructions are appended by the LIR_List class itself; +// +// Notes: +// - all offsets are(should be) in bytes +// - local positions are specified with an offset, with offset 0 being local 0 + +class LIR_List: public CompilationResourceObj { + private: + LIR_OpList _operations; + + Compilation* _compilation; +#ifndef PRODUCT + BlockBegin* _block; +#endif +#ifdef ASSERT + const char * _file; + int _line; +#endif + + void append(LIR_Op* op) { + if (op->source() == NULL) + op->set_source(_compilation->current_instruction()); +#ifndef PRODUCT + if (PrintIRWithLIR) { + _compilation->maybe_print_current_instruction(); + op->print(); tty->cr(); + } +#endif // PRODUCT + + _operations.append(op); + +#ifdef ASSERT + op->verify(); + op->set_file_and_line(_file, _line); + _file = NULL; + _line = 0; +#endif + } + + public: + LIR_List(Compilation* compilation, BlockBegin* block = NULL); + +#ifdef ASSERT + void set_file_and_line(const char * file, int line); +#endif + + //---------- accessors --------------- + LIR_OpList* instructions_list() { return &_operations; } + int length() const { return _operations.length(); } + LIR_Op* at(int i) const { return _operations.at(i); } + + NOT_PRODUCT(BlockBegin* block() const { return _block; }); + + // insert LIR_Ops in buffer to right places in LIR_List + void append(LIR_InsertionBuffer* buffer); + + //---------- mutators --------------- + void insert_before(int i, LIR_List* op_list) { _operations.insert_before(i, op_list->instructions_list()); } + void insert_before(int i, LIR_Op* op) { _operations.insert_before(i, op); } + + //---------- printing ------------- + void print_instructions() PRODUCT_RETURN; + + + //---------- instructions ------------- + void call_opt_virtual(ciMethod* method, LIR_Opr receiver, LIR_Opr result, + address dest, LIR_OprList* arguments, + CodeEmitInfo* info) { + append(new LIR_OpJavaCall(lir_optvirtual_call, method, receiver, result, dest, arguments, info)); + } + void call_static(ciMethod* method, LIR_Opr result, + address dest, LIR_OprList* arguments, CodeEmitInfo* info) { + append(new LIR_OpJavaCall(lir_static_call, method, LIR_OprFact::illegalOpr, result, dest, arguments, info)); + } + void call_icvirtual(ciMethod* method, LIR_Opr receiver, LIR_Opr result, + address dest, LIR_OprList* arguments, CodeEmitInfo* info) { + append(new LIR_OpJavaCall(lir_icvirtual_call, method, receiver, result, dest, arguments, info)); + } + void call_virtual(ciMethod* method, LIR_Opr receiver, LIR_Opr result, + intptr_t vtable_offset, LIR_OprList* arguments, CodeEmitInfo* info) { + append(new LIR_OpJavaCall(lir_virtual_call, method, receiver, result, vtable_offset, arguments, info)); + } + + void get_thread(LIR_Opr result) { append(new LIR_Op0(lir_get_thread, result)); } + void word_align() { append(new LIR_Op0(lir_word_align)); } + void membar() { append(new LIR_Op0(lir_membar)); } + void membar_acquire() { append(new LIR_Op0(lir_membar_acquire)); } + void membar_release() { append(new LIR_Op0(lir_membar_release)); } + + void nop() { append(new LIR_Op0(lir_nop)); } + void build_frame() { append(new LIR_Op0(lir_build_frame)); } + + void std_entry(LIR_Opr receiver) { append(new LIR_Op0(lir_std_entry, receiver)); } + void osr_entry(LIR_Opr osrPointer) { append(new LIR_Op0(lir_osr_entry, osrPointer)); } + + void branch_destination(Label* lbl) { append(new LIR_OpLabel(lbl)); } + + void negate(LIR_Opr from, LIR_Opr to) { append(new LIR_Op1(lir_neg, from, to)); } + void leal(LIR_Opr from, LIR_Opr result_reg) { append(new LIR_Op1(lir_leal, from, result_reg)); } + + // result is a stack location for old backend and vreg for UseLinearScan + // stack_loc_temp is an illegal register for old backend + void roundfp(LIR_Opr reg, LIR_Opr stack_loc_temp, LIR_Opr result) { append(new LIR_OpRoundFP(reg, stack_loc_temp, result)); } + void unaligned_move(LIR_Address* src, LIR_Opr dst) { append(new LIR_Op1(lir_move, LIR_OprFact::address(src), dst, dst->type(), lir_patch_none, NULL, lir_move_unaligned)); } + void unaligned_move(LIR_Opr src, LIR_Address* dst) { append(new LIR_Op1(lir_move, src, LIR_OprFact::address(dst), src->type(), lir_patch_none, NULL, lir_move_unaligned)); } + void unaligned_move(LIR_Opr src, LIR_Opr dst) { append(new LIR_Op1(lir_move, src, dst, dst->type(), lir_patch_none, NULL, lir_move_unaligned)); } + void move(LIR_Opr src, LIR_Opr dst, CodeEmitInfo* info = NULL) { append(new LIR_Op1(lir_move, src, dst, dst->type(), lir_patch_none, info)); } + void move(LIR_Address* src, LIR_Opr dst, CodeEmitInfo* info = NULL) { append(new LIR_Op1(lir_move, LIR_OprFact::address(src), dst, src->type(), lir_patch_none, info)); } + void move(LIR_Opr src, LIR_Address* dst, CodeEmitInfo* info = NULL) { append(new LIR_Op1(lir_move, src, LIR_OprFact::address(dst), dst->type(), lir_patch_none, info)); } + + void volatile_move(LIR_Opr src, LIR_Opr dst, BasicType type, CodeEmitInfo* info = NULL, LIR_PatchCode patch_code = lir_patch_none) { append(new LIR_Op1(lir_move, src, dst, type, patch_code, info, lir_move_volatile)); } + + void oop2reg (jobject o, LIR_Opr reg) { append(new LIR_Op1(lir_move, LIR_OprFact::oopConst(o), reg)); } + void oop2reg_patch(jobject o, LIR_Opr reg, CodeEmitInfo* info); + + void return_op(LIR_Opr result) { append(new LIR_Op1(lir_return, result)); } + + void safepoint(LIR_Opr tmp, CodeEmitInfo* info) { append(new LIR_Op1(lir_safepoint, tmp, info)); } + + void convert(Bytecodes::Code code, LIR_Opr left, LIR_Opr dst, ConversionStub* stub = NULL/*, bool is_32bit = false*/) { append(new LIR_OpConvert(code, left, dst, stub)); } + + void logical_and (LIR_Opr left, LIR_Opr right, LIR_Opr dst) { append(new LIR_Op2(lir_logic_and, left, right, dst)); } + void logical_or (LIR_Opr left, LIR_Opr right, LIR_Opr dst) { append(new LIR_Op2(lir_logic_or, left, right, dst)); } + void logical_xor (LIR_Opr left, LIR_Opr right, LIR_Opr dst) { append(new LIR_Op2(lir_logic_xor, left, right, dst)); } + + void null_check(LIR_Opr opr, CodeEmitInfo* info) { append(new LIR_Op1(lir_null_check, opr, info)); } + void throw_exception(LIR_Opr exceptionPC, LIR_Opr exceptionOop, CodeEmitInfo* info) { append(new LIR_Op2(lir_throw, exceptionPC, exceptionOop, LIR_OprFact::illegalOpr, info)); } + void unwind_exception(LIR_Opr exceptionPC, LIR_Opr exceptionOop, CodeEmitInfo* info) { append(new LIR_Op2(lir_unwind, exceptionPC, exceptionOop, LIR_OprFact::illegalOpr, info)); } + + void compare_to (LIR_Opr left, LIR_Opr right, LIR_Opr dst) { + append(new LIR_Op2(lir_compare_to, left, right, dst)); + } + + void push(LIR_Opr opr) { append(new LIR_Op1(lir_push, opr)); } + void pop(LIR_Opr reg) { append(new LIR_Op1(lir_pop, reg)); } + + void cmp(LIR_Condition condition, LIR_Opr left, LIR_Opr right, CodeEmitInfo* info = NULL) { + append(new LIR_Op2(lir_cmp, condition, left, right, info)); + } + void cmp(LIR_Condition condition, LIR_Opr left, int right, CodeEmitInfo* info = NULL) { + cmp(condition, left, LIR_OprFact::intConst(right), info); + } + + void cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int c, CodeEmitInfo* info); + void cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Address* addr, CodeEmitInfo* info); + + void cmove(LIR_Condition condition, LIR_Opr src1, LIR_Opr src2, LIR_Opr dst) { + append(new LIR_Op2(lir_cmove, condition, src1, src2, dst)); + } + + void cas_long(LIR_Opr addr, LIR_Opr cmp_value, LIR_Opr new_value, LIR_Opr t1, LIR_Opr t2); + void cas_obj(LIR_Opr addr, LIR_Opr cmp_value, LIR_Opr new_value, LIR_Opr t1, LIR_Opr t2); + void cas_int(LIR_Opr addr, LIR_Opr cmp_value, LIR_Opr new_value, LIR_Opr t1, LIR_Opr t2); + + void abs (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_abs , from, tmp, to)); } + void sqrt(LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_sqrt, from, tmp, to)); } + void log (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_log, from, tmp, to)); } + void log10 (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_log10, from, tmp, to)); } + void sin (LIR_Opr from, LIR_Opr to, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_Op2(lir_sin , from, tmp1, to, tmp2)); } + void cos (LIR_Opr from, LIR_Opr to, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_Op2(lir_cos , from, tmp1, to, tmp2)); } + void tan (LIR_Opr from, LIR_Opr to, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_Op2(lir_tan , from, tmp1, to, tmp2)); } + + void add (LIR_Opr left, LIR_Opr right, LIR_Opr res) { append(new LIR_Op2(lir_add, left, right, res)); } + void sub (LIR_Opr left, LIR_Opr right, LIR_Opr res, CodeEmitInfo* info = NULL) { append(new LIR_Op2(lir_sub, left, right, res, info)); } + void mul (LIR_Opr left, LIR_Opr right, LIR_Opr res) { append(new LIR_Op2(lir_mul, left, right, res)); } + void mul_strictfp (LIR_Opr left, LIR_Opr right, LIR_Opr res, LIR_Opr tmp) { append(new LIR_Op2(lir_mul_strictfp, left, right, res, tmp)); } + void div (LIR_Opr left, LIR_Opr right, LIR_Opr res, CodeEmitInfo* info = NULL) { append(new LIR_Op2(lir_div, left, right, res, info)); } + void div_strictfp (LIR_Opr left, LIR_Opr right, LIR_Opr res, LIR_Opr tmp) { append(new LIR_Op2(lir_div_strictfp, left, right, res, tmp)); } + void rem (LIR_Opr left, LIR_Opr right, LIR_Opr res, CodeEmitInfo* info = NULL) { append(new LIR_Op2(lir_rem, left, right, res, info)); } + + void volatile_load_mem_reg(LIR_Address* address, LIR_Opr dst, CodeEmitInfo* info, LIR_PatchCode patch_code = lir_patch_none); + void volatile_load_unsafe_reg(LIR_Opr base, LIR_Opr offset, LIR_Opr dst, BasicType type, CodeEmitInfo* info, LIR_PatchCode patch_code); + + void load(LIR_Address* addr, LIR_Opr src, CodeEmitInfo* info = NULL, LIR_PatchCode patch_code = lir_patch_none); + + void prefetch(LIR_Address* addr, bool is_store); + + void store_mem_int(jint v, LIR_Opr base, int offset_in_bytes, BasicType type, CodeEmitInfo* info, LIR_PatchCode patch_code = lir_patch_none); + void store_mem_oop(jobject o, LIR_Opr base, int offset_in_bytes, BasicType type, CodeEmitInfo* info, LIR_PatchCode patch_code = lir_patch_none); + void store(LIR_Opr src, LIR_Address* addr, CodeEmitInfo* info = NULL, LIR_PatchCode patch_code = lir_patch_none); + void volatile_store_mem_reg(LIR_Opr src, LIR_Address* address, CodeEmitInfo* info, LIR_PatchCode patch_code = lir_patch_none); + void volatile_store_unsafe_reg(LIR_Opr src, LIR_Opr base, LIR_Opr offset, BasicType type, CodeEmitInfo* info, LIR_PatchCode patch_code); + + void idiv(LIR_Opr left, LIR_Opr right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info); + void idiv(LIR_Opr left, int right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info); + void irem(LIR_Opr left, LIR_Opr right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info); + void irem(LIR_Opr left, int right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info); + + void allocate_object(LIR_Opr dst, LIR_Opr t1, LIR_Opr t2, LIR_Opr t3, LIR_Opr t4, int header_size, int object_size, LIR_Opr klass, bool init_check, CodeStub* stub); + void allocate_array(LIR_Opr dst, LIR_Opr len, LIR_Opr t1,LIR_Opr t2, LIR_Opr t3,LIR_Opr t4, BasicType type, LIR_Opr klass, CodeStub* stub); + + // jump is an unconditional branch + void jump(BlockBegin* block) { + append(new LIR_OpBranch(lir_cond_always, T_ILLEGAL, block)); + } + void jump(CodeStub* stub) { + append(new LIR_OpBranch(lir_cond_always, T_ILLEGAL, stub)); + } + void branch(LIR_Condition cond, Label* lbl) { append(new LIR_OpBranch(cond, lbl)); } + void branch(LIR_Condition cond, BasicType type, BlockBegin* block) { + assert(type != T_FLOAT && type != T_DOUBLE, "no fp comparisons"); + append(new LIR_OpBranch(cond, type, block)); + } + void branch(LIR_Condition cond, BasicType type, CodeStub* stub) { + assert(type != T_FLOAT && type != T_DOUBLE, "no fp comparisons"); + append(new LIR_OpBranch(cond, type, stub)); + } + void branch(LIR_Condition cond, BasicType type, BlockBegin* block, BlockBegin* unordered) { + assert(type == T_FLOAT || type == T_DOUBLE, "fp comparisons only"); + append(new LIR_OpBranch(cond, type, block, unordered)); + } + + void shift_left(LIR_Opr value, LIR_Opr count, LIR_Opr dst, LIR_Opr tmp); + void shift_right(LIR_Opr value, LIR_Opr count, LIR_Opr dst, LIR_Opr tmp); + void unsigned_shift_right(LIR_Opr value, LIR_Opr count, LIR_Opr dst, LIR_Opr tmp); + + void shift_left(LIR_Opr value, int count, LIR_Opr dst) { shift_left(value, LIR_OprFact::intConst(count), dst, LIR_OprFact::illegalOpr); } + void shift_right(LIR_Opr value, int count, LIR_Opr dst) { shift_right(value, LIR_OprFact::intConst(count), dst, LIR_OprFact::illegalOpr); } + void unsigned_shift_right(LIR_Opr value, int count, LIR_Opr dst) { unsigned_shift_right(value, LIR_OprFact::intConst(count), dst, LIR_OprFact::illegalOpr); } + + void lcmp2int(LIR_Opr left, LIR_Opr right, LIR_Opr dst) { append(new LIR_Op2(lir_cmp_l2i, left, right, dst)); } + void fcmp2int(LIR_Opr left, LIR_Opr right, LIR_Opr dst, bool is_unordered_less); + + void call_runtime_leaf(address routine, LIR_Opr tmp, LIR_Opr result, LIR_OprList* arguments) { + append(new LIR_OpRTCall(routine, tmp, result, arguments)); + } + + void call_runtime(address routine, LIR_Opr tmp, LIR_Opr result, + LIR_OprList* arguments, CodeEmitInfo* info) { + append(new LIR_OpRTCall(routine, tmp, result, arguments, info)); + } + + void load_stack_address_monitor(int monitor_ix, LIR_Opr dst) { append(new LIR_Op1(lir_monaddr, LIR_OprFact::intConst(monitor_ix), dst)); } + void unlock_object(LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, CodeStub* stub); + void lock_object(LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub, CodeEmitInfo* info); + + void set_24bit_fpu() { append(new LIR_Op0(lir_24bit_FPU )); } + void restore_fpu() { append(new LIR_Op0(lir_reset_FPU )); } + void breakpoint() { append(new LIR_Op0(lir_breakpoint)); } + + void arraycopy(LIR_Opr src, LIR_Opr src_pos, LIR_Opr dst, LIR_Opr dst_pos, LIR_Opr length, LIR_Opr tmp, ciArrayKlass* expected_type, int flags, CodeEmitInfo* info) { append(new LIR_OpArrayCopy(src, src_pos, dst, dst_pos, length, tmp, expected_type, flags, info)); } + + void fpop_raw() { append(new LIR_Op0(lir_fpop_raw)); } + + void checkcast (LIR_Opr result, LIR_Opr object, ciKlass* klass, + LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check, + CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub, + ciMethod* profiled_method, int profiled_bci); + void instanceof(LIR_Opr result, LIR_Opr object, ciKlass* klass, LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check, CodeEmitInfo* info_for_patch); + void store_check(LIR_Opr object, LIR_Opr array, LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, CodeEmitInfo* info_for_exception); + + // methodDataOop profiling + void profile_call(ciMethod* method, int bci, LIR_Opr mdo, LIR_Opr recv, LIR_Opr t1, ciKlass* cha_klass) { append(new LIR_OpProfileCall(lir_profile_call, method, bci, mdo, recv, t1, cha_klass)); } +}; + +void print_LIR(BlockList* blocks); + +class LIR_InsertionBuffer : public CompilationResourceObj { + private: + LIR_List* _lir; // the lir list where ops of this buffer should be inserted later (NULL when uninitialized) + + // list of insertion points. index and count are stored alternately: + // _index_and_count[i * 2]: the index into lir list where "count" ops should be inserted + // _index_and_count[i * 2 + 1]: the number of ops to be inserted at index + intStack _index_and_count; + + // the LIR_Ops to be inserted + LIR_OpList _ops; + + void append_new(int index, int count) { _index_and_count.append(index); _index_and_count.append(count); } + void set_index_at(int i, int value) { _index_and_count.at_put((i << 1), value); } + void set_count_at(int i, int value) { _index_and_count.at_put((i << 1) + 1, value); } + +#ifdef ASSERT + void verify(); +#endif + public: + LIR_InsertionBuffer() : _lir(NULL), _index_and_count(8), _ops(8) { } + + // must be called before using the insertion buffer + void init(LIR_List* lir) { assert(!initialized(), "already initialized"); _lir = lir; _index_and_count.clear(); _ops.clear(); } + bool initialized() const { return _lir != NULL; } + // called automatically when the buffer is appended to the LIR_List + void finish() { _lir = NULL; } + + // accessors + LIR_List* lir_list() const { return _lir; } + int number_of_insertion_points() const { return _index_and_count.length() >> 1; } + int index_at(int i) const { return _index_and_count.at((i << 1)); } + int count_at(int i) const { return _index_and_count.at((i << 1) + 1); } + + int number_of_ops() const { return _ops.length(); } + LIR_Op* op_at(int i) const { return _ops.at(i); } + + // append an instruction to the buffer + void append(int index, LIR_Op* op); + + // instruction + void move(int index, LIR_Opr src, LIR_Opr dst, CodeEmitInfo* info = NULL) { append(index, new LIR_Op1(lir_move, src, dst, dst->type(), lir_patch_none, info)); } +}; + + +// +// LIR_OpVisitState is used for manipulating LIR_Ops in an abstract way. +// Calling a LIR_Op's visit function with a LIR_OpVisitState causes +// information about the input, output and temporaries used by the +// op to be recorded. It also records whether the op has call semantics +// and also records all the CodeEmitInfos used by this op. +// + + +class LIR_OpVisitState: public StackObj { + public: + typedef enum { inputMode, firstMode = inputMode, tempMode, outputMode, numModes, invalidMode = -1 } OprMode; + + enum { + maxNumberOfOperands = 14, + maxNumberOfInfos = 4 + }; + + private: + LIR_Op* _op; + + // optimization: the operands and infos are not stored in a variable-length + // list, but in a fixed-size array to save time of size checks and resizing + int _oprs_len[numModes]; + LIR_Opr* _oprs_new[numModes][maxNumberOfOperands]; + int _info_len; + CodeEmitInfo* _info_new[maxNumberOfInfos]; + + bool _has_call; + bool _has_slow_case; + + + // only include register operands + // addresses are decomposed to the base and index registers + // constants and stack operands are ignored + void append(LIR_Opr& opr, OprMode mode) { + assert(opr->is_valid(), "should not call this otherwise"); + assert(mode >= 0 && mode < numModes, "bad mode"); + + if (opr->is_register()) { + assert(_oprs_len[mode] < maxNumberOfOperands, "array overflow"); + _oprs_new[mode][_oprs_len[mode]++] = &opr; + + } else if (opr->is_pointer()) { + LIR_Address* address = opr->as_address_ptr(); + if (address != NULL) { + // special handling for addresses: add base and index register of the address + // both are always input operands! + if (address->_base->is_valid()) { + assert(address->_base->is_register(), "must be"); + assert(_oprs_len[inputMode] < maxNumberOfOperands, "array overflow"); + _oprs_new[inputMode][_oprs_len[inputMode]++] = &address->_base; + } + if (address->_index->is_valid()) { + assert(address->_index->is_register(), "must be"); + assert(_oprs_len[inputMode] < maxNumberOfOperands, "array overflow"); + _oprs_new[inputMode][_oprs_len[inputMode]++] = &address->_index; + } + + } else { + assert(opr->is_constant(), "constant operands are not processed"); + } + } else { + assert(opr->is_stack(), "stack operands are not processed"); + } + } + + void append(CodeEmitInfo* info) { + assert(info != NULL, "should not call this otherwise"); + assert(_info_len < maxNumberOfInfos, "array overflow"); + _info_new[_info_len++] = info; + } + + public: + LIR_OpVisitState() { reset(); } + + LIR_Op* op() const { return _op; } + void set_op(LIR_Op* op) { reset(); _op = op; } + + bool has_call() const { return _has_call; } + bool has_slow_case() const { return _has_slow_case; } + + void reset() { + _op = NULL; + _has_call = false; + _has_slow_case = false; + + _oprs_len[inputMode] = 0; + _oprs_len[tempMode] = 0; + _oprs_len[outputMode] = 0; + _info_len = 0; + } + + + int opr_count(OprMode mode) const { + assert(mode >= 0 && mode < numModes, "bad mode"); + return _oprs_len[mode]; + } + + LIR_Opr opr_at(OprMode mode, int index) const { + assert(mode >= 0 && mode < numModes, "bad mode"); + assert(index >= 0 && index < _oprs_len[mode], "index out of bound"); + return *_oprs_new[mode][index]; + } + + void set_opr_at(OprMode mode, int index, LIR_Opr opr) const { + assert(mode >= 0 && mode < numModes, "bad mode"); + assert(index >= 0 && index < _oprs_len[mode], "index out of bound"); + *_oprs_new[mode][index] = opr; + } + + int info_count() const { + return _info_len; + } + + CodeEmitInfo* info_at(int index) const { + assert(index < _info_len, "index out of bounds"); + return _info_new[index]; + } + + XHandlers* all_xhandler(); + + // collects all register operands of the instruction + void visit(LIR_Op* op); + +#if ASSERT + // check that an operation has no operands + bool no_operands(LIR_Op* op); +#endif + + // LIR_Op visitor functions use these to fill in the state + void do_input(LIR_Opr& opr) { append(opr, LIR_OpVisitState::inputMode); } + void do_output(LIR_Opr& opr) { append(opr, LIR_OpVisitState::outputMode); } + void do_temp(LIR_Opr& opr) { append(opr, LIR_OpVisitState::tempMode); } + void do_info(CodeEmitInfo* info) { append(info); } + + void do_stub(CodeStub* stub); + void do_call() { _has_call = true; } + void do_slow_case() { _has_slow_case = true; } + void do_slow_case(CodeEmitInfo* info) { + _has_slow_case = true; + append(info); + } +}; + + +inline LIR_Opr LIR_OprDesc::illegalOpr() { return LIR_OprFact::illegalOpr; }; diff --git a/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp b/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp new file mode 100644 index 00000000000..a8ed3da18a7 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp @@ -0,0 +1,794 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_LIRAssembler.cpp.incl" + + +void LIR_Assembler::patching_epilog(PatchingStub* patch, LIR_PatchCode patch_code, Register obj, CodeEmitInfo* info) { + // we must have enough patching space so that call can be inserted + while ((intx) _masm->pc() - (intx) patch->pc_start() < NativeCall::instruction_size) { + _masm->nop(); + } + patch->install(_masm, patch_code, obj, info); + append_patching_stub(patch); + +#ifdef ASSERT + Bytecodes::Code code = info->scope()->method()->java_code_at_bci(info->bci()); + if (patch->id() == PatchingStub::access_field_id) { + switch (code) { + case Bytecodes::_putstatic: + case Bytecodes::_getstatic: + case Bytecodes::_putfield: + case Bytecodes::_getfield: + break; + default: + ShouldNotReachHere(); + } + } else if (patch->id() == PatchingStub::load_klass_id) { + switch (code) { + case Bytecodes::_putstatic: + case Bytecodes::_getstatic: + case Bytecodes::_new: + case Bytecodes::_anewarray: + case Bytecodes::_multianewarray: + case Bytecodes::_instanceof: + case Bytecodes::_checkcast: + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + break; + default: + ShouldNotReachHere(); + } + } else { + ShouldNotReachHere(); + } +#endif +} + + +//--------------------------------------------------------------- + + +LIR_Assembler::LIR_Assembler(Compilation* c): + _compilation(c) + , _masm(c->masm()) + , _frame_map(c->frame_map()) + , _current_block(NULL) + , _pending_non_safepoint(NULL) + , _pending_non_safepoint_offset(0) +{ + _slow_case_stubs = new CodeStubList(); +} + + +LIR_Assembler::~LIR_Assembler() { +} + + +void LIR_Assembler::append_patching_stub(PatchingStub* stub) { + _slow_case_stubs->append(stub); +} + + +void LIR_Assembler::check_codespace() { + CodeSection* cs = _masm->code_section(); + if (cs->remaining() < (int)(1*K)) { + BAILOUT("CodeBuffer overflow"); + } +} + + +void LIR_Assembler::emit_code_stub(CodeStub* stub) { + _slow_case_stubs->append(stub); +} + +void LIR_Assembler::emit_stubs(CodeStubList* stub_list) { + for (int m = 0; m < stub_list->length(); m++) { + CodeStub* s = (*stub_list)[m]; + + check_codespace(); + CHECK_BAILOUT(); + +#ifndef PRODUCT + if (CommentedAssembly) { + stringStream st; + s->print_name(&st); + st.print(" slow case"); + _masm->block_comment(st.as_string()); + } +#endif + s->emit_code(this); +#ifdef ASSERT + s->assert_no_unbound_labels(); +#endif + } +} + + +void LIR_Assembler::emit_slow_case_stubs() { + emit_stubs(_slow_case_stubs); +} + + +bool LIR_Assembler::needs_icache(ciMethod* method) const { + return !method->is_static(); +} + + +int LIR_Assembler::code_offset() const { + return _masm->offset(); +} + + +address LIR_Assembler::pc() const { + return _masm->pc(); +} + + +void LIR_Assembler::emit_exception_entries(ExceptionInfoList* info_list) { + for (int i = 0; i < info_list->length(); i++) { + XHandlers* handlers = info_list->at(i)->exception_handlers(); + + for (int j = 0; j < handlers->length(); j++) { + XHandler* handler = handlers->handler_at(j); + assert(handler->lir_op_id() != -1, "handler not processed by LinearScan"); + assert(handler->entry_code() == NULL || + handler->entry_code()->instructions_list()->last()->code() == lir_branch || + handler->entry_code()->instructions_list()->last()->code() == lir_delay_slot, "last operation must be branch"); + + if (handler->entry_pco() == -1) { + // entry code not emitted yet + if (handler->entry_code() != NULL && handler->entry_code()->instructions_list()->length() > 1) { + handler->set_entry_pco(code_offset()); + if (CommentedAssembly) { + _masm->block_comment("Exception adapter block"); + } + emit_lir_list(handler->entry_code()); + } else { + handler->set_entry_pco(handler->entry_block()->exception_handler_pco()); + } + + assert(handler->entry_pco() != -1, "must be set now"); + } + } + } +} + + +void LIR_Assembler::emit_code(BlockList* hir) { + if (PrintLIR) { + print_LIR(hir); + } + + int n = hir->length(); + for (int i = 0; i < n; i++) { + emit_block(hir->at(i)); + CHECK_BAILOUT(); + } + + flush_debug_info(code_offset()); + + DEBUG_ONLY(check_no_unbound_labels()); +} + + +void LIR_Assembler::emit_block(BlockBegin* block) { + if (block->is_set(BlockBegin::backward_branch_target_flag)) { + align_backward_branch_target(); + } + + // if this block is the start of an exception handler, record the + // PC offset of the first instruction for later construction of + // the ExceptionHandlerTable + if (block->is_set(BlockBegin::exception_entry_flag)) { + block->set_exception_handler_pco(code_offset()); + } + +#ifndef PRODUCT + if (PrintLIRWithAssembly) { + // don't print Phi's + InstructionPrinter ip(false); + block->print(ip); + } +#endif /* PRODUCT */ + + assert(block->lir() != NULL, "must have LIR"); + IA32_ONLY(assert(_masm->rsp_offset() == 0, "frame size should be fixed")); + +#ifndef PRODUCT + if (CommentedAssembly) { + stringStream st; + st.print_cr(" block B%d [%d, %d]", block->block_id(), block->bci(), block->end()->bci()); + _masm->block_comment(st.as_string()); + } +#endif + + emit_lir_list(block->lir()); + + IA32_ONLY(assert(_masm->rsp_offset() == 0, "frame size should be fixed")); +} + + +void LIR_Assembler::emit_lir_list(LIR_List* list) { + peephole(list); + + int n = list->length(); + for (int i = 0; i < n; i++) { + LIR_Op* op = list->at(i); + + check_codespace(); + CHECK_BAILOUT(); + +#ifndef PRODUCT + if (CommentedAssembly) { + // Don't record out every op since that's too verbose. Print + // branches since they include block and stub names. Also print + // patching moves since they generate funny looking code. + if (op->code() == lir_branch || + (op->code() == lir_move && op->as_Op1()->patch_code() != lir_patch_none)) { + stringStream st; + op->print_on(&st); + _masm->block_comment(st.as_string()); + } + } + if (PrintLIRWithAssembly) { + // print out the LIR operation followed by the resulting assembly + list->at(i)->print(); tty->cr(); + } +#endif /* PRODUCT */ + + op->emit_code(this); + + if (compilation()->debug_info_recorder()->recording_non_safepoints()) { + process_debug_info(op); + } + +#ifndef PRODUCT + if (PrintLIRWithAssembly) { + _masm->code()->decode(); + } +#endif /* PRODUCT */ + } +} + +#ifdef ASSERT +void LIR_Assembler::check_no_unbound_labels() { + CHECK_BAILOUT(); + + for (int i = 0; i < _branch_target_blocks.length() - 1; i++) { + if (!_branch_target_blocks.at(i)->label()->is_bound()) { + tty->print_cr("label of block B%d is not bound", _branch_target_blocks.at(i)->block_id()); + assert(false, "unbound label"); + } + } +} +#endif + +//----------------------------------debug info-------------------------------- + + +void LIR_Assembler::add_debug_info_for_branch(CodeEmitInfo* info) { + _masm->code_section()->relocate(pc(), relocInfo::poll_type); + int pc_offset = code_offset(); + flush_debug_info(pc_offset); + info->record_debug_info(compilation()->debug_info_recorder(), pc_offset); + if (info->exception_handlers() != NULL) { + compilation()->add_exception_handlers_for_pco(pc_offset, info->exception_handlers()); + } +} + + +void LIR_Assembler::add_call_info(int pc_offset, CodeEmitInfo* cinfo) { + flush_debug_info(pc_offset); + cinfo->record_debug_info(compilation()->debug_info_recorder(), pc_offset); + if (cinfo->exception_handlers() != NULL) { + compilation()->add_exception_handlers_for_pco(pc_offset, cinfo->exception_handlers()); + } +} + +static ValueStack* debug_info(Instruction* ins) { + StateSplit* ss = ins->as_StateSplit(); + if (ss != NULL) return ss->state(); + return ins->lock_stack(); +} + +void LIR_Assembler::process_debug_info(LIR_Op* op) { + Instruction* src = op->source(); + if (src == NULL) return; + int pc_offset = code_offset(); + if (_pending_non_safepoint == src) { + _pending_non_safepoint_offset = pc_offset; + return; + } + ValueStack* vstack = debug_info(src); + if (vstack == NULL) return; + if (_pending_non_safepoint != NULL) { + // Got some old debug info. Get rid of it. + if (_pending_non_safepoint->bci() == src->bci() && + debug_info(_pending_non_safepoint) == vstack) { + _pending_non_safepoint_offset = pc_offset; + return; + } + if (_pending_non_safepoint_offset < pc_offset) { + record_non_safepoint_debug_info(); + } + _pending_non_safepoint = NULL; + } + // Remember the debug info. + if (pc_offset > compilation()->debug_info_recorder()->last_pc_offset()) { + _pending_non_safepoint = src; + _pending_non_safepoint_offset = pc_offset; + } +} + +// Index caller states in s, where 0 is the oldest, 1 its callee, etc. +// Return NULL if n is too large. +// Returns the caller_bci for the next-younger state, also. +static ValueStack* nth_oldest(ValueStack* s, int n, int& bci_result) { + ValueStack* t = s; + for (int i = 0; i < n; i++) { + if (t == NULL) break; + t = t->caller_state(); + } + if (t == NULL) return NULL; + for (;;) { + ValueStack* tc = t->caller_state(); + if (tc == NULL) return s; + t = tc; + bci_result = s->scope()->caller_bci(); + s = s->caller_state(); + } +} + +void LIR_Assembler::record_non_safepoint_debug_info() { + int pc_offset = _pending_non_safepoint_offset; + ValueStack* vstack = debug_info(_pending_non_safepoint); + int bci = _pending_non_safepoint->bci(); + + DebugInformationRecorder* debug_info = compilation()->debug_info_recorder(); + assert(debug_info->recording_non_safepoints(), "sanity"); + + debug_info->add_non_safepoint(pc_offset); + + // Visit scopes from oldest to youngest. + for (int n = 0; ; n++) { + int s_bci = bci; + ValueStack* s = nth_oldest(vstack, n, s_bci); + if (s == NULL) break; + IRScope* scope = s->scope(); + debug_info->describe_scope(pc_offset, scope->method(), s_bci); + } + + debug_info->end_non_safepoint(pc_offset); +} + + +void LIR_Assembler::add_debug_info_for_null_check_here(CodeEmitInfo* cinfo) { + add_debug_info_for_null_check(code_offset(), cinfo); +} + +void LIR_Assembler::add_debug_info_for_null_check(int pc_offset, CodeEmitInfo* cinfo) { + ImplicitNullCheckStub* stub = new ImplicitNullCheckStub(pc_offset, cinfo); + emit_code_stub(stub); +} + +void LIR_Assembler::add_debug_info_for_div0_here(CodeEmitInfo* info) { + add_debug_info_for_div0(code_offset(), info); +} + +void LIR_Assembler::add_debug_info_for_div0(int pc_offset, CodeEmitInfo* cinfo) { + DivByZeroStub* stub = new DivByZeroStub(pc_offset, cinfo); + emit_code_stub(stub); +} + +void LIR_Assembler::emit_rtcall(LIR_OpRTCall* op) { + rt_call(op->result_opr(), op->addr(), op->arguments(), op->tmp(), op->info()); +} + + +void LIR_Assembler::emit_call(LIR_OpJavaCall* op) { + verify_oop_map(op->info()); + + if (os::is_MP()) { + // must align calls sites, otherwise they can't be updated atomically on MP hardware + align_call(op->code()); + } + + // emit the static call stub stuff out of line + emit_static_call_stub(); + + switch (op->code()) { + case lir_static_call: + call(op->addr(), relocInfo::static_call_type, op->info()); + break; + case lir_optvirtual_call: + call(op->addr(), relocInfo::opt_virtual_call_type, op->info()); + break; + case lir_icvirtual_call: + ic_call(op->addr(), op->info()); + break; + case lir_virtual_call: + vtable_call(op->vtable_offset(), op->info()); + break; + default: ShouldNotReachHere(); + } +#if defined(IA32) && defined(TIERED) + // C2 leave fpu stack dirty clean it + if (UseSSE < 2) { + int i; + for ( i = 1; i <= 7 ; i++ ) { + ffree(i); + } + if (!op->result_opr()->is_float_kind()) { + ffree(0); + } + } +#endif // IA32 && TIERED +} + + +void LIR_Assembler::emit_opLabel(LIR_OpLabel* op) { + _masm->bind (*(op->label())); +} + + +void LIR_Assembler::emit_op1(LIR_Op1* op) { + switch (op->code()) { + case lir_move: + if (op->move_kind() == lir_move_volatile) { + assert(op->patch_code() == lir_patch_none, "can't patch volatiles"); + volatile_move_op(op->in_opr(), op->result_opr(), op->type(), op->info()); + } else { + move_op(op->in_opr(), op->result_opr(), op->type(), + op->patch_code(), op->info(), op->pop_fpu_stack(), op->move_kind() == lir_move_unaligned); + } + break; + + case lir_prefetchr: + prefetchr(op->in_opr()); + break; + + case lir_prefetchw: + prefetchw(op->in_opr()); + break; + + case lir_roundfp: { + LIR_OpRoundFP* round_op = op->as_OpRoundFP(); + roundfp_op(round_op->in_opr(), round_op->tmp(), round_op->result_opr(), round_op->pop_fpu_stack()); + break; + } + + case lir_return: + return_op(op->in_opr()); + break; + + case lir_safepoint: + if (compilation()->debug_info_recorder()->last_pc_offset() == code_offset()) { + _masm->nop(); + } + safepoint_poll(op->in_opr(), op->info()); + break; + + case lir_fxch: + fxch(op->in_opr()->as_jint()); + break; + + case lir_fld: + fld(op->in_opr()->as_jint()); + break; + + case lir_ffree: + ffree(op->in_opr()->as_jint()); + break; + + case lir_branch: + break; + + case lir_push: + push(op->in_opr()); + break; + + case lir_pop: + pop(op->in_opr()); + break; + + case lir_neg: + negate(op->in_opr(), op->result_opr()); + break; + + case lir_leal: + leal(op->in_opr(), op->result_opr()); + break; + + case lir_null_check: + if (GenerateCompilerNullChecks) { + add_debug_info_for_null_check_here(op->info()); + + if (op->in_opr()->is_single_cpu()) { + _masm->null_check(op->in_opr()->as_register()); + } else { + Unimplemented(); + } + } + break; + + case lir_monaddr: + monitor_address(op->in_opr()->as_constant_ptr()->as_jint(), op->result_opr()); + break; + + default: + Unimplemented(); + break; + } +} + + +void LIR_Assembler::emit_op0(LIR_Op0* op) { + switch (op->code()) { + case lir_word_align: { + while (code_offset() % BytesPerWord != 0) { + _masm->nop(); + } + break; + } + + case lir_nop: + assert(op->info() == NULL, "not supported"); + _masm->nop(); + break; + + case lir_label: + Unimplemented(); + break; + + case lir_build_frame: + build_frame(); + break; + + case lir_std_entry: + // init offsets + offsets()->set_value(CodeOffsets::OSR_Entry, _masm->offset()); + _masm->align(CodeEntryAlignment); + if (needs_icache(compilation()->method())) { + check_icache(); + } + offsets()->set_value(CodeOffsets::Verified_Entry, _masm->offset()); + _masm->verified_entry(); + build_frame(); + offsets()->set_value(CodeOffsets::Frame_Complete, _masm->offset()); + break; + + case lir_osr_entry: + offsets()->set_value(CodeOffsets::OSR_Entry, _masm->offset()); + osr_entry(); + break; + + case lir_24bit_FPU: + set_24bit_FPU(); + break; + + case lir_reset_FPU: + reset_FPU(); + break; + + case lir_breakpoint: + breakpoint(); + break; + + case lir_fpop_raw: + fpop(); + break; + + case lir_membar: + membar(); + break; + + case lir_membar_acquire: + membar_acquire(); + break; + + case lir_membar_release: + membar_release(); + break; + + case lir_get_thread: + get_thread(op->result_opr()); + break; + + default: + ShouldNotReachHere(); + break; + } +} + + +void LIR_Assembler::emit_op2(LIR_Op2* op) { + switch (op->code()) { + case lir_cmp: + if (op->info() != NULL) { + assert(op->in_opr1()->is_address() || op->in_opr2()->is_address(), + "shouldn't be codeemitinfo for non-address operands"); + add_debug_info_for_null_check_here(op->info()); // exception possible + } + comp_op(op->condition(), op->in_opr1(), op->in_opr2(), op); + break; + + case lir_cmp_l2i: + case lir_cmp_fd2i: + case lir_ucmp_fd2i: + comp_fl2i(op->code(), op->in_opr1(), op->in_opr2(), op->result_opr(), op); + break; + + case lir_cmove: + cmove(op->condition(), op->in_opr1(), op->in_opr2(), op->result_opr()); + break; + + case lir_shl: + case lir_shr: + case lir_ushr: + if (op->in_opr2()->is_constant()) { + shift_op(op->code(), op->in_opr1(), op->in_opr2()->as_constant_ptr()->as_jint(), op->result_opr()); + } else { + shift_op(op->code(), op->in_opr1(), op->in_opr2(), op->result_opr(), op->tmp_opr()); + } + break; + + case lir_add: + case lir_sub: + case lir_mul: + case lir_mul_strictfp: + case lir_div: + case lir_div_strictfp: + case lir_rem: + assert(op->fpu_pop_count() < 2, ""); + arith_op( + op->code(), + op->in_opr1(), + op->in_opr2(), + op->result_opr(), + op->info(), + op->fpu_pop_count() == 1); + break; + + case lir_abs: + case lir_sqrt: + case lir_sin: + case lir_tan: + case lir_cos: + case lir_log: + case lir_log10: + intrinsic_op(op->code(), op->in_opr1(), op->in_opr2(), op->result_opr(), op); + break; + + case lir_logic_and: + case lir_logic_or: + case lir_logic_xor: + logic_op( + op->code(), + op->in_opr1(), + op->in_opr2(), + op->result_opr()); + break; + + case lir_throw: + case lir_unwind: + throw_op(op->in_opr1(), op->in_opr2(), op->info(), op->code() == lir_unwind); + break; + + default: + Unimplemented(); + break; + } +} + + +void LIR_Assembler::build_frame() { + _masm->build_frame(initial_frame_size_in_bytes()); +} + + +void LIR_Assembler::roundfp_op(LIR_Opr src, LIR_Opr tmp, LIR_Opr dest, bool pop_fpu_stack) { + assert((src->is_single_fpu() && dest->is_single_stack()) || + (src->is_double_fpu() && dest->is_double_stack()), + "round_fp: rounds register -> stack location"); + + reg2stack (src, dest, src->type(), pop_fpu_stack); +} + + +void LIR_Assembler::move_op(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool pop_fpu_stack, bool unaligned) { + if (src->is_register()) { + if (dest->is_register()) { + assert(patch_code == lir_patch_none && info == NULL, "no patching and info allowed here"); + reg2reg(src, dest); + } else if (dest->is_stack()) { + assert(patch_code == lir_patch_none && info == NULL, "no patching and info allowed here"); + reg2stack(src, dest, type, pop_fpu_stack); + } else if (dest->is_address()) { + reg2mem(src, dest, type, patch_code, info, pop_fpu_stack, unaligned); + } else { + ShouldNotReachHere(); + } + + } else if (src->is_stack()) { + assert(patch_code == lir_patch_none && info == NULL, "no patching and info allowed here"); + if (dest->is_register()) { + stack2reg(src, dest, type); + } else if (dest->is_stack()) { + stack2stack(src, dest, type); + } else { + ShouldNotReachHere(); + } + + } else if (src->is_constant()) { + if (dest->is_register()) { + const2reg(src, dest, patch_code, info); // patching is possible + } else if (dest->is_stack()) { + assert(patch_code == lir_patch_none && info == NULL, "no patching and info allowed here"); + const2stack(src, dest); + } else if (dest->is_address()) { + assert(patch_code == lir_patch_none, "no patching allowed here"); + const2mem(src, dest, type, info); + } else { + ShouldNotReachHere(); + } + + } else if (src->is_address()) { + mem2reg(src, dest, type, patch_code, info, unaligned); + + } else { + ShouldNotReachHere(); + } +} + + +void LIR_Assembler::verify_oop_map(CodeEmitInfo* info) { +#ifndef PRODUCT + if (VerifyOopMaps || VerifyOops) { + bool v = VerifyOops; + VerifyOops = true; + OopMapStream s(info->oop_map()); + while (!s.is_done()) { + OopMapValue v = s.current(); + if (v.is_oop()) { + VMReg r = v.reg(); + if (!r->is_stack()) { + stringStream st; + st.print("bad oop %s at %d", r->as_Register()->name(), _masm->offset()); +#ifdef SPARC + _masm->_verify_oop(r->as_Register(), strdup(st.as_string()), __FILE__, __LINE__); +#else + _masm->verify_oop(r->as_Register()); +#endif + } else { + _masm->verify_stack_oop(r->reg2stack() * VMRegImpl::stack_slot_size); + } + } + s.next(); + } + VerifyOops = v; + } +#endif +} diff --git a/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp b/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp new file mode 100644 index 00000000000..3a64fe678fa --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp @@ -0,0 +1,233 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Compilation; +class ScopeValue; + +class LIR_Assembler: public CompilationResourceObj { + private: + C1_MacroAssembler* _masm; + CodeStubList* _slow_case_stubs; + + Compilation* _compilation; + FrameMap* _frame_map; + BlockBegin* _current_block; + + Instruction* _pending_non_safepoint; + int _pending_non_safepoint_offset; + +#ifdef ASSERT + BlockList _branch_target_blocks; + void check_no_unbound_labels(); +#endif + + FrameMap* frame_map() const { return _frame_map; } + + void set_current_block(BlockBegin* b) { _current_block = b; } + BlockBegin* current_block() const { return _current_block; } + + // non-safepoint debug info management + void flush_debug_info(int before_pc_offset) { + if (_pending_non_safepoint != NULL) { + if (_pending_non_safepoint_offset < before_pc_offset) + record_non_safepoint_debug_info(); + _pending_non_safepoint = NULL; + } + } + void process_debug_info(LIR_Op* op); + void record_non_safepoint_debug_info(); + + // unified bailout support + void bailout(const char* msg) const { compilation()->bailout(msg); } + bool bailed_out() const { return compilation()->bailed_out(); } + + // code emission patterns and accessors + void check_codespace(); + bool needs_icache(ciMethod* method) const; + + // returns offset of icache check + int check_icache(); + + void jobject2reg(jobject o, Register reg); + void jobject2reg_with_patching(Register reg, CodeEmitInfo* info); + + void emit_stubs(CodeStubList* stub_list); + + // addresses + static Address as_Address(LIR_Address* addr); + static Address as_Address_lo(LIR_Address* addr); + static Address as_Address_hi(LIR_Address* addr); + + // debug information + void add_call_info(int pc_offset, CodeEmitInfo* cinfo); + void add_debug_info_for_branch(CodeEmitInfo* info); + void add_debug_info_for_div0(int pc_offset, CodeEmitInfo* cinfo); + void add_debug_info_for_div0_here(CodeEmitInfo* info); + void add_debug_info_for_null_check(int pc_offset, CodeEmitInfo* cinfo); + void add_debug_info_for_null_check_here(CodeEmitInfo* info); + + void set_24bit_FPU(); + void reset_FPU(); + void fpop(); + void fxch(int i); + void fld(int i); + void ffree(int i); + + void breakpoint(); + void push(LIR_Opr opr); + void pop(LIR_Opr opr); + + // patching + void append_patching_stub(PatchingStub* stub); + void patching_epilog(PatchingStub* patch, LIR_PatchCode patch_code, Register obj, CodeEmitInfo* info); + + void comp_op(LIR_Condition condition, LIR_Opr src, LIR_Opr result, LIR_Op2* op); + + public: + LIR_Assembler(Compilation* c); + ~LIR_Assembler(); + C1_MacroAssembler* masm() const { return _masm; } + Compilation* compilation() const { return _compilation; } + ciMethod* method() const { return compilation()->method(); } + + CodeOffsets* offsets() const { return _compilation->offsets(); } + int code_offset() const; + address pc() const; + + int initial_frame_size_in_bytes(); + + // test for constants which can be encoded directly in instructions + static bool is_small_constant(LIR_Opr opr); + + static LIR_Opr receiverOpr(); + static LIR_Opr incomingReceiverOpr(); + static LIR_Opr osrBufferPointer(); + + // stubs + void emit_slow_case_stubs(); + void emit_static_call_stub(); + void emit_code_stub(CodeStub* op); + void add_call_info_here(CodeEmitInfo* info) { add_call_info(code_offset(), info); } + + // code patterns + void emit_exception_handler(); + void emit_exception_entries(ExceptionInfoList* info_list); + void emit_deopt_handler(); + + void emit_code(BlockList* hir); + void emit_block(BlockBegin* block); + void emit_lir_list(LIR_List* list); + + // any last minute peephole optimizations are performed here. In + // particular sparc uses this for delay slot filling. + void peephole(LIR_List* list); + + void emit_string_compare(LIR_Opr left, LIR_Opr right, LIR_Opr dst, CodeEmitInfo* info); + + void return_op(LIR_Opr result); + + // returns offset of poll instruction + int safepoint_poll(LIR_Opr result, CodeEmitInfo* info); + + void const2reg (LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_code, CodeEmitInfo* info); + void const2stack(LIR_Opr src, LIR_Opr dest); + void const2mem (LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info); + void reg2stack (LIR_Opr src, LIR_Opr dest, BasicType type, bool pop_fpu_stack); + void reg2reg (LIR_Opr src, LIR_Opr dest); + void reg2mem (LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool pop_fpu_stack, bool unaligned); + void stack2reg (LIR_Opr src, LIR_Opr dest, BasicType type); + void stack2stack(LIR_Opr src, LIR_Opr dest, BasicType type); + void mem2reg (LIR_Opr src, LIR_Opr dest, BasicType type, + LIR_PatchCode patch_code = lir_patch_none, + CodeEmitInfo* info = NULL, bool unaligned = false); + + void prefetchr (LIR_Opr src); + void prefetchw (LIR_Opr src); + + void shift_op(LIR_Code code, LIR_Opr left, LIR_Opr count, LIR_Opr dest, LIR_Opr tmp); + void shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr dest); + + void move_regs(Register from_reg, Register to_reg); + void swap_reg(Register a, Register b); + + void emit_op0(LIR_Op0* op); + void emit_op1(LIR_Op1* op); + void emit_op2(LIR_Op2* op); + void emit_op3(LIR_Op3* op); + void emit_opBranch(LIR_OpBranch* op); + void emit_opLabel(LIR_OpLabel* op); + void emit_arraycopy(LIR_OpArrayCopy* op); + void emit_opConvert(LIR_OpConvert* op); + void emit_alloc_obj(LIR_OpAllocObj* op); + void emit_alloc_array(LIR_OpAllocArray* op); + void emit_opTypeCheck(LIR_OpTypeCheck* op); + void emit_compare_and_swap(LIR_OpCompareAndSwap* op); + void emit_lock(LIR_OpLock* op); + void emit_call(LIR_OpJavaCall* op); + void emit_rtcall(LIR_OpRTCall* op); + void emit_profile_call(LIR_OpProfileCall* op); + void emit_delay(LIR_OpDelay* op); + + void arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest, CodeEmitInfo* info, bool pop_fpu_stack); + void arithmetic_idiv(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr temp, LIR_Opr result, CodeEmitInfo* info); + void intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr unused, LIR_Opr dest, LIR_Op* op); + + void logic_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest); + + void roundfp_op(LIR_Opr src, LIR_Opr tmp, LIR_Opr dest, bool pop_fpu_stack); + void move_op(LIR_Opr src, LIR_Opr result, BasicType type, + LIR_PatchCode patch_code, CodeEmitInfo* info, bool pop_fpu_stack, bool unaligned); + void volatile_move_op(LIR_Opr src, LIR_Opr result, BasicType type, CodeEmitInfo* info); + void comp_mem_op(LIR_Opr src, LIR_Opr result, BasicType type, CodeEmitInfo* info); // info set for null exceptions + void comp_fl2i(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr result, LIR_Op2* op); + void cmove(LIR_Condition code, LIR_Opr left, LIR_Opr right, LIR_Opr result); + + void ic_call(address destination, CodeEmitInfo* info); + void vtable_call(int vtable_offset, CodeEmitInfo* info); + void call(address entry, relocInfo::relocType rtype, CodeEmitInfo* info); + + void osr_entry(); + + void build_frame(); + + void throw_op(LIR_Opr exceptionPC, LIR_Opr exceptionOop, CodeEmitInfo* info, bool unwind); + void monitor_address(int monitor_ix, LIR_Opr dst); + + void align_backward_branch_target(); + void align_call(LIR_Code code); + + void negate(LIR_Opr left, LIR_Opr dest); + void leal(LIR_Opr left, LIR_Opr dest); + + void rt_call(LIR_Opr result, address dest, const LIR_OprList* args, LIR_Opr tmp, CodeEmitInfo* info); + + void membar(); + void membar_acquire(); + void membar_release(); + void get_thread(LIR_Opr result); + + void verify_oop_map(CodeEmitInfo* info); + + #include "incls/_c1_LIRAssembler_pd.hpp.incl" +}; diff --git a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp new file mode 100644 index 00000000000..eb90b3fd2bc --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp @@ -0,0 +1,2534 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_LIRGenerator.cpp.incl" + +#ifdef ASSERT +#define __ gen()->lir(__FILE__, __LINE__)-> +#else +#define __ gen()->lir()-> +#endif + + +void PhiResolverState::reset(int max_vregs) { + // Initialize array sizes + _virtual_operands.at_put_grow(max_vregs - 1, NULL, NULL); + _virtual_operands.trunc_to(0); + _other_operands.at_put_grow(max_vregs - 1, NULL, NULL); + _other_operands.trunc_to(0); + _vreg_table.at_put_grow(max_vregs - 1, NULL, NULL); + _vreg_table.trunc_to(0); +} + + + +//-------------------------------------------------------------- +// PhiResolver + +// Resolves cycles: +// +// r1 := r2 becomes temp := r1 +// r2 := r1 r1 := r2 +// r2 := temp +// and orders moves: +// +// r2 := r3 becomes r1 := r2 +// r1 := r2 r2 := r3 + +PhiResolver::PhiResolver(LIRGenerator* gen, int max_vregs) + : _gen(gen) + , _state(gen->resolver_state()) + , _temp(LIR_OprFact::illegalOpr) +{ + // reinitialize the shared state arrays + _state.reset(max_vregs); +} + + +void PhiResolver::emit_move(LIR_Opr src, LIR_Opr dest) { + assert(src->is_valid(), ""); + assert(dest->is_valid(), ""); + __ move(src, dest); +} + + +void PhiResolver::move_temp_to(LIR_Opr dest) { + assert(_temp->is_valid(), ""); + emit_move(_temp, dest); + NOT_PRODUCT(_temp = LIR_OprFact::illegalOpr); +} + + +void PhiResolver::move_to_temp(LIR_Opr src) { + assert(_temp->is_illegal(), ""); + _temp = _gen->new_register(src->type()); + emit_move(src, _temp); +} + + +// Traverse assignment graph in depth first order and generate moves in post order +// ie. two assignments: b := c, a := b start with node c: +// Call graph: move(NULL, c) -> move(c, b) -> move(b, a) +// Generates moves in this order: move b to a and move c to b +// ie. cycle a := b, b := a start with node a +// Call graph: move(NULL, a) -> move(a, b) -> move(b, a) +// Generates moves in this order: move b to temp, move a to b, move temp to a +void PhiResolver::move(ResolveNode* src, ResolveNode* dest) { + if (!dest->visited()) { + dest->set_visited(); + for (int i = dest->no_of_destinations()-1; i >= 0; i --) { + move(dest, dest->destination_at(i)); + } + } else if (!dest->start_node()) { + // cylce in graph detected + assert(_loop == NULL, "only one loop valid!"); + _loop = dest; + move_to_temp(src->operand()); + return; + } // else dest is a start node + + if (!dest->assigned()) { + if (_loop == dest) { + move_temp_to(dest->operand()); + dest->set_assigned(); + } else if (src != NULL) { + emit_move(src->operand(), dest->operand()); + dest->set_assigned(); + } + } +} + + +PhiResolver::~PhiResolver() { + int i; + // resolve any cycles in moves from and to virtual registers + for (i = virtual_operands().length() - 1; i >= 0; i --) { + ResolveNode* node = virtual_operands()[i]; + if (!node->visited()) { + _loop = NULL; + move(NULL, node); + node->set_start_node(); + assert(_temp->is_illegal(), "move_temp_to() call missing"); + } + } + + // generate move for move from non virtual register to abitrary destination + for (i = other_operands().length() - 1; i >= 0; i --) { + ResolveNode* node = other_operands()[i]; + for (int j = node->no_of_destinations() - 1; j >= 0; j --) { + emit_move(node->operand(), node->destination_at(j)->operand()); + } + } +} + + +ResolveNode* PhiResolver::create_node(LIR_Opr opr, bool source) { + ResolveNode* node; + if (opr->is_virtual()) { + int vreg_num = opr->vreg_number(); + node = vreg_table().at_grow(vreg_num, NULL); + assert(node == NULL || node->operand() == opr, ""); + if (node == NULL) { + node = new ResolveNode(opr); + vreg_table()[vreg_num] = node; + } + // Make sure that all virtual operands show up in the list when + // they are used as the source of a move. + if (source && !virtual_operands().contains(node)) { + virtual_operands().append(node); + } + } else { + assert(source, ""); + node = new ResolveNode(opr); + other_operands().append(node); + } + return node; +} + + +void PhiResolver::move(LIR_Opr src, LIR_Opr dest) { + assert(dest->is_virtual(), ""); + // tty->print("move "); src->print(); tty->print(" to "); dest->print(); tty->cr(); + assert(src->is_valid(), ""); + assert(dest->is_valid(), ""); + ResolveNode* source = source_node(src); + source->append(destination_node(dest)); +} + + +//-------------------------------------------------------------- +// LIRItem + +void LIRItem::set_result(LIR_Opr opr) { + assert(value()->operand()->is_illegal() || value()->operand()->is_constant(), "operand should never change"); + value()->set_operand(opr); + + if (opr->is_virtual()) { + _gen->_instruction_for_operand.at_put_grow(opr->vreg_number(), value(), NULL); + } + + _result = opr; +} + +void LIRItem::load_item() { + if (result()->is_illegal()) { + // update the items result + _result = value()->operand(); + } + if (!result()->is_register()) { + LIR_Opr reg = _gen->new_register(value()->type()); + __ move(result(), reg); + if (result()->is_constant()) { + _result = reg; + } else { + set_result(reg); + } + } +} + + +void LIRItem::load_for_store(BasicType type) { + if (_gen->can_store_as_constant(value(), type)) { + _result = value()->operand(); + if (!_result->is_constant()) { + _result = LIR_OprFact::value_type(value()->type()); + } + } else if (type == T_BYTE || type == T_BOOLEAN) { + load_byte_item(); + } else { + load_item(); + } +} + +void LIRItem::load_item_force(LIR_Opr reg) { + LIR_Opr r = result(); + if (r != reg) { + if (r->type() != reg->type()) { + // moves between different types need an intervening spill slot + LIR_Opr tmp = _gen->force_to_spill(r, reg->type()); + __ move(tmp, reg); + } else { + __ move(r, reg); + } + _result = reg; + } +} + +ciObject* LIRItem::get_jobject_constant() const { + ObjectType* oc = type()->as_ObjectType(); + if (oc) { + return oc->constant_value(); + } + return NULL; +} + + +jint LIRItem::get_jint_constant() const { + assert(is_constant() && value() != NULL, ""); + assert(type()->as_IntConstant() != NULL, "type check"); + return type()->as_IntConstant()->value(); +} + + +jint LIRItem::get_address_constant() const { + assert(is_constant() && value() != NULL, ""); + assert(type()->as_AddressConstant() != NULL, "type check"); + return type()->as_AddressConstant()->value(); +} + + +jfloat LIRItem::get_jfloat_constant() const { + assert(is_constant() && value() != NULL, ""); + assert(type()->as_FloatConstant() != NULL, "type check"); + return type()->as_FloatConstant()->value(); +} + + +jdouble LIRItem::get_jdouble_constant() const { + assert(is_constant() && value() != NULL, ""); + assert(type()->as_DoubleConstant() != NULL, "type check"); + return type()->as_DoubleConstant()->value(); +} + + +jlong LIRItem::get_jlong_constant() const { + assert(is_constant() && value() != NULL, ""); + assert(type()->as_LongConstant() != NULL, "type check"); + return type()->as_LongConstant()->value(); +} + + + +//-------------------------------------------------------------- + + +void LIRGenerator::init() { + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); + CardTableModRefBS* ct = (CardTableModRefBS*)bs; + assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); + +#ifdef _LP64 + _card_table_base = new LIR_Const((jlong)ct->byte_map_base); +#else + _card_table_base = new LIR_Const((jint)ct->byte_map_base); +#endif +} + + +void LIRGenerator::block_do_prolog(BlockBegin* block) { +#ifndef PRODUCT + if (PrintIRWithLIR) { + block->print(); + } +#endif + + // set up the list of LIR instructions + assert(block->lir() == NULL, "LIR list already computed for this block"); + _lir = new LIR_List(compilation(), block); + block->set_lir(_lir); + + __ branch_destination(block->label()); + + if (LIRTraceExecution && + Compilation::current_compilation()->hir()->start()->block_id() != block->block_id() && + !block->is_set(BlockBegin::exception_entry_flag)) { + assert(block->lir()->instructions_list()->length() == 1, "should come right after br_dst"); + trace_block_entry(block); + } +} + + +void LIRGenerator::block_do_epilog(BlockBegin* block) { +#ifndef PRODUCT + if (PrintIRWithLIR) { + tty->cr(); + } +#endif + + // LIR_Opr for unpinned constants shouldn't be referenced by other + // blocks so clear them out after processing the block. + for (int i = 0; i < _unpinned_constants.length(); i++) { + _unpinned_constants.at(i)->clear_operand(); + } + _unpinned_constants.trunc_to(0); + + // clear our any registers for other local constants + _constants.trunc_to(0); + _reg_for_constants.trunc_to(0); +} + + +void LIRGenerator::block_do(BlockBegin* block) { + CHECK_BAILOUT(); + + block_do_prolog(block); + set_block(block); + + for (Instruction* instr = block; instr != NULL; instr = instr->next()) { + if (instr->is_pinned()) do_root(instr); + } + + set_block(NULL); + block_do_epilog(block); +} + + +//-------------------------LIRGenerator----------------------------- + +// This is where the tree-walk starts; instr must be root; +void LIRGenerator::do_root(Value instr) { + CHECK_BAILOUT(); + + InstructionMark im(compilation(), instr); + + assert(instr->is_pinned(), "use only with roots"); + assert(instr->subst() == instr, "shouldn't have missed substitution"); + + instr->visit(this); + + assert(!instr->has_uses() || instr->operand()->is_valid() || + instr->as_Constant() != NULL || bailed_out(), "invalid item set"); +} + + +// This is called for each node in tree; the walk stops if a root is reached +void LIRGenerator::walk(Value instr) { + InstructionMark im(compilation(), instr); + //stop walk when encounter a root + if (instr->is_pinned() && instr->as_Phi() == NULL || instr->operand()->is_valid()) { + assert(instr->operand() != LIR_OprFact::illegalOpr || instr->as_Constant() != NULL, "this root has not yet been visited"); + } else { + assert(instr->subst() == instr, "shouldn't have missed substitution"); + instr->visit(this); + // assert(instr->use_count() > 0 || instr->as_Phi() != NULL, "leaf instruction must have a use"); + } +} + + +CodeEmitInfo* LIRGenerator::state_for(Instruction* x, ValueStack* state, bool ignore_xhandler) { + int index; + Value value; + for_each_stack_value(state, index, value) { + assert(value->subst() == value, "missed substition"); + if (!value->is_pinned() && value->as_Constant() == NULL && value->as_Local() == NULL) { + walk(value); + assert(value->operand()->is_valid(), "must be evaluated now"); + } + } + ValueStack* s = state; + int bci = x->bci(); + for_each_state(s) { + IRScope* scope = s->scope(); + ciMethod* method = scope->method(); + + MethodLivenessResult liveness = method->liveness_at_bci(bci); + if (bci == SynchronizationEntryBCI) { + if (x->as_ExceptionObject() || x->as_Throw()) { + // all locals are dead on exit from the synthetic unlocker + liveness.clear(); + } else { + assert(x->as_MonitorEnter(), "only other case is MonitorEnter"); + } + } + if (!liveness.is_valid()) { + // Degenerate or breakpointed method. + bailout("Degenerate or breakpointed method"); + } else { + assert((int)liveness.size() == s->locals_size(), "error in use of liveness"); + for_each_local_value(s, index, value) { + assert(value->subst() == value, "missed substition"); + if (liveness.at(index) && !value->type()->is_illegal()) { + if (!value->is_pinned() && value->as_Constant() == NULL && value->as_Local() == NULL) { + walk(value); + assert(value->operand()->is_valid(), "must be evaluated now"); + } + } else { + // NULL out this local so that linear scan can assume that all non-NULL values are live. + s->invalidate_local(index); + } + } + } + bci = scope->caller_bci(); + } + + return new CodeEmitInfo(x->bci(), state, ignore_xhandler ? NULL : x->exception_handlers()); +} + + +CodeEmitInfo* LIRGenerator::state_for(Instruction* x) { + return state_for(x, x->lock_stack()); +} + + +void LIRGenerator::jobject2reg_with_patching(LIR_Opr r, ciObject* obj, CodeEmitInfo* info) { + if (!obj->is_loaded() || PatchALot) { + assert(info != NULL, "info must be set if class is not loaded"); + __ oop2reg_patch(NULL, r, info); + } else { + // no patching needed + __ oop2reg(obj->encoding(), r); + } +} + + +void LIRGenerator::array_range_check(LIR_Opr array, LIR_Opr index, + CodeEmitInfo* null_check_info, CodeEmitInfo* range_check_info) { + CodeStub* stub = new RangeCheckStub(range_check_info, index); + if (index->is_constant()) { + cmp_mem_int(lir_cond_belowEqual, array, arrayOopDesc::length_offset_in_bytes(), + index->as_jint(), null_check_info); + __ branch(lir_cond_belowEqual, T_INT, stub); // forward branch + } else { + cmp_reg_mem(lir_cond_aboveEqual, index, array, + arrayOopDesc::length_offset_in_bytes(), T_INT, null_check_info); + __ branch(lir_cond_aboveEqual, T_INT, stub); // forward branch + } +} + + +void LIRGenerator::nio_range_check(LIR_Opr buffer, LIR_Opr index, LIR_Opr result, CodeEmitInfo* info) { + CodeStub* stub = new RangeCheckStub(info, index, true); + if (index->is_constant()) { + cmp_mem_int(lir_cond_belowEqual, buffer, java_nio_Buffer::limit_offset(), index->as_jint(), info); + __ branch(lir_cond_belowEqual, T_INT, stub); // forward branch + } else { + cmp_reg_mem(lir_cond_aboveEqual, index, buffer, + java_nio_Buffer::limit_offset(), T_INT, info); + __ branch(lir_cond_aboveEqual, T_INT, stub); // forward branch + } + __ move(index, result); +} + + +// increment a counter returning the incremented value +LIR_Opr LIRGenerator::increment_and_return_counter(LIR_Opr base, int offset, int increment) { + LIR_Address* counter = new LIR_Address(base, offset, T_INT); + LIR_Opr result = new_register(T_INT); + __ load(counter, result); + __ add(result, LIR_OprFact::intConst(increment), result); + __ store(result, counter); + return result; +} + + +void LIRGenerator::arithmetic_op(Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, bool is_strictfp, LIR_Opr tmp_op, CodeEmitInfo* info) { + LIR_Opr result_op = result; + LIR_Opr left_op = left; + LIR_Opr right_op = right; + + if (TwoOperandLIRForm && left_op != result_op) { + assert(right_op != result_op, "malformed"); + __ move(left_op, result_op); + left_op = result_op; + } + + switch(code) { + case Bytecodes::_dadd: + case Bytecodes::_fadd: + case Bytecodes::_ladd: + case Bytecodes::_iadd: __ add(left_op, right_op, result_op); break; + case Bytecodes::_fmul: + case Bytecodes::_lmul: __ mul(left_op, right_op, result_op); break; + + case Bytecodes::_dmul: + { + if (is_strictfp) { + __ mul_strictfp(left_op, right_op, result_op, tmp_op); break; + } else { + __ mul(left_op, right_op, result_op); break; + } + } + break; + + case Bytecodes::_imul: + { + bool did_strength_reduce = false; + + if (right->is_constant()) { + int c = right->as_jint(); + if (is_power_of_2(c)) { + // do not need tmp here + __ shift_left(left_op, exact_log2(c), result_op); + did_strength_reduce = true; + } else { + did_strength_reduce = strength_reduce_multiply(left_op, c, result_op, tmp_op); + } + } + // we couldn't strength reduce so just emit the multiply + if (!did_strength_reduce) { + __ mul(left_op, right_op, result_op); + } + } + break; + + case Bytecodes::_dsub: + case Bytecodes::_fsub: + case Bytecodes::_lsub: + case Bytecodes::_isub: __ sub(left_op, right_op, result_op); break; + + case Bytecodes::_fdiv: __ div (left_op, right_op, result_op); break; + // ldiv and lrem are implemented with a direct runtime call + + case Bytecodes::_ddiv: + { + if (is_strictfp) { + __ div_strictfp (left_op, right_op, result_op, tmp_op); break; + } else { + __ div (left_op, right_op, result_op); break; + } + } + break; + + case Bytecodes::_drem: + case Bytecodes::_frem: __ rem (left_op, right_op, result_op); break; + + default: ShouldNotReachHere(); + } +} + + +void LIRGenerator::arithmetic_op_int(Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, LIR_Opr tmp) { + arithmetic_op(code, result, left, right, false, tmp); +} + + +void LIRGenerator::arithmetic_op_long(Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, CodeEmitInfo* info) { + arithmetic_op(code, result, left, right, false, LIR_OprFact::illegalOpr, info); +} + + +void LIRGenerator::arithmetic_op_fpu(Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, bool is_strictfp, LIR_Opr tmp) { + arithmetic_op(code, result, left, right, is_strictfp, tmp); +} + + +void LIRGenerator::shift_op(Bytecodes::Code code, LIR_Opr result_op, LIR_Opr value, LIR_Opr count, LIR_Opr tmp) { + if (TwoOperandLIRForm && value != result_op) { + assert(count != result_op, "malformed"); + __ move(value, result_op); + value = result_op; + } + + assert(count->is_constant() || count->is_register(), "must be"); + switch(code) { + case Bytecodes::_ishl: + case Bytecodes::_lshl: __ shift_left(value, count, result_op, tmp); break; + case Bytecodes::_ishr: + case Bytecodes::_lshr: __ shift_right(value, count, result_op, tmp); break; + case Bytecodes::_iushr: + case Bytecodes::_lushr: __ unsigned_shift_right(value, count, result_op, tmp); break; + default: ShouldNotReachHere(); + } +} + + +void LIRGenerator::logic_op (Bytecodes::Code code, LIR_Opr result_op, LIR_Opr left_op, LIR_Opr right_op) { + if (TwoOperandLIRForm && left_op != result_op) { + assert(right_op != result_op, "malformed"); + __ move(left_op, result_op); + left_op = result_op; + } + + switch(code) { + case Bytecodes::_iand: + case Bytecodes::_land: __ logical_and(left_op, right_op, result_op); break; + + case Bytecodes::_ior: + case Bytecodes::_lor: __ logical_or(left_op, right_op, result_op); break; + + case Bytecodes::_ixor: + case Bytecodes::_lxor: __ logical_xor(left_op, right_op, result_op); break; + + default: ShouldNotReachHere(); + } +} + + +void LIRGenerator::monitor_enter(LIR_Opr object, LIR_Opr lock, LIR_Opr hdr, LIR_Opr scratch, int monitor_no, CodeEmitInfo* info_for_exception, CodeEmitInfo* info) { + if (!GenerateSynchronizationCode) return; + // for slow path, use debug info for state after successful locking + CodeStub* slow_path = new MonitorEnterStub(object, lock, info); + __ load_stack_address_monitor(monitor_no, lock); + // for handling NullPointerException, use debug info representing just the lock stack before this monitorenter + __ lock_object(hdr, object, lock, scratch, slow_path, info_for_exception); +} + + +void LIRGenerator::monitor_exit(LIR_Opr object, LIR_Opr lock, LIR_Opr new_hdr, int monitor_no) { + if (!GenerateSynchronizationCode) return; + // setup registers + LIR_Opr hdr = lock; + lock = new_hdr; + CodeStub* slow_path = new MonitorExitStub(lock, UseFastLocking, monitor_no); + __ load_stack_address_monitor(monitor_no, lock); + __ unlock_object(hdr, object, lock, slow_path); +} + + +void LIRGenerator::new_instance(LIR_Opr dst, ciInstanceKlass* klass, LIR_Opr scratch1, LIR_Opr scratch2, LIR_Opr scratch3, LIR_Opr scratch4, LIR_Opr klass_reg, CodeEmitInfo* info) { + jobject2reg_with_patching(klass_reg, klass, info); + // If klass is not loaded we do not know if the klass has finalizers: + if (UseFastNewInstance && klass->is_loaded() + && !Klass::layout_helper_needs_slow_path(klass->layout_helper())) { + + Runtime1::StubID stub_id = klass->is_initialized() ? Runtime1::fast_new_instance_id : Runtime1::fast_new_instance_init_check_id; + + CodeStub* slow_path = new NewInstanceStub(klass_reg, dst, klass, info, stub_id); + + assert(klass->is_loaded(), "must be loaded"); + // allocate space for instance + assert(klass->size_helper() >= 0, "illegal instance size"); + const int instance_size = align_object_size(klass->size_helper()); + __ allocate_object(dst, scratch1, scratch2, scratch3, scratch4, + oopDesc::header_size(), instance_size, klass_reg, !klass->is_initialized(), slow_path); + } else { + CodeStub* slow_path = new NewInstanceStub(klass_reg, dst, klass, info, Runtime1::new_instance_id); + __ branch(lir_cond_always, T_ILLEGAL, slow_path); + __ branch_destination(slow_path->continuation()); + } +} + + +static bool is_constant_zero(Instruction* inst) { + IntConstant* c = inst->type()->as_IntConstant(); + if (c) { + return (c->value() == 0); + } + return false; +} + + +static bool positive_constant(Instruction* inst) { + IntConstant* c = inst->type()->as_IntConstant(); + if (c) { + return (c->value() >= 0); + } + return false; +} + + +static ciArrayKlass* as_array_klass(ciType* type) { + if (type != NULL && type->is_array_klass() && type->is_loaded()) { + return (ciArrayKlass*)type; + } else { + return NULL; + } +} + +void LIRGenerator::arraycopy_helper(Intrinsic* x, int* flagsp, ciArrayKlass** expected_typep) { + Instruction* src = x->argument_at(0); + Instruction* src_pos = x->argument_at(1); + Instruction* dst = x->argument_at(2); + Instruction* dst_pos = x->argument_at(3); + Instruction* length = x->argument_at(4); + + // first try to identify the likely type of the arrays involved + ciArrayKlass* expected_type = NULL; + bool is_exact = false; + { + ciArrayKlass* src_exact_type = as_array_klass(src->exact_type()); + ciArrayKlass* src_declared_type = as_array_klass(src->declared_type()); + ciArrayKlass* dst_exact_type = as_array_klass(dst->exact_type()); + ciArrayKlass* dst_declared_type = as_array_klass(dst->declared_type()); + if (src_exact_type != NULL && src_exact_type == dst_exact_type) { + // the types exactly match so the type is fully known + is_exact = true; + expected_type = src_exact_type; + } else if (dst_exact_type != NULL && dst_exact_type->is_obj_array_klass()) { + ciArrayKlass* dst_type = (ciArrayKlass*) dst_exact_type; + ciArrayKlass* src_type = NULL; + if (src_exact_type != NULL && src_exact_type->is_obj_array_klass()) { + src_type = (ciArrayKlass*) src_exact_type; + } else if (src_declared_type != NULL && src_declared_type->is_obj_array_klass()) { + src_type = (ciArrayKlass*) src_declared_type; + } + if (src_type != NULL) { + if (src_type->element_type()->is_subtype_of(dst_type->element_type())) { + is_exact = true; + expected_type = dst_type; + } + } + } + // at least pass along a good guess + if (expected_type == NULL) expected_type = dst_exact_type; + if (expected_type == NULL) expected_type = src_declared_type; + if (expected_type == NULL) expected_type = dst_declared_type; + } + + // if a probable array type has been identified, figure out if any + // of the required checks for a fast case can be elided. + int flags = LIR_OpArrayCopy::all_flags; + if (expected_type != NULL) { + // try to skip null checks + if (src->as_NewArray() != NULL) + flags &= ~LIR_OpArrayCopy::src_null_check; + if (dst->as_NewArray() != NULL) + flags &= ~LIR_OpArrayCopy::dst_null_check; + + // check from incoming constant values + if (positive_constant(src_pos)) + flags &= ~LIR_OpArrayCopy::src_pos_positive_check; + if (positive_constant(dst_pos)) + flags &= ~LIR_OpArrayCopy::dst_pos_positive_check; + if (positive_constant(length)) + flags &= ~LIR_OpArrayCopy::length_positive_check; + + // see if the range check can be elided, which might also imply + // that src or dst is non-null. + ArrayLength* al = length->as_ArrayLength(); + if (al != NULL) { + if (al->array() == src) { + // it's the length of the source array + flags &= ~LIR_OpArrayCopy::length_positive_check; + flags &= ~LIR_OpArrayCopy::src_null_check; + if (is_constant_zero(src_pos)) + flags &= ~LIR_OpArrayCopy::src_range_check; + } + if (al->array() == dst) { + // it's the length of the destination array + flags &= ~LIR_OpArrayCopy::length_positive_check; + flags &= ~LIR_OpArrayCopy::dst_null_check; + if (is_constant_zero(dst_pos)) + flags &= ~LIR_OpArrayCopy::dst_range_check; + } + } + if (is_exact) { + flags &= ~LIR_OpArrayCopy::type_check; + } + } + + if (src == dst) { + // moving within a single array so no type checks are needed + if (flags & LIR_OpArrayCopy::type_check) { + flags &= ~LIR_OpArrayCopy::type_check; + } + } + *flagsp = flags; + *expected_typep = (ciArrayKlass*)expected_type; +} + + +LIR_Opr LIRGenerator::round_item(LIR_Opr opr) { + assert(opr->is_register(), "why spill if item is not register?"); + + if (RoundFPResults && UseSSE < 1 && opr->is_single_fpu()) { + LIR_Opr result = new_register(T_FLOAT); + set_vreg_flag(result, must_start_in_memory); + assert(opr->is_register(), "only a register can be spilled"); + assert(opr->value_type()->is_float(), "rounding only for floats available"); + __ roundfp(opr, LIR_OprFact::illegalOpr, result); + return result; + } + return opr; +} + + +LIR_Opr LIRGenerator::force_to_spill(LIR_Opr value, BasicType t) { + assert(type2size[t] == type2size[value->type()], "size mismatch"); + if (!value->is_register()) { + // force into a register + LIR_Opr r = new_register(value->type()); + __ move(value, r); + value = r; + } + + // create a spill location + LIR_Opr tmp = new_register(t); + set_vreg_flag(tmp, LIRGenerator::must_start_in_memory); + + // move from register to spill + __ move(value, tmp); + return tmp; +} + + +void LIRGenerator::profile_branch(If* if_instr, If::Condition cond) { + if (if_instr->should_profile()) { + ciMethod* method = if_instr->profiled_method(); + assert(method != NULL, "method should be set if branch is profiled"); + ciMethodData* md = method->method_data(); + if (md == NULL) { + bailout("out of memory building methodDataOop"); + return; + } + ciProfileData* data = md->bci_to_data(if_instr->profiled_bci()); + assert(data != NULL, "must have profiling data"); + assert(data->is_BranchData(), "need BranchData for two-way branches"); + int taken_count_offset = md->byte_offset_of_slot(data, BranchData::taken_offset()); + int not_taken_count_offset = md->byte_offset_of_slot(data, BranchData::not_taken_offset()); + LIR_Opr md_reg = new_register(T_OBJECT); + __ move(LIR_OprFact::oopConst(md->encoding()), md_reg); + LIR_Opr data_offset_reg = new_register(T_INT); + __ cmove(lir_cond(cond), + LIR_OprFact::intConst(taken_count_offset), + LIR_OprFact::intConst(not_taken_count_offset), + data_offset_reg); + LIR_Opr data_reg = new_register(T_INT); + LIR_Address* data_addr = new LIR_Address(md_reg, data_offset_reg, T_INT); + __ move(LIR_OprFact::address(data_addr), data_reg); + LIR_Address* fake_incr_value = new LIR_Address(data_reg, DataLayout::counter_increment, T_INT); + // Use leal instead of add to avoid destroying condition codes on x86 + __ leal(LIR_OprFact::address(fake_incr_value), data_reg); + __ move(data_reg, LIR_OprFact::address(data_addr)); + } +} + + +// Phi technique: +// This is about passing live values from one basic block to the other. +// In code generated with Java it is rather rare that more than one +// value is on the stack from one basic block to the other. +// We optimize our technique for efficient passing of one value +// (of type long, int, double..) but it can be extended. +// When entering or leaving a basic block, all registers and all spill +// slots are release and empty. We use the released registers +// and spill slots to pass the live values from one block +// to the other. The topmost value, i.e., the value on TOS of expression +// stack is passed in registers. All other values are stored in spilling +// area. Every Phi has an index which designates its spill slot +// At exit of a basic block, we fill the register(s) and spill slots. +// At entry of a basic block, the block_prolog sets up the content of phi nodes +// and locks necessary registers and spilling slots. + + +// move current value to referenced phi function +void LIRGenerator::move_to_phi(PhiResolver* resolver, Value cur_val, Value sux_val) { + Phi* phi = sux_val->as_Phi(); + // cur_val can be null without phi being null in conjunction with inlining + if (phi != NULL && cur_val != NULL && cur_val != phi && !phi->is_illegal()) { + LIR_Opr operand = cur_val->operand(); + if (cur_val->operand()->is_illegal()) { + assert(cur_val->as_Constant() != NULL || cur_val->as_Local() != NULL, + "these can be produced lazily"); + operand = operand_for_instruction(cur_val); + } + resolver->move(operand, operand_for_instruction(phi)); + } +} + + +// Moves all stack values into their PHI position +void LIRGenerator::move_to_phi(ValueStack* cur_state) { + BlockBegin* bb = block(); + if (bb->number_of_sux() == 1) { + BlockBegin* sux = bb->sux_at(0); + assert(sux->number_of_preds() > 0, "invalid CFG"); + + // a block with only one predecessor never has phi functions + if (sux->number_of_preds() > 1) { + int max_phis = cur_state->stack_size() + cur_state->locals_size(); + PhiResolver resolver(this, _virtual_register_number + max_phis * 2); + + ValueStack* sux_state = sux->state(); + Value sux_value; + int index; + + for_each_stack_value(sux_state, index, sux_value) { + move_to_phi(&resolver, cur_state->stack_at(index), sux_value); + } + + // Inlining may cause the local state not to match up, so walk up + // the caller state until we get to the same scope as the + // successor and then start processing from there. + while (cur_state->scope() != sux_state->scope()) { + cur_state = cur_state->caller_state(); + assert(cur_state != NULL, "scopes don't match up"); + } + + for_each_local_value(sux_state, index, sux_value) { + move_to_phi(&resolver, cur_state->local_at(index), sux_value); + } + + assert(cur_state->caller_state() == sux_state->caller_state(), "caller states must be equal"); + } + } +} + + +LIR_Opr LIRGenerator::new_register(BasicType type) { + int vreg = _virtual_register_number; + // add a little fudge factor for the bailout, since the bailout is + // only checked periodically. This gives a few extra registers to + // hand out before we really run out, which helps us keep from + // tripping over assertions. + if (vreg + 20 >= LIR_OprDesc::vreg_max) { + bailout("out of virtual registers"); + if (vreg + 2 >= LIR_OprDesc::vreg_max) { + // wrap it around + _virtual_register_number = LIR_OprDesc::vreg_base; + } + } + _virtual_register_number += 1; + if (type == T_ADDRESS) type = T_INT; + return LIR_OprFact::virtual_register(vreg, type); +} + + +// Try to lock using register in hint +LIR_Opr LIRGenerator::rlock(Value instr) { + return new_register(instr->type()); +} + + +// does an rlock and sets result +LIR_Opr LIRGenerator::rlock_result(Value x) { + LIR_Opr reg = rlock(x); + set_result(x, reg); + return reg; +} + + +// does an rlock and sets result +LIR_Opr LIRGenerator::rlock_result(Value x, BasicType type) { + LIR_Opr reg; + switch (type) { + case T_BYTE: + case T_BOOLEAN: + reg = rlock_byte(type); + break; + default: + reg = rlock(x); + break; + } + + set_result(x, reg); + return reg; +} + + +//--------------------------------------------------------------------- +ciObject* LIRGenerator::get_jobject_constant(Value value) { + ObjectType* oc = value->type()->as_ObjectType(); + if (oc) { + return oc->constant_value(); + } + return NULL; +} + + +void LIRGenerator::do_ExceptionObject(ExceptionObject* x) { + assert(block()->is_set(BlockBegin::exception_entry_flag), "ExceptionObject only allowed in exception handler block"); + assert(block()->next() == x, "ExceptionObject must be first instruction of block"); + + // no moves are created for phi functions at the begin of exception + // handlers, so assign operands manually here + for_each_phi_fun(block(), phi, + operand_for_instruction(phi)); + + LIR_Opr thread_reg = getThreadPointer(); + __ move(new LIR_Address(thread_reg, in_bytes(JavaThread::exception_oop_offset()), T_OBJECT), + exceptionOopOpr()); + __ move(LIR_OprFact::oopConst(NULL), + new LIR_Address(thread_reg, in_bytes(JavaThread::exception_oop_offset()), T_OBJECT)); + __ move(LIR_OprFact::oopConst(NULL), + new LIR_Address(thread_reg, in_bytes(JavaThread::exception_pc_offset()), T_OBJECT)); + + LIR_Opr result = new_register(T_OBJECT); + __ move(exceptionOopOpr(), result); + set_result(x, result); +} + + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +// visitor functions +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- + +void LIRGenerator::do_Phi(Phi* x) { + // phi functions are never visited directly + ShouldNotReachHere(); +} + + +// Code for a constant is generated lazily unless the constant is frequently used and can't be inlined. +void LIRGenerator::do_Constant(Constant* x) { + if (x->state() != NULL) { + // Any constant with a ValueStack requires patching so emit the patch here + LIR_Opr reg = rlock_result(x); + CodeEmitInfo* info = state_for(x, x->state()); + __ oop2reg_patch(NULL, reg, info); + } else if (x->use_count() > 1 && !can_inline_as_constant(x)) { + if (!x->is_pinned()) { + // unpinned constants are handled specially so that they can be + // put into registers when they are used multiple times within a + // block. After the block completes their operand will be + // cleared so that other blocks can't refer to that register. + set_result(x, load_constant(x)); + } else { + LIR_Opr res = x->operand(); + if (!res->is_valid()) { + res = LIR_OprFact::value_type(x->type()); + } + if (res->is_constant()) { + LIR_Opr reg = rlock_result(x); + __ move(res, reg); + } else { + set_result(x, res); + } + } + } else { + set_result(x, LIR_OprFact::value_type(x->type())); + } +} + + +void LIRGenerator::do_Local(Local* x) { + // operand_for_instruction has the side effect of setting the result + // so there's no need to do it here. + operand_for_instruction(x); +} + + +void LIRGenerator::do_IfInstanceOf(IfInstanceOf* x) { + Unimplemented(); +} + + +void LIRGenerator::do_Return(Return* x) { + if (DTraceMethodProbes) { + BasicTypeList signature; + signature.append(T_INT); // thread + signature.append(T_OBJECT); // methodOop + LIR_OprList* args = new LIR_OprList(); + args->append(getThreadPointer()); + LIR_Opr meth = new_register(T_OBJECT); + __ oop2reg(method()->encoding(), meth); + args->append(meth); + call_runtime(&signature, args, CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), voidType, NULL); + } + + if (x->type()->is_void()) { + __ return_op(LIR_OprFact::illegalOpr); + } else { + LIR_Opr reg = result_register_for(x->type(), /*callee=*/true); + LIRItem result(x->result(), this); + + result.load_item_force(reg); + __ return_op(result.result()); + } + set_no_result(x); +} + + +// Example: object.getClass () +void LIRGenerator::do_getClass(Intrinsic* x) { + assert(x->number_of_arguments() == 1, "wrong type"); + + LIRItem rcvr(x->argument_at(0), this); + rcvr.load_item(); + LIR_Opr result = rlock_result(x); + + // need to perform the null check on the rcvr + CodeEmitInfo* info = NULL; + if (x->needs_null_check()) { + info = state_for(x, x->state()->copy_locks()); + } + __ move(new LIR_Address(rcvr.result(), oopDesc::klass_offset_in_bytes(), T_OBJECT), result, info); + __ move(new LIR_Address(result, Klass::java_mirror_offset_in_bytes() + + klassOopDesc::klass_part_offset_in_bytes(), T_OBJECT), result); +} + + +// Example: Thread.currentThread() +void LIRGenerator::do_currentThread(Intrinsic* x) { + assert(x->number_of_arguments() == 0, "wrong type"); + LIR_Opr reg = rlock_result(x); + __ load(new LIR_Address(getThreadPointer(), in_bytes(JavaThread::threadObj_offset()), T_OBJECT), reg); +} + + +void LIRGenerator::do_RegisterFinalizer(Intrinsic* x) { + assert(x->number_of_arguments() == 1, "wrong type"); + LIRItem receiver(x->argument_at(0), this); + + receiver.load_item(); + BasicTypeList signature; + signature.append(T_OBJECT); // receiver + LIR_OprList* args = new LIR_OprList(); + args->append(receiver.result()); + CodeEmitInfo* info = state_for(x, x->state()); + call_runtime(&signature, args, + CAST_FROM_FN_PTR(address, Runtime1::entry_for(Runtime1::register_finalizer_id)), + voidType, info); + + set_no_result(x); +} + + +//------------------------local access-------------------------------------- + +LIR_Opr LIRGenerator::operand_for_instruction(Instruction* x) { + if (x->operand()->is_illegal()) { + Constant* c = x->as_Constant(); + if (c != NULL) { + x->set_operand(LIR_OprFact::value_type(c->type())); + } else { + assert(x->as_Phi() || x->as_Local() != NULL, "only for Phi and Local"); + // allocate a virtual register for this local or phi + x->set_operand(rlock(x)); + _instruction_for_operand.at_put_grow(x->operand()->vreg_number(), x, NULL); + } + } + return x->operand(); +} + + +Instruction* LIRGenerator::instruction_for_opr(LIR_Opr opr) { + if (opr->is_virtual()) { + return instruction_for_vreg(opr->vreg_number()); + } + return NULL; +} + + +Instruction* LIRGenerator::instruction_for_vreg(int reg_num) { + if (reg_num < _instruction_for_operand.length()) { + return _instruction_for_operand.at(reg_num); + } + return NULL; +} + + +void LIRGenerator::set_vreg_flag(int vreg_num, VregFlag f) { + if (_vreg_flags.size_in_bits() == 0) { + BitMap2D temp(100, num_vreg_flags); + temp.clear(); + _vreg_flags = temp; + } + _vreg_flags.at_put_grow(vreg_num, f, true); +} + +bool LIRGenerator::is_vreg_flag_set(int vreg_num, VregFlag f) { + if (!_vreg_flags.is_valid_index(vreg_num, f)) { + return false; + } + return _vreg_flags.at(vreg_num, f); +} + + +// Block local constant handling. This code is useful for keeping +// unpinned constants and constants which aren't exposed in the IR in +// registers. Unpinned Constant instructions have their operands +// cleared when the block is finished so that other blocks can't end +// up referring to their registers. + +LIR_Opr LIRGenerator::load_constant(Constant* x) { + assert(!x->is_pinned(), "only for unpinned constants"); + _unpinned_constants.append(x); + return load_constant(LIR_OprFact::value_type(x->type())->as_constant_ptr()); +} + + +LIR_Opr LIRGenerator::load_constant(LIR_Const* c) { + BasicType t = c->type(); + for (int i = 0; i < _constants.length(); i++) { + LIR_Const* other = _constants.at(i); + if (t == other->type()) { + switch (t) { + case T_INT: + case T_FLOAT: + if (c->as_jint_bits() != other->as_jint_bits()) continue; + break; + case T_LONG: + case T_DOUBLE: + if (c->as_jint_hi_bits() != other->as_jint_lo_bits()) continue; + if (c->as_jint_lo_bits() != other->as_jint_hi_bits()) continue; + break; + case T_OBJECT: + if (c->as_jobject() != other->as_jobject()) continue; + break; + } + return _reg_for_constants.at(i); + } + } + + LIR_Opr result = new_register(t); + __ move((LIR_Opr)c, result); + _constants.append(c); + _reg_for_constants.append(result); + return result; +} + +// Various barriers + +void LIRGenerator::post_barrier(LIR_OprDesc* addr, LIR_OprDesc* new_val) { + switch (Universe::heap()->barrier_set()->kind()) { + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + CardTableModRef_post_barrier(addr, new_val); + break; + case BarrierSet::ModRef: + case BarrierSet::Other: + // No post barriers + break; + default : + ShouldNotReachHere(); + } +} + +void LIRGenerator::CardTableModRef_post_barrier(LIR_OprDesc* addr, LIR_OprDesc* new_val) { + + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(sizeof(*((CardTableModRefBS*)bs)->byte_map_base) == sizeof(jbyte), "adjust this code"); + LIR_Const* card_table_base = new LIR_Const(((CardTableModRefBS*)bs)->byte_map_base); + if (addr->is_address()) { + LIR_Address* address = addr->as_address_ptr(); + LIR_Opr ptr = new_register(T_OBJECT); + if (!address->index()->is_valid() && address->disp() == 0) { + __ move(address->base(), ptr); + } else { + assert(address->disp() != max_jint, "lea doesn't support patched addresses!"); + __ leal(addr, ptr); + } + addr = ptr; + } + assert(addr->is_register(), "must be a register at this point"); + + LIR_Opr tmp = new_pointer_register(); + if (TwoOperandLIRForm) { + __ move(addr, tmp); + __ unsigned_shift_right(tmp, CardTableModRefBS::card_shift, tmp); + } else { + __ unsigned_shift_right(addr, CardTableModRefBS::card_shift, tmp); + } + if (can_inline_as_constant(card_table_base)) { + __ move(LIR_OprFact::intConst(0), + new LIR_Address(tmp, card_table_base->as_jint(), T_BYTE)); + } else { + __ move(LIR_OprFact::intConst(0), + new LIR_Address(tmp, load_constant(card_table_base), + T_BYTE)); + } +} + + +//------------------------field access-------------------------------------- + +// Comment copied form templateTable_i486.cpp +// ---------------------------------------------------------------------------- +// Volatile variables demand their effects be made known to all CPU's in +// order. Store buffers on most chips allow reads & writes to reorder; the +// JMM's ReadAfterWrite.java test fails in -Xint mode without some kind of +// memory barrier (i.e., it's not sufficient that the interpreter does not +// reorder volatile references, the hardware also must not reorder them). +// +// According to the new Java Memory Model (JMM): +// (1) All volatiles are serialized wrt to each other. +// ALSO reads & writes act as aquire & release, so: +// (2) A read cannot let unrelated NON-volatile memory refs that happen after +// the read float up to before the read. It's OK for non-volatile memory refs +// that happen before the volatile read to float down below it. +// (3) Similar a volatile write cannot let unrelated NON-volatile memory refs +// that happen BEFORE the write float down to after the write. It's OK for +// non-volatile memory refs that happen after the volatile write to float up +// before it. +// +// We only put in barriers around volatile refs (they are expensive), not +// _between_ memory refs (that would require us to track the flavor of the +// previous memory refs). Requirements (2) and (3) require some barriers +// before volatile stores and after volatile loads. These nearly cover +// requirement (1) but miss the volatile-store-volatile-load case. This final +// case is placed after volatile-stores although it could just as well go +// before volatile-loads. + + +void LIRGenerator::do_StoreField(StoreField* x) { + bool needs_patching = x->needs_patching(); + bool is_volatile = x->field()->is_volatile(); + BasicType field_type = x->field_type(); + bool is_oop = (field_type == T_ARRAY || field_type == T_OBJECT); + + CodeEmitInfo* info = NULL; + if (needs_patching) { + assert(x->explicit_null_check() == NULL, "can't fold null check into patching field access"); + info = state_for(x, x->state_before()); + } else if (x->needs_null_check()) { + NullCheck* nc = x->explicit_null_check(); + if (nc == NULL) { + info = state_for(x, x->lock_stack()); + } else { + info = state_for(nc); + } + } + + + LIRItem object(x->obj(), this); + LIRItem value(x->value(), this); + + object.load_item(); + + if (is_volatile || needs_patching) { + // load item if field is volatile (fewer special cases for volatiles) + // load item if field not initialized + // load item if field not constant + // because of code patching we cannot inline constants + if (field_type == T_BYTE || field_type == T_BOOLEAN) { + value.load_byte_item(); + } else { + value.load_item(); + } + } else { + value.load_for_store(field_type); + } + + set_no_result(x); + + if (PrintNotLoaded && needs_patching) { + tty->print_cr(" ###class not loaded at store_%s bci %d", + x->is_static() ? "static" : "field", x->bci()); + } + + if (x->needs_null_check() && + (needs_patching || + MacroAssembler::needs_explicit_null_check(x->offset()))) { + // emit an explicit null check because the offset is too large + __ null_check(object.result(), new CodeEmitInfo(info)); + } + + LIR_Address* address; + if (needs_patching) { + // we need to patch the offset in the instruction so don't allow + // generate_address to try to be smart about emitting the -1. + // Otherwise the patching code won't know how to find the + // instruction to patch. + address = new LIR_Address(object.result(), max_jint, field_type); + } else { + address = generate_address(object.result(), x->offset(), field_type); + } + + if (is_volatile && os::is_MP()) { + __ membar_release(); + } + + if (is_volatile) { + assert(!needs_patching && x->is_loaded(), + "how do we know it's volatile if it's not loaded"); + volatile_field_store(value.result(), address, info); + } else { + LIR_PatchCode patch_code = needs_patching ? lir_patch_normal : lir_patch_none; + __ store(value.result(), address, info, patch_code); + } + + if (is_oop) { + post_barrier(object.result(), value.result()); + } + + if (is_volatile && os::is_MP()) { + __ membar(); + } +} + + +void LIRGenerator::do_LoadField(LoadField* x) { + bool needs_patching = x->needs_patching(); + bool is_volatile = x->field()->is_volatile(); + BasicType field_type = x->field_type(); + + CodeEmitInfo* info = NULL; + if (needs_patching) { + assert(x->explicit_null_check() == NULL, "can't fold null check into patching field access"); + info = state_for(x, x->state_before()); + } else if (x->needs_null_check()) { + NullCheck* nc = x->explicit_null_check(); + if (nc == NULL) { + info = state_for(x, x->lock_stack()); + } else { + info = state_for(nc); + } + } + + LIRItem object(x->obj(), this); + + object.load_item(); + + if (PrintNotLoaded && needs_patching) { + tty->print_cr(" ###class not loaded at load_%s bci %d", + x->is_static() ? "static" : "field", x->bci()); + } + + if (x->needs_null_check() && + (needs_patching || + MacroAssembler::needs_explicit_null_check(x->offset()))) { + // emit an explicit null check because the offset is too large + __ null_check(object.result(), new CodeEmitInfo(info)); + } + + LIR_Opr reg = rlock_result(x, field_type); + LIR_Address* address; + if (needs_patching) { + // we need to patch the offset in the instruction so don't allow + // generate_address to try to be smart about emitting the -1. + // Otherwise the patching code won't know how to find the + // instruction to patch. + address = new LIR_Address(object.result(), max_jint, field_type); + } else { + address = generate_address(object.result(), x->offset(), field_type); + } + + if (is_volatile) { + assert(!needs_patching && x->is_loaded(), + "how do we know it's volatile if it's not loaded"); + volatile_field_load(address, reg, info); + } else { + LIR_PatchCode patch_code = needs_patching ? lir_patch_normal : lir_patch_none; + __ load(address, reg, info, patch_code); + } + + if (is_volatile && os::is_MP()) { + __ membar_acquire(); + } +} + + +//------------------------java.nio.Buffer.checkIndex------------------------ + +// int java.nio.Buffer.checkIndex(int) +void LIRGenerator::do_NIOCheckIndex(Intrinsic* x) { + // NOTE: by the time we are in checkIndex() we are guaranteed that + // the buffer is non-null (because checkIndex is package-private and + // only called from within other methods in the buffer). + assert(x->number_of_arguments() == 2, "wrong type"); + LIRItem buf (x->argument_at(0), this); + LIRItem index(x->argument_at(1), this); + buf.load_item(); + index.load_item(); + + LIR_Opr result = rlock_result(x); + if (GenerateRangeChecks) { + CodeEmitInfo* info = state_for(x); + CodeStub* stub = new RangeCheckStub(info, index.result(), true); + if (index.result()->is_constant()) { + cmp_mem_int(lir_cond_belowEqual, buf.result(), java_nio_Buffer::limit_offset(), index.result()->as_jint(), info); + __ branch(lir_cond_belowEqual, T_INT, stub); + } else { + cmp_reg_mem(lir_cond_aboveEqual, index.result(), buf.result(), + java_nio_Buffer::limit_offset(), T_INT, info); + __ branch(lir_cond_aboveEqual, T_INT, stub); + } + __ move(index.result(), result); + } else { + // Just load the index into the result register + __ move(index.result(), result); + } +} + + +//------------------------array access-------------------------------------- + + +void LIRGenerator::do_ArrayLength(ArrayLength* x) { + LIRItem array(x->array(), this); + array.load_item(); + LIR_Opr reg = rlock_result(x); + + CodeEmitInfo* info = NULL; + if (x->needs_null_check()) { + NullCheck* nc = x->explicit_null_check(); + if (nc == NULL) { + info = state_for(x); + } else { + info = state_for(nc); + } + } + __ load(new LIR_Address(array.result(), arrayOopDesc::length_offset_in_bytes(), T_INT), reg, info, lir_patch_none); +} + + +void LIRGenerator::do_LoadIndexed(LoadIndexed* x) { + bool use_length = x->length() != NULL; + LIRItem array(x->array(), this); + LIRItem index(x->index(), this); + LIRItem length(this); + bool needs_range_check = true; + + if (use_length) { + needs_range_check = x->compute_needs_range_check(); + if (needs_range_check) { + length.set_instruction(x->length()); + length.load_item(); + } + } + + array.load_item(); + if (index.is_constant() && can_inline_as_constant(x->index())) { + // let it be a constant + index.dont_load_item(); + } else { + index.load_item(); + } + + CodeEmitInfo* range_check_info = state_for(x); + CodeEmitInfo* null_check_info = NULL; + if (x->needs_null_check()) { + NullCheck* nc = x->explicit_null_check(); + if (nc != NULL) { + null_check_info = state_for(nc); + } else { + null_check_info = range_check_info; + } + } + + // emit array address setup early so it schedules better + LIR_Address* array_addr = emit_array_address(array.result(), index.result(), x->elt_type(), false); + + if (GenerateRangeChecks && needs_range_check) { + if (use_length) { + // TODO: use a (modified) version of array_range_check that does not require a + // constant length to be loaded to a register + __ cmp(lir_cond_belowEqual, length.result(), index.result()); + __ branch(lir_cond_belowEqual, T_INT, new RangeCheckStub(range_check_info, index.result())); + } else { + array_range_check(array.result(), index.result(), null_check_info, range_check_info); + // The range check performs the null check, so clear it out for the load + null_check_info = NULL; + } + } + + __ move(array_addr, rlock_result(x, x->elt_type()), null_check_info); +} + + +void LIRGenerator::do_NullCheck(NullCheck* x) { + if (x->can_trap()) { + LIRItem value(x->obj(), this); + value.load_item(); + CodeEmitInfo* info = state_for(x); + __ null_check(value.result(), info); + } +} + + +void LIRGenerator::do_Throw(Throw* x) { + LIRItem exception(x->exception(), this); + exception.load_item(); + set_no_result(x); + LIR_Opr exception_opr = exception.result(); + CodeEmitInfo* info = state_for(x, x->state()); + +#ifndef PRODUCT + if (PrintC1Statistics) { + increment_counter(Runtime1::throw_count_address()); + } +#endif + + // check if the instruction has an xhandler in any of the nested scopes + bool unwind = false; + if (info->exception_handlers()->length() == 0) { + // this throw is not inside an xhandler + unwind = true; + } else { + // get some idea of the throw type + bool type_is_exact = true; + ciType* throw_type = x->exception()->exact_type(); + if (throw_type == NULL) { + type_is_exact = false; + throw_type = x->exception()->declared_type(); + } + if (throw_type != NULL && throw_type->is_instance_klass()) { + ciInstanceKlass* throw_klass = (ciInstanceKlass*)throw_type; + unwind = !x->exception_handlers()->could_catch(throw_klass, type_is_exact); + } + } + + // do null check before moving exception oop into fixed register + // to avoid a fixed interval with an oop during the null check. + // Use a copy of the CodeEmitInfo because debug information is + // different for null_check and throw. + if (GenerateCompilerNullChecks && + (x->exception()->as_NewInstance() == NULL && x->exception()->as_ExceptionObject() == NULL)) { + // if the exception object wasn't created using new then it might be null. + __ null_check(exception_opr, new CodeEmitInfo(info, true)); + } + + if (JvmtiExport::can_post_exceptions() && + !block()->is_set(BlockBegin::default_exception_handler_flag)) { + // we need to go through the exception lookup path to get JVMTI + // notification done + unwind = false; + } + + assert(!block()->is_set(BlockBegin::default_exception_handler_flag) || unwind, + "should be no more handlers to dispatch to"); + + if (DTraceMethodProbes && + block()->is_set(BlockBegin::default_exception_handler_flag)) { + // notify that this frame is unwinding + BasicTypeList signature; + signature.append(T_INT); // thread + signature.append(T_OBJECT); // methodOop + LIR_OprList* args = new LIR_OprList(); + args->append(getThreadPointer()); + LIR_Opr meth = new_register(T_OBJECT); + __ oop2reg(method()->encoding(), meth); + args->append(meth); + call_runtime(&signature, args, CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), voidType, NULL); + } + + // move exception oop into fixed register + __ move(exception_opr, exceptionOopOpr()); + + if (unwind) { + __ unwind_exception(LIR_OprFact::illegalOpr, exceptionOopOpr(), info); + } else { + __ throw_exception(exceptionPcOpr(), exceptionOopOpr(), info); + } +} + + +void LIRGenerator::do_RoundFP(RoundFP* x) { + LIRItem input(x->input(), this); + input.load_item(); + LIR_Opr input_opr = input.result(); + assert(input_opr->is_register(), "why round if value is not in a register?"); + assert(input_opr->is_single_fpu() || input_opr->is_double_fpu(), "input should be floating-point value"); + if (input_opr->is_single_fpu()) { + set_result(x, round_item(input_opr)); // This code path not currently taken + } else { + LIR_Opr result = new_register(T_DOUBLE); + set_vreg_flag(result, must_start_in_memory); + __ roundfp(input_opr, LIR_OprFact::illegalOpr, result); + set_result(x, result); + } +} + +void LIRGenerator::do_UnsafeGetRaw(UnsafeGetRaw* x) { + LIRItem base(x->base(), this); + LIRItem idx(this); + + base.load_item(); + if (x->has_index()) { + idx.set_instruction(x->index()); + idx.load_nonconstant(); + } + + LIR_Opr reg = rlock_result(x, x->basic_type()); + + int log2_scale = 0; + if (x->has_index()) { + assert(x->index()->type()->tag() == intTag, "should not find non-int index"); + log2_scale = x->log2_scale(); + } + + assert(!x->has_index() || idx.value() == x->index(), "should match"); + + LIR_Opr base_op = base.result(); +#ifndef _LP64 + if (x->base()->type()->tag() == longTag) { + base_op = new_register(T_INT); + __ convert(Bytecodes::_l2i, base.result(), base_op); + } else { + assert(x->base()->type()->tag() == intTag, "must be"); + } +#endif + + BasicType dst_type = x->basic_type(); + LIR_Opr index_op = idx.result(); + + LIR_Address* addr; + if (index_op->is_constant()) { + assert(log2_scale == 0, "must not have a scale"); + addr = new LIR_Address(base_op, index_op->as_jint(), dst_type); + } else { +#ifdef IA32 + addr = new LIR_Address(base_op, index_op, LIR_Address::Scale(log2_scale), 0, dst_type); +#else + if (index_op->is_illegal() || log2_scale == 0) { + addr = new LIR_Address(base_op, index_op, dst_type); + } else { + LIR_Opr tmp = new_register(T_INT); + __ shift_left(index_op, log2_scale, tmp); + addr = new LIR_Address(base_op, tmp, dst_type); + } +#endif + } + + if (x->may_be_unaligned() && (dst_type == T_LONG || dst_type == T_DOUBLE)) { + __ unaligned_move(addr, reg); + } else { + __ move(addr, reg); + } +} + + +void LIRGenerator::do_UnsafePutRaw(UnsafePutRaw* x) { + int log2_scale = 0; + BasicType type = x->basic_type(); + + if (x->has_index()) { + assert(x->index()->type()->tag() == intTag, "should not find non-int index"); + log2_scale = x->log2_scale(); + } + + LIRItem base(x->base(), this); + LIRItem value(x->value(), this); + LIRItem idx(this); + + base.load_item(); + if (x->has_index()) { + idx.set_instruction(x->index()); + idx.load_item(); + } + + if (type == T_BYTE || type == T_BOOLEAN) { + value.load_byte_item(); + } else { + value.load_item(); + } + + set_no_result(x); + + LIR_Opr base_op = base.result(); +#ifndef _LP64 + if (x->base()->type()->tag() == longTag) { + base_op = new_register(T_INT); + __ convert(Bytecodes::_l2i, base.result(), base_op); + } else { + assert(x->base()->type()->tag() == intTag, "must be"); + } +#endif + + LIR_Opr index_op = idx.result(); + if (log2_scale != 0) { + // temporary fix (platform dependent code without shift on Intel would be better) + index_op = new_register(T_INT); + __ move(idx.result(), index_op); + __ shift_left(index_op, log2_scale, index_op); + } + + LIR_Address* addr = new LIR_Address(base_op, index_op, x->basic_type()); + __ move(value.result(), addr); +} + + +void LIRGenerator::do_UnsafeGetObject(UnsafeGetObject* x) { + BasicType type = x->basic_type(); + LIRItem src(x->object(), this); + LIRItem off(x->offset(), this); + + off.load_item(); + src.load_item(); + + LIR_Opr reg = reg = rlock_result(x, x->basic_type()); + + if (x->is_volatile() && os::is_MP()) __ membar_acquire(); + get_Object_unsafe(reg, src.result(), off.result(), type, x->is_volatile()); + if (x->is_volatile() && os::is_MP()) __ membar(); +} + + +void LIRGenerator::do_UnsafePutObject(UnsafePutObject* x) { + BasicType type = x->basic_type(); + LIRItem src(x->object(), this); + LIRItem off(x->offset(), this); + LIRItem data(x->value(), this); + + src.load_item(); + if (type == T_BOOLEAN || type == T_BYTE) { + data.load_byte_item(); + } else { + data.load_item(); + } + off.load_item(); + + set_no_result(x); + + if (x->is_volatile() && os::is_MP()) __ membar_release(); + put_Object_unsafe(src.result(), off.result(), data.result(), type, x->is_volatile()); +} + + +void LIRGenerator::do_UnsafePrefetch(UnsafePrefetch* x, bool is_store) { + LIRItem src(x->object(), this); + LIRItem off(x->offset(), this); + + src.load_item(); + if (off.is_constant() && can_inline_as_constant(x->offset())) { + // let it be a constant + off.dont_load_item(); + } else { + off.load_item(); + } + + set_no_result(x); + + LIR_Address* addr = generate_address(src.result(), off.result(), 0, 0, T_BYTE); + __ prefetch(addr, is_store); +} + + +void LIRGenerator::do_UnsafePrefetchRead(UnsafePrefetchRead* x) { + do_UnsafePrefetch(x, false); +} + + +void LIRGenerator::do_UnsafePrefetchWrite(UnsafePrefetchWrite* x) { + do_UnsafePrefetch(x, true); +} + + +void LIRGenerator::do_SwitchRanges(SwitchRangeArray* x, LIR_Opr value, BlockBegin* default_sux) { + int lng = x->length(); + + for (int i = 0; i < lng; i++) { + SwitchRange* one_range = x->at(i); + int low_key = one_range->low_key(); + int high_key = one_range->high_key(); + BlockBegin* dest = one_range->sux(); + if (low_key == high_key) { + __ cmp(lir_cond_equal, value, low_key); + __ branch(lir_cond_equal, T_INT, dest); + } else if (high_key - low_key == 1) { + __ cmp(lir_cond_equal, value, low_key); + __ branch(lir_cond_equal, T_INT, dest); + __ cmp(lir_cond_equal, value, high_key); + __ branch(lir_cond_equal, T_INT, dest); + } else { + LabelObj* L = new LabelObj(); + __ cmp(lir_cond_less, value, low_key); + __ branch(lir_cond_less, L->label()); + __ cmp(lir_cond_lessEqual, value, high_key); + __ branch(lir_cond_lessEqual, T_INT, dest); + __ branch_destination(L->label()); + } + } + __ jump(default_sux); +} + + +SwitchRangeArray* LIRGenerator::create_lookup_ranges(TableSwitch* x) { + SwitchRangeList* res = new SwitchRangeList(); + int len = x->length(); + if (len > 0) { + BlockBegin* sux = x->sux_at(0); + int key = x->lo_key(); + BlockBegin* default_sux = x->default_sux(); + SwitchRange* range = new SwitchRange(key, sux); + for (int i = 0; i < len; i++, key++) { + BlockBegin* new_sux = x->sux_at(i); + if (sux == new_sux) { + // still in same range + range->set_high_key(key); + } else { + // skip tests which explicitly dispatch to the default + if (sux != default_sux) { + res->append(range); + } + range = new SwitchRange(key, new_sux); + } + sux = new_sux; + } + if (res->length() == 0 || res->last() != range) res->append(range); + } + return res; +} + + +// we expect the keys to be sorted by increasing value +SwitchRangeArray* LIRGenerator::create_lookup_ranges(LookupSwitch* x) { + SwitchRangeList* res = new SwitchRangeList(); + int len = x->length(); + if (len > 0) { + BlockBegin* default_sux = x->default_sux(); + int key = x->key_at(0); + BlockBegin* sux = x->sux_at(0); + SwitchRange* range = new SwitchRange(key, sux); + for (int i = 1; i < len; i++) { + int new_key = x->key_at(i); + BlockBegin* new_sux = x->sux_at(i); + if (key+1 == new_key && sux == new_sux) { + // still in same range + range->set_high_key(new_key); + } else { + // skip tests which explicitly dispatch to the default + if (range->sux() != default_sux) { + res->append(range); + } + range = new SwitchRange(new_key, new_sux); + } + key = new_key; + sux = new_sux; + } + if (res->length() == 0 || res->last() != range) res->append(range); + } + return res; +} + + +void LIRGenerator::do_TableSwitch(TableSwitch* x) { + LIRItem tag(x->tag(), this); + tag.load_item(); + set_no_result(x); + + if (x->is_safepoint()) { + __ safepoint(safepoint_poll_register(), state_for(x, x->state_before())); + } + + // move values into phi locations + move_to_phi(x->state()); + + int lo_key = x->lo_key(); + int hi_key = x->hi_key(); + int len = x->length(); + CodeEmitInfo* info = state_for(x, x->state()); + LIR_Opr value = tag.result(); + if (UseTableRanges) { + do_SwitchRanges(create_lookup_ranges(x), value, x->default_sux()); + } else { + for (int i = 0; i < len; i++) { + __ cmp(lir_cond_equal, value, i + lo_key); + __ branch(lir_cond_equal, T_INT, x->sux_at(i)); + } + __ jump(x->default_sux()); + } +} + + +void LIRGenerator::do_LookupSwitch(LookupSwitch* x) { + LIRItem tag(x->tag(), this); + tag.load_item(); + set_no_result(x); + + if (x->is_safepoint()) { + __ safepoint(safepoint_poll_register(), state_for(x, x->state_before())); + } + + // move values into phi locations + move_to_phi(x->state()); + + LIR_Opr value = tag.result(); + if (UseTableRanges) { + do_SwitchRanges(create_lookup_ranges(x), value, x->default_sux()); + } else { + int len = x->length(); + for (int i = 0; i < len; i++) { + __ cmp(lir_cond_equal, value, x->key_at(i)); + __ branch(lir_cond_equal, T_INT, x->sux_at(i)); + } + __ jump(x->default_sux()); + } +} + + +void LIRGenerator::do_Goto(Goto* x) { + set_no_result(x); + + if (block()->next()->as_OsrEntry()) { + // need to free up storage used for OSR entry point + LIR_Opr osrBuffer = block()->next()->operand(); + BasicTypeList signature; + signature.append(T_INT); + CallingConvention* cc = frame_map()->c_calling_convention(&signature); + __ move(osrBuffer, cc->args()->at(0)); + __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_end), + getThreadTemp(), LIR_OprFact::illegalOpr, cc->args()); + } + + if (x->is_safepoint()) { + ValueStack* state = x->state_before() ? x->state_before() : x->state(); + + // increment backedge counter if needed + increment_backedge_counter(state_for(x, state)); + + CodeEmitInfo* safepoint_info = state_for(x, state); + __ safepoint(safepoint_poll_register(), safepoint_info); + } + + // emit phi-instruction move after safepoint since this simplifies + // describing the state as the safepoint. + move_to_phi(x->state()); + + __ jump(x->default_sux()); +} + + +void LIRGenerator::do_Base(Base* x) { + __ std_entry(LIR_OprFact::illegalOpr); + // Emit moves from physical registers / stack slots to virtual registers + CallingConvention* args = compilation()->frame_map()->incoming_arguments(); + IRScope* irScope = compilation()->hir()->top_scope(); + int java_index = 0; + for (int i = 0; i < args->length(); i++) { + LIR_Opr src = args->at(i); + assert(!src->is_illegal(), "check"); + BasicType t = src->type(); + + // Types which are smaller than int are passed as int, so + // correct the type which passed. + switch (t) { + case T_BYTE: + case T_BOOLEAN: + case T_SHORT: + case T_CHAR: + t = T_INT; + break; + } + + LIR_Opr dest = new_register(t); + __ move(src, dest); + + // Assign new location to Local instruction for this local + Local* local = x->state()->local_at(java_index)->as_Local(); + assert(local != NULL, "Locals for incoming arguments must have been created"); + assert(as_ValueType(t)->tag() == local->type()->tag(), "check"); + local->set_operand(dest); + _instruction_for_operand.at_put_grow(dest->vreg_number(), local, NULL); + java_index += type2size[t]; + } + + if (DTraceMethodProbes) { + BasicTypeList signature; + signature.append(T_INT); // thread + signature.append(T_OBJECT); // methodOop + LIR_OprList* args = new LIR_OprList(); + args->append(getThreadPointer()); + LIR_Opr meth = new_register(T_OBJECT); + __ oop2reg(method()->encoding(), meth); + args->append(meth); + call_runtime(&signature, args, CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), voidType, NULL); + } + + if (method()->is_synchronized()) { + LIR_Opr obj; + if (method()->is_static()) { + obj = new_register(T_OBJECT); + __ oop2reg(method()->holder()->java_mirror()->encoding(), obj); + } else { + Local* receiver = x->state()->local_at(0)->as_Local(); + assert(receiver != NULL, "must already exist"); + obj = receiver->operand(); + } + assert(obj->is_valid(), "must be valid"); + + if (method()->is_synchronized() && GenerateSynchronizationCode) { + LIR_Opr lock = new_register(T_INT); + __ load_stack_address_monitor(0, lock); + + CodeEmitInfo* info = new CodeEmitInfo(SynchronizationEntryBCI, scope()->start()->state(), NULL); + CodeStub* slow_path = new MonitorEnterStub(obj, lock, info); + + // receiver is guaranteed non-NULL so don't need CodeEmitInfo + __ lock_object(syncTempOpr(), obj, lock, new_register(T_OBJECT), slow_path, NULL); + } + } + + // increment invocation counters if needed + increment_invocation_counter(new CodeEmitInfo(0, scope()->start()->state(), NULL)); + + // all blocks with a successor must end with an unconditional jump + // to the successor even if they are consecutive + __ jump(x->default_sux()); +} + + +void LIRGenerator::do_OsrEntry(OsrEntry* x) { + // construct our frame and model the production of incoming pointer + // to the OSR buffer. + __ osr_entry(LIR_Assembler::osrBufferPointer()); + LIR_Opr result = rlock_result(x); + __ move(LIR_Assembler::osrBufferPointer(), result); +} + + +void LIRGenerator::invoke_load_arguments(Invoke* x, LIRItemList* args, const LIR_OprList* arg_list) { + int i = x->has_receiver() ? 1 : 0; + for (; i < args->length(); i++) { + LIRItem* param = args->at(i); + LIR_Opr loc = arg_list->at(i); + if (loc->is_register()) { + param->load_item_force(loc); + } else { + LIR_Address* addr = loc->as_address_ptr(); + param->load_for_store(addr->type()); + if (addr->type() == T_LONG || addr->type() == T_DOUBLE) { + __ unaligned_move(param->result(), addr); + } else { + __ move(param->result(), addr); + } + } + } + + if (x->has_receiver()) { + LIRItem* receiver = args->at(0); + LIR_Opr loc = arg_list->at(0); + if (loc->is_register()) { + receiver->load_item_force(loc); + } else { + assert(loc->is_address(), "just checking"); + receiver->load_for_store(T_OBJECT); + __ move(receiver->result(), loc); + } + } +} + + +// Visits all arguments, returns appropriate items without loading them +LIRItemList* LIRGenerator::invoke_visit_arguments(Invoke* x) { + LIRItemList* argument_items = new LIRItemList(); + if (x->has_receiver()) { + LIRItem* receiver = new LIRItem(x->receiver(), this); + argument_items->append(receiver); + } + int idx = x->has_receiver() ? 1 : 0; + for (int i = 0; i < x->number_of_arguments(); i++) { + LIRItem* param = new LIRItem(x->argument_at(i), this); + argument_items->append(param); + idx += (param->type()->is_double_word() ? 2 : 1); + } + return argument_items; +} + + +// The invoke with receiver has following phases: +// a) traverse and load/lock receiver; +// b) traverse all arguments -> item-array (invoke_visit_argument) +// c) push receiver on stack +// d) load each of the items and push on stack +// e) unlock receiver +// f) move receiver into receiver-register %o0 +// g) lock result registers and emit call operation +// +// Before issuing a call, we must spill-save all values on stack +// that are in caller-save register. "spill-save" moves thos registers +// either in a free callee-save register or spills them if no free +// callee save register is available. +// +// The problem is where to invoke spill-save. +// - if invoked between e) and f), we may lock callee save +// register in "spill-save" that destroys the receiver register +// before f) is executed +// - if we rearange the f) to be earlier, by loading %o0, it +// may destroy a value on the stack that is currently in %o0 +// and is waiting to be spilled +// - if we keep the receiver locked while doing spill-save, +// we cannot spill it as it is spill-locked +// +void LIRGenerator::do_Invoke(Invoke* x) { + CallingConvention* cc = frame_map()->java_calling_convention(x->signature(), true); + + LIR_OprList* arg_list = cc->args(); + LIRItemList* args = invoke_visit_arguments(x); + LIR_Opr receiver = LIR_OprFact::illegalOpr; + + // setup result register + LIR_Opr result_register = LIR_OprFact::illegalOpr; + if (x->type() != voidType) { + result_register = result_register_for(x->type()); + } + + CodeEmitInfo* info = state_for(x, x->state()); + + invoke_load_arguments(x, args, arg_list); + + if (x->has_receiver()) { + args->at(0)->load_item_force(LIR_Assembler::receiverOpr()); + receiver = args->at(0)->result(); + } + + // emit invoke code + bool optimized = x->target_is_loaded() && x->target_is_final(); + assert(receiver->is_illegal() || receiver->is_equal(LIR_Assembler::receiverOpr()), "must match"); + + switch (x->code()) { + case Bytecodes::_invokestatic: + __ call_static(x->target(), result_register, + SharedRuntime::get_resolve_static_call_stub(), + arg_list, info); + break; + case Bytecodes::_invokespecial: + case Bytecodes::_invokevirtual: + case Bytecodes::_invokeinterface: + // for final target we still produce an inline cache, in order + // to be able to call mixed mode + if (x->code() == Bytecodes::_invokespecial || optimized) { + __ call_opt_virtual(x->target(), receiver, result_register, + SharedRuntime::get_resolve_opt_virtual_call_stub(), + arg_list, info); + } else if (x->vtable_index() < 0) { + __ call_icvirtual(x->target(), receiver, result_register, + SharedRuntime::get_resolve_virtual_call_stub(), + arg_list, info); + } else { + int entry_offset = instanceKlass::vtable_start_offset() + x->vtable_index() * vtableEntry::size(); + int vtable_offset = entry_offset * wordSize + vtableEntry::method_offset_in_bytes(); + __ call_virtual(x->target(), receiver, result_register, vtable_offset, arg_list, info); + } + break; + default: + ShouldNotReachHere(); + break; + } + + if (x->type()->is_float() || x->type()->is_double()) { + // Force rounding of results from non-strictfp when in strictfp + // scope (or when we don't know the strictness of the callee, to + // be safe.) + if (method()->is_strict()) { + if (!x->target_is_loaded() || !x->target_is_strictfp()) { + result_register = round_item(result_register); + } + } + } + + if (result_register->is_valid()) { + LIR_Opr result = rlock_result(x); + __ move(result_register, result); + } +} + + +void LIRGenerator::do_FPIntrinsics(Intrinsic* x) { + assert(x->number_of_arguments() == 1, "wrong type"); + LIRItem value (x->argument_at(0), this); + LIR_Opr reg = rlock_result(x); + value.load_item(); + LIR_Opr tmp = force_to_spill(value.result(), as_BasicType(x->type())); + __ move(tmp, reg); +} + + + +// Code for : x->x() {x->cond()} x->y() ? x->tval() : x->fval() +void LIRGenerator::do_IfOp(IfOp* x) { +#ifdef ASSERT + { + ValueTag xtag = x->x()->type()->tag(); + ValueTag ttag = x->tval()->type()->tag(); + assert(xtag == intTag || xtag == objectTag, "cannot handle others"); + assert(ttag == addressTag || ttag == intTag || ttag == objectTag || ttag == longTag, "cannot handle others"); + assert(ttag == x->fval()->type()->tag(), "cannot handle others"); + } +#endif + + LIRItem left(x->x(), this); + LIRItem right(x->y(), this); + left.load_item(); + if (can_inline_as_constant(right.value())) { + right.dont_load_item(); + } else { + right.load_item(); + } + + LIRItem t_val(x->tval(), this); + LIRItem f_val(x->fval(), this); + t_val.dont_load_item(); + f_val.dont_load_item(); + LIR_Opr reg = rlock_result(x); + + __ cmp(lir_cond(x->cond()), left.result(), right.result()); + __ cmove(lir_cond(x->cond()), t_val.result(), f_val.result(), reg); +} + + +void LIRGenerator::do_Intrinsic(Intrinsic* x) { + switch (x->id()) { + case vmIntrinsics::_intBitsToFloat : + case vmIntrinsics::_doubleToRawLongBits : + case vmIntrinsics::_longBitsToDouble : + case vmIntrinsics::_floatToRawIntBits : { + do_FPIntrinsics(x); + break; + } + + case vmIntrinsics::_currentTimeMillis: { + assert(x->number_of_arguments() == 0, "wrong type"); + LIR_Opr reg = result_register_for(x->type()); + __ call_runtime_leaf(CAST_FROM_FN_PTR(address, os::javaTimeMillis), getThreadTemp(), + reg, new LIR_OprList()); + LIR_Opr result = rlock_result(x); + __ move(reg, result); + break; + } + + case vmIntrinsics::_nanoTime: { + assert(x->number_of_arguments() == 0, "wrong type"); + LIR_Opr reg = result_register_for(x->type()); + __ call_runtime_leaf(CAST_FROM_FN_PTR(address, os::javaTimeNanos), getThreadTemp(), + reg, new LIR_OprList()); + LIR_Opr result = rlock_result(x); + __ move(reg, result); + break; + } + + case vmIntrinsics::_Object_init: do_RegisterFinalizer(x); break; + case vmIntrinsics::_getClass: do_getClass(x); break; + case vmIntrinsics::_currentThread: do_currentThread(x); break; + + case vmIntrinsics::_dlog: // fall through + case vmIntrinsics::_dlog10: // fall through + case vmIntrinsics::_dabs: // fall through + case vmIntrinsics::_dsqrt: // fall through + case vmIntrinsics::_dtan: // fall through + case vmIntrinsics::_dsin : // fall through + case vmIntrinsics::_dcos : do_MathIntrinsic(x); break; + case vmIntrinsics::_arraycopy: do_ArrayCopy(x); break; + + // java.nio.Buffer.checkIndex + case vmIntrinsics::_checkIndex: do_NIOCheckIndex(x); break; + + case vmIntrinsics::_compareAndSwapObject: + do_CompareAndSwap(x, objectType); + break; + case vmIntrinsics::_compareAndSwapInt: + do_CompareAndSwap(x, intType); + break; + case vmIntrinsics::_compareAndSwapLong: + do_CompareAndSwap(x, longType); + break; + + // sun.misc.AtomicLongCSImpl.attemptUpdate + case vmIntrinsics::_attemptUpdate: + do_AttemptUpdate(x); + break; + + default: ShouldNotReachHere(); break; + } +} + + +void LIRGenerator::do_ProfileCall(ProfileCall* x) { + // Need recv in a temporary register so it interferes with the other temporaries + LIR_Opr recv = LIR_OprFact::illegalOpr; + LIR_Opr mdo = new_register(T_OBJECT); + LIR_Opr tmp = new_register(T_INT); + if (x->recv() != NULL) { + LIRItem value(x->recv(), this); + value.load_item(); + recv = new_register(T_OBJECT); + __ move(value.result(), recv); + } + __ profile_call(x->method(), x->bci_of_invoke(), mdo, recv, tmp, x->known_holder()); +} + + +void LIRGenerator::do_ProfileCounter(ProfileCounter* x) { + LIRItem mdo(x->mdo(), this); + mdo.load_item(); + + increment_counter(new LIR_Address(mdo.result(), x->offset(), T_INT), x->increment()); +} + + +LIR_Opr LIRGenerator::call_runtime(Value arg1, address entry, ValueType* result_type, CodeEmitInfo* info) { + LIRItemList args(1); + LIRItem value(arg1, this); + args.append(&value); + BasicTypeList signature; + signature.append(as_BasicType(arg1->type())); + + return call_runtime(&signature, &args, entry, result_type, info); +} + + +LIR_Opr LIRGenerator::call_runtime(Value arg1, Value arg2, address entry, ValueType* result_type, CodeEmitInfo* info) { + LIRItemList args(2); + LIRItem value1(arg1, this); + LIRItem value2(arg2, this); + args.append(&value1); + args.append(&value2); + BasicTypeList signature; + signature.append(as_BasicType(arg1->type())); + signature.append(as_BasicType(arg2->type())); + + return call_runtime(&signature, &args, entry, result_type, info); +} + + +LIR_Opr LIRGenerator::call_runtime(BasicTypeArray* signature, LIR_OprList* args, + address entry, ValueType* result_type, CodeEmitInfo* info) { + // get a result register + LIR_Opr phys_reg = LIR_OprFact::illegalOpr; + LIR_Opr result = LIR_OprFact::illegalOpr; + if (result_type->tag() != voidTag) { + result = new_register(result_type); + phys_reg = result_register_for(result_type); + } + + // move the arguments into the correct location + CallingConvention* cc = frame_map()->c_calling_convention(signature); + assert(cc->length() == args->length(), "argument mismatch"); + for (int i = 0; i < args->length(); i++) { + LIR_Opr arg = args->at(i); + LIR_Opr loc = cc->at(i); + if (loc->is_register()) { + __ move(arg, loc); + } else { + LIR_Address* addr = loc->as_address_ptr(); +// if (!can_store_as_constant(arg)) { +// LIR_Opr tmp = new_register(arg->type()); +// __ move(arg, tmp); +// arg = tmp; +// } + if (addr->type() == T_LONG || addr->type() == T_DOUBLE) { + __ unaligned_move(arg, addr); + } else { + __ move(arg, addr); + } + } + } + + if (info) { + __ call_runtime(entry, getThreadTemp(), phys_reg, cc->args(), info); + } else { + __ call_runtime_leaf(entry, getThreadTemp(), phys_reg, cc->args()); + } + if (result->is_valid()) { + __ move(phys_reg, result); + } + return result; +} + + +LIR_Opr LIRGenerator::call_runtime(BasicTypeArray* signature, LIRItemList* args, + address entry, ValueType* result_type, CodeEmitInfo* info) { + // get a result register + LIR_Opr phys_reg = LIR_OprFact::illegalOpr; + LIR_Opr result = LIR_OprFact::illegalOpr; + if (result_type->tag() != voidTag) { + result = new_register(result_type); + phys_reg = result_register_for(result_type); + } + + // move the arguments into the correct location + CallingConvention* cc = frame_map()->c_calling_convention(signature); + + assert(cc->length() == args->length(), "argument mismatch"); + for (int i = 0; i < args->length(); i++) { + LIRItem* arg = args->at(i); + LIR_Opr loc = cc->at(i); + if (loc->is_register()) { + arg->load_item_force(loc); + } else { + LIR_Address* addr = loc->as_address_ptr(); + arg->load_for_store(addr->type()); + if (addr->type() == T_LONG || addr->type() == T_DOUBLE) { + __ unaligned_move(arg->result(), addr); + } else { + __ move(arg->result(), addr); + } + } + } + + if (info) { + __ call_runtime(entry, getThreadTemp(), phys_reg, cc->args(), info); + } else { + __ call_runtime_leaf(entry, getThreadTemp(), phys_reg, cc->args()); + } + if (result->is_valid()) { + __ move(phys_reg, result); + } + return result; +} + + + +void LIRGenerator::increment_invocation_counter(CodeEmitInfo* info, bool backedge) { +#ifdef TIERED + if (_compilation->env()->comp_level() == CompLevel_fast_compile && + (method()->code_size() >= Tier1BytecodeLimit || backedge)) { + int limit = InvocationCounter::Tier1InvocationLimit; + int offset = in_bytes(methodOopDesc::invocation_counter_offset() + + InvocationCounter::counter_offset()); + if (backedge) { + limit = InvocationCounter::Tier1BackEdgeLimit; + offset = in_bytes(methodOopDesc::backedge_counter_offset() + + InvocationCounter::counter_offset()); + } + + LIR_Opr meth = new_register(T_OBJECT); + __ oop2reg(method()->encoding(), meth); + LIR_Opr result = increment_and_return_counter(meth, offset, InvocationCounter::count_increment); + __ cmp(lir_cond_aboveEqual, result, LIR_OprFact::intConst(limit)); + CodeStub* overflow = new CounterOverflowStub(info, info->bci()); + __ branch(lir_cond_aboveEqual, T_INT, overflow); + __ branch_destination(overflow->continuation()); + } +#endif +} diff --git a/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp b/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp new file mode 100644 index 00000000000..1b70887883f --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp @@ -0,0 +1,577 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The classes responsible for code emission and register allocation + + +class LIRGenerator; +class LIREmitter; +class Invoke; +class SwitchRange; +class LIRItem; + +define_array(LIRItemArray, LIRItem*) +define_stack(LIRItemList, LIRItemArray) + +class SwitchRange: public CompilationResourceObj { + private: + int _low_key; + int _high_key; + BlockBegin* _sux; + public: + SwitchRange(int start_key, BlockBegin* sux): _low_key(start_key), _high_key(start_key), _sux(sux) {} + void set_high_key(int key) { _high_key = key; } + + int high_key() const { return _high_key; } + int low_key() const { return _low_key; } + BlockBegin* sux() const { return _sux; } +}; + +define_array(SwitchRangeArray, SwitchRange*) +define_stack(SwitchRangeList, SwitchRangeArray) + + +class ResolveNode; + +define_array(NodeArray, ResolveNode*); +define_stack(NodeList, NodeArray); + + +// Node objects form a directed graph of LIR_Opr +// Edges between Nodes represent moves from one Node to its destinations +class ResolveNode: public CompilationResourceObj { + private: + LIR_Opr _operand; // the source or destinaton + NodeList _destinations; // for the operand + bool _assigned; // Value assigned to this Node? + bool _visited; // Node already visited? + bool _start_node; // Start node already visited? + + public: + ResolveNode(LIR_Opr operand) + : _operand(operand) + , _assigned(false) + , _visited(false) + , _start_node(false) {}; + + // accessors + LIR_Opr operand() const { return _operand; } + int no_of_destinations() const { return _destinations.length(); } + ResolveNode* destination_at(int i) { return _destinations[i]; } + bool assigned() const { return _assigned; } + bool visited() const { return _visited; } + bool start_node() const { return _start_node; } + + // modifiers + void append(ResolveNode* dest) { _destinations.append(dest); } + void set_assigned() { _assigned = true; } + void set_visited() { _visited = true; } + void set_start_node() { _start_node = true; } +}; + + +// This is shared state to be used by the PhiResolver so the operand +// arrays don't have to be reallocated for reach resolution. +class PhiResolverState: public CompilationResourceObj { + friend class PhiResolver; + + private: + NodeList _virtual_operands; // Nodes where the operand is a virtual register + NodeList _other_operands; // Nodes where the operand is not a virtual register + NodeList _vreg_table; // Mapping from virtual register to Node + + public: + PhiResolverState() {} + + void reset(int max_vregs); +}; + + +// class used to move value of phi operand to phi function +class PhiResolver: public CompilationResourceObj { + private: + LIRGenerator* _gen; + PhiResolverState& _state; // temporary state cached by LIRGenerator + + ResolveNode* _loop; + LIR_Opr _temp; + + // access to shared state arrays + NodeList& virtual_operands() { return _state._virtual_operands; } + NodeList& other_operands() { return _state._other_operands; } + NodeList& vreg_table() { return _state._vreg_table; } + + ResolveNode* create_node(LIR_Opr opr, bool source); + ResolveNode* source_node(LIR_Opr opr) { return create_node(opr, true); } + ResolveNode* destination_node(LIR_Opr opr) { return create_node(opr, false); } + + void emit_move(LIR_Opr src, LIR_Opr dest); + void move_to_temp(LIR_Opr src); + void move_temp_to(LIR_Opr dest); + void move(ResolveNode* src, ResolveNode* dest); + + LIRGenerator* gen() { + return _gen; + } + + public: + PhiResolver(LIRGenerator* _lir_gen, int max_vregs); + ~PhiResolver(); + + void move(LIR_Opr src, LIR_Opr dest); +}; + + +// only the classes below belong in the same file +class LIRGenerator: public InstructionVisitor, public BlockClosure { + private: + Compilation* _compilation; + ciMethod* _method; // method that we are compiling + PhiResolverState _resolver_state; + BlockBegin* _block; + int _virtual_register_number; + Values _instruction_for_operand; + BitMap2D _vreg_flags; // flags which can be set on a per-vreg basis + LIR_List* _lir; + + LIRGenerator* gen() { + return this; + } + +#ifdef ASSERT + LIR_List* lir(const char * file, int line) const { + _lir->set_file_and_line(file, line); + return _lir; + } +#endif + LIR_List* lir() const { + return _lir; + } + + // a simple cache of constants used within a block + GrowableArray _constants; + LIR_OprList _reg_for_constants; + Values _unpinned_constants; + + LIR_Const* _card_table_base; + + friend class PhiResolver; + + // unified bailout support + void bailout(const char* msg) const { compilation()->bailout(msg); } + bool bailed_out() const { return compilation()->bailed_out(); } + + void block_do_prolog(BlockBegin* block); + void block_do_epilog(BlockBegin* block); + + // register allocation + LIR_Opr rlock(Value instr); // lock a free register + LIR_Opr rlock_result(Value instr); + LIR_Opr rlock_result(Value instr, BasicType type); + LIR_Opr rlock_byte(BasicType type); + LIR_Opr rlock_callee_saved(BasicType type); + + // get a constant into a register and get track of what register was used + LIR_Opr load_constant(Constant* x); + LIR_Opr load_constant(LIR_Const* constant); + + LIR_Const* card_table_base() const { return _card_table_base; } + + void set_result(Value x, LIR_Opr opr) { + assert(opr->is_valid(), "must set to valid value"); + assert(x->operand()->is_illegal(), "operand should never change"); + assert(!opr->is_register() || opr->is_virtual(), "should never set result to a physical register"); + x->set_operand(opr); + assert(opr == x->operand(), "must be"); + if (opr->is_virtual()) { + _instruction_for_operand.at_put_grow(opr->vreg_number(), x, NULL); + } + } + void set_no_result(Value x) { assert(!x->has_uses(), "can't have use"); x->clear_operand(); } + + friend class LIRItem; + + LIR_Opr round_item(LIR_Opr opr); + LIR_Opr force_to_spill(LIR_Opr value, BasicType t); + + void profile_branch(If* if_instr, If::Condition cond); + + PhiResolverState& resolver_state() { return _resolver_state; } + + void move_to_phi(PhiResolver* resolver, Value cur_val, Value sux_val); + void move_to_phi(ValueStack* cur_state); + + // code emission + void do_ArithmeticOp_Long (ArithmeticOp* x); + void do_ArithmeticOp_Int (ArithmeticOp* x); + void do_ArithmeticOp_FPU (ArithmeticOp* x); + + // platform dependent + LIR_Opr getThreadPointer(); + + void do_RegisterFinalizer(Intrinsic* x); + void do_getClass(Intrinsic* x); + void do_currentThread(Intrinsic* x); + void do_MathIntrinsic(Intrinsic* x); + void do_ArrayCopy(Intrinsic* x); + void do_CompareAndSwap(Intrinsic* x, ValueType* type); + void do_AttemptUpdate(Intrinsic* x); + void do_NIOCheckIndex(Intrinsic* x); + void do_FPIntrinsics(Intrinsic* x); + + void do_UnsafePrefetch(UnsafePrefetch* x, bool is_store); + + LIR_Opr call_runtime(BasicTypeArray* signature, LIRItemList* args, address entry, ValueType* result_type, CodeEmitInfo* info); + LIR_Opr call_runtime(BasicTypeArray* signature, LIR_OprList* args, address entry, ValueType* result_type, CodeEmitInfo* info); + + // convenience functions + LIR_Opr call_runtime(Value arg1, address entry, ValueType* result_type, CodeEmitInfo* info); + LIR_Opr call_runtime(Value arg1, Value arg2, address entry, ValueType* result_type, CodeEmitInfo* info); + + // GC Barriers + + // generic interface + + void post_barrier(LIR_OprDesc* addr, LIR_OprDesc* new_val); + + // specific implementations + + // post barriers + + void CardTableModRef_post_barrier(LIR_OprDesc* addr, LIR_OprDesc* new_val); + + + static LIR_Opr result_register_for(ValueType* type, bool callee = false); + + ciObject* get_jobject_constant(Value value); + + LIRItemList* invoke_visit_arguments(Invoke* x); + void invoke_load_arguments(Invoke* x, LIRItemList* args, const LIR_OprList* arg_list); + + void trace_block_entry(BlockBegin* block); + + // volatile field operations are never patchable because a klass + // must be loaded to know it's volatile which means that the offset + // it always known as well. + void volatile_field_store(LIR_Opr value, LIR_Address* address, CodeEmitInfo* info); + void volatile_field_load(LIR_Address* address, LIR_Opr result, CodeEmitInfo* info); + + void put_Object_unsafe(LIR_Opr src, LIR_Opr offset, LIR_Opr data, BasicType type, bool is_volatile); + void get_Object_unsafe(LIR_Opr dest, LIR_Opr src, LIR_Opr offset, BasicType type, bool is_volatile); + + void arithmetic_call_op (Bytecodes::Code code, LIR_Opr result, LIR_OprList* args); + + void increment_counter(address counter, int step = 1); + void increment_counter(LIR_Address* addr, int step = 1); + + // increment a counter returning the incremented value + LIR_Opr increment_and_return_counter(LIR_Opr base, int offset, int increment); + + // is_strictfp is only needed for mul and div (and only generates different code on i486) + void arithmetic_op(Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, bool is_strictfp, LIR_Opr tmp, CodeEmitInfo* info = NULL); + // machine dependent. returns true if it emitted code for the multiply + bool strength_reduce_multiply(LIR_Opr left, int constant, LIR_Opr result, LIR_Opr tmp); + + void store_stack_parameter (LIR_Opr opr, ByteSize offset_from_sp_in_bytes); + + void jobject2reg_with_patching(LIR_Opr r, ciObject* obj, CodeEmitInfo* info); + + // this loads the length and compares against the index + void array_range_check (LIR_Opr array, LIR_Opr index, CodeEmitInfo* null_check_info, CodeEmitInfo* range_check_info); + // For java.nio.Buffer.checkIndex + void nio_range_check (LIR_Opr buffer, LIR_Opr index, LIR_Opr result, CodeEmitInfo* info); + + void arithmetic_op_int (Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, LIR_Opr tmp); + void arithmetic_op_long (Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, CodeEmitInfo* info = NULL); + void arithmetic_op_fpu (Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, bool is_strictfp, LIR_Opr tmp = LIR_OprFact::illegalOpr); + + void shift_op (Bytecodes::Code code, LIR_Opr dst_reg, LIR_Opr value, LIR_Opr count, LIR_Opr tmp); + + void logic_op (Bytecodes::Code code, LIR_Opr dst_reg, LIR_Opr left, LIR_Opr right); + + void monitor_enter (LIR_Opr object, LIR_Opr lock, LIR_Opr hdr, LIR_Opr scratch, int monitor_no, CodeEmitInfo* info_for_exception, CodeEmitInfo* info); + void monitor_exit (LIR_Opr object, LIR_Opr lock, LIR_Opr hdr, int monitor_no); + + void new_instance (LIR_Opr dst, ciInstanceKlass* klass, LIR_Opr scratch1, LIR_Opr scratch2, LIR_Opr scratch3, LIR_Opr scratch4, LIR_Opr klass_reg, CodeEmitInfo* info); + + // machine dependent + void cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int c, CodeEmitInfo* info); + void cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, int disp, BasicType type, CodeEmitInfo* info); + void cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, LIR_Opr disp, BasicType type, CodeEmitInfo* info); + + void arraycopy_helper(Intrinsic* x, int* flags, ciArrayKlass** expected_type); + + // returns a LIR_Address to address an array location. May also + // emit some code as part of address calculation. If + // needs_card_mark is true then compute the full address for use by + // both the store and the card mark. + LIR_Address* generate_address(LIR_Opr base, + LIR_Opr index, int shift, + int disp, + BasicType type); + LIR_Address* generate_address(LIR_Opr base, int disp, BasicType type) { + return generate_address(base, LIR_OprFact::illegalOpr, 0, disp, type); + } + LIR_Address* emit_array_address(LIR_Opr array_opr, LIR_Opr index_opr, BasicType type, bool needs_card_mark); + + // machine preferences and characteristics + bool can_inline_as_constant(Value i) const; + bool can_inline_as_constant(LIR_Const* c) const; + bool can_store_as_constant(Value i, BasicType type) const; + + LIR_Opr safepoint_poll_register(); + void increment_invocation_counter(CodeEmitInfo* info, bool backedge = false); + void increment_backedge_counter(CodeEmitInfo* info) { + increment_invocation_counter(info, true); + } + + CodeEmitInfo* state_for(Instruction* x, ValueStack* state, bool ignore_xhandler = false); + CodeEmitInfo* state_for(Instruction* x); + + // allocates a virtual register for this instruction if + // one isn't already allocated. Only for Phi and Local. + LIR_Opr operand_for_instruction(Instruction *x); + + void set_block(BlockBegin* block) { _block = block; } + + void block_prolog(BlockBegin* block); + void block_epilog(BlockBegin* block); + + void do_root (Instruction* instr); + void walk (Instruction* instr); + + void bind_block_entry(BlockBegin* block); + void start_block(BlockBegin* block); + + LIR_Opr new_register(BasicType type); + LIR_Opr new_register(Value value) { return new_register(as_BasicType(value->type())); } + LIR_Opr new_register(ValueType* type) { return new_register(as_BasicType(type)); } + + // returns a register suitable for doing pointer math + LIR_Opr new_pointer_register() { +#ifdef _LP64 + return new_register(T_LONG); +#else + return new_register(T_INT); +#endif + } + + static LIR_Condition lir_cond(If::Condition cond) { + LIR_Condition l; + switch (cond) { + case If::eql: l = lir_cond_equal; break; + case If::neq: l = lir_cond_notEqual; break; + case If::lss: l = lir_cond_less; break; + case If::leq: l = lir_cond_lessEqual; break; + case If::geq: l = lir_cond_greaterEqual; break; + case If::gtr: l = lir_cond_greater; break; + }; + return l; + } + + void init(); + + SwitchRangeArray* create_lookup_ranges(TableSwitch* x); + SwitchRangeArray* create_lookup_ranges(LookupSwitch* x); + void do_SwitchRanges(SwitchRangeArray* x, LIR_Opr value, BlockBegin* default_sux); + + public: + Compilation* compilation() const { return _compilation; } + FrameMap* frame_map() const { return _compilation->frame_map(); } + ciMethod* method() const { return _method; } + BlockBegin* block() const { return _block; } + IRScope* scope() const { return block()->scope(); } + + int max_virtual_register_number() const { return _virtual_register_number; } + + void block_do(BlockBegin* block); + + // Flags that can be set on vregs + enum VregFlag { + must_start_in_memory = 0 // needs to be assigned a memory location at beginning, but may then be loaded in a register + , callee_saved = 1 // must be in a callee saved register + , byte_reg = 2 // must be in a byte register + , num_vreg_flags + + }; + + LIRGenerator(Compilation* compilation, ciMethod* method) + : _compilation(compilation) + , _method(method) + , _virtual_register_number(LIR_OprDesc::vreg_base) + , _vreg_flags(NULL, 0, num_vreg_flags) { + init(); + } + + // for virtual registers, maps them back to Phi's or Local's + Instruction* instruction_for_opr(LIR_Opr opr); + Instruction* instruction_for_vreg(int reg_num); + + void set_vreg_flag (int vreg_num, VregFlag f); + bool is_vreg_flag_set(int vreg_num, VregFlag f); + void set_vreg_flag (LIR_Opr opr, VregFlag f) { set_vreg_flag(opr->vreg_number(), f); } + bool is_vreg_flag_set(LIR_Opr opr, VregFlag f) { return is_vreg_flag_set(opr->vreg_number(), f); } + + // statics + static LIR_Opr exceptionOopOpr(); + static LIR_Opr exceptionPcOpr(); + static LIR_Opr divInOpr(); + static LIR_Opr divOutOpr(); + static LIR_Opr remOutOpr(); + static LIR_Opr shiftCountOpr(); + LIR_Opr syncTempOpr(); + + // returns a register suitable for saving the thread in a + // call_runtime_leaf if one is needed. + LIR_Opr getThreadTemp(); + + // visitor functionality + virtual void do_Phi (Phi* x); + virtual void do_Local (Local* x); + virtual void do_Constant (Constant* x); + virtual void do_LoadField (LoadField* x); + virtual void do_StoreField (StoreField* x); + virtual void do_ArrayLength (ArrayLength* x); + virtual void do_LoadIndexed (LoadIndexed* x); + virtual void do_StoreIndexed (StoreIndexed* x); + virtual void do_NegateOp (NegateOp* x); + virtual void do_ArithmeticOp (ArithmeticOp* x); + virtual void do_ShiftOp (ShiftOp* x); + virtual void do_LogicOp (LogicOp* x); + virtual void do_CompareOp (CompareOp* x); + virtual void do_IfOp (IfOp* x); + virtual void do_Convert (Convert* x); + virtual void do_NullCheck (NullCheck* x); + virtual void do_Invoke (Invoke* x); + virtual void do_NewInstance (NewInstance* x); + virtual void do_NewTypeArray (NewTypeArray* x); + virtual void do_NewObjectArray (NewObjectArray* x); + virtual void do_NewMultiArray (NewMultiArray* x); + virtual void do_CheckCast (CheckCast* x); + virtual void do_InstanceOf (InstanceOf* x); + virtual void do_MonitorEnter (MonitorEnter* x); + virtual void do_MonitorExit (MonitorExit* x); + virtual void do_Intrinsic (Intrinsic* x); + virtual void do_BlockBegin (BlockBegin* x); + virtual void do_Goto (Goto* x); + virtual void do_If (If* x); + virtual void do_IfInstanceOf (IfInstanceOf* x); + virtual void do_TableSwitch (TableSwitch* x); + virtual void do_LookupSwitch (LookupSwitch* x); + virtual void do_Return (Return* x); + virtual void do_Throw (Throw* x); + virtual void do_Base (Base* x); + virtual void do_OsrEntry (OsrEntry* x); + virtual void do_ExceptionObject(ExceptionObject* x); + virtual void do_RoundFP (RoundFP* x); + virtual void do_UnsafeGetRaw (UnsafeGetRaw* x); + virtual void do_UnsafePutRaw (UnsafePutRaw* x); + virtual void do_UnsafeGetObject(UnsafeGetObject* x); + virtual void do_UnsafePutObject(UnsafePutObject* x); + virtual void do_UnsafePrefetchRead (UnsafePrefetchRead* x); + virtual void do_UnsafePrefetchWrite(UnsafePrefetchWrite* x); + virtual void do_ProfileCall (ProfileCall* x); + virtual void do_ProfileCounter (ProfileCounter* x); +}; + + +class LIRItem: public CompilationResourceObj { + private: + Value _value; + LIRGenerator* _gen; + LIR_Opr _result; + bool _destroys_register; + LIR_Opr _new_result; + + LIRGenerator* gen() const { return _gen; } + + public: + LIRItem(Value value, LIRGenerator* gen) { + _destroys_register = false; + _gen = gen; + set_instruction(value); + } + + LIRItem(LIRGenerator* gen) { + _destroys_register = false; + _gen = gen; + _result = LIR_OprFact::illegalOpr; + set_instruction(NULL); + } + + void set_instruction(Value value) { + _value = value; + _result = LIR_OprFact::illegalOpr; + if (_value != NULL) { + _gen->walk(_value); + _result = _value->operand(); + } + _new_result = LIR_OprFact::illegalOpr; + } + + Value value() const { return _value; } + ValueType* type() const { return value()->type(); } + LIR_Opr result() { + assert(!_destroys_register || (!_result->is_register() || _result->is_virtual()), + "shouldn't use set_destroys_register with physical regsiters"); + if (_destroys_register && _result->is_register()) { + if (_new_result->is_illegal()) { + _new_result = _gen->new_register(type()); + gen()->lir()->move(_result, _new_result); + } + return _new_result; + } else { + return _result; + } + return _result; + } + + void set_result(LIR_Opr opr); + + void load_item(); + void load_byte_item(); + void load_nonconstant(); + // load any values which can't be expressed as part of a single store instruction + void load_for_store(BasicType store_type); + void load_item_force(LIR_Opr reg); + + void dont_load_item() { + // do nothing + } + + void set_destroys_register() { + _destroys_register = true; + } + + bool is_constant() const { return value()->as_Constant() != NULL; } + bool is_stack() { return result()->is_stack(); } + bool is_register() { return result()->is_register(); } + + ciObject* get_jobject_constant() const; + jint get_jint_constant() const; + jlong get_jlong_constant() const; + jfloat get_jfloat_constant() const; + jdouble get_jdouble_constant() const; + jint get_address_constant() const; +}; diff --git a/hotspot/src/share/vm/c1/c1_LinearScan.cpp b/hotspot/src/share/vm/c1/c1_LinearScan.cpp new file mode 100644 index 00000000000..0d733b7e660 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_LinearScan.cpp @@ -0,0 +1,6530 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_LinearScan.cpp.incl" + + +#ifndef PRODUCT + + static LinearScanStatistic _stat_before_alloc; + static LinearScanStatistic _stat_after_asign; + static LinearScanStatistic _stat_final; + + static LinearScanTimers _total_timer; + + // helper macro for short definition of timer + #define TIME_LINEAR_SCAN(timer_name) TraceTime _block_timer("", _total_timer.timer(LinearScanTimers::timer_name), TimeLinearScan || TimeEachLinearScan, Verbose); + + // helper macro for short definition of trace-output inside code + #define TRACE_LINEAR_SCAN(level, code) \ + if (TraceLinearScanLevel >= level) { \ + code; \ + } + +#else + + #define TIME_LINEAR_SCAN(timer_name) + #define TRACE_LINEAR_SCAN(level, code) + +#endif + +// Map BasicType to spill size in 32-bit words, matching VMReg's notion of words +#ifdef _LP64 +static int type2spill_size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 0, 1, -1}; +#else +static int type2spill_size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, -1}; +#endif + + +// Implementation of LinearScan + +LinearScan::LinearScan(IR* ir, LIRGenerator* gen, FrameMap* frame_map) + : _compilation(ir->compilation()) + , _ir(ir) + , _gen(gen) + , _frame_map(frame_map) + , _num_virtual_regs(gen->max_virtual_register_number()) + , _has_fpu_registers(false) + , _num_calls(-1) + , _max_spills(0) + , _unused_spill_slot(-1) + , _intervals(0) // initialized later with correct length + , _new_intervals_from_allocation(new IntervalList()) + , _sorted_intervals(NULL) + , _lir_ops(0) // initialized later with correct length + , _block_of_op(0) // initialized later with correct length + , _has_info(0) + , _has_call(0) + , _scope_value_cache(0) // initialized later with correct length + , _interval_in_loop(0, 0) // initialized later with correct length + , _cached_blocks(*ir->linear_scan_order()) +#ifdef IA32 + , _fpu_stack_allocator(NULL) +#endif +{ + // note: to use more than on instance of LinearScan at a time this function call has to + // be moved somewhere outside of this constructor: + Interval::initialize(); + + assert(this->ir() != NULL, "check if valid"); + assert(this->compilation() != NULL, "check if valid"); + assert(this->gen() != NULL, "check if valid"); + assert(this->frame_map() != NULL, "check if valid"); +} + + +// ********** functions for converting LIR-Operands to register numbers +// +// Emulate a flat register file comprising physical integer registers, +// physical floating-point registers and virtual registers, in that order. +// Virtual registers already have appropriate numbers, since V0 is +// the number of physical registers. +// Returns -1 for hi word if opr is a single word operand. +// +// Note: the inverse operation (calculating an operand for register numbers) +// is done in calc_operand_for_interval() + +int LinearScan::reg_num(LIR_Opr opr) { + assert(opr->is_register(), "should not call this otherwise"); + + if (opr->is_virtual_register()) { + assert(opr->vreg_number() >= nof_regs, "found a virtual register with a fixed-register number"); + return opr->vreg_number(); + } else if (opr->is_single_cpu()) { + return opr->cpu_regnr(); + } else if (opr->is_double_cpu()) { + return opr->cpu_regnrLo(); +#ifdef IA32 + } else if (opr->is_single_xmm()) { + return opr->fpu_regnr() + pd_first_xmm_reg; + } else if (opr->is_double_xmm()) { + return opr->fpu_regnrLo() + pd_first_xmm_reg; +#endif + } else if (opr->is_single_fpu()) { + return opr->fpu_regnr() + pd_first_fpu_reg; + } else if (opr->is_double_fpu()) { + return opr->fpu_regnrLo() + pd_first_fpu_reg; + } else { + ShouldNotReachHere(); + } +} + +int LinearScan::reg_numHi(LIR_Opr opr) { + assert(opr->is_register(), "should not call this otherwise"); + + if (opr->is_virtual_register()) { + return -1; + } else if (opr->is_single_cpu()) { + return -1; + } else if (opr->is_double_cpu()) { + return opr->cpu_regnrHi(); +#ifdef IA32 + } else if (opr->is_single_xmm()) { + return -1; + } else if (opr->is_double_xmm()) { + return -1; +#endif + } else if (opr->is_single_fpu()) { + return -1; + } else if (opr->is_double_fpu()) { + return opr->fpu_regnrHi() + pd_first_fpu_reg; + } else { + ShouldNotReachHere(); + } +} + + +// ********** functions for classification of intervals + +bool LinearScan::is_precolored_interval(const Interval* i) { + return i->reg_num() < LinearScan::nof_regs; +} + +bool LinearScan::is_virtual_interval(const Interval* i) { + return i->reg_num() >= LIR_OprDesc::vreg_base; +} + +bool LinearScan::is_precolored_cpu_interval(const Interval* i) { + return i->reg_num() < LinearScan::nof_cpu_regs; +} + +bool LinearScan::is_virtual_cpu_interval(const Interval* i) { + return i->reg_num() >= LIR_OprDesc::vreg_base && (i->type() != T_FLOAT && i->type() != T_DOUBLE); +} + +bool LinearScan::is_precolored_fpu_interval(const Interval* i) { + return i->reg_num() >= LinearScan::nof_cpu_regs && i->reg_num() < LinearScan::nof_regs; +} + +bool LinearScan::is_virtual_fpu_interval(const Interval* i) { + return i->reg_num() >= LIR_OprDesc::vreg_base && (i->type() == T_FLOAT || i->type() == T_DOUBLE); +} + +bool LinearScan::is_in_fpu_register(const Interval* i) { + // fixed intervals not needed for FPU stack allocation + return i->reg_num() >= nof_regs && pd_first_fpu_reg <= i->assigned_reg() && i->assigned_reg() <= pd_last_fpu_reg; +} + +bool LinearScan::is_oop_interval(const Interval* i) { + // fixed intervals never contain oops + return i->reg_num() >= nof_regs && i->type() == T_OBJECT; +} + + +// ********** General helper functions + +// compute next unused stack index that can be used for spilling +int LinearScan::allocate_spill_slot(bool double_word) { + int spill_slot; + if (double_word) { + if ((_max_spills & 1) == 1) { + // alignment of double-word values + // the hole because of the alignment is filled with the next single-word value + assert(_unused_spill_slot == -1, "wasting a spill slot"); + _unused_spill_slot = _max_spills; + _max_spills++; + } + spill_slot = _max_spills; + _max_spills += 2; + + } else if (_unused_spill_slot != -1) { + // re-use hole that was the result of a previous double-word alignment + spill_slot = _unused_spill_slot; + _unused_spill_slot = -1; + + } else { + spill_slot = _max_spills; + _max_spills++; + } + + int result = spill_slot + LinearScan::nof_regs + frame_map()->argcount(); + + // the class OopMapValue uses only 11 bits for storing the name of the + // oop location. So a stack slot bigger than 2^11 leads to an overflow + // that is not reported in product builds. Prevent this by checking the + // spill slot here (altough this value and the later used location name + // are slightly different) + if (result > 2000) { + bailout("too many stack slots used"); + } + + return result; +} + +void LinearScan::assign_spill_slot(Interval* it) { + // assign the canonical spill slot of the parent (if a part of the interval + // is already spilled) or allocate a new spill slot + if (it->canonical_spill_slot() >= 0) { + it->assign_reg(it->canonical_spill_slot()); + } else { + int spill = allocate_spill_slot(type2spill_size[it->type()] == 2); + it->set_canonical_spill_slot(spill); + it->assign_reg(spill); + } +} + +void LinearScan::propagate_spill_slots() { + if (!frame_map()->finalize_frame(max_spills())) { + bailout("frame too large"); + } +} + +// create a new interval with a predefined reg_num +// (only used for parent intervals that are created during the building phase) +Interval* LinearScan::create_interval(int reg_num) { + assert(_intervals.at(reg_num) == NULL, "overwriting exisiting interval"); + + Interval* interval = new Interval(reg_num); + _intervals.at_put(reg_num, interval); + + // assign register number for precolored intervals + if (reg_num < LIR_OprDesc::vreg_base) { + interval->assign_reg(reg_num); + } + return interval; +} + +// assign a new reg_num to the interval and append it to the list of intervals +// (only used for child intervals that are created during register allocation) +void LinearScan::append_interval(Interval* it) { + it->set_reg_num(_intervals.length()); + _intervals.append(it); + _new_intervals_from_allocation->append(it); +} + +// copy the vreg-flags if an interval is split +void LinearScan::copy_register_flags(Interval* from, Interval* to) { + if (gen()->is_vreg_flag_set(from->reg_num(), LIRGenerator::byte_reg)) { + gen()->set_vreg_flag(to->reg_num(), LIRGenerator::byte_reg); + } + if (gen()->is_vreg_flag_set(from->reg_num(), LIRGenerator::callee_saved)) { + gen()->set_vreg_flag(to->reg_num(), LIRGenerator::callee_saved); + } + + // Note: do not copy the must_start_in_memory flag because it is not necessary for child + // intervals (only the very beginning of the interval must be in memory) +} + + +// ********** spill move optimization +// eliminate moves from register to stack if stack slot is known to be correct + +// called during building of intervals +void LinearScan::change_spill_definition_pos(Interval* interval, int def_pos) { + assert(interval->is_split_parent(), "can only be called for split parents"); + + switch (interval->spill_state()) { + case noDefinitionFound: + assert(interval->spill_definition_pos() == -1, "must no be set before"); + interval->set_spill_definition_pos(def_pos); + interval->set_spill_state(oneDefinitionFound); + break; + + case oneDefinitionFound: + assert(def_pos <= interval->spill_definition_pos(), "positions are processed in reverse order when intervals are created"); + if (def_pos < interval->spill_definition_pos() - 2) { + // second definition found, so no spill optimization possible for this interval + interval->set_spill_state(noOptimization); + } else { + // two consecutive definitions (because of two-operand LIR form) + assert(block_of_op_with_id(def_pos) == block_of_op_with_id(interval->spill_definition_pos()), "block must be equal"); + } + break; + + case noOptimization: + // nothing to do + break; + + default: + assert(false, "other states not allowed at this time"); + } +} + +// called during register allocation +void LinearScan::change_spill_state(Interval* interval, int spill_pos) { + switch (interval->spill_state()) { + case oneDefinitionFound: { + int def_loop_depth = block_of_op_with_id(interval->spill_definition_pos())->loop_depth(); + int spill_loop_depth = block_of_op_with_id(spill_pos)->loop_depth(); + + if (def_loop_depth < spill_loop_depth) { + // the loop depth of the spilling position is higher then the loop depth + // at the definition of the interval -> move write to memory out of loop + // by storing at definitin of the interval + interval->set_spill_state(storeAtDefinition); + } else { + // the interval is currently spilled only once, so for now there is no + // reason to store the interval at the definition + interval->set_spill_state(oneMoveInserted); + } + break; + } + + case oneMoveInserted: { + // the interval is spilled more then once, so it is better to store it to + // memory at the definition + interval->set_spill_state(storeAtDefinition); + break; + } + + case storeAtDefinition: + case startInMemory: + case noOptimization: + case noDefinitionFound: + // nothing to do + break; + + default: + assert(false, "other states not allowed at this time"); + } +} + + +bool LinearScan::must_store_at_definition(const Interval* i) { + return i->is_split_parent() && i->spill_state() == storeAtDefinition; +} + +// called once before asignment of register numbers +void LinearScan::eliminate_spill_moves() { + TIME_LINEAR_SCAN(timer_eliminate_spill_moves); + TRACE_LINEAR_SCAN(3, tty->print_cr("***** Eliminating unnecessary spill moves")); + + // collect all intervals that must be stored after their definion. + // the list is sorted by Interval::spill_definition_pos + Interval* interval; + Interval* temp_list; + create_unhandled_lists(&interval, &temp_list, must_store_at_definition, NULL); + +#ifdef ASSERT + Interval* prev = NULL; + Interval* temp = interval; + while (temp != Interval::end()) { + assert(temp->spill_definition_pos() > 0, "invalid spill definition pos"); + if (prev != NULL) { + assert(temp->from() >= prev->from(), "intervals not sorted"); + assert(temp->spill_definition_pos() >= prev->spill_definition_pos(), "when intervals are sorted by from, then they must also be sorted by spill_definition_pos"); + } + + assert(temp->canonical_spill_slot() >= LinearScan::nof_regs, "interval has no spill slot assigned"); + assert(temp->spill_definition_pos() >= temp->from(), "invalid order"); + assert(temp->spill_definition_pos() <= temp->from() + 2, "only intervals defined once at their start-pos can be optimized"); + + TRACE_LINEAR_SCAN(4, tty->print_cr("interval %d (from %d to %d) must be stored at %d", temp->reg_num(), temp->from(), temp->to(), temp->spill_definition_pos())); + + temp = temp->next(); + } +#endif + + LIR_InsertionBuffer insertion_buffer; + int num_blocks = block_count(); + for (int i = 0; i < num_blocks; i++) { + BlockBegin* block = block_at(i); + LIR_OpList* instructions = block->lir()->instructions_list(); + int num_inst = instructions->length(); + bool has_new = false; + + // iterate all instructions of the block. skip the first because it is always a label + for (int j = 1; j < num_inst; j++) { + LIR_Op* op = instructions->at(j); + int op_id = op->id(); + + if (op_id == -1) { + // remove move from register to stack if the stack slot is guaranteed to be correct. + // only moves that have been inserted by LinearScan can be removed. + assert(op->code() == lir_move, "only moves can have a op_id of -1"); + assert(op->as_Op1() != NULL, "move must be LIR_Op1"); + assert(op->as_Op1()->result_opr()->is_virtual(), "LinearScan inserts only moves to virtual registers"); + + LIR_Op1* op1 = (LIR_Op1*)op; + Interval* interval = interval_at(op1->result_opr()->vreg_number()); + + if (interval->assigned_reg() >= LinearScan::nof_regs && interval->always_in_memory()) { + // move target is a stack slot that is always correct, so eliminate instruction + TRACE_LINEAR_SCAN(4, tty->print_cr("eliminating move from interval %d to %d", op1->in_opr()->vreg_number(), op1->result_opr()->vreg_number())); + instructions->at_put(j, NULL); // NULL-instructions are deleted by assign_reg_num + } + + } else { + // insert move from register to stack just after the beginning of the interval + assert(interval == Interval::end() || interval->spill_definition_pos() >= op_id, "invalid order"); + assert(interval == Interval::end() || (interval->is_split_parent() && interval->spill_state() == storeAtDefinition), "invalid interval"); + + while (interval != Interval::end() && interval->spill_definition_pos() == op_id) { + if (!has_new) { + // prepare insertion buffer (appended when all instructions of the block are processed) + insertion_buffer.init(block->lir()); + has_new = true; + } + + LIR_Opr from_opr = operand_for_interval(interval); + LIR_Opr to_opr = canonical_spill_opr(interval); + assert(from_opr->is_fixed_cpu() || from_opr->is_fixed_fpu(), "from operand must be a register"); + assert(to_opr->is_stack(), "to operand must be a stack slot"); + + insertion_buffer.move(j, from_opr, to_opr); + TRACE_LINEAR_SCAN(4, tty->print_cr("inserting move after definition of interval %d to stack slot %d at op_id %d", interval->reg_num(), interval->canonical_spill_slot() - LinearScan::nof_regs, op_id)); + + interval = interval->next(); + } + } + } // end of instruction iteration + + if (has_new) { + block->lir()->append(&insertion_buffer); + } + } // end of block iteration + + assert(interval == Interval::end(), "missed an interval"); +} + + +// ********** Phase 1: number all instructions in all blocks +// Compute depth-first and linear scan block orders, and number LIR_Op nodes for linear scan. + +void LinearScan::number_instructions() { + { + // dummy-timer to measure the cost of the timer itself + // (this time is then subtracted from all other timers to get the real value) + TIME_LINEAR_SCAN(timer_do_nothing); + } + TIME_LINEAR_SCAN(timer_number_instructions); + + // Assign IDs to LIR nodes and build a mapping, lir_ops, from ID to LIR_Op node. + int num_blocks = block_count(); + int num_instructions = 0; + int i; + for (i = 0; i < num_blocks; i++) { + num_instructions += block_at(i)->lir()->instructions_list()->length(); + } + + // initialize with correct length + _lir_ops = LIR_OpArray(num_instructions); + _block_of_op = BlockBeginArray(num_instructions); + + int op_id = 0; + int idx = 0; + + for (i = 0; i < num_blocks; i++) { + BlockBegin* block = block_at(i); + block->set_first_lir_instruction_id(op_id); + LIR_OpList* instructions = block->lir()->instructions_list(); + + int num_inst = instructions->length(); + for (int j = 0; j < num_inst; j++) { + LIR_Op* op = instructions->at(j); + op->set_id(op_id); + + _lir_ops.at_put(idx, op); + _block_of_op.at_put(idx, block); + assert(lir_op_with_id(op_id) == op, "must match"); + + idx++; + op_id += 2; // numbering of lir_ops by two + } + block->set_last_lir_instruction_id(op_id - 2); + } + assert(idx == num_instructions, "must match"); + assert(idx * 2 == op_id, "must match"); + + _has_call = BitMap(num_instructions); _has_call.clear(); + _has_info = BitMap(num_instructions); _has_info.clear(); +} + + +// ********** Phase 2: compute local live sets separately for each block +// (sets live_gen and live_kill for each block) + +void LinearScan::set_live_gen_kill(Value value, LIR_Op* op, BitMap& live_gen, BitMap& live_kill) { + LIR_Opr opr = value->operand(); + Constant* con = value->as_Constant(); + + // check some asumptions about debug information + assert(!value->type()->is_illegal(), "if this local is used by the interpreter it shouldn't be of indeterminate type"); + assert(con == NULL || opr->is_virtual() || opr->is_constant() || opr->is_illegal(), "asumption: Constant instructions have only constant operands"); + assert(con != NULL || opr->is_virtual(), "asumption: non-Constant instructions have only virtual operands"); + + if ((con == NULL || con->is_pinned()) && opr->is_register()) { + assert(reg_num(opr) == opr->vreg_number() && !is_valid_reg_num(reg_numHi(opr)), "invalid optimization below"); + int reg = opr->vreg_number(); + if (!live_kill.at(reg)) { + live_gen.set_bit(reg); + TRACE_LINEAR_SCAN(4, tty->print_cr(" Setting live_gen for value %c%d, LIR op_id %d, register number %d", value->type()->tchar(), value->id(), op->id(), reg)); + } + } +} + + +void LinearScan::compute_local_live_sets() { + TIME_LINEAR_SCAN(timer_compute_local_live_sets); + + int num_blocks = block_count(); + int live_size = live_set_size(); + bool local_has_fpu_registers = false; + int local_num_calls = 0; + LIR_OpVisitState visitor; + + BitMap2D local_interval_in_loop = BitMap2D(_num_virtual_regs, num_loops()); + local_interval_in_loop.clear(); + + // iterate all blocks + for (int i = 0; i < num_blocks; i++) { + BlockBegin* block = block_at(i); + + BitMap live_gen(live_size); live_gen.clear(); + BitMap live_kill(live_size); live_kill.clear(); + + if (block->is_set(BlockBegin::exception_entry_flag)) { + // Phi functions at the begin of an exception handler are + // implicitly defined (= killed) at the beginning of the block. + for_each_phi_fun(block, phi, + live_kill.set_bit(phi->operand()->vreg_number()) + ); + } + + LIR_OpList* instructions = block->lir()->instructions_list(); + int num_inst = instructions->length(); + + // iterate all instructions of the block. skip the first because it is always a label + assert(visitor.no_operands(instructions->at(0)), "first operation must always be a label"); + for (int j = 1; j < num_inst; j++) { + LIR_Op* op = instructions->at(j); + + // visit operation to collect all operands + visitor.visit(op); + + if (visitor.has_call()) { + _has_call.set_bit(op->id() >> 1); + local_num_calls++; + } + if (visitor.info_count() > 0) { + _has_info.set_bit(op->id() >> 1); + } + + // iterate input operands of instruction + int k, n, reg; + n = visitor.opr_count(LIR_OpVisitState::inputMode); + for (k = 0; k < n; k++) { + LIR_Opr opr = visitor.opr_at(LIR_OpVisitState::inputMode, k); + assert(opr->is_register(), "visitor should only return register operands"); + + if (opr->is_virtual_register()) { + assert(reg_num(opr) == opr->vreg_number() && !is_valid_reg_num(reg_numHi(opr)), "invalid optimization below"); + reg = opr->vreg_number(); + if (!live_kill.at(reg)) { + live_gen.set_bit(reg); + TRACE_LINEAR_SCAN(4, tty->print_cr(" Setting live_gen for register %d at instruction %d", reg, op->id())); + } + if (block->loop_index() >= 0) { + local_interval_in_loop.set_bit(reg, block->loop_index()); + } + local_has_fpu_registers = local_has_fpu_registers || opr->is_virtual_fpu(); + } + +#ifdef ASSERT + // fixed intervals are never live at block boundaries, so + // they need not be processed in live sets. + // this is checked by these assertions to be sure about it. + // the entry block may have incoming values in registers, which is ok. + if (!opr->is_virtual_register() && block != ir()->start()) { + reg = reg_num(opr); + if (is_processed_reg_num(reg)) { + assert(live_kill.at(reg), "using fixed register that is not defined in this block"); + } + reg = reg_numHi(opr); + if (is_valid_reg_num(reg) && is_processed_reg_num(reg)) { + assert(live_kill.at(reg), "using fixed register that is not defined in this block"); + } + } +#endif + } + + // Add uses of live locals from interpreter's point of view for proper debug information generation + n = visitor.info_count(); + for (k = 0; k < n; k++) { + CodeEmitInfo* info = visitor.info_at(k); + ValueStack* stack = info->stack(); + for_each_state_value(stack, value, + set_live_gen_kill(value, op, live_gen, live_kill) + ); + } + + // iterate temp operands of instruction + n = visitor.opr_count(LIR_OpVisitState::tempMode); + for (k = 0; k < n; k++) { + LIR_Opr opr = visitor.opr_at(LIR_OpVisitState::tempMode, k); + assert(opr->is_register(), "visitor should only return register operands"); + + if (opr->is_virtual_register()) { + assert(reg_num(opr) == opr->vreg_number() && !is_valid_reg_num(reg_numHi(opr)), "invalid optimization below"); + reg = opr->vreg_number(); + live_kill.set_bit(reg); + if (block->loop_index() >= 0) { + local_interval_in_loop.set_bit(reg, block->loop_index()); + } + local_has_fpu_registers = local_has_fpu_registers || opr->is_virtual_fpu(); + } + +#ifdef ASSERT + // fixed intervals are never live at block boundaries, so + // they need not be processed in live sets + // process them only in debug mode so that this can be checked + if (!opr->is_virtual_register()) { + reg = reg_num(opr); + if (is_processed_reg_num(reg)) { + live_kill.set_bit(reg_num(opr)); + } + reg = reg_numHi(opr); + if (is_valid_reg_num(reg) && is_processed_reg_num(reg)) { + live_kill.set_bit(reg); + } + } +#endif + } + + // iterate output operands of instruction + n = visitor.opr_count(LIR_OpVisitState::outputMode); + for (k = 0; k < n; k++) { + LIR_Opr opr = visitor.opr_at(LIR_OpVisitState::outputMode, k); + assert(opr->is_register(), "visitor should only return register operands"); + + if (opr->is_virtual_register()) { + assert(reg_num(opr) == opr->vreg_number() && !is_valid_reg_num(reg_numHi(opr)), "invalid optimization below"); + reg = opr->vreg_number(); + live_kill.set_bit(reg); + if (block->loop_index() >= 0) { + local_interval_in_loop.set_bit(reg, block->loop_index()); + } + local_has_fpu_registers = local_has_fpu_registers || opr->is_virtual_fpu(); + } + +#ifdef ASSERT + // fixed intervals are never live at block boundaries, so + // they need not be processed in live sets + // process them only in debug mode so that this can be checked + if (!opr->is_virtual_register()) { + reg = reg_num(opr); + if (is_processed_reg_num(reg)) { + live_kill.set_bit(reg_num(opr)); + } + reg = reg_numHi(opr); + if (is_valid_reg_num(reg) && is_processed_reg_num(reg)) { + live_kill.set_bit(reg); + } + } +#endif + } + } // end of instruction iteration + + block->set_live_gen (live_gen); + block->set_live_kill(live_kill); + block->set_live_in (BitMap(live_size)); block->live_in().clear(); + block->set_live_out (BitMap(live_size)); block->live_out().clear(); + + TRACE_LINEAR_SCAN(4, tty->print("live_gen B%d ", block->block_id()); print_bitmap(block->live_gen())); + TRACE_LINEAR_SCAN(4, tty->print("live_kill B%d ", block->block_id()); print_bitmap(block->live_kill())); + } // end of block iteration + + // propagate local calculated information into LinearScan object + _has_fpu_registers = local_has_fpu_registers; + compilation()->set_has_fpu_code(local_has_fpu_registers); + + _num_calls = local_num_calls; + _interval_in_loop = local_interval_in_loop; +} + + +// ********** Phase 3: perform a backward dataflow analysis to compute global live sets +// (sets live_in and live_out for each block) + +void LinearScan::compute_global_live_sets() { + TIME_LINEAR_SCAN(timer_compute_global_live_sets); + + int num_blocks = block_count(); + bool change_occurred; + bool change_occurred_in_block; + int iteration_count = 0; + BitMap live_out(live_set_size()); live_out.clear(); // scratch set for calculations + + // Perform a backward dataflow analysis to compute live_out and live_in for each block. + // The loop is executed until a fixpoint is reached (no changes in an iteration) + // Exception handlers must be processed because not all live values are + // present in the state array, e.g. because of global value numbering + do { + change_occurred = false; + + // iterate all blocks in reverse order + for (int i = num_blocks - 1; i >= 0; i--) { + BlockBegin* block = block_at(i); + + change_occurred_in_block = false; + + // live_out(block) is the union of live_in(sux), for successors sux of block + int n = block->number_of_sux(); + int e = block->number_of_exception_handlers(); + if (n + e > 0) { + // block has successors + if (n > 0) { + live_out.set_from(block->sux_at(0)->live_in()); + for (int j = 1; j < n; j++) { + live_out.set_union(block->sux_at(j)->live_in()); + } + } else { + live_out.clear(); + } + for (int j = 0; j < e; j++) { + live_out.set_union(block->exception_handler_at(j)->live_in()); + } + + if (!block->live_out().is_same(live_out)) { + // A change occurred. Swap the old and new live out sets to avoid copying. + BitMap temp = block->live_out(); + block->set_live_out(live_out); + live_out = temp; + + change_occurred = true; + change_occurred_in_block = true; + } + } + + if (iteration_count == 0 || change_occurred_in_block) { + // live_in(block) is the union of live_gen(block) with (live_out(block) & !live_kill(block)) + // note: live_in has to be computed only in first iteration or if live_out has changed! + BitMap live_in = block->live_in(); + live_in.set_from(block->live_out()); + live_in.set_difference(block->live_kill()); + live_in.set_union(block->live_gen()); + } + +#ifndef PRODUCT + if (TraceLinearScanLevel >= 4) { + char c = ' '; + if (iteration_count == 0 || change_occurred_in_block) { + c = '*'; + } + tty->print("(%d) live_in%c B%d ", iteration_count, c, block->block_id()); print_bitmap(block->live_in()); + tty->print("(%d) live_out%c B%d ", iteration_count, c, block->block_id()); print_bitmap(block->live_out()); + } +#endif + } + iteration_count++; + + if (change_occurred && iteration_count > 50) { + BAILOUT("too many iterations in compute_global_live_sets"); + } + } while (change_occurred); + + +#ifdef ASSERT + // check that fixed intervals are not live at block boundaries + // (live set must be empty at fixed intervals) + for (int i = 0; i < num_blocks; i++) { + BlockBegin* block = block_at(i); + for (int j = 0; j < LIR_OprDesc::vreg_base; j++) { + assert(block->live_in().at(j) == false, "live_in set of fixed register must be empty"); + assert(block->live_out().at(j) == false, "live_out set of fixed register must be empty"); + assert(block->live_gen().at(j) == false, "live_gen set of fixed register must be empty"); + } + } +#endif + + // check that the live_in set of the first block is empty + BitMap live_in_args(ir()->start()->live_in().size()); + live_in_args.clear(); + if (!ir()->start()->live_in().is_same(live_in_args)) { +#ifdef ASSERT + tty->print_cr("Error: live_in set of first block must be empty (when this fails, virtual registers are used before they are defined)"); + tty->print_cr("affected registers:"); + print_bitmap(ir()->start()->live_in()); + + // print some additional information to simplify debugging + for (unsigned int i = 0; i < ir()->start()->live_in().size(); i++) { + if (ir()->start()->live_in().at(i)) { + Instruction* instr = gen()->instruction_for_vreg(i); + tty->print_cr("* vreg %d (HIR instruction %c%d)", i, instr == NULL ? ' ' : instr->type()->tchar(), instr == NULL ? 0 : instr->id()); + + for (int j = 0; j < num_blocks; j++) { + BlockBegin* block = block_at(j); + if (block->live_gen().at(i)) { + tty->print_cr(" used in block B%d", block->block_id()); + } + if (block->live_kill().at(i)) { + tty->print_cr(" defined in block B%d", block->block_id()); + } + } + } + } + +#endif + // when this fails, virtual registers are used before they are defined. + assert(false, "live_in set of first block must be empty"); + // bailout of if this occurs in product mode. + bailout("live_in set of first block not empty"); + } +} + + +// ********** Phase 4: build intervals +// (fills the list _intervals) + +void LinearScan::add_use(Value value, int from, int to, IntervalUseKind use_kind) { + assert(!value->type()->is_illegal(), "if this value is used by the interpreter it shouldn't be of indeterminate type"); + LIR_Opr opr = value->operand(); + Constant* con = value->as_Constant(); + + if ((con == NULL || con->is_pinned()) && opr->is_register()) { + assert(reg_num(opr) == opr->vreg_number() && !is_valid_reg_num(reg_numHi(opr)), "invalid optimization below"); + add_use(opr, from, to, use_kind); + } +} + + +void LinearScan::add_def(LIR_Opr opr, int def_pos, IntervalUseKind use_kind) { + TRACE_LINEAR_SCAN(2, tty->print(" def "); opr->print(tty); tty->print_cr(" def_pos %d (%d)", def_pos, use_kind)); + assert(opr->is_register(), "should not be called otherwise"); + + if (opr->is_virtual_register()) { + assert(reg_num(opr) == opr->vreg_number() && !is_valid_reg_num(reg_numHi(opr)), "invalid optimization below"); + add_def(opr->vreg_number(), def_pos, use_kind, opr->type_register()); + + } else { + int reg = reg_num(opr); + if (is_processed_reg_num(reg)) { + add_def(reg, def_pos, use_kind, opr->type_register()); + } + reg = reg_numHi(opr); + if (is_valid_reg_num(reg) && is_processed_reg_num(reg)) { + add_def(reg, def_pos, use_kind, opr->type_register()); + } + } +} + +void LinearScan::add_use(LIR_Opr opr, int from, int to, IntervalUseKind use_kind) { + TRACE_LINEAR_SCAN(2, tty->print(" use "); opr->print(tty); tty->print_cr(" from %d to %d (%d)", from, to, use_kind)); + assert(opr->is_register(), "should not be called otherwise"); + + if (opr->is_virtual_register()) { + assert(reg_num(opr) == opr->vreg_number() && !is_valid_reg_num(reg_numHi(opr)), "invalid optimization below"); + add_use(opr->vreg_number(), from, to, use_kind, opr->type_register()); + + } else { + int reg = reg_num(opr); + if (is_processed_reg_num(reg)) { + add_use(reg, from, to, use_kind, opr->type_register()); + } + reg = reg_numHi(opr); + if (is_valid_reg_num(reg) && is_processed_reg_num(reg)) { + add_use(reg, from, to, use_kind, opr->type_register()); + } + } +} + +void LinearScan::add_temp(LIR_Opr opr, int temp_pos, IntervalUseKind use_kind) { + TRACE_LINEAR_SCAN(2, tty->print(" temp "); opr->print(tty); tty->print_cr(" temp_pos %d (%d)", temp_pos, use_kind)); + assert(opr->is_register(), "should not be called otherwise"); + + if (opr->is_virtual_register()) { + assert(reg_num(opr) == opr->vreg_number() && !is_valid_reg_num(reg_numHi(opr)), "invalid optimization below"); + add_temp(opr->vreg_number(), temp_pos, use_kind, opr->type_register()); + + } else { + int reg = reg_num(opr); + if (is_processed_reg_num(reg)) { + add_temp(reg, temp_pos, use_kind, opr->type_register()); + } + reg = reg_numHi(opr); + if (is_valid_reg_num(reg) && is_processed_reg_num(reg)) { + add_temp(reg, temp_pos, use_kind, opr->type_register()); + } + } +} + + +void LinearScan::add_def(int reg_num, int def_pos, IntervalUseKind use_kind, BasicType type) { + Interval* interval = interval_at(reg_num); + if (interval != NULL) { + assert(interval->reg_num() == reg_num, "wrong interval"); + + if (type != T_ILLEGAL) { + interval->set_type(type); + } + + Range* r = interval->first(); + if (r->from() <= def_pos) { + // Update the starting point (when a range is first created for a use, its + // start is the beginning of the current block until a def is encountered.) + r->set_from(def_pos); + interval->add_use_pos(def_pos, use_kind); + + } else { + // Dead value - make vacuous interval + // also add use_kind for dead intervals + interval->add_range(def_pos, def_pos + 1); + interval->add_use_pos(def_pos, use_kind); + TRACE_LINEAR_SCAN(2, tty->print_cr("Warning: def of reg %d at %d occurs without use", reg_num, def_pos)); + } + + } else { + // Dead value - make vacuous interval + // also add use_kind for dead intervals + interval = create_interval(reg_num); + if (type != T_ILLEGAL) { + interval->set_type(type); + } + + interval->add_range(def_pos, def_pos + 1); + interval->add_use_pos(def_pos, use_kind); + TRACE_LINEAR_SCAN(2, tty->print_cr("Warning: dead value %d at %d in live intervals", reg_num, def_pos)); + } + + change_spill_definition_pos(interval, def_pos); + if (use_kind == noUse && interval->spill_state() <= startInMemory) { + // detection of method-parameters and roundfp-results + // TODO: move this directly to position where use-kind is computed + interval->set_spill_state(startInMemory); + } +} + +void LinearScan::add_use(int reg_num, int from, int to, IntervalUseKind use_kind, BasicType type) { + Interval* interval = interval_at(reg_num); + if (interval == NULL) { + interval = create_interval(reg_num); + } + assert(interval->reg_num() == reg_num, "wrong interval"); + + if (type != T_ILLEGAL) { + interval->set_type(type); + } + + interval->add_range(from, to); + interval->add_use_pos(to, use_kind); +} + +void LinearScan::add_temp(int reg_num, int temp_pos, IntervalUseKind use_kind, BasicType type) { + Interval* interval = interval_at(reg_num); + if (interval == NULL) { + interval = create_interval(reg_num); + } + assert(interval->reg_num() == reg_num, "wrong interval"); + + if (type != T_ILLEGAL) { + interval->set_type(type); + } + + interval->add_range(temp_pos, temp_pos + 1); + interval->add_use_pos(temp_pos, use_kind); +} + + +// the results of this functions are used for optimizing spilling and reloading +// if the functions return shouldHaveRegister and the interval is spilled, +// it is not reloaded to a register. +IntervalUseKind LinearScan::use_kind_of_output_operand(LIR_Op* op, LIR_Opr opr) { + if (op->code() == lir_move) { + assert(op->as_Op1() != NULL, "lir_move must be LIR_Op1"); + LIR_Op1* move = (LIR_Op1*)op; + LIR_Opr res = move->result_opr(); + bool result_in_memory = res->is_virtual() && gen()->is_vreg_flag_set(res->vreg_number(), LIRGenerator::must_start_in_memory); + + if (result_in_memory) { + // Begin of an interval with must_start_in_memory set. + // This interval will always get a stack slot first, so return noUse. + return noUse; + + } else if (move->in_opr()->is_stack()) { + // method argument (condition must be equal to handle_method_arguments) + return noUse; + + } else if (move->in_opr()->is_register() && move->result_opr()->is_register()) { + // Move from register to register + if (block_of_op_with_id(op->id())->is_set(BlockBegin::osr_entry_flag)) { + // special handling of phi-function moves inside osr-entry blocks + // input operand must have a register instead of output operand (leads to better register allocation) + return shouldHaveRegister; + } + } + } + + if (opr->is_virtual() && + gen()->is_vreg_flag_set(opr->vreg_number(), LIRGenerator::must_start_in_memory)) { + // result is a stack-slot, so prevent immediate reloading + return noUse; + } + + // all other operands require a register + return mustHaveRegister; +} + +IntervalUseKind LinearScan::use_kind_of_input_operand(LIR_Op* op, LIR_Opr opr) { + if (op->code() == lir_move) { + assert(op->as_Op1() != NULL, "lir_move must be LIR_Op1"); + LIR_Op1* move = (LIR_Op1*)op; + LIR_Opr res = move->result_opr(); + bool result_in_memory = res->is_virtual() && gen()->is_vreg_flag_set(res->vreg_number(), LIRGenerator::must_start_in_memory); + + if (result_in_memory) { + // Move to an interval with must_start_in_memory set. + // To avoid moves from stack to stack (not allowed) force the input operand to a register + return mustHaveRegister; + + } else if (move->in_opr()->is_register() && move->result_opr()->is_register()) { + // Move from register to register + if (block_of_op_with_id(op->id())->is_set(BlockBegin::osr_entry_flag)) { + // special handling of phi-function moves inside osr-entry blocks + // input operand must have a register instead of output operand (leads to better register allocation) + return mustHaveRegister; + } + + // The input operand is not forced to a register (moves from stack to register are allowed), + // but it is faster if the input operand is in a register + return shouldHaveRegister; + } + } + + +#ifdef IA32 + if (op->code() == lir_cmove) { + // conditional moves can handle stack operands + assert(op->result_opr()->is_register(), "result must always be in a register"); + return shouldHaveRegister; + } + + // optimizations for second input operand of arithmehtic operations on Intel + // this operand is allowed to be on the stack in some cases + BasicType opr_type = opr->type_register(); + if (opr_type == T_FLOAT || opr_type == T_DOUBLE) { + if ((UseSSE == 1 && opr_type == T_FLOAT) || UseSSE >= 2) { + // SSE float instruction (T_DOUBLE only supported with SSE2) + switch (op->code()) { + case lir_cmp: + case lir_add: + case lir_sub: + case lir_mul: + case lir_div: + { + assert(op->as_Op2() != NULL, "must be LIR_Op2"); + LIR_Op2* op2 = (LIR_Op2*)op; + if (op2->in_opr1() != op2->in_opr2() && op2->in_opr2() == opr) { + assert((op2->result_opr()->is_register() || op->code() == lir_cmp) && op2->in_opr1()->is_register(), "cannot mark second operand as stack if others are not in register"); + return shouldHaveRegister; + } + } + } + } else { + // FPU stack float instruction + switch (op->code()) { + case lir_add: + case lir_sub: + case lir_mul: + case lir_div: + { + assert(op->as_Op2() != NULL, "must be LIR_Op2"); + LIR_Op2* op2 = (LIR_Op2*)op; + if (op2->in_opr1() != op2->in_opr2() && op2->in_opr2() == opr) { + assert((op2->result_opr()->is_register() || op->code() == lir_cmp) && op2->in_opr1()->is_register(), "cannot mark second operand as stack if others are not in register"); + return shouldHaveRegister; + } + } + } + } + + } else if (opr_type != T_LONG) { + // integer instruction (note: long operands must always be in register) + switch (op->code()) { + case lir_cmp: + case lir_add: + case lir_sub: + case lir_logic_and: + case lir_logic_or: + case lir_logic_xor: + { + assert(op->as_Op2() != NULL, "must be LIR_Op2"); + LIR_Op2* op2 = (LIR_Op2*)op; + if (op2->in_opr1() != op2->in_opr2() && op2->in_opr2() == opr) { + assert((op2->result_opr()->is_register() || op->code() == lir_cmp) && op2->in_opr1()->is_register(), "cannot mark second operand as stack if others are not in register"); + return shouldHaveRegister; + } + } + } + } +#endif // IA32 + + // all other operands require a register + return mustHaveRegister; +} + + +void LinearScan::handle_method_arguments(LIR_Op* op) { + // special handling for method arguments (moves from stack to virtual register): + // the interval gets no register assigned, but the stack slot. + // it is split before the first use by the register allocator. + + if (op->code() == lir_move) { + assert(op->as_Op1() != NULL, "must be LIR_Op1"); + LIR_Op1* move = (LIR_Op1*)op; + + if (move->in_opr()->is_stack()) { +#ifdef ASSERT + int arg_size = compilation()->method()->arg_size(); + LIR_Opr o = move->in_opr(); + if (o->is_single_stack()) { + assert(o->single_stack_ix() >= 0 && o->single_stack_ix() < arg_size, "out of range"); + } else if (o->is_double_stack()) { + assert(o->double_stack_ix() >= 0 && o->double_stack_ix() < arg_size, "out of range"); + } else { + ShouldNotReachHere(); + } + + assert(move->id() > 0, "invalid id"); + assert(block_of_op_with_id(move->id())->number_of_preds() == 0, "move from stack must be in first block"); + assert(move->result_opr()->is_virtual(), "result of move must be a virtual register"); + + TRACE_LINEAR_SCAN(4, tty->print_cr("found move from stack slot %d to vreg %d", o->is_single_stack() ? o->single_stack_ix() : o->double_stack_ix(), reg_num(move->result_opr()))); +#endif + + Interval* interval = interval_at(reg_num(move->result_opr())); + + int stack_slot = LinearScan::nof_regs + (move->in_opr()->is_single_stack() ? move->in_opr()->single_stack_ix() : move->in_opr()->double_stack_ix()); + interval->set_canonical_spill_slot(stack_slot); + interval->assign_reg(stack_slot); + } + } +} + +void LinearScan::handle_doubleword_moves(LIR_Op* op) { + // special handling for doubleword move from memory to register: + // in this case the registers of the input address and the result + // registers must not overlap -> add a temp range for the input registers + if (op->code() == lir_move) { + assert(op->as_Op1() != NULL, "must be LIR_Op1"); + LIR_Op1* move = (LIR_Op1*)op; + + if (move->result_opr()->is_double_cpu() && move->in_opr()->is_pointer()) { + LIR_Address* address = move->in_opr()->as_address_ptr(); + if (address != NULL) { + if (address->base()->is_valid()) { + add_temp(address->base(), op->id(), noUse); + } + if (address->index()->is_valid()) { + add_temp(address->index(), op->id(), noUse); + } + } + } + } +} + +void LinearScan::add_register_hints(LIR_Op* op) { + switch (op->code()) { + case lir_move: // fall through + case lir_convert: { + assert(op->as_Op1() != NULL, "lir_move, lir_convert must be LIR_Op1"); + LIR_Op1* move = (LIR_Op1*)op; + + LIR_Opr move_from = move->in_opr(); + LIR_Opr move_to = move->result_opr(); + + if (move_to->is_register() && move_from->is_register()) { + Interval* from = interval_at(reg_num(move_from)); + Interval* to = interval_at(reg_num(move_to)); + if (from != NULL && to != NULL) { + to->set_register_hint(from); + TRACE_LINEAR_SCAN(4, tty->print_cr("operation at op_id %d: added hint from interval %d to %d", move->id(), from->reg_num(), to->reg_num())); + } + } + break; + } + case lir_cmove: { + assert(op->as_Op2() != NULL, "lir_cmove must be LIR_Op2"); + LIR_Op2* cmove = (LIR_Op2*)op; + + LIR_Opr move_from = cmove->in_opr1(); + LIR_Opr move_to = cmove->result_opr(); + + if (move_to->is_register() && move_from->is_register()) { + Interval* from = interval_at(reg_num(move_from)); + Interval* to = interval_at(reg_num(move_to)); + if (from != NULL && to != NULL) { + to->set_register_hint(from); + TRACE_LINEAR_SCAN(4, tty->print_cr("operation at op_id %d: added hint from interval %d to %d", cmove->id(), from->reg_num(), to->reg_num())); + } + } + break; + } + } +} + + +void LinearScan::build_intervals() { + TIME_LINEAR_SCAN(timer_build_intervals); + + // initialize interval list with expected number of intervals + // (32 is added to have some space for split children without having to resize the list) + _intervals = IntervalList(num_virtual_regs() + 32); + // initialize all slots that are used by build_intervals + _intervals.at_put_grow(num_virtual_regs() - 1, NULL, NULL); + + // create a list with all caller-save registers (cpu, fpu, xmm) + // when an instruction is a call, a temp range is created for all these registers + int num_caller_save_registers = 0; + int caller_save_registers[LinearScan::nof_regs]; + + int i; + for (i = 0; i < FrameMap::nof_caller_save_cpu_regs; i++) { + LIR_Opr opr = FrameMap::caller_save_cpu_reg_at(i); + assert(opr->is_valid() && opr->is_register(), "FrameMap should not return invalid operands"); + assert(reg_numHi(opr) == -1, "missing addition of range for hi-register"); + caller_save_registers[num_caller_save_registers++] = reg_num(opr); + } + + // temp ranges for fpu registers are only created when the method has + // virtual fpu operands. Otherwise no allocation for fpu registers is + // perfomed and so the temp ranges would be useless + if (has_fpu_registers()) { +#ifdef IA32 + if (UseSSE < 2) { +#endif + for (i = 0; i < FrameMap::nof_caller_save_fpu_regs; i++) { + LIR_Opr opr = FrameMap::caller_save_fpu_reg_at(i); + assert(opr->is_valid() && opr->is_register(), "FrameMap should not return invalid operands"); + assert(reg_numHi(opr) == -1, "missing addition of range for hi-register"); + caller_save_registers[num_caller_save_registers++] = reg_num(opr); + } +#ifdef IA32 + } + if (UseSSE > 0) { + for (i = 0; i < FrameMap::nof_caller_save_xmm_regs; i++) { + LIR_Opr opr = FrameMap::caller_save_xmm_reg_at(i); + assert(opr->is_valid() && opr->is_register(), "FrameMap should not return invalid operands"); + assert(reg_numHi(opr) == -1, "missing addition of range for hi-register"); + caller_save_registers[num_caller_save_registers++] = reg_num(opr); + } + } +#endif + } + assert(num_caller_save_registers <= LinearScan::nof_regs, "out of bounds"); + + + LIR_OpVisitState visitor; + + // iterate all blocks in reverse order + for (i = block_count() - 1; i >= 0; i--) { + BlockBegin* block = block_at(i); + LIR_OpList* instructions = block->lir()->instructions_list(); + int block_from = block->first_lir_instruction_id(); + int block_to = block->last_lir_instruction_id(); + + assert(block_from == instructions->at(0)->id(), "must be"); + assert(block_to == instructions->at(instructions->length() - 1)->id(), "must be"); + + // Update intervals for registers live at the end of this block; + BitMap live = block->live_out(); + int size = live.size(); + for (int number = live.get_next_one_offset(0, size); number < size; number = live.get_next_one_offset(number + 1, size)) { + assert(live.at(number), "should not stop here otherwise"); + assert(number >= LIR_OprDesc::vreg_base, "fixed intervals must not be live on block bounds"); + TRACE_LINEAR_SCAN(2, tty->print_cr("live in %d to %d", number, block_to + 2)); + + add_use(number, block_from, block_to + 2, noUse, T_ILLEGAL); + + // add special use positions for loop-end blocks when the + // interval is used anywhere inside this loop. It's possible + // that the block was part of a non-natural loop, so it might + // have an invalid loop index. + if (block->is_set(BlockBegin::linear_scan_loop_end_flag) && + block->loop_index() != -1 && + is_interval_in_loop(number, block->loop_index())) { + interval_at(number)->add_use_pos(block_to + 1, loopEndMarker); + } + } + + // iterate all instructions of the block in reverse order. + // skip the first instruction because it is always a label + // definitions of intervals are processed before uses + assert(visitor.no_operands(instructions->at(0)), "first operation must always be a label"); + for (int j = instructions->length() - 1; j >= 1; j--) { + LIR_Op* op = instructions->at(j); + int op_id = op->id(); + + // visit operation to collect all operands + visitor.visit(op); + + // add a temp range for each register if operation destroys caller-save registers + if (visitor.has_call()) { + for (int k = 0; k < num_caller_save_registers; k++) { + add_temp(caller_save_registers[k], op_id, noUse, T_ILLEGAL); + } + TRACE_LINEAR_SCAN(4, tty->print_cr("operation destroys all caller-save registers")); + } + + // Add any platform dependent temps + pd_add_temps(op); + + // visit definitions (output and temp operands) + int k, n; + n = visitor.opr_count(LIR_OpVisitState::outputMode); + for (k = 0; k < n; k++) { + LIR_Opr opr = visitor.opr_at(LIR_OpVisitState::outputMode, k); + assert(opr->is_register(), "visitor should only return register operands"); + add_def(opr, op_id, use_kind_of_output_operand(op, opr)); + } + + n = visitor.opr_count(LIR_OpVisitState::tempMode); + for (k = 0; k < n; k++) { + LIR_Opr opr = visitor.opr_at(LIR_OpVisitState::tempMode, k); + assert(opr->is_register(), "visitor should only return register operands"); + add_temp(opr, op_id, mustHaveRegister); + } + + // visit uses (input operands) + n = visitor.opr_count(LIR_OpVisitState::inputMode); + for (k = 0; k < n; k++) { + LIR_Opr opr = visitor.opr_at(LIR_OpVisitState::inputMode, k); + assert(opr->is_register(), "visitor should only return register operands"); + add_use(opr, block_from, op_id, use_kind_of_input_operand(op, opr)); + } + + // Add uses of live locals from interpreter's point of view for proper + // debug information generation + // Treat these operands as temp values (if the life range is extended + // to a call site, the value would be in a register at the call otherwise) + n = visitor.info_count(); + for (k = 0; k < n; k++) { + CodeEmitInfo* info = visitor.info_at(k); + ValueStack* stack = info->stack(); + for_each_state_value(stack, value, + add_use(value, block_from, op_id + 1, noUse); + ); + } + + // special steps for some instructions (especially moves) + handle_method_arguments(op); + handle_doubleword_moves(op); + add_register_hints(op); + + } // end of instruction iteration + } // end of block iteration + + + // add the range [0, 1[ to all fixed intervals + // -> the register allocator need not handle unhandled fixed intervals + for (int n = 0; n < LinearScan::nof_regs; n++) { + Interval* interval = interval_at(n); + if (interval != NULL) { + interval->add_range(0, 1); + } + } +} + + +// ********** Phase 5: actual register allocation + +int LinearScan::interval_cmp(Interval** a, Interval** b) { + if (*a != NULL) { + if (*b != NULL) { + return (*a)->from() - (*b)->from(); + } else { + return -1; + } + } else { + if (*b != NULL) { + return 1; + } else { + return 0; + } + } +} + +#ifndef PRODUCT +bool LinearScan::is_sorted(IntervalArray* intervals) { + int from = -1; + int i, j; + for (i = 0; i < intervals->length(); i ++) { + Interval* it = intervals->at(i); + if (it != NULL) { + if (from > it->from()) { + assert(false, ""); + return false; + } + from = it->from(); + } + } + + // check in both directions if sorted list and unsorted list contain same intervals + for (i = 0; i < interval_count(); i++) { + if (interval_at(i) != NULL) { + int num_found = 0; + for (j = 0; j < intervals->length(); j++) { + if (interval_at(i) == intervals->at(j)) { + num_found++; + } + } + assert(num_found == 1, "lists do not contain same intervals"); + } + } + for (j = 0; j < intervals->length(); j++) { + int num_found = 0; + for (i = 0; i < interval_count(); i++) { + if (interval_at(i) == intervals->at(j)) { + num_found++; + } + } + assert(num_found == 1, "lists do not contain same intervals"); + } + + return true; +} +#endif + +void LinearScan::add_to_list(Interval** first, Interval** prev, Interval* interval) { + if (*prev != NULL) { + (*prev)->set_next(interval); + } else { + *first = interval; + } + *prev = interval; +} + +void LinearScan::create_unhandled_lists(Interval** list1, Interval** list2, bool (is_list1)(const Interval* i), bool (is_list2)(const Interval* i)) { + assert(is_sorted(_sorted_intervals), "interval list is not sorted"); + + *list1 = *list2 = Interval::end(); + + Interval* list1_prev = NULL; + Interval* list2_prev = NULL; + Interval* v; + + const int n = _sorted_intervals->length(); + for (int i = 0; i < n; i++) { + v = _sorted_intervals->at(i); + if (v == NULL) continue; + + if (is_list1(v)) { + add_to_list(list1, &list1_prev, v); + } else if (is_list2 == NULL || is_list2(v)) { + add_to_list(list2, &list2_prev, v); + } + } + + if (list1_prev != NULL) list1_prev->set_next(Interval::end()); + if (list2_prev != NULL) list2_prev->set_next(Interval::end()); + + assert(list1_prev == NULL || list1_prev->next() == Interval::end(), "linear list ends not with sentinel"); + assert(list2_prev == NULL || list2_prev->next() == Interval::end(), "linear list ends not with sentinel"); +} + + +void LinearScan::sort_intervals_before_allocation() { + TIME_LINEAR_SCAN(timer_sort_intervals_before); + + IntervalList* unsorted_list = &_intervals; + int unsorted_len = unsorted_list->length(); + int sorted_len = 0; + int unsorted_idx; + int sorted_idx = 0; + int sorted_from_max = -1; + + // calc number of items for sorted list (sorted list must not contain NULL values) + for (unsorted_idx = 0; unsorted_idx < unsorted_len; unsorted_idx++) { + if (unsorted_list->at(unsorted_idx) != NULL) { + sorted_len++; + } + } + IntervalArray* sorted_list = new IntervalArray(sorted_len); + + // special sorting algorithm: the original interval-list is almost sorted, + // only some intervals are swapped. So this is much faster than a complete QuickSort + for (unsorted_idx = 0; unsorted_idx < unsorted_len; unsorted_idx++) { + Interval* cur_interval = unsorted_list->at(unsorted_idx); + + if (cur_interval != NULL) { + int cur_from = cur_interval->from(); + + if (sorted_from_max <= cur_from) { + sorted_list->at_put(sorted_idx++, cur_interval); + sorted_from_max = cur_interval->from(); + } else { + // the asumption that the intervals are already sorted failed, + // so this interval must be sorted in manually + int j; + for (j = sorted_idx - 1; j >= 0 && cur_from < sorted_list->at(j)->from(); j--) { + sorted_list->at_put(j + 1, sorted_list->at(j)); + } + sorted_list->at_put(j + 1, cur_interval); + sorted_idx++; + } + } + } + _sorted_intervals = sorted_list; +} + +void LinearScan::sort_intervals_after_allocation() { + TIME_LINEAR_SCAN(timer_sort_intervals_after); + + IntervalArray* old_list = _sorted_intervals; + IntervalList* new_list = _new_intervals_from_allocation; + int old_len = old_list->length(); + int new_len = new_list->length(); + + if (new_len == 0) { + // no intervals have been added during allocation, so sorted list is already up to date + return; + } + + // conventional sort-algorithm for new intervals + new_list->sort(interval_cmp); + + // merge old and new list (both already sorted) into one combined list + IntervalArray* combined_list = new IntervalArray(old_len + new_len); + int old_idx = 0; + int new_idx = 0; + + while (old_idx + new_idx < old_len + new_len) { + if (new_idx >= new_len || (old_idx < old_len && old_list->at(old_idx)->from() <= new_list->at(new_idx)->from())) { + combined_list->at_put(old_idx + new_idx, old_list->at(old_idx)); + old_idx++; + } else { + combined_list->at_put(old_idx + new_idx, new_list->at(new_idx)); + new_idx++; + } + } + + _sorted_intervals = combined_list; +} + + +void LinearScan::allocate_registers() { + TIME_LINEAR_SCAN(timer_allocate_registers); + + Interval* precolored_cpu_intervals, *not_precolored_cpu_intervals; + Interval* precolored_fpu_intervals, *not_precolored_fpu_intervals; + + create_unhandled_lists(&precolored_cpu_intervals, ¬_precolored_cpu_intervals, is_precolored_cpu_interval, is_virtual_cpu_interval); + if (has_fpu_registers()) { + create_unhandled_lists(&precolored_fpu_intervals, ¬_precolored_fpu_intervals, is_precolored_fpu_interval, is_virtual_fpu_interval); +#ifdef ASSERT + } else { + // fpu register allocation is omitted because no virtual fpu registers are present + // just check this again... + create_unhandled_lists(&precolored_fpu_intervals, ¬_precolored_fpu_intervals, is_precolored_fpu_interval, is_virtual_fpu_interval); + assert(not_precolored_fpu_intervals == Interval::end(), "missed an uncolored fpu interval"); +#endif + } + + // allocate cpu registers + LinearScanWalker cpu_lsw(this, precolored_cpu_intervals, not_precolored_cpu_intervals); + cpu_lsw.walk(); + cpu_lsw.finish_allocation(); + + if (has_fpu_registers()) { + // allocate fpu registers + LinearScanWalker fpu_lsw(this, precolored_fpu_intervals, not_precolored_fpu_intervals); + fpu_lsw.walk(); + fpu_lsw.finish_allocation(); + } +} + + +// ********** Phase 6: resolve data flow +// (insert moves at edges between blocks if intervals have been split) + +// wrapper for Interval::split_child_at_op_id that performs a bailout in product mode +// instead of returning NULL +Interval* LinearScan::split_child_at_op_id(Interval* interval, int op_id, LIR_OpVisitState::OprMode mode) { + Interval* result = interval->split_child_at_op_id(op_id, mode); + if (result != NULL) { + return result; + } + + assert(false, "must find an interval, but do a clean bailout in product mode"); + result = new Interval(LIR_OprDesc::vreg_base); + result->assign_reg(0); + result->set_type(T_INT); + BAILOUT_("LinearScan: interval is NULL", result); +} + + +Interval* LinearScan::interval_at_block_begin(BlockBegin* block, int reg_num) { + assert(LinearScan::nof_regs <= reg_num && reg_num < num_virtual_regs(), "register number out of bounds"); + assert(interval_at(reg_num) != NULL, "no interval found"); + + return split_child_at_op_id(interval_at(reg_num), block->first_lir_instruction_id(), LIR_OpVisitState::outputMode); +} + +Interval* LinearScan::interval_at_block_end(BlockBegin* block, int reg_num) { + assert(LinearScan::nof_regs <= reg_num && reg_num < num_virtual_regs(), "register number out of bounds"); + assert(interval_at(reg_num) != NULL, "no interval found"); + + return split_child_at_op_id(interval_at(reg_num), block->last_lir_instruction_id() + 1, LIR_OpVisitState::outputMode); +} + +Interval* LinearScan::interval_at_op_id(int reg_num, int op_id) { + assert(LinearScan::nof_regs <= reg_num && reg_num < num_virtual_regs(), "register number out of bounds"); + assert(interval_at(reg_num) != NULL, "no interval found"); + + return split_child_at_op_id(interval_at(reg_num), op_id, LIR_OpVisitState::inputMode); +} + + +void LinearScan::resolve_collect_mappings(BlockBegin* from_block, BlockBegin* to_block, MoveResolver &move_resolver) { + DEBUG_ONLY(move_resolver.check_empty()); + + const int num_regs = num_virtual_regs(); + const int size = live_set_size(); + const BitMap live_at_edge = to_block->live_in(); + + // visit all registers where the live_at_edge bit is set + for (int r = live_at_edge.get_next_one_offset(0, size); r < size; r = live_at_edge.get_next_one_offset(r + 1, size)) { + assert(r < num_regs, "live information set for not exisiting interval"); + assert(from_block->live_out().at(r) && to_block->live_in().at(r), "interval not live at this edge"); + + Interval* from_interval = interval_at_block_end(from_block, r); + Interval* to_interval = interval_at_block_begin(to_block, r); + + if (from_interval != to_interval && (from_interval->assigned_reg() != to_interval->assigned_reg() || from_interval->assigned_regHi() != to_interval->assigned_regHi())) { + // need to insert move instruction + move_resolver.add_mapping(from_interval, to_interval); + } + } +} + + +void LinearScan::resolve_find_insert_pos(BlockBegin* from_block, BlockBegin* to_block, MoveResolver &move_resolver) { + if (from_block->number_of_sux() <= 1) { + TRACE_LINEAR_SCAN(4, tty->print_cr("inserting moves at end of from_block B%d", from_block->block_id())); + + LIR_OpList* instructions = from_block->lir()->instructions_list(); + LIR_OpBranch* branch = instructions->last()->as_OpBranch(); + if (branch != NULL) { + // insert moves before branch + assert(branch->cond() == lir_cond_always, "block does not end with an unconditional jump"); + move_resolver.set_insert_position(from_block->lir(), instructions->length() - 2); + } else { + move_resolver.set_insert_position(from_block->lir(), instructions->length() - 1); + } + + } else { + TRACE_LINEAR_SCAN(4, tty->print_cr("inserting moves at beginning of to_block B%d", to_block->block_id())); +#ifdef ASSERT + assert(from_block->lir()->instructions_list()->at(0)->as_OpLabel() != NULL, "block does not start with a label"); + + // because the number of predecessor edges matches the number of + // successor edges, blocks which are reached by switch statements + // may have be more than one predecessor but it will be guaranteed + // that all predecessors will be the same. + for (int i = 0; i < to_block->number_of_preds(); i++) { + assert(from_block == to_block->pred_at(i), "all critical edges must be broken"); + } +#endif + + move_resolver.set_insert_position(to_block->lir(), 0); + } +} + + +// insert necessary moves (spilling or reloading) at edges between blocks if interval has been split +void LinearScan::resolve_data_flow() { + TIME_LINEAR_SCAN(timer_resolve_data_flow); + + int num_blocks = block_count(); + MoveResolver move_resolver(this); + BitMap block_completed(num_blocks); block_completed.clear(); + BitMap already_resolved(num_blocks); already_resolved.clear(); + + int i; + for (i = 0; i < num_blocks; i++) { + BlockBegin* block = block_at(i); + + // check if block has only one predecessor and only one successor + if (block->number_of_preds() == 1 && block->number_of_sux() == 1 && block->number_of_exception_handlers() == 0) { + LIR_OpList* instructions = block->lir()->instructions_list(); + assert(instructions->at(0)->code() == lir_label, "block must start with label"); + assert(instructions->last()->code() == lir_branch, "block with successors must end with branch"); + assert(instructions->last()->as_OpBranch()->cond() == lir_cond_always, "block with successor must end with unconditional branch"); + + // check if block is empty (only label and branch) + if (instructions->length() == 2) { + BlockBegin* pred = block->pred_at(0); + BlockBegin* sux = block->sux_at(0); + + // prevent optimization of two consecutive blocks + if (!block_completed.at(pred->linear_scan_number()) && !block_completed.at(sux->linear_scan_number())) { + TRACE_LINEAR_SCAN(3, tty->print_cr("**** optimizing empty block B%d (pred: B%d, sux: B%d)", block->block_id(), pred->block_id(), sux->block_id())); + block_completed.set_bit(block->linear_scan_number()); + + // directly resolve between pred and sux (without looking at the empty block between) + resolve_collect_mappings(pred, sux, move_resolver); + if (move_resolver.has_mappings()) { + move_resolver.set_insert_position(block->lir(), 0); + move_resolver.resolve_and_append_moves(); + } + } + } + } + } + + + for (i = 0; i < num_blocks; i++) { + if (!block_completed.at(i)) { + BlockBegin* from_block = block_at(i); + already_resolved.set_from(block_completed); + + int num_sux = from_block->number_of_sux(); + for (int s = 0; s < num_sux; s++) { + BlockBegin* to_block = from_block->sux_at(s); + + // check for duplicate edges between the same blocks (can happen with switch blocks) + if (!already_resolved.at(to_block->linear_scan_number())) { + TRACE_LINEAR_SCAN(3, tty->print_cr("**** processing edge between B%d and B%d", from_block->block_id(), to_block->block_id())); + already_resolved.set_bit(to_block->linear_scan_number()); + + // collect all intervals that have been split between from_block and to_block + resolve_collect_mappings(from_block, to_block, move_resolver); + if (move_resolver.has_mappings()) { + resolve_find_insert_pos(from_block, to_block, move_resolver); + move_resolver.resolve_and_append_moves(); + } + } + } + } + } +} + + +void LinearScan::resolve_exception_entry(BlockBegin* block, int reg_num, MoveResolver &move_resolver) { + if (interval_at(reg_num) == NULL) { + // if a phi function is never used, no interval is created -> ignore this + return; + } + + Interval* interval = interval_at_block_begin(block, reg_num); + int reg = interval->assigned_reg(); + int regHi = interval->assigned_regHi(); + + if ((reg < nof_regs && interval->always_in_memory()) || + (use_fpu_stack_allocation() && reg >= pd_first_fpu_reg && reg <= pd_last_fpu_reg)) { + // the interval is split to get a short range that is located on the stack + // in the following two cases: + // * the interval started in memory (e.g. method parameter), but is currently in a register + // this is an optimization for exception handling that reduces the number of moves that + // are necessary for resolving the states when an exception uses this exception handler + // * the interval would be on the fpu stack at the begin of the exception handler + // this is not allowed because of the complicated fpu stack handling on Intel + + // range that will be spilled to memory + int from_op_id = block->first_lir_instruction_id(); + int to_op_id = from_op_id + 1; // short live range of length 1 + assert(interval->from() <= from_op_id && interval->to() >= to_op_id, + "no split allowed between exception entry and first instruction"); + + if (interval->from() != from_op_id) { + // the part before from_op_id is unchanged + interval = interval->split(from_op_id); + interval->assign_reg(reg, regHi); + append_interval(interval); + } + assert(interval->from() == from_op_id, "must be true now"); + + Interval* spilled_part = interval; + if (interval->to() != to_op_id) { + // the part after to_op_id is unchanged + spilled_part = interval->split_from_start(to_op_id); + append_interval(spilled_part); + move_resolver.add_mapping(spilled_part, interval); + } + assign_spill_slot(spilled_part); + + assert(spilled_part->from() == from_op_id && spilled_part->to() == to_op_id, "just checking"); + } +} + +void LinearScan::resolve_exception_entry(BlockBegin* block, MoveResolver &move_resolver) { + assert(block->is_set(BlockBegin::exception_entry_flag), "should not call otherwise"); + DEBUG_ONLY(move_resolver.check_empty()); + + // visit all registers where the live_in bit is set + int size = live_set_size(); + for (int r = block->live_in().get_next_one_offset(0, size); r < size; r = block->live_in().get_next_one_offset(r + 1, size)) { + resolve_exception_entry(block, r, move_resolver); + } + + // the live_in bits are not set for phi functions of the xhandler entry, so iterate them separately + for_each_phi_fun(block, phi, + resolve_exception_entry(block, phi->operand()->vreg_number(), move_resolver) + ); + + if (move_resolver.has_mappings()) { + // insert moves after first instruction + move_resolver.set_insert_position(block->lir(), 1); + move_resolver.resolve_and_append_moves(); + } +} + + +void LinearScan::resolve_exception_edge(XHandler* handler, int throwing_op_id, int reg_num, Phi* phi, MoveResolver &move_resolver) { + if (interval_at(reg_num) == NULL) { + // if a phi function is never used, no interval is created -> ignore this + return; + } + + // the computation of to_interval is equal to resolve_collect_mappings, + // but from_interval is more complicated because of phi functions + BlockBegin* to_block = handler->entry_block(); + Interval* to_interval = interval_at_block_begin(to_block, reg_num); + + if (phi != NULL) { + // phi function of the exception entry block + // no moves are created for this phi function in the LIR_Generator, so the + // interval at the throwing instruction must be searched using the operands + // of the phi function + Value from_value = phi->operand_at(handler->phi_operand()); + + // with phi functions it can happen that the same from_value is used in + // multiple mappings, so notify move-resolver that this is allowed + move_resolver.set_multiple_reads_allowed(); + + Constant* con = from_value->as_Constant(); + if (con != NULL && !con->is_pinned()) { + // unpinned constants may have no register, so add mapping from constant to interval + move_resolver.add_mapping(LIR_OprFact::value_type(con->type()), to_interval); + } else { + // search split child at the throwing op_id + Interval* from_interval = interval_at_op_id(from_value->operand()->vreg_number(), throwing_op_id); + move_resolver.add_mapping(from_interval, to_interval); + } + + } else { + // no phi function, so use reg_num also for from_interval + // search split child at the throwing op_id + Interval* from_interval = interval_at_op_id(reg_num, throwing_op_id); + if (from_interval != to_interval) { + // optimization to reduce number of moves: when to_interval is on stack and + // the stack slot is known to be always correct, then no move is necessary + if (!from_interval->always_in_memory() || from_interval->canonical_spill_slot() != to_interval->assigned_reg()) { + move_resolver.add_mapping(from_interval, to_interval); + } + } + } +} + +void LinearScan::resolve_exception_edge(XHandler* handler, int throwing_op_id, MoveResolver &move_resolver) { + TRACE_LINEAR_SCAN(4, tty->print_cr("resolving exception handler B%d: throwing_op_id=%d", handler->entry_block()->block_id(), throwing_op_id)); + + DEBUG_ONLY(move_resolver.check_empty()); + assert(handler->lir_op_id() == -1, "already processed this xhandler"); + DEBUG_ONLY(handler->set_lir_op_id(throwing_op_id)); + assert(handler->entry_code() == NULL, "code already present"); + + // visit all registers where the live_in bit is set + BlockBegin* block = handler->entry_block(); + int size = live_set_size(); + for (int r = block->live_in().get_next_one_offset(0, size); r < size; r = block->live_in().get_next_one_offset(r + 1, size)) { + resolve_exception_edge(handler, throwing_op_id, r, NULL, move_resolver); + } + + // the live_in bits are not set for phi functions of the xhandler entry, so iterate them separately + for_each_phi_fun(block, phi, + resolve_exception_edge(handler, throwing_op_id, phi->operand()->vreg_number(), phi, move_resolver) + ); + + if (move_resolver.has_mappings()) { + LIR_List* entry_code = new LIR_List(compilation()); + move_resolver.set_insert_position(entry_code, 0); + move_resolver.resolve_and_append_moves(); + + entry_code->jump(handler->entry_block()); + handler->set_entry_code(entry_code); + } +} + + +void LinearScan::resolve_exception_handlers() { + MoveResolver move_resolver(this); + LIR_OpVisitState visitor; + int num_blocks = block_count(); + + int i; + for (i = 0; i < num_blocks; i++) { + BlockBegin* block = block_at(i); + if (block->is_set(BlockBegin::exception_entry_flag)) { + resolve_exception_entry(block, move_resolver); + } + } + + for (i = 0; i < num_blocks; i++) { + BlockBegin* block = block_at(i); + LIR_List* ops = block->lir(); + int num_ops = ops->length(); + + // iterate all instructions of the block. skip the first because it is always a label + assert(visitor.no_operands(ops->at(0)), "first operation must always be a label"); + for (int j = 1; j < num_ops; j++) { + LIR_Op* op = ops->at(j); + int op_id = op->id(); + + if (op_id != -1 && has_info(op_id)) { + // visit operation to collect all operands + visitor.visit(op); + assert(visitor.info_count() > 0, "should not visit otherwise"); + + XHandlers* xhandlers = visitor.all_xhandler(); + int n = xhandlers->length(); + for (int k = 0; k < n; k++) { + resolve_exception_edge(xhandlers->handler_at(k), op_id, move_resolver); + } + +#ifdef ASSERT + } else { + visitor.visit(op); + assert(visitor.all_xhandler()->length() == 0, "missed exception handler"); +#endif + } + } + } +} + + +// ********** Phase 7: assign register numbers back to LIR +// (includes computation of debug information and oop maps) + +VMReg LinearScan::vm_reg_for_interval(Interval* interval) { + VMReg reg = interval->cached_vm_reg(); + if (!reg->is_valid() ) { + reg = vm_reg_for_operand(operand_for_interval(interval)); + interval->set_cached_vm_reg(reg); + } + assert(reg == vm_reg_for_operand(operand_for_interval(interval)), "wrong cached value"); + return reg; +} + +VMReg LinearScan::vm_reg_for_operand(LIR_Opr opr) { + assert(opr->is_oop(), "currently only implemented for oop operands"); + return frame_map()->regname(opr); +} + + +LIR_Opr LinearScan::operand_for_interval(Interval* interval) { + LIR_Opr opr = interval->cached_opr(); + if (opr->is_illegal()) { + opr = calc_operand_for_interval(interval); + interval->set_cached_opr(opr); + } + + assert(opr == calc_operand_for_interval(interval), "wrong cached value"); + return opr; +} + +LIR_Opr LinearScan::calc_operand_for_interval(const Interval* interval) { + int assigned_reg = interval->assigned_reg(); + BasicType type = interval->type(); + + if (assigned_reg >= nof_regs) { + // stack slot + assert(interval->assigned_regHi() == any_reg, "must not have hi register"); + return LIR_OprFact::stack(assigned_reg - nof_regs, type); + + } else { + // register + switch (type) { + case T_OBJECT: { + assert(assigned_reg >= pd_first_cpu_reg && assigned_reg <= pd_last_cpu_reg, "no cpu register"); + assert(interval->assigned_regHi() == any_reg, "must not have hi register"); + return LIR_OprFact::single_cpu_oop(assigned_reg); + } + + case T_INT: { + assert(assigned_reg >= pd_first_cpu_reg && assigned_reg <= pd_last_cpu_reg, "no cpu register"); + assert(interval->assigned_regHi() == any_reg, "must not have hi register"); + return LIR_OprFact::single_cpu(assigned_reg); + } + + case T_LONG: { + int assigned_regHi = interval->assigned_regHi(); + assert(assigned_reg >= pd_first_cpu_reg && assigned_reg <= pd_last_cpu_reg, "no cpu register"); + assert(num_physical_regs(T_LONG) == 1 || + (assigned_regHi >= pd_first_cpu_reg && assigned_regHi <= pd_last_cpu_reg), "no cpu register"); + + assert(assigned_reg != assigned_regHi, "invalid allocation"); + assert(num_physical_regs(T_LONG) == 1 || assigned_reg < assigned_regHi, + "register numbers must be sorted (ensure that e.g. a move from eax,ebx to ebx,eax can not occur)"); + assert((assigned_regHi != any_reg) ^ (num_physical_regs(T_LONG) == 1), "must be match"); + if (requires_adjacent_regs(T_LONG)) { + assert(assigned_reg % 2 == 0 && assigned_reg + 1 == assigned_regHi, "must be sequential and even"); + } + +#ifdef SPARC +#ifdef _LP64 + return LIR_OprFact::double_cpu(assigned_reg, assigned_reg); +#else + return LIR_OprFact::double_cpu(assigned_regHi, assigned_reg); +#endif +#else + return LIR_OprFact::double_cpu(assigned_reg, assigned_regHi); +#endif + } + + case T_FLOAT: { +#ifdef IA32 + if (UseSSE >= 1) { + assert(assigned_reg >= pd_first_xmm_reg && assigned_reg <= pd_last_xmm_reg, "no xmm register"); + assert(interval->assigned_regHi() == any_reg, "must not have hi register"); + return LIR_OprFact::single_xmm(assigned_reg - pd_first_xmm_reg); + } +#endif + + assert(assigned_reg >= pd_first_fpu_reg && assigned_reg <= pd_last_fpu_reg, "no fpu register"); + assert(interval->assigned_regHi() == any_reg, "must not have hi register"); + return LIR_OprFact::single_fpu(assigned_reg - pd_first_fpu_reg); + } + + case T_DOUBLE: { +#ifdef IA32 + if (UseSSE >= 2) { + assert(assigned_reg >= pd_first_xmm_reg && assigned_reg <= pd_last_xmm_reg, "no xmm register"); + assert(interval->assigned_regHi() == any_reg, "must not have hi register (double xmm values are stored in one register)"); + return LIR_OprFact::double_xmm(assigned_reg - pd_first_xmm_reg); + } +#endif + +#ifdef SPARC + assert(assigned_reg >= pd_first_fpu_reg && assigned_reg <= pd_last_fpu_reg, "no fpu register"); + assert(interval->assigned_regHi() >= pd_first_fpu_reg && interval->assigned_regHi() <= pd_last_fpu_reg, "no fpu register"); + assert(assigned_reg % 2 == 0 && assigned_reg + 1 == interval->assigned_regHi(), "must be sequential and even"); + LIR_Opr result = LIR_OprFact::double_fpu(interval->assigned_regHi() - pd_first_fpu_reg, assigned_reg - pd_first_fpu_reg); +#else + assert(assigned_reg >= pd_first_fpu_reg && assigned_reg <= pd_last_fpu_reg, "no fpu register"); + assert(interval->assigned_regHi() == any_reg, "must not have hi register (double fpu values are stored in one register on Intel)"); + LIR_Opr result = LIR_OprFact::double_fpu(assigned_reg - pd_first_fpu_reg); +#endif + return result; + } + + default: { + ShouldNotReachHere(); + return LIR_OprFact::illegalOpr; + } + } + } +} + +LIR_Opr LinearScan::canonical_spill_opr(Interval* interval) { + assert(interval->canonical_spill_slot() >= nof_regs, "canonical spill slot not set"); + return LIR_OprFact::stack(interval->canonical_spill_slot() - nof_regs, interval->type()); +} + +LIR_Opr LinearScan::color_lir_opr(LIR_Opr opr, int op_id, LIR_OpVisitState::OprMode mode) { + assert(opr->is_virtual(), "should not call this otherwise"); + + Interval* interval = interval_at(opr->vreg_number()); + assert(interval != NULL, "interval must exist"); + + if (op_id != -1) { +#ifdef ASSERT + BlockBegin* block = block_of_op_with_id(op_id); + if (block->number_of_sux() <= 1 && op_id == block->last_lir_instruction_id()) { + // check if spill moves could have been appended at the end of this block, but + // before the branch instruction. So the split child information for this branch would + // be incorrect. + LIR_OpBranch* branch = block->lir()->instructions_list()->last()->as_OpBranch(); + if (branch != NULL) { + if (block->live_out().at(opr->vreg_number())) { + assert(branch->cond() == lir_cond_always, "block does not end with an unconditional jump"); + assert(false, "can't get split child for the last branch of a block because the information would be incorrect (moves are inserted before the branch in resolve_data_flow)"); + } + } + } +#endif + + // operands are not changed when an interval is split during allocation, + // so search the right interval here + interval = split_child_at_op_id(interval, op_id, mode); + } + + LIR_Opr res = operand_for_interval(interval); + +#ifdef IA32 + // new semantic for is_last_use: not only set on definite end of interval, + // but also before hole + // This may still miss some cases (e.g. for dead values), but it is not necessary that the + // last use information is completely correct + // information is only needed for fpu stack allocation + if (res->is_fpu_register()) { + if (opr->is_last_use() || op_id == interval->to() || (op_id != -1 && interval->has_hole_between(op_id, op_id + 1))) { + assert(op_id == -1 || !is_block_begin(op_id), "holes at begin of block may also result from control flow"); + res = res->make_last_use(); + } + } +#endif + + assert(!gen()->is_vreg_flag_set(opr->vreg_number(), LIRGenerator::callee_saved) || !FrameMap::is_caller_save_register(res), "bad allocation"); + + return res; +} + + +#ifdef ASSERT +// some methods used to check correctness of debug information + +void assert_no_register_values(GrowableArray* values) { + if (values == NULL) { + return; + } + + for (int i = 0; i < values->length(); i++) { + ScopeValue* value = values->at(i); + + if (value->is_location()) { + Location location = ((LocationValue*)value)->location(); + assert(location.where() == Location::on_stack, "value is in register"); + } + } +} + +void assert_no_register_values(GrowableArray* values) { + if (values == NULL) { + return; + } + + for (int i = 0; i < values->length(); i++) { + MonitorValue* value = values->at(i); + + if (value->owner()->is_location()) { + Location location = ((LocationValue*)value->owner())->location(); + assert(location.where() == Location::on_stack, "owner is in register"); + } + assert(value->basic_lock().where() == Location::on_stack, "basic_lock is in register"); + } +} + +void assert_equal(Location l1, Location l2) { + assert(l1.where() == l2.where() && l1.type() == l2.type() && l1.offset() == l2.offset(), ""); +} + +void assert_equal(ScopeValue* v1, ScopeValue* v2) { + if (v1->is_location()) { + assert(v2->is_location(), ""); + assert_equal(((LocationValue*)v1)->location(), ((LocationValue*)v2)->location()); + } else if (v1->is_constant_int()) { + assert(v2->is_constant_int(), ""); + assert(((ConstantIntValue*)v1)->value() == ((ConstantIntValue*)v2)->value(), ""); + } else if (v1->is_constant_double()) { + assert(v2->is_constant_double(), ""); + assert(((ConstantDoubleValue*)v1)->value() == ((ConstantDoubleValue*)v2)->value(), ""); + } else if (v1->is_constant_long()) { + assert(v2->is_constant_long(), ""); + assert(((ConstantLongValue*)v1)->value() == ((ConstantLongValue*)v2)->value(), ""); + } else if (v1->is_constant_oop()) { + assert(v2->is_constant_oop(), ""); + assert(((ConstantOopWriteValue*)v1)->value() == ((ConstantOopWriteValue*)v2)->value(), ""); + } else { + ShouldNotReachHere(); + } +} + +void assert_equal(MonitorValue* m1, MonitorValue* m2) { + assert_equal(m1->owner(), m2->owner()); + assert_equal(m1->basic_lock(), m2->basic_lock()); +} + +void assert_equal(IRScopeDebugInfo* d1, IRScopeDebugInfo* d2) { + assert(d1->scope() == d2->scope(), "not equal"); + assert(d1->bci() == d2->bci(), "not equal"); + + if (d1->locals() != NULL) { + assert(d1->locals() != NULL && d2->locals() != NULL, "not equal"); + assert(d1->locals()->length() == d2->locals()->length(), "not equal"); + for (int i = 0; i < d1->locals()->length(); i++) { + assert_equal(d1->locals()->at(i), d2->locals()->at(i)); + } + } else { + assert(d1->locals() == NULL && d2->locals() == NULL, "not equal"); + } + + if (d1->expressions() != NULL) { + assert(d1->expressions() != NULL && d2->expressions() != NULL, "not equal"); + assert(d1->expressions()->length() == d2->expressions()->length(), "not equal"); + for (int i = 0; i < d1->expressions()->length(); i++) { + assert_equal(d1->expressions()->at(i), d2->expressions()->at(i)); + } + } else { + assert(d1->expressions() == NULL && d2->expressions() == NULL, "not equal"); + } + + if (d1->monitors() != NULL) { + assert(d1->monitors() != NULL && d2->monitors() != NULL, "not equal"); + assert(d1->monitors()->length() == d2->monitors()->length(), "not equal"); + for (int i = 0; i < d1->monitors()->length(); i++) { + assert_equal(d1->monitors()->at(i), d2->monitors()->at(i)); + } + } else { + assert(d1->monitors() == NULL && d2->monitors() == NULL, "not equal"); + } + + if (d1->caller() != NULL) { + assert(d1->caller() != NULL && d2->caller() != NULL, "not equal"); + assert_equal(d1->caller(), d2->caller()); + } else { + assert(d1->caller() == NULL && d2->caller() == NULL, "not equal"); + } +} + +void check_stack_depth(CodeEmitInfo* info, int stack_end) { + if (info->bci() != SynchronizationEntryBCI && !info->scope()->method()->is_native()) { + Bytecodes::Code code = info->scope()->method()->java_code_at_bci(info->bci()); + switch (code) { + case Bytecodes::_ifnull : // fall through + case Bytecodes::_ifnonnull : // fall through + case Bytecodes::_ifeq : // fall through + case Bytecodes::_ifne : // fall through + case Bytecodes::_iflt : // fall through + case Bytecodes::_ifge : // fall through + case Bytecodes::_ifgt : // fall through + case Bytecodes::_ifle : // fall through + case Bytecodes::_if_icmpeq : // fall through + case Bytecodes::_if_icmpne : // fall through + case Bytecodes::_if_icmplt : // fall through + case Bytecodes::_if_icmpge : // fall through + case Bytecodes::_if_icmpgt : // fall through + case Bytecodes::_if_icmple : // fall through + case Bytecodes::_if_acmpeq : // fall through + case Bytecodes::_if_acmpne : + assert(stack_end >= -Bytecodes::depth(code), "must have non-empty expression stack at if bytecode"); + break; + } + } +} + +#endif // ASSERT + + +IntervalWalker* LinearScan::init_compute_oop_maps() { + // setup lists of potential oops for walking + Interval* oop_intervals; + Interval* non_oop_intervals; + + create_unhandled_lists(&oop_intervals, &non_oop_intervals, is_oop_interval, NULL); + + // intervals that have no oops inside need not to be processed + // to ensure a walking until the last instruction id, add a dummy interval + // with a high operation id + non_oop_intervals = new Interval(any_reg); + non_oop_intervals->add_range(max_jint - 2, max_jint - 1); + + return new IntervalWalker(this, oop_intervals, non_oop_intervals); +} + + +OopMap* LinearScan::compute_oop_map(IntervalWalker* iw, LIR_Op* op, CodeEmitInfo* info, bool is_call_site) { + TRACE_LINEAR_SCAN(3, tty->print_cr("creating oop map at op_id %d", op->id())); + + // walk before the current operation -> intervals that start at + // the operation (= output operands of the operation) are not + // included in the oop map + iw->walk_before(op->id()); + + int frame_size = frame_map()->framesize(); + int arg_count = frame_map()->oop_map_arg_count(); + OopMap* map = new OopMap(frame_size, arg_count); + + // Check if this is a patch site. + bool is_patch_info = false; + if (op->code() == lir_move) { + assert(!is_call_site, "move must not be a call site"); + assert(op->as_Op1() != NULL, "move must be LIR_Op1"); + LIR_Op1* move = (LIR_Op1*)op; + + is_patch_info = move->patch_code() != lir_patch_none; + } + + // Iterate through active intervals + for (Interval* interval = iw->active_first(fixedKind); interval != Interval::end(); interval = interval->next()) { + int assigned_reg = interval->assigned_reg(); + + assert(interval->current_from() <= op->id() && op->id() <= interval->current_to(), "interval should not be active otherwise"); + assert(interval->assigned_regHi() == any_reg, "oop must be single word"); + assert(interval->reg_num() >= LIR_OprDesc::vreg_base, "fixed interval found"); + + // Check if this range covers the instruction. Intervals that + // start or end at the current operation are not included in the + // oop map, except in the case of patching moves. For patching + // moves, any intervals which end at this instruction are included + // in the oop map since we may safepoint while doing the patch + // before we've consumed the inputs. + if (is_patch_info || op->id() < interval->current_to()) { + + // caller-save registers must not be included into oop-maps at calls + assert(!is_call_site || assigned_reg >= nof_regs || !is_caller_save(assigned_reg), "interval is in a caller-save register at a call -> register will be overwritten"); + + VMReg name = vm_reg_for_interval(interval); + map->set_oop(name); + + // Spill optimization: when the stack value is guaranteed to be always correct, + // then it must be added to the oop map even if the interval is currently in a register + if (interval->always_in_memory() && + op->id() > interval->spill_definition_pos() && + interval->assigned_reg() != interval->canonical_spill_slot()) { + assert(interval->spill_definition_pos() > 0, "position not set correctly"); + assert(interval->canonical_spill_slot() >= LinearScan::nof_regs, "no spill slot assigned"); + assert(interval->assigned_reg() < LinearScan::nof_regs, "interval is on stack, so stack slot is registered twice"); + + map->set_oop(frame_map()->slot_regname(interval->canonical_spill_slot() - LinearScan::nof_regs)); + } + } + } + + // add oops from lock stack + assert(info->stack() != NULL, "CodeEmitInfo must always have a stack"); + int locks_count = info->stack()->locks_size(); + for (int i = 0; i < locks_count; i++) { + map->set_oop(frame_map()->monitor_object_regname(i)); + } + + return map; +} + + +void LinearScan::compute_oop_map(IntervalWalker* iw, const LIR_OpVisitState &visitor, LIR_Op* op) { + assert(visitor.info_count() > 0, "no oop map needed"); + + // compute oop_map only for first CodeEmitInfo + // because it is (in most cases) equal for all other infos of the same operation + CodeEmitInfo* first_info = visitor.info_at(0); + OopMap* first_oop_map = compute_oop_map(iw, op, first_info, visitor.has_call()); + + for (int i = 0; i < visitor.info_count(); i++) { + CodeEmitInfo* info = visitor.info_at(i); + OopMap* oop_map = first_oop_map; + + if (info->stack()->locks_size() != first_info->stack()->locks_size()) { + // this info has a different number of locks then the precomputed oop map + // (possible for lock and unlock instructions) -> compute oop map with + // correct lock information + oop_map = compute_oop_map(iw, op, info, visitor.has_call()); + } + + if (info->_oop_map == NULL) { + info->_oop_map = oop_map; + } else { + // a CodeEmitInfo can not be shared between different LIR-instructions + // because interval splitting can occur anywhere between two instructions + // and so the oop maps must be different + // -> check if the already set oop_map is exactly the one calculated for this operation + assert(info->_oop_map == oop_map, "same CodeEmitInfo used for multiple LIR instructions"); + } + } +} + + +// frequently used constants +ConstantOopWriteValue LinearScan::_oop_null_scope_value = ConstantOopWriteValue(NULL); +ConstantIntValue LinearScan::_int_m1_scope_value = ConstantIntValue(-1); +ConstantIntValue LinearScan::_int_0_scope_value = ConstantIntValue(0); +ConstantIntValue LinearScan::_int_1_scope_value = ConstantIntValue(1); +ConstantIntValue LinearScan::_int_2_scope_value = ConstantIntValue(2); +LocationValue _illegal_value = LocationValue(Location()); + +void LinearScan::init_compute_debug_info() { + // cache for frequently used scope values + // (cpu registers and stack slots) + _scope_value_cache = ScopeValueArray((LinearScan::nof_cpu_regs + frame_map()->argcount() + max_spills()) * 2, NULL); +} + +MonitorValue* LinearScan::location_for_monitor_index(int monitor_index) { + Location loc; + if (!frame_map()->location_for_monitor_object(monitor_index, &loc)) { + bailout("too large frame"); + } + ScopeValue* object_scope_value = new LocationValue(loc); + + if (!frame_map()->location_for_monitor_lock(monitor_index, &loc)) { + bailout("too large frame"); + } + return new MonitorValue(object_scope_value, loc); +} + +LocationValue* LinearScan::location_for_name(int name, Location::Type loc_type) { + Location loc; + if (!frame_map()->locations_for_slot(name, loc_type, &loc)) { + bailout("too large frame"); + } + return new LocationValue(loc); +} + + +int LinearScan::append_scope_value_for_constant(LIR_Opr opr, GrowableArray* scope_values) { + assert(opr->is_constant(), "should not be called otherwise"); + + LIR_Const* c = opr->as_constant_ptr(); + BasicType t = c->type(); + switch (t) { + case T_OBJECT: { + jobject value = c->as_jobject(); + if (value == NULL) { + scope_values->append(&_oop_null_scope_value); + } else { + scope_values->append(new ConstantOopWriteValue(c->as_jobject())); + } + return 1; + } + + case T_INT: // fall through + case T_FLOAT: { + int value = c->as_jint_bits(); + switch (value) { + case -1: scope_values->append(&_int_m1_scope_value); break; + case 0: scope_values->append(&_int_0_scope_value); break; + case 1: scope_values->append(&_int_1_scope_value); break; + case 2: scope_values->append(&_int_2_scope_value); break; + default: scope_values->append(new ConstantIntValue(c->as_jint_bits())); break; + } + return 1; + } + + case T_LONG: // fall through + case T_DOUBLE: { + if (hi_word_offset_in_bytes > lo_word_offset_in_bytes) { + scope_values->append(new ConstantIntValue(c->as_jint_hi_bits())); + scope_values->append(new ConstantIntValue(c->as_jint_lo_bits())); + } else { + scope_values->append(new ConstantIntValue(c->as_jint_lo_bits())); + scope_values->append(new ConstantIntValue(c->as_jint_hi_bits())); + } + + return 2; + } + + default: + ShouldNotReachHere(); + } +} + +int LinearScan::append_scope_value_for_operand(LIR_Opr opr, GrowableArray* scope_values) { + if (opr->is_single_stack()) { + int stack_idx = opr->single_stack_ix(); + bool is_oop = opr->is_oop_register(); + int cache_idx = (stack_idx + LinearScan::nof_cpu_regs) * 2 + (is_oop ? 1 : 0); + + ScopeValue* sv = _scope_value_cache.at(cache_idx); + if (sv == NULL) { + Location::Type loc_type = is_oop ? Location::oop : Location::normal; + sv = location_for_name(stack_idx, loc_type); + _scope_value_cache.at_put(cache_idx, sv); + } + + // check if cached value is correct + DEBUG_ONLY(assert_equal(sv, location_for_name(stack_idx, is_oop ? Location::oop : Location::normal))); + + scope_values->append(sv); + return 1; + + } else if (opr->is_single_cpu()) { + bool is_oop = opr->is_oop_register(); + int cache_idx = opr->cpu_regnr() * 2 + (is_oop ? 1 : 0); + + ScopeValue* sv = _scope_value_cache.at(cache_idx); + if (sv == NULL) { + Location::Type loc_type = is_oop ? Location::oop : Location::normal; + VMReg rname = frame_map()->regname(opr); + sv = new LocationValue(Location::new_reg_loc(loc_type, rname)); + _scope_value_cache.at_put(cache_idx, sv); + } + + // check if cached value is correct + DEBUG_ONLY(assert_equal(sv, new LocationValue(Location::new_reg_loc(is_oop ? Location::oop : Location::normal, frame_map()->regname(opr))))); + + scope_values->append(sv); + return 1; + +#ifdef IA32 + } else if (opr->is_single_xmm()) { + VMReg rname = opr->as_xmm_float_reg()->as_VMReg(); + LocationValue* sv = new LocationValue(Location::new_reg_loc(Location::normal, rname)); + + scope_values->append(sv); + return 1; +#endif + + } else if (opr->is_single_fpu()) { +#ifdef IA32 + // the exact location of fpu stack values is only known + // during fpu stack allocation, so the stack allocator object + // must be present + assert(use_fpu_stack_allocation(), "should not have float stack values without fpu stack allocation (all floats must be SSE2)"); + assert(_fpu_stack_allocator != NULL, "must be present"); + opr = _fpu_stack_allocator->to_fpu_stack(opr); +#endif + + Location::Type loc_type = float_saved_as_double ? Location::float_in_dbl : Location::normal; + VMReg rname = frame_map()->fpu_regname(opr->fpu_regnr()); + LocationValue* sv = new LocationValue(Location::new_reg_loc(loc_type, rname)); + + scope_values->append(sv); + return 1; + + } else { + // double-size operands + + ScopeValue* first; + ScopeValue* second; + + if (opr->is_double_stack()) { + Location loc1, loc2; + if (!frame_map()->locations_for_slot(opr->double_stack_ix(), Location::normal, &loc1, &loc2)) { + bailout("too large frame"); + } + first = new LocationValue(loc1); + second = new LocationValue(loc2); + + } else if (opr->is_double_cpu()) { +#ifdef _LP64 + VMReg rname_first = opr->as_register_lo()->as_VMReg(); + first = new LocationValue(Location::new_reg_loc(Location::lng, rname_first)); + second = &_int_0_scope_value; +#else + VMReg rname_first = opr->as_register_lo()->as_VMReg(); + VMReg rname_second = opr->as_register_hi()->as_VMReg(); + + if (hi_word_offset_in_bytes < lo_word_offset_in_bytes) { + // lo/hi and swapped relative to first and second, so swap them + VMReg tmp = rname_first; + rname_first = rname_second; + rname_second = tmp; + } + + first = new LocationValue(Location::new_reg_loc(Location::normal, rname_first)); + second = new LocationValue(Location::new_reg_loc(Location::normal, rname_second)); +#endif + +#ifdef IA32 + } else if (opr->is_double_xmm()) { + assert(opr->fpu_regnrLo() == opr->fpu_regnrHi(), "assumed in calculation"); + VMReg rname_first = opr->as_xmm_double_reg()->as_VMReg(); + first = new LocationValue(Location::new_reg_loc(Location::normal, rname_first)); + // %%% This is probably a waste but we'll keep things as they were for now + if (true) { + VMReg rname_second = rname_first->next(); + second = new LocationValue(Location::new_reg_loc(Location::normal, rname_second)); + } +#endif + + } else if (opr->is_double_fpu()) { + // On SPARC, fpu_regnrLo/fpu_regnrHi represents the two halves of + // the double as float registers in the native ordering. On IA32, + // fpu_regnrLo is a FPU stack slot whose VMReg represents + // the low-order word of the double and fpu_regnrLo + 1 is the + // name for the other half. *first and *second must represent the + // least and most significant words, respectively. + +#ifdef IA32 + // the exact location of fpu stack values is only known + // during fpu stack allocation, so the stack allocator object + // must be present + assert(use_fpu_stack_allocation(), "should not have float stack values without fpu stack allocation (all floats must be SSE2)"); + assert(_fpu_stack_allocator != NULL, "must be present"); + opr = _fpu_stack_allocator->to_fpu_stack(opr); + + assert(opr->fpu_regnrLo() == opr->fpu_regnrHi(), "assumed in calculation (only fpu_regnrHi is used)"); +#endif +#ifdef SPARC + assert(opr->fpu_regnrLo() == opr->fpu_regnrHi() + 1, "assumed in calculation (only fpu_regnrHi is used)"); +#endif + + VMReg rname_first = frame_map()->fpu_regname(opr->fpu_regnrHi()); + + first = new LocationValue(Location::new_reg_loc(Location::normal, rname_first)); + // %%% This is probably a waste but we'll keep things as they were for now + if (true) { + VMReg rname_second = rname_first->next(); + second = new LocationValue(Location::new_reg_loc(Location::normal, rname_second)); + } + + } else { + ShouldNotReachHere(); + first = NULL; + second = NULL; + } + + assert(first != NULL && second != NULL, "must be set"); + // The convention the interpreter uses is that the second local + // holds the first raw word of the native double representation. + // This is actually reasonable, since locals and stack arrays + // grow downwards in all implementations. + // (If, on some machine, the interpreter's Java locals or stack + // were to grow upwards, the embedded doubles would be word-swapped.) + scope_values->append(second); + scope_values->append(first); + return 2; + } +} + + +int LinearScan::append_scope_value(int op_id, Value value, GrowableArray* scope_values) { + if (value != NULL) { + LIR_Opr opr = value->operand(); + Constant* con = value->as_Constant(); + + assert(con == NULL || opr->is_virtual() || opr->is_constant() || opr->is_illegal(), "asumption: Constant instructions have only constant operands (or illegal if constant is optimized away)"); + assert(con != NULL || opr->is_virtual(), "asumption: non-Constant instructions have only virtual operands"); + + if (con != NULL && !con->is_pinned() && !opr->is_constant()) { + // Unpinned constants may have a virtual operand for a part of the lifetime + // or may be illegal when it was optimized away, + // so always use a constant operand + opr = LIR_OprFact::value_type(con->type()); + } + assert(opr->is_virtual() || opr->is_constant(), "other cases not allowed here"); + + if (opr->is_virtual()) { + LIR_OpVisitState::OprMode mode = LIR_OpVisitState::inputMode; + + BlockBegin* block = block_of_op_with_id(op_id); + if (block->number_of_sux() == 1 && op_id == block->last_lir_instruction_id()) { + // generating debug information for the last instruction of a block. + // if this instruction is a branch, spill moves are inserted before this branch + // and so the wrong operand would be returned (spill moves at block boundaries are not + // considered in the live ranges of intervals) + // Solution: use the first op_id of the branch target block instead. + if (block->lir()->instructions_list()->last()->as_OpBranch() != NULL) { + if (block->live_out().at(opr->vreg_number())) { + op_id = block->sux_at(0)->first_lir_instruction_id(); + mode = LIR_OpVisitState::outputMode; + } + } + } + + // Get current location of operand + // The operand must be live because debug information is considered when building the intervals + // if the interval is not live, color_lir_opr will cause an assertion failure + opr = color_lir_opr(opr, op_id, mode); + assert(!has_call(op_id) || opr->is_stack() || !is_caller_save(reg_num(opr)), "can not have caller-save register operands at calls"); + + // Append to ScopeValue array + return append_scope_value_for_operand(opr, scope_values); + + } else { + assert(value->as_Constant() != NULL, "all other instructions have only virtual operands"); + assert(opr->is_constant(), "operand must be constant"); + + return append_scope_value_for_constant(opr, scope_values); + } + } else { + // append a dummy value because real value not needed + scope_values->append(&_illegal_value); + return 1; + } +} + + +IRScopeDebugInfo* LinearScan::compute_debug_info_for_scope(int op_id, IRScope* cur_scope, ValueStack* cur_state, ValueStack* innermost_state, int cur_bci, int stack_end, int locks_end) { + IRScopeDebugInfo* caller_debug_info = NULL; + int stack_begin, locks_begin; + + ValueStack* caller_state = cur_scope->caller_state(); + if (caller_state != NULL) { + // process recursively to compute outermost scope first + stack_begin = caller_state->stack_size(); + locks_begin = caller_state->locks_size(); + caller_debug_info = compute_debug_info_for_scope(op_id, cur_scope->caller(), caller_state, innermost_state, cur_scope->caller_bci(), stack_begin, locks_begin); + } else { + stack_begin = 0; + locks_begin = 0; + } + + // initialize these to null. + // If we don't need deopt info or there are no locals, expressions or monitors, + // then these get recorded as no information and avoids the allocation of 0 length arrays. + GrowableArray* locals = NULL; + GrowableArray* expressions = NULL; + GrowableArray* monitors = NULL; + + // describe local variable values + int nof_locals = cur_scope->method()->max_locals(); + if (nof_locals > 0) { + locals = new GrowableArray(nof_locals); + + int pos = 0; + while (pos < nof_locals) { + assert(pos < cur_state->locals_size(), "why not?"); + + Value local = cur_state->local_at(pos); + pos += append_scope_value(op_id, local, locals); + + assert(locals->length() == pos, "must match"); + } + assert(locals->length() == cur_scope->method()->max_locals(), "wrong number of locals"); + assert(locals->length() == cur_state->locals_size(), "wrong number of locals"); + } + + + // describe expression stack + // + // When we inline methods containing exception handlers, the + // "lock_stacks" are changed to preserve expression stack values + // in caller scopes when exception handlers are present. This + // can cause callee stacks to be smaller than caller stacks. + if (stack_end > innermost_state->stack_size()) { + stack_end = innermost_state->stack_size(); + } + + + + int nof_stack = stack_end - stack_begin; + if (nof_stack > 0) { + expressions = new GrowableArray(nof_stack); + + int pos = stack_begin; + while (pos < stack_end) { + Value expression = innermost_state->stack_at_inc(pos); + append_scope_value(op_id, expression, expressions); + + assert(expressions->length() + stack_begin == pos, "must match"); + } + } + + // describe monitors + assert(locks_begin <= locks_end, "error in scope iteration"); + int nof_locks = locks_end - locks_begin; + if (nof_locks > 0) { + monitors = new GrowableArray(nof_locks); + for (int i = locks_begin; i < locks_end; i++) { + monitors->append(location_for_monitor_index(i)); + } + } + + return new IRScopeDebugInfo(cur_scope, cur_bci, locals, expressions, monitors, caller_debug_info); +} + + +void LinearScan::compute_debug_info(CodeEmitInfo* info, int op_id) { + if (!compilation()->needs_debug_information()) { + return; + } + TRACE_LINEAR_SCAN(3, tty->print_cr("creating debug information at op_id %d", op_id)); + + IRScope* innermost_scope = info->scope(); + ValueStack* innermost_state = info->stack(); + + assert(innermost_scope != NULL && innermost_state != NULL, "why is it missing?"); + + int stack_end = innermost_state->stack_size(); + int locks_end = innermost_state->locks_size(); + + DEBUG_ONLY(check_stack_depth(info, stack_end)); + + if (info->_scope_debug_info == NULL) { + // compute debug information + info->_scope_debug_info = compute_debug_info_for_scope(op_id, innermost_scope, innermost_state, innermost_state, info->bci(), stack_end, locks_end); + } else { + // debug information already set. Check that it is correct from the current point of view + DEBUG_ONLY(assert_equal(info->_scope_debug_info, compute_debug_info_for_scope(op_id, innermost_scope, innermost_state, innermost_state, info->bci(), stack_end, locks_end))); + } +} + + +void LinearScan::assign_reg_num(LIR_OpList* instructions, IntervalWalker* iw) { + LIR_OpVisitState visitor; + int num_inst = instructions->length(); + bool has_dead = false; + + for (int j = 0; j < num_inst; j++) { + LIR_Op* op = instructions->at(j); + if (op == NULL) { // this can happen when spill-moves are removed in eliminate_spill_moves + has_dead = true; + continue; + } + int op_id = op->id(); + + // visit instruction to get list of operands + visitor.visit(op); + + // iterate all modes of the visitor and process all virtual operands + for_each_visitor_mode(mode) { + int n = visitor.opr_count(mode); + for (int k = 0; k < n; k++) { + LIR_Opr opr = visitor.opr_at(mode, k); + if (opr->is_virtual_register()) { + visitor.set_opr_at(mode, k, color_lir_opr(opr, op_id, mode)); + } + } + } + + if (visitor.info_count() > 0) { + // exception handling + if (compilation()->has_exception_handlers()) { + XHandlers* xhandlers = visitor.all_xhandler(); + int n = xhandlers->length(); + for (int k = 0; k < n; k++) { + XHandler* handler = xhandlers->handler_at(k); + if (handler->entry_code() != NULL) { + assign_reg_num(handler->entry_code()->instructions_list(), NULL); + } + } + } else { + assert(visitor.all_xhandler()->length() == 0, "missed exception handler"); + } + + // compute oop map + assert(iw != NULL, "needed for compute_oop_map"); + compute_oop_map(iw, visitor, op); + + // compute debug information + if (!use_fpu_stack_allocation()) { + // compute debug information if fpu stack allocation is not needed. + // when fpu stack allocation is needed, the debug information can not + // be computed here because the exact location of fpu operands is not known + // -> debug information is created inside the fpu stack allocator + int n = visitor.info_count(); + for (int k = 0; k < n; k++) { + compute_debug_info(visitor.info_at(k), op_id); + } + } + } + +#ifdef ASSERT + // make sure we haven't made the op invalid. + op->verify(); +#endif + +#ifndef _LP64 + // remove useless moves + if (op->code() == lir_move) { + assert(op->as_Op1() != NULL, "move must be LIR_Op1"); + LIR_Op1* move = (LIR_Op1*)op; + LIR_Opr src = move->in_opr(); + LIR_Opr dst = move->result_opr(); + if (dst == src || + !dst->is_pointer() && !src->is_pointer() && + src->is_same_register(dst)) { + instructions->at_put(j, NULL); + has_dead = true; + } + } +#endif + } + + if (has_dead) { + // iterate all instructions of the block and remove all null-values. + int insert_point = 0; + for (int j = 0; j < num_inst; j++) { + LIR_Op* op = instructions->at(j); + if (op != NULL) { + if (insert_point != j) { + instructions->at_put(insert_point, op); + } + insert_point++; + } + } + instructions->truncate(insert_point); + } +} + +void LinearScan::assign_reg_num() { + TIME_LINEAR_SCAN(timer_assign_reg_num); + + init_compute_debug_info(); + IntervalWalker* iw = init_compute_oop_maps(); + + int num_blocks = block_count(); + for (int i = 0; i < num_blocks; i++) { + BlockBegin* block = block_at(i); + assign_reg_num(block->lir()->instructions_list(), iw); + } +} + + +void LinearScan::do_linear_scan() { + NOT_PRODUCT(_total_timer.begin_method()); + + number_instructions(); + + NOT_PRODUCT(print_lir(1, "Before Register Allocation")); + + compute_local_live_sets(); + compute_global_live_sets(); + CHECK_BAILOUT(); + + build_intervals(); + CHECK_BAILOUT(); + sort_intervals_before_allocation(); + + NOT_PRODUCT(print_intervals("Before Register Allocation")); + NOT_PRODUCT(LinearScanStatistic::compute(this, _stat_before_alloc)); + + allocate_registers(); + CHECK_BAILOUT(); + + resolve_data_flow(); + if (compilation()->has_exception_handlers()) { + resolve_exception_handlers(); + } + // fill in number of spill slots into frame_map + propagate_spill_slots(); + CHECK_BAILOUT(); + + NOT_PRODUCT(print_intervals("After Register Allocation")); + NOT_PRODUCT(print_lir(2, "LIR after register allocation:")); + DEBUG_ONLY(verify()); + + sort_intervals_after_allocation(); + eliminate_spill_moves(); + assign_reg_num(); + CHECK_BAILOUT(); + + NOT_PRODUCT(print_lir(2, "LIR after assignment of register numbers:")); + NOT_PRODUCT(LinearScanStatistic::compute(this, _stat_after_asign)); + + { TIME_LINEAR_SCAN(timer_allocate_fpu_stack); + + if (use_fpu_stack_allocation()) { + allocate_fpu_stack(); // Only has effect on Intel + NOT_PRODUCT(print_lir(2, "LIR after FPU stack allocation:")); + } + } + + { TIME_LINEAR_SCAN(timer_optimize_lir); + + EdgeMoveOptimizer::optimize(ir()->code()); + ControlFlowOptimizer::optimize(ir()->code()); + // check that cfg is still correct after optimizations + ir()->verify(); + } + + NOT_PRODUCT(print_lir(1, "Before Code Generation", false)); + NOT_PRODUCT(LinearScanStatistic::compute(this, _stat_final)); + NOT_PRODUCT(_total_timer.end_method(this)); +} + + +// ********** Printing functions + +#ifndef PRODUCT + +void LinearScan::print_timers(double total) { + _total_timer.print(total); +} + +void LinearScan::print_statistics() { + _stat_before_alloc.print("before allocation"); + _stat_after_asign.print("after assignment of register"); + _stat_final.print("after optimization"); +} + +void LinearScan::print_bitmap(BitMap& b) { + for (unsigned int i = 0; i < b.size(); i++) { + if (b.at(i)) tty->print("%d ", i); + } + tty->cr(); +} + +void LinearScan::print_intervals(const char* label) { + if (TraceLinearScanLevel >= 1) { + int i; + tty->cr(); + tty->print_cr("%s", label); + + for (i = 0; i < interval_count(); i++) { + Interval* interval = interval_at(i); + if (interval != NULL) { + interval->print(); + } + } + + tty->cr(); + tty->print_cr("--- Basic Blocks ---"); + for (i = 0; i < block_count(); i++) { + BlockBegin* block = block_at(i); + tty->print("B%d [%d, %d, %d, %d] ", block->block_id(), block->first_lir_instruction_id(), block->last_lir_instruction_id(), block->loop_index(), block->loop_depth()); + } + tty->cr(); + tty->cr(); + } + + if (PrintCFGToFile) { + CFGPrinter::print_intervals(&_intervals, label); + } +} + +void LinearScan::print_lir(int level, const char* label, bool hir_valid) { + if (TraceLinearScanLevel >= level) { + tty->cr(); + tty->print_cr("%s", label); + print_LIR(ir()->linear_scan_order()); + tty->cr(); + } + + if (level == 1 && PrintCFGToFile) { + CFGPrinter::print_cfg(ir()->linear_scan_order(), label, hir_valid, true); + } +} + +#endif //PRODUCT + + +// ********** verification functions for allocation +// (check that all intervals have a correct register and that no registers are overwritten) +#ifdef ASSERT + +void LinearScan::verify() { + TRACE_LINEAR_SCAN(2, tty->print_cr("********* verifying intervals ******************************************")); + verify_intervals(); + + TRACE_LINEAR_SCAN(2, tty->print_cr("********* verifying that no oops are in fixed intervals ****************")); + verify_no_oops_in_fixed_intervals(); + + TRACE_LINEAR_SCAN(2, tty->print_cr("********* verifying that unpinned constants are not alive across block boundaries")); + verify_constants(); + + TRACE_LINEAR_SCAN(2, tty->print_cr("********* verifying register allocation ********************************")); + verify_registers(); + + TRACE_LINEAR_SCAN(2, tty->print_cr("********* no errors found **********************************************")); +} + +void LinearScan::verify_intervals() { + int len = interval_count(); + bool has_error = false; + + for (int i = 0; i < len; i++) { + Interval* i1 = interval_at(i); + if (i1 == NULL) continue; + + i1->check_split_children(); + + if (i1->reg_num() != i) { + tty->print_cr("Interval %d is on position %d in list", i1->reg_num(), i); i1->print(); tty->cr(); + has_error = true; + } + + if (i1->reg_num() >= LIR_OprDesc::vreg_base && i1->type() == T_ILLEGAL) { + tty->print_cr("Interval %d has no type assigned", i1->reg_num()); i1->print(); tty->cr(); + has_error = true; + } + + if (i1->assigned_reg() == any_reg) { + tty->print_cr("Interval %d has no register assigned", i1->reg_num()); i1->print(); tty->cr(); + has_error = true; + } + + if (i1->assigned_reg() == i1->assigned_regHi()) { + tty->print_cr("Interval %d: low and high register equal", i1->reg_num()); i1->print(); tty->cr(); + has_error = true; + } + + if (!is_processed_reg_num(i1->assigned_reg())) { + tty->print_cr("Can not have an Interval for an ignored register"); i1->print(); tty->cr(); + has_error = true; + } + + if (i1->first() == Range::end()) { + tty->print_cr("Interval %d has no Range", i1->reg_num()); i1->print(); tty->cr(); + has_error = true; + } + + for (Range* r = i1->first(); r != Range::end(); r = r->next()) { + if (r->from() >= r->to()) { + tty->print_cr("Interval %d has zero length range", i1->reg_num()); i1->print(); tty->cr(); + has_error = true; + } + } + + for (int j = i + 1; j < len; j++) { + Interval* i2 = interval_at(j); + if (i2 == NULL) continue; + + // special intervals that are created in MoveResolver + // -> ignore them because the range information has no meaning there + if (i1->from() == 1 && i1->to() == 2) continue; + if (i2->from() == 1 && i2->to() == 2) continue; + + int r1 = i1->assigned_reg(); + int r1Hi = i1->assigned_regHi(); + int r2 = i2->assigned_reg(); + int r2Hi = i2->assigned_regHi(); + if (i1->intersects(i2) && (r1 == r2 || r1 == r2Hi || (r1Hi != any_reg && (r1Hi == r2 || r1Hi == r2Hi)))) { + tty->print_cr("Intervals %d and %d overlap and have the same register assigned", i1->reg_num(), i2->reg_num()); + i1->print(); tty->cr(); + i2->print(); tty->cr(); + has_error = true; + } + } + } + + assert(has_error == false, "register allocation invalid"); +} + + +void LinearScan::verify_no_oops_in_fixed_intervals() { + LIR_OpVisitState visitor; + for (int i = 0; i < block_count(); i++) { + BlockBegin* block = block_at(i); + + LIR_OpList* instructions = block->lir()->instructions_list(); + + for (int j = 0; j < instructions->length(); j++) { + LIR_Op* op = instructions->at(j); + int op_id = op->id(); + + visitor.visit(op); + + // oop-maps at calls do not contain registers, so check is not needed + if (!visitor.has_call()) { + + for_each_visitor_mode(mode) { + int n = visitor.opr_count(mode); + for (int k = 0; k < n; k++) { + LIR_Opr opr = visitor.opr_at(mode, k); + + if (opr->is_fixed_cpu() && opr->is_oop()) { + // operand is a non-virtual cpu register and contains an oop + TRACE_LINEAR_SCAN(4, op->print_on(tty); tty->print("checking operand "); opr->print(); tty->cr()); + + Interval* interval = interval_at(reg_num(opr)); + assert(interval != NULL, "no interval"); + + if (mode == LIR_OpVisitState::inputMode) { + if (interval->to() >= op_id + 1) { + assert(interval->to() < op_id + 2 || + interval->has_hole_between(op_id, op_id + 2), + "oop input operand live after instruction"); + } + } else if (mode == LIR_OpVisitState::outputMode) { + if (interval->from() <= op_id - 1) { + assert(interval->has_hole_between(op_id - 1, op_id), + "oop input operand live after instruction"); + } + } + } + } + } + } + } + } +} + + +void LinearScan::verify_constants() { + int num_regs = num_virtual_regs(); + int size = live_set_size(); + int num_blocks = block_count(); + + for (int i = 0; i < num_blocks; i++) { + BlockBegin* block = block_at(i); + BitMap live_at_edge = block->live_in(); + + // visit all registers where the live_at_edge bit is set + for (int r = live_at_edge.get_next_one_offset(0, size); r < size; r = live_at_edge.get_next_one_offset(r + 1, size)) { + TRACE_LINEAR_SCAN(4, tty->print("checking interval %d of block B%d", r, block->block_id())); + + Value value = gen()->instruction_for_vreg(r); + + assert(value != NULL, "all intervals live across block boundaries must have Value"); + assert(value->operand()->is_register() && value->operand()->is_virtual(), "value must have virtual operand"); + assert(value->operand()->vreg_number() == r, "register number must match"); + // TKR assert(value->as_Constant() == NULL || value->is_pinned(), "only pinned constants can be alive accross block boundaries"); + } + } +} + + +class RegisterVerifier: public StackObj { + private: + LinearScan* _allocator; + BlockList _work_list; // all blocks that must be processed + IntervalsList _saved_states; // saved information of previous check + + // simplified access to methods of LinearScan + Compilation* compilation() const { return _allocator->compilation(); } + Interval* interval_at(int reg_num) const { return _allocator->interval_at(reg_num); } + int reg_num(LIR_Opr opr) const { return _allocator->reg_num(opr); } + + // currently, only registers are processed + int state_size() { return LinearScan::nof_regs; } + + // accessors + IntervalList* state_for_block(BlockBegin* block) { return _saved_states.at(block->block_id()); } + void set_state_for_block(BlockBegin* block, IntervalList* saved_state) { _saved_states.at_put(block->block_id(), saved_state); } + void add_to_work_list(BlockBegin* block) { if (!_work_list.contains(block)) _work_list.append(block); } + + // helper functions + IntervalList* copy(IntervalList* input_state); + void state_put(IntervalList* input_state, int reg, Interval* interval); + bool check_state(IntervalList* input_state, int reg, Interval* interval); + + void process_block(BlockBegin* block); + void process_xhandler(XHandler* xhandler, IntervalList* input_state); + void process_successor(BlockBegin* block, IntervalList* input_state); + void process_operations(LIR_List* ops, IntervalList* input_state); + + public: + RegisterVerifier(LinearScan* allocator) + : _allocator(allocator) + , _work_list(16) + , _saved_states(BlockBegin::number_of_blocks(), NULL) + { } + + void verify(BlockBegin* start); +}; + + +// entry function from LinearScan that starts the verification +void LinearScan::verify_registers() { + RegisterVerifier verifier(this); + verifier.verify(block_at(0)); +} + + +void RegisterVerifier::verify(BlockBegin* start) { + // setup input registers (method arguments) for first block + IntervalList* input_state = new IntervalList(state_size(), NULL); + CallingConvention* args = compilation()->frame_map()->incoming_arguments(); + for (int n = 0; n < args->length(); n++) { + LIR_Opr opr = args->at(n); + if (opr->is_register()) { + Interval* interval = interval_at(reg_num(opr)); + + if (interval->assigned_reg() < state_size()) { + input_state->at_put(interval->assigned_reg(), interval); + } + if (interval->assigned_regHi() != LinearScan::any_reg && interval->assigned_regHi() < state_size()) { + input_state->at_put(interval->assigned_regHi(), interval); + } + } + } + + set_state_for_block(start, input_state); + add_to_work_list(start); + + // main loop for verification + do { + BlockBegin* block = _work_list.at(0); + _work_list.remove_at(0); + + process_block(block); + } while (!_work_list.is_empty()); +} + +void RegisterVerifier::process_block(BlockBegin* block) { + TRACE_LINEAR_SCAN(2, tty->cr(); tty->print_cr("process_block B%d", block->block_id())); + + // must copy state because it is modified + IntervalList* input_state = copy(state_for_block(block)); + + if (TraceLinearScanLevel >= 4) { + tty->print_cr("Input-State of intervals:"); + tty->print(" "); + for (int i = 0; i < state_size(); i++) { + if (input_state->at(i) != NULL) { + tty->print(" %4d", input_state->at(i)->reg_num()); + } else { + tty->print(" __"); + } + } + tty->cr(); + tty->cr(); + } + + // process all operations of the block + process_operations(block->lir(), input_state); + + // iterate all successors + for (int i = 0; i < block->number_of_sux(); i++) { + process_successor(block->sux_at(i), input_state); + } +} + +void RegisterVerifier::process_xhandler(XHandler* xhandler, IntervalList* input_state) { + TRACE_LINEAR_SCAN(2, tty->print_cr("process_xhandler B%d", xhandler->entry_block()->block_id())); + + // must copy state because it is modified + input_state = copy(input_state); + + if (xhandler->entry_code() != NULL) { + process_operations(xhandler->entry_code(), input_state); + } + process_successor(xhandler->entry_block(), input_state); +} + +void RegisterVerifier::process_successor(BlockBegin* block, IntervalList* input_state) { + IntervalList* saved_state = state_for_block(block); + + if (saved_state != NULL) { + // this block was already processed before. + // check if new input_state is consistent with saved_state + + bool saved_state_correct = true; + for (int i = 0; i < state_size(); i++) { + if (input_state->at(i) != saved_state->at(i)) { + // current input_state and previous saved_state assume a different + // interval in this register -> assume that this register is invalid + if (saved_state->at(i) != NULL) { + // invalidate old calculation only if it assumed that + // register was valid. when the register was already invalid, + // then the old calculation was correct. + saved_state_correct = false; + saved_state->at_put(i, NULL); + + TRACE_LINEAR_SCAN(4, tty->print_cr("process_successor B%d: invalidating slot %d", block->block_id(), i)); + } + } + } + + if (saved_state_correct) { + // already processed block with correct input_state + TRACE_LINEAR_SCAN(2, tty->print_cr("process_successor B%d: previous visit already correct", block->block_id())); + } else { + // must re-visit this block + TRACE_LINEAR_SCAN(2, tty->print_cr("process_successor B%d: must re-visit because input state changed", block->block_id())); + add_to_work_list(block); + } + + } else { + // block was not processed before, so set initial input_state + TRACE_LINEAR_SCAN(2, tty->print_cr("process_successor B%d: initial visit", block->block_id())); + + set_state_for_block(block, copy(input_state)); + add_to_work_list(block); + } +} + + +IntervalList* RegisterVerifier::copy(IntervalList* input_state) { + IntervalList* copy_state = new IntervalList(input_state->length()); + copy_state->push_all(input_state); + return copy_state; +} + +void RegisterVerifier::state_put(IntervalList* input_state, int reg, Interval* interval) { + if (reg != LinearScan::any_reg && reg < state_size()) { + if (interval != NULL) { + TRACE_LINEAR_SCAN(4, tty->print_cr(" reg[%d] = %d", reg, interval->reg_num())); + } else if (input_state->at(reg) != NULL) { + TRACE_LINEAR_SCAN(4, tty->print_cr(" reg[%d] = NULL", reg)); + } + + input_state->at_put(reg, interval); + } +} + +bool RegisterVerifier::check_state(IntervalList* input_state, int reg, Interval* interval) { + if (reg != LinearScan::any_reg && reg < state_size()) { + if (input_state->at(reg) != interval) { + tty->print_cr("!! Error in register allocation: register %d does not contain interval %d", reg, interval->reg_num()); + return true; + } + } + return false; +} + +void RegisterVerifier::process_operations(LIR_List* ops, IntervalList* input_state) { + // visit all instructions of the block + LIR_OpVisitState visitor; + bool has_error = false; + + for (int i = 0; i < ops->length(); i++) { + LIR_Op* op = ops->at(i); + visitor.visit(op); + + TRACE_LINEAR_SCAN(4, op->print_on(tty)); + + // check if input operands are correct + int j; + int n = visitor.opr_count(LIR_OpVisitState::inputMode); + for (j = 0; j < n; j++) { + LIR_Opr opr = visitor.opr_at(LIR_OpVisitState::inputMode, j); + if (opr->is_register() && LinearScan::is_processed_reg_num(reg_num(opr))) { + Interval* interval = interval_at(reg_num(opr)); + if (op->id() != -1) { + interval = interval->split_child_at_op_id(op->id(), LIR_OpVisitState::inputMode); + } + + has_error |= check_state(input_state, interval->assigned_reg(), interval->split_parent()); + has_error |= check_state(input_state, interval->assigned_regHi(), interval->split_parent()); + + // When an operand is marked with is_last_use, then the fpu stack allocator + // removes the register from the fpu stack -> the register contains no value + if (opr->is_last_use()) { + state_put(input_state, interval->assigned_reg(), NULL); + state_put(input_state, interval->assigned_regHi(), NULL); + } + } + } + + // invalidate all caller save registers at calls + if (visitor.has_call()) { + for (j = 0; j < FrameMap::nof_caller_save_cpu_regs; j++) { + state_put(input_state, reg_num(FrameMap::caller_save_cpu_reg_at(j)), NULL); + } + for (j = 0; j < FrameMap::nof_caller_save_fpu_regs; j++) { + state_put(input_state, reg_num(FrameMap::caller_save_fpu_reg_at(j)), NULL); + } + +#ifdef IA32 + for (j = 0; j < FrameMap::nof_caller_save_xmm_regs; j++) { + state_put(input_state, reg_num(FrameMap::caller_save_xmm_reg_at(j)), NULL); + } +#endif + } + + // process xhandler before output and temp operands + XHandlers* xhandlers = visitor.all_xhandler(); + n = xhandlers->length(); + for (int k = 0; k < n; k++) { + process_xhandler(xhandlers->handler_at(k), input_state); + } + + // set temp operands (some operations use temp operands also as output operands, so can't set them NULL) + n = visitor.opr_count(LIR_OpVisitState::tempMode); + for (j = 0; j < n; j++) { + LIR_Opr opr = visitor.opr_at(LIR_OpVisitState::tempMode, j); + if (opr->is_register() && LinearScan::is_processed_reg_num(reg_num(opr))) { + Interval* interval = interval_at(reg_num(opr)); + if (op->id() != -1) { + interval = interval->split_child_at_op_id(op->id(), LIR_OpVisitState::tempMode); + } + + state_put(input_state, interval->assigned_reg(), interval->split_parent()); + state_put(input_state, interval->assigned_regHi(), interval->split_parent()); + } + } + + // set output operands + n = visitor.opr_count(LIR_OpVisitState::outputMode); + for (j = 0; j < n; j++) { + LIR_Opr opr = visitor.opr_at(LIR_OpVisitState::outputMode, j); + if (opr->is_register() && LinearScan::is_processed_reg_num(reg_num(opr))) { + Interval* interval = interval_at(reg_num(opr)); + if (op->id() != -1) { + interval = interval->split_child_at_op_id(op->id(), LIR_OpVisitState::outputMode); + } + + state_put(input_state, interval->assigned_reg(), interval->split_parent()); + state_put(input_state, interval->assigned_regHi(), interval->split_parent()); + } + } + } + assert(has_error == false, "Error in register allocation"); +} + +#endif // ASSERT + + + +// **** Implementation of MoveResolver ****************************** + +MoveResolver::MoveResolver(LinearScan* allocator) : + _allocator(allocator), + _multiple_reads_allowed(false), + _mapping_from(8), + _mapping_from_opr(8), + _mapping_to(8), + _insert_list(NULL), + _insert_idx(-1), + _insertion_buffer() +{ + for (int i = 0; i < LinearScan::nof_regs; i++) { + _register_blocked[i] = 0; + } + DEBUG_ONLY(check_empty()); +} + + +#ifdef ASSERT + +void MoveResolver::check_empty() { + assert(_mapping_from.length() == 0 && _mapping_from_opr.length() == 0 && _mapping_to.length() == 0, "list must be empty before and after processing"); + for (int i = 0; i < LinearScan::nof_regs; i++) { + assert(register_blocked(i) == 0, "register map must be empty before and after processing"); + } + assert(_multiple_reads_allowed == false, "must have default value"); +} + +void MoveResolver::verify_before_resolve() { + assert(_mapping_from.length() == _mapping_from_opr.length(), "length must be equal"); + assert(_mapping_from.length() == _mapping_to.length(), "length must be equal"); + assert(_insert_list != NULL && _insert_idx != -1, "insert position not set"); + + int i, j; + if (!_multiple_reads_allowed) { + for (i = 0; i < _mapping_from.length(); i++) { + for (j = i + 1; j < _mapping_from.length(); j++) { + assert(_mapping_from.at(i) == NULL || _mapping_from.at(i) != _mapping_from.at(j), "cannot read from same interval twice"); + } + } + } + + for (i = 0; i < _mapping_to.length(); i++) { + for (j = i + 1; j < _mapping_to.length(); j++) { + assert(_mapping_to.at(i) != _mapping_to.at(j), "cannot write to same interval twice"); + } + } + + + BitMap used_regs(LinearScan::nof_regs + allocator()->frame_map()->argcount() + allocator()->max_spills()); + used_regs.clear(); + if (!_multiple_reads_allowed) { + for (i = 0; i < _mapping_from.length(); i++) { + Interval* it = _mapping_from.at(i); + if (it != NULL) { + assert(!used_regs.at(it->assigned_reg()), "cannot read from same register twice"); + used_regs.set_bit(it->assigned_reg()); + + if (it->assigned_regHi() != LinearScan::any_reg) { + assert(!used_regs.at(it->assigned_regHi()), "cannot read from same register twice"); + used_regs.set_bit(it->assigned_regHi()); + } + } + } + } + + used_regs.clear(); + for (i = 0; i < _mapping_to.length(); i++) { + Interval* it = _mapping_to.at(i); + assert(!used_regs.at(it->assigned_reg()), "cannot write to same register twice"); + used_regs.set_bit(it->assigned_reg()); + + if (it->assigned_regHi() != LinearScan::any_reg) { + assert(!used_regs.at(it->assigned_regHi()), "cannot write to same register twice"); + used_regs.set_bit(it->assigned_regHi()); + } + } + + used_regs.clear(); + for (i = 0; i < _mapping_from.length(); i++) { + Interval* it = _mapping_from.at(i); + if (it != NULL && it->assigned_reg() >= LinearScan::nof_regs) { + used_regs.set_bit(it->assigned_reg()); + } + } + for (i = 0; i < _mapping_to.length(); i++) { + Interval* it = _mapping_to.at(i); + assert(!used_regs.at(it->assigned_reg()) || it->assigned_reg() == _mapping_from.at(i)->assigned_reg(), "stack slots used in _mapping_from must be disjoint to _mapping_to"); + } +} + +#endif // ASSERT + + +// mark assigned_reg and assigned_regHi of the interval as blocked +void MoveResolver::block_registers(Interval* it) { + int reg = it->assigned_reg(); + if (reg < LinearScan::nof_regs) { + assert(_multiple_reads_allowed || register_blocked(reg) == 0, "register already marked as used"); + set_register_blocked(reg, 1); + } + reg = it->assigned_regHi(); + if (reg != LinearScan::any_reg && reg < LinearScan::nof_regs) { + assert(_multiple_reads_allowed || register_blocked(reg) == 0, "register already marked as used"); + set_register_blocked(reg, 1); + } +} + +// mark assigned_reg and assigned_regHi of the interval as unblocked +void MoveResolver::unblock_registers(Interval* it) { + int reg = it->assigned_reg(); + if (reg < LinearScan::nof_regs) { + assert(register_blocked(reg) > 0, "register already marked as unused"); + set_register_blocked(reg, -1); + } + reg = it->assigned_regHi(); + if (reg != LinearScan::any_reg && reg < LinearScan::nof_regs) { + assert(register_blocked(reg) > 0, "register already marked as unused"); + set_register_blocked(reg, -1); + } +} + +// check if assigned_reg and assigned_regHi of the to-interval are not blocked (or only blocked by from) +bool MoveResolver::save_to_process_move(Interval* from, Interval* to) { + int from_reg = -1; + int from_regHi = -1; + if (from != NULL) { + from_reg = from->assigned_reg(); + from_regHi = from->assigned_regHi(); + } + + int reg = to->assigned_reg(); + if (reg < LinearScan::nof_regs) { + if (register_blocked(reg) > 1 || (register_blocked(reg) == 1 && reg != from_reg && reg != from_regHi)) { + return false; + } + } + reg = to->assigned_regHi(); + if (reg != LinearScan::any_reg && reg < LinearScan::nof_regs) { + if (register_blocked(reg) > 1 || (register_blocked(reg) == 1 && reg != from_reg && reg != from_regHi)) { + return false; + } + } + + return true; +} + + +void MoveResolver::create_insertion_buffer(LIR_List* list) { + assert(!_insertion_buffer.initialized(), "overwriting existing buffer"); + _insertion_buffer.init(list); +} + +void MoveResolver::append_insertion_buffer() { + if (_insertion_buffer.initialized()) { + _insertion_buffer.lir_list()->append(&_insertion_buffer); + } + assert(!_insertion_buffer.initialized(), "must be uninitialized now"); + + _insert_list = NULL; + _insert_idx = -1; +} + +void MoveResolver::insert_move(Interval* from_interval, Interval* to_interval) { + assert(from_interval->reg_num() != to_interval->reg_num(), "from and to interval equal"); + assert(from_interval->type() == to_interval->type(), "move between different types"); + assert(_insert_list != NULL && _insert_idx != -1, "must setup insert position first"); + assert(_insertion_buffer.lir_list() == _insert_list, "wrong insertion buffer"); + + LIR_Opr from_opr = LIR_OprFact::virtual_register(from_interval->reg_num(), from_interval->type()); + LIR_Opr to_opr = LIR_OprFact::virtual_register(to_interval->reg_num(), to_interval->type()); + + if (!_multiple_reads_allowed) { + // the last_use flag is an optimization for FPU stack allocation. When the same + // input interval is used in more than one move, then it is too difficult to determine + // if this move is really the last use. + from_opr = from_opr->make_last_use(); + } + _insertion_buffer.move(_insert_idx, from_opr, to_opr); + + TRACE_LINEAR_SCAN(4, tty->print_cr("MoveResolver: inserted move from register %d (%d, %d) to %d (%d, %d)", from_interval->reg_num(), from_interval->assigned_reg(), from_interval->assigned_regHi(), to_interval->reg_num(), to_interval->assigned_reg(), to_interval->assigned_regHi())); +} + +void MoveResolver::insert_move(LIR_Opr from_opr, Interval* to_interval) { + assert(from_opr->type() == to_interval->type(), "move between different types"); + assert(_insert_list != NULL && _insert_idx != -1, "must setup insert position first"); + assert(_insertion_buffer.lir_list() == _insert_list, "wrong insertion buffer"); + + LIR_Opr to_opr = LIR_OprFact::virtual_register(to_interval->reg_num(), to_interval->type()); + _insertion_buffer.move(_insert_idx, from_opr, to_opr); + + TRACE_LINEAR_SCAN(4, tty->print("MoveResolver: inserted move from constant "); from_opr->print(); tty->print_cr(" to %d (%d, %d)", to_interval->reg_num(), to_interval->assigned_reg(), to_interval->assigned_regHi())); +} + + +void MoveResolver::resolve_mappings() { + TRACE_LINEAR_SCAN(4, tty->print_cr("MoveResolver: resolving mappings for Block B%d, index %d", _insert_list->block() != NULL ? _insert_list->block()->block_id() : -1, _insert_idx)); + DEBUG_ONLY(verify_before_resolve()); + + // Block all registers that are used as input operands of a move. + // When a register is blocked, no move to this register is emitted. + // This is necessary for detecting cycles in moves. + int i; + for (i = _mapping_from.length() - 1; i >= 0; i--) { + Interval* from_interval = _mapping_from.at(i); + if (from_interval != NULL) { + block_registers(from_interval); + } + } + + int spill_candidate = -1; + while (_mapping_from.length() > 0) { + bool processed_interval = false; + + for (i = _mapping_from.length() - 1; i >= 0; i--) { + Interval* from_interval = _mapping_from.at(i); + Interval* to_interval = _mapping_to.at(i); + + if (save_to_process_move(from_interval, to_interval)) { + // this inverval can be processed because target is free + if (from_interval != NULL) { + insert_move(from_interval, to_interval); + unblock_registers(from_interval); + } else { + insert_move(_mapping_from_opr.at(i), to_interval); + } + _mapping_from.remove_at(i); + _mapping_from_opr.remove_at(i); + _mapping_to.remove_at(i); + + processed_interval = true; + } else if (from_interval != NULL && from_interval->assigned_reg() < LinearScan::nof_regs) { + // this interval cannot be processed now because target is not free + // it starts in a register, so it is a possible candidate for spilling + spill_candidate = i; + } + } + + if (!processed_interval) { + // no move could be processed because there is a cycle in the move list + // (e.g. r1 -> r2, r2 -> r1), so one interval must be spilled to memory + assert(spill_candidate != -1, "no interval in register for spilling found"); + + // create a new spill interval and assign a stack slot to it + Interval* from_interval = _mapping_from.at(spill_candidate); + Interval* spill_interval = new Interval(-1); + spill_interval->set_type(from_interval->type()); + + // add a dummy range because real position is difficult to calculate + // Note: this range is a special case when the integrity of the allocation is checked + spill_interval->add_range(1, 2); + + // do not allocate a new spill slot for temporary interval, but + // use spill slot assigned to from_interval. Otherwise moves from + // one stack slot to another can happen (not allowed by LIR_Assembler + int spill_slot = from_interval->canonical_spill_slot(); + if (spill_slot < 0) { + spill_slot = allocator()->allocate_spill_slot(type2spill_size[spill_interval->type()] == 2); + from_interval->set_canonical_spill_slot(spill_slot); + } + spill_interval->assign_reg(spill_slot); + allocator()->append_interval(spill_interval); + + TRACE_LINEAR_SCAN(4, tty->print_cr("created new Interval %d for spilling", spill_interval->reg_num())); + + // insert a move from register to stack and update the mapping + insert_move(from_interval, spill_interval); + _mapping_from.at_put(spill_candidate, spill_interval); + unblock_registers(from_interval); + } + } + + // reset to default value + _multiple_reads_allowed = false; + + // check that all intervals have been processed + DEBUG_ONLY(check_empty()); +} + + +void MoveResolver::set_insert_position(LIR_List* insert_list, int insert_idx) { + TRACE_LINEAR_SCAN(4, tty->print_cr("MoveResolver: setting insert position to Block B%d, index %d", insert_list->block() != NULL ? insert_list->block()->block_id() : -1, insert_idx)); + assert(_insert_list == NULL && _insert_idx == -1, "use move_insert_position instead of set_insert_position when data already set"); + + create_insertion_buffer(insert_list); + _insert_list = insert_list; + _insert_idx = insert_idx; +} + +void MoveResolver::move_insert_position(LIR_List* insert_list, int insert_idx) { + TRACE_LINEAR_SCAN(4, tty->print_cr("MoveResolver: moving insert position to Block B%d, index %d", insert_list->block() != NULL ? insert_list->block()->block_id() : -1, insert_idx)); + + if (_insert_list != NULL && (insert_list != _insert_list || insert_idx != _insert_idx)) { + // insert position changed -> resolve current mappings + resolve_mappings(); + } + + if (insert_list != _insert_list) { + // block changed -> append insertion_buffer because it is + // bound to a specific block and create a new insertion_buffer + append_insertion_buffer(); + create_insertion_buffer(insert_list); + } + + _insert_list = insert_list; + _insert_idx = insert_idx; +} + +void MoveResolver::add_mapping(Interval* from_interval, Interval* to_interval) { + TRACE_LINEAR_SCAN(4, tty->print_cr("MoveResolver: adding mapping from %d (%d, %d) to %d (%d, %d)", from_interval->reg_num(), from_interval->assigned_reg(), from_interval->assigned_regHi(), to_interval->reg_num(), to_interval->assigned_reg(), to_interval->assigned_regHi())); + + _mapping_from.append(from_interval); + _mapping_from_opr.append(LIR_OprFact::illegalOpr); + _mapping_to.append(to_interval); +} + + +void MoveResolver::add_mapping(LIR_Opr from_opr, Interval* to_interval) { + TRACE_LINEAR_SCAN(4, tty->print("MoveResolver: adding mapping from "); from_opr->print(); tty->print_cr(" to %d (%d, %d)", to_interval->reg_num(), to_interval->assigned_reg(), to_interval->assigned_regHi())); + assert(from_opr->is_constant(), "only for constants"); + + _mapping_from.append(NULL); + _mapping_from_opr.append(from_opr); + _mapping_to.append(to_interval); +} + +void MoveResolver::resolve_and_append_moves() { + if (has_mappings()) { + resolve_mappings(); + } + append_insertion_buffer(); +} + + + +// **** Implementation of Range ************************************* + +Range::Range(int from, int to, Range* next) : + _from(from), + _to(to), + _next(next) +{ +} + +// initialize sentinel +Range* Range::_end = NULL; +void Range::initialize() { + _end = new Range(max_jint, max_jint, NULL); +} + +int Range::intersects_at(Range* r2) const { + const Range* r1 = this; + + assert(r1 != NULL && r2 != NULL, "null ranges not allowed"); + assert(r1 != _end && r2 != _end, "empty ranges not allowed"); + + do { + if (r1->from() < r2->from()) { + if (r1->to() <= r2->from()) { + r1 = r1->next(); if (r1 == _end) return -1; + } else { + return r2->from(); + } + } else if (r2->from() < r1->from()) { + if (r2->to() <= r1->from()) { + r2 = r2->next(); if (r2 == _end) return -1; + } else { + return r1->from(); + } + } else { // r1->from() == r2->from() + if (r1->from() == r1->to()) { + r1 = r1->next(); if (r1 == _end) return -1; + } else if (r2->from() == r2->to()) { + r2 = r2->next(); if (r2 == _end) return -1; + } else { + return r1->from(); + } + } + } while (true); +} + +#ifndef PRODUCT +void Range::print(outputStream* out) const { + out->print("[%d, %d[ ", _from, _to); +} +#endif + + + +// **** Implementation of Interval ********************************** + +// initialize sentinel +Interval* Interval::_end = NULL; +void Interval::initialize() { + Range::initialize(); + _end = new Interval(-1); +} + +Interval::Interval(int reg_num) : + _reg_num(reg_num), + _type(T_ILLEGAL), + _first(Range::end()), + _use_pos_and_kinds(12), + _current(Range::end()), + _next(_end), + _state(invalidState), + _assigned_reg(LinearScan::any_reg), + _assigned_regHi(LinearScan::any_reg), + _cached_to(-1), + _cached_opr(LIR_OprFact::illegalOpr), + _cached_vm_reg(VMRegImpl::Bad()), + _split_children(0), + _canonical_spill_slot(-1), + _insert_move_when_activated(false), + _register_hint(NULL), + _spill_state(noDefinitionFound), + _spill_definition_pos(-1) +{ + _split_parent = this; + _current_split_child = this; +} + +int Interval::calc_to() { + assert(_first != Range::end(), "interval has no range"); + + Range* r = _first; + while (r->next() != Range::end()) { + r = r->next(); + } + return r->to(); +} + + +#ifdef ASSERT +// consistency check of split-children +void Interval::check_split_children() { + if (_split_children.length() > 0) { + assert(is_split_parent(), "only split parents can have children"); + + for (int i = 0; i < _split_children.length(); i++) { + Interval* i1 = _split_children.at(i); + + assert(i1->split_parent() == this, "not a split child of this interval"); + assert(i1->type() == type(), "must be equal for all split children"); + assert(i1->canonical_spill_slot() == canonical_spill_slot(), "must be equal for all split children"); + + for (int j = i + 1; j < _split_children.length(); j++) { + Interval* i2 = _split_children.at(j); + + assert(i1->reg_num() != i2->reg_num(), "same register number"); + + if (i1->from() < i2->from()) { + assert(i1->to() <= i2->from() && i1->to() < i2->to(), "intervals overlapping"); + } else { + assert(i2->from() < i1->from(), "intervals start at same op_id"); + assert(i2->to() <= i1->from() && i2->to() < i1->to(), "intervals overlapping"); + } + } + } + } +} +#endif // ASSERT + +Interval* Interval::register_hint(bool search_split_child) const { + if (!search_split_child) { + return _register_hint; + } + + if (_register_hint != NULL) { + assert(_register_hint->is_split_parent(), "ony split parents are valid hint registers"); + + if (_register_hint->assigned_reg() >= 0 && _register_hint->assigned_reg() < LinearScan::nof_regs) { + return _register_hint; + + } else if (_register_hint->_split_children.length() > 0) { + // search the first split child that has a register assigned + int len = _register_hint->_split_children.length(); + for (int i = 0; i < len; i++) { + Interval* cur = _register_hint->_split_children.at(i); + + if (cur->assigned_reg() >= 0 && cur->assigned_reg() < LinearScan::nof_regs) { + return cur; + } + } + } + } + + // no hint interval found that has a register assigned + return NULL; +} + + +Interval* Interval::split_child_at_op_id(int op_id, LIR_OpVisitState::OprMode mode) { + assert(is_split_parent(), "can only be called for split parents"); + assert(op_id >= 0, "invalid op_id (method can not be called for spill moves)"); + + Interval* result; + if (_split_children.length() == 0) { + result = this; + } else { + result = NULL; + int len = _split_children.length(); + + // in outputMode, the end of the interval (op_id == cur->to()) is not valid + int to_offset = (mode == LIR_OpVisitState::outputMode ? 0 : 1); + + int i; + for (i = 0; i < len; i++) { + Interval* cur = _split_children.at(i); + if (cur->from() <= op_id && op_id < cur->to() + to_offset) { + if (i > 0) { + // exchange current split child to start of list (faster access for next call) + _split_children.at_put(i, _split_children.at(0)); + _split_children.at_put(0, cur); + } + + // interval found + result = cur; + break; + } + } + +#ifdef ASSERT + for (i = 0; i < len; i++) { + Interval* tmp = _split_children.at(i); + if (tmp != result && tmp->from() <= op_id && op_id < tmp->to() + to_offset) { + tty->print_cr("two valid result intervals found for op_id %d: %d and %d", op_id, result->reg_num(), tmp->reg_num()); + result->print(); + tmp->print(); + assert(false, "two valid result intervals found"); + } + } +#endif + } + + assert(result != NULL, "no matching interval found"); + assert(result->covers(op_id, mode), "op_id not covered by interval"); + + return result; +} + + +// returns the last split child that ends before the given op_id +Interval* Interval::split_child_before_op_id(int op_id) { + assert(op_id >= 0, "invalid op_id"); + + Interval* parent = split_parent(); + Interval* result = NULL; + + int len = parent->_split_children.length(); + assert(len > 0, "no split children available"); + + for (int i = len - 1; i >= 0; i--) { + Interval* cur = parent->_split_children.at(i); + if (cur->to() <= op_id && (result == NULL || result->to() < cur->to())) { + result = cur; + } + } + + assert(result != NULL, "no split child found"); + return result; +} + + +// checks if op_id is covered by any split child +bool Interval::split_child_covers(int op_id, LIR_OpVisitState::OprMode mode) { + assert(is_split_parent(), "can only be called for split parents"); + assert(op_id >= 0, "invalid op_id (method can not be called for spill moves)"); + + if (_split_children.length() == 0) { + // simple case if interval was not split + return covers(op_id, mode); + + } else { + // extended case: check all split children + int len = _split_children.length(); + for (int i = 0; i < len; i++) { + Interval* cur = _split_children.at(i); + if (cur->covers(op_id, mode)) { + return true; + } + } + return false; + } +} + + +// Note: use positions are sorted descending -> first use has highest index +int Interval::first_usage(IntervalUseKind min_use_kind) const { + assert(LinearScan::is_virtual_interval(this), "cannot access use positions for fixed intervals"); + + for (int i = _use_pos_and_kinds.length() - 2; i >= 0; i -= 2) { + if (_use_pos_and_kinds.at(i + 1) >= min_use_kind) { + return _use_pos_and_kinds.at(i); + } + } + return max_jint; +} + +int Interval::next_usage(IntervalUseKind min_use_kind, int from) const { + assert(LinearScan::is_virtual_interval(this), "cannot access use positions for fixed intervals"); + + for (int i = _use_pos_and_kinds.length() - 2; i >= 0; i -= 2) { + if (_use_pos_and_kinds.at(i) >= from && _use_pos_and_kinds.at(i + 1) >= min_use_kind) { + return _use_pos_and_kinds.at(i); + } + } + return max_jint; +} + +int Interval::next_usage_exact(IntervalUseKind exact_use_kind, int from) const { + assert(LinearScan::is_virtual_interval(this), "cannot access use positions for fixed intervals"); + + for (int i = _use_pos_and_kinds.length() - 2; i >= 0; i -= 2) { + if (_use_pos_and_kinds.at(i) >= from && _use_pos_and_kinds.at(i + 1) == exact_use_kind) { + return _use_pos_and_kinds.at(i); + } + } + return max_jint; +} + +int Interval::previous_usage(IntervalUseKind min_use_kind, int from) const { + assert(LinearScan::is_virtual_interval(this), "cannot access use positions for fixed intervals"); + + int prev = 0; + for (int i = _use_pos_and_kinds.length() - 2; i >= 0; i -= 2) { + if (_use_pos_and_kinds.at(i) > from) { + return prev; + } + if (_use_pos_and_kinds.at(i + 1) >= min_use_kind) { + prev = _use_pos_and_kinds.at(i); + } + } + return prev; +} + +void Interval::add_use_pos(int pos, IntervalUseKind use_kind) { + assert(covers(pos, LIR_OpVisitState::inputMode), "use position not covered by live range"); + + // do not add use positions for precolored intervals because + // they are never used + if (use_kind != noUse && reg_num() >= LIR_OprDesc::vreg_base) { +#ifdef ASSERT + assert(_use_pos_and_kinds.length() % 2 == 0, "must be"); + for (int i = 0; i < _use_pos_and_kinds.length(); i += 2) { + assert(pos <= _use_pos_and_kinds.at(i), "already added a use-position with lower position"); + assert(_use_pos_and_kinds.at(i + 1) >= firstValidKind && _use_pos_and_kinds.at(i + 1) <= lastValidKind, "invalid use kind"); + if (i > 0) { + assert(_use_pos_and_kinds.at(i) < _use_pos_and_kinds.at(i - 2), "not sorted descending"); + } + } +#endif + + // Note: add_use is called in descending order, so list gets sorted + // automatically by just appending new use positions + int len = _use_pos_and_kinds.length(); + if (len == 0 || _use_pos_and_kinds.at(len - 2) > pos) { + _use_pos_and_kinds.append(pos); + _use_pos_and_kinds.append(use_kind); + } else if (_use_pos_and_kinds.at(len - 1) < use_kind) { + assert(_use_pos_and_kinds.at(len - 2) == pos, "list not sorted correctly"); + _use_pos_and_kinds.at_put(len - 1, use_kind); + } + } +} + +void Interval::add_range(int from, int to) { + assert(from < to, "invalid range"); + assert(first() == Range::end() || to < first()->next()->from(), "not inserting at begin of interval"); + assert(from <= first()->to(), "not inserting at begin of interval"); + + if (first()->from() <= to) { + // join intersecting ranges + first()->set_from(MIN2(from, first()->from())); + first()->set_to (MAX2(to, first()->to())); + } else { + // insert new range + _first = new Range(from, to, first()); + } +} + +Interval* Interval::new_split_child() { + // allocate new interval + Interval* result = new Interval(-1); + result->set_type(type()); + + Interval* parent = split_parent(); + result->_split_parent = parent; + result->set_register_hint(parent); + + // insert new interval in children-list of parent + if (parent->_split_children.length() == 0) { + assert(is_split_parent(), "list must be initialized at first split"); + + parent->_split_children = IntervalList(4); + parent->_split_children.append(this); + } + parent->_split_children.append(result); + + return result; +} + +// split this interval at the specified position and return +// the remainder as a new interval. +// +// when an interval is split, a bi-directional link is established between the original interval +// (the split parent) and the intervals that are split off this interval (the split children) +// When a split child is split again, the new created interval is also a direct child +// of the original parent (there is no tree of split children stored, but a flat list) +// All split children are spilled to the same stack slot (stored in _canonical_spill_slot) +// +// Note: The new interval has no valid reg_num +Interval* Interval::split(int split_pos) { + assert(LinearScan::is_virtual_interval(this), "cannot split fixed intervals"); + + // allocate new interval + Interval* result = new_split_child(); + + // split the ranges + Range* prev = NULL; + Range* cur = _first; + while (cur != Range::end() && cur->to() <= split_pos) { + prev = cur; + cur = cur->next(); + } + assert(cur != Range::end(), "split interval after end of last range"); + + if (cur->from() < split_pos) { + result->_first = new Range(split_pos, cur->to(), cur->next()); + cur->set_to(split_pos); + cur->set_next(Range::end()); + + } else { + assert(prev != NULL, "split before start of first range"); + result->_first = cur; + prev->set_next(Range::end()); + } + result->_current = result->_first; + _cached_to = -1; // clear cached value + + // split list of use positions + int total_len = _use_pos_and_kinds.length(); + int start_idx = total_len - 2; + while (start_idx >= 0 && _use_pos_and_kinds.at(start_idx) < split_pos) { + start_idx -= 2; + } + + intStack new_use_pos_and_kinds(total_len - start_idx); + int i; + for (i = start_idx + 2; i < total_len; i++) { + new_use_pos_and_kinds.append(_use_pos_and_kinds.at(i)); + } + + _use_pos_and_kinds.truncate(start_idx + 2); + result->_use_pos_and_kinds = _use_pos_and_kinds; + _use_pos_and_kinds = new_use_pos_and_kinds; + +#ifdef ASSERT + assert(_use_pos_and_kinds.length() % 2 == 0, "must have use kind for each use pos"); + assert(result->_use_pos_and_kinds.length() % 2 == 0, "must have use kind for each use pos"); + assert(_use_pos_and_kinds.length() + result->_use_pos_and_kinds.length() == total_len, "missed some entries"); + + for (i = 0; i < _use_pos_and_kinds.length(); i += 2) { + assert(_use_pos_and_kinds.at(i) < split_pos, "must be"); + assert(_use_pos_and_kinds.at(i + 1) >= firstValidKind && _use_pos_and_kinds.at(i + 1) <= lastValidKind, "invalid use kind"); + } + for (i = 0; i < result->_use_pos_and_kinds.length(); i += 2) { + assert(result->_use_pos_and_kinds.at(i) >= split_pos, "must be"); + assert(result->_use_pos_and_kinds.at(i + 1) >= firstValidKind && result->_use_pos_and_kinds.at(i + 1) <= lastValidKind, "invalid use kind"); + } +#endif + + return result; +} + +// split this interval at the specified position and return +// the head as a new interval (the original interval is the tail) +// +// Currently, only the first range can be split, and the new interval +// must not have split positions +Interval* Interval::split_from_start(int split_pos) { + assert(LinearScan::is_virtual_interval(this), "cannot split fixed intervals"); + assert(split_pos > from() && split_pos < to(), "can only split inside interval"); + assert(split_pos > _first->from() && split_pos <= _first->to(), "can only split inside first range"); + assert(first_usage(noUse) > split_pos, "can not split when use positions are present"); + + // allocate new interval + Interval* result = new_split_child(); + + // the new created interval has only one range (checked by assertion above), + // so the splitting of the ranges is very simple + result->add_range(_first->from(), split_pos); + + if (split_pos == _first->to()) { + assert(_first->next() != Range::end(), "must not be at end"); + _first = _first->next(); + } else { + _first->set_from(split_pos); + } + + return result; +} + + +// returns true if the op_id is inside the interval +bool Interval::covers(int op_id, LIR_OpVisitState::OprMode mode) const { + Range* cur = _first; + + while (cur != Range::end() && cur->to() < op_id) { + cur = cur->next(); + } + if (cur != Range::end()) { + assert(cur->to() != cur->next()->from(), "ranges not separated"); + + if (mode == LIR_OpVisitState::outputMode) { + return cur->from() <= op_id && op_id < cur->to(); + } else { + return cur->from() <= op_id && op_id <= cur->to(); + } + } + return false; +} + +// returns true if the interval has any hole between hole_from and hole_to +// (even if the hole has only the length 1) +bool Interval::has_hole_between(int hole_from, int hole_to) { + assert(hole_from < hole_to, "check"); + assert(from() <= hole_from && hole_to <= to(), "index out of interval"); + + Range* cur = _first; + while (cur != Range::end()) { + assert(cur->to() < cur->next()->from(), "no space between ranges"); + + // hole-range starts before this range -> hole + if (hole_from < cur->from()) { + return true; + + // hole-range completely inside this range -> no hole + } else if (hole_to <= cur->to()) { + return false; + + // overlapping of hole-range with this range -> hole + } else if (hole_from <= cur->to()) { + return true; + } + + cur = cur->next(); + } + + return false; +} + + +#ifndef PRODUCT +void Interval::print(outputStream* out) const { + const char* SpillState2Name[] = { "no definition", "no spill store", "one spill store", "store at definition", "start in memory", "no optimization" }; + const char* UseKind2Name[] = { "N", "L", "S", "M" }; + + const char* type_name; + LIR_Opr opr = LIR_OprFact::illegal(); + if (reg_num() < LIR_OprDesc::vreg_base) { + type_name = "fixed"; + // need a temporary operand for fixed intervals because type() cannot be called + if (assigned_reg() >= pd_first_cpu_reg && assigned_reg() <= pd_last_cpu_reg) { + opr = LIR_OprFact::single_cpu(assigned_reg()); + } else if (assigned_reg() >= pd_first_fpu_reg && assigned_reg() <= pd_last_fpu_reg) { + opr = LIR_OprFact::single_fpu(assigned_reg() - pd_first_fpu_reg); +#ifdef IA32 + } else if (assigned_reg() >= pd_first_xmm_reg && assigned_reg() <= pd_last_xmm_reg) { + opr = LIR_OprFact::single_xmm(assigned_reg() - pd_first_xmm_reg); +#endif + } else { + ShouldNotReachHere(); + } + } else { + type_name = type2name(type()); + if (assigned_reg() != -1) { + opr = LinearScan::calc_operand_for_interval(this); + } + } + + out->print("%d %s ", reg_num(), type_name); + if (opr->is_valid()) { + out->print("\""); + opr->print(out); + out->print("\" "); + } + out->print("%d %d ", split_parent()->reg_num(), (register_hint(false) != NULL ? register_hint(false)->reg_num() : -1)); + + // print ranges + Range* cur = _first; + while (cur != Range::end()) { + cur->print(out); + cur = cur->next(); + assert(cur != NULL, "range list not closed with range sentinel"); + } + + // print use positions + int prev = 0; + assert(_use_pos_and_kinds.length() % 2 == 0, "must be"); + for (int i =_use_pos_and_kinds.length() - 2; i >= 0; i -= 2) { + assert(_use_pos_and_kinds.at(i + 1) >= firstValidKind && _use_pos_and_kinds.at(i + 1) <= lastValidKind, "invalid use kind"); + assert(prev < _use_pos_and_kinds.at(i), "use positions not sorted"); + + out->print("%d %s ", _use_pos_and_kinds.at(i), UseKind2Name[_use_pos_and_kinds.at(i + 1)]); + prev = _use_pos_and_kinds.at(i); + } + + out->print(" \"%s\"", SpillState2Name[spill_state()]); + out->cr(); +} +#endif + + + +// **** Implementation of IntervalWalker **************************** + +IntervalWalker::IntervalWalker(LinearScan* allocator, Interval* unhandled_fixed_first, Interval* unhandled_any_first) + : _compilation(allocator->compilation()) + , _allocator(allocator) +{ + _unhandled_first[fixedKind] = unhandled_fixed_first; + _unhandled_first[anyKind] = unhandled_any_first; + _active_first[fixedKind] = Interval::end(); + _inactive_first[fixedKind] = Interval::end(); + _active_first[anyKind] = Interval::end(); + _inactive_first[anyKind] = Interval::end(); + _current_position = -1; + _current = NULL; + next_interval(); +} + + +// append interval at top of list +void IntervalWalker::append_unsorted(Interval** list, Interval* interval) { + interval->set_next(*list); *list = interval; +} + + +// append interval in order of current range from() +void IntervalWalker::append_sorted(Interval** list, Interval* interval) { + Interval* prev = NULL; + Interval* cur = *list; + while (cur->current_from() < interval->current_from()) { + prev = cur; cur = cur->next(); + } + if (prev == NULL) { + *list = interval; + } else { + prev->set_next(interval); + } + interval->set_next(cur); +} + +void IntervalWalker::append_to_unhandled(Interval** list, Interval* interval) { + assert(interval->from() >= current()->current_from(), "cannot append new interval before current walk position"); + + Interval* prev = NULL; + Interval* cur = *list; + while (cur->from() < interval->from() || (cur->from() == interval->from() && cur->first_usage(noUse) < interval->first_usage(noUse))) { + prev = cur; cur = cur->next(); + } + if (prev == NULL) { + *list = interval; + } else { + prev->set_next(interval); + } + interval->set_next(cur); +} + + +inline bool IntervalWalker::remove_from_list(Interval** list, Interval* i) { + while (*list != Interval::end() && *list != i) { + list = (*list)->next_addr(); + } + if (*list != Interval::end()) { + assert(*list == i, "check"); + *list = (*list)->next(); + return true; + } else { + return false; + } +} + +void IntervalWalker::remove_from_list(Interval* i) { + bool deleted; + + if (i->state() == activeState) { + deleted = remove_from_list(active_first_addr(anyKind), i); + } else { + assert(i->state() == inactiveState, "invalid state"); + deleted = remove_from_list(inactive_first_addr(anyKind), i); + } + + assert(deleted, "interval has not been found in list"); +} + + +void IntervalWalker::walk_to(IntervalState state, int from) { + assert (state == activeState || state == inactiveState, "wrong state"); + for_each_interval_kind(kind) { + Interval** prev = state == activeState ? active_first_addr(kind) : inactive_first_addr(kind); + Interval* next = *prev; + while (next->current_from() <= from) { + Interval* cur = next; + next = cur->next(); + + bool range_has_changed = false; + while (cur->current_to() <= from) { + cur->next_range(); + range_has_changed = true; + } + + // also handle move from inactive list to active list + range_has_changed = range_has_changed || (state == inactiveState && cur->current_from() <= from); + + if (range_has_changed) { + // remove cur from list + *prev = next; + if (cur->current_at_end()) { + // move to handled state (not maintained as a list) + cur->set_state(handledState); + interval_moved(cur, kind, state, handledState); + } else if (cur->current_from() <= from){ + // sort into active list + append_sorted(active_first_addr(kind), cur); + cur->set_state(activeState); + if (*prev == cur) { + assert(state == activeState, "check"); + prev = cur->next_addr(); + } + interval_moved(cur, kind, state, activeState); + } else { + // sort into inactive list + append_sorted(inactive_first_addr(kind), cur); + cur->set_state(inactiveState); + if (*prev == cur) { + assert(state == inactiveState, "check"); + prev = cur->next_addr(); + } + interval_moved(cur, kind, state, inactiveState); + } + } else { + prev = cur->next_addr(); + continue; + } + } + } +} + + +void IntervalWalker::next_interval() { + IntervalKind kind; + Interval* any = _unhandled_first[anyKind]; + Interval* fixed = _unhandled_first[fixedKind]; + + if (any != Interval::end()) { + // intervals may start at same position -> prefer fixed interval + kind = fixed != Interval::end() && fixed->from() <= any->from() ? fixedKind : anyKind; + + assert (kind == fixedKind && fixed->from() <= any->from() || + kind == anyKind && any->from() <= fixed->from(), "wrong interval!!!"); + assert(any == Interval::end() || fixed == Interval::end() || any->from() != fixed->from() || kind == fixedKind, "if fixed and any-Interval start at same position, fixed must be processed first"); + + } else if (fixed != Interval::end()) { + kind = fixedKind; + } else { + _current = NULL; return; + } + _current_kind = kind; + _current = _unhandled_first[kind]; + _unhandled_first[kind] = _current->next(); + _current->set_next(Interval::end()); + _current->rewind_range(); +} + + +void IntervalWalker::walk_to(int lir_op_id) { + assert(_current_position <= lir_op_id, "can not walk backwards"); + while (current() != NULL) { + bool is_active = current()->from() <= lir_op_id; + int id = is_active ? current()->from() : lir_op_id; + + TRACE_LINEAR_SCAN(2, if (_current_position < id) { tty->cr(); tty->print_cr("walk_to(%d) **************************************************************", id); }) + + // set _current_position prior to call of walk_to + _current_position = id; + + // call walk_to even if _current_position == id + walk_to(activeState, id); + walk_to(inactiveState, id); + + if (is_active) { + current()->set_state(activeState); + if (activate_current()) { + append_sorted(active_first_addr(current_kind()), current()); + interval_moved(current(), current_kind(), unhandledState, activeState); + } + + next_interval(); + } else { + return; + } + } +} + +void IntervalWalker::interval_moved(Interval* interval, IntervalKind kind, IntervalState from, IntervalState to) { +#ifndef PRODUCT + if (TraceLinearScanLevel >= 4) { + #define print_state(state) \ + switch(state) {\ + case unhandledState: tty->print("unhandled"); break;\ + case activeState: tty->print("active"); break;\ + case inactiveState: tty->print("inactive"); break;\ + case handledState: tty->print("handled"); break;\ + default: ShouldNotReachHere(); \ + } + + print_state(from); tty->print(" to "); print_state(to); + tty->fill_to(23); + interval->print(); + + #undef print_state + } +#endif +} + + + +// **** Implementation of LinearScanWalker ************************** + +LinearScanWalker::LinearScanWalker(LinearScan* allocator, Interval* unhandled_fixed_first, Interval* unhandled_any_first) + : IntervalWalker(allocator, unhandled_fixed_first, unhandled_any_first) + , _move_resolver(allocator) +{ + for (int i = 0; i < LinearScan::nof_regs; i++) { + _spill_intervals[i] = new IntervalList(2); + } +} + + +inline void LinearScanWalker::init_use_lists(bool only_process_use_pos) { + for (int i = _first_reg; i <= _last_reg; i++) { + _use_pos[i] = max_jint; + + if (!only_process_use_pos) { + _block_pos[i] = max_jint; + _spill_intervals[i]->clear(); + } + } +} + +inline void LinearScanWalker::exclude_from_use(int reg) { + assert(reg < LinearScan::nof_regs, "interval must have a register assigned (stack slots not allowed)"); + if (reg >= _first_reg && reg <= _last_reg) { + _use_pos[reg] = 0; + } +} +inline void LinearScanWalker::exclude_from_use(Interval* i) { + assert(i->assigned_reg() != any_reg, "interval has no register assigned"); + + exclude_from_use(i->assigned_reg()); + exclude_from_use(i->assigned_regHi()); +} + +inline void LinearScanWalker::set_use_pos(int reg, Interval* i, int use_pos, bool only_process_use_pos) { + assert(use_pos != 0, "must use exclude_from_use to set use_pos to 0"); + + if (reg >= _first_reg && reg <= _last_reg) { + if (_use_pos[reg] > use_pos) { + _use_pos[reg] = use_pos; + } + if (!only_process_use_pos) { + _spill_intervals[reg]->append(i); + } + } +} +inline void LinearScanWalker::set_use_pos(Interval* i, int use_pos, bool only_process_use_pos) { + assert(i->assigned_reg() != any_reg, "interval has no register assigned"); + if (use_pos != -1) { + set_use_pos(i->assigned_reg(), i, use_pos, only_process_use_pos); + set_use_pos(i->assigned_regHi(), i, use_pos, only_process_use_pos); + } +} + +inline void LinearScanWalker::set_block_pos(int reg, Interval* i, int block_pos) { + if (reg >= _first_reg && reg <= _last_reg) { + if (_block_pos[reg] > block_pos) { + _block_pos[reg] = block_pos; + } + if (_use_pos[reg] > block_pos) { + _use_pos[reg] = block_pos; + } + } +} +inline void LinearScanWalker::set_block_pos(Interval* i, int block_pos) { + assert(i->assigned_reg() != any_reg, "interval has no register assigned"); + if (block_pos != -1) { + set_block_pos(i->assigned_reg(), i, block_pos); + set_block_pos(i->assigned_regHi(), i, block_pos); + } +} + + +void LinearScanWalker::free_exclude_active_fixed() { + Interval* list = active_first(fixedKind); + while (list != Interval::end()) { + assert(list->assigned_reg() < LinearScan::nof_regs, "active interval must have a register assigned"); + exclude_from_use(list); + list = list->next(); + } +} + +void LinearScanWalker::free_exclude_active_any() { + Interval* list = active_first(anyKind); + while (list != Interval::end()) { + exclude_from_use(list); + list = list->next(); + } +} + +void LinearScanWalker::free_collect_inactive_fixed(Interval* cur) { + Interval* list = inactive_first(fixedKind); + while (list != Interval::end()) { + if (cur->to() <= list->current_from()) { + assert(list->current_intersects_at(cur) == -1, "must not intersect"); + set_use_pos(list, list->current_from(), true); + } else { + set_use_pos(list, list->current_intersects_at(cur), true); + } + list = list->next(); + } +} + +void LinearScanWalker::free_collect_inactive_any(Interval* cur) { + Interval* list = inactive_first(anyKind); + while (list != Interval::end()) { + set_use_pos(list, list->current_intersects_at(cur), true); + list = list->next(); + } +} + +void LinearScanWalker::free_collect_unhandled(IntervalKind kind, Interval* cur) { + Interval* list = unhandled_first(kind); + while (list != Interval::end()) { + set_use_pos(list, list->intersects_at(cur), true); + if (kind == fixedKind && cur->to() <= list->from()) { + set_use_pos(list, list->from(), true); + } + list = list->next(); + } +} + +void LinearScanWalker::spill_exclude_active_fixed() { + Interval* list = active_first(fixedKind); + while (list != Interval::end()) { + exclude_from_use(list); + list = list->next(); + } +} + +void LinearScanWalker::spill_block_unhandled_fixed(Interval* cur) { + Interval* list = unhandled_first(fixedKind); + while (list != Interval::end()) { + set_block_pos(list, list->intersects_at(cur)); + list = list->next(); + } +} + +void LinearScanWalker::spill_block_inactive_fixed(Interval* cur) { + Interval* list = inactive_first(fixedKind); + while (list != Interval::end()) { + if (cur->to() > list->current_from()) { + set_block_pos(list, list->current_intersects_at(cur)); + } else { + assert(list->current_intersects_at(cur) == -1, "invalid optimization: intervals intersect"); + } + + list = list->next(); + } +} + +void LinearScanWalker::spill_collect_active_any() { + Interval* list = active_first(anyKind); + while (list != Interval::end()) { + set_use_pos(list, MIN2(list->next_usage(loopEndMarker, _current_position), list->to()), false); + list = list->next(); + } +} + +void LinearScanWalker::spill_collect_inactive_any(Interval* cur) { + Interval* list = inactive_first(anyKind); + while (list != Interval::end()) { + if (list->current_intersects(cur)) { + set_use_pos(list, MIN2(list->next_usage(loopEndMarker, _current_position), list->to()), false); + } + list = list->next(); + } +} + + +void LinearScanWalker::insert_move(int op_id, Interval* src_it, Interval* dst_it) { + // output all moves here. When source and target are equal, the move is + // optimized away later in assign_reg_nums + + op_id = (op_id + 1) & ~1; + BlockBegin* op_block = allocator()->block_of_op_with_id(op_id); + assert(op_id > 0 && allocator()->block_of_op_with_id(op_id - 2) == op_block, "cannot insert move at block boundary"); + + // calculate index of instruction inside instruction list of current block + // the minimal index (for a block with no spill moves) can be calculated because the + // numbering of instructions is known. + // When the block already contains spill moves, the index must be increased until the + // correct index is reached. + LIR_OpList* list = op_block->lir()->instructions_list(); + int index = (op_id - list->at(0)->id()) / 2; + assert(list->at(index)->id() <= op_id, "error in calculation"); + + while (list->at(index)->id() != op_id) { + index++; + assert(0 <= index && index < list->length(), "index out of bounds"); + } + assert(1 <= index && index < list->length(), "index out of bounds"); + assert(list->at(index)->id() == op_id, "error in calculation"); + + // insert new instruction before instruction at position index + _move_resolver.move_insert_position(op_block->lir(), index - 1); + _move_resolver.add_mapping(src_it, dst_it); +} + + +int LinearScanWalker::find_optimal_split_pos(BlockBegin* min_block, BlockBegin* max_block, int max_split_pos) { + int from_block_nr = min_block->linear_scan_number(); + int to_block_nr = max_block->linear_scan_number(); + + assert(0 <= from_block_nr && from_block_nr < block_count(), "out of range"); + assert(0 <= to_block_nr && to_block_nr < block_count(), "out of range"); + assert(from_block_nr < to_block_nr, "must cross block boundary"); + + // Try to split at end of max_block. If this would be after + // max_split_pos, then use the begin of max_block + int optimal_split_pos = max_block->last_lir_instruction_id() + 2; + if (optimal_split_pos > max_split_pos) { + optimal_split_pos = max_block->first_lir_instruction_id(); + } + + int min_loop_depth = max_block->loop_depth(); + for (int i = to_block_nr - 1; i >= from_block_nr; i--) { + BlockBegin* cur = block_at(i); + + if (cur->loop_depth() < min_loop_depth) { + // block with lower loop-depth found -> split at the end of this block + min_loop_depth = cur->loop_depth(); + optimal_split_pos = cur->last_lir_instruction_id() + 2; + } + } + assert(optimal_split_pos > allocator()->max_lir_op_id() || allocator()->is_block_begin(optimal_split_pos), "algorithm must move split pos to block boundary"); + + return optimal_split_pos; +} + + +int LinearScanWalker::find_optimal_split_pos(Interval* it, int min_split_pos, int max_split_pos, bool do_loop_optimization) { + int optimal_split_pos = -1; + if (min_split_pos == max_split_pos) { + // trivial case, no optimization of split position possible + TRACE_LINEAR_SCAN(4, tty->print_cr(" min-pos and max-pos are equal, no optimization possible")); + optimal_split_pos = min_split_pos; + + } else { + assert(min_split_pos < max_split_pos, "must be true then"); + assert(min_split_pos > 0, "cannot access min_split_pos - 1 otherwise"); + + // reason for using min_split_pos - 1: when the minimal split pos is exactly at the + // beginning of a block, then min_split_pos is also a possible split position. + // Use the block before as min_block, because then min_block->last_lir_instruction_id() + 2 == min_split_pos + BlockBegin* min_block = allocator()->block_of_op_with_id(min_split_pos - 1); + + // reason for using max_split_pos - 1: otherwise there would be an assertion failure + // when an interval ends at the end of the last block of the method + // (in this case, max_split_pos == allocator()->max_lir_op_id() + 2, and there is no + // block at this op_id) + BlockBegin* max_block = allocator()->block_of_op_with_id(max_split_pos - 1); + + assert(min_block->linear_scan_number() <= max_block->linear_scan_number(), "invalid order"); + if (min_block == max_block) { + // split position cannot be moved to block boundary, so split as late as possible + TRACE_LINEAR_SCAN(4, tty->print_cr(" cannot move split pos to block boundary because min_pos and max_pos are in same block")); + optimal_split_pos = max_split_pos; + + } else if (it->has_hole_between(max_split_pos - 1, max_split_pos) && !allocator()->is_block_begin(max_split_pos)) { + // Do not move split position if the interval has a hole before max_split_pos. + // Intervals resulting from Phi-Functions have more than one definition (marked + // as mustHaveRegister) with a hole before each definition. When the register is needed + // for the second definition, an earlier reloading is unnecessary. + TRACE_LINEAR_SCAN(4, tty->print_cr(" interval has hole just before max_split_pos, so splitting at max_split_pos")); + optimal_split_pos = max_split_pos; + + } else { + // seach optimal block boundary between min_split_pos and max_split_pos + TRACE_LINEAR_SCAN(4, tty->print_cr(" moving split pos to optimal block boundary between block B%d and B%d", min_block->block_id(), max_block->block_id())); + + if (do_loop_optimization) { + // Loop optimization: if a loop-end marker is found between min- and max-position, + // then split before this loop + int loop_end_pos = it->next_usage_exact(loopEndMarker, min_block->last_lir_instruction_id() + 2); + TRACE_LINEAR_SCAN(4, tty->print_cr(" loop optimization: loop end found at pos %d", loop_end_pos)); + + assert(loop_end_pos > min_split_pos, "invalid order"); + if (loop_end_pos < max_split_pos) { + // loop-end marker found between min- and max-position + // if it is not the end marker for the same loop as the min-position, then move + // the max-position to this loop block. + // Desired result: uses tagged as shouldHaveRegister inside a loop cause a reloading + // of the interval (normally, only mustHaveRegister causes a reloading) + BlockBegin* loop_block = allocator()->block_of_op_with_id(loop_end_pos); + + TRACE_LINEAR_SCAN(4, tty->print_cr(" interval is used in loop that ends in block B%d, so trying to move max_block back from B%d to B%d", loop_block->block_id(), max_block->block_id(), loop_block->block_id())); + assert(loop_block != min_block, "loop_block and min_block must be different because block boundary is needed between"); + + optimal_split_pos = find_optimal_split_pos(min_block, loop_block, loop_block->last_lir_instruction_id() + 2); + if (optimal_split_pos == loop_block->last_lir_instruction_id() + 2) { + optimal_split_pos = -1; + TRACE_LINEAR_SCAN(4, tty->print_cr(" loop optimization not necessary")); + } else { + TRACE_LINEAR_SCAN(4, tty->print_cr(" loop optimization successful")); + } + } + } + + if (optimal_split_pos == -1) { + // not calculated by loop optimization + optimal_split_pos = find_optimal_split_pos(min_block, max_block, max_split_pos); + } + } + } + TRACE_LINEAR_SCAN(4, tty->print_cr(" optimal split position: %d", optimal_split_pos)); + + return optimal_split_pos; +} + + +/* + split an interval at the optimal position between min_split_pos and + max_split_pos in two parts: + 1) the left part has already a location assigned + 2) the right part is sorted into to the unhandled-list +*/ +void LinearScanWalker::split_before_usage(Interval* it, int min_split_pos, int max_split_pos) { + TRACE_LINEAR_SCAN(2, tty->print ("----- splitting interval: "); it->print()); + TRACE_LINEAR_SCAN(2, tty->print_cr(" between %d and %d", min_split_pos, max_split_pos)); + + assert(it->from() < min_split_pos, "cannot split at start of interval"); + assert(current_position() < min_split_pos, "cannot split before current position"); + assert(min_split_pos <= max_split_pos, "invalid order"); + assert(max_split_pos <= it->to(), "cannot split after end of interval"); + + int optimal_split_pos = find_optimal_split_pos(it, min_split_pos, max_split_pos, true); + + assert(min_split_pos <= optimal_split_pos && optimal_split_pos <= max_split_pos, "out of range"); + assert(optimal_split_pos <= it->to(), "cannot split after end of interval"); + assert(optimal_split_pos > it->from(), "cannot split at start of interval"); + + if (optimal_split_pos == it->to() && it->next_usage(mustHaveRegister, min_split_pos) == max_jint) { + // the split position would be just before the end of the interval + // -> no split at all necessary + TRACE_LINEAR_SCAN(4, tty->print_cr(" no split necessary because optimal split position is at end of interval")); + return; + } + + // must calculate this before the actual split is performed and before split position is moved to odd op_id + bool move_necessary = !allocator()->is_block_begin(optimal_split_pos) && !it->has_hole_between(optimal_split_pos - 1, optimal_split_pos); + + if (!allocator()->is_block_begin(optimal_split_pos)) { + // move position before actual instruction (odd op_id) + optimal_split_pos = (optimal_split_pos - 1) | 1; + } + + TRACE_LINEAR_SCAN(4, tty->print_cr(" splitting at position %d", optimal_split_pos)); + assert(allocator()->is_block_begin(optimal_split_pos) || (optimal_split_pos % 2 == 1), "split pos must be odd when not on block boundary"); + assert(!allocator()->is_block_begin(optimal_split_pos) || (optimal_split_pos % 2 == 0), "split pos must be even on block boundary"); + + Interval* split_part = it->split(optimal_split_pos); + + allocator()->append_interval(split_part); + allocator()->copy_register_flags(it, split_part); + split_part->set_insert_move_when_activated(move_necessary); + append_to_unhandled(unhandled_first_addr(anyKind), split_part); + + TRACE_LINEAR_SCAN(2, tty->print_cr(" split interval in two parts (insert_move_when_activated: %d)", move_necessary)); + TRACE_LINEAR_SCAN(2, tty->print (" "); it->print()); + TRACE_LINEAR_SCAN(2, tty->print (" "); split_part->print()); +} + +/* + split an interval at the optimal position between min_split_pos and + max_split_pos in two parts: + 1) the left part has already a location assigned + 2) the right part is always on the stack and therefore ignored in further processing +*/ +void LinearScanWalker::split_for_spilling(Interval* it) { + // calculate allowed range of splitting position + int max_split_pos = current_position(); + int min_split_pos = MAX2(it->previous_usage(shouldHaveRegister, max_split_pos) + 1, it->from()); + + TRACE_LINEAR_SCAN(2, tty->print ("----- splitting and spilling interval: "); it->print()); + TRACE_LINEAR_SCAN(2, tty->print_cr(" between %d and %d", min_split_pos, max_split_pos)); + + assert(it->state() == activeState, "why spill interval that is not active?"); + assert(it->from() <= min_split_pos, "cannot split before start of interval"); + assert(min_split_pos <= max_split_pos, "invalid order"); + assert(max_split_pos < it->to(), "cannot split at end end of interval"); + assert(current_position() < it->to(), "interval must not end before current position"); + + if (min_split_pos == it->from()) { + // the whole interval is never used, so spill it entirely to memory + TRACE_LINEAR_SCAN(2, tty->print_cr(" spilling entire interval because split pos is at beginning of interval")); + assert(it->first_usage(shouldHaveRegister) > current_position(), "interval must not have use position before current_position"); + + allocator()->assign_spill_slot(it); + allocator()->change_spill_state(it, min_split_pos); + + // Also kick parent intervals out of register to memory when they have no use + // position. This avoids short interval in register surrounded by intervals in + // memory -> avoid useless moves from memory to register and back + Interval* parent = it; + while (parent != NULL && parent->is_split_child()) { + parent = parent->split_child_before_op_id(parent->from()); + + if (parent->assigned_reg() < LinearScan::nof_regs) { + if (parent->first_usage(shouldHaveRegister) == max_jint) { + // parent is never used, so kick it out of its assigned register + TRACE_LINEAR_SCAN(4, tty->print_cr(" kicking out interval %d out of its register because it is never used", parent->reg_num())); + allocator()->assign_spill_slot(parent); + } else { + // do not go further back because the register is actually used by the interval + parent = NULL; + } + } + } + + } else { + // search optimal split pos, split interval and spill only the right hand part + int optimal_split_pos = find_optimal_split_pos(it, min_split_pos, max_split_pos, false); + + assert(min_split_pos <= optimal_split_pos && optimal_split_pos <= max_split_pos, "out of range"); + assert(optimal_split_pos < it->to(), "cannot split at end of interval"); + assert(optimal_split_pos >= it->from(), "cannot split before start of interval"); + + if (!allocator()->is_block_begin(optimal_split_pos)) { + // move position before actual instruction (odd op_id) + optimal_split_pos = (optimal_split_pos - 1) | 1; + } + + TRACE_LINEAR_SCAN(4, tty->print_cr(" splitting at position %d", optimal_split_pos)); + assert(allocator()->is_block_begin(optimal_split_pos) || (optimal_split_pos % 2 == 1), "split pos must be odd when not on block boundary"); + assert(!allocator()->is_block_begin(optimal_split_pos) || (optimal_split_pos % 2 == 0), "split pos must be even on block boundary"); + + Interval* spilled_part = it->split(optimal_split_pos); + allocator()->append_interval(spilled_part); + allocator()->assign_spill_slot(spilled_part); + allocator()->change_spill_state(spilled_part, optimal_split_pos); + + if (!allocator()->is_block_begin(optimal_split_pos)) { + TRACE_LINEAR_SCAN(4, tty->print_cr(" inserting move from interval %d to %d", it->reg_num(), spilled_part->reg_num())); + insert_move(optimal_split_pos, it, spilled_part); + } + + // the current_split_child is needed later when moves are inserted for reloading + assert(spilled_part->current_split_child() == it, "overwriting wrong current_split_child"); + spilled_part->make_current_split_child(); + + TRACE_LINEAR_SCAN(2, tty->print_cr(" split interval in two parts")); + TRACE_LINEAR_SCAN(2, tty->print (" "); it->print()); + TRACE_LINEAR_SCAN(2, tty->print (" "); spilled_part->print()); + } +} + + +void LinearScanWalker::split_stack_interval(Interval* it) { + int min_split_pos = current_position() + 1; + int max_split_pos = MIN2(it->first_usage(shouldHaveRegister), it->to()); + + split_before_usage(it, min_split_pos, max_split_pos); +} + +void LinearScanWalker::split_when_partial_register_available(Interval* it, int register_available_until) { + int min_split_pos = MAX2(it->previous_usage(shouldHaveRegister, register_available_until), it->from() + 1); + int max_split_pos = register_available_until; + + split_before_usage(it, min_split_pos, max_split_pos); +} + +void LinearScanWalker::split_and_spill_interval(Interval* it) { + assert(it->state() == activeState || it->state() == inactiveState, "other states not allowed"); + + int current_pos = current_position(); + if (it->state() == inactiveState) { + // the interval is currently inactive, so no spill slot is needed for now. + // when the split part is activated, the interval has a new chance to get a register, + // so in the best case no stack slot is necessary + assert(it->has_hole_between(current_pos - 1, current_pos + 1), "interval can not be inactive otherwise"); + split_before_usage(it, current_pos + 1, current_pos + 1); + + } else { + // search the position where the interval must have a register and split + // at the optimal position before. + // The new created part is added to the unhandled list and will get a register + // when it is activated + int min_split_pos = current_pos + 1; + int max_split_pos = MIN2(it->next_usage(mustHaveRegister, min_split_pos), it->to()); + + split_before_usage(it, min_split_pos, max_split_pos); + + assert(it->next_usage(mustHaveRegister, current_pos) == max_jint, "the remaining part is spilled to stack and therefore has no register"); + split_for_spilling(it); + } +} + + +int LinearScanWalker::find_free_reg(int reg_needed_until, int interval_to, int hint_reg, int ignore_reg, bool* need_split) { + int min_full_reg = any_reg; + int max_partial_reg = any_reg; + + for (int i = _first_reg; i <= _last_reg; i++) { + if (i == ignore_reg) { + // this register must be ignored + + } else if (_use_pos[i] >= interval_to) { + // this register is free for the full interval + if (min_full_reg == any_reg || i == hint_reg || (_use_pos[i] < _use_pos[min_full_reg] && min_full_reg != hint_reg)) { + min_full_reg = i; + } + } else if (_use_pos[i] > reg_needed_until) { + // this register is at least free until reg_needed_until + if (max_partial_reg == any_reg || i == hint_reg || (_use_pos[i] > _use_pos[max_partial_reg] && max_partial_reg != hint_reg)) { + max_partial_reg = i; + } + } + } + + if (min_full_reg != any_reg) { + return min_full_reg; + } else if (max_partial_reg != any_reg) { + *need_split = true; + return max_partial_reg; + } else { + return any_reg; + } +} + +int LinearScanWalker::find_free_double_reg(int reg_needed_until, int interval_to, int hint_reg, bool* need_split) { + assert((_last_reg - _first_reg + 1) % 2 == 0, "adjust algorithm"); + + int min_full_reg = any_reg; + int max_partial_reg = any_reg; + + for (int i = _first_reg; i < _last_reg; i+=2) { + if (_use_pos[i] >= interval_to && _use_pos[i + 1] >= interval_to) { + // this register is free for the full interval + if (min_full_reg == any_reg || i == hint_reg || (_use_pos[i] < _use_pos[min_full_reg] && min_full_reg != hint_reg)) { + min_full_reg = i; + } + } else if (_use_pos[i] > reg_needed_until && _use_pos[i + 1] > reg_needed_until) { + // this register is at least free until reg_needed_until + if (max_partial_reg == any_reg || i == hint_reg || (_use_pos[i] > _use_pos[max_partial_reg] && max_partial_reg != hint_reg)) { + max_partial_reg = i; + } + } + } + + if (min_full_reg != any_reg) { + return min_full_reg; + } else if (max_partial_reg != any_reg) { + *need_split = true; + return max_partial_reg; + } else { + return any_reg; + } +} + + +bool LinearScanWalker::alloc_free_reg(Interval* cur) { + TRACE_LINEAR_SCAN(2, tty->print("trying to find free register for "); cur->print()); + + init_use_lists(true); + free_exclude_active_fixed(); + free_exclude_active_any(); + free_collect_inactive_fixed(cur); + free_collect_inactive_any(cur); +// free_collect_unhandled(fixedKind, cur); + assert(unhandled_first(fixedKind) == Interval::end(), "must not have unhandled fixed intervals because all fixed intervals have a use at position 0"); + + // _use_pos contains the start of the next interval that has this register assigned + // (either as a fixed register or a normal allocated register in the past) + // only intervals overlapping with cur are processed, non-overlapping invervals can be ignored safely + TRACE_LINEAR_SCAN(4, tty->print_cr(" state of registers:")); + TRACE_LINEAR_SCAN(4, for (int i = _first_reg; i <= _last_reg; i++) tty->print_cr(" reg %d: use_pos: %d", i, _use_pos[i])); + + int hint_reg, hint_regHi; + Interval* register_hint = cur->register_hint(); + if (register_hint != NULL) { + hint_reg = register_hint->assigned_reg(); + hint_regHi = register_hint->assigned_regHi(); + + if (allocator()->is_precolored_cpu_interval(register_hint)) { + assert(hint_reg != any_reg && hint_regHi == any_reg, "must be for fixed intervals"); + hint_regHi = hint_reg + 1; // connect e.g. eax-edx + } + TRACE_LINEAR_SCAN(4, tty->print(" hint registers %d, %d from interval ", hint_reg, hint_regHi); register_hint->print()); + + } else { + hint_reg = any_reg; + hint_regHi = any_reg; + } + assert(hint_reg == any_reg || hint_reg != hint_regHi, "hint reg and regHi equal"); + assert(cur->assigned_reg() == any_reg && cur->assigned_regHi() == any_reg, "register already assigned to interval"); + + // the register must be free at least until this position + int reg_needed_until = cur->from() + 1; + int interval_to = cur->to(); + + bool need_split = false; + int split_pos = -1; + int reg = any_reg; + int regHi = any_reg; + + if (_adjacent_regs) { + reg = find_free_double_reg(reg_needed_until, interval_to, hint_reg, &need_split); + regHi = reg + 1; + if (reg == any_reg) { + return false; + } + split_pos = MIN2(_use_pos[reg], _use_pos[regHi]); + + } else { + reg = find_free_reg(reg_needed_until, interval_to, hint_reg, any_reg, &need_split); + if (reg == any_reg) { + return false; + } + split_pos = _use_pos[reg]; + + if (_num_phys_regs == 2) { + regHi = find_free_reg(reg_needed_until, interval_to, hint_regHi, reg, &need_split); + + if (_use_pos[reg] < interval_to && regHi == any_reg) { + // do not split interval if only one register can be assigned until the split pos + // (when one register is found for the whole interval, split&spill is only + // performed for the hi register) + return false; + + } else if (regHi != any_reg) { + split_pos = MIN2(split_pos, _use_pos[regHi]); + + // sort register numbers to prevent e.g. a move from eax,ebx to ebx,eax + if (reg > regHi) { + int temp = reg; + reg = regHi; + regHi = temp; + } + } + } + } + + cur->assign_reg(reg, regHi); + TRACE_LINEAR_SCAN(2, tty->print_cr("selected register %d, %d", reg, regHi)); + + assert(split_pos > 0, "invalid split_pos"); + if (need_split) { + // register not available for full interval, so split it + split_when_partial_register_available(cur, split_pos); + } + + // only return true if interval is completely assigned + return _num_phys_regs == 1 || regHi != any_reg; +} + + +int LinearScanWalker::find_locked_reg(int reg_needed_until, int interval_to, int hint_reg, int ignore_reg, bool* need_split) { + int max_reg = any_reg; + + for (int i = _first_reg; i <= _last_reg; i++) { + if (i == ignore_reg) { + // this register must be ignored + + } else if (_use_pos[i] > reg_needed_until) { + if (max_reg == any_reg || i == hint_reg || (_use_pos[i] > _use_pos[max_reg] && max_reg != hint_reg)) { + max_reg = i; + } + } + } + + if (max_reg != any_reg && _block_pos[max_reg] <= interval_to) { + *need_split = true; + } + + return max_reg; +} + +int LinearScanWalker::find_locked_double_reg(int reg_needed_until, int interval_to, int hint_reg, bool* need_split) { + assert((_last_reg - _first_reg + 1) % 2 == 0, "adjust algorithm"); + + int max_reg = any_reg; + + for (int i = _first_reg; i < _last_reg; i+=2) { + if (_use_pos[i] > reg_needed_until && _use_pos[i + 1] > reg_needed_until) { + if (max_reg == any_reg || _use_pos[i] > _use_pos[max_reg]) { + max_reg = i; + } + } + } + + if (_block_pos[max_reg] <= interval_to || _block_pos[max_reg + 1] <= interval_to) { + *need_split = true; + } + + return max_reg; +} + +void LinearScanWalker::split_and_spill_intersecting_intervals(int reg, int regHi) { + assert(reg != any_reg, "no register assigned"); + + for (int i = 0; i < _spill_intervals[reg]->length(); i++) { + Interval* it = _spill_intervals[reg]->at(i); + remove_from_list(it); + split_and_spill_interval(it); + } + + if (regHi != any_reg) { + IntervalList* processed = _spill_intervals[reg]; + for (int i = 0; i < _spill_intervals[regHi]->length(); i++) { + Interval* it = _spill_intervals[regHi]->at(i); + if (processed->index_of(it) == -1) { + remove_from_list(it); + split_and_spill_interval(it); + } + } + } +} + + +// Split an Interval and spill it to memory so that cur can be placed in a register +void LinearScanWalker::alloc_locked_reg(Interval* cur) { + TRACE_LINEAR_SCAN(2, tty->print("need to split and spill to get register for "); cur->print()); + + // collect current usage of registers + init_use_lists(false); + spill_exclude_active_fixed(); +// spill_block_unhandled_fixed(cur); + assert(unhandled_first(fixedKind) == Interval::end(), "must not have unhandled fixed intervals because all fixed intervals have a use at position 0"); + spill_block_inactive_fixed(cur); + spill_collect_active_any(); + spill_collect_inactive_any(cur); + +#ifndef PRODUCT + if (TraceLinearScanLevel >= 4) { + tty->print_cr(" state of registers:"); + for (int i = _first_reg; i <= _last_reg; i++) { + tty->print(" reg %d: use_pos: %d, block_pos: %d, intervals: ", i, _use_pos[i], _block_pos[i]); + for (int j = 0; j < _spill_intervals[i]->length(); j++) { + tty->print("%d ", _spill_intervals[i]->at(j)->reg_num()); + } + tty->cr(); + } + } +#endif + + // the register must be free at least until this position + int reg_needed_until = MIN2(cur->first_usage(mustHaveRegister), cur->from() + 1); + int interval_to = cur->to(); + assert (reg_needed_until > 0 && reg_needed_until < max_jint, "interval has no use"); + + int split_pos = 0; + int use_pos = 0; + bool need_split = false; + int reg, regHi; + + if (_adjacent_regs) { + reg = find_locked_double_reg(reg_needed_until, interval_to, any_reg, &need_split); + regHi = reg + 1; + + if (reg != any_reg) { + use_pos = MIN2(_use_pos[reg], _use_pos[regHi]); + split_pos = MIN2(_block_pos[reg], _block_pos[regHi]); + } + } else { + reg = find_locked_reg(reg_needed_until, interval_to, any_reg, cur->assigned_reg(), &need_split); + regHi = any_reg; + + if (reg != any_reg) { + use_pos = _use_pos[reg]; + split_pos = _block_pos[reg]; + + if (_num_phys_regs == 2) { + if (cur->assigned_reg() != any_reg) { + regHi = reg; + reg = cur->assigned_reg(); + } else { + regHi = find_locked_reg(reg_needed_until, interval_to, any_reg, reg, &need_split); + if (regHi != any_reg) { + use_pos = MIN2(use_pos, _use_pos[regHi]); + split_pos = MIN2(split_pos, _block_pos[regHi]); + } + } + + if (regHi != any_reg && reg > regHi) { + // sort register numbers to prevent e.g. a move from eax,ebx to ebx,eax + int temp = reg; + reg = regHi; + regHi = temp; + } + } + } + } + + if (reg == any_reg || (_num_phys_regs == 2 && regHi == any_reg) || use_pos <= cur->first_usage(mustHaveRegister)) { + // the first use of cur is later than the spilling position -> spill cur + TRACE_LINEAR_SCAN(4, tty->print_cr("able to spill current interval. first_usage(register): %d, use_pos: %d", cur->first_usage(mustHaveRegister), use_pos)); + + if (cur->first_usage(mustHaveRegister) <= cur->from() + 1) { + assert(false, "cannot spill interval that is used in first instruction (possible reason: no register found)"); + // assign a reasonable register and do a bailout in product mode to avoid errors + allocator()->assign_spill_slot(cur); + BAILOUT("LinearScan: no register found"); + } + + split_and_spill_interval(cur); + } else { + TRACE_LINEAR_SCAN(4, tty->print_cr("decided to use register %d, %d", reg, regHi)); + assert(reg != any_reg && (_num_phys_regs == 1 || regHi != any_reg), "no register found"); + assert(split_pos > 0, "invalid split_pos"); + assert(need_split == false || split_pos > cur->from(), "splitting interval at from"); + + cur->assign_reg(reg, regHi); + if (need_split) { + // register not available for full interval, so split it + split_when_partial_register_available(cur, split_pos); + } + + // perform splitting and spilling for all affected intervalls + split_and_spill_intersecting_intervals(reg, regHi); + } +} + +bool LinearScanWalker::no_allocation_possible(Interval* cur) { +#ifdef IA32 + // fast calculation of intervals that can never get a register because the + // the next instruction is a call that blocks all registers + // Note: this does not work if callee-saved registers are available (e.g. on Sparc) + + // check if this interval is the result of a split operation + // (an interval got a register until this position) + int pos = cur->from(); + if ((pos & 1) == 1) { + // the current instruction is a call that blocks all registers + if (pos < allocator()->max_lir_op_id() && allocator()->has_call(pos + 1)) { + TRACE_LINEAR_SCAN(4, tty->print_cr(" free register cannot be available because all registers blocked by following call")); + + // safety check that there is really no register available + assert(alloc_free_reg(cur) == false, "found a register for this interval"); + return true; + } + + } +#endif + return false; +} + +void LinearScanWalker::init_vars_for_alloc(Interval* cur) { + BasicType type = cur->type(); + _num_phys_regs = LinearScan::num_physical_regs(type); + _adjacent_regs = LinearScan::requires_adjacent_regs(type); + + if (pd_init_regs_for_alloc(cur)) { + // the appropriate register range was selected. + } else if (type == T_FLOAT || type == T_DOUBLE) { + _first_reg = pd_first_fpu_reg; + _last_reg = pd_last_fpu_reg; + } else { + _first_reg = pd_first_cpu_reg; + _last_reg = pd_last_cpu_reg; + } + + assert(0 <= _first_reg && _first_reg < LinearScan::nof_regs, "out of range"); + assert(0 <= _last_reg && _last_reg < LinearScan::nof_regs, "out of range"); +} + + +bool LinearScanWalker::is_move(LIR_Op* op, Interval* from, Interval* to) { + if (op->code() != lir_move) { + return false; + } + assert(op->as_Op1() != NULL, "move must be LIR_Op1"); + + LIR_Opr in = ((LIR_Op1*)op)->in_opr(); + LIR_Opr res = ((LIR_Op1*)op)->result_opr(); + return in->is_virtual() && res->is_virtual() && in->vreg_number() == from->reg_num() && res->vreg_number() == to->reg_num(); +} + +// optimization (especially for phi functions of nested loops): +// assign same spill slot to non-intersecting intervals +void LinearScanWalker::combine_spilled_intervals(Interval* cur) { + if (cur->is_split_child()) { + // optimization is only suitable for split parents + return; + } + + Interval* register_hint = cur->register_hint(false); + if (register_hint == NULL) { + // cur is not the target of a move, otherwise register_hint would be set + return; + } + assert(register_hint->is_split_parent(), "register hint must be split parent"); + + if (cur->spill_state() != noOptimization || register_hint->spill_state() != noOptimization) { + // combining the stack slots for intervals where spill move optimization is applied + // is not benefitial and would cause problems + return; + } + + int begin_pos = cur->from(); + int end_pos = cur->to(); + if (end_pos > allocator()->max_lir_op_id() || (begin_pos & 1) != 0 || (end_pos & 1) != 0) { + // safety check that lir_op_with_id is allowed + return; + } + + if (!is_move(allocator()->lir_op_with_id(begin_pos), register_hint, cur) || !is_move(allocator()->lir_op_with_id(end_pos), cur, register_hint)) { + // cur and register_hint are not connected with two moves + return; + } + + Interval* begin_hint = register_hint->split_child_at_op_id(begin_pos, LIR_OpVisitState::inputMode); + Interval* end_hint = register_hint->split_child_at_op_id(end_pos, LIR_OpVisitState::outputMode); + if (begin_hint == end_hint || begin_hint->to() != begin_pos || end_hint->from() != end_pos) { + // register_hint must be split, otherwise the re-writing of use positions does not work + return; + } + + assert(begin_hint->assigned_reg() != any_reg, "must have register assigned"); + assert(end_hint->assigned_reg() == any_reg, "must not have register assigned"); + assert(cur->first_usage(mustHaveRegister) == begin_pos, "must have use position at begin of interval because of move"); + assert(end_hint->first_usage(mustHaveRegister) == end_pos, "must have use position at begin of interval because of move"); + + if (begin_hint->assigned_reg() < LinearScan::nof_regs) { + // register_hint is not spilled at begin_pos, so it would not be benefitial to immediately spill cur + return; + } + assert(register_hint->canonical_spill_slot() != -1, "must be set when part of interval was spilled"); + + // modify intervals such that cur gets the same stack slot as register_hint + // delete use positions to prevent the intervals to get a register at beginning + cur->set_canonical_spill_slot(register_hint->canonical_spill_slot()); + cur->remove_first_use_pos(); + end_hint->remove_first_use_pos(); +} + + +// allocate a physical register or memory location to an interval +bool LinearScanWalker::activate_current() { + Interval* cur = current(); + bool result = true; + + TRACE_LINEAR_SCAN(2, tty->print ("+++++ activating interval "); cur->print()); + TRACE_LINEAR_SCAN(4, tty->print_cr(" split_parent: %d, insert_move_when_activated: %d", cur->split_parent()->reg_num(), cur->insert_move_when_activated())); + + if (cur->assigned_reg() >= LinearScan::nof_regs) { + // activating an interval that has a stack slot assigned -> split it at first use position + // used for method parameters + TRACE_LINEAR_SCAN(4, tty->print_cr(" interval has spill slot assigned (method parameter) -> split it before first use")); + + split_stack_interval(cur); + result = false; + + } else if (allocator()->gen()->is_vreg_flag_set(cur->reg_num(), LIRGenerator::must_start_in_memory)) { + // activating an interval that must start in a stack slot, but may get a register later + // used for lir_roundfp: rounding is done by store to stack and reload later + TRACE_LINEAR_SCAN(4, tty->print_cr(" interval must start in stack slot -> split it before first use")); + assert(cur->assigned_reg() == any_reg && cur->assigned_regHi() == any_reg, "register already assigned"); + + allocator()->assign_spill_slot(cur); + split_stack_interval(cur); + result = false; + + } else if (cur->assigned_reg() == any_reg) { + // interval has not assigned register -> normal allocation + // (this is the normal case for most intervals) + TRACE_LINEAR_SCAN(4, tty->print_cr(" normal allocation of register")); + + // assign same spill slot to non-intersecting intervals + combine_spilled_intervals(cur); + + init_vars_for_alloc(cur); + if (no_allocation_possible(cur) || !alloc_free_reg(cur)) { + // no empty register available. + // split and spill another interval so that this interval gets a register + alloc_locked_reg(cur); + } + + // spilled intervals need not be move to active-list + if (cur->assigned_reg() >= LinearScan::nof_regs) { + result = false; + } + } + + // load spilled values that become active from stack slot to register + if (cur->insert_move_when_activated()) { + assert(cur->is_split_child(), "must be"); + assert(cur->current_split_child() != NULL, "must be"); + assert(cur->current_split_child()->reg_num() != cur->reg_num(), "cannot insert move between same interval"); + TRACE_LINEAR_SCAN(4, tty->print_cr("Inserting move from interval %d to %d because insert_move_when_activated is set", cur->current_split_child()->reg_num(), cur->reg_num())); + + insert_move(cur->from(), cur->current_split_child(), cur); + } + cur->make_current_split_child(); + + return result; // true = interval is moved to active list +} + + +// Implementation of EdgeMoveOptimizer + +EdgeMoveOptimizer::EdgeMoveOptimizer() : + _edge_instructions(4), + _edge_instructions_idx(4) +{ +} + +void EdgeMoveOptimizer::optimize(BlockList* code) { + EdgeMoveOptimizer optimizer = EdgeMoveOptimizer(); + + // ignore the first block in the list (index 0 is not processed) + for (int i = code->length() - 1; i >= 1; i--) { + BlockBegin* block = code->at(i); + + if (block->number_of_preds() > 1 && !block->is_set(BlockBegin::exception_entry_flag)) { + optimizer.optimize_moves_at_block_end(block); + } + if (block->number_of_sux() == 2) { + optimizer.optimize_moves_at_block_begin(block); + } + } +} + + +// clear all internal data structures +void EdgeMoveOptimizer::init_instructions() { + _edge_instructions.clear(); + _edge_instructions_idx.clear(); +} + +// append a lir-instruction-list and the index of the current operation in to the list +void EdgeMoveOptimizer::append_instructions(LIR_OpList* instructions, int instructions_idx) { + _edge_instructions.append(instructions); + _edge_instructions_idx.append(instructions_idx); +} + +// return the current operation of the given edge (predecessor or successor) +LIR_Op* EdgeMoveOptimizer::instruction_at(int edge) { + LIR_OpList* instructions = _edge_instructions.at(edge); + int idx = _edge_instructions_idx.at(edge); + + if (idx < instructions->length()) { + return instructions->at(idx); + } else { + return NULL; + } +} + +// removes the current operation of the given edge (predecessor or successor) +void EdgeMoveOptimizer::remove_cur_instruction(int edge, bool decrement_index) { + LIR_OpList* instructions = _edge_instructions.at(edge); + int idx = _edge_instructions_idx.at(edge); + instructions->remove_at(idx); + + if (decrement_index) { + _edge_instructions_idx.at_put(edge, idx - 1); + } +} + + +bool EdgeMoveOptimizer::operations_different(LIR_Op* op1, LIR_Op* op2) { + if (op1 == NULL || op2 == NULL) { + // at least one block is already empty -> no optimization possible + return true; + } + + if (op1->code() == lir_move && op2->code() == lir_move) { + assert(op1->as_Op1() != NULL, "move must be LIR_Op1"); + assert(op2->as_Op1() != NULL, "move must be LIR_Op1"); + LIR_Op1* move1 = (LIR_Op1*)op1; + LIR_Op1* move2 = (LIR_Op1*)op2; + if (move1->info() == move2->info() && move1->in_opr() == move2->in_opr() && move1->result_opr() == move2->result_opr()) { + // these moves are exactly equal and can be optimized + return false; + } + + } else if (op1->code() == lir_fxch && op2->code() == lir_fxch) { + assert(op1->as_Op1() != NULL, "fxch must be LIR_Op1"); + assert(op2->as_Op1() != NULL, "fxch must be LIR_Op1"); + LIR_Op1* fxch1 = (LIR_Op1*)op1; + LIR_Op1* fxch2 = (LIR_Op1*)op2; + if (fxch1->in_opr()->as_jint() == fxch2->in_opr()->as_jint()) { + // equal FPU stack operations can be optimized + return false; + } + + } else if (op1->code() == lir_fpop_raw && op2->code() == lir_fpop_raw) { + // equal FPU stack operations can be optimized + return false; + } + + // no optimization possible + return true; +} + +void EdgeMoveOptimizer::optimize_moves_at_block_end(BlockBegin* block) { + TRACE_LINEAR_SCAN(4, tty->print_cr("optimizing moves at end of block B%d", block->block_id())); + + if (block->is_predecessor(block)) { + // currently we can't handle this correctly. + return; + } + + init_instructions(); + int num_preds = block->number_of_preds(); + assert(num_preds > 1, "do not call otherwise"); + assert(!block->is_set(BlockBegin::exception_entry_flag), "exception handlers not allowed"); + + // setup a list with the lir-instructions of all predecessors + int i; + for (i = 0; i < num_preds; i++) { + BlockBegin* pred = block->pred_at(i); + LIR_OpList* pred_instructions = pred->lir()->instructions_list(); + + if (pred->number_of_sux() != 1) { + // this can happen with switch-statements where multiple edges are between + // the same blocks. + return; + } + + assert(pred->number_of_sux() == 1, "can handle only one successor"); + assert(pred->sux_at(0) == block, "invalid control flow"); + assert(pred_instructions->last()->code() == lir_branch, "block with successor must end with branch"); + assert(pred_instructions->last()->as_OpBranch() != NULL, "branch must be LIR_OpBranch"); + assert(pred_instructions->last()->as_OpBranch()->cond() == lir_cond_always, "block must end with unconditional branch"); + + if (pred_instructions->last()->info() != NULL) { + // can not optimize instructions when debug info is needed + return; + } + + // ignore the unconditional branch at the end of the block + append_instructions(pred_instructions, pred_instructions->length() - 2); + } + + + // process lir-instructions while all predecessors end with the same instruction + while (true) { + LIR_Op* op = instruction_at(0); + for (i = 1; i < num_preds; i++) { + if (operations_different(op, instruction_at(i))) { + // these instructions are different and cannot be optimized -> + // no further optimization possible + return; + } + } + + TRACE_LINEAR_SCAN(4, tty->print("found instruction that is equal in all %d predecessors: ", num_preds); op->print()); + + // insert the instruction at the beginning of the current block + block->lir()->insert_before(1, op); + + // delete the instruction at the end of all predecessors + for (i = 0; i < num_preds; i++) { + remove_cur_instruction(i, true); + } + } +} + + +void EdgeMoveOptimizer::optimize_moves_at_block_begin(BlockBegin* block) { + TRACE_LINEAR_SCAN(4, tty->print_cr("optimization moves at begin of block B%d", block->block_id())); + + init_instructions(); + int num_sux = block->number_of_sux(); + + LIR_OpList* cur_instructions = block->lir()->instructions_list(); + + assert(num_sux == 2, "method should not be called otherwise"); + assert(cur_instructions->last()->code() == lir_branch, "block with successor must end with branch"); + assert(cur_instructions->last()->as_OpBranch() != NULL, "branch must be LIR_OpBranch"); + assert(cur_instructions->last()->as_OpBranch()->cond() == lir_cond_always, "block must end with unconditional branch"); + + if (cur_instructions->last()->info() != NULL) { + // can no optimize instructions when debug info is needed + return; + } + + LIR_Op* branch = cur_instructions->at(cur_instructions->length() - 2); + if (branch->info() != NULL || (branch->code() != lir_branch && branch->code() != lir_cond_float_branch)) { + // not a valid case for optimization + // currently, only blocks that end with two branches (conditional branch followed + // by unconditional branch) are optimized + return; + } + + // now it is guaranteed that the block ends with two branch instructions. + // the instructions are inserted at the end of the block before these two branches + int insert_idx = cur_instructions->length() - 2; + + int i; +#ifdef ASSERT + for (i = insert_idx - 1; i >= 0; i--) { + LIR_Op* op = cur_instructions->at(i); + if ((op->code() == lir_branch || op->code() == lir_cond_float_branch) && ((LIR_OpBranch*)op)->block() != NULL) { + assert(false, "block with two successors can have only two branch instructions"); + } + } +#endif + + // setup a list with the lir-instructions of all successors + for (i = 0; i < num_sux; i++) { + BlockBegin* sux = block->sux_at(i); + LIR_OpList* sux_instructions = sux->lir()->instructions_list(); + + assert(sux_instructions->at(0)->code() == lir_label, "block must start with label"); + + if (sux->number_of_preds() != 1) { + // this can happen with switch-statements where multiple edges are between + // the same blocks. + return; + } + assert(sux->pred_at(0) == block, "invalid control flow"); + assert(!sux->is_set(BlockBegin::exception_entry_flag), "exception handlers not allowed"); + + // ignore the label at the beginning of the block + append_instructions(sux_instructions, 1); + } + + // process lir-instructions while all successors begin with the same instruction + while (true) { + LIR_Op* op = instruction_at(0); + for (i = 1; i < num_sux; i++) { + if (operations_different(op, instruction_at(i))) { + // these instructions are different and cannot be optimized -> + // no further optimization possible + return; + } + } + + TRACE_LINEAR_SCAN(4, tty->print("----- found instruction that is equal in all %d successors: ", num_sux); op->print()); + + // insert instruction at end of current block + block->lir()->insert_before(insert_idx, op); + insert_idx++; + + // delete the instructions at the beginning of all successors + for (i = 0; i < num_sux; i++) { + remove_cur_instruction(i, false); + } + } +} + + +// Implementation of ControlFlowOptimizer + +ControlFlowOptimizer::ControlFlowOptimizer() : + _original_preds(4) +{ +} + +void ControlFlowOptimizer::optimize(BlockList* code) { + ControlFlowOptimizer optimizer = ControlFlowOptimizer(); + + // push the OSR entry block to the end so that we're not jumping over it. + BlockBegin* osr_entry = code->at(0)->end()->as_Base()->osr_entry(); + if (osr_entry) { + int index = osr_entry->linear_scan_number(); + assert(code->at(index) == osr_entry, "wrong index"); + code->remove_at(index); + code->append(osr_entry); + } + + optimizer.reorder_short_loops(code); + optimizer.delete_empty_blocks(code); + optimizer.delete_unnecessary_jumps(code); + optimizer.delete_jumps_to_return(code); +} + +void ControlFlowOptimizer::reorder_short_loop(BlockList* code, BlockBegin* header_block, int header_idx) { + int i = header_idx + 1; + int max_end = MIN2(header_idx + ShortLoopSize, code->length()); + while (i < max_end && code->at(i)->loop_depth() >= header_block->loop_depth()) { + i++; + } + + if (i == code->length() || code->at(i)->loop_depth() < header_block->loop_depth()) { + int end_idx = i - 1; + BlockBegin* end_block = code->at(end_idx); + + if (end_block->number_of_sux() == 1 && end_block->sux_at(0) == header_block) { + // short loop from header_idx to end_idx found -> reorder blocks such that + // the header_block is the last block instead of the first block of the loop + TRACE_LINEAR_SCAN(1, tty->print_cr("Reordering short loop: length %d, header B%d, end B%d", + end_idx - header_idx + 1, + header_block->block_id(), end_block->block_id())); + + for (int j = header_idx; j < end_idx; j++) { + code->at_put(j, code->at(j + 1)); + } + code->at_put(end_idx, header_block); + + // correct the flags so that any loop alignment occurs in the right place. + assert(code->at(end_idx)->is_set(BlockBegin::backward_branch_target_flag), "must be backward branch target"); + code->at(end_idx)->clear(BlockBegin::backward_branch_target_flag); + code->at(header_idx)->set(BlockBegin::backward_branch_target_flag); + } + } +} + +void ControlFlowOptimizer::reorder_short_loops(BlockList* code) { + for (int i = code->length() - 1; i >= 0; i--) { + BlockBegin* block = code->at(i); + + if (block->is_set(BlockBegin::linear_scan_loop_header_flag)) { + reorder_short_loop(code, block, i); + } + } + + DEBUG_ONLY(verify(code)); +} + +// only blocks with exactly one successor can be deleted. Such blocks +// must always end with an unconditional branch to this successor +bool ControlFlowOptimizer::can_delete_block(BlockBegin* block) { + if (block->number_of_sux() != 1 || block->number_of_exception_handlers() != 0 || block->is_entry_block()) { + return false; + } + + LIR_OpList* instructions = block->lir()->instructions_list(); + + assert(instructions->length() >= 2, "block must have label and branch"); + assert(instructions->at(0)->code() == lir_label, "first instruction must always be a label"); + assert(instructions->last()->as_OpBranch() != NULL, "last instrcution must always be a branch"); + assert(instructions->last()->as_OpBranch()->cond() == lir_cond_always, "branch must be unconditional"); + assert(instructions->last()->as_OpBranch()->block() == block->sux_at(0), "branch target must be the successor"); + + // block must have exactly one successor + + if (instructions->length() == 2 && instructions->last()->info() == NULL) { + return true; + } + return false; +} + +// substitute branch targets in all branch-instructions of this blocks +void ControlFlowOptimizer::substitute_branch_target(BlockBegin* block, BlockBegin* target_from, BlockBegin* target_to) { + TRACE_LINEAR_SCAN(3, tty->print_cr("Deleting empty block: substituting from B%d to B%d inside B%d", target_from->block_id(), target_to->block_id(), block->block_id())); + + LIR_OpList* instructions = block->lir()->instructions_list(); + + assert(instructions->at(0)->code() == lir_label, "first instruction must always be a label"); + for (int i = instructions->length() - 1; i >= 1; i--) { + LIR_Op* op = instructions->at(i); + + if (op->code() == lir_branch || op->code() == lir_cond_float_branch) { + assert(op->as_OpBranch() != NULL, "branch must be of type LIR_OpBranch"); + LIR_OpBranch* branch = (LIR_OpBranch*)op; + + if (branch->block() == target_from) { + branch->change_block(target_to); + } + if (branch->ublock() == target_from) { + branch->change_ublock(target_to); + } + } + } +} + +void ControlFlowOptimizer::delete_empty_blocks(BlockList* code) { + int old_pos = 0; + int new_pos = 0; + int num_blocks = code->length(); + + while (old_pos < num_blocks) { + BlockBegin* block = code->at(old_pos); + + if (can_delete_block(block)) { + BlockBegin* new_target = block->sux_at(0); + + // propagate backward branch target flag for correct code alignment + if (block->is_set(BlockBegin::backward_branch_target_flag)) { + new_target->set(BlockBegin::backward_branch_target_flag); + } + + // collect a list with all predecessors that contains each predecessor only once + // the predecessors of cur are changed during the substitution, so a copy of the + // predecessor list is necessary + int j; + _original_preds.clear(); + for (j = block->number_of_preds() - 1; j >= 0; j--) { + BlockBegin* pred = block->pred_at(j); + if (_original_preds.index_of(pred) == -1) { + _original_preds.append(pred); + } + } + + for (j = _original_preds.length() - 1; j >= 0; j--) { + BlockBegin* pred = _original_preds.at(j); + substitute_branch_target(pred, block, new_target); + pred->substitute_sux(block, new_target); + } + } else { + // adjust position of this block in the block list if blocks before + // have been deleted + if (new_pos != old_pos) { + code->at_put(new_pos, code->at(old_pos)); + } + new_pos++; + } + old_pos++; + } + code->truncate(new_pos); + + DEBUG_ONLY(verify(code)); +} + +void ControlFlowOptimizer::delete_unnecessary_jumps(BlockList* code) { + // skip the last block because there a branch is always necessary + for (int i = code->length() - 2; i >= 0; i--) { + BlockBegin* block = code->at(i); + LIR_OpList* instructions = block->lir()->instructions_list(); + + LIR_Op* last_op = instructions->last(); + if (last_op->code() == lir_branch) { + assert(last_op->as_OpBranch() != NULL, "branch must be of type LIR_OpBranch"); + LIR_OpBranch* last_branch = (LIR_OpBranch*)last_op; + + assert(last_branch->block() != NULL, "last branch must always have a block as target"); + assert(last_branch->label() == last_branch->block()->label(), "must be equal"); + + if (last_branch->info() == NULL) { + if (last_branch->block() == code->at(i + 1)) { + + TRACE_LINEAR_SCAN(3, tty->print_cr("Deleting unconditional branch at end of block B%d", block->block_id())); + + // delete last branch instruction + instructions->truncate(instructions->length() - 1); + + } else { + LIR_Op* prev_op = instructions->at(instructions->length() - 2); + if (prev_op->code() == lir_branch || prev_op->code() == lir_cond_float_branch) { + assert(prev_op->as_OpBranch() != NULL, "branch must be of type LIR_OpBranch"); + LIR_OpBranch* prev_branch = (LIR_OpBranch*)prev_op; + + if (prev_branch->block() == code->at(i + 1) && prev_branch->info() == NULL) { + + TRACE_LINEAR_SCAN(3, tty->print_cr("Negating conditional branch and deleting unconditional branch at end of block B%d", block->block_id())); + + // eliminate a conditional branch to the immediate successor + prev_branch->change_block(last_branch->block()); + prev_branch->negate_cond(); + instructions->truncate(instructions->length() - 1); + } + } + } + } + } + } + + DEBUG_ONLY(verify(code)); +} + +void ControlFlowOptimizer::delete_jumps_to_return(BlockList* code) { +#ifdef ASSERT + BitMap return_converted(BlockBegin::number_of_blocks()); + return_converted.clear(); +#endif + + for (int i = code->length() - 1; i >= 0; i--) { + BlockBegin* block = code->at(i); + LIR_OpList* cur_instructions = block->lir()->instructions_list(); + LIR_Op* cur_last_op = cur_instructions->last(); + + assert(cur_instructions->at(0)->code() == lir_label, "first instruction must always be a label"); + if (cur_instructions->length() == 2 && cur_last_op->code() == lir_return) { + // the block contains only a label and a return + // if a predecessor ends with an unconditional jump to this block, then the jump + // can be replaced with a return instruction + // + // Note: the original block with only a return statement cannot be deleted completely + // because the predecessors might have other (conditional) jumps to this block + // -> this may lead to unnecesary return instructions in the final code + + assert(cur_last_op->info() == NULL, "return instructions do not have debug information"); + assert(block->number_of_sux() == 0 || + (return_converted.at(block->block_id()) && block->number_of_sux() == 1), + "blocks that end with return must not have successors"); + + assert(cur_last_op->as_Op1() != NULL, "return must be LIR_Op1"); + LIR_Opr return_opr = ((LIR_Op1*)cur_last_op)->in_opr(); + + for (int j = block->number_of_preds() - 1; j >= 0; j--) { + BlockBegin* pred = block->pred_at(j); + LIR_OpList* pred_instructions = pred->lir()->instructions_list(); + LIR_Op* pred_last_op = pred_instructions->last(); + + if (pred_last_op->code() == lir_branch) { + assert(pred_last_op->as_OpBranch() != NULL, "branch must be LIR_OpBranch"); + LIR_OpBranch* pred_last_branch = (LIR_OpBranch*)pred_last_op; + + if (pred_last_branch->block() == block && pred_last_branch->cond() == lir_cond_always && pred_last_branch->info() == NULL) { + // replace the jump to a return with a direct return + // Note: currently the edge between the blocks is not deleted + pred_instructions->at_put(pred_instructions->length() - 1, new LIR_Op1(lir_return, return_opr)); +#ifdef ASSERT + return_converted.set_bit(pred->block_id()); +#endif + } + } + } + } + } +} + + +#ifdef ASSERT +void ControlFlowOptimizer::verify(BlockList* code) { + for (int i = 0; i < code->length(); i++) { + BlockBegin* block = code->at(i); + LIR_OpList* instructions = block->lir()->instructions_list(); + + int j; + for (j = 0; j < instructions->length(); j++) { + LIR_OpBranch* op_branch = instructions->at(j)->as_OpBranch(); + + if (op_branch != NULL) { + assert(op_branch->block() == NULL || code->index_of(op_branch->block()) != -1, "branch target not valid"); + assert(op_branch->ublock() == NULL || code->index_of(op_branch->ublock()) != -1, "branch target not valid"); + } + } + + for (j = 0; j < block->number_of_sux() - 1; j++) { + BlockBegin* sux = block->sux_at(j); + assert(code->index_of(sux) != -1, "successor not valid"); + } + + for (j = 0; j < block->number_of_preds() - 1; j++) { + BlockBegin* pred = block->pred_at(j); + assert(code->index_of(pred) != -1, "successor not valid"); + } + } +} +#endif + + +#ifndef PRODUCT + +// Implementation of LinearStatistic + +const char* LinearScanStatistic::counter_name(int counter_idx) { + switch (counter_idx) { + case counter_method: return "compiled methods"; + case counter_fpu_method: return "methods using fpu"; + case counter_loop_method: return "methods with loops"; + case counter_exception_method:return "methods with xhandler"; + + case counter_loop: return "loops"; + case counter_block: return "blocks"; + case counter_loop_block: return "blocks inside loop"; + case counter_exception_block: return "exception handler entries"; + case counter_interval: return "intervals"; + case counter_fixed_interval: return "fixed intervals"; + case counter_range: return "ranges"; + case counter_fixed_range: return "fixed ranges"; + case counter_use_pos: return "use positions"; + case counter_fixed_use_pos: return "fixed use positions"; + case counter_spill_slots: return "spill slots"; + + // counter for classes of lir instructions + case counter_instruction: return "total instructions"; + case counter_label: return "labels"; + case counter_entry: return "method entries"; + case counter_return: return "method returns"; + case counter_call: return "method calls"; + case counter_move: return "moves"; + case counter_cmp: return "compare"; + case counter_cond_branch: return "conditional branches"; + case counter_uncond_branch: return "unconditional branches"; + case counter_stub_branch: return "branches to stub"; + case counter_alu: return "artithmetic + logic"; + case counter_alloc: return "allocations"; + case counter_sync: return "synchronisation"; + case counter_throw: return "throw"; + case counter_unwind: return "unwind"; + case counter_typecheck: return "type+null-checks"; + case counter_fpu_stack: return "fpu-stack"; + case counter_misc_inst: return "other instructions"; + case counter_other_inst: return "misc. instructions"; + + // counter for different types of moves + case counter_move_total: return "total moves"; + case counter_move_reg_reg: return "register->register"; + case counter_move_reg_stack: return "register->stack"; + case counter_move_stack_reg: return "stack->register"; + case counter_move_stack_stack:return "stack->stack"; + case counter_move_reg_mem: return "register->memory"; + case counter_move_mem_reg: return "memory->register"; + case counter_move_const_any: return "constant->any"; + + case blank_line_1: return ""; + case blank_line_2: return ""; + + default: ShouldNotReachHere(); return ""; + } +} + +LinearScanStatistic::Counter LinearScanStatistic::base_counter(int counter_idx) { + if (counter_idx == counter_fpu_method || counter_idx == counter_loop_method || counter_idx == counter_exception_method) { + return counter_method; + } else if (counter_idx == counter_loop_block || counter_idx == counter_exception_block) { + return counter_block; + } else if (counter_idx >= counter_instruction && counter_idx <= counter_other_inst) { + return counter_instruction; + } else if (counter_idx >= counter_move_total && counter_idx <= counter_move_const_any) { + return counter_move_total; + } + return invalid_counter; +} + +LinearScanStatistic::LinearScanStatistic() { + for (int i = 0; i < number_of_counters; i++) { + _counters_sum[i] = 0; + _counters_max[i] = -1; + } + +} + +// add the method-local numbers to the total sum +void LinearScanStatistic::sum_up(LinearScanStatistic &method_statistic) { + for (int i = 0; i < number_of_counters; i++) { + _counters_sum[i] += method_statistic._counters_sum[i]; + _counters_max[i] = MAX2(_counters_max[i], method_statistic._counters_sum[i]); + } +} + +void LinearScanStatistic::print(const char* title) { + if (CountLinearScan || TraceLinearScanLevel > 0) { + tty->cr(); + tty->print_cr("***** LinearScan statistic - %s *****", title); + + for (int i = 0; i < number_of_counters; i++) { + if (_counters_sum[i] > 0 || _counters_max[i] >= 0) { + tty->print("%25s: %8d", counter_name(i), _counters_sum[i]); + + if (base_counter(i) != invalid_counter) { + tty->print(" (%5.1f%%) ", _counters_sum[i] * 100.0 / _counters_sum[base_counter(i)]); + } else { + tty->print(" "); + } + + if (_counters_max[i] >= 0) { + tty->print("%8d", _counters_max[i]); + } + } + tty->cr(); + } + } +} + +void LinearScanStatistic::collect(LinearScan* allocator) { + inc_counter(counter_method); + if (allocator->has_fpu_registers()) { + inc_counter(counter_fpu_method); + } + if (allocator->num_loops() > 0) { + inc_counter(counter_loop_method); + } + inc_counter(counter_loop, allocator->num_loops()); + inc_counter(counter_spill_slots, allocator->max_spills()); + + int i; + for (i = 0; i < allocator->interval_count(); i++) { + Interval* cur = allocator->interval_at(i); + + if (cur != NULL) { + inc_counter(counter_interval); + inc_counter(counter_use_pos, cur->num_use_positions()); + if (LinearScan::is_precolored_interval(cur)) { + inc_counter(counter_fixed_interval); + inc_counter(counter_fixed_use_pos, cur->num_use_positions()); + } + + Range* range = cur->first(); + while (range != Range::end()) { + inc_counter(counter_range); + if (LinearScan::is_precolored_interval(cur)) { + inc_counter(counter_fixed_range); + } + range = range->next(); + } + } + } + + bool has_xhandlers = false; + // Note: only count blocks that are in code-emit order + for (i = 0; i < allocator->ir()->code()->length(); i++) { + BlockBegin* cur = allocator->ir()->code()->at(i); + + inc_counter(counter_block); + if (cur->loop_depth() > 0) { + inc_counter(counter_loop_block); + } + if (cur->is_set(BlockBegin::exception_entry_flag)) { + inc_counter(counter_exception_block); + has_xhandlers = true; + } + + LIR_OpList* instructions = cur->lir()->instructions_list(); + for (int j = 0; j < instructions->length(); j++) { + LIR_Op* op = instructions->at(j); + + inc_counter(counter_instruction); + + switch (op->code()) { + case lir_label: inc_counter(counter_label); break; + case lir_std_entry: + case lir_osr_entry: inc_counter(counter_entry); break; + case lir_return: inc_counter(counter_return); break; + + case lir_rtcall: + case lir_static_call: + case lir_optvirtual_call: + case lir_virtual_call: inc_counter(counter_call); break; + + case lir_move: { + inc_counter(counter_move); + inc_counter(counter_move_total); + + LIR_Opr in = op->as_Op1()->in_opr(); + LIR_Opr res = op->as_Op1()->result_opr(); + if (in->is_register()) { + if (res->is_register()) { + inc_counter(counter_move_reg_reg); + } else if (res->is_stack()) { + inc_counter(counter_move_reg_stack); + } else if (res->is_address()) { + inc_counter(counter_move_reg_mem); + } else { + ShouldNotReachHere(); + } + } else if (in->is_stack()) { + if (res->is_register()) { + inc_counter(counter_move_stack_reg); + } else { + inc_counter(counter_move_stack_stack); + } + } else if (in->is_address()) { + assert(res->is_register(), "must be"); + inc_counter(counter_move_mem_reg); + } else if (in->is_constant()) { + inc_counter(counter_move_const_any); + } else { + ShouldNotReachHere(); + } + break; + } + + case lir_cmp: inc_counter(counter_cmp); break; + + case lir_branch: + case lir_cond_float_branch: { + LIR_OpBranch* branch = op->as_OpBranch(); + if (branch->block() == NULL) { + inc_counter(counter_stub_branch); + } else if (branch->cond() == lir_cond_always) { + inc_counter(counter_uncond_branch); + } else { + inc_counter(counter_cond_branch); + } + break; + } + + case lir_neg: + case lir_add: + case lir_sub: + case lir_mul: + case lir_mul_strictfp: + case lir_div: + case lir_div_strictfp: + case lir_rem: + case lir_sqrt: + case lir_sin: + case lir_cos: + case lir_abs: + case lir_log10: + case lir_log: + case lir_logic_and: + case lir_logic_or: + case lir_logic_xor: + case lir_shl: + case lir_shr: + case lir_ushr: inc_counter(counter_alu); break; + + case lir_alloc_object: + case lir_alloc_array: inc_counter(counter_alloc); break; + + case lir_monaddr: + case lir_lock: + case lir_unlock: inc_counter(counter_sync); break; + + case lir_throw: inc_counter(counter_throw); break; + + case lir_unwind: inc_counter(counter_unwind); break; + + case lir_null_check: + case lir_leal: + case lir_instanceof: + case lir_checkcast: + case lir_store_check: inc_counter(counter_typecheck); break; + + case lir_fpop_raw: + case lir_fxch: + case lir_fld: inc_counter(counter_fpu_stack); break; + + case lir_nop: + case lir_push: + case lir_pop: + case lir_convert: + case lir_roundfp: + case lir_cmove: inc_counter(counter_misc_inst); break; + + default: inc_counter(counter_other_inst); break; + } + } + } + + if (has_xhandlers) { + inc_counter(counter_exception_method); + } +} + +void LinearScanStatistic::compute(LinearScan* allocator, LinearScanStatistic &global_statistic) { + if (CountLinearScan || TraceLinearScanLevel > 0) { + + LinearScanStatistic local_statistic = LinearScanStatistic(); + + local_statistic.collect(allocator); + global_statistic.sum_up(local_statistic); + + if (TraceLinearScanLevel > 2) { + local_statistic.print("current local statistic"); + } + } +} + + +// Implementation of LinearTimers + +LinearScanTimers::LinearScanTimers() { + for (int i = 0; i < number_of_timers; i++) { + timer(i)->reset(); + } +} + +const char* LinearScanTimers::timer_name(int idx) { + switch (idx) { + case timer_do_nothing: return "Nothing (Time Check)"; + case timer_number_instructions: return "Number Instructions"; + case timer_compute_local_live_sets: return "Local Live Sets"; + case timer_compute_global_live_sets: return "Global Live Sets"; + case timer_build_intervals: return "Build Intervals"; + case timer_sort_intervals_before: return "Sort Intervals Before"; + case timer_allocate_registers: return "Allocate Registers"; + case timer_resolve_data_flow: return "Resolve Data Flow"; + case timer_sort_intervals_after: return "Sort Intervals After"; + case timer_eliminate_spill_moves: return "Spill optimization"; + case timer_assign_reg_num: return "Assign Reg Num"; + case timer_allocate_fpu_stack: return "Allocate FPU Stack"; + case timer_optimize_lir: return "Optimize LIR"; + default: ShouldNotReachHere(); return ""; + } +} + +void LinearScanTimers::begin_method() { + if (TimeEachLinearScan) { + // reset all timers to measure only current method + for (int i = 0; i < number_of_timers; i++) { + timer(i)->reset(); + } + } +} + +void LinearScanTimers::end_method(LinearScan* allocator) { + if (TimeEachLinearScan) { + + double c = timer(timer_do_nothing)->seconds(); + double total = 0; + for (int i = 1; i < number_of_timers; i++) { + total += timer(i)->seconds() - c; + } + + if (total >= 0.0005) { + // print all information in one line for automatic processing + tty->print("@"); allocator->compilation()->method()->print_name(); + + tty->print("@ %d ", allocator->compilation()->method()->code_size()); + tty->print("@ %d ", allocator->block_at(allocator->block_count() - 1)->last_lir_instruction_id() / 2); + tty->print("@ %d ", allocator->block_count()); + tty->print("@ %d ", allocator->num_virtual_regs()); + tty->print("@ %d ", allocator->interval_count()); + tty->print("@ %d ", allocator->_num_calls); + tty->print("@ %d ", allocator->num_loops()); + + tty->print("@ %6.6f ", total); + for (int i = 1; i < number_of_timers; i++) { + tty->print("@ %4.1f ", ((timer(i)->seconds() - c) / total) * 100); + } + tty->cr(); + } + } +} + +void LinearScanTimers::print(double total_time) { + if (TimeLinearScan) { + // correction value: sum of dummy-timer that only measures the time that + // is necesary to start and stop itself + double c = timer(timer_do_nothing)->seconds(); + + for (int i = 0; i < number_of_timers; i++) { + double t = timer(i)->seconds(); + tty->print_cr(" %25s: %6.3f s (%4.1f%%) corrected: %6.3f s (%4.1f%%)", timer_name(i), t, (t / total_time) * 100.0, t - c, (t - c) / (total_time - 2 * number_of_timers * c) * 100); + } + } +} + +#endif // #ifndef PRODUCT diff --git a/hotspot/src/share/vm/c1/c1_LinearScan.hpp b/hotspot/src/share/vm/c1/c1_LinearScan.hpp new file mode 100644 index 00000000000..bc187fa75d1 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_LinearScan.hpp @@ -0,0 +1,958 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class DebugInfoCache; +class FpuStackAllocator; +class IRScopeDebugInfo; +class Interval; +class IntervalWalker; +class LIRGenerator; +class LinearScan; +class MoveResolver; +class Range; + +define_array(IntervalArray, Interval*) +define_stack(IntervalList, IntervalArray) + +define_array(IntervalsArray, IntervalList*) +define_stack(IntervalsList, IntervalsArray) + +define_array(OopMapArray, OopMap*) +define_stack(OopMapList, OopMapArray) + +define_array(ScopeValueArray, ScopeValue*) + +define_array(LIR_OpListArray, LIR_OpList*); +define_stack(LIR_OpListStack, LIR_OpListArray); + + +enum IntervalUseKind { + // priority of use kinds must be ascending + noUse = 0, + loopEndMarker = 1, + shouldHaveRegister = 2, + mustHaveRegister = 3, + + firstValidKind = 1, + lastValidKind = 3 +}; +define_array(UseKindArray, IntervalUseKind) +define_stack(UseKindStack, UseKindArray) + + +enum IntervalKind { + fixedKind = 0, // interval pre-colored by LIR_Generator + anyKind = 1, // no register/memory allocated by LIR_Generator + nofKinds, + firstKind = fixedKind +}; + + +// during linear scan an interval is in one of four states in +enum IntervalState { + unhandledState = 0, // unhandled state (not processed yet) + activeState = 1, // life and is in a physical register + inactiveState = 2, // in a life time hole and is in a physical register + handledState = 3, // spilled or not life again + invalidState = -1 +}; + + +enum IntervalSpillState { + noDefinitionFound, // starting state of calculation: no definition found yet + oneDefinitionFound, // one definition has already been found. + // Note: two consecutive definitions are treated as one (e.g. consecutive move and add because of two-operand LIR form) + // the position of this definition is stored in _definition_pos + oneMoveInserted, // one spill move has already been inserted. + storeAtDefinition, // the interval should be stored immediately after its definition because otherwise + // there would be multiple redundant stores + startInMemory, // the interval starts in memory (e.g. method parameter), so a store is never necessary + noOptimization // the interval has more then one definition (e.g. resulting from phi moves), so stores to memory are not optimized +}; + + +#define for_each_interval_kind(kind) \ + for (IntervalKind kind = firstKind; kind < nofKinds; kind = (IntervalKind)(kind + 1)) + +#define for_each_visitor_mode(mode) \ + for (LIR_OpVisitState::OprMode mode = LIR_OpVisitState::firstMode; mode < LIR_OpVisitState::numModes; mode = (LIR_OpVisitState::OprMode)(mode + 1)) + + +class LinearScan : public CompilationResourceObj { + // declare classes used by LinearScan as friends because they + // need a wide variety of functions declared here + // + // Only the small interface to the rest of the compiler is public + friend class Interval; + friend class IntervalWalker; + friend class LinearScanWalker; + friend class FpuStackAllocator; + friend class MoveResolver; + friend class LinearScanStatistic; + friend class LinearScanTimers; + friend class RegisterVerifier; + + public: + enum { + any_reg = -1, + nof_cpu_regs = pd_nof_cpu_regs_linearscan, + nof_fpu_regs = pd_nof_fpu_regs_linearscan, + nof_xmm_regs = pd_nof_xmm_regs_linearscan, + nof_regs = nof_cpu_regs + nof_fpu_regs + nof_xmm_regs + }; + + private: + Compilation* _compilation; + IR* _ir; + LIRGenerator* _gen; + FrameMap* _frame_map; + + BlockList _cached_blocks; // cached list with all blocks in linear-scan order (only correct if original list keeps unchanged) + int _num_virtual_regs; // number of virtual registers (without new registers introduced because of splitting intervals) + bool _has_fpu_registers; // true if this method uses any floating point registers (and so fpu stack allocation is necessary) + int _num_calls; // total number of calls in this method + int _max_spills; // number of stack slots used for intervals allocated to memory + int _unused_spill_slot; // unused spill slot for a single-word value because of alignment of a double-word value + + IntervalList _intervals; // mapping from register number to interval + IntervalList* _new_intervals_from_allocation; // list with all intervals created during allocation when an existing interval is split + IntervalArray* _sorted_intervals; // intervals sorted by Interval::from() + + LIR_OpArray _lir_ops; // mapping from LIR_Op id to LIR_Op node + BlockBeginArray _block_of_op; // mapping from LIR_Op id to the BlockBegin containing this instruction + BitMap _has_info; // bit set for each LIR_Op id that has a CodeEmitInfo + BitMap _has_call; // bit set for each LIR_Op id that destroys all caller save registers + BitMap2D _interval_in_loop; // bit set for each virtual register that is contained in each loop + + // cached debug info to prevent multiple creation of same object + // TODO: cached scope values for registers could be static + ScopeValueArray _scope_value_cache; + + static ConstantOopWriteValue _oop_null_scope_value; + static ConstantIntValue _int_m1_scope_value; + static ConstantIntValue _int_0_scope_value; + static ConstantIntValue _int_1_scope_value; + static ConstantIntValue _int_2_scope_value; + + // accessors + IR* ir() const { return _ir; } + Compilation* compilation() const { return _compilation; } + LIRGenerator* gen() const { return _gen; } + FrameMap* frame_map() const { return _frame_map; } + + // unified bailout support + void bailout(const char* msg) const { compilation()->bailout(msg); } + bool bailed_out() const { return compilation()->bailed_out(); } + + // access to block list (sorted in linear scan order) + int block_count() const { assert(_cached_blocks.length() == ir()->linear_scan_order()->length(), "invalid cached block list"); return _cached_blocks.length(); } + BlockBegin* block_at(int idx) const { assert(_cached_blocks.at(idx) == ir()->linear_scan_order()->at(idx), "invalid cached block list"); return _cached_blocks.at(idx); } + + int num_virtual_regs() const { return _num_virtual_regs; } + // size of live_in and live_out sets of BasicBlocks (BitMap needs rounded size for iteration) + int live_set_size() const { return round_to(_num_virtual_regs, BitsPerWord); } + bool has_fpu_registers() const { return _has_fpu_registers; } + int num_loops() const { return ir()->num_loops(); } + bool is_interval_in_loop(int interval, int loop) const { return _interval_in_loop.at(interval, loop); } + + // handling of fpu stack allocation (platform dependent, needed for debug information generation) +#ifdef IA32 + FpuStackAllocator* _fpu_stack_allocator; + bool use_fpu_stack_allocation() const { return UseSSE < 2 && has_fpu_registers(); } +#else + bool use_fpu_stack_allocation() const { return false; } +#endif + + + // access to interval list + int interval_count() const { return _intervals.length(); } + Interval* interval_at(int reg_num) const { return _intervals.at(reg_num); } + + IntervalList* new_intervals_from_allocation() const { return _new_intervals_from_allocation; } + + // access to LIR_Ops and Blocks indexed by op_id + int max_lir_op_id() const { assert(_lir_ops.length() > 0, "no operations"); return (_lir_ops.length() - 1) << 1; } + LIR_Op* lir_op_with_id(int op_id) const { assert(op_id >= 0 && op_id <= max_lir_op_id() && op_id % 2 == 0, "op_id out of range or not even"); return _lir_ops.at(op_id >> 1); } + BlockBegin* block_of_op_with_id(int op_id) const { assert(_block_of_op.length() > 0 && op_id >= 0 && op_id <= max_lir_op_id() + 1, "op_id out of range"); return _block_of_op.at(op_id >> 1); } + + bool is_block_begin(int op_id) { return op_id == 0 || block_of_op_with_id(op_id) != block_of_op_with_id(op_id - 1); } + bool covers_block_begin(int op_id_1, int op_id_2) { return block_of_op_with_id(op_id_1) != block_of_op_with_id(op_id_2); } + + bool has_call(int op_id) { assert(op_id % 2 == 0, "must be even"); return _has_call.at(op_id >> 1); } + bool has_info(int op_id) { assert(op_id % 2 == 0, "must be even"); return _has_info.at(op_id >> 1); } + + + // functions for converting LIR-Operands to register numbers + static bool is_valid_reg_num(int reg_num) { return reg_num >= 0; } + static int reg_num(LIR_Opr opr); + static int reg_numHi(LIR_Opr opr); + + // functions for classification of intervals + static bool is_precolored_interval(const Interval* i); + static bool is_virtual_interval(const Interval* i); + + static bool is_precolored_cpu_interval(const Interval* i); + static bool is_virtual_cpu_interval(const Interval* i); + static bool is_precolored_fpu_interval(const Interval* i); + static bool is_virtual_fpu_interval(const Interval* i); + + static bool is_in_fpu_register(const Interval* i); + static bool is_oop_interval(const Interval* i); + + + // General helper functions + int allocate_spill_slot(bool double_word); + void assign_spill_slot(Interval* it); + void propagate_spill_slots(); + + Interval* create_interval(int reg_num); + void append_interval(Interval* it); + void copy_register_flags(Interval* from, Interval* to); + + // platform dependent functions + static bool is_processed_reg_num(int reg_num); + static int num_physical_regs(BasicType type); + static bool requires_adjacent_regs(BasicType type); + static bool is_caller_save(int assigned_reg); + + // spill move optimization: eliminate moves from register to stack if + // stack slot is known to be correct + void change_spill_definition_pos(Interval* interval, int def_pos); + void change_spill_state(Interval* interval, int spill_pos); + static bool must_store_at_definition(const Interval* i); + void eliminate_spill_moves(); + + // Phase 1: number all instructions in all blocks + void number_instructions(); + + // Phase 2: compute local live sets separately for each block + // (sets live_gen and live_kill for each block) + // + // helper methods used by compute_local_live_sets() + void set_live_gen_kill(Value value, LIR_Op* op, BitMap& live_gen, BitMap& live_kill); + + void compute_local_live_sets(); + + // Phase 3: perform a backward dataflow analysis to compute global live sets + // (sets live_in and live_out for each block) + void compute_global_live_sets(); + + + // Phase 4: build intervals + // (fills the list _intervals) + // + // helper methods used by build_intervals() + void add_use (Value value, int from, int to, IntervalUseKind use_kind); + + void add_def (LIR_Opr opr, int def_pos, IntervalUseKind use_kind); + void add_use (LIR_Opr opr, int from, int to, IntervalUseKind use_kind); + void add_temp(LIR_Opr opr, int temp_pos, IntervalUseKind use_kind); + + void add_def (int reg_num, int def_pos, IntervalUseKind use_kind, BasicType type); + void add_use (int reg_num, int from, int to, IntervalUseKind use_kind, BasicType type); + void add_temp(int reg_num, int temp_pos, IntervalUseKind use_kind, BasicType type); + + // Add platform dependent kills for particular LIR ops. Can be used + // to add platform dependent behaviour for some operations. + void pd_add_temps(LIR_Op* op); + + IntervalUseKind use_kind_of_output_operand(LIR_Op* op, LIR_Opr opr); + IntervalUseKind use_kind_of_input_operand(LIR_Op* op, LIR_Opr opr); + void handle_method_arguments(LIR_Op* op); + void handle_doubleword_moves(LIR_Op* op); + void add_register_hints(LIR_Op* op); + + void build_intervals(); + + + // Phase 5: actual register allocation + // (Uses LinearScanWalker) + // + // helper functions for building a sorted list of intervals + NOT_PRODUCT(bool is_sorted(IntervalArray* intervals);) + static int interval_cmp(Interval** a, Interval** b); + void add_to_list(Interval** first, Interval** prev, Interval* interval); + void create_unhandled_lists(Interval** list1, Interval** list2, bool (is_list1)(const Interval* i), bool (is_list2)(const Interval* i)); + + void sort_intervals_before_allocation(); + void sort_intervals_after_allocation(); + void allocate_registers(); + + + // Phase 6: resolve data flow + // (insert moves at edges between blocks if intervals have been split) + // + // helper functions for resolve_data_flow() + Interval* split_child_at_op_id(Interval* interval, int op_id, LIR_OpVisitState::OprMode mode); + Interval* interval_at_block_begin(BlockBegin* block, int reg_num); + Interval* interval_at_block_end(BlockBegin* block, int reg_num); + Interval* interval_at_op_id(int reg_num, int op_id); + void resolve_collect_mappings(BlockBegin* from_block, BlockBegin* to_block, MoveResolver &move_resolver); + void resolve_find_insert_pos(BlockBegin* from_block, BlockBegin* to_block, MoveResolver &move_resolver); + void resolve_data_flow(); + + void resolve_exception_entry(BlockBegin* block, int reg_num, MoveResolver &move_resolver); + void resolve_exception_entry(BlockBegin* block, MoveResolver &move_resolver); + void resolve_exception_edge(XHandler* handler, int throwing_op_id, int reg_num, Phi* phi, MoveResolver &move_resolver); + void resolve_exception_edge(XHandler* handler, int throwing_op_id, MoveResolver &move_resolver); + void resolve_exception_handlers(); + + // Phase 7: assign register numbers back to LIR + // (includes computation of debug information and oop maps) + // + // helper functions for assign_reg_num() + VMReg vm_reg_for_interval(Interval* interval); + VMReg vm_reg_for_operand(LIR_Opr opr); + + static LIR_Opr operand_for_interval(Interval* interval); + static LIR_Opr calc_operand_for_interval(const Interval* interval); + LIR_Opr canonical_spill_opr(Interval* interval); + + LIR_Opr color_lir_opr(LIR_Opr opr, int id, LIR_OpVisitState::OprMode); + + // methods used for oop map computation + IntervalWalker* init_compute_oop_maps(); + OopMap* compute_oop_map(IntervalWalker* iw, LIR_Op* op, CodeEmitInfo* info, bool is_call_site); + void compute_oop_map(IntervalWalker* iw, const LIR_OpVisitState &visitor, LIR_Op* op); + + // methods used for debug information computation + void init_compute_debug_info(); + + MonitorValue* location_for_monitor_index(int monitor_index); + LocationValue* location_for_name(int name, Location::Type loc_type); + + int append_scope_value_for_constant(LIR_Opr opr, GrowableArray* scope_values); + int append_scope_value_for_operand(LIR_Opr opr, GrowableArray* scope_values); + int append_scope_value(int op_id, Value value, GrowableArray* scope_values); + + IRScopeDebugInfo* compute_debug_info_for_scope(int op_id, IRScope* cur_scope, ValueStack* cur_state, ValueStack* innermost_state, int cur_bci, int stack_end, int locks_end); + void compute_debug_info(CodeEmitInfo* info, int op_id); + + void assign_reg_num(LIR_OpList* instructions, IntervalWalker* iw); + void assign_reg_num(); + + + // Phase 8: fpu stack allocation + // (Used only on x86 when fpu operands are present) + void allocate_fpu_stack(); + + + // helper functions for printing state +#ifndef PRODUCT + static void print_bitmap(BitMap& bitmap); + void print_intervals(const char* label); + void print_lir(int level, const char* label, bool hir_valid = true); +#endif + +#ifdef ASSERT + // verification functions for allocation + // (check that all intervals have a correct register and that no registers are overwritten) + void verify(); + void verify_intervals(); + void verify_no_oops_in_fixed_intervals(); + void verify_constants(); + void verify_registers(); +#endif + + public: + // creation + LinearScan(IR* ir, LIRGenerator* gen, FrameMap* frame_map); + + // main entry function: perform linear scan register allocation + void do_linear_scan(); + + // accessors used by Compilation + int max_spills() const { return _max_spills; } + int num_calls() const { assert(_num_calls >= 0, "not set"); return _num_calls; } + + // entry functions for printing +#ifndef PRODUCT + static void print_statistics(); + static void print_timers(double total); +#endif +}; + + +// Helper class for ordering moves that are inserted at the same position in the LIR +// When moves between registers are inserted, it is important that the moves are +// ordered such that no register is overwritten. So moves from register to stack +// are processed prior to moves from stack to register. When moves have circular +// dependencies, a temporary stack slot is used to break the circle. +// The same logic is used in the LinearScanWalker and in LinearScan during resolve_data_flow +// and therefore factored out in a separate class +class MoveResolver: public StackObj { + private: + LinearScan* _allocator; + + LIR_List* _insert_list; + int _insert_idx; + LIR_InsertionBuffer _insertion_buffer; // buffer where moves are inserted + + IntervalList _mapping_from; + LIR_OprList _mapping_from_opr; + IntervalList _mapping_to; + bool _multiple_reads_allowed; + int _register_blocked[LinearScan::nof_regs]; + + int register_blocked(int reg) { assert(reg >= 0 && reg < LinearScan::nof_regs, "out of bounds"); return _register_blocked[reg]; } + void set_register_blocked(int reg, int direction) { assert(reg >= 0 && reg < LinearScan::nof_regs, "out of bounds"); assert(direction == 1 || direction == -1, "out of bounds"); _register_blocked[reg] += direction; } + + void block_registers(Interval* it); + void unblock_registers(Interval* it); + bool save_to_process_move(Interval* from, Interval* to); + + void create_insertion_buffer(LIR_List* list); + void append_insertion_buffer(); + void insert_move(Interval* from_interval, Interval* to_interval); + void insert_move(LIR_Opr from_opr, Interval* to_interval); + + DEBUG_ONLY(void verify_before_resolve();) + void resolve_mappings(); + public: + MoveResolver(LinearScan* allocator); + + DEBUG_ONLY(void check_empty();) + void set_multiple_reads_allowed() { _multiple_reads_allowed = true; } + void set_insert_position(LIR_List* insert_list, int insert_idx); + void move_insert_position(LIR_List* insert_list, int insert_idx); + void add_mapping(Interval* from, Interval* to); + void add_mapping(LIR_Opr from, Interval* to); + void resolve_and_append_moves(); + + LinearScan* allocator() { return _allocator; } + bool has_mappings() { return _mapping_from.length() > 0; } +}; + + +class Range : public CompilationResourceObj { + friend class Interval; + + private: + static Range* _end; // sentinel (from == to == max_jint) + + int _from; // from (inclusive) + int _to; // to (exclusive) + Range* _next; // linear list of Ranges + + // used only by class Interval, so hide them + bool intersects(Range* r) const { return intersects_at(r) != -1; } + int intersects_at(Range* r) const; + + public: + Range(int from, int to, Range* next); + + static void initialize(); + static Range* end() { return _end; } + + int from() const { return _from; } + int to() const { return _to; } + Range* next() const { return _next; } + void set_from(int from) { _from = from; } + void set_to(int to) { _to = to; } + void set_next(Range* next) { _next = next; } + + // for testing + void print(outputStream* out = tty) const PRODUCT_RETURN; +}; + + +// Interval is an ordered list of disjoint ranges. + +// For pre-colored double word LIR_Oprs, one interval is created for +// the low word register and one is created for the hi word register. +// On Intel for FPU double registers only one interval is created. At +// all times assigned_reg contains the reg. number of the physical +// register. + +// For LIR_Opr in virtual registers a single interval can represent +// single and double word values. When a physical register is +// assigned to the interval, assigned_reg contains the +// phys. reg. number and for double word values assigned_regHi the +// phys. reg. number of the hi word if there is any. For spilled +// intervals assigned_reg contains the stack index. assigned_regHi is +// always -1. + +class Interval : public CompilationResourceObj { + private: + static Interval* _end; // sentinel (interval with only range Range::end()) + + int _reg_num; + BasicType _type; // valid only for virtual registers + Range* _first; // sorted list of Ranges + intStack _use_pos_and_kinds; // sorted list of use-positions and their according use-kinds + + Range* _current; // interval iteration: the current Range + Interval* _next; // interval iteration: sorted list of Intervals (ends with sentinel) + IntervalState _state; // interval iteration: to which set belongs this interval + + + int _assigned_reg; + int _assigned_regHi; + + int _cached_to; // cached value: to of last range (-1: not cached) + LIR_Opr _cached_opr; + VMReg _cached_vm_reg; + + Interval* _split_parent; // the original interval where this interval is derived from + IntervalList _split_children; // list of all intervals that are split off from this interval (only available for split parents) + Interval* _current_split_child; // the current split child that has been active or inactive last (always stored in split parents) + + int _canonical_spill_slot; // the stack slot where all split parts of this interval are spilled to (always stored in split parents) + bool _insert_move_when_activated; // true if move is inserted between _current_split_child and this interval when interval gets active the first time + IntervalSpillState _spill_state; // for spill move optimization + int _spill_definition_pos; // position where the interval is defined (if defined only once) + Interval* _register_hint; // this interval should be in the same register as the hint interval + + int calc_to(); + Interval* new_split_child(); + public: + Interval(int reg_num); + + static void initialize(); + static Interval* end() { return _end; } + + // accessors + int reg_num() const { return _reg_num; } + void set_reg_num(int r) { assert(_reg_num == -1, "cannot change reg_num"); _reg_num = r; } + BasicType type() const { assert(_reg_num == -1 || _reg_num >= LIR_OprDesc::vreg_base, "cannot access type for fixed interval"); return _type; } + void set_type(BasicType type) { assert(_reg_num < LIR_OprDesc::vreg_base || _type == T_ILLEGAL || _type == type, "overwriting existing type"); _type = type; } + + Range* first() const { return _first; } + int from() const { return _first->from(); } + int to() { if (_cached_to == -1) _cached_to = calc_to(); assert(_cached_to == calc_to(), "invalid cached value"); return _cached_to; } + int num_use_positions() const { return _use_pos_and_kinds.length() / 2; } + + Interval* next() const { return _next; } + Interval** next_addr() { return &_next; } + void set_next(Interval* next) { _next = next; } + + int assigned_reg() const { return _assigned_reg; } + int assigned_regHi() const { return _assigned_regHi; } + void assign_reg(int reg) { _assigned_reg = reg; _assigned_regHi = LinearScan::any_reg; } + void assign_reg(int reg,int regHi) { _assigned_reg = reg; _assigned_regHi = regHi; } + + Interval* register_hint(bool search_split_child = true) const; // calculation needed + void set_register_hint(Interval* i) { _register_hint = i; } + + int state() const { return _state; } + void set_state(IntervalState s) { _state = s; } + + // access to split parent and split children + bool is_split_parent() const { return _split_parent == this; } + bool is_split_child() const { return _split_parent != this; } + Interval* split_parent() const { assert(_split_parent->is_split_parent(), "must be"); return _split_parent; } + Interval* split_child_at_op_id(int op_id, LIR_OpVisitState::OprMode mode); + Interval* split_child_before_op_id(int op_id); + bool split_child_covers(int op_id, LIR_OpVisitState::OprMode mode); + DEBUG_ONLY(void check_split_children();) + + // information stored in split parent, but available for all children + int canonical_spill_slot() const { return split_parent()->_canonical_spill_slot; } + void set_canonical_spill_slot(int slot) { assert(split_parent()->_canonical_spill_slot == -1, "overwriting existing value"); split_parent()->_canonical_spill_slot = slot; } + Interval* current_split_child() const { return split_parent()->_current_split_child; } + void make_current_split_child() { split_parent()->_current_split_child = this; } + + bool insert_move_when_activated() const { return _insert_move_when_activated; } + void set_insert_move_when_activated(bool b) { _insert_move_when_activated = b; } + + // for spill optimization + IntervalSpillState spill_state() const { return split_parent()->_spill_state; } + int spill_definition_pos() const { return split_parent()->_spill_definition_pos; } + void set_spill_state(IntervalSpillState state) { assert(state >= spill_state(), "state cannot decrease"); split_parent()->_spill_state = state; } + void set_spill_definition_pos(int pos) { assert(spill_definition_pos() == -1, "cannot set the position twice"); split_parent()->_spill_definition_pos = pos; } + // returns true if this interval has a shadow copy on the stack that is always correct + bool always_in_memory() const { return split_parent()->_spill_state == storeAtDefinition || split_parent()->_spill_state == startInMemory; } + + // caching of values that take time to compute and are used multiple times + LIR_Opr cached_opr() const { return _cached_opr; } + VMReg cached_vm_reg() const { return _cached_vm_reg; } + void set_cached_opr(LIR_Opr opr) { _cached_opr = opr; } + void set_cached_vm_reg(VMReg reg) { _cached_vm_reg = reg; } + + // access to use positions + int first_usage(IntervalUseKind min_use_kind) const; // id of the first operation requiring this interval in a register + int next_usage(IntervalUseKind min_use_kind, int from) const; // id of next usage seen from the given position + int next_usage_exact(IntervalUseKind exact_use_kind, int from) const; + int previous_usage(IntervalUseKind min_use_kind, int from) const; + + // manipulating intervals + void add_use_pos(int pos, IntervalUseKind use_kind); + void add_range(int from, int to); + Interval* split(int split_pos); + Interval* split_from_start(int split_pos); + void remove_first_use_pos() { _use_pos_and_kinds.truncate(_use_pos_and_kinds.length() - 2); } + + // test intersection + bool covers(int op_id, LIR_OpVisitState::OprMode mode) const; + bool has_hole_between(int from, int to); + bool intersects(Interval* i) const { return _first->intersects(i->_first); } + int intersects_at(Interval* i) const { return _first->intersects_at(i->_first); } + + // range iteration + void rewind_range() { _current = _first; } + void next_range() { assert(this != _end, "not allowed on sentinel"); _current = _current->next(); } + int current_from() const { return _current->from(); } + int current_to() const { return _current->to(); } + bool current_at_end() const { return _current == Range::end(); } + bool current_intersects(Interval* it) { return _current->intersects(it->_current); }; + int current_intersects_at(Interval* it) { return _current->intersects_at(it->_current); }; + + // printing + void print(outputStream* out = tty) const PRODUCT_RETURN; +}; + + +class IntervalWalker : public CompilationResourceObj { + protected: + Compilation* _compilation; + LinearScan* _allocator; + + Interval* _unhandled_first[nofKinds]; // sorted list of intervals, not life before the current position + Interval* _active_first [nofKinds]; // sorted list of intervals, life at the current position + Interval* _inactive_first [nofKinds]; // sorted list of intervals, intervals in a life time hole at the current position + + Interval* _current; // the current interval coming from unhandled list + int _current_position; // the current position (intercept point through the intervals) + IntervalKind _current_kind; // and whether it is fixed_kind or any_kind. + + + Compilation* compilation() const { return _compilation; } + LinearScan* allocator() const { return _allocator; } + + // unified bailout support + void bailout(const char* msg) const { compilation()->bailout(msg); } + bool bailed_out() const { return compilation()->bailed_out(); } + + void check_bounds(IntervalKind kind) { assert(kind >= fixedKind && kind <= anyKind, "invalid interval_kind"); } + + Interval** unhandled_first_addr(IntervalKind kind) { check_bounds(kind); return &_unhandled_first[kind]; } + Interval** active_first_addr(IntervalKind kind) { check_bounds(kind); return &_active_first[kind]; } + Interval** inactive_first_addr(IntervalKind kind) { check_bounds(kind); return &_inactive_first[kind]; } + + void append_unsorted(Interval** first, Interval* interval); + void append_sorted(Interval** first, Interval* interval); + void append_to_unhandled(Interval** list, Interval* interval); + + bool remove_from_list(Interval** list, Interval* i); + void remove_from_list(Interval* i); + + void next_interval(); + Interval* current() const { return _current; } + IntervalKind current_kind() const { return _current_kind; } + + void walk_to(IntervalState state, int from); + + // activate_current() is called when an unhandled interval becomes active (in current(), current_kind()). + // Return false if current() should not be moved the the active interval list. + // It is safe to append current to any interval list but the unhandled list. + virtual bool activate_current() { return true; } + + // interval_moved() is called whenever an interval moves from one interval list to another. + // In the implementation of this method it is prohibited to move the interval to any list. + virtual void interval_moved(Interval* interval, IntervalKind kind, IntervalState from, IntervalState to); + + public: + IntervalWalker(LinearScan* allocator, Interval* unhandled_fixed_first, Interval* unhandled_any_first); + + Interval* unhandled_first(IntervalKind kind) { check_bounds(kind); return _unhandled_first[kind]; } + Interval* active_first(IntervalKind kind) { check_bounds(kind); return _active_first[kind]; } + Interval* inactive_first(IntervalKind kind) { check_bounds(kind); return _inactive_first[kind]; } + + // active contains the intervals that are live after the lir_op + void walk_to(int lir_op_id); + // active contains the intervals that are live before the lir_op + void walk_before(int lir_op_id) { walk_to(lir_op_id-1); } + // walk through all intervals + void walk() { walk_to(max_jint); } + + int current_position() { return _current_position; } +}; + + +// The actual linear scan register allocator +class LinearScanWalker : public IntervalWalker { + enum { + any_reg = LinearScan::any_reg + }; + + private: + int _first_reg; // the reg. number of the first phys. register + int _last_reg; // the reg. nmber of the last phys. register + int _num_phys_regs; // required by current interval + bool _adjacent_regs; // have lo/hi words of phys. regs be adjacent + + int _use_pos[LinearScan::nof_regs]; + int _block_pos[LinearScan::nof_regs]; + IntervalList* _spill_intervals[LinearScan::nof_regs]; + + MoveResolver _move_resolver; // for ordering spill moves + + // accessors mapped to same functions in class LinearScan + int block_count() const { return allocator()->block_count(); } + BlockBegin* block_at(int idx) const { return allocator()->block_at(idx); } + BlockBegin* block_of_op_with_id(int op_id) const { return allocator()->block_of_op_with_id(op_id); } + + void init_use_lists(bool only_process_use_pos); + void exclude_from_use(int reg); + void exclude_from_use(Interval* i); + void set_use_pos(int reg, Interval* i, int use_pos, bool only_process_use_pos); + void set_use_pos(Interval* i, int use_pos, bool only_process_use_pos); + void set_block_pos(int reg, Interval* i, int block_pos); + void set_block_pos(Interval* i, int block_pos); + + void free_exclude_active_fixed(); + void free_exclude_active_any(); + void free_collect_inactive_fixed(Interval* cur); + void free_collect_inactive_any(Interval* cur); + void free_collect_unhandled(IntervalKind kind, Interval* cur); + void spill_exclude_active_fixed(); + void spill_block_unhandled_fixed(Interval* cur); + void spill_block_inactive_fixed(Interval* cur); + void spill_collect_active_any(); + void spill_collect_inactive_any(Interval* cur); + + void insert_move(int op_id, Interval* src_it, Interval* dst_it); + int find_optimal_split_pos(BlockBegin* min_block, BlockBegin* max_block, int max_split_pos); + int find_optimal_split_pos(Interval* it, int min_split_pos, int max_split_pos, bool do_loop_optimization); + void split_before_usage(Interval* it, int min_split_pos, int max_split_pos); + void split_for_spilling(Interval* it); + void split_stack_interval(Interval* it); + void split_when_partial_register_available(Interval* it, int register_available_until); + void split_and_spill_interval(Interval* it); + + int find_free_reg(int reg_needed_until, int interval_to, int hint_reg, int ignore_reg, bool* need_split); + int find_free_double_reg(int reg_needed_until, int interval_to, int hint_reg, bool* need_split); + bool alloc_free_reg(Interval* cur); + + int find_locked_reg(int reg_needed_until, int interval_to, int hint_reg, int ignore_reg, bool* need_split); + int find_locked_double_reg(int reg_needed_until, int interval_to, int hint_reg, bool* need_split); + void split_and_spill_intersecting_intervals(int reg, int regHi); + void alloc_locked_reg(Interval* cur); + + bool no_allocation_possible(Interval* cur); + void update_phys_reg_range(bool requires_cpu_register); + void init_vars_for_alloc(Interval* cur); + bool pd_init_regs_for_alloc(Interval* cur); + + void combine_spilled_intervals(Interval* cur); + bool is_move(LIR_Op* op, Interval* from, Interval* to); + + bool activate_current(); + + public: + LinearScanWalker(LinearScan* allocator, Interval* unhandled_fixed_first, Interval* unhandled_any_first); + + // must be called when all intervals are allocated + void finish_allocation() { _move_resolver.resolve_and_append_moves(); } +}; + + + +/* +When a block has more than one predecessor, and all predecessors end with +the same sequence of move-instructions, than this moves can be placed once +at the beginning of the block instead of multiple times in the predecessors. + +Similarly, when a block has more than one successor, then equal sequences of +moves at the beginning of the successors can be placed once at the end of +the block. But because the moves must be inserted before all branch +instructions, this works only when there is exactly one conditional branch +at the end of the block (because the moves must be inserted before all +branches, but after all compares). + +This optimization affects all kind of moves (reg->reg, reg->stack and +stack->reg). Because this optimization works best when a block contains only +few moves, it has a huge impact on the number of blocks that are totally +empty. +*/ +class EdgeMoveOptimizer : public StackObj { + private: + // the class maintains a list with all lir-instruction-list of the + // successors (predecessors) and the current index into the lir-lists + LIR_OpListStack _edge_instructions; + intStack _edge_instructions_idx; + + void init_instructions(); + void append_instructions(LIR_OpList* instructions, int instructions_idx); + LIR_Op* instruction_at(int edge); + void remove_cur_instruction(int edge, bool decrement_index); + + bool operations_different(LIR_Op* op1, LIR_Op* op2); + + void optimize_moves_at_block_end(BlockBegin* cur); + void optimize_moves_at_block_begin(BlockBegin* cur); + + EdgeMoveOptimizer(); + + public: + static void optimize(BlockList* code); +}; + + + +class ControlFlowOptimizer : public StackObj { + private: + BlockList _original_preds; + + enum { + ShortLoopSize = 5 + }; + void reorder_short_loop(BlockList* code, BlockBegin* header_block, int header_idx); + void reorder_short_loops(BlockList* code); + + bool can_delete_block(BlockBegin* cur); + void substitute_branch_target(BlockBegin* cur, BlockBegin* target_from, BlockBegin* target_to); + void delete_empty_blocks(BlockList* code); + + void delete_unnecessary_jumps(BlockList* code); + void delete_jumps_to_return(BlockList* code); + + DEBUG_ONLY(void verify(BlockList* code);) + + ControlFlowOptimizer(); + public: + static void optimize(BlockList* code); +}; + + +#ifndef PRODUCT + +// Helper class for collecting statistics of LinearScan +class LinearScanStatistic : public StackObj { + public: + enum Counter { + // general counters + counter_method, + counter_fpu_method, + counter_loop_method, + counter_exception_method, + counter_loop, + counter_block, + counter_loop_block, + counter_exception_block, + counter_interval, + counter_fixed_interval, + counter_range, + counter_fixed_range, + counter_use_pos, + counter_fixed_use_pos, + counter_spill_slots, + blank_line_1, + + // counter for classes of lir instructions + counter_instruction, + counter_label, + counter_entry, + counter_return, + counter_call, + counter_move, + counter_cmp, + counter_cond_branch, + counter_uncond_branch, + counter_stub_branch, + counter_alu, + counter_alloc, + counter_sync, + counter_throw, + counter_unwind, + counter_typecheck, + counter_fpu_stack, + counter_misc_inst, + counter_other_inst, + blank_line_2, + + // counter for different types of moves + counter_move_total, + counter_move_reg_reg, + counter_move_reg_stack, + counter_move_stack_reg, + counter_move_stack_stack, + counter_move_reg_mem, + counter_move_mem_reg, + counter_move_const_any, + + number_of_counters, + invalid_counter = -1 + }; + + private: + int _counters_sum[number_of_counters]; + int _counters_max[number_of_counters]; + + void inc_counter(Counter idx, int value = 1) { _counters_sum[idx] += value; } + + const char* counter_name(int counter_idx); + Counter base_counter(int counter_idx); + + void sum_up(LinearScanStatistic &method_statistic); + void collect(LinearScan* allocator); + + public: + LinearScanStatistic(); + void print(const char* title); + static void compute(LinearScan* allocator, LinearScanStatistic &global_statistic); +}; + + +// Helper class for collecting compilation time of LinearScan +class LinearScanTimers : public StackObj { + public: + enum Timer { + timer_do_nothing, + timer_number_instructions, + timer_compute_local_live_sets, + timer_compute_global_live_sets, + timer_build_intervals, + timer_sort_intervals_before, + timer_allocate_registers, + timer_resolve_data_flow, + timer_sort_intervals_after, + timer_eliminate_spill_moves, + timer_assign_reg_num, + timer_allocate_fpu_stack, + timer_optimize_lir, + + number_of_timers + }; + + private: + elapsedTimer _timers[number_of_timers]; + const char* timer_name(int idx); + + public: + LinearScanTimers(); + + void begin_method(); // called for each method when register allocation starts + void end_method(LinearScan* allocator); // called for each method when register allocation completed + void print(double total_time); // called before termination of VM to print global summary + + elapsedTimer* timer(int idx) { return &(_timers[idx]); } +}; + + +#endif // ifndef PRODUCT + + +// Pick up platform-dependent implementation details +# include "incls/_c1_LinearScan_pd.hpp.incl" diff --git a/hotspot/src/share/vm/c1/c1_MacroAssembler.hpp b/hotspot/src/share/vm/c1/c1_MacroAssembler.hpp new file mode 100644 index 00000000000..79f3969f7a1 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_MacroAssembler.hpp @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class CodeEmitInfo; + +class C1_MacroAssembler: public MacroAssembler { + public: + // creation + C1_MacroAssembler(CodeBuffer* code) : MacroAssembler(code) { pd_init(); } + + //---------------------------------------------------- + void explicit_null_check(Register base); + + void inline_cache_check(Register receiver, Register iCache); + void build_frame(int frame_size_in_bytes); + void method_exit(bool restore_frame); + + void unverified_entry(Register receiver, Register ic_klass); + void verified_entry(); + void verify_stack_oop(int offset) PRODUCT_RETURN; + void verify_not_null_oop(Register r) PRODUCT_RETURN; + +#include "incls/_c1_MacroAssembler_pd.hpp.incl" +}; + + + +// A StubAssembler is a MacroAssembler w/ extra functionality for runtime +// stubs. Currently it 'knows' some stub info. Eventually, the information +// may be set automatically or can be asserted when using specialised +// StubAssembler functions. + +class StubAssembler: public C1_MacroAssembler { + private: + const char* _name; + bool _must_gc_arguments; + int _frame_size; + int _num_rt_args; + int _stub_id; + + public: + // creation + StubAssembler(CodeBuffer* code, const char * name, int stub_id); + void set_info(const char* name, bool must_gc_arguments); + + void set_frame_size(int size); + void set_num_rt_args(int args); + + // accessors + const char* name() const { return _name; } + bool must_gc_arguments() const { return _must_gc_arguments; } + int frame_size() const { return _frame_size; } + int num_rt_args() const { return _num_rt_args; } + int stub_id() const { return _stub_id; } + + // runtime calls (return offset of call to be used by GC map) + int call_RT(Register oop_result1, Register oop_result2, address entry, int args_size = 0); + int call_RT(Register oop_result1, Register oop_result2, address entry, Register arg1); + int call_RT(Register oop_result1, Register oop_result2, address entry, Register arg1, Register arg2); + int call_RT(Register oop_result1, Register oop_result2, address entry, Register arg1, Register arg2, Register arg3); +}; diff --git a/hotspot/src/share/vm/c1/c1_Optimizer.cpp b/hotspot/src/share/vm/c1/c1_Optimizer.cpp new file mode 100644 index 00000000000..ea4b3a576db --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Optimizer.cpp @@ -0,0 +1,1070 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_Optimizer.cpp.incl" + +define_array(ValueSetArray, ValueSet*); +define_stack(ValueSetList, ValueSetArray); + + +Optimizer::Optimizer(IR* ir) { + assert(ir->is_valid(), "IR must be valid"); + _ir = ir; +} + +class CE_Eliminator: public BlockClosure { + private: + IR* _hir; + int _cee_count; // the number of CEs successfully eliminated + int _has_substitution; + + public: + CE_Eliminator(IR* hir) : _cee_count(0), _hir(hir) { + _has_substitution = false; + _hir->iterate_preorder(this); + if (_has_substitution) { + // substituted some phis so resolve the substitution + SubstitutionResolver sr(_hir); + } + } + int cee_count() const { return _cee_count; } + + void adjust_exception_edges(BlockBegin* block, BlockBegin* sux) { + int e = sux->number_of_exception_handlers(); + for (int i = 0; i < e; i++) { + BlockBegin* xhandler = sux->exception_handler_at(i); + block->add_exception_handler(xhandler); + + assert(xhandler->is_predecessor(sux), "missing predecessor"); + if (sux->number_of_preds() == 0) { + // sux is disconnected from graph so disconnect from exception handlers + xhandler->remove_predecessor(sux); + } + if (!xhandler->is_predecessor(block)) { + xhandler->add_predecessor(block); + } + } + } + + virtual void block_do(BlockBegin* block) { + // 1) find conditional expression + // check if block ends with an If + If* if_ = block->end()->as_If(); + if (if_ == NULL) return; + + // check if If works on int or object types + // (we cannot handle If's working on long, float or doubles yet, + // since IfOp doesn't support them - these If's show up if cmp + // operations followed by If's are eliminated) + ValueType* if_type = if_->x()->type(); + if (!if_type->is_int() && !if_type->is_object()) return; + + BlockBegin* t_block = if_->tsux(); + BlockBegin* f_block = if_->fsux(); + Instruction* t_cur = t_block->next(); + Instruction* f_cur = f_block->next(); + + // one Constant may be present between BlockBegin and BlockEnd + Value t_const = NULL; + Value f_const = NULL; + if (t_cur->as_Constant() != NULL && !t_cur->can_trap()) { + t_const = t_cur; + t_cur = t_cur->next(); + } + if (f_cur->as_Constant() != NULL && !f_cur->can_trap()) { + f_const = f_cur; + f_cur = f_cur->next(); + } + + // check if both branches end with a goto + Goto* t_goto = t_cur->as_Goto(); + if (t_goto == NULL) return; + Goto* f_goto = f_cur->as_Goto(); + if (f_goto == NULL) return; + + // check if both gotos merge into the same block + BlockBegin* sux = t_goto->default_sux(); + if (sux != f_goto->default_sux()) return; + + // check if at least one word was pushed on sux_state + ValueStack* sux_state = sux->state(); + if (sux_state->stack_size() <= if_->state()->stack_size()) return; + + // check if phi function is present at end of successor stack and that + // only this phi was pushed on the stack + Value sux_phi = sux_state->stack_at(if_->state()->stack_size()); + if (sux_phi == NULL || sux_phi->as_Phi() == NULL || sux_phi->as_Phi()->block() != sux) return; + if (sux_phi->type()->size() != sux_state->stack_size() - if_->state()->stack_size()) return; + + // get the values that were pushed in the true- and false-branch + Value t_value = t_goto->state()->stack_at(if_->state()->stack_size()); + Value f_value = f_goto->state()->stack_at(if_->state()->stack_size()); + + // backend does not support floats + assert(t_value->type()->base() == f_value->type()->base(), "incompatible types"); + if (t_value->type()->is_float_kind()) return; + + // check that successor has no other phi functions but sux_phi + // this can happen when t_block or f_block contained additonal stores to local variables + // that are no longer represented by explicit instructions + for_each_phi_fun(sux, phi, + if (phi != sux_phi) return; + ); + // true and false blocks can't have phis + for_each_phi_fun(t_block, phi, return; ); + for_each_phi_fun(f_block, phi, return; ); + + // 2) substitute conditional expression + // with an IfOp followed by a Goto + // cut if_ away and get node before + Instruction* cur_end = if_->prev(block); + int bci = if_->bci(); + + // append constants of true- and false-block if necessary + // clone constants because original block must not be destroyed + assert((t_value != f_const && f_value != t_const) || t_const == f_const, "mismatch"); + if (t_value == t_const) { + t_value = new Constant(t_const->type()); + cur_end = cur_end->set_next(t_value, bci); + } + if (f_value == f_const) { + f_value = new Constant(f_const->type()); + cur_end = cur_end->set_next(f_value, bci); + } + + // it is very unlikely that the condition can be statically decided + // (this was checked previously by the Canonicalizer), so always + // append IfOp + Value result = new IfOp(if_->x(), if_->cond(), if_->y(), t_value, f_value); + cur_end = cur_end->set_next(result, bci); + + // append Goto to successor + ValueStack* state_before = if_->is_safepoint() ? if_->state_before() : NULL; + Goto* goto_ = new Goto(sux, state_before, if_->is_safepoint() || t_goto->is_safepoint() || f_goto->is_safepoint()); + + // prepare state for Goto + ValueStack* goto_state = if_->state(); + while (sux_state->scope() != goto_state->scope()) { + goto_state = goto_state->pop_scope(); + assert(goto_state != NULL, "states do not match up"); + } + goto_state = goto_state->copy(); + goto_state->push(result->type(), result); + assert(goto_state->is_same_across_scopes(sux_state), "states must match now"); + goto_->set_state(goto_state); + + // Steal the bci for the goto from the sux + cur_end = cur_end->set_next(goto_, sux->bci()); + + // Adjust control flow graph + BlockBegin::disconnect_edge(block, t_block); + BlockBegin::disconnect_edge(block, f_block); + if (t_block->number_of_preds() == 0) { + BlockBegin::disconnect_edge(t_block, sux); + } + adjust_exception_edges(block, t_block); + if (f_block->number_of_preds() == 0) { + BlockBegin::disconnect_edge(f_block, sux); + } + adjust_exception_edges(block, f_block); + + // update block end + block->set_end(goto_); + + // substitute the phi if possible + if (sux_phi->as_Phi()->operand_count() == 1) { + assert(sux_phi->as_Phi()->operand_at(0) == result, "screwed up phi"); + sux_phi->set_subst(result); + _has_substitution = true; + } + + // 3) successfully eliminated a conditional expression + _cee_count++; + if (PrintCEE) { + tty->print_cr("%d. CEE in B%d (B%d B%d)", cee_count(), block->block_id(), t_block->block_id(), f_block->block_id()); + } + + _hir->verify(); + } +}; + + +void Optimizer::eliminate_conditional_expressions() { + // find conditional expressions & replace them with IfOps + CE_Eliminator ce(ir()); +} + + +class BlockMerger: public BlockClosure { + private: + IR* _hir; + int _merge_count; // the number of block pairs successfully merged + + public: + BlockMerger(IR* hir) + : _hir(hir) + , _merge_count(0) + { + _hir->iterate_preorder(this); + } + + bool try_merge(BlockBegin* block) { + BlockEnd* end = block->end(); + if (end->as_Goto() != NULL) { + assert(end->number_of_sux() == 1, "end must have exactly one successor"); + // Note: It would be sufficient to check for the number of successors (= 1) + // in order to decide if this block can be merged potentially. That + // would then also include switch statements w/ only a default case. + // However, in that case we would need to make sure the switch tag + // expression is executed if it can produce observable side effects. + // We should probably have the canonicalizer simplifying such switch + // statements and then we are sure we don't miss these merge opportunities + // here (was bug - gri 7/7/99). + BlockBegin* sux = end->default_sux(); + if (sux->number_of_preds() == 1 && !sux->is_entry_block() && !end->is_safepoint()) { + // merge the two blocks + +#ifdef ASSERT + // verify that state at the end of block and at the beginning of sux are equal + // no phi functions must be present at beginning of sux + ValueStack* sux_state = sux->state(); + ValueStack* end_state = end->state(); + while (end_state->scope() != sux_state->scope()) { + // match up inlining level + end_state = end_state->pop_scope(); + } + assert(end_state->stack_size() == sux_state->stack_size(), "stack not equal"); + assert(end_state->locals_size() == sux_state->locals_size(), "locals not equal"); + + int index; + Value sux_value; + for_each_stack_value(sux_state, index, sux_value) { + assert(sux_value == end_state->stack_at(index), "stack not equal"); + } + for_each_local_value(sux_state, index, sux_value) { + assert(sux_value == end_state->local_at(index), "locals not equal"); + } + assert(sux_state->caller_state() == end_state->caller_state(), "caller not equal"); +#endif + + // find instruction before end & append first instruction of sux block + Instruction* prev = end->prev(block); + Instruction* next = sux->next(); + assert(prev->as_BlockEnd() == NULL, "must not be a BlockEnd"); + prev->set_next(next, next->bci()); + sux->disconnect_from_graph(); + block->set_end(sux->end()); + // add exception handlers of deleted block, if any + for (int k = 0; k < sux->number_of_exception_handlers(); k++) { + BlockBegin* xhandler = sux->exception_handler_at(k); + block->add_exception_handler(xhandler); + + // also substitute predecessor of exception handler + assert(xhandler->is_predecessor(sux), "missing predecessor"); + xhandler->remove_predecessor(sux); + if (!xhandler->is_predecessor(block)) { + xhandler->add_predecessor(block); + } + } + + // debugging output + _merge_count++; + if (PrintBlockElimination) { + tty->print_cr("%d. merged B%d & B%d (stack size = %d)", + _merge_count, block->block_id(), sux->block_id(), sux->state()->stack_size()); + } + + _hir->verify(); + + If* if_ = block->end()->as_If(); + if (if_) { + IfOp* ifop = if_->x()->as_IfOp(); + Constant* con = if_->y()->as_Constant(); + bool swapped = false; + if (!con || !ifop) { + ifop = if_->y()->as_IfOp(); + con = if_->x()->as_Constant(); + swapped = true; + } + if (con && ifop) { + Constant* tval = ifop->tval()->as_Constant(); + Constant* fval = ifop->fval()->as_Constant(); + if (tval && fval) { + // Find the instruction before if_, starting with ifop. + // When if_ and ifop are not in the same block, prev + // becomes NULL In such (rare) cases it is not + // profitable to perform the optimization. + Value prev = ifop; + while (prev != NULL && prev->next() != if_) { + prev = prev->next(); + } + + if (prev != NULL) { + Instruction::Condition cond = if_->cond(); + BlockBegin* tsux = if_->tsux(); + BlockBegin* fsux = if_->fsux(); + if (swapped) { + cond = Instruction::mirror(cond); + tsux = if_->fsux(); + fsux = if_->tsux(); + } + + BlockBegin* tblock = tval->compare(cond, con, tsux, fsux); + BlockBegin* fblock = fval->compare(cond, con, tsux, fsux); + if (tblock != fblock && !if_->is_safepoint()) { + If* newif = new If(ifop->x(), ifop->cond(), false, ifop->y(), + tblock, fblock, if_->state_before(), if_->is_safepoint()); + newif->set_state(if_->state()->copy()); + + assert(prev->next() == if_, "must be guaranteed by above search"); + prev->set_next(newif, if_->bci()); + block->set_end(newif); + + _merge_count++; + if (PrintBlockElimination) { + tty->print_cr("%d. replaced If and IfOp at end of B%d with single If", _merge_count, block->block_id()); + } + + _hir->verify(); + } + } + } + } + } + + return true; + } + } + return false; + } + + virtual void block_do(BlockBegin* block) { + _hir->verify(); + // repeat since the same block may merge again + while (try_merge(block)) { + _hir->verify(); + } + } +}; + + +void Optimizer::eliminate_blocks() { + // merge blocks if possible + BlockMerger bm(ir()); +} + + +class NullCheckEliminator; +class NullCheckVisitor: public InstructionVisitor { +private: + NullCheckEliminator* _nce; + NullCheckEliminator* nce() { return _nce; } + +public: + NullCheckVisitor() {} + + void set_eliminator(NullCheckEliminator* nce) { _nce = nce; } + + void do_Phi (Phi* x); + void do_Local (Local* x); + void do_Constant (Constant* x); + void do_LoadField (LoadField* x); + void do_StoreField (StoreField* x); + void do_ArrayLength (ArrayLength* x); + void do_LoadIndexed (LoadIndexed* x); + void do_StoreIndexed (StoreIndexed* x); + void do_NegateOp (NegateOp* x); + void do_ArithmeticOp (ArithmeticOp* x); + void do_ShiftOp (ShiftOp* x); + void do_LogicOp (LogicOp* x); + void do_CompareOp (CompareOp* x); + void do_IfOp (IfOp* x); + void do_Convert (Convert* x); + void do_NullCheck (NullCheck* x); + void do_Invoke (Invoke* x); + void do_NewInstance (NewInstance* x); + void do_NewTypeArray (NewTypeArray* x); + void do_NewObjectArray (NewObjectArray* x); + void do_NewMultiArray (NewMultiArray* x); + void do_CheckCast (CheckCast* x); + void do_InstanceOf (InstanceOf* x); + void do_MonitorEnter (MonitorEnter* x); + void do_MonitorExit (MonitorExit* x); + void do_Intrinsic (Intrinsic* x); + void do_BlockBegin (BlockBegin* x); + void do_Goto (Goto* x); + void do_If (If* x); + void do_IfInstanceOf (IfInstanceOf* x); + void do_TableSwitch (TableSwitch* x); + void do_LookupSwitch (LookupSwitch* x); + void do_Return (Return* x); + void do_Throw (Throw* x); + void do_Base (Base* x); + void do_OsrEntry (OsrEntry* x); + void do_ExceptionObject(ExceptionObject* x); + void do_RoundFP (RoundFP* x); + void do_UnsafeGetRaw (UnsafeGetRaw* x); + void do_UnsafePutRaw (UnsafePutRaw* x); + void do_UnsafeGetObject(UnsafeGetObject* x); + void do_UnsafePutObject(UnsafePutObject* x); + void do_UnsafePrefetchRead (UnsafePrefetchRead* x); + void do_UnsafePrefetchWrite(UnsafePrefetchWrite* x); + void do_ProfileCall (ProfileCall* x); + void do_ProfileCounter (ProfileCounter* x); +}; + + +// Because of a static contained within (for the purpose of iteration +// over instructions), it is only valid to have one of these active at +// a time +class NullCheckEliminator { + private: + static NullCheckEliminator* _static_nce; + static void do_value(Value* vp); + + Optimizer* _opt; + + ValueSet* _visitable_instructions; // Visit each instruction only once per basic block + BlockList* _work_list; // Basic blocks to visit + + bool visitable(Value x) { + assert(_visitable_instructions != NULL, "check"); + return _visitable_instructions->contains(x); + } + void mark_visited(Value x) { + assert(_visitable_instructions != NULL, "check"); + _visitable_instructions->remove(x); + } + void mark_visitable(Value x) { + assert(_visitable_instructions != NULL, "check"); + _visitable_instructions->put(x); + } + void clear_visitable_state() { + assert(_visitable_instructions != NULL, "check"); + _visitable_instructions->clear(); + } + + ValueSet* _set; // current state, propagated to subsequent BlockBegins + ValueSetList _block_states; // BlockBegin null-check states for all processed blocks + NullCheckVisitor _visitor; + NullCheck* _last_explicit_null_check; + + bool set_contains(Value x) { assert(_set != NULL, "check"); return _set->contains(x); } + void set_put (Value x) { assert(_set != NULL, "check"); _set->put(x); } + void set_remove (Value x) { assert(_set != NULL, "check"); _set->remove(x); } + + BlockList* work_list() { return _work_list; } + + void iterate_all(); + void iterate_one(BlockBegin* block); + + ValueSet* state() { return _set; } + void set_state_from (ValueSet* state) { _set->set_from(state); } + ValueSet* state_for (BlockBegin* block) { return _block_states[block->block_id()]; } + void set_state_for (BlockBegin* block, ValueSet* stack) { _block_states[block->block_id()] = stack; } + // Returns true if caused a change in the block's state. + bool merge_state_for(BlockBegin* block, + ValueSet* incoming_state); + + public: + // constructor + NullCheckEliminator(Optimizer* opt) + : _opt(opt) + , _set(new ValueSet()) + , _last_explicit_null_check(NULL) + , _block_states(BlockBegin::number_of_blocks(), NULL) + , _work_list(new BlockList()) { + _visitable_instructions = new ValueSet(); + _visitor.set_eliminator(this); + } + + Optimizer* opt() { return _opt; } + IR* ir () { return opt()->ir(); } + + // Process a graph + void iterate(BlockBegin* root); + + // In some situations (like NullCheck(x); getfield(x)) the debug + // information from the explicit NullCheck can be used to populate + // the getfield, even if the two instructions are in different + // scopes; this allows implicit null checks to be used but the + // correct exception information to be generated. We must clear the + // last-traversed NullCheck when we reach a potentially-exception- + // throwing instruction, as well as in some other cases. + void set_last_explicit_null_check(NullCheck* check) { _last_explicit_null_check = check; } + NullCheck* last_explicit_null_check() { return _last_explicit_null_check; } + Value last_explicit_null_check_obj() { return (_last_explicit_null_check + ? _last_explicit_null_check->obj() + : NULL); } + NullCheck* consume_last_explicit_null_check() { + _last_explicit_null_check->unpin(Instruction::PinExplicitNullCheck); + _last_explicit_null_check->set_can_trap(false); + return _last_explicit_null_check; + } + void clear_last_explicit_null_check() { _last_explicit_null_check = NULL; } + + // Handlers for relevant instructions + // (separated out from NullCheckVisitor for clarity) + + // The basic contract is that these must leave the instruction in + // the desired state; must not assume anything about the state of + // the instruction. We make multiple passes over some basic blocks + // and the last pass is the only one whose result is valid. + void handle_AccessField (AccessField* x); + void handle_ArrayLength (ArrayLength* x); + void handle_LoadIndexed (LoadIndexed* x); + void handle_StoreIndexed (StoreIndexed* x); + void handle_NullCheck (NullCheck* x); + void handle_Invoke (Invoke* x); + void handle_NewInstance (NewInstance* x); + void handle_NewArray (NewArray* x); + void handle_AccessMonitor (AccessMonitor* x); + void handle_Intrinsic (Intrinsic* x); + void handle_ExceptionObject (ExceptionObject* x); + void handle_Phi (Phi* x); +}; + + +// NEEDS_CLEANUP +// There may be other instructions which need to clear the last +// explicit null check. Anything across which we can not hoist the +// debug information for a NullCheck instruction must clear it. It +// might be safer to pattern match "NullCheck ; {AccessField, +// ArrayLength, LoadIndexed}" but it is more easily structured this way. +// Should test to see performance hit of clearing it for all handlers +// with empty bodies below. If it is negligible then we should leave +// that in for safety, otherwise should think more about it. +void NullCheckVisitor::do_Phi (Phi* x) { nce()->handle_Phi(x); } +void NullCheckVisitor::do_Local (Local* x) {} +void NullCheckVisitor::do_Constant (Constant* x) { /* FIXME: handle object constants */ } +void NullCheckVisitor::do_LoadField (LoadField* x) { nce()->handle_AccessField(x); } +void NullCheckVisitor::do_StoreField (StoreField* x) { nce()->handle_AccessField(x); } +void NullCheckVisitor::do_ArrayLength (ArrayLength* x) { nce()->handle_ArrayLength(x); } +void NullCheckVisitor::do_LoadIndexed (LoadIndexed* x) { nce()->handle_LoadIndexed(x); } +void NullCheckVisitor::do_StoreIndexed (StoreIndexed* x) { nce()->handle_StoreIndexed(x); } +void NullCheckVisitor::do_NegateOp (NegateOp* x) {} +void NullCheckVisitor::do_ArithmeticOp (ArithmeticOp* x) { if (x->can_trap()) nce()->clear_last_explicit_null_check(); } +void NullCheckVisitor::do_ShiftOp (ShiftOp* x) {} +void NullCheckVisitor::do_LogicOp (LogicOp* x) {} +void NullCheckVisitor::do_CompareOp (CompareOp* x) {} +void NullCheckVisitor::do_IfOp (IfOp* x) {} +void NullCheckVisitor::do_Convert (Convert* x) {} +void NullCheckVisitor::do_NullCheck (NullCheck* x) { nce()->handle_NullCheck(x); } +void NullCheckVisitor::do_Invoke (Invoke* x) { nce()->handle_Invoke(x); } +void NullCheckVisitor::do_NewInstance (NewInstance* x) { nce()->handle_NewInstance(x); } +void NullCheckVisitor::do_NewTypeArray (NewTypeArray* x) { nce()->handle_NewArray(x); } +void NullCheckVisitor::do_NewObjectArray (NewObjectArray* x) { nce()->handle_NewArray(x); } +void NullCheckVisitor::do_NewMultiArray (NewMultiArray* x) { nce()->handle_NewArray(x); } +void NullCheckVisitor::do_CheckCast (CheckCast* x) {} +void NullCheckVisitor::do_InstanceOf (InstanceOf* x) {} +void NullCheckVisitor::do_MonitorEnter (MonitorEnter* x) { nce()->handle_AccessMonitor(x); } +void NullCheckVisitor::do_MonitorExit (MonitorExit* x) { nce()->handle_AccessMonitor(x); } +void NullCheckVisitor::do_Intrinsic (Intrinsic* x) { nce()->clear_last_explicit_null_check(); } +void NullCheckVisitor::do_BlockBegin (BlockBegin* x) {} +void NullCheckVisitor::do_Goto (Goto* x) {} +void NullCheckVisitor::do_If (If* x) {} +void NullCheckVisitor::do_IfInstanceOf (IfInstanceOf* x) {} +void NullCheckVisitor::do_TableSwitch (TableSwitch* x) {} +void NullCheckVisitor::do_LookupSwitch (LookupSwitch* x) {} +void NullCheckVisitor::do_Return (Return* x) {} +void NullCheckVisitor::do_Throw (Throw* x) { nce()->clear_last_explicit_null_check(); } +void NullCheckVisitor::do_Base (Base* x) {} +void NullCheckVisitor::do_OsrEntry (OsrEntry* x) {} +void NullCheckVisitor::do_ExceptionObject(ExceptionObject* x) { nce()->handle_ExceptionObject(x); } +void NullCheckVisitor::do_RoundFP (RoundFP* x) {} +void NullCheckVisitor::do_UnsafeGetRaw (UnsafeGetRaw* x) {} +void NullCheckVisitor::do_UnsafePutRaw (UnsafePutRaw* x) {} +void NullCheckVisitor::do_UnsafeGetObject(UnsafeGetObject* x) {} +void NullCheckVisitor::do_UnsafePutObject(UnsafePutObject* x) {} +void NullCheckVisitor::do_UnsafePrefetchRead (UnsafePrefetchRead* x) {} +void NullCheckVisitor::do_UnsafePrefetchWrite(UnsafePrefetchWrite* x) {} +void NullCheckVisitor::do_ProfileCall (ProfileCall* x) { nce()->clear_last_explicit_null_check(); } +void NullCheckVisitor::do_ProfileCounter (ProfileCounter* x) {} + + +NullCheckEliminator* NullCheckEliminator::_static_nce = NULL; + + +void NullCheckEliminator::do_value(Value* p) { + assert(*p != NULL, "should not find NULL instructions"); + if (_static_nce->visitable(*p)) { + _static_nce->mark_visited(*p); + (*p)->visit(&_static_nce->_visitor); + } +} + +bool NullCheckEliminator::merge_state_for(BlockBegin* block, ValueSet* incoming_state) { + ValueSet* state = state_for(block); + if (state == NULL) { + state = incoming_state->copy(); + set_state_for(block, state); + return true; + } else { + bool changed = state->set_intersect(incoming_state); + if (PrintNullCheckElimination && changed) { + tty->print_cr("Block %d's null check state changed", block->block_id()); + } + return changed; + } +} + + +void NullCheckEliminator::iterate_all() { + while (work_list()->length() > 0) { + iterate_one(work_list()->pop()); + } +} + + +void NullCheckEliminator::iterate_one(BlockBegin* block) { + _static_nce = this; + clear_visitable_state(); + // clear out an old explicit null checks + set_last_explicit_null_check(NULL); + + if (PrintNullCheckElimination) { + tty->print_cr(" ...iterating block %d in null check elimination for %s::%s%s", + block->block_id(), + ir()->method()->holder()->name()->as_utf8(), + ir()->method()->name()->as_utf8(), + ir()->method()->signature()->as_symbol()->as_utf8()); + } + + // Create new state if none present (only happens at root) + if (state_for(block) == NULL) { + ValueSet* tmp_state = new ValueSet(); + set_state_for(block, tmp_state); + // Initial state is that local 0 (receiver) is non-null for + // non-static methods + ValueStack* stack = block->state(); + IRScope* scope = stack->scope(); + ciMethod* method = scope->method(); + if (!method->is_static()) { + Local* local0 = stack->local_at(0)->as_Local(); + assert(local0 != NULL, "must be"); + assert(local0->type() == objectType, "invalid type of receiver"); + + if (local0 != NULL) { + // Local 0 is used in this scope + tmp_state->put(local0); + if (PrintNullCheckElimination) { + tty->print_cr("Local 0 (value %d) proven non-null upon entry", local0->id()); + } + } + } + } + + // Must copy block's state to avoid mutating it during iteration + // through the block -- otherwise "not-null" states can accidentally + // propagate "up" through the block during processing of backward + // branches and algorithm is incorrect (and does not converge) + set_state_from(state_for(block)); + + // allow visiting of Phis belonging to this block + for_each_phi_fun(block, phi, + mark_visitable(phi); + ); + + BlockEnd* e = block->end(); + assert(e != NULL, "incomplete graph"); + int i; + + // Propagate the state before this block into the exception + // handlers. They aren't true successors since we aren't guaranteed + // to execute the whole block before executing them. Also putting + // them on first seems to help reduce the amount of iteration to + // reach a fixed point. + for (i = 0; i < block->number_of_exception_handlers(); i++) { + BlockBegin* next = block->exception_handler_at(i); + if (merge_state_for(next, state())) { + if (!work_list()->contains(next)) { + work_list()->push(next); + } + } + } + + // Iterate through block, updating state. + for (Instruction* instr = block; instr != NULL; instr = instr->next()) { + // Mark instructions in this block as visitable as they are seen + // in the instruction list. This keeps the iteration from + // visiting instructions which are references in other blocks or + // visiting instructions more than once. + mark_visitable(instr); + if (instr->is_root() || instr->can_trap() || (instr->as_NullCheck() != NULL)) { + mark_visited(instr); + instr->input_values_do(&NullCheckEliminator::do_value); + instr->visit(&_visitor); + } + } + + // Propagate state to successors if necessary + for (i = 0; i < e->number_of_sux(); i++) { + BlockBegin* next = e->sux_at(i); + if (merge_state_for(next, state())) { + if (!work_list()->contains(next)) { + work_list()->push(next); + } + } + } +} + + +void NullCheckEliminator::iterate(BlockBegin* block) { + work_list()->push(block); + iterate_all(); +} + +void NullCheckEliminator::handle_AccessField(AccessField* x) { + if (x->is_static()) { + if (x->as_LoadField() != NULL) { + // If the field is a non-null static final object field (as is + // often the case for sun.misc.Unsafe), put this LoadField into + // the non-null map + ciField* field = x->field(); + if (field->is_constant()) { + ciConstant field_val = field->constant_value(); + BasicType field_type = field_val.basic_type(); + if (field_type == T_OBJECT || field_type == T_ARRAY) { + ciObject* obj_val = field_val.as_object(); + if (!obj_val->is_null_object()) { + if (PrintNullCheckElimination) { + tty->print_cr("AccessField %d proven non-null by static final non-null oop check", + x->id()); + } + set_put(x); + } + } + } + } + // Be conservative + clear_last_explicit_null_check(); + return; + } + + Value obj = x->obj(); + if (set_contains(obj)) { + // Value is non-null => update AccessField + if (last_explicit_null_check_obj() == obj && !x->needs_patching()) { + x->set_explicit_null_check(consume_last_explicit_null_check()); + x->set_needs_null_check(true); + if (PrintNullCheckElimination) { + tty->print_cr("Folded NullCheck %d into AccessField %d's null check for value %d", + x->explicit_null_check()->id(), x->id(), obj->id()); + } + } else { + x->set_explicit_null_check(NULL); + x->set_needs_null_check(false); + if (PrintNullCheckElimination) { + tty->print_cr("Eliminated AccessField %d's null check for value %d", x->id(), obj->id()); + } + } + } else { + set_put(obj); + if (PrintNullCheckElimination) { + tty->print_cr("AccessField %d of value %d proves value to be non-null", x->id(), obj->id()); + } + // Ensure previous passes do not cause wrong state + x->set_needs_null_check(true); + x->set_explicit_null_check(NULL); + } + clear_last_explicit_null_check(); +} + + +void NullCheckEliminator::handle_ArrayLength(ArrayLength* x) { + Value array = x->array(); + if (set_contains(array)) { + // Value is non-null => update AccessArray + if (last_explicit_null_check_obj() == array) { + x->set_explicit_null_check(consume_last_explicit_null_check()); + x->set_needs_null_check(true); + if (PrintNullCheckElimination) { + tty->print_cr("Folded NullCheck %d into ArrayLength %d's null check for value %d", + x->explicit_null_check()->id(), x->id(), array->id()); + } + } else { + x->set_explicit_null_check(NULL); + x->set_needs_null_check(false); + if (PrintNullCheckElimination) { + tty->print_cr("Eliminated ArrayLength %d's null check for value %d", x->id(), array->id()); + } + } + } else { + set_put(array); + if (PrintNullCheckElimination) { + tty->print_cr("ArrayLength %d of value %d proves value to be non-null", x->id(), array->id()); + } + // Ensure previous passes do not cause wrong state + x->set_needs_null_check(true); + x->set_explicit_null_check(NULL); + } + clear_last_explicit_null_check(); +} + + +void NullCheckEliminator::handle_LoadIndexed(LoadIndexed* x) { + Value array = x->array(); + if (set_contains(array)) { + // Value is non-null => update AccessArray + if (last_explicit_null_check_obj() == array) { + x->set_explicit_null_check(consume_last_explicit_null_check()); + x->set_needs_null_check(true); + if (PrintNullCheckElimination) { + tty->print_cr("Folded NullCheck %d into LoadIndexed %d's null check for value %d", + x->explicit_null_check()->id(), x->id(), array->id()); + } + } else { + x->set_explicit_null_check(NULL); + x->set_needs_null_check(false); + if (PrintNullCheckElimination) { + tty->print_cr("Eliminated LoadIndexed %d's null check for value %d", x->id(), array->id()); + } + } + } else { + set_put(array); + if (PrintNullCheckElimination) { + tty->print_cr("LoadIndexed %d of value %d proves value to be non-null", x->id(), array->id()); + } + // Ensure previous passes do not cause wrong state + x->set_needs_null_check(true); + x->set_explicit_null_check(NULL); + } + clear_last_explicit_null_check(); +} + + +void NullCheckEliminator::handle_StoreIndexed(StoreIndexed* x) { + Value array = x->array(); + if (set_contains(array)) { + // Value is non-null => update AccessArray + if (PrintNullCheckElimination) { + tty->print_cr("Eliminated StoreIndexed %d's null check for value %d", x->id(), array->id()); + } + x->set_needs_null_check(false); + } else { + set_put(array); + if (PrintNullCheckElimination) { + tty->print_cr("StoreIndexed %d of value %d proves value to be non-null", x->id(), array->id()); + } + // Ensure previous passes do not cause wrong state + x->set_needs_null_check(true); + } + clear_last_explicit_null_check(); +} + + +void NullCheckEliminator::handle_NullCheck(NullCheck* x) { + Value obj = x->obj(); + if (set_contains(obj)) { + // Already proven to be non-null => this NullCheck is useless + if (PrintNullCheckElimination) { + tty->print_cr("Eliminated NullCheck %d for value %d", x->id(), obj->id()); + } + // Don't unpin since that may shrink obj's live range and make it unavailable for debug info. + // The code generator won't emit LIR for a NullCheck that cannot trap. + x->set_can_trap(false); + } else { + // May be null => add to map and set last explicit NullCheck + x->set_can_trap(true); + // make sure it's pinned if it can trap + x->pin(Instruction::PinExplicitNullCheck); + set_put(obj); + set_last_explicit_null_check(x); + if (PrintNullCheckElimination) { + tty->print_cr("NullCheck %d of value %d proves value to be non-null", x->id(), obj->id()); + } + } +} + + +void NullCheckEliminator::handle_Invoke(Invoke* x) { + if (!x->has_receiver()) { + // Be conservative + clear_last_explicit_null_check(); + return; + } + + Value recv = x->receiver(); + if (!set_contains(recv)) { + set_put(recv); + if (PrintNullCheckElimination) { + tty->print_cr("Invoke %d of value %d proves value to be non-null", x->id(), recv->id()); + } + } + clear_last_explicit_null_check(); +} + + +void NullCheckEliminator::handle_NewInstance(NewInstance* x) { + set_put(x); + if (PrintNullCheckElimination) { + tty->print_cr("NewInstance %d is non-null", x->id()); + } +} + + +void NullCheckEliminator::handle_NewArray(NewArray* x) { + set_put(x); + if (PrintNullCheckElimination) { + tty->print_cr("NewArray %d is non-null", x->id()); + } +} + + +void NullCheckEliminator::handle_ExceptionObject(ExceptionObject* x) { + set_put(x); + if (PrintNullCheckElimination) { + tty->print_cr("ExceptionObject %d is non-null", x->id()); + } +} + + +void NullCheckEliminator::handle_AccessMonitor(AccessMonitor* x) { + Value obj = x->obj(); + if (set_contains(obj)) { + // Value is non-null => update AccessMonitor + if (PrintNullCheckElimination) { + tty->print_cr("Eliminated AccessMonitor %d's null check for value %d", x->id(), obj->id()); + } + x->set_needs_null_check(false); + } else { + set_put(obj); + if (PrintNullCheckElimination) { + tty->print_cr("AccessMonitor %d of value %d proves value to be non-null", x->id(), obj->id()); + } + // Ensure previous passes do not cause wrong state + x->set_needs_null_check(true); + } + clear_last_explicit_null_check(); +} + + +void NullCheckEliminator::handle_Intrinsic(Intrinsic* x) { + if (!x->has_receiver()) { + // Be conservative + clear_last_explicit_null_check(); + return; + } + + Value recv = x->receiver(); + if (set_contains(recv)) { + // Value is non-null => update Intrinsic + if (PrintNullCheckElimination) { + tty->print_cr("Eliminated Intrinsic %d's null check for value %d", x->id(), recv->id()); + } + x->set_needs_null_check(false); + } else { + set_put(recv); + if (PrintNullCheckElimination) { + tty->print_cr("Intrinsic %d of value %d proves value to be non-null", x->id(), recv->id()); + } + // Ensure previous passes do not cause wrong state + x->set_needs_null_check(true); + } + clear_last_explicit_null_check(); +} + + +void NullCheckEliminator::handle_Phi(Phi* x) { + int i; + bool all_non_null = true; + if (x->is_illegal()) { + all_non_null = false; + } else { + for (i = 0; i < x->operand_count(); i++) { + Value input = x->operand_at(i); + if (!set_contains(input)) { + all_non_null = false; + } + } + } + + if (all_non_null) { + // Value is non-null => update Phi + if (PrintNullCheckElimination) { + tty->print_cr("Eliminated Phi %d's null check for phifun because all inputs are non-null", x->id()); + } + x->set_needs_null_check(false); + } else if (set_contains(x)) { + set_remove(x); + } +} + + +void Optimizer::eliminate_null_checks() { + ResourceMark rm; + + NullCheckEliminator nce(this); + + if (PrintNullCheckElimination) { + tty->print_cr("Starting null check elimination for method %s::%s%s", + ir()->method()->holder()->name()->as_utf8(), + ir()->method()->name()->as_utf8(), + ir()->method()->signature()->as_symbol()->as_utf8()); + } + + // Apply to graph + nce.iterate(ir()->start()); + + // walk over the graph looking for exception + // handlers and iterate over them as well + int nblocks = BlockBegin::number_of_blocks(); + BlockList blocks(nblocks); + boolArray visited_block(nblocks, false); + + blocks.push(ir()->start()); + visited_block[ir()->start()->block_id()] = true; + for (int i = 0; i < blocks.length(); i++) { + BlockBegin* b = blocks[i]; + // exception handlers need to be treated as additional roots + for (int e = b->number_of_exception_handlers(); e-- > 0; ) { + BlockBegin* excp = b->exception_handler_at(e); + int id = excp->block_id(); + if (!visited_block[id]) { + blocks.push(excp); + visited_block[id] = true; + nce.iterate(excp); + } + } + // traverse successors + BlockEnd *end = b->end(); + for (int s = end->number_of_sux(); s-- > 0; ) { + BlockBegin* next = end->sux_at(s); + int id = next->block_id(); + if (!visited_block[id]) { + blocks.push(next); + visited_block[id] = true; + } + } + } + + + if (PrintNullCheckElimination) { + tty->print_cr("Done with null check elimination for method %s::%s%s", + ir()->method()->holder()->name()->as_utf8(), + ir()->method()->name()->as_utf8(), + ir()->method()->signature()->as_symbol()->as_utf8()); + } +} diff --git a/hotspot/src/share/vm/c1/c1_Optimizer.hpp b/hotspot/src/share/vm/c1/c1_Optimizer.hpp new file mode 100644 index 00000000000..ba0aeb9916c --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Optimizer.hpp @@ -0,0 +1,37 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Optimizer VALUE_OBJ_CLASS_SPEC { + private: + IR* _ir; + + public: + Optimizer(IR* ir); + IR* ir() const { return _ir; } + + // optimizations + void eliminate_conditional_expressions(); + void eliminate_blocks(); + void eliminate_null_checks(); +}; diff --git a/hotspot/src/share/vm/c1/c1_Runtime1.cpp b/hotspot/src/share/vm/c1/c1_Runtime1.cpp new file mode 100644 index 00000000000..8fe43955757 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Runtime1.cpp @@ -0,0 +1,1194 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_Runtime1.cpp.incl" + + +// Implementation of StubAssembler + +StubAssembler::StubAssembler(CodeBuffer* code, const char * name, int stub_id) : C1_MacroAssembler(code) { + _name = name; + _must_gc_arguments = false; + _frame_size = no_frame_size; + _num_rt_args = 0; + _stub_id = stub_id; +} + + +void StubAssembler::set_info(const char* name, bool must_gc_arguments) { + _name = name; + _must_gc_arguments = must_gc_arguments; +} + + +void StubAssembler::set_frame_size(int size) { + if (_frame_size == no_frame_size) { + _frame_size = size; + } + assert(_frame_size == size, "can't change the frame size"); +} + + +void StubAssembler::set_num_rt_args(int args) { + if (_num_rt_args == 0) { + _num_rt_args = args; + } + assert(_num_rt_args == args, "can't change the number of args"); +} + +// Implementation of Runtime1 + +bool Runtime1::_is_initialized = false; +CodeBlob* Runtime1::_blobs[Runtime1::number_of_ids]; +const char *Runtime1::_blob_names[] = { + RUNTIME1_STUBS(STUB_NAME, LAST_STUB_NAME) +}; + +#ifndef PRODUCT +// statistics +int Runtime1::_generic_arraycopy_cnt = 0; +int Runtime1::_primitive_arraycopy_cnt = 0; +int Runtime1::_oop_arraycopy_cnt = 0; +int Runtime1::_arraycopy_slowcase_cnt = 0; +int Runtime1::_new_type_array_slowcase_cnt = 0; +int Runtime1::_new_object_array_slowcase_cnt = 0; +int Runtime1::_new_instance_slowcase_cnt = 0; +int Runtime1::_new_multi_array_slowcase_cnt = 0; +int Runtime1::_monitorenter_slowcase_cnt = 0; +int Runtime1::_monitorexit_slowcase_cnt = 0; +int Runtime1::_patch_code_slowcase_cnt = 0; +int Runtime1::_throw_range_check_exception_count = 0; +int Runtime1::_throw_index_exception_count = 0; +int Runtime1::_throw_div0_exception_count = 0; +int Runtime1::_throw_null_pointer_exception_count = 0; +int Runtime1::_throw_class_cast_exception_count = 0; +int Runtime1::_throw_incompatible_class_change_error_count = 0; +int Runtime1::_throw_array_store_exception_count = 0; +int Runtime1::_throw_count = 0; +#endif + +BufferBlob* Runtime1::_buffer_blob = NULL; + +// Simple helper to see if the caller of a runtime stub which +// entered the VM has been deoptimized + +static bool caller_is_deopted() { + JavaThread* thread = JavaThread::current(); + RegisterMap reg_map(thread, false); + frame runtime_frame = thread->last_frame(); + frame caller_frame = runtime_frame.sender(®_map); + assert(caller_frame.is_compiled_frame(), "must be compiled"); + return caller_frame.is_deoptimized_frame(); +} + +// Stress deoptimization +static void deopt_caller() { + if ( !caller_is_deopted()) { + JavaThread* thread = JavaThread::current(); + RegisterMap reg_map(thread, false); + frame runtime_frame = thread->last_frame(); + frame caller_frame = runtime_frame.sender(®_map); + VM_DeoptimizeFrame deopt(thread, caller_frame.id()); + VMThread::execute(&deopt); + assert(caller_is_deopted(), "Must be deoptimized"); + } +} + + +BufferBlob* Runtime1::get_buffer_blob() { + // Allocate code buffer space only once + BufferBlob* blob = _buffer_blob; + if (blob == NULL) { + // setup CodeBuffer. Preallocate a BufferBlob of size + // NMethodSizeLimit plus some extra space for constants. + int code_buffer_size = desired_max_code_buffer_size() + desired_max_constant_size(); + blob = BufferBlob::create("Compiler1 temporary CodeBuffer", + code_buffer_size); + guarantee(blob != NULL, "must create initial code buffer"); + _buffer_blob = blob; + } + return _buffer_blob; +} + +void Runtime1::setup_code_buffer(CodeBuffer* code, int call_stub_estimate) { + // Preinitialize the consts section to some large size: + int locs_buffer_size = 20 * (relocInfo::length_limit + sizeof(relocInfo)); + char* locs_buffer = NEW_RESOURCE_ARRAY(char, locs_buffer_size); + code->insts()->initialize_shared_locs((relocInfo*)locs_buffer, + locs_buffer_size / sizeof(relocInfo)); + code->initialize_consts_size(desired_max_constant_size()); + // Call stubs + deopt/exception handler + code->initialize_stubs_size((call_stub_estimate * LIR_Assembler::call_stub_size) + + LIR_Assembler::exception_handler_size + + LIR_Assembler::deopt_handler_size); +} + + +void Runtime1::generate_blob_for(StubID id) { + assert(0 <= id && id < number_of_ids, "illegal stub id"); + ResourceMark rm; + // create code buffer for code storage + CodeBuffer code(get_buffer_blob()->instructions_begin(), + get_buffer_blob()->instructions_size()); + + setup_code_buffer(&code, 0); + + // create assembler for code generation + StubAssembler* sasm = new StubAssembler(&code, name_for(id), id); + // generate code for runtime stub + OopMapSet* oop_maps; + oop_maps = generate_code_for(id, sasm); + assert(oop_maps == NULL || sasm->frame_size() != no_frame_size, + "if stub has an oop map it must have a valid frame size"); + +#ifdef ASSERT + // Make sure that stubs that need oopmaps have them + switch (id) { + // These stubs don't need to have an oopmap + case dtrace_object_alloc_id: + case slow_subtype_check_id: + case fpu2long_stub_id: + case unwind_exception_id: +#ifndef TIERED + case counter_overflow_id: // Not generated outside the tiered world +#endif +#ifdef SPARC + case handle_exception_nofpu_id: // Unused on sparc +#endif + break; + + // All other stubs should have oopmaps + default: + assert(oop_maps != NULL, "must have an oopmap"); + } +#endif + + // align so printing shows nop's instead of random code at the end (SimpleStubs are aligned) + sasm->align(BytesPerWord); + // make sure all code is in code buffer + sasm->flush(); + // create blob - distinguish a few special cases + CodeBlob* blob = RuntimeStub::new_runtime_stub(name_for(id), + &code, + CodeOffsets::frame_never_safe, + sasm->frame_size(), + oop_maps, + sasm->must_gc_arguments()); + // install blob + assert(blob != NULL, "blob must exist"); + _blobs[id] = blob; +} + + +void Runtime1::initialize() { + // Warning: If we have more than one compilation running in parallel, we + // need a lock here with the current setup (lazy initialization). + if (!is_initialized()) { + _is_initialized = true; + + // platform-dependent initialization + initialize_pd(); + // generate stubs + for (int id = 0; id < number_of_ids; id++) generate_blob_for((StubID)id); + // printing +#ifndef PRODUCT + if (PrintSimpleStubs) { + ResourceMark rm; + for (int id = 0; id < number_of_ids; id++) { + _blobs[id]->print(); + if (_blobs[id]->oop_maps() != NULL) { + _blobs[id]->oop_maps()->print(); + } + } + } +#endif + } +} + + +CodeBlob* Runtime1::blob_for(StubID id) { + assert(0 <= id && id < number_of_ids, "illegal stub id"); + if (!is_initialized()) initialize(); + return _blobs[id]; +} + + +const char* Runtime1::name_for(StubID id) { + assert(0 <= id && id < number_of_ids, "illegal stub id"); + return _blob_names[id]; +} + +const char* Runtime1::name_for_address(address entry) { + for (int id = 0; id < number_of_ids; id++) { + if (entry == entry_for((StubID)id)) return name_for((StubID)id); + } + +#define FUNCTION_CASE(a, f) \ + if ((intptr_t)a == CAST_FROM_FN_PTR(intptr_t, f)) return #f + + FUNCTION_CASE(entry, os::javaTimeMillis); + FUNCTION_CASE(entry, os::javaTimeNanos); + FUNCTION_CASE(entry, SharedRuntime::OSR_migration_end); + FUNCTION_CASE(entry, SharedRuntime::d2f); + FUNCTION_CASE(entry, SharedRuntime::d2i); + FUNCTION_CASE(entry, SharedRuntime::d2l); + FUNCTION_CASE(entry, SharedRuntime::dcos); + FUNCTION_CASE(entry, SharedRuntime::dexp); + FUNCTION_CASE(entry, SharedRuntime::dlog); + FUNCTION_CASE(entry, SharedRuntime::dlog10); + FUNCTION_CASE(entry, SharedRuntime::dpow); + FUNCTION_CASE(entry, SharedRuntime::drem); + FUNCTION_CASE(entry, SharedRuntime::dsin); + FUNCTION_CASE(entry, SharedRuntime::dtan); + FUNCTION_CASE(entry, SharedRuntime::f2i); + FUNCTION_CASE(entry, SharedRuntime::f2l); + FUNCTION_CASE(entry, SharedRuntime::frem); + FUNCTION_CASE(entry, SharedRuntime::l2d); + FUNCTION_CASE(entry, SharedRuntime::l2f); + FUNCTION_CASE(entry, SharedRuntime::ldiv); + FUNCTION_CASE(entry, SharedRuntime::lmul); + FUNCTION_CASE(entry, SharedRuntime::lrem); + FUNCTION_CASE(entry, SharedRuntime::lrem); + FUNCTION_CASE(entry, SharedRuntime::dtrace_method_entry); + FUNCTION_CASE(entry, SharedRuntime::dtrace_method_exit); + FUNCTION_CASE(entry, trace_block_entry); + +#undef FUNCTION_CASE + + return ""; +} + + +JRT_ENTRY(void, Runtime1::new_instance(JavaThread* thread, klassOopDesc* klass)) + NOT_PRODUCT(_new_instance_slowcase_cnt++;) + + assert(oop(klass)->is_klass(), "not a class"); + instanceKlassHandle h(thread, klass); + h->check_valid_for_instantiation(true, CHECK); + // make sure klass is initialized + h->initialize(CHECK); + // allocate instance and return via TLS + oop obj = h->allocate_instance(CHECK); + thread->set_vm_result(obj); +JRT_END + + +JRT_ENTRY(void, Runtime1::new_type_array(JavaThread* thread, klassOopDesc* klass, jint length)) + NOT_PRODUCT(_new_type_array_slowcase_cnt++;) + // Note: no handle for klass needed since they are not used + // anymore after new_typeArray() and no GC can happen before. + // (This may have to change if this code changes!) + assert(oop(klass)->is_klass(), "not a class"); + BasicType elt_type = typeArrayKlass::cast(klass)->element_type(); + oop obj = oopFactory::new_typeArray(elt_type, length, CHECK); + thread->set_vm_result(obj); + // This is pretty rare but this runtime patch is stressful to deoptimization + // if we deoptimize here so force a deopt to stress the path. + if (DeoptimizeALot) { + deopt_caller(); + } + +JRT_END + + +JRT_ENTRY(void, Runtime1::new_object_array(JavaThread* thread, klassOopDesc* array_klass, jint length)) + NOT_PRODUCT(_new_object_array_slowcase_cnt++;) + + // Note: no handle for klass needed since they are not used + // anymore after new_objArray() and no GC can happen before. + // (This may have to change if this code changes!) + assert(oop(array_klass)->is_klass(), "not a class"); + klassOop elem_klass = objArrayKlass::cast(array_klass)->element_klass(); + objArrayOop obj = oopFactory::new_objArray(elem_klass, length, CHECK); + thread->set_vm_result(obj); + // This is pretty rare but this runtime patch is stressful to deoptimization + // if we deoptimize here so force a deopt to stress the path. + if (DeoptimizeALot) { + deopt_caller(); + } +JRT_END + + +JRT_ENTRY(void, Runtime1::new_multi_array(JavaThread* thread, klassOopDesc* klass, int rank, jint* dims)) + NOT_PRODUCT(_new_multi_array_slowcase_cnt++;) + + assert(oop(klass)->is_klass(), "not a class"); + assert(rank >= 1, "rank must be nonzero"); +#ifdef _LP64 +// In 64 bit mode, the sizes are stored in the top 32 bits +// of each 64 bit stack entry. +// dims is actually an intptr_t * because the arguments +// are pushed onto a 64 bit stack. +// We must create an array of jints to pass to multi_allocate. +// We reuse the current stack because it will be popped +// after this bytecode is completed. + if ( rank > 1 ) { + int index; + for ( index = 1; index < rank; index++ ) { // First size is ok + dims[index] = dims[index*2]; + } + } +#endif + oop obj = arrayKlass::cast(klass)->multi_allocate(rank, dims, CHECK); + thread->set_vm_result(obj); +JRT_END + + +JRT_ENTRY(void, Runtime1::unimplemented_entry(JavaThread* thread, StubID id)) + tty->print_cr("Runtime1::entry_for(%d) returned unimplemented entry point", id); +JRT_END + + +JRT_ENTRY(void, Runtime1::throw_array_store_exception(JavaThread* thread)) + THROW(vmSymbolHandles::java_lang_ArrayStoreException()); +JRT_END + + +JRT_ENTRY(void, Runtime1::post_jvmti_exception_throw(JavaThread* thread)) + if (JvmtiExport::can_post_exceptions()) { + vframeStream vfst(thread, true); + address bcp = vfst.method()->bcp_from(vfst.bci()); + JvmtiExport::post_exception_throw(thread, vfst.method(), bcp, thread->exception_oop()); + } +JRT_END + +#ifdef TIERED +JRT_ENTRY(void, Runtime1::counter_overflow(JavaThread* thread, int bci)) + RegisterMap map(thread, false); + frame fr = thread->last_frame().sender(&map); + nmethod* nm = (nmethod*) fr.cb(); + assert(nm!= NULL && nm->is_nmethod(), "what?"); + methodHandle method(thread, nm->method()); + if (bci == 0) { + // invocation counter overflow + if (!Tier1CountOnly) { + CompilationPolicy::policy()->method_invocation_event(method, CHECK); + } else { + method()->invocation_counter()->reset(); + } + } else { + if (!Tier1CountOnly) { + // Twe have a bci but not the destination bci and besides a backedge + // event is more for OSR which we don't want here. + CompilationPolicy::policy()->method_invocation_event(method, CHECK); + } else { + method()->backedge_counter()->reset(); + } + } +JRT_END +#endif // TIERED + +extern void vm_exit(int code); + +// Enter this method from compiled code handler below. This is where we transition +// to VM mode. This is done as a helper routine so that the method called directly +// from compiled code does not have to transition to VM. This allows the entry +// method to see if the nmethod that we have just looked up a handler for has +// been deoptimized while we were in the vm. This simplifies the assembly code +// cpu directories. +// +// We are entering here from exception stub (via the entry method below) +// If there is a compiled exception handler in this method, we will continue there; +// otherwise we will unwind the stack and continue at the caller of top frame method +// Note: we enter in Java using a special JRT wrapper. This wrapper allows us to +// control the area where we can allow a safepoint. After we exit the safepoint area we can +// check to see if the handler we are going to return is now in a nmethod that has +// been deoptimized. If that is the case we return the deopt blob +// unpack_with_exception entry instead. This makes life for the exception blob easier +// because making that same check and diverting is painful from assembly language. +// + + +JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* thread, oopDesc* ex, address pc, nmethod*& nm)) + + Handle exception(thread, ex); + nm = CodeCache::find_nmethod(pc); + assert(nm != NULL, "this is not an nmethod"); + // Adjust the pc as needed/ + if (nm->is_deopt_pc(pc)) { + RegisterMap map(thread, false); + frame exception_frame = thread->last_frame().sender(&map); + // if the frame isn't deopted then pc must not correspond to the caller of last_frame + assert(exception_frame.is_deoptimized_frame(), "must be deopted"); + pc = exception_frame.pc(); + } +#ifdef ASSERT + assert(exception.not_null(), "NULL exceptions should be handled by throw_exception"); + assert(exception->is_oop(), "just checking"); + // Check that exception is a subclass of Throwable, otherwise we have a VerifyError + if (!(exception->is_a(SystemDictionary::throwable_klass()))) { + if (ExitVMOnVerifyError) vm_exit(-1); + ShouldNotReachHere(); + } +#endif + + // Check the stack guard pages and reenable them if necessary and there is + // enough space on the stack to do so. Use fast exceptions only if the guard + // pages are enabled. + bool guard_pages_enabled = thread->stack_yellow_zone_enabled(); + if (!guard_pages_enabled) guard_pages_enabled = thread->reguard_stack(); + + if (JvmtiExport::can_post_exceptions()) { + // To ensure correct notification of exception catches and throws + // we have to deoptimize here. If we attempted to notify the + // catches and throws during this exception lookup it's possible + // we could deoptimize on the way out of the VM and end back in + // the interpreter at the throw site. This would result in double + // notifications since the interpreter would also notify about + // these same catches and throws as it unwound the frame. + + RegisterMap reg_map(thread); + frame stub_frame = thread->last_frame(); + frame caller_frame = stub_frame.sender(®_map); + + // We don't really want to deoptimize the nmethod itself since we + // can actually continue in the exception handler ourselves but I + // don't see an easy way to have the desired effect. + VM_DeoptimizeFrame deopt(thread, caller_frame.id()); + VMThread::execute(&deopt); + + return SharedRuntime::deopt_blob()->unpack_with_exception_in_tls(); + } + + // ExceptionCache is used only for exceptions at call and not for implicit exceptions + if (guard_pages_enabled) { + address fast_continuation = nm->handler_for_exception_and_pc(exception, pc); + if (fast_continuation != NULL) { + if (fast_continuation == ExceptionCache::unwind_handler()) fast_continuation = NULL; + return fast_continuation; + } + } + + // If the stack guard pages are enabled, check whether there is a handler in + // the current method. Otherwise (guard pages disabled), force an unwind and + // skip the exception cache update (i.e., just leave continuation==NULL). + address continuation = NULL; + if (guard_pages_enabled) { + + // New exception handling mechanism can support inlined methods + // with exception handlers since the mappings are from PC to PC + + // debugging support + // tracing + if (TraceExceptions) { + ttyLocker ttyl; + ResourceMark rm; + tty->print_cr("Exception <%s> (0x%x) thrown in compiled method <%s> at PC " PTR_FORMAT " for thread 0x%x", + exception->print_value_string(), (address)exception(), nm->method()->print_value_string(), pc, thread); + } + // for AbortVMOnException flag + NOT_PRODUCT(Exceptions::debug_check_abort(exception)); + + // Clear out the exception oop and pc since looking up an + // exception handler can cause class loading, which might throw an + // exception and those fields are expected to be clear during + // normal bytecode execution. + thread->set_exception_oop(NULL); + thread->set_exception_pc(NULL); + + continuation = SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, false, false); + // If an exception was thrown during exception dispatch, the exception oop may have changed + thread->set_exception_oop(exception()); + thread->set_exception_pc(pc); + + // the exception cache is used only by non-implicit exceptions + if (continuation == NULL) { + nm->add_handler_for_exception_and_pc(exception, pc, ExceptionCache::unwind_handler()); + } else { + nm->add_handler_for_exception_and_pc(exception, pc, continuation); + } + } + + thread->set_vm_result(exception()); + + if (TraceExceptions) { + ttyLocker ttyl; + ResourceMark rm; + tty->print_cr("Thread " PTR_FORMAT " continuing at PC " PTR_FORMAT " for exception thrown at PC " PTR_FORMAT, + thread, continuation, pc); + } + + return continuation; +JRT_END + +// Enter this method from compiled code only if there is a Java exception handler +// in the method handling the exception +// We are entering here from exception stub. We don't do a normal VM transition here. +// We do it in a helper. This is so we can check to see if the nmethod we have just +// searched for an exception handler has been deoptimized in the meantime. +address Runtime1::exception_handler_for_pc(JavaThread* thread) { + oop exception = thread->exception_oop(); + address pc = thread->exception_pc(); + // Still in Java mode + debug_only(ResetNoHandleMark rnhm); + nmethod* nm = NULL; + address continuation = NULL; + { + // Enter VM mode by calling the helper + + ResetNoHandleMark rnhm; + continuation = exception_handler_for_pc_helper(thread, exception, pc, nm); + } + // Back in JAVA, use no oops DON'T safepoint + + // Now check to see if the nmethod we were called from is now deoptimized. + // If so we must return to the deopt blob and deoptimize the nmethod + + if (nm != NULL && caller_is_deopted()) { + continuation = SharedRuntime::deopt_blob()->unpack_with_exception_in_tls(); + } + + return continuation; +} + + +JRT_ENTRY(void, Runtime1::throw_range_check_exception(JavaThread* thread, int index)) + NOT_PRODUCT(_throw_range_check_exception_count++;) + Events::log("throw_range_check"); + char message[jintAsStringSize]; + sprintf(message, "%d", index); + SharedRuntime::throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), message); +JRT_END + + +JRT_ENTRY(void, Runtime1::throw_index_exception(JavaThread* thread, int index)) + NOT_PRODUCT(_throw_index_exception_count++;) + Events::log("throw_index"); + char message[16]; + sprintf(message, "%d", index); + SharedRuntime::throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_IndexOutOfBoundsException(), message); +JRT_END + + +JRT_ENTRY(void, Runtime1::throw_div0_exception(JavaThread* thread)) + NOT_PRODUCT(_throw_div0_exception_count++;) + SharedRuntime::throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_ArithmeticException(), "/ by zero"); +JRT_END + + +JRT_ENTRY(void, Runtime1::throw_null_pointer_exception(JavaThread* thread)) + NOT_PRODUCT(_throw_null_pointer_exception_count++;) + SharedRuntime::throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_NullPointerException()); +JRT_END + + +JRT_ENTRY(void, Runtime1::throw_class_cast_exception(JavaThread* thread, oopDesc* object)) + NOT_PRODUCT(_throw_class_cast_exception_count++;) + ResourceMark rm(thread); + char* message = SharedRuntime::generate_class_cast_message( + thread, Klass::cast(object->klass())->external_name()); + SharedRuntime::throw_and_post_jvmti_exception( + thread, vmSymbols::java_lang_ClassCastException(), message); +JRT_END + + +JRT_ENTRY(void, Runtime1::throw_incompatible_class_change_error(JavaThread* thread)) + NOT_PRODUCT(_throw_incompatible_class_change_error_count++;) + ResourceMark rm(thread); + SharedRuntime::throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_IncompatibleClassChangeError()); +JRT_END + + +JRT_ENTRY_NO_ASYNC(void, Runtime1::monitorenter(JavaThread* thread, oopDesc* obj, BasicObjectLock* lock)) + NOT_PRODUCT(_monitorenter_slowcase_cnt++;) + if (PrintBiasedLockingStatistics) { + Atomic::inc(BiasedLocking::slow_path_entry_count_addr()); + } + Handle h_obj(thread, obj); + assert(h_obj()->is_oop(), "must be NULL or an object"); + if (UseBiasedLocking) { + // Retry fast entry if bias is revoked to avoid unnecessary inflation + ObjectSynchronizer::fast_enter(h_obj, lock->lock(), true, CHECK); + } else { + if (UseFastLocking) { + // When using fast locking, the compiled code has already tried the fast case + assert(obj == lock->obj(), "must match"); + ObjectSynchronizer::slow_enter(h_obj, lock->lock(), THREAD); + } else { + lock->set_obj(obj); + ObjectSynchronizer::fast_enter(h_obj, lock->lock(), false, THREAD); + } + } +JRT_END + + +JRT_LEAF(void, Runtime1::monitorexit(JavaThread* thread, BasicObjectLock* lock)) + NOT_PRODUCT(_monitorexit_slowcase_cnt++;) + assert(thread == JavaThread::current(), "threads must correspond"); + assert(thread->last_Java_sp(), "last_Java_sp must be set"); + // monitorexit is non-blocking (leaf routine) => no exceptions can be thrown + EXCEPTION_MARK; + + oop obj = lock->obj(); + assert(obj->is_oop(), "must be NULL or an object"); + if (UseFastLocking) { + // When using fast locking, the compiled code has already tried the fast case + ObjectSynchronizer::slow_exit(obj, lock->lock(), THREAD); + } else { + ObjectSynchronizer::fast_exit(obj, lock->lock(), THREAD); + } +JRT_END + + +static klassOop resolve_field_return_klass(methodHandle caller, int bci, TRAPS) { + Bytecode_field* field_access = Bytecode_field_at(caller(), caller->bcp_from(bci)); + // This can be static or non-static field access + Bytecodes::Code code = field_access->code(); + + // We must load class, initialize class and resolvethe field + FieldAccessInfo result; // initialize class if needed + constantPoolHandle constants(THREAD, caller->constants()); + LinkResolver::resolve_field(result, constants, field_access->index(), Bytecodes::java_code(code), false, CHECK_NULL); + return result.klass()(); +} + + +// +// This routine patches sites where a class wasn't loaded or +// initialized at the time the code was generated. It handles +// references to classes, fields and forcing of initialization. Most +// of the cases are straightforward and involving simply forcing +// resolution of a class, rewriting the instruction stream with the +// needed constant and replacing the call in this function with the +// patched code. The case for static field is more complicated since +// the thread which is in the process of initializing a class can +// access it's static fields but other threads can't so the code +// either has to deoptimize when this case is detected or execute a +// check that the current thread is the initializing thread. The +// current +// +// Patches basically look like this: +// +// +// patch_site: jmp patch stub ;; will be patched +// continue: ... +// ... +// ... +// ... +// +// They have a stub which looks like this: +// +// ;; patch body +// movl , reg (for class constants) +// movl [reg1 + ], reg (for field offsets) +// movl reg, [reg1 + ] (for field offsets) +// +// patch_stub: call Runtime1::patch_code (through a runtime stub) +// jmp patch_site +// +// +// A normal patch is done by rewriting the patch body, usually a move, +// and then copying it into place over top of the jmp instruction +// being careful to flush caches and doing it in an MP-safe way. The +// constants following the patch body are used to find various pieces +// of the patch relative to the call site for Runtime1::patch_code. +// The case for getstatic and putstatic is more complicated because +// getstatic and putstatic have special semantics when executing while +// the class is being initialized. getstatic/putstatic on a class +// which is being_initialized may be executed by the initializing +// thread but other threads have to block when they execute it. This +// is accomplished in compiled code by executing a test of the current +// thread against the initializing thread of the class. It's emitted +// as boilerplate in their stub which allows the patched code to be +// executed before it's copied back into the main body of the nmethod. +// +// being_init: get_thread( +// cmpl [reg1 + ], +// jne patch_stub +// movl [reg1 + ], reg (for field offsets) +// movl reg, [reg1 + ] (for field offsets) +// jmp continue +// +// patch_stub: jmp Runtim1::patch_code (through a runtime stub) +// jmp patch_site +// +// If the class is being initialized the patch body is rewritten and +// the patch site is rewritten to jump to being_init, instead of +// patch_stub. Whenever this code is executed it checks the current +// thread against the intializing thread so other threads will enter +// the runtime and end up blocked waiting the class to finish +// initializing inside the calls to resolve_field below. The +// initializing class will continue on it's way. Once the class is +// fully_initialized, the intializing_thread of the class becomes +// NULL, so the next thread to execute this code will fail the test, +// call into patch_code and complete the patching process by copying +// the patch body back into the main part of the nmethod and resume +// executing. +// +// + +JRT_ENTRY(void, Runtime1::patch_code(JavaThread* thread, Runtime1::StubID stub_id )) + NOT_PRODUCT(_patch_code_slowcase_cnt++;) + + ResourceMark rm(thread); + RegisterMap reg_map(thread, false); + frame runtime_frame = thread->last_frame(); + frame caller_frame = runtime_frame.sender(®_map); + + // last java frame on stack + vframeStream vfst(thread, true); + assert(!vfst.at_end(), "Java frame must exist"); + + methodHandle caller_method(THREAD, vfst.method()); + // Note that caller_method->code() may not be same as caller_code because of OSR's + // Note also that in the presence of inlining it is not guaranteed + // that caller_method() == caller_code->method() + + + int bci = vfst.bci(); + + Events::log("patch_code @ " INTPTR_FORMAT , caller_frame.pc()); + + Bytecodes::Code code = Bytecode_at(caller_method->bcp_from(bci))->java_code(); + +#ifndef PRODUCT + // this is used by assertions in the access_field_patching_id + BasicType patch_field_type = T_ILLEGAL; +#endif // PRODUCT + bool deoptimize_for_volatile = false; + int patch_field_offset = -1; + KlassHandle init_klass(THREAD, klassOop(NULL)); // klass needed by access_field_patching code + Handle load_klass(THREAD, NULL); // oop needed by load_klass_patching code + if (stub_id == Runtime1::access_field_patching_id) { + + Bytecode_field* field_access = Bytecode_field_at(caller_method(), caller_method->bcp_from(bci)); + FieldAccessInfo result; // initialize class if needed + Bytecodes::Code code = field_access->code(); + constantPoolHandle constants(THREAD, caller_method->constants()); + LinkResolver::resolve_field(result, constants, field_access->index(), Bytecodes::java_code(code), false, CHECK); + patch_field_offset = result.field_offset(); + + // If we're patching a field which is volatile then at compile it + // must not have been know to be volatile, so the generated code + // isn't correct for a volatile reference. The nmethod has to be + // deoptimized so that the code can be regenerated correctly. + // This check is only needed for access_field_patching since this + // is the path for patching field offsets. load_klass is only + // used for patching references to oops which don't need special + // handling in the volatile case. + deoptimize_for_volatile = result.access_flags().is_volatile(); + +#ifndef PRODUCT + patch_field_type = result.field_type(); +#endif + } else if (stub_id == Runtime1::load_klass_patching_id) { + oop k; + switch (code) { + case Bytecodes::_putstatic: + case Bytecodes::_getstatic: + { klassOop klass = resolve_field_return_klass(caller_method, bci, CHECK); + // Save a reference to the class that has to be checked for initialization + init_klass = KlassHandle(THREAD, klass); + k = klass; + } + break; + case Bytecodes::_new: + { Bytecode_new* bnew = Bytecode_new_at(caller_method->bcp_from(bci)); + k = caller_method->constants()->klass_at(bnew->index(), CHECK); + } + break; + case Bytecodes::_multianewarray: + { Bytecode_multianewarray* mna = Bytecode_multianewarray_at(caller_method->bcp_from(bci)); + k = caller_method->constants()->klass_at(mna->index(), CHECK); + } + break; + case Bytecodes::_instanceof: + { Bytecode_instanceof* io = Bytecode_instanceof_at(caller_method->bcp_from(bci)); + k = caller_method->constants()->klass_at(io->index(), CHECK); + } + break; + case Bytecodes::_checkcast: + { Bytecode_checkcast* cc = Bytecode_checkcast_at(caller_method->bcp_from(bci)); + k = caller_method->constants()->klass_at(cc->index(), CHECK); + } + break; + case Bytecodes::_anewarray: + { Bytecode_anewarray* anew = Bytecode_anewarray_at(caller_method->bcp_from(bci)); + klassOop ek = caller_method->constants()->klass_at(anew->index(), CHECK); + k = Klass::cast(ek)->array_klass(CHECK); + } + break; + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + { + Bytecode_loadconstant* cc = Bytecode_loadconstant_at(caller_method(), + caller_method->bcp_from(bci)); + klassOop resolved = caller_method->constants()->klass_at(cc->index(), CHECK); + // ldc wants the java mirror. + k = resolved->klass_part()->java_mirror(); + } + break; + default: Unimplemented(); + } + // convert to handle + load_klass = Handle(THREAD, k); + } else { + ShouldNotReachHere(); + } + + if (deoptimize_for_volatile) { + // At compile time we assumed the field wasn't volatile but after + // loading it turns out it was volatile so we have to throw the + // compiled code out and let it be regenerated. + if (TracePatching) { + tty->print_cr("Deoptimizing for patching volatile field reference"); + } + VM_DeoptimizeFrame deopt(thread, caller_frame.id()); + VMThread::execute(&deopt); + + // Return to the now deoptimized frame. + } + + + // Now copy code back + + { + MutexLockerEx ml_patch (Patching_lock, Mutex::_no_safepoint_check_flag); + // + // Deoptimization may have happened while we waited for the lock. + // In that case we don't bother to do any patching we just return + // and let the deopt happen + if (!caller_is_deopted()) { + NativeGeneralJump* jump = nativeGeneralJump_at(caller_frame.pc()); + address instr_pc = jump->jump_destination(); + NativeInstruction* ni = nativeInstruction_at(instr_pc); + if (ni->is_jump() ) { + // the jump has not been patched yet + // The jump destination is slow case and therefore not part of the stubs + // (stubs are only for StaticCalls) + + // format of buffer + // .... + // instr byte 0 <-- copy_buff + // instr byte 1 + // .. + // instr byte n-1 + // n + // .... <-- call destination + + address stub_location = caller_frame.pc() + PatchingStub::patch_info_offset(); + unsigned char* byte_count = (unsigned char*) (stub_location - 1); + unsigned char* byte_skip = (unsigned char*) (stub_location - 2); + unsigned char* being_initialized_entry_offset = (unsigned char*) (stub_location - 3); + address copy_buff = stub_location - *byte_skip - *byte_count; + address being_initialized_entry = stub_location - *being_initialized_entry_offset; + if (TracePatching) { + tty->print_cr(" Patching %s at bci %d at address 0x%x (%s)", Bytecodes::name(code), bci, + instr_pc, (stub_id == Runtime1::access_field_patching_id) ? "field" : "klass"); + nmethod* caller_code = CodeCache::find_nmethod(caller_frame.pc()); + assert(caller_code != NULL, "nmethod not found"); + + // NOTE we use pc() not original_pc() because we already know they are + // identical otherwise we'd have never entered this block of code + + OopMap* map = caller_code->oop_map_for_return_address(caller_frame.pc()); + assert(map != NULL, "null check"); + map->print(); + tty->cr(); + + Disassembler::decode(copy_buff, copy_buff + *byte_count, tty); + } + // depending on the code below, do_patch says whether to copy the patch body back into the nmethod + bool do_patch = true; + if (stub_id == Runtime1::access_field_patching_id) { + // The offset may not be correct if the class was not loaded at code generation time. + // Set it now. + NativeMovRegMem* n_move = nativeMovRegMem_at(copy_buff); + assert(n_move->offset() == 0 || (n_move->offset() == 4 && (patch_field_type == T_DOUBLE || patch_field_type == T_LONG)), "illegal offset for type"); + assert(patch_field_offset >= 0, "illegal offset"); + n_move->add_offset_in_bytes(patch_field_offset); + } else if (stub_id == Runtime1::load_klass_patching_id) { + // If a getstatic or putstatic is referencing a klass which + // isn't fully initialized, the patch body isn't copied into + // place until initialization is complete. In this case the + // patch site is setup so that any threads besides the + // initializing thread are forced to come into the VM and + // block. + do_patch = (code != Bytecodes::_getstatic && code != Bytecodes::_putstatic) || + instanceKlass::cast(init_klass())->is_initialized(); + NativeGeneralJump* jump = nativeGeneralJump_at(instr_pc); + if (jump->jump_destination() == being_initialized_entry) { + assert(do_patch == true, "initialization must be complete at this point"); + } else { + // patch the instruction + NativeMovConstReg* n_copy = nativeMovConstReg_at(copy_buff); + assert(n_copy->data() == 0, "illegal init value"); + assert(load_klass() != NULL, "klass not set"); + n_copy->set_data((intx) (load_klass())); + + if (TracePatching) { + Disassembler::decode(copy_buff, copy_buff + *byte_count, tty); + } + +#ifdef SPARC + // Update the oop location in the nmethod with the proper + // oop. When the code was generated, a NULL was stuffed + // in the oop table and that table needs to be update to + // have the right value. On intel the value is kept + // directly in the instruction instead of in the oop + // table, so set_data above effectively updated the value. + nmethod* nm = CodeCache::find_nmethod(instr_pc); + assert(nm != NULL, "invalid nmethod_pc"); + RelocIterator oops(nm, copy_buff, copy_buff + 1); + bool found = false; + while (oops.next() && !found) { + if (oops.type() == relocInfo::oop_type) { + oop_Relocation* r = oops.oop_reloc(); + oop* oop_adr = r->oop_addr(); + *oop_adr = load_klass(); + r->fix_oop_relocation(); + found = true; + } + } + assert(found, "the oop must exist!"); +#endif + + } + } else { + ShouldNotReachHere(); + } + if (do_patch) { + // replace instructions + // first replace the tail, then the call + for (int i = NativeCall::instruction_size; i < *byte_count; i++) { + address ptr = copy_buff + i; + int a_byte = (*ptr) & 0xFF; + address dst = instr_pc + i; + *(unsigned char*)dst = (unsigned char) a_byte; + } + ICache::invalidate_range(instr_pc, *byte_count); + NativeGeneralJump::replace_mt_safe(instr_pc, copy_buff); + + if (stub_id == Runtime1::load_klass_patching_id) { + // update relocInfo to oop + nmethod* nm = CodeCache::find_nmethod(instr_pc); + assert(nm != NULL, "invalid nmethod_pc"); + + // The old patch site is now a move instruction so update + // the reloc info so that it will get updated during + // future GCs. + RelocIterator iter(nm, (address)instr_pc, (address)(instr_pc + 1)); + relocInfo::change_reloc_info_for_address(&iter, (address) instr_pc, + relocInfo::none, relocInfo::oop_type); +#ifdef SPARC + // Sparc takes two relocations for an oop so update the second one. + address instr_pc2 = instr_pc + NativeMovConstReg::add_offset; + RelocIterator iter2(nm, instr_pc2, instr_pc2 + 1); + relocInfo::change_reloc_info_for_address(&iter2, (address) instr_pc2, + relocInfo::none, relocInfo::oop_type); +#endif + } + + } else { + ICache::invalidate_range(copy_buff, *byte_count); + NativeGeneralJump::insert_unconditional(instr_pc, being_initialized_entry); + } + } + } + } +JRT_END + +// +// Entry point for compiled code. We want to patch a nmethod. +// We don't do a normal VM transition here because we want to +// know after the patching is complete and any safepoint(s) are taken +// if the calling nmethod was deoptimized. We do this by calling a +// helper method which does the normal VM transition and when it +// completes we can check for deoptimization. This simplifies the +// assembly code in the cpu directories. +// +int Runtime1::move_klass_patching(JavaThread* thread) { +// +// NOTE: we are still in Java +// + Thread* THREAD = thread; + debug_only(NoHandleMark nhm;) + { + // Enter VM mode + + ResetNoHandleMark rnhm; + patch_code(thread, load_klass_patching_id); + } + // Back in JAVA, use no oops DON'T safepoint + + // Return true if calling code is deoptimized + + return caller_is_deopted(); +} + +// +// Entry point for compiled code. We want to patch a nmethod. +// We don't do a normal VM transition here because we want to +// know after the patching is complete and any safepoint(s) are taken +// if the calling nmethod was deoptimized. We do this by calling a +// helper method which does the normal VM transition and when it +// completes we can check for deoptimization. This simplifies the +// assembly code in the cpu directories. +// + +int Runtime1::access_field_patching(JavaThread* thread) { +// +// NOTE: we are still in Java +// + Thread* THREAD = thread; + debug_only(NoHandleMark nhm;) + { + // Enter VM mode + + ResetNoHandleMark rnhm; + patch_code(thread, access_field_patching_id); + } + // Back in JAVA, use no oops DON'T safepoint + + // Return true if calling code is deoptimized + + return caller_is_deopted(); +JRT_END + + +JRT_LEAF(void, Runtime1::trace_block_entry(jint block_id)) + // for now we just print out the block id + tty->print("%d ", block_id); +JRT_END + + +// fast and direct copy of arrays; returning -1, means that an exception may be thrown +// and we did not copy anything +JRT_LEAF(int, Runtime1::arraycopy(oopDesc* src, int src_pos, oopDesc* dst, int dst_pos, int length)) +#ifndef PRODUCT + _generic_arraycopy_cnt++; // Slow-path oop array copy +#endif + + enum { + ac_failed = -1, // arraycopy failed + ac_ok = 0 // arraycopy succeeded + }; + + if (src == NULL || dst == NULL || src_pos < 0 || dst_pos < 0 || length < 0) return ac_failed; + if (!dst->is_array() || !src->is_array()) return ac_failed; + if ((unsigned int) arrayOop(src)->length() < (unsigned int)src_pos + (unsigned int)length) return ac_failed; + if ((unsigned int) arrayOop(dst)->length() < (unsigned int)dst_pos + (unsigned int)length) return ac_failed; + + if (length == 0) return ac_ok; + if (src->is_typeArray()) { + const klassOop klass_oop = src->klass(); + if (klass_oop != dst->klass()) return ac_failed; + typeArrayKlass* klass = typeArrayKlass::cast(klass_oop); + const int l2es = klass->log2_element_size(); + const int ihs = klass->array_header_in_bytes() / wordSize; + char* src_addr = (char*) ((oopDesc**)src + ihs) + (src_pos << l2es); + char* dst_addr = (char*) ((oopDesc**)dst + ihs) + (dst_pos << l2es); + // Potential problem: memmove is not guaranteed to be word atomic + // Revisit in Merlin + memmove(dst_addr, src_addr, length << l2es); + return ac_ok; + } else if (src->is_objArray() && dst->is_objArray()) { + oop* src_addr = objArrayOop(src)->obj_at_addr(src_pos); + oop* dst_addr = objArrayOop(dst)->obj_at_addr(dst_pos); + // For performance reasons, we assume we are using a card marking write + // barrier. The assert will fail if this is not the case. + // Note that we use the non-virtual inlineable variant of write_ref_array. + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->has_write_ref_array_opt(), + "Barrier set must have ref array opt"); + if (src == dst) { + // same object, no check + Copy::conjoint_oops_atomic(src_addr, dst_addr, length); + bs->write_ref_array(MemRegion((HeapWord*)dst_addr, + (HeapWord*)(dst_addr + length))); + return ac_ok; + } else { + klassOop bound = objArrayKlass::cast(dst->klass())->element_klass(); + klassOop stype = objArrayKlass::cast(src->klass())->element_klass(); + if (stype == bound || Klass::cast(stype)->is_subtype_of(bound)) { + // Elements are guaranteed to be subtypes, so no check necessary + Copy::conjoint_oops_atomic(src_addr, dst_addr, length); + bs->write_ref_array(MemRegion((HeapWord*)dst_addr, + (HeapWord*)(dst_addr + length))); + return ac_ok; + } + } + } + return ac_failed; +JRT_END + + +JRT_LEAF(void, Runtime1::primitive_arraycopy(HeapWord* src, HeapWord* dst, int length)) +#ifndef PRODUCT + _primitive_arraycopy_cnt++; +#endif + + if (length == 0) return; + // Not guaranteed to be word atomic, but that doesn't matter + // for anything but an oop array, which is covered by oop_arraycopy. + Copy::conjoint_bytes(src, dst, length); +JRT_END + +JRT_LEAF(void, Runtime1::oop_arraycopy(HeapWord* src, HeapWord* dst, int num)) +#ifndef PRODUCT + _oop_arraycopy_cnt++; +#endif + + if (num == 0) return; + Copy::conjoint_oops_atomic((oop*) src, (oop*) dst, num); + BarrierSet* bs = Universe::heap()->barrier_set(); + bs->write_ref_array(MemRegion(dst, dst + num)); +JRT_END + + +#ifndef PRODUCT +void Runtime1::print_statistics() { + tty->print_cr("C1 Runtime statistics:"); + tty->print_cr(" _resolve_invoke_virtual_cnt: %d", SharedRuntime::_resolve_virtual_ctr); + tty->print_cr(" _resolve_invoke_opt_virtual_cnt: %d", SharedRuntime::_resolve_opt_virtual_ctr); + tty->print_cr(" _resolve_invoke_static_cnt: %d", SharedRuntime::_resolve_static_ctr); + tty->print_cr(" _handle_wrong_method_cnt: %d", SharedRuntime::_wrong_method_ctr); + tty->print_cr(" _ic_miss_cnt: %d", SharedRuntime::_ic_miss_ctr); + tty->print_cr(" _generic_arraycopy_cnt: %d", _generic_arraycopy_cnt); + tty->print_cr(" _primitive_arraycopy_cnt: %d", _primitive_arraycopy_cnt); + tty->print_cr(" _oop_arraycopy_cnt: %d", _oop_arraycopy_cnt); + tty->print_cr(" _arraycopy_slowcase_cnt: %d", _arraycopy_slowcase_cnt); + + tty->print_cr(" _new_type_array_slowcase_cnt: %d", _new_type_array_slowcase_cnt); + tty->print_cr(" _new_object_array_slowcase_cnt: %d", _new_object_array_slowcase_cnt); + tty->print_cr(" _new_instance_slowcase_cnt: %d", _new_instance_slowcase_cnt); + tty->print_cr(" _new_multi_array_slowcase_cnt: %d", _new_multi_array_slowcase_cnt); + tty->print_cr(" _monitorenter_slowcase_cnt: %d", _monitorenter_slowcase_cnt); + tty->print_cr(" _monitorexit_slowcase_cnt: %d", _monitorexit_slowcase_cnt); + tty->print_cr(" _patch_code_slowcase_cnt: %d", _patch_code_slowcase_cnt); + + tty->print_cr(" _throw_range_check_exception_count: %d:", _throw_range_check_exception_count); + tty->print_cr(" _throw_index_exception_count: %d:", _throw_index_exception_count); + tty->print_cr(" _throw_div0_exception_count: %d:", _throw_div0_exception_count); + tty->print_cr(" _throw_null_pointer_exception_count: %d:", _throw_null_pointer_exception_count); + tty->print_cr(" _throw_class_cast_exception_count: %d:", _throw_class_cast_exception_count); + tty->print_cr(" _throw_incompatible_class_change_error_count: %d:", _throw_incompatible_class_change_error_count); + tty->print_cr(" _throw_array_store_exception_count: %d:", _throw_array_store_exception_count); + tty->print_cr(" _throw_count: %d:", _throw_count); + + SharedRuntime::print_ic_miss_histogram(); + tty->cr(); +} +#endif // PRODUCT diff --git a/hotspot/src/share/vm/c1/c1_Runtime1.hpp b/hotspot/src/share/vm/c1/c1_Runtime1.hpp new file mode 100644 index 00000000000..df6c03883f3 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_Runtime1.hpp @@ -0,0 +1,190 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class StubAssembler; + +// The Runtime1 holds all assembly stubs and VM +// runtime routines needed by code code generated +// by the Compiler1. + +#define RUNTIME1_STUBS(stub, last_entry) \ + stub(dtrace_object_alloc) \ + stub(unwind_exception) \ + stub(forward_exception) \ + stub(throw_range_check_failed) /* throws ArrayIndexOutOfBoundsException */ \ + stub(throw_index_exception) /* throws IndexOutOfBoundsException */ \ + stub(throw_div0_exception) \ + stub(throw_null_pointer_exception) \ + stub(register_finalizer) \ + stub(new_instance) \ + stub(fast_new_instance) \ + stub(fast_new_instance_init_check) \ + stub(new_type_array) \ + stub(new_object_array) \ + stub(new_multi_array) \ + stub(handle_exception_nofpu) /* optimized version that does not preserve fpu registers */ \ + stub(handle_exception) \ + stub(throw_array_store_exception) \ + stub(throw_class_cast_exception) \ + stub(throw_incompatible_class_change_error) \ + stub(slow_subtype_check) \ + stub(monitorenter) \ + stub(monitorenter_nofpu) /* optimized version that does not preserve fpu registers */ \ + stub(monitorexit) \ + stub(monitorexit_nofpu) /* optimized version that does not preserve fpu registers */ \ + stub(access_field_patching) \ + stub(load_klass_patching) \ + stub(jvmti_exception_throw) \ + stub(fpu2long_stub) \ + stub(counter_overflow) \ + last_entry(number_of_ids) + +#define DECLARE_STUB_ID(x) x ## _id , +#define DECLARE_LAST_STUB_ID(x) x +#define STUB_NAME(x) #x " Runtime1 stub", +#define LAST_STUB_NAME(x) #x " Runtime1 stub" + +class Runtime1: public AllStatic { + friend class VMStructs; + friend class ArrayCopyStub; + private: + static int desired_max_code_buffer_size() { + return (int) NMethodSizeLimit; // default 256K or 512K + } + static int desired_max_constant_size() { + return (int) NMethodSizeLimit / 10; // about 25K + } + + // Note: This buffers is allocated once at startup since allocation + // for each compilation seems to be too expensive (at least on Intel + // win32). + static BufferBlob* _buffer_blob; + + public: + enum StubID { + RUNTIME1_STUBS(DECLARE_STUB_ID, DECLARE_LAST_STUB_ID) + }; + + // statistics +#ifndef PRODUCT + static int _resolve_invoke_cnt; + static int _handle_wrong_method_cnt; + static int _ic_miss_cnt; + static int _generic_arraycopy_cnt; + static int _primitive_arraycopy_cnt; + static int _oop_arraycopy_cnt; + static int _arraycopy_slowcase_cnt; + static int _new_type_array_slowcase_cnt; + static int _new_object_array_slowcase_cnt; + static int _new_instance_slowcase_cnt; + static int _new_multi_array_slowcase_cnt; + static int _monitorenter_slowcase_cnt; + static int _monitorexit_slowcase_cnt; + static int _patch_code_slowcase_cnt; + static int _throw_range_check_exception_count; + static int _throw_index_exception_count; + static int _throw_div0_exception_count; + static int _throw_null_pointer_exception_count; + static int _throw_class_cast_exception_count; + static int _throw_incompatible_class_change_error_count; + static int _throw_array_store_exception_count; + static int _throw_count; +#endif + + private: + static bool _is_initialized; + static CodeBlob* _blobs[number_of_ids]; + static const char* _blob_names[]; + + // stub generation + static void generate_blob_for(StubID id); + static OopMapSet* generate_code_for(StubID id, StubAssembler* masm); + static OopMapSet* generate_exception_throw(StubAssembler* sasm, address target, bool has_argument); + static void generate_handle_exception(StubAssembler *sasm, OopMapSet* oop_maps, OopMap* oop_map, bool ignore_fpu_registers = false); + static void generate_unwind_exception(StubAssembler *sasm); + static OopMapSet* generate_patching(StubAssembler* sasm, address target); + + static OopMapSet* generate_stub_call(StubAssembler* sasm, Register result, address entry, + Register arg1 = noreg, Register arg2 = noreg, Register arg3 = noreg); + + // runtime entry points + static void new_instance (JavaThread* thread, klassOopDesc* klass); + static void new_type_array (JavaThread* thread, klassOopDesc* klass, jint length); + static void new_object_array(JavaThread* thread, klassOopDesc* klass, jint length); + static void new_multi_array (JavaThread* thread, klassOopDesc* klass, int rank, jint* dims); + +#ifdef TIERED + static void counter_overflow(JavaThread* thread, int bci); +#endif // TIERED + + static void unimplemented_entry (JavaThread* thread, StubID id); + + static address exception_handler_for_pc(JavaThread* thread); + static void post_jvmti_exception_throw(JavaThread* thread); + + static void throw_range_check_exception(JavaThread* thread, int index); + static void throw_index_exception(JavaThread* thread, int index); + static void throw_div0_exception(JavaThread* thread); + static void throw_null_pointer_exception(JavaThread* thread); + static void throw_class_cast_exception(JavaThread* thread, oopDesc* obect); + static void throw_incompatible_class_change_error(JavaThread* thread); + static void throw_array_store_exception(JavaThread* thread); + + static void monitorenter(JavaThread* thread, oopDesc* obj, BasicObjectLock* lock); + static void monitorexit (JavaThread* thread, BasicObjectLock* lock); + + static int access_field_patching(JavaThread* thread); + static int move_klass_patching(JavaThread* thread); + + static void patch_code(JavaThread* thread, StubID stub_id); + + public: + static BufferBlob* get_buffer_blob(); + static void setup_code_buffer(CodeBuffer* cb, int call_stub_estimate); + + // initialization + static bool is_initialized() { return _is_initialized; } + static void initialize(); + static void initialize_pd(); + + // stubs + static CodeBlob* blob_for (StubID id); + static address entry_for(StubID id) { return blob_for(id)->instructions_begin(); } + static const char* name_for (StubID id); + static const char* name_for_address(address entry); + + // method tracing + static void trace_block_entry(jint block_id); + +#ifndef PRODUCT + static address throw_count_address() { return (address)&_throw_count; } +#endif + + // directly accessible leaf routine + static int arraycopy(oopDesc* src, int src_pos, oopDesc* dst, int dst_pos, int length); + static void primitive_arraycopy(HeapWord* src, HeapWord* dst, int length); + static void oop_arraycopy(HeapWord* src, HeapWord* dst, int length); + + static void print_statistics() PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/c1/c1_ValueMap.cpp b/hotspot/src/share/vm/c1/c1_ValueMap.cpp new file mode 100644 index 00000000000..392de32477c --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_ValueMap.cpp @@ -0,0 +1,434 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_ValueMap.cpp.incl" + + +#ifndef PRODUCT + + int ValueMap::_number_of_finds = 0; + int ValueMap::_number_of_hits = 0; + int ValueMap::_number_of_kills = 0; + + #define TRACE_VALUE_NUMBERING(code) if (PrintValueNumbering) { code; } + +#else + + #define TRACE_VALUE_NUMBERING(code) + +#endif + + +ValueMap::ValueMap() + : _nesting(0) + , _entries(ValueMapInitialSize, NULL) + , _killed_values() + , _entry_count(0) +{ + NOT_PRODUCT(reset_statistics()); +} + + +ValueMap::ValueMap(ValueMap* old) + : _nesting(old->_nesting + 1) + , _entries(old->_entries.length()) + , _killed_values() + , _entry_count(old->_entry_count) +{ + for (int i = size() - 1; i >= 0; i--) { + _entries.at_put(i, old->entry_at(i)); + } + _killed_values.set_from(&old->_killed_values); +} + + +void ValueMap::increase_table_size() { + int old_size = size(); + int new_size = old_size * 2 + 1; + + ValueMapEntryList worklist(8); + ValueMapEntryArray new_entries(new_size, NULL); + int new_entry_count = 0; + + TRACE_VALUE_NUMBERING(tty->print_cr("increasing table size from %d to %d", old_size, new_size)); + + for (int i = old_size - 1; i >= 0; i--) { + ValueMapEntry* entry; + for (entry = entry_at(i); entry != NULL; entry = entry->next()) { + if (!is_killed(entry->value())) { + worklist.push(entry); + } + } + + while (!worklist.is_empty()) { + entry = worklist.pop(); + int new_index = entry_index(entry->hash(), new_size); + + if (entry->nesting() != nesting() && new_entries.at(new_index) != entry->next()) { + // changing entries with a lower nesting than the current nesting of the table + // is not allowed because then the same entry is contained in multiple value maps. + // clone entry when next-pointer must be changed + entry = new ValueMapEntry(entry->hash(), entry->value(), entry->nesting(), NULL); + } + entry->set_next(new_entries.at(new_index)); + new_entries.at_put(new_index, entry); + new_entry_count++; + } + } + + _entries = new_entries; + _entry_count = new_entry_count; +} + + +Value ValueMap::find_insert(Value x) { + const intx hash = x->hash(); + if (hash != 0) { + // 0 hash means: exclude from value numbering + NOT_PRODUCT(_number_of_finds++); + + for (ValueMapEntry* entry = entry_at(entry_index(hash, size())); entry != NULL; entry = entry->next()) { + if (entry->hash() == hash) { + Value f = entry->value(); + + if (!is_killed(f) && f->is_equal(x)) { + NOT_PRODUCT(_number_of_hits++); + TRACE_VALUE_NUMBERING(tty->print_cr("Value Numbering: %s %c%d equal to %c%d (size %d, entries %d, nesting-diff %d)", x->name(), x->type()->tchar(), x->id(), f->type()->tchar(), f->id(), size(), entry_count(), nesting() - entry->nesting())); + + if (entry->nesting() != nesting() && f->as_Constant() == NULL) { + // non-constant values of of another block must be pinned, + // otherwise it is possible that they are not evaluated + f->pin(Instruction::PinGlobalValueNumbering); + } + + return f; + + } + } + } + + // x not found, so insert it + if (entry_count() >= size_threshold()) { + increase_table_size(); + } + int idx = entry_index(hash, size()); + _entries.at_put(idx, new ValueMapEntry(hash, x, nesting(), entry_at(idx))); + _entry_count++; + + TRACE_VALUE_NUMBERING(tty->print_cr("Value Numbering: insert %s %c%d (size %d, entries %d, nesting %d)", x->name(), x->type()->tchar(), x->id(), size(), entry_count(), nesting())); + } + + return x; +} + + +#define GENERIC_KILL_VALUE(must_kill_implementation) \ + NOT_PRODUCT(_number_of_kills++); \ + \ + for (int i = size() - 1; i >= 0; i--) { \ + ValueMapEntry* prev_entry = NULL; \ + for (ValueMapEntry* entry = entry_at(i); entry != NULL; entry = entry->next()) { \ + Value value = entry->value(); \ + \ + must_kill_implementation(must_kill, entry, value) \ + \ + if (must_kill) { \ + kill_value(value); \ + \ + if (prev_entry == NULL) { \ + _entries.at_put(i, entry->next()); \ + _entry_count--; \ + } else if (prev_entry->nesting() == nesting()) { \ + prev_entry->set_next(entry->next()); \ + _entry_count--; \ + } else { \ + prev_entry = entry; \ + } \ + \ + TRACE_VALUE_NUMBERING(tty->print_cr("Value Numbering: killed %s %c%d (size %d, entries %d, nesting-diff %d)", value->name(), value->type()->tchar(), value->id(), size(), entry_count(), nesting() - entry->nesting())); \ + } else { \ + prev_entry = entry; \ + } \ + } \ + } \ + +#define MUST_KILL_MEMORY(must_kill, entry, value) \ + bool must_kill = value->as_LoadField() != NULL || value->as_LoadIndexed() != NULL; + +#define MUST_KILL_ARRAY(must_kill, entry, value) \ + bool must_kill = value->as_LoadIndexed() != NULL \ + && value->type()->tag() == type->tag(); + +#define MUST_KILL_FIELD(must_kill, entry, value) \ + /* ciField's are not unique; must compare their contents */ \ + LoadField* lf = value->as_LoadField(); \ + bool must_kill = lf != NULL \ + && lf->field()->holder() == field->holder() \ + && lf->field()->offset() == field->offset(); + +#define MUST_KILL_EXCEPTION(must_kill, entry, value) \ + assert(entry->nesting() < nesting(), "must not find bigger nesting than current"); \ + bool must_kill = (entry->nesting() == nesting() - 1); + + +void ValueMap::kill_memory() { + GENERIC_KILL_VALUE(MUST_KILL_MEMORY); +} + +void ValueMap::kill_array(ValueType* type) { + GENERIC_KILL_VALUE(MUST_KILL_ARRAY); +} + +void ValueMap::kill_field(ciField* field) { + GENERIC_KILL_VALUE(MUST_KILL_FIELD); +} + +void ValueMap::kill_exception() { + GENERIC_KILL_VALUE(MUST_KILL_EXCEPTION); +} + + +void ValueMap::kill_map(ValueMap* map) { + assert(is_global_value_numbering(), "only for global value numbering"); + _killed_values.set_union(&map->_killed_values); +} + +void ValueMap::kill_all() { + assert(is_local_value_numbering(), "only for local value numbering"); + for (int i = size() - 1; i >= 0; i--) { + _entries.at_put(i, NULL); + } + _entry_count = 0; +} + + +#ifndef PRODUCT + +void ValueMap::print() { + tty->print_cr("(size %d, entries %d, nesting %d)", size(), entry_count(), nesting()); + + int entries = 0; + for (int i = 0; i < size(); i++) { + if (entry_at(i) != NULL) { + tty->print(" %2d: ", i); + for (ValueMapEntry* entry = entry_at(i); entry != NULL; entry = entry->next()) { + Value value = entry->value(); + tty->print("%s %c%d (%s%d) -> ", value->name(), value->type()->tchar(), value->id(), is_killed(value) ? "x" : "", entry->nesting()); + entries++; + } + tty->print_cr("NULL"); + } + } + + _killed_values.print(); + assert(entry_count() == entries, "entry_count incorrect"); +} + +void ValueMap::reset_statistics() { + _number_of_finds = 0; + _number_of_hits = 0; + _number_of_kills = 0; +} + +void ValueMap::print_statistics() { + float hit_rate = 0; + if (_number_of_finds != 0) { + hit_rate = (float)_number_of_hits / _number_of_finds; + } + + tty->print_cr("finds:%3d hits:%3d kills:%3d hit rate: %1.4f", _number_of_finds, _number_of_hits, _number_of_kills, hit_rate); +} + +#endif + + + +class ShortLoopOptimizer : public ValueNumberingVisitor { + private: + GlobalValueNumbering* _gvn; + BlockList _loop_blocks; + bool _too_complicated_loop; + + // simplified access to methods of GlobalValueNumbering + ValueMap* current_map() { return _gvn->current_map(); } + ValueMap* value_map_of(BlockBegin* block) { return _gvn->value_map_of(block); } + + // implementation for abstract methods of ValueNumberingVisitor + void kill_memory() { _too_complicated_loop = true; } + void kill_field(ciField* field) { current_map()->kill_field(field); }; + void kill_array(ValueType* type) { current_map()->kill_array(type); }; + + public: + ShortLoopOptimizer(GlobalValueNumbering* gvn) + : _gvn(gvn) + , _loop_blocks(ValueMapMaxLoopSize) + , _too_complicated_loop(false) + { + } + + bool process(BlockBegin* loop_header); +}; + + +bool ShortLoopOptimizer::process(BlockBegin* loop_header) { + TRACE_VALUE_NUMBERING(tty->print_cr("** loop header block")); + + _too_complicated_loop = false; + _loop_blocks.clear(); + _loop_blocks.append(loop_header); + + for (int i = 0; i < _loop_blocks.length(); i++) { + BlockBegin* block = _loop_blocks.at(i); + TRACE_VALUE_NUMBERING(tty->print_cr("processing loop block B%d", block->block_id())); + + if (block->is_set(BlockBegin::exception_entry_flag)) { + // this would be too complicated + return false; + } + + // add predecessors to worklist + for (int j = block->number_of_preds() - 1; j >= 0; j--) { + BlockBegin* pred = block->pred_at(j); + + ValueMap* pred_map = value_map_of(pred); + if (pred_map != NULL) { + current_map()->kill_map(pred_map); + } else if (!_loop_blocks.contains(pred)) { + if (_loop_blocks.length() >= ValueMapMaxLoopSize) { + return false; + } + _loop_blocks.append(pred); + } + } + + // use the instruction visitor for killing values + for (Value instr = block->next(); instr != NULL; instr = instr->next()) { + instr->visit(this); + if (_too_complicated_loop) { + return false; + } + } + } + + TRACE_VALUE_NUMBERING(tty->print_cr("** loop successfully optimized")); + return true; +} + + +GlobalValueNumbering::GlobalValueNumbering(IR* ir) + : _current_map(NULL) + , _value_maps(ir->linear_scan_order()->length(), NULL) +{ + TRACE_VALUE_NUMBERING(tty->print_cr("****** start of global value numbering")); + + ShortLoopOptimizer short_loop_optimizer(this); + int subst_count = 0; + + BlockList* blocks = ir->linear_scan_order(); + int num_blocks = blocks->length(); + + BlockBegin* start_block = blocks->at(0); + assert(start_block == ir->start() && start_block->number_of_preds() == 0 && start_block->dominator() == NULL, "must be start block"); + assert(start_block->next()->as_Base() != NULL && start_block->next()->next() == NULL, "start block must not have instructions"); + + // initial, empty value map with nesting 0 + set_value_map_of(start_block, new ValueMap()); + + for (int i = 1; i < num_blocks; i++) { + BlockBegin* block = blocks->at(i); + TRACE_VALUE_NUMBERING(tty->print_cr("**** processing block B%d", block->block_id())); + + int num_preds = block->number_of_preds(); + assert(num_preds > 0, "block must have predecessors"); + + BlockBegin* dominator = block->dominator(); + assert(dominator != NULL, "dominator must exist"); + assert(value_map_of(dominator) != NULL, "value map of dominator must exist"); + + // create new value map with increased nesting + _current_map = new ValueMap(value_map_of(dominator)); + + if (num_preds == 1) { + assert(dominator == block->pred_at(0), "dominator must be equal to predecessor"); + // nothing to do here + + } else if (block->is_set(BlockBegin::linear_scan_loop_header_flag)) { + // block has incoming backward branches -> try to optimize short loops + if (!short_loop_optimizer.process(block)) { + // loop is too complicated, so kill all memory loads because there might be + // stores to them in the loop + current_map()->kill_memory(); + } + + } else { + // only incoming forward branches that are already processed + for (int j = 0; j < num_preds; j++) { + BlockBegin* pred = block->pred_at(j); + ValueMap* pred_map = value_map_of(pred); + + if (pred_map != NULL) { + // propagate killed values of the predecessor to this block + current_map()->kill_map(value_map_of(pred)); + } else { + // kill all memory loads because predecessor not yet processed + // (this can happen with non-natural loops and OSR-compiles) + current_map()->kill_memory(); + } + } + } + + if (block->is_set(BlockBegin::exception_entry_flag)) { + current_map()->kill_exception(); + } + + TRACE_VALUE_NUMBERING(tty->print("value map before processing block: "); current_map()->print()); + + // visit all instructions of this block + for (Value instr = block->next(); instr != NULL; instr = instr->next()) { + assert(!instr->has_subst(), "substitution already set"); + + // check if instruction kills any values + instr->visit(this); + + if (instr->hash() != 0) { + Value f = current_map()->find_insert(instr); + if (f != instr) { + assert(!f->has_subst(), "can't have a substitution"); + instr->set_subst(f); + subst_count++; + } + } + } + + // remember value map for successors + set_value_map_of(block, current_map()); + } + + if (subst_count != 0) { + SubstitutionResolver resolver(ir); + } + + TRACE_VALUE_NUMBERING(tty->print("****** end of global value numbering. "); ValueMap::print_statistics()); +} diff --git a/hotspot/src/share/vm/c1/c1_ValueMap.hpp b/hotspot/src/share/vm/c1/c1_ValueMap.hpp new file mode 100644 index 00000000000..efc32200910 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_ValueMap.hpp @@ -0,0 +1,204 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ValueMapEntry: public CompilationResourceObj { + private: + intx _hash; + Value _value; + int _nesting; + ValueMapEntry* _next; + + public: + ValueMapEntry(intx hash, Value value, int nesting, ValueMapEntry* next) + : _hash(hash) + , _value(value) + , _nesting(nesting) + , _next(next) + { + } + + intx hash() { return _hash; } + Value value() { return _value; } + int nesting() { return _nesting; } + ValueMapEntry* next() { return _next; } + + void set_next(ValueMapEntry* next) { _next = next; } +}; + +define_array(ValueMapEntryArray, ValueMapEntry*) +define_stack(ValueMapEntryList, ValueMapEntryArray) + +// ValueMap implements nested hash tables for value numbering. It +// maintains a set _killed_values which represents the instructions +// which have been killed so far and an array of linked lists of +// ValueMapEntries names _entries. Each ValueMapEntry has a nesting +// which indicates what ValueMap nesting it belongs to. Higher +// nesting values are always before lower values in the linked list. +// This allows cloning of parent ValueMaps by simply copying the heads +// of the list. _entry_count represents the number of reachable +// entries in the ValueMap. A ValueMap is only allowed to mutate +// ValueMapEntries with the same nesting level. Adding or removing +// entries at the current nesting level requires updating +// _entry_count. Elements in the parent's list that get killed can be +// skipped if they are at the head of the list by simply moving to the +// next element in the list and decrementing _entry_count. + +class ValueMap: public CompilationResourceObj { + private: + int _nesting; + ValueMapEntryArray _entries; + ValueSet _killed_values; + int _entry_count; + + int nesting() { return _nesting; } + bool is_local_value_numbering() { return _nesting == 0; } + bool is_global_value_numbering() { return _nesting > 0; } + + int entry_count() { return _entry_count; } + int size() { return _entries.length(); } + ValueMapEntry* entry_at(int i) { return _entries.at(i); } + + // calculates the index of a hash value in a hash table of size n + int entry_index(intx hash, int n) { return (unsigned int)hash % n; } + + // if entry_count > size_threshold, the size of the hash table is increased + int size_threshold() { return size(); } + + // management of the killed-bitset for global value numbering + void kill_value(Value v) { if (is_global_value_numbering()) _killed_values.put(v); } + bool is_killed(Value v) { if (is_global_value_numbering()) return _killed_values.contains(v); else return false; } + + // helper functions + void increase_table_size(); + +#ifndef PRODUCT + static int _number_of_finds; + static int _number_of_hits; + static int _number_of_kills; +#endif // PRODUCT + + public: + // creation + ValueMap(); // empty value map + ValueMap(ValueMap* old); // value map with increased nesting + + // manipulation + Value find_insert(Value x); + + void kill_memory(); + void kill_field(ciField* field); + void kill_array(ValueType* type); + void kill_exception(); + void kill_map(ValueMap* map); + void kill_all(); + +#ifndef PRODUCT + // debugging/printing + void print(); + + static void reset_statistics(); + static void print_statistics(); +#endif +}; + +define_array(ValueMapArray, ValueMap*) + + +class ValueNumberingVisitor: public InstructionVisitor { + protected: + // called by visitor functions for instructions that kill values + virtual void kill_memory() = 0; + virtual void kill_field(ciField* field) = 0; + virtual void kill_array(ValueType* type) = 0; + + // visitor functions + void do_StoreField (StoreField* x) { kill_field(x->field()); }; + void do_StoreIndexed (StoreIndexed* x) { kill_array(x->type()); }; + void do_MonitorEnter (MonitorEnter* x) { kill_memory(); }; + void do_MonitorExit (MonitorExit* x) { kill_memory(); }; + void do_Invoke (Invoke* x) { kill_memory(); }; + void do_UnsafePutRaw (UnsafePutRaw* x) { kill_memory(); }; + void do_UnsafePutObject(UnsafePutObject* x) { kill_memory(); }; + void do_Intrinsic (Intrinsic* x) { if (!x->preserves_state()) kill_memory(); }; + + void do_Phi (Phi* x) { /* nothing to do */ }; + void do_Local (Local* x) { /* nothing to do */ }; + void do_Constant (Constant* x) { /* nothing to do */ }; + void do_LoadField (LoadField* x) { /* nothing to do */ }; + void do_ArrayLength (ArrayLength* x) { /* nothing to do */ }; + void do_LoadIndexed (LoadIndexed* x) { /* nothing to do */ }; + void do_NegateOp (NegateOp* x) { /* nothing to do */ }; + void do_ArithmeticOp (ArithmeticOp* x) { /* nothing to do */ }; + void do_ShiftOp (ShiftOp* x) { /* nothing to do */ }; + void do_LogicOp (LogicOp* x) { /* nothing to do */ }; + void do_CompareOp (CompareOp* x) { /* nothing to do */ }; + void do_IfOp (IfOp* x) { /* nothing to do */ }; + void do_Convert (Convert* x) { /* nothing to do */ }; + void do_NullCheck (NullCheck* x) { /* nothing to do */ }; + void do_NewInstance (NewInstance* x) { /* nothing to do */ }; + void do_NewTypeArray (NewTypeArray* x) { /* nothing to do */ }; + void do_NewObjectArray (NewObjectArray* x) { /* nothing to do */ }; + void do_NewMultiArray (NewMultiArray* x) { /* nothing to do */ }; + void do_CheckCast (CheckCast* x) { /* nothing to do */ }; + void do_InstanceOf (InstanceOf* x) { /* nothing to do */ }; + void do_BlockBegin (BlockBegin* x) { /* nothing to do */ }; + void do_Goto (Goto* x) { /* nothing to do */ }; + void do_If (If* x) { /* nothing to do */ }; + void do_IfInstanceOf (IfInstanceOf* x) { /* nothing to do */ }; + void do_TableSwitch (TableSwitch* x) { /* nothing to do */ }; + void do_LookupSwitch (LookupSwitch* x) { /* nothing to do */ }; + void do_Return (Return* x) { /* nothing to do */ }; + void do_Throw (Throw* x) { /* nothing to do */ }; + void do_Base (Base* x) { /* nothing to do */ }; + void do_OsrEntry (OsrEntry* x) { /* nothing to do */ }; + void do_ExceptionObject(ExceptionObject* x) { /* nothing to do */ }; + void do_RoundFP (RoundFP* x) { /* nothing to do */ }; + void do_UnsafeGetRaw (UnsafeGetRaw* x) { /* nothing to do */ }; + void do_UnsafeGetObject(UnsafeGetObject* x) { /* nothing to do */ }; + void do_UnsafePrefetchRead (UnsafePrefetchRead* x) { /* nothing to do */ }; + void do_UnsafePrefetchWrite(UnsafePrefetchWrite* x) { /* nothing to do */ }; + void do_ProfileCall (ProfileCall* x) { /* nothing to do */ }; + void do_ProfileCounter (ProfileCounter* x) { /* nothing to do */ }; +}; + + +class GlobalValueNumbering: public ValueNumberingVisitor { + private: + ValueMap* _current_map; // value map of current block + ValueMapArray _value_maps; // list of value maps for all blocks + + public: + // accessors + ValueMap* current_map() { return _current_map; } + ValueMap* value_map_of(BlockBegin* block) { return _value_maps.at(block->linear_scan_number()); } + void set_value_map_of(BlockBegin* block, ValueMap* map) { assert(value_map_of(block) == NULL, ""); _value_maps.at_put(block->linear_scan_number(), map); } + + // implementation for abstract methods of ValueNumberingVisitor + void kill_memory() { current_map()->kill_memory(); } + void kill_field(ciField* field) { current_map()->kill_field(field); } + void kill_array(ValueType* type) { current_map()->kill_array(type); } + + // main entry point that performs global value numbering + GlobalValueNumbering(IR* ir); +}; diff --git a/hotspot/src/share/vm/c1/c1_ValueSet.cpp b/hotspot/src/share/vm/c1/c1_ValueSet.cpp new file mode 100644 index 00000000000..f9f5b691cc0 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_ValueSet.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_ValueSet.cpp.incl" diff --git a/hotspot/src/share/vm/c1/c1_ValueSet.hpp b/hotspot/src/share/vm/c1/c1_ValueSet.hpp new file mode 100644 index 00000000000..6d661b1574e --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_ValueSet.hpp @@ -0,0 +1,95 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A ValueSet is a simple abstraction on top of a BitMap representing +// a set of Instructions. Currently it assumes that the number of +// instructions is fixed during its lifetime; should make it +// automatically resizable. + +class ValueSet: public CompilationResourceObj { + private: + BitMap _map; + + public: + ValueSet(); + + ValueSet* copy(); + bool contains(Value x); + void put (Value x); + void remove (Value x); + bool set_intersect(ValueSet* other); + void set_union(ValueSet* other); + void clear (); + void set_from(ValueSet* other); + bool equals (ValueSet* other); +}; + +inline ValueSet::ValueSet() : _map(Instruction::number_of_instructions()) { + _map.clear(); +} + + +inline ValueSet* ValueSet::copy() { + ValueSet* res = new ValueSet(); + res->_map.set_from(_map); + return res; +} + + +inline bool ValueSet::contains(Value x) { + return _map.at(x->id()); +} + + +inline void ValueSet::put(Value x) { + _map.set_bit(x->id()); +} + + +inline void ValueSet::remove(Value x) { + _map.clear_bit(x->id()); +} + + +inline bool ValueSet::set_intersect(ValueSet* other) { + return _map.set_intersection_with_result(other->_map); +} + + +inline void ValueSet::set_union(ValueSet* other) { + _map.set_union(other->_map); +} + + +inline void ValueSet::clear() { + _map.clear(); +} + +inline void ValueSet::set_from(ValueSet* other) { + _map.set_from(other->_map); +} + +inline bool ValueSet::equals(ValueSet* other) { + return _map.is_same(other->_map); +} diff --git a/hotspot/src/share/vm/c1/c1_ValueStack.cpp b/hotspot/src/share/vm/c1/c1_ValueStack.cpp new file mode 100644 index 00000000000..05f4602b653 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_ValueStack.cpp @@ -0,0 +1,285 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c1_ValueStack.cpp.incl" + + +// Implementation of ValueStack + +ValueStack::ValueStack(IRScope* scope, int locals_size, int max_stack_size) +: _scope(scope) +, _locals(locals_size, NULL) +, _stack(max_stack_size) +, _lock_stack(false) +, _locks(1) +{ + assert(scope != NULL, "scope must exist"); +} + +ValueStack* ValueStack::copy() { + ValueStack* s = new ValueStack(scope(), locals_size(), max_stack_size()); + s->_stack.appendAll(&_stack); + s->_locks.appendAll(&_locks); + s->replace_locals(this); + return s; +} + + +ValueStack* ValueStack::copy_locks() { + int sz = scope()->lock_stack_size(); + if (stack_size() == 0) { + sz = 0; + } + ValueStack* s = new ValueStack(scope(), locals_size(), sz); + s->_lock_stack = true; + s->_locks.appendAll(&_locks); + s->replace_locals(this); + if (sz > 0) { + assert(sz <= stack_size(), "lock stack underflow"); + for (int i = 0; i < sz; i++) { + s->_stack.append(_stack[i]); + } + } + return s; +} + +bool ValueStack::is_same(ValueStack* s) { + assert(s != NULL, "state must exist"); + assert(scope () == s->scope (), "scopes must correspond"); + assert(locals_size() == s->locals_size(), "locals sizes must correspond"); + return is_same_across_scopes(s); +} + + +bool ValueStack::is_same_across_scopes(ValueStack* s) { + assert(s != NULL, "state must exist"); + assert(stack_size () == s->stack_size (), "stack sizes must correspond"); + assert(locks_size () == s->locks_size (), "locks sizes must correspond"); + // compare each stack element with the corresponding stack element of s + int index; + Value value; + for_each_stack_value(this, index, value) { + if (value->type()->tag() != s->stack_at(index)->type()->tag()) return false; + } + for_each_lock_value(this, index, value) { + if (value != s->lock_at(index)) return false; + } + return true; +} + + +ValueStack* ValueStack::caller_state() const { + return scope()->caller_state(); +} + + +void ValueStack::clear_locals() { + for (int i = _locals.length() - 1; i >= 0; i--) { + _locals.at_put(i, NULL); + } +} + + +void ValueStack::replace_locals(ValueStack* with) { + assert(locals_size() == with->locals_size(), "number of locals must match"); + for (int i = locals_size() - 1; i >= 0; i--) { + _locals.at_put(i, with->_locals.at(i)); + } +} + +void ValueStack::pin_stack_for_linear_scan() { + for_each_state_value(this, v, + if (v->as_Constant() == NULL && v->as_Local() == NULL) { + v->pin(Instruction::PinStackForStateSplit); + } + ); +} + + +// apply function to all values of a list; factored out from values_do(f) +void ValueStack::apply(Values list, void f(Value*)) { + for (int i = 0; i < list.length(); i++) { + Value* va = list.adr_at(i); + Value v0 = *va; + if (v0 != NULL) { + if (!v0->type()->is_illegal()) { + assert(v0->as_HiWord() == NULL, "should never see HiWord during traversal"); + f(va); +#ifdef ASSERT + Value v1 = *va; + if (v0 != v1) { + assert(v1->type()->is_illegal() || v0->type()->tag() == v1->type()->tag(), "types must match"); + if (v0->type()->is_double_word()) { + list.at_put(i + 1, v0->hi_word()); + } + } +#endif + if (v0->type()->is_double_word()) i++; + } + } + } +} + + +void ValueStack::values_do(void f(Value*)) { + apply(_stack, f); + apply(_locks, f); + + ValueStack* state = this; + for_each_state(state) { + apply(state->_locals, f); + } +} + + +Values* ValueStack::pop_arguments(int argument_size) { + assert(stack_size() >= argument_size, "stack too small or too many arguments"); + int base = stack_size() - argument_size; + Values* args = new Values(argument_size); + for (int i = base; i < stack_size();) args->push(stack_at_inc(i)); + truncate_stack(base); + return args; +} + + +int ValueStack::lock(IRScope* scope, Value obj) { + _locks.push(obj); + scope->set_min_number_of_locks(locks_size()); + return locks_size() - 1; +} + + +int ValueStack::unlock() { + _locks.pop(); + return locks_size(); +} + + +ValueStack* ValueStack::push_scope(IRScope* scope) { + assert(scope->caller() == _scope, "scopes must have caller/callee relationship"); + ValueStack* res = new ValueStack(scope, + scope->method()->max_locals(), + max_stack_size() + scope->method()->max_stack()); + // Preserves stack and monitors. + res->_stack.appendAll(&_stack); + res->_locks.appendAll(&_locks); + assert(res->_stack.size() <= res->max_stack_size(), "stack overflow"); + return res; +} + + +ValueStack* ValueStack::pop_scope() { + assert(_scope->caller() != NULL, "scope must have caller"); + IRScope* scope = _scope->caller(); + int max_stack = max_stack_size() - _scope->method()->max_stack(); + assert(max_stack >= 0, "stack underflow"); + ValueStack* res = new ValueStack(scope, + scope->method()->max_locals(), + max_stack); + // Preserves stack and monitors. Restores local and store state from caller scope. + res->_stack.appendAll(&_stack); + res->_locks.appendAll(&_locks); + ValueStack* caller = caller_state(); + if (caller != NULL) { + for (int i = 0; i < caller->_locals.length(); i++) { + res->_locals.at_put(i, caller->_locals.at(i)); + } + assert(res->_locals.length() == res->scope()->method()->max_locals(), "just checking"); + } + assert(res->_stack.size() <= res->max_stack_size(), "stack overflow"); + return res; +} + + +void ValueStack::setup_phi_for_stack(BlockBegin* b, int index) { + assert(stack_at(index)->as_Phi() == NULL || stack_at(index)->as_Phi()->block() != b, "phi function already created"); + + ValueType* t = stack_at(index)->type(); + Value phi = new Phi(t, b, -index - 1); + _stack[index] = phi; + +#ifdef ASSERT + if (t->is_double_word()) { + _stack[index + 1] = phi->hi_word(); + } +#endif +} + +void ValueStack::setup_phi_for_local(BlockBegin* b, int index) { + assert(local_at(index)->as_Phi() == NULL || local_at(index)->as_Phi()->block() != b, "phi function already created"); + + ValueType* t = local_at(index)->type(); + Value phi = new Phi(t, b, index); + store_local(index, phi); +} + +#ifndef PRODUCT +void ValueStack::print() { + if (stack_is_empty()) { + tty->print_cr("empty stack"); + } else { + InstructionPrinter ip; + for (int i = 0; i < stack_size();) { + Value t = stack_at_inc(i); + tty->print("%2d ", i); + ip.print_instr(t); + tty->cr(); + } + } + if (!no_active_locks()) { + InstructionPrinter ip; + for (int i = 0; i < locks_size(); i--) { + Value t = lock_at(i); + tty->print("lock %2d ", i); + if (t == NULL) { + tty->print("this"); + } else { + ip.print_instr(t); + } + tty->cr(); + } + } + if (locals_size() > 0) { + InstructionPrinter ip; + for (int i = 0; i < locals_size();) { + Value l = _locals[i]; + tty->print("local %d ", i); + if (l == NULL) { + tty->print("null"); + i ++; + } else { + ip.print_instr(l); + if (l->type()->is_illegal() || l->type()->is_single_word()) i ++; else i += 2; + } + tty->cr(); + } + } +} + + +void ValueStack::verify() { + Unimplemented(); +} +#endif // PRODUCT diff --git a/hotspot/src/share/vm/c1/c1_ValueStack.hpp b/hotspot/src/share/vm/c1/c1_ValueStack.hpp new file mode 100644 index 00000000000..65f5b985c5c --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_ValueStack.hpp @@ -0,0 +1,345 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ValueStack: public CompilationResourceObj { + private: + IRScope* _scope; // the enclosing scope + bool _lock_stack; // indicates that this ValueStack is for an exception site + Values _locals; // the locals + Values _stack; // the expression stack + Values _locks; // the monitor stack (holding the locked values) + + Value check(ValueTag tag, Value t) { + assert(tag == t->type()->tag() || tag == objectTag && t->type()->tag() == addressTag, "types must correspond"); + return t; + } + + Value check(ValueTag tag, Value t, Value h) { + assert(h->as_HiWord()->lo_word() == t, "incorrect stack pair"); + return check(tag, t); + } + + // helper routine + static void apply(Values list, void f(Value*)); + + public: + // creation + ValueStack(IRScope* scope, int locals_size, int max_stack_size); + + // merging + ValueStack* copy(); // returns a copy of this w/ cleared locals + ValueStack* copy_locks(); // returns a copy of this w/ cleared locals and stack + // Note that when inlining of methods with exception + // handlers is enabled, this stack may have a + // non-empty expression stack (size defined by + // scope()->lock_stack_size()) + bool is_same(ValueStack* s); // returns true if this & s's types match (w/o checking locals) + bool is_same_across_scopes(ValueStack* s); // same as is_same but returns true even if stacks are in different scopes (used for block merging w/inlining) + + // accessors + IRScope* scope() const { return _scope; } + bool is_lock_stack() const { return _lock_stack; } + int locals_size() const { return _locals.length(); } + int stack_size() const { return _stack.length(); } + int locks_size() const { return _locks.length(); } + int max_stack_size() const { return _stack.capacity(); } + bool stack_is_empty() const { return _stack.is_empty(); } + bool no_active_locks() const { return _locks.is_empty(); } + ValueStack* caller_state() const; + + // locals access + void clear_locals(); // sets all locals to NULL; + + // Kill local i. Also kill local i+1 if i was a long or double. + void invalidate_local(int i) { + Value x = _locals.at(i); + if (x != NULL && x->type()->is_double_word()) { + assert(_locals.at(i + 1)->as_HiWord()->lo_word() == x, "locals inconsistent"); + _locals.at_put(i + 1, NULL); + } + _locals.at_put(i, NULL); + } + + + Value load_local(int i) const { + Value x = _locals.at(i); + if (x != NULL && x->type()->is_illegal()) return NULL; + assert(x == NULL || x->as_HiWord() == NULL, "index points to hi word"); + assert(x == NULL || x->type()->is_illegal() || x->type()->is_single_word() || x == _locals.at(i+1)->as_HiWord()->lo_word(), "locals inconsistent"); + return x; + } + + Value local_at(int i) const { return _locals.at(i); } + + // Store x into local i. + void store_local(int i, Value x) { + // Kill the old value + invalidate_local(i); + _locals.at_put(i, x); + + // Writing a double word can kill other locals + if (x != NULL && x->type()->is_double_word()) { + // If x + i was the start of a double word local then kill i + 2. + Value x2 = _locals.at(i + 1); + if (x2 != NULL && x2->type()->is_double_word()) { + _locals.at_put(i + 2, NULL); + } + + // If x is a double word local, also update i + 1. +#ifdef ASSERT + _locals.at_put(i + 1, x->hi_word()); +#else + _locals.at_put(i + 1, NULL); +#endif + } + // If x - 1 was the start of a double word local then kill i - 1. + if (i > 0) { + Value prev = _locals.at(i - 1); + if (prev != NULL && prev->type()->is_double_word()) { + _locals.at_put(i - 1, NULL); + } + } + } + + void replace_locals(ValueStack* with); + + // stack access + Value stack_at(int i) const { + Value x = _stack.at(i); + assert(x->as_HiWord() == NULL, "index points to hi word"); + assert(x->type()->is_single_word() || + x->subst() == _stack.at(i+1)->as_HiWord()->lo_word(), "stack inconsistent"); + return x; + } + + Value stack_at_inc(int& i) const { + Value x = stack_at(i); + i += x->type()->size(); + return x; + } + + // pinning support + void pin_stack_for_linear_scan(); + + // iteration + void values_do(void f(Value*)); + + // untyped manipulation (for dup_x1, etc.) + void clear_stack() { _stack.clear(); } + void truncate_stack(int size) { _stack.trunc_to(size); } + void raw_push(Value t) { _stack.push(t); } + Value raw_pop() { return _stack.pop(); } + + // typed manipulation + void ipush(Value t) { _stack.push(check(intTag , t)); } + void fpush(Value t) { _stack.push(check(floatTag , t)); } + void apush(Value t) { _stack.push(check(objectTag , t)); } + void rpush(Value t) { _stack.push(check(addressTag, t)); } +#ifdef ASSERT + // in debug mode, use HiWord for 2-word values + void lpush(Value t) { _stack.push(check(longTag , t)); _stack.push(new HiWord(t)); } + void dpush(Value t) { _stack.push(check(doubleTag , t)); _stack.push(new HiWord(t)); } +#else + // in optimized mode, use NULL for 2-word values + void lpush(Value t) { _stack.push(check(longTag , t)); _stack.push(NULL); } + void dpush(Value t) { _stack.push(check(doubleTag , t)); _stack.push(NULL); } +#endif // ASSERT + + void push(ValueType* type, Value t) { + switch (type->tag()) { + case intTag : ipush(t); return; + case longTag : lpush(t); return; + case floatTag : fpush(t); return; + case doubleTag : dpush(t); return; + case objectTag : apush(t); return; + case addressTag: rpush(t); return; + } + ShouldNotReachHere(); + } + + Value ipop() { return check(intTag , _stack.pop()); } + Value fpop() { return check(floatTag , _stack.pop()); } + Value apop() { return check(objectTag , _stack.pop()); } + Value rpop() { return check(addressTag, _stack.pop()); } +#ifdef ASSERT + // in debug mode, check for HiWord consistency + Value lpop() { Value h = _stack.pop(); return check(longTag , _stack.pop(), h); } + Value dpop() { Value h = _stack.pop(); return check(doubleTag, _stack.pop(), h); } +#else + // in optimized mode, ignore HiWord since it is NULL + Value lpop() { _stack.pop(); return check(longTag , _stack.pop()); } + Value dpop() { _stack.pop(); return check(doubleTag, _stack.pop()); } +#endif // ASSERT + + Value pop(ValueType* type) { + switch (type->tag()) { + case intTag : return ipop(); + case longTag : return lpop(); + case floatTag : return fpop(); + case doubleTag : return dpop(); + case objectTag : return apop(); + case addressTag: return rpop(); + } + ShouldNotReachHere(); + return NULL; + } + + Values* pop_arguments(int argument_size); + + // locks access + int lock (IRScope* scope, Value obj); + int unlock(); + Value lock_at(int i) const { return _locks.at(i); } + + // Inlining support + ValueStack* push_scope(IRScope* scope); // "Push" new scope, returning new resulting stack + // Preserves stack and locks, destroys locals + ValueStack* pop_scope(); // "Pop" topmost scope, returning new resulting stack + // Preserves stack and locks, destroys locals + + // SSA form IR support + void setup_phi_for_stack(BlockBegin* b, int index); + void setup_phi_for_local(BlockBegin* b, int index); + + // debugging + void print() PRODUCT_RETURN; + void verify() PRODUCT_RETURN; +}; + + + +// Macro definitions for simple iteration of stack and local values of a ValueStack +// The macros can be used like a for-loop. All variables (state, index and value) +// must be defined before the loop. +// When states are nested because of inlining, the stack of the innermost state +// cumulates also the stack of the nested states. In contrast, the locals of all +// states must be iterated each. +// Use the following code pattern to iterate all stack values and all nested local values: +// +// ValueStack* state = ... // state that is iterated +// int index; // current loop index (overwritten in loop) +// Value value; // value at current loop index (overwritten in loop) +// +// for_each_stack_value(state, index, value { +// do something with value and index +// } +// +// for_each_state(state) { +// for_each_local_value(state, index, value) { +// do something with value and index +// } +// } +// as an invariant, state is NULL now + + +// construct a unique variable name with the line number where the macro is used +#define temp_var3(x) temp__ ## x +#define temp_var2(x) temp_var3(x) +#define temp_var temp_var2(__LINE__) + +#define for_each_state(state) \ + for (; state != NULL; state = state->caller_state()) + +#define for_each_local_value(state, index, value) \ + int temp_var = state->locals_size(); \ + for (index = 0; \ + index < temp_var && (value = state->local_at(index), true); \ + index += (value == NULL || value->type()->is_illegal() ? 1 : value->type()->size())) \ + if (value != NULL) + + +#define for_each_stack_value(state, index, value) \ + int temp_var = state->stack_size(); \ + for (index = 0; \ + index < temp_var && (value = state->stack_at(index), true); \ + index += value->type()->size()) + + +#define for_each_lock_value(state, index, value) \ + int temp_var = state->locks_size(); \ + for (index = 0; \ + index < temp_var && (value = state->lock_at(index), true); \ + index++) \ + if (value != NULL) + + +// Macro definition for simple iteration of all state values of a ValueStack +// Because the code cannot be executed in a single loop, the code must be passed +// as a macro parameter. +// Use the following code pattern to iterate all stack values and all nested local values: +// +// ValueStack* state = ... // state that is iterated +// for_each_state_value(state, value, +// do something with value (note that this is a macro parameter) +// ); + +#define for_each_state_value(v_state, v_value, v_code) \ +{ \ + int cur_index; \ + ValueStack* cur_state = v_state; \ + Value v_value; \ + { \ + for_each_stack_value(cur_state, cur_index, v_value) { \ + v_code; \ + } \ + } \ + for_each_state(cur_state) { \ + for_each_local_value(cur_state, cur_index, v_value) { \ + v_code; \ + } \ + } \ +} + + +// Macro definition for simple iteration of all phif functions of a block, i.e all +// phi functions of the ValueStack where the block matches. +// Use the following code pattern to iterate all phi functions of a block: +// +// BlockBegin* block = ... // block that is iterated +// for_each_phi_function(block, phi, +// do something with the phi function phi (note that this is a macro parameter) +// ); + +#define for_each_phi_fun(v_block, v_phi, v_code) \ +{ \ + int cur_index; \ + ValueStack* cur_state = v_block->state(); \ + Value value; \ + { \ + for_each_stack_value(cur_state, cur_index, value) { \ + Phi* v_phi = value->as_Phi(); \ + if (v_phi != NULL && v_phi->block() == v_block) { \ + v_code; \ + } \ + } \ + } \ + { \ + for_each_local_value(cur_state, cur_index, value) { \ + Phi* v_phi = value->as_Phi(); \ + if (v_phi != NULL && v_phi->block() == v_block) { \ + v_code; \ + } \ + } \ + } \ +} diff --git a/hotspot/src/share/vm/c1/c1_ValueType.cpp b/hotspot/src/share/vm/c1/c1_ValueType.cpp new file mode 100644 index 00000000000..79896e43fcf --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_ValueType.cpp @@ -0,0 +1,155 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_ValueType.cpp.incl" + + +// predefined types +VoidType* voidType = NULL; +IntType* intType = NULL; +LongType* longType = NULL; +FloatType* floatType = NULL; +DoubleType* doubleType = NULL; +ObjectType* objectType = NULL; +ArrayType* arrayType = NULL; +InstanceType* instanceType = NULL; +ClassType* classType = NULL; +AddressType* addressType = NULL; +IllegalType* illegalType = NULL; + + +// predefined constants +IntConstant* intZero = NULL; +IntConstant* intOne = NULL; +ObjectConstant* objectNull = NULL; + + +void ValueType::initialize() { + // Note: Must initialize all types for each compilation + // as they are allocated within a ResourceMark! + + // types + voidType = new VoidType(); + intType = new IntType(); + longType = new LongType(); + floatType = new FloatType(); + doubleType = new DoubleType(); + objectType = new ObjectType(); + arrayType = new ArrayType(); + instanceType = new InstanceType(); + classType = new ClassType(); + addressType = new AddressType(); + illegalType = new IllegalType(); + + // constants + intZero = new IntConstant(0); + intOne = new IntConstant(1); + objectNull = new ObjectConstant(ciNullObject::make()); +}; + + +ValueType* ValueType::meet(ValueType* y) const { + // incomplete & conservative solution for now - fix this! + assert(tag() == y->tag(), "types must match"); + return base(); +} + + +ValueType* ValueType::join(ValueType* y) const { + Unimplemented(); + return NULL; +} + + + +jobject ObjectType::encoding() const { + assert(is_constant(), "must be"); + return constant_value()->encoding(); +} + +bool ObjectType::is_loaded() const { + assert(is_constant(), "must be"); + return constant_value()->is_loaded(); +} + +ciObject* ObjectConstant::constant_value() const { return _value; } +ciObject* ArrayConstant::constant_value() const { return _value; } +ciObject* InstanceConstant::constant_value() const { return _value; } +ciObject* ClassConstant::constant_value() const { return _value; } + + +ValueType* as_ValueType(BasicType type) { + switch (type) { + case T_VOID : return voidType; + case T_BYTE : // fall through + case T_CHAR : // fall through + case T_SHORT : // fall through + case T_BOOLEAN: // fall through + case T_INT : return intType; + case T_LONG : return longType; + case T_FLOAT : return floatType; + case T_DOUBLE : return doubleType; + case T_ARRAY : return arrayType; + case T_OBJECT : return objectType; + case T_ADDRESS: return addressType; + case T_ILLEGAL: return illegalType; + } + ShouldNotReachHere(); + return illegalType; +} + + +ValueType* as_ValueType(ciConstant value) { + switch (value.basic_type()) { + case T_BYTE : // fall through + case T_CHAR : // fall through + case T_SHORT : // fall through + case T_BOOLEAN: // fall through + case T_INT : return new IntConstant (value.as_int ()); + case T_LONG : return new LongConstant (value.as_long ()); + case T_FLOAT : return new FloatConstant (value.as_float ()); + case T_DOUBLE : return new DoubleConstant(value.as_double()); + case T_ARRAY : // fall through (ciConstant doesn't have an array accessor) + case T_OBJECT : return new ObjectConstant(value.as_object()); + } + ShouldNotReachHere(); + return illegalType; +} + + +BasicType as_BasicType(ValueType* type) { + switch (type->tag()) { + case voidTag: return T_VOID; + case intTag: return T_INT; + case longTag: return T_LONG; + case floatTag: return T_FLOAT; + case doubleTag: return T_DOUBLE; + case objectTag: return T_OBJECT; + case addressTag: return T_ADDRESS; + case illegalTag: return T_ILLEGAL; + } + ShouldNotReachHere(); + return T_ILLEGAL; +} diff --git a/hotspot/src/share/vm/c1/c1_ValueType.hpp b/hotspot/src/share/vm/c1/c1_ValueType.hpp new file mode 100644 index 00000000000..6f66bfbcc55 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_ValueType.hpp @@ -0,0 +1,421 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// type hierarchy +class ValueType; +class VoidType; +class IntType; +class IntConstant; +class IntInterval; +class LongType; +class LongConstant; +class FloatType; +class FloatConstant; +class DoubleType; +class DoubleConstant; +class ObjectType; +class ObjectConstant; +class ArrayType; +class ArrayConstant; +class InstanceType; +class InstanceConstant; +class ClassType; +class ClassConstant; +class AddressType; +class AddressConstant; +class IllegalType; + + +// predefined types +extern VoidType* voidType; +extern IntType* intType; +extern LongType* longType; +extern FloatType* floatType; +extern DoubleType* doubleType; +extern ObjectType* objectType; +extern ArrayType* arrayType; +extern InstanceType* instanceType; +extern ClassType* classType; +extern AddressType* addressType; +extern IllegalType* illegalType; + + +// predefined constants +extern IntConstant* intZero; +extern IntConstant* intOne; +extern ObjectConstant* objectNull; + + +// tags +enum ValueTag { + // all legal tags must come first + intTag, + longTag, + floatTag, + doubleTag, + objectTag, + addressTag, + number_of_legal_tags, + // all other tags must follow afterwards + voidTag = number_of_legal_tags, + illegalTag, + number_of_tags +}; + + +class ValueType: public CompilationResourceObj { + private: + const int _size; + const ValueTag _tag; + ValueType(); + protected: + ValueType(ValueTag tag, int size): _tag(tag), _size(size) {} + + public: + // initialization + static void initialize(); + + // accessors + virtual ValueType* base() const = 0; // the 'canonical' type (e.g., intType for an IntConstant) + ValueTag tag() const { return _tag; } // the 'canonical' tag (useful for type matching) + int size() const { // the size of an object of the type in words + assert(_size > -1, "shouldn't be asking for size"); + return _size; + } + virtual const char tchar() const = 0; // the type 'character' for printing + virtual const char* name() const = 0; // the type name for printing + virtual bool is_constant() const { return false; } + + // testers + bool is_void() { return tag() == voidTag; } + bool is_int() { return tag() == intTag; } + bool is_long() { return tag() == longTag; } + bool is_float() { return tag() == floatTag; } + bool is_double() { return tag() == doubleTag; } + bool is_object() { return as_ObjectType() != NULL; } + bool is_array() { return as_ArrayType() != NULL; } + bool is_instance() { return as_InstanceType() != NULL; } + bool is_class() { return as_ClassType() != NULL; } + bool is_address() { return as_AddressType() != NULL; } + bool is_illegal() { return tag() == illegalTag; } + + bool is_int_kind() const { return tag() == intTag || tag() == longTag; } + bool is_float_kind() const { return tag() == floatTag || tag() == doubleTag; } + bool is_object_kind() const { return tag() == objectTag; } + + bool is_single_word() const { return _size == 1; } + bool is_double_word() const { return _size == 2; } + + // casting + virtual VoidType* as_VoidType() { return NULL; } + virtual IntType* as_IntType() { return NULL; } + virtual LongType* as_LongType() { return NULL; } + virtual FloatType* as_FloatType() { return NULL; } + virtual DoubleType* as_DoubleType() { return NULL; } + virtual ObjectType* as_ObjectType() { return NULL; } + virtual ArrayType* as_ArrayType() { return NULL; } + virtual InstanceType* as_InstanceType() { return NULL; } + virtual ClassType* as_ClassType() { return NULL; } + virtual AddressType* as_AddressType() { return NULL; } + virtual IllegalType* as_IllegalType() { return NULL; } + + virtual IntConstant* as_IntConstant() { return NULL; } + virtual LongConstant* as_LongConstant() { return NULL; } + virtual FloatConstant* as_FloatConstant() { return NULL; } + virtual DoubleConstant* as_DoubleConstant() { return NULL; } + virtual ObjectConstant* as_ObjectConstant() { return NULL; } + virtual InstanceConstant* as_InstanceConstant(){ return NULL; } + virtual ClassConstant* as_ClassConstant() { return NULL; } + virtual ArrayConstant* as_ArrayConstant() { return NULL; } + virtual AddressConstant* as_AddressConstant() { return NULL; } + + // type operations + ValueType* meet(ValueType* y) const; + ValueType* join(ValueType* y) const; + + // debugging + void print(outputStream* s = tty) { s->print(name()); } +}; + + +class VoidType: public ValueType { + public: + VoidType(): ValueType(voidTag, 0) {} + virtual ValueType* base() const { return voidType; } + virtual const char tchar() const { return 'v'; } + virtual const char* name() const { return "void"; } + virtual VoidType* as_VoidType() { return this; } +}; + + +class IntType: public ValueType { + public: + IntType(): ValueType(intTag, 1) {} + virtual ValueType* base() const { return intType; } + virtual const char tchar() const { return 'i'; } + virtual const char* name() const { return "int"; } + virtual IntType* as_IntType() { return this; } +}; + + +class IntConstant: public IntType { + private: + jint _value; + + public: + IntConstant(jint value) { _value = value; } + + jint value() const { return _value; } + + virtual bool is_constant() const { return true; } + virtual IntConstant* as_IntConstant() { return this; } +}; + + +class IntInterval: public IntType { + private: + jint _beg; + jint _end; + + public: + IntInterval(jint beg, jint end) { + assert(beg <= end, "illegal interval"); + _beg = beg; + _end = end; + } + + jint beg() const { return _beg; } + jint end() const { return _end; } + + virtual bool is_interval() const { return true; } +}; + + +class LongType: public ValueType { + public: + LongType(): ValueType(longTag, 2) {} + virtual ValueType* base() const { return longType; } + virtual const char tchar() const { return 'l'; } + virtual const char* name() const { return "long"; } + virtual LongType* as_LongType() { return this; } +}; + + +class LongConstant: public LongType { + private: + jlong _value; + + public: + LongConstant(jlong value) { _value = value; } + + jlong value() const { return _value; } + + virtual bool is_constant() const { return true; } + virtual LongConstant* as_LongConstant() { return this; } +}; + + +class FloatType: public ValueType { + public: + FloatType(): ValueType(floatTag, 1) {} + virtual ValueType* base() const { return floatType; } + virtual const char tchar() const { return 'f'; } + virtual const char* name() const { return "float"; } + virtual FloatType* as_FloatType() { return this; } +}; + + +class FloatConstant: public FloatType { + private: + jfloat _value; + + public: + FloatConstant(jfloat value) { _value = value; } + + jfloat value() const { return _value; } + + virtual bool is_constant() const { return true; } + virtual FloatConstant* as_FloatConstant() { return this; } +}; + + +class DoubleType: public ValueType { + public: + DoubleType(): ValueType(doubleTag, 2) {} + virtual ValueType* base() const { return doubleType; } + virtual const char tchar() const { return 'd'; } + virtual const char* name() const { return "double"; } + virtual DoubleType* as_DoubleType() { return this; } +}; + + +class DoubleConstant: public DoubleType { + private: + jdouble _value; + + public: + DoubleConstant(jdouble value) { _value = value; } + + jdouble value() const { return _value; } + + virtual bool is_constant() const { return true; } + virtual DoubleConstant* as_DoubleConstant() { return this; } +}; + + +class ObjectType: public ValueType { + public: + ObjectType(): ValueType(objectTag, 1) {} + virtual ValueType* base() const { return objectType; } + virtual const char tchar() const { return 'a'; } + virtual const char* name() const { return "object"; } + virtual ObjectType* as_ObjectType() { return this; } + virtual ciObject* constant_value() const { ShouldNotReachHere(); return NULL; } + bool is_loaded() const; + jobject encoding() const; +}; + + +class ObjectConstant: public ObjectType { + private: + ciObject* _value; + + public: + ObjectConstant(ciObject* value) { _value = value; } + + ciObject* value() const { return _value; } + + virtual bool is_constant() const { return true; } + virtual ObjectConstant* as_ObjectConstant() { return this; } + virtual ciObject* constant_value() const; +}; + + +class ArrayType: public ObjectType { + public: + virtual ArrayType* as_ArrayType() { return this; } +}; + + +class ArrayConstant: public ArrayType { + private: + ciArray* _value; + + public: + ArrayConstant(ciArray* value) { _value = value; } + + ciArray* value() const { return _value; } + + virtual bool is_constant() const { return true; } + + virtual ArrayConstant* as_ArrayConstant() { return this; } + virtual ciObject* constant_value() const; +}; + + +class InstanceType: public ObjectType { + public: + virtual InstanceType* as_InstanceType() { return this; } +}; + + +class InstanceConstant: public InstanceType { + private: + ciInstance* _value; + + public: + InstanceConstant(ciInstance* value) { _value = value; } + + ciInstance* value() const { return _value; } + + virtual bool is_constant() const { return true; } + + virtual InstanceConstant* as_InstanceConstant(){ return this; } + virtual ciObject* constant_value() const; +}; + + +class ClassType: public ObjectType { + public: + virtual ClassType* as_ClassType() { return this; } +}; + + +class ClassConstant: public ClassType { + private: + ciInstanceKlass* _value; + + public: + ClassConstant(ciInstanceKlass* value) { _value = value; } + + ciInstanceKlass* value() const { return _value; } + + virtual bool is_constant() const { return true; } + + virtual ClassConstant* as_ClassConstant() { return this; } + virtual ciObject* constant_value() const; +}; + + +class AddressType: public ValueType { + public: + AddressType(): ValueType(addressTag, 1) {} + virtual ValueType* base() const { return addressType; } + virtual const char tchar() const { return 'r'; } + virtual const char* name() const { return "address"; } + virtual AddressType* as_AddressType() { return this; } +}; + + +class AddressConstant: public AddressType { + private: + jint _value; + + public: + AddressConstant(jint value) { _value = value; } + + jint value() const { return _value; } + + virtual bool is_constant() const { return true; } + + virtual AddressConstant* as_AddressConstant() { return this; } +}; + + +class IllegalType: public ValueType { + public: + IllegalType(): ValueType(illegalTag, -1) {} + virtual ValueType* base() const { return illegalType; } + virtual const char tchar() const { return ' '; } + virtual const char* name() const { return "illegal"; } + virtual IllegalType* as_IllegalType() { return this; } +}; + + +// conversion between ValueTypes, BasicTypes, and ciConstants +ValueType* as_ValueType(BasicType type); +ValueType* as_ValueType(ciConstant value); +BasicType as_BasicType(ValueType* type); + +inline ValueType* as_ValueType(ciType* type) { return as_ValueType(type->basic_type()); } diff --git a/hotspot/src/share/vm/c1/c1_globals.cpp b/hotspot/src/share/vm/c1/c1_globals.cpp new file mode 100644 index 00000000000..4b8ca5c3a4d --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_globals.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c1_globals.cpp.incl" + +C1_FLAGS(MATERIALIZE_DEVELOPER_FLAG, MATERIALIZE_PD_DEVELOPER_FLAG, MATERIALIZE_PRODUCT_FLAG, MATERIALIZE_PD_PRODUCT_FLAG, MATERIALIZE_NOTPRODUCT_FLAG) diff --git a/hotspot/src/share/vm/c1/c1_globals.hpp b/hotspot/src/share/vm/c1/c1_globals.hpp new file mode 100644 index 00000000000..52a10b72490 --- /dev/null +++ b/hotspot/src/share/vm/c1/c1_globals.hpp @@ -0,0 +1,332 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Defines all global flags used by the client compiler. +// +#ifndef TIERED + #define NOT_TIERED(x) x +#else + #define NOT_TIERED(x) +#endif + +#define C1_FLAGS(develop, develop_pd, product, product_pd, notproduct) \ + \ + /* Printing */ \ + notproduct(bool, PrintC1Statistics, false, \ + "Print Compiler1 statistics" ) \ + \ + notproduct(bool, PrintInitialBlockList, false, \ + "Print block list of BlockListBuilder") \ + \ + notproduct(bool, PrintCFG, false, \ + "Print control flow graph after each change") \ + \ + notproduct(bool, PrintCFG0, false, \ + "Print control flow graph after construction") \ + \ + notproduct(bool, PrintCFG1, false, \ + "Print control flow graph after optimizations") \ + \ + notproduct(bool, PrintCFG2, false, \ + "Print control flow graph before code generation") \ + \ + notproduct(bool, PrintIRDuringConstruction, false, \ + "Print IR as it's being constructed (helpful for debugging frontend)")\ + \ + notproduct(bool, PrintPhiFunctions, false, \ + "Print phi functions when they are created and simplified") \ + \ + notproduct(bool, PrintIR, false, \ + "Print full intermediate representation after each change") \ + \ + notproduct(bool, PrintIR0, false, \ + "Print full intermediate representation after construction") \ + \ + notproduct(bool, PrintIR1, false, \ + "Print full intermediate representation after optimizations") \ + \ + notproduct(bool, PrintIR2, false, \ + "Print full intermediate representation before code generation") \ + \ + notproduct(bool, PrintSimpleStubs, false, \ + "Print SimpleStubs") \ + \ + /* C1 optimizations */ \ + \ + develop(bool, UseC1Optimizations, true, \ + "Turn on C1 optimizations") \ + \ + develop(bool, SelectivePhiFunctions, true, \ + "create phi functions at loop headers only when necessary") \ + \ + develop(bool, DoCEE, true, \ + "Do Conditional Expression Elimination to simplify CFG") \ + \ + develop(bool, PrintCEE, false, \ + "Print Conditional Expression Elimination") \ + \ + develop(bool, UseLocalValueNumbering, true, \ + "Use Local Value Numbering (embedded in GraphBuilder)") \ + \ + develop(bool, UseGlobalValueNumbering, true, \ + "Use Global Value Numbering (separate phase)") \ + \ + develop(bool, PrintValueNumbering, false, \ + "Print Value Numbering") \ + \ + product(intx, ValueMapInitialSize, 11, \ + "Initial size of a value map") \ + \ + product(intx, ValueMapMaxLoopSize, 8, \ + "maximum size of a loop optimized by global value numbering") \ + \ + develop(bool, EliminateBlocks, true, \ + "Eliminate unneccessary basic blocks") \ + \ + develop(bool, PrintBlockElimination, false, \ + "Print basic block elimination") \ + \ + develop(bool, EliminateNullChecks, true, \ + "Eliminate unneccessary null checks") \ + \ + develop(bool, PrintNullCheckElimination, false, \ + "Print null check elimination") \ + \ + develop(bool, EliminateFieldAccess, true, \ + "Optimize field loads and stores") \ + \ + develop(bool, InlineMethodsWithExceptionHandlers, true, \ + "Inline methods containing exception handlers " \ + "(NOTE: does not work with current backend)") \ + \ + develop(bool, InlineSynchronizedMethods, true, \ + "Inline synchronized methods") \ + \ + develop(bool, InlineNIOCheckIndex, true, \ + "Intrinsify java.nio.Buffer.checkIndex") \ + \ + develop(bool, CanonicalizeNodes, true, \ + "Canonicalize graph nodes") \ + \ + develop(bool, CanonicalizeExperimental, false, \ + "Canonicalize graph nodes, experimental code") \ + \ + develop(bool, PrintCanonicalization, false, \ + "Print graph node canonicalization") \ + \ + develop(bool, UseTableRanges, true, \ + "Faster versions of lookup table using ranges") \ + \ + develop(bool, UseFastExceptionHandling, true, \ + "Faster handling of exceptions") \ + \ + develop_pd(bool, RoundFPResults, \ + "Indicates whether rounding is needed for floating point results")\ + \ + develop(intx, NestedInliningSizeRatio, 90, \ + "Percentage of prev. allowed inline size in recursive inlining") \ + \ + notproduct(bool, PrintIRWithLIR, false, \ + "Print IR instructions with generated LIR") \ + \ + notproduct(bool, PrintLIRWithAssembly, false, \ + "Show LIR instruction with generated assembly") \ + \ + develop(bool, CommentedAssembly, trueInDebug, \ + "Show extra info in PrintNMethods output") \ + \ + develop(bool, LIRTracePeephole, false, \ + "Trace peephole optimizer") \ + \ + develop(bool, LIRTraceExecution, false, \ + "add LIR code which logs the execution of blocks") \ + \ + product_pd(bool, LIRFillDelaySlots, \ + "fill delays on on SPARC with LIR") \ + \ + develop_pd(bool, CSEArrayLength, \ + "Create separate nodes for length in array accesses") \ + \ + develop_pd(bool, TwoOperandLIRForm, \ + "true if LIR requires src1 and dst to match in binary LIR ops") \ + \ + develop(intx, TraceLinearScanLevel, 0, \ + "Debug levels for the linear scan allocator") \ + \ + develop(bool, StressLinearScan, false, \ + "scramble block order used by LinearScan (stress test)") \ + \ + product(bool, TimeLinearScan, false, \ + "detailed timing of LinearScan phases") \ + \ + develop(bool, TimeEachLinearScan, false, \ + "print detailed timing of each LinearScan run") \ + \ + develop(bool, CountLinearScan, false, \ + "collect statistic counters during LinearScan") \ + \ + /* C1 variable */ \ + \ + develop(bool, C1Breakpoint, false, \ + "Sets a breakpoint at entry of each compiled method") \ + \ + develop(bool, ImplicitDiv0Checks, true, \ + "Use implicit division by zero checks") \ + \ + develop(bool, PinAllInstructions, false, \ + "All instructions are pinned") \ + \ + develop(bool, ValueStackPinStackAll, true, \ + "Pinning in ValueStack pin everything") \ + \ + develop(bool, UseFastNewInstance, true, \ + "Use fast inlined instance allocation") \ + \ + develop(bool, UseFastNewTypeArray, true, \ + "Use fast inlined type array allocation") \ + \ + develop(bool, UseFastNewObjectArray, true, \ + "Use fast inlined object array allocation") \ + \ + develop(bool, UseFastLocking, true, \ + "Use fast inlined locking code") \ + \ + product(bool, FastTLABRefill, true, \ + "Use fast TLAB refill code") \ + \ + develop(bool, UseSlowPath, false, \ + "For debugging: test slow cases by always using them") \ + \ + develop(bool, GenerateArrayStoreCheck, true, \ + "Generates code for array store checks") \ + \ + develop(bool, DeoptC1, true, \ + "Use deoptimization in C1") \ + \ + develop(bool, DeoptOnAsyncException, true, \ + "Deoptimize upon Thread.stop(); improves precision of IR") \ + \ + develop(bool, PrintBailouts, false, \ + "Print bailout and its reason") \ + \ + develop(bool, TracePatching, false, \ + "Trace patching of field access on uninitialized classes") \ + \ + develop(bool, PatchALot, false, \ + "Marks all fields as having unloaded classes") \ + \ + develop(bool, PrintNotLoaded, false, \ + "Prints where classes are not loaded during code generation") \ + \ + notproduct(bool, VerifyOopMaps, false, \ + "Adds oopmap verification code to the generated code") \ + \ + develop(bool, PrintLIR, false, \ + "print low-level IR") \ + \ + develop(bool, BailoutAfterHIR, false, \ + "bailout of compilation after building of HIR") \ + \ + develop(bool, BailoutAfterLIR, false, \ + "bailout of compilation after building of LIR") \ + \ + develop(bool, BailoutOnExceptionHandlers, false, \ + "bailout of compilation for methods with exception handlers") \ + \ + develop(bool, AlwaysEmitDebugInfo, false, \ + "always emit debug info") \ + \ + develop(bool, InstallMethods, true, \ + "Install methods at the end of successful compilations") \ + \ + product(intx, CompilationRepeat, 0, \ + "Number of times to recompile method before returning result") \ + \ + develop(intx, NMethodSizeLimit, (32*K)*wordSize, \ + "Maximum size of a compiled method.") \ + \ + develop(bool, TraceFPUStack, false, \ + "Trace emulation of the FPU stack (intel only)") \ + \ + develop(bool, TraceFPURegisterUsage, false, \ + "Trace usage of FPU registers at start of blocks (intel only)") \ + \ + develop(bool, OptimizeUnsafes, true, \ + "Optimize raw unsafe ops") \ + \ + develop(bool, PrintUnsafeOptimization, false, \ + "Print optimization of raw unsafe ops") \ + \ + develop(intx, InstructionCountCutoff, 37000, \ + "If GraphBuilder adds this many instructions, bails out") \ + \ + product_pd(intx, SafepointPollOffset, \ + "Offset added to polling address (Intel only)") \ + \ + product(bool, UseNewFeature1, false, \ + "Enable new feature for testing. This is a dummy flag.") \ + \ + product(bool, UseNewFeature2, false, \ + "Enable new feature for testing. This is a dummy flag.") \ + \ + product(bool, UseNewFeature3, false, \ + "Enable new feature for testing. This is a dummy flag.") \ + \ + product(bool, UseNewFeature4, false, \ + "Enable new feature for testing. This is a dummy flag.") \ + \ + develop(bool, ComputeExactFPURegisterUsage, true, \ + "Compute additional live set for fpu registers to simplify fpu stack merge (Intel only)") \ + \ + product(bool, Tier1ProfileCalls, true, \ + "Profile calls when generating code for updating MDOs") \ + \ + product(bool, Tier1ProfileVirtualCalls, true, \ + "Profile virtual calls when generating code for updating MDOs") \ + \ + product(bool, Tier1ProfileInlinedCalls, true, \ + "Profile inlined calls when generating code for updating MDOs") \ + \ + product(bool, Tier1ProfileBranches, true, \ + "Profile branches when generating code for updating MDOs") \ + \ + product(bool, Tier1ProfileCheckcasts, true, \ + "Profile checkcasts when generating code for updating MDOs") \ + \ + product(bool, Tier1OptimizeVirtualCallProfiling, true, \ + "Use CHA and exact type results at call sites when updating MDOs") \ + \ + develop(bool, Tier1CountOnly, false, \ + "Don't schedule tier 2 compiles. Enter VM only") \ + \ + develop(bool, PrintCFGToFile, false, \ + "print control flow graph to a separate file during compilation") \ + \ + + +// Read default values for c1 globals +// #include "incls/_c1_globals_pd.hpp.incl" + +C1_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_NOTPRODUCT_FLAG) diff --git a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp new file mode 100644 index 00000000000..5a414028b28 --- /dev/null +++ b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp @@ -0,0 +1,1321 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +#include "incls/_precompiled.incl" +#include "incls/_bcEscapeAnalyzer.cpp.incl" + + +#ifndef PRODUCT + #define TRACE_BCEA(level, code) \ + if (EstimateArgEscape && BCEATraceLevel >= level) { \ + code; \ + } +#else + #define TRACE_BCEA(level, code) +#endif + +// Maintain a map of which aguments a local variable or +// stack slot may contain. In addition to tracking +// arguments, it tracks two special values, "allocated" +// which represents any object allocated in the current +// method, and "unknown" which is any other object. +// Up to 30 arguments are handled, with the last one +// representing summary information for any extra arguments +class BCEscapeAnalyzer::ArgumentMap { + uint _bits; + enum {MAXBIT = 29, + ALLOCATED = 1, + UNKNOWN = 2}; + + uint int_to_bit(uint e) const { + if (e > MAXBIT) + e = MAXBIT; + return (1 << (e + 2)); + } + +public: + ArgumentMap() { _bits = 0;} + void set_bits(uint bits) { _bits = bits;} + uint get_bits() const { return _bits;} + void clear() { _bits = 0;} + void set_all() { _bits = ~0u; } + bool is_empty() const { return _bits == 0; } + bool contains(uint var) const { return (_bits & int_to_bit(var)) != 0; } + bool is_singleton(uint var) const { return (_bits == int_to_bit(var)); } + bool contains_unknown() const { return (_bits & UNKNOWN) != 0; } + bool contains_allocated() const { return (_bits & ALLOCATED) != 0; } + bool contains_vars() const { return (_bits & (((1 << MAXBIT) -1) << 2)) != 0; } + void set(uint var) { _bits = int_to_bit(var); } + void add(uint var) { _bits |= int_to_bit(var); } + void add_unknown() { _bits = UNKNOWN; } + void add_allocated() { _bits = ALLOCATED; } + void set_union(const ArgumentMap &am) { _bits |= am._bits; } + void set_intersect(const ArgumentMap &am) { _bits |= am._bits; } + void set_difference(const ArgumentMap &am) { _bits &= ~am._bits; } + void operator=(const ArgumentMap &am) { _bits = am._bits; } + bool operator==(const ArgumentMap &am) { return _bits == am._bits; } + bool operator!=(const ArgumentMap &am) { return _bits != am._bits; } +}; + +class BCEscapeAnalyzer::StateInfo { +public: + ArgumentMap *_vars; + ArgumentMap *_stack; + short _stack_height; + short _max_stack; + bool _initialized; + ArgumentMap empty_map; + + StateInfo() { + empty_map.clear(); + } + + ArgumentMap raw_pop() { assert(_stack_height > 0, "stack underflow"); return _stack[--_stack_height]; } + ArgumentMap apop() { return raw_pop(); } + void spop() { raw_pop(); } + void lpop() { spop(); spop(); } + void raw_push(ArgumentMap i) { assert(_stack_height < _max_stack, "stack overflow"); _stack[_stack_height++] = i; } + void apush(ArgumentMap i) { raw_push(i); } + void spush() { raw_push(empty_map); } + void lpush() { spush(); spush(); } + +}; + +void BCEscapeAnalyzer::set_returned(ArgumentMap vars) { + for (int i = 0; i <= _arg_size; i++) { + if (vars.contains(i)) + _arg_returned.set_bit(i); + } + _return_local = _return_local && !(vars.contains_unknown() || vars.contains_allocated()); + _return_allocated = _return_allocated && vars.contains_allocated() && !(vars.contains_unknown() || vars.contains_vars()); +} + + +// return true if any element of vars is an argument +bool BCEscapeAnalyzer::is_argument(ArgumentMap vars) { + for (int i = 0; i <= _arg_size; i++) { + if (vars.contains(i)) + return true; + } + return false; +} + +// return true if any element of vars is an arg_stack argument +bool BCEscapeAnalyzer::is_arg_stack(ArgumentMap vars){ + if (_conservative) + return true; + for (int i = 0; i <= _arg_size; i++) { + if (vars.contains(i) && _arg_stack.at(i)) + return true; + } + return false; +} + +void BCEscapeAnalyzer::clear_bits(ArgumentMap vars, BitMap &bm) { + for (int i = 0; i <= _arg_size; i++) { + if (vars.contains(i)) { + bm.clear_bit(i); + } + } +} +void BCEscapeAnalyzer::set_method_escape(ArgumentMap vars) { + clear_bits(vars, _arg_local); +} + +void BCEscapeAnalyzer::set_global_escape(ArgumentMap vars) { + clear_bits(vars, _arg_local); + clear_bits(vars, _arg_stack); + if (vars.contains_allocated()) + _allocated_escapes = true; +} + +void BCEscapeAnalyzer::set_dirty(ArgumentMap vars) { + clear_bits(vars, _dirty); +} + +bool BCEscapeAnalyzer::is_recursive_call(ciMethod* callee) { + for (BCEscapeAnalyzer* scope = this; scope != NULL; scope = scope->_parent) { + if (scope->method() == callee) { + return true; + } + } + return false; +} + +void BCEscapeAnalyzer::invoke(StateInfo &state, Bytecodes::Code code, ciMethod* target, ciKlass* holder) { + int i; + + // retrieve information about the callee + ciInstanceKlass* klass = target->holder(); + ciInstanceKlass* calling_klass = method()->holder(); + ciInstanceKlass* callee_holder = ciEnv::get_instance_klass_for_declared_method_holder(holder); + ciInstanceKlass* actual_recv = callee_holder; + + // compute size of arguments + int arg_size = target->arg_size(); + if (!target->is_loaded() && code == Bytecodes::_invokestatic) { + arg_size--; + } + int arg_base = MAX2(state._stack_height - arg_size, 0); + + // direct recursive calls are skipped if they can be bound statically without introducing + // dependencies and if parameters are passed at the same position as in the current method + // other calls are skipped if there are no unescaped arguments passed to them + bool directly_recursive = (method() == target) && + (code != Bytecodes::_invokevirtual || target->is_final_method() || state._stack[arg_base] .is_empty()); + + // check if analysis of callee can safely be skipped + bool skip_callee = true; + for (i = state._stack_height - 1; i >= arg_base && skip_callee; i--) { + ArgumentMap arg = state._stack[i]; + skip_callee = !is_argument(arg) || !is_arg_stack(arg) || (directly_recursive && arg.is_singleton(i - arg_base)); + } + if (skip_callee) { + TRACE_BCEA(3, tty->print_cr("[EA] skipping method %s::%s", holder->name()->as_utf8(), target->name()->as_utf8())); + for (i = 0; i < arg_size; i++) { + set_method_escape(state.raw_pop()); + } + return; + } + + // determine actual method (use CHA if necessary) + ciMethod* inline_target = NULL; + if (target->is_loaded() && klass->is_loaded() + && (klass->is_initialized() || klass->is_interface() && target->holder()->is_initialized()) + && target->will_link(klass, callee_holder, code)) { + if (code == Bytecodes::_invokestatic + || code == Bytecodes::_invokespecial + || code == Bytecodes::_invokevirtual && target->is_final_method()) { + inline_target = target; + } else { + inline_target = target->find_monomorphic_target(calling_klass, callee_holder, actual_recv); + } + } + + if (inline_target != NULL && !is_recursive_call(inline_target)) { + // analyze callee + BCEscapeAnalyzer analyzer(inline_target, this); + + // adjust escape state of actual parameters + bool must_record_dependencies = false; + for (i = arg_size - 1; i >= 0; i--) { + ArgumentMap arg = state.raw_pop(); + if (!is_argument(arg)) + continue; + if (!is_arg_stack(arg)) { + // arguments have already been recognized as escaping + } else if (analyzer.is_arg_stack(i) && !analyzer.is_arg_returned(i)) { + set_method_escape(arg); + must_record_dependencies = true; + } else { + set_global_escape(arg); + } + } + + // record dependencies if at least one parameter retained stack-allocatable + if (must_record_dependencies) { + if (code == Bytecodes::_invokeinterface || code == Bytecodes::_invokevirtual && !target->is_final_method()) { + _dependencies.append(actual_recv); + _dependencies.append(inline_target); + } + _dependencies.appendAll(analyzer.dependencies()); + } + } else { + TRACE_BCEA(1, tty->print_cr("[EA] virtual method %s is not monomorphic.", + target->name()->as_utf8())); + // conservatively mark all actual parameters as escaping globally + for (i = 0; i < arg_size; i++) { + ArgumentMap arg = state.raw_pop(); + if (!is_argument(arg)) + continue; + set_global_escape(arg); + } + } +} + +bool BCEscapeAnalyzer::contains(uint arg_set1, uint arg_set2) { + return ((~arg_set1) | arg_set2) == 0; +} + + +void BCEscapeAnalyzer::iterate_one_block(ciBlock *blk, StateInfo &state, GrowableArray &successors) { + + blk->set_processed(); + ciBytecodeStream s(method()); + int limit_bci = blk->limit_bci(); + bool fall_through = false; + ArgumentMap allocated_obj; + allocated_obj.add_allocated(); + ArgumentMap unknown_obj; + unknown_obj.add_unknown(); + ArgumentMap empty_map; + + s.reset_to_bci(blk->start_bci()); + while (s.next() != ciBytecodeStream::EOBC() && s.cur_bci() < limit_bci) { + fall_through = true; + switch (s.cur_bc()) { + case Bytecodes::_nop: + break; + case Bytecodes::_aconst_null: + state.apush(empty_map); + break; + case Bytecodes::_iconst_m1: + case Bytecodes::_iconst_0: + case Bytecodes::_iconst_1: + case Bytecodes::_iconst_2: + case Bytecodes::_iconst_3: + case Bytecodes::_iconst_4: + case Bytecodes::_iconst_5: + case Bytecodes::_fconst_0: + case Bytecodes::_fconst_1: + case Bytecodes::_fconst_2: + case Bytecodes::_bipush: + case Bytecodes::_sipush: + state.spush(); + break; + case Bytecodes::_lconst_0: + case Bytecodes::_lconst_1: + case Bytecodes::_dconst_0: + case Bytecodes::_dconst_1: + state.lpush(); + break; + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + if (type2size[s.get_constant().basic_type()] == 1) { + state.spush(); + } else { + state.lpush(); + } + break; + case Bytecodes::_aload: + state.apush(state._vars[s.get_index()]); + break; + case Bytecodes::_iload: + case Bytecodes::_fload: + case Bytecodes::_iload_0: + case Bytecodes::_iload_1: + case Bytecodes::_iload_2: + case Bytecodes::_iload_3: + case Bytecodes::_fload_0: + case Bytecodes::_fload_1: + case Bytecodes::_fload_2: + case Bytecodes::_fload_3: + state.spush(); + break; + case Bytecodes::_lload: + case Bytecodes::_dload: + case Bytecodes::_lload_0: + case Bytecodes::_lload_1: + case Bytecodes::_lload_2: + case Bytecodes::_lload_3: + case Bytecodes::_dload_0: + case Bytecodes::_dload_1: + case Bytecodes::_dload_2: + case Bytecodes::_dload_3: + state.lpush(); + break; + case Bytecodes::_aload_0: + state.apush(state._vars[0]); + break; + case Bytecodes::_aload_1: + state.apush(state._vars[1]); + break; + case Bytecodes::_aload_2: + state.apush(state._vars[2]); + break; + case Bytecodes::_aload_3: + state.apush(state._vars[3]); + break; + case Bytecodes::_iaload: + case Bytecodes::_faload: + case Bytecodes::_baload: + case Bytecodes::_caload: + case Bytecodes::_saload: + state.spop(); + set_method_escape(state.apop()); + state.spush(); + break; + case Bytecodes::_laload: + case Bytecodes::_daload: + state.spop(); + set_method_escape(state.apop()); + state.lpush(); + break; + case Bytecodes::_aaload: + { state.spop(); + ArgumentMap array = state.apop(); + set_method_escape(array); + state.apush(unknown_obj); + set_dirty(array); + } + break; + case Bytecodes::_istore: + case Bytecodes::_fstore: + case Bytecodes::_istore_0: + case Bytecodes::_istore_1: + case Bytecodes::_istore_2: + case Bytecodes::_istore_3: + case Bytecodes::_fstore_0: + case Bytecodes::_fstore_1: + case Bytecodes::_fstore_2: + case Bytecodes::_fstore_3: + state.spop(); + break; + case Bytecodes::_lstore: + case Bytecodes::_dstore: + case Bytecodes::_lstore_0: + case Bytecodes::_lstore_1: + case Bytecodes::_lstore_2: + case Bytecodes::_lstore_3: + case Bytecodes::_dstore_0: + case Bytecodes::_dstore_1: + case Bytecodes::_dstore_2: + case Bytecodes::_dstore_3: + state.lpop(); + break; + case Bytecodes::_astore: + state._vars[s.get_index()] = state.apop(); + break; + case Bytecodes::_astore_0: + state._vars[0] = state.apop(); + break; + case Bytecodes::_astore_1: + state._vars[1] = state.apop(); + break; + case Bytecodes::_astore_2: + state._vars[2] = state.apop(); + break; + case Bytecodes::_astore_3: + state._vars[3] = state.apop(); + break; + case Bytecodes::_iastore: + case Bytecodes::_fastore: + case Bytecodes::_bastore: + case Bytecodes::_castore: + case Bytecodes::_sastore: + { + state.spop(); + state.spop(); + ArgumentMap arr = state.apop(); + set_method_escape(arr); + break; + } + case Bytecodes::_lastore: + case Bytecodes::_dastore: + { + state.lpop(); + state.spop(); + ArgumentMap arr = state.apop(); + set_method_escape(arr); + break; + } + case Bytecodes::_aastore: + { + set_global_escape(state.apop()); + state.spop(); + ArgumentMap arr = state.apop(); + break; + } + case Bytecodes::_pop: + state.raw_pop(); + break; + case Bytecodes::_pop2: + state.raw_pop(); + state.raw_pop(); + break; + case Bytecodes::_dup: + { ArgumentMap w1 = state.raw_pop(); + state.raw_push(w1); + state.raw_push(w1); + } + break; + case Bytecodes::_dup_x1: + { ArgumentMap w1 = state.raw_pop(); + ArgumentMap w2 = state.raw_pop(); + state.raw_push(w1); + state.raw_push(w2); + state.raw_push(w1); + } + break; + case Bytecodes::_dup_x2: + { ArgumentMap w1 = state.raw_pop(); + ArgumentMap w2 = state.raw_pop(); + ArgumentMap w3 = state.raw_pop(); + state.raw_push(w1); + state.raw_push(w3); + state.raw_push(w2); + state.raw_push(w1); + } + break; + case Bytecodes::_dup2: + { ArgumentMap w1 = state.raw_pop(); + ArgumentMap w2 = state.raw_pop(); + state.raw_push(w2); + state.raw_push(w1); + state.raw_push(w2); + state.raw_push(w1); + } + break; + case Bytecodes::_dup2_x1: + { ArgumentMap w1 = state.raw_pop(); + ArgumentMap w2 = state.raw_pop(); + ArgumentMap w3 = state.raw_pop(); + state.raw_push(w2); + state.raw_push(w1); + state.raw_push(w3); + state.raw_push(w2); + state.raw_push(w1); + } + break; + case Bytecodes::_dup2_x2: + { ArgumentMap w1 = state.raw_pop(); + ArgumentMap w2 = state.raw_pop(); + ArgumentMap w3 = state.raw_pop(); + ArgumentMap w4 = state.raw_pop(); + state.raw_push(w2); + state.raw_push(w1); + state.raw_push(w4); + state.raw_push(w3); + state.raw_push(w2); + state.raw_push(w1); + } + break; + case Bytecodes::_swap: + { ArgumentMap w1 = state.raw_pop(); + ArgumentMap w2 = state.raw_pop(); + state.raw_push(w1); + state.raw_push(w2); + } + break; + case Bytecodes::_iadd: + case Bytecodes::_fadd: + case Bytecodes::_isub: + case Bytecodes::_fsub: + case Bytecodes::_imul: + case Bytecodes::_fmul: + case Bytecodes::_idiv: + case Bytecodes::_fdiv: + case Bytecodes::_irem: + case Bytecodes::_frem: + case Bytecodes::_iand: + case Bytecodes::_ior: + case Bytecodes::_ixor: + state.spop(); + state.spop(); + state.spush(); + break; + case Bytecodes::_ladd: + case Bytecodes::_dadd: + case Bytecodes::_lsub: + case Bytecodes::_dsub: + case Bytecodes::_lmul: + case Bytecodes::_dmul: + case Bytecodes::_ldiv: + case Bytecodes::_ddiv: + case Bytecodes::_lrem: + case Bytecodes::_drem: + case Bytecodes::_land: + case Bytecodes::_lor: + case Bytecodes::_lxor: + state.lpop(); + state.lpop(); + state.lpush(); + break; + case Bytecodes::_ishl: + case Bytecodes::_ishr: + case Bytecodes::_iushr: + state.spop(); + state.spop(); + state.spush(); + break; + case Bytecodes::_lshl: + case Bytecodes::_lshr: + case Bytecodes::_lushr: + state.spop(); + state.lpop(); + state.lpush(); + break; + case Bytecodes::_ineg: + case Bytecodes::_fneg: + state.spop(); + state.spush(); + break; + case Bytecodes::_lneg: + case Bytecodes::_dneg: + state.lpop(); + state.lpush(); + break; + case Bytecodes::_iinc: + break; + case Bytecodes::_i2l: + case Bytecodes::_i2d: + case Bytecodes::_f2l: + case Bytecodes::_f2d: + state.spop(); + state.lpush(); + break; + case Bytecodes::_i2f: + case Bytecodes::_f2i: + state.spop(); + state.spush(); + break; + case Bytecodes::_l2i: + case Bytecodes::_l2f: + case Bytecodes::_d2i: + case Bytecodes::_d2f: + state.lpop(); + state.spush(); + break; + case Bytecodes::_l2d: + case Bytecodes::_d2l: + state.lpop(); + state.lpush(); + break; + case Bytecodes::_i2b: + case Bytecodes::_i2c: + case Bytecodes::_i2s: + state.spop(); + state.spush(); + break; + case Bytecodes::_lcmp: + case Bytecodes::_dcmpl: + case Bytecodes::_dcmpg: + state.lpop(); + state.lpop(); + state.spush(); + break; + case Bytecodes::_fcmpl: + case Bytecodes::_fcmpg: + state.spop(); + state.spop(); + state.spush(); + break; + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + { + state.spop(); + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + break; + } + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + { + state.spop(); + state.spop(); + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + break; + } + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + { + set_method_escape(state.apop()); + set_method_escape(state.apop()); + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + break; + } + case Bytecodes::_goto: + { + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + fall_through = false; + break; + } + case Bytecodes::_jsr: + { + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + state.apush(empty_map); + successors.push(_methodBlocks->block_containing(dest_bci)); + fall_through = false; + break; + } + case Bytecodes::_ret: + // we don't track the destination of a "ret" instruction + assert(s.next_bci() == limit_bci, "branch must end block"); + fall_through = false; + break; + case Bytecodes::_return: + assert(s.next_bci() == limit_bci, "return must end block"); + fall_through = false; + break; + case Bytecodes::_tableswitch: + { + state.spop(); + Bytecode_tableswitch* switch_ = Bytecode_tableswitch_at(s.cur_bcp()); + int len = switch_->length(); + int dest_bci; + for (int i = 0; i < len; i++) { + dest_bci = s.cur_bci() + switch_->dest_offset_at(i); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + } + dest_bci = s.cur_bci() + switch_->default_offset(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + assert(s.next_bci() == limit_bci, "branch must end block"); + fall_through = false; + break; + } + case Bytecodes::_lookupswitch: + { + state.spop(); + Bytecode_lookupswitch* switch_ = Bytecode_lookupswitch_at(s.cur_bcp()); + int len = switch_->number_of_pairs(); + int dest_bci; + for (int i = 0; i < len; i++) { + dest_bci = s.cur_bci() + switch_->pair_at(i)->offset(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + } + dest_bci = s.cur_bci() + switch_->default_offset(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + fall_through = false; + break; + } + case Bytecodes::_ireturn: + case Bytecodes::_freturn: + state.spop(); + fall_through = false; + break; + case Bytecodes::_lreturn: + case Bytecodes::_dreturn: + state.lpop(); + fall_through = false; + break; + case Bytecodes::_areturn: + set_returned(state.apop()); + fall_through = false; + break; + case Bytecodes::_getstatic: + case Bytecodes::_getfield: + { bool will_link; + ciField* field = s.get_field(will_link); + BasicType field_type = field->type()->basic_type(); + if (s.cur_bc() != Bytecodes::_getstatic) { + set_method_escape(state.apop()); + } + if (field_type == T_OBJECT || field_type == T_ARRAY) { + state.apush(unknown_obj); + } else if (type2size[field_type] == 1) { + state.spush(); + } else { + state.lpush(); + } + } + break; + case Bytecodes::_putstatic: + case Bytecodes::_putfield: + { bool will_link; + ciField* field = s.get_field(will_link); + BasicType field_type = field->type()->basic_type(); + if (field_type == T_OBJECT || field_type == T_ARRAY) { + set_global_escape(state.apop()); + } else if (type2size[field_type] == 1) { + state.spop(); + } else { + state.lpop(); + } + if (s.cur_bc() != Bytecodes::_putstatic) { + ArgumentMap p = state.apop(); + set_method_escape(p); + } + } + break; + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokeinterface: + { bool will_link; + ciMethod* target = s.get_method(will_link); + ciKlass* holder = s.get_declared_method_holder(); + invoke(state, s.cur_bc(), target, holder); + ciType* return_type = target->return_type(); + if (!return_type->is_primitive_type()) { + state.apush(unknown_obj); + } else if (return_type->is_one_word()) { + state.spush(); + } else if (return_type->is_two_word()) { + state.lpush(); + } + } + break; + case Bytecodes::_xxxunusedxxx: + ShouldNotReachHere(); + break; + case Bytecodes::_new: + state.apush(allocated_obj); + break; + case Bytecodes::_newarray: + case Bytecodes::_anewarray: + state.spop(); + state.apush(allocated_obj); + break; + case Bytecodes::_multianewarray: + { int i = s.cur_bcp()[3]; + while (i-- > 0) state.spop(); + state.apush(allocated_obj); + } + break; + case Bytecodes::_arraylength: + set_method_escape(state.apop()); + state.spush(); + break; + case Bytecodes::_athrow: + set_global_escape(state.apop()); + fall_through = false; + break; + case Bytecodes::_checkcast: + { ArgumentMap obj = state.apop(); + set_method_escape(obj); + state.apush(obj); + } + break; + case Bytecodes::_instanceof: + set_method_escape(state.apop()); + state.spush(); + break; + case Bytecodes::_monitorenter: + case Bytecodes::_monitorexit: + state.apop(); + break; + case Bytecodes::_wide: + ShouldNotReachHere(); + break; + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + { + set_method_escape(state.apop()); + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + break; + } + case Bytecodes::_goto_w: + { + int dest_bci = s.get_far_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + fall_through = false; + break; + } + case Bytecodes::_jsr_w: + { + int dest_bci = s.get_far_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + state.apush(empty_map); + successors.push(_methodBlocks->block_containing(dest_bci)); + fall_through = false; + break; + } + case Bytecodes::_breakpoint: + break; + default: + ShouldNotReachHere(); + break; + } + + } + if (fall_through) { + int fall_through_bci = s.cur_bci(); + if (fall_through_bci < _method->code_size()) { + assert(_methodBlocks->is_block_start(fall_through_bci), "must fall through to block start."); + successors.push(_methodBlocks->block_containing(fall_through_bci)); + } + } +} + +void BCEscapeAnalyzer::merge_block_states(StateInfo *blockstates, ciBlock *dest, StateInfo *s_state) { + StateInfo *d_state = blockstates+dest->index(); + int nlocals = _method->max_locals(); + + // exceptions may cause transfer of control to handlers in the middle of a + // block, so we don't merge the incoming state of exception handlers + if (dest->is_handler()) + return; + if (!d_state->_initialized ) { + // destination not initialized, just copy + for (int i = 0; i < nlocals; i++) { + d_state->_vars[i] = s_state->_vars[i]; + } + for (int i = 0; i < s_state->_stack_height; i++) { + d_state->_stack[i] = s_state->_stack[i]; + } + d_state->_stack_height = s_state->_stack_height; + d_state->_max_stack = s_state->_max_stack; + d_state->_initialized = true; + } else if (!dest->processed()) { + // we have not yet walked the bytecodes of dest, we can merge + // the states + assert(d_state->_stack_height == s_state->_stack_height, "computed stack heights must match"); + for (int i = 0; i < nlocals; i++) { + d_state->_vars[i].set_union(s_state->_vars[i]); + } + for (int i = 0; i < s_state->_stack_height; i++) { + d_state->_stack[i].set_union(s_state->_stack[i]); + } + } else { + // the bytecodes of dest have already been processed, mark any + // arguments in the source state which are not in the dest state + // as global escape. + // Future refinement: we only need to mark these variable to the + // maximum escape of any variables in dest state + assert(d_state->_stack_height == s_state->_stack_height, "computed stack heights must match"); + ArgumentMap extra_vars; + for (int i = 0; i < nlocals; i++) { + ArgumentMap t; + t = s_state->_vars[i]; + t.set_difference(d_state->_vars[i]); + extra_vars.set_union(t); + } + for (int i = 0; i < s_state->_stack_height; i++) { + ArgumentMap t; + t.clear(); + t = s_state->_stack[i]; + t.set_difference(d_state->_stack[i]); + extra_vars.set_union(t); + } + set_global_escape(extra_vars); + } +} + +void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { + int numblocks = _methodBlocks->num_blocks(); + int stkSize = _method->max_stack(); + int numLocals = _method->max_locals(); + StateInfo state; + + int datacount = (numblocks + 1) * (stkSize + numLocals); + int datasize = datacount * sizeof(ArgumentMap); + StateInfo *blockstates = (StateInfo *) arena->Amalloc(_methodBlocks->num_blocks() * sizeof(StateInfo)); + ArgumentMap *statedata = (ArgumentMap *) arena->Amalloc(datasize); + for (int i = 0; i < datacount; i++) ::new ((void*)&statedata[i]) ArgumentMap(); + ArgumentMap *dp = statedata; + state._vars = dp; + dp += numLocals; + state._stack = dp; + dp += stkSize; + state._initialized = false; + state._max_stack = stkSize; + for (int i = 0; i < numblocks; i++) { + blockstates[i]._vars = dp; + dp += numLocals; + blockstates[i]._stack = dp; + dp += stkSize; + blockstates[i]._initialized = false; + blockstates[i]._stack_height = 0; + blockstates[i]._max_stack = stkSize; + } + GrowableArray worklist(arena, numblocks / 4, 0, NULL); + GrowableArray successors(arena, 4, 0, NULL); + + _methodBlocks->clear_processed(); + + // initialize block 0 state from method signature + ArgumentMap allVars; // all oop arguments to method + ciSignature* sig = method()->signature(); + int j = 0; + if (!method()->is_static()) { + // record information for "this" + blockstates[0]._vars[j].set(j); + allVars.add(j); + j++; + } + for (int i = 0; i < sig->count(); i++) { + ciType* t = sig->type_at(i); + if (!t->is_primitive_type()) { + blockstates[0]._vars[j].set(j); + allVars.add(j); + } + j += t->size(); + } + blockstates[0]._initialized = true; + assert(j == _arg_size, "just checking"); + + ArgumentMap unknown_map; + unknown_map.add_unknown(); + + worklist.push(_methodBlocks->block_containing(0)); + while(worklist.length() > 0) { + ciBlock *blk = worklist.pop(); + StateInfo *blkState = blockstates+blk->index(); + if (blk->is_handler() || blk->is_ret_target()) { + // for an exception handler or a target of a ret instruction, we assume the worst case, + // that any variable or stack slot could contain any argument + for (int i = 0; i < numLocals; i++) { + state._vars[i] = allVars; + } + if (blk->is_handler()) { + state._stack_height = 1; + } else { + state._stack_height = blkState->_stack_height; + } + for (int i = 0; i < state._stack_height; i++) { + state._stack[i] = allVars; + } + } else { + for (int i = 0; i < numLocals; i++) { + state._vars[i] = blkState->_vars[i]; + } + for (int i = 0; i < blkState->_stack_height; i++) { + state._stack[i] = blkState->_stack[i]; + } + state._stack_height = blkState->_stack_height; + } + iterate_one_block(blk, state, successors); + // if this block has any exception handlers, push them + // onto successor list + if (blk->has_handler()) { + DEBUG_ONLY(int handler_count = 0;) + int blk_start = blk->start_bci(); + int blk_end = blk->limit_bci(); + for (int i = 0; i < numblocks; i++) { + ciBlock *b = _methodBlocks->block(i); + if (b->is_handler()) { + int ex_start = b->ex_start_bci(); + int ex_end = b->ex_limit_bci(); + if ((ex_start >= blk_start && ex_start < blk_end) || + (ex_end > blk_start && ex_end <= blk_end)) { + successors.push(b); + } + DEBUG_ONLY(handler_count++;) + } + } + assert(handler_count > 0, "must find at least one handler"); + } + // merge computed variable state with successors + while(successors.length() > 0) { + ciBlock *succ = successors.pop(); + merge_block_states(blockstates, succ, &state); + if (!succ->processed()) + worklist.push(succ); + } + } +} + +bool BCEscapeAnalyzer::do_analysis() { + Arena* arena = CURRENT_ENV->arena(); + // identify basic blocks + _methodBlocks = _method->get_method_blocks(); + + iterate_blocks(arena); + // TEMPORARY + return true; +} + +vmIntrinsics::ID BCEscapeAnalyzer::known_intrinsic() { + vmIntrinsics::ID iid = method()->intrinsic_id(); + + if (iid == vmIntrinsics::_getClass || + iid == vmIntrinsics::_hashCode) + return iid; + else + return vmIntrinsics::_none; +} + +bool BCEscapeAnalyzer::compute_escape_for_intrinsic(vmIntrinsics::ID iid) { + ArgumentMap empty; + empty.clear(); + switch (iid) { + case vmIntrinsics::_getClass: + _return_local = false; + break; + case vmIntrinsics::_hashCode: + // initialized state is correct + break; + default: + assert(false, "unexpected intrinsic"); + } + return true; +} + +void BCEscapeAnalyzer::initialize() { + int i; + + // clear escape information (method may have been deoptimized) + methodData()->clear_escape_info(); + + // initialize escape state of object parameters + ciSignature* sig = method()->signature(); + int j = 0; + if (!method()->is_static()) { + _arg_local.set_bit(0); + _arg_stack.set_bit(0); + j++; + } + for (i = 0; i < sig->count(); i++) { + ciType* t = sig->type_at(i); + if (!t->is_primitive_type()) { + _arg_local.set_bit(j); + _arg_stack.set_bit(j); + } + j += t->size(); + } + assert(j == _arg_size, "just checking"); + + // start with optimistic assumption + ciType *rt = _method->return_type(); + if (rt->is_primitive_type()) { + _return_local = false; + _return_allocated = false; + } else { + _return_local = true; + _return_allocated = true; + } + _allocated_escapes = false; +} + +void BCEscapeAnalyzer::clear_escape_info() { + ciSignature* sig = method()->signature(); + int arg_count = sig->count(); + ArgumentMap var; + for (int i = 0; i < arg_count; i++) { + var.clear(); + var.set(i); + set_global_escape(var); + } + _arg_local.clear(); + _arg_stack.clear(); + _arg_returned.clear(); + _return_local = false; + _return_allocated = false; + _allocated_escapes = true; +} + + +void BCEscapeAnalyzer::compute_escape_info() { + int i; + assert(!methodData()->has_escape_info(), "do not overwrite escape info"); + + vmIntrinsics::ID iid = known_intrinsic(); + + // check if method can be analyzed + if (iid == vmIntrinsics::_none && (method()->is_abstract() || method()->is_native() || !method()->holder()->is_initialized() + || _level > MaxBCEAEstimateLevel + || method()->code_size() > MaxBCEAEstimateSize)) { + if (BCEATraceLevel >= 1) { + tty->print("Skipping method because: "); + if (method()->is_abstract()) + tty->print_cr("method is abstract."); + else if (method()->is_native()) + tty->print_cr("method is native."); + else if (!method()->holder()->is_initialized()) + tty->print_cr("class of method is not initialized."); + else if (_level > MaxBCEAEstimateLevel) + tty->print_cr("level (%d) exceeds MaxBCEAEstimateLevel (%d).", + _level, MaxBCEAEstimateLevel); + else if (method()->code_size() > MaxBCEAEstimateSize) + tty->print_cr("code size (%d) exceeds MaxBCEAEstimateSize.", + method()->code_size(), MaxBCEAEstimateSize); + else + ShouldNotReachHere(); + } + clear_escape_info(); + + return; + } + + if (BCEATraceLevel >= 1) { + tty->print("[EA] estimating escape information for"); + if (iid != vmIntrinsics::_none) + tty->print(" intrinsic"); + method()->print_short_name(); + tty->print_cr(" (%d bytes)", method()->code_size()); + } + + bool success; + + initialize(); + + // do not scan method if it has no object parameters + if (_arg_local.is_empty()) { + methodData()->set_eflag(methodDataOopDesc::estimated); + return; + } + + if (iid != vmIntrinsics::_none) + success = compute_escape_for_intrinsic(iid); + else { + success = do_analysis(); + } + + // dump result of bytecode analysis +#ifndef PRODUCT + if (BCEATraceLevel >= 3) { + tty->print("[EA] estimated escape information for"); + if (iid != vmIntrinsics::_none) + tty->print(" intrinsic"); + method()->print_short_name(); + tty->print_cr(has_dependencies() ? " (not stored)" : ""); + tty->print(" non-escaping args: "); + _arg_local.print_on(tty); + tty->print(" stack-allocatable args: "); + _arg_stack.print_on(tty); + if (_return_local) { + tty->print(" returned args: "); + _arg_returned.print_on(tty); + } else if (is_return_allocated()) { + tty->print_cr(" allocated return values"); + } else { + tty->print_cr(" non-local return values"); + } + tty->cr(); + tty->print(" flags: "); + if (_return_allocated) + tty->print(" return_allocated"); + tty->cr(); + } + +#endif + // don't store interprocedural escape information if it introduces dependencies + // or if method data is empty + // + if (!has_dependencies() && !methodData()->is_empty()) { + for (i = 0; i < _arg_size; i++) { + if (_arg_local.at(i)) { + assert(_arg_stack.at(i), "inconsistent escape info"); + methodData()->set_arg_local(i); + methodData()->set_arg_stack(i); + } else if (_arg_stack.at(i)) { + methodData()->set_arg_stack(i); + } + if (_arg_returned.at(i)) { + methodData()->set_arg_returned(i); + } + } + if (_return_local) { + methodData()->set_eflag(methodDataOopDesc::return_local); + } + methodData()->set_eflag(methodDataOopDesc::estimated); + } +} + +void BCEscapeAnalyzer::read_escape_info() { + assert(methodData()->has_escape_info(), "no escape info available"); + + // read escape information from method descriptor + for (int i = 0; i < _arg_size; i++) { + _arg_local.at_put(i, methodData()->is_arg_local(i)); + _arg_stack.at_put(i, methodData()->is_arg_stack(i)); + _arg_returned.at_put(i, methodData()->is_arg_returned(i)); + } + _return_local = methodData()->eflag_set(methodDataOopDesc::return_local); + + // dump result of loaded escape information +#ifndef PRODUCT + if (BCEATraceLevel >= 4) { + tty->print(" non-escaping args: "); + _arg_local.print_on(tty); + tty->print(" stack-allocatable args: "); + _arg_stack.print_on(tty); + if (_return_local) { + tty->print(" returned args: "); + _arg_returned.print_on(tty); + } else { + tty->print_cr(" non-local return values"); + } + tty->print(" modified args: "); + tty->cr(); + } +#endif + +} + + +BCEscapeAnalyzer::BCEscapeAnalyzer(ciMethod* method, BCEscapeAnalyzer* parent) + : _conservative(method == NULL || !EstimateArgEscape) + , _method(method) + , _methodData(method ? method->method_data() : NULL) + , _arg_size(method ? method->arg_size() : 0) + , _stack() + , _arg_local(_arg_size) + , _arg_stack(_arg_size) + , _arg_returned(_arg_size) + , _dirty(_arg_size) + , _return_local(false) + , _return_allocated(false) + , _allocated_escapes(false) + , _dependencies() + , _parent(parent) + , _level(parent == NULL ? 0 : parent->level() + 1) { + if (!_conservative) { + _arg_local.clear(); + _arg_stack.clear(); + _arg_returned.clear(); + _dirty.clear(); + Arena* arena = CURRENT_ENV->arena(); + + if (methodData() == NULL) + return; + bool printit = _method->should_print_assembly(); + if (methodData()->has_escape_info()) { + TRACE_BCEA(2, tty->print_cr("[EA] Reading previous results for %s.%s", + method->holder()->name()->as_utf8(), + method->name()->as_utf8())); + read_escape_info(); + } else { + TRACE_BCEA(2, tty->print_cr("[EA] computing results for %s.%s", + method->holder()->name()->as_utf8(), + method->name()->as_utf8())); + + compute_escape_info(); + methodData()->update_escape_info(); + } + } +} + +void BCEscapeAnalyzer::copy_dependencies(Dependencies *deps) { + if(!has_dependencies()) + return; + for (int i = 0; i < _dependencies.length(); i+=2) { + ciKlass *k = _dependencies[i]->as_klass(); + ciMethod *m = _dependencies[i+1]->as_method(); + deps->assert_unique_concrete_method(k, m); + } +} diff --git a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.hpp b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.hpp new file mode 100644 index 00000000000..4f9802bfa51 --- /dev/null +++ b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.hpp @@ -0,0 +1,145 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +define_array(ciObjectArray, ciObject*); +define_stack(ciObjectList, ciObjectArray); + +// This class implements a fast, conservative analysis of effect of methods +// on the escape state of their arguments. The analysis is at the bytecode +// level. + +class ciMethodBlocks; +class ciBlock; + +class BCEscapeAnalyzer : public ResourceObj { + private: + bool _conservative; // If true, return maximally + // conservative results. + ciMethod* _method; + ciMethodData* _methodData; + int _arg_size; + + intStack _stack; + + BitMap _arg_local; + BitMap _arg_stack; + BitMap _arg_returned; + BitMap _dirty; + + bool _return_local; + bool _allocated_escapes; + bool _return_allocated; + + ciObjectList _dependencies; + + ciMethodBlocks *_methodBlocks; + + BCEscapeAnalyzer* _parent; + int _level; + + class ArgumentMap; + class StateInfo; + + // helper functions + bool is_argument(int i) { return i >= 0 && i < _arg_size; } + + void raw_push(int i) { _stack.push(i); } + int raw_pop() { return _stack.is_empty() ? -1 : _stack.pop(); } + void apush(int i) { raw_push(i); } + void spush() { raw_push(-1); } + void lpush() { spush(); spush(); } + int apop() { return raw_pop(); } + void spop() { assert(_stack.is_empty() || _stack.top() == -1, ""); raw_pop(); } + void lpop() { spop(); spop(); } + + void set_returned(ArgumentMap vars); + bool is_argument(ArgumentMap vars); + bool is_arg_stack(ArgumentMap vars); + void clear_bits(ArgumentMap vars, BitMap &bs); + void set_method_escape(ArgumentMap vars); + void set_global_escape(ArgumentMap vars); + void set_dirty(ArgumentMap vars); + + bool is_recursive_call(ciMethod* callee); + void add_dependence(ciKlass *klass, ciMethod *meth); + void propagate_dependencies(ciMethod *meth); + void invoke(StateInfo &state, Bytecodes::Code code, ciMethod* target, ciKlass* holder); + + void iterate_one_block(ciBlock *blk, StateInfo &state, GrowableArray &successors); + void iterate_blocks(Arena *); + void merge_block_states(StateInfo *blockstates, ciBlock *dest, StateInfo *s_state); + + // analysis + void initialize(); + void clear_escape_info(); + void compute_escape_info(); + vmIntrinsics::ID known_intrinsic(); + bool compute_escape_for_intrinsic(vmIntrinsics::ID iid); + bool do_analysis(); + + void read_escape_info(); + + bool contains(uint arg_set1, uint arg_set2); + + public: + BCEscapeAnalyzer(ciMethod* method, BCEscapeAnalyzer* parent = NULL); + + // accessors + ciMethod* method() const { return _method; } + ciMethodData* methodData() const { return _methodData; } + BCEscapeAnalyzer* parent() const { return _parent; } + int level() const { return _level; } + ciObjectList* dependencies() { return &_dependencies; } + bool has_dependencies() const { return !_dependencies.is_empty(); } + + // retrieval of interprocedural escape information + + // The given argument does not escape the callee. + bool is_arg_local(int i) const { + return !_conservative && _arg_local.at(i); + } + + // The given argument escapes the callee, but does not become globally + // reachable. + bool is_arg_stack(int i) const { + return !_conservative && _arg_stack.at(i); + } + + // The given argument does not escape globally, and may be returned. + bool is_arg_returned(int i) const { + return !_conservative && _arg_returned.at(i); } + + // True iff only input arguments are returned. + bool is_return_local() const { + return !_conservative && _return_local; + } + + // True iff only newly allocated unescaped objects are returned. + bool is_return_allocated() const { + return !_conservative && _return_allocated && !_allocated_escapes; + } + + // Copy dependencies from this analysis into "deps" + void copy_dependencies(Dependencies *deps); +}; diff --git a/hotspot/src/share/vm/ci/ciArray.cpp b/hotspot/src/share/vm/ci/ciArray.cpp new file mode 100644 index 00000000000..ced9f7efb11 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciArray.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 1999-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciArray.cpp.incl" + +// ciArray +// +// This class represents an arrayOop in the HotSpot virtual +// machine. + +// ------------------------------------------------------------------ +// ciArray::print_impl +// +// Implementation of the print method. +void ciArray::print_impl(outputStream* st) { + st->print(" length=%d type=", length()); + klass()->print(st); +} diff --git a/hotspot/src/share/vm/ci/ciArray.hpp b/hotspot/src/share/vm/ci/ciArray.hpp new file mode 100644 index 00000000000..3330b2b454d --- /dev/null +++ b/hotspot/src/share/vm/ci/ciArray.hpp @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciArray +// +// This class represents an arrayOop in the HotSpot virtual +// machine. +class ciArray : public ciObject { +private: + int _length; + +protected: + ciArray( arrayHandle h_a) : ciObject(h_a), _length(h_a()->length()) {} + ciArray( objArrayHandle h_a) : ciObject(h_a), _length(h_a()->length()) {} + ciArray(typeArrayHandle h_a) : ciObject(h_a), _length(h_a()->length()) {} + + ciArray(ciKlass* klass, int len) : ciObject(klass), _length(len) {} + + arrayOop get_arrayOop() { return (arrayOop)get_oop(); } + + const char* type_string() { return "ciArray"; } + + void print_impl(outputStream* st); + +public: + int length() { return _length; } + + // What kind of ciObject is this? + bool is_array() { return true; } + bool is_java_object() { return true; } +}; diff --git a/hotspot/src/share/vm/ci/ciArrayKlass.cpp b/hotspot/src/share/vm/ci/ciArrayKlass.cpp new file mode 100644 index 00000000000..c0dd0483a04 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciArrayKlass.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciArrayKlass.cpp.incl" + +// ciArrayKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part in an arrayKlass. + +// ------------------------------------------------------------------ +// ciArrayKlass::ciArrayKlass +// +// Loaded array klass. +ciArrayKlass::ciArrayKlass(KlassHandle h_k) : ciKlass(h_k) { + assert(get_Klass()->oop_is_array(), "wrong type"); + _dimension = get_arrayKlass()->dimension(); +} + +// ------------------------------------------------------------------ +// ciArrayKlass::ciArrayKlass +// +// Unloaded array klass. +ciArrayKlass::ciArrayKlass(ciSymbol* name, int dimension, ciKlass* klass) + : ciKlass(name, klass) { + _dimension = dimension; +} + +// ------------------------------------------------------------------ +// ciArrayKlass::element_type +// +// What type is obtained when this array is indexed once? +ciType* ciArrayKlass::element_type() { + if (is_type_array_klass()) { + return ciType::make(as_type_array_klass()->element_type()); + } else { + return as_obj_array_klass()->element_klass()->as_klass(); + } +} + + +// ------------------------------------------------------------------ +// ciArrayKlass::base_element_type +// +// What type is obtained when this array is indexed as many times as possible? +ciType* ciArrayKlass::base_element_type() { + if (is_type_array_klass()) { + return ciType::make(as_type_array_klass()->element_type()); + } else { + ciKlass* ek = as_obj_array_klass()->base_element_klass(); + if (ek->is_type_array_klass()) { + return ciType::make(ek->as_type_array_klass()->element_type()); + } + return ek; + } +} + + +// ------------------------------------------------------------------ +// ciArrayKlass::is_leaf_type +bool ciArrayKlass::is_leaf_type() { + if (is_type_array_klass()) { + return true; + } else { + return as_obj_array_klass()->base_element_klass()->is_leaf_type(); + } +} + + +// ------------------------------------------------------------------ +// ciArrayKlass::base_element_type +// +// What type is obtained when this array is indexed as many times as possible? +ciArrayKlass* ciArrayKlass::make(ciType* element_type) { + if (element_type->is_primitive_type()) { + return ciTypeArrayKlass::make(element_type->basic_type()); + } else { + return ciObjArrayKlass::make(element_type->as_klass()); + } +} diff --git a/hotspot/src/share/vm/ci/ciArrayKlass.hpp b/hotspot/src/share/vm/ci/ciArrayKlass.hpp new file mode 100644 index 00000000000..e1e8fa181d0 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciArrayKlass.hpp @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciArrayKlass +// +// This class, and its subclasses represent klassOops in the +// HotSpot virtual machine whose Klass part is an arrayKlass. +class ciArrayKlass : public ciKlass { +private: + jint _dimension; + +protected: + ciArrayKlass(KlassHandle h_k); + ciArrayKlass(ciSymbol* name, int dimension, ciKlass* klass); + + arrayKlass* get_arrayKlass() { + return (arrayKlass*)get_Klass(); + } + + const char* type_string() { return "ciArrayKlass"; } + +public: + jint dimension() { return _dimension; } + ciType* element_type(); // JLS calls this the "component type" + ciType* base_element_type(); // JLS calls this the "element type" + bool is_leaf_type(); // No subtypes of this array type. + + ciInstance* component_mirror() { + // This is a real field in arrayKlass, but we derive it from element_type. + return element_type()->java_mirror(); + } + + // What kind of vmObject is this? + bool is_array_klass() { return true; } + bool is_java_klass() { return true; } + + static ciArrayKlass* make(ciType* element_type); +}; diff --git a/hotspot/src/share/vm/ci/ciArrayKlassKlass.hpp b/hotspot/src/share/vm/ci/ciArrayKlassKlass.hpp new file mode 100644 index 00000000000..f3bb7a301c3 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciArrayKlassKlass.hpp @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciArrayKlassKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part in a arrayKlassKlass. +class ciArrayKlassKlass : public ciKlassKlass { +protected: + ciArrayKlassKlass(KlassHandle h_k, ciSymbol* name) + : ciKlassKlass(h_k, name) {} + + arrayKlassKlass* get_arrayKlassKlass() { + return (arrayKlassKlass*)get_Klass(); + } + + const char* type_string() { return "ciArrayKlassKlass"; } + +public: + // What kind of ciObject is this? + bool is_array_klass_klass() { return true; } +}; diff --git a/hotspot/src/share/vm/ci/ciCallProfile.hpp b/hotspot/src/share/vm/ci/ciCallProfile.hpp new file mode 100644 index 00000000000..600aeeb6186 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciCallProfile.hpp @@ -0,0 +1,75 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciCallProfile +// +// This class is used to determine the frequently called method +// at some call site +class ciCallProfile : StackObj { +private: + // Fields are initialized directly by ciMethod::call_profile_at_bci. + friend class ciMethod; + + enum { MorphismLimit = 2 }; // Max call site's morphism we care about + int _limit; // number of receivers have been determined + int _morphism; // determined call site's morphism + int _count; // # times has this call been executed + int _receiver_count[MorphismLimit + 1]; // # times receivers have been seen + ciMethod* _method[MorphismLimit + 1]; // receivers methods + ciKlass* _receiver[MorphismLimit + 1]; // receivers (exact) + + ciCallProfile() { + _limit = 0; + _morphism = 0; + _count = -1; + _receiver_count[0] = -1; + _method[0] = NULL; + _receiver[0] = NULL; + } + + void add_receiver(ciKlass* receiver, int receiver_count); + +public: + // Note: The following predicates return false for invalid profiles: + bool has_receiver(int i) { return _limit > i; } + int morphism() { return _morphism; } + + int count() { return _count; } + int receiver_count(int i) { + assert(i < _limit, "out of Call Profile MorphismLimit"); + return _receiver_count[i]; + } + float receiver_prob(int i) { + assert(i < _limit, "out of Call Profile MorphismLimit"); + return (float)_receiver_count[i]/(float)_count; + } + ciMethod* method(int i) { + assert(i < _limit, "out of Call Profile MorphismLimit"); + return _method[i]; + } + ciKlass* receiver(int i) { + assert(i < _limit, "out of Call Profile MorphismLimit"); + return _receiver[i]; + } +}; diff --git a/hotspot/src/share/vm/ci/ciClassList.hpp b/hotspot/src/share/vm/ci/ciClassList.hpp new file mode 100644 index 00000000000..f6a534477e2 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciClassList.hpp @@ -0,0 +1,114 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ciEnv; +class ciObjectFactory; +class ciConstantPoolCache; + +class ciField; +class ciConstant; +class ciFlags; +class ciExceptionHandler; +class ciCallProfile; +class ciSignature; + +class ciBytecodeStream; +class ciSignatureStream; +class ciExceptionHandlerStream; + +class ciTypeFlow; + +class ciObject; +class ciNullObject; +class ciInstance; +class ciMethod; +class ciMethodData; +class ciReceiverTypeData; // part of ciMethodData +class ciSymbol; +class ciArray; +class ciObjArray; +class ciTypeArray; +class ciType; +class ciReturnAddress; +class ciKlass; +class ciInstanceKlass; +class ciMethodKlass; +class ciSymbolKlass; +class ciArrayKlass; +class ciObjArrayKlass; +class ciTypeArrayKlass; +class ciKlassKlass; +class ciInstanceKlassKlass; +class ciArrayKlassKlass; +class ciObjArrayKlassKlass; +class ciTypeArrayKlassKlass; + +// Simulate Java Language style package-private access with +// friend declarations. +// This is a great idea but gcc and other C++ compilers give an +// error for being friends with yourself, so this macro does not +// compile on some platforms. + +// Everyone gives access to ciObjectFactory +#define CI_PACKAGE_ACCESS \ +friend class ciObjectFactory; + +// These are the packages that have access to ciEnv +// Any more access must be given explicitly. +#define CI_PACKAGE_ACCESS_TO \ +friend class ciObjectFactory; \ +friend class ciConstantPoolCache; \ +friend class ciField; \ +friend class ciConstant; \ +friend class ciFlags; \ +friend class ciExceptionHandler; \ +friend class ciCallProfile; \ +friend class ciSignature; \ +friend class ciBytecodeStream; \ +friend class ciSignatureStream; \ +friend class ciExceptionHandlerStream; \ +friend class ciObject; \ +friend class ciNullObject; \ +friend class ciInstance; \ +friend class ciMethod; \ +friend class ciMethodData; \ +friend class ciReceiverTypeData; \ +friend class ciSymbol; \ +friend class ciArray; \ +friend class ciObjArray; \ +friend class ciTypeArray; \ +friend class ciType; \ +friend class ciReturnAddress; \ +friend class ciKlass; \ +friend class ciInstanceKlass; \ +friend class ciMethodKlass; \ +friend class ciSymbolKlass; \ +friend class ciArrayKlass; \ +friend class ciObjArrayKlass; \ +friend class ciTypeArrayKlass; \ +friend class ciKlassKlass; \ +friend class ciInstanceKlassKlass; \ +friend class ciArrayKlassKlass; \ +friend class ciObjArrayKlassKlass; \ +friend class ciTypeArrayKlassKlass; diff --git a/hotspot/src/share/vm/ci/ciConstant.cpp b/hotspot/src/share/vm/ci/ciConstant.cpp new file mode 100644 index 00000000000..cd3ac2b0c39 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciConstant.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciConstant.cpp.incl" + +// ciConstant +// +// This class represents a constant value. + +// ------------------------------------------------------------------ +// ciConstant::print +void ciConstant::print() { + tty->print("print("%d", _value._int); + break; + case T_LONG: + tty->print(INT64_FORMAT, _value._long); + break; + case T_FLOAT: + tty->print("%f", _value._float); + break; + case T_DOUBLE: + tty->print("%lf", _value._double); + break; + case T_OBJECT: + case T_ARRAY: + _value._object->print(); + break; + default: + tty->print("ILLEGAL"); + break; + } + tty->print(">"); +} diff --git a/hotspot/src/share/vm/ci/ciConstant.hpp b/hotspot/src/share/vm/ci/ciConstant.hpp new file mode 100644 index 00000000000..0b7d864fb71 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciConstant.hpp @@ -0,0 +1,112 @@ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciConstant +// +// This class represents a constant value. +class ciConstant VALUE_OBJ_CLASS_SPEC { +private: + friend class ciEnv; + friend class ciField; + + BasicType _type; + union { + jint _int; + jlong _long; + jint _long_half[2]; + jfloat _float; + jdouble _double; + ciObject* _object; + } _value; + + // Implementation of the print method. + void print_impl(outputStream* st); + +public: + + ciConstant() { + _type = T_ILLEGAL; _value._long = -1; + } + ciConstant(BasicType type, jint value) { + assert(type != T_LONG && type != T_DOUBLE && type != T_FLOAT, + "using the wrong ciConstant constructor"); + _type = type; _value._int = value; + } + ciConstant(jlong value) { + _type = T_LONG; _value._long = value; + } + ciConstant(jfloat value) { + _type = T_FLOAT; _value._float = value; + } + ciConstant(jdouble value) { + _type = T_DOUBLE; _value._double = value; + } + ciConstant(BasicType type, ciObject* p) { + _type = type; _value._object = p; + } + + BasicType basic_type() const { return _type; } + + jboolean as_boolean() { + assert(basic_type() == T_BOOLEAN, "wrong type"); + return (jboolean)_value._int; + } + jchar as_char() { + assert(basic_type() == T_CHAR, "wrong type"); + return (jchar)_value._int; + } + jbyte as_byte() { + assert(basic_type() == T_BYTE, "wrong type"); + return (jbyte)_value._int; + } + jshort as_short() { + assert(basic_type() == T_SHORT, "wrong type"); + return (jshort)_value._int; + } + jint as_int() { + assert(basic_type() == T_BOOLEAN || basic_type() == T_CHAR || + basic_type() == T_BYTE || basic_type() == T_SHORT || + basic_type() == T_INT, "wrong type"); + return _value._int; + } + jlong as_long() { + assert(basic_type() == T_LONG, "wrong type"); + return _value._long; + } + jfloat as_float() { + assert(basic_type() == T_FLOAT, "wrong type"); + return _value._float; + } + jdouble as_double() { + assert(basic_type() == T_DOUBLE, "wrong type"); + return _value._double; + } + ciObject* as_object() const { + assert(basic_type() == T_OBJECT || basic_type() == T_ARRAY, "wrong type"); + return _value._object; + } + + // Debugging output + void print(); +}; diff --git a/hotspot/src/share/vm/ci/ciConstantPoolCache.cpp b/hotspot/src/share/vm/ci/ciConstantPoolCache.cpp new file mode 100644 index 00000000000..8ed6eba560d --- /dev/null +++ b/hotspot/src/share/vm/ci/ciConstantPoolCache.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciConstantPoolCache.cpp.incl" + +// ciConstantPoolCache +// +// This class caches indexed constant pool lookups. + +// ------------------------------------------------------------------ +// ciConstantPoolCache::ciConstantPoolCache +ciConstantPoolCache::ciConstantPoolCache(Arena* arena, + int expected_size) { + _elements = + new (arena) GrowableArray(arena, expected_size, 0, 0); + _keys = new (arena) GrowableArray(arena, expected_size, 0, 0); +} + +// ------------------------------------------------------------------ +// ciConstantPoolCache::get +// +// Get the entry at some index +void* ciConstantPoolCache::get(int index) { + ASSERT_IN_VM; + int pos = find(index); + if (pos >= _keys->length() || + _keys->at(pos) != index) { + // This element is not present in the cache. + return NULL; + } + return _elements->at(pos); +} + +// ------------------------------------------------------------------ +// ciConstantPoolCache::find +// +// Use binary search to find the position of this index in the cache. +// If there is no entry in the cache corresponding to this oop, return +// the position at which the index would be inserted. +int ciConstantPoolCache::find(int key) { + int min = 0; + int max = _keys->length()-1; + + while (max >= min) { + int mid = (max + min) / 2; + int value = _keys->at(mid); + if (value < key) { + min = mid + 1; + } else if (value > key) { + max = mid - 1; + } else { + return mid; + } + } + return min; +} + +// ------------------------------------------------------------------ +// ciConstantPoolCache::insert +// +// Insert a ciObject into the table at some index. +void ciConstantPoolCache::insert(int index, void* elem) { + int i; + int pos = find(index); + for (i = _keys->length()-1; i >= pos; i--) { + _keys->at_put_grow(i+1, _keys->at(i)); + _elements->at_put_grow(i+1, _elements->at(i)); + } + _keys->at_put_grow(pos, index); + _elements->at_put_grow(pos, elem); +} + +// ------------------------------------------------------------------ +// ciConstantPoolCache::print +// +// Print debugging information about the cache. +void ciConstantPoolCache::print() { + Unimplemented(); +} diff --git a/hotspot/src/share/vm/ci/ciConstantPoolCache.hpp b/hotspot/src/share/vm/ci/ciConstantPoolCache.hpp new file mode 100644 index 00000000000..dc931919db5 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciConstantPoolCache.hpp @@ -0,0 +1,47 @@ +/* + * Copyright 1999-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciConstantPoolCache +// +// The class caches indexed constant pool lookups. +// +// Usage note: this klass has nothing to do with constantPoolCacheOop. +class ciConstantPoolCache : public ResourceObj { +private: + GrowableArray* _keys; + GrowableArray* _elements; + + int find(int index); + +public: + ciConstantPoolCache(Arena* arena, int expected_size); + + // Get the element associated with some index. + void* get(int index); + + // Associate an element with an index. + void insert(int index, void* element); + + void print(); +}; diff --git a/hotspot/src/share/vm/ci/ciEnv.cpp b/hotspot/src/share/vm/ci/ciEnv.cpp new file mode 100644 index 00000000000..1d279540e11 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciEnv.cpp @@ -0,0 +1,1004 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciEnv.cpp.incl" + +// ciEnv +// +// This class is the top level broker for requests from the compiler +// to the VM. + +ciObject* ciEnv::_null_object_instance; +ciMethodKlass* ciEnv::_method_klass_instance; +ciSymbolKlass* ciEnv::_symbol_klass_instance; +ciKlassKlass* ciEnv::_klass_klass_instance; +ciInstanceKlassKlass* ciEnv::_instance_klass_klass_instance; +ciTypeArrayKlassKlass* ciEnv::_type_array_klass_klass_instance; +ciObjArrayKlassKlass* ciEnv::_obj_array_klass_klass_instance; + +ciInstanceKlass* ciEnv::_ArrayStoreException; +ciInstanceKlass* ciEnv::_Class; +ciInstanceKlass* ciEnv::_ClassCastException; +ciInstanceKlass* ciEnv::_Object; +ciInstanceKlass* ciEnv::_Throwable; +ciInstanceKlass* ciEnv::_Thread; +ciInstanceKlass* ciEnv::_OutOfMemoryError; +ciInstanceKlass* ciEnv::_String; + +ciSymbol* ciEnv::_unloaded_cisymbol = NULL; +ciInstanceKlass* ciEnv::_unloaded_ciinstance_klass = NULL; +ciObjArrayKlass* ciEnv::_unloaded_ciobjarrayklass = NULL; + +jobject ciEnv::_ArrayIndexOutOfBoundsException_handle = NULL; +jobject ciEnv::_ArrayStoreException_handle = NULL; +jobject ciEnv::_ClassCastException_handle = NULL; + +#ifndef PRODUCT +static bool firstEnv = true; +#endif /* PRODUCT */ + +// ------------------------------------------------------------------ +// ciEnv::ciEnv +ciEnv::ciEnv(CompileTask* task, int system_dictionary_modification_counter) { + VM_ENTRY_MARK; + + // Set up ciEnv::current immediately, for the sake of ciObjectFactory, etc. + thread->set_env(this); + assert(ciEnv::current() == this, "sanity"); + + _oop_recorder = NULL; + _debug_info = NULL; + _dependencies = NULL; + _failure_reason = NULL; + _compilable = MethodCompilable; + _break_at_compile = false; + _compiler_data = NULL; +#ifndef PRODUCT + assert(!firstEnv, "not initialized properly"); +#endif /* !PRODUCT */ + + _system_dictionary_modification_counter = system_dictionary_modification_counter; + _num_inlined_bytecodes = 0; + assert(task == NULL || thread->task() == task, "sanity"); + _task = task; + _log = NULL; + + // Temporary buffer for creating symbols and such. + _name_buffer = NULL; + _name_buffer_len = 0; + + _arena = &_ciEnv_arena; + _factory = new (_arena) ciObjectFactory(_arena, 128); + + // Preload commonly referenced system ciObjects. + + // During VM initialization, these instances have not yet been created. + // Assertions ensure that these instances are not accessed before + // their initialization. + + assert(Universe::is_fully_initialized(), "should be complete"); + + oop o = Universe::null_ptr_exception_instance(); + assert(o != NULL, "should have been initialized"); + _NullPointerException_instance = get_object(o)->as_instance(); + o = Universe::arithmetic_exception_instance(); + assert(o != NULL, "should have been initialized"); + _ArithmeticException_instance = get_object(o)->as_instance(); + + _ArrayIndexOutOfBoundsException_instance = NULL; + _ArrayStoreException_instance = NULL; + _ClassCastException_instance = NULL; +} + +ciEnv::ciEnv(Arena* arena) { + ASSERT_IN_VM; + + // Set up ciEnv::current immediately, for the sake of ciObjectFactory, etc. + CompilerThread* current_thread = CompilerThread::current(); + assert(current_thread->env() == NULL, "must be"); + current_thread->set_env(this); + assert(ciEnv::current() == this, "sanity"); + + _oop_recorder = NULL; + _debug_info = NULL; + _dependencies = NULL; + _failure_reason = NULL; + _compilable = MethodCompilable_never; + _break_at_compile = false; + _compiler_data = NULL; +#ifndef PRODUCT + assert(firstEnv, "must be first"); + firstEnv = false; +#endif /* !PRODUCT */ + + _system_dictionary_modification_counter = 0; + _num_inlined_bytecodes = 0; + _task = NULL; + _log = NULL; + + // Temporary buffer for creating symbols and such. + _name_buffer = NULL; + _name_buffer_len = 0; + + _arena = arena; + _factory = new (_arena) ciObjectFactory(_arena, 128); + + // Preload commonly referenced system ciObjects. + + // During VM initialization, these instances have not yet been created. + // Assertions ensure that these instances are not accessed before + // their initialization. + + assert(Universe::is_fully_initialized(), "must be"); + + oop o = Universe::null_ptr_exception_instance(); + assert(o != NULL, "should have been initialized"); + _NullPointerException_instance = get_object(o)->as_instance(); + o = Universe::arithmetic_exception_instance(); + assert(o != NULL, "should have been initialized"); + _ArithmeticException_instance = get_object(o)->as_instance(); + + _ArrayIndexOutOfBoundsException_instance = NULL; + _ArrayStoreException_instance = NULL; + _ClassCastException_instance = NULL; +} + +ciEnv::~ciEnv() { + CompilerThread* current_thread = CompilerThread::current(); + current_thread->set_env(NULL); +} + +// ------------------------------------------------------------------ +// helper for lazy exception creation +ciInstance* ciEnv::get_or_create_exception(jobject& handle, symbolHandle name) { + VM_ENTRY_MARK; + if (handle == NULL) { + // Cf. universe.cpp, creation of Universe::_null_ptr_exception_instance. + klassOop k = SystemDictionary::find(name, Handle(), Handle(), THREAD); + jobject objh = NULL; + if (!HAS_PENDING_EXCEPTION && k != NULL) { + oop obj = instanceKlass::cast(k)->allocate_permanent_instance(THREAD); + if (!HAS_PENDING_EXCEPTION) + objh = JNIHandles::make_global(obj); + } + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + } else { + handle = objh; + } + } + oop obj = JNIHandles::resolve(handle); + return obj == NULL? NULL: get_object(obj)->as_instance(); +} + +// ------------------------------------------------------------------ +// ciEnv::ArrayIndexOutOfBoundsException_instance, etc. +ciInstance* ciEnv::ArrayIndexOutOfBoundsException_instance() { + if (_ArrayIndexOutOfBoundsException_instance == NULL) { + _ArrayIndexOutOfBoundsException_instance + = get_or_create_exception(_ArrayIndexOutOfBoundsException_handle, + vmSymbolHandles::java_lang_ArrayIndexOutOfBoundsException()); + } + return _ArrayIndexOutOfBoundsException_instance; +} +ciInstance* ciEnv::ArrayStoreException_instance() { + if (_ArrayStoreException_instance == NULL) { + _ArrayStoreException_instance + = get_or_create_exception(_ArrayStoreException_handle, + vmSymbolHandles::java_lang_ArrayStoreException()); + } + return _ArrayStoreException_instance; +} +ciInstance* ciEnv::ClassCastException_instance() { + if (_ClassCastException_instance == NULL) { + _ClassCastException_instance + = get_or_create_exception(_ClassCastException_handle, + vmSymbolHandles::java_lang_ClassCastException()); + } + return _ClassCastException_instance; +} + +// ------------------------------------------------------------------ +// ciEnv::get_method_from_handle +ciMethod* ciEnv::get_method_from_handle(jobject method) { + VM_ENTRY_MARK; + return get_object(JNIHandles::resolve(method))->as_method(); +} + +// ------------------------------------------------------------------ +// ciEnv::make_array +ciArray* ciEnv::make_array(GrowableArray* objects) { + VM_ENTRY_MARK; + int length = objects->length(); + objArrayOop a = oopFactory::new_system_objArray(length, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + record_out_of_memory_failure(); + return NULL; + } + for (int i = 0; i < length; i++) { + a->obj_at_put(i, objects->at(i)->get_oop()); + } + assert(a->is_perm(), ""); + return get_object(a)->as_array(); +} + + +// ------------------------------------------------------------------ +// ciEnv::array_element_offset_in_bytes +int ciEnv::array_element_offset_in_bytes(ciArray* a_h, ciObject* o_h) { + VM_ENTRY_MARK; + objArrayOop a = (objArrayOop)a_h->get_oop(); + assert(a->is_objArray(), ""); + int length = a->length(); + oop o = o_h->get_oop(); + for (int i = 0; i < length; i++) { + if (a->obj_at(i) == o) return i; + } + return -1; +} + + +// ------------------------------------------------------------------ +// ciEnv::check_klass_accessiblity +// +// Note: the logic of this method should mirror the logic of +// constantPoolOopDesc::verify_constant_pool_resolve. +bool ciEnv::check_klass_accessibility(ciKlass* accessing_klass, + klassOop resolved_klass) { + if (accessing_klass == NULL || !accessing_klass->is_loaded()) { + return true; + } + if (accessing_klass->is_obj_array()) { + accessing_klass = accessing_klass->as_obj_array_klass()->base_element_klass(); + } + if (!accessing_klass->is_instance_klass()) { + return true; + } + + if (resolved_klass->klass_part()->oop_is_objArray()) { + // Find the element klass, if this is an array. + resolved_klass = objArrayKlass::cast(resolved_klass)->bottom_klass(); + } + if (resolved_klass->klass_part()->oop_is_instance()) { + return Reflection::verify_class_access(accessing_klass->get_klassOop(), + resolved_klass, + true); + } + return true; +} + +// ------------------------------------------------------------------ +// ciEnv::get_klass_by_name_impl +ciKlass* ciEnv::get_klass_by_name_impl(ciKlass* accessing_klass, + ciSymbol* name, + bool require_local) { + ASSERT_IN_VM; + EXCEPTION_CONTEXT; + + // Now we need to check the SystemDictionary + symbolHandle sym(THREAD, name->get_symbolOop()); + if (sym->byte_at(0) == 'L' && + sym->byte_at(sym->utf8_length()-1) == ';') { + // This is a name from a signature. Strip off the trimmings. + sym = oopFactory::new_symbol_handle(sym->as_utf8()+1, + sym->utf8_length()-2, + KILL_COMPILE_ON_FATAL_(_unloaded_ciinstance_klass)); + name = get_object(sym())->as_symbol(); + } + + // Check for prior unloaded klass. The SystemDictionary's answers + // can vary over time but the compiler needs consistency. + ciKlass* unloaded_klass = check_get_unloaded_klass(accessing_klass, name); + if (unloaded_klass != NULL) { + if (require_local) return NULL; + return unloaded_klass; + } + + Handle loader(THREAD, (oop)NULL); + Handle domain(THREAD, (oop)NULL); + if (accessing_klass != NULL) { + loader = Handle(THREAD, accessing_klass->loader()); + domain = Handle(THREAD, accessing_klass->protection_domain()); + } + + // setup up the proper type to return on OOM + ciKlass* fail_type; + if (sym->byte_at(0) == '[') { + fail_type = _unloaded_ciobjarrayklass; + } else { + fail_type = _unloaded_ciinstance_klass; + } + klassOop found_klass; + if (!require_local) { + found_klass = + SystemDictionary::find_constrained_instance_or_array_klass(sym, loader, + KILL_COMPILE_ON_FATAL_(fail_type)); + } else { + found_klass = + SystemDictionary::find_instance_or_array_klass(sym, loader, domain, + KILL_COMPILE_ON_FATAL_(fail_type)); + } + + if (found_klass != NULL) { + // Found it. Build a CI handle. + return get_object(found_klass)->as_klass(); + } + + // If we fail to find an array klass, look again for its element type. + // The element type may be available either locally or via constraints. + // In either case, if we can find the element type in the system dictionary, + // we must build an array type around it. The CI requires array klasses + // to be loaded if their element klasses are loaded, except when memory + // is exhausted. + if (sym->byte_at(0) == '[' && + (sym->byte_at(1) == '[' || sym->byte_at(1) == 'L')) { + // We have an unloaded array. + // Build it on the fly if the element class exists. + symbolOop elem_sym = oopFactory::new_symbol(sym->as_utf8()+1, + sym->utf8_length()-1, + KILL_COMPILE_ON_FATAL_(fail_type)); + // Get element ciKlass recursively. + ciKlass* elem_klass = + get_klass_by_name_impl(accessing_klass, + get_object(elem_sym)->as_symbol(), + require_local); + if (elem_klass != NULL && elem_klass->is_loaded()) { + // Now make an array for it + return ciObjArrayKlass::make_impl(elem_klass); + } + } + + if (require_local) return NULL; + // Not yet loaded into the VM, or not governed by loader constraints. + // Make a CI representative for it. + return get_unloaded_klass(accessing_klass, name); +} + +// ------------------------------------------------------------------ +// ciEnv::get_klass_by_name +ciKlass* ciEnv::get_klass_by_name(ciKlass* accessing_klass, + ciSymbol* klass_name, + bool require_local) { + GUARDED_VM_ENTRY(return get_klass_by_name_impl(accessing_klass, + klass_name, + require_local);) +} + +// ------------------------------------------------------------------ +// ciEnv::get_klass_by_index_impl +// +// Implementation of get_klass_by_index. +ciKlass* ciEnv::get_klass_by_index_impl(ciInstanceKlass* accessor, + int index, + bool& is_accessible) { + assert(accessor->get_instanceKlass()->is_linked(), "must be linked before accessing constant pool"); + EXCEPTION_CONTEXT; + constantPoolHandle cpool(THREAD, accessor->get_instanceKlass()->constants()); + KlassHandle klass (THREAD, constantPoolOopDesc::klass_at_if_loaded(cpool, index)); + symbolHandle klass_name; + if (klass.is_null()) { + // The klass has not been inserted into the constant pool. + // Try to look it up by name. + { + // We have to lock the cpool to keep the oop from being resolved + // while we are accessing it. + ObjectLocker ol(cpool, THREAD); + + constantTag tag = cpool->tag_at(index); + if (tag.is_klass()) { + // The klass has been inserted into the constant pool + // very recently. + klass = KlassHandle(THREAD, cpool->resolved_klass_at(index)); + } else if (tag.is_symbol()) { + klass_name = symbolHandle(THREAD, cpool->symbol_at(index)); + } else { + assert(cpool->tag_at(index).is_unresolved_klass(), "wrong tag"); + klass_name = symbolHandle(THREAD, cpool->unresolved_klass_at(index)); + } + } + } + + if (klass.is_null()) { + // Not found in constant pool. Use the name to do the lookup. + ciKlass* k = get_klass_by_name_impl(accessor, + get_object(klass_name())->as_symbol(), + false); + // Calculate accessibility the hard way. + if (!k->is_loaded()) { + is_accessible = false; + } else if (k->loader() != accessor->loader() && + get_klass_by_name_impl(accessor, k->name(), true) == NULL) { + // Loaded only remotely. Not linked yet. + is_accessible = false; + } else { + // Linked locally, and we must also check public/private, etc. + is_accessible = check_klass_accessibility(accessor, k->get_klassOop()); + } + return k; + } + + // Check for prior unloaded klass. The SystemDictionary's answers + // can vary over time but the compiler needs consistency. + ciSymbol* name = get_object(klass()->klass_part()->name())->as_symbol(); + ciKlass* unloaded_klass = check_get_unloaded_klass(accessor, name); + if (unloaded_klass != NULL) { + is_accessible = false; + return unloaded_klass; + } + + // It is known to be accessible, since it was found in the constant pool. + is_accessible = true; + return get_object(klass())->as_klass(); +} + +// ------------------------------------------------------------------ +// ciEnv::get_klass_by_index +// +// Get a klass from the constant pool. +ciKlass* ciEnv::get_klass_by_index(ciInstanceKlass* accessor, + int index, + bool& is_accessible) { + GUARDED_VM_ENTRY(return get_klass_by_index_impl(accessor, index, is_accessible);) +} + +// ------------------------------------------------------------------ +// ciEnv::get_constant_by_index_impl +// +// Implementation of get_constant_by_index(). +ciConstant ciEnv::get_constant_by_index_impl(ciInstanceKlass* accessor, + int index) { + EXCEPTION_CONTEXT; + instanceKlass* ik_accessor = accessor->get_instanceKlass(); + assert(ik_accessor->is_linked(), "must be linked before accessing constant pool"); + constantPoolOop cpool = ik_accessor->constants(); + constantTag tag = cpool->tag_at(index); + if (tag.is_int()) { + return ciConstant(T_INT, (jint)cpool->int_at(index)); + } else if (tag.is_long()) { + return ciConstant((jlong)cpool->long_at(index)); + } else if (tag.is_float()) { + return ciConstant((jfloat)cpool->float_at(index)); + } else if (tag.is_double()) { + return ciConstant((jdouble)cpool->double_at(index)); + } else if (tag.is_string() || tag.is_unresolved_string()) { + oop string = cpool->string_at(index, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + record_out_of_memory_failure(); + return ciConstant(); + } + ciObject* constant = get_object(string); + assert (constant->is_instance(), "must be an instance, or not? "); + return ciConstant(T_OBJECT, constant); + } else if (tag.is_klass() || tag.is_unresolved_klass()) { + // 4881222: allow ldc to take a class type + bool ignore; + ciKlass* klass = get_klass_by_index_impl(accessor, index, ignore); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + record_out_of_memory_failure(); + return ciConstant(); + } + assert (klass->is_instance_klass() || klass->is_array_klass(), + "must be an instance or array klass "); + return ciConstant(T_OBJECT, klass); + } else { + ShouldNotReachHere(); + return ciConstant(); + } +} + +// ------------------------------------------------------------------ +// ciEnv::is_unresolved_string_impl +// +// Implementation of is_unresolved_string(). +bool ciEnv::is_unresolved_string_impl(instanceKlass* accessor, int index) const { + EXCEPTION_CONTEXT; + assert(accessor->is_linked(), "must be linked before accessing constant pool"); + constantPoolOop cpool = accessor->constants(); + constantTag tag = cpool->tag_at(index); + return tag.is_unresolved_string(); +} + +// ------------------------------------------------------------------ +// ciEnv::is_unresolved_klass_impl +// +// Implementation of is_unresolved_klass(). +bool ciEnv::is_unresolved_klass_impl(instanceKlass* accessor, int index) const { + EXCEPTION_CONTEXT; + assert(accessor->is_linked(), "must be linked before accessing constant pool"); + constantPoolOop cpool = accessor->constants(); + constantTag tag = cpool->tag_at(index); + return tag.is_unresolved_klass(); +} + +// ------------------------------------------------------------------ +// ciEnv::get_constant_by_index +// +// Pull a constant out of the constant pool. How appropriate. +// +// Implementation note: this query is currently in no way cached. +ciConstant ciEnv::get_constant_by_index(ciInstanceKlass* accessor, + int index) { + GUARDED_VM_ENTRY(return get_constant_by_index_impl(accessor, index); ) +} + +// ------------------------------------------------------------------ +// ciEnv::is_unresolved_string +// +// Check constant pool +// +// Implementation note: this query is currently in no way cached. +bool ciEnv::is_unresolved_string(ciInstanceKlass* accessor, + int index) const { + GUARDED_VM_ENTRY(return is_unresolved_string_impl(accessor->get_instanceKlass(), index); ) +} + +// ------------------------------------------------------------------ +// ciEnv::is_unresolved_klass +// +// Check constant pool +// +// Implementation note: this query is currently in no way cached. +bool ciEnv::is_unresolved_klass(ciInstanceKlass* accessor, + int index) const { + GUARDED_VM_ENTRY(return is_unresolved_klass_impl(accessor->get_instanceKlass(), index); ) +} + +// ------------------------------------------------------------------ +// ciEnv::get_field_by_index_impl +// +// Implementation of get_field_by_index. +// +// Implementation note: the results of field lookups are cached +// in the accessor klass. +ciField* ciEnv::get_field_by_index_impl(ciInstanceKlass* accessor, + int index) { + ciConstantPoolCache* cache = accessor->field_cache(); + if (cache == NULL) { + ciField* field = new (arena()) ciField(accessor, index); + return field; + } else { + ciField* field = (ciField*)cache->get(index); + if (field == NULL) { + field = new (arena()) ciField(accessor, index); + cache->insert(index, field); + } + return field; + } +} + +// ------------------------------------------------------------------ +// ciEnv::get_field_by_index +// +// Get a field by index from a klass's constant pool. +ciField* ciEnv::get_field_by_index(ciInstanceKlass* accessor, + int index) { + GUARDED_VM_ENTRY(return get_field_by_index_impl(accessor, index);) +} + +// ------------------------------------------------------------------ +// ciEnv::lookup_method +// +// Perform an appropriate method lookup based on accessor, holder, +// name, signature, and bytecode. +methodOop ciEnv::lookup_method(instanceKlass* accessor, + instanceKlass* holder, + symbolOop name, + symbolOop sig, + Bytecodes::Code bc) { + EXCEPTION_CONTEXT; + KlassHandle h_accessor(THREAD, accessor); + KlassHandle h_holder(THREAD, holder); + symbolHandle h_name(THREAD, name); + symbolHandle h_sig(THREAD, sig); + LinkResolver::check_klass_accessability(h_accessor, h_holder, KILL_COMPILE_ON_FATAL_(NULL)); + methodHandle dest_method; + switch (bc) { + case Bytecodes::_invokestatic: + dest_method = + LinkResolver::resolve_static_call_or_null(h_holder, h_name, h_sig, h_accessor); + break; + case Bytecodes::_invokespecial: + dest_method = + LinkResolver::resolve_special_call_or_null(h_holder, h_name, h_sig, h_accessor); + break; + case Bytecodes::_invokeinterface: + dest_method = + LinkResolver::linktime_resolve_interface_method_or_null(h_holder, h_name, h_sig, + h_accessor, true); + break; + case Bytecodes::_invokevirtual: + dest_method = + LinkResolver::linktime_resolve_virtual_method_or_null(h_holder, h_name, h_sig, + h_accessor, true); + break; + default: ShouldNotReachHere(); + } + + return dest_method(); +} + + +// ------------------------------------------------------------------ +// ciEnv::get_method_by_index_impl +ciMethod* ciEnv::get_method_by_index_impl(ciInstanceKlass* accessor, + int index, Bytecodes::Code bc) { + // Get the method's declared holder. + + assert(accessor->get_instanceKlass()->is_linked(), "must be linked before accessing constant pool"); + constantPoolHandle cpool = accessor->get_instanceKlass()->constants(); + int holder_index = cpool->klass_ref_index_at(index); + bool holder_is_accessible; + ciKlass* holder = get_klass_by_index_impl(accessor, holder_index, holder_is_accessible); + ciInstanceKlass* declared_holder = get_instance_klass_for_declared_method_holder(holder); + + // Get the method's name and signature. + int nt_index = cpool->name_and_type_ref_index_at(index); + int sig_index = cpool->signature_ref_index_at(nt_index); + symbolOop name_sym = cpool->name_ref_at(index); + symbolOop sig_sym = cpool->symbol_at(sig_index); + + if (holder_is_accessible) { // Our declared holder is loaded. + instanceKlass* lookup = declared_holder->get_instanceKlass(); + methodOop m = lookup_method(accessor->get_instanceKlass(), lookup, name_sym, sig_sym, bc); + if (m != NULL) { + // We found the method. + return get_object(m)->as_method(); + } + } + + // Either the declared holder was not loaded, or the method could + // not be found. Create a dummy ciMethod to represent the failed + // lookup. + + return get_unloaded_method(declared_holder, + get_object(name_sym)->as_symbol(), + get_object(sig_sym)->as_symbol()); +} + + +// ------------------------------------------------------------------ +// ciEnv::get_instance_klass_for_declared_method_holder +ciInstanceKlass* ciEnv::get_instance_klass_for_declared_method_holder(ciKlass* method_holder) { + // For the case of .clone(), the method holder can be a ciArrayKlass + // instead of a ciInstanceKlass. For that case simply pretend that the + // declared holder is Object.clone since that's where the call will bottom out. + // A more correct fix would trickle out through many interfaces in CI, + // requiring ciInstanceKlass* to become ciKlass* and many more places would + // require checks to make sure the expected type was found. Given that this + // only occurs for clone() the more extensive fix seems like overkill so + // instead we simply smear the array type into Object. + if (method_holder->is_instance_klass()) { + return method_holder->as_instance_klass(); + } else if (method_holder->is_array_klass()) { + return current()->Object_klass(); + } else { + ShouldNotReachHere(); + } + return NULL; +} + + + + +// ------------------------------------------------------------------ +// ciEnv::get_method_by_index +ciMethod* ciEnv::get_method_by_index(ciInstanceKlass* accessor, + int index, Bytecodes::Code bc) { + GUARDED_VM_ENTRY(return get_method_by_index_impl(accessor, index, bc);) +} + +// ------------------------------------------------------------------ +// ciEnv::name_buffer +char *ciEnv::name_buffer(int req_len) { + if (_name_buffer_len < req_len) { + if (_name_buffer == NULL) { + _name_buffer = (char*)arena()->Amalloc(sizeof(char)*req_len); + _name_buffer_len = req_len; + } else { + _name_buffer = + (char*)arena()->Arealloc(_name_buffer, _name_buffer_len, req_len); + _name_buffer_len = req_len; + } + } + return _name_buffer; +} + +// ------------------------------------------------------------------ +// ciEnv::is_in_vm +bool ciEnv::is_in_vm() { + return JavaThread::current()->thread_state() == _thread_in_vm; +} + +bool ciEnv::system_dictionary_modification_counter_changed() { + return _system_dictionary_modification_counter != SystemDictionary::number_of_modifications(); +} + +// ------------------------------------------------------------------ +// ciEnv::check_for_system_dictionary_modification +// Check for changes to the system dictionary during compilation +// class loads, evolution, breakpoints +void ciEnv::check_for_system_dictionary_modification(ciMethod* target) { + if (failing()) return; // no need for further checks + + // Dependencies must be checked when the system dictionary changes. + // If logging is enabled all violated dependences will be recorded in + // the log. In debug mode check dependencies even if the system + // dictionary hasn't changed to verify that no invalid dependencies + // were inserted. Any violated dependences in this case are dumped to + // the tty. + + bool counter_changed = system_dictionary_modification_counter_changed(); + bool test_deps = counter_changed; + DEBUG_ONLY(test_deps = true); + if (!test_deps) return; + + bool print_failures = false; + DEBUG_ONLY(print_failures = !counter_changed); + + bool keep_going = (print_failures || xtty != NULL); + + int violated = 0; + + for (Dependencies::DepStream deps(dependencies()); deps.next(); ) { + klassOop witness = deps.check_dependency(); + if (witness != NULL) { + ++violated; + if (print_failures) deps.print_dependency(witness, /*verbose=*/ true); + // If there's no log and we're not sanity-checking, we're done. + if (!keep_going) break; + } + } + + if (violated != 0) { + assert(counter_changed, "failed dependencies, but counter didn't change"); + record_failure("concurrent class loading"); + } +} + +// ------------------------------------------------------------------ +// ciEnv::register_method +void ciEnv::register_method(ciMethod* target, + int entry_bci, + CodeOffsets* offsets, + int orig_pc_offset, + CodeBuffer* code_buffer, + int frame_words, + OopMapSet* oop_map_set, + ExceptionHandlerTable* handler_table, + ImplicitExceptionTable* inc_table, + AbstractCompiler* compiler, + int comp_level, + bool has_debug_info, + bool has_unsafe_access) { + VM_ENTRY_MARK; + nmethod* nm = NULL; + { + // To prevent compile queue updates. + MutexLocker locker(MethodCompileQueue_lock, THREAD); + + // Prevent SystemDictionary::add_to_hierarchy from running + // and invalidating our dependencies until we install this method. + MutexLocker ml(Compile_lock); + + if (log() != NULL) { + // Log the dependencies which this compilation declares. + dependencies()->log_all_dependencies(); + } + + // Encode the dependencies now, so we can check them right away. + dependencies()->encode_content_bytes(); + + // Check for {class loads, evolution, breakpoints} during compilation + check_for_system_dictionary_modification(target); + + methodHandle method(THREAD, target->get_methodOop()); + + if (failing()) { + // While not a true deoptimization, it is a preemptive decompile. + methodDataOop mdo = method()->method_data(); + if (mdo != NULL) { + mdo->inc_decompile_count(); + } + + // All buffers in the CodeBuffer are allocated in the CodeCache. + // If the code buffer is created on each compile attempt + // as in C2, then it must be freed. + code_buffer->free_blob(); + return; + } + + assert(offsets->value(CodeOffsets::Deopt) != -1, "must have deopt entry"); + assert(offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry"); + + nm = nmethod::new_nmethod(method, + compile_id(), + entry_bci, + offsets, + orig_pc_offset, + debug_info(), dependencies(), code_buffer, + frame_words, oop_map_set, + handler_table, inc_table, + compiler, comp_level); + + // Free codeBlobs + code_buffer->free_blob(); + + // stress test 6243940 by immediately making the method + // non-entrant behind the system's back. This has serious + // side effects on the code cache and is not meant for + // general stress testing + if (nm != NULL && StressNonEntrant) { + MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag); + NativeJump::patch_verified_entry(nm->entry_point(), nm->verified_entry_point(), + SharedRuntime::get_handle_wrong_method_stub()); + } + + if (nm == NULL) { + // The CodeCache is full. Print out warning and disable compilation. + record_failure("code cache is full"); + UseInterpreter = true; + if (UseCompiler || AlwaysCompileLoopMethods ) { +#ifndef PRODUCT + warning("CodeCache is full. Compiler has been disabled"); + if (CompileTheWorld || ExitOnFullCodeCache) { + before_exit(JavaThread::current()); + exit_globals(); // will delete tty + vm_direct_exit(CompileTheWorld ? 0 : 1); + } +#endif + UseCompiler = false; + AlwaysCompileLoopMethods = false; + } + } else { + NOT_PRODUCT(nm->set_has_debug_info(has_debug_info); ) + nm->set_has_unsafe_access(has_unsafe_access); + + // Record successful registration. + // (Put nm into the task handle *before* publishing to the Java heap.) + if (task() != NULL) task()->set_code(nm); + + if (entry_bci == InvocationEntryBci) { +#ifdef TIERED + // If there is an old version we're done with it + nmethod* old = method->code(); + if (TraceMethodReplacement && old != NULL) { + ResourceMark rm; + char *method_name = method->name_and_sig_as_C_string(); + tty->print_cr("Replacing method %s", method_name); + } + if (old != NULL ) { + old->make_not_entrant(); + } +#endif // TIERED + if (TraceNMethodInstalls ) { + ResourceMark rm; + char *method_name = method->name_and_sig_as_C_string(); + ttyLocker ttyl; + tty->print_cr("Installing method (%d) %s ", + comp_level, + method_name); + } + // Allow the code to be executed + method->set_code(method, nm); + } else { + if (TraceNMethodInstalls ) { + ResourceMark rm; + char *method_name = method->name_and_sig_as_C_string(); + ttyLocker ttyl; + tty->print_cr("Installing osr method (%d) %s @ %d", + comp_level, + method_name, + entry_bci); + } + instanceKlass::cast(method->method_holder())->add_osr_nmethod(nm); + + } + } + } + // JVMTI -- compiled method notification (must be done outside lock) + if (nm != NULL) { + nm->post_compiled_method_load_event(); + } + +} + + +// ------------------------------------------------------------------ +// ciEnv::find_system_klass +ciKlass* ciEnv::find_system_klass(ciSymbol* klass_name) { + VM_ENTRY_MARK; + return get_klass_by_name_impl(NULL, klass_name, false); +} + +// ------------------------------------------------------------------ +// ciEnv::comp_level +int ciEnv::comp_level() { + if (task() == NULL) return CompLevel_full_optimization; + return task()->comp_level(); +} + +// ------------------------------------------------------------------ +// ciEnv::compile_id +uint ciEnv::compile_id() { + if (task() == NULL) return 0; + return task()->compile_id(); +} + +// ------------------------------------------------------------------ +// ciEnv::notice_inlined_method() +void ciEnv::notice_inlined_method(ciMethod* method) { + _num_inlined_bytecodes += method->code_size(); +} + +// ------------------------------------------------------------------ +// ciEnv::num_inlined_bytecodes() +int ciEnv::num_inlined_bytecodes() const { + return _num_inlined_bytecodes; +} + +// ------------------------------------------------------------------ +// ciEnv::record_failure() +void ciEnv::record_failure(const char* reason) { + if (log() != NULL) { + log()->elem("failure reason='%s'", reason); + } + if (_failure_reason == NULL) { + // Record the first failure reason. + _failure_reason = reason; + } +} + +// ------------------------------------------------------------------ +// ciEnv::record_method_not_compilable() +void ciEnv::record_method_not_compilable(const char* reason, bool all_tiers) { + int new_compilable = + all_tiers ? MethodCompilable_never : MethodCompilable_not_at_tier ; + + // Only note transitions to a worse state + if (new_compilable > _compilable) { + if (log() != NULL) { + if (all_tiers) { + log()->elem("method_not_compilable"); + } else { + log()->elem("method_not_compilable_at_tier"); + } + } + _compilable = new_compilable; + + // Reset failure reason; this one is more important. + _failure_reason = NULL; + record_failure(reason); + } +} + +// ------------------------------------------------------------------ +// ciEnv::record_out_of_memory_failure() +void ciEnv::record_out_of_memory_failure() { + // If memory is low, we stop compiling methods. + record_method_not_compilable("out of memory"); +} diff --git a/hotspot/src/share/vm/ci/ciEnv.hpp b/hotspot/src/share/vm/ci/ciEnv.hpp new file mode 100644 index 00000000000..2ad2b40e137 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciEnv.hpp @@ -0,0 +1,359 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class CompileTask; + +// ciEnv +// +// This class is the top level broker for requests from the compiler +// to the VM. +class ciEnv : StackObj { + CI_PACKAGE_ACCESS_TO + + friend class CompileBroker; + friend class Dependencies; // for get_object, during logging + +private: + Arena* _arena; // Alias for _ciEnv_arena except in init_shared_objects() + Arena _ciEnv_arena; + int _system_dictionary_modification_counter; + ciObjectFactory* _factory; + OopRecorder* _oop_recorder; + DebugInformationRecorder* _debug_info; + Dependencies* _dependencies; + const char* _failure_reason; + int _compilable; + bool _break_at_compile; + int _num_inlined_bytecodes; + CompileTask* _task; // faster access to CompilerThread::task + CompileLog* _log; // faster access to CompilerThread::log + void* _compiler_data; // compiler-specific stuff, if any + + char* _name_buffer; + int _name_buffer_len; + + // Distinguished instances of certain ciObjects.. + static ciObject* _null_object_instance; + static ciMethodKlass* _method_klass_instance; + static ciSymbolKlass* _symbol_klass_instance; + static ciKlassKlass* _klass_klass_instance; + static ciInstanceKlassKlass* _instance_klass_klass_instance; + static ciTypeArrayKlassKlass* _type_array_klass_klass_instance; + static ciObjArrayKlassKlass* _obj_array_klass_klass_instance; + + static ciInstanceKlass* _ArrayStoreException; + static ciInstanceKlass* _Class; + static ciInstanceKlass* _ClassCastException; + static ciInstanceKlass* _Object; + static ciInstanceKlass* _Throwable; + static ciInstanceKlass* _Thread; + static ciInstanceKlass* _OutOfMemoryError; + static ciInstanceKlass* _String; + + static ciSymbol* _unloaded_cisymbol; + static ciInstanceKlass* _unloaded_ciinstance_klass; + static ciObjArrayKlass* _unloaded_ciobjarrayklass; + + static jobject _ArrayIndexOutOfBoundsException_handle; + static jobject _ArrayStoreException_handle; + static jobject _ClassCastException_handle; + + ciInstance* _NullPointerException_instance; + ciInstance* _ArithmeticException_instance; + ciInstance* _ArrayIndexOutOfBoundsException_instance; + ciInstance* _ArrayStoreException_instance; + ciInstance* _ClassCastException_instance; + + // Look up a klass by name from a particular class loader (the accessor's). + // If require_local, result must be defined in that class loader, or NULL. + // If !require_local, a result from remote class loader may be reported, + // if sufficient class loader constraints exist such that initiating + // a class loading request from the given loader is bound to return + // the class defined in the remote loader (or throw an error). + // + // Return an unloaded klass if !require_local and no class at all is found. + // + // The CI treats a klass as loaded if it is consistently defined in + // another loader, even if it hasn't yet been loaded in all loaders + // that could potentially see it via delegation. + ciKlass* get_klass_by_name(ciKlass* accessing_klass, + ciSymbol* klass_name, + bool require_local); + + // Constant pool access. + ciKlass* get_klass_by_index(ciInstanceKlass* loading_klass, + int klass_index, + bool& is_accessible); + ciConstant get_constant_by_index(ciInstanceKlass* loading_klass, + int constant_index); + bool is_unresolved_string(ciInstanceKlass* loading_klass, + int constant_index) const; + bool is_unresolved_klass(ciInstanceKlass* loading_klass, + int constant_index) const; + ciField* get_field_by_index(ciInstanceKlass* loading_klass, + int field_index); + ciMethod* get_method_by_index(ciInstanceKlass* loading_klass, + int method_index, Bytecodes::Code bc); + + // Implementation methods for loading and constant pool access. + ciKlass* get_klass_by_name_impl(ciKlass* accessing_klass, + ciSymbol* klass_name, + bool require_local); + ciKlass* get_klass_by_index_impl(ciInstanceKlass* loading_klass, + int klass_index, + bool& is_accessible); + ciConstant get_constant_by_index_impl(ciInstanceKlass* loading_klass, + int constant_index); + bool is_unresolved_string_impl (instanceKlass* loading_klass, + int constant_index) const; + bool is_unresolved_klass_impl (instanceKlass* loading_klass, + int constant_index) const; + ciField* get_field_by_index_impl(ciInstanceKlass* loading_klass, + int field_index); + ciMethod* get_method_by_index_impl(ciInstanceKlass* loading_klass, + int method_index, Bytecodes::Code bc); + + // Helper methods + bool check_klass_accessibility(ciKlass* accessing_klass, + klassOop resolved_klassOop); + methodOop lookup_method(instanceKlass* accessor, + instanceKlass* holder, + symbolOop name, + symbolOop sig, + Bytecodes::Code bc); + + // Get a ciObject from the object factory. Ensures uniqueness + // of ciObjects. + ciObject* get_object(oop o) { + if (o == NULL) { + return _null_object_instance; + } else { + return _factory->get(o); + } + } + + ciMethod* get_method_from_handle(jobject method); + + ciInstance* get_or_create_exception(jobject& handle, symbolHandle name); + + // Get a ciMethod representing either an unfound method or + // a method with an unloaded holder. Ensures uniqueness of + // the result. + ciMethod* get_unloaded_method(ciInstanceKlass* holder, + ciSymbol* name, + ciSymbol* signature) { + return _factory->get_unloaded_method(holder, name, signature); + } + + // Get a ciKlass representing an unloaded klass. + // Ensures uniqueness of the result. + ciKlass* get_unloaded_klass(ciKlass* accessing_klass, + ciSymbol* name) { + return _factory->get_unloaded_klass(accessing_klass, name, true); + } + + // See if we already have an unloaded klass for the given name + // or return NULL if not. + ciKlass *check_get_unloaded_klass(ciKlass* accessing_klass, ciSymbol* name) { + return _factory->get_unloaded_klass(accessing_klass, name, false); + } + + // Get a ciReturnAddress corresponding to the given bci. + // Ensures uniqueness of the result. + ciReturnAddress* get_return_address(int bci) { + return _factory->get_return_address(bci); + } + + // Get a ciMethodData representing the methodData for a method + // with none. + ciMethodData* get_empty_methodData() { + return _factory->get_empty_methodData(); + } + + // General utility : get a buffer of some required length. + // Used in symbol creation. + char* name_buffer(int req_len); + + // Is this thread currently in the VM state? + static bool is_in_vm(); + + // Helper routine for determining the validity of a compilation + // with respect to concurrent class loading. + void check_for_system_dictionary_modification(ciMethod* target); + +public: + enum { + MethodCompilable, + MethodCompilable_not_at_tier, + MethodCompilable_never + }; + + ciEnv(CompileTask* task, int system_dictionary_modification_counter); + // Used only during initialization of the ci + ciEnv(Arena* arena); + ~ciEnv(); + + OopRecorder* oop_recorder() { return _oop_recorder; } + void set_oop_recorder(OopRecorder* r) { _oop_recorder = r; } + + DebugInformationRecorder* debug_info() { return _debug_info; } + void set_debug_info(DebugInformationRecorder* i) { _debug_info = i; } + + Dependencies* dependencies() { return _dependencies; } + void set_dependencies(Dependencies* d) { _dependencies = d; } + + // This is true if the compilation is not going to produce code. + // (It is reasonable to retry failed compilations.) + bool failing() { return _failure_reason != NULL; } + + // Reason this compilation is failing, such as "too many basic blocks". + const char* failure_reason() { return _failure_reason; } + + // Return state of appropriate compilability + int compilable() { return _compilable; } + + bool break_at_compile() { return _break_at_compile; } + void set_break_at_compile(bool z) { _break_at_compile = z; } + + // The compiler task which has created this env. + // May be useful to find out compile_id, comp_level, etc. + CompileTask* task() { return _task; } + // Handy forwards to the task: + int comp_level(); // task()->comp_level() + uint compile_id(); // task()->compile_id() + + // Register the result of a compilation. + void register_method(ciMethod* target, + int entry_bci, + CodeOffsets* offsets, + int orig_pc_offset, + CodeBuffer* code_buffer, + int frame_words, + OopMapSet* oop_map_set, + ExceptionHandlerTable* handler_table, + ImplicitExceptionTable* inc_table, + AbstractCompiler* compiler, + int comp_level, + bool has_debug_info = true, + bool has_unsafe_access = false); + + + // Access to certain well known ciObjects. + ciInstanceKlass* ArrayStoreException_klass() { + return _ArrayStoreException; + } + ciInstanceKlass* Class_klass() { + return _Class; + } + ciInstanceKlass* ClassCastException_klass() { + return _ClassCastException; + } + ciInstanceKlass* Object_klass() { + return _Object; + } + ciInstanceKlass* Throwable_klass() { + return _Throwable; + } + ciInstanceKlass* Thread_klass() { + return _Thread; + } + ciInstanceKlass* OutOfMemoryError_klass() { + return _OutOfMemoryError; + } + ciInstanceKlass* String_klass() { + return _String; + } + ciInstance* NullPointerException_instance() { + assert(_NullPointerException_instance != NULL, "initialization problem"); + return _NullPointerException_instance; + } + ciInstance* ArithmeticException_instance() { + assert(_ArithmeticException_instance != NULL, "initialization problem"); + return _ArithmeticException_instance; + } + + // Lazy constructors: + ciInstance* ArrayIndexOutOfBoundsException_instance(); + ciInstance* ArrayStoreException_instance(); + ciInstance* ClassCastException_instance(); + + static ciSymbol* unloaded_cisymbol() { + return _unloaded_cisymbol; + } + static ciObjArrayKlass* unloaded_ciobjarrayklass() { + return _unloaded_ciobjarrayklass; + } + static ciInstanceKlass* unloaded_ciinstance_klass() { + return _unloaded_ciinstance_klass; + } + + ciKlass* find_system_klass(ciSymbol* klass_name); + // Note: To find a class from its name string, use ciSymbol::make, + // but consider adding to vmSymbols.hpp instead. + + // Use this to make a holder for non-perm compile time constants. + // The resulting array is guaranteed to satisfy "has_encoding". + ciArray* make_array(GrowableArray* objects); + + // converts the ciKlass* representing the holder of a method into a + // ciInstanceKlass*. This is needed since the holder of a method in + // the bytecodes could be an array type. Basically this converts + // array types into java/lang/Object and other types stay as they are. + static ciInstanceKlass* get_instance_klass_for_declared_method_holder(ciKlass* klass); + + // Return the machine-level offset of o, which must be an element of a. + // This may be used to form constant-loading expressions in lieu of simpler encodings. + int array_element_offset_in_bytes(ciArray* a, ciObject* o); + + // Access to the compile-lifetime allocation arena. + Arena* arena() { return _arena; } + + // What is the current compilation environment? + static ciEnv* current() { return CompilerThread::current()->env(); } + + // Overload with current thread argument + static ciEnv* current(CompilerThread *thread) { return thread->env(); } + + // Per-compiler data. (Used by C2 to publish the Compile* pointer.) + void* compiler_data() { return _compiler_data; } + void set_compiler_data(void* x) { _compiler_data = x; } + + // Notice that a method has been inlined in the current compile; + // used only for statistics. + void notice_inlined_method(ciMethod* method); + + // Total number of bytecodes in inlined methods in this compile + int num_inlined_bytecodes() const; + + // Output stream for logging compilation info. + CompileLog* log() { return _log; } + void set_log(CompileLog* log) { _log = log; } + + // Check for changes to the system dictionary during compilation + bool system_dictionary_modification_counter_changed(); + + void record_failure(const char* reason); + void record_method_not_compilable(const char* reason, bool all_tiers = true); + void record_out_of_memory_failure(); +}; diff --git a/hotspot/src/share/vm/ci/ciExceptionHandler.cpp b/hotspot/src/share/vm/ci/ciExceptionHandler.cpp new file mode 100644 index 00000000000..209f00e5b86 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciExceptionHandler.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciExceptionHandler.cpp.incl" + +// ciExceptionHandler +// +// This class represents an exception handler for a method. + +// ------------------------------------------------------------------ +// ciExceptionHandler::catch_klass +// +// Get the exception klass that this handler catches. +ciInstanceKlass* ciExceptionHandler::catch_klass() { + assert(!is_catch_all(), "bad index"); + if (_catch_klass == NULL) { + bool will_link; + ciKlass* k = CURRENT_ENV->get_klass_by_index(_loading_klass, + _catch_klass_index, + will_link); + if (!will_link && k->is_loaded()) { + GUARDED_VM_ENTRY( + k = CURRENT_ENV->get_unloaded_klass(_loading_klass, k->name()); + ) + } + _catch_klass = k->as_instance_klass(); + } + return _catch_klass; +} + +// ------------------------------------------------------------------ +// ciExceptionHandler::print() +void ciExceptionHandler::print() { + tty->print("print(" ex_klass="); + _catch_klass->print(); + } + tty->print(">"); +} diff --git a/hotspot/src/share/vm/ci/ciExceptionHandler.hpp b/hotspot/src/share/vm/ci/ciExceptionHandler.hpp new file mode 100644 index 00000000000..0a03efc3675 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciExceptionHandler.hpp @@ -0,0 +1,75 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciExceptionHandler +// +// This class represents an exception handler for a method. +class ciExceptionHandler : public ResourceObj { +private: + friend class ciMethod; + + // The loader to be used for resolving the exception + // klass. + ciInstanceKlass* _loading_klass; + + // Handler data. + int _start; + int _limit; + int _handler_bci; + int _catch_klass_index; + + // The exception klass that this handler catches. + ciInstanceKlass* _catch_klass; + +public: + ciExceptionHandler(ciInstanceKlass* loading_klass, + int start, int limit, + int handler_bci, int klass_index) { + _loading_klass = loading_klass; + _start = start; + _limit = limit; + _handler_bci = handler_bci; + _catch_klass_index = klass_index; + _catch_klass = NULL; + } + + int start() { return _start; } + int limit() { return _limit; } + int handler_bci() { return _handler_bci; } + int catch_klass_index() { return _catch_klass_index; } + + // Get the exception klass that this handler catches. + ciInstanceKlass* catch_klass(); + + bool is_catch_all() { return catch_klass_index() == 0; } + bool is_in_range(int bci) { + return start() <= bci && bci < limit(); + } + bool catches(ciInstanceKlass *exc) { + return is_catch_all() || exc->is_subtype_of(catch_klass()); + } + bool is_rethrow() { return handler_bci() == -1; } + + void print(); +}; diff --git a/hotspot/src/share/vm/ci/ciField.cpp b/hotspot/src/share/vm/ci/ciField.cpp new file mode 100644 index 00000000000..ed66c1781c4 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciField.cpp @@ -0,0 +1,333 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciField.cpp.incl" + +// ciField +// +// This class represents the result of a field lookup in the VM. +// The lookup may not succeed, in which case the information in +// the ciField will be incomplete. + +// The ciObjectFactory cannot create circular data structures in one query. +// To avoid vicious circularities, we initialize ciField::_type to NULL +// for reference types and derive it lazily from the ciField::_signature. +// Primitive types are eagerly initialized, and basic layout queries +// can succeed without initialization, using only the BasicType of the field. + +// Notes on bootstrapping and shared CI objects: A field is shared if and +// only if it is (a) non-static and (b) declared by a shared instance klass. +// This allows non-static field lists to be cached on shared types. +// Because the _type field is lazily initialized, however, there is a +// special restriction that a shared field cannot cache an unshared type. +// This puts a small performance penalty on shared fields with unshared +// types, such as StackTraceElement[] Throwable.stackTrace. +// (Throwable is shared because ClassCastException is shared, but +// StackTraceElement is not presently shared.) + +// It is not a vicious circularity for a ciField to recursively create +// the ciSymbols necessary to represent its name and signature. +// Therefore, these items are created eagerly, and the name and signature +// of a shared field are themselves shared symbols. This somewhat +// pollutes the set of shared CI objects: It grows from 50 to 93 items, +// with all of the additional 43 being uninteresting shared ciSymbols. +// This adds at most one step to the binary search, an amount which +// decreases for complex compilation tasks. + +// ------------------------------------------------------------------ +// ciField::ciField +ciField::ciField(ciInstanceKlass* klass, int index): _known_to_link_with(NULL) { + ASSERT_IN_VM; + CompilerThread *thread = CompilerThread::current(); + + assert(ciObjectFactory::is_initialized(), "not a shared field"); + + assert(klass->get_instanceKlass()->is_linked(), "must be linked before using its constan-pool"); + + _cp_index = index; + constantPoolHandle cpool(thread, klass->get_instanceKlass()->constants()); + + // Get the field's name, signature, and type. + symbolHandle name (thread, cpool->name_ref_at(index)); + _name = ciEnv::current(thread)->get_object(name())->as_symbol(); + + int nt_index = cpool->name_and_type_ref_index_at(index); + int sig_index = cpool->signature_ref_index_at(nt_index); + symbolHandle signature (thread, cpool->symbol_at(sig_index)); + _signature = ciEnv::current(thread)->get_object(signature())->as_symbol(); + + BasicType field_type = FieldType::basic_type(signature()); + + // If the field is a pointer type, get the klass of the + // field. + if (field_type == T_OBJECT || field_type == T_ARRAY) { + bool ignore; + // This is not really a class reference; the index always refers to the + // field's type signature, as a symbol. Linkage checks do not apply. + _type = ciEnv::current(thread)->get_klass_by_index(klass, sig_index, ignore); + } else { + _type = ciType::make(field_type); + } + + _name = (ciSymbol*)ciEnv::current(thread)->get_object(name()); + + // Get the field's declared holder. + // + // Note: we actually create a ciInstanceKlass for this klass, + // even though we may not need to. + int holder_index = cpool->klass_ref_index_at(index); + bool holder_is_accessible; + ciInstanceKlass* declared_holder = + ciEnv::current(thread)->get_klass_by_index(klass, holder_index, + holder_is_accessible) + ->as_instance_klass(); + + // The declared holder of this field may not have been loaded. + // Bail out with partial field information. + if (!holder_is_accessible) { + // _cp_index and _type have already been set. + // The default values for _flags and _constant_value will suffice. + // We need values for _holder, _offset, and _is_constant, + _holder = declared_holder; + _offset = -1; + _is_constant = false; + return; + } + + instanceKlass* loaded_decl_holder = declared_holder->get_instanceKlass(); + + // Perform the field lookup. + fieldDescriptor field_desc; + klassOop canonical_holder = + loaded_decl_holder->find_field(name(), signature(), &field_desc); + if (canonical_holder == NULL) { + // Field lookup failed. Will be detected by will_link. + _holder = declared_holder; + _offset = -1; + _is_constant = false; + return; + } + + assert(canonical_holder == field_desc.field_holder(), "just checking"); + initialize_from(&field_desc); +} + +ciField::ciField(fieldDescriptor *fd): _known_to_link_with(NULL) { + ASSERT_IN_VM; + + _cp_index = -1; + + // Get the field's name, signature, and type. + ciEnv* env = CURRENT_ENV; + _name = env->get_object(fd->name())->as_symbol(); + _signature = env->get_object(fd->signature())->as_symbol(); + + BasicType field_type = fd->field_type(); + + // If the field is a pointer type, get the klass of the + // field. + if (field_type == T_OBJECT || field_type == T_ARRAY) { + _type = NULL; // must call compute_type on first access + } else { + _type = ciType::make(field_type); + } + + initialize_from(fd); + + // Either (a) it is marked shared, or else (b) we are done bootstrapping. + assert(is_shared() || ciObjectFactory::is_initialized(), + "bootstrap classes must not create & cache unshared fields"); +} + +void ciField::initialize_from(fieldDescriptor* fd) { + // Get the flags, offset, and canonical holder of the field. + _flags = ciFlags(fd->access_flags()); + _offset = fd->offset(); + _holder = CURRENT_ENV->get_object(fd->field_holder())->as_instance_klass(); + + // Check to see if the field is constant. + if (_holder->is_initialized() && + this->is_final() && this->is_static()) { + // This field just may be constant. The only cases where it will + // not be constant are: + // + // 1. The field holds a non-perm-space oop. The field is, strictly + // speaking, constant but we cannot embed non-perm-space oops into + // generated code. For the time being we need to consider the + // field to be not constant. + // 2. The field is a *special* static&final field whose value + // may change. The three examples are java.lang.System.in, + // java.lang.System.out, and java.lang.System.err. + + klassOop k = _holder->get_klassOop(); + assert( SystemDictionary::system_klass() != NULL, "Check once per vm"); + if( k == SystemDictionary::system_klass() ) { + // Check offsets for case 2: System.in, System.out, or System.err + if( _offset == java_lang_System::in_offset_in_bytes() || + _offset == java_lang_System::out_offset_in_bytes() || + _offset == java_lang_System::err_offset_in_bytes() ) { + _is_constant = false; + return; + } + } + + _is_constant = true; + switch(type()->basic_type()) { + case T_BYTE: + _constant_value = ciConstant(type()->basic_type(), k->byte_field(_offset)); + break; + case T_CHAR: + _constant_value = ciConstant(type()->basic_type(), k->char_field(_offset)); + break; + case T_SHORT: + _constant_value = ciConstant(type()->basic_type(), k->short_field(_offset)); + break; + case T_BOOLEAN: + _constant_value = ciConstant(type()->basic_type(), k->bool_field(_offset)); + break; + case T_INT: + _constant_value = ciConstant(type()->basic_type(), k->int_field(_offset)); + break; + case T_FLOAT: + _constant_value = ciConstant(k->float_field(_offset)); + break; + case T_DOUBLE: + _constant_value = ciConstant(k->double_field(_offset)); + break; + case T_LONG: + _constant_value = ciConstant(k->long_field(_offset)); + break; + case T_OBJECT: + case T_ARRAY: + { + oop o = k->obj_field(_offset); + + // A field will be "constant" if it is known always to be + // a non-null reference to an instance of a particular class, + // or to a particular array. This can happen even if the instance + // or array is not perm. In such a case, an "unloaded" ciArray + // or ciInstance is created. The compiler may be able to use + // information about the object's class (which is exact) or length. + + if (o == NULL) { + _constant_value = ciConstant(type()->basic_type(), ciNullObject::make()); + } else { + _constant_value = ciConstant(type()->basic_type(), CURRENT_ENV->get_object(o)); + assert(_constant_value.as_object() == CURRENT_ENV->get_object(o), "check interning"); + } + } + } + } else { + _is_constant = false; + } +} + +// ------------------------------------------------------------------ +// ciField::compute_type +// +// Lazily compute the type, if it is an instance klass. +ciType* ciField::compute_type() { + GUARDED_VM_ENTRY(return compute_type_impl();) +} + +ciType* ciField::compute_type_impl() { + ciKlass* type = CURRENT_ENV->get_klass_by_name_impl(_holder, _signature, false); + if (!type->is_primitive_type() && is_shared()) { + // We must not cache a pointer to an unshared type, in a shared field. + bool type_is_also_shared = false; + if (type->is_type_array_klass()) { + type_is_also_shared = true; // int[] etc. are explicitly bootstrapped + } else if (type->is_instance_klass()) { + type_is_also_shared = type->as_instance_klass()->is_shared(); + } else { + // Currently there is no 'shared' query for array types. + type_is_also_shared = !ciObjectFactory::is_initialized(); + } + if (!type_is_also_shared) + return type; // Bummer. + } + _type = type; + return type; +} + + +// ------------------------------------------------------------------ +// ciField::will_link +// +// Can a specific access to this field be made without causing +// link errors? +bool ciField::will_link(ciInstanceKlass* accessing_klass, + Bytecodes::Code bc) { + VM_ENTRY_MARK; + if (_offset == -1) { + // at creation we couldn't link to our holder so we need to + // maintain that stance, otherwise there's no safe way to use this + // ciField. + return false; + } + + if (_known_to_link_with == accessing_klass) { + return true; + } + + FieldAccessInfo result; + constantPoolHandle c_pool(THREAD, + accessing_klass->get_instanceKlass()->constants()); + LinkResolver::resolve_field(result, c_pool, _cp_index, + Bytecodes::java_code(bc), + true, false, KILL_COMPILE_ON_FATAL_(false)); + + // update the hit-cache, unless there is a problem with memory scoping: + if (accessing_klass->is_shared() || !is_shared()) + _known_to_link_with = accessing_klass; + + return true; +} + +// ------------------------------------------------------------------ +// ciField::print +void ciField::print() { + tty->print("print_name(); + tty->print("."); + _name->print_symbol(); + tty->print(" offset=%d type=", _offset); + if (_type != NULL) _type->print_name(); + else tty->print("(reference)"); + tty->print(" is_constant=%s", bool_to_str(_is_constant)); + if (_is_constant) { + tty->print(" constant_value="); + _constant_value.print(); + } + tty->print(">"); +} + +// ------------------------------------------------------------------ +// ciField::print_name_on +// +// Print the name of this field +void ciField::print_name_on(outputStream* st) { + name()->print_symbol_on(st); +} diff --git a/hotspot/src/share/vm/ci/ciField.hpp b/hotspot/src/share/vm/ci/ciField.hpp new file mode 100644 index 00000000000..2498007f506 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciField.hpp @@ -0,0 +1,168 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciField +// +// This class represents the result of a field lookup in the VM. +// The lookup may not succeed, in which case the information in +// the ciField will be incomplete. +class ciField : public ResourceObj { + CI_PACKAGE_ACCESS + friend class ciEnv; + friend class ciInstanceKlass; + friend class NonStaticFieldFiller; + +private: + ciFlags _flags; + ciInstanceKlass* _holder; + ciSymbol* _name; + ciSymbol* _signature; + ciType* _type; + int _offset; + bool _is_constant; + ciInstanceKlass* _known_to_link_with; + ciConstant _constant_value; + + // Used for will_link + int _cp_index; + + ciType* compute_type(); + ciType* compute_type_impl(); + + ciField(ciInstanceKlass* klass, int index); + ciField(fieldDescriptor* fd); + + // shared constructor code + void initialize_from(fieldDescriptor* fd); + + // The implementation of the print method. + void print_impl(outputStream* st); + +public: + ciFlags flags() { return _flags; } + + // Of which klass is this field a member? + // + // Usage note: the declared holder of a field is the class + // referenced by name in the bytecodes. The canonical holder + // is the most general class which holds the field. This + // method returns the canonical holder. The declared holder + // can be accessed via a method in ciBytecodeStream. + // + // Ex. + // class A { + // public int f = 7; + // } + // class B extends A { + // public void test() { + // System.out.println(f); + // } + // } + // + // A java compiler is permitted to compile the access to + // field f as: + // + // getfield B.f + // + // In that case the declared holder of f would be B and + // the canonical holder of f would be A. + ciInstanceKlass* holder() { return _holder; } + + // Name of this field? + ciSymbol* name() { return _name; } + + // Signature of this field? + ciSymbol* signature() { return _signature; } + + // Of what type is this field? + ciType* type() { return (_type == NULL) ? compute_type() : _type; } + + // How is this field actually stored in memory? + BasicType layout_type() { return type2field[(_type == NULL) ? T_OBJECT : _type->basic_type()]; } + + // How big is this field in memory? + int size_in_bytes() { return type2aelembytes[layout_type()]; } + + // What is the offset of this field? + int offset() { + assert(_offset >= 1, "illegal call to offset()"); + return _offset; + } + + // Same question, explicit units. (Fields are aligned to the byte level.) + int offset_in_bytes() { + return offset(); + } + + // Is this field shared? + bool is_shared() { + // non-static fields of shared holders are cached + return _holder->is_shared() && !is_static(); + } + + // Is this field a constant? + // + // Clarification: A field is considered constant if: + // 1. The field is both static and final + // 2. The canonical holder of the field has undergone + // static initialization. + // 3. If the field is an object or array, then the oop + // in question is allocated in perm space. + // 4. The field is not one of the special static/final + // non-constant fields. These are java.lang.System.in + // and java.lang.System.out. Abomination. + // + // Note: the check for case 4 is not yet implemented. + bool is_constant() { return _is_constant; } + + // Get the constant value of this field. + ciConstant constant_value() { + assert(is_constant(), "illegal call to constant_value()"); + return _constant_value; + } + + // Check for link time errors. Accessing a field from a + // certain class via a certain bytecode may or may not be legal. + // This call checks to see if an exception may be raised by + // an access of this field. + // + // Usage note: if the same field is accessed multiple times + // in the same compilation, will_link will need to be checked + // at each point of access. + bool will_link(ciInstanceKlass* accessing_klass, + Bytecodes::Code bc); + + // Java access flags + bool is_public () { return flags().is_public(); } + bool is_private () { return flags().is_private(); } + bool is_protected () { return flags().is_protected(); } + bool is_static () { return flags().is_static(); } + bool is_final () { return flags().is_final(); } + bool is_volatile () { return flags().is_volatile(); } + bool is_transient () { return flags().is_transient(); } + + // Debugging output + void print(); + void print_name_on(outputStream* st); +}; diff --git a/hotspot/src/share/vm/ci/ciFlags.cpp b/hotspot/src/share/vm/ci/ciFlags.cpp new file mode 100644 index 00000000000..5a9a4260e69 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciFlags.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciFlags.cpp.incl" + +// ciFlags +// +// This class represents klass or method flags + +// ------------------------------------------------------------------ +// ciFlags::print_klass_flags +void ciFlags::print_klass_flags(outputStream* st) { + if (is_public()) { + st->print("public"); + } else { + st->print("DEFAULT_ACCESS"); + } + + if (is_final()) { + st->print(",final"); + } + if (is_super()) { + st->print(",super"); + } + if (is_interface()) { + st->print(",interface"); + } + if (is_abstract()) { + st->print(",abstract"); + } +} + +// ------------------------------------------------------------------ +// ciFlags::print_member_flags +void ciFlags::print_member_flags(outputStream* st) { + if (is_public()) { + st->print("public"); + } else if (is_private()) { + st->print("private"); + } else if (is_protected()) { + st->print("protected"); + } else { + st->print("DEFAULT_ACCESS"); + } + + if (is_static()) { + st->print(",static"); + } + if (is_final()) { + st->print(",final"); + } + if (is_synchronized()) { + st->print(",synchronized"); + } + if (is_volatile()) { + st->print(",volatile"); + } + if (is_transient()) { + st->print(",transient"); + } + if (is_native()) { + st->print(",native"); + } + if (is_abstract()) { + st->print(",abstract"); + } + if (is_strict()) { + st->print(",strict"); + } + +} + +// ------------------------------------------------------------------ +// ciFlags::print +void ciFlags::print(outputStream* st) { + st->print(" flags=%x", _flags); +} diff --git a/hotspot/src/share/vm/ci/ciFlags.hpp b/hotspot/src/share/vm/ci/ciFlags.hpp new file mode 100644 index 00000000000..b3cda80f519 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciFlags.hpp @@ -0,0 +1,61 @@ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciFlags +// +// This class represents klass or method flags. +class ciFlags VALUE_OBJ_CLASS_SPEC { +private: + friend class ciInstanceKlass; + friend class ciField; + friend class ciMethod; + + jint _flags; + + ciFlags() { _flags = 0; } + ciFlags(AccessFlags flags) { _flags = flags.as_int(); } + +public: + // Java access flags + bool is_public () const { return (_flags & JVM_ACC_PUBLIC ) != 0; } + bool is_private () const { return (_flags & JVM_ACC_PRIVATE ) != 0; } + bool is_protected () const { return (_flags & JVM_ACC_PROTECTED ) != 0; } + bool is_static () const { return (_flags & JVM_ACC_STATIC ) != 0; } + bool is_final () const { return (_flags & JVM_ACC_FINAL ) != 0; } + bool is_synchronized() const { return (_flags & JVM_ACC_SYNCHRONIZED) != 0; } + bool is_super () const { return (_flags & JVM_ACC_SUPER ) != 0; } + bool is_volatile () const { return (_flags & JVM_ACC_VOLATILE ) != 0; } + bool is_transient () const { return (_flags & JVM_ACC_TRANSIENT ) != 0; } + bool is_native () const { return (_flags & JVM_ACC_NATIVE ) != 0; } + bool is_interface () const { return (_flags & JVM_ACC_INTERFACE ) != 0; } + bool is_abstract () const { return (_flags & JVM_ACC_ABSTRACT ) != 0; } + bool is_strict () const { return (_flags & JVM_ACC_STRICT ) != 0; } + + // Conversion + jint as_int() { return _flags; } + + void print_klass_flags(outputStream* st = tty); + void print_member_flags(outputStream* st = tty); + void print(outputStream* st = tty); +}; diff --git a/hotspot/src/share/vm/ci/ciInstance.cpp b/hotspot/src/share/vm/ci/ciInstance.cpp new file mode 100644 index 00000000000..9d07a4a6229 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciInstance.cpp @@ -0,0 +1,134 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciInstance.cpp.incl" + +// ciInstance +// +// This class represents an instanceOop in the HotSpot virtual +// machine. + +// ------------------------------------------------------------------ +// ciObject::java_mirror_type +ciType* ciInstance::java_mirror_type() { + VM_ENTRY_MARK; + oop m = get_oop(); + // Return NULL if it is not java.lang.Class. + if (m == NULL || m->klass() != SystemDictionary::class_klass()) { + return NULL; + } + // Return either a primitive type or a klass. + if (java_lang_Class::is_primitive(m)) { + return ciType::make(java_lang_Class::primitive_type(m)); + } else { + klassOop k = java_lang_Class::as_klassOop(m); + assert(k != NULL, ""); + return CURRENT_THREAD_ENV->get_object(k)->as_klass(); + } +} + +// ------------------------------------------------------------------ +// ciInstance::field_value +// +// Constant value of a field. +ciConstant ciInstance::field_value(ciField* field) { + assert(is_loaded() && + field->holder()->is_loaded() && + klass()->is_subclass_of(field->holder()), + "invalid access"); + VM_ENTRY_MARK; + ciConstant result; + oop obj = get_oop(); + assert(obj != NULL, "bad oop"); + BasicType field_btype = field->type()->basic_type(); + int offset = field->offset(); + + switch(field_btype) { + case T_BYTE: + return ciConstant(field_btype, obj->byte_field(offset)); + break; + case T_CHAR: + return ciConstant(field_btype, obj->char_field(offset)); + break; + case T_SHORT: + return ciConstant(field_btype, obj->short_field(offset)); + break; + case T_BOOLEAN: + return ciConstant(field_btype, obj->bool_field(offset)); + break; + case T_INT: + return ciConstant(field_btype, obj->int_field(offset)); + break; + case T_FLOAT: + return ciConstant(obj->float_field(offset)); + break; + case T_DOUBLE: + return ciConstant(obj->double_field(offset)); + break; + case T_LONG: + return ciConstant(obj->long_field(offset)); + break; + case T_OBJECT: + case T_ARRAY: + { + oop o = obj->obj_field(offset); + + // A field will be "constant" if it is known always to be + // a non-null reference to an instance of a particular class, + // or to a particular array. This can happen even if the instance + // or array is not perm. In such a case, an "unloaded" ciArray + // or ciInstance is created. The compiler may be able to use + // information about the object's class (which is exact) or length. + + if (o == NULL) { + return ciConstant(field_btype, ciNullObject::make()); + } else { + return ciConstant(field_btype, CURRENT_ENV->get_object(o)); + } + } + } + ShouldNotReachHere(); + // to shut up the compiler + return ciConstant(); +} + +// ------------------------------------------------------------------ +// ciInstance::field_value_by_offset +// +// Constant value of a field at the specified offset. +ciConstant ciInstance::field_value_by_offset(int field_offset) { + ciInstanceKlass* ik = klass()->as_instance_klass(); + ciField* field = ik->get_field_by_offset(field_offset, false); + return field_value(field); +} + +// ------------------------------------------------------------------ +// ciInstance::print_impl +// +// Implementation of the print method. +void ciInstance::print_impl(outputStream* st) { + st->print(" type="); + klass()->print(st); +} diff --git a/hotspot/src/share/vm/ci/ciInstance.hpp b/hotspot/src/share/vm/ci/ciInstance.hpp new file mode 100644 index 00000000000..4a4314e13da --- /dev/null +++ b/hotspot/src/share/vm/ci/ciInstance.hpp @@ -0,0 +1,61 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciInstance +// +// This class represents an instanceOop in the HotSpot virtual +// machine. This is an oop which corresponds to a non-array +// instance of java.lang.Object. +class ciInstance : public ciObject { + CI_PACKAGE_ACCESS + +protected: + ciInstance(instanceHandle h_i) : ciObject(h_i) { + assert(h_i()->is_instance(), "wrong type"); + } + + ciInstance(ciKlass* klass) : ciObject(klass) {} + + instanceOop get_instanceOop() { return (instanceOop)get_oop(); } + + const char* type_string() { return "ciInstance"; } + + void print_impl(outputStream* st); + +public: + // If this object is a java mirror, return the corresponding type. + // Otherwise, return NULL. + // (Remember that a java mirror is an instance of java.lang.Class.) + ciType* java_mirror_type(); + + // What kind of ciObject is this? + bool is_instance() { return true; } + bool is_java_object() { return true; } + + // Constant value of a field. + ciConstant field_value(ciField* field); + + // Constant value of a field at the specified offset. + ciConstant field_value_by_offset(int field_offset); +}; diff --git a/hotspot/src/share/vm/ci/ciInstanceKlass.cpp b/hotspot/src/share/vm/ci/ciInstanceKlass.cpp new file mode 100644 index 00000000000..375d5bf94b1 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciInstanceKlass.cpp @@ -0,0 +1,517 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciInstanceKlass.cpp.incl" + +// ciInstanceKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part in an instanceKlass. + +// ------------------------------------------------------------------ +// ciInstanceKlass::ciInstanceKlass +// +// Loaded instance klass. +ciInstanceKlass::ciInstanceKlass(KlassHandle h_k) : ciKlass(h_k) { + assert(get_Klass()->oop_is_instance(), "wrong type"); + instanceKlass* ik = get_instanceKlass(); + + AccessFlags access_flags = ik->access_flags(); + _flags = ciFlags(access_flags); + _has_finalizer = access_flags.has_finalizer(); + _has_subklass = ik->subklass() != NULL; + _is_initialized = ik->is_initialized(); + // Next line must follow and use the result of the previous line: + _is_linked = _is_initialized || ik->is_linked(); + _nonstatic_field_size = ik->nonstatic_field_size(); + _nonstatic_fields = NULL; // initialized lazily by compute_nonstatic_fields: + + _nof_implementors = ik->nof_implementors(); + for (int i = 0; i < implementors_limit; i++) { + _implementors[i] = NULL; // we will fill these lazily + } + + Thread *thread = Thread::current(); + if (ciObjectFactory::is_initialized()) { + _loader = JNIHandles::make_local(thread, ik->class_loader()); + _protection_domain = JNIHandles::make_local(thread, + ik->protection_domain()); + _is_shared = false; + } else { + Handle h_loader(thread, ik->class_loader()); + Handle h_protection_domain(thread, ik->protection_domain()); + _loader = JNIHandles::make_global(h_loader); + _protection_domain = JNIHandles::make_global(h_protection_domain); + _is_shared = true; + } + + // Lazy fields get filled in only upon request. + _super = NULL; + _java_mirror = NULL; + + if (is_shared()) { + if (h_k() != SystemDictionary::object_klass()) { + super(); + } + java_mirror(); + //compute_nonstatic_fields(); // done outside of constructor + } + + _field_cache = NULL; +} + +// Version for unloaded classes: +ciInstanceKlass::ciInstanceKlass(ciSymbol* name, + jobject loader, jobject protection_domain) + : ciKlass(name, ciInstanceKlassKlass::make()) +{ + assert(name->byte_at(0) != '[', "not an instance klass"); + _is_initialized = false; + _is_linked = false; + _nonstatic_field_size = -1; + _nonstatic_fields = NULL; + _nof_implementors = -1; + _loader = loader; + _protection_domain = protection_domain; + _is_shared = false; + _super = NULL; + _java_mirror = NULL; + _field_cache = NULL; +} + + + +// ------------------------------------------------------------------ +// ciInstanceKlass::compute_shared_is_initialized +bool ciInstanceKlass::compute_shared_is_initialized() { + GUARDED_VM_ENTRY( + instanceKlass* ik = get_instanceKlass(); + _is_initialized = ik->is_initialized(); + return _is_initialized; + ) +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::compute_shared_is_linked +bool ciInstanceKlass::compute_shared_is_linked() { + GUARDED_VM_ENTRY( + instanceKlass* ik = get_instanceKlass(); + _is_linked = ik->is_linked(); + return _is_linked; + ) +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::compute_shared_has_subklass +bool ciInstanceKlass::compute_shared_has_subklass() { + GUARDED_VM_ENTRY( + instanceKlass* ik = get_instanceKlass(); + _has_subklass = ik->subklass() != NULL; + return _has_subklass; + ) +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::compute_shared_nof_implementors +int ciInstanceKlass::compute_shared_nof_implementors() { + // We requery this property, since it is a very old ciObject. + GUARDED_VM_ENTRY( + instanceKlass* ik = get_instanceKlass(); + _nof_implementors = ik->nof_implementors(); + return _nof_implementors; + ) +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::loader +oop ciInstanceKlass::loader() { + ASSERT_IN_VM; + return JNIHandles::resolve(_loader); +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::loader_handle +jobject ciInstanceKlass::loader_handle() { + return _loader; +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::protection_domain +oop ciInstanceKlass::protection_domain() { + ASSERT_IN_VM; + return JNIHandles::resolve(_protection_domain); +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::protection_domain_handle +jobject ciInstanceKlass::protection_domain_handle() { + return _protection_domain; +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::field_cache +// +// Get the field cache associated with this klass. +ciConstantPoolCache* ciInstanceKlass::field_cache() { + if (is_shared()) { + return NULL; + } + if (_field_cache == NULL) { + assert(!is_java_lang_Object(), "Object has no fields"); + Arena* arena = CURRENT_ENV->arena(); + _field_cache = new (arena) ciConstantPoolCache(arena, 5); + } + return _field_cache; +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::get_canonical_holder +// +ciInstanceKlass* ciInstanceKlass::get_canonical_holder(int offset) { + #ifdef ASSERT + if (!(offset >= 0 && offset < layout_helper())) { + tty->print("*** get_canonical_holder(%d) on ", offset); + this->print(); + tty->print_cr(" ***"); + }; + assert(offset >= 0 && offset < layout_helper(), "offset must be tame"); + #endif + + if (offset < (instanceOopDesc::header_size() * wordSize)) { + // All header offsets belong properly to java/lang/Object. + return CURRENT_ENV->Object_klass(); + } + + ciInstanceKlass* self = this; + for (;;) { + assert(self->is_loaded(), "must be loaded to have size"); + ciInstanceKlass* super = self->super(); + if (super == NULL || !super->contains_field_offset(offset)) { + return self; + } else { + self = super; // return super->get_canonical_holder(offset) + } + } +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::is_java_lang_Object +// +// Is this klass java.lang.Object? +bool ciInstanceKlass::is_java_lang_Object() { + return equals(CURRENT_ENV->Object_klass()); +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::uses_default_loader +bool ciInstanceKlass::uses_default_loader() { + VM_ENTRY_MARK; + return loader() == NULL; +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::print_impl +// +// Implementation of the print method. +void ciInstanceKlass::print_impl(outputStream* st) { + ciKlass::print_impl(st); + GUARDED_VM_ENTRY(st->print(" loader=0x%x", (address)loader());) + if (is_loaded()) { + st->print(" loaded=true initialized=%s finalized=%s subklass=%s size=%d flags=", + bool_to_str(is_initialized()), + bool_to_str(has_finalizer()), + bool_to_str(has_subklass()), + layout_helper()); + + _flags.print_klass_flags(); + + if (_super) { + st->print(" super="); + _super->print_name(); + } + if (_java_mirror) { + st->print(" mirror=PRESENT"); + } + } else { + st->print(" loaded=false"); + } +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::super +// +// Get the superklass of this klass. +ciInstanceKlass* ciInstanceKlass::super() { + assert(is_loaded(), "must be loaded"); + if (_super == NULL && !is_java_lang_Object()) { + GUARDED_VM_ENTRY( + klassOop super_klass = get_instanceKlass()->super(); + _super = CURRENT_ENV->get_object(super_klass)->as_instance_klass(); + ) + } + return _super; +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::java_mirror +// +// Get the instance of java.lang.Class corresponding to this klass. +ciInstance* ciInstanceKlass::java_mirror() { + assert(is_loaded(), "must be loaded"); + if (_java_mirror == NULL) { + _java_mirror = ciKlass::java_mirror(); + } + return _java_mirror; +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::unique_concrete_subklass +ciInstanceKlass* ciInstanceKlass::unique_concrete_subklass() { + if (!is_loaded()) return NULL; // No change if class is not loaded + if (!is_abstract()) return NULL; // Only applies to abstract classes. + if (!has_subklass()) return NULL; // Must have at least one subklass. + VM_ENTRY_MARK; + instanceKlass* ik = get_instanceKlass(); + Klass* up = ik->up_cast_abstract(); + assert(up->oop_is_instance(), "must be instanceKlass"); + if (ik == up) { + return NULL; + } + return CURRENT_THREAD_ENV->get_object(up->as_klassOop())->as_instance_klass(); +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::has_finalizable_subclass +bool ciInstanceKlass::has_finalizable_subclass() { + if (!is_loaded()) return true; + VM_ENTRY_MARK; + return Dependencies::find_finalizable_subclass(get_instanceKlass()) != NULL; +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::get_field_by_offset +ciField* ciInstanceKlass::get_field_by_offset(int field_offset, bool is_static) { + if (!is_static) { + for (int i = 0, len = nof_nonstatic_fields(); i < len; i++) { + ciField* field = _nonstatic_fields->at(i); + int field_off = field->offset_in_bytes(); + if (field_off == field_offset) + return field; + if (field_off > field_offset) + break; + // could do binary search or check bins, but probably not worth it + } + return NULL; + } + VM_ENTRY_MARK; + instanceKlass* k = get_instanceKlass(); + fieldDescriptor fd; + if (!k->find_field_from_offset(field_offset, is_static, &fd)) { + return NULL; + } + ciField* field = new (CURRENT_THREAD_ENV->arena()) ciField(&fd); + return field; +} + +static int sort_field_by_offset(ciField** a, ciField** b) { + return (*a)->offset_in_bytes() - (*b)->offset_in_bytes(); + // (no worries about 32-bit overflow...) +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::compute_nonstatic_fields +int ciInstanceKlass::compute_nonstatic_fields() { + assert(is_loaded(), "must be loaded"); + + if (_nonstatic_fields != NULL) + return _nonstatic_fields->length(); + + // Size in bytes of my fields, including inherited fields. + // About equal to size_helper() - sizeof(oopDesc). + int fsize = nonstatic_field_size() * wordSize; + if (fsize == 0) { // easy shortcut + Arena* arena = CURRENT_ENV->arena(); + _nonstatic_fields = new (arena) GrowableArray(arena, 0, 0, NULL); + return 0; + } + assert(!is_java_lang_Object(), "bootstrap OK"); + + ciInstanceKlass* super = this->super(); + int super_fsize = 0; + int super_flen = 0; + GrowableArray* super_fields = NULL; + if (super != NULL) { + super_fsize = super->nonstatic_field_size() * wordSize; + super_flen = super->nof_nonstatic_fields(); + super_fields = super->_nonstatic_fields; + assert(super_flen == 0 || super_fields != NULL, "first get nof_fields"); + } + + // See if I am no larger than my super; if so, I can use his fields. + if (fsize == super_fsize) { + _nonstatic_fields = super_fields; + return super_fields->length(); + } + + GrowableArray* fields = NULL; + GUARDED_VM_ENTRY({ + fields = compute_nonstatic_fields_impl(super_fields); + }); + + if (fields == NULL) { + // This can happen if this class (java.lang.Class) has invisible fields. + _nonstatic_fields = super_fields; + return super_fields->length(); + } + + int flen = fields->length(); + + // Now sort them by offset, ascending. + // (In principle, they could mix with superclass fields.) + fields->sort(sort_field_by_offset); +#ifdef ASSERT + int last_offset = sizeof(oopDesc); + for (int i = 0; i < fields->length(); i++) { + ciField* field = fields->at(i); + int offset = field->offset_in_bytes(); + int size = (field->_type == NULL) ? oopSize : field->size_in_bytes(); + assert(last_offset <= offset, "no field overlap"); + if (last_offset > (int)sizeof(oopDesc)) + assert((offset - last_offset) < BytesPerLong, "no big holes"); + // Note: Two consecutive T_BYTE fields will be separated by wordSize-1 + // padding bytes if one of them is declared by a superclass. + // This is a minor inefficiency classFileParser.cpp. + last_offset = offset + size; + } + assert(last_offset <= (int)sizeof(oopDesc) + fsize, "no overflow"); +#endif + + _nonstatic_fields = fields; + return flen; +} + +GrowableArray* +ciInstanceKlass::compute_nonstatic_fields_impl(GrowableArray* + super_fields) { + ASSERT_IN_VM; + Arena* arena = CURRENT_ENV->arena(); + int flen = 0; + GrowableArray* fields = NULL; + instanceKlass* k = get_instanceKlass(); + typeArrayOop fields_array = k->fields(); + for (int pass = 0; pass <= 1; pass++) { + for (int i = 0, alen = fields_array->length(); i < alen; i += instanceKlass::next_offset) { + fieldDescriptor fd; + fd.initialize(k->as_klassOop(), i); + if (fd.is_static()) continue; + if (pass == 0) { + flen += 1; + } else { + ciField* field = new (arena) ciField(&fd); + fields->append(field); + } + } + + // Between passes, allocate the array: + if (pass == 0) { + if (flen == 0) { + return NULL; // return nothing if none are locally declared + } + if (super_fields != NULL) { + flen += super_fields->length(); + } + fields = new (arena) GrowableArray(arena, flen, 0, NULL); + if (super_fields != NULL) { + fields->appendAll(super_fields); + } + } + } + assert(fields->length() == flen, "sanity"); + return fields; +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::find_method +// +// Find a method in this klass. +ciMethod* ciInstanceKlass::find_method(ciSymbol* name, ciSymbol* signature) { + VM_ENTRY_MARK; + instanceKlass* k = get_instanceKlass(); + symbolOop name_sym = name->get_symbolOop(); + symbolOop sig_sym= signature->get_symbolOop(); + + methodOop m = k->find_method(name_sym, sig_sym); + if (m == NULL) return NULL; + + return CURRENT_THREAD_ENV->get_object(m)->as_method(); +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::is_leaf_type +bool ciInstanceKlass::is_leaf_type() { + assert(is_loaded(), "must be loaded"); + if (is_shared()) { + return is_final(); // approximately correct + } else { + return !_has_subklass && (_nof_implementors == 0); + } +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::implementor +// +// Report an implementor of this interface. +// Returns NULL if exact information is not available. +// Note that there are various races here, since my copy +// of _nof_implementors might be out of date with respect +// to results returned by instanceKlass::implementor. +// This is OK, since any dependencies we decide to assert +// will be checked later under the Compile_lock. +ciInstanceKlass* ciInstanceKlass::implementor(int n) { + if (n > implementors_limit) { + return NULL; + } + ciInstanceKlass* impl = _implementors[n]; + if (impl == NULL) { + if (_nof_implementors > implementors_limit) { + return NULL; + } + // Go into the VM to fetch the implementor. + { + VM_ENTRY_MARK; + klassOop k = get_instanceKlass()->implementor(n); + if (k != NULL) { + impl = CURRENT_THREAD_ENV->get_object(k)->as_instance_klass(); + } + } + // Memoize this result. + if (!is_shared()) { + _implementors[n] = (impl == NULL)? this: impl; + } + } else if (impl == this) { + impl = NULL; // memoized null result from a VM query + } + return impl; +} diff --git a/hotspot/src/share/vm/ci/ciInstanceKlass.hpp b/hotspot/src/share/vm/ci/ciInstanceKlass.hpp new file mode 100644 index 00000000000..3a084ab410b --- /dev/null +++ b/hotspot/src/share/vm/ci/ciInstanceKlass.hpp @@ -0,0 +1,189 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciInstanceKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is an instanceKlass. It may or may not +// be loaded. +class ciInstanceKlass : public ciKlass { + CI_PACKAGE_ACCESS + friend class ciEnv; + friend class ciMethod; + friend class ciField; + friend class ciBytecodeStream; + +private: + bool _is_shared; + + jobject _loader; + jobject _protection_domain; + + bool _is_initialized; + bool _is_linked; + bool _has_finalizer; + bool _has_subklass; + ciFlags _flags; + jint _nonstatic_field_size; + + // Lazy fields get filled in only upon request. + ciInstanceKlass* _super; + ciInstance* _java_mirror; + + ciConstantPoolCache* _field_cache; // cached map index->field + GrowableArray* _nonstatic_fields; + + enum { implementors_limit = instanceKlass::implementors_limit }; + ciInstanceKlass* _implementors[implementors_limit]; + jint _nof_implementors; + +protected: + ciInstanceKlass(KlassHandle h_k); + ciInstanceKlass(ciSymbol* name, jobject loader, jobject protection_domain); + + instanceKlass* get_instanceKlass() const { + return (instanceKlass*)get_Klass(); + } + + oop loader(); + jobject loader_handle(); + + oop protection_domain(); + jobject protection_domain_handle(); + + const char* type_string() { return "ciInstanceKlass"; } + + void print_impl(outputStream* st); + + ciConstantPoolCache* field_cache(); + + bool is_shared() { return _is_shared; } + + bool compute_shared_is_initialized(); + bool compute_shared_is_linked(); + bool compute_shared_has_subklass(); + int compute_shared_nof_implementors(); + int compute_nonstatic_fields(); + GrowableArray* compute_nonstatic_fields_impl(GrowableArray* super_fields); + +public: + // Has this klass been initialized? + bool is_initialized() { + if (_is_shared && !_is_initialized) { + return is_loaded() && compute_shared_is_initialized(); + } + return _is_initialized; + } + // Has this klass been linked? + bool is_linked() { + if (_is_shared && !_is_linked) { + return is_loaded() && compute_shared_is_linked(); + } + return _is_linked; + } + + // General klass information. + ciFlags flags() { + assert(is_loaded(), "must be loaded"); + return _flags; + } + bool has_finalizer() { + assert(is_loaded(), "must be loaded"); + return _has_finalizer; } + bool has_subklass() { + assert(is_loaded(), "must be loaded"); + if (_is_shared && !_has_subklass) { + if (flags().is_final()) { + return false; + } else { + return compute_shared_has_subklass(); + } + } + return _has_subklass; + } + jint size_helper() { + return (Klass::layout_helper_size_in_bytes(layout_helper()) + >> LogHeapWordSize); + } + jint nonstatic_field_size() { + assert(is_loaded(), "must be loaded"); + return _nonstatic_field_size; } + ciInstanceKlass* super(); + jint nof_implementors() { + assert(is_loaded(), "must be loaded"); + if (_is_shared) return compute_shared_nof_implementors(); + return _nof_implementors; + } + + ciInstanceKlass* get_canonical_holder(int offset); + ciField* get_field_by_offset(int field_offset, bool is_static); + // total number of nonstatic fields (including inherited): + int nof_nonstatic_fields() { + if (_nonstatic_fields == NULL) + return compute_nonstatic_fields(); + else + return _nonstatic_fields->length(); + } + // nth nonstatic field (presented by ascending address) + ciField* nonstatic_field_at(int i) { + assert(_nonstatic_fields != NULL, ""); + return _nonstatic_fields->at(i); + } + + ciInstanceKlass* unique_concrete_subklass(); + bool has_finalizable_subclass(); + + bool contains_field_offset(int offset) { + return (offset/wordSize) >= instanceOopDesc::header_size() + && (offset/wordSize)-instanceOopDesc::header_size() < nonstatic_field_size(); + } + + // Get the instance of java.lang.Class corresponding to + // this klass. This instance is used for locking of + // synchronized static methods of this klass. + ciInstance* java_mirror(); + + // Java access flags + bool is_public () { return flags().is_public(); } + bool is_final () { return flags().is_final(); } + bool is_super () { return flags().is_super(); } + bool is_interface () { return flags().is_interface(); } + bool is_abstract () { return flags().is_abstract(); } + + ciMethod* find_method(ciSymbol* name, ciSymbol* signature); + // Note: To find a method from name and type strings, use ciSymbol::make, + // but consider adding to vmSymbols.hpp instead. + + bool is_leaf_type(); + ciInstanceKlass* implementor(int n); + + // Is the defining class loader of this class the default loader? + bool uses_default_loader(); + + bool is_java_lang_Object(); + + // What kind of ciObject is this? + bool is_instance_klass() { return true; } + bool is_java_klass() { return true; } +}; diff --git a/hotspot/src/share/vm/ci/ciInstanceKlassKlass.cpp b/hotspot/src/share/vm/ci/ciInstanceKlassKlass.cpp new file mode 100644 index 00000000000..48d54bbac60 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciInstanceKlassKlass.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciInstanceKlassKlass.cpp.incl" + +// ciInstanceKlassKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is an instanceKlassKlass. + +// ------------------------------------------------------------------ +// ciInstanceKlassKlass::instance +// +// Return the distinguished instance of this class +ciInstanceKlassKlass* ciInstanceKlassKlass::make() { + return CURRENT_ENV->_instance_klass_klass_instance; +} diff --git a/hotspot/src/share/vm/ci/ciInstanceKlassKlass.hpp b/hotspot/src/share/vm/ci/ciInstanceKlassKlass.hpp new file mode 100644 index 00000000000..4758d37a8e5 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciInstanceKlassKlass.hpp @@ -0,0 +1,50 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciInstanceKlassKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is a instanceKlassKlass. +class ciInstanceKlassKlass : public ciKlassKlass { + CI_PACKAGE_ACCESS + +private: + ciInstanceKlassKlass(KlassHandle h_k) + : ciKlassKlass(h_k, ciSymbol::make("unique_instanceKlassKlass")) { + assert(h_k()->klass_part()->oop_is_instanceKlass(), "wrong type"); + } + + instanceKlassKlass* get_instanceKlassKlass() { + return (instanceKlassKlass*)get_Klass(); + } + + const char* type_string() { return "ciInstanceKlassKlass"; } + +public: + // What kind of ciObject is this? + bool is_instance_klass_klass() { return true; } + + // Return the distinguished ciInstanceKlassKlass instance. + static ciInstanceKlassKlass* make(); +}; diff --git a/hotspot/src/share/vm/ci/ciKlass.cpp b/hotspot/src/share/vm/ci/ciKlass.cpp new file mode 100644 index 00000000000..ac5da354422 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciKlass.cpp @@ -0,0 +1,235 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciKlass.cpp.incl" + +// ciKlass +// +// This class represents a klassOop in the HotSpot virtual +// machine. + +// ------------------------------------------------------------------ +// ciKlass::ciKlass +ciKlass::ciKlass(KlassHandle h_k) : ciType(h_k) { + assert(get_oop()->is_klass(), "wrong type"); + Klass* k = get_Klass(); + _layout_helper = k->layout_helper(); + symbolOop klass_name = k->name(); + assert(klass_name != NULL, "wrong ciKlass constructor"); + _name = CURRENT_ENV->get_object(klass_name)->as_symbol(); +} + +// ------------------------------------------------------------------ +// ciKlass::ciKlass +// +// Nameless klass variant. +ciKlass::ciKlass(KlassHandle h_k, ciSymbol* name) : ciType(h_k) { + assert(get_oop()->is_klass(), "wrong type"); + _name = name; + _layout_helper = Klass::_lh_neutral_value; +} + +// ------------------------------------------------------------------ +// ciKlass::ciKlass +// +// Unloaded klass variant. +ciKlass::ciKlass(ciSymbol* name, ciKlass* klass) : ciType(klass) { + _name = name; + _layout_helper = Klass::_lh_neutral_value; +} + +// ------------------------------------------------------------------ +// ciKlass::is_subtype_of +bool ciKlass::is_subtype_of(ciKlass* that) { + assert(is_loaded() && that->is_loaded(), "must be loaded"); + assert(is_java_klass() && that->is_java_klass(), "must be java klasses"); + // Check to see if the klasses are identical. + if (this == that) { + return true; + } + + VM_ENTRY_MARK; + Klass* this_klass = get_Klass(); + klassOop that_klass = that->get_klassOop(); + bool result = this_klass->is_subtype_of(that_klass); + + return result; +} + +// ------------------------------------------------------------------ +// ciKlass::is_subclass_of +bool ciKlass::is_subclass_of(ciKlass* that) { + assert(is_loaded() && that->is_loaded(), "must be loaded"); + assert(is_java_klass() && that->is_java_klass(), "must be java klasses"); + // Check to see if the klasses are identical. + + VM_ENTRY_MARK; + Klass* this_klass = get_Klass(); + klassOop that_klass = that->get_klassOop(); + bool result = this_klass->is_subclass_of(that_klass); + + return result; +} + +// ------------------------------------------------------------------ +// ciKlass::super_depth +juint ciKlass::super_depth() { + assert(is_loaded(), "must be loaded"); + assert(is_java_klass(), "must be java klasses"); + + VM_ENTRY_MARK; + Klass* this_klass = get_Klass(); + return this_klass->super_depth(); +} + +// ------------------------------------------------------------------ +// ciKlass::super_check_offset +juint ciKlass::super_check_offset() { + assert(is_loaded(), "must be loaded"); + assert(is_java_klass(), "must be java klasses"); + + VM_ENTRY_MARK; + Klass* this_klass = get_Klass(); + return this_klass->super_check_offset(); +} + +// ------------------------------------------------------------------ +// ciKlass::super_of_depth +ciKlass* ciKlass::super_of_depth(juint i) { + assert(is_loaded(), "must be loaded"); + assert(is_java_klass(), "must be java klasses"); + + VM_ENTRY_MARK; + Klass* this_klass = get_Klass(); + klassOop super = this_klass->primary_super_of_depth(i); + return (super != NULL) ? CURRENT_THREAD_ENV->get_object(super)->as_klass() : NULL; +} + +// ------------------------------------------------------------------ +// ciKlass::can_be_primary_super +bool ciKlass::can_be_primary_super() { + assert(is_loaded(), "must be loaded"); + assert(is_java_klass(), "must be java klasses"); + + VM_ENTRY_MARK; + Klass* this_klass = get_Klass(); + return this_klass->can_be_primary_super(); +} + +// ------------------------------------------------------------------ +// ciKlass::least_common_ancestor +// +// Get the shared parent of two klasses. +// +// Implementation note: this method currently goes "over the wall" +// and does all of the work on the VM side. It could be rewritten +// to use the super() method and do all of the work (aside from the +// lazy computation of super()) in native mode. This may be +// worthwhile if the compiler is repeatedly requesting the same lca +// computation or possibly if most of the superklasses have already +// been created as ciObjects anyway. Something to think about... +ciKlass* +ciKlass::least_common_ancestor(ciKlass* that) { + assert(is_loaded() && that->is_loaded(), "must be loaded"); + assert(is_java_klass() && that->is_java_klass(), "must be java klasses"); + // Check to see if the klasses are identical. + if (this == that) { + return this; + } + + VM_ENTRY_MARK; + Klass* this_klass = get_Klass(); + Klass* that_klass = that->get_Klass(); + Klass* lca = this_klass->LCA(that_klass); + + // Many times the LCA will be either this_klass or that_klass. + // Treat these as special cases. + if (lca == that_klass) { + return that; + } + if (this_klass == lca) { + return this; + } + + // Create the ciInstanceKlass for the lca. + ciKlass* result = + CURRENT_THREAD_ENV->get_object(lca->as_klassOop())->as_klass(); + + return result; +} + +// ------------------------------------------------------------------ +// ciKlass::find_klass +// +// Find a klass using this klass's class loader. +ciKlass* ciKlass::find_klass(ciSymbol* klass_name) { + assert(is_loaded(), "cannot find_klass through an unloaded klass"); + return CURRENT_ENV->get_klass_by_name(this, + klass_name, false); +} + +// ------------------------------------------------------------------ +// ciKlass::java_mirror +ciInstance* ciKlass::java_mirror() { + GUARDED_VM_ENTRY( + oop java_mirror = get_Klass()->java_mirror(); + return CURRENT_ENV->get_object(java_mirror)->as_instance(); + ) +} + +// ------------------------------------------------------------------ +// ciKlass::modifier_flags +jint ciKlass::modifier_flags() { + assert(is_loaded(), "not loaded"); + GUARDED_VM_ENTRY( + return get_Klass()->modifier_flags(); + ) +} + +// ------------------------------------------------------------------ +// ciKlass::access_flags +jint ciKlass::access_flags() { + assert(is_loaded(), "not loaded"); + GUARDED_VM_ENTRY( + return get_Klass()->access_flags().as_int(); + ) +} + +// ------------------------------------------------------------------ +// ciKlass::print_impl +// +// Implementation of the print method +void ciKlass::print_impl(outputStream* st) { + st->print(" name="); + print_name_on(st); +} + +// ------------------------------------------------------------------ +// ciKlass::print_name +// +// Print the name of this klass +void ciKlass::print_name_on(outputStream* st) { + name()->print_symbol_on(st); +} diff --git a/hotspot/src/share/vm/ci/ciKlass.hpp b/hotspot/src/share/vm/ci/ciKlass.hpp new file mode 100644 index 00000000000..1f2571718bc --- /dev/null +++ b/hotspot/src/share/vm/ci/ciKlass.hpp @@ -0,0 +1,119 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciKlass +// +// This class and its subclasses represent klassOops in the +// HotSpot virtual machine. In the vm, each klassOop contains an +// embedded Klass object. ciKlass is subclassed to explicitly +// represent the kind of Klass embedded in the klassOop. For +// example, a klassOop with an embedded objArrayKlass object is +// represented in the ciObject hierarchy by the class +// ciObjArrayKlass. +class ciKlass : public ciType { + CI_PACKAGE_ACCESS + friend class ciEnv; + friend class ciField; + friend class ciMethod; + friend class ciObjArrayKlass; + +private: + ciSymbol* _name; + jint _layout_helper; + +protected: + ciKlass(KlassHandle k_h, ciSymbol* name); + ciKlass(ciSymbol* name, ciKlass* klass); + + klassOop get_klassOop() const { + klassOop k = (klassOop)get_oop(); + assert(k != NULL, "illegal use of unloaded klass"); + return k; + } + + Klass* get_Klass() const { return get_klassOop()->klass_part(); } + + // Certain subklasses have an associated class loader. + virtual oop loader() { return NULL; } + virtual jobject loader_handle() { return NULL; } + + virtual oop protection_domain() { return NULL; } + virtual jobject protection_domain_handle() { return NULL; } + + const char* type_string() { return "ciKlass"; } + + void print_impl(outputStream* st); + +public: + ciKlass(KlassHandle k_h); + + // What is the name of this klass? + ciSymbol* name() { return _name; } + + // What is its layout helper value? + jint layout_helper() { return _layout_helper; } + + bool is_subtype_of(ciKlass* klass); + bool is_subclass_of(ciKlass* klass); + juint super_depth(); + juint super_check_offset(); + ciKlass* super_of_depth(juint i); + bool can_be_primary_super(); + static juint primary_super_limit() { return Klass::primary_super_limit(); } + + // Get the shared parent of two klasses. + ciKlass* least_common_ancestor(ciKlass* k); + + virtual bool is_interface() { + return false; + } + + virtual bool is_abstract() { + return false; + } + + // Does this type (array, class, interface) have no subtypes? + virtual bool is_leaf_type() { + return false; + } + + // Attempt to get a klass using this ciKlass's loader. + ciKlass* find_klass(ciSymbol* klass_name); + // Note: To find a class from its name string, use ciSymbol::make, + // but consider adding to vmSymbols.hpp instead. + + // Get the instance of java.lang.Class corresponding to this klass. + ciInstance* java_mirror(); + + // Fetch Klass::modifier_flags. + jint modifier_flags(); + + // Fetch Klass::access_flags. + jint access_flags(); + + // What kind of ciObject is this? + bool is_klass() { return true; } + + void print_name_on(outputStream* st); +}; diff --git a/hotspot/src/share/vm/ci/ciKlassKlass.cpp b/hotspot/src/share/vm/ci/ciKlassKlass.cpp new file mode 100644 index 00000000000..f4d57f95465 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciKlassKlass.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciKlassKlass.cpp.incl" + +// ciKlassKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is a klassKlass. + +// ------------------------------------------------------------------ +// ciKlassKlass::instance +// +// Return the distinguished instance of this class +ciKlassKlass* ciKlassKlass::make() { + return CURRENT_ENV->_klass_klass_instance; +} diff --git a/hotspot/src/share/vm/ci/ciKlassKlass.hpp b/hotspot/src/share/vm/ci/ciKlassKlass.hpp new file mode 100644 index 00000000000..e02833c1b90 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciKlassKlass.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciKlassKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is a klassKlass or one of its subclasses +// (instanceKlassKlass, objArrayKlassKlass, typeArrayKlassKlass). +class ciKlassKlass : public ciKlass { + CI_PACKAGE_ACCESS + +protected: + ciKlassKlass(KlassHandle h_k) + : ciKlass(h_k, ciSymbol::make("unique_klassKlass")) { + assert(h_k()->klass_part()->oop_is_klass(), "wrong type"); + } + ciKlassKlass(KlassHandle h_k, ciSymbol *name) + : ciKlass(h_k, name) {} + + klassKlass* get_klassKlass() { return (klassKlass*)get_Klass(); } + + const char* type_string() { return "ciKlassKlass"; } + +public: + // What kind of ciObject is this? + bool is_klass_klass() { return true; } + + // Return the distinguished ciKlassKlass instance. + static ciKlassKlass* make(); +}; diff --git a/hotspot/src/share/vm/ci/ciMethod.cpp b/hotspot/src/share/vm/ci/ciMethod.cpp new file mode 100644 index 00000000000..f527b1fd6e6 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciMethod.cpp @@ -0,0 +1,1042 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciMethod.cpp.incl" + +// ciMethod +// +// This class represents a methodOop in the HotSpot virtual +// machine. + + +// ------------------------------------------------------------------ +// ciMethod::ciMethod +// +// Loaded method. +ciMethod::ciMethod(methodHandle h_m) : ciObject(h_m) { + assert(h_m() != NULL, "no null method"); + + // These fields are always filled in in loaded methods. + _flags = ciFlags(h_m()->access_flags()); + + // Easy to compute, so fill them in now. + _max_stack = h_m()->max_stack(); + _max_locals = h_m()->max_locals(); + _code_size = h_m()->code_size(); + _intrinsic_id = h_m()->intrinsic_id(); + _handler_count = h_m()->exception_table()->length() / 4; + _uses_monitors = h_m()->access_flags().has_monitor_bytecodes(); + _balanced_monitors = !_uses_monitors || h_m()->access_flags().is_monitor_matching(); + _is_compilable = !h_m()->is_not_compilable(); + // Lazy fields, filled in on demand. Require allocation. + _code = NULL; + _exception_handlers = NULL; + _liveness = NULL; + _bcea = NULL; + _method_blocks = NULL; +#ifdef COMPILER2 + _flow = NULL; +#endif // COMPILER2 + + if (JvmtiExport::can_hotswap_or_post_breakpoint() && _is_compilable) { + // 6328518 check hotswap conditions under the right lock. + MutexLocker locker(Compile_lock); + if (Dependencies::check_evol_method(h_m()) != NULL) { + _is_compilable = false; + } + } else { + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } + + if (instanceKlass::cast(h_m()->method_holder())->is_linked()) { + _can_be_statically_bound = h_m()->can_be_statically_bound(); + } else { + // Have to use a conservative value in this case. + _can_be_statically_bound = false; + } + + // Adjust the definition of this condition to be more useful: + // %%% take these conditions into account in vtable generation + if (!_can_be_statically_bound && h_m()->is_private()) + _can_be_statically_bound = true; + if (_can_be_statically_bound && h_m()->is_abstract()) + _can_be_statically_bound = false; + + ciEnv *env = CURRENT_ENV; + // generating _signature may allow GC and therefore move m. + // These fields are always filled in. + _name = env->get_object(h_m()->name())->as_symbol(); + _holder = env->get_object(h_m()->method_holder())->as_instance_klass(); + ciSymbol* sig_symbol = env->get_object(h_m()->signature())->as_symbol(); + _signature = new (env->arena()) ciSignature(_holder, sig_symbol); + _method_data = NULL; + // Take a snapshot of these values, so they will be commensurate with the MDO. + if (ProfileInterpreter) { + int invcnt = h_m()->interpreter_invocation_count(); + // if the value overflowed report it as max int + _interpreter_invocation_count = invcnt < 0 ? max_jint : invcnt ; + _interpreter_throwout_count = h_m()->interpreter_throwout_count(); + } else { + _interpreter_invocation_count = 0; + _interpreter_throwout_count = 0; + } + if (_interpreter_invocation_count == 0) + _interpreter_invocation_count = 1; +} + + +// ------------------------------------------------------------------ +// ciMethod::ciMethod +// +// Unloaded method. +ciMethod::ciMethod(ciInstanceKlass* holder, + ciSymbol* name, + ciSymbol* signature) : ciObject(ciMethodKlass::make()) { + // These fields are always filled in. + _name = name; + _holder = holder; + _signature = new (CURRENT_ENV->arena()) ciSignature(_holder, signature); + _intrinsic_id = vmIntrinsics::_none; + _liveness = NULL; + _can_be_statically_bound = false; + _bcea = NULL; + _method_blocks = NULL; + _method_data = NULL; +#ifdef COMPILER2 + _flow = NULL; +#endif // COMPILER2 +} + + +// ------------------------------------------------------------------ +// ciMethod::load_code +// +// Load the bytecodes and exception handler table for this method. +void ciMethod::load_code() { + VM_ENTRY_MARK; + assert(is_loaded(), "only loaded methods have code"); + + methodOop me = get_methodOop(); + Arena* arena = CURRENT_THREAD_ENV->arena(); + + // Load the bytecodes. + _code = (address)arena->Amalloc(code_size()); + memcpy(_code, me->code_base(), code_size()); + + // Revert any breakpoint bytecodes in ci's copy + if (_is_compilable && me->number_of_breakpoints() > 0) { + BreakpointInfo* bp = instanceKlass::cast(me->method_holder())->breakpoints(); + for (; bp != NULL; bp = bp->next()) { + if (bp->match(me)) { + code_at_put(bp->bci(), bp->orig_bytecode()); + } + } + } + + // And load the exception table. + typeArrayOop exc_table = me->exception_table(); + + // Allocate one extra spot in our list of exceptions. This + // last entry will be used to represent the possibility that + // an exception escapes the method. See ciExceptionHandlerStream + // for details. + _exception_handlers = + (ciExceptionHandler**)arena->Amalloc(sizeof(ciExceptionHandler*) + * (_handler_count + 1)); + if (_handler_count > 0) { + for (int i=0; i<_handler_count; i++) { + int base = i*4; + _exception_handlers[i] = new (arena) ciExceptionHandler( + holder(), + /* start */ exc_table->int_at(base), + /* limit */ exc_table->int_at(base+1), + /* goto pc */ exc_table->int_at(base+2), + /* cp index */ exc_table->int_at(base+3)); + } + } + + // Put an entry at the end of our list to represent the possibility + // of exceptional exit. + _exception_handlers[_handler_count] = + new (arena) ciExceptionHandler(holder(), 0, code_size(), -1, 0); + + if (CIPrintMethodCodes) { + print_codes(); + } +} + + +// ------------------------------------------------------------------ +// ciMethod::has_linenumber_table +// +// length unknown until decompression +bool ciMethod::has_linenumber_table() const { + check_is_loaded(); + VM_ENTRY_MARK; + return get_methodOop()->has_linenumber_table(); +} + + +// ------------------------------------------------------------------ +// ciMethod::compressed_linenumber_table +u_char* ciMethod::compressed_linenumber_table() const { + check_is_loaded(); + VM_ENTRY_MARK; + return get_methodOop()->compressed_linenumber_table(); +} + + +// ------------------------------------------------------------------ +// ciMethod::line_number_from_bci +int ciMethod::line_number_from_bci(int bci) const { + check_is_loaded(); + VM_ENTRY_MARK; + return get_methodOop()->line_number_from_bci(bci); +} + + +// ------------------------------------------------------------------ +// ciMethod::vtable_index +// +// Get the position of this method's entry in the vtable, if any. +int ciMethod::vtable_index() { + check_is_loaded(); + assert(holder()->is_linked(), "must be linked"); + VM_ENTRY_MARK; + return get_methodOop()->vtable_index(); +} + + +// ------------------------------------------------------------------ +// ciMethod::native_entry +// +// Get the address of this method's native code, if any. +address ciMethod::native_entry() { + check_is_loaded(); + assert(flags().is_native(), "must be native method"); + VM_ENTRY_MARK; + methodOop method = get_methodOop(); + address entry = method->native_function(); + assert(entry != NULL, "must be valid entry point"); + return entry; +} + + +// ------------------------------------------------------------------ +// ciMethod::interpreter_entry +// +// Get the entry point for running this method in the interpreter. +address ciMethod::interpreter_entry() { + check_is_loaded(); + VM_ENTRY_MARK; + methodHandle mh(THREAD, get_methodOop()); + return Interpreter::entry_for_method(mh); +} + + +// ------------------------------------------------------------------ +// ciMethod::uses_balanced_monitors +// +// Does this method use monitors in a strict stack-disciplined manner? +bool ciMethod::has_balanced_monitors() { + check_is_loaded(); + if (_balanced_monitors) return true; + + // Analyze the method to see if monitors are used properly. + VM_ENTRY_MARK; + methodHandle method(THREAD, get_methodOop()); + assert(method->has_monitor_bytecodes(), "should have checked this"); + + // Check to see if a previous compilation computed the + // monitor-matching analysis. + if (method->guaranteed_monitor_matching()) { + _balanced_monitors = true; + return true; + } + + { + EXCEPTION_MARK; + ResourceMark rm(THREAD); + GeneratePairingInfo gpi(method); + gpi.compute_map(CATCH); + if (!gpi.monitor_safe()) { + return false; + } + method->set_guaranteed_monitor_matching(); + _balanced_monitors = true; + } + return true; +} + + +// ------------------------------------------------------------------ +// ciMethod::get_flow_analysis +ciTypeFlow* ciMethod::get_flow_analysis() { +#ifdef COMPILER2 + if (_flow == NULL) { + ciEnv* env = CURRENT_ENV; + _flow = new (env->arena()) ciTypeFlow(env, this); + _flow->do_flow(); + } + return _flow; +#else // COMPILER2 + ShouldNotReachHere(); + return NULL; +#endif // COMPILER2 +} + + +// ------------------------------------------------------------------ +// ciMethod::get_osr_flow_analysis +ciTypeFlow* ciMethod::get_osr_flow_analysis(int osr_bci) { +#ifdef COMPILER2 + // OSR entry points are always place after a call bytecode of some sort + assert(osr_bci >= 0, "must supply valid OSR entry point"); + ciEnv* env = CURRENT_ENV; + ciTypeFlow* flow = new (env->arena()) ciTypeFlow(env, this, osr_bci); + flow->do_flow(); + return flow; +#else // COMPILER2 + ShouldNotReachHere(); + return NULL; +#endif // COMPILER2 +} + +// ------------------------------------------------------------------ +// ciMethod::liveness_at_bci +// +// Which local variables are live at a specific bci? +MethodLivenessResult ciMethod::liveness_at_bci(int bci) { + check_is_loaded(); + if (_liveness == NULL) { + // Create the liveness analyzer. + Arena* arena = CURRENT_ENV->arena(); + _liveness = new (arena) MethodLiveness(arena, this); + _liveness->compute_liveness(); + } + MethodLivenessResult result = _liveness->get_liveness_at(bci); + if (JvmtiExport::can_access_local_variables() || DeoptimizeALot || CompileTheWorld) { + // Keep all locals live for the user's edification and amusement. + result.at_put_range(0, result.size(), true); + } + return result; +} + +// ciMethod::live_local_oops_at_bci +// +// find all the live oops in the locals array for a particular bci +// Compute what the interpreter believes by using the interpreter +// oopmap generator. This is used as a double check during osr to +// guard against conservative result from MethodLiveness making us +// think a dead oop is live. MethodLiveness is conservative in the +// sense that it may consider locals to be live which cannot be live, +// like in the case where a local could contain an oop or a primitive +// along different paths. In that case the local must be dead when +// those paths merge. Since the interpreter's viewpoint is used when +// gc'ing an interpreter frame we need to use its viewpoint during +// OSR when loading the locals. + +BitMap ciMethod::live_local_oops_at_bci(int bci) { + VM_ENTRY_MARK; + InterpreterOopMap mask; + OopMapCache::compute_one_oop_map(get_methodOop(), bci, &mask); + int mask_size = max_locals(); + BitMap result(mask_size); + result.clear(); + int i; + for (i = 0; i < mask_size ; i++ ) { + if (mask.is_oop(i)) result.set_bit(i); + } + return result; +} + + +#ifdef COMPILER1 +// ------------------------------------------------------------------ +// ciMethod::bci_block_start +// +// Marks all bcis where a new basic block starts +const BitMap ciMethod::bci_block_start() { + check_is_loaded(); + if (_liveness == NULL) { + // Create the liveness analyzer. + Arena* arena = CURRENT_ENV->arena(); + _liveness = new (arena) MethodLiveness(arena, this); + _liveness->compute_liveness(); + } + + return _liveness->get_bci_block_start(); +} +#endif // COMPILER1 + + +// ------------------------------------------------------------------ +// ciMethod::call_profile_at_bci +// +// Get the ciCallProfile for the invocation of this method. +// Also reports receiver types for non-call type checks (if TypeProfileCasts). +ciCallProfile ciMethod::call_profile_at_bci(int bci) { + ResourceMark rm; + ciCallProfile result; + if (method_data() != NULL && method_data()->is_mature()) { + ciProfileData* data = method_data()->bci_to_data(bci); + if (data != NULL && data->is_CounterData()) { + // Every profiled call site has a counter. + int count = data->as_CounterData()->count(); + + if (!data->is_ReceiverTypeData()) { + result._receiver_count[0] = 0; // that's a definite zero + } else { // ReceiverTypeData is a subclass of CounterData + ciReceiverTypeData* call = (ciReceiverTypeData*)data->as_ReceiverTypeData(); + // In addition, virtual call sites have receiver type information + int receivers_count_total = 0; + int morphism = 0; + for (uint i = 0; i < call->row_limit(); i++) { + ciKlass* receiver = call->receiver(i); + if (receiver == NULL) continue; + morphism += 1; + int rcount = call->receiver_count(i); + if (rcount == 0) rcount = 1; // Should be valid value + receivers_count_total += rcount; + // Add the receiver to result data. + result.add_receiver(receiver, rcount); + // If we extend profiling to record methods, + // we will set result._method also. + } + // Determine call site's morphism. + // The call site count could be == (receivers_count_total + 1) + // not only in the case of a polymorphic call but also in the case + // when a method data snapshot is taken after the site count was updated + // but before receivers counters were updated. + if (morphism == result._limit) { + // There were no array klasses and morphism <= MorphismLimit. + if (morphism < ciCallProfile::MorphismLimit || + morphism == ciCallProfile::MorphismLimit && + (receivers_count_total+1) >= count) { + result._morphism = morphism; + } + } + // Make the count consistent if this is a call profile. If count is + // zero or less, presume that this is a typecheck profile and + // do nothing. Otherwise, increase count to be the sum of all + // receiver's counts. + if (count > 0) { + if (count < receivers_count_total) { + count = receivers_count_total; + } + } + } + result._count = count; + } + } + return result; +} + +// ------------------------------------------------------------------ +// Add new receiver and sort data by receiver's profile count. +void ciCallProfile::add_receiver(ciKlass* receiver, int receiver_count) { + // Add new receiver and sort data by receiver's counts when we have space + // for it otherwise replace the less called receiver (less called receiver + // is placed to the last array element which is not used). + // First array's element contains most called receiver. + int i = _limit; + for (; i > 0 && receiver_count > _receiver_count[i-1]; i--) { + _receiver[i] = _receiver[i-1]; + _receiver_count[i] = _receiver_count[i-1]; + } + _receiver[i] = receiver; + _receiver_count[i] = receiver_count; + if (_limit < MorphismLimit) _limit++; +} + +// ------------------------------------------------------------------ +// ciMethod::find_monomorphic_target +// +// Given a certain calling environment, find the monomorphic target +// for the call. Return NULL if the call is not monomorphic in +// its calling environment, or if there are only abstract methods. +// The returned method is never abstract. +// Note: If caller uses a non-null result, it must inform dependencies +// via assert_unique_concrete_method or assert_leaf_type. +ciMethod* ciMethod::find_monomorphic_target(ciInstanceKlass* caller, + ciInstanceKlass* callee_holder, + ciInstanceKlass* actual_recv) { + check_is_loaded(); + + if (actual_recv->is_interface()) { + // %%% We cannot trust interface types, yet. See bug 6312651. + return NULL; + } + + ciMethod* root_m = resolve_invoke(caller, actual_recv); + if (root_m == NULL) { + // Something went wrong looking up the actual receiver method. + return NULL; + } + assert(!root_m->is_abstract(), "resolve_invoke promise"); + + // Make certain quick checks even if UseCHA is false. + + // Is it private or final? + if (root_m->can_be_statically_bound()) { + return root_m; + } + + if (actual_recv->is_leaf_type() && actual_recv == root_m->holder()) { + // Easy case. There is no other place to put a method, so don't bother + // to go through the VM_ENTRY_MARK and all the rest. + return root_m; + } + + // Array methods (clone, hashCode, etc.) are always statically bound. + // If we were to see an array type here, we'd return root_m. + // However, this method processes only ciInstanceKlasses. (See 4962591.) + // The inline_native_clone intrinsic narrows Object to T[] properly, + // so there is no need to do the same job here. + + if (!UseCHA) return NULL; + + VM_ENTRY_MARK; + + methodHandle target; + { + MutexLocker locker(Compile_lock); + klassOop context = actual_recv->get_klassOop(); + target = Dependencies::find_unique_concrete_method(context, + root_m->get_methodOop()); + // %%% Should upgrade this ciMethod API to look for 1 or 2 concrete methods. + } + +#ifndef PRODUCT + if (TraceDependencies && target() != NULL && target() != root_m->get_methodOop()) { + tty->print("found a non-root unique target method"); + tty->print_cr(" context = %s", instanceKlass::cast(actual_recv->get_klassOop())->external_name()); + tty->print(" method = "); + target->print_short_name(tty); + tty->cr(); + } +#endif //PRODUCT + + if (target() == NULL) { + return NULL; + } + if (target() == root_m->get_methodOop()) { + return root_m; + } + if (!root_m->is_public() && + !root_m->is_protected()) { + // If we are going to reason about inheritance, it's easiest + // if the method in question is public, protected, or private. + // If the answer is not root_m, it is conservatively correct + // to return NULL, even if the CHA encountered irrelevant + // methods in other packages. + // %%% TO DO: Work out logic for package-private methods + // with the same name but different vtable indexes. + return NULL; + } + return CURRENT_THREAD_ENV->get_object(target())->as_method(); +} + +// ------------------------------------------------------------------ +// ciMethod::resolve_invoke +// +// Given a known receiver klass, find the target for the call. +// Return NULL if the call has no target or the target is abstract. +ciMethod* ciMethod::resolve_invoke(ciKlass* caller, ciKlass* exact_receiver) { + check_is_loaded(); + VM_ENTRY_MARK; + + KlassHandle caller_klass (THREAD, caller->get_klassOop()); + KlassHandle h_recv (THREAD, exact_receiver->get_klassOop()); + KlassHandle h_resolved (THREAD, holder()->get_klassOop()); + symbolHandle h_name (THREAD, name()->get_symbolOop()); + symbolHandle h_signature (THREAD, signature()->get_symbolOop()); + + methodHandle m; + // Only do exact lookup if receiver klass has been linked. Otherwise, + // the vtable has not been setup, and the LinkResolver will fail. + if (h_recv->oop_is_javaArray() + || + instanceKlass::cast(h_recv())->is_linked() && !exact_receiver->is_interface()) { + if (holder()->is_interface()) { + m = LinkResolver::resolve_interface_call_or_null(h_recv, h_resolved, h_name, h_signature, caller_klass); + } else { + m = LinkResolver::resolve_virtual_call_or_null(h_recv, h_resolved, h_name, h_signature, caller_klass); + } + } + + if (m.is_null()) { + // Return NULL only if there was a problem with lookup (uninitialized class, etc.) + return NULL; + } + + ciMethod* result = this; + if (m() != get_methodOop()) { + result = CURRENT_THREAD_ENV->get_object(m())->as_method(); + } + + // Don't return abstract methods because they aren't + // optimizable or interesting. + if (result->is_abstract()) { + return NULL; + } else { + return result; + } +} + +// ------------------------------------------------------------------ +// ciMethod::resolve_vtable_index +// +// Given a known receiver klass, find the vtable index for the call. +// Return methodOopDesc::invalid_vtable_index if the vtable_index is unknown. +int ciMethod::resolve_vtable_index(ciKlass* caller, ciKlass* receiver) { + check_is_loaded(); + + int vtable_index = methodOopDesc::invalid_vtable_index; + // Only do lookup if receiver klass has been linked. Otherwise, + // the vtable has not been setup, and the LinkResolver will fail. + if (!receiver->is_interface() + && (!receiver->is_instance_klass() || + receiver->as_instance_klass()->is_linked())) { + VM_ENTRY_MARK; + + KlassHandle caller_klass (THREAD, caller->get_klassOop()); + KlassHandle h_recv (THREAD, receiver->get_klassOop()); + symbolHandle h_name (THREAD, name()->get_symbolOop()); + symbolHandle h_signature (THREAD, signature()->get_symbolOop()); + + vtable_index = LinkResolver::resolve_virtual_vtable_index(h_recv, h_recv, h_name, h_signature, caller_klass); + if (vtable_index == methodOopDesc::nonvirtual_vtable_index) { + // A statically bound method. Return "no such index". + vtable_index = methodOopDesc::invalid_vtable_index; + } + } + + return vtable_index; +} + +// ------------------------------------------------------------------ +// ciMethod::interpreter_call_site_count +int ciMethod::interpreter_call_site_count(int bci) { + if (method_data() != NULL) { + ResourceMark rm; + ciProfileData* data = method_data()->bci_to_data(bci); + if (data != NULL && data->is_CounterData()) { + return scale_count(data->as_CounterData()->count()); + } + } + return -1; // unknown +} + +// ------------------------------------------------------------------ +// Adjust a CounterData count to be commensurate with +// interpreter_invocation_count. If the MDO exists for +// only 25% of the time the method exists, then the +// counts in the MDO should be scaled by 4X, so that +// they can be usefully and stably compared against the +// invocation counts in methods. +int ciMethod::scale_count(int count, float prof_factor) { + if (count > 0 && method_data() != NULL) { + int current_mileage = method_data()->current_mileage(); + int creation_mileage = method_data()->creation_mileage(); + int counter_life = current_mileage - creation_mileage; + int method_life = interpreter_invocation_count(); + // counter_life due to backedge_counter could be > method_life + if (counter_life > method_life) + counter_life = method_life; + if (0 < counter_life && counter_life <= method_life) { + count = (int)((double)count * prof_factor * method_life / counter_life + 0.5); + count = (count > 0) ? count : 1; + } + } + return count; +} + +// ------------------------------------------------------------------ +// ciMethod::build_method_data +// +// Generate new methodDataOop objects at compile time. +void ciMethod::build_method_data(methodHandle h_m) { + EXCEPTION_CONTEXT; + if (is_native() || is_abstract() || h_m()->is_accessor()) return; + if (h_m()->method_data() == NULL) { + methodOopDesc::build_interpreter_method_data(h_m, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + } + } + if (h_m()->method_data() != NULL) { + _method_data = CURRENT_ENV->get_object(h_m()->method_data())->as_method_data(); + _method_data->load_data(); + } else { + _method_data = CURRENT_ENV->get_empty_methodData(); + } +} + +// public, retroactive version +void ciMethod::build_method_data() { + if (_method_data == NULL || _method_data->is_empty()) { + GUARDED_VM_ENTRY({ + build_method_data(get_methodOop()); + }); + } +} + + +// ------------------------------------------------------------------ +// ciMethod::method_data +// +ciMethodData* ciMethod::method_data() { + if (_method_data != NULL) { + return _method_data; + } + VM_ENTRY_MARK; + ciEnv* env = CURRENT_ENV; + Thread* my_thread = JavaThread::current(); + methodHandle h_m(my_thread, get_methodOop()); + + if (Tier1UpdateMethodData && is_tier1_compile(env->comp_level())) { + build_method_data(h_m); + } + + if (h_m()->method_data() != NULL) { + _method_data = CURRENT_ENV->get_object(h_m()->method_data())->as_method_data(); + _method_data->load_data(); + } else { + _method_data = CURRENT_ENV->get_empty_methodData(); + } + return _method_data; + +} + + +// ------------------------------------------------------------------ +// ciMethod::will_link +// +// Will this method link in a specific calling context? +bool ciMethod::will_link(ciKlass* accessing_klass, + ciKlass* declared_method_holder, + Bytecodes::Code bc) { + if (!is_loaded()) { + // Method lookup failed. + return false; + } + + // The link checks have been front-loaded into the get_method + // call. This method (ciMethod::will_link()) will be removed + // in the future. + + return true; +} + +// ------------------------------------------------------------------ +// ciMethod::should_exclude +// +// Should this method be excluded from compilation? +bool ciMethod::should_exclude() { + check_is_loaded(); + VM_ENTRY_MARK; + methodHandle mh(THREAD, get_methodOop()); + bool ignore; + return CompilerOracle::should_exclude(mh, ignore); +} + +// ------------------------------------------------------------------ +// ciMethod::should_inline +// +// Should this method be inlined during compilation? +bool ciMethod::should_inline() { + check_is_loaded(); + VM_ENTRY_MARK; + methodHandle mh(THREAD, get_methodOop()); + return CompilerOracle::should_inline(mh); +} + +// ------------------------------------------------------------------ +// ciMethod::should_not_inline +// +// Should this method be disallowed from inlining during compilation? +bool ciMethod::should_not_inline() { + check_is_loaded(); + VM_ENTRY_MARK; + methodHandle mh(THREAD, get_methodOop()); + return CompilerOracle::should_not_inline(mh); +} + +// ------------------------------------------------------------------ +// ciMethod::should_print_assembly +// +// Should the compiler print the generated code for this method? +bool ciMethod::should_print_assembly() { + check_is_loaded(); + VM_ENTRY_MARK; + methodHandle mh(THREAD, get_methodOop()); + return CompilerOracle::should_print(mh); +} + +// ------------------------------------------------------------------ +// ciMethod::break_at_execute +// +// Should the compiler insert a breakpoint into the generated code +// method? +bool ciMethod::break_at_execute() { + check_is_loaded(); + VM_ENTRY_MARK; + methodHandle mh(THREAD, get_methodOop()); + return CompilerOracle::should_break_at(mh); +} + +// ------------------------------------------------------------------ +// ciMethod::has_option +// +bool ciMethod::has_option(const char* option) { + check_is_loaded(); + VM_ENTRY_MARK; + methodHandle mh(THREAD, get_methodOop()); + return CompilerOracle::has_option_string(mh, option); +} + +// ------------------------------------------------------------------ +// ciMethod::can_be_compiled +// +// Have previous compilations of this method succeeded? +bool ciMethod::can_be_compiled() { + check_is_loaded(); + return _is_compilable; +} + +// ------------------------------------------------------------------ +// ciMethod::set_not_compilable +// +// Tell the VM that this method cannot be compiled at all. +void ciMethod::set_not_compilable() { + check_is_loaded(); + VM_ENTRY_MARK; + _is_compilable = false; + get_methodOop()->set_not_compilable(); +} + +// ------------------------------------------------------------------ +// ciMethod::can_be_osr_compiled +// +// Have previous compilations of this method succeeded? +// +// Implementation note: the VM does not currently keep track +// of failed OSR compilations per bci. The entry_bci parameter +// is currently unused. +bool ciMethod::can_be_osr_compiled(int entry_bci) { + check_is_loaded(); + VM_ENTRY_MARK; + return !get_methodOop()->access_flags().is_not_osr_compilable(); +} + +// ------------------------------------------------------------------ +// ciMethod::has_compiled_code +bool ciMethod::has_compiled_code() { + VM_ENTRY_MARK; + return get_methodOop()->code() != NULL; +} + +// ------------------------------------------------------------------ +// ciMethod::instructions_size +// This is a rough metric for "fat" methods, compared +// before inlining with InlineSmallCode. +// The CodeBlob::instructions_size accessor includes +// junk like exception handler, stubs, and constant table, +// which are not highly relevant to an inlined method. +// So we use the more specific accessor nmethod::code_size. +int ciMethod::instructions_size() { + GUARDED_VM_ENTRY( + nmethod* code = get_methodOop()->code(); + // if there's no compiled code or the code was produced by the + // tier1 profiler return 0 for the code size. This should + // probably be based on the compilation level of the nmethod but + // that currently isn't properly recorded. + if (code == NULL || + (TieredCompilation && code->compiler() != NULL && code->compiler()->is_c1())) { + return 0; + } + return code->code_size(); + ) +} + +// ------------------------------------------------------------------ +// ciMethod::log_nmethod_identity +void ciMethod::log_nmethod_identity(xmlStream* log) { + GUARDED_VM_ENTRY( + nmethod* code = get_methodOop()->code(); + if (code != NULL) { + code->log_identity(log); + } + ) +} + +// ------------------------------------------------------------------ +// ciMethod::is_not_reached +bool ciMethod::is_not_reached(int bci) { + check_is_loaded(); + VM_ENTRY_MARK; + return Interpreter::is_not_reached( + methodHandle(THREAD, get_methodOop()), bci); +} + +// ------------------------------------------------------------------ +// ciMethod::was_never_executed +bool ciMethod::was_executed_more_than(int times) { + VM_ENTRY_MARK; + return get_methodOop()->was_executed_more_than(times); +} + +// ------------------------------------------------------------------ +// ciMethod::has_unloaded_classes_in_signature +bool ciMethod::has_unloaded_classes_in_signature() { + VM_ENTRY_MARK; + { + EXCEPTION_MARK; + methodHandle m(THREAD, get_methodOop()); + bool has_unloaded = methodOopDesc::has_unloaded_classes_in_signature(m, (JavaThread *)THREAD); + if( HAS_PENDING_EXCEPTION ) { + CLEAR_PENDING_EXCEPTION; + return true; // Declare that we may have unloaded classes + } + return has_unloaded; + } +} + +// ------------------------------------------------------------------ +// ciMethod::is_klass_loaded +bool ciMethod::is_klass_loaded(int refinfo_index, bool must_be_resolved) const { + VM_ENTRY_MARK; + return get_methodOop()->is_klass_loaded(refinfo_index, must_be_resolved); +} + +// ------------------------------------------------------------------ +// ciMethod::check_call +bool ciMethod::check_call(int refinfo_index, bool is_static) const { + VM_ENTRY_MARK; + { + EXCEPTION_MARK; + HandleMark hm(THREAD); + constantPoolHandle pool (THREAD, get_methodOop()->constants()); + methodHandle spec_method; + KlassHandle spec_klass; + LinkResolver::resolve_method(spec_method, spec_klass, pool, refinfo_index, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return false; + } else { + return (spec_method->is_static() == is_static); + } + } + return false; +} + +// ------------------------------------------------------------------ +// ciMethod::print_codes +// +// Print the bytecodes for this method. +void ciMethod::print_codes_on(outputStream* st) { + check_is_loaded(); + GUARDED_VM_ENTRY(get_methodOop()->print_codes_on(st);) +} + + +#define FETCH_FLAG_FROM_VM(flag_accessor) { \ + check_is_loaded(); \ + VM_ENTRY_MARK; \ + return get_methodOop()->flag_accessor(); \ +} + +bool ciMethod::is_empty_method() const { FETCH_FLAG_FROM_VM(is_empty_method); } +bool ciMethod::is_vanilla_constructor() const { FETCH_FLAG_FROM_VM(is_vanilla_constructor); } +bool ciMethod::has_loops () const { FETCH_FLAG_FROM_VM(has_loops); } +bool ciMethod::has_jsrs () const { FETCH_FLAG_FROM_VM(has_jsrs); } +bool ciMethod::is_accessor () const { FETCH_FLAG_FROM_VM(is_accessor); } +bool ciMethod::is_initializer () const { FETCH_FLAG_FROM_VM(is_initializer); } + +BCEscapeAnalyzer *ciMethod::get_bcea() { + if (_bcea == NULL) { + _bcea = new (CURRENT_ENV->arena()) BCEscapeAnalyzer(this, NULL); + } + return _bcea; +} + +ciMethodBlocks *ciMethod::get_method_blocks() { + Arena *arena = CURRENT_ENV->arena(); + if (_method_blocks == NULL) { + _method_blocks = new (arena) ciMethodBlocks(arena, this); + } + return _method_blocks; +} + +#undef FETCH_FLAG_FROM_VM + + +// ------------------------------------------------------------------ +// ciMethod::print_codes +// +// Print a range of the bytecodes for this method. +void ciMethod::print_codes_on(int from, int to, outputStream* st) { + check_is_loaded(); + GUARDED_VM_ENTRY(get_methodOop()->print_codes_on(from, to, st);) +} + +// ------------------------------------------------------------------ +// ciMethod::print_name +// +// Print the name of this method, including signature and some flags. +void ciMethod::print_name(outputStream* st) { + check_is_loaded(); + GUARDED_VM_ENTRY(get_methodOop()->print_name(st);) +} + +// ------------------------------------------------------------------ +// ciMethod::print_short_name +// +// Print the name of this method, without signature. +void ciMethod::print_short_name(outputStream* st) { + check_is_loaded(); + GUARDED_VM_ENTRY(get_methodOop()->print_short_name(st);) +} + +// ------------------------------------------------------------------ +// ciMethod::print_impl +// +// Implementation of the print method. +void ciMethod::print_impl(outputStream* st) { + ciObject::print_impl(st); + st->print(" name="); + name()->print_symbol_on(st); + st->print(" holder="); + holder()->print_name_on(st); + st->print(" signature="); + signature()->as_symbol()->print_symbol_on(st); + if (is_loaded()) { + st->print(" loaded=true flags="); + flags().print_member_flags(st); + } else { + st->print(" loaded=false"); + } +} diff --git a/hotspot/src/share/vm/ci/ciMethod.hpp b/hotspot/src/share/vm/ci/ciMethod.hpp new file mode 100644 index 00000000000..ee30a21a645 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciMethod.hpp @@ -0,0 +1,246 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ciMethodBlocks; +class MethodLiveness; +class BitMap; +class Arena; +class BCEscapeAnalyzer; + + +// ciMethod +// +// This class represents a methodOop in the HotSpot virtual +// machine. +class ciMethod : public ciObject { + friend class CompileBroker; + CI_PACKAGE_ACCESS + friend class ciEnv; + friend class ciExceptionHandlerStream; + + private: + // General method information. + ciFlags _flags; + ciSymbol* _name; + ciInstanceKlass* _holder; + ciSignature* _signature; + ciMethodData* _method_data; + BCEscapeAnalyzer* _bcea; + ciMethodBlocks* _method_blocks; + + // Code attributes. + int _code_size; + int _max_stack; + int _max_locals; + vmIntrinsics::ID _intrinsic_id; + int _handler_count; + int _interpreter_invocation_count; + int _interpreter_throwout_count; + + bool _uses_monitors; + bool _balanced_monitors; + bool _is_compilable; + bool _can_be_statically_bound; + + // Lazy fields, filled in on demand + address _code; + ciExceptionHandler** _exception_handlers; + + // Optional liveness analyzer. + MethodLiveness* _liveness; +#ifdef COMPILER2 + ciTypeFlow* _flow; +#endif + + ciMethod(methodHandle h_m); + ciMethod(ciInstanceKlass* holder, ciSymbol* name, ciSymbol* signature); + + methodOop get_methodOop() const { + methodOop m = (methodOop)get_oop(); + assert(m != NULL, "illegal use of unloaded method"); + return m; + } + + oop loader() const { return _holder->loader(); } + + const char* type_string() { return "ciMethod"; } + + void print_impl(outputStream* st); + + void load_code(); + + void check_is_loaded() const { assert(is_loaded(), "not loaded"); } + + void build_method_data(methodHandle h_m); + + void code_at_put(int bci, Bytecodes::Code code) { + Bytecodes::check(code); + assert(0 <= bci && bci < code_size(), "valid bci"); + address bcp = _code + bci; + *bcp = code; + } + + public: + // Basic method information. + ciFlags flags() const { check_is_loaded(); return _flags; } + ciSymbol* name() const { return _name; } + ciInstanceKlass* holder() const { return _holder; } + ciMethodData* method_data(); + + // Signature information. + ciSignature* signature() const { return _signature; } + ciType* return_type() const { return _signature->return_type(); } + int arg_size_no_receiver() const { return _signature->size(); } + int arg_size() const { return _signature->size() + (_flags.is_static() ? 0 : 1); } + + // Method code and related information. + address code() { if (_code == NULL) load_code(); return _code; } + int code_size() const { check_is_loaded(); return _code_size; } + int max_stack() const { check_is_loaded(); return _max_stack; } + int max_locals() const { check_is_loaded(); return _max_locals; } + vmIntrinsics::ID intrinsic_id() const { check_is_loaded(); return _intrinsic_id; } + bool has_exception_handlers() const { check_is_loaded(); return _handler_count > 0; } + int exception_table_length() const { check_is_loaded(); return _handler_count; } + int interpreter_invocation_count() const { check_is_loaded(); return _interpreter_invocation_count; } + int interpreter_throwout_count() const { check_is_loaded(); return _interpreter_throwout_count; } + + Bytecodes::Code java_code_at_bci(int bci) { + address bcp = code() + bci; + return Bytecodes::java_code_at(bcp); + } + BCEscapeAnalyzer *get_bcea(); + ciMethodBlocks *get_method_blocks(); + + bool has_linenumber_table() const; // length unknown until decompression + u_char* compressed_linenumber_table() const; // not preserved by gc + + int line_number_from_bci(int bci) const; + + // Runtime information. + int vtable_index(); + address native_entry(); + address interpreter_entry(); + + // Analysis and profiling. + // + // Usage note: liveness_at_bci and init_vars should be wrapped in ResourceMarks. + bool uses_monitors() const { return _uses_monitors; } // this one should go away, it has a misleading name + bool has_monitor_bytecodes() const { return _uses_monitors; } + bool has_balanced_monitors(); + + MethodLivenessResult liveness_at_bci(int bci); + + // Get the interpreters viewpoint on oop liveness. MethodLiveness is + // conservative in the sense that it may consider locals to be live which + // cannot be live, like in the case where a local could contain an oop or + // a primitive along different paths. In that case the local must be + // dead when those paths merge. Since the interpreter's viewpoint is + // used when gc'ing an interpreter frame we need to use its viewpoint + // during OSR when loading the locals. + + BitMap live_local_oops_at_bci(int bci); + +#ifdef COMPILER1 + const BitMap bci_block_start(); +#endif + + ciTypeFlow* get_flow_analysis(); + ciTypeFlow* get_osr_flow_analysis(int osr_bci); // alternate entry point + ciCallProfile call_profile_at_bci(int bci); + int interpreter_call_site_count(int bci); + + // Given a certain calling environment, find the monomorphic target + // for the call. Return NULL if the call is not monomorphic in + // its calling environment. + ciMethod* find_monomorphic_target(ciInstanceKlass* caller, + ciInstanceKlass* callee_holder, + ciInstanceKlass* actual_receiver); + + // Given a known receiver klass, find the target for the call. + // Return NULL if the call has no target or is abstract. + ciMethod* resolve_invoke(ciKlass* caller, ciKlass* exact_receiver); + + // Find the proper vtable index to invoke this method. + int resolve_vtable_index(ciKlass* caller, ciKlass* receiver); + + // Compilation directives + bool will_link(ciKlass* accessing_klass, + ciKlass* declared_method_holder, + Bytecodes::Code bc); + bool should_exclude(); + bool should_inline(); + bool should_not_inline(); + bool should_print_assembly(); + bool break_at_execute(); + bool has_option(const char *option); + bool can_be_compiled(); + bool can_be_osr_compiled(int entry_bci); + void set_not_compilable(); + bool has_compiled_code(); + int instructions_size(); + void log_nmethod_identity(xmlStream* log); + bool is_not_reached(int bci); + bool was_executed_more_than(int times); + bool has_unloaded_classes_in_signature(); + bool is_klass_loaded(int refinfo_index, bool must_be_resolved) const; + bool check_call(int refinfo_index, bool is_static) const; + void build_method_data(); // make sure it exists in the VM also + int scale_count(int count, float prof_factor = 1.); // make MDO count commensurate with IIC + + // What kind of ciObject is this? + bool is_method() { return true; } + + // Java access flags + bool is_public () const { return flags().is_public(); } + bool is_private () const { return flags().is_private(); } + bool is_protected () const { return flags().is_protected(); } + bool is_static () const { return flags().is_static(); } + bool is_final () const { return flags().is_final(); } + bool is_synchronized() const { return flags().is_synchronized(); } + bool is_native () const { return flags().is_native(); } + bool is_interface () const { return flags().is_interface(); } + bool is_abstract () const { return flags().is_abstract(); } + bool is_strict () const { return flags().is_strict(); } + + // Other flags + bool is_empty_method() const; + bool is_vanilla_constructor() const; + bool is_final_method() const { return is_final() || holder()->is_final(); } + bool has_loops () const; + bool has_jsrs () const; + bool is_accessor () const; + bool is_initializer () const; + bool can_be_statically_bound() const { return _can_be_statically_bound; } + + // Print the bytecodes of this method. + void print_codes_on(outputStream* st); + void print_codes() { + print_codes_on(tty); + } + void print_codes_on(int from, int to, outputStream* st); + + // Print the name of this method in various incarnations. + void print_name(outputStream* st = tty); + void print_short_name(outputStream* st = tty); +}; diff --git a/hotspot/src/share/vm/ci/ciMethodBlocks.cpp b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp new file mode 100644 index 00000000000..d2b93186d9d --- /dev/null +++ b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp @@ -0,0 +1,360 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciMethodBlocks.cpp.incl" + +// ciMethodBlocks + + + +ciBlock *ciMethodBlocks::block_containing(int bci) { + ciBlock *blk = _bci_to_block[bci]; + return blk; +} + +bool ciMethodBlocks::is_block_start(int bci) { + assert(bci >=0 && bci < _code_size, "valid bytecode range"); + ciBlock *b = _bci_to_block[bci]; + assert(b != NULL, "must have block for bytecode"); + return b->start_bci() == bci; +} + +// ------------------------------------------------------------------ +// ciMethodBlocks::split_block_at +// +// Split the block spanning bci into two separate ranges. The former +// block becomes the second half and a new range is created for the +// first half. Returns the range beginning at bci. +ciBlock *ciMethodBlocks::split_block_at(int bci) { + ciBlock *former_block = block_containing(bci); + ciBlock *new_block = new(_arena) ciBlock(_method, _num_blocks++, this, former_block->start_bci()); + _blocks->append(new_block); + assert(former_block != NULL, "must not be NULL"); + new_block->set_limit_bci(bci); + former_block->set_start_bci(bci); + for (int pos=bci-1; pos >= 0; pos--) { + ciBlock *current_block = block_containing(pos); + if (current_block == former_block) { + // Replace it. + _bci_to_block[pos] = new_block; + } else if (current_block == NULL) { + // Non-bytecode start. Skip. + continue; + } else { + // We are done with our backwards walk + break; + } + } + return former_block; +} + +ciBlock *ciMethodBlocks::make_block_at(int bci) { + ciBlock *cb = block_containing(bci); + if (cb == NULL ) { + // This is our first time visiting this bytecode. Create + // a fresh block and assign it this starting point. + ciBlock *nb = new(_arena) ciBlock(_method, _num_blocks++, this, bci); + _blocks->append(nb); + _bci_to_block[bci] = nb; + return nb; + } else if (cb->start_bci() == bci) { + // The block begins at bci. Simply return it. + return cb; + } else { + // We have already created a block containing bci but + // not starting at bci. This existing block needs to + // be split into two. + return split_block_at(bci); + } +} + +void ciMethodBlocks::do_analysis() { + ciBytecodeStream s(_method); + ciBlock *cur_block = block_containing(0); + int limit_bci = _method->code_size(); + + while (s.next() != ciBytecodeStream::EOBC()) { + int bci = s.cur_bci(); + // Determine if a new block has been made at the current bci. If + // this block differs from our current range, switch to the new + // one and end the old one. + assert(cur_block != NULL, "must always have a current block"); + ciBlock *new_block = block_containing(bci); + if (new_block == NULL) { + // We have not marked this bci as the start of a new block. + // Keep interpreting the current_range. + _bci_to_block[bci] = cur_block; + } else { + cur_block->set_limit_bci(bci); + cur_block = new_block; + } + + switch (s.cur_bc()) { + case Bytecodes::_ifeq : + case Bytecodes::_ifne : + case Bytecodes::_iflt : + case Bytecodes::_ifge : + case Bytecodes::_ifgt : + case Bytecodes::_ifle : + case Bytecodes::_if_icmpeq : + case Bytecodes::_if_icmpne : + case Bytecodes::_if_icmplt : + case Bytecodes::_if_icmpge : + case Bytecodes::_if_icmpgt : + case Bytecodes::_if_icmple : + case Bytecodes::_if_acmpeq : + case Bytecodes::_if_acmpne : + case Bytecodes::_ifnull : + case Bytecodes::_ifnonnull : + { + cur_block->set_control_bci(bci); + ciBlock *fall_through = make_block_at(s.next_bci()); + int dest_bci = s.get_dest(); + ciBlock *dest = make_block_at(dest_bci); + break; + } + + case Bytecodes::_goto : + { + cur_block->set_control_bci(bci); + if (s.next_bci() < limit_bci) { + (void) make_block_at(s.next_bci()); + } + int dest_bci = s.get_dest(); + ciBlock *dest = make_block_at(dest_bci); + break; + } + + case Bytecodes::_jsr : + { + cur_block->set_control_bci(bci); + ciBlock *ret = make_block_at(s.next_bci()); + int dest_bci = s.get_dest(); + ciBlock *dest = make_block_at(dest_bci); + break; + } + + case Bytecodes::_tableswitch : + { + cur_block->set_control_bci(bci); + Bytecode_tableswitch* switch_ = Bytecode_tableswitch_at(s.cur_bcp()); + int len = switch_->length(); + ciBlock *dest; + int dest_bci; + for (int i = 0; i < len; i++) { + dest_bci = s.cur_bci() + switch_->dest_offset_at(i); + dest = make_block_at(dest_bci); + } + dest_bci = s.cur_bci() + switch_->default_offset(); + make_block_at(dest_bci); + if (s.next_bci() < limit_bci) { + dest = make_block_at(s.next_bci()); + } + } + break; + + case Bytecodes::_lookupswitch: + { + cur_block->set_control_bci(bci); + Bytecode_lookupswitch* switch_ = Bytecode_lookupswitch_at(s.cur_bcp()); + int len = switch_->number_of_pairs(); + ciBlock *dest; + int dest_bci; + for (int i = 0; i < len; i++) { + dest_bci = s.cur_bci() + switch_->pair_at(i)->offset(); + dest = make_block_at(dest_bci); + } + dest_bci = s.cur_bci() + switch_->default_offset(); + dest = make_block_at(dest_bci); + if (s.next_bci() < limit_bci) { + dest = make_block_at(s.next_bci()); + } + } + break; + + case Bytecodes::_goto_w : + { + cur_block->set_control_bci(bci); + if (s.next_bci() < limit_bci) { + (void) make_block_at(s.next_bci()); + } + int dest_bci = s.get_far_dest(); + ciBlock *dest = make_block_at(dest_bci); + break; + } + + case Bytecodes::_jsr_w : + { + cur_block->set_control_bci(bci); + ciBlock *ret = make_block_at(s.next_bci()); + int dest_bci = s.get_far_dest(); + ciBlock *dest = make_block_at(dest_bci); + break; + } + + case Bytecodes::_athrow : + cur_block->set_may_throw(); + // fall-through + case Bytecodes::_ret : + case Bytecodes::_ireturn : + case Bytecodes::_lreturn : + case Bytecodes::_freturn : + case Bytecodes::_dreturn : + case Bytecodes::_areturn : + case Bytecodes::_return : + cur_block->set_control_bci(bci); + if (s.next_bci() < limit_bci) { + (void) make_block_at(s.next_bci()); + } + break; + } + } + // End the last block + cur_block->set_limit_bci(limit_bci); +} + +ciMethodBlocks::ciMethodBlocks(Arena *arena, ciMethod *meth): _method(meth), + _arena(arena), _num_blocks(0), _code_size(meth->code_size()) { + int block_estimate = _code_size / 8; + + _blocks = new(_arena) GrowableArray(block_estimate); + int b2bsize = _code_size * sizeof(ciBlock **); + _bci_to_block = (ciBlock **) arena->Amalloc(b2bsize); + Copy::zero_to_words((HeapWord*) _bci_to_block, b2bsize / sizeof(HeapWord)); + + // create initial block covering the entire method + ciBlock *b = new(arena) ciBlock(_method, _num_blocks++, this, 0); + _blocks->append(b); + _bci_to_block[0] = b; + + // create blocks for exception handlers + if (meth->has_exception_handlers()) { + for(ciExceptionHandlerStream str(meth); !str.is_done(); str.next()) { + ciExceptionHandler* handler = str.handler(); + ciBlock *eb = make_block_at(handler->handler_bci()); + eb->set_handler(); + int ex_start = handler->start(); + int ex_end = handler->limit(); + eb->set_exception_range(ex_start, ex_end); + // ensure a block at the start of exception range and start of following code + (void) make_block_at(ex_start); + if (ex_end < _code_size) + (void) make_block_at(ex_end); + } + } + + // scan the bytecodes and identify blocks + do_analysis(); + + // mark blocks that have exception handlers + if (meth->has_exception_handlers()) { + for(ciExceptionHandlerStream str(meth); !str.is_done(); str.next()) { + ciExceptionHandler* handler = str.handler(); + int ex_start = handler->start(); + int ex_end = handler->limit(); + + int bci = ex_start; + while (bci < ex_end) { + ciBlock *b = block_containing(bci); + b->set_has_handler(); + bci = b->limit_bci(); + } + } + } +} + +void ciMethodBlocks::clear_processed() { + for (int i = 0; i < _blocks->length(); i++) + _blocks->at(i)->clear_processed(); +} + +#ifndef PRODUCT +void ciMethodBlocks::dump() { + tty->print("---- blocks for method: "); + _method->print(); + tty->cr(); + for (int i = 0; i < _blocks->length(); i++) { + tty->print(" B%d: ", i); _blocks->at(i)->dump(); + } +} +#endif + + +ciBlock::ciBlock(ciMethod *method, int index, ciMethodBlocks *mb, int start_bci) : +#ifndef PRODUCT + _method(method), +#endif + _idx(index), _flags(0), _start_bci(start_bci), _limit_bci(-1), _control_bci(fall_through_bci), + _ex_start_bci(-1), _ex_limit_bci(-1) { +} + +void ciBlock::set_exception_range(int start_bci, int limit_bci) { + assert(limit_bci >= start_bci, "valid range"); + assert(is_handler(), "must be handler"); + _ex_start_bci = start_bci; + _ex_limit_bci = limit_bci; +} + +#ifndef PRODUCT +static char *flagnames[] = { + "Processed", + "Handler", + "MayThrow", + "Jsr", + "Ret", + "RetTarget", + "HasHandler", +}; + +void ciBlock::dump() { + tty->print(" [%d .. %d), {", _start_bci, _limit_bci); + for (int i = 0; i < 8; i++) { + if ((_flags & (1 << i)) != 0) { + tty->print(" %s", flagnames[i]); + } + } + tty->print(" ]"); + if (is_handler()) + tty->print(" handles(%d..%d)", _ex_start_bci, _ex_limit_bci); + tty->cr(); +} + +// ------------------------------------------------------------------ +// ciBlock::print_on +void ciBlock::print_on(outputStream* st) const { + st->print_cr("--------------------------------------------------------"); + st->print ("ciBlock [%d - %d) control : ", start_bci(), limit_bci()); + if (control_bci() == fall_through_bci) { + st->print_cr("%d:fall through", limit_bci()); + } else { + st->print_cr("%d:%s", control_bci(), + Bytecodes::name(method()->java_code_at_bci(control_bci()))); + } + + if (Verbose || WizardMode) { + method()->print_codes_on(start_bci(), limit_bci(), st); + } +} +#endif diff --git a/hotspot/src/share/vm/ci/ciMethodBlocks.hpp b/hotspot/src/share/vm/ci/ciMethodBlocks.hpp new file mode 100644 index 00000000000..a0db4bb6dcd --- /dev/null +++ b/hotspot/src/share/vm/ci/ciMethodBlocks.hpp @@ -0,0 +1,121 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +class ciBlock; + +typedef short ciBlockIndex; + +class ciMethodBlocks : public ResourceObj { +private: + ciMethod *_method; + Arena *_arena; + GrowableArray *_blocks; + ciBlock **_bci_to_block; + int _num_blocks; + int _code_size; + + void do_analysis(); +public: + ciMethodBlocks(Arena *arena, ciMethod *meth); + + ciBlock *block_containing(int bci); + ciBlock *block(int index) { return _blocks->at(index); } + ciBlock *make_block_at(int bci); + ciBlock *split_block_at(int bci); + bool is_block_start(int bci); + int num_blocks() { return _num_blocks;} + void clear_processed(); + +#ifndef PRODUCT + void dump(); +#endif +}; + +class ciBlock : public ResourceObj { +private: + int _idx; + int _start_bci; + int _limit_bci; + int _control_bci; + uint _flags; + int _ex_start_bci; + int _ex_limit_bci; +#ifndef PRODUCT + ciMethod *_method; +#endif + enum { + Processed = (1 << 0), + Handler = (1 << 1), + MayThrow = (1 << 2), + DoesJsr = (1 << 3), + DoesRet = (1 << 4), + RetTarget = (1 << 5), + HasHandler = (1 << 6) + }; + + +public: + enum { + fall_through_bci = -1 + }; + + ciBlock(ciMethod *method, int index, ciMethodBlocks *mb, int start_bci); + int start_bci() const { return _start_bci; } + int limit_bci() const { return _limit_bci; } + int control_bci() const { return _control_bci; } + int index() const { return _idx; } + void set_start_bci(int bci) { _start_bci = bci; } + void set_limit_bci(int bci) { _limit_bci = bci; } + void set_control_bci(int bci) { _control_bci = bci;} + void set_exception_range(int start_bci, int limit_bci); + int ex_start_bci() const { return _ex_start_bci; } + int ex_limit_bci() const { return _ex_limit_bci; } + bool contains(int bci) const { return start_bci() <= bci && bci < limit_bci(); } + + + // flag handling + bool processed() const { return (_flags & Processed) != 0; } + bool is_handler() const { return (_flags & Handler) != 0; } + bool may_throw() const { return (_flags & MayThrow) != 0; } + bool does_jsr() const { return (_flags & DoesJsr) != 0; } + bool does_ret() const { return (_flags & DoesRet) != 0; } + bool has_handler() const { return (_flags & HasHandler) != 0; } + bool is_ret_target() const { return (_flags & RetTarget) != 0; } + void set_processed() { _flags |= Processed; } + void clear_processed() { _flags &= ~Processed; } + void set_handler() { _flags |= Handler; } + void set_may_throw() { _flags |= MayThrow; } + void set_does_jsr() { _flags |= DoesJsr; } + void clear_does_jsr() { _flags &= ~DoesJsr; } + void set_does_ret() { _flags |= DoesRet; } + void clear_does_ret() { _flags |= DoesRet; } + void set_is_ret_target() { _flags |= RetTarget; } + void set_has_handler() { _flags |= HasHandler; } +#ifndef PRODUCT + ciMethod *method() const { return _method; } + void dump(); + void print_on(outputStream* st) const PRODUCT_RETURN; +#endif +}; diff --git a/hotspot/src/share/vm/ci/ciMethodData.cpp b/hotspot/src/share/vm/ci/ciMethodData.cpp new file mode 100644 index 00000000000..b1ee47716a7 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciMethodData.cpp @@ -0,0 +1,335 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciMethodData.cpp.incl" + +// ciMethodData + +// ------------------------------------------------------------------ +// ciMethodData::ciMethodData +// +ciMethodData::ciMethodData(methodDataHandle h_md) : ciObject(h_md) { + assert(h_md() != NULL, "no null method data"); + Copy::zero_to_words((HeapWord*) &_orig, sizeof(_orig) / sizeof(HeapWord)); + _data = NULL; + _data_size = 0; + _extra_data_size = 0; + _current_mileage = 0; + _state = empty_state; + _saw_free_extra_data = false; + // Set an initial hint. Don't use set_hint_di() because + // first_di() may be out of bounds if data_size is 0. + _hint_di = first_di(); +} + +// ------------------------------------------------------------------ +// ciMethodData::ciMethodData +// +// No methodDataOop. +ciMethodData::ciMethodData() : ciObject() { + Copy::zero_to_words((HeapWord*) &_orig, sizeof(_orig) / sizeof(HeapWord)); + _data = NULL; + _data_size = 0; + _extra_data_size = 0; + _current_mileage = 0; + _state = empty_state; + _saw_free_extra_data = false; + // Set an initial hint. Don't use set_hint_di() because + // first_di() may be out of bounds if data_size is 0. + _hint_di = first_di(); +} + +void ciMethodData::load_data() { + methodDataOop mdo = get_methodDataOop(); + if (mdo == NULL) return; + + // To do: don't copy the data if it is not "ripe" -- require a minimum # + // of invocations. + + // Snapshot the data -- actually, take an approximate snapshot of + // the data. Any concurrently executing threads may be changing the + // data as we copy it. + int skip_header = oopDesc::header_size(); + Copy::disjoint_words((HeapWord*) mdo + skip_header, + (HeapWord*) &_orig + skip_header, + sizeof(_orig) / HeapWordSize - skip_header); + DEBUG_ONLY(*_orig.adr_method() = NULL); // no dangling oops, please + Arena* arena = CURRENT_ENV->arena(); + _data_size = mdo->data_size(); + _extra_data_size = mdo->extra_data_size(); + int total_size = _data_size + _extra_data_size; + _data = (intptr_t *) arena->Amalloc(total_size); + Copy::disjoint_words((HeapWord*) mdo->data_base(), (HeapWord*) _data, total_size / HeapWordSize); + + // Traverse the profile data, translating any oops into their + // ci equivalents. + ResourceMark rm; + ciProfileData* ci_data = first_data(); + ProfileData* data = mdo->first_data(); + while (is_valid(ci_data)) { + ci_data->translate_from(data); + ci_data = next_data(ci_data); + data = mdo->next_data(data); + } + // Note: Extra data are all BitData, and do not need translation. + _current_mileage = methodDataOopDesc::mileage_of(mdo->method()); + _state = mdo->is_mature()? mature_state: immature_state; + + _eflags = mdo->eflags(); + _arg_local = mdo->arg_local(); + _arg_stack = mdo->arg_stack(); + _arg_returned = mdo->arg_returned(); +} + +void ciReceiverTypeData::translate_receiver_data_from(ProfileData* data) { + for (uint row = 0; row < row_limit(); row++) { + klassOop k = data->as_ReceiverTypeData()->receiver(row); + if (k != NULL) { + ciKlass* klass = CURRENT_ENV->get_object(k)->as_klass(); + set_receiver(row, klass); + } + } +} + + +// Get the data at an arbitrary (sort of) data index. +ciProfileData* ciMethodData::data_at(int data_index) { + if (out_of_bounds(data_index)) { + return NULL; + } + DataLayout* data_layout = data_layout_at(data_index); + + switch (data_layout->tag()) { + case DataLayout::no_tag: + default: + ShouldNotReachHere(); + return NULL; + case DataLayout::bit_data_tag: + return new ciBitData(data_layout); + case DataLayout::counter_data_tag: + return new ciCounterData(data_layout); + case DataLayout::jump_data_tag: + return new ciJumpData(data_layout); + case DataLayout::receiver_type_data_tag: + return new ciReceiverTypeData(data_layout); + case DataLayout::virtual_call_data_tag: + return new ciVirtualCallData(data_layout); + case DataLayout::ret_data_tag: + return new ciRetData(data_layout); + case DataLayout::branch_data_tag: + return new ciBranchData(data_layout); + case DataLayout::multi_branch_data_tag: + return new ciMultiBranchData(data_layout); + }; +} + +// Iteration over data. +ciProfileData* ciMethodData::next_data(ciProfileData* current) { + int current_index = dp_to_di(current->dp()); + int next_index = current_index + current->size_in_bytes(); + ciProfileData* next = data_at(next_index); + return next; +} + +// Translate a bci to its corresponding data, or NULL. +ciProfileData* ciMethodData::bci_to_data(int bci) { + ciProfileData* data = data_before(bci); + for ( ; is_valid(data); data = next_data(data)) { + if (data->bci() == bci) { + set_hint_di(dp_to_di(data->dp())); + return data; + } else if (data->bci() > bci) { + break; + } + } + // bci_to_extra_data(bci) ... + DataLayout* dp = data_layout_at(data_size()); + DataLayout* end = data_layout_at(data_size() + extra_data_size()); + for (; dp < end; dp = methodDataOopDesc::next_extra(dp)) { + if (dp->tag() == DataLayout::no_tag) { + _saw_free_extra_data = true; // observed an empty slot (common case) + return NULL; + } + if (dp->bci() == bci) { + assert(dp->tag() == DataLayout::bit_data_tag, "sane"); + return new ciBitData(dp); + } + } + return NULL; +} + +// Conservatively decode the trap_state of a ciProfileData. +int ciMethodData::has_trap_at(ciProfileData* data, int reason) { + typedef Deoptimization::DeoptReason DR_t; + int per_bc_reason + = Deoptimization::reason_recorded_per_bytecode_if_any((DR_t) reason); + if (trap_count(reason) == 0) { + // Impossible for this trap to have occurred, regardless of trap_state. + // Note: This happens if the MDO is empty. + return 0; + } else if (per_bc_reason == Deoptimization::Reason_none) { + // We cannot conclude anything; a trap happened somewhere, maybe here. + return -1; + } else if (data == NULL) { + // No profile here, not even an extra_data record allocated on the fly. + // If there are empty extra_data records, and there had been a trap, + // there would have been a non-null data pointer. If there are no + // free extra_data records, we must return a conservative -1. + if (_saw_free_extra_data) + return 0; // Q.E.D. + else + return -1; // bail with a conservative answer + } else { + return Deoptimization::trap_state_has_reason(data->trap_state(), per_bc_reason); + } +} + +int ciMethodData::trap_recompiled_at(ciProfileData* data) { + if (data == NULL) { + return (_saw_free_extra_data? 0: -1); // (see previous method) + } else { + return Deoptimization::trap_state_is_recompiled(data->trap_state())? 1: 0; + } +} + +void ciMethodData::clear_escape_info() { + VM_ENTRY_MARK; + methodDataOop mdo = get_methodDataOop(); + if (mdo != NULL) + mdo->clear_escape_info(); + _eflags = _arg_local = _arg_stack = _arg_returned = 0; +} + +// copy our escape info to the methodDataOop if it exists +void ciMethodData::update_escape_info() { + VM_ENTRY_MARK; + methodDataOop mdo = get_methodDataOop(); + if ( mdo != NULL) { + mdo->set_eflags(_eflags); + mdo->set_arg_local(_arg_local); + mdo->set_arg_stack(_arg_stack); + mdo->set_arg_returned(_arg_returned); + } +} + +bool ciMethodData::has_escape_info() { + return eflag_set(methodDataOopDesc::estimated); +} + +void ciMethodData::set_eflag(methodDataOopDesc::EscapeFlag f) { + set_bits(_eflags, f); +} + +void ciMethodData::clear_eflag(methodDataOopDesc::EscapeFlag f) { + clear_bits(_eflags, f); +} + +bool ciMethodData::eflag_set(methodDataOopDesc::EscapeFlag f) const { + return mask_bits(_eflags, f) != 0; +} + +void ciMethodData::set_arg_local(int i) { + set_nth_bit(_arg_local, i); +} + +void ciMethodData::set_arg_stack(int i) { + set_nth_bit(_arg_stack, i); +} + +void ciMethodData::set_arg_returned(int i) { + set_nth_bit(_arg_returned, i); +} + +bool ciMethodData::is_arg_local(int i) const { + return is_set_nth_bit(_arg_local, i); +} + +bool ciMethodData::is_arg_stack(int i) const { + return is_set_nth_bit(_arg_stack, i); +} + +bool ciMethodData::is_arg_returned(int i) const { + return is_set_nth_bit(_arg_returned, i); +} + +ByteSize ciMethodData::offset_of_slot(ciProfileData* data, ByteSize slot_offset_in_data) { + // Get offset within methodDataOop of the data array + ByteSize data_offset = methodDataOopDesc::data_offset(); + + // Get cell offset of the ProfileData within data array + int cell_offset = dp_to_di(data->dp()); + + // Add in counter_offset, the # of bytes into the ProfileData of counter or flag + int offset = in_bytes(data_offset) + cell_offset + in_bytes(slot_offset_in_data); + + return in_ByteSize(offset); +} + +// Implementation of the print method. +void ciMethodData::print_impl(outputStream* st) { + ciObject::print_impl(st); +} + +#ifndef PRODUCT +void ciMethodData::print() { + print_data_on(tty); +} + +void ciMethodData::print_data_on(outputStream* st) { + ResourceMark rm; + ciProfileData* data; + for (data = first_data(); is_valid(data); data = next_data(data)) { + st->print("%d", dp_to_di(data->dp())); + st->fill_to(6); + data->print_data_on(st); + } +} + +void ciReceiverTypeData::print_receiver_data_on(outputStream* st) { + uint row; + int entries = 0; + for (row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) entries++; + } + st->print_cr("count(%u) entries(%u)", count(), entries); + for (row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + tab(st); + receiver(row)->print_name_on(st); + st->print_cr("(%u)", receiver_count(row)); + } + } +} + +void ciReceiverTypeData::print_data_on(outputStream* st) { + print_shared(st, "ciReceiverTypeData"); + print_receiver_data_on(st); +} + +void ciVirtualCallData::print_data_on(outputStream* st) { + print_shared(st, "ciVirtualCallData"); + rtd_super()->print_receiver_data_on(st); +} +#endif diff --git a/hotspot/src/share/vm/ci/ciMethodData.hpp b/hotspot/src/share/vm/ci/ciMethodData.hpp new file mode 100644 index 00000000000..7e16d2f0974 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciMethodData.hpp @@ -0,0 +1,287 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ciBitData; +class ciCounterData; +class ciJumpData; +class ciReceiverTypeData; +class ciRetData; +class ciBranchData; +class ciArrayData; +class ciMultiBranchData; + +typedef ProfileData ciProfileData; + +class ciBitData : public BitData { +public: + ciBitData(DataLayout* layout) : BitData(layout) {}; +}; + +class ciCounterData : public CounterData { +public: + ciCounterData(DataLayout* layout) : CounterData(layout) {}; +}; + +class ciJumpData : public JumpData { +public: + ciJumpData(DataLayout* layout) : JumpData(layout) {}; +}; + +class ciReceiverTypeData : public ReceiverTypeData { +public: + ciReceiverTypeData(DataLayout* layout) : ReceiverTypeData(layout) {}; + + void set_receiver(uint row, ciKlass* recv) { + assert((uint)row < row_limit(), "oob"); + set_intptr_at(receiver0_offset + row * receiver_type_row_cell_count, + (intptr_t) recv); + } + + ciKlass* receiver(uint row) { + assert((uint)row < row_limit(), "oob"); + ciObject* recv = (ciObject*)intptr_at(receiver0_offset + row * receiver_type_row_cell_count); + assert(recv == NULL || recv->is_klass(), "wrong type"); + return (ciKlass*)recv; + } + + // Copy & translate from oop based ReceiverTypeData + virtual void translate_from(ProfileData* data) { + translate_receiver_data_from(data); + } + void translate_receiver_data_from(ProfileData* data); +#ifndef PRODUCT + void print_data_on(outputStream* st); + void print_receiver_data_on(outputStream* st); +#endif +}; + +class ciVirtualCallData : public VirtualCallData { + // Fake multiple inheritance... It's a ciReceiverTypeData also. + ciReceiverTypeData* rtd_super() { return (ciReceiverTypeData*) this; } + +public: + ciVirtualCallData(DataLayout* layout) : VirtualCallData(layout) {}; + + void set_receiver(uint row, ciKlass* recv) { + rtd_super()->set_receiver(row, recv); + } + + ciKlass* receiver(uint row) { + return rtd_super()->receiver(row); + } + + // Copy & translate from oop based VirtualCallData + virtual void translate_from(ProfileData* data) { + rtd_super()->translate_receiver_data_from(data); + } +#ifndef PRODUCT + void print_data_on(outputStream* st); +#endif +}; + + +class ciRetData : public RetData { +public: + ciRetData(DataLayout* layout) : RetData(layout) {}; +}; + +class ciBranchData : public BranchData { +public: + ciBranchData(DataLayout* layout) : BranchData(layout) {}; +}; + +class ciArrayData : public ArrayData { +public: + ciArrayData(DataLayout* layout) : ArrayData(layout) {}; +}; + +class ciMultiBranchData : public MultiBranchData { +public: + ciMultiBranchData(DataLayout* layout) : MultiBranchData(layout) {}; +}; + +// ciMethodData +// +// This class represents a methodDataOop in the HotSpot virtual +// machine. + +class ciMethodData : public ciObject { + CI_PACKAGE_ACCESS + +private: + // Size in bytes + int _data_size; + int _extra_data_size; + + // Data entries + intptr_t* _data; + + // Cached hint for data_before() + int _hint_di; + + // Is data attached? And is it mature? + enum { empty_state, immature_state, mature_state }; + u_char _state; + + // Set this true if empty extra_data slots are ever witnessed. + u_char _saw_free_extra_data; + + // Support for interprocedural escape analysis + intx _eflags; // flags on escape information + intx _arg_local; // bit set of non-escaping arguments + intx _arg_stack; // bit set of stack-allocatable arguments + intx _arg_returned; // bit set of returned arguments + + // Maturity of the oop when the snapshot is taken. + int _current_mileage; + + // Coherent snapshot of original header. + methodDataOopDesc _orig; + + ciMethodData(methodDataHandle h_md); + ciMethodData(); + + // Accessors + int data_size() { return _data_size; } + int extra_data_size() { return _extra_data_size; } + intptr_t * data() { return _data; } + + methodDataOop get_methodDataOop() const { + if (handle() == NULL) return NULL; + methodDataOop mdo = (methodDataOop)get_oop(); + assert(mdo != NULL, "illegal use of unloaded method data"); + return mdo; + } + + const char* type_string() { return "ciMethodData"; } + + void print_impl(outputStream* st); + + DataLayout* data_layout_at(int data_index) { + assert(data_index % sizeof(intptr_t) == 0, "unaligned"); + return (DataLayout*) (((address)_data) + data_index); + } + + bool out_of_bounds(int data_index) { + return data_index >= data_size(); + } + + // hint accessors + int hint_di() const { return _hint_di; } + void set_hint_di(int di) { + assert(!out_of_bounds(di), "hint_di out of bounds"); + _hint_di = di; + } + ciProfileData* data_before(int bci) { + // avoid SEGV on this edge case + if (data_size() == 0) + return NULL; + int hint = hint_di(); + if (data_layout_at(hint)->bci() <= bci) + return data_at(hint); + return first_data(); + } + + + // What is the index of the first data entry? + int first_di() { return 0; } + +public: + bool is_method_data() { return true; } + bool is_empty() { return _state == empty_state; } + bool is_mature() { return _state == mature_state; } + + int creation_mileage() { return _orig.creation_mileage(); } + int current_mileage() { return _current_mileage; } + + void load_data(); + + // Convert a dp (data pointer) to a di (data index). + int dp_to_di(address dp) { + return dp - ((address)_data); + } + + // Get the data at an arbitrary (sort of) data index. + ciProfileData* data_at(int data_index); + + // Walk through the data in order. + ciProfileData* first_data() { return data_at(first_di()); } + ciProfileData* next_data(ciProfileData* current); + bool is_valid(ciProfileData* current) { return current != NULL; } + + // Get the data at an arbitrary bci, or NULL if there is none. + ciProfileData* bci_to_data(int bci); + ciProfileData* bci_to_extra_data(int bci, bool create_if_missing); + + uint overflow_trap_count() const { + return _orig.overflow_trap_count(); + } + uint overflow_recompile_count() const { + return _orig.overflow_recompile_count(); + } + uint decompile_count() const { + return _orig.decompile_count(); + } + uint trap_count(int reason) const { + return _orig.trap_count(reason); + } + uint trap_reason_limit() const { return _orig.trap_reason_limit(); } + uint trap_count_limit() const { return _orig.trap_count_limit(); } + + // Helpful query functions that decode trap_state. + int has_trap_at(ciProfileData* data, int reason); + int has_trap_at(int bci, int reason) { + return has_trap_at(bci_to_data(bci), reason); + } + int trap_recompiled_at(ciProfileData* data); + int trap_recompiled_at(int bci) { + return trap_recompiled_at(bci_to_data(bci)); + } + + void clear_escape_info(); + bool has_escape_info(); + void update_escape_info(); + + void set_eflag(methodDataOopDesc::EscapeFlag f); + void clear_eflag(methodDataOopDesc::EscapeFlag f); + bool eflag_set(methodDataOopDesc::EscapeFlag f) const; + + void set_arg_local(int i); + void set_arg_stack(int i); + void set_arg_returned(int i); + + bool is_arg_local(int i) const; + bool is_arg_stack(int i) const; + bool is_arg_returned(int i) const; + + // Code generation helper + ByteSize offset_of_slot(ciProfileData* data, ByteSize slot_offset_in_data); + int byte_offset_of_slot(ciProfileData* data, ByteSize slot_offset_in_data) { return in_bytes(offset_of_slot(data, slot_offset_in_data)); } + +#ifndef PRODUCT + // printing support for method data + void print(); + void print_data_on(outputStream* st); +#endif +}; diff --git a/hotspot/src/share/vm/ci/ciMethodKlass.cpp b/hotspot/src/share/vm/ci/ciMethodKlass.cpp new file mode 100644 index 00000000000..c960a982e2d --- /dev/null +++ b/hotspot/src/share/vm/ci/ciMethodKlass.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciMethodKlass.cpp.incl" + +// ciMethodKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is a methodKlass. + +// ------------------------------------------------------------------ +// ciMethodKlass::instance +// +// Return the distinguished instance of this class +ciMethodKlass* ciMethodKlass::make() { + return CURRENT_ENV->_method_klass_instance; +} diff --git a/hotspot/src/share/vm/ci/ciMethodKlass.hpp b/hotspot/src/share/vm/ci/ciMethodKlass.hpp new file mode 100644 index 00000000000..7f53767d99f --- /dev/null +++ b/hotspot/src/share/vm/ci/ciMethodKlass.hpp @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciMethodKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part in a methodKlass. +class ciMethodKlass : public ciKlass { + CI_PACKAGE_ACCESS + +protected: + ciMethodKlass(KlassHandle h_k) : + ciKlass(h_k, ciSymbol::make("unique_methodKlass")) { + assert(get_Klass()->oop_is_method(), "wrong type"); + } + + methodKlass* get_methodKlass() { return (methodKlass*)get_Klass(); } + + const char* type_string() { return "ciMethodKlass"; } + +public: + // What kind of ciObject is this? + bool is_method_klass() { return true; } + + // Return the distinguished ciMethodKlass instance. + static ciMethodKlass* make(); +}; diff --git a/hotspot/src/share/vm/ci/ciNullObject.cpp b/hotspot/src/share/vm/ci/ciNullObject.cpp new file mode 100644 index 00000000000..8b5e83494a9 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciNullObject.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciNullObject.cpp.incl" + +// ciNullObject +// +// This class represents a null reference. It can be used +// as a class loader or as the null constant. + +// ------------------------------------------------------------------ +// ciNullObject::print_impl +// +// Implementation of the print method. +void ciNullObject::print_impl(outputStream* st) { + ciObject::print_impl(st); + st->print(" unique"); +} + +// ------------------------------------------------------------------ +// ciNullObject::make +// +// Get the distinguished instance of this class. +ciNullObject* ciNullObject::make() { + return CURRENT_ENV->_null_object_instance->as_null_object(); +} diff --git a/hotspot/src/share/vm/ci/ciNullObject.hpp b/hotspot/src/share/vm/ci/ciNullObject.hpp new file mode 100644 index 00000000000..90f61c6242b --- /dev/null +++ b/hotspot/src/share/vm/ci/ciNullObject.hpp @@ -0,0 +1,49 @@ +/* + * Copyright 1999-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciNullObject +// +// This class represents a null reference in the VM. +class ciNullObject : public ciObject { + CI_PACKAGE_ACCESS + +private: + ciNullObject() : ciObject() {} + + const char* type_string() { return "ciNullObject"; } + + void print_impl(outputStream* st); + +public: + // Is this ciObject a Java Language Object? That is, + // is the ciObject an instance or an array + bool is_java_object() { return true; } + + // What kind of ciObject is this? + bool is_null_object() const { return true; } + bool is_classless() const { return true; } + + // Get the distinguished instance of this klass. + static ciNullObject* make(); +}; diff --git a/hotspot/src/share/vm/ci/ciObjArray.hpp b/hotspot/src/share/vm/ci/ciObjArray.hpp new file mode 100644 index 00000000000..c43700fac23 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciObjArray.hpp @@ -0,0 +1,46 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciObjArray +// +// This class represents a ObjArrayOop in the HotSpot virtual +// machine. +class ciObjArray : public ciArray { + CI_PACKAGE_ACCESS + +protected: + ciObjArray(objArrayHandle h_o) : ciArray(h_o) {} + + ciObjArray(ciKlass* klass, int len) : ciArray(klass, len) {} + + objArrayOop get_objArrayOop() { + return (objArrayOop)get_oop(); + } + + const char* type_string() { return "ciObjArray"; } + +public: + // What kind of ciObject is this? + bool is_obj_array() { return true; } +}; diff --git a/hotspot/src/share/vm/ci/ciObjArrayKlass.cpp b/hotspot/src/share/vm/ci/ciObjArrayKlass.cpp new file mode 100644 index 00000000000..c5fc97cfdb4 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciObjArrayKlass.cpp @@ -0,0 +1,178 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciObjArrayKlass.cpp.incl" + +// ciObjArrayKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is an objArrayKlass. + +// ------------------------------------------------------------------ +// ciObjArrayKlass::ciObjArrayKlass +// +// Constructor for loaded object array klasses. +ciObjArrayKlass::ciObjArrayKlass(KlassHandle h_k) : ciArrayKlass(h_k) { + assert(get_Klass()->oop_is_objArray(), "wrong type"); + klassOop element_klassOop = get_objArrayKlass()->bottom_klass(); + _base_element_klass = CURRENT_ENV->get_object(element_klassOop)->as_klass(); + assert(_base_element_klass->is_instance_klass() || + _base_element_klass->is_type_array_klass(), "bad base klass"); + if (dimension() == 1) { + _element_klass = _base_element_klass; + } else { + _element_klass = NULL; + } + if (!ciObjectFactory::is_initialized()) { + assert(_element_klass->is_java_lang_Object(), "only arrays of object are shared"); + } +} + +// ------------------------------------------------------------------ +// ciObjArrayKlass::ciObjArrayKlass +// +// Constructor for unloaded object array klasses. +ciObjArrayKlass::ciObjArrayKlass(ciSymbol* array_name, + ciKlass* base_element_klass, + int dimension) + : ciArrayKlass(array_name, + dimension, + ciObjArrayKlassKlass::make()) { + _base_element_klass = base_element_klass; + assert(_base_element_klass->is_instance_klass() || + _base_element_klass->is_type_array_klass(), "bad base klass"); + if (dimension == 1) { + _element_klass = base_element_klass; + } else { + _element_klass = NULL; + } +} + +// ------------------------------------------------------------------ +// ciObjArrayKlass::element_klass +// +// What is the one-level element type of this array? +ciKlass* ciObjArrayKlass::element_klass() { + if (_element_klass == NULL) { + assert(dimension() > 1, "_element_klass should not be NULL"); + // Produce the element klass. + if (is_loaded()) { + VM_ENTRY_MARK; + klassOop element_klassOop = get_objArrayKlass()->element_klass(); + _element_klass = CURRENT_THREAD_ENV->get_object(element_klassOop)->as_klass(); + } else { + VM_ENTRY_MARK; + // We are an unloaded array klass. Attempt to fetch our + // element klass by name. + _element_klass = CURRENT_THREAD_ENV->get_klass_by_name_impl( + this, + construct_array_name(base_element_klass()->name(), + dimension() - 1), + false); + } + } + return _element_klass; +} + +// ------------------------------------------------------------------ +// ciObjArrayKlass::construct_array_name +// +// Build an array name from an element name and a dimension. +ciSymbol* ciObjArrayKlass::construct_array_name(ciSymbol* element_name, + int dimension) { + EXCEPTION_CONTEXT; + int element_len = element_name->utf8_length(); + + symbolOop base_name_sym = element_name->get_symbolOop(); + char* name; + + if (base_name_sym->byte_at(0) == '[' || + (base_name_sym->byte_at(0) == 'L' && // watch package name 'Lxx' + base_name_sym->byte_at(element_len-1) == ';')) { + + int new_len = element_len + dimension + 1; // for the ['s and '\0' + name = CURRENT_THREAD_ENV->name_buffer(new_len); + + int pos = 0; + for ( ; pos < dimension; pos++) { + name[pos] = '['; + } + strncpy(name+pos, (char*)element_name->base(), element_len); + name[new_len-1] = '\0'; + } else { + int new_len = 3 // for L, ;, and '\0' + + dimension // for ['s + + element_len; + + name = CURRENT_THREAD_ENV->name_buffer(new_len); + int pos = 0; + for ( ; pos < dimension; pos++) { + name[pos] = '['; + } + name[pos++] = 'L'; + strncpy(name+pos, (char*)element_name->base(), element_len); + name[new_len-2] = ';'; + name[new_len-1] = '\0'; + } + return ciSymbol::make(name); +} + +// ------------------------------------------------------------------ +// ciObjArrayKlass::make_impl +// +// Implementation of make. +ciObjArrayKlass* ciObjArrayKlass::make_impl(ciKlass* element_klass) { + + if (element_klass->is_loaded()) { + EXCEPTION_CONTEXT; + // The element klass is loaded + klassOop array = element_klass->get_Klass()->array_klass(THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + CURRENT_THREAD_ENV->record_out_of_memory_failure(); + return ciEnv::unloaded_ciobjarrayklass(); + } + return CURRENT_THREAD_ENV->get_object(array)->as_obj_array_klass(); + } + + // The array klass was unable to be made or the element klass was + // not loaded. + ciSymbol* array_name = construct_array_name(element_klass->name(), 1); + if (array_name == ciEnv::unloaded_cisymbol()) { + return ciEnv::unloaded_ciobjarrayklass(); + } + return + CURRENT_ENV->get_unloaded_klass(element_klass, array_name) + ->as_obj_array_klass(); +} + +// ------------------------------------------------------------------ +// ciObjArrayKlass::make +// +// Make an array klass corresponding to the specified primitive type. +ciObjArrayKlass* ciObjArrayKlass::make(ciKlass* element_klass) { + assert(element_klass->is_java_klass(), "wrong kind of klass"); + GUARDED_VM_ENTRY(return make_impl(element_klass);) +} diff --git a/hotspot/src/share/vm/ci/ciObjArrayKlass.hpp b/hotspot/src/share/vm/ci/ciObjArrayKlass.hpp new file mode 100644 index 00000000000..325f21c1685 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciObjArrayKlass.hpp @@ -0,0 +1,71 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciObjArrayKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is an objArrayKlass. +class ciObjArrayKlass : public ciArrayKlass { + CI_PACKAGE_ACCESS + friend class ciEnv; + +private: + ciKlass* _element_klass; + ciKlass* _base_element_klass; + +protected: + ciObjArrayKlass(KlassHandle h_k); + ciObjArrayKlass(ciSymbol* array_name, + ciKlass* base_element_klass, + int dimension); + + objArrayKlass* get_objArrayKlass() { + return (objArrayKlass*)get_Klass(); + } + + static ciObjArrayKlass* make_impl(ciKlass* element_klass); + static ciSymbol* construct_array_name(ciSymbol* element_name, + int dimension); + + const char* type_string() { return "ciObjArrayKlass"; } + + oop loader() { return _base_element_klass->loader(); } + jobject loader_handle() { return _base_element_klass->loader_handle(); } + + oop protection_domain() { return _base_element_klass->protection_domain(); } + jobject protection_domain_handle() { return _base_element_klass->protection_domain_handle(); } + + +public: + // The one-level type of the array elements. + ciKlass* element_klass(); + + // The innermost type of the array elements. + ciKlass* base_element_klass() { return _base_element_klass; } + + // What kind of ciObject is this? + bool is_obj_array_klass() { return true; } + + static ciObjArrayKlass* make(ciKlass* element_klass); +}; diff --git a/hotspot/src/share/vm/ci/ciObjArrayKlassKlass.cpp b/hotspot/src/share/vm/ci/ciObjArrayKlassKlass.cpp new file mode 100644 index 00000000000..d3f66f66830 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciObjArrayKlassKlass.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciObjArrayKlassKlass.cpp.incl" + +// ciObjArrayKlassKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is an arrayKlassKlass. + +// ------------------------------------------------------------------ +// ciObjArrayKlassKlass::instance +// +// Return the distinguished instance of this class +ciObjArrayKlassKlass* ciObjArrayKlassKlass::make() { + return CURRENT_ENV->_obj_array_klass_klass_instance; +} diff --git a/hotspot/src/share/vm/ci/ciObjArrayKlassKlass.hpp b/hotspot/src/share/vm/ci/ciObjArrayKlassKlass.hpp new file mode 100644 index 00000000000..a59e290e457 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciObjArrayKlassKlass.hpp @@ -0,0 +1,50 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciObjArrayKlassKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is a objArrayKlassKlass. +class ciObjArrayKlassKlass : public ciArrayKlassKlass { + CI_PACKAGE_ACCESS + +private: + ciObjArrayKlassKlass(KlassHandle h_k) + : ciArrayKlassKlass(h_k, ciSymbol::make("unique_objArrayKlassKlass")) { + assert(h_k()->klass_part()->oop_is_objArrayKlass(), "wrong type"); + } + + objArrayKlassKlass* get_objArrayKlassKlass() { + return (objArrayKlassKlass*)get_Klass(); + } + + const char* type_string() { return "ciObjArrayKlassKlass"; } + +public: + // What kind of ciObject is this? + bool is_obj_array_klass_klass() { return true; } + + // Return the distinguished ciObjArrayKlassKlass instance. + static ciObjArrayKlassKlass* make(); +}; diff --git a/hotspot/src/share/vm/ci/ciObject.cpp b/hotspot/src/share/vm/ci/ciObject.cpp new file mode 100644 index 00000000000..4be69e785d0 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciObject.cpp @@ -0,0 +1,215 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciObject.cpp.incl" + +// ciObject +// +// This class represents an oop in the HotSpot virtual machine. +// Its subclasses are structured in a hierarchy which mirrors +// an aggregate of the VM's oop and klass hierarchies (see +// oopHierarchy.hpp). Each instance of ciObject holds a handle +// to a corresponding oop on the VM side and provides routines +// for accessing the information in its oop. By using the ciObject +// hierarchy for accessing oops in the VM, the compiler ensures +// that it is safe with respect to garbage collection; that is, +// GC and compilation can proceed independently without +// interference. +// +// Within the VM, the oop and klass hierarchies are separate. +// The compiler interface does not preserve this separation -- +// the distinction between `klassOop' and `Klass' are not +// reflected in the interface and instead the Klass hierarchy +// is directly modeled as the subclasses of ciKlass. + +// ------------------------------------------------------------------ +// ciObject::ciObject +ciObject::ciObject(oop o) { + ASSERT_IN_VM; + if (ciObjectFactory::is_initialized()) { + _handle = JNIHandles::make_local(o); + } else { + _handle = JNIHandles::make_global(o); + } + _klass = NULL; + _ident = 0; +} + +// ------------------------------------------------------------------ +// ciObject::ciObject +// +ciObject::ciObject(Handle h) { + ASSERT_IN_VM; + if (ciObjectFactory::is_initialized()) { + _handle = JNIHandles::make_local(h()); + } else { + _handle = JNIHandles::make_global(h); + } + _klass = NULL; + _ident = 0; +} + +// ------------------------------------------------------------------ +// ciObject::ciObject +// +// Unloaded klass/method variant. `klass' is the klass of the unloaded +// klass/method, if that makes sense. +ciObject::ciObject(ciKlass* klass) { + ASSERT_IN_VM; + assert(klass != NULL, "must supply klass"); + _handle = NULL; + _klass = klass; + _ident = 0; +} + +// ------------------------------------------------------------------ +// ciObject::ciObject +// +// NULL variant. Used only by ciNullObject. +ciObject::ciObject() { + ASSERT_IN_VM; + _handle = NULL; + _klass = NULL; + _ident = 0; +} + +// ------------------------------------------------------------------ +// ciObject::klass +// +// Get the ciKlass of this ciObject. +ciKlass* ciObject::klass() { + if (_klass == NULL) { + if (_handle == NULL) { + // When both _klass and _handle are NULL, we are dealing + // with the distinguished instance of ciNullObject. + // No one should ask it for its klass. + assert(is_null_object(), "must be null object"); + ShouldNotReachHere(); + return NULL; + } + + GUARDED_VM_ENTRY( + oop o = get_oop(); + _klass = CURRENT_ENV->get_object(o->klass())->as_klass(); + ); + } + return _klass; +} + +// ------------------------------------------------------------------ +// ciObject::set_ident +// +// Set the unique identity number of a ciObject. +void ciObject::set_ident(uint id) { + assert((_ident >> FLAG_BITS) == 0, "must only initialize once"); + assert( id < ((uint)1 << (BitsPerInt-FLAG_BITS)), "id too big"); + _ident = _ident + (id << FLAG_BITS); +} + +// ------------------------------------------------------------------ +// ciObject::ident +// +// Report the unique identity number of a ciObject. +uint ciObject::ident() { + uint id = _ident >> FLAG_BITS; + assert(id != 0, "must be initialized"); + return id; +} + +// ------------------------------------------------------------------ +// ciObject::equals +// +// Are two ciObjects equal? +bool ciObject::equals(ciObject* obj) { + return (this == obj); +} + +// ------------------------------------------------------------------ +// ciObject::hash +// +// A hash value for the convenience of compilers. +// +// Implementation note: we use the address of the ciObject as the +// basis for the hash. Use the _ident field, which is well-behaved. +int ciObject::hash() { + return ident() * 31; +} + +// ------------------------------------------------------------------ +// ciObject::encoding +// +// The address which the compiler should embed into the +// generated code to represent this oop. This address +// is not the true address of the oop -- it will get patched +// during nmethod creation. +// +// +// +// Implementation note: we use the handle as the encoding. The +// nmethod constructor resolves the handle and patches in the oop. +// +// This method should be changed to return an generified address +// to discourage use of the JNI handle. +jobject ciObject::encoding() { + assert(is_null_object() || handle() != NULL, "cannot embed null pointer"); + assert(has_encoding(), "oop must be NULL or perm"); + return handle(); +} + +// ------------------------------------------------------------------ +// ciObject::has_encoding +bool ciObject::has_encoding() { + return handle() == NULL || is_perm(); +} + + +// ------------------------------------------------------------------ +// ciObject::print +// +// Print debugging output about this ciObject. +// +// Implementation note: dispatch to the virtual print_impl behavior +// for this ciObject. +void ciObject::print(outputStream* st) { + st->print("<%s", type_string()); + GUARDED_VM_ENTRY(print_impl(st);) + st->print(" ident=%d %s address=0x%x>", ident(), + is_perm() ? "PERM" : "", + (address)this); +} + +// ------------------------------------------------------------------ +// ciObject::print_oop +// +// Print debugging output about the oop this ciObject represents. +void ciObject::print_oop(outputStream* st) { + if (is_null_object()) { + st->print_cr("NULL"); + } else if (!is_loaded()) { + st->print_cr("UNLOADED"); + } else { + GUARDED_VM_ENTRY(get_oop()->print_on(st);) + } +} diff --git a/hotspot/src/share/vm/ci/ciObject.hpp b/hotspot/src/share/vm/ci/ciObject.hpp new file mode 100644 index 00000000000..30c07adf111 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciObject.hpp @@ -0,0 +1,260 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciObject +// +// This class represents an oop in the HotSpot virtual machine. +// Its subclasses are structured in a hierarchy which mirrors +// an aggregate of the VM's oop and klass hierarchies (see +// oopHierarchy.hpp). Each instance of ciObject holds a handle +// to a corresponding oop on the VM side and provides routines +// for accessing the information in its oop. By using the ciObject +// hierarchy for accessing oops in the VM, the compiler ensures +// that it is safe with respect to garbage collection; that is, +// GC and compilation can proceed independently without +// interference. +// +// Within the VM, the oop and klass hierarchies are separate. +// The compiler interface does not preserve this separation -- +// the distinction between `klassOop' and `Klass' are not +// reflected in the interface and instead the Klass hierarchy +// is directly modeled as the subclasses of ciKlass. +class ciObject : public ResourceObj { + CI_PACKAGE_ACCESS + friend class ciEnv; + +private: + // A JNI handle referring to an oop in the VM. This + // handle may, in a small set of cases, correctly be NULL. + jobject _handle; + ciKlass* _klass; + uint _ident; + + enum { FLAG_BITS = 1}; + enum { + PERM_FLAG = 1 + }; +protected: + ciObject(); + ciObject(oop o); + ciObject(Handle h); + ciObject(ciKlass* klass); + + jobject handle() const { return _handle; } + // Get the VM oop that this object holds. + oop get_oop() const { + assert(_handle != NULL, "null oop"); + return JNIHandles::resolve_non_null(_handle); + } + + void set_perm() { + _ident |= PERM_FLAG; + } + + // Virtual behavior of the print() method. + virtual void print_impl(outputStream* st) {} + + virtual const char* type_string() { return "ciObject"; } + + void set_ident(uint id); +public: + // The klass of this ciObject. + ciKlass* klass(); + + // A number unique to this object. + uint ident(); + + // Are two ciObjects equal? + bool equals(ciObject* obj); + + // A hash value for the convenience of compilers. + int hash(); + + // Tells if this oop has an encoding. (I.e., is it null or perm?) + // If it does not have an encoding, the compiler is responsible for + // making other arrangements for dealing with the oop. + // See ciEnv::make_perm_array + bool has_encoding(); + + // Is this object guaranteed to be in the permanent part of the heap? + // If so, CollectedHeap::can_elide_permanent_oop_store_barriers is relevant. + // If the answer is false, no guarantees are made. + bool is_perm() { return (_ident & PERM_FLAG) != 0; } + + // The address which the compiler should embed into the + // generated code to represent this oop. This address + // is not the true address of the oop -- it will get patched + // during nmethod creation. + // + // Usage note: no address arithmetic allowed. Oop must + // be registered with the oopRecorder. + jobject encoding(); + + // What kind of ciObject is this? + virtual bool is_null_object() const { return false; } + virtual bool is_instance() { return false; } + virtual bool is_method() { return false; } + virtual bool is_method_data() { return false; } + virtual bool is_array() { return false; } + virtual bool is_obj_array() { return false; } + virtual bool is_type_array() { return false; } + virtual bool is_symbol() { return false; } + virtual bool is_type() { return false; } + virtual bool is_return_address() { return false; } + virtual bool is_klass() { return false; } + virtual bool is_instance_klass() { return false; } + virtual bool is_method_klass() { return false; } + virtual bool is_array_klass() { return false; } + virtual bool is_obj_array_klass() { return false; } + virtual bool is_type_array_klass() { return false; } + virtual bool is_symbol_klass() { return false; } + virtual bool is_klass_klass() { return false; } + virtual bool is_instance_klass_klass() { return false; } + virtual bool is_array_klass_klass() { return false; } + virtual bool is_obj_array_klass_klass() { return false; } + virtual bool is_type_array_klass_klass() { return false; } + + // Is this a type or value which has no associated class? + // It is true of primitive types and null objects. + virtual bool is_classless() const { return false; } + + // Is this ciObject a Java Language Object? That is, + // is the ciObject an instance or an array + virtual bool is_java_object() { return false; } + + // Does this ciObject represent a Java Language class? + // That is, is the ciObject an instanceKlass or arrayKlass? + virtual bool is_java_klass() { return false; } + + // Is this ciObject the ciInstanceKlass representing + // java.lang.Object()? + virtual bool is_java_lang_Object() { return false; } + + // Does this ciObject refer to a real oop in the VM? + // + // Note: some ciObjects refer to oops which have yet to be + // created. We refer to these as "unloaded". Specifically, + // there are unloaded ciMethods, ciObjArrayKlasses, and + // ciInstanceKlasses. By convention the ciNullObject is + // considered loaded, and primitive types are considered loaded. + bool is_loaded() const { + return handle() != NULL || is_classless(); + } + + // Subclass casting with assertions. + ciNullObject* as_null_object() { + assert(is_null_object(), "bad cast"); + return (ciNullObject*)this; + } + ciInstance* as_instance() { + assert(is_instance(), "bad cast"); + return (ciInstance*)this; + } + ciMethod* as_method() { + assert(is_method(), "bad cast"); + return (ciMethod*)this; + } + ciMethodData* as_method_data() { + assert(is_method_data(), "bad cast"); + return (ciMethodData*)this; + } + ciArray* as_array() { + assert(is_array(), "bad cast"); + return (ciArray*)this; + } + ciObjArray* as_obj_array() { + assert(is_obj_array(), "bad cast"); + return (ciObjArray*)this; + } + ciTypeArray* as_type_array() { + assert(is_type_array(), "bad cast"); + return (ciTypeArray*)this; + } + ciSymbol* as_symbol() { + assert(is_symbol(), "bad cast"); + return (ciSymbol*)this; + } + ciType* as_type() { + assert(is_type(), "bad cast"); + return (ciType*)this; + } + ciReturnAddress* as_return_address() { + assert(is_return_address(), "bad cast"); + return (ciReturnAddress*)this; + } + ciKlass* as_klass() { + assert(is_klass(), "bad cast"); + return (ciKlass*)this; + } + ciInstanceKlass* as_instance_klass() { + assert(is_instance_klass(), "bad cast"); + return (ciInstanceKlass*)this; + } + ciMethodKlass* as_method_klass() { + assert(is_method_klass(), "bad cast"); + return (ciMethodKlass*)this; + } + ciArrayKlass* as_array_klass() { + assert(is_array_klass(), "bad cast"); + return (ciArrayKlass*)this; + } + ciObjArrayKlass* as_obj_array_klass() { + assert(is_obj_array_klass(), "bad cast"); + return (ciObjArrayKlass*)this; + } + ciTypeArrayKlass* as_type_array_klass() { + assert(is_type_array_klass(), "bad cast"); + return (ciTypeArrayKlass*)this; + } + ciSymbolKlass* as_symbol_klass() { + assert(is_symbol_klass(), "bad cast"); + return (ciSymbolKlass*)this; + } + ciKlassKlass* as_klass_klass() { + assert(is_klass_klass(), "bad cast"); + return (ciKlassKlass*)this; + } + ciInstanceKlassKlass* as_instance_klass_klass() { + assert(is_instance_klass_klass(), "bad cast"); + return (ciInstanceKlassKlass*)this; + } + ciArrayKlassKlass* as_array_klass_klass() { + assert(is_array_klass_klass(), "bad cast"); + return (ciArrayKlassKlass*)this; + } + ciObjArrayKlassKlass* as_obj_array_klass_klass() { + assert(is_obj_array_klass_klass(), "bad cast"); + return (ciObjArrayKlassKlass*)this; + } + ciTypeArrayKlassKlass* as_type_array_klass_klass() { + assert(is_type_array_klass_klass(), "bad cast"); + return (ciTypeArrayKlassKlass*)this; + } + + // Print debugging output about this ciObject. + void print(outputStream* st = tty); + + // Print debugging output about the oop this ciObject represents. + void print_oop(outputStream* st = tty); +}; diff --git a/hotspot/src/share/vm/ci/ciObjectFactory.cpp b/hotspot/src/share/vm/ci/ciObjectFactory.cpp new file mode 100644 index 00000000000..52612b55b0e --- /dev/null +++ b/hotspot/src/share/vm/ci/ciObjectFactory.cpp @@ -0,0 +1,647 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciObjectFactory.cpp.incl" + +// ciObjectFactory +// +// This class handles requests for the creation of new instances +// of ciObject and its subclasses. It contains a caching mechanism +// which ensures that for each oop, at most one ciObject is created. +// This invariant allows more efficient implementation of ciObject. +// +// Implementation note: the oop->ciObject mapping is represented as +// a table stored in an array. Even though objects are moved +// by the garbage collector, the compactor preserves their relative +// order; address comparison of oops (in perm space) is safe so long +// as we prohibit GC during our comparisons. We currently use binary +// search to find the oop in the table, and inserting a new oop +// into the table may be costly. If this cost ends up being +// problematic the underlying data structure can be switched to some +// sort of balanced binary tree. + +GrowableArray* ciObjectFactory::_shared_ci_objects = NULL; +ciSymbol* ciObjectFactory::_shared_ci_symbols[vmSymbols::SID_LIMIT]; +int ciObjectFactory::_shared_ident_limit = 0; +volatile bool ciObjectFactory::_initialized = false; + + +// ------------------------------------------------------------------ +// ciObjectFactory::ciObjectFactory +ciObjectFactory::ciObjectFactory(Arena* arena, + int expected_size) { + + for (int i = 0; i < NON_PERM_BUCKETS; i++) { + _non_perm_bucket[i] = NULL; + } + _non_perm_count = 0; + + _next_ident = _shared_ident_limit; + _arena = arena; + _ci_objects = new (arena) GrowableArray(arena, expected_size, 0, NULL); + + // If the shared ci objects exist append them to this factory's objects + + if (_shared_ci_objects != NULL) { + _ci_objects->appendAll(_shared_ci_objects); + } + + _unloaded_methods = new (arena) GrowableArray(arena, 4, 0, NULL); + _unloaded_klasses = new (arena) GrowableArray(arena, 8, 0, NULL); + _return_addresses = + new (arena) GrowableArray(arena, 8, 0, NULL); +} + +// ------------------------------------------------------------------ +// ciObjectFactory::ciObjectFactory +void ciObjectFactory::initialize() { + ASSERT_IN_VM; + JavaThread* thread = JavaThread::current(); + HandleMark handle_mark(thread); + + // This Arena is long lived and exists in the resource mark of the + // compiler thread that initializes the initial ciObjectFactory which + // creates the shared ciObjects that all later ciObjectFactories use. + Arena* arena = new Arena(); + ciEnv initial(arena); + ciEnv* env = ciEnv::current(); + env->_factory->init_shared_objects(); + + _initialized = true; + +} + +void ciObjectFactory::init_shared_objects() { + + _next_ident = 1; // start numbering CI objects at 1 + + { + // Create the shared symbols, but not in _shared_ci_objects. + int i; + for (i = vmSymbols::FIRST_SID; i < vmSymbols::SID_LIMIT; i++) { + symbolHandle sym_handle = vmSymbolHandles::symbol_handle_at((vmSymbols::SID) i); + assert(vmSymbols::find_sid(sym_handle()) == i, "1-1 mapping"); + ciSymbol* sym = new (_arena) ciSymbol(sym_handle); + init_ident_of(sym); + _shared_ci_symbols[i] = sym; + } +#ifdef ASSERT + for (i = vmSymbols::FIRST_SID; i < vmSymbols::SID_LIMIT; i++) { + symbolHandle sym_handle = vmSymbolHandles::symbol_handle_at((vmSymbols::SID) i); + ciSymbol* sym = vm_symbol_at((vmSymbols::SID) i); + assert(sym->get_oop() == sym_handle(), "oop must match"); + } + assert(ciSymbol::void_class_signature()->get_oop() == vmSymbols::void_class_signature(), "spot check"); +#endif + } + + _ci_objects = new (_arena) GrowableArray(_arena, 64, 0, NULL); + + for (int i = T_BOOLEAN; i <= T_CONFLICT; i++) { + BasicType t = (BasicType)i; + if (type2name(t) != NULL && t != T_OBJECT && t != T_ARRAY) { + ciType::_basic_types[t] = new (_arena) ciType(t); + init_ident_of(ciType::_basic_types[t]); + } + } + + ciEnv::_null_object_instance = new (_arena) ciNullObject(); + init_ident_of(ciEnv::_null_object_instance); + ciEnv::_method_klass_instance = + get(Universe::methodKlassObj())->as_method_klass(); + ciEnv::_symbol_klass_instance = + get(Universe::symbolKlassObj())->as_symbol_klass(); + ciEnv::_klass_klass_instance = + get(Universe::klassKlassObj())->as_klass_klass(); + ciEnv::_instance_klass_klass_instance = + get(Universe::instanceKlassKlassObj()) + ->as_instance_klass_klass(); + ciEnv::_type_array_klass_klass_instance = + get(Universe::typeArrayKlassKlassObj()) + ->as_type_array_klass_klass(); + ciEnv::_obj_array_klass_klass_instance = + get(Universe::objArrayKlassKlassObj()) + ->as_obj_array_klass_klass(); + ciEnv::_ArrayStoreException = + get(SystemDictionary::ArrayStoreException_klass()) + ->as_instance_klass(); + ciEnv::_Class = + get(SystemDictionary::class_klass()) + ->as_instance_klass(); + ciEnv::_ClassCastException = + get(SystemDictionary::ClassCastException_klass()) + ->as_instance_klass(); + ciEnv::_Object = + get(SystemDictionary::object_klass()) + ->as_instance_klass(); + ciEnv::_Throwable = + get(SystemDictionary::throwable_klass()) + ->as_instance_klass(); + ciEnv::_Thread = + get(SystemDictionary::thread_klass()) + ->as_instance_klass(); + ciEnv::_OutOfMemoryError = + get(SystemDictionary::OutOfMemoryError_klass()) + ->as_instance_klass(); + ciEnv::_String = + get(SystemDictionary::string_klass()) + ->as_instance_klass(); + + for (int len = -1; len != _ci_objects->length(); ) { + len = _ci_objects->length(); + for (int i2 = 0; i2 < len; i2++) { + ciObject* obj = _ci_objects->at(i2); + if (obj->is_loaded() && obj->is_instance_klass()) { + obj->as_instance_klass()->compute_nonstatic_fields(); + } + } + } + + ciEnv::_unloaded_cisymbol = (ciSymbol*) ciObjectFactory::get(vmSymbols::dummy_symbol_oop()); + // Create dummy instanceKlass and objArrayKlass object and assign them idents + ciEnv::_unloaded_ciinstance_klass = new (_arena) ciInstanceKlass(ciEnv::_unloaded_cisymbol, NULL, NULL); + init_ident_of(ciEnv::_unloaded_ciinstance_klass); + ciEnv::_unloaded_ciobjarrayklass = new (_arena) ciObjArrayKlass(ciEnv::_unloaded_cisymbol, ciEnv::_unloaded_ciinstance_klass, 1); + init_ident_of(ciEnv::_unloaded_ciobjarrayklass); + assert(ciEnv::_unloaded_ciobjarrayklass->is_obj_array_klass(), "just checking"); + + get(Universe::boolArrayKlassObj()); + get(Universe::charArrayKlassObj()); + get(Universe::singleArrayKlassObj()); + get(Universe::doubleArrayKlassObj()); + get(Universe::byteArrayKlassObj()); + get(Universe::shortArrayKlassObj()); + get(Universe::intArrayKlassObj()); + get(Universe::longArrayKlassObj()); + + + + assert(_non_perm_count == 0, "no shared non-perm objects"); + + // The shared_ident_limit is the first ident number that will + // be used for non-shared objects. That is, numbers less than + // this limit are permanently assigned to shared CI objects, + // while the higher numbers are recycled afresh by each new ciEnv. + + _shared_ident_limit = _next_ident; + _shared_ci_objects = _ci_objects; +} + +// ------------------------------------------------------------------ +// ciObjectFactory::get +// +// Get the ciObject corresponding to some oop. If the ciObject has +// already been created, it is returned. Otherwise, a new ciObject +// is created. +ciObject* ciObjectFactory::get(oop key) { + ASSERT_IN_VM; + +#ifdef ASSERT + oop last = NULL; + for (int j = 0; j< _ci_objects->length(); j++) { + oop o = _ci_objects->at(j)->get_oop(); + assert(last < o, "out of order"); + last = o; + } +#endif // ASSERT + int len = _ci_objects->length(); + int index = find(key, _ci_objects); +#ifdef ASSERT + for (int i=0; i<_ci_objects->length(); i++) { + if (_ci_objects->at(i)->get_oop() == key) { + assert(index == i, " bad lookup"); + } + } +#endif + if (!is_found_at(index, key, _ci_objects)) { + + // Check in the non-perm area before putting it in the list. + NonPermObject* &bucket = find_non_perm(key); + if (bucket != NULL) { + return bucket->object(); + } + + // Check in the shared symbol area before putting it in the list. + if (key->is_symbol()) { + vmSymbols::SID sid = vmSymbols::find_sid((symbolOop)key); + if (sid != vmSymbols::NO_SID) { + // do not pollute the main cache with it + return vm_symbol_at(sid); + } + } + + // The ciObject does not yet exist. Create it and insert it + // into the cache. + Handle keyHandle(key); + ciObject* new_object = create_new_object(keyHandle()); + assert(keyHandle() == new_object->get_oop(), "must be properly recorded"); + init_ident_of(new_object); + if (!keyHandle->is_perm()) { + // Not a perm-space object. + insert_non_perm(bucket, keyHandle(), new_object); + return new_object; + } + new_object->set_perm(); + if (len != _ci_objects->length()) { + // creating the new object has recursively entered new objects + // into the table. We need to recompute our index. + index = find(keyHandle(), _ci_objects); + } + assert(!is_found_at(index, keyHandle(), _ci_objects), "no double insert"); + insert(index, new_object, _ci_objects); + return new_object; + } + return _ci_objects->at(index); +} + +// ------------------------------------------------------------------ +// ciObjectFactory::create_new_object +// +// Create a new ciObject from an oop. +// +// Implementation note: this functionality could be virtual behavior +// of the oop itself. For now, we explicitly marshal the object. +ciObject* ciObjectFactory::create_new_object(oop o) { + EXCEPTION_CONTEXT; + + if (o->is_symbol()) { + symbolHandle h_o(THREAD, (symbolOop)o); + return new (arena()) ciSymbol(h_o); + } else if (o->is_klass()) { + KlassHandle h_k(THREAD, (klassOop)o); + Klass* k = ((klassOop)o)->klass_part(); + if (k->oop_is_instance()) { + return new (arena()) ciInstanceKlass(h_k); + } else if (k->oop_is_objArray()) { + return new (arena()) ciObjArrayKlass(h_k); + } else if (k->oop_is_typeArray()) { + return new (arena()) ciTypeArrayKlass(h_k); + } else if (k->oop_is_method()) { + return new (arena()) ciMethodKlass(h_k); + } else if (k->oop_is_symbol()) { + return new (arena()) ciSymbolKlass(h_k); + } else if (k->oop_is_klass()) { + if (k->oop_is_objArrayKlass()) { + return new (arena()) ciObjArrayKlassKlass(h_k); + } else if (k->oop_is_typeArrayKlass()) { + return new (arena()) ciTypeArrayKlassKlass(h_k); + } else if (k->oop_is_instanceKlass()) { + return new (arena()) ciInstanceKlassKlass(h_k); + } else { + assert(o == Universe::klassKlassObj(), "bad klassKlass"); + return new (arena()) ciKlassKlass(h_k); + } + } + } else if (o->is_method()) { + methodHandle h_m(THREAD, (methodOop)o); + return new (arena()) ciMethod(h_m); + } else if (o->is_methodData()) { + methodDataHandle h_md(THREAD, (methodDataOop)o); + return new (arena()) ciMethodData(h_md); + } else if (o->is_instance()) { + instanceHandle h_i(THREAD, (instanceOop)o); + return new (arena()) ciInstance(h_i); + } else if (o->is_objArray()) { + objArrayHandle h_oa(THREAD, (objArrayOop)o); + return new (arena()) ciObjArray(h_oa); + } else if (o->is_typeArray()) { + typeArrayHandle h_ta(THREAD, (typeArrayOop)o); + return new (arena()) ciTypeArray(h_ta); + } + + // The oop is of some type not supported by the compiler interface. + ShouldNotReachHere(); + return NULL; +} + +//------------------------------------------------------------------ +// ciObjectFactory::get_unloaded_method +// +// Get the ciMethod representing an unloaded/unfound method. +// +// Implementation note: unloaded methods are currently stored in +// an unordered array, requiring a linear-time lookup for each +// unloaded method. This may need to change. +ciMethod* ciObjectFactory::get_unloaded_method(ciInstanceKlass* holder, + ciSymbol* name, + ciSymbol* signature) { + for (int i=0; i<_unloaded_methods->length(); i++) { + ciMethod* entry = _unloaded_methods->at(i); + if (entry->holder()->equals(holder) && + entry->name()->equals(name) && + entry->signature()->as_symbol()->equals(signature)) { + // We've found a match. + return entry; + } + } + + // This is a new unloaded method. Create it and stick it in + // the cache. + ciMethod* new_method = new (arena()) ciMethod(holder, name, signature); + + init_ident_of(new_method); + _unloaded_methods->append(new_method); + + return new_method; +} + +//------------------------------------------------------------------ +// ciObjectFactory::get_unloaded_klass +// +// Get a ciKlass representing an unloaded klass. +// +// Implementation note: unloaded klasses are currently stored in +// an unordered array, requiring a linear-time lookup for each +// unloaded klass. This may need to change. +ciKlass* ciObjectFactory::get_unloaded_klass(ciKlass* accessing_klass, + ciSymbol* name, + bool create_if_not_found) { + EXCEPTION_CONTEXT; + oop loader = NULL; + oop domain = NULL; + if (accessing_klass != NULL) { + loader = accessing_klass->loader(); + domain = accessing_klass->protection_domain(); + } + for (int i=0; i<_unloaded_klasses->length(); i++) { + ciKlass* entry = _unloaded_klasses->at(i); + if (entry->name()->equals(name) && + entry->loader() == loader && + entry->protection_domain() == domain) { + // We've found a match. + return entry; + } + } + + if (!create_if_not_found) + return NULL; + + // This is a new unloaded klass. Create it and stick it in + // the cache. + ciKlass* new_klass = NULL; + + // Two cases: this is an unloaded objArrayKlass or an + // unloaded instanceKlass. Deal with both. + if (name->byte_at(0) == '[') { + // Decompose the name.' + jint dimension = 0; + symbolOop element_name = NULL; + BasicType element_type= FieldType::get_array_info(name->get_symbolOop(), + &dimension, + &element_name, + THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + CURRENT_THREAD_ENV->record_out_of_memory_failure(); + return ciEnv::_unloaded_ciobjarrayklass; + } + assert(element_type != T_ARRAY, "unsuccessful decomposition"); + ciKlass* element_klass = NULL; + if (element_type == T_OBJECT) { + ciEnv *env = CURRENT_THREAD_ENV; + ciSymbol* ci_name = env->get_object(element_name)->as_symbol(); + element_klass = + env->get_klass_by_name(accessing_klass, ci_name, false)->as_instance_klass(); + } else { + assert(dimension > 1, "one dimensional type arrays are always loaded."); + + // The type array itself takes care of one of the dimensions. + dimension--; + + // The element klass is a typeArrayKlass. + element_klass = ciTypeArrayKlass::make(element_type); + } + new_klass = new (arena()) ciObjArrayKlass(name, element_klass, dimension); + } else { + jobject loader_handle = NULL; + jobject domain_handle = NULL; + if (accessing_klass != NULL) { + loader_handle = accessing_klass->loader_handle(); + domain_handle = accessing_klass->protection_domain_handle(); + } + new_klass = new (arena()) ciInstanceKlass(name, loader_handle, domain_handle); + } + init_ident_of(new_klass); + _unloaded_klasses->append(new_klass); + + return new_klass; +} + +//------------------------------------------------------------------ +// ciObjectFactory::get_empty_methodData +// +// Get the ciMethodData representing the methodData for a method with +// none. +ciMethodData* ciObjectFactory::get_empty_methodData() { + ciMethodData* new_methodData = new (arena()) ciMethodData(); + init_ident_of(new_methodData); + return new_methodData; +} + +//------------------------------------------------------------------ +// ciObjectFactory::get_return_address +// +// Get a ciReturnAddress for a specified bci. +ciReturnAddress* ciObjectFactory::get_return_address(int bci) { + for (int i=0; i<_return_addresses->length(); i++) { + ciReturnAddress* entry = _return_addresses->at(i); + if (entry->bci() == bci) { + // We've found a match. + return entry; + } + } + + ciReturnAddress* new_ret_addr = new (arena()) ciReturnAddress(bci); + init_ident_of(new_ret_addr); + _return_addresses->append(new_ret_addr); + return new_ret_addr; +} + +// ------------------------------------------------------------------ +// ciObjectFactory::init_ident_of +void ciObjectFactory::init_ident_of(ciObject* obj) { + obj->set_ident(_next_ident++); +} + + +// ------------------------------------------------------------------ +// ciObjectFactory::find +// +// Use binary search to find the position of this oop in the cache. +// If there is no entry in the cache corresponding to this oop, return +// the position at which the oop should be inserted. +int ciObjectFactory::find(oop key, GrowableArray* objects) { + int min = 0; + int max = objects->length()-1; + + // print_contents(); + + while (max >= min) { + int mid = (max + min) / 2; + oop value = objects->at(mid)->get_oop(); + if (value < key) { + min = mid + 1; + } else if (value > key) { + max = mid - 1; + } else { + return mid; + } + } + return min; +} + +// ------------------------------------------------------------------ +// ciObjectFactory::is_found_at +// +// Verify that the binary seach found the given key. +bool ciObjectFactory::is_found_at(int index, oop key, GrowableArray* objects) { + return (index < objects->length() && + objects->at(index)->get_oop() == key); +} + + +// ------------------------------------------------------------------ +// ciObjectFactory::insert +// +// Insert a ciObject into the table at some index. +void ciObjectFactory::insert(int index, ciObject* obj, GrowableArray* objects) { + int len = objects->length(); + if (len == index) { + objects->append(obj); + } else { + objects->append(objects->at(len-1)); + int pos; + for (pos = len-2; pos >= index; pos--) { + objects->at_put(pos+1,objects->at(pos)); + } + objects->at_put(index, obj); + } +#ifdef ASSERT + oop last = NULL; + for (int j = 0; j< objects->length(); j++) { + oop o = objects->at(j)->get_oop(); + assert(last < o, "out of order"); + last = o; + } +#endif // ASSERT +} + +static ciObjectFactory::NonPermObject* emptyBucket = NULL; + +// ------------------------------------------------------------------ +// ciObjectFactory::find_non_perm +// +// Use a small hash table, hashed on the klass of the key. +// If there is no entry in the cache corresponding to this oop, return +// the null tail of the bucket into which the oop should be inserted. +ciObjectFactory::NonPermObject* &ciObjectFactory::find_non_perm(oop key) { + // Be careful: is_perm might change from false to true. + // Thus, there might be a matching perm object in the table. + // If there is, this probe must find it. + if (key->is_perm() && _non_perm_count == 0) { + return emptyBucket; + } else if (key->is_instance()) { + if (key->klass() == SystemDictionary::class_klass()) { + // class mirror instances are always perm + return emptyBucket; + } + // fall through to probe + } else if (key->is_array()) { + // fall through to probe + } else { + // not an array or instance + return emptyBucket; + } + + ciObject* klass = get(key->klass()); + NonPermObject* *bp = &_non_perm_bucket[(unsigned) klass->hash() % NON_PERM_BUCKETS]; + for (NonPermObject* p; (p = (*bp)) != NULL; bp = &p->next()) { + if (is_equal(p, key)) break; + } + return (*bp); +} + + + +// ------------------------------------------------------------------ +// Code for for NonPermObject +// +inline ciObjectFactory::NonPermObject::NonPermObject(ciObjectFactory::NonPermObject* &bucket, oop key, ciObject* object) { + assert(ciObjectFactory::is_initialized(), ""); + _object = object; + _next = bucket; + bucket = this; +} + + + +// ------------------------------------------------------------------ +// ciObjectFactory::insert_non_perm +// +// Insert a ciObject into the non-perm table. +void ciObjectFactory::insert_non_perm(ciObjectFactory::NonPermObject* &where, oop key, ciObject* obj) { + assert(&where != &emptyBucket, "must not try to fill empty bucket"); + NonPermObject* p = new (arena()) NonPermObject(where, key, obj); + assert(where == p && is_equal(p, key) && p->object() == obj, "entry must match"); + assert(find_non_perm(key) == p, "must find the same spot"); + ++_non_perm_count; +} + +// ------------------------------------------------------------------ +// ciObjectFactory::vm_symbol_at +// Get the ciSymbol corresponding to some index in vmSymbols. +ciSymbol* ciObjectFactory::vm_symbol_at(int index) { + assert(index >= vmSymbols::FIRST_SID && index < vmSymbols::SID_LIMIT, "oob"); + return _shared_ci_symbols[index]; +} + +// ------------------------------------------------------------------ +// ciObjectFactory::print_contents_impl +void ciObjectFactory::print_contents_impl() { + int len = _ci_objects->length(); + tty->print_cr("ciObjectFactory (%d) oop contents:", len); + for (int i=0; iat(i)->print(); + tty->cr(); + } +} + +// ------------------------------------------------------------------ +// ciObjectFactory::print_contents +void ciObjectFactory::print_contents() { + print(); + tty->cr(); + GUARDED_VM_ENTRY(print_contents_impl();) +} + +// ------------------------------------------------------------------ +// ciObjectFactory::print +// +// Print debugging information about the object factory +void ciObjectFactory::print() { + tty->print("", + _ci_objects->length(), _unloaded_methods->length(), + _unloaded_klasses->length()); +} diff --git a/hotspot/src/share/vm/ci/ciObjectFactory.hpp b/hotspot/src/share/vm/ci/ciObjectFactory.hpp new file mode 100644 index 00000000000..0e312aba893 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciObjectFactory.hpp @@ -0,0 +1,110 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciObjectFactory +// +// This class handles requests for the creation of new instances +// of ciObject and its subclasses. It contains a caching mechanism +// which ensures that for each oop, at most one ciObject is created. +// This invariant allows efficient implementation of ciObject. +class ciObjectFactory : public ResourceObj { +private: + static volatile bool _initialized; + static GrowableArray* _shared_ci_objects; + static ciSymbol* _shared_ci_symbols[]; + static int _shared_ident_limit; + + Arena* _arena; + GrowableArray* _ci_objects; + GrowableArray* _unloaded_methods; + GrowableArray* _unloaded_klasses; + GrowableArray* _return_addresses; + int _next_ident; + +public: + struct NonPermObject : public ResourceObj { + ciObject* _object; + NonPermObject* _next; + + inline NonPermObject(NonPermObject* &bucket, oop key, ciObject* object); + ciObject* object() { return _object; } + NonPermObject* &next() { return _next; } + }; +private: + enum { NON_PERM_BUCKETS = 61 }; + NonPermObject* _non_perm_bucket[NON_PERM_BUCKETS]; + int _non_perm_count; + + int find(oop key, GrowableArray* objects); + bool is_found_at(int index, oop key, GrowableArray* objects); + void insert(int index, ciObject* obj, GrowableArray* objects); + ciObject* create_new_object(oop o); + static bool is_equal(NonPermObject* p, oop key) { + return p->object()->get_oop() == key; + } + + NonPermObject* &find_non_perm(oop key); + void insert_non_perm(NonPermObject* &where, oop key, ciObject* obj); + + void init_ident_of(ciObject* obj); + + Arena* arena() { return _arena; } + + void print_contents_impl(); + +public: + static bool is_initialized() { return _initialized; } + + static void initialize(); + void init_shared_objects(); + + ciObjectFactory(Arena* arena, int expected_size); + + + // Get the ciObject corresponding to some oop. + ciObject* get(oop key); + + // Get the ciSymbol corresponding to one of the vmSymbols. + static ciSymbol* vm_symbol_at(int index); + + // Get the ciMethod representing an unloaded/unfound method. + ciMethod* get_unloaded_method(ciInstanceKlass* holder, + ciSymbol* name, + ciSymbol* signature); + + // Get a ciKlass representing an unloaded klass. + ciKlass* get_unloaded_klass(ciKlass* accessing_klass, + ciSymbol* name, + bool create_if_not_found); + + + // Get the ciMethodData representing the methodData for a method + // with none. + ciMethodData* get_empty_methodData(); + + ciReturnAddress* get_return_address(int bci); + + void print_contents(); + void print(); +}; diff --git a/hotspot/src/share/vm/ci/ciSignature.cpp b/hotspot/src/share/vm/ci/ciSignature.cpp new file mode 100644 index 00000000000..b4ae815e2c8 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciSignature.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciSignature.cpp.incl" + +// ciSignature +// +// This class represents the signature of a method. + +// ------------------------------------------------------------------ +// ciSignature::ciSignature +ciSignature::ciSignature(ciKlass* accessing_klass, ciSymbol* symbol) { + ASSERT_IN_VM; + EXCEPTION_CONTEXT; + _accessing_klass = accessing_klass; + _symbol = symbol; + + ciEnv* env = CURRENT_ENV; + Arena* arena = env->arena(); + _types = new (arena) GrowableArray(arena, 8, 0, NULL); + + int size = 0; + int count = 0; + symbolHandle sh (THREAD, symbol->get_symbolOop()); + SignatureStream ss(sh); + for (; ; ss.next()) { + // Process one element of the signature + ciType* type; + if (!ss.is_object()) { + type = ciType::make(ss.type()); + } else { + symbolOop name = ss.as_symbol(THREAD); + if (HAS_PENDING_EXCEPTION) { + type = ss.is_array() ? (ciType*)ciEnv::unloaded_ciobjarrayklass() + : (ciType*)ciEnv::unloaded_ciinstance_klass(); + env->record_out_of_memory_failure(); + CLEAR_PENDING_EXCEPTION; + } else { + ciSymbol* klass_name = env->get_object(name)->as_symbol(); + type = env->get_klass_by_name_impl(_accessing_klass, klass_name, false); + } + } + _types->append(type); + if (ss.at_return_type()) { + // Done processing the return type; do not add it into the count. + break; + } + size += type->size(); + count++; + } + _size = size; + _count = count; +} + +// ------------------------------------------------------------------ +// ciSignature::return_ciType +// +// What is the return type of this signature? +ciType* ciSignature::return_type() const { + return _types->at(_count); +} + +// ------------------------------------------------------------------ +// ciSignature::ciType_at +// +// What is the type of the index'th element of this +// signature? +ciType* ciSignature::type_at(int index) const { + assert(index < _count, "out of bounds"); + // The first _klasses element holds the return klass. + return _types->at(index); +} + +// ------------------------------------------------------------------ +// ciSignature::print_signature +void ciSignature::print_signature() { + _symbol->print_symbol(); +} + +// ------------------------------------------------------------------ +// ciSignature::print +void ciSignature::print() { + tty->print("", (address)this); +} diff --git a/hotspot/src/share/vm/ci/ciSignature.hpp b/hotspot/src/share/vm/ci/ciSignature.hpp new file mode 100644 index 00000000000..468b18cbb92 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciSignature.hpp @@ -0,0 +1,56 @@ +/* + * Copyright 1999-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciSignature +// +// This class represents the signature of a method. +class ciSignature : public ResourceObj { +private: + ciSymbol* _symbol; + ciKlass* _accessing_klass; + + GrowableArray* _types; + int _size; + int _count; + + friend class ciMethod; + + ciSignature(ciKlass* accessing_klass, ciSymbol* signature); + + void get_all_klasses(); + + symbolOop get_symbolOop() const { return _symbol->get_symbolOop(); } + +public: + ciSymbol* as_symbol() const { return _symbol; } + + ciType* return_type() const; + ciType* type_at(int index) const; + + int size() const { return _size; } + int count() const { return _count; } + + void print_signature(); + void print(); +}; diff --git a/hotspot/src/share/vm/ci/ciStreams.cpp b/hotspot/src/share/vm/ci/ciStreams.cpp new file mode 100644 index 00000000000..577c06d8572 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciStreams.cpp @@ -0,0 +1,367 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciStreams.cpp.incl" + +// ciExceptionHandlerStream +// +// Walk over some selected set of a methods exception handlers. + +// ------------------------------------------------------------------ +// ciExceptionHandlerStream::count +// +// How many exception handlers are there in this stream? +// +// Implementation note: Compiler2 needs this functionality, so I had +int ciExceptionHandlerStream::count() { + int save_pos = _pos; + int save_end = _end; + + int count = 0; + + _pos = -1; + _end = _method->_handler_count; + + + next(); + while (!is_done()) { + count++; + next(); + } + + _pos = save_pos; + _end = save_end; + + return count; +} + +int ciExceptionHandlerStream::count_remaining() { + int save_pos = _pos; + int save_end = _end; + + int count = 0; + + while (!is_done()) { + count++; + next(); + } + + _pos = save_pos; + _end = save_end; + + return count; +} + +// ciBytecodeStream +// +// The class is used to iterate over the bytecodes of a method. +// It hides the details of constant pool structure/access by +// providing accessors for constant pool items. + +// ------------------------------------------------------------------ +// ciBytecodeStream::wide +// +// Special handling for the wide bytcode +Bytecodes::Code ciBytecodeStream::wide() +{ + // Get following bytecode; do not return wide + Bytecodes::Code bc = (Bytecodes::Code)_pc[1]; + _pc += 2; // Skip both bytecodes + _pc += 2; // Skip index always + if( bc == Bytecodes::_iinc ) + _pc += 2; // Skip optional constant + _was_wide = _pc; // Flag last wide bytecode found + return bc; +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::table +// +// Special handling for switch ops +Bytecodes::Code ciBytecodeStream::table( Bytecodes::Code bc ) { + switch( bc ) { // Check for special bytecode handling + + case Bytecodes::_lookupswitch: + _pc++; // Skip wide bytecode + _pc += (_start-_pc)&3; // Word align + _table_base = (jint*)_pc; // Capture for later usage + // table_base[0] is default far_dest + // Table has 2 lead elements (default, length), then pairs of u4 values. + // So load table length, and compute address at end of table + _pc = (address)&_table_base[2+ 2*Bytes::get_Java_u4((address)&_table_base[1])]; + break; + + case Bytecodes::_tableswitch: { + _pc++; // Skip wide bytecode + _pc += (_start-_pc)&3; // Word align + _table_base = (jint*)_pc; // Capture for later usage + // table_base[0] is default far_dest + int lo = Bytes::get_Java_u4((address)&_table_base[1]);// Low bound + int hi = Bytes::get_Java_u4((address)&_table_base[2]);// High bound + int len = hi - lo + 1; // Dense table size + _pc = (address)&_table_base[3+len]; // Skip past table + break; + } + + default: + fatal("unhandled bytecode"); + } + return bc; +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::reset_to_bci +void ciBytecodeStream::reset_to_bci( int bci ) { + _bc_start=_was_wide=0; + _pc = _start+bci; +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::force_bci +void ciBytecodeStream::force_bci(int bci) { + if (bci < 0) { + reset_to_bci(0); + _bc_start = _start + bci; + _bc = EOBC(); + } else { + reset_to_bci(bci); + next(); + } +} + + +// ------------------------------------------------------------------ +// Constant pool access +// ------------------------------------------------------------------ + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_klass_index +// +// If this bytecodes references a klass, return the index of the +// referenced klass. +int ciBytecodeStream::get_klass_index() const { + switch(cur_bc()) { + case Bytecodes::_ldc: + return get_index(); + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + case Bytecodes::_checkcast: + case Bytecodes::_instanceof: + case Bytecodes::_anewarray: + case Bytecodes::_multianewarray: + case Bytecodes::_new: + case Bytecodes::_newarray: + return get_index_big(); + default: + ShouldNotReachHere(); + return 0; + } +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_klass +// +// If this bytecode is a new, newarray, multianewarray, instanceof, +// or checkcast, get the referenced klass. +ciKlass* ciBytecodeStream::get_klass(bool& will_link) { + return CURRENT_ENV->get_klass_by_index(_holder, get_klass_index(), + will_link); +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_constant_index +// +// If this bytecode is one of the ldc variants, get the index of the +// referenced constant. +int ciBytecodeStream::get_constant_index() const { + switch(cur_bc()) { + case Bytecodes::_ldc: + return get_index(); + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + return get_index_big(); + default: + ShouldNotReachHere(); + return 0; + } +} +// ------------------------------------------------------------------ +// ciBytecodeStream::get_constant +// +// If this bytecode is one of the ldc variants, get the referenced +// constant. +ciConstant ciBytecodeStream::get_constant() { + return CURRENT_ENV->get_constant_by_index(_holder, get_constant_index()); +} + +// ------------------------------------------------------------------ +bool ciBytecodeStream::is_unresolved_string() const { + return CURRENT_ENV->is_unresolved_string(_holder, get_constant_index()); +} + +// ------------------------------------------------------------------ +bool ciBytecodeStream::is_unresolved_klass() const { + return CURRENT_ENV->is_unresolved_klass(_holder, get_klass_index()); +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_field_index +// +// If this is a field access bytecode, get the constant pool +// index of the referenced field. +int ciBytecodeStream::get_field_index() { + assert(cur_bc() == Bytecodes::_getfield || + cur_bc() == Bytecodes::_putfield || + cur_bc() == Bytecodes::_getstatic || + cur_bc() == Bytecodes::_putstatic, "wrong bc"); + return get_index_big(); +} + + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_field +// +// If this bytecode is one of get_field, get_static, put_field, +// or put_static, get the referenced field. +ciField* ciBytecodeStream::get_field(bool& will_link) { + ciField* f = CURRENT_ENV->get_field_by_index(_holder, get_field_index()); + will_link = f->will_link(_holder, _bc); + return f; +} + + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_declared_field_holder +// +// Get the declared holder of the currently referenced field. +// +// Usage note: the holder() of a ciField class returns the canonical +// holder of the field, rather than the holder declared in the +// bytecodes. +// +// There is no "will_link" result passed back. The user is responsible +// for checking linkability when retrieving the associated field. +ciInstanceKlass* ciBytecodeStream::get_declared_field_holder() { + int holder_index = get_field_holder_index(); + bool ignore; + return CURRENT_ENV->get_klass_by_index(_holder, holder_index, ignore) + ->as_instance_klass(); +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_field_holder_index +// +// Get the constant pool index of the declared holder of the field +// referenced by the current bytecode. Used for generating +// deoptimization information. +int ciBytecodeStream::get_field_holder_index() { + VM_ENTRY_MARK; + constantPoolOop cpool = _holder->get_instanceKlass()->constants(); + return cpool->klass_ref_index_at(get_field_index()); +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_field_signature_index +// +// Get the constant pool index of the signature of the field +// referenced by the current bytecode. Used for generating +// deoptimization information. +int ciBytecodeStream::get_field_signature_index() { + VM_ENTRY_MARK; + constantPoolOop cpool = _holder->get_instanceKlass()->constants(); + int nt_index = cpool->name_and_type_ref_index_at(get_field_index()); + return cpool->signature_ref_index_at(nt_index); +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_method_index +// +// If this is a method invocation bytecode, get the constant pool +// index of the invoked method. +int ciBytecodeStream::get_method_index() { + switch (cur_bc()) { + case Bytecodes::_invokeinterface: + return Bytes::get_Java_u2(_pc-4); + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + return get_index_big(); + default: + ShouldNotReachHere(); + return 0; + } +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_method +// +// If this is a method invocation bytecode, get the invoked method. +ciMethod* ciBytecodeStream::get_method(bool& will_link) { + ciMethod* m = CURRENT_ENV->get_method_by_index(_holder, get_method_index(),cur_bc()); + will_link = m->is_loaded(); + return m; +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_declared_method_holder +// +// Get the declared holder of the currently referenced method. +// +// Usage note: the holder() of a ciMethod class returns the canonical +// holder of the method, rather than the holder declared in the +// bytecodes. +// +// There is no "will_link" result passed back. The user is responsible +// for checking linkability when retrieving the associated method. +ciKlass* ciBytecodeStream::get_declared_method_holder() { + bool ignore; + return CURRENT_ENV->get_klass_by_index(_holder, get_method_holder_index(), ignore); +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_method_holder_index +// +// Get the constant pool index of the declared holder of the method +// referenced by the current bytecode. Used for generating +// deoptimization information. +int ciBytecodeStream::get_method_holder_index() { + VM_ENTRY_MARK; + constantPoolOop cpool = _holder->get_instanceKlass()->constants(); + return cpool->klass_ref_index_at(get_method_index()); +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_method_signature_index +// +// Get the constant pool index of the signature of the method +// referenced by the current bytecode. Used for generating +// deoptimization information. +int ciBytecodeStream::get_method_signature_index() { + VM_ENTRY_MARK; + constantPoolOop cpool = _holder->get_instanceKlass()->constants(); + int method_index = get_method_index(); + int name_and_type_index = cpool->name_and_type_ref_index_at(method_index); + return cpool->signature_ref_index_at(name_and_type_index); +} diff --git a/hotspot/src/share/vm/ci/ciStreams.hpp b/hotspot/src/share/vm/ci/ciStreams.hpp new file mode 100644 index 00000000000..eebb6042ca8 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciStreams.hpp @@ -0,0 +1,369 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciBytecodeStream +// +// The class is used to iterate over the bytecodes of a method. +// It hides the details of constant pool structure/access by +// providing accessors for constant pool items. It returns only pure +// Java bytecodes; VM-internal _fast bytecodes are translated back to +// their original form during iteration. +class ciBytecodeStream : StackObj { +private: + // Handling for the weird bytecodes + Bytecodes::Code wide(); // Handle wide bytecode + Bytecodes::Code table(Bytecodes::Code); // Handle complicated inline table + + static Bytecodes::Code check_java(Bytecodes::Code c) { + assert(Bytecodes::is_java_code(c), "should not return _fast bytecodes"); + return c; + } + + ciMethod* _method; // the method + ciInstanceKlass* _holder; + address _bc_start; // Start of current bytecode for table + address _was_wide; // Address past last wide bytecode + jint* _table_base; // Aligned start of last table or switch + + address _start; // Start of bytecodes + address _end; // Past end of bytecodes + address _pc; // Current PC + Bytecodes::Code _bc; // Current bytecode + + void reset( address base, unsigned int size ) { + _bc_start =_was_wide = 0; + _start = _pc = base; _end = base + size; } + +public: + // End-Of-Bytecodes + static Bytecodes::Code EOBC() { + return Bytecodes::_illegal; + } + + ciBytecodeStream(ciMethod* m) { + reset_to_method(m); + } + + ciBytecodeStream() { + reset_to_method(NULL); + } + + ciMethod* method() const { return _method; } + + void reset_to_method(ciMethod* m) { + _method = m; + if (m == NULL) { + _holder = NULL; + reset(NULL, 0); + } else { + _holder = m->holder(); + reset(m->code(), m->code_size()); + } + } + + void reset_to_bci( int bci ); + + // Force the iterator to report a certain bci. + void force_bci(int bci); + + void set_max_bci( int max ) { + _end = _start + max; + } + + address cur_bcp() { return _bc_start; } // Returns bcp to current instruction + int next_bci() const { return _pc -_start; } + int cur_bci() const { return _bc_start - _start; } + + Bytecodes::Code cur_bc() const{ return check_java(_bc); } + Bytecodes::Code next_bc() { return Bytecodes::java_code((Bytecodes::Code)* _pc); } + + // Return current ByteCode and increment PC to next bytecode, skipping all + // intermediate constants. Returns EOBC at end. + // Expected usage: + // while( (bc = iter.next()) != EOBC() ) { ... } + Bytecodes::Code next() { + _bc_start = _pc; // Capture start of bc + if( _pc >= _end ) return EOBC(); // End-Of-Bytecodes + + // Fetch Java bytecode + // All rewritten bytecodes maintain the size of original bytecode. + _bc = Bytecodes::java_code((Bytecodes::Code)*_pc); + int csize = Bytecodes::length_for(_bc); // Expected size + + if( _bc == Bytecodes::_wide ) { + _bc=wide(); // Handle wide bytecode + } else if( csize == 0 ) { + _bc=table(_bc); // Handle inline tables + } else { + _pc += csize; // Bump PC past bytecode + } + return check_java(_bc); + } + + bool is_wide() { return ( _pc == _was_wide ); } + + // Get a byte index following this bytecode. + // If prefixed with a wide bytecode, get a wide index. + int get_index() const { + return (_pc == _was_wide) // was widened? + ? Bytes::get_Java_u2(_bc_start+2) // yes, return wide index + : _bc_start[1]; // no, return narrow index + } + + // Set a byte index following this bytecode. + // If prefixed with a wide bytecode, get a wide index. + void put_index(int idx) { + if (_pc == _was_wide) // was widened? + Bytes::put_Java_u2(_bc_start+2,idx); // yes, set wide index + else + _bc_start[1]=idx; // no, set narrow index + } + + // Get 2-byte index (getfield/putstatic/etc) + int get_index_big() const { return Bytes::get_Java_u2(_bc_start+1); } + + // Get dimensions byte (multinewarray) + int get_dimensions() const { return *(unsigned char*)(_pc-1); } + + // Get unsigned index fast + int get_index_fast() const { return Bytes::get_native_u2(_pc-2); } + + // Sign-extended index byte/short, no widening + int get_byte() const { return (int8_t)(_pc[-1]); } + int get_short() const { return (int16_t)Bytes::get_Java_u2(_pc-2); } + int get_long() const { return (int32_t)Bytes::get_Java_u4(_pc-4); } + + // Get a byte signed constant for "iinc". Invalid for other bytecodes. + // If prefixed with a wide bytecode, get a wide constant + int get_iinc_con() const {return (_pc==_was_wide) ? get_short() :get_byte();} + + // 2-byte branch offset from current pc + int get_dest( ) const { + assert( Bytecodes::length_at(_bc_start) == sizeof(jshort)+1, "get_dest called with bad bytecode" ); + return _bc_start-_start + (short)Bytes::get_Java_u2(_pc-2); + } + + // 2-byte branch offset from next pc + int next_get_dest( ) const { + address next_bc_start = _pc; + assert( _pc < _end, "" ); + Bytecodes::Code next_bc = (Bytecodes::Code)*_pc; + assert( next_bc != Bytecodes::_wide, ""); + int next_csize = Bytecodes::length_for(next_bc); + assert( next_csize != 0, "" ); + assert( next_bc <= Bytecodes::_jsr_w, ""); + address next_pc = _pc + next_csize; + assert( Bytecodes::length_at(next_bc_start) == sizeof(jshort)+1, "next_get_dest called with bad bytecode" ); + return next_bc_start-_start + (short)Bytes::get_Java_u2(next_pc-2); + } + + // 4-byte branch offset from current pc + int get_far_dest( ) const { + assert( Bytecodes::length_at(_bc_start) == sizeof(jint)+1, "dest4 called with bad bytecode" ); + return _bc_start-_start + (int)Bytes::get_Java_u4(_pc-4); + } + + // For a lookup or switch table, return target destination + int get_int_table( int index ) const { + return Bytes::get_Java_u4((address)&_table_base[index]); } + + // For tableswitch - get length of offset part + int get_tableswitch_length() { return get_int_table(2)-get_int_table(1)+1; } + + int get_dest_table( int index ) const { + return cur_bci() + get_int_table(index); } + + // --- Constant pool access --- + int get_constant_index() const; + int get_field_index(); + int get_method_index(); + + // If this bytecode is a new, newarray, multianewarray, instanceof, + // or checkcast, get the referenced klass. + ciKlass* get_klass(bool& will_link); + int get_klass_index() const; + + // If this bytecode is one of the ldc variants, get the referenced + // constant + ciConstant get_constant(); + // True if the ldc variant points to an unresolved string + bool is_unresolved_string() const; + // True if the ldc variant points to an unresolved klass + bool is_unresolved_klass() const; + + // If this bytecode is one of get_field, get_static, put_field, + // or put_static, get the referenced field. + ciField* get_field(bool& will_link); + + ciInstanceKlass* get_declared_field_holder(); + int get_field_holder_index(); + int get_field_signature_index(); + + // If this is a method invocation bytecode, get the invoked method. + ciMethod* get_method(bool& will_link); + ciKlass* get_declared_method_holder(); + int get_method_holder_index(); + int get_method_signature_index(); +}; + + +// ciSignatureStream +// +// The class is used to iterate over the elements of a method signature. +class ciSignatureStream : public StackObj { +private: + ciSignature* _sig; + int _pos; +public: + ciSignatureStream(ciSignature* signature) { + _sig = signature; + _pos = 0; + } + + bool at_return_type() { return _pos == _sig->count(); } + + bool is_done() { return _pos > _sig->count(); } + + void next() { + if (_pos <= _sig->count()) { + _pos++; + } + } + + ciType* type() { + if (at_return_type()) { + return _sig->return_type(); + } else { + return _sig->type_at(_pos); + } + } +}; + + +// ciExceptionHandlerStream +// +// The class is used to iterate over the exception handlers of +// a method. +class ciExceptionHandlerStream : public StackObj { +private: + // The method whose handlers we are traversing + ciMethod* _method; + + // Our current position in the list of handlers + int _pos; + int _end; + + ciInstanceKlass* _exception_klass; + int _bci; + bool _is_exact; + +public: + ciExceptionHandlerStream(ciMethod* method) { + _method = method; + + // Force loading of method code and handlers. + _method->code(); + + _pos = 0; + _end = _method->_handler_count; + _exception_klass = NULL; + _bci = -1; + _is_exact = false; + } + + ciExceptionHandlerStream(ciMethod* method, int bci, + ciInstanceKlass* exception_klass = NULL, + bool is_exact = false) { + _method = method; + + // Force loading of method code and handlers. + _method->code(); + + _pos = -1; + _end = _method->_handler_count + 1; // include the rethrow handler + _exception_klass = (exception_klass != NULL && exception_klass->is_loaded() + ? exception_klass + : NULL); + _bci = bci; + assert(_bci >= 0, "bci out of range"); + _is_exact = is_exact; + next(); + } + + // These methods are currently implemented in an odd way. + // Count the number of handlers the iterator has ever produced + // or will ever produce. Do not include the final rethrow handler. + // That is, a trivial exception handler stream will have a count + // of zero and produce just the rethrow handler. + int count(); + + // Count the number of handlers this stream will produce from now on. + // Include the current handler, and the final rethrow handler. + // The remaining count will be zero iff is_done() is true, + int count_remaining(); + + bool is_done() { + return (_pos >= _end); + } + + void next() { + _pos++; + if (_bci != -1) { + // We are not iterating over all handlers... + while (!is_done()) { + ciExceptionHandler* handler = _method->_exception_handlers[_pos]; + if (handler->is_in_range(_bci)) { + if (handler->is_catch_all()) { + // Found final active catch block. + _end = _pos+1; + return; + } else if (_exception_klass == NULL || !handler->catch_klass()->is_loaded()) { + // We cannot do any type analysis here. Must conservatively assume + // catch block is reachable. + return; + } else if (_exception_klass->is_subtype_of(handler->catch_klass())) { + // This catch clause will definitely catch the exception. + // Final candidate. + _end = _pos+1; + return; + } else if (!_is_exact && + handler->catch_klass()->is_subtype_of(_exception_klass)) { + // This catch block may be reachable. + return; + } + } + + // The catch block was not pertinent. Go on. + _pos++; + } + } else { + // This is an iteration over all handlers. + return; + } + } + + ciExceptionHandler* handler() { + return _method->_exception_handlers[_pos]; + } +}; diff --git a/hotspot/src/share/vm/ci/ciSymbol.cpp b/hotspot/src/share/vm/ci/ciSymbol.cpp new file mode 100644 index 00000000000..7284893e81d --- /dev/null +++ b/hotspot/src/share/vm/ci/ciSymbol.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciSymbol.cpp.incl" + +// ------------------------------------------------------------------ +// ciSymbol::ciSymbol +// +// Preallocated handle variant. Used with handles from vmSymboHandles. +ciSymbol::ciSymbol(symbolHandle h_s) : ciObject(h_s) { +} + +// ciSymbol +// +// This class represents a symbolOop in the HotSpot virtual +// machine. + +// ------------------------------------------------------------------ +// ciSymbol::as_utf8 +// +// The text of the symbol as a null-terminated C string. +const char* ciSymbol::as_utf8() { + VM_QUICK_ENTRY_MARK; + symbolOop s = get_symbolOop(); + return s->as_utf8(); +} + +// ------------------------------------------------------------------ +// ciSymbol::base +jbyte* ciSymbol::base() { + GUARDED_VM_ENTRY(return get_symbolOop()->base();) +} + +// ------------------------------------------------------------------ +// ciSymbol::byte_at +int ciSymbol::byte_at(int i) { + GUARDED_VM_ENTRY(return get_symbolOop()->byte_at(i);) +} + +// ------------------------------------------------------------------ +// ciSymbol::utf8_length +int ciSymbol::utf8_length() { + GUARDED_VM_ENTRY(return get_symbolOop()->utf8_length();) +} + +// ------------------------------------------------------------------ +// ciSymbol::print_impl +// +// Implementation of the print method +void ciSymbol::print_impl(outputStream* st) { + st->print(" value="); + print_symbol_on(st); +} + +// ------------------------------------------------------------------ +// ciSymbol::print_symbol_on +// +// Print the value of this symbol on an outputStream +void ciSymbol::print_symbol_on(outputStream *st) { + GUARDED_VM_ENTRY(get_symbolOop()->print_symbol_on(st);) +} + +// ------------------------------------------------------------------ +// ciSymbol::make_impl +// +// Make a ciSymbol from a C string (implementation). +ciSymbol* ciSymbol::make_impl(const char* s) { + EXCEPTION_CONTEXT; + // For some reason, oopFactory::new_symbol doesn't declare its + // char* argument as const. + symbolOop sym = oopFactory::new_symbol((char*)s, (int)strlen(s), THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + CURRENT_THREAD_ENV->record_out_of_memory_failure(); + return ciEnv::_unloaded_cisymbol; + } + return CURRENT_THREAD_ENV->get_object(sym)->as_symbol(); +} + +// ------------------------------------------------------------------ +// ciSymbol::make +// +// Make a ciSymbol from a C string. +ciSymbol* ciSymbol::make(const char* s) { + GUARDED_VM_ENTRY(return make_impl(s);) +} diff --git a/hotspot/src/share/vm/ci/ciSymbol.hpp b/hotspot/src/share/vm/ci/ciSymbol.hpp new file mode 100644 index 00000000000..701fb8023d7 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciSymbol.hpp @@ -0,0 +1,75 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciSymbol +// +// This class represents a symbolOop in the HotSpot virtual +// machine. +class ciSymbol : public ciObject { + CI_PACKAGE_ACCESS + friend class ciEnv; + friend class ciInstanceKlass; + friend class ciSignature; + friend class ciMethod; + friend class ciObjArrayKlass; + +private: + ciSymbol(symbolOop s) : ciObject(s) {} + ciSymbol(symbolHandle s); // for use with vmSymbolHandles + + symbolOop get_symbolOop() { return (symbolOop)get_oop(); } + + const char* type_string() { return "ciSymbol"; } + + void print_impl(outputStream* st); + + int byte_at(int i); + jbyte* base(); + + // Make a ciSymbol from a C string (implementation). + static ciSymbol* make_impl(const char* s); + +public: + // The text of the symbol as a null-terminated utf8 string. + const char* as_utf8(); + int utf8_length(); + + // What kind of ciObject is this? + bool is_symbol() { return true; } + + void print_symbol_on(outputStream* st); + void print_symbol() { + print_symbol_on(tty); + } + + // Make a ciSymbol from a C string. + // Consider adding to vmSymbols.hpp instead of using this constructor. + // (Your code will be less subject to typographical bugs.) + static ciSymbol* make(const char* s); + +#define CI_SYMBOL_DECLARE(name, ignore_def) \ + static ciSymbol* name() { return ciObjectFactory::vm_symbol_at(vmSymbols::VM_SYMBOL_ENUM_NAME(name)); } + VM_SYMBOLS_DO(CI_SYMBOL_DECLARE, CI_SYMBOL_DECLARE) +#undef CI_SYMBOL_DECLARE +}; diff --git a/hotspot/src/share/vm/ci/ciSymbolKlass.cpp b/hotspot/src/share/vm/ci/ciSymbolKlass.cpp new file mode 100644 index 00000000000..9872151c780 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciSymbolKlass.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciSymbolKlass.cpp.incl" + +// ciSymbolKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is a symbolKlass. + +// ------------------------------------------------------------------ +// ciSymbolKlass::instance +// +// Return the distinguished instance of this class +ciSymbolKlass* ciSymbolKlass::make() { + return CURRENT_ENV->_symbol_klass_instance; +} diff --git a/hotspot/src/share/vm/ci/ciSymbolKlass.hpp b/hotspot/src/share/vm/ci/ciSymbolKlass.hpp new file mode 100644 index 00000000000..acdb5ffa094 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciSymbolKlass.hpp @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciSymbolKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part in a symbolKlass. Although, in the VM +// Klass hierarchy, symbolKlass is a direct subclass of typeArrayKlass, +// we do not model this relationship in the ciObject hierarchy -- the +// subclassing is used to share implementation and is not of note +// to compiler writers. +class ciSymbolKlass : public ciKlass { + CI_PACKAGE_ACCESS + +protected: + ciSymbolKlass(KlassHandle h_k) + : ciKlass(h_k, ciSymbol::make("unique_symbolKlass")) { + assert(get_Klass()->oop_is_symbol(), "wrong type"); + } + + symbolKlass* get_symbolKlass() { return (symbolKlass*)get_Klass(); } + + const char* type_string() { return "ciSymbolKlass"; } + +public: + // What kind of ciObject is this? + bool is_symbol_klass() { return true; } + + // Return the distinguished ciSymbolKlass instance. + static ciSymbolKlass* make(); +}; diff --git a/hotspot/src/share/vm/ci/ciType.cpp b/hotspot/src/share/vm/ci/ciType.cpp new file mode 100644 index 00000000000..ca2c79a102a --- /dev/null +++ b/hotspot/src/share/vm/ci/ciType.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciType.cpp.incl" + +ciType* ciType::_basic_types[T_CONFLICT+1]; + +// ciType +// +// This class represents either a class (T_OBJECT), array (T_ARRAY), +// or one of the primitive types such as T_INT. + +// ------------------------------------------------------------------ +// ciType::ciType +// +ciType::ciType(BasicType basic_type) : ciObject() { + assert(basic_type >= T_BOOLEAN && basic_type <= T_CONFLICT, "range check"); + assert(basic_type != T_OBJECT && basic_type != T_ARRAY, "not a reference type"); + _basic_type = basic_type; +} + +ciType::ciType(KlassHandle k) : ciObject(k) { + _basic_type = Klass::cast(k())->oop_is_array() ? T_ARRAY : T_OBJECT; +} + +ciType::ciType(ciKlass* klass) : ciObject(klass) { + _basic_type = klass->is_array_klass_klass() ? T_ARRAY : T_OBJECT; +} + + +// ------------------------------------------------------------------ +// ciType::is_subtype_of +// +bool ciType::is_subtype_of(ciType* type) { + if (this == type) return true; + if (is_klass() && type->is_klass()) + return this->as_klass()->is_subtype_of(type->as_klass()); + return false; +} + +// ------------------------------------------------------------------ +// ciType::print_impl +// +// Implementation of the print method. +void ciType::print_impl(outputStream* st) { + st->print(" type="); + print_name_on(st); +} + +// ------------------------------------------------------------------ +// ciType::print_name +// +// Print the name of this type +void ciType::print_name_on(outputStream* st) { + st->print(type2name(basic_type())); +} + + + +// ------------------------------------------------------------------ +// ciType::java_mirror +// +ciInstance* ciType::java_mirror() { + VM_ENTRY_MARK; + return CURRENT_THREAD_ENV->get_object(Universe::java_mirror(basic_type()))->as_instance(); +} + +// ------------------------------------------------------------------ +// ciType::box_klass +// +ciKlass* ciType::box_klass() { + if (!is_primitive_type()) return this->as_klass(); // reference types are "self boxing" + + // Void is "boxed" with a null. + if (basic_type() == T_VOID) return NULL; + + VM_ENTRY_MARK; + return CURRENT_THREAD_ENV->get_object(SystemDictionary::box_klass(basic_type()))->as_instance_klass(); +} + + +// ------------------------------------------------------------------ +// ciType::make +// +// Produce the ciType for a given primitive BasicType. +// As a bonus, produce the right reference type for T_OBJECT. +// Does not work on T_ARRAY. +ciType* ciType::make(BasicType t) { + // short, etc. + // Note: Bare T_ADDRESS means a raw pointer type, not a return_address. + assert((uint)t < T_CONFLICT+1, "range check"); + if (t == T_OBJECT) return ciEnv::_Object; // java/lang/Object + assert(_basic_types[t] != NULL, "domain check"); + return _basic_types[t]; +} + +// ciReturnAddress +// +// This class represents the type of a specific return address in the +// bytecodes. + +// ------------------------------------------------------------------ +// ciReturnAddress::ciReturnAddress +// +ciReturnAddress::ciReturnAddress(int bci) : ciType(T_ADDRESS) { + assert(0 <= bci, "bci cannot be negative"); + _bci = bci; +} + +// ------------------------------------------------------------------ +// ciReturnAddress::print_impl +// +// Implementation of the print method. +void ciReturnAddress::print_impl(outputStream* st) { + st->print(" bci=%d", _bci); +} + +// ------------------------------------------------------------------ +// ciReturnAddress::make +ciReturnAddress* ciReturnAddress::make(int bci) { + GUARDED_VM_ENTRY(return CURRENT_ENV->get_return_address(bci);) +} diff --git a/hotspot/src/share/vm/ci/ciType.hpp b/hotspot/src/share/vm/ci/ciType.hpp new file mode 100644 index 00000000000..f8b33392d4c --- /dev/null +++ b/hotspot/src/share/vm/ci/ciType.hpp @@ -0,0 +1,108 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciType +// +// This class represents either a class (T_OBJECT), array (T_ARRAY), +// or one of the primitive types such as T_INT. +class ciType : public ciObject { + CI_PACKAGE_ACCESS + friend class ciKlass; + friend class ciReturnAddress; + +private: + BasicType _basic_type; + + ciType(BasicType t); // for the primitive types only + ciType(KlassHandle k); // for subclasses (reference types) + ciType(ciKlass* klass); // for unloaded types + + const char* type_string() { return "ciType"; } + + void print_impl(outputStream* st); + + // Distinguished instances of primitive ciTypes.. + static ciType* _basic_types[T_CONFLICT+1]; + +public: + BasicType basic_type() const { return _basic_type; } + + // Returns true iff the types are identical, or if both are klasses + // and the is_subtype_of relation holds between the klasses. + bool is_subtype_of(ciType* type); + + // Get the instance of java.lang.Class corresponding to this type. + // There are mirrors for instance, array, and primitive types (incl. void). + virtual ciInstance* java_mirror(); + + // Get the class which "boxes" (or "wraps") values of this type. + // Example: short is boxed by java.lang.Short, etc. + // Returns self if it is a reference type. + // Returns NULL for void, since null is used in such cases. + ciKlass* box_klass(); + + // Returns true if this is not a klass or array (i.e., not a reference type). + bool is_primitive_type() const { return basic_type() != T_OBJECT && basic_type() != T_ARRAY; } + int size() const { return type2size[basic_type()]; } + bool is_void() const { return basic_type() == T_VOID; } + bool is_one_word() const { return size() == 1; } + bool is_two_word() const { return size() == 2; } + + // What kind of ciObject is this? + bool is_type() { return true; } + bool is_classless() const { return is_primitive_type(); } + + virtual void print_name_on(outputStream* st); + void print_name() { + print_name_on(tty); + } + + static ciType* make(BasicType t); +}; + + +// ciReturnAddress +// +// This class represents the type of a specific return address in the +// bytecodes. +class ciReturnAddress : public ciType { + CI_PACKAGE_ACCESS + +private: + // The bci of this return address. + int _bci; + + ciReturnAddress(int bci); + + const char* type_string() { return "ciReturnAddress"; } + + void print_impl(outputStream* st); + +public: + bool is_return_address() { return true; } + + int bci() { return _bci; } + + static ciReturnAddress* make(int bci); +}; diff --git a/hotspot/src/share/vm/ci/ciTypeArray.cpp b/hotspot/src/share/vm/ci/ciTypeArray.cpp new file mode 100644 index 00000000000..ee67bbb4397 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciTypeArray.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciTypeArray.cpp.incl" + +// ciTypeArray +// +// This class represents an typeArrayOop in the HotSpot virtual +// machine. + + +// ------------------------------------------------------------------ +// ciTypeArray::char_at +// +// Implementation of the char_at method. +jchar ciTypeArray::char_at(int index) { + VM_ENTRY_MARK; + assert(index >= 0 && index < length(), "out of range"); + return get_typeArrayOop()->char_at(index); +} diff --git a/hotspot/src/share/vm/ci/ciTypeArray.hpp b/hotspot/src/share/vm/ci/ciTypeArray.hpp new file mode 100644 index 00000000000..733aec8c0f2 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciTypeArray.hpp @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciTypeArray +// +// This class represents a typeArrayOop in the HotSpot virtual +// machine. +class ciTypeArray : public ciArray { + CI_PACKAGE_ACCESS + +protected: + ciTypeArray(typeArrayHandle h_t) : ciArray(h_t) {} + + ciTypeArray(ciKlass* klass, int len) : ciArray(klass, len) {} + + typeArrayOop get_typeArrayOop() { + return (typeArrayOop)get_oop(); + } + + const char* type_string() { return "ciTypeArray"; } + +public: + // What kind of ciObject is this? + bool is_type_array() { return true; } + + // Return character at index. This is only useful if the + // compiler has already proved that the contents of the + // array will never change. + jchar char_at(int index); + +}; diff --git a/hotspot/src/share/vm/ci/ciTypeArrayKlass.cpp b/hotspot/src/share/vm/ci/ciTypeArrayKlass.cpp new file mode 100644 index 00000000000..94073a969d9 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciTypeArrayKlass.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciTypeArrayKlass.cpp.incl" + +// ciTypeArrayKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part in a TypeArrayKlass. + +// ------------------------------------------------------------------ +// ciTypeArrayKlass::ciTypeArrayKlass +ciTypeArrayKlass::ciTypeArrayKlass(KlassHandle h_k) : ciArrayKlass(h_k) { + assert(get_Klass()->oop_is_typeArray(), "wrong type"); + assert(element_type() == get_typeArrayKlass()->element_type(), ""); +} + +// ------------------------------------------------------------------ +// ciTypeArrayKlass::make_impl +// +// Implementation of make. +ciTypeArrayKlass* ciTypeArrayKlass::make_impl(BasicType t) { + klassOop k = Universe::typeArrayKlassObj(t); + return CURRENT_ENV->get_object(k)->as_type_array_klass(); +} + +// ------------------------------------------------------------------ +// ciTypeArrayKlass::make +// +// Make an array klass corresponding to the specified primitive type. +ciTypeArrayKlass* ciTypeArrayKlass::make(BasicType t) { + GUARDED_VM_ENTRY(return make_impl(t);) +} diff --git a/hotspot/src/share/vm/ci/ciTypeArrayKlass.hpp b/hotspot/src/share/vm/ci/ciTypeArrayKlass.hpp new file mode 100644 index 00000000000..5f7353dd65e --- /dev/null +++ b/hotspot/src/share/vm/ci/ciTypeArrayKlass.hpp @@ -0,0 +1,55 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciTypeArrayKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part in a TypeArrayKlass. +class ciTypeArrayKlass : public ciArrayKlass { + CI_PACKAGE_ACCESS + +protected: + ciTypeArrayKlass(KlassHandle h_k); + + typeArrayKlass* get_typeArrayKlass() { + return (typeArrayKlass*)get_Klass(); + } + + const char* type_string() { return "ciTypeArrayKlass"; } + + // Helper method for make. + static ciTypeArrayKlass* make_impl(BasicType type); + +public: + // The type of the array elements. + BasicType element_type() { + return Klass::layout_helper_element_type(layout_helper()); + } + + // What kind of ciObject is this? + bool is_type_array_klass() { return true; } + + // Make an array klass corresponding to the specified primitive type. + static ciTypeArrayKlass* make(BasicType type); +}; diff --git a/hotspot/src/share/vm/ci/ciTypeArrayKlassKlass.cpp b/hotspot/src/share/vm/ci/ciTypeArrayKlassKlass.cpp new file mode 100644 index 00000000000..41dffaca3a1 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciTypeArrayKlassKlass.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciTypeArrayKlassKlass.cpp.incl" + +// ciTypeArrayKlassKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is a arrayKlassKlass. + +// ------------------------------------------------------------------ +// ciTypeArrayKlassKlass::instance +// +// Return the distinguished instance of this class +ciTypeArrayKlassKlass* ciTypeArrayKlassKlass::make() { + return CURRENT_ENV->_type_array_klass_klass_instance; +} diff --git a/hotspot/src/share/vm/ci/ciTypeArrayKlassKlass.hpp b/hotspot/src/share/vm/ci/ciTypeArrayKlassKlass.hpp new file mode 100644 index 00000000000..74996eafcdc --- /dev/null +++ b/hotspot/src/share/vm/ci/ciTypeArrayKlassKlass.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciTypeArrayKlassKlass +// +// This class represents a klassOop in the HotSpot virtual machine +// whose Klass part is a typeArrayKlassKlass. +class ciTypeArrayKlassKlass : public ciArrayKlassKlass { + CI_PACKAGE_ACCESS + +private: + ciTypeArrayKlassKlass(KlassHandle h_k) + : ciArrayKlassKlass(h_k, ciSymbol::make("unique_typeArrayKlassKlass")) { + assert(h_k()->klass_part()->oop_is_typeArrayKlass(), "wrong type"); + } + + + typeArrayKlassKlass* get_typeArrayKlassKlass() { + return (typeArrayKlassKlass*)get_Klass(); + } + + const char* type_string() { return "ciTypeArrayKlassKlass"; } + +public: + // What kind of ciTypeect is this? + bool is_type_array_klass_klass() { return true; } + + // Return the distinguished ciTypeArrayKlassKlass instance. + static ciTypeArrayKlassKlass* make(); +}; diff --git a/hotspot/src/share/vm/ci/ciTypeFlow.cpp b/hotspot/src/share/vm/ci/ciTypeFlow.cpp new file mode 100644 index 00000000000..df053f35ea4 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciTypeFlow.cpp @@ -0,0 +1,2469 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciTypeFlow.cpp.incl" + +// ciTypeFlow::JsrSet +// +// A JsrSet represents some set of JsrRecords. This class +// is used to record a set of all jsr routines which we permit +// execution to return (ret) from. +// +// During abstract interpretation, JsrSets are used to determine +// whether two paths which reach a given block are unique, and +// should be cloned apart, or are compatible, and should merge +// together. + +// ------------------------------------------------------------------ +// ciTypeFlow::JsrSet::JsrSet +ciTypeFlow::JsrSet::JsrSet(Arena* arena, int default_len) { + if (arena != NULL) { + // Allocate growable array in Arena. + _set = new (arena) GrowableArray(arena, default_len, 0, NULL); + } else { + // Allocate growable array in current ResourceArea. + _set = new GrowableArray(4, 0, NULL, false); + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::JsrSet::copy_into +void ciTypeFlow::JsrSet::copy_into(JsrSet* jsrs) { + int len = size(); + jsrs->_set->clear(); + for (int i = 0; i < len; i++) { + jsrs->_set->append(_set->at(i)); + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::JsrSet::is_compatible_with +// +// !!!! MISGIVINGS ABOUT THIS... disregard +// +// Is this JsrSet compatible with some other JsrSet? +// +// In set-theoretic terms, a JsrSet can be viewed as a partial function +// from entry addresses to return addresses. Two JsrSets A and B are +// compatible iff +// +// For any x, +// A(x) defined and B(x) defined implies A(x) == B(x) +// +// Less formally, two JsrSets are compatible when they have identical +// return addresses for any entry addresses they share in common. +bool ciTypeFlow::JsrSet::is_compatible_with(JsrSet* other) { + // Walk through both sets in parallel. If the same entry address + // appears in both sets, then the return address must match for + // the sets to be compatible. + int size1 = size(); + int size2 = other->size(); + + // Special case. If nothing is on the jsr stack, then there can + // be no ret. + if (size2 == 0) { + return true; + } else if (size1 != size2) { + return false; + } else { + for (int i = 0; i < size1; i++) { + JsrRecord* record1 = record_at(i); + JsrRecord* record2 = other->record_at(i); + if (record1->entry_address() != record2->entry_address() || + record1->return_address() != record2->return_address()) { + return false; + } + } + return true; + } + +#if 0 + int pos1 = 0; + int pos2 = 0; + int size1 = size(); + int size2 = other->size(); + while (pos1 < size1 && pos2 < size2) { + JsrRecord* record1 = record_at(pos1); + JsrRecord* record2 = other->record_at(pos2); + int entry1 = record1->entry_address(); + int entry2 = record2->entry_address(); + if (entry1 < entry2) { + pos1++; + } else if (entry1 > entry2) { + pos2++; + } else { + if (record1->return_address() == record2->return_address()) { + pos1++; + pos2++; + } else { + // These two JsrSets are incompatible. + return false; + } + } + } + // The two JsrSets agree. + return true; +#endif +} + +// ------------------------------------------------------------------ +// ciTypeFlow::JsrSet::insert_jsr_record +// +// Insert the given JsrRecord into the JsrSet, maintaining the order +// of the set and replacing any element with the same entry address. +void ciTypeFlow::JsrSet::insert_jsr_record(JsrRecord* record) { + int len = size(); + int entry = record->entry_address(); + int pos = 0; + for ( ; pos < len; pos++) { + JsrRecord* current = record_at(pos); + if (entry == current->entry_address()) { + // Stomp over this entry. + _set->at_put(pos, record); + assert(size() == len, "must be same size"); + return; + } else if (entry < current->entry_address()) { + break; + } + } + + // Insert the record into the list. + JsrRecord* swap = record; + JsrRecord* temp = NULL; + for ( ; pos < len; pos++) { + temp = _set->at(pos); + _set->at_put(pos, swap); + swap = temp; + } + _set->append(swap); + assert(size() == len+1, "must be larger"); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::JsrSet::remove_jsr_record +// +// Remove the JsrRecord with the given return address from the JsrSet. +void ciTypeFlow::JsrSet::remove_jsr_record(int return_address) { + int len = size(); + for (int i = 0; i < len; i++) { + if (record_at(i)->return_address() == return_address) { + // We have found the proper entry. Remove it from the + // JsrSet and exit. + for (int j = i+1; j < len ; j++) { + _set->at_put(j-1, _set->at(j)); + } + _set->trunc_to(len-1); + assert(size() == len-1, "must be smaller"); + return; + } + } + assert(false, "verify: returning from invalid subroutine"); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::JsrSet::apply_control +// +// Apply the effect of a control-flow bytecode on the JsrSet. The +// only bytecodes that modify the JsrSet are jsr and ret. +void ciTypeFlow::JsrSet::apply_control(ciTypeFlow* analyzer, + ciBytecodeStream* str, + ciTypeFlow::StateVector* state) { + Bytecodes::Code code = str->cur_bc(); + if (code == Bytecodes::_jsr) { + JsrRecord* record = + analyzer->make_jsr_record(str->get_dest(), str->next_bci()); + insert_jsr_record(record); + } else if (code == Bytecodes::_jsr_w) { + JsrRecord* record = + analyzer->make_jsr_record(str->get_far_dest(), str->next_bci()); + insert_jsr_record(record); + } else if (code == Bytecodes::_ret) { + Cell local = state->local(str->get_index()); + ciType* return_address = state->type_at(local); + assert(return_address->is_return_address(), "verify: wrong type"); + if (size() == 0) { + // Ret-state underflow: Hit a ret w/o any previous jsrs. Bail out. + // This can happen when a loop is inside a finally clause (4614060). + analyzer->record_failure("OSR in finally clause"); + return; + } + remove_jsr_record(return_address->as_return_address()->bci()); + } +} + +#ifndef PRODUCT +// ------------------------------------------------------------------ +// ciTypeFlow::JsrSet::print_on +void ciTypeFlow::JsrSet::print_on(outputStream* st) const { + st->print("{ "); + int num_elements = size(); + if (num_elements > 0) { + int i = 0; + for( ; i < num_elements - 1; i++) { + _set->at(i)->print_on(st); + st->print(", "); + } + _set->at(i)->print_on(st); + st->print(" "); + } + st->print("}"); +} +#endif + +// ciTypeFlow::StateVector +// +// A StateVector summarizes the type information at some point in +// the program. + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::type_meet +// +// Meet two types. +// +// The semi-lattice of types use by this analysis are modeled on those +// of the verifier. The lattice is as follows: +// +// top_type() >= all non-extremal types >= bottom_type +// and +// Every primitive type is comparable only with itself. The meet of +// reference types is determined by their kind: instance class, +// interface, or array class. The meet of two types of the same +// kind is their least common ancestor. The meet of two types of +// different kinds is always java.lang.Object. +ciType* ciTypeFlow::StateVector::type_meet_internal(ciType* t1, ciType* t2, ciTypeFlow* analyzer) { + assert(t1 != t2, "checked in caller"); + if (t1->equals(top_type())) { + return t2; + } else if (t2->equals(top_type())) { + return t1; + } else if (t1->is_primitive_type() || t2->is_primitive_type()) { + // Special case null_type. null_type meet any reference type T + // is T. null_type meet null_type is null_type. + if (t1->equals(null_type())) { + if (!t2->is_primitive_type() || t2->equals(null_type())) { + return t2; + } + } else if (t2->equals(null_type())) { + if (!t1->is_primitive_type()) { + return t1; + } + } + + // At least one of the two types is a non-top primitive type. + // The other type is not equal to it. Fall to bottom. + return bottom_type(); + } else { + // Both types are non-top non-primitive types. That is, + // both types are either instanceKlasses or arrayKlasses. + ciKlass* object_klass = analyzer->env()->Object_klass(); + ciKlass* k1 = t1->as_klass(); + ciKlass* k2 = t2->as_klass(); + if (k1->equals(object_klass) || k2->equals(object_klass)) { + return object_klass; + } else if (!k1->is_loaded() || !k2->is_loaded()) { + // Unloaded classes fall to java.lang.Object at a merge. + return object_klass; + } else if (k1->is_interface() != k2->is_interface()) { + // When an interface meets a non-interface, we get Object; + // This is what the verifier does. + return object_klass; + } else if (k1->is_array_klass() || k2->is_array_klass()) { + // When an array meets a non-array, we get Object. + // When objArray meets typeArray, we also get Object. + // And when typeArray meets different typeArray, we again get Object. + // But when objArray meets objArray, we look carefully at element types. + if (k1->is_obj_array_klass() && k2->is_obj_array_klass()) { + // Meet the element types, then construct the corresponding array type. + ciKlass* elem1 = k1->as_obj_array_klass()->element_klass(); + ciKlass* elem2 = k2->as_obj_array_klass()->element_klass(); + ciKlass* elem = type_meet_internal(elem1, elem2, analyzer)->as_klass(); + // Do an easy shortcut if one type is a super of the other. + if (elem == elem1) { + assert(k1 == ciObjArrayKlass::make(elem), "shortcut is OK"); + return k1; + } else if (elem == elem2) { + assert(k2 == ciObjArrayKlass::make(elem), "shortcut is OK"); + return k2; + } else { + return ciObjArrayKlass::make(elem); + } + } else { + return object_klass; + } + } else { + // Must be two plain old instance klasses. + assert(k1->is_instance_klass(), "previous cases handle non-instances"); + assert(k2->is_instance_klass(), "previous cases handle non-instances"); + return k1->least_common_ancestor(k2); + } + } +} + + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::StateVector +// +// Build a new state vector +ciTypeFlow::StateVector::StateVector(ciTypeFlow* analyzer) { + _outer = analyzer; + _stack_size = -1; + _monitor_count = -1; + // Allocate the _types array + int max_cells = analyzer->max_cells(); + _types = (ciType**)analyzer->arena()->Amalloc(sizeof(ciType*) * max_cells); + for (int i=0; iget_flow_analysis(); + if (non_osr_flow->failing()) { + record_failure(non_osr_flow->failure_reason()); + return NULL; + } + JsrSet* jsrs = new JsrSet(NULL, 16); + Block* non_osr_block = non_osr_flow->existing_block_at(start_bci(), jsrs); + if (non_osr_block == NULL) { + record_failure("cannot reach OSR point"); + return NULL; + } + // load up the non-OSR state at this point + non_osr_block->copy_state_into(state); + int non_osr_start = non_osr_block->start(); + if (non_osr_start != start_bci()) { + // must flow forward from it + if (CITraceTypeFlow) { + tty->print_cr(">> Interpreting pre-OSR block %d:", non_osr_start); + } + Block* block = block_at(non_osr_start, jsrs); + assert(block->limit() == start_bci(), "must flow forward to start"); + flow_block(block, state, jsrs); + } + return state; + // Note: The code below would be an incorrect for an OSR flow, + // even if it were possible for an OSR entry point to be at bci zero. + } + // "Push" the method signature into the first few locals. + state->set_stack_size(-max_locals()); + if (!method()->is_static()) { + state->push(method()->holder()); + assert(state->tos() == state->local(0), ""); + } + for (ciSignatureStream str(method()->signature()); + !str.at_return_type(); + str.next()) { + state->push_translate(str.type()); + } + // Set the rest of the locals to bottom. + Cell cell = state->next_cell(state->tos()); + state->set_stack_size(0); + int limit = state->limit_cell(); + for (; cell < limit; cell = state->next_cell(cell)) { + state->set_type_at(cell, state->bottom_type()); + } + // Lock an object, if necessary. + state->set_monitor_count(method()->is_synchronized() ? 1 : 0); + return state; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::copy_into +// +// Copy our value into some other StateVector +void ciTypeFlow::StateVector::copy_into(ciTypeFlow::StateVector* copy) +const { + copy->set_stack_size(stack_size()); + copy->set_monitor_count(monitor_count()); + Cell limit = limit_cell(); + for (Cell c = start_cell(); c < limit; c = next_cell(c)) { + copy->set_type_at(c, type_at(c)); + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::meet +// +// Meets this StateVector with another, destructively modifying this +// one. Returns true if any modification takes place. +bool ciTypeFlow::StateVector::meet(const ciTypeFlow::StateVector* incoming) { + if (monitor_count() == -1) { + set_monitor_count(incoming->monitor_count()); + } + assert(monitor_count() == incoming->monitor_count(), "monitors must match"); + + if (stack_size() == -1) { + set_stack_size(incoming->stack_size()); + Cell limit = limit_cell(); + #ifdef ASSERT + { for (Cell c = start_cell(); c < limit; c = next_cell(c)) { + assert(type_at(c) == top_type(), ""); + } } + #endif + // Make a simple copy of the incoming state. + for (Cell c = start_cell(); c < limit; c = next_cell(c)) { + set_type_at(c, incoming->type_at(c)); + } + return true; // it is always different the first time + } +#ifdef ASSERT + if (stack_size() != incoming->stack_size()) { + _outer->method()->print_codes(); + tty->print_cr("!!!! Stack size conflict"); + tty->print_cr("Current state:"); + print_on(tty); + tty->print_cr("Incoming state:"); + ((StateVector*)incoming)->print_on(tty); + } +#endif + assert(stack_size() == incoming->stack_size(), "sanity"); + + bool different = false; + Cell limit = limit_cell(); + for (Cell c = start_cell(); c < limit; c = next_cell(c)) { + ciType* t1 = type_at(c); + ciType* t2 = incoming->type_at(c); + if (!t1->equals(t2)) { + ciType* new_type = type_meet(t1, t2); + if (!t1->equals(new_type)) { + set_type_at(c, new_type); + different = true; + } + } + } + return different; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::meet_exception +// +// Meets this StateVector with another, destructively modifying this +// one. The incoming state is coming via an exception. Returns true +// if any modification takes place. +bool ciTypeFlow::StateVector::meet_exception(ciInstanceKlass* exc, + const ciTypeFlow::StateVector* incoming) { + if (monitor_count() == -1) { + set_monitor_count(incoming->monitor_count()); + } + assert(monitor_count() == incoming->monitor_count(), "monitors must match"); + + if (stack_size() == -1) { + set_stack_size(1); + } + + assert(stack_size() == 1, "must have one-element stack"); + + bool different = false; + + // Meet locals from incoming array. + Cell limit = local(_outer->max_locals()-1); + for (Cell c = start_cell(); c <= limit; c = next_cell(c)) { + ciType* t1 = type_at(c); + ciType* t2 = incoming->type_at(c); + if (!t1->equals(t2)) { + ciType* new_type = type_meet(t1, t2); + if (!t1->equals(new_type)) { + set_type_at(c, new_type); + different = true; + } + } + } + + // Handle stack separately. When an exception occurs, the + // only stack entry is the exception instance. + ciType* tos_type = type_at_tos(); + if (!tos_type->equals(exc)) { + ciType* new_type = type_meet(tos_type, exc); + if (!tos_type->equals(new_type)) { + set_type_at_tos(new_type); + different = true; + } + } + + return different; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::push_translate +void ciTypeFlow::StateVector::push_translate(ciType* type) { + BasicType basic_type = type->basic_type(); + if (basic_type == T_BOOLEAN || basic_type == T_CHAR || + basic_type == T_BYTE || basic_type == T_SHORT) { + push_int(); + } else { + push(type); + if (type->is_two_word()) { + push(half_type(type)); + } + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_aaload +void ciTypeFlow::StateVector::do_aaload(ciBytecodeStream* str) { + pop_int(); + ciObjArrayKlass* array_klass = pop_objArray(); + if (array_klass == NULL) { + // Did aaload on a null reference; push a null and ignore the exception. + // This instruction will never continue normally. All we have to do + // is report a value that will meet correctly with any downstream + // reference types on paths that will truly be executed. This null type + // meets with any reference type to yield that same reference type. + // (The compiler will generate an unconditonal exception here.) + push(null_type()); + return; + } + if (!array_klass->is_loaded()) { + // Only fails for some -Xcomp runs + trap(str, array_klass, + Deoptimization::make_trap_request + (Deoptimization::Reason_unloaded, + Deoptimization::Action_reinterpret)); + return; + } + ciKlass* element_klass = array_klass->element_klass(); + if (!element_klass->is_loaded() && element_klass->is_instance_klass()) { + Untested("unloaded array element class in ciTypeFlow"); + trap(str, element_klass, + Deoptimization::make_trap_request + (Deoptimization::Reason_unloaded, + Deoptimization::Action_reinterpret)); + } else { + push_object(element_klass); + } +} + + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_checkcast +void ciTypeFlow::StateVector::do_checkcast(ciBytecodeStream* str) { + bool will_link; + ciKlass* klass = str->get_klass(will_link); + if (!will_link) { + // VM's interpreter will not load 'klass' if object is NULL. + // Type flow after this block may still be needed in two situations: + // 1) C2 uses do_null_assert() and continues compilation for later blocks + // 2) C2 does an OSR compile in a later block (see bug 4778368). + pop_object(); + do_null_assert(klass); + } else { + pop_object(); + push_object(klass); + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_getfield +void ciTypeFlow::StateVector::do_getfield(ciBytecodeStream* str) { + // could add assert here for type of object. + pop_object(); + do_getstatic(str); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_getstatic +void ciTypeFlow::StateVector::do_getstatic(ciBytecodeStream* str) { + bool will_link; + ciField* field = str->get_field(will_link); + if (!will_link) { + trap(str, field->holder(), str->get_field_holder_index()); + } else { + ciType* field_type = field->type(); + if (!field_type->is_loaded()) { + // Normally, we need the field's type to be loaded if we are to + // do anything interesting with its value. + // We used to do this: trap(str, str->get_field_signature_index()); + // + // There is one good reason not to trap here. Execution can + // get past this "getfield" or "getstatic" if the value of + // the field is null. As long as the value is null, the class + // does not need to be loaded! The compiler must assume that + // the value of the unloaded class reference is null; if the code + // ever sees a non-null value, loading has occurred. + // + // This actually happens often enough to be annoying. If the + // compiler throws an uncommon trap at this bytecode, you can + // get an endless loop of recompilations, when all the code + // needs to do is load a series of null values. Also, a trap + // here can make an OSR entry point unreachable, triggering the + // assert on non_osr_block in ciTypeFlow::get_start_state. + // (See bug 4379915.) + do_null_assert(field_type->as_klass()); + } else { + push_translate(field_type); + } + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_invoke +void ciTypeFlow::StateVector::do_invoke(ciBytecodeStream* str, + bool has_receiver) { + bool will_link; + ciMethod* method = str->get_method(will_link); + if (!will_link) { + // We weren't able to find the method. + ciKlass* unloaded_holder = method->holder(); + trap(str, unloaded_holder, str->get_method_holder_index()); + } else { + ciSignature* signature = method->signature(); + ciSignatureStream sigstr(signature); + int arg_size = signature->size(); + int stack_base = stack_size() - arg_size; + int i = 0; + for( ; !sigstr.at_return_type(); sigstr.next()) { + ciType* type = sigstr.type(); + ciType* stack_type = type_at(stack(stack_base + i++)); + // Do I want to check this type? + // assert(stack_type->is_subtype_of(type), "bad type for field value"); + if (type->is_two_word()) { + ciType* stack_type2 = type_at(stack(stack_base + i++)); + assert(stack_type2->equals(half_type(type)), "must be 2nd half"); + } + } + assert(arg_size == i, "must match"); + for (int j = 0; j < arg_size; j++) { + pop(); + } + if (has_receiver) { + // Check this? + pop_object(); + } + assert(!sigstr.is_done(), "must have return type"); + ciType* return_type = sigstr.type(); + if (!return_type->is_void()) { + if (!return_type->is_loaded()) { + // As in do_getstatic(), generally speaking, we need the return type to + // be loaded if we are to do anything interesting with its value. + // We used to do this: trap(str, str->get_method_signature_index()); + // + // We do not trap here since execution can get past this invoke if + // the return value is null. As long as the value is null, the class + // does not need to be loaded! The compiler must assume that + // the value of the unloaded class reference is null; if the code + // ever sees a non-null value, loading has occurred. + // + // See do_getstatic() for similar explanation, as well as bug 4684993. + do_null_assert(return_type->as_klass()); + } else { + push_translate(return_type); + } + } + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_jsr +void ciTypeFlow::StateVector::do_jsr(ciBytecodeStream* str) { + push(ciReturnAddress::make(str->next_bci())); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_ldc +void ciTypeFlow::StateVector::do_ldc(ciBytecodeStream* str) { + ciConstant con = str->get_constant(); + BasicType basic_type = con.basic_type(); + if (basic_type == T_ILLEGAL) { + // OutOfMemoryError in the CI while loading constant + push_null(); + outer()->record_failure("ldc did not link"); + return; + } + if (basic_type == T_OBJECT || basic_type == T_ARRAY) { + ciObject* obj = con.as_object(); + if (obj->is_null_object()) { + push_null(); + } else if (obj->is_klass()) { + // The type of ldc is java.lang.Class + push_object(outer()->env()->Class_klass()); + } else { + push_object(obj->klass()); + } + } else { + push_translate(ciType::make(basic_type)); + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_multianewarray +void ciTypeFlow::StateVector::do_multianewarray(ciBytecodeStream* str) { + int dimensions = str->get_dimensions(); + bool will_link; + ciArrayKlass* array_klass = str->get_klass(will_link)->as_array_klass(); + if (!will_link) { + trap(str, array_klass, str->get_klass_index()); + } else { + for (int i = 0; i < dimensions; i++) { + pop_int(); + } + push_object(array_klass); + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_new +void ciTypeFlow::StateVector::do_new(ciBytecodeStream* str) { + bool will_link; + ciKlass* klass = str->get_klass(will_link); + if (!will_link) { + trap(str, klass, str->get_klass_index()); + } else { + push_object(klass); + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_newarray +void ciTypeFlow::StateVector::do_newarray(ciBytecodeStream* str) { + pop_int(); + ciKlass* klass = ciTypeArrayKlass::make((BasicType)str->get_index()); + push_object(klass); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_putfield +void ciTypeFlow::StateVector::do_putfield(ciBytecodeStream* str) { + do_putstatic(str); + if (_trap_bci != -1) return; // unloaded field holder, etc. + // could add assert here for type of object. + pop_object(); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_putstatic +void ciTypeFlow::StateVector::do_putstatic(ciBytecodeStream* str) { + bool will_link; + ciField* field = str->get_field(will_link); + if (!will_link) { + trap(str, field->holder(), str->get_field_holder_index()); + } else { + ciType* field_type = field->type(); + ciType* type = pop_value(); + // Do I want to check this type? + // assert(type->is_subtype_of(field_type), "bad type for field value"); + if (field_type->is_two_word()) { + ciType* type2 = pop_value(); + assert(type2->is_two_word(), "must be 2nd half"); + assert(type == half_type(type2), "must be 2nd half"); + } + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_ret +void ciTypeFlow::StateVector::do_ret(ciBytecodeStream* str) { + Cell index = local(str->get_index()); + + ciType* address = type_at(index); + assert(address->is_return_address(), "bad return address"); + set_type_at(index, bottom_type()); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::trap +// +// Stop interpretation of this path with a trap. +void ciTypeFlow::StateVector::trap(ciBytecodeStream* str, ciKlass* klass, int index) { + _trap_bci = str->cur_bci(); + _trap_index = index; + + // Log information about this trap: + CompileLog* log = outer()->env()->log(); + if (log != NULL) { + int mid = log->identify(outer()->method()); + int kid = (klass == NULL)? -1: log->identify(klass); + log->begin_elem("uncommon_trap method='%d' bci='%d'", mid, str->cur_bci()); + char buf[100]; + log->print(" %s", Deoptimization::format_trap_request(buf, sizeof(buf), + index)); + if (kid >= 0) + log->print(" klass='%d'", kid); + log->end_elem(); + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::do_null_assert +// Corresponds to graphKit::do_null_assert. +void ciTypeFlow::StateVector::do_null_assert(ciKlass* unloaded_klass) { + if (unloaded_klass->is_loaded()) { + // We failed to link, but we can still compute with this class, + // since it is loaded somewhere. The compiler will uncommon_trap + // if the object is not null, but the typeflow pass can not assume + // that the object will be null, otherwise it may incorrectly tell + // the parser that an object is known to be null. 4761344, 4807707 + push_object(unloaded_klass); + } else { + // The class is not loaded anywhere. It is safe to model the + // null in the typestates, because we can compile in a null check + // which will deoptimize us if someone manages to load the + // class later. + push_null(); + } +} + + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::apply_one_bytecode +// +// Apply the effect of one bytecode to this StateVector +bool ciTypeFlow::StateVector::apply_one_bytecode(ciBytecodeStream* str) { + _trap_bci = -1; + _trap_index = 0; + + if (CITraceTypeFlow) { + tty->print_cr(">> Interpreting bytecode %d:%s", str->cur_bci(), + Bytecodes::name(str->cur_bc())); + } + + switch(str->cur_bc()) { + case Bytecodes::_aaload: do_aaload(str); break; + + case Bytecodes::_aastore: + { + pop_object(); + pop_int(); + pop_objArray(); + break; + } + case Bytecodes::_aconst_null: + { + push_null(); + break; + } + case Bytecodes::_aload: load_local_object(str->get_index()); break; + case Bytecodes::_aload_0: load_local_object(0); break; + case Bytecodes::_aload_1: load_local_object(1); break; + case Bytecodes::_aload_2: load_local_object(2); break; + case Bytecodes::_aload_3: load_local_object(3); break; + + case Bytecodes::_anewarray: + { + pop_int(); + bool will_link; + ciKlass* element_klass = str->get_klass(will_link); + if (!will_link) { + trap(str, element_klass, str->get_klass_index()); + } else { + push_object(ciObjArrayKlass::make(element_klass)); + } + break; + } + case Bytecodes::_areturn: + case Bytecodes::_ifnonnull: + case Bytecodes::_ifnull: + { + pop_object(); + break; + } + case Bytecodes::_monitorenter: + { + pop_object(); + set_monitor_count(monitor_count() + 1); + break; + } + case Bytecodes::_monitorexit: + { + pop_object(); + assert(monitor_count() > 0, "must be a monitor to exit from"); + set_monitor_count(monitor_count() - 1); + break; + } + case Bytecodes::_arraylength: + { + pop_array(); + push_int(); + break; + } + case Bytecodes::_astore: store_local_object(str->get_index()); break; + case Bytecodes::_astore_0: store_local_object(0); break; + case Bytecodes::_astore_1: store_local_object(1); break; + case Bytecodes::_astore_2: store_local_object(2); break; + case Bytecodes::_astore_3: store_local_object(3); break; + + case Bytecodes::_athrow: + { + NEEDS_CLEANUP; + pop_object(); + break; + } + case Bytecodes::_baload: + case Bytecodes::_caload: + case Bytecodes::_iaload: + case Bytecodes::_saload: + { + pop_int(); + ciTypeArrayKlass* array_klass = pop_typeArray(); + // Put assert here for right type? + push_int(); + break; + } + case Bytecodes::_bastore: + case Bytecodes::_castore: + case Bytecodes::_iastore: + case Bytecodes::_sastore: + { + pop_int(); + pop_int(); + pop_typeArray(); + // assert here? + break; + } + case Bytecodes::_bipush: + case Bytecodes::_iconst_m1: + case Bytecodes::_iconst_0: + case Bytecodes::_iconst_1: + case Bytecodes::_iconst_2: + case Bytecodes::_iconst_3: + case Bytecodes::_iconst_4: + case Bytecodes::_iconst_5: + case Bytecodes::_sipush: + { + push_int(); + break; + } + case Bytecodes::_checkcast: do_checkcast(str); break; + + case Bytecodes::_d2f: + { + pop_double(); + push_float(); + break; + } + case Bytecodes::_d2i: + { + pop_double(); + push_int(); + break; + } + case Bytecodes::_d2l: + { + pop_double(); + push_long(); + break; + } + case Bytecodes::_dadd: + case Bytecodes::_ddiv: + case Bytecodes::_dmul: + case Bytecodes::_drem: + case Bytecodes::_dsub: + { + pop_double(); + pop_double(); + push_double(); + break; + } + case Bytecodes::_daload: + { + pop_int(); + ciTypeArrayKlass* array_klass = pop_typeArray(); + // Put assert here for right type? + push_double(); + break; + } + case Bytecodes::_dastore: + { + pop_double(); + pop_int(); + pop_typeArray(); + // assert here? + break; + } + case Bytecodes::_dcmpg: + case Bytecodes::_dcmpl: + { + pop_double(); + pop_double(); + push_int(); + break; + } + case Bytecodes::_dconst_0: + case Bytecodes::_dconst_1: + { + push_double(); + break; + } + case Bytecodes::_dload: load_local_double(str->get_index()); break; + case Bytecodes::_dload_0: load_local_double(0); break; + case Bytecodes::_dload_1: load_local_double(1); break; + case Bytecodes::_dload_2: load_local_double(2); break; + case Bytecodes::_dload_3: load_local_double(3); break; + + case Bytecodes::_dneg: + { + pop_double(); + push_double(); + break; + } + case Bytecodes::_dreturn: + { + pop_double(); + break; + } + case Bytecodes::_dstore: store_local_double(str->get_index()); break; + case Bytecodes::_dstore_0: store_local_double(0); break; + case Bytecodes::_dstore_1: store_local_double(1); break; + case Bytecodes::_dstore_2: store_local_double(2); break; + case Bytecodes::_dstore_3: store_local_double(3); break; + + case Bytecodes::_dup: + { + push(type_at_tos()); + break; + } + case Bytecodes::_dup_x1: + { + ciType* value1 = pop_value(); + ciType* value2 = pop_value(); + push(value1); + push(value2); + push(value1); + break; + } + case Bytecodes::_dup_x2: + { + ciType* value1 = pop_value(); + ciType* value2 = pop_value(); + ciType* value3 = pop_value(); + push(value1); + push(value3); + push(value2); + push(value1); + break; + } + case Bytecodes::_dup2: + { + ciType* value1 = pop_value(); + ciType* value2 = pop_value(); + push(value2); + push(value1); + push(value2); + push(value1); + break; + } + case Bytecodes::_dup2_x1: + { + ciType* value1 = pop_value(); + ciType* value2 = pop_value(); + ciType* value3 = pop_value(); + push(value2); + push(value1); + push(value3); + push(value2); + push(value1); + break; + } + case Bytecodes::_dup2_x2: + { + ciType* value1 = pop_value(); + ciType* value2 = pop_value(); + ciType* value3 = pop_value(); + ciType* value4 = pop_value(); + push(value2); + push(value1); + push(value4); + push(value3); + push(value2); + push(value1); + break; + } + case Bytecodes::_f2d: + { + pop_float(); + push_double(); + break; + } + case Bytecodes::_f2i: + { + pop_float(); + push_int(); + break; + } + case Bytecodes::_f2l: + { + pop_float(); + push_long(); + break; + } + case Bytecodes::_fadd: + case Bytecodes::_fdiv: + case Bytecodes::_fmul: + case Bytecodes::_frem: + case Bytecodes::_fsub: + { + pop_float(); + pop_float(); + push_float(); + break; + } + case Bytecodes::_faload: + { + pop_int(); + ciTypeArrayKlass* array_klass = pop_typeArray(); + // Put assert here. + push_float(); + break; + } + case Bytecodes::_fastore: + { + pop_float(); + pop_int(); + ciTypeArrayKlass* array_klass = pop_typeArray(); + // Put assert here. + break; + } + case Bytecodes::_fcmpg: + case Bytecodes::_fcmpl: + { + pop_float(); + pop_float(); + push_int(); + break; + } + case Bytecodes::_fconst_0: + case Bytecodes::_fconst_1: + case Bytecodes::_fconst_2: + { + push_float(); + break; + } + case Bytecodes::_fload: load_local_float(str->get_index()); break; + case Bytecodes::_fload_0: load_local_float(0); break; + case Bytecodes::_fload_1: load_local_float(1); break; + case Bytecodes::_fload_2: load_local_float(2); break; + case Bytecodes::_fload_3: load_local_float(3); break; + + case Bytecodes::_fneg: + { + pop_float(); + push_float(); + break; + } + case Bytecodes::_freturn: + { + pop_float(); + break; + } + case Bytecodes::_fstore: store_local_float(str->get_index()); break; + case Bytecodes::_fstore_0: store_local_float(0); break; + case Bytecodes::_fstore_1: store_local_float(1); break; + case Bytecodes::_fstore_2: store_local_float(2); break; + case Bytecodes::_fstore_3: store_local_float(3); break; + + case Bytecodes::_getfield: do_getfield(str); break; + case Bytecodes::_getstatic: do_getstatic(str); break; + + case Bytecodes::_goto: + case Bytecodes::_goto_w: + case Bytecodes::_nop: + case Bytecodes::_return: + { + // do nothing. + break; + } + case Bytecodes::_i2b: + case Bytecodes::_i2c: + case Bytecodes::_i2s: + case Bytecodes::_ineg: + { + pop_int(); + push_int(); + break; + } + case Bytecodes::_i2d: + { + pop_int(); + push_double(); + break; + } + case Bytecodes::_i2f: + { + pop_int(); + push_float(); + break; + } + case Bytecodes::_i2l: + { + pop_int(); + push_long(); + break; + } + case Bytecodes::_iadd: + case Bytecodes::_iand: + case Bytecodes::_idiv: + case Bytecodes::_imul: + case Bytecodes::_ior: + case Bytecodes::_irem: + case Bytecodes::_ishl: + case Bytecodes::_ishr: + case Bytecodes::_isub: + case Bytecodes::_iushr: + case Bytecodes::_ixor: + { + pop_int(); + pop_int(); + push_int(); + break; + } + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + { + pop_object(); + pop_object(); + break; + } + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpne: + { + pop_int(); + pop_int(); + break; + } + case Bytecodes::_ifeq: + case Bytecodes::_ifle: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifne: + case Bytecodes::_ireturn: + case Bytecodes::_lookupswitch: + case Bytecodes::_tableswitch: + { + pop_int(); + break; + } + case Bytecodes::_iinc: + { + check_int(local(str->get_index())); + break; + } + case Bytecodes::_iload: load_local_int(str->get_index()); break; + case Bytecodes::_iload_0: load_local_int(0); break; + case Bytecodes::_iload_1: load_local_int(1); break; + case Bytecodes::_iload_2: load_local_int(2); break; + case Bytecodes::_iload_3: load_local_int(3); break; + + case Bytecodes::_instanceof: + { + // Check for uncommon trap: + do_checkcast(str); + pop_object(); + push_int(); + break; + } + case Bytecodes::_invokeinterface: do_invoke(str, true); break; + case Bytecodes::_invokespecial: do_invoke(str, true); break; + case Bytecodes::_invokestatic: do_invoke(str, false); break; + + case Bytecodes::_invokevirtual: do_invoke(str, true); break; + + case Bytecodes::_istore: store_local_int(str->get_index()); break; + case Bytecodes::_istore_0: store_local_int(0); break; + case Bytecodes::_istore_1: store_local_int(1); break; + case Bytecodes::_istore_2: store_local_int(2); break; + case Bytecodes::_istore_3: store_local_int(3); break; + + case Bytecodes::_jsr: + case Bytecodes::_jsr_w: do_jsr(str); break; + + case Bytecodes::_l2d: + { + pop_long(); + push_double(); + break; + } + case Bytecodes::_l2f: + { + pop_long(); + push_float(); + break; + } + case Bytecodes::_l2i: + { + pop_long(); + push_int(); + break; + } + case Bytecodes::_ladd: + case Bytecodes::_land: + case Bytecodes::_ldiv: + case Bytecodes::_lmul: + case Bytecodes::_lor: + case Bytecodes::_lrem: + case Bytecodes::_lsub: + case Bytecodes::_lxor: + { + pop_long(); + pop_long(); + push_long(); + break; + } + case Bytecodes::_laload: + { + pop_int(); + ciTypeArrayKlass* array_klass = pop_typeArray(); + // Put assert here for right type? + push_long(); + break; + } + case Bytecodes::_lastore: + { + pop_long(); + pop_int(); + pop_typeArray(); + // assert here? + break; + } + case Bytecodes::_lcmp: + { + pop_long(); + pop_long(); + push_int(); + break; + } + case Bytecodes::_lconst_0: + case Bytecodes::_lconst_1: + { + push_long(); + break; + } + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + { + do_ldc(str); + break; + } + + case Bytecodes::_lload: load_local_long(str->get_index()); break; + case Bytecodes::_lload_0: load_local_long(0); break; + case Bytecodes::_lload_1: load_local_long(1); break; + case Bytecodes::_lload_2: load_local_long(2); break; + case Bytecodes::_lload_3: load_local_long(3); break; + + case Bytecodes::_lneg: + { + pop_long(); + push_long(); + break; + } + case Bytecodes::_lreturn: + { + pop_long(); + break; + } + case Bytecodes::_lshl: + case Bytecodes::_lshr: + case Bytecodes::_lushr: + { + pop_int(); + pop_long(); + push_long(); + break; + } + case Bytecodes::_lstore: store_local_long(str->get_index()); break; + case Bytecodes::_lstore_0: store_local_long(0); break; + case Bytecodes::_lstore_1: store_local_long(1); break; + case Bytecodes::_lstore_2: store_local_long(2); break; + case Bytecodes::_lstore_3: store_local_long(3); break; + + case Bytecodes::_multianewarray: do_multianewarray(str); break; + + case Bytecodes::_new: do_new(str); break; + + case Bytecodes::_newarray: do_newarray(str); break; + + case Bytecodes::_pop: + { + pop(); + break; + } + case Bytecodes::_pop2: + { + pop(); + pop(); + break; + } + + case Bytecodes::_putfield: do_putfield(str); break; + case Bytecodes::_putstatic: do_putstatic(str); break; + + case Bytecodes::_ret: do_ret(str); break; + + case Bytecodes::_swap: + { + ciType* value1 = pop_value(); + ciType* value2 = pop_value(); + push(value1); + push(value2); + break; + } + case Bytecodes::_wide: + default: + { + // The iterator should skip this. + ShouldNotReachHere(); + break; + } + } + + if (CITraceTypeFlow) { + print_on(tty); + } + + return (_trap_bci != -1); +} + +#ifndef PRODUCT +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::print_cell_on +void ciTypeFlow::StateVector::print_cell_on(outputStream* st, Cell c) const { + ciType* type = type_at(c); + if (type == top_type()) { + st->print("top"); + } else if (type == bottom_type()) { + st->print("bottom"); + } else if (type == null_type()) { + st->print("null"); + } else if (type == long2_type()) { + st->print("long2"); + } else if (type == double2_type()) { + st->print("double2"); + } else if (is_int(type)) { + st->print("int"); + } else if (is_long(type)) { + st->print("long"); + } else if (is_float(type)) { + st->print("float"); + } else if (is_double(type)) { + st->print("double"); + } else if (type->is_return_address()) { + st->print("address(%d)", type->as_return_address()->bci()); + } else { + if (type->is_klass()) { + type->as_klass()->name()->print_symbol_on(st); + } else { + st->print("UNEXPECTED TYPE"); + type->print(); + } + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::StateVector::print_on +void ciTypeFlow::StateVector::print_on(outputStream* st) const { + int num_locals = _outer->max_locals(); + int num_stack = stack_size(); + int num_monitors = monitor_count(); + st->print_cr(" State : locals %d, stack %d, monitors %d", num_locals, num_stack, num_monitors); + if (num_stack >= 0) { + int i; + for (i = 0; i < num_locals; i++) { + st->print(" local %2d : ", i); + print_cell_on(st, local(i)); + st->cr(); + } + for (i = 0; i < num_stack; i++) { + st->print(" stack %2d : ", i); + print_cell_on(st, stack(i)); + st->cr(); + } + } +} +#endif + +// ciTypeFlow::Block +// +// A basic block. + +// ------------------------------------------------------------------ +// ciTypeFlow::Block::Block +ciTypeFlow::Block::Block(ciTypeFlow* outer, + ciBlock *ciblk, + ciTypeFlow::JsrSet* jsrs) { + _ciblock = ciblk; + _exceptions = NULL; + _exc_klasses = NULL; + _successors = NULL; + _state = new (outer->arena()) StateVector(outer); + JsrSet* new_jsrs = + new (outer->arena()) JsrSet(outer->arena(), jsrs->size()); + jsrs->copy_into(new_jsrs); + _jsrs = new_jsrs; + _next = NULL; + _on_work_list = false; + _pre_order = -1; assert(!has_pre_order(), ""); + _private_copy = false; + _trap_bci = -1; + _trap_index = 0; + + if (CITraceTypeFlow) { + tty->print_cr(">> Created new block"); + print_on(tty); + } + + assert(this->outer() == outer, "outer link set up"); + assert(!outer->have_block_count(), "must not have mapped blocks yet"); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::Block::clone_loop_head +// +ciTypeFlow::Block* +ciTypeFlow::Block::clone_loop_head(ciTypeFlow* analyzer, + int branch_bci, + ciTypeFlow::Block* target, + ciTypeFlow::JsrSet* jsrs) { + // Loop optimizations are not performed on Tier1 compiles. Do nothing. + if (analyzer->env()->comp_level() < CompLevel_full_optimization) { + return target; + } + + // The current block ends with a branch. + // + // If the target block appears to be the test-clause of a for loop, and + // it is not too large, and it has not yet been cloned, clone it. + // The pre-existing copy becomes the private clone used only by + // the initial iteration of the loop. (We know we are simulating + // the initial iteration right now, since we have never calculated + // successors before for this block.) + + if (branch_bci <= start() + && (target->limit() - target->start()) <= CICloneLoopTestLimit + && target->private_copy_count() == 0) { + // Setting the private_copy bit ensures that the target block cannot be + // reached by any other paths, such as fall-in from the loop body. + // The private copy will be accessible only on successor lists + // created up to this point. + target->set_private_copy(true); + if (CITraceTypeFlow) { + tty->print(">> Cloning a test-clause block "); + print_value_on(tty); + tty->cr(); + } + // If the target is the current block, then later on a new copy of the + // target block will be created when its bytecodes are reached by + // an alternate path. (This is the case for loops with the loop + // head at the bci-wise bottom of the loop, as with pre-1.4.2 javac.) + // + // Otherwise, duplicate the target block now and use it immediately. + // (The case for loops with the loop head at the bci-wise top of the + // loop, as with 1.4.2 javac.) + // + // In either case, the new copy of the block will remain public. + if (target != this) { + target = analyzer->block_at(branch_bci, jsrs); + } + } + return target; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::Block::successors +// +// Get the successors for this Block. +GrowableArray* +ciTypeFlow::Block::successors(ciBytecodeStream* str, + ciTypeFlow::StateVector* state, + ciTypeFlow::JsrSet* jsrs) { + if (_successors == NULL) { + if (CITraceTypeFlow) { + tty->print(">> Computing successors for block "); + print_value_on(tty); + tty->cr(); + } + + ciTypeFlow* analyzer = outer(); + Arena* arena = analyzer->arena(); + Block* block = NULL; + bool has_successor = !has_trap() && + (control() != ciBlock::fall_through_bci || limit() < analyzer->code_size()); + if (!has_successor) { + _successors = + new (arena) GrowableArray(arena, 1, 0, NULL); + // No successors + } else if (control() == ciBlock::fall_through_bci) { + assert(str->cur_bci() == limit(), "bad block end"); + // This block simply falls through to the next. + _successors = + new (arena) GrowableArray(arena, 1, 0, NULL); + + Block* block = analyzer->block_at(limit(), _jsrs); + assert(_successors->length() == FALL_THROUGH, ""); + _successors->append(block); + } else { + int current_bci = str->cur_bci(); + int next_bci = str->next_bci(); + int branch_bci = -1; + Block* target = NULL; + assert(str->next_bci() == limit(), "bad block end"); + // This block is not a simple fall-though. Interpret + // the current bytecode to find our successors. + switch (str->cur_bc()) { + case Bytecodes::_ifeq: case Bytecodes::_ifne: + case Bytecodes::_iflt: case Bytecodes::_ifge: + case Bytecodes::_ifgt: case Bytecodes::_ifle: + case Bytecodes::_if_icmpeq: case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: case Bytecodes::_if_icmple: + case Bytecodes::_if_acmpeq: case Bytecodes::_if_acmpne: + case Bytecodes::_ifnull: case Bytecodes::_ifnonnull: + // Our successors are the branch target and the next bci. + branch_bci = str->get_dest(); + clone_loop_head(analyzer, branch_bci, this, jsrs); + _successors = + new (arena) GrowableArray(arena, 2, 0, NULL); + assert(_successors->length() == IF_NOT_TAKEN, ""); + _successors->append(analyzer->block_at(next_bci, jsrs)); + assert(_successors->length() == IF_TAKEN, ""); + _successors->append(analyzer->block_at(branch_bci, jsrs)); + break; + + case Bytecodes::_goto: + branch_bci = str->get_dest(); + _successors = + new (arena) GrowableArray(arena, 1, 0, NULL); + assert(_successors->length() == GOTO_TARGET, ""); + target = analyzer->block_at(branch_bci, jsrs); + // If the target block has not been visited yet, and looks like + // a two-way branch, attempt to clone it if it is a loop head. + if (target->_successors != NULL + && target->_successors->length() == (IF_TAKEN + 1)) { + target = clone_loop_head(analyzer, branch_bci, target, jsrs); + } + _successors->append(target); + break; + + case Bytecodes::_jsr: + branch_bci = str->get_dest(); + _successors = + new (arena) GrowableArray(arena, 1, 0, NULL); + assert(_successors->length() == GOTO_TARGET, ""); + _successors->append(analyzer->block_at(branch_bci, jsrs)); + break; + + case Bytecodes::_goto_w: + case Bytecodes::_jsr_w: + _successors = + new (arena) GrowableArray(arena, 1, 0, NULL); + assert(_successors->length() == GOTO_TARGET, ""); + _successors->append(analyzer->block_at(str->get_far_dest(), jsrs)); + break; + + case Bytecodes::_tableswitch: { + Bytecode_tableswitch *tableswitch = + Bytecode_tableswitch_at(str->cur_bcp()); + + int len = tableswitch->length(); + _successors = + new (arena) GrowableArray(arena, len+1, 0, NULL); + int bci = current_bci + tableswitch->default_offset(); + Block* block = analyzer->block_at(bci, jsrs); + assert(_successors->length() == SWITCH_DEFAULT, ""); + _successors->append(block); + while (--len >= 0) { + int bci = current_bci + tableswitch->dest_offset_at(len); + block = analyzer->block_at(bci, jsrs); + assert(_successors->length() >= SWITCH_CASES, ""); + _successors->append_if_missing(block); + } + break; + } + + case Bytecodes::_lookupswitch: { + Bytecode_lookupswitch *lookupswitch = + Bytecode_lookupswitch_at(str->cur_bcp()); + + int npairs = lookupswitch->number_of_pairs(); + _successors = + new (arena) GrowableArray(arena, npairs+1, 0, NULL); + int bci = current_bci + lookupswitch->default_offset(); + Block* block = analyzer->block_at(bci, jsrs); + assert(_successors->length() == SWITCH_DEFAULT, ""); + _successors->append(block); + while(--npairs >= 0) { + LookupswitchPair *pair = lookupswitch->pair_at(npairs); + int bci = current_bci + pair->offset(); + Block* block = analyzer->block_at(bci, jsrs); + assert(_successors->length() >= SWITCH_CASES, ""); + _successors->append_if_missing(block); + } + break; + } + + case Bytecodes::_athrow: case Bytecodes::_ireturn: + case Bytecodes::_lreturn: case Bytecodes::_freturn: + case Bytecodes::_dreturn: case Bytecodes::_areturn: + case Bytecodes::_return: + _successors = + new (arena) GrowableArray(arena, 1, 0, NULL); + // No successors + break; + + case Bytecodes::_ret: { + _successors = + new (arena) GrowableArray(arena, 1, 0, NULL); + + Cell local = state->local(str->get_index()); + ciType* return_address = state->type_at(local); + assert(return_address->is_return_address(), "verify: wrong type"); + int bci = return_address->as_return_address()->bci(); + assert(_successors->length() == GOTO_TARGET, ""); + _successors->append(analyzer->block_at(bci, jsrs)); + break; + } + + case Bytecodes::_wide: + default: + ShouldNotReachHere(); + break; + } + } + } + return _successors; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::Block:compute_exceptions +// +// Compute the exceptional successors and types for this Block. +void ciTypeFlow::Block::compute_exceptions() { + assert(_exceptions == NULL && _exc_klasses == NULL, "repeat"); + + if (CITraceTypeFlow) { + tty->print(">> Computing exceptions for block "); + print_value_on(tty); + tty->cr(); + } + + ciTypeFlow* analyzer = outer(); + Arena* arena = analyzer->arena(); + + // Any bci in the block will do. + ciExceptionHandlerStream str(analyzer->method(), start()); + + // Allocate our growable arrays. + int exc_count = str.count(); + _exceptions = new (arena) GrowableArray(arena, exc_count, 0, NULL); + _exc_klasses = new (arena) GrowableArray(arena, exc_count, + 0, NULL); + + for ( ; !str.is_done(); str.next()) { + ciExceptionHandler* handler = str.handler(); + int bci = handler->handler_bci(); + ciInstanceKlass* klass = NULL; + if (bci == -1) { + // There is no catch all. It is possible to exit the method. + break; + } + if (handler->is_catch_all()) { + klass = analyzer->env()->Throwable_klass(); + } else { + klass = handler->catch_klass(); + } + _exceptions->append(analyzer->block_at(bci, _jsrs)); + _exc_klasses->append(klass); + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::Block::is_simpler_than +// +// A relation used to order our work list. We work on a block earlier +// if it has a smaller jsr stack or it occurs earlier in the program +// text. +// +// Note: maybe we should redo this functionality to make blocks +// which correspond to exceptions lower priority. +bool ciTypeFlow::Block::is_simpler_than(ciTypeFlow::Block* other) { + if (other == NULL) { + return true; + } else { + int size1 = _jsrs->size(); + int size2 = other->_jsrs->size(); + if (size1 < size2) { + return true; + } else if (size2 < size1) { + return false; + } else { +#if 0 + if (size1 > 0) { + int r1 = _jsrs->record_at(0)->return_address(); + int r2 = _jsrs->record_at(0)->return_address(); + if (r1 < r2) { + return true; + } else if (r2 < r1) { + return false; + } else { + int e1 = _jsrs->record_at(0)->return_address(); + int e2 = _jsrs->record_at(0)->return_address(); + if (e1 < e2) { + return true; + } else if (e2 < e1) { + return false; + } + } + } +#endif + return (start() <= other->start()); + } + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::Block::set_private_copy +// Use this only to make a pre-existing public block into a private copy. +void ciTypeFlow::Block::set_private_copy(bool z) { + assert(z || (z == is_private_copy()), "cannot make a private copy public"); + _private_copy = z; +} + +#ifndef PRODUCT +// ------------------------------------------------------------------ +// ciTypeFlow::Block::print_value_on +void ciTypeFlow::Block::print_value_on(outputStream* st) const { + if (has_pre_order()) st->print("#%-2d ", pre_order()); + st->print("[%d - %d)", start(), limit()); + if (_jsrs->size() > 0) { st->print("/"); _jsrs->print_on(st); } + if (is_private_copy()) st->print("/private_copy"); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::Block::print_on +void ciTypeFlow::Block::print_on(outputStream* st) const { + if ((Verbose || WizardMode)) { + outer()->method()->print_codes_on(start(), limit(), st); + } + st->print_cr(" ==================================================== "); + st->print (" "); + print_value_on(st); + st->cr(); + _state->print_on(st); + if (_successors == NULL) { + st->print_cr(" No successor information"); + } else { + int num_successors = _successors->length(); + st->print_cr(" Successors : %d", num_successors); + for (int i = 0; i < num_successors; i++) { + Block* successor = _successors->at(i); + st->print(" "); + successor->print_value_on(st); + st->cr(); + } + } + if (_exceptions == NULL) { + st->print_cr(" No exception information"); + } else { + int num_exceptions = _exceptions->length(); + st->print_cr(" Exceptions : %d", num_exceptions); + for (int i = 0; i < num_exceptions; i++) { + Block* exc_succ = _exceptions->at(i); + ciInstanceKlass* exc_klass = _exc_klasses->at(i); + st->print(" "); + exc_succ->print_value_on(st); + st->print(" -- "); + exc_klass->name()->print_symbol_on(st); + st->cr(); + } + } + if (has_trap()) { + st->print_cr(" Traps on %d with trap index %d", trap_bci(), trap_index()); + } + st->print_cr(" ==================================================== "); +} +#endif + +// ciTypeFlow +// +// This is a pass over the bytecodes which computes the following: +// basic block structure +// interpreter type-states (a la the verifier) + +// ------------------------------------------------------------------ +// ciTypeFlow::ciTypeFlow +ciTypeFlow::ciTypeFlow(ciEnv* env, ciMethod* method, int osr_bci) { + _env = env; + _method = method; + _methodBlocks = method->get_method_blocks(); + _max_locals = method->max_locals(); + _max_stack = method->max_stack(); + _code_size = method->code_size(); + _osr_bci = osr_bci; + _failure_reason = NULL; + assert(start_bci() >= 0 && start_bci() < code_size() , "correct osr_bci argument"); + + _work_list = NULL; + _next_pre_order = 0; + + _ciblock_count = _methodBlocks->num_blocks(); + _idx_to_blocklist = NEW_ARENA_ARRAY(arena(), GrowableArray*, _ciblock_count); + for (int i = 0; i < _ciblock_count; i++) { + _idx_to_blocklist[i] = NULL; + } + _block_map = NULL; // until all blocks are seen + _jsr_count = 0; + _jsr_records = NULL; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::work_list_next +// +// Get the next basic block from our work list. +ciTypeFlow::Block* ciTypeFlow::work_list_next() { + assert(!work_list_empty(), "work list must not be empty"); + Block* next_block = _work_list; + _work_list = next_block->next(); + next_block->set_next(NULL); + next_block->set_on_work_list(false); + if (!next_block->has_pre_order()) { + // Assign "pre_order" as each new block is taken from the work list. + // This number may be used by following phases to order block visits. + assert(!have_block_count(), "must not have mapped blocks yet") + next_block->set_pre_order(_next_pre_order++); + } + return next_block; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::add_to_work_list +// +// Add a basic block to our work list. +void ciTypeFlow::add_to_work_list(ciTypeFlow::Block* block) { + assert(!block->is_on_work_list(), "must not already be on work list"); + + if (CITraceTypeFlow) { + tty->print(">> Adding block%s ", block->has_pre_order() ? " (again)" : ""); + block->print_value_on(tty); + tty->print_cr(" to the work list : "); + } + + block->set_on_work_list(true); + if (block->is_simpler_than(_work_list)) { + block->set_next(_work_list); + _work_list = block; + } else { + Block *temp = _work_list; + while (!block->is_simpler_than(temp->next())) { + if (CITraceTypeFlow) { + tty->print("."); + } + temp = temp->next(); + } + block->set_next(temp->next()); + temp->set_next(block); + } + if (CITraceTypeFlow) { + tty->cr(); + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::block_at +// +// Return the block beginning at bci which has a JsrSet compatible +// with jsrs. +ciTypeFlow::Block* ciTypeFlow::block_at(int bci, ciTypeFlow::JsrSet* jsrs, CreateOption option) { + // First find the right ciBlock. + if (CITraceTypeFlow) { + tty->print(">> Requesting block for %d/", bci); + jsrs->print_on(tty); + tty->cr(); + } + + ciBlock* ciblk = _methodBlocks->block_containing(bci); + assert(ciblk->start_bci() == bci, "bad ciBlock boundaries"); + Block* block = get_block_for(ciblk->index(), jsrs, option); + + assert(block == NULL? (option == no_create): block->is_private_copy() == (option == create_private_copy), "create option consistent with result"); + + if (CITraceTypeFlow) { + if (block != NULL) { + tty->print(">> Found block "); + block->print_value_on(tty); + tty->cr(); + } else { + tty->print_cr(">> No such block."); + } + } + + return block; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::make_jsr_record +// +// Make a JsrRecord for a given (entry, return) pair, if such a record +// does not already exist. +ciTypeFlow::JsrRecord* ciTypeFlow::make_jsr_record(int entry_address, + int return_address) { + if (_jsr_records == NULL) { + _jsr_records = new (arena()) GrowableArray(arena(), + _jsr_count, + 0, + NULL); + } + JsrRecord* record = NULL; + int len = _jsr_records->length(); + for (int i = 0; i < len; i++) { + JsrRecord* record = _jsr_records->at(i); + if (record->entry_address() == entry_address && + record->return_address() == return_address) { + return record; + } + } + + record = new (arena()) JsrRecord(entry_address, return_address); + _jsr_records->append(record); + return record; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::flow_exceptions +// +// Merge the current state into all exceptional successors at the +// current point in the code. +void ciTypeFlow::flow_exceptions(GrowableArray* exceptions, + GrowableArray* exc_klasses, + ciTypeFlow::StateVector* state) { + int len = exceptions->length(); + assert(exc_klasses->length() == len, "must have same length"); + for (int i = 0; i < len; i++) { + Block* block = exceptions->at(i); + ciInstanceKlass* exception_klass = exc_klasses->at(i); + + if (!exception_klass->is_loaded()) { + // Do not compile any code for unloaded exception types. + // Following compiler passes are responsible for doing this also. + continue; + } + + if (block->meet_exception(exception_klass, state)) { + // Block was modified. Add it to the work list. + if (!block->is_on_work_list()) { + add_to_work_list(block); + } + } + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::flow_successors +// +// Merge the current state into all successors at the current point +// in the code. +void ciTypeFlow::flow_successors(GrowableArray* successors, + ciTypeFlow::StateVector* state) { + int len = successors->length(); + for (int i = 0; i < len; i++) { + Block* block = successors->at(i); + if (block->meet(state)) { + // Block was modified. Add it to the work list. + if (!block->is_on_work_list()) { + add_to_work_list(block); + } + } + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::can_trap +// +// Tells if a given instruction is able to generate an exception edge. +bool ciTypeFlow::can_trap(ciBytecodeStream& str) { + // Cf. GenerateOopMap::do_exception_edge. + if (!Bytecodes::can_trap(str.cur_bc())) return false; + + switch (str.cur_bc()) { + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + case Bytecodes::_aload_0: + // These bytecodes can trap for rewriting. We need to assume that + // they do not throw exceptions to make the monitor analysis work. + return false; + + case Bytecodes::_ireturn: + case Bytecodes::_lreturn: + case Bytecodes::_freturn: + case Bytecodes::_dreturn: + case Bytecodes::_areturn: + case Bytecodes::_return: + // We can assume the monitor stack is empty in this analysis. + return false; + + case Bytecodes::_monitorexit: + // We can assume monitors are matched in this analysis. + return false; + } + + return true; +} + + +// ------------------------------------------------------------------ +// ciTypeFlow::flow_block +// +// Interpret the effects of the bytecodes on the incoming state +// vector of a basic block. Push the changed state to succeeding +// basic blocks. +void ciTypeFlow::flow_block(ciTypeFlow::Block* block, + ciTypeFlow::StateVector* state, + ciTypeFlow::JsrSet* jsrs) { + if (CITraceTypeFlow) { + tty->print("\n>> ANALYZING BLOCK : "); + tty->cr(); + block->print_on(tty); + } + assert(block->has_pre_order(), "pre-order is assigned before 1st flow"); + + int start = block->start(); + int limit = block->limit(); + int control = block->control(); + if (control != ciBlock::fall_through_bci) { + limit = control; + } + + // Grab the state from the current block. + block->copy_state_into(state); + + GrowableArray* exceptions = block->exceptions(); + GrowableArray* exc_klasses = block->exc_klasses(); + bool has_exceptions = exceptions->length() > 0; + + ciBytecodeStream str(method()); + str.reset_to_bci(start); + Bytecodes::Code code; + while ((code = str.next()) != ciBytecodeStream::EOBC() && + str.cur_bci() < limit) { + // Check for exceptional control flow from this point. + if (has_exceptions && can_trap(str)) { + flow_exceptions(exceptions, exc_klasses, state); + } + // Apply the effects of the current bytecode to our state. + bool res = state->apply_one_bytecode(&str); + + // Watch for bailouts. + if (failing()) return; + + if (res) { + + // We have encountered a trap. Record it in this block. + block->set_trap(state->trap_bci(), state->trap_index()); + + if (CITraceTypeFlow) { + tty->print_cr(">> Found trap"); + block->print_on(tty); + } + + // Record (no) successors. + block->successors(&str, state, jsrs); + + // Discontinue interpretation of this Block. + return; + } + } + + GrowableArray* successors = NULL; + if (control != ciBlock::fall_through_bci) { + // Check for exceptional control flow from this point. + if (has_exceptions && can_trap(str)) { + flow_exceptions(exceptions, exc_klasses, state); + } + + // Fix the JsrSet to reflect effect of the bytecode. + block->copy_jsrs_into(jsrs); + jsrs->apply_control(this, &str, state); + + // Find successor edges based on old state and new JsrSet. + successors = block->successors(&str, state, jsrs); + + // Apply the control changes to the state. + state->apply_one_bytecode(&str); + } else { + // Fall through control + successors = block->successors(&str, NULL, NULL); + } + + // Pass our state to successors. + flow_successors(successors, state); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::flow_types +// +// Perform the type flow analysis, creating and cloning Blocks as +// necessary. +void ciTypeFlow::flow_types() { + ResourceMark rm; + StateVector* temp_vector = new StateVector(this); + JsrSet* temp_set = new JsrSet(NULL, 16); + + // Create the method entry block. + Block* block = block_at(start_bci(), temp_set); + block->set_pre_order(_next_pre_order++); + assert(block->is_start(), "start block must have order #0"); + + // Load the initial state into it. + const StateVector* start_state = get_start_state(); + if (failing()) return; + block->meet(start_state); + add_to_work_list(block); + + // Trickle away. + while (!work_list_empty()) { + Block* block = work_list_next(); + flow_block(block, temp_vector, temp_set); + + + // NodeCountCutoff is the number of nodes at which the parser + // will bail out. Probably if we already have lots of BBs, + // the parser will generate at least twice that many nodes and bail out. + // Therefore, this is a conservatively large limit at which to + // bail out in the pre-parse typeflow pass. + int block_limit = MaxNodeLimit / 2; + + if (_next_pre_order >= block_limit) { + // Too many basic blocks. Bail out. + // + // This can happen when try/finally constructs are nested to depth N, + // and there is O(2**N) cloning of jsr bodies. See bug 4697245! + record_failure("too many basic blocks"); + return; + } + + // Watch for bailouts. + if (failing()) return; + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::map_blocks +// +// Create the block map, which indexes blocks in pre_order. +void ciTypeFlow::map_blocks() { + assert(_block_map == NULL, "single initialization"); + int pre_order_limit = _next_pre_order; + _block_map = NEW_ARENA_ARRAY(arena(), Block*, pre_order_limit); + assert(pre_order_limit == block_count(), ""); + int po; + for (po = 0; po < pre_order_limit; po++) { + debug_only(_block_map[po] = NULL); + } + ciMethodBlocks *mblks = _methodBlocks; + ciBlock* current = NULL; + int limit_bci = code_size(); + for (int bci = 0; bci < limit_bci; bci++) { + ciBlock* ciblk = mblks->block_containing(bci); + if (ciblk != NULL && ciblk != current) { + current = ciblk; + int curidx = ciblk->index(); + int block_count = (_idx_to_blocklist[curidx] == NULL) ? 0 : _idx_to_blocklist[curidx]->length(); + for (int i = 0; i < block_count; i++) { + Block* block = _idx_to_blocklist[curidx]->at(i); + if (!block->has_pre_order()) continue; + int po = block->pre_order(); + assert(_block_map[po] == NULL, "unique ref to block"); + assert(0 <= po && po < pre_order_limit, ""); + _block_map[po] = block; + } + } + } + for (po = 0; po < pre_order_limit; po++) { + assert(_block_map[po] != NULL, "must not drop any blocks"); + Block* block = _block_map[po]; + // Remove dead blocks from successor lists: + for (int e = 0; e <= 1; e++) { + GrowableArray* l = e? block->exceptions(): block->successors(); + for (int i = 0; i < l->length(); i++) { + Block* s = l->at(i); + if (!s->has_pre_order()) { + if (CITraceTypeFlow) { + tty->print("Removing dead %s successor of #%d: ", (e? "exceptional": "normal"), block->pre_order()); + s->print_value_on(tty); + tty->cr(); + } + l->remove(s); + --i; + } + } + } + } +} + +// ------------------------------------------------------------------ +// ciTypeFlow::get_block_for +// +// Find a block with this ciBlock which has a compatible JsrSet. +// If no such block exists, create it, unless the option is no_create. +// If the option is create_private_copy, always create a fresh private copy. +ciTypeFlow::Block* ciTypeFlow::get_block_for(int ciBlockIndex, ciTypeFlow::JsrSet* jsrs, CreateOption option) { + Arena* a = arena(); + GrowableArray* blocks = _idx_to_blocklist[ciBlockIndex]; + if (blocks == NULL) { + // Query only? + if (option == no_create) return NULL; + + // Allocate the growable array. + blocks = new (a) GrowableArray(a, 4, 0, NULL); + _idx_to_blocklist[ciBlockIndex] = blocks; + } + + if (option != create_private_copy) { + int len = blocks->length(); + for (int i = 0; i < len; i++) { + Block* block = blocks->at(i); + if (!block->is_private_copy() && block->is_compatible_with(jsrs)) { + return block; + } + } + } + + // Query only? + if (option == no_create) return NULL; + + // We did not find a compatible block. Create one. + Block* new_block = new (a) Block(this, _methodBlocks->block(ciBlockIndex), jsrs); + if (option == create_private_copy) new_block->set_private_copy(true); + blocks->append(new_block); + return new_block; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::private_copy_count +// +int ciTypeFlow::private_copy_count(int ciBlockIndex, ciTypeFlow::JsrSet* jsrs) const { + GrowableArray* blocks = _idx_to_blocklist[ciBlockIndex]; + + if (blocks == NULL) { + return 0; + } + + int count = 0; + int len = blocks->length(); + for (int i = 0; i < len; i++) { + Block* block = blocks->at(i); + if (block->is_private_copy() && block->is_compatible_with(jsrs)) { + count++; + } + } + + return count; +} + +// ------------------------------------------------------------------ +// ciTypeFlow::do_flow +// +// Perform type inference flow analysis. +void ciTypeFlow::do_flow() { + if (CITraceTypeFlow) { + tty->print_cr("\nPerforming flow analysis on method"); + method()->print(); + if (is_osr_flow()) tty->print(" at OSR bci %d", start_bci()); + tty->cr(); + method()->print_codes(); + } + if (CITraceTypeFlow) { + tty->print_cr("Initial CI Blocks"); + print_on(tty); + } + flow_types(); + // Watch for bailouts. + if (failing()) { + return; + } + if (CIPrintTypeFlow || CITraceTypeFlow) { + print_on(tty); + } + map_blocks(); +} + +// ------------------------------------------------------------------ +// ciTypeFlow::record_failure() +// The ciTypeFlow object keeps track of failure reasons separately from the ciEnv. +// This is required because there is not a 1-1 relation between the ciEnv and +// the TypeFlow passes within a compilation task. For example, if the compiler +// is considering inlining a method, it will request a TypeFlow. If that fails, +// the compilation as a whole may continue without the inlining. Some TypeFlow +// requests are not optional; if they fail the requestor is responsible for +// copying the failure reason up to the ciEnv. (See Parse::Parse.) +void ciTypeFlow::record_failure(const char* reason) { + if (env()->log() != NULL) { + env()->log()->elem("failure reason='%s' phase='typeflow'", reason); + } + if (_failure_reason == NULL) { + // Record the first failure reason. + _failure_reason = reason; + } +} + +#ifndef PRODUCT +// ------------------------------------------------------------------ +// ciTypeFlow::print_on +void ciTypeFlow::print_on(outputStream* st) const { + // Walk through CI blocks + st->print_cr("********************************************************"); + st->print ("TypeFlow for "); + method()->name()->print_symbol_on(st); + int limit_bci = code_size(); + st->print_cr(" %d bytes", limit_bci); + ciMethodBlocks *mblks = _methodBlocks; + ciBlock* current = NULL; + for (int bci = 0; bci < limit_bci; bci++) { + ciBlock* blk = mblks->block_containing(bci); + if (blk != NULL && blk != current) { + current = blk; + current->print_on(st); + + GrowableArray* blocks = _idx_to_blocklist[blk->index()]; + int num_blocks = (blocks == NULL) ? 0 : blocks->length(); + + if (num_blocks == 0) { + st->print_cr(" No Blocks"); + } else { + for (int i = 0; i < num_blocks; i++) { + Block* block = blocks->at(i); + block->print_on(st); + } + } + st->print_cr("--------------------------------------------------------"); + st->cr(); + } + } + st->print_cr("********************************************************"); + st->cr(); +} +#endif diff --git a/hotspot/src/share/vm/ci/ciTypeFlow.hpp b/hotspot/src/share/vm/ci/ciTypeFlow.hpp new file mode 100644 index 00000000000..2235a90a1df --- /dev/null +++ b/hotspot/src/share/vm/ci/ciTypeFlow.hpp @@ -0,0 +1,714 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +class ciTypeFlow : public ResourceObj { +private: + ciEnv* _env; + ciMethod* _method; + ciMethodBlocks* _methodBlocks; + int _osr_bci; + + // information cached from the method: + int _max_locals; + int _max_stack; + int _code_size; + + const char* _failure_reason; + +public: + class StateVector; + class Block; + + // Build a type flow analyzer + // Do an OSR analysis if osr_bci >= 0. + ciTypeFlow(ciEnv* env, ciMethod* method, int osr_bci = InvocationEntryBci); + + // Accessors + ciMethod* method() const { return _method; } + ciEnv* env() { return _env; } + Arena* arena() { return _env->arena(); } + bool is_osr_flow() const{ return _osr_bci != InvocationEntryBci; } + int start_bci() const { return is_osr_flow()? _osr_bci: 0; } + int max_locals() const { return _max_locals; } + int max_stack() const { return _max_stack; } + int max_cells() const { return _max_locals + _max_stack; } + int code_size() const { return _code_size; } + + // Represents information about an "active" jsr call. This + // class represents a call to the routine at some entry address + // with some distinct return address. + class JsrRecord : public ResourceObj { + private: + int _entry_address; + int _return_address; + public: + JsrRecord(int entry_address, int return_address) { + _entry_address = entry_address; + _return_address = return_address; + } + + int entry_address() const { return _entry_address; } + int return_address() const { return _return_address; } + + void print_on(outputStream* st) const { +#ifndef PRODUCT + st->print("%d->%d", entry_address(), return_address()); +#endif + } + }; + + // A JsrSet represents some set of JsrRecords. This class + // is used to record a set of all jsr routines which we permit + // execution to return (ret) from. + // + // During abstract interpretation, JsrSets are used to determine + // whether two paths which reach a given block are unique, and + // should be cloned apart, or are compatible, and should merge + // together. + // + // Note that different amounts of effort can be expended determining + // if paths are compatible. + class JsrSet : public ResourceObj { + private: + GrowableArray* _set; + + JsrRecord* record_at(int i) { + return _set->at(i); + } + + // Insert the given JsrRecord into the JsrSet, maintaining the order + // of the set and replacing any element with the same entry address. + void insert_jsr_record(JsrRecord* record); + + // Remove the JsrRecord with the given return address from the JsrSet. + void remove_jsr_record(int return_address); + + public: + JsrSet(Arena* arena, int default_len = 4); + + // Copy this JsrSet. + void copy_into(JsrSet* jsrs); + + // Is this JsrSet compatible with some other JsrSet? + bool is_compatible_with(JsrSet* other); + + // Apply the effect of a single bytecode to the JsrSet. + void apply_control(ciTypeFlow* analyzer, + ciBytecodeStream* str, + StateVector* state); + + // What is the cardinality of this set? + int size() const { return _set->length(); } + + void print_on(outputStream* st) const PRODUCT_RETURN; + }; + + // Used as a combined index for locals and temps + enum Cell { + Cell_0 + }; + + // A StateVector summarizes the type information at some + // point in the program + class StateVector : public ResourceObj { + private: + ciType** _types; + int _stack_size; + int _monitor_count; + ciTypeFlow* _outer; + + int _trap_bci; + int _trap_index; + + static ciType* type_meet_internal(ciType* t1, ciType* t2, ciTypeFlow* analyzer); + + public: + // Special elements in our type lattice. + enum { + T_TOP = T_VOID, // why not? + T_BOTTOM = T_CONFLICT, + T_LONG2 = T_SHORT, // 2nd word of T_LONG + T_DOUBLE2 = T_CHAR, // 2nd word of T_DOUBLE + T_NULL = T_BYTE // for now. + }; + static ciType* top_type() { return ciType::make((BasicType)T_TOP); } + static ciType* bottom_type() { return ciType::make((BasicType)T_BOTTOM); } + static ciType* long2_type() { return ciType::make((BasicType)T_LONG2); } + static ciType* double2_type(){ return ciType::make((BasicType)T_DOUBLE2); } + static ciType* null_type() { return ciType::make((BasicType)T_NULL); } + + static ciType* half_type(ciType* t) { + switch (t->basic_type()) { + case T_LONG: return long2_type(); + case T_DOUBLE: return double2_type(); + default: ShouldNotReachHere(); return NULL; + } + } + + // The meet operation for our type lattice. + ciType* type_meet(ciType* t1, ciType* t2) { + return type_meet_internal(t1, t2, outer()); + } + + // Accessors + ciTypeFlow* outer() const { return _outer; } + + int stack_size() const { return _stack_size; } + void set_stack_size(int ss) { _stack_size = ss; } + + int monitor_count() const { return _monitor_count; } + void set_monitor_count(int mc) { _monitor_count = mc; } + + static Cell start_cell() { return (Cell)0; } + static Cell next_cell(Cell c) { return (Cell)(((int)c) + 1); } + Cell limit_cell() const { + return (Cell)(outer()->max_locals() + stack_size()); + } + + // Cell creation + Cell local(int lnum) const { + assert(lnum < outer()->max_locals(), "index check"); + return (Cell)(lnum); + } + + Cell stack(int snum) const { + assert(snum < stack_size(), "index check"); + return (Cell)(outer()->max_locals() + snum); + } + + Cell tos() const { return stack(stack_size()-1); } + + // For external use only: + ciType* local_type_at(int i) const { return type_at(local(i)); } + ciType* stack_type_at(int i) const { return type_at(stack(i)); } + + // Accessors for the type of some Cell c + ciType* type_at(Cell c) const { + assert(start_cell() <= c && c < limit_cell(), "out of bounds"); + return _types[c]; + } + + void set_type_at(Cell c, ciType* type) { + assert(start_cell() <= c && c < limit_cell(), "out of bounds"); + _types[c] = type; + } + + // Top-of-stack operations. + void set_type_at_tos(ciType* type) { set_type_at(tos(), type); } + ciType* type_at_tos() const { return type_at(tos()); } + + void push(ciType* type) { + _stack_size++; + set_type_at_tos(type); + } + void pop() { + debug_only(set_type_at_tos(bottom_type())); + _stack_size--; + } + ciType* pop_value() { + ciType* t = type_at_tos(); + pop(); + return t; + } + + // Convenience operations. + bool is_reference(ciType* type) const { + return type == null_type() || !type->is_primitive_type(); + } + bool is_int(ciType* type) const { + return type->basic_type() == T_INT; + } + bool is_long(ciType* type) const { + return type->basic_type() == T_LONG; + } + bool is_float(ciType* type) const { + return type->basic_type() == T_FLOAT; + } + bool is_double(ciType* type) const { + return type->basic_type() == T_DOUBLE; + } + + void push_translate(ciType* type); + + void push_int() { + push(ciType::make(T_INT)); + } + void pop_int() { + assert(is_int(type_at_tos()), "must be integer"); + pop(); + } + void check_int(Cell c) { + assert(is_int(type_at(c)), "must be integer"); + } + void push_double() { + push(ciType::make(T_DOUBLE)); + push(double2_type()); + } + void pop_double() { + assert(type_at_tos() == double2_type(), "must be 2nd half"); + pop(); + assert(is_double(type_at_tos()), "must be double"); + pop(); + } + void push_float() { + push(ciType::make(T_FLOAT)); + } + void pop_float() { + assert(is_float(type_at_tos()), "must be float"); + pop(); + } + void push_long() { + push(ciType::make(T_LONG)); + push(long2_type()); + } + void pop_long() { + assert(type_at_tos() == long2_type(), "must be 2nd half"); + pop(); + assert(is_long(type_at_tos()), "must be long"); + pop(); + } + void push_object(ciKlass* klass) { + push(klass); + } + void pop_object() { + assert(is_reference(type_at_tos()), "must be reference type"); + pop(); + } + void pop_array() { + assert(type_at_tos() == null_type() || + type_at_tos()->is_array_klass(), "must be array type"); + pop(); + } + // pop_objArray and pop_typeArray narrow the tos to ciObjArrayKlass + // or ciTypeArrayKlass (resp.). In the rare case that an explicit + // null is popped from the stack, we return NULL. Caller beware. + ciObjArrayKlass* pop_objArray() { + ciType* array = pop_value(); + if (array == null_type()) return NULL; + assert(array->is_obj_array_klass(), "must be object array type"); + return array->as_obj_array_klass(); + } + ciTypeArrayKlass* pop_typeArray() { + ciType* array = pop_value(); + if (array == null_type()) return NULL; + assert(array->is_type_array_klass(), "must be prim array type"); + return array->as_type_array_klass(); + } + void push_null() { + push(null_type()); + } + void do_null_assert(ciKlass* unloaded_klass); + + // Helper convenience routines. + void do_aaload(ciBytecodeStream* str); + void do_checkcast(ciBytecodeStream* str); + void do_getfield(ciBytecodeStream* str); + void do_getstatic(ciBytecodeStream* str); + void do_invoke(ciBytecodeStream* str, bool has_receiver); + void do_jsr(ciBytecodeStream* str); + void do_ldc(ciBytecodeStream* str); + void do_multianewarray(ciBytecodeStream* str); + void do_new(ciBytecodeStream* str); + void do_newarray(ciBytecodeStream* str); + void do_putfield(ciBytecodeStream* str); + void do_putstatic(ciBytecodeStream* str); + void do_ret(ciBytecodeStream* str); + + void overwrite_local_double_long(int index) { + // Invalidate the previous local if it contains first half of + // a double or long value since it's seconf half is being overwritten. + int prev_index = index - 1; + if (prev_index >= 0 && + (is_double(type_at(local(prev_index))) || + is_long(type_at(local(prev_index))))) { + set_type_at(local(prev_index), bottom_type()); + } + } + + void load_local_object(int index) { + ciType* type = type_at(local(index)); + assert(is_reference(type), "must be reference type"); + push(type); + } + void store_local_object(int index) { + ciType* type = pop_value(); + assert(is_reference(type) || type->is_return_address(), + "must be reference type or return address"); + overwrite_local_double_long(index); + set_type_at(local(index), type); + } + + void load_local_double(int index) { + ciType* type = type_at(local(index)); + ciType* type2 = type_at(local(index+1)); + assert(is_double(type), "must be double type"); + assert(type2 == double2_type(), "must be 2nd half"); + push(type); + push(double2_type()); + } + void store_local_double(int index) { + ciType* type2 = pop_value(); + ciType* type = pop_value(); + assert(is_double(type), "must be double"); + assert(type2 == double2_type(), "must be 2nd half"); + overwrite_local_double_long(index); + set_type_at(local(index), type); + set_type_at(local(index+1), type2); + } + + void load_local_float(int index) { + ciType* type = type_at(local(index)); + assert(is_float(type), "must be float type"); + push(type); + } + void store_local_float(int index) { + ciType* type = pop_value(); + assert(is_float(type), "must be float type"); + overwrite_local_double_long(index); + set_type_at(local(index), type); + } + + void load_local_int(int index) { + ciType* type = type_at(local(index)); + assert(is_int(type), "must be int type"); + push(type); + } + void store_local_int(int index) { + ciType* type = pop_value(); + assert(is_int(type), "must be int type"); + overwrite_local_double_long(index); + set_type_at(local(index), type); + } + + void load_local_long(int index) { + ciType* type = type_at(local(index)); + ciType* type2 = type_at(local(index+1)); + assert(is_long(type), "must be long type"); + assert(type2 == long2_type(), "must be 2nd half"); + push(type); + push(long2_type()); + } + void store_local_long(int index) { + ciType* type2 = pop_value(); + ciType* type = pop_value(); + assert(is_long(type), "must be long"); + assert(type2 == long2_type(), "must be 2nd half"); + overwrite_local_double_long(index); + set_type_at(local(index), type); + set_type_at(local(index+1), type2); + } + + // Stop interpretation of this path with a trap. + void trap(ciBytecodeStream* str, ciKlass* klass, int index); + + public: + StateVector(ciTypeFlow* outer); + + // Copy our value into some other StateVector + void copy_into(StateVector* copy) const; + + // Meets this StateVector with another, destructively modifying this + // one. Returns true if any modification takes place. + bool meet(const StateVector* incoming); + + // Ditto, except that the incoming state is coming from an exception. + bool meet_exception(ciInstanceKlass* exc, const StateVector* incoming); + + // Apply the effect of one bytecode to this StateVector + bool apply_one_bytecode(ciBytecodeStream* stream); + + // What is the bci of the trap? + int trap_bci() { return _trap_bci; } + + // What is the index associated with the trap? + int trap_index() { return _trap_index; } + + void print_cell_on(outputStream* st, Cell c) const PRODUCT_RETURN; + void print_on(outputStream* st) const PRODUCT_RETURN; + }; + + // Parameter for "find_block" calls: + // Describes the difference between a public and private copy. + enum CreateOption { + create_public_copy, + create_private_copy, + no_create + }; + + // A basic block + class Block : public ResourceObj { + private: + ciBlock* _ciblock; + GrowableArray* _exceptions; + GrowableArray* _exc_klasses; + GrowableArray* _successors; + StateVector* _state; + JsrSet* _jsrs; + + int _trap_bci; + int _trap_index; + + // A reasonable approximation to pre-order, provided.to the client. + int _pre_order; + + // Has this block been cloned for some special purpose? + bool _private_copy; + + // A pointer used for our internal work list + Block* _next; + bool _on_work_list; + + ciBlock* ciblock() const { return _ciblock; } + StateVector* state() const { return _state; } + + // Compute the exceptional successors and types for this Block. + void compute_exceptions(); + + public: + // constructors + Block(ciTypeFlow* outer, ciBlock* ciblk, JsrSet* jsrs); + + void set_trap(int trap_bci, int trap_index) { + _trap_bci = trap_bci; + _trap_index = trap_index; + assert(has_trap(), ""); + } + bool has_trap() const { return _trap_bci != -1; } + int trap_bci() const { assert(has_trap(), ""); return _trap_bci; } + int trap_index() const { assert(has_trap(), ""); return _trap_index; } + + // accessors + ciTypeFlow* outer() const { return state()->outer(); } + int start() const { return _ciblock->start_bci(); } + int limit() const { return _ciblock->limit_bci(); } + int control() const { return _ciblock->control_bci(); } + + bool is_private_copy() const { return _private_copy; } + void set_private_copy(bool z); + int private_copy_count() const { return outer()->private_copy_count(ciblock()->index(), _jsrs); } + + // access to entry state + int stack_size() const { return _state->stack_size(); } + int monitor_count() const { return _state->monitor_count(); } + ciType* local_type_at(int i) const { return _state->local_type_at(i); } + ciType* stack_type_at(int i) const { return _state->stack_type_at(i); } + + // Get the successors for this Block. + GrowableArray* successors(ciBytecodeStream* str, + StateVector* state, + JsrSet* jsrs); + GrowableArray* successors() { + assert(_successors != NULL, "must be filled in"); + return _successors; + } + + // Helper function for "successors" when making private copies of + // loop heads for C2. + Block * clone_loop_head(ciTypeFlow* analyzer, + int branch_bci, + Block* target, + JsrSet* jsrs); + + // Get the exceptional successors for this Block. + GrowableArray* exceptions() { + if (_exceptions == NULL) { + compute_exceptions(); + } + return _exceptions; + } + + // Get the exception klasses corresponding to the + // exceptional successors for this Block. + GrowableArray* exc_klasses() { + if (_exc_klasses == NULL) { + compute_exceptions(); + } + return _exc_klasses; + } + + // Is this Block compatible with a given JsrSet? + bool is_compatible_with(JsrSet* other) { + return _jsrs->is_compatible_with(other); + } + + // Copy the value of our state vector into another. + void copy_state_into(StateVector* copy) const { + _state->copy_into(copy); + } + + // Copy the value of our JsrSet into another + void copy_jsrs_into(JsrSet* copy) const { + _jsrs->copy_into(copy); + } + + // Meets the start state of this block with another state, destructively + // modifying this one. Returns true if any modification takes place. + bool meet(const StateVector* incoming) { + return state()->meet(incoming); + } + + // Ditto, except that the incoming state is coming from an + // exception path. This means the stack is replaced by the + // appropriate exception type. + bool meet_exception(ciInstanceKlass* exc, const StateVector* incoming) { + return state()->meet_exception(exc, incoming); + } + + // Work list manipulation + void set_next(Block* block) { _next = block; } + Block* next() const { return _next; } + + void set_on_work_list(bool c) { _on_work_list = c; } + bool is_on_work_list() const { return _on_work_list; } + + bool has_pre_order() const { return _pre_order >= 0; } + void set_pre_order(int po) { assert(!has_pre_order() && po >= 0, ""); _pre_order = po; } + int pre_order() const { assert(has_pre_order(), ""); return _pre_order; } + bool is_start() const { return _pre_order == outer()->start_block_num(); } + + // A ranking used in determining order within the work list. + bool is_simpler_than(Block* other); + + void print_value_on(outputStream* st) const PRODUCT_RETURN; + void print_on(outputStream* st) const PRODUCT_RETURN; + }; + + // Standard indexes of successors, for various bytecodes. + enum { + FALL_THROUGH = 0, // normal control + IF_NOT_TAKEN = 0, // the not-taken branch of an if (i.e., fall-through) + IF_TAKEN = 1, // the taken branch of an if + GOTO_TARGET = 0, // unique successor for goto, jsr, or ret + SWITCH_DEFAULT = 0, // default branch of a switch + SWITCH_CASES = 1 // first index for any non-default switch branches + // Unlike in other blocks, the successors of a switch are listed uniquely. + }; + +private: + // A mapping from pre_order to Blocks. This array is created + // only at the end of the flow. + Block** _block_map; + + // For each ciBlock index, a list of Blocks which share this ciBlock. + GrowableArray** _idx_to_blocklist; + // count of ciBlocks + int _ciblock_count; + + // Tells if a given instruction is able to generate an exception edge. + bool can_trap(ciBytecodeStream& str); + +public: + // Return the block beginning at bci which has a JsrSet compatible + // with jsrs. + Block* block_at(int bci, JsrSet* set, CreateOption option = create_public_copy); + + // block factory + Block* get_block_for(int ciBlockIndex, JsrSet* jsrs, CreateOption option = create_public_copy); + + // How many of the blocks have the private_copy bit set? + int private_copy_count(int ciBlockIndex, JsrSet* jsrs) const; + + // Return an existing block containing bci which has a JsrSet compatible + // with jsrs, or NULL if there is none. + Block* existing_block_at(int bci, JsrSet* set) { return block_at(bci, set, no_create); } + + // Tell whether the flow analysis has encountered an error of some sort. + bool failing() { return env()->failing() || _failure_reason != NULL; } + + // Reason this compilation is failing, such as "too many basic blocks". + const char* failure_reason() { return _failure_reason; } + + // Note a failure. + void record_failure(const char* reason); + + // Return the block of a given pre-order number. + int have_block_count() const { return _block_map != NULL; } + int block_count() const { assert(have_block_count(), ""); + return _next_pre_order; } + Block* pre_order_at(int po) const { assert(0 <= po && po < block_count(), "out of bounds"); + return _block_map[po]; } + Block* start_block() const { return pre_order_at(start_block_num()); } + int start_block_num() const { return 0; } + +private: + // A work list used during flow analysis. + Block* _work_list; + + // Next Block::_pre_order. After mapping, doubles as block_count. + int _next_pre_order; + + // Are there more blocks on the work list? + bool work_list_empty() { return _work_list == NULL; } + + // Get the next basic block from our work list. + Block* work_list_next(); + + // Add a basic block to our work list. + void add_to_work_list(Block* block); + + // State used for make_jsr_record + int _jsr_count; + GrowableArray* _jsr_records; + +public: + // Make a JsrRecord for a given (entry, return) pair, if such a record + // does not already exist. + JsrRecord* make_jsr_record(int entry_address, int return_address); + +private: + // Get the initial state for start_bci: + const StateVector* get_start_state(); + + // Merge the current state into all exceptional successors at the + // current point in the code. + void flow_exceptions(GrowableArray* exceptions, + GrowableArray* exc_klasses, + StateVector* state); + + // Merge the current state into all successors at the current point + // in the code. + void flow_successors(GrowableArray* successors, + StateVector* state); + + // Interpret the effects of the bytecodes on the incoming state + // vector of a basic block. Push the changed state to succeeding + // basic blocks. + void flow_block(Block* block, + StateVector* scratch_state, + JsrSet* scratch_jsrs); + + // Perform the type flow analysis, creating and cloning Blocks as + // necessary. + void flow_types(); + + // Create the block map, which indexes blocks in pre_order. + void map_blocks(); + +public: + // Perform type inference flow analysis. + void do_flow(); + + void print_on(outputStream* st) const PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/ci/ciUtilities.cpp b/hotspot/src/share/vm/ci/ciUtilities.cpp new file mode 100644 index 00000000000..e5339850ab1 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciUtilities.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 1999-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciUtilities.cpp.incl" + +// ciUtilities +// +// Miscellaneous internal compiler interface routines. + +// ------------------------------------------------------------------ +// basictype_to_str +const char* basictype_to_str(BasicType t) { + const char* str = type2name(t); + if (str == NULL) return "illegal"; + return str; +} + +// ------------------------------------------------------------------ +// basictype_to_char +const char basictype_to_char(BasicType t) { + char c = type2char(t); + return c ? c : 'X'; +} diff --git a/hotspot/src/share/vm/ci/ciUtilities.hpp b/hotspot/src/share/vm/ci/ciUtilities.hpp new file mode 100644 index 00000000000..163e8089b65 --- /dev/null +++ b/hotspot/src/share/vm/ci/ciUtilities.hpp @@ -0,0 +1,106 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The following routines and definitions are used internally in the +// compiler interface. + + +// Add a ci native entry wrapper? + +// Bring the compilation thread into the VM state. +#define VM_ENTRY_MARK \ + CompilerThread* thread=CompilerThread::current(); \ + ThreadInVMfromNative __tiv(thread); \ + ResetNoHandleMark rnhm; \ + HandleMarkCleaner __hm(thread); \ + Thread* THREAD = thread; \ + debug_only(VMNativeEntryWrapper __vew;) + + + +// Bring the compilation thread into the VM state. No handle mark. +#define VM_QUICK_ENTRY_MARK \ + CompilerThread* thread=CompilerThread::current(); \ + ThreadInVMfromNative __tiv(thread); \ +/* \ + * [TODO] The NoHandleMark line does nothing but declare a function prototype \ + * The NoHandkeMark constructor is NOT executed. If the ()'s are \ + * removed, causes the NoHandleMark assert to trigger. \ + * debug_only(NoHandleMark __hm();) \ + */ \ + Thread* THREAD = thread; \ + debug_only(VMNativeEntryWrapper __vew;) + + +#define EXCEPTION_CONTEXT \ + CompilerThread* thread=CompilerThread::current(); \ + Thread* THREAD = thread; + + +#define CURRENT_ENV \ + ciEnv::current() + +// where current thread is THREAD +#define CURRENT_THREAD_ENV \ + ciEnv::current(thread) + +#define IS_IN_VM \ + ciEnv::is_in_vm() + +#define ASSERT_IN_VM \ + assert(IS_IN_VM, "must be in vm state"); + +#define GUARDED_VM_ENTRY(action) \ + {if (IS_IN_VM) { action } else { VM_ENTRY_MARK; { action }}} + +// Redefine this later. +#define KILL_COMPILE_ON_FATAL_(result) \ + THREAD); \ + if (HAS_PENDING_EXCEPTION) { \ + if (PENDING_EXCEPTION->klass() == \ + SystemDictionary::threaddeath_klass()) { \ + /* Kill the compilation. */ \ + fatal("unhandled ci exception"); \ + return (result); \ + } \ + CLEAR_PENDING_EXCEPTION; \ + return (result); \ + } \ + (0 + +#define KILL_COMPILE_ON_ANY \ + THREAD); \ + if (HAS_PENDING_EXCEPTION) { \ + fatal("unhandled ci exception"); \ + CLEAR_PENDING_EXCEPTION; \ + } \ +(0 + + +inline const char* bool_to_str(bool b) { + return ((b) ? "true" : "false"); +} + +const char* basictype_to_str(BasicType t); +const char basictype_to_char(BasicType t); diff --git a/hotspot/src/share/vm/ci/compilerInterface.hpp b/hotspot/src/share/vm/ci/compilerInterface.hpp new file mode 100644 index 00000000000..f74b21de9b0 --- /dev/null +++ b/hotspot/src/share/vm/ci/compilerInterface.hpp @@ -0,0 +1,26 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This is a dummy file used for including the complete +// compiler interface. diff --git a/hotspot/src/share/vm/classfile/classFileError.cpp b/hotspot/src/share/vm/classfile/classFileError.cpp new file mode 100644 index 00000000000..fc011b60243 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classFileError.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_classFileError.cpp.incl" + +// Keep these in a separate file to prevent inlining + +void ClassFileParser::classfile_parse_error(const char* msg, TRAPS) { + ResourceMark rm(THREAD); + Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbolHandles::java_lang_ClassFormatError(), + msg, _class_name->as_C_string()); +} + +void ClassFileParser::classfile_parse_error(const char* msg, int index, TRAPS) { + ResourceMark rm(THREAD); + Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbolHandles::java_lang_ClassFormatError(), + msg, index, _class_name->as_C_string()); +} + +void ClassFileParser::classfile_parse_error(const char* msg, const char *name, TRAPS) { + ResourceMark rm(THREAD); + Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbolHandles::java_lang_ClassFormatError(), + msg, name, _class_name->as_C_string()); +} + +void ClassFileParser::classfile_parse_error(const char* msg, int index, const char *name, TRAPS) { + ResourceMark rm(THREAD); + Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbolHandles::java_lang_ClassFormatError(), + msg, index, name, _class_name->as_C_string()); +} + +void StackMapStream::stackmap_format_error(const char* msg, TRAPS) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_ClassFormatError(), + "StackMapTable format error: %s", msg + ); +} diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp new file mode 100644 index 00000000000..5b9cb524f21 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -0,0 +1,4014 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_classFileParser.cpp.incl" + +// We generally try to create the oops directly when parsing, rather than allocating +// temporary data structures and copying the bytes twice. A temporary area is only +// needed when parsing utf8 entries in the constant pool and when parsing line number +// tables. + +// We add assert in debug mode when class format is not checked. + +#define JAVA_CLASSFILE_MAGIC 0xCAFEBABE +#define JAVA_MIN_SUPPORTED_VERSION 45 +#define JAVA_MAX_SUPPORTED_VERSION 50 +#define JAVA_MAX_SUPPORTED_MINOR_VERSION 0 + +// Used for two backward compatibility reasons: +// - to check for new additions to the class file format in JDK1.5 +// - to check for bug fixes in the format checker in JDK1.5 +#define JAVA_1_5_VERSION 49 + +// Used for backward compatibility reasons: +// - to check for javac bug fixes that happened after 1.5 +#define JAVA_6_VERSION 50 + + +void ClassFileParser::parse_constant_pool_entries(constantPoolHandle cp, int length, TRAPS) { + // Use a local copy of ClassFileStream. It helps the C++ compiler to optimize + // this function (_current can be allocated in a register, with scalar + // replacement of aggregates). The _current pointer is copied back to + // stream() when this function returns. DON'T call another method within + // this method that uses stream(). + ClassFileStream* cfs0 = stream(); + ClassFileStream cfs1 = *cfs0; + ClassFileStream* cfs = &cfs1; +#ifdef ASSERT + u1* old_current = cfs0->current(); +#endif + + // Used for batching symbol allocations. + const char* names[SymbolTable::symbol_alloc_batch_size]; + int lengths[SymbolTable::symbol_alloc_batch_size]; + int indices[SymbolTable::symbol_alloc_batch_size]; + unsigned int hashValues[SymbolTable::symbol_alloc_batch_size]; + int names_count = 0; + + // parsing Index 0 is unused + for (int index = 1; index < length; index++) { + // Each of the following case guarantees one more byte in the stream + // for the following tag or the access_flags following constant pool, + // so we don't need bounds-check for reading tag. + u1 tag = cfs->get_u1_fast(); + switch (tag) { + case JVM_CONSTANT_Class : + { + cfs->guarantee_more(3, CHECK); // name_index, tag/access_flags + u2 name_index = cfs->get_u2_fast(); + cp->klass_index_at_put(index, name_index); + } + break; + case JVM_CONSTANT_Fieldref : + { + cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags + u2 class_index = cfs->get_u2_fast(); + u2 name_and_type_index = cfs->get_u2_fast(); + cp->field_at_put(index, class_index, name_and_type_index); + } + break; + case JVM_CONSTANT_Methodref : + { + cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags + u2 class_index = cfs->get_u2_fast(); + u2 name_and_type_index = cfs->get_u2_fast(); + cp->method_at_put(index, class_index, name_and_type_index); + } + break; + case JVM_CONSTANT_InterfaceMethodref : + { + cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags + u2 class_index = cfs->get_u2_fast(); + u2 name_and_type_index = cfs->get_u2_fast(); + cp->interface_method_at_put(index, class_index, name_and_type_index); + } + break; + case JVM_CONSTANT_String : + { + cfs->guarantee_more(3, CHECK); // string_index, tag/access_flags + u2 string_index = cfs->get_u2_fast(); + cp->string_index_at_put(index, string_index); + } + break; + case JVM_CONSTANT_Integer : + { + cfs->guarantee_more(5, CHECK); // bytes, tag/access_flags + u4 bytes = cfs->get_u4_fast(); + cp->int_at_put(index, (jint) bytes); + } + break; + case JVM_CONSTANT_Float : + { + cfs->guarantee_more(5, CHECK); // bytes, tag/access_flags + u4 bytes = cfs->get_u4_fast(); + cp->float_at_put(index, *(jfloat*)&bytes); + } + break; + case JVM_CONSTANT_Long : + // A mangled type might cause you to overrun allocated memory + guarantee_property(index+1 < length, + "Invalid constant pool entry %u in class file %s", + index, CHECK); + { + cfs->guarantee_more(9, CHECK); // bytes, tag/access_flags + u8 bytes = cfs->get_u8_fast(); + cp->long_at_put(index, bytes); + } + index++; // Skip entry following eigth-byte constant, see JVM book p. 98 + break; + case JVM_CONSTANT_Double : + // A mangled type might cause you to overrun allocated memory + guarantee_property(index+1 < length, + "Invalid constant pool entry %u in class file %s", + index, CHECK); + { + cfs->guarantee_more(9, CHECK); // bytes, tag/access_flags + u8 bytes = cfs->get_u8_fast(); + cp->double_at_put(index, *(jdouble*)&bytes); + } + index++; // Skip entry following eigth-byte constant, see JVM book p. 98 + break; + case JVM_CONSTANT_NameAndType : + { + cfs->guarantee_more(5, CHECK); // name_index, signature_index, tag/access_flags + u2 name_index = cfs->get_u2_fast(); + u2 signature_index = cfs->get_u2_fast(); + cp->name_and_type_at_put(index, name_index, signature_index); + } + break; + case JVM_CONSTANT_Utf8 : + { + cfs->guarantee_more(2, CHECK); // utf8_length + u2 utf8_length = cfs->get_u2_fast(); + u1* utf8_buffer = cfs->get_u1_buffer(); + assert(utf8_buffer != NULL, "null utf8 buffer"); + // Got utf8 string, guarantee utf8_length+1 bytes, set stream position forward. + cfs->guarantee_more(utf8_length+1, CHECK); // utf8 string, tag/access_flags + cfs->skip_u1_fast(utf8_length); + // Before storing the symbol, make sure it's legal + if (_need_verify) { + verify_legal_utf8((unsigned char*)utf8_buffer, utf8_length, CHECK); + } + + unsigned int hash; + symbolOop result = SymbolTable::lookup_only((char*)utf8_buffer, utf8_length, hash); + if (result == NULL) { + names[names_count] = (char*)utf8_buffer; + lengths[names_count] = utf8_length; + indices[names_count] = index; + hashValues[names_count++] = hash; + if (names_count == SymbolTable::symbol_alloc_batch_size) { + oopFactory::new_symbols(cp, names_count, names, lengths, indices, hashValues, CHECK); + names_count = 0; + } + } else { + cp->symbol_at_put(index, result); + } + } + break; + default: + classfile_parse_error( + "Unknown constant tag %u in class file %s", tag, CHECK); + break; + } + } + + // Allocate the remaining symbols + if (names_count > 0) { + oopFactory::new_symbols(cp, names_count, names, lengths, indices, hashValues, CHECK); + } + + // Copy _current pointer of local copy back to stream(). +#ifdef ASSERT + assert(cfs0->current() == old_current, "non-exclusive use of stream()"); +#endif + cfs0->set_current(cfs1.current()); +} + +bool inline valid_cp_range(int index, int length) { return (index > 0 && index < length); } + +constantPoolHandle ClassFileParser::parse_constant_pool(TRAPS) { + ClassFileStream* cfs = stream(); + constantPoolHandle nullHandle; + + cfs->guarantee_more(3, CHECK_(nullHandle)); // length, first cp tag + u2 length = cfs->get_u2_fast(); + guarantee_property( + length >= 1, "Illegal constant pool size %u in class file %s", + length, CHECK_(nullHandle)); + constantPoolOop constant_pool = + oopFactory::new_constantPool(length, CHECK_(nullHandle)); + constantPoolHandle cp (THREAD, constant_pool); + + cp->set_partially_loaded(); // Enables heap verify to work on partial constantPoolOops + + // parsing constant pool entries + parse_constant_pool_entries(cp, length, CHECK_(nullHandle)); + + int index = 1; // declared outside of loops for portability + + // first verification pass - validate cross references and fixup class and string constants + for (index = 1; index < length; index++) { // Index 0 is unused + switch (cp->tag_at(index).value()) { + case JVM_CONSTANT_Class : + ShouldNotReachHere(); // Only JVM_CONSTANT_ClassIndex should be present + break; + case JVM_CONSTANT_Fieldref : + // fall through + case JVM_CONSTANT_Methodref : + // fall through + case JVM_CONSTANT_InterfaceMethodref : { + if (!_need_verify) break; + int klass_ref_index = cp->klass_ref_index_at(index); + int name_and_type_ref_index = cp->name_and_type_ref_index_at(index); + check_property(valid_cp_range(klass_ref_index, length) && + cp->tag_at(klass_ref_index).is_klass_reference(), + "Invalid constant pool index %u in class file %s", + klass_ref_index, + CHECK_(nullHandle)); + check_property(valid_cp_range(name_and_type_ref_index, length) && + cp->tag_at(name_and_type_ref_index).is_name_and_type(), + "Invalid constant pool index %u in class file %s", + name_and_type_ref_index, + CHECK_(nullHandle)); + break; + } + case JVM_CONSTANT_String : + ShouldNotReachHere(); // Only JVM_CONSTANT_StringIndex should be present + break; + case JVM_CONSTANT_Integer : + break; + case JVM_CONSTANT_Float : + break; + case JVM_CONSTANT_Long : + case JVM_CONSTANT_Double : + index++; + check_property( + (index < length && cp->tag_at(index).is_invalid()), + "Improper constant pool long/double index %u in class file %s", + index, CHECK_(nullHandle)); + break; + case JVM_CONSTANT_NameAndType : { + if (!_need_verify) break; + int name_ref_index = cp->name_ref_index_at(index); + int signature_ref_index = cp->signature_ref_index_at(index); + check_property( + valid_cp_range(name_ref_index, length) && + cp->tag_at(name_ref_index).is_utf8(), + "Invalid constant pool index %u in class file %s", + name_ref_index, CHECK_(nullHandle)); + check_property( + valid_cp_range(signature_ref_index, length) && + cp->tag_at(signature_ref_index).is_utf8(), + "Invalid constant pool index %u in class file %s", + signature_ref_index, CHECK_(nullHandle)); + break; + } + case JVM_CONSTANT_Utf8 : + break; + case JVM_CONSTANT_UnresolvedClass : // fall-through + case JVM_CONSTANT_UnresolvedClassInError: + ShouldNotReachHere(); // Only JVM_CONSTANT_ClassIndex should be present + break; + case JVM_CONSTANT_ClassIndex : + { + int class_index = cp->klass_index_at(index); + check_property( + valid_cp_range(class_index, length) && + cp->tag_at(class_index).is_utf8(), + "Invalid constant pool index %u in class file %s", + class_index, CHECK_(nullHandle)); + cp->unresolved_klass_at_put(index, cp->symbol_at(class_index)); + } + break; + case JVM_CONSTANT_UnresolvedString : + ShouldNotReachHere(); // Only JVM_CONSTANT_StringIndex should be present + break; + case JVM_CONSTANT_StringIndex : + { + int string_index = cp->string_index_at(index); + check_property( + valid_cp_range(string_index, length) && + cp->tag_at(string_index).is_utf8(), + "Invalid constant pool index %u in class file %s", + string_index, CHECK_(nullHandle)); + symbolOop sym = cp->symbol_at(string_index); + cp->unresolved_string_at_put(index, sym); + } + break; + default: + fatal1("bad constant pool tag value %u", cp->tag_at(index).value()); + ShouldNotReachHere(); + break; + } // end of switch + } // end of for + + if (!_need_verify) { + return cp; + } + + // second verification pass - checks the strings are of the right format. + for (index = 1; index < length; index++) { + jbyte tag = cp->tag_at(index).value(); + switch (tag) { + case JVM_CONSTANT_UnresolvedClass: { + symbolHandle class_name(THREAD, cp->unresolved_klass_at(index)); + verify_legal_class_name(class_name, CHECK_(nullHandle)); + break; + } + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: { + int name_and_type_ref_index = cp->name_and_type_ref_index_at(index); + // already verified to be utf8 + int name_ref_index = cp->name_ref_index_at(name_and_type_ref_index); + // already verified to be utf8 + int signature_ref_index = cp->signature_ref_index_at(name_and_type_ref_index); + symbolHandle name(THREAD, cp->symbol_at(name_ref_index)); + symbolHandle signature(THREAD, cp->symbol_at(signature_ref_index)); + if (tag == JVM_CONSTANT_Fieldref) { + verify_legal_field_name(name, CHECK_(nullHandle)); + verify_legal_field_signature(name, signature, CHECK_(nullHandle)); + } else { + verify_legal_method_name(name, CHECK_(nullHandle)); + verify_legal_method_signature(name, signature, CHECK_(nullHandle)); + if (tag == JVM_CONSTANT_Methodref) { + // 4509014: If a class method name begins with '<', it must be "". + assert(!name.is_null(), "method name in constant pool is null"); + unsigned int name_len = name->utf8_length(); + assert(name_len > 0, "bad method name"); // already verified as legal name + if (name->byte_at(0) == '<') { + if (name() != vmSymbols::object_initializer_name()) { + classfile_parse_error( + "Bad method name at constant pool index %u in class file %s", + name_ref_index, CHECK_(nullHandle)); + } + } + } + } + break; + } + } // end of switch + } // end of for + + return cp; +} + + +class NameSigHash: public ResourceObj { + public: + symbolOop _name; // name + symbolOop _sig; // signature + NameSigHash* _next; // Next entry in hash table +}; + + +#define HASH_ROW_SIZE 256 + +unsigned int hash(symbolOop name, symbolOop sig) { + unsigned int raw_hash = 0; + raw_hash += ((unsigned int)(uintptr_t)name) >> (LogHeapWordSize + 2); + raw_hash += ((unsigned int)(uintptr_t)sig) >> LogHeapWordSize; + + return (raw_hash + (unsigned int)(uintptr_t)name) % HASH_ROW_SIZE; +} + + +void initialize_hashtable(NameSigHash** table) { + memset((void*)table, 0, sizeof(NameSigHash*) * HASH_ROW_SIZE); +} + +// Return false if the name/sig combination is found in table. +// Return true if no duplicate is found. And name/sig is added as a new entry in table. +// The old format checker uses heap sort to find duplicates. +// NOTE: caller should guarantee that GC doesn't happen during the life cycle +// of table since we don't expect symbolOop's to move. +bool put_after_lookup(symbolOop name, symbolOop sig, NameSigHash** table) { + assert(name != NULL, "name in constant pool is NULL"); + + // First lookup for duplicates + int index = hash(name, sig); + NameSigHash* entry = table[index]; + while (entry != NULL) { + if (entry->_name == name && entry->_sig == sig) { + return false; + } + entry = entry->_next; + } + + // No duplicate is found, allocate a new entry and fill it. + entry = new NameSigHash(); + entry->_name = name; + entry->_sig = sig; + + // Insert into hash table + entry->_next = table[index]; + table[index] = entry; + + return true; +} + + +objArrayHandle ClassFileParser::parse_interfaces(constantPoolHandle cp, + int length, + Handle class_loader, + Handle protection_domain, + PerfTraceTime* vmtimer, + symbolHandle class_name, + TRAPS) { + ClassFileStream* cfs = stream(); + assert(length > 0, "only called for length>0"); + objArrayHandle nullHandle; + objArrayOop interface_oop = oopFactory::new_system_objArray(length, CHECK_(nullHandle)); + objArrayHandle interfaces (THREAD, interface_oop); + + int index; + for (index = 0; index < length; index++) { + u2 interface_index = cfs->get_u2(CHECK_(nullHandle)); + check_property( + valid_cp_range(interface_index, cp->length()) && + cp->tag_at(interface_index).is_unresolved_klass(), + "Interface name has bad constant pool index %u in class file %s", + interface_index, CHECK_(nullHandle)); + symbolHandle unresolved_klass (THREAD, cp->klass_name_at(interface_index)); + + // Don't need to check legal name because it's checked when parsing constant pool. + // But need to make sure it's not an array type. + guarantee_property(unresolved_klass->byte_at(0) != JVM_SIGNATURE_ARRAY, + "Bad interface name in class file %s", CHECK_(nullHandle)); + + vmtimer->suspend(); // do not count recursive loading twice + // Call resolve_super so classcircularity is checked + klassOop k = SystemDictionary::resolve_super_or_fail(class_name, + unresolved_klass, class_loader, protection_domain, + false, CHECK_(nullHandle)); + KlassHandle interf (THREAD, k); + vmtimer->resume(); + + if (!Klass::cast(interf())->is_interface()) { + THROW_MSG_(vmSymbols::java_lang_IncompatibleClassChangeError(), "Implementing class", nullHandle); + } + interfaces->obj_at_put(index, interf()); + } + + if (!_need_verify || length <= 1) { + return interfaces; + } + + // Check if there's any duplicates in interfaces + ResourceMark rm(THREAD); + NameSigHash** interface_names = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, NameSigHash*, HASH_ROW_SIZE); + initialize_hashtable(interface_names); + bool dup = false; + { + debug_only(No_Safepoint_Verifier nsv;) + for (index = 0; index < length; index++) { + klassOop k = (klassOop)interfaces->obj_at(index); + symbolOop name = instanceKlass::cast(k)->name(); + // If no duplicates, add (name, NULL) in hashtable interface_names. + if (!put_after_lookup(name, NULL, interface_names)) { + dup = true; + break; + } + } + } + if (dup) { + classfile_parse_error("Duplicate interface name in class file %s", + CHECK_(nullHandle)); + } + + return interfaces; +} + + +void ClassFileParser::verify_constantvalue(int constantvalue_index, int signature_index, constantPoolHandle cp, TRAPS) { + // Make sure the constant pool entry is of a type appropriate to this field + guarantee_property( + (constantvalue_index > 0 && + constantvalue_index < cp->length()), + "Bad initial value index %u in ConstantValue attribute in class file %s", + constantvalue_index, CHECK); + constantTag value_type = cp->tag_at(constantvalue_index); + switch ( cp->basic_type_for_signature_at(signature_index) ) { + case T_LONG: + guarantee_property(value_type.is_long(), "Inconsistent constant value type in class file %s", CHECK); + break; + case T_FLOAT: + guarantee_property(value_type.is_float(), "Inconsistent constant value type in class file %s", CHECK); + break; + case T_DOUBLE: + guarantee_property(value_type.is_double(), "Inconsistent constant value type in class file %s", CHECK); + break; + case T_BYTE: case T_CHAR: case T_SHORT: case T_BOOLEAN: case T_INT: + guarantee_property(value_type.is_int(), "Inconsistent constant value type in class file %s", CHECK); + break; + case T_OBJECT: + guarantee_property((cp->symbol_at(signature_index)->equals("Ljava/lang/String;", 18) + && (value_type.is_string() || value_type.is_unresolved_string())), + "Bad string initial value in class file %s", CHECK); + break; + default: + classfile_parse_error( + "Unable to set initial value %u in class file %s", + constantvalue_index, CHECK); + } +} + + +// Parse attributes for a field. +void ClassFileParser::parse_field_attributes(constantPoolHandle cp, + u2 attributes_count, + bool is_static, u2 signature_index, + u2* constantvalue_index_addr, + bool* is_synthetic_addr, + u2* generic_signature_index_addr, + typeArrayHandle* field_annotations, + TRAPS) { + ClassFileStream* cfs = stream(); + assert(attributes_count > 0, "length should be greater than 0"); + u2 constantvalue_index = 0; + u2 generic_signature_index = 0; + bool is_synthetic = false; + u1* runtime_visible_annotations = NULL; + int runtime_visible_annotations_length = 0; + u1* runtime_invisible_annotations = NULL; + int runtime_invisible_annotations_length = 0; + while (attributes_count--) { + cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length + u2 attribute_name_index = cfs->get_u2_fast(); + u4 attribute_length = cfs->get_u4_fast(); + check_property(valid_cp_range(attribute_name_index, cp->length()) && + cp->tag_at(attribute_name_index).is_utf8(), + "Invalid field attribute index %u in class file %s", + attribute_name_index, + CHECK); + symbolOop attribute_name = cp->symbol_at(attribute_name_index); + if (is_static && attribute_name == vmSymbols::tag_constant_value()) { + // ignore if non-static + if (constantvalue_index != 0) { + classfile_parse_error("Duplicate ConstantValue attribute in class file %s", CHECK); + } + check_property( + attribute_length == 2, + "Invalid ConstantValue field attribute length %u in class file %s", + attribute_length, CHECK); + constantvalue_index = cfs->get_u2(CHECK); + if (_need_verify) { + verify_constantvalue(constantvalue_index, signature_index, cp, CHECK); + } + } else if (attribute_name == vmSymbols::tag_synthetic()) { + if (attribute_length != 0) { + classfile_parse_error( + "Invalid Synthetic field attribute length %u in class file %s", + attribute_length, CHECK); + } + is_synthetic = true; + } else if (attribute_name == vmSymbols::tag_deprecated()) { // 4276120 + if (attribute_length != 0) { + classfile_parse_error( + "Invalid Deprecated field attribute length %u in class file %s", + attribute_length, CHECK); + } + } else if (_major_version >= JAVA_1_5_VERSION) { + if (attribute_name == vmSymbols::tag_signature()) { + if (attribute_length != 2) { + classfile_parse_error( + "Wrong size %u for field's Signature attribute in class file %s", + attribute_length, CHECK); + } + generic_signature_index = cfs->get_u2(CHECK); + } else if (attribute_name == vmSymbols::tag_runtime_visible_annotations()) { + runtime_visible_annotations_length = attribute_length; + runtime_visible_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_annotations != NULL, "null visible annotations"); + cfs->skip_u1(runtime_visible_annotations_length, CHECK); + } else if (PreserveAllAnnotations && attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { + runtime_invisible_annotations_length = attribute_length; + runtime_invisible_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_annotations != NULL, "null invisible annotations"); + cfs->skip_u1(runtime_invisible_annotations_length, CHECK); + } else { + cfs->skip_u1(attribute_length, CHECK); // Skip unknown attributes + } + } else { + cfs->skip_u1(attribute_length, CHECK); // Skip unknown attributes + } + } + + *constantvalue_index_addr = constantvalue_index; + *is_synthetic_addr = is_synthetic; + *generic_signature_index_addr = generic_signature_index; + *field_annotations = assemble_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, + runtime_invisible_annotations, + runtime_invisible_annotations_length, + CHECK); + return; +} + + +// Field allocation types. Used for computing field offsets. + +enum FieldAllocationType { + STATIC_OOP, // Oops + STATIC_BYTE, // Boolean, Byte, char + STATIC_SHORT, // shorts + STATIC_WORD, // ints + STATIC_DOUBLE, // long or double + STATIC_ALIGNED_DOUBLE,// aligned long or double + NONSTATIC_OOP, + NONSTATIC_BYTE, + NONSTATIC_SHORT, + NONSTATIC_WORD, + NONSTATIC_DOUBLE, + NONSTATIC_ALIGNED_DOUBLE +}; + + +struct FieldAllocationCount { + int static_oop_count; + int static_byte_count; + int static_short_count; + int static_word_count; + int static_double_count; + int nonstatic_oop_count; + int nonstatic_byte_count; + int nonstatic_short_count; + int nonstatic_word_count; + int nonstatic_double_count; +}; + +typeArrayHandle ClassFileParser::parse_fields(constantPoolHandle cp, bool is_interface, + struct FieldAllocationCount *fac, + objArrayHandle* fields_annotations, TRAPS) { + ClassFileStream* cfs = stream(); + typeArrayHandle nullHandle; + cfs->guarantee_more(2, CHECK_(nullHandle)); // length + u2 length = cfs->get_u2_fast(); + // Tuples of shorts [access, name index, sig index, initial value index, byte offset, generic signature index] + typeArrayOop new_fields = oopFactory::new_permanent_shortArray(length*instanceKlass::next_offset, CHECK_(nullHandle)); + typeArrayHandle fields(THREAD, new_fields); + + int index = 0; + typeArrayHandle field_annotations; + for (int n = 0; n < length; n++) { + cfs->guarantee_more(8, CHECK_(nullHandle)); // access_flags, name_index, descriptor_index, attributes_count + + AccessFlags access_flags; + jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_FIELD_MODIFIERS; + verify_legal_field_modifiers(flags, is_interface, CHECK_(nullHandle)); + access_flags.set_flags(flags); + + u2 name_index = cfs->get_u2_fast(); + int cp_size = cp->length(); + check_property( + valid_cp_range(name_index, cp_size) && cp->tag_at(name_index).is_utf8(), + "Invalid constant pool index %u for field name in class file %s", + name_index, CHECK_(nullHandle)); + symbolHandle name(THREAD, cp->symbol_at(name_index)); + verify_legal_field_name(name, CHECK_(nullHandle)); + + u2 signature_index = cfs->get_u2_fast(); + check_property( + valid_cp_range(signature_index, cp_size) && + cp->tag_at(signature_index).is_utf8(), + "Invalid constant pool index %u for field signature in class file %s", + signature_index, CHECK_(nullHandle)); + symbolHandle sig(THREAD, cp->symbol_at(signature_index)); + verify_legal_field_signature(name, sig, CHECK_(nullHandle)); + + u2 constantvalue_index = 0; + bool is_synthetic = false; + u2 generic_signature_index = 0; + bool is_static = access_flags.is_static(); + + u2 attributes_count = cfs->get_u2_fast(); + if (attributes_count > 0) { + parse_field_attributes(cp, attributes_count, is_static, signature_index, + &constantvalue_index, &is_synthetic, + &generic_signature_index, &field_annotations, + CHECK_(nullHandle)); + if (field_annotations.not_null()) { + if (fields_annotations->is_null()) { + objArrayOop md = oopFactory::new_system_objArray(length, CHECK_(nullHandle)); + *fields_annotations = objArrayHandle(THREAD, md); + } + (*fields_annotations)->obj_at_put(n, field_annotations()); + } + if (is_synthetic) { + access_flags.set_is_synthetic(); + } + } + + fields->short_at_put(index++, access_flags.as_short()); + fields->short_at_put(index++, name_index); + fields->short_at_put(index++, signature_index); + fields->short_at_put(index++, constantvalue_index); + + // Remember how many oops we encountered and compute allocation type + BasicType type = cp->basic_type_for_signature_at(signature_index); + FieldAllocationType atype; + if ( is_static ) { + switch ( type ) { + case T_BOOLEAN: + case T_BYTE: + fac->static_byte_count++; + atype = STATIC_BYTE; + break; + case T_LONG: + case T_DOUBLE: + if (Universe::field_type_should_be_aligned(type)) { + atype = STATIC_ALIGNED_DOUBLE; + } else { + atype = STATIC_DOUBLE; + } + fac->static_double_count++; + break; + case T_CHAR: + case T_SHORT: + fac->static_short_count++; + atype = STATIC_SHORT; + break; + case T_FLOAT: + case T_INT: + fac->static_word_count++; + atype = STATIC_WORD; + break; + case T_ARRAY: + case T_OBJECT: + fac->static_oop_count++; + atype = STATIC_OOP; + break; + case T_ADDRESS: + case T_VOID: + default: + assert(0, "bad field type"); + } + } else { + switch ( type ) { + case T_BOOLEAN: + case T_BYTE: + fac->nonstatic_byte_count++; + atype = NONSTATIC_BYTE; + break; + case T_LONG: + case T_DOUBLE: + if (Universe::field_type_should_be_aligned(type)) { + atype = NONSTATIC_ALIGNED_DOUBLE; + } else { + atype = NONSTATIC_DOUBLE; + } + fac->nonstatic_double_count++; + break; + case T_CHAR: + case T_SHORT: + fac->nonstatic_short_count++; + atype = NONSTATIC_SHORT; + break; + case T_FLOAT: + case T_INT: + fac->nonstatic_word_count++; + atype = NONSTATIC_WORD; + break; + case T_ARRAY: + case T_OBJECT: + fac->nonstatic_oop_count++; + atype = NONSTATIC_OOP; + break; + case T_ADDRESS: + case T_VOID: + default: + assert(0, "bad field type"); + } + } + + // The correct offset is computed later (all oop fields will be located together) + // We temporarily store the allocation type in the offset field + fields->short_at_put(index++, atype); + fields->short_at_put(index++, 0); // Clear out high word of byte offset + fields->short_at_put(index++, generic_signature_index); + } + + if (_need_verify && length > 1) { + // Check duplicated fields + ResourceMark rm(THREAD); + NameSigHash** names_and_sigs = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, NameSigHash*, HASH_ROW_SIZE); + initialize_hashtable(names_and_sigs); + bool dup = false; + { + debug_only(No_Safepoint_Verifier nsv;) + for (int i = 0; i < length*instanceKlass::next_offset; i += instanceKlass::next_offset) { + int name_index = fields->ushort_at(i + instanceKlass::name_index_offset); + symbolOop name = cp->symbol_at(name_index); + int sig_index = fields->ushort_at(i + instanceKlass::signature_index_offset); + symbolOop sig = cp->symbol_at(sig_index); + // If no duplicates, add name/signature in hashtable names_and_sigs. + if (!put_after_lookup(name, sig, names_and_sigs)) { + dup = true; + break; + } + } + } + if (dup) { + classfile_parse_error("Duplicate field name&signature in class file %s", + CHECK_(nullHandle)); + } + } + + return fields; +} + + +static void copy_u2_with_conversion(u2* dest, u2* src, int length) { + while (length-- > 0) { + *dest++ = Bytes::get_Java_u2((u1*) (src++)); + } +} + + +typeArrayHandle ClassFileParser::parse_exception_table(u4 code_length, + u4 exception_table_length, + constantPoolHandle cp, + TRAPS) { + ClassFileStream* cfs = stream(); + typeArrayHandle nullHandle; + + // 4-tuples of ints [start_pc, end_pc, handler_pc, catch_type index] + typeArrayOop eh = oopFactory::new_permanent_intArray(exception_table_length*4, CHECK_(nullHandle)); + typeArrayHandle exception_handlers = typeArrayHandle(THREAD, eh); + + int index = 0; + cfs->guarantee_more(8 * exception_table_length, CHECK_(nullHandle)); // start_pc, end_pc, handler_pc, catch_type_index + for (unsigned int i = 0; i < exception_table_length; i++) { + u2 start_pc = cfs->get_u2_fast(); + u2 end_pc = cfs->get_u2_fast(); + u2 handler_pc = cfs->get_u2_fast(); + u2 catch_type_index = cfs->get_u2_fast(); + // Will check legal target after parsing code array in verifier. + if (_need_verify) { + guarantee_property((start_pc < end_pc) && (end_pc <= code_length), + "Illegal exception table range in class file %s", CHECK_(nullHandle)); + guarantee_property(handler_pc < code_length, + "Illegal exception table handler in class file %s", CHECK_(nullHandle)); + if (catch_type_index != 0) { + guarantee_property(valid_cp_range(catch_type_index, cp->length()) && + (cp->tag_at(catch_type_index).is_klass() || + cp->tag_at(catch_type_index).is_unresolved_klass()), + "Catch type in exception table has bad constant type in class file %s", CHECK_(nullHandle)); + } + } + exception_handlers->int_at_put(index++, start_pc); + exception_handlers->int_at_put(index++, end_pc); + exception_handlers->int_at_put(index++, handler_pc); + exception_handlers->int_at_put(index++, catch_type_index); + } + return exception_handlers; +} + +void ClassFileParser::parse_linenumber_table( + u4 code_attribute_length, u4 code_length, + CompressedLineNumberWriteStream** write_stream, TRAPS) { + ClassFileStream* cfs = stream(); + unsigned int num_entries = cfs->get_u2(CHECK); + + // Each entry is a u2 start_pc, and a u2 line_number + unsigned int length_in_bytes = num_entries * (sizeof(u2) + sizeof(u2)); + + // Verify line number attribute and table length + check_property( + code_attribute_length == sizeof(u2) + length_in_bytes, + "LineNumberTable attribute has wrong length in class file %s", CHECK); + + cfs->guarantee_more(length_in_bytes, CHECK); + + if ((*write_stream) == NULL) { + if (length_in_bytes > fixed_buffer_size) { + (*write_stream) = new CompressedLineNumberWriteStream(length_in_bytes); + } else { + (*write_stream) = new CompressedLineNumberWriteStream( + linenumbertable_buffer, fixed_buffer_size); + } + } + + while (num_entries-- > 0) { + u2 bci = cfs->get_u2_fast(); // start_pc + u2 line = cfs->get_u2_fast(); // line_number + guarantee_property(bci < code_length, + "Invalid pc in LineNumberTable in class file %s", CHECK); + (*write_stream)->write_pair(bci, line); + } +} + + +// Class file LocalVariableTable elements. +class Classfile_LVT_Element VALUE_OBJ_CLASS_SPEC { + public: + u2 start_bci; + u2 length; + u2 name_cp_index; + u2 descriptor_cp_index; + u2 slot; +}; + + +class LVT_Hash: public CHeapObj { + public: + LocalVariableTableElement *_elem; // element + LVT_Hash* _next; // Next entry in hash table +}; + +unsigned int hash(LocalVariableTableElement *elem) { + unsigned int raw_hash = elem->start_bci; + + raw_hash = elem->length + raw_hash * 37; + raw_hash = elem->name_cp_index + raw_hash * 37; + raw_hash = elem->slot + raw_hash * 37; + + return raw_hash % HASH_ROW_SIZE; +} + +void initialize_hashtable(LVT_Hash** table) { + for (int i = 0; i < HASH_ROW_SIZE; i++) { + table[i] = NULL; + } +} + +void clear_hashtable(LVT_Hash** table) { + for (int i = 0; i < HASH_ROW_SIZE; i++) { + LVT_Hash* current = table[i]; + LVT_Hash* next; + while (current != NULL) { + next = current->_next; + current->_next = NULL; + delete(current); + current = next; + } + table[i] = NULL; + } +} + +LVT_Hash* LVT_lookup(LocalVariableTableElement *elem, int index, LVT_Hash** table) { + LVT_Hash* entry = table[index]; + + /* + * 3-tuple start_bci/length/slot has to be unique key, + * so the following comparison seems to be redundant: + * && elem->name_cp_index == entry->_elem->name_cp_index + */ + while (entry != NULL) { + if (elem->start_bci == entry->_elem->start_bci + && elem->length == entry->_elem->length + && elem->name_cp_index == entry->_elem->name_cp_index + && elem->slot == entry->_elem->slot + ) { + return entry; + } + entry = entry->_next; + } + return NULL; +} + +// Return false if the local variable is found in table. +// Return true if no duplicate is found. +// And local variable is added as a new entry in table. +bool LVT_put_after_lookup(LocalVariableTableElement *elem, LVT_Hash** table) { + // First lookup for duplicates + int index = hash(elem); + LVT_Hash* entry = LVT_lookup(elem, index, table); + + if (entry != NULL) { + return false; + } + // No duplicate is found, allocate a new entry and fill it. + if ((entry = new LVT_Hash()) == NULL) { + return false; + } + entry->_elem = elem; + + // Insert into hash table + entry->_next = table[index]; + table[index] = entry; + + return true; +} + +void copy_lvt_element(Classfile_LVT_Element *src, LocalVariableTableElement *lvt) { + lvt->start_bci = Bytes::get_Java_u2((u1*) &src->start_bci); + lvt->length = Bytes::get_Java_u2((u1*) &src->length); + lvt->name_cp_index = Bytes::get_Java_u2((u1*) &src->name_cp_index); + lvt->descriptor_cp_index = Bytes::get_Java_u2((u1*) &src->descriptor_cp_index); + lvt->signature_cp_index = 0; + lvt->slot = Bytes::get_Java_u2((u1*) &src->slot); +} + +// Function is used to parse both attributes: +// LocalVariableTable (LVT) and LocalVariableTypeTable (LVTT) +u2* ClassFileParser::parse_localvariable_table(u4 code_length, + u2 max_locals, + u4 code_attribute_length, + constantPoolHandle cp, + u2* localvariable_table_length, + bool isLVTT, + TRAPS) { + ClassFileStream* cfs = stream(); + const char * tbl_name = (isLVTT) ? "LocalVariableTypeTable" : "LocalVariableTable"; + *localvariable_table_length = cfs->get_u2(CHECK_NULL); + unsigned int size = (*localvariable_table_length) * sizeof(Classfile_LVT_Element) / sizeof(u2); + // Verify local variable table attribute has right length + if (_need_verify) { + guarantee_property(code_attribute_length == (sizeof(*localvariable_table_length) + size * sizeof(u2)), + "%s has wrong length in class file %s", tbl_name, CHECK_NULL); + } + u2* localvariable_table_start = cfs->get_u2_buffer(); + assert(localvariable_table_start != NULL, "null local variable table"); + if (!_need_verify) { + cfs->skip_u2_fast(size); + } else { + cfs->guarantee_more(size * 2, CHECK_NULL); + for(int i = 0; i < (*localvariable_table_length); i++) { + u2 start_pc = cfs->get_u2_fast(); + u2 length = cfs->get_u2_fast(); + u2 name_index = cfs->get_u2_fast(); + u2 descriptor_index = cfs->get_u2_fast(); + u2 index = cfs->get_u2_fast(); + // Assign to a u4 to avoid overflow + u4 end_pc = (u4)start_pc + (u4)length; + + if (start_pc >= code_length) { + classfile_parse_error( + "Invalid start_pc %u in %s in class file %s", + start_pc, tbl_name, CHECK_NULL); + } + if (end_pc > code_length) { + classfile_parse_error( + "Invalid length %u in %s in class file %s", + length, tbl_name, CHECK_NULL); + } + int cp_size = cp->length(); + guarantee_property( + valid_cp_range(name_index, cp_size) && + cp->tag_at(name_index).is_utf8(), + "Name index %u in %s has bad constant type in class file %s", + name_index, tbl_name, CHECK_NULL); + guarantee_property( + valid_cp_range(descriptor_index, cp_size) && + cp->tag_at(descriptor_index).is_utf8(), + "Signature index %u in %s has bad constant type in class file %s", + descriptor_index, tbl_name, CHECK_NULL); + + symbolHandle name(THREAD, cp->symbol_at(name_index)); + symbolHandle sig(THREAD, cp->symbol_at(descriptor_index)); + verify_legal_field_name(name, CHECK_NULL); + u2 extra_slot = 0; + if (!isLVTT) { + verify_legal_field_signature(name, sig, CHECK_NULL); + + // 4894874: check special cases for double and long local variables + if (sig() == vmSymbols::type_signature(T_DOUBLE) || + sig() == vmSymbols::type_signature(T_LONG)) { + extra_slot = 1; + } + } + guarantee_property((index + extra_slot) < max_locals, + "Invalid index %u in %s in class file %s", + index, tbl_name, CHECK_NULL); + } + } + return localvariable_table_start; +} + + +void ClassFileParser::parse_type_array(u2 array_length, u4 code_length, u4* u1_index, u4* u2_index, + u1* u1_array, u2* u2_array, constantPoolHandle cp, TRAPS) { + ClassFileStream* cfs = stream(); + u2 index = 0; // index in the array with long/double occupying two slots + u4 i1 = *u1_index; + u4 i2 = *u2_index + 1; + for(int i = 0; i < array_length; i++) { + u1 tag = u1_array[i1++] = cfs->get_u1(CHECK); + index++; + if (tag == ITEM_Long || tag == ITEM_Double) { + index++; + } else if (tag == ITEM_Object) { + u2 class_index = u2_array[i2++] = cfs->get_u2(CHECK); + guarantee_property(valid_cp_range(class_index, cp->length()) && + cp->tag_at(class_index).is_unresolved_klass(), + "Bad class index %u in StackMap in class file %s", + class_index, CHECK); + } else if (tag == ITEM_Uninitialized) { + u2 offset = u2_array[i2++] = cfs->get_u2(CHECK); + guarantee_property( + offset < code_length, + "Bad uninitialized type offset %u in StackMap in class file %s", + offset, CHECK); + } else { + guarantee_property( + tag <= (u1)ITEM_Uninitialized, + "Unknown variable type %u in StackMap in class file %s", + tag, CHECK); + } + } + u2_array[*u2_index] = index; + *u1_index = i1; + *u2_index = i2; +} + +typeArrayOop ClassFileParser::parse_stackmap_table( + u4 code_attribute_length, TRAPS) { + if (code_attribute_length == 0) + return NULL; + + ClassFileStream* cfs = stream(); + u1* stackmap_table_start = cfs->get_u1_buffer(); + assert(stackmap_table_start != NULL, "null stackmap table"); + + // check code_attribute_length first + stream()->skip_u1(code_attribute_length, CHECK_NULL); + + if (!_need_verify && !DumpSharedSpaces) { + return NULL; + } + + typeArrayOop stackmap_data = + oopFactory::new_permanent_byteArray(code_attribute_length, CHECK_NULL); + + stackmap_data->set_length(code_attribute_length); + memcpy((void*)stackmap_data->byte_at_addr(0), + (void*)stackmap_table_start, code_attribute_length); + return stackmap_data; +} + +u2* ClassFileParser::parse_checked_exceptions(u2* checked_exceptions_length, + u4 method_attribute_length, + constantPoolHandle cp, TRAPS) { + ClassFileStream* cfs = stream(); + cfs->guarantee_more(2, CHECK_NULL); // checked_exceptions_length + *checked_exceptions_length = cfs->get_u2_fast(); + unsigned int size = (*checked_exceptions_length) * sizeof(CheckedExceptionElement) / sizeof(u2); + u2* checked_exceptions_start = cfs->get_u2_buffer(); + assert(checked_exceptions_start != NULL, "null checked exceptions"); + if (!_need_verify) { + cfs->skip_u2_fast(size); + } else { + // Verify each value in the checked exception table + u2 checked_exception; + u2 len = *checked_exceptions_length; + cfs->guarantee_more(2 * len, CHECK_NULL); + for (int i = 0; i < len; i++) { + checked_exception = cfs->get_u2_fast(); + check_property( + valid_cp_range(checked_exception, cp->length()) && + cp->tag_at(checked_exception).is_klass_reference(), + "Exception name has bad type at constant pool %u in class file %s", + checked_exception, CHECK_NULL); + } + } + // check exceptions attribute length + if (_need_verify) { + guarantee_property(method_attribute_length == (sizeof(*checked_exceptions_length) + + sizeof(u2) * size), + "Exceptions attribute has wrong length in class file %s", CHECK_NULL); + } + return checked_exceptions_start; +} + + +#define MAX_ARGS_SIZE 255 +#define MAX_CODE_SIZE 65535 +#define INITIAL_MAX_LVT_NUMBER 256 + +// Note: the parse_method below is big and clunky because all parsing of the code and exceptions +// attribute is inlined. This is curbersome to avoid since we inline most of the parts in the +// methodOop to save footprint, so we only know the size of the resulting methodOop when the +// entire method attribute is parsed. +// +// The promoted_flags parameter is used to pass relevant access_flags +// from the method back up to the containing klass. These flag values +// are added to klass's access_flags. + +methodHandle ClassFileParser::parse_method(constantPoolHandle cp, bool is_interface, + AccessFlags *promoted_flags, + typeArrayHandle* method_annotations, + typeArrayHandle* method_parameter_annotations, + typeArrayHandle* method_default_annotations, + TRAPS) { + ClassFileStream* cfs = stream(); + methodHandle nullHandle; + ResourceMark rm(THREAD); + // Parse fixed parts + cfs->guarantee_more(8, CHECK_(nullHandle)); // access_flags, name_index, descriptor_index, attributes_count + + int flags = cfs->get_u2_fast(); + u2 name_index = cfs->get_u2_fast(); + int cp_size = cp->length(); + check_property( + valid_cp_range(name_index, cp_size) && + cp->tag_at(name_index).is_utf8(), + "Illegal constant pool index %u for method name in class file %s", + name_index, CHECK_(nullHandle)); + symbolHandle name(THREAD, cp->symbol_at(name_index)); + verify_legal_method_name(name, CHECK_(nullHandle)); + + u2 signature_index = cfs->get_u2_fast(); + guarantee_property( + valid_cp_range(signature_index, cp_size) && + cp->tag_at(signature_index).is_utf8(), + "Illegal constant pool index %u for method signature in class file %s", + signature_index, CHECK_(nullHandle)); + symbolHandle signature(THREAD, cp->symbol_at(signature_index)); + + AccessFlags access_flags; + if (name == vmSymbols::class_initializer_name()) { + // We ignore the access flags for a class initializer. (JVM Spec. p. 116) + flags = JVM_ACC_STATIC; + } else { + verify_legal_method_modifiers(flags, is_interface, name, CHECK_(nullHandle)); + } + + int args_size = -1; // only used when _need_verify is true + if (_need_verify) { + args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) + + verify_legal_method_signature(name, signature, CHECK_(nullHandle)); + if (args_size > MAX_ARGS_SIZE) { + classfile_parse_error("Too many arguments in method signature in class file %s", CHECK_(nullHandle)); + } + } + + access_flags.set_flags(flags & JVM_RECOGNIZED_METHOD_MODIFIERS); + + // Default values for code and exceptions attribute elements + u2 max_stack = 0; + u2 max_locals = 0; + u4 code_length = 0; + u1* code_start = 0; + u2 exception_table_length = 0; + typeArrayHandle exception_handlers(THREAD, Universe::the_empty_int_array()); + u2 checked_exceptions_length = 0; + u2* checked_exceptions_start = NULL; + CompressedLineNumberWriteStream* linenumber_table = NULL; + int linenumber_table_length = 0; + int total_lvt_length = 0; + u2 lvt_cnt = 0; + u2 lvtt_cnt = 0; + bool lvt_allocated = false; + u2 max_lvt_cnt = INITIAL_MAX_LVT_NUMBER; + u2 max_lvtt_cnt = INITIAL_MAX_LVT_NUMBER; + u2* localvariable_table_length; + u2** localvariable_table_start; + u2* localvariable_type_table_length; + u2** localvariable_type_table_start; + bool parsed_code_attribute = false; + bool parsed_checked_exceptions_attribute = false; + bool parsed_stackmap_attribute = false; + // stackmap attribute - JDK1.5 + typeArrayHandle stackmap_data; + u2 generic_signature_index = 0; + u1* runtime_visible_annotations = NULL; + int runtime_visible_annotations_length = 0; + u1* runtime_invisible_annotations = NULL; + int runtime_invisible_annotations_length = 0; + u1* runtime_visible_parameter_annotations = NULL; + int runtime_visible_parameter_annotations_length = 0; + u1* runtime_invisible_parameter_annotations = NULL; + int runtime_invisible_parameter_annotations_length = 0; + u1* annotation_default = NULL; + int annotation_default_length = 0; + + // Parse code and exceptions attribute + u2 method_attributes_count = cfs->get_u2_fast(); + while (method_attributes_count--) { + cfs->guarantee_more(6, CHECK_(nullHandle)); // method_attribute_name_index, method_attribute_length + u2 method_attribute_name_index = cfs->get_u2_fast(); + u4 method_attribute_length = cfs->get_u4_fast(); + check_property( + valid_cp_range(method_attribute_name_index, cp_size) && + cp->tag_at(method_attribute_name_index).is_utf8(), + "Invalid method attribute name index %u in class file %s", + method_attribute_name_index, CHECK_(nullHandle)); + + symbolOop method_attribute_name = cp->symbol_at(method_attribute_name_index); + if (method_attribute_name == vmSymbols::tag_code()) { + // Parse Code attribute + if (_need_verify) { + guarantee_property(!access_flags.is_native() && !access_flags.is_abstract(), + "Code attribute in native or abstract methods in class file %s", + CHECK_(nullHandle)); + } + if (parsed_code_attribute) { + classfile_parse_error("Multiple Code attributes in class file %s", CHECK_(nullHandle)); + } + parsed_code_attribute = true; + + // Stack size, locals size, and code size + if (_major_version == 45 && _minor_version <= 2) { + cfs->guarantee_more(4, CHECK_(nullHandle)); + max_stack = cfs->get_u1_fast(); + max_locals = cfs->get_u1_fast(); + code_length = cfs->get_u2_fast(); + } else { + cfs->guarantee_more(8, CHECK_(nullHandle)); + max_stack = cfs->get_u2_fast(); + max_locals = cfs->get_u2_fast(); + code_length = cfs->get_u4_fast(); + } + if (_need_verify) { + guarantee_property(args_size <= max_locals, + "Arguments can't fit into locals in class file %s", CHECK_(nullHandle)); + guarantee_property(code_length > 0 && code_length <= MAX_CODE_SIZE, + "Invalid method Code length %u in class file %s", + code_length, CHECK_(nullHandle)); + } + // Code pointer + code_start = cfs->get_u1_buffer(); + assert(code_start != NULL, "null code start"); + cfs->guarantee_more(code_length, CHECK_(nullHandle)); + cfs->skip_u1_fast(code_length); + + // Exception handler table + cfs->guarantee_more(2, CHECK_(nullHandle)); // exception_table_length + exception_table_length = cfs->get_u2_fast(); + if (exception_table_length > 0) { + exception_handlers = + parse_exception_table(code_length, exception_table_length, cp, CHECK_(nullHandle)); + } + + // Parse additional attributes in code attribute + cfs->guarantee_more(2, CHECK_(nullHandle)); // code_attributes_count + u2 code_attributes_count = cfs->get_u2_fast(); + unsigned int calculated_attribute_length = sizeof(max_stack) + + sizeof(max_locals) + + sizeof(code_length) + + code_length + + sizeof(exception_table_length) + + sizeof(code_attributes_count) + + exception_table_length*(sizeof(u2) /* start_pc */+ + sizeof(u2) /* end_pc */ + + sizeof(u2) /* handler_pc */ + + sizeof(u2) /* catch_type_index */); + + while (code_attributes_count--) { + cfs->guarantee_more(6, CHECK_(nullHandle)); // code_attribute_name_index, code_attribute_length + u2 code_attribute_name_index = cfs->get_u2_fast(); + u4 code_attribute_length = cfs->get_u4_fast(); + calculated_attribute_length += code_attribute_length + + sizeof(code_attribute_name_index) + + sizeof(code_attribute_length); + check_property(valid_cp_range(code_attribute_name_index, cp_size) && + cp->tag_at(code_attribute_name_index).is_utf8(), + "Invalid code attribute name index %u in class file %s", + code_attribute_name_index, + CHECK_(nullHandle)); + if (LoadLineNumberTables && + cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_line_number_table()) { + // Parse and compress line number table + parse_linenumber_table(code_attribute_length, code_length, + &linenumber_table, CHECK_(nullHandle)); + + } else if (LoadLocalVariableTables && + cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_table()) { + // Parse local variable table + if (!lvt_allocated) { + localvariable_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, INITIAL_MAX_LVT_NUMBER); + localvariable_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2*, INITIAL_MAX_LVT_NUMBER); + localvariable_type_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, INITIAL_MAX_LVT_NUMBER); + localvariable_type_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2*, INITIAL_MAX_LVT_NUMBER); + lvt_allocated = true; + } + if (lvt_cnt == max_lvt_cnt) { + max_lvt_cnt <<= 1; + REALLOC_RESOURCE_ARRAY(u2, localvariable_table_length, lvt_cnt, max_lvt_cnt); + REALLOC_RESOURCE_ARRAY(u2*, localvariable_table_start, lvt_cnt, max_lvt_cnt); + } + localvariable_table_start[lvt_cnt] = + parse_localvariable_table(code_length, + max_locals, + code_attribute_length, + cp, + &localvariable_table_length[lvt_cnt], + false, // is not LVTT + CHECK_(nullHandle)); + total_lvt_length += localvariable_table_length[lvt_cnt]; + lvt_cnt++; + } else if (LoadLocalVariableTypeTables && + _major_version >= JAVA_1_5_VERSION && + cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_type_table()) { + if (!lvt_allocated) { + localvariable_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, INITIAL_MAX_LVT_NUMBER); + localvariable_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2*, INITIAL_MAX_LVT_NUMBER); + localvariable_type_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, INITIAL_MAX_LVT_NUMBER); + localvariable_type_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2*, INITIAL_MAX_LVT_NUMBER); + lvt_allocated = true; + } + // Parse local variable type table + if (lvtt_cnt == max_lvtt_cnt) { + max_lvtt_cnt <<= 1; + REALLOC_RESOURCE_ARRAY(u2, localvariable_type_table_length, lvtt_cnt, max_lvtt_cnt); + REALLOC_RESOURCE_ARRAY(u2*, localvariable_type_table_start, lvtt_cnt, max_lvtt_cnt); + } + localvariable_type_table_start[lvtt_cnt] = + parse_localvariable_table(code_length, + max_locals, + code_attribute_length, + cp, + &localvariable_type_table_length[lvtt_cnt], + true, // is LVTT + CHECK_(nullHandle)); + lvtt_cnt++; + } else if (UseSplitVerifier && + _major_version >= Verifier::STACKMAP_ATTRIBUTE_MAJOR_VERSION && + cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_stack_map_table()) { + // Stack map is only needed by the new verifier in JDK1.5. + if (parsed_stackmap_attribute) { + classfile_parse_error("Multiple StackMapTable attributes in class file %s", CHECK_(nullHandle)); + } + typeArrayOop sm = + parse_stackmap_table(code_attribute_length, CHECK_(nullHandle)); + stackmap_data = typeArrayHandle(THREAD, sm); + parsed_stackmap_attribute = true; + } else { + // Skip unknown attributes + cfs->skip_u1(code_attribute_length, CHECK_(nullHandle)); + } + } + // check method attribute length + if (_need_verify) { + guarantee_property(method_attribute_length == calculated_attribute_length, + "Code segment has wrong length in class file %s", CHECK_(nullHandle)); + } + } else if (method_attribute_name == vmSymbols::tag_exceptions()) { + // Parse Exceptions attribute + if (parsed_checked_exceptions_attribute) { + classfile_parse_error("Multiple Exceptions attributes in class file %s", CHECK_(nullHandle)); + } + parsed_checked_exceptions_attribute = true; + checked_exceptions_start = + parse_checked_exceptions(&checked_exceptions_length, + method_attribute_length, + cp, CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_synthetic()) { + if (method_attribute_length != 0) { + classfile_parse_error( + "Invalid Synthetic method attribute length %u in class file %s", + method_attribute_length, CHECK_(nullHandle)); + } + // Should we check that there hasn't already been a synthetic attribute? + access_flags.set_is_synthetic(); + } else if (method_attribute_name == vmSymbols::tag_deprecated()) { // 4276120 + if (method_attribute_length != 0) { + classfile_parse_error( + "Invalid Deprecated method attribute length %u in class file %s", + method_attribute_length, CHECK_(nullHandle)); + } + } else if (_major_version >= JAVA_1_5_VERSION) { + if (method_attribute_name == vmSymbols::tag_signature()) { + if (method_attribute_length != 2) { + classfile_parse_error( + "Invalid Signature attribute length %u in class file %s", + method_attribute_length, CHECK_(nullHandle)); + } + cfs->guarantee_more(2, CHECK_(nullHandle)); // generic_signature_index + generic_signature_index = cfs->get_u2_fast(); + } else if (method_attribute_name == vmSymbols::tag_runtime_visible_annotations()) { + runtime_visible_annotations_length = method_attribute_length; + runtime_visible_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_annotations != NULL, "null visible annotations"); + cfs->skip_u1(runtime_visible_annotations_length, CHECK_(nullHandle)); + } else if (PreserveAllAnnotations && method_attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { + runtime_invisible_annotations_length = method_attribute_length; + runtime_invisible_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_annotations != NULL, "null invisible annotations"); + cfs->skip_u1(runtime_invisible_annotations_length, CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_runtime_visible_parameter_annotations()) { + runtime_visible_parameter_annotations_length = method_attribute_length; + runtime_visible_parameter_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_parameter_annotations != NULL, "null visible parameter annotations"); + cfs->skip_u1(runtime_visible_parameter_annotations_length, CHECK_(nullHandle)); + } else if (PreserveAllAnnotations && method_attribute_name == vmSymbols::tag_runtime_invisible_parameter_annotations()) { + runtime_invisible_parameter_annotations_length = method_attribute_length; + runtime_invisible_parameter_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_parameter_annotations != NULL, "null invisible parameter annotations"); + cfs->skip_u1(runtime_invisible_parameter_annotations_length, CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_annotation_default()) { + annotation_default_length = method_attribute_length; + annotation_default = cfs->get_u1_buffer(); + assert(annotation_default != NULL, "null annotation default"); + cfs->skip_u1(annotation_default_length, CHECK_(nullHandle)); + } else { + // Skip unknown attributes + cfs->skip_u1(method_attribute_length, CHECK_(nullHandle)); + } + } else { + // Skip unknown attributes + cfs->skip_u1(method_attribute_length, CHECK_(nullHandle)); + } + } + + if (linenumber_table != NULL) { + linenumber_table->write_terminator(); + linenumber_table_length = linenumber_table->position(); + } + + // Make sure there's at least one Code attribute in non-native/non-abstract method + if (_need_verify) { + guarantee_property(access_flags.is_native() || access_flags.is_abstract() || parsed_code_attribute, + "Absent Code attribute in method that is not native or abstract in class file %s", CHECK_(nullHandle)); + } + + // All sizing information for a methodOop is finally available, now create it + methodOop m_oop = oopFactory::new_method( + code_length, access_flags, linenumber_table_length, + total_lvt_length, checked_exceptions_length, CHECK_(nullHandle)); + methodHandle m (THREAD, m_oop); + + ClassLoadingService::add_class_method_size(m_oop->size()*HeapWordSize); + + // Fill in information from fixed part (access_flags already set) + m->set_constants(cp()); + m->set_name_index(name_index); + m->set_signature_index(signature_index); + m->set_generic_signature_index(generic_signature_index); +#ifdef CC_INTERP + // hmm is there a gc issue here?? + ResultTypeFinder rtf(cp->symbol_at(signature_index)); + m->set_result_index(rtf.type()); +#endif + + if (args_size >= 0) { + m->set_size_of_parameters(args_size); + } else { + m->compute_size_of_parameters(THREAD); + } +#ifdef ASSERT + if (args_size >= 0) { + m->compute_size_of_parameters(THREAD); + assert(args_size == m->size_of_parameters(), ""); + } +#endif + + // Fill in code attribute information + m->set_max_stack(max_stack); + m->set_max_locals(max_locals); + m->constMethod()->set_stackmap_data(stackmap_data()); + + /** + * The exception_table field is the flag used to indicate + * that the methodOop and it's associated constMethodOop are partially + * initialized and thus are exempt from pre/post GC verification. Once + * the field is set, the oops are considered fully initialized so make + * sure that the oops can pass verification when this field is set. + */ + m->set_exception_table(exception_handlers()); + + // Copy byte codes + if (code_length > 0) { + memcpy(m->code_base(), code_start, code_length); + } + + // Copy line number table + if (linenumber_table != NULL) { + memcpy(m->compressed_linenumber_table(), + linenumber_table->buffer(), linenumber_table_length); + } + + // Copy checked exceptions + if (checked_exceptions_length > 0) { + int size = checked_exceptions_length * sizeof(CheckedExceptionElement) / sizeof(u2); + copy_u2_with_conversion((u2*) m->checked_exceptions_start(), checked_exceptions_start, size); + } + + /* Copy class file LVT's/LVTT's into the HotSpot internal LVT. + * + * Rules for LVT's and LVTT's are: + * - There can be any number of LVT's and LVTT's. + * - If there are n LVT's, it is the same as if there was just + * one LVT containing all the entries from the n LVT's. + * - There may be no more than one LVT entry per local variable. + * Two LVT entries are 'equal' if these fields are the same: + * start_pc, length, name, slot + * - There may be no more than one LVTT entry per each LVT entry. + * Each LVTT entry has to match some LVT entry. + * - HotSpot internal LVT keeps natural ordering of class file LVT entries. + */ + if (total_lvt_length > 0) { + int tbl_no, idx; + + promoted_flags->set_has_localvariable_table(); + + LVT_Hash** lvt_Hash = NEW_RESOURCE_ARRAY(LVT_Hash*, HASH_ROW_SIZE); + initialize_hashtable(lvt_Hash); + + // To fill LocalVariableTable in + Classfile_LVT_Element* cf_lvt; + LocalVariableTableElement* lvt = m->localvariable_table_start(); + + for (tbl_no = 0; tbl_no < lvt_cnt; tbl_no++) { + cf_lvt = (Classfile_LVT_Element *) localvariable_table_start[tbl_no]; + for (idx = 0; idx < localvariable_table_length[tbl_no]; idx++, lvt++) { + copy_lvt_element(&cf_lvt[idx], lvt); + // If no duplicates, add LVT elem in hashtable lvt_Hash. + if (LVT_put_after_lookup(lvt, lvt_Hash) == false + && _need_verify + && _major_version >= JAVA_1_5_VERSION ) { + clear_hashtable(lvt_Hash); + classfile_parse_error("Duplicated LocalVariableTable attribute " + "entry for '%s' in class file %s", + cp->symbol_at(lvt->name_cp_index)->as_utf8(), + CHECK_(nullHandle)); + } + } + } + + // To merge LocalVariableTable and LocalVariableTypeTable + Classfile_LVT_Element* cf_lvtt; + LocalVariableTableElement lvtt_elem; + + for (tbl_no = 0; tbl_no < lvtt_cnt; tbl_no++) { + cf_lvtt = (Classfile_LVT_Element *) localvariable_type_table_start[tbl_no]; + for (idx = 0; idx < localvariable_type_table_length[tbl_no]; idx++) { + copy_lvt_element(&cf_lvtt[idx], &lvtt_elem); + int index = hash(&lvtt_elem); + LVT_Hash* entry = LVT_lookup(&lvtt_elem, index, lvt_Hash); + if (entry == NULL) { + if (_need_verify) { + clear_hashtable(lvt_Hash); + classfile_parse_error("LVTT entry for '%s' in class file %s " + "does not match any LVT entry", + cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), + CHECK_(nullHandle)); + } + } else if (entry->_elem->signature_cp_index != 0 && _need_verify) { + clear_hashtable(lvt_Hash); + classfile_parse_error("Duplicated LocalVariableTypeTable attribute " + "entry for '%s' in class file %s", + cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), + CHECK_(nullHandle)); + } else { + // to add generic signatures into LocalVariableTable + entry->_elem->signature_cp_index = lvtt_elem.descriptor_cp_index; + } + } + } + clear_hashtable(lvt_Hash); + } + + *method_annotations = assemble_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, + runtime_invisible_annotations, + runtime_invisible_annotations_length, + CHECK_(nullHandle)); + *method_parameter_annotations = assemble_annotations(runtime_visible_parameter_annotations, + runtime_visible_parameter_annotations_length, + runtime_invisible_parameter_annotations, + runtime_invisible_parameter_annotations_length, + CHECK_(nullHandle)); + *method_default_annotations = assemble_annotations(annotation_default, + annotation_default_length, + NULL, + 0, + CHECK_(nullHandle)); + + if (name() == vmSymbols::finalize_method_name() && + signature() == vmSymbols::void_method_signature()) { + if (m->is_empty_method()) { + _has_empty_finalizer = true; + } else { + _has_finalizer = true; + } + } + if (name() == vmSymbols::object_initializer_name() && + signature() == vmSymbols::void_method_signature() && + m->is_vanilla_constructor()) { + _has_vanilla_constructor = true; + } + + return m; +} + + +// The promoted_flags parameter is used to pass relevant access_flags +// from the methods back up to the containing klass. These flag values +// are added to klass's access_flags. + +objArrayHandle ClassFileParser::parse_methods(constantPoolHandle cp, bool is_interface, + AccessFlags* promoted_flags, + bool* has_final_method, + objArrayOop* methods_annotations_oop, + objArrayOop* methods_parameter_annotations_oop, + objArrayOop* methods_default_annotations_oop, + TRAPS) { + ClassFileStream* cfs = stream(); + objArrayHandle nullHandle; + typeArrayHandle method_annotations; + typeArrayHandle method_parameter_annotations; + typeArrayHandle method_default_annotations; + cfs->guarantee_more(2, CHECK_(nullHandle)); // length + u2 length = cfs->get_u2_fast(); + if (length == 0) { + return objArrayHandle(THREAD, Universe::the_empty_system_obj_array()); + } else { + objArrayOop m = oopFactory::new_system_objArray(length, CHECK_(nullHandle)); + objArrayHandle methods(THREAD, m); + HandleMark hm(THREAD); + objArrayHandle methods_annotations; + objArrayHandle methods_parameter_annotations; + objArrayHandle methods_default_annotations; + for (int index = 0; index < length; index++) { + methodHandle method = parse_method(cp, is_interface, + promoted_flags, + &method_annotations, + &method_parameter_annotations, + &method_default_annotations, + CHECK_(nullHandle)); + if (method->is_final()) { + *has_final_method = true; + } + methods->obj_at_put(index, method()); + if (method_annotations.not_null()) { + if (methods_annotations.is_null()) { + objArrayOop md = oopFactory::new_system_objArray(length, CHECK_(nullHandle)); + methods_annotations = objArrayHandle(THREAD, md); + } + methods_annotations->obj_at_put(index, method_annotations()); + } + if (method_parameter_annotations.not_null()) { + if (methods_parameter_annotations.is_null()) { + objArrayOop md = oopFactory::new_system_objArray(length, CHECK_(nullHandle)); + methods_parameter_annotations = objArrayHandle(THREAD, md); + } + methods_parameter_annotations->obj_at_put(index, method_parameter_annotations()); + } + if (method_default_annotations.not_null()) { + if (methods_default_annotations.is_null()) { + objArrayOop md = oopFactory::new_system_objArray(length, CHECK_(nullHandle)); + methods_default_annotations = objArrayHandle(THREAD, md); + } + methods_default_annotations->obj_at_put(index, method_default_annotations()); + } + } + if (_need_verify && length > 1) { + // Check duplicated methods + ResourceMark rm(THREAD); + NameSigHash** names_and_sigs = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, NameSigHash*, HASH_ROW_SIZE); + initialize_hashtable(names_and_sigs); + bool dup = false; + { + debug_only(No_Safepoint_Verifier nsv;) + for (int i = 0; i < length; i++) { + methodOop m = (methodOop)methods->obj_at(i); + // If no duplicates, add name/signature in hashtable names_and_sigs. + if (!put_after_lookup(m->name(), m->signature(), names_and_sigs)) { + dup = true; + break; + } + } + } + if (dup) { + classfile_parse_error("Duplicate method name&signature in class file %s", + CHECK_(nullHandle)); + } + } + + *methods_annotations_oop = methods_annotations(); + *methods_parameter_annotations_oop = methods_parameter_annotations(); + *methods_default_annotations_oop = methods_default_annotations(); + + return methods; + } +} + + +typeArrayHandle ClassFileParser::sort_methods(objArrayHandle methods, + objArrayHandle methods_annotations, + objArrayHandle methods_parameter_annotations, + objArrayHandle methods_default_annotations, + TRAPS) { + typeArrayHandle nullHandle; + int length = methods()->length(); + // If JVMTI original method ordering is enabled we have to + // remember the original class file ordering. + // We temporarily use the vtable_index field in the methodOop to store the + // class file index, so we can read in after calling qsort. + if (JvmtiExport::can_maintain_original_method_order()) { + for (int index = 0; index < length; index++) { + methodOop m = methodOop(methods->obj_at(index)); + assert(!m->valid_vtable_index(), "vtable index should not be set"); + m->set_vtable_index(index); + } + } + // Sort method array by ascending method name (for faster lookups & vtable construction) + // Note that the ordering is not alphabetical, see symbolOopDesc::fast_compare + methodOopDesc::sort_methods(methods(), + methods_annotations(), + methods_parameter_annotations(), + methods_default_annotations()); + + // If JVMTI original method ordering is enabled construct int array remembering the original ordering + if (JvmtiExport::can_maintain_original_method_order()) { + typeArrayOop new_ordering = oopFactory::new_permanent_intArray(length, CHECK_(nullHandle)); + typeArrayHandle method_ordering(THREAD, new_ordering); + for (int index = 0; index < length; index++) { + methodOop m = methodOop(methods->obj_at(index)); + int old_index = m->vtable_index(); + assert(old_index >= 0 && old_index < length, "invalid method index"); + method_ordering->int_at_put(index, old_index); + m->set_vtable_index(methodOopDesc::invalid_vtable_index); + } + return method_ordering; + } else { + return typeArrayHandle(THREAD, Universe::the_empty_int_array()); + } +} + + +void ClassFileParser::parse_classfile_sourcefile_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS) { + ClassFileStream* cfs = stream(); + cfs->guarantee_more(2, CHECK); // sourcefile_index + u2 sourcefile_index = cfs->get_u2_fast(); + check_property( + valid_cp_range(sourcefile_index, cp->length()) && + cp->tag_at(sourcefile_index).is_utf8(), + "Invalid SourceFile attribute at constant pool index %u in class file %s", + sourcefile_index, CHECK); + k->set_source_file_name(cp->symbol_at(sourcefile_index)); +} + + + +void ClassFileParser::parse_classfile_source_debug_extension_attribute(constantPoolHandle cp, + instanceKlassHandle k, + int length, TRAPS) { + ClassFileStream* cfs = stream(); + u1* sde_buffer = cfs->get_u1_buffer(); + assert(sde_buffer != NULL, "null sde buffer"); + + // Don't bother storing it if there is no way to retrieve it + if (JvmtiExport::can_get_source_debug_extension()) { + // Optimistically assume that only 1 byte UTF format is used + // (common case) + symbolOop sde_symbol = oopFactory::new_symbol((char*)sde_buffer, + length, CHECK); + k->set_source_debug_extension(sde_symbol); + } + // Got utf8 string, set stream position forward + cfs->skip_u1(length, CHECK); +} + + +// Inner classes can be static, private or protected (classic VM does this) +#define RECOGNIZED_INNER_CLASS_MODIFIERS (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_PRIVATE | JVM_ACC_PROTECTED | JVM_ACC_STATIC) + +// Return number of classes in the inner classes attribute table +u2 ClassFileParser::parse_classfile_inner_classes_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS) { + ClassFileStream* cfs = stream(); + cfs->guarantee_more(2, CHECK_0); // length + u2 length = cfs->get_u2_fast(); + + // 4-tuples of shorts [inner_class_info_index, outer_class_info_index, inner_name_index, inner_class_access_flags] + typeArrayOop ic = oopFactory::new_permanent_shortArray(length*4, CHECK_0); + typeArrayHandle inner_classes(THREAD, ic); + int index = 0; + int cp_size = cp->length(); + cfs->guarantee_more(8 * length, CHECK_0); // 4-tuples of u2 + for (int n = 0; n < length; n++) { + // Inner class index + u2 inner_class_info_index = cfs->get_u2_fast(); + check_property( + inner_class_info_index == 0 || + (valid_cp_range(inner_class_info_index, cp_size) && + cp->tag_at(inner_class_info_index).is_klass_reference()), + "inner_class_info_index %u has bad constant type in class file %s", + inner_class_info_index, CHECK_0); + // Outer class index + u2 outer_class_info_index = cfs->get_u2_fast(); + check_property( + outer_class_info_index == 0 || + (valid_cp_range(outer_class_info_index, cp_size) && + cp->tag_at(outer_class_info_index).is_klass_reference()), + "outer_class_info_index %u has bad constant type in class file %s", + outer_class_info_index, CHECK_0); + // Inner class name + u2 inner_name_index = cfs->get_u2_fast(); + check_property( + inner_name_index == 0 || (valid_cp_range(inner_name_index, cp_size) && + cp->tag_at(inner_name_index).is_utf8()), + "inner_name_index %u has bad constant type in class file %s", + inner_name_index, CHECK_0); + if (_need_verify) { + guarantee_property(inner_class_info_index != outer_class_info_index, + "Class is both outer and inner class in class file %s", CHECK_0); + } + // Access flags + AccessFlags inner_access_flags; + jint flags = cfs->get_u2_fast() & RECOGNIZED_INNER_CLASS_MODIFIERS; + if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) { + // Set abstract bit for old class files for backward compatibility + flags |= JVM_ACC_ABSTRACT; + } + verify_legal_class_modifiers(flags, CHECK_0); + inner_access_flags.set_flags(flags); + + inner_classes->short_at_put(index++, inner_class_info_index); + inner_classes->short_at_put(index++, outer_class_info_index); + inner_classes->short_at_put(index++, inner_name_index); + inner_classes->short_at_put(index++, inner_access_flags.as_short()); + } + + // 4347400: make sure there's no duplicate entry in the classes array + if (_need_verify && _major_version >= JAVA_1_5_VERSION) { + for(int i = 0; i < inner_classes->length(); i += 4) { + for(int j = i + 4; j < inner_classes->length(); j += 4) { + guarantee_property((inner_classes->ushort_at(i) != inner_classes->ushort_at(j) || + inner_classes->ushort_at(i+1) != inner_classes->ushort_at(j+1) || + inner_classes->ushort_at(i+2) != inner_classes->ushort_at(j+2) || + inner_classes->ushort_at(i+3) != inner_classes->ushort_at(j+3)), + "Duplicate entry in InnerClasses in class file %s", + CHECK_0); + } + } + } + + // Update instanceKlass with inner class info. + k->set_inner_classes(inner_classes()); + return length; +} + +void ClassFileParser::parse_classfile_synthetic_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS) { + k->set_is_synthetic(); +} + +void ClassFileParser::parse_classfile_signature_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS) { + ClassFileStream* cfs = stream(); + u2 signature_index = cfs->get_u2(CHECK); + check_property( + valid_cp_range(signature_index, cp->length()) && + cp->tag_at(signature_index).is_utf8(), + "Invalid constant pool index %u in Signature attribute in class file %s", + signature_index, CHECK); + k->set_generic_signature(cp->symbol_at(signature_index)); +} + +void ClassFileParser::parse_classfile_attributes(constantPoolHandle cp, instanceKlassHandle k, TRAPS) { + ClassFileStream* cfs = stream(); + // Set inner classes attribute to default sentinel + k->set_inner_classes(Universe::the_empty_short_array()); + cfs->guarantee_more(2, CHECK); // attributes_count + u2 attributes_count = cfs->get_u2_fast(); + bool parsed_sourcefile_attribute = false; + bool parsed_innerclasses_attribute = false; + bool parsed_enclosingmethod_attribute = false; + u1* runtime_visible_annotations = NULL; + int runtime_visible_annotations_length = 0; + u1* runtime_invisible_annotations = NULL; + int runtime_invisible_annotations_length = 0; + // Iterate over attributes + while (attributes_count--) { + cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length + u2 attribute_name_index = cfs->get_u2_fast(); + u4 attribute_length = cfs->get_u4_fast(); + check_property( + valid_cp_range(attribute_name_index, cp->length()) && + cp->tag_at(attribute_name_index).is_utf8(), + "Attribute name has bad constant pool index %u in class file %s", + attribute_name_index, CHECK); + symbolOop tag = cp->symbol_at(attribute_name_index); + if (tag == vmSymbols::tag_source_file()) { + // Check for SourceFile tag + if (_need_verify) { + guarantee_property(attribute_length == 2, "Wrong SourceFile attribute length in class file %s", CHECK); + } + if (parsed_sourcefile_attribute) { + classfile_parse_error("Multiple SourceFile attributes in class file %s", CHECK); + } else { + parsed_sourcefile_attribute = true; + } + parse_classfile_sourcefile_attribute(cp, k, CHECK); + } else if (tag == vmSymbols::tag_source_debug_extension()) { + // Check for SourceDebugExtension tag + parse_classfile_source_debug_extension_attribute(cp, k, (int)attribute_length, CHECK); + } else if (tag == vmSymbols::tag_inner_classes()) { + // Check for InnerClasses tag + if (parsed_innerclasses_attribute) { + classfile_parse_error("Multiple InnerClasses attributes in class file %s", CHECK); + } else { + parsed_innerclasses_attribute = true; + } + u2 num_of_classes = parse_classfile_inner_classes_attribute(cp, k, CHECK); + if (_need_verify && _major_version >= JAVA_1_5_VERSION) { + guarantee_property(attribute_length == sizeof(num_of_classes) + 4 * sizeof(u2) * num_of_classes, + "Wrong InnerClasses attribute length in class file %s", CHECK); + } + } else if (tag == vmSymbols::tag_synthetic()) { + // Check for Synthetic tag + // Shouldn't we check that the synthetic flags wasn't already set? - not required in spec + if (attribute_length != 0) { + classfile_parse_error( + "Invalid Synthetic classfile attribute length %u in class file %s", + attribute_length, CHECK); + } + parse_classfile_synthetic_attribute(cp, k, CHECK); + } else if (tag == vmSymbols::tag_deprecated()) { + // Check for Deprecatd tag - 4276120 + if (attribute_length != 0) { + classfile_parse_error( + "Invalid Deprecated classfile attribute length %u in class file %s", + attribute_length, CHECK); + } + } else if (_major_version >= JAVA_1_5_VERSION) { + if (tag == vmSymbols::tag_signature()) { + if (attribute_length != 2) { + classfile_parse_error( + "Wrong Signature attribute length %u in class file %s", + attribute_length, CHECK); + } + parse_classfile_signature_attribute(cp, k, CHECK); + } else if (tag == vmSymbols::tag_runtime_visible_annotations()) { + runtime_visible_annotations_length = attribute_length; + runtime_visible_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_annotations != NULL, "null visible annotations"); + cfs->skip_u1(runtime_visible_annotations_length, CHECK); + } else if (PreserveAllAnnotations && tag == vmSymbols::tag_runtime_invisible_annotations()) { + runtime_invisible_annotations_length = attribute_length; + runtime_invisible_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_annotations != NULL, "null invisible annotations"); + cfs->skip_u1(runtime_invisible_annotations_length, CHECK); + } else if (tag == vmSymbols::tag_enclosing_method()) { + if (parsed_enclosingmethod_attribute) { + classfile_parse_error("Multiple EnclosingMethod attributes in class file %s", CHECK); + } else { + parsed_enclosingmethod_attribute = true; + } + cfs->guarantee_more(4, CHECK); // class_index, method_index + u2 class_index = cfs->get_u2_fast(); + u2 method_index = cfs->get_u2_fast(); + if (class_index == 0) { + classfile_parse_error("Invalid class index in EnclosingMethod attribute in class file %s", CHECK); + } + // Validate the constant pool indices and types + if (!cp->is_within_bounds(class_index) || + !cp->tag_at(class_index).is_klass_reference()) { + classfile_parse_error("Invalid or out-of-bounds class index in EnclosingMethod attribute in class file %s", CHECK); + } + if (method_index != 0 && + (!cp->is_within_bounds(method_index) || + !cp->tag_at(method_index).is_name_and_type())) { + classfile_parse_error("Invalid or out-of-bounds method index in EnclosingMethod attribute in class file %s", CHECK); + } + k->set_enclosing_method_indices(class_index, method_index); + } else { + // Unknown attribute + cfs->skip_u1(attribute_length, CHECK); + } + } else { + // Unknown attribute + cfs->skip_u1(attribute_length, CHECK); + } + } + typeArrayHandle annotations = assemble_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, + runtime_invisible_annotations, + runtime_invisible_annotations_length, + CHECK); + k->set_class_annotations(annotations()); +} + + +typeArrayHandle ClassFileParser::assemble_annotations(u1* runtime_visible_annotations, + int runtime_visible_annotations_length, + u1* runtime_invisible_annotations, + int runtime_invisible_annotations_length, TRAPS) { + typeArrayHandle annotations; + if (runtime_visible_annotations != NULL || + runtime_invisible_annotations != NULL) { + typeArrayOop anno = oopFactory::new_permanent_byteArray(runtime_visible_annotations_length + + runtime_invisible_annotations_length, CHECK_(annotations)); + annotations = typeArrayHandle(THREAD, anno); + if (runtime_visible_annotations != NULL) { + memcpy(annotations->byte_at_addr(0), runtime_visible_annotations, runtime_visible_annotations_length); + } + if (runtime_invisible_annotations != NULL) { + memcpy(annotations->byte_at_addr(runtime_visible_annotations_length), runtime_invisible_annotations, runtime_invisible_annotations_length); + } + } + return annotations; +} + + +static void initialize_static_field(fieldDescriptor* fd, TRAPS) { + KlassHandle h_k (THREAD, fd->field_holder()); + assert(h_k.not_null() && fd->is_static(), "just checking"); + if (fd->has_initial_value()) { + BasicType t = fd->field_type(); + switch (t) { + case T_BYTE: + h_k()->byte_field_put(fd->offset(), fd->int_initial_value()); + break; + case T_BOOLEAN: + h_k()->bool_field_put(fd->offset(), fd->int_initial_value()); + break; + case T_CHAR: + h_k()->char_field_put(fd->offset(), fd->int_initial_value()); + break; + case T_SHORT: + h_k()->short_field_put(fd->offset(), fd->int_initial_value()); + break; + case T_INT: + h_k()->int_field_put(fd->offset(), fd->int_initial_value()); + break; + case T_FLOAT: + h_k()->float_field_put(fd->offset(), fd->float_initial_value()); + break; + case T_DOUBLE: + h_k()->double_field_put(fd->offset(), fd->double_initial_value()); + break; + case T_LONG: + h_k()->long_field_put(fd->offset(), fd->long_initial_value()); + break; + case T_OBJECT: + { + #ifdef ASSERT + symbolOop sym = oopFactory::new_symbol("Ljava/lang/String;", CHECK); + assert(fd->signature() == sym, "just checking"); + #endif + oop string = fd->string_initial_value(CHECK); + h_k()->obj_field_put(fd->offset(), string); + } + break; + default: + THROW_MSG(vmSymbols::java_lang_ClassFormatError(), + "Illegal ConstantValue attribute in class file"); + } + } +} + + +void ClassFileParser::java_lang_ref_Reference_fix_pre(typeArrayHandle* fields_ptr, + constantPoolHandle cp, FieldAllocationCount *fac_ptr, TRAPS) { + // This code is for compatibility with earlier jdk's that do not + // have the "discovered" field in java.lang.ref.Reference. For 1.5 + // the check for the "discovered" field should issue a warning if + // the field is not found. For 1.6 this code should be issue a + // fatal error if the "discovered" field is not found. + // + // Increment fac.nonstatic_oop_count so that the start of the + // next type of non-static oops leaves room for the fake oop. + // Do not increment next_nonstatic_oop_offset so that the + // fake oop is place after the java.lang.ref.Reference oop + // fields. + // + // Check the fields in java.lang.ref.Reference for the "discovered" + // field. If it is not present, artifically create a field for it. + // This allows this VM to run on early JDK where the field is not + // present. + + // + // Increment fac.nonstatic_oop_count so that the start of the + // next type of non-static oops leaves room for the fake oop. + // Do not increment next_nonstatic_oop_offset so that the + // fake oop is place after the java.lang.ref.Reference oop + // fields. + // + // Check the fields in java.lang.ref.Reference for the "discovered" + // field. If it is not present, artifically create a field for it. + // This allows this VM to run on early JDK where the field is not + // present. + int reference_sig_index = 0; + int reference_name_index = 0; + int reference_index = 0; + int extra = java_lang_ref_Reference::number_of_fake_oop_fields; + const int n = (*fields_ptr)()->length(); + for (int i = 0; i < n; i += instanceKlass::next_offset ) { + int name_index = + (*fields_ptr)()->ushort_at(i + instanceKlass::name_index_offset); + int sig_index = + (*fields_ptr)()->ushort_at(i + instanceKlass::signature_index_offset); + symbolOop f_name = cp->symbol_at(name_index); + symbolOop f_sig = cp->symbol_at(sig_index); + if (f_sig == vmSymbols::reference_signature() && reference_index == 0) { + // Save the index for reference signature for later use. + // The fake discovered field does not entries in the + // constant pool so the index for its signature cannot + // be extracted from the constant pool. It will need + // later, however. It's signature is vmSymbols::reference_signature() + // so same an index for that signature. + reference_sig_index = sig_index; + reference_name_index = name_index; + reference_index = i; + } + if (f_name == vmSymbols::reference_discovered_name() && + f_sig == vmSymbols::reference_signature()) { + // The values below are fake but will force extra + // non-static oop fields and a corresponding non-static + // oop map block to be allocated. + extra = 0; + break; + } + } + if (extra != 0) { + fac_ptr->nonstatic_oop_count += extra; + // Add the additional entry to "fields" so that the klass + // contains the "discoverd" field and the field will be initialized + // in instances of the object. + int fields_with_fix_length = (*fields_ptr)()->length() + + instanceKlass::next_offset; + typeArrayOop ff = oopFactory::new_permanent_shortArray( + fields_with_fix_length, CHECK); + typeArrayHandle fields_with_fix(THREAD, ff); + + // Take everything from the original but the length. + for (int idx = 0; idx < (*fields_ptr)->length(); idx++) { + fields_with_fix->ushort_at_put(idx, (*fields_ptr)->ushort_at(idx)); + } + + // Add the fake field at the end. + int i = (*fields_ptr)->length(); + // There is no name index for the fake "discovered" field nor + // signature but a signature is needed so that the field will + // be properly initialized. Use one found for + // one of the other reference fields. Be sure the index for the + // name is 0. In fieldDescriptor::initialize() the index of the + // name is checked. That check is by passed for the last nonstatic + // oop field in a java.lang.ref.Reference which is assumed to be + // this artificial "discovered" field. An assertion checks that + // the name index is 0. + assert(reference_index != 0, "Missing signature for reference"); + + int j; + for (j = 0; j < instanceKlass::next_offset; j++) { + fields_with_fix->ushort_at_put(i + j, + (*fields_ptr)->ushort_at(reference_index +j)); + } + // Clear the public access flag and set the private access flag. + short flags; + flags = + fields_with_fix->ushort_at(i + instanceKlass::access_flags_offset); + assert(!(flags & JVM_RECOGNIZED_FIELD_MODIFIERS), "Unexpected access flags set"); + flags = flags & (~JVM_ACC_PUBLIC); + flags = flags | JVM_ACC_PRIVATE; + AccessFlags access_flags; + access_flags.set_flags(flags); + assert(!access_flags.is_public(), "Failed to clear public flag"); + assert(access_flags.is_private(), "Failed to set private flag"); + fields_with_fix->ushort_at_put(i + instanceKlass::access_flags_offset, + flags); + + assert(fields_with_fix->ushort_at(i + instanceKlass::name_index_offset) + == reference_name_index, "The fake reference name is incorrect"); + assert(fields_with_fix->ushort_at(i + instanceKlass::signature_index_offset) + == reference_sig_index, "The fake reference signature is incorrect"); + // The type of the field is stored in the low_offset entry during + // parsing. + assert(fields_with_fix->ushort_at(i + instanceKlass::low_offset) == + NONSTATIC_OOP, "The fake reference type is incorrect"); + + // "fields" is allocated in the permanent generation. Disgard + // it and let it be collected. + (*fields_ptr) = fields_with_fix; + } + return; +} + + +void ClassFileParser::java_lang_Class_fix_pre(objArrayHandle* methods_ptr, + FieldAllocationCount *fac_ptr, TRAPS) { + // Add fake fields for java.lang.Class instances + // + // This is not particularly nice. We should consider adding a + // private transient object field at the Java level to + // java.lang.Class. Alternatively we could add a subclass of + // instanceKlass which provides an accessor and size computer for + // this field, but that appears to be more code than this hack. + // + // NOTE that we wedge these in at the beginning rather than the + // end of the object because the Class layout changed between JDK + // 1.3 and JDK 1.4 with the new reflection implementation; some + // nonstatic oop fields were added at the Java level. The offsets + // of these fake fields can't change between these two JDK + // versions because when the offsets are computed at bootstrap + // time we don't know yet which version of the JDK we're running in. + + // The values below are fake but will force two non-static oop fields and + // a corresponding non-static oop map block to be allocated. + const int extra = java_lang_Class::number_of_fake_oop_fields; + fac_ptr->nonstatic_oop_count += extra; +} + + +void ClassFileParser::java_lang_Class_fix_post(int* next_nonstatic_oop_offset_ptr) { + // Cause the extra fake fields in java.lang.Class to show up before + // the Java fields for layout compatibility between 1.3 and 1.4 + // Incrementing next_nonstatic_oop_offset here advances the + // location where the real java fields are placed. + const int extra = java_lang_Class::number_of_fake_oop_fields; + (*next_nonstatic_oop_offset_ptr) += (extra * wordSize); +} + + +instanceKlassHandle ClassFileParser::parseClassFile(symbolHandle name, + Handle class_loader, + Handle protection_domain, + symbolHandle& parsed_name, + TRAPS) { + // So that JVMTI can cache class file in the state before retransformable agents + // have modified it + unsigned char *cached_class_file_bytes = NULL; + jint cached_class_file_length; + + ClassFileStream* cfs = stream(); + // Timing + PerfTraceTime vmtimer(ClassLoader::perf_accumulated_time()); + + _has_finalizer = _has_empty_finalizer = _has_vanilla_constructor = false; + + if (JvmtiExport::should_post_class_file_load_hook()) { + unsigned char* ptr = cfs->buffer(); + unsigned char* end_ptr = cfs->buffer() + cfs->length(); + + JvmtiExport::post_class_file_load_hook(name, class_loader, protection_domain, + &ptr, &end_ptr, + &cached_class_file_bytes, + &cached_class_file_length); + + if (ptr != cfs->buffer()) { + // JVMTI agent has modified class file data. + // Set new class file stream using JVMTI agent modified + // class file data. + cfs = new ClassFileStream(ptr, end_ptr - ptr, cfs->source()); + set_stream(cfs); + } + } + + + instanceKlassHandle nullHandle; + + // Figure out whether we can skip format checking (matching classic VM behavior) + _need_verify = Verifier::should_verify_for(class_loader()); + + // Set the verify flag in stream + cfs->set_verify(_need_verify); + + // Save the class file name for easier error message printing. + _class_name = name.not_null()? name : vmSymbolHandles::unknown_class_name(); + + cfs->guarantee_more(8, CHECK_(nullHandle)); // magic, major, minor + // Magic value + u4 magic = cfs->get_u4_fast(); + guarantee_property(magic == JAVA_CLASSFILE_MAGIC, + "Incompatible magic value %u in class file %s", + magic, CHECK_(nullHandle)); + + // Version numbers + u2 minor_version = cfs->get_u2_fast(); + u2 major_version = cfs->get_u2_fast(); + + // Check version numbers - we check this even with verifier off + if (!is_supported_version(major_version, minor_version)) { + if (name.is_null()) { + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_UnsupportedClassVersionError(), + "Unsupported major.minor version %u.%u", + major_version, + minor_version); + } else { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_UnsupportedClassVersionError(), + "%s : Unsupported major.minor version %u.%u", + name->as_C_string(), + major_version, + minor_version); + } + return nullHandle; + } + + _major_version = major_version; + _minor_version = minor_version; + + + // Check if verification needs to be relaxed for this class file + // Do not restrict it to jdk1.0 or jdk1.1 to maintain backward compatibility (4982376) + _relax_verify = Verifier::relax_verify_for(class_loader()); + + // Constant pool + constantPoolHandle cp = parse_constant_pool(CHECK_(nullHandle)); + int cp_size = cp->length(); + + cfs->guarantee_more(8, CHECK_(nullHandle)); // flags, this_class, super_class, infs_len + + // Access flags + AccessFlags access_flags; + jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS; + + if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) { + // Set abstract bit for old class files for backward compatibility + flags |= JVM_ACC_ABSTRACT; + } + verify_legal_class_modifiers(flags, CHECK_(nullHandle)); + access_flags.set_flags(flags); + + // This class and superclass + instanceKlassHandle super_klass; + u2 this_class_index = cfs->get_u2_fast(); + check_property( + valid_cp_range(this_class_index, cp_size) && + cp->tag_at(this_class_index).is_unresolved_klass(), + "Invalid this class index %u in constant pool in class file %s", + this_class_index, CHECK_(nullHandle)); + + symbolHandle class_name (THREAD, cp->unresolved_klass_at(this_class_index)); + assert(class_name.not_null(), "class_name can't be null"); + + // It's important to set parsed_name *before* resolving the super class. + // (it's used for cleanup by the caller if parsing fails) + parsed_name = class_name; + + // Update _class_name which could be null previously to be class_name + _class_name = class_name; + + // Don't need to check whether this class name is legal or not. + // It has been checked when constant pool is parsed. + // However, make sure it is not an array type. + if (_need_verify) { + guarantee_property(class_name->byte_at(0) != JVM_SIGNATURE_ARRAY, + "Bad class name in class file %s", + CHECK_(nullHandle)); + } + + klassOop preserve_this_klass; // for storing result across HandleMark + + // release all handles when parsing is done + { HandleMark hm(THREAD); + + // Checks if name in class file matches requested name + if (name.not_null() && class_name() != name()) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_NoClassDefFoundError(), + "%s (wrong name: %s)", + name->as_C_string(), + class_name->as_C_string() + ); + return nullHandle; + } + + if (TraceClassLoadingPreorder) { + tty->print("[Loading %s", name()->as_klass_external_name()); + if (cfs->source() != NULL) tty->print(" from %s", cfs->source()); + tty->print_cr("]"); + } + + u2 super_class_index = cfs->get_u2_fast(); + if (super_class_index == 0) { + check_property(class_name() == vmSymbols::java_lang_Object(), + "Invalid superclass index %u in class file %s", + super_class_index, + CHECK_(nullHandle)); + } else { + check_property(valid_cp_range(super_class_index, cp_size) && + cp->tag_at(super_class_index).is_unresolved_klass(), + "Invalid superclass index %u in class file %s", + super_class_index, + CHECK_(nullHandle)); + // The class name should be legal because it is checked when parsing constant pool. + // However, make sure it is not an array type. + if (_need_verify) { + guarantee_property(cp->unresolved_klass_at(super_class_index)->byte_at(0) != JVM_SIGNATURE_ARRAY, + "Bad superclass name in class file %s", CHECK_(nullHandle)); + } + } + + // Interfaces + u2 itfs_len = cfs->get_u2_fast(); + objArrayHandle local_interfaces; + if (itfs_len == 0) { + local_interfaces = objArrayHandle(THREAD, Universe::the_empty_system_obj_array()); + } else { + local_interfaces = parse_interfaces(cp, itfs_len, class_loader, protection_domain, &vmtimer, _class_name, CHECK_(nullHandle)); + } + + // Fields (offsets are filled in later) + struct FieldAllocationCount fac = {0,0,0,0,0,0,0,0,0,0}; + objArrayHandle fields_annotations; + typeArrayHandle fields = parse_fields(cp, access_flags.is_interface(), &fac, &fields_annotations, CHECK_(nullHandle)); + // Methods + bool has_final_method = false; + AccessFlags promoted_flags; + promoted_flags.set_flags(0); + // These need to be oop pointers because they are allocated lazily + // inside parse_methods inside a nested HandleMark + objArrayOop methods_annotations_oop = NULL; + objArrayOop methods_parameter_annotations_oop = NULL; + objArrayOop methods_default_annotations_oop = NULL; + objArrayHandle methods = parse_methods(cp, access_flags.is_interface(), + &promoted_flags, + &has_final_method, + &methods_annotations_oop, + &methods_parameter_annotations_oop, + &methods_default_annotations_oop, + CHECK_(nullHandle)); + + objArrayHandle methods_annotations(THREAD, methods_annotations_oop); + objArrayHandle methods_parameter_annotations(THREAD, methods_parameter_annotations_oop); + objArrayHandle methods_default_annotations(THREAD, methods_default_annotations_oop); + + // We check super class after class file is parsed and format is checked + if (super_class_index > 0) { + symbolHandle sk (THREAD, cp->klass_name_at(super_class_index)); + if (access_flags.is_interface()) { + // Before attempting to resolve the superclass, check for class format + // errors not checked yet. + guarantee_property(sk() == vmSymbols::java_lang_Object(), + "Interfaces must have java.lang.Object as superclass in class file %s", + CHECK_(nullHandle)); + } + klassOop k = SystemDictionary::resolve_super_or_fail(class_name, + sk, + class_loader, + protection_domain, + true, + CHECK_(nullHandle)); + KlassHandle kh (THREAD, k); + super_klass = instanceKlassHandle(THREAD, kh()); + if (super_klass->is_interface()) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_IncompatibleClassChangeError(), + "class %s has interface %s as super class", + class_name->as_klass_external_name(), + super_klass->external_name() + ); + return nullHandle; + } + // Make sure super class is not final + if (super_klass->is_final()) { + THROW_MSG_(vmSymbols::java_lang_VerifyError(), "Cannot inherit from final class", nullHandle); + } + } + + // Compute the transitive list of all unique interfaces implemented by this class + objArrayHandle transitive_interfaces = compute_transitive_interfaces(super_klass, local_interfaces, CHECK_(nullHandle)); + + // sort methods + typeArrayHandle method_ordering = sort_methods(methods, + methods_annotations, + methods_parameter_annotations, + methods_default_annotations, + CHECK_(nullHandle)); + + // promote flags from parse_methods() to the klass' flags + access_flags.add_promoted_flags(promoted_flags.as_int()); + + // Size of Java vtable (in words) + int vtable_size = 0; + int itable_size = 0; + int num_miranda_methods = 0; + + klassVtable::compute_vtable_size_and_num_mirandas(vtable_size, + num_miranda_methods, + super_klass(), + methods(), + access_flags, + class_loader(), + class_name(), + local_interfaces()); + + // Size of Java itable (in words) + itable_size = access_flags.is_interface() ? 0 : klassItable::compute_itable_size(transitive_interfaces); + + // Field size and offset computation + int nonstatic_field_size = super_klass() == NULL ? 0 : super_klass->nonstatic_field_size(); +#ifndef PRODUCT + int orig_nonstatic_field_size = 0; +#endif + int static_field_size = 0; + int next_static_oop_offset; + int next_static_double_offset; + int next_static_word_offset; + int next_static_short_offset; + int next_static_byte_offset; + int next_static_type_offset; + int next_nonstatic_oop_offset; + int next_nonstatic_double_offset; + int next_nonstatic_word_offset; + int next_nonstatic_short_offset; + int next_nonstatic_byte_offset; + int next_nonstatic_type_offset; + int first_nonstatic_oop_offset; + int first_nonstatic_field_offset; + int next_nonstatic_field_offset; + + // Calculate the starting byte offsets + next_static_oop_offset = (instanceKlass::header_size() + + align_object_offset(vtable_size) + + align_object_offset(itable_size)) * wordSize; + next_static_double_offset = next_static_oop_offset + + (fac.static_oop_count * oopSize); + if ( fac.static_double_count && + (Universe::field_type_should_be_aligned(T_DOUBLE) || + Universe::field_type_should_be_aligned(T_LONG)) ) { + next_static_double_offset = align_size_up(next_static_double_offset, BytesPerLong); + } + + next_static_word_offset = next_static_double_offset + + (fac.static_double_count * BytesPerLong); + next_static_short_offset = next_static_word_offset + + (fac.static_word_count * BytesPerInt); + next_static_byte_offset = next_static_short_offset + + (fac.static_short_count * BytesPerShort); + next_static_type_offset = align_size_up((next_static_byte_offset + + fac.static_byte_count ), wordSize ); + static_field_size = (next_static_type_offset - + next_static_oop_offset) / wordSize; + first_nonstatic_field_offset = (instanceOopDesc::header_size() + + nonstatic_field_size) * wordSize; + next_nonstatic_field_offset = first_nonstatic_field_offset; + + // Add fake fields for java.lang.Class instances (also see below) + if (class_name() == vmSymbols::java_lang_Class() && class_loader.is_null()) { + java_lang_Class_fix_pre(&methods, &fac, CHECK_(nullHandle)); + } + + // Add a fake "discovered" field if it is not present + // for compatibility with earlier jdk's. + if (class_name() == vmSymbols::java_lang_ref_Reference() + && class_loader.is_null()) { + java_lang_ref_Reference_fix_pre(&fields, cp, &fac, CHECK_(nullHandle)); + } + // end of "discovered" field compactibility fix + + int nonstatic_double_count = fac.nonstatic_double_count; + int nonstatic_word_count = fac.nonstatic_word_count; + int nonstatic_short_count = fac.nonstatic_short_count; + int nonstatic_byte_count = fac.nonstatic_byte_count; + int nonstatic_oop_count = fac.nonstatic_oop_count; + + // Prepare list of oops for oop maps generation. + u2* nonstatic_oop_offsets; + u2* nonstatic_oop_length; + int nonstatic_oop_map_count = 0; + + nonstatic_oop_offsets = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, nonstatic_oop_count+1); + nonstatic_oop_length = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, nonstatic_oop_count+1); + + // Add fake fields for java.lang.Class instances (also see above). + // FieldsAllocationStyle and CompactFields values will be reset to default. + if(class_name() == vmSymbols::java_lang_Class() && class_loader.is_null()) { + java_lang_Class_fix_post(&next_nonstatic_field_offset); + nonstatic_oop_offsets[0] = (u2)first_nonstatic_field_offset; + int fake_oop_count = (( next_nonstatic_field_offset - + first_nonstatic_field_offset ) / oopSize); + nonstatic_oop_length [0] = (u2)fake_oop_count; + nonstatic_oop_map_count = 1; + nonstatic_oop_count -= fake_oop_count; + first_nonstatic_oop_offset = first_nonstatic_field_offset; + } else { + first_nonstatic_oop_offset = 0; // will be set for first oop field + } + +#ifndef PRODUCT + if( PrintCompactFieldsSavings ) { + next_nonstatic_double_offset = next_nonstatic_field_offset + + (nonstatic_oop_count * oopSize); + if ( nonstatic_double_count > 0 ) { + next_nonstatic_double_offset = align_size_up(next_nonstatic_double_offset, BytesPerLong); + } + next_nonstatic_word_offset = next_nonstatic_double_offset + + (nonstatic_double_count * BytesPerLong); + next_nonstatic_short_offset = next_nonstatic_word_offset + + (nonstatic_word_count * BytesPerInt); + next_nonstatic_byte_offset = next_nonstatic_short_offset + + (nonstatic_short_count * BytesPerShort); + next_nonstatic_type_offset = align_size_up((next_nonstatic_byte_offset + + nonstatic_byte_count ), wordSize ); + orig_nonstatic_field_size = nonstatic_field_size + + ((next_nonstatic_type_offset - first_nonstatic_field_offset)/wordSize); + } +#endif + bool compact_fields = CompactFields; + int allocation_style = FieldsAllocationStyle; + if( allocation_style < 0 || allocation_style > 1 ) { // Out of range? + assert(false, "0 <= FieldsAllocationStyle <= 1"); + allocation_style = 1; // Optimistic + } + + // The next classes have predefined hard-coded fields offsets + // (see in JavaClasses::compute_hard_coded_offsets()). + // Use default fields allocation order for them. + if( (allocation_style != 0 || compact_fields ) && class_loader.is_null() && + (class_name() == vmSymbols::java_lang_AssertionStatusDirectives() || + class_name() == vmSymbols::java_lang_Class() || + class_name() == vmSymbols::java_lang_ClassLoader() || + class_name() == vmSymbols::java_lang_ref_Reference() || + class_name() == vmSymbols::java_lang_ref_SoftReference() || + class_name() == vmSymbols::java_lang_StackTraceElement() || + class_name() == vmSymbols::java_lang_String() || + class_name() == vmSymbols::java_lang_Throwable()) ) { + allocation_style = 0; // Allocate oops first + compact_fields = false; // Don't compact fields + } + + if( allocation_style == 0 ) { + // Fields order: oops, longs/doubles, ints, shorts/chars, bytes + next_nonstatic_oop_offset = next_nonstatic_field_offset; + next_nonstatic_double_offset = next_nonstatic_oop_offset + + (nonstatic_oop_count * oopSize); + } else if( allocation_style == 1 ) { + // Fields order: longs/doubles, ints, shorts/chars, bytes, oops + next_nonstatic_double_offset = next_nonstatic_field_offset; + } else { + ShouldNotReachHere(); + } + + int nonstatic_oop_space_count = 0; + int nonstatic_word_space_count = 0; + int nonstatic_short_space_count = 0; + int nonstatic_byte_space_count = 0; + int nonstatic_oop_space_offset; + int nonstatic_word_space_offset; + int nonstatic_short_space_offset; + int nonstatic_byte_space_offset; + + if( nonstatic_double_count > 0 ) { + int offset = next_nonstatic_double_offset; + next_nonstatic_double_offset = align_size_up(offset, BytesPerLong); + if( compact_fields && offset != next_nonstatic_double_offset ) { + // Allocate available fields into the gap before double field. + int length = next_nonstatic_double_offset - offset; + assert(length == BytesPerInt, ""); + nonstatic_word_space_offset = offset; + if( nonstatic_word_count > 0 ) { + nonstatic_word_count -= 1; + nonstatic_word_space_count = 1; // Only one will fit + length -= BytesPerInt; + offset += BytesPerInt; + } + nonstatic_short_space_offset = offset; + while( length >= BytesPerShort && nonstatic_short_count > 0 ) { + nonstatic_short_count -= 1; + nonstatic_short_space_count += 1; + length -= BytesPerShort; + offset += BytesPerShort; + } + nonstatic_byte_space_offset = offset; + while( length > 0 && nonstatic_byte_count > 0 ) { + nonstatic_byte_count -= 1; + nonstatic_byte_space_count += 1; + length -= 1; + } + // Allocate oop field in the gap if there are no other fields for that. + nonstatic_oop_space_offset = offset; + if( length >= oopSize && nonstatic_oop_count > 0 && + allocation_style != 0 ) { // when oop fields not first + nonstatic_oop_count -= 1; + nonstatic_oop_space_count = 1; // Only one will fit + length -= oopSize; + offset += oopSize; + } + } + } + + next_nonstatic_word_offset = next_nonstatic_double_offset + + (nonstatic_double_count * BytesPerLong); + next_nonstatic_short_offset = next_nonstatic_word_offset + + (nonstatic_word_count * BytesPerInt); + next_nonstatic_byte_offset = next_nonstatic_short_offset + + (nonstatic_short_count * BytesPerShort); + + int notaligned_offset; + if( allocation_style == 0 ) { + notaligned_offset = next_nonstatic_byte_offset + nonstatic_byte_count; + } else { // allocation_style == 1 + next_nonstatic_oop_offset = next_nonstatic_byte_offset + nonstatic_byte_count; + if( nonstatic_oop_count > 0 ) { + notaligned_offset = next_nonstatic_oop_offset; + next_nonstatic_oop_offset = align_size_up(next_nonstatic_oop_offset, oopSize); + } + notaligned_offset = next_nonstatic_oop_offset + (nonstatic_oop_count * oopSize); + } + next_nonstatic_type_offset = align_size_up(notaligned_offset, wordSize ); + nonstatic_field_size = nonstatic_field_size + ((next_nonstatic_type_offset + - first_nonstatic_field_offset)/wordSize); + + // Iterate over fields again and compute correct offsets. + // The field allocation type was temporarily stored in the offset slot. + // oop fields are located before non-oop fields (static and non-static). + int len = fields->length(); + for (int i = 0; i < len; i += instanceKlass::next_offset) { + int real_offset; + FieldAllocationType atype = (FieldAllocationType) fields->ushort_at(i+4); + switch (atype) { + case STATIC_OOP: + real_offset = next_static_oop_offset; + next_static_oop_offset += oopSize; + break; + case STATIC_BYTE: + real_offset = next_static_byte_offset; + next_static_byte_offset += 1; + break; + case STATIC_SHORT: + real_offset = next_static_short_offset; + next_static_short_offset += BytesPerShort; + break; + case STATIC_WORD: + real_offset = next_static_word_offset; + next_static_word_offset += BytesPerInt; + break; + case STATIC_ALIGNED_DOUBLE: + case STATIC_DOUBLE: + real_offset = next_static_double_offset; + next_static_double_offset += BytesPerLong; + break; + case NONSTATIC_OOP: + if( nonstatic_oop_space_count > 0 ) { + real_offset = nonstatic_oop_space_offset; + nonstatic_oop_space_offset += oopSize; + nonstatic_oop_space_count -= 1; + } else { + real_offset = next_nonstatic_oop_offset; + next_nonstatic_oop_offset += oopSize; + } + // Update oop maps + if( nonstatic_oop_map_count > 0 && + nonstatic_oop_offsets[nonstatic_oop_map_count - 1] == + (u2)(real_offset - nonstatic_oop_length[nonstatic_oop_map_count - 1] * oopSize) ) { + // Extend current oop map + nonstatic_oop_length[nonstatic_oop_map_count - 1] += 1; + } else { + // Create new oop map + nonstatic_oop_offsets[nonstatic_oop_map_count] = (u2)real_offset; + nonstatic_oop_length [nonstatic_oop_map_count] = 1; + nonstatic_oop_map_count += 1; + if( first_nonstatic_oop_offset == 0 ) { // Undefined + first_nonstatic_oop_offset = real_offset; + } + } + break; + case NONSTATIC_BYTE: + if( nonstatic_byte_space_count > 0 ) { + real_offset = nonstatic_byte_space_offset; + nonstatic_byte_space_offset += 1; + nonstatic_byte_space_count -= 1; + } else { + real_offset = next_nonstatic_byte_offset; + next_nonstatic_byte_offset += 1; + } + break; + case NONSTATIC_SHORT: + if( nonstatic_short_space_count > 0 ) { + real_offset = nonstatic_short_space_offset; + nonstatic_short_space_offset += BytesPerShort; + nonstatic_short_space_count -= 1; + } else { + real_offset = next_nonstatic_short_offset; + next_nonstatic_short_offset += BytesPerShort; + } + break; + case NONSTATIC_WORD: + if( nonstatic_word_space_count > 0 ) { + real_offset = nonstatic_word_space_offset; + nonstatic_word_space_offset += BytesPerInt; + nonstatic_word_space_count -= 1; + } else { + real_offset = next_nonstatic_word_offset; + next_nonstatic_word_offset += BytesPerInt; + } + break; + case NONSTATIC_ALIGNED_DOUBLE: + case NONSTATIC_DOUBLE: + real_offset = next_nonstatic_double_offset; + next_nonstatic_double_offset += BytesPerLong; + break; + default: + ShouldNotReachHere(); + } + fields->short_at_put(i+4, extract_low_short_from_int(real_offset) ); + fields->short_at_put(i+5, extract_high_short_from_int(real_offset) ); + } + + // Size of instances + int instance_size; + + instance_size = align_object_size(next_nonstatic_type_offset / wordSize); + + assert(instance_size == align_object_size(instanceOopDesc::header_size() + nonstatic_field_size), "consistent layout helper value"); + + // Size of non-static oop map blocks (in words) allocated at end of klass + int nonstatic_oop_map_size = compute_oop_map_size(super_klass, nonstatic_oop_map_count, first_nonstatic_oop_offset); + + // Compute reference type + ReferenceType rt; + if (super_klass() == NULL) { + rt = REF_NONE; + } else { + rt = super_klass->reference_type(); + } + + // We can now create the basic klassOop for this klass + klassOop ik = oopFactory::new_instanceKlass( + vtable_size, itable_size, + static_field_size, nonstatic_oop_map_size, + rt, CHECK_(nullHandle)); + instanceKlassHandle this_klass (THREAD, ik); + + assert(this_klass->static_field_size() == static_field_size && + this_klass->nonstatic_oop_map_size() == nonstatic_oop_map_size, "sanity check"); + + // Fill in information already parsed + this_klass->set_access_flags(access_flags); + jint lh = Klass::instance_layout_helper(instance_size, false); + this_klass->set_layout_helper(lh); + assert(this_klass->oop_is_instance(), "layout is correct"); + assert(this_klass->size_helper() == instance_size, "correct size_helper"); + // Not yet: supers are done below to support the new subtype-checking fields + //this_klass->set_super(super_klass()); + this_klass->set_class_loader(class_loader()); + this_klass->set_nonstatic_field_size(nonstatic_field_size); + this_klass->set_static_oop_field_size(fac.static_oop_count); + cp->set_pool_holder(this_klass()); + this_klass->set_constants(cp()); + this_klass->set_local_interfaces(local_interfaces()); + this_klass->set_fields(fields()); + this_klass->set_methods(methods()); + if (has_final_method) { + this_klass->set_has_final_method(); + } + this_klass->set_method_ordering(method_ordering()); + this_klass->set_initial_method_idnum(methods->length()); + this_klass->set_name(cp->klass_name_at(this_class_index)); + this_klass->set_protection_domain(protection_domain()); + this_klass->set_fields_annotations(fields_annotations()); + this_klass->set_methods_annotations(methods_annotations()); + this_klass->set_methods_parameter_annotations(methods_parameter_annotations()); + this_klass->set_methods_default_annotations(methods_default_annotations()); + + this_klass->set_minor_version(minor_version); + this_klass->set_major_version(major_version); + + if (cached_class_file_bytes != NULL) { + // JVMTI: we have an instanceKlass now, tell it about the cached bytes + this_klass->set_cached_class_file(cached_class_file_bytes, + cached_class_file_length); + } + + // Miranda methods + if ((num_miranda_methods > 0) || + // if this class introduced new miranda methods or + (super_klass.not_null() && (super_klass->has_miranda_methods())) + // super class exists and this class inherited miranda methods + ) { + this_klass->set_has_miranda_methods(); // then set a flag + } + + // Additional attributes + parse_classfile_attributes(cp, this_klass, CHECK_(nullHandle)); + + // Make sure this is the end of class file stream + guarantee_property(cfs->at_eos(), "Extra bytes at the end of class file %s", CHECK_(nullHandle)); + + // Initialize static fields + this_klass->do_local_static_fields(&initialize_static_field, CHECK_(nullHandle)); + + // VerifyOops believes that once this has been set, the object is completely loaded. + // Compute transitive closure of interfaces this class implements + this_klass->set_transitive_interfaces(transitive_interfaces()); + + // Fill in information needed to compute superclasses. + this_klass->initialize_supers(super_klass(), CHECK_(nullHandle)); + + // Initialize itable offset tables + klassItable::setup_itable_offset_table(this_klass); + + // Do final class setup + fill_oop_maps(this_klass, nonstatic_oop_map_count, nonstatic_oop_offsets, nonstatic_oop_length); + + set_precomputed_flags(this_klass); + + // reinitialize modifiers, using the InnerClasses attribute + int computed_modifiers = this_klass->compute_modifier_flags(CHECK_(nullHandle)); + this_klass->set_modifier_flags(computed_modifiers); + + // check if this class can access its super class + check_super_class_access(this_klass, CHECK_(nullHandle)); + + // check if this class can access its superinterfaces + check_super_interface_access(this_klass, CHECK_(nullHandle)); + + // check if this class overrides any final method + check_final_method_override(this_klass, CHECK_(nullHandle)); + + // check that if this class is an interface then it doesn't have static methods + if (this_klass->is_interface()) { + check_illegal_static_method(this_klass, CHECK_(nullHandle)); + } + + ClassLoadingService::notify_class_loaded(instanceKlass::cast(this_klass()), + false /* not shared class */); + + if (TraceClassLoading) { + // print in a single call to reduce interleaving of output + if (cfs->source() != NULL) { + tty->print("[Loaded %s from %s]\n", this_klass->external_name(), + cfs->source()); + } else if (class_loader.is_null()) { + if (THREAD->is_Java_thread()) { + klassOop caller = ((JavaThread*)THREAD)->security_get_caller_class(1); + tty->print("[Loaded %s by instance of %s]\n", + this_klass->external_name(), + instanceKlass::cast(caller)->external_name()); + } else { + tty->print("[Loaded %s]\n", this_klass->external_name()); + } + } else { + ResourceMark rm; + tty->print("[Loaded %s from %s]\n", this_klass->external_name(), + instanceKlass::cast(class_loader->klass())->external_name()); + } + } + + if (TraceClassResolution) { + // print out the superclass. + const char * from = Klass::cast(this_klass())->external_name(); + if (this_klass->java_super() != NULL) { + tty->print("RESOLVE %s %s\n", from, instanceKlass::cast(this_klass->java_super())->external_name()); + } + // print out each of the interface classes referred to by this class. + objArrayHandle local_interfaces(THREAD, this_klass->local_interfaces()); + if (!local_interfaces.is_null()) { + int length = local_interfaces->length(); + for (int i = 0; i < length; i++) { + klassOop k = klassOop(local_interfaces->obj_at(i)); + instanceKlass* to_class = instanceKlass::cast(k); + const char * to = to_class->external_name(); + tty->print("RESOLVE %s %s\n", from, to); + } + } + } + +#ifndef PRODUCT + if( PrintCompactFieldsSavings ) { + if( nonstatic_field_size < orig_nonstatic_field_size ) { + tty->print("[Saved %d of %3d words in %s]\n", + orig_nonstatic_field_size - nonstatic_field_size, + orig_nonstatic_field_size, this_klass->external_name()); + } else if( nonstatic_field_size > orig_nonstatic_field_size ) { + tty->print("[Wasted %d over %3d words in %s]\n", + nonstatic_field_size - orig_nonstatic_field_size, + orig_nonstatic_field_size, this_klass->external_name()); + } + } +#endif + + // preserve result across HandleMark + preserve_this_klass = this_klass(); + } + + // Create new handle outside HandleMark + instanceKlassHandle this_klass (THREAD, preserve_this_klass); + debug_only(this_klass->as_klassOop()->verify();) + + return this_klass; +} + + +int ClassFileParser::compute_oop_map_size(instanceKlassHandle super, int nonstatic_oop_map_count, int first_nonstatic_oop_offset) { + int map_size = super.is_null() ? 0 : super->nonstatic_oop_map_size(); + if (nonstatic_oop_map_count > 0) { + // We have oops to add to map + if (map_size == 0) { + map_size = nonstatic_oop_map_count; + } else { + // Check whether we should add a new map block or whether the last one can be extended + OopMapBlock* first_map = super->start_of_nonstatic_oop_maps(); + OopMapBlock* last_map = first_map + map_size - 1; + + int next_offset = last_map->offset() + (last_map->length() * oopSize); + if (next_offset == first_nonstatic_oop_offset) { + // There is no gap bettwen superklass's last oop field and first + // local oop field, merge maps. + nonstatic_oop_map_count -= 1; + } else { + // Superklass didn't end with a oop field, add extra maps + assert(next_offsetstart_of_nonstatic_oop_maps(); + OopMapBlock* last_oop_map = this_oop_map + k->nonstatic_oop_map_size(); + instanceKlass* super = k->superklass(); + if (super != NULL) { + int super_oop_map_size = super->nonstatic_oop_map_size(); + OopMapBlock* super_oop_map = super->start_of_nonstatic_oop_maps(); + // Copy maps from superklass + while (super_oop_map_size-- > 0) { + *this_oop_map++ = *super_oop_map++; + } + } + if (nonstatic_oop_map_count > 0) { + if (this_oop_map + nonstatic_oop_map_count > last_oop_map) { + // Calculated in compute_oop_map_size() number of oop maps is less then + // collected oop maps since there is no gap between superklass's last oop + // field and first local oop field. Extend the last oop map copied + // from the superklass instead of creating new one. + nonstatic_oop_map_count--; + nonstatic_oop_offsets++; + this_oop_map--; + this_oop_map->set_length(this_oop_map->length() + *nonstatic_oop_length++); + this_oop_map++; + } + assert((this_oop_map + nonstatic_oop_map_count) == last_oop_map, "just checking"); + // Add new map blocks, fill them + while (nonstatic_oop_map_count-- > 0) { + this_oop_map->set_offset(*nonstatic_oop_offsets++); + this_oop_map->set_length(*nonstatic_oop_length++); + this_oop_map++; + } + } +} + + +void ClassFileParser::set_precomputed_flags(instanceKlassHandle k) { + klassOop super = k->super(); + + // Check if this klass has an empty finalize method (i.e. one with return bytecode only), + // in which case we don't have to register objects as finalizable + if (!_has_empty_finalizer) { + if (_has_finalizer || + (super != NULL && super->klass_part()->has_finalizer())) { + k->set_has_finalizer(); + } + } + +#ifdef ASSERT + bool f = false; + methodOop m = k->lookup_method(vmSymbols::finalize_method_name(), + vmSymbols::void_method_signature()); + if (m != NULL && !m->is_empty_method()) { + f = true; + } + assert(f == k->has_finalizer(), "inconsistent has_finalizer"); +#endif + + // Check if this klass supports the java.lang.Cloneable interface + if (SystemDictionary::cloneable_klass_loaded()) { + if (k->is_subtype_of(SystemDictionary::cloneable_klass())) { + k->set_is_cloneable(); + } + } + + // Check if this klass has a vanilla default constructor + if (super == NULL) { + // java.lang.Object has empty default constructor + k->set_has_vanilla_constructor(); + } else { + if (Klass::cast(super)->has_vanilla_constructor() && + _has_vanilla_constructor) { + k->set_has_vanilla_constructor(); + } +#ifdef ASSERT + bool v = false; + if (Klass::cast(super)->has_vanilla_constructor()) { + methodOop constructor = k->find_method(vmSymbols::object_initializer_name( +), vmSymbols::void_method_signature()); + if (constructor != NULL && constructor->is_vanilla_constructor()) { + v = true; + } + } + assert(v == k->has_vanilla_constructor(), "inconsistent has_vanilla_constructor"); +#endif + } + + // If it cannot be fast-path allocated, set a bit in the layout helper. + // See documentation of instanceKlass::can_be_fastpath_allocated(). + assert(k->size_helper() > 0, "layout_helper is initialized"); + if ((!RegisterFinalizersAtInit && k->has_finalizer()) + || k->is_abstract() || k->is_interface() + || (k->name() == vmSymbols::java_lang_Class() + && k->class_loader() == NULL) + || k->size_helper() >= FastAllocateSizeLimit) { + // Forbid fast-path allocation. + jint lh = Klass::instance_layout_helper(k->size_helper(), true); + k->set_layout_helper(lh); + } +} + + +// utility method for appending and array with check for duplicates + +void append_interfaces(objArrayHandle result, int& index, objArrayOop ifs) { + // iterate over new interfaces + for (int i = 0; i < ifs->length(); i++) { + oop e = ifs->obj_at(i); + assert(e->is_klass() && instanceKlass::cast(klassOop(e))->is_interface(), "just checking"); + // check for duplicates + bool duplicate = false; + for (int j = 0; j < index; j++) { + if (result->obj_at(j) == e) { + duplicate = true; + break; + } + } + // add new interface + if (!duplicate) { + result->obj_at_put(index++, e); + } + } +} + +objArrayHandle ClassFileParser::compute_transitive_interfaces(instanceKlassHandle super, objArrayHandle local_ifs, TRAPS) { + // Compute maximum size for transitive interfaces + int max_transitive_size = 0; + int super_size = 0; + // Add superclass transitive interfaces size + if (super.not_null()) { + super_size = super->transitive_interfaces()->length(); + max_transitive_size += super_size; + } + // Add local interfaces' super interfaces + int local_size = local_ifs->length(); + for (int i = 0; i < local_size; i++) { + klassOop l = klassOop(local_ifs->obj_at(i)); + max_transitive_size += instanceKlass::cast(l)->transitive_interfaces()->length(); + } + // Finally add local interfaces + max_transitive_size += local_size; + // Construct array + objArrayHandle result; + if (max_transitive_size == 0) { + // no interfaces, use canonicalized array + result = objArrayHandle(THREAD, Universe::the_empty_system_obj_array()); + } else if (max_transitive_size == super_size) { + // no new local interfaces added, share superklass' transitive interface array + result = objArrayHandle(THREAD, super->transitive_interfaces()); + } else if (max_transitive_size == local_size) { + // only local interfaces added, share local interface array + result = local_ifs; + } else { + objArrayHandle nullHandle; + objArrayOop new_objarray = oopFactory::new_system_objArray(max_transitive_size, CHECK_(nullHandle)); + result = objArrayHandle(THREAD, new_objarray); + int index = 0; + // Copy down from superclass + if (super.not_null()) { + append_interfaces(result, index, super->transitive_interfaces()); + } + // Copy down from local interfaces' superinterfaces + for (int i = 0; i < local_ifs->length(); i++) { + klassOop l = klassOop(local_ifs->obj_at(i)); + append_interfaces(result, index, instanceKlass::cast(l)->transitive_interfaces()); + } + // Finally add local interfaces + append_interfaces(result, index, local_ifs()); + + // Check if duplicates were removed + if (index != max_transitive_size) { + assert(index < max_transitive_size, "just checking"); + objArrayOop new_result = oopFactory::new_system_objArray(index, CHECK_(nullHandle)); + for (int i = 0; i < index; i++) { + oop e = result->obj_at(i); + assert(e != NULL, "just checking"); + new_result->obj_at_put(i, e); + } + result = objArrayHandle(THREAD, new_result); + } + } + return result; +} + + +void ClassFileParser::check_super_class_access(instanceKlassHandle this_klass, TRAPS) { + klassOop super = this_klass->super(); + if ((super != NULL) && + (!Reflection::verify_class_access(this_klass->as_klassOop(), super, false))) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_IllegalAccessError(), + "class %s cannot access its superclass %s", + this_klass->external_name(), + instanceKlass::cast(super)->external_name() + ); + return; + } +} + + +void ClassFileParser::check_super_interface_access(instanceKlassHandle this_klass, TRAPS) { + objArrayHandle local_interfaces (THREAD, this_klass->local_interfaces()); + int lng = local_interfaces->length(); + for (int i = lng - 1; i >= 0; i--) { + klassOop k = klassOop(local_interfaces->obj_at(i)); + assert (k != NULL && Klass::cast(k)->is_interface(), "invalid interface"); + if (!Reflection::verify_class_access(this_klass->as_klassOop(), k, false)) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_IllegalAccessError(), + "class %s cannot access its superinterface %s", + this_klass->external_name(), + instanceKlass::cast(k)->external_name() + ); + return; + } + } +} + + +void ClassFileParser::check_final_method_override(instanceKlassHandle this_klass, TRAPS) { + objArrayHandle methods (THREAD, this_klass->methods()); + int num_methods = methods->length(); + + // go thru each method and check if it overrides a final method + for (int index = 0; index < num_methods; index++) { + methodOop m = (methodOop)methods->obj_at(index); + + // skip private, static and methods + if ((!m->is_private()) && + (!m->is_static()) && + (m->name() != vmSymbols::object_initializer_name())) { + + symbolOop name = m->name(); + symbolOop signature = m->signature(); + klassOop k = this_klass->super(); + methodOop super_m = NULL; + while (k != NULL) { + // skip supers that don't have final methods. + if (k->klass_part()->has_final_method()) { + // lookup a matching method in the super class hierarchy + super_m = instanceKlass::cast(k)->lookup_method(name, signature); + if (super_m == NULL) { + break; // didn't find any match; get out + } + + if (super_m->is_final() && + // matching method in super is final + (Reflection::verify_field_access(this_klass->as_klassOop(), + super_m->method_holder(), + super_m->method_holder(), + super_m->access_flags(), false)) + // this class can access super final method and therefore override + ) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_VerifyError(), + "class %s overrides final method %s.%s", + this_klass->external_name(), + name->as_C_string(), + signature->as_C_string() + ); + return; + } + + // continue to look from super_m's holder's super. + k = instanceKlass::cast(super_m->method_holder())->super(); + continue; + } + + k = k->klass_part()->super(); + } + } + } +} + + +// assumes that this_klass is an interface +void ClassFileParser::check_illegal_static_method(instanceKlassHandle this_klass, TRAPS) { + assert(this_klass->is_interface(), "not an interface"); + objArrayHandle methods (THREAD, this_klass->methods()); + int num_methods = methods->length(); + + for (int index = 0; index < num_methods; index++) { + methodOop m = (methodOop)methods->obj_at(index); + // if m is static and not the init method, throw a verify error + if ((m->is_static()) && (m->name() != vmSymbols::class_initializer_name())) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_VerifyError(), + "Illegal static method %s in interface %s", + m->name()->as_C_string(), + this_klass->external_name() + ); + return; + } + } +} + +// utility methods for format checking + +void ClassFileParser::verify_legal_class_modifiers(jint flags, TRAPS) { + if (!_need_verify) { return; } + + const bool is_interface = (flags & JVM_ACC_INTERFACE) != 0; + const bool is_abstract = (flags & JVM_ACC_ABSTRACT) != 0; + const bool is_final = (flags & JVM_ACC_FINAL) != 0; + const bool is_super = (flags & JVM_ACC_SUPER) != 0; + const bool is_enum = (flags & JVM_ACC_ENUM) != 0; + const bool is_annotation = (flags & JVM_ACC_ANNOTATION) != 0; + const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; + + if ((is_abstract && is_final) || + (is_interface && !is_abstract) || + (is_interface && major_gte_15 && (is_super || is_enum)) || + (!is_interface && major_gte_15 && is_annotation)) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_ClassFormatError(), + "Illegal class modifiers in class %s: 0x%X", + _class_name->as_C_string(), flags + ); + return; + } +} + +bool ClassFileParser::has_illegal_visibility(jint flags) { + const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; + const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; + const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; + + return ((is_public && is_protected) || + (is_public && is_private) || + (is_protected && is_private)); +} + +bool ClassFileParser::is_supported_version(u2 major, u2 minor) { + return (major >= JAVA_MIN_SUPPORTED_VERSION) && + (major <= JAVA_MAX_SUPPORTED_VERSION) && + ((major != JAVA_MAX_SUPPORTED_VERSION) || + (minor <= JAVA_MAX_SUPPORTED_MINOR_VERSION)); +} + +void ClassFileParser::verify_legal_field_modifiers( + jint flags, bool is_interface, TRAPS) { + if (!_need_verify) { return; } + + const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; + const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; + const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; + const bool is_static = (flags & JVM_ACC_STATIC) != 0; + const bool is_final = (flags & JVM_ACC_FINAL) != 0; + const bool is_volatile = (flags & JVM_ACC_VOLATILE) != 0; + const bool is_transient = (flags & JVM_ACC_TRANSIENT) != 0; + const bool is_enum = (flags & JVM_ACC_ENUM) != 0; + const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; + + bool is_illegal = false; + + if (is_interface) { + if (!is_public || !is_static || !is_final || is_private || + is_protected || is_volatile || is_transient || + (major_gte_15 && is_enum)) { + is_illegal = true; + } + } else { // not interface + if (has_illegal_visibility(flags) || (is_final && is_volatile)) { + is_illegal = true; + } + } + + if (is_illegal) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_ClassFormatError(), + "Illegal field modifiers in class %s: 0x%X", + _class_name->as_C_string(), flags); + return; + } +} + +void ClassFileParser::verify_legal_method_modifiers( + jint flags, bool is_interface, symbolHandle name, TRAPS) { + if (!_need_verify) { return; } + + const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; + const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; + const bool is_static = (flags & JVM_ACC_STATIC) != 0; + const bool is_final = (flags & JVM_ACC_FINAL) != 0; + const bool is_native = (flags & JVM_ACC_NATIVE) != 0; + const bool is_abstract = (flags & JVM_ACC_ABSTRACT) != 0; + const bool is_bridge = (flags & JVM_ACC_BRIDGE) != 0; + const bool is_strict = (flags & JVM_ACC_STRICT) != 0; + const bool is_synchronized = (flags & JVM_ACC_SYNCHRONIZED) != 0; + const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; + const bool is_initializer = (name == vmSymbols::object_initializer_name()); + + bool is_illegal = false; + + if (is_interface) { + if (!is_abstract || !is_public || is_static || is_final || + is_native || (major_gte_15 && (is_synchronized || is_strict))) { + is_illegal = true; + } + } else { // not interface + if (is_initializer) { + if (is_static || is_final || is_synchronized || is_native || + is_abstract || (major_gte_15 && is_bridge)) { + is_illegal = true; + } + } else { // not initializer + if (is_abstract) { + if ((is_final || is_native || is_private || is_static || + (major_gte_15 && (is_synchronized || is_strict)))) { + is_illegal = true; + } + } + if (has_illegal_visibility(flags)) { + is_illegal = true; + } + } + } + + if (is_illegal) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_ClassFormatError(), + "Method %s in class %s has illegal modifiers: 0x%X", + name->as_C_string(), _class_name->as_C_string(), flags); + return; + } +} + +void ClassFileParser::verify_legal_utf8(const unsigned char* buffer, int length, TRAPS) { + assert(_need_verify, "only called when _need_verify is true"); + int i = 0; + int count = length >> 2; + for (int k=0; k= 128 (highest bit 1) for v == 0 or v >= 128. + unsigned char res = b0 | b0 - 1 | + b1 | b1 - 1 | + b2 | b2 - 1 | + b3 | b3 - 1; + if (res >= 128) break; + i += 4; + } + for(; i < length; i++) { + unsigned short c; + // no embedded zeros + guarantee_property((buffer[i] != 0), "Illegal UTF8 string in constant pool in class file %s", CHECK); + if(buffer[i] < 128) { + continue; + } + if ((i + 5) < length) { // see if it's legal supplementary character + if (UTF8::is_supplementary_character(&buffer[i])) { + c = UTF8::get_supplementary_character(&buffer[i]); + i += 5; + continue; + } + } + switch (buffer[i] >> 4) { + default: break; + case 0x8: case 0x9: case 0xA: case 0xB: case 0xF: + classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK); + case 0xC: case 0xD: // 110xxxxx 10xxxxxx + c = (buffer[i] & 0x1F) << 6; + i++; + if ((i < length) && ((buffer[i] & 0xC0) == 0x80)) { + c += buffer[i] & 0x3F; + if (_major_version <= 47 || c == 0 || c >= 0x80) { + // for classes with major > 47, c must a null or a character in its shortest form + break; + } + } + classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK); + case 0xE: // 1110xxxx 10xxxxxx 10xxxxxx + c = (buffer[i] & 0xF) << 12; + i += 2; + if ((i < length) && ((buffer[i-1] & 0xC0) == 0x80) && ((buffer[i] & 0xC0) == 0x80)) { + c += ((buffer[i-1] & 0x3F) << 6) + (buffer[i] & 0x3F); + if (_major_version <= 47 || c >= 0x800) { + // for classes with major > 47, c must be in its shortest form + break; + } + } + classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK); + } // end of switch + } // end of for +} + +// Checks if name is a legal class name. +void ClassFileParser::verify_legal_class_name(symbolHandle name, TRAPS) { + if (!_need_verify || _relax_verify) { return; } + + char buf[fixed_buffer_size]; + char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); + unsigned int length = name->utf8_length(); + bool legal = false; + + if (length > 0) { + char* p; + if (bytes[0] == JVM_SIGNATURE_ARRAY) { + p = skip_over_field_signature(bytes, false, length, CHECK); + legal = (p != NULL) && ((p - bytes) == (int)length); + } else if (_major_version < JAVA_1_5_VERSION) { + if (bytes[0] != '<') { + p = skip_over_field_name(bytes, true, length); + legal = (p != NULL) && ((p - bytes) == (int)length); + } + } else { + // 4900761: relax the constraints based on JSR202 spec + // Class names may be drawn from the entire Unicode character set. + // Identifiers between '/' must be unqualified names. + // The utf8 string has been verified when parsing cpool entries. + legal = verify_unqualified_name(bytes, length, LegalClass); + } + } + if (!legal) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_ClassFormatError(), + "Illegal class name \"%s\" in class file %s", bytes, + _class_name->as_C_string() + ); + return; + } +} + +// Checks if name is a legal field name. +void ClassFileParser::verify_legal_field_name(symbolHandle name, TRAPS) { + if (!_need_verify || _relax_verify) { return; } + + char buf[fixed_buffer_size]; + char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); + unsigned int length = name->utf8_length(); + bool legal = false; + + if (length > 0) { + if (_major_version < JAVA_1_5_VERSION) { + if (bytes[0] != '<') { + char* p = skip_over_field_name(bytes, false, length); + legal = (p != NULL) && ((p - bytes) == (int)length); + } + } else { + // 4881221: relax the constraints based on JSR202 spec + legal = verify_unqualified_name(bytes, length, LegalField); + } + } + + if (!legal) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_ClassFormatError(), + "Illegal field name \"%s\" in class %s", bytes, + _class_name->as_C_string() + ); + return; + } +} + +// Checks if name is a legal method name. +void ClassFileParser::verify_legal_method_name(symbolHandle name, TRAPS) { + if (!_need_verify || _relax_verify) { return; } + + assert(!name.is_null(), "method name is null"); + char buf[fixed_buffer_size]; + char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); + unsigned int length = name->utf8_length(); + bool legal = false; + + if (length > 0) { + if (bytes[0] == '<') { + if (name == vmSymbols::object_initializer_name() || name == vmSymbols::class_initializer_name()) { + legal = true; + } + } else if (_major_version < JAVA_1_5_VERSION) { + char* p; + p = skip_over_field_name(bytes, false, length); + legal = (p != NULL) && ((p - bytes) == (int)length); + } else { + // 4881221: relax the constraints based on JSR202 spec + legal = verify_unqualified_name(bytes, length, LegalMethod); + } + } + + if (!legal) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_ClassFormatError(), + "Illegal method name \"%s\" in class %s", bytes, + _class_name->as_C_string() + ); + return; + } +} + + +// Checks if signature is a legal field signature. +void ClassFileParser::verify_legal_field_signature(symbolHandle name, symbolHandle signature, TRAPS) { + if (!_need_verify) { return; } + + char buf[fixed_buffer_size]; + char* bytes = signature->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); + unsigned int length = signature->utf8_length(); + char* p = skip_over_field_signature(bytes, false, length, CHECK); + + if (p == NULL || (p - bytes) != (int)length) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_ClassFormatError(), + "Field \"%s\" in class %s has illegal signature \"%s\"", + name->as_C_string(), _class_name->as_C_string(), bytes + ); + return; + } +} + +// Checks if signature is a legal method signature. +// Returns number of parameters +int ClassFileParser::verify_legal_method_signature(symbolHandle name, symbolHandle signature, TRAPS) { + if (!_need_verify) { + // make sure caller's args_size will be less than 0 even for non-static + // method so it will be recomputed in compute_size_of_parameters(). + return -2; + } + + unsigned int args_size = 0; + char buf[fixed_buffer_size]; + char* p = signature->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); + unsigned int length = signature->utf8_length(); + char* nextp; + + // The first character must be a '(' + if ((length > 0) && (*p++ == JVM_SIGNATURE_FUNC)) { + length--; + // Skip over legal field signatures + nextp = skip_over_field_signature(p, false, length, CHECK_0); + while ((length > 0) && (nextp != NULL)) { + args_size++; + if (p[0] == 'J' || p[0] == 'D') { + args_size++; + } + length -= nextp - p; + p = nextp; + nextp = skip_over_field_signature(p, false, length, CHECK_0); + } + // The first non-signature thing better be a ')' + if ((length > 0) && (*p++ == JVM_SIGNATURE_ENDFUNC)) { + length--; + if (name->utf8_length() > 0 && name->byte_at(0) == '<') { + // All internal methods must return void + if ((length == 1) && (p[0] == JVM_SIGNATURE_VOID)) { + return args_size; + } + } else { + // Now we better just have a return value + nextp = skip_over_field_signature(p, true, length, CHECK_0); + if (nextp && ((int)length == (nextp - p))) { + return args_size; + } + } + } + } + // Report error + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_ClassFormatError(), + "Method \"%s\" in class %s has illegal signature \"%s\"", + name->as_C_string(), _class_name->as_C_string(), p + ); + return 0; +} + + +// Unqualified names may not contain the characters '.', ';', or '/'. +// Method names also may not contain the characters '<' or '>', unless or . +// Note that method names may not be or in this method. +// Because these names have been checked as special cases before calling this method +// in verify_legal_method_name. +bool ClassFileParser::verify_unqualified_name(char* name, unsigned int length, int type) { + jchar ch; + + for (char* p = name; p != name + length; ) { + ch = *p; + if (ch < 128) { + p++; + if (ch == '.' || ch == ';') { + return false; // do not permit '.' or ';' + } + if (type != LegalClass && ch == '/') { + return false; // do not permit '/' unless it's class name + } + if (type == LegalMethod && (ch == '<' || ch == '>')) { + return false; // do not permit '<' or '>' in method names + } + } else { + char* tmp_p = UTF8::next(p, &ch); + p = tmp_p; + } + } + return true; +} + + +// Take pointer to a string. Skip over the longest part of the string that could +// be taken as a fieldname. Allow '/' if slash_ok is true. +// Return a pointer to just past the fieldname. +// Return NULL if no fieldname at all was found, or in the case of slash_ok +// being true, we saw consecutive slashes (meaning we were looking for a +// qualified path but found something that was badly-formed). +char* ClassFileParser::skip_over_field_name(char* name, bool slash_ok, unsigned int length) { + char* p; + jchar ch; + jboolean last_is_slash = false; + jboolean not_first_ch = false; + + for (p = name; p != name + length; not_first_ch = true) { + char* old_p = p; + ch = *p; + if (ch < 128) { + p++; + // quick check for ascii + if ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch == '_' || ch == '$') || + (not_first_ch && ch >= '0' && ch <= '9')) { + last_is_slash = false; + continue; + } + if (slash_ok && ch == '/') { + if (last_is_slash) { + return NULL; // Don't permit consecutive slashes + } + last_is_slash = true; + continue; + } + } else { + jint unicode_ch; + char* tmp_p = UTF8::next_character(p, &unicode_ch); + p = tmp_p; + last_is_slash = false; + // Check if ch is Java identifier start or is Java identifier part + // 4672820: call java.lang.Character methods directly without generating separate tables. + EXCEPTION_MARK; + instanceKlassHandle klass (THREAD, SystemDictionary::char_klass()); + + // return value + JavaValue result(T_BOOLEAN); + // Set up the arguments to isJavaIdentifierStart and isJavaIdentifierPart + JavaCallArguments args; + args.push_int(unicode_ch); + + // public static boolean isJavaIdentifierStart(char ch); + JavaCalls::call_static(&result, + klass, + vmSymbolHandles::isJavaIdentifierStart_name(), + vmSymbolHandles::int_bool_signature(), + &args, + THREAD); + + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return 0; + } + if (result.get_jboolean()) { + continue; + } + + if (not_first_ch) { + // public static boolean isJavaIdentifierPart(char ch); + JavaCalls::call_static(&result, + klass, + vmSymbolHandles::isJavaIdentifierPart_name(), + vmSymbolHandles::int_bool_signature(), + &args, + THREAD); + + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return 0; + } + + if (result.get_jboolean()) { + continue; + } + } + } + return (not_first_ch) ? old_p : NULL; + } + return (not_first_ch) ? p : NULL; +} + + +// Take pointer to a string. Skip over the longest part of the string that could +// be taken as a field signature. Allow "void" if void_ok. +// Return a pointer to just past the signature. +// Return NULL if no legal signature is found. +char* ClassFileParser::skip_over_field_signature(char* signature, + bool void_ok, + unsigned int length, + TRAPS) { + unsigned int array_dim = 0; + while (length > 0) { + switch (signature[0]) { + case JVM_SIGNATURE_VOID: if (!void_ok) { return NULL; } + case JVM_SIGNATURE_BOOLEAN: + case JVM_SIGNATURE_BYTE: + case JVM_SIGNATURE_CHAR: + case JVM_SIGNATURE_SHORT: + case JVM_SIGNATURE_INT: + case JVM_SIGNATURE_FLOAT: + case JVM_SIGNATURE_LONG: + case JVM_SIGNATURE_DOUBLE: + return signature + 1; + case JVM_SIGNATURE_CLASS: { + if (_major_version < JAVA_1_5_VERSION) { + // Skip over the class name if one is there + char* p = skip_over_field_name(signature + 1, true, --length); + + // The next character better be a semicolon + if (p && (p - signature) > 1 && p[0] == ';') { + return p + 1; + } + } else { + // 4900761: For class version > 48, any unicode is allowed in class name. + length--; + signature++; + while (length > 0 && signature[0] != ';') { + if (signature[0] == '.') { + classfile_parse_error("Class name contains illegal character '.' in descriptor in class file %s", CHECK_0); + } + length--; + signature++; + } + if (signature[0] == ';') { return signature + 1; } + } + + return NULL; + } + case JVM_SIGNATURE_ARRAY: + array_dim++; + if (array_dim > 255) { + // 4277370: array descriptor is valid only if it represents 255 or fewer dimensions. + classfile_parse_error("Array type descriptor has more than 255 dimensions in class file %s", CHECK_0); + } + // The rest of what's there better be a legal signature + signature++; + length--; + void_ok = false; + break; + + default: + return NULL; + } + } + return NULL; +} diff --git a/hotspot/src/share/vm/classfile/classFileParser.hpp b/hotspot/src/share/vm/classfile/classFileParser.hpp new file mode 100644 index 00000000000..a29905fc4c1 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classFileParser.hpp @@ -0,0 +1,228 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Parser for for .class files +// +// The bytes describing the class file structure is read from a Stream object + +class ClassFileParser VALUE_OBJ_CLASS_SPEC { + private: + bool _need_verify; + bool _relax_verify; + u2 _major_version; + u2 _minor_version; + symbolHandle _class_name; + + bool _has_finalizer; + bool _has_empty_finalizer; + bool _has_vanilla_constructor; + + enum { fixed_buffer_size = 128 }; + u_char linenumbertable_buffer[fixed_buffer_size]; + + ClassFileStream* _stream; // Actual input stream + + enum { LegalClass, LegalField, LegalMethod }; // used to verify unqualified names + + // Accessors + ClassFileStream* stream() { return _stream; } + void set_stream(ClassFileStream* st) { _stream = st; } + + // Constant pool parsing + void parse_constant_pool_entries(constantPoolHandle cp, int length, TRAPS); + + constantPoolHandle parse_constant_pool(TRAPS); + + // Interface parsing + objArrayHandle parse_interfaces(constantPoolHandle cp, + int length, + Handle class_loader, + Handle protection_domain, + PerfTraceTime* vmtimer, + symbolHandle class_name, + TRAPS); + + // Field parsing + void parse_field_attributes(constantPoolHandle cp, u2 attributes_count, + bool is_static, u2 signature_index, + u2* constantvalue_index_addr, + bool* is_synthetic_addr, + u2* generic_signature_index_addr, + typeArrayHandle* field_annotations, TRAPS); + typeArrayHandle parse_fields(constantPoolHandle cp, bool is_interface, + struct FieldAllocationCount *fac, + objArrayHandle* fields_annotations, TRAPS); + + // Method parsing + methodHandle parse_method(constantPoolHandle cp, bool is_interface, + AccessFlags* promoted_flags, + typeArrayHandle* method_annotations, + typeArrayHandle* method_parameter_annotations, + typeArrayHandle* method_default_annotations, + TRAPS); + objArrayHandle parse_methods (constantPoolHandle cp, bool is_interface, + AccessFlags* promoted_flags, + bool* has_final_method, + objArrayOop* methods_annotations_oop, + objArrayOop* methods_parameter_annotations_oop, + objArrayOop* methods_default_annotations_oop, + TRAPS); + typeArrayHandle sort_methods (objArrayHandle methods, + objArrayHandle methods_annotations, + objArrayHandle methods_parameter_annotations, + objArrayHandle methods_default_annotations, + TRAPS); + typeArrayHandle parse_exception_table(u4 code_length, u4 exception_table_length, + constantPoolHandle cp, TRAPS); + void parse_linenumber_table( + u4 code_attribute_length, u4 code_length, + CompressedLineNumberWriteStream** write_stream, TRAPS); + u2* parse_localvariable_table(u4 code_length, u2 max_locals, u4 code_attribute_length, + constantPoolHandle cp, u2* localvariable_table_length, + bool isLVTT, TRAPS); + u2* parse_checked_exceptions(u2* checked_exceptions_length, u4 method_attribute_length, + constantPoolHandle cp, TRAPS); + void parse_type_array(u2 array_length, u4 code_length, u4* u1_index, u4* u2_index, + u1* u1_array, u2* u2_array, constantPoolHandle cp, TRAPS); + typeArrayOop parse_stackmap_table(u4 code_attribute_length, TRAPS); + + // Classfile attribute parsing + void parse_classfile_sourcefile_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS); + void parse_classfile_source_debug_extension_attribute(constantPoolHandle cp, + instanceKlassHandle k, int length, TRAPS); + u2 parse_classfile_inner_classes_attribute(constantPoolHandle cp, + instanceKlassHandle k, TRAPS); + void parse_classfile_attributes(constantPoolHandle cp, instanceKlassHandle k, TRAPS); + void parse_classfile_synthetic_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS); + void parse_classfile_signature_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS); + + // Annotations handling + typeArrayHandle assemble_annotations(u1* runtime_visible_annotations, + int runtime_visible_annotations_length, + u1* runtime_invisible_annotations, + int runtime_invisible_annotations_length, TRAPS); + + // Final setup + int compute_oop_map_size(instanceKlassHandle super, int nonstatic_oop_count, + int first_nonstatic_oop_offset); + void fill_oop_maps(instanceKlassHandle k, int nonstatic_oop_map_count, + u2* nonstatic_oop_offsets, u2* nonstatic_oop_length); + void set_precomputed_flags(instanceKlassHandle k); + objArrayHandle compute_transitive_interfaces(instanceKlassHandle super, + objArrayHandle local_ifs, TRAPS); + + // Special handling for certain classes. + // Add the "discovered" field to java.lang.ref.Reference if + // it does not exist. + void java_lang_ref_Reference_fix_pre(typeArrayHandle* fields_ptr, + constantPoolHandle cp, FieldAllocationCount *fac_ptr, TRAPS); + // Adjust the field allocation counts for java.lang.Class to add + // fake fields. + void java_lang_Class_fix_pre(objArrayHandle* methods_ptr, + FieldAllocationCount *fac_ptr, TRAPS); + // Adjust the next_nonstatic_oop_offset to place the fake fields + // before any Java fields. + void java_lang_Class_fix_post(int* next_nonstatic_oop_offset); + + // Format checker methods + void classfile_parse_error(const char* msg, TRAPS); + void classfile_parse_error(const char* msg, int index, TRAPS); + void classfile_parse_error(const char* msg, const char *name, TRAPS); + void classfile_parse_error(const char* msg, int index, const char *name, TRAPS); + inline void guarantee_property(bool b, const char* msg, TRAPS) { + if (!b) { classfile_parse_error(msg, CHECK); } + } + + inline void assert_property(bool b, const char* msg, TRAPS) { +#ifdef ASSERT + if (!b) { fatal(msg); } +#endif + } + + inline void check_property(bool property, const char* msg, int index, TRAPS) { + if (_need_verify) { + guarantee_property(property, msg, index, CHECK); + } else { + assert_property(property, msg, CHECK); + } + } + + inline void check_property(bool property, const char* msg, TRAPS) { + if (_need_verify) { + guarantee_property(property, msg, CHECK); + } else { + assert_property(property, msg, CHECK); + } + } + + inline void guarantee_property(bool b, const char* msg, int index, TRAPS) { + if (!b) { classfile_parse_error(msg, index, CHECK); } + } + inline void guarantee_property(bool b, const char* msg, const char *name, TRAPS) { + if (!b) { classfile_parse_error(msg, name, CHECK); } + } + inline void guarantee_property(bool b, const char* msg, int index, const char *name, TRAPS) { + if (!b) { classfile_parse_error(msg, index, name, CHECK); } + } + + bool is_supported_version(u2 major, u2 minor); + bool has_illegal_visibility(jint flags); + + void verify_constantvalue(int constantvalue_index, int signature_index, constantPoolHandle cp, TRAPS); + void verify_legal_utf8(const unsigned char* buffer, int length, TRAPS); + void verify_legal_class_name(symbolHandle name, TRAPS); + void verify_legal_field_name(symbolHandle name, TRAPS); + void verify_legal_method_name(symbolHandle name, TRAPS); + void verify_legal_field_signature(symbolHandle fieldname, symbolHandle signature, TRAPS); + int verify_legal_method_signature(symbolHandle methodname, symbolHandle signature, TRAPS); + void verify_legal_class_modifiers(jint flags, TRAPS); + void verify_legal_field_modifiers(jint flags, bool is_interface, TRAPS); + void verify_legal_method_modifiers(jint flags, bool is_interface, symbolHandle name, TRAPS); + bool verify_unqualified_name(char* name, unsigned int length, int type); + char* skip_over_field_name(char* name, bool slash_ok, unsigned int length); + char* skip_over_field_signature(char* signature, bool void_ok, unsigned int length, TRAPS); + + public: + // Constructor + ClassFileParser(ClassFileStream* st) { set_stream(st); } + + // Parse .class file and return new klassOop. The klassOop is not hooked up + // to the system dictionary or any other structures, so a .class file can + // be loaded several times if desired. + // The system dictionary hookup is done by the caller. + // + // "parsed_name" is updated by this method, and is the name found + // while parsing the stream. + instanceKlassHandle parseClassFile(symbolHandle name, + Handle class_loader, + Handle protection_domain, + symbolHandle& parsed_name, + TRAPS); + + // Verifier checks + static void check_super_class_access(instanceKlassHandle this_klass, TRAPS); + static void check_super_interface_access(instanceKlassHandle this_klass, TRAPS); + static void check_final_method_override(instanceKlassHandle this_klass, TRAPS); + static void check_illegal_static_method(instanceKlassHandle this_klass, TRAPS); +}; diff --git a/hotspot/src/share/vm/classfile/classFileStream.cpp b/hotspot/src/share/vm/classfile/classFileStream.cpp new file mode 100644 index 00000000000..e4ec5810fbc --- /dev/null +++ b/hotspot/src/share/vm/classfile/classFileStream.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_classFileStream.cpp.incl" + +void ClassFileStream::truncated_file_error(TRAPS) { + THROW_MSG(vmSymbols::java_lang_ClassFormatError(), "Truncated class file"); +} + +ClassFileStream::ClassFileStream(u1* buffer, int length, char* source) { + _buffer_start = buffer; + _buffer_end = buffer + length; + _current = buffer; + _source = source; + _need_verify = false; +} + +u1 ClassFileStream::get_u1(TRAPS) { + if (_need_verify) { + guarantee_more(1, CHECK_0); + } else { + assert(1 <= _buffer_end - _current, "buffer overflow"); + } + return *_current++; +} + +u2 ClassFileStream::get_u2(TRAPS) { + if (_need_verify) { + guarantee_more(2, CHECK_0); + } else { + assert(2 <= _buffer_end - _current, "buffer overflow"); + } + u1* tmp = _current; + _current += 2; + return Bytes::get_Java_u2(tmp); +} + +u4 ClassFileStream::get_u4(TRAPS) { + if (_need_verify) { + guarantee_more(4, CHECK_0); + } else { + assert(4 <= _buffer_end - _current, "buffer overflow"); + } + u1* tmp = _current; + _current += 4; + return Bytes::get_Java_u4(tmp); +} + +u8 ClassFileStream::get_u8(TRAPS) { + if (_need_verify) { + guarantee_more(8, CHECK_0); + } else { + assert(8 <= _buffer_end - _current, "buffer overflow"); + } + u1* tmp = _current; + _current += 8; + return Bytes::get_Java_u8(tmp); +} + +void ClassFileStream::skip_u1(int length, TRAPS) { + if (_need_verify) { + guarantee_more(length, CHECK); + } + _current += length; +} + +void ClassFileStream::skip_u2(int length, TRAPS) { + if (_need_verify) { + guarantee_more(length * 2, CHECK); + } + _current += length * 2; +} diff --git a/hotspot/src/share/vm/classfile/classFileStream.hpp b/hotspot/src/share/vm/classfile/classFileStream.hpp new file mode 100644 index 00000000000..97af0378646 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classFileStream.hpp @@ -0,0 +1,118 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Input stream for reading .class file +// +// The entire input stream is present in a buffer allocated by the caller. +// The caller is responsible for deallocating the buffer and for using +// ResourceMarks appropriately when constructing streams. + +class ClassFileStream: public ResourceObj { + private: + u1* _buffer_start; // Buffer bottom + u1* _buffer_end; // Buffer top (one past last element) + u1* _current; // Current buffer position + char* _source; // Source of stream (directory name, ZIP/JAR archive name) + bool _need_verify; // True if verification is on for the class file + + void truncated_file_error(TRAPS); + public: + // Constructor + ClassFileStream(u1* buffer, int length, char* source); + + // Buffer access + u1* buffer() const { return _buffer_start; } + int length() const { return _buffer_end - _buffer_start; } + u1* current() const { return _current; } + void set_current(u1* pos) { _current = pos; } + char* source() const { return _source; } + void set_verify(bool flag) { _need_verify = flag; } + + void check_truncated_file(bool b, TRAPS) { + if (b) { + truncated_file_error(THREAD); + } + } + + void guarantee_more(int size, TRAPS) { + size_t remaining = (size_t)(_buffer_end - _current); + unsigned int usize = (unsigned int)size; + check_truncated_file(usize > remaining, CHECK); + } + + // Read u1 from stream + u1 get_u1(TRAPS); + u1 get_u1_fast() { + return *_current++; + } + + // Read u2 from stream + u2 get_u2(TRAPS); + u2 get_u2_fast() { + u2 res = Bytes::get_Java_u2(_current); + _current += 2; + return res; + } + + // Read u4 from stream + u4 get_u4(TRAPS); + u4 get_u4_fast() { + u4 res = Bytes::get_Java_u4(_current); + _current += 4; + return res; + } + + // Read u8 from stream + u8 get_u8(TRAPS); + u8 get_u8_fast() { + u8 res = Bytes::get_Java_u8(_current); + _current += 8; + return res; + } + + // Get direct pointer into stream at current position. + // Returns NULL if length elements are not remaining. The caller is + // responsible for calling skip below if buffer contents is used. + u1* get_u1_buffer() { + return _current; + } + + u2* get_u2_buffer() { + return (u2*) _current; + } + + // Skip length u1 or u2 elements from stream + void skip_u1(int length, TRAPS); + void skip_u1_fast(int length) { + _current += length; + } + + void skip_u2(int length, TRAPS); + void skip_u2_fast(int length) { + _current += 2 * length; + } + + // Tells whether eos is reached + bool at_eos() const { return _current == _buffer_end; } +}; diff --git a/hotspot/src/share/vm/classfile/classLoader.cpp b/hotspot/src/share/vm/classfile/classLoader.cpp new file mode 100644 index 00000000000..2e93cd045c2 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classLoader.cpp @@ -0,0 +1,1274 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_classLoader.cpp.incl" + + +// Entry points in zip.dll for loading zip/jar file entries + +typedef void * * (JNICALL *ZipOpen_t)(const char *name, char **pmsg); +typedef void (JNICALL *ZipClose_t)(jzfile *zip); +typedef jzentry* (JNICALL *FindEntry_t)(jzfile *zip, const char *name, jint *sizeP, jint *nameLen); +typedef jboolean (JNICALL *ReadEntry_t)(jzfile *zip, jzentry *entry, unsigned char *buf, char *namebuf); +typedef jboolean (JNICALL *ReadMappedEntry_t)(jzfile *zip, jzentry *entry, unsigned char **buf, char *namebuf); +typedef jzentry* (JNICALL *GetNextEntry_t)(jzfile *zip, jint n); + +static ZipOpen_t ZipOpen = NULL; +static ZipClose_t ZipClose = NULL; +static FindEntry_t FindEntry = NULL; +static ReadEntry_t ReadEntry = NULL; +static ReadMappedEntry_t ReadMappedEntry = NULL; +static GetNextEntry_t GetNextEntry = NULL; +static canonicalize_fn_t CanonicalizeEntry = NULL; + +// Globals + +PerfCounter* ClassLoader::_perf_accumulated_time = NULL; +PerfCounter* ClassLoader::_perf_classes_inited = NULL; +PerfCounter* ClassLoader::_perf_class_init_time = NULL; +PerfCounter* ClassLoader::_perf_class_verify_time = NULL; +PerfCounter* ClassLoader::_perf_classes_linked = NULL; +PerfCounter* ClassLoader::_perf_class_link_time = NULL; +PerfCounter* ClassLoader::_sync_systemLoaderLockContentionRate = NULL; +PerfCounter* ClassLoader::_sync_nonSystemLoaderLockContentionRate = NULL; +PerfCounter* ClassLoader::_sync_JVMFindLoadedClassLockFreeCounter = NULL; +PerfCounter* ClassLoader::_sync_JVMDefineClassLockFreeCounter = NULL; +PerfCounter* ClassLoader::_sync_JNIDefineClassLockFreeCounter = NULL; +PerfCounter* ClassLoader::_unsafe_defineClassCallCounter = NULL; +PerfCounter* ClassLoader::_isUnsyncloadClass = NULL; +PerfCounter* ClassLoader::_load_instance_class_failCounter = NULL; + +ClassPathEntry* ClassLoader::_first_entry = NULL; +ClassPathEntry* ClassLoader::_last_entry = NULL; +PackageHashtable* ClassLoader::_package_hash_table = NULL; + +// helper routines +bool string_starts_with(const char* str, const char* str_to_find) { + size_t str_len = strlen(str); + size_t str_to_find_len = strlen(str_to_find); + if (str_to_find_len > str_len) { + return false; + } + return (strncmp(str, str_to_find, str_to_find_len) == 0); +} + +bool string_ends_with(const char* str, const char* str_to_find) { + size_t str_len = strlen(str); + size_t str_to_find_len = strlen(str_to_find); + if (str_to_find_len > str_len) { + return false; + } + return (strncmp(str + (str_len - str_to_find_len), str_to_find, str_to_find_len) == 0); +} + + +MetaIndex::MetaIndex(char** meta_package_names, int num_meta_package_names) { + if (num_meta_package_names == 0) { + _meta_package_names = NULL; + _num_meta_package_names = 0; + } else { + _meta_package_names = NEW_C_HEAP_ARRAY(char*, num_meta_package_names); + _num_meta_package_names = num_meta_package_names; + memcpy(_meta_package_names, meta_package_names, num_meta_package_names * sizeof(char*)); + } +} + + +MetaIndex::~MetaIndex() { + FREE_C_HEAP_ARRAY(char*, _meta_package_names); +} + + +bool MetaIndex::may_contain(const char* class_name) { + if ( _num_meta_package_names == 0) { + return false; + } + size_t class_name_len = strlen(class_name); + for (int i = 0; i < _num_meta_package_names; i++) { + char* pkg = _meta_package_names[i]; + size_t pkg_len = strlen(pkg); + size_t min_len = MIN2(class_name_len, pkg_len); + if (!strncmp(class_name, pkg, min_len)) { + return true; + } + } + return false; +} + + +ClassPathEntry::ClassPathEntry() { + set_next(NULL); +} + + +bool ClassPathEntry::is_lazy() { + return false; +} + +ClassPathDirEntry::ClassPathDirEntry(char* dir) : ClassPathEntry() { + _dir = NEW_C_HEAP_ARRAY(char, strlen(dir)+1); + strcpy(_dir, dir); +} + + +ClassFileStream* ClassPathDirEntry::open_stream(const char* name) { + // construct full path name + char path[JVM_MAXPATHLEN]; + if (jio_snprintf(path, sizeof(path), "%s%s%s", _dir, os::file_separator(), name) == -1) { + return NULL; + } + // check if file exists + struct stat st; + if (os::stat(path, &st) == 0) { + // found file, open it + int file_handle = hpi::open(path, 0, 0); + if (file_handle != -1) { + // read contents into resource array + u1* buffer = NEW_RESOURCE_ARRAY(u1, st.st_size); + size_t num_read = os::read(file_handle, (char*) buffer, st.st_size); + // close file + hpi::close(file_handle); + // construct ClassFileStream + if (num_read == (size_t)st.st_size) { + return new ClassFileStream(buffer, st.st_size, _dir); // Resource allocated + } + } + } + return NULL; +} + + +ClassPathZipEntry::ClassPathZipEntry(jzfile* zip, const char* zip_name) : ClassPathEntry() { + _zip = zip; + _zip_name = NEW_C_HEAP_ARRAY(char, strlen(zip_name)+1); + strcpy(_zip_name, zip_name); +} + +ClassPathZipEntry::~ClassPathZipEntry() { + if (ZipClose != NULL) { + (*ZipClose)(_zip); + } + FREE_C_HEAP_ARRAY(char, _zip_name); +} + +ClassFileStream* ClassPathZipEntry::open_stream(const char* name) { + // enable call to C land + JavaThread* thread = JavaThread::current(); + ThreadToNativeFromVM ttn(thread); + // check whether zip archive contains name + jint filesize, name_len; + jzentry* entry = (*FindEntry)(_zip, name, &filesize, &name_len); + if (entry == NULL) return NULL; + u1* buffer; + char name_buf[128]; + char* filename; + if (name_len < 128) { + filename = name_buf; + } else { + filename = NEW_RESOURCE_ARRAY(char, name_len + 1); + } + + // file found, get pointer to class in mmaped jar file. + if (ReadMappedEntry == NULL || + !(*ReadMappedEntry)(_zip, entry, &buffer, filename)) { + // mmaped access not available, perhaps due to compression, + // read contents into resource array + buffer = NEW_RESOURCE_ARRAY(u1, filesize); + if (!(*ReadEntry)(_zip, entry, buffer, filename)) return NULL; + } + // return result + return new ClassFileStream(buffer, filesize, _zip_name); // Resource allocated +} + +// invoke function for each entry in the zip file +void ClassPathZipEntry::contents_do(void f(const char* name, void* context), void* context) { + JavaThread* thread = JavaThread::current(); + HandleMark handle_mark(thread); + ThreadToNativeFromVM ttn(thread); + for (int n = 0; ; n++) { + jzentry * ze = ((*GetNextEntry)(_zip, n)); + if (ze == NULL) break; + (*f)(ze->name, context); + } +} + +LazyClassPathEntry::LazyClassPathEntry(char* path, struct stat st) : ClassPathEntry() { + _path = strdup(path); + _st = st; + _meta_index = NULL; + _resolved_entry = NULL; +} + +bool LazyClassPathEntry::is_jar_file() { + return ((_st.st_mode & S_IFREG) == S_IFREG); +} + +ClassPathEntry* LazyClassPathEntry::resolve_entry() { + if (_resolved_entry != NULL) { + return (ClassPathEntry*) _resolved_entry; + } + ClassPathEntry* new_entry = NULL; + ClassLoader::create_class_path_entry(_path, _st, &new_entry, false); + assert(new_entry != NULL, "earlier code should have caught this"); + { + ThreadCritical tc; + if (_resolved_entry == NULL) { + _resolved_entry = new_entry; + return new_entry; + } + } + assert(_resolved_entry != NULL, "bug in MT-safe resolution logic"); + delete new_entry; + return (ClassPathEntry*) _resolved_entry; +} + +ClassFileStream* LazyClassPathEntry::open_stream(const char* name) { + if (_meta_index != NULL && + !_meta_index->may_contain(name)) { + return NULL; + } + return resolve_entry()->open_stream(name); +} + +bool LazyClassPathEntry::is_lazy() { + return true; +} + +static void print_meta_index(LazyClassPathEntry* entry, + GrowableArray& meta_packages) { + tty->print("[Meta index for %s=", entry->name()); + for (int i = 0; i < meta_packages.length(); i++) { + if (i > 0) tty->print(" "); + tty->print(meta_packages.at(i)); + } + tty->print_cr("]"); +} + + +void ClassLoader::setup_meta_index() { + // Set up meta index which allows us to open boot jars lazily if + // class data sharing is enabled + const char* known_version = "% VERSION 2"; + char* meta_index_path = Arguments::get_meta_index_path(); + char* meta_index_dir = Arguments::get_meta_index_dir(); + FILE* file = fopen(meta_index_path, "r"); + int line_no = 0; + if (file != NULL) { + ResourceMark rm; + LazyClassPathEntry* cur_entry = NULL; + GrowableArray boot_class_path_packages(10); + char package_name[256]; + bool skipCurrentJar = false; + while (fgets(package_name, sizeof(package_name), file) != NULL) { + ++line_no; + // Remove trailing newline + package_name[strlen(package_name) - 1] = '\0'; + switch(package_name[0]) { + case '%': + { + if ((line_no == 1) && (strcmp(package_name, known_version) != 0)) { + if (TraceClassLoading && Verbose) { + tty->print("[Unsupported meta index version]"); + } + fclose(file); + return; + } + } + + // These directives indicate jar files which contain only + // classes, only non-classfile resources, or a combination of + // the two. See src/share/classes/sun/misc/MetaIndex.java and + // make/tools/MetaIndex/BuildMetaIndex.java in the J2SE + // workspace. + case '#': + case '!': + case '@': + { + // Hand off current packages to current lazy entry (if any) + if ((cur_entry != NULL) && + (boot_class_path_packages.length() > 0)) { + if (TraceClassLoading && Verbose) { + print_meta_index(cur_entry, boot_class_path_packages); + } + MetaIndex* index = new MetaIndex(boot_class_path_packages.adr_at(0), + boot_class_path_packages.length()); + cur_entry->set_meta_index(index); + } + cur_entry = NULL; + boot_class_path_packages.clear(); + + // Find lazy entry corresponding to this jar file + for (ClassPathEntry* entry = _first_entry; entry != NULL; entry = entry->next()) { + if (entry->is_lazy() && + string_starts_with(entry->name(), meta_index_dir) && + string_ends_with(entry->name(), &package_name[2])) { + cur_entry = (LazyClassPathEntry*) entry; + break; + } + } + + // If the first character is '@', it indicates the following jar + // file is a resource only jar file in which case, we should skip + // reading the subsequent entries since the resource loading is + // totally handled by J2SE side. + if (package_name[0] == '@') { + if (cur_entry != NULL) { + cur_entry->set_meta_index(new MetaIndex(NULL, 0)); + } + cur_entry = NULL; + skipCurrentJar = true; + } else { + skipCurrentJar = false; + } + + break; + } + + default: + { + if (!skipCurrentJar && cur_entry != NULL) { + char* new_name = strdup(package_name); + boot_class_path_packages.append(new_name); + } + } + } + } + // Hand off current packages to current lazy entry (if any) + if ((cur_entry != NULL) && + (boot_class_path_packages.length() > 0)) { + if (TraceClassLoading && Verbose) { + print_meta_index(cur_entry, boot_class_path_packages); + } + MetaIndex* index = new MetaIndex(boot_class_path_packages.adr_at(0), + boot_class_path_packages.length()); + cur_entry->set_meta_index(index); + } + fclose(file); + } +} + +void ClassLoader::setup_bootstrap_search_path() { + assert(_first_entry == NULL, "should not setup bootstrap class search path twice"); + char* sys_class_path = os::strdup(Arguments::get_sysclasspath()); + if (TraceClassLoading && Verbose) { + tty->print_cr("[Bootstrap loader class path=%s]", sys_class_path); + } + + int len = (int)strlen(sys_class_path); + int end = 0; + + // Iterate over class path entries + for (int start = 0; start < len; start = end) { + while (sys_class_path[end] && sys_class_path[end] != os::path_separator()[0]) { + end++; + } + char* path = NEW_C_HEAP_ARRAY(char, end-start+1); + strncpy(path, &sys_class_path[start], end-start); + path[end-start] = '\0'; + update_class_path_entry_list(path, false); + FREE_C_HEAP_ARRAY(char, path); + while (sys_class_path[end] == os::path_separator()[0]) { + end++; + } + } +} + +void ClassLoader::create_class_path_entry(char *path, struct stat st, ClassPathEntry **new_entry, bool lazy) { + JavaThread* thread = JavaThread::current(); + if (lazy) { + *new_entry = new LazyClassPathEntry(path, st); + return; + } + if ((st.st_mode & S_IFREG) == S_IFREG) { + // Regular file, should be a zip file + // Canonicalized filename + char canonical_path[JVM_MAXPATHLEN]; + if (!get_canonical_path(path, canonical_path, JVM_MAXPATHLEN)) { + // This matches the classic VM + EXCEPTION_MARK; + THROW_MSG(vmSymbols::java_io_IOException(), "Bad pathname"); + } + char* error_msg = NULL; + jzfile* zip; + { + // enable call to C land + ThreadToNativeFromVM ttn(thread); + HandleMark hm(thread); + zip = (*ZipOpen)(canonical_path, &error_msg); + } + if (zip != NULL && error_msg == NULL) { + *new_entry = new ClassPathZipEntry(zip, path); + if (TraceClassLoading) { + tty->print_cr("[Opened %s]", path); + } + } else { + ResourceMark rm(thread); + char *msg; + if (error_msg == NULL) { + msg = NEW_RESOURCE_ARRAY(char, strlen(path) + 128); ; + jio_snprintf(msg, strlen(path) + 127, "error in opening JAR file %s", path); + } else { + int len = (int)(strlen(path) + strlen(error_msg) + 128); + msg = NEW_RESOURCE_ARRAY(char, len); ; + jio_snprintf(msg, len - 1, "error in opening JAR file <%s> %s", error_msg, path); + } + EXCEPTION_MARK; + THROW_MSG(vmSymbols::java_lang_ClassNotFoundException(), msg); + } + } else { + // Directory + *new_entry = new ClassPathDirEntry(path); + if (TraceClassLoading) { + tty->print_cr("[Path %s]", path); + } + } +} + + +// Create a class path zip entry for a given path (return NULL if not found +// or zip/JAR file cannot be opened) +ClassPathZipEntry* ClassLoader::create_class_path_zip_entry(const char *path) { + // check for a regular file + struct stat st; + if (os::stat(path, &st) == 0) { + if ((st.st_mode & S_IFREG) == S_IFREG) { + char orig_path[JVM_MAXPATHLEN]; + char canonical_path[JVM_MAXPATHLEN]; + + strcpy(orig_path, path); + if (get_canonical_path(orig_path, canonical_path, JVM_MAXPATHLEN)) { + char* error_msg = NULL; + jzfile* zip; + { + // enable call to C land + JavaThread* thread = JavaThread::current(); + ThreadToNativeFromVM ttn(thread); + HandleMark hm(thread); + zip = (*ZipOpen)(canonical_path, &error_msg); + } + if (zip != NULL && error_msg == NULL) { + // create using canonical path + return new ClassPathZipEntry(zip, canonical_path); + } + } + } + } + return NULL; +} + +// returns true if entry already on class path +bool ClassLoader::contains_entry(ClassPathEntry *entry) { + ClassPathEntry* e = _first_entry; + while (e != NULL) { + // assume zip entries have been canonicalized + if (strcmp(entry->name(), e->name()) == 0) { + return true; + } + e = e->next(); + } + return false; +} + +void ClassLoader::add_to_list(ClassPathEntry *new_entry) { + if (new_entry != NULL) { + if (_last_entry == NULL) { + _first_entry = _last_entry = new_entry; + } else { + _last_entry->set_next(new_entry); + _last_entry = new_entry; + } + } +} + +void ClassLoader::update_class_path_entry_list(const char *path, + bool check_for_duplicates) { + struct stat st; + if (os::stat((char *)path, &st) == 0) { + // File or directory found + ClassPathEntry* new_entry = NULL; + create_class_path_entry((char *)path, st, &new_entry, LazyBootClassLoader); + // The kernel VM adds dynamically to the end of the classloader path and + // doesn't reorder the bootclasspath which would break java.lang.Package + // (see PackageInfo). + // Add new entry to linked list + if (!check_for_duplicates || !contains_entry(new_entry)) { + add_to_list(new_entry); + } + } +} + +void ClassLoader::print_bootclasspath() { + ClassPathEntry* e = _first_entry; + tty->print("[bootclasspath= "); + while (e != NULL) { + tty->print("%s ;", e->name()); + e = e->next(); + } + tty->print_cr("]"); +} + +void ClassLoader::load_zip_library() { + assert(ZipOpen == NULL, "should not load zip library twice"); + // First make sure native library is loaded + os::native_java_library(); + // Load zip library + char path[JVM_MAXPATHLEN]; + char ebuf[1024]; + hpi::dll_build_name(path, sizeof(path), Arguments::get_dll_dir(), "zip"); + void* handle = hpi::dll_load(path, ebuf, sizeof ebuf); + if (handle == NULL) { + vm_exit_during_initialization("Unable to load ZIP library", path); + } + // Lookup zip entry points + ZipOpen = CAST_TO_FN_PTR(ZipOpen_t, hpi::dll_lookup(handle, "ZIP_Open")); + ZipClose = CAST_TO_FN_PTR(ZipClose_t, hpi::dll_lookup(handle, "ZIP_Close")); + FindEntry = CAST_TO_FN_PTR(FindEntry_t, hpi::dll_lookup(handle, "ZIP_FindEntry")); + ReadEntry = CAST_TO_FN_PTR(ReadEntry_t, hpi::dll_lookup(handle, "ZIP_ReadEntry")); + ReadMappedEntry = CAST_TO_FN_PTR(ReadMappedEntry_t, hpi::dll_lookup(handle, "ZIP_ReadMappedEntry")); + GetNextEntry = CAST_TO_FN_PTR(GetNextEntry_t, hpi::dll_lookup(handle, "ZIP_GetNextEntry")); + + // ZIP_Close is not exported on Windows in JDK5.0 so don't abort if ZIP_Close is NULL + if (ZipOpen == NULL || FindEntry == NULL || ReadEntry == NULL || GetNextEntry == NULL) { + vm_exit_during_initialization("Corrupted ZIP library", path); + } + + // Lookup canonicalize entry in libjava.dll + void *javalib_handle = os::native_java_library(); + CanonicalizeEntry = CAST_TO_FN_PTR(canonicalize_fn_t, hpi::dll_lookup(javalib_handle, "Canonicalize")); + // This lookup only works on 1.3. Do not check for non-null here +} + +// PackageInfo data exists in order to support the java.lang.Package +// class. A Package object provides information about a java package +// (version, vendor, etc.) which originates in the manifest of the jar +// file supplying the package. For application classes, the ClassLoader +// object takes care of this. + +// For system (boot) classes, the Java code in the Package class needs +// to be able to identify which source jar file contained the boot +// class, so that it can extract the manifest from it. This table +// identifies java packages with jar files in the boot classpath. + +// Because the boot classpath cannot change, the classpath index is +// sufficient to identify the source jar file or directory. (Since +// directories have no manifests, the directory name is not required, +// but is available.) + +// When using sharing -- the pathnames of entries in the boot classpath +// may not be the same at runtime as they were when the archive was +// created (NFS, Samba, etc.). The actual files and directories named +// in the classpath must be the same files, in the same order, even +// though the exact name is not the same. + +class PackageInfo: public BasicHashtableEntry { +public: + const char* _pkgname; // Package name + int _classpath_index; // Index of directory or JAR file loaded from + + PackageInfo* next() { + return (PackageInfo*)BasicHashtableEntry::next(); + } + + const char* pkgname() { return _pkgname; } + void set_pkgname(char* pkgname) { _pkgname = pkgname; } + + const char* filename() { + return ClassLoader::classpath_entry(_classpath_index)->name(); + } + + void set_index(int index) { + _classpath_index = index; + } +}; + + +class PackageHashtable : public BasicHashtable { +private: + inline unsigned int compute_hash(const char *s, int n) { + unsigned int val = 0; + while (--n >= 0) { + val = *s++ + 31 * val; + } + return val; + } + + PackageInfo* bucket(int index) { + return (PackageInfo*)BasicHashtable::bucket(index); + } + + PackageInfo* get_entry(int index, unsigned int hash, + const char* pkgname, size_t n) { + for (PackageInfo* pp = bucket(index); pp != NULL; pp = pp->next()) { + if (pp->hash() == hash && + strncmp(pkgname, pp->pkgname(), n) == 0 && + pp->pkgname()[n] == '\0') { + return pp; + } + } + return NULL; + } + +public: + PackageHashtable(int table_size) + : BasicHashtable(table_size, sizeof(PackageInfo)) {} + + PackageHashtable(int table_size, HashtableBucket* t, int number_of_entries) + : BasicHashtable(table_size, sizeof(PackageInfo), t, number_of_entries) {} + + PackageInfo* get_entry(const char* pkgname, int n) { + unsigned int hash = compute_hash(pkgname, n); + return get_entry(hash_to_index(hash), hash, pkgname, n); + } + + PackageInfo* new_entry(char* pkgname, int n) { + unsigned int hash = compute_hash(pkgname, n); + PackageInfo* pp; + pp = (PackageInfo*)BasicHashtable::new_entry(hash); + pp->set_pkgname(pkgname); + return pp; + } + + void add_entry(PackageInfo* pp) { + int index = hash_to_index(pp->hash()); + BasicHashtable::add_entry(index, pp); + } + + void copy_pkgnames(const char** packages) { + int n = 0; + for (int i = 0; i < table_size(); ++i) { + for (PackageInfo* pp = bucket(i); pp != NULL; pp = pp->next()) { + packages[n++] = pp->pkgname(); + } + } + assert(n == number_of_entries(), "just checking"); + } + + void copy_table(char** top, char* end, PackageHashtable* table); +}; + + +void PackageHashtable::copy_table(char** top, char* end, + PackageHashtable* table) { + // Copy (relocate) the table to the shared space. + BasicHashtable::copy_table(top, end); + + // Calculate the space needed for the package name strings. + int i; + int n = 0; + for (i = 0; i < table_size(); ++i) { + for (PackageInfo* pp = table->bucket(i); + pp != NULL; + pp = pp->next()) { + n += (int)(strlen(pp->pkgname()) + 1); + } + } + if (*top + n + sizeof(intptr_t) >= end) { + warning("\nThe shared miscellaneous data space is not large " + "enough to \npreload requested classes. Use " + "-XX:SharedMiscDataSize= to increase \nthe initial " + "size of the miscellaneous data space.\n"); + exit(2); + } + + // Copy the table data (the strings) to the shared space. + n = align_size_up(n, sizeof(HeapWord)); + *(intptr_t*)(*top) = n; + *top += sizeof(intptr_t); + + for (i = 0; i < table_size(); ++i) { + for (PackageInfo* pp = table->bucket(i); + pp != NULL; + pp = pp->next()) { + int n1 = (int)(strlen(pp->pkgname()) + 1); + pp->set_pkgname((char*)memcpy(*top, pp->pkgname(), n1)); + *top += n1; + } + } + *top = (char*)align_size_up((intptr_t)*top, sizeof(HeapWord)); +} + + +void ClassLoader::copy_package_info_buckets(char** top, char* end) { + _package_hash_table->copy_buckets(top, end); +} + +void ClassLoader::copy_package_info_table(char** top, char* end) { + _package_hash_table->copy_table(top, end, _package_hash_table); +} + + +PackageInfo* ClassLoader::lookup_package(const char *pkgname) { + const char *cp = strrchr(pkgname, '/'); + if (cp != NULL) { + // Package prefix found + int n = cp - pkgname + 1; + return _package_hash_table->get_entry(pkgname, n); + } + return NULL; +} + + +bool ClassLoader::add_package(const char *pkgname, int classpath_index, TRAPS) { + assert(pkgname != NULL, "just checking"); + // Bootstrap loader no longer holds system loader lock obj serializing + // load_instance_class and thereby add_package + { + MutexLocker ml(PackageTable_lock, THREAD); + // First check for previously loaded entry + PackageInfo* pp = lookup_package(pkgname); + if (pp != NULL) { + // Existing entry found, check source of package + pp->set_index(classpath_index); + return true; + } + + const char *cp = strrchr(pkgname, '/'); + if (cp != NULL) { + // Package prefix found + int n = cp - pkgname + 1; + + char* new_pkgname = NEW_C_HEAP_ARRAY(char, n + 1); + if (new_pkgname == NULL) { + return false; + } + + memcpy(new_pkgname, pkgname, n); + new_pkgname[n] = '\0'; + pp = _package_hash_table->new_entry(new_pkgname, n); + pp->set_index(classpath_index); + + // Insert into hash table + _package_hash_table->add_entry(pp); + } + return true; + } +} + + +oop ClassLoader::get_system_package(const char* name, TRAPS) { + PackageInfo* pp; + { + MutexLocker ml(PackageTable_lock, THREAD); + pp = lookup_package(name); + } + if (pp == NULL) { + return NULL; + } else { + Handle p = java_lang_String::create_from_str(pp->filename(), THREAD); + return p(); + } +} + + +objArrayOop ClassLoader::get_system_packages(TRAPS) { + ResourceMark rm(THREAD); + int nof_entries; + const char** packages; + { + MutexLocker ml(PackageTable_lock, THREAD); + // Allocate resource char* array containing package names + nof_entries = _package_hash_table->number_of_entries(); + if ((packages = NEW_RESOURCE_ARRAY(const char*, nof_entries)) == NULL) { + return NULL; + } + _package_hash_table->copy_pkgnames(packages); + } + // Allocate objArray and fill with java.lang.String + objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + nof_entries, CHECK_0); + objArrayHandle result(THREAD, r); + for (int i = 0; i < nof_entries; i++) { + Handle str = java_lang_String::create_from_str(packages[i], CHECK_0); + result->obj_at_put(i, str()); + } + + return result(); +} + + +instanceKlassHandle ClassLoader::load_classfile(symbolHandle h_name, TRAPS) { + VTuneClassLoadMarker clm; + ResourceMark rm(THREAD); + EventMark m("loading class " INTPTR_FORMAT, (address)h_name()); + ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion); + + stringStream st; + // st.print() uses too much stack space while handling a StackOverflowError + // st.print("%s.class", h_name->as_utf8()); + st.print_raw(h_name->as_utf8()); + st.print_raw(".class"); + char* name = st.as_string(); + + // Lookup stream for parsing .class file + ClassFileStream* stream = NULL; + int classpath_index = 0; + { + PerfTraceTime vmtimer(perf_accumulated_time()); + ClassPathEntry* e = _first_entry; + while (e != NULL) { + stream = e->open_stream(name); + if (stream != NULL) { + break; + } + e = e->next(); + ++classpath_index; + } + } + + instanceKlassHandle h(THREAD, klassOop(NULL)); + if (stream != NULL) { + + // class file found, parse it + ClassFileParser parser(stream); + Handle class_loader; + Handle protection_domain; + symbolHandle parsed_name; + instanceKlassHandle result = parser.parseClassFile(h_name, + class_loader, + protection_domain, + parsed_name, + CHECK_(h)); + + // add to package table + if (add_package(name, classpath_index, THREAD)) { + h = result; + } + } + + return h; +} + + +void ClassLoader::create_package_info_table(HashtableBucket *t, int length, + int number_of_entries) { + assert(_package_hash_table == NULL, "One package info table allowed."); + assert(length == package_hash_table_size * sizeof(HashtableBucket), + "bad shared package info size."); + _package_hash_table = new PackageHashtable(package_hash_table_size, t, + number_of_entries); +} + + +void ClassLoader::create_package_info_table() { + assert(_package_hash_table == NULL, "shouldn't have one yet"); + _package_hash_table = new PackageHashtable(package_hash_table_size); +} + + +// Initialize the class loader's access to methods in libzip. Parse and +// process the boot classpath into a list ClassPathEntry objects. Once +// this list has been created, it must not change order (see class PackageInfo) +// it can be appended to and is by jvmti and the kernel vm. + +void ClassLoader::initialize() { + assert(_package_hash_table == NULL, "should have been initialized by now."); + EXCEPTION_MARK; + + if (UsePerfData) { + // jvmstat performance counters + NEWPERFTICKCOUNTER(_perf_accumulated_time, SUN_CLS, "time"); + NEWPERFTICKCOUNTER(_perf_class_init_time, SUN_CLS, "classInitTime"); + NEWPERFTICKCOUNTER(_perf_class_verify_time, SUN_CLS, "classVerifyTime"); + NEWPERFTICKCOUNTER(_perf_class_link_time, SUN_CLS, "classLinkedTime"); + + NEWPERFEVENTCOUNTER(_perf_classes_inited, SUN_CLS, "initializedClasses"); + NEWPERFEVENTCOUNTER(_perf_classes_linked, SUN_CLS, "linkedClasses"); + + // The following performance counters are added for measuring the impact + // of the bug fix of 6365597. They are mainly focused on finding out + // the behavior of system & user-defined classloader lock, whether + // ClassLoader.loadClass/findClass is being called synchronized or not. + // Also two additional counters are created to see whether 'UnsyncloadClass' + // flag is being set or not and how many times load_instance_class call + // fails with linkageError etc. + NEWPERFEVENTCOUNTER(_sync_systemLoaderLockContentionRate, SUN_CLS, + "systemLoaderLockContentionRate"); + NEWPERFEVENTCOUNTER(_sync_nonSystemLoaderLockContentionRate, SUN_CLS, + "nonSystemLoaderLockContentionRate"); + NEWPERFEVENTCOUNTER(_sync_JVMFindLoadedClassLockFreeCounter, SUN_CLS, + "jvmFindLoadedClassNoLockCalls"); + NEWPERFEVENTCOUNTER(_sync_JVMDefineClassLockFreeCounter, SUN_CLS, + "jvmDefineClassNoLockCalls"); + + NEWPERFEVENTCOUNTER(_sync_JNIDefineClassLockFreeCounter, SUN_CLS, + "jniDefineClassNoLockCalls"); + + NEWPERFEVENTCOUNTER(_unsafe_defineClassCallCounter, SUN_CLS, + "unsafeDefineClassCalls"); + + NEWPERFEVENTCOUNTER(_isUnsyncloadClass, SUN_CLS, "isUnsyncloadClassSet"); + NEWPERFEVENTCOUNTER(_load_instance_class_failCounter, SUN_CLS, + "loadInstanceClassFailRate"); + + // increment the isUnsyncloadClass counter if UnsyncloadClass is set. + if (UnsyncloadClass) { + _isUnsyncloadClass->inc(); + } + } + + // lookup zip library entry points + load_zip_library(); + // initialize search path + setup_bootstrap_search_path(); + if (LazyBootClassLoader) { + // set up meta index which makes boot classpath initialization lazier + setup_meta_index(); + } +} + + +jlong ClassLoader::classloader_time_ms() { + return UsePerfData ? + Management::ticks_to_ms(_perf_accumulated_time->get_value()) : -1; +} + +jlong ClassLoader::class_init_count() { + return UsePerfData ? _perf_classes_inited->get_value() : -1; +} + +jlong ClassLoader::class_init_time_ms() { + return UsePerfData ? + Management::ticks_to_ms(_perf_class_init_time->get_value()) : -1; +} + +jlong ClassLoader::class_verify_time_ms() { + return UsePerfData ? + Management::ticks_to_ms(_perf_class_verify_time->get_value()) : -1; +} + +jlong ClassLoader::class_link_count() { + return UsePerfData ? _perf_classes_linked->get_value() : -1; +} + +jlong ClassLoader::class_link_time_ms() { + return UsePerfData ? + Management::ticks_to_ms(_perf_class_link_time->get_value()) : -1; +} + +int ClassLoader::compute_Object_vtable() { + // hardwired for JDK1.2 -- would need to duplicate class file parsing + // code to determine actual value from file + // Would be value '11' if finals were in vtable + int JDK_1_2_Object_vtable_size = 5; + return JDK_1_2_Object_vtable_size * vtableEntry::size(); +} + + +void classLoader_init() { + ClassLoader::initialize(); +} + + +bool ClassLoader::get_canonical_path(char* orig, char* out, int len) { + assert(orig != NULL && out != NULL && len > 0, "bad arguments"); + if (CanonicalizeEntry != NULL) { + JNIEnv* env = JavaThread::current()->jni_environment(); + if ((CanonicalizeEntry)(env, hpi::native_path(orig), out, len) < 0) { + return false; + } + } else { + // On JDK 1.2.2 the Canonicalize does not exist, so just do nothing + strncpy(out, orig, len); + out[len - 1] = '\0'; + } + return true; +} + +#ifndef PRODUCT + +void ClassLoader::verify() { + _package_hash_table->verify(); +} + + +// CompileTheWorld +// +// Iterates over all class path entries and forces compilation of all methods +// in all classes found. Currently, only zip/jar archives are searched. +// +// The classes are loaded by the Java level bootstrap class loader, and the +// initializer is called. If DelayCompilationDuringStartup is true (default), +// the interpreter will run the initialization code. Note that forcing +// initialization in this way could potentially lead to initialization order +// problems, in which case we could just force the initialization bit to be set. + + +// We need to iterate over the contents of a zip/jar file, so we replicate the +// jzcell and jzfile definitions from zip_util.h but rename jzfile to real_jzfile, +// since jzfile already has a void* definition. +// +// Note that this is only used in debug mode. +// +// HotSpot integration note: +// Matches zip_util.h 1.14 99/06/01 from jdk1.3 beta H build + + +// JDK 1.3 version +typedef struct real_jzentry13 { /* Zip file entry */ + char *name; /* entry name */ + jint time; /* modification time */ + jint size; /* size of uncompressed data */ + jint csize; /* size of compressed data (zero if uncompressed) */ + jint crc; /* crc of uncompressed data */ + char *comment; /* optional zip file comment */ + jbyte *extra; /* optional extra data */ + jint pos; /* position of LOC header (if negative) or data */ +} real_jzentry13; + +typedef struct real_jzfile13 { /* Zip file */ + char *name; /* zip file name */ + jint refs; /* number of active references */ + jint fd; /* open file descriptor */ + void *lock; /* read lock */ + char *comment; /* zip file comment */ + char *msg; /* zip error message */ + void *entries; /* array of hash cells */ + jint total; /* total number of entries */ + unsigned short *table; /* Hash chain heads: indexes into entries */ + jint tablelen; /* number of hash eads */ + real_jzfile13 *next; /* next zip file in search list */ + jzentry *cache; /* we cache the most recently freed jzentry */ + /* Information on metadata names in META-INF directory */ + char **metanames; /* array of meta names (may have null names) */ + jint metacount; /* number of slots in metanames array */ + /* If there are any per-entry comments, they are in the comments array */ + char **comments; +} real_jzfile13; + +// JDK 1.2 version +typedef struct real_jzentry12 { /* Zip file entry */ + char *name; /* entry name */ + jint time; /* modification time */ + jint size; /* size of uncompressed data */ + jint csize; /* size of compressed data (zero if uncompressed) */ + jint crc; /* crc of uncompressed data */ + char *comment; /* optional zip file comment */ + jbyte *extra; /* optional extra data */ + jint pos; /* position of LOC header (if negative) or data */ + struct real_jzentry12 *next; /* next entry in hash table */ +} real_jzentry12; + +typedef struct real_jzfile12 { /* Zip file */ + char *name; /* zip file name */ + jint refs; /* number of active references */ + jint fd; /* open file descriptor */ + void *lock; /* read lock */ + char *comment; /* zip file comment */ + char *msg; /* zip error message */ + real_jzentry12 *entries; /* array of zip entries */ + jint total; /* total number of entries */ + real_jzentry12 **table; /* hash table of entries */ + jint tablelen; /* number of buckets */ + jzfile *next; /* next zip file in search list */ +} real_jzfile12; + + +void ClassPathDirEntry::compile_the_world(Handle loader, TRAPS) { + // For now we only compile all methods in all classes in zip/jar files + tty->print_cr("CompileTheWorld : Skipped classes in %s", _dir); + tty->cr(); +} + + +bool ClassPathDirEntry::is_rt_jar() { + return false; +} + +void ClassPathZipEntry::compile_the_world(Handle loader, TRAPS) { + if (JDK_Version::is_jdk12x_version()) { + compile_the_world12(loader, THREAD); + } else { + compile_the_world13(loader, THREAD); + } + if (HAS_PENDING_EXCEPTION) { + if (PENDING_EXCEPTION->is_a(SystemDictionary::OutOfMemoryError_klass())) { + CLEAR_PENDING_EXCEPTION; + tty->print_cr("\nCompileTheWorld : Ran out of memory\n"); + size_t used = Universe::heap()->permanent_used(); + size_t capacity = Universe::heap()->permanent_capacity(); + tty->print_cr("Permanent generation used %dK of %dK", used/K, capacity/K); + tty->print_cr("Increase size by setting e.g. -XX:MaxPermSize=%dK\n", capacity*2/K); + } else { + tty->print_cr("\nCompileTheWorld : Unexpected exception occurred\n"); + } + } +} + +// Version that works for JDK 1.3.x +void ClassPathZipEntry::compile_the_world13(Handle loader, TRAPS) { + real_jzfile13* zip = (real_jzfile13*) _zip; + tty->print_cr("CompileTheWorld : Compiling all classes in %s", zip->name); + tty->cr(); + // Iterate over all entries in zip file + for (int n = 0; ; n++) { + real_jzentry13 * ze = (real_jzentry13 *)((*GetNextEntry)(_zip, n)); + if (ze == NULL) break; + ClassLoader::compile_the_world_in(ze->name, loader, CHECK); + } +} + + +// Version that works for JDK 1.2.x +void ClassPathZipEntry::compile_the_world12(Handle loader, TRAPS) { + real_jzfile12* zip = (real_jzfile12*) _zip; + tty->print_cr("CompileTheWorld : Compiling all classes in %s", zip->name); + tty->cr(); + // Iterate over all entries in zip file + for (int n = 0; ; n++) { + real_jzentry12 * ze = (real_jzentry12 *)((*GetNextEntry)(_zip, n)); + if (ze == NULL) break; + ClassLoader::compile_the_world_in(ze->name, loader, CHECK); + } +} + +bool ClassPathZipEntry::is_rt_jar() { + if (JDK_Version::is_jdk12x_version()) { + return is_rt_jar12(); + } else { + return is_rt_jar13(); + } +} + +// JDK 1.3 version +bool ClassPathZipEntry::is_rt_jar13() { + real_jzfile13* zip = (real_jzfile13*) _zip; + int len = (int)strlen(zip->name); + // Check whether zip name ends in "rt.jar" + // This will match other archives named rt.jar as well, but this is + // only used for debugging. + return (len >= 6) && (strcasecmp(zip->name + len - 6, "rt.jar") == 0); +} + +// JDK 1.2 version +bool ClassPathZipEntry::is_rt_jar12() { + real_jzfile12* zip = (real_jzfile12*) _zip; + int len = (int)strlen(zip->name); + // Check whether zip name ends in "rt.jar" + // This will match other archives named rt.jar as well, but this is + // only used for debugging. + return (len >= 6) && (strcasecmp(zip->name + len - 6, "rt.jar") == 0); +} + +void LazyClassPathEntry::compile_the_world(Handle loader, TRAPS) { + resolve_entry()->compile_the_world(loader, CHECK); +} + +bool LazyClassPathEntry::is_rt_jar() { + return resolve_entry()->is_rt_jar(); +} + +void ClassLoader::compile_the_world() { + EXCEPTION_MARK; + HandleMark hm(THREAD); + ResourceMark rm(THREAD); + // Make sure we don't run with background compilation + BackgroundCompilation = false; + // Find bootstrap loader + Handle system_class_loader (THREAD, SystemDictionary::java_system_loader()); + // Iterate over all bootstrap class path entries + ClassPathEntry* e = _first_entry; + while (e != NULL) { + // We stop at rt.jar, unless it is the first bootstrap path entry + if (e->is_rt_jar() && e != _first_entry) break; + e->compile_the_world(system_class_loader, CATCH); + e = e->next(); + } + tty->print_cr("CompileTheWorld : Done"); + { + // Print statistics as if before normal exit: + extern void print_statistics(); + print_statistics(); + } + vm_exit(0); +} + +int ClassLoader::_compile_the_world_counter = 0; + +void ClassLoader::compile_the_world_in(char* name, Handle loader, TRAPS) { + int len = (int)strlen(name); + if (len > 6 && strcmp(".class", name + len - 6) == 0) { + // We have a .class file + char buffer[2048]; + strncpy(buffer, name, len - 6); + buffer[len-6] = 0; + // If the file has a period after removing .class, it's not really a + // valid class file. The class loader will check everything else. + if (strchr(buffer, '.') == NULL) { + _compile_the_world_counter++; + if (_compile_the_world_counter >= CompileTheWorldStartAt && _compile_the_world_counter <= CompileTheWorldStopAt) { + // Construct name without extension + symbolHandle sym = oopFactory::new_symbol_handle(buffer, CHECK); + // Use loader to load and initialize class + klassOop ik = SystemDictionary::resolve_or_null(sym, loader, Handle(), THREAD); + instanceKlassHandle k (THREAD, ik); + if (k.not_null() && !HAS_PENDING_EXCEPTION) { + k->initialize(THREAD); + } + bool exception_occurred = HAS_PENDING_EXCEPTION; + CLEAR_PENDING_EXCEPTION; + if (k.is_null() || (exception_occurred && !CompileTheWorldIgnoreInitErrors)) { + // If something went wrong (e.g. ExceptionInInitializerError) we skip this class + tty->print_cr("CompileTheWorld (%d) : Skipping %s", _compile_the_world_counter, buffer); + } else { + tty->print_cr("CompileTheWorld (%d) : %s", _compile_the_world_counter, buffer); + // Preload all classes to get around uncommon traps + if (CompileTheWorldPreloadClasses) { + constantPoolKlass::preload_and_initialize_all_classes(k->constants(), THREAD); + if (HAS_PENDING_EXCEPTION) { + // If something went wrong in preloading we just ignore it + CLEAR_PENDING_EXCEPTION; + tty->print_cr("Preloading failed for (%d) %s", _compile_the_world_counter, buffer); + } + } + // Iterate over all methods in class + for (int n = 0; n < k->methods()->length(); n++) { + methodHandle m (THREAD, methodOop(k->methods()->obj_at(n))); + if (CompilationPolicy::canBeCompiled(m)) { + // Force compilation + CompileBroker::compile_method(m, InvocationEntryBci, + methodHandle(), 0, "CTW", THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + tty->print_cr("CompileTheWorld (%d) : Skipping method: %s", _compile_the_world_counter, m->name()->as_C_string()); + } + if (TieredCompilation) { + // Clobber the first compile and force second tier compilation + m->clear_code(); + CompileBroker::compile_method(m, InvocationEntryBci, + methodHandle(), 0, "CTW", THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + tty->print_cr("CompileTheWorld (%d) : Skipping method: %s", _compile_the_world_counter, m->name()->as_C_string()); + } + } + } + } + } + } + } + } +} + +#endif //PRODUCT diff --git a/hotspot/src/share/vm/classfile/classLoader.hpp b/hotspot/src/share/vm/classfile/classLoader.hpp new file mode 100644 index 00000000000..9a04c2e43fc --- /dev/null +++ b/hotspot/src/share/vm/classfile/classLoader.hpp @@ -0,0 +1,309 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The VM class loader. +#include + + +// Meta-index (optional, to be able to skip opening boot classpath jar files) +class MetaIndex: public CHeapObj { + private: + char** _meta_package_names; + int _num_meta_package_names; + public: + MetaIndex(char** meta_package_names, int num_meta_package_names); + ~MetaIndex(); + bool may_contain(const char* class_name); +}; + + +// Class path entry (directory or zip file) + +class ClassPathEntry: public CHeapObj { + private: + ClassPathEntry* _next; + public: + // Next entry in class path + ClassPathEntry* next() { return _next; } + void set_next(ClassPathEntry* next) { + // may have unlocked readers, so write atomically. + OrderAccess::release_store_ptr(&_next, next); + } + virtual bool is_jar_file() = 0; + virtual const char* name() = 0; + virtual bool is_lazy(); + // Constructor + ClassPathEntry(); + // Attempt to locate file_name through this class path entry. + // Returns a class file parsing stream if successfull. + virtual ClassFileStream* open_stream(const char* name) = 0; + // Debugging + NOT_PRODUCT(virtual void compile_the_world(Handle loader, TRAPS) = 0;) + NOT_PRODUCT(virtual bool is_rt_jar() = 0;) +}; + + +class ClassPathDirEntry: public ClassPathEntry { + private: + char* _dir; // Name of directory + public: + bool is_jar_file() { return false; } + const char* name() { return _dir; } + ClassPathDirEntry(char* dir); + ClassFileStream* open_stream(const char* name); + // Debugging + NOT_PRODUCT(void compile_the_world(Handle loader, TRAPS);) + NOT_PRODUCT(bool is_rt_jar();) +}; + + +// Type definitions for zip file and zip file entry +typedef void* jzfile; +typedef struct { + char *name; /* entry name */ + jlong time; /* modification time */ + jlong size; /* size of uncompressed data */ + jlong csize; /* size of compressed data (zero if uncompressed) */ + jint crc; /* crc of uncompressed data */ + char *comment; /* optional zip file comment */ + jbyte *extra; /* optional extra data */ + jlong pos; /* position of LOC header (if negative) or data */ +} jzentry; + + +class ClassPathZipEntry: public ClassPathEntry { + private: + jzfile* _zip; // The zip archive + char* _zip_name; // Name of zip archive + public: + bool is_jar_file() { return true; } + const char* name() { return _zip_name; } + ClassPathZipEntry(jzfile* zip, const char* zip_name); + ~ClassPathZipEntry(); + ClassFileStream* open_stream(const char* name); + void contents_do(void f(const char* name, void* context), void* context); + // Debugging + NOT_PRODUCT(void compile_the_world(Handle loader, TRAPS);) + NOT_PRODUCT(void compile_the_world12(Handle loader, TRAPS);) // JDK 1.2 version + NOT_PRODUCT(void compile_the_world13(Handle loader, TRAPS);) // JDK 1.3 version + NOT_PRODUCT(bool is_rt_jar();) + NOT_PRODUCT(bool is_rt_jar12();) + NOT_PRODUCT(bool is_rt_jar13();) +}; + + +// For lazier loading of boot class path entries +class LazyClassPathEntry: public ClassPathEntry { + private: + char* _path; // dir or file + struct stat _st; + MetaIndex* _meta_index; + volatile ClassPathEntry* _resolved_entry; + ClassPathEntry* resolve_entry(); + public: + bool is_jar_file(); + const char* name() { return _path; } + LazyClassPathEntry(char* path, struct stat st); + ClassFileStream* open_stream(const char* name); + void set_meta_index(MetaIndex* meta_index) { _meta_index = meta_index; } + virtual bool is_lazy(); + // Debugging + NOT_PRODUCT(void compile_the_world(Handle loader, TRAPS);) + NOT_PRODUCT(bool is_rt_jar();) +}; + +class PackageHashtable; +class PackageInfo; +class HashtableBucket; + +class ClassLoader: AllStatic { + public: + enum SomeConstants { + package_hash_table_size = 31 // Number of buckets + }; + private: + friend class LazyClassPathEntry; + + // Performance counters + static PerfCounter* _perf_accumulated_time; + static PerfCounter* _perf_classes_inited; + static PerfCounter* _perf_class_init_time; + static PerfCounter* _perf_class_verify_time; + static PerfCounter* _perf_classes_linked; + static PerfCounter* _perf_class_link_time; + + static PerfCounter* _sync_systemLoaderLockContentionRate; + static PerfCounter* _sync_nonSystemLoaderLockContentionRate; + static PerfCounter* _sync_JVMFindLoadedClassLockFreeCounter; + static PerfCounter* _sync_JVMDefineClassLockFreeCounter; + static PerfCounter* _sync_JNIDefineClassLockFreeCounter; + + static PerfCounter* _unsafe_defineClassCallCounter; + static PerfCounter* _isUnsyncloadClass; + static PerfCounter* _load_instance_class_failCounter; + + // First entry in linked list of ClassPathEntry instances + static ClassPathEntry* _first_entry; + // Last entry in linked list of ClassPathEntry instances + static ClassPathEntry* _last_entry; + // Hash table used to keep track of loaded packages + static PackageHashtable* _package_hash_table; + static const char* _shared_archive; + + // Hash function + static unsigned int hash(const char *s, int n); + // Returns the package file name corresponding to the specified package + // or class name, or null if not found. + static PackageInfo* lookup_package(const char *pkgname); + // Adds a new package entry for the specified class or package name and + // corresponding directory or jar file name. + static bool add_package(const char *pkgname, int classpath_index, TRAPS); + + // Initialization + static void setup_meta_index(); + static void setup_bootstrap_search_path(); + static void load_zip_library(); + static void create_class_path_entry(char *path, struct stat st, ClassPathEntry **new_entry, bool lazy); + + // Canonicalizes path names, so strcmp will work properly. This is mainly + // to avoid confusing the zip library + static bool get_canonical_path(char* orig, char* out, int len); + public: + // Used by the kernel jvm. + static void update_class_path_entry_list(const char *path, + bool check_for_duplicates); + static void print_bootclasspath(); + + // Timing + static PerfCounter* perf_accumulated_time() { return _perf_accumulated_time; } + static PerfCounter* perf_classes_inited() { return _perf_classes_inited; } + static PerfCounter* perf_class_init_time() { return _perf_class_init_time; } + static PerfCounter* perf_class_verify_time() { return _perf_class_verify_time; } + static PerfCounter* perf_classes_linked() { return _perf_classes_linked; } + static PerfCounter* perf_class_link_time() { return _perf_class_link_time; } + + // Record how often system loader lock object is contended + static PerfCounter* sync_systemLoaderLockContentionRate() { + return _sync_systemLoaderLockContentionRate; + } + + // Record how often non system loader lock object is contended + static PerfCounter* sync_nonSystemLoaderLockContentionRate() { + return _sync_nonSystemLoaderLockContentionRate; + } + + // Record how many calls to JVM_FindLoadedClass w/o holding a lock + static PerfCounter* sync_JVMFindLoadedClassLockFreeCounter() { + return _sync_JVMFindLoadedClassLockFreeCounter; + } + + // Record how many calls to JVM_DefineClass w/o holding a lock + static PerfCounter* sync_JVMDefineClassLockFreeCounter() { + return _sync_JVMDefineClassLockFreeCounter; + } + + // Record how many calls to jni_DefineClass w/o holding a lock + static PerfCounter* sync_JNIDefineClassLockFreeCounter() { + return _sync_JNIDefineClassLockFreeCounter; + } + + // Record how many calls to Unsafe_DefineClass + static PerfCounter* unsafe_defineClassCallCounter() { + return _unsafe_defineClassCallCounter; + } + + // Record how many times SystemDictionary::load_instance_class call + // fails with linkageError when Unsyncloadclass flag is set. + static PerfCounter* load_instance_class_failCounter() { + return _load_instance_class_failCounter; + } + + // Load individual .class file + static instanceKlassHandle load_classfile(symbolHandle h_name, TRAPS); + + // If the specified package has been loaded by the system, then returns + // the name of the directory or ZIP file that the package was loaded from. + // Returns null if the package was not loaded. + // Note: The specified name can either be the name of a class or package. + // If a package name is specified, then it must be "/"-separator and also + // end with a trailing "/". + static oop get_system_package(const char* name, TRAPS); + + // Returns an array of Java strings representing all of the currently + // loaded system packages. + // Note: The package names returned are "/"-separated and end with a + // trailing "/". + static objArrayOop get_system_packages(TRAPS); + + // Initialization + static void initialize(); + static void create_package_info_table(); + static void create_package_info_table(HashtableBucket *t, int length, + int number_of_entries); + static int compute_Object_vtable(); + + static ClassPathEntry* classpath_entry(int n) { + ClassPathEntry* e = ClassLoader::_first_entry; + while (--n >= 0) { + assert(e != NULL, "Not that many classpath entries."); + e = e->next(); + } + return e; + } + + // Sharing dump and restore + static void copy_package_info_buckets(char** top, char* end); + static void copy_package_info_table(char** top, char* end); + + // VM monitoring and management support + static jlong classloader_time_ms(); + static jlong class_method_total_size(); + static jlong class_init_count(); + static jlong class_init_time_ms(); + static jlong class_verify_time_ms(); + static jlong class_link_count(); + static jlong class_link_time_ms(); + + // indicates if class path already contains a entry (exact match by name) + static bool contains_entry(ClassPathEntry* entry); + + // adds a class path list + static void add_to_list(ClassPathEntry* new_entry); + + // creates a class path zip entry (returns NULL if JAR file cannot be opened) + static ClassPathZipEntry* create_class_path_zip_entry(const char *apath); + + // Debugging + static void verify() PRODUCT_RETURN; + + // Force compilation of all methods in all classes in bootstrap class path (stress test) +#ifndef PRODUCT + private: + static int _compile_the_world_counter; + public: + static void compile_the_world(); + static void compile_the_world_in(char* name, Handle loader, TRAPS); + static int compile_the_world_counter() { return _compile_the_world_counter; } +#endif //PRODUCT +}; diff --git a/hotspot/src/share/vm/classfile/dictionary.cpp b/hotspot/src/share/vm/classfile/dictionary.cpp new file mode 100644 index 00000000000..6c1d1ffce61 --- /dev/null +++ b/hotspot/src/share/vm/classfile/dictionary.cpp @@ -0,0 +1,609 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_dictionary.cpp.incl" + + +DictionaryEntry* Dictionary::_current_class_entry = NULL; +int Dictionary::_current_class_index = 0; + + +Dictionary::Dictionary(int table_size) + : TwoOopHashtable(table_size, sizeof(DictionaryEntry)) { + _current_class_index = 0; + _current_class_entry = NULL; +}; + + + +Dictionary::Dictionary(int table_size, HashtableBucket* t, + int number_of_entries) + : TwoOopHashtable(table_size, sizeof(DictionaryEntry), t, number_of_entries) { + _current_class_index = 0; + _current_class_entry = NULL; +}; + + +DictionaryEntry* Dictionary::new_entry(unsigned int hash, klassOop klass, + oop loader) { + DictionaryEntry* entry; + entry = (DictionaryEntry*)Hashtable::new_entry(hash, klass); + entry->set_loader(loader); + entry->set_pd_set(NULL); + return entry; +} + + +DictionaryEntry* Dictionary::new_entry() { + DictionaryEntry* entry = (DictionaryEntry*)Hashtable::new_entry(0L, NULL); + entry->set_loader(NULL); + entry->set_pd_set(NULL); + return entry; +} + + +void Dictionary::free_entry(DictionaryEntry* entry) { + // avoid recursion when deleting linked list + while (entry->pd_set() != NULL) { + ProtectionDomainEntry* to_delete = entry->pd_set(); + entry->set_pd_set(to_delete->next()); + delete to_delete; + } + Hashtable::free_entry(entry); +} + + +bool DictionaryEntry::contains_protection_domain(oop protection_domain) const { +#ifdef ASSERT + if (protection_domain == instanceKlass::cast(klass())->protection_domain()) { + // Ensure this doesn't show up in the pd_set (invariant) + bool in_pd_set = false; + for (ProtectionDomainEntry* current = _pd_set; + current != NULL; + current = current->next()) { + if (current->protection_domain() == protection_domain) { + in_pd_set = true; + break; + } + } + if (in_pd_set) { + assert(false, "A klass's protection domain should not show up " + "in its sys. dict. PD set"); + } + } +#endif /* ASSERT */ + + if (protection_domain == instanceKlass::cast(klass())->protection_domain()) { + // Succeeds trivially + return true; + } + + for (ProtectionDomainEntry* current = _pd_set; + current != NULL; + current = current->next()) { + if (current->protection_domain() == protection_domain) return true; + } + return false; +} + + +void DictionaryEntry::add_protection_domain(oop protection_domain) { + assert_locked_or_safepoint(SystemDictionary_lock); + if (!contains_protection_domain(protection_domain)) { + ProtectionDomainEntry* new_head = + new ProtectionDomainEntry(protection_domain, _pd_set); + // Warning: Preserve store ordering. The SystemDictionary is read + // without locks. The new ProtectionDomainEntry must be + // complete before other threads can be allowed to see it + // via a store to _pd_set. + OrderAccess::release_store_ptr(&_pd_set, new_head); + } + if (TraceProtectionDomainVerification && WizardMode) { + print(); + } +} + + +bool Dictionary::do_unloading(BoolObjectClosure* is_alive) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint") + bool class_was_unloaded = false; + int index = 0; // Defined here for portability! Do not move + + // Remove unloadable entries and classes from system dictionary + // The placeholder array has been handled in always_strong_oops_do. + DictionaryEntry* probe = NULL; + for (index = 0; index < table_size(); index++) { + for (DictionaryEntry** p = bucket_addr(index); *p != NULL; ) { + probe = *p; + klassOop e = probe->klass(); + oop class_loader = probe->loader(); + + instanceKlass* ik = instanceKlass::cast(e); + if (ik->previous_versions() != NULL) { + // This klass has previous versions so see what we can cleanup + // while it is safe to do so. + + int gc_count = 0; // leave debugging breadcrumbs + int live_count = 0; + + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00000200, ("unload: %s: previous version length=%d", + ik->external_name(), ik->previous_versions()->length())); + + for (int i = ik->previous_versions()->length() - 1; i >= 0; i--) { + // check the previous versions array for GC'ed weak refs + PreviousVersionNode * pv_node = ik->previous_versions()->at(i); + jweak cp_ref = pv_node->prev_constant_pool(); + assert(cp_ref != NULL, "weak cp ref was unexpectedly cleared"); + if (cp_ref == NULL) { + delete pv_node; + ik->previous_versions()->remove_at(i); + // Since we are traversing the array backwards, we don't have to + // do anything special with the index. + continue; // robustness + } + + constantPoolOop pvcp = (constantPoolOop)JNIHandles::resolve(cp_ref); + if (pvcp == NULL) { + // this entry has been GC'ed so remove it + delete pv_node; + ik->previous_versions()->remove_at(i); + // Since we are traversing the array backwards, we don't have to + // do anything special with the index. + gc_count++; + continue; + } else { + RC_TRACE(0x00000200, ("unload: previous version @%d is alive", i)); + if (is_alive->do_object_b(pvcp)) { + live_count++; + } else { + guarantee(false, "sanity check"); + } + } + + GrowableArray* method_refs = pv_node->prev_EMCP_methods(); + if (method_refs != NULL) { + RC_TRACE(0x00000200, ("unload: previous methods length=%d", + method_refs->length())); + for (int j = method_refs->length() - 1; j >= 0; j--) { + jweak method_ref = method_refs->at(j); + assert(method_ref != NULL, "weak method ref was unexpectedly cleared"); + if (method_ref == NULL) { + method_refs->remove_at(j); + // Since we are traversing the array backwards, we don't have to + // do anything special with the index. + continue; // robustness + } + + methodOop method = (methodOop)JNIHandles::resolve(method_ref); + if (method == NULL) { + // this method entry has been GC'ed so remove it + JNIHandles::destroy_weak_global(method_ref); + method_refs->remove_at(j); + } else { + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00000200, + ("unload: %s(%s): prev method @%d in version @%d is alive", + method->name()->as_C_string(), + method->signature()->as_C_string(), j, i)); + } + } + } + } + assert(ik->previous_versions()->length() == live_count, "sanity check"); + RC_TRACE(0x00000200, + ("unload: previous version stats: live=%d, GC'ed=%d", live_count, + gc_count)); + } + + // Non-unloadable classes were handled in always_strong_oops_do + if (!is_strongly_reachable(class_loader, e)) { + // Entry was not visited in phase1 (negated test from phase1) + assert(class_loader != NULL, "unloading entry with null class loader"); + oop k_def_class_loader = ik->class_loader(); + + // Do we need to delete this system dictionary entry? + bool purge_entry = false; + + // Do we need to delete this system dictionary entry? + if (!is_alive->do_object_b(class_loader)) { + // If the loader is not live this entry should always be + // removed (will never be looked up again). Note that this is + // not the same as unloading the referred class. + if (k_def_class_loader == class_loader) { + // This is the defining entry, so the referred class is about + // to be unloaded. + // Notify the debugger and clean up the class. + guarantee(!is_alive->do_object_b(e), + "klass should not be live if defining loader is not"); + class_was_unloaded = true; + // notify the debugger + if (JvmtiExport::should_post_class_unload()) { + JvmtiExport::post_class_unload(ik->as_klassOop()); + } + + // notify ClassLoadingService of class unload + ClassLoadingService::notify_class_unloaded(ik); + + // Clean up C heap + ik->release_C_heap_structures(); + } + // Also remove this system dictionary entry. + purge_entry = true; + + } else { + // The loader in this entry is alive. If the klass is dead, + // the loader must be an initiating loader (rather than the + // defining loader). Remove this entry. + if (!is_alive->do_object_b(e)) { + guarantee(!is_alive->do_object_b(k_def_class_loader), + "defining loader should not be live if klass is not"); + // If we get here, the class_loader must not be the defining + // loader, it must be an initiating one. + assert(k_def_class_loader != class_loader, + "cannot have live defining loader and unreachable klass"); + + // Loader is live, but class and its defining loader are dead. + // Remove the entry. The class is going away. + purge_entry = true; + } + } + + if (purge_entry) { + *p = probe->next(); + if (probe == _current_class_entry) { + _current_class_entry = NULL; + } + free_entry(probe); + continue; + } + } + p = probe->next_addr(); + } + } + return class_was_unloaded; +} + + +void Dictionary::always_strong_classes_do(OopClosure* blk) { + // Follow all system classes and temporary placeholders in dictionary + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry *probe = bucket(index); + probe != NULL; + probe = probe->next()) { + oop e = probe->klass(); + oop class_loader = probe->loader(); + if (is_strongly_reachable(class_loader, e)) { + blk->do_oop((oop*)probe->klass_addr()); + if (class_loader != NULL) { + blk->do_oop(probe->loader_addr()); + } + probe->protection_domain_set_oops_do(blk); + } + } + } +} + + +// Just the classes from defining class loaders +void Dictionary::classes_do(void f(klassOop)) { + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + klassOop k = probe->klass(); + if (probe->loader() == instanceKlass::cast(k)->class_loader()) { + f(k); + } + } + } +} + +// Added for initialize_itable_for_klass to handle exceptions +// Just the classes from defining class loaders +void Dictionary::classes_do(void f(klassOop, TRAPS), TRAPS) { + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + klassOop k = probe->klass(); + if (probe->loader() == instanceKlass::cast(k)->class_loader()) { + f(k, CHECK); + } + } + } +} + + +// All classes, and their class loaders +// (added for helpers that use HandleMarks and ResourceMarks) +// Don't iterate over placeholders +void Dictionary::classes_do(void f(klassOop, oop, TRAPS), TRAPS) { + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + klassOop k = probe->klass(); + f(k, probe->loader(), CHECK); + } + } +} + + +// All classes, and their class loaders +// Don't iterate over placeholders +void Dictionary::classes_do(void f(klassOop, oop)) { + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + klassOop k = probe->klass(); + f(k, probe->loader()); + } + } +} + + +void Dictionary::oops_do(OopClosure* f) { + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + f->do_oop((oop*)probe->klass_addr()); + if (probe->loader() != NULL) { + f->do_oop(probe->loader_addr()); + } + probe->protection_domain_set_oops_do(f); + } + } +} + + +void Dictionary::methods_do(void f(methodOop)) { + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + klassOop k = probe->klass(); + if (probe->loader() == instanceKlass::cast(k)->class_loader()) { + // only take klass is we have the entry with the defining class loader + instanceKlass::cast(k)->methods_do(f); + } + } + } +} + + +klassOop Dictionary::try_get_next_class() { + while (true) { + if (_current_class_entry != NULL) { + klassOop k = _current_class_entry->klass(); + _current_class_entry = _current_class_entry->next(); + return k; + } + _current_class_index = (_current_class_index + 1) % table_size(); + _current_class_entry = bucket(_current_class_index); + } + // never reached +} + + +// Add a loaded class to the system dictionary. +// Readers of the SystemDictionary aren't always locked, so _buckets +// is volatile. The store of the next field in the constructor is +// also cast to volatile; we do this to ensure store order is maintained +// by the compilers. + +void Dictionary::add_klass(symbolHandle class_name, Handle class_loader, + KlassHandle obj) { + assert_locked_or_safepoint(SystemDictionary_lock); + assert(obj() != NULL, "adding NULL obj"); + assert(Klass::cast(obj())->name() == class_name(), "sanity check on name"); + + unsigned int hash = compute_hash(class_name, class_loader); + int index = hash_to_index(hash); + DictionaryEntry* entry = new_entry(hash, obj(), class_loader()); + add_entry(index, entry); +} + + +// This routine does not lock the system dictionary. +// +// Since readers don't hold a lock, we must make sure that system +// dictionary entries are only removed at a safepoint (when only one +// thread is running), and are added to in a safe way (all links must +// be updated in an MT-safe manner). +// +// Callers should be aware that an entry could be added just after +// _buckets[index] is read here, so the caller will not see the new entry. +DictionaryEntry* Dictionary::get_entry(int index, unsigned int hash, + symbolHandle class_name, + Handle class_loader) { + symbolOop name_ = class_name(); + oop loader_ = class_loader(); + debug_only(_lookup_count++); + for (DictionaryEntry* entry = bucket(index); + entry != NULL; + entry = entry->next()) { + if (entry->hash() == hash && entry->equals(name_, loader_)) { + return entry; + } + debug_only(_lookup_length++); + } + return NULL; +} + + +klassOop Dictionary::find(int index, unsigned int hash, symbolHandle name, + Handle loader, Handle protection_domain, TRAPS) { + DictionaryEntry* entry = get_entry(index, hash, name, loader); + if (entry != NULL && entry->is_valid_protection_domain(protection_domain)) { + return entry->klass(); + } else { + return NULL; + } +} + + +klassOop Dictionary::find_class(int index, unsigned int hash, + symbolHandle name, Handle loader) { + assert_locked_or_safepoint(SystemDictionary_lock); + assert (index == index_for(name, loader), "incorrect index?"); + + DictionaryEntry* entry = get_entry(index, hash, name, loader); + return (entry != NULL) ? entry->klass() : (klassOop)NULL; +} + + +// Variant of find_class for shared classes. No locking required, as +// that table is static. + +klassOop Dictionary::find_shared_class(int index, unsigned int hash, + symbolHandle name) { + assert (index == index_for(name, Handle()), "incorrect index?"); + + DictionaryEntry* entry = get_entry(index, hash, name, Handle()); + return (entry != NULL) ? entry->klass() : (klassOop)NULL; +} + + +void Dictionary::add_protection_domain(int index, unsigned int hash, + instanceKlassHandle klass, + Handle loader, Handle protection_domain, + TRAPS) { + symbolHandle klass_name(THREAD, klass->name()); + DictionaryEntry* entry = get_entry(index, hash, klass_name, loader); + + assert(entry != NULL,"entry must be present, we just created it"); + assert(protection_domain() != NULL, + "real protection domain should be present"); + + entry->add_protection_domain(protection_domain()); + + assert(entry->contains_protection_domain(protection_domain()), + "now protection domain should be present"); +} + + +bool Dictionary::is_valid_protection_domain(int index, unsigned int hash, + symbolHandle name, + Handle loader, + Handle protection_domain) { + DictionaryEntry* entry = get_entry(index, hash, name, loader); + return entry->is_valid_protection_domain(protection_domain); +} + + +void Dictionary::reorder_dictionary() { + + // Copy all the dictionary entries into a single master list. + + DictionaryEntry* master_list = NULL; + for (int i = 0; i < table_size(); ++i) { + DictionaryEntry* p = bucket(i); + while (p != NULL) { + DictionaryEntry* tmp; + tmp = p->next(); + p->set_next(master_list); + master_list = p; + p = tmp; + } + set_entry(i, NULL); + } + + // Add the dictionary entries back to the list in the correct buckets. + Thread *thread = Thread::current(); + + while (master_list != NULL) { + DictionaryEntry* p = master_list; + master_list = master_list->next(); + p->set_next(NULL); + symbolHandle class_name (thread, instanceKlass::cast((klassOop)(p->klass()))->name()); + unsigned int hash = compute_hash(class_name, Handle(thread, p->loader())); + int index = hash_to_index(hash); + p->set_hash(hash); + p->set_next(bucket(index)); + set_entry(index, p); + } +} + + +// ---------------------------------------------------------------------------- +#ifndef PRODUCT + +void Dictionary::print() { + ResourceMark rm; + HandleMark hm; + + tty->print_cr("Java system dictionary (classes=%d)", number_of_entries()); + tty->print_cr("^ indicates that initiating loader is different from " + "defining loader"); + + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + if (Verbose) tty->print("%4d: ", index); + klassOop e = probe->klass(); + oop class_loader = probe->loader(); + bool is_defining_class = + (class_loader == instanceKlass::cast(e)->class_loader()); + tty->print("%s%s", is_defining_class ? " " : "^", + Klass::cast(e)->external_name()); + if (class_loader != NULL) { + tty->print(", loader "); + class_loader->print_value(); + } + tty->cr(); + } + } +} + +#endif + +void Dictionary::verify() { + guarantee(number_of_entries() >= 0, "Verify of system dictionary failed"); + int element_count = 0; + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + klassOop e = probe->klass(); + oop class_loader = probe->loader(); + guarantee(Klass::cast(e)->oop_is_instance(), + "Verify of system dictionary failed"); + // class loader must be present; a null class loader is the + // boostrap loader + guarantee(class_loader == NULL || class_loader->is_instance(), + "checking type of class_loader"); + e->verify(); + probe->verify_protection_domain_set(); + element_count++; + } + } + guarantee(number_of_entries() == element_count, + "Verify of system dictionary failed"); + debug_only(verify_lookup_length((double)number_of_entries() / table_size())); +} diff --git a/hotspot/src/share/vm/classfile/dictionary.hpp b/hotspot/src/share/vm/classfile/dictionary.hpp new file mode 100644 index 00000000000..b082c739208 --- /dev/null +++ b/hotspot/src/share/vm/classfile/dictionary.hpp @@ -0,0 +1,219 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class DictionaryEntry; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// The data structure for the system dictionary (and the shared system +// dictionary). + +class Dictionary : public TwoOopHashtable { + friend class VMStructs; +private: + // current iteration index. + static int _current_class_index; + // pointer to the current hash table entry. + static DictionaryEntry* _current_class_entry; + + DictionaryEntry* get_entry(int index, unsigned int hash, + symbolHandle name, Handle loader); + + DictionaryEntry* bucket(int i) { + return (DictionaryEntry*)Hashtable::bucket(i); + } + + // The following method is not MT-safe and must be done under lock. + DictionaryEntry** bucket_addr(int i) { + return (DictionaryEntry**)Hashtable::bucket_addr(i); + } + + void add_entry(int index, DictionaryEntry* new_entry) { + Hashtable::add_entry(index, (HashtableEntry*)new_entry); + } + + +public: + Dictionary(int table_size); + Dictionary(int table_size, HashtableBucket* t, int number_of_entries); + + DictionaryEntry* new_entry(unsigned int hash, klassOop klass, oop loader); + + DictionaryEntry* new_entry(); + + void free_entry(DictionaryEntry* entry); + + void add_klass(symbolHandle class_name, Handle class_loader,KlassHandle obj); + + klassOop find_class(int index, unsigned int hash, + symbolHandle name, Handle loader); + + klassOop find_shared_class(int index, unsigned int hash, symbolHandle name); + + // Compiler support + klassOop try_get_next_class(); + + // GC support + + void oops_do(OopClosure* f); + void always_strong_classes_do(OopClosure* blk); + void classes_do(void f(klassOop)); + void classes_do(void f(klassOop, TRAPS), TRAPS); + void classes_do(void f(klassOop, oop)); + void classes_do(void f(klassOop, oop, TRAPS), TRAPS); + + void methods_do(void f(methodOop)); + + + // Classes loaded by the bootstrap loader are always strongly reachable. + // If we're not doing class unloading, all classes are strongly reachable. + static bool is_strongly_reachable(oop class_loader, oop klass) { + assert (klass != NULL, "should have non-null klass"); + return (class_loader == NULL || !ClassUnloading); + } + + // Unload (that is, break root links to) all unmarked classes and + // loaders. Returns "true" iff something was unloaded. + bool do_unloading(BoolObjectClosure* is_alive); + + // Protection domains + klassOop find(int index, unsigned int hash, symbolHandle name, + Handle loader, Handle protection_domain, TRAPS); + bool is_valid_protection_domain(int index, unsigned int hash, + symbolHandle name, Handle class_loader, + Handle protection_domain); + void add_protection_domain(int index, unsigned int hash, + instanceKlassHandle klass, Handle loader, + Handle protection_domain, TRAPS); + + // Sharing support + void dump(SerializeOopClosure* soc); + void restore(SerializeOopClosure* soc); + void reorder_dictionary(); + + +#ifndef PRODUCT + void print(); +#endif + void verify(); +}; + +// The following classes can be in dictionary.cpp, but we need these +// to be in header file so that SA's vmStructs can access. + +class ProtectionDomainEntry :public CHeapObj { + friend class VMStructs; + public: + ProtectionDomainEntry* _next; + oop _protection_domain; + + ProtectionDomainEntry(oop protection_domain, ProtectionDomainEntry* next) { + _protection_domain = protection_domain; + _next = next; + } + + ProtectionDomainEntry* next() { return _next; } + oop protection_domain() { return _protection_domain; } +}; + +// An entry in the system dictionary, this describes a class as +// { klassOop, loader, protection_domain }. + +class DictionaryEntry : public HashtableEntry { + friend class VMStructs; + private: + // Contains the set of approved protection domains that can access + // this system dictionary entry. + ProtectionDomainEntry* _pd_set; + oop _loader; + + + public: + // Tells whether a protection is in the approved set. + bool contains_protection_domain(oop protection_domain) const; + // Adds a protection domain to the approved set. + void add_protection_domain(oop protection_domain); + + klassOop klass() const { return (klassOop)literal(); } + klassOop* klass_addr() { return (klassOop*)literal_addr(); } + + DictionaryEntry* next() const { + return (DictionaryEntry*)HashtableEntry::next(); + } + + DictionaryEntry** next_addr() { + return (DictionaryEntry**)HashtableEntry::next_addr(); + } + + oop loader() const { return _loader; } + void set_loader(oop loader) { _loader = loader; } + oop* loader_addr() { return &_loader; } + + ProtectionDomainEntry* pd_set() const { return _pd_set; } + void set_pd_set(ProtectionDomainEntry* pd_set) { _pd_set = pd_set; } + + bool has_protection_domain() { return _pd_set != NULL; } + + // Tells whether the initiating class' protection can access the this _klass + bool is_valid_protection_domain(Handle protection_domain) { + if (!ProtectionDomainVerification) return true; + if (!SystemDictionary::has_checkPackageAccess()) return true; + + return protection_domain() == NULL + ? true + : contains_protection_domain(protection_domain()); + } + + + void protection_domain_set_oops_do(OopClosure* f) { + for (ProtectionDomainEntry* current = _pd_set; + current != NULL; + current = current->_next) { + f->do_oop(&(current->_protection_domain)); + } + } + + void verify_protection_domain_set() { + for (ProtectionDomainEntry* current = _pd_set; + current != NULL; + current = current->_next) { + current->_protection_domain->verify(); + } + } + + bool equals(symbolOop class_name, oop class_loader) const { + klassOop klass = (klassOop)literal(); + return (instanceKlass::cast(klass)->name() == class_name && + _loader == class_loader); + } + + void print() { + int count = 0; + for (ProtectionDomainEntry* current = _pd_set; + current != NULL; + current = current->_next) { + count++; + } + tty->print_cr("pd set = #%d", count); + } +}; diff --git a/hotspot/src/share/vm/classfile/javaAssertions.cpp b/hotspot/src/share/vm/classfile/javaAssertions.cpp new file mode 100644 index 00000000000..8f318e6a426 --- /dev/null +++ b/hotspot/src/share/vm/classfile/javaAssertions.cpp @@ -0,0 +1,207 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_javaAssertions.cpp.incl" + +bool JavaAssertions::_userDefault = false; +bool JavaAssertions::_sysDefault = false; +JavaAssertions::OptionList* JavaAssertions::_classes = 0; +JavaAssertions::OptionList* JavaAssertions::_packages = 0; + +JavaAssertions::OptionList::OptionList(const char* name, bool enabled, + OptionList* next) { + assert(name != 0, "need a name"); + _name = name; + _enabled = enabled; + _next = next; +} + +int JavaAssertions::OptionList::count(OptionList* p) { + int rc; + for (rc = 0; p != 0; p = p->next(), ++rc) /* empty */; + return rc; +} + +void JavaAssertions::addOption(const char* name, bool enable) { + assert(name != 0, "must have a name"); + + // Copy the name. The storage needs to exist for the the lifetime of the vm; + // it is never freed, so will be leaked (along with other option strings - + // e.g., bootclasspath) if a process creates/destroys multiple VMs. + int len = (int)strlen(name); + char *name_copy = NEW_C_HEAP_ARRAY(char, len + 1); + strcpy(name_copy, name); + + // Figure out which list the new item should go on. Names that end in "..." + // go on the package tree list. + OptionList** head = &_classes; + if (len >= 3 && strcmp(name_copy + len - 3, "...") == 0) { + // Delete the "...". + len -= 3; + name_copy[len] = '\0'; + head = &_packages; + } + + // Convert class/package names to internal format. Will have to convert back + // when copying to java in createJavaAssertionStatusDirectives, but that + // should happen only once. Alternative would require that + // JVM_DesiredAssertionStatus pass the external_name() to + // JavaAssertion::enabled(), but that is done once per loaded class. + for (int i = 0; i < len; ++i) { + if (name_copy[i] == '.') name_copy[i] = '/'; + } + + if (TraceJavaAssertions) { + tty->print_cr("JavaAssertions: adding %s %s=%d", + head == &_classes ? "class" : "package", + name_copy[0] != '\0' ? name_copy : "'default'", + enable); + } + + // Prepend a new item to the list. Items added later take precedence, so + // prepending allows us to stop searching the list after the first match. + *head = new OptionList(name_copy, enable, *head); +} + +oop JavaAssertions::createAssertionStatusDirectives(TRAPS) { + symbolHandle asd_sym = vmSymbolHandles::java_lang_AssertionStatusDirectives(); + klassOop k = SystemDictionary::resolve_or_fail(asd_sym, true, CHECK_NULL); + instanceKlassHandle asd_klass (THREAD, k); + asd_klass->initialize(CHECK_NULL); + Handle h = asd_klass->allocate_instance_handle(CHECK_NULL); + + int len; + typeArrayOop t; + len = OptionList::count(_packages); + objArrayOop pn = oopFactory::new_objArray(SystemDictionary::string_klass(), len, CHECK_NULL); + objArrayHandle pkgNames (THREAD, pn); + t = oopFactory::new_typeArray(T_BOOLEAN, len, CHECK_NULL); + typeArrayHandle pkgEnabled(THREAD, t); + fillJavaArrays(_packages, len, pkgNames, pkgEnabled, CHECK_NULL); + + len = OptionList::count(_classes); + objArrayOop cn = oopFactory::new_objArray(SystemDictionary::string_klass(), len, CHECK_NULL); + objArrayHandle classNames (THREAD, cn); + t = oopFactory::new_typeArray(T_BOOLEAN, len, CHECK_NULL); + typeArrayHandle classEnabled(THREAD, t); + fillJavaArrays(_classes, len, classNames, classEnabled, CHECK_NULL); + + java_lang_AssertionStatusDirectives::set_packages(h(), pkgNames()); + java_lang_AssertionStatusDirectives::set_packageEnabled(h(), pkgEnabled()); + java_lang_AssertionStatusDirectives::set_classes(h(), classNames()); + java_lang_AssertionStatusDirectives::set_classEnabled(h(), classEnabled()); + java_lang_AssertionStatusDirectives::set_deflt(h(), userClassDefault()); + return h(); +} + +void JavaAssertions::fillJavaArrays(const OptionList* p, int len, +objArrayHandle names, typeArrayHandle enabled, TRAPS) { + // Fill in the parallel names and enabled (boolean) arrays. Start at the end + // of the array and work backwards, so the order of items in the arrays + // matches the order on the command line (the list is in reverse order, since + // it was created by prepending successive items from the command line). + int index; + for (index = len - 1; p != 0; p = p->next(), --index) { + assert(index >= 0, "length does not match list"); + Handle s = java_lang_String::create_from_str(p->name(), CHECK); + s = java_lang_String::char_converter(s, '/', '.', CHECK); + names->obj_at_put(index, s()); + enabled->bool_at_put(index, p->enabled()); + } + assert(index == -1, "length does not match list"); +} + +inline JavaAssertions::OptionList* +JavaAssertions::match_class(const char* classname) { + for (OptionList* p = _classes; p != 0; p = p->next()) { + if (strcmp(p->name(), classname) == 0) { + return p; + } + } + return 0; +} + +JavaAssertions::OptionList* +JavaAssertions::match_package(const char* classname) { + // Search the package list for any items that apply to classname. Each + // sub-package in classname is checked, from most-specific to least, until one + // is found. + if (_packages == 0) return 0; + + // Find the length of the "most-specific" package in classname. If classname + // does not include a package, length will be 0 which will match items for the + // default package (from options "-ea:..." or "-da:..."). + size_t len = strlen(classname); + for (/* empty */; len > 0 && classname[len] != '/'; --len) /* empty */; + + do { + assert(len == 0 || classname[len] == '/', "not a package name"); + for (OptionList* p = _packages; p != 0; p = p->next()) { + if (strncmp(p->name(), classname, len) == 0 && p->name()[len] == '\0') { + return p; + } + } + + // Find the length of the next package, taking care to avoid decrementing + // past 0 (len is unsigned). + while (len > 0 && classname[--len] != '/') /* empty */; + } while (len > 0); + + return 0; +} + +inline void JavaAssertions::trace(const char* name, +const char* typefound, const char* namefound, bool enabled) { + if (TraceJavaAssertions) { + tty->print_cr("JavaAssertions: search for %s found %s %s=%d", + name, typefound, namefound[0] != '\0' ? namefound : "'default'", enabled); + } +} + +bool JavaAssertions::enabled(const char* classname, bool systemClass) { + assert(classname != 0, "must have a classname"); + + // This will be slow if the number of assertion options on the command line is + // large--it traverses two lists, one of them multiple times. Could use a + // single n-ary tree instead of lists if someone ever notices. + + // First check options that apply to classes. If we find a match we're done. + OptionList* p; + if (p = match_class(classname)) { + trace(classname, "class", p->name(), p->enabled()); + return p->enabled(); + } + + // Now check packages, from most specific to least. + if (p = match_package(classname)) { + trace(classname, "package", p->name(), p->enabled()); + return p->enabled(); + } + + // No match. Return the default status. + bool result = systemClass ? systemClassDefault() : userClassDefault(); + trace(classname, systemClass ? "system" : "user", "default", result); + return result; +} diff --git a/hotspot/src/share/vm/classfile/javaAssertions.hpp b/hotspot/src/share/vm/classfile/javaAssertions.hpp new file mode 100644 index 00000000000..8ba1850cf06 --- /dev/null +++ b/hotspot/src/share/vm/classfile/javaAssertions.hpp @@ -0,0 +1,97 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class JavaAssertions: AllStatic { +public: + static inline bool userClassDefault(); + static inline void setUserClassDefault(bool enabled); + static inline bool systemClassDefault(); + static inline void setSystemClassDefault(bool enabled); + + // Add a command-line option. A name ending in "..." applies to a package and + // any subpackages; other names apply to a single class. + static void addOption(const char* name, bool enable); + + // Return true if command-line options have enabled assertions for the named + // class. Should be called only after all command-line options have been + // processed. Note: this only consults command-line options and does not + // account for any dynamic changes to assertion status. + static bool enabled(const char* classname, bool systemClass); + + // Create an instance of java.lang.AssertionStatusDirectives and fill in the + // fields based on the command-line assertion options. + static oop createAssertionStatusDirectives(TRAPS); + +private: + class OptionList; + static void fillJavaArrays(const OptionList* p, int len, objArrayHandle names, + typeArrayHandle status, TRAPS); + + static inline void trace(const char* name, const char* typefound, + const char* namefound, bool enabled); + + static inline OptionList* match_class(const char* classname); + static OptionList* match_package(const char* classname); + + static bool _userDefault; // User class default (-ea/-da). + static bool _sysDefault; // System class default (-esa/-dsa). + static OptionList* _classes; // Options for classes. + static OptionList* _packages; // Options for package trees. +}; + +class JavaAssertions::OptionList: public CHeapObj { +public: + inline OptionList(const char* name, bool enable, OptionList* next); + + inline const char* name() const { return _name; } + inline bool enabled() const { return _enabled; } + inline OptionList* next() const { return _next; } + + static int count(OptionList* p); + +private: + const char* _name; + OptionList* _next; + bool _enabled; +}; + +inline bool JavaAssertions::userClassDefault() { + return _userDefault; +} + +inline void JavaAssertions::setUserClassDefault(bool enabled) { + if (TraceJavaAssertions) + tty->print_cr("JavaAssertions::setUserClassDefault(%d)", enabled); + _userDefault = enabled; +} + +inline bool JavaAssertions::systemClassDefault() { + return _sysDefault; +} + +inline void JavaAssertions::setSystemClassDefault(bool enabled) { + if (TraceJavaAssertions) + tty->print_cr("JavaAssertions::setSystemClassDefault(%d)", enabled); + _sysDefault = enabled; +} diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp new file mode 100644 index 00000000000..115f45231e7 --- /dev/null +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp @@ -0,0 +1,2515 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_javaClasses.cpp.incl" + +// Helpful macro for computing field offsets at run time rather than hardcoding them +#define COMPUTE_OFFSET(klass_name_as_C_str, dest_offset, klass_oop, name_symbol, signature_symbol) \ +{ \ + fieldDescriptor fd; \ + instanceKlass* ik = instanceKlass::cast(klass_oop); \ + if (!ik->find_local_field(name_symbol, signature_symbol, &fd)) { \ + fatal("Invalid layout of " klass_name_as_C_str); \ + } \ + dest_offset = fd.offset(); \ +} + +// Same as above but for "optional" offsets that might not be present in certain JDK versions +#define COMPUTE_OPTIONAL_OFFSET(klass_name_as_C_str, dest_offset, klass_oop, name_symbol, signature_symbol) \ +{ \ + fieldDescriptor fd; \ + instanceKlass* ik = instanceKlass::cast(klass_oop); \ + if (ik->find_local_field(name_symbol, signature_symbol, &fd)) { \ + dest_offset = fd.offset(); \ + } \ +} + +Handle java_lang_String::basic_create(int length, bool tenured, TRAPS) { + // Create the String object first, so there's a chance that the String + // and the char array it points to end up in the same cache line. + oop obj; + if (tenured) { + obj = instanceKlass::cast(SystemDictionary::string_klass())->allocate_permanent_instance(CHECK_NH); + } else { + obj = instanceKlass::cast(SystemDictionary::string_klass())->allocate_instance(CHECK_NH); + } + + // Create the char array. The String object must be handlized here + // because GC can happen as a result of the allocation attempt. + Handle h_obj(THREAD, obj); + typeArrayOop buffer; + if (tenured) { + buffer = oopFactory::new_permanent_charArray(length, CHECK_NH); + } else { + buffer = oopFactory::new_charArray(length, CHECK_NH); + } + + // Point the String at the char array + obj = h_obj(); + set_value(obj, buffer); + // No need to zero the offset, allocation zero'ed the entire String object + assert(offset(obj) == 0, "initial String offset should be zero"); +//set_offset(obj, 0); + set_count(obj, length); + + return h_obj; +} + +Handle java_lang_String::basic_create_from_unicode(jchar* unicode, int length, bool tenured, TRAPS) { + Handle h_obj = basic_create(length, tenured, CHECK_NH); + typeArrayOop buffer = value(h_obj()); + for (int index = 0; index < length; index++) { + buffer->char_at_put(index, unicode[index]); + } + return h_obj; +} + +Handle java_lang_String::create_from_unicode(jchar* unicode, int length, TRAPS) { + return basic_create_from_unicode(unicode, length, false, CHECK_NH); +} + +Handle java_lang_String::create_tenured_from_unicode(jchar* unicode, int length, TRAPS) { + return basic_create_from_unicode(unicode, length, true, CHECK_NH); +} + +oop java_lang_String::create_oop_from_unicode(jchar* unicode, int length, TRAPS) { + Handle h_obj = basic_create_from_unicode(unicode, length, false, CHECK_0); + return h_obj(); +} + +Handle java_lang_String::create_from_str(const char* utf8_str, TRAPS) { + if (utf8_str == NULL) { + return Handle(); + } + int length = UTF8::unicode_length(utf8_str); + Handle h_obj = basic_create(length, false, CHECK_NH); + if (length > 0) { + UTF8::convert_to_unicode(utf8_str, value(h_obj())->char_at_addr(0), length); + } + return h_obj; +} + +oop java_lang_String::create_oop_from_str(const char* utf8_str, TRAPS) { + Handle h_obj = create_from_str(utf8_str, CHECK_0); + return h_obj(); +} + +Handle java_lang_String::create_from_symbol(symbolHandle symbol, TRAPS) { + int length = UTF8::unicode_length((char*)symbol->bytes(), symbol->utf8_length()); + Handle h_obj = basic_create(length, false, CHECK_NH); + if (length > 0) { + UTF8::convert_to_unicode((char*)symbol->bytes(), value(h_obj())->char_at_addr(0), length); + } + return h_obj; +} + +// Converts a C string to a Java String based on current encoding +Handle java_lang_String::create_from_platform_dependent_str(const char* str, TRAPS) { + assert(str != NULL, "bad arguments"); + + typedef jstring (*to_java_string_fn_t)(JNIEnv*, const char *); + static to_java_string_fn_t _to_java_string_fn = NULL; + + if (_to_java_string_fn == NULL) { + void *lib_handle = os::native_java_library(); + _to_java_string_fn = CAST_TO_FN_PTR(to_java_string_fn_t, hpi::dll_lookup(lib_handle, "NewStringPlatform")); + if (_to_java_string_fn == NULL) { + fatal("NewStringPlatform missing"); + } + } + + jstring js = NULL; + { JavaThread* thread = (JavaThread*)THREAD; + assert(thread->is_Java_thread(), "must be java thread"); + ThreadToNativeFromVM ttn(thread); + HandleMark hm(thread); + js = (_to_java_string_fn)(thread->jni_environment(), str); + } + return Handle(THREAD, JNIHandles::resolve(js)); +} + +Handle java_lang_String::char_converter(Handle java_string, jchar from_char, jchar to_char, TRAPS) { + oop obj = java_string(); + // Typical usage is to convert all '/' to '.' in string. + typeArrayOop value = java_lang_String::value(obj); + int offset = java_lang_String::offset(obj); + int length = java_lang_String::length(obj); + + // First check if any from_char exist + int index; // Declared outside, used later + for (index = 0; index < length; index++) { + if (value->char_at(index + offset) == from_char) { + break; + } + } + if (index == length) { + // No from_char, so do not copy. + return java_string; + } + + // Create new UNICODE buffer. Must handlize value because GC + // may happen during String and char array creation. + typeArrayHandle h_value(THREAD, value); + Handle string = basic_create(length, false, CHECK_NH); + + typeArrayOop from_buffer = h_value(); + typeArrayOop to_buffer = java_lang_String::value(string()); + + // Copy contents + for (index = 0; index < length; index++) { + jchar c = from_buffer->char_at(index + offset); + if (c == from_char) { + c = to_char; + } + to_buffer->char_at_put(index, c); + } + return string; +} + +jchar* java_lang_String::as_unicode_string(oop java_string, int& length) { + typeArrayOop value = java_lang_String::value(java_string); + int offset = java_lang_String::offset(java_string); + length = java_lang_String::length(java_string); + + jchar* result = NEW_RESOURCE_ARRAY(jchar, length); + for (int index = 0; index < length; index++) { + result[index] = value->char_at(index + offset); + } + return result; +} + +symbolHandle java_lang_String::as_symbol(Handle java_string, TRAPS) { + oop obj = java_string(); + typeArrayOop value = java_lang_String::value(obj); + int offset = java_lang_String::offset(obj); + int length = java_lang_String::length(obj); + + ResourceMark rm(THREAD); + symbolHandle result; + + if (length > 0) { + int utf8_length = UNICODE::utf8_length(value->char_at_addr(offset), length); + char* chars = NEW_RESOURCE_ARRAY(char, utf8_length + 1); + UNICODE::convert_to_utf8(value->char_at_addr(offset), length, chars); + // Allocate the symbol + result = oopFactory::new_symbol_handle(chars, utf8_length, CHECK_(symbolHandle())); + } else { + result = oopFactory::new_symbol_handle("", 0, CHECK_(symbolHandle())); + } + return result; +} + +int java_lang_String::utf8_length(oop java_string) { + typeArrayOop value = java_lang_String::value(java_string); + int offset = java_lang_String::offset(java_string); + int length = java_lang_String::length(java_string); + jchar* position = (length == 0) ? NULL : value->char_at_addr(offset); + return UNICODE::utf8_length(position, length); +} + +char* java_lang_String::as_utf8_string(oop java_string) { + typeArrayOop value = java_lang_String::value(java_string); + int offset = java_lang_String::offset(java_string); + int length = java_lang_String::length(java_string); + jchar* position = (length == 0) ? NULL : value->char_at_addr(offset); + return UNICODE::as_utf8(position, length); +} + +char* java_lang_String::as_utf8_string(oop java_string, int start, int len) { + typeArrayOop value = java_lang_String::value(java_string); + int offset = java_lang_String::offset(java_string); + int length = java_lang_String::length(java_string); + assert(start + len <= length, "just checking"); + jchar* position = value->char_at_addr(offset + start); + return UNICODE::as_utf8(position, len); +} + +bool java_lang_String::equals(oop java_string, jchar* chars, int len) { + assert(SharedSkipVerify || + java_string->klass() == SystemDictionary::string_klass(), + "must be java_string"); + typeArrayOop value = java_lang_String::value(java_string); + int offset = java_lang_String::offset(java_string); + int length = java_lang_String::length(java_string); + if (length != len) { + return false; + } + for (int i = 0; i < len; i++) { + if (value->char_at(i + offset) != chars[i]) { + return false; + } + } + return true; +} + +void java_lang_String::print(Handle java_string, outputStream* st) { + oop obj = java_string(); + assert(obj->klass() == SystemDictionary::string_klass(), "must be java_string"); + typeArrayOop value = java_lang_String::value(obj); + int offset = java_lang_String::offset(obj); + int length = java_lang_String::length(obj); + + int end = MIN2(length, 100); + if (value == NULL) { + // This can happen if, e.g., printing a String + // object before its initializer has been called + st->print_cr("NULL"); + } else { + st->print("\""); + for (int index = 0; index < length; index++) { + st->print("%c", value->char_at(index + offset)); + } + st->print("\""); + } +} + + +oop java_lang_Class::create_mirror(KlassHandle k, TRAPS) { + assert(k->java_mirror() == NULL, "should only assign mirror once"); + // Use this moment of initialization to cache modifier_flags also, + // to support Class.getModifiers(). Instance classes recalculate + // the cached flags after the class file is parsed, but before the + // class is put into the system dictionary. + int computed_modifiers = k->compute_modifier_flags(CHECK_0); + k->set_modifier_flags(computed_modifiers); + if (SystemDictionary::class_klass_loaded()) { + // Allocate mirror (java.lang.Class instance) + Handle mirror = instanceKlass::cast(SystemDictionary::class_klass())->allocate_permanent_instance(CHECK_0); + // Setup indirections + mirror->obj_field_put(klass_offset, k()); + k->set_java_mirror(mirror()); + // It might also have a component mirror. This mirror must already exist. + if (k->oop_is_javaArray()) { + Handle comp_mirror; + if (k->oop_is_typeArray()) { + BasicType type = typeArrayKlass::cast(k->as_klassOop())->element_type(); + comp_mirror = Universe::java_mirror(type); + assert(comp_mirror.not_null(), "must have primitive mirror"); + } else if (k->oop_is_objArray()) { + klassOop element_klass = objArrayKlass::cast(k->as_klassOop())->element_klass(); + if (element_klass != NULL + && (Klass::cast(element_klass)->oop_is_instance() || + Klass::cast(element_klass)->oop_is_javaArray())) { + comp_mirror = Klass::cast(element_klass)->java_mirror(); + assert(comp_mirror.not_null(), "must have element mirror"); + } + // else some object array internal to the VM, like systemObjArrayKlassObj + } + if (comp_mirror.not_null()) { + // Two-way link between the array klass and its component mirror: + arrayKlass::cast(k->as_klassOop())->set_component_mirror(comp_mirror()); + set_array_klass(comp_mirror(), k->as_klassOop()); + } + } + return mirror(); + } else { + return NULL; + } +} + + +oop java_lang_Class::create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS) { + // This should be improved by adding a field at the Java level or by + // introducing a new VM klass (see comment in ClassFileParser) + oop java_class = instanceKlass::cast(SystemDictionary::class_klass())->allocate_permanent_instance(CHECK_0); + if (type != T_VOID) { + klassOop aklass = Universe::typeArrayKlassObj(type); + assert(aklass != NULL, "correct bootstrap"); + set_array_klass(java_class, aklass); + } + return java_class; +} + + +klassOop java_lang_Class::as_klassOop(oop java_class) { + //%note memory_2 + klassOop k = klassOop(java_class->obj_field(klass_offset)); + assert(k == NULL || k->is_klass(), "type check"); + return k; +} + + +klassOop java_lang_Class::array_klass(oop java_class) { + klassOop k = klassOop(java_class->obj_field(array_klass_offset)); + assert(k == NULL || k->is_klass() && Klass::cast(k)->oop_is_javaArray(), "should be array klass"); + return k; +} + + +void java_lang_Class::set_array_klass(oop java_class, klassOop klass) { + assert(klass->is_klass() && Klass::cast(klass)->oop_is_javaArray(), "should be array klass"); + java_class->obj_field_put(array_klass_offset, klass); +} + + +methodOop java_lang_Class::resolved_constructor(oop java_class) { + oop constructor = java_class->obj_field(resolved_constructor_offset); + assert(constructor == NULL || constructor->is_method(), "should be method"); + return methodOop(constructor); +} + + +void java_lang_Class::set_resolved_constructor(oop java_class, methodOop constructor) { + assert(constructor->is_method(), "should be method"); + java_class->obj_field_put(resolved_constructor_offset, constructor); +} + + +bool java_lang_Class::is_primitive(oop java_class) { + klassOop k = klassOop(java_class->obj_field(klass_offset)); + return k == NULL; +} + + +BasicType java_lang_Class::primitive_type(oop java_class) { + assert(java_lang_Class::is_primitive(java_class), "just checking"); + klassOop ak = klassOop(java_class->obj_field(array_klass_offset)); + BasicType type = T_VOID; + if (ak != NULL) { + // Note: create_basic_type_mirror above initializes ak to a non-null value. + type = arrayKlass::cast(ak)->element_type(); + } else { + assert(java_class == Universe::void_mirror(), "only valid non-array primitive"); + } + assert(Universe::java_mirror(type) == java_class, "must be consistent"); + return type; +} + + +oop java_lang_Class::primitive_mirror(BasicType t) { + oop mirror = Universe::java_mirror(t); + assert(mirror != NULL && mirror->is_a(SystemDictionary::class_klass()), "must be a Class"); + assert(java_lang_Class::is_primitive(mirror), "must be primitive"); + return mirror; +} + +bool java_lang_Class::offsets_computed = false; +int java_lang_Class::classRedefinedCount_offset = -1; + +void java_lang_Class::compute_offsets() { + assert(!offsets_computed, "offsets should be initialized only once"); + offsets_computed = true; + + klassOop k = SystemDictionary::class_klass(); + // The classRedefinedCount field is only present starting in 1.5, + // so don't go fatal. + COMPUTE_OPTIONAL_OFFSET("java.lang.Class", classRedefinedCount_offset, + k, vmSymbols::classRedefinedCount_name(), vmSymbols::int_signature()); +} + +int java_lang_Class::classRedefinedCount(oop the_class_mirror) { + if (!JDK_Version::is_gte_jdk15x_version() + || classRedefinedCount_offset == -1) { + // The classRedefinedCount field is only present starting in 1.5. + // If we don't have an offset for it then just return -1 as a marker. + return -1; + } + + return the_class_mirror->int_field(classRedefinedCount_offset); +} + +void java_lang_Class::set_classRedefinedCount(oop the_class_mirror, int value) { + if (!JDK_Version::is_gte_jdk15x_version() + || classRedefinedCount_offset == -1) { + // The classRedefinedCount field is only present starting in 1.5. + // If we don't have an offset for it then nothing to set. + return; + } + + the_class_mirror->int_field_put(classRedefinedCount_offset, value); +} + + +// Note: JDK1.1 and before had a privateInfo_offset field which was used for the +// platform thread structure, and a eetop offset which was used for thread +// local storage (and unused by the HotSpot VM). In JDK1.2 the two structures +// merged, so in the HotSpot VM we just use the eetop field for the thread +// instead of the privateInfo_offset. +// +// Note: The stackSize field is only present starting in 1.4. + +int java_lang_Thread::_name_offset = 0; +int java_lang_Thread::_group_offset = 0; +int java_lang_Thread::_contextClassLoader_offset = 0; +int java_lang_Thread::_inheritedAccessControlContext_offset = 0; +int java_lang_Thread::_priority_offset = 0; +int java_lang_Thread::_eetop_offset = 0; +int java_lang_Thread::_daemon_offset = 0; +int java_lang_Thread::_stillborn_offset = 0; +int java_lang_Thread::_stackSize_offset = 0; +int java_lang_Thread::_tid_offset = 0; +int java_lang_Thread::_thread_status_offset = 0; +int java_lang_Thread::_park_blocker_offset = 0; +int java_lang_Thread::_park_event_offset = 0 ; + + +void java_lang_Thread::compute_offsets() { + assert(_group_offset == 0, "offsets should be initialized only once"); + + klassOop k = SystemDictionary::thread_klass(); + COMPUTE_OFFSET("java.lang.Thread", _name_offset, k, vmSymbols::name_name(), vmSymbols::char_array_signature()); + COMPUTE_OFFSET("java.lang.Thread", _group_offset, k, vmSymbols::group_name(), vmSymbols::threadgroup_signature()); + COMPUTE_OFFSET("java.lang.Thread", _contextClassLoader_offset, k, vmSymbols::contextClassLoader_name(), vmSymbols::classloader_signature()); + COMPUTE_OFFSET("java.lang.Thread", _inheritedAccessControlContext_offset, k, vmSymbols::inheritedAccessControlContext_name(), vmSymbols::accesscontrolcontext_signature()); + COMPUTE_OFFSET("java.lang.Thread", _priority_offset, k, vmSymbols::priority_name(), vmSymbols::int_signature()); + COMPUTE_OFFSET("java.lang.Thread", _daemon_offset, k, vmSymbols::daemon_name(), vmSymbols::bool_signature()); + COMPUTE_OFFSET("java.lang.Thread", _eetop_offset, k, vmSymbols::eetop_name(), vmSymbols::long_signature()); + COMPUTE_OFFSET("java.lang.Thread", _stillborn_offset, k, vmSymbols::stillborn_name(), vmSymbols::bool_signature()); + // The stackSize field is only present starting in 1.4, so don't go fatal. + COMPUTE_OPTIONAL_OFFSET("java.lang.Thread", _stackSize_offset, k, vmSymbols::stackSize_name(), vmSymbols::long_signature()); + // The tid and thread_status fields are only present starting in 1.5, so don't go fatal. + COMPUTE_OPTIONAL_OFFSET("java.lang.Thread", _tid_offset, k, vmSymbols::thread_id_name(), vmSymbols::long_signature()); + COMPUTE_OPTIONAL_OFFSET("java.lang.Thread", _thread_status_offset, k, vmSymbols::thread_status_name(), vmSymbols::int_signature()); + // The parkBlocker field is only present starting in 1.6, so don't go fatal. + COMPUTE_OPTIONAL_OFFSET("java.lang.Thread", _park_blocker_offset, k, vmSymbols::park_blocker_name(), vmSymbols::object_signature()); + COMPUTE_OPTIONAL_OFFSET("java.lang.Thread", _park_event_offset, k, vmSymbols::park_event_name(), + vmSymbols::long_signature()); +} + + +JavaThread* java_lang_Thread::thread(oop java_thread) { + return (JavaThread*) java_thread->obj_field(_eetop_offset); +} + + +void java_lang_Thread::set_thread(oop java_thread, JavaThread* thread) { + // We are storing a JavaThread* (malloc'ed data) into a long field in the thread + // object. The store has to be 64-bit wide so we use a pointer store, but we + // cannot call oopDesc::obj_field_put since it includes a write barrier! + oop* addr = java_thread->obj_field_addr(_eetop_offset); + *addr = (oop) thread; +} + + +typeArrayOop java_lang_Thread::name(oop java_thread) { + oop name = java_thread->obj_field(_name_offset); + assert(name == NULL || (name->is_typeArray() && typeArrayKlass::cast(name->klass())->element_type() == T_CHAR), "just checking"); + return typeArrayOop(name); +} + + +void java_lang_Thread::set_name(oop java_thread, typeArrayOop name) { + assert(java_thread->obj_field(_name_offset) == NULL, "name should be NULL"); + java_thread->obj_field_put(_name_offset, name); +} + + +ThreadPriority java_lang_Thread::priority(oop java_thread) { + return (ThreadPriority)java_thread->int_field(_priority_offset); +} + + +void java_lang_Thread::set_priority(oop java_thread, ThreadPriority priority) { + java_thread->int_field_put(_priority_offset, priority); +} + + +oop java_lang_Thread::threadGroup(oop java_thread) { + return java_thread->obj_field(_group_offset); +} + + +bool java_lang_Thread::is_stillborn(oop java_thread) { + return java_thread->bool_field(_stillborn_offset) != 0; +} + + +// We never have reason to turn the stillborn bit off +void java_lang_Thread::set_stillborn(oop java_thread) { + java_thread->bool_field_put(_stillborn_offset, true); +} + + +bool java_lang_Thread::is_alive(oop java_thread) { + JavaThread* thr = java_lang_Thread::thread(java_thread); + return (thr != NULL); +} + + +bool java_lang_Thread::is_daemon(oop java_thread) { + return java_thread->bool_field(_daemon_offset) != 0; +} + + +void java_lang_Thread::set_daemon(oop java_thread) { + java_thread->bool_field_put(_daemon_offset, true); +} + +oop java_lang_Thread::context_class_loader(oop java_thread) { + return java_thread->obj_field(_contextClassLoader_offset); +} + +oop java_lang_Thread::inherited_access_control_context(oop java_thread) { + return java_thread->obj_field(_inheritedAccessControlContext_offset); +} + + +jlong java_lang_Thread::stackSize(oop java_thread) { + // The stackSize field is only present starting in 1.4 + if (_stackSize_offset > 0) { + assert(JDK_Version::is_gte_jdk14x_version(), "sanity check"); + return java_thread->long_field(_stackSize_offset); + } else { + return 0; + } +} + +// Write the thread status value to threadStatus field in java.lang.Thread java class. +void java_lang_Thread::set_thread_status(oop java_thread, + java_lang_Thread::ThreadStatus status) { + assert(JavaThread::current()->thread_state() == _thread_in_vm, "Java Thread is not running in vm"); + // The threadStatus is only present starting in 1.5 + if (_thread_status_offset > 0) { + java_thread->int_field_put(_thread_status_offset, status); + } +} + +// Read thread status value from threadStatus field in java.lang.Thread java class. +java_lang_Thread::ThreadStatus java_lang_Thread::get_thread_status(oop java_thread) { + assert(Thread::current()->is_VM_thread() || + JavaThread::current()->thread_state() == _thread_in_vm, + "Java Thread is not running in vm"); + // The threadStatus is only present starting in 1.5 + if (_thread_status_offset > 0) { + return (java_lang_Thread::ThreadStatus)java_thread->int_field(_thread_status_offset); + } else { + // All we can easily figure out is if it is alive, but that is + // enough info for a valid unknown status. + // These aren't restricted to valid set ThreadStatus values, so + // use JVMTI values and cast. + JavaThread* thr = java_lang_Thread::thread(java_thread); + if (thr == NULL) { + // the thread hasn't run yet or is in the process of exiting + return NEW; + } + return (java_lang_Thread::ThreadStatus)JVMTI_THREAD_STATE_ALIVE; + } +} + + +jlong java_lang_Thread::thread_id(oop java_thread) { + // The thread ID field is only present starting in 1.5 + if (_tid_offset > 0) { + return java_thread->long_field(_tid_offset); + } else { + return 0; + } +} + +oop java_lang_Thread::park_blocker(oop java_thread) { + assert(JDK_Version::supports_thread_park_blocker() && _park_blocker_offset != 0, + "Must support parkBlocker field"); + + if (_park_blocker_offset > 0) { + return java_thread->obj_field(_park_blocker_offset); + } + + return NULL; +} + +jlong java_lang_Thread::park_event(oop java_thread) { + if (_park_event_offset > 0) { + return java_thread->long_field(_park_event_offset); + } + return 0; +} + +bool java_lang_Thread::set_park_event(oop java_thread, jlong ptr) { + if (_park_event_offset > 0) { + java_thread->long_field_put(_park_event_offset, ptr); + return true; + } + return false; +} + + +const char* java_lang_Thread::thread_status_name(oop java_thread) { + assert(JDK_Version::is_gte_jdk15x_version() && _thread_status_offset != 0, "Must have thread status"); + ThreadStatus status = (java_lang_Thread::ThreadStatus)java_thread->int_field(_thread_status_offset); + switch (status) { + case NEW : return "NEW"; + case RUNNABLE : return "RUNNABLE"; + case SLEEPING : return "TIMED_WAITING (sleeping)"; + case IN_OBJECT_WAIT : return "WAITING (on object monitor)"; + case IN_OBJECT_WAIT_TIMED : return "TIMED_WAITING (on object monitor)"; + case PARKED : return "WAITING (parking)"; + case PARKED_TIMED : return "TIMED_WAITING (parking)"; + case BLOCKED_ON_MONITOR_ENTER : return "BLOCKED (on object monitor)"; + case TERMINATED : return "TERMINATED"; + default : return "UNKNOWN"; + }; +} +int java_lang_ThreadGroup::_parent_offset = 0; +int java_lang_ThreadGroup::_name_offset = 0; +int java_lang_ThreadGroup::_threads_offset = 0; +int java_lang_ThreadGroup::_groups_offset = 0; +int java_lang_ThreadGroup::_maxPriority_offset = 0; +int java_lang_ThreadGroup::_destroyed_offset = 0; +int java_lang_ThreadGroup::_daemon_offset = 0; +int java_lang_ThreadGroup::_vmAllowSuspension_offset = 0; +int java_lang_ThreadGroup::_nthreads_offset = 0; +int java_lang_ThreadGroup::_ngroups_offset = 0; + +oop java_lang_ThreadGroup::parent(oop java_thread_group) { + assert(java_thread_group->is_oop(), "thread group must be oop"); + return java_thread_group->obj_field(_parent_offset); +} + +// ("name as oop" accessor is not necessary) + +typeArrayOop java_lang_ThreadGroup::name(oop java_thread_group) { + oop name = java_thread_group->obj_field(_name_offset); + // ThreadGroup.name can be null + return name == NULL ? (typeArrayOop)NULL : java_lang_String::value(name); +} + +int java_lang_ThreadGroup::nthreads(oop java_thread_group) { + assert(java_thread_group->is_oop(), "thread group must be oop"); + return java_thread_group->int_field(_nthreads_offset); +} + +objArrayOop java_lang_ThreadGroup::threads(oop java_thread_group) { + oop threads = java_thread_group->obj_field(_threads_offset); + assert(threads != NULL, "threadgroups should have threads"); + assert(threads->is_objArray(), "just checking"); // Todo: Add better type checking code + return objArrayOop(threads); +} + +int java_lang_ThreadGroup::ngroups(oop java_thread_group) { + assert(java_thread_group->is_oop(), "thread group must be oop"); + return java_thread_group->int_field(_ngroups_offset); +} + +objArrayOop java_lang_ThreadGroup::groups(oop java_thread_group) { + oop groups = java_thread_group->obj_field(_groups_offset); + assert(groups == NULL || groups->is_objArray(), "just checking"); // Todo: Add better type checking code + return objArrayOop(groups); +} + +ThreadPriority java_lang_ThreadGroup::maxPriority(oop java_thread_group) { + assert(java_thread_group->is_oop(), "thread group must be oop"); + return (ThreadPriority) java_thread_group->int_field(_maxPriority_offset); +} + +bool java_lang_ThreadGroup::is_destroyed(oop java_thread_group) { + assert(java_thread_group->is_oop(), "thread group must be oop"); + return java_thread_group->bool_field(_destroyed_offset) != 0; +} + +bool java_lang_ThreadGroup::is_daemon(oop java_thread_group) { + assert(java_thread_group->is_oop(), "thread group must be oop"); + return java_thread_group->bool_field(_daemon_offset) != 0; +} + +bool java_lang_ThreadGroup::is_vmAllowSuspension(oop java_thread_group) { + assert(java_thread_group->is_oop(), "thread group must be oop"); + return java_thread_group->bool_field(_vmAllowSuspension_offset) != 0; +} + +void java_lang_ThreadGroup::compute_offsets() { + assert(_parent_offset == 0, "offsets should be initialized only once"); + + klassOop k = SystemDictionary::threadGroup_klass(); + + COMPUTE_OFFSET("java.lang.ThreadGroup", _parent_offset, k, vmSymbols::parent_name(), vmSymbols::threadgroup_signature()); + COMPUTE_OFFSET("java.lang.ThreadGroup", _name_offset, k, vmSymbols::name_name(), vmSymbols::string_signature()); + COMPUTE_OFFSET("java.lang.ThreadGroup", _threads_offset, k, vmSymbols::threads_name(), vmSymbols::thread_array_signature()); + COMPUTE_OFFSET("java.lang.ThreadGroup", _groups_offset, k, vmSymbols::groups_name(), vmSymbols::threadgroup_array_signature()); + COMPUTE_OFFSET("java.lang.ThreadGroup", _maxPriority_offset, k, vmSymbols::maxPriority_name(), vmSymbols::int_signature()); + COMPUTE_OFFSET("java.lang.ThreadGroup", _destroyed_offset, k, vmSymbols::destroyed_name(), vmSymbols::bool_signature()); + COMPUTE_OFFSET("java.lang.ThreadGroup", _daemon_offset, k, vmSymbols::daemon_name(), vmSymbols::bool_signature()); + COMPUTE_OFFSET("java.lang.ThreadGroup", _vmAllowSuspension_offset, k, vmSymbols::vmAllowSuspension_name(), vmSymbols::bool_signature()); + COMPUTE_OFFSET("java.lang.ThreadGroup", _nthreads_offset, k, vmSymbols::nthreads_name(), vmSymbols::int_signature()); + COMPUTE_OFFSET("java.lang.ThreadGroup", _ngroups_offset, k, vmSymbols::ngroups_name(), vmSymbols::int_signature()); +} + +oop java_lang_Throwable::backtrace(oop throwable) { + return throwable->obj_field_acquire(backtrace_offset); +} + + +void java_lang_Throwable::set_backtrace(oop throwable, oop value) { + throwable->release_obj_field_put(backtrace_offset, value); +} + + +oop java_lang_Throwable::message(oop throwable) { + return throwable->obj_field(detailMessage_offset); +} + + +oop java_lang_Throwable::message(Handle throwable) { + return throwable->obj_field(detailMessage_offset); +} + + +void java_lang_Throwable::set_message(oop throwable, oop value) { + throwable->obj_field_put(detailMessage_offset, value); +} + + +void java_lang_Throwable::clear_stacktrace(oop throwable) { + assert(JDK_Version::is_gte_jdk14x_version(), "should only be called in >= 1.4"); + throwable->obj_field_put(stackTrace_offset, NULL); +} + + +void java_lang_Throwable::print(oop throwable, outputStream* st) { + ResourceMark rm; + klassOop k = throwable->klass(); + assert(k != NULL, "just checking"); + st->print("%s", instanceKlass::cast(k)->external_name()); + oop msg = message(throwable); + if (msg != NULL) { + st->print(": %s", java_lang_String::as_utf8_string(msg)); + } +} + + +void java_lang_Throwable::print(Handle throwable, outputStream* st) { + ResourceMark rm; + klassOop k = throwable->klass(); + assert(k != NULL, "just checking"); + st->print("%s", instanceKlass::cast(k)->external_name()); + oop msg = message(throwable); + if (msg != NULL) { + st->print(": %s", java_lang_String::as_utf8_string(msg)); + } +} + +// Print stack trace element to resource allocated buffer +char* java_lang_Throwable::print_stack_element_to_buffer(methodOop method, int bci) { + // Get strings and string lengths + instanceKlass* klass = instanceKlass::cast(method->method_holder()); + const char* klass_name = klass->external_name(); + int buf_len = (int)strlen(klass_name); + char* source_file_name; + if (klass->source_file_name() == NULL) { + source_file_name = NULL; + } else { + source_file_name = klass->source_file_name()->as_C_string(); + buf_len += (int)strlen(source_file_name); + } + char* method_name = method->name()->as_C_string(); + buf_len += (int)strlen(method_name); + + // Allocate temporary buffer with extra space for formatting and line number + char* buf = NEW_RESOURCE_ARRAY(char, buf_len + 64); + + // Print stack trace line in buffer + sprintf(buf, "\tat %s.%s", klass_name, method_name); + if (method->is_native()) { + strcat(buf, "(Native Method)"); + } else { + int line_number = method->line_number_from_bci(bci); + if (source_file_name != NULL && (line_number != -1)) { + // Sourcename and linenumber + sprintf(buf + (int)strlen(buf), "(%s:%d)", source_file_name, line_number); + } else if (source_file_name != NULL) { + // Just sourcename + sprintf(buf + (int)strlen(buf), "(%s)", source_file_name); + } else { + // Neither soucename and linenumber + sprintf(buf + (int)strlen(buf), "(Unknown Source)"); + } + nmethod* nm = method->code(); + if (WizardMode && nm != NULL) { + sprintf(buf + (int)strlen(buf), "(nmethod %#x)", nm); + } + } + + return buf; +} + + +void java_lang_Throwable::print_stack_element(Handle stream, methodOop method, int bci) { + ResourceMark rm; + char* buf = print_stack_element_to_buffer(method, bci); + print_to_stream(stream, buf); +} + +void java_lang_Throwable::print_stack_element(outputStream *st, methodOop method, int bci) { + ResourceMark rm; + char* buf = print_stack_element_to_buffer(method, bci); + st->print_cr("%s", buf); +} + +void java_lang_Throwable::print_to_stream(Handle stream, const char* str) { + if (stream.is_null()) { + tty->print_cr("%s", str); + } else { + EXCEPTION_MARK; + JavaValue result(T_VOID); + Handle arg (THREAD, oopFactory::new_charArray(str, THREAD)); + if (!HAS_PENDING_EXCEPTION) { + JavaCalls::call_virtual(&result, + stream, + KlassHandle(THREAD, stream->klass()), + vmSymbolHandles::println_name(), + vmSymbolHandles::char_array_void_signature(), + arg, + THREAD); + } + // Ignore any exceptions. we are in the middle of exception handling. Same as classic VM. + if (HAS_PENDING_EXCEPTION) CLEAR_PENDING_EXCEPTION; + } + +} + + +const char* java_lang_Throwable::no_stack_trace_message() { + return "\t<>"; +} + + +// Currently used only for exceptions occurring during startup +void java_lang_Throwable::print_stack_trace(oop throwable, outputStream* st) { + Thread *THREAD = Thread::current(); + Handle h_throwable(THREAD, throwable); + while (h_throwable.not_null()) { + objArrayHandle result (THREAD, objArrayOop(backtrace(h_throwable()))); + if (result.is_null()) { + st->print_cr(no_stack_trace_message()); + return; + } + + while (result.not_null()) { + objArrayHandle methods (THREAD, + objArrayOop(result->obj_at(trace_methods_offset))); + typeArrayHandle bcis (THREAD, + typeArrayOop(result->obj_at(trace_bcis_offset))); + + if (methods.is_null() || bcis.is_null()) { + st->print_cr(no_stack_trace_message()); + return; + } + + int length = methods()->length(); + for (int index = 0; index < length; index++) { + methodOop method = methodOop(methods()->obj_at(index)); + if (method == NULL) goto handle_cause; + int bci = bcis->ushort_at(index); + print_stack_element(st, method, bci); + } + result = objArrayHandle(THREAD, objArrayOop(result->obj_at(trace_next_offset))); + } + handle_cause: + { + EXCEPTION_MARK; + JavaValue result(T_OBJECT); + JavaCalls::call_virtual(&result, + h_throwable, + KlassHandle(THREAD, h_throwable->klass()), + vmSymbolHandles::getCause_name(), + vmSymbolHandles::void_throwable_signature(), + THREAD); + // Ignore any exceptions. we are in the middle of exception handling. Same as classic VM. + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + h_throwable = Handle(); + } else { + h_throwable = Handle(THREAD, (oop) result.get_jobject()); + if (h_throwable.not_null()) { + st->print("Caused by: "); + print(h_throwable, st); + st->cr(); + } + } + } + } +} + + +void java_lang_Throwable::print_stack_trace(oop throwable, oop print_stream) { + // Note: this is no longer used in Merlin, but we support it for compatibility. + Thread *thread = Thread::current(); + Handle stream(thread, print_stream); + objArrayHandle result (thread, objArrayOop(backtrace(throwable))); + if (result.is_null()) { + print_to_stream(stream, no_stack_trace_message()); + return; + } + + while (result.not_null()) { + objArrayHandle methods (thread, + objArrayOop(result->obj_at(trace_methods_offset))); + typeArrayHandle bcis (thread, + typeArrayOop(result->obj_at(trace_bcis_offset))); + + if (methods.is_null() || bcis.is_null()) { + print_to_stream(stream, no_stack_trace_message()); + return; + } + + int length = methods()->length(); + for (int index = 0; index < length; index++) { + methodOop method = methodOop(methods()->obj_at(index)); + if (method == NULL) return; + int bci = bcis->ushort_at(index); + print_stack_element(stream, method, bci); + } + result = objArrayHandle(thread, objArrayOop(result->obj_at(trace_next_offset))); + } +} + +// This class provides a simple wrapper over the internal structure of +// exception backtrace to insulate users of the backtrace from needing +// to know what it looks like. +class BacktraceBuilder: public StackObj { + private: + Handle _backtrace; + objArrayOop _head; + objArrayOop _methods; + typeArrayOop _bcis; + int _index; + bool _dirty; + bool _done; + No_Safepoint_Verifier _nsv; + + public: + + enum { + trace_methods_offset = java_lang_Throwable::trace_methods_offset, + trace_bcis_offset = java_lang_Throwable::trace_bcis_offset, + trace_next_offset = java_lang_Throwable::trace_next_offset, + trace_size = java_lang_Throwable::trace_size, + trace_chunk_size = java_lang_Throwable::trace_chunk_size + }; + + // constructor for new backtrace + BacktraceBuilder(TRAPS): _methods(NULL), _bcis(NULL), _head(NULL) { + expand(CHECK); + _backtrace = _head; + _index = 0; + _dirty = false; + _done = false; + } + + void flush() { + if (_dirty && _methods != NULL) { + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt"); + bs->write_ref_array(MemRegion((HeapWord*)_methods->obj_at_addr(0), + _methods->length() * HeapWordsPerOop)); + _dirty = false; + } + } + + void expand(TRAPS) { + flush(); + + objArrayHandle old_head(THREAD, _head); + Pause_No_Safepoint_Verifier pnsv(&_nsv); + + objArrayOop head = oopFactory::new_objectArray(trace_size, CHECK); + objArrayHandle new_head(THREAD, head); + + objArrayOop methods = oopFactory::new_objectArray(trace_chunk_size, CHECK); + objArrayHandle new_methods(THREAD, methods); + + typeArrayOop bcis = oopFactory::new_shortArray(trace_chunk_size, CHECK); + typeArrayHandle new_bcis(THREAD, bcis); + + if (!old_head.is_null()) { + old_head->obj_at_put(trace_next_offset, new_head()); + } + new_head->obj_at_put(trace_methods_offset, new_methods()); + new_head->obj_at_put(trace_bcis_offset, new_bcis()); + + _head = new_head(); + _methods = new_methods(); + _bcis = new_bcis(); + _index = 0; + } + + oop backtrace() { + flush(); + return _backtrace(); + } + + inline void push(methodOop method, short bci, TRAPS) { + if (_index >= trace_chunk_size) { + methodHandle mhandle(THREAD, method); + expand(CHECK); + method = mhandle(); + } + + // _methods->obj_at_put(_index, method); + *_methods->obj_at_addr(_index) = method; + _bcis->ushort_at_put(_index, bci); + _index++; + _dirty = true; + } + + methodOop current_method() { + assert(_index >= 0 && _index < trace_chunk_size, "out of range"); + return methodOop(_methods->obj_at(_index)); + } + + jushort current_bci() { + assert(_index >= 0 && _index < trace_chunk_size, "out of range"); + return _bcis->ushort_at(_index); + } +}; + + +void java_lang_Throwable::fill_in_stack_trace(Handle throwable, TRAPS) { + if (!StackTraceInThrowable) return; + ResourceMark rm(THREAD); + + // Start out by clearing the backtrace for this object, in case the VM + // runs out of memory while allocating the stack trace + set_backtrace(throwable(), NULL); + if (JDK_Version::is_gte_jdk14x_version()) { + // New since 1.4, clear lazily constructed Java level stacktrace if + // refilling occurs + clear_stacktrace(throwable()); + } + + int max_depth = MaxJavaStackTraceDepth; + JavaThread* thread = (JavaThread*)THREAD; + BacktraceBuilder bt(CHECK); + + // Instead of using vframe directly, this version of fill_in_stack_trace + // basically handles everything by hand. This significantly improved the + // speed of this method call up to 28.5% on Solaris sparc. 27.1% on Windows. + // See bug 6333838 for more details. + // The "ASSERT" here is to verify this method generates the exactly same stack + // trace as utilizing vframe. +#ifdef ASSERT + vframeStream st(thread); + methodHandle st_method(THREAD, st.method()); +#endif + int total_count = 0; + RegisterMap map(thread, false); + int decode_offset = 0; + nmethod* nm = NULL; + bool skip_fillInStackTrace_check = false; + bool skip_throwableInit_check = false; + + for (frame fr = thread->last_frame(); max_depth != total_count;) { + methodOop method = NULL; + int bci = 0; + + // Compiled java method case. + if (decode_offset != 0) { + DebugInfoReadStream stream(nm, decode_offset); + decode_offset = stream.read_int(); + method = (methodOop)nm->oop_at(stream.read_int()); + bci = stream.read_bci(); + } else { + if (fr.is_first_frame()) break; + address pc = fr.pc(); + if (fr.is_interpreted_frame()) { + intptr_t bcx = fr.interpreter_frame_bcx(); + method = fr.interpreter_frame_method(); + bci = fr.is_bci(bcx) ? bcx : method->bci_from((address)bcx); + fr = fr.sender(&map); + } else { + CodeBlob* cb = fr.cb(); + // HMMM QQQ might be nice to have frame return nm as NULL if cb is non-NULL + // but non nmethod + fr = fr.sender(&map); + if (cb == NULL || !cb->is_nmethod()) { + continue; + } + nm = (nmethod*)cb; + if (nm->method()->is_native()) { + method = nm->method(); + bci = 0; + } else { + PcDesc* pd = nm->pc_desc_at(pc); + decode_offset = pd->scope_decode_offset(); + // if decode_offset is not equal to 0, it will execute the + // "compiled java method case" at the beginning of the loop. + continue; + } + } + } +#ifdef ASSERT + assert(st_method() == method && st.bci() == bci, + "Wrong stack trace"); + st.next(); + // vframeStream::method isn't GC-safe so store off a copy + // of the methodOop in case we GC. + if (!st.at_end()) { + st_method = st.method(); + } +#endif + if (!skip_fillInStackTrace_check) { + // check "fillInStackTrace" only once, so we negate the flag + // after the first time check. + skip_fillInStackTrace_check = true; + if (method->name() == vmSymbols::fillInStackTrace_name()) { + continue; + } + } + // skip methods of the exceptions klass. If there is methods + // that belongs to a superclass of the exception we are going to skipping + // them in stack trace. This is simlar to classic VM. + if (!skip_throwableInit_check) { + if (method->name() == vmSymbols::object_initializer_name() && + throwable->is_a(method->method_holder())) { + continue; + } else { + // if no "Throwable.init()" method found, we stop checking it next time. + skip_throwableInit_check = true; + } + } + bt.push(method, bci, CHECK); + total_count++; + } + + // Put completed stack trace into throwable object + set_backtrace(throwable(), bt.backtrace()); +} + +void java_lang_Throwable::fill_in_stack_trace(Handle throwable) { + // No-op if stack trace is disabled + if (!StackTraceInThrowable) { + return; + } + + // Disable stack traces for some preallocated out of memory errors + if (!Universe::should_fill_in_stack_trace(throwable)) { + return; + } + + PRESERVE_EXCEPTION_MARK; + + JavaThread* thread = JavaThread::active(); + fill_in_stack_trace(throwable, thread); + // ignore exceptions thrown during stack trace filling + CLEAR_PENDING_EXCEPTION; +} + +void java_lang_Throwable::allocate_backtrace(Handle throwable, TRAPS) { + // Allocate stack trace - backtrace is created but not filled in + + // No-op if stack trace is disabled + if (!StackTraceInThrowable) return; + + objArrayOop h_oop = oopFactory::new_objectArray(trace_size, CHECK); + objArrayHandle backtrace (THREAD, h_oop); + objArrayOop m_oop = oopFactory::new_objectArray(trace_chunk_size, CHECK); + objArrayHandle methods (THREAD, m_oop); + typeArrayOop b = oopFactory::new_shortArray(trace_chunk_size, CHECK); + typeArrayHandle bcis(THREAD, b); + + // backtrace has space for one chunk (next is NULL) + backtrace->obj_at_put(trace_methods_offset, methods()); + backtrace->obj_at_put(trace_bcis_offset, bcis()); + set_backtrace(throwable(), backtrace()); +} + + +void java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(Handle throwable) { + // Fill in stack trace into preallocated backtrace (no GC) + + // No-op if stack trace is disabled + if (!StackTraceInThrowable) return; + + assert(throwable->is_a(SystemDictionary::throwable_klass()), "sanity check"); + + oop backtrace = java_lang_Throwable::backtrace(throwable()); + assert(backtrace != NULL, "backtrace not preallocated"); + + oop m = objArrayOop(backtrace)->obj_at(trace_methods_offset); + objArrayOop methods = objArrayOop(m); + assert(methods != NULL && methods->length() > 0, "method array not preallocated"); + + oop b = objArrayOop(backtrace)->obj_at(trace_bcis_offset); + typeArrayOop bcis = typeArrayOop(b); + assert(bcis != NULL, "bci array not preallocated"); + + assert(methods->length() == bcis->length(), "method and bci arrays should match"); + + JavaThread* thread = JavaThread::current(); + ResourceMark rm(thread); + vframeStream st(thread); + + // Unlike fill_in_stack_trace we do not skip fillInStackTrace or throwable init + // methods as preallocated errors aren't created by "java" code. + + // fill in as much stack trace as possible + int max_chunks = MIN2(methods->length(), (int)MaxJavaStackTraceDepth); + int chunk_count = 0; + + for (;!st.at_end(); st.next()) { + // add element + bcis->ushort_at_put(chunk_count, st.bci()); + methods->obj_at_put(chunk_count, st.method()); + + chunk_count++; + + // Bail-out for deep stacks + if (chunk_count >= max_chunks) break; + } +} + + +int java_lang_Throwable::get_stack_trace_depth(oop throwable, TRAPS) { + if (throwable == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + objArrayOop chunk = objArrayOop(backtrace(throwable)); + int depth = 0; + if (chunk != NULL) { + // Iterate over chunks and count full ones + while (true) { + objArrayOop next = objArrayOop(chunk->obj_at(trace_next_offset)); + if (next == NULL) break; + depth += trace_chunk_size; + chunk = next; + } + assert(chunk != NULL && chunk->obj_at(trace_next_offset) == NULL, "sanity check"); + // Count element in remaining partial chunk + objArrayOop methods = objArrayOop(chunk->obj_at(trace_methods_offset)); + typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset)); + assert(methods != NULL && bcis != NULL, "sanity check"); + for (int i = 0; i < methods->length(); i++) { + if (methods->obj_at(i) == NULL) break; + depth++; + } + } + return depth; +} + + +oop java_lang_Throwable::get_stack_trace_element(oop throwable, int index, TRAPS) { + if (throwable == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + if (index < 0) { + THROW_(vmSymbols::java_lang_IndexOutOfBoundsException(), NULL); + } + // Compute how many chunks to skip and index into actual chunk + objArrayOop chunk = objArrayOop(backtrace(throwable)); + int skip_chunks = index / trace_chunk_size; + int chunk_index = index % trace_chunk_size; + while (chunk != NULL && skip_chunks > 0) { + chunk = objArrayOop(chunk->obj_at(trace_next_offset)); + skip_chunks--; + } + if (chunk == NULL) { + THROW_(vmSymbols::java_lang_IndexOutOfBoundsException(), NULL); + } + // Get method,bci from chunk + objArrayOop methods = objArrayOop(chunk->obj_at(trace_methods_offset)); + typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset)); + assert(methods != NULL && bcis != NULL, "sanity check"); + methodHandle method(THREAD, methodOop(methods->obj_at(chunk_index))); + int bci = bcis->ushort_at(chunk_index); + // Chunk can be partial full + if (method.is_null()) { + THROW_(vmSymbols::java_lang_IndexOutOfBoundsException(), NULL); + } + + oop element = java_lang_StackTraceElement::create(method, bci, CHECK_0); + return element; +} + +oop java_lang_StackTraceElement::create(methodHandle method, int bci, TRAPS) { + // SystemDictionary::stackTraceElement_klass() will be null for pre-1.4 JDKs + assert(JDK_Version::is_gte_jdk14x_version(), "should only be called in >= 1.4"); + + // Allocate java.lang.StackTraceElement instance + klassOop k = SystemDictionary::stackTraceElement_klass(); + instanceKlassHandle ik (THREAD, k); + if (ik->should_be_initialized()) { + ik->initialize(CHECK_0); + } + + Handle element = ik->allocate_instance_handle(CHECK_0); + // Fill in class name + ResourceMark rm(THREAD); + const char* str = instanceKlass::cast(method->method_holder())->external_name(); + oop classname = StringTable::intern((char*) str, CHECK_0); + java_lang_StackTraceElement::set_declaringClass(element(), classname); + // Fill in method name + oop methodname = StringTable::intern(method->name(), CHECK_0); + java_lang_StackTraceElement::set_methodName(element(), methodname); + // Fill in source file name + symbolOop source = instanceKlass::cast(method->method_holder())->source_file_name(); + oop filename = StringTable::intern(source, CHECK_0); + java_lang_StackTraceElement::set_fileName(element(), filename); + // File in source line number + int line_number; + if (method->is_native()) { + // Negative value different from -1 below, enabling Java code in + // class java.lang.StackTraceElement to distinguish "native" from + // "no LineNumberTable". + line_number = -2; + } else { + // Returns -1 if no LineNumberTable, and otherwise actual line number + line_number = method->line_number_from_bci(bci); + } + java_lang_StackTraceElement::set_lineNumber(element(), line_number); + + return element(); +} + + +void java_lang_reflect_AccessibleObject::compute_offsets() { + klassOop k = SystemDictionary::reflect_accessible_object_klass(); + COMPUTE_OFFSET("java.lang.reflect.AccessibleObject", override_offset, k, vmSymbols::override_name(), vmSymbols::bool_signature()); +} + +jboolean java_lang_reflect_AccessibleObject::override(oop reflect) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return (jboolean) reflect->bool_field(override_offset); +} + +void java_lang_reflect_AccessibleObject::set_override(oop reflect, jboolean value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + reflect->bool_field_put(override_offset, (int) value); +} + +void java_lang_reflect_Method::compute_offsets() { + klassOop k = SystemDictionary::reflect_method_klass(); + COMPUTE_OFFSET("java.lang.reflect.Method", clazz_offset, k, vmSymbols::clazz_name(), vmSymbols::class_signature()); + COMPUTE_OFFSET("java.lang.reflect.Method", name_offset, k, vmSymbols::name_name(), vmSymbols::string_signature()); + COMPUTE_OFFSET("java.lang.reflect.Method", returnType_offset, k, vmSymbols::returnType_name(), vmSymbols::class_signature()); + COMPUTE_OFFSET("java.lang.reflect.Method", parameterTypes_offset, k, vmSymbols::parameterTypes_name(), vmSymbols::class_array_signature()); + COMPUTE_OFFSET("java.lang.reflect.Method", exceptionTypes_offset, k, vmSymbols::exceptionTypes_name(), vmSymbols::class_array_signature()); + COMPUTE_OFFSET("java.lang.reflect.Method", slot_offset, k, vmSymbols::slot_name(), vmSymbols::int_signature()); + COMPUTE_OFFSET("java.lang.reflect.Method", modifiers_offset, k, vmSymbols::modifiers_name(), vmSymbols::int_signature()); + // The generic signature and annotations fields are only present in 1.5 + signature_offset = -1; + annotations_offset = -1; + parameter_annotations_offset = -1; + annotation_default_offset = -1; + COMPUTE_OPTIONAL_OFFSET("java.lang.reflect.Method", signature_offset, k, vmSymbols::signature_name(), vmSymbols::string_signature()); + COMPUTE_OPTIONAL_OFFSET("java.lang.reflect.Method", annotations_offset, k, vmSymbols::annotations_name(), vmSymbols::byte_array_signature()); + COMPUTE_OPTIONAL_OFFSET("java.lang.reflect.Method", parameter_annotations_offset, k, vmSymbols::parameter_annotations_name(), vmSymbols::byte_array_signature()); + COMPUTE_OPTIONAL_OFFSET("java.lang.reflect.Method", annotation_default_offset, k, vmSymbols::annotation_default_name(), vmSymbols::byte_array_signature()); +} + +Handle java_lang_reflect_Method::create(TRAPS) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + klassOop klass = SystemDictionary::reflect_method_klass(); + // This class is eagerly initialized during VM initialization, since we keep a refence + // to one of the methods + assert(instanceKlass::cast(klass)->is_initialized(), "must be initialized"); + return instanceKlass::cast(klass)->allocate_instance_handle(CHECK_NH); +} + +oop java_lang_reflect_Method::clazz(oop reflect) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return reflect->obj_field(clazz_offset); +} + +void java_lang_reflect_Method::set_clazz(oop reflect, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + reflect->obj_field_put(clazz_offset, value); +} + +int java_lang_reflect_Method::slot(oop reflect) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return reflect->int_field(slot_offset); +} + +void java_lang_reflect_Method::set_slot(oop reflect, int value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + reflect->int_field_put(slot_offset, value); +} + +oop java_lang_reflect_Method::name(oop method) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return method->obj_field(name_offset); +} + +void java_lang_reflect_Method::set_name(oop method, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + method->obj_field_put(name_offset, value); +} + +oop java_lang_reflect_Method::return_type(oop method) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return method->obj_field(returnType_offset); +} + +void java_lang_reflect_Method::set_return_type(oop method, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + method->obj_field_put(returnType_offset, value); +} + +oop java_lang_reflect_Method::parameter_types(oop method) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return method->obj_field(parameterTypes_offset); +} + +void java_lang_reflect_Method::set_parameter_types(oop method, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + method->obj_field_put(parameterTypes_offset, value); +} + +oop java_lang_reflect_Method::exception_types(oop method) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return method->obj_field(exceptionTypes_offset); +} + +void java_lang_reflect_Method::set_exception_types(oop method, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + method->obj_field_put(exceptionTypes_offset, value); +} + +int java_lang_reflect_Method::modifiers(oop method) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return method->int_field(modifiers_offset); +} + +void java_lang_reflect_Method::set_modifiers(oop method, int value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + method->int_field_put(modifiers_offset, value); +} + +bool java_lang_reflect_Method::has_signature_field() { + return (signature_offset >= 0); +} + +oop java_lang_reflect_Method::signature(oop method) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_signature_field(), "signature field must be present"); + return method->obj_field(signature_offset); +} + +void java_lang_reflect_Method::set_signature(oop method, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_signature_field(), "signature field must be present"); + method->obj_field_put(signature_offset, value); +} + +bool java_lang_reflect_Method::has_annotations_field() { + return (annotations_offset >= 0); +} + +oop java_lang_reflect_Method::annotations(oop method) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_annotations_field(), "annotations field must be present"); + return method->obj_field(annotations_offset); +} + +void java_lang_reflect_Method::set_annotations(oop method, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_annotations_field(), "annotations field must be present"); + method->obj_field_put(annotations_offset, value); +} + +bool java_lang_reflect_Method::has_parameter_annotations_field() { + return (parameter_annotations_offset >= 0); +} + +oop java_lang_reflect_Method::parameter_annotations(oop method) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_parameter_annotations_field(), "parameter annotations field must be present"); + return method->obj_field(parameter_annotations_offset); +} + +void java_lang_reflect_Method::set_parameter_annotations(oop method, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_parameter_annotations_field(), "parameter annotations field must be present"); + method->obj_field_put(parameter_annotations_offset, value); +} + +bool java_lang_reflect_Method::has_annotation_default_field() { + return (annotation_default_offset >= 0); +} + +oop java_lang_reflect_Method::annotation_default(oop method) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_annotation_default_field(), "annotation default field must be present"); + return method->obj_field(annotation_default_offset); +} + +void java_lang_reflect_Method::set_annotation_default(oop method, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_annotation_default_field(), "annotation default field must be present"); + method->obj_field_put(annotation_default_offset, value); +} + +void java_lang_reflect_Constructor::compute_offsets() { + klassOop k = SystemDictionary::reflect_constructor_klass(); + COMPUTE_OFFSET("java.lang.reflect.Constructor", clazz_offset, k, vmSymbols::clazz_name(), vmSymbols::class_signature()); + COMPUTE_OFFSET("java.lang.reflect.Constructor", parameterTypes_offset, k, vmSymbols::parameterTypes_name(), vmSymbols::class_array_signature()); + COMPUTE_OFFSET("java.lang.reflect.Constructor", exceptionTypes_offset, k, vmSymbols::exceptionTypes_name(), vmSymbols::class_array_signature()); + COMPUTE_OFFSET("java.lang.reflect.Constructor", slot_offset, k, vmSymbols::slot_name(), vmSymbols::int_signature()); + COMPUTE_OFFSET("java.lang.reflect.Constructor", modifiers_offset, k, vmSymbols::modifiers_name(), vmSymbols::int_signature()); + // The generic signature and annotations fields are only present in 1.5 + signature_offset = -1; + annotations_offset = -1; + parameter_annotations_offset = -1; + COMPUTE_OPTIONAL_OFFSET("java.lang.reflect.Constructor", signature_offset, k, vmSymbols::signature_name(), vmSymbols::string_signature()); + COMPUTE_OPTIONAL_OFFSET("java.lang.reflect.Constructor", annotations_offset, k, vmSymbols::annotations_name(), vmSymbols::byte_array_signature()); + COMPUTE_OPTIONAL_OFFSET("java.lang.reflect.Constructor", parameter_annotations_offset, k, vmSymbols::parameter_annotations_name(), vmSymbols::byte_array_signature()); +} + +Handle java_lang_reflect_Constructor::create(TRAPS) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + symbolHandle name = vmSymbolHandles::java_lang_reflect_Constructor(); + klassOop k = SystemDictionary::resolve_or_fail(name, true, CHECK_NH); + instanceKlassHandle klass (THREAD, k); + // Ensure it is initialized + klass->initialize(CHECK_NH); + return klass->allocate_instance_handle(CHECK_NH); +} + +oop java_lang_reflect_Constructor::clazz(oop reflect) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return reflect->obj_field(clazz_offset); +} + +void java_lang_reflect_Constructor::set_clazz(oop reflect, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + reflect->obj_field_put(clazz_offset, value); +} + +oop java_lang_reflect_Constructor::parameter_types(oop constructor) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return constructor->obj_field(parameterTypes_offset); +} + +void java_lang_reflect_Constructor::set_parameter_types(oop constructor, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + constructor->obj_field_put(parameterTypes_offset, value); +} + +oop java_lang_reflect_Constructor::exception_types(oop constructor) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return constructor->obj_field(exceptionTypes_offset); +} + +void java_lang_reflect_Constructor::set_exception_types(oop constructor, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + constructor->obj_field_put(exceptionTypes_offset, value); +} + +int java_lang_reflect_Constructor::slot(oop reflect) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return reflect->int_field(slot_offset); +} + +void java_lang_reflect_Constructor::set_slot(oop reflect, int value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + reflect->int_field_put(slot_offset, value); +} + +int java_lang_reflect_Constructor::modifiers(oop constructor) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return constructor->int_field(modifiers_offset); +} + +void java_lang_reflect_Constructor::set_modifiers(oop constructor, int value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + constructor->int_field_put(modifiers_offset, value); +} + +bool java_lang_reflect_Constructor::has_signature_field() { + return (signature_offset >= 0); +} + +oop java_lang_reflect_Constructor::signature(oop constructor) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_signature_field(), "signature field must be present"); + return constructor->obj_field(signature_offset); +} + +void java_lang_reflect_Constructor::set_signature(oop constructor, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_signature_field(), "signature field must be present"); + constructor->obj_field_put(signature_offset, value); +} + +bool java_lang_reflect_Constructor::has_annotations_field() { + return (annotations_offset >= 0); +} + +oop java_lang_reflect_Constructor::annotations(oop constructor) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_annotations_field(), "annotations field must be present"); + return constructor->obj_field(annotations_offset); +} + +void java_lang_reflect_Constructor::set_annotations(oop constructor, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_annotations_field(), "annotations field must be present"); + constructor->obj_field_put(annotations_offset, value); +} + +bool java_lang_reflect_Constructor::has_parameter_annotations_field() { + return (parameter_annotations_offset >= 0); +} + +oop java_lang_reflect_Constructor::parameter_annotations(oop method) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_parameter_annotations_field(), "parameter annotations field must be present"); + return method->obj_field(parameter_annotations_offset); +} + +void java_lang_reflect_Constructor::set_parameter_annotations(oop method, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_parameter_annotations_field(), "parameter annotations field must be present"); + method->obj_field_put(parameter_annotations_offset, value); +} + +void java_lang_reflect_Field::compute_offsets() { + klassOop k = SystemDictionary::reflect_field_klass(); + COMPUTE_OFFSET("java.lang.reflect.Field", clazz_offset, k, vmSymbols::clazz_name(), vmSymbols::class_signature()); + COMPUTE_OFFSET("java.lang.reflect.Field", name_offset, k, vmSymbols::name_name(), vmSymbols::string_signature()); + COMPUTE_OFFSET("java.lang.reflect.Field", type_offset, k, vmSymbols::type_name(), vmSymbols::class_signature()); + COMPUTE_OFFSET("java.lang.reflect.Field", slot_offset, k, vmSymbols::slot_name(), vmSymbols::int_signature()); + COMPUTE_OFFSET("java.lang.reflect.Field", modifiers_offset, k, vmSymbols::modifiers_name(), vmSymbols::int_signature()); + // The generic signature and annotations fields are only present in 1.5 + signature_offset = -1; + annotations_offset = -1; + COMPUTE_OPTIONAL_OFFSET("java.lang.reflect.Field", signature_offset, k, vmSymbols::signature_name(), vmSymbols::string_signature()); + COMPUTE_OPTIONAL_OFFSET("java.lang.reflect.Field", annotations_offset, k, vmSymbols::annotations_name(), vmSymbols::byte_array_signature()); +} + +Handle java_lang_reflect_Field::create(TRAPS) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + symbolHandle name = vmSymbolHandles::java_lang_reflect_Field(); + klassOop k = SystemDictionary::resolve_or_fail(name, true, CHECK_NH); + instanceKlassHandle klass (THREAD, k); + // Ensure it is initialized + klass->initialize(CHECK_NH); + return klass->allocate_instance_handle(CHECK_NH); +} + +oop java_lang_reflect_Field::clazz(oop reflect) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return reflect->obj_field(clazz_offset); +} + +void java_lang_reflect_Field::set_clazz(oop reflect, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + reflect->obj_field_put(clazz_offset, value); +} + +oop java_lang_reflect_Field::name(oop field) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return field->obj_field(name_offset); +} + +void java_lang_reflect_Field::set_name(oop field, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + field->obj_field_put(name_offset, value); +} + +oop java_lang_reflect_Field::type(oop field) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return field->obj_field(type_offset); +} + +void java_lang_reflect_Field::set_type(oop field, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + field->obj_field_put(type_offset, value); +} + +int java_lang_reflect_Field::slot(oop reflect) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return reflect->int_field(slot_offset); +} + +void java_lang_reflect_Field::set_slot(oop reflect, int value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + reflect->int_field_put(slot_offset, value); +} + +int java_lang_reflect_Field::modifiers(oop field) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return field->int_field(modifiers_offset); +} + +void java_lang_reflect_Field::set_modifiers(oop field, int value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + field->int_field_put(modifiers_offset, value); +} + +bool java_lang_reflect_Field::has_signature_field() { + return (signature_offset >= 0); +} + +oop java_lang_reflect_Field::signature(oop field) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_signature_field(), "signature field must be present"); + return field->obj_field(signature_offset); +} + +void java_lang_reflect_Field::set_signature(oop field, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_signature_field(), "signature field must be present"); + field->obj_field_put(signature_offset, value); +} + +bool java_lang_reflect_Field::has_annotations_field() { + return (annotations_offset >= 0); +} + +oop java_lang_reflect_Field::annotations(oop field) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_annotations_field(), "annotations field must be present"); + return field->obj_field(annotations_offset); +} + +void java_lang_reflect_Field::set_annotations(oop field, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + assert(has_annotations_field(), "annotations field must be present"); + field->obj_field_put(annotations_offset, value); +} + + +void sun_reflect_ConstantPool::compute_offsets() { + klassOop k = SystemDictionary::reflect_constant_pool_klass(); + // This null test can be removed post beta + if (k != NULL) { + COMPUTE_OFFSET("sun.reflect.ConstantPool", _cp_oop_offset, k, vmSymbols::constantPoolOop_name(), vmSymbols::object_signature()); + } +} + + +Handle sun_reflect_ConstantPool::create(TRAPS) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + klassOop k = SystemDictionary::reflect_constant_pool_klass(); + instanceKlassHandle klass (THREAD, k); + // Ensure it is initialized + klass->initialize(CHECK_NH); + return klass->allocate_instance_handle(CHECK_NH); +} + + +oop sun_reflect_ConstantPool::cp_oop(oop reflect) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + return reflect->obj_field(_cp_oop_offset); +} + + +void sun_reflect_ConstantPool::set_cp_oop(oop reflect, oop value) { + assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + reflect->obj_field_put(_cp_oop_offset, value); +} + +void sun_reflect_UnsafeStaticFieldAccessorImpl::compute_offsets() { + klassOop k = SystemDictionary::reflect_unsafe_static_field_accessor_impl_klass(); + // This null test can be removed post beta + if (k != NULL) { + COMPUTE_OFFSET("sun.reflect.UnsafeStaticFieldAccessorImpl", _base_offset, k, + vmSymbols::base_name(), vmSymbols::object_signature()); + } +} + +oop java_lang_boxing_object::initialize_and_allocate(klassOop k, TRAPS) { + instanceKlassHandle h (THREAD, k); + if (!h->is_initialized()) h->initialize(CHECK_0); + return h->allocate_instance(THREAD); +} + + +oop java_lang_boxing_object::create(BasicType type, jvalue* value, TRAPS) { + oop box; + switch (type) { + case T_BOOLEAN: + box = initialize_and_allocate(SystemDictionary::boolean_klass(), CHECK_0); + box->bool_field_put(value_offset, value->z); + break; + case T_CHAR: + box = initialize_and_allocate(SystemDictionary::char_klass(), CHECK_0); + box->char_field_put(value_offset, value->c); + break; + case T_FLOAT: + box = initialize_and_allocate(SystemDictionary::float_klass(), CHECK_0); + box->float_field_put(value_offset, value->f); + break; + case T_DOUBLE: + box = initialize_and_allocate(SystemDictionary::double_klass(), CHECK_0); + box->double_field_put(value_offset, value->d); + break; + case T_BYTE: + box = initialize_and_allocate(SystemDictionary::byte_klass(), CHECK_0); + box->byte_field_put(value_offset, value->b); + break; + case T_SHORT: + box = initialize_and_allocate(SystemDictionary::short_klass(), CHECK_0); + box->short_field_put(value_offset, value->s); + break; + case T_INT: + box = initialize_and_allocate(SystemDictionary::int_klass(), CHECK_0); + box->int_field_put(value_offset, value->i); + break; + case T_LONG: + box = initialize_and_allocate(SystemDictionary::long_klass(), CHECK_0); + box->long_field_put(value_offset, value->j); + break; + default: + return NULL; + } + return box; +} + + +BasicType java_lang_boxing_object::get_value(oop box, jvalue* value) { + klassOop k = box->klass(); + if (k == SystemDictionary::boolean_klass()) { + value->z = box->bool_field(value_offset); + return T_BOOLEAN; + } + if (k == SystemDictionary::char_klass()) { + value->c = box->char_field(value_offset); + return T_CHAR; + } + if (k == SystemDictionary::float_klass()) { + value->f = box->float_field(value_offset); + return T_FLOAT; + } + if (k == SystemDictionary::double_klass()) { + value->d = box->double_field(value_offset); + return T_DOUBLE; + } + if (k == SystemDictionary::byte_klass()) { + value->b = box->byte_field(value_offset); + return T_BYTE; + } + if (k == SystemDictionary::short_klass()) { + value->s = box->short_field(value_offset); + return T_SHORT; + } + if (k == SystemDictionary::int_klass()) { + value->i = box->int_field(value_offset); + return T_INT; + } + if (k == SystemDictionary::long_klass()) { + value->j = box->long_field(value_offset); + return T_LONG; + } + return T_ILLEGAL; +} + + +BasicType java_lang_boxing_object::set_value(oop box, jvalue* value) { + klassOop k = box->klass(); + if (k == SystemDictionary::boolean_klass()) { + box->bool_field_put(value_offset, value->z); + return T_BOOLEAN; + } + if (k == SystemDictionary::char_klass()) { + box->char_field_put(value_offset, value->c); + return T_CHAR; + } + if (k == SystemDictionary::float_klass()) { + box->float_field_put(value_offset, value->f); + return T_FLOAT; + } + if (k == SystemDictionary::double_klass()) { + box->double_field_put(value_offset, value->d); + return T_DOUBLE; + } + if (k == SystemDictionary::byte_klass()) { + box->byte_field_put(value_offset, value->b); + return T_BYTE; + } + if (k == SystemDictionary::short_klass()) { + box->short_field_put(value_offset, value->s); + return T_SHORT; + } + if (k == SystemDictionary::int_klass()) { + box->int_field_put(value_offset, value->i); + return T_INT; + } + if (k == SystemDictionary::long_klass()) { + box->long_field_put(value_offset, value->j); + return T_LONG; + } + return T_ILLEGAL; +} + + +// Support for java_lang_ref_Reference + +void java_lang_ref_Reference::set_referent(oop ref, oop value) { + ref->obj_field_put(referent_offset, value); +} + +oop* java_lang_ref_Reference::referent_addr(oop ref) { + return ref->obj_field_addr(referent_offset); +} + +void java_lang_ref_Reference::set_next(oop ref, oop value) { + ref->obj_field_put(next_offset, value); +} + +oop* java_lang_ref_Reference::next_addr(oop ref) { + return ref->obj_field_addr(next_offset); +} + +void java_lang_ref_Reference::set_discovered(oop ref, oop value) { + ref->obj_field_put(discovered_offset, value); +} + +oop* java_lang_ref_Reference::discovered_addr(oop ref) { + return ref->obj_field_addr(discovered_offset); +} + +oop* java_lang_ref_Reference::pending_list_lock_addr() { + instanceKlass* ik = instanceKlass::cast(SystemDictionary::reference_klass()); + return (oop*)(((char *)ik->start_of_static_fields()) + static_lock_offset); +} + +oop* java_lang_ref_Reference::pending_list_addr() { + instanceKlass* ik = instanceKlass::cast(SystemDictionary::reference_klass()); + return (oop *)(((char *)ik->start_of_static_fields()) + static_pending_offset); +} + + +// Support for java_lang_ref_SoftReference + +jlong java_lang_ref_SoftReference::timestamp(oop ref) { + return ref->long_field(timestamp_offset); +} + +jlong java_lang_ref_SoftReference::clock() { + instanceKlass* ik = instanceKlass::cast(SystemDictionary::soft_reference_klass()); + int offset = ik->offset_of_static_fields() + static_clock_offset; + + return SystemDictionary::soft_reference_klass()->long_field(offset); +} + +void java_lang_ref_SoftReference::set_clock(jlong value) { + instanceKlass* ik = instanceKlass::cast(SystemDictionary::soft_reference_klass()); + int offset = ik->offset_of_static_fields() + static_clock_offset; + + SystemDictionary::soft_reference_klass()->long_field_put(offset, value); +} + + +// Support for java_security_AccessControlContext + +int java_security_AccessControlContext::_context_offset = 0; +int java_security_AccessControlContext::_privilegedContext_offset = 0; +int java_security_AccessControlContext::_isPrivileged_offset = 0; + + +void java_security_AccessControlContext::compute_offsets() { + assert(_isPrivileged_offset == 0, "offsets should be initialized only once"); + fieldDescriptor fd; + instanceKlass* ik = instanceKlass::cast(SystemDictionary::AccessControlContext_klass()); + + if (!ik->find_local_field(vmSymbols::context_name(), vmSymbols::protectiondomain_signature(), &fd)) { + fatal("Invalid layout of java.security.AccessControlContext"); + } + _context_offset = fd.offset(); + + if (!ik->find_local_field(vmSymbols::privilegedContext_name(), vmSymbols::accesscontrolcontext_signature(), &fd)) { + fatal("Invalid layout of java.security.AccessControlContext"); + } + _privilegedContext_offset = fd.offset(); + + if (!ik->find_local_field(vmSymbols::isPrivileged_name(), vmSymbols::bool_signature(), &fd)) { + fatal("Invalid layout of java.security.AccessControlContext"); + } + _isPrivileged_offset = fd.offset(); +} + + +oop java_security_AccessControlContext::create(objArrayHandle context, bool isPrivileged, Handle privileged_context, TRAPS) { + assert(_isPrivileged_offset != 0, "offsets should have been initialized"); + // Ensure klass is initialized + instanceKlass::cast(SystemDictionary::AccessControlContext_klass())->initialize(CHECK_0); + // Allocate result + oop result = instanceKlass::cast(SystemDictionary::AccessControlContext_klass())->allocate_instance(CHECK_0); + // Fill in values + result->obj_field_put(_context_offset, context()); + result->obj_field_put(_privilegedContext_offset, privileged_context()); + result->bool_field_put(_isPrivileged_offset, isPrivileged); + return result; +} + + +// Support for java_lang_ClassLoader + +oop java_lang_ClassLoader::parent(oop loader) { + assert(loader->is_oop(), "loader must be oop"); + return loader->obj_field(parent_offset); +} + + +bool java_lang_ClassLoader::is_trusted_loader(oop loader) { + // Fix for 4474172; see evaluation for more details + loader = non_reflection_class_loader(loader); + + oop cl = SystemDictionary::java_system_loader(); + while(cl != NULL) { + if (cl == loader) return true; + cl = parent(cl); + } + return false; +} + +oop java_lang_ClassLoader::non_reflection_class_loader(oop loader) { + if (loader != NULL) { + // See whether this is one of the class loaders associated with + // the generated bytecodes for reflection, and if so, "magically" + // delegate to its parent to prevent class loading from occurring + // in places where applications using reflection didn't expect it. + klassOop delegating_cl_class = SystemDictionary::reflect_delegating_classloader_klass(); + // This might be null in non-1.4 JDKs + if (delegating_cl_class != NULL && loader->is_a(delegating_cl_class)) { + return parent(loader); + } + } + return loader; +} + + +// Support for java_lang_System + +void java_lang_System::compute_offsets() { + assert(offset_of_static_fields == 0, "offsets should be initialized only once"); + + instanceKlass* ik = instanceKlass::cast(SystemDictionary::system_klass()); + offset_of_static_fields = ik->offset_of_static_fields(); +} + +int java_lang_System::in_offset_in_bytes() { + return (offset_of_static_fields + static_in_offset); +} + + +int java_lang_System::out_offset_in_bytes() { + return (offset_of_static_fields + static_out_offset); +} + + +int java_lang_System::err_offset_in_bytes() { + return (offset_of_static_fields + static_err_offset); +} + + + +int java_lang_String::value_offset; +int java_lang_String::offset_offset; +int java_lang_String::count_offset; +int java_lang_String::hash_offset; +int java_lang_Class::klass_offset; +int java_lang_Class::array_klass_offset; +int java_lang_Class::resolved_constructor_offset; +int java_lang_Class::number_of_fake_oop_fields; +int java_lang_Throwable::backtrace_offset; +int java_lang_Throwable::detailMessage_offset; +int java_lang_Throwable::cause_offset; +int java_lang_Throwable::stackTrace_offset; +int java_lang_reflect_AccessibleObject::override_offset; +int java_lang_reflect_Method::clazz_offset; +int java_lang_reflect_Method::name_offset; +int java_lang_reflect_Method::returnType_offset; +int java_lang_reflect_Method::parameterTypes_offset; +int java_lang_reflect_Method::exceptionTypes_offset; +int java_lang_reflect_Method::slot_offset; +int java_lang_reflect_Method::modifiers_offset; +int java_lang_reflect_Method::signature_offset; +int java_lang_reflect_Method::annotations_offset; +int java_lang_reflect_Method::parameter_annotations_offset; +int java_lang_reflect_Method::annotation_default_offset; +int java_lang_reflect_Constructor::clazz_offset; +int java_lang_reflect_Constructor::parameterTypes_offset; +int java_lang_reflect_Constructor::exceptionTypes_offset; +int java_lang_reflect_Constructor::slot_offset; +int java_lang_reflect_Constructor::modifiers_offset; +int java_lang_reflect_Constructor::signature_offset; +int java_lang_reflect_Constructor::annotations_offset; +int java_lang_reflect_Constructor::parameter_annotations_offset; +int java_lang_reflect_Field::clazz_offset; +int java_lang_reflect_Field::name_offset; +int java_lang_reflect_Field::type_offset; +int java_lang_reflect_Field::slot_offset; +int java_lang_reflect_Field::modifiers_offset; +int java_lang_reflect_Field::signature_offset; +int java_lang_reflect_Field::annotations_offset; +int java_lang_boxing_object::value_offset; +int java_lang_ref_Reference::referent_offset; +int java_lang_ref_Reference::queue_offset; +int java_lang_ref_Reference::next_offset; +int java_lang_ref_Reference::discovered_offset; +int java_lang_ref_Reference::static_lock_offset; +int java_lang_ref_Reference::static_pending_offset; +int java_lang_ref_Reference::number_of_fake_oop_fields; +int java_lang_ref_SoftReference::timestamp_offset; +int java_lang_ref_SoftReference::static_clock_offset; +int java_lang_ClassLoader::parent_offset; +int java_lang_System::offset_of_static_fields; +int java_lang_System::static_in_offset; +int java_lang_System::static_out_offset; +int java_lang_System::static_err_offset; +int java_lang_StackTraceElement::declaringClass_offset; +int java_lang_StackTraceElement::methodName_offset; +int java_lang_StackTraceElement::fileName_offset; +int java_lang_StackTraceElement::lineNumber_offset; +int java_lang_AssertionStatusDirectives::classes_offset; +int java_lang_AssertionStatusDirectives::classEnabled_offset; +int java_lang_AssertionStatusDirectives::packages_offset; +int java_lang_AssertionStatusDirectives::packageEnabled_offset; +int java_lang_AssertionStatusDirectives::deflt_offset; +int java_nio_Buffer::_limit_offset; +int sun_misc_AtomicLongCSImpl::_value_offset; +int java_util_concurrent_locks_AbstractOwnableSynchronizer::_owner_offset = 0; +int sun_reflect_ConstantPool::_cp_oop_offset; +int sun_reflect_UnsafeStaticFieldAccessorImpl::_base_offset; + + +// Support for java_lang_StackTraceElement + +void java_lang_StackTraceElement::set_fileName(oop element, oop value) { + element->obj_field_put(fileName_offset, value); +} + +void java_lang_StackTraceElement::set_declaringClass(oop element, oop value) { + element->obj_field_put(declaringClass_offset, value); +} + +void java_lang_StackTraceElement::set_methodName(oop element, oop value) { + element->obj_field_put(methodName_offset, value); +} + +void java_lang_StackTraceElement::set_lineNumber(oop element, int value) { + element->int_field_put(lineNumber_offset, value); +} + + +// Support for java Assertions - java_lang_AssertionStatusDirectives. + +void java_lang_AssertionStatusDirectives::set_classes(oop o, oop val) { + o->obj_field_put(classes_offset, val); +} + +void java_lang_AssertionStatusDirectives::set_classEnabled(oop o, oop val) { + o->obj_field_put(classEnabled_offset, val); +} + +void java_lang_AssertionStatusDirectives::set_packages(oop o, oop val) { + o->obj_field_put(packages_offset, val); +} + +void java_lang_AssertionStatusDirectives::set_packageEnabled(oop o, oop val) { + o->obj_field_put(packageEnabled_offset, val); +} + +void java_lang_AssertionStatusDirectives::set_deflt(oop o, bool val) { + o->bool_field_put(deflt_offset, val); +} + + +// Support for intrinsification of java.nio.Buffer.checkIndex +int java_nio_Buffer::limit_offset() { + return _limit_offset; +} + + +void java_nio_Buffer::compute_offsets() { + klassOop k = SystemDictionary::java_nio_Buffer_klass(); + COMPUTE_OFFSET("java.nio.Buffer", _limit_offset, k, vmSymbols::limit_name(), vmSymbols::int_signature()); +} + +// Support for intrinsification of sun.misc.AtomicLongCSImpl.attemptUpdate +int sun_misc_AtomicLongCSImpl::value_offset() { + assert(SystemDictionary::sun_misc_AtomicLongCSImpl_klass() != NULL, "can't call this"); + return _value_offset; +} + + +void sun_misc_AtomicLongCSImpl::compute_offsets() { + klassOop k = SystemDictionary::sun_misc_AtomicLongCSImpl_klass(); + // If this class is not present, its value field offset won't be referenced. + if (k != NULL) { + COMPUTE_OFFSET("sun.misc.AtomicLongCSImpl", _value_offset, k, vmSymbols::value_name(), vmSymbols::long_signature()); + } +} + +void java_util_concurrent_locks_AbstractOwnableSynchronizer::initialize(TRAPS) { + if (_owner_offset != 0) return; + + assert(JDK_Version::is_gte_jdk16x_version(), "Must be JDK 1.6 or later"); + SystemDictionary::load_abstract_ownable_synchronizer_klass(CHECK); + klassOop k = SystemDictionary::abstract_ownable_synchronizer_klass(); + COMPUTE_OFFSET("java.util.concurrent.locks.AbstractOwnableSynchronizer", _owner_offset, k, + vmSymbols::exclusive_owner_thread_name(), vmSymbols::thread_signature()); +} + +oop java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(oop obj) { + assert(_owner_offset != 0, "Must be initialized"); + return obj->obj_field(_owner_offset); +} + +// Compute hard-coded offsets +// Invoked before SystemDictionary::initialize, so pre-loaded classes +// are not available to determine the offset_of_static_fields. +void JavaClasses::compute_hard_coded_offsets() { + const int x = wordSize; + const int header = instanceOopDesc::header_size_in_bytes(); + + // Do the String Class + java_lang_String::value_offset = java_lang_String::hc_value_offset * x + header; + java_lang_String::offset_offset = java_lang_String::hc_offset_offset * x + header; + java_lang_String::count_offset = java_lang_String::offset_offset + sizeof (jint); + java_lang_String::hash_offset = java_lang_String::count_offset + sizeof (jint); + + // Do the Class Class + java_lang_Class::klass_offset = java_lang_Class::hc_klass_offset * x + header; + java_lang_Class::array_klass_offset = java_lang_Class::hc_array_klass_offset * x + header; + java_lang_Class::resolved_constructor_offset = java_lang_Class::hc_resolved_constructor_offset * x + header; + + // This is NOT an offset + java_lang_Class::number_of_fake_oop_fields = java_lang_Class::hc_number_of_fake_oop_fields; + + // Throwable Class + java_lang_Throwable::backtrace_offset = java_lang_Throwable::hc_backtrace_offset * x + header; + java_lang_Throwable::detailMessage_offset = java_lang_Throwable::hc_detailMessage_offset * x + header; + java_lang_Throwable::cause_offset = java_lang_Throwable::hc_cause_offset * x + header; + java_lang_Throwable::stackTrace_offset = java_lang_Throwable::hc_stackTrace_offset * x + header; + + // java_lang_boxing_object + java_lang_boxing_object::value_offset = java_lang_boxing_object::hc_value_offset * x + header; + + // java_lang_ref_Reference: + java_lang_ref_Reference::referent_offset = java_lang_ref_Reference::hc_referent_offset * x + header; + java_lang_ref_Reference::queue_offset = java_lang_ref_Reference::hc_queue_offset * x + header; + java_lang_ref_Reference::next_offset = java_lang_ref_Reference::hc_next_offset * x + header; + java_lang_ref_Reference::discovered_offset = java_lang_ref_Reference::hc_discovered_offset * x + header; + java_lang_ref_Reference::static_lock_offset = java_lang_ref_Reference::hc_static_lock_offset * x; + java_lang_ref_Reference::static_pending_offset = java_lang_ref_Reference::hc_static_pending_offset * x; + // Artificial fields for java_lang_ref_Reference + // The first field is for the discovered field added in 1.4 + java_lang_ref_Reference::number_of_fake_oop_fields = 1; + + // java_lang_ref_SoftReference Class + java_lang_ref_SoftReference::timestamp_offset = java_lang_ref_SoftReference::hc_timestamp_offset * x + header; + // Don't multiply static fields because they are always in wordSize units + java_lang_ref_SoftReference::static_clock_offset = java_lang_ref_SoftReference::hc_static_clock_offset * x; + + // java_lang_ClassLoader + java_lang_ClassLoader::parent_offset = java_lang_ClassLoader::hc_parent_offset * x + header; + + // java_lang_System + java_lang_System::static_in_offset = java_lang_System::hc_static_in_offset * x; + java_lang_System::static_out_offset = java_lang_System::hc_static_out_offset * x; + java_lang_System::static_err_offset = java_lang_System::hc_static_err_offset * x; + + // java_lang_StackTraceElement + java_lang_StackTraceElement::declaringClass_offset = java_lang_StackTraceElement::hc_declaringClass_offset * x + header; + java_lang_StackTraceElement::methodName_offset = java_lang_StackTraceElement::hc_methodName_offset * x + header; + java_lang_StackTraceElement::fileName_offset = java_lang_StackTraceElement::hc_fileName_offset * x + header; + java_lang_StackTraceElement::lineNumber_offset = java_lang_StackTraceElement::hc_lineNumber_offset * x + header; + java_lang_AssertionStatusDirectives::classes_offset = java_lang_AssertionStatusDirectives::hc_classes_offset * x + header; + java_lang_AssertionStatusDirectives::classEnabled_offset = java_lang_AssertionStatusDirectives::hc_classEnabled_offset * x + header; + java_lang_AssertionStatusDirectives::packages_offset = java_lang_AssertionStatusDirectives::hc_packages_offset * x + header; + java_lang_AssertionStatusDirectives::packageEnabled_offset = java_lang_AssertionStatusDirectives::hc_packageEnabled_offset * x + header; + java_lang_AssertionStatusDirectives::deflt_offset = java_lang_AssertionStatusDirectives::hc_deflt_offset * x + header; + +} + + +// Compute non-hard-coded field offsets of all the classes in this file +void JavaClasses::compute_offsets() { + + java_lang_Class::compute_offsets(); + java_lang_System::compute_offsets(); + java_lang_Thread::compute_offsets(); + java_lang_ThreadGroup::compute_offsets(); + java_security_AccessControlContext::compute_offsets(); + // Initialize reflection classes. The layouts of these classes + // changed with the new reflection implementation in JDK 1.4, and + // since the Universe doesn't know what JDK version it is until this + // point we defer computation of these offsets until now. + java_lang_reflect_AccessibleObject::compute_offsets(); + java_lang_reflect_Method::compute_offsets(); + java_lang_reflect_Constructor::compute_offsets(); + java_lang_reflect_Field::compute_offsets(); + if (JDK_Version::is_gte_jdk14x_version()) { + java_nio_Buffer::compute_offsets(); + } + if (JDK_Version::is_gte_jdk15x_version()) { + sun_reflect_ConstantPool::compute_offsets(); + sun_reflect_UnsafeStaticFieldAccessorImpl::compute_offsets(); + } + sun_misc_AtomicLongCSImpl::compute_offsets(); +} + +#ifndef PRODUCT + +// These functions exist to assert the validity of hard-coded field offsets to guard +// against changes in the class files + +bool JavaClasses::check_offset(const char *klass_name, int hardcoded_offset, const char *field_name, const char* field_sig) { + EXCEPTION_MARK; + fieldDescriptor fd; + symbolHandle klass_sym = oopFactory::new_symbol_handle(klass_name, CATCH); + klassOop k = SystemDictionary::resolve_or_fail(klass_sym, true, CATCH); + instanceKlassHandle h_klass (THREAD, k); + //instanceKlassHandle h_klass(klass); + symbolHandle f_name = oopFactory::new_symbol_handle(field_name, CATCH); + symbolHandle f_sig = oopFactory::new_symbol_handle(field_sig, CATCH); + if (!h_klass->find_local_field(f_name(), f_sig(), &fd)) { + tty->print_cr("Nonstatic field %s.%s not found", klass_name, field_name); + return false; + } + if (fd.is_static()) { + tty->print_cr("Nonstatic field %s.%s appears to be static", klass_name, field_name); + return false; + } + if (fd.offset() == hardcoded_offset ) { + return true; + } else { + tty->print_cr("Offset of nonstatic field %s.%s is hardcoded as %d but should really be %d.", + klass_name, field_name, hardcoded_offset, fd.offset()); + return false; + } +} + + +bool JavaClasses::check_static_offset(const char *klass_name, int hardcoded_offset, const char *field_name, const char* field_sig) { + EXCEPTION_MARK; + fieldDescriptor fd; + symbolHandle klass_sym = oopFactory::new_symbol_handle(klass_name, CATCH); + klassOop k = SystemDictionary::resolve_or_fail(klass_sym, true, CATCH); + instanceKlassHandle h_klass (THREAD, k); + symbolHandle f_name = oopFactory::new_symbol_handle(field_name, CATCH); + symbolHandle f_sig = oopFactory::new_symbol_handle(field_sig, CATCH); + if (!h_klass->find_local_field(f_name(), f_sig(), &fd)) { + tty->print_cr("Static field %s.%s not found", klass_name, field_name); + return false; + } + if (!fd.is_static()) { + tty->print_cr("Static field %s.%s appears to be nonstatic", klass_name, field_name); + return false; + } + if (fd.offset() == hardcoded_offset + h_klass->offset_of_static_fields()) { + return true; + } else { + tty->print_cr("Offset of static field %s.%s is hardcoded as %d but should really be %d.", klass_name, field_name, hardcoded_offset, fd.offset() - h_klass->offset_of_static_fields()); + return false; + } +} + + +// Check the hard-coded field offsets of all the classes in this file + +void JavaClasses::check_offsets() { + bool valid = true; + +#define CHECK_OFFSET(klass_name, cpp_klass_name, field_name, field_sig) \ + valid &= check_offset(klass_name, cpp_klass_name :: field_name ## _offset, #field_name, field_sig) + +#define CHECK_STATIC_OFFSET(klass_name, cpp_klass_name, field_name, field_sig) \ + valid &= check_static_offset(klass_name, cpp_klass_name :: static_ ## field_name ## _offset, #field_name, field_sig) + + // java.lang.String + + CHECK_OFFSET("java/lang/String", java_lang_String, value, "[C"); + CHECK_OFFSET("java/lang/String", java_lang_String, offset, "I"); + CHECK_OFFSET("java/lang/String", java_lang_String, count, "I"); + CHECK_OFFSET("java/lang/String", java_lang_String, hash, "I"); + + // java.lang.Class + + // Fake fields + // CHECK_OFFSET("java/lang/Class", java_lang_Class, klass); // %%% this needs to be checked + // CHECK_OFFSET("java/lang/Class", java_lang_Class, array_klass); // %%% this needs to be checked + // CHECK_OFFSET("java/lang/Class", java_lang_Class, resolved_constructor); // %%% this needs to be checked + + // java.lang.Throwable + + CHECK_OFFSET("java/lang/Throwable", java_lang_Throwable, backtrace, "Ljava/lang/Object;"); + CHECK_OFFSET("java/lang/Throwable", java_lang_Throwable, detailMessage, "Ljava/lang/String;"); + CHECK_OFFSET("java/lang/Throwable", java_lang_Throwable, cause, "Ljava/lang/Throwable;"); + CHECK_OFFSET("java/lang/Throwable", java_lang_Throwable, stackTrace, "[Ljava/lang/StackTraceElement;"); + + // Boxed primitive objects (java_lang_boxing_object) + + CHECK_OFFSET("java/lang/Boolean", java_lang_boxing_object, value, "Z"); + CHECK_OFFSET("java/lang/Character", java_lang_boxing_object, value, "C"); + CHECK_OFFSET("java/lang/Float", java_lang_boxing_object, value, "F"); + CHECK_OFFSET("java/lang/Double", java_lang_boxing_object, value, "D"); + CHECK_OFFSET("java/lang/Byte", java_lang_boxing_object, value, "B"); + CHECK_OFFSET("java/lang/Short", java_lang_boxing_object, value, "S"); + CHECK_OFFSET("java/lang/Integer", java_lang_boxing_object, value, "I"); + CHECK_OFFSET("java/lang/Long", java_lang_boxing_object, value, "J"); + + // java.lang.ClassLoader + + CHECK_OFFSET("java/lang/ClassLoader", java_lang_ClassLoader, parent, "Ljava/lang/ClassLoader;"); + + // java.lang.System + + CHECK_STATIC_OFFSET("java/lang/System", java_lang_System, in, "Ljava/io/InputStream;"); + CHECK_STATIC_OFFSET("java/lang/System", java_lang_System, out, "Ljava/io/PrintStream;"); + CHECK_STATIC_OFFSET("java/lang/System", java_lang_System, err, "Ljava/io/PrintStream;"); + + // java.lang.StackTraceElement + + CHECK_OFFSET("java/lang/StackTraceElement", java_lang_StackTraceElement, declaringClass, "Ljava/lang/String;"); + CHECK_OFFSET("java/lang/StackTraceElement", java_lang_StackTraceElement, methodName, "Ljava/lang/String;"); + CHECK_OFFSET("java/lang/StackTraceElement", java_lang_StackTraceElement, fileName, "Ljava/lang/String;"); + CHECK_OFFSET("java/lang/StackTraceElement", java_lang_StackTraceElement, lineNumber, "I"); + + // java.lang.ref.Reference + + CHECK_OFFSET("java/lang/ref/Reference", java_lang_ref_Reference, referent, "Ljava/lang/Object;"); + CHECK_OFFSET("java/lang/ref/Reference", java_lang_ref_Reference, queue, "Ljava/lang/ref/ReferenceQueue;"); + CHECK_OFFSET("java/lang/ref/Reference", java_lang_ref_Reference, next, "Ljava/lang/ref/Reference;"); + // Fake field + //CHECK_OFFSET("java/lang/ref/Reference", java_lang_ref_Reference, discovered, "Ljava/lang/ref/Reference;"); + CHECK_STATIC_OFFSET("java/lang/ref/Reference", java_lang_ref_Reference, lock, "Ljava/lang/ref/Reference$Lock;"); + CHECK_STATIC_OFFSET("java/lang/ref/Reference", java_lang_ref_Reference, pending, "Ljava/lang/ref/Reference;"); + + // java.lang.ref.SoftReference + + CHECK_OFFSET("java/lang/ref/SoftReference", java_lang_ref_SoftReference, timestamp, "J"); + CHECK_STATIC_OFFSET("java/lang/ref/SoftReference", java_lang_ref_SoftReference, clock, "J"); + + // java.lang.AssertionStatusDirectives + // + // The CheckAssertionStatusDirectives boolean can be removed from here and + // globals.hpp after the AssertionStatusDirectives class has been integrated + // into merlin "for some time." Without it, the vm will fail with early + // merlin builds. + + if (CheckAssertionStatusDirectives && JDK_Version::is_gte_jdk14x_version()) { + const char* nm = "java/lang/AssertionStatusDirectives"; + const char* sig = "[Ljava/lang/String;"; + CHECK_OFFSET(nm, java_lang_AssertionStatusDirectives, classes, sig); + CHECK_OFFSET(nm, java_lang_AssertionStatusDirectives, classEnabled, "[Z"); + CHECK_OFFSET(nm, java_lang_AssertionStatusDirectives, packages, sig); + CHECK_OFFSET(nm, java_lang_AssertionStatusDirectives, packageEnabled, "[Z"); + CHECK_OFFSET(nm, java_lang_AssertionStatusDirectives, deflt, "Z"); + } + + if (!valid) vm_exit_during_initialization("Hard-coded field offset verification failed"); +} + +#endif // PRODUCT + +void javaClasses_init() { + JavaClasses::compute_offsets(); + JavaClasses::check_offsets(); + FilteredFieldsMap::initialize(); // must be done after computing offsets. +} diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp new file mode 100644 index 00000000000..45acae2bd98 --- /dev/null +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp @@ -0,0 +1,904 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Interface for manipulating the basic Java classes. +// +// All dependencies on layout of actual Java classes should be kept here. +// If the layout of any of the classes above changes the offsets must be adjusted. +// +// For most classes we hardwire the offsets for performance reasons. In certain +// cases (e.g. java.security.AccessControlContext) we compute the offsets at +// startup since the layout here differs between JDK1.2 and JDK1.3. +// +// Note that fields (static and non-static) are arranged with oops before non-oops +// on a per class basis. The offsets below have to reflect this ordering. +// +// When editing the layouts please update the check_offset verification code +// correspondingly. The names in the enums must be identical to the actual field +// names in order for the verification code to work. + + +// Interface to java.lang.String objects + +class java_lang_String : AllStatic { + private: + enum { + hc_value_offset = 0, + hc_offset_offset = 1, + hc_count_offset = 2, + hc_hash_offset = 3 + }; + + static int value_offset; + static int offset_offset; + static int count_offset; + static int hash_offset; + + static Handle basic_create(int length, bool tenured, TRAPS); + static Handle basic_create_from_unicode(jchar* unicode, int length, bool tenured, TRAPS); + + static void set_value( oop string, typeArrayOop buffer) { string->obj_field_put(value_offset, (oop)buffer); } + static void set_offset(oop string, int offset) { string->int_field_put(offset_offset, offset); } + static void set_count( oop string, int count) { string->int_field_put(count_offset, count); } + + public: + // Instance creation + static Handle create_from_unicode(jchar* unicode, int len, TRAPS); + static Handle create_tenured_from_unicode(jchar* unicode, int len, TRAPS); + static oop create_oop_from_unicode(jchar* unicode, int len, TRAPS); + static Handle create_from_str(const char* utf8_str, TRAPS); + static oop create_oop_from_str(const char* utf8_str, TRAPS); + static Handle create_from_symbol(symbolHandle symbol, TRAPS); + static Handle create_from_platform_dependent_str(const char* str, TRAPS); + static Handle char_converter(Handle java_string, jchar from_char, jchar to_char, TRAPS); + + static int value_offset_in_bytes() { return value_offset; } + static int count_offset_in_bytes() { return count_offset; } + static int offset_offset_in_bytes() { return offset_offset; } + static int hash_offset_in_bytes() { return hash_offset; } + + // Accessors + static typeArrayOop value(oop java_string) { + assert(is_instance(java_string), "must be java_string"); + return (typeArrayOop) java_string->obj_field(value_offset); + } + static int offset(oop java_string) { + assert(is_instance(java_string), "must be java_string"); + return java_string->int_field(offset_offset); + } + static int length(oop java_string) { + assert(is_instance(java_string), "must be java_string"); + return java_string->int_field(count_offset); + } + static int utf8_length(oop java_string); + + // String converters + static char* as_utf8_string(oop java_string); + static char* as_utf8_string(oop java_string, int start, int len); + static jchar* as_unicode_string(oop java_string, int& length); + + static bool equals(oop java_string, jchar* chars, int len); + + // Conversion between '.' and '/' formats + static Handle externalize_classname(Handle java_string, TRAPS) { return char_converter(java_string, '/', '.', THREAD); } + static Handle internalize_classname(Handle java_string, TRAPS) { return char_converter(java_string, '.', '/', THREAD); } + + // Conversion + static symbolHandle as_symbol(Handle java_string, TRAPS); + + // Testers + static bool is_instance(oop obj) { + return obj != NULL && obj->klass() == SystemDictionary::string_klass(); + } + + // Debugging + static void print(Handle java_string, outputStream* st); + friend class JavaClasses; +}; + + +// Interface to java.lang.Class objects + +class java_lang_Class : AllStatic { + friend class VMStructs; + private: + // The fake offsets are added by the class loader when java.lang.Class is loaded + + enum { + hc_klass_offset = 0, + hc_array_klass_offset = 1, + hc_resolved_constructor_offset = 2, + hc_number_of_fake_oop_fields = 3 + }; + + static int klass_offset; + static int resolved_constructor_offset; + static int array_klass_offset; + static int number_of_fake_oop_fields; + + static void compute_offsets(); + static bool offsets_computed; + static int classRedefinedCount_offset; + + public: + // Instance creation + static oop create_mirror(KlassHandle k, TRAPS); + static oop create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS); + // Conversion + static klassOop as_klassOop(oop java_class); + // Testing + static bool is_primitive(oop java_class); + static BasicType primitive_type(oop java_class); + static oop primitive_mirror(BasicType t); + // JVM_NewInstance support + static methodOop resolved_constructor(oop java_class); + static void set_resolved_constructor(oop java_class, methodOop constructor); + // JVM_NewArray support + static klassOop array_klass(oop java_class); + static void set_array_klass(oop java_class, klassOop klass); + // compiler support for class operations + static int klass_offset_in_bytes() { return klass_offset; } + static int resolved_constructor_offset_in_bytes() { return resolved_constructor_offset; } + static int array_klass_offset_in_bytes() { return array_klass_offset; } + // Support for classRedefinedCount field + static int classRedefinedCount(oop the_class_mirror); + static void set_classRedefinedCount(oop the_class_mirror, int value); + // Debugging + friend class JavaClasses; + friend class instanceKlass; // verification code accesses offsets + friend class ClassFileParser; // access to number_of_fake_fields +}; + +// Interface to java.lang.Thread objects + +class java_lang_Thread : AllStatic { + private: + // Note that for this class the layout changed between JDK1.2 and JDK1.3, + // so we compute the offsets at startup rather than hard-wiring them. + static int _name_offset; + static int _group_offset; + static int _contextClassLoader_offset; + static int _inheritedAccessControlContext_offset; + static int _priority_offset; + static int _eetop_offset; + static int _daemon_offset; + static int _stillborn_offset; + static int _stackSize_offset; + static int _tid_offset; + static int _thread_status_offset; + static int _park_blocker_offset; + static int _park_event_offset ; + + static void compute_offsets(); + + public: + // Instance creation + static oop create(); + // Returns the JavaThread associated with the thread obj + static JavaThread* thread(oop java_thread); + // Set JavaThread for instance + static void set_thread(oop java_thread, JavaThread* thread); + // Name + static typeArrayOop name(oop java_thread); + static void set_name(oop java_thread, typeArrayOop name); + // Priority + static ThreadPriority priority(oop java_thread); + static void set_priority(oop java_thread, ThreadPriority priority); + // Thread group + static oop threadGroup(oop java_thread); + // Stillborn + static bool is_stillborn(oop java_thread); + static void set_stillborn(oop java_thread); + // Alive (NOTE: this is not really a field, but provides the correct + // definition without doing a Java call) + static bool is_alive(oop java_thread); + // Daemon + static bool is_daemon(oop java_thread); + static void set_daemon(oop java_thread); + // Context ClassLoader + static oop context_class_loader(oop java_thread); + // Control context + static oop inherited_access_control_context(oop java_thread); + // Stack size hint + static jlong stackSize(oop java_thread); + // Thread ID + static jlong thread_id(oop java_thread); + + // Blocker object responsible for thread parking + static oop park_blocker(oop java_thread); + + // Pointer to type-stable park handler, encoded as jlong. + // Should be set when apparently null + // For details, see unsafe.cpp Unsafe_Unpark + static jlong park_event(oop java_thread); + static bool set_park_event(oop java_thread, jlong ptr); + + // Java Thread Status for JVMTI and M&M use. + // This thread status info is saved in threadStatus field of + // java.lang.Thread java class. + enum ThreadStatus { + NEW = 0, + RUNNABLE = JVMTI_THREAD_STATE_ALIVE + // runnable / running + JVMTI_THREAD_STATE_RUNNABLE, + SLEEPING = JVMTI_THREAD_STATE_ALIVE + // Thread.sleep() + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + + JVMTI_THREAD_STATE_SLEEPING, + IN_OBJECT_WAIT = JVMTI_THREAD_STATE_ALIVE + // Object.wait() + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_INDEFINITELY + + JVMTI_THREAD_STATE_IN_OBJECT_WAIT, + IN_OBJECT_WAIT_TIMED = JVMTI_THREAD_STATE_ALIVE + // Object.wait(long) + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + + JVMTI_THREAD_STATE_IN_OBJECT_WAIT, + PARKED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park() + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_INDEFINITELY + + JVMTI_THREAD_STATE_PARKED, + PARKED_TIMED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park(long) + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + + JVMTI_THREAD_STATE_PARKED, + BLOCKED_ON_MONITOR_ENTER = JVMTI_THREAD_STATE_ALIVE + // (re-)entering a synchronization block + JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER, + TERMINATED = JVMTI_THREAD_STATE_TERMINATED + }; + // Write thread status info to threadStatus field of java.lang.Thread. + static void set_thread_status(oop java_thread_oop, ThreadStatus status); + // Read thread status info from threadStatus field of java.lang.Thread. + static ThreadStatus get_thread_status(oop java_thread_oop); + + static const char* thread_status_name(oop java_thread_oop); + + // Debugging + friend class JavaClasses; +}; + +// Interface to java.lang.ThreadGroup objects + +class java_lang_ThreadGroup : AllStatic { + private: + static int _parent_offset; + static int _name_offset; + static int _threads_offset; + static int _groups_offset; + static int _maxPriority_offset; + static int _destroyed_offset; + static int _daemon_offset; + static int _vmAllowSuspension_offset; + static int _nthreads_offset; + static int _ngroups_offset; + + static void compute_offsets(); + + public: + // parent ThreadGroup + static oop parent(oop java_thread_group); + // name + static typeArrayOop name(oop java_thread_group); + // ("name as oop" accessor is not necessary) + // Number of threads in group + static int nthreads(oop java_thread_group); + // threads + static objArrayOop threads(oop java_thread_group); + // Number of threads in group + static int ngroups(oop java_thread_group); + // groups + static objArrayOop groups(oop java_thread_group); + // maxPriority in group + static ThreadPriority maxPriority(oop java_thread_group); + // Destroyed + static bool is_destroyed(oop java_thread_group); + // Daemon + static bool is_daemon(oop java_thread_group); + // vmAllowSuspension + static bool is_vmAllowSuspension(oop java_thread_group); + // Debugging + friend class JavaClasses; +}; + + + +// Interface to java.lang.Throwable objects + +class java_lang_Throwable: AllStatic { + friend class BacktraceBuilder; + + private: + // Offsets + enum { + hc_backtrace_offset = 0, + hc_detailMessage_offset = 1, + hc_cause_offset = 2, // New since 1.4 + hc_stackTrace_offset = 3 // New since 1.4 + }; + // Trace constants + enum { + trace_methods_offset = 0, + trace_bcis_offset = 1, + trace_next_offset = 2, + trace_size = 3, + trace_chunk_size = 32 + }; + + static int backtrace_offset; + static int detailMessage_offset; + static int cause_offset; + static int stackTrace_offset; + + // Printing + static char* print_stack_element_to_buffer(methodOop method, int bci); + static void print_to_stream(Handle stream, const char* str); + // StackTrace (programmatic access, new since 1.4) + static void clear_stacktrace(oop throwable); + // No stack trace available + static const char* no_stack_trace_message(); + + public: + // Backtrace + static oop backtrace(oop throwable); + static void set_backtrace(oop throwable, oop value); + // Needed by JVMTI to filter out this internal field. + static int get_backtrace_offset() { return backtrace_offset;} + static int get_detailMessage_offset() { return detailMessage_offset;} + // Message + static oop message(oop throwable); + static oop message(Handle throwable); + static void set_message(oop throwable, oop value); + // Print stack trace stored in exception by call-back to Java + // Note: this is no longer used in Merlin, but we still suppport + // it for compatibility. + static void print_stack_trace(oop throwable, oop print_stream); + static void print_stack_element(Handle stream, methodOop method, int bci); + static void print_stack_element(outputStream *st, methodOop method, int bci); + static void print_stack_usage(Handle stream); + + // Allocate space for backtrace (created but stack trace not filled in) + static void allocate_backtrace(Handle throwable, TRAPS); + // Fill in current stack trace for throwable with preallocated backtrace (no GC) + static void fill_in_stack_trace_of_preallocated_backtrace(Handle throwable); + + // Fill in current stack trace, can cause GC + static void fill_in_stack_trace(Handle throwable, TRAPS); + static void fill_in_stack_trace(Handle throwable); + // Programmatic access to stack trace + static oop get_stack_trace_element(oop throwable, int index, TRAPS); + static int get_stack_trace_depth(oop throwable, TRAPS); + // Printing + static void print(oop throwable, outputStream* st); + static void print(Handle throwable, outputStream* st); + static void print_stack_trace(oop throwable, outputStream* st); + // Debugging + friend class JavaClasses; +}; + + +// Interface to java.lang.reflect.AccessibleObject objects + +class java_lang_reflect_AccessibleObject: AllStatic { + private: + // Note that to reduce dependencies on the JDK we compute these + // offsets at run-time. + static int override_offset; + + static void compute_offsets(); + + public: + // Accessors + static jboolean override(oop reflect); + static void set_override(oop reflect, jboolean value); + + // Debugging + friend class JavaClasses; +}; + + +// Interface to java.lang.reflect.Method objects + +class java_lang_reflect_Method : public java_lang_reflect_AccessibleObject { + private: + // Note that to reduce dependencies on the JDK we compute these + // offsets at run-time. + static int clazz_offset; + static int name_offset; + static int returnType_offset; + static int parameterTypes_offset; + static int exceptionTypes_offset; + static int slot_offset; + static int modifiers_offset; + static int signature_offset; + static int annotations_offset; + static int parameter_annotations_offset; + static int annotation_default_offset; + + static void compute_offsets(); + + public: + // Allocation + static Handle create(TRAPS); + + // Accessors + static oop clazz(oop reflect); + static void set_clazz(oop reflect, oop value); + + static oop name(oop method); + static void set_name(oop method, oop value); + + static oop return_type(oop method); + static void set_return_type(oop method, oop value); + + static oop parameter_types(oop method); + static void set_parameter_types(oop method, oop value); + + static oop exception_types(oop method); + static void set_exception_types(oop method, oop value); + + static int slot(oop reflect); + static void set_slot(oop reflect, int value); + + static int modifiers(oop method); + static void set_modifiers(oop method, int value); + + static bool has_signature_field(); + static oop signature(oop method); + static void set_signature(oop method, oop value); + + static bool has_annotations_field(); + static oop annotations(oop method); + static void set_annotations(oop method, oop value); + + static bool has_parameter_annotations_field(); + static oop parameter_annotations(oop method); + static void set_parameter_annotations(oop method, oop value); + + static bool has_annotation_default_field(); + static oop annotation_default(oop method); + static void set_annotation_default(oop method, oop value); + + // Debugging + friend class JavaClasses; +}; + + +// Interface to java.lang.reflect.Constructor objects + +class java_lang_reflect_Constructor : public java_lang_reflect_AccessibleObject { + private: + // Note that to reduce dependencies on the JDK we compute these + // offsets at run-time. + static int clazz_offset; + static int parameterTypes_offset; + static int exceptionTypes_offset; + static int slot_offset; + static int modifiers_offset; + static int signature_offset; + static int annotations_offset; + static int parameter_annotations_offset; + + static void compute_offsets(); + + public: + // Allocation + static Handle create(TRAPS); + + // Accessors + static oop clazz(oop reflect); + static void set_clazz(oop reflect, oop value); + + static oop parameter_types(oop constructor); + static void set_parameter_types(oop constructor, oop value); + + static oop exception_types(oop constructor); + static void set_exception_types(oop constructor, oop value); + + static int slot(oop reflect); + static void set_slot(oop reflect, int value); + + static int modifiers(oop constructor); + static void set_modifiers(oop constructor, int value); + + static bool has_signature_field(); + static oop signature(oop constructor); + static void set_signature(oop constructor, oop value); + + static bool has_annotations_field(); + static oop annotations(oop constructor); + static void set_annotations(oop constructor, oop value); + + static bool has_parameter_annotations_field(); + static oop parameter_annotations(oop method); + static void set_parameter_annotations(oop method, oop value); + + // Debugging + friend class JavaClasses; +}; + + +// Interface to java.lang.reflect.Field objects + +class java_lang_reflect_Field : public java_lang_reflect_AccessibleObject { + private: + // Note that to reduce dependencies on the JDK we compute these + // offsets at run-time. + static int clazz_offset; + static int name_offset; + static int type_offset; + static int slot_offset; + static int modifiers_offset; + static int signature_offset; + static int annotations_offset; + + static void compute_offsets(); + + public: + // Allocation + static Handle create(TRAPS); + + // Accessors + static oop clazz(oop reflect); + static void set_clazz(oop reflect, oop value); + + static oop name(oop field); + static void set_name(oop field, oop value); + + static oop type(oop field); + static void set_type(oop field, oop value); + + static int slot(oop reflect); + static void set_slot(oop reflect, int value); + + static int modifiers(oop field); + static void set_modifiers(oop field, int value); + + static bool has_signature_field(); + static oop signature(oop constructor); + static void set_signature(oop constructor, oop value); + + static bool has_annotations_field(); + static oop annotations(oop constructor); + static void set_annotations(oop constructor, oop value); + + static bool has_parameter_annotations_field(); + static oop parameter_annotations(oop method); + static void set_parameter_annotations(oop method, oop value); + + static bool has_annotation_default_field(); + static oop annotation_default(oop method); + static void set_annotation_default(oop method, oop value); + + // Debugging + friend class JavaClasses; +}; + +// Interface to sun.reflect.ConstantPool objects +class sun_reflect_ConstantPool { + private: + // Note that to reduce dependencies on the JDK we compute these + // offsets at run-time. + static int _cp_oop_offset; + + static void compute_offsets(); + + public: + // Allocation + static Handle create(TRAPS); + + // Accessors + static oop cp_oop(oop reflect); + static void set_cp_oop(oop reflect, oop value); + static int cp_oop_offset() { + return _cp_oop_offset; + } + + // Debugging + friend class JavaClasses; +}; + +// Interface to sun.reflect.UnsafeStaticFieldAccessorImpl objects +class sun_reflect_UnsafeStaticFieldAccessorImpl { + private: + static int _base_offset; + static void compute_offsets(); + + public: + static int base_offset() { + return _base_offset; + } + + // Debugging + friend class JavaClasses; +}; + +// Interface to java.lang primitive type boxing objects: +// - java.lang.Boolean +// - java.lang.Character +// - java.lang.Float +// - java.lang.Double +// - java.lang.Byte +// - java.lang.Short +// - java.lang.Integer +// - java.lang.Long + +// This could be separated out into 8 individual classes. + +class java_lang_boxing_object: AllStatic { + private: + enum { + hc_value_offset = 0 + }; + static int value_offset; + + static oop initialize_and_allocate(klassOop klass, TRAPS); + public: + // Allocation. Returns a boxed value, or NULL for invalid type. + static oop create(BasicType type, jvalue* value, TRAPS); + // Accessors. Returns the basic type being boxed, or T_ILLEGAL for invalid oop. + static BasicType get_value(oop box, jvalue* value); + static BasicType set_value(oop box, jvalue* value); + + static int value_offset_in_bytes() { return value_offset; } + + // Debugging + friend class JavaClasses; +}; + + + +// Interface to java.lang.ref.Reference objects + +class java_lang_ref_Reference: AllStatic { + public: + enum { + hc_referent_offset = 0, + hc_queue_offset = 1, + hc_next_offset = 2, + hc_discovered_offset = 3 // Is not last, see SoftRefs. + }; + enum { + hc_static_lock_offset = 0, + hc_static_pending_offset = 1 + }; + + static int referent_offset; + static int queue_offset; + static int next_offset; + static int discovered_offset; + static int static_lock_offset; + static int static_pending_offset; + static int number_of_fake_oop_fields; + + // Accessors + static oop referent(oop ref) { return *referent_addr(ref); } + static void set_referent(oop ref, oop value); + static oop* referent_addr(oop ref); + + static oop next(oop ref) { return *next_addr(ref); } + static void set_next(oop ref, oop value); + static oop* next_addr(oop ref); + + static oop discovered(oop ref) { return *discovered_addr(ref); } + static void set_discovered(oop ref, oop value); + static oop* discovered_addr(oop ref); + + // Accessors for statics + static oop pending_list_lock() { return *pending_list_lock_addr(); } + static oop pending_list() { return *pending_list_addr(); } + + static oop* pending_list_lock_addr(); + static oop* pending_list_addr(); +}; + + +// Interface to java.lang.ref.SoftReference objects + +class java_lang_ref_SoftReference: public java_lang_ref_Reference { + public: + enum { + // The timestamp is a long field and may need to be adjusted for alignment. + hc_timestamp_offset = align_object_offset_(hc_discovered_offset + 1) + }; + enum { + hc_static_clock_offset = 0 + }; + + static int timestamp_offset; + static int static_clock_offset; + + // Accessors + static jlong timestamp(oop ref); + + // Accessors for statics + static jlong clock(); + static void set_clock(jlong value); +}; + + +// Interface to java.security.AccessControlContext objects + +class java_security_AccessControlContext: AllStatic { + private: + // Note that for this class the layout changed between JDK1.2 and JDK1.3, + // so we compute the offsets at startup rather than hard-wiring them. + static int _context_offset; + static int _privilegedContext_offset; + static int _isPrivileged_offset; + + static void compute_offsets(); + public: + static oop create(objArrayHandle context, bool isPrivileged, Handle privileged_context, TRAPS); + + // Debugging/initialization + friend class JavaClasses; +}; + + +// Interface to java.lang.ClassLoader objects + +class java_lang_ClassLoader : AllStatic { + private: + enum { + hc_parent_offset = 0 + }; + + static int parent_offset; + + public: + static oop parent(oop loader); + + static bool is_trusted_loader(oop loader); + + // Fix for 4474172 + static oop non_reflection_class_loader(oop loader); + + // Debugging + friend class JavaClasses; +}; + + +// Interface to java.lang.System objects + +class java_lang_System : AllStatic { + private: + enum { + hc_static_in_offset = 0, + hc_static_out_offset = 1, + hc_static_err_offset = 2 + }; + + static int offset_of_static_fields; + static int static_in_offset; + static int static_out_offset; + static int static_err_offset; + + static void compute_offsets(); + + public: + static int in_offset_in_bytes(); + static int out_offset_in_bytes(); + static int err_offset_in_bytes(); + + // Debugging + friend class JavaClasses; +}; + + +// Interface to java.lang.StackTraceElement objects + +class java_lang_StackTraceElement: AllStatic { + private: + enum { + hc_declaringClass_offset = 0, + hc_methodName_offset = 1, + hc_fileName_offset = 2, + hc_lineNumber_offset = 3 + }; + + static int declaringClass_offset; + static int methodName_offset; + static int fileName_offset; + static int lineNumber_offset; + + public: + // Setters + static void set_declaringClass(oop element, oop value); + static void set_methodName(oop element, oop value); + static void set_fileName(oop element, oop value); + static void set_lineNumber(oop element, int value); + + // Create an instance of StackTraceElement + static oop create(methodHandle m, int bci, TRAPS); + + // Debugging + friend class JavaClasses; +}; + + +// Interface to java.lang.AssertionStatusDirectives objects + +class java_lang_AssertionStatusDirectives: AllStatic { + private: + enum { + hc_classes_offset, + hc_classEnabled_offset, + hc_packages_offset, + hc_packageEnabled_offset, + hc_deflt_offset + }; + + static int classes_offset; + static int classEnabled_offset; + static int packages_offset; + static int packageEnabled_offset; + static int deflt_offset; + + public: + // Setters + static void set_classes(oop obj, oop val); + static void set_classEnabled(oop obj, oop val); + static void set_packages(oop obj, oop val); + static void set_packageEnabled(oop obj, oop val); + static void set_deflt(oop obj, bool val); + // Debugging + friend class JavaClasses; +}; + + +class java_nio_Buffer: AllStatic { + private: + static int _limit_offset; + + public: + static int limit_offset(); + static void compute_offsets(); +}; + +class sun_misc_AtomicLongCSImpl: AllStatic { + private: + static int _value_offset; + + public: + static int value_offset(); + static void compute_offsets(); +}; + +class java_util_concurrent_locks_AbstractOwnableSynchronizer : AllStatic { + private: + static int _owner_offset; + public: + static void initialize(TRAPS); + static oop get_owner_threadObj(oop obj); +}; + +// Interface to hard-coded offset checking + +class JavaClasses : AllStatic { + private: + static bool check_offset(const char *klass_name, int offset, const char *field_name, const char* field_sig) PRODUCT_RETURN0; + static bool check_static_offset(const char *klass_name, int hardcoded_offset, const char *field_name, const char* field_sig) PRODUCT_RETURN0; + public: + static void compute_hard_coded_offsets(); + static void compute_offsets(); + static void check_offsets() PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/classfile/loaderConstraints.cpp b/hotspot/src/share/vm/classfile/loaderConstraints.cpp new file mode 100644 index 00000000000..daaaa327556 --- /dev/null +++ b/hotspot/src/share/vm/classfile/loaderConstraints.cpp @@ -0,0 +1,507 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_loaderConstraints.cpp.incl" + +LoaderConstraintTable::LoaderConstraintTable(int nof_buckets) + : Hashtable(nof_buckets, sizeof(LoaderConstraintEntry)) {}; + + +LoaderConstraintEntry* LoaderConstraintTable::new_entry( + unsigned int hash, symbolOop name, + klassOop klass, int num_loaders, + int max_loaders) { + LoaderConstraintEntry* entry; + entry = (LoaderConstraintEntry*)Hashtable::new_entry(hash, klass); + entry->set_name(name); + entry->set_num_loaders(num_loaders); + entry->set_max_loaders(max_loaders); + return entry; +} + + +void LoaderConstraintTable::oops_do(OopClosure* f) { + for (int index = 0; index < table_size(); index++) { + for (LoaderConstraintEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + f->do_oop((oop*)(probe->name_addr())); + if (probe->klass() != NULL) { + f->do_oop((oop*)probe->klass_addr()); + } + for (int n = 0; n < probe->num_loaders(); n++) { + if (probe->loader(n) != NULL) { + f->do_oop(probe->loader_addr(n)); + } + } + } + } +} + +// We must keep the symbolOop used in the name alive. We'll use the +// loaders to decide if a particular entry can be purged. +void LoaderConstraintTable::always_strong_classes_do(OopClosure* blk) { + // We must keep the symbolOop used in the name alive. + for (int cindex = 0; cindex < table_size(); cindex++) { + for (LoaderConstraintEntry* lc_probe = bucket(cindex); + lc_probe != NULL; + lc_probe = lc_probe->next()) { + assert (lc_probe->name() != NULL, "corrupted loader constraint table"); + blk->do_oop((oop*)lc_probe->name_addr()); + } + } +} + + +// The loaderConstraintTable must always be accessed with the +// SystemDictionary lock held. This is true even for readers as +// entries in the table could be being dynamically resized. + +LoaderConstraintEntry** LoaderConstraintTable::find_loader_constraint( + symbolHandle name, Handle loader) { + + unsigned int hash = compute_hash(name); + int index = hash_to_index(hash); + LoaderConstraintEntry** pp = bucket_addr(index); + while (*pp) { + LoaderConstraintEntry* p = *pp; + if (p->hash() == hash) { + if (p->name() == name()) { + for (int i = p->num_loaders() - 1; i >= 0; i--) { + if (p->loader(i) == loader()) { + return pp; + } + } + } + } + pp = p->next_addr(); + } + return pp; +} + + +void LoaderConstraintTable::purge_loader_constraints(BoolObjectClosure* is_alive) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint") + // Remove unloaded entries from constraint table + for (int index = 0; index < table_size(); index++) { + LoaderConstraintEntry** p = bucket_addr(index); + while(*p) { + LoaderConstraintEntry* probe = *p; + klassOop klass = probe->klass(); + // Remove klass that is no longer alive + if (klass != NULL && !is_alive->do_object_b(klass)) { + probe->set_klass(NULL); + if (TraceLoaderConstraints) { + ResourceMark rm; + tty->print_cr("[Purging class object from constraint for name %s," + " loader list:", + probe->name()->as_C_string()); + for (int i = 0; i < probe->num_loaders(); i++) { + tty->print_cr("[ [%d]: %s", i, + SystemDictionary::loader_name(probe->loader(i))); + } + } + } + // Remove entries no longer alive from loader array + int n = 0; + while (n < probe->num_loaders()) { + if (probe->loader(n) != NULL) { + if (!is_alive->do_object_b(probe->loader(n))) { + if (TraceLoaderConstraints) { + ResourceMark rm; + tty->print_cr("[Purging loader %s from constraint for name %s", + SystemDictionary::loader_name(probe->loader(n)), + probe->name()->as_C_string() + ); + } + + // Compact array + int num = probe->num_loaders() - 1; + probe->set_num_loaders(num); + probe->set_loader(n, probe->loader(num)); + probe->set_loader(num, NULL); + + if (TraceLoaderConstraints) { + ResourceMark rm; + tty->print_cr("[New loader list:"); + for (int i = 0; i < probe->num_loaders(); i++) { + tty->print_cr("[ [%d]: %s", i, + SystemDictionary::loader_name(probe->loader(i))); + } + } + + continue; // current element replaced, so restart without + // incrementing n + } + } + n++; + } + // Check whether entry should be purged + if (probe->num_loaders() < 2) { + if (TraceLoaderConstraints) { + ResourceMark rm; + tty->print("[Purging complete constraint for name %s\n", + probe->name()->as_C_string()); + } + + // Purge entry + *p = probe->next(); + FREE_C_HEAP_ARRAY(oop, probe->loaders()); + free_entry(probe); + } else { +#ifdef ASSERT + assert(is_alive->do_object_b(probe->name()), "name should be live"); + if (probe->klass() != NULL) { + assert(is_alive->do_object_b(probe->klass()), "klass should be live"); + } + for (n = 0; n < probe->num_loaders(); n++) { + if (probe->loader(n) != NULL) { + assert(is_alive->do_object_b(probe->loader(n)), "loader should be live"); + } + } +#endif + // Go to next entry + p = probe->next_addr(); + } + } + } +} + +bool LoaderConstraintTable::add_entry(symbolHandle class_name, + klassOop klass1, Handle class_loader1, + klassOop klass2, Handle class_loader2) { + int failure_code = 0; // encode different reasons for failing + + if (klass1 != NULL && klass2 != NULL && klass1 != klass2) { + failure_code = 1; + } else { + klassOop klass = klass1 != NULL ? klass1 : klass2; + + LoaderConstraintEntry** pp1 = find_loader_constraint(class_name, + class_loader1); + if (*pp1 != NULL && (*pp1)->klass() != NULL) { + if (klass != NULL) { + if (klass != (*pp1)->klass()) { + failure_code = 2; + } + } else { + klass = (*pp1)->klass(); + } + } + + LoaderConstraintEntry** pp2 = find_loader_constraint(class_name, + class_loader2); + if (*pp2 != NULL && (*pp2)->klass() != NULL) { + if (klass != NULL) { + if (klass != (*pp2)->klass()) { + failure_code = 3; + } + } else { + klass = (*pp2)->klass(); + } + } + + if (failure_code == 0) { + if (*pp1 == NULL && *pp2 == NULL) { + unsigned int hash = compute_hash(class_name); + int index = hash_to_index(hash); + LoaderConstraintEntry* p; + p = new_entry(hash, class_name(), klass, 2, 2); + p->set_loaders(NEW_C_HEAP_ARRAY(oop, 2)); + p->set_loader(0, class_loader1()); + p->set_loader(1, class_loader2()); + p->set_klass(klass); + p->set_next(bucket(index)); + set_entry(index, p); + if (TraceLoaderConstraints) { + ResourceMark rm; + tty->print("[Adding new constraint for name: %s, loader[0]: %s," + " loader[1]: %s ]\n", + class_name()->as_C_string(), + SystemDictionary::loader_name(class_loader1()), + SystemDictionary::loader_name(class_loader2()) + ); + } + } else if (*pp1 == *pp2) { + /* constraint already imposed */ + if ((*pp1)->klass() == NULL) { + (*pp1)->set_klass(klass); + if (TraceLoaderConstraints) { + ResourceMark rm; + tty->print("[Setting class object in existing constraint for" + " name: %s and loader %s ]\n", + class_name()->as_C_string(), + SystemDictionary::loader_name(class_loader1()) + ); + } + } else { + assert((*pp1)->klass() == klass, "loader constraints corrupted"); + } + } else if (*pp1 == NULL) { + extend_loader_constraint(*pp2, class_loader1, klass); + } else if (*pp2 == NULL) { + extend_loader_constraint(*pp1, class_loader2, klass); + } else { + merge_loader_constraints(pp1, pp2, klass); + } + } + } + + if (failure_code != 0 && TraceLoaderConstraints) { + ResourceMark rm; + const char* reason = ""; + switch(failure_code) { + case 1: reason = "the class objects presented by loader[0] and loader[1]" + " are different"; break; + case 2: reason = "the class object presented by loader[0] does not match" + " the stored class object in the constraint"; break; + case 3: reason = "the class object presented by loader[1] does not match" + " the stored class object in the constraint"; break; + default: reason = "unknown reason code"; + } + tty->print("[Failed to add constraint for name: %s, loader[0]: %s," + " loader[1]: %s, Reason: %s ]\n", + class_name()->as_C_string(), + SystemDictionary::loader_name(class_loader1()), + SystemDictionary::loader_name(class_loader2()), + reason + ); + } + + return failure_code == 0; +} + + +// return true if the constraint was updated, false if the constraint is +// violated +bool LoaderConstraintTable::check_or_update(instanceKlassHandle k, + Handle loader, + symbolHandle name) { + LoaderConstraintEntry* p = *(find_loader_constraint(name, loader)); + if (p && p->klass() != NULL && p->klass() != k()) { + if (TraceLoaderConstraints) { + ResourceMark rm; + tty->print("[Constraint check failed for name %s, loader %s: " + "the presented class object differs from that stored ]\n", + name()->as_C_string(), + SystemDictionary::loader_name(loader())); + } + return false; + } else { + if (p && p->klass() == NULL) { + p->set_klass(k()); + if (TraceLoaderConstraints) { + ResourceMark rm; + tty->print("[Updating constraint for name %s, loader %s, " + "by setting class object ]\n", + name()->as_C_string(), + SystemDictionary::loader_name(loader())); + } + } + return true; + } +} + +klassOop LoaderConstraintTable::find_constrained_klass(symbolHandle name, + Handle loader) { + LoaderConstraintEntry *p = *(find_loader_constraint(name, loader)); + if (p != NULL && p->klass() != NULL) + return p->klass(); + + // No constraints, or else no klass loaded yet. + return NULL; +} + + +klassOop LoaderConstraintTable::find_constrained_elem_klass(symbolHandle name, + symbolHandle elem_name, + Handle loader, + TRAPS) { + LoaderConstraintEntry *p = *(find_loader_constraint(name, loader)); + if (p != NULL) { + assert(p->klass() == NULL, "Expecting null array klass"); + + // The array name has a constraint, but it will not have a class. Check + // each loader for an associated elem + for (int i = 0; i < p->num_loaders(); i++) { + Handle no_protection_domain; + + klassOop k = SystemDictionary::find(elem_name, p->loader(i), no_protection_domain, THREAD); + if (k != NULL) { + // Return the first elem klass found. + return k; + } + } + } + + // No constraints, or else no klass loaded yet. + return NULL; +} + + +void LoaderConstraintTable::ensure_loader_constraint_capacity( + LoaderConstraintEntry *p, + int nfree) { + if (p->max_loaders() - p->num_loaders() < nfree) { + int n = nfree + p->num_loaders(); + oop* new_loaders = NEW_C_HEAP_ARRAY(oop, n); + memcpy(new_loaders, p->loaders(), sizeof(oop) * p->num_loaders()); + p->set_max_loaders(n); + FREE_C_HEAP_ARRAY(oop, p->loaders()); + p->set_loaders(new_loaders); + } +} + + +void LoaderConstraintTable::extend_loader_constraint(LoaderConstraintEntry* p, + Handle loader, + klassOop klass) { + ensure_loader_constraint_capacity(p, 1); + int num = p->num_loaders(); + p->set_loader(num, loader()); + p->set_num_loaders(num + 1); + if (TraceLoaderConstraints) { + ResourceMark rm; + tty->print("[Extending constraint for name %s by adding loader[%d]: %s %s", + p->name()->as_C_string(), + num, + SystemDictionary::loader_name(loader()), + (p->klass() == NULL ? " and setting class object ]\n" : " ]\n") + ); + } + if (p->klass() == NULL) { + p->set_klass(klass); + } else { + assert(klass == NULL || p->klass() == klass, "constraints corrupted"); + } +} + + +void LoaderConstraintTable::merge_loader_constraints( + LoaderConstraintEntry** pp1, + LoaderConstraintEntry** pp2, + klassOop klass) { + // make sure *pp1 has higher capacity + if ((*pp1)->max_loaders() < (*pp2)->max_loaders()) { + LoaderConstraintEntry** tmp = pp2; + pp2 = pp1; + pp1 = tmp; + } + + LoaderConstraintEntry* p1 = *pp1; + LoaderConstraintEntry* p2 = *pp2; + + ensure_loader_constraint_capacity(p1, p2->num_loaders()); + + for (int i = 0; i < p2->num_loaders(); i++) { + int num = p1->num_loaders(); + p1->set_loader(num, p2->loader(i)); + p1->set_num_loaders(num + 1); + } + + if (TraceLoaderConstraints) { + ResourceMark rm; + tty->print_cr("[Merged constraints for name %s, new loader list:", + p1->name()->as_C_string() + ); + + for (int i = 0; i < p1->num_loaders(); i++) { + tty->print_cr("[ [%d]: %s", i, + SystemDictionary::loader_name(p1->loader(i))); + } + if (p1->klass() == NULL) { + tty->print_cr("[... and setting class object]"); + } + } + + // p1->klass() will hold NULL if klass, p2->klass(), and old + // p1->klass() are all NULL. In addition, all three must have + // matching non-NULL values, otherwise either the constraints would + // have been violated, or the constraints had been corrupted (and an + // assertion would fail). + if (p2->klass() != NULL) { + assert(p2->klass() == klass, "constraints corrupted"); + } + if (p1->klass() == NULL) { + p1->set_klass(klass); + } else { + assert(p1->klass() == klass, "constraints corrupted"); + } + + *pp2 = p2->next(); + FREE_C_HEAP_ARRAY(oop, p2->loaders()); + free_entry(p2); + return; +} + + +void LoaderConstraintTable::verify(Dictionary* dictionary) { + Thread *thread = Thread::current(); + for (int cindex = 0; cindex < _loader_constraint_size; cindex++) { + for (LoaderConstraintEntry* probe = bucket(cindex); + probe != NULL; + probe = probe->next()) { + guarantee(probe->name()->is_symbol(), "should be symbol"); + if (probe->klass() != NULL) { + instanceKlass* ik = instanceKlass::cast(probe->klass()); + guarantee(ik->name() == probe->name(), "name should match"); + symbolHandle name (thread, ik->name()); + Handle loader(thread, ik->class_loader()); + unsigned int d_hash = dictionary->compute_hash(name, loader); + int d_index = dictionary->hash_to_index(d_hash); + klassOop k = dictionary->find_class(d_index, d_hash, name, loader); + guarantee(k == probe->klass(), "klass should be in dictionary"); + } + for (int n = 0; n< probe->num_loaders(); n++) { + guarantee(probe->loader(n)->is_oop_or_null(), "should be oop"); + } + } + } +} + +#ifndef PRODUCT + +// Called with the system dictionary lock held +void LoaderConstraintTable::print() { + ResourceMark rm; + + assert_locked_or_safepoint(SystemDictionary_lock); + tty->print_cr("Java loader constraints (entries=%d)", _loader_constraint_size); + for (int cindex = 0; cindex < _loader_constraint_size; cindex++) { + for (LoaderConstraintEntry* probe = bucket(cindex); + probe != NULL; + probe = probe->next()) { + tty->print("%4d: ", cindex); + probe->name()->print(); + tty->print(" , loaders:"); + for (int n = 0; n < probe->num_loaders(); n++) { + probe->loader(n)->print_value(); + tty->print(", "); + } + tty->cr(); + } + } +} +#endif diff --git a/hotspot/src/share/vm/classfile/loaderConstraints.hpp b/hotspot/src/share/vm/classfile/loaderConstraints.hpp new file mode 100644 index 00000000000..9d1a6880a89 --- /dev/null +++ b/hotspot/src/share/vm/classfile/loaderConstraints.hpp @@ -0,0 +1,133 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class LoaderConstraintEntry; + +class LoaderConstraintTable : public Hashtable { + friend class VMStructs; +private: + + enum Constants { + _loader_constraint_size = 107, // number of entries in constraint table + _nof_buckets = 1009 // number of buckets in hash table + }; + + LoaderConstraintEntry** find_loader_constraint(symbolHandle name, + Handle loader); + +public: + + LoaderConstraintTable(int nof_buckets); + + LoaderConstraintEntry* new_entry(unsigned int hash, symbolOop name, + klassOop klass, int num_loaders, + int max_loaders); + + LoaderConstraintEntry* bucket(int i) { + return (LoaderConstraintEntry*)Hashtable::bucket(i); + } + + LoaderConstraintEntry** bucket_addr(int i) { + return (LoaderConstraintEntry**)Hashtable::bucket_addr(i); + } + + // GC support + void oops_do(OopClosure* f); + void always_strong_classes_do(OopClosure* blk); + + // Check class loader constraints + bool add_entry(symbolHandle name, klassOop klass1, Handle loader1, + klassOop klass2, Handle loader2); + + void check_signature_loaders(symbolHandle signature, Handle loader1, + Handle loader2, bool is_method, TRAPS); + + klassOop find_constrained_klass(symbolHandle name, Handle loader); + klassOop find_constrained_elem_klass(symbolHandle name, symbolHandle elem_name, + Handle loader, TRAPS); + + + // Class loader constraints + + void ensure_loader_constraint_capacity(LoaderConstraintEntry *p, int nfree); + void extend_loader_constraint(LoaderConstraintEntry* p, Handle loader, + klassOop klass); + void merge_loader_constraints(LoaderConstraintEntry** pp1, + LoaderConstraintEntry** pp2, klassOop klass); + + bool check_or_update(instanceKlassHandle k, Handle loader, + symbolHandle name); + + + void purge_loader_constraints(BoolObjectClosure* is_alive); + + void verify(Dictionary* dictionary); +#ifndef PRODUCT + void print(); +#endif +}; + +class LoaderConstraintEntry : public HashtableEntry { + friend class VMStructs; +private: + symbolOop _name; // class name + int _num_loaders; + int _max_loaders; + oop* _loaders; // initiating loaders + +public: + + klassOop klass() { return (klassOop)literal(); } + klassOop* klass_addr() { return (klassOop*)literal_addr(); } + void set_klass(klassOop k) { set_literal(k); } + + LoaderConstraintEntry* next() { + return (LoaderConstraintEntry*)HashtableEntry::next(); + } + + LoaderConstraintEntry** next_addr() { + return (LoaderConstraintEntry**)HashtableEntry::next_addr(); + } + void set_next(LoaderConstraintEntry* next) { + HashtableEntry::set_next(next); + } + + symbolOop name() { return _name; } + symbolOop* name_addr() { return &_name; } + void set_name(symbolOop name) { _name = name; } + + int num_loaders() { return _num_loaders; } + void set_num_loaders(int i) { _num_loaders = i; } + + int max_loaders() { return _max_loaders; } + void set_max_loaders(int i) { _max_loaders = i; } + + oop* loaders() { return _loaders; } + void set_loaders(oop* loaders) { _loaders = loaders; } + + oop loader(int i) { return _loaders[i]; } + oop* loader_addr(int i) { return &_loaders[i]; } + void set_loader(int i, oop p) { _loaders[i] = p; } + +}; diff --git a/hotspot/src/share/vm/classfile/placeholders.cpp b/hotspot/src/share/vm/classfile/placeholders.cpp new file mode 100644 index 00000000000..8f5b3fd3280 --- /dev/null +++ b/hotspot/src/share/vm/classfile/placeholders.cpp @@ -0,0 +1,269 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_placeholders.cpp.incl" + +// Placeholder methods + +PlaceholderEntry* PlaceholderTable::new_entry(int hash, symbolOop name, + oop loader, bool havesupername, + symbolOop supername) { + PlaceholderEntry* entry = (PlaceholderEntry*)Hashtable::new_entry(hash, name); + entry->set_loader(loader); + entry->set_havesupername(havesupername); + entry->set_supername(supername); + entry->set_superThreadQ(NULL); + entry->set_loadInstanceThreadQ(NULL); + entry->set_defineThreadQ(NULL); + entry->set_definer(NULL); + entry->set_instanceKlass(NULL); + return entry; +} + + +// Placeholder objects represent classes currently being loaded. +// All threads examining the placeholder table must hold the +// SystemDictionary_lock, so we don't need special precautions +// on store ordering here. +void PlaceholderTable::add_entry(int index, unsigned int hash, + symbolHandle class_name, Handle class_loader, + bool havesupername, symbolHandle supername){ + assert_locked_or_safepoint(SystemDictionary_lock); + assert(!class_name.is_null(), "adding NULL obj"); + + // Both readers and writers are locked so it's safe to just + // create the placeholder and insert it in the list without a membar. + PlaceholderEntry* entry = new_entry(hash, class_name(), class_loader(), havesupername, supername()); + add_entry(index, entry); +} + + +// Remove a placeholder object. +void PlaceholderTable::remove_entry(int index, unsigned int hash, + symbolHandle class_name, + Handle class_loader) { + assert_locked_or_safepoint(SystemDictionary_lock); + PlaceholderEntry** p = bucket_addr(index); + while (*p) { + PlaceholderEntry *probe = *p; + if (probe->hash() == hash && probe->equals(class_name(), class_loader())) { + // Delete entry + *p = probe->next(); + free_entry(probe); + return; + } + p = probe->next_addr(); + } +} + +PlaceholderEntry* PlaceholderTable::get_entry(int index, unsigned int hash, + symbolHandle class_name, + Handle class_loader) { + assert_locked_or_safepoint(SystemDictionary_lock); + + symbolOop class_name_ = class_name(); + oop class_loader_ = class_loader(); + + for (PlaceholderEntry *place_probe = bucket(index); + place_probe != NULL; + place_probe = place_probe->next()) { + if (place_probe->hash() == hash && + place_probe->equals(class_name_, class_loader_)) { + return place_probe; + } + } + return NULL; +} + +symbolOop PlaceholderTable::find_entry(int index, unsigned int hash, + symbolHandle class_name, + Handle class_loader) { + PlaceholderEntry* probe = get_entry(index, hash, class_name, class_loader); + return (probe? probe->klass(): symbolOop(NULL)); +} + + // find_and_add returns probe pointer - old or new + // If no entry exists, add a placeholder entry + // If entry exists, reuse entry + // For both, push SeenThread for classloadAction + // if havesupername: this is used for circularity for instanceklass loading +PlaceholderEntry* PlaceholderTable::find_and_add(int index, unsigned int hash, symbolHandle name, Handle loader, classloadAction action, symbolHandle supername, Thread* thread) { + PlaceholderEntry* probe = get_entry(index, hash, name, loader); + if (probe == NULL) { + // Nothing found, add place holder + add_entry(index, hash, name, loader, (action == LOAD_SUPER), supername); + probe = get_entry(index, hash, name, loader); + } else { + if (action == LOAD_SUPER) { + probe->set_havesupername(true); + probe->set_supername(supername()); + } + } + if (probe) probe->add_seen_thread(thread, action); + return probe; +} + + +// placeholder used to track class loading internal states +// placeholder existence now for loading superclass/superinterface +// superthreadQ tracks class circularity, while loading superclass/superinterface +// loadInstanceThreadQ tracks load_instance_class calls +// definer() tracks the single thread that owns define token +// defineThreadQ tracks waiters on defining thread's results +// 1st claimant creates placeholder +// find_and_add adds SeenThread entry for appropriate queue +// All claimants remove SeenThread after completing action +// On removal: if definer and all queues empty, remove entry +// Note: you can be in both placeholders and systemDictionary +// see parse_stream for redefine classes +// Therefore - must always check SD first +// Ignores the case where entry is not found +void PlaceholderTable::find_and_remove(int index, unsigned int hash, + symbolHandle name, Handle loader, Thread* thread) { + assert_locked_or_safepoint(SystemDictionary_lock); + PlaceholderEntry *probe = get_entry(index, hash, name, loader); + if (probe != NULL) { + // No other threads using this entry + if ((probe->superThreadQ() == NULL) && (probe->loadInstanceThreadQ() == NULL) + && (probe->defineThreadQ() == NULL) && (probe->definer() == NULL)) { + remove_entry(index, hash, name, loader); + } + } + } + +PlaceholderTable::PlaceholderTable(int table_size) + : TwoOopHashtable(table_size, sizeof(PlaceholderEntry)) { +} + + +void PlaceholderTable::oops_do(OopClosure* f) { + for (int index = 0; index < table_size(); index++) { + for (PlaceholderEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + probe->oops_do(f); + } + } +} + + +void PlaceholderEntry::oops_do(OopClosure* blk) { + assert(klass() != NULL, "should have a non-null klass"); + blk->do_oop((oop*)klass_addr()); + if (_loader != NULL) { + blk->do_oop(loader_addr()); + } + if (_supername != NULL) { + blk->do_oop((oop*)supername_addr()); + } + if (_instanceKlass != NULL) { + blk->do_oop((oop*)instanceKlass_addr()); + } +} + +// do all entries in the placeholder table +void PlaceholderTable::entries_do(void f(symbolOop, oop)) { + for (int index = 0; index < table_size(); index++) { + for (PlaceholderEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + f(probe->klass(), probe->loader()); + } + } +} + + +#ifndef PRODUCT +// Note, doesn't append a cr +void PlaceholderEntry::print() const { + klass()->print_value(); + if (loader() != NULL) { + tty->print(", loader "); + loader()->print_value(); + } + if (supername() != NULL) { + tty->print(", supername "); + supername()->print_value(); + } + if (definer() != NULL) { + tty->print(", definer "); + definer()->print_value(); + } + if (instanceKlass() != NULL) { + tty->print(", instanceKlass "); + instanceKlass()->print_value(); + } + tty->print("\n"); + tty->print("loadInstanceThreadQ threads:"); + loadInstanceThreadQ()->printActionQ(); + tty->print("\n"); + tty->print("superThreadQ threads:"); + superThreadQ()->printActionQ(); + tty->print("\n"); + tty->print("defineThreadQ threads:"); + defineThreadQ()->printActionQ(); + tty->print("\n"); +} +#endif + +void PlaceholderEntry::verify() const { + guarantee(loader() == NULL || loader()->is_instance(), + "checking type of _loader"); + guarantee(instanceKlass() == NULL + || Klass::cast(instanceKlass())->oop_is_instance(), + "checking type of instanceKlass result"); + klass()->verify(); +} + +void PlaceholderTable::verify() { + int element_count = 0; + for (int pindex = 0; pindex < table_size(); pindex++) { + for (PlaceholderEntry* probe = bucket(pindex); + probe != NULL; + probe = probe->next()) { + probe->verify(); + element_count++; // both klasses and place holders count + } + } + guarantee(number_of_entries() == element_count, + "Verify of system dictionary failed"); +} + + +#ifndef PRODUCT +void PlaceholderTable::print() { + for (int pindex = 0; pindex < table_size(); pindex++) { + for (PlaceholderEntry* probe = bucket(pindex); + probe != NULL; + probe = probe->next()) { + if (Verbose) tty->print("%4d: ", pindex); + tty->print(" place holder "); + + probe->print(); + tty->cr(); + } + } +} +#endif diff --git a/hotspot/src/share/vm/classfile/placeholders.hpp b/hotspot/src/share/vm/classfile/placeholders.hpp new file mode 100644 index 00000000000..1c01ddfd824 --- /dev/null +++ b/hotspot/src/share/vm/classfile/placeholders.hpp @@ -0,0 +1,331 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class PlaceholderEntry; + +// Placeholder objects. These represent classes currently +// being loaded, as well as arrays of primitives. +// + +class PlaceholderTable : public TwoOopHashtable { + friend class VMStructs; + +public: + PlaceholderTable(int table_size); + + PlaceholderEntry* new_entry(int hash, symbolOop name, oop loader, bool havesupername, symbolOop supername); + + PlaceholderEntry* bucket(int i) { + return (PlaceholderEntry*)Hashtable::bucket(i); + } + + PlaceholderEntry** bucket_addr(int i) { + return (PlaceholderEntry**)Hashtable::bucket_addr(i); + } + + void add_entry(int index, PlaceholderEntry* new_entry) { + Hashtable::add_entry(index, (HashtableEntry*)new_entry); + } + + void add_entry(int index, unsigned int hash, symbolHandle name, + Handle loader, bool havesupername, symbolHandle supername); + +// This returns a symbolOop to match type for SystemDictionary + symbolOop find_entry(int index, unsigned int hash, + symbolHandle name, Handle loader); + + PlaceholderEntry* get_entry(int index, unsigned int hash, + symbolHandle name, Handle loader); + +// caller to create a placeholder entry must enumerate an action +// caller claims ownership of that action +// For parallel classloading: +// multiple LOAD_INSTANCE threads can proceed in parallel +// multiple LOAD_SUPER threads can proceed in parallel +// LOAD_SUPER needed to check for class circularity +// DEFINE_CLASS: ultimately define class must be single threaded +// on a class/classloader basis +// so the head of that queue owns the token +// and the rest of the threads return the result the first thread gets + enum classloadAction { + LOAD_INSTANCE = 1, // calling load_instance_class + LOAD_SUPER = 2, // loading superclass for this class + DEFINE_CLASS = 3 // find_or_define class + }; + + // find_and_add returns probe pointer - old or new + // If no entry exists, add a placeholder entry and push SeenThread + // If entry exists, reuse entry and push SeenThread for classloadAction + PlaceholderEntry* find_and_add(int index, unsigned int hash, + symbolHandle name, Handle loader, + classloadAction action, symbolHandle supername, + Thread* thread); + + void remove_entry(int index, unsigned int hash, + symbolHandle name, Handle loader); + +// Remove placeholder information + void find_and_remove(int index, unsigned int hash, + symbolHandle name, Handle loader, Thread* thread); + + // GC support. + void oops_do(OopClosure* f); + + // JVMTI support + void entries_do(void f(symbolOop, oop)); + +#ifndef PRODUCT + void print(); +#endif + void verify(); +}; + +// SeenThread objects represent list of threads that are +// currently performing a load action on a class. +// For class circularity, set before loading a superclass. +// For bootclasssearchpath, set before calling load_instance_class. +// Defining must be single threaded on a class/classloader basis +// For DEFINE_CLASS, the head of the queue owns the +// define token and the rest of the threads wait to return the +// result the first thread gets. +class SeenThread: public CHeapObj { +private: + Thread *_thread; + SeenThread* _stnext; + SeenThread* _stprev; +public: + SeenThread(Thread *thread) { + _thread = thread; + _stnext = NULL; + _stprev = NULL; + } + Thread* thread() const { return _thread;} + void set_thread(Thread *thread) { _thread = thread; } + + SeenThread* next() const { return _stnext;} + void set_next(SeenThread *seen) { _stnext = seen; } + void set_prev(SeenThread *seen) { _stprev = seen; } + +#ifndef PRODUCT + void printActionQ() { + SeenThread* seen = this; + while (seen != NULL) { + seen->thread()->print_value(); + tty->print(", "); + seen = seen->next(); + } + } +#endif // PRODUCT +}; + +// Placeholder objects represent classes currently being loaded. +// All threads examining the placeholder table must hold the +// SystemDictionary_lock, so we don't need special precautions +// on store ordering here. +// The system dictionary is the only user of this class. + +class PlaceholderEntry : public HashtableEntry { + friend class VMStructs; + + + private: + oop _loader; // initiating loader + bool _havesupername; // distinguish between null supername, and unknown + symbolOop _supername; + Thread* _definer; // owner of define token + klassOop _instanceKlass; // instanceKlass from successful define + SeenThread* _superThreadQ; // doubly-linked queue of Threads loading a superclass for this class + SeenThread* _loadInstanceThreadQ; // loadInstance thread + // can be multiple threads if classloader object lock broken by application + // or if classloader supports parallel classloading + + SeenThread* _defineThreadQ; // queue of Threads trying to define this class + // including _definer + // _definer owns token + // queue waits for and returns results from _definer + + public: + // Simple accessors, used only by SystemDictionary + symbolOop klass() const { return (symbolOop)literal(); } + symbolOop* klass_addr() { return (symbolOop*)literal_addr(); } + + oop loader() const { return _loader; } + void set_loader(oop loader) { _loader = loader; } + oop* loader_addr() { return &_loader; } + + bool havesupername() const { return _havesupername; } + void set_havesupername(bool havesupername) { _havesupername = havesupername; } + + symbolOop supername() const { return _supername; } + void set_supername(symbolOop supername) { _supername = supername; } + symbolOop* supername_addr() { return &_supername; } + + Thread* definer() const {return _definer; } + void set_definer(Thread* definer) { _definer = definer; } + + klassOop instanceKlass() const {return _instanceKlass; } + void set_instanceKlass(klassOop instanceKlass) { _instanceKlass = instanceKlass; } + klassOop* instanceKlass_addr() { return &_instanceKlass; } + + SeenThread* superThreadQ() const { return _superThreadQ; } + void set_superThreadQ(SeenThread* SeenThread) { _superThreadQ = SeenThread; } + + SeenThread* loadInstanceThreadQ() const { return _loadInstanceThreadQ; } + void set_loadInstanceThreadQ(SeenThread* SeenThread) { _loadInstanceThreadQ = SeenThread; } + + SeenThread* defineThreadQ() const { return _defineThreadQ; } + void set_defineThreadQ(SeenThread* SeenThread) { _defineThreadQ = SeenThread; } + + PlaceholderEntry* next() const { + return (PlaceholderEntry*)HashtableEntry::next(); + } + + PlaceholderEntry** next_addr() { + return (PlaceholderEntry**)HashtableEntry::next_addr(); + } + + // Test for equality + // Entries are unique for class/classloader name pair + bool equals(symbolOop class_name, oop class_loader) const { + return (klass() == class_name && loader() == class_loader); + } + + SeenThread* actionToQueue(PlaceholderTable::classloadAction action) { + SeenThread* queuehead; + switch (action) { + case PlaceholderTable::LOAD_INSTANCE: + queuehead = _loadInstanceThreadQ; + break; + case PlaceholderTable::LOAD_SUPER: + queuehead = _superThreadQ; + break; + case PlaceholderTable::DEFINE_CLASS: + queuehead = _defineThreadQ; + break; + default: Unimplemented(); + } + return queuehead; + } + + void set_threadQ(SeenThread* seenthread, PlaceholderTable::classloadAction action) { + switch (action) { + case PlaceholderTable::LOAD_INSTANCE: + _loadInstanceThreadQ = seenthread; + break; + case PlaceholderTable::LOAD_SUPER: + _superThreadQ = seenthread; + break; + case PlaceholderTable::DEFINE_CLASS: + _defineThreadQ = seenthread; + break; + default: Unimplemented(); + } + return; + } + + bool super_load_in_progress() { + return (_superThreadQ != NULL); + } + + bool instance_load_in_progress() { + return (_loadInstanceThreadQ != NULL); + } + + bool define_class_in_progress() { + return (_defineThreadQ != NULL); + } + +// Doubly-linked list of Threads per action for class/classloader pair +// Class circularity support: links in thread before loading superclass +// bootstrapsearchpath support: links in a thread before load_instance_class +// definers: use as queue of define requestors, including owner of +// define token. Appends for debugging of requestor order + void add_seen_thread(Thread* thread, PlaceholderTable::classloadAction action) { + assert_lock_strong(SystemDictionary_lock); + SeenThread* threadEntry = new SeenThread(thread); + SeenThread* seen = actionToQueue(action); + + if (seen == NULL) { + set_threadQ(threadEntry, action); + return; + } + SeenThread* next; + while ((next = seen->next()) != NULL) { + seen = next; + } + seen->set_next(threadEntry); + threadEntry->set_prev(seen); + return; + } + + bool check_seen_thread(Thread* thread, PlaceholderTable::classloadAction action) { + assert_lock_strong(SystemDictionary_lock); + SeenThread* threadQ = actionToQueue(action); + SeenThread* seen = threadQ; + while (seen) { + if (thread == seen->thread()) { + return true; + } + seen = seen->next(); + } + return false; + } + + // returns true if seenthreadQ is now empty + // Note, caller must ensure probe still exists while holding + // SystemDictionary_lock + // ignores if cleanup has already been done + // if found, deletes SeenThread + bool remove_seen_thread(Thread* thread, PlaceholderTable::classloadAction action) { + assert_lock_strong(SystemDictionary_lock); + SeenThread* threadQ = actionToQueue(action); + SeenThread* seen = threadQ; + SeenThread* prev = NULL; + while (seen) { + if (thread == seen->thread()) { + if (prev) { + prev->set_next(seen->next()); + } else { + set_threadQ(seen->next(), action); + } + if (seen->next()) { + seen->next()->set_prev(prev); + } + delete seen; + break; + } + prev = seen; + seen = seen->next(); + } + return (actionToQueue(action) == NULL); + } + + // GC support + // Applies "f->do_oop" to all root oops in the placeholder table. + void oops_do(OopClosure* blk); + + // Print method doesn't append a cr + void print() const PRODUCT_RETURN; + void verify() const; +}; diff --git a/hotspot/src/share/vm/classfile/resolutionErrors.cpp b/hotspot/src/share/vm/classfile/resolutionErrors.cpp new file mode 100644 index 00000000000..397981540df --- /dev/null +++ b/hotspot/src/share/vm/classfile/resolutionErrors.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_resolutionErrors.cpp.incl" + +// add new entry to the table +void ResolutionErrorTable::add_entry(int index, unsigned int hash, + constantPoolHandle pool, int cp_index, symbolHandle error) +{ + assert_locked_or_safepoint(SystemDictionary_lock); + assert(!pool.is_null() && !error.is_null(), "adding NULL obj"); + + ResolutionErrorEntry* entry = new_entry(hash, pool(), cp_index, error()); + add_entry(index, entry); +} + +// find entry in the table +ResolutionErrorEntry* ResolutionErrorTable::find_entry(int index, unsigned int hash, + constantPoolHandle pool, int cp_index) +{ + assert_locked_or_safepoint(SystemDictionary_lock); + + for (ResolutionErrorEntry *error_probe = bucket(index); + error_probe != NULL; + error_probe = error_probe->next()) { + if (error_probe->hash() == hash && error_probe->pool() == pool()) { + return error_probe;; + } + } + return NULL; +} + +// create new error entry +ResolutionErrorEntry* ResolutionErrorTable::new_entry(int hash, constantPoolOop pool, + int cp_index, symbolOop error) +{ + ResolutionErrorEntry* entry = (ResolutionErrorEntry*)Hashtable::new_entry(hash, pool); + entry->set_cp_index(cp_index); + entry->set_error(error); + + return entry; +} + +// create resolution error table +ResolutionErrorTable::ResolutionErrorTable(int table_size) + : Hashtable(table_size, sizeof(ResolutionErrorEntry)) { +} + +// GC support +void ResolutionErrorTable::oops_do(OopClosure* f) { + for (int i = 0; i < table_size(); i++) { + for (ResolutionErrorEntry* probe = bucket(i); + probe != NULL; + probe = probe->next()) { + assert(probe->pool() != (constantPoolOop)NULL, "resolution error table is corrupt"); + assert(probe->error() != (symbolOop)NULL, "resolution error table is corrupt"); + probe->oops_do(f); + } + } +} + +// GC support +void ResolutionErrorEntry::oops_do(OopClosure* blk) { + blk->do_oop((oop*)pool_addr()); + blk->do_oop((oop*)error_addr()); +} + +// We must keep the symbolOop used in the error alive. The constantPoolOop will +// decide when the entry can be purged. +void ResolutionErrorTable::always_strong_classes_do(OopClosure* blk) { + for (int i = 0; i < table_size(); i++) { + for (ResolutionErrorEntry* probe = bucket(i); + probe != NULL; + probe = probe->next()) { + assert(probe->error() != (symbolOop)NULL, "resolution error table is corrupt"); + blk->do_oop((oop*)probe->error_addr()); + } + } +} + +// Remove unloaded entries from the table +void ResolutionErrorTable::purge_resolution_errors(BoolObjectClosure* is_alive) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint") + for (int i = 0; i < table_size(); i++) { + for (ResolutionErrorEntry** p = bucket_addr(i); *p != NULL; ) { + ResolutionErrorEntry* entry = *p; + assert(entry->pool() != (constantPoolOop)NULL, "resolution error table is corrupt"); + constantPoolOop pool = entry->pool(); + if (is_alive->do_object_b(pool)) { + p = entry->next_addr(); + } else { + *p = entry->next(); + free_entry(entry); + } + } + } +} diff --git a/hotspot/src/share/vm/classfile/resolutionErrors.hpp b/hotspot/src/share/vm/classfile/resolutionErrors.hpp new file mode 100644 index 00000000000..37edf7228ff --- /dev/null +++ b/hotspot/src/share/vm/classfile/resolutionErrors.hpp @@ -0,0 +1,99 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ResolutionErrorEntry; + +// ResolutionError objects are used to record errors encountered during +// constant pool resolution (JVMS 5.4.3). + +class ResolutionErrorTable : public Hashtable { + +public: + ResolutionErrorTable(int table_size); + + ResolutionErrorEntry* new_entry(int hash, constantPoolOop pool, int cp_index, symbolOop error); + + ResolutionErrorEntry* bucket(int i) { + return (ResolutionErrorEntry*)Hashtable::bucket(i); + } + + ResolutionErrorEntry** bucket_addr(int i) { + return (ResolutionErrorEntry**)Hashtable::bucket_addr(i); + } + + void add_entry(int index, ResolutionErrorEntry* new_entry) { + Hashtable::add_entry(index, (HashtableEntry*)new_entry); + } + + void add_entry(int index, unsigned int hash, + constantPoolHandle pool, int which, symbolHandle error); + + + // find error given the constant pool and constant pool index + ResolutionErrorEntry* find_entry(int index, unsigned int hash, + constantPoolHandle pool, int cp_index); + + + unsigned int compute_hash(constantPoolHandle pool, int cp_index) { + return (unsigned int) pool->identity_hash() + cp_index; + } + + // purges unloaded entries from the table + void purge_resolution_errors(BoolObjectClosure* is_alive); + + // this table keeps symbolOops alive + void always_strong_classes_do(OopClosure* blk); + + // GC support. + void oops_do(OopClosure* f); +}; + + +class ResolutionErrorEntry : public HashtableEntry { + private: + int _cp_index; + symbolOop _error; + + public: + constantPoolOop pool() const { return (constantPoolOop)literal(); } + constantPoolOop* pool_addr() { return (constantPoolOop*)literal_addr(); } + + int cp_index() const { return _cp_index; } + void set_cp_index(int cp_index) { _cp_index = cp_index; } + + symbolOop error() const { return _error; } + void set_error(symbolOop e) { _error = e; } + symbolOop* error_addr() { return &_error; } + + ResolutionErrorEntry* next() const { + return (ResolutionErrorEntry*)HashtableEntry::next(); + } + + ResolutionErrorEntry** next_addr() { + return (ResolutionErrorEntry**)HashtableEntry::next_addr(); + } + + // GC support + void oops_do(OopClosure* blk); +}; diff --git a/hotspot/src/share/vm/classfile/stackMapFrame.cpp b/hotspot/src/share/vm/classfile/stackMapFrame.cpp new file mode 100644 index 00000000000..b8f45d2bbc5 --- /dev/null +++ b/hotspot/src/share/vm/classfile/stackMapFrame.cpp @@ -0,0 +1,303 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_stackMapFrame.cpp.incl" + +StackMapFrame::StackMapFrame(u2 max_locals, u2 max_stack, ClassVerifier* v) : + _offset(0), _locals_size(0), _stack_size(0), _flags(0), + _max_locals(max_locals), _max_stack(max_stack), + _verifier(v) { + Thread* thr = v->thread(); + _locals = NEW_RESOURCE_ARRAY_IN_THREAD(thr, VerificationType, max_locals); + _stack = NEW_RESOURCE_ARRAY_IN_THREAD(thr, VerificationType, max_stack); + int32_t i; + for(i = 0; i < max_locals; i++) { + _locals[i] = VerificationType::bogus_type(); + } + for(i = 0; i < max_stack; i++) { + _stack[i] = VerificationType::bogus_type(); + } +} + +StackMapFrame* StackMapFrame::frame_in_exception_handler(u1 flags) { + Thread* thr = _verifier->thread(); + VerificationType* stack = NEW_RESOURCE_ARRAY_IN_THREAD(thr, VerificationType, 1); + StackMapFrame* frame = new StackMapFrame(_offset, flags, _locals_size, 0, _max_locals, _max_stack, _locals, stack, _verifier); + return frame; +} + +bool StackMapFrame::has_new_object() const { + int32_t i; + for (i = 0; i < _max_locals; i++) { + if (_locals[i].is_uninitialized()) { + return true; + } + } + for (i = 0; i < _stack_size; i++) { + if (_stack[i].is_uninitialized()) { + return true; + } + } + return false; +} + +void StackMapFrame::initialize_object( + VerificationType old_object, VerificationType new_object) { + int32_t i; + for (i = 0; i < _max_locals; i++) { + if (_locals[i].equals(old_object)) { + _locals[i] = new_object; + } + } + for (i = 0; i < _stack_size; i++) { + if (_stack[i].equals(old_object)) { + _stack[i] = new_object; + } + } + if (old_object == VerificationType::uninitialized_this_type()) { + // "this" has been initialized - reset flags + _flags = 0; + } +} + +VerificationType StackMapFrame::set_locals_from_arg( + const methodHandle m, VerificationType thisKlass, TRAPS) { + symbolHandle signature(THREAD, m->signature()); + SignatureStream ss(signature); + int init_local_num = 0; + if (!m->is_static()) { + init_local_num++; + // add one extra argument for instance method + if (m->name() == vmSymbols::object_initializer_name() && + thisKlass.name() != vmSymbols::java_lang_Object()) { + _locals[0] = VerificationType::uninitialized_this_type(); + _flags |= FLAG_THIS_UNINIT; + } else { + _locals[0] = thisKlass; + } + } + + // local num may be greater than size of parameters because long/double occupies two slots + while(!ss.at_return_type()) { + init_local_num += _verifier->change_sig_to_verificationType( + &ss, &_locals[init_local_num], + CHECK_VERIFY_(verifier(), VerificationType::bogus_type())); + ss.next(); + } + _locals_size = init_local_num; + + switch (ss.type()) { + case T_OBJECT: + case T_ARRAY: + { + symbolOop sig = ss.as_symbol(CHECK_(VerificationType::bogus_type())); + return VerificationType::reference_type(symbolHandle(THREAD, sig)); + } + case T_INT: return VerificationType::integer_type(); + case T_BYTE: return VerificationType::byte_type(); + case T_CHAR: return VerificationType::char_type(); + case T_SHORT: return VerificationType::short_type(); + case T_BOOLEAN: return VerificationType::boolean_type(); + case T_FLOAT: return VerificationType::float_type(); + case T_DOUBLE: return VerificationType::double_type(); + case T_LONG: return VerificationType::long_type(); + case T_VOID: return VerificationType::bogus_type(); + default: + ShouldNotReachHere(); + } + return VerificationType::bogus_type(); +} + +void StackMapFrame::copy_locals(const StackMapFrame* src) { + int32_t len = src->locals_size() < _locals_size ? + src->locals_size() : _locals_size; + for (int32_t i = 0; i < len; i++) { + _locals[i] = src->locals()[i]; + } +} + +void StackMapFrame::copy_stack(const StackMapFrame* src) { + int32_t len = src->stack_size() < _stack_size ? + src->stack_size() : _stack_size; + for (int32_t i = 0; i < len; i++) { + _stack[i] = src->stack()[i]; + } +} + + +bool StackMapFrame::is_assignable_to( + VerificationType* from, VerificationType* to, int32_t len, TRAPS) const { + for (int32_t i = 0; i < len; i++) { + bool subtype = to[i].is_assignable_from( + from[i], verifier()->current_class(), THREAD); + if (!subtype) { + return false; + } + } + return true; +} + +bool StackMapFrame::is_assignable_to(const StackMapFrame* target, TRAPS) const { + if (_max_locals != target->max_locals() || _stack_size != target->stack_size()) { + return false; + } + // Only need to compare type elements up to target->locals() or target->stack(). + // The remaining type elements in this state can be ignored because they are + // assignable to bogus type. + bool match_locals = is_assignable_to( + _locals, target->locals(), target->locals_size(), CHECK_false); + bool match_stack = is_assignable_to( + _stack, target->stack(), _stack_size, CHECK_false); + bool match_flags = (_flags | target->flags()) == target->flags(); + return (match_locals && match_stack && match_flags); +} + +VerificationType StackMapFrame::pop_stack_ex(VerificationType type, TRAPS) { + if (_stack_size <= 0) { + verifier()->verify_error(_offset, "Operand stack underflow"); + return VerificationType::bogus_type(); + } + VerificationType top = _stack[--_stack_size]; + bool subtype = type.is_assignable_from( + top, verifier()->current_class(), CHECK_(VerificationType::bogus_type())); + if (!subtype) { + verifier()->verify_error(_offset, "Bad type on operand stack"); + return VerificationType::bogus_type(); + } + NOT_PRODUCT( _stack[_stack_size] = VerificationType::bogus_type(); ) + return top; +} + +VerificationType StackMapFrame::get_local( + int32_t index, VerificationType type, TRAPS) { + if (index >= _max_locals) { + verifier()->verify_error(_offset, "Local variable table overflow"); + return VerificationType::bogus_type(); + } + bool subtype = type.is_assignable_from(_locals[index], + verifier()->current_class(), CHECK_(VerificationType::bogus_type())); + if (!subtype) { + verifier()->verify_error(_offset, "Bad local variable type"); + return VerificationType::bogus_type(); + } + if(index >= _locals_size) { _locals_size = index + 1; } + return _locals[index]; +} + +void StackMapFrame::get_local_2( + int32_t index, VerificationType type1, VerificationType type2, TRAPS) { + assert(type1.is_long() || type1.is_double(), "must be long/double"); + assert(type2.is_long2() || type2.is_double2(), "must be long/double_2"); + if (index >= _locals_size - 1) { + verifier()->verify_error(_offset, "get long/double overflows locals"); + return; + } + bool subtype1 = type1.is_assignable_from( + _locals[index], verifier()->current_class(), CHECK); + bool subtype2 = type2.is_assignable_from( + _locals[index+1], verifier()->current_class(), CHECK); + if (!subtype1 || !subtype2) { + verifier()->verify_error(_offset, "Bad local variable type"); + return; + } +} + +void StackMapFrame::set_local(int32_t index, VerificationType type, TRAPS) { + assert(!type.is_check(), "Must be a real type"); + if (index >= _max_locals) { + verifier()->verify_error("Local variable table overflow", _offset); + return; + } + // If type at index is double or long, set the next location to be unusable + if (_locals[index].is_double() || _locals[index].is_long()) { + assert((index + 1) < _locals_size, "Local variable table overflow"); + _locals[index + 1] = VerificationType::bogus_type(); + } + // If type at index is double_2 or long_2, set the previous location to be unusable + if (_locals[index].is_double2() || _locals[index].is_long2()) { + assert(index >= 1, "Local variable table underflow"); + _locals[index - 1] = VerificationType::bogus_type(); + } + _locals[index] = type; + if (index >= _locals_size) { +#ifdef ASSERT + for (int i=_locals_size; i= _max_locals - 1) { + verifier()->verify_error("Local variable table overflow", _offset); + return; + } + // If type at index+1 is double or long, set the next location to be unusable + if (_locals[index+1].is_double() || _locals[index+1].is_long()) { + assert((index + 2) < _locals_size, "Local variable table overflow"); + _locals[index + 2] = VerificationType::bogus_type(); + } + // If type at index is double_2 or long_2, set the previous location to be unusable + if (_locals[index].is_double2() || _locals[index].is_long2()) { + assert(index >= 1, "Local variable table underflow"); + _locals[index - 1] = VerificationType::bogus_type(); + } + _locals[index] = type1; + _locals[index+1] = type2; + if (index >= _locals_size - 1) { +#ifdef ASSERT + for (int i=_locals_size; iprint_cr("stackmap_frame[%d]:", _offset); + tty->print_cr("flags = 0x%x", _flags); + tty->print("locals[%d] = { ", _locals_size); + for (int32_t i = 0; i < _locals_size; i++) { + _locals[i].print_on(tty); + } + tty->print_cr(" }"); + tty->print("stack[%d] = { ", _stack_size); + for (int32_t j = 0; j < _stack_size; j++) { + _stack[j].print_on(tty); + } + tty->print_cr(" }"); +} + +#endif diff --git a/hotspot/src/share/vm/classfile/stackMapFrame.hpp b/hotspot/src/share/vm/classfile/stackMapFrame.hpp new file mode 100644 index 00000000000..60b61a77e0e --- /dev/null +++ b/hotspot/src/share/vm/classfile/stackMapFrame.hpp @@ -0,0 +1,226 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A StackMapFrame represents one frame in the stack map attribute. + +enum { + FLAG_THIS_UNINIT = 0x01 +}; + +class StackMapFrame : public ResourceObj { + private: + int32_t _offset; + + // See comment in StackMapTable about _frame_count about why these + // fields are int32_t instead of u2. + int32_t _locals_size; // number of valid type elements in _locals + int32_t _stack_size; // number of valid type elements in _stack + + int32_t _max_locals; + int32_t _max_stack; + + u1 _flags; + VerificationType* _locals; // local variable type array + VerificationType* _stack; // operand stack type array + + ClassVerifier* _verifier; // the verifier verifying this method + + public: + // constructors + + // This constructor is used by the type checker to allocate frames + // in type state, which have _max_locals and _max_stack array elements + // in _locals and _stack. + StackMapFrame(u2 max_locals, u2 max_stack, ClassVerifier* verifier); + + // This constructor is used to initialize stackmap frames in stackmap table, + // which have _locals_size and _stack_size array elements in _locals and _stack. + StackMapFrame(int32_t offset, + u1 flags, + u2 locals_size, + u2 stack_size, + u2 max_locals, + u2 max_stack, + VerificationType* locals, + VerificationType* stack, + ClassVerifier* v) : _offset(offset), _flags(flags), + _locals_size(locals_size), + _stack_size(stack_size), + _max_locals(max_locals), + _max_stack(max_stack), + _locals(locals), _stack(stack), + _verifier(v) { } + + inline void set_offset(int32_t offset) { _offset = offset; } + inline void set_verifier(ClassVerifier* v) { _verifier = v; } + inline void set_flags(u1 flags) { _flags = flags; } + inline void set_locals_size(u2 locals_size) { _locals_size = locals_size; } + inline void set_stack_size(u2 stack_size) { _stack_size = stack_size; } + inline void clear_stack() { _stack_size = 0; } + inline int32_t offset() const { return _offset; } + inline ClassVerifier* verifier() const { return _verifier; } + inline u1 flags() const { return _flags; } + inline int32_t locals_size() const { return _locals_size; } + inline VerificationType* locals() const { return _locals; } + inline int32_t stack_size() const { return _stack_size; } + inline VerificationType* stack() const { return _stack; } + inline int32_t max_locals() const { return _max_locals; } + inline int32_t max_stack() const { return _max_stack; } + inline bool flag_this_uninit() const { return _flags & FLAG_THIS_UNINIT; } + + // Set locals and stack types to bogus + inline void reset() { + int32_t i; + for (i = 0; i < _max_locals; i++) { + _locals[i] = VerificationType::bogus_type(); + } + for (i = 0; i < _max_stack; i++) { + _stack[i] = VerificationType::bogus_type(); + } + } + + // Return a StackMapFrame with the same local variable array and empty stack. + // Stack array is allocate with unused one element. + StackMapFrame* frame_in_exception_handler(u1 flags); + + // Set local variable type array based on m's signature. + VerificationType set_locals_from_arg( + const methodHandle m, VerificationType thisKlass, TRAPS); + + // Search local variable type array and stack type array. + // Return true if an uninitialized object is found. + bool has_new_object() const; + + // Search local variable type array and stack type array. + // Set every element with type of old_object to new_object. + void initialize_object( + VerificationType old_object, VerificationType new_object); + + // Copy local variable type array in src into this local variable type array. + void copy_locals(const StackMapFrame* src); + + // Copy stack type array in src into this stack type array. + void copy_stack(const StackMapFrame* src); + + // Return true if this stack map frame is assignable to target. + bool is_assignable_to(const StackMapFrame* target, TRAPS) const; + + // Push type into stack type array. + inline void push_stack(VerificationType type, TRAPS) { + assert(!type.is_check(), "Must be a real type"); + if (_stack_size >= _max_stack) { + verifier()->verify_error(_offset, "Operand stack overflow"); + return; + } + _stack[_stack_size++] = type; + } + + inline void push_stack_2( + VerificationType type1, VerificationType type2, TRAPS) { + assert(type1.is_long() || type1.is_double(), "must be long/double"); + assert(type2.is_long2() || type2.is_double2(), "must be long/double_2"); + if (_stack_size >= _max_stack - 1) { + verifier()->verify_error(_offset, "Operand stack overflow"); + return; + } + _stack[_stack_size++] = type1; + _stack[_stack_size++] = type2; + } + + // Pop and return the top type on stack without verifying. + inline VerificationType pop_stack(TRAPS) { + if (_stack_size <= 0) { + verifier()->verify_error(_offset, "Operand stack underflow"); + return VerificationType::bogus_type(); + } + // Put bogus type to indicate it's no longer valid. + // Added to make it consistent with the other pop_stack method. + VerificationType top = _stack[--_stack_size]; + NOT_PRODUCT( _stack[_stack_size] = VerificationType::bogus_type(); ) + return top; + } + + // Pop and return the top type on stack type array after verifying it + // is assignable to type. + inline VerificationType pop_stack(VerificationType type, TRAPS) { + if (_stack_size != 0) { + VerificationType top = _stack[_stack_size - 1]; + bool subtype = type.is_assignable_from( + top, verifier()->current_class(), + CHECK_(VerificationType::bogus_type())); + if (subtype) { + _stack_size --; + NOT_PRODUCT( _stack[_stack_size] = VerificationType::bogus_type(); ) + return top; + } + } + return pop_stack_ex(type, THREAD); + } + + inline void pop_stack_2( + VerificationType type1, VerificationType type2, TRAPS) { + assert(type1.is_long2() || type1.is_double2(), "must be long/double"); + assert(type2.is_long() || type2.is_double(), "must be long/double_2"); + if (_stack_size >= 2) { + VerificationType top1 = _stack[_stack_size - 1]; + bool subtype1 = type1.is_assignable_from( + top1, verifier()->current_class(), CHECK); + VerificationType top2 = _stack[_stack_size - 2]; + bool subtype2 = type2.is_assignable_from( + top2, verifier()->current_class(), CHECK); + if (subtype1 && subtype2) { + _stack_size -= 2; + NOT_PRODUCT( _stack[_stack_size] = VerificationType::bogus_type(); ) + NOT_PRODUCT( _stack[_stack_size+1] = VerificationType::bogus_type(); ) + return; + } + } + pop_stack_ex(type1, THREAD); + pop_stack_ex(type2, THREAD); + } + + // Uncommon case that throws exceptions. + VerificationType pop_stack_ex(VerificationType type, TRAPS); + + // Return the type at index in local variable array after verifying + // it is assignable to type. + VerificationType get_local(int32_t index, VerificationType type, TRAPS); + // For long/double. + void get_local_2( + int32_t index, VerificationType type1, VerificationType type2, TRAPS); + + // Set element at index in local variable array to type. + void set_local(int32_t index, VerificationType type, TRAPS); + // For long/double. + void set_local_2( + int32_t index, VerificationType type1, VerificationType type2, TRAPS); + + // Private auxiliary method used only in is_assignable_to(StackMapFrame). + // Returns true if src is assignable to target. + bool is_assignable_to( + VerificationType* src, VerificationType* target, int32_t len, TRAPS) const; + + // Debugging + void print() const PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/classfile/stackMapTable.cpp b/hotspot/src/share/vm/classfile/stackMapTable.cpp new file mode 100644 index 00000000000..cb00852018a --- /dev/null +++ b/hotspot/src/share/vm/classfile/stackMapTable.cpp @@ -0,0 +1,427 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_stackMapTable.cpp.incl" + +StackMapTable::StackMapTable(StackMapReader* reader, StackMapFrame* init_frame, + u2 max_locals, u2 max_stack, + char* code_data, int code_len, TRAPS) { + _code_length = code_len; + _frame_count = reader->get_frame_count(); + if (_frame_count > 0) { + _frame_array = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, + StackMapFrame*, _frame_count); + StackMapFrame* pre_frame = init_frame; + for (int32_t i = 0; i < _frame_count; i++) { + StackMapFrame* frame = reader->next( + pre_frame, i == 0, max_locals, max_stack, + CHECK_VERIFY(pre_frame->verifier())); + _frame_array[i] = frame; + int offset = frame->offset(); + if (offset >= code_len || code_data[offset] == 0) { + frame->verifier()->verify_error("StackMapTable error: bad offset"); + return; + } + pre_frame = frame; + } + } + reader->check_end(CHECK); +} + +// This method is only called by method in StackMapTable. +int StackMapTable::get_index_from_offset(int32_t offset) const { + int i = 0; + for (; i < _frame_count; i++) { + if (_frame_array[i]->offset() == offset) { + return i; + } + } + return i; // frame with offset doesn't exist in the array +} + +bool StackMapTable::match_stackmap( + StackMapFrame* frame, int32_t target, + bool match, bool update, TRAPS) const { + int index = get_index_from_offset(target); + + return match_stackmap( + frame, target, index, match, + update, CHECK_VERIFY_(frame->verifier(), false)); +} + +// Match and/or update current_frame to the frame in stackmap table with +// specified offset and frame index. Return true if the two frames match. +// +// The values of match and update are: _match__update_ +// +// checking a branch target/exception handler: true false +// linear bytecode verification following an +// unconditional branch: false true +// linear bytecode verification not following an +// unconditional branch: true true +bool StackMapTable::match_stackmap( + StackMapFrame* frame, int32_t target, int32_t frame_index, + bool match, bool update, TRAPS) const { + if (frame_index < 0 || frame_index >= _frame_count) { + frame->verifier()->verify_error(frame->offset(), + "Expecting a stackmap frame at branch target %d", target); + return false; + } + + bool result = true; + StackMapFrame *stackmap_frame = _frame_array[frame_index]; + if (match) { + // Has direct control flow from last instruction, need to match the two + // frames. + result = frame->is_assignable_to( + stackmap_frame, CHECK_VERIFY_(frame->verifier(), false)); + } + if (update) { + // Use the frame in stackmap table as current frame + int lsize = stackmap_frame->locals_size(); + int ssize = stackmap_frame->stack_size(); + if (frame->locals_size() > lsize || frame->stack_size() > ssize) { + // Make sure unused type array items are all _bogus_type. + frame->reset(); + } + frame->set_locals_size(lsize); + frame->copy_locals(stackmap_frame); + frame->set_stack_size(ssize); + frame->copy_stack(stackmap_frame); + frame->set_flags(stackmap_frame->flags()); + } + return result; +} + +void StackMapTable::check_jump_target( + StackMapFrame* frame, int32_t target, TRAPS) const { + bool match = match_stackmap( + frame, target, true, false, CHECK_VERIFY(frame->verifier())); + if (!match || (target < 0 || target >= _code_length)) { + frame->verifier()->verify_error(frame->offset(), + "Inconsistent stackmap frames at branch target %d", target); + return; + } + // check if uninitialized objects exist on backward branches + check_new_object(frame, target, CHECK_VERIFY(frame->verifier())); +} + +void StackMapTable::check_new_object( + const StackMapFrame* frame, int32_t target, TRAPS) const { + if (frame->offset() > target && frame->has_new_object()) { + frame->verifier()->verify_error(frame->offset(), + "Uninitialized object exists on backward branch %d", target); + return; + } +} + +#ifndef PRODUCT + +void StackMapTable::print() const { + tty->print_cr("StackMapTable: frame_count = %d", _frame_count); + tty->print_cr("table = { "); + for (int32_t i = 0; i < _frame_count; i++) { + _frame_array[i]->print(); + } + tty->print_cr(" }"); +} + +#endif + +int32_t StackMapReader::chop( + VerificationType* locals, int32_t length, int32_t chops) { + int32_t pos = length - 1; + for (int32_t i=0; iget_u1(THREAD); + if (tag < (u1)ITEM_UninitializedThis) { + return VerificationType::from_tag(tag); + } + if (tag == ITEM_Object) { + u2 class_index = _stream->get_u2(THREAD); + int nconstants = _cp->length(); + if ((class_index <= 0 || class_index >= nconstants) || + (!_cp->tag_at(class_index).is_klass() && + !_cp->tag_at(class_index).is_unresolved_klass())) { + _stream->stackmap_format_error("bad class index", THREAD); + return VerificationType::bogus_type(); + } + return VerificationType::reference_type( + symbolHandle(THREAD, _cp->klass_name_at(class_index))); + } + if (tag == ITEM_UninitializedThis) { + if (flags != NULL) { + *flags |= FLAG_THIS_UNINIT; + } + return VerificationType::uninitialized_this_type(); + } + if (tag == ITEM_Uninitialized) { + u2 offset = _stream->get_u2(THREAD); + if (offset >= _code_length || + _code_data[offset] != ClassVerifier::NEW_OFFSET) { + ResourceMark rm(THREAD); + _verifier->class_format_error( + "StackMapTable format error: bad offset for Uninitialized"); + return VerificationType::bogus_type(); + } + return VerificationType::uninitialized_type(offset); + } + _stream->stackmap_format_error("bad verification type", THREAD); + return VerificationType::bogus_type(); +} + +StackMapFrame* StackMapReader::next( + StackMapFrame* pre_frame, bool first, u2 max_locals, u2 max_stack, TRAPS) { + StackMapFrame* frame; + int offset; + VerificationType* locals = NULL; + u1 frame_type = _stream->get_u1(THREAD); + if (frame_type < 64) { + // same_frame + if (first) { + offset = frame_type; + // Can't share the locals array since that is updated by the verifier. + if (pre_frame->locals_size() > 0) { + locals = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, VerificationType, pre_frame->locals_size()); + } + } else { + offset = pre_frame->offset() + frame_type + 1; + locals = pre_frame->locals(); + } + frame = new StackMapFrame( + offset, pre_frame->flags(), pre_frame->locals_size(), 0, + max_locals, max_stack, locals, NULL, _verifier); + if (first && locals != NULL) { + frame->copy_locals(pre_frame); + } + return frame; + } + if (frame_type < 128) { + // same_locals_1_stack_item_frame + if (first) { + offset = frame_type - 64; + // Can't share the locals array since that is updated by the verifier. + if (pre_frame->locals_size() > 0) { + locals = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, VerificationType, pre_frame->locals_size()); + } + } else { + offset = pre_frame->offset() + frame_type - 63; + locals = pre_frame->locals(); + } + VerificationType* stack = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, VerificationType, 2); + u2 stack_size = 1; + stack[0] = parse_verification_type(NULL, CHECK_VERIFY_(_verifier, NULL)); + if (stack[0].is_category2()) { + stack[1] = stack[0].to_category2_2nd(); + stack_size = 2; + } + check_verification_type_array_size( + stack_size, max_stack, CHECK_VERIFY_(_verifier, NULL)); + frame = new StackMapFrame( + offset, pre_frame->flags(), pre_frame->locals_size(), stack_size, + max_locals, max_stack, locals, stack, _verifier); + if (first && locals != NULL) { + frame->copy_locals(pre_frame); + } + return frame; + } + + u2 offset_delta = _stream->get_u2(THREAD); + + if (frame_type < SAME_LOCALS_1_STACK_ITEM_EXTENDED) { + // reserved frame types + _stream->stackmap_format_error( + "reserved frame type", CHECK_VERIFY_(_verifier, NULL)); + } + + if (frame_type == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { + // same_locals_1_stack_item_frame_extended + if (first) { + offset = offset_delta; + // Can't share the locals array since that is updated by the verifier. + if (pre_frame->locals_size() > 0) { + locals = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, VerificationType, pre_frame->locals_size()); + } + } else { + offset = pre_frame->offset() + offset_delta + 1; + locals = pre_frame->locals(); + } + VerificationType* stack = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, VerificationType, 2); + u2 stack_size = 1; + stack[0] = parse_verification_type(NULL, CHECK_VERIFY_(_verifier, NULL)); + if (stack[0].is_category2()) { + stack[1] = stack[0].to_category2_2nd(); + stack_size = 2; + } + check_verification_type_array_size( + stack_size, max_stack, CHECK_VERIFY_(_verifier, NULL)); + frame = new StackMapFrame( + offset, pre_frame->flags(), pre_frame->locals_size(), stack_size, + max_locals, max_stack, locals, stack, _verifier); + if (first && locals != NULL) { + frame->copy_locals(pre_frame); + } + return frame; + } + + if (frame_type <= SAME_EXTENDED) { + // chop_frame or same_frame_extended + locals = pre_frame->locals(); + int length = pre_frame->locals_size(); + int chops = SAME_EXTENDED - frame_type; + int new_length = length; + u1 flags = pre_frame->flags(); + if (chops != 0) { + new_length = chop(locals, length, chops); + check_verification_type_array_size( + new_length, max_locals, CHECK_VERIFY_(_verifier, NULL)); + // Recompute flags since uninitializedThis could have been chopped. + flags = 0; + for (int i=0; i 0) { + locals = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, VerificationType, new_length); + } else { + locals = NULL; + } + } else { + offset = pre_frame->offset() + offset_delta + 1; + } + frame = new StackMapFrame( + offset, flags, new_length, 0, max_locals, max_stack, + locals, NULL, _verifier); + if (first && locals != NULL) { + frame->copy_locals(pre_frame); + } + return frame; + } else if (frame_type < SAME_EXTENDED + 4) { + // append_frame + int appends = frame_type - SAME_EXTENDED; + int real_length = pre_frame->locals_size(); + int new_length = real_length + appends*2; + locals = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, VerificationType, new_length); + VerificationType* pre_locals = pre_frame->locals(); + int i; + for (i=0; ilocals_size(); i++) { + locals[i] = pre_locals[i]; + } + u1 flags = pre_frame->flags(); + for (i=0; ioffset() + offset_delta + 1; + } + frame = new StackMapFrame( + offset, flags, real_length, 0, max_locals, + max_stack, locals, NULL, _verifier); + return frame; + } + if (frame_type == FULL) { + // full_frame + u1 flags = 0; + u2 locals_size = _stream->get_u2(THREAD); + int real_locals_size = 0; + if (locals_size > 0) { + locals = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, VerificationType, locals_size*2); + } + int i; + for (i=0; iget_u2(THREAD); + int real_stack_size = 0; + VerificationType* stack = NULL; + if (stack_size > 0) { + stack = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, VerificationType, stack_size*2); + } + for (i=0; ioffset() + offset_delta + 1; + } + frame = new StackMapFrame( + offset, flags, real_locals_size, real_stack_size, + max_locals, max_stack, locals, stack, _verifier); + return frame; + } + + _stream->stackmap_format_error( + "reserved frame type", CHECK_VERIFY_(pre_frame->verifier(), NULL)); + return NULL; +} diff --git a/hotspot/src/share/vm/classfile/stackMapTable.hpp b/hotspot/src/share/vm/classfile/stackMapTable.hpp new file mode 100644 index 00000000000..f1d393ac101 --- /dev/null +++ b/hotspot/src/share/vm/classfile/stackMapTable.hpp @@ -0,0 +1,161 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class StackMapReader; + +// StackMapTable class is the StackMap table used by type checker +class StackMapTable : public StackObj { + private: + // Logically, the _frame_count (as well as many fields in the StackFrame) + // should be a u2, but if we defined the variable as that type it will + // be difficult to detect/recover from overflow or underflow conditions. + // Widening the type and making it signed will help detect these. + int32_t _code_length; + int32_t _frame_count; // Stackmap frame count + StackMapFrame** _frame_array; + + public: + StackMapTable(StackMapReader* reader, StackMapFrame* init_frame, + u2 max_locals, u2 max_stack, + char* code_data, int code_len, TRAPS); + + inline int32_t get_frame_count() const { return _frame_count; } + inline int get_offset(int index) const { + return _frame_array[index]->offset(); + } + + // Match and/or update current_frame to the frame in stackmap table with + // specified offset. Return true if the two frames match. + bool match_stackmap( + StackMapFrame* current_frame, int32_t offset, + bool match, bool update, TRAPS) const; + // Match and/or update current_frame to the frame in stackmap table with + // specified offset and frame index. Return true if the two frames match. + bool match_stackmap( + StackMapFrame* current_frame, int32_t offset, int32_t frame_index, + bool match, bool update, TRAPS) const; + + // Check jump instructions. Make sure there are no uninitialized + // instances on backward branch. + void check_jump_target(StackMapFrame* frame, int32_t target, TRAPS) const; + + // The following methods are only used inside this class. + + // Returns the frame array index where the frame with offset is stored. + int get_index_from_offset(int32_t offset) const; + + // Make sure that there's no uninitialized object exist on backward branch. + void check_new_object( + const StackMapFrame* frame, int32_t target, TRAPS) const; + + // Debugging + void print() const PRODUCT_RETURN; +}; + +class StackMapStream : StackObj { + private: + typeArrayHandle _data; + int _index; + public: + StackMapStream(typeArrayHandle ah) + : _data(ah), _index(0) { + } + u1 get_u1(TRAPS) { + if (_data == NULL || _index >= _data->length()) { + stackmap_format_error("access beyond the end of attribute", CHECK_0); + } + return _data->byte_at(_index++); + } + u2 get_u2(TRAPS) { + if (_data == NULL || _index >= _data->length() - 1) { + stackmap_format_error("access beyond the end of attribute", CHECK_0); + } + u2 res = Bytes::get_Java_u2((u1*)_data->byte_at_addr(_index)); + _index += 2; + return res; + } + bool at_end() { + return (_data == NULL) || (_index == _data->length()); + } + static void stackmap_format_error(const char* msg, TRAPS); +}; + +class StackMapReader : StackObj { + private: + // information about the class and method + constantPoolHandle _cp; + ClassVerifier* _verifier; + StackMapStream* _stream; + char* _code_data; + int32_t _code_length; + + // information get from the attribute + int32_t _frame_count; // frame count + + int32_t chop(VerificationType* locals, int32_t length, int32_t chops); + VerificationType parse_verification_type(u1* flags, TRAPS); + void check_verification_type_array_size( + int32_t size, int32_t max_size, TRAPS) { + if (size < 0 || size > max_size) { + // Since this error could be caused someone rewriting the method + // but not knowing to update the stackmap data, we call the the + // verifier's error method, which may not throw an exception and + // failover to the old verifier instead. + _verifier->class_format_error( + "StackMapTable format error: bad type array size"); + } + } + + enum { + SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247, + SAME_EXTENDED = 251, + FULL = 255 + }; + + public: + // Constructor + StackMapReader(ClassVerifier* v, StackMapStream* stream, char* code_data, + int32_t code_len, TRAPS) : + _verifier(v), _stream(stream), + _code_data(code_data), _code_length(code_len) { + methodHandle m = v->method(); + if (m->has_stackmap_table()) { + _cp = constantPoolHandle(THREAD, m->constants()); + _frame_count = _stream->get_u2(CHECK); + } else { + // There's no stackmap table present. Frame count and size are 0. + _frame_count = 0; + } + } + + inline int32_t get_frame_count() const { return _frame_count; } + StackMapFrame* next(StackMapFrame* pre_frame, bool first, + u2 max_locals, u2 max_stack, TRAPS); + + void check_end(TRAPS) { + if (!_stream->at_end()) { + StackMapStream::stackmap_format_error("wrong attribute size", CHECK); + } + } +}; diff --git a/hotspot/src/share/vm/classfile/symbolTable.cpp b/hotspot/src/share/vm/classfile/symbolTable.cpp new file mode 100644 index 00000000000..0c2d6bbb3e9 --- /dev/null +++ b/hotspot/src/share/vm/classfile/symbolTable.cpp @@ -0,0 +1,484 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_symbolTable.cpp.incl" + +// -------------------------------------------------------------------------- + +SymbolTable* SymbolTable::_the_table = NULL; + +// Lookup a symbol in a bucket. + +symbolOop SymbolTable::lookup(int index, const char* name, + int len, unsigned int hash) { + for (HashtableEntry* e = bucket(index); e != NULL; e = e->next()) { + if (e->hash() == hash) { + symbolOop sym = symbolOop(e->literal()); + if (sym->equals(name, len)) { + return sym; + } + } + } + return NULL; +} + + +// We take care not to be blocking while holding the +// SymbolTable_lock. Otherwise, the system might deadlock, since the +// symboltable is used during compilation (VM_thread) The lock free +// synchronization is simplified by the fact that we do not delete +// entries in the symbol table during normal execution (only during +// safepoints). + +symbolOop SymbolTable::lookup(const char* name, int len, TRAPS) { + unsigned int hashValue = hash_symbol(name, len); + int index = the_table()->hash_to_index(hashValue); + + symbolOop s = the_table()->lookup(index, name, len, hashValue); + + // Found + if (s != NULL) return s; + + // Otherwise, add to symbol to table + return the_table()->basic_add(index, (u1*)name, len, hashValue, CHECK_NULL); +} + +symbolOop SymbolTable::lookup(symbolHandle sym, int begin, int end, TRAPS) { + char* buffer; + int index, len; + unsigned int hashValue; + char* name; + { + debug_only(No_Safepoint_Verifier nsv;) + + name = (char*)sym->base() + begin; + len = end - begin; + hashValue = hash_symbol(name, len); + index = the_table()->hash_to_index(hashValue); + symbolOop s = the_table()->lookup(index, name, len, hashValue); + + // Found + if (s != NULL) return s; + } + + // Otherwise, add to symbol to table. Copy to a C string first. + char stack_buf[128]; + ResourceMark rm(THREAD); + if (len <= 128) { + buffer = stack_buf; + } else { + buffer = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, len); + } + for (int i=0; ibasic_add(index, (u1*)buffer, len, hashValue, CHECK_NULL); +} + +symbolOop SymbolTable::lookup_only(const char* name, int len, + unsigned int& hash) { + hash = hash_symbol(name, len); + int index = the_table()->hash_to_index(hash); + + return the_table()->lookup(index, name, len, hash); +} + +void SymbolTable::add(constantPoolHandle cp, int names_count, + const char** names, int* lengths, int* cp_indices, + unsigned int* hashValues, TRAPS) { + SymbolTable* table = the_table(); + bool added = table->basic_add(cp, names_count, names, lengths, + cp_indices, hashValues, CHECK); + if (!added) { + // do it the hard way + for (int i=0; ihash_to_index(hashValues[i]); + symbolOop sym = table->basic_add(index, (u1*)names[i], lengths[i], + hashValues[i], CHECK); + cp->symbol_at_put(cp_indices[i], sym); + } + } +} + +// Needed for preloading classes in signatures when compiling. + +symbolOop SymbolTable::probe(const char* name, int len) { + unsigned int hashValue = hash_symbol(name, len); + int index = the_table()->hash_to_index(hashValue); + return the_table()->lookup(index, name, len, hashValue); +} + + +symbolOop SymbolTable::basic_add(int index, u1 *name, int len, + unsigned int hashValue, TRAPS) { + assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(), + "proposed name of symbol must be stable"); + + // We assume that lookup() has been called already, that it failed, + // and symbol was not found. We create the symbol here. + symbolKlass* sk = (symbolKlass*) Universe::symbolKlassObj()->klass_part(); + symbolOop s_oop = sk->allocate_symbol(name, len, CHECK_NULL); + symbolHandle sym (THREAD, s_oop); + + // Allocation must be done before grapping the SymbolTable_lock lock + MutexLocker ml(SymbolTable_lock, THREAD); + + assert(sym->equals((char*)name, len), "symbol must be properly initialized"); + + // Since look-up was done lock-free, we need to check if another + // thread beat us in the race to insert the symbol. + + symbolOop test = lookup(index, (char*)name, len, hashValue); + if (test != NULL) { + // A race occured and another thread introduced the symbol, this one + // will be dropped and collected. + return test; + } + + HashtableEntry* entry = new_entry(hashValue, sym()); + add_entry(index, entry); + return sym(); +} + +bool SymbolTable::basic_add(constantPoolHandle cp, int names_count, + const char** names, int* lengths, + int* cp_indices, unsigned int* hashValues, + TRAPS) { + symbolKlass* sk = (symbolKlass*) Universe::symbolKlassObj()->klass_part(); + symbolOop sym_oops[symbol_alloc_batch_size]; + bool allocated = sk->allocate_symbols(names_count, names, lengths, + sym_oops, CHECK_false); + if (!allocated) { + return false; + } + symbolHandle syms[symbol_alloc_batch_size]; + int i; + for (i=0; iequals(names[i], lengths[i]), "symbol must be properly initialized"); + // Since look-up was done lock-free, we need to check if another + // thread beat us in the race to insert the symbol. + int index = hash_to_index(hashValues[i]); + symbolOop test = lookup(index, names[i], lengths[i], hashValues[i]); + if (test != NULL) { + // A race occured and another thread introduced the symbol, this one + // will be dropped and collected. Use test instead. + cp->symbol_at_put(cp_indices[i], test); + } else { + symbolOop sym = syms[i](); + HashtableEntry* entry = new_entry(hashValues[i], sym); + add_entry(index, entry); + cp->symbol_at_put(cp_indices[i], sym); + } + } + + return true; +} + + +void SymbolTable::verify() { + for (int i = 0; i < the_table()->table_size(); ++i) { + HashtableEntry* p = the_table()->bucket(i); + for ( ; p != NULL; p = p->next()) { + symbolOop s = symbolOop(p->literal()); + guarantee(s != NULL, "symbol is NULL"); + s->verify(); + guarantee(s->is_perm(), "symbol not in permspace"); + unsigned int h = hash_symbol((char*)s->bytes(), s->utf8_length()); + guarantee(p->hash() == h, "broken hash in symbol table entry"); + guarantee(the_table()->hash_to_index(h) == i, + "wrong index in symbol table"); + } + } +} + + +//--------------------------------------------------------------------------- +// Non-product code + +#ifndef PRODUCT + +void SymbolTable::print_histogram() { + MutexLocker ml(SymbolTable_lock); + const int results_length = 100; + int results[results_length]; + int i,j; + + // initialize results to zero + for (j = 0; j < results_length; j++) { + results[j] = 0; + } + + int total = 0; + int max_symbols = 0; + int out_of_range = 0; + for (i = 0; i < the_table()->table_size(); i++) { + HashtableEntry* p = the_table()->bucket(i); + for ( ; p != NULL; p = p->next()) { + int counter = symbolOop(p->literal())->utf8_length(); + total += counter; + if (counter < results_length) { + results[counter]++; + } else { + out_of_range++; + } + max_symbols = MAX2(max_symbols, counter); + } + } + tty->print_cr("Symbol Table:"); + tty->print_cr("%8s %5d", "Total ", total); + tty->print_cr("%8s %5d", "Maximum", max_symbols); + tty->print_cr("%8s %3.2f", "Average", + ((float) total / (float) the_table()->table_size())); + tty->print_cr("%s", "Histogram:"); + tty->print_cr(" %s %29s", "Length", "Number chains that length"); + for (i = 0; i < results_length; i++) { + if (results[i] > 0) { + tty->print_cr("%6d %10d", i, results[i]); + } + } + int line_length = 70; + tty->print_cr("%s %30s", " Length", "Number chains that length"); + for (i = 0; i < results_length; i++) { + if (results[i] > 0) { + tty->print("%4d", i); + for (j = 0; (j < results[i]) && (j < line_length); j++) { + tty->print("%1s", "*"); + } + if (j == line_length) { + tty->print("%1s", "+"); + } + tty->cr(); + } + } + tty->print_cr(" %s %d: %d\n", "Number chains longer than", + results_length, out_of_range); +} + +#endif // PRODUCT + +// -------------------------------------------------------------------------- + +#ifdef ASSERT +class StableMemoryChecker : public StackObj { + enum { _bufsize = wordSize*4 }; + + address _region; + jint _size; + u1 _save_buf[_bufsize]; + + int sample(u1* save_buf) { + if (_size <= _bufsize) { + memcpy(save_buf, _region, _size); + return _size; + } else { + // copy head and tail + memcpy(&save_buf[0], _region, _bufsize/2); + memcpy(&save_buf[_bufsize/2], _region + _size - _bufsize/2, _bufsize/2); + return (_bufsize/2)*2; + } + } + + public: + StableMemoryChecker(const void* region, jint size) { + _region = (address) region; + _size = size; + sample(_save_buf); + } + + bool verify() { + u1 check_buf[sizeof(_save_buf)]; + int check_size = sample(check_buf); + return (0 == memcmp(_save_buf, check_buf, check_size)); + } + + void set_region(const void* region) { _region = (address) region; } +}; +#endif + + +// -------------------------------------------------------------------------- + + +// Compute the hash value for a java.lang.String object which would +// contain the characters passed in. This hash value is used for at +// least two purposes. +// +// (a) As the hash value used by the StringTable for bucket selection +// and comparison (stored in the HashtableEntry structures). This +// is used in the String.intern() method. +// +// (b) As the hash value used by the String object itself, in +// String.hashCode(). This value is normally calculate in Java code +// in the String.hashCode method(), but is precomputed for String +// objects in the shared archive file. +// +// For this reason, THIS ALGORITHM MUST MATCH String.hashCode(). + +int StringTable::hash_string(jchar* s, int len) { + unsigned h = 0; + while (len-- > 0) { + h = 31*h + (unsigned) *s; + s++; + } + return h; +} + + +StringTable* StringTable::_the_table = NULL; + +oop StringTable::lookup(int index, jchar* name, + int len, unsigned int hash) { + for (HashtableEntry* l = bucket(index); l != NULL; l = l->next()) { + if (l->hash() == hash) { + if (java_lang_String::equals(l->literal(), name, len)) { + return l->literal(); + } + } + } + return NULL; +} + + +oop StringTable::basic_add(int index, Handle string_or_null, jchar* name, + int len, unsigned int hashValue, TRAPS) { + debug_only(StableMemoryChecker smc(name, len * sizeof(name[0]))); + assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(), + "proposed name of symbol must be stable"); + + Handle string; + // try to reuse the string if possible + if (!string_or_null.is_null() && string_or_null()->is_perm()) { + string = string_or_null; + } else { + string = java_lang_String::create_tenured_from_unicode(name, len, CHECK_NULL); + } + + // Allocation must be done before grapping the SymbolTable_lock lock + MutexLocker ml(StringTable_lock, THREAD); + + assert(java_lang_String::equals(string(), name, len), + "string must be properly initialized"); + + // Since look-up was done lock-free, we need to check if another + // thread beat us in the race to insert the symbol. + + oop test = lookup(index, name, len, hashValue); // calls lookup(u1*, int) + if (test != NULL) { + // Entry already added + return test; + } + + HashtableEntry* entry = new_entry(hashValue, string()); + add_entry(index, entry); + return string(); +} + + +oop StringTable::lookup(symbolOop symbol) { + ResourceMark rm; + int length; + jchar* chars = symbol->as_unicode(length); + unsigned int hashValue = hash_string(chars, length); + int index = the_table()->hash_to_index(hashValue); + return the_table()->lookup(index, chars, length, hashValue); +} + + +oop StringTable::intern(Handle string_or_null, jchar* name, + int len, TRAPS) { + unsigned int hashValue = hash_string(name, len); + int index = the_table()->hash_to_index(hashValue); + oop string = the_table()->lookup(index, name, len, hashValue); + + // Found + if (string != NULL) return string; + + // Otherwise, add to symbol to table + return the_table()->basic_add(index, string_or_null, name, len, + hashValue, CHECK_NULL); +} + +oop StringTable::intern(symbolOop symbol, TRAPS) { + if (symbol == NULL) return NULL; + ResourceMark rm(THREAD); + int length; + jchar* chars = symbol->as_unicode(length); + Handle string; + oop result = intern(string, chars, length, CHECK_NULL); + return result; +} + + +oop StringTable::intern(oop string, TRAPS) +{ + if (string == NULL) return NULL; + ResourceMark rm(THREAD); + int length; + Handle h_string (THREAD, string); + jchar* chars = java_lang_String::as_unicode_string(string, length); + oop result = intern(h_string, chars, length, CHECK_NULL); + return result; +} + + +oop StringTable::intern(const char* utf8_string, TRAPS) { + if (utf8_string == NULL) return NULL; + ResourceMark rm(THREAD); + int length = UTF8::unicode_length(utf8_string); + jchar* chars = NEW_RESOURCE_ARRAY(jchar, length); + UTF8::convert_to_unicode(utf8_string, chars, length); + Handle string; + oop result = intern(string, chars, length, CHECK_NULL); + return result; +} + +void StringTable::verify() { + for (int i = 0; i < the_table()->table_size(); ++i) { + HashtableEntry* p = the_table()->bucket(i); + for ( ; p != NULL; p = p->next()) { + oop s = p->literal(); + guarantee(s != NULL, "interned string is NULL"); + guarantee(s->is_perm(), "interned string not in permspace"); + + int length; + jchar* chars = java_lang_String::as_unicode_string(s, length); + unsigned int h = hash_string(chars, length); + guarantee(p->hash() == h, "broken hash in string table entry"); + guarantee(the_table()->hash_to_index(h) == i, + "wrong index in string table"); + } + } +} diff --git a/hotspot/src/share/vm/classfile/symbolTable.hpp b/hotspot/src/share/vm/classfile/symbolTable.hpp new file mode 100644 index 00000000000..828512780bf --- /dev/null +++ b/hotspot/src/share/vm/classfile/symbolTable.hpp @@ -0,0 +1,213 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The symbol table holds all symbolOops and corresponding interned strings. +// symbolOops and literal strings should be canonicalized. +// +// The interned strings are created lazily. +// +// It is implemented as an open hash table with a fixed number of buckets. +// +// %note: +// - symbolTableEntrys are allocated in blocks to reduce the space overhead. + +class BoolObjectClosure; + + +class SymbolTable : public Hashtable { + friend class VMStructs; + +private: + // The symbol table + static SymbolTable* _the_table; + + // Adding elements + symbolOop basic_add(int index, u1* name, int len, + unsigned int hashValue, TRAPS); + bool basic_add(constantPoolHandle cp, int names_count, + const char** names, int* lengths, int* cp_indices, + unsigned int* hashValues, TRAPS); + + // Table size + enum { + symbol_table_size = 20011 + }; + + symbolOop lookup(int index, const char* name, int len, unsigned int hash); + + SymbolTable() + : Hashtable(symbol_table_size, sizeof (HashtableEntry)) {} + + SymbolTable(HashtableBucket* t, int number_of_entries) + : Hashtable(symbol_table_size, sizeof (HashtableEntry), t, + number_of_entries) {} + + +public: + enum { + symbol_alloc_batch_size = 8 + }; + + // The symbol table + static SymbolTable* the_table() { return _the_table; } + + static void create_table() { + assert(_the_table == NULL, "One symbol table allowed."); + _the_table = new SymbolTable(); + } + + static void create_table(HashtableBucket* t, int length, + int number_of_entries) { + assert(_the_table == NULL, "One symbol table allowed."); + assert(length == symbol_table_size * sizeof(HashtableBucket), + "bad shared symbol size."); + _the_table = new SymbolTable(t, number_of_entries); + } + + static symbolOop lookup(const char* name, int len, TRAPS); + // lookup only, won't add. Also calculate hash. + static symbolOop lookup_only(const char* name, int len, unsigned int& hash); + // Only copy to C string to be added if lookup failed. + static symbolOop lookup(symbolHandle sym, int begin, int end, TRAPS); + + static void add(constantPoolHandle cp, int names_count, + const char** names, int* lengths, int* cp_indices, + unsigned int* hashValues, TRAPS); + + // GC support + // Delete pointers to otherwise-unreachable objects. + static void unlink(BoolObjectClosure* cl) { + the_table()->Hashtable::unlink(cl); + } + + // Invoke "f->do_oop" on the locations of all oops in the table. + static void oops_do(OopClosure* f) { + the_table()->Hashtable::oops_do(f); + } + + // Symbol lookup + static symbolOop lookup(int index, const char* name, int len, TRAPS); + + // Needed for preloading classes in signatures when compiling. + // Returns the symbol is already present in symbol table, otherwise + // NULL. NO ALLOCATION IS GUARANTEED! + static symbolOop probe(const char* name, int len); + + // Histogram + static void print_histogram() PRODUCT_RETURN; + + // Debugging + static void verify(); + + // Sharing + static void copy_buckets(char** top, char*end) { + the_table()->Hashtable::copy_buckets(top, end); + } + static void copy_table(char** top, char*end) { + the_table()->Hashtable::copy_table(top, end); + } + static void reverse(void* boundary = NULL) { + ((Hashtable*)the_table())->reverse(boundary); + } +}; + + +class StringTable : public Hashtable { + friend class VMStructs; + +private: + // The string table + static StringTable* _the_table; + + static oop intern(Handle string_or_null, jchar* chars, int length, TRAPS); + oop basic_add(int index, Handle string_or_null, jchar* name, int len, + unsigned int hashValue, TRAPS); + + // Table size + enum { + string_table_size = 1009 + }; + + oop lookup(int index, jchar* chars, int length, unsigned int hashValue); + + StringTable() : Hashtable(string_table_size, sizeof (HashtableEntry)) {} + + StringTable(HashtableBucket* t, int number_of_entries) + : Hashtable(string_table_size, sizeof (HashtableEntry), t, + number_of_entries) {} + +public: + // The string table + static StringTable* the_table() { return _the_table; } + + static void create_table() { + assert(_the_table == NULL, "One string table allowed."); + _the_table = new StringTable(); + } + + static void create_table(HashtableBucket* t, int length, + int number_of_entries) { + assert(_the_table == NULL, "One string table allowed."); + assert(length == string_table_size * sizeof(HashtableBucket), + "bad shared string size."); + _the_table = new StringTable(t, number_of_entries); + } + + + static int hash_string(jchar* s, int len); + + + // GC support + // Delete pointers to otherwise-unreachable objects. + static void unlink(BoolObjectClosure* cl) { + the_table()->Hashtable::unlink(cl); + } + + // Invoke "f->do_oop" on the locations of all oops in the table. + static void oops_do(OopClosure* f) { + the_table()->Hashtable::oops_do(f); + } + + // Probing + static oop lookup(symbolOop symbol); + + // Interning + static oop intern(symbolOop symbol, TRAPS); + static oop intern(oop string, TRAPS); + static oop intern(const char *utf8_string, TRAPS); + + // Debugging + static void verify(); + + // Sharing + static void copy_buckets(char** top, char*end) { + the_table()->Hashtable::copy_buckets(top, end); + } + static void copy_table(char** top, char*end) { + the_table()->Hashtable::copy_table(top, end); + } + static void reverse() { + ((BasicHashtable*)the_table())->reverse(); + } +}; diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp new file mode 100644 index 00000000000..d71f63a6c12 --- /dev/null +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp @@ -0,0 +1,2473 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_systemDictionary.cpp.incl" + + +Dictionary* SystemDictionary::_dictionary = NULL; +PlaceholderTable* SystemDictionary::_placeholders = NULL; +Dictionary* SystemDictionary::_shared_dictionary = NULL; +LoaderConstraintTable* SystemDictionary::_loader_constraints = NULL; +ResolutionErrorTable* SystemDictionary::_resolution_errors = NULL; + + +int SystemDictionary::_number_of_modifications = 0; + +oop SystemDictionary::_system_loader_lock_obj = NULL; + +klassOop SystemDictionary::_object_klass = NULL; +klassOop SystemDictionary::_string_klass = NULL; +klassOop SystemDictionary::_class_klass = NULL; +klassOop SystemDictionary::_cloneable_klass = NULL; +klassOop SystemDictionary::_classloader_klass = NULL; +klassOop SystemDictionary::_serializable_klass = NULL; +klassOop SystemDictionary::_system_klass = NULL; + +klassOop SystemDictionary::_throwable_klass = NULL; +klassOop SystemDictionary::_error_klass = NULL; +klassOop SystemDictionary::_threaddeath_klass = NULL; +klassOop SystemDictionary::_exception_klass = NULL; +klassOop SystemDictionary::_runtime_exception_klass = NULL; +klassOop SystemDictionary::_classNotFoundException_klass = NULL; +klassOop SystemDictionary::_noClassDefFoundError_klass = NULL; +klassOop SystemDictionary::_linkageError_klass = NULL; +klassOop SystemDictionary::_classCastException_klass = NULL; +klassOop SystemDictionary::_arrayStoreException_klass = NULL; +klassOop SystemDictionary::_virtualMachineError_klass = NULL; +klassOop SystemDictionary::_outOfMemoryError_klass = NULL; +klassOop SystemDictionary::_StackOverflowError_klass = NULL; +klassOop SystemDictionary::_illegalMonitorStateException_klass = NULL; +klassOop SystemDictionary::_protectionDomain_klass = NULL; +klassOop SystemDictionary::_AccessControlContext_klass = NULL; + +klassOop SystemDictionary::_reference_klass = NULL; +klassOop SystemDictionary::_soft_reference_klass = NULL; +klassOop SystemDictionary::_weak_reference_klass = NULL; +klassOop SystemDictionary::_final_reference_klass = NULL; +klassOop SystemDictionary::_phantom_reference_klass = NULL; +klassOop SystemDictionary::_finalizer_klass = NULL; + +klassOop SystemDictionary::_thread_klass = NULL; +klassOop SystemDictionary::_threadGroup_klass = NULL; +klassOop SystemDictionary::_properties_klass = NULL; +klassOop SystemDictionary::_reflect_accessible_object_klass = NULL; +klassOop SystemDictionary::_reflect_field_klass = NULL; +klassOop SystemDictionary::_reflect_method_klass = NULL; +klassOop SystemDictionary::_reflect_constructor_klass = NULL; +klassOop SystemDictionary::_reflect_magic_klass = NULL; +klassOop SystemDictionary::_reflect_method_accessor_klass = NULL; +klassOop SystemDictionary::_reflect_constructor_accessor_klass = NULL; +klassOop SystemDictionary::_reflect_delegating_classloader_klass = NULL; +klassOop SystemDictionary::_reflect_constant_pool_klass = NULL; +klassOop SystemDictionary::_reflect_unsafe_static_field_accessor_impl_klass = NULL; + +klassOop SystemDictionary::_vector_klass = NULL; +klassOop SystemDictionary::_hashtable_klass = NULL; +klassOop SystemDictionary::_stringBuffer_klass = NULL; + +klassOop SystemDictionary::_stackTraceElement_klass = NULL; + +klassOop SystemDictionary::_java_nio_Buffer_klass = NULL; + +klassOop SystemDictionary::_sun_misc_AtomicLongCSImpl_klass = NULL; +klassOop SystemDictionary::_sun_jkernel_DownloadManager_klass = NULL; + +klassOop SystemDictionary::_boolean_klass = NULL; +klassOop SystemDictionary::_char_klass = NULL; +klassOop SystemDictionary::_float_klass = NULL; +klassOop SystemDictionary::_double_klass = NULL; +klassOop SystemDictionary::_byte_klass = NULL; +klassOop SystemDictionary::_short_klass = NULL; +klassOop SystemDictionary::_int_klass = NULL; +klassOop SystemDictionary::_long_klass = NULL; +klassOop SystemDictionary::_box_klasses[T_VOID+1] = { NULL /*, NULL...*/ }; + +oop SystemDictionary::_java_system_loader = NULL; + +bool SystemDictionary::_has_loadClassInternal = false; +bool SystemDictionary::_has_checkPackageAccess = false; + +// lazily initialized klass variables +volatile klassOop SystemDictionary::_abstract_ownable_synchronizer_klass = NULL; + + +// ---------------------------------------------------------------------------- +// Java-level SystemLoader + +oop SystemDictionary::java_system_loader() { + return _java_system_loader; +} + +void SystemDictionary::compute_java_system_loader(TRAPS) { + KlassHandle system_klass(THREAD, _classloader_klass); + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, + KlassHandle(THREAD, _classloader_klass), + vmSymbolHandles::getSystemClassLoader_name(), + vmSymbolHandles::void_classloader_signature(), + CHECK); + + _java_system_loader = (oop)result.get_jobject(); +} + + +// ---------------------------------------------------------------------------- +// debugging + +#ifdef ASSERT + +// return true if class_name contains no '.' (internal format is '/') +bool SystemDictionary::is_internal_format(symbolHandle class_name) { + if (class_name.not_null()) { + ResourceMark rm; + char* name = class_name->as_C_string(); + return strchr(name, '.') == NULL; + } else { + return true; + } +} + +#endif + +// ---------------------------------------------------------------------------- +// Resolving of classes + +// Forwards to resolve_or_null + +klassOop SystemDictionary::resolve_or_fail(symbolHandle class_name, Handle class_loader, Handle protection_domain, bool throw_error, TRAPS) { + klassOop klass = resolve_or_null(class_name, class_loader, protection_domain, THREAD); + if (HAS_PENDING_EXCEPTION || klass == NULL) { + KlassHandle k_h(THREAD, klass); + // can return a null klass + klass = handle_resolution_exception(class_name, class_loader, protection_domain, throw_error, k_h, THREAD); + } + return klass; +} + +klassOop SystemDictionary::handle_resolution_exception(symbolHandle class_name, Handle class_loader, Handle protection_domain, bool throw_error, KlassHandle klass_h, TRAPS) { + if (HAS_PENDING_EXCEPTION) { + // If we have a pending exception we forward it to the caller, unless throw_error is true, + // in which case we have to check whether the pending exception is a ClassNotFoundException, + // and if so convert it to a NoClassDefFoundError + // And chain the original ClassNotFoundException + if (throw_error && PENDING_EXCEPTION->is_a(SystemDictionary::classNotFoundException_klass())) { + ResourceMark rm(THREAD); + assert(klass_h() == NULL, "Should not have result with exception pending"); + Handle e(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + THROW_MSG_CAUSE_0(vmSymbols::java_lang_NoClassDefFoundError(), class_name->as_C_string(), e); + } else { + return NULL; + } + } + // Class not found, throw appropriate error or exception depending on value of throw_error + if (klass_h() == NULL) { + ResourceMark rm(THREAD); + if (throw_error) { + THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), class_name->as_C_string()); + } else { + THROW_MSG_0(vmSymbols::java_lang_ClassNotFoundException(), class_name->as_C_string()); + } + } + return (klassOop)klass_h(); +} + + +klassOop SystemDictionary::resolve_or_fail(symbolHandle class_name, + bool throw_error, TRAPS) +{ + return resolve_or_fail(class_name, Handle(), Handle(), throw_error, THREAD); +} + + +// Forwards to resolve_instance_class_or_null + +klassOop SystemDictionary::resolve_or_null(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS) { + assert(!THREAD->is_Compiler_thread(), "Can not load classes with the Compiler thread"); + if (FieldType::is_array(class_name())) { + return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL); + } else { + return resolve_instance_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL); + } +} + +klassOop SystemDictionary::resolve_or_null(symbolHandle class_name, TRAPS) { + return resolve_or_null(class_name, Handle(), Handle(), THREAD); +} + +// Forwards to resolve_instance_class_or_null + +klassOop SystemDictionary::resolve_array_class_or_null(symbolHandle class_name, + Handle class_loader, + Handle protection_domain, + TRAPS) { + assert(FieldType::is_array(class_name()), "must be array"); + jint dimension; + symbolOop object_key; + klassOop k = NULL; + // dimension and object_key are assigned as a side-effect of this call + BasicType t = FieldType::get_array_info(class_name(), + &dimension, + &object_key, + CHECK_NULL); + + if (t == T_OBJECT) { + symbolHandle h_key(THREAD, object_key); + // naked oop "k" is OK here -- we assign back into it + k = SystemDictionary::resolve_instance_class_or_null(h_key, + class_loader, + protection_domain, + CHECK_NULL); + if (k != NULL) { + k = Klass::cast(k)->array_klass(dimension, CHECK_NULL); + } + } else { + k = Universe::typeArrayKlassObj(t); + k = typeArrayKlass::cast(k)->array_klass(dimension, CHECK_NULL); + } + return k; +} + + +// Must be called for any super-class or super-interface resolution +// during class definition to allow class circularity checking +// super-interface callers: +// parse_interfaces - for defineClass & jvmtiRedefineClasses +// super-class callers: +// ClassFileParser - for defineClass & jvmtiRedefineClasses +// load_shared_class - while loading a class from shared archive +// resolve_instance_class_or_fail: +// when resolving a class that has an existing placeholder with +// a saved superclass [i.e. a defineClass is currently in progress] +// if another thread is trying to resolve the class, it must do +// super-class checks on its own thread to catch class circularity +// This last call is critical in class circularity checking for cases +// where classloading is delegated to different threads and the +// classloader lock is released. +// Take the case: Base->Super->Base +// 1. If thread T1 tries to do a defineClass of class Base +// resolve_super_or_fail creates placeholder: T1, Base (super Super) +// 2. resolve_instance_class_or_null does not find SD or placeholder for Super +// so it tries to load Super +// 3. If we load the class internally, or user classloader uses same thread +// loadClassFromxxx or defineClass via parseClassFile Super ... +// 3.1 resolve_super_or_fail creates placeholder: T1, Super (super Base) +// 3.3 resolve_instance_class_or_null Base, finds placeholder for Base +// 3.4 calls resolve_super_or_fail Base +// 3.5 finds T1,Base -> throws class circularity +//OR 4. If T2 tries to resolve Super via defineClass Super ... +// 4.1 resolve_super_or_fail creates placeholder: T2, Super (super Base) +// 4.2 resolve_instance_class_or_null Base, finds placeholder for Base (super Super) +// 4.3 calls resolve_super_or_fail Super in parallel on own thread T2 +// 4.4 finds T2, Super -> throws class circularity +// Must be called, even if superclass is null, since this is +// where the placeholder entry is created which claims this +// thread is loading this class/classloader. +klassOop SystemDictionary::resolve_super_or_fail(symbolHandle child_name, + symbolHandle class_name, + Handle class_loader, + Handle protection_domain, + bool is_superclass, + TRAPS) { + + // Double-check, if child class is already loaded, just return super-class,interface + // Don't add a placedholder if already loaded, i.e. already in system dictionary + // Make sure there's a placeholder for the *child* before resolving. + // Used as a claim that this thread is currently loading superclass/classloader + // Used here for ClassCircularity checks and also for heap verification + // (every instanceKlass in the heap needs to be in the system dictionary + // or have a placeholder). + // Must check ClassCircularity before checking if super class is already loaded + // + // We might not already have a placeholder if this child_name was + // first seen via resolve_from_stream (jni_DefineClass or JVM_DefineClass); + // the name of the class might not be known until the stream is actually + // parsed. + // Bugs 4643874, 4715493 + // compute_hash can have a safepoint + + unsigned int d_hash = dictionary()->compute_hash(child_name, class_loader); + int d_index = dictionary()->hash_to_index(d_hash); + unsigned int p_hash = placeholders()->compute_hash(child_name, class_loader); + int p_index = placeholders()->hash_to_index(p_hash); + // can't throw error holding a lock + bool child_already_loaded = false; + bool throw_circularity_error = false; + { + MutexLocker mu(SystemDictionary_lock, THREAD); + klassOop childk = find_class(d_index, d_hash, child_name, class_loader); + klassOop quicksuperk; + // to support // loading: if child done loading, just return superclass + // if class_name, & class_loader don't match: + // if initial define, SD update will give LinkageError + // if redefine: compare_class_versions will give HIERARCHY_CHANGED + // so we don't throw an exception here. + // see: nsk redefclass014 & java.lang.instrument Instrument032 + if ((childk != NULL ) && (is_superclass) && + ((quicksuperk = instanceKlass::cast(childk)->super()) != NULL) && + + ((Klass::cast(quicksuperk)->name() == class_name()) && + (Klass::cast(quicksuperk)->class_loader() == class_loader()))) { + return quicksuperk; + } else { + PlaceholderEntry* probe = placeholders()->get_entry(p_index, p_hash, child_name, class_loader); + if (probe && probe->check_seen_thread(THREAD, PlaceholderTable::LOAD_SUPER)) { + throw_circularity_error = true; + } + + // add placeholder entry even if error - callers will remove on error + PlaceholderEntry* newprobe = placeholders()->find_and_add(p_index, p_hash, child_name, class_loader, PlaceholderTable::LOAD_SUPER, class_name, THREAD); + if (throw_circularity_error) { + newprobe->remove_seen_thread(THREAD, PlaceholderTable::LOAD_SUPER); + } + } + } + if (throw_circularity_error) { + ResourceMark rm(THREAD); + THROW_MSG_0(vmSymbols::java_lang_ClassCircularityError(), child_name->as_C_string()); + } + +// java.lang.Object should have been found above + assert(class_name() != NULL, "null super class for resolving"); + // Resolve the super class or interface, check results on return + klassOop superk = NULL; + superk = SystemDictionary::resolve_or_null(class_name, + class_loader, + protection_domain, + THREAD); + + KlassHandle superk_h(THREAD, superk); + + // Note: clean up of placeholders currently in callers of + // resolve_super_or_fail - either at update_dictionary time + // or on error + { + MutexLocker mu(SystemDictionary_lock, THREAD); + PlaceholderEntry* probe = placeholders()->get_entry(p_index, p_hash, child_name, class_loader); + if (probe != NULL) { + probe->remove_seen_thread(THREAD, PlaceholderTable::LOAD_SUPER); + } + } + if (HAS_PENDING_EXCEPTION || superk_h() == NULL) { + // can null superk + superk_h = KlassHandle(THREAD, handle_resolution_exception(class_name, class_loader, protection_domain, true, superk_h, THREAD)); + } + + return superk_h(); +} + + +void SystemDictionary::validate_protection_domain(instanceKlassHandle klass, + Handle class_loader, + Handle protection_domain, + TRAPS) { + if(!has_checkPackageAccess()) return; + + // Now we have to call back to java to check if the initating class has access + JavaValue result(T_VOID); + if (TraceProtectionDomainVerification) { + // Print out trace information + tty->print_cr("Checking package access"); + tty->print(" - class loader: "); class_loader()->print_value_on(tty); tty->cr(); + tty->print(" - protection domain: "); protection_domain()->print_value_on(tty); tty->cr(); + tty->print(" - loading: "); klass()->print_value_on(tty); tty->cr(); + } + + assert(class_loader() != NULL, "should not have non-null protection domain for null classloader"); + + KlassHandle system_loader(THREAD, SystemDictionary::classloader_klass()); + JavaCalls::call_special(&result, + class_loader, + system_loader, + vmSymbolHandles::checkPackageAccess_name(), + vmSymbolHandles::class_protectiondomain_signature(), + Handle(THREAD, klass->java_mirror()), + protection_domain, + THREAD); + + if (TraceProtectionDomainVerification) { + if (HAS_PENDING_EXCEPTION) { + tty->print_cr(" -> DENIED !!!!!!!!!!!!!!!!!!!!!"); + } else { + tty->print_cr(" -> granted"); + } + tty->cr(); + } + + if (HAS_PENDING_EXCEPTION) return; + + // If no exception has been thrown, we have validated the protection domain + // Insert the protection domain of the initiating class into the set. + { + // We recalculate the entry here -- we've called out to java since + // the last time it was calculated. + symbolHandle kn(THREAD, klass->name()); + unsigned int d_hash = dictionary()->compute_hash(kn, class_loader); + int d_index = dictionary()->hash_to_index(d_hash); + + MutexLocker mu(SystemDictionary_lock, THREAD); + { + // Note that we have an entry, and entries can be deleted only during GC, + // so we cannot allow GC to occur while we're holding this entry. + + // We're using a No_Safepoint_Verifier to catch any place where we + // might potentially do a GC at all. + // SystemDictionary::do_unloading() asserts that classes are only + // unloaded at a safepoint. + No_Safepoint_Verifier nosafepoint; + dictionary()->add_protection_domain(d_index, d_hash, klass, class_loader, + protection_domain, THREAD); + } + } +} + +// We only get here if this thread finds that another thread +// has already claimed the placeholder token for the current operation, +// but that other thread either never owned or gave up the +// object lock +// Waits on SystemDictionary_lock to indicate placeholder table updated +// On return, caller must recheck placeholder table state +// +// We only get here if +// 1) custom classLoader, i.e. not bootstrap classloader +// 2) UnsyncloadClass not set +// 3) custom classLoader has broken the class loader objectLock +// so another thread got here in parallel +// +// lockObject must be held. +// Complicated dance due to lock ordering: +// Must first release the classloader object lock to +// allow initial definer to complete the class definition +// and to avoid deadlock +// Reclaim classloader lock object with same original recursion count +// Must release SystemDictionary_lock after notify, since +// class loader lock must be claimed before SystemDictionary_lock +// to prevent deadlocks +// +// The notify allows applications that did an untimed wait() on +// the classloader object lock to not hang. +void SystemDictionary::double_lock_wait(Handle lockObject, TRAPS) { + assert_lock_strong(SystemDictionary_lock); + + bool calledholdinglock + = ObjectSynchronizer::current_thread_holds_lock((JavaThread*)THREAD, lockObject); + assert(calledholdinglock,"must hold lock for notify"); + assert(!UnsyncloadClass, "unexpected double_lock_wait"); + ObjectSynchronizer::notifyall(lockObject, THREAD); + intptr_t recursions = ObjectSynchronizer::complete_exit(lockObject, THREAD); + SystemDictionary_lock->wait(); + SystemDictionary_lock->unlock(); + ObjectSynchronizer::reenter(lockObject, recursions, THREAD); + SystemDictionary_lock->lock(); +} + +// If the class in is in the placeholder table, class loading is in progress +// For cases where the application changes threads to load classes, it +// is critical to ClassCircularity detection that we try loading +// the superclass on the same thread internally, so we do parallel +// super class loading here. +// This also is critical in cases where the original thread gets stalled +// even in non-circularity situations. +// Note: only one thread can define the class, but multiple can resolve +// Note: must call resolve_super_or_fail even if null super - +// to force placeholder entry creation for this class +// Caller must check for pending exception +// Returns non-null klassOop if other thread has completed load +// and we are done, +// If return null klassOop and no pending exception, the caller must load the class +instanceKlassHandle SystemDictionary::handle_parallel_super_load( + symbolHandle name, symbolHandle superclassname, Handle class_loader, + Handle protection_domain, Handle lockObject, TRAPS) { + + instanceKlassHandle nh = instanceKlassHandle(); // null Handle + unsigned int d_hash = dictionary()->compute_hash(name, class_loader); + int d_index = dictionary()->hash_to_index(d_hash); + unsigned int p_hash = placeholders()->compute_hash(name, class_loader); + int p_index = placeholders()->hash_to_index(p_hash); + + // superk is not used, resolve_super called for circularity check only + // This code is reached in two situations. One if this thread + // is loading the same class twice (e.g. ClassCircularity, or + // java.lang.instrument). + // The second is if another thread started the resolve_super first + // and has not yet finished. + // In both cases the original caller will clean up the placeholder + // entry on error. + klassOop superk = SystemDictionary::resolve_super_or_fail(name, + superclassname, + class_loader, + protection_domain, + true, + CHECK_(nh)); + // We don't redefine the class, so we just need to clean up if there + // was not an error (don't want to modify any system dictionary + // data structures). + { + MutexLocker mu(SystemDictionary_lock, THREAD); + placeholders()->find_and_remove(p_index, p_hash, name, class_loader, THREAD); + SystemDictionary_lock->notify_all(); + } + + // UnsyncloadClass does NOT wait for parallel superclass loads to complete + // Bootstrap classloader does wait for parallel superclass loads + if (UnsyncloadClass) { + MutexLocker mu(SystemDictionary_lock, THREAD); + // Check if classloading completed while we were loading superclass or waiting + klassOop check = find_class(d_index, d_hash, name, class_loader); + if (check != NULL) { + // Klass is already loaded, so just return it + return(instanceKlassHandle(THREAD, check)); + } else { + return nh; + } + } + + // must loop to both handle other placeholder updates + // and spurious notifications + bool super_load_in_progress = true; + PlaceholderEntry* placeholder; + while (super_load_in_progress) { + MutexLocker mu(SystemDictionary_lock, THREAD); + // Check if classloading completed while we were loading superclass or waiting + klassOop check = find_class(d_index, d_hash, name, class_loader); + if (check != NULL) { + // Klass is already loaded, so just return it + return(instanceKlassHandle(THREAD, check)); + } else { + placeholder = placeholders()->get_entry(p_index, p_hash, name, class_loader); + if (placeholder && placeholder->super_load_in_progress() ){ + // Before UnsyncloadClass: + // We only get here if the application has released the + // classloader lock when another thread was in the middle of loading a + // superclass/superinterface for this class, and now + // this thread is also trying to load this class. + // To minimize surprises, the first thread that started to + // load a class should be the one to complete the loading + // with the classfile it initially expected. + // This logic has the current thread wait once it has done + // all the superclass/superinterface loading it can, until + // the original thread completes the class loading or fails + // If it completes we will use the resulting instanceKlass + // which we will find below in the systemDictionary. + // We also get here for parallel bootstrap classloader + if (class_loader.is_null()) { + SystemDictionary_lock->wait(); + } else { + double_lock_wait(lockObject, THREAD); + } + } else { + // If not in SD and not in PH, other thread's load must have failed + super_load_in_progress = false; + } + } + } + return (nh); +} + + +klassOop SystemDictionary::resolve_instance_class_or_null(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS) { + assert(class_name.not_null() && !FieldType::is_array(class_name()), "invalid class name"); + // First check to see if we should remove wrapping L and ; + symbolHandle name; + if (FieldType::is_obj(class_name())) { + ResourceMark rm(THREAD); + // Ignore wrapping L and ;. + name = oopFactory::new_symbol_handle(class_name()->as_C_string() + 1, class_name()->utf8_length() - 2, CHECK_NULL); + } else { + name = class_name; + } + + // UseNewReflection + // Fix for 4474172; see evaluation for more details + class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader())); + + // Do lookup to see if class already exist and the protection domain + // has the right access + unsigned int d_hash = dictionary()->compute_hash(name, class_loader); + int d_index = dictionary()->hash_to_index(d_hash); + klassOop probe = dictionary()->find(d_index, d_hash, name, class_loader, + protection_domain, THREAD); + if (probe != NULL) return probe; + + + // Non-bootstrap class loaders will call out to class loader and + // define via jvm/jni_DefineClass which will acquire the + // class loader object lock to protect against multiple threads + // defining the class in parallel by accident. + // This lock must be acquired here so the waiter will find + // any successful result in the SystemDictionary and not attempt + // the define + // Classloaders that support parallelism, e.g. bootstrap classloader, + // or all classloaders with UnsyncloadClass do not acquire lock here + bool DoObjectLock = true; + if (UnsyncloadClass || (class_loader.is_null())) { + DoObjectLock = false; + } + + unsigned int p_hash = placeholders()->compute_hash(name, class_loader); + int p_index = placeholders()->hash_to_index(p_hash); + + // Class is not in SystemDictionary so we have to do loading. + // Make sure we are synchronized on the class loader before we proceed + Handle lockObject = compute_loader_lock_object(class_loader, THREAD); + check_loader_lock_contention(lockObject, THREAD); + ObjectLocker ol(lockObject, THREAD, DoObjectLock); + + // Check again (after locking) if class already exist in SystemDictionary + bool class_has_been_loaded = false; + bool super_load_in_progress = false; + bool havesupername = false; + instanceKlassHandle k; + PlaceholderEntry* placeholder; + symbolHandle superclassname; + + { + MutexLocker mu(SystemDictionary_lock, THREAD); + klassOop check = find_class(d_index, d_hash, name, class_loader); + if (check != NULL) { + // Klass is already loaded, so just return it + class_has_been_loaded = true; + k = instanceKlassHandle(THREAD, check); + } else { + placeholder = placeholders()->get_entry(p_index, p_hash, name, class_loader); + if (placeholder && placeholder->super_load_in_progress()) { + super_load_in_progress = true; + if (placeholder->havesupername() == true) { + superclassname = symbolHandle(THREAD, placeholder->supername()); + havesupername = true; + } + } + } + } + + // If the class in is in the placeholder table, class loading is in progress + if (super_load_in_progress && havesupername==true) { + k = SystemDictionary::handle_parallel_super_load(name, superclassname, + class_loader, protection_domain, lockObject, THREAD); + if (HAS_PENDING_EXCEPTION) { + return NULL; + } + if (!k.is_null()) { + class_has_been_loaded = true; + } + } + + if (!class_has_been_loaded) { + + // add placeholder entry to record loading instance class + // Five cases: + // All cases need to prevent modifying bootclasssearchpath + // in parallel with a classload of same classname + // case 1. traditional classloaders that rely on the classloader object lock + // - no other need for LOAD_INSTANCE + // case 2. traditional classloaders that break the classloader object lock + // as a deadlock workaround. Detection of this case requires that + // this check is done while holding the classloader object lock, + // and that lock is still held when calling classloader's loadClass. + // For these classloaders, we ensure that the first requestor + // completes the load and other requestors wait for completion. + // case 3. UnsyncloadClass - don't use objectLocker + // With this flag, we allow parallel classloading of a + // class/classloader pair + // case4. Bootstrap classloader - don't own objectLocker + // This classloader supports parallelism at the classloader level, + // but only allows a single load of a class/classloader pair. + // No performance benefit and no deadlock issues. + // case 5. Future: parallel user level classloaders - without objectLocker + symbolHandle nullsymbolHandle; + bool throw_circularity_error = false; + { + MutexLocker mu(SystemDictionary_lock, THREAD); + if (!UnsyncloadClass) { + PlaceholderEntry* oldprobe = placeholders()->get_entry(p_index, p_hash, name, class_loader); + if (oldprobe) { + // only need check_seen_thread once, not on each loop + // 6341374 java/lang/Instrument with -Xcomp + if (oldprobe->check_seen_thread(THREAD, PlaceholderTable::LOAD_INSTANCE)) { + throw_circularity_error = true; + } else { + // case 1: traditional: should never see load_in_progress. + while (!class_has_been_loaded && oldprobe && oldprobe->instance_load_in_progress()) { + + // case 4: bootstrap classloader: prevent futile classloading, + // wait on first requestor + if (class_loader.is_null()) { + SystemDictionary_lock->wait(); + } else { + // case 2: traditional with broken classloader lock. wait on first + // requestor. + double_lock_wait(lockObject, THREAD); + } + // Check if classloading completed while we were waiting + klassOop check = find_class(d_index, d_hash, name, class_loader); + if (check != NULL) { + // Klass is already loaded, so just return it + k = instanceKlassHandle(THREAD, check); + class_has_been_loaded = true; + } + // check if other thread failed to load and cleaned up + oldprobe = placeholders()->get_entry(p_index, p_hash, name, class_loader); + } + } + } + } + // All cases: add LOAD_INSTANCE + // case 3: UnsyncloadClass: allow competing threads to try + // LOAD_INSTANCE in parallel + // add placeholder entry even if error - callers will remove on error + if (!class_has_been_loaded) { + PlaceholderEntry* newprobe = placeholders()->find_and_add(p_index, p_hash, name, class_loader, PlaceholderTable::LOAD_INSTANCE, nullsymbolHandle, THREAD); + if (throw_circularity_error) { + newprobe->remove_seen_thread(THREAD, PlaceholderTable::LOAD_INSTANCE); + } + // For class loaders that do not acquire the classloader object lock, + // if they did not catch another thread holding LOAD_INSTANCE, + // need a check analogous to the acquire ObjectLocker/find_class + // i.e. now that we hold the LOAD_INSTANCE token on loading this class/CL + // one final check if the load has already completed + klassOop check = find_class(d_index, d_hash, name, class_loader); + if (check != NULL) { + // Klass is already loaded, so just return it + k = instanceKlassHandle(THREAD, check); + class_has_been_loaded = true; + newprobe->remove_seen_thread(THREAD, PlaceholderTable::LOAD_INSTANCE); + } + } + } + // must throw error outside of owning lock + if (throw_circularity_error) { + ResourceMark rm(THREAD); + THROW_MSG_0(vmSymbols::java_lang_ClassCircularityError(), name->as_C_string()); + } + + if (!class_has_been_loaded) { + + // Do actual loading + k = load_instance_class(name, class_loader, THREAD); + + // In custom class loaders, the usual findClass calls + // findLoadedClass, which directly searches the SystemDictionary, then + // defineClass. If these are not atomic with respect to other threads, + // the findLoadedClass can fail, but the defineClass can get a + // LinkageError:: duplicate class definition. + // If they got a linkageError, check if a parallel class load succeeded. + // If it did, then for bytecode resolution the specification requires + // that we return the same result we did for the other thread, i.e. the + // successfully loaded instanceKlass + // Note: Class can not be unloaded as long as any classloader refs exist + // Should not get here for classloaders that support parallelism + // with the new cleaner mechanism, e.g. bootstrap classloader + if (UnsyncloadClass || (class_loader.is_null())) { + if (k.is_null() && HAS_PENDING_EXCEPTION + && PENDING_EXCEPTION->is_a(SystemDictionary::linkageError_klass())) { + MutexLocker mu(SystemDictionary_lock, THREAD); + klassOop check = find_class(d_index, d_hash, name, class_loader); + if (check != NULL) { + // Klass is already loaded, so just use it + k = instanceKlassHandle(THREAD, check); + CLEAR_PENDING_EXCEPTION; + guarantee((!class_loader.is_null()), "dup definition for bootstrap loader?"); + } + } + } + + // clean up placeholder entries for success or error + // This cleans up LOAD_INSTANCE entries + // It also cleans up LOAD_SUPER entries on errors from + // calling load_instance_class + { + MutexLocker mu(SystemDictionary_lock, THREAD); + PlaceholderEntry* probe = placeholders()->get_entry(p_index, p_hash, name, class_loader); + if (probe != NULL) { + probe->remove_seen_thread(THREAD, PlaceholderTable::LOAD_INSTANCE); + placeholders()->find_and_remove(p_index, p_hash, name, class_loader, THREAD); + SystemDictionary_lock->notify_all(); + } + } + + // If everything was OK (no exceptions, no null return value), and + // class_loader is NOT the defining loader, do a little more bookkeeping. + if (!HAS_PENDING_EXCEPTION && !k.is_null() && + k->class_loader() != class_loader()) { + + check_constraints(d_index, d_hash, k, class_loader, false, THREAD); + + // Need to check for a PENDING_EXCEPTION again; check_constraints + // can throw and doesn't use the CHECK macro. + if (!HAS_PENDING_EXCEPTION) { + { // Grabbing the Compile_lock prevents systemDictionary updates + // during compilations. + MutexLocker mu(Compile_lock, THREAD); + update_dictionary(d_index, d_hash, p_index, p_hash, + k, class_loader, THREAD); + } + if (JvmtiExport::should_post_class_load()) { + Thread *thread = THREAD; + assert(thread->is_Java_thread(), "thread->is_Java_thread()"); + JvmtiExport::post_class_load((JavaThread *) thread, k()); + } + } + } + if (HAS_PENDING_EXCEPTION || k.is_null()) { + // On error, clean up placeholders + { + MutexLocker mu(SystemDictionary_lock, THREAD); + placeholders()->find_and_remove(p_index, p_hash, name, class_loader, THREAD); + SystemDictionary_lock->notify_all(); + } + return NULL; + } + } + } + +#ifdef ASSERT + { + Handle loader (THREAD, k->class_loader()); + MutexLocker mu(SystemDictionary_lock, THREAD); + oop kk = find_class_or_placeholder(name, loader); + assert(kk == k(), "should be present in dictionary"); + } +#endif + + // return if the protection domain in NULL + if (protection_domain() == NULL) return k(); + + // Check the protection domain has the right access + { + MutexLocker mu(SystemDictionary_lock, THREAD); + // Note that we have an entry, and entries can be deleted only during GC, + // so we cannot allow GC to occur while we're holding this entry. + // We're using a No_Safepoint_Verifier to catch any place where we + // might potentially do a GC at all. + // SystemDictionary::do_unloading() asserts that classes are only + // unloaded at a safepoint. + No_Safepoint_Verifier nosafepoint; + if (dictionary()->is_valid_protection_domain(d_index, d_hash, name, + class_loader, + protection_domain)) { + return k(); + } + } + + // Verify protection domain. If it fails an exception is thrown + validate_protection_domain(k, class_loader, protection_domain, CHECK_(klassOop(NULL))); + + return k(); +} + + +// This routine does not lock the system dictionary. +// +// Since readers don't hold a lock, we must make sure that system +// dictionary entries are only removed at a safepoint (when only one +// thread is running), and are added to in a safe way (all links must +// be updated in an MT-safe manner). +// +// Callers should be aware that an entry could be added just after +// _dictionary->bucket(index) is read here, so the caller will not see +// the new entry. + +klassOop SystemDictionary::find(symbolHandle class_name, + Handle class_loader, + Handle protection_domain, + TRAPS) { + + unsigned int d_hash = dictionary()->compute_hash(class_name, class_loader); + int d_index = dictionary()->hash_to_index(d_hash); + + { + // Note that we have an entry, and entries can be deleted only during GC, + // so we cannot allow GC to occur while we're holding this entry. + // We're using a No_Safepoint_Verifier to catch any place where we + // might potentially do a GC at all. + // SystemDictionary::do_unloading() asserts that classes are only + // unloaded at a safepoint. + No_Safepoint_Verifier nosafepoint; + return dictionary()->find(d_index, d_hash, class_name, class_loader, + protection_domain, THREAD); + } +} + + +// Look for a loaded instance or array klass by name. Do not do any loading. +// return NULL in case of error. +klassOop SystemDictionary::find_instance_or_array_klass(symbolHandle class_name, + Handle class_loader, + Handle protection_domain, + TRAPS) { + klassOop k = NULL; + assert(class_name() != NULL, "class name must be non NULL"); + if (FieldType::is_array(class_name())) { + // The name refers to an array. Parse the name. + jint dimension; + symbolOop object_key; + + // dimension and object_key are assigned as a side-effect of this call + BasicType t = FieldType::get_array_info(class_name(), &dimension, + &object_key, CHECK_(NULL)); + if (t != T_OBJECT) { + k = Universe::typeArrayKlassObj(t); + } else { + symbolHandle h_key(THREAD, object_key); + k = SystemDictionary::find(h_key, class_loader, protection_domain, THREAD); + } + if (k != NULL) { + k = Klass::cast(k)->array_klass_or_null(dimension); + } + } else { + k = find(class_name, class_loader, protection_domain, THREAD); + } + return k; +} + +// Note: this method is much like resolve_from_stream, but +// updates no supplemental data structures. +// TODO consolidate the two methods with a helper routine? +klassOop SystemDictionary::parse_stream(symbolHandle class_name, + Handle class_loader, + Handle protection_domain, + ClassFileStream* st, + TRAPS) { + symbolHandle parsed_name; + + // Parse the stream. Note that we do this even though this klass might + // already be present in the SystemDictionary, otherwise we would not + // throw potential ClassFormatErrors. + // + // Note: "name" is updated. + // Further note: a placeholder will be added for this class when + // super classes are loaded (resolve_super_or_fail). We expect this + // to be called for all classes but java.lang.Object; and we preload + // java.lang.Object through resolve_or_fail, not this path. + + instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, + class_loader, + protection_domain, + parsed_name, + THREAD); + + + // We don't redefine the class, so we just need to clean up whether there + // was an error or not (don't want to modify any system dictionary + // data structures). + // Parsed name could be null if we threw an error before we got far + // enough along to parse it -- in that case, there is nothing to clean up. + if (!parsed_name.is_null()) { + unsigned int p_hash = placeholders()->compute_hash(parsed_name, + class_loader); + int p_index = placeholders()->hash_to_index(p_hash); + { + MutexLocker mu(SystemDictionary_lock, THREAD); + placeholders()->find_and_remove(p_index, p_hash, parsed_name, class_loader, THREAD); + SystemDictionary_lock->notify_all(); + } + } + + return k(); +} + +// Add a klass to the system from a stream (called by jni_DefineClass and +// JVM_DefineClass). +// Note: class_name can be NULL. In that case we do not know the name of +// the class until we have parsed the stream. + +klassOop SystemDictionary::resolve_from_stream(symbolHandle class_name, + Handle class_loader, + Handle protection_domain, + ClassFileStream* st, + TRAPS) { + + // Make sure we are synchronized on the class loader before we initiate + // loading. + Handle lockObject = compute_loader_lock_object(class_loader, THREAD); + check_loader_lock_contention(lockObject, THREAD); + ObjectLocker ol(lockObject, THREAD); + + symbolHandle parsed_name; + + // Parse the stream. Note that we do this even though this klass might + // already be present in the SystemDictionary, otherwise we would not + // throw potential ClassFormatErrors. + // + // Note: "name" is updated. + // Further note: a placeholder will be added for this class when + // super classes are loaded (resolve_super_or_fail). We expect this + // to be called for all classes but java.lang.Object; and we preload + // java.lang.Object through resolve_or_fail, not this path. + + instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, + class_loader, + protection_domain, + parsed_name, + THREAD); + + const char* pkg = "java/"; + if (!HAS_PENDING_EXCEPTION && + !class_loader.is_null() && + !parsed_name.is_null() && + !strncmp((const char*)parsed_name->bytes(), pkg, strlen(pkg))) { + // It is illegal to define classes in the "java." package from + // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader + ResourceMark rm(THREAD); + char* name = parsed_name->as_C_string(); + char* index = strrchr(name, '/'); + *index = '\0'; // chop to just the package name + while ((index = strchr(name, '/')) != NULL) { + *index = '.'; // replace '/' with '.' in package name + } + const char* fmt = "Prohibited package name: %s"; + size_t len = strlen(fmt) + strlen(name); + char* message = NEW_RESOURCE_ARRAY(char, len); + jio_snprintf(message, len, fmt, name); + Exceptions::_throw_msg(THREAD_AND_LOCATION, + vmSymbols::java_lang_SecurityException(), message); + } + + if (!HAS_PENDING_EXCEPTION) { + assert(!parsed_name.is_null(), "Sanity"); + assert(class_name.is_null() || class_name() == parsed_name(), + "name mismatch"); + // Verification prevents us from creating names with dots in them, this + // asserts that that's the case. + assert(is_internal_format(parsed_name), + "external class name format used internally"); + + // Add class just loaded + define_instance_class(k, THREAD); + } + + // If parsing the class file or define_instance_class failed, we + // need to remove the placeholder added on our behalf. But we + // must make sure parsed_name is valid first (it won't be if we had + // a format error before the class was parsed far enough to + // find the name). + if (HAS_PENDING_EXCEPTION && !parsed_name.is_null()) { + unsigned int p_hash = placeholders()->compute_hash(parsed_name, + class_loader); + int p_index = placeholders()->hash_to_index(p_hash); + { + MutexLocker mu(SystemDictionary_lock, THREAD); + placeholders()->find_and_remove(p_index, p_hash, parsed_name, class_loader, THREAD); + SystemDictionary_lock->notify_all(); + } + return NULL; + } + + // Make sure that we didn't leave a place holder in the + // SystemDictionary; this is only done on success + debug_only( { + if (!HAS_PENDING_EXCEPTION) { + assert(!parsed_name.is_null(), "parsed_name is still null?"); + symbolHandle h_name (THREAD, k->name()); + Handle h_loader (THREAD, k->class_loader()); + + MutexLocker mu(SystemDictionary_lock, THREAD); + + oop check = find_class_or_placeholder(parsed_name, class_loader); + assert(check == k(), "should be present in the dictionary"); + + oop check2 = find_class_or_placeholder(h_name, h_loader); + assert(check == check2, "name inconsistancy in SystemDictionary"); + } + } ); + + return k(); +} + + +void SystemDictionary::set_shared_dictionary(HashtableBucket* t, int length, + int number_of_entries) { + assert(length == _nof_buckets * sizeof(HashtableBucket), + "bad shared dictionary size."); + _shared_dictionary = new Dictionary(_nof_buckets, t, number_of_entries); +} + + +// If there is a shared dictionary, then find the entry for the +// given shared system class, if any. + +klassOop SystemDictionary::find_shared_class(symbolHandle class_name) { + if (shared_dictionary() != NULL) { + unsigned int d_hash = dictionary()->compute_hash(class_name, Handle()); + int d_index = dictionary()->hash_to_index(d_hash); + return shared_dictionary()->find_shared_class(d_index, d_hash, class_name); + } else { + return NULL; + } +} + + +// Load a class from the shared spaces (found through the shared system +// dictionary). Force the superclass and all interfaces to be loaded. +// Update the class definition to include sibling classes and no +// subclasses (yet). [Classes in the shared space are not part of the +// object hierarchy until loaded.] + +instanceKlassHandle SystemDictionary::load_shared_class( + symbolHandle class_name, Handle class_loader, TRAPS) { + instanceKlassHandle ik (THREAD, find_shared_class(class_name)); + return load_shared_class(ik, class_loader, THREAD); +} + +// Note well! Changes to this method may affect oop access order +// in the shared archive. Please take care to not make changes that +// adversely affect cold start time by changing the oop access order +// that is specified in dump.cpp MarkAndMoveOrderedReadOnly and +// MarkAndMoveOrderedReadWrite closures. +instanceKlassHandle SystemDictionary::load_shared_class( + instanceKlassHandle ik, Handle class_loader, TRAPS) { + assert(class_loader.is_null(), "non-null classloader for shared class?"); + if (ik.not_null()) { + instanceKlassHandle nh = instanceKlassHandle(); // null Handle + symbolHandle class_name(THREAD, ik->name()); + + // Found the class, now load the superclass and interfaces. If they + // are shared, add them to the main system dictionary and reset + // their hierarchy references (supers, subs, and interfaces). + + if (ik->super() != NULL) { + symbolHandle cn(THREAD, ik->super()->klass_part()->name()); + resolve_super_or_fail(class_name, cn, + class_loader, Handle(), true, CHECK_(nh)); + } + + objArrayHandle interfaces (THREAD, ik->local_interfaces()); + int num_interfaces = interfaces->length(); + for (int index = 0; index < num_interfaces; index++) { + klassOop k = klassOop(interfaces->obj_at(index)); + + // Note: can not use instanceKlass::cast here because + // interfaces' instanceKlass's C++ vtbls haven't been + // reinitialized yet (they will be once the interface classes + // are loaded) + symbolHandle name (THREAD, k->klass_part()->name()); + resolve_super_or_fail(class_name, name, class_loader, Handle(), false, CHECK_(nh)); + } + + // Adjust methods to recover missing data. They need addresses for + // interpreter entry points and their default native method address + // must be reset. + + // Updating methods must be done under a lock so multiple + // threads don't update these in parallel + // Shared classes are all currently loaded by the bootstrap + // classloader, so this will never cause a deadlock on + // a custom class loader lock. + + { + Handle lockObject = compute_loader_lock_object(class_loader, THREAD); + check_loader_lock_contention(lockObject, THREAD); + ObjectLocker ol(lockObject, THREAD, true); + + objArrayHandle methods (THREAD, ik->methods()); + int num_methods = methods->length(); + for (int index2 = 0; index2 < num_methods; ++index2) { + methodHandle m(THREAD, methodOop(methods->obj_at(index2))); + m()->link_method(m, CHECK_(nh)); + } + } + + if (TraceClassLoading) { + ResourceMark rm; + tty->print("[Loaded %s", ik->external_name()); + tty->print(" from shared objects file"); + tty->print_cr("]"); + } + // notify a class loaded from shared object + ClassLoadingService::notify_class_loaded(instanceKlass::cast(ik()), + true /* shared class */); + } + return ik; +} + +#ifdef KERNEL +// Some classes on the bootstrap class path haven't been installed on the +// system yet. Call the DownloadManager method to make them appear in the +// bootstrap class path and try again to load the named class. +// Note that with delegation class loaders all classes in another loader will +// first try to call this so it'd better be fast!! +static instanceKlassHandle download_and_retry_class_load( + symbolHandle class_name, + TRAPS) { + + klassOop dlm = SystemDictionary::sun_jkernel_DownloadManager_klass(); + instanceKlassHandle nk; + + // If download manager class isn't loaded just return. + if (dlm == NULL) return nk; + + { HandleMark hm(THREAD); + ResourceMark rm(THREAD); + Handle s = java_lang_String::create_from_symbol(class_name, CHECK_(nk)); + Handle class_string = java_lang_String::externalize_classname(s, CHECK_(nk)); + + // return value + JavaValue result(T_OBJECT); + + // Call the DownloadManager. We assume that it has a lock because + // multiple classes could be not found and downloaded at the same time. + // class sun.misc.DownloadManager; + // public static String getBootClassPathEntryForClass(String className); + JavaCalls::call_static(&result, + KlassHandle(THREAD, dlm), + vmSymbolHandles::getBootClassPathEntryForClass_name(), + vmSymbolHandles::string_string_signature(), + class_string, + CHECK_(nk)); + + // Get result.string and add to bootclasspath + assert(result.get_type() == T_OBJECT, "just checking"); + oop obj = (oop) result.get_jobject(); + if (obj == NULL) { return nk; } + + char* new_class_name = java_lang_String::as_utf8_string(obj); + + // lock the loader + // we use this lock because JVMTI does. + Handle loader_lock(THREAD, SystemDictionary::system_loader_lock()); + + ObjectLocker ol(loader_lock, THREAD); + // add the file to the bootclasspath + ClassLoader::update_class_path_entry_list(new_class_name, true); + } // end HandleMark + + if (TraceClassLoading) { + ClassLoader::print_bootclasspath(); + } + return ClassLoader::load_classfile(class_name, CHECK_(nk)); +} +#endif // KERNEL + + +instanceKlassHandle SystemDictionary::load_instance_class(symbolHandle class_name, Handle class_loader, TRAPS) { + instanceKlassHandle nh = instanceKlassHandle(); // null Handle + if (class_loader.is_null()) { + // Search the shared system dictionary for classes preloaded into the + // shared spaces. + instanceKlassHandle k; + k = load_shared_class(class_name, class_loader, THREAD); + + if (k.is_null()) { + // Use VM class loader + k = ClassLoader::load_classfile(class_name, CHECK_(nh)); + } + +#ifdef KERNEL + // If the VM class loader has failed to load the class, call the + // DownloadManager class to make it magically appear on the classpath + // and try again. This is only configured with the Kernel VM. + if (k.is_null()) { + k = download_and_retry_class_load(class_name, CHECK_(nh)); + } +#endif // KERNEL + + // find_or_define_instance_class may return a different k + if (!k.is_null()) { + k = find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh)); + } + return k; + } else { + // Use user specified class loader to load class. Call loadClass operation on class_loader. + ResourceMark rm(THREAD); + + Handle s = java_lang_String::create_from_symbol(class_name, CHECK_(nh)); + // Translate to external class name format, i.e., convert '/' chars to '.' + Handle string = java_lang_String::externalize_classname(s, CHECK_(nh)); + + JavaValue result(T_OBJECT); + + KlassHandle spec_klass (THREAD, SystemDictionary::classloader_klass()); + + // UnsyncloadClass option means don't synchronize loadClass() calls. + // loadClassInternal() is synchronized and public loadClass(String) is not. + // This flag is for diagnostic purposes only. It is risky to call + // custom class loaders without synchronization. + // WARNING If a custom class loader does NOT synchronizer findClass, or callers of + // findClass, this flag risks unexpected timing bugs in the field. + // Do NOT assume this will be supported in future releases. + if (!UnsyncloadClass && has_loadClassInternal()) { + JavaCalls::call_special(&result, + class_loader, + spec_klass, + vmSymbolHandles::loadClassInternal_name(), + vmSymbolHandles::string_class_signature(), + string, + CHECK_(nh)); + } else { + JavaCalls::call_virtual(&result, + class_loader, + spec_klass, + vmSymbolHandles::loadClass_name(), + vmSymbolHandles::string_class_signature(), + string, + CHECK_(nh)); + } + + assert(result.get_type() == T_OBJECT, "just checking"); + oop obj = (oop) result.get_jobject(); + + // Primitive classes return null since forName() can not be + // used to obtain any of the Class objects representing primitives or void + if ((obj != NULL) && !(java_lang_Class::is_primitive(obj))) { + instanceKlassHandle k = + instanceKlassHandle(THREAD, java_lang_Class::as_klassOop(obj)); + // For user defined Java class loaders, check that the name returned is + // the same as that requested. This check is done for the bootstrap + // loader when parsing the class file. + if (class_name() == k->name()) { + return k; + } + } + // Class is not found or has the wrong name, return NULL + return nh; + } +} + +void SystemDictionary::define_instance_class(instanceKlassHandle k, TRAPS) { + + Handle class_loader_h(THREAD, k->class_loader()); + + // for bootstrap classloader don't acquire lock + if (!class_loader_h.is_null()) { + assert(ObjectSynchronizer::current_thread_holds_lock((JavaThread*)THREAD, + compute_loader_lock_object(class_loader_h, THREAD)), + "define called without lock"); + } + + + // Check class-loading constraints. Throw exception if violation is detected. + // Grabs and releases SystemDictionary_lock + // The check_constraints/find_class call and update_dictionary sequence + // must be "atomic" for a specific class/classloader pair so we never + // define two different instanceKlasses for that class/classloader pair. + // Existing classloaders will call define_instance_class with the + // classloader lock held + // Parallel classloaders will call find_or_define_instance_class + // which will require a token to perform the define class + symbolHandle name_h(THREAD, k->name()); + unsigned int d_hash = dictionary()->compute_hash(name_h, class_loader_h); + int d_index = dictionary()->hash_to_index(d_hash); + check_constraints(d_index, d_hash, k, class_loader_h, true, CHECK); + + // Register class just loaded with class loader (placed in Vector) + // Note we do this before updating the dictionary, as this can + // fail with an OutOfMemoryError (if it does, we will *not* put this + // class in the dictionary and will not update the class hierarchy). + if (k->class_loader() != NULL) { + methodHandle m(THREAD, Universe::loader_addClass_method()); + JavaValue result(T_VOID); + JavaCallArguments args(class_loader_h); + args.push_oop(Handle(THREAD, k->java_mirror())); + JavaCalls::call(&result, m, &args, CHECK); + } + + // Add the new class. We need recompile lock during update of CHA. + { + unsigned int p_hash = placeholders()->compute_hash(name_h, class_loader_h); + int p_index = placeholders()->hash_to_index(p_hash); + + MutexLocker mu_r(Compile_lock, THREAD); + + // Add to class hierarchy, initialize vtables, and do possible + // deoptimizations. + add_to_hierarchy(k, CHECK); // No exception, but can block + + // Add to systemDictionary - so other classes can see it. + // Grabs and releases SystemDictionary_lock + update_dictionary(d_index, d_hash, p_index, p_hash, + k, class_loader_h, THREAD); + } + k->eager_initialize(THREAD); + + // notify jvmti + if (JvmtiExport::should_post_class_load()) { + assert(THREAD->is_Java_thread(), "thread->is_Java_thread()"); + JvmtiExport::post_class_load((JavaThread *) THREAD, k()); + + } +} + +// Support parallel classloading +// Initial implementation for bootstrap classloader +// For future: +// For custom class loaders that support parallel classloading, +// in case they do not synchronize around +// FindLoadedClass/DefineClass calls, we check for parallel +// loading for them, wait if a defineClass is in progress +// and return the initial requestor's results +// For better performance, the class loaders should synchronize +// findClass(), i.e. FindLoadedClass/DefineClass or they +// potentially waste time reading and parsing the bytestream. +// Note: VM callers should ensure consistency of k/class_name,class_loader +instanceKlassHandle SystemDictionary::find_or_define_instance_class(symbolHandle class_name, Handle class_loader, instanceKlassHandle k, TRAPS) { + + instanceKlassHandle nh = instanceKlassHandle(); // null Handle + + unsigned int d_hash = dictionary()->compute_hash(class_name, class_loader); + int d_index = dictionary()->hash_to_index(d_hash); + +// Hold SD lock around find_class and placeholder creation for DEFINE_CLASS + unsigned int p_hash = placeholders()->compute_hash(class_name, class_loader); + int p_index = placeholders()->hash_to_index(p_hash); + PlaceholderEntry* probe; + + { + MutexLocker mu(SystemDictionary_lock, THREAD); + // First check if class already defined + klassOop check = find_class(d_index, d_hash, class_name, class_loader); + if (check != NULL) { + return(instanceKlassHandle(THREAD, check)); + } + + // Acquire define token for this class/classloader + symbolHandle nullsymbolHandle; + probe = placeholders()->find_and_add(p_index, p_hash, class_name, class_loader, PlaceholderTable::DEFINE_CLASS, nullsymbolHandle, THREAD); + // Check if another thread defining in parallel + if (probe->definer() == NULL) { + // Thread will define the class + probe->set_definer(THREAD); + } else { + // Wait for defining thread to finish and return results + while (probe->definer() != NULL) { + SystemDictionary_lock->wait(); + } + if (probe->instanceKlass() != NULL) { + probe->remove_seen_thread(THREAD, PlaceholderTable::DEFINE_CLASS); + return(instanceKlassHandle(THREAD, probe->instanceKlass())); + } else { + // If definer had an error, try again as any new thread would + probe->set_definer(THREAD); +#ifdef ASSERT + klassOop check = find_class(d_index, d_hash, class_name, class_loader); + assert(check == NULL, "definer missed recording success"); +#endif + } + } + } + + define_instance_class(k, THREAD); + + Handle linkage_exception = Handle(); // null handle + + // definer must notify any waiting threads + { + MutexLocker mu(SystemDictionary_lock, THREAD); + PlaceholderEntry* probe = placeholders()->get_entry(p_index, p_hash, class_name, class_loader); + assert(probe != NULL, "DEFINE_CLASS placeholder lost?"); + if (probe != NULL) { + if (HAS_PENDING_EXCEPTION) { + linkage_exception = Handle(THREAD,PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + } else { + probe->set_instanceKlass(k()); + } + probe->set_definer(NULL); + probe->remove_seen_thread(THREAD, PlaceholderTable::DEFINE_CLASS); + SystemDictionary_lock->notify_all(); + } + } + + // Can't throw exception while holding lock due to rank ordering + if (linkage_exception() != NULL) { + THROW_OOP_(linkage_exception(), nh); // throws exception and returns + } + + return k; +} + +Handle SystemDictionary::compute_loader_lock_object(Handle class_loader, TRAPS) { + // If class_loader is NULL we synchronize on _system_loader_lock_obj + if (class_loader.is_null()) { + return Handle(THREAD, _system_loader_lock_obj); + } else { + return class_loader; + } +} + +// This method is added to check how often we have to wait to grab loader +// lock. The results are being recorded in the performance counters defined in +// ClassLoader::_sync_systemLoaderLockContentionRate and +// ClassLoader::_sync_nonSystemLoaderLockConteionRate. +void SystemDictionary::check_loader_lock_contention(Handle loader_lock, TRAPS) { + if (!UsePerfData) { + return; + } + + assert(!loader_lock.is_null(), "NULL lock object"); + + if (ObjectSynchronizer::query_lock_ownership((JavaThread*)THREAD, loader_lock) + == ObjectSynchronizer::owner_other) { + // contention will likely happen, so increment the corresponding + // contention counter. + if (loader_lock() == _system_loader_lock_obj) { + ClassLoader::sync_systemLoaderLockContentionRate()->inc(); + } else { + ClassLoader::sync_nonSystemLoaderLockContentionRate()->inc(); + } + } +} + +// ---------------------------------------------------------------------------- +// Lookup + +klassOop SystemDictionary::find_class(int index, unsigned int hash, + symbolHandle class_name, + Handle class_loader) { + assert_locked_or_safepoint(SystemDictionary_lock); + assert (index == dictionary()->index_for(class_name, class_loader), + "incorrect index?"); + + klassOop k = dictionary()->find_class(index, hash, class_name, class_loader); + return k; +} + + +// Basic find on classes in the midst of being loaded +symbolOop SystemDictionary::find_placeholder(int index, unsigned int hash, + symbolHandle class_name, + Handle class_loader) { + assert_locked_or_safepoint(SystemDictionary_lock); + + return placeholders()->find_entry(index, hash, class_name, class_loader); +} + + +// Used for assertions and verification only +oop SystemDictionary::find_class_or_placeholder(symbolHandle class_name, + Handle class_loader) { + #ifndef ASSERT + guarantee(VerifyBeforeGC || + VerifyDuringGC || + VerifyBeforeExit || + VerifyAfterGC, "too expensive"); + #endif + assert_locked_or_safepoint(SystemDictionary_lock); + symbolOop class_name_ = class_name(); + oop class_loader_ = class_loader(); + + // First look in the loaded class array + unsigned int d_hash = dictionary()->compute_hash(class_name, class_loader); + int d_index = dictionary()->hash_to_index(d_hash); + oop lookup = find_class(d_index, d_hash, class_name, class_loader); + + if (lookup == NULL) { + // Next try the placeholders + unsigned int p_hash = placeholders()->compute_hash(class_name,class_loader); + int p_index = placeholders()->hash_to_index(p_hash); + lookup = find_placeholder(p_index, p_hash, class_name, class_loader); + } + + return lookup; +} + + +// Get the next class in the diictionary. +klassOop SystemDictionary::try_get_next_class() { + return dictionary()->try_get_next_class(); +} + + +// ---------------------------------------------------------------------------- +// Update hierachy. This is done before the new klass has been added to the SystemDictionary. The Recompile_lock +// is held, to ensure that the compiler is not using the class hierachy, and that deoptimization will kick in +// before a new class is used. + +void SystemDictionary::add_to_hierarchy(instanceKlassHandle k, TRAPS) { + assert(k.not_null(), "just checking"); + // Link into hierachy. Make sure the vtables are initialized before linking into + k->append_to_sibling_list(); // add to superklass/sibling list + k->process_interfaces(THREAD); // handle all "implements" declarations + k->set_init_state(instanceKlass::loaded); + // Now flush all code that depended on old class hierarchy. + // Note: must be done *after* linking k into the hierarchy (was bug 12/9/97) + // Also, first reinitialize vtable because it may have gotten out of synch + // while the new class wasn't connected to the class hierarchy. + Universe::flush_dependents_on(k); +} + + +// ---------------------------------------------------------------------------- +// GC support + +// Following roots during mark-sweep is separated in two phases. +// +// The first phase follows preloaded classes and all other system +// classes, since these will never get unloaded anyway. +// +// The second phase removes (unloads) unreachable classes from the +// system dictionary and follows the remaining classes' contents. + +void SystemDictionary::always_strong_oops_do(OopClosure* blk) { + // Follow preloaded classes/mirrors and system loader object + blk->do_oop(&_java_system_loader); + preloaded_oops_do(blk); + always_strong_classes_do(blk); +} + + +void SystemDictionary::always_strong_classes_do(OopClosure* blk) { + // Follow all system classes and temporary placeholders in dictionary + dictionary()->always_strong_classes_do(blk); + + // Placeholders. These are *always* strong roots, as they + // represent classes we're actively loading. + placeholders_do(blk); + + // Loader constraints. We must keep the symbolOop used in the name alive. + constraints()->always_strong_classes_do(blk); + + // Resolution errors keep the symbolOop for the error alive + resolution_errors()->always_strong_classes_do(blk); +} + + +void SystemDictionary::placeholders_do(OopClosure* blk) { + placeholders()->oops_do(blk); +} + + +bool SystemDictionary::do_unloading(BoolObjectClosure* is_alive) { + bool result = dictionary()->do_unloading(is_alive); + constraints()->purge_loader_constraints(is_alive); + resolution_errors()->purge_resolution_errors(is_alive); + return result; +} + + +// The mirrors are scanned by shared_oops_do() which is +// not called by oops_do(). In order to process oops in +// a necessary order, shared_oops_do() is call by +// Universe::oops_do(). +void SystemDictionary::oops_do(OopClosure* f) { + // Adjust preloaded classes and system loader object + f->do_oop(&_java_system_loader); + preloaded_oops_do(f); + + lazily_loaded_oops_do(f); + + // Adjust dictionary + dictionary()->oops_do(f); + + // Partially loaded classes + placeholders()->oops_do(f); + + // Adjust constraint table + constraints()->oops_do(f); + + // Adjust resolution error table + resolution_errors()->oops_do(f); +} + + +void SystemDictionary::preloaded_oops_do(OopClosure* f) { + f->do_oop((oop*) &_string_klass); + f->do_oop((oop*) &_object_klass); + f->do_oop((oop*) &_class_klass); + f->do_oop((oop*) &_cloneable_klass); + f->do_oop((oop*) &_classloader_klass); + f->do_oop((oop*) &_serializable_klass); + f->do_oop((oop*) &_system_klass); + + f->do_oop((oop*) &_throwable_klass); + f->do_oop((oop*) &_error_klass); + f->do_oop((oop*) &_threaddeath_klass); + f->do_oop((oop*) &_exception_klass); + f->do_oop((oop*) &_runtime_exception_klass); + f->do_oop((oop*) &_classNotFoundException_klass); + f->do_oop((oop*) &_noClassDefFoundError_klass); + f->do_oop((oop*) &_linkageError_klass); + f->do_oop((oop*) &_classCastException_klass); + f->do_oop((oop*) &_arrayStoreException_klass); + f->do_oop((oop*) &_virtualMachineError_klass); + f->do_oop((oop*) &_outOfMemoryError_klass); + f->do_oop((oop*) &_StackOverflowError_klass); + f->do_oop((oop*) &_illegalMonitorStateException_klass); + f->do_oop((oop*) &_protectionDomain_klass); + f->do_oop((oop*) &_AccessControlContext_klass); + + f->do_oop((oop*) &_reference_klass); + f->do_oop((oop*) &_soft_reference_klass); + f->do_oop((oop*) &_weak_reference_klass); + f->do_oop((oop*) &_final_reference_klass); + f->do_oop((oop*) &_phantom_reference_klass); + f->do_oop((oop*) &_finalizer_klass); + + f->do_oop((oop*) &_thread_klass); + f->do_oop((oop*) &_threadGroup_klass); + f->do_oop((oop*) &_properties_klass); + f->do_oop((oop*) &_reflect_accessible_object_klass); + f->do_oop((oop*) &_reflect_field_klass); + f->do_oop((oop*) &_reflect_method_klass); + f->do_oop((oop*) &_reflect_constructor_klass); + f->do_oop((oop*) &_reflect_magic_klass); + f->do_oop((oop*) &_reflect_method_accessor_klass); + f->do_oop((oop*) &_reflect_constructor_accessor_klass); + f->do_oop((oop*) &_reflect_delegating_classloader_klass); + f->do_oop((oop*) &_reflect_constant_pool_klass); + f->do_oop((oop*) &_reflect_unsafe_static_field_accessor_impl_klass); + + f->do_oop((oop*) &_stringBuffer_klass); + f->do_oop((oop*) &_vector_klass); + f->do_oop((oop*) &_hashtable_klass); + + f->do_oop((oop*) &_stackTraceElement_klass); + + f->do_oop((oop*) &_java_nio_Buffer_klass); + + f->do_oop((oop*) &_sun_misc_AtomicLongCSImpl_klass); + f->do_oop((oop*) &_sun_jkernel_DownloadManager_klass); + + f->do_oop((oop*) &_boolean_klass); + f->do_oop((oop*) &_char_klass); + f->do_oop((oop*) &_float_klass); + f->do_oop((oop*) &_double_klass); + f->do_oop((oop*) &_byte_klass); + f->do_oop((oop*) &_short_klass); + f->do_oop((oop*) &_int_klass); + f->do_oop((oop*) &_long_klass); + { + for (int i = 0; i < T_VOID+1; i++) { + if (_box_klasses[i] != NULL) { + assert(i >= T_BOOLEAN, "checking"); + f->do_oop((oop*) &_box_klasses[i]); + } + } + } + + // The basic type mirrors would have already been processed in + // Universe::oops_do(), via a call to shared_oops_do(), so should + // not be processed again. + + f->do_oop((oop*) &_system_loader_lock_obj); + FilteredFieldsMap::klasses_oops_do(f); +} + +void SystemDictionary::lazily_loaded_oops_do(OopClosure* f) { + f->do_oop((oop*) &_abstract_ownable_synchronizer_klass); +} + +// Just the classes from defining class loaders +// Don't iterate over placeholders +void SystemDictionary::classes_do(void f(klassOop)) { + dictionary()->classes_do(f); +} + +// Added for initialize_itable_for_klass +// Just the classes from defining class loaders +// Don't iterate over placeholders +void SystemDictionary::classes_do(void f(klassOop, TRAPS), TRAPS) { + dictionary()->classes_do(f, CHECK); +} + +// All classes, and their class loaders +// Don't iterate over placeholders +void SystemDictionary::classes_do(void f(klassOop, oop)) { + dictionary()->classes_do(f); +} + +// All classes, and their class loaders +// (added for helpers that use HandleMarks and ResourceMarks) +// Don't iterate over placeholders +void SystemDictionary::classes_do(void f(klassOop, oop, TRAPS), TRAPS) { + dictionary()->classes_do(f, CHECK); +} + +void SystemDictionary::placeholders_do(void f(symbolOop, oop)) { + placeholders()->entries_do(f); +} + +void SystemDictionary::methods_do(void f(methodOop)) { + dictionary()->methods_do(f); +} + +// ---------------------------------------------------------------------------- +// Lazily load klasses + +void SystemDictionary::load_abstract_ownable_synchronizer_klass(TRAPS) { + assert(JDK_Version::is_gte_jdk16x_version(), "Must be JDK 1.6 or later"); + + // if multiple threads calling this function, only one thread will load + // the class. The other threads will find the loaded version once the + // class is loaded. + klassOop aos = _abstract_ownable_synchronizer_klass; + if (aos == NULL) { + klassOop k = resolve_or_fail(vmSymbolHandles::java_util_concurrent_locks_AbstractOwnableSynchronizer(), true, CHECK); + // Force a fence to prevent any read before the write completes + OrderAccess::fence(); + _abstract_ownable_synchronizer_klass = k; + } +} + +// ---------------------------------------------------------------------------- +// Initialization + +void SystemDictionary::initialize(TRAPS) { + // Allocate arrays + assert(dictionary() == NULL, + "SystemDictionary should only be initialized once"); + _dictionary = new Dictionary(_nof_buckets); + _placeholders = new PlaceholderTable(_nof_buckets); + _number_of_modifications = 0; + _loader_constraints = new LoaderConstraintTable(_loader_constraint_size); + _resolution_errors = new ResolutionErrorTable(_resolution_error_size); + + // Allocate private object used as system class loader lock + _system_loader_lock_obj = oopFactory::new_system_objArray(0, CHECK); + // Initialize basic classes + initialize_preloaded_classes(CHECK); +} + + +void SystemDictionary::initialize_preloaded_classes(TRAPS) { + assert(_object_klass == NULL, "preloaded classes should only be initialized once"); + // Preload commonly used klasses + _object_klass = resolve_or_fail(vmSymbolHandles::java_lang_Object(), true, CHECK); + _string_klass = resolve_or_fail(vmSymbolHandles::java_lang_String(), true, CHECK); + _class_klass = resolve_or_fail(vmSymbolHandles::java_lang_Class(), true, CHECK); + debug_only(instanceKlass::verify_class_klass_nonstatic_oop_maps(_class_klass)); + // Fixup mirrors for classes loaded before java.lang.Class. + // These calls iterate over the objects currently in the perm gen + // so calling them at this point is matters (not before when there + // are fewer objects and not later after there are more objects + // in the perm gen. + Universe::initialize_basic_type_mirrors(CHECK); + Universe::fixup_mirrors(CHECK); + + _cloneable_klass = resolve_or_fail(vmSymbolHandles::java_lang_Cloneable(), true, CHECK); + _classloader_klass = resolve_or_fail(vmSymbolHandles::java_lang_ClassLoader(), true, CHECK); + _serializable_klass = resolve_or_fail(vmSymbolHandles::java_io_Serializable(), true, CHECK); + _system_klass = resolve_or_fail(vmSymbolHandles::java_lang_System(), true, CHECK); + + _throwable_klass = resolve_or_fail(vmSymbolHandles::java_lang_Throwable(), true, CHECK); + _error_klass = resolve_or_fail(vmSymbolHandles::java_lang_Error(), true, CHECK); + _threaddeath_klass = resolve_or_fail(vmSymbolHandles::java_lang_ThreadDeath(), true, CHECK); + _exception_klass = resolve_or_fail(vmSymbolHandles::java_lang_Exception(), true, CHECK); + _runtime_exception_klass = resolve_or_fail(vmSymbolHandles::java_lang_RuntimeException(), true, CHECK); + _protectionDomain_klass = resolve_or_fail(vmSymbolHandles::java_security_ProtectionDomain(), true, CHECK); + _AccessControlContext_klass = resolve_or_fail(vmSymbolHandles::java_security_AccessControlContext(), true, CHECK); + _classNotFoundException_klass = resolve_or_fail(vmSymbolHandles::java_lang_ClassNotFoundException(), true, CHECK); + _noClassDefFoundError_klass = resolve_or_fail(vmSymbolHandles::java_lang_NoClassDefFoundError(), true, CHECK); + _linkageError_klass = resolve_or_fail(vmSymbolHandles::java_lang_LinkageError(), true, CHECK); + _classCastException_klass = resolve_or_fail(vmSymbolHandles::java_lang_ClassCastException(), true, CHECK); + _arrayStoreException_klass = resolve_or_fail(vmSymbolHandles::java_lang_ArrayStoreException(), true, CHECK); + _virtualMachineError_klass = resolve_or_fail(vmSymbolHandles::java_lang_VirtualMachineError(), true, CHECK); + _outOfMemoryError_klass = resolve_or_fail(vmSymbolHandles::java_lang_OutOfMemoryError(), true, CHECK); + _StackOverflowError_klass = resolve_or_fail(vmSymbolHandles::java_lang_StackOverflowError(), true, CHECK); + _illegalMonitorStateException_klass = resolve_or_fail(vmSymbolHandles::java_lang_IllegalMonitorStateException(), true, CHECK); + + // Preload ref klasses and set reference types + _reference_klass = resolve_or_fail(vmSymbolHandles::java_lang_ref_Reference(), true, CHECK); + instanceKlass::cast(_reference_klass)->set_reference_type(REF_OTHER); + instanceRefKlass::update_nonstatic_oop_maps(_reference_klass); + + _soft_reference_klass = resolve_or_fail(vmSymbolHandles::java_lang_ref_SoftReference(), true, CHECK); + instanceKlass::cast(_soft_reference_klass)->set_reference_type(REF_SOFT); + _weak_reference_klass = resolve_or_fail(vmSymbolHandles::java_lang_ref_WeakReference(), true, CHECK); + instanceKlass::cast(_weak_reference_klass)->set_reference_type(REF_WEAK); + _final_reference_klass = resolve_or_fail(vmSymbolHandles::java_lang_ref_FinalReference(), true, CHECK); + instanceKlass::cast(_final_reference_klass)->set_reference_type(REF_FINAL); + _phantom_reference_klass = resolve_or_fail(vmSymbolHandles::java_lang_ref_PhantomReference(), true, CHECK); + instanceKlass::cast(_phantom_reference_klass)->set_reference_type(REF_PHANTOM); + _finalizer_klass = resolve_or_fail(vmSymbolHandles::java_lang_ref_Finalizer(), true, CHECK); + + _thread_klass = resolve_or_fail(vmSymbolHandles::java_lang_Thread(), true, CHECK); + _threadGroup_klass = resolve_or_fail(vmSymbolHandles::java_lang_ThreadGroup(), true, CHECK); + _properties_klass = resolve_or_fail(vmSymbolHandles::java_util_Properties(), true, CHECK); + _reflect_accessible_object_klass = resolve_or_fail(vmSymbolHandles::java_lang_reflect_AccessibleObject(), true, CHECK); + _reflect_field_klass = resolve_or_fail(vmSymbolHandles::java_lang_reflect_Field(), true, CHECK); + _reflect_method_klass = resolve_or_fail(vmSymbolHandles::java_lang_reflect_Method(), true, CHECK); + _reflect_constructor_klass = resolve_or_fail(vmSymbolHandles::java_lang_reflect_Constructor(), true, CHECK); + // Universe::is_gte_jdk14x_version() is not set up by this point. + // It's okay if these turn out to be NULL in non-1.4 JDKs. + _reflect_magic_klass = resolve_or_null(vmSymbolHandles::sun_reflect_MagicAccessorImpl(), CHECK); + _reflect_method_accessor_klass = resolve_or_null(vmSymbolHandles::sun_reflect_MethodAccessorImpl(), CHECK); + _reflect_constructor_accessor_klass = resolve_or_null(vmSymbolHandles::sun_reflect_ConstructorAccessorImpl(), CHECK); + _reflect_delegating_classloader_klass = resolve_or_null(vmSymbolHandles::sun_reflect_DelegatingClassLoader(), CHECK); + _reflect_constant_pool_klass = resolve_or_null(vmSymbolHandles::sun_reflect_ConstantPool(), CHECK); + _reflect_unsafe_static_field_accessor_impl_klass = resolve_or_null(vmSymbolHandles::sun_reflect_UnsafeStaticFieldAccessorImpl(), CHECK); + + _vector_klass = resolve_or_fail(vmSymbolHandles::java_util_Vector(), true, CHECK); + _hashtable_klass = resolve_or_fail(vmSymbolHandles::java_util_Hashtable(), true, CHECK); + _stringBuffer_klass = resolve_or_fail(vmSymbolHandles::java_lang_StringBuffer(), true, CHECK); + + // It's NULL in non-1.4 JDKs. + _stackTraceElement_klass = resolve_or_null(vmSymbolHandles::java_lang_StackTraceElement(), CHECK); + + // Universe::is_gte_jdk14x_version() is not set up by this point. + // It's okay if this turns out to be NULL in non-1.4 JDKs. + _java_nio_Buffer_klass = resolve_or_null(vmSymbolHandles::java_nio_Buffer(), CHECK); + + // If this class isn't present, it won't be referenced. + _sun_misc_AtomicLongCSImpl_klass = resolve_or_null(vmSymbolHandles::sun_misc_AtomicLongCSImpl(), CHECK); +#ifdef KERNEL + _sun_jkernel_DownloadManager_klass = resolve_or_null(vmSymbolHandles::sun_jkernel_DownloadManager(), CHECK); + if (_sun_jkernel_DownloadManager_klass == NULL) { + warning("Cannot find sun/jkernel/DownloadManager"); + } +#endif // KERNEL + + // Preload boxing klasses + _boolean_klass = resolve_or_fail(vmSymbolHandles::java_lang_Boolean(), true, CHECK); + _char_klass = resolve_or_fail(vmSymbolHandles::java_lang_Character(), true, CHECK); + _float_klass = resolve_or_fail(vmSymbolHandles::java_lang_Float(), true, CHECK); + _double_klass = resolve_or_fail(vmSymbolHandles::java_lang_Double(), true, CHECK); + _byte_klass = resolve_or_fail(vmSymbolHandles::java_lang_Byte(), true, CHECK); + _short_klass = resolve_or_fail(vmSymbolHandles::java_lang_Short(), true, CHECK); + _int_klass = resolve_or_fail(vmSymbolHandles::java_lang_Integer(), true, CHECK); + _long_klass = resolve_or_fail(vmSymbolHandles::java_lang_Long(), true, CHECK); + + _box_klasses[T_BOOLEAN] = _boolean_klass; + _box_klasses[T_CHAR] = _char_klass; + _box_klasses[T_FLOAT] = _float_klass; + _box_klasses[T_DOUBLE] = _double_klass; + _box_klasses[T_BYTE] = _byte_klass; + _box_klasses[T_SHORT] = _short_klass; + _box_klasses[T_INT] = _int_klass; + _box_klasses[T_LONG] = _long_klass; + //_box_klasses[T_OBJECT] = _object_klass; + //_box_klasses[T_ARRAY] = _object_klass; + + { // Compute whether we should use loadClass or loadClassInternal when loading classes. + methodOop method = instanceKlass::cast(classloader_klass())->find_method(vmSymbols::loadClassInternal_name(), vmSymbols::string_class_signature()); + _has_loadClassInternal = (method != NULL); + } + + { // Compute whether we should use checkPackageAccess or NOT + methodOop method = instanceKlass::cast(classloader_klass())->find_method(vmSymbols::checkPackageAccess_name(), vmSymbols::class_protectiondomain_signature()); + _has_checkPackageAccess = (method != NULL); + } +} + +// Tells if a given klass is a box (wrapper class, such as java.lang.Integer). +// If so, returns the basic type it holds. If not, returns T_OBJECT. +BasicType SystemDictionary::box_klass_type(klassOop k) { + assert(k != NULL, ""); + for (int i = T_BOOLEAN; i < T_VOID+1; i++) { + if (_box_klasses[i] == k) + return (BasicType)i; + } + return T_OBJECT; +} + +// Constraints on class loaders. The details of the algorithm can be +// found in the OOPSLA'98 paper "Dynamic Class Loading in the Java +// Virtual Machine" by Sheng Liang and Gilad Bracha. The basic idea is +// that the system dictionary needs to maintain a set of contraints that +// must be satisfied by all classes in the dictionary. +// if defining is true, then LinkageError if already in systemDictionary +// if initiating loader, then ok if instanceKlass matches existing entry + +void SystemDictionary::check_constraints(int d_index, unsigned int d_hash, + instanceKlassHandle k, + Handle class_loader, bool defining, + TRAPS) { + const char *linkage_error = NULL; + { + symbolHandle name (THREAD, k->name()); + MutexLocker mu(SystemDictionary_lock, THREAD); + + klassOop check = find_class(d_index, d_hash, name, class_loader); + if (check != (klassOop)NULL) { + // if different instanceKlass - duplicate class definition, + // else - ok, class loaded by a different thread in parallel, + // we should only have found it if it was done loading and ok to use + // system dictionary only holds instance classes, placeholders + // also holds array classes + + assert(check->klass_part()->oop_is_instance(), "noninstance in systemdictionary"); + if ((defining == true) || (k() != check)) { + linkage_error = "loader (instance of %s): attempted duplicate class " + "definition for name: \"%s\""; + } else { + return; + } + } + +#ifdef ASSERT + unsigned int p_hash = placeholders()->compute_hash(name, class_loader); + int p_index = placeholders()->hash_to_index(p_hash); + symbolOop ph_check = find_placeholder(p_index, p_hash, name, class_loader); + assert(ph_check == NULL || ph_check == name(), "invalid symbol"); +#endif + + if (linkage_error == NULL) { + if (constraints()->check_or_update(k, class_loader, name) == false) { + linkage_error = "loader constraint violation: loader (instance of %s)" + " previously initiated loading for a different type with name \"%s\""; + } + } + } + + // Throw error now if needed (cannot throw while holding + // SystemDictionary_lock because of rank ordering) + + if (linkage_error) { + ResourceMark rm(THREAD); + const char* class_loader_name = loader_name(class_loader()); + char* type_name = k->name()->as_C_string(); + size_t buflen = strlen(linkage_error) + strlen(class_loader_name) + + strlen(type_name); + char* buf = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, buflen); + jio_snprintf(buf, buflen, linkage_error, class_loader_name, type_name); + THROW_MSG(vmSymbols::java_lang_LinkageError(), buf); + } +} + + +// Update system dictionary - done after check_constraint and add_to_hierachy +// have been called. +void SystemDictionary::update_dictionary(int d_index, unsigned int d_hash, + int p_index, unsigned int p_hash, + instanceKlassHandle k, + Handle class_loader, + TRAPS) { + // Compile_lock prevents systemDictionary updates during compilations + assert_locked_or_safepoint(Compile_lock); + symbolHandle name (THREAD, k->name()); + + { + MutexLocker mu1(SystemDictionary_lock, THREAD); + + // See whether biased locking is enabled and if so set it for this + // klass. + // Note that this must be done past the last potential blocking + // point / safepoint. We enable biased locking lazily using a + // VM_Operation to iterate the SystemDictionary and installing the + // biasable mark word into each instanceKlass's prototype header. + // To avoid race conditions where we accidentally miss enabling the + // optimization for one class in the process of being added to the + // dictionary, we must not safepoint after the test of + // BiasedLocking::enabled(). + if (UseBiasedLocking && BiasedLocking::enabled()) { + // Set biased locking bit for all loaded classes; it will be + // cleared if revocation occurs too often for this type + // NOTE that we must only do this when the class is initally + // defined, not each time it is referenced from a new class loader + if (k->class_loader() == class_loader()) { + k->set_prototype_header(markOopDesc::biased_locking_prototype()); + } + } + + // Check for a placeholder. If there, remove it and make a + // new system dictionary entry. + placeholders()->find_and_remove(p_index, p_hash, name, class_loader, THREAD); + klassOop sd_check = find_class(d_index, d_hash, name, class_loader); + if (sd_check == NULL) { + dictionary()->add_klass(name, class_loader, k); + notice_modification(); + } +#ifdef ASSERT + sd_check = find_class(d_index, d_hash, name, class_loader); + assert (sd_check != NULL, "should have entry in system dictionary"); +// Changed to allow PH to remain to complete class circularity checking +// while only one thread can define a class at one time, multiple +// classes can resolve the superclass for a class at one time, +// and the placeholder is used to track that +// symbolOop ph_check = find_placeholder(p_index, p_hash, name, class_loader); +// assert (ph_check == NULL, "should not have a placeholder entry"); +#endif + SystemDictionary_lock->notify_all(); + } +} + + +klassOop SystemDictionary::find_constrained_instance_or_array_klass( + symbolHandle class_name, Handle class_loader, TRAPS) { + + // First see if it has been loaded directly. + // Force the protection domain to be null. (This removes protection checks.) + Handle no_protection_domain; + klassOop klass = find_instance_or_array_klass(class_name, class_loader, + no_protection_domain, CHECK_NULL); + if (klass != NULL) + return klass; + + // Now look to see if it has been loaded elsewhere, and is subject to + // a loader constraint that would require this loader to return the + // klass that is already loaded. + if (FieldType::is_array(class_name())) { + // Array classes are hard because their klassOops are not kept in the + // constraint table. The array klass may be constrained, but the elem class + // may not be. + jint dimension; + symbolOop object_key; + BasicType t = FieldType::get_array_info(class_name(), &dimension, + &object_key, CHECK_(NULL)); + if (t != T_OBJECT) { + klass = Universe::typeArrayKlassObj(t); + } else { + symbolHandle elem_name(THREAD, object_key); + MutexLocker mu(SystemDictionary_lock, THREAD); + klass = constraints()->find_constrained_elem_klass(class_name, elem_name, class_loader, THREAD); + } + if (klass != NULL) { + klass = Klass::cast(klass)->array_klass_or_null(dimension); + } + } else { + MutexLocker mu(SystemDictionary_lock, THREAD); + // Non-array classes are easy: simply check the constraint table. + klass = constraints()->find_constrained_klass(class_name, class_loader); + } + + return klass; +} + + +bool SystemDictionary::add_loader_constraint(symbolHandle class_name, + Handle class_loader1, + Handle class_loader2, + Thread* THREAD) { + unsigned int d_hash1 = dictionary()->compute_hash(class_name, class_loader1); + int d_index1 = dictionary()->hash_to_index(d_hash1); + + unsigned int d_hash2 = dictionary()->compute_hash(class_name, class_loader2); + int d_index2 = dictionary()->hash_to_index(d_hash2); + + { + MutexLocker mu_s(SystemDictionary_lock, THREAD); + + // Better never do a GC while we're holding these oops + No_Safepoint_Verifier nosafepoint; + + klassOop klass1 = find_class(d_index1, d_hash1, class_name, class_loader1); + klassOop klass2 = find_class(d_index2, d_hash2, class_name, class_loader2); + return constraints()->add_entry(class_name, klass1, class_loader1, + klass2, class_loader2); + } +} + +// Add entry to resolution error table to record the error when the first +// attempt to resolve a reference to a class has failed. +void SystemDictionary::add_resolution_error(constantPoolHandle pool, int which, symbolHandle error) { + unsigned int hash = resolution_errors()->compute_hash(pool, which); + int index = resolution_errors()->hash_to_index(hash); + { + MutexLocker ml(SystemDictionary_lock, Thread::current()); + resolution_errors()->add_entry(index, hash, pool, which, error); + } +} + +// Lookup resolution error table. Returns error if found, otherwise NULL. +symbolOop SystemDictionary::find_resolution_error(constantPoolHandle pool, int which) { + unsigned int hash = resolution_errors()->compute_hash(pool, which); + int index = resolution_errors()->hash_to_index(hash); + { + MutexLocker ml(SystemDictionary_lock, Thread::current()); + ResolutionErrorEntry* entry = resolution_errors()->find_entry(index, hash, pool, which); + return (entry != NULL) ? entry->error() : (symbolOop)NULL; + } +} + + +// Make sure all class components (including arrays) in the given +// signature will be resolved to the same class in both loaders. +// Returns the name of the type that failed a loader constraint check, or +// NULL if no constraint failed. The returned C string needs cleaning up +// with a ResourceMark in the caller +char* SystemDictionary::check_signature_loaders(symbolHandle signature, + Handle loader1, Handle loader2, + bool is_method, TRAPS) { + // Nothing to do if loaders are the same. + if (loader1() == loader2()) { + return NULL; + } + + SignatureStream sig_strm(signature, is_method); + while (!sig_strm.is_done()) { + if (sig_strm.is_object()) { + symbolOop s = sig_strm.as_symbol(CHECK_NULL); + symbolHandle sig (THREAD, s); + if (!add_loader_constraint(sig, loader1, loader2, THREAD)) { + return sig()->as_C_string(); + } + } + sig_strm.next(); + } + return NULL; +} + + +// Since the identity hash code for symbols changes when the symbols are +// moved from the regular perm gen (hash in the mark word) to the shared +// spaces (hash is the address), the classes loaded into the dictionary +// may be in the wrong buckets. + +void SystemDictionary::reorder_dictionary() { + dictionary()->reorder_dictionary(); +} + + +void SystemDictionary::copy_buckets(char** top, char* end) { + dictionary()->copy_buckets(top, end); +} + + +void SystemDictionary::copy_table(char** top, char* end) { + dictionary()->copy_table(top, end); +} + + +void SystemDictionary::reverse() { + dictionary()->reverse(); +} + +int SystemDictionary::number_of_classes() { + return dictionary()->number_of_entries(); +} + + +// ---------------------------------------------------------------------------- +#ifndef PRODUCT + +void SystemDictionary::print() { + dictionary()->print(); + + // Placeholders + GCMutexLocker mu(SystemDictionary_lock); + placeholders()->print(); + + // loader constraints - print under SD_lock + constraints()->print(); +} + +#endif + +void SystemDictionary::verify() { + guarantee(dictionary() != NULL, "Verify of system dictionary failed"); + guarantee(constraints() != NULL, + "Verify of loader constraints failed"); + guarantee(dictionary()->number_of_entries() >= 0 && + placeholders()->number_of_entries() >= 0, + "Verify of system dictionary failed"); + + // Verify dictionary + dictionary()->verify(); + + GCMutexLocker mu(SystemDictionary_lock); + placeholders()->verify(); + + // Verify constraint table + guarantee(constraints() != NULL, "Verify of loader constraints failed"); + constraints()->verify(dictionary()); +} + + +void SystemDictionary::verify_obj_klass_present(Handle obj, + symbolHandle class_name, + Handle class_loader) { + GCMutexLocker mu(SystemDictionary_lock); + oop probe = find_class_or_placeholder(class_name, class_loader); + if (probe == NULL) { + probe = SystemDictionary::find_shared_class(class_name); + } + guarantee(probe != NULL && + (!probe->is_klass() || probe == obj()), + "Loaded klasses should be in SystemDictionary"); +} + +#ifndef PRODUCT + +// statistics code +class ClassStatistics: AllStatic { + private: + static int nclasses; // number of classes + static int nmethods; // number of methods + static int nmethoddata; // number of methodData + static int class_size; // size of class objects in words + static int method_size; // size of method objects in words + static int debug_size; // size of debug info in methods + static int methoddata_size; // size of methodData objects in words + + static void do_class(klassOop k) { + nclasses++; + class_size += k->size(); + if (k->klass_part()->oop_is_instance()) { + instanceKlass* ik = (instanceKlass*)k->klass_part(); + class_size += ik->methods()->size(); + class_size += ik->constants()->size(); + class_size += ik->local_interfaces()->size(); + class_size += ik->transitive_interfaces()->size(); + // We do not have to count implementors, since we only store one! + class_size += ik->fields()->size(); + } + } + + static void do_method(methodOop m) { + nmethods++; + method_size += m->size(); + // class loader uses same objArray for empty vectors, so don't count these + if (m->exception_table()->length() != 0) method_size += m->exception_table()->size(); + if (m->has_stackmap_table()) { + method_size += m->stackmap_data()->size(); + } + + methodDataOop mdo = m->method_data(); + if (mdo != NULL) { + nmethoddata++; + methoddata_size += mdo->size(); + } + } + + public: + static void print() { + SystemDictionary::classes_do(do_class); + SystemDictionary::methods_do(do_method); + tty->print_cr("Class statistics:"); + tty->print_cr("%d classes (%d bytes)", nclasses, class_size * oopSize); + tty->print_cr("%d methods (%d bytes = %d base + %d debug info)", nmethods, + (method_size + debug_size) * oopSize, method_size * oopSize, debug_size * oopSize); + tty->print_cr("%d methoddata (%d bytes)", nmethoddata, methoddata_size * oopSize); + } +}; + + +int ClassStatistics::nclasses = 0; +int ClassStatistics::nmethods = 0; +int ClassStatistics::nmethoddata = 0; +int ClassStatistics::class_size = 0; +int ClassStatistics::method_size = 0; +int ClassStatistics::debug_size = 0; +int ClassStatistics::methoddata_size = 0; + +void SystemDictionary::print_class_statistics() { + ResourceMark rm; + ClassStatistics::print(); +} + + +class MethodStatistics: AllStatic { + public: + enum { + max_parameter_size = 10 + }; + private: + + static int _number_of_methods; + static int _number_of_final_methods; + static int _number_of_static_methods; + static int _number_of_native_methods; + static int _number_of_synchronized_methods; + static int _number_of_profiled_methods; + static int _number_of_bytecodes; + static int _parameter_size_profile[max_parameter_size]; + static int _bytecodes_profile[Bytecodes::number_of_java_codes]; + + static void initialize() { + _number_of_methods = 0; + _number_of_final_methods = 0; + _number_of_static_methods = 0; + _number_of_native_methods = 0; + _number_of_synchronized_methods = 0; + _number_of_profiled_methods = 0; + _number_of_bytecodes = 0; + for (int i = 0; i < max_parameter_size ; i++) _parameter_size_profile[i] = 0; + for (int j = 0; j < Bytecodes::number_of_java_codes; j++) _bytecodes_profile [j] = 0; + }; + + static void do_method(methodOop m) { + _number_of_methods++; + // collect flag info + if (m->is_final() ) _number_of_final_methods++; + if (m->is_static() ) _number_of_static_methods++; + if (m->is_native() ) _number_of_native_methods++; + if (m->is_synchronized()) _number_of_synchronized_methods++; + if (m->method_data() != NULL) _number_of_profiled_methods++; + // collect parameter size info (add one for receiver, if any) + _parameter_size_profile[MIN2(m->size_of_parameters() + (m->is_static() ? 0 : 1), max_parameter_size - 1)]++; + // collect bytecodes info + { + Thread *thread = Thread::current(); + HandleMark hm(thread); + BytecodeStream s(methodHandle(thread, m)); + Bytecodes::Code c; + while ((c = s.next()) >= 0) { + _number_of_bytecodes++; + _bytecodes_profile[c]++; + } + } + } + + public: + static void print() { + initialize(); + SystemDictionary::methods_do(do_method); + // generate output + tty->cr(); + tty->print_cr("Method statistics (static):"); + // flag distribution + tty->cr(); + tty->print_cr("%6d final methods %6.1f%%", _number_of_final_methods , _number_of_final_methods * 100.0F / _number_of_methods); + tty->print_cr("%6d static methods %6.1f%%", _number_of_static_methods , _number_of_static_methods * 100.0F / _number_of_methods); + tty->print_cr("%6d native methods %6.1f%%", _number_of_native_methods , _number_of_native_methods * 100.0F / _number_of_methods); + tty->print_cr("%6d synchronized methods %6.1f%%", _number_of_synchronized_methods, _number_of_synchronized_methods * 100.0F / _number_of_methods); + tty->print_cr("%6d profiled methods %6.1f%%", _number_of_profiled_methods, _number_of_profiled_methods * 100.0F / _number_of_methods); + // parameter size profile + tty->cr(); + { int tot = 0; + int avg = 0; + for (int i = 0; i < max_parameter_size; i++) { + int n = _parameter_size_profile[i]; + tot += n; + avg += n*i; + tty->print_cr("parameter size = %1d: %6d methods %5.1f%%", i, n, n * 100.0F / _number_of_methods); + } + assert(tot == _number_of_methods, "should be the same"); + tty->print_cr(" %6d methods 100.0%%", _number_of_methods); + tty->print_cr("(average parameter size = %3.1f including receiver, if any)", (float)avg / _number_of_methods); + } + // bytecodes profile + tty->cr(); + { int tot = 0; + for (int i = 0; i < Bytecodes::number_of_java_codes; i++) { + if (Bytecodes::is_defined(i)) { + Bytecodes::Code c = Bytecodes::cast(i); + int n = _bytecodes_profile[c]; + tot += n; + tty->print_cr("%9d %7.3f%% %s", n, n * 100.0F / _number_of_bytecodes, Bytecodes::name(c)); + } + } + assert(tot == _number_of_bytecodes, "should be the same"); + tty->print_cr("%9d 100.000%%", _number_of_bytecodes); + } + tty->cr(); + } +}; + +int MethodStatistics::_number_of_methods; +int MethodStatistics::_number_of_final_methods; +int MethodStatistics::_number_of_static_methods; +int MethodStatistics::_number_of_native_methods; +int MethodStatistics::_number_of_synchronized_methods; +int MethodStatistics::_number_of_profiled_methods; +int MethodStatistics::_number_of_bytecodes; +int MethodStatistics::_parameter_size_profile[MethodStatistics::max_parameter_size]; +int MethodStatistics::_bytecodes_profile[Bytecodes::number_of_java_codes]; + + +void SystemDictionary::print_method_statistics() { + MethodStatistics::print(); +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp new file mode 100644 index 00000000000..519dac9a95a --- /dev/null +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp @@ -0,0 +1,581 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The system dictionary stores all loaded classes and maps: +// +// [class name,class loader] -> class i.e. [symbolOop,oop] -> klassOop +// +// Classes are loaded lazily. The default VM class loader is +// represented as NULL. + +// The underlying data structure is an open hash table with a fixed number +// of buckets. During loading the loader object is locked, (for the VM loader +// a private lock object is used). Class loading can thus be done concurrently, +// but only by different loaders. +// +// During loading a placeholder (name, loader) is temporarily placed in +// a side data structure, and is used to detect ClassCircularityErrors +// and to perform verification during GC. A GC can occur in the midst +// of class loading, as we call out to Java, have to take locks, etc. +// +// When class loading is finished, a new entry is added to the system +// dictionary and the place holder is removed. Note that the protection +// domain field of the system dictionary has not yet been filled in when +// the "real" system dictionary entry is created. +// +// Clients of this class who are interested in finding if a class has +// been completely loaded -- not classes in the process of being loaded -- +// can read the SystemDictionary unlocked. This is safe because +// - entries are only deleted at safepoints +// - readers cannot come to a safepoint while actively examining +// an entry (an entry cannot be deleted from under a reader) +// - entries must be fully formed before they are available to concurrent +// readers (we must ensure write ordering) +// +// Note that placeholders are deleted at any time, as they are removed +// when a class is completely loaded. Therefore, readers as well as writers +// of placeholders must hold the SystemDictionary_lock. +// + +class Dictionary; +class PlaceholderTable; +class LoaderConstraintTable; +class HashtableBucket; +class ResolutionErrorTable; + +class SystemDictionary : AllStatic { + friend class VMStructs; + friend class CompactingPermGenGen; + NOT_PRODUCT(friend class instanceKlassKlass;) + + public: + // Returns a class with a given class name and class loader. Loads the + // class if needed. If not found a NoClassDefFoundError or a + // ClassNotFoundException is thrown, depending on the value on the + // throw_error flag. For most uses the throw_error argument should be set + // to true. + + static klassOop resolve_or_fail(symbolHandle class_name, Handle class_loader, Handle protection_domain, bool throw_error, TRAPS); + // Convenient call for null loader and protection domain. + static klassOop resolve_or_fail(symbolHandle class_name, bool throw_error, TRAPS); +private: + // handle error translation for resolve_or_null results + static klassOop handle_resolution_exception(symbolHandle class_name, Handle class_loader, Handle protection_domain, bool throw_error, KlassHandle klass_h, TRAPS); + +public: + + // Returns a class with a given class name and class loader. + // Loads the class if needed. If not found NULL is returned. + static klassOop resolve_or_null(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS); + // Version with null loader and protection domain + static klassOop resolve_or_null(symbolHandle class_name, TRAPS); + + // Resolve a superclass or superinterface. Called from ClassFileParser, + // parse_interfaces, resolve_instance_class_or_null, load_shared_class + // "child_name" is the class whose super class or interface is being resolved. + static klassOop resolve_super_or_fail(symbolHandle child_name, + symbolHandle class_name, + Handle class_loader, + Handle protection_domain, + bool is_superclass, + TRAPS); + + // Parse new stream. This won't update the system dictionary or + // class hierarchy, simply parse the stream. Used by JVMTI RedefineClasses. + static klassOop parse_stream(symbolHandle class_name, + Handle class_loader, + Handle protection_domain, + ClassFileStream* st, + TRAPS); + + // Resolve from stream (called by jni_DefineClass and JVM_DefineClass) + static klassOop resolve_from_stream(symbolHandle class_name, Handle class_loader, Handle protection_domain, ClassFileStream* st, TRAPS); + + // Lookup an already loaded class. If not found NULL is returned. + static klassOop find(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS); + + // Lookup an already loaded instance or array class. + // Do not make any queries to class loaders; consult only the cache. + // If not found NULL is returned. + static klassOop find_instance_or_array_klass(symbolHandle class_name, + Handle class_loader, + Handle protection_domain, + TRAPS); + + // Lookup an instance or array class that has already been loaded + // either into the given class loader, or else into another class + // loader that is constrained (via loader constraints) to produce + // a consistent class. Do not take protection domains into account. + // Do not make any queries to class loaders; consult only the cache. + // Return NULL if the class is not found. + // + // This function is a strict superset of find_instance_or_array_klass. + // This function (the unchecked version) makes a conservative prediction + // of the result of the checked version, assuming successful lookup. + // If both functions return non-null, they must return the same value. + // Also, the unchecked version may sometimes be non-null where the + // checked version is null. This can occur in several ways: + // 1. No query has yet been made to the class loader. + // 2. The class loader was queried, but chose not to delegate. + // 3. ClassLoader.checkPackageAccess rejected a proposed protection domain. + // 4. Loading was attempted, but there was a linkage error of some sort. + // In all of these cases, the loader constraints on this type are + // satisfied, and it is safe for classes in the given class loader + // to manipulate strongly-typed values of the found class, subject + // to local linkage and access checks. + static klassOop find_constrained_instance_or_array_klass(symbolHandle class_name, + Handle class_loader, + TRAPS); + + // Iterate over all klasses in dictionary + // Just the classes from defining class loaders + static void classes_do(void f(klassOop)); + // Added for initialize_itable_for_klass to handle exceptions + static void classes_do(void f(klassOop, TRAPS), TRAPS); + // All classes, and their class loaders + static void classes_do(void f(klassOop, oop)); + // All classes, and their class loaders + // (added for helpers that use HandleMarks and ResourceMarks) + static void classes_do(void f(klassOop, oop, TRAPS), TRAPS); + // All entries in the placeholder table and their class loaders + static void placeholders_do(void f(symbolOop, oop)); + + // Iterate over all methods in all klasses in dictionary + static void methods_do(void f(methodOop)); + + // Garbage collection support + + // This method applies "blk->do_oop" to all the pointers to "system" + // classes and loaders. + static void always_strong_oops_do(OopClosure* blk); + static void always_strong_classes_do(OopClosure* blk); + // This method applies "blk->do_oop" to all the placeholders. + static void placeholders_do(OopClosure* blk); + + // Unload (that is, break root links to) all unmarked classes and + // loaders. Returns "true" iff something was unloaded. + static bool do_unloading(BoolObjectClosure* is_alive); + + // Applies "f->do_oop" to all root oops in the system dictionary. + static void oops_do(OopClosure* f); + + // System loader lock + static oop system_loader_lock() { return _system_loader_lock_obj; } + +private: + // Traverses preloaded oops: various system classes. These are + // guaranteed to be in the perm gen. + static void preloaded_oops_do(OopClosure* f); + static void lazily_loaded_oops_do(OopClosure* f); + +public: + // Sharing support. + static void reorder_dictionary(); + static void copy_buckets(char** top, char* end); + static void copy_table(char** top, char* end); + static void reverse(); + static void set_shared_dictionary(HashtableBucket* t, int length, + int number_of_entries); + // Printing + static void print() PRODUCT_RETURN; + static void print_class_statistics() PRODUCT_RETURN; + static void print_method_statistics() PRODUCT_RETURN; + + // Number of contained klasses + // This is both fully loaded classes and classes in the process + // of being loaded + static int number_of_classes(); + + // Monotonically increasing counter which grows as classes are + // loaded or modifications such as hot-swapping or setting/removing + // of breakpoints are performed + static inline int number_of_modifications() { assert_locked_or_safepoint(Compile_lock); return _number_of_modifications; } + // Needed by evolution and breakpoint code + static inline void notice_modification() { assert_locked_or_safepoint(Compile_lock); ++_number_of_modifications; } + + // Verification + static void verify(); + +#ifdef ASSERT + static bool is_internal_format(symbolHandle class_name); +#endif + + // Verify class is in dictionary + static void verify_obj_klass_present(Handle obj, + symbolHandle class_name, + Handle class_loader); + + // Initialization + static void initialize(TRAPS); + + // Fast access to commonly used classes (preloaded) + static klassOop check_klass(klassOop k) { + assert(k != NULL, "preloaded klass not initialized"); + return k; + } + +public: + static klassOop object_klass() { return check_klass(_object_klass); } + static klassOop string_klass() { return check_klass(_string_klass); } + static klassOop class_klass() { return check_klass(_class_klass); } + static klassOop cloneable_klass() { return check_klass(_cloneable_klass); } + static klassOop classloader_klass() { return check_klass(_classloader_klass); } + static klassOop serializable_klass() { return check_klass(_serializable_klass); } + static klassOop system_klass() { return check_klass(_system_klass); } + + static klassOop throwable_klass() { return check_klass(_throwable_klass); } + static klassOop error_klass() { return check_klass(_error_klass); } + static klassOop threaddeath_klass() { return check_klass(_threaddeath_klass); } + static klassOop exception_klass() { return check_klass(_exception_klass); } + static klassOop runtime_exception_klass() { return check_klass(_runtime_exception_klass); } + static klassOop classNotFoundException_klass() { return check_klass(_classNotFoundException_klass); } + static klassOop noClassDefFoundError_klass() { return check_klass(_noClassDefFoundError_klass); } + static klassOop linkageError_klass() { return check_klass(_linkageError_klass); } + static klassOop ClassCastException_klass() { return check_klass(_classCastException_klass); } + static klassOop ArrayStoreException_klass() { return check_klass(_arrayStoreException_klass); } + static klassOop virtualMachineError_klass() { return check_klass(_virtualMachineError_klass); } + static klassOop OutOfMemoryError_klass() { return check_klass(_outOfMemoryError_klass); } + static klassOop StackOverflowError_klass() { return check_klass(_StackOverflowError_klass); } + static klassOop IllegalMonitorStateException_klass() { return check_klass(_illegalMonitorStateException_klass); } + static klassOop protectionDomain_klass() { return check_klass(_protectionDomain_klass); } + static klassOop AccessControlContext_klass() { return check_klass(_AccessControlContext_klass); } + static klassOop reference_klass() { return check_klass(_reference_klass); } + static klassOop soft_reference_klass() { return check_klass(_soft_reference_klass); } + static klassOop weak_reference_klass() { return check_klass(_weak_reference_klass); } + static klassOop final_reference_klass() { return check_klass(_final_reference_klass); } + static klassOop phantom_reference_klass() { return check_klass(_phantom_reference_klass); } + static klassOop finalizer_klass() { return check_klass(_finalizer_klass); } + + static klassOop thread_klass() { return check_klass(_thread_klass); } + static klassOop threadGroup_klass() { return check_klass(_threadGroup_klass); } + static klassOop properties_klass() { return check_klass(_properties_klass); } + static klassOop reflect_accessible_object_klass() { return check_klass(_reflect_accessible_object_klass); } + static klassOop reflect_field_klass() { return check_klass(_reflect_field_klass); } + static klassOop reflect_method_klass() { return check_klass(_reflect_method_klass); } + static klassOop reflect_constructor_klass() { return check_klass(_reflect_constructor_klass); } + static klassOop reflect_method_accessor_klass() { + assert(JDK_Version::is_gte_jdk14x_version() && UseNewReflection, "JDK 1.4 only"); + return check_klass(_reflect_method_accessor_klass); + } + static klassOop reflect_constructor_accessor_klass() { + assert(JDK_Version::is_gte_jdk14x_version() && UseNewReflection, "JDK 1.4 only"); + return check_klass(_reflect_constructor_accessor_klass); + } + // NOTE: needed too early in bootstrapping process to have checks based on JDK version + static klassOop reflect_magic_klass() { return _reflect_magic_klass; } + static klassOop reflect_delegating_classloader_klass() { return _reflect_delegating_classloader_klass; } + static klassOop reflect_constant_pool_klass() { + assert(JDK_Version::is_gte_jdk15x_version(), "JDK 1.5 only"); + return _reflect_constant_pool_klass; + } + static klassOop reflect_unsafe_static_field_accessor_impl_klass() { + assert(JDK_Version::is_gte_jdk15x_version(), "JDK 1.5 only"); + return _reflect_unsafe_static_field_accessor_impl_klass; + } + + static klassOop vector_klass() { return check_klass(_vector_klass); } + static klassOop hashtable_klass() { return check_klass(_hashtable_klass); } + static klassOop stringBuffer_klass() { return check_klass(_stringBuffer_klass); } + static klassOop stackTraceElement_klass() { return check_klass(_stackTraceElement_klass); } + + static klassOop java_nio_Buffer_klass() { return check_klass(_java_nio_Buffer_klass); } + + static klassOop sun_misc_AtomicLongCSImpl_klass() { return _sun_misc_AtomicLongCSImpl_klass; } + + // To support incremental JRE downloads (KERNEL JRE). Null if not present. + static klassOop sun_jkernel_DownloadManager_klass() { return _sun_jkernel_DownloadManager_klass; } + + static klassOop boolean_klass() { return check_klass(_boolean_klass); } + static klassOop char_klass() { return check_klass(_char_klass); } + static klassOop float_klass() { return check_klass(_float_klass); } + static klassOop double_klass() { return check_klass(_double_klass); } + static klassOop byte_klass() { return check_klass(_byte_klass); } + static klassOop short_klass() { return check_klass(_short_klass); } + static klassOop int_klass() { return check_klass(_int_klass); } + static klassOop long_klass() { return check_klass(_long_klass); } + + static klassOop box_klass(BasicType t) { + assert((uint)t < T_VOID+1, "range check"); + return check_klass(_box_klasses[t]); + } + static BasicType box_klass_type(klassOop k); // inverse of box_klass + + // methods returning lazily loaded klasses + // The corresponding method to load the class must be called before calling them. + static klassOop abstract_ownable_synchronizer_klass() { return check_klass(_abstract_ownable_synchronizer_klass); } + + static void load_abstract_ownable_synchronizer_klass(TRAPS); + +private: + // Tells whether ClassLoader.loadClassInternal is present + static bool has_loadClassInternal() { return _has_loadClassInternal; } + +public: + // Tells whether ClassLoader.checkPackageAccess is present + static bool has_checkPackageAccess() { return _has_checkPackageAccess; } + + static bool class_klass_loaded() { return _class_klass != NULL; } + static bool cloneable_klass_loaded() { return _cloneable_klass != NULL; } + + // Returns default system loader + static oop java_system_loader(); + + // Compute the default system loader + static void compute_java_system_loader(TRAPS); + +private: + // Mirrors for primitive classes (created eagerly) + static oop check_mirror(oop m) { + assert(m != NULL, "mirror not initialized"); + return m; + } + +public: + // Note: java_lang_Class::primitive_type is the inverse of java_mirror + + // Check class loader constraints + static bool add_loader_constraint(symbolHandle name, Handle loader1, + Handle loader2, TRAPS); + static char* check_signature_loaders(symbolHandle signature, Handle loader1, + Handle loader2, bool is_method, TRAPS); + + // Utility for printing loader "name" as part of tracing constraints + static const char* loader_name(oop loader) { + return ((loader) == NULL ? "" : + instanceKlass::cast((loader)->klass())->name()->as_C_string() ); + } + + // Record the error when the first attempt to resolve a reference from a constant + // pool entry to a class fails. + static void add_resolution_error(constantPoolHandle pool, int which, symbolHandle error); + static symbolOop find_resolution_error(constantPoolHandle pool, int which); + + private: + + enum Constants { + _loader_constraint_size = 107, // number of entries in constraint table + _resolution_error_size = 107, // number of entries in resolution error table + _nof_buckets = 1009 // number of buckets in hash table + }; + + + // Static variables + + // Hashtable holding loaded classes. + static Dictionary* _dictionary; + + // Hashtable holding placeholders for classes being loaded. + static PlaceholderTable* _placeholders; + + // Hashtable holding classes from the shared archive. + static Dictionary* _shared_dictionary; + + // Monotonically increasing counter which grows with + // _number_of_classes as well as hot-swapping and breakpoint setting + // and removal. + static int _number_of_modifications; + + // Lock object for system class loader + static oop _system_loader_lock_obj; + + // Constraints on class loaders + static LoaderConstraintTable* _loader_constraints; + + // Resolution errors + static ResolutionErrorTable* _resolution_errors; + +public: + // for VM_CounterDecay iteration support + friend class CounterDecay; + static klassOop try_get_next_class(); + +private: + static void validate_protection_domain(instanceKlassHandle klass, + Handle class_loader, + Handle protection_domain, TRAPS); + + friend class VM_PopulateDumpSharedSpace; + friend class TraversePlaceholdersClosure; + static Dictionary* dictionary() { return _dictionary; } + static Dictionary* shared_dictionary() { return _shared_dictionary; } + static PlaceholderTable* placeholders() { return _placeholders; } + static LoaderConstraintTable* constraints() { return _loader_constraints; } + static ResolutionErrorTable* resolution_errors() { return _resolution_errors; } + + // Basic loading operations + static klassOop resolve_instance_class_or_null(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS); + static klassOop resolve_array_class_or_null(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS); + static instanceKlassHandle handle_parallel_super_load(symbolHandle class_name, symbolHandle supername, Handle class_loader, Handle protection_domain, Handle lockObject, TRAPS); + // Wait on SystemDictionary_lock; unlocks lockObject before + // waiting; relocks lockObject with correct recursion count + // after waiting, but before reentering SystemDictionary_lock + // to preserve lock order semantics. + static void double_lock_wait(Handle lockObject, TRAPS); + static void define_instance_class(instanceKlassHandle k, TRAPS); + static instanceKlassHandle find_or_define_instance_class(symbolHandle class_name, + Handle class_loader, + instanceKlassHandle k, TRAPS); + static instanceKlassHandle load_shared_class(symbolHandle class_name, + Handle class_loader, TRAPS); + static instanceKlassHandle load_shared_class(instanceKlassHandle ik, + Handle class_loader, TRAPS); + static instanceKlassHandle load_instance_class(symbolHandle class_name, Handle class_loader, TRAPS); + static Handle compute_loader_lock_object(Handle class_loader, TRAPS); + static void check_loader_lock_contention(Handle loader_lock, TRAPS); + + static klassOop find_shared_class(symbolHandle class_name); + + // Setup link to hierarchy + static void add_to_hierarchy(instanceKlassHandle k, TRAPS); + +private: + // We pass in the hashtable index so we can calculate it outside of + // the SystemDictionary_lock. + + // Basic find on loaded classes + static klassOop find_class(int index, unsigned int hash, + symbolHandle name, Handle loader); + + // Basic find on classes in the midst of being loaded + static symbolOop find_placeholder(int index, unsigned int hash, + symbolHandle name, Handle loader); + + // Basic find operation of loaded classes and classes in the midst + // of loading; used for assertions and verification only. + static oop find_class_or_placeholder(symbolHandle class_name, + Handle class_loader); + + // Updating entry in dictionary + // Add a completely loaded class + static void add_klass(int index, symbolHandle class_name, + Handle class_loader, KlassHandle obj); + + // Add a placeholder for a class being loaded + static void add_placeholder(int index, + symbolHandle class_name, + Handle class_loader); + static void remove_placeholder(int index, + symbolHandle class_name, + Handle class_loader); + + // Performs cleanups after resolve_super_or_fail. This typically needs + // to be called on failure. + // Won't throw, but can block. + static void resolution_cleanups(symbolHandle class_name, + Handle class_loader, + TRAPS); + + // Initialization + static void initialize_preloaded_classes(TRAPS); + + // Class loader constraints + static void check_constraints(int index, unsigned int hash, + instanceKlassHandle k, Handle loader, + bool defining, TRAPS); + static void update_dictionary(int d_index, unsigned int d_hash, + int p_index, unsigned int p_hash, + instanceKlassHandle k, Handle loader, TRAPS); + + // Variables holding commonly used klasses (preloaded) + static klassOop _object_klass; + static klassOop _string_klass; + static klassOop _class_klass; + static klassOop _cloneable_klass; + static klassOop _classloader_klass; + static klassOop _serializable_klass; + static klassOop _system_klass; + + static klassOop _throwable_klass; + static klassOop _error_klass; + static klassOop _threaddeath_klass; + static klassOop _exception_klass; + static klassOop _runtime_exception_klass; + static klassOop _classNotFoundException_klass; + static klassOop _noClassDefFoundError_klass; + static klassOop _linkageError_klass; + static klassOop _classCastException_klass; + static klassOop _arrayStoreException_klass; + static klassOop _virtualMachineError_klass; + static klassOop _outOfMemoryError_klass; + static klassOop _StackOverflowError_klass; + static klassOop _illegalMonitorStateException_klass; + static klassOop _protectionDomain_klass; + static klassOop _AccessControlContext_klass; + static klassOop _reference_klass; + static klassOop _soft_reference_klass; + static klassOop _weak_reference_klass; + static klassOop _final_reference_klass; + static klassOop _phantom_reference_klass; + static klassOop _finalizer_klass; + + static klassOop _thread_klass; + static klassOop _threadGroup_klass; + static klassOop _properties_klass; + static klassOop _reflect_accessible_object_klass; + static klassOop _reflect_field_klass; + static klassOop _reflect_method_klass; + static klassOop _reflect_constructor_klass; + // 1.4 reflection implementation + static klassOop _reflect_magic_klass; + static klassOop _reflect_method_accessor_klass; + static klassOop _reflect_constructor_accessor_klass; + static klassOop _reflect_delegating_classloader_klass; + // 1.5 annotations implementation + static klassOop _reflect_constant_pool_klass; + static klassOop _reflect_unsafe_static_field_accessor_impl_klass; + + static klassOop _stringBuffer_klass; + static klassOop _vector_klass; + static klassOop _hashtable_klass; + + static klassOop _stackTraceElement_klass; + + static klassOop _java_nio_Buffer_klass; + + static klassOop _sun_misc_AtomicLongCSImpl_klass; + + // KERNEL JRE support. + static klassOop _sun_jkernel_DownloadManager_klass; + + // Lazily loaded klasses + static volatile klassOop _abstract_ownable_synchronizer_klass; + + // Box klasses + static klassOop _boolean_klass; + static klassOop _char_klass; + static klassOop _float_klass; + static klassOop _double_klass; + static klassOop _byte_klass; + static klassOop _short_klass; + static klassOop _int_klass; + static klassOop _long_klass; + + // table of same + static klassOop _box_klasses[T_VOID+1]; + + static oop _java_system_loader; + + static bool _has_loadClassInternal; + static bool _has_checkPackageAccess; +}; diff --git a/hotspot/src/share/vm/classfile/verificationType.cpp b/hotspot/src/share/vm/classfile/verificationType.cpp new file mode 100644 index 00000000000..a531b5a54cb --- /dev/null +++ b/hotspot/src/share/vm/classfile/verificationType.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_verificationType.cpp.incl" + +VerificationType VerificationType::from_tag(u1 tag) { + switch (tag) { + case ITEM_Top: return bogus_type(); + case ITEM_Integer: return integer_type(); + case ITEM_Float: return float_type(); + case ITEM_Double: return double_type(); + case ITEM_Long: return long_type(); + case ITEM_Null: return null_type(); + default: + ShouldNotReachHere(); + return bogus_type(); + } +} + +bool VerificationType::is_reference_assignable_from( + const VerificationType& from, instanceKlassHandle context, TRAPS) const { + if (from.is_null()) { + // null is assignable to any reference + return true; + } else if (is_null()) { + return false; + } else if (name() == from.name()) { + return true; + } else if (is_object()) { + // We need check the class hierarchy to check assignability + if (name() == vmSymbols::java_lang_Object()) { + // any object or array is assignable to java.lang.Object + return true; + } + klassOop this_class = SystemDictionary::resolve_or_fail( + name_handle(), Handle(THREAD, context->class_loader()), + Handle(THREAD, context->protection_domain()), true, CHECK_false); + if (this_class->klass_part()->is_interface()) { + // We treat interfaces as java.lang.Object, including + // java.lang.Cloneable and java.io.Serializable + return true; + } else if (from.is_object()) { + klassOop from_class = SystemDictionary::resolve_or_fail( + from.name_handle(), Handle(THREAD, context->class_loader()), + Handle(THREAD, context->protection_domain()), true, CHECK_false); + return instanceKlass::cast(from_class)->is_subclass_of(this_class); + } + } else if (is_array() && from.is_array()) { + VerificationType comp_this = get_component(CHECK_false); + VerificationType comp_from = from.get_component(CHECK_false); + return comp_this.is_assignable_from(comp_from, context, CHECK_false); + } + return false; +} + +VerificationType VerificationType::get_component(TRAPS) const { + assert(is_array() && name()->utf8_length() >= 2, "Must be a valid array"); + symbolOop component; + switch (name()->byte_at(1)) { + case 'Z': return VerificationType(Boolean); + case 'B': return VerificationType(Byte); + case 'C': return VerificationType(Char); + case 'S': return VerificationType(Short); + case 'I': return VerificationType(Integer); + case 'J': return VerificationType(Long); + case 'F': return VerificationType(Float); + case 'D': return VerificationType(Double); + case '[': + component = SymbolTable::lookup( + name(), 1, name()->utf8_length(), + CHECK_(VerificationType::bogus_type())); + return VerificationType::reference_type(component); + case 'L': + component = SymbolTable::lookup( + name(), 2, name()->utf8_length() - 1, + CHECK_(VerificationType::bogus_type())); + return VerificationType::reference_type(component); + default: + ShouldNotReachHere(); + return VerificationType::bogus_type(); + } +} + +#ifndef PRODUCT + +void VerificationType::print_on(outputStream* st) const { + switch (_u._data) { + case Bogus: st->print(" bogus "); break; + case Category1: st->print(" category1 "); break; + case Category2: st->print(" category2 "); break; + case Category2_2nd: st->print(" category2_2nd "); break; + case Boolean: st->print(" boolean "); break; + case Byte: st->print(" byte "); break; + case Short: st->print(" short "); break; + case Char: st->print(" char "); break; + case Integer: st->print(" integer "); break; + case Float: st->print(" float "); break; + case Long: st->print(" long "); break; + case Double: st->print(" double "); break; + case Long_2nd: st->print(" long_2nd "); break; + case Double_2nd: st->print(" double_2nd "); break; + case Null: st->print(" null "); break; + default: + if (is_uninitialized_this()) { + st->print(" uninitializedThis "); + } else if (is_uninitialized()) { + st->print(" uninitialized %d ", bci()); + } else { + st->print(" class %s ", name()->as_klass_external_name()); + } + } +} + +#endif diff --git a/hotspot/src/share/vm/classfile/verificationType.hpp b/hotspot/src/share/vm/classfile/verificationType.hpp new file mode 100644 index 00000000000..b379e992505 --- /dev/null +++ b/hotspot/src/share/vm/classfile/verificationType.hpp @@ -0,0 +1,305 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +enum { + // As specifed in the JVM spec + ITEM_Top = 0, + ITEM_Integer = 1, + ITEM_Float = 2, + ITEM_Double = 3, + ITEM_Long = 4, + ITEM_Null = 5, + ITEM_UninitializedThis = 6, + ITEM_Object = 7, + ITEM_Uninitialized = 8, + ITEM_Bogus = (uint)-1 +}; + +class VerificationType VALUE_OBJ_CLASS_SPEC { + private: + // Least significant bits of _handle are always 0, so we use these as + // the indicator that the _handle is valid. Otherwise, the _data field + // contains encoded data (as specified below). Should the VM change + // and the lower bits on oops aren't 0, the assert in the constructor + // will catch this and we'll have to add a descriminator tag to this + // structure. + union { + symbolOop* _handle; + uintptr_t _data; + } _u; + + enum { + // These rest are not found in classfiles, but used by the verifier + ITEM_Boolean = 9, ITEM_Byte, ITEM_Short, ITEM_Char, + ITEM_Long_2nd, ITEM_Double_2nd + }; + + // Enum for the _data field + enum { + // Bottom two bits determine if the type is a reference, primitive, + // uninitialized or a query-type. + TypeMask = 0x00000003, + + // Topmost types encoding + Reference = 0x0, // _handle contains the name + Primitive = 0x1, // see below for primitive list + Uninitialized = 0x2, // 0x00ffff00 contains bci + TypeQuery = 0x3, // Meta-types used for category testing + + // Utility flags + ReferenceFlag = 0x00, // For reference query types + Category1Flag = 0x01, // One-word values + Category2Flag = 0x02, // First word of a two-word value + Category2_2ndFlag = 0x04, // Second word of a two-word value + + // special reference values + Null = 0x00000000, // A reference with a 0 handle is null + + // Primitives categories (the second byte determines the category) + Category1 = (Category1Flag << 1 * BitsPerByte) | Primitive, + Category2 = (Category2Flag << 1 * BitsPerByte) | Primitive, + Category2_2nd = (Category2_2ndFlag << 1 * BitsPerByte) | Primitive, + + // Primitive values (type descriminator stored in most-signifcant bytes) + Bogus = (ITEM_Bogus << 2 * BitsPerByte) | Category1, + Boolean = (ITEM_Boolean << 2 * BitsPerByte) | Category1, + Byte = (ITEM_Byte << 2 * BitsPerByte) | Category1, + Short = (ITEM_Short << 2 * BitsPerByte) | Category1, + Char = (ITEM_Char << 2 * BitsPerByte) | Category1, + Integer = (ITEM_Integer << 2 * BitsPerByte) | Category1, + Float = (ITEM_Float << 2 * BitsPerByte) | Category1, + Long = (ITEM_Long << 2 * BitsPerByte) | Category2, + Double = (ITEM_Double << 2 * BitsPerByte) | Category2, + Long_2nd = (ITEM_Long_2nd << 2 * BitsPerByte) | Category2_2nd, + Double_2nd = (ITEM_Double_2nd << 2 * BitsPerByte) | Category2_2nd, + + // Used by Uninitialized (second and third bytes hold the bci) + BciMask = 0xffff << 1 * BitsPerByte, + BciForThis = ((u2)-1), // A bci of -1 is an Unintialized-This + + // Query values + ReferenceQuery = (ReferenceFlag << 1 * BitsPerByte) | TypeQuery, + Category1Query = (Category1Flag << 1 * BitsPerByte) | TypeQuery, + Category2Query = (Category2Flag << 1 * BitsPerByte) | TypeQuery, + Category2_2ndQuery = (Category2_2ndFlag << 1 * BitsPerByte) | TypeQuery + }; + + VerificationType(uintptr_t raw_data) { + _u._data = raw_data; + } + + public: + + VerificationType() { *this = bogus_type(); } + + // Create verification types + static VerificationType bogus_type() { return VerificationType(Bogus); } + static VerificationType null_type() { return VerificationType(Null); } + static VerificationType integer_type() { return VerificationType(Integer); } + static VerificationType float_type() { return VerificationType(Float); } + static VerificationType long_type() { return VerificationType(Long); } + static VerificationType long2_type() { return VerificationType(Long_2nd); } + static VerificationType double_type() { return VerificationType(Double); } + static VerificationType boolean_type() { return VerificationType(Boolean); } + static VerificationType byte_type() { return VerificationType(Byte); } + static VerificationType char_type() { return VerificationType(Char); } + static VerificationType short_type() { return VerificationType(Short); } + static VerificationType double2_type() + { return VerificationType(Double_2nd); } + + // "check" types are used for queries. A "check" type is not assignable + // to anything, but the specified types are assignable to a "check". For + // example, any category1 primitive is assignable to category1_check and + // any reference is assignable to reference_check. + static VerificationType reference_check() + { return VerificationType(ReferenceQuery); } + static VerificationType category1_check() + { return VerificationType(Category1Query); } + static VerificationType category2_check() + { return VerificationType(Category2Query); } + static VerificationType category2_2nd_check() + { return VerificationType(Category2_2ndQuery); } + + // For reference types, store the actual oop* handle + static VerificationType reference_type(symbolHandle sh) { + assert(((uintptr_t)sh.raw_value() & 0x3) == 0, "Oops must be aligned"); + // If the above assert fails in the future because oop* isn't aligned, + // then this type encoding system will have to change to have a tag value + // to descriminate between oops and primitives. + return VerificationType((uintptr_t)((symbolOop*)sh.raw_value())); + } + static VerificationType reference_type(symbolOop s, TRAPS) + { return reference_type(symbolHandle(THREAD, s)); } + + static VerificationType uninitialized_type(u2 bci) + { return VerificationType(bci << 1 * BitsPerByte | Uninitialized); } + static VerificationType uninitialized_this_type() + { return uninitialized_type(BciForThis); } + + // Create based on u1 read from classfile + static VerificationType from_tag(u1 tag); + + bool is_bogus() const { return (_u._data == Bogus); } + bool is_null() const { return (_u._data == Null); } + bool is_boolean() const { return (_u._data == Boolean); } + bool is_byte() const { return (_u._data == Byte); } + bool is_char() const { return (_u._data == Char); } + bool is_short() const { return (_u._data == Short); } + bool is_integer() const { return (_u._data == Integer); } + bool is_long() const { return (_u._data == Long); } + bool is_float() const { return (_u._data == Float); } + bool is_double() const { return (_u._data == Double); } + bool is_long2() const { return (_u._data == Long_2nd); } + bool is_double2() const { return (_u._data == Double_2nd); } + bool is_reference() const { return ((_u._data & TypeMask) == Reference); } + bool is_category1() const { + // This should return true for all one-word types, which are category1 + // primitives, and references (including uninitialized refs). Though + // the 'query' types should technically return 'false' here, if we + // allow this to return true, we can perform the test using only + // 2 operations rather than 8 (3 masks, 3 compares and 2 logical 'ands'). + // Since noone should call this on a query type anyway, this is ok. + assert(!is_check(), "Must not be a check type (wrong value returned)"); + return ((_u._data & Category1) != Primitive); + // should only return false if it's a primitive, and the category1 flag + // is not set. + } + bool is_category2() const { return ((_u._data & Category2) == Category2); } + bool is_category2_2nd() const { + return ((_u._data & Category2_2nd) == Category2_2nd); + } + bool is_reference_check() const { return _u._data == ReferenceQuery; } + bool is_category1_check() const { return _u._data == Category1Query; } + bool is_category2_check() const { return _u._data == Category2Query; } + bool is_category2_2nd_check() const { return _u._data == Category2_2ndQuery; } + bool is_check() const { return (_u._data & TypeQuery) == TypeQuery; } + + bool is_x_array(char sig) const { + return is_null() || (is_array() && (name()->byte_at(1) == sig)); + } + bool is_int_array() const { return is_x_array('I'); } + bool is_byte_array() const { return is_x_array('B'); } + bool is_bool_array() const { return is_x_array('Z'); } + bool is_char_array() const { return is_x_array('C'); } + bool is_short_array() const { return is_x_array('S'); } + bool is_long_array() const { return is_x_array('J'); } + bool is_float_array() const { return is_x_array('F'); } + bool is_double_array() const { return is_x_array('D'); } + bool is_object_array() const { return is_x_array('L'); } + bool is_array_array() const { return is_x_array('['); } + bool is_reference_array() const + { return is_object_array() || is_array_array(); } + bool is_object() const + { return (is_reference() && !is_null() && name()->utf8_length() >= 1 && + name()->byte_at(0) != '['); } + bool is_array() const + { return (is_reference() && !is_null() && name()->utf8_length() >= 2 && + name()->byte_at(0) == '['); } + bool is_uninitialized() const + { return ((_u._data & Uninitialized) == Uninitialized); } + bool is_uninitialized_this() const + { return is_uninitialized() && bci() == BciForThis; } + + VerificationType to_category2_2nd() const { + assert(is_category2(), "Must be a double word"); + return VerificationType(is_long() ? Long_2nd : Double_2nd); + } + + u2 bci() const { + assert(is_uninitialized(), "Must be uninitialized type"); + return ((_u._data & BciMask) >> 1 * BitsPerByte); + } + + symbolHandle name_handle() const { + assert(is_reference() && !is_null(), "Must be a non-null reference"); + return symbolHandle(_u._handle, true); + } + symbolOop name() const { + assert(is_reference() && !is_null(), "Must be a non-null reference"); + return *(_u._handle); + } + + bool equals(const VerificationType& t) const { + return (_u._data == t._u._data || + (is_reference() && t.is_reference() && !is_null() && !t.is_null() && + name() == t.name())); + } + + bool operator ==(const VerificationType& t) const { + return equals(t); + } + + bool operator !=(const VerificationType& t) const { + return !equals(t); + } + + // The whole point of this type system - check to see if one type + // is assignable to another. Returns true if one can assign 'from' to + // this. + bool is_assignable_from( + const VerificationType& from, instanceKlassHandle context, TRAPS) const { + if (equals(from) || is_bogus()) { + return true; + } else { + switch(_u._data) { + case Category1Query: + return from.is_category1(); + case Category2Query: + return from.is_category2(); + case Category2_2ndQuery: + return from.is_category2_2nd(); + case ReferenceQuery: + return from.is_reference() || from.is_uninitialized(); + case Boolean: + case Byte: + case Char: + case Short: + // An int can be assigned to boolean, byte, char or short values. + return from.is_integer(); + default: + if (is_reference() && from.is_reference()) { + return is_reference_assignable_from(from, context, CHECK_false); + } else { + return false; + } + } + } + } + + VerificationType get_component(TRAPS) const; + + int dimensions() const { + assert(is_array(), "Must be an array"); + int index = 0; + while (name()->byte_at(index++) == '['); + return index; + } + + void print_on(outputStream* st) const PRODUCT_RETURN; + + private: + + bool is_reference_assignable_from( + const VerificationType&, instanceKlassHandle, TRAPS) const; +}; diff --git a/hotspot/src/share/vm/classfile/verifier.cpp b/hotspot/src/share/vm/classfile/verifier.cpp new file mode 100644 index 00000000000..20157e96749 --- /dev/null +++ b/hotspot/src/share/vm/classfile/verifier.cpp @@ -0,0 +1,2197 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_verifier.cpp.incl" + +// Access to external entry for VerifyClassCodes - old byte code verifier + +extern "C" { + typedef jboolean (*verify_byte_codes_fn_t)(JNIEnv *, jclass, char *, jint); + typedef jboolean (*verify_byte_codes_fn_new_t)(JNIEnv *, jclass, char *, jint, jint); +} + +static void* volatile _verify_byte_codes_fn = NULL; + +static volatile jint _is_new_verify_byte_codes_fn = (jint) true; + +static void* verify_byte_codes_fn() { + if (_verify_byte_codes_fn == NULL) { + void *lib_handle = os::native_java_library(); + void *func = hpi::dll_lookup(lib_handle, "VerifyClassCodesForMajorVersion"); + OrderAccess::release_store_ptr(&_verify_byte_codes_fn, func); + if (func == NULL) { + OrderAccess::release_store(&_is_new_verify_byte_codes_fn, false); + func = hpi::dll_lookup(lib_handle, "VerifyClassCodes"); + OrderAccess::release_store_ptr(&_verify_byte_codes_fn, func); + } + } + return (void*)_verify_byte_codes_fn; +} + + +// Methods in Verifier + +bool Verifier::should_verify_for(oop class_loader) { + return class_loader == NULL ? + BytecodeVerificationLocal : BytecodeVerificationRemote; +} + +bool Verifier::relax_verify_for(oop loader) { + bool trusted = java_lang_ClassLoader::is_trusted_loader(loader); + bool need_verify = + // verifyAll + (BytecodeVerificationLocal && BytecodeVerificationRemote) || + // verifyRemote + (!BytecodeVerificationLocal && BytecodeVerificationRemote && !trusted); + return !need_verify; +} + +bool Verifier::verify(instanceKlassHandle klass, Verifier::Mode mode, TRAPS) { + ResourceMark rm(THREAD); + HandleMark hm; + + symbolHandle exception_name; + const size_t message_buffer_len = klass->name()->utf8_length() + 1024; + char* message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len); + + const char* klassName = klass->external_name(); + + // If the class should be verified, first see if we can use the split + // verifier. If not, or if verification fails and FailOverToOldVerifier + // is set, then call the inference verifier. + if (is_eligible_for_verification(klass)) { + if (TraceClassInitialization) { + tty->print_cr("Start class verification for: %s", klassName); + } + if (UseSplitVerifier && + klass->major_version() >= STACKMAP_ATTRIBUTE_MAJOR_VERSION) { + ClassVerifier split_verifier( + klass, message_buffer, message_buffer_len, THREAD); + split_verifier.verify_class(THREAD); + exception_name = split_verifier.result(); + if (FailOverToOldVerifier && !HAS_PENDING_EXCEPTION && + (exception_name == vmSymbols::java_lang_VerifyError() || + exception_name == vmSymbols::java_lang_ClassFormatError())) { + if (TraceClassInitialization) { + tty->print_cr( + "Fail over class verification to old verifier for: %s", klassName); + } + exception_name = inference_verify( + klass, message_buffer, message_buffer_len, THREAD); + } + } else { + exception_name = inference_verify( + klass, message_buffer, message_buffer_len, THREAD); + } + + if (TraceClassInitialization) { + if (HAS_PENDING_EXCEPTION) { + tty->print("Verification for %s has", klassName); + tty->print_cr(" exception pending %s ", + instanceKlass::cast(PENDING_EXCEPTION->klass())->external_name()); + } else if (!exception_name.is_null()) { + tty->print_cr("Verification for %s failed", klassName); + } + tty->print_cr("End class verification for: %s", klassName); + } + } + + if (HAS_PENDING_EXCEPTION) { + return false; // use the existing exception + } else if (exception_name.is_null()) { + return true; // verifcation succeeded + } else { // VerifyError or ClassFormatError to be created and thrown + ResourceMark rm(THREAD); + instanceKlassHandle kls = + SystemDictionary::resolve_or_fail(exception_name, true, CHECK_false); + while (!kls.is_null()) { + if (kls == klass) { + // If the class being verified is the exception we're creating + // or one of it's superclasses, we're in trouble and are going + // to infinitely recurse when we try to initialize the exception. + // So bail out here by throwing the preallocated VM error. + THROW_OOP_(Universe::virtual_machine_error_instance(), false); + } + kls = kls->super(); + } + message_buffer[message_buffer_len - 1] = '\0'; // just to be sure + THROW_MSG_(exception_name, message_buffer, false); + } +} + +bool Verifier::is_eligible_for_verification(instanceKlassHandle klass) { + symbolOop name = klass->name(); + klassOop refl_magic_klass = SystemDictionary::reflect_magic_klass(); + + return (should_verify_for(klass->class_loader()) && + // return if the class is a bootstrapping class + // We need to skip the following four for bootstraping + name != vmSymbols::java_lang_Object() && + name != vmSymbols::java_lang_Class() && + name != vmSymbols::java_lang_String() && + name != vmSymbols::java_lang_Throwable() && + + // Can not verify the bytecodes for shared classes because they have + // already been rewritten to contain constant pool cache indices, + // which the verifier can't understand. + // Shared classes shouldn't have stackmaps either. + !klass()->is_shared() && + + // As of the fix for 4486457 we disable verification for all of the + // dynamically-generated bytecodes associated with the 1.4 + // reflection implementation, not just those associated with + // sun/reflect/SerializationConstructorAccessor. + // NOTE: this is called too early in the bootstrapping process to be + // guarded by Universe::is_gte_jdk14x_version()/UseNewReflection. + (refl_magic_klass == NULL || + !klass->is_subtype_of(refl_magic_klass) || + VerifyReflectionBytecodes) + ); +} + +symbolHandle Verifier::inference_verify( + instanceKlassHandle klass, char* message, size_t message_len, TRAPS) { + JavaThread* thread = (JavaThread*)THREAD; + JNIEnv *env = thread->jni_environment(); + + void* verify_func = verify_byte_codes_fn(); + + if (verify_func == NULL) { + jio_snprintf(message, message_len, "Could not link verifier"); + return vmSymbols::java_lang_VerifyError(); + } + + ResourceMark rm(THREAD); + if (ClassVerifier::_verify_verbose) { + tty->print_cr("Verifying class %s with old format", klass->external_name()); + } + + jclass cls = (jclass) JNIHandles::make_local(env, klass->java_mirror()); + jint result; + + { + HandleMark hm(thread); + ThreadToNativeFromVM ttn(thread); + // ThreadToNativeFromVM takes care of changing thread_state, so safepoint + // code knows that we have left the VM + + if (_is_new_verify_byte_codes_fn) { + verify_byte_codes_fn_new_t func = + CAST_TO_FN_PTR(verify_byte_codes_fn_new_t, verify_func); + result = (*func)(env, cls, message, (int)message_len, + klass->major_version()); + } else { + verify_byte_codes_fn_t func = + CAST_TO_FN_PTR(verify_byte_codes_fn_t, verify_func); + result = (*func)(env, cls, message, (int)message_len); + } + } + + JNIHandles::destroy_local(cls); + + // These numbers are chosen so that VerifyClassCodes interface doesn't need + // to be changed (still return jboolean (unsigned char)), and result is + // 1 when verification is passed. + symbolHandle nh(NULL); + if (result == 0) { + return vmSymbols::java_lang_VerifyError(); + } else if (result == 1) { + return nh; // verified. + } else if (result == 2) { + THROW_MSG_(vmSymbols::java_lang_OutOfMemoryError(), message, nh); + } else if (result == 3) { + return vmSymbols::java_lang_ClassFormatError(); + } else { + ShouldNotReachHere(); + return nh; + } +} + +// Methods in ClassVerifier + +bool ClassVerifier::_verify_verbose = false; + +ClassVerifier::ClassVerifier( + instanceKlassHandle klass, char* msg, size_t msg_len, TRAPS) + : _thread(THREAD), _exception_type(symbolHandle()), _message(msg), + _message_buffer_len(msg_len), _klass(klass) { + _this_type = VerificationType::reference_type(klass->name()); +} + +ClassVerifier::~ClassVerifier() { +} + +void ClassVerifier::verify_class(TRAPS) { + if (_verify_verbose) { + tty->print_cr("Verifying class %s with new format", + _klass->external_name()); + } + + objArrayHandle methods(THREAD, _klass->methods()); + int num_methods = methods->length(); + + for (int index = 0; index < num_methods; index++) { + methodOop m = (methodOop)methods->obj_at(index); + if (m->is_native() || m->is_abstract()) { + // If m is native or abstract, skip it. It is checked in class file + // parser that methods do not override a final method. + continue; + } + verify_method(methodHandle(THREAD, m), CHECK_VERIFY(this)); + } +} + +void ClassVerifier::verify_method(methodHandle m, TRAPS) { + ResourceMark rm(THREAD); + _method = m; // initialize _method + if (_verify_verbose) { + tty->print_cr("Verifying method %s", m->name_and_sig_as_C_string()); + } + + const char* bad_type_msg = "Bad type on operand stack in %s"; + + int32_t max_stack = m->max_stack(); + int32_t max_locals = m->max_locals(); + constantPoolHandle cp(THREAD, m->constants()); + + if (!SignatureVerifier::is_valid_method_signature(m->signature())) { + class_format_error("Invalid method signature"); + return; + } + + // Initial stack map frame: offset is 0, stack is initially empty. + StackMapFrame current_frame(max_locals, max_stack, this); + // Set initial locals + VerificationType return_type = current_frame.set_locals_from_arg( + m, current_type(), CHECK_VERIFY(this)); + + int32_t stackmap_index = 0; // index to the stackmap array + + u4 code_length = m->code_size(); + + // Scan the bytecode and map each instruction's start offset to a number. + char* code_data = generate_code_data(m, code_length, CHECK_VERIFY(this)); + + int ex_min = code_length; + int ex_max = -1; + // Look through each item on the exception table. Each of the fields must refer + // to a legal instruction. + verify_exception_handler_table( + code_length, code_data, ex_min, ex_max, CHECK_VERIFY(this)); + + // Look through each entry on the local variable table and make sure + // its range of code array offsets is valid. (4169817) + if (m->has_localvariable_table()) { + verify_local_variable_table(code_length, code_data, CHECK_VERIFY(this)); + } + + typeArrayHandle stackmap_data(THREAD, m->stackmap_data()); + StackMapStream stream(stackmap_data); + StackMapReader reader(this, &stream, code_data, code_length, THREAD); + StackMapTable stackmap_table(&reader, ¤t_frame, max_locals, max_stack, + code_data, code_length, CHECK_VERIFY(this)); + + if (_verify_verbose) { + stackmap_table.print(); + } + + RawBytecodeStream bcs(m); + + // Scan the byte code linearly from the start to the end + bool no_control_flow = false; // Set to true when there is no direct control + // flow from current instruction to the next + // instruction in sequence + Bytecodes::Code opcode; + while (!bcs.is_last_bytecode()) { + opcode = bcs.raw_next(); + u2 bci = bcs.bci(); + + // Set current frame's offset to bci + current_frame.set_offset(bci); + + // Make sure every offset in stackmap table point to the beginning to + // an instruction. Match current_frame to stackmap_table entry with + // the same offset if exists. + stackmap_index = verify_stackmap_table( + stackmap_index, bci, ¤t_frame, &stackmap_table, + no_control_flow, CHECK_VERIFY(this)); + + bool this_uninit = false; // Set to true when invokespecial initialized 'this' + + // Merge with the next instruction + { + u2 index; + int target; + VerificationType type, type2; + VerificationType atype; + +#ifndef PRODUCT + if (_verify_verbose) { + current_frame.print(); + tty->print_cr("offset = %d, opcode = %s", bci, Bytecodes::name(opcode)); + } +#endif + + // Make sure wide instruction is in correct format + if (bcs.is_wide()) { + if (opcode != Bytecodes::_iinc && opcode != Bytecodes::_iload && + opcode != Bytecodes::_aload && opcode != Bytecodes::_lload && + opcode != Bytecodes::_istore && opcode != Bytecodes::_astore && + opcode != Bytecodes::_lstore && opcode != Bytecodes::_fload && + opcode != Bytecodes::_dload && opcode != Bytecodes::_fstore && + opcode != Bytecodes::_dstore) { + verify_error(bci, "Bad wide instruction"); + return; + } + } + + switch (opcode) { + case Bytecodes::_nop : + no_control_flow = false; break; + case Bytecodes::_aconst_null : + current_frame.push_stack( + VerificationType::null_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_iconst_m1 : + case Bytecodes::_iconst_0 : + case Bytecodes::_iconst_1 : + case Bytecodes::_iconst_2 : + case Bytecodes::_iconst_3 : + case Bytecodes::_iconst_4 : + case Bytecodes::_iconst_5 : + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_lconst_0 : + case Bytecodes::_lconst_1 : + current_frame.push_stack_2( + VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_fconst_0 : + case Bytecodes::_fconst_1 : + case Bytecodes::_fconst_2 : + current_frame.push_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_dconst_0 : + case Bytecodes::_dconst_1 : + current_frame.push_stack_2( + VerificationType::double_type(), + VerificationType::double2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_sipush : + case Bytecodes::_bipush : + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_ldc : + verify_ldc( + opcode, bcs.get_index(), ¤t_frame, + cp, bci, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_ldc_w : + case Bytecodes::_ldc2_w : + verify_ldc( + opcode, bcs.get_index_big(), ¤t_frame, + cp, bci, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_iload : + verify_iload(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_iload_0 : + case Bytecodes::_iload_1 : + case Bytecodes::_iload_2 : + case Bytecodes::_iload_3 : + index = opcode - Bytecodes::_iload_0; + verify_iload(index, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_lload : + verify_lload(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_lload_0 : + case Bytecodes::_lload_1 : + case Bytecodes::_lload_2 : + case Bytecodes::_lload_3 : + index = opcode - Bytecodes::_lload_0; + verify_lload(index, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_fload : + verify_fload(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_fload_0 : + case Bytecodes::_fload_1 : + case Bytecodes::_fload_2 : + case Bytecodes::_fload_3 : + index = opcode - Bytecodes::_fload_0; + verify_fload(index, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_dload : + verify_dload(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_dload_0 : + case Bytecodes::_dload_1 : + case Bytecodes::_dload_2 : + case Bytecodes::_dload_3 : + index = opcode - Bytecodes::_dload_0; + verify_dload(index, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_aload : + verify_aload(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_aload_0 : + case Bytecodes::_aload_1 : + case Bytecodes::_aload_2 : + case Bytecodes::_aload_3 : + index = opcode - Bytecodes::_aload_0; + verify_aload(index, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_iaload : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_int_array()) { + verify_error(bci, bad_type_msg, "iaload"); + return; + } + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_baload : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_bool_array() && !atype.is_byte_array()) { + verify_error(bci, bad_type_msg, "baload"); + return; + } + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_caload : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_char_array()) { + verify_error(bci, bad_type_msg, "caload"); + return; + } + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_saload : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_short_array()) { + verify_error(bci, bad_type_msg, "saload"); + return; + } + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_laload : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_long_array()) { + verify_error(bci, bad_type_msg, "laload"); + return; + } + current_frame.push_stack_2( + VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_faload : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_float_array()) { + verify_error(bci, bad_type_msg, "faload"); + return; + } + current_frame.push_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_daload : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_double_array()) { + verify_error(bci, bad_type_msg, "daload"); + return; + } + current_frame.push_stack_2( + VerificationType::double_type(), + VerificationType::double2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_aaload : { + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_reference_array()) { + verify_error(bci, bad_type_msg, "aaload"); + return; + } + if (atype.is_null()) { + current_frame.push_stack( + VerificationType::null_type(), CHECK_VERIFY(this)); + } else { + VerificationType component = + atype.get_component(CHECK_VERIFY(this)); + current_frame.push_stack(component, CHECK_VERIFY(this)); + } + no_control_flow = false; break; + } + case Bytecodes::_istore : + verify_istore(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_istore_0 : + case Bytecodes::_istore_1 : + case Bytecodes::_istore_2 : + case Bytecodes::_istore_3 : + index = opcode - Bytecodes::_istore_0; + verify_istore(index, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_lstore : + verify_lstore(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_lstore_0 : + case Bytecodes::_lstore_1 : + case Bytecodes::_lstore_2 : + case Bytecodes::_lstore_3 : + index = opcode - Bytecodes::_lstore_0; + verify_lstore(index, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_fstore : + verify_fstore(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_fstore_0 : + case Bytecodes::_fstore_1 : + case Bytecodes::_fstore_2 : + case Bytecodes::_fstore_3 : + index = opcode - Bytecodes::_fstore_0; + verify_fstore(index, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_dstore : + verify_dstore(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_dstore_0 : + case Bytecodes::_dstore_1 : + case Bytecodes::_dstore_2 : + case Bytecodes::_dstore_3 : + index = opcode - Bytecodes::_dstore_0; + verify_dstore(index, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_astore : + verify_astore(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_astore_0 : + case Bytecodes::_astore_1 : + case Bytecodes::_astore_2 : + case Bytecodes::_astore_3 : + index = opcode - Bytecodes::_astore_0; + verify_astore(index, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_iastore : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + type2 = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_int_array()) { + verify_error(bci, bad_type_msg, "iastore"); + return; + } + no_control_flow = false; break; + case Bytecodes::_bastore : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + type2 = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_bool_array() && !atype.is_byte_array()) { + verify_error(bci, bad_type_msg, "bastore"); + return; + } + no_control_flow = false; break; + case Bytecodes::_castore : + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_char_array()) { + verify_error(bci, bad_type_msg, "castore"); + return; + } + no_control_flow = false; break; + case Bytecodes::_sastore : + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_short_array()) { + verify_error(bci, bad_type_msg, "sastore"); + return; + } + no_control_flow = false; break; + case Bytecodes::_lastore : + current_frame.pop_stack_2( + VerificationType::long2_type(), + VerificationType::long_type(), CHECK_VERIFY(this)); + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_long_array()) { + verify_error(bci, bad_type_msg, "lastore"); + return; + } + no_control_flow = false; break; + case Bytecodes::_fastore : + current_frame.pop_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + current_frame.pop_stack + (VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_float_array()) { + verify_error(bci, bad_type_msg, "fastore"); + return; + } + no_control_flow = false; break; + case Bytecodes::_dastore : + current_frame.pop_stack_2( + VerificationType::double2_type(), + VerificationType::double_type(), CHECK_VERIFY(this)); + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!atype.is_double_array()) { + verify_error(bci, bad_type_msg, "dastore"); + return; + } + no_control_flow = false; break; + case Bytecodes::_aastore : + type = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + type2 = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + atype = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + // more type-checking is done at runtime + if (!atype.is_reference_array()) { + verify_error(bci, bad_type_msg, "aastore"); + return; + } + // 4938384: relaxed constraint in JVMS 3nd edition. + no_control_flow = false; break; + case Bytecodes::_pop : + current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_pop2 : + type = current_frame.pop_stack(CHECK_VERIFY(this)); + if (type.is_category1()) { + current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + } else if (type.is_category2_2nd()) { + current_frame.pop_stack( + VerificationType::category2_check(), CHECK_VERIFY(this)); + } else { + verify_error(bci, bad_type_msg, "pop2"); + return; + } + no_control_flow = false; break; + case Bytecodes::_dup : + type = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_dup_x1 : + type = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + type2 = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + current_frame.push_stack(type2, CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_dup_x2 : + { + VerificationType type3; + type = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + type2 = current_frame.pop_stack(CHECK_VERIFY(this)); + if (type2.is_category1()) { + type3 = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + } else if (type2.is_category2_2nd()) { + type3 = current_frame.pop_stack( + VerificationType::category2_check(), CHECK_VERIFY(this)); + } else { + verify_error(bci, bad_type_msg, "dup_x2"); + return; + } + current_frame.push_stack(type, CHECK_VERIFY(this)); + current_frame.push_stack(type3, CHECK_VERIFY(this)); + current_frame.push_stack(type2, CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + no_control_flow = false; break; + } + case Bytecodes::_dup2 : + type = current_frame.pop_stack(CHECK_VERIFY(this)); + if (type.is_category1()) { + type2 = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + } else if (type.is_category2_2nd()) { + type2 = current_frame.pop_stack( + VerificationType::category2_check(), CHECK_VERIFY(this)); + } else { + verify_error(bci, bad_type_msg, "dup2"); + return; + } + current_frame.push_stack(type2, CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + current_frame.push_stack(type2, CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_dup2_x1 : + { + VerificationType type3; + type = current_frame.pop_stack(CHECK_VERIFY(this)); + if (type.is_category1()) { + type2 = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + } else if(type.is_category2_2nd()) { + type2 = current_frame.pop_stack + (VerificationType::category2_check(), CHECK_VERIFY(this)); + } else { + verify_error(bci, bad_type_msg, "dup2_x1"); + return; + } + type3 = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + current_frame.push_stack(type2, CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + current_frame.push_stack(type3, CHECK_VERIFY(this)); + current_frame.push_stack(type2, CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + no_control_flow = false; break; + } + case Bytecodes::_dup2_x2 : + { + VerificationType type3, type4; + type = current_frame.pop_stack(CHECK_VERIFY(this)); + if (type.is_category1()) { + type2 = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + } else if (type.is_category2_2nd()) { + type2 = current_frame.pop_stack( + VerificationType::category2_check(), CHECK_VERIFY(this)); + } else { + verify_error(bci, bad_type_msg, "dup2_x2"); + return; + } + type3 = current_frame.pop_stack(CHECK_VERIFY(this)); + if (type3.is_category1()) { + type4 = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + } else if (type3.is_category2_2nd()) { + type4 = current_frame.pop_stack( + VerificationType::category2_check(), CHECK_VERIFY(this)); + } else { + verify_error(bci, bad_type_msg, "dup2_x2"); + return; + } + current_frame.push_stack(type2, CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + current_frame.push_stack(type4, CHECK_VERIFY(this)); + current_frame.push_stack(type3, CHECK_VERIFY(this)); + current_frame.push_stack(type2, CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + no_control_flow = false; break; + } + case Bytecodes::_swap : + type = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + type2 = current_frame.pop_stack( + VerificationType::category1_check(), CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + current_frame.push_stack(type2, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_iadd : + case Bytecodes::_isub : + case Bytecodes::_imul : + case Bytecodes::_idiv : + case Bytecodes::_irem : + case Bytecodes::_ishl : + case Bytecodes::_ishr : + case Bytecodes::_iushr : + case Bytecodes::_ior : + case Bytecodes::_ixor : + case Bytecodes::_iand : + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + // fall through + case Bytecodes::_ineg : + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_ladd : + case Bytecodes::_lsub : + case Bytecodes::_lmul : + case Bytecodes::_ldiv : + case Bytecodes::_lrem : + case Bytecodes::_land : + case Bytecodes::_lor : + case Bytecodes::_lxor : + current_frame.pop_stack_2( + VerificationType::long2_type(), + VerificationType::long_type(), CHECK_VERIFY(this)); + // fall through + case Bytecodes::_lneg : + current_frame.pop_stack_2( + VerificationType::long2_type(), + VerificationType::long_type(), CHECK_VERIFY(this)); + current_frame.push_stack_2( + VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_lshl : + case Bytecodes::_lshr : + case Bytecodes::_lushr : + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame.pop_stack_2( + VerificationType::long2_type(), + VerificationType::long_type(), CHECK_VERIFY(this)); + current_frame.push_stack_2( + VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_fadd : + case Bytecodes::_fsub : + case Bytecodes::_fmul : + case Bytecodes::_fdiv : + case Bytecodes::_frem : + current_frame.pop_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + // fall through + case Bytecodes::_fneg : + current_frame.pop_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_dadd : + case Bytecodes::_dsub : + case Bytecodes::_dmul : + case Bytecodes::_ddiv : + case Bytecodes::_drem : + current_frame.pop_stack_2( + VerificationType::double2_type(), + VerificationType::double_type(), CHECK_VERIFY(this)); + // fall through + case Bytecodes::_dneg : + current_frame.pop_stack_2( + VerificationType::double2_type(), + VerificationType::double_type(), CHECK_VERIFY(this)); + current_frame.push_stack_2( + VerificationType::double_type(), + VerificationType::double2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_iinc : + verify_iinc(bcs.get_index(), ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_i2l : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame.push_stack_2( + VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_l2i : + current_frame.pop_stack_2( + VerificationType::long2_type(), + VerificationType::long_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_i2f : + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_i2d : + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame.push_stack_2( + VerificationType::double_type(), + VerificationType::double2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_l2f : + current_frame.pop_stack_2( + VerificationType::long2_type(), + VerificationType::long_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_l2d : + current_frame.pop_stack_2( + VerificationType::long2_type(), + VerificationType::long_type(), CHECK_VERIFY(this)); + current_frame.push_stack_2( + VerificationType::double_type(), + VerificationType::double2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_f2i : + current_frame.pop_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_f2l : + current_frame.pop_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + current_frame.push_stack_2( + VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_f2d : + current_frame.pop_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + current_frame.push_stack_2( + VerificationType::double_type(), + VerificationType::double2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_d2i : + current_frame.pop_stack_2( + VerificationType::double2_type(), + VerificationType::double_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_d2l : + current_frame.pop_stack_2( + VerificationType::double2_type(), + VerificationType::double_type(), CHECK_VERIFY(this)); + current_frame.push_stack_2( + VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_d2f : + current_frame.pop_stack_2( + VerificationType::double2_type(), + VerificationType::double_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_i2b : + case Bytecodes::_i2c : + case Bytecodes::_i2s : + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_lcmp : + current_frame.pop_stack_2( + VerificationType::long2_type(), + VerificationType::long_type(), CHECK_VERIFY(this)); + current_frame.pop_stack_2( + VerificationType::long2_type(), + VerificationType::long_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_fcmpl : + case Bytecodes::_fcmpg : + current_frame.pop_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + current_frame.pop_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_dcmpl : + case Bytecodes::_dcmpg : + current_frame.pop_stack_2( + VerificationType::double2_type(), + VerificationType::double_type(), CHECK_VERIFY(this)); + current_frame.pop_stack_2( + VerificationType::double2_type(), + VerificationType::double_type(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + // fall through + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + target = bcs.dest(); + stackmap_table.check_jump_target( + ¤t_frame, target, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_if_acmpeq : + case Bytecodes::_if_acmpne : + current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + // fall through + case Bytecodes::_ifnull : + case Bytecodes::_ifnonnull : + current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + target = bcs.dest(); + stackmap_table.check_jump_target + (¤t_frame, target, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_goto : + target = bcs.dest(); + stackmap_table.check_jump_target( + ¤t_frame, target, CHECK_VERIFY(this)); + no_control_flow = true; break; + case Bytecodes::_goto_w : + target = bcs.dest_w(); + stackmap_table.check_jump_target( + ¤t_frame, target, CHECK_VERIFY(this)); + no_control_flow = true; break; + case Bytecodes::_tableswitch : + case Bytecodes::_lookupswitch : + verify_switch( + &bcs, code_length, code_data, ¤t_frame, + &stackmap_table, CHECK_VERIFY(this)); + no_control_flow = true; break; + case Bytecodes::_ireturn : + type = current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + verify_return_value(return_type, type, bci, CHECK_VERIFY(this)); + no_control_flow = true; break; + case Bytecodes::_lreturn : + type2 = current_frame.pop_stack( + VerificationType::long2_type(), CHECK_VERIFY(this)); + type = current_frame.pop_stack( + VerificationType::long_type(), CHECK_VERIFY(this)); + verify_return_value(return_type, type, bci, CHECK_VERIFY(this)); + no_control_flow = true; break; + case Bytecodes::_freturn : + type = current_frame.pop_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + verify_return_value(return_type, type, bci, CHECK_VERIFY(this)); + no_control_flow = true; break; + case Bytecodes::_dreturn : + type2 = current_frame.pop_stack( + VerificationType::double2_type(), CHECK_VERIFY(this)); + type = current_frame.pop_stack( + VerificationType::double_type(), CHECK_VERIFY(this)); + verify_return_value(return_type, type, bci, CHECK_VERIFY(this)); + no_control_flow = true; break; + case Bytecodes::_areturn : + type = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + verify_return_value(return_type, type, bci, CHECK_VERIFY(this)); + no_control_flow = true; break; + case Bytecodes::_return : + if (return_type != VerificationType::bogus_type()) { + verify_error(bci, "Method expects no return value"); + return; + } + // Make sure "this" has been initialized if current method is an + // + if (_method->name() == vmSymbols::object_initializer_name() && + current_frame.flag_this_uninit()) { + verify_error(bci, + "Constructor must call super() or this() before return"); + return; + } + no_control_flow = true; break; + case Bytecodes::_getstatic : + case Bytecodes::_putstatic : + case Bytecodes::_getfield : + case Bytecodes::_putfield : + verify_field_instructions( + &bcs, ¤t_frame, cp, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_invokevirtual : + case Bytecodes::_invokespecial : + case Bytecodes::_invokestatic : + verify_invoke_instructions( + &bcs, code_length, ¤t_frame, + &this_uninit, return_type, cp, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_invokeinterface : + verify_invoke_instructions( + &bcs, code_length, ¤t_frame, + &this_uninit, return_type, cp, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_new : + { + index = bcs.get_index_big(); + verify_cp_class_type(index, cp, CHECK_VERIFY(this)); + VerificationType new_class_type = + cp_index_to_type(index, cp, CHECK_VERIFY(this)); + if (!new_class_type.is_object()) { + verify_error(bci, "Illegal new instruction"); + return; + } + type = VerificationType::uninitialized_type(bci); + current_frame.push_stack(type, CHECK_VERIFY(this)); + no_control_flow = false; break; + } + case Bytecodes::_newarray : + type = get_newarray_type(bcs.get_index(), bci, CHECK_VERIFY(this)); + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame.push_stack(type, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_anewarray : + verify_anewarray( + bcs.get_index_big(), cp, ¤t_frame, CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_arraylength : + type = current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (!type.is_array()) { + verify_error(bci, bad_type_msg, "arraylength"); + } + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_checkcast : + { + index = bcs.get_index_big(); + verify_cp_class_type(index, cp, CHECK_VERIFY(this)); + current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + VerificationType klass_type = cp_index_to_type( + index, cp, CHECK_VERIFY(this)); + current_frame.push_stack(klass_type, CHECK_VERIFY(this)); + no_control_flow = false; break; + } + case Bytecodes::_instanceof : { + index = bcs.get_index_big(); + verify_cp_class_type(index, cp, CHECK_VERIFY(this)); + current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + current_frame.push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + no_control_flow = false; break; + } + case Bytecodes::_monitorenter : + case Bytecodes::_monitorexit : + current_frame.pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + no_control_flow = false; break; + case Bytecodes::_multianewarray : + { + index = bcs.get_index_big(); + u2 dim = *(bcs.bcp()+3); + verify_cp_class_type(index, cp, CHECK_VERIFY(this)); + VerificationType new_array_type = + cp_index_to_type(index, cp, CHECK_VERIFY(this)); + if (!new_array_type.is_array()) { + verify_error(bci, + "Illegal constant pool index in multianewarray instruction"); + return; + } + if (dim < 1 || new_array_type.dimensions() < dim) { + verify_error(bci, + "Illegal dimension in multianewarray instruction"); + return; + } + for (int i = 0; i < dim; i++) { + current_frame.pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + } + current_frame.push_stack(new_array_type, CHECK_VERIFY(this)); + no_control_flow = false; break; + } + case Bytecodes::_athrow : + type = VerificationType::reference_type( + vmSymbols::java_lang_Throwable()); + current_frame.pop_stack(type, CHECK_VERIFY(this)); + no_control_flow = true; break; + default: + // We only need to check the valid bytecodes in class file. + // And jsr and ret are not in the new class file format in JDK1.5. + verify_error(bci, "Bad instruction"); + no_control_flow = false; + return; + } // end switch + } // end Merge with the next instruction + + // Look for possible jump target in exception handlers and see if it + // matches current_frame + if (bci >= ex_min && bci < ex_max) { + verify_exception_handler_targets( + bci, this_uninit, ¤t_frame, &stackmap_table, CHECK_VERIFY(this)); + } + } // end while + + // Make sure that control flow does not fall through end of the method + if (!no_control_flow) { + verify_error(code_length, "Control flow falls through code end"); + return; + } +} + +char* ClassVerifier::generate_code_data(methodHandle m, u4 code_length, TRAPS) { + char* code_data = NEW_RESOURCE_ARRAY(char, code_length); + memset(code_data, 0, sizeof(char) * code_length); + RawBytecodeStream bcs(m); + + while (!bcs.is_last_bytecode()) { + if (bcs.raw_next() != Bytecodes::_illegal) { + int bci = bcs.bci(); + if (bcs.code() == Bytecodes::_new) { + code_data[bci] = NEW_OFFSET; + } else { + code_data[bci] = BYTECODE_OFFSET; + } + } else { + verify_error(bcs.bci(), "Bad instruction"); + return NULL; + } + } + + return code_data; +} + +void ClassVerifier::verify_exception_handler_table(u4 code_length, char* code_data, int& min, int& max, TRAPS) { + typeArrayHandle exhandlers (THREAD, _method->exception_table()); + constantPoolHandle cp (THREAD, _method->constants()); + + if (exhandlers() != NULL) { + for(int i = 0; i < exhandlers->length();) { + u2 start_pc = exhandlers->int_at(i++); + u2 end_pc = exhandlers->int_at(i++); + u2 handler_pc = exhandlers->int_at(i++); + if (start_pc >= code_length || code_data[start_pc] == 0) { + class_format_error("Illegal exception table start_pc %d", start_pc); + return; + } + if (end_pc != code_length) { // special case: end_pc == code_length + if (end_pc > code_length || code_data[end_pc] == 0) { + class_format_error("Illegal exception table end_pc %d", end_pc); + return; + } + } + if (handler_pc >= code_length || code_data[handler_pc] == 0) { + class_format_error("Illegal exception table handler_pc %d", handler_pc); + return; + } + int catch_type_index = exhandlers->int_at(i++); + if (catch_type_index != 0) { + VerificationType catch_type = cp_index_to_type( + catch_type_index, cp, CHECK_VERIFY(this)); + VerificationType throwable = + VerificationType::reference_type(vmSymbols::java_lang_Throwable()); + bool is_subclass = throwable.is_assignable_from( + catch_type, current_class(), CHECK_VERIFY(this)); + if (!is_subclass) { + // 4286534: should throw VerifyError according to recent spec change + verify_error( + "Catch type is not a subclass of Throwable in handler %d", + handler_pc); + return; + } + } + if (start_pc < min) min = start_pc; + if (end_pc > max) max = end_pc; + } + } +} + +void ClassVerifier::verify_local_variable_table(u4 code_length, char* code_data, TRAPS) { + int localvariable_table_length = _method()->localvariable_table_length(); + if (localvariable_table_length > 0) { + LocalVariableTableElement* table = _method()->localvariable_table_start(); + for (int i = 0; i < localvariable_table_length; i++) { + u2 start_bci = table[i].start_bci; + u2 length = table[i].length; + + if (start_bci >= code_length || code_data[start_bci] == 0) { + class_format_error( + "Illegal local variable table start_pc %d", start_bci); + return; + } + u4 end_bci = (u4)(start_bci + length); + if (end_bci != code_length) { + if (end_bci >= code_length || code_data[end_bci] == 0) { + class_format_error( "Illegal local variable table length %d", length); + return; + } + } + } + } +} + +u2 ClassVerifier::verify_stackmap_table(u2 stackmap_index, u2 bci, + StackMapFrame* current_frame, + StackMapTable* stackmap_table, + bool no_control_flow, TRAPS) { + if (stackmap_index < stackmap_table->get_frame_count()) { + u2 this_offset = stackmap_table->get_offset(stackmap_index); + if (no_control_flow && this_offset > bci) { + verify_error(bci, "Expecting a stack map frame"); + return 0; + } + if (this_offset == bci) { + // See if current stack map can be assigned to the frame in table. + // current_frame is the stackmap frame got from the last instruction. + // If matched, current_frame will be updated by this method. + bool match = stackmap_table->match_stackmap( + current_frame, this_offset, stackmap_index, + !no_control_flow, true, CHECK_VERIFY_(this, 0)); + if (!match) { + // report type error + verify_error(bci, "Instruction type does not match stack map"); + return 0; + } + stackmap_index++; + } else if (this_offset < bci) { + // current_offset should have met this_offset. + class_format_error("Bad stack map offset %d", this_offset); + return 0; + } + } else if (no_control_flow) { + verify_error(bci, "Expecting a stack map frame"); + return 0; + } + return stackmap_index; +} + +void ClassVerifier::verify_exception_handler_targets(u2 bci, bool this_uninit, StackMapFrame* current_frame, + StackMapTable* stackmap_table, TRAPS) { + constantPoolHandle cp (THREAD, _method->constants()); + typeArrayHandle exhandlers (THREAD, _method->exception_table()); + if (exhandlers() != NULL) { + for(int i = 0; i < exhandlers->length();) { + u2 start_pc = exhandlers->int_at(i++); + u2 end_pc = exhandlers->int_at(i++); + u2 handler_pc = exhandlers->int_at(i++); + int catch_type_index = exhandlers->int_at(i++); + if(bci >= start_pc && bci < end_pc) { + u1 flags = current_frame->flags(); + if (this_uninit) { flags |= FLAG_THIS_UNINIT; } + + ResourceMark rm(THREAD); + StackMapFrame* new_frame = current_frame->frame_in_exception_handler(flags); + if (catch_type_index != 0) { + // We know that this index refers to a subclass of Throwable + VerificationType catch_type = cp_index_to_type( + catch_type_index, cp, CHECK_VERIFY(this)); + new_frame->push_stack(catch_type, CHECK_VERIFY(this)); + } else { + VerificationType throwable = + VerificationType::reference_type(vmSymbols::java_lang_Throwable()); + new_frame->push_stack(throwable, CHECK_VERIFY(this)); + } + bool match = stackmap_table->match_stackmap( + new_frame, handler_pc, true, false, CHECK_VERIFY(this)); + if (!match) { + verify_error(bci, + "Stack map does not match the one at exception handler %d", + handler_pc); + return; + } + } + } + } +} + +void ClassVerifier::verify_cp_index(constantPoolHandle cp, int index, TRAPS) { + int nconstants = cp->length(); + if ((index <= 0) || (index >= nconstants)) { + verify_error("Illegal constant pool index %d in class %s", + index, instanceKlass::cast(cp->pool_holder())->external_name()); + return; + } +} + +void ClassVerifier::verify_cp_type( + int index, constantPoolHandle cp, unsigned int types, TRAPS) { + + // In some situations, bytecode rewriting may occur while we're verifying. + // In this case, a constant pool cache exists and some indices refer to that + // instead. Get the original index for the tag check + constantPoolCacheOop cache = cp->cache(); + if (cache != NULL && + ((types == (1 << JVM_CONSTANT_InterfaceMethodref)) || + (types == (1 << JVM_CONSTANT_Methodref)) || + (types == (1 << JVM_CONSTANT_Fieldref)))) { + int native_index = index; + if (Bytes::is_Java_byte_ordering_different()) { + native_index = Bytes::swap_u2(index); + } + assert((native_index >= 0) && (native_index < cache->length()), + "Must be a legal index into the cp cache"); + index = cache->entry_at(native_index)->constant_pool_index(); + } + + verify_cp_index(cp, index, CHECK_VERIFY(this)); + unsigned int tag = cp->tag_at(index).value(); + if ((types & (1 << tag)) == 0) { + verify_error( + "Illegal type at constant pool entry %d in class %s", + index, instanceKlass::cast(cp->pool_holder())->external_name()); + return; + } +} + +void ClassVerifier::verify_cp_class_type( + int index, constantPoolHandle cp, TRAPS) { + verify_cp_index(cp, index, CHECK_VERIFY(this)); + constantTag tag = cp->tag_at(index); + if (!tag.is_klass() && !tag.is_unresolved_klass()) { + verify_error("Illegal type at constant pool entry %d in class %s", + index, instanceKlass::cast(cp->pool_holder())->external_name()); + return; + } +} + +void ClassVerifier::format_error_message( + const char* fmt, int offset, va_list va) { + ResourceMark rm(_thread); + stringStream message(_message, _message_buffer_len); + message.vprint(fmt, va); + if (!_method.is_null()) { + message.print(" in method %s", _method->name_and_sig_as_C_string()); + } + if (offset != -1) { + message.print(" at offset %d", offset); + } +} + +void ClassVerifier::verify_error(u2 offset, const char* fmt, ...) { + _exception_type = vmSymbols::java_lang_VerifyError(); + va_list va; + va_start(va, fmt); + format_error_message(fmt, offset, va); + va_end(va); +} + +void ClassVerifier::verify_error(const char* fmt, ...) { + _exception_type = vmSymbols::java_lang_VerifyError(); + va_list va; + va_start(va, fmt); + format_error_message(fmt, -1, va); + va_end(va); +} + +void ClassVerifier::class_format_error(const char* msg, ...) { + _exception_type = vmSymbols::java_lang_ClassFormatError(); + va_list va; + va_start(va, msg); + format_error_message(msg, -1, va); + va_end(va); +} + +klassOop ClassVerifier::load_class(symbolHandle name, TRAPS) { + // Get current loader and protection domain first. + oop loader = current_class()->class_loader(); + oop protection_domain = current_class()->protection_domain(); + + return SystemDictionary::resolve_or_fail( + name, Handle(THREAD, loader), Handle(THREAD, protection_domain), + true, CHECK_NULL); +} + +bool ClassVerifier::is_protected_access(instanceKlassHandle this_class, + klassOop target_class, + symbolOop field_name, + symbolOop field_sig, + bool is_method) { + No_Safepoint_Verifier nosafepoint; + + // If target class isn't a super class of this class, we don't worry about this case + if (!this_class->is_subclass_of(target_class)) { + return false; + } + // Check if the specified method or field is protected + instanceKlass* target_instance = instanceKlass::cast(target_class); + fieldDescriptor fd; + if (is_method) { + methodOop m = target_instance->uncached_lookup_method(field_name, field_sig); + if (m != NULL && m->is_protected()) { + if (!this_class->is_same_class_package(m->method_holder())) { + return true; + } + } + } else { + klassOop member_klass = target_instance->find_field(field_name, field_sig, &fd); + if(member_klass != NULL && fd.is_protected()) { + if (!this_class->is_same_class_package(member_klass)) { + return true; + } + } + } + return false; +} + +void ClassVerifier::verify_ldc( + int opcode, u2 index, StackMapFrame *current_frame, + constantPoolHandle cp, u2 bci, TRAPS) { + verify_cp_index(cp, index, CHECK_VERIFY(this)); + constantTag tag = cp->tag_at(index); + unsigned int types; + if (opcode == Bytecodes::_ldc || opcode == Bytecodes::_ldc_w) { + if (!tag.is_unresolved_string() && !tag.is_unresolved_klass()) { + types = (1 << JVM_CONSTANT_Integer) | (1 << JVM_CONSTANT_Float) + | (1 << JVM_CONSTANT_String) | (1 << JVM_CONSTANT_Class); + verify_cp_type(index, cp, types, CHECK_VERIFY(this)); + } + } else { + assert(opcode == Bytecodes::_ldc2_w, "must be ldc2_w"); + types = (1 << JVM_CONSTANT_Double) | (1 << JVM_CONSTANT_Long); + verify_cp_type(index, cp, types, CHECK_VERIFY(this)); + } + if (tag.is_string() || tag.is_unresolved_string()) { + current_frame->push_stack( + VerificationType::reference_type( + vmSymbols::java_lang_String()), CHECK_VERIFY(this)); + } else if (tag.is_klass() || tag.is_unresolved_klass()) { + current_frame->push_stack( + VerificationType::reference_type( + vmSymbols::java_lang_Class()), CHECK_VERIFY(this)); + } else if (tag.is_int()) { + current_frame->push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + } else if (tag.is_float()) { + current_frame->push_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); + } else if (tag.is_double()) { + current_frame->push_stack_2( + VerificationType::double_type(), + VerificationType::double2_type(), CHECK_VERIFY(this)); + } else if (tag.is_long()) { + current_frame->push_stack_2( + VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); + } else { + verify_error(bci, "Invalid index in ldc"); + return; + } +} + +void ClassVerifier::verify_switch( + RawBytecodeStream* bcs, u4 code_length, char* code_data, + StackMapFrame* current_frame, StackMapTable* stackmap_table, TRAPS) { + int bci = bcs->bci(); + address bcp = bcs->bcp(); + address aligned_bcp = (address) round_to((intptr_t)(bcp + 1), jintSize); + + // 4639449 & 4647081: padding bytes must be 0 + u2 padding_offset = 1; + while ((bcp + padding_offset) < aligned_bcp) { + if(*(bcp + padding_offset) != 0) { + verify_error(bci, "Nonzero padding byte in lookswitch or tableswitch"); + return; + } + padding_offset++; + } + int default_offset = (int) Bytes::get_Java_u4(aligned_bcp); + int keys, delta; + current_frame->pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + if (bcs->code() == Bytecodes::_tableswitch) { + jint low = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize); + jint high = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize); + if (low > high) { + verify_error(bci, + "low must be less than or equal to high in tableswitch"); + return; + } + keys = high - low + 1; + if (keys < 0) { + verify_error(bci, "too many keys in tableswitch"); + return; + } + delta = 1; + } else { + keys = (int)Bytes::get_Java_u4(aligned_bcp + jintSize); + if (keys < 0) { + verify_error(bci, "number of keys in lookupswitch less than 0"); + return; + } + delta = 2; + // Make sure that the lookupswitch items are sorted + for (int i = 0; i < (keys - 1); i++) { + jint this_key = Bytes::get_Java_u4(aligned_bcp + (2+2*i)*jintSize); + jint next_key = Bytes::get_Java_u4(aligned_bcp + (2+2*i+2)*jintSize); + if (this_key >= next_key) { + verify_error(bci, "Bad lookupswitch instruction"); + return; + } + } + } + int target = bci + default_offset; + stackmap_table->check_jump_target(current_frame, target, CHECK_VERIFY(this)); + for (int i = 0; i < keys; i++) { + target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); + stackmap_table->check_jump_target( + current_frame, target, CHECK_VERIFY(this)); + } +} + +bool ClassVerifier::name_in_supers( + symbolOop ref_name, instanceKlassHandle current) { + klassOop super = current->super(); + while (super != NULL) { + if (super->klass_part()->name() == ref_name) { + return true; + } + super = super->klass_part()->super(); + } + return false; +} + +void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs, + StackMapFrame* current_frame, + constantPoolHandle cp, + TRAPS) { + u2 index = bcs->get_index_big(); + verify_cp_type(index, cp, 1 << JVM_CONSTANT_Fieldref, CHECK_VERIFY(this)); + + // Get field name and signature + symbolHandle field_name = symbolHandle(THREAD, cp->name_ref_at(index)); + symbolHandle field_sig = symbolHandle(THREAD, cp->signature_ref_at(index)); + + if (!SignatureVerifier::is_valid_type_signature(field_sig)) { + class_format_error( + "Invalid signature for field in class %s referenced " + "from constant pool index %d", _klass->external_name(), index); + return; + } + + // Get referenced class type + VerificationType ref_class_type = cp_ref_index_to_type( + index, cp, CHECK_VERIFY(this)); + if (!ref_class_type.is_object()) { + verify_error( + "Expecting reference to class in class %s at constant pool index %d", + _klass->external_name(), index); + return; + } + VerificationType target_class_type = ref_class_type; + + assert(sizeof(VerificationType) == sizeof(uintptr_t), + "buffer type must match VerificationType size"); + uintptr_t field_type_buffer[2]; + VerificationType* field_type = (VerificationType*)field_type_buffer; + // If we make a VerificationType[2] array directly, the compiler calls + // to the c-runtime library to do the allocation instead of just + // stack allocating it. Plus it would run constructors. This shows up + // in performance profiles. + + SignatureStream sig_stream(field_sig, false); + VerificationType stack_object_type; + int n = change_sig_to_verificationType( + &sig_stream, field_type, CHECK_VERIFY(this)); + u2 bci = bcs->bci(); + bool is_assignable; + switch (bcs->code()) { + case Bytecodes::_getstatic: { + for (int i = 0; i < n; i++) { + current_frame->push_stack(field_type[i], CHECK_VERIFY(this)); + } + break; + } + case Bytecodes::_putstatic: { + for (int i = n - 1; i >= 0; i--) { + current_frame->pop_stack(field_type[i], CHECK_VERIFY(this)); + } + break; + } + case Bytecodes::_getfield: { + stack_object_type = current_frame->pop_stack( + target_class_type, CHECK_VERIFY(this)); + for (int i = 0; i < n; i++) { + current_frame->push_stack(field_type[i], CHECK_VERIFY(this)); + } + goto check_protected; + } + case Bytecodes::_putfield: { + for (int i = n - 1; i >= 0; i--) { + current_frame->pop_stack(field_type[i], CHECK_VERIFY(this)); + } + stack_object_type = current_frame->pop_stack(CHECK_VERIFY(this)); + + // The JVMS 2nd edition allows field initialization before the superclass + // initializer, if the field is defined within the current class. + fieldDescriptor fd; + if (stack_object_type == VerificationType::uninitialized_this_type() && + target_class_type.equals(current_type()) && + _klass->find_local_field(field_name(), field_sig(), &fd)) { + stack_object_type = current_type(); + } + is_assignable = target_class_type.is_assignable_from( + stack_object_type, current_class(), CHECK_VERIFY(this)); + if (!is_assignable) { + verify_error(bci, "Bad type on operand stack in putfield"); + return; + } + } + check_protected: { + if (_this_type == stack_object_type) + break; // stack_object_type must be assignable to _current_class_type + symbolHandle ref_class_name = symbolHandle(THREAD, + cp->klass_name_at(cp->klass_ref_index_at(index))); + if (!name_in_supers(ref_class_name(), current_class())) + // stack_object_type must be assignable to _current_class_type since: + // 1. stack_object_type must be assignable to ref_class. + // 2. ref_class must be _current_class or a subclass of it. It can't + // be a superclass of it. See revised JVMS 5.4.4. + break; + + klassOop ref_class_oop = load_class(ref_class_name, CHECK); + if (is_protected_access(current_class(), ref_class_oop, field_name(), + field_sig(), false)) { + // It's protected access, check if stack object is assignable to + // current class. + is_assignable = current_type().is_assignable_from( + stack_object_type, current_class(), CHECK_VERIFY(this)); + if (!is_assignable) { + verify_error(bci, "Bad access to protected data in getfield"); + return; + } + } + break; + } + default: ShouldNotReachHere(); + } +} + +void ClassVerifier::verify_invoke_init( + RawBytecodeStream* bcs, VerificationType ref_class_type, + StackMapFrame* current_frame, u4 code_length, bool *this_uninit, + constantPoolHandle cp, TRAPS) { + u2 bci = bcs->bci(); + VerificationType type = current_frame->pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + if (type == VerificationType::uninitialized_this_type()) { + // The method must be an method of either this class, or one of its + // superclasses + klassOop oop = current_class()(); + Klass* klass = oop->klass_part(); + while (klass != NULL && ref_class_type.name() != klass->name()) { + klass = klass->super()->klass_part(); + } + if (klass == NULL) { + verify_error(bci, "Bad method call"); + return; + } + current_frame->initialize_object(type, current_type()); + *this_uninit = true; + } else if (type.is_uninitialized()) { + u2 new_offset = type.bci(); + address new_bcp = bcs->bcp() - bci + new_offset; + if (new_offset > (code_length - 3) || (*new_bcp) != Bytecodes::_new) { + verify_error(new_offset, "Expecting new instruction"); + return; + } + u2 new_class_index = Bytes::get_Java_u2(new_bcp + 1); + verify_cp_class_type(new_class_index, cp, CHECK_VERIFY(this)); + + // The method must be an method of the indicated class + VerificationType new_class_type = cp_index_to_type( + new_class_index, cp, CHECK_VERIFY(this)); + if (!new_class_type.equals(ref_class_type)) { + verify_error(bci, "Call to wrong method"); + return; + } + // According to the VM spec, if the referent class is a superclass of the + // current class, and is in a different runtime package, and the method is + // protected, then the objectref must be the current class or a subclass + // of the current class. + VerificationType objectref_type = new_class_type; + if (name_in_supers(ref_class_type.name(), current_class())) { + klassOop ref_klass = load_class( + ref_class_type.name(), CHECK_VERIFY(this)); + methodOop m = instanceKlass::cast(ref_klass)->uncached_lookup_method( + vmSymbols::object_initializer_name(), + cp->signature_ref_at(bcs->get_index_big())); + instanceKlassHandle mh(THREAD, m->method_holder()); + if (m->is_protected() && !mh->is_same_class_package(_klass())) { + bool assignable = current_type().is_assignable_from( + objectref_type, current_class(), CHECK_VERIFY(this)); + if (!assignable) { + verify_error(bci, "Bad access to protected method"); + return; + } + } + } + current_frame->initialize_object(type, new_class_type); + } else { + verify_error(bci, "Bad operand type when invoking "); + return; + } +} + +void ClassVerifier::verify_invoke_instructions( + RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame, + bool *this_uninit, VerificationType return_type, + constantPoolHandle cp, TRAPS) { + // Make sure the constant pool item is the right type + u2 index = bcs->get_index_big(); + Bytecodes::Code opcode = bcs->code(); + unsigned int types = (opcode == Bytecodes::_invokeinterface + ? 1 << JVM_CONSTANT_InterfaceMethodref + : 1 << JVM_CONSTANT_Methodref); + verify_cp_type(index, cp, types, CHECK_VERIFY(this)); + + // Get method name and signature + symbolHandle method_name(THREAD, cp->name_ref_at(index)); + symbolHandle method_sig(THREAD, cp->signature_ref_at(index)); + + if (!SignatureVerifier::is_valid_method_signature(method_sig)) { + class_format_error( + "Invalid method signature in class %s referenced " + "from constant pool index %d", _klass->external_name(), index); + return; + } + + // Get referenced class type + VerificationType ref_class_type = cp_ref_index_to_type( + index, cp, CHECK_VERIFY(this)); + + // For a small signature length, we just allocate 128 bytes instead + // of parsing the signature once to find its size. + // -3 is for '(', ')' and return descriptor; multiply by 2 is for + // longs/doubles to be consertive. + assert(sizeof(VerificationType) == sizeof(uintptr_t), + "buffer type must match VerificationType size"); + uintptr_t on_stack_sig_types_buffer[128]; + // If we make a VerificationType[128] array directly, the compiler calls + // to the c-runtime library to do the allocation instead of just + // stack allocating it. Plus it would run constructors. This shows up + // in performance profiles. + + VerificationType* sig_types; + int size = (method_sig->utf8_length() - 3) * 2; + if (size > 128) { + // Long and double occupies two slots here. + ArgumentSizeComputer size_it(method_sig); + size = size_it.size(); + sig_types = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, VerificationType, size); + } else{ + sig_types = (VerificationType*)on_stack_sig_types_buffer; + } + SignatureStream sig_stream(method_sig); + int sig_i = 0; + while (!sig_stream.at_return_type()) { + sig_i += change_sig_to_verificationType( + &sig_stream, &sig_types[sig_i], CHECK_VERIFY(this)); + sig_stream.next(); + } + int nargs = sig_i; + +#ifdef ASSERT + { + ArgumentSizeComputer size_it(method_sig); + assert(nargs == size_it.size(), "Argument sizes do not match"); + assert(nargs <= (method_sig->utf8_length() - 3) * 2, "estimate of max size isn't conservative enough"); + } +#endif + + // Check instruction operands + u2 bci = bcs->bci(); + if (opcode == Bytecodes::_invokeinterface) { + address bcp = bcs->bcp(); + // 4905268: count operand in invokeinterface should be nargs+1, not nargs. + // JSR202 spec: The count operand of an invokeinterface instruction is valid if it is + // the difference between the size of the operand stack before and after the instruction + // executes. + if (*(bcp+3) != (nargs+1)) { + verify_error(bci, "Inconsistent args count operand in invokeinterface"); + return; + } + if (*(bcp+4) != 0) { + verify_error(bci, "Fourth operand byte of invokeinterface must be zero"); + return; + } + } + + if (method_name->byte_at(0) == '<') { + // Make sure can only be invoked by invokespecial + if (opcode != Bytecodes::_invokespecial || + method_name() != vmSymbols::object_initializer_name()) { + verify_error(bci, "Illegal call to internal method"); + return; + } + } else if (opcode == Bytecodes::_invokespecial + && !ref_class_type.equals(current_type()) + && !ref_class_type.equals(VerificationType::reference_type( + current_class()->super()->klass_part()->name()))) { + bool subtype = ref_class_type.is_assignable_from( + current_type(), current_class(), CHECK_VERIFY(this)); + if (!subtype) { + verify_error(bci, "Bad invokespecial instruction: " + "current class isn't assignable to reference class."); + return; + } + } + // Match method descriptor with operand stack + for (int i = nargs - 1; i >= 0; i--) { // Run backwards + current_frame->pop_stack(sig_types[i], CHECK_VERIFY(this)); + } + // Check objectref on operand stack + if (opcode != Bytecodes::_invokestatic) { + if (method_name() == vmSymbols::object_initializer_name()) { // method + verify_invoke_init(bcs, ref_class_type, current_frame, + code_length, this_uninit, cp, CHECK_VERIFY(this)); + } else { // other methods + // Ensures that target class is assignable to method class. + if (opcode == Bytecodes::_invokespecial) { + current_frame->pop_stack(current_type(), CHECK_VERIFY(this)); + } else if (opcode == Bytecodes::_invokevirtual) { + VerificationType stack_object_type = + current_frame->pop_stack(ref_class_type, CHECK_VERIFY(this)); + if (current_type() != stack_object_type) { + assert(cp->cache() == NULL, "not rewritten yet"); + symbolHandle ref_class_name = symbolHandle(THREAD, + cp->klass_name_at(cp->klass_ref_index_at(index))); + // See the comments in verify_field_instructions() for + // the rationale behind this. + if (name_in_supers(ref_class_name(), current_class())) { + klassOop ref_class = load_class(ref_class_name, CHECK); + if (is_protected_access( + _klass, ref_class, method_name(), method_sig(), true)) { + // It's protected access, check if stack object is + // assignable to current class. + bool is_assignable = current_type().is_assignable_from( + stack_object_type, current_class(), CHECK_VERIFY(this)); + if (!is_assignable) { + if (ref_class_type.name() == vmSymbols::java_lang_Object() + && stack_object_type.is_array() + && method_name() == vmSymbols::clone_name()) { + // Special case: arrays pretend to implement public Object + // clone(). + } else { + verify_error(bci, + "Bad access to protected data in invokevirtual"); + return; + } + } + } + } + } + } else { + assert(opcode == Bytecodes::_invokeinterface, "Unexpected opcode encountered"); + current_frame->pop_stack(ref_class_type, CHECK_VERIFY(this)); + } + } + } + // Push the result type. + if (sig_stream.type() != T_VOID) { + if (method_name() == vmSymbols::object_initializer_name()) { + // method must have a void return type + verify_error(bci, "Return type must be void in method"); + return; + } + VerificationType return_type[2]; + int n = change_sig_to_verificationType( + &sig_stream, return_type, CHECK_VERIFY(this)); + for (int i = 0; i < n; i++) { + current_frame->push_stack(return_type[i], CHECK_VERIFY(this)); // push types backwards + } + } +} + +VerificationType ClassVerifier::get_newarray_type( + u2 index, u2 bci, TRAPS) { + const char* from_bt[] = { + NULL, NULL, NULL, NULL, "[Z", "[C", "[F", "[D", "[B", "[S", "[I", "[J", + }; + if (index < T_BOOLEAN || index > T_LONG) { + verify_error(bci, "Illegal newarray instruction"); + return VerificationType::bogus_type(); + } + + // from_bt[index] contains the array signature which has a length of 2 + symbolHandle sig = oopFactory::new_symbol_handle( + from_bt[index], 2, CHECK_(VerificationType::bogus_type())); + return VerificationType::reference_type(sig); +} + +void ClassVerifier::verify_anewarray( + u2 index, constantPoolHandle cp, StackMapFrame* current_frame, TRAPS) { + verify_cp_class_type(index, cp, CHECK_VERIFY(this)); + current_frame->pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + + VerificationType component_type = + cp_index_to_type(index, cp, CHECK_VERIFY(this)); + ResourceMark rm(THREAD); + int length; + char* arr_sig_str; + if (component_type.is_array()) { // it's an array + const char* component_name = component_type.name()->as_utf8(); + // add one dimension to component + length = (int)strlen(component_name) + 1; + arr_sig_str = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, length); + arr_sig_str[0] = '['; + strncpy(&arr_sig_str[1], component_name, length - 1); + } else { // it's an object or interface + const char* component_name = component_type.name()->as_utf8(); + // add one dimension to component with 'L' prepended and ';' postpended. + length = (int)strlen(component_name) + 3; + arr_sig_str = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, length); + arr_sig_str[0] = '['; + arr_sig_str[1] = 'L'; + strncpy(&arr_sig_str[2], component_name, length - 2); + arr_sig_str[length - 1] = ';'; + } + symbolHandle arr_sig = oopFactory::new_symbol_handle( + arr_sig_str, length, CHECK_VERIFY(this)); + VerificationType new_array_type = VerificationType::reference_type(arr_sig); + current_frame->push_stack(new_array_type, CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_iload(u2 index, StackMapFrame* current_frame, TRAPS) { + current_frame->get_local( + index, VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame->push_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_lload(u2 index, StackMapFrame* current_frame, TRAPS) { + current_frame->get_local_2( + index, VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); + current_frame->push_stack_2( + VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_fload(u2 index, StackMapFrame* current_frame, TRAPS) { + current_frame->get_local( + index, VerificationType::float_type(), CHECK_VERIFY(this)); + current_frame->push_stack( + VerificationType::float_type(), CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_dload(u2 index, StackMapFrame* current_frame, TRAPS) { + current_frame->get_local_2( + index, VerificationType::double_type(), + VerificationType::double2_type(), CHECK_VERIFY(this)); + current_frame->push_stack_2( + VerificationType::double_type(), + VerificationType::double2_type(), CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_aload(u2 index, StackMapFrame* current_frame, TRAPS) { + VerificationType type = current_frame->get_local( + index, VerificationType::reference_check(), CHECK_VERIFY(this)); + current_frame->push_stack(type, CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_istore(u2 index, StackMapFrame* current_frame, TRAPS) { + current_frame->pop_stack( + VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame->set_local( + index, VerificationType::integer_type(), CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_lstore(u2 index, StackMapFrame* current_frame, TRAPS) { + current_frame->pop_stack_2( + VerificationType::long2_type(), + VerificationType::long_type(), CHECK_VERIFY(this)); + current_frame->set_local_2( + index, VerificationType::long_type(), + VerificationType::long2_type(), CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_fstore(u2 index, StackMapFrame* current_frame, TRAPS) { + current_frame->pop_stack(VerificationType::float_type(), CHECK_VERIFY(this)); + current_frame->set_local( + index, VerificationType::float_type(), CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_dstore(u2 index, StackMapFrame* current_frame, TRAPS) { + current_frame->pop_stack_2( + VerificationType::double2_type(), + VerificationType::double_type(), CHECK_VERIFY(this)); + current_frame->set_local_2( + index, VerificationType::double_type(), + VerificationType::double2_type(), CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_astore(u2 index, StackMapFrame* current_frame, TRAPS) { + VerificationType type = current_frame->pop_stack( + VerificationType::reference_check(), CHECK_VERIFY(this)); + current_frame->set_local(index, type, CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_iinc(u2 index, StackMapFrame* current_frame, TRAPS) { + VerificationType type = current_frame->get_local( + index, VerificationType::integer_type(), CHECK_VERIFY(this)); + current_frame->set_local(index, type, CHECK_VERIFY(this)); +} + +void ClassVerifier::verify_return_value( + VerificationType return_type, VerificationType type, u2 bci, TRAPS) { + if (return_type == VerificationType::bogus_type()) { + verify_error(bci, "Method expects a return value"); + return; + } + bool match = return_type.is_assignable_from(type, _klass, CHECK_VERIFY(this)); + if (!match) { + verify_error(bci, "Bad return type"); + return; + } +} diff --git a/hotspot/src/share/vm/classfile/verifier.hpp b/hotspot/src/share/vm/classfile/verifier.hpp new file mode 100644 index 00000000000..2c06b1ee61c --- /dev/null +++ b/hotspot/src/share/vm/classfile/verifier.hpp @@ -0,0 +1,239 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The verifier class +class Verifier : AllStatic { + public: + enum { STACKMAP_ATTRIBUTE_MAJOR_VERSION = 50 }; + typedef enum { ThrowException, NoException } Mode; + + /** + * Verify the bytecodes for a class. If 'throw_exception' is true + * then the appropriate VerifyError or ClassFormatError will be thrown. + * Otherwise, no exception is thrown and the return indicates the + * error. + */ + static bool verify(instanceKlassHandle klass, Mode mode, TRAPS); + + // Return false if the class is loaded by the bootstrap loader. + static bool should_verify_for(oop class_loader); + + // Relax certain verifier checks to enable some broken 1.1 apps to run on 1.2. + static bool relax_verify_for(oop class_loader); + + private: + static bool is_eligible_for_verification(instanceKlassHandle klass); + static symbolHandle inference_verify( + instanceKlassHandle klass, char* msg, size_t msg_len, TRAPS); +}; + +class RawBytecodeStream; +class StackMapFrame; +class StackMapTable; + +// Summary of verifier's memory usage: +// StackMapTable is stack allocated. +// StackMapFrame are resource allocated. There is one ResourceMark +// for each method. +// There is one mutable StackMapFrame (current_frame) which is updated +// by abstract bytecode interpretation. frame_in_exception_handler() returns +// a frame that has a mutable one-item stack (ready for pushing the +// catch type exception object). All the other StackMapFrame's +// are immutable (including their locals and stack arrays) after +// their constructions. +// locals/stack arrays in StackMapFrame are resource allocated. +// locals/stack arrays can be shared between StackMapFrame's, except +// the mutable StackMapFrame (current_frame). +// Care needs to be taken to make sure resource objects don't outlive +// the lifetime of their ResourceMark. + +// These macros are used similarly to CHECK macros but also check +// the status of the verifier and return if that has an error. +#define CHECK_VERIFY(verifier) \ + CHECK); if ((verifier)->has_error()) return; (0 +#define CHECK_VERIFY_(verifier, result) \ + CHECK_(result)); if ((verifier)->has_error()) return (result); (0 + +// A new instance of this class is created for each class being verified +class ClassVerifier : public StackObj { + private: + Thread* _thread; + symbolHandle _exception_type; + char* _message; + size_t _message_buffer_len; + + void verify_method(methodHandle method, TRAPS); + char* generate_code_data(methodHandle m, u4 code_length, TRAPS); + void verify_exception_handler_table(u4 code_length, char* code_data, int& min, int& max, TRAPS); + void verify_local_variable_table(u4 code_length, char* code_data, TRAPS); + + VerificationType cp_ref_index_to_type( + int index, constantPoolHandle cp, TRAPS) { + return cp_index_to_type(cp->klass_ref_index_at(index), cp, THREAD); + } + + bool is_protected_access( + instanceKlassHandle this_class, klassOop target_class, + symbolOop field_name, symbolOop field_sig, bool is_method); + + void verify_cp_index(constantPoolHandle cp, int index, TRAPS); + void verify_cp_type( + int index, constantPoolHandle cp, unsigned int types, TRAPS); + void verify_cp_class_type(int index, constantPoolHandle cp, TRAPS); + + u2 verify_stackmap_table( + u2 stackmap_index, u2 bci, StackMapFrame* current_frame, + StackMapTable* stackmap_table, bool no_control_flow, TRAPS); + + void verify_exception_handler_targets( + u2 bci, bool this_uninit, StackMapFrame* current_frame, + StackMapTable* stackmap_table, TRAPS); + + void verify_ldc( + int opcode, u2 index, StackMapFrame *current_frame, + constantPoolHandle cp, u2 bci, TRAPS); + + void verify_switch( + RawBytecodeStream* bcs, u4 code_length, char* code_data, + StackMapFrame* current_frame, StackMapTable* stackmap_table, TRAPS); + + void verify_field_instructions( + RawBytecodeStream* bcs, StackMapFrame* current_frame, + constantPoolHandle cp, TRAPS); + + void verify_invoke_init( + RawBytecodeStream* bcs, VerificationType ref_class_type, + StackMapFrame* current_frame, u4 code_length, bool* this_uninit, + constantPoolHandle cp, TRAPS); + + void verify_invoke_instructions( + RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame, + bool* this_uninit, VerificationType return_type, + constantPoolHandle cp, TRAPS); + + VerificationType get_newarray_type(u2 index, u2 bci, TRAPS); + void verify_anewarray( + u2 index, constantPoolHandle cp, StackMapFrame* current_frame, TRAPS); + void verify_return_value( + VerificationType return_type, VerificationType type, u2 offset, TRAPS); + + void verify_iload (u2 index, StackMapFrame* current_frame, TRAPS); + void verify_lload (u2 index, StackMapFrame* current_frame, TRAPS); + void verify_fload (u2 index, StackMapFrame* current_frame, TRAPS); + void verify_dload (u2 index, StackMapFrame* current_frame, TRAPS); + void verify_aload (u2 index, StackMapFrame* current_frame, TRAPS); + void verify_istore(u2 index, StackMapFrame* current_frame, TRAPS); + void verify_lstore(u2 index, StackMapFrame* current_frame, TRAPS); + void verify_fstore(u2 index, StackMapFrame* current_frame, TRAPS); + void verify_dstore(u2 index, StackMapFrame* current_frame, TRAPS); + void verify_astore(u2 index, StackMapFrame* current_frame, TRAPS); + void verify_iinc (u2 index, StackMapFrame* current_frame, TRAPS); + + bool name_in_supers(symbolOop ref_name, instanceKlassHandle current); + + instanceKlassHandle _klass; // the class being verified + methodHandle _method; // current method being verified + VerificationType _this_type; // the verification type of the current class + + public: + enum { + BYTECODE_OFFSET = 1, + NEW_OFFSET = 2 + }; + + // constructor + ClassVerifier(instanceKlassHandle klass, char* msg, size_t msg_len, TRAPS); + + // destructor + ~ClassVerifier(); + + Thread* thread() { return _thread; } + methodHandle method() { return _method; } + instanceKlassHandle current_class() const { return _klass; } + VerificationType current_type() const { return _this_type; } + + // Verifies the class. If a verify or class file format error occurs, + // the '_exception_name' symbols will set to the exception name and + // the message_buffer will be filled in with the exception message. + void verify_class(TRAPS); + + // Return status modes + symbolHandle result() const { return _exception_type; } + bool has_error() const { return !(result().is_null()); } + + // Called when verify or class format errors are encountered. + // May throw an exception based upon the mode. + void verify_error(u2 offset, const char* fmt, ...); + void verify_error(const char* fmt, ...); + void class_format_error(const char* fmt, ...); + void format_error_message(const char* fmt, int offset, va_list args); + + klassOop load_class(symbolHandle name, TRAPS); + + int change_sig_to_verificationType( + SignatureStream* sig_type, VerificationType* inference_type, TRAPS); + + VerificationType cp_index_to_type(int index, constantPoolHandle cp, TRAPS) { + return VerificationType::reference_type( + symbolHandle(THREAD, cp->klass_name_at(index))); + } + + static bool _verify_verbose; // for debugging +}; + +inline int ClassVerifier::change_sig_to_verificationType( + SignatureStream* sig_type, VerificationType* inference_type, TRAPS) { + BasicType bt = sig_type->type(); + switch (bt) { + case T_OBJECT: + case T_ARRAY: + { + symbolOop name = sig_type->as_symbol(CHECK_0); + *inference_type = + VerificationType::reference_type(symbolHandle(THREAD, name)); + return 1; + } + case T_LONG: + *inference_type = VerificationType::long_type(); + *++inference_type = VerificationType::long2_type(); + return 2; + case T_DOUBLE: + *inference_type = VerificationType::double_type(); + *++inference_type = VerificationType::double2_type(); + return 2; + case T_INT: + case T_BOOLEAN: + case T_BYTE: + case T_CHAR: + case T_SHORT: + *inference_type = VerificationType::integer_type(); + return 1; + case T_FLOAT: + *inference_type = VerificationType::float_type(); + return 1; + default: + ShouldNotReachHere(); + return 1; + } +} diff --git a/hotspot/src/share/vm/classfile/vmSymbols.cpp b/hotspot/src/share/vm/classfile/vmSymbols.cpp new file mode 100644 index 00000000000..36ab6d9ddd0 --- /dev/null +++ b/hotspot/src/share/vm/classfile/vmSymbols.cpp @@ -0,0 +1,492 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vmSymbols.cpp.incl" + + +symbolOop vmSymbols::_symbols[vmSymbols::SID_LIMIT]; + +symbolOop vmSymbols::_type_signatures[T_VOID+1] = { NULL /*, NULL...*/ }; + +inline int compare_symbol(symbolOop a, symbolOop b) { + if (a == b) return 0; + // follow the natural address order: + return (address)a > (address)b ? +1 : -1; +} + +static vmSymbols::SID vm_symbol_index[vmSymbols::SID_LIMIT]; +extern "C" { + static int compare_vmsymbol_sid(const void* void_a, const void* void_b) { + symbolOop a = vmSymbols::symbol_at(*((vmSymbols::SID*) void_a)); + symbolOop b = vmSymbols::symbol_at(*((vmSymbols::SID*) void_b)); + return compare_symbol(a, b); + } +} + +#ifndef PRODUCT +#define VM_SYMBOL_ENUM_NAME_BODY(name, string) #name "\0" +static const char* vm_symbol_enum_names = + VM_SYMBOLS_DO(VM_SYMBOL_ENUM_NAME_BODY, VM_ALIAS_IGNORE) + "\0"; +static const char* vm_symbol_enum_name(vmSymbols::SID sid) { + const char* string = &vm_symbol_enum_names[0]; + int skip = (int)sid - (int)vmSymbols::FIRST_SID; + for (; skip != 0; skip--) { + size_t skiplen = strlen(string); + if (skiplen == 0) return ""; // overflow + string += skiplen+1; + } + return string; +} +#endif //PRODUCT + +// Put all the VM symbol strings in one place. +// Makes for a more compact libjvm. +#define VM_SYMBOL_BODY(name, string) string "\0" +static const char* vm_symbol_bodies = VM_SYMBOLS_DO(VM_SYMBOL_BODY, VM_ALIAS_IGNORE); + +void vmSymbols::initialize(TRAPS) { + assert((int)SID_LIMIT <= (1< (1<print("*** Duplicate VM symbol SIDs %s(%d) and %s(%d): \"", + vm_symbol_enum_name((SID)i2), i2, + vm_symbol_enum_name((SID)i1), i1); + sym->print_symbol_on(tty); + tty->print_cr("\""); + } + } + } +#endif //ASSERT + + // Create an index for find_id: + { + for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { + vm_symbol_index[index] = (SID)index; + } + int num_sids = SID_LIMIT-FIRST_SID; + qsort(&vm_symbol_index[FIRST_SID], num_sids, sizeof(vm_symbol_index[0]), + compare_vmsymbol_sid); + } + +#ifdef ASSERT + { + // Spot-check correspondence between strings, symbols, and enums: + assert(_symbols[NO_SID] == NULL, "must be"); + const char* str = "java/lang/Object"; + symbolOop sym = oopFactory::new_symbol(str, CHECK); + assert(strcmp(str, (char*)sym->base()) == 0, ""); + assert(sym == java_lang_Object(), ""); + SID sid = VM_SYMBOL_ENUM_NAME(java_lang_Object); + assert(find_sid(sym) == sid, ""); + assert(symbol_at(sid) == sym, ""); + + // Make sure find_sid produces the right answer in each case. + for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { + sym = symbol_at((SID)index); + sid = find_sid(sym); + assert(sid == (SID)index, "symbol index works"); + // Note: If there are duplicates, this assert will fail. + // A "Duplicate VM symbol" message will have already been printed. + } + + // The string "format" happens (at the moment) not to be a vmSymbol, + // though it is a method name in java.lang.String. + str = "format"; + sym = oopFactory::new_symbol(str, CHECK); + sid = find_sid(sym); + assert(sid == NO_SID, "symbol index works (negative test)"); + } +#endif +} + + +#ifndef PRODUCT +const char* vmSymbols::name_for(vmSymbols::SID sid) { + if (sid == NO_SID) + return "NO_SID"; + const char* string = &vm_symbol_bodies[0]; + for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { + if (index == (int)sid) + return string; + string += strlen(string); // skip string body + string += 1; // skip trailing null + } + return "BAD_SID"; +} +#endif + + + +void vmSymbols::oops_do(OopClosure* f, bool do_all) { + for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { + f->do_oop((oop*) &_symbols[index]); + } + for (int i = 0; i < T_VOID+1; i++) { + if (_type_signatures[i] != NULL) { + assert(i >= T_BOOLEAN, "checking"); + f->do_oop((oop*)&_type_signatures[i]); + } else if (do_all) { + f->do_oop((oop*)&_type_signatures[i]); + } + } +} + + +BasicType vmSymbols::signature_type(symbolOop s) { + assert(s != NULL, "checking"); + for (int i = T_BOOLEAN; i < T_VOID+1; i++) { + if (s == _type_signatures[i]) { + return (BasicType)i; + } + } + return T_OBJECT; +} + + +static int mid_hint = (int)vmSymbols::FIRST_SID+1; + +#ifndef PRODUCT +static int find_sid_calls, find_sid_probes; +// (Typical counts are calls=7000 and probes=17000.) +#endif + +vmSymbols::SID vmSymbols::find_sid(symbolOop symbol) { + // Handle the majority of misses by a bounds check. + // Then, use a binary search over the index. + // Expected trip count is less than log2_SID_LIMIT, about eight. + // This is slow but acceptable, given that calls are not + // dynamically common. (methodOop::intrinsic_id has a cache.) + NOT_PRODUCT(find_sid_calls++); + int min = (int)FIRST_SID, max = (int)SID_LIMIT - 1; + SID sid = NO_SID, sid1; + int cmp1; + sid1 = vm_symbol_index[min]; + cmp1 = compare_symbol(symbol, symbol_at(sid1)); + if (cmp1 <= 0) { // before the first + if (cmp1 == 0) sid = sid1; + } else { + sid1 = vm_symbol_index[max]; + cmp1 = compare_symbol(symbol, symbol_at(sid1)); + if (cmp1 >= 0) { // after the last + if (cmp1 == 0) sid = sid1; + } else { + // After checking the extremes, do a binary search. + ++min; --max; // endpoints are done + int mid = mid_hint; // start at previous success + while (max >= min) { + assert(mid >= min && mid <= max, ""); + NOT_PRODUCT(find_sid_probes++); + sid1 = vm_symbol_index[mid]; + cmp1 = compare_symbol(symbol, symbol_at(sid1)); + if (cmp1 == 0) { + mid_hint = mid; + sid = sid1; + break; + } + if (cmp1 < 0) + max = mid - 1; // symbol < symbol_at(sid) + else + min = mid + 1; + + // Pick a new probe point: + mid = (max + min) / 2; + } + } + } + +#ifdef ASSERT + // Perform the exhaustive self-check the first 1000 calls, + // and every 100 calls thereafter. + static int find_sid_check_count = -2000; + if ((uint)++find_sid_check_count > (uint)100) { + if (find_sid_check_count > 0) find_sid_check_count = 0; + + // Make sure this is the right answer, using linear search. + // (We have already proven that there are no duplicates in the list.) + SID sid2 = NO_SID; + for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { + symbolOop sym2 = symbol_at((SID)index); + if (sym2 == symbol) { + sid2 = (SID)index; + break; + } + } + // Unless it's a duplicate, assert that the sids are the same. + if (_symbols[sid] != _symbols[sid2]) { + assert(sid == sid2, "binary same as linear search"); + } + } +#endif //ASSERT + + return sid; +} + + +#define VM_INTRINSIC_INITIALIZE(id, klass, name, sig, flags) #id "\0" +static const char* vm_intrinsic_name_bodies = + VM_INTRINSICS_DO(VM_INTRINSIC_INITIALIZE, + VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE); + +static const char* vm_intrinsic_name_table[vmIntrinsics::ID_LIMIT]; + +const char* vmIntrinsics::name_at(vmIntrinsics::ID id) { + const char** nt = &vm_intrinsic_name_table[0]; + if (nt[_none] == NULL) { + char* string = (char*) &vm_intrinsic_name_bodies[0]; + for (int index = FIRST_ID; index < ID_LIMIT; index++) { + nt[index] = string; + string += strlen(string); // skip string body + string += 1; // skip trailing null + } + assert(!strcmp(nt[_hashCode], "_hashCode"), "lined up"); + nt[_none] = "_none"; + } + if ((uint)id < (uint)ID_LIMIT) + return vm_intrinsic_name_table[(uint)id]; + else + return "(unknown intrinsic)"; +} + +// These are flag-matching functions: +inline bool match_F_R(jshort flags) { + const int req = 0; + const int neg = JVM_ACC_STATIC | JVM_ACC_SYNCHRONIZED; + return (flags & (req | neg)) == req; +} +inline bool match_F_RN(jshort flags) { + const int req = JVM_ACC_NATIVE; + const int neg = JVM_ACC_STATIC | JVM_ACC_SYNCHRONIZED; + return (flags & (req | neg)) == req; +} +inline bool match_F_S(jshort flags) { + const int req = JVM_ACC_STATIC; + const int neg = JVM_ACC_SYNCHRONIZED; + return (flags & (req | neg)) == req; +} +inline bool match_F_SN(jshort flags) { + const int req = JVM_ACC_STATIC | JVM_ACC_NATIVE; + const int neg = JVM_ACC_SYNCHRONIZED; + return (flags & (req | neg)) == req; +} + +// These are for forming case labels: +#define ID3(x, y, z) (( jint)(z) + \ + ((jint)(y) << vmSymbols::log2_SID_LIMIT) + \ + ((jint)(x) << (2*vmSymbols::log2_SID_LIMIT)) ) +#define SID_ENUM(n) vmSymbols::VM_SYMBOL_ENUM_NAME(n) + +vmIntrinsics::ID vmIntrinsics::find_id(vmSymbols::SID holder, + vmSymbols::SID name, + vmSymbols::SID sig, + jshort flags) { + assert((int)vmSymbols::SID_LIMIT <= (1<name() == n && + m->signature() == s); +} + +static vmIntrinsics::ID match_method_with_klass(methodOop m, symbolOop mk) { +#define VM_INTRINSIC_MATCH(id, klassname, namepart, sigpart, flags) \ + { symbolOop k = vmSymbols::klassname(); \ + if (mk == k) { \ + symbolOop n = vmSymbols::namepart(); \ + symbolOop s = vmSymbols::sigpart(); \ + if (match_method(m, n, s)) \ + return vmIntrinsics::id; \ + } } + VM_INTRINSICS_DO(VM_INTRINSIC_MATCH, + VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE); + return vmIntrinsics::_none; +#undef VM_INTRINSIC_MATCH +} + +void vmIntrinsics::verify_method(ID actual_id, methodOop m) { + symbolOop mk = Klass::cast(m->method_holder())->name(); + ID declared_id = match_method_with_klass(m, mk); + + if (declared_id == actual_id) return; // success + + if (declared_id == _none && actual_id != _none && mk == vmSymbols::java_lang_StrictMath()) { + // Here are a few special cases in StrictMath not declared in vmSymbols.hpp. + switch (actual_id) { + case _min: + case _max: + case _dsqrt: + declared_id = match_method_with_klass(m, vmSymbols::java_lang_Math()); + if (declared_id == actual_id) return; // acceptable alias + break; + } + } + + const char* declared_name = name_at(declared_id); + const char* actual_name = name_at(actual_id); + methodHandle mh = m; + m = NULL; + ttyLocker ttyl; + if (xtty != NULL) { + xtty->begin_elem("intrinsic_misdeclared actual='%s' declared='%s'", + actual_name, declared_name); + xtty->method(mh); + xtty->end_elem(""); + } + if (PrintMiscellaneous && (WizardMode || Verbose)) { + tty->print_cr("*** misidentified method; %s(%d) should be %s(%d):", + declared_name, declared_id, actual_name, actual_id); + m->print_short_name(tty); + tty->cr(); + } +} +#endif //PRODUCT diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp new file mode 100644 index 00000000000..e0119432ddc --- /dev/null +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -0,0 +1,893 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The classes vmSymbols and vmSymbolHandles are a name spaces for fast lookup of +// symbols commonly used in the VM. The first class return a symbolOop, while the +// second class returns a SymbolHandle. The underlying data structure is shared +// between the two classes. +// +// Sample usage: +// +// symbolOop obj = vmSymbols::java_lang_Object()(); +// SymbolHandle handle = vmSymbolHandles::java_lang_Object(); + + +// Useful sub-macros exported by this header file: + +#define VM_SYMBOL_ENUM_NAME(name) name##_enum +#define VM_INTRINSIC_IGNORE(id, class, name, sig, flags) /*ignored*/ +#define VM_SYMBOL_IGNORE(id, name) /*ignored*/ +#define VM_ALIAS_IGNORE(id, id2) /*ignored*/ + + +// Mapping function names to values. New entries should be added below. + +#define VM_SYMBOLS_DO(template, do_alias) \ + /* commonly used class names */ \ + template(java_lang_System, "java/lang/System") \ + template(java_lang_Object, "java/lang/Object") \ + template(java_lang_Class, "java/lang/Class") \ + template(java_lang_String, "java/lang/String") \ + template(java_lang_Thread, "java/lang/Thread") \ + template(java_lang_ThreadGroup, "java/lang/ThreadGroup") \ + template(java_lang_Cloneable, "java/lang/Cloneable") \ + template(java_lang_Throwable, "java/lang/Throwable") \ + template(java_lang_ClassLoader, "java/lang/ClassLoader") \ + template(java_lang_ClassLoader_NativeLibrary, "java/lang/ClassLoader\x024NativeLibrary") \ + template(java_lang_ThreadDeath, "java/lang/ThreadDeath") \ + template(java_lang_Boolean, "java/lang/Boolean") \ + template(java_lang_Character, "java/lang/Character") \ + template(java_lang_Float, "java/lang/Float") \ + template(java_lang_Double, "java/lang/Double") \ + template(java_lang_Byte, "java/lang/Byte") \ + template(java_lang_Short, "java/lang/Short") \ + template(java_lang_Integer, "java/lang/Integer") \ + template(java_lang_Long, "java/lang/Long") \ + template(java_lang_Shutdown, "java/lang/Shutdown") \ + template(java_lang_ref_Reference, "java/lang/ref/Reference") \ + template(java_lang_ref_SoftReference, "java/lang/ref/SoftReference") \ + template(java_lang_ref_WeakReference, "java/lang/ref/WeakReference") \ + template(java_lang_ref_FinalReference, "java/lang/ref/FinalReference") \ + template(java_lang_ref_PhantomReference, "java/lang/ref/PhantomReference") \ + template(java_lang_ref_Finalizer, "java/lang/ref/Finalizer") \ + template(java_lang_reflect_AccessibleObject, "java/lang/reflect/AccessibleObject") \ + template(java_lang_reflect_Method, "java/lang/reflect/Method") \ + template(java_lang_reflect_Constructor, "java/lang/reflect/Constructor") \ + template(java_lang_reflect_Field, "java/lang/reflect/Field") \ + template(java_lang_reflect_Array, "java/lang/reflect/Array") \ + template(java_lang_StringBuffer, "java/lang/StringBuffer") \ + template(java_lang_CharSequence, "java/lang/CharSequence") \ + template(java_security_AccessControlContext, "java/security/AccessControlContext") \ + template(java_security_ProtectionDomain, "java/security/ProtectionDomain") \ + template(java_io_OutputStream, "java/io/OutputStream") \ + template(java_io_Reader, "java/io/Reader") \ + template(java_io_BufferedReader, "java/io/BufferedReader") \ + template(java_io_FileInputStream, "java/io/FileInputStream") \ + template(java_io_ByteArrayInputStream, "java/io/ByteArrayInputStream") \ + template(java_io_Serializable, "java/io/Serializable") \ + template(java_util_Arrays, "java/util/Arrays") \ + template(java_util_Properties, "java/util/Properties") \ + template(java_util_Vector, "java/util/Vector") \ + template(java_util_AbstractList, "java/util/AbstractList") \ + template(java_util_Hashtable, "java/util/Hashtable") \ + template(java_lang_Compiler, "java/lang/Compiler") \ + template(sun_misc_Signal, "sun/misc/Signal") \ + template(java_lang_AssertionStatusDirectives, "java/lang/AssertionStatusDirectives") \ + template(sun_jkernel_DownloadManager, "sun/jkernel/DownloadManager") \ + template(getBootClassPathEntryForClass_name, "getBootClassPathEntryForClass") \ + \ + /* class file format tags */ \ + template(tag_source_file, "SourceFile") \ + template(tag_inner_classes, "InnerClasses") \ + template(tag_constant_value, "ConstantValue") \ + template(tag_code, "Code") \ + template(tag_exceptions, "Exceptions") \ + template(tag_line_number_table, "LineNumberTable") \ + template(tag_local_variable_table, "LocalVariableTable") \ + template(tag_local_variable_type_table, "LocalVariableTypeTable") \ + template(tag_stack_map_table, "StackMapTable") \ + template(tag_synthetic, "Synthetic") \ + template(tag_deprecated, "Deprecated") \ + template(tag_source_debug_extension, "SourceDebugExtension") \ + template(tag_signature, "Signature") \ + template(tag_runtime_visible_annotations, "RuntimeVisibleAnnotations") \ + template(tag_runtime_invisible_annotations, "RuntimeInvisibleAnnotations") \ + template(tag_runtime_visible_parameter_annotations, "RuntimeVisibleParameterAnnotations") \ + template(tag_runtime_invisible_parameter_annotations,"RuntimeInvisibleParameterAnnotations") \ + template(tag_annotation_default, "AnnotationDefault") \ + template(tag_enclosing_method, "EnclosingMethod") \ + \ + /* exception klasses: at least all exceptions thrown by the VM have entries here */ \ + template(java_lang_ArithmeticException, "java/lang/ArithmeticException") \ + template(java_lang_ArrayIndexOutOfBoundsException, "java/lang/ArrayIndexOutOfBoundsException") \ + template(java_lang_ArrayStoreException, "java/lang/ArrayStoreException") \ + template(java_lang_ClassCastException, "java/lang/ClassCastException") \ + template(java_lang_ClassNotFoundException, "java/lang/ClassNotFoundException") \ + template(java_lang_CloneNotSupportedException, "java/lang/CloneNotSupportedException") \ + template(java_lang_IllegalAccessException, "java/lang/IllegalAccessException") \ + template(java_lang_IllegalArgumentException, "java/lang/IllegalArgumentException") \ + template(java_lang_IllegalMonitorStateException, "java/lang/IllegalMonitorStateException") \ + template(java_lang_IllegalThreadStateException, "java/lang/IllegalThreadStateException") \ + template(java_lang_IndexOutOfBoundsException, "java/lang/IndexOutOfBoundsException") \ + template(java_lang_InstantiationException, "java/lang/InstantiationException") \ + template(java_lang_InstantiationError, "java/lang/InstantiationError") \ + template(java_lang_InterruptedException, "java/lang/InterruptedException") \ + template(java_lang_LinkageError, "java/lang/LinkageError") \ + template(java_lang_NegativeArraySizeException, "java/lang/NegativeArraySizeException") \ + template(java_lang_NoSuchFieldException, "java/lang/NoSuchFieldException") \ + template(java_lang_NoSuchMethodException, "java/lang/NoSuchMethodException") \ + template(java_lang_NullPointerException, "java/lang/NullPointerException") \ + template(java_lang_StringIndexOutOfBoundsException, "java/lang/StringIndexOutOfBoundsException")\ + template(java_lang_InvalidClassException, "java/lang/InvalidClassException") \ + template(java_lang_reflect_InvocationTargetException, "java/lang/reflect/InvocationTargetException") \ + template(java_lang_Exception, "java/lang/Exception") \ + template(java_lang_RuntimeException, "java/lang/RuntimeException") \ + template(java_io_IOException, "java/io/IOException") \ + template(java_security_PrivilegedActionException, "java/security/PrivilegedActionException") \ + \ + /* error klasses: at least all errors thrown by the VM have entries here */ \ + template(java_lang_AbstractMethodError, "java/lang/AbstractMethodError") \ + template(java_lang_ClassCircularityError, "java/lang/ClassCircularityError") \ + template(java_lang_ClassFormatError, "java/lang/ClassFormatError") \ + template(java_lang_UnsupportedClassVersionError, "java/lang/UnsupportedClassVersionError") \ + template(java_lang_Error, "java/lang/Error") \ + template(java_lang_ExceptionInInitializerError, "java/lang/ExceptionInInitializerError") \ + template(java_lang_IllegalAccessError, "java/lang/IllegalAccessError") \ + template(java_lang_IncompatibleClassChangeError, "java/lang/IncompatibleClassChangeError") \ + template(java_lang_InternalError, "java/lang/InternalError") \ + template(java_lang_NoClassDefFoundError, "java/lang/NoClassDefFoundError") \ + template(java_lang_NoSuchFieldError, "java/lang/NoSuchFieldError") \ + template(java_lang_NoSuchMethodError, "java/lang/NoSuchMethodError") \ + template(java_lang_OutOfMemoryError, "java/lang/OutOfMemoryError") \ + template(java_lang_UnsatisfiedLinkError, "java/lang/UnsatisfiedLinkError") \ + template(java_lang_VerifyError, "java/lang/VerifyError") \ + template(java_lang_SecurityException, "java/lang/SecurityException") \ + template(java_lang_VirtualMachineError, "java/lang/VirtualMachineError") \ + template(java_lang_StackOverflowError, "java/lang/StackOverflowError") \ + template(java_lang_StackTraceElement, "java/lang/StackTraceElement") \ + template(java_util_concurrent_locks_AbstractOwnableSynchronizer, "java/util/concurrent/locks/AbstractOwnableSynchronizer") \ + \ + /* class symbols needed by intrinsics */ \ + VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, template, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) \ + \ + /* Support for reflection based on dynamic bytecode generation (JDK 1.4 and above) */ \ + \ + template(sun_reflect_FieldInfo, "sun/reflect/FieldInfo") \ + template(sun_reflect_MethodInfo, "sun/reflect/MethodInfo") \ + template(sun_reflect_MagicAccessorImpl, "sun/reflect/MagicAccessorImpl") \ + template(sun_reflect_MethodAccessorImpl, "sun/reflect/MethodAccessorImpl") \ + template(sun_reflect_ConstructorAccessorImpl, "sun/reflect/ConstructorAccessorImpl") \ + template(sun_reflect_SerializationConstructorAccessorImpl, "sun/reflect/SerializationConstructorAccessorImpl") \ + template(sun_reflect_DelegatingClassLoader, "sun/reflect/DelegatingClassLoader") \ + template(sun_reflect_Reflection, "sun/reflect/Reflection") \ + template(checkedExceptions_name, "checkedExceptions") \ + template(clazz_name, "clazz") \ + template(exceptionTypes_name, "exceptionTypes") \ + template(modifiers_name, "modifiers") \ + template(newConstructor_name, "newConstructor") \ + template(newConstructor_signature, "(Lsun/reflect/MethodInfo;)Ljava/lang/reflect/Constructor;") \ + template(newField_name, "newField") \ + template(newField_signature, "(Lsun/reflect/FieldInfo;)Ljava/lang/reflect/Field;") \ + template(newMethod_name, "newMethod") \ + template(newMethod_signature, "(Lsun/reflect/MethodInfo;)Ljava/lang/reflect/Method;") \ + template(invoke_name, "invoke") \ + template(override_name, "override") \ + template(parameterTypes_name, "parameterTypes") \ + template(returnType_name, "returnType") \ + template(signature_name, "signature") \ + template(slot_name, "slot") \ + \ + /* Support for annotations (JDK 1.5 and above) */ \ + \ + template(annotations_name, "annotations") \ + template(parameter_annotations_name, "parameterAnnotations") \ + template(annotation_default_name, "annotationDefault") \ + template(sun_reflect_ConstantPool, "sun/reflect/ConstantPool") \ + template(constantPoolOop_name, "constantPoolOop") \ + template(sun_reflect_UnsafeStaticFieldAccessorImpl, "sun/reflect/UnsafeStaticFieldAccessorImpl")\ + template(base_name, "base") \ + \ + /* common method names */ \ + template(object_initializer_name, "") \ + template(class_initializer_name, "") \ + template(println_name, "println") \ + template(printStackTrace_name, "printStackTrace") \ + template(main_name, "main") \ + template(name_name, "name") \ + template(priority_name, "priority") \ + template(stillborn_name, "stillborn") \ + template(group_name, "group") \ + template(daemon_name, "daemon") \ + template(eetop_name, "eetop") \ + template(thread_status_name, "threadStatus") \ + template(run_method_name, "run") \ + template(exit_method_name, "exit") \ + template(add_method_name, "add") \ + template(parent_name, "parent") \ + template(threads_name, "threads") \ + template(groups_name, "groups") \ + template(maxPriority_name, "maxPriority") \ + template(destroyed_name, "destroyed") \ + template(vmAllowSuspension_name, "vmAllowSuspension") \ + template(nthreads_name, "nthreads") \ + template(ngroups_name, "ngroups") \ + template(shutdown_method_name, "shutdown") \ + template(finalize_method_name, "finalize") \ + template(reference_lock_name, "lock") \ + template(reference_discovered_name, "discovered") \ + template(run_finalizers_on_exit_name, "runFinalizersOnExit") \ + template(uncaughtException_name, "uncaughtException") \ + template(dispatchUncaughtException_name, "dispatchUncaughtException") \ + template(initializeSystemClass_name, "initializeSystemClass") \ + template(loadClass_name, "loadClass") \ + template(loadClassInternal_name, "loadClassInternal") \ + template(get_name, "get") \ + template(put_name, "put") \ + template(type_name, "type") \ + template(findNative_name, "findNative") \ + template(deadChild_name, "deadChild") \ + template(addClass_name, "addClass") \ + template(getFromClass_name, "getFromClass") \ + template(dispatch_name, "dispatch") \ + template(getSystemClassLoader_name, "getSystemClassLoader") \ + template(fillInStackTrace_name, "fillInStackTrace") \ + template(getCause_name, "getCause") \ + template(initCause_name, "initCause") \ + template(setProperty_name, "setProperty") \ + template(getProperty_name, "getProperty") \ + template(context_name, "context") \ + template(privilegedContext_name, "privilegedContext") \ + template(contextClassLoader_name, "contextClassLoader") \ + template(inheritedAccessControlContext_name, "inheritedAccessControlContext") \ + template(isPrivileged_name, "isPrivileged") \ + template(wait_name, "wait") \ + template(checkPackageAccess_name, "checkPackageAccess") \ + template(stackSize_name, "stackSize") \ + template(thread_id_name, "tid") \ + template(newInstance0_name, "newInstance0") \ + template(limit_name, "limit") \ + template(forName_name, "forName") \ + template(forName0_name, "forName0") \ + template(isJavaIdentifierStart_name, "isJavaIdentifierStart") \ + template(isJavaIdentifierPart_name, "isJavaIdentifierPart") \ + template(exclusive_owner_thread_name, "exclusiveOwnerThread") \ + template(park_blocker_name, "parkBlocker") \ + template(park_event_name, "nativeParkEventPointer") \ + template(value_name, "value") \ + \ + /* non-intrinsic name/signature pairs: */ \ + template(register_method_name, "register") \ + do_alias(register_method_signature, object_void_signature) \ + \ + /* name symbols needed by intrinsics */ \ + VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, template, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) \ + \ + /* common signatures names */ \ + template(void_method_signature, "()V") \ + template(void_int_signature, "()I") \ + template(void_long_signature, "()J") \ + template(void_boolean_signature, "()Z") \ + template(int_void_signature, "(I)V") \ + template(int_int_signature, "(I)I") \ + template(int_bool_signature, "(I)Z") \ + template(float_int_signature, "(F)I") \ + template(double_long_signature, "(D)J") \ + template(double_double_signature, "(D)D") \ + template(int_float_signature, "(I)F") \ + template(long_long_signature, "(J)J") \ + template(long_double_signature, "(J)D") \ + template(byte_signature, "B") \ + template(char_signature, "C") \ + template(double_signature, "D") \ + template(float_signature, "F") \ + template(int_signature, "I") \ + template(long_signature, "J") \ + template(short_signature, "S") \ + template(bool_signature, "Z") \ + template(void_signature, "V") \ + template(byte_array_signature, "[B") \ + template(char_array_signature, "[C") \ + template(object_void_signature, "(Ljava/lang/Object;)V") \ + template(object_int_signature, "(Ljava/lang/Object;)I") \ + template(object_boolean_signature, "(Ljava/lang/Object;)Z") \ + template(string_void_signature, "(Ljava/lang/String;)V") \ + template(string_int_signature, "(Ljava/lang/String;)I") \ + template(throwable_void_signature, "(Ljava/lang/Throwable;)V") \ + template(void_throwable_signature, "()Ljava/lang/Throwable;") \ + template(throwable_throwable_signature, "(Ljava/lang/Throwable;)Ljava/lang/Throwable;") \ + template(class_void_signature, "(Ljava/lang/Class;)V") \ + template(class_int_signature, "(Ljava/lang/Class;)I") \ + template(class_boolean_signature, "(Ljava/lang/Class;)Z") \ + template(throwable_string_void_signature, "(Ljava/lang/Throwable;Ljava/lang/String;)V") \ + template(string_array_void_signature, "([Ljava/lang/String;)V") \ + template(string_array_string_array_void_signature, "([Ljava/lang/String;[Ljava/lang/String;)V") \ + template(thread_throwable_void_signature, "(Ljava/lang/Thread;Ljava/lang/Throwable;)V") \ + template(thread_void_signature, "(Ljava/lang/Thread;)V") \ + template(threadgroup_runnable_void_signature, "(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;)V") \ + template(threadgroup_string_void_signature, "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V") \ + template(string_class_signature, "(Ljava/lang/String;)Ljava/lang/Class;") \ + template(object_object_object_signature, "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") \ + template(string_string_string_signature, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;") \ + template(string_string_signature, "(Ljava/lang/String;)Ljava/lang/String;") \ + template(classloader_string_long_signature, "(Ljava/lang/ClassLoader;Ljava/lang/String;)J") \ + template(byte_array_void_signature, "([B)V") \ + template(char_array_void_signature, "([C)V") \ + template(int_int_void_signature, "(II)V") \ + template(long_long_void_signature, "(JJ)V") \ + template(void_classloader_signature, "()Ljava/lang/ClassLoader;") \ + template(void_object_signature, "()Ljava/lang/Object;") \ + template(void_class_signature, "()Ljava/lang/Class;") \ + template(object_array_object_object_signature, "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")\ + template(exception_void_signature, "(Ljava/lang/Exception;)V") \ + template(protectiondomain_signature, "[Ljava/security/ProtectionDomain;") \ + template(accesscontrolcontext_signature, "Ljava/security/AccessControlContext;") \ + template(class_protectiondomain_signature, "(Ljava/lang/Class;Ljava/security/ProtectionDomain;)V") \ + template(thread_signature, "Ljava/lang/Thread;") \ + template(thread_array_signature, "[Ljava/lang/Thread;") \ + template(threadgroup_signature, "Ljava/lang/ThreadGroup;") \ + template(threadgroup_array_signature, "[Ljava/lang/ThreadGroup;") \ + template(class_array_signature, "[Ljava/lang/Class;") \ + template(classloader_signature, "Ljava/lang/ClassLoader;") \ + template(object_signature, "Ljava/lang/Object;") \ + template(class_signature, "Ljava/lang/Class;") \ + template(string_signature, "Ljava/lang/String;") \ + template(reference_signature, "Ljava/lang/ref/Reference;") \ + /* signature symbols needed by intrinsics */ \ + VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, template, VM_ALIAS_IGNORE) \ + \ + /* symbol aliases needed by intrinsics */ \ + VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, do_alias) \ + \ + /* returned by the C1 compiler in case there's not enough memory to allocate a new symbol*/ \ + template(dummy_symbol_oop, "illegal symbol") \ + \ + /* used by ClassFormatError when class name is not known yet */ \ + template(unknown_class_name, "") \ + \ + /* JVM monitoring and management support */ \ + template(java_lang_StackTraceElement_array, "[Ljava/lang/StackTraceElement;") \ + template(java_lang_management_ThreadState, "java/lang/management/ThreadState") \ + template(java_lang_management_MemoryUsage, "java/lang/management/MemoryUsage") \ + template(java_lang_management_ThreadInfo, "java/lang/management/ThreadInfo") \ + template(sun_management_ManagementFactory, "sun/management/ManagementFactory") \ + template(sun_management_Sensor, "sun/management/Sensor") \ + template(sun_management_Agent, "sun/management/Agent") \ + template(createMemoryPoolMBean_name, "createMemoryPoolMBean") \ + template(createMemoryManagerMBean_name, "createMemoryManagerMBean") \ + template(createGarbageCollectorMBean_name, "createGarbageCollectorMBean") \ + template(createMemoryPoolMBean_signature, "(Ljava/lang/String;ZJJ)Ljava/lang/management/MemoryPoolMBean;") \ + template(createMemoryManagerMBean_signature, "(Ljava/lang/String;)Ljava/lang/management/MemoryManagerMBean;") \ + template(createGarbageCollectorMBean_signature, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/management/GarbageCollectorMBean;") \ + template(trigger_name, "trigger") \ + template(clear_name, "clear") \ + template(trigger_method_signature, "(ILjava/lang/management/MemoryUsage;)V") \ + template(startAgent_name, "startAgent") \ + template(java_lang_management_ThreadInfo_constructor_signature, "(Ljava/lang/Thread;ILjava/lang/Object;Ljava/lang/Thread;JJJJ[Ljava/lang/StackTraceElement;)V") \ + template(java_lang_management_ThreadInfo_with_locks_constructor_signature, "(Ljava/lang/Thread;ILjava/lang/Object;Ljava/lang/Thread;JJJJ[Ljava/lang/StackTraceElement;[Ljava/lang/Object;[I[Ljava/lang/Object;)V") \ + template(long_long_long_long_void_signature, "(JJJJ)V") \ + \ + template(java_lang_management_MemoryPoolMXBean, "java/lang/management/MemoryPoolMXBean") \ + template(java_lang_management_MemoryManagerMXBean, "java/lang/management/MemoryManagerMXBean") \ + template(java_lang_management_GarbageCollectorMXBean,"java/lang/management/GarbageCollectorMXBean") \ + template(createMemoryPool_name, "createMemoryPool") \ + template(createMemoryManager_name, "createMemoryManager") \ + template(createGarbageCollector_name, "createGarbageCollector") \ + template(createMemoryPool_signature, "(Ljava/lang/String;ZJJ)Ljava/lang/management/MemoryPoolMXBean;") \ + template(createMemoryManager_signature, "(Ljava/lang/String;)Ljava/lang/management/MemoryManagerMXBean;") \ + template(createGarbageCollector_signature, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/management/GarbageCollectorMXBean;") \ + template(addThreadDumpForMonitors_name, "addThreadDumpForMonitors") \ + template(addThreadDumpForSynchronizers_name, "addThreadDumpForSynchronizers") \ + template(addThreadDumpForMonitors_signature, "(Ljava/lang/management/ThreadInfo;[Ljava/lang/Object;[I)V") \ + template(addThreadDumpForSynchronizers_signature, "(Ljava/lang/management/ThreadInfo;[Ljava/lang/Object;)V") \ + \ + /* JVMTI/java.lang.instrument support and VM Attach mechanism */ \ + template(sun_misc_VMSupport, "sun/misc/VMSupport") \ + template(appendToClassPathForInstrumentation_name, "appendToClassPathForInstrumentation") \ + do_alias(appendToClassPathForInstrumentation_signature, string_void_signature) \ + template(serializePropertiesToByteArray_name, "serializePropertiesToByteArray") \ + template(serializePropertiesToByteArray_signature, "()[B") \ + template(serializeAgentPropertiesToByteArray_name, "serializeAgentPropertiesToByteArray") \ + template(classRedefinedCount_name, "classRedefinedCount") \ + /*end*/ + + + +// Here are all the intrinsics known to the runtime and the CI. +// Each intrinsic consists of a public enum name (like _hashCode), +// followed by a specification of its klass, name, and signature: +// template(, , , , ) +// +// If you add an intrinsic here, you must also define its name +// and signature as members of the VM symbols. The VM symbols for +// the intrinsic name and signature may be defined above. +// +// Because the VM_SYMBOLS_DO macro makes reference to VM_INTRINSICS_DO, +// you can also define an intrinsic's name and/or signature locally to the +// intrinsic, if this makes sense. (It often does make sense.) +// +// For example: +// do_intrinsic(_foo, java_lang_Object, foo_name, foo_signature, F_xx) +// do_name( foo_name, "foo") +// do_signature(foo_signature, "()F") +// klass = vmSymbols::java_lang_Object() +// name = vmSymbols::foo_name() +// signature = vmSymbols::foo_signature() +// +// The name and/or signature might be a "well known" symbol +// like "equal" or "()I", in which case there will be no local +// re-definition of the symbol. +// +// The do_class, do_name, and do_signature calls are all used for the +// same purpose: Define yet another VM symbol. They could all be merged +// into a common 'do_symbol' call, but it seems useful to record our +// intentions here about kinds of symbols (class vs. name vs. signature). +// +// The F_xx is one of the Flags enum; see below. +// +// for Emacs: (let ((c-backslash-column 120) (c-backslash-max-column 120)) (c-backslash-region (point) (point-max) nil t)) +#define VM_INTRINSICS_DO(do_intrinsic, do_class, do_name, do_signature, do_alias) \ + do_intrinsic(_Object_init, java_lang_Object, object_initializer_name, void_method_signature, F_R) \ + /* (symbol object_initializer_name defined above) */ \ + \ + do_intrinsic(_hashCode, java_lang_Object, hashCode_name, void_int_signature, F_R) \ + do_name( hashCode_name, "hashCode") \ + do_intrinsic(_getClass, java_lang_Object, getClass_name, void_class_signature, F_R) \ + do_name( getClass_name, "getClass") \ + do_intrinsic(_clone, java_lang_Object, clone_name, void_object_signature, F_R) \ + do_name( clone_name, "clone") \ + \ + /* Math & StrictMath intrinsics are defined in terms of just a few signatures: */ \ + do_class(java_lang_Math, "java/lang/Math") \ + do_class(java_lang_StrictMath, "java/lang/StrictMath") \ + do_signature(double2_double_signature, "(DD)D") \ + do_signature(int2_int_signature, "(II)I") \ + \ + /* here are the math names, all together: */ \ + do_name(abs_name,"abs") do_name(sin_name,"sin") do_name(cos_name,"cos") \ + do_name(tan_name,"tan") do_name(atan2_name,"atan2") do_name(sqrt_name,"sqrt") \ + do_name(log_name,"log") do_name(log10_name,"log10") do_name(pow_name,"pow") \ + do_name(exp_name,"exp") do_name(min_name,"min") do_name(max_name,"max") \ + \ + do_intrinsic(_dabs, java_lang_Math, abs_name, double_double_signature, F_S) \ + do_intrinsic(_dsin, java_lang_Math, sin_name, double_double_signature, F_S) \ + do_intrinsic(_dcos, java_lang_Math, cos_name, double_double_signature, F_S) \ + do_intrinsic(_dtan, java_lang_Math, tan_name, double_double_signature, F_S) \ + do_intrinsic(_datan2, java_lang_Math, atan2_name, double2_double_signature, F_S) \ + do_intrinsic(_dsqrt, java_lang_Math, sqrt_name, double_double_signature, F_S) \ + do_intrinsic(_dlog, java_lang_Math, log_name, double_double_signature, F_S) \ + do_intrinsic(_dlog10, java_lang_Math, log10_name, double_double_signature, F_S) \ + do_intrinsic(_dpow, java_lang_Math, pow_name, double2_double_signature, F_S) \ + do_intrinsic(_dexp, java_lang_Math, exp_name, double_double_signature, F_S) \ + do_intrinsic(_min, java_lang_Math, min_name, int2_int_signature, F_S) \ + do_intrinsic(_max, java_lang_Math, max_name, int2_int_signature, F_S) \ + \ + do_intrinsic(_floatToRawIntBits, java_lang_Float, floatToRawIntBits_name, float_int_signature, F_S) \ + do_name( floatToRawIntBits_name, "floatToRawIntBits") \ + do_intrinsic(_floatToIntBits, java_lang_Float, floatToIntBits_name, float_int_signature, F_S) \ + do_name( floatToIntBits_name, "floatToIntBits") \ + do_intrinsic(_intBitsToFloat, java_lang_Float, intBitsToFloat_name, int_float_signature, F_S) \ + do_name( intBitsToFloat_name, "intBitsToFloat") \ + do_intrinsic(_doubleToRawLongBits, java_lang_Double, doubleToRawLongBits_name, double_long_signature, F_S) \ + do_name( doubleToRawLongBits_name, "doubleToRawLongBits") \ + do_intrinsic(_doubleToLongBits, java_lang_Double, doubleToLongBits_name, double_long_signature, F_S) \ + do_name( doubleToLongBits_name, "doubleToLongBits") \ + do_intrinsic(_longBitsToDouble, java_lang_Double, longBitsToDouble_name, long_double_signature, F_S) \ + do_name( longBitsToDouble_name, "longBitsToDouble") \ + do_intrinsic(_reverseBytes_i, java_lang_Integer, reverseBytes_name, int_int_signature, F_S) \ + do_name( reverseBytes_name, "reverseBytes") \ + do_intrinsic(_reverseBytes_l, java_lang_Long, reverseBytes_name, long_long_signature, F_S) \ + /* (symbol reverseBytes_name defined above) */ \ + \ + do_intrinsic(_identityHashCode, java_lang_System, identityHashCode_name, object_int_signature, F_S) \ + do_name( identityHashCode_name, "identityHashCode") \ + do_intrinsic(_currentTimeMillis, java_lang_System, currentTimeMillis_name, void_long_signature, F_S) \ + \ + do_name( currentTimeMillis_name, "currentTimeMillis") \ + do_intrinsic(_nanoTime, java_lang_System, nanoTime_name, void_long_signature, F_S) \ + do_name( nanoTime_name, "nanoTime") \ + \ + do_intrinsic(_arraycopy, java_lang_System, arraycopy_name, arraycopy_signature, F_S) \ + do_name( arraycopy_name, "arraycopy") \ + do_signature(arraycopy_signature, "(Ljava/lang/Object;ILjava/lang/Object;II)V") \ + do_intrinsic(_isInterrupted, java_lang_Thread, isInterrupted_name, isInterrupted_signature, F_R) \ + do_name( isInterrupted_name, "isInterrupted") \ + do_signature(isInterrupted_signature, "(Z)Z") \ + do_intrinsic(_currentThread, java_lang_Thread, currentThread_name, currentThread_signature, F_S) \ + do_name( currentThread_name, "currentThread") \ + do_signature(currentThread_signature, "()Ljava/lang/Thread;") \ + \ + /* reflective intrinsics, for java/lang/Class, etc. */ \ + do_intrinsic(_isAssignableFrom, java_lang_Class, isAssignableFrom_name, class_boolean_signature, F_RN) \ + do_name( isAssignableFrom_name, "isAssignableFrom") \ + do_intrinsic(_isInstance, java_lang_Class, isInstance_name, object_boolean_signature, F_RN) \ + do_name( isInstance_name, "isInstance") \ + do_intrinsic(_getModifiers, java_lang_Class, getModifiers_name, void_int_signature, F_RN) \ + do_name( getModifiers_name, "getModifiers") \ + do_intrinsic(_isInterface, java_lang_Class, isInterface_name, void_boolean_signature, F_RN) \ + do_name( isInterface_name, "isInterface") \ + do_intrinsic(_isArray, java_lang_Class, isArray_name, void_boolean_signature, F_RN) \ + do_name( isArray_name, "isArray") \ + do_intrinsic(_isPrimitive, java_lang_Class, isPrimitive_name, void_boolean_signature, F_RN) \ + do_name( isPrimitive_name, "isPrimitive") \ + do_intrinsic(_getSuperclass, java_lang_Class, getSuperclass_name, void_class_signature, F_RN) \ + do_name( getSuperclass_name, "getSuperclass") \ + do_intrinsic(_getComponentType, java_lang_Class, getComponentType_name, void_class_signature, F_RN) \ + do_name( getComponentType_name, "getComponentType") \ + \ + do_intrinsic(_getClassAccessFlags, sun_reflect_Reflection, getClassAccessFlags_name, class_int_signature, F_SN) \ + do_name( getClassAccessFlags_name, "getClassAccessFlags") \ + do_intrinsic(_getLength, java_lang_reflect_Array, getLength_name, object_int_signature, F_SN) \ + do_name( getLength_name, "getLength") \ + \ + do_intrinsic(_getCallerClass, sun_reflect_Reflection, getCallerClass_name, getCallerClass_signature, F_SN) \ + do_name( getCallerClass_name, "getCallerClass") \ + do_signature(getCallerClass_signature, "(I)Ljava/lang/Class;") \ + \ + do_intrinsic(_newArray, java_lang_reflect_Array, newArray_name, newArray_signature, F_SN) \ + do_name( newArray_name, "newArray") \ + do_signature(newArray_signature, "(Ljava/lang/Class;I)Ljava/lang/Object;") \ + \ + do_intrinsic(_copyOf, java_util_Arrays, copyOf_name, copyOf_signature, F_S) \ + do_name( copyOf_name, "copyOf") \ + do_signature(copyOf_signature, "([Ljava/lang/Object;ILjava/lang/Class;)[Ljava/lang/Object;") \ + \ + do_intrinsic(_copyOfRange, java_util_Arrays, copyOfRange_name, copyOfRange_signature, F_S) \ + do_name( copyOfRange_name, "copyOfRange") \ + do_signature(copyOfRange_signature, "([Ljava/lang/Object;IILjava/lang/Class;)[Ljava/lang/Object;") \ + \ + do_intrinsic(_invoke, java_lang_reflect_Method, invoke_name, object_array_object_object_signature, F_R) \ + /* (symbols invoke_name and invoke_signature defined above) */ \ + \ + do_intrinsic(_compareTo, java_lang_String, compareTo_name, string_int_signature, F_R) \ + do_name( compareTo_name, "compareTo") \ + do_intrinsic(_indexOf, java_lang_String, indexOf_name, string_int_signature, F_R) \ + do_name( indexOf_name, "indexOf") \ + \ + do_class(java_nio_Buffer, "java/nio/Buffer") \ + do_intrinsic(_checkIndex, java_nio_Buffer, checkIndex_name, int_int_signature, F_R) \ + do_name( checkIndex_name, "checkIndex") \ + \ + do_class(sun_misc_AtomicLongCSImpl, "sun/misc/AtomicLongCSImpl") \ + do_intrinsic(_get_AtomicLong, sun_misc_AtomicLongCSImpl, get_name, void_long_signature, F_R) \ + /* (symbols get_name and void_long_signature defined above) */ \ + \ + do_intrinsic(_attemptUpdate, sun_misc_AtomicLongCSImpl, attemptUpdate_name, attemptUpdate_signature, F_R) \ + do_name( attemptUpdate_name, "attemptUpdate") \ + do_signature(attemptUpdate_signature, "(JJ)Z") \ + \ + /* support for sun.misc.Unsafe */ \ + do_class(sun_misc_Unsafe, "sun/misc/Unsafe") \ + \ + do_intrinsic(_allocateInstance, sun_misc_Unsafe, allocateInstance_name, allocateInstance_signature, F_RN) \ + do_name( allocateInstance_name, "allocateInstance") \ + do_signature(allocateInstance_signature, "(Ljava/lang/Class;)Ljava/lang/Object;") \ + do_intrinsic(_copyMemory, sun_misc_Unsafe, copyMemory_name, copyMemory_signature, F_RN) \ + do_name( copyMemory_name, "copyMemory") \ + do_signature(copyMemory_signature, "(Ljava/lang/Object;JLjava/lang/Object;JJ)V") \ + do_intrinsic(_park, sun_misc_Unsafe, park_name, park_signature, F_RN) \ + do_name( park_name, "park") \ + do_signature(park_signature, "(ZJ)V") \ + do_intrinsic(_unpark, sun_misc_Unsafe, unpark_name, unpark_signature, F_RN) \ + do_name( unpark_name, "unpark") \ + do_alias( unpark_signature, /*(LObject;)V*/ object_void_signature) \ + \ + /* unsafe memory references (there are a lot of them...) */ \ + do_signature(getObject_signature, "(Ljava/lang/Object;J)Ljava/lang/Object;") \ + do_signature(putObject_signature, "(Ljava/lang/Object;JLjava/lang/Object;)V") \ + do_signature(getBoolean_signature, "(Ljava/lang/Object;J)Z") \ + do_signature(putBoolean_signature, "(Ljava/lang/Object;JZ)V") \ + do_signature(getByte_signature, "(Ljava/lang/Object;J)B") \ + do_signature(putByte_signature, "(Ljava/lang/Object;JB)V") \ + do_signature(getShort_signature, "(Ljava/lang/Object;J)S") \ + do_signature(putShort_signature, "(Ljava/lang/Object;JS)V") \ + do_signature(getChar_signature, "(Ljava/lang/Object;J)C") \ + do_signature(putChar_signature, "(Ljava/lang/Object;JC)V") \ + do_signature(getInt_signature, "(Ljava/lang/Object;J)I") \ + do_signature(putInt_signature, "(Ljava/lang/Object;JI)V") \ + do_signature(getLong_signature, "(Ljava/lang/Object;J)J") \ + do_signature(putLong_signature, "(Ljava/lang/Object;JJ)V") \ + do_signature(getFloat_signature, "(Ljava/lang/Object;J)F") \ + do_signature(putFloat_signature, "(Ljava/lang/Object;JF)V") \ + do_signature(getDouble_signature, "(Ljava/lang/Object;J)D") \ + do_signature(putDouble_signature, "(Ljava/lang/Object;JD)V") \ + \ + do_name(getObject_name,"getObject") do_name(putObject_name,"putObject") \ + do_name(getBoolean_name,"getBoolean") do_name(putBoolean_name,"putBoolean") \ + do_name(getByte_name,"getByte") do_name(putByte_name,"putByte") \ + do_name(getShort_name,"getShort") do_name(putShort_name,"putShort") \ + do_name(getChar_name,"getChar") do_name(putChar_name,"putChar") \ + do_name(getInt_name,"getInt") do_name(putInt_name,"putInt") \ + do_name(getLong_name,"getLong") do_name(putLong_name,"putLong") \ + do_name(getFloat_name,"getFloat") do_name(putFloat_name,"putFloat") \ + do_name(getDouble_name,"getDouble") do_name(putDouble_name,"putDouble") \ + \ + do_intrinsic(_getObject, sun_misc_Unsafe, getObject_name, getObject_signature, F_RN) \ + do_intrinsic(_getBoolean, sun_misc_Unsafe, getBoolean_name, getBoolean_signature, F_RN) \ + do_intrinsic(_getByte, sun_misc_Unsafe, getByte_name, getByte_signature, F_RN) \ + do_intrinsic(_getShort, sun_misc_Unsafe, getShort_name, getShort_signature, F_RN) \ + do_intrinsic(_getChar, sun_misc_Unsafe, getChar_name, getChar_signature, F_RN) \ + do_intrinsic(_getInt, sun_misc_Unsafe, getInt_name, getInt_signature, F_RN) \ + do_intrinsic(_getLong, sun_misc_Unsafe, getLong_name, getLong_signature, F_RN) \ + do_intrinsic(_getFloat, sun_misc_Unsafe, getFloat_name, getFloat_signature, F_RN) \ + do_intrinsic(_getDouble, sun_misc_Unsafe, getDouble_name, getDouble_signature, F_RN) \ + do_intrinsic(_putObject, sun_misc_Unsafe, putObject_name, putObject_signature, F_RN) \ + do_intrinsic(_putBoolean, sun_misc_Unsafe, putBoolean_name, putBoolean_signature, F_RN) \ + do_intrinsic(_putByte, sun_misc_Unsafe, putByte_name, putByte_signature, F_RN) \ + do_intrinsic(_putShort, sun_misc_Unsafe, putShort_name, putShort_signature, F_RN) \ + do_intrinsic(_putChar, sun_misc_Unsafe, putChar_name, putChar_signature, F_RN) \ + do_intrinsic(_putInt, sun_misc_Unsafe, putInt_name, putInt_signature, F_RN) \ + do_intrinsic(_putLong, sun_misc_Unsafe, putLong_name, putLong_signature, F_RN) \ + do_intrinsic(_putFloat, sun_misc_Unsafe, putFloat_name, putFloat_signature, F_RN) \ + do_intrinsic(_putDouble, sun_misc_Unsafe, putDouble_name, putDouble_signature, F_RN) \ + \ + do_name(getObjectVolatile_name,"getObjectVolatile") do_name(putObjectVolatile_name,"putObjectVolatile") \ + do_name(getBooleanVolatile_name,"getBooleanVolatile") do_name(putBooleanVolatile_name,"putBooleanVolatile") \ + do_name(getByteVolatile_name,"getByteVolatile") do_name(putByteVolatile_name,"putByteVolatile") \ + do_name(getShortVolatile_name,"getShortVolatile") do_name(putShortVolatile_name,"putShortVolatile") \ + do_name(getCharVolatile_name,"getCharVolatile") do_name(putCharVolatile_name,"putCharVolatile") \ + do_name(getIntVolatile_name,"getIntVolatile") do_name(putIntVolatile_name,"putIntVolatile") \ + do_name(getLongVolatile_name,"getLongVolatile") do_name(putLongVolatile_name,"putLongVolatile") \ + do_name(getFloatVolatile_name,"getFloatVolatile") do_name(putFloatVolatile_name,"putFloatVolatile") \ + do_name(getDoubleVolatile_name,"getDoubleVolatile") do_name(putDoubleVolatile_name,"putDoubleVolatile") \ + \ + do_intrinsic(_getObjectVolatile, sun_misc_Unsafe, getObjectVolatile_name, getObject_signature, F_RN) \ + do_intrinsic(_getBooleanVolatile, sun_misc_Unsafe, getBooleanVolatile_name, getBoolean_signature, F_RN) \ + do_intrinsic(_getByteVolatile, sun_misc_Unsafe, getByteVolatile_name, getByte_signature, F_RN) \ + do_intrinsic(_getShortVolatile, sun_misc_Unsafe, getShortVolatile_name, getShort_signature, F_RN) \ + do_intrinsic(_getCharVolatile, sun_misc_Unsafe, getCharVolatile_name, getChar_signature, F_RN) \ + do_intrinsic(_getIntVolatile, sun_misc_Unsafe, getIntVolatile_name, getInt_signature, F_RN) \ + do_intrinsic(_getLongVolatile, sun_misc_Unsafe, getLongVolatile_name, getLong_signature, F_RN) \ + do_intrinsic(_getFloatVolatile, sun_misc_Unsafe, getFloatVolatile_name, getFloat_signature, F_RN) \ + do_intrinsic(_getDoubleVolatile, sun_misc_Unsafe, getDoubleVolatile_name, getDouble_signature, F_RN) \ + do_intrinsic(_putObjectVolatile, sun_misc_Unsafe, putObjectVolatile_name, putObject_signature, F_RN) \ + do_intrinsic(_putBooleanVolatile, sun_misc_Unsafe, putBooleanVolatile_name, putBoolean_signature, F_RN) \ + do_intrinsic(_putByteVolatile, sun_misc_Unsafe, putByteVolatile_name, putByte_signature, F_RN) \ + do_intrinsic(_putShortVolatile, sun_misc_Unsafe, putShortVolatile_name, putShort_signature, F_RN) \ + do_intrinsic(_putCharVolatile, sun_misc_Unsafe, putCharVolatile_name, putChar_signature, F_RN) \ + do_intrinsic(_putIntVolatile, sun_misc_Unsafe, putIntVolatile_name, putInt_signature, F_RN) \ + do_intrinsic(_putLongVolatile, sun_misc_Unsafe, putLongVolatile_name, putLong_signature, F_RN) \ + do_intrinsic(_putFloatVolatile, sun_misc_Unsafe, putFloatVolatile_name, putFloat_signature, F_RN) \ + do_intrinsic(_putDoubleVolatile, sun_misc_Unsafe, putDoubleVolatile_name, putDouble_signature, F_RN) \ + \ + /* %%% these are redundant except perhaps for getAddress, but Unsafe has native methods for them */ \ + do_signature(getByte_raw_signature, "(J)B") \ + do_signature(putByte_raw_signature, "(JB)V") \ + do_signature(getShort_raw_signature, "(J)S") \ + do_signature(putShort_raw_signature, "(JS)V") \ + do_signature(getChar_raw_signature, "(J)C") \ + do_signature(putChar_raw_signature, "(JC)V") \ + do_signature(getInt_raw_signature, "(J)I") \ + do_signature(putInt_raw_signature, "(JI)V") \ + do_alias(getLong_raw_signature, /*(J)J*/ long_long_signature) \ + do_alias(putLong_raw_signature, /*(JJ)V*/ long_long_void_signature) \ + do_signature(getFloat_raw_signature, "(J)F") \ + do_signature(putFloat_raw_signature, "(JF)V") \ + do_alias(getDouble_raw_signature, /*(J)D*/ long_double_signature) \ + do_signature(putDouble_raw_signature, "(JD)V") \ + do_alias(getAddress_raw_signature, /*(J)J*/ long_long_signature) \ + do_alias(putAddress_raw_signature, /*(JJ)V*/ long_long_void_signature) \ + \ + do_name( getAddress_name, "getAddress") \ + do_name( putAddress_name, "putAddress") \ + \ + do_intrinsic(_getByte_raw, sun_misc_Unsafe, getByte_name, getByte_raw_signature, F_RN) \ + do_intrinsic(_getShort_raw, sun_misc_Unsafe, getShort_name, getShort_raw_signature, F_RN) \ + do_intrinsic(_getChar_raw, sun_misc_Unsafe, getChar_name, getChar_raw_signature, F_RN) \ + do_intrinsic(_getInt_raw, sun_misc_Unsafe, getInt_name, getInt_raw_signature, F_RN) \ + do_intrinsic(_getLong_raw, sun_misc_Unsafe, getLong_name, getLong_raw_signature, F_RN) \ + do_intrinsic(_getFloat_raw, sun_misc_Unsafe, getFloat_name, getFloat_raw_signature, F_RN) \ + do_intrinsic(_getDouble_raw, sun_misc_Unsafe, getDouble_name, getDouble_raw_signature, F_RN) \ + do_intrinsic(_getAddress_raw, sun_misc_Unsafe, getAddress_name, getAddress_raw_signature, F_RN) \ + do_intrinsic(_putByte_raw, sun_misc_Unsafe, putByte_name, putByte_raw_signature, F_RN) \ + do_intrinsic(_putShort_raw, sun_misc_Unsafe, putShort_name, putShort_raw_signature, F_RN) \ + do_intrinsic(_putChar_raw, sun_misc_Unsafe, putChar_name, putChar_raw_signature, F_RN) \ + do_intrinsic(_putInt_raw, sun_misc_Unsafe, putInt_name, putInt_raw_signature, F_RN) \ + do_intrinsic(_putLong_raw, sun_misc_Unsafe, putLong_name, putLong_raw_signature, F_RN) \ + do_intrinsic(_putFloat_raw, sun_misc_Unsafe, putFloat_name, putFloat_raw_signature, F_RN) \ + do_intrinsic(_putDouble_raw, sun_misc_Unsafe, putDouble_name, putDouble_raw_signature, F_RN) \ + do_intrinsic(_putAddress_raw, sun_misc_Unsafe, putAddress_name, putAddress_raw_signature, F_RN) \ + \ + do_intrinsic(_compareAndSwapObject, sun_misc_Unsafe, compareAndSwapObject_name, compareAndSwapObject_signature, F_RN) \ + do_name( compareAndSwapObject_name, "compareAndSwapObject") \ + do_signature(compareAndSwapObject_signature, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z") \ + do_intrinsic(_compareAndSwapLong, sun_misc_Unsafe, compareAndSwapLong_name, compareAndSwapLong_signature, F_RN) \ + do_name( compareAndSwapLong_name, "compareAndSwapLong") \ + do_signature(compareAndSwapLong_signature, "(Ljava/lang/Object;JJJ)Z") \ + do_intrinsic(_compareAndSwapInt, sun_misc_Unsafe, compareAndSwapInt_name, compareAndSwapInt_signature, F_RN) \ + do_name( compareAndSwapInt_name, "compareAndSwapInt") \ + do_signature(compareAndSwapInt_signature, "(Ljava/lang/Object;JII)Z") \ + do_intrinsic(_putOrderedObject, sun_misc_Unsafe, putOrderedObject_name, putOrderedObject_signature, F_RN) \ + do_name( putOrderedObject_name, "putOrderedObject") \ + do_alias( putOrderedObject_signature, /*(LObject;JLObject;)V*/ putObject_signature) \ + do_intrinsic(_putOrderedLong, sun_misc_Unsafe, putOrderedLong_name, putOrderedLong_signature, F_RN) \ + do_name( putOrderedLong_name, "putOrderedLong") \ + do_alias( putOrderedLong_signature, /*(Ljava/lang/Object;JJ)V*/ putLong_signature) \ + do_intrinsic(_putOrderedInt, sun_misc_Unsafe, putOrderedInt_name, putOrderedInt_signature, F_RN) \ + do_name( putOrderedInt_name, "putOrderedInt") \ + do_alias( putOrderedInt_signature, /*(Ljava/lang/Object;JI)V*/ putInt_signature) \ + \ + /* prefetch_signature is shared by all prefetch variants */ \ + do_signature( prefetch_signature, "(Ljava/lang/Object;J)V") \ + \ + do_intrinsic(_prefetchRead, sun_misc_Unsafe, prefetchRead_name, prefetch_signature, F_RN) \ + do_name( prefetchRead_name, "prefetchRead") \ + do_intrinsic(_prefetchWrite, sun_misc_Unsafe, prefetchWrite_name, prefetch_signature, F_RN) \ + do_name( prefetchWrite_name, "prefetchWrite") \ + do_intrinsic(_prefetchReadStatic, sun_misc_Unsafe, prefetchReadStatic_name, prefetch_signature, F_SN) \ + do_name( prefetchReadStatic_name, "prefetchReadStatic") \ + do_intrinsic(_prefetchWriteStatic, sun_misc_Unsafe, prefetchWriteStatic_name, prefetch_signature, F_SN) \ + do_name( prefetchWriteStatic_name, "prefetchWriteStatic") \ + /*end*/ + + + +// Class vmSymbols + +class vmSymbols: AllStatic { + friend class vmSymbolHandles; + friend class vmIntrinsics; + public: + // enum for figuring positions and size of array holding symbolOops + enum SID { + NO_SID = 0, + + #define VM_SYMBOL_ENUM(name, string) VM_SYMBOL_ENUM_NAME(name), + VM_SYMBOLS_DO(VM_SYMBOL_ENUM, VM_ALIAS_IGNORE) + #undef VM_SYMBOL_ENUM + + SID_LIMIT, + + #define VM_ALIAS_ENUM(name, def) VM_SYMBOL_ENUM_NAME(name) = VM_SYMBOL_ENUM_NAME(def), + VM_SYMBOLS_DO(VM_SYMBOL_IGNORE, VM_ALIAS_ENUM) + #undef VM_ALIAS_ENUM + + FIRST_SID = NO_SID + 1 + }; + enum { + log2_SID_LIMIT = 10 // checked by an assert at start-up + }; + + private: + // The symbol array + static symbolOop _symbols[]; + + // Field signatures indexed by BasicType. + static symbolOop _type_signatures[T_VOID+1]; + + public: + // Initialization + static void initialize(TRAPS); + // Accessing + #define VM_SYMBOL_DECLARE(name, ignore) \ + static symbolOop name() { return _symbols[VM_SYMBOL_ENUM_NAME(name)]; } + VM_SYMBOLS_DO(VM_SYMBOL_DECLARE, VM_SYMBOL_DECLARE) + #undef VM_SYMBOL_DECLARE + + // GC support + static void oops_do(OopClosure* f, bool do_all = false); + + static symbolOop type_signature(BasicType t) { + assert((uint)t < T_VOID+1, "range check"); + assert(_type_signatures[t] != NULL, "domain check"); + return _type_signatures[t]; + } + // inverse of type_signature; returns T_OBJECT if s is not recognized + static BasicType signature_type(symbolOop s); + + static symbolOop symbol_at(SID id) { + assert(id >= FIRST_SID && id < SID_LIMIT, "oob"); + assert(_symbols[id] != NULL, "init"); + return _symbols[id]; + } + + // Returns symbol's SID if one is assigned, else NO_SID. + static SID find_sid(symbolOop symbol); + +#ifndef PRODUCT + // No need for this in the product: + static const char* name_for(SID sid); +#endif //PRODUCT +}; + + +// Class vmSymbolHandles + +class vmSymbolHandles: AllStatic { + friend class vmIntrinsics; + friend class ciObjectFactory; + + public: + // Accessing + #define VM_SYMBOL_HANDLE_DECLARE(name, ignore) \ + static symbolHandle name() { return symbol_handle_at(vmSymbols::VM_SYMBOL_ENUM_NAME(name)); } + VM_SYMBOLS_DO(VM_SYMBOL_HANDLE_DECLARE, VM_SYMBOL_HANDLE_DECLARE) + #undef VM_SYMBOL_HANDLE_DECLARE + + static symbolHandle symbol_handle_at(vmSymbols::SID id) { + return symbolHandle(&vmSymbols::_symbols[(int)id], false); + } + + static symbolHandle type_signature(BasicType t) { + assert(vmSymbols::type_signature(t) != NULL, "domain check"); + return symbolHandle(&vmSymbols::_type_signatures[t], false); + } + // inverse of type_signature; returns T_OBJECT if s is not recognized + static BasicType signature_type(symbolHandle s) { + return vmSymbols::signature_type(s()); + } +}; + +// VM Intrinsic ID's uniquely identify some very special methods +class vmIntrinsics: AllStatic { + friend class vmSymbols; + friend class ciObjectFactory; + + public: + // Accessing + enum ID { + _none = 0, // not an intrinsic (default answer) + + #define VM_INTRINSIC_ENUM(id, klass, name, sig, flags) id, + VM_INTRINSICS_DO(VM_INTRINSIC_ENUM, + VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) + #undef VM_INTRINSIC_ENUM + + ID_LIMIT, + FIRST_ID = _none + 1 + }; + + enum Flags { + // AccessFlags syndromes relevant to intrinsics. + F_none = 0, + F_R, // !static !synchronized (R="regular") + F_S, // static !synchronized + F_RN, // !static native !synchronized + F_SN // static native !synchronized + }; + +public: + static ID ID_from(int raw_id) { + assert(raw_id >= (int)_none && raw_id < (int)ID_LIMIT, + "must be a valid intrinsic ID"); + return (ID)raw_id; + } + + static const char* name_at(ID id); + + // Given a method's class, name, signature, and access flags, report its ID. + static ID find_id(vmSymbols::SID holder, + vmSymbols::SID name, + vmSymbols::SID sig, + jshort flags); + + static void verify_method(ID actual_id, methodOop m) PRODUCT_RETURN; + + // No need for these in the product: + static vmSymbols::SID class_for(ID id); + static vmSymbols::SID name_for(ID id); + static vmSymbols::SID signature_for(ID id); + static Flags flags_for(ID id); + + static const char* short_name_as_C_string(ID id, char* buf, int size); +}; diff --git a/hotspot/src/share/vm/code/codeBlob.cpp b/hotspot/src/share/vm/code/codeBlob.cpp new file mode 100644 index 00000000000..4ccc377a308 --- /dev/null +++ b/hotspot/src/share/vm/code/codeBlob.cpp @@ -0,0 +1,703 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_codeBlob.cpp.incl" + +unsigned int align_code_offset(int offset) { + // align the size to CodeEntryAlignment + return + ((offset + (int)CodeHeap::header_size() + (CodeEntryAlignment-1)) & ~(CodeEntryAlignment-1)) + - (int)CodeHeap::header_size(); +} + + +// This must be consistent with the CodeBlob constructor's layout actions. +unsigned int CodeBlob::allocation_size(CodeBuffer* cb, int header_size) { + unsigned int size = header_size; + size += round_to(cb->total_relocation_size(), oopSize); + // align the size to CodeEntryAlignment + size = align_code_offset(size); + size += round_to(cb->total_code_size(), oopSize); + size += round_to(cb->total_oop_size(), oopSize); + return size; +} + + +// Creates a simple CodeBlob. Sets up the size of the different regions. +CodeBlob::CodeBlob(const char* name, int header_size, int size, int frame_complete, int locs_size) { + assert(size == round_to(size, oopSize), "unaligned size"); + assert(locs_size == round_to(locs_size, oopSize), "unaligned size"); + assert(header_size == round_to(header_size, oopSize), "unaligned size"); + assert(!UseRelocIndex, "no space allocated for reloc index yet"); + + // Note: If UseRelocIndex is enabled, there needs to be (at least) one + // extra word for the relocation information, containing the reloc + // index table length. Unfortunately, the reloc index table imple- + // mentation is not easily understandable and thus it is not clear + // what exactly the format is supposed to be. For now, we just turn + // off the use of this table (gri 7/6/2000). + + _name = name; + _size = size; + _frame_complete_offset = frame_complete; + _header_size = header_size; + _relocation_size = locs_size; + _instructions_offset = align_code_offset(header_size + locs_size); + _data_offset = size; + _oops_offset = size; + _oops_length = 0; + _frame_size = 0; + set_oop_maps(NULL); +} + + +// Creates a CodeBlob from a CodeBuffer. Sets up the size of the different regions, +// and copy code and relocation info. +CodeBlob::CodeBlob( + const char* name, + CodeBuffer* cb, + int header_size, + int size, + int frame_complete, + int frame_size, + OopMapSet* oop_maps +) { + assert(size == round_to(size, oopSize), "unaligned size"); + assert(header_size == round_to(header_size, oopSize), "unaligned size"); + + _name = name; + _size = size; + _frame_complete_offset = frame_complete; + _header_size = header_size; + _relocation_size = round_to(cb->total_relocation_size(), oopSize); + _instructions_offset = align_code_offset(header_size + _relocation_size); + _data_offset = _instructions_offset + round_to(cb->total_code_size(), oopSize); + _oops_offset = _size - round_to(cb->total_oop_size(), oopSize); + _oops_length = 0; // temporary, until the copy_oops handshake + assert(_oops_offset >= _data_offset, "codeBlob is too small"); + assert(_data_offset <= size, "codeBlob is too small"); + + cb->copy_code_and_locs_to(this); + set_oop_maps(oop_maps); + _frame_size = frame_size; +#ifdef COMPILER1 + // probably wrong for tiered + assert(_frame_size >= -1, "must use frame size or -1 for runtime stubs"); +#endif // COMPILER1 +} + + +void CodeBlob::set_oop_maps(OopMapSet* p) { + // Danger Will Robinson! This method allocates a big + // chunk of memory, its your job to free it. + if (p != NULL) { + // We need to allocate a chunk big enough to hold the OopMapSet and all of its OopMaps + _oop_maps = (OopMapSet* )NEW_C_HEAP_ARRAY(unsigned char, p->heap_size()); + p->copy_to((address)_oop_maps); + } else { + _oop_maps = NULL; + } +} + + +void CodeBlob::flush() { + if (_oop_maps) { + FREE_C_HEAP_ARRAY(unsigned char, _oop_maps); + _oop_maps = NULL; + } + _comments.free(); +} + + +// Promote one word from an assembly-time handle to a live embedded oop. +inline void CodeBlob::initialize_immediate_oop(oop* dest, jobject handle) { + if (handle == NULL || + // As a special case, IC oops are initialized to 1 or -1. + handle == (jobject) Universe::non_oop_word()) { + (*dest) = (oop)handle; + } else { + (*dest) = JNIHandles::resolve_non_null(handle); + } +} + + +void CodeBlob::copy_oops(GrowableArray* array) { + assert(_oops_length == 0, "do this handshake just once, please"); + int length = array->length(); + assert((address)(oops_begin() + length) <= data_end(), "oops big enough"); + oop* dest = oops_begin(); + for (int index = 0 ; index < length; index++) { + initialize_immediate_oop(&dest[index], array->at(index)); + } + _oops_length = length; + + // Now we can fix up all the oops in the code. + // We need to do this in the code because + // the assembler uses jobjects as placeholders. + // The code and relocations have already been + // initialized by the CodeBlob constructor, + // so it is valid even at this early point to + // iterate over relocations and patch the code. + fix_oop_relocations(NULL, NULL, /*initialize_immediates=*/ true); +} + + +relocInfo::relocType CodeBlob::reloc_type_for_address(address pc) { + RelocIterator iter(this, pc, pc+1); + while (iter.next()) { + return (relocInfo::relocType) iter.type(); + } + // No relocation info found for pc + ShouldNotReachHere(); + return relocInfo::none; // dummy return value +} + + +bool CodeBlob::is_at_poll_return(address pc) { + RelocIterator iter(this, pc, pc+1); + while (iter.next()) { + if (iter.type() == relocInfo::poll_return_type) + return true; + } + return false; +} + + +bool CodeBlob::is_at_poll_or_poll_return(address pc) { + RelocIterator iter(this, pc, pc+1); + while (iter.next()) { + relocInfo::relocType t = iter.type(); + if (t == relocInfo::poll_return_type || t == relocInfo::poll_type) + return true; + } + return false; +} + + +void CodeBlob::fix_oop_relocations(address begin, address end, + bool initialize_immediates) { + // re-patch all oop-bearing instructions, just in case some oops moved + RelocIterator iter(this, begin, end); + while (iter.next()) { + if (iter.type() == relocInfo::oop_type) { + oop_Relocation* reloc = iter.oop_reloc(); + if (initialize_immediates && reloc->oop_is_immediate()) { + oop* dest = reloc->oop_addr(); + initialize_immediate_oop(dest, (jobject) *dest); + } + // Refresh the oop-related bits of this instruction. + reloc->fix_oop_relocation(); + } + + // There must not be any interfering patches or breakpoints. + assert(!(iter.type() == relocInfo::breakpoint_type + && iter.breakpoint_reloc()->active()), + "no active breakpoint"); + } +} + +void CodeBlob::do_unloading(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + bool unloading_occurred) { + ShouldNotReachHere(); +} + +OopMap* CodeBlob::oop_map_for_return_address(address return_address) { + address pc = return_address ; + assert (oop_maps() != NULL, "nope"); + return oop_maps()->find_map_at_offset ((intptr_t) pc - (intptr_t) instructions_begin()); +} + + +//---------------------------------------------------------------------------------------------------- +// Implementation of BufferBlob + + +BufferBlob::BufferBlob(const char* name, int size) +: CodeBlob(name, sizeof(BufferBlob), size, CodeOffsets::frame_never_safe, /*locs_size:*/ 0) +{} + +BufferBlob* BufferBlob::create(const char* name, int buffer_size) { + ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + + BufferBlob* blob = NULL; + unsigned int size = sizeof(BufferBlob); + // align the size to CodeEntryAlignment + size = align_code_offset(size); + size += round_to(buffer_size, oopSize); + assert(name != NULL, "must provide a name"); + { + + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + blob = new (size) BufferBlob(name, size); + } + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); + + return blob; +} + + +BufferBlob::BufferBlob(const char* name, int size, CodeBuffer* cb) + : CodeBlob(name, cb, sizeof(BufferBlob), size, CodeOffsets::frame_never_safe, 0, NULL) +{} + +BufferBlob* BufferBlob::create(const char* name, CodeBuffer* cb) { + ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + + BufferBlob* blob = NULL; + unsigned int size = allocation_size(cb, sizeof(BufferBlob)); + assert(name != NULL, "must provide a name"); + { + + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + blob = new (size) BufferBlob(name, size, cb); + } + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); + + return blob; +} + + +void* BufferBlob::operator new(size_t s, unsigned size) { + void* p = CodeCache::allocate(size); + return p; +} + + +void BufferBlob::free( BufferBlob *blob ) { + ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + CodeCache::free((CodeBlob*)blob); + } + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); +} + +bool BufferBlob::is_adapter_blob() const { + return (strcmp(AdapterHandlerEntry::name, name()) == 0); +} + +//---------------------------------------------------------------------------------------------------- +// Implementation of RuntimeStub + +RuntimeStub::RuntimeStub( + const char* name, + CodeBuffer* cb, + int size, + int frame_complete, + int frame_size, + OopMapSet* oop_maps, + bool caller_must_gc_arguments +) +: CodeBlob(name, cb, sizeof(RuntimeStub), size, frame_complete, frame_size, oop_maps) +{ + _caller_must_gc_arguments = caller_must_gc_arguments; +} + + +RuntimeStub* RuntimeStub::new_runtime_stub(const char* stub_name, + CodeBuffer* cb, + int frame_complete, + int frame_size, + OopMapSet* oop_maps, + bool caller_must_gc_arguments) +{ + RuntimeStub* stub = NULL; + ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + unsigned int size = allocation_size(cb, sizeof(RuntimeStub)); + stub = new (size) RuntimeStub(stub_name, cb, size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments); + } + + // Do not hold the CodeCache lock during name formatting. + if (stub != NULL) { + char stub_id[256]; + jio_snprintf(stub_id, sizeof(stub_id), "RuntimeStub - %s", stub_name); + if (PrintStubCode) { + tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, stub); + Disassembler::decode(stub->instructions_begin(), stub->instructions_end()); + } + VTune::register_stub(stub_id, stub->instructions_begin(), stub->instructions_end()); + Forte::register_stub(stub_id, stub->instructions_begin(), stub->instructions_end()); + + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated(stub_name, stub->instructions_begin(), stub->instructions_end()); + } + } + + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); + + return stub; +} + + +void* RuntimeStub::operator new(size_t s, unsigned size) { + void* p = CodeCache::allocate(size); + if (!p) fatal("Initial size of CodeCache is too small"); + return p; +} + + +//---------------------------------------------------------------------------------------------------- +// Implementation of DeoptimizationBlob + +DeoptimizationBlob::DeoptimizationBlob( + CodeBuffer* cb, + int size, + OopMapSet* oop_maps, + int unpack_offset, + int unpack_with_exception_offset, + int unpack_with_reexecution_offset, + int frame_size +) +: SingletonBlob("DeoptimizationBlob", cb, sizeof(DeoptimizationBlob), size, frame_size, oop_maps) +{ + _unpack_offset = unpack_offset; + _unpack_with_exception = unpack_with_exception_offset; + _unpack_with_reexecution = unpack_with_reexecution_offset; +#ifdef COMPILER1 + _unpack_with_exception_in_tls = -1; +#endif +} + + +DeoptimizationBlob* DeoptimizationBlob::create( + CodeBuffer* cb, + OopMapSet* oop_maps, + int unpack_offset, + int unpack_with_exception_offset, + int unpack_with_reexecution_offset, + int frame_size) +{ + DeoptimizationBlob* blob = NULL; + ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + unsigned int size = allocation_size(cb, sizeof(DeoptimizationBlob)); + blob = new (size) DeoptimizationBlob(cb, + size, + oop_maps, + unpack_offset, + unpack_with_exception_offset, + unpack_with_reexecution_offset, + frame_size); + } + + // Do not hold the CodeCache lock during name formatting. + if (blob != NULL) { + char blob_id[256]; + jio_snprintf(blob_id, sizeof(blob_id), "DeoptimizationBlob@" PTR_FORMAT, blob->instructions_begin()); + if (PrintStubCode) { + tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob); + Disassembler::decode(blob->instructions_begin(), blob->instructions_end()); + } + VTune::register_stub(blob_id, blob->instructions_begin(), blob->instructions_end()); + Forte::register_stub(blob_id, blob->instructions_begin(), blob->instructions_end()); + + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated("DeoptimizationBlob", + blob->instructions_begin(), + blob->instructions_end()); + } + } + + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); + + return blob; +} + + +void* DeoptimizationBlob::operator new(size_t s, unsigned size) { + void* p = CodeCache::allocate(size); + if (!p) fatal("Initial size of CodeCache is too small"); + return p; +} + +//---------------------------------------------------------------------------------------------------- +// Implementation of UncommonTrapBlob + +#ifdef COMPILER2 +UncommonTrapBlob::UncommonTrapBlob( + CodeBuffer* cb, + int size, + OopMapSet* oop_maps, + int frame_size +) +: SingletonBlob("UncommonTrapBlob", cb, sizeof(UncommonTrapBlob), size, frame_size, oop_maps) +{} + + +UncommonTrapBlob* UncommonTrapBlob::create( + CodeBuffer* cb, + OopMapSet* oop_maps, + int frame_size) +{ + UncommonTrapBlob* blob = NULL; + ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + unsigned int size = allocation_size(cb, sizeof(UncommonTrapBlob)); + blob = new (size) UncommonTrapBlob(cb, size, oop_maps, frame_size); + } + + // Do not hold the CodeCache lock during name formatting. + if (blob != NULL) { + char blob_id[256]; + jio_snprintf(blob_id, sizeof(blob_id), "UncommonTrapBlob@" PTR_FORMAT, blob->instructions_begin()); + if (PrintStubCode) { + tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob); + Disassembler::decode(blob->instructions_begin(), blob->instructions_end()); + } + VTune::register_stub(blob_id, blob->instructions_begin(), blob->instructions_end()); + Forte::register_stub(blob_id, blob->instructions_begin(), blob->instructions_end()); + + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated("UncommonTrapBlob", + blob->instructions_begin(), + blob->instructions_end()); + } + } + + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); + + return blob; +} + + +void* UncommonTrapBlob::operator new(size_t s, unsigned size) { + void* p = CodeCache::allocate(size); + if (!p) fatal("Initial size of CodeCache is too small"); + return p; +} +#endif // COMPILER2 + + +//---------------------------------------------------------------------------------------------------- +// Implementation of ExceptionBlob + +#ifdef COMPILER2 +ExceptionBlob::ExceptionBlob( + CodeBuffer* cb, + int size, + OopMapSet* oop_maps, + int frame_size +) +: SingletonBlob("ExceptionBlob", cb, sizeof(ExceptionBlob), size, frame_size, oop_maps) +{} + + +ExceptionBlob* ExceptionBlob::create( + CodeBuffer* cb, + OopMapSet* oop_maps, + int frame_size) +{ + ExceptionBlob* blob = NULL; + ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + unsigned int size = allocation_size(cb, sizeof(ExceptionBlob)); + blob = new (size) ExceptionBlob(cb, size, oop_maps, frame_size); + } + + // We do not need to hold the CodeCache lock during name formatting + if (blob != NULL) { + char blob_id[256]; + jio_snprintf(blob_id, sizeof(blob_id), "ExceptionBlob@" PTR_FORMAT, blob->instructions_begin()); + if (PrintStubCode) { + tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob); + Disassembler::decode(blob->instructions_begin(), blob->instructions_end()); + } + VTune::register_stub(blob_id, blob->instructions_begin(), blob->instructions_end()); + Forte::register_stub(blob_id, blob->instructions_begin(), blob->instructions_end()); + + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated("ExceptionBlob", + blob->instructions_begin(), + blob->instructions_end()); + } + } + + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); + + return blob; +} + + +void* ExceptionBlob::operator new(size_t s, unsigned size) { + void* p = CodeCache::allocate(size); + if (!p) fatal("Initial size of CodeCache is too small"); + return p; +} +#endif // COMPILER2 + + +//---------------------------------------------------------------------------------------------------- +// Implementation of SafepointBlob + +SafepointBlob::SafepointBlob( + CodeBuffer* cb, + int size, + OopMapSet* oop_maps, + int frame_size +) +: SingletonBlob("SafepointBlob", cb, sizeof(SafepointBlob), size, frame_size, oop_maps) +{} + + +SafepointBlob* SafepointBlob::create( + CodeBuffer* cb, + OopMapSet* oop_maps, + int frame_size) +{ + SafepointBlob* blob = NULL; + ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + unsigned int size = allocation_size(cb, sizeof(SafepointBlob)); + blob = new (size) SafepointBlob(cb, size, oop_maps, frame_size); + } + + // We do not need to hold the CodeCache lock during name formatting. + if (blob != NULL) { + char blob_id[256]; + jio_snprintf(blob_id, sizeof(blob_id), "SafepointBlob@" PTR_FORMAT, blob->instructions_begin()); + if (PrintStubCode) { + tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob); + Disassembler::decode(blob->instructions_begin(), blob->instructions_end()); + } + VTune::register_stub(blob_id, blob->instructions_begin(), blob->instructions_end()); + Forte::register_stub(blob_id, blob->instructions_begin(), blob->instructions_end()); + + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated("SafepointBlob", + blob->instructions_begin(), + blob->instructions_end()); + } + } + + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); + + return blob; +} + + +void* SafepointBlob::operator new(size_t s, unsigned size) { + void* p = CodeCache::allocate(size); + if (!p) fatal("Initial size of CodeCache is too small"); + return p; +} + + +//---------------------------------------------------------------------------------------------------- +// Verification and printing + +void CodeBlob::verify() { + ShouldNotReachHere(); +} + +#ifndef PRODUCT + +void CodeBlob::print() const { + tty->print_cr("[CodeBlob (" INTPTR_FORMAT ")]", this); + tty->print_cr("Framesize: %d", _frame_size); +} + + +void CodeBlob::print_value_on(outputStream* st) const { + st->print_cr("[CodeBlob]"); +} + +#endif + +void BufferBlob::verify() { + // unimplemented +} + +#ifndef PRODUCT + +void BufferBlob::print() const { + CodeBlob::print(); + print_value_on(tty); +} + + +void BufferBlob::print_value_on(outputStream* st) const { + st->print_cr("BufferBlob (" INTPTR_FORMAT ") used for %s", this, name()); +} + + +#endif + +void RuntimeStub::verify() { + // unimplemented +} + +#ifndef PRODUCT + +void RuntimeStub::print() const { + CodeBlob::print(); + tty->print("Runtime Stub (" INTPTR_FORMAT "): ", this); + tty->print_cr(name()); + Disassembler::decode((CodeBlob*)this); +} + + +void RuntimeStub::print_value_on(outputStream* st) const { + st->print("RuntimeStub (" INTPTR_FORMAT "): ", this); st->print(name()); +} + +#endif + +void SingletonBlob::verify() { + // unimplemented +} + +#ifndef PRODUCT + +void SingletonBlob::print() const { + CodeBlob::print(); + tty->print_cr(name()); + Disassembler::decode((CodeBlob*)this); +} + + +void SingletonBlob::print_value_on(outputStream* st) const { + st->print_cr(name()); +} + +void DeoptimizationBlob::print_value_on(outputStream* st) const { + st->print_cr("Deoptimization (frame not available)"); +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/code/codeBlob.hpp b/hotspot/src/share/vm/code/codeBlob.hpp new file mode 100644 index 00000000000..748d7de032a --- /dev/null +++ b/hotspot/src/share/vm/code/codeBlob.hpp @@ -0,0 +1,511 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// CodeBlob - superclass for all entries in the CodeCache. +// +// Suptypes are: +// nmethod : Compiled Java methods (include method that calls to native code) +// RuntimeStub : Call to VM runtime methods +// DeoptimizationBlob : Used for deoptimizatation +// ExceptionBlob : Used for stack unrolling +// SafepointBlob : Used to handle illegal instruction exceptions +// +// +// Layout: +// - header +// - relocation +// - instruction space +// - data space +class DeoptimizationBlob; + +class CodeBlob VALUE_OBJ_CLASS_SPEC { + + friend class VMStructs; + + private: + const char* _name; + int _size; // total size of CodeBlob in bytes + int _header_size; // size of header (depends on subclass) + int _relocation_size; // size of relocation + int _instructions_offset; // offset to where instructions region begins + int _frame_complete_offset; // instruction offsets in [0.._frame_complete_offset) have + // not finished setting up their frame. Beware of pc's in + // that range. There is a similar range(s) on returns + // which we don't detect. + int _data_offset; // offset to where data region begins + int _oops_offset; // offset to where embedded oop table begins (inside data) + int _oops_length; // number of embedded oops + int _frame_size; // size of stack frame + OopMapSet* _oop_maps; // OopMap for this CodeBlob + CodeComments _comments; + + friend class OopRecorder; + + void fix_oop_relocations(address begin, address end, bool initialize_immediates); + inline void initialize_immediate_oop(oop* dest, jobject handle); + + public: + // Returns the space needed for CodeBlob + static unsigned int allocation_size(CodeBuffer* cb, int header_size); + + // Creation + // a) simple CodeBlob + // frame_complete is the offset from the beginning of the instructions + // to where the frame setup (from stackwalk viewpoint) is complete. + CodeBlob(const char* name, int header_size, int size, int frame_complete, int locs_size); + + // b) full CodeBlob + CodeBlob( + const char* name, + CodeBuffer* cb, + int header_size, + int size, + int frame_complete, + int frame_size, + OopMapSet* oop_maps + ); + + // Deletion + void flush(); + + // Typing + virtual bool is_buffer_blob() const { return false; } + virtual bool is_nmethod() const { return false; } + virtual bool is_runtime_stub() const { return false; } + virtual bool is_deoptimization_stub() const { return false; } + virtual bool is_uncommon_trap_stub() const { return false; } + virtual bool is_exception_stub() const { return false; } + virtual bool is_safepoint_stub() const { return false; } + virtual bool is_adapter_blob() const { return false; } + + virtual bool is_compiled_by_c2() const { return false; } + virtual bool is_compiled_by_c1() const { return false; } + + // Boundaries + address header_begin() const { return (address) this; } + address header_end() const { return ((address) this) + _header_size; }; + relocInfo* relocation_begin() const { return (relocInfo*) header_end(); }; + relocInfo* relocation_end() const { return (relocInfo*)(header_end() + _relocation_size); } + address instructions_begin() const { return (address) header_begin() + _instructions_offset; } + address instructions_end() const { return (address) header_begin() + _data_offset; } + address data_begin() const { return (address) header_begin() + _data_offset; } + address data_end() const { return (address) header_begin() + _size; } + oop* oops_begin() const { return (oop*) (header_begin() + _oops_offset); } + oop* oops_end() const { return oops_begin() + _oops_length; } + + // Offsets + int relocation_offset() const { return _header_size; } + int instructions_offset() const { return _instructions_offset; } + int data_offset() const { return _data_offset; } + int oops_offset() const { return _oops_offset; } + + // Sizes + int size() const { return _size; } + int header_size() const { return _header_size; } + int relocation_size() const { return (address) relocation_end() - (address) relocation_begin(); } + int instructions_size() const { return instructions_end() - instructions_begin(); } + int data_size() const { return data_end() - data_begin(); } + int oops_size() const { return (address) oops_end() - (address) oops_begin(); } + + // Containment + bool blob_contains(address addr) const { return header_begin() <= addr && addr < data_end(); } + bool relocation_contains(relocInfo* addr) const{ return relocation_begin() <= addr && addr < relocation_end(); } + bool instructions_contains(address addr) const { return instructions_begin() <= addr && addr < instructions_end(); } + bool data_contains(address addr) const { return data_begin() <= addr && addr < data_end(); } + bool oops_contains(oop* addr) const { return oops_begin() <= addr && addr < oops_end(); } + bool contains(address addr) const { return instructions_contains(addr); } + bool is_frame_complete_at(address addr) const { return instructions_contains(addr) && + addr >= instructions_begin() + _frame_complete_offset; } + + // Relocation support + void fix_oop_relocations(address begin, address end) { + fix_oop_relocations(begin, end, false); + } + void fix_oop_relocations() { + fix_oop_relocations(NULL, NULL, false); + } + relocInfo::relocType reloc_type_for_address(address pc); + bool is_at_poll_return(address pc); + bool is_at_poll_or_poll_return(address pc); + + // Support for oops in scopes and relocs: + // Note: index 0 is reserved for null. + oop oop_at(int index) const { return index == 0? (oop)NULL: *oop_addr_at(index); } + oop* oop_addr_at(int index) const{ // for GC + // relocation indexes are biased by 1 (because 0 is reserved) + assert(index > 0 && index <= _oops_length, "must be a valid non-zero index"); + return &oops_begin()[index-1]; + } + + void copy_oops(GrowableArray* oops); + + // CodeCache support: really only used by the nmethods, but in order to get + // asserts and certain bookkeeping to work in the CodeCache they are defined + // virtual here. + virtual bool is_zombie() const { return false; } + virtual bool is_locked_by_vm() const { return false; } + + virtual bool is_unloaded() const { return false; } + virtual bool is_not_entrant() const { return false; } + + // GC support + virtual bool is_alive() const = 0; + virtual void do_unloading(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + bool unloading_occurred); + virtual void oops_do(OopClosure* f) = 0; + + // OopMap for frame + OopMapSet* oop_maps() const { return _oop_maps; } + void set_oop_maps(OopMapSet* p); + OopMap* oop_map_for_return_address(address return_address); + virtual void preserve_callee_argument_oops(frame fr, const RegisterMap* reg_map, OopClosure* f) { ShouldNotReachHere(); } + + // Frame support + int frame_size() const { return _frame_size; } + void set_frame_size(int size) { _frame_size = size; } + + // Returns true, if the next frame is responsible for GC'ing oops passed as arguments + virtual bool caller_must_gc_arguments(JavaThread* thread) const { return false; } + + // Naming + const char* name() const { return _name; } + void set_name(const char* name) { _name = name; } + + // Debugging + virtual void verify(); + virtual void print() const PRODUCT_RETURN; + virtual void print_value_on(outputStream* st) const PRODUCT_RETURN; + + // Print the comment associated with offset on stream, if there is one + void print_block_comment(outputStream* stream, intptr_t offset) { + _comments.print_block_comment(stream, offset); + } + + // Transfer ownership of comments to this CodeBlob + void set_comments(CodeComments& comments) { + _comments.assign(comments); + } +}; + + +//---------------------------------------------------------------------------------------------------- +// BufferBlob: used to hold non-relocatable machine code such as the interpreter, stubroutines, etc. + +class BufferBlob: public CodeBlob { + friend class VMStructs; + private: + // Creation support + BufferBlob(const char* name, int size); + BufferBlob(const char* name, int size, CodeBuffer* cb); + + void* operator new(size_t s, unsigned size); + + public: + // Creation + static BufferBlob* create(const char* name, int buffer_size); + static BufferBlob* create(const char* name, CodeBuffer* cb); + + static void free(BufferBlob* buf); + + // Typing + bool is_buffer_blob() const { return true; } + bool is_adapter_blob() const; + + // GC/Verification support + void preserve_callee_argument_oops(frame fr, const RegisterMap* reg_map, OopClosure* f) { /* nothing to do */ } + bool is_alive() const { return true; } + void do_unloading(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + bool unloading_occurred) { /* do nothing */ } + + void oops_do(OopClosure* f) { /* do nothing*/ } + + void verify(); + void print() const PRODUCT_RETURN; + void print_value_on(outputStream* st) const PRODUCT_RETURN; +}; + + +//---------------------------------------------------------------------------------------------------- +// RuntimeStub: describes stubs used by compiled code to call a (static) C++ runtime routine + +class RuntimeStub: public CodeBlob { + friend class VMStructs; + private: + bool _caller_must_gc_arguments; + + // Creation support + RuntimeStub( + const char* name, + CodeBuffer* cb, + int size, + int frame_complete, + int frame_size, + OopMapSet* oop_maps, + bool caller_must_gc_arguments + ); + + void* operator new(size_t s, unsigned size); + + public: + // Creation + static RuntimeStub* new_runtime_stub( + const char* stub_name, + CodeBuffer* cb, + int frame_complete, + int frame_size, + OopMapSet* oop_maps, + bool caller_must_gc_arguments + ); + + // Typing + bool is_runtime_stub() const { return true; } + + // GC support + bool caller_must_gc_arguments(JavaThread* thread) const { return _caller_must_gc_arguments; } + + address entry_point() { return instructions_begin(); } + + // GC/Verification support + void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) { /* nothing to do */ } + bool is_alive() const { return true; } + void do_unloading(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + bool unloading_occurred) { /* do nothing */ } + void oops_do(OopClosure* f) { /* do-nothing*/ } + + void verify(); + void print() const PRODUCT_RETURN; + void print_value_on(outputStream* st) const PRODUCT_RETURN; +}; + + +//---------------------------------------------------------------------------------------------------- +// Super-class for all blobs that exist in only one instance. Implements default behaviour. + +class SingletonBlob: public CodeBlob { + friend class VMStructs; + public: + SingletonBlob( + const char* name, + CodeBuffer* cb, + int header_size, + int size, + int frame_size, + OopMapSet* oop_maps + ) + : CodeBlob(name, cb, header_size, size, CodeOffsets::frame_never_safe, frame_size, oop_maps) + {}; + + bool is_alive() const { return true; } + void do_unloading(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + bool unloading_occurred) { /* do-nothing*/ } + + void verify(); // does nothing + void print() const PRODUCT_RETURN; + void print_value_on(outputStream* st) const PRODUCT_RETURN; +}; + + +//---------------------------------------------------------------------------------------------------- +// DeoptimizationBlob + +class DeoptimizationBlob: public SingletonBlob { + friend class VMStructs; + private: + int _unpack_offset; + int _unpack_with_exception; + int _unpack_with_reexecution; + + int _unpack_with_exception_in_tls; + + // Creation support + DeoptimizationBlob( + CodeBuffer* cb, + int size, + OopMapSet* oop_maps, + int unpack_offset, + int unpack_with_exception_offset, + int unpack_with_reexecution_offset, + int frame_size + ); + + void* operator new(size_t s, unsigned size); + + public: + // Creation + static DeoptimizationBlob* create( + CodeBuffer* cb, + OopMapSet* oop_maps, + int unpack_offset, + int unpack_with_exception_offset, + int unpack_with_reexecution_offset, + int frame_size + ); + + // Typing + bool is_deoptimization_stub() const { return true; } + const DeoptimizationBlob *as_deoptimization_stub() const { return this; } + bool exception_address_is_unpack_entry(address pc) const { + address unpack_pc = unpack(); + return (pc == unpack_pc || (pc + frame::pc_return_offset) == unpack_pc); + } + + + + + // GC for args + void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) { /* Nothing to do */ } + + // Iteration + void oops_do(OopClosure* f) {} + + // Printing + void print_value_on(outputStream* st) const PRODUCT_RETURN; + + address unpack() const { return instructions_begin() + _unpack_offset; } + address unpack_with_exception() const { return instructions_begin() + _unpack_with_exception; } + address unpack_with_reexecution() const { return instructions_begin() + _unpack_with_reexecution; } + + // Alternate entry point for C1 where the exception and issuing pc + // are in JavaThread::_exception_oop and JavaThread::_exception_pc + // instead of being in registers. This is needed because C1 doesn't + // model exception paths in a way that keeps these registers free so + // there may be live values in those registers during deopt. + void set_unpack_with_exception_in_tls_offset(int offset) { + _unpack_with_exception_in_tls = offset; + assert(contains(instructions_begin() + _unpack_with_exception_in_tls), "must be PC inside codeblob"); + } + address unpack_with_exception_in_tls() const { return instructions_begin() + _unpack_with_exception_in_tls; } +}; + + +//---------------------------------------------------------------------------------------------------- +// UncommonTrapBlob (currently only used by Compiler 2) + +#ifdef COMPILER2 + +class UncommonTrapBlob: public SingletonBlob { + friend class VMStructs; + private: + // Creation support + UncommonTrapBlob( + CodeBuffer* cb, + int size, + OopMapSet* oop_maps, + int frame_size + ); + + void* operator new(size_t s, unsigned size); + + public: + // Creation + static UncommonTrapBlob* create( + CodeBuffer* cb, + OopMapSet* oop_maps, + int frame_size + ); + + // GC for args + void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) { /* nothing to do */ } + + // Typing + bool is_uncommon_trap_stub() const { return true; } + + // Iteration + void oops_do(OopClosure* f) {} +}; + + +//---------------------------------------------------------------------------------------------------- +// ExceptionBlob: used for exception unwinding in compiled code (currently only used by Compiler 2) + +class ExceptionBlob: public SingletonBlob { + friend class VMStructs; + private: + // Creation support + ExceptionBlob( + CodeBuffer* cb, + int size, + OopMapSet* oop_maps, + int frame_size + ); + + void* operator new(size_t s, unsigned size); + + public: + // Creation + static ExceptionBlob* create( + CodeBuffer* cb, + OopMapSet* oop_maps, + int frame_size + ); + + // GC for args + void preserve_callee_argument_oops(frame fr, const RegisterMap* reg_map, OopClosure* f) { /* nothing to do */ } + + // Typing + bool is_exception_stub() const { return true; } + + // Iteration + void oops_do(OopClosure* f) {} +}; +#endif // COMPILER2 + + +//---------------------------------------------------------------------------------------------------- +// SafepointBlob: handles illegal_instruction exceptions during a safepoint + +class SafepointBlob: public SingletonBlob { + friend class VMStructs; + private: + // Creation support + SafepointBlob( + CodeBuffer* cb, + int size, + OopMapSet* oop_maps, + int frame_size + ); + + void* operator new(size_t s, unsigned size); + + public: + // Creation + static SafepointBlob* create( + CodeBuffer* cb, + OopMapSet* oop_maps, + int frame_size + ); + + // GC for args + void preserve_callee_argument_oops(frame fr, const RegisterMap* reg_map, OopClosure* f) { /* nothing to do */ } + + // Typing + bool is_safepoint_stub() const { return true; } + + // Iteration + void oops_do(OopClosure* f) {} +}; diff --git a/hotspot/src/share/vm/code/codeCache.cpp b/hotspot/src/share/vm/code/codeCache.cpp new file mode 100644 index 00000000000..ca4f8b7d0d4 --- /dev/null +++ b/hotspot/src/share/vm/code/codeCache.cpp @@ -0,0 +1,662 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_codeCache.cpp.incl" + +// Helper class for printing in CodeCache + +class CodeBlob_sizes { + private: + int count; + int total_size; + int header_size; + int code_size; + int stub_size; + int relocation_size; + int scopes_oop_size; + int scopes_data_size; + int scopes_pcs_size; + + public: + CodeBlob_sizes() { + count = 0; + total_size = 0; + header_size = 0; + code_size = 0; + stub_size = 0; + relocation_size = 0; + scopes_oop_size = 0; + scopes_data_size = 0; + scopes_pcs_size = 0; + } + + int total() { return total_size; } + bool is_empty() { return count == 0; } + + void print(const char* title) { + tty->print_cr(" #%d %s = %dK (hdr %d%%, loc %d%%, code %d%%, stub %d%%, [oops %d%%, data %d%%, pcs %d%%])", + count, + title, + total() / K, + header_size * 100 / total_size, + relocation_size * 100 / total_size, + code_size * 100 / total_size, + stub_size * 100 / total_size, + scopes_oop_size * 100 / total_size, + scopes_data_size * 100 / total_size, + scopes_pcs_size * 100 / total_size); + } + + void add(CodeBlob* cb) { + count++; + total_size += cb->size(); + header_size += cb->header_size(); + relocation_size += cb->relocation_size(); + scopes_oop_size += cb->oops_size(); + if (cb->is_nmethod()) { + nmethod *nm = (nmethod*)cb; + code_size += nm->code_size(); + stub_size += nm->stub_size(); + + scopes_data_size += nm->scopes_data_size(); + scopes_pcs_size += nm->scopes_pcs_size(); + } else { + code_size += cb->instructions_size(); + } + } +}; + + +// CodeCache implementation + +CodeHeap * CodeCache::_heap = new CodeHeap(); +int CodeCache::_number_of_blobs = 0; +int CodeCache::_number_of_nmethods_with_dependencies = 0; +bool CodeCache::_needs_cache_clean = false; + + +CodeBlob* CodeCache::first() { + assert_locked_or_safepoint(CodeCache_lock); + return (CodeBlob*)_heap->first(); +} + + +CodeBlob* CodeCache::next(CodeBlob* cb) { + assert_locked_or_safepoint(CodeCache_lock); + return (CodeBlob*)_heap->next(cb); +} + + +CodeBlob* CodeCache::alive(CodeBlob *cb) { + assert_locked_or_safepoint(CodeCache_lock); + while (cb != NULL && !cb->is_alive()) cb = next(cb); + return cb; +} + + +nmethod* CodeCache::alive_nmethod(CodeBlob* cb) { + assert_locked_or_safepoint(CodeCache_lock); + while (cb != NULL && (!cb->is_alive() || !cb->is_nmethod())) cb = next(cb); + return (nmethod*)cb; +} + + +CodeBlob* CodeCache::allocate(int size) { + // Do not seize the CodeCache lock here--if the caller has not + // already done so, we are going to lose bigtime, since the code + // cache will contain a garbage CodeBlob until the caller can + // run the constructor for the CodeBlob subclass he is busy + // instantiating. + guarantee(size >= 0, "allocation request must be reasonable"); + assert_locked_or_safepoint(CodeCache_lock); + CodeBlob* cb = NULL; + _number_of_blobs++; + while (true) { + cb = (CodeBlob*)_heap->allocate(size); + if (cb != NULL) break; + if (!_heap->expand_by(CodeCacheExpansionSize)) { + // Expansion failed + return NULL; + } + if (PrintCodeCacheExtension) { + ResourceMark rm; + tty->print_cr("code cache extended to [" INTPTR_FORMAT ", " INTPTR_FORMAT "] (%d bytes)", + (intptr_t)_heap->begin(), (intptr_t)_heap->end(), + (address)_heap->end() - (address)_heap->begin()); + } + } + verify_if_often(); + if (PrintCodeCache2) { // Need to add a new flag + ResourceMark rm; + tty->print_cr("CodeCache allocation: addr: " INTPTR_FORMAT ", size: 0x%x\n", cb, size); + } + return cb; +} + +void CodeCache::free(CodeBlob* cb) { + assert_locked_or_safepoint(CodeCache_lock); + verify_if_often(); + + if (PrintCodeCache2) { // Need to add a new flag + ResourceMark rm; + tty->print_cr("CodeCache free: addr: " INTPTR_FORMAT ", size: 0x%x\n", cb, cb->size()); + } + if (cb->is_nmethod() && ((nmethod *)cb)->has_dependencies()) { + _number_of_nmethods_with_dependencies--; + } + _number_of_blobs--; + + _heap->deallocate(cb); + + verify_if_often(); + assert(_number_of_blobs >= 0, "sanity check"); +} + + +void CodeCache::commit(CodeBlob* cb) { + // this is called by nmethod::nmethod, which must already own CodeCache_lock + assert_locked_or_safepoint(CodeCache_lock); + if (cb->is_nmethod() && ((nmethod *)cb)->has_dependencies()) { + _number_of_nmethods_with_dependencies++; + } + // flush the hardware I-cache + ICache::invalidate_range(cb->instructions_begin(), cb->instructions_size()); +} + + +void CodeCache::flush() { + assert_locked_or_safepoint(CodeCache_lock); + Unimplemented(); +} + + +// Iteration over CodeBlobs + +#define FOR_ALL_BLOBS(var) for (CodeBlob *var = first() ; var != NULL; var = next(var) ) +#define FOR_ALL_ALIVE_BLOBS(var) for (CodeBlob *var = alive(first()); var != NULL; var = alive(next(var))) +#define FOR_ALL_ALIVE_NMETHODS(var) for (nmethod *var = alive_nmethod(first()); var != NULL; var = alive_nmethod(next(var))) + + +bool CodeCache::contains(void *p) { + // It should be ok to call contains without holding a lock + return _heap->contains(p); +} + + +// This method is safe to call without holding the CodeCache_lock, as long as a dead codeblob is not +// looked up (i.e., one that has been marked for deletion). It only dependes on the _segmap to contain +// valid indices, which it will always do, as long as the CodeBlob is not in the process of being recycled. +CodeBlob* CodeCache::find_blob(void* start) { + CodeBlob* result = find_blob_unsafe(start); + if (result == NULL) return NULL; + // We could potientially look up non_entrant methods + guarantee(!result->is_zombie() || result->is_locked_by_vm() || is_error_reported(), "unsafe access to zombie method"); + return result; +} + +nmethod* CodeCache::find_nmethod(void* start) { + CodeBlob *cb = find_blob(start); + assert(cb == NULL || cb->is_nmethod(), "did not find an nmethod"); + return (nmethod*)cb; +} + + +void CodeCache::blobs_do(void f(CodeBlob* nm)) { + assert_locked_or_safepoint(CodeCache_lock); + FOR_ALL_BLOBS(p) { + f(p); + } +} + + +void CodeCache::nmethods_do(void f(nmethod* nm)) { + assert_locked_or_safepoint(CodeCache_lock); + FOR_ALL_BLOBS(nm) { + if (nm->is_nmethod()) f((nmethod*)nm); + } +} + + +int CodeCache::alignment_unit() { + return (int)_heap->alignment_unit(); +} + + +int CodeCache::alignment_offset() { + return (int)_heap->alignment_offset(); +} + + +// Mark code blobs for unloading if they contain otherwise +// unreachable oops. +void CodeCache::do_unloading(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + bool unloading_occurred) { + assert_locked_or_safepoint(CodeCache_lock); + FOR_ALL_ALIVE_BLOBS(cb) { + cb->do_unloading(is_alive, keep_alive, unloading_occurred); + } +} + +void CodeCache::oops_do(OopClosure* f) { + assert_locked_or_safepoint(CodeCache_lock); + FOR_ALL_ALIVE_BLOBS(cb) { + cb->oops_do(f); + } +} + +void CodeCache::gc_prologue() { +} + + +void CodeCache::gc_epilogue() { + assert_locked_or_safepoint(CodeCache_lock); + FOR_ALL_ALIVE_BLOBS(cb) { + if (cb->is_nmethod()) { + nmethod *nm = (nmethod*)cb; + assert(!nm->is_unloaded(), "Tautology"); + if (needs_cache_clean()) { + nm->cleanup_inline_caches(); + } + debug_only(nm->verify();) + } + cb->fix_oop_relocations(); + } + set_needs_cache_clean(false); +} + + +address CodeCache::first_address() { + assert_locked_or_safepoint(CodeCache_lock); + return (address)_heap->begin(); +} + + +address CodeCache::last_address() { + assert_locked_or_safepoint(CodeCache_lock); + return (address)_heap->end(); +} + + +void icache_init(); + +void CodeCache::initialize() { + assert(CodeCacheSegmentSize >= (uintx)CodeEntryAlignment, "CodeCacheSegmentSize must be large enough to align entry points"); +#ifdef COMPILER2 + assert(CodeCacheSegmentSize >= (uintx)OptoLoopAlignment, "CodeCacheSegmentSize must be large enough to align inner loops"); +#endif + assert(CodeCacheSegmentSize >= sizeof(jdouble), "CodeCacheSegmentSize must be large enough to align constants"); + // This was originally just a check of the alignment, causing failure, instead, round + // the code cache to the page size. In particular, Solaris is moving to a larger + // default page size. + CodeCacheExpansionSize = round_to(CodeCacheExpansionSize, os::vm_page_size()); + InitialCodeCacheSize = round_to(InitialCodeCacheSize, os::vm_page_size()); + ReservedCodeCacheSize = round_to(ReservedCodeCacheSize, os::vm_page_size()); + if (!_heap->reserve(ReservedCodeCacheSize, InitialCodeCacheSize, CodeCacheSegmentSize)) { + vm_exit_during_initialization("Could not reserve enough space for code cache"); + } + + MemoryService::add_code_heap_memory_pool(_heap); + + // Initialize ICache flush mechanism + // This service is needed for os::register_code_area + icache_init(); + + // Give OS a chance to register generated code area. + // This is used on Windows 64 bit platforms to register + // Structured Exception Handlers for our generated code. + os::register_code_area(_heap->low_boundary(), _heap->high_boundary()); +} + + +void codeCache_init() { + CodeCache::initialize(); +} + +//------------------------------------------------------------------------------------------------ + +int CodeCache::number_of_nmethods_with_dependencies() { + return _number_of_nmethods_with_dependencies; +} + +void CodeCache::clear_inline_caches() { + assert_locked_or_safepoint(CodeCache_lock); + FOR_ALL_ALIVE_NMETHODS(nm) { + nm->clear_inline_caches(); + } +} + +#ifndef PRODUCT +// used to keep track of how much time is spent in mark_for_deoptimization +static elapsedTimer dependentCheckTime; +static int dependentCheckCount = 0; +#endif // PRODUCT + + +int CodeCache::mark_for_deoptimization(DepChange& changes) { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + +#ifndef PRODUCT + dependentCheckTime.start(); + dependentCheckCount++; +#endif // PRODUCT + + int number_of_marked_CodeBlobs = 0; + + // search the hierarchy looking for nmethods which are affected by the loading of this class + + // then search the interfaces this class implements looking for nmethods + // which might be dependent of the fact that an interface only had one + // implementor. + + { No_Safepoint_Verifier nsv; + for (DepChange::ContextStream str(changes, nsv); str.next(); ) { + klassOop d = str.klass(); + number_of_marked_CodeBlobs += instanceKlass::cast(d)->mark_dependent_nmethods(changes); + } + } + + if (VerifyDependencies) { + // Turn off dependency tracing while actually testing deps. + NOT_PRODUCT( FlagSetting fs(TraceDependencies, false) ); + FOR_ALL_ALIVE_NMETHODS(nm) { + if (!nm->is_marked_for_deoptimization() && + nm->check_all_dependencies()) { + ResourceMark rm; + tty->print_cr("Should have been marked for deoptimization:"); + changes.print(); + nm->print(); + nm->print_dependencies(); + } + } + } + +#ifndef PRODUCT + dependentCheckTime.stop(); +#endif // PRODUCT + + return number_of_marked_CodeBlobs; +} + + +#ifdef HOTSWAP +int CodeCache::mark_for_evol_deoptimization(instanceKlassHandle dependee) { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + int number_of_marked_CodeBlobs = 0; + + // Deoptimize all methods of the evolving class itself + objArrayOop old_methods = dependee->methods(); + for (int i = 0; i < old_methods->length(); i++) { + ResourceMark rm; + methodOop old_method = (methodOop) old_methods->obj_at(i); + nmethod *nm = old_method->code(); + if (nm != NULL) { + nm->mark_for_deoptimization(); + number_of_marked_CodeBlobs++; + } + } + + FOR_ALL_ALIVE_NMETHODS(nm) { + if (nm->is_marked_for_deoptimization()) { + // ...Already marked in the previous pass; don't count it again. + } else if (nm->is_evol_dependent_on(dependee())) { + ResourceMark rm; + nm->mark_for_deoptimization(); + number_of_marked_CodeBlobs++; + } else { + // flush caches in case they refer to a redefined methodOop + nm->clear_inline_caches(); + } + } + + return number_of_marked_CodeBlobs; +} +#endif // HOTSWAP + + +// Deoptimize all methods +void CodeCache::mark_all_nmethods_for_deoptimization() { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + FOR_ALL_ALIVE_NMETHODS(nm) { + nm->mark_for_deoptimization(); + } +} + + +int CodeCache::mark_for_deoptimization(methodOop dependee) { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + int number_of_marked_CodeBlobs = 0; + + FOR_ALL_ALIVE_NMETHODS(nm) { + if (nm->is_dependent_on_method(dependee)) { + ResourceMark rm; + nm->mark_for_deoptimization(); + number_of_marked_CodeBlobs++; + } + } + + return number_of_marked_CodeBlobs; +} + +void CodeCache::make_marked_nmethods_zombies() { + assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint"); + FOR_ALL_ALIVE_NMETHODS(nm) { + if (nm->is_marked_for_deoptimization()) { + + // If the nmethod has already been made non-entrant and it can be converted + // then zombie it now. Otherwise make it non-entrant and it will eventually + // be zombied when it is no longer seen on the stack. Note that the nmethod + // might be "entrant" and not on the stack and so could be zombied immediately + // but we can't tell because we don't track it on stack until it becomes + // non-entrant. + + if (nm->is_not_entrant() && nm->can_not_entrant_be_converted()) { + nm->make_zombie(); + } else { + nm->make_not_entrant(); + } + } + } +} + +void CodeCache::make_marked_nmethods_not_entrant() { + assert_locked_or_safepoint(CodeCache_lock); + FOR_ALL_ALIVE_NMETHODS(nm) { + if (nm->is_marked_for_deoptimization()) { + nm->make_not_entrant(); + } + } +} + +void CodeCache::verify() { + _heap->verify(); + FOR_ALL_ALIVE_BLOBS(p) { + p->verify(); + } +} + +//------------------------------------------------------------------------------------------------ +// Non-product version + +#ifndef PRODUCT + +void CodeCache::verify_if_often() { + if (VerifyCodeCacheOften) { + _heap->verify(); + } +} + +void CodeCache::print_internals() { + int nmethodCount = 0; + int runtimeStubCount = 0; + int adapterCount = 0; + int deoptimizationStubCount = 0; + int uncommonTrapStubCount = 0; + int bufferBlobCount = 0; + int total = 0; + int nmethodAlive = 0; + int nmethodNotEntrant = 0; + int nmethodZombie = 0; + int nmethodUnloaded = 0; + int nmethodJava = 0; + int nmethodNative = 0; + int maxCodeSize = 0; + ResourceMark rm; + + CodeBlob *cb; + for (cb = first(); cb != NULL; cb = next(cb)) { + total++; + if (cb->is_nmethod()) { + nmethod* nm = (nmethod*)cb; + + if (Verbose && nm->method() != NULL) { + ResourceMark rm; + char *method_name = nm->method()->name_and_sig_as_C_string(); + tty->print("%s", method_name); + if(nm->is_alive()) { tty->print_cr(" alive"); } + if(nm->is_not_entrant()) { tty->print_cr(" not-entrant"); } + if(nm->is_zombie()) { tty->print_cr(" zombie"); } + } + + nmethodCount++; + + if(nm->is_alive()) { nmethodAlive++; } + if(nm->is_not_entrant()) { nmethodNotEntrant++; } + if(nm->is_zombie()) { nmethodZombie++; } + if(nm->is_unloaded()) { nmethodUnloaded++; } + if(nm->is_native_method()) { nmethodNative++; } + + if(nm->method() != NULL && nm->is_java_method()) { + nmethodJava++; + if(nm->code_size() > maxCodeSize) { + maxCodeSize = nm->code_size(); + } + } + } else if (cb->is_runtime_stub()) { + runtimeStubCount++; + } else if (cb->is_deoptimization_stub()) { + deoptimizationStubCount++; + } else if (cb->is_uncommon_trap_stub()) { + uncommonTrapStubCount++; + } else if (cb->is_adapter_blob()) { + adapterCount++; + } else if (cb->is_buffer_blob()) { + bufferBlobCount++; + } + } + + int bucketSize = 512; + int bucketLimit = maxCodeSize / bucketSize + 1; + int *buckets = NEW_C_HEAP_ARRAY(int, bucketLimit); + memset(buckets,0,sizeof(int) * bucketLimit); + + for (cb = first(); cb != NULL; cb = next(cb)) { + if (cb->is_nmethod()) { + nmethod* nm = (nmethod*)cb; + if(nm->is_java_method()) { + buckets[nm->code_size() / bucketSize]++; + } + } + } + tty->print_cr("Code Cache Entries (total of %d)",total); + tty->print_cr("-------------------------------------------------"); + tty->print_cr("nmethods: %d",nmethodCount); + tty->print_cr("\talive: %d",nmethodAlive); + tty->print_cr("\tnot_entrant: %d",nmethodNotEntrant); + tty->print_cr("\tzombie: %d",nmethodZombie); + tty->print_cr("\tunloaded: %d",nmethodUnloaded); + tty->print_cr("\tjava: %d",nmethodJava); + tty->print_cr("\tnative: %d",nmethodNative); + tty->print_cr("runtime_stubs: %d",runtimeStubCount); + tty->print_cr("adapters: %d",adapterCount); + tty->print_cr("buffer blobs: %d",bufferBlobCount); + tty->print_cr("deoptimization_stubs: %d",deoptimizationStubCount); + tty->print_cr("uncommon_traps: %d",uncommonTrapStubCount); + tty->print_cr("\nnmethod size distribution (non-zombie java)"); + tty->print_cr("-------------------------------------------------"); + + for(int i=0; iprint("%d - %d bytes",i*bucketSize,(i+1)*bucketSize); + tty->fill_to(40); + tty->print_cr("%d",buckets[i]); + } + } + + FREE_C_HEAP_ARRAY(int, buckets); +} + +void CodeCache::print() { + CodeBlob_sizes live; + CodeBlob_sizes dead; + + FOR_ALL_BLOBS(p) { + if (!p->is_alive()) { + dead.add(p); + } else { + live.add(p); + } + } + + tty->print_cr("CodeCache:"); + + tty->print_cr("nmethod dependency checking time %f", dependentCheckTime.seconds(), + dependentCheckTime.seconds() / dependentCheckCount); + + if (!live.is_empty()) { + live.print("live"); + } + if (!dead.is_empty()) { + dead.print("dead"); + } + + + if (Verbose) { + // print the oop_map usage + int code_size = 0; + int number_of_blobs = 0; + int number_of_oop_maps = 0; + int map_size = 0; + FOR_ALL_BLOBS(p) { + if (p->is_alive()) { + number_of_blobs++; + code_size += p->instructions_size(); + OopMapSet* set = p->oop_maps(); + if (set != NULL) { + number_of_oop_maps += set->size(); + map_size += set->heap_size(); + } + } + } + tty->print_cr("OopMaps"); + tty->print_cr(" #blobs = %d", number_of_blobs); + tty->print_cr(" code size = %d", code_size); + tty->print_cr(" #oop_maps = %d", number_of_oop_maps); + tty->print_cr(" map size = %d", map_size); + } + +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/code/codeCache.hpp b/hotspot/src/share/vm/code/codeCache.hpp new file mode 100644 index 00000000000..a46d9202d1c --- /dev/null +++ b/hotspot/src/share/vm/code/codeCache.hpp @@ -0,0 +1,129 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The CodeCache implements the code cache for various pieces of generated +// code, e.g., compiled java methods, runtime stubs, transition frames, etc. +// The entries in the CodeCache are all CodeBlob's. + +// Implementation: +// - Each CodeBlob occupies one chunk of memory. +// - Like the offset table in oldspace the zone has at table for +// locating a method given a addess of an instruction. + +class OopClosure; +class DepChange; + +class CodeCache : AllStatic { + friend class VMStructs; + private: + // CodeHeap is malloc()'ed at startup and never deleted during shutdown, + // so that the generated assembly code is always there when it's needed. + // This may cause memory leak, but is necessary, for now. See 4423824, + // 4422213 or 4436291 for details. + static CodeHeap * _heap; + static int _number_of_blobs; + static int _number_of_nmethods_with_dependencies; + static bool _needs_cache_clean; + + static void verify_if_often() PRODUCT_RETURN; + public: + + // Initialization + static void initialize(); + + // Allocation/administration + static CodeBlob* allocate(int size); // allocates a new CodeBlob + static void commit(CodeBlob* cb); // called when the allocated CodeBlob has been filled + static int alignment_unit(); // guaranteed alignment of all CodeBlobs + static int alignment_offset(); // guaranteed offset of first CodeBlob byte within alignment unit (i.e., allocation header) + static void free(CodeBlob* cb); // frees a CodeBlob + static void flush(); // flushes all CodeBlobs + static bool contains(void *p); // returns whether p is included + static void blobs_do(void f(CodeBlob* cb)); // iterates over all CodeBlobs + static void nmethods_do(void f(nmethod* nm)); // iterates over all nmethods + + // Lookup + static CodeBlob* find_blob(void* start); + static nmethod* find_nmethod(void* start); + + // Lookup that does not fail if you lookup a zombie method (if you call this, be sure to know + // what you are doing) + static CodeBlob* find_blob_unsafe(void* start) { + CodeBlob* result = (CodeBlob*)_heap->find_start(start); + assert(result == NULL || result->blob_contains((address)start), "found wrong CodeBlob"); + return result; + } + + // Iteration + static CodeBlob* first(); + static CodeBlob* next (CodeBlob* cb); + static CodeBlob* alive(CodeBlob *cb); + static nmethod* alive_nmethod(CodeBlob *cb); + static int nof_blobs() { return _number_of_blobs; } + + // GC support + static void gc_epilogue(); + static void gc_prologue(); + // If "unloading_occurred" is true, then unloads (i.e., breaks root links + // to) any unmarked codeBlobs in the cache. Sets "marked_for_unloading" + // to "true" iff some code got unloaded. + static void do_unloading(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + bool unloading_occurred); + static void oops_do(OopClosure* f); + + // Printing/debugging + static void print() PRODUCT_RETURN; // prints summary + static void print_internals(); + static void verify(); // verifies the code cache + + // The full limits of the codeCache + static address low_bound() { return (address) _heap->low_boundary(); } + static address high_bound() { return (address) _heap->high_boundary(); } + + // Profiling + static address first_address(); // first address used for CodeBlobs + static address last_address(); // last address used for CodeBlobs + static size_t capacity() { return _heap->capacity(); } + static size_t max_capacity() { return _heap->max_capacity(); } + static size_t unallocated_capacity() { return _heap->unallocated_capacity(); } + + static bool needs_cache_clean() { return _needs_cache_clean; } + static void set_needs_cache_clean(bool v) { _needs_cache_clean = v; } + static void clear_inline_caches(); // clear all inline caches + + // Deoptimization + static int mark_for_deoptimization(DepChange& changes); +#ifdef HOTSWAP + static int mark_for_evol_deoptimization(instanceKlassHandle dependee); +#endif // HOTSWAP + + static void mark_all_nmethods_for_deoptimization(); + static int mark_for_deoptimization(methodOop dependee); + static void make_marked_nmethods_zombies(); + static void make_marked_nmethods_not_entrant(); + + // tells how many nmethods have dependencies + static int number_of_nmethods_with_dependencies(); +}; diff --git a/hotspot/src/share/vm/code/compiledIC.cpp b/hotspot/src/share/vm/code/compiledIC.cpp new file mode 100644 index 00000000000..4e1bd5a6686 --- /dev/null +++ b/hotspot/src/share/vm/code/compiledIC.cpp @@ -0,0 +1,662 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_compiledIC.cpp.incl" + + +// Every time a compiled IC is changed or its type is being accessed, +// either the CompiledIC_lock must be set or we must be at a safe point. + +//----------------------------------------------------------------------------- +// Low-level access to an inline cache. Private, since they might not be +// MT-safe to use. + +void CompiledIC::set_cached_oop(oop cache) { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + assert (!is_optimized(), "an optimized virtual call does not have a cached oop"); + assert (cache == NULL || cache != badOop, "invalid oop"); + + if (TraceCompiledIC) { + tty->print(" "); + print_compiled_ic(); + tty->print_cr(" changing oop to " INTPTR_FORMAT, (address)cache); + } + + if (cache == NULL) cache = (oop)Universe::non_oop_word(); + + *_oop_addr = cache; + // fix up the relocations + RelocIterator iter = _oops; + while (iter.next()) { + if (iter.type() == relocInfo::oop_type) { + oop_Relocation* r = iter.oop_reloc(); + if (r->oop_addr() == _oop_addr) + r->fix_oop_relocation(); + } + } + return; +} + + +oop CompiledIC::cached_oop() const { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + assert (!is_optimized(), "an optimized virtual call does not have a cached oop"); + + if (!is_in_transition_state()) { + oop data = *_oop_addr; + // If we let the oop value here be initialized to zero... + assert(data != NULL || Universe::non_oop_word() == NULL, + "no raw nulls in CompiledIC oops, because of patching races"); + return (data == (oop)Universe::non_oop_word()) ? (oop)NULL : data; + } else { + return InlineCacheBuffer::cached_oop_for((CompiledIC *)this); + } +} + + +void CompiledIC::set_ic_destination(address entry_point) { + assert(entry_point != NULL, "must set legal entry point"); + assert(CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + if (TraceCompiledIC) { + tty->print(" "); + print_compiled_ic(); + tty->print_cr(" changing destination to " INTPTR_FORMAT, entry_point); + } + MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag); +#ifdef ASSERT + CodeBlob* cb = CodeCache::find_blob_unsafe(_ic_call); + assert(cb != NULL && cb->is_nmethod(), "must be nmethod"); +#endif + _ic_call->set_destination_mt_safe(entry_point); +} + + +address CompiledIC::ic_destination() const { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + if (!is_in_transition_state()) { + return _ic_call->destination(); + } else { + return InlineCacheBuffer::ic_destination_for((CompiledIC *)this); + } +} + + +bool CompiledIC::is_in_transition_state() const { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + return InlineCacheBuffer::contains(_ic_call->destination()); +} + + +// Returns native address of 'call' instruction in inline-cache. Used by +// the InlineCacheBuffer when it needs to find the stub. +address CompiledIC::stub_address() const { + assert(is_in_transition_state(), "should only be called when we are in a transition state"); + return _ic_call->destination(); +} + + +//----------------------------------------------------------------------------- +// High-level access to an inline cache. Guaranteed to be MT-safe. + + +void CompiledIC::set_to_megamorphic(CallInfo* call_info, Bytecodes::Code bytecode, TRAPS) { + methodHandle method = call_info->selected_method(); + bool is_invoke_interface = (bytecode == Bytecodes::_invokeinterface && !call_info->has_vtable_index()); + assert(CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + assert(method->is_oop(), "cannot be NULL and must be oop"); + assert(!is_optimized(), "cannot set an optimized virtual call to megamorphic"); + assert(is_call_to_compiled() || is_call_to_interpreted(), "going directly to megamorphic?"); + + address entry; + if (is_invoke_interface) { + int index = klassItable::compute_itable_index(call_info->resolved_method()()); + entry = VtableStubs::create_stub(false, index, method()); + assert(entry != NULL, "entry not computed"); + klassOop k = call_info->resolved_method()->method_holder(); + assert(Klass::cast(k)->is_interface(), "sanity check"); + InlineCacheBuffer::create_transition_stub(this, k, entry); + } else { + // Can be different than method->vtable_index(), due to package-private etc. + int vtable_index = call_info->vtable_index(); + entry = VtableStubs::create_stub(true, vtable_index, method()); + InlineCacheBuffer::create_transition_stub(this, method(), entry); + } + + if (TraceICs) { + ResourceMark rm; + tty->print_cr ("IC@" INTPTR_FORMAT ": to megamorphic %s entry: " INTPTR_FORMAT, + instruction_address(), method->print_value_string(), entry); + } + + Events::log("compiledIC " INTPTR_FORMAT " --> megamorphic " INTPTR_FORMAT, this, (address)method()); + // We can't check this anymore. With lazy deopt we could have already + // cleaned this IC entry before we even return. This is possible if + // we ran out of space in the inline cache buffer trying to do the + // set_next and we safepointed to free up space. This is a benign + // race because the IC entry was complete when we safepointed so + // cleaning it immediately is harmless. + // assert(is_megamorphic(), "sanity check"); +} + + +// true if destination is megamorphic stub +bool CompiledIC::is_megamorphic() const { + assert(CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + assert(!is_optimized(), "an optimized call cannot be megamorphic"); + + // Cannot rely on cached_oop. It is either an interface or a method. + return VtableStubs::is_entry_point(ic_destination()); +} + +bool CompiledIC::is_call_to_compiled() const { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + + // Use unsafe, since an inline cache might point to a zombie method. However, the zombie + // method is guaranteed to still exist, since we only remove methods after all inline caches + // has been cleaned up + CodeBlob* cb = CodeCache::find_blob_unsafe(ic_destination()); + bool is_monomorphic = (cb != NULL && cb->is_nmethod()); + // Check that the cached_oop is a klass for non-optimized monomorphic calls + // This assertion is invalid for compiler1: a call that does not look optimized (no static stub) can be used + // for calling directly to vep without using the inline cache (i.e., cached_oop == NULL) +#ifdef ASSERT +#ifdef TIERED + CodeBlob* caller = CodeCache::find_blob_unsafe(instruction_address()); + bool is_c1_method = caller->is_compiled_by_c1(); +#else +#ifdef COMPILER1 + bool is_c1_method = true; +#else + bool is_c1_method = false; +#endif // COMPILER1 +#endif // TIERED + assert( is_c1_method || + !is_monomorphic || + is_optimized() || + (cached_oop() != NULL && cached_oop()->is_klass()), "sanity check"); +#endif // ASSERT + return is_monomorphic; +} + + +bool CompiledIC::is_call_to_interpreted() const { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + // Call to interpreter if destination is either calling to a stub (if it + // is optimized), or calling to an I2C blob + bool is_call_to_interpreted = false; + if (!is_optimized()) { + // must use unsafe because the destination can be a zombie (and we're cleaning) + // and the print_compiled_ic code wants to know if site (in the non-zombie) + // is to the interpreter. + CodeBlob* cb = CodeCache::find_blob_unsafe(ic_destination()); + is_call_to_interpreted = (cb != NULL && cb->is_adapter_blob()); + assert(!is_call_to_interpreted || (cached_oop() != NULL && cached_oop()->is_compiledICHolder()), "sanity check"); + } else { + // Check if we are calling into our own codeblob (i.e., to a stub) + CodeBlob* cb = CodeCache::find_blob(_ic_call->instruction_address()); + address dest = ic_destination(); +#ifdef ASSERT + { + CodeBlob* db = CodeCache::find_blob_unsafe(dest); + assert(!db->is_adapter_blob(), "must use stub!"); + } +#endif /* ASSERT */ + is_call_to_interpreted = cb->contains(dest); + } + return is_call_to_interpreted; +} + + +void CompiledIC::set_to_clean() { + assert(SafepointSynchronize::is_at_safepoint() || CompiledIC_lock->is_locked() , "MT-unsafe call"); + if (TraceInlineCacheClearing || TraceICs) { + tty->print_cr("IC@" INTPTR_FORMAT ": set to clean", instruction_address()); + print(); + } + + address entry; + if (is_optimized()) { + entry = SharedRuntime::get_resolve_opt_virtual_call_stub(); + } else { + entry = SharedRuntime::get_resolve_virtual_call_stub(); + } + + // A zombie transition will always be safe, since the oop has already been set to NULL, so + // we only need to patch the destination + bool safe_transition = is_optimized() || SafepointSynchronize::is_at_safepoint(); + + if (safe_transition) { + if (!is_optimized()) set_cached_oop(NULL); + // Kill any leftover stub we might have too + if (is_in_transition_state()) { + ICStub* old_stub = ICStub_from_destination_address(stub_address()); + old_stub->clear(); + } + set_ic_destination(entry); + } else { + // Unsafe transition - create stub. + InlineCacheBuffer::create_transition_stub(this, NULL, entry); + } + // We can't check this anymore. With lazy deopt we could have already + // cleaned this IC entry before we even return. This is possible if + // we ran out of space in the inline cache buffer trying to do the + // set_next and we safepointed to free up space. This is a benign + // race because the IC entry was complete when we safepointed so + // cleaning it immediately is harmless. + // assert(is_clean(), "sanity check"); +} + + +bool CompiledIC::is_clean() const { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + bool is_clean = false; + address dest = ic_destination(); + is_clean = dest == SharedRuntime::get_resolve_opt_virtual_call_stub() || + dest == SharedRuntime::get_resolve_virtual_call_stub(); + assert(!is_clean || is_optimized() || cached_oop() == NULL, "sanity check"); + return is_clean; +} + + +void CompiledIC::set_to_monomorphic(const CompiledICInfo& info) { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), ""); + // Updating a cache to the wrong entry can cause bugs that are very hard + // to track down - if cache entry gets invalid - we just clean it. In + // this way it is always the same code path that is responsible for + // updating and resolving an inline cache + // + // The above is no longer true. SharedRuntime::fixup_callers_callsite will change optimized + // callsites. In addition ic_miss code will update a site to monomorphic if it determines + // that an monomorphic call to the interpreter can now be monomorphic to compiled code. + // + // In both of these cases the only thing being modifed is the jump/call target and these + // transitions are mt_safe + + Thread *thread = Thread::current(); + if (info._to_interpreter) { + // Call to interpreter + if (info.is_optimized() && is_optimized()) { + assert(is_clean(), "unsafe IC path"); + MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag); + // the call analysis (callee structure) specifies that the call is optimized + // (either because of CHA or the static target is final) + // At code generation time, this call has been emitted as static call + // Call via stub + assert(info.cached_oop().not_null() && info.cached_oop()->is_method(), "sanity check"); + CompiledStaticCall* csc = compiledStaticCall_at(instruction_address()); + methodHandle method (thread, (methodOop)info.cached_oop()()); + csc->set_to_interpreted(method, info.entry()); + if (TraceICs) { + ResourceMark rm(thread); + tty->print_cr ("IC@" INTPTR_FORMAT ": monomorphic to interpreter: %s", + instruction_address(), + method->print_value_string()); + } + } else { + // Call via method-klass-holder + assert(info.cached_oop().not_null(), "must be set"); + InlineCacheBuffer::create_transition_stub(this, info.cached_oop()(), info.entry()); + + if (TraceICs) { + ResourceMark rm(thread); + tty->print_cr ("IC@" INTPTR_FORMAT ": monomorphic to interpreter via mkh", instruction_address()); + } + } + } else { + // Call to compiled code + bool static_bound = info.is_optimized() || (info.cached_oop().is_null()); +#ifdef ASSERT + CodeBlob* cb = CodeCache::find_blob_unsafe(info.entry()); + assert (cb->is_nmethod(), "must be compiled!"); +#endif /* ASSERT */ + + // This is MT safe if we come from a clean-cache and go through a + // non-verified entry point + bool safe = SafepointSynchronize::is_at_safepoint() || + (!is_in_transition_state() && (info.is_optimized() || static_bound || is_clean())); + + if (!safe) { + InlineCacheBuffer::create_transition_stub(this, info.cached_oop()(), info.entry()); + } else { + set_ic_destination(info.entry()); + if (!is_optimized()) set_cached_oop(info.cached_oop()()); + } + + if (TraceICs) { + ResourceMark rm(thread); + assert(info.cached_oop() == NULL || info.cached_oop()()->is_klass(), "must be"); + tty->print_cr ("IC@" INTPTR_FORMAT ": monomorphic to compiled (rcvr klass) %s: %s", + instruction_address(), + ((klassOop)info.cached_oop()())->print_value_string(), + (safe) ? "" : "via stub"); + } + } + // We can't check this anymore. With lazy deopt we could have already + // cleaned this IC entry before we even return. This is possible if + // we ran out of space in the inline cache buffer trying to do the + // set_next and we safepointed to free up space. This is a benign + // race because the IC entry was complete when we safepointed so + // cleaning it immediately is harmless. + // assert(is_call_to_compiled() || is_call_to_interpreted(), "sanity check"); +} + + +// is_optimized: Compiler has generated an optimized call (i.e., no inline +// cache) static_bound: The call can be static bound (i.e, no need to use +// inline cache) +void CompiledIC::compute_monomorphic_entry(methodHandle method, + KlassHandle receiver_klass, + bool is_optimized, + bool static_bound, + CompiledICInfo& info, + TRAPS) { + info._is_optimized = is_optimized; + + nmethod* method_code = method->code(); + address entry = NULL; + if (method_code != NULL) { + // Call to compiled code + if (static_bound || is_optimized) { + entry = method_code->verified_entry_point(); + } else { + entry = method_code->entry_point(); + } + } + if (entry != NULL) { + // Call to compiled code + info._entry = entry; + if (static_bound || is_optimized) { + info._cached_oop = Handle(THREAD, (oop)NULL); + } else { + info._cached_oop = receiver_klass; + } + info._to_interpreter = false; + } else { + // Note: the following problem exists with Compiler1: + // - at compile time we may or may not know if the destination is final + // - if we know that the destination is final, we will emit an optimized + // virtual call (no inline cache), and need a methodOop to make a call + // to the interpreter + // - if we do not know if the destination is final, we emit a standard + // virtual call, and use CompiledICHolder to call interpreted code + // (no static call stub has been generated) + // However in that case we will now notice it is static_bound + // and convert the call into what looks to be an optimized + // virtual call. This causes problems in verifying the IC because + // it look vanilla but is optimized. Code in is_call_to_interpreted + // is aware of this and weakens its asserts. + + info._to_interpreter = true; + // static_bound should imply is_optimized -- otherwise we have a + // performance bug (statically-bindable method is called via + // dynamically-dispatched call note: the reverse implication isn't + // necessarily true -- the call may have been optimized based on compiler + // analysis (static_bound is only based on "final" etc.) +#ifdef COMPILER2 +#ifdef TIERED +#if defined(ASSERT) + // can't check the assert because we don't have the CompiledIC with which to + // find the address if the call instruction. + // + // CodeBlob* cb = find_blob_unsafe(instruction_address()); + // assert(cb->is_compiled_by_c1() || !static_bound || is_optimized, "static_bound should imply is_optimized"); +#endif // ASSERT +#else + assert(!static_bound || is_optimized, "static_bound should imply is_optimized"); +#endif // TIERED +#endif // COMPILER2 + if (is_optimized) { + // Use stub entry + info._entry = method()->get_c2i_entry(); + info._cached_oop = method; + } else { + // Use mkh entry + oop holder = oopFactory::new_compiledICHolder(method, receiver_klass, CHECK); + info._cached_oop = Handle(THREAD, holder); + info._entry = method()->get_c2i_unverified_entry(); + } + } +} + + +inline static RelocIterator parse_ic(CodeBlob* code, address ic_call, oop* &_oop_addr, bool *is_optimized) { + address first_oop = NULL; + // Mergers please note: Sun SC5.x CC insists on an lvalue for a reference parameter. + CodeBlob *code1 = code; + return virtual_call_Relocation::parse_ic(code1, ic_call, first_oop, _oop_addr, is_optimized); +} + +CompiledIC::CompiledIC(NativeCall* ic_call) + : _ic_call(ic_call), + _oops(parse_ic(NULL, ic_call->instruction_address(), _oop_addr, &_is_optimized)) +{ +} + + +CompiledIC::CompiledIC(Relocation* ic_reloc) + : _ic_call(nativeCall_at(ic_reloc->addr())), + _oops(parse_ic(ic_reloc->code(), ic_reloc->addr(), _oop_addr, &_is_optimized)) +{ + assert(ic_reloc->type() == relocInfo::virtual_call_type || + ic_reloc->type() == relocInfo::opt_virtual_call_type, "wrong reloc. info"); +} + + +// ---------------------------------------------------------------------------- + +void CompiledStaticCall::set_to_clean() { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), "mt unsafe call"); + // Reset call site + MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag); +#ifdef ASSERT + CodeBlob* cb = CodeCache::find_blob_unsafe(this); + assert(cb != NULL && cb->is_nmethod(), "must be nmethod"); +#endif + set_destination_mt_safe(SharedRuntime::get_resolve_static_call_stub()); + + // Do not reset stub here: It is too expensive to call find_stub. + // Instead, rely on caller (nmethod::clear_inline_caches) to clear + // both the call and its stub. +} + + +bool CompiledStaticCall::is_clean() const { + return destination() == SharedRuntime::get_resolve_static_call_stub(); +} + +bool CompiledStaticCall::is_call_to_compiled() const { + return CodeCache::contains(destination()); +} + + +bool CompiledStaticCall::is_call_to_interpreted() const { + // It is a call to interpreted, if it calls to a stub. Hence, the destination + // must be in the stub part of the nmethod that contains the call + nmethod* nm = CodeCache::find_nmethod(instruction_address()); + return nm->stub_contains(destination()); +} + + +void CompiledStaticCall::set_to_interpreted(methodHandle callee, address entry) { + address stub=find_stub(); + assert(stub!=NULL, "stub not found"); + + if (TraceICs) { + ResourceMark rm; + tty->print_cr("CompiledStaticCall@" INTPTR_FORMAT ": set_to_interpreted %s", + instruction_address(), + callee->name_and_sig_as_C_string()); + } + + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); // creation also verifies the object + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); + + assert(method_holder->data() == 0 || method_holder->data() == (intptr_t)callee(), "a) MT-unsafe modification of inline cache"); + assert(jump->jump_destination() == (address)-1 || jump->jump_destination() == entry, "b) MT-unsafe modification of inline cache"); + + // Update stub + method_holder->set_data((intptr_t)callee()); + jump->set_jump_destination(entry); + + // Update jump to call + set_destination_mt_safe(stub); +} + + +void CompiledStaticCall::set(const StaticCallInfo& info) { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), "mt unsafe call"); + MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag); + // Updating a cache to the wrong entry can cause bugs that are very hard + // to track down - if cache entry gets invalid - we just clean it. In + // this way it is always the same code path that is responsible for + // updating and resolving an inline cache + assert(is_clean(), "do not update a call entry - use clean"); + + if (info._to_interpreter) { + // Call to interpreted code + set_to_interpreted(info.callee(), info.entry()); + } else { + if (TraceICs) { + ResourceMark rm; + tty->print_cr("CompiledStaticCall@" INTPTR_FORMAT ": set_to_compiled " INTPTR_FORMAT, + instruction_address(), + info.entry()); + } + // Call to compiled code + assert (CodeCache::contains(info.entry()), "wrong entry point"); + set_destination_mt_safe(info.entry()); + } +} + + +// Compute settings for a CompiledStaticCall. Since we might have to set +// the stub when calling to the interpreter, we need to return arguments. +void CompiledStaticCall::compute_entry(methodHandle m, StaticCallInfo& info) { + nmethod* m_code = m->code(); + info._callee = m; + if (m_code != NULL) { + info._to_interpreter = false; + info._entry = m_code->verified_entry_point(); + } else { + // Callee is interpreted code. In any case entering the interpreter + // puts a converter-frame on the stack to save arguments. + info._to_interpreter = true; + info._entry = m()->get_c2i_entry(); + } +} + + +void CompiledStaticCall::set_stub_to_clean(static_stub_Relocation* static_stub) { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), "mt unsafe call"); + // Reset stub + address stub = static_stub->addr(); + assert(stub!=NULL, "stub not found"); + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); // creation also verifies the object + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); + method_holder->set_data(0); + jump->set_jump_destination((address)-1); +} + + +address CompiledStaticCall::find_stub() { + // Find reloc. information containing this call-site + RelocIterator iter((nmethod*)NULL, instruction_address()); + while (iter.next()) { + if (iter.addr() == instruction_address()) { + switch(iter.type()) { + case relocInfo::static_call_type: + return iter.static_call_reloc()->static_stub(); + // We check here for opt_virtual_call_type, since we reuse the code + // from the CompiledIC implementation + case relocInfo::opt_virtual_call_type: + return iter.opt_virtual_call_reloc()->static_stub(); + case relocInfo::poll_type: + case relocInfo::poll_return_type: // A safepoint can't overlap a call. + default: + ShouldNotReachHere(); + } + } + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Non-product mode code +#ifndef PRODUCT + +void CompiledIC::verify() { + // make sure code pattern is actually a call imm32 instruction + _ic_call->verify(); + if (os::is_MP()) { + _ic_call->verify_alignment(); + } + assert(is_clean() || is_call_to_compiled() || is_call_to_interpreted() + || is_optimized() || is_megamorphic(), "sanity check"); +} + + +void CompiledIC::print() { + print_compiled_ic(); + tty->cr(); +} + + +void CompiledIC::print_compiled_ic() { + tty->print("Inline cache at " INTPTR_FORMAT ", calling %s " INTPTR_FORMAT, + instruction_address(), is_call_to_interpreted() ? "interpreted " : "", ic_destination()); +} + + +void CompiledStaticCall::print() { + tty->print("static call at " INTPTR_FORMAT " -> ", instruction_address()); + if (is_clean()) { + tty->print("clean"); + } else if (is_call_to_compiled()) { + tty->print("compiled"); + } else if (is_call_to_interpreted()) { + tty->print("interpreted"); + } + tty->cr(); +} + +void CompiledStaticCall::verify() { + // Verify call + NativeCall::verify(); + if (os::is_MP()) { + verify_alignment(); + } + + // Verify stub + address stub = find_stub(); + assert(stub != NULL, "no stub found for static call"); + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); // creation also verifies the object + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); + + // Verify state + assert(is_clean() || is_call_to_compiled() || is_call_to_interpreted(), "sanity check"); +} + +#endif diff --git a/hotspot/src/share/vm/code/compiledIC.hpp b/hotspot/src/share/vm/code/compiledIC.hpp new file mode 100644 index 00000000000..1fa2f968503 --- /dev/null +++ b/hotspot/src/share/vm/code/compiledIC.hpp @@ -0,0 +1,241 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//----------------------------------------------------------------------------- +// The CompiledIC represents a compiled inline cache. +// +// In order to make patching of the inline cache MT-safe, we only allow the following +// transitions (when not at a safepoint): +// +// +// [1] --<-- Clean -->--- [1] +// / (null) \ +// / \ /-<-\ +// / [2] \ / \ +// Interpreted ---------> Monomorphic | [3] +// (compiledICHolderOop) (klassOop) | +// \ / \ / +// [4] \ / [4] \->-/ +// \->- Megamorphic -<-/ +// (methodOop) +// +// The text in paranteses () refere to the value of the inline cache receiver (mov instruction) +// +// The numbers in square brackets refere to the kind of transition: +// [1]: Initial fixup. Receiver it found from debug information +// [2]: Compilation of a method +// [3]: Recompilation of a method (note: only entry is changed. The klassOop must stay the same) +// [4]: Inline cache miss. We go directly to megamorphic call. +// +// The class automatically inserts transition stubs (using the InlineCacheBuffer) when an MT-unsafe +// transition is made to a stub. +// +class CompiledIC; + +class CompiledICInfo { + friend class CompiledIC; + private: + address _entry; // entry point for call + Handle _cached_oop; // Value of cached_oop (either in stub or inline cache) + bool _is_optimized; // it is an optimized virtual call (i.e., can be statically bound) + bool _to_interpreter; // Call it to interpreter + public: + address entry() const { return _entry; } + Handle cached_oop() const { return _cached_oop; } + bool is_optimized() const { return _is_optimized; } +}; + +class CompiledIC: public ResourceObj { + friend class InlineCacheBuffer; + friend class ICStub; + + + private: + NativeCall* _ic_call; // the call instruction + oop* _oop_addr; // patchable oop cell for this IC + RelocIterator _oops; // iteration over any and all set-oop instructions + bool _is_optimized; // an optimized virtual call (i.e., no compiled IC) + + CompiledIC(NativeCall* ic_call); + CompiledIC(Relocation* ic_reloc); // Must be of virtual_call_type/opt_virtual_call_type + + // low-level inline-cache manipulation. Cannot be accessed directly, since it might not be MT-safe + // to change an inline-cache. These changes the underlying inline-cache directly. They *newer* make + // changes to a transition stub. + void set_ic_destination(address entry_point); + void set_cached_oop(oop cache); + + // Reads the location of the transition stub. This will fail with an assertion, if no transition stub is + // associated with the inline cache. + address stub_address() const; + bool is_in_transition_state() const; // Use InlineCacheBuffer + + public: + // conversion (machine PC to CompiledIC*) + friend CompiledIC* CompiledIC_before(address return_addr); + friend CompiledIC* CompiledIC_at(address call_site); + friend CompiledIC* CompiledIC_at(Relocation* call_site); + + // Return the cached_oop/destination associated with this inline cache. If the cache currently points + // to a transition stub, it will read the values from the transition stub. + oop cached_oop() const; + address ic_destination() const; + + bool is_optimized() const { return _is_optimized; } + + // State + bool is_clean() const; + bool is_megamorphic() const; + bool is_call_to_compiled() const; + bool is_call_to_interpreted() const; + + address end_of_call() { return _ic_call->return_address(); } + + // MT-safe patching of inline caches. Note: Only safe to call is_xxx when holding the CompiledIC_ock + // so you are guaranteed that no patching takes place. The same goes for verify. + // + // Note: We do not provide any direct access to the stub code, to prevent parts of the code + // to manipulate the inline cache in MT-unsafe ways. + // + // They all takes a TRAP argument, since they can cause a GC if the inline-cache buffer is full. + // + void set_to_clean(); // Can only be called during a safepoint operation + void set_to_monomorphic(const CompiledICInfo& info); + void set_to_megamorphic(CallInfo* call_info, Bytecodes::Code bytecode, TRAPS); + + static void compute_monomorphic_entry(methodHandle method, KlassHandle receiver_klass, + bool is_optimized, bool static_bound, CompiledICInfo& info, TRAPS); + + // Location + address instruction_address() const { return _ic_call->instruction_address(); } + + // Misc + void print() PRODUCT_RETURN; + void print_compiled_ic() PRODUCT_RETURN; + void verify() PRODUCT_RETURN; +}; + +inline CompiledIC* CompiledIC_before(address return_addr) { + CompiledIC* c_ic = new CompiledIC(nativeCall_before(return_addr)); + c_ic->verify(); + return c_ic; +} + +inline CompiledIC* CompiledIC_at(address call_site) { + CompiledIC* c_ic = new CompiledIC(nativeCall_at(call_site)); + c_ic->verify(); + return c_ic; +} + +inline CompiledIC* CompiledIC_at(Relocation* call_site) { + CompiledIC* c_ic = new CompiledIC(call_site); + c_ic->verify(); + return c_ic; +} + + +//----------------------------------------------------------------------------- +// The CompiledStaticCall represents a call to a static method in the compiled +// +// Transition diagram of a static call site is somewhat simpler than for an inlined cache: +// +// +// -----<----- Clean ----->----- +// / \ +// / \ +// compilled code <------------> interpreted code +// +// Clean: Calls directly to runtime method for fixup +// Compiled code: Calls directly to compiled code +// Interpreted code: Calls to stub that set methodOop reference +// +// +class CompiledStaticCall; + +class StaticCallInfo { + private: + address _entry; // Entrypoint + methodHandle _callee; // Callee (used when calling interpreter) + bool _to_interpreter; // call to interpreted method (otherwise compiled) + + friend class CompiledStaticCall; + public: + address entry() const { return _entry; } + methodHandle callee() const { return _callee; } +}; + + +class CompiledStaticCall: public NativeCall { + friend class CompiledIC; + + // Also used by CompiledIC + void set_to_interpreted(methodHandle callee, address entry); + bool is_optimized_virtual(); + + public: + friend CompiledStaticCall* compiledStaticCall_before(address return_addr); + friend CompiledStaticCall* compiledStaticCall_at(address native_call); + friend CompiledStaticCall* compiledStaticCall_at(Relocation* call_site); + + // State + bool is_clean() const; + bool is_call_to_compiled() const; + bool is_call_to_interpreted() const; + + // Clean static call (will force resolving on next use) + void set_to_clean(); + + // Set state. The entry must be the same, as computed by compute_entry. + // Computation and setting is split up, since the actions are separate during + // a OptoRuntime::resolve_xxx. + void set(const StaticCallInfo& info); + + // Compute entry point given a method + static void compute_entry(methodHandle m, StaticCallInfo& info); + + // Stub support + address find_stub(); + static void set_stub_to_clean(static_stub_Relocation* static_stub); + + // Misc. + void print() PRODUCT_RETURN; + void verify() PRODUCT_RETURN; +}; + + +inline CompiledStaticCall* compiledStaticCall_before(address return_addr) { + CompiledStaticCall* st = (CompiledStaticCall*)nativeCall_before(return_addr); + st->verify(); + return st; +} + +inline CompiledStaticCall* compiledStaticCall_at(address native_call) { + CompiledStaticCall* st = (CompiledStaticCall*)native_call; + st->verify(); + return st; +} + +inline CompiledStaticCall* compiledStaticCall_at(Relocation* call_site) { + return compiledStaticCall_at(call_site->addr()); +} diff --git a/hotspot/src/share/vm/code/compressedStream.cpp b/hotspot/src/share/vm/code/compressedStream.cpp new file mode 100644 index 00000000000..c114eb4ed9b --- /dev/null +++ b/hotspot/src/share/vm/code/compressedStream.cpp @@ -0,0 +1,281 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_compressedStream.cpp.incl" + +// 32-bit one-to-one sign encoding taken from Pack200 +// converts leading sign bits into leading zeroes with trailing sign bit +inline juint CompressedStream::encode_sign(jint value) { + return (value << 1) ^ (value >> 31); +} +inline jint CompressedStream::decode_sign(juint value) { + return (value >> 1) ^ -(jint)(value & 1); +} + +// 32-bit self-inverse encoding of float bits +// converts trailing zeroes (common in floats) to leading zeroes +inline juint CompressedStream::reverse_int(juint i) { + // Hacker's Delight, Figure 7-1 + i = (i & 0x55555555) << 1 | (i >> 1) & 0x55555555; + i = (i & 0x33333333) << 2 | (i >> 2) & 0x33333333; + i = (i & 0x0f0f0f0f) << 4 | (i >> 4) & 0x0f0f0f0f; + i = (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24); + return i; +} + + +jint CompressedReadStream::read_signed_int() { + return decode_sign(read_int()); +} + +// Compressing floats is simple, because the only common pattern +// is trailing zeroes. (Compare leading sign bits on ints.) +// Since floats are left-justified, as opposed to right-justified +// ints, we can bit-reverse them in order to take advantage of int +// compression. + +jfloat CompressedReadStream::read_float() { + int rf = read_int(); + int f = reverse_int(rf); + return jfloat_cast(f); +} + +jdouble CompressedReadStream::read_double() { + jint rh = read_int(); + jint rl = read_int(); + jint h = reverse_int(rh); + jint l = reverse_int(rl); + return jdouble_cast(jlong_from(h, l)); +} + +jlong CompressedReadStream::read_long() { + jint low = read_signed_int(); + jint high = read_signed_int(); + return jlong_from(high, low); +} + +CompressedWriteStream::CompressedWriteStream(int initial_size) : CompressedStream(NULL, 0) { + _buffer = NEW_RESOURCE_ARRAY(u_char, initial_size); + _size = initial_size; + _position = 0; +} + +void CompressedWriteStream::grow() { + u_char* _new_buffer = NEW_RESOURCE_ARRAY(u_char, _size * 2); + memcpy(_new_buffer, _buffer, _position); + _buffer = _new_buffer; + _size = _size * 2; +} + +void CompressedWriteStream::write_signed_int(jint value) { + // this encoding, called SIGNED5, is taken from Pack200 + write_int(encode_sign(value)); +} + +void CompressedWriteStream::write_float(jfloat value) { + juint f = jint_cast(value); + juint rf = reverse_int(f); + assert(f == reverse_int(rf), "can re-read same bits"); + write_int(rf); +} + +void CompressedWriteStream::write_double(jdouble value) { + juint h = high(jlong_cast(value)); + juint l = low( jlong_cast(value)); + juint rh = reverse_int(h); + juint rl = reverse_int(l); + assert(h == reverse_int(rh), "can re-read same bits"); + assert(l == reverse_int(rl), "can re-read same bits"); + write_int(rh); + write_int(rl); +} + +void CompressedWriteStream::write_long(jlong value) { + write_signed_int(low(value)); + write_signed_int(high(value)); +} + + +/// The remaining details + +#ifndef PRODUCT +// set this to trigger unit test +void test_compressed_stream(int trace); +bool test_compressed_stream_enabled = false; +#endif + +// This encoding, called UNSIGNED5, is taken from J2SE Pack200. +// It assumes that most values have lots of leading zeroes. +// Very small values, in the range [0..191], code in one byte. +// Any 32-bit value (including negatives) can be coded, in +// up to five bytes. The grammar is: +// low_byte = [0..191] +// high_byte = [192..255] +// any_byte = low_byte | high_byte +// coding = low_byte +// | high_byte low_byte +// | high_byte high_byte low_byte +// | high_byte high_byte high_byte low_byte +// | high_byte high_byte high_byte high_byte any_byte +// Each high_byte contributes six bits of payload. +// The encoding is one-to-one (except for integer overflow) +// and easy to parse and unparse. + +jint CompressedReadStream::read_int_mb(jint b0) { + int pos = position() - 1; + u_char* buf = buffer() + pos; + assert(buf[0] == b0 && b0 >= L, "correctly called"); + jint sum = b0; + // must collect more bytes: b[1]...b[4] + int lg_H_i = lg_H; + for (int i = 0; ; ) { + jint b_i = buf[++i]; // b_i = read(); ++i; + sum += b_i << lg_H_i; // sum += b[i]*(64**i) + if (b_i < L || i == MAX_i) { + set_position(pos+i+1); + return sum; + } + lg_H_i += lg_H; + } +} + +void CompressedWriteStream::write_int_mb(jint value) { + debug_only(int pos1 = position()); + juint sum = value; + for (int i = 0; ; ) { + if (sum < L || i == MAX_i) { + // remainder is either a "low code" or the 5th byte + assert(sum == (u_char)sum, "valid byte"); + write((u_char)sum); + break; + } + sum -= L; + int b_i = L + (sum % H); // this is a "high code" + sum >>= lg_H; // extracted 6 bits + write(b_i); ++i; + } + +#ifndef PRODUCT + if (test_compressed_stream_enabled) { // hack to enable this stress test + test_compressed_stream_enabled = false; + test_compressed_stream(0); + } +#endif +} + + +#ifndef PRODUCT +/// a unit test (can be run by hand from a debugger) + +// Avoid a VS2005 compiler stack overflow w/ fastdebug build. +// The following pragma optimize turns off optimization ONLY +// for this block (a matching directive turns it back on later). +// These directives can be removed once the MS VS.NET 2005 +// compiler stack overflow is fixed. +#if _MSC_VER >=1400 && !defined(_WIN64) +#pragma optimize("", off) +#endif + +// generator for an "interesting" set of critical values +enum { stretch_limit = (1<<16) * (64-16+1) }; +static jlong stretch(jint x, int bits) { + // put x[high 4] into place + jlong h = (jlong)((x >> (16-4))) << (bits - 4); + // put x[low 12] into place, sign extended + jlong l = ((jlong)x << (64-12)) >> (64-12); + // move l upwards, maybe + l <<= (x >> 16); + return h ^ l; +} + +void test_compressed_stream(int trace) { + CompressedWriteStream bytes(stretch_limit * 100); + jint n; + int step = 0, fails = 0; +#define CHECKXY(x, y, fmt) { \ + ++step; \ + int xlen = (pos = decode.position()) - lastpos; lastpos = pos; \ + if (trace > 0 && (step % trace) == 0) { \ + tty->print_cr("step %d, n=%08x: value=" fmt " (len=%d)", \ + step, n, x, xlen); } \ + if (x != y) { \ + tty->print_cr("step %d, n=%d: " fmt " != " fmt, step, n, x, y); \ + fails++; \ + } } + for (n = 0; n < (1<<8); n++) { + jbyte x = (jbyte)n; + bytes.write_byte(x); ++step; + } + for (n = 0; n < stretch_limit; n++) { + jint x = (jint)stretch(n, 32); + bytes.write_int(x); ++step; + bytes.write_signed_int(x); ++step; + bytes.write_float(jfloat_cast(x)); ++step; + } + for (n = 0; n < stretch_limit; n++) { + jlong x = stretch(n, 64); + bytes.write_long(x); ++step; + bytes.write_double(jdouble_cast(x)); ++step; + } + int length = bytes.position(); + if (trace != 0) + tty->print_cr("set up test of %d stream values, size %d", step, length); + step = 0; + // now decode it all + CompressedReadStream decode(bytes.buffer()); + int pos, lastpos = decode.position(); + for (n = 0; n < (1<<8); n++) { + jbyte x = (jbyte)n; + jbyte y = decode.read_byte(); + CHECKXY(x, y, "%db"); + } + for (n = 0; n < stretch_limit; n++) { + jint x = (jint)stretch(n, 32); + jint y1 = decode.read_int(); + CHECKXY(x, y1, "%du"); + jint y2 = decode.read_signed_int(); + CHECKXY(x, y2, "%di"); + jint y3 = jint_cast(decode.read_float()); + CHECKXY(x, y3, "%df"); + } + for (n = 0; n < stretch_limit; n++) { + jlong x = stretch(n, 64); + jlong y1 = decode.read_long(); + CHECKXY(x, y1, INT64_FORMAT "l"); + jlong y2 = jlong_cast(decode.read_double()); + CHECKXY(x, y2, INT64_FORMAT "d"); + } + int length2 = decode.position(); + if (trace != 0) + tty->print_cr("finished test of %d stream values, size %d", step, length2); + guarantee(length == length2, "bad length"); + guarantee(fails == 0, "test failures"); +} + +#if _MSC_VER >=1400 && !defined(_WIN64) +#pragma optimize("", on) +#endif + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/code/compressedStream.hpp b/hotspot/src/share/vm/code/compressedStream.hpp new file mode 100644 index 00000000000..44e9631f68a --- /dev/null +++ b/hotspot/src/share/vm/code/compressedStream.hpp @@ -0,0 +1,120 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Simple interface for filing out and filing in basic types +// Used for writing out and reading in debugging information. + +class CompressedStream : public ResourceObj { + friend class VMStructs; + protected: + u_char* _buffer; + int _position; + + enum { + // Constants for UNSIGNED5 coding of Pack200 + lg_H = 6, H = 1<= _size; + } + void store(u_char b) { + _buffer[_position++] = b; + } + void write(u_char b) { + if (full()) grow(); + store(b); + } + void grow(); + + void write_int_mb(jint value); // UNSIGNED5 coding, 1-5 byte cases + + protected: + int _size; + + public: + CompressedWriteStream(int initial_size); + CompressedWriteStream(u_char* buffer, int initial_size, int position = 0) + : CompressedStream(buffer, position) { _size = initial_size; } + + void write_bool(jboolean value) { write(value); } + void write_byte(jbyte value) { write(value); } + void write_char(jchar value) { write_int(value); } + void write_short(jshort value) { write_signed_int(value); } + void write_int(jint value) { if ((juint)value < L && !full()) + store((u_char)value); + else write_int_mb(value); } + void write_signed_int(jint value); // write_int(encode_sign(value)) + void write_float(jfloat value); // write_int(reverse_int(jint_cast(v))) + void write_double(jdouble value); // write_int(reverse_int()) + void write_long(jlong value); // write_signed_int() +}; diff --git a/hotspot/src/share/vm/code/debugInfo.cpp b/hotspot/src/share/vm/code/debugInfo.cpp new file mode 100644 index 00000000000..2a2e1a56369 --- /dev/null +++ b/hotspot/src/share/vm/code/debugInfo.cpp @@ -0,0 +1,252 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_debugInfo.cpp.incl" + +// Comstructors + +DebugInfoWriteStream::DebugInfoWriteStream(DebugInformationRecorder* recorder, int initial_size) +: CompressedWriteStream(initial_size) { + _recorder = recorder; +} + +// Serializing oops + +void DebugInfoWriteStream::write_handle(jobject h) { + write_int(recorder()->oop_recorder()->find_index(h)); +} + +ScopeValue* DebugInfoReadStream::read_object_value() { + int id = read_int(); +#ifdef ASSERT + assert(_obj_pool != NULL, "object pool does not exist"); + for (int i = _obj_pool->length() - 1; i >= 0; i--) { + assert(((ObjectValue*) _obj_pool->at(i))->id() != id, "should not be read twice"); + } +#endif + ObjectValue* result = new ObjectValue(id); + _obj_pool->append(result); + result->read_object(this); + return result; +} + +ScopeValue* DebugInfoReadStream::get_cached_object() { + int id = read_int(); + assert(_obj_pool != NULL, "object pool does not exist"); + for (int i = _obj_pool->length() - 1; i >= 0; i--) { + ObjectValue* sv = (ObjectValue*) _obj_pool->at(i); + if (sv->id() == id) { + return sv; + } + } + ShouldNotReachHere(); + return NULL; +} + +// Serializing scope values + +enum { LOCATION_CODE = 0, CONSTANT_INT_CODE = 1, CONSTANT_OOP_CODE = 2, + CONSTANT_LONG_CODE = 3, CONSTANT_DOUBLE_CODE = 4, + OBJECT_CODE = 5, OBJECT_ID_CODE = 6 }; + +ScopeValue* ScopeValue::read_from(DebugInfoReadStream* stream) { + ScopeValue* result = NULL; + switch(stream->read_int()) { + case LOCATION_CODE: result = new LocationValue(stream); break; + case CONSTANT_INT_CODE: result = new ConstantIntValue(stream); break; + case CONSTANT_OOP_CODE: result = new ConstantOopReadValue(stream); break; + case CONSTANT_LONG_CODE: result = new ConstantLongValue(stream); break; + case CONSTANT_DOUBLE_CODE: result = new ConstantDoubleValue(stream); break; + case OBJECT_CODE: result = stream->read_object_value(); break; + case OBJECT_ID_CODE: result = stream->get_cached_object(); break; + default: ShouldNotReachHere(); + } + return result; +} + +// LocationValue + +LocationValue::LocationValue(DebugInfoReadStream* stream) { + _location = Location(stream); +} + +void LocationValue::write_on(DebugInfoWriteStream* stream) { + stream->write_int(LOCATION_CODE); + location().write_on(stream); +} + +void LocationValue::print_on(outputStream* st) const { + location().print_on(st); +} + +// ObjectValue + +void ObjectValue::read_object(DebugInfoReadStream* stream) { + _klass = read_from(stream); + assert(_klass->is_constant_oop(), "should be constant klass oop"); + int length = stream->read_int(); + for (int i = 0; i < length; i++) { + ScopeValue* val = read_from(stream); + _field_values.append(val); + } +} + +void ObjectValue::write_on(DebugInfoWriteStream* stream) { + if (_visited) { + stream->write_int(OBJECT_ID_CODE); + stream->write_int(_id); + } else { + _visited = true; + stream->write_int(OBJECT_CODE); + stream->write_int(_id); + _klass->write_on(stream); + int length = _field_values.length(); + stream->write_int(length); + for (int i = 0; i < length; i++) { + _field_values.at(i)->write_on(stream); + } + } +} + +void ObjectValue::print_on(outputStream* st) const { + st->print("obj[%d]", _id); +} + +void ObjectValue::print_fields_on(outputStream* st) const { +#ifndef PRODUCT + if (_field_values.length() > 0) { + _field_values.at(0)->print_on(st); + } + for (int i = 1; i < _field_values.length(); i++) { + st->print(", "); + _field_values.at(i)->print_on(st); + } +#endif +} + +// ConstantIntValue + +ConstantIntValue::ConstantIntValue(DebugInfoReadStream* stream) { + _value = stream->read_signed_int(); +} + +void ConstantIntValue::write_on(DebugInfoWriteStream* stream) { + stream->write_int(CONSTANT_INT_CODE); + stream->write_signed_int(value()); +} + +void ConstantIntValue::print_on(outputStream* st) const { + st->print("%d", value()); +} + +// ConstantLongValue + +ConstantLongValue::ConstantLongValue(DebugInfoReadStream* stream) { + _value = stream->read_long(); +} + +void ConstantLongValue::write_on(DebugInfoWriteStream* stream) { + stream->write_int(CONSTANT_LONG_CODE); + stream->write_long(value()); +} + +void ConstantLongValue::print_on(outputStream* st) const { + st->print(INT64_FORMAT, value()); +} + +// ConstantDoubleValue + +ConstantDoubleValue::ConstantDoubleValue(DebugInfoReadStream* stream) { + _value = stream->read_double(); +} + +void ConstantDoubleValue::write_on(DebugInfoWriteStream* stream) { + stream->write_int(CONSTANT_DOUBLE_CODE); + stream->write_double(value()); +} + +void ConstantDoubleValue::print_on(outputStream* st) const { + st->print("%f", value()); +} + +// ConstantOopWriteValue + +void ConstantOopWriteValue::write_on(DebugInfoWriteStream* stream) { + stream->write_int(CONSTANT_OOP_CODE); + stream->write_handle(value()); +} + +void ConstantOopWriteValue::print_on(outputStream* st) const { + JNIHandles::resolve(value())->print_value_on(st); +} + + +// ConstantOopReadValue + +ConstantOopReadValue::ConstantOopReadValue(DebugInfoReadStream* stream) { + _value = Handle(stream->read_oop()); +} + +void ConstantOopReadValue::write_on(DebugInfoWriteStream* stream) { + ShouldNotReachHere(); +} + +void ConstantOopReadValue::print_on(outputStream* st) const { + value()()->print_value_on(st); +} + + +// MonitorValue + +MonitorValue::MonitorValue(ScopeValue* owner, Location basic_lock, bool eliminated) { + _owner = owner; + _basic_lock = basic_lock; + _eliminated = eliminated; +} + +MonitorValue::MonitorValue(DebugInfoReadStream* stream) { + _basic_lock = Location(stream); + _owner = ScopeValue::read_from(stream); + _eliminated = (stream->read_bool() != 0); +} + +void MonitorValue::write_on(DebugInfoWriteStream* stream) { + _basic_lock.write_on(stream); + _owner->write_on(stream); + stream->write_bool(_eliminated); +} + +#ifndef PRODUCT +void MonitorValue::print_on(outputStream* st) const { + st->print("monitor{"); + owner()->print_on(st); + st->print(","); + basic_lock().print_on(st); + st->print("}"); + if (_eliminated) { + st->print(" (eliminated)"); + } +} +#endif diff --git a/hotspot/src/share/vm/code/debugInfo.hpp b/hotspot/src/share/vm/code/debugInfo.hpp new file mode 100644 index 00000000000..13b3775b762 --- /dev/null +++ b/hotspot/src/share/vm/code/debugInfo.hpp @@ -0,0 +1,272 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Classes used for serializing debugging information. +// These abstractions are introducted to provide symmetric +// read and write operations. + +// ScopeValue describes the value of a variable/expression in a scope +// - LocationValue describes a value in a given location (in frame or register) +// - ConstantValue describes a constant + +class ScopeValue: public ResourceObj { + public: + // Testers + virtual bool is_location() const { return false; } + virtual bool is_object() const { return false; } + virtual bool is_constant_int() const { return false; } + virtual bool is_constant_double() const { return false; } + virtual bool is_constant_long() const { return false; } + virtual bool is_constant_oop() const { return false; } + virtual bool equals(ScopeValue* other) const { return false; } + + // Serialization of debugging information + virtual void write_on(DebugInfoWriteStream* stream) = 0; + static ScopeValue* read_from(DebugInfoReadStream* stream); +}; + + +// A Location value describes a value in a given location; i.e. the corresponding +// logical entity (e.g., a method temporary) lives in this location. + +class LocationValue: public ScopeValue { + private: + Location _location; + public: + LocationValue(Location location) { _location = location; } + bool is_location() const { return true; } + Location location() const { return _location; } + + // Serialization of debugging information + LocationValue(DebugInfoReadStream* stream); + void write_on(DebugInfoWriteStream* stream); + + // Printing + void print_on(outputStream* st) const; +}; + + +// An ObjectValue describes an object eliminated by escape analysis. + +class ObjectValue: public ScopeValue { + private: + int _id; + ScopeValue* _klass; + GrowableArray _field_values; + Handle _value; + bool _visited; + + public: + ObjectValue(int id, ScopeValue* klass) + : _id(id) + , _klass(klass) + , _field_values() + , _value() + , _visited(false) { + assert(klass->is_constant_oop(), "should be constant klass oop"); + } + + ObjectValue(int id) + : _id(id) + , _klass(NULL) + , _field_values() + , _value() + , _visited(false) {} + + // Accessors + bool is_object() const { return true; } + int id() const { return _id; } + ScopeValue* klass() const { return _klass; } + GrowableArray* field_values() { return &_field_values; } + ScopeValue* field_at(int i) const { return _field_values.at(i); } + int field_size() { return _field_values.length(); } + Handle value() const { return _value; } + bool is_visited() const { return _visited; } + + void set_value(oop value) { _value = Handle(value); } + void set_visited(bool visited) { _visited = false; } + + // Serialization of debugging information + void read_object(DebugInfoReadStream* stream); + void write_on(DebugInfoWriteStream* stream); + + // Printing + void print_on(outputStream* st) const; + void print_fields_on(outputStream* st) const; +}; + + +// A ConstantIntValue describes a constant int; i.e., the corresponding logical entity +// is either a source constant or its computation has been constant-folded. + +class ConstantIntValue: public ScopeValue { + private: + jint _value; + public: + ConstantIntValue(jint value) { _value = value; } + jint value() const { return _value; } + bool is_constant_int() const { return true; } + bool equals(ScopeValue* other) const { return false; } + + // Serialization of debugging information + ConstantIntValue(DebugInfoReadStream* stream); + void write_on(DebugInfoWriteStream* stream); + + // Printing + void print_on(outputStream* st) const; +}; + +class ConstantLongValue: public ScopeValue { + private: + jlong _value; + public: + ConstantLongValue(jlong value) { _value = value; } + jlong value() const { return _value; } + bool is_constant_long() const { return true; } + bool equals(ScopeValue* other) const { return false; } + + // Serialization of debugging information + ConstantLongValue(DebugInfoReadStream* stream); + void write_on(DebugInfoWriteStream* stream); + + // Printing + void print_on(outputStream* st) const; +}; + +class ConstantDoubleValue: public ScopeValue { + private: + jdouble _value; + public: + ConstantDoubleValue(jdouble value) { _value = value; } + jdouble value() const { return _value; } + bool is_constant_double() const { return true; } + bool equals(ScopeValue* other) const { return false; } + + // Serialization of debugging information + ConstantDoubleValue(DebugInfoReadStream* stream); + void write_on(DebugInfoWriteStream* stream); + + // Printing + void print_on(outputStream* st) const; +}; + +// A ConstantOopWriteValue is created by the compiler to +// be written as debugging information. + +class ConstantOopWriteValue: public ScopeValue { + private: + jobject _value; + public: + ConstantOopWriteValue(jobject value) { _value = value; } + jobject value() const { return _value; } + bool is_constant_oop() const { return true; } + bool equals(ScopeValue* other) const { return false; } + + // Serialization of debugging information + void write_on(DebugInfoWriteStream* stream); + + // Printing + void print_on(outputStream* st) const; +}; + +// A ConstantOopReadValue is created by the VM when reading +// debug information + +class ConstantOopReadValue: public ScopeValue { + private: + Handle _value; + public: + Handle value() const { return _value; } + bool is_constant_oop() const { return true; } + bool equals(ScopeValue* other) const { return false; } + + // Serialization of debugging information + ConstantOopReadValue(DebugInfoReadStream* stream); + void write_on(DebugInfoWriteStream* stream); + + // Printing + void print_on(outputStream* st) const; +}; + +// MonitorValue describes the pair used for monitor_enter and monitor_exit. + +class MonitorValue: public ResourceObj { + private: + ScopeValue* _owner; + Location _basic_lock; + bool _eliminated; + public: + // Constructor + MonitorValue(ScopeValue* owner, Location basic_lock, bool eliminated = false); + + // Accessors + ScopeValue* owner() const { return _owner; } + Location basic_lock() const { return _basic_lock; } + bool eliminated() const { return _eliminated; } + + // Serialization of debugging information + MonitorValue(DebugInfoReadStream* stream); + void write_on(DebugInfoWriteStream* stream); + + // Printing + void print_on(outputStream* st) const; +}; + +// DebugInfoReadStream specializes CompressedReadStream for reading +// debugging information. Used by ScopeDesc. + +class DebugInfoReadStream : public CompressedReadStream { + private: + const nmethod* _code; + const nmethod* code() const { return _code; } + GrowableArray* _obj_pool; + public: + DebugInfoReadStream(const nmethod* code, int offset, GrowableArray* obj_pool = NULL) : + CompressedReadStream(code->scopes_data_begin(), offset) { + _code = code; + _obj_pool = obj_pool; + + } ; + + oop read_oop() { + return code()->oop_at(read_int()); + } + ScopeValue* read_object_value(); + ScopeValue* get_cached_object(); + // BCI encoding is mostly unsigned, but -1 is a distinguished value + int read_bci() { return read_int() + InvocationEntryBci; } +}; + +// DebugInfoWriteStream specializes CompressedWriteStream for +// writing debugging information. Used by ScopeDescRecorder. + +class DebugInfoWriteStream : public CompressedWriteStream { + private: + DebugInformationRecorder* _recorder; + DebugInformationRecorder* recorder() const { return _recorder; } + public: + DebugInfoWriteStream(DebugInformationRecorder* recorder, int initial_size); + void write_handle(jobject h); + void write_bci(int bci) { write_int(bci - InvocationEntryBci); } +}; diff --git a/hotspot/src/share/vm/code/debugInfoRec.cpp b/hotspot/src/share/vm/code/debugInfoRec.cpp new file mode 100644 index 00000000000..04b58f4c70a --- /dev/null +++ b/hotspot/src/share/vm/code/debugInfoRec.cpp @@ -0,0 +1,411 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_debugInfoRec.cpp.incl" + +// Private definition. +// There is one DIR_Chunk for each scope and values array. +// A chunk can potentially be used more than once. +// We keep track of these chunks in order to detect +// repetition and enable sharing. +class DIR_Chunk { + friend class DebugInformationRecorder; + int _offset; // location in the stream of this scope + int _length; // number of bytes in the stream + int _hash; // hash of stream bytes (for quicker reuse) + + void* operator new(size_t ignore, DebugInformationRecorder* dir) { + assert(ignore == sizeof(DIR_Chunk), ""); + if (dir->_next_chunk >= dir->_next_chunk_limit) { + const int CHUNK = 100; + dir->_next_chunk = NEW_RESOURCE_ARRAY(DIR_Chunk, CHUNK); + dir->_next_chunk_limit = dir->_next_chunk + CHUNK; + } + return dir->_next_chunk++; + } + + DIR_Chunk(int offset, int length, DebugInformationRecorder* dir) { + _offset = offset; + _length = length; + unsigned int hash = 0; + address p = dir->stream()->buffer() + _offset; + for (int i = 0; i < length; i++) { + if (i == 6) break; + hash *= 127; + hash += p[i]; + } + _hash = hash; + } + + DIR_Chunk* find_match(GrowableArray* arr, + int start_index, + DebugInformationRecorder* dir) { + int end_index = arr->length(); + int hash = this->_hash, length = this->_length; + address buf = dir->stream()->buffer(); + for (int i = end_index; --i >= start_index; ) { + DIR_Chunk* that = arr->at(i); + if (hash == that->_hash && + length == that->_length && + 0 == memcmp(buf + this->_offset, buf + that->_offset, length)) { + return that; + } + } + return NULL; + } +}; + +static inline bool compute_recording_non_safepoints() { + if (JvmtiExport::should_post_compiled_method_load() + && FLAG_IS_DEFAULT(DebugNonSafepoints)) { + // The default value of this flag is taken to be true, + // if JVMTI is looking at nmethod codes. + // We anticipate that JVMTI may wish to participate in profiling. + return true; + } + + // If the flag is set manually, use it, whether true or false. + // Otherwise, if JVMTI is not in the picture, use the default setting. + // (This is true in debug, just for the exercise, false in product mode.) + return DebugNonSafepoints; +} + +DebugInformationRecorder::DebugInformationRecorder(OopRecorder* oop_recorder) + : _recording_non_safepoints(compute_recording_non_safepoints()) +{ + _pcs_size = 100; + _pcs = NEW_RESOURCE_ARRAY(PcDesc, _pcs_size); + _pcs_length = 0; + + _prev_safepoint_pc = PcDesc::lower_offset_limit; + + _stream = new DebugInfoWriteStream(this, 10 * K); + // make sure that there is no stream_decode_offset that is zero + _stream->write_byte((jbyte)0xFF); + + // make sure that we can distinguish the value "serialized_null" from offsets + assert(_stream->position() > serialized_null, "sanity"); + + _oop_recorder = oop_recorder; + + _all_chunks = new GrowableArray(300); + _shared_chunks = new GrowableArray(30); + _next_chunk = _next_chunk_limit = NULL; + + add_new_pc_offset(PcDesc::lower_offset_limit); // sentinel record + + debug_only(_recording_state = rs_null); +} + + +void DebugInformationRecorder::add_oopmap(int pc_offset, OopMap* map) { + // !!!!! Preserve old style handling of oopmaps for now + _oopmaps->add_gc_map(pc_offset, map); +} + +void DebugInformationRecorder::add_safepoint(int pc_offset, OopMap* map) { + assert(!_oop_recorder->is_complete(), "not frozen yet"); + // Store the new safepoint + + // Add the oop map + add_oopmap(pc_offset, map); + + add_new_pc_offset(pc_offset); + + assert(_recording_state == rs_null, "nesting of recording calls"); + debug_only(_recording_state = rs_safepoint); +} + +void DebugInformationRecorder::add_non_safepoint(int pc_offset) { + assert(!_oop_recorder->is_complete(), "not frozen yet"); + assert(_recording_non_safepoints, "must be recording non-safepoints"); + + add_new_pc_offset(pc_offset); + + assert(_recording_state == rs_null, "nesting of recording calls"); + debug_only(_recording_state = rs_non_safepoint); +} + +void DebugInformationRecorder::add_new_pc_offset(int pc_offset) { + assert(_pcs_length == 0 || last_pc()->pc_offset() < pc_offset, + "must specify a new, larger pc offset"); + + // add the pcdesc + if (_pcs_length == _pcs_size) { + // Expand + int new_pcs_size = _pcs_size * 2; + PcDesc* new_pcs = NEW_RESOURCE_ARRAY(PcDesc, new_pcs_size); + for (int index = 0; index < _pcs_length; index++) { + new_pcs[index] = _pcs[index]; + } + _pcs_size = new_pcs_size; + _pcs = new_pcs; + } + assert(_pcs_size > _pcs_length, "There must be room for after expanding"); + + _pcs[_pcs_length++] = PcDesc(pc_offset, DebugInformationRecorder::serialized_null, + DebugInformationRecorder::serialized_null); +} + + +int DebugInformationRecorder::serialize_monitor_values(GrowableArray* monitors) { + if (monitors == NULL || monitors->is_empty()) return DebugInformationRecorder::serialized_null; + assert(_recording_state == rs_safepoint, "must be recording a safepoint"); + int result = stream()->position(); + stream()->write_int(monitors->length()); + for (int index = 0; index < monitors->length(); index++) { + monitors->at(index)->write_on(stream()); + } + assert(result != serialized_null, "sanity"); + + // (See comment below on DebugInformationRecorder::describe_scope.) + int shared_result = find_sharable_decode_offset(result); + if (shared_result != serialized_null) { + stream()->set_position(result); + result = shared_result; + } + + return result; +} + + +int DebugInformationRecorder::serialize_scope_values(GrowableArray* values) { + if (values == NULL || values->is_empty()) return DebugInformationRecorder::serialized_null; + assert(_recording_state == rs_safepoint, "must be recording a safepoint"); + int result = stream()->position(); + assert(result != serialized_null, "sanity"); + stream()->write_int(values->length()); + for (int index = 0; index < values->length(); index++) { + values->at(index)->write_on(stream()); + } + + // (See comment below on DebugInformationRecorder::describe_scope.) + int shared_result = find_sharable_decode_offset(result); + if (shared_result != serialized_null) { + stream()->set_position(result); + result = shared_result; + } + + return result; +} + + +#ifndef PRODUCT +// These variables are put into one block to reduce relocations +// and make it simpler to print from the debugger. +static +struct dir_stats_struct { + int chunks_queried; + int chunks_shared; + int chunks_reshared; + int chunks_elided; + + void print() { + tty->print_cr("Debug Data Chunks: %d, shared %d+%d, non-SP's elided %d", + chunks_queried, + chunks_shared, chunks_reshared, + chunks_elided); + } +} dir_stats; +#endif //PRODUCT + + +int DebugInformationRecorder::find_sharable_decode_offset(int stream_offset) { + // Only pull this trick if non-safepoint recording + // is enabled, for now. + if (!recording_non_safepoints()) + return serialized_null; + + NOT_PRODUCT(++dir_stats.chunks_queried); + int stream_length = stream()->position() - stream_offset; + assert(stream_offset != serialized_null, "should not be null"); + assert(stream_length != 0, "should not be empty"); + + DIR_Chunk* ns = new(this) DIR_Chunk(stream_offset, stream_length, this); + + // Look in previously shared scopes first: + DIR_Chunk* ms = ns->find_match(_shared_chunks, 0, this); + if (ms != NULL) { + NOT_PRODUCT(++dir_stats.chunks_reshared); + assert(ns+1 == _next_chunk, ""); + _next_chunk = ns; + return ms->_offset; + } + + // Look in recently encountered scopes next: + const int MAX_RECENT = 50; + int start_index = _all_chunks->length() - MAX_RECENT; + if (start_index < 0) start_index = 0; + ms = ns->find_match(_all_chunks, start_index, this); + if (ms != NULL) { + NOT_PRODUCT(++dir_stats.chunks_shared); + // Searching in _all_chunks is limited to a window, + // but searching in _shared_chunks is unlimited. + _shared_chunks->append(ms); + assert(ns+1 == _next_chunk, ""); + _next_chunk = ns; + return ms->_offset; + } + + // No match. Add this guy to the list, in hopes of future shares. + _all_chunks->append(ns); + return serialized_null; +} + + +// must call add_safepoint before: it sets PcDesc and this routine uses +// the last PcDesc set +void DebugInformationRecorder::describe_scope(int pc_offset, + ciMethod* method, + int bci, + DebugToken* locals, + DebugToken* expressions, + DebugToken* monitors) { + assert(_recording_state != rs_null, "nesting of recording calls"); + PcDesc* last_pd = last_pc(); + assert(last_pd->pc_offset() == pc_offset, "must be last pc"); + int sender_stream_offset = last_pd->scope_decode_offset(); + // update the stream offset of current pc desc + int stream_offset = stream()->position(); + last_pd->set_scope_decode_offset(stream_offset); + + // serialize sender stream offest + stream()->write_int(sender_stream_offset); + + // serialize scope + jobject method_enc = (method == NULL)? NULL: method->encoding(); + stream()->write_int(oop_recorder()->find_index(method_enc)); + stream()->write_bci(bci); + assert(method == NULL || + (method->is_native() && bci == 0) || + (!method->is_native() && 0 <= bci && bci < method->code_size()) || + bci == -1, "illegal bci"); + + // serialize the locals/expressions/monitors + stream()->write_int((intptr_t) locals); + stream()->write_int((intptr_t) expressions); + stream()->write_int((intptr_t) monitors); + + // Here's a tricky bit. We just wrote some bytes. + // Wouldn't it be nice to find that we had already + // written those same bytes somewhere else? + // If we get lucky this way, reset the stream + // and reuse the old bytes. By the way, this + // trick not only shares parent scopes, but also + // compresses equivalent non-safepoint PcDescs. + int shared_stream_offset = find_sharable_decode_offset(stream_offset); + if (shared_stream_offset != serialized_null) { + stream()->set_position(stream_offset); + last_pd->set_scope_decode_offset(shared_stream_offset); + } +} + +void DebugInformationRecorder::dump_object_pool(GrowableArray* objects) { + guarantee( _pcs_length > 0, "safepoint must exist before describing scopes"); + PcDesc* last_pd = &_pcs[_pcs_length-1]; + if (objects != NULL) { + for (int i = objects->length() - 1; i >= 0; i--) { + ((ObjectValue*) objects->at(i))->set_visited(false); + } + } + int offset = serialize_scope_values(objects); + last_pd->set_obj_decode_offset(offset); +} + +void DebugInformationRecorder::end_scopes(int pc_offset, bool is_safepoint) { + assert(_recording_state == (is_safepoint? rs_safepoint: rs_non_safepoint), + "nesting of recording calls"); + debug_only(_recording_state = rs_null); + + // Try to compress away an equivalent non-safepoint predecessor. + // (This only works because we have previously recognized redundant + // scope trees and made them use a common scope_decode_offset.) + if (_pcs_length >= 2 && recording_non_safepoints()) { + PcDesc* last = last_pc(); + PcDesc* prev = prev_pc(); + // If prev is (a) not a safepoint and (b) has the same + // stream pointer, then it can be coalesced into the last. + // This is valid because non-safepoints are only sought + // with pc_desc_near, which (when it misses prev) will + // search forward until it finds last. + // In addition, it does not matter if the last PcDesc + // is for a safepoint or not. + if (_prev_safepoint_pc < prev->pc_offset() && + prev->scope_decode_offset() == last->scope_decode_offset()) { + assert(prev == last-1, "sane"); + prev->set_pc_offset(pc_offset); + _pcs_length -= 1; + NOT_PRODUCT(++dir_stats.chunks_elided); + } + } + + // We have just recorded this safepoint. + // Remember it in case the previous paragraph needs to know. + if (is_safepoint) { + _prev_safepoint_pc = pc_offset; + } +} + +DebugToken* DebugInformationRecorder::create_scope_values(GrowableArray* values) { + assert(!_oop_recorder->is_complete(), "not frozen yet"); + return (DebugToken*) (intptr_t) serialize_scope_values(values); +} + + +DebugToken* DebugInformationRecorder::create_monitor_values(GrowableArray* monitors) { + assert(!_oop_recorder->is_complete(), "not frozen yet"); + return (DebugToken*) (intptr_t) serialize_monitor_values(monitors); +} + + +int DebugInformationRecorder::data_size() { + debug_only(_oop_recorder->oop_size()); // mark it "frozen" for asserts + return _stream->position(); +} + + +int DebugInformationRecorder::pcs_size() { + debug_only(_oop_recorder->oop_size()); // mark it "frozen" for asserts + if (last_pc()->pc_offset() != PcDesc::upper_offset_limit) + add_new_pc_offset(PcDesc::upper_offset_limit); + return _pcs_length * sizeof(PcDesc); +} + + +void DebugInformationRecorder::copy_to(nmethod* nm) { + nm->copy_scopes_data(stream()->buffer(), stream()->position()); + nm->copy_scopes_pcs(_pcs, _pcs_length); +} + + +void DebugInformationRecorder::verify(const nmethod* code) { + Unimplemented(); +} + +#ifndef PRODUCT +void DebugInformationRecorder::print_statistics() { + dir_stats.print(); +} +#endif //PRODUCT diff --git a/hotspot/src/share/vm/code/debugInfoRec.hpp b/hotspot/src/share/vm/code/debugInfoRec.hpp new file mode 100644 index 00000000000..9e775c964da --- /dev/null +++ b/hotspot/src/share/vm/code/debugInfoRec.hpp @@ -0,0 +1,182 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//** The DebugInformationRecorder collects debugging information +// for a compiled method. +// Debugging information is used for: +// - garbage collecting compiled frames +// - stack tracing across compiled frames +// - deoptimizating compiled frames +// +// The implementation requires the compiler to use the recorder +// in the following order: +// 1) Describe debug information for safepoints at increasing addresses. +// a) Add safepoint entry (use add_safepoint or add_non_safepoint) +// b) Describe scopes for that safepoint +// - create locals if needed (use create_scope_values) +// - create expressions if needed (use create_scope_values) +// - create monitor stack if needed (use create_monitor_values) +// - describe scope (use describe_scope) +// "repeat last four steps for all scopes" +// "outer most scope first and inner most scope last" +// NB: nodes from create_scope_values and create_locations +// can be reused for simple sharing. +// - mark the end of the scopes (end_safepoint or end_non_safepoint) +// 2) Use oop_size, data_size, pcs_size to create the nmethod and +// finally migrate the debugging information into the nmethod +// by calling copy_to. + +class DebugToken; // Opaque datatype for stored: + // - GrowableArray + // - GrowableArray + +// Alias for InvocationEntryBci. +// Both constants are used for a pseudo-BCI which refers +// to the state just _before_ a method is entered. +// SynchronizationEntryBCI is used where the emphasis +// is on the implicit monitorenter of a synchronized method. +const int SynchronizationEntryBCI = InvocationEntryBci; + +class DIR_Chunk; // private class, a nugget of collected information + +class DebugInformationRecorder: public ResourceObj { + public: + // constructor + DebugInformationRecorder(OopRecorder* oop_recorder); + + // adds an oopmap at a specific offset + void add_oopmap(int pc_offset, OopMap* map); + + // adds a jvm mapping at pc-offset, for a safepoint only + void add_safepoint(int pc_offset, OopMap* map); + + // adds a jvm mapping at pc-offset, for a non-safepoint (profile point) + void add_non_safepoint(int pc_offset); + + // Describes debugging information for a scope at the given pc_offset. + // Calls must be in non-decreasing order of pc_offset. + // If there are several calls at a single pc_offset, + // then they occur in the same order as they were performed by the JVM, + // with the most recent (innermost) call being described last. + // For a safepoint, the pc_offset must have been mentioned + // previously by add_safepoint. + // Otherwise, the pc_offset must have been mentioned previously + // by add_non_safepoint, and the locals, expressions, and monitors + // must all be null. + void describe_scope(int pc_offset, + ciMethod* method, + int bci, + DebugToken* locals = NULL, + DebugToken* expressions = NULL, + DebugToken* monitors = NULL); + + + void dump_object_pool(GrowableArray* objects); + + // This call must follow every add_safepoint, + // after any intervening describe_scope calls. + void end_safepoint(int pc_offset) { end_scopes(pc_offset, true); } + void end_non_safepoint(int pc_offset) { end_scopes(pc_offset, false); } + + // helper fuctions for describe_scope to enable sharing + DebugToken* create_scope_values(GrowableArray* values); + DebugToken* create_monitor_values(GrowableArray* monitors); + + // returns the size of the generated scopeDescs. + int data_size(); + int pcs_size(); + int oop_size() { return oop_recorder()->oop_size(); } + + // copy the generated debugging information to nmethod + void copy_to(nmethod* nm); + + // verifies the debug information + void verify(const nmethod* code); + + static void print_statistics() PRODUCT_RETURN; + + // Method for setting oopmaps to temporarily preserve old handling of oopmaps + OopMapSet *_oopmaps; + void set_oopmaps(OopMapSet *oopmaps) { _oopmaps = oopmaps; } + + OopRecorder* oop_recorder() { return _oop_recorder; } + + int last_pc_offset() { return last_pc()->pc_offset(); } + + bool recording_non_safepoints() { return _recording_non_safepoints; } + + private: + friend class ScopeDesc; + friend class vframeStreamCommon; + friend class DIR_Chunk; + + // True if we are recording non-safepoint scopes. + // This flag is set if DebugNonSafepoints is true, or if + // JVMTI post_compiled_method_load events are enabled. + const bool _recording_non_safepoints; + + DebugInfoWriteStream* _stream; + + DebugInfoWriteStream* stream() const { return _stream; } + + OopRecorder* _oop_recorder; + + // Scopes that have been described so far. + GrowableArray* _all_chunks; + GrowableArray* _shared_chunks; + DIR_Chunk* _next_chunk; + DIR_Chunk* _next_chunk_limit; + +#ifdef ASSERT + enum { rs_null, rs_safepoint, rs_non_safepoint }; + int _recording_state; +#endif + + PcDesc* _pcs; + int _pcs_size; + int _pcs_length; + // Note: Would use GrowableArray, but structs are not supported. + + // PC of most recent real safepoint before the current one, + // updated after end_scopes. + int _prev_safepoint_pc; + + PcDesc* last_pc() { + guarantee(_pcs_length > 0, "a safepoint must be declared already"); + return &_pcs[_pcs_length-1]; + } + PcDesc* prev_pc() { + guarantee(_pcs_length > 1, "a safepoint must be declared already"); + return &_pcs[_pcs_length-2]; + } + void add_new_pc_offset(int pc_offset); + void end_scopes(int pc_offset, bool is_safepoint); + + int serialize_monitor_values(GrowableArray* monitors); + int serialize_scope_values(GrowableArray* values); + int find_sharable_decode_offset(int stream_offset); + + public: + enum { serialized_null = 0 }; +}; diff --git a/hotspot/src/share/vm/code/dependencies.cpp b/hotspot/src/share/vm/code/dependencies.cpp new file mode 100644 index 00000000000..75d0546642e --- /dev/null +++ b/hotspot/src/share/vm/code/dependencies.cpp @@ -0,0 +1,1549 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_dependencies.cpp.incl" + + +#ifdef ASSERT +static bool must_be_in_vm() { + Thread* thread = Thread::current(); + if (thread->is_Java_thread()) + return ((JavaThread*)thread)->thread_state() == _thread_in_vm; + else + return true; //something like this: thread->is_VM_thread(); +} +#endif //ASSERT + +void Dependencies::initialize(ciEnv* env) { + Arena* arena = env->arena(); + _oop_recorder = env->oop_recorder(); + _log = env->log(); + _dep_seen = new(arena) GrowableArray(arena, 500, 0, 0); + DEBUG_ONLY(_deps[end_marker] = NULL); + for (int i = (int)FIRST_TYPE; i < (int)TYPE_LIMIT; i++) { + _deps[i] = new(arena) GrowableArray(arena, 10, 0, 0); + } + _content_bytes = NULL; + _size_in_bytes = (size_t)-1; + + assert(TYPE_LIMIT <= (1<is_array_klass()) { + // As a special case, support this assertion on an array type, + // which reduces to an assertion on its element type. + // Note that this cannot be done with assertions that + // relate to concreteness or abstractness. + ciType* elemt = ctxk->as_array_klass()->base_element_type(); + if (!elemt->is_instance_klass()) return; // Ex: int[][] + ctxk = elemt->as_instance_klass(); + //if (ctxk->is_final()) return; // Ex: String[][] + } + check_ctxk(ctxk); + assert_common_1(leaf_type, ctxk); +} + +void Dependencies::assert_abstract_with_unique_concrete_subtype(ciKlass* ctxk, ciKlass* conck) { + check_ctxk_abstract(ctxk); + assert_common_2(abstract_with_unique_concrete_subtype, ctxk, conck); +} + +void Dependencies::assert_abstract_with_no_concrete_subtype(ciKlass* ctxk) { + check_ctxk_abstract(ctxk); + assert_common_1(abstract_with_no_concrete_subtype, ctxk); +} + +void Dependencies::assert_concrete_with_no_concrete_subtype(ciKlass* ctxk) { + check_ctxk_concrete(ctxk); + assert_common_1(concrete_with_no_concrete_subtype, ctxk); +} + +void Dependencies::assert_unique_concrete_method(ciKlass* ctxk, ciMethod* uniqm) { + check_ctxk(ctxk); + assert_common_2(unique_concrete_method, ctxk, uniqm); +} + +void Dependencies::assert_abstract_with_exclusive_concrete_subtypes(ciKlass* ctxk, ciKlass* k1, ciKlass* k2) { + check_ctxk(ctxk); + assert_common_3(abstract_with_exclusive_concrete_subtypes_2, ctxk, k1, k2); +} + +void Dependencies::assert_exclusive_concrete_methods(ciKlass* ctxk, ciMethod* m1, ciMethod* m2) { + check_ctxk(ctxk); + assert_common_3(exclusive_concrete_methods_2, ctxk, m1, m2); +} + +void Dependencies::assert_has_no_finalizable_subclasses(ciKlass* ctxk) { + check_ctxk(ctxk); + assert_common_1(no_finalizable_subclasses, ctxk); +} + +// Helper function. If we are adding a new dep. under ctxk2, +// try to find an old dep. under a broader* ctxk1. If there is +// +bool Dependencies::maybe_merge_ctxk(GrowableArray* deps, + int ctxk_i, ciKlass* ctxk2) { + ciKlass* ctxk1 = deps->at(ctxk_i)->as_klass(); + if (ctxk2->is_subtype_of(ctxk1)) { + return true; // success, and no need to change + } else if (ctxk1->is_subtype_of(ctxk2)) { + // new context class fully subsumes previous one + deps->at_put(ctxk_i, ctxk2); + return true; + } else { + return false; + } +} + +void Dependencies::assert_common_1(Dependencies::DepType dept, ciObject* x) { + assert(dep_args(dept) == 1, "sanity"); + log_dependency(dept, x); + GrowableArray* deps = _deps[dept]; + + // see if the same (or a similar) dep is already recorded + if (note_dep_seen(dept, x)) { + assert(deps->find(x) >= 0, "sanity"); + } else { + deps->append(x); + } +} + +void Dependencies::assert_common_2(Dependencies::DepType dept, + ciKlass* ctxk, ciObject* x) { + assert(dep_context_arg(dept) == 0, "sanity"); + assert(dep_args(dept) == 2, "sanity"); + log_dependency(dept, ctxk, x); + GrowableArray* deps = _deps[dept]; + + // see if the same (or a similar) dep is already recorded + if (note_dep_seen(dept, x)) { + // look in this bucket for redundant assertions + const int stride = 2; + for (int i = deps->length(); (i -= stride) >= 0; ) { + ciObject* x1 = deps->at(i+1); + if (x == x1) { // same subject; check the context + if (maybe_merge_ctxk(deps, i+0, ctxk)) { + return; + } + } + } + } + + // append the assertion in the correct bucket: + deps->append(ctxk); + deps->append(x); +} + +void Dependencies::assert_common_3(Dependencies::DepType dept, + ciKlass* ctxk, ciObject* x, ciObject* x2) { + assert(dep_context_arg(dept) == 0, "sanity"); + assert(dep_args(dept) == 3, "sanity"); + log_dependency(dept, ctxk, x, x2); + GrowableArray* deps = _deps[dept]; + + // try to normalize an unordered pair: + bool swap = false; + switch (dept) { + case abstract_with_exclusive_concrete_subtypes_2: + swap = (x->ident() > x2->ident() && x != ctxk); + break; + case exclusive_concrete_methods_2: + swap = (x->ident() > x2->ident() && x->as_method()->holder() != ctxk); + break; + } + if (swap) { ciObject* t = x; x = x2; x2 = t; } + + // see if the same (or a similar) dep is already recorded + if (note_dep_seen(dept, x) && note_dep_seen(dept, x2)) { + // look in this bucket for redundant assertions + const int stride = 3; + for (int i = deps->length(); (i -= stride) >= 0; ) { + ciObject* y = deps->at(i+1); + ciObject* y2 = deps->at(i+2); + if (x == y && x2 == y2) { // same subjects; check the context + if (maybe_merge_ctxk(deps, i+0, ctxk)) { + return; + } + } + } + } + // append the assertion in the correct bucket: + deps->append(ctxk); + deps->append(x); + deps->append(x2); +} + +/// Support for encoding dependencies into an nmethod: + +void Dependencies::copy_to(nmethod* nm) { + address beg = nm->dependencies_begin(); + address end = nm->dependencies_end(); + guarantee(end - beg >= (ptrdiff_t) size_in_bytes(), "bad sizing"); + Copy::disjoint_words((HeapWord*) content_bytes(), + (HeapWord*) beg, + size_in_bytes() / sizeof(HeapWord)); + assert(size_in_bytes() % sizeof(HeapWord) == 0, "copy by words"); +} + +static int sort_dep(ciObject** p1, ciObject** p2, int narg) { + for (int i = 0; i < narg; i++) { + int diff = p1[i]->ident() - p2[i]->ident(); + if (diff != 0) return diff; + } + return 0; +} +static int sort_dep_arg_1(ciObject** p1, ciObject** p2) +{ return sort_dep(p1, p2, 1); } +static int sort_dep_arg_2(ciObject** p1, ciObject** p2) +{ return sort_dep(p1, p2, 2); } +static int sort_dep_arg_3(ciObject** p1, ciObject** p2) +{ return sort_dep(p1, p2, 3); } + +void Dependencies::sort_all_deps() { + for (int deptv = (int)FIRST_TYPE; deptv < (int)TYPE_LIMIT; deptv++) { + DepType dept = (DepType)deptv; + GrowableArray* deps = _deps[dept]; + if (deps->length() <= 1) continue; + switch (dep_args(dept)) { + case 1: deps->sort(sort_dep_arg_1, 1); break; + case 2: deps->sort(sort_dep_arg_2, 2); break; + case 3: deps->sort(sort_dep_arg_3, 3); break; + default: ShouldNotReachHere(); + } + } +} + +size_t Dependencies::estimate_size_in_bytes() { + size_t est_size = 100; + for (int deptv = (int)FIRST_TYPE; deptv < (int)TYPE_LIMIT; deptv++) { + DepType dept = (DepType)deptv; + GrowableArray* deps = _deps[dept]; + est_size += deps->length()*2; // tags and argument(s) + } + return est_size; +} + +ciKlass* Dependencies::ctxk_encoded_as_null(DepType dept, ciObject* x) { + switch (dept) { + case abstract_with_exclusive_concrete_subtypes_2: + return x->as_klass(); + case unique_concrete_method: + case exclusive_concrete_methods_2: + return x->as_method()->holder(); + } + return NULL; // let NULL be NULL +} + +klassOop Dependencies::ctxk_encoded_as_null(DepType dept, oop x) { + assert(must_be_in_vm(), "raw oops here"); + switch (dept) { + case abstract_with_exclusive_concrete_subtypes_2: + assert(x->is_klass(), "sanity"); + return (klassOop) x; + case unique_concrete_method: + case exclusive_concrete_methods_2: + assert(x->is_method(), "sanity"); + return ((methodOop)x)->method_holder(); + } + return NULL; // let NULL be NULL +} + +void Dependencies::encode_content_bytes() { + sort_all_deps(); + + // cast is safe, no deps can overflow INT_MAX + CompressedWriteStream bytes((int)estimate_size_in_bytes()); + + for (int deptv = (int)FIRST_TYPE; deptv < (int)TYPE_LIMIT; deptv++) { + DepType dept = (DepType)deptv; + GrowableArray* deps = _deps[dept]; + if (deps->length() == 0) continue; + int stride = dep_args(dept); + int ctxkj = dep_context_arg(dept); // -1 if no context arg + assert(stride > 0, "sanity"); + for (int i = 0; i < deps->length(); i += stride) { + jbyte code_byte = (jbyte)dept; + int skipj = -1; + if (ctxkj >= 0 && ctxkj+1 < stride) { + ciKlass* ctxk = deps->at(i+ctxkj+0)->as_klass(); + ciObject* x = deps->at(i+ctxkj+1); // following argument + if (ctxk == ctxk_encoded_as_null(dept, x)) { + skipj = ctxkj; // we win: maybe one less oop to keep track of + code_byte |= default_context_type_bit; + } + } + bytes.write_byte(code_byte); + for (int j = 0; j < stride; j++) { + if (j == skipj) continue; + bytes.write_int(_oop_recorder->find_index(deps->at(i+j)->encoding())); + } + } + } + + // write a sentinel byte to mark the end + bytes.write_byte(end_marker); + + // round it out to a word boundary + while (bytes.position() % sizeof(HeapWord) != 0) { + bytes.write_byte(end_marker); + } + + // check whether the dept byte encoding really works + assert((jbyte)default_context_type_bit != 0, "byte overflow"); + + _content_bytes = bytes.buffer(); + _size_in_bytes = bytes.position(); +} + + +const char* Dependencies::_dep_name[TYPE_LIMIT] = { + "end_marker", + "evol_method", + "leaf_type", + "abstract_with_unique_concrete_subtype", + "abstract_with_no_concrete_subtype", + "concrete_with_no_concrete_subtype", + "unique_concrete_method", + "abstract_with_exclusive_concrete_subtypes_2", + "exclusive_concrete_methods_2", + "no_finalizable_subclasses" +}; + +int Dependencies::_dep_args[TYPE_LIMIT] = { + -1,// end_marker + 1, // evol_method m + 1, // leaf_type ctxk + 2, // abstract_with_unique_concrete_subtype ctxk, k + 1, // abstract_with_no_concrete_subtype ctxk + 1, // concrete_with_no_concrete_subtype ctxk + 2, // unique_concrete_method ctxk, m + 3, // unique_concrete_subtypes_2 ctxk, k1, k2 + 3, // unique_concrete_methods_2 ctxk, m1, m2 + 1 // no_finalizable_subclasses ctxk +}; + +const char* Dependencies::dep_name(Dependencies::DepType dept) { + if (!dept_in_mask(dept, all_types)) return "?bad-dep?"; + return _dep_name[dept]; +} + +int Dependencies::dep_args(Dependencies::DepType dept) { + if (!dept_in_mask(dept, all_types)) return -1; + return _dep_args[dept]; +} + +// for the sake of the compiler log, print out current dependencies: +void Dependencies::log_all_dependencies() { + if (log() == NULL) return; + ciObject* args[max_arg_count]; + for (int deptv = (int)FIRST_TYPE; deptv < (int)TYPE_LIMIT; deptv++) { + DepType dept = (DepType)deptv; + GrowableArray* deps = _deps[dept]; + if (deps->length() == 0) continue; + int stride = dep_args(dept); + for (int i = 0; i < deps->length(); i += stride) { + for (int j = 0; j < stride; j++) { + // flush out the identities before printing + args[j] = deps->at(i+j); + } + write_dependency_to(log(), dept, stride, args); + } + } +} + +void Dependencies::write_dependency_to(CompileLog* log, + DepType dept, + int nargs, oop args[], + klassOop witness) { + if (log == NULL) { + return; + } + ciEnv* env = ciEnv::current(); + ciObject* ciargs[max_arg_count]; + assert(nargs <= max_arg_count, "oob"); + for (int j = 0; j < nargs; j++) { + ciargs[j] = env->get_object(args[j]); + } + Dependencies::write_dependency_to(log, dept, nargs, ciargs, witness); +} + +void Dependencies::write_dependency_to(CompileLog* log, + DepType dept, + int nargs, ciObject* args[], + klassOop witness) { + if (log == NULL) return; + assert(nargs <= max_arg_count, "oob"); + int argids[max_arg_count]; + int ctxkj = dep_context_arg(dept); // -1 if no context arg + int j; + for (j = 0; j < nargs; j++) { + argids[j] = log->identify(args[j]); + } + if (witness != NULL) { + log->begin_elem("dependency_failed"); + } else { + log->begin_elem("dependency"); + } + log->print(" type='%s'", dep_name(dept)); + if (ctxkj >= 0) { + log->print(" ctxk='%d'", argids[ctxkj]); + } + // write remaining arguments, if any. + for (j = 0; j < nargs; j++) { + if (j == ctxkj) continue; // already logged + if (j == 1) { + log->print( " x='%d'", argids[j]); + } else { + log->print(" x%d='%d'", j, argids[j]); + } + } + if (witness != NULL) { + log->object("witness", witness); + log->stamp(); + } + log->end_elem(); +} + +void Dependencies::write_dependency_to(xmlStream* xtty, + DepType dept, + int nargs, oop args[], + klassOop witness) { + if (xtty == NULL) return; + ttyLocker ttyl; + int ctxkj = dep_context_arg(dept); // -1 if no context arg + if (witness != NULL) { + xtty->begin_elem("dependency_failed"); + } else { + xtty->begin_elem("dependency"); + } + xtty->print(" type='%s'", dep_name(dept)); + if (ctxkj >= 0) { + xtty->object("ctxk", args[ctxkj]); + } + // write remaining arguments, if any. + for (int j = 0; j < nargs; j++) { + if (j == ctxkj) continue; // already logged + if (j == 1) { + xtty->object("x", args[j]); + } else { + char xn[10]; sprintf(xn, "x%d", j); + xtty->object(xn, args[j]); + } + } + if (witness != NULL) { + xtty->object("witness", witness); + xtty->stamp(); + } + xtty->end_elem(); +} + +void Dependencies::print_dependency(DepType dept, int nargs, oop args[], + klassOop witness) { + ResourceMark rm; + ttyLocker ttyl; // keep the following output all in one block + tty->print_cr("%s of type %s", + (witness == NULL)? "Dependency": "Failed dependency", + dep_name(dept)); + // print arguments + int ctxkj = dep_context_arg(dept); // -1 if no context arg + for (int j = 0; j < nargs; j++) { + oop arg = args[j]; + bool put_star = false; + if (arg == NULL) continue; + const char* what; + if (j == ctxkj) { + what = "context"; + put_star = !Dependencies::is_concrete_klass((klassOop)arg); + } else if (arg->is_method()) { + what = "method "; + put_star = !Dependencies::is_concrete_method((methodOop)arg); + } else if (arg->is_klass()) { + what = "class "; + } else { + what = "object "; + } + tty->print(" %s = %s", what, (put_star? "*": "")); + if (arg->is_klass()) + tty->print("%s", Klass::cast((klassOop)arg)->external_name()); + else + arg->print_value(); + tty->cr(); + } + if (witness != NULL) { + bool put_star = !Dependencies::is_concrete_klass(witness); + tty->print_cr(" witness = %s%s", + (put_star? "*": ""), + Klass::cast(witness)->external_name()); + } +} + +void Dependencies::DepStream::log_dependency(klassOop witness) { + if (_deps == NULL && xtty == NULL) return; // fast cutout for runtime + int nargs = argument_count(); + oop args[max_arg_count]; + for (int j = 0; j < nargs; j++) { + args[j] = argument(j); + } + if (_deps != NULL && _deps->log() != NULL) { + Dependencies::write_dependency_to(_deps->log(), + type(), nargs, args, witness); + } else { + Dependencies::write_dependency_to(xtty, + type(), nargs, args, witness); + } +} + +void Dependencies::DepStream::print_dependency(klassOop witness, bool verbose) { + int nargs = argument_count(); + oop args[max_arg_count]; + for (int j = 0; j < nargs; j++) { + args[j] = argument(j); + } + Dependencies::print_dependency(type(), nargs, args, witness); + if (verbose) { + if (_code != NULL) { + tty->print(" code: "); + _code->print_value_on(tty); + tty->cr(); + } + } +} + + +/// Dependency stream support (decodes dependencies from an nmethod): + +#ifdef ASSERT +void Dependencies::DepStream::initial_asserts(size_t byte_limit) { + assert(must_be_in_vm(), "raw oops here"); + _byte_limit = byte_limit; + _type = (DepType)(end_marker-1); // defeat "already at end" assert + assert((_code!=NULL) + (_deps!=NULL) == 1, "one or t'other"); +} +#endif //ASSERT + +bool Dependencies::DepStream::next() { + assert(_type != end_marker, "already at end"); + if (_bytes.position() == 0 && _code != NULL + && _code->dependencies_size() == 0) { + // Method has no dependencies at all. + return false; + } + int code_byte = (_bytes.read_byte() & 0xFF); + if (code_byte == end_marker) { + DEBUG_ONLY(_type = end_marker); + return false; + } else { + int ctxk_bit = (code_byte & Dependencies::default_context_type_bit); + code_byte -= ctxk_bit; + DepType dept = (DepType)code_byte; + _type = dept; + guarantee((dept - FIRST_TYPE) < (TYPE_LIMIT - FIRST_TYPE), + "bad dependency type tag"); + int stride = _dep_args[dept]; + assert(stride == dep_args(dept), "sanity"); + int skipj = -1; + if (ctxk_bit != 0) { + skipj = 0; // currently the only context argument is at zero + assert(skipj == dep_context_arg(dept), "zero arg always ctxk"); + } + for (int j = 0; j < stride; j++) { + _xi[j] = (j == skipj)? 0: _bytes.read_int(); + } + DEBUG_ONLY(_xi[stride] = -1); // help detect overruns + return true; + } +} + +inline oop Dependencies::DepStream::recorded_oop_at(int i) { + return (_code != NULL) + ? _code->oop_at(i) + : JNIHandles::resolve(_deps->oop_recorder()->handle_at(i)); +} + +oop Dependencies::DepStream::argument(int i) { + return recorded_oop_at(argument_index(i)); +} + +klassOop Dependencies::DepStream::context_type() { + assert(must_be_in_vm(), "raw oops here"); + int ctxkj = dep_context_arg(_type); // -1 if no context arg + if (ctxkj < 0) { + return NULL; // for example, evol_method + } else { + oop k = recorded_oop_at(_xi[ctxkj]); + if (k != NULL) { // context type was not compressed away + assert(k->is_klass(), "type check"); + return (klassOop) k; + } else { // recompute "default" context type + return ctxk_encoded_as_null(_type, recorded_oop_at(_xi[ctxkj+1])); + } + } +} + +/// Checking dependencies: + +// This hierarchy walker inspects subtypes of a given type, +// trying to find a "bad" class which breaks a dependency. +// Such a class is called a "witness" to the broken dependency. +// While searching around, we ignore "participants", which +// are already known to the dependency. +class ClassHierarchyWalker { + public: + enum { PARTICIPANT_LIMIT = 3 }; + + private: + // optional method descriptor to check for: + symbolOop _name; + symbolOop _signature; + + // special classes which are not allowed to be witnesses: + klassOop _participants[PARTICIPANT_LIMIT+1]; + int _num_participants; + + // cache of method lookups + methodOop _found_methods[PARTICIPANT_LIMIT+1]; + + // if non-zero, tells how many witnesses to convert to participants + int _record_witnesses; + + void initialize(klassOop participant) { + _record_witnesses = 0; + _participants[0] = participant; + _found_methods[0] = NULL; + _num_participants = 0; + if (participant != NULL) { + // Terminating NULL. + _participants[1] = NULL; + _found_methods[1] = NULL; + _num_participants = 1; + } + } + + void initialize_from_method(methodOop m) { + assert(m != NULL && m->is_method(), "sanity"); + _name = m->name(); + _signature = m->signature(); + } + + public: + // The walker is initialized to recognize certain methods and/or types + // as friendly participants. + ClassHierarchyWalker(klassOop participant, methodOop m) { + initialize_from_method(m); + initialize(participant); + } + ClassHierarchyWalker(methodOop m) { + initialize_from_method(m); + initialize(NULL); + } + ClassHierarchyWalker(klassOop participant = NULL) { + _name = NULL; + _signature = NULL; + initialize(participant); + } + + // This is common code for two searches: One for concrete subtypes, + // the other for concrete method implementations and overrides. + bool doing_subtype_search() { + return _name == NULL; + } + + int num_participants() { return _num_participants; } + klassOop participant(int n) { + assert((uint)n <= (uint)_num_participants, "oob"); + return _participants[n]; + } + + // Note: If n==num_participants, returns NULL. + methodOop found_method(int n) { + assert((uint)n <= (uint)_num_participants, "oob"); + methodOop fm = _found_methods[n]; + assert(n == _num_participants || fm != NULL, "proper usage"); + assert(fm == NULL || fm->method_holder() == _participants[n], "sanity"); + return fm; + } + +#ifdef ASSERT + // Assert that m is inherited into ctxk, without intervening overrides. + // (May return true even if this is not true, in corner cases where we punt.) + bool check_method_context(klassOop ctxk, methodOop m) { + if (m->method_holder() == ctxk) + return true; // Quick win. + if (m->is_private()) + return false; // Quick lose. Should not happen. + if (!(m->is_public() || m->is_protected())) + // The override story is complex when packages get involved. + return true; // Must punt the assertion to true. + Klass* k = Klass::cast(ctxk); + methodOop lm = k->lookup_method(m->name(), m->signature()); + if (lm == NULL && k->oop_is_instance()) { + // It might be an abstract interface method, devoid of mirandas. + lm = ((instanceKlass*)k)->lookup_method_in_all_interfaces(m->name(), + m->signature()); + } + if (lm == m) + // Method m is inherited into ctxk. + return true; + if (lm != NULL) { + if (!(lm->is_public() || lm->is_protected())) + // Method is [package-]private, so the override story is complex. + return true; // Must punt the assertion to true. + if ( !Dependencies::is_concrete_method(lm) + && !Dependencies::is_concrete_method(m) + && Klass::cast(lm->method_holder())->is_subtype_of(m->method_holder())) + // Method m is overridden by lm, but both are non-concrete. + return true; + } + ResourceMark rm; + tty->print_cr("Dependency method not found in the associated context:"); + tty->print_cr(" context = %s", Klass::cast(ctxk)->external_name()); + tty->print( " method = "); m->print_short_name(tty); tty->cr(); + if (lm != NULL) { + tty->print( " found = "); lm->print_short_name(tty); tty->cr(); + } + return false; + } +#endif + + void add_participant(klassOop participant) { + assert(_num_participants + _record_witnesses < PARTICIPANT_LIMIT, "oob"); + int np = _num_participants++; + _participants[np] = participant; + _participants[np+1] = NULL; + _found_methods[np+1] = NULL; + } + + void record_witnesses(int add) { + if (add > PARTICIPANT_LIMIT) add = PARTICIPANT_LIMIT; + assert(_num_participants + add < PARTICIPANT_LIMIT, "oob"); + _record_witnesses = add; + } + + bool is_witness(klassOop k) { + if (doing_subtype_search()) { + return Dependencies::is_concrete_klass(k); + } else { + methodOop m = instanceKlass::cast(k)->find_method(_name, _signature); + if (m == NULL || !Dependencies::is_concrete_method(m)) return false; + _found_methods[_num_participants] = m; + // Note: If add_participant(k) is called, + // the method m will already be memoized for it. + return true; + } + } + + bool is_participant(klassOop k) { + if (k == _participants[0]) { + return true; + } else if (_num_participants <= 1) { + return false; + } else { + return in_list(k, &_participants[1]); + } + } + bool ignore_witness(klassOop witness) { + if (_record_witnesses == 0) { + return false; + } else { + --_record_witnesses; + add_participant(witness); + return true; + } + } + static bool in_list(klassOop x, klassOop* list) { + for (int i = 0; ; i++) { + klassOop y = list[i]; + if (y == NULL) break; + if (y == x) return true; + } + return false; // not in list + } + + private: + // the actual search method: + klassOop find_witness_anywhere(klassOop context_type, + bool participants_hide_witnesses, + bool top_level_call = true); + // the spot-checking version: + klassOop find_witness_in(DepChange& changes, + klassOop context_type, + bool participants_hide_witnesses); + public: + klassOop find_witness_subtype(klassOop context_type, DepChange* changes = NULL) { + assert(doing_subtype_search(), "must set up a subtype search"); + // When looking for unexpected concrete types, + // do not look beneath expected ones. + const bool participants_hide_witnesses = true; + // CX > CC > C' is OK, even if C' is new. + // CX > { CC, C' } is not OK if C' is new, and C' is the witness. + if (changes != NULL) { + return find_witness_in(*changes, context_type, participants_hide_witnesses); + } else { + return find_witness_anywhere(context_type, participants_hide_witnesses); + } + } + klassOop find_witness_definer(klassOop context_type, DepChange* changes = NULL) { + assert(!doing_subtype_search(), "must set up a method definer search"); + // When looking for unexpected concrete methods, + // look beneath expected ones, to see if there are overrides. + const bool participants_hide_witnesses = true; + // CX.m > CC.m > C'.m is not OK, if C'.m is new, and C' is the witness. + if (changes != NULL) { + return find_witness_in(*changes, context_type, !participants_hide_witnesses); + } else { + return find_witness_anywhere(context_type, !participants_hide_witnesses); + } + } +}; + +#ifndef PRODUCT +static int deps_find_witness_calls = 0; +static int deps_find_witness_steps = 0; +static int deps_find_witness_recursions = 0; +static int deps_find_witness_singles = 0; +static int deps_find_witness_print = 0; // set to -1 to force a final print +static bool count_find_witness_calls() { + if (TraceDependencies || LogCompilation) { + int pcount = deps_find_witness_print + 1; + bool final_stats = (pcount == 0); + bool initial_call = (pcount == 1); + bool occasional_print = ((pcount & ((1<<10) - 1)) == 0); + if (pcount < 0) pcount = 1; // crude overflow protection + deps_find_witness_print = pcount; + if (VerifyDependencies && initial_call) { + tty->print_cr("Warning: TraceDependencies results may be inflated by VerifyDependencies"); + } + if (occasional_print || final_stats) { + // Every now and then dump a little info about dependency searching. + if (xtty != NULL) { + xtty->elem("deps_find_witness calls='%d' steps='%d' recursions='%d' singles='%d'", + deps_find_witness_calls, + deps_find_witness_steps, + deps_find_witness_recursions, + deps_find_witness_singles); + } + if (final_stats || (TraceDependencies && WizardMode)) { + tty->print_cr("Dependency check (find_witness) " + "calls=%d, steps=%d (avg=%.1f), recursions=%d, singles=%d", + deps_find_witness_calls, + deps_find_witness_steps, + (double)deps_find_witness_steps / deps_find_witness_calls, + deps_find_witness_recursions, + deps_find_witness_singles); + } + } + return true; + } + return false; +} +#else +#define count_find_witness_calls() (0) +#endif //PRODUCT + + +klassOop ClassHierarchyWalker::find_witness_in(DepChange& changes, + klassOop context_type, + bool participants_hide_witnesses) { + assert(changes.involves_context(context_type), "irrelevant dependency"); + klassOop new_type = changes.new_type(); + + count_find_witness_calls(); + NOT_PRODUCT(deps_find_witness_singles++); + + // Current thread must be in VM (not native mode, as in CI): + assert(must_be_in_vm(), "raw oops here"); + // Must not move the class hierarchy during this check: + assert_locked_or_safepoint(Compile_lock); + + assert(!is_participant(new_type), "only old classes are participants"); + if (participants_hide_witnesses) { + // If the new type is a subtype of a participant, we are done. + for (int i = 0; i < num_participants(); i++) { + klassOop part = participant(i); + if (part == NULL) continue; + assert(changes.involves_context(part) == Klass::cast(new_type)->is_subtype_of(part), + "correct marking of participants, b/c new_type is unique"); + if (changes.involves_context(part)) { + // new guy is protected from this check by previous participant + return NULL; + } + } + } + + if (is_witness(new_type) && + !ignore_witness(new_type)) { + return new_type; + } + + return NULL; +} + + +// Walk hierarchy under a context type, looking for unexpected types. +// Do not report participant types, and recursively walk beneath +// them only if participants_hide_witnesses is false. +// If top_level_call is false, skip testing the context type, +// because the caller has already considered it. +klassOop ClassHierarchyWalker::find_witness_anywhere(klassOop context_type, + bool participants_hide_witnesses, + bool top_level_call) { + // Current thread must be in VM (not native mode, as in CI): + assert(must_be_in_vm(), "raw oops here"); + // Must not move the class hierarchy during this check: + assert_locked_or_safepoint(Compile_lock); + + bool do_counts = count_find_witness_calls(); + + // Check the root of the sub-hierarchy first. + if (top_level_call) { + if (do_counts) { + NOT_PRODUCT(deps_find_witness_calls++); + NOT_PRODUCT(deps_find_witness_steps++); + } + if (is_participant(context_type)) { + if (participants_hide_witnesses) return NULL; + // else fall through to search loop... + } else if (is_witness(context_type) && !ignore_witness(context_type)) { + // The context is an abstract class or interface, to start with. + return context_type; + } + } + + // Now we must check each implementor and each subclass. + // Use a short worklist to avoid blowing the stack. + // Each worklist entry is a *chain* of subklass siblings to process. + const int CHAINMAX = 100; // >= 1 + instanceKlass::implementors_limit + Klass* chains[CHAINMAX]; + int chaini = 0; // index into worklist + Klass* chain; // scratch variable +#define ADD_SUBCLASS_CHAIN(k) { \ + assert(chaini < CHAINMAX, "oob"); \ + chain = instanceKlass::cast(k)->subklass(); \ + if (chain != NULL) chains[chaini++] = chain; } + + // Look for non-abstract subclasses. + // (Note: Interfaces do not have subclasses.) + ADD_SUBCLASS_CHAIN(context_type); + + // If it is an interface, search its direct implementors. + // (Their subclasses are additional indirect implementors. + // See instanceKlass::add_implementor.) + // (Note: nof_implementors is always zero for non-interfaces.) + int nof_impls = instanceKlass::cast(context_type)->nof_implementors(); + if (nof_impls > 1) { + // Avoid this case: *I.m > { A.m, C }; B.m > C + // Here, I.m has 2 concrete implementations, but m appears unique + // as A.m, because the search misses B.m when checking C. + // The inherited method B.m was getting missed by the walker + // when interface 'I' was the starting point. + // %%% Until this is fixed more systematically, bail out. + // (Old CHA had the same limitation.) + return context_type; + } + for (int i = 0; i < nof_impls; i++) { + klassOop impl = instanceKlass::cast(context_type)->implementor(i); + if (impl == NULL) { + // implementors array overflowed => no exact info. + return context_type; // report an inexact witness to this sad affair + } + if (do_counts) + { NOT_PRODUCT(deps_find_witness_steps++); } + if (is_participant(impl)) { + if (participants_hide_witnesses) continue; + // else fall through to process this guy's subclasses + } else if (is_witness(impl) && !ignore_witness(impl)) { + return impl; + } + ADD_SUBCLASS_CHAIN(impl); + } + + // Recursively process each non-trivial sibling chain. + while (chaini > 0) { + Klass* chain = chains[--chaini]; + for (Klass* subk = chain; subk != NULL; subk = subk->next_sibling()) { + klassOop sub = subk->as_klassOop(); + if (do_counts) { NOT_PRODUCT(deps_find_witness_steps++); } + if (is_participant(sub)) { + if (participants_hide_witnesses) continue; + // else fall through to process this guy's subclasses + } else if (is_witness(sub) && !ignore_witness(sub)) { + return sub; + } + if (chaini < (VerifyDependencies? 2: CHAINMAX)) { + // Fast path. (Partially disabled if VerifyDependencies.) + ADD_SUBCLASS_CHAIN(sub); + } else { + // Worklist overflow. Do a recursive call. Should be rare. + // The recursive call will have its own worklist, of course. + // (Note that sub has already been tested, so that there is + // no need for the recursive call to re-test. That's handy, + // since the recursive call sees sub as the context_type.) + if (do_counts) { NOT_PRODUCT(deps_find_witness_recursions++); } + klassOop witness = find_witness_anywhere(sub, + participants_hide_witnesses, + /*top_level_call=*/ false); + if (witness != NULL) return witness; + } + } + } + + // No witness found. The dependency remains unbroken. + return NULL; +#undef ADD_SUBCLASS_CHAIN +} + + +bool Dependencies::is_concrete_klass(klassOop k) { + if (Klass::cast(k)->is_abstract()) return false; + // %%% We could treat classes which are concrete but + // have not yet been instantiated as virtually abstract. + // This would require a deoptimization barrier on first instantiation. + //if (k->is_not_instantiated()) return false; + return true; +} + +bool Dependencies::is_concrete_method(methodOop m) { + if (m->is_abstract()) return false; + // %%% We could treat unexecuted methods as virtually abstract also. + // This would require a deoptimization barrier on first execution. + return !m->is_abstract(); +} + + +Klass* Dependencies::find_finalizable_subclass(Klass* k) { + if (k->is_interface()) return NULL; + if (k->has_finalizer()) return k; + k = k->subklass(); + while (k != NULL) { + Klass* result = find_finalizable_subclass(k); + if (result != NULL) return result; + k = k->next_sibling(); + } + return NULL; +} + + +bool Dependencies::is_concrete_klass(ciInstanceKlass* k) { + if (k->is_abstract()) return false; + // We could return also false if k does not yet appear to be + // instantiated, if the VM version supports this distinction also. + //if (k->is_not_instantiated()) return false; + return true; +} + +bool Dependencies::is_concrete_method(ciMethod* m) { + // Statics are irrelevant to virtual call sites. + if (m->is_static()) return false; + + // We could return also false if m does not yet appear to be + // executed, if the VM version supports this distinction also. + return !m->is_abstract(); +} + + +bool Dependencies::has_finalizable_subclass(ciInstanceKlass* k) { + return k->has_finalizable_subclass(); +} + + +// Any use of the contents (bytecodes) of a method must be +// marked by an "evol_method" dependency, if those contents +// can change. (Note: A method is always dependent on itself.) +klassOop Dependencies::check_evol_method(methodOop m) { + assert(must_be_in_vm(), "raw oops here"); + // Did somebody do a JVMTI RedefineClasses while our backs were turned? + // Or is there a now a breakpoint? + // (Assumes compiled code cannot handle bkpts; change if UseFastBreakpoints.) + if (m->is_old() + || m->number_of_breakpoints() > 0) { + return m->method_holder(); + } else { + return NULL; + } +} + +// This is a strong assertion: It is that the given type +// has no subtypes whatever. It is most useful for +// optimizing checks on reflected types or on array types. +// (Checks on types which are derived from real instances +// can be optimized more strongly than this, because we +// know that the checked type comes from a concrete type, +// and therefore we can disregard abstract types.) +klassOop Dependencies::check_leaf_type(klassOop ctxk) { + assert(must_be_in_vm(), "raw oops here"); + assert_locked_or_safepoint(Compile_lock); + instanceKlass* ctx = instanceKlass::cast(ctxk); + Klass* sub = ctx->subklass(); + if (sub != NULL) { + return sub->as_klassOop(); + } else if (ctx->nof_implementors() != 0) { + // if it is an interface, it must be unimplemented + // (if it is not an interface, nof_implementors is always zero) + klassOop impl = ctx->implementor(0); + return (impl != NULL)? impl: ctxk; + } else { + return NULL; + } +} + +// Test the assertion that conck is the only concrete subtype* of ctxk. +// The type conck itself is allowed to have have further concrete subtypes. +// This allows the compiler to narrow occurrences of ctxk by conck, +// when dealing with the types of actual instances. +klassOop Dependencies::check_abstract_with_unique_concrete_subtype(klassOop ctxk, + klassOop conck, + DepChange* changes) { + ClassHierarchyWalker wf(conck); + return wf.find_witness_subtype(ctxk, changes); +} + +// If a non-concrete class has no concrete subtypes, it is not (yet) +// instantiatable. This can allow the compiler to make some paths go +// dead, if they are gated by a test of the type. +klassOop Dependencies::check_abstract_with_no_concrete_subtype(klassOop ctxk, + DepChange* changes) { + // Find any concrete subtype, with no participants: + ClassHierarchyWalker wf; + return wf.find_witness_subtype(ctxk, changes); +} + + +// If a concrete class has no concrete subtypes, it can always be +// exactly typed. This allows the use of a cheaper type test. +klassOop Dependencies::check_concrete_with_no_concrete_subtype(klassOop ctxk, + DepChange* changes) { + // Find any concrete subtype, with only the ctxk as participant: + ClassHierarchyWalker wf(ctxk); + return wf.find_witness_subtype(ctxk, changes); +} + + +// Find the unique concrete proper subtype of ctxk, or NULL if there +// is more than one concrete proper subtype. If there are no concrete +// proper subtypes, return ctxk itself, whether it is concrete or not. +// The returned subtype is allowed to have have further concrete subtypes. +// That is, return CC1 for CX > CC1 > CC2, but NULL for CX > { CC1, CC2 }. +klassOop Dependencies::find_unique_concrete_subtype(klassOop ctxk) { + ClassHierarchyWalker wf(ctxk); // Ignore ctxk when walking. + wf.record_witnesses(1); // Record one other witness when walking. + klassOop wit = wf.find_witness_subtype(ctxk); + if (wit != NULL) return NULL; // Too many witnesses. + klassOop conck = wf.participant(0); + if (conck == NULL) { +#ifndef PRODUCT + // Make sure the dependency mechanism will pass this discovery: + if (VerifyDependencies) { + // Turn off dependency tracing while actually testing deps. + FlagSetting fs(TraceDependencies, false); + if (!Dependencies::is_concrete_klass(ctxk)) { + guarantee(NULL == + (void *)check_abstract_with_no_concrete_subtype(ctxk), + "verify dep."); + } else { + guarantee(NULL == + (void *)check_concrete_with_no_concrete_subtype(ctxk), + "verify dep."); + } + } +#endif //PRODUCT + return ctxk; // Return ctxk as a flag for "no subtypes". + } else { +#ifndef PRODUCT + // Make sure the dependency mechanism will pass this discovery: + if (VerifyDependencies) { + // Turn off dependency tracing while actually testing deps. + FlagSetting fs(TraceDependencies, false); + if (!Dependencies::is_concrete_klass(ctxk)) { + guarantee(NULL == (void *) + check_abstract_with_unique_concrete_subtype(ctxk, conck), + "verify dep."); + } + } +#endif //PRODUCT + return conck; + } +} + +// Test the assertion that the k[12] are the only concrete subtypes of ctxk, +// except possibly for further subtypes of k[12] themselves. +// The context type must be abstract. The types k1 and k2 are themselves +// allowed to have further concrete subtypes. +klassOop Dependencies::check_abstract_with_exclusive_concrete_subtypes( + klassOop ctxk, + klassOop k1, + klassOop k2, + DepChange* changes) { + ClassHierarchyWalker wf; + wf.add_participant(k1); + wf.add_participant(k2); + return wf.find_witness_subtype(ctxk, changes); +} + +// Search ctxk for concrete implementations. If there are klen or fewer, +// pack them into the given array and return the number. +// Otherwise, return -1, meaning the given array would overflow. +// (Note that a return of 0 means there are exactly no concrete subtypes.) +// In this search, if ctxk is concrete, it will be reported alone. +// For any type CC reported, no proper subtypes of CC will be reported. +int Dependencies::find_exclusive_concrete_subtypes(klassOop ctxk, + int klen, + klassOop karray[]) { + ClassHierarchyWalker wf; + wf.record_witnesses(klen); + klassOop wit = wf.find_witness_subtype(ctxk); + if (wit != NULL) return -1; // Too many witnesses. + int num = wf.num_participants(); + assert(num <= klen, "oob"); + // Pack the result array with the good news. + for (int i = 0; i < num; i++) + karray[i] = wf.participant(i); +#ifndef PRODUCT + // Make sure the dependency mechanism will pass this discovery: + if (VerifyDependencies) { + // Turn off dependency tracing while actually testing deps. + FlagSetting fs(TraceDependencies, false); + switch (Dependencies::is_concrete_klass(ctxk)? -1: num) { + case -1: // ctxk was itself concrete + guarantee(num == 1 && karray[0] == ctxk, "verify dep."); + break; + case 0: + guarantee(NULL == (void *)check_abstract_with_no_concrete_subtype(ctxk), + "verify dep."); + break; + case 1: + guarantee(NULL == (void *) + check_abstract_with_unique_concrete_subtype(ctxk, karray[0]), + "verify dep."); + break; + case 2: + guarantee(NULL == (void *) + check_abstract_with_exclusive_concrete_subtypes(ctxk, + karray[0], + karray[1]), + "verify dep."); + break; + default: + ShouldNotReachHere(); // klen > 2 yet supported + } + } +#endif //PRODUCT + return num; +} + +// If a class (or interface) has a unique concrete method uniqm, return NULL. +// Otherwise, return a class that contains an interfering method. +klassOop Dependencies::check_unique_concrete_method(klassOop ctxk, methodOop uniqm, + DepChange* changes) { + // Here is a missing optimization: If uniqm->is_final(), + // we don't really need to search beneath it for overrides. + // This is probably not important, since we don't use dependencies + // to track final methods. (They can't be "definalized".) + ClassHierarchyWalker wf(uniqm->method_holder(), uniqm); + return wf.find_witness_definer(ctxk, changes); +} + +// Find the set of all non-abstract methods under ctxk that match m. +// (The method m must be defined or inherited in ctxk.) +// Include m itself in the set, unless it is abstract. +// If this set has exactly one element, return that element. +methodOop Dependencies::find_unique_concrete_method(klassOop ctxk, methodOop m) { + ClassHierarchyWalker wf(m); + assert(wf.check_method_context(ctxk, m), "proper context"); + wf.record_witnesses(1); + klassOop wit = wf.find_witness_definer(ctxk); + if (wit != NULL) return NULL; // Too many witnesses. + methodOop fm = wf.found_method(0); // Will be NULL if num_parts == 0. + if (Dependencies::is_concrete_method(m)) { + if (fm == NULL) { + // It turns out that m was always the only implementation. + fm = m; + } else if (fm != m) { + // Two conflicting implementations after all. + // (This can happen if m is inherited into ctxk and fm overrides it.) + return NULL; + } + } +#ifndef PRODUCT + // Make sure the dependency mechanism will pass this discovery: + if (VerifyDependencies && fm != NULL) { + guarantee(NULL == (void *)check_unique_concrete_method(ctxk, fm), + "verify dep."); + } +#endif //PRODUCT + return fm; +} + +klassOop Dependencies::check_exclusive_concrete_methods(klassOop ctxk, + methodOop m1, + methodOop m2, + DepChange* changes) { + ClassHierarchyWalker wf(m1); + wf.add_participant(m1->method_holder()); + wf.add_participant(m2->method_holder()); + return wf.find_witness_definer(ctxk, changes); +} + +// Find the set of all non-abstract methods under ctxk that match m[0]. +// (The method m[0] must be defined or inherited in ctxk.) +// Include m itself in the set, unless it is abstract. +// Fill the given array m[0..(mlen-1)] with this set, and return the length. +// (The length may be zero if no concrete methods are found anywhere.) +// If there are too many concrete methods to fit in marray, return -1. +int Dependencies::find_exclusive_concrete_methods(klassOop ctxk, + int mlen, + methodOop marray[]) { + methodOop m0 = marray[0]; + ClassHierarchyWalker wf(m0); + assert(wf.check_method_context(ctxk, m0), "proper context"); + wf.record_witnesses(mlen); + bool participants_hide_witnesses = true; + klassOop wit = wf.find_witness_definer(ctxk); + if (wit != NULL) return -1; // Too many witnesses. + int num = wf.num_participants(); + assert(num <= mlen, "oob"); + // Keep track of whether m is also part of the result set. + int mfill = 0; + assert(marray[mfill] == m0, "sanity"); + if (Dependencies::is_concrete_method(m0)) + mfill++; // keep m0 as marray[0], the first result + for (int i = 0; i < num; i++) { + methodOop fm = wf.found_method(i); + if (fm == m0) continue; // Already put this guy in the list. + if (mfill == mlen) { + return -1; // Oops. Too many methods after all! + } + marray[mfill++] = fm; + } +#ifndef PRODUCT + // Make sure the dependency mechanism will pass this discovery: + if (VerifyDependencies) { + // Turn off dependency tracing while actually testing deps. + FlagSetting fs(TraceDependencies, false); + switch (mfill) { + case 1: + guarantee(NULL == (void *)check_unique_concrete_method(ctxk, marray[0]), + "verify dep."); + break; + case 2: + guarantee(NULL == (void *) + check_exclusive_concrete_methods(ctxk, marray[0], marray[1]), + "verify dep."); + break; + default: + ShouldNotReachHere(); // mlen > 2 yet supported + } + } +#endif //PRODUCT + return mfill; +} + + +klassOop Dependencies::check_has_no_finalizable_subclasses(klassOop ctxk, DepChange* changes) { + Klass* search_at = ctxk->klass_part(); + if (changes != NULL) + search_at = changes->new_type()->klass_part(); // just look at the new bit + Klass* result = find_finalizable_subclass(search_at); + if (result == NULL) { + return NULL; + } + return result->as_klassOop(); +} + + +klassOop Dependencies::DepStream::check_dependency_impl(DepChange* changes) { + assert_locked_or_safepoint(Compile_lock); + + klassOop witness = NULL; + switch (type()) { + case evol_method: + witness = check_evol_method(method_argument(0)); + break; + case leaf_type: + witness = check_leaf_type(context_type()); + break; + case abstract_with_unique_concrete_subtype: + witness = check_abstract_with_unique_concrete_subtype(context_type(), + type_argument(1), + changes); + break; + case abstract_with_no_concrete_subtype: + witness = check_abstract_with_no_concrete_subtype(context_type(), + changes); + break; + case concrete_with_no_concrete_subtype: + witness = check_concrete_with_no_concrete_subtype(context_type(), + changes); + break; + case unique_concrete_method: + witness = check_unique_concrete_method(context_type(), + method_argument(1), + changes); + break; + case abstract_with_exclusive_concrete_subtypes_2: + witness = check_abstract_with_exclusive_concrete_subtypes(context_type(), + type_argument(1), + type_argument(2), + changes); + break; + case exclusive_concrete_methods_2: + witness = check_exclusive_concrete_methods(context_type(), + method_argument(1), + method_argument(2), + changes); + break; + case no_finalizable_subclasses: + witness = check_has_no_finalizable_subclasses(context_type(), + changes); + break; + default: + witness = NULL; + ShouldNotReachHere(); + break; + } + if (witness != NULL) { + if (TraceDependencies) { + print_dependency(witness, /*verbose=*/ true); + } + // The following is a no-op unless logging is enabled: + log_dependency(witness); + } + return witness; +} + + +klassOop Dependencies::DepStream::spot_check_dependency_at(DepChange& changes) { + if (!changes.involves_context(context_type())) + // irrelevant dependency; skip it + return NULL; + + return check_dependency_impl(&changes); +} + + +void DepChange::initialize() { + // entire transaction must be under this lock: + assert_lock_strong(Compile_lock); + + // Mark all dependee and all its superclasses + // Mark transitive interfaces + for (ContextStream str(*this); str.next(); ) { + klassOop d = str.klass(); + assert(!instanceKlass::cast(d)->is_marked_dependent(), "checking"); + instanceKlass::cast(d)->set_is_marked_dependent(true); + } +} + +DepChange::~DepChange() { + // Unmark all dependee and all its superclasses + // Unmark transitive interfaces + for (ContextStream str(*this); str.next(); ) { + klassOop d = str.klass(); + instanceKlass::cast(d)->set_is_marked_dependent(false); + } +} + +bool DepChange::involves_context(klassOop k) { + if (k == NULL || !Klass::cast(k)->oop_is_instance()) { + return false; + } + instanceKlass* ik = instanceKlass::cast(k); + bool is_contained = ik->is_marked_dependent(); + assert(is_contained == Klass::cast(new_type())->is_subtype_of(k), + "correct marking of potential context types"); + return is_contained; +} + +bool DepChange::ContextStream::next() { + switch (_change_type) { + case Start_Klass: // initial state; _klass is the new type + _ti_base = instanceKlass::cast(_klass)->transitive_interfaces(); + _ti_index = 0; + _change_type = Change_new_type; + return true; + case Change_new_type: + // fall through: + _change_type = Change_new_sub; + case Change_new_sub: + _klass = instanceKlass::cast(_klass)->super(); + if (_klass != NULL) { + return true; + } + // else set up _ti_limit and fall through: + _ti_limit = (_ti_base == NULL) ? 0 : _ti_base->length(); + _change_type = Change_new_impl; + case Change_new_impl: + if (_ti_index < _ti_limit) { + _klass = klassOop( _ti_base->obj_at(_ti_index++) ); + return true; + } + // fall through: + _change_type = NO_CHANGE; // iterator is exhausted + case NO_CHANGE: + break; + default: + ShouldNotReachHere(); + } + return false; +} + +void DepChange::print() { + int nsup = 0, nint = 0; + for (ContextStream str(*this); str.next(); ) { + klassOop k = str.klass(); + switch (str._change_type) { + case Change_new_type: + tty->print_cr(" dependee = %s", instanceKlass::cast(k)->external_name()); + break; + case Change_new_sub: + if (!WizardMode) + ++nsup; + else tty->print_cr(" context super = %s", instanceKlass::cast(k)->external_name()); + break; + case Change_new_impl: + if (!WizardMode) + ++nint; + else tty->print_cr(" context interface = %s", instanceKlass::cast(k)->external_name()); + break; + } + } + if (nsup + nint != 0) { + tty->print_cr(" context supers = %d, interfaces = %d", nsup, nint); + } +} + +#ifndef PRODUCT +void Dependencies::print_statistics() { + if (deps_find_witness_print != 0) { + // Call one final time, to flush out the data. + deps_find_witness_print = -1; + count_find_witness_calls(); + } +} +#endif diff --git a/hotspot/src/share/vm/code/dependencies.hpp b/hotspot/src/share/vm/code/dependencies.hpp new file mode 100644 index 00000000000..faf98b36537 --- /dev/null +++ b/hotspot/src/share/vm/code/dependencies.hpp @@ -0,0 +1,550 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//** Dependencies represent assertions (approximate invariants) within +// the class hierarchy. An example is an assertion that a given +// method is not overridden; another example is that a type has only +// one concrete subtype. Compiled code which relies on such +// assertions must be discarded if they are overturned by changes in +// the class hierarchy. We can think of these assertions as +// approximate invariants, because we expect them to be overturned +// very infrequently. We are willing to perform expensive recovery +// operations when they are overturned. The benefit, of course, is +// performing optimistic optimizations (!) on the object code. +// +// Changes in the class hierarchy due to dynamic linking or +// class evolution can violate dependencies. There is enough +// indexing between classes and nmethods to make dependency +// checking reasonably efficient. + +class ciEnv; +class nmethod; +class OopRecorder; +class xmlStream; +class CompileLog; +class DepChange; +class No_Safepoint_Verifier; + +class Dependencies: public ResourceObj { + public: + // Note: In the comments on dependency types, most uses of the terms + // subtype and supertype are used in a "non-strict" or "inclusive" + // sense, and are starred to remind the reader of this fact. + // Strict uses of the terms use the word "proper". + // + // Specifically, every class is its own subtype* and supertype*. + // (This trick is easier than continually saying things like "Y is a + // subtype of X or X itself".) + // + // Sometimes we write X > Y to mean X is a proper supertype of Y. + // The notation X > {Y, Z} means X has proper subtypes Y, Z. + // The notation X.m > Y means that Y inherits m from X, while + // X.m > Y.m means Y overrides X.m. A star denotes abstractness, + // as *I > A, meaning (abstract) interface I is a super type of A, + // or A.*m > B.m, meaning B.m implements abstract method A.m. + // + // In this module, the terms "subtype" and "supertype" refer to + // Java-level reference type conversions, as detected by + // "instanceof" and performed by "checkcast" operations. The method + // Klass::is_subtype_of tests these relations. Note that "subtype" + // is richer than "subclass" (as tested by Klass::is_subclass_of), + // since it takes account of relations involving interface and array + // types. + // + // To avoid needless complexity, dependencies involving array types + // are not accepted. If you need to make an assertion about an + // array type, make the assertion about its corresponding element + // types. Any assertion that might change about an array type can + // be converted to an assertion about its element type. + // + // Most dependencies are evaluated over a "context type" CX, which + // stands for the set Subtypes(CX) of every Java type that is a subtype* + // of CX. When the system loads a new class or interface N, it is + // responsible for re-evaluating changed dependencies whose context + // type now includes N, that is, all super types of N. + // + enum DepType { + end_marker = 0, + + // An 'evol' dependency simply notes that the contents of the + // method were used. If it evolves (is replaced), the nmethod + // must be recompiled. No other dependencies are implied. + evol_method, + FIRST_TYPE = evol_method, + + // A context type CX is a leaf it if has no proper subtype. + leaf_type, + + // An abstract class CX has exactly one concrete subtype CC. + abstract_with_unique_concrete_subtype, + + // The type CX is purely abstract, with no concrete subtype* at all. + abstract_with_no_concrete_subtype, + + // The concrete CX is free of concrete proper subtypes. + concrete_with_no_concrete_subtype, + + // Given a method M1 and a context class CX, the set MM(CX, M1) of + // "concrete matching methods" in CX of M1 is the set of every + // concrete M2 for which it is possible to create an invokevirtual + // or invokeinterface call site that can reach either M1 or M2. + // That is, M1 and M2 share a name, signature, and vtable index. + // We wish to notice when the set MM(CX, M1) is just {M1}, or + // perhaps a set of two {M1,M2}, and issue dependencies on this. + + // The set MM(CX, M1) can be computed by starting with any matching + // concrete M2 that is inherited into CX, and then walking the + // subtypes* of CX looking for concrete definitions. + + // The parameters to this dependency are the method M1 and the + // context class CX. M1 must be either inherited in CX or defined + // in a subtype* of CX. It asserts that MM(CX, M1) is no greater + // than {M1}. + unique_concrete_method, // one unique concrete method under CX + + // An "exclusive" assertion concerns two methods or subtypes, and + // declares that there are at most two (or perhaps later N>2) + // specific items that jointly satisfy the restriction. + // We list all items explicitly rather than just giving their + // count, for robustness in the face of complex schema changes. + + // A context class CX (which may be either abstract or concrete) + // has two exclusive concrete subtypes* C1, C2 if every concrete + // subtype* of CX is either C1 or C2. Note that if neither C1 or C2 + // are equal to CX, then CX itself must be abstract. But it is + // also possible (for example) that C1 is CX (a concrete class) + // and C2 is a proper subtype of C1. + abstract_with_exclusive_concrete_subtypes_2, + + // This dependency asserts that MM(CX, M1) is no greater than {M1,M2}. + exclusive_concrete_methods_2, + + // This dependency asserts that no instances of class or it's + // subclasses require finalization registration. + no_finalizable_subclasses, + + TYPE_LIMIT + }; + enum { + LG2_TYPE_LIMIT = 4, // assert(TYPE_LIMIT <= (1<* _dep_seen; // (seen[h->ident] & (1<* _deps[TYPE_LIMIT]; + + static const char* _dep_name[TYPE_LIMIT]; + static int _dep_args[TYPE_LIMIT]; + + static bool dept_in_mask(DepType dept, int mask) { + return (int)dept >= 0 && dept < TYPE_LIMIT && ((1<ident(); + assert(_dep_seen != NULL, "deps must be writable"); + int seen = _dep_seen->at_grow(x_id, 0); + _dep_seen->at_put(x_id, seen | (1<* deps, + int ctxk_i, ciKlass* ctxk); + + void sort_all_deps(); + size_t estimate_size_in_bytes(); + + // Initialize _deps, etc. + void initialize(ciEnv* env); + + // State for making a new set of dependencies: + OopRecorder* _oop_recorder; + + // Logging support + CompileLog* _log; + + address _content_bytes; // everything but the oop references, encoded + size_t _size_in_bytes; + + public: + // Make a new empty dependencies set. + Dependencies(ciEnv* env) { + initialize(env); + } + + private: + // Check for a valid context type. + // Enforce the restriction against array types. + static void check_ctxk(ciKlass* ctxk) { + assert(ctxk->is_instance_klass(), "java types only"); + } + static void check_ctxk_concrete(ciKlass* ctxk) { + assert(is_concrete_klass(ctxk->as_instance_klass()), "must be concrete"); + } + static void check_ctxk_abstract(ciKlass* ctxk) { + check_ctxk(ctxk); + assert(!is_concrete_klass(ctxk->as_instance_klass()), "must be abstract"); + } + + void assert_common_1(DepType dept, ciObject* x); + void assert_common_2(DepType dept, ciKlass* ctxk, ciObject* x); + void assert_common_3(DepType dept, ciKlass* ctxk, ciObject* x, ciObject* x2); + + public: + // Adding assertions to a new dependency set at compile time: + void assert_evol_method(ciMethod* m); + void assert_leaf_type(ciKlass* ctxk); + void assert_abstract_with_unique_concrete_subtype(ciKlass* ctxk, ciKlass* conck); + void assert_abstract_with_no_concrete_subtype(ciKlass* ctxk); + void assert_concrete_with_no_concrete_subtype(ciKlass* ctxk); + void assert_unique_concrete_method(ciKlass* ctxk, ciMethod* uniqm); + void assert_abstract_with_exclusive_concrete_subtypes(ciKlass* ctxk, ciKlass* k1, ciKlass* k2); + void assert_exclusive_concrete_methods(ciKlass* ctxk, ciMethod* m1, ciMethod* m2); + void assert_has_no_finalizable_subclasses(ciKlass* ctxk); + + // Define whether a given method or type is concrete. + // These methods define the term "concrete" as used in this module. + // For this module, an "abstract" class is one which is non-concrete. + // + // Future optimizations may allow some classes to remain + // non-concrete until their first instantiation, and allow some + // methods to remain non-concrete until their first invocation. + // In that case, there would be a middle ground between concrete + // and abstract (as defined by the Java language and VM). + static bool is_concrete_klass(klassOop k); // k is instantiable + static bool is_concrete_method(methodOop m); // m is invocable + static Klass* find_finalizable_subclass(Klass* k); + + // These versions of the concreteness queries work through the CI. + // The CI versions are allowed to skew sometimes from the VM + // (oop-based) versions. The cost of such a difference is a + // (safely) aborted compilation, or a deoptimization, or a missed + // optimization opportunity. + // + // In order to prevent spurious assertions, query results must + // remain stable within any single ciEnv instance. (I.e., they must + // not go back into the VM to get their value; they must cache the + // bit in the CI, either eagerly or lazily.) + static bool is_concrete_klass(ciInstanceKlass* k); // k appears instantiable + static bool is_concrete_method(ciMethod* m); // m appears invocable + static bool has_finalizable_subclass(ciInstanceKlass* k); + + // As a general rule, it is OK to compile under the assumption that + // a given type or method is concrete, even if it at some future + // point becomes abstract. So dependency checking is one-sided, in + // that it permits supposedly concrete classes or methods to turn up + // as really abstract. (This shouldn't happen, except during class + // evolution, but that's the logic of the checking.) However, if a + // supposedly abstract class or method suddenly becomes concrete, a + // dependency on it must fail. + + // Checking old assertions at run-time (in the VM only): + static klassOop check_evol_method(methodOop m); + static klassOop check_leaf_type(klassOop ctxk); + static klassOop check_abstract_with_unique_concrete_subtype(klassOop ctxk, klassOop conck, + DepChange* changes = NULL); + static klassOop check_abstract_with_no_concrete_subtype(klassOop ctxk, + DepChange* changes = NULL); + static klassOop check_concrete_with_no_concrete_subtype(klassOop ctxk, + DepChange* changes = NULL); + static klassOop check_unique_concrete_method(klassOop ctxk, methodOop uniqm, + DepChange* changes = NULL); + static klassOop check_abstract_with_exclusive_concrete_subtypes(klassOop ctxk, klassOop k1, klassOop k2, + DepChange* changes = NULL); + static klassOop check_exclusive_concrete_methods(klassOop ctxk, methodOop m1, methodOop m2, + DepChange* changes = NULL); + static klassOop check_has_no_finalizable_subclasses(klassOop ctxk, + DepChange* changes = NULL); + // A returned klassOop is NULL if the dependency assertion is still + // valid. A non-NULL klassOop is a 'witness' to the assertion + // failure, a point in the class hierarchy where the assertion has + // been proven false. For example, if check_leaf_type returns + // non-NULL, the value is a subtype of the supposed leaf type. This + // witness value may be useful for logging the dependency failure. + // Note that, when a dependency fails, there may be several possible + // witnesses to the failure. The value returned from the check_foo + // method is chosen arbitrarily. + + // The 'changes' value, if non-null, requests a limited spot-check + // near the indicated recent changes in the class hierarchy. + // It is used by DepStream::spot_check_dependency_at. + + // Detecting possible new assertions: + static klassOop find_unique_concrete_subtype(klassOop ctxk); + static methodOop find_unique_concrete_method(klassOop ctxk, methodOop m); + static int find_exclusive_concrete_subtypes(klassOop ctxk, int klen, klassOop k[]); + static int find_exclusive_concrete_methods(klassOop ctxk, int mlen, methodOop m[]); + + // Create the encoding which will be stored in an nmethod. + void encode_content_bytes(); + + address content_bytes() { + assert(_content_bytes != NULL, "encode it first"); + return _content_bytes; + } + size_t size_in_bytes() { + assert(_content_bytes != NULL, "encode it first"); + return _size_in_bytes; + } + + OopRecorder* oop_recorder() { return _oop_recorder; } + CompileLog* log() { return _log; } + + void copy_to(nmethod* nm); + + void log_all_dependencies(); + void log_dependency(DepType dept, int nargs, ciObject* args[]) { + write_dependency_to(log(), dept, nargs, args); + } + void log_dependency(DepType dept, + ciObject* x0, + ciObject* x1 = NULL, + ciObject* x2 = NULL) { + if (log() == NULL) return; + ciObject* args[max_arg_count]; + args[0] = x0; + args[1] = x1; + args[2] = x2; + assert(2 < max_arg_count, ""); + log_dependency(dept, dep_args(dept), args); + } + + static void write_dependency_to(CompileLog* log, + DepType dept, + int nargs, ciObject* args[], + klassOop witness = NULL); + static void write_dependency_to(CompileLog* log, + DepType dept, + int nargs, oop args[], + klassOop witness = NULL); + static void write_dependency_to(xmlStream* xtty, + DepType dept, + int nargs, oop args[], + klassOop witness = NULL); + static void print_dependency(DepType dept, + int nargs, oop args[], + klassOop witness = NULL); + + private: + // helper for encoding common context types as zero: + static ciKlass* ctxk_encoded_as_null(DepType dept, ciObject* x); + + static klassOop ctxk_encoded_as_null(DepType dept, oop x); + + public: + // Use this to iterate over an nmethod's dependency set. + // Works on new and old dependency sets. + // Usage: + // + // ; + // Dependencies::DepType dept; + // for (Dependencies::DepStream deps(nm); deps.next(); ) { + // ... + // } + // + // The caller must be in the VM, since oops are not wrapped in handles. + class DepStream { + private: + nmethod* _code; // null if in a compiler thread + Dependencies* _deps; // null if not in a compiler thread + CompressedReadStream _bytes; +#ifdef ASSERT + size_t _byte_limit; +#endif + + // iteration variables: + DepType _type; + int _xi[max_arg_count+1]; + + void initial_asserts(size_t byte_limit) NOT_DEBUG({}); + + inline oop recorded_oop_at(int i); + // => _code? _code->oop_at(i): *_deps->_oop_recorder->handle_at(i) + + klassOop check_dependency_impl(DepChange* changes); + + public: + DepStream(Dependencies* deps) + : _deps(deps), + _code(NULL), + _bytes(deps->content_bytes()) + { + initial_asserts(deps->size_in_bytes()); + } + DepStream(nmethod* code) + : _deps(NULL), + _code(code), + _bytes(code->dependencies_begin()) + { + initial_asserts(code->dependencies_size()); + } + + bool next(); + + DepType type() { return _type; } + int argument_count() { return dep_args(type()); } + int argument_index(int i) { assert(0 <= i && i < argument_count(), "oob"); + return _xi[i]; } + oop argument(int i); // => recorded_oop_at(argument_index(i)) + klassOop context_type(); + + methodOop method_argument(int i) { + oop x = argument(i); + assert(x->is_method(), "type"); + return (methodOop) x; + } + klassOop type_argument(int i) { + oop x = argument(i); + assert(x->is_klass(), "type"); + return (klassOop) x; + } + + // The point of the whole exercise: Is this dep is still OK? + klassOop check_dependency() { + return check_dependency_impl(NULL); + } + // A lighter version: Checks only around recent changes in a class + // hierarchy. (See Universe::flush_dependents_on.) + klassOop spot_check_dependency_at(DepChange& changes); + + // Log the current dependency to xtty or compilation log. + void log_dependency(klassOop witness = NULL); + + // Print the current dependency to tty. + void print_dependency(klassOop witness = NULL, bool verbose = false); + }; + friend class Dependencies::DepStream; + + static void print_statistics() PRODUCT_RETURN; +}; + +// A class hierarchy change coming through the VM (under the Compile_lock). +// The change is structured as a single new type with any number of supers +// and implemented interface types. Other than the new type, any of the +// super types can be context types for a relevant dependency, which the +// new type could invalidate. +class DepChange : public StackObj { + private: + enum ChangeType { + NO_CHANGE = 0, // an uninvolved klass + Change_new_type, // a newly loaded type + Change_new_sub, // a super with a new subtype + Change_new_impl, // an interface with a new implementation + CHANGE_LIMIT, + Start_Klass = CHANGE_LIMIT // internal indicator for ContextStream + }; + + // each change set is rooted in exactly one new type (at present): + KlassHandle _new_type; + + void initialize(); + + public: + // notes the new type, marks it and all its super-types + DepChange(KlassHandle new_type) + : _new_type(new_type) + { + initialize(); + } + + // cleans up the marks + ~DepChange(); + + klassOop new_type() { return _new_type(); } + + // involves_context(k) is true if k is new_type or any of the super types + bool involves_context(klassOop k); + + // Usage: + // for (DepChange::ContextStream str(changes); str.next(); ) { + // klassOop k = str.klass(); + // switch (str.change_type()) { + // ... + // } + // } + class ContextStream : public StackObj { + private: + DepChange& _changes; + friend class DepChange; + + // iteration variables: + ChangeType _change_type; + klassOop _klass; + objArrayOop _ti_base; // i.e., transitive_interfaces + int _ti_index; + int _ti_limit; + + // start at the beginning: + void start() { + klassOop new_type = _changes.new_type(); + _change_type = (new_type == NULL ? NO_CHANGE: Start_Klass); + _klass = new_type; + _ti_base = NULL; + _ti_index = 0; + _ti_limit = 0; + } + + ContextStream(DepChange& changes) + : _changes(changes) + { start(); } + + public: + ContextStream(DepChange& changes, No_Safepoint_Verifier& nsv) + : _changes(changes) + // the nsv argument makes it safe to hold oops like _klass + { start(); } + + bool next(); + + klassOop klass() { return _klass; } + }; + friend class DepChange::ContextStream; + + void print(); +}; diff --git a/hotspot/src/share/vm/code/exceptionHandlerTable.cpp b/hotspot/src/share/vm/code/exceptionHandlerTable.cpp new file mode 100644 index 00000000000..19d95e9cf1d --- /dev/null +++ b/hotspot/src/share/vm/code/exceptionHandlerTable.cpp @@ -0,0 +1,226 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_exceptionHandlerTable.cpp.incl" + +void ExceptionHandlerTable::add_entry(HandlerTableEntry entry) { + _nesting.check(); + if (_length >= _size) { + // not enough space => grow the table (amortized growth, double its size) + guarantee(_size > 0, "no space allocated => cannot grow the table since it is part of nmethod"); + int new_size = _size * 2; + _table = REALLOC_RESOURCE_ARRAY(HandlerTableEntry, _table, _size, new_size); + _size = new_size; + } + assert(_length < _size, "sanity check"); + _table[_length++] = entry; +} + + +HandlerTableEntry* ExceptionHandlerTable::subtable_for(int catch_pco) const { + int i = 0; + while (i < _length) { + HandlerTableEntry* t = _table + i; + if (t->pco() == catch_pco) { + // found subtable matching the catch_pco + return t; + } else { + // advance to next subtable + i += t->len() + 1; // +1 for header + } + } + return NULL; +} + + +ExceptionHandlerTable::ExceptionHandlerTable(int initial_size) { + guarantee(initial_size > 0, "initial size must be > 0"); + _table = NEW_RESOURCE_ARRAY(HandlerTableEntry, initial_size); + _length = 0; + _size = initial_size; +} + + +ExceptionHandlerTable::ExceptionHandlerTable(const nmethod* nm) { + _table = (HandlerTableEntry*)nm->handler_table_begin(); + _length = nm->handler_table_size() / sizeof(HandlerTableEntry); + _size = 0; // no space allocated by ExeptionHandlerTable! +} + + +void ExceptionHandlerTable::add_subtable( + int catch_pco, + GrowableArray* handler_bcis, + GrowableArray* scope_depths_from_top_scope, + GrowableArray* handler_pcos +) { + assert(subtable_for(catch_pco) == NULL, "catch handlers for this catch_pco added twice"); + assert(handler_bcis->length() == handler_pcos->length(), "bci & pc table have different length"); + assert(scope_depths_from_top_scope == NULL || handler_bcis->length() == scope_depths_from_top_scope->length(), "bci & scope_depths table have different length"); + if (handler_bcis->length() > 0) { + // add subtable header + add_entry(HandlerTableEntry(handler_bcis->length(), catch_pco, 0)); + // add individual entries + for (int i = 0; i < handler_bcis->length(); i++) { + intptr_t scope_depth = 0; + if (scope_depths_from_top_scope != NULL) { + scope_depth = scope_depths_from_top_scope->at(i); + } + add_entry(HandlerTableEntry(handler_bcis->at(i), handler_pcos->at(i), scope_depth)); + assert(entry_for(catch_pco, handler_bcis->at(i), scope_depth)->pco() == handler_pcos->at(i), "entry not added correctly (1)"); + assert(entry_for(catch_pco, handler_bcis->at(i), scope_depth)->scope_depth() == scope_depth, "entry not added correctly (2)"); + } + } +} + + +void ExceptionHandlerTable::copy_to(nmethod* nm) { + assert(size_in_bytes() == nm->handler_table_size(), "size of space allocated in nmethod incorrect"); + memmove(nm->handler_table_begin(), _table, size_in_bytes()); +} + + +HandlerTableEntry* ExceptionHandlerTable::entry_for(int catch_pco, int handler_bci, int scope_depth) const { + HandlerTableEntry* t = subtable_for(catch_pco); + if (t != NULL) { + int l = t->len(); + while (l-- > 0) { + t++; + if (t->bci() == handler_bci && t->scope_depth() == scope_depth) return t; + } + } + return NULL; +} + + +void ExceptionHandlerTable::print_subtable(HandlerTableEntry* t) const { + int l = t->len(); + tty->print_cr("catch_pco = %d (%d entries)", t->pco(), l); + while (l-- > 0) { + t++; + tty->print_cr(" bci %d at scope depth %d -> pco %d", t->bci(), t->scope_depth(), t->pco()); + } +} + + +void ExceptionHandlerTable::print() const { + tty->print_cr("ExceptionHandlerTable (size = %d bytes)", size_in_bytes()); + int i = 0; + while (i < _length) { + HandlerTableEntry* t = _table + i; + print_subtable(t); + // advance to next subtable + i += t->len() + 1; // +1 for header + } +} + +void ExceptionHandlerTable::print_subtable_for(int catch_pco) const { + HandlerTableEntry* subtable = subtable_for(catch_pco); + + if( subtable != NULL ) { print_subtable( subtable ); } +} + +// ---------------------------------------------------------------------------- +// Implicit null exception tables. Maps an exception PC offset to a +// continuation PC offset. During construction it's a variable sized +// array with a max size and current length. When stored inside an +// nmethod a zero length table takes no space. This is detected by +// nul_chk_table_size() == 0. Otherwise the table has a length word +// followed by pairs of . +void ImplicitExceptionTable::set_size( uint size ) { + _size = size; + _data = NEW_RESOURCE_ARRAY(implicit_null_entry, (size*2)); + _len = 0; +} + +void ImplicitExceptionTable::append( uint exec_off, uint cont_off ) { + assert( (sizeof(implicit_null_entry) >= 4) || (exec_off < 65535), "" ); + assert( (sizeof(implicit_null_entry) >= 4) || (cont_off < 65535), "" ); + uint l = len(); + if (l == _size) { + uint old_size_in_elements = _size*2; + if (_size == 0) _size = 4; + _size *= 2; + uint new_size_in_elements = _size*2; + _data = REALLOC_RESOURCE_ARRAY(uint, _data, old_size_in_elements, new_size_in_elements); + } + *(adr(l) ) = exec_off; + *(adr(l)+1) = cont_off; + _len = l+1; +}; + +uint ImplicitExceptionTable::at( uint exec_off ) const { + uint l = len(); + for( uint i=0; iprint("{"); + for( uint i=0; iprint("< "INTPTR_FORMAT", "INTPTR_FORMAT" > ",base + *adr(i), base + *(adr(i)+1)); + tty->print_cr("}"); +} + +ImplicitExceptionTable::ImplicitExceptionTable(const nmethod* nm) { + if (nm->nul_chk_table_size() == 0) { + _len = 0; + _data = NULL; + } else { + // the first word is the length if non-zero, so read it out and + // skip to the next word to get the table. + _data = (implicit_null_entry*)nm->nul_chk_table_begin(); + _len = _data[0]; + _data++; + } + _size = len(); + assert(size_in_bytes() <= nm->nul_chk_table_size(), "size of space allocated in nmethod incorrect"); +} + +void ImplicitExceptionTable::copy_to( nmethod* nm ) { + assert(size_in_bytes() <= nm->nul_chk_table_size(), "size of space allocated in nmethod incorrect"); + if (len() != 0) { + implicit_null_entry* nmdata = (implicit_null_entry*)nm->nul_chk_table_begin(); + // store the length in the first uint + nmdata[0] = _len; + nmdata++; + // copy the table after the length + memmove( nmdata, _data, 2 * len() * sizeof(implicit_null_entry)); + } else { + // zero length table takes zero bytes + assert(size_in_bytes() == 0, "bad size"); + assert(nm->nul_chk_table_size() == 0, "bad size"); + } +} + +void ImplicitExceptionTable::verify(nmethod *nm) const { + for (uint i = 0; i < len(); i++) { + if ((*adr(i) > (unsigned int)nm->code_size()) || + (*(adr(i)+1) > (unsigned int)nm->code_size())) + fatal1("Invalid offset in ImplicitExceptionTable at %lx", _data); + } +} diff --git a/hotspot/src/share/vm/code/exceptionHandlerTable.hpp b/hotspot/src/share/vm/code/exceptionHandlerTable.hpp new file mode 100644 index 00000000000..6a9601fd023 --- /dev/null +++ b/hotspot/src/share/vm/code/exceptionHandlerTable.hpp @@ -0,0 +1,156 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A HandlerTableEntry describes an individual entry of a subtable +// of ExceptionHandlerTable. An entry consists of a pair(bci, pco), +// where bci is the exception handler bci, and pco is the pc offset +// relative to the nmethod code start for the compiled exception +// handler corresponding to the (interpreted) exception handler +// starting at bci. +// +// The first HandlerTableEntry of each subtable holds the length +// and catch_pco for the subtable (the length is the number of +// subtable entries w/o header). + +class HandlerTableEntry { + private: + int _bci; + int _pco; + int _scope_depth; + + public: + HandlerTableEntry(int bci, int pco, int scope_depth) { + assert( 0 <= pco, "pco must be positive"); + assert( 0 <= scope_depth, "scope_depth must be positive"); + _bci = bci; + _pco = pco; + _scope_depth = scope_depth; + } + + int len() const { return _bci; } // for entry at subtable begin + int bci() const { return _bci; } + int pco() const { return _pco; } + int scope_depth() const { return _scope_depth; } +}; + + +// An ExceptionHandlerTable is an abstraction over a list of subtables +// of exception handlers for CatchNodes. Each subtable has a one-entry +// header holding length and catch_pco of the subtable, followed +// by 'length' entries for each exception handler that can be reached +// from the corresponding CatchNode. The catch_pco is the pc offset of +// the CatchNode in the corresponding nmethod. Empty subtables are dis- +// carded. +// +// Structure of the table: +// +// table = { subtable }. +// subtable = header entry { entry }. +// header = a pair (number of subtable entries, catch pc offset, [unused]) +// entry = a pair (handler bci, handler pc offset, scope depth) +// +// An ExceptionHandlerTable can be created from scratch, in which case +// it is possible to add subtables. It can also be created from an +// nmethod (for lookup purposes) in which case the table cannot be +// modified. + +class nmethod; +class ExceptionHandlerTable VALUE_OBJ_CLASS_SPEC { + private: + HandlerTableEntry* _table; // the table + int _length; // the current length of the table + int _size; // the number of allocated entries + ReallocMark _nesting; // assertion check for reallocations + + // add the entry & grow the table if needed + void add_entry(HandlerTableEntry entry); + HandlerTableEntry* subtable_for(int catch_pco) const; + + public: + // (compile-time) construction within compiler + ExceptionHandlerTable(int initial_size = 8); + + // (run-time) construction from nmethod + ExceptionHandlerTable(const nmethod* nm); + + // (compile-time) add entries + void add_subtable( + int catch_pco, // the pc offset for the CatchNode + GrowableArray* handler_bcis, // the exception handler entry point bcis + GrowableArray* scope_depths_from_top_scope, + // if representing exception handlers in multiple + // inlined scopes, indicates which scope relative to + // the youngest/innermost one in which we are performing + // the lookup; zero (or null GrowableArray) indicates + // innermost scope + GrowableArray* handler_pcos // pc offsets for the compiled handlers + ); + + // nmethod support + int size_in_bytes() const { return round_to(_length * sizeof(HandlerTableEntry), oopSize); } + void copy_to(nmethod* nm); + + // lookup + HandlerTableEntry* entry_for(int catch_pco, int handler_bci, int scope_depth) const; + + // debugging + void print_subtable(HandlerTableEntry* t) const; + void print() const; + void print_subtable_for(int catch_pco) const; +}; + + +// ---------------------------------------------------------------------------- +// Implicit null exception tables. Maps an exception PC offset to a +// continuation PC offset. During construction it's a variable sized +// array with a max size and current length. When stored inside an +// nmethod a zero length table takes no space. This is detected by +// nul_chk_table_size() == 0. Otherwise the table has a length word +// followed by pairs of . + +// Use 32-bit representation for offsets +typedef uint implicit_null_entry; + +class ImplicitExceptionTable VALUE_OBJ_CLASS_SPEC { + uint _size; + uint _len; + implicit_null_entry *_data; + implicit_null_entry *adr( uint idx ) const { return &_data[2*idx]; } + ReallocMark _nesting; // assertion check for reallocations +public: + ImplicitExceptionTable( ) : _data(0), _size(0), _len(0) { } + // (run-time) construction from nmethod + ImplicitExceptionTable( const nmethod *nm ); + + void set_size( uint size ); + void append( uint exec_off, uint cont_off ); + uint at( uint exec_off ) const; + + uint len() const { return _len; } + int size_in_bytes() const { return len() == 0 ? 0 : ((2 * len() + 1) * sizeof(implicit_null_entry)); } + + void copy_to(nmethod* nm); + void print(address base) const; + void verify(nmethod *nm) const; +}; diff --git a/hotspot/src/share/vm/code/icBuffer.cpp b/hotspot/src/share/vm/code/icBuffer.cpp new file mode 100644 index 00000000000..af6f14829c3 --- /dev/null +++ b/hotspot/src/share/vm/code/icBuffer.cpp @@ -0,0 +1,186 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_icBuffer.cpp.incl" + + +DEF_STUB_INTERFACE(ICStub); + +StubQueue* InlineCacheBuffer::_buffer = NULL; +ICStub* InlineCacheBuffer::_next_stub = NULL; + + +void ICStub::finalize() { + if (!is_empty()) { + ResourceMark rm; + CompiledIC *ic = CompiledIC_at(ic_site()); + assert(CodeCache::find_nmethod(ic->instruction_address()) != NULL, "inline cache in non-nmethod?"); + + assert(this == ICStub_from_destination_address(ic->stub_address()), "wrong owner of ic buffer"); + ic->set_cached_oop(cached_oop()); + ic->set_ic_destination(destination()); + } +} + + +address ICStub::destination() const { + return InlineCacheBuffer::ic_buffer_entry_point(code_begin()); +} + +oop ICStub::cached_oop() const { + return InlineCacheBuffer::ic_buffer_cached_oop(code_begin()); +} + + +void ICStub::set_stub(CompiledIC *ic, oop cached_value, address dest_addr) { + // We cannot store a pointer to the 'ic' object, since it is resource allocated. Instead we + // store the location of the inline cache. Then we have enough information recreate the CompiledIC + // object when we need to remove the stub. + _ic_site = ic->instruction_address(); + + // Assemble new stub + InlineCacheBuffer::assemble_ic_buffer_code(code_begin(), cached_value, dest_addr); + assert(destination() == dest_addr, "can recover destination"); + assert(cached_oop() == cached_value, "can recover destination"); +} + + +void ICStub::clear() { + _ic_site = NULL; +} + + +#ifndef PRODUCT +// anybody calling to this stub will trap + +void ICStub::verify() { +} + +void ICStub::print() { + tty->print_cr("ICStub: site: " INTPTR_FORMAT, _ic_site); +} +#endif + +//----------------------------------------------------------------------------------------------- +// Implementation of InlineCacheBuffer + +void InlineCacheBuffer::init_next_stub() { + ICStub* ic_stub = (ICStub*)buffer()->request_committed (ic_stub_code_size()); + assert (ic_stub != NULL, "no room for a single stub"); + set_next_stub(ic_stub); +} + +void InlineCacheBuffer::initialize() { + if (_buffer != NULL) return; // already initialized + _buffer = new StubQueue(new ICStubInterface, 10*K, InlineCacheBuffer_lock, "InlineCacheBuffer"); + assert (_buffer != NULL, "cannot allocate InlineCacheBuffer"); + init_next_stub(); +} + + +ICStub* InlineCacheBuffer::new_ic_stub() { + while (true) { + ICStub* ic_stub = (ICStub*)buffer()->request_committed(ic_stub_code_size()); + if (ic_stub != NULL) { + return ic_stub; + } + // we ran out of inline cache buffer space; must enter safepoint. + // We do this by forcing a safepoint + EXCEPTION_MARK; + + VM_ForceSafepoint vfs; + VMThread::execute(&vfs); + // We could potential get an async. exception at this point. + // In that case we will rethrow it to ourselvs. + if (HAS_PENDING_EXCEPTION) { + oop exception = PENDING_EXCEPTION; + CLEAR_PENDING_EXCEPTION; + Thread::send_async_exception(JavaThread::current()->threadObj(), exception); + } + } + ShouldNotReachHere(); + return NULL; +} + + +void InlineCacheBuffer::update_inline_caches() { + if (buffer()->number_of_stubs() > 1) { + if (TraceICBuffer) { + tty->print_cr("[updating inline caches with %d stubs]", buffer()->number_of_stubs()); + } + buffer()->remove_all(); + init_next_stub(); + } +} + + +bool InlineCacheBuffer::contains(address instruction_address) { + return buffer()->contains(instruction_address); +} + + +bool InlineCacheBuffer::is_empty() { + return buffer()->number_of_stubs() == 1; // always has sentinel +} + + +void InlineCacheBuffer_init() { + InlineCacheBuffer::initialize(); +} + + +void InlineCacheBuffer::create_transition_stub(CompiledIC *ic, oop cached_oop, address entry) { + assert(!SafepointSynchronize::is_at_safepoint(), "should not be called during a safepoint"); + assert (CompiledIC_lock->is_locked(), ""); + assert(cached_oop == NULL || cached_oop->is_perm(), "must belong to perm. space"); + if (TraceICBuffer) { tty->print_cr(" create transition stub for " INTPTR_FORMAT, ic->instruction_address()); } + + // If an transition stub is already associate with the inline cache, then we remove the association. + if (ic->is_in_transition_state()) { + ICStub* old_stub = ICStub_from_destination_address(ic->stub_address()); + old_stub->clear(); + } + + // allocate and initialize new "out-of-line" inline-cache + ICStub* ic_stub = get_next_stub(); + ic_stub->set_stub(ic, cached_oop, entry); + + // Update inline cache in nmethod to point to new "out-of-line" allocated inline cache + ic->set_ic_destination(ic_stub->code_begin()); + + set_next_stub(new_ic_stub()); // can cause safepoint synchronization +} + + +address InlineCacheBuffer::ic_destination_for(CompiledIC *ic) { + ICStub* stub = ICStub_from_destination_address(ic->stub_address()); + return stub->destination(); +} + + +oop InlineCacheBuffer::cached_oop_for(CompiledIC *ic) { + ICStub* stub = ICStub_from_destination_address(ic->stub_address()); + return stub->cached_oop(); +} diff --git a/hotspot/src/share/vm/code/icBuffer.hpp b/hotspot/src/share/vm/code/icBuffer.hpp new file mode 100644 index 00000000000..b7cc212d31b --- /dev/null +++ b/hotspot/src/share/vm/code/icBuffer.hpp @@ -0,0 +1,128 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// For CompiledIC's: +// +// In cases where we do not have MT-safe state transformation, +// we go to a transition state, using ICStubs. At a safepoint, +// the inline caches are transferred from the transitional code: +// +// instruction_address --> 01 set xxx_oop, Ginline_cache_klass +// 23 jump_to Gtemp, yyyy +// 4 nop + +class ICStub: public Stub { + private: + int _size; // total size of the stub incl. code + address _ic_site; // points at call instruction of owning ic-buffer + /* stub code follows here */ + protected: + friend class ICStubInterface; + // This will be called only by ICStubInterface + void initialize(int size) { _size = size; _ic_site = NULL; } + void finalize(); // called when a method is removed + + // General info + int size() const { return _size; } + static int code_size_to_size(int code_size) { return round_to(sizeof(ICStub), CodeEntryAlignment) + code_size; } + + public: + // Creation + void set_stub(CompiledIC *ic, oop cached_value, address dest_addr); + + // Code info + address code_begin() const { return (address)this + round_to(sizeof(ICStub), CodeEntryAlignment); } + address code_end() const { return (address)this + size(); } + + // Call site info + address ic_site() const { return _ic_site; } + void clear(); + bool is_empty() const { return _ic_site == NULL; } + + // stub info + address destination() const; // destination of jump instruction + oop cached_oop() const; // cached_oop for stub + + // Debugging + void verify() PRODUCT_RETURN; + void print() PRODUCT_RETURN; + + // Creation + friend ICStub* ICStub_from_destination_address(address destination_address); +}; + +// ICStub Creation +inline ICStub* ICStub_from_destination_address(address destination_address) { + ICStub* stub = (ICStub*) (destination_address - round_to(sizeof(ICStub), CodeEntryAlignment)); + #ifdef ASSERT + stub->verify(); + #endif + return stub; +} + +class InlineCacheBuffer: public AllStatic { + private: + // friends + friend class ICStub; + + static int ic_stub_code_size(); + + static StubQueue* _buffer; + static ICStub* _next_stub; + + static StubQueue* buffer() { return _buffer; } + static void set_next_stub(ICStub* next_stub) { _next_stub = next_stub; } + static ICStub* get_next_stub() { return _next_stub; } + + static void init_next_stub(); + + static ICStub* new_ic_stub(); + + + // Machine-dependent implementation of ICBuffer + static void assemble_ic_buffer_code(address code_begin, oop cached_oop, address entry_point); + static address ic_buffer_entry_point (address code_begin); + static oop ic_buffer_cached_oop (address code_begin); + + public: + + // Initialization; must be called before first usage + static void initialize(); + + // Access + static bool contains(address instruction_address); + + // removes the ICStubs after backpatching + static void update_inline_caches(); + + // for debugging + static bool is_empty(); + + + // New interface + static void create_transition_stub(CompiledIC *ic, oop cached_oop, address entry); + static address ic_destination_for(CompiledIC *ic); + static oop cached_oop_for(CompiledIC *ic); +}; diff --git a/hotspot/src/share/vm/code/location.cpp b/hotspot/src/share/vm/code/location.cpp new file mode 100644 index 00000000000..a74bb87305e --- /dev/null +++ b/hotspot/src/share/vm/code/location.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_location.cpp.incl" + +void Location::print_on(outputStream* st) const { + if(type() == invalid && !legal_offset_in_bytes(offset() * BytesPerInt)) { + // product of Location::invalid_loc() or Location::Location(). + switch (where()) { + case on_stack: st->print("empty"); break; + case in_register: st->print("invalid"); break; + } + return; + } + switch (where()) { + case on_stack: st->print("stack[%d]", stack_offset()); break; + case in_register: st->print("reg %s [%d]", reg()->name(), register_number()); break; + default: st->print("Wrong location where %d", where()); + } + switch (type()) { + case normal: break; + case oop: st->print(",oop"); break; + case int_in_long: st->print(",int"); break; + case lng: st->print(",long"); break; + case float_in_dbl: st->print(",float"); break; + case dbl: st->print(",double"); break; + case addr: st->print(",address"); break; + default: st->print("Wrong location type %d", type()); + } +} + + +Location::Location(DebugInfoReadStream* stream) { + _value = (uint16_t) stream->read_int(); +} + + +void Location::write_on(DebugInfoWriteStream* stream) { + stream->write_int(_value & 0x0000FFFF); +} + + +// Valid argument to Location::new_stk_loc()? +bool Location::legal_offset_in_bytes(int offset_in_bytes) { + if ((offset_in_bytes % BytesPerInt) != 0) return false; + return (offset_in_bytes / BytesPerInt) < (OFFSET_MASK >> OFFSET_SHIFT); +} diff --git a/hotspot/src/share/vm/code/location.hpp b/hotspot/src/share/vm/code/location.hpp new file mode 100644 index 00000000000..0a12b8707a4 --- /dev/null +++ b/hotspot/src/share/vm/code/location.hpp @@ -0,0 +1,114 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A Location describes a concrete machine variable location +// (such as integer or floating point register or a stack-held +// variable). Used when generating debug-information for nmethods. +// +// Encoding: +// +// bits: +// Where: [15] +// Type: [14..12] +// Offset: [11..0] + +class Location VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + public: + enum Where { + on_stack, + in_register + }; + + enum Type { + normal, // Ints, floats, double halves + oop, // Oop (please GC me!) + int_in_long, // Integer held in long register + lng, // Long held in one register + float_in_dbl, // Float held in double register + dbl, // Double held in one register + addr, // JSR return address + invalid // Invalid location + }; + + + private: + enum { + OFFSET_MASK = (jchar) 0x0FFF, + OFFSET_SHIFT = 0, + TYPE_MASK = (jchar) 0x7000, + TYPE_SHIFT = 12, + WHERE_MASK = (jchar) 0x8000, + WHERE_SHIFT = 15 + }; + + uint16_t _value; + + // Create a bit-packed Location + Location(Where where_, Type type_, unsigned offset_) { + set(where_, type_, offset_); + assert( where () == where_ , "" ); + assert( type () == type_ , "" ); + assert( offset() == offset_, "" ); + } + + inline void set(Where where_, Type type_, unsigned offset_) { + _value = (uint16_t) ((where_ << WHERE_SHIFT) | + (type_ << TYPE_SHIFT) | + ((offset_ << OFFSET_SHIFT) & OFFSET_MASK)); + } + + public: + + // Stack location Factory. Offset is 4-byte aligned; remove low bits + static Location new_stk_loc( Type t, int offset ) { return Location(on_stack,t,offset>>LogBytesPerInt); } + // Register location Factory + static Location new_reg_loc( Type t, VMReg reg ) { return Location(in_register, t, reg->value()); } + // Default constructor + Location() { set(on_stack,invalid,(unsigned) -1); } + + // Bit field accessors + Where where() const { return (Where) ((_value & WHERE_MASK) >> WHERE_SHIFT);} + Type type() const { return (Type) ((_value & TYPE_MASK) >> TYPE_SHIFT); } + unsigned offset() const { return (unsigned) ((_value & OFFSET_MASK) >> OFFSET_SHIFT); } + + // Accessors + bool is_register() const { return where() == in_register; } + bool is_stack() const { return where() == on_stack; } + + int stack_offset() const { assert(where() == on_stack, "wrong Where"); return offset()<klass_name(); \ + symbolOop name = m->name(); \ + symbolOop signature = m->signature(); \ + HS_DTRACE_PROBE6(hotspot, compiled__method__unload, \ + klass_name->bytes(), klass_name->utf8_length(), \ + name->bytes(), name->utf8_length(), \ + signature->bytes(), signature->utf8_length()); \ + } \ + } + +#else // ndef DTRACE_ENABLED + +#define DTRACE_METHOD_UNLOAD_PROBE(method) + +#endif + +bool nmethod::is_compiled_by_c1() const { + if (is_native_method()) return false; + assert(compiler() != NULL, "must be"); + return compiler()->is_c1(); +} +bool nmethod::is_compiled_by_c2() const { + if (is_native_method()) return false; + assert(compiler() != NULL, "must be"); + return compiler()->is_c2(); +} + + + +//--------------------------------------------------------------------------------- +// NMethod statistics +// They are printed under various flags, including: +// PrintC1Statistics, PrintOptoStatistics, LogVMOutput, and LogCompilation. +// (In the latter two cases, they like other stats are printed to the log only.) + +#ifndef PRODUCT +// These variables are put into one block to reduce relocations +// and make it simpler to print from the debugger. +static +struct nmethod_stats_struct { + int nmethod_count; + int total_size; + int relocation_size; + int code_size; + int stub_size; + int consts_size; + int scopes_data_size; + int scopes_pcs_size; + int dependencies_size; + int handler_table_size; + int nul_chk_table_size; + int oops_size; + + void note_nmethod(nmethod* nm) { + nmethod_count += 1; + total_size += nm->size(); + relocation_size += nm->relocation_size(); + code_size += nm->code_size(); + stub_size += nm->stub_size(); + consts_size += nm->consts_size(); + scopes_data_size += nm->scopes_data_size(); + scopes_pcs_size += nm->scopes_pcs_size(); + dependencies_size += nm->dependencies_size(); + handler_table_size += nm->handler_table_size(); + nul_chk_table_size += nm->nul_chk_table_size(); + oops_size += nm->oops_size(); + } + void print_nmethod_stats() { + if (nmethod_count == 0) return; + tty->print_cr("Statistics for %d bytecoded nmethods:", nmethod_count); + if (total_size != 0) tty->print_cr(" total in heap = %d", total_size); + if (relocation_size != 0) tty->print_cr(" relocation = %d", relocation_size); + if (code_size != 0) tty->print_cr(" main code = %d", code_size); + if (stub_size != 0) tty->print_cr(" stub code = %d", stub_size); + if (consts_size != 0) tty->print_cr(" constants = %d", consts_size); + if (scopes_data_size != 0) tty->print_cr(" scopes data = %d", scopes_data_size); + if (scopes_pcs_size != 0) tty->print_cr(" scopes pcs = %d", scopes_pcs_size); + if (dependencies_size != 0) tty->print_cr(" dependencies = %d", dependencies_size); + if (handler_table_size != 0) tty->print_cr(" handler table = %d", handler_table_size); + if (nul_chk_table_size != 0) tty->print_cr(" nul chk table = %d", nul_chk_table_size); + if (oops_size != 0) tty->print_cr(" oops = %d", oops_size); + } + + int native_nmethod_count; + int native_total_size; + int native_relocation_size; + int native_code_size; + int native_oops_size; + void note_native_nmethod(nmethod* nm) { + native_nmethod_count += 1; + native_total_size += nm->size(); + native_relocation_size += nm->relocation_size(); + native_code_size += nm->code_size(); + native_oops_size += nm->oops_size(); + } + void print_native_nmethod_stats() { + if (native_nmethod_count == 0) return; + tty->print_cr("Statistics for %d native nmethods:", native_nmethod_count); + if (native_total_size != 0) tty->print_cr(" N. total size = %d", native_total_size); + if (native_relocation_size != 0) tty->print_cr(" N. relocation = %d", native_relocation_size); + if (native_code_size != 0) tty->print_cr(" N. main code = %d", native_code_size); + if (native_oops_size != 0) tty->print_cr(" N. oops = %d", native_oops_size); + } + + int pc_desc_resets; // number of resets (= number of caches) + int pc_desc_queries; // queries to nmethod::find_pc_desc + int pc_desc_approx; // number of those which have approximate true + int pc_desc_repeats; // number of _last_pc_desc hits + int pc_desc_hits; // number of LRU cache hits + int pc_desc_tests; // total number of PcDesc examinations + int pc_desc_searches; // total number of quasi-binary search steps + int pc_desc_adds; // number of LUR cache insertions + + void print_pc_stats() { + tty->print_cr("PcDesc Statistics: %d queries, %.2f comparisons per query", + pc_desc_queries, + (double)(pc_desc_tests + pc_desc_searches) + / pc_desc_queries); + tty->print_cr(" caches=%d queries=%d/%d, hits=%d+%d, tests=%d+%d, adds=%d", + pc_desc_resets, + pc_desc_queries, pc_desc_approx, + pc_desc_repeats, pc_desc_hits, + pc_desc_tests, pc_desc_searches, pc_desc_adds); + } +} nmethod_stats; +#endif //PRODUCT + +//--------------------------------------------------------------------------------- + + +// The _unwind_handler is a special marker address, which says that +// for given exception oop and address, the frame should be removed +// as the tuple cannot be caught in the nmethod +address ExceptionCache::_unwind_handler = (address) -1; + + +ExceptionCache::ExceptionCache(Handle exception, address pc, address handler) { + assert(pc != NULL, "Must be non null"); + assert(exception.not_null(), "Must be non null"); + assert(handler != NULL, "Must be non null"); + + _count = 0; + _exception_type = exception->klass(); + _next = NULL; + + add_address_and_handler(pc,handler); +} + + +address ExceptionCache::match(Handle exception, address pc) { + assert(pc != NULL,"Must be non null"); + assert(exception.not_null(),"Must be non null"); + if (exception->klass() == exception_type()) { + return (test_address(pc)); + } + + return NULL; +} + + +bool ExceptionCache::match_exception_with_space(Handle exception) { + assert(exception.not_null(),"Must be non null"); + if (exception->klass() == exception_type() && count() < cache_size) { + return true; + } + return false; +} + + +address ExceptionCache::test_address(address addr) { + for (int i=0; imatch_exception_with_space(exception)) { + return ec; + } + ec = ec->next(); + } + return NULL; +} + + +//----------------------------------------------------------------------------- + + +// Helper used by both find_pc_desc methods. +static inline bool match_desc(PcDesc* pc, int pc_offset, bool approximate) { + NOT_PRODUCT(++nmethod_stats.pc_desc_tests); + if (!approximate) + return pc->pc_offset() == pc_offset; + else + return (pc-1)->pc_offset() < pc_offset && pc_offset <= pc->pc_offset(); +} + +void PcDescCache::reset_to(PcDesc* initial_pc_desc) { + if (initial_pc_desc == NULL) { + _last_pc_desc = NULL; // native method + return; + } + NOT_PRODUCT(++nmethod_stats.pc_desc_resets); + // reset the cache by filling it with benign (non-null) values + assert(initial_pc_desc->pc_offset() < 0, "must be sentinel"); + _last_pc_desc = initial_pc_desc + 1; // first valid one is after sentinel + for (int i = 0; i < cache_size; i++) + _pc_descs[i] = initial_pc_desc; +} + +PcDesc* PcDescCache::find_pc_desc(int pc_offset, bool approximate) { + NOT_PRODUCT(++nmethod_stats.pc_desc_queries); + NOT_PRODUCT(if (approximate) ++nmethod_stats.pc_desc_approx); + + // In order to prevent race conditions do not load cache elements + // repeatedly, but use a local copy: + PcDesc* res; + + // Step one: Check the most recently returned value. + res = _last_pc_desc; + if (res == NULL) return NULL; // native method; no PcDescs at all + if (match_desc(res, pc_offset, approximate)) { + NOT_PRODUCT(++nmethod_stats.pc_desc_repeats); + return res; + } + + // Step two: Check the LRU cache. + for (int i = 0; i < cache_size; i++) { + res = _pc_descs[i]; + if (res->pc_offset() < 0) break; // optimization: skip empty cache + if (match_desc(res, pc_offset, approximate)) { + NOT_PRODUCT(++nmethod_stats.pc_desc_hits); + _last_pc_desc = res; // record this cache hit in case of repeat + return res; + } + } + + // Report failure. + return NULL; +} + +void PcDescCache::add_pc_desc(PcDesc* pc_desc) { + NOT_PRODUCT(++nmethod_stats.pc_desc_adds); + // Update the LRU cache by shifting pc_desc forward: + for (int i = 0; i < cache_size; i++) { + PcDesc* next = _pc_descs[i]; + _pc_descs[i] = pc_desc; + pc_desc = next; + } + // Note: Do not update _last_pc_desc. It fronts for the LRU cache. +} + +// adjust pcs_size so that it is a multiple of both oopSize and +// sizeof(PcDesc) (assumes that if sizeof(PcDesc) is not a multiple +// of oopSize, then 2*sizeof(PcDesc) is) +static int adjust_pcs_size(int pcs_size) { + int nsize = round_to(pcs_size, oopSize); + if ((nsize % sizeof(PcDesc)) != 0) { + nsize = pcs_size + sizeof(PcDesc); + } + assert((nsize % oopSize) == 0, "correct alignment"); + return nsize; +} + +//----------------------------------------------------------------------------- + + +void nmethod::add_exception_cache_entry(ExceptionCache* new_entry) { + assert(ExceptionCache_lock->owned_by_self(),"Must hold the ExceptionCache_lock"); + assert(new_entry != NULL,"Must be non null"); + assert(new_entry->next() == NULL, "Must be null"); + + if (exception_cache() != NULL) { + new_entry->set_next(exception_cache()); + } + set_exception_cache(new_entry); +} + +void nmethod::remove_from_exception_cache(ExceptionCache* ec) { + ExceptionCache* prev = NULL; + ExceptionCache* curr = exception_cache(); + assert(curr != NULL, "nothing to remove"); + // find the previous and next entry of ec + while (curr != ec) { + prev = curr; + curr = curr->next(); + assert(curr != NULL, "ExceptionCache not found"); + } + // now: curr == ec + ExceptionCache* next = curr->next(); + if (prev == NULL) { + set_exception_cache(next); + } else { + prev->set_next(next); + } + delete curr; +} + + +// public method for accessing the exception cache +// These are the public access methods. +address nmethod::handler_for_exception_and_pc(Handle exception, address pc) { + // We never grab a lock to read the exception cache, so we may + // have false negatives. This is okay, as it can only happen during + // the first few exception lookups for a given nmethod. + ExceptionCache* ec = exception_cache(); + while (ec != NULL) { + address ret_val; + if ((ret_val = ec->match(exception,pc)) != NULL) { + return ret_val; + } + ec = ec->next(); + } + return NULL; +} + + +void nmethod::add_handler_for_exception_and_pc(Handle exception, address pc, address handler) { + // There are potential race conditions during exception cache updates, so we + // must own the ExceptionCache_lock before doing ANY modifications. Because + // we dont lock during reads, it is possible to have several threads attempt + // to update the cache with the same data. We need to check for already inserted + // copies of the current data before adding it. + + MutexLocker ml(ExceptionCache_lock); + ExceptionCache* target_entry = exception_cache_entry_for_exception(exception); + + if (target_entry == NULL || !target_entry->add_address_and_handler(pc,handler)) { + target_entry = new ExceptionCache(exception,pc,handler); + add_exception_cache_entry(target_entry); + } +} + + +//-------------end of code for ExceptionCache-------------- + + +void nmFlags::clear() { + assert(sizeof(nmFlags) == sizeof(int), "using more than one word for nmFlags"); + *(jint*)this = 0; +} + +int nmethod::total_size() const { + return + code_size() + + stub_size() + + consts_size() + + scopes_data_size() + + scopes_pcs_size() + + handler_table_size() + + nul_chk_table_size(); +} + +const char* nmethod::compile_kind() const { + if (method() == NULL) return "unloaded"; + if (is_native_method()) return "c2n"; + if (is_osr_method()) return "osr"; + return NULL; +} + +// %%% This variable is no longer used? +int nmethod::_zombie_instruction_size = NativeJump::instruction_size; + + +nmethod* nmethod::new_native_nmethod(methodHandle method, + CodeBuffer *code_buffer, + int vep_offset, + int frame_complete, + int frame_size, + ByteSize basic_lock_owner_sp_offset, + ByteSize basic_lock_sp_offset, + OopMapSet* oop_maps) { + // create nmethod + nmethod* nm = NULL; + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + int native_nmethod_size = allocation_size(code_buffer, sizeof(nmethod)); + const int dummy = -1; // Flag to force proper "operator new" + CodeOffsets offsets; + offsets.set_value(CodeOffsets::Verified_Entry, vep_offset); + offsets.set_value(CodeOffsets::Frame_Complete, frame_complete); + nm = new (native_nmethod_size) + nmethod(method(), native_nmethod_size, &offsets, + code_buffer, frame_size, + basic_lock_owner_sp_offset, basic_lock_sp_offset, + oop_maps); + NOT_PRODUCT(if (nm != NULL) nmethod_stats.note_native_nmethod(nm)); + if (PrintAssembly && nm != NULL) + Disassembler::decode(nm); + } + // verify nmethod + debug_only(if (nm) nm->verify();) // might block + + if (nm != NULL) { + nm->log_new_nmethod(); + } + + return nm; +} + +nmethod* nmethod::new_nmethod(methodHandle method, + int compile_id, + int entry_bci, + CodeOffsets* offsets, + int orig_pc_offset, + DebugInformationRecorder* debug_info, + Dependencies* dependencies, + CodeBuffer* code_buffer, int frame_size, + OopMapSet* oop_maps, + ExceptionHandlerTable* handler_table, + ImplicitExceptionTable* nul_chk_table, + AbstractCompiler* compiler, + int comp_level +) +{ + assert(debug_info->oop_recorder() == code_buffer->oop_recorder(), "shared OR"); + // create nmethod + nmethod* nm = NULL; + { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + int nmethod_size = + allocation_size(code_buffer, sizeof(nmethod)) + + adjust_pcs_size(debug_info->pcs_size()) + + round_to(dependencies->size_in_bytes() , oopSize) + + round_to(handler_table->size_in_bytes(), oopSize) + + round_to(nul_chk_table->size_in_bytes(), oopSize) + + round_to(debug_info->data_size() , oopSize); + nm = new (nmethod_size) + nmethod(method(), nmethod_size, compile_id, entry_bci, offsets, + orig_pc_offset, debug_info, dependencies, code_buffer, frame_size, + oop_maps, + handler_table, + nul_chk_table, + compiler, + comp_level); + if (nm != NULL) { + // To make dependency checking during class loading fast, record + // the nmethod dependencies in the classes it is dependent on. + // This allows the dependency checking code to simply walk the + // class hierarchy above the loaded class, checking only nmethods + // which are dependent on those classes. The slow way is to + // check every nmethod for dependencies which makes it linear in + // the number of methods compiled. For applications with a lot + // classes the slow way is too slow. + for (Dependencies::DepStream deps(nm); deps.next(); ) { + klassOop klass = deps.context_type(); + if (klass == NULL) continue; // ignore things like evol_method + + // record this nmethod as dependent on this klass + instanceKlass::cast(klass)->add_dependent_nmethod(nm); + } + } + NOT_PRODUCT(if (nm != NULL) nmethod_stats.note_nmethod(nm)); + if (PrintAssembly && nm != NULL) + Disassembler::decode(nm); + } + + // verify nmethod + debug_only(if (nm) nm->verify();) // might block + + if (nm != NULL) { + nm->log_new_nmethod(); + } + + // done + return nm; +} + + +// For native wrappers +nmethod::nmethod( + methodOop method, + int nmethod_size, + CodeOffsets* offsets, + CodeBuffer* code_buffer, + int frame_size, + ByteSize basic_lock_owner_sp_offset, + ByteSize basic_lock_sp_offset, + OopMapSet* oop_maps ) + : CodeBlob("native nmethod", code_buffer, sizeof(nmethod), + nmethod_size, offsets->value(CodeOffsets::Frame_Complete), frame_size, oop_maps), + _compiled_synchronized_native_basic_lock_owner_sp_offset(basic_lock_owner_sp_offset), + _compiled_synchronized_native_basic_lock_sp_offset(basic_lock_sp_offset) +{ + { + debug_only(No_Safepoint_Verifier nsv;) + assert_locked_or_safepoint(CodeCache_lock); + + NOT_PRODUCT(_has_debug_info = false; ) + _method = method; + _entry_bci = InvocationEntryBci; + _link = NULL; + _compiler = NULL; + // We have no exception handler or deopt handler make the + // values something that will never match a pc like the nmethod vtable entry + _exception_offset = 0; + _deoptimize_offset = 0; + _orig_pc_offset = 0; + _stub_offset = data_offset(); + _consts_offset = data_offset(); + _scopes_data_offset = data_offset(); + _scopes_pcs_offset = _scopes_data_offset; + _dependencies_offset = _scopes_pcs_offset; + _handler_table_offset = _dependencies_offset; + _nul_chk_table_offset = _handler_table_offset; + _nmethod_end_offset = _nul_chk_table_offset; + _compile_id = 0; // default + _comp_level = CompLevel_none; + _entry_point = instructions_begin(); + _verified_entry_point = instructions_begin() + offsets->value(CodeOffsets::Verified_Entry); + _osr_entry_point = NULL; + _exception_cache = NULL; + _pc_desc_cache.reset_to(NULL); + + flags.clear(); + flags.state = alive; + _markedForDeoptimization = 0; + + _lock_count = 0; + _stack_traversal_mark = 0; + + code_buffer->copy_oops_to(this); + debug_only(check_store();) + CodeCache::commit(this); + VTune::create_nmethod(this); + } + + if (PrintNativeNMethods || PrintDebugInfo || PrintRelocations || PrintDependencies) { + ttyLocker ttyl; // keep the following output all in one block + // This output goes directly to the tty, not the compiler log. + // To enable tools to match it up with the compilation activity, + // be sure to tag this tty output with the compile ID. + if (xtty != NULL) { + xtty->begin_head("print_native_nmethod"); + xtty->method(_method); + xtty->stamp(); + xtty->end_head(" address='" INTPTR_FORMAT "'", (intptr_t) this); + } + // print the header part first + print(); + // then print the requested information + if (PrintNativeNMethods) { + print_code(); + oop_maps->print(); + } + if (PrintRelocations) { + print_relocations(); + } + if (xtty != NULL) { + xtty->tail("print_native_nmethod"); + } + } + Events::log("Create nmethod " INTPTR_FORMAT, this); +} + + +void* nmethod::operator new(size_t size, int nmethod_size) { + // Always leave some room in the CodeCache for I2C/C2I adapters + if (CodeCache::unallocated_capacity() < CodeCacheMinimumFreeSpace) return NULL; + return CodeCache::allocate(nmethod_size); +} + + +nmethod::nmethod( + methodOop method, + int nmethod_size, + int compile_id, + int entry_bci, + CodeOffsets* offsets, + int orig_pc_offset, + DebugInformationRecorder* debug_info, + Dependencies* dependencies, + CodeBuffer *code_buffer, + int frame_size, + OopMapSet* oop_maps, + ExceptionHandlerTable* handler_table, + ImplicitExceptionTable* nul_chk_table, + AbstractCompiler* compiler, + int comp_level + ) + : CodeBlob("nmethod", code_buffer, sizeof(nmethod), + nmethod_size, offsets->value(CodeOffsets::Frame_Complete), frame_size, oop_maps), + _compiled_synchronized_native_basic_lock_owner_sp_offset(in_ByteSize(-1)), + _compiled_synchronized_native_basic_lock_sp_offset(in_ByteSize(-1)) +{ + assert(debug_info->oop_recorder() == code_buffer->oop_recorder(), "shared OR"); + { + debug_only(No_Safepoint_Verifier nsv;) + assert_locked_or_safepoint(CodeCache_lock); + + NOT_PRODUCT(_has_debug_info = false; ) + _method = method; + _compile_id = compile_id; + _comp_level = comp_level; + _entry_bci = entry_bci; + _link = NULL; + _compiler = compiler; + _orig_pc_offset = orig_pc_offset; + _stub_offset = instructions_offset() + code_buffer->total_offset_of(code_buffer->stubs()->start()); + + // Exception handler and deopt handler are in the stub section + _exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions); + _deoptimize_offset = _stub_offset + offsets->value(CodeOffsets::Deopt); + _consts_offset = instructions_offset() + code_buffer->total_offset_of(code_buffer->consts()->start()); + _scopes_data_offset = data_offset(); + _scopes_pcs_offset = _scopes_data_offset + round_to(debug_info->data_size (), oopSize); + _dependencies_offset = _scopes_pcs_offset + adjust_pcs_size(debug_info->pcs_size()); + _handler_table_offset = _dependencies_offset + round_to(dependencies->size_in_bytes (), oopSize); + _nul_chk_table_offset = _handler_table_offset + round_to(handler_table->size_in_bytes(), oopSize); + _nmethod_end_offset = _nul_chk_table_offset + round_to(nul_chk_table->size_in_bytes(), oopSize); + + _entry_point = instructions_begin(); + _verified_entry_point = instructions_begin() + offsets->value(CodeOffsets::Verified_Entry); + _osr_entry_point = instructions_begin() + offsets->value(CodeOffsets::OSR_Entry); + _exception_cache = NULL; + _pc_desc_cache.reset_to(scopes_pcs_begin()); + + flags.clear(); + flags.state = alive; + _markedForDeoptimization = 0; + + _unload_reported = false; // jvmti state + + _lock_count = 0; + _stack_traversal_mark = 0; + + // Copy contents of ScopeDescRecorder to nmethod + code_buffer->copy_oops_to(this); + debug_info->copy_to(this); + dependencies->copy_to(this); + debug_only(check_store();) + + CodeCache::commit(this); + + VTune::create_nmethod(this); + + // Copy contents of ExceptionHandlerTable to nmethod + handler_table->copy_to(this); + nul_chk_table->copy_to(this); + + // we use the information of entry points to find out if a method is + // static or non static + assert(compiler->is_c2() || + _method->is_static() == (entry_point() == _verified_entry_point), + " entry points must be same for static methods and vice versa"); + } + + bool printnmethods = PrintNMethods || CompilerOracle::has_option_string(_method, "PrintNMethods"); + if (printnmethods || PrintDebugInfo || PrintRelocations || PrintDependencies || PrintExceptionHandlers) { + print_nmethod(printnmethods); + } + + // Note: Do not verify in here as the CodeCache_lock is + // taken which would conflict with the CompiledIC_lock + // which taken during the verification of call sites. + // (was bug - gri 10/25/99) + + Events::log("Create nmethod " INTPTR_FORMAT, this); +} + + +// Print a short set of xml attributes to identify this nmethod. The +// output should be embedded in some other element. +void nmethod::log_identity(xmlStream* log) const { + log->print(" compile_id='%d'", compile_id()); + const char* nm_kind = compile_kind(); + if (nm_kind != NULL) log->print(" compile_kind='%s'", nm_kind); + if (compiler() != NULL) { + log->print(" compiler='%s'", compiler()->name()); + } +#ifdef TIERED + log->print(" level='%d'", comp_level()); +#endif // TIERED +} + + +#define LOG_OFFSET(log, name) \ + if ((intptr_t)name##_end() - (intptr_t)name##_begin()) \ + log->print(" " XSTR(name) "_offset='%d'" , \ + (intptr_t)name##_begin() - (intptr_t)this) + + +void nmethod::log_new_nmethod() const { + if (LogCompilation && xtty != NULL) { + ttyLocker ttyl; + HandleMark hm; + xtty->begin_elem("nmethod"); + log_identity(xtty); + xtty->print(" entry='" INTPTR_FORMAT "' size='%d'", + instructions_begin(), size()); + xtty->print(" address='" INTPTR_FORMAT "'", (intptr_t) this); + + LOG_OFFSET(xtty, relocation); + LOG_OFFSET(xtty, code); + LOG_OFFSET(xtty, stub); + LOG_OFFSET(xtty, consts); + LOG_OFFSET(xtty, scopes_data); + LOG_OFFSET(xtty, scopes_pcs); + LOG_OFFSET(xtty, dependencies); + LOG_OFFSET(xtty, handler_table); + LOG_OFFSET(xtty, nul_chk_table); + LOG_OFFSET(xtty, oops); + + xtty->method(method()); + xtty->stamp(); + xtty->end_elem(); + } +} + +#undef LOG_OFFSET + + +// Print out more verbose output usually for a newly created nmethod. +void nmethod::print_on(outputStream* st, const char* title) const { + if (st != NULL) { + ttyLocker ttyl; + // Print a little tag line that looks like +PrintCompilation output: + st->print("%3d%c %s", + compile_id(), + is_osr_method() ? '%' : + method() != NULL && + is_native_method() ? 'n' : ' ', + title); +#ifdef TIERED + st->print(" (%d) ", comp_level()); +#endif // TIERED + if (WizardMode) st->print(" (" INTPTR_FORMAT ")", this); + if (method() != NULL) { + method()->print_short_name(st); + if (is_osr_method()) + st->print(" @ %d", osr_entry_bci()); + if (method()->code_size() > 0) + st->print(" (%d bytes)", method()->code_size()); + } + } +} + + +#ifndef PRODUCT +void nmethod::print_nmethod(bool printmethod) { + ttyLocker ttyl; // keep the following output all in one block + if (xtty != NULL) { + xtty->begin_head("print_nmethod"); + xtty->stamp(); + xtty->end_head(); + } + // print the header part first + print(); + // then print the requested information + if (printmethod) { + print_code(); + print_pcs(); + oop_maps()->print(); + } + if (PrintDebugInfo) { + print_scopes(); + } + if (PrintRelocations) { + print_relocations(); + } + if (PrintDependencies) { + print_dependencies(); + } + if (PrintExceptionHandlers) { + print_handler_table(); + print_nul_chk_table(); + } + if (xtty != NULL) { + xtty->tail("print_nmethod"); + } +} +#endif + + +void nmethod::set_version(int v) { + flags.version = v; +} + + +ScopeDesc* nmethod::scope_desc_at(address pc) { + PcDesc* pd = pc_desc_at(pc); + guarantee(pd != NULL, "scope must be present"); + return new ScopeDesc(this, pd->scope_decode_offset(), + pd->obj_decode_offset()); +} + + +void nmethod::clear_inline_caches() { + assert(SafepointSynchronize::is_at_safepoint(), "cleaning of IC's only allowed at safepoint"); + if (is_zombie()) { + return; + } + + RelocIterator iter(this); + while (iter.next()) { + iter.reloc()->clear_inline_cache(); + } +} + + +void nmethod::cleanup_inline_caches() { + + assert(SafepointSynchronize::is_at_safepoint() && + !CompiledIC_lock->is_locked() && + !Patching_lock->is_locked(), "no threads must be updating the inline caches by them selfs"); + + // If the method is not entrant or zombie then a JMP is plastered over the + // first few bytes. If an oop in the old code was there, that oop + // should not get GC'd. Skip the first few bytes of oops on + // not-entrant methods. + address low_boundary = verified_entry_point(); + if (!is_in_use()) { + low_boundary += NativeJump::instruction_size; + // %%% Note: On SPARC we patch only a 4-byte trap, not a full NativeJump. + // This means that the low_boundary is going to be a little too high. + // This shouldn't matter, since oops of non-entrant methods are never used. + // In fact, why are we bothering to look at oops in a non-entrant method?? + } + + // Find all calls in an nmethod, and clear the ones that points to zombie methods + ResourceMark rm; + RelocIterator iter(this, low_boundary); + while(iter.next()) { + switch(iter.type()) { + case relocInfo::virtual_call_type: + case relocInfo::opt_virtual_call_type: { + CompiledIC *ic = CompiledIC_at(iter.reloc()); + // Ok, to lookup references to zombies here + CodeBlob *cb = CodeCache::find_blob_unsafe(ic->ic_destination()); + if( cb != NULL && cb->is_nmethod() ) { + nmethod* nm = (nmethod*)cb; + // Clean inline caches pointing to both zombie and not_entrant methods + if (!nm->is_in_use()) ic->set_to_clean(); + } + break; + } + case relocInfo::static_call_type: { + CompiledStaticCall *csc = compiledStaticCall_at(iter.reloc()); + CodeBlob *cb = CodeCache::find_blob_unsafe(csc->destination()); + if( cb != NULL && cb->is_nmethod() ) { + nmethod* nm = (nmethod*)cb; + // Clean inline caches pointing to both zombie and not_entrant methods + if (!nm->is_in_use()) csc->set_to_clean(); + } + break; + } + } + } +} + +void nmethod::mark_as_seen_on_stack() { + assert(is_not_entrant(), "must be a non-entrant method"); + set_stack_traversal_mark(NMethodSweeper::traversal_count()); +} + +// Tell if a non-entrant method can be converted to a zombie (i.e., there is no activations on the stack) +bool nmethod::can_not_entrant_be_converted() { + assert(is_not_entrant(), "must be a non-entrant method"); + assert(SafepointSynchronize::is_at_safepoint(), "must be called during a safepoint"); + + // Since the nmethod sweeper only does partial sweep the sweeper's traversal + // count can be greater than the stack traversal count before it hits the + // nmethod for the second time. + return stack_traversal_mark()+1 < NMethodSweeper::traversal_count(); +} + +void nmethod::inc_decompile_count() { + // Could be gated by ProfileTraps, but do not bother... + methodOop m = method(); + if (m == NULL) return; + methodDataOop mdo = m->method_data(); + if (mdo == NULL) return; + // There is a benign race here. See comments in methodDataOop.hpp. + mdo->inc_decompile_count(); +} + +void nmethod::make_unloaded(BoolObjectClosure* is_alive, oop cause) { + + post_compiled_method_unload(); + + // Since this nmethod is being unloaded, make sure that dependencies + // recorded in instanceKlasses get flushed and pass non-NULL closure to + // indicate that this work is being done during a GC. + assert(Universe::heap()->is_gc_active(), "should only be called during gc"); + assert(is_alive != NULL, "Should be non-NULL"); + // A non-NULL is_alive closure indicates that this is being called during GC. + flush_dependencies(is_alive); + + // Break cycle between nmethod & method + if (TraceClassUnloading && WizardMode) { + tty->print_cr("[Class unloading: Making nmethod " INTPTR_FORMAT + " unloadable], methodOop(" INTPTR_FORMAT + "), cause(" INTPTR_FORMAT ")", + this, (address)_method, (address)cause); + cause->klass()->print(); + } + // If _method is already NULL the methodOop is about to be unloaded, + // so we don't have to break the cycle. Note that it is possible to + // have the methodOop live here, in case we unload the nmethod because + // it is pointing to some oop (other than the methodOop) being unloaded. + if (_method != NULL) { + // OSR methods point to the methodOop, but the methodOop does not + // point back! + if (_method->code() == this) { + _method->clear_code(); // Break a cycle + } + inc_decompile_count(); // Last chance to make a mark on the MDO + _method = NULL; // Clear the method of this dead nmethod + } + // Make the class unloaded - i.e., change state and notify sweeper + check_safepoint(); + if (is_in_use()) { + // Transitioning directly from live to unloaded -- so + // we need to force a cache clean-up; remember this + // for later on. + CodeCache::set_needs_cache_clean(true); + } + flags.state = unloaded; + + // The methodOop is gone at this point + assert(_method == NULL, "Tautology"); + + set_link(NULL); + NMethodSweeper::notify(this); +} + +void nmethod::invalidate_osr_method() { + assert(_entry_bci != InvocationEntryBci, "wrong kind of nmethod"); + if (_entry_bci != InvalidOSREntryBci) + inc_decompile_count(); + // Remove from list of active nmethods + if (method() != NULL) + instanceKlass::cast(method()->method_holder())->remove_osr_nmethod(this); + // Set entry as invalid + _entry_bci = InvalidOSREntryBci; +} + +void nmethod::log_state_change(int state) const { + if (LogCompilation) { + if (xtty != NULL) { + ttyLocker ttyl; // keep the following output all in one block + xtty->begin_elem("make_not_entrant %sthread='" UINTX_FORMAT "'", + (state == zombie ? "zombie='1' " : ""), + os::current_thread_id()); + log_identity(xtty); + xtty->stamp(); + xtty->end_elem(); + } + } + if (PrintCompilation) { + print_on(tty, state == zombie ? "made zombie " : "made not entrant "); + tty->cr(); + } +} + +// Common functionality for both make_not_entrant and make_zombie +void nmethod::make_not_entrant_or_zombie(int state) { + assert(state == zombie || state == not_entrant, "must be zombie or not_entrant"); + + // Code for an on-stack-replacement nmethod is removed when a class gets unloaded. + // They never become zombie/non-entrant, so the nmethod sweeper will never remove + // them. Instead the entry_bci is set to InvalidOSREntryBci, so the osr nmethod + // will never be used anymore. That the nmethods only gets removed when class unloading + // happens, make life much simpler, since the nmethods are not just going to disappear + // out of the blue. + if (is_osr_only_method()) { + if (osr_entry_bci() != InvalidOSREntryBci) { + // only log this once + log_state_change(state); + } + invalidate_osr_method(); + return; + } + + // If the method is already zombie or set to the state we want, nothing to do + if (is_zombie() || (state == not_entrant && is_not_entrant())) { + return; + } + + log_state_change(state); + + // Make sure the nmethod is not flushed in case of a safepoint in code below. + nmethodLocker nml(this); + + { + // Enter critical section. Does not block for safepoint. + MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag); + // The caller can be calling the method statically or through an inline + // cache call. + if (!is_not_entrant()) { + NativeJump::patch_verified_entry(entry_point(), verified_entry_point(), + SharedRuntime::get_handle_wrong_method_stub()); + assert (NativeJump::instruction_size == nmethod::_zombie_instruction_size, ""); + } + + // When the nmethod becomes zombie it is no longer alive so the + // dependencies must be flushed. nmethods in the not_entrant + // state will be flushed later when the transition to zombie + // happens or they get unloaded. + if (state == zombie) { + assert(SafepointSynchronize::is_at_safepoint(), "must be done at safepoint"); + flush_dependencies(NULL); + } else { + assert(state == not_entrant, "other cases may need to be handled differently"); + } + + // Change state + flags.state = state; + } // leave critical region under Patching_lock + + if (state == not_entrant) { + Events::log("Make nmethod not entrant " INTPTR_FORMAT, this); + } else { + Events::log("Make nmethod zombie " INTPTR_FORMAT, this); + } + + if (TraceCreateZombies) { + tty->print_cr("nmethod <" INTPTR_FORMAT "> code made %s", this, (state == not_entrant) ? "not entrant" : "zombie"); + } + + // Make sweeper aware that there is a zombie method that needs to be removed + NMethodSweeper::notify(this); + + // not_entrant only stuff + if (state == not_entrant) { + mark_as_seen_on_stack(); + } + + // It's a true state change, so mark the method as decompiled. + inc_decompile_count(); + + + // zombie only - if a JVMTI agent has enabled the CompiledMethodUnload event + // and it hasn't already been reported for this nmethod then report it now. + // (the event may have been reported earilier if the GC marked it for unloading). + if (state == zombie) { + + DTRACE_METHOD_UNLOAD_PROBE(method()); + + if (JvmtiExport::should_post_compiled_method_unload() && + !unload_reported()) { + assert(method() != NULL, "checking"); + { + HandleMark hm; + JvmtiExport::post_compiled_method_unload_at_safepoint( + method()->jmethod_id(), code_begin()); + } + set_unload_reported(); + } + } + + + // Zombie only stuff + if (state == zombie) { + VTune::delete_nmethod(this); + } + + // Check whether method got unloaded at a safepoint before this, + // if so we can skip the flushing steps below + if (method() == NULL) return; + + // Remove nmethod from method. + // We need to check if both the _code and _from_compiled_code_entry_point + // refer to this nmethod because there is a race in setting these two fields + // in methodOop as seen in bugid 4947125. + // If the vep() points to the zombie nmethod, the memory for the nmethod + // could be flushed and the compiler and vtable stubs could still call + // through it. + if (method()->code() == this || + method()->from_compiled_entry() == verified_entry_point()) { + HandleMark hm; + method()->clear_code(); + } +} + + +#ifndef PRODUCT +void nmethod::check_safepoint() { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); +} +#endif + + +void nmethod::flush() { + // Note that there are no valid oops in the nmethod anymore. + assert(is_zombie() || (is_osr_method() && is_unloaded()), "must be a zombie method"); + assert(is_marked_for_reclamation() || (is_osr_method() && is_unloaded()), "must be marked for reclamation"); + + assert (!is_locked_by_vm(), "locked methods shouldn't be flushed"); + check_safepoint(); + + // completely deallocate this method + EventMark m("flushing nmethod " INTPTR_FORMAT " %s", this, ""); + if (PrintMethodFlushing) { + tty->print_cr("*flushing nmethod " INTPTR_FORMAT ". Live blobs: %d", this, CodeCache::nof_blobs()); + } + + // We need to deallocate any ExceptionCache data. + // Note that we do not need to grab the nmethod lock for this, it + // better be thread safe if we're disposing of it! + ExceptionCache* ec = exception_cache(); + set_exception_cache(NULL); + while(ec != NULL) { + ExceptionCache* next = ec->next(); + delete ec; + ec = next; + } + + ((CodeBlob*)(this))->flush(); + + CodeCache::free(this); +} + + +// +// Notify all classes this nmethod is dependent on that it is no +// longer dependent. This should only be called in two situations. +// First, when a nmethod transitions to a zombie all dependents need +// to be clear. Since zombification happens at a safepoint there's no +// synchronization issues. The second place is a little more tricky. +// During phase 1 of mark sweep class unloading may happen and as a +// result some nmethods may get unloaded. In this case the flushing +// of dependencies must happen during phase 1 since after GC any +// dependencies in the unloaded nmethod won't be updated, so +// traversing the dependency information in unsafe. In that case this +// function is called with a non-NULL argument and this function only +// notifies instanceKlasses that are reachable + +void nmethod::flush_dependencies(BoolObjectClosure* is_alive) { + assert(SafepointSynchronize::is_at_safepoint(), "must be done at safepoint"); + assert(Universe::heap()->is_gc_active() == (is_alive != NULL), + "is_alive is non-NULL if and only if we are called during GC"); + if (!has_flushed_dependencies()) { + set_has_flushed_dependencies(); + for (Dependencies::DepStream deps(this); deps.next(); ) { + klassOop klass = deps.context_type(); + if (klass == NULL) continue; // ignore things like evol_method + + // During GC the is_alive closure is non-NULL, and is used to + // determine liveness of dependees that need to be updated. + if (is_alive == NULL || is_alive->do_object_b(klass)) { + instanceKlass::cast(klass)->remove_dependent_nmethod(this); + } + } + } +} + + +// If this oop is not live, the nmethod can be unloaded. +bool nmethod::can_unload(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + oop* root, bool unloading_occurred) { + assert(root != NULL, "just checking"); + oop obj = *root; + if (obj == NULL || is_alive->do_object_b(obj)) { + return false; + } + if (obj->is_compiledICHolder()) { + compiledICHolderOop cichk_oop = compiledICHolderOop(obj); + if (is_alive->do_object_b( + cichk_oop->holder_method()->method_holder()) && + is_alive->do_object_b(cichk_oop->holder_klass())) { + // The oop should be kept alive + keep_alive->do_oop(root); + return false; + } + } + if (!UseParallelOldGC || !VerifyParallelOldWithMarkSweep) { + // Cannot do this test if verification of the UseParallelOldGC + // code using the PSMarkSweep code is being done. + assert(unloading_occurred, "Inconsistency in unloading"); + } + make_unloaded(is_alive, obj); + return true; +} + +// ------------------------------------------------------------------ +// post_compiled_method_load_event +// new method for install_code() path +// Transfer information from compilation to jvmti +void nmethod::post_compiled_method_load_event() { + + methodOop moop = method(); + HS_DTRACE_PROBE8(hotspot, compiled__method__load, + moop->klass_name()->bytes(), + moop->klass_name()->utf8_length(), + moop->name()->bytes(), + moop->name()->utf8_length(), + moop->signature()->bytes(), + moop->signature()->utf8_length(), + code_begin(), code_size()); + + if (JvmtiExport::should_post_compiled_method_load()) { + JvmtiExport::post_compiled_method_load(this); + } +} + +void nmethod::post_compiled_method_unload() { + assert(_method != NULL && !is_unloaded(), "just checking"); + DTRACE_METHOD_UNLOAD_PROBE(method()); + + // If a JVMTI agent has enabled the CompiledMethodUnload event then + // post the event. Sometime later this nmethod will be made a zombie by + // the sweeper but the methodOop will not be valid at that point. + if (JvmtiExport::should_post_compiled_method_unload()) { + assert(!unload_reported(), "already unloaded"); + HandleMark hm; + JvmtiExport::post_compiled_method_unload_at_safepoint( + method()->jmethod_id(), code_begin()); + } + + // The JVMTI CompiledMethodUnload event can be enabled or disabled at + // any time. As the nmethod is being unloaded now we mark it has + // having the unload event reported - this will ensure that we don't + // attempt to report the event in the unlikely scenario where the + // event is enabled at the time the nmethod is made a zombie. + set_unload_reported(); +} + +// This is called at the end of the strong tracing/marking phase of a +// GC to unload an nmethod if it contains otherwise unreachable +// oops. + +void nmethod::do_unloading(BoolObjectClosure* is_alive, + OopClosure* keep_alive, bool unloading_occurred) { + // Make sure the oop's ready to receive visitors + assert(!is_zombie() && !is_unloaded(), + "should not call follow on zombie or unloaded nmethod"); + + // If the method is not entrant then a JMP is plastered over the + // first few bytes. If an oop in the old code was there, that oop + // should not get GC'd. Skip the first few bytes of oops on + // not-entrant methods. + address low_boundary = verified_entry_point(); + if (is_not_entrant()) { + low_boundary += NativeJump::instruction_size; + // %%% Note: On SPARC we patch only a 4-byte trap, not a full NativeJump. + // (See comment above.) + } + + // The RedefineClasses() API can cause the class unloading invariant + // to no longer be true. See jvmtiExport.hpp for details. + // Also, leave a debugging breadcrumb in local flag. + bool a_class_was_redefined = JvmtiExport::has_redefined_a_class(); + if (a_class_was_redefined) { + // This set of the unloading_occurred flag is done before the + // call to post_compiled_method_unload() so that the unloading + // of this nmethod is reported. + unloading_occurred = true; + } + + // Follow methodOop + if (can_unload(is_alive, keep_alive, (oop*)&_method, unloading_occurred)) { + return; + } + + // Exception cache + ExceptionCache* ec = exception_cache(); + while (ec != NULL) { + oop* ex_addr = (oop*)ec->exception_type_addr(); + oop ex = *ex_addr; + ExceptionCache* next_ec = ec->next(); + if (ex != NULL && !is_alive->do_object_b(ex)) { + assert(!ex->is_compiledICHolder(), "Possible error here"); + remove_from_exception_cache(ec); + } + ec = next_ec; + } + + // If class unloading occurred we first iterate over all inline caches and + // clear ICs where the cached oop is referring to an unloaded klass or method. + // The remaining live cached oops will be traversed in the relocInfo::oop_type + // iteration below. + if (unloading_occurred) { + RelocIterator iter(this, low_boundary); + while(iter.next()) { + if (iter.type() == relocInfo::virtual_call_type) { + CompiledIC *ic = CompiledIC_at(iter.reloc()); + oop ic_oop = ic->cached_oop(); + if (ic_oop != NULL && !is_alive->do_object_b(ic_oop)) { + // The only exception is compiledICHolder oops which may + // yet be marked below. (We check this further below). + if (ic_oop->is_compiledICHolder()) { + compiledICHolderOop cichk_oop = compiledICHolderOop(ic_oop); + if (is_alive->do_object_b( + cichk_oop->holder_method()->method_holder()) && + is_alive->do_object_b(cichk_oop->holder_klass())) { + continue; + } + } + ic->set_to_clean(); + assert(ic->cached_oop() == NULL, "cached oop in IC should be cleared") + } + } + } + } + + // Compiled code + RelocIterator iter(this, low_boundary); + while (iter.next()) { + if (iter.type() == relocInfo::oop_type) { + oop_Relocation* r = iter.oop_reloc(); + // In this loop, we must only traverse those oops directly embedded in + // the code. Other oops (oop_index>0) are seen as part of scopes_oops. + assert(1 == (r->oop_is_immediate()) + + (r->oop_addr() >= oops_begin() && r->oop_addr() < oops_end()), + "oop must be found in exactly one place"); + if (r->oop_is_immediate() && r->oop_value() != NULL) { + if (can_unload(is_alive, keep_alive, r->oop_addr(), unloading_occurred)) { + return; + } + } + } + } + + + // Scopes + for (oop* p = oops_begin(); p < oops_end(); p++) { + if (*p == Universe::non_oop_word()) continue; // skip non-oops + if (can_unload(is_alive, keep_alive, p, unloading_occurred)) { + return; + } + } + +#ifndef PRODUCT + // This nmethod was not unloaded; check below that all CompiledICs + // refer to marked oops. + { + RelocIterator iter(this, low_boundary); + while (iter.next()) { + if (iter.type() == relocInfo::virtual_call_type) { + CompiledIC *ic = CompiledIC_at(iter.reloc()); + oop ic_oop = ic->cached_oop(); + assert(ic_oop == NULL || is_alive->do_object_b(ic_oop), + "Found unmarked ic_oop in reachable nmethod"); + } + } + } +#endif // !PRODUCT +} + +void nmethod::oops_do(OopClosure* f) { + // make sure the oops ready to receive visitors + assert(!is_zombie() && !is_unloaded(), + "should not call follow on zombie or unloaded nmethod"); + + // If the method is not entrant or zombie then a JMP is plastered over the + // first few bytes. If an oop in the old code was there, that oop + // should not get GC'd. Skip the first few bytes of oops on + // not-entrant methods. + address low_boundary = verified_entry_point(); + if (is_not_entrant()) { + low_boundary += NativeJump::instruction_size; + // %%% Note: On SPARC we patch only a 4-byte trap, not a full NativeJump. + // (See comment above.) + } + + // Compiled code + f->do_oop((oop*) &_method); + ExceptionCache* ec = exception_cache(); + while(ec != NULL) { + f->do_oop((oop*)ec->exception_type_addr()); + ec = ec->next(); + } + + RelocIterator iter(this, low_boundary); + while (iter.next()) { + if (iter.type() == relocInfo::oop_type ) { + oop_Relocation* r = iter.oop_reloc(); + // In this loop, we must only follow those oops directly embedded in + // the code. Other oops (oop_index>0) are seen as part of scopes_oops. + assert(1 == (r->oop_is_immediate()) + (r->oop_addr() >= oops_begin() && r->oop_addr() < oops_end()), "oop must be found in exactly one place"); + if (r->oop_is_immediate() && r->oop_value() != NULL) { + f->do_oop(r->oop_addr()); + } + } + } + + // Scopes + for (oop* p = oops_begin(); p < oops_end(); p++) { + if (*p == Universe::non_oop_word()) continue; // skip non-oops + f->do_oop(p); + } +} + +// Method that knows how to preserve outgoing arguments at call. This method must be +// called with a frame corresponding to a Java invoke +void nmethod::preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) { + if (!method()->is_native()) { + SimpleScopeDesc ssd(this, fr.pc()); + Bytecode_invoke* call = Bytecode_invoke_at(ssd.method(), ssd.bci()); + bool is_static = call->is_invokestatic(); + symbolOop signature = call->signature(); + fr.oops_compiled_arguments_do(signature, is_static, reg_map, f); + } +} + + +oop nmethod::embeddedOop_at(u_char* p) { + RelocIterator iter(this, p, p + oopSize); + while (iter.next()) + if (iter.type() == relocInfo::oop_type) { + return iter.oop_reloc()->oop_value(); + } + return NULL; +} + + +inline bool includes(void* p, void* from, void* to) { + return from <= p && p < to; +} + + +void nmethod::copy_scopes_pcs(PcDesc* pcs, int count) { + assert(count >= 2, "must be sentinel values, at least"); + +#ifdef ASSERT + // must be sorted and unique; we do a binary search in find_pc_desc() + int prev_offset = pcs[0].pc_offset(); + assert(prev_offset == PcDesc::lower_offset_limit, + "must start with a sentinel"); + for (int i = 1; i < count; i++) { + int this_offset = pcs[i].pc_offset(); + assert(this_offset > prev_offset, "offsets must be sorted"); + prev_offset = this_offset; + } + assert(prev_offset == PcDesc::upper_offset_limit, + "must end with a sentinel"); +#endif //ASSERT + + int size = count * sizeof(PcDesc); + assert(scopes_pcs_size() >= size, "oob"); + memcpy(scopes_pcs_begin(), pcs, size); + + // Adjust the final sentinel downward. + PcDesc* last_pc = &scopes_pcs_begin()[count-1]; + assert(last_pc->pc_offset() == PcDesc::upper_offset_limit, "sanity"); + last_pc->set_pc_offset(instructions_size() + 1); + for (; last_pc + 1 < scopes_pcs_end(); last_pc += 1) { + // Fill any rounding gaps with copies of the last record. + last_pc[1] = last_pc[0]; + } + // The following assert could fail if sizeof(PcDesc) is not + // an integral multiple of oopSize (the rounding term). + // If it fails, change the logic to always allocate a multiple + // of sizeof(PcDesc), and fill unused words with copies of *last_pc. + assert(last_pc + 1 == scopes_pcs_end(), "must match exactly"); +} + +void nmethod::copy_scopes_data(u_char* buffer, int size) { + assert(scopes_data_size() >= size, "oob"); + memcpy(scopes_data_begin(), buffer, size); +} + + +#ifdef ASSERT +static PcDesc* linear_search(nmethod* nm, int pc_offset, bool approximate) { + PcDesc* lower = nm->scopes_pcs_begin(); + PcDesc* upper = nm->scopes_pcs_end(); + lower += 1; // exclude initial sentinel + PcDesc* res = NULL; + for (PcDesc* p = lower; p < upper; p++) { + NOT_PRODUCT(--nmethod_stats.pc_desc_tests); // don't count this call to match_desc + if (match_desc(p, pc_offset, approximate)) { + if (res == NULL) + res = p; + else + res = (PcDesc*) badAddress; + } + } + return res; +} +#endif + + +// Finds a PcDesc with real-pc equal to "pc" +PcDesc* nmethod::find_pc_desc_internal(address pc, bool approximate) { + address base_address = instructions_begin(); + if ((pc < base_address) || + (pc - base_address) >= (ptrdiff_t) PcDesc::upper_offset_limit) { + return NULL; // PC is wildly out of range + } + int pc_offset = (int) (pc - base_address); + + // Check the PcDesc cache if it contains the desired PcDesc + // (This as an almost 100% hit rate.) + PcDesc* res = _pc_desc_cache.find_pc_desc(pc_offset, approximate); + if (res != NULL) { + assert(res == linear_search(this, pc_offset, approximate), "cache ok"); + return res; + } + + // Fallback algorithm: quasi-linear search for the PcDesc + // Find the last pc_offset less than the given offset. + // The successor must be the required match, if there is a match at all. + // (Use a fixed radix to avoid expensive affine pointer arithmetic.) + PcDesc* lower = scopes_pcs_begin(); + PcDesc* upper = scopes_pcs_end(); + upper -= 1; // exclude final sentinel + if (lower >= upper) return NULL; // native method; no PcDescs at all + +#define assert_LU_OK \ + /* invariant on lower..upper during the following search: */ \ + assert(lower->pc_offset() < pc_offset, "sanity"); \ + assert(upper->pc_offset() >= pc_offset, "sanity") + assert_LU_OK; + + // Use the last successful return as a split point. + PcDesc* mid = _pc_desc_cache.last_pc_desc(); + NOT_PRODUCT(++nmethod_stats.pc_desc_searches); + if (mid->pc_offset() < pc_offset) { + lower = mid; + } else { + upper = mid; + } + + // Take giant steps at first (4096, then 256, then 16, then 1) + const int LOG2_RADIX = 4 /*smaller steps in debug mode:*/ debug_only(-1); + const int RADIX = (1 << LOG2_RADIX); + for (int step = (1 << (LOG2_RADIX*3)); step > 1; step >>= LOG2_RADIX) { + while ((mid = lower + step) < upper) { + assert_LU_OK; + NOT_PRODUCT(++nmethod_stats.pc_desc_searches); + if (mid->pc_offset() < pc_offset) { + lower = mid; + } else { + upper = mid; + break; + } + } + assert_LU_OK; + } + + // Sneak up on the value with a linear search of length ~16. + while (true) { + assert_LU_OK; + mid = lower + 1; + NOT_PRODUCT(++nmethod_stats.pc_desc_searches); + if (mid->pc_offset() < pc_offset) { + lower = mid; + } else { + upper = mid; + break; + } + } +#undef assert_LU_OK + + if (match_desc(upper, pc_offset, approximate)) { + assert(upper == linear_search(this, pc_offset, approximate), "search ok"); + _pc_desc_cache.add_pc_desc(upper); + return upper; + } else { + assert(NULL == linear_search(this, pc_offset, approximate), "search ok"); + return NULL; + } +} + + +bool nmethod::check_all_dependencies() { + bool found_check = false; + // wholesale check of all dependencies + for (Dependencies::DepStream deps(this); deps.next(); ) { + if (deps.check_dependency() != NULL) { + found_check = true; + NOT_DEBUG(break); + } + } + return found_check; // tell caller if we found anything +} + +bool nmethod::check_dependency_on(DepChange& changes) { + // What has happened: + // 1) a new class dependee has been added + // 2) dependee and all its super classes have been marked + bool found_check = false; // set true if we are upset + for (Dependencies::DepStream deps(this); deps.next(); ) { + // Evaluate only relevant dependencies. + if (deps.spot_check_dependency_at(changes) != NULL) { + found_check = true; + NOT_DEBUG(break); + } + } + return found_check; +} + +bool nmethod::is_evol_dependent_on(klassOop dependee) { + instanceKlass *dependee_ik = instanceKlass::cast(dependee); + objArrayOop dependee_methods = dependee_ik->methods(); + for (Dependencies::DepStream deps(this); deps.next(); ) { + if (deps.type() == Dependencies::evol_method) { + methodOop method = deps.method_argument(0); + for (int j = 0; j < dependee_methods->length(); j++) { + if ((methodOop) dependee_methods->obj_at(j) == method) { + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x01000000, + ("Found evol dependency of nmethod %s.%s(%s) compile_id=%d on method %s.%s(%s)", + _method->method_holder()->klass_part()->external_name(), + _method->name()->as_C_string(), + _method->signature()->as_C_string(), compile_id(), + method->method_holder()->klass_part()->external_name(), + method->name()->as_C_string(), + method->signature()->as_C_string())); + if (TraceDependencies || LogCompilation) + deps.log_dependency(dependee); + return true; + } + } + } + } + return false; +} + +// Called from mark_for_deoptimization, when dependee is invalidated. +bool nmethod::is_dependent_on_method(methodOop dependee) { + for (Dependencies::DepStream deps(this); deps.next(); ) { + if (deps.type() != Dependencies::evol_method) + continue; + methodOop method = deps.method_argument(0); + if (method == dependee) return true; + } + return false; +} + + +bool nmethod::is_patchable_at(address instr_addr) { + assert (code_contains(instr_addr), "wrong nmethod used"); + if (is_zombie()) { + // a zombie may never be patched + return false; + } + return true; +} + + +address nmethod::continuation_for_implicit_exception(address pc) { + // Exception happened outside inline-cache check code => we are inside + // an active nmethod => use cpc to determine a return address + int exception_offset = pc - instructions_begin(); + int cont_offset = ImplicitExceptionTable(this).at( exception_offset ); +#ifdef ASSERT + if (cont_offset == 0) { + Thread* thread = ThreadLocalStorage::get_thread_slow(); + ResetNoHandleMark rnm; // Might be called from LEAF/QUICK ENTRY + HandleMark hm(thread); + ResourceMark rm(thread); + CodeBlob* cb = CodeCache::find_blob(pc); + assert(cb != NULL && cb == this, ""); + tty->print_cr("implicit exception happened at " INTPTR_FORMAT, pc); + print(); + method()->print_codes(); + print_code(); + print_pcs(); + } +#endif + guarantee(cont_offset != 0, "unhandled implicit exception in compiled code"); + return instructions_begin() + cont_offset; +} + + + +void nmethod_init() { + // make sure you didn't forget to adjust the filler fields + assert(sizeof(nmFlags) <= 4, "nmFlags occupies more than a word"); + assert(sizeof(nmethod) % oopSize == 0, "nmethod size must be multiple of a word"); +} + + +//------------------------------------------------------------------------------------------- + + +// QQQ might we make this work from a frame?? +nmethodLocker::nmethodLocker(address pc) { + CodeBlob* cb = CodeCache::find_blob(pc); + guarantee(cb != NULL && cb->is_nmethod(), "bad pc for a nmethod found"); + _nm = (nmethod*)cb; + lock_nmethod(_nm); +} + +void nmethodLocker::lock_nmethod(nmethod* nm) { + if (nm == NULL) return; + Atomic::inc(&nm->_lock_count); + guarantee(!nm->is_zombie(), "cannot lock a zombie method"); +} + +void nmethodLocker::unlock_nmethod(nmethod* nm) { + if (nm == NULL) return; + Atomic::dec(&nm->_lock_count); + guarantee(nm->_lock_count >= 0, "unmatched nmethod lock/unlock"); +} + +bool nmethod::is_deopt_pc(address pc) { + bool ret = pc == deopt_handler_begin(); + return ret; +} + + +// ----------------------------------------------------------------------------- +// Verification + +void nmethod::verify() { + + // Hmm. OSR methods can be deopted but not marked as zombie or not_entrant + // seems odd. + + if( is_zombie() || is_not_entrant() ) + return; + + // Make sure all the entry points are correctly aligned for patching. + NativeJump::check_verified_entry_alignment(entry_point(), verified_entry_point()); + + assert(method()->is_oop(), "must be valid"); + + ResourceMark rm; + + if (!CodeCache::contains(this)) { + fatal1("nmethod at " INTPTR_FORMAT " not in zone", this); + } + + if(is_native_method() ) + return; + + nmethod* nm = CodeCache::find_nmethod(verified_entry_point()); + if (nm != this) { + fatal1("findNMethod did not find this nmethod (" INTPTR_FORMAT ")", this); + } + + for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) { + if (! p->verify(this)) { + tty->print_cr("\t\tin nmethod at " INTPTR_FORMAT " (pcs)", this); + } + } + + verify_scopes(); +} + + +void nmethod::verify_interrupt_point(address call_site) { + // This code does not work in release mode since + // owns_lock only is available in debug mode. + CompiledIC* ic = NULL; + Thread *cur = Thread::current(); + if (CompiledIC_lock->owner() == cur || + ((cur->is_VM_thread() || cur->is_ConcurrentGC_thread()) && + SafepointSynchronize::is_at_safepoint())) { + ic = CompiledIC_at(call_site); + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } else { + MutexLocker ml_verify (CompiledIC_lock); + ic = CompiledIC_at(call_site); + } + PcDesc* pd = pc_desc_at(ic->end_of_call()); + assert(pd != NULL, "PcDesc must exist"); + for (ScopeDesc* sd = new ScopeDesc(this, pd->scope_decode_offset(), + pd->obj_decode_offset()); + !sd->is_top(); sd = sd->sender()) { + sd->verify(); + } +} + +void nmethod::verify_scopes() { + if( !method() ) return; // Runtime stubs have no scope + if (method()->is_native()) return; // Ignore stub methods. + // iterate through all interrupt point + // and verify the debug information is valid. + RelocIterator iter((nmethod*)this); + while (iter.next()) { + address stub = NULL; + switch (iter.type()) { + case relocInfo::virtual_call_type: + verify_interrupt_point(iter.addr()); + break; + case relocInfo::opt_virtual_call_type: + stub = iter.opt_virtual_call_reloc()->static_stub(); + verify_interrupt_point(iter.addr()); + break; + case relocInfo::static_call_type: + stub = iter.static_call_reloc()->static_stub(); + //verify_interrupt_point(iter.addr()); + break; + case relocInfo::runtime_call_type: + address destination = iter.reloc()->value(); + // Right now there is no way to find out which entries support + // an interrupt point. It would be nice if we had this + // information in a table. + break; + } + assert(stub == NULL || stub_contains(stub), "static call stub outside stub section"); + } +} + + +// ----------------------------------------------------------------------------- +// Non-product code +#ifndef PRODUCT + +void nmethod::check_store() { + // Make sure all oops in the compiled code are tenured + + RelocIterator iter(this); + while (iter.next()) { + if (iter.type() == relocInfo::oop_type) { + oop_Relocation* reloc = iter.oop_reloc(); + oop obj = reloc->oop_value(); + if (obj != NULL && !obj->is_perm()) { + fatal("must be permanent oop in compiled code"); + } + } + } +} + + +// Printing operations + +void nmethod::print() const { + ResourceMark rm; + ttyLocker ttyl; // keep the following output all in one block + + tty->print("Compiled "); + + if (is_compiled_by_c1()) { + tty->print("(c1) "); + } else if (is_compiled_by_c2()) { + tty->print("(c2) "); + } else { + assert(is_native_method(), "Who else?"); + tty->print("(nm) "); + } + + print_on(tty, "nmethod"); + tty->cr(); + if (WizardMode) { + tty->print("((nmethod*) "INTPTR_FORMAT ") ", this); + tty->print(" for method " INTPTR_FORMAT , (address)method()); + tty->print(" { "); + if (version()) tty->print("v%d ", version()); + if (level()) tty->print("l%d ", level()); + if (is_in_use()) tty->print("in_use "); + if (is_not_entrant()) tty->print("not_entrant "); + if (is_zombie()) tty->print("zombie "); + if (is_unloaded()) tty->print("unloaded "); + tty->print_cr("}:"); + } + if (size () > 0) tty->print_cr(" total in heap [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + (address)this, + (address)this + size(), + size()); + if (relocation_size () > 0) tty->print_cr(" relocation [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + relocation_begin(), + relocation_end(), + relocation_size()); + if (code_size () > 0) tty->print_cr(" main code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + code_begin(), + code_end(), + code_size()); + if (stub_size () > 0) tty->print_cr(" stub code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + stub_begin(), + stub_end(), + stub_size()); + if (consts_size () > 0) tty->print_cr(" constants [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + consts_begin(), + consts_end(), + consts_size()); + if (scopes_data_size () > 0) tty->print_cr(" scopes data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + scopes_data_begin(), + scopes_data_end(), + scopes_data_size()); + if (scopes_pcs_size () > 0) tty->print_cr(" scopes pcs [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + scopes_pcs_begin(), + scopes_pcs_end(), + scopes_pcs_size()); + if (dependencies_size () > 0) tty->print_cr(" dependencies [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + dependencies_begin(), + dependencies_end(), + dependencies_size()); + if (handler_table_size() > 0) tty->print_cr(" handler table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + handler_table_begin(), + handler_table_end(), + handler_table_size()); + if (nul_chk_table_size() > 0) tty->print_cr(" nul chk table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + nul_chk_table_begin(), + nul_chk_table_end(), + nul_chk_table_size()); + if (oops_size () > 0) tty->print_cr(" oops [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + oops_begin(), + oops_end(), + oops_size()); +} + + +void nmethod::print_scopes() { + // Find the first pc desc for all scopes in the code and print it. + ResourceMark rm; + for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) { + if (p->scope_decode_offset() == DebugInformationRecorder::serialized_null) + continue; + + ScopeDesc* sd = scope_desc_at(p->real_pc(this)); + sd->print_on(tty, p); + } +} + +void nmethod::print_dependencies() { + ResourceMark rm; + ttyLocker ttyl; // keep the following output all in one block + tty->print_cr("Dependencies:"); + for (Dependencies::DepStream deps(this); deps.next(); ) { + deps.print_dependency(); + klassOop ctxk = deps.context_type(); + if (ctxk != NULL) { + Klass* k = Klass::cast(ctxk); + if (k->oop_is_instance() && ((instanceKlass*)k)->is_dependent_nmethod(this)) { + tty->print(" [nmethod<=klass]%s", k->external_name()); + } + } + deps.log_dependency(); // put it into the xml log also + } +} + + +void nmethod::print_code() { + HandleMark hm; + ResourceMark m; + Disassembler().decode(this); +} + + +void nmethod::print_relocations() { + ResourceMark m; // in case methods get printed via the debugger + tty->print_cr("relocations:"); + RelocIterator iter(this); + iter.print(); + if (UseRelocIndex) { + jint* index_end = (jint*)relocation_end() - 1; + jint index_size = *index_end; + jint* index_start = (jint*)( (address)index_end - index_size ); + tty->print_cr(" index @" INTPTR_FORMAT ": index_size=%d", index_start, index_size); + if (index_size > 0) { + jint* ip; + for (ip = index_start; ip+2 <= index_end; ip += 2) + tty->print_cr(" (%d %d) addr=" INTPTR_FORMAT " @" INTPTR_FORMAT, + ip[0], + ip[1], + header_end()+ip[0], + relocation_begin()-1+ip[1]); + for (; ip < index_end; ip++) + tty->print_cr(" (%d ?)", ip[0]); + tty->print_cr(" @" INTPTR_FORMAT ": index_size=%d", ip, *ip++); + tty->print_cr("reloc_end @" INTPTR_FORMAT ":", ip); + } + } +} + + +void nmethod::print_pcs() { + ResourceMark m; // in case methods get printed via debugger + tty->print_cr("pc-bytecode offsets:"); + for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) { + p->print(this); + } +} + + +const char* nmethod::reloc_string_for(u_char* begin, u_char* end) { + RelocIterator iter(this, begin, end); + bool have_one = false; + while (iter.next()) { + have_one = true; + switch (iter.type()) { + case relocInfo::none: return "no_reloc"; + case relocInfo::oop_type: { + stringStream st; + oop_Relocation* r = iter.oop_reloc(); + oop obj = r->oop_value(); + st.print("oop("); + if (obj == NULL) st.print("NULL"); + else obj->print_value_on(&st); + st.print(")"); + return st.as_string(); + } + case relocInfo::virtual_call_type: return "virtual_call"; + case relocInfo::opt_virtual_call_type: return "optimized virtual_call"; + case relocInfo::static_call_type: return "static_call"; + case relocInfo::static_stub_type: return "static_stub"; + case relocInfo::runtime_call_type: return "runtime_call"; + case relocInfo::external_word_type: return "external_word"; + case relocInfo::internal_word_type: return "internal_word"; + case relocInfo::section_word_type: return "section_word"; + case relocInfo::poll_type: return "poll"; + case relocInfo::poll_return_type: return "poll_return"; + case relocInfo::type_mask: return "type_bit_mask"; + } + } + return have_one ? "other" : NULL; +} + + +// Return a the last scope in (begin..end] +ScopeDesc* nmethod::scope_desc_in(address begin, address end) { + PcDesc* p = pc_desc_near(begin+1); + if (p != NULL && p->real_pc(this) <= end) { + return new ScopeDesc(this, p->scope_decode_offset(), + p->obj_decode_offset()); + } + return NULL; +} + +void nmethod::print_code_comment_on(outputStream* st, int column, u_char* begin, u_char* end) { + // First, find an oopmap in (begin, end]. + // We use the odd half-closed interval so that oop maps and scope descs + // which are tied to the byte after a call are printed with the call itself. + address base = instructions_begin(); + OopMapSet* oms = oop_maps(); + if (oms != NULL) { + for (int i = 0, imax = oms->size(); i < imax; i++) { + OopMap* om = oms->at(i); + address pc = base + om->offset(); + if (pc > begin) { + if (pc <= end) { + st->fill_to(column); + if (st == tty) { + st->print("; OopMap "); + om->print(); + tty->cr(); + } else { + st->print_cr("; OopMap #%d offset:%d", i, om->offset()); + } + } + break; + } + } + } + ScopeDesc* sd = scope_desc_in(begin, end); + if (sd != NULL) { + st->fill_to(column); + if (sd->bci() == SynchronizationEntryBCI) { + st->print(";*synchronization entry"); + } else { + if (sd->method().is_null()) { + tty->print("method is NULL"); + } else if (sd->method()->is_native()) { + tty->print("method is native"); + } else { + address bcp = sd->method()->bcp_from(sd->bci()); + Bytecodes::Code bc = Bytecodes::java_code_at(bcp); + st->print(";*%s", Bytecodes::name(bc)); + switch (bc) { + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokeinterface: + { + Bytecode_invoke* invoke = Bytecode_invoke_at(sd->method(), sd->bci()); + st->print(" "); + if (invoke->name() != NULL) + invoke->name()->print_symbol_on(st); + else + st->print(""); + break; + } + case Bytecodes::_getfield: + case Bytecodes::_putfield: + case Bytecodes::_getstatic: + case Bytecodes::_putstatic: + { + methodHandle sdm = sd->method(); + Bytecode_field* field = Bytecode_field_at(sdm(), sdm->bcp_from(sd->bci())); + constantPoolOop sdmc = sdm->constants(); + symbolOop name = sdmc->name_ref_at(field->index()); + st->print(" "); + if (name != NULL) + name->print_symbol_on(st); + else + st->print(""); + } + } + } + } + st->cr(); + // Print all scopes + for (;sd != NULL; sd = sd->sender()) { + st->fill_to(column); + st->print("; -"); + if (sd->method().is_null()) { + tty->print("method is NULL"); + } else { + sd->method()->print_short_name(st); + } + int lineno = sd->method()->line_number_from_bci(sd->bci()); + if (lineno != -1) { + st->print("@%d (line %d)", sd->bci(), lineno); + } else { + st->print("@%d", sd->bci()); + } + st->cr(); + } + } + + // Print relocation information + const char* str = reloc_string_for(begin, end); + if (str != NULL) { + if (sd != NULL) st->cr(); + st->fill_to(column); + st->print("; {%s}", str); + } + int cont_offset = ImplicitExceptionTable(this).at(begin - instructions_begin()); + if (cont_offset != 0) { + st->fill_to(column); + st->print("; implicit exception: dispatches to " INTPTR_FORMAT, instructions_begin() + cont_offset); + } + +} + +void nmethod::print_value_on(outputStream* st) const { + print_on(st, "nmethod"); +} + +void nmethod::print_calls(outputStream* st) { + RelocIterator iter(this); + while (iter.next()) { + switch (iter.type()) { + case relocInfo::virtual_call_type: + case relocInfo::opt_virtual_call_type: { + VerifyMutexLocker mc(CompiledIC_lock); + CompiledIC_at(iter.reloc())->print(); + break; + } + case relocInfo::static_call_type: + st->print_cr("Static call at " INTPTR_FORMAT, iter.reloc()->addr()); + compiledStaticCall_at(iter.reloc())->print(); + break; + } + } +} + +void nmethod::print_handler_table() { + ExceptionHandlerTable(this).print(); +} + +void nmethod::print_nul_chk_table() { + ImplicitExceptionTable(this).print(instructions_begin()); +} + +void nmethod::print_statistics() { + ttyLocker ttyl; + if (xtty != NULL) xtty->head("statistics type='nmethod'"); + nmethod_stats.print_native_nmethod_stats(); + nmethod_stats.print_nmethod_stats(); + DebugInformationRecorder::print_statistics(); + nmethod_stats.print_pc_stats(); + Dependencies::print_statistics(); + if (xtty != NULL) xtty->tail("statistics"); +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/code/nmethod.hpp b/hotspot/src/share/vm/code/nmethod.hpp new file mode 100644 index 00000000000..0b1c61977e3 --- /dev/null +++ b/hotspot/src/share/vm/code/nmethod.hpp @@ -0,0 +1,578 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This class is used internally by nmethods, to cache +// exception/pc/handler information. + +class ExceptionCache : public CHeapObj { + friend class VMStructs; + private: + static address _unwind_handler; + enum { cache_size = 16 }; + klassOop _exception_type; + address _pc[cache_size]; + address _handler[cache_size]; + int _count; + ExceptionCache* _next; + + address pc_at(int index) { assert(index >= 0 && index < count(),""); return _pc[index]; } + void set_pc_at(int index, address a) { assert(index >= 0 && index < cache_size,""); _pc[index] = a; } + address handler_at(int index) { assert(index >= 0 && index < count(),""); return _handler[index]; } + void set_handler_at(int index, address a) { assert(index >= 0 && index < cache_size,""); _handler[index] = a; } + int count() { return _count; } + void increment_count() { _count++; } + + public: + + ExceptionCache(Handle exception, address pc, address handler); + + klassOop exception_type() { return _exception_type; } + klassOop* exception_type_addr() { return &_exception_type; } + ExceptionCache* next() { return _next; } + void set_next(ExceptionCache *ec) { _next = ec; } + + address match(Handle exception, address pc); + bool match_exception_with_space(Handle exception) ; + address test_address(address addr); + bool add_address_and_handler(address addr, address handler) ; + + static address unwind_handler() { return _unwind_handler; } +}; + + +// cache pc descs found in earlier inquiries +class PcDescCache VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + enum { cache_size = 4 }; + PcDesc* _last_pc_desc; // most recent pc_desc found + PcDesc* _pc_descs[cache_size]; // last cache_size pc_descs found + public: + PcDescCache() { debug_only(_last_pc_desc = NULL); } + void reset_to(PcDesc* initial_pc_desc); + PcDesc* find_pc_desc(int pc_offset, bool approximate); + void add_pc_desc(PcDesc* pc_desc); + PcDesc* last_pc_desc() { return _last_pc_desc; } +}; + + +// nmethods (native methods) are the compiled code versions of Java methods. + +struct nmFlags { + friend class VMStructs; + unsigned int version:8; // version number (0 = first version) + unsigned int level:4; // optimization level + unsigned int age:4; // age (in # of sweep steps) + + unsigned int state:2; // {alive, zombie, unloaded) + + unsigned int isUncommonRecompiled:1; // recompiled because of uncommon trap? + unsigned int isToBeRecompiled:1; // to be recompiled as soon as it matures + unsigned int hasFlushedDependencies:1; // Used for maintenance of dependencies + unsigned int markedForReclamation:1; // Used by NMethodSweeper + + unsigned int has_unsafe_access:1; // May fault due to unsafe access. + + void clear(); +}; + + +// A nmethod contains: +// - header (the nmethod structure) +// [Relocation] +// - relocation information +// - constant part (doubles, longs and floats used in nmethod) +// [Code] +// - code body +// - exception handler +// - stub code +// [Debugging information] +// - oop array +// - data array +// - pcs +// [Exception handler table] +// - handler entry point array +// [Implicit Null Pointer exception table] +// - implicit null table array + +class Dependencies; +class ExceptionHandlerTable; +class ImplicitExceptionTable; +class AbstractCompiler; +class xmlStream; + +class nmethod : public CodeBlob { + friend class VMStructs; + friend class NMethodSweeper; + private: + // Shared fields for all nmethod's + static int _zombie_instruction_size; + + methodOop _method; + int _entry_bci; // != InvocationEntryBci if this nmethod is an on-stack replacement method + + nmethod* _link; // To support simple linked-list chaining of nmethods + + AbstractCompiler* _compiler; // The compiler which compiled this nmethod + + // Offsets for different nmethod parts + int _exception_offset; + // All deoptee's will resume execution at this location described by this offset + int _deoptimize_offset; + int _stub_offset; + int _consts_offset; + int _scopes_data_offset; + int _scopes_pcs_offset; + int _dependencies_offset; + int _handler_table_offset; + int _nul_chk_table_offset; + int _nmethod_end_offset; + + // location in frame (offset for sp) that deopt can store the original + // pc during a deopt. + int _orig_pc_offset; + + int _compile_id; // which compilation made this nmethod + int _comp_level; // compilation level + + // offsets for entry points + address _entry_point; // entry point with class check + address _verified_entry_point; // entry point without class check + address _osr_entry_point; // entry point for on stack replacement + + nmFlags flags; // various flags to keep track of nmethod state + bool _markedForDeoptimization; // Used for stack deoptimization + enum { alive = 0, + not_entrant = 1, // uncommon trap has happend but activations may still exist + zombie = 2, + unloaded = 3 }; + + // used by jvmti to track if an unload event has been posted for this nmethod. + bool _unload_reported; + + NOT_PRODUCT(bool _has_debug_info; ) + + // Nmethod Flushing lock (if non-zero, then the nmethod is not removed) + jint _lock_count; + + // not_entrant method removal. Each mark_sweep pass will update + // this mark to current sweep invocation count if it is seen on the + // stack. An not_entrant method can be removed when there is no + // more activations, i.e., when the _stack_traversal_mark is less than + // current sweep traversal index. + long _stack_traversal_mark; + + ExceptionCache *_exception_cache; + PcDescCache _pc_desc_cache; + + // These are only used for compiled synchronized native methods to + // locate the owner and stack slot for the BasicLock so that we can + // properly revoke the bias of the owner if necessary. They are + // needed because there is no debug information for compiled native + // wrappers and the oop maps are insufficient to allow + // frame::retrieve_receiver() to work. Currently they are expected + // to be byte offsets from the Java stack pointer for maximum code + // sharing between platforms. Note that currently biased locking + // will never cause Class instances to be biased but this code + // handles the static synchronized case as well. + ByteSize _compiled_synchronized_native_basic_lock_owner_sp_offset; + ByteSize _compiled_synchronized_native_basic_lock_sp_offset; + + friend class nmethodLocker; + + // For native wrappers + nmethod(methodOop method, + int nmethod_size, + CodeOffsets* offsets, + CodeBuffer *code_buffer, + int frame_size, + ByteSize basic_lock_owner_sp_offset, /* synchronized natives only */ + ByteSize basic_lock_sp_offset, /* synchronized natives only */ + OopMapSet* oop_maps); + + // Creation support + nmethod(methodOop method, + int nmethod_size, + int compile_id, + int entry_bci, + CodeOffsets* offsets, + int orig_pc_offset, + DebugInformationRecorder *recorder, + Dependencies* dependencies, + CodeBuffer *code_buffer, + int frame_size, + OopMapSet* oop_maps, + ExceptionHandlerTable* handler_table, + ImplicitExceptionTable* nul_chk_table, + AbstractCompiler* compiler, + int comp_level); + + // helper methods + void* operator new(size_t size, int nmethod_size); + void check_store(); + + const char* reloc_string_for(u_char* begin, u_char* end); + void make_not_entrant_or_zombie(int state); + void inc_decompile_count(); + + // used to check that writes to nmFlags are done consistently. + static void check_safepoint() PRODUCT_RETURN; + + // Used to manipulate the exception cache + void add_exception_cache_entry(ExceptionCache* new_entry); + ExceptionCache* exception_cache_entry_for_exception(Handle exception); + + // Inform external interfaces that a compiled method has been unloaded + inline void post_compiled_method_unload(); + + public: + // create nmethod with entry_bci + static nmethod* new_nmethod(methodHandle method, + int compile_id, + int entry_bci, + CodeOffsets* offsets, + int orig_pc_offset, + DebugInformationRecorder* recorder, + Dependencies* dependencies, + CodeBuffer *code_buffer, + int frame_size, + OopMapSet* oop_maps, + ExceptionHandlerTable* handler_table, + ImplicitExceptionTable* nul_chk_table, + AbstractCompiler* compiler, + int comp_level); + + static nmethod* new_native_nmethod(methodHandle method, + CodeBuffer *code_buffer, + int vep_offset, + int frame_complete, + int frame_size, + ByteSize receiver_sp_offset, + ByteSize basic_lock_sp_offset, + OopMapSet* oop_maps); + + // accessors + methodOop method() const { return _method; } + AbstractCompiler* compiler() const { return _compiler; } + +#ifndef PRODUCT + bool has_debug_info() const { return _has_debug_info; } + void set_has_debug_info(bool f) { _has_debug_info = false; } +#endif // NOT PRODUCT + + // type info + bool is_nmethod() const { return true; } + bool is_java_method() const { return !method()->is_native(); } + bool is_native_method() const { return method()->is_native(); } + bool is_osr_method() const { return _entry_bci != InvocationEntryBci; } + bool is_osr_only_method() const { return is_osr_method(); } + + bool is_compiled_by_c1() const; + bool is_compiled_by_c2() const; + + // boundaries for different parts + address code_begin () const { return _entry_point; } + address code_end () const { return header_begin() + _stub_offset ; } + address exception_begin () const { return header_begin() + _exception_offset ; } + address deopt_handler_begin() const { return header_begin() + _deoptimize_offset ; } + address stub_begin () const { return header_begin() + _stub_offset ; } + address stub_end () const { return header_begin() + _consts_offset ; } + address consts_begin () const { return header_begin() + _consts_offset ; } + address consts_end () const { return header_begin() + _scopes_data_offset ; } + address scopes_data_begin () const { return header_begin() + _scopes_data_offset ; } + address scopes_data_end () const { return header_begin() + _scopes_pcs_offset ; } + PcDesc* scopes_pcs_begin () const { return (PcDesc*)(header_begin() + _scopes_pcs_offset ); } + PcDesc* scopes_pcs_end () const { return (PcDesc*)(header_begin() + _dependencies_offset); } + address dependencies_begin () const { return header_begin() + _dependencies_offset ; } + address dependencies_end () const { return header_begin() + _handler_table_offset ; } + address handler_table_begin() const { return header_begin() + _handler_table_offset ; } + address handler_table_end () const { return header_begin() + _nul_chk_table_offset ; } + address nul_chk_table_begin() const { return header_begin() + _nul_chk_table_offset ; } + address nul_chk_table_end () const { return header_begin() + _nmethod_end_offset ; } + + int code_size () const { return code_end () - code_begin (); } + int stub_size () const { return stub_end () - stub_begin (); } + int consts_size () const { return consts_end () - consts_begin (); } + int scopes_data_size () const { return scopes_data_end () - scopes_data_begin (); } + int scopes_pcs_size () const { return (intptr_t)scopes_pcs_end () - (intptr_t)scopes_pcs_begin (); } + int dependencies_size () const { return dependencies_end () - dependencies_begin (); } + int handler_table_size() const { return handler_table_end() - handler_table_begin(); } + int nul_chk_table_size() const { return nul_chk_table_end() - nul_chk_table_begin(); } + + int total_size () const; + + bool code_contains (address addr) const { return code_begin () <= addr && addr < code_end (); } + bool stub_contains (address addr) const { return stub_begin () <= addr && addr < stub_end (); } + bool consts_contains (address addr) const { return consts_begin () <= addr && addr < consts_end (); } + bool scopes_data_contains (address addr) const { return scopes_data_begin () <= addr && addr < scopes_data_end (); } + bool scopes_pcs_contains (PcDesc* addr) const { return scopes_pcs_begin () <= addr && addr < scopes_pcs_end (); } + bool handler_table_contains(address addr) const { return handler_table_begin() <= addr && addr < handler_table_end(); } + bool nul_chk_table_contains(address addr) const { return nul_chk_table_begin() <= addr && addr < nul_chk_table_end(); } + + // entry points + address entry_point() const { return _entry_point; } // normal entry point + address verified_entry_point() const { return _verified_entry_point; } // if klass is correct + + // flag accessing and manipulation + bool is_in_use() const { return flags.state == alive; } + bool is_alive() const { return flags.state == alive || flags.state == not_entrant; } + bool is_not_entrant() const { return flags.state == not_entrant; } + bool is_zombie() const { return flags.state == zombie; } + bool is_unloaded() const { return flags.state == unloaded; } + + // Make the nmethod non entrant. The nmethod will continue to be alive. + // It is used when an uncommon trap happens. + void make_not_entrant() { make_not_entrant_or_zombie(not_entrant); } + void make_zombie() { make_not_entrant_or_zombie(zombie); } + + // used by jvmti to track if the unload event has been reported + bool unload_reported() { return _unload_reported; } + void set_unload_reported() { _unload_reported = true; } + + bool is_marked_for_deoptimization() const { return _markedForDeoptimization; } + void mark_for_deoptimization() { _markedForDeoptimization = true; } + + void make_unloaded(BoolObjectClosure* is_alive, oop cause); + + bool has_dependencies() { return dependencies_size() != 0; } + void flush_dependencies(BoolObjectClosure* is_alive); + bool has_flushed_dependencies() { return flags.hasFlushedDependencies; } + void set_has_flushed_dependencies() { + check_safepoint(); + assert(!has_flushed_dependencies(), "should only happen once"); + flags.hasFlushedDependencies = 1; + } + + bool is_marked_for_reclamation() const { return flags.markedForReclamation; } + void mark_for_reclamation() { check_safepoint(); flags.markedForReclamation = 1; } + void unmark_for_reclamation() { check_safepoint(); flags.markedForReclamation = 0; } + + bool has_unsafe_access() const { return flags.has_unsafe_access; } + void set_has_unsafe_access(bool z) { flags.has_unsafe_access = z; } + + int level() const { return flags.level; } + void set_level(int newLevel) { check_safepoint(); flags.level = newLevel; } + + int comp_level() const { return _comp_level; } + + int version() const { return flags.version; } + void set_version(int v); + + // Sweeper support + long stack_traversal_mark() { return _stack_traversal_mark; } + void set_stack_traversal_mark(long l) { _stack_traversal_mark = l; } + + // Exception cache support + ExceptionCache* exception_cache() const { return _exception_cache; } + void set_exception_cache(ExceptionCache *ec) { _exception_cache = ec; } + address handler_for_exception_and_pc(Handle exception, address pc); + void add_handler_for_exception_and_pc(Handle exception, address pc, address handler); + void remove_from_exception_cache(ExceptionCache* ec); + + // implicit exceptions support + address continuation_for_implicit_exception(address pc); + + // On-stack replacement support + int osr_entry_bci() const { assert(_entry_bci != InvocationEntryBci, "wrong kind of nmethod"); return _entry_bci; } + address osr_entry() const { assert(_entry_bci != InvocationEntryBci, "wrong kind of nmethod"); return _osr_entry_point; } + void invalidate_osr_method(); + nmethod* link() const { return _link; } + void set_link(nmethod *n) { _link = n; } + + // tells whether frames described by this nmethod can be deoptimized + // note: native wrappers cannot be deoptimized. + bool can_be_deoptimized() const { return is_java_method(); } + + // Inline cache support + void clear_inline_caches(); + void cleanup_inline_caches(); + bool inlinecache_check_contains(address addr) const { + return (addr >= instructions_begin() && addr < verified_entry_point()); + } + + // unlink and deallocate this nmethod + // Only NMethodSweeper class is expected to use this. NMethodSweeper is not + // expected to use any other private methods/data in this class. + + protected: + void flush(); + + public: + // If returning true, it is unsafe to remove this nmethod even though it is a zombie + // nmethod, since the VM might have a reference to it. Should only be called from a safepoint. + bool is_locked_by_vm() const { return _lock_count >0; } + + // See comment at definition of _last_seen_on_stack + void mark_as_seen_on_stack(); + bool can_not_entrant_be_converted(); + + // Evolution support. We make old (discarded) compiled methods point to new methodOops. + void set_method(methodOop method) { _method = method; } + + // GC support + void do_unloading(BoolObjectClosure* is_alive, OopClosure* keep_alive, + bool unloading_occurred); + bool can_unload(BoolObjectClosure* is_alive, OopClosure* keep_alive, + oop* root, bool unloading_occurred); + + void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, + OopClosure* f); + void oops_do(OopClosure* f); + + // ScopeDesc for an instruction + ScopeDesc* scope_desc_at(address pc); + + private: + ScopeDesc* scope_desc_in(address begin, address end); + + address* orig_pc_addr(const frame* fr ) { return (address*) ((address)fr->unextended_sp() + _orig_pc_offset); } + + PcDesc* find_pc_desc_internal(address pc, bool approximate); + + PcDesc* find_pc_desc(address pc, bool approximate) { + PcDesc* desc = _pc_desc_cache.last_pc_desc(); + if (desc != NULL && desc->pc_offset() == pc - instructions_begin()) { + return desc; + } + return find_pc_desc_internal(pc, approximate); + } + + public: + // ScopeDesc retrieval operation + PcDesc* pc_desc_at(address pc) { return find_pc_desc(pc, false); } + // pc_desc_near returns the first PcDesc at or after the givne pc. + PcDesc* pc_desc_near(address pc) { return find_pc_desc(pc, true); } + + public: + // copying of debugging information + void copy_scopes_pcs(PcDesc* pcs, int count); + void copy_scopes_data(address buffer, int size); + + // deopt + // return true is the pc is one would expect if the frame is being deopted. + bool is_deopt_pc(address pc); + // Accessor/mutator for the original pc of a frame before a frame was deopted. + address get_original_pc(const frame* fr) { return *orig_pc_addr(fr); } + void set_original_pc(const frame* fr, address pc) { *orig_pc_addr(fr) = pc; } + + // jvmti support: + void post_compiled_method_load_event(); + + // verify operations + void verify(); + void verify_scopes(); + void verify_interrupt_point(address interrupt_point); + + // printing support + void print() const PRODUCT_RETURN; + void print_code() PRODUCT_RETURN; + void print_relocations() PRODUCT_RETURN; + void print_pcs() PRODUCT_RETURN; + void print_scopes() PRODUCT_RETURN; + void print_dependencies() PRODUCT_RETURN; + void print_value_on(outputStream* st) const PRODUCT_RETURN; + void print_calls(outputStream* st) PRODUCT_RETURN; + void print_handler_table() PRODUCT_RETURN; + void print_nul_chk_table() PRODUCT_RETURN; + void print_nmethod(bool print_code) PRODUCT_RETURN; + + void print_on(outputStream* st, const char* title) const; + + // Logging + void log_identity(xmlStream* log) const; + void log_new_nmethod() const; + void log_state_change(int state) const; + + // Prints a comment for one native instruction (reloc info, pc desc) + void print_code_comment_on(outputStream* st, int column, address begin, address end) PRODUCT_RETURN; + static void print_statistics() PRODUCT_RETURN; + + // Compiler task identification. Note that all OSR methods + // are numbered in an independent sequence if CICountOSR is true, + // and native method wrappers are also numbered independently if + // CICountNative is true. + int compile_id() const { return _compile_id; } + const char* compile_kind() const; + + // For debugging + // CompiledIC* IC_at(char* p) const; + // PrimitiveIC* primitiveIC_at(char* p) const; + oop embeddedOop_at(address p); + + // tells if any of this method's dependencies have been invalidated + // (this is expensive!) + bool check_all_dependencies(); + + // tells if this compiled method is dependent on the given changes, + // and the changes have invalidated it + bool check_dependency_on(DepChange& changes); + + // Evolution support. Tells if this compiled method is dependent on any of + // methods m() of class dependee, such that if m() in dependee is replaced, + // this compiled method will have to be deoptimized. + bool is_evol_dependent_on(klassOop dependee); + + // Fast breakpoint support. Tells if this compiled method is + // dependent on the given method. Returns true if this nmethod + // corresponds to the given method as well. + bool is_dependent_on_method(methodOop dependee); + + // is it ok to patch at address? + bool is_patchable_at(address instr_address); + + // UseBiasedLocking support + ByteSize compiled_synchronized_native_basic_lock_owner_sp_offset() { + return _compiled_synchronized_native_basic_lock_owner_sp_offset; + } + ByteSize compiled_synchronized_native_basic_lock_sp_offset() { + return _compiled_synchronized_native_basic_lock_sp_offset; + } + + // support for code generation + static int verified_entry_point_offset() { return offset_of(nmethod, _verified_entry_point); } + static int osr_entry_point_offset() { return offset_of(nmethod, _osr_entry_point); } + static int entry_bci_offset() { return offset_of(nmethod, _entry_bci); } + +}; + +// Locks an nmethod so its code will not get removed, even if it is a zombie/not_entrant method +class nmethodLocker : public StackObj { + nmethod* _nm; + + static void lock_nmethod(nmethod* nm); // note: nm can be NULL + static void unlock_nmethod(nmethod* nm); // (ditto) + + public: + nmethodLocker(address pc); // derive nm from pc + nmethodLocker(nmethod *nm) { _nm = nm; lock_nmethod(_nm); } + nmethodLocker() { _nm = NULL; } + ~nmethodLocker() { unlock_nmethod(_nm); } + + nmethod* code() { return _nm; } + void set_code(nmethod* new_nm) { + unlock_nmethod(_nm); // note: This works even if _nm==new_nm. + _nm = new_nm; + lock_nmethod(_nm); + } +}; diff --git a/hotspot/src/share/vm/code/oopRecorder.cpp b/hotspot/src/share/vm/code/oopRecorder.cpp new file mode 100644 index 00000000000..fd1697490ac --- /dev/null +++ b/hotspot/src/share/vm/code/oopRecorder.cpp @@ -0,0 +1,156 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_oopRecorder.cpp.incl" + +#ifdef ASSERT +int OopRecorder::_find_index_calls = 0; +int OopRecorder::_hit_indexes = 0; +int OopRecorder::_missed_indexes = 0; +#endif //ASSERT + + +OopRecorder::OopRecorder(Arena* arena) { + _handles = NULL; + _indexes = NULL; + _arena = arena; + _complete = false; +} + +OopRecorder::IndexCache::IndexCache() { + assert(first_index > 0, "initial zero state of cache must be invalid index"); + Copy::zero_to_bytes(&_cache[0], sizeof(_cache)); +} + +int OopRecorder::oop_size() { + _complete = true; + if (_handles == NULL) return 0; + return _handles->length() * sizeof(oop); +} + +void OopRecorder::copy_to(CodeBlob* code) { + assert(_complete, "must be frozen"); + maybe_initialize(); // get non-null handles, even if we have no oops + code->copy_oops(_handles); +} + +void OopRecorder::maybe_initialize() { + if (_handles == NULL) { + if (_arena != NULL) { + _handles = new(_arena) GrowableArray(_arena, 10, 0, 0); + _no_finds = new(_arena) GrowableArray( _arena, 10, 0, 0); + } else { + _handles = new GrowableArray(10, 0, 0); + _no_finds = new GrowableArray( 10, 0, 0); + } + } +} + + +jobject OopRecorder::handle_at(int index) { + // there is always a NULL virtually present as first object + if (index == null_index) return NULL; + return _handles->at(index - first_index); +} + + +// Local definition. Used only in this module. +inline bool OopRecorder::is_real_jobject(jobject h) { + return h != NULL && h != (jobject)Universe::non_oop_word(); +} + + +int OopRecorder::add_handle(jobject h, bool make_findable) { + assert(!_complete, "cannot allocate more elements after size query"); + maybe_initialize(); + // indexing uses 1 as an origin--0 means null + int index = _handles->length() + first_index; + _handles->append(h); + + // Support correct operation of find_index(). + assert(!(make_findable && !is_real_jobject(h)), "nulls are not findable"); + if (make_findable) { + // This index may be returned from find_index(). + if (_indexes != NULL) { + int* cloc = _indexes->cache_location(h); + _indexes->set_cache_location_index(cloc, index); + } else if (index == index_cache_threshold && _arena != NULL) { + _indexes = new(_arena) IndexCache(); + for (int i = 0; i < _handles->length(); i++) { + // Load the cache with pre-existing elements. + int index0 = i + first_index; + if (_no_finds->contains(index0)) continue; + int* cloc = _indexes->cache_location(_handles->at(i)); + _indexes->set_cache_location_index(cloc, index0); + } + } + } else if (is_real_jobject(h)) { + // Remember that this index is not to be returned from find_index(). + // This case is rare, because most or all uses of allocate_index pass + // a jobject argument of NULL or Universe::non_oop_word. + // Thus, the expected length of _no_finds is zero. + _no_finds->append(index); + } + + return index; +} + + +int OopRecorder::maybe_find_index(jobject h) { + debug_only(_find_index_calls++); + assert(!_complete, "cannot allocate more elements after size query"); + maybe_initialize(); + if (h == NULL) return null_index; + assert(is_real_jobject(h), "must be valid jobject"); + int* cloc = (_indexes == NULL)? NULL: _indexes->cache_location(h); + if (cloc != NULL) { + int cindex = _indexes->cache_location_index(cloc); + if (cindex == 0) { + return -1; // We know this handle is completely new. + } + if (cindex >= first_index && _handles->at(cindex - first_index) == h) { + debug_only(_hit_indexes++); + return cindex; + } + if (!_indexes->cache_location_collision(cloc)) { + return -1; // We know the current cache occupant is unique to that cloc. + } + } + + // Not found in cache, due to a cache collision. (Or, no cache at all.) + // Do a linear search, most recent to oldest. + for (int i = _handles->length() - 1; i >= 0; i--) { + if (_handles->at(i) == h) { + int findex = i + first_index; + if (_no_finds->contains(findex)) continue; // oops; skip this one + if (cloc != NULL) { + _indexes->set_cache_location_index(cloc, findex); + } + debug_only(_missed_indexes++); + return findex; + } + } + return -1; +} diff --git a/hotspot/src/share/vm/code/oopRecorder.hpp b/hotspot/src/share/vm/code/oopRecorder.hpp new file mode 100644 index 00000000000..7a8531515de --- /dev/null +++ b/hotspot/src/share/vm/code/oopRecorder.hpp @@ -0,0 +1,136 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Recording and retrieval of oop relocations in compiled code. + +class CodeBlob; + +class OopRecorder : public ResourceObj { + public: + // A two-way mapping from positive indexes to oop handles. + // The zero index is reserved for a constant (sharable) null. + // Indexes may not be negative. + + // Use the given arena to manage storage, if not NULL. + // By default, uses the current ResourceArea. + OopRecorder(Arena* arena = NULL); + + // Generate a new index on which CodeBlob::oop_addr_at will work. + // allocate_index and find_index never return the same index, + // and allocate_index never returns the same index twice. + // In fact, two successive calls to allocate_index return successive ints. + int allocate_index(jobject h) { + return add_handle(h, false); + } + + // For a given jobject, this will return the same index repeatedly. + // The index can later be given to oop_at to retrieve the oop. + // However, the oop must not be changed via CodeBlob::oop_addr_at. + int find_index(jobject h) { + int index = maybe_find_index(h); + if (index < 0) { // previously unallocated + index = add_handle(h, true); + } + return index; + } + + // variant of find_index which does not allocate if not found (yields -1) + int maybe_find_index(jobject h); + + // returns the size of the generated oop table, for sizing the CodeBlob. + // must be called after all oops are allocated! + int oop_size(); + + // Retrieve the oop handle at a given index. + jobject handle_at(int index); + + int element_count() { + // there is always a NULL virtually present as first object + return _handles->length() + first_index; + } + + // copy the generated oop table to CodeBlob + void copy_to(CodeBlob* code); // => code->copy_oops(_handles) + + bool is_unused() { return _handles == NULL && !_complete; } +#ifdef ASSERT + bool is_complete() { return _complete; } +#endif + + private: + // leaky hash table of handle => index, to help detect duplicate insertion + class IndexCache: public ResourceObj { + // This class is only used by the OopRecorder class. + friend class OopRecorder; + enum { + _log_cache_size = 9, + _cache_size = (1<<_log_cache_size), + // Index entries are ints. The LSBit is a collision indicator. + _collision_bit_shift = 0, + _collision_bit = 1, + _index_shift = _collision_bit_shift+1 + }; + int _cache[_cache_size]; + static juint cache_index(jobject handle) { + juint ci = (int) (intptr_t) handle; + ci ^= ci >> (BitsPerByte*2); + ci += ci >> (BitsPerByte*1); + return ci & (_cache_size-1); + } + int* cache_location(jobject handle) { + return &_cache[ cache_index(handle) ]; + } + static bool cache_location_collision(int* cloc) { + return ((*cloc) & _collision_bit) != 0; + } + static int cache_location_index(int* cloc) { + return (*cloc) >> _index_shift; + } + static void set_cache_location_index(int* cloc, int index) { + int cval0 = (*cloc); + int cval1 = (index << _index_shift); + if (cval0 != 0 && cval1 != cval0) cval1 += _collision_bit; + (*cloc) = cval1; + } + IndexCache(); + }; + + // Helper function; returns false for NULL or Universe::non_oop_word(). + inline bool is_real_jobject(jobject h); + + void maybe_initialize(); + int add_handle(jobject h, bool make_findable); + + enum { null_index = 0, first_index = 1, index_cache_threshold = 20 }; + + GrowableArray* _handles; // ordered list (first is always NULL) + GrowableArray* _no_finds; // all unfindable indexes; usually empty + IndexCache* _indexes; // map: jobject -> its probable index + Arena* _arena; + bool _complete; + +#ifdef ASSERT + static int _find_index_calls, _hit_indexes, _missed_indexes; +#endif +}; diff --git a/hotspot/src/share/vm/code/pcDesc.cpp b/hotspot/src/share/vm/code/pcDesc.cpp new file mode 100644 index 00000000000..da513017773 --- /dev/null +++ b/hotspot/src/share/vm/code/pcDesc.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_pcDesc.cpp.incl" + +PcDesc::PcDesc(int pc_offset, int scope_decode_offset, int obj_decode_offset) { + _pc_offset = pc_offset; + _scope_decode_offset = scope_decode_offset; + _obj_decode_offset = obj_decode_offset; +} + +address PcDesc::real_pc(const nmethod* code) const { + return code->instructions_begin() + pc_offset(); +} + +void PcDesc::print(nmethod* code) { +#ifndef PRODUCT + ResourceMark rm; + tty->print_cr("PcDesc(pc=0x%lx offset=%x):", real_pc(code), pc_offset()); + + if (scope_decode_offset() == DebugInformationRecorder::serialized_null) { + return; + } + + for (ScopeDesc* sd = code->scope_desc_at(real_pc(code)); + sd != NULL; + sd = sd->sender()) { + tty->print(" "); + sd->method()->print_short_name(tty); + tty->print(" @%d", sd->bci()); + tty->cr(); + } +#endif +} + +bool PcDesc::verify(nmethod* code) { + //Unimplemented(); + return true; +} diff --git a/hotspot/src/share/vm/code/pcDesc.hpp b/hotspot/src/share/vm/code/pcDesc.hpp new file mode 100644 index 00000000000..64cb3bb08a0 --- /dev/null +++ b/hotspot/src/share/vm/code/pcDesc.hpp @@ -0,0 +1,61 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// PcDescs map a physical PC (given as offset from start of nmethod) to +// the corresponding source scope and byte code index. + +class nmethod; + +class PcDesc VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + int _pc_offset; // offset from start of nmethod + int _scope_decode_offset; // offset for scope in nmethod + int _obj_decode_offset; + + public: + int pc_offset() const { return _pc_offset; } + int scope_decode_offset() const { return _scope_decode_offset; } + int obj_decode_offset() const { return _obj_decode_offset; } + + void set_pc_offset(int x) { _pc_offset = x; } + void set_scope_decode_offset(int x) { _scope_decode_offset = x; } + void set_obj_decode_offset(int x) { _obj_decode_offset = x; } + + // Constructor (only used for static in nmethod.cpp) + // Also used by ScopeDesc::sender()] + PcDesc(int pc_offset, int scope_decode_offset, int obj_decode_offset); + + enum { + // upper and lower exclusive limits real offsets: + lower_offset_limit = -1, + upper_offset_limit = (unsigned int)-1 >> 1 + }; + + // Returns the real pc + address real_pc(const nmethod* code) const; + + void print(nmethod* code); + bool verify(nmethod* code); +}; diff --git a/hotspot/src/share/vm/code/relocInfo.cpp b/hotspot/src/share/vm/code/relocInfo.cpp new file mode 100644 index 00000000000..42c72c70ea9 --- /dev/null +++ b/hotspot/src/share/vm/code/relocInfo.cpp @@ -0,0 +1,1188 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_relocInfo.cpp.incl" + + +const RelocationHolder RelocationHolder::none; // its type is relocInfo::none + + +// Implementation of relocInfo + +#ifdef ASSERT +relocInfo::relocInfo(relocType t, int off, int f) { + assert(t != data_prefix_tag, "cannot build a prefix this way"); + assert((t & type_mask) == t, "wrong type"); + assert((f & format_mask) == f, "wrong format"); + assert(off >= 0 && off < offset_limit(), "offset out off bounds"); + assert((off & (offset_unit-1)) == 0, "misaligned offset"); + (*this) = relocInfo(t, RAW_BITS, off, f); +} +#endif + +void relocInfo::initialize(CodeSection* dest, Relocation* reloc) { + relocInfo* data = this+1; // here's where the data might go + dest->set_locs_end(data); // sync end: the next call may read dest.locs_end + reloc->pack_data_to(dest); // maybe write data into locs, advancing locs_end + relocInfo* data_limit = dest->locs_end(); + if (data_limit > data) { + relocInfo suffix = (*this); + data_limit = this->finish_prefix((short*) data_limit); + // Finish up with the suffix. (Hack note: pack_data_to might edit this.) + *data_limit = suffix; + dest->set_locs_end(data_limit+1); + } +} + +relocInfo* relocInfo::finish_prefix(short* prefix_limit) { + assert(sizeof(relocInfo) == sizeof(short), "change this code"); + short* p = (short*)(this+1); + assert(prefix_limit >= p, "must be a valid span of data"); + int plen = prefix_limit - p; + if (plen == 0) { + debug_only(_value = 0xFFFF); + return this; // no data: remove self completely + } + if (plen == 1 && fits_into_immediate(p[0])) { + (*this) = immediate_relocInfo(p[0]); // move data inside self + return this+1; + } + // cannot compact, so just update the count and return the limit pointer + (*this) = prefix_relocInfo(plen); // write new datalen + assert(data() + datalen() == prefix_limit, "pointers must line up"); + return (relocInfo*)prefix_limit; +} + + +void relocInfo::set_type(relocType t) { + int old_offset = addr_offset(); + int old_format = format(); + (*this) = relocInfo(t, old_offset, old_format); + assert(type()==(int)t, "sanity check"); + assert(addr_offset()==old_offset, "sanity check"); + assert(format()==old_format, "sanity check"); +} + + +void relocInfo::set_format(int f) { + int old_offset = addr_offset(); + assert((f & format_mask) == f, "wrong format"); + _value = (_value & ~(format_mask << offset_width)) | (f << offset_width); + assert(addr_offset()==old_offset, "sanity check"); +} + + +void relocInfo::change_reloc_info_for_address(RelocIterator *itr, address pc, relocType old_type, relocType new_type) { + bool found = false; + while (itr->next() && !found) { + if (itr->addr() == pc) { + assert(itr->type()==old_type, "wrong relocInfo type found"); + itr->current()->set_type(new_type); + found=true; + } + } + assert(found, "no relocInfo found for pc"); +} + + +void relocInfo::remove_reloc_info_for_address(RelocIterator *itr, address pc, relocType old_type) { + change_reloc_info_for_address(itr, pc, old_type, none); +} + + +// ---------------------------------------------------------------------------------------------------- +// Implementation of RelocIterator + +void RelocIterator::initialize(CodeBlob* cb, address begin, address limit) { + initialize_misc(); + + if (cb == NULL && begin != NULL) { + // allow CodeBlob to be deduced from beginning address + cb = CodeCache::find_blob(begin); + } + assert(cb != NULL, "must be able to deduce nmethod from other arguments"); + + _code = cb; + _current = cb->relocation_begin()-1; + _end = cb->relocation_end(); + _addr = (address) cb->instructions_begin(); + + assert(!has_current(), "just checking"); + address code_end = cb->instructions_end(); + + assert(begin == NULL || begin >= cb->instructions_begin(), "in bounds"); + // FIX THIS assert(limit == NULL || limit <= code_end, "in bounds"); + set_limits(begin, limit); +} + + +RelocIterator::RelocIterator(CodeSection* cs, address begin, address limit) { + initialize_misc(); + + _current = cs->locs_start()-1; + _end = cs->locs_end(); + _addr = cs->start(); + _code = NULL; // Not cb->blob(); + + CodeBuffer* cb = cs->outer(); + assert((int)SECT_LIMIT == CodeBuffer::SECT_LIMIT, "my copy must be equal"); + for (int n = 0; n < (int)SECT_LIMIT; n++) { + _section_start[n] = cb->code_section(n)->start(); + } + + assert(!has_current(), "just checking"); + + assert(begin == NULL || begin >= cs->start(), "in bounds"); + assert(limit == NULL || limit <= cs->end(), "in bounds"); + set_limits(begin, limit); +} + + +enum { indexCardSize = 128 }; +struct RelocIndexEntry { + jint addr_offset; // offset from header_end of an addr() + jint reloc_offset; // offset from header_end of a relocInfo (prefix) +}; + + +static inline int num_cards(int code_size) { + return (code_size-1) / indexCardSize; +} + + +int RelocIterator::locs_and_index_size(int code_size, int locs_size) { + if (!UseRelocIndex) return locs_size; // no index + code_size = round_to(code_size, oopSize); + locs_size = round_to(locs_size, oopSize); + int index_size = num_cards(code_size) * sizeof(RelocIndexEntry); + // format of indexed relocs: + // relocation_begin: relocInfo ... + // index: (addr,reloc#) ... + // indexSize :relocation_end + return locs_size + index_size + BytesPerInt; +} + + +void RelocIterator::create_index(relocInfo* dest_begin, int dest_count, relocInfo* dest_end) { + address relocation_begin = (address)dest_begin; + address relocation_end = (address)dest_end; + int total_size = relocation_end - relocation_begin; + int locs_size = dest_count * sizeof(relocInfo); + if (!UseRelocIndex) { + Copy::fill_to_bytes(relocation_begin + locs_size, total_size-locs_size, 0); + return; + } + int index_size = total_size - locs_size - BytesPerInt; // find out how much space is left + int ncards = index_size / sizeof(RelocIndexEntry); + assert(total_size == locs_size + index_size + BytesPerInt, "checkin'"); + assert(index_size >= 0 && index_size % sizeof(RelocIndexEntry) == 0, "checkin'"); + jint* index_size_addr = (jint*)relocation_end - 1; + + assert(sizeof(jint) == BytesPerInt, "change this code"); + + *index_size_addr = index_size; + if (index_size != 0) { + assert(index_size > 0, "checkin'"); + + RelocIndexEntry* index = (RelocIndexEntry *)(relocation_begin + locs_size); + assert(index == (RelocIndexEntry*)index_size_addr - ncards, "checkin'"); + + // walk over the relocations, and fill in index entries as we go + RelocIterator iter; + const address initial_addr = NULL; + relocInfo* const initial_current = dest_begin - 1; // biased by -1 like elsewhere + + iter._code = NULL; + iter._addr = initial_addr; + iter._limit = (address)(intptr_t)(ncards * indexCardSize); + iter._current = initial_current; + iter._end = dest_begin + dest_count; + + int i = 0; + address next_card_addr = (address)indexCardSize; + int addr_offset = 0; + int reloc_offset = 0; + while (true) { + // Checkpoint the iterator before advancing it. + addr_offset = iter._addr - initial_addr; + reloc_offset = iter._current - initial_current; + if (!iter.next()) break; + while (iter.addr() >= next_card_addr) { + index[i].addr_offset = addr_offset; + index[i].reloc_offset = reloc_offset; + i++; + next_card_addr += indexCardSize; + } + } + while (i < ncards) { + index[i].addr_offset = addr_offset; + index[i].reloc_offset = reloc_offset; + i++; + } + } +} + + +void RelocIterator::set_limits(address begin, address limit) { + int index_size = 0; + if (UseRelocIndex && _code != NULL) { + index_size = ((jint*)_end)[-1]; + _end = (relocInfo*)( (address)_end - index_size - BytesPerInt ); + } + + _limit = limit; + + // the limit affects this next stuff: + if (begin != NULL) { +#ifdef ASSERT + // In ASSERT mode we do not actually use the index, but simply + // check that its contents would have led us to the right answer. + address addrCheck = _addr; + relocInfo* infoCheck = _current; +#endif // ASSERT + if (index_size > 0) { + // skip ahead + RelocIndexEntry* index = (RelocIndexEntry*)_end; + RelocIndexEntry* index_limit = (RelocIndexEntry*)((address)index + index_size); + assert(_addr == _code->instructions_begin(), "_addr must be unadjusted"); + int card = (begin - _addr) / indexCardSize; + if (card > 0) { + if (index+card-1 < index_limit) index += card-1; + else index = index_limit - 1; +#ifdef ASSERT + addrCheck = _addr + index->addr_offset; + infoCheck = _current + index->reloc_offset; +#else + // Advance the iterator immediately to the last valid state + // for the previous card. Calling "next" will then advance + // it to the first item on the required card. + _addr += index->addr_offset; + _current += index->reloc_offset; +#endif // ASSERT + } + } + + relocInfo* backup; + address backup_addr; + while (true) { + backup = _current; + backup_addr = _addr; +#ifdef ASSERT + if (backup == infoCheck) { + assert(backup_addr == addrCheck, "must match"); addrCheck = NULL; infoCheck = NULL; + } else { + assert(addrCheck == NULL || backup_addr <= addrCheck, "must not pass addrCheck"); + } +#endif // ASSERT + if (!next() || addr() >= begin) break; + } + assert(addrCheck == NULL || addrCheck == backup_addr, "must have matched addrCheck"); + assert(infoCheck == NULL || infoCheck == backup, "must have matched infoCheck"); + // At this point, either we are at the first matching record, + // or else there is no such record, and !has_current(). + // In either case, revert to the immediatly preceding state. + _current = backup; + _addr = backup_addr; + set_has_current(false); + } +} + + +void RelocIterator::set_limit(address limit) { + address code_end = (address)code() + code()->size(); + assert(limit == NULL || limit <= code_end, "in bounds"); + _limit = limit; +} + + +void PatchingRelocIterator:: prepass() { + // turn breakpoints off during patching + _init_state = (*this); // save cursor + while (next()) { + if (type() == relocInfo::breakpoint_type) { + breakpoint_reloc()->set_active(false); + } + } + (RelocIterator&)(*this) = _init_state; // reset cursor for client +} + + +void PatchingRelocIterator:: postpass() { + // turn breakpoints back on after patching + (RelocIterator&)(*this) = _init_state; // reset cursor again + while (next()) { + if (type() == relocInfo::breakpoint_type) { + breakpoint_Relocation* bpt = breakpoint_reloc(); + bpt->set_active(bpt->enabled()); + } + } +} + + +// All the strange bit-encodings are in here. +// The idea is to encode relocation data which are small integers +// very efficiently (a single extra halfword). Larger chunks of +// relocation data need a halfword header to hold their size. +void RelocIterator::advance_over_prefix() { + if (_current->is_datalen()) { + _data = (short*) _current->data(); + _datalen = _current->datalen(); + _current += _datalen + 1; // skip the embedded data & header + } else { + _databuf = _current->immediate(); + _data = &_databuf; + _datalen = 1; + _current++; // skip the header + } + // The client will see the following relocInfo, whatever that is. + // It is the reloc to which the preceding data applies. +} + + +address RelocIterator::compute_section_start(int n) const { +// This routine not only computes a section start, but also +// memoizes it for later. +#define CACHE ((RelocIterator*)this)->_section_start[n] + CodeBlob* cb = code(); + guarantee(cb != NULL, "must have a code blob"); + if (n == CodeBuffer::SECT_INSTS) + return CACHE = cb->instructions_begin(); + assert(cb->is_nmethod(), "only nmethods have these sections"); + nmethod* nm = (nmethod*) cb; + address res = NULL; + switch (n) { + case CodeBuffer::SECT_STUBS: + res = nm->stub_begin(); + break; + case CodeBuffer::SECT_CONSTS: + res = nm->consts_begin(); + break; + default: + ShouldNotReachHere(); + } + assert(nm->contains(res) || res == nm->instructions_end(), "tame pointer"); + CACHE = res; + return res; +#undef CACHE +} + + +Relocation* RelocIterator::reloc() { + // (take the "switch" out-of-line) + relocInfo::relocType t = type(); + if (false) {} + #define EACH_TYPE(name) \ + else if (t == relocInfo::name##_type) { \ + return name##_reloc(); \ + } + APPLY_TO_RELOCATIONS(EACH_TYPE); + #undef EACH_TYPE + assert(t == relocInfo::none, "must be padding"); + return new(_rh) Relocation(); +} + + +//////// Methods for flyweight Relocation types + + +RelocationHolder RelocationHolder::plus(int offset) const { + if (offset != 0) { + switch (type()) { + case relocInfo::none: + break; + case relocInfo::oop_type: + { + oop_Relocation* r = (oop_Relocation*)reloc(); + return oop_Relocation::spec(r->oop_index(), r->offset() + offset); + } + default: + ShouldNotReachHere(); + } + } + return (*this); +} + + +void Relocation::guarantee_size() { + guarantee(false, "Make _relocbuf bigger!"); +} + + // some relocations can compute their own values +address Relocation::value() { + ShouldNotReachHere(); + return NULL; +} + + +void Relocation::set_value(address x) { + ShouldNotReachHere(); +} + + +RelocationHolder Relocation::spec_simple(relocInfo::relocType rtype) { + if (rtype == relocInfo::none) return RelocationHolder::none; + relocInfo ri = relocInfo(rtype, 0); + RelocIterator itr; + itr.set_current(ri); + itr.reloc(); + return itr._rh; +} + + +static inline bool is_index(intptr_t index) { + return 0 < index && index < os::vm_page_size(); +} + + +int32_t Relocation::runtime_address_to_index(address runtime_address) { + assert(!is_index((intptr_t)runtime_address), "must not look like an index"); + + if (runtime_address == NULL) return 0; + + StubCodeDesc* p = StubCodeDesc::desc_for(runtime_address); + if (p != NULL && p->begin() == runtime_address) { + assert(is_index(p->index()), "there must not be too many stubs"); + return (int32_t)p->index(); + } else { + // Known "miscellaneous" non-stub pointers: + // os::get_polling_page(), SafepointSynchronize::address_of_state() + if (PrintRelocations) { + tty->print_cr("random unregistered address in relocInfo: " INTPTR_FORMAT, runtime_address); + } +#ifndef _LP64 + return (int32_t) (intptr_t)runtime_address; +#else + // didn't fit return non-index + return -1; +#endif /* _LP64 */ + } +} + + +address Relocation::index_to_runtime_address(int32_t index) { + if (index == 0) return NULL; + + if (is_index(index)) { + StubCodeDesc* p = StubCodeDesc::desc_for_index(index); + assert(p != NULL, "there must be a stub for this index"); + return p->begin(); + } else { +#ifndef _LP64 + // this only works on 32bit machines + return (address) ((intptr_t) index); +#else + fatal("Relocation::index_to_runtime_address, int32_t not pointer sized"); + return NULL; +#endif /* _LP64 */ + } +} + +address Relocation::old_addr_for(address newa, + const CodeBuffer* src, CodeBuffer* dest) { + int sect = dest->section_index_of(newa); + guarantee(sect != CodeBuffer::SECT_NONE, "lost track of this address"); + address ostart = src->code_section(sect)->start(); + address nstart = dest->code_section(sect)->start(); + return ostart + (newa - nstart); +} + +address Relocation::new_addr_for(address olda, + const CodeBuffer* src, CodeBuffer* dest) { + debug_only(const CodeBuffer* src0 = src); + int sect = CodeBuffer::SECT_NONE; + // Look for olda in the source buffer, and all previous incarnations + // if the source buffer has been expanded. + for (; src != NULL; src = src->before_expand()) { + sect = src->section_index_of(olda); + if (sect != CodeBuffer::SECT_NONE) break; + } + guarantee(sect != CodeBuffer::SECT_NONE, "lost track of this address"); + address ostart = src->code_section(sect)->start(); + address nstart = dest->code_section(sect)->start(); + return nstart + (olda - ostart); +} + +void Relocation::normalize_address(address& addr, const CodeSection* dest, bool allow_other_sections) { + address addr0 = addr; + if (addr0 == NULL || dest->allocates2(addr0)) return; + CodeBuffer* cb = dest->outer(); + addr = new_addr_for(addr0, cb, cb); + assert(allow_other_sections || dest->contains2(addr), + "addr must be in required section"); +} + + +void CallRelocation::set_destination(address x) { + pd_set_call_destination(x); +} + +void CallRelocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { + // Usually a self-relative reference to an external routine. + // On some platforms, the reference is absolute (not self-relative). + // The enhanced use of pd_call_destination sorts this all out. + address orig_addr = old_addr_for(addr(), src, dest); + address callee = pd_call_destination(orig_addr); + // Reassert the callee address, this time in the new copy of the code. + pd_set_call_destination(callee); +} + + +//// pack/unpack methods + +void oop_Relocation::pack_data_to(CodeSection* dest) { + short* p = (short*) dest->locs_end(); + p = pack_2_ints_to(p, _oop_index, _offset); + dest->set_locs_end((relocInfo*) p); +} + + +void oop_Relocation::unpack_data() { + unpack_2_ints(_oop_index, _offset); +} + + +void virtual_call_Relocation::pack_data_to(CodeSection* dest) { + short* p = (short*) dest->locs_end(); + address point = dest->locs_point(); + + // Try to make a pointer NULL first. + if (_oop_limit >= point && + _oop_limit <= point + NativeCall::instruction_size) { + _oop_limit = NULL; + } + // If the _oop_limit is NULL, it "defaults" to the end of the call. + // See ic_call_Relocation::oop_limit() below. + + normalize_address(_first_oop, dest); + normalize_address(_oop_limit, dest); + jint x0 = scaled_offset_null_special(_first_oop, point); + jint x1 = scaled_offset_null_special(_oop_limit, point); + p = pack_2_ints_to(p, x0, x1); + dest->set_locs_end((relocInfo*) p); +} + + +void virtual_call_Relocation::unpack_data() { + jint x0, x1; unpack_2_ints(x0, x1); + address point = addr(); + _first_oop = x0==0? NULL: address_from_scaled_offset(x0, point); + _oop_limit = x1==0? NULL: address_from_scaled_offset(x1, point); +} + + +void static_stub_Relocation::pack_data_to(CodeSection* dest) { + short* p = (short*) dest->locs_end(); + CodeSection* insts = dest->outer()->insts(); + normalize_address(_static_call, insts); + p = pack_1_int_to(p, scaled_offset(_static_call, insts->start())); + dest->set_locs_end((relocInfo*) p); +} + +void static_stub_Relocation::unpack_data() { + address base = binding()->section_start(CodeBuffer::SECT_INSTS); + _static_call = address_from_scaled_offset(unpack_1_int(), base); +} + + +void external_word_Relocation::pack_data_to(CodeSection* dest) { + short* p = (short*) dest->locs_end(); + int32_t index = runtime_address_to_index(_target); +#ifndef _LP64 + p = pack_1_int_to(p, index); +#else + if (is_index(index)) { + p = pack_2_ints_to(p, index, 0); + } else { + jlong t = (jlong) _target; + int32_t lo = low(t); + int32_t hi = high(t); + p = pack_2_ints_to(p, lo, hi); + DEBUG_ONLY(jlong t1 = jlong_from(hi, lo)); + assert(!is_index(t1) && (address) t1 == _target, "not symmetric"); + } +#endif /* _LP64 */ + dest->set_locs_end((relocInfo*) p); +} + + +void external_word_Relocation::unpack_data() { +#ifndef _LP64 + _target = index_to_runtime_address(unpack_1_int()); +#else + int32_t lo, hi; + unpack_2_ints(lo, hi); + jlong t = jlong_from(hi, lo);; + if (is_index(t)) { + _target = index_to_runtime_address(t); + } else { + _target = (address) t; + } +#endif /* _LP64 */ +} + + +void internal_word_Relocation::pack_data_to(CodeSection* dest) { + short* p = (short*) dest->locs_end(); + normalize_address(_target, dest, true); + + // Check whether my target address is valid within this section. + // If not, strengthen the relocation type to point to another section. + int sindex = _section; + if (sindex == CodeBuffer::SECT_NONE && _target != NULL + && (!dest->allocates(_target) || _target == dest->locs_point())) { + sindex = dest->outer()->section_index_of(_target); + guarantee(sindex != CodeBuffer::SECT_NONE, "must belong somewhere"); + relocInfo* base = dest->locs_end() - 1; + assert(base->type() == this->type(), "sanity"); + // Change the written type, to be section_word_type instead. + base->set_type(relocInfo::section_word_type); + } + + // Note: An internal_word relocation cannot refer to its own instruction, + // because we reserve "0" to mean that the pointer itself is embedded + // in the code stream. We use a section_word relocation for such cases. + + if (sindex == CodeBuffer::SECT_NONE) { + assert(type() == relocInfo::internal_word_type, "must be base class"); + guarantee(_target == NULL || dest->allocates2(_target), "must be within the given code section"); + jint x0 = scaled_offset_null_special(_target, dest->locs_point()); + assert(!(x0 == 0 && _target != NULL), "correct encoding of null target"); + p = pack_1_int_to(p, x0); + } else { + assert(_target != NULL, "sanity"); + CodeSection* sect = dest->outer()->code_section(sindex); + guarantee(sect->allocates2(_target), "must be in correct section"); + address base = sect->start(); + jint offset = scaled_offset(_target, base); + assert((uint)sindex < (uint)CodeBuffer::SECT_LIMIT, "sanity"); + assert(CodeBuffer::SECT_LIMIT <= (1 << section_width), "section_width++"); + p = pack_1_int_to(p, (offset << section_width) | sindex); + } + + dest->set_locs_end((relocInfo*) p); +} + + +void internal_word_Relocation::unpack_data() { + jint x0 = unpack_1_int(); + _target = x0==0? NULL: address_from_scaled_offset(x0, addr()); + _section = CodeBuffer::SECT_NONE; +} + + +void section_word_Relocation::unpack_data() { + jint x = unpack_1_int(); + jint offset = (x >> section_width); + int sindex = (x & ((1<section_start(sindex); + + _section = sindex; + _target = address_from_scaled_offset(offset, base); +} + + +void breakpoint_Relocation::pack_data_to(CodeSection* dest) { + short* p = (short*) dest->locs_end(); + address point = dest->locs_point(); + + *p++ = _bits; + + assert(_target != NULL, "sanity"); + + if (internal()) normalize_address(_target, dest); + + jint target_bits = + (jint)( internal() ? scaled_offset (_target, point) + : runtime_address_to_index(_target) ); + if (settable()) { + // save space for set_target later + p = add_jint(p, target_bits); + } else { + p = add_var_int(p, target_bits); + } + + for (int i = 0; i < instrlen(); i++) { + // put placeholder words until bytes can be saved + p = add_short(p, (short)0x7777); + } + + dest->set_locs_end((relocInfo*) p); +} + + +void breakpoint_Relocation::unpack_data() { + _bits = live_bits(); + + int targetlen = datalen() - 1 - instrlen(); + jint target_bits = 0; + if (targetlen == 0) target_bits = 0; + else if (targetlen == 1) target_bits = *(data()+1); + else if (targetlen == 2) target_bits = relocInfo::jint_from_data(data()+1); + else { ShouldNotReachHere(); } + + _target = internal() ? address_from_scaled_offset(target_bits, addr()) + : index_to_runtime_address (target_bits); +} + + +//// miscellaneous methods +oop* oop_Relocation::oop_addr() { + int n = _oop_index; + if (n == 0) { + // oop is stored in the code stream + return (oop*) pd_address_in_code(); + } else { + // oop is stored in table at CodeBlob::oops_begin + return code()->oop_addr_at(n); + } +} + + +oop oop_Relocation::oop_value() { + oop v = *oop_addr(); + // clean inline caches store a special pseudo-null + if (v == (oop)Universe::non_oop_word()) v = NULL; + return v; +} + + +void oop_Relocation::fix_oop_relocation() { + if (!oop_is_immediate()) { + // get the oop from the pool, and re-insert it into the instruction: + set_value(value()); + } +} + + +RelocIterator virtual_call_Relocation::parse_ic(CodeBlob* &code, address &ic_call, address &first_oop, + oop* &oop_addr, bool *is_optimized) { + assert(ic_call != NULL, "ic_call address must be set"); + assert(ic_call != NULL || first_oop != NULL, "must supply a non-null input"); + if (code == NULL) { + if (ic_call != NULL) { + code = CodeCache::find_blob(ic_call); + } else if (first_oop != NULL) { + code = CodeCache::find_blob(first_oop); + } + assert(code != NULL, "address to parse must be in CodeBlob"); + } + assert(ic_call == NULL || code->contains(ic_call), "must be in CodeBlob"); + assert(first_oop == NULL || code->contains(first_oop), "must be in CodeBlob"); + + address oop_limit = NULL; + + if (ic_call != NULL) { + // search for the ic_call at the given address + RelocIterator iter(code, ic_call, ic_call+1); + bool ret = iter.next(); + assert(ret == true, "relocInfo must exist at this address"); + assert(iter.addr() == ic_call, "must find ic_call"); + if (iter.type() == relocInfo::virtual_call_type) { + virtual_call_Relocation* r = iter.virtual_call_reloc(); + first_oop = r->first_oop(); + oop_limit = r->oop_limit(); + *is_optimized = false; + } else { + assert(iter.type() == relocInfo::opt_virtual_call_type, "must be a virtual call"); + *is_optimized = true; + oop_addr = NULL; + first_oop = NULL; + return iter; + } + } + + // search for the first_oop, to get its oop_addr + RelocIterator all_oops(code, first_oop); + RelocIterator iter = all_oops; + iter.set_limit(first_oop+1); + bool found_oop = false; + while (iter.next()) { + if (iter.type() == relocInfo::oop_type) { + assert(iter.addr() == first_oop, "must find first_oop"); + oop_addr = iter.oop_reloc()->oop_addr(); + found_oop = true; + break; + } + } + assert(found_oop, "must find first_oop"); + + bool did_reset = false; + while (ic_call == NULL) { + // search forward for the ic_call matching the given first_oop + while (iter.next()) { + if (iter.type() == relocInfo::virtual_call_type) { + virtual_call_Relocation* r = iter.virtual_call_reloc(); + if (r->first_oop() == first_oop) { + ic_call = r->addr(); + oop_limit = r->oop_limit(); + break; + } + } + } + guarantee(!did_reset, "cannot find ic_call"); + iter = RelocIterator(code); // search the whole CodeBlob + did_reset = true; + } + + assert(oop_limit != NULL && first_oop != NULL && ic_call != NULL, ""); + all_oops.set_limit(oop_limit); + return all_oops; +} + + +address virtual_call_Relocation::first_oop() { + assert(_first_oop != NULL && _first_oop < addr(), "must precede ic_call"); + return _first_oop; +} + + +address virtual_call_Relocation::oop_limit() { + if (_oop_limit == NULL) + return addr() + NativeCall::instruction_size; + else + return _oop_limit; +} + + + +void virtual_call_Relocation::clear_inline_cache() { + // No stubs for ICs + // Clean IC + ResourceMark rm; + CompiledIC* icache = CompiledIC_at(this); + icache->set_to_clean(); +} + + +void opt_virtual_call_Relocation::clear_inline_cache() { + // No stubs for ICs + // Clean IC + ResourceMark rm; + CompiledIC* icache = CompiledIC_at(this); + icache->set_to_clean(); +} + + +address opt_virtual_call_Relocation::static_stub() { + // search for the static stub who points back to this static call + address static_call_addr = addr(); + RelocIterator iter(code()); + while (iter.next()) { + if (iter.type() == relocInfo::static_stub_type) { + if (iter.static_stub_reloc()->static_call() == static_call_addr) { + return iter.addr(); + } + } + } + return NULL; +} + + +void static_call_Relocation::clear_inline_cache() { + // Safe call site info + CompiledStaticCall* handler = compiledStaticCall_at(this); + handler->set_to_clean(); +} + + +address static_call_Relocation::static_stub() { + // search for the static stub who points back to this static call + address static_call_addr = addr(); + RelocIterator iter(code()); + while (iter.next()) { + if (iter.type() == relocInfo::static_stub_type) { + if (iter.static_stub_reloc()->static_call() == static_call_addr) { + return iter.addr(); + } + } + } + return NULL; +} + + +void static_stub_Relocation::clear_inline_cache() { + // Call stub is only used when calling the interpreted code. + // It does not really need to be cleared, except that we want to clean out the methodoop. + CompiledStaticCall::set_stub_to_clean(this); +} + + +void external_word_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { + address target = _target; + if (target == NULL) { + // An absolute embedded reference to an external location, + // which means there is nothing to fix here. + return; + } + // Probably this reference is absolute, not relative, so the + // following is probably a no-op. + assert(src->section_index_of(target) == CodeBuffer::SECT_NONE, "sanity"); + set_value(target); +} + + +address external_word_Relocation::target() { + address target = _target; + if (target == NULL) { + target = pd_get_address_from_code(); + } + return target; +} + + +void internal_word_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { + address target = _target; + if (target == NULL) { + if (addr_in_const()) { + target = new_addr_for(*(address*)addr(), src, dest); + } else { + target = new_addr_for(pd_get_address_from_code(), src, dest); + } + } + set_value(target); +} + + +address internal_word_Relocation::target() { + address target = _target; + if (target == NULL) { + target = pd_get_address_from_code(); + } + return target; +} + + +breakpoint_Relocation::breakpoint_Relocation(int kind, address target, bool internal) { + bool active = false; + bool enabled = (kind == initialization); + bool removable = (kind != safepoint); + bool settable = (target == NULL); + + int bits = kind; + if (enabled) bits |= enabled_state; + if (internal) bits |= internal_attr; + if (removable) bits |= removable_attr; + if (settable) bits |= settable_attr; + + _bits = bits | high_bit; + _target = target; + + assert(this->kind() == kind, "kind encoded"); + assert(this->enabled() == enabled, "enabled encoded"); + assert(this->active() == active, "active encoded"); + assert(this->internal() == internal, "internal encoded"); + assert(this->removable() == removable, "removable encoded"); + assert(this->settable() == settable, "settable encoded"); +} + + +address breakpoint_Relocation::target() const { + return _target; +} + + +void breakpoint_Relocation::set_target(address x) { + assert(settable(), "must be settable"); + jint target_bits = + (jint)(internal() ? scaled_offset (x, addr()) + : runtime_address_to_index(x)); + short* p = &live_bits() + 1; + p = add_jint(p, target_bits); + assert(p == instrs(), "new target must fit"); + _target = x; +} + + +void breakpoint_Relocation::set_enabled(bool b) { + if (enabled() == b) return; + + if (b) { + set_bits(bits() | enabled_state); + } else { + set_active(false); // remove the actual breakpoint insn, if any + set_bits(bits() & ~enabled_state); + } +} + + +void breakpoint_Relocation::set_active(bool b) { + assert(!b || enabled(), "cannot activate a disabled breakpoint"); + + if (active() == b) return; + + // %%% should probably seize a lock here (might not be the right lock) + //MutexLockerEx ml_patch(Patching_lock, true); + //if (active() == b) return; // recheck state after locking + + if (b) { + set_bits(bits() | active_state); + if (instrlen() == 0) + fatal("breakpoints in original code must be undoable"); + pd_swap_in_breakpoint (addr(), instrs(), instrlen()); + } else { + set_bits(bits() & ~active_state); + pd_swap_out_breakpoint(addr(), instrs(), instrlen()); + } +} + + +//--------------------------------------------------------------------------------- +// Non-product code + +#ifndef PRODUCT + +static const char* reloc_type_string(relocInfo::relocType t) { + switch (t) { + #define EACH_CASE(name) \ + case relocInfo::name##_type: \ + return #name; + + APPLY_TO_RELOCATIONS(EACH_CASE); + #undef EACH_CASE + + case relocInfo::none: + return "none"; + case relocInfo::data_prefix_tag: + return "prefix"; + default: + return "UNKNOWN RELOC TYPE"; + } +} + + +void RelocIterator::print_current() { + if (!has_current()) { + tty->print_cr("(no relocs)"); + return; + } + tty->print("relocInfo@" INTPTR_FORMAT " [type=%d(%s) addr=" INTPTR_FORMAT, + _current, type(), reloc_type_string((relocInfo::relocType) type()), _addr); + if (current()->format() != 0) + tty->print(" format=%d", current()->format()); + if (datalen() == 1) { + tty->print(" data=%d", data()[0]); + } else if (datalen() > 0) { + tty->print(" data={"); + for (int i = 0; i < datalen(); i++) { + tty->print("%04x", data()[i] & 0xFFFF); + } + tty->print("}"); + } + tty->print("]"); + switch (type()) { + case relocInfo::oop_type: + { + oop_Relocation* r = oop_reloc(); + oop* oop_addr = NULL; + oop raw_oop = NULL; + oop oop_value = NULL; + if (code() != NULL || r->oop_is_immediate()) { + oop_addr = r->oop_addr(); + raw_oop = *oop_addr; + oop_value = r->oop_value(); + } + tty->print(" | [oop_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT " offset=%d]", + oop_addr, (address)raw_oop, r->offset()); + // Do not print the oop by default--we want this routine to + // work even during GC or other inconvenient times. + if (WizardMode && oop_value != NULL) { + tty->print("oop_value=" INTPTR_FORMAT ": ", (address)oop_value); + oop_value->print_value_on(tty); + } + break; + } + case relocInfo::external_word_type: + case relocInfo::internal_word_type: + case relocInfo::section_word_type: + { + DataRelocation* r = (DataRelocation*) reloc(); + tty->print(" | [target=" INTPTR_FORMAT "]", r->value()); //value==target + break; + } + case relocInfo::static_call_type: + case relocInfo::runtime_call_type: + { + CallRelocation* r = (CallRelocation*) reloc(); + tty->print(" | [destination=" INTPTR_FORMAT "]", r->destination()); + break; + } + case relocInfo::virtual_call_type: + { + virtual_call_Relocation* r = (virtual_call_Relocation*) reloc(); + tty->print(" | [destination=" INTPTR_FORMAT " first_oop=" INTPTR_FORMAT " oop_limit=" INTPTR_FORMAT "]", + r->destination(), r->first_oop(), r->oop_limit()); + break; + } + case relocInfo::static_stub_type: + { + static_stub_Relocation* r = (static_stub_Relocation*) reloc(); + tty->print(" | [static_call=" INTPTR_FORMAT "]", r->static_call()); + break; + } + } + tty->cr(); +} + + +void RelocIterator::print() { + RelocIterator save_this = (*this); + relocInfo* scan = _current; + if (!has_current()) scan += 1; // nothing to scan here! + + bool skip_next = has_current(); + bool got_next; + while (true) { + got_next = (skip_next || next()); + skip_next = false; + + tty->print(" @" INTPTR_FORMAT ": ", scan); + relocInfo* newscan = _current+1; + if (!has_current()) newscan -= 1; // nothing to scan here! + while (scan < newscan) { + tty->print("%04x", *(short*)scan & 0xFFFF); + scan++; + } + tty->cr(); + + if (!got_next) break; + print_current(); + } + + (*this) = save_this; +} + +// For the debugger: +extern "C" +void print_blob_locs(CodeBlob* cb) { + cb->print(); + RelocIterator iter(cb); + iter.print(); +} +extern "C" +void print_buf_locs(CodeBuffer* cb) { + FlagSetting fs(PrintRelocations, true); + cb->print(); +} +#endif // !PRODUCT diff --git a/hotspot/src/share/vm/code/relocInfo.hpp b/hotspot/src/share/vm/code/relocInfo.hpp new file mode 100644 index 00000000000..8a2867dc00e --- /dev/null +++ b/hotspot/src/share/vm/code/relocInfo.hpp @@ -0,0 +1,1328 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Types in this file: +// relocInfo +// One element of an array of halfwords encoding compressed relocations. +// Also, the source of relocation types (relocInfo::oop_type, ...). +// Relocation +// A flyweight object representing a single relocation. +// It is fully unpacked from the compressed relocation array. +// oop_Relocation, ... (subclasses of Relocation) +// The location of some type-specific operations (oop_addr, ...). +// Also, the source of relocation specs (oop_Relocation::spec, ...). +// RelocationHolder +// A ValueObj type which acts as a union holding a Relocation object. +// Represents a relocation spec passed into a CodeBuffer during assembly. +// RelocIterator +// A StackObj which iterates over the relocations associated with +// a range of code addresses. Can be used to operate a copy of code. +// PatchingRelocIterator +// Specialized subtype of RelocIterator which removes breakpoints +// temporarily during iteration, then restores them. +// BoundRelocation +// An _internal_ type shared by packers and unpackers of relocations. +// It pastes together a RelocationHolder with some pointers into +// code and relocInfo streams. + + +// Notes on relocType: +// +// These hold enough information to read or write a value embedded in +// the instructions of an CodeBlob. They're used to update: +// +// 1) embedded oops (isOop() == true) +// 2) inline caches (isIC() == true) +// 3) runtime calls (isRuntimeCall() == true) +// 4) internal word ref (isInternalWord() == true) +// 5) external word ref (isExternalWord() == true) +// +// when objects move (GC) or if code moves (compacting the code heap). +// They are also used to patch the code (if a call site must change) +// +// A relocInfo is represented in 16 bits: +// 4 bits indicating the relocation type +// 12 bits indicating the offset from the previous relocInfo address +// +// The offsets accumulate along the relocInfo stream to encode the +// address within the CodeBlob, which is named RelocIterator::addr(). +// The address of a particular relocInfo always points to the first +// byte of the relevant instruction (and not to any of its subfields +// or embedded immediate constants). +// +// The offset value is scaled appropriately for the target machine. +// (See relocInfo_.hpp for the offset scaling.) +// +// On some machines, there may also be a "format" field which may provide +// additional information about the format of the instruction stream +// at the corresponding code address. The format value is usually zero. +// Any machine (such as Intel) whose instructions can sometimes contain +// more than one relocatable constant needs format codes to distinguish +// which operand goes with a given relocation. +// +// If the target machine needs N format bits, the offset has 12-N bits, +// the format is encoded between the offset and the type, and the +// relocInfo_.hpp file has manifest constants for the format codes. +// +// If the type is "data_prefix_tag" then the offset bits are further encoded, +// and in fact represent not a code-stream offset but some inline data. +// The data takes the form of a counted sequence of halfwords, which +// precedes the actual relocation record. (Clients never see it directly.) +// The interpetation of this extra data depends on the relocation type. +// +// On machines that have 32-bit immediate fields, there is usually +// little need for relocation "prefix" data, because the instruction stream +// is a perfectly reasonable place to store the value. On machines in +// which 32-bit values must be "split" across instructions, the relocation +// data is the "true" specification of the value, which is then applied +// to some field of the instruction (22 or 13 bits, on SPARC). +// +// Whenever the location of the CodeBlob changes, any PC-relative +// relocations, and any internal_word_type relocations, must be reapplied. +// After the GC runs, oop_type relocations must be reapplied. +// +// +// Here are meanings of the types: +// +// relocInfo::none -- a filler record +// Value: none +// Instruction: The corresponding code address is ignored +// Data: Any data prefix and format code are ignored +// (This means that any relocInfo can be disabled by setting +// its type to none. See relocInfo::remove.) +// +// relocInfo::oop_type -- a reference to an oop +// Value: an oop, or else the address (handle) of an oop +// Instruction types: memory (load), set (load address) +// Data: [] an oop stored in 4 bytes of instruction +// [n] n is the index of an oop in the CodeBlob's oop pool +// [[N]n l] and l is a byte offset to be applied to the oop +// [Nn Ll] both index and offset may be 32 bits if necessary +// Here is a special hack, used only by the old compiler: +// [[N]n 00] the value is the __address__ of the nth oop in the pool +// (Note that the offset allows optimal references to class variables.) +// +// relocInfo::internal_word_type -- an address within the same CodeBlob +// relocInfo::section_word_type -- same, but can refer to another section +// Value: an address in the CodeBlob's code or constants section +// Instruction types: memory (load), set (load address) +// Data: [] stored in 4 bytes of instruction +// [[L]l] a relative offset (see [About Offsets] below) +// In the case of section_word_type, the offset is relative to a section +// base address, and the section number (e.g., SECT_INSTS) is encoded +// into the low two bits of the offset L. +// +// relocInfo::external_word_type -- a fixed address in the runtime system +// Value: an address +// Instruction types: memory (load), set (load address) +// Data: [] stored in 4 bytes of instruction +// [n] the index of a "well-known" stub (usual case on RISC) +// [Ll] a 32-bit address +// +// relocInfo::runtime_call_type -- a fixed subroutine in the runtime system +// Value: an address +// Instruction types: PC-relative call (or a PC-relative branch) +// Data: [] stored in 4 bytes of instruction +// +// relocInfo::static_call_type -- a static call +// Value: an CodeBlob, a stub, or a fixup routine +// Instruction types: a call +// Data: [] +// The identity of the callee is extracted from debugging information. +// //%note reloc_3 +// +// relocInfo::virtual_call_type -- a virtual call site (which includes an inline +// cache) +// Value: an CodeBlob, a stub, the interpreter, or a fixup routine +// Instruction types: a call, plus some associated set-oop instructions +// Data: [] the associated set-oops are adjacent to the call +// [n] n is a relative offset to the first set-oop +// [[N]n l] and l is a limit within which the set-oops occur +// [Nn Ll] both n and l may be 32 bits if necessary +// The identity of the callee is extracted from debugging information. +// +// relocInfo::opt_virtual_call_type -- a virtual call site that is statically bound +// +// Same info as a static_call_type. We use a special type, so the handling of +// virtuals and statics are separated. +// +// +// The offset n points to the first set-oop. (See [About Offsets] below.) +// In turn, the set-oop instruction specifies or contains an oop cell devoted +// exclusively to the IC call, which can be patched along with the call. +// +// The locations of any other set-oops are found by searching the relocation +// information starting at the first set-oop, and continuing until all +// relocations up through l have been inspected. The value l is another +// relative offset. (Both n and l are relative to the call's first byte.) +// +// The limit l of the search is exclusive. However, if it points within +// the call (e.g., offset zero), it is adjusted to point after the call and +// any associated machine-specific delay slot. +// +// Since the offsets could be as wide as 32-bits, these conventions +// put no restrictions whatever upon code reorganization. +// +// The compiler is responsible for ensuring that transition from a clean +// state to a monomorphic compiled state is MP-safe. This implies that +// the system must respond well to intermediate states where a random +// subset of the set-oops has been correctly from the clean state +// upon entry to the VEP of the compiled method. In the case of a +// machine (Intel) with a single set-oop instruction, the 32-bit +// immediate field must not straddle a unit of memory coherence. +// //%note reloc_3 +// +// relocInfo::breakpoint_type -- a conditional breakpoint in the code +// Value: none +// Instruction types: any whatsoever +// Data: [b [T]t i...] +// The b is a bit-packed word representing the breakpoint's attributes. +// The t is a target address which the breakpoint calls (when it is enabled). +// The i... is a place to store one or two instruction words overwritten +// by a trap, so that the breakpoint may be subsequently removed. +// +// relocInfo::static_stub_type -- an extra stub for each static_call_type +// Value: none +// Instruction types: a virtual call: { set_oop; jump; } +// Data: [[N]n] the offset of the associated static_call reloc +// This stub becomes the target of a static call which must be upgraded +// to a virtual call (because the callee is interpreted). +// See [About Offsets] below. +// //%note reloc_2 +// +// For example: +// +// INSTRUCTIONS RELOC: TYPE PREFIX DATA +// ------------ ---- ----------- +// sethi %hi(myObject), R oop_type [n(myObject)] +// ld [R+%lo(myObject)+fldOffset], R2 oop_type [n(myObject) fldOffset] +// add R2, 1, R2 +// st R2, [R+%lo(myObject)+fldOffset] oop_type [n(myObject) fldOffset] +//%note reloc_1 +// +// This uses 4 instruction words, 8 relocation halfwords, +// and an entry (which is sharable) in the CodeBlob's oop pool, +// for a total of 36 bytes. +// +// Note that the compiler is responsible for ensuring the "fldOffset" when +// added to "%lo(myObject)" does not overflow the immediate fields of the +// memory instructions. +// +// +// [About Offsets] Relative offsets are supplied to this module as +// positive byte offsets, but they may be internally stored scaled +// and/or negated, depending on what is most compact for the target +// system. Since the object pointed to by the offset typically +// precedes the relocation address, it is profitable to store +// these negative offsets as positive numbers, but this decision +// is internal to the relocation information abstractions. +// + +class Relocation; +class CodeBuffer; +class CodeSection; +class RelocIterator; + +class relocInfo VALUE_OBJ_CLASS_SPEC { + friend class RelocIterator; + public: + enum relocType { + none = 0, // Used when no relocation should be generated + oop_type = 1, // embedded oop + virtual_call_type = 2, // a standard inline cache call for a virtual send + opt_virtual_call_type = 3, // a virtual call that has been statically bound (i.e., no IC cache) + static_call_type = 4, // a static send + static_stub_type = 5, // stub-entry for static send (takes care of interpreter case) + runtime_call_type = 6, // call to fixed external routine + external_word_type = 7, // reference to fixed external address + internal_word_type = 8, // reference within the current code blob + section_word_type = 9, // internal, but a cross-section reference + poll_type = 10, // polling instruction for safepoints + poll_return_type = 11, // polling instruction for safepoints at return + breakpoint_type = 12, // an initialization barrier or safepoint + yet_unused_type = 13, // Still unused + yet_unused_type_2 = 14, // Still unused + data_prefix_tag = 15, // tag for a prefix (carries data arguments) + type_mask = 15 // A mask which selects only the above values + }; + + protected: + unsigned short _value; + + enum RawBitsToken { RAW_BITS }; + relocInfo(relocType type, RawBitsToken ignore, int bits) + : _value((type << nontype_width) + bits) { } + + relocInfo(relocType type, RawBitsToken ignore, int off, int f) + : _value((type << nontype_width) + (off / (unsigned)offset_unit) + (f << offset_width)) { } + + public: + // constructor + relocInfo(relocType type, int offset, int format = 0) +#ifndef ASSERT + { + (*this) = relocInfo(type, RAW_BITS, offset, format); + } +#else + // Put a bunch of assertions out-of-line. + ; +#endif + + #define APPLY_TO_RELOCATIONS(visitor) \ + visitor(oop) \ + visitor(virtual_call) \ + visitor(opt_virtual_call) \ + visitor(static_call) \ + visitor(static_stub) \ + visitor(runtime_call) \ + visitor(external_word) \ + visitor(internal_word) \ + visitor(poll) \ + visitor(poll_return) \ + visitor(breakpoint) \ + visitor(section_word) \ + + + public: + enum { + value_width = sizeof(unsigned short) * BitsPerByte, + type_width = 4, // == log2(type_mask+1) + nontype_width = value_width - type_width, + datalen_width = nontype_width-1, + datalen_tag = 1 << datalen_width, // or-ed into _value + datalen_limit = 1 << datalen_width, + datalen_mask = (1 << datalen_width)-1 + }; + + // accessors + public: + relocType type() const { return (relocType)((unsigned)_value >> nontype_width); } + int format() const { return format_mask==0? 0: format_mask & + ((unsigned)_value >> offset_width); } + int addr_offset() const { assert(!is_prefix(), "must have offset"); + return (_value & offset_mask)*offset_unit; } + + protected: + const short* data() const { assert(is_datalen(), "must have data"); + return (const short*)(this + 1); } + int datalen() const { assert(is_datalen(), "must have data"); + return (_value & datalen_mask); } + int immediate() const { assert(is_immediate(), "must have immed"); + return (_value & datalen_mask); } + public: + static int addr_unit() { return offset_unit; } + static int offset_limit() { return (1 << offset_width) * offset_unit; } + + void set_type(relocType type); + void set_format(int format); + + void remove() { set_type(none); } + + protected: + bool is_none() const { return type() == none; } + bool is_prefix() const { return type() == data_prefix_tag; } + bool is_datalen() const { assert(is_prefix(), "must be prefix"); + return (_value & datalen_tag) != 0; } + bool is_immediate() const { assert(is_prefix(), "must be prefix"); + return (_value & datalen_tag) == 0; } + + public: + // Occasionally records of type relocInfo::none will appear in the stream. + // We do not bother to filter these out, but clients should ignore them. + // These records serve as "filler" in three ways: + // - to skip large spans of unrelocated code (this is rare) + // - to pad out the relocInfo array to the required oop alignment + // - to disable old relocation information which is no longer applicable + + inline friend relocInfo filler_relocInfo(); + + // Every non-prefix relocation may be preceded by at most one prefix, + // which supplies 1 or more halfwords of associated data. Conventionally, + // an int is represented by 0, 1, or 2 halfwords, depending on how + // many bits are required to represent the value. (In addition, + // if the sole halfword is a 10-bit unsigned number, it is made + // "immediate" in the prefix header word itself. This optimization + // is invisible outside this module.) + + inline friend relocInfo prefix_relocInfo(int datalen = 0); + + protected: + // an immediate relocInfo optimizes a prefix with one 10-bit unsigned value + static relocInfo immediate_relocInfo(int data0) { + assert(fits_into_immediate(data0), "data0 in limits"); + return relocInfo(relocInfo::data_prefix_tag, RAW_BITS, data0); + } + static bool fits_into_immediate(int data0) { + return (data0 >= 0 && data0 < datalen_limit); + } + + public: + // Support routines for compilers. + + // This routine takes an infant relocInfo (unprefixed) and + // edits in its prefix, if any. It also updates dest.locs_end. + void initialize(CodeSection* dest, Relocation* reloc); + + // This routine updates a prefix and returns the limit pointer. + // It tries to compress the prefix from 32 to 16 bits, and if + // successful returns a reduced "prefix_limit" pointer. + relocInfo* finish_prefix(short* prefix_limit); + + // bit-packers for the data array: + + // As it happens, the bytes within the shorts are ordered natively, + // but the shorts within the word are ordered big-endian. + // This is an arbitrary choice, made this way mainly to ease debugging. + static int data0_from_int(jint x) { return x >> value_width; } + static int data1_from_int(jint x) { return (short)x; } + static jint jint_from_data(short* data) { + return (data[0] << value_width) + (unsigned short)data[1]; + } + + static jint short_data_at(int n, short* data, int datalen) { + return datalen > n ? data[n] : 0; + } + + static jint jint_data_at(int n, short* data, int datalen) { + return datalen > n+1 ? jint_from_data(&data[n]) : short_data_at(n, data, datalen); + } + + // Update methods for relocation information + // (since code is dynamically patched, we also need to dynamically update the relocation info) + // Both methods takes old_type, so it is able to performe sanity checks on the information removed. + static void change_reloc_info_for_address(RelocIterator *itr, address pc, relocType old_type, relocType new_type); + static void remove_reloc_info_for_address(RelocIterator *itr, address pc, relocType old_type); + + // Machine dependent stuff + #include "incls/_relocInfo_pd.hpp.incl" + + protected: + // Derived constant, based on format_width which is PD: + enum { + offset_width = nontype_width - format_width, + offset_mask = (1< 0 + }; +}; + +#define FORWARD_DECLARE_EACH_CLASS(name) \ +class name##_Relocation; +APPLY_TO_RELOCATIONS(FORWARD_DECLARE_EACH_CLASS) +#undef FORWARD_DECLARE_EACH_CLASS + + + +inline relocInfo filler_relocInfo() { + return relocInfo(relocInfo::none, relocInfo::offset_limit() - relocInfo::offset_unit); +} + +inline relocInfo prefix_relocInfo(int datalen) { + assert(relocInfo::fits_into_immediate(datalen), "datalen in limits"); + return relocInfo(relocInfo::data_prefix_tag, relocInfo::RAW_BITS, relocInfo::datalen_tag | datalen); +} + + +// Holder for flyweight relocation objects. +// Although the flyweight subclasses are of varying sizes, +// the holder is "one size fits all". +class RelocationHolder VALUE_OBJ_CLASS_SPEC { + friend class Relocation; + friend class CodeSection; + + private: + // this preallocated memory must accommodate all subclasses of Relocation + // (this number is assertion-checked in Relocation::operator new) + enum { _relocbuf_size = 5 }; + void* _relocbuf[ _relocbuf_size ]; + + public: + Relocation* reloc() const { return (Relocation*) &_relocbuf[0]; } + inline relocInfo::relocType type() const; + + // Add a constant offset to a relocation. Helper for class Address. + RelocationHolder plus(int offset) const; + + inline RelocationHolder(); // initializes type to none + + inline RelocationHolder(Relocation* r); // make a copy + + static const RelocationHolder none; +}; + +// A RelocIterator iterates through the relocation information of a CodeBlob. +// It is a variable BoundRelocation which is able to take on successive +// values as it is advanced through a code stream. +// Usage: +// RelocIterator iter(nm); +// while (iter.next()) { +// iter.reloc()->some_operation(); +// } +// or: +// RelocIterator iter(nm); +// while (iter.next()) { +// switch (iter.type()) { +// case relocInfo::oop_type : +// case relocInfo::ic_type : +// case relocInfo::prim_type : +// case relocInfo::uncommon_type : +// case relocInfo::runtime_call_type : +// case relocInfo::internal_word_type: +// case relocInfo::external_word_type: +// ... +// } +// } + +class RelocIterator : public StackObj { + enum { SECT_CONSTS = 2, + SECT_LIMIT = 3 }; // must be equal to CodeBuffer::SECT_LIMIT + friend class Relocation; + friend class relocInfo; // for change_reloc_info_for_address only + typedef relocInfo::relocType relocType; + + private: + address _limit; // stop producing relocations after this _addr + relocInfo* _current; // the current relocation information + relocInfo* _end; // end marker; we're done iterating when _current == _end + CodeBlob* _code; // compiled method containing _addr + address _addr; // instruction to which the relocation applies + short _databuf; // spare buffer for compressed data + short* _data; // pointer to the relocation's data + short _datalen; // number of halfwords in _data + char _format; // position within the instruction + + // Base addresses needed to compute targets of section_word_type relocs. + address _section_start[SECT_LIMIT]; + + void set_has_current(bool b) { + _datalen = !b ? -1 : 0; + debug_only(_data = NULL); + } + void set_current(relocInfo& ri) { + _current = &ri; + set_has_current(true); + } + + RelocationHolder _rh; // where the current relocation is allocated + + relocInfo* current() const { assert(has_current(), "must have current"); + return _current; } + + void set_limits(address begin, address limit); + + void advance_over_prefix(); // helper method + + void initialize_misc() { + set_has_current(false); + for (int i = 0; i < SECT_LIMIT; i++) { + _section_start[i] = NULL; // these will be lazily computed, if needed + } + } + + address compute_section_start(int n) const; // out-of-line helper + + void initialize(CodeBlob* nm, address begin, address limit); + + friend class PatchingRelocIterator; + // make an uninitialized one, for PatchingRelocIterator: + RelocIterator() { initialize_misc(); } + + public: + // constructor + RelocIterator(CodeBlob* cb, address begin = NULL, address limit = NULL); + RelocIterator(CodeSection* cb, address begin = NULL, address limit = NULL); + + // get next reloc info, return !eos + bool next() { + _current++; + assert(_current <= _end, "must not overrun relocInfo"); + if (_current == _end) { + set_has_current(false); + return false; + } + set_has_current(true); + + if (_current->is_prefix()) { + advance_over_prefix(); + assert(!current()->is_prefix(), "only one prefix at a time"); + } + + _addr += _current->addr_offset(); + + if (_limit != NULL && _addr >= _limit) { + set_has_current(false); + return false; + } + + if (relocInfo::have_format) _format = current()->format(); + return true; + } + + // accessors + address limit() const { return _limit; } + void set_limit(address x); + relocType type() const { return current()->type(); } + int format() const { return (relocInfo::have_format) ? current()->format() : 0; } + address addr() const { return _addr; } + CodeBlob* code() const { return _code; } + short* data() const { return _data; } + int datalen() const { return _datalen; } + bool has_current() const { return _datalen >= 0; } + + void set_addr(address addr) { _addr = addr; } + bool addr_in_const() const { return addr() >= section_start(SECT_CONSTS); } + + address section_start(int n) const { + address res = _section_start[n]; + return (res != NULL) ? res : compute_section_start(n); + } + + // The address points to the affected displacement part of the instruction. + // For RISC, this is just the whole instruction. + // For Intel, this is an unaligned 32-bit word. + + // type-specific relocation accessors: oop_Relocation* oop_reloc(), etc. + #define EACH_TYPE(name) \ + inline name##_Relocation* name##_reloc(); + APPLY_TO_RELOCATIONS(EACH_TYPE) + #undef EACH_TYPE + // generic relocation accessor; switches on type to call the above + Relocation* reloc(); + + // CodeBlob's have relocation indexes for faster random access: + static int locs_and_index_size(int code_size, int locs_size); + // Store an index into [dest_start+dest_count..dest_end). + // At dest_start[0..dest_count] is the actual relocation information. + // Everything else up to dest_end is free space for the index. + static void create_index(relocInfo* dest_begin, int dest_count, relocInfo* dest_end); + +#ifndef PRODUCT + public: + void print(); + void print_current(); +#endif +}; + + +// A Relocation is a flyweight object allocated within a RelocationHolder. +// It represents the relocation data of relocation record. +// So, the RelocIterator unpacks relocInfos into Relocations. + +class Relocation VALUE_OBJ_CLASS_SPEC { + friend class RelocationHolder; + friend class RelocIterator; + + private: + static void guarantee_size(); + + // When a relocation has been created by a RelocIterator, + // this field is non-null. It allows the relocation to know + // its context, such as the address to which it applies. + RelocIterator* _binding; + + protected: + RelocIterator* binding() const { + assert(_binding != NULL, "must be bound"); + return _binding; + } + void set_binding(RelocIterator* b) { + assert(_binding == NULL, "must be unbound"); + _binding = b; + assert(_binding != NULL, "must now be bound"); + } + + Relocation() { + _binding = NULL; + } + + static RelocationHolder newHolder() { + return RelocationHolder(); + } + + public: + void* operator new(size_t size, const RelocationHolder& holder) { + if (size > sizeof(holder._relocbuf)) guarantee_size(); + assert((void* const *)holder.reloc() == &holder._relocbuf[0], "ptrs must agree"); + return holder.reloc(); + } + + // make a generic relocation for a given type (if possible) + static RelocationHolder spec_simple(relocInfo::relocType rtype); + + // here is the type-specific hook which writes relocation data: + virtual void pack_data_to(CodeSection* dest) { } + + // here is the type-specific hook which reads (unpacks) relocation data: + virtual void unpack_data() { + assert(datalen()==0 || type()==relocInfo::none, "no data here"); + } + + protected: + // Helper functions for pack_data_to() and unpack_data(). + + // Most of the compression logic is confined here. + // (The "immediate data" mechanism of relocInfo works independently + // of this stuff, and acts to further compress most 1-word data prefixes.) + + // A variable-width int is encoded as a short if it will fit in 16 bits. + // The decoder looks at datalen to decide whether to unpack short or jint. + // Most relocation records are quite simple, containing at most two ints. + + static bool is_short(jint x) { return x == (short)x; } + static short* add_short(short* p, int x) { *p++ = x; return p; } + static short* add_jint (short* p, jint x) { + *p++ = relocInfo::data0_from_int(x); *p++ = relocInfo::data1_from_int(x); + return p; + } + static short* add_var_int(short* p, jint x) { // add a variable-width int + if (is_short(x)) p = add_short(p, x); + else p = add_jint (p, x); + return p; + } + + static short* pack_1_int_to(short* p, jint x0) { + // Format is one of: [] [x] [Xx] + if (x0 != 0) p = add_var_int(p, x0); + return p; + } + int unpack_1_int() { + assert(datalen() <= 2, "too much data"); + return relocInfo::jint_data_at(0, data(), datalen()); + } + + // With two ints, the short form is used only if both ints are short. + short* pack_2_ints_to(short* p, jint x0, jint x1) { + // Format is one of: [] [x y?] [Xx Y?y] + if (x0 == 0 && x1 == 0) { + // no halfwords needed to store zeroes + } else if (is_short(x0) && is_short(x1)) { + // 1-2 halfwords needed to store shorts + p = add_short(p, x0); if (x1!=0) p = add_short(p, x1); + } else { + // 3-4 halfwords needed to store jints + p = add_jint(p, x0); p = add_var_int(p, x1); + } + return p; + } + void unpack_2_ints(jint& x0, jint& x1) { + int dlen = datalen(); + short* dp = data(); + if (dlen <= 2) { + x0 = relocInfo::short_data_at(0, dp, dlen); + x1 = relocInfo::short_data_at(1, dp, dlen); + } else { + assert(dlen <= 4, "too much data"); + x0 = relocInfo::jint_data_at(0, dp, dlen); + x1 = relocInfo::jint_data_at(2, dp, dlen); + } + } + + protected: + // platform-dependent utilities for decoding and patching instructions + void pd_set_data_value (address x, intptr_t off); // a set or mem-ref + address pd_call_destination (address orig_addr = NULL); + void pd_set_call_destination (address x); + void pd_swap_in_breakpoint (address x, short* instrs, int instrlen); + void pd_swap_out_breakpoint (address x, short* instrs, int instrlen); + static int pd_breakpoint_size (); + + // this extracts the address of an address in the code stream instead of the reloc data + address* pd_address_in_code (); + + // this extracts an address from the code stream instead of the reloc data + address pd_get_address_from_code (); + + // these convert from byte offsets, to scaled offsets, to addresses + static jint scaled_offset(address x, address base) { + int byte_offset = x - base; + int offset = -byte_offset / relocInfo::addr_unit(); + assert(address_from_scaled_offset(offset, base) == x, "just checkin'"); + return offset; + } + static jint scaled_offset_null_special(address x, address base) { + // Some relocations treat offset=0 as meaning NULL. + // Handle this extra convention carefully. + if (x == NULL) return 0; + assert(x != base, "offset must not be zero"); + return scaled_offset(x, base); + } + static address address_from_scaled_offset(jint offset, address base) { + int byte_offset = -( offset * relocInfo::addr_unit() ); + return base + byte_offset; + } + + // these convert between indexes and addresses in the runtime system + static int32_t runtime_address_to_index(address runtime_address); + static address index_to_runtime_address(int32_t index); + + // helpers for mapping between old and new addresses after a move or resize + address old_addr_for(address newa, const CodeBuffer* src, CodeBuffer* dest); + address new_addr_for(address olda, const CodeBuffer* src, CodeBuffer* dest); + void normalize_address(address& addr, const CodeSection* dest, bool allow_other_sections = false); + + public: + // accessors which only make sense for a bound Relocation + address addr() const { return binding()->addr(); } + CodeBlob* code() const { return binding()->code(); } + bool addr_in_const() const { return binding()->addr_in_const(); } + protected: + short* data() const { return binding()->data(); } + int datalen() const { return binding()->datalen(); } + int format() const { return binding()->format(); } + + public: + virtual relocInfo::relocType type() { return relocInfo::none; } + + // is it a call instruction? + virtual bool is_call() { return false; } + + // is it a data movement instruction? + virtual bool is_data() { return false; } + + // some relocations can compute their own values + virtual address value(); + + // all relocations are able to reassert their values + virtual void set_value(address x); + + virtual void clear_inline_cache() { } + + // This method assumes that all virtual/static (inline) caches are cleared (since for static_call_type and + // ic_call_type is not always posisition dependent (depending on the state of the cache)). However, this is + // probably a reasonable assumption, since empty caches simplifies code reloacation. + virtual void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { } + + void print(); +}; + + +// certain inlines must be deferred until class Relocation is defined: + +inline RelocationHolder::RelocationHolder() { + // initialize the vtbl, just to keep things type-safe + new(*this) Relocation(); +} + + +inline RelocationHolder::RelocationHolder(Relocation* r) { + // wordwise copy from r (ok if it copies garbage after r) + for (int i = 0; i < _relocbuf_size; i++) { + _relocbuf[i] = ((void**)r)[i]; + } +} + + +relocInfo::relocType RelocationHolder::type() const { + return reloc()->type(); +} + +// A DataRelocation always points at a memory or load-constant instruction.. +// It is absolute on most machines, and the constant is split on RISCs. +// The specific subtypes are oop, external_word, and internal_word. +// By convention, the "value" does not include a separately reckoned "offset". +class DataRelocation : public Relocation { + public: + bool is_data() { return true; } + + // both target and offset must be computed somehow from relocation data + virtual int offset() { return 0; } + address value() = 0; + void set_value(address x) { set_value(x, offset()); } + void set_value(address x, intptr_t o) { + if (addr_in_const()) + *(address*)addr() = x; + else + pd_set_data_value(x, o); + } + + // The "o" (displacement) argument is relevant only to split relocations + // on RISC machines. In some CPUs (SPARC), the set-hi and set-lo ins'ns + // can encode more than 32 bits between them. This allows compilers to + // share set-hi instructions between addresses that differ by a small + // offset (e.g., different static variables in the same class). + // On such machines, the "x" argument to set_value on all set-lo + // instructions must be the same as the "x" argument for the + // corresponding set-hi instructions. The "o" arguments for the + // set-hi instructions are ignored, and must not affect the high-half + // immediate constant. The "o" arguments for the set-lo instructions are + // added into the low-half immediate constant, and must not overflow it. +}; + +// A CallRelocation always points at a call instruction. +// It is PC-relative on most machines. +class CallRelocation : public Relocation { + public: + bool is_call() { return true; } + + address destination() { return pd_call_destination(); } + void set_destination(address x); // pd_set_call_destination + + void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest); + address value() { return destination(); } + void set_value(address x) { set_destination(x); } +}; + +class oop_Relocation : public DataRelocation { + relocInfo::relocType type() { return relocInfo::oop_type; } + + public: + // encode in one of these formats: [] [n] [n l] [Nn l] [Nn Ll] + // an oop in the CodeBlob's oop pool + static RelocationHolder spec(int oop_index, int offset = 0) { + assert(oop_index > 0, "must be a pool-resident oop"); + RelocationHolder rh = newHolder(); + new(rh) oop_Relocation(oop_index, offset); + return rh; + } + // an oop in the instruction stream + static RelocationHolder spec_for_immediate() { + const int oop_index = 0; + const int offset = 0; // if you want an offset, use the oop pool + RelocationHolder rh = newHolder(); + new(rh) oop_Relocation(oop_index, offset); + return rh; + } + + private: + jint _oop_index; // if > 0, index into CodeBlob::oop_at + jint _offset; // byte offset to apply to the oop itself + + oop_Relocation(int oop_index, int offset) { + _oop_index = oop_index; _offset = offset; + } + + friend class RelocIterator; + oop_Relocation() { } + + public: + int oop_index() { return _oop_index; } + int offset() { return _offset; } + + // data is packed in "2_ints" format: [i o] or [Ii Oo] + void pack_data_to(CodeSection* dest); + void unpack_data(); + + void fix_oop_relocation(); // reasserts oop value + + address value() { return (address) *oop_addr(); } + + bool oop_is_immediate() { return oop_index() == 0; } + + oop* oop_addr(); // addr or &pool[jint_data] + oop oop_value(); // *oop_addr + // Note: oop_value transparently converts Universe::non_oop_word to NULL. +}; + +class virtual_call_Relocation : public CallRelocation { + relocInfo::relocType type() { return relocInfo::virtual_call_type; } + + public: + // "first_oop" points to the first associated set-oop. + // The oop_limit helps find the last associated set-oop. + // (See comments at the top of this file.) + static RelocationHolder spec(address first_oop, address oop_limit = NULL) { + RelocationHolder rh = newHolder(); + new(rh) virtual_call_Relocation(first_oop, oop_limit); + return rh; + } + + virtual_call_Relocation(address first_oop, address oop_limit) { + _first_oop = first_oop; _oop_limit = oop_limit; + assert(first_oop != NULL, "first oop address must be specified"); + } + + private: + address _first_oop; // location of first set-oop instruction + address _oop_limit; // search limit for set-oop instructions + + friend class RelocIterator; + virtual_call_Relocation() { } + + + public: + address first_oop(); + address oop_limit(); + + // data is packed as scaled offsets in "2_ints" format: [f l] or [Ff Ll] + // oop_limit is set to 0 if the limit falls somewhere within the call. + // When unpacking, a zero oop_limit is taken to refer to the end of the call. + // (This has the effect of bringing in the call's delay slot on SPARC.) + void pack_data_to(CodeSection* dest); + void unpack_data(); + + void clear_inline_cache(); + + // Figure out where an ic_call is hiding, given a set-oop or call. + // Either ic_call or first_oop must be non-null; the other is deduced. + // Code if non-NULL must be the CodeBlob, else it is deduced. + // The address of the patchable oop is also deduced. + // The returned iterator will enumerate over the oops and the ic_call, + // as well as any other relocations that happen to be in that span of code. + // Recognize relevant set_oops with: oop_reloc()->oop_addr() == oop_addr. + static RelocIterator parse_ic(CodeBlob* &code, address &ic_call, address &first_oop, oop* &oop_addr, bool *is_optimized); +}; + + +class opt_virtual_call_Relocation : public CallRelocation { + relocInfo::relocType type() { return relocInfo::opt_virtual_call_type; } + + public: + static RelocationHolder spec() { + RelocationHolder rh = newHolder(); + new(rh) opt_virtual_call_Relocation(); + return rh; + } + + private: + friend class RelocIterator; + opt_virtual_call_Relocation() { } + + public: + void clear_inline_cache(); + + // find the matching static_stub + address static_stub(); +}; + + +class static_call_Relocation : public CallRelocation { + relocInfo::relocType type() { return relocInfo::static_call_type; } + + public: + static RelocationHolder spec() { + RelocationHolder rh = newHolder(); + new(rh) static_call_Relocation(); + return rh; + } + + private: + friend class RelocIterator; + static_call_Relocation() { } + + public: + void clear_inline_cache(); + + // find the matching static_stub + address static_stub(); +}; + +class static_stub_Relocation : public Relocation { + relocInfo::relocType type() { return relocInfo::static_stub_type; } + + public: + static RelocationHolder spec(address static_call) { + RelocationHolder rh = newHolder(); + new(rh) static_stub_Relocation(static_call); + return rh; + } + + private: + address _static_call; // location of corresponding static_call + + static_stub_Relocation(address static_call) { + _static_call = static_call; + } + + friend class RelocIterator; + static_stub_Relocation() { } + + public: + void clear_inline_cache(); + + address static_call() { return _static_call; } + + // data is packed as a scaled offset in "1_int" format: [c] or [Cc] + void pack_data_to(CodeSection* dest); + void unpack_data(); +}; + +class runtime_call_Relocation : public CallRelocation { + relocInfo::relocType type() { return relocInfo::runtime_call_type; } + + public: + static RelocationHolder spec() { + RelocationHolder rh = newHolder(); + new(rh) runtime_call_Relocation(); + return rh; + } + + private: + friend class RelocIterator; + runtime_call_Relocation() { } + + public: +}; + +class external_word_Relocation : public DataRelocation { + relocInfo::relocType type() { return relocInfo::external_word_type; } + + public: + static RelocationHolder spec(address target) { + assert(target != NULL, "must not be null"); + RelocationHolder rh = newHolder(); + new(rh) external_word_Relocation(target); + return rh; + } + + // Use this one where all 32/64 bits of the target live in the code stream. + // The target must be an intptr_t, and must be absolute (not relative). + static RelocationHolder spec_for_immediate() { + RelocationHolder rh = newHolder(); + new(rh) external_word_Relocation(NULL); + return rh; + } + + private: + address _target; // address in runtime + + external_word_Relocation(address target) { + _target = target; + } + + friend class RelocIterator; + external_word_Relocation() { } + + public: + // data is packed as a well-known address in "1_int" format: [a] or [Aa] + // The function runtime_address_to_index is used to turn full addresses + // to short indexes, if they are pre-registered by the stub mechanism. + // If the "a" value is 0 (i.e., _target is NULL), the address is stored + // in the code stream. See external_word_Relocation::target(). + void pack_data_to(CodeSection* dest); + void unpack_data(); + + void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest); + address target(); // if _target==NULL, fetch addr from code stream + address value() { return target(); } +}; + +class internal_word_Relocation : public DataRelocation { + relocInfo::relocType type() { return relocInfo::internal_word_type; } + + public: + static RelocationHolder spec(address target) { + assert(target != NULL, "must not be null"); + RelocationHolder rh = newHolder(); + new(rh) internal_word_Relocation(target); + return rh; + } + + // use this one where all the bits of the target can fit in the code stream: + static RelocationHolder spec_for_immediate() { + RelocationHolder rh = newHolder(); + new(rh) internal_word_Relocation(NULL); + return rh; + } + + internal_word_Relocation(address target) { + _target = target; + _section = -1; // self-relative + } + + protected: + address _target; // address in CodeBlob + int _section; // section providing base address, if any + + friend class RelocIterator; + internal_word_Relocation() { } + + // bit-width of LSB field in packed offset, if section >= 0 + enum { section_width = 2 }; // must equal CodeBuffer::sect_bits + + public: + // data is packed as a scaled offset in "1_int" format: [o] or [Oo] + // If the "o" value is 0 (i.e., _target is NULL), the offset is stored + // in the code stream. See internal_word_Relocation::target(). + // If _section is not -1, it is appended to the low bits of the offset. + void pack_data_to(CodeSection* dest); + void unpack_data(); + + void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest); + address target(); // if _target==NULL, fetch addr from code stream + int section() { return _section; } + address value() { return target(); } +}; + +class section_word_Relocation : public internal_word_Relocation { + relocInfo::relocType type() { return relocInfo::section_word_type; } + + public: + static RelocationHolder spec(address target, int section) { + RelocationHolder rh = newHolder(); + new(rh) section_word_Relocation(target, section); + return rh; + } + + section_word_Relocation(address target, int section) { + assert(target != NULL, "must not be null"); + assert(section >= 0, "must be a valid section"); + _target = target; + _section = section; + } + + //void pack_data_to -- inherited + void unpack_data(); + + private: + friend class RelocIterator; + section_word_Relocation() { } +}; + + +class poll_Relocation : public Relocation { + bool is_data() { return true; } + relocInfo::relocType type() { return relocInfo::poll_type; } +}; + +class poll_return_Relocation : public Relocation { + bool is_data() { return true; } + relocInfo::relocType type() { return relocInfo::poll_return_type; } +}; + + +class breakpoint_Relocation : public Relocation { + relocInfo::relocType type() { return relocInfo::breakpoint_type; } + + enum { + // attributes which affect the interpretation of the data: + removable_attr = 0x0010, // buffer [i...] allows for undoing the trap + internal_attr = 0x0020, // the target is an internal addr (local stub) + settable_attr = 0x0040, // the target is settable + + // states which can change over time: + enabled_state = 0x0100, // breakpoint must be active in running code + active_state = 0x0200, // breakpoint instruction actually in code + + kind_mask = 0x000F, // mask for extracting kind + high_bit = 0x4000 // extra bit which is always set + }; + + public: + enum { + // kinds: + initialization = 1, + safepoint = 2 + }; + + // If target is NULL, 32 bits are reserved for a later set_target(). + static RelocationHolder spec(int kind, address target = NULL, bool internal_target = false) { + RelocationHolder rh = newHolder(); + new(rh) breakpoint_Relocation(kind, target, internal_target); + return rh; + } + + private: + // We require every bits value to NOT to fit into relocInfo::datalen_width, + // because we are going to actually store state in the reloc, and so + // cannot allow it to be compressed (and hence copied by the iterator). + + short _bits; // bit-encoded kind, attrs, & state + address _target; + + breakpoint_Relocation(int kind, address target, bool internal_target); + + friend class RelocIterator; + breakpoint_Relocation() { } + + short bits() const { return _bits; } + short& live_bits() const { return data()[0]; } + short* instrs() const { return data() + datalen() - instrlen(); } + int instrlen() const { return removable() ? pd_breakpoint_size() : 0; } + + void set_bits(short x) { + assert(live_bits() == _bits, "must be the only mutator of reloc info"); + live_bits() = _bits = x; + } + + public: + address target() const; + void set_target(address x); + + int kind() const { return bits() & kind_mask; } + bool enabled() const { return (bits() & enabled_state) != 0; } + bool active() const { return (bits() & active_state) != 0; } + bool internal() const { return (bits() & internal_attr) != 0; } + bool removable() const { return (bits() & removable_attr) != 0; } + bool settable() const { return (bits() & settable_attr) != 0; } + + void set_enabled(bool b); // to activate, you must also say set_active + void set_active(bool b); // actually inserts bpt (must be enabled 1st) + + // data is packed as 16 bits, followed by the target (1 or 2 words), followed + // if necessary by empty storage for saving away original instruction bytes. + void pack_data_to(CodeSection* dest); + void unpack_data(); + + // during certain operations, breakpoints must be out of the way: + void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { + assert(!active(), "cannot perform relocation on enabled breakpoints"); + } +}; + + +// We know all the xxx_Relocation classes, so now we can define these: +#define EACH_CASE(name) \ +inline name##_Relocation* RelocIterator::name##_reloc() { \ + assert(type() == relocInfo::name##_type, "type must agree"); \ + /* The purpose of the placed "new" is to re-use the same */ \ + /* stack storage for each new iteration. */ \ + name##_Relocation* r = new(_rh) name##_Relocation(); \ + r->set_binding(this); \ + r->name##_Relocation::unpack_data(); \ + return r; \ +} +APPLY_TO_RELOCATIONS(EACH_CASE); +#undef EACH_CASE + +inline RelocIterator::RelocIterator(CodeBlob* cb, address begin, address limit) { + initialize(cb, begin, limit); +} + +// if you are going to patch code, you should use this subclass of +// RelocIterator +class PatchingRelocIterator : public RelocIterator { + private: + RelocIterator _init_state; + + void prepass(); // deactivates all breakpoints + void postpass(); // reactivates all enabled breakpoints + + // do not copy these puppies; it would have unpredictable side effects + // these are private and have no bodies defined because they should not be called + PatchingRelocIterator(const RelocIterator&); + void operator=(const RelocIterator&); + + public: + PatchingRelocIterator(CodeBlob* cb, address begin =NULL, address limit =NULL) + : RelocIterator(cb, begin, limit) { prepass(); } + + ~PatchingRelocIterator() { postpass(); } +}; diff --git a/hotspot/src/share/vm/code/scopeDesc.cpp b/hotspot/src/share/vm/code/scopeDesc.cpp new file mode 100644 index 00000000000..23a7e7d8d1a --- /dev/null +++ b/hotspot/src/share/vm/code/scopeDesc.cpp @@ -0,0 +1,237 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_scopeDesc.cpp.incl" + + +ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset) { + _code = code; + _decode_offset = decode_offset; + _objects = decode_object_values(obj_decode_offset); + decode_body(); +} + +ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset) { + _code = code; + _decode_offset = decode_offset; + _objects = decode_object_values(DebugInformationRecorder::serialized_null); + decode_body(); +} + + +ScopeDesc::ScopeDesc(const ScopeDesc* parent) { + _code = parent->_code; + _decode_offset = parent->_sender_decode_offset; + _objects = parent->_objects; + decode_body(); +} + + +void ScopeDesc::decode_body() { + if (decode_offset() == DebugInformationRecorder::serialized_null) { + // This is a sentinel record, which is only relevant to + // approximate queries. Decode a reasonable frame. + _sender_decode_offset = DebugInformationRecorder::serialized_null; + _method = methodHandle(_code->method()); + _bci = InvocationEntryBci; + _locals_decode_offset = DebugInformationRecorder::serialized_null; + _expressions_decode_offset = DebugInformationRecorder::serialized_null; + _monitors_decode_offset = DebugInformationRecorder::serialized_null; + } else { + // decode header + DebugInfoReadStream* stream = stream_at(decode_offset()); + + _sender_decode_offset = stream->read_int(); + _method = methodHandle((methodOop) stream->read_oop()); + _bci = stream->read_bci(); + // decode offsets for body and sender + _locals_decode_offset = stream->read_int(); + _expressions_decode_offset = stream->read_int(); + _monitors_decode_offset = stream->read_int(); + } +} + + +GrowableArray* ScopeDesc::decode_scope_values(int decode_offset) { + if (decode_offset == DebugInformationRecorder::serialized_null) return NULL; + DebugInfoReadStream* stream = stream_at(decode_offset); + int length = stream->read_int(); + GrowableArray* result = new GrowableArray (length); + for (int index = 0; index < length; index++) { + result->push(ScopeValue::read_from(stream)); + } + return result; +} + +GrowableArray* ScopeDesc::decode_object_values(int decode_offset) { + if (decode_offset == DebugInformationRecorder::serialized_null) return NULL; + GrowableArray* result = new GrowableArray(); + DebugInfoReadStream* stream = new DebugInfoReadStream(_code, decode_offset, result); + int length = stream->read_int(); + for (int index = 0; index < length; index++) { + result->push(ScopeValue::read_from(stream)); + } + assert(result->length() == length, "inconsistent debug information"); + return result; +} + + +GrowableArray* ScopeDesc::decode_monitor_values(int decode_offset) { + if (decode_offset == DebugInformationRecorder::serialized_null) return NULL; + DebugInfoReadStream* stream = stream_at(decode_offset); + int length = stream->read_int(); + GrowableArray* result = new GrowableArray (length); + for (int index = 0; index < length; index++) { + result->push(new MonitorValue(stream)); + } + return result; +} + +DebugInfoReadStream* ScopeDesc::stream_at(int decode_offset) const { + return new DebugInfoReadStream(_code, decode_offset, _objects); +} + +GrowableArray* ScopeDesc::locals() { + return decode_scope_values(_locals_decode_offset); +} + +GrowableArray* ScopeDesc::expressions() { + return decode_scope_values(_expressions_decode_offset); +} + +GrowableArray* ScopeDesc::monitors() { + return decode_monitor_values(_monitors_decode_offset); +} + +GrowableArray* ScopeDesc::objects() { + return _objects; +} + +bool ScopeDesc::is_top() const { + return _sender_decode_offset == DebugInformationRecorder::serialized_null; +} + +ScopeDesc* ScopeDesc::sender() const { + if (is_top()) return NULL; + return new ScopeDesc(this); +} + + +#ifndef PRODUCT + +void ScopeDesc::print_value_on(outputStream* st) const { + tty->print(" "); + method()()->print_short_name(st); + int lineno = method()->line_number_from_bci(bci()); + if (lineno != -1) { + st->print_cr("@%d (line %d)", bci(), lineno); + } else { + st->print_cr("@%d", bci()); + } +} + +void ScopeDesc::print_on(outputStream* st) const { + print_on(st, NULL); +} + +void ScopeDesc::print_on(outputStream* st, PcDesc* pd) const { + // header + if (pd != NULL) { + tty->print_cr("ScopeDesc(pc=" PTR_FORMAT " offset=%x):", pd->real_pc(_code), pd->pc_offset()); + } + + print_value_on(st); + // decode offsets + if (WizardMode) { + st->print("ScopeDesc[%d]@" PTR_FORMAT " ", _decode_offset, _code->instructions_begin()); + st->print_cr(" offset: %d", _decode_offset); + st->print_cr(" bci: %d", bci()); + st->print_cr(" locals: %d", _locals_decode_offset); + st->print_cr(" stack: %d", _expressions_decode_offset); + st->print_cr(" monitor: %d", _monitors_decode_offset); + st->print_cr(" sender: %d", _sender_decode_offset); + } + // locals + { GrowableArray* l = ((ScopeDesc*) this)->locals(); + if (l != NULL) { + tty->print_cr(" Locals"); + for (int index = 0; index < l->length(); index++) { + st->print(" - l%d: ", index); + l->at(index)->print_on(st); + st->cr(); + } + } + } + // expressions + { GrowableArray* l = ((ScopeDesc*) this)->expressions(); + if (l != NULL) { + st->print_cr(" Expression stack"); + for (int index = 0; index < l->length(); index++) { + st->print(" - @%d: ", index); + l->at(index)->print_on(st); + st->cr(); + } + } + } + // monitors + { GrowableArray* l = ((ScopeDesc*) this)->monitors(); + if (l != NULL) { + st->print_cr(" Monitor stack"); + for (int index = 0; index < l->length(); index++) { + st->print(" - @%d: ", index); + l->at(index)->print_on(st); + st->cr(); + } + } + } + +#ifdef COMPILER2 + if (DoEscapeAnalysis && is_top() && _objects != NULL) { + tty->print_cr("Objects"); + for (int i = 0; i < _objects->length(); i++) { + ObjectValue* sv = (ObjectValue*) _objects->at(i); + tty->print(" - %d: ", sv->id()); + sv->print_fields_on(tty); + tty->cr(); + } + } +#endif // COMPILER2 +} + +#endif + +void ScopeDesc::verify() { + ResourceMark rm; + guarantee(method()->is_method(), "type check"); + + // check if we have any illegal elements on the expression stack + { GrowableArray* l = expressions(); + if (l != NULL) { + for (int index = 0; index < l->length(); index++) { + //guarantee(!l->at(index)->is_illegal(), "expression element cannot be illegal"); + } + } + } +} diff --git a/hotspot/src/share/vm/code/scopeDesc.hpp b/hotspot/src/share/vm/code/scopeDesc.hpp new file mode 100644 index 00000000000..35c99176c81 --- /dev/null +++ b/hotspot/src/share/vm/code/scopeDesc.hpp @@ -0,0 +1,123 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// SimpleScopeDesc is used when all you need to extract from +// a given pc,nmethod pair is a methodOop and a bci. This is +// quite a bit faster than allocating a full ScopeDesc, but +// very limited in abilities. + +class SimpleScopeDesc : public StackObj { + private: + methodOop _method; + int _bci; + + public: + SimpleScopeDesc(nmethod* code,address pc) { + PcDesc* pc_desc = code->pc_desc_at(pc); + assert(pc_desc != NULL, "Must be able to find matching PcDesc"); + DebugInfoReadStream buffer(code, pc_desc->scope_decode_offset()); + int ignore_sender = buffer.read_int(); + _method = methodOop(buffer.read_oop()); + _bci = buffer.read_bci(); + } + + methodOop method() { return _method; } + int bci() { return _bci; } +}; + +// ScopeDescs contain the information that makes source-level debugging of +// nmethods possible; each scopeDesc describes a method activation + +class ScopeDesc : public ResourceObj { + public: + // Constructor + ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset); + + // Calls above, giving default value of "serialized_null" to the + // "obj_decode_offset" argument. (We don't use a default argument to + // avoid a .hpp-.hpp dependency.) + ScopeDesc(const nmethod* code, int decode_offset); + + // JVM state + methodHandle method() const { return _method; } + int bci() const { return _bci; } + + GrowableArray* locals(); + GrowableArray* expressions(); + GrowableArray* monitors(); + GrowableArray* objects(); + + // Stack walking, returns NULL if this is the outer most scope. + ScopeDesc* sender() const; + + // Returns where the scope was decoded + int decode_offset() const { return _decode_offset; } + + // Tells whether sender() returns NULL + bool is_top() const; + // Tells whether sd is equal to this + bool is_equal(ScopeDesc* sd) const; + + private: + // Alternative constructor + ScopeDesc(const ScopeDesc* parent); + + // JVM state + methodHandle _method; + int _bci; + + // Decoding offsets + int _decode_offset; + int _sender_decode_offset; + int _locals_decode_offset; + int _expressions_decode_offset; + int _monitors_decode_offset; + + // Object pool + GrowableArray* _objects; + + // Nmethod information + const nmethod* _code; + + // Decoding operations + void decode_body(); + GrowableArray* decode_scope_values(int decode_offset); + GrowableArray* decode_monitor_values(int decode_offset); + GrowableArray* decode_object_values(int decode_offset); + + DebugInfoReadStream* stream_at(int decode_offset) const; + + + public: + // Verification + void verify(); + +#ifndef PRODUCT + public: + // Printing support + void print_on(outputStream* st) const; + void print_on(outputStream* st, PcDesc* pd) const; + void print_value_on(outputStream* st) const; +#endif +}; diff --git a/hotspot/src/share/vm/code/stubs.cpp b/hotspot/src/share/vm/code/stubs.cpp new file mode 100644 index 00000000000..90323e87824 --- /dev/null +++ b/hotspot/src/share/vm/code/stubs.cpp @@ -0,0 +1,254 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubs.cpp.incl" + + +// Implementation of StubQueue +// +// Standard wrap-around queue implementation; the queue dimensions +// are specified by the _queue_begin & _queue_end indices. The queue +// can be in two states (transparent to the outside): +// +// a) contiguous state: all queue entries in one block (or empty) +// +// Queue: |...|XXXXXXX|...............| +// ^0 ^begin ^end ^size = limit +// |_______| +// one block +// +// b) non-contiguous state: queue entries in two blocks +// +// Queue: |XXX|.......|XXXXXXX|.......| +// ^0 ^end ^begin ^limit ^size +// |___| |_______| +// 1st block 2nd block +// +// In the non-contiguous state, the wrap-around point is +// indicated via the _buffer_limit index since the last +// queue entry may not fill up the queue completely in +// which case we need to know where the 2nd block's end +// is to do the proper wrap-around. When removing the +// last entry of the 2nd block, _buffer_limit is reset +// to _buffer_size. +// +// CAUTION: DO NOT MESS WITH THIS CODE IF YOU CANNOT PROVE +// ITS CORRECTNESS! THIS CODE IS MORE SUBTLE THAN IT LOOKS! + + +StubQueue::StubQueue(StubInterface* stub_interface, int buffer_size, + Mutex* lock, const char* name) : _mutex(lock) { + intptr_t size = round_to(buffer_size, 2*BytesPerWord); + BufferBlob* blob = BufferBlob::create(name, size); + if( blob == NULL ) vm_exit_out_of_memory1(size, "CodeCache: no room for %s", name); + _stub_interface = stub_interface; + _buffer_size = blob->instructions_size(); + _buffer_limit = blob->instructions_size(); + _stub_buffer = blob->instructions_begin(); + _queue_begin = 0; + _queue_end = 0; + _number_of_stubs = 0; + register_queue(this); +} + + +StubQueue::~StubQueue() { + // Note: Currently StubQueues are never destroyed so nothing needs to be done here. + // If we want to implement the destructor, we need to release the BufferBlob + // allocated in the constructor (i.e., we need to keep it around or look it + // up via CodeCache::find_blob(...). + Unimplemented(); +} + + +Stub* StubQueue::stub_containing(address pc) const { + if (contains(pc)) { + for (Stub* s = first(); s != NULL; s = next(s)) { + if (stub_contains(s, pc)) return s; + } + } + return NULL; +} + + +Stub* StubQueue::request_committed(int code_size) { + Stub* s = request(code_size); + if (s != NULL) commit(code_size); + return s; +} + + +Stub* StubQueue::request(int requested_code_size) { + assert(requested_code_size > 0, "requested_code_size must be > 0"); + if (_mutex != NULL) _mutex->lock(); + Stub* s = current_stub(); + int requested_size = round_to(stub_code_size_to_size(requested_code_size), CodeEntryAlignment); + if (requested_size <= available_space()) { + if (is_contiguous()) { + // Queue: |...|XXXXXXX|.............| + // ^0 ^begin ^end ^size = limit + assert(_buffer_limit == _buffer_size, "buffer must be fully usable"); + if (_queue_end + requested_size <= _buffer_size) { + // code fits in at the end => nothing to do + stub_initialize(s, requested_size); + return s; + } else { + // stub doesn't fit in at the queue end + // => reduce buffer limit & wrap around + assert(!is_empty(), "just checkin'"); + _buffer_limit = _queue_end; + _queue_end = 0; + } + } + } + if (requested_size <= available_space()) { + assert(!is_contiguous(), "just checkin'"); + assert(_buffer_limit <= _buffer_size, "queue invariant broken"); + // Queue: |XXX|.......|XXXXXXX|.......| + // ^0 ^end ^begin ^limit ^size + s = current_stub(); + stub_initialize(s, requested_size); + return s; + } + // Not enough space left + if (_mutex != NULL) _mutex->unlock(); + return NULL; +} + + +void StubQueue::commit(int committed_code_size) { + assert(committed_code_size > 0, "committed_code_size must be > 0"); + int committed_size = round_to(stub_code_size_to_size(committed_code_size), CodeEntryAlignment); + Stub* s = current_stub(); + assert(committed_size <= stub_size(s), "committed size must not exceed requested size"); + stub_initialize(s, committed_size); + _queue_end += committed_size; + _number_of_stubs++; + if (_mutex != NULL) _mutex->unlock(); + debug_only(stub_verify(s);) +} + + +void StubQueue::remove_first() { + if (number_of_stubs() == 0) return; + Stub* s = first(); + debug_only(stub_verify(s);) + stub_finalize(s); + _queue_begin += stub_size(s); + assert(_queue_begin <= _buffer_limit, "sanity check"); + if (_queue_begin == _queue_end) { + // buffer empty + // => reset queue indices + _queue_begin = 0; + _queue_end = 0; + _buffer_limit = _buffer_size; + } else if (_queue_begin == _buffer_limit) { + // buffer limit reached + // => reset buffer limit & wrap around + _buffer_limit = _buffer_size; + _queue_begin = 0; + } + _number_of_stubs--; +} + + +void StubQueue::remove_first(int n) { + int i = MIN2(n, number_of_stubs()); + while (i-- > 0) remove_first(); +} + + +void StubQueue::remove_all(){ + debug_only(verify();) + remove_first(number_of_stubs()); + assert(number_of_stubs() == 0, "sanity check"); +} + + +enum { StubQueueLimit = 10 }; // there are only a few in the world +static StubQueue* registered_stub_queues[StubQueueLimit]; + +void StubQueue::register_queue(StubQueue* sq) { + for (int i = 0; i < StubQueueLimit; i++) { + if (registered_stub_queues[i] == NULL) { + registered_stub_queues[i] = sq; + return; + } + } + ShouldNotReachHere(); +} + + +void StubQueue::queues_do(void f(StubQueue* sq)) { + for (int i = 0; i < StubQueueLimit; i++) { + if (registered_stub_queues[i] != NULL) { + f(registered_stub_queues[i]); + } + } +} + + +void StubQueue::stubs_do(void f(Stub* s)) { + debug_only(verify();) + MutexLockerEx lock(_mutex); + for (Stub* s = first(); s != NULL; s = next(s)) f(s); +} + + +void StubQueue::verify() { + // verify only if initialized + if (_stub_buffer == NULL) return; + MutexLockerEx lock(_mutex); + // verify index boundaries + guarantee(0 <= _buffer_size, "buffer size must be positive"); + guarantee(0 <= _buffer_limit && _buffer_limit <= _buffer_size , "_buffer_limit out of bounds"); + guarantee(0 <= _queue_begin && _queue_begin < _buffer_limit, "_queue_begin out of bounds"); + guarantee(0 <= _queue_end && _queue_end <= _buffer_limit, "_queue_end out of bounds"); + // verify alignment + guarantee(_buffer_size % CodeEntryAlignment == 0, "_buffer_size not aligned"); + guarantee(_buffer_limit % CodeEntryAlignment == 0, "_buffer_limit not aligned"); + guarantee(_queue_begin % CodeEntryAlignment == 0, "_queue_begin not aligned"); + guarantee(_queue_end % CodeEntryAlignment == 0, "_queue_end not aligned"); + // verify buffer limit/size relationship + if (is_contiguous()) { + guarantee(_buffer_limit == _buffer_size, "_buffer_limit must equal _buffer_size"); + } + // verify contents + int n = 0; + for (Stub* s = first(); s != NULL; s = next(s)) { + stub_verify(s); + n++; + } + guarantee(n == number_of_stubs(), "number of stubs inconsistent"); + guarantee(_queue_begin != _queue_end || n == 0, "buffer indices must be the same"); +} + + +void StubQueue::print() { + MutexLockerEx lock(_mutex); + for (Stub* s = first(); s != NULL; s = next(s)) { + stub_print(s); + } +} diff --git a/hotspot/src/share/vm/code/stubs.hpp b/hotspot/src/share/vm/code/stubs.hpp new file mode 100644 index 00000000000..9fd6b9ea24b --- /dev/null +++ b/hotspot/src/share/vm/code/stubs.hpp @@ -0,0 +1,208 @@ +/* + * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The classes in this file provide a simple framework for the +// management of little pieces of machine code - or stubs - +// created on the fly and frequently discarded. In this frame- +// work stubs are stored in a queue. + + +// Stub serves as abstract base class. A concrete stub +// implementation is a subclass of Stub, implementing +// all (non-virtual!) functions required sketched out +// in the Stub class. +// +// A concrete stub layout may look like this (both data +// and code sections could be empty as well): +// +// ________ +// stub -->| | <--+ +// | data | | +// |________| | +// code_begin -->| | | +// | | | +// | code | | size +// | | | +// |________| | +// code_end -->| | | +// | data | | +// |________| | +// <--+ + + +class Stub VALUE_OBJ_CLASS_SPEC { + public: + // Initialization/finalization + void initialize(int size) { ShouldNotCallThis(); } // called to initialize/specify the stub's size + void finalize() { ShouldNotCallThis(); } // called before the stub is deallocated + + // General info/converters + int size() const { ShouldNotCallThis(); return 0; } // must return the size provided by initialize + static int code_size_to_size(int code_size) { ShouldNotCallThis(); return 0; } // computes the size given the code size + + // Code info + address code_begin() const { ShouldNotCallThis(); return NULL; } // points to the first byte of the code + address code_end() const { ShouldNotCallThis(); return NULL; } // points to the first byte after the code + + // Debugging + void verify() { ShouldNotCallThis(); } // verifies the Stub + void print() { ShouldNotCallThis(); } // prints some information about the stub +}; + + +// A stub interface defines the interface between a stub queue +// and the stubs it queues. In order to avoid a vtable and +// (and thus the extra word) in each stub, a concrete stub +// interface object is created and associated with a stub +// buffer which in turn uses the stub interface to interact +// with its stubs. +// +// StubInterface serves as an abstract base class. A concrete +// stub interface implementation is a subclass of StubInterface, +// forwarding its virtual function calls to non-virtual calls +// of the concrete stub (see also macro below). There's exactly +// one stub interface instance required per stub queue. + +class StubInterface: public CHeapObj { + public: + // Initialization/finalization + virtual void initialize(Stub* self, int size) = 0; // called after creation (called twice if allocated via (request, commit)) + virtual void finalize(Stub* self) = 0; // called before deallocation + + // General info/converters + virtual int size(Stub* self) const = 0; // the total size of the stub in bytes (must be a multiple of CodeEntryAlignment) + virtual int code_size_to_size(int code_size) const = 0; // computes the total stub size in bytes given the code size in bytes + + // Code info + virtual address code_begin(Stub* self) const = 0; // points to the first code byte + virtual address code_end(Stub* self) const = 0; // points to the first byte after the code + + // Debugging + virtual void verify(Stub* self) = 0; // verifies the stub + virtual void print(Stub* self) = 0; // prints information about the stub +}; + + +// DEF_STUB_INTERFACE is used to create a concrete stub interface +// class, forwarding stub interface calls to the corresponding +// stub calls. + +#define DEF_STUB_INTERFACE(stub) \ + class stub##Interface: public StubInterface { \ + private: \ + static stub* cast(Stub* self) { return (stub*)self; } \ + \ + public: \ + /* Initialization/finalization */ \ + virtual void initialize(Stub* self, int size) { cast(self)->initialize(size); } \ + virtual void finalize(Stub* self) { cast(self)->finalize(); } \ + \ + /* General info */ \ + virtual int size(Stub* self) const { return cast(self)->size(); } \ + virtual int code_size_to_size(int code_size) const { return stub::code_size_to_size(code_size); } \ + \ + /* Code info */ \ + virtual address code_begin(Stub* self) const { return cast(self)->code_begin(); } \ + virtual address code_end(Stub* self) const { return cast(self)->code_end(); } \ + \ + /* Debugging */ \ + virtual void verify(Stub* self) { cast(self)->verify(); } \ + virtual void print(Stub* self) { cast(self)->print(); } \ + }; + + +// A StubQueue maintains a queue of stubs. +// Note: All sizes (spaces) are given in bytes. + +class StubQueue: public CHeapObj { + friend class VMStructs; + private: + StubInterface* _stub_interface; // the interface prototype + address _stub_buffer; // where all stubs are stored + int _buffer_size; // the buffer size in bytes + int _buffer_limit; // the (byte) index of the actual buffer limit (_buffer_limit <= _buffer_size) + int _queue_begin; // the (byte) index of the first queue entry (word-aligned) + int _queue_end; // the (byte) index of the first entry after the queue (word-aligned) + int _number_of_stubs; // the number of buffered stubs + Mutex* const _mutex; // the lock used for a (request, commit) transaction + + void check_index(int i) const { assert(0 <= i && i < _buffer_limit && i % CodeEntryAlignment == 0, "illegal index"); } + bool is_contiguous() const { return _queue_begin <= _queue_end; } + int index_of(Stub* s) const { int i = (address)s - _stub_buffer; check_index(i); return i; } + Stub* stub_at(int i) const { check_index(i); return (Stub*)(_stub_buffer + i); } + Stub* current_stub() const { return stub_at(_queue_end); } + + // Stub functionality accessed via interface + void stub_initialize(Stub* s, int size) { assert(size % CodeEntryAlignment == 0, "size not aligned"); _stub_interface->initialize(s, size); } + void stub_finalize(Stub* s) { _stub_interface->finalize(s); } + int stub_size(Stub* s) const { return _stub_interface->size(s); } + bool stub_contains(Stub* s, address pc) const { return _stub_interface->code_begin(s) <= pc && pc < _stub_interface->code_end(s); } + int stub_code_size_to_size(int code_size) const { return _stub_interface->code_size_to_size(code_size); } + void stub_verify(Stub* s) { _stub_interface->verify(s); } + void stub_print(Stub* s) { _stub_interface->print(s); } + + static void register_queue(StubQueue*); + + public: + StubQueue(StubInterface* stub_interface, int buffer_size, Mutex* lock, + const char* name); + ~StubQueue(); + + // General queue info + bool is_empty() const { return _queue_begin == _queue_end; } + int total_space() const { return _buffer_size - 1; } + int available_space() const { int d = _queue_begin - _queue_end - 1; return d < 0 ? d + _buffer_size : d; } + int used_space() const { return total_space() - available_space(); } + int number_of_stubs() const { return _number_of_stubs; } + bool contains(address pc) const { return _stub_buffer <= pc && pc < _stub_buffer + _buffer_limit; } + Stub* stub_containing(address pc) const; + address code_start() const { return _stub_buffer; } + address code_end() const { return _stub_buffer + _buffer_limit; } + + // Stub allocation (atomic transactions) + Stub* request_committed(int code_size); // request a stub that provides exactly code_size space for code + Stub* request(int requested_code_size); // request a stub with a (maximum) code space - locks the queue + void commit (int committed_code_size); // commit the previously requested stub - unlocks the queue + + // Stub deallocation + void remove_first(); // remove the first stub in the queue + void remove_first(int n); // remove the first n stubs in the queue + void remove_all(); // remove all stubs in the queue + + // Iteration + static void queues_do(void f(StubQueue* s)); // call f with each StubQueue + void stubs_do(void f(Stub* s)); // call f with all stubs + Stub* first() const { return number_of_stubs() > 0 ? stub_at(_queue_begin) : NULL; } + Stub* next(Stub* s) const { int i = index_of(s) + stub_size(s); + if (i == _buffer_limit) i = 0; + return (i == _queue_end) ? NULL : stub_at(i); + } + + address stub_code_begin(Stub* s) const { return _stub_interface->code_begin(s); } + address stub_code_end(Stub* s) const { return _stub_interface->code_end(s); } + + // Debugging/printing + void verify(); // verifies the stub queue + void print(); // prints information about the stub queue +}; diff --git a/hotspot/src/share/vm/code/vmreg.cpp b/hotspot/src/share/vm/code/vmreg.cpp new file mode 100644 index 00000000000..32e70f8cda4 --- /dev/null +++ b/hotspot/src/share/vm/code/vmreg.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vmreg.cpp.incl" + +// First VMReg value that could refer to a stack slot +VMReg VMRegImpl::stack0 = (VMReg)(intptr_t)((ConcreteRegisterImpl::number_of_registers + 1) & ~1); + +// VMRegs are 4 bytes wide on all platforms +const int VMRegImpl::stack_slot_size = 4; +const int VMRegImpl::slots_per_word = wordSize / stack_slot_size; + +const int VMRegImpl::register_count = ConcreteRegisterImpl::number_of_registers; +// Register names +const char *VMRegImpl::regName[ConcreteRegisterImpl::number_of_registers]; + +void VMRegImpl::print() { +#ifndef PRODUCT + if( is_reg() ) { + assert( VMRegImpl::regName[value()], "" ); + tty->print("%s",VMRegImpl::regName[value()]); + } else if (is_stack()) { + int stk = value() - stack0->value(); + tty->print("[%d]", stk*4); + } else { + tty->print("BAD!"); + } +#endif // PRODUCT +} diff --git a/hotspot/src/share/vm/code/vmreg.hpp b/hotspot/src/share/vm/code/vmreg.hpp new file mode 100644 index 00000000000..66b3540789f --- /dev/null +++ b/hotspot/src/share/vm/code/vmreg.hpp @@ -0,0 +1,182 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//------------------------------VMReg------------------------------------------ +// The VM uses 'unwarped' stack slots; the compiler uses 'warped' stack slots. +// Register numbers below VMRegImpl::stack0 are the same for both. Register +// numbers above stack0 are either warped (in the compiler) or unwarped +// (in the VM). Unwarped numbers represent stack indices, offsets from +// the current stack pointer. Warped numbers are required during compilation +// when we do not yet know how big the frame will be. + +class VMRegImpl; +typedef VMRegImpl* VMReg; + +class VMRegImpl { +// friend class OopMap; +friend class VMStructs; +friend class OptoReg; +// friend class Location; +private: + enum { + BAD = -1 + }; + + + + static VMReg stack0; + // Names for registers + static const char *regName[]; + static const int register_count; + + +public: + + static VMReg as_VMReg(int val, bool bad_ok = false) { assert(val > BAD || bad_ok, "invalid"); return (VMReg) (intptr_t) val; } + + const char* name() { + if (is_reg()) { + return regName[value()]; + } else if (!is_valid()) { + return "BAD"; + } else { + // shouldn't really be called with stack + return "STACKED REG"; + } + } + static VMReg Bad() { return (VMReg) (intptr_t) BAD; } + bool is_valid() { return ((intptr_t) this) != BAD; } + bool is_stack() { return (intptr_t) this >= (intptr_t) stack0; } + bool is_reg() { return is_valid() && !is_stack(); } + + // A concrete register is a value that returns true for is_reg() and is + // also a register you could use in the assembler. On machines with + // 64bit registers only one half of the VMReg (and OptoReg) is considered + // concrete. + bool is_concrete(); + + // VMRegs are 4 bytes wide on all platforms + static const int stack_slot_size; + static const int slots_per_word; + + + // This really ought to check that the register is "real" in the sense that + // we don't try and get the VMReg number of a physical register that doesn't + // have an expressible part. That would be pd specific code + VMReg next() { + assert((is_reg() && value() < stack0->value() - 1) || is_stack(), "must be"); + return (VMReg)(intptr_t)(value() + 1); + } + VMReg prev() { + assert((is_stack() && value() > stack0->value()) || (is_reg() && value() != 0), "must be"); + return (VMReg)(intptr_t)(value() - 1); + } + + + intptr_t value() const {return (intptr_t) this; } + + void print(); + + // bias a stack slot. + // Typically used to adjust a virtual frame slots by amounts that are offset by + // amounts that are part of the native abi. The VMReg must be a stack slot + // and the result must be also. + + VMReg bias(int offset) { + assert(is_stack(), "must be"); + // VMReg res = VMRegImpl::as_VMReg(value() + offset); + VMReg res = stack2reg(reg2stack() + offset); + assert(res->is_stack(), "must be"); + return res; + } + + // Convert register numbers to stack slots and vice versa + static VMReg stack2reg( int idx ) { + return (VMReg) (intptr_t) (stack0->value() + idx); + } + + uintptr_t reg2stack() { + assert( is_stack(), "Not a stack-based register" ); + return value() - stack0->value(); + } + + static void set_regName(); + +#include "incls/_vmreg_pd.hpp.incl" + +}; + +//---------------------------VMRegPair------------------------------------------- +// Pairs of 32-bit registers for arguments. +// SharedRuntime::java_calling_convention will overwrite the structs with +// the calling convention's registers. VMRegImpl::Bad is returned for any +// unused 32-bit register. This happens for the unused high half of Int +// arguments, or for 32-bit pointers or for longs in the 32-bit sparc build +// (which are passed to natives in low 32-bits of e.g. O0/O1 and the high +// 32-bits of O0/O1 are set to VMRegImpl::Bad). Longs in one register & doubles +// always return a high and a low register, as do 64-bit pointers. +// +class VMRegPair { +private: + VMReg _second; + VMReg _first; +public: + void set_bad ( ) { _second=VMRegImpl::Bad(); _first=VMRegImpl::Bad(); } + void set1 ( VMReg v ) { _second=VMRegImpl::Bad(); _first=v; } + void set2 ( VMReg v ) { _second=v->next(); _first=v; } + void set_pair( VMReg second, VMReg first ) { _second= second; _first= first; } + void set_ptr ( VMReg ptr ) { +#ifdef _LP64 + _second = ptr->next(); +#else + _second = VMRegImpl::Bad(); +#endif + _first = ptr; + } + // Return true if single register, even if the pair is really just adjacent stack slots + bool is_single_reg() { + return (_first->is_valid()) && (_first->value() + 1 == _second->value()); + } + + // Return true if single stack based "register" where the slot alignment matches input alignment + bool is_adjacent_on_stack(int alignment) { + return (_first->is_stack() && (_first->value() + 1 == _second->value()) && ((_first->value() & (alignment-1)) == 0)); + } + + // Return true if single stack based "register" where the slot alignment matches input alignment + bool is_adjacent_aligned_on_stack(int alignment) { + return (_first->is_stack() && (_first->value() + 1 == _second->value()) && ((_first->value() & (alignment-1)) == 0)); + } + + // Return true if single register but adjacent stack slots do not count + bool is_single_phys_reg() { + return (_first->is_reg() && (_first->value() + 1 == _second->value())); + } + + VMReg second() const { return _second; } + VMReg first() const { return _first; } + VMRegPair(VMReg s, VMReg f) { _second = s; _first = f; } + VMRegPair(VMReg f) { _second = VMRegImpl::Bad(); _first = f; } + VMRegPair() { _second = VMRegImpl::Bad(); _first = VMRegImpl::Bad(); } +}; diff --git a/hotspot/src/share/vm/code/vtableStubs.cpp b/hotspot/src/share/vm/code/vtableStubs.cpp new file mode 100644 index 00000000000..76c0f3356e0 --- /dev/null +++ b/hotspot/src/share/vm/code/vtableStubs.cpp @@ -0,0 +1,197 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vtableStubs.cpp.incl" + +// ----------------------------------------------------------------------------------------- +// Implementation of VtableStub + +address VtableStub::_chunk = NULL; +address VtableStub::_chunk_end = NULL; +VMReg VtableStub::_receiver_location = VMRegImpl::Bad(); + +static int num_vtable_chunks = 0; + + +void* VtableStub::operator new(size_t size, int code_size) { + assert(size == sizeof(VtableStub), "mismatched size"); + num_vtable_chunks++; + // compute real VtableStub size (rounded to nearest word) + const int real_size = round_to(code_size + sizeof(VtableStub), wordSize); + // malloc them in chunks to minimize header overhead + const int chunk_factor = 32; + if (_chunk == NULL || _chunk + real_size > _chunk_end) { + const int bytes = chunk_factor * real_size + pd_code_alignment(); + BufferBlob* blob = BufferBlob::create("vtable chunks", bytes); + if( blob == NULL ) vm_exit_out_of_memory1(bytes, "CodeCache: no room for %s", "vtable chunks"); + _chunk = blob->instructions_begin(); + _chunk_end = _chunk + bytes; + VTune::register_stub("vtable stub", _chunk, _chunk_end); + Forte::register_stub("vtable stub", _chunk, _chunk_end); + // Notify JVMTI about this stub. The event will be recorded by the enclosing + // JvmtiDynamicCodeEventCollector and posted when this thread has released + // all locks. + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated_while_holding_locks("vtable stub", _chunk, _chunk_end); + } + align_chunk(); + } + assert(_chunk + real_size <= _chunk_end, "bad allocation"); + void* res = _chunk; + _chunk += real_size; + align_chunk(); + return res; +} + + +void VtableStub::print() { + tty->print("vtable stub (index = %d, receiver_location = %d, code = [" INTPTR_FORMAT ", " INTPTR_FORMAT "[)", + index(), receiver_location(), code_begin(), code_end()); +} + + +// ----------------------------------------------------------------------------------------- +// Implementation of VtableStubs +// +// For each hash value there's a linked list of vtable stubs (with that +// hash value). Each list is anchored in a little hash _table, indexed +// by that hash value. + +VtableStub* VtableStubs::_table[VtableStubs::N]; +int VtableStubs::_number_of_vtable_stubs = 0; + + +void VtableStubs::initialize() { + VtableStub::_receiver_location = SharedRuntime::name_for_receiver(); + { + MutexLocker ml(VtableStubs_lock); + assert(_number_of_vtable_stubs == 0, "potential performance bug: VtableStubs initialized more than once"); + assert(is_power_of_2(N), "N must be a power of 2"); + for (int i = 0; i < N; i++) { + _table[i] = NULL; + } + } +} + + +address VtableStubs::create_stub(bool is_vtable_stub, int vtable_index, methodOop method) { + assert(vtable_index >= 0, "must be positive"); + + VtableStub* s = ShareVtableStubs ? lookup(is_vtable_stub, vtable_index) : NULL; + if (s == NULL) { + if (is_vtable_stub) { + s = create_vtable_stub(vtable_index); + } else { + s = create_itable_stub(vtable_index); + } + enter(is_vtable_stub, vtable_index, s); +#ifndef PRODUCT + if (PrintAdapterHandlers) { + tty->print_cr("Decoding VtableStub %s[%d]@%d", + is_vtable_stub? "vtbl": "itbl", vtable_index, VtableStub::receiver_location()); + Disassembler::decode(s->code_begin(), s->code_end()); + } +#endif + } + return s->entry_point(); +} + + +inline uint VtableStubs::hash(bool is_vtable_stub, int vtable_index){ + // Assumption: receiver_location < 4 in most cases. + int hash = ((vtable_index << 2) ^ VtableStub::receiver_location()->value()) + vtable_index; + return (is_vtable_stub ? ~hash : hash) & mask; +} + + +VtableStub* VtableStubs::lookup(bool is_vtable_stub, int vtable_index) { + MutexLocker ml(VtableStubs_lock); + unsigned hash = VtableStubs::hash(is_vtable_stub, vtable_index); + VtableStub* s = _table[hash]; + while( s && !s->matches(is_vtable_stub, vtable_index)) s = s->next(); + return s; +} + + +void VtableStubs::enter(bool is_vtable_stub, int vtable_index, VtableStub* s) { + MutexLocker ml(VtableStubs_lock); + assert(s->matches(is_vtable_stub, vtable_index), "bad vtable stub"); + unsigned int h = VtableStubs::hash(is_vtable_stub, vtable_index); + // enter s at the beginning of the corresponding list + s->set_next(_table[h]); + _table[h] = s; + _number_of_vtable_stubs++; +} + + +bool VtableStubs::is_entry_point(address pc) { + MutexLocker ml(VtableStubs_lock); + VtableStub* stub = (VtableStub*)(pc - VtableStub::entry_offset()); + uint hash = VtableStubs::hash(stub->is_vtable_stub(), stub->index()); + VtableStub* s; + for (s = _table[hash]; s != NULL && s != stub; s = s->next()) {} + return s == stub; +} + + +bool VtableStubs::contains(address pc) { + // simple solution for now - we may want to use + // a faster way if this function is called often + return stub_containing(pc) != NULL; +} + + +VtableStub* VtableStubs::stub_containing(address pc) { + // Note: No locking needed since any change to the data structure + // happens with an atomic store into it (we don't care about + // consistency with the _number_of_vtable_stubs counter). + for (int i = 0; i < N; i++) { + for (VtableStub* s = _table[i]; s != NULL; s = s->next()) { + if (s->contains(pc)) return s; + } + } + return NULL; +} + +void vtableStubs_init() { + VtableStubs::initialize(); +} + + +//----------------------------------------------------------------------------------------------------- +// Non-product code +#ifndef PRODUCT + +extern "C" void bad_compiled_vtable_index(JavaThread* thread, oop receiver, int index) { + ResourceMark rm; + HandleMark hm; + klassOop klass = receiver->klass(); + instanceKlass* ik = instanceKlass::cast(klass); + klassVtable* vt = ik->vtable(); + klass->print(); + fatal3("bad compiled vtable dispatch: receiver " INTPTR_FORMAT ", index %d (vtable length %d)", (address)receiver, index, vt->length()); +} + +#endif // Product diff --git a/hotspot/src/share/vm/code/vtableStubs.hpp b/hotspot/src/share/vm/code/vtableStubs.hpp new file mode 100644 index 00000000000..e9f41f7fddd --- /dev/null +++ b/hotspot/src/share/vm/code/vtableStubs.hpp @@ -0,0 +1,121 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A VtableStub holds an individual code stub for a pair (vtable index, #args) for either itables or vtables +// There's a one-to-one relationship between a VtableStub and such a pair. + +class VtableStub { + private: + friend class VtableStubs; + + static address _chunk; // For allocation + static address _chunk_end; // For allocation + static VMReg _receiver_location; // Where to find receiver + + VtableStub* _next; // Pointer to next entry in hash table + const short _index; // vtable index + short _ame_offset; // Where an AbstractMethodError might occur + short _npe_offset; // Where a NullPointerException might occur + bool _is_vtable_stub; // True if vtable stub, false, is itable stub + /* code follows here */ // The vtableStub code + + void* operator new(size_t size, int code_size); + + VtableStub(bool is_vtable_stub, int index) + : _next(NULL), _is_vtable_stub(is_vtable_stub), + _index(index), _ame_offset(-1), _npe_offset(-1) {} + VtableStub* next() const { return _next; } + int index() const { return _index; } + static VMReg receiver_location() { return _receiver_location; } + void set_next(VtableStub* n) { _next = n; } + address code_begin() const { return (address)(this + 1); } + address code_end() const { return code_begin() + pd_code_size_limit(_is_vtable_stub); } + address entry_point() const { return code_begin(); } + static int entry_offset() { return sizeof(class VtableStub); } + + bool matches(bool is_vtable_stub, int index) const { + return _index == index && _is_vtable_stub == is_vtable_stub; + } + bool contains(address pc) const { return code_begin() <= pc && pc < code_end(); } + + void set_exception_points(address npe_addr, address ame_addr) { + _npe_offset = npe_addr - code_begin(); + _ame_offset = ame_addr - code_begin(); + assert(is_abstract_method_error(ame_addr), "offset must be correct"); + assert(is_null_pointer_exception(npe_addr), "offset must be correct"); + assert(!is_abstract_method_error(npe_addr), "offset must be correct"); + assert(!is_null_pointer_exception(ame_addr), "offset must be correct"); + } + + // platform-dependent routines + static int pd_code_size_limit(bool is_vtable_stub); + static int pd_code_alignment(); + // CNC: Removed because vtable stubs are now made with an ideal graph + // static bool pd_disregard_arg_size(); + + static void align_chunk() { + uintptr_t off = (uintptr_t)( _chunk + sizeof(VtableStub) ) % pd_code_alignment(); + if (off != 0) _chunk += pd_code_alignment() - off; + } + + public: + // Query + bool is_itable_stub() { return !_is_vtable_stub; } + bool is_vtable_stub() { return _is_vtable_stub; } + bool is_abstract_method_error(address epc) { return epc == code_begin()+_ame_offset; } + bool is_null_pointer_exception(address epc) { return epc == code_begin()+_npe_offset; } + + void print(); +}; + + +// VtableStubs creates the code stubs for compiled calls through vtables. +// There is one stub per (vtable index, args_size) pair, and the stubs are +// never deallocated. They don't need to be GCed because they contain no oops. + +class VtableStubs : AllStatic { + public: // N must be public (some compilers need this for _table) + enum { + N = 256, // size of stub table; must be power of two + mask = N - 1 + }; + + private: + static VtableStub* _table[N]; // table of existing stubs + static int _number_of_vtable_stubs; // number of stubs created so far (for statistics) + + static VtableStub* create_vtable_stub(int vtable_index); + static VtableStub* create_itable_stub(int vtable_index); + static VtableStub* lookup (bool is_vtable_stub, int vtable_index); + static void enter (bool is_vtable_stub, int vtable_index, VtableStub* s); + static inline uint hash (bool is_vtable_stub, int vtable_index); + + public: + static address create_stub(bool is_vtable_stub, int vtable_index, methodOop method); // return the entry point of a stub for this call + static bool is_entry_point(address pc); // is pc a vtable stub entry point? + static bool contains(address pc); // is pc within any stub? + static VtableStub* stub_containing(address pc); // stub containing pc or NULL + static int number_of_vtable_stubs() { return _number_of_vtable_stubs; } + static void initialize(); +}; diff --git a/hotspot/src/share/vm/compiler/abstractCompiler.cpp b/hotspot/src/share/vm/compiler/abstractCompiler.cpp new file mode 100644 index 00000000000..5357fde85b2 --- /dev/null +++ b/hotspot/src/share/vm/compiler/abstractCompiler.cpp @@ -0,0 +1,61 @@ +// +// Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// + +#include "incls/_precompiled.incl" +#include "incls/_abstractCompiler.cpp.incl" + +void AbstractCompiler::initialize_runtimes(initializer f, volatile int* state) { + if (*state != initialized) { + + // We are thread in native here... + CompilerThread* thread = CompilerThread::current(); + bool do_initialization = false; + { + ThreadInVMfromNative tv(thread); + MutexLocker only_one(CompileThread_lock, thread); + if ( *state == uninitialized) { + do_initialization = true; + *state = initializing; + } else { + while (*state == initializing ) { + CompileThread_lock->wait(); + } + } + } + if (do_initialization) { + // We can not hold any locks here since JVMTI events may call agents + + // Compiler(s) run as native + + (*f)(); + + // To in_vm so we can use the lock + + ThreadInVMfromNative tv(thread); + MutexLocker only_one(CompileThread_lock, thread); + assert(*state == initializing, "wrong state"); + *state = initialized; + CompileThread_lock->notify_all(); + } + } +} diff --git a/hotspot/src/share/vm/compiler/abstractCompiler.hpp b/hotspot/src/share/vm/compiler/abstractCompiler.hpp new file mode 100644 index 00000000000..547485f8149 --- /dev/null +++ b/hotspot/src/share/vm/compiler/abstractCompiler.hpp @@ -0,0 +1,82 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +typedef void (*initializer)(void); + +class AbstractCompiler : public CHeapObj { + private: + bool _is_initialized; // Mark whether compiler object is initialized + + protected: + // Used for tracking global state of compiler runtime initialization + enum { uninitialized, initializing, initialized }; + + // This method will call the initialization method "f" once (per compiler class/subclass) + // and do so without holding any locks + void initialize_runtimes(initializer f, volatile int* state); + + public: + AbstractCompiler() : _is_initialized(false) {} + + // Name of this compiler + virtual const char* name() = 0; + + // Missing feature tests + virtual bool supports_native() { return true; } + virtual bool supports_osr () { return true; } +#if defined(TIERED) || ( !defined(COMPILER1) && !defined(COMPILER2)) + virtual bool is_c1 () { return false; } + virtual bool is_c2 () { return false; } +#else +#ifdef COMPILER1 + bool is_c1 () { return true; } + bool is_c2 () { return false; } +#endif // COMPILER1 +#ifdef COMPILER2 + bool is_c1 () { return false; } + bool is_c2 () { return true; } +#endif // COMPILER2 +#endif // TIERED + + // Customization + virtual bool needs_stubs () = 0; + + void mark_initialized() { _is_initialized = true; } + bool is_initialized() { return _is_initialized; } + + virtual void initialize() = 0; + + // Compilation entry point for methods + virtual void compile_method(ciEnv* env, + ciMethod* target, + int entry_bci) { + ShouldNotReachHere(); + } + + + // Print compilation timers and statistics + virtual void print_timers() { + ShouldNotReachHere(); + } +}; diff --git a/hotspot/src/share/vm/compiler/compileBroker.cpp b/hotspot/src/share/vm/compiler/compileBroker.cpp new file mode 100644 index 00000000000..3157d45036e --- /dev/null +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp @@ -0,0 +1,1857 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_compileBroker.cpp.incl" + +#ifdef DTRACE_ENABLED + +// Only bother with this argument setup if dtrace is available + +HS_DTRACE_PROBE_DECL8(hotspot, method__compile__begin, + char*, intptr_t, char*, intptr_t, char*, intptr_t, char*, intptr_t); +HS_DTRACE_PROBE_DECL9(hotspot, method__compile__end, + char*, intptr_t, char*, intptr_t, char*, intptr_t, char*, intptr_t, bool); + +#define DTRACE_METHOD_COMPILE_BEGIN_PROBE(compiler, method) \ + { \ + char* comp_name = (char*)(compiler)->name(); \ + symbolOop klass_name = (method)->klass_name(); \ + symbolOop name = (method)->name(); \ + symbolOop signature = (method)->signature(); \ + HS_DTRACE_PROBE8(hotspot, method__compile__begin, \ + comp_name, strlen(comp_name), \ + klass_name->bytes(), klass_name->utf8_length(), \ + name->bytes(), name->utf8_length(), \ + signature->bytes(), signature->utf8_length()); \ + } + +#define DTRACE_METHOD_COMPILE_END_PROBE(compiler, method, success) \ + { \ + char* comp_name = (char*)(compiler)->name(); \ + symbolOop klass_name = (method)->klass_name(); \ + symbolOop name = (method)->name(); \ + symbolOop signature = (method)->signature(); \ + HS_DTRACE_PROBE9(hotspot, method__compile__end, \ + comp_name, strlen(comp_name), \ + klass_name->bytes(), klass_name->utf8_length(), \ + name->bytes(), name->utf8_length(), \ + signature->bytes(), signature->utf8_length(), (success)); \ + } + +#else // ndef DTRACE_ENABLED + +#define DTRACE_METHOD_COMPILE_BEGIN_PROBE(compiler, method) +#define DTRACE_METHOD_COMPILE_END_PROBE(compiler, method, success) + +#endif // ndef DTRACE_ENABLED + +bool CompileBroker::_initialized = false; +volatile bool CompileBroker::_should_block = false; + +// The installed compiler(s) +AbstractCompiler* CompileBroker::_compilers[2]; + +// These counters are used for assigning id's to each compilation +uint CompileBroker::_compilation_id = 0; +uint CompileBroker::_osr_compilation_id = 0; + +// Debugging information +int CompileBroker::_last_compile_type = no_compile; +int CompileBroker::_last_compile_level = CompLevel_none; +char CompileBroker::_last_method_compiled[CompileBroker::name_buffer_length]; + +// Performance counters +PerfCounter* CompileBroker::_perf_total_compilation = NULL; +PerfCounter* CompileBroker::_perf_osr_compilation = NULL; +PerfCounter* CompileBroker::_perf_standard_compilation = NULL; + +PerfCounter* CompileBroker::_perf_total_bailout_count = NULL; +PerfCounter* CompileBroker::_perf_total_invalidated_count = NULL; +PerfCounter* CompileBroker::_perf_total_compile_count = NULL; +PerfCounter* CompileBroker::_perf_total_osr_compile_count = NULL; +PerfCounter* CompileBroker::_perf_total_standard_compile_count = NULL; + +PerfCounter* CompileBroker::_perf_sum_osr_bytes_compiled = NULL; +PerfCounter* CompileBroker::_perf_sum_standard_bytes_compiled = NULL; +PerfCounter* CompileBroker::_perf_sum_nmethod_size = NULL; +PerfCounter* CompileBroker::_perf_sum_nmethod_code_size = NULL; + +PerfStringVariable* CompileBroker::_perf_last_method = NULL; +PerfStringVariable* CompileBroker::_perf_last_failed_method = NULL; +PerfStringVariable* CompileBroker::_perf_last_invalidated_method = NULL; +PerfVariable* CompileBroker::_perf_last_compile_type = NULL; +PerfVariable* CompileBroker::_perf_last_compile_size = NULL; +PerfVariable* CompileBroker::_perf_last_failed_type = NULL; +PerfVariable* CompileBroker::_perf_last_invalidated_type = NULL; + +// Timers and counters for generating statistics +elapsedTimer CompileBroker::_t_total_compilation; +elapsedTimer CompileBroker::_t_osr_compilation; +elapsedTimer CompileBroker::_t_standard_compilation; + +int CompileBroker::_total_bailout_count = 0; +int CompileBroker::_total_invalidated_count = 0; +int CompileBroker::_total_compile_count = 0; +int CompileBroker::_total_osr_compile_count = 0; +int CompileBroker::_total_standard_compile_count = 0; + +int CompileBroker::_sum_osr_bytes_compiled = 0; +int CompileBroker::_sum_standard_bytes_compiled = 0; +int CompileBroker::_sum_nmethod_size = 0; +int CompileBroker::_sum_nmethod_code_size = 0; + +CompileQueue* CompileBroker::_method_queue = NULL; +CompileTask* CompileBroker::_task_free_list = NULL; + +GrowableArray* CompileBroker::_method_threads = NULL; + +// CompileTaskWrapper +// +// Assign this task to the current thread. Deallocate the task +// when the compilation is complete. +class CompileTaskWrapper : StackObj { +public: + CompileTaskWrapper(CompileTask* task); + ~CompileTaskWrapper(); +}; + +CompileTaskWrapper::CompileTaskWrapper(CompileTask* task) { + CompilerThread* thread = CompilerThread::current(); + thread->set_task(task); + CompileLog* log = thread->log(); + if (log != NULL) task->log_task_start(log); +} + +CompileTaskWrapper::~CompileTaskWrapper() { + CompilerThread* thread = CompilerThread::current(); + CompileTask* task = thread->task(); + CompileLog* log = thread->log(); + if (log != NULL) task->log_task_done(log); + thread->set_task(NULL); + task->set_code_handle(NULL); + DEBUG_ONLY(thread->set_env((ciEnv*)badAddress)); + if (task->is_blocking()) { + MutexLocker notifier(task->lock(), thread); + task->mark_complete(); + // Notify the waiting thread that the compilation has completed. + task->lock()->notify_all(); + } else { + task->mark_complete(); + + // By convention, the compiling thread is responsible for + // recycling a non-blocking CompileTask. + CompileBroker::free_task(task); + } +} + + +// ------------------------------------------------------------------ +// CompileTask::initialize +void CompileTask::initialize(int compile_id, + methodHandle method, + int osr_bci, + int comp_level, + methodHandle hot_method, + int hot_count, + const char* comment, + bool is_blocking) { + assert(!_lock->is_locked(), "bad locking"); + + _compile_id = compile_id; + _method = JNIHandles::make_global(method); + _osr_bci = osr_bci; + _is_blocking = is_blocking; + _comp_level = comp_level; + _num_inlined_bytecodes = 0; + + _is_complete = false; + _is_success = false; + _code_handle = NULL; + + _hot_method = NULL; + _hot_count = hot_count; + _time_queued = 0; // tidy + _comment = comment; + + if (LogCompilation) { + _time_queued = os::elapsed_counter(); + if (hot_method.not_null()) { + if (hot_method == method) { + _hot_method = _method; + } else { + _hot_method = JNIHandles::make_global(hot_method); + } + } + } + + _next = NULL; +} + +// ------------------------------------------------------------------ +// CompileTask::code/set_code +nmethod* CompileTask::code() const { + if (_code_handle == NULL) return NULL; + return _code_handle->code(); +} +void CompileTask::set_code(nmethod* nm) { + if (_code_handle == NULL && nm == NULL) return; + guarantee(_code_handle != NULL, ""); + _code_handle->set_code(nm); + if (nm == NULL) _code_handle = NULL; // drop the handle also +} + +// ------------------------------------------------------------------ +// CompileTask::free +void CompileTask::free() { + set_code(NULL); + assert(!_lock->is_locked(), "Should not be locked when freed"); + if (_hot_method != NULL && _hot_method != _method) { + JNIHandles::destroy_global(_hot_method); + } + JNIHandles::destroy_global(_method); +} + + +// ------------------------------------------------------------------ +// CompileTask::print +void CompileTask::print() { + tty->print("print("method="); + ((methodOop)JNIHandles::resolve(_method))->print_name(tty); + tty->print_cr(" osr_bci=%d is_blocking=%s is_complete=%s is_success=%s>", + _osr_bci, bool_to_str(_is_blocking), + bool_to_str(_is_complete), bool_to_str(_is_success)); +} + +// ------------------------------------------------------------------ +// CompileTask::print_line_on_error +// +// This function is called by fatal error handler when the thread +// causing troubles is a compiler thread. +// +// Do not grab any lock, do not allocate memory. +// +// Otherwise it's the same as CompileTask::print_line() +// +void CompileTask::print_line_on_error(outputStream* st, char* buf, int buflen) { + methodOop method = (methodOop)JNIHandles::resolve(_method); + + // print compiler name + st->print("%s:", CompileBroker::compiler(comp_level())->name()); + + // print compilation number + st->print("%3d", compile_id()); + + // print method attributes + const bool is_osr = osr_bci() != CompileBroker::standard_entry_bci; + { const char blocking_char = is_blocking() ? 'b' : ' '; + const char compile_type = is_osr ? '%' : ' '; + const char sync_char = method->is_synchronized() ? 's' : ' '; + const char exception_char = method->has_exception_handler() ? '!' : ' '; + const char tier_char = + is_highest_tier_compile(comp_level()) ? ' ' : ('0' + comp_level()); + st->print("%c%c%c%c%c ", compile_type, sync_char, exception_char, blocking_char, tier_char); + } + + // Use buf to get method name and signature + if (method != NULL) st->print("%s", method->name_and_sig_as_C_string(buf, buflen)); + + // print osr_bci if any + if (is_osr) st->print(" @ %d", osr_bci()); + + // print method size + st->print_cr(" (%d bytes)", method->code_size()); +} + +// ------------------------------------------------------------------ +// CompileTask::print_line +void CompileTask::print_line() { + Thread *thread = Thread::current(); + methodHandle method(thread, + (methodOop)JNIHandles::resolve(method_handle())); + ResourceMark rm(thread); + + ttyLocker ttyl; // keep the following output all in one block + + // print compiler name if requested + if (CIPrintCompilerName) tty->print("%s:", CompileBroker::compiler(comp_level())->name()); + + // print compilation number + tty->print("%3d", compile_id()); + + // print method attributes + const bool is_osr = osr_bci() != CompileBroker::standard_entry_bci; + { const char blocking_char = is_blocking() ? 'b' : ' '; + const char compile_type = is_osr ? '%' : ' '; + const char sync_char = method->is_synchronized() ? 's' : ' '; + const char exception_char = method->has_exception_handler() ? '!' : ' '; + const char tier_char = + is_highest_tier_compile(comp_level()) ? ' ' : ('0' + comp_level()); + tty->print("%c%c%c%c%c ", compile_type, sync_char, exception_char, blocking_char, tier_char); + } + + // print method name + method->print_short_name(tty); + + // print osr_bci if any + if (is_osr) tty->print(" @ %d", osr_bci()); + + // print method size + tty->print_cr(" (%d bytes)", method->code_size()); +} + + +// ------------------------------------------------------------------ +// CompileTask::log_task +void CompileTask::log_task(xmlStream* log) { + Thread* thread = Thread::current(); + methodHandle method(thread, + (methodOop)JNIHandles::resolve(method_handle())); + ResourceMark rm(thread); + + // + if (_compile_id != 0) log->print(" compile_id='%d'", _compile_id); + if (_osr_bci != CompileBroker::standard_entry_bci) { + log->print(" compile_kind='osr'"); // same as nmethod::compile_kind + } // else compile_kind='c2c' + if (!method.is_null()) log->method(method); + if (_osr_bci != CompileBroker::standard_entry_bci) { + log->print(" osr_bci='%d'", _osr_bci); + } + if (_comp_level != CompLevel_highest_tier) { + log->print(" level='%d'", _comp_level); + } + if (_is_blocking) { + log->print(" blocking='1'"); + } + log->stamp(); +} + + +// ------------------------------------------------------------------ +// CompileTask::log_task_queued +void CompileTask::log_task_queued() { + Thread* thread = Thread::current(); + ttyLocker ttyl; + ResourceMark rm(thread); + + xtty->begin_elem("task_queued"); + log_task(xtty); + if (_comment != NULL) { + xtty->print(" comment='%s'", _comment); + } + if (_hot_method != NULL) { + methodHandle hot(thread, + (methodOop)JNIHandles::resolve(_hot_method)); + methodHandle method(thread, + (methodOop)JNIHandles::resolve(_method)); + if (hot() != method()) { + xtty->method(hot); + } + } + if (_hot_count != 0) { + xtty->print(" hot_count='%d'", _hot_count); + } + xtty->end_elem(); +} + + +// ------------------------------------------------------------------ +// CompileTask::log_task_start +void CompileTask::log_task_start(CompileLog* log) { + log->begin_head("task"); + log_task(log); + log->end_head(); +} + + +// ------------------------------------------------------------------ +// CompileTask::log_task_done +void CompileTask::log_task_done(CompileLog* log) { + Thread* thread = Thread::current(); + methodHandle method(thread, + (methodOop)JNIHandles::resolve(method_handle())); + ResourceMark rm(thread); + + // + nmethod* nm = code(); + log->begin_elem("task_done success='%d' nmsize='%d' count='%d'", + _is_success, nm == NULL ? 0 : nm->instructions_size(), + method->invocation_count()); + int bec = method->backedge_count(); + if (bec != 0) log->print(" backedge_count='%d'", bec); + // Note: "_is_complete" is about to be set, but is not. + if (_num_inlined_bytecodes != 0) { + log->print(" inlined_bytes='%d'", _num_inlined_bytecodes); + } + log->stamp(); + log->end_elem(); + log->tail("task"); + log->clear_identities(); // next task will have different CI + if (log->unflushed_count() > 2000) { + log->flush(); + } + log->mark_file_end(); +} + + + +// ------------------------------------------------------------------ +// CompileQueue::add +// +// Add a CompileTask to a CompileQueue +void CompileQueue::add(CompileTask* task) { + assert(lock()->owned_by_self(), "must own lock"); + + task->set_next(NULL); + + if (_last == NULL) { + // The compile queue is empty. + assert(_first == NULL, "queue is empty"); + _first = task; + _last = task; + } else { + // Append the task to the queue. + assert(_last->next() == NULL, "not last"); + _last->set_next(task); + _last = task; + } + + // Mark the method as being in the compile queue. + ((methodOop)JNIHandles::resolve(task->method_handle()))->set_queued_for_compilation(); + + if (CIPrintCompileQueue) { + print(); + } + + if (LogCompilation && xtty != NULL) { + task->log_task_queued(); + } + + // Notify CompilerThreads that a task is available. + lock()->notify(); +} + + +// ------------------------------------------------------------------ +// CompileQueue::get +// +// Get the next CompileTask from a CompileQueue +CompileTask* CompileQueue::get() { + MutexLocker locker(lock()); + + // Wait for an available CompileTask. + while (_first == NULL) { + // There is no work to be done right now. Wait. + lock()->wait(); + } + + CompileTask* task = _first; + + // Update queue first and last + _first =_first->next(); + if (_first == NULL) { + _last = NULL; + } + + return task; + +} + + +// ------------------------------------------------------------------ +// CompileQueue::print +void CompileQueue::print() { + tty->print_cr("Contents of %s", name()); + tty->print_cr("----------------------"); + CompileTask* task = _first; + while (task != NULL) { + task->print_line(); + task = task->next(); + } + tty->print_cr("----------------------"); +} + +CompilerCounters::CompilerCounters(const char* thread_name, int instance, TRAPS) { + + _current_method[0] = '\0'; + _compile_type = CompileBroker::no_compile; + + if (UsePerfData) { + ResourceMark rm; + + // create the thread instance name space string - don't create an + // instance subspace if instance is -1 - keeps the adapterThread + // counters from having a ".0" namespace. + const char* thread_i = (instance == -1) ? thread_name : + PerfDataManager::name_space(thread_name, instance); + + + char* name = PerfDataManager::counter_name(thread_i, "method"); + _perf_current_method = + PerfDataManager::create_string_variable(SUN_CI, name, + cmname_buffer_length, + _current_method, CHECK); + + name = PerfDataManager::counter_name(thread_i, "type"); + _perf_compile_type = PerfDataManager::create_variable(SUN_CI, name, + PerfData::U_None, + (jlong)_compile_type, + CHECK); + + name = PerfDataManager::counter_name(thread_i, "time"); + _perf_time = PerfDataManager::create_counter(SUN_CI, name, + PerfData::U_Ticks, CHECK); + + name = PerfDataManager::counter_name(thread_i, "compiles"); + _perf_compiles = PerfDataManager::create_counter(SUN_CI, name, + PerfData::U_Events, CHECK); + } +} + + +// ------------------------------------------------------------------ +// CompileBroker::compilation_init +// +// Initialize the Compilation object +void CompileBroker::compilation_init() { + _last_method_compiled[0] = '\0'; + + // Set the interface to the current compiler(s). +#ifdef COMPILER1 + _compilers[0] = new Compiler(); +#ifndef COMPILER2 + _compilers[1] = _compilers[0]; +#endif +#endif // COMPILER1 + +#ifdef COMPILER2 + _compilers[1] = new C2Compiler(); +#ifndef COMPILER1 + _compilers[0] = _compilers[1]; +#endif +#endif // COMPILER2 + + // Initialize the CompileTask free list + _task_free_list = NULL; + + // Start the CompilerThreads + init_compiler_threads(compiler_count()); + + + // totalTime performance counter is always created as it is required + // by the implementation of java.lang.management.CompilationMBean. + { + EXCEPTION_MARK; + _perf_total_compilation = + PerfDataManager::create_counter(JAVA_CI, "totalTime", + PerfData::U_Ticks, CHECK); + } + + + if (UsePerfData) { + + EXCEPTION_MARK; + + // create the jvmstat performance counters + _perf_osr_compilation = + PerfDataManager::create_counter(SUN_CI, "osrTime", + PerfData::U_Ticks, CHECK); + + _perf_standard_compilation = + PerfDataManager::create_counter(SUN_CI, "standardTime", + PerfData::U_Ticks, CHECK); + + _perf_total_bailout_count = + PerfDataManager::create_counter(SUN_CI, "totalBailouts", + PerfData::U_Events, CHECK); + + _perf_total_invalidated_count = + PerfDataManager::create_counter(SUN_CI, "totalInvalidates", + PerfData::U_Events, CHECK); + + _perf_total_compile_count = + PerfDataManager::create_counter(SUN_CI, "totalCompiles", + PerfData::U_Events, CHECK); + _perf_total_osr_compile_count = + PerfDataManager::create_counter(SUN_CI, "osrCompiles", + PerfData::U_Events, CHECK); + + _perf_total_standard_compile_count = + PerfDataManager::create_counter(SUN_CI, "standardCompiles", + PerfData::U_Events, CHECK); + + _perf_sum_osr_bytes_compiled = + PerfDataManager::create_counter(SUN_CI, "osrBytes", + PerfData::U_Bytes, CHECK); + + _perf_sum_standard_bytes_compiled = + PerfDataManager::create_counter(SUN_CI, "standardBytes", + PerfData::U_Bytes, CHECK); + + _perf_sum_nmethod_size = + PerfDataManager::create_counter(SUN_CI, "nmethodSize", + PerfData::U_Bytes, CHECK); + + _perf_sum_nmethod_code_size = + PerfDataManager::create_counter(SUN_CI, "nmethodCodeSize", + PerfData::U_Bytes, CHECK); + + _perf_last_method = + PerfDataManager::create_string_variable(SUN_CI, "lastMethod", + CompilerCounters::cmname_buffer_length, + "", CHECK); + + _perf_last_failed_method = + PerfDataManager::create_string_variable(SUN_CI, "lastFailedMethod", + CompilerCounters::cmname_buffer_length, + "", CHECK); + + _perf_last_invalidated_method = + PerfDataManager::create_string_variable(SUN_CI, "lastInvalidatedMethod", + CompilerCounters::cmname_buffer_length, + "", CHECK); + + _perf_last_compile_type = + PerfDataManager::create_variable(SUN_CI, "lastType", + PerfData::U_None, + (jlong)CompileBroker::no_compile, + CHECK); + + _perf_last_compile_size = + PerfDataManager::create_variable(SUN_CI, "lastSize", + PerfData::U_Bytes, + (jlong)CompileBroker::no_compile, + CHECK); + + + _perf_last_failed_type = + PerfDataManager::create_variable(SUN_CI, "lastFailedType", + PerfData::U_None, + (jlong)CompileBroker::no_compile, + CHECK); + + _perf_last_invalidated_type = + PerfDataManager::create_variable(SUN_CI, "lastInvalidatedType", + PerfData::U_None, + (jlong)CompileBroker::no_compile, + CHECK); + } + + _initialized = true; +} + + + +// ------------------------------------------------------------------ +// CompileBroker::make_compiler_thread +CompilerThread* CompileBroker::make_compiler_thread(const char* name, CompileQueue* queue, CompilerCounters* counters, TRAPS) { + CompilerThread* compiler_thread = NULL; + + klassOop k = + SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_Thread(), + true, CHECK_0); + instanceKlassHandle klass (THREAD, k); + instanceHandle thread_oop = klass->allocate_instance_handle(CHECK_0); + Handle string = java_lang_String::create_from_str(name, CHECK_0); + + // Initialize thread_oop to put it into the system threadGroup + Handle thread_group (THREAD, Universe::system_thread_group()); + JavaValue result(T_VOID); + JavaCalls::call_special(&result, thread_oop, + klass, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::threadgroup_string_void_signature(), + thread_group, + string, + CHECK_0); + + { + MutexLocker mu(Threads_lock, THREAD); + compiler_thread = new CompilerThread(queue, counters); + // At this point the new CompilerThread data-races with this startup + // thread (which I believe is the primoridal thread and NOT the VM + // thread). This means Java bytecodes being executed at startup can + // queue compile jobs which will run at whatever default priority the + // newly created CompilerThread runs at. + + + // At this point it may be possible that no osthread was created for the + // JavaThread due to lack of memory. We would have to throw an exception + // in that case. However, since this must work and we do not allow + // exceptions anyway, check and abort if this fails. + + if (compiler_thread == NULL || compiler_thread->osthread() == NULL){ + vm_exit_during_initialization("java.lang.OutOfMemoryError", + "unable to create new native thread"); + } + + java_lang_Thread::set_thread(thread_oop(), compiler_thread); + + // Note that this only sets the JavaThread _priority field, which by + // definition is limited to Java priorities and not OS priorities. + // The os-priority is set in the CompilerThread startup code itself + java_lang_Thread::set_priority(thread_oop(), NearMaxPriority); + // CLEANUP PRIORITIES: This -if- statement hids a bug whereby the compiler + // threads never have their OS priority set. The assumption here is to + // enable the Performance group to do flag tuning, figure out a suitable + // CompilerThreadPriority, and then remove this 'if' statement (and + // comment) and unconditionally set the priority. + + // Compiler Threads should be at the highest Priority + if ( CompilerThreadPriority != -1 ) + os::set_native_priority( compiler_thread, CompilerThreadPriority ); + else + os::set_native_priority( compiler_thread, os::java_to_os_priority[NearMaxPriority]); + + // Note that I cannot call os::set_priority because it expects Java + // priorities and I am *explicitly* using OS priorities so that it's + // possible to set the compiler thread priority higher than any Java + // thread. + + java_lang_Thread::set_daemon(thread_oop()); + + compiler_thread->set_threadObj(thread_oop()); + Threads::add(compiler_thread); + Thread::start(compiler_thread); + } + // Let go of Threads_lock before yielding + os::yield(); // make sure that the compiler thread is started early (especially helpful on SOLARIS) + + return compiler_thread; +} + + +// ------------------------------------------------------------------ +// CompileBroker::init_compiler_threads +// +// Initialize the compilation queue +void CompileBroker::init_compiler_threads(int compiler_count) { + EXCEPTION_MARK; + + _method_queue = new CompileQueue("MethodQueue", MethodCompileQueue_lock); + _method_threads = + new (ResourceObj::C_HEAP) GrowableArray(compiler_count, true); + + char name_buffer[256]; + int i; + for (i = 0; i < compiler_count; i++) { + // Create a name for our thread. + sprintf(name_buffer, "CompilerThread%d", i); + CompilerCounters* counters = new CompilerCounters("compilerThread", i, CHECK); + + CompilerThread* new_thread = make_compiler_thread(name_buffer, _method_queue, counters, CHECK); + _method_threads->append(new_thread); + } + if (UsePerfData) { + PerfDataManager::create_constant(SUN_CI, "threads", PerfData::U_Bytes, + compiler_count, CHECK); + } +} + +// ------------------------------------------------------------------ +// CompileBroker::is_idle +bool CompileBroker::is_idle() { + if (!_method_queue->is_empty()) { + return false; + } else { + int num_threads = _method_threads->length(); + for (int i=0; iat(i)->task() != NULL) { + return false; + } + } + + // No pending or active compilations. + return true; + } +} + + +// ------------------------------------------------------------------ +// CompileBroker::compile_method +// +// Request compilation of a method. +void CompileBroker::compile_method_base(methodHandle method, + int osr_bci, + int comp_level, + methodHandle hot_method, + int hot_count, + const char* comment, + TRAPS) { + // do nothing if compiler thread(s) is not available + if (!_initialized ) { + return; + } + + guarantee(!method->is_abstract(), "cannot compile abstract methods"); + assert(method->method_holder()->klass_part()->oop_is_instance(), + "sanity check"); + assert(!instanceKlass::cast(method->method_holder())->is_not_initialized(), + "method holder must be initialized"); + + if (CIPrintRequests) { + tty->print("request: "); + method->print_short_name(tty); + if (osr_bci != InvocationEntryBci) { + tty->print(" osr_bci: %d", osr_bci); + } + tty->print(" comment: %s count: %d", comment, hot_count); + if (!hot_method.is_null()) { + tty->print(" hot: "); + if (hot_method() != method()) { + hot_method->print_short_name(tty); + } else { + tty->print("yes"); + } + } + tty->cr(); + } + + // A request has been made for compilation. Before we do any + // real work, check to see if the method has been compiled + // in the meantime with a definitive result. + if (compilation_is_complete(method, osr_bci, comp_level)) { + return; + } + + // If this method is already in the compile queue, then + // we do not block the current thread. + if (compilation_is_in_queue(method, osr_bci)) { + // We may want to decay our counter a bit here to prevent + // multiple denied requests for compilation. This is an + // open compilation policy issue. Note: The other possibility, + // in the case that this is a blocking compile request, is to have + // all subsequent blocking requesters wait for completion of + // ongoing compiles. Note that in this case we'll need a protocol + // for freeing the associated compile tasks. [Or we could have + // a single static monitor on which all these waiters sleep.] + return; + } + + // Outputs from the following MutexLocker block: + CompileTask* task = NULL; + bool blocking = false; + + // Acquire our lock. + { + MutexLocker locker(_method_queue->lock(), THREAD); + + // Make sure the method has not slipped into the queues since + // last we checked; note that those checks were "fast bail-outs". + // Here we need to be more careful, see 14012000 below. + if (compilation_is_in_queue(method, osr_bci)) { + return; + } + + // We need to check again to see if the compilation has + // completed. A previous compilation may have registered + // some result. + if (compilation_is_complete(method, osr_bci, comp_level)) { + return; + } + + // We now know that this compilation is not pending, complete, + // or prohibited. Assign a compile_id to this compilation + // and check to see if it is in our [Start..Stop) range. + uint compile_id = assign_compile_id(method, osr_bci); + if (compile_id == 0) { + // The compilation falls outside the allowed range. + return; + } + + // Should this thread wait for completion of the compile? + blocking = is_compile_blocking(method, osr_bci); + + // We will enter the compilation in the queue. + // 14012000: Note that this sets the queued_for_compile bits in + // the target method. We can now reason that a method cannot be + // queued for compilation more than once, as follows: + // Before a thread queues a task for compilation, it first acquires + // the compile queue lock, then checks if the method's queued bits + // are set or it has already been compiled. Thus there can not be two + // instances of a compilation task for the same method on the + // compilation queue. Consider now the case where the compilation + // thread has already removed a task for that method from the queue + // and is in the midst of compiling it. In this case, the + // queued_for_compile bits must be set in the method (and these + // will be visible to the current thread, since the bits were set + // under protection of the compile queue lock, which we hold now. + // When the compilation completes, the compiler thread first sets + // the compilation result and then clears the queued_for_compile + // bits. Neither of these actions are protected by a barrier (or done + // under the protection of a lock), so the only guarantee we have + // (on machines with TSO (Total Store Order)) is that these values + // will update in that order. As a result, the only combinations of + // these bits that the current thread will see are, in temporal order: + // : + // <0, 1> : in compile queue, but not yet compiled + // <1, 1> : compiled but queue bit not cleared + // <1, 0> : compiled and queue bit cleared + // Because we first check the queue bits then check the result bits, + // we are assured that we cannot introduce a duplicate task. + // Note that if we did the tests in the reverse order (i.e. check + // result then check queued bit), we could get the result bit before + // the compilation completed, and the queue bit after the compilation + // completed, and end up introducing a "duplicate" (redundant) task. + // In that case, the compiler thread should first check if a method + // has already been compiled before trying to compile it. + // NOTE: in the event that there are multiple compiler threads and + // there is de-optimization/recompilation, things will get hairy, + // and in that case it's best to protect both the testing (here) of + // these bits, and their updating (here and elsewhere) under a + // common lock. + task = create_compile_task(_method_queue, + compile_id, method, + osr_bci, comp_level, + hot_method, hot_count, comment, + blocking); + } + + if (blocking) { + wait_for_completion(task); + } +} + + +nmethod* CompileBroker::compile_method(methodHandle method, int osr_bci, + methodHandle hot_method, int hot_count, + const char* comment, TRAPS) { + // make sure arguments make sense + assert(method->method_holder()->klass_part()->oop_is_instance(), "not an instance method"); + assert(osr_bci == InvocationEntryBci || (0 <= osr_bci && osr_bci < method->code_size()), "bci out of range"); + assert(!method->is_abstract() && (osr_bci == InvocationEntryBci || !method->is_native()), "cannot compile abstract/native methods"); + assert(!instanceKlass::cast(method->method_holder())->is_not_initialized(), "method holder must be initialized"); + + int comp_level = CompilationPolicy::policy()->compilation_level(method, osr_bci); + +#ifdef TIERED + if (TieredCompilation && StressTieredRuntime) { + static int flipper = 0; + if (is_even(flipper++)) { + comp_level = CompLevel_fast_compile; + } else { + comp_level = CompLevel_full_optimization; + } + } +#ifdef SPARC + // QQQ FIX ME + // C2 only returns long results in G1 and c1 doesn't understand so disallow c2 + // compiles of long results + if (TieredCompilation && method()->result_type() == T_LONG) { + comp_level = CompLevel_fast_compile; + } +#endif // SPARC +#endif // TIERED + + // return quickly if possible + + // lock, make sure that the compilation + // isn't prohibited in a straightforward way. + + if (compiler(comp_level) == NULL || compilation_is_prohibited(method, osr_bci, comp_level)) { + return NULL; + } + + if (osr_bci == InvocationEntryBci) { + // standard compilation + nmethod* method_code = method->code(); + if (method_code != NULL +#ifdef TIERED + && ( method_code->is_compiled_by_c2() || comp_level == CompLevel_fast_compile ) +#endif // TIERED + ) { + return method_code; + } + if (method->is_not_compilable(comp_level)) return NULL; + } else { + // osr compilation +#ifndef TIERED + // seems like an assert of dubious value + assert(comp_level == CompLevel_full_optimization, + "all OSR compiles are assumed to be at a single compilation lavel"); +#endif // TIERED + nmethod* nm = method->lookup_osr_nmethod_for(osr_bci); + if (nm != NULL) return nm; + if (method->is_not_osr_compilable()) return NULL; + } + + assert(!HAS_PENDING_EXCEPTION, "No exception should be present"); + // some prerequisites that are compiler specific + if (compiler(comp_level)->is_c2()) { + method->constants()->resolve_string_constants(CHECK_0); + // Resolve all classes seen in the signature of the method + // we are compiling. + methodOopDesc::load_signature_classes(method, CHECK_0); + } + + // If the method is native, do the lookup in the thread requesting + // the compilation. Native lookups can load code, which is not + // permitted during compilation. + // + // Note: A native method implies non-osr compilation which is + // checked with an assertion at the entry of this method. + if (method->is_native()) { + bool in_base_library; + address adr = NativeLookup::lookup(method, in_base_library, THREAD); + if (HAS_PENDING_EXCEPTION) { + // In case of an exception looking up the method, we just forget + // about it. The interpreter will kick-in and throw the exception. + method->set_not_compilable(); // implies is_not_osr_compilable() + CLEAR_PENDING_EXCEPTION; + return NULL; + } + assert(method->has_native_function(), "must have native code by now"); + } + + // RedefineClasses() has replaced this method; just return + if (method->is_old()) { + return NULL; + } + + // JVMTI -- post_compile_event requires jmethod_id() that may require + // a lock the compiling thread can not acquire. Prefetch it here. + if (JvmtiExport::should_post_compiled_method_load()) { + method->jmethod_id(); + } + + // do the compilation + if (method->is_native()) { + if (!PreferInterpreterNativeStubs) { + (void) AdapterHandlerLibrary::create_native_wrapper(method); + } else { + return NULL; + } + } else { + compile_method_base(method, osr_bci, comp_level, hot_method, hot_count, comment, CHECK_0); + } + + // return requested nmethod + return osr_bci == InvocationEntryBci ? method->code() : method->lookup_osr_nmethod_for(osr_bci); +} + + +// ------------------------------------------------------------------ +// CompileBroker::compilation_is_complete +// +// See if compilation of this method is already complete. +bool CompileBroker::compilation_is_complete(methodHandle method, + int osr_bci, + int comp_level) { + bool is_osr = (osr_bci != standard_entry_bci); + if (is_osr) { + if (method->is_not_osr_compilable()) { + return true; + } else { + nmethod* result = method->lookup_osr_nmethod_for(osr_bci); + return (result != NULL); + } + } else { + if (method->is_not_compilable(comp_level)) { + return true; + } else { + nmethod* result = method->code(); + if (result == NULL) return false; +#ifdef TIERED + if (comp_level == CompLevel_fast_compile) { + // At worst the code is from c1 + return true; + } + // comp level must be full opt + return result->is_compiled_by_c2(); +#endif // TIERED + return true; + } + } +} + + +// ------------------------------------------------------------------ +// CompileBroker::compilation_is_in_queue +// +// See if this compilation is already requested. +// +// Implementation note: there is only a single "is in queue" bit +// for each method. This means that the check below is overly +// conservative in the sense that an osr compilation in the queue +// will block a normal compilation from entering the queue (and vice +// versa). This can be remedied by a full queue search to disambiguate +// cases. If it is deemed profitible, this may be done. +bool CompileBroker::compilation_is_in_queue(methodHandle method, + int osr_bci) { + return method->queued_for_compilation(); +} + + +// ------------------------------------------------------------------ +// CompileBroker::compilation_is_prohibited +// +// See if this compilation is not allowed. +bool CompileBroker::compilation_is_prohibited(methodHandle method, int osr_bci, int comp_level) { + bool is_native = method->is_native(); + // Some compilers may not support the compilation of natives. + // QQQ this needs some work ought to only record not compilable at + // the specified level + if (is_native && + (!CICompileNatives || !compiler(comp_level)->supports_native())) { + method->set_not_compilable(); + return true; + } + + bool is_osr = (osr_bci != standard_entry_bci); + // Some compilers may not support on stack replacement. + if (is_osr && + (!CICompileOSR || !compiler(comp_level)->supports_osr())) { + method->set_not_osr_compilable(); + return true; + } + + // The method may be explicitly excluded by the user. + bool quietly; + if (CompilerOracle::should_exclude(method, quietly)) { + if (!quietly) { + // This does not happen quietly... + ResourceMark rm; + tty->print("### Excluding %s:%s", + method->is_native() ? "generation of native wrapper" : "compile", + (method->is_static() ? " static" : "")); + method->print_short_name(tty); + tty->cr(); + } + method->set_not_compilable(); + } + + return false; +} + + +// ------------------------------------------------------------------ +// CompileBroker::assign_compile_id +// +// Assign a serialized id number to this compilation request. If the +// number falls out of the allowed range, return a 0. OSR +// compilations may be numbered separately from regular compilations +// if certain debugging flags are used. +uint CompileBroker::assign_compile_id(methodHandle method, int osr_bci) { + assert(_method_queue->lock()->owner() == JavaThread::current(), + "must hold the compilation queue lock"); + bool is_osr = (osr_bci != standard_entry_bci); + assert(!method->is_native(), "no longer compile natives"); + uint id; + if (CICountOSR && is_osr) { + id = ++_osr_compilation_id; + if ((uint)CIStartOSR <= id && id < (uint)CIStopOSR) { + return id; + } + } else { + id = ++_compilation_id; + if ((uint)CIStart <= id && id < (uint)CIStop) { + return id; + } + } + + // Method was not in the appropriate compilation range. + method->set_not_compilable(); + return 0; +} + + +// ------------------------------------------------------------------ +// CompileBroker::is_compile_blocking +// +// Should the current thread be blocked until this compilation request +// has been fulfilled? +bool CompileBroker::is_compile_blocking(methodHandle method, int osr_bci) { + return !BackgroundCompilation; +} + + +// ------------------------------------------------------------------ +// CompileBroker::preload_classes +void CompileBroker::preload_classes(methodHandle method, TRAPS) { + // Move this code over from c1_Compiler.cpp + ShouldNotReachHere(); +} + + +// ------------------------------------------------------------------ +// CompileBroker::create_compile_task +// +// Create a CompileTask object representing the current request for +// compilation. Add this task to the queue. +CompileTask* CompileBroker::create_compile_task(CompileQueue* queue, + int compile_id, + methodHandle method, + int osr_bci, + int comp_level, + methodHandle hot_method, + int hot_count, + const char* comment, + bool blocking) { + CompileTask* new_task = allocate_task(); + new_task->initialize(compile_id, method, osr_bci, comp_level, + hot_method, hot_count, comment, + blocking); + queue->add(new_task); + return new_task; +} + + +// ------------------------------------------------------------------ +// CompileBroker::allocate_task +// +// Allocate a CompileTask, from the free list if possible. +CompileTask* CompileBroker::allocate_task() { + MutexLocker locker(CompileTaskAlloc_lock); + CompileTask* task = NULL; + if (_task_free_list != NULL) { + task = _task_free_list; + _task_free_list = task->next(); + task->set_next(NULL); + } else { + task = new CompileTask(); + task->set_next(NULL); + } + return task; +} + + +// ------------------------------------------------------------------ +// CompileBroker::free_task +// +// Add a task to the free list. +void CompileBroker::free_task(CompileTask* task) { + MutexLocker locker(CompileTaskAlloc_lock); + task->free(); + task->set_next(_task_free_list); + _task_free_list = task; +} + + +// ------------------------------------------------------------------ +// CompileBroker::wait_for_completion +// +// Wait for the given method CompileTask to complete. +void CompileBroker::wait_for_completion(CompileTask* task) { + if (CIPrintCompileQueue) { + tty->print_cr("BLOCKING FOR COMPILE"); + } + + assert(task->is_blocking(), "can only wait on blocking task"); + + JavaThread *thread = JavaThread::current(); + thread->set_blocked_on_compilation(true); + + methodHandle method(thread, + (methodOop)JNIHandles::resolve(task->method_handle())); + { + MutexLocker waiter(task->lock(), thread); + + while (!task->is_complete()) + task->lock()->wait(); + } + // It is harmless to check this status without the lock, because + // completion is a stable property (until the task object is recycled). + assert(task->is_complete(), "Compilation should have completed"); + assert(task->code_handle() == NULL, "must be reset"); + + thread->set_blocked_on_compilation(false); + + // By convention, the waiter is responsible for recycling a + // blocking CompileTask. Since there is only one waiter ever + // waiting on a CompileTask, we know that no one else will + // be using this CompileTask; we can free it. + free_task(task); +} + +// ------------------------------------------------------------------ +// CompileBroker::compiler_thread_loop +// +// The main loop run by a CompilerThread. +void CompileBroker::compiler_thread_loop() { + CompilerThread* thread = CompilerThread::current(); + CompileQueue* queue = thread->queue(); + + // For the thread that initializes the ciObjectFactory + // this resource mark holds all the shared objects + ResourceMark rm; + + // First thread to get here will initialize the compiler interface + + if (!ciObjectFactory::is_initialized()) { + ASSERT_IN_VM; + MutexLocker only_one (CompileThread_lock, thread); + if (!ciObjectFactory::is_initialized()) { + ciObjectFactory::initialize(); + } + } + + // Open a log. + if (LogCompilation) { + init_compiler_thread_log(); + } + CompileLog* log = thread->log(); + if (log != NULL) { + log->begin_elem("start_compile_thread thread='" UINTX_FORMAT "' process='%d'", + os::current_thread_id(), + os::current_process_id()); + log->stamp(); + log->end_elem(); + } + + while (true) { + { + // We need this HandleMark to avoid leaking VM handles. + HandleMark hm(thread); + if (CodeCache::unallocated_capacity() < CodeCacheMinimumFreeSpace) { + // The CodeCache is full. Print out warning and disable compilation. + UseInterpreter = true; + if (UseCompiler || AlwaysCompileLoopMethods ) { + if (log != NULL) { + log->begin_elem("code_cache_full"); + log->stamp(); + log->end_elem(); + } +#ifndef PRODUCT + warning("CodeCache is full. Compiler has been disabled"); + if (CompileTheWorld || ExitOnFullCodeCache) { + before_exit(thread); + exit_globals(); // will delete tty + vm_direct_exit(CompileTheWorld ? 0 : 1); + } +#endif + UseCompiler = false; + AlwaysCompileLoopMethods = false; + } + } + + CompileTask* task = queue->get(); + + // Give compiler threads an extra quanta. They tend to be bursty and + // this helps the compiler to finish up the job. + if( CompilerThreadHintNoPreempt ) + os::hint_no_preempt(); + + // trace per thread time and compile statistics + CompilerCounters* counters = ((CompilerThread*)thread)->counters(); + PerfTraceTimedEvent(counters->time_counter(), counters->compile_counter()); + + // Assign the task to the current thread. Mark this compilation + // thread as active for the profiler. + CompileTaskWrapper ctw(task); + nmethodLocker result_handle; // (handle for the nmethod produced by this task) + task->set_code_handle(&result_handle); + methodHandle method(thread, + (methodOop)JNIHandles::resolve(task->method_handle())); + + // Never compile a method if breakpoints are present in it + if (method()->number_of_breakpoints() == 0) { + // Compile the method. + if (UseCompiler || AlwaysCompileLoopMethods) { +#ifdef COMPILER1 + // Allow repeating compilations for the purpose of benchmarking + // compile speed. This is not useful for customers. + if (CompilationRepeat != 0) { + int compile_count = CompilationRepeat; + while (compile_count > 0) { + invoke_compiler_on_method(task); + nmethod* nm = method->code(); + if (nm != NULL) { + nm->make_zombie(); + method->clear_code(); + } + compile_count--; + } + } +#endif /* COMPILER1 */ + invoke_compiler_on_method(task); + } else { + // After compilation is disabled, remove remaining methods from queue + method->clear_queued_for_compilation(); + } + } + } + } +} + + +// ------------------------------------------------------------------ +// CompileBroker::init_compiler_thread_log +// +// Set up state required by +LogCompilation. +void CompileBroker::init_compiler_thread_log() { + CompilerThread* thread = CompilerThread::current(); + char fileBuf[4*K]; + FILE* fp = NULL; + char* file = NULL; + intx thread_id = os::current_thread_id(); + for (int try_temp_dir = 1; try_temp_dir >= 0; try_temp_dir--) { + const char* dir = (try_temp_dir ? os::get_temp_directory() : NULL); + if (dir == NULL) dir = ""; + sprintf(fileBuf, "%shs_c" UINTX_FORMAT "_pid%u.log", + dir, thread_id, os::current_process_id()); + fp = fopen(fileBuf, "at"); + if (fp != NULL) { + file = NEW_C_HEAP_ARRAY(char, strlen(fileBuf)+1); + strcpy(file, fileBuf); + break; + } + } + if (fp == NULL) { + warning("Cannot open log file: %s", fileBuf); + } else { + if (LogCompilation && Verbose) + tty->print_cr("Opening compilation log %s", file); + CompileLog* log = new(ResourceObj::C_HEAP) CompileLog(file, fp, thread_id); + thread->init_log(log); + + if (xtty != NULL) { + ttyLocker ttyl; + + // Record any per thread log files + xtty->elem("thread_logfile thread='%d' filename='%s'", thread_id, file); + } + } +} + +// ------------------------------------------------------------------ +// CompileBroker::set_should_block +// +// Set _should_block. +// Call this from the VM, with Threads_lock held and a safepoint requested. +void CompileBroker::set_should_block() { + assert(Threads_lock->owner() == Thread::current(), "must have threads lock"); + assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint already"); +#ifndef PRODUCT + if (PrintCompilation && (Verbose || WizardMode)) + tty->print_cr("notifying compiler thread pool to block"); +#endif + _should_block = true; +} + +// ------------------------------------------------------------------ +// CompileBroker::maybe_block +// +// Call this from the compiler at convenient points, to poll for _should_block. +void CompileBroker::maybe_block() { + if (_should_block) { +#ifndef PRODUCT + if (PrintCompilation && (Verbose || WizardMode)) + tty->print_cr("compiler thread " INTPTR_FORMAT " poll detects block request", Thread::current()); +#endif + ThreadInVMfromNative tivfn(JavaThread::current()); + } +} + + +// ------------------------------------------------------------------ +// CompileBroker::invoke_compiler_on_method +// +// Compile a method. +// +void CompileBroker::invoke_compiler_on_method(CompileTask* task) { + if (PrintCompilation) { + ResourceMark rm; + task->print_line(); + } + elapsedTimer time; + + CompilerThread* thread = CompilerThread::current(); + ResourceMark rm(thread); + + // Common flags. + uint compile_id = task->compile_id(); + int osr_bci = task->osr_bci(); + bool is_osr = (osr_bci != standard_entry_bci); + bool should_log = (thread->log() != NULL); + bool should_break = false; + { + // create the handle inside it's own block so it can't + // accidentally be referenced once the thread transitions to + // native. The NoHandleMark before the transition should catch + // any cases where this occurs in the future. + methodHandle method(thread, + (methodOop)JNIHandles::resolve(task->method_handle())); + should_break = check_break_at(method, compile_id, is_osr); + if (should_log && !CompilerOracle::should_log(method)) { + should_log = false; + } + assert(!method->is_native(), "no longer compile natives"); + + // Save information about this method in case of failure. + set_last_compile(thread, method, is_osr, task->comp_level()); + + DTRACE_METHOD_COMPILE_BEGIN_PROBE(compiler(task->comp_level()), method); + } + + // Allocate a new set of JNI handles. + push_jni_handle_block(); + jobject target_handle = JNIHandles::make_local(thread, JNIHandles::resolve(task->method_handle())); + int compilable = ciEnv::MethodCompilable; + { + int system_dictionary_modification_counter; + { + MutexLocker locker(Compile_lock, thread); + system_dictionary_modification_counter = SystemDictionary::number_of_modifications(); + } + + NoHandleMark nhm; + ThreadToNativeFromVM ttn(thread); + + ciEnv ci_env(task, system_dictionary_modification_counter); + if (should_break) { + ci_env.set_break_at_compile(true); + } + if (should_log) { + ci_env.set_log(thread->log()); + } + assert(thread->env() == &ci_env, "set by ci_env"); + // The thread-env() field is cleared in ~CompileTaskWrapper. + + ciMethod* target = ci_env.get_method_from_handle(target_handle); + + TraceTime t1("compilation", &time); + + compiler(task->comp_level())->compile_method(&ci_env, target, osr_bci); + + if (!ci_env.failing() && task->code() == NULL) { + //assert(false, "compiler should always document failure"); + // The compiler elected, without comment, not to register a result. + // Do not attempt further compilations of this method. + ci_env.record_method_not_compilable("compile failed"); + } + + if (ci_env.failing()) { + // Copy this bit to the enclosing block: + compilable = ci_env.compilable(); + if (PrintCompilation) { + const char* reason = ci_env.failure_reason(); + if (compilable == ciEnv::MethodCompilable_not_at_tier) { + if (is_highest_tier_compile(ci_env.comp_level())) { + // Already at highest tier, promote to not compilable. + compilable = ciEnv::MethodCompilable_never; + } else { + tty->print_cr("%3d COMPILE SKIPPED: %s (retry at different tier)", compile_id, reason); + } + } + + if (compilable == ciEnv::MethodCompilable_never) { + tty->print_cr("%3d COMPILE SKIPPED: %s (not retryable)", compile_id, reason); + } else if (compilable == ciEnv::MethodCompilable) { + tty->print_cr("%3d COMPILE SKIPPED: %s", compile_id, reason); + } + } + } else { + task->mark_success(); + task->set_num_inlined_bytecodes(ci_env.num_inlined_bytecodes()); + } + } + pop_jni_handle_block(); + + methodHandle method(thread, + (methodOop)JNIHandles::resolve(task->method_handle())); + + DTRACE_METHOD_COMPILE_END_PROBE(compiler(task->comp_level()), method, task->is_success()); + + collect_statistics(thread, time, task); + + if (compilable == ciEnv::MethodCompilable_never) { + if (is_osr) { + method->set_not_osr_compilable(); + } else { + method->set_not_compilable(); + } + } else if (compilable == ciEnv::MethodCompilable_not_at_tier) { + method->set_not_compilable(task->comp_level()); + } + + // Note that the queued_for_compilation bits are cleared without + // protection of a mutex. [They were set by the requester thread, + // when adding the task to the complie queue -- at which time the + // compile queue lock was held. Subsequently, we acquired the compile + // queue lock to get this task off the compile queue; thus (to belabour + // the point somewhat) our clearing of the bits must be occurring + // only after the setting of the bits. See also 14012000 above. + method->clear_queued_for_compilation(); + +#ifdef ASSERT + if (CollectedHeap::fired_fake_oom()) { + // The current compile received a fake OOM during compilation so + // go ahead and exit the VM since the test apparently succeeded + tty->print_cr("*** Shutting down VM after successful fake OOM"); + vm_exit(0); + } +#endif +} + + +// ------------------------------------------------------------------ +// CompileBroker::set_last_compile +// +// Record this compilation for debugging purposes. +void CompileBroker::set_last_compile(CompilerThread* thread, methodHandle method, bool is_osr, int comp_level) { + ResourceMark rm; + char* method_name = method->name()->as_C_string(); + strncpy(_last_method_compiled, method_name, CompileBroker::name_buffer_length); + char current_method[CompilerCounters::cmname_buffer_length]; + size_t maxLen = CompilerCounters::cmname_buffer_length; + + if (UsePerfData) { + const char* class_name = method->method_holder()->klass_part()->name()->as_C_string(); + + size_t s1len = strlen(class_name); + size_t s2len = strlen(method_name); + + // check if we need to truncate the string + if (s1len + s2len + 2 > maxLen) { + + // the strategy is to lop off the leading characters of the + // class name and the trailing characters of the method name. + + if (s2len + 2 > maxLen) { + // lop of the entire class name string, let snprintf handle + // truncation of the method name. + class_name += s1len; // null string + } + else { + // lop off the extra characters from the front of the class name + class_name += ((s1len + s2len + 2) - maxLen); + } + } + + jio_snprintf(current_method, maxLen, "%s %s", class_name, method_name); + } + + if (CICountOSR && is_osr) { + _last_compile_type = osr_compile; + } else { + _last_compile_type = normal_compile; + } + _last_compile_level = comp_level; + + if (UsePerfData) { + CompilerCounters* counters = thread->counters(); + counters->set_current_method(current_method); + counters->set_compile_type((jlong)_last_compile_type); + } +} + + +// ------------------------------------------------------------------ +// CompileBroker::push_jni_handle_block +// +// Push on a new block of JNI handles. +void CompileBroker::push_jni_handle_block() { + JavaThread* thread = JavaThread::current(); + + // Allocate a new block for JNI handles. + // Inlined code from jni_PushLocalFrame() + JNIHandleBlock* java_handles = thread->active_handles(); + JNIHandleBlock* compile_handles = JNIHandleBlock::allocate_block(thread); + assert(compile_handles != NULL && java_handles != NULL, "should not be NULL"); + compile_handles->set_pop_frame_link(java_handles); // make sure java handles get gc'd. + thread->set_active_handles(compile_handles); +} + + +// ------------------------------------------------------------------ +// CompileBroker::pop_jni_handle_block +// +// Pop off the current block of JNI handles. +void CompileBroker::pop_jni_handle_block() { + JavaThread* thread = JavaThread::current(); + + // Release our JNI handle block + JNIHandleBlock* compile_handles = thread->active_handles(); + JNIHandleBlock* java_handles = compile_handles->pop_frame_link(); + thread->set_active_handles(java_handles); + compile_handles->set_pop_frame_link(NULL); + JNIHandleBlock::release_block(compile_handles, thread); // may block +} + + +// ------------------------------------------------------------------ +// CompileBroker::check_break_at +// +// Should the compilation break at the current compilation. +bool CompileBroker::check_break_at(methodHandle method, int compile_id, bool is_osr) { + if (CICountOSR && is_osr && (compile_id == CIBreakAtOSR)) { + return true; + } else if( CompilerOracle::should_break_at(method) ) { // break when compiling + return true; + } else { + return (compile_id == CIBreakAt); + } +} + +// ------------------------------------------------------------------ +// CompileBroker::collect_statistics +// +// Collect statistics about the compilation. + +void CompileBroker::collect_statistics(CompilerThread* thread, elapsedTimer time, CompileTask* task) { + bool success = task->is_success(); + methodHandle method (thread, (methodOop)JNIHandles::resolve(task->method_handle())); + uint compile_id = task->compile_id(); + bool is_osr = (task->osr_bci() != standard_entry_bci); + nmethod* code = task->code(); + CompilerCounters* counters = thread->counters(); + + assert(code == NULL || code->is_locked_by_vm(), "will survive the MutexLocker"); + MutexLocker locker(CompileStatistics_lock); + + // _perf variables are production performance counters which are + // updated regardless of the setting of the CITime and CITimeEach flags + // + if (!success) { + _total_bailout_count++; + if (UsePerfData) { + _perf_last_failed_method->set_value(counters->current_method()); + _perf_last_failed_type->set_value(counters->compile_type()); + _perf_total_bailout_count->inc(); + } + } else if (code == NULL) { + if (UsePerfData) { + _perf_last_invalidated_method->set_value(counters->current_method()); + _perf_last_invalidated_type->set_value(counters->compile_type()); + _perf_total_invalidated_count->inc(); + } + _total_invalidated_count++; + } else { + // Compilation succeeded + + // update compilation ticks - used by the implementation of + // java.lang.management.CompilationMBean + _perf_total_compilation->inc(time.ticks()); + + if (CITime) { + _t_total_compilation.add(time); + if (is_osr) { + _t_osr_compilation.add(time); + _sum_osr_bytes_compiled += method->code_size() + task->num_inlined_bytecodes(); + } else { + _t_standard_compilation.add(time); + _sum_standard_bytes_compiled += method->code_size() + task->num_inlined_bytecodes(); + } + } + + if (UsePerfData) { + // save the name of the last method compiled + _perf_last_method->set_value(counters->current_method()); + _perf_last_compile_type->set_value(counters->compile_type()); + _perf_last_compile_size->set_value(method->code_size() + + task->num_inlined_bytecodes()); + if (is_osr) { + _perf_osr_compilation->inc(time.ticks()); + _perf_sum_osr_bytes_compiled->inc(method->code_size() + task->num_inlined_bytecodes()); + } else { + _perf_standard_compilation->inc(time.ticks()); + _perf_sum_standard_bytes_compiled->inc(method->code_size() + task->num_inlined_bytecodes()); + } + } + + if (CITimeEach) { + float bytes_per_sec = 1.0 * (method->code_size() + task->num_inlined_bytecodes()) / time.seconds(); + tty->print_cr("%3d seconds: %f bytes/sec : %f (bytes %d + %d inlined)", + compile_id, time.seconds(), bytes_per_sec, method->code_size(), task->num_inlined_bytecodes()); + } + + // Collect counts of successful compilations + _sum_nmethod_size += code->total_size(); + _sum_nmethod_code_size += code->code_size(); + _total_compile_count++; + + if (UsePerfData) { + _perf_sum_nmethod_size->inc(code->total_size()); + _perf_sum_nmethod_code_size->inc(code->code_size()); + _perf_total_compile_count->inc(); + } + + if (is_osr) { + if (UsePerfData) _perf_total_osr_compile_count->inc(); + _total_osr_compile_count++; + } else { + if (UsePerfData) _perf_total_standard_compile_count->inc(); + _total_standard_compile_count++; + } + } + // set the current method for the thread to null + if (UsePerfData) counters->set_current_method(""); +} + + + +void CompileBroker::print_times() { + tty->cr(); + tty->print_cr("Accumulated compiler times (for compiled methods only)"); + tty->print_cr("------------------------------------------------"); + //0000000000111111111122222222223333333333444444444455555555556666666666 + //0123456789012345678901234567890123456789012345678901234567890123456789 + tty->print_cr(" Total compilation time : %6.3f s", CompileBroker::_t_total_compilation.seconds()); + tty->print_cr(" Standard compilation : %6.3f s, Average : %2.3f", + CompileBroker::_t_standard_compilation.seconds(), + CompileBroker::_t_standard_compilation.seconds() / CompileBroker::_total_standard_compile_count); + tty->print_cr(" On stack replacement : %6.3f s, Average : %2.3f", CompileBroker::_t_osr_compilation.seconds(), CompileBroker::_t_osr_compilation.seconds() / CompileBroker::_total_osr_compile_count); + compiler(CompLevel_fast_compile)->print_timers(); + if (compiler(CompLevel_fast_compile) != compiler(CompLevel_highest_tier)) { + compiler(CompLevel_highest_tier)->print_timers(); + } + + tty->cr(); + int tcb = CompileBroker::_sum_osr_bytes_compiled + CompileBroker::_sum_standard_bytes_compiled; + tty->print_cr(" Total compiled bytecodes : %6d bytes", tcb); + tty->print_cr(" Standard compilation : %6d bytes", CompileBroker::_sum_standard_bytes_compiled); + tty->print_cr(" On stack replacement : %6d bytes", CompileBroker::_sum_osr_bytes_compiled); + int bps = (int)(tcb / CompileBroker::_t_total_compilation.seconds()); + tty->print_cr(" Average compilation speed: %6d bytes/s", bps); + tty->cr(); + tty->print_cr(" nmethod code size : %6d bytes", CompileBroker::_sum_nmethod_code_size); + tty->print_cr(" nmethod total size : %6d bytes", CompileBroker::_sum_nmethod_size); +} + + +// Debugging output for failure +void CompileBroker::print_last_compile() { + if ( _last_compile_level != CompLevel_none && + compiler(_last_compile_level) != NULL && + _last_method_compiled != NULL && + _last_compile_type != no_compile) { + if (_last_compile_type == osr_compile) { + tty->print_cr("Last parse: [osr]%d+++(%d) %s", + _osr_compilation_id, _last_compile_level, _last_method_compiled); + } else { + tty->print_cr("Last parse: %d+++(%d) %s", + _compilation_id, _last_compile_level, _last_method_compiled); + } + } +} + + +void CompileBroker::print_compiler_threads_on(outputStream* st) { +#ifndef PRODUCT + st->print_cr("Compiler thread printing unimplemented."); + st->cr(); +#endif +} diff --git a/hotspot/src/share/vm/compiler/compileBroker.hpp b/hotspot/src/share/vm/compiler/compileBroker.hpp new file mode 100644 index 00000000000..d976be14f26 --- /dev/null +++ b/hotspot/src/share/vm/compiler/compileBroker.hpp @@ -0,0 +1,343 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class nmethod; +class nmethodLocker; + +// CompileTask +// +// An entry in the compile queue. It represents a pending or current +// compilation. +class CompileTask : public CHeapObj { + private: + Monitor* _lock; + uint _compile_id; + jobject _method; + int _osr_bci; + bool _is_complete; + bool _is_success; + bool _is_blocking; + int _comp_level; + int _num_inlined_bytecodes; + nmethodLocker* _code_handle; // holder of eventual result + CompileTask* _next; + + // Fields used for logging why the compilation was initiated: + jlong _time_queued; // in units of os::elapsed_counter() + jobject _hot_method; // which method actually triggered this task + int _hot_count; // information about its invocation counter + const char* _comment; // more info about the task + + public: + CompileTask() { + _lock = new Monitor(Mutex::nonleaf+2, "CompileTaskLock"); + } + + void initialize(int compile_id, methodHandle method, int osr_bci, int comp_level, + methodHandle hot_method, int hot_count, const char* comment, + bool is_blocking); + + void free(); + + int compile_id() const { return _compile_id; } + jobject method_handle() const { return _method; } + int osr_bci() const { return _osr_bci; } + bool is_complete() const { return _is_complete; } + bool is_blocking() const { return _is_blocking; } + bool is_success() const { return _is_success; } + + nmethodLocker* code_handle() const { return _code_handle; } + void set_code_handle(nmethodLocker* l) { _code_handle = l; } + nmethod* code() const; // _code_handle->code() + void set_code(nmethod* nm); // _code_handle->set_code(nm) + + Monitor* lock() const { return _lock; } + + void mark_complete() { _is_complete = true; } + void mark_success() { _is_success = true; } + + int comp_level() { return _comp_level;} + void set_comp_level(int comp_level) { _comp_level = comp_level;} + + int num_inlined_bytecodes() const { return _num_inlined_bytecodes; } + void set_num_inlined_bytecodes(int n) { _num_inlined_bytecodes = n; } + + CompileTask* next() const { return _next; } + void set_next(CompileTask* next) { _next = next; } + + void print(); + void print_line(); + void print_line_on_error(outputStream* st, char* buf, int buflen); + void log_task(xmlStream* log); + void log_task_queued(); + void log_task_start(CompileLog* log); + void log_task_done(CompileLog* log); + +}; + +// CompilerCounters +// +// Per Compiler Performance Counters. +// +class CompilerCounters : public CHeapObj { + + public: + enum { + cmname_buffer_length = 160 + }; + + private: + + char _current_method[cmname_buffer_length]; + PerfStringVariable* _perf_current_method; + + int _compile_type; + PerfVariable* _perf_compile_type; + + PerfCounter* _perf_time; + PerfCounter* _perf_compiles; + + public: + CompilerCounters(const char* name, int instance, TRAPS); + + // these methods should be called in a thread safe context + + void set_current_method(const char* method) { + strncpy(_current_method, method, (size_t)cmname_buffer_length); + if (UsePerfData) _perf_current_method->set_value(method); + } + + char* current_method() { return _current_method; } + + void set_compile_type(int compile_type) { + _compile_type = compile_type; + if (UsePerfData) _perf_compile_type->set_value((jlong)compile_type); + } + + int compile_type() { return _compile_type; } + + PerfCounter* time_counter() { return _perf_time; } + PerfCounter* compile_counter() { return _perf_compiles; } +}; + + +// CompileQueue +// +// A list of CompileTasks. +class CompileQueue : public CHeapObj { + private: + const char* _name; + Monitor* _lock; + + CompileTask* _first; + CompileTask* _last; + + public: + CompileQueue(const char* name, Monitor* lock) { + _name = name; + _lock = lock; + _first = NULL; + _last = NULL; + } + + const char* name() const { return _name; } + Monitor* lock() const { return _lock; } + + void add(CompileTask* task); + + CompileTask* get(); + + bool is_empty() const { return _first == NULL; } + + void print(); +}; + + +// Compilation +// +// The broker for all compilation requests. +class CompileBroker: AllStatic { + friend class Threads; + friend class CompileTaskWrapper; + + public: + enum { + name_buffer_length = 100 + }; + + // Compile type Information for print_last_compile() and CompilerCounters + enum { no_compile, normal_compile, osr_compile, native_compile }; + + private: + static bool _initialized; + static volatile bool _should_block; + + // The installed compiler(s) + static AbstractCompiler* _compilers[2]; + + // These counters are used for assigning id's to each compilation + static uint _compilation_id; + static uint _osr_compilation_id; + static uint _native_compilation_id; + + static int _last_compile_type; + static int _last_compile_level; + static char _last_method_compiled[name_buffer_length]; + + static CompileQueue* _method_queue; + static CompileTask* _task_free_list; + + static GrowableArray* _method_threads; + + // performance counters + static PerfCounter* _perf_total_compilation; + static PerfCounter* _perf_native_compilation; + static PerfCounter* _perf_osr_compilation; + static PerfCounter* _perf_standard_compilation; + + static PerfCounter* _perf_total_bailout_count; + static PerfCounter* _perf_total_invalidated_count; + static PerfCounter* _perf_total_compile_count; + static PerfCounter* _perf_total_native_compile_count; + static PerfCounter* _perf_total_osr_compile_count; + static PerfCounter* _perf_total_standard_compile_count; + + static PerfCounter* _perf_sum_osr_bytes_compiled; + static PerfCounter* _perf_sum_standard_bytes_compiled; + static PerfCounter* _perf_sum_nmethod_size; + static PerfCounter* _perf_sum_nmethod_code_size; + + static PerfStringVariable* _perf_last_method; + static PerfStringVariable* _perf_last_failed_method; + static PerfStringVariable* _perf_last_invalidated_method; + static PerfVariable* _perf_last_compile_type; + static PerfVariable* _perf_last_compile_size; + static PerfVariable* _perf_last_failed_type; + static PerfVariable* _perf_last_invalidated_type; + + // Timers and counters for generating statistics + static elapsedTimer _t_total_compilation; + static elapsedTimer _t_osr_compilation; + static elapsedTimer _t_standard_compilation; + + static int _total_bailout_count; + static int _total_invalidated_count; + static int _total_compile_count; + static int _total_native_compile_count; + static int _total_osr_compile_count; + static int _total_standard_compile_count; + + static int _sum_osr_bytes_compiled; + static int _sum_standard_bytes_compiled; + static int _sum_nmethod_size; + static int _sum_nmethod_code_size; + + static int compiler_count() { + return CICompilerCountPerCPU + // Example: if CICompilerCountPerCPU is true, then we get + // max(log2(8)-1,1) = 2 compiler threads on an 8-way machine. + // May help big-app startup time. + ? (MAX2(log2_intptr(os::active_processor_count())-1,1)) + : CICompilerCount; + } + + static CompilerThread* make_compiler_thread(const char* name, CompileQueue* queue, CompilerCounters* counters, TRAPS); + static void init_compiler_threads(int compiler_count); + static bool compilation_is_complete (methodHandle method, int osr_bci, int comp_level); + static bool compilation_is_in_queue (methodHandle method, int osr_bci); + static bool compilation_is_prohibited(methodHandle method, int osr_bci, int comp_level); + static uint assign_compile_id (methodHandle method, int osr_bci); + static bool is_compile_blocking (methodHandle method, int osr_bci); + static void preload_classes (methodHandle method, TRAPS); + + static CompileTask* create_compile_task(CompileQueue* queue, + int compile_id, + methodHandle method, + int osr_bci, + int comp_level, + methodHandle hot_method, + int hot_count, + const char* comment, + bool blocking); + static CompileTask* allocate_task(); + static void free_task(CompileTask* task); + static void wait_for_completion(CompileTask* task); + + static void invoke_compiler_on_method(CompileTask* task); + static void set_last_compile(CompilerThread *thread, methodHandle method, bool is_osr, int comp_level); + static void push_jni_handle_block(); + static void pop_jni_handle_block(); + static bool check_break_at(methodHandle method, int compile_id, bool is_osr); + static void collect_statistics(CompilerThread* thread, elapsedTimer time, CompileTask* task); + + static void compile_method_base(methodHandle method, + int osr_bci, + int comp_level, + methodHandle hot_method, + int hot_count, + const char* comment, + TRAPS); + + public: + enum { + // The entry bci used for non-OSR compilations. + standard_entry_bci = InvocationEntryBci + }; + + static AbstractCompiler* compiler(int level ) { + if (level == CompLevel_fast_compile) return _compilers[0]; + assert(level == CompLevel_highest_tier, "what level?") + return _compilers[1]; + } + + static void compilation_init(); + static void init_compiler_thread_log(); + static nmethod* compile_method(methodHandle method, int osr_bci, + methodHandle hot_method, int hot_count, + const char* comment, TRAPS); + + static void compiler_thread_loop(); + + static bool is_idle(); + + // Set _should_block. + // Call this from the VM, with Threads_lock held and a safepoint requested. + static void set_should_block(); + + // Call this from the compiler at convenient points, to poll for _should_block. + static void maybe_block(); + + // Return total compilation ticks + static jlong total_compilation_ticks() { + return _perf_total_compilation != NULL ? _perf_total_compilation->get_value() : 0; + } + + // Print a detailed accounting of compilation time + static void print_times(); + + // Debugging output for failure + static void print_last_compile(); + + static void print_compiler_threads_on(outputStream* st); +}; diff --git a/hotspot/src/share/vm/compiler/compileLog.cpp b/hotspot/src/share/vm/compiler/compileLog.cpp new file mode 100644 index 00000000000..e5cf481e985 --- /dev/null +++ b/hotspot/src/share/vm/compiler/compileLog.cpp @@ -0,0 +1,294 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_compileLog.cpp.incl" + +CompileLog* CompileLog::_first = NULL; + +// ------------------------------------------------------------------ +// CompileLog::CompileLog +CompileLog::CompileLog(const char* file, FILE* fp, intx thread_id) + : _context(_context_buffer, sizeof(_context_buffer)) +{ + initialize(new(ResourceObj::C_HEAP) fileStream(fp)); + _file = file; + _file_end = 0; + _thread_id = thread_id; + + _identities_limit = 0; + _identities_capacity = 400; + _identities = NEW_C_HEAP_ARRAY(char, _identities_capacity); + + // link into the global list + { MutexLocker locker(CompileTaskAlloc_lock); + _next = _first; + _first = this; + } +} + +CompileLog::~CompileLog() { + delete _out; + _out = NULL; + FREE_C_HEAP_ARRAY(char, _identities); +} + + +// Advance kind up to a null or space, return this tail. +// Make sure kind is null-terminated, not space-terminated. +// Use the buffer if necessary. +static const char* split_attrs(const char* &kind, char* buffer) { + const char* attrs = strchr(kind, ' '); + // Tease apart the first word from the rest: + if (attrs == NULL) { + return ""; // no attrs, no split + } else if (kind == buffer) { + ((char*) attrs)[-1] = 0; + return attrs; + } else { + // park it in the buffer, so we can put a null on the end + assert(!(kind >= buffer && kind < buffer+100), "not obviously in buffer") + int klen = attrs - kind; + strncpy(buffer, kind, klen); + buffer[klen] = 0; + kind = buffer; // return by reference + return attrs; + } +} + +// see_tag, pop_tag: Override the default do-nothing methods on xmlStream. +// These methods provide a hook for managing the the extra context markup. +void CompileLog::see_tag(const char* tag, bool push) { + if (_context.size() > 0 && _out != NULL) { + _out->write(_context.base(), _context.size()); + _context.reset(); + } + xmlStream::see_tag(tag, push); +} +void CompileLog::pop_tag(const char* tag) { + _context.reset(); // toss any context info. + xmlStream::pop_tag(tag); +} + + +// ------------------------------------------------------------------ +// CompileLog::identify +int CompileLog::identify(ciObject* obj) { + if (obj == NULL) return 0; + int id = obj->ident(); + if (id < 0) return id; + // If it has already been identified, just return the id. + if (id < _identities_limit && _identities[id] != 0) return id; + // Lengthen the array, if necessary. + if (id >= _identities_capacity) { + int new_cap = _identities_capacity * 2; + if (new_cap <= id) new_cap = id + 100; + _identities = REALLOC_C_HEAP_ARRAY(char, _identities, new_cap); + _identities_capacity = new_cap; + } + while (id >= _identities_limit) { + _identities[_identities_limit++] = 0; + } + assert(id < _identities_limit, "oob"); + // Mark this id as processed. + // (Be sure to do this before any recursive calls to identify.) + _identities[id] = 1; // mark + + // Now, print the object's identity once, in detail. + if (obj->is_klass()) { + ciKlass* klass = obj->as_klass(); + begin_elem("klass id='%d'", id); + name(klass->name()); + if (!klass->is_loaded()) { + print(" unloaded='1'"); + } else { + print(" flags='%d'", klass->modifier_flags()); + } + end_elem(); + } else if (obj->is_method()) { + ciMethod* method = obj->as_method(); + ciSignature* sig = method->signature(); + // Pre-identify items that we will need! + identify(sig->return_type()); + for (int i = 0; i < sig->count(); i++) { + identify(sig->type_at(i)); + } + begin_elem("method id='%d' holder='%d'", + id, identify(method->holder())); + name(method->name()); + print(" return='%d'", identify(sig->return_type())); + if (sig->count() > 0) { + print(" arguments='"); + for (int i = 0; i < sig->count(); i++) { + print((i == 0) ? "%d" : " %d", identify(sig->type_at(i))); + } + print("'"); + } + if (!method->is_loaded()) { + print(" unloaded='1'"); + } else { + print(" flags='%d'", (jchar) method->flags().as_int()); + // output a few metrics + print(" bytes='%d'", method->code_size()); + method->log_nmethod_identity(this); + //print(" count='%d'", method->invocation_count()); + //int bec = method->backedge_count(); + //if (bec != 0) print(" backedge_count='%d'", bec); + print(" iicount='%d'", method->interpreter_invocation_count()); + } + end_elem(); + } else if (obj->is_symbol()) { + begin_elem("symbol id='%d'", id); + name(obj->as_symbol()); + end_elem(); + } else if (obj->is_null_object()) { + elem("null_object id='%d'", id); + } else if (obj->is_type()) { + BasicType type = obj->as_type()->basic_type(); + elem("type id='%d' name='%s'", id, type2name(type)); + } else { + // Should not happen. + elem("unknown id='%d'", id); + } + return id; +} + +void CompileLog::name(ciSymbol* name) { + if (name == NULL) return; + print(" name='"); + name->print_symbol_on(text()); // handles quoting conventions + print("'"); +} + + +// ------------------------------------------------------------------ +// CompileLog::clear_identities +// Forget which identities have been printed. +void CompileLog::clear_identities() { + _identities_limit = 0; +} + +// ------------------------------------------------------------------ +// CompileLog::finish_log_on_error +// +// Note: This function is called after fatal error, avoid unnecessary memory +// or stack allocation, use only async-safe functions. It's possible JVM is +// only partially initialized. +void CompileLog::finish_log_on_error(outputStream* file, char* buf, int buflen) { + static bool called_exit = false; + if (called_exit) return; + called_exit = true; + + for (CompileLog* log = _first; log != NULL; log = log->_next) { + log->flush(); + const char* partial_file = log->file(); + int partial_fd = open(partial_file, O_RDONLY); + if (partial_fd != -1) { + // print/print_cr may need to allocate large stack buffer to format + // strings, here we use snprintf() and print_raw() instead. + file->print_raw(""); + + size_t nr; // number read into buf from partial log + // Copy data up to the end of the last element: + julong to_read = log->_file_end; + while (to_read > 0) { + if (to_read < (julong)buflen) + nr = (size_t)to_read; + else nr = buflen; + nr = read(partial_fd, buf, (int)nr); + if (nr <= 0) break; + to_read -= (julong)nr; + file->write(buf, nr); + } + + // Copy any remaining data inside a quote: + bool saw_slop = false; + int end_cdata = 0; // state machine [0..2] watching for too many "]]" + while ((nr = read(partial_fd, buf, buflen)) > 0) { + if (!saw_slop) { + file->print_raw_cr(""); + file->print_raw_cr("write(buf, nr); } + // However, it must sometimes output the buffer in parts, + // in case there is a CDATA quote embedded in the fragment. + const char* bufp; // pointer into buf + size_t nw; // number written in each pass of the following loop: + for (bufp = buf; nr > 0; nr -= nw, bufp += nw) { + // Write up to any problematic CDATA delimiter (usually all of nr). + for (nw = 0; nw < nr; nw++) { + // First, scan ahead into the buf, checking the state machine. + switch (bufp[nw]) { + case ']': + if (end_cdata < 2) end_cdata += 1; // saturating counter + continue; // keep scanning + case '>': + if (end_cdata == 2) break; // found CDATA delimiter! + // else fall through: + default: + end_cdata = 0; + continue; // keep scanning + } + // If we get here, nw is pointing at a bad '>'. + // It is very rare for this to happen. + // However, this code has been tested by introducing + // CDATA sequences into the compilation log. + break; + } + // Now nw is the number of characters to write, usually == nr. + file->write(bufp, nw); + if (nw < nr) { + // We are about to go around the loop again. + // But first, disrupt the ]]> by closing and reopening the quote. + file->print_raw("]]>print_raw_cr("]]>"); + file->print_raw_cr(""); + } + file->print_raw_cr(""); + close(partial_fd); + unlink(partial_file); + } + } +} + +// ------------------------------------------------------------------ +// CompileLog::finish_log +// +// Called during normal shutdown. For now, any clean-up needed in normal +// shutdown is also needed in VM abort, so is covered by finish_log_on_error(). +// Just allocate a buffer and call finish_log_on_error(). +void CompileLog::finish_log(outputStream* file) { + char buf[4 * K]; + finish_log_on_error(file, buf, sizeof(buf)); +} diff --git a/hotspot/src/share/vm/compiler/compileLog.hpp b/hotspot/src/share/vm/compiler/compileLog.hpp new file mode 100644 index 00000000000..aee22166af8 --- /dev/null +++ b/hotspot/src/share/vm/compiler/compileLog.hpp @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ciObject; +class ciSymbol; + +// CompileLog +// +// An open stream for logging information about activities in a +// compiler thread. There is exactly one per CompilerThread, +// if the +LogCompilation switch is enabled. +class CompileLog : public xmlStream { + private: + const char* _file; // name of file where XML goes + julong _file_end; // last good end of file + intx _thread_id; // which compile thread + + stringStream _context; // optional, killable context marker + char _context_buffer[100]; + + char* _identities; // array of boolean + int _identities_limit; + int _identities_capacity; + + CompileLog* _next; // static chain of all logs + + static CompileLog* _first; // head of static chain + + void va_tag(bool push, const char* format, va_list ap); + + public: + CompileLog(const char* file, FILE* fp, intx thread_id); + ~CompileLog(); + + intx thread_id() { return _thread_id; } + const char* file() { return _file; } + stringStream* context() { return &_context; } + + void name(ciSymbol* s); // name='s' + void name(symbolHandle s) { xmlStream::name(s); } + + // Output an object description, return obj->ident(). + int identify(ciObject* obj); + void clear_identities(); + + // virtuals + virtual void see_tag(const char* tag, bool push); + virtual void pop_tag(const char* tag); + + // make a provisional end of log mark + void mark_file_end() { _file_end = out()->count(); } + + // copy all logs to the given stream + static void finish_log(outputStream* out); + static void finish_log_on_error(outputStream* out, char *buf, int buflen); +}; diff --git a/hotspot/src/share/vm/compiler/compilerOracle.cpp b/hotspot/src/share/vm/compiler/compilerOracle.cpp new file mode 100644 index 00000000000..1829e044a06 --- /dev/null +++ b/hotspot/src/share/vm/compiler/compilerOracle.cpp @@ -0,0 +1,710 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_compilerOracle.cpp.incl" + +class MethodMatcher : public CHeapObj { + public: + enum Mode { + Exact, + Prefix = 1, + Suffix = 2, + Substring = Prefix | Suffix, + Any, + Unknown = -1 + }; + + protected: + jobject _class_name; + Mode _class_mode; + jobject _method_name; + Mode _method_mode; + jobject _signature; + MethodMatcher* _next; + + static bool match(symbolHandle candidate, symbolHandle match, Mode match_mode); + + symbolHandle class_name() const { return (symbolOop)JNIHandles::resolve_non_null(_class_name); } + symbolHandle method_name() const { return (symbolOop)JNIHandles::resolve_non_null(_method_name); } + symbolHandle signature() const { return (symbolOop)JNIHandles::resolve(_signature); } + + public: + MethodMatcher(symbolHandle class_name, Mode class_mode, + symbolHandle method_name, Mode method_mode, + symbolHandle signature, MethodMatcher* next); + MethodMatcher(symbolHandle class_name, symbolHandle method_name, MethodMatcher* next); + + // utility method + MethodMatcher* find(methodHandle method) { + symbolHandle class_name = Klass::cast(method->method_holder())->name(); + symbolHandle method_name = method->name(); + for (MethodMatcher* current = this; current != NULL; current = current->_next) { + if (match(class_name, current->class_name(), current->_class_mode) && + match(method_name, current->method_name(), current->_method_mode) && + (current->signature().is_null() || current->signature()() == method->signature())) { + return current; + } + } + return NULL; + } + + bool match(methodHandle method) { + return find(method) != NULL; + } + + MethodMatcher* next() const { return _next; } + + static void print_symbol(symbolHandle h, Mode mode) { + ResourceMark rm; + + if (mode == Suffix || mode == Substring || mode == Any) { + tty->print("*"); + } + if (mode != Any) { + h()->print_symbol_on(tty); + } + if (mode == Prefix || mode == Substring) { + tty->print("*"); + } + } + + void print_base() { + print_symbol(class_name(), _class_mode); + tty->print("."); + print_symbol(method_name(), _method_mode); + if (!signature().is_null()) { + tty->print(" "); + signature()->print_symbol_on(tty); + } + } + + virtual void print() { + print_base(); + tty->cr(); + } +}; + +MethodMatcher::MethodMatcher(symbolHandle class_name, symbolHandle method_name, MethodMatcher* next) { + _class_name = JNIHandles::make_global(class_name); + _method_name = JNIHandles::make_global(method_name); + _next = next; + _class_mode = MethodMatcher::Exact; + _method_mode = MethodMatcher::Exact; + _signature = NULL; +} + + +MethodMatcher::MethodMatcher(symbolHandle class_name, Mode class_mode, + symbolHandle method_name, Mode method_mode, + symbolHandle signature, MethodMatcher* next): + _class_mode(class_mode) + , _method_mode(method_mode) + , _next(next) + , _class_name(JNIHandles::make_global(class_name())) + , _method_name(JNIHandles::make_global(method_name())) + , _signature(JNIHandles::make_global(signature())) { +} + +bool MethodMatcher::match(symbolHandle candidate, symbolHandle match, Mode match_mode) { + if (match_mode == Any) { + return true; + } + + if (match_mode == Exact) { + return candidate() == match(); + } + + ResourceMark rm; + const char * candidate_string = candidate->as_C_string(); + const char * match_string = match->as_C_string(); + + switch (match_mode) { + case Prefix: + return strstr(candidate_string, match_string) == candidate_string; + + case Suffix: { + size_t clen = strlen(candidate_string); + size_t mlen = strlen(match_string); + return clen >= mlen && strcmp(candidate_string + clen - mlen, match_string) == 0; + } + + case Substring: + return strstr(candidate_string, match_string) != NULL; + + default: + return false; + } +} + + +class MethodOptionMatcher: public MethodMatcher { + const char * option; + public: + MethodOptionMatcher(symbolHandle class_name, Mode class_mode, + symbolHandle method_name, Mode method_mode, + symbolHandle signature, const char * opt, MethodMatcher* next): + MethodMatcher(class_name, class_mode, method_name, method_mode, signature, next) { + option = opt; + } + + bool match(methodHandle method, const char* opt) { + MethodOptionMatcher* current = this; + while (current != NULL) { + current = (MethodOptionMatcher*)current->find(method); + if (current == NULL) { + return false; + } + if (strcmp(current->option, opt) == 0) { + return true; + } + current = current->next(); + } + return false; + } + + MethodOptionMatcher* next() { + return (MethodOptionMatcher*)_next; + } + + virtual void print() { + print_base(); + tty->print(" %s", option); + tty->cr(); + } +}; + + + +// this must parallel the command_names below +enum OracleCommand { + UnknownCommand = -1, + OracleFirstCommand = 0, + BreakCommand = OracleFirstCommand, + PrintCommand, + ExcludeCommand, + InlineCommand, + DontInlineCommand, + CompileOnlyCommand, + LogCommand, + OptionCommand, + QuietCommand, + HelpCommand, + OracleCommandCount +}; + +// this must parallel the enum OracleCommand +static const char * command_names[] = { + "break", + "print", + "exclude", + "inline", + "dontinline", + "compileonly", + "log", + "option", + "quiet", + "help" +}; + +static const char * command_name(OracleCommand command) { + if (command < OracleFirstCommand || command >= OracleCommandCount) { + return "unknown command"; + } + return command_names[command]; +} + +class MethodMatcher; +static MethodMatcher* lists[OracleCommandCount] = { 0, }; + + +static bool check_predicate(OracleCommand command, methodHandle method) { + return ((lists[command] != NULL) && + !method.is_null() && + lists[command]->match(method)); +} + + +static MethodMatcher* add_predicate(OracleCommand command, + symbolHandle class_name, MethodMatcher::Mode c_mode, + symbolHandle method_name, MethodMatcher::Mode m_mode, + symbolHandle signature) { + assert(command != OptionCommand, "must use add_option_string"); + if (command == LogCommand && !LogCompilation && lists[LogCommand] == NULL) + tty->print_cr("Warning: +LogCompilation must be enabled in order for individual methods to be logged."); + lists[command] = new MethodMatcher(class_name, c_mode, method_name, m_mode, signature, lists[command]); + return lists[command]; +} + + + +static MethodMatcher* add_option_string(symbolHandle class_name, MethodMatcher::Mode c_mode, + symbolHandle method_name, MethodMatcher::Mode m_mode, + symbolHandle signature, + const char* option) { + lists[OptionCommand] = new MethodOptionMatcher(class_name, c_mode, method_name, m_mode, + signature, option, lists[OptionCommand]); + return lists[OptionCommand]; +} + + +bool CompilerOracle::has_option_string(methodHandle method, const char* option) { + return lists[OptionCommand] != NULL && + ((MethodOptionMatcher*)lists[OptionCommand])->match(method, option); +} + + +bool CompilerOracle::should_exclude(methodHandle method, bool& quietly) { + quietly = true; + if (lists[ExcludeCommand] != NULL) { + if (lists[ExcludeCommand]->match(method)) { + quietly = _quiet; + return true; + } + } + + if (lists[CompileOnlyCommand] != NULL) { + return !lists[CompileOnlyCommand]->match(method); + } + return false; +} + + +bool CompilerOracle::should_inline(methodHandle method) { + return (check_predicate(InlineCommand, method)); +} + + +bool CompilerOracle::should_not_inline(methodHandle method) { + return (check_predicate(DontInlineCommand, method)); +} + + +bool CompilerOracle::should_print(methodHandle method) { + return (check_predicate(PrintCommand, method)); +} + + +bool CompilerOracle::should_log(methodHandle method) { + if (!LogCompilation) return false; + if (lists[LogCommand] == NULL) return true; // by default, log all + return (check_predicate(LogCommand, method)); +} + + +bool CompilerOracle::should_break_at(methodHandle method) { + return check_predicate(BreakCommand, method); +} + + +static OracleCommand parse_command_name(const char * line, int* bytes_read) { + assert(ARRAY_SIZE(command_names) == OracleCommandCount, + "command_names size mismatch"); + + *bytes_read = 0; + char command[32]; + int result = sscanf(line, "%32[a-z]%n", command, bytes_read); + for (uint i = 0; i < ARRAY_SIZE(command_names); i++) { + if (strcmp(command, command_names[i]) == 0) { + return (OracleCommand)i; + } + } + return UnknownCommand; +} + + +static void usage() { + tty->print_cr(" CompileCommand and the CompilerOracle allows simple control over"); + tty->print_cr(" what's allowed to be compiled. The standard supported directives"); + tty->print_cr(" are exclude and compileonly. The exclude directive stops a method"); + tty->print_cr(" from being compiled and compileonly excludes all methods except for"); + tty->print_cr(" the ones mentioned by compileonly directives. The basic form of"); + tty->print_cr(" all commands is a command name followed by the name of the method"); + tty->print_cr(" in one of two forms: the standard class file format as in"); + tty->print_cr(" class/name.methodName or the PrintCompilation format"); + tty->print_cr(" class.name::methodName. The method name can optionally be followed"); + tty->print_cr(" by a space then the signature of the method in the class file"); + tty->print_cr(" format. Otherwise the directive applies to all methods with the"); + tty->print_cr(" same name and class regardless of signature. Leading and trailing"); + tty->print_cr(" *'s in the class and/or method name allows a small amount of"); + tty->print_cr(" wildcarding. "); + tty->cr(); + tty->print_cr(" Examples:"); + tty->cr(); + tty->print_cr(" exclude java/lang/StringBuffer.append"); + tty->print_cr(" compileonly java/lang/StringBuffer.toString ()Ljava/lang/String;"); + tty->print_cr(" exclude java/lang/String*.*"); + tty->print_cr(" exclude *.toString"); +} + + +// The characters allowed in a class or method name. All characters > 0x7f +// are allowed in order to handle obfuscated class files (e.g. Volano) +#define RANGEBASE "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_<>" \ + "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" \ + "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" \ + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" \ + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" \ + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" \ + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" \ + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" \ + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + +#define RANGE0 "[*" RANGEBASE "]" +#define RANGEDOT "[*" RANGEBASE ".]" +#define RANGESLASH "[*" RANGEBASE "/]" + + +// Accept several syntaxes for these patterns +// original syntax +// cmd java.lang.String foo +// PrintCompilation syntax +// cmd java.lang.String::foo +// VM syntax +// cmd java/lang/String[. ]foo +// + +static const char* patterns[] = { + "%*[ \t]%255" RANGEDOT " " "%255" RANGE0 "%n", + "%*[ \t]%255" RANGEDOT "::" "%255" RANGE0 "%n", + "%*[ \t]%255" RANGESLASH "%*[ .]" "%255" RANGE0 "%n", +}; + +static MethodMatcher::Mode check_mode(char name[], const char*& error_msg) { + if (strcmp(name, "*") == 0) return MethodMatcher::Any; + + int match = MethodMatcher::Exact; + if (name[0] == '*') { + match |= MethodMatcher::Suffix; + strcpy(name, name + 1); + } + + size_t len = strlen(name); + if (len > 0 && name[len - 1] == '*') { + match |= MethodMatcher::Prefix; + name[len - 1] = '\0'; + } + + if (strstr(name, "*") != NULL) { + error_msg = " Embedded * not allowed"; + return MethodMatcher::Unknown; + } + return (MethodMatcher::Mode)match; +} + +static bool scan_line(const char * line, + char class_name[], MethodMatcher::Mode* c_mode, + char method_name[], MethodMatcher::Mode* m_mode, + int* bytes_read, const char*& error_msg) { + *bytes_read = 0; + error_msg = NULL; + for (uint i = 0; i < ARRAY_SIZE(patterns); i++) { + if (2 == sscanf(line, patterns[i], class_name, method_name, bytes_read)) { + *c_mode = check_mode(class_name, error_msg); + *m_mode = check_mode(method_name, error_msg); + return *c_mode != MethodMatcher::Unknown && *m_mode != MethodMatcher::Unknown; + } + } + return false; +} + + + +void CompilerOracle::parse_from_line(char* line) { + if (line[0] == '\0') return; + if (line[0] == '#') return; + + bool have_colon = (strstr(line, "::") != NULL); + for (char* lp = line; *lp != '\0'; lp++) { + // Allow '.' to separate the class name from the method name. + // This is the preferred spelling of methods: + // exclude java/lang/String.indexOf(I)I + // Allow ',' for spaces (eases command line quoting). + // exclude,java/lang/String.indexOf + // For backward compatibility, allow space as separator also. + // exclude java/lang/String indexOf + // exclude,java/lang/String,indexOf + // For easy cut-and-paste of method names, allow VM output format + // as produced by methodOopDesc::print_short_name: + // exclude java.lang.String::indexOf + // For simple implementation convenience here, convert them all to space. + if (have_colon) { + if (*lp == '.') *lp = '/'; // dots build the package prefix + if (*lp == ':') *lp = ' '; + } + if (*lp == ',' || *lp == '.') *lp = ' '; + } + + char* original_line = line; + int bytes_read; + OracleCommand command = parse_command_name(line, &bytes_read); + line += bytes_read; + + if (command == QuietCommand) { + _quiet = true; + return; + } + + if (command == HelpCommand) { + usage(); + return; + } + + MethodMatcher::Mode c_match = MethodMatcher::Exact; + MethodMatcher::Mode m_match = MethodMatcher::Exact; + char class_name[256]; + char method_name[256]; + char sig[1024]; + char errorbuf[1024]; + const char* error_msg = NULL; + MethodMatcher* match = NULL; + + if (scan_line(line, class_name, &c_match, method_name, &m_match, &bytes_read, error_msg)) { + EXCEPTION_MARK; + symbolHandle c_name = oopFactory::new_symbol_handle(class_name, CHECK); + symbolHandle m_name = oopFactory::new_symbol_handle(method_name, CHECK); + symbolHandle signature; + + line += bytes_read; + // there might be a signature following the method. + // signatures always begin with ( so match that by hand + if (1 == sscanf(line, "%*[ \t](%254[);/" RANGEBASE "]%n", sig + 1, &bytes_read)) { + sig[0] = '('; + line += bytes_read; + signature = oopFactory::new_symbol_handle(sig, CHECK); + } + + if (command == OptionCommand) { + // Look for trailing options to support + // ciMethod::has_option("string") to control features in the + // compiler. Multiple options may follow the method name. + char option[256]; + while (sscanf(line, "%*[ \t]%255[a-zA-Z0-9]%n", option, &bytes_read) == 1) { + if (match != NULL && !_quiet) { + // Print out the last match added + tty->print("CompilerOracle: %s ", command_names[command]); + match->print(); + } + match = add_option_string(c_name, c_match, m_name, m_match, signature, strdup(option)); + line += bytes_read; + } + } else { + bytes_read = 0; + sscanf(line, "%*[ \t]%n", &bytes_read); + if (line[bytes_read] != '\0') { + jio_snprintf(errorbuf, sizeof(errorbuf), " Unrecognized text after command: %s", line); + error_msg = errorbuf; + } else { + match = add_predicate(command, c_name, c_match, m_name, m_match, signature); + } + } + } + + if (match != NULL) { + if (!_quiet) { + tty->print("CompilerOracle: %s ", command_names[command]); + match->print(); + } + } else { + tty->print_cr("CompilerOracle: unrecognized line"); + tty->print_cr(" \"%s\"", original_line); + if (error_msg != NULL) { + tty->print_cr(error_msg); + } + } +} + +static const char* cc_file() { + if (CompileCommandFile == NULL) + return ".hotspot_compiler"; + return CompileCommandFile; +} +bool CompilerOracle::_quiet = false; + +void CompilerOracle::parse_from_file() { + FILE* stream = fopen(cc_file(), "rt"); + if (stream == NULL) return; + + char token[1024]; + int pos = 0; + int c = getc(stream); + while(c != EOF) { + if (c == '\n') { + token[pos++] = '\0'; + parse_from_line(token); + pos = 0; + } else { + token[pos++] = c; + } + c = getc(stream); + } + token[pos++] = '\0'; + parse_from_line(token); + + fclose(stream); +} + +void CompilerOracle::parse_from_string(const char* str, void (*parse_line)(char*)) { + char token[1024]; + int pos = 0; + const char* sp = str; + int c = *sp++; + while (c != '\0') { + if (c == '\n') { + token[pos++] = '\0'; + parse_line(token); + pos = 0; + } else { + token[pos++] = c; + } + c = *sp++; + } + token[pos++] = '\0'; + parse_line(token); +} + +void CompilerOracle::append_comment_to_file(const char* message) { + fileStream stream(fopen(cc_file(), "at")); + stream.print("# "); + for (int index = 0; message[index] != '\0'; index++) { + stream.put(message[index]); + if (message[index] == '\n') stream.print("# "); + } + stream.cr(); +} + +void CompilerOracle::append_exclude_to_file(methodHandle method) { + fileStream stream(fopen(cc_file(), "at")); + stream.print("exclude "); + Klass::cast(method->method_holder())->name()->print_symbol_on(&stream); + stream.print("."); + method->name()->print_symbol_on(&stream); + method->signature()->print_symbol_on(&stream); + stream.cr(); + stream.cr(); +} + + +void compilerOracle_init() { + CompilerOracle::parse_from_string(CompileCommand, CompilerOracle::parse_from_line); + CompilerOracle::parse_from_string(CompileOnly, CompilerOracle::parse_compile_only); + CompilerOracle::parse_from_file(); +} + + +void CompilerOracle::parse_compile_only(char * line) { + int i; + char name[1024]; + const char* className = NULL; + const char* methodName = NULL; + + bool have_colon = (strstr(line, "::") != NULL); + char method_sep = have_colon ? ':' : '.'; + + if (Verbose) { + tty->print_cr(line); + } + + ResourceMark rm; + while (*line != '\0') { + MethodMatcher::Mode c_match = MethodMatcher::Exact; + MethodMatcher::Mode m_match = MethodMatcher::Exact; + + for (i = 0; + i < 1024 && *line != '\0' && *line != method_sep && *line != ',' && !isspace(*line); + line++, i++) { + name[i] = *line; + if (name[i] == '.') name[i] = '/'; // package prefix uses '/' + } + + if (i > 0) { + char* newName = NEW_RESOURCE_ARRAY( char, i + 1); + if (newName == NULL) + return; + strncpy(newName, name, i); + newName[i] = '\0'; + + if (className == NULL) { + className = newName; + c_match = MethodMatcher::Prefix; + } else { + methodName = newName; + } + } + + if (*line == method_sep) { + if (className == NULL) { + className = ""; + c_match = MethodMatcher::Any; + } else { + // foo/bar.blah is an exact match on foo/bar, bar.blah is a suffix match on bar + if (strchr(className, '/') != NULL) { + c_match = MethodMatcher::Exact; + } else { + c_match = MethodMatcher::Suffix; + } + } + } else { + // got foo or foo/bar + if (className == NULL) { + ShouldNotReachHere(); + } else { + // got foo or foo/bar + if (strchr(className, '/') != NULL) { + c_match = MethodMatcher::Prefix; + } else if (className[0] == '\0') { + c_match = MethodMatcher::Any; + } else { + c_match = MethodMatcher::Substring; + } + } + } + + // each directive is terminated by , or NUL or . followed by NUL + if (*line == ',' || *line == '\0' || (line[0] == '.' && line[1] == '\0')) { + if (methodName == NULL) { + methodName = ""; + if (*line != method_sep) { + m_match = MethodMatcher::Any; + } + } + + EXCEPTION_MARK; + symbolHandle c_name = oopFactory::new_symbol_handle(className, CHECK); + symbolHandle m_name = oopFactory::new_symbol_handle(methodName, CHECK); + symbolHandle signature; + + add_predicate(CompileOnlyCommand, c_name, c_match, m_name, m_match, signature); + if (PrintVMOptions) { + tty->print("CompileOnly: compileonly "); + lists[CompileOnlyCommand]->print(); + } + + className = NULL; + methodName = NULL; + } + + line = *line == '\0' ? line : line + 1; + } +} diff --git a/hotspot/src/share/vm/compiler/compilerOracle.hpp b/hotspot/src/share/vm/compiler/compilerOracle.hpp new file mode 100644 index 00000000000..0b75f514eb9 --- /dev/null +++ b/hotspot/src/share/vm/compiler/compilerOracle.hpp @@ -0,0 +1,68 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// CompilerOracle is an interface for turning on and off compilation +// for some methods + +class symbolHandle; + +class CompilerOracle : AllStatic { + private: + static bool _quiet; + + public: + // Reads from file and adds to lists + static void parse_from_file(); + + // Tells whether we to exclude compilation of method + static bool should_exclude(methodHandle method, bool& quietly); + + // Tells whether we want to inline this method + static bool should_inline(methodHandle method); + + // Tells whether we want to disallow inlining of this method + static bool should_not_inline(methodHandle method); + + // Tells whether we should print the assembly for this method + static bool should_print(methodHandle method); + + // Tells whether we should log the compilation data for this method + static bool should_log(methodHandle method); + + // Tells whether to break when compiling method + static bool should_break_at(methodHandle method); + + // Check to see if this method has option set for it + static bool has_option_string(methodHandle method, const char * option); + + // Reads from string instead of file + static void parse_from_string(const char* command_string, void (*parser)(char*)); + + static void parse_from_line(char* line); + static void parse_compile_only(char * line); + + // For updating the oracle file + static void append_comment_to_file(const char* message); + static void append_exclude_to_file(methodHandle method); +}; diff --git a/hotspot/src/share/vm/compiler/disassemblerEnv.hpp b/hotspot/src/share/vm/compiler/disassemblerEnv.hpp new file mode 100644 index 00000000000..69bb38f77c1 --- /dev/null +++ b/hotspot/src/share/vm/compiler/disassemblerEnv.hpp @@ -0,0 +1,35 @@ +/* + * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Call-back interface for external disassembler +class DisassemblerEnv { + public: + // printing + virtual void print_label(intptr_t value) = 0; + virtual void print_raw(char* str) = 0; + virtual void print(char* format, ...) = 0; + // helpers + virtual char* string_for_offset(intptr_t value) = 0; + virtual char* string_for_constant(unsigned char* pc, intptr_t value, int is_decimal) = 0; +}; diff --git a/hotspot/src/share/vm/compiler/methodLiveness.cpp b/hotspot/src/share/vm/compiler/methodLiveness.cpp new file mode 100644 index 00000000000..92d60cfeca8 --- /dev/null +++ b/hotspot/src/share/vm/compiler/methodLiveness.cpp @@ -0,0 +1,1063 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The MethodLiveness class performs a simple liveness analysis on a method +// in order to decide which locals are live (that is, will be used again) at +// a particular bytecode index (bci). +// +// The algorithm goes: +// +// 1. Break the method into a set of basic blocks. For each basic block we +// also keep track of its set of predecessors through normal control flow +// and predecessors through exceptional control flow. +// +// 2. For each basic block, compute two sets, gen (the set of values used before +// they are defined) and kill (the set of values defined before they are used) +// in the basic block. A basic block "needs" the locals in its gen set to +// perform its computation. A basic block "provides" values for the locals in +// its kill set, allowing a need from a successor to be ignored. +// +// 3. Liveness information (the set of locals which are needed) is pushed backwards through +// the program, from blocks to their predecessors. We compute and store liveness +// information for the normal/exceptional exit paths for each basic block. When +// this process reaches a fixed point, we are done. +// +// 4. When we are asked about the liveness at a particular bci with a basic block, we +// compute gen/kill sets which represent execution from that bci to the exit of +// its blocks. We then compose this range gen/kill information with the normal +// and exceptional exit information for the block to produce liveness information +// at that bci. +// +// The algorithm is approximate in many respects. Notably: +// +// 1. We do not do the analysis necessary to match jsr's with the appropriate ret. +// Instead we make the conservative assumption that any ret can return to any +// jsr return site. +// 2. Instead of computing the effects of exceptions at every instruction, we +// summarize the effects of all exceptional continuations from the block as +// a single set (_exception_exit), losing some information but simplifying the +// analysis. + + +# include "incls/_precompiled.incl" +# include "incls/_methodLiveness.cpp.incl" + +//-------------------------------------------------------------------------- +// The BitCounter class is used for counting the number of bits set in +// some BitMap. It is only used when collecting liveness statistics. + +#ifndef PRODUCT + +class BitCounter: public BitMapClosure { + private: + int _count; + public: + BitCounter() : _count(0) {} + + // Callback when bit in map is set + virtual void do_bit(size_t offset) { + _count++; + } + + int count() { + return _count; + } +}; + + +//-------------------------------------------------------------------------- + + +// Counts +long MethodLiveness::_total_bytes = 0; +int MethodLiveness::_total_methods = 0; + +long MethodLiveness::_total_blocks = 0; +int MethodLiveness::_max_method_blocks = 0; + +long MethodLiveness::_total_edges = 0; +int MethodLiveness::_max_block_edges = 0; + +long MethodLiveness::_total_exc_edges = 0; +int MethodLiveness::_max_block_exc_edges = 0; + +long MethodLiveness::_total_method_locals = 0; +int MethodLiveness::_max_method_locals = 0; + +long MethodLiveness::_total_locals_queried = 0; +long MethodLiveness::_total_live_locals_queried = 0; + +long MethodLiveness::_total_visits = 0; + +#endif + +// Timers +elapsedTimer MethodLiveness::_time_build_graph; +elapsedTimer MethodLiveness::_time_gen_kill; +elapsedTimer MethodLiveness::_time_flow; +elapsedTimer MethodLiveness::_time_query; +elapsedTimer MethodLiveness::_time_total; + +MethodLiveness::MethodLiveness(Arena* arena, ciMethod* method) +#ifdef COMPILER1 + : _bci_block_start((uintptr_t*)arena->Amalloc((method->code_size() >> LogBitsPerByte) + 1), method->code_size()) +#endif +{ + _arena = arena; + _method = method; + _bit_map_size_bits = method->max_locals(); + _bit_map_size_words = (_bit_map_size_bits / sizeof(unsigned int)) + 1; + +#ifdef COMPILER1 + _bci_block_start.clear(); +#endif +} + +void MethodLiveness::compute_liveness() { +#ifndef PRODUCT + if (TraceLivenessGen) { + tty->print_cr("################################################################"); + tty->print("# Computing liveness information for "); + method()->print_short_name(); + } + + if (TimeLivenessAnalysis) _time_total.start(); +#endif + + { + TraceTime buildGraph(NULL, &_time_build_graph, TimeLivenessAnalysis); + init_basic_blocks(); + } + { + TraceTime genKill(NULL, &_time_gen_kill, TimeLivenessAnalysis); + init_gen_kill(); + } + { + TraceTime flow(NULL, &_time_flow, TimeLivenessAnalysis); + propagate_liveness(); + } + +#ifndef PRODUCT + if (TimeLivenessAnalysis) _time_total.stop(); + + if (TimeLivenessAnalysis) { + // Collect statistics + _total_bytes += method()->code_size(); + _total_methods++; + + int num_blocks = _block_count; + _total_blocks += num_blocks; + _max_method_blocks = MAX2(num_blocks,_max_method_blocks); + + for (int i=0; i_normal_predecessors->length(); + int numExcEdges = block->_exception_predecessors->length(); + + _total_edges += numEdges; + _total_exc_edges += numExcEdges; + _max_block_edges = MAX2(numEdges,_max_block_edges); + _max_block_exc_edges = MAX2(numExcEdges,_max_block_exc_edges); + } + + int numLocals = _bit_map_size_bits; + _total_method_locals += numLocals; + _max_method_locals = MAX2(numLocals,_max_method_locals); + } +#endif +} + + +void MethodLiveness::init_basic_blocks() { + bool bailout = false; + + int method_len = method()->code_size(); + ciMethodBlocks *mblocks = method()->get_method_blocks(); + + // Create an array to store the bci->BasicBlock mapping. + _block_map = new (arena()) GrowableArray(arena(), method_len, method_len, NULL); + + _block_count = mblocks->num_blocks(); + _block_list = (BasicBlock **) arena()->Amalloc(sizeof(BasicBlock *) * _block_count); + + // Used for patching up jsr/ret control flow. + GrowableArray* jsr_exit_list = new GrowableArray(5); + GrowableArray* ret_list = new GrowableArray(5); + + // generate our block list from ciMethodBlocks + for (int blk = 0; blk < _block_count; blk++) { + ciBlock *cib = mblocks->block(blk); + int start_bci = cib->start_bci(); + _block_list[blk] = new (arena()) BasicBlock(this, start_bci, cib->limit_bci()); + _block_map->at_put(start_bci, _block_list[blk]); +#ifdef COMPILER1 + // mark all bcis where a new basic block starts + _bci_block_start.set_bit(start_bci); +#endif // COMPILER1 + } + // fill in the predecessors of blocks + ciBytecodeStream bytes(method()); + + for (int blk = 0; blk < _block_count; blk++) { + BasicBlock *current_block = _block_list[blk]; + int bci = mblocks->block(blk)->control_bci(); + + if (bci == ciBlock::fall_through_bci) { + int limit = current_block->limit_bci(); + if (limit < method_len) { + BasicBlock *next = _block_map->at(limit); + assert( next != NULL, "must be a block immediately following this one."); + next->add_normal_predecessor(current_block); + } + continue; + } + bytes.reset_to_bci(bci); + Bytecodes::Code code = bytes.next(); + BasicBlock *dest; + + // Now we need to interpret the instruction's effect + // on control flow. + assert (current_block != NULL, "we must have a current block"); + switch (code) { + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + // Two way branch. Set predecessors at each destination. + dest = _block_map->at(bytes.next_bci()); + assert(dest != NULL, "must be a block immediately following this one."); + dest->add_normal_predecessor(current_block); + + dest = _block_map->at(bytes.get_dest()); + assert(dest != NULL, "branch desination must start a block."); + dest->add_normal_predecessor(current_block); + break; + case Bytecodes::_goto: + dest = _block_map->at(bytes.get_dest()); + assert(dest != NULL, "branch desination must start a block."); + dest->add_normal_predecessor(current_block); + break; + case Bytecodes::_goto_w: + dest = _block_map->at(bytes.get_far_dest()); + assert(dest != NULL, "branch desination must start a block."); + dest->add_normal_predecessor(current_block); + break; + case Bytecodes::_tableswitch: + { + Bytecode_tableswitch *tableswitch = + Bytecode_tableswitch_at(bytes.cur_bcp()); + + int len = tableswitch->length(); + + dest = _block_map->at(bci + tableswitch->default_offset()); + assert(dest != NULL, "branch desination must start a block."); + dest->add_normal_predecessor(current_block); + while (--len >= 0) { + dest = _block_map->at(bci + tableswitch->dest_offset_at(len)); + assert(dest != NULL, "branch desination must start a block."); + dest->add_normal_predecessor(current_block); + } + break; + } + + case Bytecodes::_lookupswitch: + { + Bytecode_lookupswitch *lookupswitch = + Bytecode_lookupswitch_at(bytes.cur_bcp()); + + int npairs = lookupswitch->number_of_pairs(); + + dest = _block_map->at(bci + lookupswitch->default_offset()); + assert(dest != NULL, "branch desination must start a block."); + dest->add_normal_predecessor(current_block); + while(--npairs >= 0) { + LookupswitchPair *pair = lookupswitch->pair_at(npairs); + dest = _block_map->at( bci + pair->offset()); + assert(dest != NULL, "branch desination must start a block."); + dest->add_normal_predecessor(current_block); + } + break; + } + + case Bytecodes::_jsr: + { + assert(bytes.is_wide()==false, "sanity check"); + dest = _block_map->at(bytes.get_dest()); + assert(dest != NULL, "branch desination must start a block."); + dest->add_normal_predecessor(current_block); + BasicBlock *jsrExit = _block_map->at(current_block->limit_bci()); + assert(jsrExit != NULL, "jsr return bci must start a block."); + jsr_exit_list->append(jsrExit); + break; + } + case Bytecodes::_jsr_w: + { + dest = _block_map->at(bytes.get_far_dest()); + assert(dest != NULL, "branch desination must start a block."); + dest->add_normal_predecessor(current_block); + BasicBlock *jsrExit = _block_map->at(current_block->limit_bci()); + assert(jsrExit != NULL, "jsr return bci must start a block."); + jsr_exit_list->append(jsrExit); + break; + } + + case Bytecodes::_wide: + assert(false, "wide opcodes should not be seen here"); + break; + case Bytecodes::_athrow: + case Bytecodes::_ireturn: + case Bytecodes::_lreturn: + case Bytecodes::_freturn: + case Bytecodes::_dreturn: + case Bytecodes::_areturn: + case Bytecodes::_return: + // These opcodes are not the normal predecessors of any other opcodes. + break; + case Bytecodes::_ret: + // We will patch up jsr/rets in a subsequent pass. + ret_list->append(current_block); + break; + case Bytecodes::_breakpoint: + // Bail out of there are breakpoints in here. + bailout = true; + break; + default: + // Do nothing. + break; + } + } + // Patch up the jsr/ret's. We conservatively assume that any ret + // can return to any jsr site. + int ret_list_len = ret_list->length(); + int jsr_exit_list_len = jsr_exit_list->length(); + if (ret_list_len > 0 && jsr_exit_list_len > 0) { + for (int i = jsr_exit_list_len - 1; i >= 0; i--) { + BasicBlock *jsrExit = jsr_exit_list->at(i); + for (int i = ret_list_len - 1; i >= 0; i--) { + jsrExit->add_normal_predecessor(ret_list->at(i)); + } + } + } + + // Compute exception edges. + for (int b=_block_count-1; b >= 0; b--) { + BasicBlock *block = _block_list[b]; + int block_start = block->start_bci(); + int block_limit = block->limit_bci(); + ciExceptionHandlerStream handlers(method()); + for (; !handlers.is_done(); handlers.next()) { + ciExceptionHandler* handler = handlers.handler(); + int start = handler->start(); + int limit = handler->limit(); + int handler_bci = handler->handler_bci(); + + int intersect_start = MAX2(block_start, start); + int intersect_limit = MIN2(block_limit, limit); + if (intersect_start < intersect_limit) { + // The catch range has a nonempty intersection with this + // basic block. That means this basic block can be an + // exceptional predecessor. + _block_map->at(handler_bci)->add_exception_predecessor(block); + + if (handler->is_catch_all()) { + // This is a catch-all block. + if (intersect_start == block_start && intersect_limit == block_limit) { + // The basic block is entirely contained in this catch-all block. + // Skip the rest of the exception handlers -- they can never be + // reached in execution. + break; + } + } + } + } + } +} + +void MethodLiveness::init_gen_kill() { + for (int i=_block_count-1; i >= 0; i--) { + _block_list[i]->compute_gen_kill(method()); + } +} + +void MethodLiveness::propagate_liveness() { + int num_blocks = _block_count; + BasicBlock *block; + + // We start our work list off with all blocks in it. + // Alternately, we could start off the work list with the list of all + // blocks which could exit the method directly, along with one block + // from any infinite loop. If this matters, it can be changed. It + // may not be clear from looking at the code, but the order of the + // workList will be the opposite of the creation order of the basic + // blocks, which should be decent for quick convergence (with the + // possible exception of exception handlers, which are all created + // early). + _work_list = NULL; + for (int i = 0; i < num_blocks; i++) { + block = _block_list[i]; + block->set_next(_work_list); + block->set_on_work_list(true); + _work_list = block; + } + + + while ((block = work_list_get()) != NULL) { + block->propagate(this); + NOT_PRODUCT(_total_visits++;) + } +} + +void MethodLiveness::work_list_add(BasicBlock *block) { + if (!block->on_work_list()) { + block->set_next(_work_list); + block->set_on_work_list(true); + _work_list = block; + } +} + +MethodLiveness::BasicBlock *MethodLiveness::work_list_get() { + BasicBlock *block = _work_list; + if (block != NULL) { + block->set_on_work_list(false); + _work_list = block->next(); + } + return block; +} + + +MethodLivenessResult MethodLiveness::get_liveness_at(int entry_bci) { + int bci = entry_bci; + bool is_entry = false; + if (entry_bci == InvocationEntryBci) { + is_entry = true; + bci = 0; + } + + MethodLivenessResult answer(NULL,0); + + if (_block_count > 0) { + if (TimeLivenessAnalysis) _time_total.start(); + if (TimeLivenessAnalysis) _time_query.start(); + + assert( 0 <= bci && bci < method()->code_size(), "bci out of range" ); + BasicBlock *block = _block_map->at(bci); + // We may not be at the block start, so search backwards to find the block + // containing bci. + int t = bci; + while (block == NULL && t > 0) { + block = _block_map->at(--t); + } + assert( block != NULL, "invalid bytecode index; must be instruction index" ); + assert(bci >= block->start_bci() && bci < block->limit_bci(), "block must contain bci."); + + answer = block->get_liveness_at(method(), bci); + + if (is_entry && method()->is_synchronized() && !method()->is_static()) { + // Synchronized methods use the receiver once on entry. + answer.at_put(0, true); + } + +#ifndef PRODUCT + if (TraceLivenessQuery) { + tty->print("Liveness query of "); + method()->print_short_name(); + tty->print(" @ %d : result is ", bci); + answer.print_on(tty); + } + + if (TimeLivenessAnalysis) _time_query.stop(); + if (TimeLivenessAnalysis) _time_total.stop(); +#endif + } + +#ifndef PRODUCT + if (TimeLivenessAnalysis) { + // Collect statistics. + _total_locals_queried += _bit_map_size_bits; + BitCounter counter; + answer.iterate(&counter); + _total_live_locals_queried += counter.count(); + } +#endif + + return answer; +} + + +#ifndef PRODUCT + +void MethodLiveness::print_times() { + tty->print_cr ("Accumulated liveness analysis times/statistics:"); + tty->print_cr ("-----------------------------------------------"); + tty->print_cr (" Total : %3.3f sec.", _time_total.seconds()); + tty->print_cr (" Build graph : %3.3f sec. (%2.2f%%)", _time_build_graph.seconds(), + _time_build_graph.seconds() * 100 / _time_total.seconds()); + tty->print_cr (" Gen / Kill : %3.3f sec. (%2.2f%%)", _time_gen_kill.seconds(), + _time_gen_kill.seconds() * 100 / _time_total.seconds()); + tty->print_cr (" Dataflow : %3.3f sec. (%2.2f%%)", _time_flow.seconds(), + _time_flow.seconds() * 100 / _time_total.seconds()); + tty->print_cr (" Query : %3.3f sec. (%2.2f%%)", _time_query.seconds(), + _time_query.seconds() * 100 / _time_total.seconds()); + tty->print_cr (" #bytes : %8d (%3.0f bytes per sec)", + _total_bytes, + _total_bytes / _time_total.seconds()); + tty->print_cr (" #methods : %8d (%3.0f methods per sec)", + _total_methods, + _total_methods / _time_total.seconds()); + tty->print_cr (" avg locals : %3.3f max locals : %3d", + (float)_total_method_locals / _total_methods, + _max_method_locals); + tty->print_cr (" avg blocks : %3.3f max blocks : %3d", + (float)_total_blocks / _total_methods, + _max_method_blocks); + tty->print_cr (" avg bytes : %3.3f", + (float)_total_bytes / _total_methods); + tty->print_cr (" #blocks : %8d", + _total_blocks); + tty->print_cr (" avg normal predecessors : %3.3f max normal predecessors : %3d", + (float)_total_edges / _total_blocks, + _max_block_edges); + tty->print_cr (" avg exception predecessors : %3.3f max exception predecessors : %3d", + (float)_total_exc_edges / _total_blocks, + _max_block_exc_edges); + tty->print_cr (" avg visits : %3.3f", + (float)_total_visits / _total_blocks); + tty->print_cr (" #locals queried : %8d #live : %8d %%live : %2.2f%%", + _total_locals_queried, + _total_live_locals_queried, + 100.0 * _total_live_locals_queried / _total_locals_queried); +} + +#endif + + +MethodLiveness::BasicBlock::BasicBlock(MethodLiveness *analyzer, int start, int limit) : + _gen((uintptr_t*)analyzer->arena()->Amalloc(BytesPerWord * analyzer->bit_map_size_words()), + analyzer->bit_map_size_bits()), + _kill((uintptr_t*)analyzer->arena()->Amalloc(BytesPerWord * analyzer->bit_map_size_words()), + analyzer->bit_map_size_bits()), + _entry((uintptr_t*)analyzer->arena()->Amalloc(BytesPerWord * analyzer->bit_map_size_words()), + analyzer->bit_map_size_bits()), + _normal_exit((uintptr_t*)analyzer->arena()->Amalloc(BytesPerWord * analyzer->bit_map_size_words()), + analyzer->bit_map_size_bits()), + _exception_exit((uintptr_t*)analyzer->arena()->Amalloc(BytesPerWord * analyzer->bit_map_size_words()), + analyzer->bit_map_size_bits()), + _last_bci(-1) { + _analyzer = analyzer; + _start_bci = start; + _limit_bci = limit; + _normal_predecessors = + new (analyzer->arena()) GrowableArray(analyzer->arena(), 5, 0, NULL); + _exception_predecessors = + new (analyzer->arena()) GrowableArray(analyzer->arena(), 5, 0, NULL); + _normal_exit.clear(); + _exception_exit.clear(); + _entry.clear(); + + // this initialization is not strictly necessary. + // _gen and _kill are cleared at the beginning of compute_gen_kill_range() + _gen.clear(); + _kill.clear(); +} + + + +MethodLiveness::BasicBlock *MethodLiveness::BasicBlock::split(int split_bci) { + int start = _start_bci; + int limit = _limit_bci; + + if (TraceLivenessGen) { + tty->print_cr(" ** Splitting block (%d,%d) at %d", start, limit, split_bci); + } + + GrowableArray* save_predecessors = _normal_predecessors; + + assert (start < split_bci && split_bci < limit, "improper split"); + + // Make a new block to cover the first half of the range. + BasicBlock *first_half = new (_analyzer->arena()) BasicBlock(_analyzer, start, split_bci); + + // Assign correct values to the second half (this) + _normal_predecessors = first_half->_normal_predecessors; + _start_bci = split_bci; + add_normal_predecessor(first_half); + + // Assign correct predecessors to the new first half + first_half->_normal_predecessors = save_predecessors; + + return first_half; +} + +void MethodLiveness::BasicBlock::compute_gen_kill(ciMethod* method) { + ciBytecodeStream bytes(method); + bytes.reset_to_bci(start_bci()); + bytes.set_max_bci(limit_bci()); + compute_gen_kill_range(&bytes); + +} + +void MethodLiveness::BasicBlock::compute_gen_kill_range(ciBytecodeStream *bytes) { + _gen.clear(); + _kill.clear(); + + while (bytes->next() != ciBytecodeStream::EOBC()) { + compute_gen_kill_single(bytes); + } +} + +void MethodLiveness::BasicBlock::compute_gen_kill_single(ciBytecodeStream *instruction) { + int localNum; + + // We prohibit _gen and _kill from having locals in common. If we + // know that one is definitely going to be applied before the other, + // we could save some computation time by relaxing this prohibition. + + switch (instruction->cur_bc()) { + case Bytecodes::_nop: + case Bytecodes::_goto: + case Bytecodes::_goto_w: + case Bytecodes::_aconst_null: + case Bytecodes::_new: + case Bytecodes::_iconst_m1: + case Bytecodes::_iconst_0: + case Bytecodes::_iconst_1: + case Bytecodes::_iconst_2: + case Bytecodes::_iconst_3: + case Bytecodes::_iconst_4: + case Bytecodes::_iconst_5: + case Bytecodes::_fconst_0: + case Bytecodes::_fconst_1: + case Bytecodes::_fconst_2: + case Bytecodes::_bipush: + case Bytecodes::_sipush: + case Bytecodes::_lconst_0: + case Bytecodes::_lconst_1: + case Bytecodes::_dconst_0: + case Bytecodes::_dconst_1: + case Bytecodes::_ldc2_w: + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + case Bytecodes::_iaload: + case Bytecodes::_faload: + case Bytecodes::_baload: + case Bytecodes::_caload: + case Bytecodes::_saload: + case Bytecodes::_laload: + case Bytecodes::_daload: + case Bytecodes::_aaload: + case Bytecodes::_iastore: + case Bytecodes::_fastore: + case Bytecodes::_bastore: + case Bytecodes::_castore: + case Bytecodes::_sastore: + case Bytecodes::_lastore: + case Bytecodes::_dastore: + case Bytecodes::_aastore: + case Bytecodes::_pop: + case Bytecodes::_pop2: + case Bytecodes::_dup: + case Bytecodes::_dup_x1: + case Bytecodes::_dup_x2: + case Bytecodes::_dup2: + case Bytecodes::_dup2_x1: + case Bytecodes::_dup2_x2: + case Bytecodes::_swap: + case Bytecodes::_iadd: + case Bytecodes::_fadd: + case Bytecodes::_isub: + case Bytecodes::_fsub: + case Bytecodes::_imul: + case Bytecodes::_fmul: + case Bytecodes::_idiv: + case Bytecodes::_fdiv: + case Bytecodes::_irem: + case Bytecodes::_frem: + case Bytecodes::_ishl: + case Bytecodes::_ishr: + case Bytecodes::_iushr: + case Bytecodes::_iand: + case Bytecodes::_ior: + case Bytecodes::_ixor: + case Bytecodes::_l2f: + case Bytecodes::_l2i: + case Bytecodes::_d2f: + case Bytecodes::_d2i: + case Bytecodes::_fcmpl: + case Bytecodes::_fcmpg: + case Bytecodes::_ladd: + case Bytecodes::_dadd: + case Bytecodes::_lsub: + case Bytecodes::_dsub: + case Bytecodes::_lmul: + case Bytecodes::_dmul: + case Bytecodes::_ldiv: + case Bytecodes::_ddiv: + case Bytecodes::_lrem: + case Bytecodes::_drem: + case Bytecodes::_land: + case Bytecodes::_lor: + case Bytecodes::_lxor: + case Bytecodes::_ineg: + case Bytecodes::_fneg: + case Bytecodes::_i2f: + case Bytecodes::_f2i: + case Bytecodes::_i2c: + case Bytecodes::_i2s: + case Bytecodes::_i2b: + case Bytecodes::_lneg: + case Bytecodes::_dneg: + case Bytecodes::_l2d: + case Bytecodes::_d2l: + case Bytecodes::_lshl: + case Bytecodes::_lshr: + case Bytecodes::_lushr: + case Bytecodes::_i2l: + case Bytecodes::_i2d: + case Bytecodes::_f2l: + case Bytecodes::_f2d: + case Bytecodes::_lcmp: + case Bytecodes::_dcmpl: + case Bytecodes::_dcmpg: + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + case Bytecodes::_tableswitch: + case Bytecodes::_ireturn: + case Bytecodes::_freturn: + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_lreturn: + case Bytecodes::_dreturn: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_jsr: + case Bytecodes::_jsr_w: + case Bytecodes::_getstatic: + case Bytecodes::_putstatic: + case Bytecodes::_getfield: + case Bytecodes::_putfield: + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokeinterface: + case Bytecodes::_newarray: + case Bytecodes::_anewarray: + case Bytecodes::_checkcast: + case Bytecodes::_arraylength: + case Bytecodes::_instanceof: + case Bytecodes::_athrow: + case Bytecodes::_areturn: + case Bytecodes::_monitorenter: + case Bytecodes::_monitorexit: + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + case Bytecodes::_multianewarray: + case Bytecodes::_lookupswitch: + // These bytecodes have no effect on the method's locals. + break; + + case Bytecodes::_return: + if (instruction->method()->intrinsic_id() == vmIntrinsics::_Object_init) { + // return from Object.init implicitly registers a finalizer + // for the receiver if needed, so keep it alive. + load_one(0); + } + break; + + + case Bytecodes::_lload: + case Bytecodes::_dload: + load_two(instruction->get_index()); + break; + + case Bytecodes::_lload_0: + case Bytecodes::_dload_0: + load_two(0); + break; + + case Bytecodes::_lload_1: + case Bytecodes::_dload_1: + load_two(1); + break; + + case Bytecodes::_lload_2: + case Bytecodes::_dload_2: + load_two(2); + break; + + case Bytecodes::_lload_3: + case Bytecodes::_dload_3: + load_two(3); + break; + + case Bytecodes::_iload: + case Bytecodes::_iinc: + case Bytecodes::_fload: + case Bytecodes::_aload: + case Bytecodes::_ret: + load_one(instruction->get_index()); + break; + + case Bytecodes::_iload_0: + case Bytecodes::_fload_0: + case Bytecodes::_aload_0: + load_one(0); + break; + + case Bytecodes::_iload_1: + case Bytecodes::_fload_1: + case Bytecodes::_aload_1: + load_one(1); + break; + + case Bytecodes::_iload_2: + case Bytecodes::_fload_2: + case Bytecodes::_aload_2: + load_one(2); + break; + + case Bytecodes::_iload_3: + case Bytecodes::_fload_3: + case Bytecodes::_aload_3: + load_one(3); + break; + + case Bytecodes::_lstore: + case Bytecodes::_dstore: + store_two(localNum = instruction->get_index()); + break; + + case Bytecodes::_lstore_0: + case Bytecodes::_dstore_0: + store_two(0); + break; + + case Bytecodes::_lstore_1: + case Bytecodes::_dstore_1: + store_two(1); + break; + + case Bytecodes::_lstore_2: + case Bytecodes::_dstore_2: + store_two(2); + break; + + case Bytecodes::_lstore_3: + case Bytecodes::_dstore_3: + store_two(3); + break; + + case Bytecodes::_istore: + case Bytecodes::_fstore: + case Bytecodes::_astore: + store_one(instruction->get_index()); + break; + + case Bytecodes::_istore_0: + case Bytecodes::_fstore_0: + case Bytecodes::_astore_0: + store_one(0); + break; + + case Bytecodes::_istore_1: + case Bytecodes::_fstore_1: + case Bytecodes::_astore_1: + store_one(1); + break; + + case Bytecodes::_istore_2: + case Bytecodes::_fstore_2: + case Bytecodes::_astore_2: + store_one(2); + break; + + case Bytecodes::_istore_3: + case Bytecodes::_fstore_3: + case Bytecodes::_astore_3: + store_one(3); + break; + + case Bytecodes::_wide: + fatal("Iterator should skip this bytecode"); + break; + + default: + tty->print("unexpected opcode: %d\n", instruction->cur_bc()); + ShouldNotReachHere(); + break; + } +} + +void MethodLiveness::BasicBlock::load_two(int local) { + load_one(local); + load_one(local+1); +} + +void MethodLiveness::BasicBlock::load_one(int local) { + if (!_kill.at(local)) { + _gen.at_put(local, true); + } +} + +void MethodLiveness::BasicBlock::store_two(int local) { + store_one(local); + store_one(local+1); +} + +void MethodLiveness::BasicBlock::store_one(int local) { + if (!_gen.at(local)) { + _kill.at_put(local, true); + } +} + +void MethodLiveness::BasicBlock::propagate(MethodLiveness *ml) { + // These set operations could be combined for efficiency if the + // performance of this analysis becomes an issue. + _entry.set_union(_normal_exit); + _entry.set_difference(_kill); + _entry.set_union(_gen); + + // Note that we merge information from our exceptional successors + // just once, rather than at individual bytecodes. + _entry.set_union(_exception_exit); + + if (TraceLivenessGen) { + tty->print_cr(" ** Visiting block at %d **", start_bci()); + print_on(tty); + } + + int i; + for (i=_normal_predecessors->length()-1; i>=0; i--) { + BasicBlock *block = _normal_predecessors->at(i); + if (block->merge_normal(_entry)) { + ml->work_list_add(block); + } + } + for (i=_exception_predecessors->length()-1; i>=0; i--) { + BasicBlock *block = _exception_predecessors->at(i); + if (block->merge_exception(_entry)) { + ml->work_list_add(block); + } + } +} + +bool MethodLiveness::BasicBlock::merge_normal(BitMap other) { + return _normal_exit.set_union_with_result(other); +} + +bool MethodLiveness::BasicBlock::merge_exception(BitMap other) { + return _exception_exit.set_union_with_result(other); +} + +MethodLivenessResult MethodLiveness::BasicBlock::get_liveness_at(ciMethod* method, int bci) { + MethodLivenessResult answer(NEW_RESOURCE_ARRAY(uintptr_t, _analyzer->bit_map_size_words()), + _analyzer->bit_map_size_bits()); + answer.set_is_valid(); + +#ifndef ASSERT + if (bci == start_bci()) { + answer.set_from(_entry); + return answer; + } +#endif + +#ifdef ASSERT + ResourceMark rm; + BitMap g(_gen.size()); g.set_from(_gen); + BitMap k(_kill.size()); k.set_from(_kill); +#endif + if (_last_bci != bci || trueInDebug) { + ciBytecodeStream bytes(method); + bytes.reset_to_bci(bci); + bytes.set_max_bci(limit_bci()); + compute_gen_kill_range(&bytes); + assert(_last_bci != bci || + (g.is_same(_gen) && k.is_same(_kill)), "cached computation is incorrect"); + _last_bci = bci; + } + + answer.clear(); + answer.set_union(_normal_exit); + answer.set_difference(_kill); + answer.set_union(_gen); + answer.set_union(_exception_exit); + +#ifdef ASSERT + if (bci == start_bci()) { + assert(answer.is_same(_entry), "optimized answer must be accurate"); + } +#endif + + return answer; +} + +#ifndef PRODUCT + +void MethodLiveness::BasicBlock::print_on(outputStream *os) const { + os->print_cr("==================================================================="); + os->print_cr(" Block start: %4d, limit: %4d", _start_bci, _limit_bci); + os->print (" Normal predecessors (%2d) @", _normal_predecessors->length()); + int i; + for (i=0; i < _normal_predecessors->length(); i++) { + os->print(" %4d", _normal_predecessors->at(i)->start_bci()); + } + os->cr(); + os->print (" Exceptional predecessors (%2d) @", _exception_predecessors->length()); + for (i=0; i < _exception_predecessors->length(); i++) { + os->print(" %4d", _exception_predecessors->at(i)->start_bci()); + } + os->cr(); + os->print (" Normal Exit : "); + _normal_exit.print_on(os); + os->print (" Gen : "); + _gen.print_on(os); + os->print (" Kill : "); + _kill.print_on(os); + os->print (" Exception Exit: "); + _exception_exit.print_on(os); + os->print (" Entry : "); + _entry.print_on(os); +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/compiler/methodLiveness.hpp b/hotspot/src/share/vm/compiler/methodLiveness.hpp new file mode 100644 index 00000000000..a679c34d007 --- /dev/null +++ b/hotspot/src/share/vm/compiler/methodLiveness.hpp @@ -0,0 +1,271 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ciMethod; + +class MethodLivenessResult : public BitMap { + private: + bool _is_valid; + + public: + MethodLivenessResult(uintptr_t* map, idx_t size_in_bits) + : BitMap(map, size_in_bits) + , _is_valid(false) + {} + + MethodLivenessResult(idx_t size_in_bits) + : BitMap(size_in_bits) + , _is_valid(false) + {} + + void set_is_valid() { _is_valid = true; } + bool is_valid() { return _is_valid; } +}; + +class MethodLiveness : public ResourceObj { + public: + // The BasicBlock class is used to represent a basic block in the + // liveness analysis. + class BasicBlock : public ResourceObj { + private: + // This class is only used by the MethodLiveness class. + friend class MethodLiveness; + + // The analyzer which created this basic block. + MethodLiveness* _analyzer; + + // The range of this basic block is [start_bci,limit_bci) + int _start_bci; + int _limit_bci; + + // The liveness at the start of the block; + BitMap _entry; + + // The summarized liveness effects of our direct successors reached + // by normal control flow + BitMap _normal_exit; + + // The summarized liveness effects of our direct successors reached + // by exceptional control flow + BitMap _exception_exit; + + // These members hold the results of the last call to + // compute_gen_kill_range(). _gen is the set of locals + // used before they are defined in the range. _kill is the + // set of locals defined before they are used. + BitMap _gen; + BitMap _kill; + int _last_bci; + + // A list of all blocks which could come directly before this one + // in normal (non-exceptional) control flow. We propagate liveness + // information to these blocks. + GrowableArray* _normal_predecessors; + + // A list of all blocks which could come directly before this one + // in exceptional control flow. + GrowableArray* _exception_predecessors; + + // The following fields are used to manage a work list used in the + // dataflow. + BasicBlock *_next; + bool _on_work_list; + + // Our successors call this method to merge liveness information into + // our _normal_exit member. + bool merge_normal(BitMap other); + + // Our successors call this method to merge liveness information into + // our _exception_exit member. + bool merge_exception(BitMap other); + + // This helper routine is used to help compute the gen/kill pair for + // the block. It is also used to answer queries. + void compute_gen_kill_range(ciBytecodeStream *bytes); + + // Compute the gen/kill effect of a single instruction. + void compute_gen_kill_single(ciBytecodeStream *instruction); + + // Helpers for compute_gen_kill_single. + void load_one(int local); + void load_two(int local); + void store_one(int local); + void store_two(int local); + + BasicBlock(MethodLiveness *analyzer, int start, int limit); + + // -- Accessors + + int start_bci() const { return _start_bci; } + + int limit_bci() const { return _limit_bci; } + void set_limit_bci(int limit) { _limit_bci = limit; } + + BasicBlock *next() const { return _next; } + void set_next(BasicBlock *next) { _next = next; } + + bool on_work_list() const { return _on_work_list; } + void set_on_work_list(bool val) { _on_work_list = val; } + + // -- Flow graph construction. + + // Add a basic block to our list of normal predecessors. + void add_normal_predecessor(BasicBlock *pred) { + _normal_predecessors->append_if_missing(pred); + } + + // Add a basic block to our list of exceptional predecessors + void add_exception_predecessor(BasicBlock *pred) { + _exception_predecessors->append_if_missing(pred); + } + + // Split the basic block at splitBci. This basic block + // becomes the second half. The first half is newly created. + BasicBlock *split(int splitBci); + + // -- Dataflow. + + void compute_gen_kill(ciMethod* method); + + // Propagate changes from this basic block + void propagate(MethodLiveness *ml); + + // -- Query. + + MethodLivenessResult get_liveness_at(ciMethod* method, int bci); + + // -- Debugging. + + void print_on(outputStream *os) const PRODUCT_RETURN; + + }; // End of MethodLiveness::BasicBlock + + private: + // The method we are analyzing. + ciMethod* _method; + ciMethod* method() const { return _method; } + + // The arena for storing structures... + Arena* _arena; + Arena* arena() const { return _arena; } + + // We cache the length of the method. + int _code_size; + + // The size of a BitMap. + int _bit_map_size_bits; + int _bit_map_size_words; + + // A list of all BasicBlocks. + BasicBlock **_block_list; + + // number of blocks + int _block_count; + + // Keeps track of bci->block mapping. One entry for each bci. Only block starts are + // recorded. + GrowableArray* _block_map; + + // Our work list. + BasicBlock *_work_list; + +#ifdef COMPILER1 + // bcis where blocks start are marked + BitMap _bci_block_start; +#endif // COMPILER1 + + // -- Graph construction & Analysis + + // Compute ranges and predecessors for basic blocks. + void init_basic_blocks(); + + // Compute gen/kill information for all basic blocks. + void init_gen_kill(); + + // Perform the dataflow. + void propagate_liveness(); + + // The class MethodLiveness::BasicBlock needs special access to some + // of our members. + friend class MethodLiveness::BasicBlock; + + // And accessors. + int bit_map_size_bits() const { return _bit_map_size_bits; } + int bit_map_size_words() const { return _bit_map_size_words; } + + // Work list manipulation routines. Called internally by BasicBlock. + BasicBlock *work_list_get(); + void work_list_add(BasicBlock *block); + + // -- Timing and Statistics. + + + // Timers + static elapsedTimer _time_build_graph; + static elapsedTimer _time_gen_kill; + static elapsedTimer _time_flow; + static elapsedTimer _time_query; + static elapsedTimer _time_total; + +#ifndef PRODUCT + + // Counts + static long _total_bytes; + static int _total_methods; + + static long _total_blocks; + static int _max_method_blocks; + + static long _total_edges; + static int _max_block_edges; + + static long _total_exc_edges; + static int _max_block_exc_edges; + + static long _total_method_locals; + static int _max_method_locals; + + static long _total_locals_queried; + static long _total_live_locals_queried; + + static long _total_visits; + +#endif + + public: + // Create a liveness analyzer for a method + MethodLiveness(Arena* arena, ciMethod* method); + + // Compute liveness information for the method + void compute_liveness(); + + // Find out which locals are live at a specific bci. + MethodLivenessResult get_liveness_at(int bci); + +#ifdef COMPILER1 + const BitMap get_bci_block_start() const { return _bci_block_start; } +#endif // COMPILER1 + + static void print_times() PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/compiler/oopMap.cpp b/hotspot/src/share/vm/compiler/oopMap.cpp new file mode 100644 index 00000000000..bd6dc80132e --- /dev/null +++ b/hotspot/src/share/vm/compiler/oopMap.cpp @@ -0,0 +1,662 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_oopMap.cpp.incl" + +// OopMapStream + +OopMapStream::OopMapStream(OopMap* oop_map) { + if(oop_map->omv_data() == NULL) { + _stream = new CompressedReadStream(oop_map->write_stream()->buffer()); + } else { + _stream = new CompressedReadStream(oop_map->omv_data()); + } + _mask = OopMapValue::type_mask_in_place; + _size = oop_map->omv_count(); + _position = 0; + _valid_omv = false; +} + + +OopMapStream::OopMapStream(OopMap* oop_map, int oop_types_mask) { + if(oop_map->omv_data() == NULL) { + _stream = new CompressedReadStream(oop_map->write_stream()->buffer()); + } else { + _stream = new CompressedReadStream(oop_map->omv_data()); + } + _mask = oop_types_mask; + _size = oop_map->omv_count(); + _position = 0; + _valid_omv = false; +} + + +void OopMapStream::find_next() { + while(_position++ < _size) { + _omv.read_from(_stream); + if(((int)_omv.type() & _mask) > 0) { + _valid_omv = true; + return; + } + } + _valid_omv = false; +} + + +// OopMap + +// frame_size units are stack-slots (4 bytes) NOT intptr_t; we can name odd +// slots to hold 4-byte values like ints and floats in the LP64 build. +OopMap::OopMap(int frame_size, int arg_count) { + // OopMaps are usually quite so small, so pick a small initial size + set_write_stream(new CompressedWriteStream(32)); + set_omv_data(NULL); + set_omv_count(0); + +#ifdef ASSERT + _locs_length = VMRegImpl::stack2reg(0)->value() + frame_size + arg_count; + _locs_used = NEW_RESOURCE_ARRAY(OopMapValue::oop_types, _locs_length); + for(int i = 0; i < _locs_length; i++) _locs_used[i] = OopMapValue::unused_value; +#endif +} + + +OopMap::OopMap(OopMap::DeepCopyToken, OopMap* source) { + // This constructor does a deep copy + // of the source OopMap. + set_write_stream(new CompressedWriteStream(source->omv_count() * 2)); + set_omv_data(NULL); + set_omv_count(0); + set_offset(source->offset()); + +#ifdef ASSERT + _locs_length = source->_locs_length; + _locs_used = NEW_RESOURCE_ARRAY(OopMapValue::oop_types, _locs_length); + for(int i = 0; i < _locs_length; i++) _locs_used[i] = OopMapValue::unused_value; +#endif + + // We need to copy the entries too. + for (OopMapStream oms(source); !oms.is_done(); oms.next()) { + OopMapValue omv = oms.current(); + omv.write_on(write_stream()); + increment_count(); + } +} + + +OopMap* OopMap::deep_copy() { + return new OopMap(_deep_copy_token, this); +} + + +void OopMap::copy_to(address addr) { + memcpy(addr,this,sizeof(OopMap)); + memcpy(addr + sizeof(OopMap),write_stream()->buffer(),write_stream()->position()); + OopMap* new_oop = (OopMap*)addr; + new_oop->set_omv_data_size(write_stream()->position()); + new_oop->set_omv_data((unsigned char *)(addr + sizeof(OopMap))); + new_oop->set_write_stream(NULL); +} + + +int OopMap::heap_size() const { + int size = sizeof(OopMap); + int align = sizeof(void *) - 1; + if(write_stream() != NULL) { + size += write_stream()->position(); + } else { + size += omv_data_size(); + } + // Align to a reasonable ending point + size = ((size+align) & ~align); + return size; +} + +// frame_size units are stack-slots (4 bytes) NOT intptr_t; we can name odd +// slots to hold 4-byte values like ints and floats in the LP64 build. +void OopMap::set_xxx(VMReg reg, OopMapValue::oop_types x, VMReg optional) { + + assert(reg->value() < _locs_length, "too big reg value for stack size"); + assert( _locs_used[reg->value()] == OopMapValue::unused_value, "cannot insert twice" ); + debug_only( _locs_used[reg->value()] = x; ) + + OopMapValue o(reg, x); + + if(x == OopMapValue::callee_saved_value) { + // This can never be a stack location, so we don't need to transform it. + assert(optional->is_reg(), "Trying to callee save a stack location"); + o.set_content_reg(optional); + } else if(x == OopMapValue::derived_oop_value) { + o.set_content_reg(optional); + } + + o.write_on(write_stream()); + increment_count(); +} + + +void OopMap::set_oop(VMReg reg) { + set_xxx(reg, OopMapValue::oop_value, VMRegImpl::Bad()); +} + + +void OopMap::set_value(VMReg reg) { + // At this time, we only need value entries in our OopMap when ZapDeadCompiledLocals is active. + if (ZapDeadCompiledLocals) + set_xxx(reg, OopMapValue::value_value, VMRegImpl::Bad()); +} + + +void OopMap::set_dead(VMReg reg) { + // At this time, we only need dead entries in our OopMap when ZapDeadCompiledLocals is active. + if (ZapDeadCompiledLocals) { + set_xxx(reg, OopMapValue::dead_value, VMRegImpl::Bad()); + } +} + + +void OopMap::set_callee_saved(VMReg reg, VMReg caller_machine_register ) { + set_xxx(reg, OopMapValue::callee_saved_value, caller_machine_register); +} + + +void OopMap::set_derived_oop(VMReg reg, VMReg derived_from_local_register ) { + if( reg == derived_from_local_register ) { + // Actually an oop, derived shares storage with base, + set_oop(reg); + } else { + set_xxx(reg, OopMapValue::derived_oop_value, derived_from_local_register); + } +} + +void OopMap::set_stack_obj(VMReg reg) { + set_xxx(reg, OopMapValue::stack_obj, VMRegImpl::Bad()); +} + +// OopMapSet + +OopMapSet::OopMapSet() { + set_om_size(MinOopMapAllocation); + set_om_count(0); + OopMap** temp = NEW_RESOURCE_ARRAY(OopMap*, om_size()); + set_om_data(temp); +} + + +void OopMapSet::grow_om_data() { + int new_size = om_size() * 2; + OopMap** new_data = NEW_RESOURCE_ARRAY(OopMap*, new_size); + memcpy(new_data,om_data(),om_size() * sizeof(OopMap*)); + set_om_size(new_size); + set_om_data(new_data); +} + + +void OopMapSet::copy_to(address addr) { + address temp = addr; + int align = sizeof(void *) - 1; + // Copy this + memcpy(addr,this,sizeof(OopMapSet)); + temp += sizeof(OopMapSet); + temp = (address)((intptr_t)(temp + align) & ~align); + // Do the needed fixups to the new OopMapSet + OopMapSet* new_set = (OopMapSet*)addr; + new_set->set_om_data((OopMap**)temp); + // Allow enough space for the OopMap pointers + temp += (om_count() * sizeof(OopMap*)); + + for(int i=0; i < om_count(); i++) { + OopMap* map = at(i); + map->copy_to((address)temp); + new_set->set(i,(OopMap*)temp); + temp += map->heap_size(); + } + // This "locks" the OopMapSet + new_set->set_om_size(-1); +} + + +void OopMapSet::add_gc_map(int pc_offset, OopMap *map ) { + assert(om_size() != -1,"Cannot grow a fixed OopMapSet"); + + if(om_count() >= om_size()) { + grow_om_data(); + } + map->set_offset(pc_offset); + +#ifdef ASSERT + if(om_count() > 0) { + OopMap* last = at(om_count()-1); + if (last->offset() == map->offset() ) { + fatal("OopMap inserted twice"); + } + if(last->offset() > map->offset()) { + tty->print_cr( "WARNING, maps not sorted: pc[%d]=%d, pc[%d]=%d", + om_count(),last->offset(),om_count()+1,map->offset()); + } + } +#endif // ASSERT + + set(om_count(),map); + increment_count(); +} + + +int OopMapSet::heap_size() const { + // The space we use + int size = sizeof(OopMap); + int align = sizeof(void *) - 1; + size = ((size+align) & ~align); + size += om_count() * sizeof(OopMap*); + + // Now add in the space needed for the indivdiual OopMaps + for(int i=0; i < om_count(); i++) { + size += at(i)->heap_size(); + } + // We don't need to align this, it will be naturally pointer aligned + return size; +} + + +OopMap* OopMapSet::singular_oop_map() { + guarantee(om_count() == 1, "Make sure we only have a single gc point"); + return at(0); +} + + +OopMap* OopMapSet::find_map_at_offset(int pc_offset) const { + int i, len = om_count(); + assert( len > 0, "must have pointer maps" ); + + // Scan through oopmaps. Stop when current offset is either equal or greater + // than the one we are looking for. + for( i = 0; i < len; i++) { + if( at(i)->offset() >= pc_offset ) + break; + } + + assert( i < len, "oopmap not found" ); + + OopMap* m = at(i); + assert( m->offset() == pc_offset, "oopmap not found" ); + return m; +} + +class DoNothingClosure: public OopClosure { +public: void do_oop(oop* p) {} +}; +static DoNothingClosure do_nothing; + +static void add_derived_oop(oop* base, oop* derived) { +#ifndef TIERED + COMPILER1_PRESENT(ShouldNotReachHere();) +#endif // TIERED +#ifdef COMPILER2 + DerivedPointerTable::add(derived, base); +#endif // COMPILER2 +} + + +#ifndef PRODUCT +static void trace_codeblob_maps(const frame *fr, const RegisterMap *reg_map) { + // Print oopmap and regmap + tty->print_cr("------ "); + CodeBlob* cb = fr->cb(); + OopMapSet* maps = cb->oop_maps(); + OopMap* map = cb->oop_map_for_return_address(fr->pc()); + map->print(); + if( cb->is_nmethod() ) { + nmethod* nm = (nmethod*)cb; + // native wrappers have no scope data, it is implied + if (nm->is_native_method()) { + tty->print("bci: 0 (native)"); + } else { + ScopeDesc* scope = nm->scope_desc_at(fr->pc()); + tty->print("bci: %d ",scope->bci()); + } + } + tty->cr(); + fr->print_on(tty); + tty->print(" "); + cb->print_value_on(tty); tty->cr(); + reg_map->print(); + tty->print_cr("------ "); + +} +#endif // PRODUCT + +void OopMapSet::oops_do(const frame *fr, const RegisterMap* reg_map, OopClosure* f) { + // add derived oops to a table + all_do(fr, reg_map, f, add_derived_oop, &do_nothing, &do_nothing); +} + + +void OopMapSet::all_do(const frame *fr, const RegisterMap *reg_map, + OopClosure* oop_fn, void derived_oop_fn(oop*, oop*), + OopClosure* value_fn, OopClosure* dead_fn) { + CodeBlob* cb = fr->cb(); + { + assert(cb != NULL, "no codeblob"); + } + + NOT_PRODUCT(if (TraceCodeBlobStacks) trace_codeblob_maps(fr, reg_map);) + + OopMapSet* maps = cb->oop_maps(); + OopMap* map = cb->oop_map_for_return_address(fr->pc()); + assert(map != NULL, " no ptr map found"); + + // handle derived pointers first (otherwise base pointer may be + // changed before derived pointer offset has been collected) + OopMapValue omv; + { + OopMapStream oms(map,OopMapValue::derived_oop_value); + if (!oms.is_done()) { +#ifndef TIERED + COMPILER1_PRESENT(ShouldNotReachHere();) +#endif // !TIERED + // Protect the operation on the derived pointers. This + // protects the addition of derived pointers to the shared + // derived pointer table in DerivedPointerTable::add(). + MutexLockerEx x(DerivedPointerTableGC_lock, Mutex::_no_safepoint_check_flag); + do { + omv = oms.current(); + oop* loc = fr->oopmapreg_to_location(omv.reg(),reg_map); + if ( loc != NULL ) { + oop *base_loc = fr->oopmapreg_to_location(omv.content_reg(), reg_map); + oop *derived_loc = loc; + derived_oop_fn(base_loc, derived_loc); + } + oms.next(); + } while (!oms.is_done()); + } + } + + // We want dead, value and oop oop_types + int mask = OopMapValue::oop_value | OopMapValue::value_value | OopMapValue::dead_value; + { + for (OopMapStream oms(map,mask); !oms.is_done(); oms.next()) { + omv = oms.current(); + oop* loc = fr->oopmapreg_to_location(omv.reg(),reg_map); + if ( loc != NULL ) { + if ( omv.type() == OopMapValue::oop_value ) { +#ifdef ASSERT + if (COMPILER2_PRESENT(!DoEscapeAnalysis &&) !Universe::heap()->is_in_or_null(*loc)) { + tty->print_cr("# Found non oop pointer. Dumping state at failure"); + // try to dump out some helpful debugging information + trace_codeblob_maps(fr, reg_map); + omv.print(); + tty->print_cr("loc = %p *loc = %p\n", loc, (address)*loc); + // do the real assert. + assert(Universe::heap()->is_in_or_null(*loc), "found non oop pointer"); + } +#endif // ASSERT + oop_fn->do_oop(loc); + } else if ( omv.type() == OopMapValue::value_value ) { + value_fn->do_oop(loc); + } else if ( omv.type() == OopMapValue::dead_value ) { + dead_fn->do_oop(loc); + } + } + } + } + +#ifdef COMPILER2 + if (DoEscapeAnalysis) { + for (OopMapStream oms(map, OopMapValue::stack_obj); !oms.is_done(); oms.next()) { + omv = oms.current(); + assert(omv.is_stack_loc(), "should refer to stack location"); + oop loc = (oop) fr->oopmapreg_to_location(omv.reg(),reg_map); + oop_fn->do_oop(&loc); + } + } +#endif // COMPILER2 +} + + +// Update callee-saved register info for the following frame +void OopMapSet::update_register_map(const frame *fr, RegisterMap *reg_map) { + ResourceMark rm; + CodeBlob* cb = fr->cb(); + assert(cb != NULL, "no codeblob"); + + // Any reg might be saved by a safepoint handler (see generate_handler_blob). + const int max_saved_on_entry_reg_count = ConcreteRegisterImpl::number_of_registers; + assert( reg_map->_update_for_id == NULL || fr->is_older(reg_map->_update_for_id), + "already updated this map; do not 'update' it twice!" ); + debug_only(reg_map->_update_for_id = fr->id()); + + // Check if caller must update oop argument + assert((reg_map->include_argument_oops() || + !cb->caller_must_gc_arguments(reg_map->thread())), + "include_argument_oops should already be set"); + + int nof_callee = 0; + oop* locs[2*max_saved_on_entry_reg_count+1]; + VMReg regs[2*max_saved_on_entry_reg_count+1]; + // ("+1" because max_saved_on_entry_reg_count might be zero) + + // Scan through oopmap and find location of all callee-saved registers + // (we do not do update in place, since info could be overwritten) + + address pc = fr->pc(); + + OopMap* map = cb->oop_map_for_return_address(pc); + + assert(map != NULL, " no ptr map found"); + + OopMapValue omv; + for(OopMapStream oms(map,OopMapValue::callee_saved_value); !oms.is_done(); oms.next()) { + omv = oms.current(); + assert(nof_callee < 2*max_saved_on_entry_reg_count, "overflow"); + regs[nof_callee] = omv.content_reg(); + locs[nof_callee] = fr->oopmapreg_to_location(omv.reg(),reg_map); + nof_callee++; + } + + // Check that runtime stubs save all callee-saved registers +#ifdef COMPILER2 + assert(cb->is_compiled_by_c1() || !cb->is_runtime_stub() || + (nof_callee >= SAVED_ON_ENTRY_REG_COUNT || nof_callee >= C_SAVED_ON_ENTRY_REG_COUNT), + "must save all"); +#endif // COMPILER2 + + // Copy found callee-saved register to reg_map + for(int i = 0; i < nof_callee; i++) { + reg_map->set_location(regs[i], (address)locs[i]); + } +} + +//============================================================================= +// Non-Product code + +#ifndef PRODUCT + +bool OopMap::has_derived_pointer() const { +#ifndef TIERED + COMPILER1_PRESENT(return false); +#endif // !TIERED +#ifdef COMPILER2 + OopMapStream oms((OopMap*)this,OopMapValue::derived_oop_value); + return oms.is_done(); +#else + return false; +#endif // COMPILER2 +} + + +void print_register_type(OopMapValue::oop_types x, VMReg optional) { + switch( x ) { + case OopMapValue::oop_value: + tty->print("Oop"); + break; + case OopMapValue::value_value: + tty->print("Value" ); + break; + case OopMapValue::dead_value: + tty->print("Dead" ); + break; + case OopMapValue::callee_saved_value: + tty->print("Callers_" ); + optional->print(); + break; + case OopMapValue::derived_oop_value: + tty->print("Derived_oop_" ); + optional->print(); + break; + case OopMapValue::stack_obj: + tty->print("Stack"); + break; + default: + ShouldNotReachHere(); + } +} + + +void OopMapValue::print() const { + reg()->print(); + tty->print("="); + print_register_type(type(),content_reg()); + tty->print(" "); +} + + +void OopMap::print_on(outputStream* st) const { + OopMapValue omv; + for(OopMapStream oms((OopMap*)this); !oms.is_done(); oms.next()) { + omv = oms.current(); + omv.print_on(st); + } +} + + +void OopMapSet::print_on(outputStream* st) const { + int i, len = om_count(); + + st->print_cr("OopMapSet contains %d OopMaps\n",len); + + for( i = 0; i < len; i++) { + OopMap* m = at(i); + st->print_cr("OopMap #%d offset:%p",i,m->offset()); + m->print_on(st); + st->print_cr("\n"); + } +} +#endif // !PRODUCT + + +//------------------------------DerivedPointerTable--------------------------- + +#ifdef COMPILER2 + +class DerivedPointerEntry : public CHeapObj { + private: + oop* _location; // Location of derived pointer (also pointing to the base) + intptr_t _offset; // Offset from base pointer + public: + DerivedPointerEntry(oop* location, intptr_t offset) { _location = location; _offset = offset; } + oop* location() { return _location; } + intptr_t offset() { return _offset; } +}; + + +GrowableArray* DerivedPointerTable::_list = NULL; +bool DerivedPointerTable::_active = false; + + +void DerivedPointerTable::clear() { + // The first time, we create the list. Otherwise it should be + // empty. If not, then we have probably forgotton to call + // update_pointers after last GC/Scavenge. + assert (!_active, "should not be active"); + assert(_list == NULL || _list->length() == 0, "table not empty"); + if (_list == NULL) { + _list = new (ResourceObj::C_HEAP) GrowableArray(10, true); // Allocated on C heap + } + _active = true; +} + + +// Returns value of location as an int +intptr_t value_of_loc(oop *pointer) { return (intptr_t)(*pointer); } + + +void DerivedPointerTable::add(oop *derived_loc, oop *base_loc) { + assert(Universe::heap()->is_in_or_null(*base_loc), "not an oop"); + assert(derived_loc != base_loc, "Base and derived in same location"); + if (_active) { + assert(*derived_loc != (oop)base_loc, "location already added"); + assert(_list != NULL, "list must exist"); + intptr_t offset = value_of_loc(derived_loc) - value_of_loc(base_loc); + assert(offset >= -1000000, "wrong derived pointer info"); + + if (TraceDerivedPointers) { + tty->print_cr( + "Add derived pointer@" INTPTR_FORMAT + " - Derived: " INTPTR_FORMAT + " Base: " INTPTR_FORMAT " (@" INTPTR_FORMAT ") (Offset: %d)", + derived_loc, (address)*derived_loc, (address)*base_loc, base_loc, offset + ); + } + // Set derived oop location to point to base. + *derived_loc = (oop)base_loc; + assert_lock_strong(DerivedPointerTableGC_lock); + DerivedPointerEntry *entry = new DerivedPointerEntry(derived_loc, offset); + _list->append(entry); + } +} + + +void DerivedPointerTable::update_pointers() { + assert(_list != NULL, "list must exist"); + for(int i = 0; i < _list->length(); i++) { + DerivedPointerEntry* entry = _list->at(i); + oop* derived_loc = entry->location(); + intptr_t offset = entry->offset(); + // The derived oop was setup to point to location of base + oop base = **(oop**)derived_loc; + assert(Universe::heap()->is_in_or_null(base), "must be an oop"); + + *derived_loc = (oop)(((address)base) + offset); + assert(value_of_loc(derived_loc) - value_of_loc(&base) == offset, "sanity check"); + + if (TraceDerivedPointers) { + tty->print_cr("Updating derived pointer@" INTPTR_FORMAT + " - Derived: " INTPTR_FORMAT " Base: " INTPTR_FORMAT " (Offset: %d)", + derived_loc, (address)*derived_loc, (address)base, offset); + } + + // Delete entry + delete entry; + _list->at_put(i, NULL); + } + // Clear list, so it is ready for next traversal (this is an invariant) + if (TraceDerivedPointers && !_list->is_empty()) { + tty->print_cr("--------------------------"); + } + _list->clear(); + _active = false; +} + +#endif // COMPILER2 diff --git a/hotspot/src/share/vm/compiler/oopMap.hpp b/hotspot/src/share/vm/compiler/oopMap.hpp new file mode 100644 index 00000000000..dec8afe5789 --- /dev/null +++ b/hotspot/src/share/vm/compiler/oopMap.hpp @@ -0,0 +1,315 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Interface for generating the frame map for compiled code. A frame map +// describes for a specific pc whether each register and frame stack slot is: +// Oop - A GC root for current frame +// Value - Live non-oop, non-float value: int, either half of double +// Dead - Dead; can be Zapped for debugging +// CalleeXX - Callee saved; also describes which caller register is saved +// DerivedXX - A derived oop; original oop is described. +// +// OopMapValue describes a single OopMap entry + +class frame; +class RegisterMap; +class DerivedPointerEntry; + +class OopMapValue: public StackObj { + friend class VMStructs; +private: + short _value; + int value() const { return _value; } + void set_value(int value) { _value = value; } + short _content_reg; + +public: + // Constants + enum { type_bits = 6, + register_bits = BitsPerShort - type_bits }; + + enum { type_shift = 0, + register_shift = type_bits }; + + enum { type_mask = right_n_bits(type_bits), + type_mask_in_place = type_mask << type_shift, + register_mask = right_n_bits(register_bits), + register_mask_in_place = register_mask << register_shift }; + + enum oop_types { // must fit in type_bits + unused_value =0, // powers of 2, for masking OopMapStream + oop_value = 1, + value_value = 2, + dead_value = 4, + callee_saved_value = 8, + derived_oop_value= 16, + stack_obj = 32 }; + + // Constructors + OopMapValue () { set_value(0); set_content_reg(VMRegImpl::Bad()); } + OopMapValue (VMReg reg, oop_types t) { set_reg_type(reg,t); } + OopMapValue (VMReg reg, oop_types t, VMReg reg2) { set_reg_type(reg,t); set_content_reg(reg2); } + OopMapValue (CompressedReadStream* stream) { read_from(stream); } + + // Archiving + void write_on(CompressedWriteStream* stream) { + stream->write_int(value()); + if(is_callee_saved() || is_derived_oop()) { + stream->write_int(content_reg()->value()); + } + } + + void read_from(CompressedReadStream* stream) { + set_value(stream->read_int()); + if(is_callee_saved() || is_derived_oop()) { + set_content_reg(VMRegImpl::as_VMReg(stream->read_int(), true)); + } + } + + // Querying + bool is_oop() { return mask_bits(value(), type_mask_in_place) == oop_value; } + bool is_value() { return mask_bits(value(), type_mask_in_place) == value_value; } + bool is_dead() { return mask_bits(value(), type_mask_in_place) == dead_value; } + bool is_callee_saved() { return mask_bits(value(), type_mask_in_place) == callee_saved_value; } + bool is_derived_oop() { return mask_bits(value(), type_mask_in_place) == derived_oop_value; } + bool is_stack_obj() { return mask_bits(value(), type_mask_in_place) == stack_obj; } + + void set_oop() { set_value((value() & register_mask_in_place) | oop_value); } + void set_value() { set_value((value() & register_mask_in_place) | value_value); } + void set_dead() { set_value((value() & register_mask_in_place) | dead_value); } + void set_callee_saved() { set_value((value() & register_mask_in_place) | callee_saved_value); } + void set_derived_oop() { set_value((value() & register_mask_in_place) | derived_oop_value); } + void set_stack_obj() { set_value((value() & register_mask_in_place) | stack_obj); } + + VMReg reg() const { return VMRegImpl::as_VMReg(mask_bits(value(), register_mask_in_place) >> register_shift); } + oop_types type() const { return (oop_types)mask_bits(value(), type_mask_in_place); } + + static bool legal_vm_reg_name(VMReg p) { + return (p->value() == (p->value() & register_mask)); + } + + void set_reg_type(VMReg p, oop_types t) { + set_value((p->value() << register_shift) | t); + assert(reg() == p, "sanity check" ); + assert(type() == t, "sanity check" ); + } + + + VMReg content_reg() const { return VMRegImpl::as_VMReg(_content_reg, true); } + void set_content_reg(VMReg r) { _content_reg = r->value(); } + + // Physical location queries + bool is_register_loc() { return reg()->is_reg(); } + bool is_stack_loc() { return reg()->is_stack(); } + + // Returns offset from sp. + int stack_offset() { + assert(is_stack_loc(), "must be stack location"); + return reg()->reg2stack(); + } + + void print( ) const PRODUCT_RETURN; +}; + + +class OopMap: public ResourceObj { + friend class OopMapStream; + friend class VMStructs; + private: + int _pc_offset; + int _omv_count; + int _omv_data_size; + unsigned char* _omv_data; + CompressedWriteStream* _write_stream; + + debug_only( OopMapValue::oop_types* _locs_used; int _locs_length;) + + // Accessors + unsigned char* omv_data() const { return _omv_data; } + void set_omv_data(unsigned char* value) { _omv_data = value; } + int omv_data_size() const { return _omv_data_size; } + void set_omv_data_size(int value) { _omv_data_size = value; } + int omv_count() const { return _omv_count; } + void set_omv_count(int value) { _omv_count = value; } + void increment_count() { _omv_count++; } + CompressedWriteStream* write_stream() const { return _write_stream; } + void set_write_stream(CompressedWriteStream* value) { _write_stream = value; } + + private: + enum DeepCopyToken { _deep_copy_token }; + OopMap(DeepCopyToken, OopMap* source); // used only by deep_copy + + public: + OopMap(int frame_size, int arg_count); + + // pc-offset handling + int offset() const { return _pc_offset; } + void set_offset(int o) { _pc_offset = o; } + + // Check to avoid double insertion + debug_only(OopMapValue::oop_types locs_used( int indx ) { return _locs_used[indx]; }) + + // Construction + // frame_size units are stack-slots (4 bytes) NOT intptr_t; we can name odd + // slots to hold 4-byte values like ints and floats in the LP64 build. + void set_oop ( VMReg local); + void set_value( VMReg local); + void set_dead ( VMReg local); + void set_callee_saved( VMReg local, VMReg caller_machine_register ); + void set_derived_oop ( VMReg local, VMReg derived_from_local_register ); + void set_stack_obj( VMReg local); + void set_xxx(VMReg reg, OopMapValue::oop_types x, VMReg optional); + + int heap_size() const; + void copy_to(address addr); + OopMap* deep_copy(); + + bool has_derived_pointer() const PRODUCT_RETURN0; + + bool legal_vm_reg_name(VMReg local) { + return OopMapValue::legal_vm_reg_name(local); + } + + // Printing + void print_on(outputStream* st) const PRODUCT_RETURN; + void print() const { print_on(tty); } +}; + + +class OopMapSet : public ResourceObj { + friend class VMStructs; + private: + int _om_count; + int _om_size; + OopMap** _om_data; + + int om_count() const { return _om_count; } + void set_om_count(int value) { _om_count = value; } + void increment_count() { _om_count++; } + int om_size() const { return _om_size; } + void set_om_size(int value) { _om_size = value; } + OopMap** om_data() const { return _om_data; } + void set_om_data(OopMap** value) { _om_data = value; } + void grow_om_data(); + void set(int index,OopMap* value) { assert((index == 0) || ((index > 0) && (index < om_size())),"bad index"); _om_data[index] = value; } + + public: + OopMapSet(); + + // returns the number of OopMaps in this OopMapSet + int size() const { return _om_count; } + // returns the OopMap at a given index + OopMap* at(int index) const { assert((index >= 0) && (index <= om_count()),"bad index"); return _om_data[index]; } + + // Collect OopMaps. + void add_gc_map(int pc, OopMap* map); + + // Returns the only oop map. Used for reconstructing + // Adapter frames during deoptimization + OopMap* singular_oop_map(); + + // returns OopMap in that is anchored to the pc + OopMap* find_map_at_offset(int pc_offset) const; + + int heap_size() const; + void copy_to(address addr); + + // Iterates through frame for a compiled method + static void oops_do (const frame* fr, + const RegisterMap* reg_map, OopClosure* f); + static void update_register_map(const frame* fr, RegisterMap *reg_map); + + // Iterates through frame for a compiled method for dead ones and values, too + static void all_do(const frame* fr, const RegisterMap* reg_map, + OopClosure* oop_fn, + void derived_oop_fn(oop* base, oop* derived), + OopClosure* value_fn, OopClosure* dead_fn); + + // Printing + void print_on(outputStream* st) const PRODUCT_RETURN; + void print() const { print_on(tty); } +}; + + +class OopMapStream : public StackObj { + private: + CompressedReadStream* _stream; + int _mask; + int _size; + int _position; + bool _valid_omv; + OopMapValue _omv; + void find_next(); + + public: + OopMapStream(OopMap* oop_map); + OopMapStream(OopMap* oop_map, int oop_types_mask); + bool is_done() { if(!_valid_omv) { find_next(); } return !_valid_omv; } + void next() { find_next(); } + OopMapValue current() { return _omv; } +}; + + +// Derived pointer support. This table keeps track of all derived points on a +// stack. It is cleared before each scavenge/GC. During the traversal of all +// oops, it is filled in with references to all locations that contains a +// derived oop (assumed to be very few). When the GC is complete, the derived +// pointers are updated based on their base pointers new value and an offset. +#ifdef COMPILER2 +class DerivedPointerTable : public AllStatic { + friend class VMStructs; + private: + static GrowableArray* _list; + static bool _active; // do not record pointers for verify pass etc. + public: + static void clear(); // Called before scavenge/GC + static void add(oop *derived, oop *base); // Called during scavenge/GC + static void update_pointers(); // Called after scavenge/GC + static bool is_empty() { return _list == NULL || _list->is_empty(); } + static bool is_active() { return _active; } + static void set_active(bool value) { _active = value; } +}; + +// A utility class to temporarily "deactivate" the DerivedPointerTable. +// (Note: clients are responsible for any MT-safety issues) +class DerivedPointerTableDeactivate: public StackObj { + private: + bool _active; + public: + DerivedPointerTableDeactivate() { + _active = DerivedPointerTable::is_active(); + if (_active) { + DerivedPointerTable::set_active(false); + } + } + + ~DerivedPointerTableDeactivate() { + assert(!DerivedPointerTable::is_active(), + "Inconsistency: not MT-safe"); + if (_active) { + DerivedPointerTable::set_active(true); + } + } +}; +#endif // COMPILER2 diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.cpp new file mode 100644 index 00000000000..09e0b282b06 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.cpp @@ -0,0 +1,1210 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_binaryTreeDictionary.cpp.incl" + +//////////////////////////////////////////////////////////////////////////////// +// A binary tree based search structure for free blocks. +// This is currently used in the Concurrent Mark&Sweep implementation. +//////////////////////////////////////////////////////////////////////////////// + +TreeChunk* TreeChunk::as_TreeChunk(FreeChunk* fc) { + // Do some assertion checking here. + return (TreeChunk*) fc; +} + +void TreeChunk::verifyTreeChunkList() const { + TreeChunk* nextTC = (TreeChunk*)next(); + if (prev() != NULL) { // interior list node shouldn'r have tree fields + guarantee(embedded_list()->parent() == NULL && embedded_list()->left() == NULL && + embedded_list()->right() == NULL, "should be clear"); + } + if (nextTC != NULL) { + guarantee(as_TreeChunk(nextTC->prev()) == this, "broken chain"); + guarantee(nextTC->size() == size(), "wrong size"); + nextTC->verifyTreeChunkList(); + } +} + + +TreeList* TreeList::as_TreeList(TreeChunk* tc) { + // This first free chunk in the list will be the tree list. + assert(tc->size() >= sizeof(TreeChunk), "Chunk is too small for a TreeChunk"); + TreeList* tl = tc->embedded_list(); + tc->set_list(tl); +#ifdef ASSERT + tl->set_protecting_lock(NULL); +#endif + tl->set_hint(0); + tl->set_size(tc->size()); + tl->link_head(tc); + tl->link_tail(tc); + tl->set_count(1); + tl->init_statistics(); + tl->setParent(NULL); + tl->setLeft(NULL); + tl->setRight(NULL); + return tl; +} +TreeList* TreeList::as_TreeList(HeapWord* addr, size_t size) { + TreeChunk* tc = (TreeChunk*) addr; + assert(size >= sizeof(TreeChunk), "Chunk is too small for a TreeChunk"); + assert(tc->size() == 0 && tc->prev() == NULL && tc->next() == NULL, + "Space should be clear"); + tc->setSize(size); + tc->linkPrev(NULL); + tc->linkNext(NULL); + TreeList* tl = TreeList::as_TreeList(tc); + return tl; +} + +TreeList* TreeList::removeChunkReplaceIfNeeded(TreeChunk* tc) { + + TreeList* retTL = this; + FreeChunk* list = head(); + assert(!list || list != list->next(), "Chunk on list twice"); + assert(tc != NULL, "Chunk being removed is NULL"); + assert(parent() == NULL || this == parent()->left() || + this == parent()->right(), "list is inconsistent"); + assert(tc->isFree(), "Header is not marked correctly"); + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + + FreeChunk* prevFC = tc->prev(); + TreeChunk* nextTC = TreeChunk::as_TreeChunk(tc->next()); + assert(list != NULL, "should have at least the target chunk"); + + // Is this the first item on the list? + if (tc == list) { + // The "getChunk..." functions for a TreeList will not return the + // first chunk in the list unless it is the last chunk in the list + // because the first chunk is also acting as the tree node. + // When coalescing happens, however, the first chunk in the a tree + // list can be the start of a free range. Free ranges are removed + // from the free lists so that they are not available to be + // allocated when the sweeper yields (giving up the free list lock) + // to allow mutator activity. If this chunk is the first in the + // list and is not the last in the list, do the work to copy the + // TreeList from the first chunk to the next chunk and update all + // the TreeList pointers in the chunks in the list. + if (nextTC == NULL) { + assert(prevFC == NULL, "Not last chunk in the list") + set_tail(NULL); + set_head(NULL); + } else { + // copy embedded list. + nextTC->set_embedded_list(tc->embedded_list()); + retTL = nextTC->embedded_list(); + // Fix the pointer to the list in each chunk in the list. + // This can be slow for a long list. Consider having + // an option that does not allow the first chunk on the + // list to be coalesced. + for (TreeChunk* curTC = nextTC; curTC != NULL; + curTC = TreeChunk::as_TreeChunk(curTC->next())) { + curTC->set_list(retTL); + } + // Fix the parent to point to the new TreeList. + if (retTL->parent() != NULL) { + if (this == retTL->parent()->left()) { + retTL->parent()->setLeft(retTL); + } else { + assert(this == retTL->parent()->right(), "Parent is incorrect"); + retTL->parent()->setRight(retTL); + } + } + // Fix the children's parent pointers to point to the + // new list. + assert(right() == retTL->right(), "Should have been copied"); + if (retTL->right() != NULL) { + retTL->right()->setParent(retTL); + } + assert(left() == retTL->left(), "Should have been copied"); + if (retTL->left() != NULL) { + retTL->left()->setParent(retTL); + } + retTL->link_head(nextTC); + assert(nextTC->isFree(), "Should be a free chunk"); + } + } else { + if (nextTC == NULL) { + // Removing chunk at tail of list + link_tail(prevFC); + } + // Chunk is interior to the list + prevFC->linkAfter(nextTC); + } + + // Below this point the embeded TreeList being used for the + // tree node may have changed. Don't use "this" + // TreeList*. + // chunk should still be a free chunk (bit set in _prev) + assert(!retTL->head() || retTL->size() == retTL->head()->size(), + "Wrong sized chunk in list"); + debug_only( + tc->linkPrev(NULL); + tc->linkNext(NULL); + tc->set_list(NULL); + bool prev_found = false; + bool next_found = false; + for (FreeChunk* curFC = retTL->head(); + curFC != NULL; curFC = curFC->next()) { + assert(curFC != tc, "Chunk is still in list"); + if (curFC == prevFC) { + prev_found = true; + } + if (curFC == nextTC) { + next_found = true; + } + } + assert(prevFC == NULL || prev_found, "Chunk was lost from list"); + assert(nextTC == NULL || next_found, "Chunk was lost from list"); + assert(retTL->parent() == NULL || + retTL == retTL->parent()->left() || + retTL == retTL->parent()->right(), + "list is inconsistent"); + ) + retTL->decrement_count(); + + assert(tc->isFree(), "Should still be a free chunk"); + assert(retTL->head() == NULL || retTL->head()->prev() == NULL, + "list invariant"); + assert(retTL->tail() == NULL || retTL->tail()->next() == NULL, + "list invariant"); + return retTL; +} +void TreeList::returnChunkAtTail(TreeChunk* chunk) { + assert(chunk != NULL, "returning NULL chunk"); + assert(chunk->list() == this, "list should be set for chunk"); + assert(tail() != NULL, "The tree list is embedded in the first chunk"); + // which means that the list can never be empty. + assert(!verifyChunkInFreeLists(chunk), "Double entry"); + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + + FreeChunk* fc = tail(); + fc->linkAfter(chunk); + link_tail(chunk); + + assert(!tail() || size() == tail()->size(), "Wrong sized chunk in list"); + increment_count(); + debug_only(increment_returnedBytes_by(chunk->size()*sizeof(HeapWord));) + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); +} + +// Add this chunk at the head of the list. "At the head of the list" +// is defined to be after the chunk pointer to by head(). This is +// because the TreeList is embedded in the first TreeChunk in the +// list. See the definition of TreeChunk. +void TreeList::returnChunkAtHead(TreeChunk* chunk) { + assert(chunk->list() == this, "list should be set for chunk"); + assert(head() != NULL, "The tree list is embedded in the first chunk"); + assert(chunk != NULL, "returning NULL chunk"); + assert(!verifyChunkInFreeLists(chunk), "Double entry"); + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + + FreeChunk* fc = head()->next(); + if (fc != NULL) { + chunk->linkAfter(fc); + } else { + assert(tail() == NULL, "List is inconsistent"); + link_tail(chunk); + } + head()->linkAfter(chunk); + assert(!head() || size() == head()->size(), "Wrong sized chunk in list"); + increment_count(); + debug_only(increment_returnedBytes_by(chunk->size()*sizeof(HeapWord));) + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); +} + +TreeChunk* TreeList::head_as_TreeChunk() { + assert(head() == NULL || TreeChunk::as_TreeChunk(head())->list() == this, + "Wrong type of chunk?"); + return TreeChunk::as_TreeChunk(head()); +} + +TreeChunk* TreeList::first_available() { + guarantee(head() != NULL, "The head of the list cannot be NULL"); + FreeChunk* fc = head()->next(); + TreeChunk* retTC; + if (fc == NULL) { + retTC = head_as_TreeChunk(); + } else { + retTC = TreeChunk::as_TreeChunk(fc); + } + assert(retTC->list() == this, "Wrong type of chunk."); + return retTC; +} + +BinaryTreeDictionary::BinaryTreeDictionary(MemRegion mr, bool splay): + _splay(splay) +{ + assert(mr.byte_size() > MIN_TREE_CHUNK_SIZE, "minimum chunk size"); + + reset(mr); + assert(root()->left() == NULL, "reset check failed"); + assert(root()->right() == NULL, "reset check failed"); + assert(root()->head()->next() == NULL, "reset check failed"); + assert(root()->head()->prev() == NULL, "reset check failed"); + assert(totalSize() == root()->size(), "reset check failed"); + assert(totalFreeBlocks() == 1, "reset check failed"); +} + +void BinaryTreeDictionary::inc_totalSize(size_t inc) { + _totalSize = _totalSize + inc; +} + +void BinaryTreeDictionary::dec_totalSize(size_t dec) { + _totalSize = _totalSize - dec; +} + +void BinaryTreeDictionary::reset(MemRegion mr) { + assert(mr.byte_size() > MIN_TREE_CHUNK_SIZE, "minimum chunk size"); + set_root(TreeList::as_TreeList(mr.start(), mr.word_size())); + set_totalSize(mr.word_size()); + set_totalFreeBlocks(1); +} + +void BinaryTreeDictionary::reset(HeapWord* addr, size_t byte_size) { + MemRegion mr(addr, heap_word_size(byte_size)); + reset(mr); +} + +void BinaryTreeDictionary::reset() { + set_root(NULL); + set_totalSize(0); + set_totalFreeBlocks(0); +} + +// Get a free block of size at least size from tree, or NULL. +// If a splay step is requested, the removal algorithm (only) incorporates +// a splay step as follows: +// . the search proceeds down the tree looking for a possible +// match. At the (closest) matching location, an appropriate splay step is applied +// (zig, zig-zig or zig-zag). A chunk of the appropriate size is then returned +// if available, and if it's the last chunk, the node is deleted. A deteleted +// node is replaced in place by its tree successor. +TreeChunk* +BinaryTreeDictionary::getChunkFromTree(size_t size, Dither dither, bool splay) +{ + TreeList *curTL, *prevTL; + TreeChunk* retTC = NULL; + assert(size >= MIN_TREE_CHUNK_SIZE, "minimum chunk size"); + if (FLSVerifyDictionary) { + verifyTree(); + } + // starting at the root, work downwards trying to find match. + // Remember the last node of size too great or too small. + for (prevTL = curTL = root(); curTL != NULL;) { + if (curTL->size() == size) { // exact match + break; + } + prevTL = curTL; + if (curTL->size() < size) { // proceed to right sub-tree + curTL = curTL->right(); + } else { // proceed to left sub-tree + assert(curTL->size() > size, "size inconsistency"); + curTL = curTL->left(); + } + } + if (curTL == NULL) { // couldn't find exact match + // try and find the next larger size by walking back up the search path + for (curTL = prevTL; curTL != NULL;) { + if (curTL->size() >= size) break; + else curTL = curTL->parent(); + } + assert(curTL == NULL || curTL->count() > 0, + "An empty list should not be in the tree"); + } + if (curTL != NULL) { + assert(curTL->size() >= size, "size inconsistency"); + if (UseCMSAdaptiveFreeLists) { + + // A candidate chunk has been found. If it is already under + // populated, get a chunk associated with the hint for this + // chunk. + if (curTL->surplus() <= 0) { + /* Use the hint to find a size with a surplus, and reset the hint. */ + TreeList* hintTL = curTL; + while (hintTL->hint() != 0) { + assert(hintTL->hint() == 0 || hintTL->hint() > hintTL->size(), + "hint points in the wrong direction"); + hintTL = findList(hintTL->hint()); + assert(curTL != hintTL, "Infinite loop"); + if (hintTL == NULL || + hintTL == curTL /* Should not happen but protect against it */ ) { + // No useful hint. Set the hint to NULL and go on. + curTL->set_hint(0); + break; + } + assert(hintTL->size() > size, "hint is inconsistent"); + if (hintTL->surplus() > 0) { + // The hint led to a list that has a surplus. Use it. + // Set the hint for the candidate to an overpopulated + // size. + curTL->set_hint(hintTL->size()); + // Change the candidate. + curTL = hintTL; + break; + } + // The evm code reset the hint of the candidate as + // at an interrim point. Why? Seems like this leaves + // the hint pointing to a list that didn't work. + // curTL->set_hint(hintTL->size()); + } + } + } + // don't waste time splaying if chunk's singleton + if (splay && curTL->head()->next() != NULL) { + semiSplayStep(curTL); + } + retTC = curTL->first_available(); + assert((retTC != NULL) && (curTL->count() > 0), + "A list in the binary tree should not be NULL"); + assert(retTC->size() >= size, + "A chunk of the wrong size was found"); + removeChunkFromTree(retTC); + assert(retTC->isFree(), "Header is not marked correctly"); + } + + if (FLSVerifyDictionary) { + verify(); + } + return retTC; +} + +TreeList* BinaryTreeDictionary::findList(size_t size) const { + TreeList* curTL; + for (curTL = root(); curTL != NULL;) { + if (curTL->size() == size) { // exact match + break; + } + + if (curTL->size() < size) { // proceed to right sub-tree + curTL = curTL->right(); + } else { // proceed to left sub-tree + assert(curTL->size() > size, "size inconsistency"); + curTL = curTL->left(); + } + } + return curTL; +} + + +bool BinaryTreeDictionary::verifyChunkInFreeLists(FreeChunk* tc) const { + size_t size = tc->size(); + TreeList* tl = findList(size); + if (tl == NULL) { + return false; + } else { + return tl->verifyChunkInFreeLists(tc); + } +} + +FreeChunk* BinaryTreeDictionary::findLargestDict() const { + TreeList *curTL = root(); + if (curTL != NULL) { + while(curTL->right() != NULL) curTL = curTL->right(); + return curTL->first_available(); + } else { + return NULL; + } +} + +// Remove the current chunk from the tree. If it is not the last +// chunk in a list on a tree node, just unlink it. +// If it is the last chunk in the list (the next link is NULL), +// remove the node and repair the tree. +TreeChunk* +BinaryTreeDictionary::removeChunkFromTree(TreeChunk* tc) { + assert(tc != NULL, "Should not call with a NULL chunk"); + assert(tc->isFree(), "Header is not marked correctly"); + + TreeList *newTL, *parentTL; + TreeChunk* retTC; + TreeList* tl = tc->list(); + debug_only( + bool removing_only_chunk = false; + if (tl == _root) { + if ((_root->left() == NULL) && (_root->right() == NULL)) { + if (_root->count() == 1) { + assert(_root->head() == tc, "Should only be this one chunk"); + removing_only_chunk = true; + } + } + } + ) + assert(tl != NULL, "List should be set"); + assert(tl->parent() == NULL || tl == tl->parent()->left() || + tl == tl->parent()->right(), "list is inconsistent"); + + bool complicatedSplice = false; + + retTC = tc; + // Removing this chunk can have the side effect of changing the node + // (TreeList*) in the tree. If the node is the root, update it. + TreeList* replacementTL = tl->removeChunkReplaceIfNeeded(tc); + assert(tc->isFree(), "Chunk should still be free"); + assert(replacementTL->parent() == NULL || + replacementTL == replacementTL->parent()->left() || + replacementTL == replacementTL->parent()->right(), + "list is inconsistent"); + if (tl == root()) { + assert(replacementTL->parent() == NULL, "Incorrectly replacing root"); + set_root(replacementTL); + } + debug_only( + if (tl != replacementTL) { + assert(replacementTL->head() != NULL, + "If the tree list was replaced, it should not be a NULL list"); + TreeList* rhl = replacementTL->head_as_TreeChunk()->list(); + TreeList* rtl = TreeChunk::as_TreeChunk(replacementTL->tail())->list(); + assert(rhl == replacementTL, "Broken head"); + assert(rtl == replacementTL, "Broken tail"); + assert(replacementTL->size() == tc->size(), "Broken size"); + } + ) + + // Does the tree need to be repaired? + if (replacementTL->count() == 0) { + assert(replacementTL->head() == NULL && + replacementTL->tail() == NULL, "list count is incorrect"); + // Find the replacement node for the (soon to be empty) node being removed. + // if we have a single (or no) child, splice child in our stead + if (replacementTL->left() == NULL) { + // left is NULL so pick right. right may also be NULL. + newTL = replacementTL->right(); + debug_only(replacementTL->clearRight();) + } else if (replacementTL->right() == NULL) { + // right is NULL + newTL = replacementTL->left(); + debug_only(replacementTL->clearLeft();) + } else { // we have both children, so, by patriarchal convention, + // my replacement is least node in right sub-tree + complicatedSplice = true; + newTL = removeTreeMinimum(replacementTL->right()); + assert(newTL != NULL && newTL->left() == NULL && + newTL->right() == NULL, "sub-tree minimum exists"); + } + // newTL is the replacement for the (soon to be empty) node. + // newTL may be NULL. + // should verify; we just cleanly excised our replacement + if (FLSVerifyDictionary) { + verifyTree(); + } + // first make newTL my parent's child + if ((parentTL = replacementTL->parent()) == NULL) { + // newTL should be root + assert(tl == root(), "Incorrectly replacing root"); + set_root(newTL); + if (newTL != NULL) { + newTL->clearParent(); + } + } else if (parentTL->right() == replacementTL) { + // replacementTL is a right child + parentTL->setRight(newTL); + } else { // replacementTL is a left child + assert(parentTL->left() == replacementTL, "should be left child"); + parentTL->setLeft(newTL); + } + debug_only(replacementTL->clearParent();) + if (complicatedSplice) { // we need newTL to get replacementTL's + // two children + assert(newTL != NULL && + newTL->left() == NULL && newTL->right() == NULL, + "newTL should not have encumbrances from the past"); + // we'd like to assert as below: + // assert(replacementTL->left() != NULL && replacementTL->right() != NULL, + // "else !complicatedSplice"); + // ... however, the above assertion is too strong because we aren't + // guaranteed that replacementTL->right() is still NULL. + // Recall that we removed + // the right sub-tree minimum from replacementTL. + // That may well have been its right + // child! So we'll just assert half of the above: + assert(replacementTL->left() != NULL, "else !complicatedSplice"); + newTL->setLeft(replacementTL->left()); + newTL->setRight(replacementTL->right()); + debug_only( + replacementTL->clearRight(); + replacementTL->clearLeft(); + ) + } + assert(replacementTL->right() == NULL && + replacementTL->left() == NULL && + replacementTL->parent() == NULL, + "delete without encumbrances"); + } + + assert(totalSize() >= retTC->size(), "Incorrect total size"); + dec_totalSize(retTC->size()); // size book-keeping + assert(totalFreeBlocks() > 0, "Incorrect total count"); + set_totalFreeBlocks(totalFreeBlocks() - 1); + + assert(retTC != NULL, "null chunk?"); + assert(retTC->prev() == NULL && retTC->next() == NULL, + "should return without encumbrances"); + if (FLSVerifyDictionary) { + verifyTree(); + } + assert(!removing_only_chunk || _root == NULL, "root should be NULL"); + return TreeChunk::as_TreeChunk(retTC); +} + +// Remove the leftmost node (lm) in the tree and return it. +// If lm has a right child, link it to the left node of +// the parent of lm. +TreeList* BinaryTreeDictionary::removeTreeMinimum(TreeList* tl) { + assert(tl != NULL && tl->parent() != NULL, "really need a proper sub-tree"); + // locate the subtree minimum by walking down left branches + TreeList* curTL = tl; + for (; curTL->left() != NULL; curTL = curTL->left()); + // obviously curTL now has at most one child, a right child + if (curTL != root()) { // Should this test just be removed? + TreeList* parentTL = curTL->parent(); + if (parentTL->left() == curTL) { // curTL is a left child + parentTL->setLeft(curTL->right()); + } else { + // If the list tl has no left child, then curTL may be + // the right child of parentTL. + assert(parentTL->right() == curTL, "should be a right child"); + parentTL->setRight(curTL->right()); + } + } else { + // The only use of this method would not pass the root of the + // tree (as indicated by the assertion above that the tree list + // has a parent) but the specification does not explicitly exclude the + // passing of the root so accomodate it. + set_root(NULL); + } + debug_only( + curTL->clearParent(); // Test if this needs to be cleared + curTL->clearRight(); // recall, above, left child is already null + ) + // we just excised a (non-root) node, we should still verify all tree invariants + if (FLSVerifyDictionary) { + verifyTree(); + } + return curTL; +} + +// Based on a simplification of the algorithm by Sleator and Tarjan (JACM 1985). +// The simplifications are the following: +// . we splay only when we delete (not when we insert) +// . we apply a single spay step per deletion/access +// By doing such partial splaying, we reduce the amount of restructuring, +// while getting a reasonably efficient search tree (we think). +// [Measurements will be needed to (in)validate this expectation.] + +void BinaryTreeDictionary::semiSplayStep(TreeList* tc) { + // apply a semi-splay step at the given node: + // . if root, norting needs to be done + // . if child of root, splay once + // . else zig-zig or sig-zag depending on path from grandparent + if (root() == tc) return; + warning("*** Splaying not yet implemented; " + "tree operations may be inefficient ***"); +} + +void BinaryTreeDictionary::insertChunkInTree(FreeChunk* fc) { + TreeList *curTL, *prevTL; + size_t size = fc->size(); + + assert(size >= MIN_TREE_CHUNK_SIZE, "too small to be a TreeList"); + if (FLSVerifyDictionary) { + verifyTree(); + } + // XXX: do i need to clear the FreeChunk fields, let me do it just in case + // Revisit this later + + fc->clearNext(); + fc->linkPrev(NULL); + + // work down from the _root, looking for insertion point + for (prevTL = curTL = root(); curTL != NULL;) { + if (curTL->size() == size) // exact match + break; + prevTL = curTL; + if (curTL->size() > size) { // follow left branch + curTL = curTL->left(); + } else { // follow right branch + assert(curTL->size() < size, "size inconsistency"); + curTL = curTL->right(); + } + } + TreeChunk* tc = TreeChunk::as_TreeChunk(fc); + // This chunk is being returned to the binary try. It's embedded + // TreeList should be unused at this point. + tc->initialize(); + if (curTL != NULL) { // exact match + tc->set_list(curTL); + curTL->returnChunkAtTail(tc); + } else { // need a new node in tree + tc->clearNext(); + tc->linkPrev(NULL); + TreeList* newTL = TreeList::as_TreeList(tc); + assert(((TreeChunk*)tc)->list() == newTL, + "List was not initialized correctly"); + if (prevTL == NULL) { // we are the only tree node + assert(root() == NULL, "control point invariant"); + set_root(newTL); + } else { // insert under prevTL ... + if (prevTL->size() < size) { // am right child + assert(prevTL->right() == NULL, "control point invariant"); + prevTL->setRight(newTL); + } else { // am left child + assert(prevTL->size() > size && prevTL->left() == NULL, "cpt pt inv"); + prevTL->setLeft(newTL); + } + } + } + assert(tc->list() != NULL, "Tree list should be set"); + + inc_totalSize(size); + // Method 'totalSizeInTree' walks through the every block in the + // tree, so it can cause significant performance loss if there are + // many blocks in the tree + assert(!FLSVerifyDictionary || totalSizeInTree(root()) == totalSize(), "_totalSize inconsistency"); + set_totalFreeBlocks(totalFreeBlocks() + 1); + if (FLSVerifyDictionary) { + verifyTree(); + } +} + +size_t BinaryTreeDictionary::maxChunkSize() const { + verify_par_locked(); + TreeList* tc = root(); + if (tc == NULL) return 0; + for (; tc->right() != NULL; tc = tc->right()); + return tc->size(); +} + +size_t BinaryTreeDictionary::totalListLength(TreeList* tl) const { + size_t res; + res = tl->count(); +#ifdef ASSERT + size_t cnt; + FreeChunk* tc = tl->head(); + for (cnt = 0; tc != NULL; tc = tc->next(), cnt++); + assert(res == cnt, "The count is not being maintained correctly"); +#endif + return res; +} + +size_t BinaryTreeDictionary::totalSizeInTree(TreeList* tl) const { + if (tl == NULL) + return 0; + return (tl->size() * totalListLength(tl)) + + totalSizeInTree(tl->left()) + + totalSizeInTree(tl->right()); +} + +double BinaryTreeDictionary::sum_of_squared_block_sizes(TreeList* const tl) const { + if (tl == NULL) { + return 0.0; + } + double size = (double)(tl->size()); + double curr = size * size * totalListLength(tl); + curr += sum_of_squared_block_sizes(tl->left()); + curr += sum_of_squared_block_sizes(tl->right()); + return curr; +} + +size_t BinaryTreeDictionary::totalFreeBlocksInTree(TreeList* tl) const { + if (tl == NULL) + return 0; + return totalListLength(tl) + + totalFreeBlocksInTree(tl->left()) + + totalFreeBlocksInTree(tl->right()); +} + +size_t BinaryTreeDictionary::numFreeBlocks() const { + assert(totalFreeBlocksInTree(root()) == totalFreeBlocks(), + "_totalFreeBlocks inconsistency"); + return totalFreeBlocks(); +} + +size_t BinaryTreeDictionary::treeHeightHelper(TreeList* tl) const { + if (tl == NULL) + return 0; + return 1 + MAX2(treeHeightHelper(tl->left()), + treeHeightHelper(tl->right())); +} + +size_t BinaryTreeDictionary::treeHeight() const { + return treeHeightHelper(root()); +} + +size_t BinaryTreeDictionary::totalNodesHelper(TreeList* tl) const { + if (tl == NULL) { + return 0; + } + return 1 + totalNodesHelper(tl->left()) + + totalNodesHelper(tl->right()); +} + +size_t BinaryTreeDictionary::totalNodesInTree(TreeList* tl) const { + return totalNodesHelper(root()); +} + +void BinaryTreeDictionary::dictCensusUpdate(size_t size, bool split, bool birth){ + TreeList* nd = findList(size); + if (nd) { + if (split) { + if (birth) { + nd->increment_splitBirths(); + nd->increment_surplus(); + } else { + nd->increment_splitDeaths(); + nd->decrement_surplus(); + } + } else { + if (birth) { + nd->increment_coalBirths(); + nd->increment_surplus(); + } else { + nd->increment_coalDeaths(); + nd->decrement_surplus(); + } + } + } + // A list for this size may not be found (nd == 0) if + // This is a death where the appropriate list is now + // empty and has been removed from the list. + // This is a birth associated with a LinAB. The chunk + // for the LinAB is not in the dictionary. +} + +bool BinaryTreeDictionary::coalDictOverPopulated(size_t size) { + TreeList* list_of_size = findList(size); + // None of requested size implies overpopulated. + return list_of_size == NULL || list_of_size->coalDesired() <= 0 || + list_of_size->count() > list_of_size->coalDesired(); +} + +// Closures for walking the binary tree. +// do_list() walks the free list in a node applying the closure +// to each free chunk in the list +// do_tree() walks the nodes in the binary tree applying do_list() +// to each list at each node. + +class TreeCensusClosure : public StackObj { + protected: + virtual void do_list(FreeList* fl) = 0; + public: + virtual void do_tree(TreeList* tl) = 0; +}; + +class AscendTreeCensusClosure : public TreeCensusClosure { + public: + void do_tree(TreeList* tl) { + if (tl != NULL) { + do_tree(tl->left()); + do_list(tl); + do_tree(tl->right()); + } + } +}; + +class DescendTreeCensusClosure : public TreeCensusClosure { + public: + void do_tree(TreeList* tl) { + if (tl != NULL) { + do_tree(tl->right()); + do_list(tl); + do_tree(tl->left()); + } + } +}; + +// For each list in the tree, calculate the desired, desired +// coalesce, count before sweep, and surplus before sweep. +class BeginSweepClosure : public AscendTreeCensusClosure { + double _percentage; + float _inter_sweep_current; + float _inter_sweep_estimate; + + public: + BeginSweepClosure(double p, float inter_sweep_current, + float inter_sweep_estimate) : + _percentage(p), + _inter_sweep_current(inter_sweep_current), + _inter_sweep_estimate(inter_sweep_estimate) { } + + void do_list(FreeList* fl) { + double coalSurplusPercent = _percentage; + fl->compute_desired(_inter_sweep_current, _inter_sweep_estimate); + fl->set_coalDesired((ssize_t)((double)fl->desired() * coalSurplusPercent)); + fl->set_beforeSweep(fl->count()); + fl->set_bfrSurp(fl->surplus()); + } +}; + +// Used to search the tree until a condition is met. +// Similar to TreeCensusClosure but searches the +// tree and returns promptly when found. + +class TreeSearchClosure : public StackObj { + protected: + virtual bool do_list(FreeList* fl) = 0; + public: + virtual bool do_tree(TreeList* tl) = 0; +}; + +#if 0 // Don't need this yet but here for symmetry. +class AscendTreeSearchClosure : public TreeSearchClosure { + public: + bool do_tree(TreeList* tl) { + if (tl != NULL) { + if (do_tree(tl->left())) return true; + if (do_list(tl)) return true; + if (do_tree(tl->right())) return true; + } + return false; + } +}; +#endif + +class DescendTreeSearchClosure : public TreeSearchClosure { + public: + bool do_tree(TreeList* tl) { + if (tl != NULL) { + if (do_tree(tl->right())) return true; + if (do_list(tl)) return true; + if (do_tree(tl->left())) return true; + } + return false; + } +}; + +// Searches the tree for a chunk that ends at the +// specified address. +class EndTreeSearchClosure : public DescendTreeSearchClosure { + HeapWord* _target; + FreeChunk* _found; + + public: + EndTreeSearchClosure(HeapWord* target) : _target(target), _found(NULL) {} + bool do_list(FreeList* fl) { + FreeChunk* item = fl->head(); + while (item != NULL) { + if (item->end() == _target) { + _found = item; + return true; + } + item = item->next(); + } + return false; + } + FreeChunk* found() { return _found; } +}; + +FreeChunk* BinaryTreeDictionary::find_chunk_ends_at(HeapWord* target) const { + EndTreeSearchClosure etsc(target); + bool found_target = etsc.do_tree(root()); + assert(found_target || etsc.found() == NULL, "Consistency check"); + assert(!found_target || etsc.found() != NULL, "Consistency check"); + return etsc.found(); +} + +void BinaryTreeDictionary::beginSweepDictCensus(double coalSurplusPercent, + float inter_sweep_current, float inter_sweep_estimate) { + BeginSweepClosure bsc(coalSurplusPercent, inter_sweep_current, + inter_sweep_estimate); + bsc.do_tree(root()); +} + +// Closures and methods for calculating total bytes returned to the +// free lists in the tree. +NOT_PRODUCT( + class InitializeDictReturnedBytesClosure : public AscendTreeCensusClosure { + public: + void do_list(FreeList* fl) { + fl->set_returnedBytes(0); + } + }; + + void BinaryTreeDictionary::initializeDictReturnedBytes() { + InitializeDictReturnedBytesClosure idrb; + idrb.do_tree(root()); + } + + class ReturnedBytesClosure : public AscendTreeCensusClosure { + size_t _dictReturnedBytes; + public: + ReturnedBytesClosure() { _dictReturnedBytes = 0; } + void do_list(FreeList* fl) { + _dictReturnedBytes += fl->returnedBytes(); + } + size_t dictReturnedBytes() { return _dictReturnedBytes; } + }; + + size_t BinaryTreeDictionary::sumDictReturnedBytes() { + ReturnedBytesClosure rbc; + rbc.do_tree(root()); + + return rbc.dictReturnedBytes(); + } + + // Count the number of entries in the tree. + class treeCountClosure : public DescendTreeCensusClosure { + public: + uint count; + treeCountClosure(uint c) { count = c; } + void do_list(FreeList* fl) { + count++; + } + }; + + size_t BinaryTreeDictionary::totalCount() { + treeCountClosure ctc(0); + ctc.do_tree(root()); + return ctc.count; + } +) + +// Calculate surpluses for the lists in the tree. +class setTreeSurplusClosure : public AscendTreeCensusClosure { + double percentage; + public: + setTreeSurplusClosure(double v) { percentage = v; } + void do_list(FreeList* fl) { + double splitSurplusPercent = percentage; + fl->set_surplus(fl->count() - + (ssize_t)((double)fl->desired() * splitSurplusPercent)); + } +}; + +void BinaryTreeDictionary::setTreeSurplus(double splitSurplusPercent) { + setTreeSurplusClosure sts(splitSurplusPercent); + sts.do_tree(root()); +} + +// Set hints for the lists in the tree. +class setTreeHintsClosure : public DescendTreeCensusClosure { + size_t hint; + public: + setTreeHintsClosure(size_t v) { hint = v; } + void do_list(FreeList* fl) { + fl->set_hint(hint); + assert(fl->hint() == 0 || fl->hint() > fl->size(), + "Current hint is inconsistent"); + if (fl->surplus() > 0) { + hint = fl->size(); + } + } +}; + +void BinaryTreeDictionary::setTreeHints(void) { + setTreeHintsClosure sth(0); + sth.do_tree(root()); +} + +// Save count before previous sweep and splits and coalesces. +class clearTreeCensusClosure : public AscendTreeCensusClosure { + void do_list(FreeList* fl) { + fl->set_prevSweep(fl->count()); + fl->set_coalBirths(0); + fl->set_coalDeaths(0); + fl->set_splitBirths(0); + fl->set_splitDeaths(0); + } +}; + +void BinaryTreeDictionary::clearTreeCensus(void) { + clearTreeCensusClosure ctc; + ctc.do_tree(root()); +} + +// Do reporting and post sweep clean up. +void BinaryTreeDictionary::endSweepDictCensus(double splitSurplusPercent) { + // Does walking the tree 3 times hurt? + setTreeSurplus(splitSurplusPercent); + setTreeHints(); + if (PrintGC && Verbose) { + reportStatistics(); + } + clearTreeCensus(); +} + +// Print summary statistics +void BinaryTreeDictionary::reportStatistics() const { + verify_par_locked(); + gclog_or_tty->print("Statistics for BinaryTreeDictionary:\n" + "------------------------------------\n"); + size_t totalSize = totalChunkSize(debug_only(NULL)); + size_t freeBlocks = numFreeBlocks(); + gclog_or_tty->print("Total Free Space: %d\n", totalSize); + gclog_or_tty->print("Max Chunk Size: %d\n", maxChunkSize()); + gclog_or_tty->print("Number of Blocks: %d\n", freeBlocks); + if (freeBlocks > 0) { + gclog_or_tty->print("Av. Block Size: %d\n", totalSize/freeBlocks); + } + gclog_or_tty->print("Tree Height: %d\n", treeHeight()); +} + +// Print census information - counts, births, deaths, etc. +// for each list in the tree. Also print some summary +// information. +class printTreeCensusClosure : public AscendTreeCensusClosure { + size_t _totalFree; + AllocationStats _totals; + size_t _count; + + public: + printTreeCensusClosure() { + _totalFree = 0; + _count = 0; + _totals.initialize(); + } + AllocationStats* totals() { return &_totals; } + size_t count() { return _count; } + void increment_count_by(size_t v) { _count += v; } + size_t totalFree() { return _totalFree; } + void increment_totalFree_by(size_t v) { _totalFree += v; } + void do_list(FreeList* fl) { + bool nl = false; // "maybe this is not needed" isNearLargestChunk(fl->head()); + + gclog_or_tty->print("%c %4d\t\t" "%7d\t" "%7d\t" + "%7d\t" "%7d\t" "%7d\t" "%7d\t" + "%7d\t" "%7d\t" "%7d\t" + "%7d\t" "\n", + " n"[nl], fl->size(), fl->bfrSurp(), fl->surplus(), + fl->desired(), fl->prevSweep(), fl->beforeSweep(), fl->count(), + fl->coalBirths(), fl->coalDeaths(), fl->splitBirths(), + fl->splitDeaths()); + + increment_totalFree_by(fl->count() * fl->size()); + increment_count_by(fl->count()); + totals()->set_bfrSurp(totals()->bfrSurp() + fl->bfrSurp()); + totals()->set_surplus(totals()->splitDeaths() + fl->surplus()); + totals()->set_prevSweep(totals()->prevSweep() + fl->prevSweep()); + totals()->set_beforeSweep(totals()->beforeSweep() + fl->beforeSweep()); + totals()->set_coalBirths(totals()->coalBirths() + fl->coalBirths()); + totals()->set_coalDeaths(totals()->coalDeaths() + fl->coalDeaths()); + totals()->set_splitBirths(totals()->splitBirths() + fl->splitBirths()); + totals()->set_splitDeaths(totals()->splitDeaths() + fl->splitDeaths()); + } +}; + +void BinaryTreeDictionary::printDictCensus(void) const { + + gclog_or_tty->print("\nBinaryTree\n"); + gclog_or_tty->print( + "%4s\t\t" "%7s\t" "%7s\t" "%7s\t" "%7s\t" "%7s\t" + "%7s\t" "%7s\t" "%7s\t" "%7s\t" "%7s\t" "\n", + "size", "bfrsurp", "surplus", "desired", "prvSwep", "bfrSwep", + "count", "cBirths", "cDeaths", "sBirths", "sDeaths"); + + printTreeCensusClosure ptc; + ptc.do_tree(root()); + + gclog_or_tty->print( + "\t\t" "%7s\t" "%7s\t" "%7s\t" "%7s\t" + "%7s\t" "%7s\t" "%7s\t" "%7s\t" "%7s\t" "\n", + "bfrsurp", "surplus", "prvSwep", "bfrSwep", + "count", "cBirths", "cDeaths", "sBirths", "sDeaths"); + gclog_or_tty->print( + "%s\t\t" "%7d\t" "%7d\t" "%7d\t" "%7d\t" + "%7d\t" "%7d\t" "%7d\t" "%7d\t" "%7d\t" "\n", + "totl", + ptc.totals()->bfrSurp(), + ptc.totals()->surplus(), + ptc.totals()->prevSweep(), + ptc.totals()->beforeSweep(), + ptc.count(), + ptc.totals()->coalBirths(), + ptc.totals()->coalDeaths(), + ptc.totals()->splitBirths(), + ptc.totals()->splitDeaths()); + gclog_or_tty->print("totalFree(words): %7d growth: %8.5f deficit: %8.5f\n", + ptc.totalFree(), + (double)(ptc.totals()->splitBirths()+ptc.totals()->coalBirths() + -ptc.totals()->splitDeaths()-ptc.totals()->coalDeaths()) + /(ptc.totals()->prevSweep() != 0 ? + (double)ptc.totals()->prevSweep() : 1.0), + (double)(ptc.totals()->desired() - ptc.count()) + /(ptc.totals()->desired() != 0 ? + (double)ptc.totals()->desired() : 1.0)); +} + +// Verify the following tree invariants: +// . _root has no parent +// . parent and child point to each other +// . each node's key correctly related to that of its child(ren) +void BinaryTreeDictionary::verifyTree() const { + guarantee(root() == NULL || totalFreeBlocks() == 0 || + totalSize() != 0, "_totalSize should't be 0?"); + guarantee(root() == NULL || root()->parent() == NULL, "_root shouldn't have parent"); + verifyTreeHelper(root()); +} + +size_t BinaryTreeDictionary::verifyPrevFreePtrs(TreeList* tl) { + size_t ct = 0; + for (FreeChunk* curFC = tl->head(); curFC != NULL; curFC = curFC->next()) { + ct++; + assert(curFC->prev() == NULL || curFC->prev()->isFree(), + "Chunk should be free"); + } + return ct; +} + +// Note: this helper is recursive rather than iterative, so use with +// caution on very deep trees; and watch out for stack overflow errors; +// In general, to be used only for debugging. +void BinaryTreeDictionary::verifyTreeHelper(TreeList* tl) const { + if (tl == NULL) + return; + guarantee(tl->size() != 0, "A list must has a size"); + guarantee(tl->left() == NULL || tl->left()->parent() == tl, + "parent<-/->left"); + guarantee(tl->right() == NULL || tl->right()->parent() == tl, + "parent<-/->right");; + guarantee(tl->left() == NULL || tl->left()->size() < tl->size(), + "parent !> left"); + guarantee(tl->right() == NULL || tl->right()->size() > tl->size(), + "parent !< left"); + guarantee(tl->head() == NULL || tl->head()->isFree(), "!Free"); + guarantee(tl->head() == NULL || tl->head_as_TreeChunk()->list() == tl, + "list inconsistency"); + guarantee(tl->count() > 0 || (tl->head() == NULL && tl->tail() == NULL), + "list count is inconsistent"); + guarantee(tl->count() > 1 || tl->head() == tl->tail(), + "list is incorrectly constructed"); + size_t count = verifyPrevFreePtrs(tl); + guarantee(count == (size_t)tl->count(), "Node count is incorrect"); + if (tl->head() != NULL) { + tl->head_as_TreeChunk()->verifyTreeChunkList(); + } + verifyTreeHelper(tl->left()); + verifyTreeHelper(tl->right()); +} + +void BinaryTreeDictionary::verify() const { + verifyTree(); + guarantee(totalSize() == totalSizeInTree(root()), "Total Size inconsistency"); +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.hpp new file mode 100644 index 00000000000..faedf5f7fdf --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.hpp @@ -0,0 +1,283 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * A binary tree based search structure for free blocks. + * This is currently used in the Concurrent Mark&Sweep implementation. + */ + +// A TreeList is a FreeList which can be used to maintain a +// binary tree of free lists. + +class TreeChunk; +class BinaryTreeDictionary; +class AscendTreeCensusClosure; +class DescendTreeCensusClosure; +class DescendTreeSearchClosure; + +class TreeList: public FreeList { + friend class TreeChunk; + friend class BinaryTreeDictionary; + friend class AscendTreeCensusClosure; + friend class DescendTreeCensusClosure; + friend class DescendTreeSearchClosure; + TreeList* _parent; + TreeList* _left; + TreeList* _right; + + protected: + TreeList* parent() const { return _parent; } + TreeList* left() const { return _left; } + TreeList* right() const { return _right; } + + // Accessors for links in tree. + + void setLeft(TreeList* tl) { + _left = tl; + if (tl != NULL) + tl->setParent(this); + } + void setRight(TreeList* tl) { + _right = tl; + if (tl != NULL) + tl->setParent(this); + } + void setParent(TreeList* tl) { _parent = tl; } + + void clearLeft() { _left = NULL; } + void clearRight() { _right = NULL; } + void clearParent() { _parent = NULL; } + void initialize() { clearLeft(); clearRight(), clearParent(); } + + // For constructing a TreeList from a Tree chunk or + // address and size. + static TreeList* as_TreeList(TreeChunk* tc); + static TreeList* as_TreeList(HeapWord* addr, size_t size); + + // Returns the head of the free list as a pointer to a TreeChunk. + TreeChunk* head_as_TreeChunk(); + + // Returns the first available chunk in the free list as a pointer + // to a TreeChunk. + TreeChunk* first_available(); + + // removeChunkReplaceIfNeeded() removes the given "tc" from the TreeList. + // If "tc" is the first chunk in the list, it is also the + // TreeList that is the node in the tree. removeChunkReplaceIfNeeded() + // returns the possibly replaced TreeList* for the node in + // the tree. It also updates the parent of the original + // node to point to the new node. + TreeList* removeChunkReplaceIfNeeded(TreeChunk* tc); + // See FreeList. + void returnChunkAtHead(TreeChunk* tc); + void returnChunkAtTail(TreeChunk* tc); +}; + +// A TreeChunk is a subclass of a FreeChunk that additionally +// maintains a pointer to the free list on which it is currently +// linked. +// A TreeChunk is also used as a node in the binary tree. This +// allows the binary tree to be maintained without any additional +// storage (the free chunks are used). In a binary tree the first +// chunk in the free list is also the tree node. Note that the +// TreeChunk has an embedded TreeList for this purpose. Because +// the first chunk in the list is distinguished in this fashion +// (also is the node in the tree), it is the last chunk to be found +// on the free list for a node in the tree and is only removed if +// it is the last chunk on the free list. + +class TreeChunk : public FreeChunk { + friend class TreeList; + TreeList* _list; + TreeList _embedded_list; // if non-null, this chunk is on _list + protected: + TreeList* embedded_list() const { return (TreeList*) &_embedded_list; } + void set_embedded_list(TreeList* v) { _embedded_list = *v; } + public: + TreeList* list() { return _list; } + void set_list(TreeList* v) { _list = v; } + static TreeChunk* as_TreeChunk(FreeChunk* fc); + // Initialize fields in a TreeChunk that should be + // initialized when the TreeChunk is being added to + // a free list in the tree. + void initialize() { embedded_list()->initialize(); } + + // debugging + void verifyTreeChunkList() const; +}; + +const size_t MIN_TREE_CHUNK_SIZE = sizeof(TreeChunk)/HeapWordSize; + +class BinaryTreeDictionary: public FreeBlockDictionary { + bool _splay; + size_t _totalSize; + size_t _totalFreeBlocks; + TreeList* _root; + + // private accessors + bool splay() const { return _splay; } + void set_splay(bool v) { _splay = v; } + size_t totalSize() const { return _totalSize; } + void set_totalSize(size_t v) { _totalSize = v; } + virtual void inc_totalSize(size_t v); + virtual void dec_totalSize(size_t v); + size_t totalFreeBlocks() const { return _totalFreeBlocks; } + void set_totalFreeBlocks(size_t v) { _totalFreeBlocks = v; } + TreeList* root() const { return _root; } + void set_root(TreeList* v) { _root = v; } + + // Remove a chunk of size "size" or larger from the tree and + // return it. If the chunk + // is the last chunk of that size, remove the node for that size + // from the tree. + TreeChunk* getChunkFromTree(size_t size, Dither dither, bool splay); + // Return a list of the specified size or NULL from the tree. + // The list is not removed from the tree. + TreeList* findList (size_t size) const; + // Remove this chunk from the tree. If the removal results + // in an empty list in the tree, remove the empty list. + TreeChunk* removeChunkFromTree(TreeChunk* tc); + // Remove the node in the trees starting at tl that has the + // minimum value and return it. Repair the tree as needed. + TreeList* removeTreeMinimum(TreeList* tl); + void semiSplayStep(TreeList* tl); + // Add this free chunk to the tree. + void insertChunkInTree(FreeChunk* freeChunk); + public: + void verifyTree() const; + // verify that the given chunk is in the tree. + bool verifyChunkInFreeLists(FreeChunk* tc) const; + private: + void verifyTreeHelper(TreeList* tl) const; + static size_t verifyPrevFreePtrs(TreeList* tl); + + // Returns the total number of chunks in the list. + size_t totalListLength(TreeList* tl) const; + // Returns the total number of words in the chunks in the tree + // starting at "tl". + size_t totalSizeInTree(TreeList* tl) const; + // Returns the sum of the square of the size of each block + // in the tree starting at "tl". + double sum_of_squared_block_sizes(TreeList* const tl) const; + // Returns the total number of free blocks in the tree starting + // at "tl". + size_t totalFreeBlocksInTree(TreeList* tl) const; + size_t numFreeBlocks() const; + size_t treeHeight() const; + size_t treeHeightHelper(TreeList* tl) const; + size_t totalNodesInTree(TreeList* tl) const; + size_t totalNodesHelper(TreeList* tl) const; + + public: + // Constructor + BinaryTreeDictionary(MemRegion mr, bool splay = false); + + // Reset the dictionary to the initial conditions with + // a single free chunk. + void reset(MemRegion mr); + void reset(HeapWord* addr, size_t size); + // Reset the dictionary to be empty. + void reset(); + + // Return a chunk of size "size" or greater from + // the tree. + // want a better dynamic splay strategy for the future. + FreeChunk* getChunk(size_t size, Dither dither) { + verify_par_locked(); + FreeChunk* res = getChunkFromTree(size, dither, splay()); + assert(res == NULL || res->isFree(), + "Should be returning a free chunk"); + return res; + } + + void returnChunk(FreeChunk* chunk) { + verify_par_locked(); + insertChunkInTree(chunk); + } + + void removeChunk(FreeChunk* chunk) { + verify_par_locked(); + removeChunkFromTree((TreeChunk*)chunk); + assert(chunk->isFree(), "Should still be a free chunk"); + } + + size_t maxChunkSize() const; + size_t totalChunkSize(debug_only(const Mutex* lock)) const { + debug_only( + if (lock != NULL && lock->owned_by_self()) { + assert(totalSizeInTree(root()) == totalSize(), + "_totalSize inconsistency"); + } + ) + return totalSize(); + } + + size_t minSize() const { + return MIN_TREE_CHUNK_SIZE; + } + + double sum_of_squared_block_sizes() const { + return sum_of_squared_block_sizes(root()); + } + + FreeChunk* find_chunk_ends_at(HeapWord* target) const; + + // Find the list with size "size" in the binary tree and update + // the statistics in the list according to "split" (chunk was + // split or coalesce) and "birth" (chunk was added or removed). + void dictCensusUpdate(size_t size, bool split, bool birth); + // Return true if the dictionary is overpopulated (more chunks of + // this size than desired) for size "size". + bool coalDictOverPopulated(size_t size); + // Methods called at the beginning of a sweep to prepare the + // statistics for the sweep. + void beginSweepDictCensus(double coalSurplusPercent, + float sweep_current, + float sweep_estimate); + // Methods called after the end of a sweep to modify the + // statistics for the sweep. + void endSweepDictCensus(double splitSurplusPercent); + // Return the largest free chunk in the tree. + FreeChunk* findLargestDict() const; + // Accessors for statistics + void setTreeSurplus(double splitSurplusPercent); + void setTreeHints(void); + // Reset statistics for all the lists in the tree. + void clearTreeCensus(void); + // Print the statistcis for all the lists in the tree. Also may + // print out summaries. + void printDictCensus(void) const; + + // For debugging. Returns the sum of the _returnedBytes for + // all lists in the tree. + size_t sumDictReturnedBytes() PRODUCT_RETURN0; + // Sets the _returnedBytes for all the lists in the tree to zero. + void initializeDictReturnedBytes() PRODUCT_RETURN; + // For debugging. Return the total number of chunks in the dictionary. + size_t totalCount() PRODUCT_RETURN0; + + void reportStatistics() const; + + void verify() const; +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp new file mode 100644 index 00000000000..a120f6af4ba --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp @@ -0,0 +1,1334 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +#include "incls/_precompiled.incl" +#include "incls/_cmsAdaptiveSizePolicy.cpp.incl" + +elapsedTimer CMSAdaptiveSizePolicy::_concurrent_timer; +elapsedTimer CMSAdaptiveSizePolicy::_STW_timer; + +// Defined if the granularity of the time measurements is potentially too large. +#define CLOCK_GRANULARITY_TOO_LARGE + +CMSAdaptiveSizePolicy::CMSAdaptiveSizePolicy(size_t init_eden_size, + size_t init_promo_size, + size_t init_survivor_size, + double max_gc_minor_pause_sec, + double max_gc_pause_sec, + uint gc_cost_ratio) : + AdaptiveSizePolicy(init_eden_size, + init_promo_size, + init_survivor_size, + max_gc_pause_sec, + gc_cost_ratio) { + + clear_internal_time_intervals(); + + _processor_count = os::active_processor_count(); + + if (CMSConcurrentMTEnabled && (ParallelCMSThreads > 1)) { + assert(_processor_count > 0, "Processor count is suspect"); + _concurrent_processor_count = MIN2((uint) ParallelCMSThreads, + (uint) _processor_count); + } else { + _concurrent_processor_count = 1; + } + + _avg_concurrent_time = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_concurrent_interval = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_concurrent_gc_cost = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + + _avg_initial_pause = new AdaptivePaddedAverage(AdaptiveTimeWeight, + PausePadding); + _avg_remark_pause = new AdaptivePaddedAverage(AdaptiveTimeWeight, + PausePadding); + + _avg_cms_STW_time = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_cms_STW_gc_cost = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + + _avg_cms_free = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_cms_free_at_sweep = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_cms_promo = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + + // Mark-sweep-compact + _avg_msc_pause = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_msc_interval = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_msc_gc_cost = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + + // Mark-sweep + _avg_ms_pause = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_ms_interval = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_ms_gc_cost = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + + // Variables that estimate pause times as a function of generation + // size. + _remark_pause_old_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + _initial_pause_old_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + _remark_pause_young_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + _initial_pause_young_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + + // Alignment comes from that used in ReservedSpace. + _generation_alignment = os::vm_allocation_granularity(); + + // Start the concurrent timer here so that the first + // concurrent_phases_begin() measures a finite mutator + // time. A finite mutator time is used to determine + // if a concurrent collection has been started. If this + // proves to be a problem, use some explicit flag to + // signal that a concurrent collection has been started. + _concurrent_timer.start(); + _STW_timer.start(); +} + +double CMSAdaptiveSizePolicy::concurrent_processor_fraction() { + // For now assume no other daemon threads are taking alway + // cpu's from the application. + return ((double) _concurrent_processor_count / (double) _processor_count); +} + +double CMSAdaptiveSizePolicy::concurrent_collection_cost( + double interval_in_seconds) { + // When the precleaning and sweeping phases use multiple + // threads, change one_processor_fraction to + // concurrent_processor_fraction(). + double one_processor_fraction = 1.0 / ((double) processor_count()); + double concurrent_cost = + collection_cost(_latest_cms_concurrent_marking_time_secs, + interval_in_seconds) * concurrent_processor_fraction() + + collection_cost(_latest_cms_concurrent_precleaning_time_secs, + interval_in_seconds) * one_processor_fraction + + collection_cost(_latest_cms_concurrent_sweeping_time_secs, + interval_in_seconds) * one_processor_fraction; + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "\nCMSAdaptiveSizePolicy::scaled_concurrent_collection_cost(%f) " + "_latest_cms_concurrent_marking_cost %f " + "_latest_cms_concurrent_precleaning_cost %f " + "_latest_cms_concurrent_sweeping_cost %f " + "concurrent_processor_fraction %f " + "concurrent_cost %f ", + interval_in_seconds, + collection_cost(_latest_cms_concurrent_marking_time_secs, + interval_in_seconds), + collection_cost(_latest_cms_concurrent_precleaning_time_secs, + interval_in_seconds), + collection_cost(_latest_cms_concurrent_sweeping_time_secs, + interval_in_seconds), + concurrent_processor_fraction(), + concurrent_cost); + } + return concurrent_cost; +} + +double CMSAdaptiveSizePolicy::concurrent_collection_time() { + double latest_cms_sum_concurrent_phases_time_secs = + _latest_cms_concurrent_marking_time_secs + + _latest_cms_concurrent_precleaning_time_secs + + _latest_cms_concurrent_sweeping_time_secs; + return latest_cms_sum_concurrent_phases_time_secs; +} + +double CMSAdaptiveSizePolicy::scaled_concurrent_collection_time() { + // When the precleaning and sweeping phases use multiple + // threads, change one_processor_fraction to + // concurrent_processor_fraction(). + double one_processor_fraction = 1.0 / ((double) processor_count()); + double latest_cms_sum_concurrent_phases_time_secs = + _latest_cms_concurrent_marking_time_secs * concurrent_processor_fraction() + + _latest_cms_concurrent_precleaning_time_secs * one_processor_fraction + + _latest_cms_concurrent_sweeping_time_secs * one_processor_fraction ; + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "\nCMSAdaptiveSizePolicy::scaled_concurrent_collection_time " + "_latest_cms_concurrent_marking_time_secs %f " + "_latest_cms_concurrent_precleaning_time_secs %f " + "_latest_cms_concurrent_sweeping_time_secs %f " + "concurrent_processor_fraction %f " + "latest_cms_sum_concurrent_phases_time_secs %f ", + _latest_cms_concurrent_marking_time_secs, + _latest_cms_concurrent_precleaning_time_secs, + _latest_cms_concurrent_sweeping_time_secs, + concurrent_processor_fraction(), + latest_cms_sum_concurrent_phases_time_secs); + } + return latest_cms_sum_concurrent_phases_time_secs; +} + +void CMSAdaptiveSizePolicy::update_minor_pause_old_estimator( + double minor_pause_in_ms) { + // Get the equivalent of the free space + // that is available for promotions in the CMS generation + // and use that to update _minor_pause_old_estimator + + // Don't implement this until it is needed. A warning is + // printed if _minor_pause_old_estimator is used. +} + +void CMSAdaptiveSizePolicy::concurrent_marking_begin() { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print(" "); + gclog_or_tty->stamp(); + gclog_or_tty->print(": concurrent_marking_begin "); + } + // Update the interval time + _concurrent_timer.stop(); + _latest_cms_collection_end_to_collection_start_secs = _concurrent_timer.seconds(); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("CMSAdaptiveSizePolicy::concurrent_marking_begin: " + "mutator time %f", _latest_cms_collection_end_to_collection_start_secs); + } + _concurrent_timer.reset(); + _concurrent_timer.start(); +} + +void CMSAdaptiveSizePolicy::concurrent_marking_end() { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->stamp(); + gclog_or_tty->print_cr("CMSAdaptiveSizePolicy::concurrent_marking_end()"); + } + + _concurrent_timer.stop(); + _latest_cms_concurrent_marking_time_secs = _concurrent_timer.seconds(); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("\n CMSAdaptiveSizePolicy::concurrent_marking_end" + ":concurrent marking time (s) %f", + _latest_cms_concurrent_marking_time_secs); + } +} + +void CMSAdaptiveSizePolicy::concurrent_precleaning_begin() { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->stamp(); + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::concurrent_precleaning_begin()"); + } + _concurrent_timer.reset(); + _concurrent_timer.start(); +} + + +void CMSAdaptiveSizePolicy::concurrent_precleaning_end() { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->stamp(); + gclog_or_tty->print_cr("CMSAdaptiveSizePolicy::concurrent_precleaning_end()"); + } + + _concurrent_timer.stop(); + // May be set again by a second call during the same collection. + _latest_cms_concurrent_precleaning_time_secs = _concurrent_timer.seconds(); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("\n CMSAdaptiveSizePolicy::concurrent_precleaning_end" + ":concurrent precleaning time (s) %f", + _latest_cms_concurrent_precleaning_time_secs); + } +} + +void CMSAdaptiveSizePolicy::concurrent_sweeping_begin() { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->stamp(); + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::concurrent_sweeping_begin()"); + } + _concurrent_timer.reset(); + _concurrent_timer.start(); +} + + +void CMSAdaptiveSizePolicy::concurrent_sweeping_end() { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->stamp(); + gclog_or_tty->print_cr("CMSAdaptiveSizePolicy::concurrent_sweeping_end()"); + } + + _concurrent_timer.stop(); + _latest_cms_concurrent_sweeping_time_secs = _concurrent_timer.seconds(); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("\n CMSAdaptiveSizePolicy::concurrent_sweeping_end" + ":concurrent sweeping time (s) %f", + _latest_cms_concurrent_sweeping_time_secs); + } +} + +void CMSAdaptiveSizePolicy::concurrent_phases_end(GCCause::Cause gc_cause, + size_t cur_eden, + size_t cur_promo) { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print(" "); + gclog_or_tty->stamp(); + gclog_or_tty->print(": concurrent_phases_end "); + } + + // Update the concurrent timer + _concurrent_timer.stop(); + + if (gc_cause != GCCause::_java_lang_system_gc || + UseAdaptiveSizePolicyWithSystemGC) { + + avg_cms_free()->sample(cur_promo); + double latest_cms_sum_concurrent_phases_time_secs = + concurrent_collection_time(); + + _avg_concurrent_time->sample(latest_cms_sum_concurrent_phases_time_secs); + + // Cost of collection (unit-less) + + // Total interval for collection. May not be valid. Tests + // below determine whether to use this. + // + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("\nCMSAdaptiveSizePolicy::concurrent_phases_end \n" + "_latest_cms_reset_end_to_initial_mark_start_secs %f \n" + "_latest_cms_initial_mark_start_to_end_time_secs %f \n" + "_latest_cms_remark_start_to_end_time_secs %f \n" + "_latest_cms_concurrent_marking_time_secs %f \n" + "_latest_cms_concurrent_precleaning_time_secs %f \n" + "_latest_cms_concurrent_sweeping_time_secs %f \n" + "latest_cms_sum_concurrent_phases_time_secs %f \n" + "_latest_cms_collection_end_to_collection_start_secs %f \n" + "concurrent_processor_fraction %f", + _latest_cms_reset_end_to_initial_mark_start_secs, + _latest_cms_initial_mark_start_to_end_time_secs, + _latest_cms_remark_start_to_end_time_secs, + _latest_cms_concurrent_marking_time_secs, + _latest_cms_concurrent_precleaning_time_secs, + _latest_cms_concurrent_sweeping_time_secs, + latest_cms_sum_concurrent_phases_time_secs, + _latest_cms_collection_end_to_collection_start_secs, + concurrent_processor_fraction()); + } + double interval_in_seconds = + _latest_cms_initial_mark_start_to_end_time_secs + + _latest_cms_remark_start_to_end_time_secs + + latest_cms_sum_concurrent_phases_time_secs + + _latest_cms_collection_end_to_collection_start_secs; + assert(interval_in_seconds >= 0.0, + "Bad interval between cms collections"); + + // Sample for performance counter + avg_concurrent_interval()->sample(interval_in_seconds); + + // STW costs (initial and remark pauses) + // Cost of collection (unit-less) + assert(_latest_cms_initial_mark_start_to_end_time_secs >= 0.0, + "Bad initial mark pause"); + assert(_latest_cms_remark_start_to_end_time_secs >= 0.0, + "Bad remark pause"); + double STW_time_in_seconds = + _latest_cms_initial_mark_start_to_end_time_secs + + _latest_cms_remark_start_to_end_time_secs; + double STW_collection_cost = 0.0; + if (interval_in_seconds > 0.0) { + // cost for the STW phases of the concurrent collection. + STW_collection_cost = STW_time_in_seconds / interval_in_seconds; + avg_cms_STW_gc_cost()->sample(STW_collection_cost); + } + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print("cmsAdaptiveSizePolicy::STW_collection_end: " + "STW gc cost: %f average: %f", STW_collection_cost, + avg_cms_STW_gc_cost()->average()); + gclog_or_tty->print_cr(" STW pause: %f (ms) STW period %f (ms)", + (double) STW_time_in_seconds * MILLIUNITS, + (double) interval_in_seconds * MILLIUNITS); + } + + double concurrent_cost = 0.0; + if (latest_cms_sum_concurrent_phases_time_secs > 0.0) { + concurrent_cost = concurrent_collection_cost(interval_in_seconds); + + avg_concurrent_gc_cost()->sample(concurrent_cost); + // Average this ms cost into all the other types gc costs + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print("cmsAdaptiveSizePolicy::concurrent_phases_end: " + "concurrent gc cost: %f average: %f", + concurrent_cost, + _avg_concurrent_gc_cost->average()); + gclog_or_tty->print_cr(" concurrent time: %f (ms) cms period %f (ms)" + " processor fraction: %f", + latest_cms_sum_concurrent_phases_time_secs * MILLIUNITS, + interval_in_seconds * MILLIUNITS, + concurrent_processor_fraction()); + } + } + double total_collection_cost = STW_collection_cost + concurrent_cost; + avg_major_gc_cost()->sample(total_collection_cost); + + // Gather information for estimating future behavior + double initial_pause_in_ms = _latest_cms_initial_mark_start_to_end_time_secs * MILLIUNITS; + double remark_pause_in_ms = _latest_cms_remark_start_to_end_time_secs * MILLIUNITS; + + double cur_promo_size_in_mbytes = ((double)cur_promo)/((double)M); + initial_pause_old_estimator()->update(cur_promo_size_in_mbytes, + initial_pause_in_ms); + remark_pause_old_estimator()->update(cur_promo_size_in_mbytes, + remark_pause_in_ms); + major_collection_estimator()->update(cur_promo_size_in_mbytes, + total_collection_cost); + + // This estimate uses the average eden size. It could also + // have used the latest eden size. Which is better? + double cur_eden_size_in_mbytes = ((double)cur_eden)/((double) M); + initial_pause_young_estimator()->update(cur_eden_size_in_mbytes, + initial_pause_in_ms); + remark_pause_young_estimator()->update(cur_eden_size_in_mbytes, + remark_pause_in_ms); + } + + clear_internal_time_intervals(); + + set_first_after_collection(); + + // The concurrent phases keeps track of it's own mutator interval + // with this timer. This allows the stop-the-world phase to + // be included in the mutator time so that the stop-the-world time + // is not double counted. Reset and start it. + _concurrent_timer.reset(); + _concurrent_timer.start(); + + // The mutator time between STW phases does not include the + // concurrent collection time. + _STW_timer.reset(); + _STW_timer.start(); +} + +void CMSAdaptiveSizePolicy::checkpoint_roots_initial_begin() { + // Update the interval time + _STW_timer.stop(); + _latest_cms_reset_end_to_initial_mark_start_secs = _STW_timer.seconds(); + // Reset for the initial mark + _STW_timer.reset(); + _STW_timer.start(); +} + +void CMSAdaptiveSizePolicy::checkpoint_roots_initial_end( + GCCause::Cause gc_cause) { + _STW_timer.stop(); + + if (gc_cause != GCCause::_java_lang_system_gc || + UseAdaptiveSizePolicyWithSystemGC) { + _latest_cms_initial_mark_start_to_end_time_secs = _STW_timer.seconds(); + avg_initial_pause()->sample(_latest_cms_initial_mark_start_to_end_time_secs); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print( + "cmsAdaptiveSizePolicy::checkpoint_roots_initial_end: " + "initial pause: %f ", _latest_cms_initial_mark_start_to_end_time_secs); + } + } + + _STW_timer.reset(); + _STW_timer.start(); +} + +void CMSAdaptiveSizePolicy::checkpoint_roots_final_begin() { + _STW_timer.stop(); + _latest_cms_initial_mark_end_to_remark_start_secs = _STW_timer.seconds(); + // Start accumumlating time for the remark in the STW timer. + _STW_timer.reset(); + _STW_timer.start(); +} + +void CMSAdaptiveSizePolicy::checkpoint_roots_final_end( + GCCause::Cause gc_cause) { + _STW_timer.stop(); + if (gc_cause != GCCause::_java_lang_system_gc || + UseAdaptiveSizePolicyWithSystemGC) { + // Total initial mark pause + remark pause. + _latest_cms_remark_start_to_end_time_secs = _STW_timer.seconds(); + double STW_time_in_seconds = _latest_cms_initial_mark_start_to_end_time_secs + + _latest_cms_remark_start_to_end_time_secs; + double STW_time_in_ms = STW_time_in_seconds * MILLIUNITS; + + avg_remark_pause()->sample(_latest_cms_remark_start_to_end_time_secs); + + // Sample total for initial mark + remark + avg_cms_STW_time()->sample(STW_time_in_seconds); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print("cmsAdaptiveSizePolicy::checkpoint_roots_final_end: " + "remark pause: %f", _latest_cms_remark_start_to_end_time_secs); + } + + } + // Don't start the STW times here because the concurrent + // sweep and reset has not happened. + // Keep the old comment above in case I don't understand + // what is going on but now + // Start the STW timer because it is used by ms_collection_begin() + // and ms_collection_end() to get the sweep time if a MS is being + // done in the foreground. + _STW_timer.reset(); + _STW_timer.start(); +} + +void CMSAdaptiveSizePolicy::msc_collection_begin() { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print(" "); + gclog_or_tty->stamp(); + gclog_or_tty->print(": msc_collection_begin "); + } + _STW_timer.stop(); + _latest_cms_msc_end_to_msc_start_time_secs = _STW_timer.seconds(); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("CMSAdaptiveSizePolicy::msc_collection_begin: " + "mutator time %f", + _latest_cms_msc_end_to_msc_start_time_secs); + } + avg_msc_interval()->sample(_latest_cms_msc_end_to_msc_start_time_secs); + _STW_timer.reset(); + _STW_timer.start(); +} + +void CMSAdaptiveSizePolicy::msc_collection_end(GCCause::Cause gc_cause) { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print(" "); + gclog_or_tty->stamp(); + gclog_or_tty->print(": msc_collection_end "); + } + _STW_timer.stop(); + if (gc_cause != GCCause::_java_lang_system_gc || + UseAdaptiveSizePolicyWithSystemGC) { + double msc_pause_in_seconds = _STW_timer.seconds(); + if ((_latest_cms_msc_end_to_msc_start_time_secs > 0.0) && + (msc_pause_in_seconds > 0.0)) { + avg_msc_pause()->sample(msc_pause_in_seconds); + double mutator_time_in_seconds = 0.0; + if (_latest_cms_collection_end_to_collection_start_secs == 0.0) { + // This assertion may fail because of time stamp gradularity. + // Comment it out and investiage it at a later time. The large + // time stamp granularity occurs on some older linux systems. +#ifndef CLOCK_GRANULARITY_TOO_LARGE + assert((_latest_cms_concurrent_marking_time_secs == 0.0) && + (_latest_cms_concurrent_precleaning_time_secs == 0.0) && + (_latest_cms_concurrent_sweeping_time_secs == 0.0), + "There should not be any concurrent time"); +#endif + // A concurrent collection did not start. Mutator time + // between collections comes from the STW MSC timer. + mutator_time_in_seconds = _latest_cms_msc_end_to_msc_start_time_secs; + } else { + // The concurrent collection did start so count the mutator + // time to the start of the concurrent collection. In this + // case the _latest_cms_msc_end_to_msc_start_time_secs measures + // the time between the initial mark or remark and the + // start of the MSC. That has no real meaning. + mutator_time_in_seconds = _latest_cms_collection_end_to_collection_start_secs; + } + + double latest_cms_sum_concurrent_phases_time_secs = + concurrent_collection_time(); + double interval_in_seconds = + mutator_time_in_seconds + + _latest_cms_initial_mark_start_to_end_time_secs + + _latest_cms_remark_start_to_end_time_secs + + latest_cms_sum_concurrent_phases_time_secs + + msc_pause_in_seconds; + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" interval_in_seconds %f \n" + " mutator_time_in_seconds %f \n" + " _latest_cms_initial_mark_start_to_end_time_secs %f\n" + " _latest_cms_remark_start_to_end_time_secs %f\n" + " latest_cms_sum_concurrent_phases_time_secs %f\n" + " msc_pause_in_seconds %f\n", + interval_in_seconds, + mutator_time_in_seconds, + _latest_cms_initial_mark_start_to_end_time_secs, + _latest_cms_remark_start_to_end_time_secs, + latest_cms_sum_concurrent_phases_time_secs, + msc_pause_in_seconds); + } + + // The concurrent cost is wasted cost but it should be + // included. + double concurrent_cost = concurrent_collection_cost(interval_in_seconds); + + // Initial mark and remark, also wasted. + double STW_time_in_seconds = _latest_cms_initial_mark_start_to_end_time_secs + + _latest_cms_remark_start_to_end_time_secs; + double STW_collection_cost = + collection_cost(STW_time_in_seconds, interval_in_seconds) + + concurrent_cost; + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" msc_collection_end:\n" + "_latest_cms_collection_end_to_collection_start_secs %f\n" + "_latest_cms_msc_end_to_msc_start_time_secs %f\n" + "_latest_cms_initial_mark_start_to_end_time_secs %f\n" + "_latest_cms_remark_start_to_end_time_secs %f\n" + "latest_cms_sum_concurrent_phases_time_secs %f\n", + _latest_cms_collection_end_to_collection_start_secs, + _latest_cms_msc_end_to_msc_start_time_secs, + _latest_cms_initial_mark_start_to_end_time_secs, + _latest_cms_remark_start_to_end_time_secs, + latest_cms_sum_concurrent_phases_time_secs); + + gclog_or_tty->print_cr(" msc_collection_end: \n" + "latest_cms_sum_concurrent_phases_time_secs %f\n" + "STW_time_in_seconds %f\n" + "msc_pause_in_seconds %f\n", + latest_cms_sum_concurrent_phases_time_secs, + STW_time_in_seconds, + msc_pause_in_seconds); + } + + double cost = concurrent_cost + STW_collection_cost + + collection_cost(msc_pause_in_seconds, interval_in_seconds); + + _avg_msc_gc_cost->sample(cost); + + // Average this ms cost into all the other types gc costs + avg_major_gc_cost()->sample(cost); + + // Sample for performance counter + _avg_msc_interval->sample(interval_in_seconds); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print("cmsAdaptiveSizePolicy::msc_collection_end: " + "MSC gc cost: %f average: %f", cost, + _avg_msc_gc_cost->average()); + + double msc_pause_in_ms = msc_pause_in_seconds * MILLIUNITS; + gclog_or_tty->print_cr(" MSC pause: %f (ms) MSC period %f (ms)", + msc_pause_in_ms, (double) interval_in_seconds * MILLIUNITS); + } + } + } + + clear_internal_time_intervals(); + + // Can this call be put into the epilogue? + set_first_after_collection(); + + // The concurrent phases keeps track of it's own mutator interval + // with this timer. This allows the stop-the-world phase to + // be included in the mutator time so that the stop-the-world time + // is not double counted. Reset and start it. + _concurrent_timer.stop(); + _concurrent_timer.reset(); + _concurrent_timer.start(); + + _STW_timer.reset(); + _STW_timer.start(); +} + +void CMSAdaptiveSizePolicy::ms_collection_begin() { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print(" "); + gclog_or_tty->stamp(); + gclog_or_tty->print(": ms_collection_begin "); + } + _STW_timer.stop(); + _latest_cms_ms_end_to_ms_start = _STW_timer.seconds(); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("CMSAdaptiveSizePolicy::ms_collection_begin: " + "mutator time %f", + _latest_cms_ms_end_to_ms_start); + } + avg_ms_interval()->sample(_STW_timer.seconds()); + _STW_timer.reset(); + _STW_timer.start(); +} + +void CMSAdaptiveSizePolicy::ms_collection_end(GCCause::Cause gc_cause) { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print(" "); + gclog_or_tty->stamp(); + gclog_or_tty->print(": ms_collection_end "); + } + _STW_timer.stop(); + if (gc_cause != GCCause::_java_lang_system_gc || + UseAdaptiveSizePolicyWithSystemGC) { + // The MS collection is a foreground collection that does all + // the parts of a mostly concurrent collection. + // + // For this collection include the cost of the + // initial mark + // remark + // all concurrent time (scaled down by the + // concurrent_processor_fraction). Some + // may be zero if the baton was passed before + // it was reached. + // concurrent marking + // sweeping + // resetting + // STW after baton was passed (STW_in_foreground_in_seconds) + double STW_in_foreground_in_seconds = _STW_timer.seconds(); + + double latest_cms_sum_concurrent_phases_time_secs = + concurrent_collection_time(); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("\nCMSAdaptiveSizePolicy::ms_collecton_end " + "STW_in_foreground_in_seconds %f " + "_latest_cms_initial_mark_start_to_end_time_secs %f " + "_latest_cms_remark_start_to_end_time_secs %f " + "latest_cms_sum_concurrent_phases_time_secs %f " + "_latest_cms_ms_marking_start_to_end_time_secs %f " + "_latest_cms_ms_end_to_ms_start %f", + STW_in_foreground_in_seconds, + _latest_cms_initial_mark_start_to_end_time_secs, + _latest_cms_remark_start_to_end_time_secs, + latest_cms_sum_concurrent_phases_time_secs, + _latest_cms_ms_marking_start_to_end_time_secs, + _latest_cms_ms_end_to_ms_start); + } + + double STW_marking_in_seconds = _latest_cms_initial_mark_start_to_end_time_secs + + _latest_cms_remark_start_to_end_time_secs; +#ifndef CLOCK_GRANULARITY_TOO_LARGE + assert(_latest_cms_ms_marking_start_to_end_time_secs == 0.0 || + latest_cms_sum_concurrent_phases_time_secs == 0.0, + "marking done twice?"); +#endif + double ms_time_in_seconds = STW_marking_in_seconds + + STW_in_foreground_in_seconds + + _latest_cms_ms_marking_start_to_end_time_secs + + scaled_concurrent_collection_time(); + avg_ms_pause()->sample(ms_time_in_seconds); + // Use the STW costs from the initial mark and remark plus + // the cost of the concurrent phase to calculate a + // collection cost. + double cost = 0.0; + if ((_latest_cms_ms_end_to_ms_start > 0.0) && + (ms_time_in_seconds > 0.0)) { + double interval_in_seconds = + _latest_cms_ms_end_to_ms_start + ms_time_in_seconds; + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("\n ms_time_in_seconds %f " + "latest_cms_sum_concurrent_phases_time_secs %f " + "interval_in_seconds %f", + ms_time_in_seconds, + latest_cms_sum_concurrent_phases_time_secs, + interval_in_seconds); + } + + cost = collection_cost(ms_time_in_seconds, interval_in_seconds); + + _avg_ms_gc_cost->sample(cost); + // Average this ms cost into all the other types gc costs + avg_major_gc_cost()->sample(cost); + + // Sample for performance counter + _avg_ms_interval->sample(interval_in_seconds); + } + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print("cmsAdaptiveSizePolicy::ms_collection_end: " + "MS gc cost: %f average: %f", cost, _avg_ms_gc_cost->average()); + + double ms_time_in_ms = ms_time_in_seconds * MILLIUNITS; + gclog_or_tty->print_cr(" MS pause: %f (ms) MS period %f (ms)", + ms_time_in_ms, + _latest_cms_ms_end_to_ms_start * MILLIUNITS); + } + } + + // Consider putting this code (here to end) into a + // method for convenience. + clear_internal_time_intervals(); + + set_first_after_collection(); + + // The concurrent phases keeps track of it's own mutator interval + // with this timer. This allows the stop-the-world phase to + // be included in the mutator time so that the stop-the-world time + // is not double counted. Reset and start it. + _concurrent_timer.stop(); + _concurrent_timer.reset(); + _concurrent_timer.start(); + + _STW_timer.reset(); + _STW_timer.start(); +} + +void CMSAdaptiveSizePolicy::clear_internal_time_intervals() { + _latest_cms_reset_end_to_initial_mark_start_secs = 0.0; + _latest_cms_initial_mark_end_to_remark_start_secs = 0.0; + _latest_cms_collection_end_to_collection_start_secs = 0.0; + _latest_cms_concurrent_marking_time_secs = 0.0; + _latest_cms_concurrent_precleaning_time_secs = 0.0; + _latest_cms_concurrent_sweeping_time_secs = 0.0; + _latest_cms_msc_end_to_msc_start_time_secs = 0.0; + _latest_cms_ms_end_to_ms_start = 0.0; + _latest_cms_remark_start_to_end_time_secs = 0.0; + _latest_cms_initial_mark_start_to_end_time_secs = 0.0; + _latest_cms_ms_marking_start_to_end_time_secs = 0.0; +} + +void CMSAdaptiveSizePolicy::clear_generation_free_space_flags() { + AdaptiveSizePolicy::clear_generation_free_space_flags(); + + set_change_young_gen_for_maj_pauses(0); +} + +void CMSAdaptiveSizePolicy::concurrent_phases_resume() { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->stamp(); + gclog_or_tty->print_cr("CMSAdaptiveSizePolicy::concurrent_phases_resume()"); + } + _concurrent_timer.start(); +} + +double CMSAdaptiveSizePolicy::time_since_major_gc() const { + _concurrent_timer.stop(); + double time_since_cms_gc = _concurrent_timer.seconds(); + _concurrent_timer.start(); + _STW_timer.stop(); + double time_since_STW_gc = _STW_timer.seconds(); + _STW_timer.start(); + + return MIN2(time_since_cms_gc, time_since_STW_gc); +} + +double CMSAdaptiveSizePolicy::major_gc_interval_average_for_decay() const { + double cms_interval = _avg_concurrent_interval->average(); + double msc_interval = _avg_msc_interval->average(); + double ms_interval = _avg_ms_interval->average(); + + return MAX3(cms_interval, msc_interval, ms_interval); +} + +double CMSAdaptiveSizePolicy::cms_gc_cost() const { + return avg_major_gc_cost()->average(); +} + +void CMSAdaptiveSizePolicy::ms_collection_marking_begin() { + _STW_timer.stop(); + // Start accumumlating time for the marking in the STW timer. + _STW_timer.reset(); + _STW_timer.start(); +} + +void CMSAdaptiveSizePolicy::ms_collection_marking_end( + GCCause::Cause gc_cause) { + _STW_timer.stop(); + if (gc_cause != GCCause::_java_lang_system_gc || + UseAdaptiveSizePolicyWithSystemGC) { + _latest_cms_ms_marking_start_to_end_time_secs = _STW_timer.seconds(); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("CMSAdaptiveSizePolicy::" + "msc_collection_marking_end: mutator time %f", + _latest_cms_ms_marking_start_to_end_time_secs); + } + } + _STW_timer.reset(); + _STW_timer.start(); +} + +double CMSAdaptiveSizePolicy::gc_cost() const { + double cms_gen_cost = cms_gc_cost(); + double result = MIN2(1.0, minor_gc_cost() + cms_gen_cost); + assert(result >= 0.0, "Both minor and major costs are non-negative"); + return result; +} + +// Cost of collection (unit-less) +double CMSAdaptiveSizePolicy::collection_cost(double pause_in_seconds, + double interval_in_seconds) { + // Cost of collection (unit-less) + double cost = 0.0; + if ((interval_in_seconds > 0.0) && + (pause_in_seconds > 0.0)) { + cost = + pause_in_seconds / interval_in_seconds; + } + return cost; +} + +size_t CMSAdaptiveSizePolicy::adjust_eden_for_pause_time(size_t cur_eden) { + size_t change = 0; + size_t desired_eden = cur_eden; + + // reduce eden size + change = eden_decrement_aligned_down(cur_eden); + desired_eden = cur_eden - change; + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::adjust_eden_for_pause_time " + "adjusting eden for pause time. " + " starting eden size " SIZE_FORMAT + " reduced eden size " SIZE_FORMAT + " eden delta " SIZE_FORMAT, + cur_eden, desired_eden, change); + } + + return desired_eden; +} + +size_t CMSAdaptiveSizePolicy::adjust_eden_for_throughput(size_t cur_eden) { + + size_t desired_eden = cur_eden; + + set_change_young_gen_for_throughput(increase_young_gen_for_througput_true); + + size_t change = eden_increment_aligned_up(cur_eden); + size_t scaled_change = scale_by_gen_gc_cost(change, minor_gc_cost()); + + if (cur_eden + scaled_change > cur_eden) { + desired_eden = cur_eden + scaled_change; + } + + _young_gen_change_for_minor_throughput++; + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::adjust_eden_for_throughput " + "adjusting eden for throughput. " + " starting eden size " SIZE_FORMAT + " increased eden size " SIZE_FORMAT + " eden delta " SIZE_FORMAT, + cur_eden, desired_eden, scaled_change); + } + + return desired_eden; +} + +size_t CMSAdaptiveSizePolicy::adjust_eden_for_footprint(size_t cur_eden) { + + set_decrease_for_footprint(decrease_young_gen_for_footprint_true); + + size_t change = eden_decrement(cur_eden); + size_t desired_eden_size = cur_eden - change; + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::adjust_eden_for_footprint " + "adjusting eden for footprint. " + " starting eden size " SIZE_FORMAT + " reduced eden size " SIZE_FORMAT + " eden delta " SIZE_FORMAT, + cur_eden, desired_eden_size, change); + } + return desired_eden_size; +} + +// The eden and promo versions should be combined if possible. +// They are the same except that the sizes of the decrement +// and increment are different for eden and promo. +size_t CMSAdaptiveSizePolicy::eden_decrement_aligned_down(size_t cur_eden) { + size_t delta = eden_decrement(cur_eden); + return align_size_down(delta, generation_alignment()); +} + +size_t CMSAdaptiveSizePolicy::eden_increment_aligned_up(size_t cur_eden) { + size_t delta = eden_increment(cur_eden); + return align_size_up(delta, generation_alignment()); +} + +size_t CMSAdaptiveSizePolicy::promo_decrement_aligned_down(size_t cur_promo) { + size_t delta = promo_decrement(cur_promo); + return align_size_down(delta, generation_alignment()); +} + +size_t CMSAdaptiveSizePolicy::promo_increment_aligned_up(size_t cur_promo) { + size_t delta = promo_increment(cur_promo); + return align_size_up(delta, generation_alignment()); +} + + +void CMSAdaptiveSizePolicy::compute_young_generation_free_space(size_t cur_eden, + size_t max_eden_size) +{ + size_t desired_eden_size = cur_eden; + size_t eden_limit = max_eden_size; + + // Printout input + if (PrintGC && PrintAdaptiveSizePolicy) { + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::compute_young_generation_free_space: " + "cur_eden " SIZE_FORMAT, + cur_eden); + } + + // Used for diagnostics + clear_generation_free_space_flags(); + + if (_avg_minor_pause->padded_average() > gc_pause_goal_sec()) { + if (minor_pause_young_estimator()->decrement_will_decrease()) { + // If the minor pause is too long, shrink the young gen. + set_change_young_gen_for_min_pauses( + decrease_young_gen_for_min_pauses_true); + desired_eden_size = adjust_eden_for_pause_time(desired_eden_size); + } + } else if ((avg_remark_pause()->padded_average() > gc_pause_goal_sec()) || + (avg_initial_pause()->padded_average() > gc_pause_goal_sec())) { + // The remark or initial pauses are not meeting the goal. Should + // the generation be shrunk? + if (get_and_clear_first_after_collection() && + ((avg_remark_pause()->padded_average() > gc_pause_goal_sec() && + remark_pause_young_estimator()->decrement_will_decrease()) || + (avg_initial_pause()->padded_average() > gc_pause_goal_sec() && + initial_pause_young_estimator()->decrement_will_decrease()))) { + + set_change_young_gen_for_maj_pauses( + decrease_young_gen_for_maj_pauses_true); + + // If the remark or initial pause is too long and this is the + // first young gen collection after a cms collection, shrink + // the young gen. + desired_eden_size = adjust_eden_for_pause_time(desired_eden_size); + } + // If not the first young gen collection after a cms collection, + // don't do anything. In this case an adjustment has already + // been made and the results of the adjustment has not yet been + // measured. + } else if ((minor_gc_cost() >= 0.0) && + (adjusted_mutator_cost() < _throughput_goal)) { + desired_eden_size = adjust_eden_for_throughput(desired_eden_size); + } else { + desired_eden_size = adjust_eden_for_footprint(desired_eden_size); + } + + if (PrintGC && PrintAdaptiveSizePolicy) { + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::compute_young_generation_free_space limits:" + " desired_eden_size: " SIZE_FORMAT + " old_eden_size: " SIZE_FORMAT, + desired_eden_size, cur_eden); + } + + set_eden_size(desired_eden_size); +} + +size_t CMSAdaptiveSizePolicy::adjust_promo_for_pause_time(size_t cur_promo) { + size_t change = 0; + size_t desired_promo = cur_promo; + // Move this test up to caller like the adjust_eden_for_pause_time() + // call. + if ((AdaptiveSizePausePolicy == 0) && + ((avg_remark_pause()->padded_average() > gc_pause_goal_sec()) || + (avg_initial_pause()->padded_average() > gc_pause_goal_sec()))) { + set_change_old_gen_for_maj_pauses(decrease_old_gen_for_maj_pauses_true); + change = promo_decrement_aligned_down(cur_promo); + desired_promo = cur_promo - change; + } else if ((AdaptiveSizePausePolicy > 0) && + (((avg_remark_pause()->padded_average() > gc_pause_goal_sec()) && + remark_pause_old_estimator()->decrement_will_decrease()) || + ((avg_initial_pause()->padded_average() > gc_pause_goal_sec()) && + initial_pause_old_estimator()->decrement_will_decrease()))) { + set_change_old_gen_for_maj_pauses(decrease_old_gen_for_maj_pauses_true); + change = promo_decrement_aligned_down(cur_promo); + desired_promo = cur_promo - change; + } + + if ((change != 0) &&PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::adjust_promo_for_pause_time " + "adjusting promo for pause time. " + " starting promo size " SIZE_FORMAT + " reduced promo size " SIZE_FORMAT + " promo delta " SIZE_FORMAT, + cur_promo, desired_promo, change); + } + + return desired_promo; +} + +// Try to share this with PS. +size_t CMSAdaptiveSizePolicy::scale_by_gen_gc_cost(size_t base_change, + double gen_gc_cost) { + + // Calculate the change to use for the tenured gen. + size_t scaled_change = 0; + // Can the increment to the generation be scaled? + if (gc_cost() >= 0.0 && gen_gc_cost >= 0.0) { + double scale_by_ratio = gen_gc_cost / gc_cost(); + scaled_change = + (size_t) (scale_by_ratio * (double) base_change); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "Scaled tenured increment: " SIZE_FORMAT " by %f down to " + SIZE_FORMAT, + base_change, scale_by_ratio, scaled_change); + } + } else if (gen_gc_cost >= 0.0) { + // Scaling is not going to work. If the major gc time is the + // larger than the other GC costs, give it a full increment. + if (gen_gc_cost >= (gc_cost() - gen_gc_cost)) { + scaled_change = base_change; + } + } else { + // Don't expect to get here but it's ok if it does + // in the product build since the delta will be 0 + // and nothing will change. + assert(false, "Unexpected value for gc costs"); + } + + return scaled_change; +} + +size_t CMSAdaptiveSizePolicy::adjust_promo_for_throughput(size_t cur_promo) { + + size_t desired_promo = cur_promo; + + set_change_old_gen_for_throughput(increase_old_gen_for_throughput_true); + + size_t change = promo_increment_aligned_up(cur_promo); + size_t scaled_change = scale_by_gen_gc_cost(change, major_gc_cost()); + + if (cur_promo + scaled_change > cur_promo) { + desired_promo = cur_promo + scaled_change; + } + + _old_gen_change_for_major_throughput++; + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::adjust_promo_for_throughput " + "adjusting promo for throughput. " + " starting promo size " SIZE_FORMAT + " increased promo size " SIZE_FORMAT + " promo delta " SIZE_FORMAT, + cur_promo, desired_promo, scaled_change); + } + + return desired_promo; +} + +size_t CMSAdaptiveSizePolicy::adjust_promo_for_footprint(size_t cur_promo, + size_t cur_eden) { + + set_decrease_for_footprint(decrease_young_gen_for_footprint_true); + + size_t change = promo_decrement(cur_promo); + size_t desired_promo_size = cur_promo - change; + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::adjust_promo_for_footprint " + "adjusting promo for footprint. " + " starting promo size " SIZE_FORMAT + " reduced promo size " SIZE_FORMAT + " promo delta " SIZE_FORMAT, + cur_promo, desired_promo_size, change); + } + return desired_promo_size; +} + +void CMSAdaptiveSizePolicy::compute_tenured_generation_free_space( + size_t cur_tenured_free, + size_t max_tenured_available, + size_t cur_eden) { + // This can be bad if the desired value grows/shrinks without + // any connection to the read free space + size_t desired_promo_size = promo_size(); + size_t tenured_limit = max_tenured_available; + + // Printout input + if (PrintGC && PrintAdaptiveSizePolicy) { + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::compute_tenured_generation_free_space: " + "cur_tenured_free " SIZE_FORMAT + " max_tenured_available " SIZE_FORMAT, + cur_tenured_free, max_tenured_available); + } + + // Used for diagnostics + clear_generation_free_space_flags(); + + set_decide_at_full_gc(decide_at_full_gc_true); + if (avg_remark_pause()->padded_average() > gc_pause_goal_sec() || + avg_initial_pause()->padded_average() > gc_pause_goal_sec()) { + desired_promo_size = adjust_promo_for_pause_time(cur_tenured_free); + } else if (avg_minor_pause()->padded_average() > gc_pause_goal_sec()) { + // Nothing to do since the minor collections are too large and + // this method only deals with the cms generation. + } else if ((cms_gc_cost() >= 0.0) && + (adjusted_mutator_cost() < _throughput_goal)) { + desired_promo_size = adjust_promo_for_throughput(cur_tenured_free); + } else { + desired_promo_size = adjust_promo_for_footprint(cur_tenured_free, + cur_eden); + } + + if (PrintGC && PrintAdaptiveSizePolicy) { + gclog_or_tty->print_cr( + "CMSAdaptiveSizePolicy::compute_tenured_generation_free_space limits:" + " desired_promo_size: " SIZE_FORMAT + " old_promo_size: " SIZE_FORMAT, + desired_promo_size, cur_tenured_free); + } + + set_promo_size(desired_promo_size); +} + +int CMSAdaptiveSizePolicy::compute_survivor_space_size_and_threshold( + bool is_survivor_overflow, + int tenuring_threshold, + size_t survivor_limit) { + assert(survivor_limit >= generation_alignment(), + "survivor_limit too small"); + assert((size_t)align_size_down(survivor_limit, generation_alignment()) + == survivor_limit, "survivor_limit not aligned"); + + // Change UsePSAdaptiveSurvivorSizePolicy -> UseAdaptiveSurvivorSizePolicy? + if (!UsePSAdaptiveSurvivorSizePolicy || + !young_gen_policy_is_ready()) { + return tenuring_threshold; + } + + // We'll decide whether to increase or decrease the tenuring + // threshold based partly on the newly computed survivor size + // (if we hit the maximum limit allowed, we'll always choose to + // decrement the threshold). + bool incr_tenuring_threshold = false; + bool decr_tenuring_threshold = false; + + set_decrement_tenuring_threshold_for_gc_cost(false); + set_increment_tenuring_threshold_for_gc_cost(false); + set_decrement_tenuring_threshold_for_survivor_limit(false); + + if (!is_survivor_overflow) { + // Keep running averages on how much survived + + // We use the tenuring threshold to equalize the cost of major + // and minor collections. + // ThresholdTolerance is used to indicate how sensitive the + // tenuring threshold is to differences in cost betweent the + // collection types. + + // Get the times of interest. This involves a little work, so + // we cache the values here. + const double major_cost = major_gc_cost(); + const double minor_cost = minor_gc_cost(); + + if (minor_cost > major_cost * _threshold_tolerance_percent) { + // Minor times are getting too long; lower the threshold so + // less survives and more is promoted. + decr_tenuring_threshold = true; + set_decrement_tenuring_threshold_for_gc_cost(true); + } else if (major_cost > minor_cost * _threshold_tolerance_percent) { + // Major times are too long, so we want less promotion. + incr_tenuring_threshold = true; + set_increment_tenuring_threshold_for_gc_cost(true); + } + + } else { + // Survivor space overflow occurred, so promoted and survived are + // not accurate. We'll make our best guess by combining survived + // and promoted and count them as survivors. + // + // We'll lower the tenuring threshold to see if we can correct + // things. Also, set the survivor size conservatively. We're + // trying to avoid many overflows from occurring if defnew size + // is just too small. + + decr_tenuring_threshold = true; + } + + // The padded average also maintains a deviation from the average; + // we use this to see how good of an estimate we have of what survived. + // We're trying to pad the survivor size as little as possible without + // overflowing the survivor spaces. + size_t target_size = align_size_up((size_t)_avg_survived->padded_average(), + generation_alignment()); + target_size = MAX2(target_size, generation_alignment()); + + if (target_size > survivor_limit) { + // Target size is bigger than we can handle. Let's also reduce + // the tenuring threshold. + target_size = survivor_limit; + decr_tenuring_threshold = true; + set_decrement_tenuring_threshold_for_survivor_limit(true); + } + + // Finally, increment or decrement the tenuring threshold, as decided above. + // We test for decrementing first, as we might have hit the target size + // limit. + if (decr_tenuring_threshold && !(AlwaysTenure || NeverTenure)) { + if (tenuring_threshold > 1) { + tenuring_threshold--; + } + } else if (incr_tenuring_threshold && !(AlwaysTenure || NeverTenure)) { + if (tenuring_threshold < MaxTenuringThreshold) { + tenuring_threshold++; + } + } + + // We keep a running average of the amount promoted which is used + // to decide when we should collect the old generation (when + // the amount of old gen free space is less than what we expect to + // promote). + + if (PrintAdaptiveSizePolicy) { + // A little more detail if Verbose is on + GenCollectedHeap* gch = GenCollectedHeap::heap(); + if (Verbose) { + gclog_or_tty->print( " avg_survived: %f" + " avg_deviation: %f", + _avg_survived->average(), + _avg_survived->deviation()); + } + + gclog_or_tty->print( " avg_survived_padded_avg: %f", + _avg_survived->padded_average()); + + if (Verbose) { + gclog_or_tty->print( " avg_promoted_avg: %f" + " avg_promoted_dev: %f", + gch->gc_stats(1)->avg_promoted()->average(), + gch->gc_stats(1)->avg_promoted()->deviation()); + } + + gclog_or_tty->print( " avg_promoted_padded_avg: %f" + " avg_pretenured_padded_avg: %f" + " tenuring_thresh: %d" + " target_size: " SIZE_FORMAT + " survivor_limit: " SIZE_FORMAT, + gch->gc_stats(1)->avg_promoted()->padded_average(), + _avg_pretenured->padded_average(), + tenuring_threshold, target_size, survivor_limit); + gclog_or_tty->cr(); + } + + set_survivor_size(target_size); + + return tenuring_threshold; +} + +bool CMSAdaptiveSizePolicy::get_and_clear_first_after_collection() { + bool result = _first_after_collection; + _first_after_collection = false; + return result; +} + +bool CMSAdaptiveSizePolicy::print_adaptive_size_policy_on( + outputStream* st) const { + + if (!UseAdaptiveSizePolicy) return false; + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + Generation* gen0 = gch->get_gen(0); + DefNewGeneration* def_new = gen0->as_DefNewGeneration(); + return + AdaptiveSizePolicy::print_adaptive_size_policy_on( + st, + def_new->tenuring_threshold()); +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.hpp new file mode 100644 index 00000000000..3a07470e395 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.hpp @@ -0,0 +1,469 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This class keeps statistical information and computes the +// size of the heap for the concurrent mark sweep collector. +// +// Cost for garbage collector include cost for +// minor collection +// concurrent collection +// stop-the-world component +// concurrent component +// major compacting collection +// uses decaying cost + +// Forward decls +class elapsedTimer; + +class CMSAdaptiveSizePolicy : public AdaptiveSizePolicy { + friend class CMSGCAdaptivePolicyCounters; + friend class CMSCollector; + private: + + // Total number of processors available + int _processor_count; + // Number of processors used by the concurrent phases of GC + // This number is assumed to be the same for all concurrent + // phases. + int _concurrent_processor_count; + + // Time that the mutators run exclusive of a particular + // phase. For example, the time the mutators run excluding + // the time during which the cms collector runs concurrently + // with the mutators. + // Between end of most recent cms reset and start of initial mark + // This may be redundant + double _latest_cms_reset_end_to_initial_mark_start_secs; + // Between end of the most recent initial mark and start of remark + double _latest_cms_initial_mark_end_to_remark_start_secs; + // Between end of most recent collection and start of + // a concurrent collection + double _latest_cms_collection_end_to_collection_start_secs; + // Times of the concurrent phases of the most recent + // concurrent collection + double _latest_cms_concurrent_marking_time_secs; + double _latest_cms_concurrent_precleaning_time_secs; + double _latest_cms_concurrent_sweeping_time_secs; + // Between end of most recent STW MSC and start of next STW MSC + double _latest_cms_msc_end_to_msc_start_time_secs; + // Between end of most recent MS and start of next MS + // This does not include any time spent during a concurrent + // collection. + double _latest_cms_ms_end_to_ms_start; + // Between start and end of the initial mark of the most recent + // concurrent collection. + double _latest_cms_initial_mark_start_to_end_time_secs; + // Between start and end of the remark phase of the most recent + // concurrent collection + double _latest_cms_remark_start_to_end_time_secs; + // Between start and end of the most recent MS STW marking phase + double _latest_cms_ms_marking_start_to_end_time_secs; + + // Pause time timers + static elapsedTimer _STW_timer; + // Concurrent collection timer. Used for total of all concurrent phases + // during 1 collection cycle. + static elapsedTimer _concurrent_timer; + + // When the size of the generation is changed, the size + // of the change will rounded up or down (depending on the + // type of change) by this value. + size_t _generation_alignment; + + // If this variable is true, the size of the young generation + // may be changed in order to reduce the pause(s) of the + // collection of the tenured generation in order to meet the + // pause time goal. It is common to change the size of the + // tenured generation in order to meet the pause time goal + // for the tenured generation. With the CMS collector for + // the tenured generation, the size of the young generation + // can have an significant affect on the pause times for collecting the + // tenured generation. + // This is a duplicate of a variable in PSAdaptiveSizePolicy. It + // is duplicated because it is not clear that it is general enough + // to go into AdaptiveSizePolicy. + int _change_young_gen_for_maj_pauses; + + // Variable that is set to true after a collection. + bool _first_after_collection; + + // Fraction of collections that are of each type + double concurrent_fraction() const; + double STW_msc_fraction() const; + double STW_ms_fraction() const; + + // This call cannot be put into the epilogue as long as some + // of the counters can be set during concurrent phases. + virtual void clear_generation_free_space_flags(); + + void set_first_after_collection() { _first_after_collection = true; } + + protected: + // Average of the sum of the concurrent times for + // one collection in seconds. + AdaptiveWeightedAverage* _avg_concurrent_time; + // Average time between concurrent collections in seconds. + AdaptiveWeightedAverage* _avg_concurrent_interval; + // Average cost of the concurrent part of a collection + // in seconds. + AdaptiveWeightedAverage* _avg_concurrent_gc_cost; + + // Average of the initial pause of a concurrent collection in seconds. + AdaptivePaddedAverage* _avg_initial_pause; + // Average of the remark pause of a concurrent collection in seconds. + AdaptivePaddedAverage* _avg_remark_pause; + + // Average of the stop-the-world (STW) (initial mark + remark) + // times in seconds for concurrent collections. + AdaptiveWeightedAverage* _avg_cms_STW_time; + // Average of the STW collection cost for concurrent collections. + AdaptiveWeightedAverage* _avg_cms_STW_gc_cost; + + // Average of the bytes free at the start of the sweep. + AdaptiveWeightedAverage* _avg_cms_free_at_sweep; + // Average of the bytes free at the end of the collection. + AdaptiveWeightedAverage* _avg_cms_free; + // Average of the bytes promoted between cms collections. + AdaptiveWeightedAverage* _avg_cms_promo; + + // stop-the-world (STW) mark-sweep-compact + // Average of the pause time in seconds for STW mark-sweep-compact + // collections. + AdaptiveWeightedAverage* _avg_msc_pause; + // Average of the interval in seconds between STW mark-sweep-compact + // collections. + AdaptiveWeightedAverage* _avg_msc_interval; + // Average of the collection costs for STW mark-sweep-compact + // collections. + AdaptiveWeightedAverage* _avg_msc_gc_cost; + + // Averages for mark-sweep collections. + // The collection may have started as a background collection + // that completes in a stop-the-world (STW) collection. + // Average of the pause time in seconds for mark-sweep + // collections. + AdaptiveWeightedAverage* _avg_ms_pause; + // Average of the interval in seconds between mark-sweep + // collections. + AdaptiveWeightedAverage* _avg_ms_interval; + // Average of the collection costs for mark-sweep + // collections. + AdaptiveWeightedAverage* _avg_ms_gc_cost; + + // These variables contain a linear fit of + // a generation size as the independent variable + // and a pause time as the dependent variable. + // For example _remark_pause_old_estimator + // is a fit of the old generation size as the + // independent variable and the remark pause + // as the dependent variable. + // remark pause time vs. cms gen size + LinearLeastSquareFit* _remark_pause_old_estimator; + // initial pause time vs. cms gen size + LinearLeastSquareFit* _initial_pause_old_estimator; + // remark pause time vs. young gen size + LinearLeastSquareFit* _remark_pause_young_estimator; + // initial pause time vs. young gen size + LinearLeastSquareFit* _initial_pause_young_estimator; + + // Accessors + int processor_count() const { return _processor_count; } + int concurrent_processor_count() const { return _concurrent_processor_count; } + + AdaptiveWeightedAverage* avg_concurrent_time() const { + return _avg_concurrent_time; + } + + AdaptiveWeightedAverage* avg_concurrent_interval() const { + return _avg_concurrent_interval; + } + + AdaptiveWeightedAverage* avg_concurrent_gc_cost() const { + return _avg_concurrent_gc_cost; + } + + AdaptiveWeightedAverage* avg_cms_STW_time() const { + return _avg_cms_STW_time; + } + + AdaptiveWeightedAverage* avg_cms_STW_gc_cost() const { + return _avg_cms_STW_gc_cost; + } + + AdaptivePaddedAverage* avg_initial_pause() const { + return _avg_initial_pause; + } + + AdaptivePaddedAverage* avg_remark_pause() const { + return _avg_remark_pause; + } + + AdaptiveWeightedAverage* avg_cms_free() const { + return _avg_cms_free; + } + + AdaptiveWeightedAverage* avg_cms_free_at_sweep() const { + return _avg_cms_free_at_sweep; + } + + AdaptiveWeightedAverage* avg_msc_pause() const { + return _avg_msc_pause; + } + + AdaptiveWeightedAverage* avg_msc_interval() const { + return _avg_msc_interval; + } + + AdaptiveWeightedAverage* avg_msc_gc_cost() const { + return _avg_msc_gc_cost; + } + + AdaptiveWeightedAverage* avg_ms_pause() const { + return _avg_ms_pause; + } + + AdaptiveWeightedAverage* avg_ms_interval() const { + return _avg_ms_interval; + } + + AdaptiveWeightedAverage* avg_ms_gc_cost() const { + return _avg_ms_gc_cost; + } + + LinearLeastSquareFit* remark_pause_old_estimator() { + return _remark_pause_old_estimator; + } + LinearLeastSquareFit* initial_pause_old_estimator() { + return _initial_pause_old_estimator; + } + LinearLeastSquareFit* remark_pause_young_estimator() { + return _remark_pause_young_estimator; + } + LinearLeastSquareFit* initial_pause_young_estimator() { + return _initial_pause_young_estimator; + } + + // These *slope() methods return the slope + // m for the linear fit of an independent + // variable vs. a dependent variable. For + // example + // remark_pause = m * old_generation_size + c + // These may be used to determine if an + // adjustment should be made to achieve a goal. + // For example, if remark_pause_old_slope() is + // positive, a reduction of the old generation + // size has on average resulted in the reduction + // of the remark pause. + float remark_pause_old_slope() { + return _remark_pause_old_estimator->slope(); + } + + float initial_pause_old_slope() { + return _initial_pause_old_estimator->slope(); + } + + float remark_pause_young_slope() { + return _remark_pause_young_estimator->slope(); + } + + float initial_pause_young_slope() { + return _initial_pause_young_estimator->slope(); + } + + // Update estimators + void update_minor_pause_old_estimator(double minor_pause_in_ms); + + // Fraction of processors used by the concurrent phases. + double concurrent_processor_fraction(); + + // Returns the total times for the concurrent part of the + // latest collection in seconds. + double concurrent_collection_time(); + + // Return the total times for the concurrent part of the + // latest collection in seconds where the times of the various + // concurrent phases are scaled by the processor fraction used + // during the phase. + double scaled_concurrent_collection_time(); + + // Dimensionless concurrent GC cost for all the concurrent phases. + double concurrent_collection_cost(double interval_in_seconds); + + // Dimensionless GC cost + double collection_cost(double pause_in_seconds, double interval_in_seconds); + + virtual GCPolicyKind kind() const { return _gc_cms_adaptive_size_policy; } + + virtual double time_since_major_gc() const; + + // This returns the maximum average for the concurrent, ms, and + // msc collections. This is meant to be used for the calculation + // of the decayed major gc cost and is not in general the + // average of all the different types of major collections. + virtual double major_gc_interval_average_for_decay() const; + + public: + CMSAdaptiveSizePolicy(size_t init_eden_size, + size_t init_promo_size, + size_t init_survivor_size, + double max_gc_minor_pause_sec, + double max_gc_pause_sec, + uint gc_cost_ratio); + + // The timers for the stop-the-world phases measure a total + // stop-the-world time. The timer is started and stopped + // for each phase but is only reset after the final checkpoint. + void checkpoint_roots_initial_begin(); + void checkpoint_roots_initial_end(GCCause::Cause gc_cause); + void checkpoint_roots_final_begin(); + void checkpoint_roots_final_end(GCCause::Cause gc_cause); + + // Methods for gathering information about the + // concurrent marking phase of the collection. + // Records the mutator times and + // resets the concurrent timer. + void concurrent_marking_begin(); + // Resets concurrent phase timer in the begin methods and + // saves the time for a phase in the end methods. + void concurrent_marking_end(); + void concurrent_sweeping_begin(); + void concurrent_sweeping_end(); + // Similar to the above (e.g., concurrent_marking_end()) and + // is used for both the precleaning an abortable precleaing + // phases. + void concurrent_precleaning_begin(); + void concurrent_precleaning_end(); + // Stops the concurrent phases time. Gathers + // information and resets the timer. + void concurrent_phases_end(GCCause::Cause gc_cause, + size_t cur_eden, + size_t cur_promo); + + // Methods for gather information about STW Mark-Sweep-Compact + void msc_collection_begin(); + void msc_collection_end(GCCause::Cause gc_cause); + + // Methods for gather information about Mark-Sweep done + // in the foreground. + void ms_collection_begin(); + void ms_collection_end(GCCause::Cause gc_cause); + + // Cost for a mark-sweep tenured gen collection done in the foreground + double ms_gc_cost() const { + return MAX2(0.0F, _avg_ms_gc_cost->average()); + } + + // Cost of collecting the tenured generation. Includes + // concurrent collection and STW collection costs + double cms_gc_cost() const; + + // Cost of STW mark-sweep-compact tenured gen collection. + double msc_gc_cost() const { + return MAX2(0.0F, _avg_msc_gc_cost->average()); + } + + // + double compacting_gc_cost() const { + double result = MIN2(1.0, minor_gc_cost() + msc_gc_cost()); + assert(result >= 0.0, "Both minor and major costs are non-negative"); + return result; + } + + // Restarts the concurrent phases timer. + void concurrent_phases_resume(); + + // Time begining and end of the marking phase for + // a synchronous MS collection. A MS collection + // that finishes in the foreground can have started + // in the background. These methods capture the + // completion of the marking (after the initial + // marking) that is done in the foreground. + void ms_collection_marking_begin(); + void ms_collection_marking_end(GCCause::Cause gc_cause); + + static elapsedTimer* concurrent_timer_ptr() { + return &_concurrent_timer; + } + + AdaptiveWeightedAverage* avg_cms_promo() const { + return _avg_cms_promo; + } + + int change_young_gen_for_maj_pauses() { + return _change_young_gen_for_maj_pauses; + } + void set_change_young_gen_for_maj_pauses(int v) { + _change_young_gen_for_maj_pauses = v; + } + + void clear_internal_time_intervals(); + + + // Either calculated_promo_size_in_bytes() or promo_size() + // should be deleted. + size_t promo_size() { return _promo_size; } + void set_promo_size(size_t v) { _promo_size = v; } + + // Cost of GC for all types of collections. + virtual double gc_cost() const; + + size_t generation_alignment() { return _generation_alignment; } + + virtual void compute_young_generation_free_space(size_t cur_eden, + size_t max_eden_size); + // Calculates new survivor space size; returns a new tenuring threshold + // value. Stores new survivor size in _survivor_size. + virtual int compute_survivor_space_size_and_threshold( + bool is_survivor_overflow, + int tenuring_threshold, + size_t survivor_limit); + + virtual void compute_tenured_generation_free_space(size_t cur_tenured_free, + size_t max_tenured_available, + size_t cur_eden); + + size_t eden_decrement_aligned_down(size_t cur_eden); + size_t eden_increment_aligned_up(size_t cur_eden); + + size_t adjust_eden_for_pause_time(size_t cur_eden); + size_t adjust_eden_for_throughput(size_t cur_eden); + size_t adjust_eden_for_footprint(size_t cur_eden); + + size_t promo_decrement_aligned_down(size_t cur_promo); + size_t promo_increment_aligned_up(size_t cur_promo); + + size_t adjust_promo_for_pause_time(size_t cur_promo); + size_t adjust_promo_for_throughput(size_t cur_promo); + size_t adjust_promo_for_footprint(size_t cur_promo, size_t cur_eden); + + // Scale down the input size by the ratio of the cost to collect the + // generation to the total GC cost. + size_t scale_by_gen_gc_cost(size_t base_change, double gen_gc_cost); + + // Return the value and clear it. + bool get_and_clear_first_after_collection(); + + // Printing support + virtual bool print_adaptive_size_policy_on(outputStream* st) const; +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp new file mode 100644 index 00000000000..fa6e902b8f1 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_cmsCollectorPolicy.cpp.incl" + +// +// ConcurrentMarkSweepPolicy methods +// + +ConcurrentMarkSweepPolicy::ConcurrentMarkSweepPolicy() { + initialize_all(); +} + +void ConcurrentMarkSweepPolicy::initialize_generations() { + initialize_perm_generation(PermGen::ConcurrentMarkSweep); + _generations = new GenerationSpecPtr[number_of_generations()]; + if (_generations == NULL) + vm_exit_during_initialization("Unable to allocate gen spec"); + + if (UseParNewGC && ParallelGCThreads > 0) { + if (UseAdaptiveSizePolicy) { + _generations[0] = new GenerationSpec(Generation::ASParNew, + _initial_gen0_size, _max_gen0_size); + } else { + _generations[0] = new GenerationSpec(Generation::ParNew, + _initial_gen0_size, _max_gen0_size); + } + } else { + _generations[0] = new GenerationSpec(Generation::DefNew, + _initial_gen0_size, _max_gen0_size); + } + if (UseAdaptiveSizePolicy) { + _generations[1] = new GenerationSpec(Generation::ASConcurrentMarkSweep, + _initial_gen1_size, _max_gen1_size); + } else { + _generations[1] = new GenerationSpec(Generation::ConcurrentMarkSweep, + _initial_gen1_size, _max_gen1_size); + } + + if (_generations[0] == NULL || _generations[1] == NULL) { + vm_exit_during_initialization("Unable to allocate gen spec"); + } +} + +void ConcurrentMarkSweepPolicy::initialize_size_policy(size_t init_eden_size, + size_t init_promo_size, + size_t init_survivor_size) { + double max_gc_minor_pause_sec = ((double) MaxGCMinorPauseMillis)/1000.0; + double max_gc_pause_sec = ((double) MaxGCPauseMillis)/1000.0; + _size_policy = new CMSAdaptiveSizePolicy(init_eden_size, + init_promo_size, + init_survivor_size, + max_gc_minor_pause_sec, + max_gc_pause_sec, + GCTimeRatio); +} + +void ConcurrentMarkSweepPolicy::initialize_gc_policy_counters() { + // initialize the policy counters - 2 collectors, 3 generations + if (UseParNewGC && ParallelGCThreads > 0) { + _gc_policy_counters = new GCPolicyCounters("ParNew:CMS", 2, 3); + } + else { + _gc_policy_counters = new GCPolicyCounters("Copy:CMS", 2, 3); + } +} + +// Returns true if the incremental mode is enabled. +bool ConcurrentMarkSweepPolicy::has_soft_ended_eden() +{ + return CMSIncrementalMode; +} + + +// +// ASConcurrentMarkSweepPolicy methods +// + +void ASConcurrentMarkSweepPolicy::initialize_gc_policy_counters() { + + assert(size_policy() != NULL, "A size policy is required"); + // initialize the policy counters - 2 collectors, 3 generations + if (UseParNewGC && ParallelGCThreads > 0) { + _gc_policy_counters = new CMSGCAdaptivePolicyCounters("ParNew:CMS", 2, 3, + size_policy()); + } + else { + _gc_policy_counters = new CMSGCAdaptivePolicyCounters("Copy:CMS", 2, 3, + size_policy()); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.hpp new file mode 100644 index 00000000000..1fe98442b60 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ConcurrentMarkSweepPolicy : public TwoGenerationCollectorPolicy { + protected: + void initialize_generations(); + + public: + ConcurrentMarkSweepPolicy(); + + ConcurrentMarkSweepPolicy* as_concurrent_mark_sweep_policy() { return this; } + + void initialize_gc_policy_counters(); +#if 1 + virtual void initialize_size_policy(size_t init_eden_size, + size_t init_promo_size, + size_t init_survivor_size); +#endif + + // Returns true if the incremental mode is enabled. + virtual bool has_soft_ended_eden(); +}; + +class ASConcurrentMarkSweepPolicy : public ConcurrentMarkSweepPolicy { + public: + + // Initialize the jstat counters. This method requires a + // size policy. The size policy is expected to be created + // after the generations are fully initialized so the + // initialization of the counters need to be done post + // the initialization of the generations. + void initialize_gc_policy_counters(); + + virtual CollectorPolicy::Name kind() { + return CollectorPolicy::ASConcurrentMarkSweepPolicyKind; + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsGCAdaptivePolicyCounters.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsGCAdaptivePolicyCounters.cpp new file mode 100644 index 00000000000..6473761e329 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsGCAdaptivePolicyCounters.cpp @@ -0,0 +1,302 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_cmsGCAdaptivePolicyCounters.cpp.incl" + +CMSGCAdaptivePolicyCounters::CMSGCAdaptivePolicyCounters(const char* name_arg, + int collectors, + int generations, + AdaptiveSizePolicy* size_policy_arg) + : GCAdaptivePolicyCounters(name_arg, + collectors, + generations, + size_policy_arg) { + if (UsePerfData) { + EXCEPTION_MARK; + ResourceMark rm; + + const char* cname = + PerfDataManager::counter_name(name_space(), "cmsCapacity"); + _cms_capacity_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) OldSize, CHECK); +#ifdef NOT_PRODUCT + cname = + PerfDataManager::counter_name(name_space(), "initialPause"); + _initial_pause_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_initial_pause()->last_sample(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "remarkPause"); + _remark_pause_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_remark_pause()->last_sample(), + CHECK); +#endif + cname = + PerfDataManager::counter_name(name_space(), "avgInitialPause"); + _avg_initial_pause_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_initial_pause()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgRemarkPause"); + _avg_remark_pause_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_remark_pause()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgSTWGcCost"); + _avg_cms_STW_gc_cost_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_cms_STW_gc_cost()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgSTWTime"); + _avg_cms_STW_time_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_cms_STW_time()->average(), + CHECK); + + + cname = PerfDataManager::counter_name(name_space(), "avgConcurrentTime"); + _avg_concurrent_time_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_concurrent_time()->average(), + CHECK); + + cname = + PerfDataManager::counter_name(name_space(), "avgConcurrentInterval"); + _avg_concurrent_interval_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_concurrent_interval()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgConcurrentGcCost"); + _avg_concurrent_gc_cost_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_concurrent_gc_cost()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgCMSFreeAtSweep"); + _avg_cms_free_at_sweep_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_cms_free_at_sweep()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgCMSFree"); + _avg_cms_free_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_cms_free()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgCMSPromo"); + _avg_cms_promo_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_cms_promo()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgMscPause"); + _avg_msc_pause_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_msc_pause()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgMscInterval"); + _avg_msc_interval_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_msc_interval()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "mscGcCost"); + _msc_gc_cost_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_msc_gc_cost()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgMsPause"); + _avg_ms_pause_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_ms_pause()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgMsInterval"); + _avg_ms_interval_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_ms_interval()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "msGcCost"); + _ms_gc_cost_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) cms_size_policy()->avg_ms_gc_cost()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "majorGcCost"); + _major_gc_cost_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, (jlong) cms_size_policy()->cms_gc_cost(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgPromotedAvg"); + _promoted_avg_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + cms_size_policy()->calculated_promo_size_in_bytes(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgPromotedDev"); + _promoted_avg_dev_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + (jlong) 0 , CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgPromotedPaddedAvg"); + _promoted_padded_avg_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + cms_size_policy()->calculated_promo_size_in_bytes(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), + "changeYoungGenForMajPauses"); + _change_young_gen_for_maj_pauses_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events, + (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "remarkPauseOldSlope"); + _remark_pause_old_slope_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + (jlong) cms_size_policy()->remark_pause_old_slope(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "initialPauseOldSlope"); + _initial_pause_old_slope_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + (jlong) cms_size_policy()->initial_pause_old_slope(), CHECK); + + cname = + PerfDataManager::counter_name(name_space(), "remarkPauseYoungSlope") ; + _remark_pause_young_slope_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + (jlong) cms_size_policy()->remark_pause_young_slope(), CHECK); + + cname = + PerfDataManager::counter_name(name_space(), "initialPauseYoungSlope"); + _initial_pause_young_slope_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + (jlong) cms_size_policy()->initial_pause_young_slope(), CHECK); + + + } + assert(size_policy()->is_gc_cms_adaptive_size_policy(), + "Wrong type of size policy"); +} + +void CMSGCAdaptivePolicyCounters::update_counters() { + if (UsePerfData) { + GCAdaptivePolicyCounters::update_counters_from_policy(); + update_counters_from_policy(); + } +} + +void CMSGCAdaptivePolicyCounters::update_counters(CMSGCStats* gc_stats) { + if (UsePerfData) { + update_counters(); + update_promoted((size_t) gc_stats->avg_promoted()->last_sample()); + update_avg_promoted_avg(gc_stats); + update_avg_promoted_dev(gc_stats); + update_avg_promoted_padded_avg(gc_stats); + } +} + +void CMSGCAdaptivePolicyCounters::update_counters_from_policy() { + if (UsePerfData && (cms_size_policy() != NULL)) { + + GCAdaptivePolicyCounters::update_counters_from_policy(); + + update_major_gc_cost_counter(); + update_mutator_cost_counter(); + + update_eden_size(); + update_promo_size(); + + // If these updates from the last_sample() work, + // revise the update methods for these counters + // (both here and in PS). + update_survived((size_t) cms_size_policy()->avg_survived()->last_sample()); + + update_avg_concurrent_time_counter(); + update_avg_concurrent_interval_counter(); + update_avg_concurrent_gc_cost_counter(); +#ifdef NOT_PRODUCT + update_initial_pause_counter(); + update_remark_pause_counter(); +#endif + update_avg_initial_pause_counter(); + update_avg_remark_pause_counter(); + + update_avg_cms_STW_time_counter(); + update_avg_cms_STW_gc_cost_counter(); + + update_avg_cms_free_counter(); + update_avg_cms_free_at_sweep_counter(); + update_avg_cms_promo_counter(); + + update_avg_msc_pause_counter(); + update_avg_msc_interval_counter(); + update_msc_gc_cost_counter(); + + update_avg_ms_pause_counter(); + update_avg_ms_interval_counter(); + update_ms_gc_cost_counter(); + + update_avg_old_live_counter(); + + update_survivor_size_counters(); + update_avg_survived_avg_counters(); + update_avg_survived_dev_counters(); + + update_decrement_tenuring_threshold_for_gc_cost(); + update_increment_tenuring_threshold_for_gc_cost(); + update_decrement_tenuring_threshold_for_survivor_limit(); + + update_change_young_gen_for_maj_pauses(); + + update_major_collection_slope_counter(); + update_remark_pause_old_slope_counter(); + update_initial_pause_old_slope_counter(); + update_remark_pause_young_slope_counter(); + update_initial_pause_young_slope_counter(); + + update_decide_at_full_gc_counter(); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsGCAdaptivePolicyCounters.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsGCAdaptivePolicyCounters.hpp new file mode 100644 index 00000000000..33321824520 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsGCAdaptivePolicyCounters.hpp @@ -0,0 +1,300 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// CMSGCAdaptivePolicyCounters is a holder class for performance counters +// that track the data and decisions for the ergonomics policy for the +// concurrent mark sweep collector + +class CMSGCAdaptivePolicyCounters : public GCAdaptivePolicyCounters { + friend class VMStructs; + + private: + + // Capacity of tenured generation recorded at the end of + // any collection. + PerfVariable* _cms_capacity_counter; // Make this common with PS _old_capacity + + // Average stop-the-world pause time for both initial and + // remark pauses sampled at the end of the checkpointRootsFinalWork. + PerfVariable* _avg_cms_STW_time_counter; + // Average stop-the-world (STW) GC cost for the STW pause time + // _avg_cms_STW_time_counter. + PerfVariable* _avg_cms_STW_gc_cost_counter; + +#ifdef NOT_PRODUCT + // These are useful to see how the most recent values of these + // counters compare to their respective averages but + // do not control behavior. + PerfVariable* _initial_pause_counter; + PerfVariable* _remark_pause_counter; +#endif + + // Average of the initial marking pause for a concurrent collection. + PerfVariable* _avg_initial_pause_counter; + // Average of the remark pause for a concurrent collection. + PerfVariable* _avg_remark_pause_counter; + + // Average for the sum of all the concurrent times per collection. + PerfVariable* _avg_concurrent_time_counter; + // Average for the time between the most recent end of a + // concurrent collection and the beginning of the next + // concurrent collection. + PerfVariable* _avg_concurrent_interval_counter; + // Average of the concurrent GC costs based on _avg_concurrent_time_counter + // and _avg_concurrent_interval_counter. + PerfVariable* _avg_concurrent_gc_cost_counter; + + // Average of the free space in the tenured generation at the + // end of the sweep of the tenured generation. + PerfVariable* _avg_cms_free_counter; + // Average of the free space in the tenured generation at the + // start of the sweep of the tenured genertion. + PerfVariable* _avg_cms_free_at_sweep_counter; + // Average of the free space in the tenured generation at the + // after any resizing of the tenured generation at the end + // of a collection of the tenured generation. + PerfVariable* _avg_cms_promo_counter; + + // Average of the mark-sweep-compact (MSC) pause time for a collection + // of the tenured generation. + PerfVariable* _avg_msc_pause_counter; + // Average for the time between the most recent end of a + // MSC collection and the beginning of the next + // MSC collection. + PerfVariable* _avg_msc_interval_counter; + // Average for the GC cost of a MSC collection based on + // _avg_msc_pause_counter and _avg_msc_interval_counter. + PerfVariable* _msc_gc_cost_counter; + + // Average of the mark-sweep (MS) pause time for a collection + // of the tenured generation. + PerfVariable* _avg_ms_pause_counter; + // Average for the time between the most recent end of a + // MS collection and the beginning of the next + // MS collection. + PerfVariable* _avg_ms_interval_counter; + // Average for the GC cost of a MS collection based on + // _avg_ms_pause_counter and _avg_ms_interval_counter. + PerfVariable* _ms_gc_cost_counter; + + // Average of the bytes promoted per minor collection. + PerfVariable* _promoted_avg_counter; + // Average of the deviation of the promoted average + PerfVariable* _promoted_avg_dev_counter; + // Padded average of the bytes promoted per minor colleciton + PerfVariable* _promoted_padded_avg_counter; + + // See description of the _change_young_gen_for_maj_pauses + // variable recently in cmsAdaptiveSizePolicy.hpp. + PerfVariable* _change_young_gen_for_maj_pauses_counter; + + // See descriptions of _remark_pause_old_slope, _initial_pause_old_slope, + // etc. variables recently in cmsAdaptiveSizePolicy.hpp. + PerfVariable* _remark_pause_old_slope_counter; + PerfVariable* _initial_pause_old_slope_counter; + PerfVariable* _remark_pause_young_slope_counter; + PerfVariable* _initial_pause_young_slope_counter; + + CMSAdaptiveSizePolicy* cms_size_policy() { + assert(_size_policy->kind() == + AdaptiveSizePolicy::_gc_cms_adaptive_size_policy, + "Wrong size policy"); + return (CMSAdaptiveSizePolicy*)_size_policy; + } + + inline void update_avg_cms_STW_time_counter() { + _avg_cms_STW_time_counter->set_value( + (jlong) (cms_size_policy()->avg_cms_STW_time()->average() * + (double) MILLIUNITS)); + } + + inline void update_avg_cms_STW_gc_cost_counter() { + _avg_cms_STW_gc_cost_counter->set_value( + (jlong) (cms_size_policy()->avg_cms_STW_gc_cost()->average() * 100.0)); + } + + inline void update_avg_initial_pause_counter() { + _avg_initial_pause_counter->set_value( + (jlong) (cms_size_policy()->avg_initial_pause()->average() * + (double) MILLIUNITS)); + } +#ifdef NOT_PRODUCT + inline void update_avg_remark_pause_counter() { + _avg_remark_pause_counter->set_value( + (jlong) (cms_size_policy()-> avg_remark_pause()->average() * + (double) MILLIUNITS)); + } + + inline void update_initial_pause_counter() { + _initial_pause_counter->set_value( + (jlong) (cms_size_policy()->avg_initial_pause()->average() * + (double) MILLIUNITS)); + } +#endif + inline void update_remark_pause_counter() { + _remark_pause_counter->set_value( + (jlong) (cms_size_policy()-> avg_remark_pause()->last_sample() * + (double) MILLIUNITS)); + } + + inline void update_avg_concurrent_time_counter() { + _avg_concurrent_time_counter->set_value( + (jlong) (cms_size_policy()->avg_concurrent_time()->last_sample() * + (double) MILLIUNITS)); + } + + inline void update_avg_concurrent_interval_counter() { + _avg_concurrent_interval_counter->set_value( + (jlong) (cms_size_policy()->avg_concurrent_interval()->average() * + (double) MILLIUNITS)); + } + + inline void update_avg_concurrent_gc_cost_counter() { + _avg_concurrent_gc_cost_counter->set_value( + (jlong) (cms_size_policy()->avg_concurrent_gc_cost()->average() * 100.0)); + } + + inline void update_avg_cms_free_counter() { + _avg_cms_free_counter->set_value( + (jlong) cms_size_policy()->avg_cms_free()->average()); + } + + inline void update_avg_cms_free_at_sweep_counter() { + _avg_cms_free_at_sweep_counter->set_value( + (jlong) cms_size_policy()->avg_cms_free_at_sweep()->average()); + } + + inline void update_avg_cms_promo_counter() { + _avg_cms_promo_counter->set_value( + (jlong) cms_size_policy()->avg_cms_promo()->average()); + } + + inline void update_avg_old_live_counter() { + _avg_old_live_counter->set_value( + (jlong)(cms_size_policy()->avg_old_live()->average()) + ); + } + + inline void update_avg_msc_pause_counter() { + _avg_msc_pause_counter->set_value( + (jlong) (cms_size_policy()->avg_msc_pause()->average() * + (double) MILLIUNITS)); + } + + inline void update_avg_msc_interval_counter() { + _avg_msc_interval_counter->set_value( + (jlong) (cms_size_policy()->avg_msc_interval()->average() * + (double) MILLIUNITS)); + } + + inline void update_msc_gc_cost_counter() { + _msc_gc_cost_counter->set_value( + (jlong) (cms_size_policy()->avg_msc_gc_cost()->average() * 100.0)); + } + + inline void update_avg_ms_pause_counter() { + _avg_ms_pause_counter->set_value( + (jlong) (cms_size_policy()->avg_ms_pause()->average() * + (double) MILLIUNITS)); + } + + inline void update_avg_ms_interval_counter() { + _avg_ms_interval_counter->set_value( + (jlong) (cms_size_policy()->avg_ms_interval()->average() * + (double) MILLIUNITS)); + } + + inline void update_ms_gc_cost_counter() { + _ms_gc_cost_counter->set_value( + (jlong) (cms_size_policy()->avg_ms_gc_cost()->average() * 100.0)); + } + + inline void update_major_gc_cost_counter() { + _major_gc_cost_counter->set_value( + (jlong)(cms_size_policy()->cms_gc_cost() * 100.0) + ); + } + inline void update_mutator_cost_counter() { + _mutator_cost_counter->set_value( + (jlong)(cms_size_policy()->mutator_cost() * 100.0) + ); + } + + inline void update_avg_promoted_avg(CMSGCStats* gc_stats) { + _promoted_avg_counter->set_value( + (jlong)(gc_stats->avg_promoted()->average()) + ); + } + inline void update_avg_promoted_dev(CMSGCStats* gc_stats) { + _promoted_avg_dev_counter->set_value( + (jlong)(gc_stats->avg_promoted()->deviation()) + ); + } + inline void update_avg_promoted_padded_avg(CMSGCStats* gc_stats) { + _promoted_padded_avg_counter->set_value( + (jlong)(gc_stats->avg_promoted()->padded_average()) + ); + } + inline void update_remark_pause_old_slope_counter() { + _remark_pause_old_slope_counter->set_value( + (jlong)(cms_size_policy()->remark_pause_old_slope() * 1000) + ); + } + inline void update_initial_pause_old_slope_counter() { + _initial_pause_old_slope_counter->set_value( + (jlong)(cms_size_policy()->initial_pause_old_slope() * 1000) + ); + } + inline void update_remark_pause_young_slope_counter() { + _remark_pause_young_slope_counter->set_value( + (jlong)(cms_size_policy()->remark_pause_young_slope() * 1000) + ); + } + inline void update_initial_pause_young_slope_counter() { + _initial_pause_young_slope_counter->set_value( + (jlong)(cms_size_policy()->initial_pause_young_slope() * 1000) + ); + } + inline void update_change_young_gen_for_maj_pauses() { + _change_young_gen_for_maj_pauses_counter->set_value( + cms_size_policy()->change_young_gen_for_maj_pauses()); + } + + public: + CMSGCAdaptivePolicyCounters(const char* name, int collectors, int generations, + AdaptiveSizePolicy* size_policy); + + // update counters + void update_counters(); + void update_counters(CMSGCStats* gc_stats); + void update_counters_from_policy(); + + inline void update_cms_capacity_counter(size_t size_in_bytes) { + _cms_capacity_counter->set_value(size_in_bytes); + } + + virtual GCPolicyCounters::Name kind() const { + return GCPolicyCounters::CMSGCAdaptivePolicyCountersKind; + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.cpp new file mode 100644 index 00000000000..00ef43f6957 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_cmsLockVerifier.cpp.incl" + +///////////// Locking verification specific to CMS ////////////// +// Much like "assert_lock_strong()", except that it relaxes the +// assertion somewhat for the parallel GC case, where VM thread +// or the CMS thread might hold the lock on behalf of the parallel +// threads. The second argument is in support of an extra locking +// check for CFL spaces' free list locks. +#ifndef PRODUCT +void CMSLockVerifier::assert_locked(const Mutex* lock, const Mutex* p_lock) { + if (!Universe::is_fully_initialized()) { + return; + } + + Thread* myThread = Thread::current(); + + if (lock == NULL) { // a "lock-free" structure, e.g. MUT, protected by CMS token + assert(p_lock == NULL, "Unexpected state"); + if (myThread->is_ConcurrentGC_thread()) { + // This test might have to change in the future, if there can be + // multiple peer CMS threads. But for now, if we're testing the CMS + assert(myThread == ConcurrentMarkSweepThread::cmst(), + "In CMS, CMS thread is the only Conc GC thread."); + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "CMS thread should have CMS token"); + } else if (myThread->is_VM_thread()) { + assert(ConcurrentMarkSweepThread::vm_thread_has_cms_token(), + "VM thread should have CMS token"); + } else { + // Token should be held on our behalf by one of the other + // of CMS or VM thread; not enough easily testable + // state info to test which here. + assert(myThread->is_GC_task_thread(), "Unexpected thread type"); + } + return; + } + + if (ParallelGCThreads == 0) { + assert_lock_strong(lock); + } else { + if (myThread->is_VM_thread() + || myThread->is_ConcurrentGC_thread() + || myThread->is_Java_thread()) { + // Make sure that we are holding the associated lock. + assert_lock_strong(lock); + // The checking of p_lock is a spl case for CFLS' free list + // locks: we make sure that none of the parallel GC work gang + // threads are holding "sub-locks" of freeListLock(). We check only + // the parDictionaryAllocLock because the others are too numerous. + // This spl case code is somewhat ugly and any improvements + // are welcome XXX FIX ME!! + if (p_lock != NULL) { + assert(!p_lock->is_locked() || p_lock->owned_by_self(), + "Possible race between this and parallel GC threads"); + } + } else if (myThread->is_GC_task_thread()) { + // Make sure that the VM or CMS thread holds lock on our behalf + // XXX If there were a concept of a gang_master for a (set of) + // gang_workers, we could have used the identity of that thread + // for checking ownership here; for now we just disjunct. + assert(lock->owner() == VMThread::vm_thread() || + lock->owner() == ConcurrentMarkSweepThread::cmst(), + "Should be locked by VM thread or CMS thread on my behalf"); + } else { + // Make sure we didn't miss some obscure corner case + ShouldNotReachHere(); + } + } +} +#endif diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.hpp new file mode 100644 index 00000000000..f2fe4514061 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.hpp @@ -0,0 +1,37 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +///////////// Locking verification specific to CMS ////////////// +// Much like "assert_lock_strong()", except +// that it relaxes the assertion somewhat for the parallel GC case, where +// main GC thread or the CMS thread might hold the lock on behalf of +// the parallel threads. +class CMSLockVerifier: AllStatic { + public: + static void assert_locked(const Mutex* lock, const Mutex* p_lock) + PRODUCT_RETURN; + static void assert_locked(const Mutex* lock) { + assert_locked(lock, NULL); + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp new file mode 100644 index 00000000000..1c6d7a54855 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +///////////////////////////////////////////////////////////////// +// Closures used by ConcurrentMarkSweepGeneration's collector +///////////////////////////////////////////////////////////////// +class ConcurrentMarkSweepGeneration; +class CMSBitMap; +class CMSMarkStack; +class CMSCollector; +template class GenericTaskQueue; +typedef GenericTaskQueue OopTaskQueue; +template class GenericTaskQueueSet; +typedef GenericTaskQueueSet OopTaskQueueSet; +class MarkFromRootsClosure; +class Par_MarkFromRootsClosure; + +class MarkRefsIntoClosure: public OopsInGenClosure { + const MemRegion _span; + CMSBitMap* _bitMap; + const bool _should_do_nmethods; + public: + MarkRefsIntoClosure(MemRegion span, CMSBitMap* bitMap, + bool should_do_nmethods); + void do_oop(oop* p); + void do_oop_nv(oop* p) { MarkRefsIntoClosure::do_oop(p); } + bool do_header() { return true; } + virtual const bool do_nmethods() const { + return _should_do_nmethods; + } + Prefetch::style prefetch_style() { + return Prefetch::do_read; + } +}; + +// A variant of the above used in certain kinds of CMS +// marking verification. +class MarkRefsIntoVerifyClosure: public OopsInGenClosure { + const MemRegion _span; + CMSBitMap* _verification_bm; + CMSBitMap* _cms_bm; + const bool _should_do_nmethods; + public: + MarkRefsIntoVerifyClosure(MemRegion span, CMSBitMap* verification_bm, + CMSBitMap* cms_bm, bool should_do_nmethods); + void do_oop(oop* p); + void do_oop_nv(oop* p) { MarkRefsIntoVerifyClosure::do_oop(p); } + bool do_header() { return true; } + virtual const bool do_nmethods() const { + return _should_do_nmethods; + } + Prefetch::style prefetch_style() { + return Prefetch::do_read; + } +}; + + +// The non-parallel version (the parallel version appears further below). +class PushAndMarkClosure: public OopClosure { + CMSCollector* _collector; + MemRegion _span; + CMSBitMap* _bit_map; + CMSBitMap* _mod_union_table; + CMSMarkStack* _mark_stack; + CMSMarkStack* _revisit_stack; + bool _concurrent_precleaning; + bool const _should_remember_klasses; + public: + PushAndMarkClosure(CMSCollector* collector, + MemRegion span, + ReferenceProcessor* rp, + CMSBitMap* bit_map, + CMSBitMap* mod_union_table, + CMSMarkStack* mark_stack, + CMSMarkStack* revisit_stack, + bool concurrent_precleaning); + + void do_oop(oop* p); + void do_oop_nv(oop* p) { PushAndMarkClosure::do_oop(p); } + bool do_header() { return true; } + Prefetch::style prefetch_style() { + return Prefetch::do_read; + } + const bool should_remember_klasses() const { + return _should_remember_klasses; + } + void remember_klass(Klass* k); +}; + +// In the parallel case, the revisit stack, the bit map and the +// reference processor are currently all shared. Access to +// these shared mutable structures must use appropriate +// synchronization (for instance, via CAS). The marking stack +// used in the non-parallel case above is here replaced with +// an OopTaskQueue structure to allow efficient work stealing. +class Par_PushAndMarkClosure: public OopClosure { + CMSCollector* _collector; + MemRegion _span; + CMSBitMap* _bit_map; + OopTaskQueue* _work_queue; + CMSMarkStack* _revisit_stack; + bool const _should_remember_klasses; + public: + Par_PushAndMarkClosure(CMSCollector* collector, + MemRegion span, + ReferenceProcessor* rp, + CMSBitMap* bit_map, + OopTaskQueue* work_queue, + CMSMarkStack* revisit_stack); + + void do_oop(oop* p); + void do_oop_nv(oop* p) { Par_PushAndMarkClosure::do_oop(p); } + bool do_header() { return true; } + Prefetch::style prefetch_style() { + return Prefetch::do_read; + } + const bool should_remember_klasses() const { + return _should_remember_klasses; + } + void remember_klass(Klass* k); +}; + + +// The non-parallel version (the parallel version appears further below). +class MarkRefsIntoAndScanClosure: public OopsInGenClosure { + MemRegion _span; + CMSBitMap* _bit_map; + CMSMarkStack* _mark_stack; + PushAndMarkClosure _pushAndMarkClosure; + CMSCollector* _collector; + bool _yield; + // Whether closure is being used for concurrent precleaning + bool _concurrent_precleaning; + Mutex* _freelistLock; + public: + MarkRefsIntoAndScanClosure(MemRegion span, + ReferenceProcessor* rp, + CMSBitMap* bit_map, + CMSBitMap* mod_union_table, + CMSMarkStack* mark_stack, + CMSMarkStack* revisit_stack, + CMSCollector* collector, + bool should_yield, + bool concurrent_precleaning); + void do_oop(oop* p); + void do_oop_nv(oop* p) { MarkRefsIntoAndScanClosure::do_oop(p); } + bool do_header() { return true; } + virtual const bool do_nmethods() const { return true; } + Prefetch::style prefetch_style() { + return Prefetch::do_read; + } + void set_freelistLock(Mutex* m) { + _freelistLock = m; + } + + private: + inline void do_yield_check(); + void do_yield_work(); + bool take_from_overflow_list(); +}; + +// Tn this, the parallel avatar of MarkRefsIntoAndScanClosure, the revisit +// stack and the bitMap are shared, so access needs to be suitably +// sycnhronized. An OopTaskQueue structure, supporting efficient +// workstealing, replaces a CMSMarkStack for storing grey objects. +class Par_MarkRefsIntoAndScanClosure: public OopsInGenClosure { + MemRegion _span; + CMSBitMap* _bit_map; + OopTaskQueue* _work_queue; + const uint _low_water_mark; + Par_PushAndMarkClosure _par_pushAndMarkClosure; + public: + Par_MarkRefsIntoAndScanClosure(CMSCollector* collector, + MemRegion span, + ReferenceProcessor* rp, + CMSBitMap* bit_map, + OopTaskQueue* work_queue, + CMSMarkStack* revisit_stack); + void do_oop(oop* p); + void do_oop_nv(oop* p) { Par_MarkRefsIntoAndScanClosure::do_oop(p); } + bool do_header() { return true; } + virtual const bool do_nmethods() const { return true; } + Prefetch::style prefetch_style() { + return Prefetch::do_read; + } + void trim_queue(uint size); +}; + +// This closure is used during the concurrent marking phase +// following the first checkpoint. Its use is buried in +// the closure MarkFromRootsClosure. +class PushOrMarkClosure: public OopClosure { + CMSCollector* _collector; + MemRegion _span; + CMSBitMap* _bitMap; + CMSMarkStack* _markStack; + CMSMarkStack* _revisitStack; + HeapWord* const _finger; + MarkFromRootsClosure* const _parent; + bool const _should_remember_klasses; + public: + PushOrMarkClosure(CMSCollector* cms_collector, + MemRegion span, + CMSBitMap* bitMap, + CMSMarkStack* markStack, + CMSMarkStack* revisitStack, + HeapWord* finger, + MarkFromRootsClosure* parent); + void do_oop(oop* p); + void do_oop_nv(oop* p) { PushOrMarkClosure::do_oop(p); } + const bool should_remember_klasses() const { + return _should_remember_klasses; + } + void remember_klass(Klass* k); + // Deal with a stack overflow condition + void handle_stack_overflow(HeapWord* lost); + private: + inline void do_yield_check(); +}; + +// A parallel (MT) version of the above. +// This closure is used during the concurrent marking phase +// following the first checkpoint. Its use is buried in +// the closure Par_MarkFromRootsClosure. +class Par_PushOrMarkClosure: public OopClosure { + CMSCollector* _collector; + MemRegion _whole_span; + MemRegion _span; // local chunk + CMSBitMap* _bit_map; + OopTaskQueue* _work_queue; + CMSMarkStack* _overflow_stack; + CMSMarkStack* _revisit_stack; + HeapWord* const _finger; + HeapWord** const _global_finger_addr; + Par_MarkFromRootsClosure* const _parent; + bool const _should_remember_klasses; + public: + Par_PushOrMarkClosure(CMSCollector* cms_collector, + MemRegion span, + CMSBitMap* bit_map, + OopTaskQueue* work_queue, + CMSMarkStack* mark_stack, + CMSMarkStack* revisit_stack, + HeapWord* finger, + HeapWord** global_finger_addr, + Par_MarkFromRootsClosure* parent); + void do_oop(oop* p); + void do_oop_nv(oop* p) { Par_PushOrMarkClosure::do_oop(p); } + const bool should_remember_klasses() const { + return _should_remember_klasses; + } + void remember_klass(Klass* k); + // Deal with a stack overflow condition + void handle_stack_overflow(HeapWord* lost); + private: + inline void do_yield_check(); +}; + +// For objects in CMS generation, this closure marks +// given objects (transitively) as being reachable/live. +// This is currently used during the (weak) reference object +// processing phase of the CMS final checkpoint step. +class CMSKeepAliveClosure: public OopClosure { + CMSCollector* _collector; + MemRegion _span; + CMSMarkStack* _mark_stack; + CMSBitMap* _bit_map; + public: + CMSKeepAliveClosure(CMSCollector* collector, MemRegion span, + CMSBitMap* bit_map, CMSMarkStack* mark_stack): + _collector(collector), + _span(span), + _bit_map(bit_map), + _mark_stack(mark_stack) { } + + void do_oop(oop* p); + void do_oop_nv(oop* p) { CMSKeepAliveClosure::do_oop(p); } +}; + +class CMSInnerParMarkAndPushClosure: public OopClosure { + CMSCollector* _collector; + MemRegion _span; + OopTaskQueue* _work_queue; + CMSBitMap* _bit_map; + public: + CMSInnerParMarkAndPushClosure(CMSCollector* collector, + MemRegion span, CMSBitMap* bit_map, + OopTaskQueue* work_queue): + _collector(collector), + _span(span), + _bit_map(bit_map), + _work_queue(work_queue) { } + void do_oop(oop* p); + void do_oop_nv(oop* p) { CMSInnerParMarkAndPushClosure::do_oop(p); } +}; + +// A parallel (MT) version of the above, used when +// reference processing is parallel; the only difference +// is in the do_oop method. +class CMSParKeepAliveClosure: public OopClosure { + CMSCollector* _collector; + MemRegion _span; + OopTaskQueue* _work_queue; + CMSBitMap* _bit_map; + CMSInnerParMarkAndPushClosure _mark_and_push; + const uint _low_water_mark; + void trim_queue(uint max); + public: + CMSParKeepAliveClosure(CMSCollector* collector, MemRegion span, + CMSBitMap* bit_map, OopTaskQueue* work_queue); + void do_oop(oop* p); + void do_oop_nv(oop* p) { CMSParKeepAliveClosure::do_oop(p); } +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.inline.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.inline.hpp new file mode 100644 index 00000000000..fcf0691577e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.inline.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Trim our work_queue so its length is below max at return +inline void Par_MarkRefsIntoAndScanClosure::trim_queue(uint max) { + while (_work_queue->size() > max) { + oop newOop; + if (_work_queue->pop_local(newOop)) { + assert(newOop->is_oop(), "Expected an oop"); + assert(_bit_map->isMarked((HeapWord*)newOop), + "only grey objects on this stack"); + // iterate over the oops in this oop, marking and pushing + // the ones in CMS heap (i.e. in _span). + newOop->oop_iterate(&_par_pushAndMarkClosure); + } + } +} + +inline void PushOrMarkClosure::remember_klass(Klass* k) { + if (!_revisitStack->push(oop(k))) { + fatal("Revisit stack overflow in PushOrMarkClosure"); + } +} + +inline void Par_PushOrMarkClosure::remember_klass(Klass* k) { + if (!_revisit_stack->par_push(oop(k))) { + fatal("Revisit stack overflow in PushOrMarkClosure"); + } +} + +inline void PushOrMarkClosure::do_yield_check() { + _parent->do_yield_check(); +} + +inline void Par_PushOrMarkClosure::do_yield_check() { + _parent->do_yield_check(); +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsPermGen.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsPermGen.cpp new file mode 100644 index 00000000000..ead077be1e3 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsPermGen.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_cmsPermGen.cpp.incl" + +CMSPermGen::CMSPermGen(ReservedSpace rs, size_t initial_byte_size, + CardTableRS* ct, + FreeBlockDictionary::DictionaryChoice dictionaryChoice) { + CMSPermGenGen* g = + new CMSPermGenGen(rs, initial_byte_size, -1, ct); + if (g == NULL) { + vm_exit_during_initialization("Could not allocate a CompactingPermGen"); + } + + g->initialize_performance_counters(); + + _gen = g; +} + +HeapWord* CMSPermGen::mem_allocate(size_t size) { + Mutex* lock = _gen->freelistLock(); + bool lock_owned = lock->owned_by_self(); + if (lock_owned) { + MutexUnlocker mul(lock); + return mem_allocate_work(size); + } else { + return mem_allocate_work(size); + } +} + +HeapWord* CMSPermGen::mem_allocate_work(size_t size) { + assert(!_gen->freelistLock()->owned_by_self(), "Potetntial deadlock"); + + MutexLocker ml(Heap_lock); + HeapWord* obj = NULL; + + obj = _gen->allocate(size, false); + // Since we want to minimize pause times, we will prefer + // expanding the perm gen rather than doing a stop-world + // collection to satisfy the allocation request. + if (obj == NULL) { + // Try to expand the perm gen and allocate space. + obj = _gen->expand_and_allocate(size, false, false); + if (obj == NULL) { + // Let's see if a normal stop-world full collection will + // free up enough space. + SharedHeap::heap()->collect_locked(GCCause::_permanent_generation_full); + obj = _gen->allocate(size, false); + if (obj == NULL) { + // The collection above may have shrunk the space, so try + // to expand again and allocate space. + obj = _gen->expand_and_allocate(size, false, false); + } + if (obj == NULL) { + // We have not been able to allocate space despite a + // full stop-world collection. We now make a last-ditch collection + // attempt (in which soft refs are all aggressively freed) + // that will try to reclaim as much space as possible. + SharedHeap::heap()->collect_locked(GCCause::_last_ditch_collection); + obj = _gen->allocate(size, false); + if (obj == NULL) { + // Expand generation in case it was shrunk following the collection. + obj = _gen->expand_and_allocate(size, false, false); + } + } + } + } + return obj; +} + +void CMSPermGen::compute_new_size() { + _gen->compute_new_size(); +} + +void CMSPermGenGen::initialize_performance_counters() { + + const char* gen_name = "perm"; + + // Generation Counters - generation 2, 1 subspace + _gen_counters = new GenerationCounters(gen_name, 2, 1, &_virtual_space); + + _gc_counters = NULL; + + _space_counters = new GSpaceCounters(gen_name, 0, + _virtual_space.reserved_size(), + this, _gen_counters); +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsPermGen.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsPermGen.hpp new file mode 100644 index 00000000000..e7b7096f89c --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsPermGen.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class CardTableRS; // fwd decl +class ConcurrentMarkSweepGeneration; + +// A PermGen implemented with a CMS space, collected by a CMS collector. +class CMSPermGen: public PermGen { + friend class VMStructs; + + HeapWord* mem_allocate_work(size_t size); + protected: + // The "generation" view. + ConcurrentMarkSweepGeneration* _gen; + + public: + CMSPermGen(ReservedSpace rs, size_t initial_byte_size, + CardTableRS* ct, FreeBlockDictionary::DictionaryChoice); + + HeapWord* mem_allocate(size_t size); + + void compute_new_size(); + + Generation* as_gen() const { return _gen; } +}; + +// This is the "generation" view of a CMSPermGen. +class CMSPermGenGen: public ConcurrentMarkSweepGeneration { + // Abstractly, this is a subtype that gets access to protected fields. + friend class CMSPermGen; +public: + CMSPermGenGen(ReservedSpace rs, size_t initial_byte_size, + int level, CardTableRS* ct): + // See comments in the constructor for CompactibleFreeListSpace + // regarding not using adaptive free lists for a perm gen. + ConcurrentMarkSweepGeneration(rs, initial_byte_size, // MinPermHeapExapnsion + level, ct, false /* use adaptive freelists */, + (FreeBlockDictionary::DictionaryChoice)CMSDictionaryChoice) + {} + + void initialize_performance_counters(); + + const char* name() const { + return "concurrent-mark-sweep perm gen"; + } + + const char* short_name() const { + return "CMS Perm"; + } + + bool must_be_youngest() const { return false; } + bool must_be_oldest() const { return false; } +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp new file mode 100644 index 00000000000..27b5c08100c --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp @@ -0,0 +1,2845 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_compactibleFreeListSpace.cpp.incl" + +///////////////////////////////////////////////////////////////////////// +//// CompactibleFreeListSpace +///////////////////////////////////////////////////////////////////////// + +// highest ranked free list lock rank +int CompactibleFreeListSpace::_lockRank = Mutex::leaf + 3; + +// Constructor +CompactibleFreeListSpace::CompactibleFreeListSpace(BlockOffsetSharedArray* bs, + MemRegion mr, bool use_adaptive_freelists, + FreeBlockDictionary::DictionaryChoice dictionaryChoice) : + _dictionaryChoice(dictionaryChoice), + _adaptive_freelists(use_adaptive_freelists), + _bt(bs, mr), + // free list locks are in the range of values taken by _lockRank + // This range currently is [_leaf+2, _leaf+3] + // Note: this requires that CFLspace c'tors + // are called serially in the order in which the locks are + // are acquired in the program text. This is true today. + _freelistLock(_lockRank--, "CompactibleFreeListSpace._lock", true), + _parDictionaryAllocLock(Mutex::leaf - 1, // == rank(ExpandHeap_lock) - 1 + "CompactibleFreeListSpace._dict_par_lock", true), + _rescan_task_size(CardTableModRefBS::card_size_in_words * BitsPerWord * + CMSRescanMultiple), + _marking_task_size(CardTableModRefBS::card_size_in_words * BitsPerWord * + CMSConcMarkMultiple), + _collector(NULL) +{ + _bt.set_space(this); + initialize(mr, true); + // We have all of "mr", all of which we place in the dictionary + // as one big chunk. We'll need to decide here which of several + // possible alternative dictionary implementations to use. For + // now the choice is easy, since we have only one working + // implementation, namely, the simple binary tree (splaying + // temporarily disabled). + switch (dictionaryChoice) { + case FreeBlockDictionary::dictionaryBinaryTree: + _dictionary = new BinaryTreeDictionary(mr); + break; + case FreeBlockDictionary::dictionarySplayTree: + case FreeBlockDictionary::dictionarySkipList: + default: + warning("dictionaryChoice: selected option not understood; using" + " default BinaryTreeDictionary implementation instead."); + _dictionary = new BinaryTreeDictionary(mr); + break; + } + splitBirth(mr.word_size()); + assert(_dictionary != NULL, "CMS dictionary initialization"); + // The indexed free lists are initially all empty and are lazily + // filled in on demand. Initialize the array elements to NULL. + initializeIndexedFreeListArray(); + + // Not using adaptive free lists assumes that allocation is first + // from the linAB's. Also a cms perm gen which can be compacted + // has to have the klass's klassKlass allocated at a lower + // address in the heap than the klass so that the klassKlass is + // moved to its new location before the klass is moved. + // Set the _refillSize for the linear allocation blocks + if (!use_adaptive_freelists) { + FreeChunk* fc = _dictionary->getChunk(mr.word_size()); + // The small linAB initially has all the space and will allocate + // a chunk of any size. + HeapWord* addr = (HeapWord*) fc; + _smallLinearAllocBlock.set(addr, fc->size() , + 1024*SmallForLinearAlloc, fc->size()); + // Note that _unallocated_block is not updated here. + // Allocations from the linear allocation block should + // update it. + } else { + _smallLinearAllocBlock.set(0, 0, 1024*SmallForLinearAlloc, + SmallForLinearAlloc); + } + // CMSIndexedFreeListReplenish should be at least 1 + CMSIndexedFreeListReplenish = MAX2((uintx)1, CMSIndexedFreeListReplenish); + _promoInfo.setSpace(this); + if (UseCMSBestFit) { + _fitStrategy = FreeBlockBestFitFirst; + } else { + _fitStrategy = FreeBlockStrategyNone; + } + checkFreeListConsistency(); + + // Initialize locks for parallel case. + if (ParallelGCThreads > 0) { + for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + _indexedFreeListParLocks[i] = new Mutex(Mutex::leaf - 1, // == ExpandHeap_lock - 1 + "a freelist par lock", + true); + if (_indexedFreeListParLocks[i] == NULL) + vm_exit_during_initialization("Could not allocate a par lock"); + DEBUG_ONLY( + _indexedFreeList[i].set_protecting_lock(_indexedFreeListParLocks[i]); + ) + } + _dictionary->set_par_lock(&_parDictionaryAllocLock); + } +} + +// Like CompactibleSpace forward() but always calls cross_threshold() to +// update the block offset table. Removed initialize_threshold call because +// CFLS does not use a block offset array for contiguous spaces. +HeapWord* CompactibleFreeListSpace::forward(oop q, size_t size, + CompactPoint* cp, HeapWord* compact_top) { + // q is alive + // First check if we should switch compaction space + assert(this == cp->space, "'this' should be current compaction space."); + size_t compaction_max_size = pointer_delta(end(), compact_top); + assert(adjustObjectSize(size) == cp->space->adjust_object_size_v(size), + "virtual adjustObjectSize_v() method is not correct"); + size_t adjusted_size = adjustObjectSize(size); + assert(compaction_max_size >= MinChunkSize || compaction_max_size == 0, + "no small fragments allowed"); + assert(minimum_free_block_size() == MinChunkSize, + "for de-virtualized reference below"); + // Can't leave a nonzero size, residual fragment smaller than MinChunkSize + if (adjusted_size + MinChunkSize > compaction_max_size && + adjusted_size != compaction_max_size) { + do { + // switch to next compaction space + cp->space->set_compaction_top(compact_top); + cp->space = cp->space->next_compaction_space(); + if (cp->space == NULL) { + cp->gen = GenCollectedHeap::heap()->prev_gen(cp->gen); + assert(cp->gen != NULL, "compaction must succeed"); + cp->space = cp->gen->first_compaction_space(); + assert(cp->space != NULL, "generation must have a first compaction space"); + } + compact_top = cp->space->bottom(); + cp->space->set_compaction_top(compact_top); + // The correct adjusted_size may not be the same as that for this method + // (i.e., cp->space may no longer be "this" so adjust the size again. + // Use the virtual method which is not used above to save the virtual + // dispatch. + adjusted_size = cp->space->adjust_object_size_v(size); + compaction_max_size = pointer_delta(cp->space->end(), compact_top); + assert(cp->space->minimum_free_block_size() == 0, "just checking"); + } while (adjusted_size > compaction_max_size); + } + + // store the forwarding pointer into the mark word + if ((HeapWord*)q != compact_top) { + q->forward_to(oop(compact_top)); + assert(q->is_gc_marked(), "encoding the pointer should preserve the mark"); + } else { + // if the object isn't moving we can just set the mark to the default + // mark and handle it specially later on. + q->init_mark(); + assert(q->forwardee() == NULL, "should be forwarded to NULL"); + } + + debug_only(MarkSweep::register_live_oop(q, adjusted_size)); + compact_top += adjusted_size; + + // we need to update the offset table so that the beginnings of objects can be + // found during scavenge. Note that we are updating the offset table based on + // where the object will be once the compaction phase finishes. + + // Always call cross_threshold(). A contiguous space can only call it when + // the compaction_top exceeds the current threshold but not for an + // non-contiguous space. + cp->threshold = + cp->space->cross_threshold(compact_top - adjusted_size, compact_top); + return compact_top; +} + +// A modified copy of OffsetTableContigSpace::cross_threshold() with _offsets -> _bt +// and use of single_block instead of alloc_block. The name here is not really +// appropriate - maybe a more general name could be invented for both the +// contiguous and noncontiguous spaces. + +HeapWord* CompactibleFreeListSpace::cross_threshold(HeapWord* start, HeapWord* the_end) { + _bt.single_block(start, the_end); + return end(); +} + +// Initialize them to NULL. +void CompactibleFreeListSpace::initializeIndexedFreeListArray() { + for (size_t i = 0; i < IndexSetSize; i++) { + // Note that on platforms where objects are double word aligned, + // the odd array elements are not used. It is convenient, however, + // to map directly from the object size to the array element. + _indexedFreeList[i].reset(IndexSetSize); + _indexedFreeList[i].set_size(i); + assert(_indexedFreeList[i].count() == 0, "reset check failed"); + assert(_indexedFreeList[i].head() == NULL, "reset check failed"); + assert(_indexedFreeList[i].tail() == NULL, "reset check failed"); + assert(_indexedFreeList[i].hint() == IndexSetSize, "reset check failed"); + } +} + +void CompactibleFreeListSpace::resetIndexedFreeListArray() { + for (int i = 1; i < IndexSetSize; i++) { + assert(_indexedFreeList[i].size() == (size_t) i, + "Indexed free list sizes are incorrect"); + _indexedFreeList[i].reset(IndexSetSize); + assert(_indexedFreeList[i].count() == 0, "reset check failed"); + assert(_indexedFreeList[i].head() == NULL, "reset check failed"); + assert(_indexedFreeList[i].tail() == NULL, "reset check failed"); + assert(_indexedFreeList[i].hint() == IndexSetSize, "reset check failed"); + } +} + +void CompactibleFreeListSpace::reset(MemRegion mr) { + resetIndexedFreeListArray(); + dictionary()->reset(); + if (BlockOffsetArrayUseUnallocatedBlock) { + assert(end() == mr.end(), "We are compacting to the bottom of CMS gen"); + // Everything's allocated until proven otherwise. + _bt.set_unallocated_block(end()); + } + if (!mr.is_empty()) { + assert(mr.word_size() >= MinChunkSize, "Chunk size is too small"); + _bt.single_block(mr.start(), mr.word_size()); + FreeChunk* fc = (FreeChunk*) mr.start(); + fc->setSize(mr.word_size()); + if (mr.word_size() >= IndexSetSize ) { + returnChunkToDictionary(fc); + } else { + _bt.verify_not_unallocated((HeapWord*)fc, fc->size()); + _indexedFreeList[mr.word_size()].returnChunkAtHead(fc); + } + } + _promoInfo.reset(); + _smallLinearAllocBlock._ptr = NULL; + _smallLinearAllocBlock._word_size = 0; +} + +void CompactibleFreeListSpace::reset_after_compaction() { + // Reset the space to the new reality - one free chunk. + MemRegion mr(compaction_top(), end()); + reset(mr); + // Now refill the linear allocation block(s) if possible. + if (_adaptive_freelists) { + refillLinearAllocBlocksIfNeeded(); + } else { + // Place as much of mr in the linAB as we can get, + // provided it was big enough to go into the dictionary. + FreeChunk* fc = dictionary()->findLargestDict(); + if (fc != NULL) { + assert(fc->size() == mr.word_size(), + "Why was the chunk broken up?"); + removeChunkFromDictionary(fc); + HeapWord* addr = (HeapWord*) fc; + _smallLinearAllocBlock.set(addr, fc->size() , + 1024*SmallForLinearAlloc, fc->size()); + // Note that _unallocated_block is not updated here. + } + } +} + +// Walks the entire dictionary, returning a coterminal +// chunk, if it exists. Use with caution since it involves +// a potentially complete walk of a potentially large tree. +FreeChunk* CompactibleFreeListSpace::find_chunk_at_end() { + + assert_lock_strong(&_freelistLock); + + return dictionary()->find_chunk_ends_at(end()); +} + + +#ifndef PRODUCT +void CompactibleFreeListSpace::initializeIndexedFreeListArrayReturnedBytes() { + for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + _indexedFreeList[i].allocation_stats()->set_returnedBytes(0); + } +} + +size_t CompactibleFreeListSpace::sumIndexedFreeListArrayReturnedBytes() { + size_t sum = 0; + for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + sum += _indexedFreeList[i].allocation_stats()->returnedBytes(); + } + return sum; +} + +size_t CompactibleFreeListSpace::totalCountInIndexedFreeLists() const { + size_t count = 0; + for (int i = MinChunkSize; i < IndexSetSize; i++) { + debug_only( + ssize_t total_list_count = 0; + for (FreeChunk* fc = _indexedFreeList[i].head(); fc != NULL; + fc = fc->next()) { + total_list_count++; + } + assert(total_list_count == _indexedFreeList[i].count(), + "Count in list is incorrect"); + ) + count += _indexedFreeList[i].count(); + } + return count; +} + +size_t CompactibleFreeListSpace::totalCount() { + size_t num = totalCountInIndexedFreeLists(); + num += dictionary()->totalCount(); + if (_smallLinearAllocBlock._word_size != 0) { + num++; + } + return num; +} +#endif + +bool CompactibleFreeListSpace::is_free_block(const HeapWord* p) const { + FreeChunk* fc = (FreeChunk*) p; + return fc->isFree(); +} + +size_t CompactibleFreeListSpace::used() const { + return capacity() - free(); +} + +size_t CompactibleFreeListSpace::free() const { + // "MT-safe, but not MT-precise"(TM), if you will: i.e. + // if you do this while the structures are in flux you + // may get an approximate answer only; for instance + // because there is concurrent allocation either + // directly by mutators or for promotion during a GC. + // It's "MT-safe", however, in the sense that you are guaranteed + // not to crash and burn, for instance, because of walking + // pointers that could disappear as you were walking them. + // The approximation is because the various components + // that are read below are not read atomically (and + // further the computation of totalSizeInIndexedFreeLists() + // is itself a non-atomic computation. The normal use of + // this is during a resize operation at the end of GC + // and at that time you are guaranteed to get the + // correct actual value. However, for instance, this is + // also read completely asynchronously by the "perf-sampler" + // that supports jvmstat, and you are apt to see the values + // flicker in such cases. + assert(_dictionary != NULL, "No _dictionary?"); + return (_dictionary->totalChunkSize(DEBUG_ONLY(freelistLock())) + + totalSizeInIndexedFreeLists() + + _smallLinearAllocBlock._word_size) * HeapWordSize; +} + +size_t CompactibleFreeListSpace::max_alloc_in_words() const { + assert(_dictionary != NULL, "No _dictionary?"); + assert_locked(); + size_t res = _dictionary->maxChunkSize(); + res = MAX2(res, MIN2(_smallLinearAllocBlock._word_size, + (size_t) SmallForLinearAlloc - 1)); + // XXX the following could potentially be pretty slow; + // should one, pesimally for the rare cases when res + // caclulated above is less than IndexSetSize, + // just return res calculated above? My reasoning was that + // those cases will be so rare that the extra time spent doesn't + // really matter.... + // Note: do not change the loop test i >= res + IndexSetStride + // to i > res below, because i is unsigned and res may be zero. + for (size_t i = IndexSetSize - 1; i >= res + IndexSetStride; + i -= IndexSetStride) { + if (_indexedFreeList[i].head() != NULL) { + assert(_indexedFreeList[i].count() != 0, "Inconsistent FreeList"); + return i; + } + } + return res; +} + +void CompactibleFreeListSpace::reportFreeListStatistics() const { + assert_lock_strong(&_freelistLock); + assert(PrintFLSStatistics != 0, "Reporting error"); + _dictionary->reportStatistics(); + if (PrintFLSStatistics > 1) { + reportIndexedFreeListStatistics(); + size_t totalSize = totalSizeInIndexedFreeLists() + + _dictionary->totalChunkSize(DEBUG_ONLY(freelistLock())); + gclog_or_tty->print(" free=%ld frag=%1.4f\n", totalSize, flsFrag()); + } +} + +void CompactibleFreeListSpace::reportIndexedFreeListStatistics() const { + assert_lock_strong(&_freelistLock); + gclog_or_tty->print("Statistics for IndexedFreeLists:\n" + "--------------------------------\n"); + size_t totalSize = totalSizeInIndexedFreeLists(); + size_t freeBlocks = numFreeBlocksInIndexedFreeLists(); + gclog_or_tty->print("Total Free Space: %d\n", totalSize); + gclog_or_tty->print("Max Chunk Size: %d\n", maxChunkSizeInIndexedFreeLists()); + gclog_or_tty->print("Number of Blocks: %d\n", freeBlocks); + if (freeBlocks != 0) { + gclog_or_tty->print("Av. Block Size: %d\n", totalSize/freeBlocks); + } +} + +size_t CompactibleFreeListSpace::numFreeBlocksInIndexedFreeLists() const { + size_t res = 0; + for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + debug_only( + ssize_t recount = 0; + for (FreeChunk* fc = _indexedFreeList[i].head(); fc != NULL; + fc = fc->next()) { + recount += 1; + } + assert(recount == _indexedFreeList[i].count(), + "Incorrect count in list"); + ) + res += _indexedFreeList[i].count(); + } + return res; +} + +size_t CompactibleFreeListSpace::maxChunkSizeInIndexedFreeLists() const { + for (size_t i = IndexSetSize - 1; i != 0; i -= IndexSetStride) { + if (_indexedFreeList[i].head() != NULL) { + assert(_indexedFreeList[i].count() != 0, "Inconsistent FreeList"); + return (size_t)i; + } + } + return 0; +} + +void CompactibleFreeListSpace::set_end(HeapWord* value) { + HeapWord* prevEnd = end(); + assert(prevEnd != value, "unnecessary set_end call"); + assert(prevEnd == NULL || value >= unallocated_block(), "New end is below unallocated block"); + _end = value; + if (prevEnd != NULL) { + // Resize the underlying block offset table. + _bt.resize(pointer_delta(value, bottom())); + if (value <= prevEnd) { + assert(value >= unallocated_block(), "New end is below unallocated block"); + } else { + // Now, take this new chunk and add it to the free blocks. + // Note that the BOT has not yet been updated for this block. + size_t newFcSize = pointer_delta(value, prevEnd); + // XXX This is REALLY UGLY and should be fixed up. XXX + if (!_adaptive_freelists && _smallLinearAllocBlock._ptr == NULL) { + // Mark the boundary of the new block in BOT + _bt.mark_block(prevEnd, value); + // put it all in the linAB + if (ParallelGCThreads == 0) { + _smallLinearAllocBlock._ptr = prevEnd; + _smallLinearAllocBlock._word_size = newFcSize; + repairLinearAllocBlock(&_smallLinearAllocBlock); + } else { // ParallelGCThreads > 0 + MutexLockerEx x(parDictionaryAllocLock(), + Mutex::_no_safepoint_check_flag); + _smallLinearAllocBlock._ptr = prevEnd; + _smallLinearAllocBlock._word_size = newFcSize; + repairLinearAllocBlock(&_smallLinearAllocBlock); + } + // Births of chunks put into a LinAB are not recorded. Births + // of chunks as they are allocated out of a LinAB are. + } else { + // Add the block to the free lists, if possible coalescing it + // with the last free block, and update the BOT and census data. + addChunkToFreeListsAtEndRecordingStats(prevEnd, newFcSize); + } + } + } +} + +class FreeListSpace_DCTOC : public Filtering_DCTOC { + CompactibleFreeListSpace* _cfls; + CMSCollector* _collector; +protected: + // Override. +#define walk_mem_region_with_cl_DECL(ClosureType) \ + virtual void walk_mem_region_with_cl(MemRegion mr, \ + HeapWord* bottom, HeapWord* top, \ + ClosureType* cl); \ + void walk_mem_region_with_cl_par(MemRegion mr, \ + HeapWord* bottom, HeapWord* top, \ + ClosureType* cl); \ + void walk_mem_region_with_cl_nopar(MemRegion mr, \ + HeapWord* bottom, HeapWord* top, \ + ClosureType* cl) + walk_mem_region_with_cl_DECL(OopClosure); + walk_mem_region_with_cl_DECL(FilteringClosure); + +public: + FreeListSpace_DCTOC(CompactibleFreeListSpace* sp, + CMSCollector* collector, + OopClosure* cl, + CardTableModRefBS::PrecisionStyle precision, + HeapWord* boundary) : + Filtering_DCTOC(sp, cl, precision, boundary), + _cfls(sp), _collector(collector) {} +}; + +// We de-virtualize the block-related calls below, since we know that our +// space is a CompactibleFreeListSpace. +#define FreeListSpace_DCTOC__walk_mem_region_with_cl_DEFN(ClosureType) \ +void FreeListSpace_DCTOC::walk_mem_region_with_cl(MemRegion mr, \ + HeapWord* bottom, \ + HeapWord* top, \ + ClosureType* cl) { \ + if (SharedHeap::heap()->n_par_threads() > 0) { \ + walk_mem_region_with_cl_par(mr, bottom, top, cl); \ + } else { \ + walk_mem_region_with_cl_nopar(mr, bottom, top, cl); \ + } \ +} \ +void FreeListSpace_DCTOC::walk_mem_region_with_cl_par(MemRegion mr, \ + HeapWord* bottom, \ + HeapWord* top, \ + ClosureType* cl) { \ + /* Skip parts that are before "mr", in case "block_start" sent us \ + back too far. */ \ + HeapWord* mr_start = mr.start(); \ + size_t bot_size = _cfls->CompactibleFreeListSpace::block_size(bottom); \ + HeapWord* next = bottom + bot_size; \ + while (next < mr_start) { \ + bottom = next; \ + bot_size = _cfls->CompactibleFreeListSpace::block_size(bottom); \ + next = bottom + bot_size; \ + } \ + \ + while (bottom < top) { \ + if (_cfls->CompactibleFreeListSpace::block_is_obj(bottom) && \ + !_cfls->CompactibleFreeListSpace::obj_allocated_since_save_marks( \ + oop(bottom)) && \ + !_collector->CMSCollector::is_dead_obj(oop(bottom))) { \ + size_t word_sz = oop(bottom)->oop_iterate(cl, mr); \ + bottom += _cfls->adjustObjectSize(word_sz); \ + } else { \ + bottom += _cfls->CompactibleFreeListSpace::block_size(bottom); \ + } \ + } \ +} \ +void FreeListSpace_DCTOC::walk_mem_region_with_cl_nopar(MemRegion mr, \ + HeapWord* bottom, \ + HeapWord* top, \ + ClosureType* cl) { \ + /* Skip parts that are before "mr", in case "block_start" sent us \ + back too far. */ \ + HeapWord* mr_start = mr.start(); \ + size_t bot_size = _cfls->CompactibleFreeListSpace::block_size_nopar(bottom); \ + HeapWord* next = bottom + bot_size; \ + while (next < mr_start) { \ + bottom = next; \ + bot_size = _cfls->CompactibleFreeListSpace::block_size_nopar(bottom); \ + next = bottom + bot_size; \ + } \ + \ + while (bottom < top) { \ + if (_cfls->CompactibleFreeListSpace::block_is_obj_nopar(bottom) && \ + !_cfls->CompactibleFreeListSpace::obj_allocated_since_save_marks( \ + oop(bottom)) && \ + !_collector->CMSCollector::is_dead_obj(oop(bottom))) { \ + size_t word_sz = oop(bottom)->oop_iterate(cl, mr); \ + bottom += _cfls->adjustObjectSize(word_sz); \ + } else { \ + bottom += _cfls->CompactibleFreeListSpace::block_size_nopar(bottom); \ + } \ + } \ +} + +// (There are only two of these, rather than N, because the split is due +// only to the introduction of the FilteringClosure, a local part of the +// impl of this abstraction.) +FreeListSpace_DCTOC__walk_mem_region_with_cl_DEFN(OopClosure) +FreeListSpace_DCTOC__walk_mem_region_with_cl_DEFN(FilteringClosure) + +DirtyCardToOopClosure* +CompactibleFreeListSpace::new_dcto_cl(OopClosure* cl, + CardTableModRefBS::PrecisionStyle precision, + HeapWord* boundary) { + return new FreeListSpace_DCTOC(this, _collector, cl, precision, boundary); +} + + +// Note on locking for the space iteration functions: +// since the collector's iteration activities are concurrent with +// allocation activities by mutators, absent a suitable mutual exclusion +// mechanism the iterators may go awry. For instace a block being iterated +// may suddenly be allocated or divided up and part of it allocated and +// so on. + +// Apply the given closure to each block in the space. +void CompactibleFreeListSpace::blk_iterate_careful(BlkClosureCareful* cl) { + assert_lock_strong(freelistLock()); + HeapWord *cur, *limit; + for (cur = bottom(), limit = end(); cur < limit; + cur += cl->do_blk_careful(cur)); +} + +// Apply the given closure to each block in the space. +void CompactibleFreeListSpace::blk_iterate(BlkClosure* cl) { + assert_lock_strong(freelistLock()); + HeapWord *cur, *limit; + for (cur = bottom(), limit = end(); cur < limit; + cur += cl->do_blk(cur)); +} + +// Apply the given closure to each oop in the space. +void CompactibleFreeListSpace::oop_iterate(OopClosure* cl) { + assert_lock_strong(freelistLock()); + HeapWord *cur, *limit; + size_t curSize; + for (cur = bottom(), limit = end(); cur < limit; + cur += curSize) { + curSize = block_size(cur); + if (block_is_obj(cur)) { + oop(cur)->oop_iterate(cl); + } + } +} + +// Apply the given closure to each oop in the space \intersect memory region. +void CompactibleFreeListSpace::oop_iterate(MemRegion mr, OopClosure* cl) { + assert_lock_strong(freelistLock()); + if (is_empty()) { + return; + } + MemRegion cur = MemRegion(bottom(), end()); + mr = mr.intersection(cur); + if (mr.is_empty()) { + return; + } + if (mr.equals(cur)) { + oop_iterate(cl); + return; + } + assert(mr.end() <= end(), "just took an intersection above"); + HeapWord* obj_addr = block_start(mr.start()); + HeapWord* t = mr.end(); + + SpaceMemRegionOopsIterClosure smr_blk(cl, mr); + if (block_is_obj(obj_addr)) { + // Handle first object specially. + oop obj = oop(obj_addr); + obj_addr += adjustObjectSize(obj->oop_iterate(&smr_blk)); + } else { + FreeChunk* fc = (FreeChunk*)obj_addr; + obj_addr += fc->size(); + } + while (obj_addr < t) { + HeapWord* obj = obj_addr; + obj_addr += block_size(obj_addr); + // If "obj_addr" is not greater than top, then the + // entire object "obj" is within the region. + if (obj_addr <= t) { + if (block_is_obj(obj)) { + oop(obj)->oop_iterate(cl); + } + } else { + // "obj" extends beyond end of region + if (block_is_obj(obj)) { + oop(obj)->oop_iterate(&smr_blk); + } + break; + } + } +} + +// NOTE: In the following methods, in order to safely be able to +// apply the closure to an object, we need to be sure that the +// object has been initialized. We are guaranteed that an object +// is initialized if we are holding the Heap_lock with the +// world stopped. +void CompactibleFreeListSpace::verify_objects_initialized() const { + if (is_init_completed()) { + assert_locked_or_safepoint(Heap_lock); + if (Universe::is_fully_initialized()) { + guarantee(SafepointSynchronize::is_at_safepoint(), + "Required for objects to be initialized"); + } + } // else make a concession at vm start-up +} + +// Apply the given closure to each object in the space +void CompactibleFreeListSpace::object_iterate(ObjectClosure* blk) { + assert_lock_strong(freelistLock()); + NOT_PRODUCT(verify_objects_initialized()); + HeapWord *cur, *limit; + size_t curSize; + for (cur = bottom(), limit = end(); cur < limit; + cur += curSize) { + curSize = block_size(cur); + if (block_is_obj(cur)) { + blk->do_object(oop(cur)); + } + } +} + +void CompactibleFreeListSpace::object_iterate_mem(MemRegion mr, + UpwardsObjectClosure* cl) { + assert_locked(); + NOT_PRODUCT(verify_objects_initialized()); + Space::object_iterate_mem(mr, cl); +} + +// Callers of this iterator beware: The closure application should +// be robust in the face of uninitialized objects and should (always) +// return a correct size so that the next addr + size below gives us a +// valid block boundary. [See for instance, +// ScanMarkedObjectsAgainCarefullyClosure::do_object_careful() +// in ConcurrentMarkSweepGeneration.cpp.] +HeapWord* +CompactibleFreeListSpace::object_iterate_careful(ObjectClosureCareful* cl) { + assert_lock_strong(freelistLock()); + HeapWord *addr, *last; + size_t size; + for (addr = bottom(), last = end(); + addr < last; addr += size) { + FreeChunk* fc = (FreeChunk*)addr; + if (fc->isFree()) { + // Since we hold the free list lock, which protects direct + // allocation in this generation by mutators, a free object + // will remain free throughout this iteration code. + size = fc->size(); + } else { + // Note that the object need not necessarily be initialized, + // because (for instance) the free list lock does NOT protect + // object initialization. The closure application below must + // therefore be correct in the face of uninitialized objects. + size = cl->do_object_careful(oop(addr)); + if (size == 0) { + // An unparsable object found. Signal early termination. + return addr; + } + } + } + return NULL; +} + +// Callers of this iterator beware: The closure application should +// be robust in the face of uninitialized objects and should (always) +// return a correct size so that the next addr + size below gives us a +// valid block boundary. [See for instance, +// ScanMarkedObjectsAgainCarefullyClosure::do_object_careful() +// in ConcurrentMarkSweepGeneration.cpp.] +HeapWord* +CompactibleFreeListSpace::object_iterate_careful_m(MemRegion mr, + ObjectClosureCareful* cl) { + assert_lock_strong(freelistLock()); + // Can't use used_region() below because it may not necessarily + // be the same as [bottom(),end()); although we could + // use [used_region().start(),round_to(used_region().end(),CardSize)), + // that appears too cumbersome, so we just do the simpler check + // in the assertion below. + assert(!mr.is_empty() && MemRegion(bottom(),end()).contains(mr), + "mr should be non-empty and within used space"); + HeapWord *addr, *end; + size_t size; + for (addr = block_start_careful(mr.start()), end = mr.end(); + addr < end; addr += size) { + FreeChunk* fc = (FreeChunk*)addr; + if (fc->isFree()) { + // Since we hold the free list lock, which protects direct + // allocation in this generation by mutators, a free object + // will remain free throughout this iteration code. + size = fc->size(); + } else { + // Note that the object need not necessarily be initialized, + // because (for instance) the free list lock does NOT protect + // object initialization. The closure application below must + // therefore be correct in the face of uninitialized objects. + size = cl->do_object_careful_m(oop(addr), mr); + if (size == 0) { + // An unparsable object found. Signal early termination. + return addr; + } + } + } + return NULL; +} + + +HeapWord* CompactibleFreeListSpace::block_start(const void* p) const { + NOT_PRODUCT(verify_objects_initialized()); + return _bt.block_start(p); +} + +HeapWord* CompactibleFreeListSpace::block_start_careful(const void* p) const { + return _bt.block_start_careful(p); +} + +size_t CompactibleFreeListSpace::block_size(const HeapWord* p) const { + NOT_PRODUCT(verify_objects_initialized()); + assert(MemRegion(bottom(), end()).contains(p), "p not in space"); + // This must be volatile, or else there is a danger that the compiler + // will compile the code below into a sometimes-infinite loop, by keeping + // the value read the first time in a register. + oop o = (oop)p; + volatile oop* second_word_addr = o->klass_addr(); + while (true) { + klassOop k = (klassOop)(*second_word_addr); + // We must do this until we get a consistent view of the object. + if (FreeChunk::secondWordIndicatesFreeChunk((intptr_t)k)) { + FreeChunk* fc = (FreeChunk*)p; + volatile size_t* sz_addr = (volatile size_t*)(fc->size_addr()); + size_t res = (*sz_addr); + klassOop k2 = (klassOop)(*second_word_addr); // Read to confirm. + if (k == k2) { + assert(res != 0, "Block size should not be 0"); + return res; + } + } else if (k != NULL) { + assert(k->is_oop(true /* ignore mark word */), "Should really be klass oop."); + assert(o->is_parsable(), "Should be parsable"); + assert(o->is_oop(true /* ignore mark word */), "Should be an oop."); + size_t res = o->size_given_klass(k->klass_part()); + res = adjustObjectSize(res); + assert(res != 0, "Block size should not be 0"); + return res; + } + } +} + +// A variant of the above that uses the Printezis bits for +// unparsable but allocated objects. This avoids any possible +// stalls waiting for mutators to initialize objects, and is +// thus potentially faster than the variant above. However, +// this variant may return a zero size for a block that is +// under mutation and for which a consistent size cannot be +// inferred without stalling; see CMSCollector::block_size_if_printezis_bits(). +size_t CompactibleFreeListSpace::block_size_no_stall(HeapWord* p, + const CMSCollector* c) +const { + assert(MemRegion(bottom(), end()).contains(p), "p not in space"); + // This must be volatile, or else there is a danger that the compiler + // will compile the code below into a sometimes-infinite loop, by keeping + // the value read the first time in a register. + oop o = (oop)p; + volatile oop* second_word_addr = o->klass_addr(); + DEBUG_ONLY(uint loops = 0;) + while (true) { + klassOop k = (klassOop)(*second_word_addr); + // We must do this until we get a consistent view of the object. + if (FreeChunk::secondWordIndicatesFreeChunk((intptr_t)k)) { + FreeChunk* fc = (FreeChunk*)p; + volatile size_t* sz_addr = (volatile size_t*)(fc->size_addr()); + size_t res = (*sz_addr); + klassOop k2 = (klassOop)(*second_word_addr); // Read to confirm. + if (k == k2) { + assert(res != 0, "Block size should not be 0"); + assert(loops == 0, "Should be 0"); + return res; + } + } else if (k != NULL && o->is_parsable()) { + assert(k->is_oop(), "Should really be klass oop."); + assert(o->is_oop(), "Should be an oop"); + size_t res = o->size_given_klass(k->klass_part()); + res = adjustObjectSize(res); + assert(res != 0, "Block size should not be 0"); + return res; + } else { + return c->block_size_if_printezis_bits(p); + } + assert(loops == 0, "Can loop at most once"); + DEBUG_ONLY(loops++;) + } +} + +size_t CompactibleFreeListSpace::block_size_nopar(const HeapWord* p) const { + NOT_PRODUCT(verify_objects_initialized()); + assert(MemRegion(bottom(), end()).contains(p), "p not in space"); + FreeChunk* fc = (FreeChunk*)p; + if (fc->isFree()) { + return fc->size(); + } else { + // Ignore mark word because this may be a recently promoted + // object whose mark word is used to chain together grey + // objects (the last one would have a null value). + assert(oop(p)->is_oop(true), "Should be an oop"); + return adjustObjectSize(oop(p)->size()); + } +} + +// This implementation assumes that the property of "being an object" is +// stable. But being a free chunk may not be (because of parallel +// promotion.) +bool CompactibleFreeListSpace::block_is_obj(const HeapWord* p) const { + FreeChunk* fc = (FreeChunk*)p; + assert(is_in_reserved(p), "Should be in space"); + // When doing a mark-sweep-compact of the CMS generation, this + // assertion may fail because prepare_for_compaction() uses + // space that is garbage to maintain information on ranges of + // live objects so that these live ranges can be moved as a whole. + // Comment out this assertion until that problem can be solved + // (i.e., that the block start calculation may look at objects + // at address below "p" in finding the object that contains "p" + // and those objects (if garbage) may have been modified to hold + // live range information. + // assert(ParallelGCThreads > 0 || _bt.block_start(p) == p, "Should be a block boundary"); + klassOop k = oop(p)->klass(); + intptr_t ki = (intptr_t)k; + if (FreeChunk::secondWordIndicatesFreeChunk(ki)) return false; + if (k != NULL) { + // Ignore mark word because it may have been used to + // chain together promoted objects (the last one + // would have a null value). + assert(oop(p)->is_oop(true), "Should be an oop"); + return true; + } else { + return false; // Was not an object at the start of collection. + } +} + +// Check if the object is alive. This fact is checked either by consulting +// the main marking bitmap in the sweeping phase or, if it's a permanent +// generation and we're not in the sweeping phase, by checking the +// perm_gen_verify_bit_map where we store the "deadness" information if +// we did not sweep the perm gen in the most recent previous GC cycle. +bool CompactibleFreeListSpace::obj_is_alive(const HeapWord* p) const { + assert (block_is_obj(p), "The address should point to an object"); + + // If we're sweeping, we use object liveness information from the main bit map + // for both perm gen and old gen. + // We don't need to lock the bitmap (live_map or dead_map below), because + // EITHER we are in the middle of the sweeping phase, and the + // main marking bit map (live_map below) is locked, + // OR we're in other phases and perm_gen_verify_bit_map (dead_map below) + // is stable, because it's mutated only in the sweeping phase. + if (_collector->abstract_state() == CMSCollector::Sweeping) { + CMSBitMap* live_map = _collector->markBitMap(); + return live_map->isMarked((HeapWord*) p); + } else { + // If we're not currently sweeping and we haven't swept the perm gen in + // the previous concurrent cycle then we may have dead but unswept objects + // in the perm gen. In this case, we use the "deadness" information + // that we had saved in perm_gen_verify_bit_map at the last sweep. + if (!CMSClassUnloadingEnabled && _collector->_permGen->reserved().contains(p)) { + if (_collector->verifying()) { + CMSBitMap* dead_map = _collector->perm_gen_verify_bit_map(); + // Object is marked in the dead_map bitmap at the previous sweep + // when we know that it's dead; if the bitmap is not allocated then + // the object is alive. + return (dead_map->sizeInBits() == 0) // bit_map has been allocated + || !dead_map->par_isMarked((HeapWord*) p); + } else { + return false; // We can't say for sure if it's live, so we say that it's dead. + } + } + } + return true; +} + +bool CompactibleFreeListSpace::block_is_obj_nopar(const HeapWord* p) const { + FreeChunk* fc = (FreeChunk*)p; + assert(is_in_reserved(p), "Should be in space"); + assert(_bt.block_start(p) == p, "Should be a block boundary"); + if (!fc->isFree()) { + // Ignore mark word because it may have been used to + // chain together promoted objects (the last one + // would have a null value). + assert(oop(p)->is_oop(true), "Should be an oop"); + return true; + } + return false; +} + +// "MT-safe but not guaranteed MT-precise" (TM); you may get an +// approximate answer if you don't hold the freelistlock when you call this. +size_t CompactibleFreeListSpace::totalSizeInIndexedFreeLists() const { + size_t size = 0; + for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + debug_only( + // We may be calling here without the lock in which case we + // won't do this modest sanity check. + if (freelistLock()->owned_by_self()) { + size_t total_list_size = 0; + for (FreeChunk* fc = _indexedFreeList[i].head(); fc != NULL; + fc = fc->next()) { + total_list_size += i; + } + assert(total_list_size == i * _indexedFreeList[i].count(), + "Count in list is incorrect"); + } + ) + size += i * _indexedFreeList[i].count(); + } + return size; +} + +HeapWord* CompactibleFreeListSpace::par_allocate(size_t size) { + MutexLockerEx x(freelistLock(), Mutex::_no_safepoint_check_flag); + return allocate(size); +} + +HeapWord* +CompactibleFreeListSpace::getChunkFromSmallLinearAllocBlockRemainder(size_t size) { + return getChunkFromLinearAllocBlockRemainder(&_smallLinearAllocBlock, size); +} + +HeapWord* CompactibleFreeListSpace::allocate(size_t size) { + assert_lock_strong(freelistLock()); + HeapWord* res = NULL; + assert(size == adjustObjectSize(size), + "use adjustObjectSize() before calling into allocate()"); + + if (_adaptive_freelists) { + res = allocate_adaptive_freelists(size); + } else { // non-adaptive free lists + res = allocate_non_adaptive_freelists(size); + } + + if (res != NULL) { + // check that res does lie in this space! + assert(is_in_reserved(res), "Not in this space!"); + assert(is_aligned((void*)res), "alignment check"); + + FreeChunk* fc = (FreeChunk*)res; + fc->markNotFree(); + assert(!fc->isFree(), "shouldn't be marked free"); + assert(oop(fc)->klass() == NULL, "should look uninitialized"); + // Verify that the block offset table shows this to + // be a single block, but not one which is unallocated. + _bt.verify_single_block(res, size); + _bt.verify_not_unallocated(res, size); + // mangle a just allocated object with a distinct pattern. + debug_only(fc->mangleAllocated(size)); + } + + return res; +} + +HeapWord* CompactibleFreeListSpace::allocate_non_adaptive_freelists(size_t size) { + HeapWord* res = NULL; + // try and use linear allocation for smaller blocks + if (size < _smallLinearAllocBlock._allocation_size_limit) { + // if successful, the following also adjusts block offset table + res = getChunkFromSmallLinearAllocBlock(size); + } + // Else triage to indexed lists for smaller sizes + if (res == NULL) { + if (size < SmallForDictionary) { + res = (HeapWord*) getChunkFromIndexedFreeList(size); + } else { + // else get it from the big dictionary; if even this doesn't + // work we are out of luck. + res = (HeapWord*)getChunkFromDictionaryExact(size); + } + } + + return res; +} + +HeapWord* CompactibleFreeListSpace::allocate_adaptive_freelists(size_t size) { + assert_lock_strong(freelistLock()); + HeapWord* res = NULL; + assert(size == adjustObjectSize(size), + "use adjustObjectSize() before calling into allocate()"); + + // Strategy + // if small + // exact size from small object indexed list if small + // small or large linear allocation block (linAB) as appropriate + // take from lists of greater sized chunks + // else + // dictionary + // small or large linear allocation block if it has the space + // Try allocating exact size from indexTable first + if (size < IndexSetSize) { + res = (HeapWord*) getChunkFromIndexedFreeList(size); + if(res != NULL) { + assert(res != (HeapWord*)_indexedFreeList[size].head(), + "Not removed from free list"); + // no block offset table adjustment is necessary on blocks in + // the indexed lists. + + // Try allocating from the small LinAB + } else if (size < _smallLinearAllocBlock._allocation_size_limit && + (res = getChunkFromSmallLinearAllocBlock(size)) != NULL) { + // if successful, the above also adjusts block offset table + // Note that this call will refill the LinAB to + // satisfy the request. This is different that + // evm. + // Don't record chunk off a LinAB? smallSplitBirth(size); + + } else { + // Raid the exact free lists larger than size, even if they are not + // overpopulated. + res = (HeapWord*) getChunkFromGreater(size); + } + } else { + // Big objects get allocated directly from the dictionary. + res = (HeapWord*) getChunkFromDictionaryExact(size); + if (res == NULL) { + // Try hard not to fail since an allocation failure will likely + // trigger a synchronous GC. Try to get the space from the + // allocation blocks. + res = getChunkFromSmallLinearAllocBlockRemainder(size); + } + } + + return res; +} + +// A worst-case estimate of the space required (in HeapWords) to expand the heap +// when promoting obj. +size_t CompactibleFreeListSpace::expansionSpaceRequired(size_t obj_size) const { + // Depending on the object size, expansion may require refilling either a + // bigLAB or a smallLAB plus refilling a PromotionInfo object. MinChunkSize + // is added because the dictionary may over-allocate to avoid fragmentation. + size_t space = obj_size; + if (!_adaptive_freelists) { + space = MAX2(space, _smallLinearAllocBlock._refillSize); + } + space += _promoInfo.refillSize() + 2 * MinChunkSize; + return space; +} + +FreeChunk* CompactibleFreeListSpace::getChunkFromGreater(size_t numWords) { + FreeChunk* ret; + + assert(numWords >= MinChunkSize, "Size is less than minimum"); + assert(linearAllocationWouldFail() || bestFitFirst(), + "Should not be here"); + + size_t i; + size_t currSize = numWords + MinChunkSize; + assert(currSize % MinObjAlignment == 0, "currSize should be aligned"); + for (i = currSize; i < IndexSetSize; i += IndexSetStride) { + FreeList* fl = &_indexedFreeList[i]; + if (fl->head()) { + ret = getFromListGreater(fl, numWords); + assert(ret == NULL || ret->isFree(), "Should be returning a free chunk"); + return ret; + } + } + + currSize = MAX2((size_t)SmallForDictionary, + (size_t)(numWords + MinChunkSize)); + + /* Try to get a chunk that satisfies request, while avoiding + fragmentation that can't be handled. */ + { + ret = dictionary()->getChunk(currSize); + if (ret != NULL) { + assert(ret->size() - numWords >= MinChunkSize, + "Chunk is too small"); + _bt.allocated((HeapWord*)ret, ret->size()); + /* Carve returned chunk. */ + (void) splitChunkAndReturnRemainder(ret, numWords); + /* Label this as no longer a free chunk. */ + assert(ret->isFree(), "This chunk should be free"); + ret->linkPrev(NULL); + } + assert(ret == NULL || ret->isFree(), "Should be returning a free chunk"); + return ret; + } + ShouldNotReachHere(); +} + +bool CompactibleFreeListSpace::verifyChunkInIndexedFreeLists(FreeChunk* fc) + const { + assert(fc->size() < IndexSetSize, "Size of chunk is too large"); + return _indexedFreeList[fc->size()].verifyChunkInFreeLists(fc); +} + +bool CompactibleFreeListSpace::verifyChunkInFreeLists(FreeChunk* fc) const { + if (fc->size() >= IndexSetSize) { + return dictionary()->verifyChunkInFreeLists(fc); + } else { + return verifyChunkInIndexedFreeLists(fc); + } +} + +#ifndef PRODUCT +void CompactibleFreeListSpace::assert_locked() const { + CMSLockVerifier::assert_locked(freelistLock(), parDictionaryAllocLock()); +} +#endif + +FreeChunk* CompactibleFreeListSpace::allocateScratch(size_t size) { + // In the parallel case, the main thread holds the free list lock + // on behalf the parallel threads. + assert_locked(); + FreeChunk* fc; + { + // If GC is parallel, this might be called by several threads. + // This should be rare enough that the locking overhead won't affect + // the sequential code. + MutexLockerEx x(parDictionaryAllocLock(), + Mutex::_no_safepoint_check_flag); + fc = getChunkFromDictionary(size); + } + if (fc != NULL) { + fc->dontCoalesce(); + assert(fc->isFree(), "Should be free, but not coalescable"); + // Verify that the block offset table shows this to + // be a single block, but not one which is unallocated. + _bt.verify_single_block((HeapWord*)fc, fc->size()); + _bt.verify_not_unallocated((HeapWord*)fc, fc->size()); + } + return fc; +} + +oop CompactibleFreeListSpace::promote(oop obj, size_t obj_size, oop* ref) { + assert(obj_size == (size_t)obj->size(), "bad obj_size passed in"); + assert_locked(); + + // if we are tracking promotions, then first ensure space for + // promotion (including spooling space for saving header if necessary). + // then allocate and copy, then track promoted info if needed. + // When tracking (see PromotionInfo::track()), the mark word may + // be displaced and in this case restoration of the mark word + // occurs in the (oop_since_save_marks_)iterate phase. + if (_promoInfo.tracking() && !_promoInfo.ensure_spooling_space()) { + return NULL; + } + // Call the allocate(size_t, bool) form directly to avoid the + // additional call through the allocate(size_t) form. Having + // the compile inline the call is problematic because allocate(size_t) + // is a virtual method. + HeapWord* res = allocate(adjustObjectSize(obj_size)); + if (res != NULL) { + Copy::aligned_disjoint_words((HeapWord*)obj, res, obj_size); + // if we should be tracking promotions, do so. + if (_promoInfo.tracking()) { + _promoInfo.track((PromotedObject*)res); + } + } + return oop(res); +} + +HeapWord* +CompactibleFreeListSpace::getChunkFromSmallLinearAllocBlock(size_t size) { + assert_locked(); + assert(size >= MinChunkSize, "minimum chunk size"); + assert(size < _smallLinearAllocBlock._allocation_size_limit, + "maximum from smallLinearAllocBlock"); + return getChunkFromLinearAllocBlock(&_smallLinearAllocBlock, size); +} + +HeapWord* +CompactibleFreeListSpace::getChunkFromLinearAllocBlock(LinearAllocBlock *blk, + size_t size) { + assert_locked(); + assert(size >= MinChunkSize, "too small"); + HeapWord* res = NULL; + // Try to do linear allocation from blk, making sure that + if (blk->_word_size == 0) { + // We have probably been unable to fill this either in the prologue or + // when it was exhausted at the last linear allocation. Bail out until + // next time. + assert(blk->_ptr == NULL, "consistency check"); + return NULL; + } + assert(blk->_word_size != 0 && blk->_ptr != NULL, "consistency check"); + res = getChunkFromLinearAllocBlockRemainder(blk, size); + if (res != NULL) return res; + + // about to exhaust this linear allocation block + if (blk->_word_size == size) { // exactly satisfied + res = blk->_ptr; + _bt.allocated(res, blk->_word_size); + } else if (size + MinChunkSize <= blk->_refillSize) { + // Update _unallocated_block if the size is such that chunk would be + // returned to the indexed free list. All other chunks in the indexed + // free lists are allocated from the dictionary so that _unallocated_block + // has already been adjusted for them. Do it here so that the cost + // for all chunks added back to the indexed free lists. + if (blk->_word_size < SmallForDictionary) { + _bt.allocated(blk->_ptr, blk->_word_size); + } + // Return the chunk that isn't big enough, and then refill below. + addChunkToFreeLists(blk->_ptr, blk->_word_size); + _bt.verify_single_block(blk->_ptr, (blk->_ptr + blk->_word_size)); + // Don't keep statistics on adding back chunk from a LinAB. + } else { + // A refilled block would not satisfy the request. + return NULL; + } + + blk->_ptr = NULL; blk->_word_size = 0; + refillLinearAllocBlock(blk); + assert(blk->_ptr == NULL || blk->_word_size >= size + MinChunkSize, + "block was replenished"); + if (res != NULL) { + splitBirth(size); + repairLinearAllocBlock(blk); + } else if (blk->_ptr != NULL) { + res = blk->_ptr; + size_t blk_size = blk->_word_size; + blk->_word_size -= size; + blk->_ptr += size; + splitBirth(size); + repairLinearAllocBlock(blk); + // Update BOT last so that other (parallel) GC threads see a consistent + // view of the BOT and free blocks. + // Above must occur before BOT is updated below. + _bt.split_block(res, blk_size, size); // adjust block offset table + } + return res; +} + +HeapWord* CompactibleFreeListSpace::getChunkFromLinearAllocBlockRemainder( + LinearAllocBlock* blk, + size_t size) { + assert_locked(); + assert(size >= MinChunkSize, "too small"); + + HeapWord* res = NULL; + // This is the common case. Keep it simple. + if (blk->_word_size >= size + MinChunkSize) { + assert(blk->_ptr != NULL, "consistency check"); + res = blk->_ptr; + // Note that the BOT is up-to-date for the linAB before allocation. It + // indicates the start of the linAB. The split_block() updates the + // BOT for the linAB after the allocation (indicates the start of the + // next chunk to be allocated). + size_t blk_size = blk->_word_size; + blk->_word_size -= size; + blk->_ptr += size; + splitBirth(size); + repairLinearAllocBlock(blk); + // Update BOT last so that other (parallel) GC threads see a consistent + // view of the BOT and free blocks. + // Above must occur before BOT is updated below. + _bt.split_block(res, blk_size, size); // adjust block offset table + _bt.allocated(res, size); + } + return res; +} + +FreeChunk* +CompactibleFreeListSpace::getChunkFromIndexedFreeList(size_t size) { + assert_locked(); + assert(size < SmallForDictionary, "just checking"); + FreeChunk* res; + res = _indexedFreeList[size].getChunkAtHead(); + if (res == NULL) { + res = getChunkFromIndexedFreeListHelper(size); + } + _bt.verify_not_unallocated((HeapWord*) res, size); + return res; +} + +FreeChunk* +CompactibleFreeListSpace::getChunkFromIndexedFreeListHelper(size_t size) { + assert_locked(); + FreeChunk* fc = NULL; + if (size < SmallForDictionary) { + assert(_indexedFreeList[size].head() == NULL || + _indexedFreeList[size].surplus() <= 0, + "List for this size should be empty or under populated"); + // Try best fit in exact lists before replenishing the list + if (!bestFitFirst() || (fc = bestFitSmall(size)) == NULL) { + // Replenish list. + // + // Things tried that failed. + // Tried allocating out of the two LinAB's first before + // replenishing lists. + // Tried small linAB of size 256 (size in indexed list) + // and replenishing indexed lists from the small linAB. + // + FreeChunk* newFc = NULL; + size_t replenish_size = CMSIndexedFreeListReplenish * size; + if (replenish_size < SmallForDictionary) { + // Do not replenish from an underpopulated size. + if (_indexedFreeList[replenish_size].surplus() > 0 && + _indexedFreeList[replenish_size].head() != NULL) { + newFc = + _indexedFreeList[replenish_size].getChunkAtHead(); + } else { + newFc = bestFitSmall(replenish_size); + } + } + if (newFc != NULL) { + splitDeath(replenish_size); + } else if (replenish_size > size) { + assert(CMSIndexedFreeListReplenish > 1, "ctl pt invariant"); + newFc = + getChunkFromIndexedFreeListHelper(replenish_size); + } + if (newFc != NULL) { + assert(newFc->size() == replenish_size, "Got wrong size"); + size_t i; + FreeChunk *curFc, *nextFc; + // carve up and link blocks 0, ..., CMSIndexedFreeListReplenish - 2 + // The last chunk is not added to the lists but is returned as the + // free chunk. + for (curFc = newFc, nextFc = (FreeChunk*)((HeapWord*)curFc + size), + i = 0; + i < (CMSIndexedFreeListReplenish - 1); + curFc = nextFc, nextFc = (FreeChunk*)((HeapWord*)nextFc + size), + i++) { + curFc->setSize(size); + // Don't record this as a return in order to try and + // determine the "returns" from a GC. + _bt.verify_not_unallocated((HeapWord*) fc, size); + _indexedFreeList[size].returnChunkAtTail(curFc, false); + _bt.mark_block((HeapWord*)curFc, size); + splitBirth(size); + // Don't record the initial population of the indexed list + // as a split birth. + } + + // check that the arithmetic was OK above + assert((HeapWord*)nextFc == (HeapWord*)newFc + replenish_size, + "inconsistency in carving newFc"); + curFc->setSize(size); + _bt.mark_block((HeapWord*)curFc, size); + splitBirth(size); + return curFc; + } + } + } else { + // Get a free chunk from the free chunk dictionary to be returned to + // replenish the indexed free list. + fc = getChunkFromDictionaryExact(size); + } + assert(fc == NULL || fc->isFree(), "Should be returning a free chunk"); + return fc; +} + +FreeChunk* +CompactibleFreeListSpace::getChunkFromDictionary(size_t size) { + assert_locked(); + FreeChunk* fc = _dictionary->getChunk(size); + if (fc == NULL) { + return NULL; + } + _bt.allocated((HeapWord*)fc, fc->size()); + if (fc->size() >= size + MinChunkSize) { + fc = splitChunkAndReturnRemainder(fc, size); + } + assert(fc->size() >= size, "chunk too small"); + assert(fc->size() < size + MinChunkSize, "chunk too big"); + _bt.verify_single_block((HeapWord*)fc, fc->size()); + return fc; +} + +FreeChunk* +CompactibleFreeListSpace::getChunkFromDictionaryExact(size_t size) { + assert_locked(); + FreeChunk* fc = _dictionary->getChunk(size); + if (fc == NULL) { + return fc; + } + _bt.allocated((HeapWord*)fc, fc->size()); + if (fc->size() == size) { + _bt.verify_single_block((HeapWord*)fc, size); + return fc; + } + assert(fc->size() > size, "getChunk() guarantee"); + if (fc->size() < size + MinChunkSize) { + // Return the chunk to the dictionary and go get a bigger one. + returnChunkToDictionary(fc); + fc = _dictionary->getChunk(size + MinChunkSize); + if (fc == NULL) { + return NULL; + } + _bt.allocated((HeapWord*)fc, fc->size()); + } + assert(fc->size() >= size + MinChunkSize, "tautology"); + fc = splitChunkAndReturnRemainder(fc, size); + assert(fc->size() == size, "chunk is wrong size"); + _bt.verify_single_block((HeapWord*)fc, size); + return fc; +} + +void +CompactibleFreeListSpace::returnChunkToDictionary(FreeChunk* chunk) { + assert_locked(); + + size_t size = chunk->size(); + _bt.verify_single_block((HeapWord*)chunk, size); + // adjust _unallocated_block downward, as necessary + _bt.freed((HeapWord*)chunk, size); + _dictionary->returnChunk(chunk); +} + +void +CompactibleFreeListSpace::returnChunkToFreeList(FreeChunk* fc) { + assert_locked(); + size_t size = fc->size(); + _bt.verify_single_block((HeapWord*) fc, size); + _bt.verify_not_unallocated((HeapWord*) fc, size); + if (_adaptive_freelists) { + _indexedFreeList[size].returnChunkAtTail(fc); + } else { + _indexedFreeList[size].returnChunkAtHead(fc); + } +} + +// Add chunk to end of last block -- if it's the largest +// block -- and update BOT and census data. We would +// of course have preferred to coalesce it with the +// last block, but it's currently less expensive to find the +// largest block than it is to find the last. +void +CompactibleFreeListSpace::addChunkToFreeListsAtEndRecordingStats( + HeapWord* chunk, size_t size) { + // check that the chunk does lie in this space! + assert(chunk != NULL && is_in_reserved(chunk), "Not in this space!"); + assert_locked(); + // One of the parallel gc task threads may be here + // whilst others are allocating. + Mutex* lock = NULL; + if (ParallelGCThreads != 0) { + lock = &_parDictionaryAllocLock; + } + FreeChunk* ec; + { + MutexLockerEx x(lock, Mutex::_no_safepoint_check_flag); + ec = dictionary()->findLargestDict(); // get largest block + if (ec != NULL && ec->end() == chunk) { + // It's a coterminal block - we can coalesce. + size_t old_size = ec->size(); + coalDeath(old_size); + removeChunkFromDictionary(ec); + size += old_size; + } else { + ec = (FreeChunk*)chunk; + } + } + ec->setSize(size); + debug_only(ec->mangleFreed(size)); + if (size < SmallForDictionary) { + lock = _indexedFreeListParLocks[size]; + } + MutexLockerEx x(lock, Mutex::_no_safepoint_check_flag); + addChunkAndRepairOffsetTable((HeapWord*)ec, size, true); + // record the birth under the lock since the recording involves + // manipulation of the list on which the chunk lives and + // if the chunk is allocated and is the last on the list, + // the list can go away. + coalBirth(size); +} + +void +CompactibleFreeListSpace::addChunkToFreeLists(HeapWord* chunk, + size_t size) { + // check that the chunk does lie in this space! + assert(chunk != NULL && is_in_reserved(chunk), "Not in this space!"); + assert_locked(); + _bt.verify_single_block(chunk, size); + + FreeChunk* fc = (FreeChunk*) chunk; + fc->setSize(size); + debug_only(fc->mangleFreed(size)); + if (size < SmallForDictionary) { + returnChunkToFreeList(fc); + } else { + returnChunkToDictionary(fc); + } +} + +void +CompactibleFreeListSpace::addChunkAndRepairOffsetTable(HeapWord* chunk, + size_t size, bool coalesced) { + assert_locked(); + assert(chunk != NULL, "null chunk"); + if (coalesced) { + // repair BOT + _bt.single_block(chunk, size); + } + addChunkToFreeLists(chunk, size); +} + +// We _must_ find the purported chunk on our free lists; +// we assert if we don't. +void +CompactibleFreeListSpace::removeFreeChunkFromFreeLists(FreeChunk* fc) { + size_t size = fc->size(); + assert_locked(); + debug_only(verifyFreeLists()); + if (size < SmallForDictionary) { + removeChunkFromIndexedFreeList(fc); + } else { + removeChunkFromDictionary(fc); + } + _bt.verify_single_block((HeapWord*)fc, size); + debug_only(verifyFreeLists()); +} + +void +CompactibleFreeListSpace::removeChunkFromDictionary(FreeChunk* fc) { + size_t size = fc->size(); + assert_locked(); + assert(fc != NULL, "null chunk"); + _bt.verify_single_block((HeapWord*)fc, size); + _dictionary->removeChunk(fc); + // adjust _unallocated_block upward, as necessary + _bt.allocated((HeapWord*)fc, size); +} + +void +CompactibleFreeListSpace::removeChunkFromIndexedFreeList(FreeChunk* fc) { + assert_locked(); + size_t size = fc->size(); + _bt.verify_single_block((HeapWord*)fc, size); + NOT_PRODUCT( + if (FLSVerifyIndexTable) { + verifyIndexedFreeList(size); + } + ) + _indexedFreeList[size].removeChunk(fc); + debug_only(fc->clearNext()); + debug_only(fc->clearPrev()); + NOT_PRODUCT( + if (FLSVerifyIndexTable) { + verifyIndexedFreeList(size); + } + ) +} + +FreeChunk* CompactibleFreeListSpace::bestFitSmall(size_t numWords) { + /* A hint is the next larger size that has a surplus. + Start search at a size large enough to guarantee that + the excess is >= MIN_CHUNK. */ + size_t start = align_object_size(numWords + MinChunkSize); + if (start < IndexSetSize) { + FreeList* it = _indexedFreeList; + size_t hint = _indexedFreeList[start].hint(); + while (hint < IndexSetSize) { + assert(hint % MinObjAlignment == 0, "hint should be aligned"); + FreeList *fl = &_indexedFreeList[hint]; + if (fl->surplus() > 0 && fl->head() != NULL) { + // Found a list with surplus, reset original hint + // and split out a free chunk which is returned. + _indexedFreeList[start].set_hint(hint); + FreeChunk* res = getFromListGreater(fl, numWords); + assert(res == NULL || res->isFree(), + "Should be returning a free chunk"); + return res; + } + hint = fl->hint(); /* keep looking */ + } + /* None found. */ + it[start].set_hint(IndexSetSize); + } + return NULL; +} + +/* Requires fl->size >= numWords + MinChunkSize */ +FreeChunk* CompactibleFreeListSpace::getFromListGreater(FreeList* fl, + size_t numWords) { + FreeChunk *curr = fl->head(); + size_t oldNumWords = curr->size(); + assert(numWords >= MinChunkSize, "Word size is too small"); + assert(curr != NULL, "List is empty"); + assert(oldNumWords >= numWords + MinChunkSize, + "Size of chunks in the list is too small"); + + fl->removeChunk(curr); + // recorded indirectly by splitChunkAndReturnRemainder - + // smallSplit(oldNumWords, numWords); + FreeChunk* new_chunk = splitChunkAndReturnRemainder(curr, numWords); + // Does anything have to be done for the remainder in terms of + // fixing the card table? + assert(new_chunk == NULL || new_chunk->isFree(), + "Should be returning a free chunk"); + return new_chunk; +} + +FreeChunk* +CompactibleFreeListSpace::splitChunkAndReturnRemainder(FreeChunk* chunk, + size_t new_size) { + assert_locked(); + size_t size = chunk->size(); + assert(size > new_size, "Split from a smaller block?"); + assert(is_aligned(chunk), "alignment problem"); + assert(size == adjustObjectSize(size), "alignment problem"); + size_t rem_size = size - new_size; + assert(rem_size == adjustObjectSize(rem_size), "alignment problem"); + assert(rem_size >= MinChunkSize, "Free chunk smaller than minimum"); + FreeChunk* ffc = (FreeChunk*)((HeapWord*)chunk + new_size); + assert(is_aligned(ffc), "alignment problem"); + ffc->setSize(rem_size); + ffc->linkNext(NULL); + ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. + // Above must occur before BOT is updated below. + // adjust block offset table + _bt.split_block((HeapWord*)chunk, chunk->size(), new_size); + if (rem_size < SmallForDictionary) { + bool is_par = (SharedHeap::heap()->n_par_threads() > 0); + if (is_par) _indexedFreeListParLocks[rem_size]->lock(); + returnChunkToFreeList(ffc); + split(size, rem_size); + if (is_par) _indexedFreeListParLocks[rem_size]->unlock(); + } else { + returnChunkToDictionary(ffc); + split(size ,rem_size); + } + chunk->setSize(new_size); + return chunk; +} + +void +CompactibleFreeListSpace::sweep_completed() { + // Now that space is probably plentiful, refill linear + // allocation blocks as needed. + refillLinearAllocBlocksIfNeeded(); +} + +void +CompactibleFreeListSpace::gc_prologue() { + assert_locked(); + if (PrintFLSStatistics != 0) { + gclog_or_tty->print("Before GC:\n"); + reportFreeListStatistics(); + } + refillLinearAllocBlocksIfNeeded(); +} + +void +CompactibleFreeListSpace::gc_epilogue() { + assert_locked(); + if (PrintGCDetails && Verbose && !_adaptive_freelists) { + if (_smallLinearAllocBlock._word_size == 0) + warning("CompactibleFreeListSpace(epilogue):: Linear allocation failure"); + } + assert(_promoInfo.noPromotions(), "_promoInfo inconsistency"); + _promoInfo.stopTrackingPromotions(); + repairLinearAllocationBlocks(); + // Print Space's stats + if (PrintFLSStatistics != 0) { + gclog_or_tty->print("After GC:\n"); + reportFreeListStatistics(); + } +} + +// Iteration support, mostly delegated from a CMS generation + +void CompactibleFreeListSpace::save_marks() { + // mark the "end" of the used space at the time of this call; + // note, however, that promoted objects from this point + // on are tracked in the _promoInfo below. + set_saved_mark_word(BlockOffsetArrayUseUnallocatedBlock ? + unallocated_block() : end()); + // inform allocator that promotions should be tracked. + assert(_promoInfo.noPromotions(), "_promoInfo inconsistency"); + _promoInfo.startTrackingPromotions(); +} + +bool CompactibleFreeListSpace::no_allocs_since_save_marks() { + assert(_promoInfo.tracking(), "No preceding save_marks?"); + guarantee(SharedHeap::heap()->n_par_threads() == 0, + "Shouldn't be called (yet) during parallel part of gc."); + return _promoInfo.noPromotions(); +} + +#define CFLS_OOP_SINCE_SAVE_MARKS_DEFN(OopClosureType, nv_suffix) \ + \ +void CompactibleFreeListSpace:: \ +oop_since_save_marks_iterate##nv_suffix(OopClosureType* blk) { \ + assert(SharedHeap::heap()->n_par_threads() == 0, \ + "Shouldn't be called (yet) during parallel part of gc."); \ + _promoInfo.promoted_oops_iterate##nv_suffix(blk); \ + /* \ + * This also restores any displaced headers and removes the elements from \ + * the iteration set as they are processed, so that we have a clean slate \ + * at the end of the iteration. Note, thus, that if new objects are \ + * promoted as a result of the iteration they are iterated over as well. \ + */ \ + assert(_promoInfo.noPromotions(), "_promoInfo inconsistency"); \ +} + +ALL_SINCE_SAVE_MARKS_CLOSURES(CFLS_OOP_SINCE_SAVE_MARKS_DEFN) + +////////////////////////////////////////////////////////////////////////////// +// We go over the list of promoted objects, removing each from the list, +// and applying the closure (this may, in turn, add more elements to +// the tail of the promoted list, and these newly added objects will +// also be processed) until the list is empty. +// To aid verification and debugging, in the non-product builds +// we actually forward _promoHead each time we process a promoted oop. +// Note that this is not necessary in general (i.e. when we don't need to +// call PromotionInfo::verify()) because oop_iterate can only add to the +// end of _promoTail, and never needs to look at _promoHead. + +#define PROMOTED_OOPS_ITERATE_DEFN(OopClosureType, nv_suffix) \ + \ +void PromotionInfo::promoted_oops_iterate##nv_suffix(OopClosureType* cl) { \ + NOT_PRODUCT(verify()); \ + PromotedObject *curObj, *nextObj; \ + for (curObj = _promoHead; curObj != NULL; curObj = nextObj) { \ + if ((nextObj = curObj->next()) == NULL) { \ + /* protect ourselves against additions due to closure application \ + below by resetting the list. */ \ + assert(_promoTail == curObj, "Should have been the tail"); \ + _promoHead = _promoTail = NULL; \ + } \ + if (curObj->hasDisplacedMark()) { \ + /* restore displaced header */ \ + oop(curObj)->set_mark(nextDisplacedHeader()); \ + } else { \ + /* restore prototypical header */ \ + oop(curObj)->init_mark(); \ + } \ + /* The "promoted_mark" should now not be set */ \ + assert(!curObj->hasPromotedMark(), \ + "Should have been cleared by restoring displaced mark-word"); \ + NOT_PRODUCT(_promoHead = nextObj); \ + if (cl != NULL) oop(curObj)->oop_iterate(cl); \ + if (nextObj == NULL) { /* start at head of list reset above */ \ + nextObj = _promoHead; \ + } \ + } \ + assert(noPromotions(), "post-condition violation"); \ + assert(_promoHead == NULL && _promoTail == NULL, "emptied promoted list");\ + assert(_spoolHead == _spoolTail, "emptied spooling buffers"); \ + assert(_firstIndex == _nextIndex, "empty buffer"); \ +} + +// This should have been ALL_SINCE_...() just like the others, +// but, because the body of the method above is somehwat longer, +// the MSVC compiler cannot cope; as a workaround, we split the +// macro into its 3 constituent parts below (see original macro +// definition in specializedOopClosures.hpp). +SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG(PROMOTED_OOPS_ITERATE_DEFN) +PROMOTED_OOPS_ITERATE_DEFN(OopsInGenClosure,_v) + + +void CompactibleFreeListSpace::object_iterate_since_last_GC(ObjectClosure* cl) { + // ugghh... how would one do this efficiently for a non-contiguous space? + guarantee(false, "NYI"); +} + +bool CompactibleFreeListSpace::linearAllocationWouldFail() { + return _smallLinearAllocBlock._word_size == 0; +} + +void CompactibleFreeListSpace::repairLinearAllocationBlocks() { + // Fix up linear allocation blocks to look like free blocks + repairLinearAllocBlock(&_smallLinearAllocBlock); +} + +void CompactibleFreeListSpace::repairLinearAllocBlock(LinearAllocBlock* blk) { + assert_locked(); + if (blk->_ptr != NULL) { + assert(blk->_word_size != 0 && blk->_word_size >= MinChunkSize, + "Minimum block size requirement"); + FreeChunk* fc = (FreeChunk*)(blk->_ptr); + fc->setSize(blk->_word_size); + fc->linkPrev(NULL); // mark as free + fc->dontCoalesce(); + assert(fc->isFree(), "just marked it free"); + assert(fc->cantCoalesce(), "just marked it uncoalescable"); + } +} + +void CompactibleFreeListSpace::refillLinearAllocBlocksIfNeeded() { + assert_locked(); + if (_smallLinearAllocBlock._ptr == NULL) { + assert(_smallLinearAllocBlock._word_size == 0, + "Size of linAB should be zero if the ptr is NULL"); + // Reset the linAB refill and allocation size limit. + _smallLinearAllocBlock.set(0, 0, 1024*SmallForLinearAlloc, SmallForLinearAlloc); + } + refillLinearAllocBlockIfNeeded(&_smallLinearAllocBlock); +} + +void +CompactibleFreeListSpace::refillLinearAllocBlockIfNeeded(LinearAllocBlock* blk) { + assert_locked(); + assert((blk->_ptr == NULL && blk->_word_size == 0) || + (blk->_ptr != NULL && blk->_word_size >= MinChunkSize), + "blk invariant"); + if (blk->_ptr == NULL) { + refillLinearAllocBlock(blk); + } + if (PrintMiscellaneous && Verbose) { + if (blk->_word_size == 0) { + warning("CompactibleFreeListSpace(prologue):: Linear allocation failure"); + } + } +} + +void +CompactibleFreeListSpace::refillLinearAllocBlock(LinearAllocBlock* blk) { + assert_locked(); + assert(blk->_word_size == 0 && blk->_ptr == NULL, + "linear allocation block should be empty"); + FreeChunk* fc; + if (blk->_refillSize < SmallForDictionary && + (fc = getChunkFromIndexedFreeList(blk->_refillSize)) != NULL) { + // A linAB's strategy might be to use small sizes to reduce + // fragmentation but still get the benefits of allocation from a + // linAB. + } else { + fc = getChunkFromDictionary(blk->_refillSize); + } + if (fc != NULL) { + blk->_ptr = (HeapWord*)fc; + blk->_word_size = fc->size(); + fc->dontCoalesce(); // to prevent sweeper from sweeping us up + } +} + +// Support for compaction + +void CompactibleFreeListSpace::prepare_for_compaction(CompactPoint* cp) { + SCAN_AND_FORWARD(cp,end,block_is_obj,block_size); + // prepare_for_compaction() uses the space between live objects + // so that later phase can skip dead space quickly. So verification + // of the free lists doesn't work after. +} + +#define obj_size(q) adjustObjectSize(oop(q)->size()) +#define adjust_obj_size(s) adjustObjectSize(s) + +void CompactibleFreeListSpace::adjust_pointers() { + // In other versions of adjust_pointers(), a bail out + // based on the amount of live data in the generation + // (i.e., if 0, bail out) may be used. + // Cannot test used() == 0 here because the free lists have already + // been mangled by the compaction. + + SCAN_AND_ADJUST_POINTERS(adjust_obj_size); + // See note about verification in prepare_for_compaction(). +} + +void CompactibleFreeListSpace::compact() { + SCAN_AND_COMPACT(obj_size); +} + +// fragmentation_metric = 1 - [sum of (fbs**2) / (sum of fbs)**2] +// where fbs is free block sizes +double CompactibleFreeListSpace::flsFrag() const { + size_t itabFree = totalSizeInIndexedFreeLists(); + double frag = 0.0; + size_t i; + + for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + double sz = i; + frag += _indexedFreeList[i].count() * (sz * sz); + } + + double totFree = itabFree + + _dictionary->totalChunkSize(DEBUG_ONLY(freelistLock())); + if (totFree > 0) { + frag = ((frag + _dictionary->sum_of_squared_block_sizes()) / + (totFree * totFree)); + frag = (double)1.0 - frag; + } else { + assert(frag == 0.0, "Follows from totFree == 0"); + } + return frag; +} + +#define CoalSurplusPercent 1.05 +#define SplitSurplusPercent 1.10 + +void CompactibleFreeListSpace::beginSweepFLCensus( + float inter_sweep_current, + float inter_sweep_estimate) { + assert_locked(); + size_t i; + for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + FreeList* fl = &_indexedFreeList[i]; + fl->compute_desired(inter_sweep_current, inter_sweep_estimate); + fl->set_coalDesired((ssize_t)((double)fl->desired() * CoalSurplusPercent)); + fl->set_beforeSweep(fl->count()); + fl->set_bfrSurp(fl->surplus()); + } + _dictionary->beginSweepDictCensus(CoalSurplusPercent, + inter_sweep_current, + inter_sweep_estimate); +} + +void CompactibleFreeListSpace::setFLSurplus() { + assert_locked(); + size_t i; + for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + FreeList *fl = &_indexedFreeList[i]; + fl->set_surplus(fl->count() - + (ssize_t)((double)fl->desired() * SplitSurplusPercent)); + } +} + +void CompactibleFreeListSpace::setFLHints() { + assert_locked(); + size_t i; + size_t h = IndexSetSize; + for (i = IndexSetSize - 1; i != 0; i -= IndexSetStride) { + FreeList *fl = &_indexedFreeList[i]; + fl->set_hint(h); + if (fl->surplus() > 0) { + h = i; + } + } +} + +void CompactibleFreeListSpace::clearFLCensus() { + assert_locked(); + int i; + for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + FreeList *fl = &_indexedFreeList[i]; + fl->set_prevSweep(fl->count()); + fl->set_coalBirths(0); + fl->set_coalDeaths(0); + fl->set_splitBirths(0); + fl->set_splitDeaths(0); + } +} + +void CompactibleFreeListSpace::endSweepFLCensus(int sweepCt) { + setFLSurplus(); + setFLHints(); + if (PrintGC && PrintFLSCensus > 0) { + printFLCensus(sweepCt); + } + clearFLCensus(); + assert_locked(); + _dictionary->endSweepDictCensus(SplitSurplusPercent); +} + +bool CompactibleFreeListSpace::coalOverPopulated(size_t size) { + if (size < SmallForDictionary) { + FreeList *fl = &_indexedFreeList[size]; + return (fl->coalDesired() < 0) || + ((int)fl->count() > fl->coalDesired()); + } else { + return dictionary()->coalDictOverPopulated(size); + } +} + +void CompactibleFreeListSpace::smallCoalBirth(size_t size) { + assert(size < SmallForDictionary, "Size too large for indexed list"); + FreeList *fl = &_indexedFreeList[size]; + fl->increment_coalBirths(); + fl->increment_surplus(); +} + +void CompactibleFreeListSpace::smallCoalDeath(size_t size) { + assert(size < SmallForDictionary, "Size too large for indexed list"); + FreeList *fl = &_indexedFreeList[size]; + fl->increment_coalDeaths(); + fl->decrement_surplus(); +} + +void CompactibleFreeListSpace::coalBirth(size_t size) { + if (size < SmallForDictionary) { + smallCoalBirth(size); + } else { + dictionary()->dictCensusUpdate(size, + false /* split */, + true /* birth */); + } +} + +void CompactibleFreeListSpace::coalDeath(size_t size) { + if(size < SmallForDictionary) { + smallCoalDeath(size); + } else { + dictionary()->dictCensusUpdate(size, + false /* split */, + false /* birth */); + } +} + +void CompactibleFreeListSpace::smallSplitBirth(size_t size) { + assert(size < SmallForDictionary, "Size too large for indexed list"); + FreeList *fl = &_indexedFreeList[size]; + fl->increment_splitBirths(); + fl->increment_surplus(); +} + +void CompactibleFreeListSpace::smallSplitDeath(size_t size) { + assert(size < SmallForDictionary, "Size too large for indexed list"); + FreeList *fl = &_indexedFreeList[size]; + fl->increment_splitDeaths(); + fl->decrement_surplus(); +} + +void CompactibleFreeListSpace::splitBirth(size_t size) { + if (size < SmallForDictionary) { + smallSplitBirth(size); + } else { + dictionary()->dictCensusUpdate(size, + true /* split */, + true /* birth */); + } +} + +void CompactibleFreeListSpace::splitDeath(size_t size) { + if (size < SmallForDictionary) { + smallSplitDeath(size); + } else { + dictionary()->dictCensusUpdate(size, + true /* split */, + false /* birth */); + } +} + +void CompactibleFreeListSpace::split(size_t from, size_t to1) { + size_t to2 = from - to1; + splitDeath(from); + splitBirth(to1); + splitBirth(to2); +} + + +void CompactibleFreeListSpace::print() const { + tty->print(" CompactibleFreeListSpace"); + Space::print(); +} + +void CompactibleFreeListSpace::prepare_for_verify() { + assert_locked(); + repairLinearAllocationBlocks(); + // Verify that the SpoolBlocks look like free blocks of + // appropriate sizes... To be done ... +} + +class VerifyAllBlksClosure: public BlkClosure { + const CompactibleFreeListSpace* _sp; + const MemRegion _span; + + public: + VerifyAllBlksClosure(const CompactibleFreeListSpace* sp, + MemRegion span) : _sp(sp), _span(span) { } + + size_t do_blk(HeapWord* addr) { + size_t res; + if (_sp->block_is_obj(addr)) { + oop p = oop(addr); + guarantee(p->is_oop(), "Should be an oop"); + res = _sp->adjustObjectSize(p->size()); + if (_sp->obj_is_alive(addr)) { + p->verify(); + } + } else { + FreeChunk* fc = (FreeChunk*)addr; + res = fc->size(); + if (FLSVerifyLists && !fc->cantCoalesce()) { + guarantee(_sp->verifyChunkInFreeLists(fc), + "Chunk should be on a free list"); + } + } + guarantee(res != 0, "Livelock: no rank reduction!"); + return res; + } +}; + +class VerifyAllOopsClosure: public OopClosure { + const CMSCollector* _collector; + const CompactibleFreeListSpace* _sp; + const MemRegion _span; + const bool _past_remark; + const CMSBitMap* _bit_map; + + public: + VerifyAllOopsClosure(const CMSCollector* collector, + const CompactibleFreeListSpace* sp, MemRegion span, + bool past_remark, CMSBitMap* bit_map) : + OopClosure(), _collector(collector), _sp(sp), _span(span), + _past_remark(past_remark), _bit_map(bit_map) { } + + void do_oop(oop* ptr) { + oop p = *ptr; + if (p != NULL) { + if (_span.contains(p)) { // the interior oop points into CMS heap + if (!_span.contains(ptr)) { // reference from outside CMS heap + // Should be a valid object; the first disjunct below allows + // us to sidestep an assertion in block_is_obj() that insists + // that p be in _sp. Note that several generations (and spaces) + // are spanned by _span (CMS heap) above. + guarantee(!_sp->is_in_reserved(p) || _sp->block_is_obj((HeapWord*)p), + "Should be an object"); + guarantee(p->is_oop(), "Should be an oop"); + p->verify(); + if (_past_remark) { + // Remark has been completed, the object should be marked + _bit_map->isMarked((HeapWord*)p); + } + } + else { // reference within CMS heap + if (_past_remark) { + // Remark has been completed -- so the referent should have + // been marked, if referring object is. + if (_bit_map->isMarked(_collector->block_start(ptr))) { + guarantee(_bit_map->isMarked((HeapWord*)p), "Marking error?"); + } + } + } + } else if (_sp->is_in_reserved(ptr)) { + // the reference is from FLS, and points out of FLS + guarantee(p->is_oop(), "Should be an oop"); + p->verify(); + } + } + } +}; + +void CompactibleFreeListSpace::verify(bool ignored) const { + assert_lock_strong(&_freelistLock); + verify_objects_initialized(); + MemRegion span = _collector->_span; + bool past_remark = (_collector->abstract_state() == + CMSCollector::Sweeping); + + ResourceMark rm; + HandleMark hm; + + // Check integrity of CFL data structures + _promoInfo.verify(); + _dictionary->verify(); + if (FLSVerifyIndexTable) { + verifyIndexedFreeLists(); + } + // Check integrity of all objects and free blocks in space + { + VerifyAllBlksClosure cl(this, span); + ((CompactibleFreeListSpace*)this)->blk_iterate(&cl); // cast off const + } + // Check that all references in the heap to FLS + // are to valid objects in FLS or that references in + // FLS are to valid objects elsewhere in the heap + if (FLSVerifyAllHeapReferences) + { + VerifyAllOopsClosure cl(_collector, this, span, past_remark, + _collector->markBitMap()); + CollectedHeap* ch = Universe::heap(); + ch->oop_iterate(&cl); // all oops in generations + ch->permanent_oop_iterate(&cl); // all oops in perm gen + } + + if (VerifyObjectStartArray) { + // Verify the block offset table + _bt.verify(); + } +} + +#ifndef PRODUCT +void CompactibleFreeListSpace::verifyFreeLists() const { + if (FLSVerifyLists) { + _dictionary->verify(); + verifyIndexedFreeLists(); + } else { + if (FLSVerifyDictionary) { + _dictionary->verify(); + } + if (FLSVerifyIndexTable) { + verifyIndexedFreeLists(); + } + } +} +#endif + +void CompactibleFreeListSpace::verifyIndexedFreeLists() const { + size_t i = 0; + for (; i < MinChunkSize; i++) { + guarantee(_indexedFreeList[i].head() == NULL, "should be NULL"); + } + for (; i < IndexSetSize; i++) { + verifyIndexedFreeList(i); + } +} + +void CompactibleFreeListSpace::verifyIndexedFreeList(size_t size) const { + guarantee(size % 2 == 0, "Odd slots should be empty"); + for (FreeChunk* fc = _indexedFreeList[size].head(); fc != NULL; + fc = fc->next()) { + guarantee(fc->size() == size, "Size inconsistency"); + guarantee(fc->isFree(), "!free?"); + guarantee(fc->next() == NULL || fc->next()->prev() == fc, "Broken list"); + } +} + +#ifndef PRODUCT +void CompactibleFreeListSpace::checkFreeListConsistency() const { + assert(_dictionary->minSize() <= IndexSetSize, + "Some sizes can't be allocated without recourse to" + " linear allocation buffers"); + assert(MIN_TREE_CHUNK_SIZE*HeapWordSize == sizeof(TreeChunk), + "else MIN_TREE_CHUNK_SIZE is wrong"); + assert((IndexSetStride == 2 && IndexSetStart == 2) || + (IndexSetStride == 1 && IndexSetStart == 1), "just checking"); + assert((IndexSetStride != 2) || (MinChunkSize % 2 == 0), + "Some for-loops may be incorrectly initialized"); + assert((IndexSetStride != 2) || (IndexSetSize % 2 == 1), + "For-loops that iterate over IndexSet with stride 2 may be wrong"); +} +#endif + +void CompactibleFreeListSpace::printFLCensus(int sweepCt) const { + assert_lock_strong(&_freelistLock); + ssize_t bfrSurp = 0; + ssize_t surplus = 0; + ssize_t desired = 0; + ssize_t prevSweep = 0; + ssize_t beforeSweep = 0; + ssize_t count = 0; + ssize_t coalBirths = 0; + ssize_t coalDeaths = 0; + ssize_t splitBirths = 0; + ssize_t splitDeaths = 0; + gclog_or_tty->print("end sweep# %d\n", sweepCt); + gclog_or_tty->print("%4s\t" "%7s\t" "%7s\t" "%7s\t" "%7s\t" + "%7s\t" "%7s\t" "%7s\t" "%7s\t" "%7s\t" + "%7s\t" "\n", + "size", "bfrsurp", "surplus", "desired", "prvSwep", + "bfrSwep", "count", "cBirths", "cDeaths", "sBirths", + "sDeaths"); + + size_t totalFree = 0; + for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + const FreeList *fl = &_indexedFreeList[i]; + totalFree += fl->count() * fl->size(); + + gclog_or_tty->print("%4d\t" "%7d\t" "%7d\t" "%7d\t" + "%7d\t" "%7d\t" "%7d\t" "%7d\t" + "%7d\t" "%7d\t" "%7d\t" "\n", + fl->size(), fl->bfrSurp(), fl->surplus(), fl->desired(), + fl->prevSweep(), fl->beforeSweep(), fl->count(), fl->coalBirths(), + fl->coalDeaths(), fl->splitBirths(), fl->splitDeaths()); + bfrSurp += fl->bfrSurp(); + surplus += fl->surplus(); + desired += fl->desired(); + prevSweep += fl->prevSweep(); + beforeSweep += fl->beforeSweep(); + count += fl->count(); + coalBirths += fl->coalBirths(); + coalDeaths += fl->coalDeaths(); + splitBirths += fl->splitBirths(); + splitDeaths += fl->splitDeaths(); + } + gclog_or_tty->print("%4s\t" + "%7d\t" "%7d\t" "%7d\t" "%7d\t" "%7d\t" + "%7d\t" "%7d\t" "%7d\t" "%7d\t" "%7d\t" "\n", + "totl", + bfrSurp, surplus, desired, prevSweep, beforeSweep, + count, coalBirths, coalDeaths, splitBirths, splitDeaths); + gclog_or_tty->print_cr("Total free in indexed lists %d words", totalFree); + gclog_or_tty->print("growth: %8.5f deficit: %8.5f\n", + (double)(splitBirths+coalBirths-splitDeaths-coalDeaths)/ + (prevSweep != 0 ? (double)prevSweep : 1.0), + (double)(desired - count)/(desired != 0 ? (double)desired : 1.0)); + _dictionary->printDictCensus(); +} + +// Return the next displaced header, incrementing the pointer and +// recycling spool area as necessary. +markOop PromotionInfo::nextDisplacedHeader() { + assert(_spoolHead != NULL, "promotionInfo inconsistency"); + assert(_spoolHead != _spoolTail || _firstIndex < _nextIndex, + "Empty spool space: no displaced header can be fetched"); + assert(_spoolHead->bufferSize > _firstIndex, "Off by one error at head?"); + markOop hdr = _spoolHead->displacedHdr[_firstIndex]; + // Spool forward + if (++_firstIndex == _spoolHead->bufferSize) { // last location in this block + // forward to next block, recycling this block into spare spool buffer + SpoolBlock* tmp = _spoolHead->nextSpoolBlock; + assert(_spoolHead != _spoolTail, "Spooling storage mix-up"); + _spoolHead->nextSpoolBlock = _spareSpool; + _spareSpool = _spoolHead; + _spoolHead = tmp; + _firstIndex = 1; + NOT_PRODUCT( + if (_spoolHead == NULL) { // all buffers fully consumed + assert(_spoolTail == NULL && _nextIndex == 1, + "spool buffers processing inconsistency"); + } + ) + } + return hdr; +} + +void PromotionInfo::track(PromotedObject* trackOop) { + track(trackOop, oop(trackOop)->klass()); +} + +void PromotionInfo::track(PromotedObject* trackOop, klassOop klassOfOop) { + // make a copy of header as it may need to be spooled + markOop mark = oop(trackOop)->mark(); + trackOop->clearNext(); + if (mark->must_be_preserved_for_cms_scavenge(klassOfOop)) { + // save non-prototypical header, and mark oop + saveDisplacedHeader(mark); + trackOop->setDisplacedMark(); + } else { + // we'd like to assert something like the following: + // assert(mark == markOopDesc::prototype(), "consistency check"); + // ... but the above won't work because the age bits have not (yet) been + // cleared. The remainder of the check would be identical to the + // condition checked in must_be_preserved() above, so we don't really + // have anything useful to check here! + } + if (_promoTail != NULL) { + assert(_promoHead != NULL, "List consistency"); + _promoTail->setNext(trackOop); + _promoTail = trackOop; + } else { + assert(_promoHead == NULL, "List consistency"); + _promoHead = _promoTail = trackOop; + } + // Mask as newly promoted, so we can skip over such objects + // when scanning dirty cards + assert(!trackOop->hasPromotedMark(), "Should not have been marked"); + trackOop->setPromotedMark(); +} + +// Save the given displaced header, incrementing the pointer and +// obtaining more spool area as necessary. +void PromotionInfo::saveDisplacedHeader(markOop hdr) { + assert(_spoolHead != NULL && _spoolTail != NULL, + "promotionInfo inconsistency"); + assert(_spoolTail->bufferSize > _nextIndex, "Off by one error at tail?"); + _spoolTail->displacedHdr[_nextIndex] = hdr; + // Spool forward + if (++_nextIndex == _spoolTail->bufferSize) { // last location in this block + // get a new spooling block + assert(_spoolTail->nextSpoolBlock == NULL, "tail should terminate spool list"); + _splice_point = _spoolTail; // save for splicing + _spoolTail->nextSpoolBlock = getSpoolBlock(); // might fail + _spoolTail = _spoolTail->nextSpoolBlock; // might become NULL ... + // ... but will attempt filling before next promotion attempt + _nextIndex = 1; + } +} + +// Ensure that spooling space exists. Return false if spooling space +// could not be obtained. +bool PromotionInfo::ensure_spooling_space_work() { + assert(!has_spooling_space(), "Only call when there is no spooling space"); + // Try and obtain more spooling space + SpoolBlock* newSpool = getSpoolBlock(); + assert(newSpool == NULL || + (newSpool->bufferSize != 0 && newSpool->nextSpoolBlock == NULL), + "getSpoolBlock() sanity check"); + if (newSpool == NULL) { + return false; + } + _nextIndex = 1; + if (_spoolTail == NULL) { + _spoolTail = newSpool; + if (_spoolHead == NULL) { + _spoolHead = newSpool; + _firstIndex = 1; + } else { + assert(_splice_point != NULL && _splice_point->nextSpoolBlock == NULL, + "Splice point invariant"); + // Extra check that _splice_point is connected to list + #ifdef ASSERT + { + SpoolBlock* blk = _spoolHead; + for (; blk->nextSpoolBlock != NULL; + blk = blk->nextSpoolBlock); + assert(blk != NULL && blk == _splice_point, + "Splice point incorrect"); + } + #endif // ASSERT + _splice_point->nextSpoolBlock = newSpool; + } + } else { + assert(_spoolHead != NULL, "spool list consistency"); + _spoolTail->nextSpoolBlock = newSpool; + _spoolTail = newSpool; + } + return true; +} + +// Get a free spool buffer from the free pool, getting a new block +// from the heap if necessary. +SpoolBlock* PromotionInfo::getSpoolBlock() { + SpoolBlock* res; + if ((res = _spareSpool) != NULL) { + _spareSpool = _spareSpool->nextSpoolBlock; + res->nextSpoolBlock = NULL; + } else { // spare spool exhausted, get some from heap + res = (SpoolBlock*)(space()->allocateScratch(refillSize())); + if (res != NULL) { + res->init(); + } + } + assert(res == NULL || res->nextSpoolBlock == NULL, "postcondition"); + return res; +} + +void PromotionInfo::startTrackingPromotions() { + assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex, + "spooling inconsistency?"); + _firstIndex = _nextIndex = 1; + _tracking = true; +} + +void PromotionInfo::stopTrackingPromotions() { + assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex, + "spooling inconsistency?"); + _firstIndex = _nextIndex = 1; + _tracking = false; +} + +// When _spoolTail is not NULL, then the slot <_spoolTail, _nextIndex> +// points to the next slot available for filling. +// The set of slots holding displaced headers are then all those in the +// right-open interval denoted by: +// +// [ <_spoolHead, _firstIndex>, <_spoolTail, _nextIndex> ) +// +// When _spoolTail is NULL, then the set of slots with displaced headers +// is all those starting at the slot <_spoolHead, _firstIndex> and +// going up to the last slot of last block in the linked list. +// In this lartter case, _splice_point points to the tail block of +// this linked list of blocks holding displaced headers. +void PromotionInfo::verify() const { + // Verify the following: + // 1. the number of displaced headers matches the number of promoted + // objects that have displaced headers + // 2. each promoted object lies in this space + debug_only( + PromotedObject* junk = NULL; + assert(junk->next_addr() == (void*)(oop(junk)->mark_addr()), + "Offset of PromotedObject::_next is expected to align with " + " the OopDesc::_mark within OopDesc"); + ) + // FIXME: guarantee???? + guarantee(_spoolHead == NULL || _spoolTail != NULL || + _splice_point != NULL, "list consistency"); + guarantee(_promoHead == NULL || _promoTail != NULL, "list consistency"); + // count the number of objects with displaced headers + size_t numObjsWithDisplacedHdrs = 0; + for (PromotedObject* curObj = _promoHead; curObj != NULL; curObj = curObj->next()) { + guarantee(space()->is_in_reserved((HeapWord*)curObj), "Containment"); + // the last promoted object may fail the mark() != NULL test of is_oop(). + guarantee(curObj->next() == NULL || oop(curObj)->is_oop(), "must be an oop"); + if (curObj->hasDisplacedMark()) { + numObjsWithDisplacedHdrs++; + } + } + // Count the number of displaced headers + size_t numDisplacedHdrs = 0; + for (SpoolBlock* curSpool = _spoolHead; + curSpool != _spoolTail && curSpool != NULL; + curSpool = curSpool->nextSpoolBlock) { + // the first entry is just a self-pointer; indices 1 through + // bufferSize - 1 are occupied (thus, bufferSize - 1 slots). + guarantee((void*)curSpool->displacedHdr == (void*)&curSpool->displacedHdr, + "first entry of displacedHdr should be self-referential"); + numDisplacedHdrs += curSpool->bufferSize - 1; + } + guarantee((_spoolHead == _spoolTail) == (numDisplacedHdrs == 0), + "internal consistency"); + guarantee(_spoolTail != NULL || _nextIndex == 1, + "Inconsistency between _spoolTail and _nextIndex"); + // We overcounted (_firstIndex-1) worth of slots in block + // _spoolHead and we undercounted (_nextIndex-1) worth of + // slots in block _spoolTail. We make an appropriate + // adjustment by subtracting the first and adding the + // second: - (_firstIndex - 1) + (_nextIndex - 1) + numDisplacedHdrs += (_nextIndex - _firstIndex); + guarantee(numDisplacedHdrs == numObjsWithDisplacedHdrs, "Displaced hdr count"); +} + + +CFLS_LAB::CFLS_LAB(CompactibleFreeListSpace* cfls) : + _cfls(cfls) +{ + _blocks_to_claim = CMSParPromoteBlocksToClaim; + for (size_t i = CompactibleFreeListSpace::IndexSetStart; + i < CompactibleFreeListSpace::IndexSetSize; + i += CompactibleFreeListSpace::IndexSetStride) { + _indexedFreeList[i].set_size(i); + } +} + +HeapWord* CFLS_LAB::alloc(size_t word_sz) { + FreeChunk* res; + word_sz = _cfls->adjustObjectSize(word_sz); + if (word_sz >= CompactibleFreeListSpace::IndexSetSize) { + // This locking manages sync with other large object allocations. + MutexLockerEx x(_cfls->parDictionaryAllocLock(), + Mutex::_no_safepoint_check_flag); + res = _cfls->getChunkFromDictionaryExact(word_sz); + if (res == NULL) return NULL; + } else { + FreeList* fl = &_indexedFreeList[word_sz]; + bool filled = false; //TRAP + if (fl->count() == 0) { + bool filled = true; //TRAP + // Attempt to refill this local free list. + _cfls->par_get_chunk_of_blocks(word_sz, _blocks_to_claim, fl); + // If it didn't work, give up. + if (fl->count() == 0) return NULL; + } + res = fl->getChunkAtHead(); + assert(res != NULL, "Why was count non-zero?"); + } + res->markNotFree(); + assert(!res->isFree(), "shouldn't be marked free"); + assert(oop(res)->klass() == NULL, "should look uninitialized"); + // mangle a just allocated object with a distinct pattern. + debug_only(res->mangleAllocated(word_sz)); + return (HeapWord*)res; +} + +void CFLS_LAB::retire() { + for (size_t i = CompactibleFreeListSpace::IndexSetStart; + i < CompactibleFreeListSpace::IndexSetSize; + i += CompactibleFreeListSpace::IndexSetStride) { + if (_indexedFreeList[i].count() > 0) { + MutexLockerEx x(_cfls->_indexedFreeListParLocks[i], + Mutex::_no_safepoint_check_flag); + _cfls->_indexedFreeList[i].prepend(&_indexedFreeList[i]); + // Reset this list. + _indexedFreeList[i] = FreeList(); + _indexedFreeList[i].set_size(i); + } + } +} + +void +CompactibleFreeListSpace:: +par_get_chunk_of_blocks(size_t word_sz, size_t n, FreeList* fl) { + assert(fl->count() == 0, "Precondition."); + assert(word_sz < CompactibleFreeListSpace::IndexSetSize, + "Precondition"); + + // We'll try all multiples of word_sz in the indexed set (starting with + // word_sz itself), then try getting a big chunk and splitting it. + int k = 1; + size_t cur_sz = k * word_sz; + bool found = false; + while (cur_sz < CompactibleFreeListSpace::IndexSetSize && k == 1) { + FreeList* gfl = &_indexedFreeList[cur_sz]; + FreeList fl_for_cur_sz; // Empty. + fl_for_cur_sz.set_size(cur_sz); + { + MutexLockerEx x(_indexedFreeListParLocks[cur_sz], + Mutex::_no_safepoint_check_flag); + if (gfl->count() != 0) { + size_t nn = MAX2(n/k, (size_t)1); + gfl->getFirstNChunksFromList(nn, &fl_for_cur_sz); + found = true; + } + } + // Now transfer fl_for_cur_sz to fl. Common case, we hope, is k = 1. + if (found) { + if (k == 1) { + fl->prepend(&fl_for_cur_sz); + } else { + // Divide each block on fl_for_cur_sz up k ways. + FreeChunk* fc; + while ((fc = fl_for_cur_sz.getChunkAtHead()) != NULL) { + // Must do this in reverse order, so that anybody attempting to + // access the main chunk sees it as a single free block until we + // change it. + size_t fc_size = fc->size(); + for (int i = k-1; i >= 0; i--) { + FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz); + ffc->setSize(word_sz); + ffc->linkNext(NULL); + ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. + // Above must occur before BOT is updated below. + // splitting from the right, fc_size == (k - i + 1) * wordsize + _bt.mark_block((HeapWord*)ffc, word_sz); + fc_size -= word_sz; + _bt.verify_not_unallocated((HeapWord*)ffc, ffc->size()); + _bt.verify_single_block((HeapWord*)fc, fc_size); + _bt.verify_single_block((HeapWord*)ffc, ffc->size()); + // Push this on "fl". + fl->returnChunkAtHead(ffc); + } + // TRAP + assert(fl->tail()->next() == NULL, "List invariant."); + } + } + return; + } + k++; cur_sz = k * word_sz; + } + // Otherwise, we'll split a block from the dictionary. + FreeChunk* fc = NULL; + FreeChunk* rem_fc = NULL; + size_t rem; + { + MutexLockerEx x(parDictionaryAllocLock(), + Mutex::_no_safepoint_check_flag); + while (n > 0) { + fc = dictionary()->getChunk(MAX2(n * word_sz, + _dictionary->minSize()), + FreeBlockDictionary::atLeast); + if (fc != NULL) { + _bt.allocated((HeapWord*)fc, fc->size()); // update _unallocated_blk + dictionary()->dictCensusUpdate(fc->size(), + true /*split*/, + false /*birth*/); + break; + } else { + n--; + } + } + if (fc == NULL) return; + // Otherwise, split up that block. + size_t nn = fc->size() / word_sz; + n = MIN2(nn, n); + rem = fc->size() - n * word_sz; + // If there is a remainder, and it's too small, allocate one fewer. + if (rem > 0 && rem < MinChunkSize) { + n--; rem += word_sz; + } + // First return the remainder, if any. + // Note that we hold the lock until we decide if we're going to give + // back the remainder to the dictionary, since a contending allocator + // may otherwise see the heap as empty. (We're willing to take that + // hit if the block is a small block.) + if (rem > 0) { + size_t prefix_size = n * word_sz; + rem_fc = (FreeChunk*)((HeapWord*)fc + prefix_size); + rem_fc->setSize(rem); + rem_fc->linkNext(NULL); + rem_fc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. + // Above must occur before BOT is updated below. + _bt.split_block((HeapWord*)fc, fc->size(), prefix_size); + if (rem >= IndexSetSize) { + returnChunkToDictionary(rem_fc); + dictionary()->dictCensusUpdate(fc->size(), + true /*split*/, + true /*birth*/); + rem_fc = NULL; + } + // Otherwise, return it to the small list below. + } + } + // + if (rem_fc != NULL) { + MutexLockerEx x(_indexedFreeListParLocks[rem], + Mutex::_no_safepoint_check_flag); + _bt.verify_not_unallocated((HeapWord*)rem_fc, rem_fc->size()); + _indexedFreeList[rem].returnChunkAtHead(rem_fc); + smallSplitBirth(rem); + } + + // Now do the splitting up. + // Must do this in reverse order, so that anybody attempting to + // access the main chunk sees it as a single free block until we + // change it. + size_t fc_size = n * word_sz; + // All but first chunk in this loop + for (ssize_t i = n-1; i > 0; i--) { + FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz); + ffc->setSize(word_sz); + ffc->linkNext(NULL); + ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. + // Above must occur before BOT is updated below. + // splitting from the right, fc_size == (n - i + 1) * wordsize + _bt.mark_block((HeapWord*)ffc, word_sz); + fc_size -= word_sz; + _bt.verify_not_unallocated((HeapWord*)ffc, ffc->size()); + _bt.verify_single_block((HeapWord*)ffc, ffc->size()); + _bt.verify_single_block((HeapWord*)fc, fc_size); + // Push this on "fl". + fl->returnChunkAtHead(ffc); + } + // First chunk + fc->setSize(word_sz); + fc->linkNext(NULL); + fc->linkPrev(NULL); + _bt.verify_not_unallocated((HeapWord*)fc, fc->size()); + _bt.verify_single_block((HeapWord*)fc, fc->size()); + fl->returnChunkAtHead(fc); + + { + MutexLockerEx x(_indexedFreeListParLocks[word_sz], + Mutex::_no_safepoint_check_flag); + ssize_t new_births = _indexedFreeList[word_sz].splitBirths() + n; + _indexedFreeList[word_sz].set_splitBirths(new_births); + ssize_t new_surplus = _indexedFreeList[word_sz].surplus() + n; + _indexedFreeList[word_sz].set_surplus(new_surplus); + } + + // TRAP + assert(fl->tail()->next() == NULL, "List invariant."); +} + +// Set up the space's par_seq_tasks structure for work claiming +// for parallel rescan. See CMSParRemarkTask where this is currently used. +// XXX Need to suitably abstract and generalize this and the next +// method into one. +void +CompactibleFreeListSpace:: +initialize_sequential_subtasks_for_rescan(int n_threads) { + // The "size" of each task is fixed according to rescan_task_size. + assert(n_threads > 0, "Unexpected n_threads argument"); + const size_t task_size = rescan_task_size(); + size_t n_tasks = (used_region().word_size() + task_size - 1)/task_size; + assert((used_region().start() + (n_tasks - 1)*task_size < + used_region().end()) && + (used_region().start() + n_tasks*task_size >= + used_region().end()), "n_task calculation incorrect"); + SequentialSubTasksDone* pst = conc_par_seq_tasks(); + assert(!pst->valid(), "Clobbering existing data?"); + pst->set_par_threads(n_threads); + pst->set_n_tasks((int)n_tasks); +} + +// Set up the space's par_seq_tasks structure for work claiming +// for parallel concurrent marking. See CMSConcMarkTask where this is currently used. +void +CompactibleFreeListSpace:: +initialize_sequential_subtasks_for_marking(int n_threads, + HeapWord* low) { + // The "size" of each task is fixed according to rescan_task_size. + assert(n_threads > 0, "Unexpected n_threads argument"); + const size_t task_size = marking_task_size(); + assert(task_size > CardTableModRefBS::card_size_in_words && + (task_size % CardTableModRefBS::card_size_in_words == 0), + "Otherwise arithmetic below would be incorrect"); + MemRegion span = _gen->reserved(); + if (low != NULL) { + if (span.contains(low)) { + // Align low down to a card boundary so that + // we can use block_offset_careful() on span boundaries. + HeapWord* aligned_low = (HeapWord*)align_size_down((uintptr_t)low, + CardTableModRefBS::card_size); + // Clip span prefix at aligned_low + span = span.intersection(MemRegion(aligned_low, span.end())); + } else if (low > span.end()) { + span = MemRegion(low, low); // Null region + } // else use entire span + } + assert(span.is_empty() || + ((uintptr_t)span.start() % CardTableModRefBS::card_size == 0), + "span should start at a card boundary"); + size_t n_tasks = (span.word_size() + task_size - 1)/task_size; + assert((n_tasks == 0) == span.is_empty(), "Inconsistency"); + assert(n_tasks == 0 || + ((span.start() + (n_tasks - 1)*task_size < span.end()) && + (span.start() + n_tasks*task_size >= span.end())), + "n_task calculation incorrect"); + SequentialSubTasksDone* pst = conc_par_seq_tasks(); + assert(!pst->valid(), "Clobbering existing data?"); + pst->set_par_threads(n_threads); + pst->set_n_tasks((int)n_tasks); +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp new file mode 100644 index 00000000000..ef5d12a6cac --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp @@ -0,0 +1,748 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Classes in support of keeping track of promotions into a non-Contiguous +// space, in this case a CompactibleFreeListSpace. + +#define CFLS_LAB_REFILL_STATS 0 + +// Forward declarations +class CompactibleFreeListSpace; +class BlkClosure; +class BlkClosureCareful; +class UpwardsObjectClosure; +class ObjectClosureCareful; +class Klass; + +class PromotedObject VALUE_OBJ_CLASS_SPEC { + private: + enum { + promoted_mask = right_n_bits(2), // i.e. 0x3 + displaced_mark = nth_bit(2), // i.e. 0x4 + next_mask = ~(right_n_bits(3)) // i.e. ~(0x7) + }; + intptr_t _next; + public: + inline PromotedObject* next() const { + return (PromotedObject*)(_next & next_mask); + } + inline void setNext(PromotedObject* x) { + assert(((intptr_t)x & ~next_mask) == 0, + "Conflict in bit usage, " + " or insufficient alignment of objects"); + _next |= (intptr_t)x; + } + inline void setPromotedMark() { + _next |= promoted_mask; + } + inline bool hasPromotedMark() const { + return (_next & promoted_mask) == promoted_mask; + } + inline void setDisplacedMark() { + _next |= displaced_mark; + } + inline bool hasDisplacedMark() const { + return (_next & displaced_mark) != 0; + } + inline void clearNext() { _next = 0; } + debug_only(void *next_addr() { return (void *) &_next; }) +}; + +class SpoolBlock: public FreeChunk { + friend class PromotionInfo; + protected: + SpoolBlock* nextSpoolBlock; + size_t bufferSize; // number of usable words in this block + markOop* displacedHdr; // the displaced headers start here + + // Note about bufferSize: it denotes the number of entries available plus 1; + // legal indices range from 1 through BufferSize - 1. See the verification + // code verify() that counts the number of displaced headers spooled. + size_t computeBufferSize() { + return (size() * sizeof(HeapWord) - sizeof(*this)) / sizeof(markOop); + } + + public: + void init() { + bufferSize = computeBufferSize(); + displacedHdr = (markOop*)&displacedHdr; + nextSpoolBlock = NULL; + } +}; + +class PromotionInfo VALUE_OBJ_CLASS_SPEC { + bool _tracking; // set if tracking + CompactibleFreeListSpace* _space; // the space to which this belongs + PromotedObject* _promoHead; // head of list of promoted objects + PromotedObject* _promoTail; // tail of list of promoted objects + SpoolBlock* _spoolHead; // first spooling block + SpoolBlock* _spoolTail; // last non-full spooling block or null + SpoolBlock* _splice_point; // when _spoolTail is null, holds list tail + SpoolBlock* _spareSpool; // free spool buffer + size_t _firstIndex; // first active index in + // first spooling block (_spoolHead) + size_t _nextIndex; // last active index + 1 in last + // spooling block (_spoolTail) + private: + // ensure that spooling space exists; return true if there is spooling space + bool ensure_spooling_space_work(); + + public: + PromotionInfo() : + _tracking(0), _space(NULL), + _promoHead(NULL), _promoTail(NULL), + _spoolHead(NULL), _spoolTail(NULL), + _spareSpool(NULL), _firstIndex(1), + _nextIndex(1) {} + + bool noPromotions() const { + assert(_promoHead != NULL || _promoTail == NULL, "list inconsistency"); + return _promoHead == NULL; + } + void startTrackingPromotions(); + void stopTrackingPromotions(); + bool tracking() const { return _tracking; } + void track(PromotedObject* trackOop); // keep track of a promoted oop + // The following variant must be used when trackOop is not fully + // initialized and has a NULL klass: + void track(PromotedObject* trackOop, klassOop klassOfOop); // keep track of a promoted oop + void setSpace(CompactibleFreeListSpace* sp) { _space = sp; } + CompactibleFreeListSpace* space() const { return _space; } + markOop nextDisplacedHeader(); // get next header & forward spool pointer + void saveDisplacedHeader(markOop hdr); + // save header and forward spool + + inline size_t refillSize() const; + + SpoolBlock* getSpoolBlock(); // return a free spooling block + inline bool has_spooling_space() { + return _spoolTail != NULL && _spoolTail->bufferSize > _nextIndex; + } + // ensure that spooling space exists + bool ensure_spooling_space() { + return has_spooling_space() || ensure_spooling_space_work(); + } + #define PROMOTED_OOPS_ITERATE_DECL(OopClosureType, nv_suffix) \ + void promoted_oops_iterate##nv_suffix(OopClosureType* cl); + ALL_SINCE_SAVE_MARKS_CLOSURES(PROMOTED_OOPS_ITERATE_DECL) + #undef PROMOTED_OOPS_ITERATE_DECL + void promoted_oops_iterate(OopsInGenClosure* cl) { + promoted_oops_iterate_v(cl); + } + void verify() const; + void reset() { + _promoHead = NULL; + _promoTail = NULL; + _spoolHead = NULL; + _spoolTail = NULL; + _spareSpool = NULL; + _firstIndex = 0; + _nextIndex = 0; + + } +}; + +class LinearAllocBlock VALUE_OBJ_CLASS_SPEC { + public: + LinearAllocBlock() : _ptr(0), _word_size(0), _refillSize(0), + _allocation_size_limit(0) {} + void set(HeapWord* ptr, size_t word_size, size_t refill_size, + size_t allocation_size_limit) { + _ptr = ptr; + _word_size = word_size; + _refillSize = refill_size; + _allocation_size_limit = allocation_size_limit; + } + HeapWord* _ptr; + size_t _word_size; + size_t _refillSize; + size_t _allocation_size_limit; // largest size that will be allocated +}; + +// Concrete subclass of CompactibleSpace that implements +// a free list space, such as used in the concurrent mark sweep +// generation. + +class CompactibleFreeListSpace: public CompactibleSpace { + friend class VMStructs; + friend class ConcurrentMarkSweepGeneration; + friend class ASConcurrentMarkSweepGeneration; + friend class CMSCollector; + friend class CMSPermGenGen; + // Local alloc buffer for promotion into this space. + friend class CFLS_LAB; + + // "Size" of chunks of work (executed during parallel remark phases + // of CMS collection); this probably belongs in CMSCollector, although + // it's cached here because it's used in + // initialize_sequential_subtasks_for_rescan() which modifies + // par_seq_tasks which also lives in Space. XXX + const size_t _rescan_task_size; + const size_t _marking_task_size; + + // Yet another sequential tasks done structure. This supports + // CMS GC, where we have threads dynamically + // claiming sub-tasks from a larger parallel task. + SequentialSubTasksDone _conc_par_seq_tasks; + + BlockOffsetArrayNonContigSpace _bt; + + CMSCollector* _collector; + ConcurrentMarkSweepGeneration* _gen; + + // Data structures for free blocks (used during allocation/sweeping) + + // Allocation is done linearly from two different blocks depending on + // whether the request is small or large, in an effort to reduce + // fragmentation. We assume that any locking for allocation is done + // by the containing generation. Thus, none of the methods in this + // space are re-entrant. + enum SomeConstants { + SmallForLinearAlloc = 16, // size < this then use _sLAB + SmallForDictionary = 257, // size < this then use _indexedFreeList + IndexSetSize = SmallForDictionary, // keep this odd-sized + IndexSetStart = MinObjAlignment, + IndexSetStride = MinObjAlignment + }; + + private: + enum FitStrategyOptions { + FreeBlockStrategyNone = 0, + FreeBlockBestFitFirst + }; + + PromotionInfo _promoInfo; + + // helps to impose a global total order on freelistLock ranks; + // assumes that CFLSpace's are allocated in global total order + static int _lockRank; + + // a lock protecting the free lists and free blocks; + // mutable because of ubiquity of locking even for otherwise const methods + mutable Mutex _freelistLock; + // locking verifier convenience function + void assert_locked() const PRODUCT_RETURN; + + // Linear allocation blocks + LinearAllocBlock _smallLinearAllocBlock; + + FreeBlockDictionary::DictionaryChoice _dictionaryChoice; + FreeBlockDictionary* _dictionary; // ptr to dictionary for large size blocks + + FreeList _indexedFreeList[IndexSetSize]; + // indexed array for small size blocks + // allocation stategy + bool _fitStrategy; // Use best fit strategy. + bool _adaptive_freelists; // Use adaptive freelists + + // This is an address close to the largest free chunk in the heap. + // It is currently assumed to be at the end of the heap. Free + // chunks with addresses greater than nearLargestChunk are coalesced + // in an effort to maintain a large chunk at the end of the heap. + HeapWord* _nearLargestChunk; + + // Used to keep track of limit of sweep for the space + HeapWord* _sweep_limit; + + // Support for compacting cms + HeapWord* cross_threshold(HeapWord* start, HeapWord* end); + HeapWord* forward(oop q, size_t size, CompactPoint* cp, HeapWord* compact_top); + + // Initialization helpers. + void initializeIndexedFreeListArray(); + + // Extra stuff to manage promotion parallelism. + + // a lock protecting the dictionary during par promotion allocation. + mutable Mutex _parDictionaryAllocLock; + Mutex* parDictionaryAllocLock() const { return &_parDictionaryAllocLock; } + + // Locks protecting the exact lists during par promotion allocation. + Mutex* _indexedFreeListParLocks[IndexSetSize]; + +#if CFLS_LAB_REFILL_STATS + // Some statistics. + jint _par_get_chunk_from_small; + jint _par_get_chunk_from_large; +#endif + + + // Attempt to obtain up to "n" blocks of the size "word_sz" (which is + // required to be smaller than "IndexSetSize".) If successful, + // adds them to "fl", which is required to be an empty free list. + // If the count of "fl" is negative, it's absolute value indicates a + // number of free chunks that had been previously "borrowed" from global + // list of size "word_sz", and must now be decremented. + void par_get_chunk_of_blocks(size_t word_sz, size_t n, FreeList* fl); + + // Allocation helper functions + // Allocate using a strategy that takes from the indexed free lists + // first. This allocation strategy assumes a companion sweeping + // strategy that attempts to keep the needed number of chunks in each + // indexed free lists. + HeapWord* allocate_adaptive_freelists(size_t size); + // Allocate from the linear allocation buffers first. This allocation + // strategy assumes maximal coalescing can maintain chunks large enough + // to be used as linear allocation buffers. + HeapWord* allocate_non_adaptive_freelists(size_t size); + + // Gets a chunk from the linear allocation block (LinAB). If there + // is not enough space in the LinAB, refills it. + HeapWord* getChunkFromLinearAllocBlock(LinearAllocBlock* blk, size_t size); + HeapWord* getChunkFromSmallLinearAllocBlock(size_t size); + // Get a chunk from the space remaining in the linear allocation block. Do + // not attempt to refill if the space is not available, return NULL. Do the + // repairs on the linear allocation block as appropriate. + HeapWord* getChunkFromLinearAllocBlockRemainder(LinearAllocBlock* blk, size_t size); + inline HeapWord* getChunkFromSmallLinearAllocBlockRemainder(size_t size); + + // Helper function for getChunkFromIndexedFreeList. + // Replenish the indexed free list for this "size". Do not take from an + // underpopulated size. + FreeChunk* getChunkFromIndexedFreeListHelper(size_t size); + + // Get a chunk from the indexed free list. If the indexed free list + // does not have a free chunk, try to replenish the indexed free list + // then get the free chunk from the replenished indexed free list. + inline FreeChunk* getChunkFromIndexedFreeList(size_t size); + + // The returned chunk may be larger than requested (or null). + FreeChunk* getChunkFromDictionary(size_t size); + // The returned chunk is the exact size requested (or null). + FreeChunk* getChunkFromDictionaryExact(size_t size); + + // Find a chunk in the indexed free list that is the best + // fit for size "numWords". + FreeChunk* bestFitSmall(size_t numWords); + // For free list "fl" of chunks of size > numWords, + // remove a chunk, split off a chunk of size numWords + // and return it. The split off remainder is returned to + // the free lists. The old name for getFromListGreater + // was lookInListGreater. + FreeChunk* getFromListGreater(FreeList* fl, size_t numWords); + // Get a chunk in the indexed free list or dictionary, + // by considering a larger chunk and splitting it. + FreeChunk* getChunkFromGreater(size_t numWords); + // Verify that the given chunk is in the indexed free lists. + bool verifyChunkInIndexedFreeLists(FreeChunk* fc) const; + // Remove the specified chunk from the indexed free lists. + void removeChunkFromIndexedFreeList(FreeChunk* fc); + // Remove the specified chunk from the dictionary. + void removeChunkFromDictionary(FreeChunk* fc); + // Split a free chunk into a smaller free chunk of size "new_size". + // Return the smaller free chunk and return the remainder to the + // free lists. + FreeChunk* splitChunkAndReturnRemainder(FreeChunk* chunk, size_t new_size); + // Add a chunk to the free lists. + void addChunkToFreeLists(HeapWord* chunk, size_t size); + // Add a chunk to the free lists, preferring to suffix it + // to the last free chunk at end of space if possible, and + // updating the block census stats as well as block offset table. + // Take any locks as appropriate if we are multithreaded. + void addChunkToFreeListsAtEndRecordingStats(HeapWord* chunk, size_t size); + // Add a free chunk to the indexed free lists. + void returnChunkToFreeList(FreeChunk* chunk); + // Add a free chunk to the dictionary. + void returnChunkToDictionary(FreeChunk* chunk); + + // Functions for maintaining the linear allocation buffers (LinAB). + // Repairing a linear allocation block refers to operations + // performed on the remainder of a LinAB after an allocation + // has been made from it. + void repairLinearAllocationBlocks(); + void repairLinearAllocBlock(LinearAllocBlock* blk); + void refillLinearAllocBlock(LinearAllocBlock* blk); + void refillLinearAllocBlockIfNeeded(LinearAllocBlock* blk); + void refillLinearAllocBlocksIfNeeded(); + + void verify_objects_initialized() const; + + // Statistics reporting helper functions + void reportFreeListStatistics() const; + void reportIndexedFreeListStatistics() const; + size_t maxChunkSizeInIndexedFreeLists() const; + size_t numFreeBlocksInIndexedFreeLists() const; + // Accessor + HeapWord* unallocated_block() const { + HeapWord* ub = _bt.unallocated_block(); + assert(ub >= bottom() && + ub <= end(), "space invariant"); + return ub; + } + void freed(HeapWord* start, size_t size) { + _bt.freed(start, size); + } + + protected: + // reset the indexed free list to its initial empty condition. + void resetIndexedFreeListArray(); + // reset to an initial state with a single free block described + // by the MemRegion parameter. + void reset(MemRegion mr); + // Return the total number of words in the indexed free lists. + size_t totalSizeInIndexedFreeLists() const; + + public: + // Constructor... + CompactibleFreeListSpace(BlockOffsetSharedArray* bs, MemRegion mr, + bool use_adaptive_freelists, + FreeBlockDictionary::DictionaryChoice); + // accessors + bool bestFitFirst() { return _fitStrategy == FreeBlockBestFitFirst; } + FreeBlockDictionary* dictionary() const { return _dictionary; } + HeapWord* nearLargestChunk() const { return _nearLargestChunk; } + void set_nearLargestChunk(HeapWord* v) { _nearLargestChunk = v; } + + // Return the free chunk at the end of the space. If no such + // chunk exists, return NULL. + FreeChunk* find_chunk_at_end(); + + bool adaptive_freelists() { return _adaptive_freelists; } + + void set_collector(CMSCollector* collector) { _collector = collector; } + + // Support for parallelization of rescan and marking + const size_t rescan_task_size() const { return _rescan_task_size; } + const size_t marking_task_size() const { return _marking_task_size; } + SequentialSubTasksDone* conc_par_seq_tasks() {return &_conc_par_seq_tasks; } + void initialize_sequential_subtasks_for_rescan(int n_threads); + void initialize_sequential_subtasks_for_marking(int n_threads, + HeapWord* low = NULL); + +#if CFLS_LAB_REFILL_STATS + void print_par_alloc_stats(); +#endif + + // Space enquiries + size_t used() const; + size_t free() const; + size_t max_alloc_in_words() const; + // XXX: should have a less conservative used_region() than that of + // Space; we could consider keeping track of highest allocated + // address and correcting that at each sweep, as the sweeper + // goes through the entire allocated part of the generation. We + // could also use that information to keep the sweeper from + // sweeping more than is necessary. The allocator and sweeper will + // of course need to synchronize on this, since the sweeper will + // try to bump down the address and the allocator will try to bump it up. + // For now, however, we'll just use the default used_region() + // which overestimates the region by returning the entire + // committed region (this is safe, but inefficient). + + // Returns a subregion of the space containing all the objects in + // the space. + MemRegion used_region() const { + return MemRegion(bottom(), + BlockOffsetArrayUseUnallocatedBlock ? + unallocated_block() : end()); + } + + // This is needed because the default implementation uses block_start() + // which can;t be used at certain times (for example phase 3 of mark-sweep). + // A better fix is to change the assertions in phase 3 of mark-sweep to + // use is_in_reserved(), but that is deferred since the is_in() assertions + // are buried through several layers of callers and are used elsewhere + // as well. + bool is_in(const void* p) const { + return used_region().contains(p); + } + + virtual bool is_free_block(const HeapWord* p) const; + + // Resizing support + void set_end(HeapWord* value); // override + + // mutual exclusion support + Mutex* freelistLock() const { return &_freelistLock; } + + // Iteration support + void oop_iterate(MemRegion mr, OopClosure* cl); + void oop_iterate(OopClosure* cl); + + void object_iterate(ObjectClosure* blk); + void object_iterate_mem(MemRegion mr, UpwardsObjectClosure* cl); + + // Requires that "mr" be entirely within the space. + // Apply "cl->do_object" to all objects that intersect with "mr". + // If the iteration encounters an unparseable portion of the region, + // terminate the iteration and return the address of the start of the + // subregion that isn't done. Return of "NULL" indicates that the + // interation completed. + virtual HeapWord* + object_iterate_careful_m(MemRegion mr, + ObjectClosureCareful* cl); + virtual HeapWord* + object_iterate_careful(ObjectClosureCareful* cl); + + // Override: provides a DCTO_CL specific to this kind of space. + DirtyCardToOopClosure* new_dcto_cl(OopClosure* cl, + CardTableModRefBS::PrecisionStyle precision, + HeapWord* boundary); + + void blk_iterate(BlkClosure* cl); + void blk_iterate_careful(BlkClosureCareful* cl); + HeapWord* block_start(const void* p) const; + HeapWord* block_start_careful(const void* p) const; + size_t block_size(const HeapWord* p) const; + size_t block_size_no_stall(HeapWord* p, const CMSCollector* c) const; + bool block_is_obj(const HeapWord* p) const; + bool obj_is_alive(const HeapWord* p) const; + size_t block_size_nopar(const HeapWord* p) const; + bool block_is_obj_nopar(const HeapWord* p) const; + + // iteration support for promotion + void save_marks(); + bool no_allocs_since_save_marks(); + void object_iterate_since_last_GC(ObjectClosure* cl); + + // iteration support for sweeping + void save_sweep_limit() { + _sweep_limit = BlockOffsetArrayUseUnallocatedBlock ? + unallocated_block() : end(); + } + NOT_PRODUCT( + void clear_sweep_limit() { _sweep_limit = NULL; } + ) + HeapWord* sweep_limit() { return _sweep_limit; } + + // Apply "blk->do_oop" to the addresses of all reference fields in objects + // promoted into this generation since the most recent save_marks() call. + // Fields in objects allocated by applications of the closure + // *are* included in the iteration. Thus, when the iteration completes + // there should be no further such objects remaining. + #define CFLS_OOP_SINCE_SAVE_MARKS_DECL(OopClosureType, nv_suffix) \ + void oop_since_save_marks_iterate##nv_suffix(OopClosureType* blk); + ALL_SINCE_SAVE_MARKS_CLOSURES(CFLS_OOP_SINCE_SAVE_MARKS_DECL) + #undef CFLS_OOP_SINCE_SAVE_MARKS_DECL + + // Allocation support + HeapWord* allocate(size_t size); + HeapWord* par_allocate(size_t size); + + oop promote(oop obj, size_t obj_size, oop* ref); + void gc_prologue(); + void gc_epilogue(); + + // This call is used by a containing CMS generation / collector + // to inform the CFLS space that a sweep has been completed + // and that the space can do any related house-keeping functions. + void sweep_completed(); + + // For an object in this space, the mark-word's two + // LSB's having the value [11] indicates that it has been + // promoted since the most recent call to save_marks() on + // this generation and has not subsequently been iterated + // over (using oop_since_save_marks_iterate() above). + bool obj_allocated_since_save_marks(const oop obj) const { + assert(is_in_reserved(obj), "Wrong space?"); + return ((PromotedObject*)obj)->hasPromotedMark(); + } + + // A worst-case estimate of the space required (in HeapWords) to expand the + // heap when promoting an obj of size obj_size. + size_t expansionSpaceRequired(size_t obj_size) const; + + FreeChunk* allocateScratch(size_t size); + + // returns true if either the small or large linear allocation buffer is empty. + bool linearAllocationWouldFail(); + + // Adjust the chunk for the minimum size. This version is called in + // most cases in CompactibleFreeListSpace methods. + inline static size_t adjustObjectSize(size_t size) { + return (size_t) align_object_size(MAX2(size, (size_t)MinChunkSize)); + } + // This is a virtual version of adjustObjectSize() that is called + // only occasionally when the compaction space changes and the type + // of the new compaction space is is only known to be CompactibleSpace. + size_t adjust_object_size_v(size_t size) const { + return adjustObjectSize(size); + } + // Minimum size of a free block. + virtual size_t minimum_free_block_size() const { return MinChunkSize; } + void removeFreeChunkFromFreeLists(FreeChunk* chunk); + void addChunkAndRepairOffsetTable(HeapWord* chunk, size_t size, + bool coalesced); + + // Support for compaction + void prepare_for_compaction(CompactPoint* cp); + void adjust_pointers(); + void compact(); + // reset the space to reflect the fact that a compaction of the + // space has been done. + virtual void reset_after_compaction(); + + // Debugging support + void print() const; + void prepare_for_verify(); + void verify(bool allow_dirty) const; + void verifyFreeLists() const PRODUCT_RETURN; + void verifyIndexedFreeLists() const; + void verifyIndexedFreeList(size_t size) const; + // verify that the given chunk is in the free lists. + bool verifyChunkInFreeLists(FreeChunk* fc) const; + // Do some basic checks on the the free lists. + void checkFreeListConsistency() const PRODUCT_RETURN; + + NOT_PRODUCT ( + void initializeIndexedFreeListArrayReturnedBytes(); + size_t sumIndexedFreeListArrayReturnedBytes(); + // Return the total number of chunks in the indexed free lists. + size_t totalCountInIndexedFreeLists() const; + // Return the total numberof chunks in the space. + size_t totalCount(); + ) + + // The census consists of counts of the quantities such as + // the current count of the free chunks, number of chunks + // created as a result of the split of a larger chunk or + // coalescing of smaller chucks, etc. The counts in the + // census is used to make decisions on splitting and + // coalescing of chunks during the sweep of garbage. + + // Print the statistics for the free lists. + void printFLCensus(int sweepCt) const; + + // Statistics functions + // Initialize census for lists before the sweep. + void beginSweepFLCensus(float sweep_current, + float sweep_estimate); + // Set the surplus for each of the free lists. + void setFLSurplus(); + // Set the hint for each of the free lists. + void setFLHints(); + // Clear the census for each of the free lists. + void clearFLCensus(); + // Perform functions for the census after the end of the sweep. + void endSweepFLCensus(int sweepCt); + // Return true if the count of free chunks is greater + // than the desired number of free chunks. + bool coalOverPopulated(size_t size); + + +// Record (for each size): +// +// split-births = #chunks added due to splits in (prev-sweep-end, +// this-sweep-start) +// split-deaths = #chunks removed for splits in (prev-sweep-end, +// this-sweep-start) +// num-curr = #chunks at start of this sweep +// num-prev = #chunks at end of previous sweep +// +// The above are quantities that are measured. Now define: +// +// num-desired := num-prev + split-births - split-deaths - num-curr +// +// Roughly, num-prev + split-births is the supply, +// split-deaths is demand due to other sizes +// and num-curr is what we have left. +// +// Thus, num-desired is roughly speaking the "legitimate demand" +// for blocks of this size and what we are striving to reach at the +// end of the current sweep. +// +// For a given list, let num-len be its current population. +// Define, for a free list of a given size: +// +// coal-overpopulated := num-len >= num-desired * coal-surplus +// (coal-surplus is set to 1.05, i.e. we allow a little slop when +// coalescing -- we do not coalesce unless we think that the current +// supply has exceeded the estimated demand by more than 5%). +// +// For the set of sizes in the binary tree, which is neither dense nor +// closed, it may be the case that for a particular size we have never +// had, or do not now have, or did not have at the previous sweep, +// chunks of that size. We need to extend the definition of +// coal-overpopulated to such sizes as well: +// +// For a chunk in/not in the binary tree, extend coal-overpopulated +// defined above to include all sizes as follows: +// +// . a size that is non-existent is coal-overpopulated +// . a size that has a num-desired <= 0 as defined above is +// coal-overpopulated. +// +// Also define, for a chunk heap-offset C and mountain heap-offset M: +// +// close-to-mountain := C >= 0.99 * M +// +// Now, the coalescing strategy is: +// +// Coalesce left-hand chunk with right-hand chunk if and +// only if: +// +// EITHER +// . left-hand chunk is of a size that is coal-overpopulated +// OR +// . right-hand chunk is close-to-mountain + void smallCoalBirth(size_t size); + void smallCoalDeath(size_t size); + void coalBirth(size_t size); + void coalDeath(size_t size); + void smallSplitBirth(size_t size); + void smallSplitDeath(size_t size); + void splitBirth(size_t size); + void splitDeath(size_t size); + void split(size_t from, size_t to1); + + double flsFrag() const; +}; + +// A parallel-GC-thread-local allocation buffer for allocation into a +// CompactibleFreeListSpace. +class CFLS_LAB : public CHeapObj { + // The space that this buffer allocates into. + CompactibleFreeListSpace* _cfls; + + // Our local free lists. + FreeList _indexedFreeList[CompactibleFreeListSpace::IndexSetSize]; + + // Initialized from a command-line arg. + size_t _blocks_to_claim; + +#if CFLS_LAB_REFILL_STATS + // Some statistics. + int _refills; + int _blocksTaken; + static int _tot_refills; + static int _tot_blocksTaken; + static int _next_threshold; +#endif + +public: + CFLS_LAB(CompactibleFreeListSpace* cfls); + + // Allocate and return a block of the given size, or else return NULL. + HeapWord* alloc(size_t word_sz); + + // Return any unused portions of the buffer to the global pool. + void retire(); +}; + +size_t PromotionInfo::refillSize() const { + const size_t CMSSpoolBlockSize = 256; + const size_t sz = heap_word_size(sizeof(SpoolBlock) + sizeof(markOop) + * CMSSpoolBlockSize); + return CompactibleFreeListSpace::adjustObjectSize(sz); +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentGCThread.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentGCThread.cpp new file mode 100644 index 00000000000..8ed8b809e2a --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentGCThread.cpp @@ -0,0 +1,314 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// CopyrightVersion 1.2 + +# include "incls/_precompiled.incl" +# include "incls/_concurrentGCThread.cpp.incl" + +bool ConcurrentGCThread::_should_terminate = false; +bool ConcurrentGCThread::_has_terminated = false; +int ConcurrentGCThread::_CGC_flag = CGC_nil; + +SuspendibleThreadSet ConcurrentGCThread::_sts; + +ConcurrentGCThread::ConcurrentGCThread() { + _sts.initialize(); +}; + +void ConcurrentGCThread::stopWorldAndDo(VoidClosure* op) { + MutexLockerEx x(Heap_lock, + Mutex::_no_safepoint_check_flag); + // warning("CGC: about to try stopping world"); + SafepointSynchronize::begin(); + // warning("CGC: successfully stopped world"); + op->do_void(); + SafepointSynchronize::end(); + // warning("CGC: successfully restarted world"); +} + +void ConcurrentGCThread::safepoint_synchronize() { + _sts.suspend_all(); +} + +void ConcurrentGCThread::safepoint_desynchronize() { + _sts.resume_all(); +} + +void ConcurrentGCThread::create_and_start() { + if (os::create_thread(this, os::cgc_thread)) { + // XXX: need to set this to low priority + // unless "agressive mode" set; priority + // should be just less than that of VMThread. + os::set_priority(this, NearMaxPriority); + if (!_should_terminate && !DisableStartThread) { + os::start_thread(this); + } + } +} + +void ConcurrentGCThread::initialize_in_thread() { + this->record_stack_base_and_size(); + this->initialize_thread_local_storage(); + this->set_active_handles(JNIHandleBlock::allocate_block()); + // From this time Thread::current() should be working. + assert(this == Thread::current(), "just checking"); +} + +void ConcurrentGCThread::wait_for_universe_init() { + MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag); + while (!is_init_completed() && !_should_terminate) { + CGC_lock->wait(Mutex::_no_safepoint_check_flag, 200); + } +} + +void ConcurrentGCThread::terminate() { + // Signal that it is terminated + { + MutexLockerEx mu(Terminator_lock, + Mutex::_no_safepoint_check_flag); + _has_terminated = true; + Terminator_lock->notify(); + } + + // Thread destructor usually does this.. + ThreadLocalStorage::set_thread(NULL); +} + + +void SuspendibleThreadSet::initialize_work() { + MutexLocker x(STS_init_lock); + if (!_initialized) { + _m = new Monitor(Mutex::leaf, + "SuspendibleThreadSetLock", true); + _async = 0; + _async_stop = false; + _async_stopped = 0; + _initialized = true; + } +} + +void SuspendibleThreadSet::join() { + initialize(); + MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); + while (_async_stop) _m->wait(Mutex::_no_safepoint_check_flag); + _async++; + assert(_async > 0, "Huh."); +} + +void SuspendibleThreadSet::leave() { + assert(_initialized, "Must be initialized."); + MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); + _async--; + assert(_async >= 0, "Huh."); + if (_async_stop) _m->notify_all(); +} + +void SuspendibleThreadSet::yield(const char* id) { + assert(_initialized, "Must be initialized."); + if (_async_stop) { + MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); + if (_async_stop) { + _async_stopped++; + assert(_async_stopped > 0, "Huh."); + if (_async_stopped == _async) { + if (ConcGCYieldTimeout > 0) { + double now = os::elapsedTime(); + guarantee((now - _suspend_all_start) * 1000.0 < + (double)ConcGCYieldTimeout, + "Long delay; whodunit?"); + } + } + _m->notify_all(); + while (_async_stop) _m->wait(Mutex::_no_safepoint_check_flag); + _async_stopped--; + assert(_async >= 0, "Huh"); + _m->notify_all(); + } + } +} + +void SuspendibleThreadSet::suspend_all() { + initialize(); // If necessary. + if (ConcGCYieldTimeout > 0) { + _suspend_all_start = os::elapsedTime(); + } + MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); + assert(!_async_stop, "Only one at a time."); + _async_stop = true; + while (_async_stopped < _async) _m->wait(Mutex::_no_safepoint_check_flag); +} + +void SuspendibleThreadSet::resume_all() { + assert(_initialized, "Must be initialized."); + MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); + assert(_async_stopped == _async, "Huh."); + _async_stop = false; + _m->notify_all(); +} + +static void _sltLoop(JavaThread* thread, TRAPS) { + SurrogateLockerThread* slt = (SurrogateLockerThread*)thread; + slt->loop(); +} + +SurrogateLockerThread::SurrogateLockerThread() : + JavaThread(&_sltLoop), + _monitor(Mutex::nonleaf, "SLTMonitor"), + _buffer(empty) +{} + +SurrogateLockerThread* SurrogateLockerThread::make(TRAPS) { + klassOop k = + SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_Thread(), + true, CHECK_NULL); + instanceKlassHandle klass (THREAD, k); + instanceHandle thread_oop = klass->allocate_instance_handle(CHECK_NULL); + + const char thread_name[] = "Surrogate Locker Thread (CMS)"; + Handle string = java_lang_String::create_from_str(thread_name, CHECK_NULL); + + // Initialize thread_oop to put it into the system threadGroup + Handle thread_group (THREAD, Universe::system_thread_group()); + JavaValue result(T_VOID); + JavaCalls::call_special(&result, thread_oop, + klass, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::threadgroup_string_void_signature(), + thread_group, + string, + CHECK_NULL); + + SurrogateLockerThread* res; + { + MutexLocker mu(Threads_lock); + res = new SurrogateLockerThread(); + + // At this point it may be possible that no osthread was created for the + // JavaThread due to lack of memory. We would have to throw an exception + // in that case. However, since this must work and we do not allow + // exceptions anyway, check and abort if this fails. + if (res == NULL || res->osthread() == NULL) { + vm_exit_during_initialization("java.lang.OutOfMemoryError", + "unable to create new native thread"); + } + java_lang_Thread::set_thread(thread_oop(), res); + java_lang_Thread::set_priority(thread_oop(), NearMaxPriority); + java_lang_Thread::set_daemon(thread_oop()); + + res->set_threadObj(thread_oop()); + Threads::add(res); + Thread::start(res); + } + os::yield(); // This seems to help with initial start-up of SLT + return res; +} + +void SurrogateLockerThread::manipulatePLL(SLT_msg_type msg) { + MutexLockerEx x(&_monitor, Mutex::_no_safepoint_check_flag); + assert(_buffer == empty, "Should be empty"); + assert(msg != empty, "empty message"); + _buffer = msg; + while (_buffer != empty) { + _monitor.notify(); + _monitor.wait(Mutex::_no_safepoint_check_flag); + } +} + +// ======= Surrogate Locker Thread ============= + +void SurrogateLockerThread::loop() { + BasicLock pll_basic_lock; + SLT_msg_type msg; + debug_only(unsigned int owned = 0;) + + while (/* !isTerminated() */ 1) { + { + MutexLocker x(&_monitor); + // Since we are a JavaThread, we can't be here at a safepoint. + assert(!SafepointSynchronize::is_at_safepoint(), + "SLT is a JavaThread"); + // wait for msg buffer to become non-empty + while (_buffer == empty) { + _monitor.notify(); + _monitor.wait(); + } + msg = _buffer; + } + switch(msg) { + case acquirePLL: { + instanceRefKlass::acquire_pending_list_lock(&pll_basic_lock); + debug_only(owned++;) + break; + } + case releaseAndNotifyPLL: { + assert(owned > 0, "Don't have PLL"); + instanceRefKlass::release_and_notify_pending_list_lock(&pll_basic_lock); + debug_only(owned--;) + break; + } + case empty: + default: { + guarantee(false,"Unexpected message in _buffer"); + break; + } + } + { + MutexLocker x(&_monitor); + // Since we are a JavaThread, we can't be here at a safepoint. + assert(!SafepointSynchronize::is_at_safepoint(), + "SLT is a JavaThread"); + _buffer = empty; + _monitor.notify(); + } + } + assert(!_monitor.owned_by_self(), "Should unlock before exit."); +} + + +// ===== STS Access From Outside CGCT ===== + +void ConcurrentGCThread::stsYield(const char* id) { + assert( Thread::current()->is_ConcurrentGC_thread(), + "only a conc GC thread can call this" ); + _sts.yield(id); +} + +bool ConcurrentGCThread::stsShouldYield() { + assert( Thread::current()->is_ConcurrentGC_thread(), + "only a conc GC thread can call this" ); + return _sts.should_yield(); +} + +void ConcurrentGCThread::stsJoin() { + assert( Thread::current()->is_ConcurrentGC_thread(), + "only a conc GC thread can call this" ); + _sts.join(); +} + +void ConcurrentGCThread::stsLeave() { + assert( Thread::current()->is_ConcurrentGC_thread(), + "only a conc GC thread can call this" ); + _sts.leave(); +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentGCThread.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentGCThread.hpp new file mode 100644 index 00000000000..db6cc903ddf --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentGCThread.hpp @@ -0,0 +1,167 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class VoidClosure; + +// A SuspendibleThreadSet is (obviously) a set of threads that can be +// suspended. A thread can join and later leave the set, and periodically +// yield. If some thread (not in the set) requests, via suspend_all, that +// the threads be suspended, then the requesting thread is blocked until +// all the threads in the set have yielded or left the set. (Threads may +// not enter the set when an attempted suspension is in progress.) The +// suspending thread later calls resume_all, allowing the suspended threads +// to continue. + +class SuspendibleThreadSet { + Monitor* _m; + int _async; + bool _async_stop; + int _async_stopped; + bool _initialized; + double _suspend_all_start; + + void initialize_work(); + + public: + SuspendibleThreadSet() : _initialized(false) {} + + // Add the current thread to the set. May block if a suspension + // is in progress. + void join(); + // Removes the current thread from the set. + void leave(); + // Returns "true" iff an suspension is in progress. + bool should_yield() { return _async_stop; } + // Suspends the current thread if a suspension is in progress (for + // the duration of the suspension.) + void yield(const char* id); + // Return when all threads in the set are suspended. + void suspend_all(); + // Allow suspended threads to resume. + void resume_all(); + // Redundant initializations okay. + void initialize() { + // Double-check dirty read idiom. + if (!_initialized) initialize_work(); + } +}; + + +class ConcurrentGCThread: public NamedThread { + friend class VMStructs; + +protected: + static bool _should_terminate; + static bool _has_terminated; + + enum CGC_flag_type { + CGC_nil = 0x0, + CGC_dont_suspend = 0x1, + CGC_CGC_safepoint = 0x2, + CGC_VM_safepoint = 0x4 + }; + + static int _CGC_flag; + + static bool CGC_flag_is_set(int b) { return (_CGC_flag & b) != 0; } + static int set_CGC_flag(int b) { return _CGC_flag |= b; } + static int reset_CGC_flag(int b) { return _CGC_flag &= ~b; } + + void stopWorldAndDo(VoidClosure* op); + + // All instances share this one set. + static SuspendibleThreadSet _sts; + + // Create and start the thread (setting it's priority high.) + void create_and_start(); + + // Do initialization steps in the thread: record stack base and size, + // init thread local storage, set JNI handle block. + void initialize_in_thread(); + + // Wait until Universe::is_fully_initialized(); + void wait_for_universe_init(); + + // Record that the current thread is terminating, and will do more + // concurrent work. + void terminate(); + +public: + // Constructor + + ConcurrentGCThread(); + ~ConcurrentGCThread() {} // Exists to call NamedThread destructor. + + // Tester + bool is_ConcurrentGC_thread() const { return true; } + + static void safepoint_synchronize(); + static void safepoint_desynchronize(); + + // All overridings should probably do _sts::yield, but we allow + // overriding for distinguished debugging messages. Default is to do + // nothing. + virtual void yield() {} + + bool should_yield() { return _sts.should_yield(); } + + // they are prefixed by sts since there are already yield() and + // should_yield() (non-static) methods in this class and it was an + // easy way to differentiate them. + static void stsYield(const char* id); + static bool stsShouldYield(); + static void stsJoin(); + static void stsLeave(); + +}; + +// The SurrogateLockerThread is used by concurrent GC threads for +// manipulating Java monitors, in particular, currently for +// manipulating the pending_list_lock. XXX +class SurrogateLockerThread: public JavaThread { + friend class VMStructs; + public: + enum SLT_msg_type { + empty = 0, // no message + acquirePLL, // acquire pending list lock + releaseAndNotifyPLL // notify and release pending list lock + }; + private: + // the following are shared with the CMSThread + SLT_msg_type _buffer; // communication buffer + Monitor _monitor; // monitor controlling buffer + BasicLock _basicLock; // used for PLL locking + + public: + static SurrogateLockerThread* make(TRAPS); + + SurrogateLockerThread(); + + bool is_hidden_from_external_view() const { return true; } + + void loop(); // main method + + void manipulatePLL(SLT_msg_type msg); + +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp new file mode 100644 index 00000000000..f8355d945e6 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -0,0 +1,8787 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_concurrentMarkSweepGeneration.cpp.incl" + +// statics +CMSCollector* ConcurrentMarkSweepGeneration::_collector = NULL; +bool CMSCollector::_full_gc_requested = false; + +////////////////////////////////////////////////////////////////// +// In support of CMS/VM thread synchronization +////////////////////////////////////////////////////////////////// +// We split use of the CGC_lock into 2 "levels". +// The low-level locking is of the usual CGC_lock monitor. We introduce +// a higher level "token" (hereafter "CMS token") built on top of the +// low level monitor (hereafter "CGC lock"). +// The token-passing protocol gives priority to the VM thread. The +// CMS-lock doesn't provide any fairness guarantees, but clients +// should ensure that it is only held for very short, bounded +// durations. +// +// When either of the CMS thread or the VM thread is involved in +// collection operations during which it does not want the other +// thread to interfere, it obtains the CMS token. +// +// If either thread tries to get the token while the other has +// it, that thread waits. However, if the VM thread and CMS thread +// both want the token, then the VM thread gets priority while the +// CMS thread waits. This ensures, for instance, that the "concurrent" +// phases of the CMS thread's work do not block out the VM thread +// for long periods of time as the CMS thread continues to hog +// the token. (See bug 4616232). +// +// The baton-passing functions are, however, controlled by the +// flags _foregroundGCShouldWait and _foregroundGCIsActive, +// and here the low-level CMS lock, not the high level token, +// ensures mutual exclusion. +// +// Two important conditions that we have to satisfy: +// 1. if a thread does a low-level wait on the CMS lock, then it +// relinquishes the CMS token if it were holding that token +// when it acquired the low-level CMS lock. +// 2. any low-level notifications on the low-level lock +// should only be sent when a thread has relinquished the token. +// +// In the absence of either property, we'd have potential deadlock. +// +// We protect each of the CMS (concurrent and sequential) phases +// with the CMS _token_, not the CMS _lock_. +// +// The only code protected by CMS lock is the token acquisition code +// itself, see ConcurrentMarkSweepThread::[de]synchronize(), and the +// baton-passing code. +// +// Unfortunately, i couldn't come up with a good abstraction to factor and +// hide the naked CGC_lock manipulation in the baton-passing code +// further below. That's something we should try to do. Also, the proof +// of correctness of this 2-level locking scheme is far from obvious, +// and potentially quite slippery. We have an uneasy supsicion, for instance, +// that there may be a theoretical possibility of delay/starvation in the +// low-level lock/wait/notify scheme used for the baton-passing because of +// potential intereference with the priority scheme embodied in the +// CMS-token-passing protocol. See related comments at a CGC_lock->wait() +// invocation further below and marked with "XXX 20011219YSR". +// Indeed, as we note elsewhere, this may become yet more slippery +// in the presence of multiple CMS and/or multiple VM threads. XXX + +class CMSTokenSync: public StackObj { + private: + bool _is_cms_thread; + public: + CMSTokenSync(bool is_cms_thread): + _is_cms_thread(is_cms_thread) { + assert(is_cms_thread == Thread::current()->is_ConcurrentGC_thread(), + "Incorrect argument to constructor"); + ConcurrentMarkSweepThread::synchronize(_is_cms_thread); + } + + ~CMSTokenSync() { + assert(_is_cms_thread ? + ConcurrentMarkSweepThread::cms_thread_has_cms_token() : + ConcurrentMarkSweepThread::vm_thread_has_cms_token(), + "Incorrect state"); + ConcurrentMarkSweepThread::desynchronize(_is_cms_thread); + } +}; + +// Convenience class that does a CMSTokenSync, and then acquires +// upto three locks. +class CMSTokenSyncWithLocks: public CMSTokenSync { + private: + // Note: locks are acquired in textual declaration order + // and released in the opposite order + MutexLockerEx _locker1, _locker2, _locker3; + public: + CMSTokenSyncWithLocks(bool is_cms_thread, Mutex* mutex1, + Mutex* mutex2 = NULL, Mutex* mutex3 = NULL): + CMSTokenSync(is_cms_thread), + _locker1(mutex1, Mutex::_no_safepoint_check_flag), + _locker2(mutex2, Mutex::_no_safepoint_check_flag), + _locker3(mutex3, Mutex::_no_safepoint_check_flag) + { } +}; + + +// Wrapper class to temporarily disable icms during a foreground cms collection. +class ICMSDisabler: public StackObj { + public: + // The ctor disables icms and wakes up the thread so it notices the change; + // the dtor re-enables icms. Note that the CMSCollector methods will check + // CMSIncrementalMode. + ICMSDisabler() { CMSCollector::disable_icms(); CMSCollector::start_icms(); } + ~ICMSDisabler() { CMSCollector::enable_icms(); } +}; + +////////////////////////////////////////////////////////////////// +// Concurrent Mark-Sweep Generation ///////////////////////////// +////////////////////////////////////////////////////////////////// + +NOT_PRODUCT(CompactibleFreeListSpace* debug_cms_space;) + +// This struct contains per-thread things necessary to support parallel +// young-gen collection. +class CMSParGCThreadState: public CHeapObj { + public: + CFLS_LAB lab; + PromotionInfo promo; + + // Constructor. + CMSParGCThreadState(CompactibleFreeListSpace* cfls) : lab(cfls) { + promo.setSpace(cfls); + } +}; + +ConcurrentMarkSweepGeneration::ConcurrentMarkSweepGeneration( + ReservedSpace rs, size_t initial_byte_size, int level, + CardTableRS* ct, bool use_adaptive_freelists, + FreeBlockDictionary::DictionaryChoice dictionaryChoice) : + CardGeneration(rs, initial_byte_size, level, ct), + _dilatation_factor(((double)MinChunkSize)/((double)(oopDesc::header_size()))), + _debug_collection_type(Concurrent_collection_type) +{ + HeapWord* bottom = (HeapWord*) _virtual_space.low(); + HeapWord* end = (HeapWord*) _virtual_space.high(); + + _direct_allocated_words = 0; + NOT_PRODUCT( + _numObjectsPromoted = 0; + _numWordsPromoted = 0; + _numObjectsAllocated = 0; + _numWordsAllocated = 0; + ) + + _cmsSpace = new CompactibleFreeListSpace(_bts, MemRegion(bottom, end), + use_adaptive_freelists, + dictionaryChoice); + NOT_PRODUCT(debug_cms_space = _cmsSpace;) + if (_cmsSpace == NULL) { + vm_exit_during_initialization( + "CompactibleFreeListSpace allocation failure"); + } + _cmsSpace->_gen = this; + + _gc_stats = new CMSGCStats(); + + // Verify the assumption that FreeChunk::_prev and OopDesc::_klass + // offsets match. The ability to tell free chunks from objects + // depends on this property. + debug_only( + FreeChunk* junk = NULL; + assert(junk->prev_addr() == (void*)(oop(junk)->klass_addr()), + "Offset of FreeChunk::_prev within FreeChunk must match" + " that of OopDesc::_klass within OopDesc"); + ) + if (ParallelGCThreads > 0) { + typedef CMSParGCThreadState* CMSParGCThreadStatePtr; + _par_gc_thread_states = + NEW_C_HEAP_ARRAY(CMSParGCThreadStatePtr, ParallelGCThreads); + if (_par_gc_thread_states == NULL) { + vm_exit_during_initialization("Could not allocate par gc structs"); + } + for (uint i = 0; i < ParallelGCThreads; i++) { + _par_gc_thread_states[i] = new CMSParGCThreadState(cmsSpace()); + if (_par_gc_thread_states[i] == NULL) { + vm_exit_during_initialization("Could not allocate par gc structs"); + } + } + } else { + _par_gc_thread_states = NULL; + } + _incremental_collection_failed = false; + // The "dilatation_factor" is the expansion that can occur on + // account of the fact that the minimum object size in the CMS + // generation may be larger than that in, say, a contiguous young + // generation. + // Ideally, in the calculation below, we'd compute the dilatation + // factor as: MinChunkSize/(promoting_gen's min object size) + // Since we do not have such a general query interface for the + // promoting generation, we'll instead just use the mimimum + // object size (which today is a header's worth of space); + // note that all arithmetic is in units of HeapWords. + assert(MinChunkSize >= oopDesc::header_size(), "just checking"); + assert(_dilatation_factor >= 1.0, "from previous assert"); +} + +void ConcurrentMarkSweepGeneration::ref_processor_init() { + assert(collector() != NULL, "no collector"); + collector()->ref_processor_init(); +} + +void CMSCollector::ref_processor_init() { + if (_ref_processor == NULL) { + // Allocate and initialize a reference processor + _ref_processor = ReferenceProcessor::create_ref_processor( + _span, // span + _cmsGen->refs_discovery_is_atomic(), // atomic_discovery + _cmsGen->refs_discovery_is_mt(), // mt_discovery + &_is_alive_closure, + ParallelGCThreads, + ParallelRefProcEnabled); + // Initialize the _ref_processor field of CMSGen + _cmsGen->set_ref_processor(_ref_processor); + + // Allocate a dummy ref processor for perm gen. + ReferenceProcessor* rp2 = new ReferenceProcessor(); + if (rp2 == NULL) { + vm_exit_during_initialization("Could not allocate ReferenceProcessor object"); + } + _permGen->set_ref_processor(rp2); + } +} + +CMSAdaptiveSizePolicy* CMSCollector::size_policy() { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->kind() == CollectedHeap::GenCollectedHeap, + "Wrong type of heap"); + CMSAdaptiveSizePolicy* sp = (CMSAdaptiveSizePolicy*) + gch->gen_policy()->size_policy(); + assert(sp->is_gc_cms_adaptive_size_policy(), + "Wrong type of size policy"); + return sp; +} + +CMSGCAdaptivePolicyCounters* CMSCollector::gc_adaptive_policy_counters() { + CMSGCAdaptivePolicyCounters* results = + (CMSGCAdaptivePolicyCounters*) collector_policy()->counters(); + assert( + results->kind() == GCPolicyCounters::CMSGCAdaptivePolicyCountersKind, + "Wrong gc policy counter kind"); + return results; +} + + +void ConcurrentMarkSweepGeneration::initialize_performance_counters() { + + const char* gen_name = "old"; + + // Generation Counters - generation 1, 1 subspace + _gen_counters = new GenerationCounters(gen_name, 1, 1, &_virtual_space); + + _space_counters = new GSpaceCounters(gen_name, 0, + _virtual_space.reserved_size(), + this, _gen_counters); +} + +CMSStats::CMSStats(ConcurrentMarkSweepGeneration* cms_gen, unsigned int alpha): + _cms_gen(cms_gen) +{ + assert(alpha <= 100, "bad value"); + _saved_alpha = alpha; + + // Initialize the alphas to the bootstrap value of 100. + _gc0_alpha = _cms_alpha = 100; + + _cms_begin_time.update(); + _cms_end_time.update(); + + _gc0_duration = 0.0; + _gc0_period = 0.0; + _gc0_promoted = 0; + + _cms_duration = 0.0; + _cms_period = 0.0; + _cms_allocated = 0; + + _cms_used_at_gc0_begin = 0; + _cms_used_at_gc0_end = 0; + _allow_duty_cycle_reduction = false; + _valid_bits = 0; + _icms_duty_cycle = CMSIncrementalDutyCycle; +} + +// If promotion failure handling is on use +// the padded average size of the promotion for each +// young generation collection. +double CMSStats::time_until_cms_gen_full() const { + size_t cms_free = _cms_gen->cmsSpace()->free(); + GenCollectedHeap* gch = GenCollectedHeap::heap(); + size_t expected_promotion = gch->get_gen(0)->capacity(); + if (HandlePromotionFailure) { + expected_promotion = MIN2( + (size_t) _cms_gen->gc_stats()->avg_promoted()->padded_average(), + expected_promotion); + } + if (cms_free > expected_promotion) { + // Start a cms collection if there isn't enough space to promote + // for the next minor collection. Use the padded average as + // a safety factor. + cms_free -= expected_promotion; + + // Adjust by the safety factor. + double cms_free_dbl = (double)cms_free; + cms_free_dbl = cms_free_dbl * (100.0 - CMSIncrementalSafetyFactor) / 100.0; + + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("CMSStats::time_until_cms_gen_full: cms_free " + SIZE_FORMAT " expected_promotion " SIZE_FORMAT, + cms_free, expected_promotion); + gclog_or_tty->print_cr(" cms_free_dbl %f cms_consumption_rate %f", + cms_free_dbl, cms_consumption_rate() + 1.0); + } + // Add 1 in case the consumption rate goes to zero. + return cms_free_dbl / (cms_consumption_rate() + 1.0); + } + return 0.0; +} + +// Compare the duration of the cms collection to the +// time remaining before the cms generation is empty. +// Note that the time from the start of the cms collection +// to the start of the cms sweep (less than the total +// duration of the cms collection) can be used. This +// has been tried and some applications experienced +// promotion failures early in execution. This was +// possibly because the averages were not accurate +// enough at the beginning. +double CMSStats::time_until_cms_start() const { + // We add "gc0_period" to the "work" calculation + // below because this query is done (mostly) at the + // end of a scavenge, so we need to conservatively + // account for that much possible delay + // in the query so as to avoid concurrent mode failures + // due to starting the collection just a wee bit too + // late. + double work = cms_duration() + gc0_period(); + double deadline = time_until_cms_gen_full(); + if (work > deadline) { + if (Verbose && PrintGCDetails) { + gclog_or_tty->print( + " CMSCollector: collect because of anticipated promotion " + "before full %3.7f + %3.7f > %3.7f ", cms_duration(), + gc0_period(), time_until_cms_gen_full()); + } + return 0.0; + } + return work - deadline; +} + +// Return a duty cycle based on old_duty_cycle and new_duty_cycle, limiting the +// amount of change to prevent wild oscillation. +unsigned int CMSStats::icms_damped_duty_cycle(unsigned int old_duty_cycle, + unsigned int new_duty_cycle) { + assert(old_duty_cycle <= 100, "bad input value"); + assert(new_duty_cycle <= 100, "bad input value"); + + // Note: use subtraction with caution since it may underflow (values are + // unsigned). Addition is safe since we're in the range 0-100. + unsigned int damped_duty_cycle = new_duty_cycle; + if (new_duty_cycle < old_duty_cycle) { + const unsigned int largest_delta = MAX2(old_duty_cycle / 4, 5U); + if (new_duty_cycle + largest_delta < old_duty_cycle) { + damped_duty_cycle = old_duty_cycle - largest_delta; + } + } else if (new_duty_cycle > old_duty_cycle) { + const unsigned int largest_delta = MAX2(old_duty_cycle / 4, 15U); + if (new_duty_cycle > old_duty_cycle + largest_delta) { + damped_duty_cycle = MIN2(old_duty_cycle + largest_delta, 100U); + } + } + assert(damped_duty_cycle <= 100, "invalid duty cycle computed"); + + if (CMSTraceIncrementalPacing) { + gclog_or_tty->print(" [icms_damped_duty_cycle(%d,%d) = %d] ", + old_duty_cycle, new_duty_cycle, damped_duty_cycle); + } + return damped_duty_cycle; +} + +unsigned int CMSStats::icms_update_duty_cycle_impl() { + assert(CMSIncrementalPacing && valid(), + "should be handled in icms_update_duty_cycle()"); + + double cms_time_so_far = cms_timer().seconds(); + double scaled_duration = cms_duration_per_mb() * _cms_used_at_gc0_end / M; + double scaled_duration_remaining = fabsd(scaled_duration - cms_time_so_far); + + // Avoid division by 0. + double time_until_full = MAX2(time_until_cms_gen_full(), 0.01); + double duty_cycle_dbl = 100.0 * scaled_duration_remaining / time_until_full; + + unsigned int new_duty_cycle = MIN2((unsigned int)duty_cycle_dbl, 100U); + if (new_duty_cycle > _icms_duty_cycle) { + // Avoid very small duty cycles (1 or 2); 0 is allowed. + if (new_duty_cycle > 2) { + _icms_duty_cycle = icms_damped_duty_cycle(_icms_duty_cycle, + new_duty_cycle); + } + } else if (_allow_duty_cycle_reduction) { + // The duty cycle is reduced only once per cms cycle (see record_cms_end()). + new_duty_cycle = icms_damped_duty_cycle(_icms_duty_cycle, new_duty_cycle); + // Respect the minimum duty cycle. + unsigned int min_duty_cycle = (unsigned int)CMSIncrementalDutyCycleMin; + _icms_duty_cycle = MAX2(new_duty_cycle, min_duty_cycle); + } + + if (PrintGCDetails || CMSTraceIncrementalPacing) { + gclog_or_tty->print(" icms_dc=%d ", _icms_duty_cycle); + } + + _allow_duty_cycle_reduction = false; + return _icms_duty_cycle; +} + +#ifndef PRODUCT +void CMSStats::print_on(outputStream *st) const { + st->print(" gc0_alpha=%d,cms_alpha=%d", _gc0_alpha, _cms_alpha); + st->print(",gc0_dur=%g,gc0_per=%g,gc0_promo=" SIZE_FORMAT, + gc0_duration(), gc0_period(), gc0_promoted()); + st->print(",cms_dur=%g,cms_dur_per_mb=%g,cms_per=%g,cms_alloc=" SIZE_FORMAT, + cms_duration(), cms_duration_per_mb(), + cms_period(), cms_allocated()); + st->print(",cms_since_beg=%g,cms_since_end=%g", + cms_time_since_begin(), cms_time_since_end()); + st->print(",cms_used_beg=" SIZE_FORMAT ",cms_used_end=" SIZE_FORMAT, + _cms_used_at_gc0_begin, _cms_used_at_gc0_end); + if (CMSIncrementalMode) { + st->print(",dc=%d", icms_duty_cycle()); + } + + if (valid()) { + st->print(",promo_rate=%g,cms_alloc_rate=%g", + promotion_rate(), cms_allocation_rate()); + st->print(",cms_consumption_rate=%g,time_until_full=%g", + cms_consumption_rate(), time_until_cms_gen_full()); + } + st->print(" "); +} +#endif // #ifndef PRODUCT + +CMSCollector::CollectorState CMSCollector::_collectorState = + CMSCollector::Idling; +bool CMSCollector::_foregroundGCIsActive = false; +bool CMSCollector::_foregroundGCShouldWait = false; + +CMSCollector::CMSCollector(ConcurrentMarkSweepGeneration* cmsGen, + ConcurrentMarkSweepGeneration* permGen, + CardTableRS* ct, + ConcurrentMarkSweepPolicy* cp): + _cmsGen(cmsGen), + _permGen(permGen), + _ct(ct), + _ref_processor(NULL), // will be set later + _conc_workers(NULL), // may be set later + _abort_preclean(false), + _start_sampling(false), + _between_prologue_and_epilogue(false), + _markBitMap(0, Mutex::leaf + 1, "CMS_markBitMap_lock"), + _perm_gen_verify_bit_map(0, -1 /* no mutex */, "No_lock"), + _modUnionTable((CardTableModRefBS::card_shift - LogHeapWordSize), + -1 /* lock-free */, "No_lock" /* dummy */), + _modUnionClosure(&_modUnionTable), + _modUnionClosurePar(&_modUnionTable), + _is_alive_closure(&_markBitMap), + _restart_addr(NULL), + _overflow_list(NULL), + _preserved_oop_stack(NULL), + _preserved_mark_stack(NULL), + _stats(cmsGen), + _eden_chunk_array(NULL), // may be set in ctor body + _eden_chunk_capacity(0), // -- ditto -- + _eden_chunk_index(0), // -- ditto -- + _survivor_plab_array(NULL), // -- ditto -- + _survivor_chunk_array(NULL), // -- ditto -- + _survivor_chunk_capacity(0), // -- ditto -- + _survivor_chunk_index(0), // -- ditto -- + _ser_pmc_preclean_ovflw(0), + _ser_pmc_remark_ovflw(0), + _par_pmc_remark_ovflw(0), + _ser_kac_ovflw(0), + _par_kac_ovflw(0), +#ifndef PRODUCT + _num_par_pushes(0), +#endif + _collection_count_start(0), + _verifying(false), + _icms_start_limit(NULL), + _icms_stop_limit(NULL), + _verification_mark_bm(0, Mutex::leaf + 1, "CMS_verification_mark_bm_lock"), + _completed_initialization(false), + _collector_policy(cp), + _unload_classes(false), + _unloaded_classes_last_cycle(false), + _sweep_estimate(CMS_SweepWeight, CMS_SweepPadding) +{ + if (ExplicitGCInvokesConcurrentAndUnloadsClasses) { + ExplicitGCInvokesConcurrent = true; + } + // Now expand the span and allocate the collection support structures + // (MUT, marking bit map etc.) to cover both generations subject to + // collection. + + // First check that _permGen is adjacent to _cmsGen and above it. + assert( _cmsGen->reserved().word_size() > 0 + && _permGen->reserved().word_size() > 0, + "generations should not be of zero size"); + assert(_cmsGen->reserved().intersection(_permGen->reserved()).is_empty(), + "_cmsGen and _permGen should not overlap"); + assert(_cmsGen->reserved().end() == _permGen->reserved().start(), + "_cmsGen->end() different from _permGen->start()"); + + // For use by dirty card to oop closures. + _cmsGen->cmsSpace()->set_collector(this); + _permGen->cmsSpace()->set_collector(this); + + // Adjust my span to cover old (cms) gen and perm gen + _span = _cmsGen->reserved()._union(_permGen->reserved()); + // Initialize the span of is_alive_closure + _is_alive_closure.set_span(_span); + + // Allocate MUT and marking bit map + { + MutexLockerEx x(_markBitMap.lock(), Mutex::_no_safepoint_check_flag); + if (!_markBitMap.allocate(_span)) { + warning("Failed to allocate CMS Bit Map"); + return; + } + assert(_markBitMap.covers(_span), "_markBitMap inconsistency?"); + } + { + _modUnionTable.allocate(_span); + assert(_modUnionTable.covers(_span), "_modUnionTable inconsistency?"); + } + + if (!_markStack.allocate(CMSMarkStackSize)) { + warning("Failed to allocate CMS Marking Stack"); + return; + } + if (!_revisitStack.allocate(CMSRevisitStackSize)) { + warning("Failed to allocate CMS Revisit Stack"); + return; + } + + // Support for multi-threaded concurrent phases + if (ParallelGCThreads > 0 && CMSConcurrentMTEnabled) { + if (FLAG_IS_DEFAULT(ParallelCMSThreads)) { + // just for now + FLAG_SET_DEFAULT(ParallelCMSThreads, (ParallelGCThreads + 3)/4); + } + if (ParallelCMSThreads > 1) { + _conc_workers = new YieldingFlexibleWorkGang("Parallel CMS Threads", + ParallelCMSThreads, true); + if (_conc_workers == NULL) { + warning("GC/CMS: _conc_workers allocation failure: " + "forcing -CMSConcurrentMTEnabled"); + CMSConcurrentMTEnabled = false; + } + } else { + CMSConcurrentMTEnabled = false; + } + } + if (!CMSConcurrentMTEnabled) { + ParallelCMSThreads = 0; + } else { + // Turn off CMSCleanOnEnter optimization temporarily for + // the MT case where it's not fixed yet; see 6178663. + CMSCleanOnEnter = false; + } + assert((_conc_workers != NULL) == (ParallelCMSThreads > 1), + "Inconsistency"); + + // Parallel task queues; these are shared for the + // concurrent and stop-world phases of CMS, but + // are not shared with parallel scavenge (ParNew). + { + uint i; + uint num_queues = (uint) MAX2(ParallelGCThreads, ParallelCMSThreads); + + if ((CMSParallelRemarkEnabled || CMSConcurrentMTEnabled + || ParallelRefProcEnabled) + && num_queues > 0) { + _task_queues = new OopTaskQueueSet(num_queues); + if (_task_queues == NULL) { + warning("task_queues allocation failure."); + return; + } + _hash_seed = NEW_C_HEAP_ARRAY(int, num_queues); + if (_hash_seed == NULL) { + warning("_hash_seed array allocation failure"); + return; + } + + // XXX use a global constant instead of 64! + typedef struct OopTaskQueuePadded { + OopTaskQueue work_queue; + char pad[64 - sizeof(OopTaskQueue)]; // prevent false sharing + } OopTaskQueuePadded; + + for (i = 0; i < num_queues; i++) { + OopTaskQueuePadded *q_padded = new OopTaskQueuePadded(); + if (q_padded == NULL) { + warning("work_queue allocation failure."); + return; + } + _task_queues->register_queue(i, &q_padded->work_queue); + } + for (i = 0; i < num_queues; i++) { + _task_queues->queue(i)->initialize(); + _hash_seed[i] = 17; // copied from ParNew + } + } + } + + // "initiatingOccupancy" is the occupancy ratio at which we trigger + // a new collection cycle. Unless explicitly specified via + // CMSTriggerRatio, it is calculated by: + // Let "f" be MinHeapFreeRatio in + // + // intiatingOccupancy = 100-f + + // f * (CMSTriggerRatio/100) + // That is, if we assume the heap is at its desired maximum occupancy at the + // end of a collection, we let CMSTriggerRatio of the (purported) free + // space be allocated before initiating a new collection cycle. + if (CMSInitiatingOccupancyFraction > 0) { + _initiatingOccupancy = (double)CMSInitiatingOccupancyFraction / 100.0; + } else { + _initiatingOccupancy = ((100 - MinHeapFreeRatio) + + (double)(CMSTriggerRatio * + MinHeapFreeRatio) / 100.0) + / 100.0; + } + // Clip CMSBootstrapOccupancy between 0 and 100. + _bootstrap_occupancy = ((double)MIN2((intx)100, MAX2((intx)0, CMSBootstrapOccupancy))) + /(double)100; + + _full_gcs_since_conc_gc = 0; + + // Now tell CMS generations the identity of their collector + ConcurrentMarkSweepGeneration::set_collector(this); + + // Create & start a CMS thread for this CMS collector + _cmsThread = ConcurrentMarkSweepThread::start(this); + assert(cmsThread() != NULL, "CMS Thread should have been created"); + assert(cmsThread()->collector() == this, + "CMS Thread should refer to this gen"); + assert(CGC_lock != NULL, "Where's the CGC_lock?"); + + // Support for parallelizing young gen rescan + GenCollectedHeap* gch = GenCollectedHeap::heap(); + _young_gen = gch->prev_gen(_cmsGen); + if (gch->supports_inline_contig_alloc()) { + _top_addr = gch->top_addr(); + _end_addr = gch->end_addr(); + assert(_young_gen != NULL, "no _young_gen"); + _eden_chunk_index = 0; + _eden_chunk_capacity = (_young_gen->max_capacity()+CMSSamplingGrain)/CMSSamplingGrain; + _eden_chunk_array = NEW_C_HEAP_ARRAY(HeapWord*, _eden_chunk_capacity); + if (_eden_chunk_array == NULL) { + _eden_chunk_capacity = 0; + warning("GC/CMS: _eden_chunk_array allocation failure"); + } + } + assert(_eden_chunk_array != NULL || _eden_chunk_capacity == 0, "Error"); + + // Support for parallelizing survivor space rescan + if (CMSParallelRemarkEnabled && CMSParallelSurvivorRemarkEnabled) { + size_t max_plab_samples = MaxNewSize/((SurvivorRatio+2)*MinTLABSize); + _survivor_plab_array = NEW_C_HEAP_ARRAY(ChunkArray, ParallelGCThreads); + _survivor_chunk_array = NEW_C_HEAP_ARRAY(HeapWord*, 2*max_plab_samples); + _cursor = NEW_C_HEAP_ARRAY(size_t, ParallelGCThreads); + if (_survivor_plab_array == NULL || _survivor_chunk_array == NULL + || _cursor == NULL) { + warning("Failed to allocate survivor plab/chunk array"); + if (_survivor_plab_array != NULL) { + FREE_C_HEAP_ARRAY(ChunkArray, _survivor_plab_array); + _survivor_plab_array = NULL; + } + if (_survivor_chunk_array != NULL) { + FREE_C_HEAP_ARRAY(HeapWord*, _survivor_chunk_array); + _survivor_chunk_array = NULL; + } + if (_cursor != NULL) { + FREE_C_HEAP_ARRAY(size_t, _cursor); + _cursor = NULL; + } + } else { + _survivor_chunk_capacity = 2*max_plab_samples; + for (uint i = 0; i < ParallelGCThreads; i++) { + HeapWord** vec = NEW_C_HEAP_ARRAY(HeapWord*, max_plab_samples); + if (vec == NULL) { + warning("Failed to allocate survivor plab array"); + for (int j = i; j > 0; j--) { + FREE_C_HEAP_ARRAY(HeapWord*, _survivor_plab_array[j-1].array()); + } + FREE_C_HEAP_ARRAY(ChunkArray, _survivor_plab_array); + FREE_C_HEAP_ARRAY(HeapWord*, _survivor_chunk_array); + _survivor_plab_array = NULL; + _survivor_chunk_array = NULL; + _survivor_chunk_capacity = 0; + break; + } else { + ChunkArray* cur = + ::new (&_survivor_plab_array[i]) ChunkArray(vec, + max_plab_samples); + assert(cur->end() == 0, "Should be 0"); + assert(cur->array() == vec, "Should be vec"); + assert(cur->capacity() == max_plab_samples, "Error"); + } + } + } + } + assert( ( _survivor_plab_array != NULL + && _survivor_chunk_array != NULL) + || ( _survivor_chunk_capacity == 0 + && _survivor_chunk_index == 0), + "Error"); + + // Choose what strong roots should be scanned depending on verification options + // and perm gen collection mode. + if (!CMSClassUnloadingEnabled) { + // If class unloading is disabled we want to include all classes into the root set. + add_root_scanning_option(SharedHeap::SO_AllClasses); + } else { + add_root_scanning_option(SharedHeap::SO_SystemClasses); + } + + NOT_PRODUCT(_overflow_counter = CMSMarkStackOverflowInterval;) + _gc_counters = new CollectorCounters("CMS", 1); + _completed_initialization = true; + _sweep_timer.start(); // start of time +} + +const char* ConcurrentMarkSweepGeneration::name() const { + return "concurrent mark-sweep generation"; +} +void ConcurrentMarkSweepGeneration::update_counters() { + if (UsePerfData) { + _space_counters->update_all(); + _gen_counters->update_all(); + } +} + +// this is an optimized version of update_counters(). it takes the +// used value as a parameter rather than computing it. +// +void ConcurrentMarkSweepGeneration::update_counters(size_t used) { + if (UsePerfData) { + _space_counters->update_used(used); + _space_counters->update_capacity(); + _gen_counters->update_all(); + } +} + +void ConcurrentMarkSweepGeneration::print() const { + Generation::print(); + cmsSpace()->print(); +} + +#ifndef PRODUCT +void ConcurrentMarkSweepGeneration::print_statistics() { + cmsSpace()->printFLCensus(0); +} +#endif + +void ConcurrentMarkSweepGeneration::printOccupancy(const char *s) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + if (PrintGCDetails) { + if (Verbose) { + gclog_or_tty->print(" [%d %s-%s: "SIZE_FORMAT"("SIZE_FORMAT")]", + level(), short_name(), s, used(), capacity()); + } else { + gclog_or_tty->print(" [%d %s-%s: "SIZE_FORMAT"K("SIZE_FORMAT"K)]", + level(), short_name(), s, used() / K, capacity() / K); + } + } + if (Verbose) { + gclog_or_tty->print(" "SIZE_FORMAT"("SIZE_FORMAT")", + gch->used(), gch->capacity()); + } else { + gclog_or_tty->print(" "SIZE_FORMAT"K("SIZE_FORMAT"K)", + gch->used() / K, gch->capacity() / K); + } +} + +size_t +ConcurrentMarkSweepGeneration::contiguous_available() const { + // dld proposes an improvement in precision here. If the committed + // part of the space ends in a free block we should add that to + // uncommitted size in the calculation below. Will make this + // change later, staying with the approximation below for the + // time being. -- ysr. + return MAX2(_virtual_space.uncommitted_size(), unsafe_max_alloc_nogc()); +} + +size_t +ConcurrentMarkSweepGeneration::unsafe_max_alloc_nogc() const { + return _cmsSpace->max_alloc_in_words() * HeapWordSize; +} + +size_t ConcurrentMarkSweepGeneration::max_available() const { + return free() + _virtual_space.uncommitted_size(); +} + +bool ConcurrentMarkSweepGeneration::promotion_attempt_is_safe( + size_t max_promotion_in_bytes, + bool younger_handles_promotion_failure) const { + + // This is the most conservative test. Full promotion is + // guaranteed if this is used. The multiplicative factor is to + // account for the worst case "dilatation". + double adjusted_max_promo_bytes = _dilatation_factor * max_promotion_in_bytes; + if (adjusted_max_promo_bytes > (double)max_uintx) { // larger than size_t + adjusted_max_promo_bytes = (double)max_uintx; + } + bool result = (max_contiguous_available() >= (size_t)adjusted_max_promo_bytes); + + if (younger_handles_promotion_failure && !result) { + // Full promotion is not guaranteed because fragmentation + // of the cms generation can prevent the full promotion. + result = (max_available() >= (size_t)adjusted_max_promo_bytes); + + if (!result) { + // With promotion failure handling the test for the ability + // to support the promotion does not have to be guaranteed. + // Use an average of the amount promoted. + result = max_available() >= (size_t) + gc_stats()->avg_promoted()->padded_average(); + if (PrintGC && Verbose && result) { + gclog_or_tty->print_cr( + "\nConcurrentMarkSweepGeneration::promotion_attempt_is_safe" + " max_available: " SIZE_FORMAT + " avg_promoted: " SIZE_FORMAT, + max_available(), (size_t) + gc_stats()->avg_promoted()->padded_average()); + } + } else { + if (PrintGC && Verbose) { + gclog_or_tty->print_cr( + "\nConcurrentMarkSweepGeneration::promotion_attempt_is_safe" + " max_available: " SIZE_FORMAT + " adj_max_promo_bytes: " SIZE_FORMAT, + max_available(), (size_t)adjusted_max_promo_bytes); + } + } + } else { + if (PrintGC && Verbose) { + gclog_or_tty->print_cr( + "\nConcurrentMarkSweepGeneration::promotion_attempt_is_safe" + " contiguous_available: " SIZE_FORMAT + " adj_max_promo_bytes: " SIZE_FORMAT, + max_contiguous_available(), (size_t)adjusted_max_promo_bytes); + } + } + return result; +} + +CompactibleSpace* +ConcurrentMarkSweepGeneration::first_compaction_space() const { + return _cmsSpace; +} + +void ConcurrentMarkSweepGeneration::reset_after_compaction() { + // Clear the promotion information. These pointers can be adjusted + // along with all the other pointers into the heap but + // compaction is expected to be a rare event with + // a heap using cms so don't do it without seeing the need. + if (ParallelGCThreads > 0) { + for (uint i = 0; i < ParallelGCThreads; i++) { + _par_gc_thread_states[i]->promo.reset(); + } + } +} + +void ConcurrentMarkSweepGeneration::space_iterate(SpaceClosure* blk, bool usedOnly) { + blk->do_space(_cmsSpace); +} + +void ConcurrentMarkSweepGeneration::compute_new_size() { + assert_locked_or_safepoint(Heap_lock); + + // If incremental collection failed, we just want to expand + // to the limit. + if (incremental_collection_failed()) { + clear_incremental_collection_failed(); + grow_to_reserved(); + return; + } + + size_t expand_bytes = 0; + double free_percentage = ((double) free()) / capacity(); + double desired_free_percentage = (double) MinHeapFreeRatio / 100; + double maximum_free_percentage = (double) MaxHeapFreeRatio / 100; + + // compute expansion delta needed for reaching desired free percentage + if (free_percentage < desired_free_percentage) { + size_t desired_capacity = (size_t)(used() / ((double) 1 - desired_free_percentage)); + assert(desired_capacity >= capacity(), "invalid expansion size"); + expand_bytes = MAX2(desired_capacity - capacity(), MinHeapDeltaBytes); + } + if (expand_bytes > 0) { + if (PrintGCDetails && Verbose) { + size_t desired_capacity = (size_t)(used() / ((double) 1 - desired_free_percentage)); + gclog_or_tty->print_cr("\nFrom compute_new_size: "); + gclog_or_tty->print_cr(" Free fraction %f", free_percentage); + gclog_or_tty->print_cr(" Desired free fraction %f", + desired_free_percentage); + gclog_or_tty->print_cr(" Maximum free fraction %f", + maximum_free_percentage); + gclog_or_tty->print_cr(" Capactiy "SIZE_FORMAT, capacity()/1000); + gclog_or_tty->print_cr(" Desired capacity "SIZE_FORMAT, + desired_capacity/1000); + int prev_level = level() - 1; + if (prev_level >= 0) { + size_t prev_size = 0; + GenCollectedHeap* gch = GenCollectedHeap::heap(); + Generation* prev_gen = gch->_gens[prev_level]; + prev_size = prev_gen->capacity(); + gclog_or_tty->print_cr(" Younger gen size "SIZE_FORMAT, + prev_size/1000); + } + gclog_or_tty->print_cr(" unsafe_max_alloc_nogc "SIZE_FORMAT, + unsafe_max_alloc_nogc()/1000); + gclog_or_tty->print_cr(" contiguous available "SIZE_FORMAT, + contiguous_available()/1000); + gclog_or_tty->print_cr(" Expand by "SIZE_FORMAT" (bytes)", + expand_bytes); + } + // safe if expansion fails + expand(expand_bytes, 0, CMSExpansionCause::_satisfy_free_ratio); + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr(" Expanded free fraction %f", + ((double) free()) / capacity()); + } + } +} + +Mutex* ConcurrentMarkSweepGeneration::freelistLock() const { + return cmsSpace()->freelistLock(); +} + +HeapWord* ConcurrentMarkSweepGeneration::allocate(size_t size, + bool tlab) { + CMSSynchronousYieldRequest yr; + MutexLockerEx x(freelistLock(), + Mutex::_no_safepoint_check_flag); + return have_lock_and_allocate(size, tlab); +} + +HeapWord* ConcurrentMarkSweepGeneration::have_lock_and_allocate(size_t size, + bool tlab) { + assert_lock_strong(freelistLock()); + size_t adjustedSize = CompactibleFreeListSpace::adjustObjectSize(size); + HeapWord* res = cmsSpace()->allocate(adjustedSize); + // Allocate the object live (grey) if the background collector has + // started marking. This is necessary because the marker may + // have passed this address and consequently this object will + // not otherwise be greyed and would be incorrectly swept up. + // Note that if this object contains references, the writing + // of those references will dirty the card containing this object + // allowing the object to be blackened (and its references scanned) + // either during a preclean phase or at the final checkpoint. + if (res != NULL) { + collector()->direct_allocated(res, adjustedSize); + _direct_allocated_words += adjustedSize; + // allocation counters + NOT_PRODUCT( + _numObjectsAllocated++; + _numWordsAllocated += (int)adjustedSize; + ) + } + return res; +} + +// In the case of direct allocation by mutators in a generation that +// is being concurrently collected, the object must be allocated +// live (grey) if the background collector has started marking. +// This is necessary because the marker may +// have passed this address and consequently this object will +// not otherwise be greyed and would be incorrectly swept up. +// Note that if this object contains references, the writing +// of those references will dirty the card containing this object +// allowing the object to be blackened (and its references scanned) +// either during a preclean phase or at the final checkpoint. +void CMSCollector::direct_allocated(HeapWord* start, size_t size) { + assert(_markBitMap.covers(start, size), "Out of bounds"); + if (_collectorState >= Marking) { + MutexLockerEx y(_markBitMap.lock(), + Mutex::_no_safepoint_check_flag); + // [see comments preceding SweepClosure::do_blk() below for details] + // 1. need to mark the object as live so it isn't collected + // 2. need to mark the 2nd bit to indicate the object may be uninitialized + // 3. need to mark the end of the object so sweeper can skip over it + // if it's uninitialized when the sweeper reaches it. + _markBitMap.mark(start); // object is live + _markBitMap.mark(start + 1); // object is potentially uninitialized? + _markBitMap.mark(start + size - 1); + // mark end of object + } + // check that oop looks uninitialized + assert(oop(start)->klass() == NULL, "_klass should be NULL"); +} + +void CMSCollector::promoted(bool par, HeapWord* start, + bool is_obj_array, size_t obj_size) { + assert(_markBitMap.covers(start), "Out of bounds"); + // See comment in direct_allocated() about when objects should + // be allocated live. + if (_collectorState >= Marking) { + // we already hold the marking bit map lock, taken in + // the prologue + if (par) { + _markBitMap.par_mark(start); + } else { + _markBitMap.mark(start); + } + // We don't need to mark the object as uninitialized (as + // in direct_allocated above) because this is being done with the + // world stopped and the object will be initialized by the + // time the sweeper gets to look at it. + assert(SafepointSynchronize::is_at_safepoint(), + "expect promotion only at safepoints"); + + if (_collectorState < Sweeping) { + // Mark the appropriate cards in the modUnionTable, so that + // this object gets scanned before the sweep. If this is + // not done, CMS generation references in the object might + // not get marked. + // For the case of arrays, which are otherwise precisely + // marked, we need to dirty the entire array, not just its head. + if (is_obj_array) { + // The [par_]mark_range() method expects mr.end() below to + // be aligned to the granularity of a bit's representation + // in the heap. In the case of the MUT below, that's a + // card size. + MemRegion mr(start, + (HeapWord*)round_to((intptr_t)(start + obj_size), + CardTableModRefBS::card_size /* bytes */)); + if (par) { + _modUnionTable.par_mark_range(mr); + } else { + _modUnionTable.mark_range(mr); + } + } else { // not an obj array; we can just mark the head + if (par) { + _modUnionTable.par_mark(start); + } else { + _modUnionTable.mark(start); + } + } + } + } +} + +static inline size_t percent_of_space(Space* space, HeapWord* addr) +{ + size_t delta = pointer_delta(addr, space->bottom()); + return (size_t)(delta * 100.0 / (space->capacity() / HeapWordSize)); +} + +void CMSCollector::icms_update_allocation_limits() +{ + Generation* gen0 = GenCollectedHeap::heap()->get_gen(0); + EdenSpace* eden = gen0->as_DefNewGeneration()->eden(); + + const unsigned int duty_cycle = stats().icms_update_duty_cycle(); + if (CMSTraceIncrementalPacing) { + stats().print(); + } + + assert(duty_cycle <= 100, "invalid duty cycle"); + if (duty_cycle != 0) { + // The duty_cycle is a percentage between 0 and 100; convert to words and + // then compute the offset from the endpoints of the space. + size_t free_words = eden->free() / HeapWordSize; + double free_words_dbl = (double)free_words; + size_t duty_cycle_words = (size_t)(free_words_dbl * duty_cycle / 100.0); + size_t offset_words = (free_words - duty_cycle_words) / 2; + + _icms_start_limit = eden->top() + offset_words; + _icms_stop_limit = eden->end() - offset_words; + + // The limits may be adjusted (shifted to the right) by + // CMSIncrementalOffset, to allow the application more mutator time after a + // young gen gc (when all mutators were stopped) and before CMS starts and + // takes away one or more cpus. + if (CMSIncrementalOffset != 0) { + double adjustment_dbl = free_words_dbl * CMSIncrementalOffset / 100.0; + size_t adjustment = (size_t)adjustment_dbl; + HeapWord* tmp_stop = _icms_stop_limit + adjustment; + if (tmp_stop > _icms_stop_limit && tmp_stop < eden->end()) { + _icms_start_limit += adjustment; + _icms_stop_limit = tmp_stop; + } + } + } + if (duty_cycle == 0 || (_icms_start_limit == _icms_stop_limit)) { + _icms_start_limit = _icms_stop_limit = eden->end(); + } + + // Install the new start limit. + eden->set_soft_end(_icms_start_limit); + + if (CMSTraceIncrementalMode) { + gclog_or_tty->print(" icms alloc limits: " + PTR_FORMAT "," PTR_FORMAT + " (" SIZE_FORMAT "%%," SIZE_FORMAT "%%) ", + _icms_start_limit, _icms_stop_limit, + percent_of_space(eden, _icms_start_limit), + percent_of_space(eden, _icms_stop_limit)); + if (Verbose) { + gclog_or_tty->print("eden: "); + eden->print_on(gclog_or_tty); + } + } +} + +// Any changes here should try to maintain the invariant +// that if this method is called with _icms_start_limit +// and _icms_stop_limit both NULL, then it should return NULL +// and not notify the icms thread. +HeapWord* +CMSCollector::allocation_limit_reached(Space* space, HeapWord* top, + size_t word_size) +{ + // A start_limit equal to end() means the duty cycle is 0, so treat that as a + // nop. + if (CMSIncrementalMode && _icms_start_limit != space->end()) { + if (top <= _icms_start_limit) { + if (CMSTraceIncrementalMode) { + space->print_on(gclog_or_tty); + gclog_or_tty->stamp(); + gclog_or_tty->print_cr(" start limit top=" PTR_FORMAT + ", new limit=" PTR_FORMAT + " (" SIZE_FORMAT "%%)", + top, _icms_stop_limit, + percent_of_space(space, _icms_stop_limit)); + } + ConcurrentMarkSweepThread::start_icms(); + assert(top < _icms_stop_limit, "Tautology"); + if (word_size < pointer_delta(_icms_stop_limit, top)) { + return _icms_stop_limit; + } + + // The allocation will cross both the _start and _stop limits, so do the + // stop notification also and return end(). + if (CMSTraceIncrementalMode) { + space->print_on(gclog_or_tty); + gclog_or_tty->stamp(); + gclog_or_tty->print_cr(" +stop limit top=" PTR_FORMAT + ", new limit=" PTR_FORMAT + " (" SIZE_FORMAT "%%)", + top, space->end(), + percent_of_space(space, space->end())); + } + ConcurrentMarkSweepThread::stop_icms(); + return space->end(); + } + + if (top <= _icms_stop_limit) { + if (CMSTraceIncrementalMode) { + space->print_on(gclog_or_tty); + gclog_or_tty->stamp(); + gclog_or_tty->print_cr(" stop limit top=" PTR_FORMAT + ", new limit=" PTR_FORMAT + " (" SIZE_FORMAT "%%)", + top, space->end(), + percent_of_space(space, space->end())); + } + ConcurrentMarkSweepThread::stop_icms(); + return space->end(); + } + + if (CMSTraceIncrementalMode) { + space->print_on(gclog_or_tty); + gclog_or_tty->stamp(); + gclog_or_tty->print_cr(" end limit top=" PTR_FORMAT + ", new limit=" PTR_FORMAT, + top, NULL); + } + } + + return NULL; +} + +oop ConcurrentMarkSweepGeneration::promote(oop obj, size_t obj_size, oop* ref) { + assert(obj_size == (size_t)obj->size(), "bad obj_size passed in"); + // allocate, copy and if necessary update promoinfo -- + // delegate to underlying space. + assert_lock_strong(freelistLock()); + +#ifndef PRODUCT + if (Universe::heap()->promotion_should_fail()) { + return NULL; + } +#endif // #ifndef PRODUCT + + oop res = _cmsSpace->promote(obj, obj_size, ref); + if (res == NULL) { + // expand and retry + size_t s = _cmsSpace->expansionSpaceRequired(obj_size); // HeapWords + expand(s*HeapWordSize, MinHeapDeltaBytes, + CMSExpansionCause::_satisfy_promotion); + // Since there's currently no next generation, we don't try to promote + // into a more senior generation. + assert(next_gen() == NULL, "assumption, based upon which no attempt " + "is made to pass on a possibly failing " + "promotion to next generation"); + res = _cmsSpace->promote(obj, obj_size, ref); + } + if (res != NULL) { + // See comment in allocate() about when objects should + // be allocated live. + assert(obj->is_oop(), "Will dereference klass pointer below"); + collector()->promoted(false, // Not parallel + (HeapWord*)res, obj->is_objArray(), obj_size); + // promotion counters + NOT_PRODUCT( + _numObjectsPromoted++; + _numWordsPromoted += + (int)(CompactibleFreeListSpace::adjustObjectSize(obj->size())); + ) + } + return res; +} + + +HeapWord* +ConcurrentMarkSweepGeneration::allocation_limit_reached(Space* space, + HeapWord* top, + size_t word_sz) +{ + return collector()->allocation_limit_reached(space, top, word_sz); +} + +// Things to support parallel young-gen collection. +oop +ConcurrentMarkSweepGeneration::par_promote(int thread_num, + oop old, markOop m, + size_t word_sz) { +#ifndef PRODUCT + if (Universe::heap()->promotion_should_fail()) { + return NULL; + } +#endif // #ifndef PRODUCT + + CMSParGCThreadState* ps = _par_gc_thread_states[thread_num]; + PromotionInfo* promoInfo = &ps->promo; + // if we are tracking promotions, then first ensure space for + // promotion (including spooling space for saving header if necessary). + // then allocate and copy, then track promoted info if needed. + // When tracking (see PromotionInfo::track()), the mark word may + // be displaced and in this case restoration of the mark word + // occurs in the (oop_since_save_marks_)iterate phase. + if (promoInfo->tracking() && !promoInfo->ensure_spooling_space()) { + // Out of space for allocating spooling buffers; + // try expanding and allocating spooling buffers. + if (!expand_and_ensure_spooling_space(promoInfo)) { + return NULL; + } + } + assert(promoInfo->has_spooling_space(), "Control point invariant"); + HeapWord* obj_ptr = ps->lab.alloc(word_sz); + if (obj_ptr == NULL) { + obj_ptr = expand_and_par_lab_allocate(ps, word_sz); + if (obj_ptr == NULL) { + return NULL; + } + } + oop obj = oop(obj_ptr); + assert(obj->klass() == NULL, "Object should be uninitialized here."); + // Otherwise, copy the object. Here we must be careful to insert the + // klass pointer last, since this marks the block as an allocated object. + HeapWord* old_ptr = (HeapWord*)old; + if (word_sz > (size_t)oopDesc::header_size()) { + Copy::aligned_disjoint_words(old_ptr + oopDesc::header_size(), + obj_ptr + oopDesc::header_size(), + word_sz - oopDesc::header_size()); + } + // Restore the mark word copied above. + obj->set_mark(m); + // Now we can track the promoted object, if necessary. We take care + // To delay the transition from uninitialized to full object + // (i.e., insertion of klass pointer) until after, so that it + // atomically becomes a promoted object. + if (promoInfo->tracking()) { + promoInfo->track((PromotedObject*)obj, old->klass()); + } + // Finally, install the klass pointer. + obj->set_klass(old->klass()); + + assert(old->is_oop(), "Will dereference klass ptr below"); + collector()->promoted(true, // parallel + obj_ptr, old->is_objArray(), word_sz); + + NOT_PRODUCT( + Atomic::inc(&_numObjectsPromoted); + Atomic::add((jint)CompactibleFreeListSpace::adjustObjectSize(obj->size()), + &_numWordsPromoted); + ) + + return obj; +} + +void +ConcurrentMarkSweepGeneration:: +par_promote_alloc_undo(int thread_num, + HeapWord* obj, size_t word_sz) { + // CMS does not support promotion undo. + ShouldNotReachHere(); +} + +void +ConcurrentMarkSweepGeneration:: +par_promote_alloc_done(int thread_num) { + CMSParGCThreadState* ps = _par_gc_thread_states[thread_num]; + ps->lab.retire(); +#if CFLS_LAB_REFILL_STATS + if (thread_num == 0) { + _cmsSpace->print_par_alloc_stats(); + } +#endif +} + +void +ConcurrentMarkSweepGeneration:: +par_oop_since_save_marks_iterate_done(int thread_num) { + CMSParGCThreadState* ps = _par_gc_thread_states[thread_num]; + ParScanWithoutBarrierClosure* dummy_cl = NULL; + ps->promo.promoted_oops_iterate_nv(dummy_cl); +} + +// XXXPERM +bool ConcurrentMarkSweepGeneration::should_collect(bool full, + size_t size, + bool tlab) +{ + // We allow a STW collection only if a full + // collection was requested. + return full || should_allocate(size, tlab); // FIX ME !!! + // This and promotion failure handling are connected at the + // hip and should be fixed by untying them. +} + +bool CMSCollector::shouldConcurrentCollect() { + if (_full_gc_requested) { + assert(ExplicitGCInvokesConcurrent, "Unexpected state"); + if (Verbose && PrintGCDetails) { + gclog_or_tty->print_cr("CMSCollector: collect because of explicit " + " gc request"); + } + return true; + } + + // For debugging purposes, change the type of collection. + // If the rotation is not on the concurrent collection + // type, don't start a concurrent collection. + NOT_PRODUCT( + if (RotateCMSCollectionTypes && + (_cmsGen->debug_collection_type() != + ConcurrentMarkSweepGeneration::Concurrent_collection_type)) { + assert(_cmsGen->debug_collection_type() != + ConcurrentMarkSweepGeneration::Unknown_collection_type, + "Bad cms collection type"); + return false; + } + ) + + FreelistLocker x(this); + // ------------------------------------------------------------------ + // Print out lots of information which affects the initiation of + // a collection. + if (PrintCMSInitiationStatistics && stats().valid()) { + gclog_or_tty->print("CMSCollector shouldConcurrentCollect: "); + gclog_or_tty->stamp(); + gclog_or_tty->print_cr(""); + stats().print_on(gclog_or_tty); + gclog_or_tty->print_cr("time_until_cms_gen_full %3.7f", + stats().time_until_cms_gen_full()); + gclog_or_tty->print_cr("free="SIZE_FORMAT, _cmsGen->free()); + gclog_or_tty->print_cr("contiguous_available="SIZE_FORMAT, + _cmsGen->contiguous_available()); + gclog_or_tty->print_cr("promotion_rate=%g", stats().promotion_rate()); + gclog_or_tty->print_cr("cms_allocation_rate=%g", stats().cms_allocation_rate()); + gclog_or_tty->print_cr("occupancy=%3.7f", _cmsGen->occupancy()); + gclog_or_tty->print_cr("initiatingOccupancy=%3.7f", initiatingOccupancy()); + } + // ------------------------------------------------------------------ + + // If the estimated time to complete a cms collection (cms_duration()) + // is less than the estimated time remaining until the cms generation + // is full, start a collection. + if (!UseCMSInitiatingOccupancyOnly) { + if (stats().valid()) { + if (stats().time_until_cms_start() == 0.0) { + return true; + } + } else { + // We want to conservatively collect somewhat early in order + // to try and "bootstrap" our CMS/promotion statistics; + // this branch will not fire after the first successful CMS + // collection because the stats should then be valid. + if (_cmsGen->occupancy() >= _bootstrap_occupancy) { + if (Verbose && PrintGCDetails) { + gclog_or_tty->print_cr( + " CMSCollector: collect for bootstrapping statistics:" + " occupancy = %f, boot occupancy = %f", _cmsGen->occupancy(), + _bootstrap_occupancy); + } + return true; + } + } + } + + // Otherwise, we start a collection cycle if either the perm gen or + // old gen want a collection cycle started. Each may use + // an appropriate criterion for making this decision. + // XXX We need to make sure that the gen expansion + // criterion dovetails well with this. + if (_cmsGen->shouldConcurrentCollect(initiatingOccupancy())) { + if (Verbose && PrintGCDetails) { + gclog_or_tty->print_cr("CMS old gen initiated"); + } + return true; + } + + if (cms_should_unload_classes() && + _permGen->shouldConcurrentCollect(initiatingOccupancy())) { + if (Verbose && PrintGCDetails) { + gclog_or_tty->print_cr("CMS perm gen initiated"); + } + return true; + } + + return false; +} + +// Clear _expansion_cause fields of constituent generations +void CMSCollector::clear_expansion_cause() { + _cmsGen->clear_expansion_cause(); + _permGen->clear_expansion_cause(); +} + +bool ConcurrentMarkSweepGeneration::shouldConcurrentCollect( + double initiatingOccupancy) { + // We should be conservative in starting a collection cycle. To + // start too eagerly runs the risk of collecting too often in the + // extreme. To collect too rarely falls back on full collections, + // which works, even if not optimum in terms of concurrent work. + // As a work around for too eagerly collecting, use the flag + // UseCMSInitiatingOccupancyOnly. This also has the advantage of + // giving the user an easily understandable way of controlling the + // collections. + // We want to start a new collection cycle if any of the following + // conditions hold: + // . our current occupancy exceeds the initiating occupancy, or + // . we recently needed to expand and have not since that expansion, + // collected, or + // . we are not using adaptive free lists and linear allocation is + // going to fail, or + // . (for old gen) incremental collection has already failed or + // may soon fail in the near future as we may not be able to absorb + // promotions. + assert_lock_strong(freelistLock()); + + if (occupancy() > initiatingOccupancy) { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print(" %s: collect because of occupancy %f / %f ", + short_name(), occupancy(), initiatingOccupancy); + } + return true; + } + if (UseCMSInitiatingOccupancyOnly) { + return false; + } + if (expansion_cause() == CMSExpansionCause::_satisfy_allocation) { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print(" %s: collect because expanded for allocation ", + short_name()); + } + return true; + } + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->collector_policy()->is_two_generation_policy(), + "You may want to check the correctness of the following"); + if (gch->incremental_collection_will_fail()) { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print(" %s: collect because incremental collection will fail ", + short_name()); + } + return true; + } + if (!_cmsSpace->adaptive_freelists() && + _cmsSpace->linearAllocationWouldFail()) { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print(" %s: collect because of linAB ", + short_name()); + } + return true; + } + return false; +} + +void ConcurrentMarkSweepGeneration::collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool tlab) +{ + collector()->collect(full, clear_all_soft_refs, size, tlab); +} + +void CMSCollector::collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool tlab) +{ + if (!UseCMSCollectionPassing && _collectorState > Idling) { + // For debugging purposes skip the collection if the state + // is not currently idle + if (TraceCMSState) { + gclog_or_tty->print_cr("Thread " INTPTR_FORMAT " skipped full:%d CMS state %d", + Thread::current(), full, _collectorState); + } + return; + } + + // The following "if" branch is present for defensive reasons. + // In the current uses of this interface, it can be replaced with: + // assert(!GC_locker.is_active(), "Can't be called otherwise"); + // But I am not placing that assert here to allow future + // generality in invoking this interface. + if (GC_locker::is_active()) { + // A consistency test for GC_locker + assert(GC_locker::needs_gc(), "Should have been set already"); + // Skip this foreground collection, instead + // expanding the heap if necessary. + // Need the free list locks for the call to free() in compute_new_size() + compute_new_size(); + return; + } + acquire_control_and_collect(full, clear_all_soft_refs); + _full_gcs_since_conc_gc++; + +} + +void CMSCollector::request_full_gc(unsigned int full_gc_count) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + unsigned int gc_count = gch->total_full_collections(); + if (gc_count == full_gc_count) { + MutexLockerEx y(CGC_lock, Mutex::_no_safepoint_check_flag); + _full_gc_requested = true; + CGC_lock->notify(); // nudge CMS thread + } +} + + +// The foreground and background collectors need to coordinate in order +// to make sure that they do not mutually interfere with CMS collections. +// When a background collection is active, +// the foreground collector may need to take over (preempt) and +// synchronously complete an ongoing collection. Depending on the +// frequency of the background collections and the heap usage +// of the application, this preemption can be seldom or frequent. +// There are only certain +// points in the background collection that the "collection-baton" +// can be passed to the foreground collector. +// +// The foreground collector will wait for the baton before +// starting any part of the collection. The foreground collector +// will only wait at one location. +// +// The background collector will yield the baton before starting a new +// phase of the collection (e.g., before initial marking, marking from roots, +// precleaning, final re-mark, sweep etc.) This is normally done at the head +// of the loop which switches the phases. The background collector does some +// of the phases (initial mark, final re-mark) with the world stopped. +// Because of locking involved in stopping the world, +// the foreground collector should not block waiting for the background +// collector when it is doing a stop-the-world phase. The background +// collector will yield the baton at an additional point just before +// it enters a stop-the-world phase. Once the world is stopped, the +// background collector checks the phase of the collection. If the +// phase has not changed, it proceeds with the collection. If the +// phase has changed, it skips that phase of the collection. See +// the comments on the use of the Heap_lock in collect_in_background(). +// +// Variable used in baton passing. +// _foregroundGCIsActive - Set to true by the foreground collector when +// it wants the baton. The foreground clears it when it has finished +// the collection. +// _foregroundGCShouldWait - Set to true by the background collector +// when it is running. The foreground collector waits while +// _foregroundGCShouldWait is true. +// CGC_lock - monitor used to protect access to the above variables +// and to notify the foreground and background collectors. +// _collectorState - current state of the CMS collection. +// +// The foreground collector +// acquires the CGC_lock +// sets _foregroundGCIsActive +// waits on the CGC_lock for _foregroundGCShouldWait to be false +// various locks acquired in preparation for the collection +// are released so as not to block the background collector +// that is in the midst of a collection +// proceeds with the collection +// clears _foregroundGCIsActive +// returns +// +// The background collector in a loop iterating on the phases of the +// collection +// acquires the CGC_lock +// sets _foregroundGCShouldWait +// if _foregroundGCIsActive is set +// clears _foregroundGCShouldWait, notifies _CGC_lock +// waits on _CGC_lock for _foregroundGCIsActive to become false +// and exits the loop. +// otherwise +// proceed with that phase of the collection +// if the phase is a stop-the-world phase, +// yield the baton once more just before enqueueing +// the stop-world CMS operation (executed by the VM thread). +// returns after all phases of the collection are done +// + +void CMSCollector::acquire_control_and_collect(bool full, + bool clear_all_soft_refs) { + assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); + assert(!Thread::current()->is_ConcurrentGC_thread(), + "shouldn't try to acquire control from self!"); + + // Start the protocol for acquiring control of the + // collection from the background collector (aka CMS thread). + assert(ConcurrentMarkSweepThread::vm_thread_has_cms_token(), + "VM thread should have CMS token"); + // Remember the possibly interrupted state of an ongoing + // concurrent collection + CollectorState first_state = _collectorState; + + // Signal to a possibly ongoing concurrent collection that + // we want to do a foreground collection. + _foregroundGCIsActive = true; + + // Disable incremental mode during a foreground collection. + ICMSDisabler icms_disabler; + + // release locks and wait for a notify from the background collector + // releasing the locks in only necessary for phases which + // do yields to improve the granularity of the collection. + assert_lock_strong(bitMapLock()); + // We need to lock the Free list lock for the space that we are + // currently collecting. + assert(haveFreelistLocks(), "Must be holding free list locks"); + bitMapLock()->unlock(); + releaseFreelistLocks(); + { + MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag); + if (_foregroundGCShouldWait) { + // We are going to be waiting for action for the CMS thread; + // it had better not be gone (for instance at shutdown)! + assert(ConcurrentMarkSweepThread::cmst() != NULL, + "CMS thread must be running"); + // Wait here until the background collector gives us the go-ahead + ConcurrentMarkSweepThread::clear_CMS_flag( + ConcurrentMarkSweepThread::CMS_vm_has_token); // release token + // Get a possibly blocked CMS thread going: + // Note that we set _foregroundGCIsActive true above, + // without protection of the CGC_lock. + CGC_lock->notify(); + assert(!ConcurrentMarkSweepThread::vm_thread_wants_cms_token(), + "Possible deadlock"); + while (_foregroundGCShouldWait) { + // wait for notification + CGC_lock->wait(Mutex::_no_safepoint_check_flag); + // Possibility of delay/starvation here, since CMS token does + // not know to give priority to VM thread? Actually, i think + // there wouldn't be any delay/starvation, but the proof of + // that "fact" (?) appears non-trivial. XXX 20011219YSR + } + ConcurrentMarkSweepThread::set_CMS_flag( + ConcurrentMarkSweepThread::CMS_vm_has_token); + } + } + // The CMS_token is already held. Get back the other locks. + assert(ConcurrentMarkSweepThread::vm_thread_has_cms_token(), + "VM thread should have CMS token"); + getFreelistLocks(); + bitMapLock()->lock_without_safepoint_check(); + if (TraceCMSState) { + gclog_or_tty->print_cr("CMS foreground collector has asked for control " + INTPTR_FORMAT " with first state %d", Thread::current(), first_state); + gclog_or_tty->print_cr(" gets control with state %d", _collectorState); + } + + // Check if we need to do a compaction, or if not, whether + // we need to start the mark-sweep from scratch. + bool should_compact = false; + bool should_start_over = false; + decide_foreground_collection_type(clear_all_soft_refs, + &should_compact, &should_start_over); + +NOT_PRODUCT( + if (RotateCMSCollectionTypes) { + if (_cmsGen->debug_collection_type() == + ConcurrentMarkSweepGeneration::MSC_foreground_collection_type) { + should_compact = true; + } else if (_cmsGen->debug_collection_type() == + ConcurrentMarkSweepGeneration::MS_foreground_collection_type) { + should_compact = false; + } + } +) + + if (PrintGCDetails && first_state > Idling) { + GCCause::Cause cause = GenCollectedHeap::heap()->gc_cause(); + if (GCCause::is_user_requested_gc(cause) || + GCCause::is_serviceability_requested_gc(cause)) { + gclog_or_tty->print(" (concurrent mode interrupted)"); + } else { + gclog_or_tty->print(" (concurrent mode failure)"); + } + } + + if (should_compact) { + // If the collection is being acquired from the background + // collector, there may be references on the discovered + // references lists that have NULL referents (being those + // that were concurrently cleared by a mutator) or + // that are no longer active (having been enqueued concurrently + // by the mutator). + // Scrub the list of those references because Mark-Sweep-Compact + // code assumes referents are not NULL and that all discovered + // Reference objects are active. + ref_processor()->clean_up_discovered_references(); + + do_compaction_work(clear_all_soft_refs); + + // Has the GC time limit been exceeded? + check_gc_time_limit(); + + } else { + do_mark_sweep_work(clear_all_soft_refs, first_state, + should_start_over); + } + // Reset the expansion cause, now that we just completed + // a collection cycle. + clear_expansion_cause(); + _foregroundGCIsActive = false; + return; +} + +void CMSCollector::check_gc_time_limit() { + + // Ignore explicit GC's. Exiting here does not set the flag and + // does not reset the count. Updating of the averages for system + // GC's is still controlled by UseAdaptiveSizePolicyWithSystemGC. + GCCause::Cause gc_cause = GenCollectedHeap::heap()->gc_cause(); + if (GCCause::is_user_requested_gc(gc_cause) || + GCCause::is_serviceability_requested_gc(gc_cause)) { + return; + } + + // Calculate the fraction of the CMS generation was freed during + // the last collection. + // Only consider the STW compacting cost for now. + // + // Note that the gc time limit test only works for the collections + // of the young gen + tenured gen and not for collections of the + // permanent gen. That is because the calculation of the space + // freed by the collection is the free space in the young gen + + // tenured gen. + + double fraction_free = + ((double)_cmsGen->free())/((double)_cmsGen->max_capacity()); + if ((100.0 * size_policy()->compacting_gc_cost()) > + ((double) GCTimeLimit) && + ((fraction_free * 100) < GCHeapFreeLimit)) { + size_policy()->inc_gc_time_limit_count(); + if (UseGCOverheadLimit && + (size_policy()->gc_time_limit_count() > + AdaptiveSizePolicyGCTimeLimitThreshold)) { + size_policy()->set_gc_time_limit_exceeded(true); + // Avoid consecutive OOM due to the gc time limit by resetting + // the counter. + size_policy()->reset_gc_time_limit_count(); + if (PrintGCDetails) { + gclog_or_tty->print_cr(" GC is exceeding overhead limit " + "of %d%%", GCTimeLimit); + } + } else { + if (PrintGCDetails) { + gclog_or_tty->print_cr(" GC would exceed overhead limit " + "of %d%%", GCTimeLimit); + } + } + } else { + size_policy()->reset_gc_time_limit_count(); + } +} + +// Resize the perm generation and the tenured generation +// after obtaining the free list locks for the +// two generations. +void CMSCollector::compute_new_size() { + assert_locked_or_safepoint(Heap_lock); + FreelistLocker z(this); + _permGen->compute_new_size(); + _cmsGen->compute_new_size(); +} + +// A work method used by foreground collection to determine +// what type of collection (compacting or not, continuing or fresh) +// it should do. +// NOTE: the intent is to make UseCMSCompactAtFullCollection +// and CMSCompactWhenClearAllSoftRefs the default in the future +// and do away with the flags after a suitable period. +void CMSCollector::decide_foreground_collection_type( + bool clear_all_soft_refs, bool* should_compact, + bool* should_start_over) { + // Normally, we'll compact only if the UseCMSCompactAtFullCollection + // flag is set, and we have either requested a System.gc() or + // the number of full gc's since the last concurrent cycle + // has exceeded the threshold set by CMSFullGCsBeforeCompaction, + // or if an incremental collection has failed + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->collector_policy()->is_two_generation_policy(), + "You may want to check the correctness of the following"); + // Inform cms gen if this was due to partial collection failing. + // The CMS gen may use this fact to determine its expansion policy. + if (gch->incremental_collection_will_fail()) { + assert(!_cmsGen->incremental_collection_failed(), + "Should have been noticed, reacted to and cleared"); + _cmsGen->set_incremental_collection_failed(); + } + *should_compact = + UseCMSCompactAtFullCollection && + ((_full_gcs_since_conc_gc >= CMSFullGCsBeforeCompaction) || + GCCause::is_user_requested_gc(gch->gc_cause()) || + gch->incremental_collection_will_fail()); + *should_start_over = false; + if (clear_all_soft_refs && !*should_compact) { + // We are about to do a last ditch collection attempt + // so it would normally make sense to do a compaction + // to reclaim as much space as possible. + if (CMSCompactWhenClearAllSoftRefs) { + // Default: The rationale is that in this case either + // we are past the final marking phase, in which case + // we'd have to start over, or so little has been done + // that there's little point in saving that work. Compaction + // appears to be the sensible choice in either case. + *should_compact = true; + } else { + // We have been asked to clear all soft refs, but not to + // compact. Make sure that we aren't past the final checkpoint + // phase, for that is where we process soft refs. If we are already + // past that phase, we'll need to redo the refs discovery phase and + // if necessary clear soft refs that weren't previously + // cleared. We do so by remembering the phase in which + // we came in, and if we are past the refs processing + // phase, we'll choose to just redo the mark-sweep + // collection from scratch. + if (_collectorState > FinalMarking) { + // We are past the refs processing phase; + // start over and do a fresh synchronous CMS cycle + _collectorState = Resetting; // skip to reset to start new cycle + reset(false /* == !asynch */); + *should_start_over = true; + } // else we can continue a possibly ongoing current cycle + } + } +} + +// A work method used by the foreground collector to do +// a mark-sweep-compact. +void CMSCollector::do_compaction_work(bool clear_all_soft_refs) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + TraceTime t("CMS:MSC ", PrintGCDetails && Verbose, true, gclog_or_tty); + if (PrintGC && Verbose && !(GCCause::is_user_requested_gc(gch->gc_cause()))) { + gclog_or_tty->print_cr("Compact ConcurrentMarkSweepGeneration after %d " + "collections passed to foreground collector", _full_gcs_since_conc_gc); + } + + // Sample collection interval time and reset for collection pause. + if (UseAdaptiveSizePolicy) { + size_policy()->msc_collection_begin(); + } + + // Temporarily widen the span of the weak reference processing to + // the entire heap. + MemRegion new_span(GenCollectedHeap::heap()->reserved_region()); + ReferenceProcessorSpanMutator x(ref_processor(), new_span); + + // Temporarily, clear the "is_alive_non_header" field of the + // reference processor. + ReferenceProcessorIsAliveMutator y(ref_processor(), NULL); + + // Temporarily make reference _processing_ single threaded (non-MT). + ReferenceProcessorMTProcMutator z(ref_processor(), false); + + // Temporarily make refs discovery atomic + ReferenceProcessorAtomicMutator w(ref_processor(), true); + + ref_processor()->set_enqueuing_is_done(false); + ref_processor()->enable_discovery(); + // If an asynchronous collection finishes, the _modUnionTable is + // all clear. If we are assuming the collection from an asynchronous + // collection, clear the _modUnionTable. + assert(_collectorState != Idling || _modUnionTable.isAllClear(), + "_modUnionTable should be clear if the baton was not passed"); + _modUnionTable.clear_all(); + + // We must adjust the allocation statistics being maintained + // in the free list space. We do so by reading and clearing + // the sweep timer and updating the block flux rate estimates below. + assert(_sweep_timer.is_active(), "We should never see the timer inactive"); + _sweep_timer.stop(); + // Note that we do not use this sample to update the _sweep_estimate. + _cmsGen->cmsSpace()->beginSweepFLCensus((float)(_sweep_timer.seconds()), + _sweep_estimate.padded_average()); + + GenMarkSweep::invoke_at_safepoint(_cmsGen->level(), + ref_processor(), clear_all_soft_refs); + #ifdef ASSERT + CompactibleFreeListSpace* cms_space = _cmsGen->cmsSpace(); + size_t free_size = cms_space->free(); + assert(free_size == + pointer_delta(cms_space->end(), cms_space->compaction_top()) + * HeapWordSize, + "All the free space should be compacted into one chunk at top"); + assert(cms_space->dictionary()->totalChunkSize( + debug_only(cms_space->freelistLock())) == 0 || + cms_space->totalSizeInIndexedFreeLists() == 0, + "All the free space should be in a single chunk"); + size_t num = cms_space->totalCount(); + assert((free_size == 0 && num == 0) || + (free_size > 0 && (num == 1 || num == 2)), + "There should be at most 2 free chunks after compaction"); + #endif // ASSERT + _collectorState = Resetting; + assert(_restart_addr == NULL, + "Should have been NULL'd before baton was passed"); + reset(false /* == !asynch */); + _cmsGen->reset_after_compaction(); + + if (verifying() && !cms_should_unload_classes()) { + perm_gen_verify_bit_map()->clear_all(); + } + + // Clear any data recorded in the PLAB chunk arrays. + if (_survivor_plab_array != NULL) { + reset_survivor_plab_arrays(); + } + + // Adjust the per-size allocation stats for the next epoch. + _cmsGen->cmsSpace()->endSweepFLCensus(sweepCount() /* fake */); + // Restart the "sweep timer" for next epoch. + _sweep_timer.reset(); + _sweep_timer.start(); + + // Sample collection pause time and reset for collection interval. + if (UseAdaptiveSizePolicy) { + size_policy()->msc_collection_end(gch->gc_cause()); + } + + // For a mark-sweep-compact, compute_new_size() will be called + // in the heap's do_collection() method. +} + +// A work method used by the foreground collector to do +// a mark-sweep, after taking over from a possibly on-going +// concurrent mark-sweep collection. +void CMSCollector::do_mark_sweep_work(bool clear_all_soft_refs, + CollectorState first_state, bool should_start_over) { + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("Pass concurrent collection to foreground " + "collector with count %d", + _full_gcs_since_conc_gc); + } + switch (_collectorState) { + case Idling: + if (first_state == Idling || should_start_over) { + // The background GC was not active, or should + // restarted from scratch; start the cycle. + _collectorState = InitialMarking; + } + // If first_state was not Idling, then a background GC + // was in progress and has now finished. No need to do it + // again. Leave the state as Idling. + break; + case Precleaning: + // In the foreground case don't do the precleaning since + // it is not done concurrently and there is extra work + // required. + _collectorState = FinalMarking; + } + if (PrintGCDetails && + (_collectorState > Idling || + !GCCause::is_user_requested_gc(GenCollectedHeap::heap()->gc_cause()))) { + gclog_or_tty->print(" (concurrent mode failure)"); + } + collect_in_foreground(clear_all_soft_refs); + + // For a mark-sweep, compute_new_size() will be called + // in the heap's do_collection() method. +} + + +void CMSCollector::getFreelistLocks() const { + // Get locks for all free lists in all generations that this + // collector is responsible for + _cmsGen->freelistLock()->lock_without_safepoint_check(); + _permGen->freelistLock()->lock_without_safepoint_check(); +} + +void CMSCollector::releaseFreelistLocks() const { + // Release locks for all free lists in all generations that this + // collector is responsible for + _cmsGen->freelistLock()->unlock(); + _permGen->freelistLock()->unlock(); +} + +bool CMSCollector::haveFreelistLocks() const { + // Check locks for all free lists in all generations that this + // collector is responsible for + assert_lock_strong(_cmsGen->freelistLock()); + assert_lock_strong(_permGen->freelistLock()); + PRODUCT_ONLY(ShouldNotReachHere()); + return true; +} + +// A utility class that is used by the CMS collector to +// temporarily "release" the foreground collector from its +// usual obligation to wait for the background collector to +// complete an ongoing phase before proceeding. +class ReleaseForegroundGC: public StackObj { + private: + CMSCollector* _c; + public: + ReleaseForegroundGC(CMSCollector* c) : _c(c) { + assert(_c->_foregroundGCShouldWait, "Else should not need to call"); + MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag); + // allow a potentially blocked foreground collector to proceed + _c->_foregroundGCShouldWait = false; + if (_c->_foregroundGCIsActive) { + CGC_lock->notify(); + } + assert(!ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "Possible deadlock"); + } + + ~ReleaseForegroundGC() { + assert(!_c->_foregroundGCShouldWait, "Usage protocol violation?"); + MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag); + _c->_foregroundGCShouldWait = true; + } +}; + +// There are separate collect_in_background and collect_in_foreground because of +// the different locking requirements of the background collector and the +// foreground collector. There was originally an attempt to share +// one "collect" method between the background collector and the foreground +// collector but the if-then-else required made it cleaner to have +// separate methods. +void CMSCollector::collect_in_background(bool clear_all_soft_refs) { + assert(Thread::current()->is_ConcurrentGC_thread(), + "A CMS asynchronous collection is only allowed on a CMS thread."); + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + { + bool safepoint_check = Mutex::_no_safepoint_check_flag; + MutexLockerEx hl(Heap_lock, safepoint_check); + MutexLockerEx x(CGC_lock, safepoint_check); + if (_foregroundGCIsActive || !UseAsyncConcMarkSweepGC) { + // The foreground collector is active or we're + // not using asynchronous collections. Skip this + // background collection. + assert(!_foregroundGCShouldWait, "Should be clear"); + return; + } else { + assert(_collectorState == Idling, "Should be idling before start."); + _collectorState = InitialMarking; + // Reset the expansion cause, now that we are about to begin + // a new cycle. + clear_expansion_cause(); + } + _unloaded_classes_last_cycle = cms_should_unload_classes(); // ... from last cycle + // This controls class unloading in response to an explicit gc request. + // If ExplicitGCInvokesConcurrentAndUnloadsClasses is set, then + // we will unload classes even if CMSClassUnloadingEnabled is not set. + // See CR 6541037 and related CRs. + _unload_classes = _full_gc_requested // ... for this cycle + && ExplicitGCInvokesConcurrentAndUnloadsClasses; + _full_gc_requested = false; // acks all outstanding full gc requests + // Signal that we are about to start a collection + gch->increment_total_full_collections(); // ... starting a collection cycle + _collection_count_start = gch->total_full_collections(); + } + + // Used for PrintGC + size_t prev_used; + if (PrintGC && Verbose) { + prev_used = _cmsGen->used(); // XXXPERM + } + + // The change of the collection state is normally done at this level; + // the exceptions are phases that are executed while the world is + // stopped. For those phases the change of state is done while the + // world is stopped. For baton passing purposes this allows the + // background collector to finish the phase and change state atomically. + // The foreground collector cannot wait on a phase that is done + // while the world is stopped because the foreground collector already + // has the world stopped and would deadlock. + while (_collectorState != Idling) { + if (TraceCMSState) { + gclog_or_tty->print_cr("Thread " INTPTR_FORMAT " in CMS state %d", + Thread::current(), _collectorState); + } + // The foreground collector + // holds the Heap_lock throughout its collection. + // holds the CMS token (but not the lock) + // except while it is waiting for the background collector to yield. + // + // The foreground collector should be blocked (not for long) + // if the background collector is about to start a phase + // executed with world stopped. If the background + // collector has already started such a phase, the + // foreground collector is blocked waiting for the + // Heap_lock. The stop-world phases (InitialMarking and FinalMarking) + // are executed in the VM thread. + // + // The locking order is + // PendingListLock (PLL) -- if applicable (FinalMarking) + // Heap_lock (both this & PLL locked in VM_CMS_Operation::prologue()) + // CMS token (claimed in + // stop_world_and_do() --> + // safepoint_synchronize() --> + // CMSThread::synchronize()) + + { + // Check if the FG collector wants us to yield. + CMSTokenSync x(true); // is cms thread + if (waitForForegroundGC()) { + // We yielded to a foreground GC, nothing more to be + // done this round. + assert(_foregroundGCShouldWait == false, "We set it to false in " + "waitForForegroundGC()"); + if (TraceCMSState) { + gclog_or_tty->print_cr("CMS Thread " INTPTR_FORMAT + " exiting collection CMS state %d", + Thread::current(), _collectorState); + } + return; + } else { + // The background collector can run but check to see if the + // foreground collector has done a collection while the + // background collector was waiting to get the CGC_lock + // above. If yes, break so that _foregroundGCShouldWait + // is cleared before returning. + if (_collectorState == Idling) { + break; + } + } + } + + assert(_foregroundGCShouldWait, "Foreground collector, if active, " + "should be waiting"); + + switch (_collectorState) { + case InitialMarking: + { + ReleaseForegroundGC x(this); + stats().record_cms_begin(); + + VM_CMS_Initial_Mark initial_mark_op(this); + VMThread::execute(&initial_mark_op); + } + // The collector state may be any legal state at this point + // since the background collector may have yielded to the + // foreground collector. + break; + case Marking: + // initial marking in checkpointRootsInitialWork has been completed + if (markFromRoots(true)) { // we were successful + assert(_collectorState == Precleaning, "Collector state should " + "have changed"); + } else { + assert(_foregroundGCIsActive, "Internal state inconsistency"); + } + break; + case Precleaning: + if (UseAdaptiveSizePolicy) { + size_policy()->concurrent_precleaning_begin(); + } + // marking from roots in markFromRoots has been completed + preclean(); + if (UseAdaptiveSizePolicy) { + size_policy()->concurrent_precleaning_end(); + } + assert(_collectorState == AbortablePreclean || + _collectorState == FinalMarking, + "Collector state should have changed"); + break; + case AbortablePreclean: + if (UseAdaptiveSizePolicy) { + size_policy()->concurrent_phases_resume(); + } + abortable_preclean(); + if (UseAdaptiveSizePolicy) { + size_policy()->concurrent_precleaning_end(); + } + assert(_collectorState == FinalMarking, "Collector state should " + "have changed"); + break; + case FinalMarking: + { + ReleaseForegroundGC x(this); + + VM_CMS_Final_Remark final_remark_op(this); + VMThread::execute(&final_remark_op); + } + assert(_foregroundGCShouldWait, "block post-condition"); + break; + case Sweeping: + if (UseAdaptiveSizePolicy) { + size_policy()->concurrent_sweeping_begin(); + } + // final marking in checkpointRootsFinal has been completed + sweep(true); + assert(_collectorState == Resizing, "Collector state change " + "to Resizing must be done under the free_list_lock"); + _full_gcs_since_conc_gc = 0; + + // Stop the timers for adaptive size policy for the concurrent phases + if (UseAdaptiveSizePolicy) { + size_policy()->concurrent_sweeping_end(); + size_policy()->concurrent_phases_end(gch->gc_cause(), + gch->prev_gen(_cmsGen)->capacity(), + _cmsGen->free()); + } + + case Resizing: { + // Sweeping has been completed... + // At this point the background collection has completed. + // Don't move the call to compute_new_size() down + // into code that might be executed if the background + // collection was preempted. + { + ReleaseForegroundGC x(this); // unblock FG collection + MutexLockerEx y(Heap_lock, Mutex::_no_safepoint_check_flag); + CMSTokenSync z(true); // not strictly needed. + if (_collectorState == Resizing) { + compute_new_size(); + _collectorState = Resetting; + } else { + assert(_collectorState == Idling, "The state should only change" + " because the foreground collector has finished the collection"); + } + } + break; + } + case Resetting: + // CMS heap resizing has been completed + reset(true); + assert(_collectorState == Idling, "Collector state should " + "have changed"); + stats().record_cms_end(); + // Don't move the concurrent_phases_end() and compute_new_size() + // calls to here because a preempted background collection + // has it's state set to "Resetting". + break; + case Idling: + default: + ShouldNotReachHere(); + break; + } + if (TraceCMSState) { + gclog_or_tty->print_cr(" Thread " INTPTR_FORMAT " done - next CMS state %d", + Thread::current(), _collectorState); + } + assert(_foregroundGCShouldWait, "block post-condition"); + } + + // Should this be in gc_epilogue? + collector_policy()->counters()->update_counters(); + + { + // Clear _foregroundGCShouldWait and, in the event that the + // foreground collector is waiting, notify it, before + // returning. + MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag); + _foregroundGCShouldWait = false; + if (_foregroundGCIsActive) { + CGC_lock->notify(); + } + assert(!ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "Possible deadlock"); + } + if (TraceCMSState) { + gclog_or_tty->print_cr("CMS Thread " INTPTR_FORMAT + " exiting collection CMS state %d", + Thread::current(), _collectorState); + } + if (PrintGC && Verbose) { + _cmsGen->print_heap_change(prev_used); + } +} + +void CMSCollector::collect_in_foreground(bool clear_all_soft_refs) { + assert(_foregroundGCIsActive && !_foregroundGCShouldWait, + "Foreground collector should be waiting, not executing"); + assert(Thread::current()->is_VM_thread(), "A foreground collection" + "may only be done by the VM Thread with the world stopped"); + assert(ConcurrentMarkSweepThread::vm_thread_has_cms_token(), + "VM thread should have CMS token"); + + NOT_PRODUCT(TraceTime t("CMS:MS (foreground) ", PrintGCDetails && Verbose, + true, gclog_or_tty);) + if (UseAdaptiveSizePolicy) { + size_policy()->ms_collection_begin(); + } + COMPILER2_PRESENT(DerivedPointerTableDeactivate dpt_deact); + + HandleMark hm; // Discard invalid handles created during verification + + if (VerifyBeforeGC && + GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { + Universe::verify(true); + } + + bool init_mark_was_synchronous = false; // until proven otherwise + while (_collectorState != Idling) { + if (TraceCMSState) { + gclog_or_tty->print_cr("Thread " INTPTR_FORMAT " in CMS state %d", + Thread::current(), _collectorState); + } + switch (_collectorState) { + case InitialMarking: + init_mark_was_synchronous = true; // fact to be exploited in re-mark + checkpointRootsInitial(false); + assert(_collectorState == Marking, "Collector state should have changed" + " within checkpointRootsInitial()"); + break; + case Marking: + // initial marking in checkpointRootsInitialWork has been completed + if (VerifyDuringGC && + GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { + gclog_or_tty->print("Verify before initial mark: "); + Universe::verify(true); + } + { + bool res = markFromRoots(false); + assert(res && _collectorState == FinalMarking, "Collector state should " + "have changed"); + break; + } + case FinalMarking: + if (VerifyDuringGC && + GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { + gclog_or_tty->print("Verify before re-mark: "); + Universe::verify(true); + } + checkpointRootsFinal(false, clear_all_soft_refs, + init_mark_was_synchronous); + assert(_collectorState == Sweeping, "Collector state should not " + "have changed within checkpointRootsFinal()"); + break; + case Sweeping: + // final marking in checkpointRootsFinal has been completed + if (VerifyDuringGC && + GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { + gclog_or_tty->print("Verify before sweep: "); + Universe::verify(true); + } + sweep(false); + assert(_collectorState == Resizing, "Incorrect state"); + break; + case Resizing: { + // Sweeping has been completed; the actual resize in this case + // is done separately; nothing to be done in this state. + _collectorState = Resetting; + break; + } + case Resetting: + // The heap has been resized. + if (VerifyDuringGC && + GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { + gclog_or_tty->print("Verify before reset: "); + Universe::verify(true); + } + reset(false); + assert(_collectorState == Idling, "Collector state should " + "have changed"); + break; + case Precleaning: + case AbortablePreclean: + // Elide the preclean phase + _collectorState = FinalMarking; + break; + default: + ShouldNotReachHere(); + } + if (TraceCMSState) { + gclog_or_tty->print_cr(" Thread " INTPTR_FORMAT " done - next CMS state %d", + Thread::current(), _collectorState); + } + } + + if (UseAdaptiveSizePolicy) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + size_policy()->ms_collection_end(gch->gc_cause()); + } + + if (VerifyAfterGC && + GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { + Universe::verify(true); + } + if (TraceCMSState) { + gclog_or_tty->print_cr("CMS Thread " INTPTR_FORMAT + " exiting collection CMS state %d", + Thread::current(), _collectorState); + } +} + +bool CMSCollector::waitForForegroundGC() { + bool res = false; + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "CMS thread should have CMS token"); + // Block the foreground collector until the + // background collectors decides whether to + // yield. + MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag); + _foregroundGCShouldWait = true; + if (_foregroundGCIsActive) { + // The background collector yields to the + // foreground collector and returns a value + // indicating that it has yielded. The foreground + // collector can proceed. + res = true; + _foregroundGCShouldWait = false; + ConcurrentMarkSweepThread::clear_CMS_flag( + ConcurrentMarkSweepThread::CMS_cms_has_token); + ConcurrentMarkSweepThread::set_CMS_flag( + ConcurrentMarkSweepThread::CMS_cms_wants_token); + // Get a possibly blocked foreground thread going + CGC_lock->notify(); + if (TraceCMSState) { + gclog_or_tty->print_cr("CMS Thread " INTPTR_FORMAT " waiting at CMS state %d", + Thread::current(), _collectorState); + } + while (_foregroundGCIsActive) { + CGC_lock->wait(Mutex::_no_safepoint_check_flag); + } + ConcurrentMarkSweepThread::set_CMS_flag( + ConcurrentMarkSweepThread::CMS_cms_has_token); + ConcurrentMarkSweepThread::clear_CMS_flag( + ConcurrentMarkSweepThread::CMS_cms_wants_token); + } + if (TraceCMSState) { + gclog_or_tty->print_cr("CMS Thread " INTPTR_FORMAT " continuing at CMS state %d", + Thread::current(), _collectorState); + } + return res; +} + +// Because of the need to lock the free lists and other structures in +// the collector, common to all the generations that the collector is +// collecting, we need the gc_prologues of individual CMS generations +// delegate to their collector. It may have been simpler had the +// current infrastructure allowed one to call a prologue on a +// collector. In the absence of that we have the generation's +// prologue delegate to the collector, which delegates back +// some "local" work to a worker method in the individual generations +// that it's responsible for collecting, while itself doing any +// work common to all generations it's responsible for. A similar +// comment applies to the gc_epilogue()'s. +// The role of the varaible _between_prologue_and_epilogue is to +// enforce the invocation protocol. +void CMSCollector::gc_prologue(bool full) { + // Call gc_prologue_work() for each CMSGen and PermGen that + // we are responsible for. + + // The following locking discipline assumes that we are only called + // when the world is stopped. + assert(SafepointSynchronize::is_at_safepoint(), "world is stopped assumption"); + + // The CMSCollector prologue must call the gc_prologues for the + // "generations" (including PermGen if any) that it's responsible + // for. + + assert( Thread::current()->is_VM_thread() + || ( CMSScavengeBeforeRemark + && Thread::current()->is_ConcurrentGC_thread()), + "Incorrect thread type for prologue execution"); + + if (_between_prologue_and_epilogue) { + // We have already been invoked; this is a gc_prologue delegation + // from yet another CMS generation that we are responsible for, just + // ignore it since all relevant work has already been done. + return; + } + + // set a bit saying prologue has been called; cleared in epilogue + _between_prologue_and_epilogue = true; + // Claim locks for common data structures, then call gc_prologue_work() + // for each CMSGen and PermGen that we are responsible for. + + getFreelistLocks(); // gets free list locks on constituent spaces + bitMapLock()->lock_without_safepoint_check(); + + // Should call gc_prologue_work() for all cms gens we are responsible for + bool registerClosure = _collectorState >= Marking + && _collectorState < Sweeping; + ModUnionClosure* muc = ParallelGCThreads > 0 ? &_modUnionClosurePar + : &_modUnionClosure; + _cmsGen->gc_prologue_work(full, registerClosure, muc); + _permGen->gc_prologue_work(full, registerClosure, muc); + + if (!full) { + stats().record_gc0_begin(); + } +} + +void ConcurrentMarkSweepGeneration::gc_prologue(bool full) { + // Delegate to CMScollector which knows how to coordinate between + // this and any other CMS generations that it is responsible for + // collecting. + collector()->gc_prologue(full); +} + +// This is a "private" interface for use by this generation's CMSCollector. +// Not to be called directly by any other entity (for instance, +// GenCollectedHeap, which calls the "public" gc_prologue method above). +void ConcurrentMarkSweepGeneration::gc_prologue_work(bool full, + bool registerClosure, ModUnionClosure* modUnionClosure) { + assert(!incremental_collection_failed(), "Shouldn't be set yet"); + assert(cmsSpace()->preconsumptionDirtyCardClosure() == NULL, + "Should be NULL"); + if (registerClosure) { + cmsSpace()->setPreconsumptionDirtyCardClosure(modUnionClosure); + } + cmsSpace()->gc_prologue(); + // Clear stat counters + NOT_PRODUCT( + assert(_numObjectsPromoted == 0, "check"); + assert(_numWordsPromoted == 0, "check"); + if (Verbose && PrintGC) { + gclog_or_tty->print("Allocated "SIZE_FORMAT" objects, " + SIZE_FORMAT" bytes concurrently", + _numObjectsAllocated, _numWordsAllocated*sizeof(HeapWord)); + } + _numObjectsAllocated = 0; + _numWordsAllocated = 0; + ) +} + +void CMSCollector::gc_epilogue(bool full) { + // The following locking discipline assumes that we are only called + // when the world is stopped. + assert(SafepointSynchronize::is_at_safepoint(), + "world is stopped assumption"); + + // Currently the CMS epilogue (see CompactibleFreeListSpace) merely checks + // if linear allocation blocks need to be appropriately marked to allow the + // the blocks to be parsable. We also check here whether we need to nudge the + // CMS collector thread to start a new cycle (if it's not already active). + assert( Thread::current()->is_VM_thread() + || ( CMSScavengeBeforeRemark + && Thread::current()->is_ConcurrentGC_thread()), + "Incorrect thread type for epilogue execution"); + + if (!_between_prologue_and_epilogue) { + // We have already been invoked; this is a gc_epilogue delegation + // from yet another CMS generation that we are responsible for, just + // ignore it since all relevant work has already been done. + return; + } + assert(haveFreelistLocks(), "must have freelist locks"); + assert_lock_strong(bitMapLock()); + + _cmsGen->gc_epilogue_work(full); + _permGen->gc_epilogue_work(full); + + if (_collectorState == AbortablePreclean || _collectorState == Precleaning) { + // in case sampling was not already enabled, enable it + _start_sampling = true; + } + // reset _eden_chunk_array so sampling starts afresh + _eden_chunk_index = 0; + + size_t cms_used = _cmsGen->cmsSpace()->used(); + size_t perm_used = _permGen->cmsSpace()->used(); + + // update performance counters - this uses a special version of + // update_counters() that allows the utilization to be passed as a + // parameter, avoiding multiple calls to used(). + // + _cmsGen->update_counters(cms_used); + _permGen->update_counters(perm_used); + + if (CMSIncrementalMode) { + icms_update_allocation_limits(); + } + + bitMapLock()->unlock(); + releaseFreelistLocks(); + + _between_prologue_and_epilogue = false; // ready for next cycle +} + +void ConcurrentMarkSweepGeneration::gc_epilogue(bool full) { + collector()->gc_epilogue(full); + + // Also reset promotion tracking in par gc thread states. + if (ParallelGCThreads > 0) { + for (uint i = 0; i < ParallelGCThreads; i++) { + _par_gc_thread_states[i]->promo.stopTrackingPromotions(); + } + } +} + +void ConcurrentMarkSweepGeneration::gc_epilogue_work(bool full) { + assert(!incremental_collection_failed(), "Should have been cleared"); + cmsSpace()->setPreconsumptionDirtyCardClosure(NULL); + cmsSpace()->gc_epilogue(); + // Print stat counters + NOT_PRODUCT( + assert(_numObjectsAllocated == 0, "check"); + assert(_numWordsAllocated == 0, "check"); + if (Verbose && PrintGC) { + gclog_or_tty->print("Promoted "SIZE_FORMAT" objects, " + SIZE_FORMAT" bytes", + _numObjectsPromoted, _numWordsPromoted*sizeof(HeapWord)); + } + _numObjectsPromoted = 0; + _numWordsPromoted = 0; + ) + + if (PrintGC && Verbose) { + // Call down the chain in contiguous_available needs the freelistLock + // so print this out before releasing the freeListLock. + gclog_or_tty->print(" Contiguous available "SIZE_FORMAT" bytes ", + contiguous_available()); + } +} + +#ifndef PRODUCT +bool CMSCollector::have_cms_token() { + Thread* thr = Thread::current(); + if (thr->is_VM_thread()) { + return ConcurrentMarkSweepThread::vm_thread_has_cms_token(); + } else if (thr->is_ConcurrentGC_thread()) { + return ConcurrentMarkSweepThread::cms_thread_has_cms_token(); + } else if (thr->is_GC_task_thread()) { + return ConcurrentMarkSweepThread::vm_thread_has_cms_token() && + ParGCRareEvent_lock->owned_by_self(); + } + return false; +} +#endif + +// Check reachability of the given heap address in CMS generation, +// treating all other generations as roots. +bool CMSCollector::is_cms_reachable(HeapWord* addr) { + // We could "guarantee" below, rather than assert, but i'll + // leave these as "asserts" so that an adventurous debugger + // could try this in the product build provided some subset of + // the conditions were met, provided they were intersted in the + // results and knew that the computation below wouldn't interfere + // with other concurrent computations mutating the structures + // being read or written. + assert(SafepointSynchronize::is_at_safepoint(), + "Else mutations in object graph will make answer suspect"); + assert(have_cms_token(), "Should hold cms token"); + assert(haveFreelistLocks(), "must hold free list locks"); + assert_lock_strong(bitMapLock()); + + // Clear the marking bit map array before starting, but, just + // for kicks, first report if the given address is already marked + gclog_or_tty->print_cr("Start: Address 0x%x is%s marked", addr, + _markBitMap.isMarked(addr) ? "" : " not"); + + if (verify_after_remark()) { + MutexLockerEx x(verification_mark_bm()->lock(), Mutex::_no_safepoint_check_flag); + bool result = verification_mark_bm()->isMarked(addr); + gclog_or_tty->print_cr("TransitiveMark: Address 0x%x %s marked", addr, + result ? "IS" : "is NOT"); + return result; + } else { + gclog_or_tty->print_cr("Could not compute result"); + return false; + } +} + +//////////////////////////////////////////////////////// +// CMS Verification Support +//////////////////////////////////////////////////////// +// Following the remark phase, the following invariant +// should hold -- each object in the CMS heap which is +// marked in markBitMap() should be marked in the verification_mark_bm(). + +class VerifyMarkedClosure: public BitMapClosure { + CMSBitMap* _marks; + bool _failed; + + public: + VerifyMarkedClosure(CMSBitMap* bm): _marks(bm), _failed(false) {} + + void do_bit(size_t offset) { + HeapWord* addr = _marks->offsetToHeapWord(offset); + if (!_marks->isMarked(addr)) { + oop(addr)->print(); + gclog_or_tty->print_cr(" ("INTPTR_FORMAT" should have been marked)", addr); + _failed = true; + } + } + + bool failed() { return _failed; } +}; + +bool CMSCollector::verify_after_remark() { + gclog_or_tty->print(" [Verifying CMS Marking... "); + MutexLockerEx ml(verification_mark_bm()->lock(), Mutex::_no_safepoint_check_flag); + static bool init = false; + + assert(SafepointSynchronize::is_at_safepoint(), + "Else mutations in object graph will make answer suspect"); + assert(have_cms_token(), + "Else there may be mutual interference in use of " + " verification data structures"); + assert(_collectorState > Marking && _collectorState <= Sweeping, + "Else marking info checked here may be obsolete"); + assert(haveFreelistLocks(), "must hold free list locks"); + assert_lock_strong(bitMapLock()); + + + // Allocate marking bit map if not already allocated + if (!init) { // first time + if (!verification_mark_bm()->allocate(_span)) { + return false; + } + init = true; + } + + assert(verification_mark_stack()->isEmpty(), "Should be empty"); + + // Turn off refs discovery -- so we will be tracing through refs. + // This is as intended, because by this time + // GC must already have cleared any refs that need to be cleared, + // and traced those that need to be marked; moreover, + // the marking done here is not going to intefere in any + // way with the marking information used by GC. + NoRefDiscovery no_discovery(ref_processor()); + + COMPILER2_PRESENT(DerivedPointerTableDeactivate dpt_deact;) + + // Clear any marks from a previous round + verification_mark_bm()->clear_all(); + assert(verification_mark_stack()->isEmpty(), "markStack should be empty"); + assert(overflow_list_is_empty(), "overflow list should be empty"); + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + gch->ensure_parsability(false); // fill TLABs, but no need to retire them + // Update the saved marks which may affect the root scans. + gch->save_marks(); + + if (CMSRemarkVerifyVariant == 1) { + // In this first variant of verification, we complete + // all marking, then check if the new marks-verctor is + // a subset of the CMS marks-vector. + verify_after_remark_work_1(); + } else if (CMSRemarkVerifyVariant == 2) { + // In this second variant of verification, we flag an error + // (i.e. an object reachable in the new marks-vector not reachable + // in the CMS marks-vector) immediately, also indicating the + // identify of an object (A) that references the unmarked object (B) -- + // presumably, a mutation to A failed to be picked up by preclean/remark? + verify_after_remark_work_2(); + } else { + warning("Unrecognized value %d for CMSRemarkVerifyVariant", + CMSRemarkVerifyVariant); + } + gclog_or_tty->print(" done] "); + return true; +} + +void CMSCollector::verify_after_remark_work_1() { + ResourceMark rm; + HandleMark hm; + GenCollectedHeap* gch = GenCollectedHeap::heap(); + + // Mark from roots one level into CMS + MarkRefsIntoClosure notOlder(_span, verification_mark_bm(), true /* nmethods */); + gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. + + gch->gen_process_strong_roots(_cmsGen->level(), + true, // younger gens are roots + true, // collecting perm gen + SharedHeap::ScanningOption(roots_scanning_options()), + NULL, ¬Older); + + // Now mark from the roots + assert(_revisitStack.isEmpty(), "Should be empty"); + MarkFromRootsClosure markFromRootsClosure(this, _span, + verification_mark_bm(), verification_mark_stack(), &_revisitStack, + false /* don't yield */, true /* verifying */); + assert(_restart_addr == NULL, "Expected pre-condition"); + verification_mark_bm()->iterate(&markFromRootsClosure); + while (_restart_addr != NULL) { + // Deal with stack overflow: by restarting at the indicated + // address. + HeapWord* ra = _restart_addr; + markFromRootsClosure.reset(ra); + _restart_addr = NULL; + verification_mark_bm()->iterate(&markFromRootsClosure, ra, _span.end()); + } + assert(verification_mark_stack()->isEmpty(), "Should have been drained"); + verify_work_stacks_empty(); + // Should reset the revisit stack above, since no class tree + // surgery is forthcoming. + _revisitStack.reset(); // throwing away all contents + + // Marking completed -- now verify that each bit marked in + // verification_mark_bm() is also marked in markBitMap(); flag all + // errors by printing corresponding objects. + VerifyMarkedClosure vcl(markBitMap()); + verification_mark_bm()->iterate(&vcl); + if (vcl.failed()) { + gclog_or_tty->print("Verification failed"); + Universe::heap()->print(); + fatal(" ... aborting"); + } +} + +void CMSCollector::verify_after_remark_work_2() { + ResourceMark rm; + HandleMark hm; + GenCollectedHeap* gch = GenCollectedHeap::heap(); + + // Mark from roots one level into CMS + MarkRefsIntoVerifyClosure notOlder(_span, verification_mark_bm(), + markBitMap(), true /* nmethods */); + gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. + gch->gen_process_strong_roots(_cmsGen->level(), + true, // younger gens are roots + true, // collecting perm gen + SharedHeap::ScanningOption(roots_scanning_options()), + NULL, ¬Older); + + // Now mark from the roots + assert(_revisitStack.isEmpty(), "Should be empty"); + MarkFromRootsVerifyClosure markFromRootsClosure(this, _span, + verification_mark_bm(), markBitMap(), verification_mark_stack()); + assert(_restart_addr == NULL, "Expected pre-condition"); + verification_mark_bm()->iterate(&markFromRootsClosure); + while (_restart_addr != NULL) { + // Deal with stack overflow: by restarting at the indicated + // address. + HeapWord* ra = _restart_addr; + markFromRootsClosure.reset(ra); + _restart_addr = NULL; + verification_mark_bm()->iterate(&markFromRootsClosure, ra, _span.end()); + } + assert(verification_mark_stack()->isEmpty(), "Should have been drained"); + verify_work_stacks_empty(); + // Should reset the revisit stack above, since no class tree + // surgery is forthcoming. + _revisitStack.reset(); // throwing away all contents + + // Marking completed -- now verify that each bit marked in + // verification_mark_bm() is also marked in markBitMap(); flag all + // errors by printing corresponding objects. + VerifyMarkedClosure vcl(markBitMap()); + verification_mark_bm()->iterate(&vcl); + assert(!vcl.failed(), "Else verification above should not have succeeded"); +} + +void ConcurrentMarkSweepGeneration::save_marks() { + // delegate to CMS space + cmsSpace()->save_marks(); + for (uint i = 0; i < ParallelGCThreads; i++) { + _par_gc_thread_states[i]->promo.startTrackingPromotions(); + } +} + +bool ConcurrentMarkSweepGeneration::no_allocs_since_save_marks() { + return cmsSpace()->no_allocs_since_save_marks(); +} + +#define CMS_SINCE_SAVE_MARKS_DEFN(OopClosureType, nv_suffix) \ + \ +void ConcurrentMarkSweepGeneration:: \ +oop_since_save_marks_iterate##nv_suffix(OopClosureType* cl) { \ + cl->set_generation(this); \ + cmsSpace()->oop_since_save_marks_iterate##nv_suffix(cl); \ + cl->reset_generation(); \ + save_marks(); \ +} + +ALL_SINCE_SAVE_MARKS_CLOSURES(CMS_SINCE_SAVE_MARKS_DEFN) + +void +ConcurrentMarkSweepGeneration::object_iterate_since_last_GC(ObjectClosure* blk) +{ + // Not currently implemented; need to do the following. -- ysr. + // dld -- I think that is used for some sort of allocation profiler. So it + // really means the objects allocated by the mutator since the last + // GC. We could potentially implement this cheaply by recording only + // the direct allocations in a side data structure. + // + // I think we probably ought not to be required to support these + // iterations at any arbitrary point; I think there ought to be some + // call to enable/disable allocation profiling in a generation/space, + // and the iterator ought to return the objects allocated in the + // gen/space since the enable call, or the last iterator call (which + // will probably be at a GC.) That way, for gens like CM&S that would + // require some extra data structure to support this, we only pay the + // cost when it's in use... + cmsSpace()->object_iterate_since_last_GC(blk); +} + +void +ConcurrentMarkSweepGeneration::younger_refs_iterate(OopsInGenClosure* cl) { + cl->set_generation(this); + younger_refs_in_space_iterate(_cmsSpace, cl); + cl->reset_generation(); +} + +void +ConcurrentMarkSweepGeneration::oop_iterate(MemRegion mr, OopClosure* cl) { + if (freelistLock()->owned_by_self()) { + Generation::oop_iterate(mr, cl); + } else { + MutexLockerEx x(freelistLock(), Mutex::_no_safepoint_check_flag); + Generation::oop_iterate(mr, cl); + } +} + +void +ConcurrentMarkSweepGeneration::oop_iterate(OopClosure* cl) { + if (freelistLock()->owned_by_self()) { + Generation::oop_iterate(cl); + } else { + MutexLockerEx x(freelistLock(), Mutex::_no_safepoint_check_flag); + Generation::oop_iterate(cl); + } +} + +void +ConcurrentMarkSweepGeneration::object_iterate(ObjectClosure* cl) { + if (freelistLock()->owned_by_self()) { + Generation::object_iterate(cl); + } else { + MutexLockerEx x(freelistLock(), Mutex::_no_safepoint_check_flag); + Generation::object_iterate(cl); + } +} + +void +ConcurrentMarkSweepGeneration::pre_adjust_pointers() { +} + +void +ConcurrentMarkSweepGeneration::post_compact() { +} + +void +ConcurrentMarkSweepGeneration::prepare_for_verify() { + // Fix the linear allocation blocks to look like free blocks. + + // Locks are normally acquired/released in gc_prologue/gc_epilogue, but those + // are not called when the heap is verified during universe initialization and + // at vm shutdown. + if (freelistLock()->owned_by_self()) { + cmsSpace()->prepare_for_verify(); + } else { + MutexLockerEx fll(freelistLock(), Mutex::_no_safepoint_check_flag); + cmsSpace()->prepare_for_verify(); + } +} + +void +ConcurrentMarkSweepGeneration::verify(bool allow_dirty /* ignored */) { + // Locks are normally acquired/released in gc_prologue/gc_epilogue, but those + // are not called when the heap is verified during universe initialization and + // at vm shutdown. + if (freelistLock()->owned_by_self()) { + cmsSpace()->verify(false /* ignored */); + } else { + MutexLockerEx fll(freelistLock(), Mutex::_no_safepoint_check_flag); + cmsSpace()->verify(false /* ignored */); + } +} + +void CMSCollector::verify(bool allow_dirty /* ignored */) { + _cmsGen->verify(allow_dirty); + _permGen->verify(allow_dirty); +} + +#ifndef PRODUCT +bool CMSCollector::overflow_list_is_empty() const { + assert(_num_par_pushes >= 0, "Inconsistency"); + if (_overflow_list == NULL) { + assert(_num_par_pushes == 0, "Inconsistency"); + } + return _overflow_list == NULL; +} + +// The methods verify_work_stacks_empty() and verify_overflow_empty() +// merely consolidate assertion checks that appear to occur together frequently. +void CMSCollector::verify_work_stacks_empty() const { + assert(_markStack.isEmpty(), "Marking stack should be empty"); + assert(overflow_list_is_empty(), "Overflow list should be empty"); +} + +void CMSCollector::verify_overflow_empty() const { + assert(overflow_list_is_empty(), "Overflow list should be empty"); + assert(no_preserved_marks(), "No preserved marks"); +} +#endif // PRODUCT + +void CMSCollector::setup_cms_unloading_and_verification_state() { + const bool should_verify = VerifyBeforeGC || VerifyAfterGC || VerifyDuringGC + || VerifyBeforeExit; + const int rso = SharedHeap::SO_Symbols | SharedHeap::SO_Strings + | SharedHeap::SO_CodeCache; + + if (cms_should_unload_classes()) { // Should unload classes this cycle + remove_root_scanning_option(rso); // Shrink the root set appropriately + set_verifying(should_verify); // Set verification state for this cycle + return; // Nothing else needs to be done at this time + } + + // Not unloading classes this cycle + assert(!cms_should_unload_classes(), "Inconsitency!"); + if ((!verifying() || cms_unloaded_classes_last_cycle()) && should_verify) { + // We were not verifying, or we _were_ unloading classes in the last cycle, + // AND some verification options are enabled this cycle; in this case, + // we must make sure that the deadness map is allocated if not already so, + // and cleared (if already allocated previously -- + // CMSBitMap::sizeInBits() is used to determine if it's allocated). + if (perm_gen_verify_bit_map()->sizeInBits() == 0) { + if (!perm_gen_verify_bit_map()->allocate(_permGen->reserved())) { + warning("Failed to allocate permanent generation verification CMS Bit Map;\n" + "permanent generation verification disabled"); + return; // Note that we leave verification disabled, so we'll retry this + // allocation next cycle. We _could_ remember this failure + // and skip further attempts and permanently disable verification + // attempts if that is considered more desirable. + } + assert(perm_gen_verify_bit_map()->covers(_permGen->reserved()), + "_perm_gen_ver_bit_map inconsistency?"); + } else { + perm_gen_verify_bit_map()->clear_all(); + } + // Include symbols, strings and code cache elements to prevent their resurrection. + add_root_scanning_option(rso); + set_verifying(true); + } else if (verifying() && !should_verify) { + // We were verifying, but some verification flags got disabled. + set_verifying(false); + // Exclude symbols, strings and code cache elements from root scanning to + // reduce IM and RM pauses. + remove_root_scanning_option(rso); + } +} + + +#ifndef PRODUCT +HeapWord* CMSCollector::block_start(const void* p) const { + const HeapWord* addr = (HeapWord*)p; + if (_span.contains(p)) { + if (_cmsGen->cmsSpace()->is_in_reserved(addr)) { + return _cmsGen->cmsSpace()->block_start(p); + } else { + assert(_permGen->cmsSpace()->is_in_reserved(addr), + "Inconsistent _span?"); + return _permGen->cmsSpace()->block_start(p); + } + } + return NULL; +} +#endif + +HeapWord* +ConcurrentMarkSweepGeneration::expand_and_allocate(size_t word_size, + bool tlab, + bool parallel) { + assert(!tlab, "Can't deal with TLAB allocation"); + MutexLockerEx x(freelistLock(), Mutex::_no_safepoint_check_flag); + expand(word_size*HeapWordSize, MinHeapDeltaBytes, + CMSExpansionCause::_satisfy_allocation); + if (GCExpandToAllocateDelayMillis > 0) { + os::sleep(Thread::current(), GCExpandToAllocateDelayMillis, false); + } + size_t adj_word_sz = CompactibleFreeListSpace::adjustObjectSize(word_size); + if (parallel) { + return cmsSpace()->par_allocate(adj_word_sz); + } else { + return cmsSpace()->allocate(adj_word_sz); + } +} + +// YSR: All of this generation expansion/shrinking stuff is an exact copy of +// OneContigSpaceCardGeneration, which makes me wonder if we should move this +// to CardGeneration and share it... +void ConcurrentMarkSweepGeneration::expand(size_t bytes, size_t expand_bytes, + CMSExpansionCause::Cause cause) +{ + assert_locked_or_safepoint(Heap_lock); + + size_t aligned_bytes = ReservedSpace::page_align_size_up(bytes); + size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes); + bool success = false; + if (aligned_expand_bytes > aligned_bytes) { + success = grow_by(aligned_expand_bytes); + } + if (!success) { + success = grow_by(aligned_bytes); + } + if (!success) { + size_t remaining_bytes = _virtual_space.uncommitted_size(); + if (remaining_bytes > 0) { + success = grow_by(remaining_bytes); + } + } + if (GC_locker::is_active()) { + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("Garbage collection disabled, expanded heap instead"); + } + } + // remember why we expanded; this information is used + // by shouldConcurrentCollect() when making decisions on whether to start + // a new CMS cycle. + if (success) { + set_expansion_cause(cause); + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("Expanded CMS gen for %s", + CMSExpansionCause::to_string(cause)); + } + } +} + +HeapWord* ConcurrentMarkSweepGeneration::expand_and_par_lab_allocate(CMSParGCThreadState* ps, size_t word_sz) { + HeapWord* res = NULL; + MutexLocker x(ParGCRareEvent_lock); + while (true) { + // Expansion by some other thread might make alloc OK now: + res = ps->lab.alloc(word_sz); + if (res != NULL) return res; + // If there's not enough expansion space available, give up. + if (_virtual_space.uncommitted_size() < (word_sz * HeapWordSize)) { + return NULL; + } + // Otherwise, we try expansion. + expand(word_sz*HeapWordSize, MinHeapDeltaBytes, + CMSExpansionCause::_allocate_par_lab); + // Now go around the loop and try alloc again; + // A competing par_promote might beat us to the expansion space, + // so we may go around the loop again if promotion fails agaion. + if (GCExpandToAllocateDelayMillis > 0) { + os::sleep(Thread::current(), GCExpandToAllocateDelayMillis, false); + } + } +} + + +bool ConcurrentMarkSweepGeneration::expand_and_ensure_spooling_space( + PromotionInfo* promo) { + MutexLocker x(ParGCRareEvent_lock); + size_t refill_size_bytes = promo->refillSize() * HeapWordSize; + while (true) { + // Expansion by some other thread might make alloc OK now: + if (promo->ensure_spooling_space()) { + assert(promo->has_spooling_space(), + "Post-condition of successful ensure_spooling_space()"); + return true; + } + // If there's not enough expansion space available, give up. + if (_virtual_space.uncommitted_size() < refill_size_bytes) { + return false; + } + // Otherwise, we try expansion. + expand(refill_size_bytes, MinHeapDeltaBytes, + CMSExpansionCause::_allocate_par_spooling_space); + // Now go around the loop and try alloc again; + // A competing allocation might beat us to the expansion space, + // so we may go around the loop again if allocation fails again. + if (GCExpandToAllocateDelayMillis > 0) { + os::sleep(Thread::current(), GCExpandToAllocateDelayMillis, false); + } + } +} + + + +void ConcurrentMarkSweepGeneration::shrink(size_t bytes) { + assert_locked_or_safepoint(Heap_lock); + size_t size = ReservedSpace::page_align_size_down(bytes); + if (size > 0) { + shrink_by(size); + } +} + +bool ConcurrentMarkSweepGeneration::grow_by(size_t bytes) { + assert_locked_or_safepoint(Heap_lock); + bool result = _virtual_space.expand_by(bytes); + if (result) { + HeapWord* old_end = _cmsSpace->end(); + size_t new_word_size = + heap_word_size(_virtual_space.committed_size()); + MemRegion mr(_cmsSpace->bottom(), new_word_size); + _bts->resize(new_word_size); // resize the block offset shared array + Universe::heap()->barrier_set()->resize_covered_region(mr); + // Hmmmm... why doesn't CFLS::set_end verify locking? + // This is quite ugly; FIX ME XXX + _cmsSpace->assert_locked(); + _cmsSpace->set_end((HeapWord*)_virtual_space.high()); + + // update the space and generation capacity counters + if (UsePerfData) { + _space_counters->update_capacity(); + _gen_counters->update_all(); + } + + if (Verbose && PrintGC) { + size_t new_mem_size = _virtual_space.committed_size(); + size_t old_mem_size = new_mem_size - bytes; + gclog_or_tty->print_cr("Expanding %s from %ldK by %ldK to %ldK", + name(), old_mem_size/K, bytes/K, new_mem_size/K); + } + } + return result; +} + +bool ConcurrentMarkSweepGeneration::grow_to_reserved() { + assert_locked_or_safepoint(Heap_lock); + bool success = true; + const size_t remaining_bytes = _virtual_space.uncommitted_size(); + if (remaining_bytes > 0) { + success = grow_by(remaining_bytes); + DEBUG_ONLY(if (!success) warning("grow to reserved failed");) + } + return success; +} + +void ConcurrentMarkSweepGeneration::shrink_by(size_t bytes) { + assert_locked_or_safepoint(Heap_lock); + assert_lock_strong(freelistLock()); + // XXX Fix when compaction is implemented. + warning("Shrinking of CMS not yet implemented"); + return; +} + + +// Simple ctor/dtor wrapper for accounting & timer chores around concurrent +// phases. +class CMSPhaseAccounting: public StackObj { + public: + CMSPhaseAccounting(CMSCollector *collector, + const char *phase, + bool print_cr = true); + ~CMSPhaseAccounting(); + + private: + CMSCollector *_collector; + const char *_phase; + elapsedTimer _wallclock; + bool _print_cr; + + public: + // Not MT-safe; so do not pass around these StackObj's + // where they may be accessed by other threads. + jlong wallclock_millis() { + assert(_wallclock.is_active(), "Wall clock should not stop"); + _wallclock.stop(); // to record time + jlong ret = _wallclock.milliseconds(); + _wallclock.start(); // restart + return ret; + } +}; + +CMSPhaseAccounting::CMSPhaseAccounting(CMSCollector *collector, + const char *phase, + bool print_cr) : + _collector(collector), _phase(phase), _print_cr(print_cr) { + + if (PrintCMSStatistics != 0) { + _collector->resetYields(); + } + if (PrintGCDetails && PrintGCTimeStamps) { + gclog_or_tty->date_stamp(PrintGCDateStamps); + gclog_or_tty->stamp(); + gclog_or_tty->print_cr(": [%s-concurrent-%s-start]", + _collector->cmsGen()->short_name(), _phase); + } + _collector->resetTimer(); + _wallclock.start(); + _collector->startTimer(); +} + +CMSPhaseAccounting::~CMSPhaseAccounting() { + assert(_wallclock.is_active(), "Wall clock should not have stopped"); + _collector->stopTimer(); + _wallclock.stop(); + if (PrintGCDetails) { + gclog_or_tty->date_stamp(PrintGCDateStamps); + if (PrintGCTimeStamps) { + gclog_or_tty->stamp(); + gclog_or_tty->print(": "); + } + gclog_or_tty->print("[%s-concurrent-%s: %3.3f/%3.3f secs]", + _collector->cmsGen()->short_name(), + _phase, _collector->timerValue(), _wallclock.seconds()); + if (_print_cr) { + gclog_or_tty->print_cr(""); + } + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr(" (CMS-concurrent-%s yielded %d times)", _phase, + _collector->yields()); + } + } +} + +// CMS work + +// Checkpoint the roots into this generation from outside +// this generation. [Note this initial checkpoint need only +// be approximate -- we'll do a catch up phase subsequently.] +void CMSCollector::checkpointRootsInitial(bool asynch) { + assert(_collectorState == InitialMarking, "Wrong collector state"); + check_correct_thread_executing(); + ReferenceProcessor* rp = ref_processor(); + SpecializationStats::clear(); + assert(_restart_addr == NULL, "Control point invariant"); + if (asynch) { + // acquire locks for subsequent manipulations + MutexLockerEx x(bitMapLock(), + Mutex::_no_safepoint_check_flag); + checkpointRootsInitialWork(asynch); + rp->verify_no_references_recorded(); + rp->enable_discovery(); // enable ("weak") refs discovery + _collectorState = Marking; + } else { + // (Weak) Refs discovery: this is controlled from genCollectedHeap::do_collection + // which recognizes if we are a CMS generation, and doesn't try to turn on + // discovery; verify that they aren't meddling. + assert(!rp->discovery_is_atomic(), + "incorrect setting of discovery predicate"); + assert(!rp->discovery_enabled(), "genCollectedHeap shouldn't control " + "ref discovery for this generation kind"); + // already have locks + checkpointRootsInitialWork(asynch); + rp->enable_discovery(); // now enable ("weak") refs discovery + _collectorState = Marking; + } + SpecializationStats::print(); +} + +void CMSCollector::checkpointRootsInitialWork(bool asynch) { + assert(SafepointSynchronize::is_at_safepoint(), "world should be stopped"); + assert(_collectorState == InitialMarking, "just checking"); + + // If there has not been a GC[n-1] since last GC[n] cycle completed, + // precede our marking with a collection of all + // younger generations to keep floating garbage to a minimum. + // XXX: we won't do this for now -- it's an optimization to be done later. + + // already have locks + assert_lock_strong(bitMapLock()); + assert(_markBitMap.isAllClear(), "was reset at end of previous cycle"); + + // Setup the verification and class unloading state for this + // CMS collection cycle. + setup_cms_unloading_and_verification_state(); + + NOT_PRODUCT(TraceTime t("\ncheckpointRootsInitialWork", + PrintGCDetails && Verbose, true, gclog_or_tty);) + if (UseAdaptiveSizePolicy) { + size_policy()->checkpoint_roots_initial_begin(); + } + + // Reset all the PLAB chunk arrays if necessary. + if (_survivor_plab_array != NULL && !CMSPLABRecordAlways) { + reset_survivor_plab_arrays(); + } + + ResourceMark rm; + HandleMark hm; + + FalseClosure falseClosure; + // In the case of a synchronous collection, we will elide the + // remark step, so it's important to catch all the nmethod oops + // in this step; hence the last argument to the constrcutor below. + MarkRefsIntoClosure notOlder(_span, &_markBitMap, !asynch /* nmethods */); + GenCollectedHeap* gch = GenCollectedHeap::heap(); + + verify_work_stacks_empty(); + verify_overflow_empty(); + + gch->ensure_parsability(false); // fill TLABs, but no need to retire them + // Update the saved marks which may affect the root scans. + gch->save_marks(); + + // weak reference processing has not started yet. + ref_processor()->set_enqueuing_is_done(false); + + { + COMPILER2_PRESENT(DerivedPointerTableDeactivate dpt_deact;) + gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. + gch->gen_process_strong_roots(_cmsGen->level(), + true, // younger gens are roots + true, // collecting perm gen + SharedHeap::ScanningOption(roots_scanning_options()), + NULL, ¬Older); + } + + // Clear mod-union table; it will be dirtied in the prologue of + // CMS generation per each younger generation collection. + + assert(_modUnionTable.isAllClear(), + "Was cleared in most recent final checkpoint phase" + " or no bits are set in the gc_prologue before the start of the next " + "subsequent marking phase."); + + // Temporarily disabled, since pre/post-consumption closures don't + // care about precleaned cards + #if 0 + { + MemRegion mr = MemRegion((HeapWord*)_virtual_space.low(), + (HeapWord*)_virtual_space.high()); + _ct->ct_bs()->preclean_dirty_cards(mr); + } + #endif + + // Save the end of the used_region of the constituent generations + // to be used to limit the extent of sweep in each generation. + save_sweep_limits(); + if (UseAdaptiveSizePolicy) { + size_policy()->checkpoint_roots_initial_end(gch->gc_cause()); + } + verify_overflow_empty(); +} + +bool CMSCollector::markFromRoots(bool asynch) { + // we might be tempted to assert that: + // assert(asynch == !SafepointSynchronize::is_at_safepoint(), + // "inconsistent argument?"); + // However that wouldn't be right, because it's possible that + // a safepoint is indeed in progress as a younger generation + // stop-the-world GC happens even as we mark in this generation. + assert(_collectorState == Marking, "inconsistent state?"); + check_correct_thread_executing(); + verify_overflow_empty(); + + bool res; + if (asynch) { + + // Start the timers for adaptive size policy for the concurrent phases + // Do it here so that the foreground MS can use the concurrent + // timer since a foreground MS might has the sweep done concurrently + // or STW. + if (UseAdaptiveSizePolicy) { + size_policy()->concurrent_marking_begin(); + } + + // Weak ref discovery note: We may be discovering weak + // refs in this generation concurrent (but interleaved) with + // weak ref discovery by a younger generation collector. + + CMSTokenSyncWithLocks ts(true, bitMapLock()); + TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); + CMSPhaseAccounting pa(this, "mark", !PrintGCDetails); + res = markFromRootsWork(asynch); + if (res) { + _collectorState = Precleaning; + } else { // We failed and a foreground collection wants to take over + assert(_foregroundGCIsActive, "internal state inconsistency"); + assert(_restart_addr == NULL, "foreground will restart from scratch"); + if (PrintGCDetails) { + gclog_or_tty->print_cr("bailing out to foreground collection"); + } + } + if (UseAdaptiveSizePolicy) { + size_policy()->concurrent_marking_end(); + } + } else { + assert(SafepointSynchronize::is_at_safepoint(), + "inconsistent with asynch == false"); + if (UseAdaptiveSizePolicy) { + size_policy()->ms_collection_marking_begin(); + } + // already have locks + res = markFromRootsWork(asynch); + _collectorState = FinalMarking; + if (UseAdaptiveSizePolicy) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + size_policy()->ms_collection_marking_end(gch->gc_cause()); + } + } + verify_overflow_empty(); + return res; +} + +bool CMSCollector::markFromRootsWork(bool asynch) { + // iterate over marked bits in bit map, doing a full scan and mark + // from these roots using the following algorithm: + // . if oop is to the right of the current scan pointer, + // mark corresponding bit (we'll process it later) + // . else (oop is to left of current scan pointer) + // push oop on marking stack + // . drain the marking stack + + // Note that when we do a marking step we need to hold the + // bit map lock -- recall that direct allocation (by mutators) + // and promotion (by younger generation collectors) is also + // marking the bit map. [the so-called allocate live policy.] + // Because the implementation of bit map marking is not + // robust wrt simultaneous marking of bits in the same word, + // we need to make sure that there is no such interference + // between concurrent such updates. + + // already have locks + assert_lock_strong(bitMapLock()); + + // Clear the revisit stack, just in case there are any + // obsolete contents from a short-circuited previous CMS cycle. + _revisitStack.reset(); + verify_work_stacks_empty(); + verify_overflow_empty(); + assert(_revisitStack.isEmpty(), "tabula rasa"); + + bool result = false; + if (CMSConcurrentMTEnabled && ParallelCMSThreads > 0) { + result = do_marking_mt(asynch); + } else { + result = do_marking_st(asynch); + } + return result; +} + +// Forward decl +class CMSConcMarkingTask; + +class CMSConcMarkingTerminator: public ParallelTaskTerminator { + CMSCollector* _collector; + CMSConcMarkingTask* _task; + bool _yield; + protected: + virtual void yield(); + public: + // "n_threads" is the number of threads to be terminated. + // "queue_set" is a set of work queues of other threads. + // "collector" is the CMS collector associated with this task terminator. + // "yield" indicates whether we need the gang as a whole to yield. + CMSConcMarkingTerminator(int n_threads, TaskQueueSetSuper* queue_set, + CMSCollector* collector, bool yield) : + ParallelTaskTerminator(n_threads, queue_set), + _collector(collector), + _yield(yield) { } + + void set_task(CMSConcMarkingTask* task) { + _task = task; + } +}; + +// MT Concurrent Marking Task +class CMSConcMarkingTask: public YieldingFlexibleGangTask { + CMSCollector* _collector; + YieldingFlexibleWorkGang* _workers; // the whole gang + int _n_workers; // requested/desired # workers + bool _asynch; + bool _result; + CompactibleFreeListSpace* _cms_space; + CompactibleFreeListSpace* _perm_space; + HeapWord* _global_finger; + + // Exposed here for yielding support + Mutex* const _bit_map_lock; + + // The per thread work queues, available here for stealing + OopTaskQueueSet* _task_queues; + CMSConcMarkingTerminator _term; + + public: + CMSConcMarkingTask(CMSCollector* collector, + CompactibleFreeListSpace* cms_space, + CompactibleFreeListSpace* perm_space, + bool asynch, int n_workers, + YieldingFlexibleWorkGang* workers, + OopTaskQueueSet* task_queues): + YieldingFlexibleGangTask("Concurrent marking done multi-threaded"), + _collector(collector), + _cms_space(cms_space), + _perm_space(perm_space), + _asynch(asynch), _n_workers(n_workers), _result(true), + _workers(workers), _task_queues(task_queues), + _term(n_workers, task_queues, _collector, asynch), + _bit_map_lock(collector->bitMapLock()) + { + assert(n_workers <= workers->total_workers(), + "Else termination won't work correctly today"); // XXX FIX ME! + _requested_size = n_workers; + _term.set_task(this); + assert(_cms_space->bottom() < _perm_space->bottom(), + "Finger incorrectly initialized below"); + _global_finger = _cms_space->bottom(); + } + + + OopTaskQueueSet* task_queues() { return _task_queues; } + + OopTaskQueue* work_queue(int i) { return task_queues()->queue(i); } + + HeapWord** global_finger_addr() { return &_global_finger; } + + CMSConcMarkingTerminator* terminator() { return &_term; } + + void work(int i); + + virtual void coordinator_yield(); // stuff done by coordinator + bool result() { return _result; } + + void reset(HeapWord* ra) { + _term.reset_for_reuse(); + } + + static bool get_work_from_overflow_stack(CMSMarkStack* ovflw_stk, + OopTaskQueue* work_q); + + private: + void do_scan_and_mark(int i, CompactibleFreeListSpace* sp); + void do_work_steal(int i); + void bump_global_finger(HeapWord* f); +}; + +void CMSConcMarkingTerminator::yield() { + if (ConcurrentMarkSweepThread::should_yield() && + !_collector->foregroundGCIsActive() && + _yield) { + _task->yield(); + } else { + ParallelTaskTerminator::yield(); + } +} + +//////////////////////////////////////////////////////////////// +// Concurrent Marking Algorithm Sketch +//////////////////////////////////////////////////////////////// +// Until all tasks exhausted (both spaces): +// -- claim next available chunk +// -- bump global finger via CAS +// -- find first object that starts in this chunk +// and start scanning bitmap from that position +// -- scan marked objects for oops +// -- CAS-mark target, and if successful: +// . if target oop is above global finger (volatile read) +// nothing to do +// . if target oop is in chunk and above local finger +// then nothing to do +// . else push on work-queue +// -- Deal with possible overflow issues: +// . local work-queue overflow causes stuff to be pushed on +// global (common) overflow queue +// . always first empty local work queue +// . then get a batch of oops from global work queue if any +// . then do work stealing +// -- When all tasks claimed (both spaces) +// and local work queue empty, +// then in a loop do: +// . check global overflow stack; steal a batch of oops and trace +// . try to steal from other threads oif GOS is empty +// . if neither is available, offer termination +// -- Terminate and return result +// +void CMSConcMarkingTask::work(int i) { + elapsedTimer _timer; + ResourceMark rm; + HandleMark hm; + + DEBUG_ONLY(_collector->verify_overflow_empty();) + + // Before we begin work, our work queue should be empty + assert(work_queue(i)->size() == 0, "Expected to be empty"); + // Scan the bitmap covering _cms_space, tracing through grey objects. + _timer.start(); + do_scan_and_mark(i, _cms_space); + _timer.stop(); + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr("Finished cms space scanning in %dth thread: %3.3f sec", + i, _timer.seconds()); // XXX: need xxx/xxx type of notation, two timers + } + + // ... do the same for the _perm_space + _timer.reset(); + _timer.start(); + do_scan_and_mark(i, _perm_space); + _timer.stop(); + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr("Finished perm space scanning in %dth thread: %3.3f sec", + i, _timer.seconds()); // XXX: need xxx/xxx type of notation, two timers + } + + // ... do work stealing + _timer.reset(); + _timer.start(); + do_work_steal(i); + _timer.stop(); + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr("Finished work stealing in %dth thread: %3.3f sec", + i, _timer.seconds()); // XXX: need xxx/xxx type of notation, two timers + } + assert(_collector->_markStack.isEmpty(), "Should have been emptied"); + assert(work_queue(i)->size() == 0, "Should have been emptied"); + // Note that under the current task protocol, the + // following assertion is true even of the spaces + // expanded since the completion of the concurrent + // marking. XXX This will likely change under a strict + // ABORT semantics. + assert(_global_finger > _cms_space->end() && + _global_finger >= _perm_space->end(), + "All tasks have been completed"); + DEBUG_ONLY(_collector->verify_overflow_empty();) +} + +void CMSConcMarkingTask::bump_global_finger(HeapWord* f) { + HeapWord* read = _global_finger; + HeapWord* cur = read; + while (f > read) { + cur = read; + read = (HeapWord*) Atomic::cmpxchg_ptr(f, &_global_finger, cur); + if (cur == read) { + // our cas succeeded + assert(_global_finger >= f, "protocol consistency"); + break; + } + } +} + +// This is really inefficient, and should be redone by +// using (not yet available) block-read and -write interfaces to the +// stack and the work_queue. XXX FIX ME !!! +bool CMSConcMarkingTask::get_work_from_overflow_stack(CMSMarkStack* ovflw_stk, + OopTaskQueue* work_q) { + // Fast lock-free check + if (ovflw_stk->length() == 0) { + return false; + } + assert(work_q->size() == 0, "Shouldn't steal"); + MutexLockerEx ml(ovflw_stk->par_lock(), + Mutex::_no_safepoint_check_flag); + // Grab up to 1/4 the size of the work queue + size_t num = MIN2((size_t)work_q->max_elems()/4, + (size_t)ParGCDesiredObjsFromOverflowList); + num = MIN2(num, ovflw_stk->length()); + for (int i = (int) num; i > 0; i--) { + oop cur = ovflw_stk->pop(); + assert(cur != NULL, "Counted wrong?"); + work_q->push(cur); + } + return num > 0; +} + +void CMSConcMarkingTask::do_scan_and_mark(int i, CompactibleFreeListSpace* sp) { + SequentialSubTasksDone* pst = sp->conc_par_seq_tasks(); + int n_tasks = pst->n_tasks(); + // We allow that there may be no tasks to do here because + // we are restarting after a stack overflow. + assert(pst->valid() || n_tasks == 0, "Uninitializd use?"); + int nth_task = 0; + + HeapWord* start = sp->bottom(); + size_t chunk_size = sp->marking_task_size(); + while (!pst->is_task_claimed(/* reference */ nth_task)) { + // Having claimed the nth task in this space, + // compute the chunk that it corresponds to: + MemRegion span = MemRegion(start + nth_task*chunk_size, + start + (nth_task+1)*chunk_size); + // Try and bump the global finger via a CAS; + // note that we need to do the global finger bump + // _before_ taking the intersection below, because + // the task corresponding to that region will be + // deemed done even if the used_region() expands + // because of allocation -- as it almost certainly will + // during start-up while the threads yield in the + // closure below. + HeapWord* finger = span.end(); + bump_global_finger(finger); // atomically + // There are null tasks here corresponding to chunks + // beyond the "top" address of the space. + span = span.intersection(sp->used_region()); + if (!span.is_empty()) { // Non-null task + // We want to skip the first object because + // the protocol is to scan any object in its entirety + // that _starts_ in this span; a fortiori, any + // object starting in an earlier span is scanned + // as part of an earlier claimed task. + // Below we use the "careful" version of block_start + // so we do not try to navigate uninitialized objects. + HeapWord* prev_obj = sp->block_start_careful(span.start()); + // Below we use a variant of block_size that uses the + // Printezis bits to avoid waiting for allocated + // objects to become initialized/parsable. + while (prev_obj < span.start()) { + size_t sz = sp->block_size_no_stall(prev_obj, _collector); + if (sz > 0) { + prev_obj += sz; + } else { + // In this case we may end up doing a bit of redundant + // scanning, but that appears unavoidable, short of + // locking the free list locks; see bug 6324141. + break; + } + } + if (prev_obj < span.end()) { + MemRegion my_span = MemRegion(prev_obj, span.end()); + // Do the marking work within a non-empty span -- + // the last argument to the constructor indicates whether the + // iteration should be incremental with periodic yields. + Par_MarkFromRootsClosure cl(this, _collector, my_span, + &_collector->_markBitMap, + work_queue(i), + &_collector->_markStack, + &_collector->_revisitStack, + _asynch); + _collector->_markBitMap.iterate(&cl, my_span.start(), my_span.end()); + } // else nothing to do for this task + } // else nothing to do for this task + } + // We'd be tempted to assert here that since there are no + // more tasks left to claim in this space, the global_finger + // must exceed space->top() and a fortiori space->end(). However, + // that would not quite be correct because the bumping of + // global_finger occurs strictly after the claiming of a task, + // so by the time we reach here the global finger may not yet + // have been bumped up by the thread that claimed the last + // task. + pst->all_tasks_completed(); +} + +class Par_ConcMarkingClosure: public OopClosure { + CMSCollector* _collector; + MemRegion _span; + CMSBitMap* _bit_map; + CMSMarkStack* _overflow_stack; + CMSMarkStack* _revisit_stack; // XXXXXX Check proper use + OopTaskQueue* _work_queue; + + public: + Par_ConcMarkingClosure(CMSCollector* collector, OopTaskQueue* work_queue, + CMSBitMap* bit_map, CMSMarkStack* overflow_stack): + _collector(collector), + _span(_collector->_span), + _work_queue(work_queue), + _bit_map(bit_map), + _overflow_stack(overflow_stack) { } // need to initialize revisit stack etc. + + void do_oop(oop* p); + void trim_queue(size_t max); + void handle_stack_overflow(HeapWord* lost); +}; + +// Grey object rescan during work stealing phase -- +// the salient assumption here is that stolen oops must +// always be initialized, so we do not need to check for +// uninitialized objects before scanning here. +void Par_ConcMarkingClosure::do_oop(oop* p) { + oop this_oop = *p; + assert(this_oop->is_oop_or_null(), + "expected an oop or NULL"); + HeapWord* addr = (HeapWord*)this_oop; + // Check if oop points into the CMS generation + // and is not marked + if (_span.contains(addr) && !_bit_map->isMarked(addr)) { + // a white object ... + // If we manage to "claim" the object, by being the + // first thread to mark it, then we push it on our + // marking stack + if (_bit_map->par_mark(addr)) { // ... now grey + // push on work queue (grey set) + bool simulate_overflow = false; + NOT_PRODUCT( + if (CMSMarkStackOverflowALot && + _collector->simulate_overflow()) { + // simulate a stack overflow + simulate_overflow = true; + } + ) + if (simulate_overflow || + !(_work_queue->push(this_oop) || _overflow_stack->par_push(this_oop))) { + // stack overflow + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr("CMS marking stack overflow (benign) at " + SIZE_FORMAT, _overflow_stack->capacity()); + } + // We cannot assert that the overflow stack is full because + // it may have been emptied since. + assert(simulate_overflow || + _work_queue->size() == _work_queue->max_elems(), + "Else push should have succeeded"); + handle_stack_overflow(addr); + } + } // Else, some other thread got there first + } +} + +void Par_ConcMarkingClosure::trim_queue(size_t max) { + while (_work_queue->size() > max) { + oop new_oop; + if (_work_queue->pop_local(new_oop)) { + assert(new_oop->is_oop(), "Should be an oop"); + assert(_bit_map->isMarked((HeapWord*)new_oop), "Grey object"); + assert(_span.contains((HeapWord*)new_oop), "Not in span"); + assert(new_oop->is_parsable(), "Should be parsable"); + new_oop->oop_iterate(this); // do_oop() above + } + } +} + +// Upon stack overflow, we discard (part of) the stack, +// remembering the least address amongst those discarded +// in CMSCollector's _restart_address. +void Par_ConcMarkingClosure::handle_stack_overflow(HeapWord* lost) { + // We need to do this under a mutex to prevent other + // workers from interfering with the expansion below. + MutexLockerEx ml(_overflow_stack->par_lock(), + Mutex::_no_safepoint_check_flag); + // Remember the least grey address discarded + HeapWord* ra = (HeapWord*)_overflow_stack->least_value(lost); + _collector->lower_restart_addr(ra); + _overflow_stack->reset(); // discard stack contents + _overflow_stack->expand(); // expand the stack if possible +} + + +void CMSConcMarkingTask::do_work_steal(int i) { + OopTaskQueue* work_q = work_queue(i); + oop obj_to_scan; + CMSBitMap* bm = &(_collector->_markBitMap); + CMSMarkStack* ovflw = &(_collector->_markStack); + int* seed = _collector->hash_seed(i); + Par_ConcMarkingClosure cl(_collector, work_q, bm, ovflw); + while (true) { + cl.trim_queue(0); + assert(work_q->size() == 0, "Should have been emptied above"); + if (get_work_from_overflow_stack(ovflw, work_q)) { + // Can't assert below because the work obtained from the + // overflow stack may already have been stolen from us. + // assert(work_q->size() > 0, "Work from overflow stack"); + continue; + } else if (task_queues()->steal(i, seed, /* reference */ obj_to_scan)) { + assert(obj_to_scan->is_oop(), "Should be an oop"); + assert(bm->isMarked((HeapWord*)obj_to_scan), "Grey object"); + obj_to_scan->oop_iterate(&cl); + } else if (terminator()->offer_termination()) { + assert(work_q->size() == 0, "Impossible!"); + break; + } + } +} + +// This is run by the CMS (coordinator) thread. +void CMSConcMarkingTask::coordinator_yield() { + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "CMS thread should hold CMS token"); + + // First give up the locks, then yield, then re-lock + // We should probably use a constructor/destructor idiom to + // do this unlock/lock or modify the MutexUnlocker class to + // serve our purpose. XXX + assert_lock_strong(_bit_map_lock); + _bit_map_lock->unlock(); + ConcurrentMarkSweepThread::desynchronize(true); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + _collector->stopTimer(); + if (PrintCMSStatistics != 0) { + _collector->incrementYields(); + } + _collector->icms_wait(); + + // It is possible for whichever thread initiated the yield request + // not to get a chance to wake up and take the bitmap lock between + // this thread releasing it and reacquiring it. So, while the + // should_yield() flag is on, let's sleep for a bit to give the + // other thread a chance to wake up. The limit imposed on the number + // of iterations is defensive, to avoid any unforseen circumstances + // putting us into an infinite loop. Since it's always been this + // (coordinator_yield()) method that was observed to cause the + // problem, we are using a parameter (CMSCoordinatorYieldSleepCount) + // which is by default non-zero. For the other seven methods that + // also perform the yield operation, as are using a different + // parameter (CMSYieldSleepCount) which is by default zero. This way we + // can enable the sleeping for those methods too, if necessary. + // See 6442774. + // + // We really need to reconsider the synchronization between the GC + // thread and the yield-requesting threads in the future and we + // should really use wait/notify, which is the recommended + // way of doing this type of interaction. Additionally, we should + // consolidate the eight methods that do the yield operation and they + // are almost identical into one for better maintenability and + // readability. See 6445193. + // + // Tony 2006.06.29 + for (unsigned i = 0; i < CMSCoordinatorYieldSleepCount && + ConcurrentMarkSweepThread::should_yield() && + !CMSCollector::foregroundGCIsActive(); ++i) { + os::sleep(Thread::current(), 1, false); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + } + + ConcurrentMarkSweepThread::synchronize(true); + _bit_map_lock->lock_without_safepoint_check(); + _collector->startTimer(); +} + +bool CMSCollector::do_marking_mt(bool asynch) { + assert(ParallelCMSThreads > 0 && conc_workers() != NULL, "precondition"); + // In the future this would be determined ergonomically, based + // on #cpu's, # active mutator threads (and load), and mutation rate. + int num_workers = ParallelCMSThreads; + + CompactibleFreeListSpace* cms_space = _cmsGen->cmsSpace(); + CompactibleFreeListSpace* perm_space = _permGen->cmsSpace(); + + CMSConcMarkingTask tsk(this, cms_space, perm_space, + asynch, num_workers /* number requested XXX */, + conc_workers(), task_queues()); + + // Since the actual number of workers we get may be different + // from the number we requested above, do we need to do anything different + // below? In particular, may be we need to subclass the SequantialSubTasksDone + // class?? XXX + cms_space ->initialize_sequential_subtasks_for_marking(num_workers); + perm_space->initialize_sequential_subtasks_for_marking(num_workers); + + // Refs discovery is already non-atomic. + assert(!ref_processor()->discovery_is_atomic(), "Should be non-atomic"); + // Mutate the Refs discovery so it is MT during the + // multi-threaded marking phase. + ReferenceProcessorMTMutator mt(ref_processor(), num_workers > 1); + + conc_workers()->start_task(&tsk); + while (tsk.yielded()) { + tsk.coordinator_yield(); + conc_workers()->continue_task(&tsk); + } + // If the task was aborted, _restart_addr will be non-NULL + assert(tsk.completed() || _restart_addr != NULL, "Inconsistency"); + while (_restart_addr != NULL) { + // XXX For now we do not make use of ABORTED state and have not + // yet implemented the right abort semantics (even in the original + // single-threaded CMS case). That needs some more investigation + // and is deferred for now; see CR# TBF. 07252005YSR. XXX + assert(!CMSAbortSemantics || tsk.aborted(), "Inconsistency"); + // If _restart_addr is non-NULL, a marking stack overflow + // occured; we need to do a fresh marking iteration from the + // indicated restart address. + if (_foregroundGCIsActive && asynch) { + // We may be running into repeated stack overflows, having + // reached the limit of the stack size, while making very + // slow forward progress. It may be best to bail out and + // let the foreground collector do its job. + // Clear _restart_addr, so that foreground GC + // works from scratch. This avoids the headache of + // a "rescan" which would otherwise be needed because + // of the dirty mod union table & card table. + _restart_addr = NULL; + return false; + } + // Adjust the task to restart from _restart_addr + tsk.reset(_restart_addr); + cms_space ->initialize_sequential_subtasks_for_marking(num_workers, + _restart_addr); + perm_space->initialize_sequential_subtasks_for_marking(num_workers, + _restart_addr); + _restart_addr = NULL; + // Get the workers going again + conc_workers()->start_task(&tsk); + while (tsk.yielded()) { + tsk.coordinator_yield(); + conc_workers()->continue_task(&tsk); + } + } + assert(tsk.completed(), "Inconsistency"); + assert(tsk.result() == true, "Inconsistency"); + return true; +} + +bool CMSCollector::do_marking_st(bool asynch) { + ResourceMark rm; + HandleMark hm; + + MarkFromRootsClosure markFromRootsClosure(this, _span, &_markBitMap, + &_markStack, &_revisitStack, CMSYield && asynch); + // the last argument to iterate indicates whether the iteration + // should be incremental with periodic yields. + _markBitMap.iterate(&markFromRootsClosure); + // If _restart_addr is non-NULL, a marking stack overflow + // occured; we need to do a fresh iteration from the + // indicated restart address. + while (_restart_addr != NULL) { + if (_foregroundGCIsActive && asynch) { + // We may be running into repeated stack overflows, having + // reached the limit of the stack size, while making very + // slow forward progress. It may be best to bail out and + // let the foreground collector do its job. + // Clear _restart_addr, so that foreground GC + // works from scratch. This avoids the headache of + // a "rescan" which would otherwise be needed because + // of the dirty mod union table & card table. + _restart_addr = NULL; + return false; // indicating failure to complete marking + } + // Deal with stack overflow: + // we restart marking from _restart_addr + HeapWord* ra = _restart_addr; + markFromRootsClosure.reset(ra); + _restart_addr = NULL; + _markBitMap.iterate(&markFromRootsClosure, ra, _span.end()); + } + return true; +} + +void CMSCollector::preclean() { + check_correct_thread_executing(); + assert(Thread::current()->is_ConcurrentGC_thread(), "Wrong thread"); + verify_work_stacks_empty(); + verify_overflow_empty(); + _abort_preclean = false; + if (CMSPrecleaningEnabled) { + _eden_chunk_index = 0; + size_t used = get_eden_used(); + size_t capacity = get_eden_capacity(); + // Don't start sampling unless we will get sufficiently + // many samples. + if (used < (capacity/(CMSScheduleRemarkSamplingRatio * 100) + * CMSScheduleRemarkEdenPenetration)) { + _start_sampling = true; + } else { + _start_sampling = false; + } + TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); + CMSPhaseAccounting pa(this, "preclean", !PrintGCDetails); + preclean_work(CMSPrecleanRefLists1, CMSPrecleanSurvivors1); + } + CMSTokenSync x(true); // is cms thread + if (CMSPrecleaningEnabled) { + sample_eden(); + _collectorState = AbortablePreclean; + } else { + _collectorState = FinalMarking; + } + verify_work_stacks_empty(); + verify_overflow_empty(); +} + +// Try and schedule the remark such that young gen +// occupancy is CMSScheduleRemarkEdenPenetration %. +void CMSCollector::abortable_preclean() { + check_correct_thread_executing(); + assert(CMSPrecleaningEnabled, "Inconsistent control state"); + assert(_collectorState == AbortablePreclean, "Inconsistent control state"); + + // If Eden's current occupancy is below this threshold, + // immediately schedule the remark; else preclean + // past the next scavenge in an effort to + // schedule the pause as described avove. By choosing + // CMSScheduleRemarkEdenSizeThreshold >= max eden size + // we will never do an actual abortable preclean cycle. + if (get_eden_used() > CMSScheduleRemarkEdenSizeThreshold) { + TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); + CMSPhaseAccounting pa(this, "abortable-preclean", !PrintGCDetails); + // We need more smarts in the abortable preclean + // loop below to deal with cases where allocation + // in young gen is very very slow, and our precleaning + // is running a losing race against a horde of + // mutators intent on flooding us with CMS updates + // (dirty cards). + // One, admittedly dumb, strategy is to give up + // after a certain number of abortable precleaning loops + // or after a certain maximum time. We want to make + // this smarter in the next iteration. + // XXX FIX ME!!! YSR + size_t loops = 0, workdone = 0, cumworkdone = 0, waited = 0; + while (!(should_abort_preclean() || + ConcurrentMarkSweepThread::should_terminate())) { + workdone = preclean_work(CMSPrecleanRefLists2, CMSPrecleanSurvivors2); + cumworkdone += workdone; + loops++; + // Voluntarily terminate abortable preclean phase if we have + // been at it for too long. + if ((CMSMaxAbortablePrecleanLoops != 0) && + loops >= CMSMaxAbortablePrecleanLoops) { + if (PrintGCDetails) { + gclog_or_tty->print(" CMS: abort preclean due to loops "); + } + break; + } + if (pa.wallclock_millis() > CMSMaxAbortablePrecleanTime) { + if (PrintGCDetails) { + gclog_or_tty->print(" CMS: abort preclean due to time "); + } + break; + } + // If we are doing little work each iteration, we should + // take a short break. + if (workdone < CMSAbortablePrecleanMinWorkPerIteration) { + // Sleep for some time, waiting for work to accumulate + stopTimer(); + cmsThread()->wait_on_cms_lock(CMSAbortablePrecleanWaitMillis); + startTimer(); + waited++; + } + } + if (PrintCMSStatistics > 0) { + gclog_or_tty->print(" [%d iterations, %d waits, %d cards)] ", + loops, waited, cumworkdone); + } + } + CMSTokenSync x(true); // is cms thread + if (_collectorState != Idling) { + assert(_collectorState == AbortablePreclean, + "Spontaneous state transition?"); + _collectorState = FinalMarking; + } // Else, a foreground collection completed this CMS cycle. + return; +} + +// Respond to an Eden sampling opportunity +void CMSCollector::sample_eden() { + // Make sure a young gc cannot sneak in between our + // reading and recording of a sample. + assert(Thread::current()->is_ConcurrentGC_thread(), + "Only the cms thread may collect Eden samples"); + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "Should collect samples while holding CMS token"); + if (!_start_sampling) { + return; + } + if (_eden_chunk_array) { + if (_eden_chunk_index < _eden_chunk_capacity) { + _eden_chunk_array[_eden_chunk_index] = *_top_addr; // take sample + assert(_eden_chunk_array[_eden_chunk_index] <= *_end_addr, + "Unexpected state of Eden"); + // We'd like to check that what we just sampled is an oop-start address; + // however, we cannot do that here since the object may not yet have been + // initialized. So we'll instead do the check when we _use_ this sample + // later. + if (_eden_chunk_index == 0 || + (pointer_delta(_eden_chunk_array[_eden_chunk_index], + _eden_chunk_array[_eden_chunk_index-1]) + >= CMSSamplingGrain)) { + _eden_chunk_index++; // commit sample + } + } + } + if ((_collectorState == AbortablePreclean) && !_abort_preclean) { + size_t used = get_eden_used(); + size_t capacity = get_eden_capacity(); + assert(used <= capacity, "Unexpected state of Eden"); + if (used > (capacity/100 * CMSScheduleRemarkEdenPenetration)) { + _abort_preclean = true; + } + } +} + + +size_t CMSCollector::preclean_work(bool clean_refs, bool clean_survivor) { + assert(_collectorState == Precleaning || + _collectorState == AbortablePreclean, "incorrect state"); + ResourceMark rm; + HandleMark hm; + // Do one pass of scrubbing the discovered reference lists + // to remove any reference objects with strongly-reachable + // referents. + if (clean_refs) { + ReferenceProcessor* rp = ref_processor(); + CMSPrecleanRefsYieldClosure yield_cl(this); + assert(rp->span().equals(_span), "Spans should be equal"); + CMSKeepAliveClosure keep_alive(this, _span, &_markBitMap, + &_markStack); + CMSDrainMarkingStackClosure complete_trace(this, + _span, &_markBitMap, &_markStack, + &keep_alive); + + // We don't want this step to interfere with a young + // collection because we don't want to take CPU + // or memory bandwidth away from the young GC threads + // (which may be as many as there are CPUs). + // Note that we don't need to protect ourselves from + // interference with mutators because they can't + // manipulate the discovered reference lists nor affect + // the computed reachability of the referents, the + // only properties manipulated by the precleaning + // of these reference lists. + stopTimer(); + CMSTokenSyncWithLocks x(true /* is cms thread */, + bitMapLock()); + startTimer(); + sample_eden(); + // The following will yield to allow foreground + // collection to proceed promptly. XXX YSR: + // The code in this method may need further + // tweaking for better performance and some restructuring + // for cleaner interfaces. + rp->preclean_discovered_references( + rp->is_alive_non_header(), &keep_alive, &complete_trace, + &yield_cl); + } + + if (clean_survivor) { // preclean the active survivor space(s) + assert(_young_gen->kind() == Generation::DefNew || + _young_gen->kind() == Generation::ParNew || + _young_gen->kind() == Generation::ASParNew, + "incorrect type for cast"); + DefNewGeneration* dng = (DefNewGeneration*)_young_gen; + PushAndMarkClosure pam_cl(this, _span, ref_processor(), + &_markBitMap, &_modUnionTable, + &_markStack, &_revisitStack, + true /* precleaning phase */); + stopTimer(); + CMSTokenSyncWithLocks ts(true /* is cms thread */, + bitMapLock()); + startTimer(); + unsigned int before_count = + GenCollectedHeap::heap()->total_collections(); + SurvivorSpacePrecleanClosure + sss_cl(this, _span, &_markBitMap, &_markStack, + &pam_cl, before_count, CMSYield); + dng->from()->object_iterate_careful(&sss_cl); + dng->to()->object_iterate_careful(&sss_cl); + } + MarkRefsIntoAndScanClosure + mrias_cl(_span, ref_processor(), &_markBitMap, &_modUnionTable, + &_markStack, &_revisitStack, this, CMSYield, + true /* precleaning phase */); + // CAUTION: The following closure has persistent state that may need to + // be reset upon a decrease in the sequence of addresses it + // processes. + ScanMarkedObjectsAgainCarefullyClosure + smoac_cl(this, _span, + &_markBitMap, &_markStack, &_revisitStack, &mrias_cl, CMSYield); + + // Preclean dirty cards in ModUnionTable and CardTable using + // appropriate convergence criterion; + // repeat CMSPrecleanIter times unless we find that + // we are losing. + assert(CMSPrecleanIter < 10, "CMSPrecleanIter is too large"); + assert(CMSPrecleanNumerator < CMSPrecleanDenominator, + "Bad convergence multiplier"); + assert(CMSPrecleanThreshold >= 100, + "Unreasonably low CMSPrecleanThreshold"); + + size_t numIter, cumNumCards, lastNumCards, curNumCards; + for (numIter = 0, cumNumCards = lastNumCards = curNumCards = 0; + numIter < CMSPrecleanIter; + numIter++, lastNumCards = curNumCards, cumNumCards += curNumCards) { + curNumCards = preclean_mod_union_table(_cmsGen, &smoac_cl); + if (CMSPermGenPrecleaningEnabled) { + curNumCards += preclean_mod_union_table(_permGen, &smoac_cl); + } + if (Verbose && PrintGCDetails) { + gclog_or_tty->print(" (modUnionTable: %d cards)", curNumCards); + } + // Either there are very few dirty cards, so re-mark + // pause will be small anyway, or our pre-cleaning isn't + // that much faster than the rate at which cards are being + // dirtied, so we might as well stop and re-mark since + // precleaning won't improve our re-mark time by much. + if (curNumCards <= CMSPrecleanThreshold || + (numIter > 0 && + (curNumCards * CMSPrecleanDenominator > + lastNumCards * CMSPrecleanNumerator))) { + numIter++; + cumNumCards += curNumCards; + break; + } + } + curNumCards = preclean_card_table(_cmsGen, &smoac_cl); + if (CMSPermGenPrecleaningEnabled) { + curNumCards += preclean_card_table(_permGen, &smoac_cl); + } + cumNumCards += curNumCards; + if (PrintGCDetails && PrintCMSStatistics != 0) { + gclog_or_tty->print_cr(" (cardTable: %d cards, re-scanned %d cards, %d iterations)", + curNumCards, cumNumCards, numIter); + } + return cumNumCards; // as a measure of useful work done +} + +// PRECLEANING NOTES: +// Precleaning involves: +// . reading the bits of the modUnionTable and clearing the set bits. +// . For the cards corresponding to the set bits, we scan the +// objects on those cards. This means we need the free_list_lock +// so that we can safely iterate over the CMS space when scanning +// for oops. +// . When we scan the objects, we'll be both reading and setting +// marks in the marking bit map, so we'll need the marking bit map. +// . For protecting _collector_state transitions, we take the CGC_lock. +// Note that any races in the reading of of card table entries by the +// CMS thread on the one hand and the clearing of those entries by the +// VM thread or the setting of those entries by the mutator threads on the +// other are quite benign. However, for efficiency it makes sense to keep +// the VM thread from racing with the CMS thread while the latter is +// dirty card info to the modUnionTable. We therefore also use the +// CGC_lock to protect the reading of the card table and the mod union +// table by the CM thread. +// . We run concurrently with mutator updates, so scanning +// needs to be done carefully -- we should not try to scan +// potentially uninitialized objects. +// +// Locking strategy: While holding the CGC_lock, we scan over and +// reset a maximal dirty range of the mod union / card tables, then lock +// the free_list_lock and bitmap lock to do a full marking, then +// release these locks; and repeat the cycle. This allows for a +// certain amount of fairness in the sharing of these locks between +// the CMS collector on the one hand, and the VM thread and the +// mutators on the other. + +// NOTE: preclean_mod_union_table() and preclean_card_table() +// further below are largely identical; if you need to modify +// one of these methods, please check the other method too. + +size_t CMSCollector::preclean_mod_union_table( + ConcurrentMarkSweepGeneration* gen, + ScanMarkedObjectsAgainCarefullyClosure* cl) { + verify_work_stacks_empty(); + verify_overflow_empty(); + + // strategy: starting with the first card, accumulate contiguous + // ranges of dirty cards; clear these cards, then scan the region + // covered by these cards. + + // Since all of the MUT is committed ahead, we can just use + // that, in case the generations expand while we are precleaning. + // It might also be fine to just use the committed part of the + // generation, but we might potentially miss cards when the + // generation is rapidly expanding while we are in the midst + // of precleaning. + HeapWord* startAddr = gen->reserved().start(); + HeapWord* endAddr = gen->reserved().end(); + + cl->setFreelistLock(gen->freelistLock()); // needed for yielding + + size_t numDirtyCards, cumNumDirtyCards; + HeapWord *nextAddr, *lastAddr; + for (cumNumDirtyCards = numDirtyCards = 0, + nextAddr = lastAddr = startAddr; + nextAddr < endAddr; + nextAddr = lastAddr, cumNumDirtyCards += numDirtyCards) { + + ResourceMark rm; + HandleMark hm; + + MemRegion dirtyRegion; + { + stopTimer(); + CMSTokenSync ts(true); + startTimer(); + sample_eden(); + // Get dirty region starting at nextOffset (inclusive), + // simultaneously clearing it. + dirtyRegion = + _modUnionTable.getAndClearMarkedRegion(nextAddr, endAddr); + assert(dirtyRegion.start() >= nextAddr, + "returned region inconsistent?"); + } + // Remember where the next search should begin. + // The returned region (if non-empty) is a right open interval, + // so lastOffset is obtained from the right end of that + // interval. + lastAddr = dirtyRegion.end(); + // Should do something more transparent and less hacky XXX + numDirtyCards = + _modUnionTable.heapWordDiffToOffsetDiff(dirtyRegion.word_size()); + + // We'll scan the cards in the dirty region (with periodic + // yields for foreground GC as needed). + if (!dirtyRegion.is_empty()) { + assert(numDirtyCards > 0, "consistency check"); + HeapWord* stop_point = NULL; + { + stopTimer(); + CMSTokenSyncWithLocks ts(true, gen->freelistLock(), + bitMapLock()); + startTimer(); + verify_work_stacks_empty(); + verify_overflow_empty(); + sample_eden(); + stop_point = + gen->cmsSpace()->object_iterate_careful_m(dirtyRegion, cl); + } + if (stop_point != NULL) { + // The careful iteration stopped early either because it found an + // uninitialized object, or because we were in the midst of an + // "abortable preclean", which should now be aborted. Redirty + // the bits corresponding to the partially-scanned or unscanned + // cards. We'll either restart at the next block boundary or + // abort the preclean. + assert((CMSPermGenPrecleaningEnabled && (gen == _permGen)) || + (_collectorState == AbortablePreclean && should_abort_preclean()), + "Unparsable objects should only be in perm gen."); + + stopTimer(); + CMSTokenSyncWithLocks ts(true, bitMapLock()); + startTimer(); + _modUnionTable.mark_range(MemRegion(stop_point, dirtyRegion.end())); + if (should_abort_preclean()) { + break; // out of preclean loop + } else { + // Compute the next address at which preclean should pick up; + // might need bitMapLock in order to read P-bits. + lastAddr = next_card_start_after_block(stop_point); + } + } + } else { + assert(lastAddr == endAddr, "consistency check"); + assert(numDirtyCards == 0, "consistency check"); + break; + } + } + verify_work_stacks_empty(); + verify_overflow_empty(); + return cumNumDirtyCards; +} + +// NOTE: preclean_mod_union_table() above and preclean_card_table() +// below are largely identical; if you need to modify +// one of these methods, please check the other method too. + +size_t CMSCollector::preclean_card_table(ConcurrentMarkSweepGeneration* gen, + ScanMarkedObjectsAgainCarefullyClosure* cl) { + // strategy: it's similar to precleamModUnionTable above, in that + // we accumulate contiguous ranges of dirty cards, mark these cards + // precleaned, then scan the region covered by these cards. + HeapWord* endAddr = (HeapWord*)(gen->_virtual_space.high()); + HeapWord* startAddr = (HeapWord*)(gen->_virtual_space.low()); + + cl->setFreelistLock(gen->freelistLock()); // needed for yielding + + size_t numDirtyCards, cumNumDirtyCards; + HeapWord *lastAddr, *nextAddr; + + for (cumNumDirtyCards = numDirtyCards = 0, + nextAddr = lastAddr = startAddr; + nextAddr < endAddr; + nextAddr = lastAddr, cumNumDirtyCards += numDirtyCards) { + + ResourceMark rm; + HandleMark hm; + + MemRegion dirtyRegion; + { + // See comments in "Precleaning notes" above on why we + // do this locking. XXX Could the locking overheads be + // too high when dirty cards are sparse? [I don't think so.] + stopTimer(); + CMSTokenSync x(true); // is cms thread + startTimer(); + sample_eden(); + // Get and clear dirty region from card table + dirtyRegion = _ct->ct_bs()->dirty_card_range_after_preclean( + MemRegion(nextAddr, endAddr)); + assert(dirtyRegion.start() >= nextAddr, + "returned region inconsistent?"); + } + lastAddr = dirtyRegion.end(); + numDirtyCards = + dirtyRegion.word_size()/CardTableModRefBS::card_size_in_words; + + if (!dirtyRegion.is_empty()) { + stopTimer(); + CMSTokenSyncWithLocks ts(true, gen->freelistLock(), bitMapLock()); + startTimer(); + sample_eden(); + verify_work_stacks_empty(); + verify_overflow_empty(); + HeapWord* stop_point = + gen->cmsSpace()->object_iterate_careful_m(dirtyRegion, cl); + if (stop_point != NULL) { + // The careful iteration stopped early because it found an + // uninitialized object. Redirty the bits corresponding to the + // partially-scanned or unscanned cards, and start again at the + // next block boundary. + assert(CMSPermGenPrecleaningEnabled || + (_collectorState == AbortablePreclean && should_abort_preclean()), + "Unparsable objects should only be in perm gen."); + _ct->ct_bs()->invalidate(MemRegion(stop_point, dirtyRegion.end())); + if (should_abort_preclean()) { + break; // out of preclean loop + } else { + // Compute the next address at which preclean should pick up. + lastAddr = next_card_start_after_block(stop_point); + } + } + } else { + break; + } + } + verify_work_stacks_empty(); + verify_overflow_empty(); + return cumNumDirtyCards; +} + +void CMSCollector::checkpointRootsFinal(bool asynch, + bool clear_all_soft_refs, bool init_mark_was_synchronous) { + assert(_collectorState == FinalMarking, "incorrect state transition?"); + check_correct_thread_executing(); + // world is stopped at this checkpoint + assert(SafepointSynchronize::is_at_safepoint(), + "world should be stopped"); + verify_work_stacks_empty(); + verify_overflow_empty(); + + SpecializationStats::clear(); + if (PrintGCDetails) { + gclog_or_tty->print("[YG occupancy: "SIZE_FORMAT" K ("SIZE_FORMAT" K)]", + _young_gen->used() / K, + _young_gen->capacity() / K); + } + if (asynch) { + if (CMSScavengeBeforeRemark) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + // Temporarily set flag to false, GCH->do_collection will + // expect it to be false and set to true + FlagSetting fl(gch->_is_gc_active, false); + NOT_PRODUCT(TraceTime t("Scavenge-Before-Remark", + PrintGCDetails && Verbose, true, gclog_or_tty);) + int level = _cmsGen->level() - 1; + if (level >= 0) { + gch->do_collection(true, // full (i.e. force, see below) + false, // !clear_all_soft_refs + 0, // size + false, // is_tlab + level // max_level + ); + } + } + FreelistLocker x(this); + MutexLockerEx y(bitMapLock(), + Mutex::_no_safepoint_check_flag); + assert(!init_mark_was_synchronous, "but that's impossible!"); + checkpointRootsFinalWork(asynch, clear_all_soft_refs, false); + } else { + // already have all the locks + checkpointRootsFinalWork(asynch, clear_all_soft_refs, + init_mark_was_synchronous); + } + verify_work_stacks_empty(); + verify_overflow_empty(); + SpecializationStats::print(); +} + +void CMSCollector::checkpointRootsFinalWork(bool asynch, + bool clear_all_soft_refs, bool init_mark_was_synchronous) { + + NOT_PRODUCT(TraceTime tr("checkpointRootsFinalWork", PrintGCDetails, false, gclog_or_tty);) + + assert(haveFreelistLocks(), "must have free list locks"); + assert_lock_strong(bitMapLock()); + + if (UseAdaptiveSizePolicy) { + size_policy()->checkpoint_roots_final_begin(); + } + + ResourceMark rm; + HandleMark hm; + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + + if (cms_should_unload_classes()) { + CodeCache::gc_prologue(); + } + assert(haveFreelistLocks(), "must have free list locks"); + assert_lock_strong(bitMapLock()); + + if (!init_mark_was_synchronous) { + // We might assume that we need not fill TLAB's when + // CMSScavengeBeforeRemark is set, because we may have just done + // a scavenge which would have filled all TLAB's -- and besides + // Eden would be empty. This however may not always be the case -- + // for instance although we asked for a scavenge, it may not have + // happened because of a JNI critical section. We probably need + // a policy for deciding whether we can in that case wait until + // the critical section releases and then do the remark following + // the scavenge, and skip it here. In the absence of that policy, + // or of an indication of whether the scavenge did indeed occur, + // we cannot rely on TLAB's having been filled and must do + // so here just in case a scavenge did not happen. + gch->ensure_parsability(false); // fill TLAB's, but no need to retire them + // Update the saved marks which may affect the root scans. + gch->save_marks(); + + { + COMPILER2_PRESENT(DerivedPointerTableDeactivate dpt_deact;) + + // Note on the role of the mod union table: + // Since the marker in "markFromRoots" marks concurrently with + // mutators, it is possible for some reachable objects not to have been + // scanned. For instance, an only reference to an object A was + // placed in object B after the marker scanned B. Unless B is rescanned, + // A would be collected. Such updates to references in marked objects + // are detected via the mod union table which is the set of all cards + // dirtied since the first checkpoint in this GC cycle and prior to + // the most recent young generation GC, minus those cleaned up by the + // concurrent precleaning. + if (CMSParallelRemarkEnabled && ParallelGCThreads > 0) { + TraceTime t("Rescan (parallel) ", PrintGCDetails, false, gclog_or_tty); + do_remark_parallel(); + } else { + TraceTime t("Rescan (non-parallel) ", PrintGCDetails, false, + gclog_or_tty); + do_remark_non_parallel(); + } + } + } else { + assert(!asynch, "Can't have init_mark_was_synchronous in asynch mode"); + // The initial mark was stop-world, so there's no rescanning to + // do; go straight on to the next step below. + } + verify_work_stacks_empty(); + verify_overflow_empty(); + + { + NOT_PRODUCT(TraceTime ts("refProcessingWork", PrintGCDetails, false, gclog_or_tty);) + refProcessingWork(asynch, clear_all_soft_refs); + } + verify_work_stacks_empty(); + verify_overflow_empty(); + + if (cms_should_unload_classes()) { + CodeCache::gc_epilogue(); + } + + // If we encountered any (marking stack / work queue) overflow + // events during the current CMS cycle, take appropriate + // remedial measures, where possible, so as to try and avoid + // recurrence of that condition. + assert(_markStack.isEmpty(), "No grey objects"); + size_t ser_ovflw = _ser_pmc_remark_ovflw + _ser_pmc_preclean_ovflw + + _ser_kac_ovflw; + if (ser_ovflw > 0) { + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr("Marking stack overflow (benign) " + "(pmc_pc="SIZE_FORMAT", pmc_rm="SIZE_FORMAT", kac="SIZE_FORMAT")", + _ser_pmc_preclean_ovflw, _ser_pmc_remark_ovflw, + _ser_kac_ovflw); + } + _markStack.expand(); + _ser_pmc_remark_ovflw = 0; + _ser_pmc_preclean_ovflw = 0; + _ser_kac_ovflw = 0; + } + if (_par_pmc_remark_ovflw > 0 || _par_kac_ovflw > 0) { + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr("Work queue overflow (benign) " + "(pmc_rm="SIZE_FORMAT", kac="SIZE_FORMAT")", + _par_pmc_remark_ovflw, _par_kac_ovflw); + } + _par_pmc_remark_ovflw = 0; + _par_kac_ovflw = 0; + } + if (PrintCMSStatistics != 0) { + if (_markStack._hit_limit > 0) { + gclog_or_tty->print_cr(" (benign) Hit max stack size limit ("SIZE_FORMAT")", + _markStack._hit_limit); + } + if (_markStack._failed_double > 0) { + gclog_or_tty->print_cr(" (benign) Failed stack doubling ("SIZE_FORMAT")," + " current capacity "SIZE_FORMAT, + _markStack._failed_double, + _markStack.capacity()); + } + } + _markStack._hit_limit = 0; + _markStack._failed_double = 0; + + if ((VerifyAfterGC || VerifyDuringGC) && + GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { + verify_after_remark(); + } + + // Change under the freelistLocks. + _collectorState = Sweeping; + // Call isAllClear() under bitMapLock + assert(_modUnionTable.isAllClear(), "Should be clear by end of the" + " final marking"); + if (UseAdaptiveSizePolicy) { + size_policy()->checkpoint_roots_final_end(gch->gc_cause()); + } +} + +// Parallel remark task +class CMSParRemarkTask: public AbstractGangTask { + CMSCollector* _collector; + WorkGang* _workers; + int _n_workers; + CompactibleFreeListSpace* _cms_space; + CompactibleFreeListSpace* _perm_space; + + // The per-thread work queues, available here for stealing. + OopTaskQueueSet* _task_queues; + ParallelTaskTerminator _term; + + public: + CMSParRemarkTask(CMSCollector* collector, + CompactibleFreeListSpace* cms_space, + CompactibleFreeListSpace* perm_space, + int n_workers, WorkGang* workers, + OopTaskQueueSet* task_queues): + AbstractGangTask("Rescan roots and grey objects in parallel"), + _collector(collector), + _cms_space(cms_space), _perm_space(perm_space), + _n_workers(n_workers), + _workers(workers), + _task_queues(task_queues), + _term(workers->total_workers(), task_queues) { } + + OopTaskQueueSet* task_queues() { return _task_queues; } + + OopTaskQueue* work_queue(int i) { return task_queues()->queue(i); } + + ParallelTaskTerminator* terminator() { return &_term; } + + void work(int i); + + private: + // Work method in support of parallel rescan ... of young gen spaces + void do_young_space_rescan(int i, Par_MarkRefsIntoAndScanClosure* cl, + ContiguousSpace* space, + HeapWord** chunk_array, size_t chunk_top); + + // ... of dirty cards in old space + void do_dirty_card_rescan_tasks(CompactibleFreeListSpace* sp, int i, + Par_MarkRefsIntoAndScanClosure* cl); + + // ... work stealing for the above + void do_work_steal(int i, Par_MarkRefsIntoAndScanClosure* cl, int* seed); +}; + +void CMSParRemarkTask::work(int i) { + elapsedTimer _timer; + ResourceMark rm; + HandleMark hm; + + // ---------- rescan from roots -------------- + _timer.start(); + GenCollectedHeap* gch = GenCollectedHeap::heap(); + Par_MarkRefsIntoAndScanClosure par_mrias_cl(_collector, + _collector->_span, _collector->ref_processor(), + &(_collector->_markBitMap), + work_queue(i), &(_collector->_revisitStack)); + + // Rescan young gen roots first since these are likely + // coarsely partitioned and may, on that account, constitute + // the critical path; thus, it's best to start off that + // work first. + // ---------- young gen roots -------------- + { + DefNewGeneration* dng = _collector->_young_gen->as_DefNewGeneration(); + EdenSpace* eden_space = dng->eden(); + ContiguousSpace* from_space = dng->from(); + ContiguousSpace* to_space = dng->to(); + + HeapWord** eca = _collector->_eden_chunk_array; + size_t ect = _collector->_eden_chunk_index; + HeapWord** sca = _collector->_survivor_chunk_array; + size_t sct = _collector->_survivor_chunk_index; + + assert(ect <= _collector->_eden_chunk_capacity, "out of bounds"); + assert(sct <= _collector->_survivor_chunk_capacity, "out of bounds"); + + do_young_space_rescan(i, &par_mrias_cl, to_space, NULL, 0); + do_young_space_rescan(i, &par_mrias_cl, from_space, sca, sct); + do_young_space_rescan(i, &par_mrias_cl, eden_space, eca, ect); + + _timer.stop(); + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr( + "Finished young gen rescan work in %dth thread: %3.3f sec", + i, _timer.seconds()); + } + } + + // ---------- remaining roots -------------- + _timer.reset(); + _timer.start(); + gch->gen_process_strong_roots(_collector->_cmsGen->level(), + false, // yg was scanned above + true, // collecting perm gen + SharedHeap::ScanningOption(_collector->CMSCollector::roots_scanning_options()), + NULL, &par_mrias_cl); + _timer.stop(); + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr( + "Finished remaining root rescan work in %dth thread: %3.3f sec", + i, _timer.seconds()); + } + + // ---------- rescan dirty cards ------------ + _timer.reset(); + _timer.start(); + + // Do the rescan tasks for each of the two spaces + // (cms_space and perm_space) in turn. + do_dirty_card_rescan_tasks(_cms_space, i, &par_mrias_cl); + do_dirty_card_rescan_tasks(_perm_space, i, &par_mrias_cl); + _timer.stop(); + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr( + "Finished dirty card rescan work in %dth thread: %3.3f sec", + i, _timer.seconds()); + } + + // ---------- steal work from other threads ... + // ---------- ... and drain overflow list. + _timer.reset(); + _timer.start(); + do_work_steal(i, &par_mrias_cl, _collector->hash_seed(i)); + _timer.stop(); + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr( + "Finished work stealing in %dth thread: %3.3f sec", + i, _timer.seconds()); + } +} + +void +CMSParRemarkTask::do_young_space_rescan(int i, + Par_MarkRefsIntoAndScanClosure* cl, ContiguousSpace* space, + HeapWord** chunk_array, size_t chunk_top) { + // Until all tasks completed: + // . claim an unclaimed task + // . compute region boundaries corresponding to task claimed + // using chunk_array + // . par_oop_iterate(cl) over that region + + ResourceMark rm; + HandleMark hm; + + SequentialSubTasksDone* pst = space->par_seq_tasks(); + assert(pst->valid(), "Uninitialized use?"); + + int nth_task = 0; + int n_tasks = pst->n_tasks(); + + HeapWord *start, *end; + while (!pst->is_task_claimed(/* reference */ nth_task)) { + // We claimed task # nth_task; compute its boundaries. + if (chunk_top == 0) { // no samples were taken + assert(nth_task == 0 && n_tasks == 1, "Can have only 1 EdenSpace task"); + start = space->bottom(); + end = space->top(); + } else if (nth_task == 0) { + start = space->bottom(); + end = chunk_array[nth_task]; + } else if (nth_task < (jint)chunk_top) { + assert(nth_task >= 1, "Control point invariant"); + start = chunk_array[nth_task - 1]; + end = chunk_array[nth_task]; + } else { + assert(nth_task == (jint)chunk_top, "Control point invariant"); + start = chunk_array[chunk_top - 1]; + end = space->top(); + } + MemRegion mr(start, end); + // Verify that mr is in space + assert(mr.is_empty() || space->used_region().contains(mr), + "Should be in space"); + // Verify that "start" is an object boundary + assert(mr.is_empty() || oop(mr.start())->is_oop(), + "Should be an oop"); + space->par_oop_iterate(mr, cl); + } + pst->all_tasks_completed(); +} + +void +CMSParRemarkTask::do_dirty_card_rescan_tasks( + CompactibleFreeListSpace* sp, int i, + Par_MarkRefsIntoAndScanClosure* cl) { + // Until all tasks completed: + // . claim an unclaimed task + // . compute region boundaries corresponding to task claimed + // . transfer dirty bits ct->mut for that region + // . apply rescanclosure to dirty mut bits for that region + + ResourceMark rm; + HandleMark hm; + + OopTaskQueue* work_q = work_queue(i); + ModUnionClosure modUnionClosure(&(_collector->_modUnionTable)); + // CAUTION! CAUTION! CAUTION! CAUTION! CAUTION! CAUTION! CAUTION! + // CAUTION: This closure has state that persists across calls to + // the work method dirty_range_iterate_clear() in that it has + // imbedded in it a (subtype of) UpwardsObjectClosure. The + // use of that state in the imbedded UpwardsObjectClosure instance + // assumes that the cards are always iterated (even if in parallel + // by several threads) in monotonically increasing order per each + // thread. This is true of the implementation below which picks + // card ranges (chunks) in monotonically increasing order globally + // and, a-fortiori, in monotonically increasing order per thread + // (the latter order being a subsequence of the former). + // If the work code below is ever reorganized into a more chaotic + // work-partitioning form than the current "sequential tasks" + // paradigm, the use of that persistent state will have to be + // revisited and modified appropriately. See also related + // bug 4756801 work on which should examine this code to make + // sure that the changes there do not run counter to the + // assumptions made here and necessary for correctness and + // efficiency. Note also that this code might yield inefficient + // behaviour in the case of very large objects that span one or + // more work chunks. Such objects would potentially be scanned + // several times redundantly. Work on 4756801 should try and + // address that performance anomaly if at all possible. XXX + MemRegion full_span = _collector->_span; + CMSBitMap* bm = &(_collector->_markBitMap); // shared + CMSMarkStack* rs = &(_collector->_revisitStack); // shared + MarkFromDirtyCardsClosure + greyRescanClosure(_collector, full_span, // entire span of interest + sp, bm, work_q, rs, cl); + + SequentialSubTasksDone* pst = sp->conc_par_seq_tasks(); + assert(pst->valid(), "Uninitialized use?"); + int nth_task = 0; + const int alignment = CardTableModRefBS::card_size * BitsPerWord; + MemRegion span = sp->used_region(); + HeapWord* start_addr = span.start(); + HeapWord* end_addr = (HeapWord*)round_to((intptr_t)span.end(), + alignment); + const size_t chunk_size = sp->rescan_task_size(); // in HeapWord units + assert((HeapWord*)round_to((intptr_t)start_addr, alignment) == + start_addr, "Check alignment"); + assert((size_t)round_to((intptr_t)chunk_size, alignment) == + chunk_size, "Check alignment"); + + while (!pst->is_task_claimed(/* reference */ nth_task)) { + // Having claimed the nth_task, compute corresponding mem-region, + // which is a-fortiori aligned correctly (i.e. at a MUT bopundary). + // The alignment restriction ensures that we do not need any + // synchronization with other gang-workers while setting or + // clearing bits in thus chunk of the MUT. + MemRegion this_span = MemRegion(start_addr + nth_task*chunk_size, + start_addr + (nth_task+1)*chunk_size); + // The last chunk's end might be way beyond end of the + // used region. In that case pull back appropriately. + if (this_span.end() > end_addr) { + this_span.set_end(end_addr); + assert(!this_span.is_empty(), "Program logic (calculation of n_tasks)"); + } + // Iterate over the dirty cards covering this chunk, marking them + // precleaned, and setting the corresponding bits in the mod union + // table. Since we have been careful to partition at Card and MUT-word + // boundaries no synchronization is needed between parallel threads. + _collector->_ct->ct_bs()->dirty_card_iterate(this_span, + &modUnionClosure); + + // Having transferred these marks into the modUnionTable, + // rescan the marked objects on the dirty cards in the modUnionTable. + // Even if this is at a synchronous collection, the initial marking + // may have been done during an asynchronous collection so there + // may be dirty bits in the mod-union table. + _collector->_modUnionTable.dirty_range_iterate_clear( + this_span, &greyRescanClosure); + _collector->_modUnionTable.verifyNoOneBitsInRange( + this_span.start(), + this_span.end()); + } + pst->all_tasks_completed(); // declare that i am done +} + +// . see if we can share work_queues with ParNew? XXX +void +CMSParRemarkTask::do_work_steal(int i, Par_MarkRefsIntoAndScanClosure* cl, + int* seed) { + OopTaskQueue* work_q = work_queue(i); + NOT_PRODUCT(int num_steals = 0;) + oop obj_to_scan; + CMSBitMap* bm = &(_collector->_markBitMap); + size_t num_from_overflow_list = + MIN2((size_t)work_q->max_elems()/4, + (size_t)ParGCDesiredObjsFromOverflowList); + + while (true) { + // Completely finish any left over work from (an) earlier round(s) + cl->trim_queue(0); + // Now check if there's any work in the overflow list + if (_collector->par_take_from_overflow_list(num_from_overflow_list, + work_q)) { + // found something in global overflow list; + // not yet ready to go stealing work from others. + // We'd like to assert(work_q->size() != 0, ...) + // because we just took work from the overflow list, + // but of course we can't since all of that could have + // been already stolen from us. + // "He giveth and He taketh away." + continue; + } + // Verify that we have no work before we resort to stealing + assert(work_q->size() == 0, "Have work, shouldn't steal"); + // Try to steal from other queues that have work + if (task_queues()->steal(i, seed, /* reference */ obj_to_scan)) { + NOT_PRODUCT(num_steals++;) + assert(obj_to_scan->is_oop(), "Oops, not an oop!"); + assert(bm->isMarked((HeapWord*)obj_to_scan), "Stole an unmarked oop?"); + // Do scanning work + obj_to_scan->oop_iterate(cl); + // Loop around, finish this work, and try to steal some more + } else if (terminator()->offer_termination()) { + break; // nirvana from the infinite cycle + } + } + NOT_PRODUCT( + if (PrintCMSStatistics != 0) { + gclog_or_tty->print("\n\t(%d: stole %d oops)", i, num_steals); + } + ) + assert(work_q->size() == 0 && _collector->overflow_list_is_empty(), + "Else our work is not yet done"); +} + +// Return a thread-local PLAB recording array, as appropriate. +void* CMSCollector::get_data_recorder(int thr_num) { + if (_survivor_plab_array != NULL && + (CMSPLABRecordAlways || + (_collectorState > Marking && _collectorState < FinalMarking))) { + assert(thr_num < (int)ParallelGCThreads, "thr_num is out of bounds"); + ChunkArray* ca = &_survivor_plab_array[thr_num]; + ca->reset(); // clear it so that fresh data is recorded + return (void*) ca; + } else { + return NULL; + } +} + +// Reset all the thread-local PLAB recording arrays +void CMSCollector::reset_survivor_plab_arrays() { + for (uint i = 0; i < ParallelGCThreads; i++) { + _survivor_plab_array[i].reset(); + } +} + +// Merge the per-thread plab arrays into the global survivor chunk +// array which will provide the partitioning of the survivor space +// for CMS rescan. +void CMSCollector::merge_survivor_plab_arrays(ContiguousSpace* surv) { + assert(_survivor_plab_array != NULL, "Error"); + assert(_survivor_chunk_array != NULL, "Error"); + assert(_collectorState == FinalMarking, "Error"); + for (uint j = 0; j < ParallelGCThreads; j++) { + _cursor[j] = 0; + } + HeapWord* top = surv->top(); + size_t i; + for (i = 0; i < _survivor_chunk_capacity; i++) { // all sca entries + HeapWord* min_val = top; // Higher than any PLAB address + uint min_tid = 0; // position of min_val this round + for (uint j = 0; j < ParallelGCThreads; j++) { + ChunkArray* cur_sca = &_survivor_plab_array[j]; + if (_cursor[j] == cur_sca->end()) { + continue; + } + assert(_cursor[j] < cur_sca->end(), "ctl pt invariant"); + HeapWord* cur_val = cur_sca->nth(_cursor[j]); + assert(surv->used_region().contains(cur_val), "Out of bounds value"); + if (cur_val < min_val) { + min_tid = j; + min_val = cur_val; + } else { + assert(cur_val < top, "All recorded addresses should be less"); + } + } + // At this point min_val and min_tid are respectively + // the least address in _survivor_plab_array[j]->nth(_cursor[j]) + // and the thread (j) that witnesses that address. + // We record this address in the _survivor_chunk_array[i] + // and increment _cursor[min_tid] prior to the next round i. + if (min_val == top) { + break; + } + _survivor_chunk_array[i] = min_val; + _cursor[min_tid]++; + } + // We are all done; record the size of the _survivor_chunk_array + _survivor_chunk_index = i; // exclusive: [0, i) + if (PrintCMSStatistics > 0) { + gclog_or_tty->print(" (Survivor:" SIZE_FORMAT "chunks) ", i); + } + // Verify that we used up all the recorded entries + #ifdef ASSERT + size_t total = 0; + for (uint j = 0; j < ParallelGCThreads; j++) { + assert(_cursor[j] == _survivor_plab_array[j].end(), "Ctl pt invariant"); + total += _cursor[j]; + } + assert(total == _survivor_chunk_index, "Ctl Pt Invariant"); + // Check that the merged array is in sorted order + if (total > 0) { + for (size_t i = 0; i < total - 1; i++) { + if (PrintCMSStatistics > 0) { + gclog_or_tty->print(" (chunk" SIZE_FORMAT ":" INTPTR_FORMAT ") ", + i, _survivor_chunk_array[i]); + } + assert(_survivor_chunk_array[i] < _survivor_chunk_array[i+1], + "Not sorted"); + } + } + #endif // ASSERT +} + +// Set up the space's par_seq_tasks structure for work claiming +// for parallel rescan of young gen. +// See ParRescanTask where this is currently used. +void +CMSCollector:: +initialize_sequential_subtasks_for_young_gen_rescan(int n_threads) { + assert(n_threads > 0, "Unexpected n_threads argument"); + DefNewGeneration* dng = (DefNewGeneration*)_young_gen; + + // Eden space + { + SequentialSubTasksDone* pst = dng->eden()->par_seq_tasks(); + assert(!pst->valid(), "Clobbering existing data?"); + // Each valid entry in [0, _eden_chunk_index) represents a task. + size_t n_tasks = _eden_chunk_index + 1; + assert(n_tasks == 1 || _eden_chunk_array != NULL, "Error"); + pst->set_par_threads(n_threads); + pst->set_n_tasks((int)n_tasks); + } + + // Merge the survivor plab arrays into _survivor_chunk_array + if (_survivor_plab_array != NULL) { + merge_survivor_plab_arrays(dng->from()); + } else { + assert(_survivor_chunk_index == 0, "Error"); + } + + // To space + { + SequentialSubTasksDone* pst = dng->to()->par_seq_tasks(); + assert(!pst->valid(), "Clobbering existing data?"); + pst->set_par_threads(n_threads); + pst->set_n_tasks(1); + assert(pst->valid(), "Error"); + } + + // From space + { + SequentialSubTasksDone* pst = dng->from()->par_seq_tasks(); + assert(!pst->valid(), "Clobbering existing data?"); + size_t n_tasks = _survivor_chunk_index + 1; + assert(n_tasks == 1 || _survivor_chunk_array != NULL, "Error"); + pst->set_par_threads(n_threads); + pst->set_n_tasks((int)n_tasks); + assert(pst->valid(), "Error"); + } +} + +// Parallel version of remark +void CMSCollector::do_remark_parallel() { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + WorkGang* workers = gch->workers(); + assert(workers != NULL, "Need parallel worker threads."); + int n_workers = workers->total_workers(); + CompactibleFreeListSpace* cms_space = _cmsGen->cmsSpace(); + CompactibleFreeListSpace* perm_space = _permGen->cmsSpace(); + + CMSParRemarkTask tsk(this, + cms_space, perm_space, + n_workers, workers, task_queues()); + + // Set up for parallel process_strong_roots work. + gch->set_par_threads(n_workers); + gch->change_strong_roots_parity(); + // We won't be iterating over the cards in the card table updating + // the younger_gen cards, so we shouldn't call the following else + // the verification code as well as subsequent younger_refs_iterate + // code would get confused. XXX + // gch->rem_set()->prepare_for_younger_refs_iterate(true); // parallel + + // The young gen rescan work will not be done as part of + // process_strong_roots (which currently doesn't knw how to + // parallelize such a scan), but rather will be broken up into + // a set of parallel tasks (via the sampling that the [abortable] + // preclean phase did of EdenSpace, plus the [two] tasks of + // scanning the [two] survivor spaces. Further fine-grain + // parallelization of the scanning of the survivor spaces + // themselves, and of precleaning of the younger gen itself + // is deferred to the future. + initialize_sequential_subtasks_for_young_gen_rescan(n_workers); + + // The dirty card rescan work is broken up into a "sequence" + // of parallel tasks (per constituent space) that are dynamically + // claimed by the parallel threads. + cms_space->initialize_sequential_subtasks_for_rescan(n_workers); + perm_space->initialize_sequential_subtasks_for_rescan(n_workers); + + // It turns out that even when we're using 1 thread, doing the work in a + // separate thread causes wide variance in run times. We can't help this + // in the multi-threaded case, but we special-case n=1 here to get + // repeatable measurements of the 1-thread overhead of the parallel code. + if (n_workers > 1) { + // Make refs discovery MT-safe + ReferenceProcessorMTMutator mt(ref_processor(), true); + workers->run_task(&tsk); + } else { + tsk.work(0); + } + gch->set_par_threads(0); // 0 ==> non-parallel. + // restore, single-threaded for now, any preserved marks + // as a result of work_q overflow + restore_preserved_marks_if_any(); +} + +// Non-parallel version of remark +void CMSCollector::do_remark_non_parallel() { + ResourceMark rm; + HandleMark hm; + GenCollectedHeap* gch = GenCollectedHeap::heap(); + MarkRefsIntoAndScanClosure + mrias_cl(_span, ref_processor(), &_markBitMap, &_modUnionTable, + &_markStack, &_revisitStack, this, + false /* should_yield */, false /* not precleaning */); + MarkFromDirtyCardsClosure + markFromDirtyCardsClosure(this, _span, + NULL, // space is set further below + &_markBitMap, &_markStack, &_revisitStack, + &mrias_cl); + { + TraceTime t("grey object rescan", PrintGCDetails, false, gclog_or_tty); + // Iterate over the dirty cards, marking them precleaned, and + // setting the corresponding bits in the mod union table. + { + ModUnionClosure modUnionClosure(&_modUnionTable); + _ct->ct_bs()->dirty_card_iterate( + _cmsGen->used_region(), + &modUnionClosure); + _ct->ct_bs()->dirty_card_iterate( + _permGen->used_region(), + &modUnionClosure); + } + // Having transferred these marks into the modUnionTable, we just need + // to rescan the marked objects on the dirty cards in the modUnionTable. + // The initial marking may have been done during an asynchronous + // collection so there may be dirty bits in the mod-union table. + const int alignment = + CardTableModRefBS::card_size * BitsPerWord; + { + // ... First handle dirty cards in CMS gen + markFromDirtyCardsClosure.set_space(_cmsGen->cmsSpace()); + MemRegion ur = _cmsGen->used_region(); + HeapWord* lb = ur.start(); + HeapWord* ub = (HeapWord*)round_to((intptr_t)ur.end(), alignment); + MemRegion cms_span(lb, ub); + _modUnionTable.dirty_range_iterate_clear(cms_span, + &markFromDirtyCardsClosure); + verify_work_stacks_empty(); + if (PrintCMSStatistics != 0) { + gclog_or_tty->print(" (re-scanned "SIZE_FORMAT" dirty cards in cms gen) ", + markFromDirtyCardsClosure.num_dirty_cards()); + } + } + { + // .. and then repeat for dirty cards in perm gen + markFromDirtyCardsClosure.set_space(_permGen->cmsSpace()); + MemRegion ur = _permGen->used_region(); + HeapWord* lb = ur.start(); + HeapWord* ub = (HeapWord*)round_to((intptr_t)ur.end(), alignment); + MemRegion perm_span(lb, ub); + _modUnionTable.dirty_range_iterate_clear(perm_span, + &markFromDirtyCardsClosure); + verify_work_stacks_empty(); + if (PrintCMSStatistics != 0) { + gclog_or_tty->print(" (re-scanned "SIZE_FORMAT" dirty cards in perm gen) ", + markFromDirtyCardsClosure.num_dirty_cards()); + } + } + } + if (VerifyDuringGC && + GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { + HandleMark hm; // Discard invalid handles created during verification + Universe::verify(true); + } + { + TraceTime t("root rescan", PrintGCDetails, false, gclog_or_tty); + + verify_work_stacks_empty(); + + gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. + gch->gen_process_strong_roots(_cmsGen->level(), + true, // younger gens as roots + true, // collecting perm gen + SharedHeap::ScanningOption(roots_scanning_options()), + NULL, &mrias_cl); + } + verify_work_stacks_empty(); + // Restore evacuated mark words, if any, used for overflow list links + if (!CMSOverflowEarlyRestoration) { + restore_preserved_marks_if_any(); + } + verify_overflow_empty(); +} + +//////////////////////////////////////////////////////// +// Parallel Reference Processing Task Proxy Class +//////////////////////////////////////////////////////// +class CMSRefProcTaskProxy: public AbstractGangTask { + typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask; + CMSCollector* _collector; + CMSBitMap* _mark_bit_map; + MemRegion _span; + OopTaskQueueSet* _task_queues; + ParallelTaskTerminator _term; + ProcessTask& _task; + +public: + CMSRefProcTaskProxy(ProcessTask& task, + CMSCollector* collector, + const MemRegion& span, + CMSBitMap* mark_bit_map, + int total_workers, + OopTaskQueueSet* task_queues): + AbstractGangTask("Process referents by policy in parallel"), + _task(task), + _collector(collector), _span(span), _mark_bit_map(mark_bit_map), + _task_queues(task_queues), + _term(total_workers, task_queues) + { } + + OopTaskQueueSet* task_queues() { return _task_queues; } + + OopTaskQueue* work_queue(int i) { return task_queues()->queue(i); } + + ParallelTaskTerminator* terminator() { return &_term; } + + void do_work_steal(int i, + CMSParDrainMarkingStackClosure* drain, + CMSParKeepAliveClosure* keep_alive, + int* seed); + + virtual void work(int i); +}; + +void CMSRefProcTaskProxy::work(int i) { + CMSParKeepAliveClosure par_keep_alive(_collector, _span, + _mark_bit_map, work_queue(i)); + CMSParDrainMarkingStackClosure par_drain_stack(_collector, _span, + _mark_bit_map, work_queue(i)); + CMSIsAliveClosure is_alive_closure(_mark_bit_map); + _task.work(i, is_alive_closure, par_keep_alive, par_drain_stack); + if (_task.marks_oops_alive()) { + do_work_steal(i, &par_drain_stack, &par_keep_alive, + _collector->hash_seed(i)); + } + assert(work_queue(i)->size() == 0, "work_queue should be empty"); + assert(_collector->_overflow_list == NULL, "non-empty _overflow_list"); +} + +class CMSRefEnqueueTaskProxy: public AbstractGangTask { + typedef AbstractRefProcTaskExecutor::EnqueueTask EnqueueTask; + EnqueueTask& _task; + +public: + CMSRefEnqueueTaskProxy(EnqueueTask& task) + : AbstractGangTask("Enqueue reference objects in parallel"), + _task(task) + { } + + virtual void work(int i) + { + _task.work(i); + } +}; + +CMSParKeepAliveClosure::CMSParKeepAliveClosure(CMSCollector* collector, + MemRegion span, CMSBitMap* bit_map, OopTaskQueue* work_queue): + _collector(collector), + _span(span), + _bit_map(bit_map), + _work_queue(work_queue), + _mark_and_push(collector, span, bit_map, work_queue), + _low_water_mark(MIN2((uint)(work_queue->max_elems()/4), + (uint)(CMSWorkQueueDrainThreshold * ParallelGCThreads))) +{ } + +// . see if we can share work_queues with ParNew? XXX +void CMSRefProcTaskProxy::do_work_steal(int i, + CMSParDrainMarkingStackClosure* drain, + CMSParKeepAliveClosure* keep_alive, + int* seed) { + OopTaskQueue* work_q = work_queue(i); + NOT_PRODUCT(int num_steals = 0;) + oop obj_to_scan; + size_t num_from_overflow_list = + MIN2((size_t)work_q->max_elems()/4, + (size_t)ParGCDesiredObjsFromOverflowList); + + while (true) { + // Completely finish any left over work from (an) earlier round(s) + drain->trim_queue(0); + // Now check if there's any work in the overflow list + if (_collector->par_take_from_overflow_list(num_from_overflow_list, + work_q)) { + // Found something in global overflow list; + // not yet ready to go stealing work from others. + // We'd like to assert(work_q->size() != 0, ...) + // because we just took work from the overflow list, + // but of course we can't, since all of that might have + // been already stolen from us. + continue; + } + // Verify that we have no work before we resort to stealing + assert(work_q->size() == 0, "Have work, shouldn't steal"); + // Try to steal from other queues that have work + if (task_queues()->steal(i, seed, /* reference */ obj_to_scan)) { + NOT_PRODUCT(num_steals++;) + assert(obj_to_scan->is_oop(), "Oops, not an oop!"); + assert(_mark_bit_map->isMarked((HeapWord*)obj_to_scan), "Stole an unmarked oop?"); + // Do scanning work + obj_to_scan->oop_iterate(keep_alive); + // Loop around, finish this work, and try to steal some more + } else if (terminator()->offer_termination()) { + break; // nirvana from the infinite cycle + } + } + NOT_PRODUCT( + if (PrintCMSStatistics != 0) { + gclog_or_tty->print("\n\t(%d: stole %d oops)", i, num_steals); + } + ) +} + +void CMSRefProcTaskExecutor::execute(ProcessTask& task) +{ + GenCollectedHeap* gch = GenCollectedHeap::heap(); + WorkGang* workers = gch->workers(); + assert(workers != NULL, "Need parallel worker threads."); + int n_workers = workers->total_workers(); + CMSRefProcTaskProxy rp_task(task, &_collector, + _collector.ref_processor()->span(), + _collector.markBitMap(), + n_workers, _collector.task_queues()); + workers->run_task(&rp_task); +} + +void CMSRefProcTaskExecutor::execute(EnqueueTask& task) +{ + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + WorkGang* workers = gch->workers(); + assert(workers != NULL, "Need parallel worker threads."); + CMSRefEnqueueTaskProxy enq_task(task); + workers->run_task(&enq_task); +} + +void CMSCollector::refProcessingWork(bool asynch, bool clear_all_soft_refs) { + + ResourceMark rm; + HandleMark hm; + ReferencePolicy* soft_ref_policy; + + assert(!ref_processor()->enqueuing_is_done(), "Enqueuing should not be complete"); + // Process weak references. + if (clear_all_soft_refs) { + soft_ref_policy = new AlwaysClearPolicy(); + } else { +#ifdef COMPILER2 + soft_ref_policy = new LRUMaxHeapPolicy(); +#else + soft_ref_policy = new LRUCurrentHeapPolicy(); +#endif // COMPILER2 + } + verify_work_stacks_empty(); + + ReferenceProcessor* rp = ref_processor(); + assert(rp->span().equals(_span), "Spans should be equal"); + CMSKeepAliveClosure cmsKeepAliveClosure(this, _span, &_markBitMap, + &_markStack); + CMSDrainMarkingStackClosure cmsDrainMarkingStackClosure(this, + _span, &_markBitMap, &_markStack, + &cmsKeepAliveClosure); + { + TraceTime t("weak refs processing", PrintGCDetails, false, gclog_or_tty); + if (rp->processing_is_mt()) { + CMSRefProcTaskExecutor task_executor(*this); + rp->process_discovered_references(soft_ref_policy, + &_is_alive_closure, + &cmsKeepAliveClosure, + &cmsDrainMarkingStackClosure, + &task_executor); + } else { + rp->process_discovered_references(soft_ref_policy, + &_is_alive_closure, + &cmsKeepAliveClosure, + &cmsDrainMarkingStackClosure, + NULL); + } + verify_work_stacks_empty(); + } + + if (cms_should_unload_classes()) { + { + TraceTime t("class unloading", PrintGCDetails, false, gclog_or_tty); + + // Follow SystemDictionary roots and unload classes + bool purged_class = SystemDictionary::do_unloading(&_is_alive_closure); + + // Follow CodeCache roots and unload any methods marked for unloading + CodeCache::do_unloading(&_is_alive_closure, + &cmsKeepAliveClosure, + purged_class); + + cmsDrainMarkingStackClosure.do_void(); + verify_work_stacks_empty(); + + // Update subklass/sibling/implementor links in KlassKlass descendants + assert(!_revisitStack.isEmpty(), "revisit stack should not be empty"); + oop k; + while ((k = _revisitStack.pop()) != NULL) { + ((Klass*)(oopDesc*)k)->follow_weak_klass_links( + &_is_alive_closure, + &cmsKeepAliveClosure); + } + assert(!ClassUnloading || + (_markStack.isEmpty() && overflow_list_is_empty()), + "Should not have found new reachable objects"); + assert(_revisitStack.isEmpty(), "revisit stack should have been drained"); + cmsDrainMarkingStackClosure.do_void(); + verify_work_stacks_empty(); + } + + { + TraceTime t("scrub symbol & string tables", PrintGCDetails, false, gclog_or_tty); + // Now clean up stale oops in SymbolTable and StringTable + SymbolTable::unlink(&_is_alive_closure); + StringTable::unlink(&_is_alive_closure); + } + } + + verify_work_stacks_empty(); + // Restore any preserved marks as a result of mark stack or + // work queue overflow + restore_preserved_marks_if_any(); // done single-threaded for now + + rp->set_enqueuing_is_done(true); + if (rp->processing_is_mt()) { + CMSRefProcTaskExecutor task_executor(*this); + rp->enqueue_discovered_references(&task_executor); + } else { + rp->enqueue_discovered_references(NULL); + } + rp->verify_no_references_recorded(); + assert(!rp->discovery_enabled(), "should have been disabled"); + + // JVMTI object tagging is based on JNI weak refs. If any of these + // refs were cleared then JVMTI needs to update its maps and + // maybe post ObjectFrees to agents. + JvmtiExport::cms_ref_processing_epilogue(); +} + +#ifndef PRODUCT +void CMSCollector::check_correct_thread_executing() { + Thread* t = Thread::current(); + // Only the VM thread or the CMS thread should be here. + assert(t->is_ConcurrentGC_thread() || t->is_VM_thread(), + "Unexpected thread type"); + // If this is the vm thread, the foreground process + // should not be waiting. Note that _foregroundGCIsActive is + // true while the foreground collector is waiting. + if (_foregroundGCShouldWait) { + // We cannot be the VM thread + assert(t->is_ConcurrentGC_thread(), + "Should be CMS thread"); + } else { + // We can be the CMS thread only if we are in a stop-world + // phase of CMS collection. + if (t->is_ConcurrentGC_thread()) { + assert(_collectorState == InitialMarking || + _collectorState == FinalMarking, + "Should be a stop-world phase"); + // The CMS thread should be holding the CMS_token. + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "Potential interference with concurrently " + "executing VM thread"); + } + } +} +#endif + +void CMSCollector::sweep(bool asynch) { + assert(_collectorState == Sweeping, "just checking"); + check_correct_thread_executing(); + verify_work_stacks_empty(); + verify_overflow_empty(); + incrementSweepCount(); + _sweep_timer.stop(); + _sweep_estimate.sample(_sweep_timer.seconds()); + size_policy()->avg_cms_free_at_sweep()->sample(_cmsGen->free()); + + // PermGen verification support: If perm gen sweeping is disabled in + // this cycle, we preserve the perm gen object "deadness" information + // in the perm_gen_verify_bit_map. In order to do that we traverse + // all blocks in perm gen and mark all dead objects. + if (verifying() && !cms_should_unload_classes()) { + CMSTokenSyncWithLocks ts(true, _permGen->freelistLock(), + bitMapLock()); + assert(perm_gen_verify_bit_map()->sizeInBits() != 0, + "Should have already been allocated"); + MarkDeadObjectsClosure mdo(this, _permGen->cmsSpace(), + markBitMap(), perm_gen_verify_bit_map()); + _permGen->cmsSpace()->blk_iterate(&mdo); + } + + if (asynch) { + TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); + CMSPhaseAccounting pa(this, "sweep", !PrintGCDetails); + // First sweep the old gen then the perm gen + { + CMSTokenSyncWithLocks ts(true, _cmsGen->freelistLock(), + bitMapLock()); + sweepWork(_cmsGen, asynch); + } + + // Now repeat for perm gen + if (cms_should_unload_classes()) { + CMSTokenSyncWithLocks ts(true, _permGen->freelistLock(), + bitMapLock()); + sweepWork(_permGen, asynch); + } + + // Update Universe::_heap_*_at_gc figures. + // We need all the free list locks to make the abstract state + // transition from Sweeping to Resetting. See detailed note + // further below. + { + CMSTokenSyncWithLocks ts(true, _cmsGen->freelistLock(), + _permGen->freelistLock()); + // Update heap occupancy information which is used as + // input to soft ref clearing policy at the next gc. + Universe::update_heap_info_at_gc(); + _collectorState = Resizing; + } + } else { + // already have needed locks + sweepWork(_cmsGen, asynch); + + if (cms_should_unload_classes()) { + sweepWork(_permGen, asynch); + } + // Update heap occupancy information which is used as + // input to soft ref clearing policy at the next gc. + Universe::update_heap_info_at_gc(); + _collectorState = Resizing; + } + verify_work_stacks_empty(); + verify_overflow_empty(); + + _sweep_timer.reset(); + _sweep_timer.start(); + + update_time_of_last_gc(os::javaTimeMillis()); + + // NOTE on abstract state transitions: + // Mutators allocate-live and/or mark the mod-union table dirty + // based on the state of the collection. The former is done in + // the interval [Marking, Sweeping] and the latter in the interval + // [Marking, Sweeping). Thus the transitions into the Marking state + // and out of the Sweeping state must be synchronously visible + // globally to the mutators. + // The transition into the Marking state happens with the world + // stopped so the mutators will globally see it. Sweeping is + // done asynchronously by the background collector so the transition + // from the Sweeping state to the Resizing state must be done + // under the freelistLock (as is the check for whether to + // allocate-live and whether to dirty the mod-union table). + assert(_collectorState == Resizing, "Change of collector state to" + " Resizing must be done under the freelistLocks (plural)"); + + // Now that sweeping has been completed, if the GCH's + // incremental_collection_will_fail flag is set, clear it, + // thus inviting a younger gen collection to promote into + // this generation. If such a promotion may still fail, + // the flag will be set again when a young collection is + // attempted. + // I think the incremental_collection_will_fail flag's use + // is specific to a 2 generation collection policy, so i'll + // assert that that's the configuration we are operating within. + // The use of the flag can and should be generalized appropriately + // in the future to deal with a general n-generation system. + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->collector_policy()->is_two_generation_policy(), + "Resetting of incremental_collection_will_fail flag" + " may be incorrect otherwise"); + gch->clear_incremental_collection_will_fail(); + gch->update_full_collections_completed(_collection_count_start); +} + +// FIX ME!!! Looks like this belongs in CFLSpace, with +// CMSGen merely delegating to it. +void ConcurrentMarkSweepGeneration::setNearLargestChunk() { + double nearLargestPercent = 0.999; + HeapWord* minAddr = _cmsSpace->bottom(); + HeapWord* largestAddr = + (HeapWord*) _cmsSpace->dictionary()->findLargestDict(); + if (largestAddr == 0) { + // The dictionary appears to be empty. In this case + // try to coalesce at the end of the heap. + largestAddr = _cmsSpace->end(); + } + size_t largestOffset = pointer_delta(largestAddr, minAddr); + size_t nearLargestOffset = + (size_t)((double)largestOffset * nearLargestPercent) - MinChunkSize; + _cmsSpace->set_nearLargestChunk(minAddr + nearLargestOffset); +} + +bool ConcurrentMarkSweepGeneration::isNearLargestChunk(HeapWord* addr) { + return addr >= _cmsSpace->nearLargestChunk(); +} + +FreeChunk* ConcurrentMarkSweepGeneration::find_chunk_at_end() { + return _cmsSpace->find_chunk_at_end(); +} + +void ConcurrentMarkSweepGeneration::update_gc_stats(int current_level, + bool full) { + // The next lower level has been collected. Gather any statistics + // that are of interest at this point. + if (!full && (current_level + 1) == level()) { + // Gather statistics on the young generation collection. + collector()->stats().record_gc0_end(used()); + } +} + +CMSAdaptiveSizePolicy* ConcurrentMarkSweepGeneration::size_policy() { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->kind() == CollectedHeap::GenCollectedHeap, + "Wrong type of heap"); + CMSAdaptiveSizePolicy* sp = (CMSAdaptiveSizePolicy*) + gch->gen_policy()->size_policy(); + assert(sp->is_gc_cms_adaptive_size_policy(), + "Wrong type of size policy"); + return sp; +} + +void ConcurrentMarkSweepGeneration::rotate_debug_collection_type() { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print("Rotate from %d ", _debug_collection_type); + } + _debug_collection_type = (CollectionTypes) (_debug_collection_type + 1); + _debug_collection_type = + (CollectionTypes) (_debug_collection_type % Unknown_collection_type); + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("to %d ", _debug_collection_type); + } +} + +void CMSCollector::sweepWork(ConcurrentMarkSweepGeneration* gen, + bool asynch) { + // We iterate over the space(s) underlying this generation, + // checking the mark bit map to see if the bits corresponding + // to specific blocks are marked or not. Blocks that are + // marked are live and are not swept up. All remaining blocks + // are swept up, with coalescing on-the-fly as we sweep up + // contiguous free and/or garbage blocks: + // We need to ensure that the sweeper synchronizes with allocators + // and stop-the-world collectors. In particular, the following + // locks are used: + // . CMS token: if this is held, a stop the world collection cannot occur + // . freelistLock: if this is held no allocation can occur from this + // generation by another thread + // . bitMapLock: if this is held, no other thread can access or update + // + + // Note that we need to hold the freelistLock if we use + // block iterate below; else the iterator might go awry if + // a mutator (or promotion) causes block contents to change + // (for instance if the allocator divvies up a block). + // If we hold the free list lock, for all practical purposes + // young generation GC's can't occur (they'll usually need to + // promote), so we might as well prevent all young generation + // GC's while we do a sweeping step. For the same reason, we might + // as well take the bit map lock for the entire duration + + // check that we hold the requisite locks + assert(have_cms_token(), "Should hold cms token"); + assert( (asynch && ConcurrentMarkSweepThread::cms_thread_has_cms_token()) + || (!asynch && ConcurrentMarkSweepThread::vm_thread_has_cms_token()), + "Should possess CMS token to sweep"); + assert_lock_strong(gen->freelistLock()); + assert_lock_strong(bitMapLock()); + + assert(!_sweep_timer.is_active(), "Was switched off in an outer context"); + gen->cmsSpace()->beginSweepFLCensus((float)(_sweep_timer.seconds()), + _sweep_estimate.padded_average()); + gen->setNearLargestChunk(); + + { + SweepClosure sweepClosure(this, gen, &_markBitMap, + CMSYield && asynch); + gen->cmsSpace()->blk_iterate_careful(&sweepClosure); + // We need to free-up/coalesce garbage/blocks from a + // co-terminal free run. This is done in the SweepClosure + // destructor; so, do not remove this scope, else the + // end-of-sweep-census below will be off by a little bit. + } + gen->cmsSpace()->sweep_completed(); + gen->cmsSpace()->endSweepFLCensus(sweepCount()); +} + +// Reset CMS data structures (for now just the marking bit map) +// preparatory for the next cycle. +void CMSCollector::reset(bool asynch) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + CMSAdaptiveSizePolicy* sp = size_policy(); + AdaptiveSizePolicyOutput(sp, gch->total_collections()); + if (asynch) { + CMSTokenSyncWithLocks ts(true, bitMapLock()); + + // If the state is not "Resetting", the foreground thread + // has done a collection and the resetting. + if (_collectorState != Resetting) { + assert(_collectorState == Idling, "The state should only change" + " because the foreground collector has finished the collection"); + return; + } + + // Clear the mark bitmap (no grey objects to start with) + // for the next cycle. + TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); + CMSPhaseAccounting cmspa(this, "reset", !PrintGCDetails); + + HeapWord* curAddr = _markBitMap.startWord(); + while (curAddr < _markBitMap.endWord()) { + size_t remaining = pointer_delta(_markBitMap.endWord(), curAddr); + MemRegion chunk(curAddr, MIN2(CMSBitMapYieldQuantum, remaining)); + _markBitMap.clear_large_range(chunk); + if (ConcurrentMarkSweepThread::should_yield() && + !foregroundGCIsActive() && + CMSYield) { + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "CMS thread should hold CMS token"); + assert_lock_strong(bitMapLock()); + bitMapLock()->unlock(); + ConcurrentMarkSweepThread::desynchronize(true); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + stopTimer(); + if (PrintCMSStatistics != 0) { + incrementYields(); + } + icms_wait(); + + // See the comment in coordinator_yield() + for (unsigned i = 0; i < CMSYieldSleepCount && + ConcurrentMarkSweepThread::should_yield() && + !CMSCollector::foregroundGCIsActive(); ++i) { + os::sleep(Thread::current(), 1, false); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + } + + ConcurrentMarkSweepThread::synchronize(true); + bitMapLock()->lock_without_safepoint_check(); + startTimer(); + } + curAddr = chunk.end(); + } + _collectorState = Idling; + } else { + // already have the lock + assert(_collectorState == Resetting, "just checking"); + assert_lock_strong(bitMapLock()); + _markBitMap.clear_all(); + _collectorState = Idling; + } + + // Stop incremental mode after a cycle completes, so that any future cycles + // are triggered by allocation. + stop_icms(); + + NOT_PRODUCT( + if (RotateCMSCollectionTypes) { + _cmsGen->rotate_debug_collection_type(); + } + ) +} + +void CMSCollector::do_CMS_operation(CMS_op_type op) { + gclog_or_tty->date_stamp(PrintGC && PrintGCDateStamps); + TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); + TraceTime t("GC", PrintGC, !PrintGCDetails, gclog_or_tty); + TraceCollectorStats tcs(counters()); + + switch (op) { + case CMS_op_checkpointRootsInitial: { + checkpointRootsInitial(true); // asynch + if (PrintGC) { + _cmsGen->printOccupancy("initial-mark"); + } + break; + } + case CMS_op_checkpointRootsFinal: { + checkpointRootsFinal(true, // asynch + false, // !clear_all_soft_refs + false); // !init_mark_was_synchronous + if (PrintGC) { + _cmsGen->printOccupancy("remark"); + } + break; + } + default: + fatal("No such CMS_op"); + } +} + +#ifndef PRODUCT +size_t const CMSCollector::skip_header_HeapWords() { + return FreeChunk::header_size(); +} + +// Try and collect here conditions that should hold when +// CMS thread is exiting. The idea is that the foreground GC +// thread should not be blocked if it wants to terminate +// the CMS thread and yet continue to run the VM for a while +// after that. +void CMSCollector::verify_ok_to_terminate() const { + assert(Thread::current()->is_ConcurrentGC_thread(), + "should be called by CMS thread"); + assert(!_foregroundGCShouldWait, "should be false"); + // We could check here that all the various low-level locks + // are not held by the CMS thread, but that is overkill; see + // also CMSThread::verify_ok_to_terminate() where the CGC_lock + // is checked. +} +#endif + +size_t CMSCollector::block_size_using_printezis_bits(HeapWord* addr) const { + assert(_markBitMap.isMarked(addr) && _markBitMap.isMarked(addr + 1), + "missing Printezis mark?"); + HeapWord* nextOneAddr = _markBitMap.getNextMarkedWordAddress(addr + 2); + size_t size = pointer_delta(nextOneAddr + 1, addr); + assert(size == CompactibleFreeListSpace::adjustObjectSize(size), + "alignment problem"); + assert(size >= 3, "Necessary for Printezis marks to work"); + return size; +} + +// A variant of the above (block_size_using_printezis_bits()) except +// that we return 0 if the P-bits are not yet set. +size_t CMSCollector::block_size_if_printezis_bits(HeapWord* addr) const { + if (_markBitMap.isMarked(addr)) { + assert(_markBitMap.isMarked(addr + 1), "Missing Printezis bit?"); + HeapWord* nextOneAddr = _markBitMap.getNextMarkedWordAddress(addr + 2); + size_t size = pointer_delta(nextOneAddr + 1, addr); + assert(size == CompactibleFreeListSpace::adjustObjectSize(size), + "alignment problem"); + assert(size >= 3, "Necessary for Printezis marks to work"); + return size; + } else { + assert(!_markBitMap.isMarked(addr + 1), "Bit map inconsistency?"); + return 0; + } +} + +HeapWord* CMSCollector::next_card_start_after_block(HeapWord* addr) const { + size_t sz = 0; + oop p = (oop)addr; + if (p->klass() != NULL && p->is_parsable()) { + sz = CompactibleFreeListSpace::adjustObjectSize(p->size()); + } else { + sz = block_size_using_printezis_bits(addr); + } + assert(sz > 0, "size must be nonzero"); + HeapWord* next_block = addr + sz; + HeapWord* next_card = (HeapWord*)round_to((uintptr_t)next_block, + CardTableModRefBS::card_size); + assert(round_down((uintptr_t)addr, CardTableModRefBS::card_size) < + round_down((uintptr_t)next_card, CardTableModRefBS::card_size), + "must be different cards"); + return next_card; +} + + +// CMS Bit Map Wrapper ///////////////////////////////////////// + +// Construct a CMS bit map infrastructure, but don't create the +// bit vector itself. That is done by a separate call CMSBitMap::allocate() +// further below. +CMSBitMap::CMSBitMap(int shifter, int mutex_rank, const char* mutex_name): + _bm(NULL,0), + _shifter(shifter), + _lock(mutex_rank >= 0 ? new Mutex(mutex_rank, mutex_name, true) : NULL) +{ + _bmStartWord = 0; + _bmWordSize = 0; +} + +bool CMSBitMap::allocate(MemRegion mr) { + _bmStartWord = mr.start(); + _bmWordSize = mr.word_size(); + ReservedSpace brs(ReservedSpace::allocation_align_size_up( + (_bmWordSize >> (_shifter + LogBitsPerByte)) + 1)); + if (!brs.is_reserved()) { + warning("CMS bit map allocation failure"); + return false; + } + // For now we'll just commit all of the bit map up fromt. + // Later on we'll try to be more parsimonious with swap. + if (!_virtual_space.initialize(brs, brs.size())) { + warning("CMS bit map backing store failure"); + return false; + } + assert(_virtual_space.committed_size() == brs.size(), + "didn't reserve backing store for all of CMS bit map?"); + _bm.set_map((uintptr_t*)_virtual_space.low()); + assert(_virtual_space.committed_size() << (_shifter + LogBitsPerByte) >= + _bmWordSize, "inconsistency in bit map sizing"); + _bm.set_size(_bmWordSize >> _shifter); + + // bm.clear(); // can we rely on getting zero'd memory? verify below + assert(isAllClear(), + "Expected zero'd memory from ReservedSpace constructor"); + assert(_bm.size() == heapWordDiffToOffsetDiff(sizeInWords()), + "consistency check"); + return true; +} + +void CMSBitMap::dirty_range_iterate_clear(MemRegion mr, MemRegionClosure* cl) { + HeapWord *next_addr, *end_addr, *last_addr; + assert_locked(); + assert(covers(mr), "out-of-range error"); + // XXX assert that start and end are appropriately aligned + for (next_addr = mr.start(), end_addr = mr.end(); + next_addr < end_addr; next_addr = last_addr) { + MemRegion dirty_region = getAndClearMarkedRegion(next_addr, end_addr); + last_addr = dirty_region.end(); + if (!dirty_region.is_empty()) { + cl->do_MemRegion(dirty_region); + } else { + assert(last_addr == end_addr, "program logic"); + return; + } + } +} + +#ifndef PRODUCT +void CMSBitMap::assert_locked() const { + CMSLockVerifier::assert_locked(lock()); +} + +bool CMSBitMap::covers(MemRegion mr) const { + // assert(_bm.map() == _virtual_space.low(), "map inconsistency"); + assert((size_t)_bm.size() == (_bmWordSize >> _shifter), + "size inconsistency"); + return (mr.start() >= _bmStartWord) && + (mr.end() <= endWord()); +} + +bool CMSBitMap::covers(HeapWord* start, size_t size) const { + return (start >= _bmStartWord && (start + size) <= endWord()); +} + +void CMSBitMap::verifyNoOneBitsInRange(HeapWord* left, HeapWord* right) { + // verify that there are no 1 bits in the interval [left, right) + FalseBitMapClosure falseBitMapClosure; + iterate(&falseBitMapClosure, left, right); +} + +void CMSBitMap::region_invariant(MemRegion mr) +{ + assert_locked(); + // mr = mr.intersection(MemRegion(_bmStartWord, _bmWordSize)); + assert(!mr.is_empty(), "unexpected empty region"); + assert(covers(mr), "mr should be covered by bit map"); + // convert address range into offset range + size_t start_ofs = heapWordToOffset(mr.start()); + // Make sure that end() is appropriately aligned + assert(mr.end() == (HeapWord*)round_to((intptr_t)mr.end(), + (1 << (_shifter+LogHeapWordSize))), + "Misaligned mr.end()"); + size_t end_ofs = heapWordToOffset(mr.end()); + assert(end_ofs > start_ofs, "Should mark at least one bit"); +} + +#endif + +bool CMSMarkStack::allocate(size_t size) { + // allocate a stack of the requisite depth + ReservedSpace rs(ReservedSpace::allocation_align_size_up( + size * sizeof(oop))); + if (!rs.is_reserved()) { + warning("CMSMarkStack allocation failure"); + return false; + } + if (!_virtual_space.initialize(rs, rs.size())) { + warning("CMSMarkStack backing store failure"); + return false; + } + assert(_virtual_space.committed_size() == rs.size(), + "didn't reserve backing store for all of CMS stack?"); + _base = (oop*)(_virtual_space.low()); + _index = 0; + _capacity = size; + NOT_PRODUCT(_max_depth = 0); + return true; +} + +// XXX FIX ME !!! In the MT case we come in here holding a +// leaf lock. For printing we need to take a further lock +// which has lower rank. We need to recallibrate the two +// lock-ranks involved in order to be able to rpint the +// messages below. (Or defer the printing to the caller. +// For now we take the expedient path of just disabling the +// messages for the problematic case.) +void CMSMarkStack::expand() { + assert(_capacity <= CMSMarkStackSizeMax, "stack bigger than permitted"); + if (_capacity == CMSMarkStackSizeMax) { + if (_hit_limit++ == 0 && !CMSConcurrentMTEnabled && PrintGCDetails) { + // We print a warning message only once per CMS cycle. + gclog_or_tty->print_cr(" (benign) Hit CMSMarkStack max size limit"); + } + return; + } + // Double capacity if possible + size_t new_capacity = MIN2(_capacity*2, CMSMarkStackSizeMax); + // Do not give up existing stack until we have managed to + // get the double capacity that we desired. + ReservedSpace rs(ReservedSpace::allocation_align_size_up( + new_capacity * sizeof(oop))); + if (rs.is_reserved()) { + // Release the backing store associated with old stack + _virtual_space.release(); + // Reinitialize virtual space for new stack + if (!_virtual_space.initialize(rs, rs.size())) { + fatal("Not enough swap for expanded marking stack"); + } + _base = (oop*)(_virtual_space.low()); + _index = 0; + _capacity = new_capacity; + } else if (_failed_double++ == 0 && !CMSConcurrentMTEnabled && PrintGCDetails) { + // Failed to double capacity, continue; + // we print a detail message only once per CMS cycle. + gclog_or_tty->print(" (benign) Failed to expand marking stack from "SIZE_FORMAT"K to " + SIZE_FORMAT"K", + _capacity / K, new_capacity / K); + } +} + + +// Closures +// XXX: there seems to be a lot of code duplication here; +// should refactor and consolidate common code. + +// This closure is used to mark refs into the CMS generation in +// the CMS bit map. Called at the first checkpoint. This closure +// assumes that we do not need to re-mark dirty cards; if the CMS +// generation on which this is used is not an oldest (modulo perm gen) +// generation then this will lose younger_gen cards! + +MarkRefsIntoClosure::MarkRefsIntoClosure( + MemRegion span, CMSBitMap* bitMap, bool should_do_nmethods): + _span(span), + _bitMap(bitMap), + _should_do_nmethods(should_do_nmethods) +{ + assert(_ref_processor == NULL, "deliberately left NULL"); + assert(_bitMap->covers(_span), "_bitMap/_span mismatch"); +} + +void MarkRefsIntoClosure::do_oop(oop* p) { + // if p points into _span, then mark corresponding bit in _markBitMap + oop thisOop = *p; + if (thisOop != NULL) { + assert(thisOop->is_oop(), "expected an oop"); + HeapWord* addr = (HeapWord*)thisOop; + if (_span.contains(addr)) { + // this should be made more efficient + _bitMap->mark(addr); + } + } +} + +// A variant of the above, used for CMS marking verification. +MarkRefsIntoVerifyClosure::MarkRefsIntoVerifyClosure( + MemRegion span, CMSBitMap* verification_bm, CMSBitMap* cms_bm, + bool should_do_nmethods): + _span(span), + _verification_bm(verification_bm), + _cms_bm(cms_bm), + _should_do_nmethods(should_do_nmethods) { + assert(_ref_processor == NULL, "deliberately left NULL"); + assert(_verification_bm->covers(_span), "_verification_bm/_span mismatch"); +} + +void MarkRefsIntoVerifyClosure::do_oop(oop* p) { + // if p points into _span, then mark corresponding bit in _markBitMap + oop this_oop = *p; + if (this_oop != NULL) { + assert(this_oop->is_oop(), "expected an oop"); + HeapWord* addr = (HeapWord*)this_oop; + if (_span.contains(addr)) { + _verification_bm->mark(addr); + if (!_cms_bm->isMarked(addr)) { + oop(addr)->print(); + gclog_or_tty->print_cr(" ("INTPTR_FORMAT" should have been marked)", addr); + fatal("... aborting"); + } + } + } +} + +////////////////////////////////////////////////// +// MarkRefsIntoAndScanClosure +////////////////////////////////////////////////// + +MarkRefsIntoAndScanClosure::MarkRefsIntoAndScanClosure(MemRegion span, + ReferenceProcessor* rp, + CMSBitMap* bit_map, + CMSBitMap* mod_union_table, + CMSMarkStack* mark_stack, + CMSMarkStack* revisit_stack, + CMSCollector* collector, + bool should_yield, + bool concurrent_precleaning): + _collector(collector), + _span(span), + _bit_map(bit_map), + _mark_stack(mark_stack), + _pushAndMarkClosure(collector, span, rp, bit_map, mod_union_table, + mark_stack, revisit_stack, concurrent_precleaning), + _yield(should_yield), + _concurrent_precleaning(concurrent_precleaning), + _freelistLock(NULL) +{ + _ref_processor = rp; + assert(_ref_processor != NULL, "_ref_processor shouldn't be NULL"); +} + +// This closure is used to mark refs into the CMS generation at the +// second (final) checkpoint, and to scan and transitively follow +// the unmarked oops. It is also used during the concurrent precleaning +// phase while scanning objects on dirty cards in the CMS generation. +// The marks are made in the marking bit map and the marking stack is +// used for keeping the (newly) grey objects during the scan. +// The parallel version (Par_...) appears further below. +void MarkRefsIntoAndScanClosure::do_oop(oop* p) { + oop this_oop = *p; + if (this_oop != NULL) { + assert(this_oop->is_oop(), "expected an oop"); + HeapWord* addr = (HeapWord*)this_oop; + assert(_mark_stack->isEmpty(), "post-condition (eager drainage)"); + assert(_collector->overflow_list_is_empty(), "should be empty"); + if (_span.contains(addr) && + !_bit_map->isMarked(addr)) { + // mark bit map (object is now grey) + _bit_map->mark(addr); + // push on marking stack (stack should be empty), and drain the + // stack by applying this closure to the oops in the oops popped + // from the stack (i.e. blacken the grey objects) + bool res = _mark_stack->push(this_oop); + assert(res, "Should have space to push on empty stack"); + do { + oop new_oop = _mark_stack->pop(); + assert(new_oop != NULL && new_oop->is_oop(), "Expected an oop"); + assert(new_oop->is_parsable(), "Found unparsable oop"); + assert(_bit_map->isMarked((HeapWord*)new_oop), + "only grey objects on this stack"); + // iterate over the oops in this oop, marking and pushing + // the ones in CMS heap (i.e. in _span). + new_oop->oop_iterate(&_pushAndMarkClosure); + // check if it's time to yield + do_yield_check(); + } while (!_mark_stack->isEmpty() || + (!_concurrent_precleaning && take_from_overflow_list())); + // if marking stack is empty, and we are not doing this + // during precleaning, then check the overflow list + } + assert(_mark_stack->isEmpty(), "post-condition (eager drainage)"); + assert(_collector->overflow_list_is_empty(), + "overflow list was drained above"); + // We could restore evacuated mark words, if any, used for + // overflow list links here because the overflow list is + // provably empty here. That would reduce the maximum + // size requirements for preserved_{oop,mark}_stack. + // But we'll just postpone it until we are all done + // so we can just stream through. + if (!_concurrent_precleaning && CMSOverflowEarlyRestoration) { + _collector->restore_preserved_marks_if_any(); + assert(_collector->no_preserved_marks(), "No preserved marks"); + } + assert(!CMSOverflowEarlyRestoration || _collector->no_preserved_marks(), + "All preserved marks should have been restored above"); + } +} + +void MarkRefsIntoAndScanClosure::do_yield_work() { + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "CMS thread should hold CMS token"); + assert_lock_strong(_freelistLock); + assert_lock_strong(_bit_map->lock()); + // relinquish the free_list_lock and bitMaplock() + _bit_map->lock()->unlock(); + _freelistLock->unlock(); + ConcurrentMarkSweepThread::desynchronize(true); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + _collector->stopTimer(); + GCPauseTimer p(_collector->size_policy()->concurrent_timer_ptr()); + if (PrintCMSStatistics != 0) { + _collector->incrementYields(); + } + _collector->icms_wait(); + + // See the comment in coordinator_yield() + for (unsigned i = 0; i < CMSYieldSleepCount && + ConcurrentMarkSweepThread::should_yield() && + !CMSCollector::foregroundGCIsActive(); ++i) { + os::sleep(Thread::current(), 1, false); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + } + + ConcurrentMarkSweepThread::synchronize(true); + _freelistLock->lock_without_safepoint_check(); + _bit_map->lock()->lock_without_safepoint_check(); + _collector->startTimer(); +} + +/////////////////////////////////////////////////////////// +// Par_MarkRefsIntoAndScanClosure: a parallel version of +// MarkRefsIntoAndScanClosure +/////////////////////////////////////////////////////////// +Par_MarkRefsIntoAndScanClosure::Par_MarkRefsIntoAndScanClosure( + CMSCollector* collector, MemRegion span, ReferenceProcessor* rp, + CMSBitMap* bit_map, OopTaskQueue* work_queue, CMSMarkStack* revisit_stack): + _span(span), + _bit_map(bit_map), + _work_queue(work_queue), + _low_water_mark(MIN2((uint)(work_queue->max_elems()/4), + (uint)(CMSWorkQueueDrainThreshold * ParallelGCThreads))), + _par_pushAndMarkClosure(collector, span, rp, bit_map, work_queue, + revisit_stack) +{ + _ref_processor = rp; + assert(_ref_processor != NULL, "_ref_processor shouldn't be NULL"); +} + +// This closure is used to mark refs into the CMS generation at the +// second (final) checkpoint, and to scan and transitively follow +// the unmarked oops. The marks are made in the marking bit map and +// the work_queue is used for keeping the (newly) grey objects during +// the scan phase whence they are also available for stealing by parallel +// threads. Since the marking bit map is shared, updates are +// synchronized (via CAS). +void Par_MarkRefsIntoAndScanClosure::do_oop(oop* p) { + oop this_oop = *p; + if (this_oop != NULL) { + // Ignore mark word because this could be an already marked oop + // that may be chained at the end of the overflow list. + assert(this_oop->is_oop(true /* ignore mark word */), "expected an oop"); + HeapWord* addr = (HeapWord*)this_oop; + if (_span.contains(addr) && + !_bit_map->isMarked(addr)) { + // mark bit map (object will become grey): + // It is possible for several threads to be + // trying to "claim" this object concurrently; + // the unique thread that succeeds in marking the + // object first will do the subsequent push on + // to the work queue (or overflow list). + if (_bit_map->par_mark(addr)) { + // push on work_queue (which may not be empty), and trim the + // queue to an appropriate length by applying this closure to + // the oops in the oops popped from the stack (i.e. blacken the + // grey objects) + bool res = _work_queue->push(this_oop); + assert(res, "Low water mark should be less than capacity?"); + trim_queue(_low_water_mark); + } // Else, another thread claimed the object + } + } +} + +// This closure is used to rescan the marked objects on the dirty cards +// in the mod union table and the card table proper. +size_t ScanMarkedObjectsAgainCarefullyClosure::do_object_careful_m( + oop p, MemRegion mr) { + + size_t size = 0; + HeapWord* addr = (HeapWord*)p; + DEBUG_ONLY(_collector->verify_work_stacks_empty();) + assert(_span.contains(addr), "we are scanning the CMS generation"); + // check if it's time to yield + if (do_yield_check()) { + // We yielded for some foreground stop-world work, + // and we have been asked to abort this ongoing preclean cycle. + return 0; + } + if (_bitMap->isMarked(addr)) { + // it's marked; is it potentially uninitialized? + if (p->klass() != NULL) { + if (CMSPermGenPrecleaningEnabled && !p->is_parsable()) { + // Signal precleaning to redirty the card since + // the klass pointer is already installed. + assert(size == 0, "Initial value"); + } else { + assert(p->is_parsable(), "must be parsable."); + // an initialized object; ignore mark word in verification below + // since we are running concurrent with mutators + assert(p->is_oop(true), "should be an oop"); + if (p->is_objArray()) { + // objArrays are precisely marked; restrict scanning + // to dirty cards only. + size = p->oop_iterate(_scanningClosure, mr); + assert(size == CompactibleFreeListSpace::adjustObjectSize(size), + "adjustObjectSize should be the identity for array sizes, " + "which are necessarily larger than minimum object size of " + "two heap words"); + } else { + // A non-array may have been imprecisely marked; we need + // to scan object in its entirety. + size = CompactibleFreeListSpace::adjustObjectSize( + p->oop_iterate(_scanningClosure)); + } + #ifdef DEBUG + size_t direct_size = + CompactibleFreeListSpace::adjustObjectSize(p->size()); + assert(size == direct_size, "Inconsistency in size"); + assert(size >= 3, "Necessary for Printezis marks to work"); + if (!_bitMap->isMarked(addr+1)) { + _bitMap->verifyNoOneBitsInRange(addr+2, addr+size); + } else { + _bitMap->verifyNoOneBitsInRange(addr+2, addr+size-1); + assert(_bitMap->isMarked(addr+size-1), + "inconsistent Printezis mark"); + } + #endif // DEBUG + } + } else { + // an unitialized object + assert(_bitMap->isMarked(addr+1), "missing Printezis mark?"); + HeapWord* nextOneAddr = _bitMap->getNextMarkedWordAddress(addr + 2); + size = pointer_delta(nextOneAddr + 1, addr); + assert(size == CompactibleFreeListSpace::adjustObjectSize(size), + "alignment problem"); + // Note that pre-cleaning needn't redirty the card. OopDesc::set_klass() + // will dirty the card when the klass pointer is installed in the + // object (signalling the completion of initialization). + } + } else { + // Either a not yet marked object or an uninitialized object + if (p->klass() == NULL || !p->is_parsable()) { + // An uninitialized object, skip to the next card, since + // we may not be able to read its P-bits yet. + assert(size == 0, "Initial value"); + } else { + // An object not (yet) reached by marking: we merely need to + // compute its size so as to go look at the next block. + assert(p->is_oop(true), "should be an oop"); + size = CompactibleFreeListSpace::adjustObjectSize(p->size()); + } + } + DEBUG_ONLY(_collector->verify_work_stacks_empty();) + return size; +} + +void ScanMarkedObjectsAgainCarefullyClosure::do_yield_work() { + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "CMS thread should hold CMS token"); + assert_lock_strong(_freelistLock); + assert_lock_strong(_bitMap->lock()); + // relinquish the free_list_lock and bitMaplock() + _bitMap->lock()->unlock(); + _freelistLock->unlock(); + ConcurrentMarkSweepThread::desynchronize(true); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + _collector->stopTimer(); + GCPauseTimer p(_collector->size_policy()->concurrent_timer_ptr()); + if (PrintCMSStatistics != 0) { + _collector->incrementYields(); + } + _collector->icms_wait(); + + // See the comment in coordinator_yield() + for (unsigned i = 0; i < CMSYieldSleepCount && + ConcurrentMarkSweepThread::should_yield() && + !CMSCollector::foregroundGCIsActive(); ++i) { + os::sleep(Thread::current(), 1, false); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + } + + ConcurrentMarkSweepThread::synchronize(true); + _freelistLock->lock_without_safepoint_check(); + _bitMap->lock()->lock_without_safepoint_check(); + _collector->startTimer(); +} + + +////////////////////////////////////////////////////////////////// +// SurvivorSpacePrecleanClosure +////////////////////////////////////////////////////////////////// +// This (single-threaded) closure is used to preclean the oops in +// the survivor spaces. +size_t SurvivorSpacePrecleanClosure::do_object_careful(oop p) { + + HeapWord* addr = (HeapWord*)p; + DEBUG_ONLY(_collector->verify_work_stacks_empty();) + assert(!_span.contains(addr), "we are scanning the survivor spaces"); + assert(p->klass() != NULL, "object should be initializd"); + assert(p->is_parsable(), "must be parsable."); + // an initialized object; ignore mark word in verification below + // since we are running concurrent with mutators + assert(p->is_oop(true), "should be an oop"); + // Note that we do not yield while we iterate over + // the interior oops of p, pushing the relevant ones + // on our marking stack. + size_t size = p->oop_iterate(_scanning_closure); + do_yield_check(); + // Observe that below, we do not abandon the preclean + // phase as soon as we should; rather we empty the + // marking stack before returning. This is to satisfy + // some existing assertions. In general, it may be a + // good idea to abort immediately and complete the marking + // from the grey objects at a later time. + while (!_mark_stack->isEmpty()) { + oop new_oop = _mark_stack->pop(); + assert(new_oop != NULL && new_oop->is_oop(), "Expected an oop"); + assert(new_oop->is_parsable(), "Found unparsable oop"); + assert(_bit_map->isMarked((HeapWord*)new_oop), + "only grey objects on this stack"); + // iterate over the oops in this oop, marking and pushing + // the ones in CMS heap (i.e. in _span). + new_oop->oop_iterate(_scanning_closure); + // check if it's time to yield + do_yield_check(); + } + unsigned int after_count = + GenCollectedHeap::heap()->total_collections(); + bool abort = (_before_count != after_count) || + _collector->should_abort_preclean(); + return abort ? 0 : size; +} + +void SurvivorSpacePrecleanClosure::do_yield_work() { + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "CMS thread should hold CMS token"); + assert_lock_strong(_bit_map->lock()); + // Relinquish the bit map lock + _bit_map->lock()->unlock(); + ConcurrentMarkSweepThread::desynchronize(true); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + _collector->stopTimer(); + GCPauseTimer p(_collector->size_policy()->concurrent_timer_ptr()); + if (PrintCMSStatistics != 0) { + _collector->incrementYields(); + } + _collector->icms_wait(); + + // See the comment in coordinator_yield() + for (unsigned i = 0; i < CMSYieldSleepCount && + ConcurrentMarkSweepThread::should_yield() && + !CMSCollector::foregroundGCIsActive(); ++i) { + os::sleep(Thread::current(), 1, false); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + } + + ConcurrentMarkSweepThread::synchronize(true); + _bit_map->lock()->lock_without_safepoint_check(); + _collector->startTimer(); +} + +// This closure is used to rescan the marked objects on the dirty cards +// in the mod union table and the card table proper. In the parallel +// case, although the bitMap is shared, we do a single read so the +// isMarked() query is "safe". +bool ScanMarkedObjectsAgainClosure::do_object_bm(oop p, MemRegion mr) { + // Ignore mark word because we are running concurrent with mutators + assert(p->is_oop_or_null(true), "expected an oop or null"); + HeapWord* addr = (HeapWord*)p; + assert(_span.contains(addr), "we are scanning the CMS generation"); + bool is_obj_array = false; + #ifdef DEBUG + if (!_parallel) { + assert(_mark_stack->isEmpty(), "pre-condition (eager drainage)"); + assert(_collector->overflow_list_is_empty(), + "overflow list should be empty"); + + } + #endif // DEBUG + if (_bit_map->isMarked(addr)) { + // Obj arrays are precisely marked, non-arrays are not; + // so we scan objArrays precisely and non-arrays in their + // entirety. + if (p->is_objArray()) { + is_obj_array = true; + if (_parallel) { + p->oop_iterate(_par_scan_closure, mr); + } else { + p->oop_iterate(_scan_closure, mr); + } + } else { + if (_parallel) { + p->oop_iterate(_par_scan_closure); + } else { + p->oop_iterate(_scan_closure); + } + } + } + #ifdef DEBUG + if (!_parallel) { + assert(_mark_stack->isEmpty(), "post-condition (eager drainage)"); + assert(_collector->overflow_list_is_empty(), + "overflow list should be empty"); + + } + #endif // DEBUG + return is_obj_array; +} + +MarkFromRootsClosure::MarkFromRootsClosure(CMSCollector* collector, + MemRegion span, + CMSBitMap* bitMap, CMSMarkStack* markStack, + CMSMarkStack* revisitStack, + bool should_yield, bool verifying): + _collector(collector), + _span(span), + _bitMap(bitMap), + _mut(&collector->_modUnionTable), + _markStack(markStack), + _revisitStack(revisitStack), + _yield(should_yield), + _skipBits(0) +{ + assert(_markStack->isEmpty(), "stack should be empty"); + _finger = _bitMap->startWord(); + _threshold = _finger; + assert(_collector->_restart_addr == NULL, "Sanity check"); + assert(_span.contains(_finger), "Out of bounds _finger?"); + DEBUG_ONLY(_verifying = verifying;) +} + +void MarkFromRootsClosure::reset(HeapWord* addr) { + assert(_markStack->isEmpty(), "would cause duplicates on stack"); + assert(_span.contains(addr), "Out of bounds _finger?"); + _finger = addr; + _threshold = (HeapWord*)round_to( + (intptr_t)_finger, CardTableModRefBS::card_size); +} + +// Should revisit to see if this should be restructured for +// greater efficiency. +void MarkFromRootsClosure::do_bit(size_t offset) { + if (_skipBits > 0) { + _skipBits--; + return; + } + // convert offset into a HeapWord* + HeapWord* addr = _bitMap->startWord() + offset; + assert(_bitMap->endWord() && addr < _bitMap->endWord(), + "address out of range"); + assert(_bitMap->isMarked(addr), "tautology"); + if (_bitMap->isMarked(addr+1)) { + // this is an allocated but not yet initialized object + assert(_skipBits == 0, "tautology"); + _skipBits = 2; // skip next two marked bits ("Printezis-marks") + oop p = oop(addr); + if (p->klass() == NULL || !p->is_parsable()) { + DEBUG_ONLY(if (!_verifying) {) + // We re-dirty the cards on which this object lies and increase + // the _threshold so that we'll come back to scan this object + // during the preclean or remark phase. (CMSCleanOnEnter) + if (CMSCleanOnEnter) { + size_t sz = _collector->block_size_using_printezis_bits(addr); + HeapWord* start_card_addr = (HeapWord*)round_down( + (intptr_t)addr, CardTableModRefBS::card_size); + HeapWord* end_card_addr = (HeapWord*)round_to( + (intptr_t)(addr+sz), CardTableModRefBS::card_size); + MemRegion redirty_range = MemRegion(start_card_addr, end_card_addr); + assert(!redirty_range.is_empty(), "Arithmetical tautology"); + // Bump _threshold to end_card_addr; note that + // _threshold cannot possibly exceed end_card_addr, anyhow. + // This prevents future clearing of the card as the scan proceeds + // to the right. + assert(_threshold <= end_card_addr, + "Because we are just scanning into this object"); + if (_threshold < end_card_addr) { + _threshold = end_card_addr; + } + if (p->klass() != NULL) { + // Redirty the range of cards... + _mut->mark_range(redirty_range); + } // ...else the setting of klass will dirty the card anyway. + } + DEBUG_ONLY(}) + return; + } + } + scanOopsInOop(addr); +} + +// We take a break if we've been at this for a while, +// so as to avoid monopolizing the locks involved. +void MarkFromRootsClosure::do_yield_work() { + // First give up the locks, then yield, then re-lock + // We should probably use a constructor/destructor idiom to + // do this unlock/lock or modify the MutexUnlocker class to + // serve our purpose. XXX + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "CMS thread should hold CMS token"); + assert_lock_strong(_bitMap->lock()); + _bitMap->lock()->unlock(); + ConcurrentMarkSweepThread::desynchronize(true); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + _collector->stopTimer(); + GCPauseTimer p(_collector->size_policy()->concurrent_timer_ptr()); + if (PrintCMSStatistics != 0) { + _collector->incrementYields(); + } + _collector->icms_wait(); + + // See the comment in coordinator_yield() + for (unsigned i = 0; i < CMSYieldSleepCount && + ConcurrentMarkSweepThread::should_yield() && + !CMSCollector::foregroundGCIsActive(); ++i) { + os::sleep(Thread::current(), 1, false); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + } + + ConcurrentMarkSweepThread::synchronize(true); + _bitMap->lock()->lock_without_safepoint_check(); + _collector->startTimer(); +} + +void MarkFromRootsClosure::scanOopsInOop(HeapWord* ptr) { + assert(_bitMap->isMarked(ptr), "expected bit to be set"); + assert(_markStack->isEmpty(), + "should drain stack to limit stack usage"); + // convert ptr to an oop preparatory to scanning + oop this_oop = oop(ptr); + // Ignore mark word in verification below, since we + // may be running concurrent with mutators. + assert(this_oop->is_oop(true), "should be an oop"); + assert(_finger <= ptr, "_finger runneth ahead"); + // advance the finger to right end of this object + _finger = ptr + this_oop->size(); + assert(_finger > ptr, "we just incremented it above"); + // On large heaps, it may take us some time to get through + // the marking phase (especially if running iCMS). During + // this time it's possible that a lot of mutations have + // accumulated in the card table and the mod union table -- + // these mutation records are redundant until we have + // actually traced into the corresponding card. + // Here, we check whether advancing the finger would make + // us cross into a new card, and if so clear corresponding + // cards in the MUT (preclean them in the card-table in the + // future). + + DEBUG_ONLY(if (!_verifying) {) + // The clean-on-enter optimization is disabled by default, + // until we fix 6178663. + if (CMSCleanOnEnter && (_finger > _threshold)) { + // [_threshold, _finger) represents the interval + // of cards to be cleared in MUT (or precleaned in card table). + // The set of cards to be cleared is all those that overlap + // with the interval [_threshold, _finger); note that + // _threshold is always kept card-aligned but _finger isn't + // always card-aligned. + HeapWord* old_threshold = _threshold; + assert(old_threshold == (HeapWord*)round_to( + (intptr_t)old_threshold, CardTableModRefBS::card_size), + "_threshold should always be card-aligned"); + _threshold = (HeapWord*)round_to( + (intptr_t)_finger, CardTableModRefBS::card_size); + MemRegion mr(old_threshold, _threshold); + assert(!mr.is_empty(), "Control point invariant"); + assert(_span.contains(mr), "Should clear within span"); + // XXX When _finger crosses from old gen into perm gen + // we may be doing unnecessary cleaning; do better in the + // future by detecting that condition and clearing fewer + // MUT/CT entries. + _mut->clear_range(mr); + } + DEBUG_ONLY(}) + + // Note: the finger doesn't advance while we drain + // the stack below. + PushOrMarkClosure pushOrMarkClosure(_collector, + _span, _bitMap, _markStack, + _revisitStack, + _finger, this); + bool res = _markStack->push(this_oop); + assert(res, "Empty non-zero size stack should have space for single push"); + while (!_markStack->isEmpty()) { + oop new_oop = _markStack->pop(); + // Skip verifying header mark word below because we are + // running concurrent with mutators. + assert(new_oop->is_oop(true), "Oops! expected to pop an oop"); + // now scan this oop's oops + new_oop->oop_iterate(&pushOrMarkClosure); + do_yield_check(); + } + assert(_markStack->isEmpty(), "tautology, emphasizing post-condition"); +} + +Par_MarkFromRootsClosure::Par_MarkFromRootsClosure(CMSConcMarkingTask* task, + CMSCollector* collector, MemRegion span, + CMSBitMap* bit_map, + OopTaskQueue* work_queue, + CMSMarkStack* overflow_stack, + CMSMarkStack* revisit_stack, + bool should_yield): + _collector(collector), + _whole_span(collector->_span), + _span(span), + _bit_map(bit_map), + _mut(&collector->_modUnionTable), + _work_queue(work_queue), + _overflow_stack(overflow_stack), + _revisit_stack(revisit_stack), + _yield(should_yield), + _skip_bits(0), + _task(task) +{ + assert(_work_queue->size() == 0, "work_queue should be empty"); + _finger = span.start(); + _threshold = _finger; // XXX Defer clear-on-enter optimization for now + assert(_span.contains(_finger), "Out of bounds _finger?"); +} + +// Should revisit to see if this should be restructured for +// greater efficiency. +void Par_MarkFromRootsClosure::do_bit(size_t offset) { + if (_skip_bits > 0) { + _skip_bits--; + return; + } + // convert offset into a HeapWord* + HeapWord* addr = _bit_map->startWord() + offset; + assert(_bit_map->endWord() && addr < _bit_map->endWord(), + "address out of range"); + assert(_bit_map->isMarked(addr), "tautology"); + if (_bit_map->isMarked(addr+1)) { + // this is an allocated object that might not yet be initialized + assert(_skip_bits == 0, "tautology"); + _skip_bits = 2; // skip next two marked bits ("Printezis-marks") + oop p = oop(addr); + if (p->klass() == NULL || !p->is_parsable()) { + // in the case of Clean-on-Enter optimization, redirty card + // and avoid clearing card by increasing the threshold. + return; + } + } + scan_oops_in_oop(addr); +} + +void Par_MarkFromRootsClosure::scan_oops_in_oop(HeapWord* ptr) { + assert(_bit_map->isMarked(ptr), "expected bit to be set"); + // Should we assert that our work queue is empty or + // below some drain limit? + assert(_work_queue->size() == 0, + "should drain stack to limit stack usage"); + // convert ptr to an oop preparatory to scanning + oop this_oop = oop(ptr); + // Ignore mark word in verification below, since we + // may be running concurrent with mutators. + assert(this_oop->is_oop(true), "should be an oop"); + assert(_finger <= ptr, "_finger runneth ahead"); + // advance the finger to right end of this object + _finger = ptr + this_oop->size(); + assert(_finger > ptr, "we just incremented it above"); + // On large heaps, it may take us some time to get through + // the marking phase (especially if running iCMS). During + // this time it's possible that a lot of mutations have + // accumulated in the card table and the mod union table -- + // these mutation records are redundant until we have + // actually traced into the corresponding card. + // Here, we check whether advancing the finger would make + // us cross into a new card, and if so clear corresponding + // cards in the MUT (preclean them in the card-table in the + // future). + + // The clean-on-enter optimization is disabled by default, + // until we fix 6178663. + if (CMSCleanOnEnter && (_finger > _threshold)) { + // [_threshold, _finger) represents the interval + // of cards to be cleared in MUT (or precleaned in card table). + // The set of cards to be cleared is all those that overlap + // with the interval [_threshold, _finger); note that + // _threshold is always kept card-aligned but _finger isn't + // always card-aligned. + HeapWord* old_threshold = _threshold; + assert(old_threshold == (HeapWord*)round_to( + (intptr_t)old_threshold, CardTableModRefBS::card_size), + "_threshold should always be card-aligned"); + _threshold = (HeapWord*)round_to( + (intptr_t)_finger, CardTableModRefBS::card_size); + MemRegion mr(old_threshold, _threshold); + assert(!mr.is_empty(), "Control point invariant"); + assert(_span.contains(mr), "Should clear within span"); // _whole_span ?? + // XXX When _finger crosses from old gen into perm gen + // we may be doing unnecessary cleaning; do better in the + // future by detecting that condition and clearing fewer + // MUT/CT entries. + _mut->clear_range(mr); + } + + // Note: the local finger doesn't advance while we drain + // the stack below, but the global finger sure can and will. + HeapWord** gfa = _task->global_finger_addr(); + Par_PushOrMarkClosure pushOrMarkClosure(_collector, + _span, _bit_map, + _work_queue, + _overflow_stack, + _revisit_stack, + _finger, + gfa, this); + bool res = _work_queue->push(this_oop); // overflow could occur here + assert(res, "Will hold once we use workqueues"); + while (true) { + oop new_oop; + if (!_work_queue->pop_local(new_oop)) { + // We emptied our work_queue; check if there's stuff that can + // be gotten from the overflow stack. + if (CMSConcMarkingTask::get_work_from_overflow_stack( + _overflow_stack, _work_queue)) { + do_yield_check(); + continue; + } else { // done + break; + } + } + // Skip verifying header mark word below because we are + // running concurrent with mutators. + assert(new_oop->is_oop(true), "Oops! expected to pop an oop"); + // now scan this oop's oops + new_oop->oop_iterate(&pushOrMarkClosure); + do_yield_check(); + } + assert(_work_queue->size() == 0, "tautology, emphasizing post-condition"); +} + +// Yield in response to a request from VM Thread or +// from mutators. +void Par_MarkFromRootsClosure::do_yield_work() { + assert(_task != NULL, "sanity"); + _task->yield(); +} + +// A variant of the above used for verifying CMS marking work. +MarkFromRootsVerifyClosure::MarkFromRootsVerifyClosure(CMSCollector* collector, + MemRegion span, + CMSBitMap* verification_bm, CMSBitMap* cms_bm, + CMSMarkStack* mark_stack): + _collector(collector), + _span(span), + _verification_bm(verification_bm), + _cms_bm(cms_bm), + _mark_stack(mark_stack), + _pam_verify_closure(collector, span, verification_bm, cms_bm, + mark_stack) +{ + assert(_mark_stack->isEmpty(), "stack should be empty"); + _finger = _verification_bm->startWord(); + assert(_collector->_restart_addr == NULL, "Sanity check"); + assert(_span.contains(_finger), "Out of bounds _finger?"); +} + +void MarkFromRootsVerifyClosure::reset(HeapWord* addr) { + assert(_mark_stack->isEmpty(), "would cause duplicates on stack"); + assert(_span.contains(addr), "Out of bounds _finger?"); + _finger = addr; +} + +// Should revisit to see if this should be restructured for +// greater efficiency. +void MarkFromRootsVerifyClosure::do_bit(size_t offset) { + // convert offset into a HeapWord* + HeapWord* addr = _verification_bm->startWord() + offset; + assert(_verification_bm->endWord() && addr < _verification_bm->endWord(), + "address out of range"); + assert(_verification_bm->isMarked(addr), "tautology"); + assert(_cms_bm->isMarked(addr), "tautology"); + + assert(_mark_stack->isEmpty(), + "should drain stack to limit stack usage"); + // convert addr to an oop preparatory to scanning + oop this_oop = oop(addr); + assert(this_oop->is_oop(), "should be an oop"); + assert(_finger <= addr, "_finger runneth ahead"); + // advance the finger to right end of this object + _finger = addr + this_oop->size(); + assert(_finger > addr, "we just incremented it above"); + // Note: the finger doesn't advance while we drain + // the stack below. + bool res = _mark_stack->push(this_oop); + assert(res, "Empty non-zero size stack should have space for single push"); + while (!_mark_stack->isEmpty()) { + oop new_oop = _mark_stack->pop(); + assert(new_oop->is_oop(), "Oops! expected to pop an oop"); + // now scan this oop's oops + new_oop->oop_iterate(&_pam_verify_closure); + } + assert(_mark_stack->isEmpty(), "tautology, emphasizing post-condition"); +} + +PushAndMarkVerifyClosure::PushAndMarkVerifyClosure( + CMSCollector* collector, MemRegion span, + CMSBitMap* verification_bm, CMSBitMap* cms_bm, + CMSMarkStack* mark_stack): + OopClosure(collector->ref_processor()), + _collector(collector), + _span(span), + _verification_bm(verification_bm), + _cms_bm(cms_bm), + _mark_stack(mark_stack) +{ } + + +// Upon stack overflow, we discard (part of) the stack, +// remembering the least address amongst those discarded +// in CMSCollector's _restart_address. +void PushAndMarkVerifyClosure::handle_stack_overflow(HeapWord* lost) { + // Remember the least grey address discarded + HeapWord* ra = (HeapWord*)_mark_stack->least_value(lost); + _collector->lower_restart_addr(ra); + _mark_stack->reset(); // discard stack contents + _mark_stack->expand(); // expand the stack if possible +} + +void PushAndMarkVerifyClosure::do_oop(oop* p) { + oop this_oop = *p; + assert(this_oop->is_oop_or_null(), "expected an oop or NULL"); + HeapWord* addr = (HeapWord*)this_oop; + if (_span.contains(addr) && !_verification_bm->isMarked(addr)) { + // Oop lies in _span and isn't yet grey or black + _verification_bm->mark(addr); // now grey + if (!_cms_bm->isMarked(addr)) { + oop(addr)->print(); + gclog_or_tty->print_cr(" ("INTPTR_FORMAT" should have been marked)", addr); + fatal("... aborting"); + } + + if (!_mark_stack->push(this_oop)) { // stack overflow + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr("CMS marking stack overflow (benign) at " + SIZE_FORMAT, _mark_stack->capacity()); + } + assert(_mark_stack->isFull(), "Else push should have succeeded"); + handle_stack_overflow(addr); + } + // anything including and to the right of _finger + // will be scanned as we iterate over the remainder of the + // bit map + } +} + +PushOrMarkClosure::PushOrMarkClosure(CMSCollector* collector, + MemRegion span, + CMSBitMap* bitMap, CMSMarkStack* markStack, + CMSMarkStack* revisitStack, + HeapWord* finger, MarkFromRootsClosure* parent) : + OopClosure(collector->ref_processor()), + _collector(collector), + _span(span), + _bitMap(bitMap), + _markStack(markStack), + _revisitStack(revisitStack), + _finger(finger), + _parent(parent), + _should_remember_klasses(collector->cms_should_unload_classes()) +{ } + +Par_PushOrMarkClosure::Par_PushOrMarkClosure(CMSCollector* collector, + MemRegion span, + CMSBitMap* bit_map, + OopTaskQueue* work_queue, + CMSMarkStack* overflow_stack, + CMSMarkStack* revisit_stack, + HeapWord* finger, + HeapWord** global_finger_addr, + Par_MarkFromRootsClosure* parent) : + OopClosure(collector->ref_processor()), + _collector(collector), + _whole_span(collector->_span), + _span(span), + _bit_map(bit_map), + _work_queue(work_queue), + _overflow_stack(overflow_stack), + _revisit_stack(revisit_stack), + _finger(finger), + _global_finger_addr(global_finger_addr), + _parent(parent), + _should_remember_klasses(collector->cms_should_unload_classes()) +{ } + + +void CMSCollector::lower_restart_addr(HeapWord* low) { + assert(_span.contains(low), "Out of bounds addr"); + if (_restart_addr == NULL) { + _restart_addr = low; + } else { + _restart_addr = MIN2(_restart_addr, low); + } +} + +// Upon stack overflow, we discard (part of) the stack, +// remembering the least address amongst those discarded +// in CMSCollector's _restart_address. +void PushOrMarkClosure::handle_stack_overflow(HeapWord* lost) { + // Remember the least grey address discarded + HeapWord* ra = (HeapWord*)_markStack->least_value(lost); + _collector->lower_restart_addr(ra); + _markStack->reset(); // discard stack contents + _markStack->expand(); // expand the stack if possible +} + +// Upon stack overflow, we discard (part of) the stack, +// remembering the least address amongst those discarded +// in CMSCollector's _restart_address. +void Par_PushOrMarkClosure::handle_stack_overflow(HeapWord* lost) { + // We need to do this under a mutex to prevent other + // workers from interfering with the expansion below. + MutexLockerEx ml(_overflow_stack->par_lock(), + Mutex::_no_safepoint_check_flag); + // Remember the least grey address discarded + HeapWord* ra = (HeapWord*)_overflow_stack->least_value(lost); + _collector->lower_restart_addr(ra); + _overflow_stack->reset(); // discard stack contents + _overflow_stack->expand(); // expand the stack if possible +} + + +void PushOrMarkClosure::do_oop(oop* p) { + oop thisOop = *p; + // Ignore mark word because we are running concurrent with mutators. + assert(thisOop->is_oop_or_null(true), "expected an oop or NULL"); + HeapWord* addr = (HeapWord*)thisOop; + if (_span.contains(addr) && !_bitMap->isMarked(addr)) { + // Oop lies in _span and isn't yet grey or black + _bitMap->mark(addr); // now grey + if (addr < _finger) { + // the bit map iteration has already either passed, or + // sampled, this bit in the bit map; we'll need to + // use the marking stack to scan this oop's oops. + bool simulate_overflow = false; + NOT_PRODUCT( + if (CMSMarkStackOverflowALot && + _collector->simulate_overflow()) { + // simulate a stack overflow + simulate_overflow = true; + } + ) + if (simulate_overflow || !_markStack->push(thisOop)) { // stack overflow + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr("CMS marking stack overflow (benign) at " + SIZE_FORMAT, _markStack->capacity()); + } + assert(simulate_overflow || _markStack->isFull(), "Else push should have succeeded"); + handle_stack_overflow(addr); + } + } + // anything including and to the right of _finger + // will be scanned as we iterate over the remainder of the + // bit map + do_yield_check(); + } +} + +void Par_PushOrMarkClosure::do_oop(oop* p) { + oop this_oop = *p; + // Ignore mark word because we are running concurrent with mutators. + assert(this_oop->is_oop_or_null(true), "expected an oop or NULL"); + HeapWord* addr = (HeapWord*)this_oop; + if (_whole_span.contains(addr) && !_bit_map->isMarked(addr)) { + // Oop lies in _span and isn't yet grey or black + // We read the global_finger (volatile read) strictly after marking oop + bool res = _bit_map->par_mark(addr); // now grey + volatile HeapWord** gfa = (volatile HeapWord**)_global_finger_addr; + // Should we push this marked oop on our stack? + // -- if someone else marked it, nothing to do + // -- if target oop is above global finger nothing to do + // -- if target oop is in chunk and above local finger + // then nothing to do + // -- else push on work queue + if ( !res // someone else marked it, they will deal with it + || (addr >= *gfa) // will be scanned in a later task + || (_span.contains(addr) && addr >= _finger)) { // later in this chunk + return; + } + // the bit map iteration has already either passed, or + // sampled, this bit in the bit map; we'll need to + // use the marking stack to scan this oop's oops. + bool simulate_overflow = false; + NOT_PRODUCT( + if (CMSMarkStackOverflowALot && + _collector->simulate_overflow()) { + // simulate a stack overflow + simulate_overflow = true; + } + ) + if (simulate_overflow || + !(_work_queue->push(this_oop) || _overflow_stack->par_push(this_oop))) { + // stack overflow + if (PrintCMSStatistics != 0) { + gclog_or_tty->print_cr("CMS marking stack overflow (benign) at " + SIZE_FORMAT, _overflow_stack->capacity()); + } + // We cannot assert that the overflow stack is full because + // it may have been emptied since. + assert(simulate_overflow || + _work_queue->size() == _work_queue->max_elems(), + "Else push should have succeeded"); + handle_stack_overflow(addr); + } + do_yield_check(); + } +} + + +PushAndMarkClosure::PushAndMarkClosure(CMSCollector* collector, + MemRegion span, + ReferenceProcessor* rp, + CMSBitMap* bit_map, + CMSBitMap* mod_union_table, + CMSMarkStack* mark_stack, + CMSMarkStack* revisit_stack, + bool concurrent_precleaning): + OopClosure(rp), + _collector(collector), + _span(span), + _bit_map(bit_map), + _mod_union_table(mod_union_table), + _mark_stack(mark_stack), + _revisit_stack(revisit_stack), + _concurrent_precleaning(concurrent_precleaning), + _should_remember_klasses(collector->cms_should_unload_classes()) +{ + assert(_ref_processor != NULL, "_ref_processor shouldn't be NULL"); +} + +// Grey object rescan during pre-cleaning and second checkpoint phases -- +// the non-parallel version (the parallel version appears further below.) +void PushAndMarkClosure::do_oop(oop* p) { + oop this_oop = *p; + // Ignore mark word verification. If during concurrent precleaning + // the object monitor may be locked. If during the checkpoint + // phases, the object may already have been reached by a different + // path and may be at the end of the global overflow list (so + // the mark word may be NULL). + assert(this_oop->is_oop_or_null(true/* ignore mark word */), + "expected an oop or NULL"); + HeapWord* addr = (HeapWord*)this_oop; + // Check if oop points into the CMS generation + // and is not marked + if (_span.contains(addr) && !_bit_map->isMarked(addr)) { + // a white object ... + _bit_map->mark(addr); // ... now grey + // push on the marking stack (grey set) + bool simulate_overflow = false; + NOT_PRODUCT( + if (CMSMarkStackOverflowALot && + _collector->simulate_overflow()) { + // simulate a stack overflow + simulate_overflow = true; + } + ) + if (simulate_overflow || !_mark_stack->push(this_oop)) { + if (_concurrent_precleaning) { + // During precleaning we can just dirty the appropriate card + // in the mod union table, thus ensuring that the object remains + // in the grey set and continue. Note that no one can be intefering + // with us in this action of dirtying the mod union table, so + // no locking is required. + _mod_union_table->mark(addr); + _collector->_ser_pmc_preclean_ovflw++; + } else { + // During the remark phase, we need to remember this oop + // in the overflow list. + _collector->push_on_overflow_list(this_oop); + _collector->_ser_pmc_remark_ovflw++; + } + } + } +} + +Par_PushAndMarkClosure::Par_PushAndMarkClosure(CMSCollector* collector, + MemRegion span, + ReferenceProcessor* rp, + CMSBitMap* bit_map, + OopTaskQueue* work_queue, + CMSMarkStack* revisit_stack): + OopClosure(rp), + _collector(collector), + _span(span), + _bit_map(bit_map), + _work_queue(work_queue), + _revisit_stack(revisit_stack), + _should_remember_klasses(collector->cms_should_unload_classes()) +{ + assert(_ref_processor != NULL, "_ref_processor shouldn't be NULL"); +} + +// Grey object rescan during second checkpoint phase -- +// the parallel version. +void Par_PushAndMarkClosure::do_oop(oop* p) { + oop this_oop = *p; + // In the assert below, we ignore the mark word because + // this oop may point to an already visited object that is + // on the overflow stack (in which case the mark word has + // been hijacked for chaining into the overflow stack -- + // if this is the last object in the overflow stack then + // its mark word will be NULL). Because this object may + // have been subsequently popped off the global overflow + // stack, and the mark word possibly restored to the prototypical + // value, by the time we get to examined this failing assert in + // the debugger, is_oop_or_null(false) may subsequently start + // to hold. + assert(this_oop->is_oop_or_null(true), + "expected an oop or NULL"); + HeapWord* addr = (HeapWord*)this_oop; + // Check if oop points into the CMS generation + // and is not marked + if (_span.contains(addr) && !_bit_map->isMarked(addr)) { + // a white object ... + // If we manage to "claim" the object, by being the + // first thread to mark it, then we push it on our + // marking stack + if (_bit_map->par_mark(addr)) { // ... now grey + // push on work queue (grey set) + bool simulate_overflow = false; + NOT_PRODUCT( + if (CMSMarkStackOverflowALot && + _collector->par_simulate_overflow()) { + // simulate a stack overflow + simulate_overflow = true; + } + ) + if (simulate_overflow || !_work_queue->push(this_oop)) { + _collector->par_push_on_overflow_list(this_oop); + _collector->_par_pmc_remark_ovflw++; // imprecise OK: no need to CAS + } + } // Else, some other thread got there first + } +} + +void PushAndMarkClosure::remember_klass(Klass* k) { + if (!_revisit_stack->push(oop(k))) { + fatal("Revisit stack overflowed in PushAndMarkClosure"); + } +} + +void Par_PushAndMarkClosure::remember_klass(Klass* k) { + if (!_revisit_stack->par_push(oop(k))) { + fatal("Revist stack overflowed in Par_PushAndMarkClosure"); + } +} + +void CMSPrecleanRefsYieldClosure::do_yield_work() { + Mutex* bml = _collector->bitMapLock(); + assert_lock_strong(bml); + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "CMS thread should hold CMS token"); + + bml->unlock(); + ConcurrentMarkSweepThread::desynchronize(true); + + ConcurrentMarkSweepThread::acknowledge_yield_request(); + + _collector->stopTimer(); + GCPauseTimer p(_collector->size_policy()->concurrent_timer_ptr()); + if (PrintCMSStatistics != 0) { + _collector->incrementYields(); + } + _collector->icms_wait(); + + // See the comment in coordinator_yield() + for (unsigned i = 0; i < CMSYieldSleepCount && + ConcurrentMarkSweepThread::should_yield() && + !CMSCollector::foregroundGCIsActive(); ++i) { + os::sleep(Thread::current(), 1, false); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + } + + ConcurrentMarkSweepThread::synchronize(true); + bml->lock(); + + _collector->startTimer(); +} + +bool CMSPrecleanRefsYieldClosure::should_return() { + if (ConcurrentMarkSweepThread::should_yield()) { + do_yield_work(); + } + return _collector->foregroundGCIsActive(); +} + +void MarkFromDirtyCardsClosure::do_MemRegion(MemRegion mr) { + assert(((size_t)mr.start())%CardTableModRefBS::card_size_in_words == 0, + "mr should be aligned to start at a card boundary"); + // We'd like to assert: + // assert(mr.word_size()%CardTableModRefBS::card_size_in_words == 0, + // "mr should be a range of cards"); + // However, that would be too strong in one case -- the last + // partition ends at _unallocated_block which, in general, can be + // an arbitrary boundary, not necessarily card aligned. + if (PrintCMSStatistics != 0) { + _num_dirty_cards += + mr.word_size()/CardTableModRefBS::card_size_in_words; + } + _space->object_iterate_mem(mr, &_scan_cl); +} + +SweepClosure::SweepClosure(CMSCollector* collector, + ConcurrentMarkSweepGeneration* g, + CMSBitMap* bitMap, bool should_yield) : + _collector(collector), + _g(g), + _sp(g->cmsSpace()), + _limit(_sp->sweep_limit()), + _freelistLock(_sp->freelistLock()), + _bitMap(bitMap), + _yield(should_yield), + _inFreeRange(false), // No free range at beginning of sweep + _freeRangeInFreeLists(false), // No free range at beginning of sweep + _lastFreeRangeCoalesced(false), + _freeFinger(g->used_region().start()) +{ + NOT_PRODUCT( + _numObjectsFreed = 0; + _numWordsFreed = 0; + _numObjectsLive = 0; + _numWordsLive = 0; + _numObjectsAlreadyFree = 0; + _numWordsAlreadyFree = 0; + _last_fc = NULL; + + _sp->initializeIndexedFreeListArrayReturnedBytes(); + _sp->dictionary()->initializeDictReturnedBytes(); + ) + assert(_limit >= _sp->bottom() && _limit <= _sp->end(), + "sweep _limit out of bounds"); + if (CMSTraceSweeper) { + gclog_or_tty->print("\n====================\nStarting new sweep\n"); + } +} + +// We need this destructor to reclaim any space at the end +// of the space, which do_blk below may not have added back to +// the free lists. [basically dealing with the "fringe effect"] +SweepClosure::~SweepClosure() { + assert_lock_strong(_freelistLock); + // this should be treated as the end of a free run if any + // The current free range should be returned to the free lists + // as one coalesced chunk. + if (inFreeRange()) { + flushCurFreeChunk(freeFinger(), + pointer_delta(_limit, freeFinger())); + assert(freeFinger() < _limit, "the finger pointeth off base"); + if (CMSTraceSweeper) { + gclog_or_tty->print("destructor:"); + gclog_or_tty->print("Sweep:put_free_blk 0x%x ("SIZE_FORMAT") " + "[coalesced:"SIZE_FORMAT"]\n", + freeFinger(), pointer_delta(_limit, freeFinger()), + lastFreeRangeCoalesced()); + } + } + NOT_PRODUCT( + if (Verbose && PrintGC) { + gclog_or_tty->print("Collected "SIZE_FORMAT" objects, " + SIZE_FORMAT " bytes", + _numObjectsFreed, _numWordsFreed*sizeof(HeapWord)); + gclog_or_tty->print_cr("\nLive "SIZE_FORMAT" objects, " + SIZE_FORMAT" bytes " + "Already free "SIZE_FORMAT" objects, "SIZE_FORMAT" bytes", + _numObjectsLive, _numWordsLive*sizeof(HeapWord), + _numObjectsAlreadyFree, _numWordsAlreadyFree*sizeof(HeapWord)); + size_t totalBytes = (_numWordsFreed + _numWordsLive + _numWordsAlreadyFree) * + sizeof(HeapWord); + gclog_or_tty->print_cr("Total sweep: "SIZE_FORMAT" bytes", totalBytes); + + if (PrintCMSStatistics && CMSVerifyReturnedBytes) { + size_t indexListReturnedBytes = _sp->sumIndexedFreeListArrayReturnedBytes(); + size_t dictReturnedBytes = _sp->dictionary()->sumDictReturnedBytes(); + size_t returnedBytes = indexListReturnedBytes + dictReturnedBytes; + gclog_or_tty->print("Returned "SIZE_FORMAT" bytes", returnedBytes); + gclog_or_tty->print(" Indexed List Returned "SIZE_FORMAT" bytes", + indexListReturnedBytes); + gclog_or_tty->print_cr(" Dictionary Returned "SIZE_FORMAT" bytes", + dictReturnedBytes); + } + } + ) + // Now, in debug mode, just null out the sweep_limit + NOT_PRODUCT(_sp->clear_sweep_limit();) + if (CMSTraceSweeper) { + gclog_or_tty->print("end of sweep\n================\n"); + } +} + +void SweepClosure::initialize_free_range(HeapWord* freeFinger, + bool freeRangeInFreeLists) { + if (CMSTraceSweeper) { + gclog_or_tty->print("---- Start free range 0x%x with free block [%d] (%d)\n", + freeFinger, _sp->block_size(freeFinger), + freeRangeInFreeLists); + } + assert(!inFreeRange(), "Trampling existing free range"); + set_inFreeRange(true); + set_lastFreeRangeCoalesced(false); + + set_freeFinger(freeFinger); + set_freeRangeInFreeLists(freeRangeInFreeLists); + if (CMSTestInFreeList) { + if (freeRangeInFreeLists) { + FreeChunk* fc = (FreeChunk*) freeFinger; + assert(fc->isFree(), "A chunk on the free list should be free."); + assert(fc->size() > 0, "Free range should have a size"); + assert(_sp->verifyChunkInFreeLists(fc), "Chunk is not in free lists"); + } + } +} + +// Note that the sweeper runs concurrently with mutators. Thus, +// it is possible for direct allocation in this generation to happen +// in the middle of the sweep. Note that the sweeper also coalesces +// contiguous free blocks. Thus, unless the sweeper and the allocator +// synchronize appropriately freshly allocated blocks may get swept up. +// This is accomplished by the sweeper locking the free lists while +// it is sweeping. Thus blocks that are determined to be free are +// indeed free. There is however one additional complication: +// blocks that have been allocated since the final checkpoint and +// mark, will not have been marked and so would be treated as +// unreachable and swept up. To prevent this, the allocator marks +// the bit map when allocating during the sweep phase. This leads, +// however, to a further complication -- objects may have been allocated +// but not yet initialized -- in the sense that the header isn't yet +// installed. The sweeper can not then determine the size of the block +// in order to skip over it. To deal with this case, we use a technique +// (due to Printezis) to encode such uninitialized block sizes in the +// bit map. Since the bit map uses a bit per every HeapWord, but the +// CMS generation has a minimum object size of 3 HeapWords, it follows +// that "normal marks" won't be adjacent in the bit map (there will +// always be at least two 0 bits between successive 1 bits). We make use +// of these "unused" bits to represent uninitialized blocks -- the bit +// corresponding to the start of the uninitialized object and the next +// bit are both set. Finally, a 1 bit marks the end of the object that +// started with the two consecutive 1 bits to indicate its potentially +// uninitialized state. + +size_t SweepClosure::do_blk_careful(HeapWord* addr) { + FreeChunk* fc = (FreeChunk*)addr; + size_t res; + + // check if we are done sweepinrg + if (addr == _limit) { // we have swept up to the limit, do nothing more + assert(_limit >= _sp->bottom() && _limit <= _sp->end(), + "sweep _limit out of bounds"); + // help the closure application finish + return pointer_delta(_sp->end(), _limit); + } + assert(addr <= _limit, "sweep invariant"); + + // check if we should yield + do_yield_check(addr); + if (fc->isFree()) { + // Chunk that is already free + res = fc->size(); + doAlreadyFreeChunk(fc); + debug_only(_sp->verifyFreeLists()); + assert(res == fc->size(), "Don't expect the size to change"); + NOT_PRODUCT( + _numObjectsAlreadyFree++; + _numWordsAlreadyFree += res; + ) + NOT_PRODUCT(_last_fc = fc;) + } else if (!_bitMap->isMarked(addr)) { + // Chunk is fresh garbage + res = doGarbageChunk(fc); + debug_only(_sp->verifyFreeLists()); + NOT_PRODUCT( + _numObjectsFreed++; + _numWordsFreed += res; + ) + } else { + // Chunk that is alive. + res = doLiveChunk(fc); + debug_only(_sp->verifyFreeLists()); + NOT_PRODUCT( + _numObjectsLive++; + _numWordsLive += res; + ) + } + return res; +} + +// For the smart allocation, record following +// split deaths - a free chunk is removed from its free list because +// it is being split into two or more chunks. +// split birth - a free chunk is being added to its free list because +// a larger free chunk has been split and resulted in this free chunk. +// coal death - a free chunk is being removed from its free list because +// it is being coalesced into a large free chunk. +// coal birth - a free chunk is being added to its free list because +// it was created when two or more free chunks where coalesced into +// this free chunk. +// +// These statistics are used to determine the desired number of free +// chunks of a given size. The desired number is chosen to be relative +// to the end of a CMS sweep. The desired number at the end of a sweep +// is the +// count-at-end-of-previous-sweep (an amount that was enough) +// - count-at-beginning-of-current-sweep (the excess) +// + split-births (gains in this size during interval) +// - split-deaths (demands on this size during interval) +// where the interval is from the end of one sweep to the end of the +// next. +// +// When sweeping the sweeper maintains an accumulated chunk which is +// the chunk that is made up of chunks that have been coalesced. That +// will be termed the left-hand chunk. A new chunk of garbage that +// is being considered for coalescing will be referred to as the +// right-hand chunk. +// +// When making a decision on whether to coalesce a right-hand chunk with +// the current left-hand chunk, the current count vs. the desired count +// of the left-hand chunk is considered. Also if the right-hand chunk +// is near the large chunk at the end of the heap (see +// ConcurrentMarkSweepGeneration::isNearLargestChunk()), then the +// left-hand chunk is coalesced. +// +// When making a decision about whether to split a chunk, the desired count +// vs. the current count of the candidate to be split is also considered. +// If the candidate is underpopulated (currently fewer chunks than desired) +// a chunk of an overpopulated (currently more chunks than desired) size may +// be chosen. The "hint" associated with a free list, if non-null, points +// to a free list which may be overpopulated. +// + +void SweepClosure::doAlreadyFreeChunk(FreeChunk* fc) { + size_t size = fc->size(); + // Chunks that cannot be coalesced are not in the + // free lists. + if (CMSTestInFreeList && !fc->cantCoalesce()) { + assert(_sp->verifyChunkInFreeLists(fc), + "free chunk should be in free lists"); + } + // a chunk that is already free, should not have been + // marked in the bit map + HeapWord* addr = (HeapWord*) fc; + assert(!_bitMap->isMarked(addr), "free chunk should be unmarked"); + // Verify that the bit map has no bits marked between + // addr and purported end of this block. + _bitMap->verifyNoOneBitsInRange(addr + 1, addr + size); + + // Some chunks cannot be coalesced in under any circumstances. + // See the definition of cantCoalesce(). + if (!fc->cantCoalesce()) { + // This chunk can potentially be coalesced. + if (_sp->adaptive_freelists()) { + // All the work is done in + doPostIsFreeOrGarbageChunk(fc, size); + } else { // Not adaptive free lists + // this is a free chunk that can potentially be coalesced by the sweeper; + if (!inFreeRange()) { + // if the next chunk is a free block that can't be coalesced + // it doesn't make sense to remove this chunk from the free lists + FreeChunk* nextChunk = (FreeChunk*)(addr + size); + assert((HeapWord*)nextChunk <= _limit, "sweep invariant"); + if ((HeapWord*)nextChunk < _limit && // there's a next chunk... + nextChunk->isFree() && // which is free... + nextChunk->cantCoalesce()) { // ... but cant be coalesced + // nothing to do + } else { + // Potentially the start of a new free range: + // Don't eagerly remove it from the free lists. + // No need to remove it if it will just be put + // back again. (Also from a pragmatic point of view + // if it is a free block in a region that is beyond + // any allocated blocks, an assertion will fail) + // Remember the start of a free run. + initialize_free_range(addr, true); + // end - can coalesce with next chunk + } + } else { + // the midst of a free range, we are coalescing + debug_only(record_free_block_coalesced(fc);) + if (CMSTraceSweeper) { + gclog_or_tty->print(" -- pick up free block 0x%x (%d)\n", fc, size); + } + // remove it from the free lists + _sp->removeFreeChunkFromFreeLists(fc); + set_lastFreeRangeCoalesced(true); + // If the chunk is being coalesced and the current free range is + // in the free lists, remove the current free range so that it + // will be returned to the free lists in its entirety - all + // the coalesced pieces included. + if (freeRangeInFreeLists()) { + FreeChunk* ffc = (FreeChunk*) freeFinger(); + assert(ffc->size() == pointer_delta(addr, freeFinger()), + "Size of free range is inconsistent with chunk size."); + if (CMSTestInFreeList) { + assert(_sp->verifyChunkInFreeLists(ffc), + "free range is not in free lists"); + } + _sp->removeFreeChunkFromFreeLists(ffc); + set_freeRangeInFreeLists(false); + } + } + } + } else { + // Code path common to both original and adaptive free lists. + + // cant coalesce with previous block; this should be treated + // as the end of a free run if any + if (inFreeRange()) { + // we kicked some butt; time to pick up the garbage + assert(freeFinger() < addr, "the finger pointeth off base"); + flushCurFreeChunk(freeFinger(), pointer_delta(addr, freeFinger())); + } + // else, nothing to do, just continue + } +} + +size_t SweepClosure::doGarbageChunk(FreeChunk* fc) { + // This is a chunk of garbage. It is not in any free list. + // Add it to a free list or let it possibly be coalesced into + // a larger chunk. + HeapWord* addr = (HeapWord*) fc; + size_t size = CompactibleFreeListSpace::adjustObjectSize(oop(addr)->size()); + + if (_sp->adaptive_freelists()) { + // Verify that the bit map has no bits marked between + // addr and purported end of just dead object. + _bitMap->verifyNoOneBitsInRange(addr + 1, addr + size); + + doPostIsFreeOrGarbageChunk(fc, size); + } else { + if (!inFreeRange()) { + // start of a new free range + assert(size > 0, "A free range should have a size"); + initialize_free_range(addr, false); + + } else { + // this will be swept up when we hit the end of the + // free range + if (CMSTraceSweeper) { + gclog_or_tty->print(" -- pick up garbage 0x%x (%d) \n", fc, size); + } + // If the chunk is being coalesced and the current free range is + // in the free lists, remove the current free range so that it + // will be returned to the free lists in its entirety - all + // the coalesced pieces included. + if (freeRangeInFreeLists()) { + FreeChunk* ffc = (FreeChunk*)freeFinger(); + assert(ffc->size() == pointer_delta(addr, freeFinger()), + "Size of free range is inconsistent with chunk size."); + if (CMSTestInFreeList) { + assert(_sp->verifyChunkInFreeLists(ffc), + "free range is not in free lists"); + } + _sp->removeFreeChunkFromFreeLists(ffc); + set_freeRangeInFreeLists(false); + } + set_lastFreeRangeCoalesced(true); + } + // this will be swept up when we hit the end of the free range + + // Verify that the bit map has no bits marked between + // addr and purported end of just dead object. + _bitMap->verifyNoOneBitsInRange(addr + 1, addr + size); + } + return size; +} + +size_t SweepClosure::doLiveChunk(FreeChunk* fc) { + HeapWord* addr = (HeapWord*) fc; + // The sweeper has just found a live object. Return any accumulated + // left hand chunk to the free lists. + if (inFreeRange()) { + if (_sp->adaptive_freelists()) { + flushCurFreeChunk(freeFinger(), + pointer_delta(addr, freeFinger())); + } else { // not adaptive freelists + set_inFreeRange(false); + // Add the free range back to the free list if it is not already + // there. + if (!freeRangeInFreeLists()) { + assert(freeFinger() < addr, "the finger pointeth off base"); + if (CMSTraceSweeper) { + gclog_or_tty->print("Sweep:put_free_blk 0x%x (%d) " + "[coalesced:%d]\n", + freeFinger(), pointer_delta(addr, freeFinger()), + lastFreeRangeCoalesced()); + } + _sp->addChunkAndRepairOffsetTable(freeFinger(), + pointer_delta(addr, freeFinger()), lastFreeRangeCoalesced()); + } + } + } + + // Common code path for original and adaptive free lists. + + // this object is live: we'd normally expect this to be + // an oop, and like to assert the following: + // assert(oop(addr)->is_oop(), "live block should be an oop"); + // However, as we commented above, this may be an object whose + // header hasn't yet been initialized. + size_t size; + assert(_bitMap->isMarked(addr), "Tautology for this control point"); + if (_bitMap->isMarked(addr + 1)) { + // Determine the size from the bit map, rather than trying to + // compute it from the object header. + HeapWord* nextOneAddr = _bitMap->getNextMarkedWordAddress(addr + 2); + size = pointer_delta(nextOneAddr + 1, addr); + assert(size == CompactibleFreeListSpace::adjustObjectSize(size), + "alignment problem"); + + #ifdef DEBUG + if (oop(addr)->klass() != NULL && + ( !_collector->cms_should_unload_classes() + || oop(addr)->is_parsable())) { + // Ignore mark word because we are running concurrent with mutators + assert(oop(addr)->is_oop(true), "live block should be an oop"); + assert(size == + CompactibleFreeListSpace::adjustObjectSize(oop(addr)->size()), + "P-mark and computed size do not agree"); + } + #endif + + } else { + // This should be an initialized object that's alive. + assert(oop(addr)->klass() != NULL && + (!_collector->cms_should_unload_classes() + || oop(addr)->is_parsable()), + "Should be an initialized object"); + // Ignore mark word because we are running concurrent with mutators + assert(oop(addr)->is_oop(true), "live block should be an oop"); + // Verify that the bit map has no bits marked between + // addr and purported end of this block. + size = CompactibleFreeListSpace::adjustObjectSize(oop(addr)->size()); + assert(size >= 3, "Necessary for Printezis marks to work"); + assert(!_bitMap->isMarked(addr+1), "Tautology for this control point"); + DEBUG_ONLY(_bitMap->verifyNoOneBitsInRange(addr+2, addr+size);) + } + return size; +} + +void SweepClosure::doPostIsFreeOrGarbageChunk(FreeChunk* fc, + size_t chunkSize) { + // doPostIsFreeOrGarbageChunk() should only be called in the smart allocation + // scheme. + bool fcInFreeLists = fc->isFree(); + assert(_sp->adaptive_freelists(), "Should only be used in this case."); + assert((HeapWord*)fc <= _limit, "sweep invariant"); + if (CMSTestInFreeList && fcInFreeLists) { + assert(_sp->verifyChunkInFreeLists(fc), + "free chunk is not in free lists"); + } + + + if (CMSTraceSweeper) { + gclog_or_tty->print_cr(" -- pick up another chunk at 0x%x (%d)", fc, chunkSize); + } + + HeapWord* addr = (HeapWord*) fc; + + bool coalesce; + size_t left = pointer_delta(addr, freeFinger()); + size_t right = chunkSize; + switch (FLSCoalescePolicy) { + // numeric value forms a coalition aggressiveness metric + case 0: { // never coalesce + coalesce = false; + break; + } + case 1: { // coalesce if left & right chunks on overpopulated lists + coalesce = _sp->coalOverPopulated(left) && + _sp->coalOverPopulated(right); + break; + } + case 2: { // coalesce if left chunk on overpopulated list (default) + coalesce = _sp->coalOverPopulated(left); + break; + } + case 3: { // coalesce if left OR right chunk on overpopulated list + coalesce = _sp->coalOverPopulated(left) || + _sp->coalOverPopulated(right); + break; + } + case 4: { // always coalesce + coalesce = true; + break; + } + default: + ShouldNotReachHere(); + } + + // Should the current free range be coalesced? + // If the chunk is in a free range and either we decided to coalesce above + // or the chunk is near the large block at the end of the heap + // (isNearLargestChunk() returns true), then coalesce this chunk. + bool doCoalesce = inFreeRange() && + (coalesce || _g->isNearLargestChunk((HeapWord*)fc)); + if (doCoalesce) { + // Coalesce the current free range on the left with the new + // chunk on the right. If either is on a free list, + // it must be removed from the list and stashed in the closure. + if (freeRangeInFreeLists()) { + FreeChunk* ffc = (FreeChunk*)freeFinger(); + assert(ffc->size() == pointer_delta(addr, freeFinger()), + "Size of free range is inconsistent with chunk size."); + if (CMSTestInFreeList) { + assert(_sp->verifyChunkInFreeLists(ffc), + "Chunk is not in free lists"); + } + _sp->coalDeath(ffc->size()); + _sp->removeFreeChunkFromFreeLists(ffc); + set_freeRangeInFreeLists(false); + } + if (fcInFreeLists) { + _sp->coalDeath(chunkSize); + assert(fc->size() == chunkSize, + "The chunk has the wrong size or is not in the free lists"); + _sp->removeFreeChunkFromFreeLists(fc); + } + set_lastFreeRangeCoalesced(true); + } else { // not in a free range and/or should not coalesce + // Return the current free range and start a new one. + if (inFreeRange()) { + // In a free range but cannot coalesce with the right hand chunk. + // Put the current free range into the free lists. + flushCurFreeChunk(freeFinger(), + pointer_delta(addr, freeFinger())); + } + // Set up for new free range. Pass along whether the right hand + // chunk is in the free lists. + initialize_free_range((HeapWord*)fc, fcInFreeLists); + } +} +void SweepClosure::flushCurFreeChunk(HeapWord* chunk, size_t size) { + assert(inFreeRange(), "Should only be called if currently in a free range."); + assert(size > 0, + "A zero sized chunk cannot be added to the free lists."); + if (!freeRangeInFreeLists()) { + if(CMSTestInFreeList) { + FreeChunk* fc = (FreeChunk*) chunk; + fc->setSize(size); + assert(!_sp->verifyChunkInFreeLists(fc), + "chunk should not be in free lists yet"); + } + if (CMSTraceSweeper) { + gclog_or_tty->print_cr(" -- add free block 0x%x (%d) to free lists", + chunk, size); + } + // A new free range is going to be starting. The current + // free range has not been added to the free lists yet or + // was removed so add it back. + // If the current free range was coalesced, then the death + // of the free range was recorded. Record a birth now. + if (lastFreeRangeCoalesced()) { + _sp->coalBirth(size); + } + _sp->addChunkAndRepairOffsetTable(chunk, size, + lastFreeRangeCoalesced()); + } + set_inFreeRange(false); + set_freeRangeInFreeLists(false); +} + +// We take a break if we've been at this for a while, +// so as to avoid monopolizing the locks involved. +void SweepClosure::do_yield_work(HeapWord* addr) { + // Return current free chunk being used for coalescing (if any) + // to the appropriate freelist. After yielding, the next + // free block encountered will start a coalescing range of + // free blocks. If the next free block is adjacent to the + // chunk just flushed, they will need to wait for the next + // sweep to be coalesced. + if (inFreeRange()) { + flushCurFreeChunk(freeFinger(), pointer_delta(addr, freeFinger())); + } + + // First give up the locks, then yield, then re-lock. + // We should probably use a constructor/destructor idiom to + // do this unlock/lock or modify the MutexUnlocker class to + // serve our purpose. XXX + assert_lock_strong(_bitMap->lock()); + assert_lock_strong(_freelistLock); + assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "CMS thread should hold CMS token"); + _bitMap->lock()->unlock(); + _freelistLock->unlock(); + ConcurrentMarkSweepThread::desynchronize(true); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + _collector->stopTimer(); + GCPauseTimer p(_collector->size_policy()->concurrent_timer_ptr()); + if (PrintCMSStatistics != 0) { + _collector->incrementYields(); + } + _collector->icms_wait(); + + // See the comment in coordinator_yield() + for (unsigned i = 0; i < CMSYieldSleepCount && + ConcurrentMarkSweepThread::should_yield() && + !CMSCollector::foregroundGCIsActive(); ++i) { + os::sleep(Thread::current(), 1, false); + ConcurrentMarkSweepThread::acknowledge_yield_request(); + } + + ConcurrentMarkSweepThread::synchronize(true); + _freelistLock->lock(); + _bitMap->lock()->lock_without_safepoint_check(); + _collector->startTimer(); +} + +#ifndef PRODUCT +// This is actually very useful in a product build if it can +// be called from the debugger. Compile it into the product +// as needed. +bool debug_verifyChunkInFreeLists(FreeChunk* fc) { + return debug_cms_space->verifyChunkInFreeLists(fc); +} + +void SweepClosure::record_free_block_coalesced(FreeChunk* fc) const { + if (CMSTraceSweeper) { + gclog_or_tty->print("Sweep:coal_free_blk 0x%x (%d)\n", fc, fc->size()); + } +} +#endif + +// CMSIsAliveClosure +bool CMSIsAliveClosure::do_object_b(oop obj) { + HeapWord* addr = (HeapWord*)obj; + return addr != NULL && + (!_span.contains(addr) || _bit_map->isMarked(addr)); +} + +// CMSKeepAliveClosure: the serial version +void CMSKeepAliveClosure::do_oop(oop* p) { + oop this_oop = *p; + HeapWord* addr = (HeapWord*)this_oop; + if (_span.contains(addr) && + !_bit_map->isMarked(addr)) { + _bit_map->mark(addr); + bool simulate_overflow = false; + NOT_PRODUCT( + if (CMSMarkStackOverflowALot && + _collector->simulate_overflow()) { + // simulate a stack overflow + simulate_overflow = true; + } + ) + if (simulate_overflow || !_mark_stack->push(this_oop)) { + _collector->push_on_overflow_list(this_oop); + _collector->_ser_kac_ovflw++; + } + } +} + +// CMSParKeepAliveClosure: a parallel version of the above. +// The work queues are private to each closure (thread), +// but (may be) available for stealing by other threads. +void CMSParKeepAliveClosure::do_oop(oop* p) { + oop this_oop = *p; + HeapWord* addr = (HeapWord*)this_oop; + if (_span.contains(addr) && + !_bit_map->isMarked(addr)) { + // In general, during recursive tracing, several threads + // may be concurrently getting here; the first one to + // "tag" it, claims it. + if (_bit_map->par_mark(addr)) { + bool res = _work_queue->push(this_oop); + assert(res, "Low water mark should be much less than capacity"); + // Do a recursive trim in the hope that this will keep + // stack usage lower, but leave some oops for potential stealers + trim_queue(_low_water_mark); + } // Else, another thread got there first + } +} + +void CMSParKeepAliveClosure::trim_queue(uint max) { + while (_work_queue->size() > max) { + oop new_oop; + if (_work_queue->pop_local(new_oop)) { + assert(new_oop != NULL && new_oop->is_oop(), "Expected an oop"); + assert(_bit_map->isMarked((HeapWord*)new_oop), + "no white objects on this stack!"); + assert(_span.contains((HeapWord*)new_oop), "Out of bounds oop"); + // iterate over the oops in this oop, marking and pushing + // the ones in CMS heap (i.e. in _span). + new_oop->oop_iterate(&_mark_and_push); + } + } +} + +void CMSInnerParMarkAndPushClosure::do_oop(oop* p) { + oop this_oop = *p; + HeapWord* addr = (HeapWord*)this_oop; + if (_span.contains(addr) && + !_bit_map->isMarked(addr)) { + if (_bit_map->par_mark(addr)) { + bool simulate_overflow = false; + NOT_PRODUCT( + if (CMSMarkStackOverflowALot && + _collector->par_simulate_overflow()) { + // simulate a stack overflow + simulate_overflow = true; + } + ) + if (simulate_overflow || !_work_queue->push(this_oop)) { + _collector->par_push_on_overflow_list(this_oop); + _collector->_par_kac_ovflw++; + } + } // Else another thread got there already + } +} + +////////////////////////////////////////////////////////////////// +// CMSExpansionCause ///////////////////////////// +////////////////////////////////////////////////////////////////// +const char* CMSExpansionCause::to_string(CMSExpansionCause::Cause cause) { + switch (cause) { + case _no_expansion: + return "No expansion"; + case _satisfy_free_ratio: + return "Free ratio"; + case _satisfy_promotion: + return "Satisfy promotion"; + case _satisfy_allocation: + return "allocation"; + case _allocate_par_lab: + return "Par LAB"; + case _allocate_par_spooling_space: + return "Par Spooling Space"; + case _adaptive_size_policy: + return "Ergonomics"; + default: + return "unknown"; + } +} + +void CMSDrainMarkingStackClosure::do_void() { + // the max number to take from overflow list at a time + const size_t num = _mark_stack->capacity()/4; + while (!_mark_stack->isEmpty() || + // if stack is empty, check the overflow list + _collector->take_from_overflow_list(num, _mark_stack)) { + oop this_oop = _mark_stack->pop(); + HeapWord* addr = (HeapWord*)this_oop; + assert(_span.contains(addr), "Should be within span"); + assert(_bit_map->isMarked(addr), "Should be marked"); + assert(this_oop->is_oop(), "Should be an oop"); + this_oop->oop_iterate(_keep_alive); + } +} + +void CMSParDrainMarkingStackClosure::do_void() { + // drain queue + trim_queue(0); +} + +// Trim our work_queue so its length is below max at return +void CMSParDrainMarkingStackClosure::trim_queue(uint max) { + while (_work_queue->size() > max) { + oop new_oop; + if (_work_queue->pop_local(new_oop)) { + assert(new_oop->is_oop(), "Expected an oop"); + assert(_bit_map->isMarked((HeapWord*)new_oop), + "no white objects on this stack!"); + assert(_span.contains((HeapWord*)new_oop), "Out of bounds oop"); + // iterate over the oops in this oop, marking and pushing + // the ones in CMS heap (i.e. in _span). + new_oop->oop_iterate(&_mark_and_push); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Support for Marking Stack Overflow list handling and related code +//////////////////////////////////////////////////////////////////// +// Much of the following code is similar in shape and spirit to the +// code used in ParNewGC. We should try and share that code +// as much as possible in the future. + +#ifndef PRODUCT +// Debugging support for CMSStackOverflowALot + +// It's OK to call this multi-threaded; the worst thing +// that can happen is that we'll get a bunch of closely +// spaced simulated oveflows, but that's OK, in fact +// probably good as it would exercise the overflow code +// under contention. +bool CMSCollector::simulate_overflow() { + if (_overflow_counter-- <= 0) { // just being defensive + _overflow_counter = CMSMarkStackOverflowInterval; + return true; + } else { + return false; + } +} + +bool CMSCollector::par_simulate_overflow() { + return simulate_overflow(); +} +#endif + +// Single-threaded +bool CMSCollector::take_from_overflow_list(size_t num, CMSMarkStack* stack) { + assert(stack->isEmpty(), "Expected precondition"); + assert(stack->capacity() > num, "Shouldn't bite more than can chew"); + size_t i = num; + oop cur = _overflow_list; + const markOop proto = markOopDesc::prototype(); + NOT_PRODUCT(size_t n = 0;) + for (oop next; i > 0 && cur != NULL; cur = next, i--) { + next = oop(cur->mark()); + cur->set_mark(proto); // until proven otherwise + assert(cur->is_oop(), "Should be an oop"); + bool res = stack->push(cur); + assert(res, "Bit off more than can chew?"); + NOT_PRODUCT(n++;) + } + _overflow_list = cur; +#ifndef PRODUCT + assert(_num_par_pushes >= n, "Too many pops?"); + _num_par_pushes -=n; +#endif + return !stack->isEmpty(); +} + +// Multi-threaded; use CAS to break off a prefix +bool CMSCollector::par_take_from_overflow_list(size_t num, + OopTaskQueue* work_q) { + assert(work_q->size() == 0, "That's the current policy"); + assert(num < work_q->max_elems(), "Can't bite more than we can chew"); + if (_overflow_list == NULL) { + return false; + } + // Grab the entire list; we'll put back a suffix + oop prefix = (oop)Atomic::xchg_ptr(NULL, &_overflow_list); + if (prefix == NULL) { // someone grabbed it before we did ... + // ... we could spin for a short while, but for now we don't + return false; + } + size_t i = num; + oop cur = prefix; + for (; i > 1 && cur->mark() != NULL; cur = oop(cur->mark()), i--); + if (cur->mark() != NULL) { + oop suffix_head = cur->mark(); // suffix will be put back on global list + cur->set_mark(NULL); // break off suffix + // Find tail of suffix so we can prepend suffix to global list + for (cur = suffix_head; cur->mark() != NULL; cur = (oop)(cur->mark())); + oop suffix_tail = cur; + assert(suffix_tail != NULL && suffix_tail->mark() == NULL, + "Tautology"); + oop observed_overflow_list = _overflow_list; + do { + cur = observed_overflow_list; + suffix_tail->set_mark(markOop(cur)); + observed_overflow_list = + (oop) Atomic::cmpxchg_ptr(suffix_head, &_overflow_list, cur); + } while (cur != observed_overflow_list); + } + + // Push the prefix elements on work_q + assert(prefix != NULL, "control point invariant"); + const markOop proto = markOopDesc::prototype(); + oop next; + NOT_PRODUCT(size_t n = 0;) + for (cur = prefix; cur != NULL; cur = next) { + next = oop(cur->mark()); + cur->set_mark(proto); // until proven otherwise + assert(cur->is_oop(), "Should be an oop"); + bool res = work_q->push(cur); + assert(res, "Bit off more than we can chew?"); + NOT_PRODUCT(n++;) + } +#ifndef PRODUCT + assert(_num_par_pushes >= n, "Too many pops?"); + Atomic::add_ptr(-(intptr_t)n, &_num_par_pushes); +#endif + return true; +} + +// Single-threaded +void CMSCollector::push_on_overflow_list(oop p) { + NOT_PRODUCT(_num_par_pushes++;) + assert(p->is_oop(), "Not an oop"); + preserve_mark_if_necessary(p); + p->set_mark((markOop)_overflow_list); + _overflow_list = p; +} + +// Multi-threaded; use CAS to prepend to overflow list +void CMSCollector::par_push_on_overflow_list(oop p) { + NOT_PRODUCT(Atomic::inc_ptr(&_num_par_pushes);) + assert(p->is_oop(), "Not an oop"); + par_preserve_mark_if_necessary(p); + oop observed_overflow_list = _overflow_list; + oop cur_overflow_list; + do { + cur_overflow_list = observed_overflow_list; + p->set_mark(markOop(cur_overflow_list)); + observed_overflow_list = + (oop) Atomic::cmpxchg_ptr(p, &_overflow_list, cur_overflow_list); + } while (cur_overflow_list != observed_overflow_list); +} + +// Single threaded +// General Note on GrowableArray: pushes may silently fail +// because we are (temporarily) out of C-heap for expanding +// the stack. The problem is quite ubiquitous and affects +// a lot of code in the JVM. The prudent thing for GrowableArray +// to do (for now) is to exit with an error. However, that may +// be too draconian in some cases because the caller may be +// able to recover without much harm. For suych cases, we +// should probably introduce a "soft_push" method which returns +// an indication of success or failure with the assumption that +// the caller may be able to recover from a failure; code in +// the VM can then be changed, incrementally, to deal with such +// failures where possible, thus, incrementally hardening the VM +// in such low resource situations. +void CMSCollector::preserve_mark_work(oop p, markOop m) { + int PreserveMarkStackSize = 128; + + if (_preserved_oop_stack == NULL) { + assert(_preserved_mark_stack == NULL, + "bijection with preserved_oop_stack"); + // Allocate the stacks + _preserved_oop_stack = new (ResourceObj::C_HEAP) + GrowableArray(PreserveMarkStackSize, true); + _preserved_mark_stack = new (ResourceObj::C_HEAP) + GrowableArray(PreserveMarkStackSize, true); + if (_preserved_oop_stack == NULL || _preserved_mark_stack == NULL) { + vm_exit_out_of_memory(2* PreserveMarkStackSize * sizeof(oop) /* punt */, + "Preserved Mark/Oop Stack for CMS (C-heap)"); + } + } + _preserved_oop_stack->push(p); + _preserved_mark_stack->push(m); + assert(m == p->mark(), "Mark word changed"); + assert(_preserved_oop_stack->length() == _preserved_mark_stack->length(), + "bijection"); +} + +// Single threaded +void CMSCollector::preserve_mark_if_necessary(oop p) { + markOop m = p->mark(); + if (m->must_be_preserved(p)) { + preserve_mark_work(p, m); + } +} + +void CMSCollector::par_preserve_mark_if_necessary(oop p) { + markOop m = p->mark(); + if (m->must_be_preserved(p)) { + MutexLockerEx x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag); + // Even though we read the mark word without holding + // the lock, we are assured that it will not change + // because we "own" this oop, so no other thread can + // be trying to push it on the overflow list; see + // the assertion in preserve_mark_work() that checks + // that m == p->mark(). + preserve_mark_work(p, m); + } +} + +// We should be able to do this multi-threaded, +// a chunk of stack being a task (this is +// correct because each oop only ever appears +// once in the overflow list. However, it's +// not very easy to completely overlap this with +// other operations, so will generally not be done +// until all work's been completed. Because we +// expect the preserved oop stack (set) to be small, +// it's probably fine to do this single-threaded. +// We can explore cleverer concurrent/overlapped/parallel +// processing of preserved marks if we feel the +// need for this in the future. Stack overflow should +// be so rare in practice and, when it happens, its +// effect on performance so great that this will +// likely just be in the noise anyway. +void CMSCollector::restore_preserved_marks_if_any() { + if (_preserved_oop_stack == NULL) { + assert(_preserved_mark_stack == NULL, + "bijection with preserved_oop_stack"); + return; + } + + assert(SafepointSynchronize::is_at_safepoint(), + "world should be stopped"); + assert(Thread::current()->is_ConcurrentGC_thread() || + Thread::current()->is_VM_thread(), + "should be single-threaded"); + + int length = _preserved_oop_stack->length(); + assert(_preserved_mark_stack->length() == length, "bijection"); + for (int i = 0; i < length; i++) { + oop p = _preserved_oop_stack->at(i); + assert(p->is_oop(), "Should be an oop"); + assert(_span.contains(p), "oop should be in _span"); + assert(p->mark() == markOopDesc::prototype(), + "Set when taken from overflow list"); + markOop m = _preserved_mark_stack->at(i); + p->set_mark(m); + } + _preserved_mark_stack->clear(); + _preserved_oop_stack->clear(); + assert(_preserved_mark_stack->is_empty() && + _preserved_oop_stack->is_empty(), + "stacks were cleared above"); +} + +#ifndef PRODUCT +bool CMSCollector::no_preserved_marks() const { + return ( ( _preserved_mark_stack == NULL + && _preserved_oop_stack == NULL) + || ( _preserved_mark_stack->is_empty() + && _preserved_oop_stack->is_empty())); +} +#endif + +CMSAdaptiveSizePolicy* ASConcurrentMarkSweepGeneration::cms_size_policy() const +{ + GenCollectedHeap* gch = (GenCollectedHeap*) GenCollectedHeap::heap(); + CMSAdaptiveSizePolicy* size_policy = + (CMSAdaptiveSizePolicy*) gch->gen_policy()->size_policy(); + assert(size_policy->is_gc_cms_adaptive_size_policy(), + "Wrong type for size policy"); + return size_policy; +} + +void ASConcurrentMarkSweepGeneration::resize(size_t cur_promo_size, + size_t desired_promo_size) { + if (cur_promo_size < desired_promo_size) { + size_t expand_bytes = desired_promo_size - cur_promo_size; + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" ASConcurrentMarkSweepGeneration::resize " + "Expanding tenured generation by " SIZE_FORMAT " (bytes)", + expand_bytes); + } + expand(expand_bytes, + MinHeapDeltaBytes, + CMSExpansionCause::_adaptive_size_policy); + } else if (desired_promo_size < cur_promo_size) { + size_t shrink_bytes = cur_promo_size - desired_promo_size; + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" ASConcurrentMarkSweepGeneration::resize " + "Shrinking tenured generation by " SIZE_FORMAT " (bytes)", + shrink_bytes); + } + shrink(shrink_bytes); + } +} + +CMSGCAdaptivePolicyCounters* ASConcurrentMarkSweepGeneration::gc_adaptive_policy_counters() { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + CMSGCAdaptivePolicyCounters* counters = + (CMSGCAdaptivePolicyCounters*) gch->collector_policy()->counters(); + assert(counters->kind() == GCPolicyCounters::CMSGCAdaptivePolicyCountersKind, + "Wrong kind of counters"); + return counters; +} + + +void ASConcurrentMarkSweepGeneration::update_counters() { + if (UsePerfData) { + _space_counters->update_all(); + _gen_counters->update_all(); + CMSGCAdaptivePolicyCounters* counters = gc_adaptive_policy_counters(); + GenCollectedHeap* gch = GenCollectedHeap::heap(); + CMSGCStats* gc_stats_l = (CMSGCStats*) gc_stats(); + assert(gc_stats_l->kind() == GCStats::CMSGCStatsKind, + "Wrong gc statistics type"); + counters->update_counters(gc_stats_l); + } +} + +void ASConcurrentMarkSweepGeneration::update_counters(size_t used) { + if (UsePerfData) { + _space_counters->update_used(used); + _space_counters->update_capacity(); + _gen_counters->update_all(); + + CMSGCAdaptivePolicyCounters* counters = gc_adaptive_policy_counters(); + GenCollectedHeap* gch = GenCollectedHeap::heap(); + CMSGCStats* gc_stats_l = (CMSGCStats*) gc_stats(); + assert(gc_stats_l->kind() == GCStats::CMSGCStatsKind, + "Wrong gc statistics type"); + counters->update_counters(gc_stats_l); + } +} + +// The desired expansion delta is computed so that: +// . desired free percentage or greater is used +void ASConcurrentMarkSweepGeneration::compute_new_size() { + assert_locked_or_safepoint(Heap_lock); + + GenCollectedHeap* gch = (GenCollectedHeap*) GenCollectedHeap::heap(); + + // If incremental collection failed, we just want to expand + // to the limit. + if (incremental_collection_failed()) { + clear_incremental_collection_failed(); + grow_to_reserved(); + return; + } + + assert(UseAdaptiveSizePolicy, "Should be using adaptive sizing"); + + assert(gch->kind() == CollectedHeap::GenCollectedHeap, + "Wrong type of heap"); + int prev_level = level() - 1; + assert(prev_level >= 0, "The cms generation is the lowest generation"); + Generation* prev_gen = gch->get_gen(prev_level); + assert(prev_gen->kind() == Generation::ASParNew, + "Wrong type of young generation"); + ParNewGeneration* younger_gen = (ParNewGeneration*) prev_gen; + size_t cur_eden = younger_gen->eden()->capacity(); + CMSAdaptiveSizePolicy* size_policy = cms_size_policy(); + size_t cur_promo = free(); + size_policy->compute_tenured_generation_free_space(cur_promo, + max_available(), + cur_eden); + resize(cur_promo, size_policy->promo_size()); + + // Record the new size of the space in the cms generation + // that is available for promotions. This is temporary. + // It should be the desired promo size. + size_policy->avg_cms_promo()->sample(free()); + size_policy->avg_old_live()->sample(used()); + + if (UsePerfData) { + CMSGCAdaptivePolicyCounters* counters = gc_adaptive_policy_counters(); + counters->update_cms_capacity_counter(capacity()); + } +} + +void ASConcurrentMarkSweepGeneration::shrink_by(size_t desired_bytes) { + assert_locked_or_safepoint(Heap_lock); + assert_lock_strong(freelistLock()); + HeapWord* old_end = _cmsSpace->end(); + HeapWord* unallocated_start = _cmsSpace->unallocated_block(); + assert(old_end >= unallocated_start, "Miscalculation of unallocated_start"); + FreeChunk* chunk_at_end = find_chunk_at_end(); + if (chunk_at_end == NULL) { + // No room to shrink + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("No room to shrink: old_end " + PTR_FORMAT " unallocated_start " PTR_FORMAT + " chunk_at_end " PTR_FORMAT, + old_end, unallocated_start, chunk_at_end); + } + return; + } else { + + // Find the chunk at the end of the space and determine + // how much it can be shrunk. + size_t shrinkable_size_in_bytes = chunk_at_end->size(); + size_t aligned_shrinkable_size_in_bytes = + align_size_down(shrinkable_size_in_bytes, os::vm_page_size()); + assert(unallocated_start <= chunk_at_end->end(), + "Inconsistent chunk at end of space"); + size_t bytes = MIN2(desired_bytes, aligned_shrinkable_size_in_bytes); + size_t word_size_before = heap_word_size(_virtual_space.committed_size()); + + // Shrink the underlying space + _virtual_space.shrink_by(bytes); + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("ConcurrentMarkSweepGeneration::shrink_by:" + " desired_bytes " SIZE_FORMAT + " shrinkable_size_in_bytes " SIZE_FORMAT + " aligned_shrinkable_size_in_bytes " SIZE_FORMAT + " bytes " SIZE_FORMAT, + desired_bytes, shrinkable_size_in_bytes, + aligned_shrinkable_size_in_bytes, bytes); + gclog_or_tty->print_cr(" old_end " SIZE_FORMAT + " unallocated_start " SIZE_FORMAT, + old_end, unallocated_start); + } + + // If the space did shrink (shrinking is not guaranteed), + // shrink the chunk at the end by the appropriate amount. + if (((HeapWord*)_virtual_space.high()) < old_end) { + size_t new_word_size = + heap_word_size(_virtual_space.committed_size()); + + // Have to remove the chunk from the dictionary because it is changing + // size and might be someplace elsewhere in the dictionary. + + // Get the chunk at end, shrink it, and put it + // back. + _cmsSpace->removeChunkFromDictionary(chunk_at_end); + size_t word_size_change = word_size_before - new_word_size; + size_t chunk_at_end_old_size = chunk_at_end->size(); + assert(chunk_at_end_old_size >= word_size_change, + "Shrink is too large"); + chunk_at_end->setSize(chunk_at_end_old_size - + word_size_change); + _cmsSpace->freed((HeapWord*) chunk_at_end->end(), + word_size_change); + + _cmsSpace->returnChunkToDictionary(chunk_at_end); + + MemRegion mr(_cmsSpace->bottom(), new_word_size); + _bts->resize(new_word_size); // resize the block offset shared array + Universe::heap()->barrier_set()->resize_covered_region(mr); + _cmsSpace->assert_locked(); + _cmsSpace->set_end((HeapWord*)_virtual_space.high()); + + NOT_PRODUCT(_cmsSpace->dictionary()->verify()); + + // update the space and generation capacity counters + if (UsePerfData) { + _space_counters->update_capacity(); + _gen_counters->update_all(); + } + + if (Verbose && PrintGCDetails) { + size_t new_mem_size = _virtual_space.committed_size(); + size_t old_mem_size = new_mem_size + bytes; + gclog_or_tty->print_cr("Shrinking %s from %ldK by %ldK to %ldK", + name(), old_mem_size/K, bytes/K, new_mem_size/K); + } + } + + assert(_cmsSpace->unallocated_block() <= _cmsSpace->end(), + "Inconsistency at end of space"); + assert(chunk_at_end->end() == _cmsSpace->end(), + "Shrinking is inconsistent"); + return; + } +} + +// Transfer some number of overflown objects to usual marking +// stack. Return true if some objects were transferred. +bool MarkRefsIntoAndScanClosure::take_from_overflow_list() { + size_t num = MIN2((size_t)_mark_stack->capacity()/4, + (size_t)ParGCDesiredObjsFromOverflowList); + + bool res = _collector->take_from_overflow_list(num, _mark_stack); + assert(_collector->overflow_list_is_empty() || res, + "If list is not empty, we should have taken something"); + assert(!res || !_mark_stack->isEmpty(), + "If we took something, it should now be on our stack"); + return res; +} + +size_t MarkDeadObjectsClosure::do_blk(HeapWord* addr) { + size_t res = _sp->block_size_no_stall(addr, _collector); + assert(res != 0, "Should always be able to compute a size"); + if (_sp->block_is_obj(addr)) { + if (_live_bit_map->isMarked(addr)) { + // It can't have been dead in a previous cycle + guarantee(!_dead_bit_map->isMarked(addr), "No resurrection!"); + } else { + _dead_bit_map->mark(addr); // mark the dead object + } + } + return res; +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp new file mode 100644 index 00000000000..f4f790ebab1 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp @@ -0,0 +1,1822 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ConcurrentMarkSweepGeneration is in support of a concurrent +// mark-sweep old generation in the Detlefs-Printezis--Boehm-Demers-Schenker +// style. We assume, for now, that this generation is always the +// seniormost generation (modulo the PermGeneration), and for simplicity +// in the first implementation, that this generation is a single compactible +// space. Neither of these restrictions appears essential, and will be +// relaxed in the future when more time is available to implement the +// greater generality (and there's a need for it). +// +// Concurrent mode failures are currently handled by +// means of a sliding mark-compact. + +class CMSAdaptiveSizePolicy; +class CMSConcMarkingTask; +class CMSGCAdaptivePolicyCounters; +class ConcurrentMarkSweepGeneration; +class ConcurrentMarkSweepPolicy; +class ConcurrentMarkSweepThread; +class CompactibleFreeListSpace; +class FreeChunk; +class PromotionInfo; +class ScanMarkedObjectsAgainCarefullyClosure; + +// A generic CMS bit map. It's the basis for both the CMS marking bit map +// as well as for the mod union table (in each case only a subset of the +// methods are used). This is essentially a wrapper around the BitMap class, +// with one bit per (1<<_shifter) HeapWords. (i.e. for the marking bit map, +// we have _shifter == 0. and for the mod union table we have +// shifter == CardTableModRefBS::card_shift - LogHeapWordSize.) +// XXX 64-bit issues in BitMap? +class CMSBitMap VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + + HeapWord* _bmStartWord; // base address of range covered by map + size_t _bmWordSize; // map size (in #HeapWords covered) + const int _shifter; // shifts to convert HeapWord to bit position + VirtualSpace _virtual_space; // underlying the bit map + BitMap _bm; // the bit map itself + public: + Mutex* const _lock; // mutex protecting _bm; + + public: + // constructor + CMSBitMap(int shifter, int mutex_rank, const char* mutex_name); + + // allocates the actual storage for the map + bool allocate(MemRegion mr); + // field getter + Mutex* lock() const { return _lock; } + // locking verifier convenience function + void assert_locked() const PRODUCT_RETURN; + + // inquiries + HeapWord* startWord() const { return _bmStartWord; } + size_t sizeInWords() const { return _bmWordSize; } + size_t sizeInBits() const { return _bm.size(); } + // the following is one past the last word in space + HeapWord* endWord() const { return _bmStartWord + _bmWordSize; } + + // reading marks + bool isMarked(HeapWord* addr) const; + bool par_isMarked(HeapWord* addr) const; // do not lock checks + bool isUnmarked(HeapWord* addr) const; + bool isAllClear() const; + + // writing marks + void mark(HeapWord* addr); + // For marking by parallel GC threads; + // returns true if we did, false if another thread did + bool par_mark(HeapWord* addr); + + void mark_range(MemRegion mr); + void par_mark_range(MemRegion mr); + void mark_large_range(MemRegion mr); + void par_mark_large_range(MemRegion mr); + void par_clear(HeapWord* addr); // For unmarking by parallel GC threads. + void clear_range(MemRegion mr); + void par_clear_range(MemRegion mr); + void clear_large_range(MemRegion mr); + void par_clear_large_range(MemRegion mr); + void clear_all(); + void clear_all_incrementally(); // Not yet implemented!! + + NOT_PRODUCT( + // checks the memory region for validity + void region_invariant(MemRegion mr); + ) + + // iteration + void iterate(BitMapClosure* cl) { + _bm.iterate(cl); + } + void iterate(BitMapClosure* cl, HeapWord* left, HeapWord* right); + void dirty_range_iterate_clear(MemRegionClosure* cl); + void dirty_range_iterate_clear(MemRegion mr, MemRegionClosure* cl); + + // auxiliary support for iteration + HeapWord* getNextMarkedWordAddress(HeapWord* addr) const; + HeapWord* getNextMarkedWordAddress(HeapWord* start_addr, + HeapWord* end_addr) const; + HeapWord* getNextUnmarkedWordAddress(HeapWord* addr) const; + HeapWord* getNextUnmarkedWordAddress(HeapWord* start_addr, + HeapWord* end_addr) const; + MemRegion getAndClearMarkedRegion(HeapWord* addr); + MemRegion getAndClearMarkedRegion(HeapWord* start_addr, + HeapWord* end_addr); + + // conversion utilities + HeapWord* offsetToHeapWord(size_t offset) const; + size_t heapWordToOffset(HeapWord* addr) const; + size_t heapWordDiffToOffsetDiff(size_t diff) const; + + // debugging + // is this address range covered by the bit-map? + NOT_PRODUCT( + bool covers(MemRegion mr) const; + bool covers(HeapWord* start, size_t size = 0) const; + ) + void verifyNoOneBitsInRange(HeapWord* left, HeapWord* right) PRODUCT_RETURN; +}; + +// Represents a marking stack used by the CMS collector. +// Ideally this should be GrowableArray<> just like MSC's marking stack(s). +class CMSMarkStack: public CHeapObj { + // + friend class CMSCollector; // to get at expasion stats further below + // + + VirtualSpace _virtual_space; // space for the stack + oop* _base; // bottom of stack + size_t _index; // one more than last occupied index + size_t _capacity; // max #elements + Mutex _par_lock; // an advisory lock used in case of parallel access + NOT_PRODUCT(size_t _max_depth;) // max depth plumbed during run + + protected: + size_t _hit_limit; // we hit max stack size limit + size_t _failed_double; // we failed expansion before hitting limit + + public: + CMSMarkStack(): + _par_lock(Mutex::event, "CMSMarkStack._par_lock", true), + _hit_limit(0), + _failed_double(0) {} + + bool allocate(size_t size); + + size_t capacity() const { return _capacity; } + + oop pop() { + if (!isEmpty()) { + return _base[--_index] ; + } + return NULL; + } + + bool push(oop ptr) { + if (isFull()) { + return false; + } else { + _base[_index++] = ptr; + NOT_PRODUCT(_max_depth = MAX2(_max_depth, _index)); + return true; + } + } + + bool isEmpty() const { return _index == 0; } + bool isFull() const { + assert(_index <= _capacity, "buffer overflow"); + return _index == _capacity; + } + + size_t length() { return _index; } + + // "Parallel versions" of some of the above + oop par_pop() { + // lock and pop + MutexLockerEx x(&_par_lock, Mutex::_no_safepoint_check_flag); + return pop(); + } + + bool par_push(oop ptr) { + // lock and push + MutexLockerEx x(&_par_lock, Mutex::_no_safepoint_check_flag); + return push(ptr); + } + + // Forcibly reset the stack, losing all of its contents. + void reset() { + _index = 0; + } + + // Expand the stack, typically in response to an overflow condition + void expand(); + + // Compute the least valued stack element. + oop least_value(HeapWord* low) { + oop least = (oop)low; + for (size_t i = 0; i < _index; i++) { + least = MIN2(least, _base[i]); + } + return least; + } + + // Exposed here to allow stack expansion in || case + Mutex* par_lock() { return &_par_lock; } +}; + +class CardTableRS; +class CMSParGCThreadState; + +class ModUnionClosure: public MemRegionClosure { + protected: + CMSBitMap* _t; + public: + ModUnionClosure(CMSBitMap* t): _t(t) { } + void do_MemRegion(MemRegion mr); +}; + +class ModUnionClosurePar: public ModUnionClosure { + public: + ModUnionClosurePar(CMSBitMap* t): ModUnionClosure(t) { } + void do_MemRegion(MemRegion mr); +}; + +// Survivor Chunk Array in support of parallelization of +// Survivor Space rescan. +class ChunkArray: public CHeapObj { + size_t _index; + size_t _capacity; + HeapWord** _array; // storage for array + + public: + ChunkArray() : _index(0), _capacity(0), _array(NULL) {} + ChunkArray(HeapWord** a, size_t c): + _index(0), _capacity(c), _array(a) {} + + HeapWord** array() { return _array; } + void set_array(HeapWord** a) { _array = a; } + + size_t capacity() { return _capacity; } + void set_capacity(size_t c) { _capacity = c; } + + size_t end() { + assert(_index < capacity(), "_index out of bounds"); + return _index; + } // exclusive + + HeapWord* nth(size_t n) { + assert(n < end(), "Out of bounds access"); + return _array[n]; + } + + void reset() { + _index = 0; + } + + void record_sample(HeapWord* p, size_t sz) { + // For now we do not do anything with the size + if (_index < _capacity) { + _array[_index++] = p; + } + } +}; + +// +// Timing, allocation and promotion statistics for gc scheduling and incremental +// mode pacing. Most statistics are exponential averages. +// +class CMSStats VALUE_OBJ_CLASS_SPEC { + private: + ConcurrentMarkSweepGeneration* const _cms_gen; // The cms (old) gen. + + // The following are exponential averages with factor alpha: + // avg = (100 - alpha) * avg + alpha * cur_sample + // + // The durations measure: end_time[n] - start_time[n] + // The periods measure: start_time[n] - start_time[n-1] + // + // The cms period and duration include only concurrent collections; time spent + // in foreground cms collections due to System.gc() or because of a failure to + // keep up are not included. + // + // There are 3 alphas to "bootstrap" the statistics. The _saved_alpha is the + // real value, but is used only after the first period. A value of 100 is + // used for the first sample so it gets the entire weight. + unsigned int _saved_alpha; // 0-100 + unsigned int _gc0_alpha; + unsigned int _cms_alpha; + + double _gc0_duration; + double _gc0_period; + size_t _gc0_promoted; // bytes promoted per gc0 + double _cms_duration; + double _cms_duration_pre_sweep; // time from initiation to start of sweep + double _cms_duration_per_mb; + double _cms_period; + size_t _cms_allocated; // bytes of direct allocation per gc0 period + + // Timers. + elapsedTimer _cms_timer; + TimeStamp _gc0_begin_time; + TimeStamp _cms_begin_time; + TimeStamp _cms_end_time; + + // Snapshots of the amount used in the CMS generation. + size_t _cms_used_at_gc0_begin; + size_t _cms_used_at_gc0_end; + size_t _cms_used_at_cms_begin; + + // Used to prevent the duty cycle from being reduced in the middle of a cms + // cycle. + bool _allow_duty_cycle_reduction; + + enum { + _GC0_VALID = 0x1, + _CMS_VALID = 0x2, + _ALL_VALID = _GC0_VALID | _CMS_VALID + }; + + unsigned int _valid_bits; + + unsigned int _icms_duty_cycle; // icms duty cycle (0-100). + + protected: + + // Return a duty cycle that avoids wild oscillations, by limiting the amount + // of change between old_duty_cycle and new_duty_cycle (the latter is treated + // as a recommended value). + static unsigned int icms_damped_duty_cycle(unsigned int old_duty_cycle, + unsigned int new_duty_cycle); + unsigned int icms_update_duty_cycle_impl(); + + public: + CMSStats(ConcurrentMarkSweepGeneration* cms_gen, + unsigned int alpha = CMSExpAvgFactor); + + // Whether or not the statistics contain valid data; higher level statistics + // cannot be called until this returns true (they require at least one young + // gen and one cms cycle to have completed). + bool valid() const; + + // Record statistics. + void record_gc0_begin(); + void record_gc0_end(size_t cms_gen_bytes_used); + void record_cms_begin(); + void record_cms_end(); + + // Allow management of the cms timer, which must be stopped/started around + // yield points. + elapsedTimer& cms_timer() { return _cms_timer; } + void start_cms_timer() { _cms_timer.start(); } + void stop_cms_timer() { _cms_timer.stop(); } + + // Basic statistics; units are seconds or bytes. + double gc0_period() const { return _gc0_period; } + double gc0_duration() const { return _gc0_duration; } + size_t gc0_promoted() const { return _gc0_promoted; } + double cms_period() const { return _cms_period; } + double cms_duration() const { return _cms_duration; } + double cms_duration_per_mb() const { return _cms_duration_per_mb; } + size_t cms_allocated() const { return _cms_allocated; } + + size_t cms_used_at_gc0_end() const { return _cms_used_at_gc0_end;} + + // Seconds since the last background cms cycle began or ended. + double cms_time_since_begin() const; + double cms_time_since_end() const; + + // Higher level statistics--caller must check that valid() returns true before + // calling. + + // Returns bytes promoted per second of wall clock time. + double promotion_rate() const; + + // Returns bytes directly allocated per second of wall clock time. + double cms_allocation_rate() const; + + // Rate at which space in the cms generation is being consumed (sum of the + // above two). + double cms_consumption_rate() const; + + // Returns an estimate of the number of seconds until the cms generation will + // fill up, assuming no collection work is done. + double time_until_cms_gen_full() const; + + // Returns an estimate of the number of seconds remaining until + // the cms generation collection should start. + double time_until_cms_start() const; + + // End of higher level statistics. + + // Returns the cms incremental mode duty cycle, as a percentage (0-100). + unsigned int icms_duty_cycle() const { return _icms_duty_cycle; } + + // Update the duty cycle and return the new value. + unsigned int icms_update_duty_cycle(); + + // Debugging. + void print_on(outputStream* st) const PRODUCT_RETURN; + void print() const { print_on(gclog_or_tty); } +}; + +// A closure related to weak references processing which +// we embed in the CMSCollector, since we need to pass +// it to the reference processor for secondary filtering +// of references based on reachability of referent; +// see role of _is_alive_non_header closure in the +// ReferenceProcessor class. +// For objects in the CMS generation, this closure checks +// if the object is "live" (reachable). Used in weak +// reference processing. +class CMSIsAliveClosure: public BoolObjectClosure { + MemRegion _span; + const CMSBitMap* _bit_map; + + friend class CMSCollector; + protected: + void set_span(MemRegion span) { _span = span; } + public: + CMSIsAliveClosure(CMSBitMap* bit_map): + _bit_map(bit_map) { } + + CMSIsAliveClosure(MemRegion span, + CMSBitMap* bit_map): + _span(span), + _bit_map(bit_map) { } + void do_object(oop obj) { + assert(false, "not to be invoked"); + } + bool do_object_b(oop obj); +}; + + +// Implements AbstractRefProcTaskExecutor for CMS. +class CMSRefProcTaskExecutor: public AbstractRefProcTaskExecutor { +public: + + CMSRefProcTaskExecutor(CMSCollector& collector) + : _collector(collector) + { } + + // Executes a task using worker threads. + virtual void execute(ProcessTask& task); + virtual void execute(EnqueueTask& task); +private: + CMSCollector& _collector; +}; + + +class CMSCollector: public CHeapObj { + friend class VMStructs; + friend class ConcurrentMarkSweepThread; + friend class ConcurrentMarkSweepGeneration; + friend class CompactibleFreeListSpace; + friend class CMSParRemarkTask; + friend class CMSConcMarkingTask; + friend class CMSRefProcTaskProxy; + friend class CMSRefProcTaskExecutor; + friend class ScanMarkedObjectsAgainCarefullyClosure; // for sampling eden + friend class SurvivorSpacePrecleanClosure; // --- ditto ------- + friend class PushOrMarkClosure; // to access _restart_addr + friend class Par_PushOrMarkClosure; // to access _restart_addr + friend class MarkFromRootsClosure; // -- ditto -- + // ... and for clearing cards + friend class Par_MarkFromRootsClosure; // to access _restart_addr + // ... and for clearing cards + friend class Par_ConcMarkingClosure; // to access _restart_addr etc. + friend class MarkFromRootsVerifyClosure; // to access _restart_addr + friend class PushAndMarkVerifyClosure; // -- ditto -- + friend class MarkRefsIntoAndScanClosure; // to access _overflow_list + friend class PushAndMarkClosure; // -- ditto -- + friend class Par_PushAndMarkClosure; // -- ditto -- + friend class CMSKeepAliveClosure; // -- ditto -- + friend class CMSDrainMarkingStackClosure; // -- ditto -- + friend class CMSInnerParMarkAndPushClosure; // -- ditto -- + NOT_PRODUCT(friend class ScanMarkedObjectsAgainClosure;) // assertion on _overflow_list + friend class ReleaseForegroundGC; // to access _foregroundGCShouldWait + friend class VM_CMS_Operation; + friend class VM_CMS_Initial_Mark; + friend class VM_CMS_Final_Remark; + + private: + jlong _time_of_last_gc; + void update_time_of_last_gc(jlong now) { + _time_of_last_gc = now; + } + + OopTaskQueueSet* _task_queues; + + // Overflow list of grey objects, threaded through mark-word + // Manipulated with CAS in the parallel/multi-threaded case. + oop _overflow_list; + // The following array-pair keeps track of mark words + // displaced for accomodating overflow list above. + // This code will likely be revisited under RFE#4922830. + GrowableArray* _preserved_oop_stack; + GrowableArray* _preserved_mark_stack; + + int* _hash_seed; + + // In support of multi-threaded concurrent phases + YieldingFlexibleWorkGang* _conc_workers; + + // Performance Counters + CollectorCounters* _gc_counters; + + // Initialization Errors + bool _completed_initialization; + + // In support of ExplicitGCInvokesConcurrent + static bool _full_gc_requested; + unsigned int _collection_count_start; + // Should we unload classes this concurrent cycle? + // Set in response to a concurrent full gc request. + bool _unload_classes; + bool _unloaded_classes_last_cycle; + // Did we (allow) unload classes in the previous concurrent cycle? + bool cms_unloaded_classes_last_cycle() const { + return _unloaded_classes_last_cycle || CMSClassUnloadingEnabled; + } + + // Verification support + CMSBitMap _verification_mark_bm; + void verify_after_remark_work_1(); + void verify_after_remark_work_2(); + + // true if any verification flag is on. + bool _verifying; + bool verifying() const { return _verifying; } + void set_verifying(bool v) { _verifying = v; } + + // Collector policy + ConcurrentMarkSweepPolicy* _collector_policy; + ConcurrentMarkSweepPolicy* collector_policy() { return _collector_policy; } + + // Check whether the gc time limit has been + // exceeded and set the size policy flag + // appropriately. + void check_gc_time_limit(); + // XXX Move these to CMSStats ??? FIX ME !!! + elapsedTimer _sweep_timer; + AdaptivePaddedAverage _sweep_estimate; + + protected: + ConcurrentMarkSweepGeneration* _cmsGen; // old gen (CMS) + ConcurrentMarkSweepGeneration* _permGen; // perm gen + MemRegion _span; // span covering above two + CardTableRS* _ct; // card table + + // CMS marking support structures + CMSBitMap _markBitMap; + CMSBitMap _modUnionTable; + CMSMarkStack _markStack; + CMSMarkStack _revisitStack; // used to keep track of klassKlass objects + // to revisit + CMSBitMap _perm_gen_verify_bit_map; // Mark bit map for perm gen verification support. + + HeapWord* _restart_addr; // in support of marking stack overflow + void lower_restart_addr(HeapWord* low); + + // Counters in support of marking stack / work queue overflow handling: + // a non-zero value indicates certain types of overflow events during + // the current CMS cycle and could lead to stack resizing efforts at + // an opportune future time. + size_t _ser_pmc_preclean_ovflw; + size_t _ser_pmc_remark_ovflw; + size_t _par_pmc_remark_ovflw; + size_t _ser_kac_ovflw; + size_t _par_kac_ovflw; + NOT_PRODUCT(size_t _num_par_pushes;) + + // ("Weak") Reference processing support + ReferenceProcessor* _ref_processor; + CMSIsAliveClosure _is_alive_closure; + // keep this textually after _markBitMap; c'tor dependency + + ConcurrentMarkSweepThread* _cmsThread; // the thread doing the work + ModUnionClosure _modUnionClosure; + ModUnionClosurePar _modUnionClosurePar; + + // CMS abstract state machine + // initial_state: Idling + // next_state(Idling) = {Marking} + // next_state(Marking) = {Precleaning, Sweeping} + // next_state(Precleaning) = {AbortablePreclean, FinalMarking} + // next_state(AbortablePreclean) = {FinalMarking} + // next_state(FinalMarking) = {Sweeping} + // next_state(Sweeping) = {Resizing} + // next_state(Resizing) = {Resetting} + // next_state(Resetting) = {Idling} + // The numeric values below are chosen so that: + // . _collectorState <= Idling == post-sweep && pre-mark + // . _collectorState in (Idling, Sweeping) == {initial,final}marking || + // precleaning || abortablePrecleanb + enum CollectorState { + Resizing = 0, + Resetting = 1, + Idling = 2, + InitialMarking = 3, + Marking = 4, + Precleaning = 5, + AbortablePreclean = 6, + FinalMarking = 7, + Sweeping = 8 + }; + static CollectorState _collectorState; + + // State related to prologue/epilogue invocation for my generations + bool _between_prologue_and_epilogue; + + // Signalling/State related to coordination between fore- and backgroud GC + // Note: When the baton has been passed from background GC to foreground GC, + // _foregroundGCIsActive is true and _foregroundGCShouldWait is false. + static bool _foregroundGCIsActive; // true iff foreground collector is active or + // wants to go active + static bool _foregroundGCShouldWait; // true iff background GC is active and has not + // yet passed the baton to the foreground GC + + // Support for CMSScheduleRemark (abortable preclean) + bool _abort_preclean; + bool _start_sampling; + + int _numYields; + size_t _numDirtyCards; + uint _sweepCount; + // number of full gc's since the last concurrent gc. + uint _full_gcs_since_conc_gc; + + // if occupancy exceeds this, start a new gc cycle + double _initiatingOccupancy; + // occupancy used for bootstrapping stats + double _bootstrap_occupancy; + + // timer + elapsedTimer _timer; + + // Timing, allocation and promotion statistics, used for scheduling. + CMSStats _stats; + + // Allocation limits installed in the young gen, used only in + // CMSIncrementalMode. When an allocation in the young gen would cross one of + // these limits, the cms generation is notified and the cms thread is started + // or stopped, respectively. + HeapWord* _icms_start_limit; + HeapWord* _icms_stop_limit; + + enum CMS_op_type { + CMS_op_checkpointRootsInitial, + CMS_op_checkpointRootsFinal + }; + + void do_CMS_operation(CMS_op_type op); + bool stop_world_and_do(CMS_op_type op); + + OopTaskQueueSet* task_queues() { return _task_queues; } + int* hash_seed(int i) { return &_hash_seed[i]; } + YieldingFlexibleWorkGang* conc_workers() { return _conc_workers; } + + // Support for parallelizing Eden rescan in CMS remark phase + void sample_eden(); // ... sample Eden space top + + private: + // Support for parallelizing young gen rescan in CMS remark phase + Generation* _young_gen; // the younger gen + HeapWord** _top_addr; // ... Top of Eden + HeapWord** _end_addr; // ... End of Eden + HeapWord** _eden_chunk_array; // ... Eden partitioning array + size_t _eden_chunk_index; // ... top (exclusive) of array + size_t _eden_chunk_capacity; // ... max entries in array + + // Support for parallelizing survivor space rescan + HeapWord** _survivor_chunk_array; + size_t _survivor_chunk_index; + size_t _survivor_chunk_capacity; + size_t* _cursor; + ChunkArray* _survivor_plab_array; + + // Support for marking stack overflow handling + bool take_from_overflow_list(size_t num, CMSMarkStack* to_stack); + bool par_take_from_overflow_list(size_t num, OopTaskQueue* to_work_q); + void push_on_overflow_list(oop p); + void par_push_on_overflow_list(oop p); + // the following is, obviously, not, in general, "MT-stable" + bool overflow_list_is_empty() const; + + void preserve_mark_if_necessary(oop p); + void par_preserve_mark_if_necessary(oop p); + void preserve_mark_work(oop p, markOop m); + void restore_preserved_marks_if_any(); + NOT_PRODUCT(bool no_preserved_marks() const;) + // in support of testing overflow code + NOT_PRODUCT(int _overflow_counter;) + NOT_PRODUCT(bool simulate_overflow();) // sequential + NOT_PRODUCT(bool par_simulate_overflow();) // MT version + + int _roots_scanning_options; + int roots_scanning_options() const { return _roots_scanning_options; } + void add_root_scanning_option(int o) { _roots_scanning_options |= o; } + void remove_root_scanning_option(int o) { _roots_scanning_options &= ~o; } + + // CMS work methods + void checkpointRootsInitialWork(bool asynch); // initial checkpoint work + + // a return value of false indicates failure due to stack overflow + bool markFromRootsWork(bool asynch); // concurrent marking work + + public: // FIX ME!!! only for testing + bool do_marking_st(bool asynch); // single-threaded marking + bool do_marking_mt(bool asynch); // multi-threaded marking + + private: + + // concurrent precleaning work + size_t preclean_mod_union_table(ConcurrentMarkSweepGeneration* gen, + ScanMarkedObjectsAgainCarefullyClosure* cl); + size_t preclean_card_table(ConcurrentMarkSweepGeneration* gen, + ScanMarkedObjectsAgainCarefullyClosure* cl); + // Does precleaning work, returning a quantity indicative of + // the amount of "useful work" done. + size_t preclean_work(bool clean_refs, bool clean_survivors); + void abortable_preclean(); // Preclean while looking for possible abort + void initialize_sequential_subtasks_for_young_gen_rescan(int i); + // Helper function for above; merge-sorts the per-thread plab samples + void merge_survivor_plab_arrays(ContiguousSpace* surv); + // Resets (i.e. clears) the per-thread plab sample vectors + void reset_survivor_plab_arrays(); + + // final (second) checkpoint work + void checkpointRootsFinalWork(bool asynch, bool clear_all_soft_refs, + bool init_mark_was_synchronous); + // work routine for parallel version of remark + void do_remark_parallel(); + // work routine for non-parallel version of remark + void do_remark_non_parallel(); + // reference processing work routine (during second checkpoint) + void refProcessingWork(bool asynch, bool clear_all_soft_refs); + + // concurrent sweeping work + void sweepWork(ConcurrentMarkSweepGeneration* gen, bool asynch); + + // (concurrent) resetting of support data structures + void reset(bool asynch); + + // Clear _expansion_cause fields of constituent generations + void clear_expansion_cause(); + + // An auxilliary method used to record the ends of + // used regions of each generation to limit the extent of sweep + void save_sweep_limits(); + + // Resize the generations included in the collector. + void compute_new_size(); + + // A work method used by foreground collection to determine + // what type of collection (compacting or not, continuing or fresh) + // it should do. + void decide_foreground_collection_type(bool clear_all_soft_refs, + bool* should_compact, bool* should_start_over); + + // A work method used by the foreground collector to do + // a mark-sweep-compact. + void do_compaction_work(bool clear_all_soft_refs); + + // A work method used by the foreground collector to do + // a mark-sweep, after taking over from a possibly on-going + // concurrent mark-sweep collection. + void do_mark_sweep_work(bool clear_all_soft_refs, + CollectorState first_state, bool should_start_over); + + // If the backgrould GC is active, acquire control from the background + // GC and do the collection. + void acquire_control_and_collect(bool full, bool clear_all_soft_refs); + + // For synchronizing passing of control from background to foreground + // GC. waitForForegroundGC() is called by the background + // collector. It if had to wait for a foreground collection, + // it returns true and the background collection should assume + // that the collection was finished by the foreground + // collector. + bool waitForForegroundGC(); + + // Incremental mode triggering: recompute the icms duty cycle and set the + // allocation limits in the young gen. + void icms_update_allocation_limits(); + + size_t block_size_using_printezis_bits(HeapWord* addr) const; + size_t block_size_if_printezis_bits(HeapWord* addr) const; + HeapWord* next_card_start_after_block(HeapWord* addr) const; + + void setup_cms_unloading_and_verification_state(); + public: + CMSCollector(ConcurrentMarkSweepGeneration* cmsGen, + ConcurrentMarkSweepGeneration* permGen, + CardTableRS* ct, + ConcurrentMarkSweepPolicy* cp); + ConcurrentMarkSweepThread* cmsThread() { return _cmsThread; } + + ReferenceProcessor* ref_processor() { return _ref_processor; } + void ref_processor_init(); + + Mutex* bitMapLock() const { return _markBitMap.lock(); } + static CollectorState abstract_state() { return _collectorState; } + double initiatingOccupancy() const { return _initiatingOccupancy; } + + bool should_abort_preclean() const; // Whether preclean should be aborted. + size_t get_eden_used() const; + size_t get_eden_capacity() const; + + ConcurrentMarkSweepGeneration* cmsGen() { return _cmsGen; } + + // locking checks + NOT_PRODUCT(static bool have_cms_token();) + + // XXXPERM bool should_collect(bool full, size_t size, bool tlab); + bool shouldConcurrentCollect(); + + void collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool tlab); + void collect_in_background(bool clear_all_soft_refs); + void collect_in_foreground(bool clear_all_soft_refs); + + // In support of ExplicitGCInvokesConcurrent + static void request_full_gc(unsigned int full_gc_count); + // Should we unload classes in a particular concurrent cycle? + bool cms_should_unload_classes() const { + assert(!_unload_classes || ExplicitGCInvokesConcurrentAndUnloadsClasses, + "Inconsistency; see CR 6541037"); + return _unload_classes || CMSClassUnloadingEnabled; + } + + void direct_allocated(HeapWord* start, size_t size); + + // Object is dead if not marked and current phase is sweeping. + bool is_dead_obj(oop obj) const; + + // After a promotion (of "start"), do any necessary marking. + // If "par", then it's being done by a parallel GC thread. + // The last two args indicate if we need precise marking + // and if so the size of the object so it can be dirtied + // in its entirety. + void promoted(bool par, HeapWord* start, + bool is_obj_array, size_t obj_size); + + HeapWord* allocation_limit_reached(Space* space, HeapWord* top, + size_t word_size); + + void getFreelistLocks() const; + void releaseFreelistLocks() const; + bool haveFreelistLocks() const; + + // GC prologue and epilogue + void gc_prologue(bool full); + void gc_epilogue(bool full); + + jlong time_of_last_gc(jlong now) { + if (_collectorState <= Idling) { + // gc not in progress + return _time_of_last_gc; + } else { + // collection in progress + return now; + } + } + + // Support for parallel remark of survivor space + void* get_data_recorder(int thr_num); + + CMSBitMap* markBitMap() { return &_markBitMap; } + void directAllocated(HeapWord* start, size_t size); + + // main CMS steps and related support + void checkpointRootsInitial(bool asynch); + bool markFromRoots(bool asynch); // a return value of false indicates failure + // due to stack overflow + void preclean(); + void checkpointRootsFinal(bool asynch, bool clear_all_soft_refs, + bool init_mark_was_synchronous); + void sweep(bool asynch); + + // Check that the currently executing thread is the expected + // one (foreground collector or background collector). + void check_correct_thread_executing() PRODUCT_RETURN; + // XXXPERM void print_statistics() PRODUCT_RETURN; + + bool is_cms_reachable(HeapWord* addr); + + // Performance Counter Support + CollectorCounters* counters() { return _gc_counters; } + + // timer stuff + void startTimer() { assert(!_timer.is_active(), "Error"); _timer.start(); } + void stopTimer() { assert( _timer.is_active(), "Error"); _timer.stop(); } + void resetTimer() { assert(!_timer.is_active(), "Error"); _timer.reset(); } + double timerValue() { assert(!_timer.is_active(), "Error"); return _timer.seconds(); } + + int yields() { return _numYields; } + void resetYields() { _numYields = 0; } + void incrementYields() { _numYields++; } + void resetNumDirtyCards() { _numDirtyCards = 0; } + void incrementNumDirtyCards(size_t num) { _numDirtyCards += num; } + size_t numDirtyCards() { return _numDirtyCards; } + + static bool foregroundGCShouldWait() { return _foregroundGCShouldWait; } + static void set_foregroundGCShouldWait(bool v) { _foregroundGCShouldWait = v; } + static bool foregroundGCIsActive() { return _foregroundGCIsActive; } + static void set_foregroundGCIsActive(bool v) { _foregroundGCIsActive = v; } + uint sweepCount() const { return _sweepCount; } + void incrementSweepCount() { _sweepCount++; } + + // Timers/stats for gc scheduling and incremental mode pacing. + CMSStats& stats() { return _stats; } + + // Convenience methods that check whether CMSIncrementalMode is enabled and + // forward to the corresponding methods in ConcurrentMarkSweepThread. + static void start_icms(); + static void stop_icms(); // Called at the end of the cms cycle. + static void disable_icms(); // Called before a foreground collection. + static void enable_icms(); // Called after a foreground collection. + void icms_wait(); // Called at yield points. + + // Adaptive size policy + CMSAdaptiveSizePolicy* size_policy(); + CMSGCAdaptivePolicyCounters* gc_adaptive_policy_counters(); + + // debugging + void verify(bool); + bool verify_after_remark(); + void verify_ok_to_terminate() const PRODUCT_RETURN; + void verify_work_stacks_empty() const PRODUCT_RETURN; + void verify_overflow_empty() const PRODUCT_RETURN; + + // convenience methods in support of debugging + static const size_t skip_header_HeapWords() PRODUCT_RETURN0; + HeapWord* block_start(const void* p) const PRODUCT_RETURN0; + + // accessors + CMSMarkStack* verification_mark_stack() { return &_markStack; } + CMSBitMap* verification_mark_bm() { return &_verification_mark_bm; } + + // Get the bit map with a perm gen "deadness" information. + CMSBitMap* perm_gen_verify_bit_map() { return &_perm_gen_verify_bit_map; } + + // Initialization errors + bool completed_initialization() { return _completed_initialization; } +}; + +class CMSExpansionCause : public AllStatic { + public: + enum Cause { + _no_expansion, + _satisfy_free_ratio, + _satisfy_promotion, + _satisfy_allocation, + _allocate_par_lab, + _allocate_par_spooling_space, + _adaptive_size_policy + }; + // Return a string describing the cause of the expansion. + static const char* to_string(CMSExpansionCause::Cause cause); +}; + +class ConcurrentMarkSweepGeneration: public CardGeneration { + friend class VMStructs; + friend class ConcurrentMarkSweepThread; + friend class ConcurrentMarkSweep; + friend class CMSCollector; + protected: + static CMSCollector* _collector; // the collector that collects us + CompactibleFreeListSpace* _cmsSpace; // underlying space (only one for now) + + // Performance Counters + GenerationCounters* _gen_counters; + GSpaceCounters* _space_counters; + + // Words directly allocated, used by CMSStats. + size_t _direct_allocated_words; + + // Non-product stat counters + NOT_PRODUCT( + int _numObjectsPromoted; + int _numWordsPromoted; + int _numObjectsAllocated; + int _numWordsAllocated; + ) + + // Used for sizing decisions + bool _incremental_collection_failed; + bool incremental_collection_failed() { + return _incremental_collection_failed; + } + void set_incremental_collection_failed() { + _incremental_collection_failed = true; + } + void clear_incremental_collection_failed() { + _incremental_collection_failed = false; + } + + private: + // For parallel young-gen GC support. + CMSParGCThreadState** _par_gc_thread_states; + + // Reason generation was expanded + CMSExpansionCause::Cause _expansion_cause; + + // accessors + void set_expansion_cause(CMSExpansionCause::Cause v) { _expansion_cause = v;} + CMSExpansionCause::Cause expansion_cause() { return _expansion_cause; } + + // In support of MinChunkSize being larger than min object size + const double _dilatation_factor; + + enum CollectionTypes { + Concurrent_collection_type = 0, + MS_foreground_collection_type = 1, + MSC_foreground_collection_type = 2, + Unknown_collection_type = 3 + }; + + CollectionTypes _debug_collection_type; + + protected: + // Grow generation by specified size (returns false if unable to grow) + bool grow_by(size_t bytes); + // Grow generation to reserved size. + bool grow_to_reserved(); + // Shrink generation by specified size (returns false if unable to shrink) + virtual void shrink_by(size_t bytes); + + // Update statistics for GC + virtual void update_gc_stats(int level, bool full); + + // Maximum available space in the generation (including uncommitted) + // space. + size_t max_available() const; + + public: + ConcurrentMarkSweepGeneration(ReservedSpace rs, size_t initial_byte_size, + int level, CardTableRS* ct, + bool use_adaptive_freelists, + FreeBlockDictionary::DictionaryChoice); + + // Accessors + CMSCollector* collector() const { return _collector; } + static void set_collector(CMSCollector* collector) { + assert(_collector == NULL, "already set"); + _collector = collector; + } + CompactibleFreeListSpace* cmsSpace() const { return _cmsSpace; } + + Mutex* freelistLock() const; + + virtual Generation::Name kind() { return Generation::ConcurrentMarkSweep; } + + // Adaptive size policy + CMSAdaptiveSizePolicy* size_policy(); + + bool refs_discovery_is_atomic() const { return false; } + bool refs_discovery_is_mt() const { + // Note: CMS does MT-discovery during the parallel-remark + // phases. Use ReferenceProcessorMTMutator to make refs + // discovery MT-safe during such phases or other parallel + // discovery phases in the future. This may all go away + // if/when we decide that refs discovery is sufficiently + // rare that the cost of the CAS's involved is in the + // noise. That's a measurement that should be done, and + // the code simplified if that turns out to be the case. + return false; + } + + // Override + virtual void ref_processor_init(); + + void clear_expansion_cause() { _expansion_cause = CMSExpansionCause::_no_expansion; } + + // Space enquiries + size_t capacity() const; + size_t used() const; + size_t free() const; + double occupancy() { return ((double)used())/((double)capacity()); } + size_t contiguous_available() const; + size_t unsafe_max_alloc_nogc() const; + + // over-rides + MemRegion used_region() const; + MemRegion used_region_at_save_marks() const; + + // Does a "full" (forced) collection invoked on this generation collect + // all younger generations as well? Note that the second conjunct is a + // hack to allow the collection of the younger gen first if the flag is + // set. This is better than using th policy's should_collect_gen0_first() + // since that causes us to do an extra unnecessary pair of restart-&-stop-world. + virtual bool full_collects_younger_generations() const { + return UseCMSCompactAtFullCollection && !CollectGen0First; + } + + void space_iterate(SpaceClosure* blk, bool usedOnly = false); + + // Support for compaction + CompactibleSpace* first_compaction_space() const; + // Adjust quantites in the generation affected by + // the compaction. + void reset_after_compaction(); + + // Allocation support + HeapWord* allocate(size_t size, bool tlab); + HeapWord* have_lock_and_allocate(size_t size, bool tlab); + oop promote(oop obj, size_t obj_size, oop* ref); + HeapWord* par_allocate(size_t size, bool tlab) { + return allocate(size, tlab); + } + + // Incremental mode triggering. + HeapWord* allocation_limit_reached(Space* space, HeapWord* top, + size_t word_size); + + // Used by CMSStats to track direct allocation. The value is sampled and + // reset after each young gen collection. + size_t direct_allocated_words() const { return _direct_allocated_words; } + void reset_direct_allocated_words() { _direct_allocated_words = 0; } + + // Overrides for parallel promotion. + virtual oop par_promote(int thread_num, + oop obj, markOop m, size_t word_sz); + // This one should not be called for CMS. + virtual void par_promote_alloc_undo(int thread_num, + HeapWord* obj, size_t word_sz); + virtual void par_promote_alloc_done(int thread_num); + virtual void par_oop_since_save_marks_iterate_done(int thread_num); + + virtual bool promotion_attempt_is_safe(size_t promotion_in_bytes, + bool younger_handles_promotion_failure) const; + + bool should_collect(bool full, size_t size, bool tlab); + // XXXPERM + bool shouldConcurrentCollect(double initiatingOccupancy); // XXXPERM + void collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool tlab); + + HeapWord* expand_and_allocate(size_t word_size, + bool tlab, + bool parallel = false); + + // GC prologue and epilogue + void gc_prologue(bool full); + void gc_prologue_work(bool full, bool registerClosure, + ModUnionClosure* modUnionClosure); + void gc_epilogue(bool full); + void gc_epilogue_work(bool full); + + // Time since last GC of this generation + jlong time_of_last_gc(jlong now) { + return collector()->time_of_last_gc(now); + } + void update_time_of_last_gc(jlong now) { + collector()-> update_time_of_last_gc(now); + } + + // Allocation failure + void expand(size_t bytes, size_t expand_bytes, + CMSExpansionCause::Cause cause); + void shrink(size_t bytes); + HeapWord* expand_and_par_lab_allocate(CMSParGCThreadState* ps, size_t word_sz); + bool expand_and_ensure_spooling_space(PromotionInfo* promo); + + // Iteration support and related enquiries + void save_marks(); + bool no_allocs_since_save_marks(); + void object_iterate_since_last_GC(ObjectClosure* cl); + void younger_refs_iterate(OopsInGenClosure* cl); + + // Iteration support specific to CMS generations + void save_sweep_limit(); + + // More iteration support + virtual void oop_iterate(MemRegion mr, OopClosure* cl); + virtual void oop_iterate(OopClosure* cl); + virtual void object_iterate(ObjectClosure* cl); + + // Need to declare the full complement of closures, whether we'll + // override them or not, or get message from the compiler: + // oop_since_save_marks_iterate_nv hides virtual function... + #define CMS_SINCE_SAVE_MARKS_DECL(OopClosureType, nv_suffix) \ + void oop_since_save_marks_iterate##nv_suffix(OopClosureType* cl); + ALL_SINCE_SAVE_MARKS_CLOSURES(CMS_SINCE_SAVE_MARKS_DECL) + + // Smart allocation XXX -- move to CFLSpace? + void setNearLargestChunk(); + bool isNearLargestChunk(HeapWord* addr); + + // Get the chunk at the end of the space. Delagates to + // the space. + FreeChunk* find_chunk_at_end(); + + // Overriding of unused functionality (sharing not yet supported with CMS) + void pre_adjust_pointers(); + void post_compact(); + + // Debugging + void prepare_for_verify(); + void verify(bool allow_dirty); + void print_statistics() PRODUCT_RETURN; + + // Performance Counters support + virtual void update_counters(); + virtual void update_counters(size_t used); + void initialize_performance_counters(); + CollectorCounters* counters() { return collector()->counters(); } + + // Support for parallel remark of survivor space + void* get_data_recorder(int thr_num) { + //Delegate to collector + return collector()->get_data_recorder(thr_num); + } + + // Printing + const char* name() const; + virtual const char* short_name() const { return "CMS"; } + void print() const; + void printOccupancy(const char* s); + bool must_be_youngest() const { return false; } + bool must_be_oldest() const { return true; } + + void compute_new_size(); + + CollectionTypes debug_collection_type() { return _debug_collection_type; } + void rotate_debug_collection_type(); +}; + +class ASConcurrentMarkSweepGeneration : public ConcurrentMarkSweepGeneration { + + // Return the size policy from the heap's collector + // policy casted to CMSAdaptiveSizePolicy*. + CMSAdaptiveSizePolicy* cms_size_policy() const; + + // Resize the generation based on the adaptive size + // policy. + void resize(size_t cur_promo, size_t desired_promo); + + // Return the GC counters from the collector policy + CMSGCAdaptivePolicyCounters* gc_adaptive_policy_counters(); + + virtual void shrink_by(size_t bytes); + + public: + virtual void compute_new_size(); + ASConcurrentMarkSweepGeneration(ReservedSpace rs, size_t initial_byte_size, + int level, CardTableRS* ct, + bool use_adaptive_freelists, + FreeBlockDictionary::DictionaryChoice + dictionaryChoice) : + ConcurrentMarkSweepGeneration(rs, initial_byte_size, level, ct, + use_adaptive_freelists, dictionaryChoice) {} + + virtual const char* short_name() const { return "ASCMS"; } + virtual Generation::Name kind() { return Generation::ASConcurrentMarkSweep; } + + virtual void update_counters(); + virtual void update_counters(size_t used); +}; + +// +// Closures of various sorts used by CMS to accomplish its work +// + +// This closure is used to check that a certain set of oops is empty. +class FalseClosure: public OopClosure { + public: + void do_oop(oop* p) { + guarantee(false, "Should be an empty set"); + } +}; + +// This closure is used to do concurrent marking from the roots +// following the first checkpoint. +class MarkFromRootsClosure: public BitMapClosure { + CMSCollector* _collector; + MemRegion _span; + CMSBitMap* _bitMap; + CMSBitMap* _mut; + CMSMarkStack* _markStack; + CMSMarkStack* _revisitStack; + bool _yield; + int _skipBits; + HeapWord* _finger; + HeapWord* _threshold; + DEBUG_ONLY(bool _verifying;) + + public: + MarkFromRootsClosure(CMSCollector* collector, MemRegion span, + CMSBitMap* bitMap, + CMSMarkStack* markStack, + CMSMarkStack* revisitStack, + bool should_yield, bool verifying = false); + void do_bit(size_t offset); + void reset(HeapWord* addr); + inline void do_yield_check(); + + private: + void scanOopsInOop(HeapWord* ptr); + void do_yield_work(); +}; + +// This closure is used to do concurrent multi-threaded +// marking from the roots following the first checkpoint. +// XXX This should really be a subclass of The serial version +// above, but i have not had the time to refactor things cleanly. +// That willbe done for Dolphin. +class Par_MarkFromRootsClosure: public BitMapClosure { + CMSCollector* _collector; + MemRegion _whole_span; + MemRegion _span; + CMSBitMap* _bit_map; + CMSBitMap* _mut; + OopTaskQueue* _work_queue; + CMSMarkStack* _overflow_stack; + CMSMarkStack* _revisit_stack; + bool _yield; + int _skip_bits; + HeapWord* _finger; + HeapWord* _threshold; + CMSConcMarkingTask* _task; + public: + Par_MarkFromRootsClosure(CMSConcMarkingTask* task, CMSCollector* collector, + MemRegion span, + CMSBitMap* bit_map, + OopTaskQueue* work_queue, + CMSMarkStack* overflow_stack, + CMSMarkStack* revisit_stack, + bool should_yield); + void do_bit(size_t offset); + inline void do_yield_check(); + + private: + void scan_oops_in_oop(HeapWord* ptr); + void do_yield_work(); + bool get_work_from_overflow_stack(); +}; + +// The following closures are used to do certain kinds of verification of +// CMS marking. +class PushAndMarkVerifyClosure: public OopClosure { + CMSCollector* _collector; + MemRegion _span; + CMSBitMap* _verification_bm; + CMSBitMap* _cms_bm; + CMSMarkStack* _mark_stack; + public: + PushAndMarkVerifyClosure(CMSCollector* cms_collector, + MemRegion span, + CMSBitMap* verification_bm, + CMSBitMap* cms_bm, + CMSMarkStack* mark_stack); + void do_oop(oop* p); + // Deal with a stack overflow condition + void handle_stack_overflow(HeapWord* lost); +}; + +class MarkFromRootsVerifyClosure: public BitMapClosure { + CMSCollector* _collector; + MemRegion _span; + CMSBitMap* _verification_bm; + CMSBitMap* _cms_bm; + CMSMarkStack* _mark_stack; + HeapWord* _finger; + PushAndMarkVerifyClosure _pam_verify_closure; + public: + MarkFromRootsVerifyClosure(CMSCollector* collector, MemRegion span, + CMSBitMap* verification_bm, + CMSBitMap* cms_bm, + CMSMarkStack* mark_stack); + void do_bit(size_t offset); + void reset(HeapWord* addr); +}; + + +// This closure is used to check that a certain set of bits is +// "empty" (i.e. the bit vector doesn't have any 1-bits). +class FalseBitMapClosure: public BitMapClosure { + public: + void do_bit(size_t offset) { + guarantee(false, "Should not have a 1 bit"); + } +}; + +// This closure is used during the second checkpointing phase +// to rescan the marked objects on the dirty cards in the mod +// union table and the card table proper. It's invoked via +// MarkFromDirtyCardsClosure below. It uses either +// [Par_]MarkRefsIntoAndScanClosure (Par_ in the parallel case) +// declared in genOopClosures.hpp to accomplish some of its work. +// In the parallel case the bitMap is shared, so access to +// it needs to be suitably synchronized for updates by embedded +// closures that update it; however, this closure itself only +// reads the bit_map and because it is idempotent, is immune to +// reading stale values. +class ScanMarkedObjectsAgainClosure: public UpwardsObjectClosure { + #ifdef ASSERT + CMSCollector* _collector; + MemRegion _span; + union { + CMSMarkStack* _mark_stack; + OopTaskQueue* _work_queue; + }; + #endif // ASSERT + bool _parallel; + CMSBitMap* _bit_map; + union { + MarkRefsIntoAndScanClosure* _scan_closure; + Par_MarkRefsIntoAndScanClosure* _par_scan_closure; + }; + + public: + ScanMarkedObjectsAgainClosure(CMSCollector* collector, + MemRegion span, + ReferenceProcessor* rp, + CMSBitMap* bit_map, + CMSMarkStack* mark_stack, + CMSMarkStack* revisit_stack, + MarkRefsIntoAndScanClosure* cl): + #ifdef ASSERT + _collector(collector), + _span(span), + _mark_stack(mark_stack), + #endif // ASSERT + _parallel(false), + _bit_map(bit_map), + _scan_closure(cl) { } + + ScanMarkedObjectsAgainClosure(CMSCollector* collector, + MemRegion span, + ReferenceProcessor* rp, + CMSBitMap* bit_map, + OopTaskQueue* work_queue, + CMSMarkStack* revisit_stack, + Par_MarkRefsIntoAndScanClosure* cl): + #ifdef ASSERT + _collector(collector), + _span(span), + _work_queue(work_queue), + #endif // ASSERT + _parallel(true), + _bit_map(bit_map), + _par_scan_closure(cl) { } + + void do_object(oop obj) { + guarantee(false, "Call do_object_b(oop, MemRegion) instead"); + } + bool do_object_b(oop obj) { + guarantee(false, "Call do_object_b(oop, MemRegion) form instead"); + return false; + } + bool do_object_bm(oop p, MemRegion mr); +}; + +// This closure is used during the second checkpointing phase +// to rescan the marked objects on the dirty cards in the mod +// union table and the card table proper. It invokes +// ScanMarkedObjectsAgainClosure above to accomplish much of its work. +// In the parallel case, the bit map is shared and requires +// synchronized access. +class MarkFromDirtyCardsClosure: public MemRegionClosure { + CompactibleFreeListSpace* _space; + ScanMarkedObjectsAgainClosure _scan_cl; + size_t _num_dirty_cards; + + public: + MarkFromDirtyCardsClosure(CMSCollector* collector, + MemRegion span, + CompactibleFreeListSpace* space, + CMSBitMap* bit_map, + CMSMarkStack* mark_stack, + CMSMarkStack* revisit_stack, + MarkRefsIntoAndScanClosure* cl): + _space(space), + _num_dirty_cards(0), + _scan_cl(collector, span, collector->ref_processor(), bit_map, + mark_stack, revisit_stack, cl) { } + + MarkFromDirtyCardsClosure(CMSCollector* collector, + MemRegion span, + CompactibleFreeListSpace* space, + CMSBitMap* bit_map, + OopTaskQueue* work_queue, + CMSMarkStack* revisit_stack, + Par_MarkRefsIntoAndScanClosure* cl): + _space(space), + _num_dirty_cards(0), + _scan_cl(collector, span, collector->ref_processor(), bit_map, + work_queue, revisit_stack, cl) { } + + void do_MemRegion(MemRegion mr); + void set_space(CompactibleFreeListSpace* space) { _space = space; } + size_t num_dirty_cards() { return _num_dirty_cards; } +}; + +// This closure is used in the non-product build to check +// that there are no MemRegions with a certain property. +class FalseMemRegionClosure: public MemRegionClosure { + void do_MemRegion(MemRegion mr) { + guarantee(!mr.is_empty(), "Shouldn't be empty"); + guarantee(false, "Should never be here"); + } +}; + +// This closure is used during the precleaning phase +// to "carefully" rescan marked objects on dirty cards. +// It uses MarkRefsIntoAndScanClosure declared in genOopClosures.hpp +// to accomplish some of its work. +class ScanMarkedObjectsAgainCarefullyClosure: public ObjectClosureCareful { + CMSCollector* _collector; + MemRegion _span; + bool _yield; + Mutex* _freelistLock; + CMSBitMap* _bitMap; + CMSMarkStack* _markStack; + MarkRefsIntoAndScanClosure* _scanningClosure; + + public: + ScanMarkedObjectsAgainCarefullyClosure(CMSCollector* collector, + MemRegion span, + CMSBitMap* bitMap, + CMSMarkStack* markStack, + CMSMarkStack* revisitStack, + MarkRefsIntoAndScanClosure* cl, + bool should_yield): + _collector(collector), + _span(span), + _yield(should_yield), + _bitMap(bitMap), + _markStack(markStack), + _scanningClosure(cl) { + } + + void do_object(oop p) { + guarantee(false, "call do_object_careful instead"); + } + + size_t do_object_careful(oop p) { + guarantee(false, "Unexpected caller"); + return 0; + } + + size_t do_object_careful_m(oop p, MemRegion mr); + + void setFreelistLock(Mutex* m) { + _freelistLock = m; + _scanningClosure->set_freelistLock(m); + } + + private: + inline bool do_yield_check(); + + void do_yield_work(); +}; + +class SurvivorSpacePrecleanClosure: public ObjectClosureCareful { + CMSCollector* _collector; + MemRegion _span; + bool _yield; + CMSBitMap* _bit_map; + CMSMarkStack* _mark_stack; + PushAndMarkClosure* _scanning_closure; + unsigned int _before_count; + + public: + SurvivorSpacePrecleanClosure(CMSCollector* collector, + MemRegion span, + CMSBitMap* bit_map, + CMSMarkStack* mark_stack, + PushAndMarkClosure* cl, + unsigned int before_count, + bool should_yield): + _collector(collector), + _span(span), + _yield(should_yield), + _bit_map(bit_map), + _mark_stack(mark_stack), + _scanning_closure(cl), + _before_count(before_count) + { } + + void do_object(oop p) { + guarantee(false, "call do_object_careful instead"); + } + + size_t do_object_careful(oop p); + + size_t do_object_careful_m(oop p, MemRegion mr) { + guarantee(false, "Unexpected caller"); + return 0; + } + + private: + inline void do_yield_check(); + void do_yield_work(); +}; + +// This closure is used to accomplish the sweeping work +// after the second checkpoint but before the concurrent reset +// phase. +// +// Terminology +// left hand chunk (LHC) - block of one or more chunks currently being +// coalesced. The LHC is available for coalescing with a new chunk. +// right hand chunk (RHC) - block that is currently being swept that is +// free or garbage that can be coalesced with the LHC. +// _inFreeRange is true if there is currently a LHC +// _lastFreeRangeCoalesced is true if the LHC consists of more than one chunk. +// _freeRangeInFreeLists is true if the LHC is in the free lists. +// _freeFinger is the address of the current LHC +class SweepClosure: public BlkClosureCareful { + CMSCollector* _collector; // collector doing the work + ConcurrentMarkSweepGeneration* _g; // Generation being swept + CompactibleFreeListSpace* _sp; // Space being swept + HeapWord* _limit; + Mutex* _freelistLock; // Free list lock (in space) + CMSBitMap* _bitMap; // Marking bit map (in + // generation) + bool _inFreeRange; // Indicates if we are in the + // midst of a free run + bool _freeRangeInFreeLists; + // Often, we have just found + // a free chunk and started + // a new free range; we do not + // eagerly remove this chunk from + // the free lists unless there is + // a possibility of coalescing. + // When true, this flag indicates + // that the _freeFinger below + // points to a potentially free chunk + // that may still be in the free lists + bool _lastFreeRangeCoalesced; + // free range contains chunks + // coalesced + bool _yield; + // Whether sweeping should be + // done with yields. For instance + // when done by the foreground + // collector we shouldn't yield. + HeapWord* _freeFinger; // When _inFreeRange is set, the + // pointer to the "left hand + // chunk" + size_t _freeRangeSize; + // When _inFreeRange is set, this + // indicates the accumulated size + // of the "left hand chunk" + NOT_PRODUCT( + size_t _numObjectsFreed; + size_t _numWordsFreed; + size_t _numObjectsLive; + size_t _numWordsLive; + size_t _numObjectsAlreadyFree; + size_t _numWordsAlreadyFree; + FreeChunk* _last_fc; + ) + private: + // Code that is common to a free chunk or garbage when + // encountered during sweeping. + void doPostIsFreeOrGarbageChunk(FreeChunk *fc, + size_t chunkSize); + // Process a free chunk during sweeping. + void doAlreadyFreeChunk(FreeChunk *fc); + // Process a garbage chunk during sweeping. + size_t doGarbageChunk(FreeChunk *fc); + // Process a live chunk during sweeping. + size_t doLiveChunk(FreeChunk* fc); + + // Accessors. + HeapWord* freeFinger() const { return _freeFinger; } + void set_freeFinger(HeapWord* v) { _freeFinger = v; } + size_t freeRangeSize() const { return _freeRangeSize; } + void set_freeRangeSize(size_t v) { _freeRangeSize = v; } + bool inFreeRange() const { return _inFreeRange; } + void set_inFreeRange(bool v) { _inFreeRange = v; } + bool lastFreeRangeCoalesced() const { return _lastFreeRangeCoalesced; } + void set_lastFreeRangeCoalesced(bool v) { _lastFreeRangeCoalesced = v; } + bool freeRangeInFreeLists() const { return _freeRangeInFreeLists; } + void set_freeRangeInFreeLists(bool v) { _freeRangeInFreeLists = v; } + + // Initialize a free range. + void initialize_free_range(HeapWord* freeFinger, bool freeRangeInFreeLists); + // Return this chunk to the free lists. + void flushCurFreeChunk(HeapWord* chunk, size_t size); + + // Check if we should yield and do so when necessary. + inline void do_yield_check(HeapWord* addr); + + // Yield + void do_yield_work(HeapWord* addr); + + // Debugging/Printing + void record_free_block_coalesced(FreeChunk* fc) const PRODUCT_RETURN; + + public: + SweepClosure(CMSCollector* collector, ConcurrentMarkSweepGeneration* g, + CMSBitMap* bitMap, bool should_yield); + ~SweepClosure(); + + size_t do_blk_careful(HeapWord* addr); +}; + +// Closures related to weak references processing + +// During CMS' weak reference processing, this is a +// work-routine/closure used to complete transitive +// marking of objects as live after a certain point +// in which an initial set has been completely accumulated. +class CMSDrainMarkingStackClosure: public VoidClosure { + CMSCollector* _collector; + MemRegion _span; + CMSMarkStack* _mark_stack; + CMSBitMap* _bit_map; + CMSKeepAliveClosure* _keep_alive; + public: + CMSDrainMarkingStackClosure(CMSCollector* collector, MemRegion span, + CMSBitMap* bit_map, CMSMarkStack* mark_stack, + CMSKeepAliveClosure* keep_alive): + _collector(collector), + _span(span), + _bit_map(bit_map), + _mark_stack(mark_stack), + _keep_alive(keep_alive) { } + + void do_void(); +}; + +// A parallel version of CMSDrainMarkingStackClosure above. +class CMSParDrainMarkingStackClosure: public VoidClosure { + CMSCollector* _collector; + MemRegion _span; + OopTaskQueue* _work_queue; + CMSBitMap* _bit_map; + CMSInnerParMarkAndPushClosure _mark_and_push; + + public: + CMSParDrainMarkingStackClosure(CMSCollector* collector, + MemRegion span, CMSBitMap* bit_map, + OopTaskQueue* work_queue): + _collector(collector), + _span(span), + _bit_map(bit_map), + _work_queue(work_queue), + _mark_and_push(collector, span, bit_map, work_queue) { } + + public: + void trim_queue(uint max); + void do_void(); +}; + +// Allow yielding or short-circuiting of reference list +// prelceaning work. +class CMSPrecleanRefsYieldClosure: public YieldClosure { + CMSCollector* _collector; + void do_yield_work(); + public: + CMSPrecleanRefsYieldClosure(CMSCollector* collector): + _collector(collector) {} + virtual bool should_return(); +}; + + +// Convenience class that locks free list locks for given CMS collector +class FreelistLocker: public StackObj { + private: + CMSCollector* _collector; + public: + FreelistLocker(CMSCollector* collector): + _collector(collector) { + _collector->getFreelistLocks(); + } + + ~FreelistLocker() { + _collector->releaseFreelistLocks(); + } +}; + +// Mark all dead objects in a given space. +class MarkDeadObjectsClosure: public BlkClosure { + const CMSCollector* _collector; + const CompactibleFreeListSpace* _sp; + CMSBitMap* _live_bit_map; + CMSBitMap* _dead_bit_map; +public: + MarkDeadObjectsClosure(const CMSCollector* collector, + const CompactibleFreeListSpace* sp, + CMSBitMap *live_bit_map, + CMSBitMap *dead_bit_map) : + _collector(collector), + _sp(sp), + _live_bit_map(live_bit_map), + _dead_bit_map(dead_bit_map) {} + size_t do_blk(HeapWord* addr); +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp new file mode 100644 index 00000000000..c151fab332a --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp @@ -0,0 +1,507 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void CMSBitMap::clear_all() { + assert_locked(); + // CMS bitmaps are usually cover large memory regions + _bm.clear_large(); + return; +} + +inline size_t CMSBitMap::heapWordToOffset(HeapWord* addr) const { + return (pointer_delta(addr, _bmStartWord)) >> _shifter; +} + +inline HeapWord* CMSBitMap::offsetToHeapWord(size_t offset) const { + return _bmStartWord + (offset << _shifter); +} + +inline size_t CMSBitMap::heapWordDiffToOffsetDiff(size_t diff) const { + assert((diff & ((1 << _shifter) - 1)) == 0, "argument check"); + return diff >> _shifter; +} + +inline void CMSBitMap::mark(HeapWord* addr) { + assert_locked(); + assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize), + "outside underlying space?"); + _bm.set_bit(heapWordToOffset(addr)); +} + +inline bool CMSBitMap::par_mark(HeapWord* addr) { + assert_locked(); + assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize), + "outside underlying space?"); + return _bm.par_at_put(heapWordToOffset(addr), true); +} + +inline void CMSBitMap::par_clear(HeapWord* addr) { + assert_locked(); + assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize), + "outside underlying space?"); + _bm.par_at_put(heapWordToOffset(addr), false); +} + +inline void CMSBitMap::mark_range(MemRegion mr) { + NOT_PRODUCT(region_invariant(mr)); + // Range size is usually just 1 bit. + _bm.set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()), + BitMap::small_range); +} + +inline void CMSBitMap::clear_range(MemRegion mr) { + NOT_PRODUCT(region_invariant(mr)); + // Range size is usually just 1 bit. + _bm.clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()), + BitMap::small_range); +} + +inline void CMSBitMap::par_mark_range(MemRegion mr) { + NOT_PRODUCT(region_invariant(mr)); + // Range size is usually just 1 bit. + _bm.par_set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()), + BitMap::small_range); +} + +inline void CMSBitMap::par_clear_range(MemRegion mr) { + NOT_PRODUCT(region_invariant(mr)); + // Range size is usually just 1 bit. + _bm.par_clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()), + BitMap::small_range); +} + +inline void CMSBitMap::mark_large_range(MemRegion mr) { + NOT_PRODUCT(region_invariant(mr)); + // Range size must be greater than 32 bytes. + _bm.set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()), + BitMap::large_range); +} + +inline void CMSBitMap::clear_large_range(MemRegion mr) { + NOT_PRODUCT(region_invariant(mr)); + // Range size must be greater than 32 bytes. + _bm.clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()), + BitMap::large_range); +} + +inline void CMSBitMap::par_mark_large_range(MemRegion mr) { + NOT_PRODUCT(region_invariant(mr)); + // Range size must be greater than 32 bytes. + _bm.par_set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()), + BitMap::large_range); +} + +inline void CMSBitMap::par_clear_large_range(MemRegion mr) { + NOT_PRODUCT(region_invariant(mr)); + // Range size must be greater than 32 bytes. + _bm.par_clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()), + BitMap::large_range); +} + +// Starting at "addr" (inclusive) return a memory region +// corresponding to the first maximally contiguous marked ("1") region. +inline MemRegion CMSBitMap::getAndClearMarkedRegion(HeapWord* addr) { + return getAndClearMarkedRegion(addr, endWord()); +} + +// Starting at "start_addr" (inclusive) return a memory region +// corresponding to the first maximal contiguous marked ("1") region +// strictly less than end_addr. +inline MemRegion CMSBitMap::getAndClearMarkedRegion(HeapWord* start_addr, + HeapWord* end_addr) { + HeapWord *start, *end; + assert_locked(); + start = getNextMarkedWordAddress (start_addr, end_addr); + end = getNextUnmarkedWordAddress(start, end_addr); + assert(start <= end, "Consistency check"); + MemRegion mr(start, end); + if (!mr.is_empty()) { + clear_range(mr); + } + return mr; +} + +inline bool CMSBitMap::isMarked(HeapWord* addr) const { + assert_locked(); + assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize), + "outside underlying space?"); + return _bm.at(heapWordToOffset(addr)); +} + +// The same as isMarked() but without a lock check. +inline bool CMSBitMap::par_isMarked(HeapWord* addr) const { + assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize), + "outside underlying space?"); + return _bm.at(heapWordToOffset(addr)); +} + + +inline bool CMSBitMap::isUnmarked(HeapWord* addr) const { + assert_locked(); + assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize), + "outside underlying space?"); + return !_bm.at(heapWordToOffset(addr)); +} + +// Return the HeapWord address corresponding to next "1" bit +// (inclusive). +inline HeapWord* CMSBitMap::getNextMarkedWordAddress(HeapWord* addr) const { + return getNextMarkedWordAddress(addr, endWord()); +} + +// Return the least HeapWord address corresponding to next "1" bit +// starting at start_addr (inclusive) but strictly less than end_addr. +inline HeapWord* CMSBitMap::getNextMarkedWordAddress( + HeapWord* start_addr, HeapWord* end_addr) const { + assert_locked(); + size_t nextOffset = _bm.get_next_one_offset( + heapWordToOffset(start_addr), + heapWordToOffset(end_addr)); + HeapWord* nextAddr = offsetToHeapWord(nextOffset); + assert(nextAddr >= start_addr && + nextAddr <= end_addr, "get_next_one postcondition"); + assert((nextAddr == end_addr) || + isMarked(nextAddr), "get_next_one postcondition"); + return nextAddr; +} + + +// Return the HeapWord address corrsponding to the next "0" bit +// (inclusive). +inline HeapWord* CMSBitMap::getNextUnmarkedWordAddress(HeapWord* addr) const { + return getNextUnmarkedWordAddress(addr, endWord()); +} + +// Return the HeapWord address corrsponding to the next "0" bit +// (inclusive). +inline HeapWord* CMSBitMap::getNextUnmarkedWordAddress( + HeapWord* start_addr, HeapWord* end_addr) const { + assert_locked(); + size_t nextOffset = _bm.get_next_zero_offset( + heapWordToOffset(start_addr), + heapWordToOffset(end_addr)); + HeapWord* nextAddr = offsetToHeapWord(nextOffset); + assert(nextAddr >= start_addr && + nextAddr <= end_addr, "get_next_zero postcondition"); + assert((nextAddr == end_addr) || + isUnmarked(nextAddr), "get_next_zero postcondition"); + return nextAddr; +} + +inline bool CMSBitMap::isAllClear() const { + assert_locked(); + return getNextMarkedWordAddress(startWord()) >= endWord(); +} + +inline void CMSBitMap::iterate(BitMapClosure* cl, HeapWord* left, + HeapWord* right) { + assert_locked(); + left = MAX2(_bmStartWord, left); + right = MIN2(_bmStartWord + _bmWordSize, right); + if (right > left) { + _bm.iterate(cl, heapWordToOffset(left), heapWordToOffset(right)); + } +} + +inline void CMSCollector::start_icms() { + if (CMSIncrementalMode) { + ConcurrentMarkSweepThread::start_icms(); + } +} + +inline void CMSCollector::stop_icms() { + if (CMSIncrementalMode) { + ConcurrentMarkSweepThread::stop_icms(); + } +} + +inline void CMSCollector::disable_icms() { + if (CMSIncrementalMode) { + ConcurrentMarkSweepThread::disable_icms(); + } +} + +inline void CMSCollector::enable_icms() { + if (CMSIncrementalMode) { + ConcurrentMarkSweepThread::enable_icms(); + } +} + +inline void CMSCollector::icms_wait() { + if (CMSIncrementalMode) { + cmsThread()->icms_wait(); + } +} + +inline void CMSCollector::save_sweep_limits() { + _cmsGen->save_sweep_limit(); + _permGen->save_sweep_limit(); +} + +inline bool CMSCollector::is_dead_obj(oop obj) const { + HeapWord* addr = (HeapWord*)obj; + assert((_cmsGen->cmsSpace()->is_in_reserved(addr) + && _cmsGen->cmsSpace()->block_is_obj(addr)) + || + (_permGen->cmsSpace()->is_in_reserved(addr) + && _permGen->cmsSpace()->block_is_obj(addr)), + "must be object"); + return cms_should_unload_classes() && + _collectorState == Sweeping && + !_markBitMap.isMarked(addr); +} + +inline bool CMSCollector::should_abort_preclean() const { + // We are in the midst of an "abortable preclean" and either + // scavenge is done or foreground GC wants to take over collection + return _collectorState == AbortablePreclean && + (_abort_preclean || _foregroundGCIsActive || + GenCollectedHeap::heap()->incremental_collection_will_fail()); +} + +inline size_t CMSCollector::get_eden_used() const { + return _young_gen->as_DefNewGeneration()->eden()->used(); +} + +inline size_t CMSCollector::get_eden_capacity() const { + return _young_gen->as_DefNewGeneration()->eden()->capacity(); +} + +inline bool CMSStats::valid() const { + return _valid_bits == _ALL_VALID; +} + +inline void CMSStats::record_gc0_begin() { + if (_gc0_begin_time.is_updated()) { + float last_gc0_period = _gc0_begin_time.seconds(); + _gc0_period = AdaptiveWeightedAverage::exp_avg(_gc0_period, + last_gc0_period, _gc0_alpha); + _gc0_alpha = _saved_alpha; + _valid_bits |= _GC0_VALID; + } + _cms_used_at_gc0_begin = _cms_gen->cmsSpace()->used(); + + _gc0_begin_time.update(); +} + +inline void CMSStats::record_gc0_end(size_t cms_gen_bytes_used) { + float last_gc0_duration = _gc0_begin_time.seconds(); + _gc0_duration = AdaptiveWeightedAverage::exp_avg(_gc0_duration, + last_gc0_duration, _gc0_alpha); + + // Amount promoted. + _cms_used_at_gc0_end = cms_gen_bytes_used; + + size_t promoted_bytes = 0; + if (_cms_used_at_gc0_end >= _cms_used_at_gc0_begin) { + promoted_bytes = _cms_used_at_gc0_end - _cms_used_at_gc0_begin; + } + + // If the younger gen collections were skipped, then the + // number of promoted bytes will be 0 and adding it to the + // average will incorrectly lessen the average. It is, however, + // also possible that no promotion was needed. + // + // _gc0_promoted used to be calculated as + // _gc0_promoted = AdaptiveWeightedAverage::exp_avg(_gc0_promoted, + // promoted_bytes, _gc0_alpha); + _cms_gen->gc_stats()->avg_promoted()->sample(promoted_bytes); + _gc0_promoted = (size_t) _cms_gen->gc_stats()->avg_promoted()->average(); + + // Amount directly allocated. + size_t allocated_bytes = _cms_gen->direct_allocated_words() * HeapWordSize; + _cms_gen->reset_direct_allocated_words(); + _cms_allocated = AdaptiveWeightedAverage::exp_avg(_cms_allocated, + allocated_bytes, _gc0_alpha); +} + +inline void CMSStats::record_cms_begin() { + _cms_timer.stop(); + + // This is just an approximate value, but is good enough. + _cms_used_at_cms_begin = _cms_used_at_gc0_end; + + _cms_period = AdaptiveWeightedAverage::exp_avg((float)_cms_period, + (float) _cms_timer.seconds(), _cms_alpha); + _cms_begin_time.update(); + + _cms_timer.reset(); + _cms_timer.start(); +} + +inline void CMSStats::record_cms_end() { + _cms_timer.stop(); + + float cur_duration = _cms_timer.seconds(); + _cms_duration = AdaptiveWeightedAverage::exp_avg(_cms_duration, + cur_duration, _cms_alpha); + + // Avoid division by 0. + const size_t cms_used_mb = MAX2(_cms_used_at_cms_begin / M, (size_t)1); + _cms_duration_per_mb = AdaptiveWeightedAverage::exp_avg(_cms_duration_per_mb, + cur_duration / cms_used_mb, + _cms_alpha); + + _cms_end_time.update(); + _cms_alpha = _saved_alpha; + _allow_duty_cycle_reduction = true; + _valid_bits |= _CMS_VALID; + + _cms_timer.start(); +} + +inline double CMSStats::cms_time_since_begin() const { + return _cms_begin_time.seconds(); +} + +inline double CMSStats::cms_time_since_end() const { + return _cms_end_time.seconds(); +} + +inline double CMSStats::promotion_rate() const { + assert(valid(), "statistics not valid yet"); + return gc0_promoted() / gc0_period(); +} + +inline double CMSStats::cms_allocation_rate() const { + assert(valid(), "statistics not valid yet"); + return cms_allocated() / gc0_period(); +} + +inline double CMSStats::cms_consumption_rate() const { + assert(valid(), "statistics not valid yet"); + return (gc0_promoted() + cms_allocated()) / gc0_period(); +} + +inline unsigned int CMSStats::icms_update_duty_cycle() { + // Update the duty cycle only if pacing is enabled and the stats are valid + // (after at least one young gen gc and one cms cycle have completed). + if (CMSIncrementalPacing && valid()) { + return icms_update_duty_cycle_impl(); + } + return _icms_duty_cycle; +} + +inline void ConcurrentMarkSweepGeneration::save_sweep_limit() { + cmsSpace()->save_sweep_limit(); +} + +inline size_t ConcurrentMarkSweepGeneration::capacity() const { + return _cmsSpace->capacity(); +} + +inline size_t ConcurrentMarkSweepGeneration::used() const { + return _cmsSpace->used(); +} + +inline size_t ConcurrentMarkSweepGeneration::free() const { + return _cmsSpace->free(); +} + +inline MemRegion ConcurrentMarkSweepGeneration::used_region() const { + return _cmsSpace->used_region(); +} + +inline MemRegion ConcurrentMarkSweepGeneration::used_region_at_save_marks() const { + return _cmsSpace->used_region_at_save_marks(); +} + +inline void MarkFromRootsClosure::do_yield_check() { + if (ConcurrentMarkSweepThread::should_yield() && + !_collector->foregroundGCIsActive() && + _yield) { + do_yield_work(); + } +} + +inline void Par_MarkFromRootsClosure::do_yield_check() { + if (ConcurrentMarkSweepThread::should_yield() && + !_collector->foregroundGCIsActive() && + _yield) { + do_yield_work(); + } +} + +// Return value of "true" indicates that the on-going preclean +// should be aborted. +inline bool ScanMarkedObjectsAgainCarefullyClosure::do_yield_check() { + if (ConcurrentMarkSweepThread::should_yield() && + !_collector->foregroundGCIsActive() && + _yield) { + // Sample young gen size before and after yield + _collector->sample_eden(); + do_yield_work(); + _collector->sample_eden(); + return _collector->should_abort_preclean(); + } + return false; +} + +inline void SurvivorSpacePrecleanClosure::do_yield_check() { + if (ConcurrentMarkSweepThread::should_yield() && + !_collector->foregroundGCIsActive() && + _yield) { + // Sample young gen size before and after yield + _collector->sample_eden(); + do_yield_work(); + _collector->sample_eden(); + } +} + +inline void SweepClosure::do_yield_check(HeapWord* addr) { + if (ConcurrentMarkSweepThread::should_yield() && + !_collector->foregroundGCIsActive() && + _yield) { + do_yield_work(addr); + } +} + +inline void MarkRefsIntoAndScanClosure::do_yield_check() { + // The conditions are ordered for the remarking phase + // when _yield is false. + if (_yield && + !_collector->foregroundGCIsActive() && + ConcurrentMarkSweepThread::should_yield()) { + do_yield_work(); + } +} + + +inline void ModUnionClosure::do_MemRegion(MemRegion mr) { + // Align the end of mr so it's at a card boundary. + // This is superfluous except at the end of the space; + // we should do better than this XXX + MemRegion mr2(mr.start(), (HeapWord*)round_to((intptr_t)mr.end(), + CardTableModRefBS::card_size /* bytes */)); + _t->mark_range(mr2); +} + +inline void ModUnionClosurePar::do_MemRegion(MemRegion mr) { + // Align the end of mr so it's at a card boundary. + // This is superfluous except at the end of the space; + // we should do better than this XXX + MemRegion mr2(mr.start(), (HeapWord*)round_to((intptr_t)mr.end(), + CardTableModRefBS::card_size /* bytes */)); + _t->par_mark_range(mr2); +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.cpp new file mode 100644 index 00000000000..b4d43f817ec --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.cpp @@ -0,0 +1,350 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_concurrentMarkSweepThread.cpp.incl" + +// ======= Concurrent Mark Sweep Thread ======== + +// The CMS thread is created when Concurrent Mark Sweep is used in the +// older of two generations in a generational memory system. + +ConcurrentMarkSweepThread* + ConcurrentMarkSweepThread::_cmst = NULL; +CMSCollector* ConcurrentMarkSweepThread::_collector = NULL; +bool ConcurrentMarkSweepThread::_should_terminate = false; +int ConcurrentMarkSweepThread::_CMS_flag = CMS_nil; + +volatile jint ConcurrentMarkSweepThread::_pending_yields = 0; +volatile jint ConcurrentMarkSweepThread::_pending_decrements = 0; + +volatile bool ConcurrentMarkSweepThread::_icms_enabled = false; +volatile bool ConcurrentMarkSweepThread::_should_run = false; +// When icms is enabled, the icms thread is stopped until explicitly +// started. +volatile bool ConcurrentMarkSweepThread::_should_stop = true; + +SurrogateLockerThread* + ConcurrentMarkSweepThread::_slt = NULL; +SurrogateLockerThread::SLT_msg_type + ConcurrentMarkSweepThread::_sltBuffer = SurrogateLockerThread::empty; +Monitor* + ConcurrentMarkSweepThread::_sltMonitor = NULL; + +ConcurrentMarkSweepThread::ConcurrentMarkSweepThread(CMSCollector* collector) + : ConcurrentGCThread() { + assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set"); + assert(_cmst == NULL, "CMS thread already created"); + _cmst = this; + assert(_collector == NULL, "Collector already set"); + _collector = collector; + + set_name("Concurrent Mark-Sweep GC Thread"); + + if (os::create_thread(this, os::cgc_thread)) { + // XXX: need to set this to low priority + // unless "agressive mode" set; priority + // should be just less than that of VMThread. + os::set_priority(this, NearMaxPriority); + if (!DisableStartThread) { + os::start_thread(this); + } + } + _sltMonitor = SLT_lock; + set_icms_enabled(CMSIncrementalMode); +} + +void ConcurrentMarkSweepThread::run() { + assert(this == cmst(), "just checking"); + + this->record_stack_base_and_size(); + this->initialize_thread_local_storage(); + this->set_active_handles(JNIHandleBlock::allocate_block()); + // From this time Thread::current() should be working. + assert(this == Thread::current(), "just checking"); + if (BindCMSThreadToCPU && !os::bind_to_processor(CPUForCMSThread)) { + warning("Couldn't bind CMS thread to processor %u", CPUForCMSThread); + } + // Wait until Universe::is_fully_initialized() + { + CMSLoopCountWarn loopX("CMS::run", "waiting for " + "Universe::is_fully_initialized()", 2); + MutexLockerEx x(CGC_lock, true); + set_CMS_flag(CMS_cms_wants_token); + // Wait until Universe is initialized and all initialization is completed. + while (!is_init_completed() && !Universe::is_fully_initialized() && + !_should_terminate) { + CGC_lock->wait(true, 200); + loopX.tick(); + } + // Wait until the surrogate locker thread that will do + // pending list locking on our behalf has been created. + // We cannot start the SLT thread ourselves since we need + // to be a JavaThread to do so. + CMSLoopCountWarn loopY("CMS::run", "waiting for SLT installation", 2); + while (_slt == NULL && !_should_terminate) { + CGC_lock->wait(true, 200); + loopY.tick(); + } + clear_CMS_flag(CMS_cms_wants_token); + } + + while (!_should_terminate) { + sleepBeforeNextCycle(); + if (_should_terminate) break; + _collector->collect_in_background(false); // !clear_all_soft_refs + } + assert(_should_terminate, "just checking"); + // Check that the state of any protocol for synchronization + // between background (CMS) and foreground collector is "clean" + // (i.e. will not potentially block the foreground collector, + // requiring action by us). + verify_ok_to_terminate(); + // Signal that it is terminated + { + MutexLockerEx mu(Terminator_lock, + Mutex::_no_safepoint_check_flag); + assert(_cmst == this, "Weird!"); + _cmst = NULL; + Terminator_lock->notify(); + } + + // Thread destructor usually does this.. + ThreadLocalStorage::set_thread(NULL); +} + +#ifndef PRODUCT +void ConcurrentMarkSweepThread::verify_ok_to_terminate() const { + assert(!(CGC_lock->owned_by_self() || cms_thread_has_cms_token() || + cms_thread_wants_cms_token()), + "Must renounce all worldly possessions and desires for nirvana"); + _collector->verify_ok_to_terminate(); +} +#endif + +// create and start a new ConcurrentMarkSweep Thread for given CMS generation +ConcurrentMarkSweepThread* ConcurrentMarkSweepThread::start(CMSCollector* collector) { + if (!_should_terminate) { + assert(cmst() == NULL, "start() called twice?"); + ConcurrentMarkSweepThread* th = new ConcurrentMarkSweepThread(collector); + assert(cmst() == th, "Where did the just-created CMS thread go?"); + return th; + } + return NULL; +} + +void ConcurrentMarkSweepThread::stop() { + if (CMSIncrementalMode) { + // Disable incremental mode and wake up the thread so it notices the change. + disable_icms(); + start_icms(); + } + // it is ok to take late safepoints here, if needed + { + MutexLockerEx x(Terminator_lock); + _should_terminate = true; + } + { // Now post a notify on CGC_lock so as to nudge + // CMS thread(s) that might be slumbering in + // sleepBeforeNextCycle. + MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag); + CGC_lock->notify_all(); + } + { // Now wait until (all) CMS thread(s) have exited + MutexLockerEx x(Terminator_lock); + while(cmst() != NULL) { + Terminator_lock->wait(); + } + } +} + +void ConcurrentMarkSweepThread::threads_do(ThreadClosure* tc) { + assert(tc != NULL, "Null ThreadClosure"); + if (_cmst != NULL) { + tc->do_thread(_cmst); + } + assert(Universe::is_fully_initialized(), + "Called too early, make sure heap is fully initialized"); + if (_collector != NULL) { + AbstractWorkGang* gang = _collector->conc_workers(); + if (gang != NULL) { + gang->threads_do(tc); + } + } +} + +void ConcurrentMarkSweepThread::print_on(outputStream* st) const { + st->print("\"%s\" ", name()); + Thread::print_on(st); + st->cr(); +} + +void ConcurrentMarkSweepThread::print_all_on(outputStream* st) { + if (_cmst != NULL) { + _cmst->print_on(st); + } + if (_collector != NULL) { + AbstractWorkGang* gang = _collector->conc_workers(); + if (gang != NULL) { + gang->print_worker_threads_on(st); + } + } +} + +void ConcurrentMarkSweepThread::synchronize(bool is_cms_thread) { + assert(UseConcMarkSweepGC, "just checking"); + + MutexLockerEx x(CGC_lock, + Mutex::_no_safepoint_check_flag); + if (!is_cms_thread) { + assert(Thread::current()->is_VM_thread(), "Not a VM thread"); + CMSSynchronousYieldRequest yr; + while (CMS_flag_is_set(CMS_cms_has_token)) { + // indicate that we want to get the token + set_CMS_flag(CMS_vm_wants_token); + CGC_lock->wait(true); + } + // claim the token and proceed + clear_CMS_flag(CMS_vm_wants_token); + set_CMS_flag(CMS_vm_has_token); + } else { + assert(Thread::current()->is_ConcurrentGC_thread(), + "Not a CMS thread"); + // The following barrier assumes there's only one CMS thread. + // This will need to be modified is there are more CMS threads than one. + while (CMS_flag_is_set(CMS_vm_has_token | CMS_vm_wants_token)) { + set_CMS_flag(CMS_cms_wants_token); + CGC_lock->wait(true); + } + // claim the token + clear_CMS_flag(CMS_cms_wants_token); + set_CMS_flag(CMS_cms_has_token); + } +} + +void ConcurrentMarkSweepThread::desynchronize(bool is_cms_thread) { + assert(UseConcMarkSweepGC, "just checking"); + + MutexLockerEx x(CGC_lock, + Mutex::_no_safepoint_check_flag); + if (!is_cms_thread) { + assert(Thread::current()->is_VM_thread(), "Not a VM thread"); + assert(CMS_flag_is_set(CMS_vm_has_token), "just checking"); + clear_CMS_flag(CMS_vm_has_token); + if (CMS_flag_is_set(CMS_cms_wants_token)) { + // wake-up a waiting CMS thread + CGC_lock->notify(); + } + assert(!CMS_flag_is_set(CMS_vm_has_token | CMS_vm_wants_token), + "Should have been cleared"); + } else { + assert(Thread::current()->is_ConcurrentGC_thread(), + "Not a CMS thread"); + assert(CMS_flag_is_set(CMS_cms_has_token), "just checking"); + clear_CMS_flag(CMS_cms_has_token); + if (CMS_flag_is_set(CMS_vm_wants_token)) { + // wake-up a waiting VM thread + CGC_lock->notify(); + } + assert(!CMS_flag_is_set(CMS_cms_has_token | CMS_cms_wants_token), + "Should have been cleared"); + } +} + +// Wait until the next synchronous GC or a timeout, whichever is earlier. +void ConcurrentMarkSweepThread::wait_on_cms_lock(long t) { + MutexLockerEx x(CGC_lock, + Mutex::_no_safepoint_check_flag); + set_CMS_flag(CMS_cms_wants_token); // to provoke notifies + CGC_lock->wait(Mutex::_no_safepoint_check_flag, t); + clear_CMS_flag(CMS_cms_wants_token); + assert(!CMS_flag_is_set(CMS_cms_has_token | CMS_cms_wants_token), + "Should not be set"); +} + +void ConcurrentMarkSweepThread::sleepBeforeNextCycle() { + while (!_should_terminate) { + if (CMSIncrementalMode) { + icms_wait(); + return; + } else { + // Wait until the next synchronous GC or a timeout, whichever is earlier + wait_on_cms_lock(CMSWaitDuration); + } + // Check if we should start a CMS collection cycle + if (_collector->shouldConcurrentCollect()) { + return; + } + // .. collection criterion not yet met, let's go back + // and wait some more + } +} + +// Incremental CMS +void ConcurrentMarkSweepThread::start_icms() { + assert(UseConcMarkSweepGC && CMSIncrementalMode, "just checking"); + MutexLockerEx x(iCMS_lock, Mutex::_no_safepoint_check_flag); + trace_state("start_icms"); + _should_run = true; + iCMS_lock->notify_all(); +} + +void ConcurrentMarkSweepThread::stop_icms() { + assert(UseConcMarkSweepGC && CMSIncrementalMode, "just checking"); + MutexLockerEx x(iCMS_lock, Mutex::_no_safepoint_check_flag); + if (!_should_stop) { + trace_state("stop_icms"); + _should_stop = true; + _should_run = false; + asynchronous_yield_request(); + iCMS_lock->notify_all(); + } +} + +void ConcurrentMarkSweepThread::icms_wait() { + assert(UseConcMarkSweepGC && CMSIncrementalMode, "just checking"); + if (_should_stop && icms_enabled()) { + MutexLockerEx x(iCMS_lock, Mutex::_no_safepoint_check_flag); + trace_state("pause_icms"); + _collector->stats().stop_cms_timer(); + while(!_should_run && icms_enabled()) { + iCMS_lock->wait(Mutex::_no_safepoint_check_flag); + } + _collector->stats().start_cms_timer(); + _should_stop = false; + trace_state("pause_icms end"); + } +} + +// Note: this method, although exported by the ConcurrentMarkSweepThread, +// which is a non-JavaThread, can only be called by a JavaThread. +// Currently this is done at vm creation time (post-vm-init) by the +// main/Primordial (Java)Thread. +// XXX Consider changing this in the future to allow the CMS thread +// itself to create this thread? +void ConcurrentMarkSweepThread::makeSurrogateLockerThread(TRAPS) { + assert(UseConcMarkSweepGC, "SLT thread needed only for CMS GC"); + assert(_slt == NULL, "SLT already created"); + _slt = SurrogateLockerThread::make(THREAD); +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp new file mode 100644 index 00000000000..4cf69094ab2 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp @@ -0,0 +1,229 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ConcurrentMarkSweepGeneration; +class CMSCollector; + +// The Concurrent Mark Sweep GC Thread (could be several in the future). +class ConcurrentMarkSweepThread: public ConcurrentGCThread { + friend class VMStructs; + friend class ConcurrentMarkSweepGeneration; // XXX should remove friendship + friend class CMSCollector; + public: + virtual void run(); + + private: + static ConcurrentMarkSweepThread* _cmst; + static CMSCollector* _collector; + static SurrogateLockerThread* _slt; + static SurrogateLockerThread::SLT_msg_type _sltBuffer; + static Monitor* _sltMonitor; + + ConcurrentMarkSweepThread* _next; + + static bool _should_terminate; + + enum CMS_flag_type { + CMS_nil = NoBits, + CMS_cms_wants_token = nth_bit(0), + CMS_cms_has_token = nth_bit(1), + CMS_vm_wants_token = nth_bit(2), + CMS_vm_has_token = nth_bit(3) + }; + + static int _CMS_flag; + + static bool CMS_flag_is_set(int b) { return (_CMS_flag & b) != 0; } + static bool set_CMS_flag(int b) { return (_CMS_flag |= b) != 0; } + static bool clear_CMS_flag(int b) { return (_CMS_flag &= ~b) != 0; } + void sleepBeforeNextCycle(); + + // CMS thread should yield for a young gen collection, direct allocation, + // and iCMS activity. + static char _pad_1[64 - sizeof(jint)]; // prevent cache-line sharing + static volatile jint _pending_yields; + static volatile jint _pending_decrements; // decrements to _pending_yields + static char _pad_2[64 - sizeof(jint)]; // prevent cache-line sharing + + // Tracing messages, enabled by CMSTraceThreadState. + static inline void trace_state(const char* desc); + + static volatile bool _icms_enabled; // iCMS enabled? + static volatile bool _should_run; // iCMS may run + static volatile bool _should_stop; // iCMS should stop + + // debugging + void verify_ok_to_terminate() const PRODUCT_RETURN; + + public: + // Constructor + ConcurrentMarkSweepThread(CMSCollector* collector); + + static void makeSurrogateLockerThread(TRAPS); + static SurrogateLockerThread* slt() { return _slt; } + + // Tester + bool is_ConcurrentGC_thread() const { return true; } + + static void threads_do(ThreadClosure* tc); + + // Printing + void print_on(outputStream* st) const; + void print() const { print_on(tty); } + static void print_all_on(outputStream* st); + static void print_all() { print_all_on(tty); } + + // Returns the CMS Thread + static ConcurrentMarkSweepThread* cmst() { return _cmst; } + static CMSCollector* collector() { return _collector; } + + // Create and start the CMS Thread, or stop it on shutdown + static ConcurrentMarkSweepThread* start(CMSCollector* collector); + static void stop(); + static bool should_terminate() { return _should_terminate; } + + // Synchronization using CMS token + static void synchronize(bool is_cms_thread); + static void desynchronize(bool is_cms_thread); + static bool vm_thread_has_cms_token() { + return CMS_flag_is_set(CMS_vm_has_token); + } + static bool cms_thread_has_cms_token() { + return CMS_flag_is_set(CMS_cms_has_token); + } + static bool vm_thread_wants_cms_token() { + return CMS_flag_is_set(CMS_vm_wants_token); + } + static bool cms_thread_wants_cms_token() { + return CMS_flag_is_set(CMS_cms_wants_token); + } + + // Wait on CMS lock until the next synchronous GC + // or given timeout, whichever is earlier. + void wait_on_cms_lock(long t); // milliseconds + + // The CMS thread will yield during the work portion of it's cycle + // only when requested to. Both synchronous and asychronous requests + // are provided. A synchronous request is used for young gen + // collections and direct allocations. The requesting thread increments + // pending_yields at the beginning of an operation, and decrements it when + // the operation is completed. The CMS thread yields when pending_yields + // is positive. An asynchronous request is used by iCMS in the stop_icms() + // operation. A single yield satisfies the outstanding asynch yield requests. + // The requesting thread increments both pending_yields and pending_decrements. + // After yielding, the CMS thread decrements both by the amount in + // pending_decrements. + // Note that, while "_pending_yields >= _pending_decrements" is an invariant, + // we cannot easily test that invariant, since the counters are manipulated via + // atomic instructions without explicit locking and we cannot read + // the two counters atomically together: one suggestion is to + // use (for example) 16-bit counters so as to be able to read the + // two counters atomically even on 32-bit platforms. Notice that + // the second assert in acknowledge_yield_request() does indeed + // check a form of the above invariant, albeit indirectly. + + static void increment_pending_yields() { + Atomic::inc(&_pending_yields); + assert(_pending_yields >= 0, "can't be negative"); + } + static void decrement_pending_yields() { + Atomic::dec(&_pending_yields); + assert(_pending_yields >= 0, "can't be negative"); + } + static void asynchronous_yield_request() { + increment_pending_yields(); + Atomic::inc(&_pending_decrements); + assert(_pending_decrements >= 0, "can't be negative"); + } + static void acknowledge_yield_request() { + jint decrement = _pending_decrements; + if (decrement > 0) { + // Order important to preserve: _pending_yields >= _pending_decrements + Atomic::add(-decrement, &_pending_decrements); + Atomic::add(-decrement, &_pending_yields); + assert(_pending_decrements >= 0, "can't be negative"); + assert(_pending_yields >= 0, "can't be negative"); + } + } + static bool should_yield() { return _pending_yields > 0; } + + // CMS incremental mode. + static void start_icms(); // notify thread to start a quantum of work + static void stop_icms(); // request thread to stop working + void icms_wait(); // if asked to stop, wait until notified to start + + // Incremental mode is enabled globally by the flag CMSIncrementalMode. It + // must also be enabled/disabled dynamically to allow foreground collections. + static inline void enable_icms() { _icms_enabled = true; } + static inline void disable_icms() { _icms_enabled = false; } + static inline void set_icms_enabled(bool val) { _icms_enabled = val; } + static inline bool icms_enabled() { return _icms_enabled; } +}; + +inline void ConcurrentMarkSweepThread::trace_state(const char* desc) { + if (CMSTraceThreadState) { + char buf[128]; + TimeStamp& ts = gclog_or_tty->time_stamp(); + if (!ts.is_updated()) { + ts.update(); + } + jio_snprintf(buf, sizeof(buf), " [%.3f: CMSThread %s] ", + ts.seconds(), desc); + buf[sizeof(buf) - 1] = '\0'; + gclog_or_tty->print(buf); + } +} + +// For scoped increment/decrement of yield requests +class CMSSynchronousYieldRequest: public StackObj { + public: + CMSSynchronousYieldRequest() { + ConcurrentMarkSweepThread::increment_pending_yields(); + } + ~CMSSynchronousYieldRequest() { + ConcurrentMarkSweepThread::decrement_pending_yields(); + } +}; + +// Used to emit a warning in case of unexpectedly excessive +// looping (in "apparently endless loops") in CMS code. +class CMSLoopCountWarn: public StackObj { + private: + const char* _src; + const char* _msg; + const intx _threshold; + intx _ticks; + + public: + inline CMSLoopCountWarn(const char* src, const char* msg, + const intx threshold) : + _src(src), _msg(msg), _threshold(threshold), _ticks(0) { } + + inline void tick() { + _ticks++; + if (CMSLoopWarn && _ticks % _threshold == 0) { + warning("%s has looped %d times %s", _src, _ticks, _msg); + } + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeBlockDictionary.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeBlockDictionary.cpp new file mode 100644 index 00000000000..1c641f0acd7 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeBlockDictionary.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_freeBlockDictionary.cpp.incl" + +#ifndef PRODUCT +Mutex* FreeBlockDictionary::par_lock() const { + return _lock; +} + +void FreeBlockDictionary::set_par_lock(Mutex* lock) { + _lock = lock; +} + +void FreeBlockDictionary::verify_par_locked() const { +#ifdef ASSERT + if (ParallelGCThreads > 0) { + Thread* myThread = Thread::current(); + if (myThread->is_GC_task_thread()) { + assert(par_lock() != NULL, "Should be using locking?"); + assert_lock_strong(par_lock()); + } + } +#endif // ASSERT +} +#endif diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeBlockDictionary.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeBlockDictionary.hpp new file mode 100644 index 00000000000..2b140d0e369 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeBlockDictionary.hpp @@ -0,0 +1,171 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Free block maintenance for Concurrent Mark Sweep Generation +// +// The main data structure for free blocks are +// . an indexed array of small free blocks, and +// . a dictionary of large free blocks +// + +// No virtuals in FreeChunk (don't want any vtables). + +// A FreeChunk is merely a chunk that can be in a doubly linked list +// and has a size field. NOTE: FreeChunks are distinguished from allocated +// objects in two ways (by the sweeper). The second word (prev) has the +// LSB set to indicate a free chunk; allocated objects' klass() pointers +// don't have their LSB set. The corresponding bit in the CMSBitMap is +// set when the chunk is allocated. There are also blocks that "look free" +// but are not part of the free list and should not be coalesced into larger +// free blocks. These free blocks have their two LSB's set. + +class FreeChunk VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + FreeChunk* _next; + FreeChunk* _prev; + size_t _size; + + public: + NOT_PRODUCT(static const size_t header_size();) + // Returns "true" if the "wrd", which is required to be the second word + // of a block, indicates that the block represents a free chunk. + static bool secondWordIndicatesFreeChunk(intptr_t wrd) { + return (wrd & 0x1) == 0x1; + } + bool isFree() const { + return secondWordIndicatesFreeChunk((intptr_t)_prev); + } + bool cantCoalesce() const { return (((intptr_t)_prev) & 0x3) == 0x3; } + FreeChunk* next() const { return _next; } + FreeChunk* prev() const { return (FreeChunk*)(((intptr_t)_prev) & ~(0x3)); } + debug_only(void* prev_addr() const { return (void*)&_prev; }) + + void linkAfter(FreeChunk* ptr) { + linkNext(ptr); + if (ptr != NULL) ptr->linkPrev(this); + } + void linkAfterNonNull(FreeChunk* ptr) { + assert(ptr != NULL, "precondition violation"); + linkNext(ptr); + ptr->linkPrev(this); + } + void linkNext(FreeChunk* ptr) { _next = ptr; } + void linkPrev(FreeChunk* ptr) { _prev = (FreeChunk*)((intptr_t)ptr | 0x1); } + void clearPrev() { _prev = NULL; } + void clearNext() { _next = NULL; } + void dontCoalesce() { + // the block should be free + assert(isFree(), "Should look like a free block"); + _prev = (FreeChunk*)(((intptr_t)_prev) | 0x2); + } + void markFree() { _prev = (FreeChunk*)((intptr_t)_prev | 0x1); } + void markNotFree() { _prev = NULL; } + + size_t size() const { return _size; } + void setSize(size_t size) { _size = size; } + + // For volatile reads: + size_t* size_addr() { return &_size; } + + // Return the address past the end of this chunk + HeapWord* end() const { return ((HeapWord*) this) + _size; } + + // debugging + void verify() const PRODUCT_RETURN; + void verifyList() const PRODUCT_RETURN; + void mangleAllocated(size_t size) PRODUCT_RETURN; + void mangleFreed(size_t size) PRODUCT_RETURN; +}; + +// Alignment helpers etc. +#define numQuanta(x,y) ((x+y-1)/y) +enum AlignmentConstants { + MinChunkSize = numQuanta(sizeof(FreeChunk), MinObjAlignmentInBytes) * MinObjAlignment +}; + +// A FreeBlockDictionary is an abstract superclass that will allow +// a number of alternative implementations in the future. +class FreeBlockDictionary: public CHeapObj { + public: + enum Dither { + atLeast, + exactly, + roughly + }; + enum DictionaryChoice { + dictionaryBinaryTree = 0, + dictionarySplayTree = 1, + dictionarySkipList = 2 + }; + + private: + NOT_PRODUCT(Mutex* _lock;) + + public: + virtual void removeChunk(FreeChunk* fc) = 0; + virtual FreeChunk* getChunk(size_t size, Dither dither = atLeast) = 0; + virtual void returnChunk(FreeChunk* chunk) = 0; + virtual size_t totalChunkSize(debug_only(const Mutex* lock)) const = 0; + virtual size_t maxChunkSize() const = 0; + virtual size_t minSize() const = 0; + // Reset the dictionary to the initial conditions for a single + // block. + virtual void reset(HeapWord* addr, size_t size) = 0; + virtual void reset() = 0; + + virtual void dictCensusUpdate(size_t size, bool split, bool birth) = 0; + virtual bool coalDictOverPopulated(size_t size) = 0; + virtual void beginSweepDictCensus(double coalSurplusPercent, + float sweep_current, float sweep_ewstimate) = 0; + virtual void endSweepDictCensus(double splitSurplusPercent) = 0; + virtual FreeChunk* findLargestDict() const = 0; + // verify that the given chunk is in the dictionary. + virtual bool verifyChunkInFreeLists(FreeChunk* tc) const = 0; + + // Sigma_{all_free_blocks} (block_size^2) + virtual double sum_of_squared_block_sizes() const = 0; + + virtual FreeChunk* find_chunk_ends_at(HeapWord* target) const = 0; + virtual void inc_totalSize(size_t v) = 0; + virtual void dec_totalSize(size_t v) = 0; + + NOT_PRODUCT ( + virtual size_t sumDictReturnedBytes() = 0; + virtual void initializeDictReturnedBytes() = 0; + virtual size_t totalCount() = 0; + ) + + virtual void reportStatistics() const { + gclog_or_tty->print("No statistics available"); + } + + virtual void printDictCensus() const = 0; + + virtual void verify() const = 0; + + Mutex* par_lock() const PRODUCT_RETURN0; + void set_par_lock(Mutex* lock) PRODUCT_RETURN; + void verify_par_locked() const PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.cpp new file mode 100644 index 00000000000..b5153971b4e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_freeChunk.cpp.incl" + +#ifndef PRODUCT + +#define baadbabeHeapWord badHeapWordVal +#define deadbeefHeapWord 0xdeadbeef + +size_t const FreeChunk::header_size() { + return sizeof(FreeChunk)/HeapWordSize; +} + +void FreeChunk::mangleAllocated(size_t size) { + // mangle all but the header of a just-allocated block + // of storage + assert(size >= MinChunkSize, "smallest size of object"); + // we can't assert that _size == size because this may be an + // allocation out of a linear allocation block + assert(sizeof(FreeChunk) % HeapWordSize == 0, + "shouldn't write beyond chunk"); + HeapWord* addr = (HeapWord*)this; + size_t hdr = header_size(); + Copy::fill_to_words(addr + hdr, size - hdr, baadbabeHeapWord); +} + +void FreeChunk::mangleFreed(size_t size) { + assert(baadbabeHeapWord != deadbeefHeapWord, "Need distinct patterns"); + // mangle all but the header of a just-freed block of storage + // just prior to passing it to the storage dictionary + assert(size >= MinChunkSize, "smallest size of object"); + assert(size == _size, "just checking"); + HeapWord* addr = (HeapWord*)this; + size_t hdr = header_size(); + Copy::fill_to_words(addr + hdr, size - hdr, deadbeefHeapWord); +} + +void FreeChunk::verifyList() const { + FreeChunk* nextFC = next(); + if (nextFC != NULL) { + assert(this == nextFC->prev(), "broken chain"); + assert(size() == nextFC->size(), "wrong size"); + nextFC->verifyList(); + } +} +#endif diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeList.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeList.cpp new file mode 100644 index 00000000000..1d1024e5057 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeList.cpp @@ -0,0 +1,304 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_freeList.cpp.incl" + +// Free list. A FreeList is used to access a linked list of chunks +// of space in the heap. The head and tail are maintained so that +// items can be (as in the current implementation) added at the +// at the tail of the list and removed from the head of the list to +// maintain a FIFO queue. + +FreeList::FreeList() : + _head(NULL), _tail(NULL) +#ifdef ASSERT + , _protecting_lock(NULL) +#endif +{ + _size = 0; + _count = 0; + _hint = 0; + init_statistics(); +} + +FreeList::FreeList(FreeChunk* fc) : + _head(fc), _tail(fc) +#ifdef ASSERT + , _protecting_lock(NULL) +#endif +{ + _size = fc->size(); + _count = 1; + _hint = 0; + init_statistics(); +#ifndef PRODUCT + _allocation_stats.set_returnedBytes(size() * HeapWordSize); +#endif +} + +FreeList::FreeList(HeapWord* addr, size_t size) : + _head((FreeChunk*) addr), _tail((FreeChunk*) addr) +#ifdef ASSERT + , _protecting_lock(NULL) +#endif +{ + assert(size > sizeof(FreeChunk), "size is too small"); + head()->setSize(size); + _size = size; + _count = 1; + init_statistics(); +#ifndef PRODUCT + _allocation_stats.set_returnedBytes(_size * HeapWordSize); +#endif +} + +void FreeList::reset(size_t hint) { + set_count(0); + set_head(NULL); + set_tail(NULL); + set_hint(hint); +} + +void FreeList::init_statistics() { + _allocation_stats.initialize(); +} + +FreeChunk* FreeList::getChunkAtHead() { + assert_proper_lock_protection(); + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + FreeChunk* fc = head(); + if (fc != NULL) { + FreeChunk* nextFC = fc->next(); + if (nextFC != NULL) { + // The chunk fc being removed has a "next". Set the "next" to the + // "prev" of fc. + nextFC->linkPrev(NULL); + } else { // removed tail of list + link_tail(NULL); + } + link_head(nextFC); + decrement_count(); + } + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + return fc; +} + + +void FreeList::getFirstNChunksFromList(size_t n, FreeList* fl) { + assert_proper_lock_protection(); + assert(fl->count() == 0, "Precondition"); + if (count() > 0) { + int k = 1; + fl->set_head(head()); n--; + FreeChunk* tl = head(); + while (tl->next() != NULL && n > 0) { + tl = tl->next(); n--; k++; + } + assert(tl != NULL, "Loop Inv."); + + // First, fix up the list we took from. + FreeChunk* new_head = tl->next(); + set_head(new_head); + set_count(count() - k); + if (new_head == NULL) { + set_tail(NULL); + } else { + new_head->linkPrev(NULL); + } + // Now we can fix up the tail. + tl->linkNext(NULL); + // And return the result. + fl->set_tail(tl); + fl->set_count(k); + } +} + +// Remove this chunk from the list +void FreeList::removeChunk(FreeChunk*fc) { + assert_proper_lock_protection(); + assert(head() != NULL, "Remove from empty list"); + assert(fc != NULL, "Remove a NULL chunk"); + assert(size() == fc->size(), "Wrong list"); + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + + FreeChunk* prevFC = fc->prev(); + FreeChunk* nextFC = fc->next(); + if (nextFC != NULL) { + // The chunk fc being removed has a "next". Set the "next" to the + // "prev" of fc. + nextFC->linkPrev(prevFC); + } else { // removed tail of list + link_tail(prevFC); + } + if (prevFC == NULL) { // removed head of list + link_head(nextFC); + assert(nextFC == NULL || nextFC->prev() == NULL, + "Prev of head should be NULL"); + } else { + prevFC->linkNext(nextFC); + assert(tail() != prevFC || prevFC->next() == NULL, + "Next of tail should be NULL"); + } + decrement_count(); +#define TRAP_CODE 1 +#if TRAP_CODE + if (head() == NULL) { + guarantee(tail() == NULL, "INVARIANT"); + guarantee(count() == 0, "INVARIANT"); + } +#endif + // clear next and prev fields of fc, debug only + NOT_PRODUCT( + fc->linkPrev(NULL); + fc->linkNext(NULL); + ) + assert(fc->isFree(), "Should still be a free chunk"); + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + assert(head() == NULL || head()->size() == size(), "wrong item on list"); + assert(tail() == NULL || tail()->size() == size(), "wrong item on list"); +} + +// Add this chunk at the head of the list. +void FreeList::returnChunkAtHead(FreeChunk* chunk, bool record_return) { + assert_proper_lock_protection(); + assert(chunk != NULL, "insert a NULL chunk"); + assert(size() == chunk->size(), "Wrong size"); + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + + FreeChunk* oldHead = head(); + assert(chunk != oldHead, "double insertion"); + chunk->linkAfter(oldHead); + link_head(chunk); + if (oldHead == NULL) { // only chunk in list + assert(tail() == NULL, "inconsistent FreeList"); + link_tail(chunk); + } + increment_count(); // of # of chunks in list + DEBUG_ONLY( + if (record_return) { + increment_returnedBytes_by(size()*HeapWordSize); + } + ) + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + assert(head() == NULL || head()->size() == size(), "wrong item on list"); + assert(tail() == NULL || tail()->size() == size(), "wrong item on list"); +} + +void FreeList::returnChunkAtHead(FreeChunk* chunk) { + assert_proper_lock_protection(); + returnChunkAtHead(chunk, true); +} + +// Add this chunk at the tail of the list. +void FreeList::returnChunkAtTail(FreeChunk* chunk, bool record_return) { + assert_proper_lock_protection(); + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + assert(chunk != NULL, "insert a NULL chunk"); + assert(size() == chunk->size(), "wrong size"); + + FreeChunk* oldTail = tail(); + assert(chunk != oldTail, "double insertion"); + if (oldTail != NULL) { + oldTail->linkAfter(chunk); + } else { // only chunk in list + assert(head() == NULL, "inconsistent FreeList"); + link_head(chunk); + } + link_tail(chunk); + increment_count(); // of # of chunks in list + DEBUG_ONLY( + if (record_return) { + increment_returnedBytes_by(size()*HeapWordSize); + } + ) + assert(head() == NULL || head()->prev() == NULL, "list invariant"); + assert(tail() == NULL || tail()->next() == NULL, "list invariant"); + assert(head() == NULL || head()->size() == size(), "wrong item on list"); + assert(tail() == NULL || tail()->size() == size(), "wrong item on list"); +} + +void FreeList::returnChunkAtTail(FreeChunk* chunk) { + returnChunkAtTail(chunk, true); +} + +void FreeList::prepend(FreeList* fl) { + assert_proper_lock_protection(); + if (fl->count() > 0) { + if (count() == 0) { + set_head(fl->head()); + set_tail(fl->tail()); + set_count(fl->count()); + } else { + // Both are non-empty. + FreeChunk* fl_tail = fl->tail(); + FreeChunk* this_head = head(); + assert(fl_tail->next() == NULL, "Well-formedness of fl"); + fl_tail->linkNext(this_head); + this_head->linkPrev(fl_tail); + set_head(fl->head()); + set_count(count() + fl->count()); + } + fl->set_head(NULL); + fl->set_tail(NULL); + fl->set_count(0); + } +} + +// verifyChunkInFreeLists() is used to verify that an item is in this free list. +// It is used as a debugging aid. +bool FreeList::verifyChunkInFreeLists(FreeChunk* fc) const { + // This is an internal consistency check, not part of the check that the + // chunk is in the free lists. + guarantee(fc->size() == size(), "Wrong list is being searched"); + FreeChunk* curFC = head(); + while (curFC) { + // This is an internal consistency check. + guarantee(size() == curFC->size(), "Chunk is in wrong list."); + if (fc == curFC) { + return true; + } + curFC = curFC->next(); + } + return false; +} + +#ifndef PRODUCT +void FreeList::assert_proper_lock_protection_work() const { +#ifdef ASSERT + if (_protecting_lock != NULL && + SharedHeap::heap()->n_par_threads() > 0) { + // Should become an assert. + guarantee(_protecting_lock->owned_by_self(), "FreeList RACE DETECTED"); + } +#endif +} +#endif diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeList.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeList.hpp new file mode 100644 index 00000000000..4d4c1362a20 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeList.hpp @@ -0,0 +1,301 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class CompactibleFreeListSpace; + +// A class for maintaining a free list of FreeChunk's. The FreeList +// maintains a the structure of the list (head, tail, etc.) plus +// statistics for allocations from the list. The links between items +// are not part of FreeList. The statistics are +// used to make decisions about coalescing FreeChunk's when they +// are swept during collection. +// +// See the corresponding .cpp file for a description of the specifics +// for that implementation. + +class Mutex; + +class FreeList VALUE_OBJ_CLASS_SPEC { + friend class CompactibleFreeListSpace; + FreeChunk* _head; // List of free chunks + FreeChunk* _tail; // Tail of list of free chunks + size_t _size; // Size in Heap words of each chunks + ssize_t _count; // Number of entries in list + size_t _hint; // next larger size list with a positive surplus + + AllocationStats _allocation_stats; // statistics for smart allocation + +#ifdef ASSERT + Mutex* _protecting_lock; +#endif + + // Asserts false if the protecting lock (if any) is not held. + void assert_proper_lock_protection_work() const PRODUCT_RETURN; + void assert_proper_lock_protection() const { +#ifdef ASSERT + if (_protecting_lock != NULL) + assert_proper_lock_protection_work(); +#endif + } + + // Initialize the allocation statistics. + protected: + void init_statistics(); + void set_count(ssize_t v) { _count = v;} + void increment_count() { _count++; } + void decrement_count() { + _count--; + assert(_count >= 0, "Count should not be negative"); } + + public: + // Constructor + // Construct a list without any entries. + FreeList(); + // Construct a list with "fc" as the first (and lone) entry in the list. + FreeList(FreeChunk* fc); + // Construct a list which will have a FreeChunk at address "addr" and + // of size "size" as the first (and lone) entry in the list. + FreeList(HeapWord* addr, size_t size); + + // Reset the head, tail, hint, and count of a free list. + void reset(size_t hint); + + // Declare the current free list to be protected by the given lock. +#ifdef ASSERT + void set_protecting_lock(Mutex* protecting_lock) { + _protecting_lock = protecting_lock; + } +#endif + + // Accessors. + FreeChunk* head() const { + assert_proper_lock_protection(); + return _head; + } + void set_head(FreeChunk* v) { + assert_proper_lock_protection(); + _head = v; + assert(!_head || _head->size() == _size, "bad chunk size"); + } + // Set the head of the list and set the prev field of non-null + // values to NULL. + void link_head(FreeChunk* v) { + assert_proper_lock_protection(); + set_head(v); + // If this method is not used (just set the head instead), + // this check can be avoided. + if (v != NULL) { + v->linkPrev(NULL); + } + } + + FreeChunk* tail() const { + assert_proper_lock_protection(); + return _tail; + } + void set_tail(FreeChunk* v) { + assert_proper_lock_protection(); + _tail = v; + assert(!_tail || _tail->size() == _size, "bad chunk size"); + } + // Set the tail of the list and set the next field of non-null + // values to NULL. + void link_tail(FreeChunk* v) { + assert_proper_lock_protection(); + set_tail(v); + if (v != NULL) { + v->clearNext(); + } + } + + // No locking checks in read-accessors: lock-free reads (only) are benign. + // Readers are expected to have the lock if they are doing work that + // requires atomicity guarantees in sections of code. + size_t size() const { + return _size; + } + void set_size(size_t v) { + assert_proper_lock_protection(); + _size = v; + } + ssize_t count() const { + return _count; + } + size_t hint() const { + return _hint; + } + void set_hint(size_t v) { + assert_proper_lock_protection(); + assert(v == 0 || _size < v, "Bad hint"); _hint = v; + } + + // Accessors for statistics + AllocationStats* allocation_stats() { + assert_proper_lock_protection(); + return &_allocation_stats; + } + + ssize_t desired() const { + return _allocation_stats.desired(); + } + void compute_desired(float inter_sweep_current, + float inter_sweep_estimate) { + assert_proper_lock_protection(); + _allocation_stats.compute_desired(_count, + inter_sweep_current, + inter_sweep_estimate); + } + ssize_t coalDesired() const { + return _allocation_stats.coalDesired(); + } + void set_coalDesired(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_coalDesired(v); + } + + ssize_t surplus() const { + return _allocation_stats.surplus(); + } + void set_surplus(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_surplus(v); + } + void increment_surplus() { + assert_proper_lock_protection(); + _allocation_stats.increment_surplus(); + } + void decrement_surplus() { + assert_proper_lock_protection(); + _allocation_stats.decrement_surplus(); + } + + ssize_t bfrSurp() const { + return _allocation_stats.bfrSurp(); + } + void set_bfrSurp(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_bfrSurp(v); + } + ssize_t prevSweep() const { + return _allocation_stats.prevSweep(); + } + void set_prevSweep(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_prevSweep(v); + } + ssize_t beforeSweep() const { + return _allocation_stats.beforeSweep(); + } + void set_beforeSweep(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_beforeSweep(v); + } + + ssize_t coalBirths() const { + return _allocation_stats.coalBirths(); + } + void set_coalBirths(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_coalBirths(v); + } + void increment_coalBirths() { + assert_proper_lock_protection(); + _allocation_stats.increment_coalBirths(); + } + + ssize_t coalDeaths() const { + return _allocation_stats.coalDeaths(); + } + void set_coalDeaths(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_coalDeaths(v); + } + void increment_coalDeaths() { + assert_proper_lock_protection(); + _allocation_stats.increment_coalDeaths(); + } + + ssize_t splitBirths() const { + return _allocation_stats.splitBirths(); + } + void set_splitBirths(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_splitBirths(v); + } + void increment_splitBirths() { + assert_proper_lock_protection(); + _allocation_stats.increment_splitBirths(); + } + + ssize_t splitDeaths() const { + return _allocation_stats.splitDeaths(); + } + void set_splitDeaths(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_splitDeaths(v); + } + void increment_splitDeaths() { + assert_proper_lock_protection(); + _allocation_stats.increment_splitDeaths(); + } + + NOT_PRODUCT( + // For debugging. The "_returnedBytes" in all the lists are summed + // and compared with the total number of bytes swept during a + // collection. + size_t returnedBytes() const { return _allocation_stats.returnedBytes(); } + void set_returnedBytes(size_t v) { _allocation_stats.set_returnedBytes(v); } + void increment_returnedBytes_by(size_t v) { + _allocation_stats.set_returnedBytes(_allocation_stats.returnedBytes() + v); + } + ) + + // Unlink head of list and return it. Returns NULL if + // the list is empty. + FreeChunk* getChunkAtHead(); + + // Remove the first "n" or "count", whichever is smaller, chunks from the + // list, setting "fl", which is required to be empty, to point to them. + void getFirstNChunksFromList(size_t n, FreeList* fl); + + // Unlink this chunk from it's free list + void removeChunk(FreeChunk* fc); + + // Add this chunk to this free list. + void returnChunkAtHead(FreeChunk* fc); + void returnChunkAtTail(FreeChunk* fc); + + // Similar to returnChunk* but also records some diagnostic + // information. + void returnChunkAtHead(FreeChunk* fc, bool record_return); + void returnChunkAtTail(FreeChunk* fc, bool record_return); + + // Prepend "fl" (whose size is required to be the same as that of "this") + // to the front of "this" list. + void prepend(FreeList* fl); + + // Verify that the chunk is in the list. + // found. Return NULL if "fc" is not found. + bool verifyChunkInFreeLists(FreeChunk* fc) const; +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.cpp new file mode 100644 index 00000000000..8d2ce7f19f0 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.cpp @@ -0,0 +1,250 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +# include "incls/_precompiled.incl" +# include "incls/_vmCMSOperations.cpp.incl" + +HS_DTRACE_PROBE_DECL(hs_private, cms__initmark__begin); +HS_DTRACE_PROBE_DECL(hs_private, cms__initmark__end); + +HS_DTRACE_PROBE_DECL(hs_private, cms__remark__begin); +HS_DTRACE_PROBE_DECL(hs_private, cms__remark__end); + +////////////////////////////////////////////////////////// +// Methods in abstract class VM_CMS_Operation +////////////////////////////////////////////////////////// +void VM_CMS_Operation::acquire_pending_list_lock() { + // The caller may block while communicating + // with the SLT thread in order to acquire/release the PLL. + ConcurrentMarkSweepThread::slt()-> + manipulatePLL(SurrogateLockerThread::acquirePLL); +} + +void VM_CMS_Operation::release_and_notify_pending_list_lock() { + // The caller may block while communicating + // with the SLT thread in order to acquire/release the PLL. + ConcurrentMarkSweepThread::slt()-> + manipulatePLL(SurrogateLockerThread::releaseAndNotifyPLL); +} + +void VM_CMS_Operation::verify_before_gc() { + if (VerifyBeforeGC && + GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { + HandleMark hm; + FreelistLocker x(_collector); + MutexLockerEx y(_collector->bitMapLock(), Mutex::_no_safepoint_check_flag); + Universe::heap()->prepare_for_verify(); + Universe::verify(true); + } +} + +void VM_CMS_Operation::verify_after_gc() { + if (VerifyAfterGC && + GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { + HandleMark hm; + FreelistLocker x(_collector); + MutexLockerEx y(_collector->bitMapLock(), Mutex::_no_safepoint_check_flag); + Universe::verify(true); + } +} + +bool VM_CMS_Operation::lost_race() const { + if (CMSCollector::abstract_state() == CMSCollector::Idling) { + // We lost a race to a foreground collection + // -- there's nothing to do + return true; + } + assert(CMSCollector::abstract_state() == legal_state(), + "Inconsistent collector state?"); + return false; +} + +bool VM_CMS_Operation::doit_prologue() { + assert(Thread::current()->is_ConcurrentGC_thread(), "just checking"); + assert(!CMSCollector::foregroundGCShouldWait(), "Possible deadlock"); + assert(!ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "Possible deadlock"); + + if (needs_pll()) { + acquire_pending_list_lock(); + } + // Get the Heap_lock after the pending_list_lock. + Heap_lock->lock(); + if (lost_race()) { + assert(_prologue_succeeded == false, "Initialized in c'tor"); + Heap_lock->unlock(); + if (needs_pll()) { + release_and_notify_pending_list_lock(); + } + } else { + _prologue_succeeded = true; + } + return _prologue_succeeded; +} + +void VM_CMS_Operation::doit_epilogue() { + assert(Thread::current()->is_ConcurrentGC_thread(), "just checking"); + assert(!CMSCollector::foregroundGCShouldWait(), "Possible deadlock"); + assert(!ConcurrentMarkSweepThread::cms_thread_has_cms_token(), + "Possible deadlock"); + + // Release the Heap_lock first. + Heap_lock->unlock(); + if (needs_pll()) { + release_and_notify_pending_list_lock(); + } +} + +////////////////////////////////////////////////////////// +// Methods in class VM_CMS_Initial_Mark +////////////////////////////////////////////////////////// +void VM_CMS_Initial_Mark::doit() { + if (lost_race()) { + // Nothing to do. + return; + } + HS_DTRACE_PROBE(hs_private, cms__initmark__begin); + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + GCCauseSetter gccs(gch, GCCause::_cms_initial_mark); + + VM_CMS_Operation::verify_before_gc(); + + IsGCActiveMark x; // stop-world GC active + _collector->do_CMS_operation(CMSCollector::CMS_op_checkpointRootsInitial); + + VM_CMS_Operation::verify_after_gc(); + HS_DTRACE_PROBE(hs_private, cms__initmark__end); +} + +////////////////////////////////////////////////////////// +// Methods in class VM_CMS_Final_Remark_Operation +////////////////////////////////////////////////////////// +void VM_CMS_Final_Remark::doit() { + if (lost_race()) { + // Nothing to do. + return; + } + HS_DTRACE_PROBE(hs_private, cms__remark__begin); + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + GCCauseSetter gccs(gch, GCCause::_cms_final_remark); + + VM_CMS_Operation::verify_before_gc(); + + IsGCActiveMark x; // stop-world GC active + _collector->do_CMS_operation(CMSCollector::CMS_op_checkpointRootsFinal); + + VM_CMS_Operation::verify_after_gc(); + HS_DTRACE_PROBE(hs_private, cms__remark__end); +} + +// VM operation to invoke a concurrent collection of a +// GenCollectedHeap heap. +void VM_GenCollectFullConcurrent::doit() { + assert(Thread::current()->is_VM_thread(), "Should be VM thread"); + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + if (_gc_count_before == gch->total_collections()) { + // The "full" of do_full_collection call below "forces" + // a collection; the second arg, 0, below ensures that + // only the young gen is collected. XXX In the future, + // we'll probably need to have something in this interface + // to say do this only if we are sure we will not bail + // out to a full collection in this attempt, but that's + // for the future. + assert(SafepointSynchronize::is_at_safepoint(), + "We can only be executing this arm of if at a safepoint"); + GCCauseSetter gccs(gch, _gc_cause); + gch->do_full_collection(gch->must_clear_all_soft_refs(), + 0 /* collect only youngest gen */); + } // Else no need for a foreground young gc + assert((_gc_count_before < gch->total_collections()) || + (GC_locker::is_active() /* gc may have been skipped */ + && (_gc_count_before == gch->total_collections())), + "total_collections() should be monotonically increasing"); + + MutexLockerEx x(FullGCCount_lock, Mutex::_no_safepoint_check_flag); + if (gch->total_full_collections() == _full_gc_count_before) { + // Disable iCMS until the full collection is done. + CMSCollector::disable_icms(); + // In case CMS thread was in icms_wait(), wake it up. + CMSCollector::start_icms(); + // Nudge the CMS thread to start a concurrent collection + CMSCollector::request_full_gc(_full_gc_count_before); + } else { + FullGCCount_lock->notify_all(); // Inform the Java thread its work is done + } +} + +bool VM_GenCollectFullConcurrent::evaluate_at_safepoint() const { + Thread* thr = Thread::current(); + assert(thr != NULL, "Unexpected tid"); + if (!thr->is_Java_thread()) { + assert(thr->is_VM_thread(), "Expected to be evaluated by VM thread"); + GenCollectedHeap* gch = GenCollectedHeap::heap(); + if (_gc_count_before != gch->total_collections()) { + // No need to do a young gc, we'll just nudge the CMS thread + // in the doit() method above, to be executed soon. + assert(_gc_count_before < gch->total_collections(), + "total_collections() should be monotnically increasing"); + return false; // no need for foreground young gc + } + } + return true; // may still need foreground young gc +} + + +void VM_GenCollectFullConcurrent::doit_epilogue() { + Thread* thr = Thread::current(); + assert(thr->is_Java_thread(), "just checking"); + JavaThread* jt = (JavaThread*)thr; + // Release the Heap_lock first. + Heap_lock->unlock(); + release_and_notify_pending_list_lock(); + + // It is fine to test whether completed collections has + // exceeded our request count without locking because + // the completion count is monotonically increasing; + // this will break for very long-running apps when the + // count overflows and wraps around. XXX fix me !!! + // e.g. at the rate of 1 full gc per ms, this could + // overflow in about 1000 years. + GenCollectedHeap* gch = GenCollectedHeap::heap(); + if (gch->total_full_collections_completed() <= _full_gc_count_before) { + // Now, wait for witnessing concurrent gc cycle to complete, + // but do so in native mode, because we want to lock the + // FullGCEvent_lock, which may be needed by the VM thread + // or by the CMS thread, so we do not want to be suspended + // while holding that lock. + ThreadToNativeFromVM native(jt); + MutexLockerEx ml(FullGCCount_lock, Mutex::_no_safepoint_check_flag); + // Either a concurrent or a stop-world full gc is sufficient + // witness to our request. + while (gch->total_full_collections_completed() <= _full_gc_count_before) { + FullGCCount_lock->wait(Mutex::_no_safepoint_check_flag); + } + } + // Enable iCMS back. + CMSCollector::enable_icms(); +} diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.hpp new file mode 100644 index 00000000000..0ed06fba99c --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.hpp @@ -0,0 +1,139 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The VM_CMS_Operation is slightly different from +// a VM_GC_Operation -- and would not have subclassed easily +// to VM_GC_Operation without several changes to VM_GC_Operation. +// To minimize the changes, we have replicated some of the VM_GC_Operation +// functionality here. We will consolidate that back by doing subclassing +// as appropriate in Dolphin. +// +// VM_Operation +// VM_CMS_Operation +// - implements the common portion of work done in support +// of CMS' stop-world phases (initial mark and remark). +// +// VM_CMS_Initial_Mark +// VM_CMS_Final_Mark +// + +// Forward decl. +class CMSCollector; + +class VM_CMS_Operation: public VM_Operation { + protected: + CMSCollector* _collector; // associated collector + bool _prologue_succeeded; // whether doit_prologue succeeded + + bool lost_race() const; + + // java.lang.ref.Reference support + void acquire_pending_list_lock(); + void release_and_notify_pending_list_lock(); + + public: + VM_CMS_Operation(CMSCollector* collector): + _collector(collector), + _prologue_succeeded(false) {} + ~VM_CMS_Operation() {} + + // The legal collector state for executing this CMS op. + virtual const CMSCollector::CollectorState legal_state() const = 0; + + // Whether the pending list lock needs to be held + virtual const bool needs_pll() const = 0; + + // Execute operations in the context of the caller, + // prior to execution of the vm operation itself. + virtual bool doit_prologue(); + // Execute operations in the context of the caller, + // following completion of the vm operation. + virtual void doit_epilogue(); + + virtual bool evaluate_at_safepoint() const { return true; } + virtual bool is_cheap_allocated() const { return false; } + virtual bool allow_nested_vm_operations() const { return false; } + bool prologue_succeeded() const { return _prologue_succeeded; } + + void verify_before_gc(); + void verify_after_gc(); +}; + + +// VM_CMS_Operation for the initial marking phase of CMS. +class VM_CMS_Initial_Mark: public VM_CMS_Operation { + public: + VM_CMS_Initial_Mark(CMSCollector* _collector) : + VM_CMS_Operation(_collector) {} + + virtual VMOp_Type type() const { return VMOp_CMS_Initial_Mark; } + virtual void doit(); + + virtual const CMSCollector::CollectorState legal_state() const { + return CMSCollector::InitialMarking; + } + + virtual const bool needs_pll() const { + return false; + } +}; + +// VM_CMS_Operation for the final remark phase of CMS. +class VM_CMS_Final_Remark: public VM_CMS_Operation { + public: + VM_CMS_Final_Remark(CMSCollector* _collector) : + VM_CMS_Operation(_collector) {} + virtual VMOp_Type type() const { return VMOp_CMS_Final_Remark; } + virtual void doit(); + + virtual const CMSCollector::CollectorState legal_state() const { + return CMSCollector::FinalMarking; + } + + virtual const bool needs_pll() const { + return true; + } +}; + + +// VM operation to invoke a concurrent collection of the heap as a +// GenCollectedHeap heap. +class VM_GenCollectFullConcurrent: public VM_GC_Operation { + public: + VM_GenCollectFullConcurrent(unsigned int gc_count_before, + unsigned int full_gc_count_before, + GCCause::Cause gc_cause) + : VM_GC_Operation(gc_count_before, full_gc_count_before, true /* full */) { + _gc_cause = gc_cause; + assert(FullGCCount_lock != NULL && UseConcMarkSweepGC && + ExplicitGCInvokesConcurrent, "Otherwise shouldn't be here"); + assert(UseAsyncConcMarkSweepGC, "Else will hang caller"); + } + ~VM_GenCollectFullConcurrent() {} + virtual VMOp_Type type() const { return VMOp_GenCollectFullConcurrent; } + virtual void doit(); + virtual void doit_epilogue(); + virtual bool is_cheap_allocated() const { return false; } + virtual bool evaluate_at_safepoint() const; +}; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/vmStructs_cms.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/vmStructs_cms.hpp new file mode 100644 index 00000000000..bce90cab897 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/vmStructs_cms.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#define VM_STRUCTS_CMS(nonstatic_field, \ + static_field) \ + nonstatic_field(CompactibleFreeListSpace, _collector, CMSCollector*) \ + nonstatic_field(CompactibleFreeListSpace, _bt, BlockOffsetArrayNonContigSpace) \ + \ + nonstatic_field(CMSPermGen, _gen, ConcurrentMarkSweepGeneration*) \ + nonstatic_field(CMSBitMap, _bmStartWord, HeapWord*) \ + nonstatic_field(CMSBitMap, _bmWordSize, size_t) \ + nonstatic_field(CMSBitMap, _shifter, const int) \ + nonstatic_field(CMSBitMap, _bm, BitMap) \ + nonstatic_field(CMSBitMap, _virtual_space, VirtualSpace) \ + nonstatic_field(CMSCollector, _markBitMap, CMSBitMap) \ + nonstatic_field(ConcurrentMarkSweepGeneration, _cmsSpace, CompactibleFreeListSpace*) \ + static_field(ConcurrentMarkSweepThread, _collector, CMSCollector*) \ + nonstatic_field(FreeChunk, _next, FreeChunk*) \ + nonstatic_field(FreeChunk, _prev, FreeChunk*) \ + nonstatic_field(FreeChunk, _size, size_t) + +#define VM_TYPES_CMS(declare_type, \ + declare_toplevel_type) \ + \ + declare_type(ConcurrentMarkSweepGeneration,CardGeneration) \ + declare_type(CompactibleFreeListSpace, CompactibleSpace) \ + declare_type(CMSPermGenGen, ConcurrentMarkSweepGeneration) \ + declare_type(CMSPermGen, PermGen) \ + declare_type(ConcurrentMarkSweepThread, NamedThread) \ + declare_type(SurrogateLockerThread, JavaThread) \ + declare_toplevel_type(CMSCollector) \ + declare_toplevel_type(CMSBitMap) \ + declare_toplevel_type(FreeChunk) \ + declare_toplevel_type(ConcurrentMarkSweepThread*) \ + declare_toplevel_type(ConcurrentMarkSweepGeneration*) \ + declare_toplevel_type(SurrogateLockerThread*) \ + declare_toplevel_type(CompactibleFreeListSpace*) \ + declare_toplevel_type(CMSCollector*) \ + declare_toplevel_type(FreeChunk*) + +#define VM_INT_CONSTANTS_CMS(declare_constant) \ + declare_constant(Generation::ConcurrentMarkSweep) \ + declare_constant(PermGen::ConcurrentMarkSweep) diff --git a/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep b/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep new file mode 100644 index 00000000000..f715c15c0b0 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep @@ -0,0 +1,240 @@ +// +// Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// NOTE: DO NOT CHANGE THIS COPYRIGHT TO NEW STYLE - IT WILL BREAK makeDeps! + +binaryTreeDictionary.cpp allocationStats.hpp +binaryTreeDictionary.cpp binaryTreeDictionary.hpp +binaryTreeDictionary.cpp globals.hpp +binaryTreeDictionary.cpp ostream.hpp + +binaryTreeDictionary.hpp freeBlockDictionary.hpp +binaryTreeDictionary.hpp freeList.hpp + +cmsAdaptiveSizePolicy.cpp cmsAdaptiveSizePolicy.hpp +cmsAdaptiveSizePolicy.cpp defNewGeneration.hpp +cmsAdaptiveSizePolicy.cpp gcStats.hpp +cmsAdaptiveSizePolicy.cpp genCollectedHeap.hpp +cmsAdaptiveSizePolicy.cpp thread.hpp +cmsAdaptiveSizePolicy.cpp os_.inline.hpp + +cmsAdaptiveSizePolicy.hpp adaptiveSizePolicy.hpp +cmsAdaptiveSizePolicy.hpp timer.hpp + +cmsCollectorPolicy.cpp arguments.hpp +cmsCollectorPolicy.cpp cardTableRS.hpp +cmsCollectorPolicy.cpp cmsAdaptiveSizePolicy.hpp +cmsCollectorPolicy.cpp cmsGCAdaptivePolicyCounters.hpp +cmsCollectorPolicy.cpp cmsCollectorPolicy.hpp +cmsCollectorPolicy.cpp collectorPolicy.hpp +cmsCollectorPolicy.cpp gcLocker.inline.hpp +cmsCollectorPolicy.cpp genCollectedHeap.hpp +cmsCollectorPolicy.cpp gcPolicyCounters.hpp +cmsCollectorPolicy.cpp generationSpec.hpp +cmsCollectorPolicy.cpp globals_extension.hpp +cmsCollectorPolicy.cpp handles.inline.hpp +cmsCollectorPolicy.cpp java.hpp +cmsCollectorPolicy.cpp parNewGeneration.hpp +cmsCollectorPolicy.cpp space.hpp +cmsCollectorPolicy.cpp thread_.inline.hpp +cmsCollectorPolicy.cpp universe.hpp +cmsCollectorPolicy.cpp vmGCOperations.hpp +cmsCollectorPolicy.cpp vmThread.hpp + +cmsCollectorPolicy.hpp collectorPolicy.hpp + +cmsGCAdaptivePolicyCounters.cpp cmsGCAdaptivePolicyCounters.hpp +cmsGCAdaptivePolicyCounters.cpp resourceArea.hpp + +cmsGCAdaptivePolicyCounters.hpp cmsAdaptiveSizePolicy.hpp +cmsGCAdaptivePolicyCounters.hpp gcAdaptivePolicyCounters.hpp +cmsGCAdaptivePolicyCounters.hpp gcStats.hpp +cmsGCAdaptivePolicyCounters.hpp perfData.hpp + +cmsLockVerifier.cpp cmsLockVerifier.hpp +cmsLockVerifier.cpp concurrentMarkSweepThread.hpp +cmsLockVerifier.cpp vmThread.hpp + +cmsLockVerifier.hpp mutex.hpp + +cmsOopClosures.hpp genOopClosures.hpp + +cmsOopClosures.inline.hpp cmsOopClosures.hpp +cmsOopClosures.inline.hpp concurrentMarkSweepGeneration.hpp + +cmsPermGen.cpp blockOffsetTable.hpp +cmsPermGen.cpp cSpaceCounters.hpp +cmsPermGen.cpp cmsPermGen.hpp +cmsPermGen.cpp collectedHeap.inline.hpp +cmsPermGen.cpp compactPermGen.hpp +cmsPermGen.cpp concurrentMarkSweepGeneration.inline.hpp +cmsPermGen.cpp genCollectedHeap.hpp +cmsPermGen.cpp generation.inline.hpp +cmsPermGen.cpp java.hpp +cmsPermGen.cpp oop.inline.hpp +cmsPermGen.cpp permGen.hpp +cmsPermGen.cpp universe.hpp + +cmsPermGen.hpp concurrentMarkSweepGeneration.hpp +cmsPermGen.hpp permGen.hpp + +compactibleFreeListSpace.cpp allocation.inline.hpp +compactibleFreeListSpace.cpp blockOffsetTable.inline.hpp +compactibleFreeListSpace.cpp cmsLockVerifier.hpp +compactibleFreeListSpace.cpp collectedHeap.hpp +compactibleFreeListSpace.cpp compactibleFreeListSpace.hpp +compactibleFreeListSpace.cpp concurrentMarkSweepGeneration.inline.hpp +compactibleFreeListSpace.cpp concurrentMarkSweepThread.hpp +compactibleFreeListSpace.cpp copy.hpp +compactibleFreeListSpace.cpp globals.hpp +compactibleFreeListSpace.cpp handles.inline.hpp +compactibleFreeListSpace.cpp init.hpp +compactibleFreeListSpace.cpp java.hpp +compactibleFreeListSpace.cpp liveRange.hpp +compactibleFreeListSpace.cpp oop.inline.hpp +compactibleFreeListSpace.cpp resourceArea.hpp +compactibleFreeListSpace.cpp universe.inline.hpp +compactibleFreeListSpace.cpp vmThread.hpp + +compactibleFreeListSpace.hpp binaryTreeDictionary.hpp +compactibleFreeListSpace.hpp freeList.hpp +compactibleFreeListSpace.hpp space.hpp + +compactingPermGenGen.cpp concurrentMarkSweepGeneration.inline.hpp + +concurrentGCThread.cpp concurrentGCThread.hpp +concurrentGCThread.cpp init.hpp +concurrentGCThread.cpp instanceRefKlass.hpp +concurrentGCThread.cpp interfaceSupport.hpp +concurrentGCThread.cpp java.hpp +concurrentGCThread.cpp javaCalls.hpp +concurrentGCThread.cpp oop.inline.hpp +concurrentGCThread.cpp systemDictionary.hpp + +concurrentGCThread.hpp thread.hpp + +concurrentMarkSweepGeneration.cpp cardTableRS.hpp +concurrentMarkSweepGeneration.cpp cmsAdaptiveSizePolicy.hpp +concurrentMarkSweepGeneration.cpp cmsCollectorPolicy.hpp +concurrentMarkSweepGeneration.cpp cmsGCAdaptivePolicyCounters.hpp +concurrentMarkSweepGeneration.cpp cmsOopClosures.inline.hpp +concurrentMarkSweepGeneration.cpp codeCache.hpp +concurrentMarkSweepGeneration.cpp collectedHeap.inline.hpp +concurrentMarkSweepGeneration.cpp collectorCounters.hpp +concurrentMarkSweepGeneration.cpp collectorPolicy.hpp +concurrentMarkSweepGeneration.cpp compactibleFreeListSpace.hpp +concurrentMarkSweepGeneration.cpp concurrentMarkSweepGeneration.inline.hpp +concurrentMarkSweepGeneration.cpp concurrentMarkSweepThread.hpp +concurrentMarkSweepGeneration.cpp gcLocker.inline.hpp +concurrentMarkSweepGeneration.cpp genCollectedHeap.hpp +concurrentMarkSweepGeneration.cpp genMarkSweep.hpp +concurrentMarkSweepGeneration.cpp genOopClosures.inline.hpp +concurrentMarkSweepGeneration.cpp globals_extension.hpp +concurrentMarkSweepGeneration.cpp handles.inline.hpp +concurrentMarkSweepGeneration.cpp isGCActiveMark.hpp +concurrentMarkSweepGeneration.cpp java.hpp +concurrentMarkSweepGeneration.cpp jvmtiExport.hpp +concurrentMarkSweepGeneration.cpp oop.inline.hpp +concurrentMarkSweepGeneration.cpp parNewGeneration.hpp +concurrentMarkSweepGeneration.cpp referencePolicy.hpp +concurrentMarkSweepGeneration.cpp resourceArea.hpp +concurrentMarkSweepGeneration.cpp runtimeService.hpp +concurrentMarkSweepGeneration.cpp symbolTable.hpp +concurrentMarkSweepGeneration.cpp systemDictionary.hpp +concurrentMarkSweepGeneration.cpp vmCMSOperations.hpp +concurrentMarkSweepGeneration.cpp vmThread.hpp + +concurrentMarkSweepGeneration.hpp bitMap.hpp +concurrentMarkSweepGeneration.hpp freeBlockDictionary.hpp +concurrentMarkSweepGeneration.hpp gSpaceCounters.hpp +concurrentMarkSweepGeneration.hpp gcStats.hpp +concurrentMarkSweepGeneration.hpp generation.hpp +concurrentMarkSweepGeneration.hpp generationCounters.hpp +concurrentMarkSweepGeneration.hpp mutexLocker.hpp +concurrentMarkSweepGeneration.hpp taskqueue.hpp +concurrentMarkSweepGeneration.hpp virtualspace.hpp +concurrentMarkSweepGeneration.hpp yieldingWorkgroup.hpp + +concurrentMarkSweepGeneration.inline.hpp cmsLockVerifier.hpp +concurrentMarkSweepGeneration.inline.hpp compactibleFreeListSpace.hpp +concurrentMarkSweepGeneration.inline.hpp concurrentMarkSweepGeneration.hpp +concurrentMarkSweepGeneration.inline.hpp concurrentMarkSweepThread.hpp +concurrentMarkSweepGeneration.inline.hpp defNewGeneration.hpp +concurrentMarkSweepGeneration.inline.hpp gcUtil.hpp + +concurrentMarkSweepThread.cpp concurrentMarkSweepGeneration.inline.hpp +concurrentMarkSweepThread.cpp concurrentMarkSweepThread.hpp +concurrentMarkSweepThread.cpp genCollectedHeap.hpp +concurrentMarkSweepThread.cpp init.hpp +concurrentMarkSweepThread.cpp instanceRefKlass.hpp +concurrentMarkSweepThread.cpp interfaceSupport.hpp +concurrentMarkSweepThread.cpp java.hpp +concurrentMarkSweepThread.cpp javaCalls.hpp +concurrentMarkSweepThread.cpp mutexLocker.hpp +concurrentMarkSweepThread.cpp oop.inline.hpp +concurrentMarkSweepThread.cpp os.hpp +concurrentMarkSweepThread.cpp systemDictionary.hpp +concurrentMarkSweepThread.cpp vmThread.hpp + +concurrentMarkSweepThread.hpp concurrentGCThread.hpp +concurrentMarkSweepThread.hpp concurrentMarkSweepGeneration.hpp +concurrentMarkSweepThread.hpp thread_.inline.hpp + +freeBlockDictionary.cpp freeBlockDictionary.hpp +freeBlockDictionary.cpp thread_.inline.hpp + +freeBlockDictionary.hpp allocation.hpp +freeBlockDictionary.hpp debug.hpp +freeBlockDictionary.hpp globalDefinitions.hpp +freeBlockDictionary.hpp memRegion.hpp +freeBlockDictionary.hpp mutex.hpp +freeBlockDictionary.hpp ostream.hpp + +freeChunk.cpp copy.hpp +freeChunk.cpp freeBlockDictionary.hpp + +freeList.cpp freeBlockDictionary.hpp +freeList.cpp freeList.hpp +freeList.cpp globals.hpp +freeList.cpp mutex.hpp +freeList.cpp sharedHeap.hpp + +freeList.hpp allocationStats.hpp + +vmCMSOperations.cpp concurrentMarkSweepGeneration.inline.hpp +vmCMSOperations.cpp concurrentMarkSweepThread.hpp +vmCMSOperations.cpp dtrace.hpp +vmCMSOperations.cpp gcLocker.inline.hpp +vmCMSOperations.cpp isGCActiveMark.hpp +vmCMSOperations.cpp interfaceSupport.hpp +vmCMSOperations.cpp vmCMSOperations.hpp + +vmCMSOperations.hpp concurrentMarkSweepGeneration.hpp +vmCMSOperations.hpp gcCause.hpp +vmCMSOperations.hpp vm_operations.hpp +vmCMSOperations.hpp vmGCOperations.hpp + +yieldingWorkgroup.cpp yieldingWorkgroup.hpp + +yieldingWorkgroup.hpp workgroup.hpp diff --git a/hotspot/src/share/vm/gc_implementation/includeDB_gc_parNew b/hotspot/src/share/vm/gc_implementation/includeDB_gc_parNew new file mode 100644 index 00000000000..d0014f35822 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_parNew @@ -0,0 +1,82 @@ +// +// Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +asParNewGeneration.hpp adaptiveSizePolicy.hpp +asParNewGeneration.hpp parNewGeneration.hpp + +asParNewGeneration.cpp asParNewGeneration.hpp +asParNewGeneration.cpp cmsAdaptiveSizePolicy.hpp +asParNewGeneration.cpp cmsGCAdaptivePolicyCounters.hpp +asParNewGeneration.cpp defNewGeneration.inline.hpp +asParNewGeneration.cpp oop.pcgc.inline.hpp +asParNewGeneration.cpp parNewGeneration.hpp +asParNewGeneration.cpp referencePolicy.hpp + +parCardTableModRefBS.cpp allocation.inline.hpp +parCardTableModRefBS.cpp cardTableModRefBS.hpp +parCardTableModRefBS.cpp cardTableRS.hpp +parCardTableModRefBS.cpp java.hpp +parCardTableModRefBS.cpp mutexLocker.hpp +parCardTableModRefBS.cpp sharedHeap.hpp +parCardTableModRefBS.cpp space.hpp +parCardTableModRefBS.cpp universe.hpp +parCardTableModRefBS.cpp virtualspace.hpp + +parGCAllocBuffer.cpp arrayOop.hpp +parGCAllocBuffer.cpp oop.inline.hpp +parGCAllocBuffer.cpp parGCAllocBuffer.hpp +parGCAllocBuffer.cpp sharedHeap.hpp + +parGCAllocBuffer.hpp allocation.hpp +parGCAllocBuffer.hpp globalDefinitions.hpp +parGCAllocBuffer.hpp threadLocalAllocBuffer.hpp + +parNewGeneration.cpp adaptiveSizePolicy.hpp +parNewGeneration.cpp ageTable.hpp +parNewGeneration.cpp concurrentMarkSweepGeneration.hpp +parNewGeneration.cpp copy.hpp +parNewGeneration.cpp defNewGeneration.inline.hpp +parNewGeneration.cpp genCollectedHeap.hpp +parNewGeneration.cpp genOopClosures.inline.hpp +parNewGeneration.cpp generation.hpp +parNewGeneration.cpp generation.inline.hpp +parNewGeneration.cpp globalDefinitions.hpp +parNewGeneration.cpp handles.hpp +parNewGeneration.cpp handles.inline.hpp +parNewGeneration.cpp java.hpp +parNewGeneration.cpp objArrayOop.hpp +parNewGeneration.cpp oop.pcgc.inline.hpp +parNewGeneration.cpp oop.inline.hpp +parNewGeneration.cpp parGCAllocBuffer.hpp +parNewGeneration.cpp parNewGeneration.hpp +parNewGeneration.cpp parOopClosures.inline.hpp +parNewGeneration.cpp referencePolicy.hpp +parNewGeneration.cpp resourceArea.hpp +parNewGeneration.cpp sharedHeap.hpp +parNewGeneration.cpp space.hpp +parNewGeneration.cpp workgroup.hpp + +parNewGeneration.hpp defNewGeneration.hpp +parNewGeneration.hpp parGCAllocBuffer.hpp +parNewGeneration.hpp taskqueue.hpp diff --git a/hotspot/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge b/hotspot/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge new file mode 100644 index 00000000000..d4cf2da6d9f --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge @@ -0,0 +1,439 @@ +// +// Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// NOTE: DO NOT CHANGE THIS COPYRIGHT TO NEW STYLE - IT WILL BREAK makeDeps! + + +adjoiningGenerations.hpp adjoiningVirtualSpaces.hpp +adjoiningGenerations.hpp asPSOldGen.hpp +adjoiningGenerations.hpp asPSYoungGen.hpp +adjoiningGenerations.hpp psPermGen.hpp + +adjoiningGenerations.cpp adjoiningGenerations.hpp +adjoiningGenerations.cpp adjoiningVirtualSpaces.hpp +adjoiningGenerations.cpp parallelScavengeHeap.hpp +adjoiningGenerations.cpp psPermGen.hpp + +adjoiningVirtualSpaces.hpp psVirtualspace.hpp + +adjoiningVirtualSpaces.cpp java.hpp +adjoiningVirtualSpaces.cpp adjoiningVirtualSpaces.hpp + +asPSOldGen.hpp generationCounters.hpp +asPSOldGen.hpp mutableSpace.hpp +asPSOldGen.hpp objectStartArray.hpp +asPSOldGen.hpp psVirtualspace.hpp +asPSOldGen.hpp spaceCounters.hpp +asPSOldGen.hpp psOldGen.hpp + +asPSOldGen.cpp psAdaptiveSizePolicy.hpp +asPSOldGen.cpp cardTableModRefBS.hpp +asPSOldGen.cpp java.hpp +asPSOldGen.cpp oop.inline.hpp +asPSOldGen.cpp parallelScavengeHeap.hpp +asPSOldGen.cpp psMarkSweepDecorator.hpp +asPSOldGen.cpp asPSOldGen.hpp + +asPSYoungGen.hpp generationCounters.hpp +asPSYoungGen.hpp mutableSpace.hpp +asPSYoungGen.hpp objectStartArray.hpp +asPSYoungGen.hpp spaceCounters.hpp +asPSYoungGen.hpp psVirtualspace.hpp +asPSYoungGen.hpp psYoungGen.hpp + +asPSYoungGen.cpp gcUtil.hpp +asPSYoungGen.cpp java.hpp +asPSYoungGen.cpp oop.inline.hpp +asPSYoungGen.cpp parallelScavengeHeap.hpp +asPSYoungGen.cpp psMarkSweepDecorator.hpp +asPSYoungGen.cpp psScavenge.hpp +asPSYoungGen.cpp asPSYoungGen.hpp +asPSYoungGen.cpp psYoungGen.hpp + +cardTableExtension.cpp cardTableExtension.hpp +cardTableExtension.cpp gcTaskManager.hpp +cardTableExtension.cpp oop.inline.hpp +cardTableExtension.cpp oop.psgc.inline.hpp +cardTableExtension.cpp parallelScavengeHeap.hpp +cardTableExtension.cpp psTasks.hpp +cardTableExtension.cpp psYoungGen.hpp + +cardTableExtension.hpp cardTableModRefBS.hpp + +gcTaskManager.hpp mutex.hpp +gcTaskManager.hpp growableArray.hpp + +gcTaskManager.cpp allocation.hpp +gcTaskManager.cpp allocation.inline.hpp +gcTaskManager.cpp gcTaskManager.hpp +gcTaskManager.cpp gcTaskThread.hpp +gcTaskManager.cpp mutex.hpp +gcTaskManager.cpp mutexLocker.hpp + +gcTaskThread.hpp thread.hpp + +gcTaskThread.cpp allocation.hpp +gcTaskThread.cpp allocation.inline.hpp +gcTaskThread.cpp gcTaskManager.hpp +gcTaskThread.cpp gcTaskThread.hpp +gcTaskThread.cpp handles.hpp +gcTaskThread.cpp handles.inline.hpp +gcTaskThread.cpp os.hpp +gcTaskThread.cpp resourceArea.hpp +gcTaskThread.cpp thread.hpp + +generationSizer.hpp collectorPolicy.hpp + +objectStartArray.cpp allocation.inline.hpp +objectStartArray.cpp cardTableModRefBS.hpp +objectStartArray.cpp java.hpp +objectStartArray.cpp objectStartArray.hpp +objectStartArray.cpp oop.inline.hpp + +objectStartArray.hpp allocation.hpp +objectStartArray.hpp memRegion.hpp +objectStartArray.hpp oop.hpp +objectStartArray.hpp psVirtualspace.hpp + +parallelScavengeHeap.cpp adjoiningGenerations.hpp +parallelScavengeHeap.cpp adjoiningVirtualSpaces.hpp +parallelScavengeHeap.cpp cardTableExtension.hpp +parallelScavengeHeap.cpp gcLocker.inline.hpp +parallelScavengeHeap.cpp gcTaskManager.hpp +parallelScavengeHeap.cpp generationSizer.hpp +parallelScavengeHeap.cpp handles.inline.hpp +parallelScavengeHeap.cpp java.hpp +parallelScavengeHeap.cpp oop.inline.hpp +parallelScavengeHeap.cpp parallelScavengeHeap.inline.hpp +parallelScavengeHeap.cpp psAdaptiveSizePolicy.hpp +parallelScavengeHeap.cpp psMarkSweep.hpp +parallelScavengeHeap.cpp psParallelCompact.hpp +parallelScavengeHeap.cpp psPromotionManager.hpp +parallelScavengeHeap.cpp psScavenge.hpp +parallelScavengeHeap.cpp vmThread.hpp +parallelScavengeHeap.cpp vmPSOperations.hpp + +parallelScavengeHeap.inline.hpp parallelScavengeHeap.hpp +parallelScavengeHeap.inline.hpp psMarkSweep.hpp +parallelScavengeHeap.inline.hpp psParallelCompact.hpp +parallelScavengeHeap.inline.hpp psScavenge.hpp + +parallelScavengeHeap.hpp collectedHeap.inline.hpp +parallelScavengeHeap.hpp objectStartArray.hpp +parallelScavengeHeap.hpp gcPolicyCounters.hpp +parallelScavengeHeap.hpp psGCAdaptivePolicyCounters.hpp +parallelScavengeHeap.hpp psOldGen.hpp +parallelScavengeHeap.hpp psPermGen.hpp +parallelScavengeHeap.hpp psYoungGen.hpp +parallelScavengeHeap.hpp ostream.hpp + +parMarkBitMap.cpp bitMap.hpp +parMarkBitMap.cpp bitMap.inline.hpp +parMarkBitMap.cpp oop.inline.hpp +parMarkBitMap.cpp os.hpp +parMarkBitMap.cpp os_.inline.hpp +parMarkBitMap.cpp parMarkBitMap.hpp +parMarkBitMap.cpp parMarkBitMap.inline.hpp +parMarkBitMap.cpp psParallelCompact.hpp + +parMarkBitMap.hpp bitMap.hpp +parMarkBitMap.hpp bitMap.inline.hpp +parMarkBitMap.hpp psVirtualspace.hpp + +psAdaptiveSizePolicy.cpp gcPolicyCounters.hpp +psAdaptiveSizePolicy.cpp gcCause.hpp +psAdaptiveSizePolicy.cpp psAdaptiveSizePolicy.hpp +psAdaptiveSizePolicy.cpp psGCAdaptivePolicyCounters.hpp +psAdaptiveSizePolicy.cpp psScavenge.hpp +psAdaptiveSizePolicy.cpp timer.hpp +psAdaptiveSizePolicy.cpp top.hpp + +psAdaptiveSizePolicy.hpp gcCause.hpp +psAdaptiveSizePolicy.hpp gcStats.hpp +psAdaptiveSizePolicy.hpp gcUtil.hpp +psAdaptiveSizePolicy.hpp adaptiveSizePolicy.hpp + +psCompactionManager.cpp gcTaskManager.hpp +psCompactionManager.cpp objectStartArray.hpp +psCompactionManager.cpp oop.hpp +psCompactionManager.cpp oop.inline.hpp +psCompactionManager.cpp oop.pcgc.inline.hpp +psCompactionManager.cpp parallelScavengeHeap.hpp +psCompactionManager.cpp parMarkBitMap.hpp +psCompactionManager.cpp psParallelCompact.hpp +psCompactionManager.cpp psCompactionManager.hpp +psCompactionManager.cpp psOldGen.hpp +psCompactionManager.cpp systemDictionary.hpp + +psCompactionManager.hpp allocation.hpp +psCompactionManager.hpp taskqueue.hpp + +psGCAdaptivePolicyCounters.hpp gcAdaptivePolicyCounters.hpp +psGCAdaptivePolicyCounters.hpp gcPolicyCounters.hpp +psGCAdaptivePolicyCounters.hpp psAdaptiveSizePolicy.hpp + +psGCAdaptivePolicyCounters.cpp arguments.hpp +psGCAdaptivePolicyCounters.cpp resourceArea.hpp +psGCAdaptivePolicyCounters.cpp psGCAdaptivePolicyCounters.hpp + +psGenerationCounters.cpp psGenerationCounters.hpp +psGenerationCounters.cpp resourceArea.hpp + +psGenerationCounters.hpp generationCounters.hpp +psGenerationCounters.hpp perfData.hpp +psGenerationCounters.hpp psVirtualspace.hpp + +psMarkSweep.cpp psAdaptiveSizePolicy.hpp +psMarkSweep.cpp biasedLocking.hpp +psMarkSweep.cpp codeCache.hpp +psMarkSweep.cpp events.hpp +psMarkSweep.cpp fprofiler.hpp +psMarkSweep.cpp gcCause.hpp +psMarkSweep.cpp gcLocker.inline.hpp +psMarkSweep.cpp isGCActiveMark.hpp +psMarkSweep.cpp oop.inline.hpp +psMarkSweep.cpp memoryService.hpp +psMarkSweep.cpp management.hpp +psMarkSweep.cpp parallelScavengeHeap.hpp +psMarkSweep.cpp psMarkSweep.hpp +psMarkSweep.cpp psMarkSweepDecorator.hpp +psMarkSweep.cpp psOldGen.hpp +psMarkSweep.cpp psPermGen.hpp +psMarkSweep.cpp psScavenge.hpp +psMarkSweep.cpp psYoungGen.hpp +psMarkSweep.cpp referencePolicy.hpp +psMarkSweep.cpp referenceProcessor.hpp +psMarkSweep.cpp safepoint.hpp +psMarkSweep.cpp symbolTable.hpp +psMarkSweep.cpp systemDictionary.hpp +psMarkSweep.cpp vmThread.hpp + +psMarkSweep.hpp markSweep.inline.hpp +psMarkSweep.hpp collectorCounters.hpp + +psMarkSweepDecorator.cpp liveRange.hpp +psMarkSweepDecorator.cpp markSweep.inline.hpp +psMarkSweepDecorator.cpp objectStartArray.hpp +psMarkSweepDecorator.cpp oop.inline.hpp +psMarkSweepDecorator.cpp parallelScavengeHeap.hpp +psMarkSweepDecorator.cpp psMarkSweep.hpp +psMarkSweepDecorator.cpp psMarkSweepDecorator.hpp +psMarkSweepDecorator.cpp systemDictionary.hpp + +psMarkSweepDecorator.hpp mutableSpace.hpp + +psParallelCompact.cpp psAdaptiveSizePolicy.hpp +psParallelCompact.cpp codeCache.hpp +psParallelCompact.cpp events.hpp +psParallelCompact.cpp fprofiler.hpp +psParallelCompact.cpp gcCause.hpp +psParallelCompact.cpp gcLocker.inline.hpp +psParallelCompact.cpp gcTaskManager.hpp +psParallelCompact.cpp isGCActiveMark.hpp +psParallelCompact.cpp oop.inline.hpp +psParallelCompact.cpp oop.pcgc.inline.hpp +psParallelCompact.cpp memoryService.hpp +psParallelCompact.cpp management.hpp +psParallelCompact.cpp parallelScavengeHeap.inline.hpp +psParallelCompact.cpp pcTasks.hpp +psParallelCompact.cpp psMarkSweep.hpp +psParallelCompact.cpp psMarkSweepDecorator.hpp +psParallelCompact.cpp psCompactionManager.hpp +psParallelCompact.cpp psPromotionManager.inline.hpp +psParallelCompact.cpp psOldGen.hpp +psParallelCompact.cpp psParallelCompact.hpp +psParallelCompact.cpp psPermGen.hpp +psParallelCompact.cpp psScavenge.hpp +psParallelCompact.cpp psYoungGen.hpp +psParallelCompact.cpp referencePolicy.hpp +psParallelCompact.cpp referenceProcessor.hpp +psParallelCompact.cpp safepoint.hpp +psParallelCompact.cpp symbolTable.hpp +psParallelCompact.cpp systemDictionary.hpp +psParallelCompact.cpp vmThread.hpp + +psParallelCompact.hpp collectorCounters.hpp +psParallelCompact.hpp markSweep.hpp +psParallelCompact.hpp mutableSpace.hpp +psParallelCompact.hpp objectStartArray.hpp +psParallelCompact.hpp oop.hpp +psParallelCompact.hpp parMarkBitMap.hpp +psParallelCompact.hpp sharedHeap.hpp + +psOldGen.cpp psAdaptiveSizePolicy.hpp +psOldGen.cpp cardTableModRefBS.hpp +psOldGen.cpp gcLocker.inline.hpp +psOldGen.cpp java.hpp +psOldGen.cpp oop.inline.hpp +psOldGen.cpp parallelScavengeHeap.hpp +psOldGen.cpp psMarkSweepDecorator.hpp +psOldGen.cpp psOldGen.hpp + +psOldGen.hpp psGenerationCounters.hpp +psOldGen.hpp mutableSpace.hpp +psOldGen.hpp objectStartArray.hpp +psOldGen.hpp psVirtualspace.hpp +psOldGen.hpp safepoint.hpp +psOldGen.hpp spaceCounters.hpp + +psPermGen.cpp gcUtil.hpp +psPermGen.cpp parallelScavengeHeap.hpp +psPermGen.cpp psMarkSweepDecorator.hpp +psPermGen.cpp psParallelCompact.hpp +psPermGen.cpp psPermGen.hpp + +psPermGen.hpp psOldGen.hpp + +psPromotionManager.cpp memRegion.hpp +psPromotionManager.cpp mutableSpace.hpp +psPromotionManager.cpp oop.inline.hpp +psPromotionManager.cpp oop.psgc.inline.hpp +psPromotionManager.cpp parallelScavengeHeap.hpp +psPromotionManager.cpp psOldGen.hpp +psPromotionManager.cpp psPromotionManager.inline.hpp +psPromotionManager.cpp psScavenge.inline.hpp + +psPromotionManager.hpp allocation.hpp +psPromotionManager.hpp prefetchQueue.hpp +psPromotionManager.hpp psPromotionLAB.hpp +psPromotionManager.hpp taskqueue.hpp + +psPromotionManager.inline.hpp psPromotionManager.hpp +psPromotionManager.inline.hpp psScavenge.hpp + +psPromotionLAB.cpp mutableSpace.hpp +psPromotionLAB.cpp oop.inline.hpp +psPromotionLAB.cpp parallelScavengeHeap.hpp +psPromotionLAB.cpp psPromotionLAB.hpp + +psPromotionLAB.hpp allocation.hpp +psPromotionLAB.hpp objectStartArray.hpp + +psScavenge.cpp psAdaptiveSizePolicy.hpp +psScavenge.cpp biasedLocking.hpp +psScavenge.cpp cardTableExtension.hpp +psScavenge.cpp fprofiler.hpp +psScavenge.cpp gcCause.hpp +psScavenge.cpp gcLocker.inline.hpp +psScavenge.cpp gcTaskManager.hpp +psScavenge.cpp handles.inline.hpp +psScavenge.cpp isGCActiveMark.hpp +psScavenge.cpp oop.inline.hpp +psScavenge.cpp oop.psgc.inline.hpp +psScavenge.cpp memoryService.hpp +psScavenge.cpp parallelScavengeHeap.hpp +psScavenge.cpp psMarkSweep.hpp +psScavenge.cpp psParallelCompact.hpp +psScavenge.cpp psScavenge.inline.hpp +psScavenge.cpp psTasks.hpp +psScavenge.cpp referencePolicy.hpp +psScavenge.cpp referenceProcessor.hpp +psScavenge.cpp resourceArea.hpp +psScavenge.cpp threadCritical.hpp +psScavenge.cpp vmThread.hpp +psScavenge.cpp vm_operations.hpp + +psScavenge.hpp allocation.hpp +psScavenge.hpp cardTableExtension.hpp +psScavenge.hpp collectorCounters.hpp +psScavenge.hpp oop.hpp +psScavenge.hpp psVirtualspace.hpp + +psScavenge.inline.hpp cardTableExtension.hpp +psScavenge.inline.hpp parallelScavengeHeap.hpp +psScavenge.inline.hpp psPromotionManager.hpp +psScavenge.inline.hpp psScavenge.hpp + +pcTasks.cpp collectedHeap.hpp +pcTasks.cpp fprofiler.hpp +pcTasks.cpp jniHandles.hpp +pcTasks.cpp jvmtiExport.hpp +pcTasks.cpp management.hpp +pcTasks.cpp psParallelCompact.hpp +pcTasks.cpp pcTasks.hpp +pcTasks.cpp oop.inline.hpp +pcTasks.cpp oop.pcgc.inline.hpp +pcTasks.cpp systemDictionary.hpp +pcTasks.cpp taskqueue.hpp +pcTasks.cpp thread.hpp +pcTasks.cpp universe.hpp +pcTasks.cpp vmThread.hpp + +pcTasks.hpp gcTaskManager.hpp +pcTasks.hpp psTasks.hpp + +psTasks.cpp cardTableExtension.hpp +psTasks.cpp fprofiler.hpp +psTasks.cpp gcTaskManager.hpp +psTasks.cpp iterator.hpp +psTasks.cpp management.hpp +psTasks.cpp oop.inline.hpp +psTasks.cpp oop.psgc.inline.hpp +psTasks.cpp psMarkSweep.hpp +psTasks.cpp psPromotionManager.hpp +psTasks.cpp psPromotionManager.inline.hpp +psTasks.cpp psScavenge.hpp +psTasks.cpp psTasks.hpp +psTasks.cpp systemDictionary.hpp +psTasks.cpp taskqueue.hpp +psTasks.cpp thread.hpp +psTasks.cpp universe.hpp +psTasks.cpp vmThread.hpp + +psTasks.hpp allocation.hpp +psTasks.hpp growableArray.hpp + +psVirtualspace.hpp virtualspace.hpp + +psVirtualspace.cpp os.hpp +psVirtualspace.cpp os_.inline.hpp +psVirtualspace.cpp psVirtualspace.hpp +psVirtualspace.cpp virtualspace.hpp + +psYoungGen.cpp gcUtil.hpp +psYoungGen.cpp java.hpp +psYoungGen.cpp oop.inline.hpp +psYoungGen.cpp parallelScavengeHeap.hpp +psYoungGen.cpp psMarkSweepDecorator.hpp +psYoungGen.cpp psScavenge.hpp +psYoungGen.cpp psYoungGen.hpp +psYoungGen.cpp mutableNUMASpace.hpp + +psYoungGen.hpp psGenerationCounters.hpp +psYoungGen.hpp mutableSpace.hpp +psYoungGen.hpp objectStartArray.hpp +psYoungGen.hpp spaceCounters.hpp +psYoungGen.hpp psVirtualspace.hpp + +vmPSOperations.cpp dtrace.hpp +vmPSOperations.cpp parallelScavengeHeap.inline.hpp +vmPSOperations.cpp gcLocker.inline.hpp +vmPSOperations.cpp psMarkSweep.hpp +vmPSOperations.cpp psScavenge.hpp +vmPSOperations.cpp psScavenge.inline.hpp +vmPSOperations.cpp vmPSOperations.hpp + +vmPSOperations.hpp gcCause.hpp +vmPSOperations.hpp parallelScavengeHeap.hpp +vmPSOperations.hpp vmGCOperations.hpp diff --git a/hotspot/src/share/vm/gc_implementation/includeDB_gc_serial b/hotspot/src/share/vm/gc_implementation/includeDB_gc_serial new file mode 100644 index 00000000000..6fb42f95b6f --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_serial @@ -0,0 +1,123 @@ +// +// Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +adaptiveSizePolicy.hpp collectedHeap.hpp +adaptiveSizePolicy.hpp gcCause.hpp +adaptiveSizePolicy.hpp gcUtil.hpp +adaptiveSizePolicy.hpp allocation.hpp +adaptiveSizePolicy.hpp universe.hpp + +adaptiveSizePolicy.cpp adaptiveSizePolicy.hpp +adaptiveSizePolicy.cpp gcCause.hpp +adaptiveSizePolicy.cpp ostream.hpp +adaptiveSizePolicy.cpp timer.hpp + +ageTable.cpp ageTable.hpp +ageTable.cpp collectorPolicy.hpp +ageTable.cpp copy.hpp +ageTable.cpp gcPolicyCounters.hpp +ageTable.cpp resourceArea.hpp +ageTable.cpp sharedHeap.hpp + +ageTable.hpp markOop.hpp +ageTable.hpp oop.hpp +ageTable.hpp perfData.hpp + +collectorCounters.cpp collectorCounters.hpp +collectorCounters.cpp resourceArea.hpp + +collectorCounters.hpp perfData.hpp + +cSpaceCounters.cpp resourceArea.hpp +cSpaceCounters.cpp cSpaceCounters.hpp + +cSpaceCounters.hpp space.inline.hpp +cSpaceCounters.hpp perfData.hpp +cSpaceCounters.hpp generationCounters.hpp + +gcPolicyCounters.cpp resourceArea.hpp +gcPolicyCounters.cpp gcPolicyCounters.hpp + +gcPolicyCounters.hpp perfData.hpp + +gcStats.cpp gcStats.hpp +gcStats.cpp gcUtil.hpp + +gcStats.hpp gcUtil.hpp + +gcUtil.cpp gcUtil.hpp + +gcUtil.hpp allocation.hpp +gcUtil.hpp debug.hpp +gcUtil.hpp globalDefinitions.hpp +gcUtil.hpp timer.hpp + +generationCounters.cpp generationCounters.hpp +generationCounters.cpp resourceArea.hpp + +generationCounters.hpp perfData.hpp +generationCounters.hpp virtualspace.hpp + +immutableSpace.hpp iterator.hpp + +liveRange.hpp copy.hpp +liveRange.hpp memRegion.hpp + +markSweep.cpp collectedHeap.inline.hpp +markSweep.cpp markSweep.inline.hpp +markSweep.cpp oop.inline.hpp + +markSweep.hpp growableArray.hpp +markSweep.hpp markOop.hpp +markSweep.hpp oop.hpp +markSweep.hpp timer.hpp +markSweep.hpp universe.hpp + +markSweep.inline.hpp collectedHeap.hpp +markSweep.inline.hpp markSweep.hpp + +mutableSpace.hpp immutableSpace.hpp +mutableSpace.hpp memRegion.hpp +mutableSpace.hpp copy.hpp + +vmGCOperations.cpp vmGCOperations.hpp +vmGCOperations.cpp dtrace.hpp +vmGCOperations.cpp classLoader.hpp +vmGCOperations.cpp gcLocker.inline.hpp +vmGCOperations.cpp genCollectedHeap.hpp +vmGCOperations.cpp handles.inline.hpp +vmGCOperations.cpp init.hpp +vmGCOperations.cpp instanceKlass.hpp +vmGCOperations.cpp instanceRefKlass.hpp +vmGCOperations.cpp interfaceSupport.hpp +vmGCOperations.cpp javaClasses.hpp +vmGCOperations.cpp jvmtiExport.hpp +vmGCOperations.cpp oopFactory.hpp +vmGCOperations.cpp preserveException.hpp + +vmGCOperations.hpp vm_operations.hpp +vmGCOperations.hpp heapInspection.hpp +vmGCOperations.hpp handles.hpp +vmGCOperations.hpp jniHandles.hpp +vmGCOperations.hpp synchronizer.hpp diff --git a/hotspot/src/share/vm/gc_implementation/includeDB_gc_shared b/hotspot/src/share/vm/gc_implementation/includeDB_gc_shared new file mode 100644 index 00000000000..c0b6332e89f --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_shared @@ -0,0 +1,67 @@ +// +// Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// NOTE: DO NOT CHANGE THIS COPYRIGHT TO NEW STYLE - IT WILL BREAK makeDeps! + +gcAdaptivePolicyCounters.hpp adaptiveSizePolicy.hpp +gcAdaptivePolicyCounters.hpp gcPolicyCounters.hpp + +gcAdaptivePolicyCounters.cpp resourceArea.hpp +gcAdaptivePolicyCounters.cpp gcAdaptivePolicyCounters.hpp + +gSpaceCounters.cpp generation.hpp +gSpaceCounters.cpp resourceArea.hpp +gSpaceCounters.cpp gSpaceCounters.hpp + +gSpaceCounters.hpp generation.hpp +gSpaceCounters.hpp perfData.hpp +gSpaceCounters.hpp generationCounters.hpp + +immutableSpace.cpp immutableSpace.hpp +immutableSpace.cpp oop.inline.hpp +immutableSpace.cpp universe.hpp + +isGCActiveMark.hpp parallelScavengeHeap.hpp + +markSweep.inline.hpp psParallelCompact.hpp + +mutableNUMASpace.cpp mutableNUMASpace.hpp +mutableNUMASpace.cpp sharedHeap.hpp +mutableNUMASpace.cpp thread_.inline.hpp + +mutableNUMASpace.hpp mutableSpace.hpp +mutableNUMASpace.hpp gcUtil.hpp + +mutableSpace.cpp mutableSpace.hpp +mutableSpace.cpp oop.inline.hpp +mutableSpace.cpp safepoint.hpp +mutableSpace.cpp thread.hpp + +spaceCounters.cpp resourceArea.hpp +spaceCounters.cpp spaceCounters.hpp + +spaceCounters.hpp immutableSpace.hpp +spaceCounters.hpp mutableSpace.hpp +spaceCounters.hpp perfData.hpp +spaceCounters.hpp generationCounters.hpp diff --git a/hotspot/src/share/vm/gc_implementation/parNew/asParNewGeneration.cpp b/hotspot/src/share/vm/gc_implementation/parNew/asParNewGeneration.cpp new file mode 100644 index 00000000000..506619547d2 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parNew/asParNewGeneration.cpp @@ -0,0 +1,630 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_asParNewGeneration.cpp.incl" + +ASParNewGeneration::ASParNewGeneration(ReservedSpace rs, + size_t initial_byte_size, + size_t min_byte_size, + int level) : + ParNewGeneration(rs, initial_byte_size, level), + _min_gen_size(min_byte_size) {} + +const char* ASParNewGeneration::name() const { + return "adaptive size par new generation"; +} + +void ASParNewGeneration::adjust_desired_tenuring_threshold() { + assert(UseAdaptiveSizePolicy, + "Should only be used with UseAdaptiveSizePolicy"); +} + +void ASParNewGeneration::resize(size_t eden_size, size_t survivor_size) { + // Resize the generation if needed. If the generation resize + // reports false, do not attempt to resize the spaces. + if (resize_generation(eden_size, survivor_size)) { + // Then we lay out the spaces inside the generation + resize_spaces(eden_size, survivor_size); + + space_invariants(); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("Young generation size: " + "desired eden: " SIZE_FORMAT " survivor: " SIZE_FORMAT + " used: " SIZE_FORMAT " capacity: " SIZE_FORMAT + " gen limits: " SIZE_FORMAT " / " SIZE_FORMAT, + eden_size, survivor_size, used(), capacity(), + max_gen_size(), min_gen_size()); + } + } +} + +size_t ASParNewGeneration::available_to_min_gen() { + assert(virtual_space()->committed_size() >= min_gen_size(), "Invariant"); + return virtual_space()->committed_size() - min_gen_size(); +} + +// This method assumes that from-space has live data and that +// any shrinkage of the young gen is limited by location of +// from-space. +size_t ASParNewGeneration::available_to_live() const { +#undef SHRINKS_AT_END_OF_EDEN +#ifdef SHRINKS_AT_END_OF_EDEN + size_t delta_in_survivor = 0; + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + const size_t space_alignment = heap->intra_generation_alignment(); + const size_t gen_alignment = heap->generation_alignment(); + + MutableSpace* space_shrinking = NULL; + if (from_space()->end() > to_space()->end()) { + space_shrinking = from_space(); + } else { + space_shrinking = to_space(); + } + + // Include any space that is committed but not included in + // the survivor spaces. + assert(((HeapWord*)virtual_space()->high()) >= space_shrinking->end(), + "Survivor space beyond high end"); + size_t unused_committed = pointer_delta(virtual_space()->high(), + space_shrinking->end(), sizeof(char)); + + if (space_shrinking->is_empty()) { + // Don't let the space shrink to 0 + assert(space_shrinking->capacity_in_bytes() >= space_alignment, + "Space is too small"); + delta_in_survivor = space_shrinking->capacity_in_bytes() - space_alignment; + } else { + delta_in_survivor = pointer_delta(space_shrinking->end(), + space_shrinking->top(), + sizeof(char)); + } + + size_t delta_in_bytes = unused_committed + delta_in_survivor; + delta_in_bytes = align_size_down(delta_in_bytes, gen_alignment); + return delta_in_bytes; +#else + // The only space available for shrinking is in to-space if it + // is above from-space. + if (to()->bottom() > from()->bottom()) { + const size_t alignment = os::vm_page_size(); + if (to()->capacity() < alignment) { + return 0; + } else { + return to()->capacity() - alignment; + } + } else { + return 0; + } +#endif +} + +// Return the number of bytes available for resizing down the young +// generation. This is the minimum of +// input "bytes" +// bytes to the minimum young gen size +// bytes to the size currently being used + some small extra +size_t ASParNewGeneration::limit_gen_shrink (size_t bytes) { + // Allow shrinkage into the current eden but keep eden large enough + // to maintain the minimum young gen size + bytes = MIN3(bytes, available_to_min_gen(), available_to_live()); + return align_size_down(bytes, os::vm_page_size()); +} + +// Note that the the alignment used is the OS page size as +// opposed to an alignment associated with the virtual space +// (as is done in the ASPSYoungGen/ASPSOldGen) +bool ASParNewGeneration::resize_generation(size_t eden_size, + size_t survivor_size) { + const size_t alignment = os::vm_page_size(); + size_t orig_size = virtual_space()->committed_size(); + bool size_changed = false; + + // There used to be this guarantee there. + // guarantee ((eden_size + 2*survivor_size) <= _max_gen_size, "incorrect input arguments"); + // Code below forces this requirement. In addition the desired eden + // size and disired survivor sizes are desired goals and may + // exceed the total generation size. + + assert(min_gen_size() <= orig_size && orig_size <= max_gen_size(), + "just checking"); + + // Adjust new generation size + const size_t eden_plus_survivors = + align_size_up(eden_size + 2 * survivor_size, alignment); + size_t desired_size = MAX2(MIN2(eden_plus_survivors, max_gen_size()), + min_gen_size()); + assert(desired_size <= max_gen_size(), "just checking"); + + if (desired_size > orig_size) { + // Grow the generation + size_t change = desired_size - orig_size; + assert(change % alignment == 0, "just checking"); + if (!virtual_space()->expand_by(change)) { + return false; // Error if we fail to resize! + } + + size_changed = true; + } else if (desired_size < orig_size) { + size_t desired_change = orig_size - desired_size; + assert(desired_change % alignment == 0, "just checking"); + + desired_change = limit_gen_shrink(desired_change); + + if (desired_change > 0) { + virtual_space()->shrink_by(desired_change); + reset_survivors_after_shrink(); + + size_changed = true; + } + } else { + if (Verbose && PrintGC) { + if (orig_size == max_gen_size()) { + gclog_or_tty->print_cr("ASParNew generation size at maximum: " + SIZE_FORMAT "K", orig_size/K); + } else if (orig_size == min_gen_size()) { + gclog_or_tty->print_cr("ASParNew generation size at minium: " + SIZE_FORMAT "K", orig_size/K); + } + } + } + + if (size_changed) { + MemRegion cmr((HeapWord*)virtual_space()->low(), + (HeapWord*)virtual_space()->high()); + GenCollectedHeap::heap()->barrier_set()->resize_covered_region(cmr); + + if (Verbose && PrintGC) { + size_t current_size = virtual_space()->committed_size(); + gclog_or_tty->print_cr("ASParNew generation size changed: " + SIZE_FORMAT "K->" SIZE_FORMAT "K", + orig_size/K, current_size/K); + } + } + + guarantee(eden_plus_survivors <= virtual_space()->committed_size() || + virtual_space()->committed_size() == max_gen_size(), "Sanity"); + + return true; +} + +void ASParNewGeneration::reset_survivors_after_shrink() { + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + HeapWord* new_end = (HeapWord*)virtual_space()->high(); + + if (from()->end() > to()->end()) { + assert(new_end >= from()->end(), "Shrinking past from-space"); + } else { + assert(new_end >= to()->bottom(), "Shrink was too large"); + // Was there a shrink of the survivor space? + if (new_end < to()->end()) { + MemRegion mr(to()->bottom(), new_end); + to()->initialize(mr, false /* clear */); + } + } +} +void ASParNewGeneration::resize_spaces(size_t requested_eden_size, + size_t requested_survivor_size) { + assert(UseAdaptiveSizePolicy, "sanity check"); + assert(requested_eden_size > 0 && requested_survivor_size > 0, + "just checking"); + CollectedHeap* heap = Universe::heap(); + assert(heap->kind() == CollectedHeap::GenCollectedHeap, "Sanity"); + + + // We require eden and to space to be empty + if ((!eden()->is_empty()) || (!to()->is_empty())) { + return; + } + + size_t cur_eden_size = eden()->capacity(); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("ASParNew::resize_spaces(requested_eden_size: " + SIZE_FORMAT + ", requested_survivor_size: " SIZE_FORMAT ")", + requested_eden_size, requested_survivor_size); + gclog_or_tty->print_cr(" eden: [" PTR_FORMAT ".." PTR_FORMAT ") " + SIZE_FORMAT, + eden()->bottom(), + eden()->end(), + pointer_delta(eden()->end(), + eden()->bottom(), + sizeof(char))); + gclog_or_tty->print_cr(" from: [" PTR_FORMAT ".." PTR_FORMAT ") " + SIZE_FORMAT, + from()->bottom(), + from()->end(), + pointer_delta(from()->end(), + from()->bottom(), + sizeof(char))); + gclog_or_tty->print_cr(" to: [" PTR_FORMAT ".." PTR_FORMAT ") " + SIZE_FORMAT, + to()->bottom(), + to()->end(), + pointer_delta( to()->end(), + to()->bottom(), + sizeof(char))); + } + + // There's nothing to do if the new sizes are the same as the current + if (requested_survivor_size == to()->capacity() && + requested_survivor_size == from()->capacity() && + requested_eden_size == eden()->capacity()) { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" capacities are the right sizes, returning"); + } + return; + } + + char* eden_start = (char*)eden()->bottom(); + char* eden_end = (char*)eden()->end(); + char* from_start = (char*)from()->bottom(); + char* from_end = (char*)from()->end(); + char* to_start = (char*)to()->bottom(); + char* to_end = (char*)to()->end(); + + const size_t alignment = os::vm_page_size(); + const bool maintain_minimum = + (requested_eden_size + 2 * requested_survivor_size) <= min_gen_size(); + + // Check whether from space is below to space + if (from_start < to_start) { + // Eden, from, to + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" Eden, from, to:"); + } + + // Set eden + // "requested_eden_size" is a goal for the size of eden + // and may not be attainable. "eden_size" below is + // calculated based on the location of from-space and + // the goal for the size of eden. from-space is + // fixed in place because it contains live data. + // The calculation is done this way to avoid 32bit + // overflow (i.e., eden_start + requested_eden_size + // may too large for representation in 32bits). + size_t eden_size; + if (maintain_minimum) { + // Only make eden larger than the requested size if + // the minimum size of the generation has to be maintained. + // This could be done in general but policy at a higher + // level is determining a requested size for eden and that + // should be honored unless there is a fundamental reason. + eden_size = pointer_delta(from_start, + eden_start, + sizeof(char)); + } else { + eden_size = MIN2(requested_eden_size, + pointer_delta(from_start, eden_start, sizeof(char))); + } + +// tty->print_cr("eden_size before: " SIZE_FORMAT, eden_size); + eden_size = align_size_down(eden_size, alignment); +// tty->print_cr("eden_size after: " SIZE_FORMAT, eden_size); + eden_end = eden_start + eden_size; + assert(eden_end >= eden_start, "addition overflowed") + + // To may resize into from space as long as it is clear of live data. + // From space must remain page aligned, though, so we need to do some + // extra calculations. + + // First calculate an optimal to-space + to_end = (char*)virtual_space()->high(); + to_start = (char*)pointer_delta(to_end, (char*)requested_survivor_size, + sizeof(char)); + + // Does the optimal to-space overlap from-space? + if (to_start < (char*)from()->end()) { + // Calculate the minimum offset possible for from_end + size_t from_size = pointer_delta(from()->top(), from_start, sizeof(char)); + + // Should we be in this method if from_space is empty? Why not the set_space method? FIX ME! + if (from_size == 0) { + from_size = alignment; + } else { + from_size = align_size_up(from_size, alignment); + } + + from_end = from_start + from_size; + assert(from_end > from_start, "addition overflow or from_size problem"); + + guarantee(from_end <= (char*)from()->end(), "from_end moved to the right"); + + // Now update to_start with the new from_end + to_start = MAX2(from_end, to_start); + } else { + // If shrinking, move to-space down to abut the end of from-space + // so that shrinking will move to-space down. If not shrinking + // to-space is moving up to allow for growth on the next expansion. + if (requested_eden_size <= cur_eden_size) { + to_start = from_end; + if (to_start + requested_survivor_size > to_start) { + to_end = to_start + requested_survivor_size; + } + } + // else leave to_end pointing to the high end of the virtual space. + } + + guarantee(to_start != to_end, "to space is zero sized"); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" [eden_start .. eden_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + eden_start, + eden_end, + pointer_delta(eden_end, eden_start, sizeof(char))); + gclog_or_tty->print_cr(" [from_start .. from_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + from_start, + from_end, + pointer_delta(from_end, from_start, sizeof(char))); + gclog_or_tty->print_cr(" [ to_start .. to_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + to_start, + to_end, + pointer_delta( to_end, to_start, sizeof(char))); + } + } else { + // Eden, to, from + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" Eden, to, from:"); + } + + // Calculate the to-space boundaries based on + // the start of from-space. + to_end = from_start; + to_start = (char*)pointer_delta(from_start, + (char*)requested_survivor_size, + sizeof(char)); + // Calculate the ideal eden boundaries. + // eden_end is already at the bottom of the generation + assert(eden_start == virtual_space()->low(), + "Eden is not starting at the low end of the virtual space"); + if (eden_start + requested_eden_size >= eden_start) { + eden_end = eden_start + requested_eden_size; + } else { + eden_end = to_start; + } + + // Does eden intrude into to-space? to-space + // gets priority but eden is not allowed to shrink + // to 0. + if (eden_end > to_start) { + eden_end = to_start; + } + + // Don't let eden shrink down to 0 or less. + eden_end = MAX2(eden_end, eden_start + alignment); + assert(eden_start + alignment >= eden_start, "Overflow"); + + size_t eden_size; + if (maintain_minimum) { + // Use all the space available. + eden_end = MAX2(eden_end, to_start); + eden_size = pointer_delta(eden_end, eden_start, sizeof(char)); + eden_size = MIN2(eden_size, cur_eden_size); + } else { + eden_size = pointer_delta(eden_end, eden_start, sizeof(char)); + } + eden_size = align_size_down(eden_size, alignment); + assert(maintain_minimum || eden_size <= requested_eden_size, + "Eden size is too large"); + assert(eden_size >= alignment, "Eden size is too small"); + eden_end = eden_start + eden_size; + + // Move to-space down to eden. + if (requested_eden_size < cur_eden_size) { + to_start = eden_end; + if (to_start + requested_survivor_size > to_start) { + to_end = MIN2(from_start, to_start + requested_survivor_size); + } else { + to_end = from_start; + } + } + + // eden_end may have moved so again make sure + // the to-space and eden don't overlap. + to_start = MAX2(eden_end, to_start); + + // from-space + size_t from_used = from()->used(); + if (requested_survivor_size > from_used) { + if (from_start + requested_survivor_size >= from_start) { + from_end = from_start + requested_survivor_size; + } + if (from_end > virtual_space()->high()) { + from_end = virtual_space()->high(); + } + } + + assert(to_start >= eden_end, "to-space should be above eden"); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" [eden_start .. eden_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + eden_start, + eden_end, + pointer_delta(eden_end, eden_start, sizeof(char))); + gclog_or_tty->print_cr(" [ to_start .. to_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + to_start, + to_end, + pointer_delta( to_end, to_start, sizeof(char))); + gclog_or_tty->print_cr(" [from_start .. from_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + from_start, + from_end, + pointer_delta(from_end, from_start, sizeof(char))); + } + } + + + guarantee((HeapWord*)from_start <= from()->bottom(), + "from start moved to the right"); + guarantee((HeapWord*)from_end >= from()->top(), + "from end moved into live data"); + assert(is_object_aligned((intptr_t)eden_start), "checking alignment"); + assert(is_object_aligned((intptr_t)from_start), "checking alignment"); + assert(is_object_aligned((intptr_t)to_start), "checking alignment"); + + MemRegion edenMR((HeapWord*)eden_start, (HeapWord*)eden_end); + MemRegion toMR ((HeapWord*)to_start, (HeapWord*)to_end); + MemRegion fromMR((HeapWord*)from_start, (HeapWord*)from_end); + + // Let's make sure the call to initialize doesn't reset "top"! + HeapWord* old_from_top = from()->top(); + + // For PrintAdaptiveSizePolicy block below + size_t old_from = from()->capacity(); + size_t old_to = to()->capacity(); + + // The call to initialize NULL's the next compaction space + eden()->initialize(edenMR, true); + eden()->set_next_compaction_space(from()); + to()->initialize(toMR , true); + from()->initialize(fromMR, false); // Note, not cleared! + + assert(from()->top() == old_from_top, "from top changed!"); + + if (PrintAdaptiveSizePolicy) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->kind() == CollectedHeap::GenCollectedHeap, "Sanity"); + + gclog_or_tty->print("AdaptiveSizePolicy::survivor space sizes: " + "collection: %d " + "(" SIZE_FORMAT ", " SIZE_FORMAT ") -> " + "(" SIZE_FORMAT ", " SIZE_FORMAT ") ", + gch->total_collections(), + old_from, old_to, + from()->capacity(), + to()->capacity()); + gclog_or_tty->cr(); + } +} + +void ASParNewGeneration::compute_new_size() { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->kind() == CollectedHeap::GenCollectedHeap, + "not a CMS generational heap"); + + + CMSAdaptiveSizePolicy* size_policy = + (CMSAdaptiveSizePolicy*)gch->gen_policy()->size_policy(); + assert(size_policy->is_gc_cms_adaptive_size_policy(), + "Wrong type of size policy"); + + size_t survived = from()->used(); + if (!survivor_overflow()) { + // Keep running averages on how much survived + size_policy->avg_survived()->sample(survived); + } else { + size_t promoted = + (size_t) next_gen()->gc_stats()->avg_promoted()->last_sample(); + assert(promoted < gch->capacity(), "Conversion problem?"); + size_t survived_guess = survived + promoted; + size_policy->avg_survived()->sample(survived_guess); + } + + size_t survivor_limit = max_survivor_size(); + _tenuring_threshold = + size_policy->compute_survivor_space_size_and_threshold( + _survivor_overflow, + _tenuring_threshold, + survivor_limit); + size_policy->avg_young_live()->sample(used()); + size_policy->avg_eden_live()->sample(eden()->used()); + + size_policy->compute_young_generation_free_space(eden()->capacity(), + max_gen_size()); + + resize(size_policy->calculated_eden_size_in_bytes(), + size_policy->calculated_survivor_size_in_bytes()); + + if (UsePerfData) { + CMSGCAdaptivePolicyCounters* counters = + (CMSGCAdaptivePolicyCounters*) gch->collector_policy()->counters(); + assert(counters->kind() == + GCPolicyCounters::CMSGCAdaptivePolicyCountersKind, + "Wrong kind of counters"); + counters->update_tenuring_threshold(_tenuring_threshold); + counters->update_survivor_overflowed(_survivor_overflow); + counters->update_young_capacity(capacity()); + } +} + + +#ifndef PRODUCT +// Changes from PSYoungGen version +// value of "alignment" +void ASParNewGeneration::space_invariants() { + const size_t alignment = os::vm_page_size(); + + // Currently, our eden size cannot shrink to zero + guarantee(eden()->capacity() >= alignment, "eden too small"); + guarantee(from()->capacity() >= alignment, "from too small"); + guarantee(to()->capacity() >= alignment, "to too small"); + + // Relationship of spaces to each other + char* eden_start = (char*)eden()->bottom(); + char* eden_end = (char*)eden()->end(); + char* from_start = (char*)from()->bottom(); + char* from_end = (char*)from()->end(); + char* to_start = (char*)to()->bottom(); + char* to_end = (char*)to()->end(); + + guarantee(eden_start >= virtual_space()->low(), "eden bottom"); + guarantee(eden_start < eden_end, "eden space consistency"); + guarantee(from_start < from_end, "from space consistency"); + guarantee(to_start < to_end, "to space consistency"); + + // Check whether from space is below to space + if (from_start < to_start) { + // Eden, from, to + guarantee(eden_end <= from_start, "eden/from boundary"); + guarantee(from_end <= to_start, "from/to boundary"); + guarantee(to_end <= virtual_space()->high(), "to end"); + } else { + // Eden, to, from + guarantee(eden_end <= to_start, "eden/to boundary"); + guarantee(to_end <= from_start, "to/from boundary"); + guarantee(from_end <= virtual_space()->high(), "from end"); + } + + // More checks that the virtual space is consistent with the spaces + assert(virtual_space()->committed_size() >= + (eden()->capacity() + + to()->capacity() + + from()->capacity()), "Committed size is inconsistent"); + assert(virtual_space()->committed_size() <= virtual_space()->reserved_size(), + "Space invariant"); + char* eden_top = (char*)eden()->top(); + char* from_top = (char*)from()->top(); + char* to_top = (char*)to()->top(); + assert(eden_top <= virtual_space()->high(), "eden top"); + assert(from_top <= virtual_space()->high(), "from top"); + assert(to_top <= virtual_space()->high(), "to top"); +} +#endif diff --git a/hotspot/src/share/vm/gc_implementation/parNew/asParNewGeneration.hpp b/hotspot/src/share/vm/gc_implementation/parNew/asParNewGeneration.hpp new file mode 100644 index 00000000000..ff3aec8af3d --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parNew/asParNewGeneration.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A Generation that does parallel young-gen collection extended +// for adaptive size policy. + +// Division of generation into spaces +// done by DefNewGeneration::compute_space_boundaries() +// +---------------+ +// | uncommitted | +// |---------------| +// | ss0 | +// |---------------| +// | ss1 | +// |---------------| +// | | +// | eden | +// | | +// +---------------+ <-- low end of VirtualSpace +// +class ASParNewGeneration: public ParNewGeneration { + + size_t _min_gen_size; + + // Resize the generation based on the desired sizes of + // the constituent spaces. + bool resize_generation(size_t eden_size, size_t survivor_size); + // Resize the spaces based on their desired sizes but + // respecting the maximum size of the generation. + void resize_spaces(size_t eden_size, size_t survivor_size); + // Return the byte size remaining to the minimum generation size. + size_t available_to_min_gen(); + // Return the byte size remaining to the live data in the generation. + size_t available_to_live() const; + // Return the byte size that the generation is allowed to shrink. + size_t limit_gen_shrink(size_t bytes); + // Reset the size of the spaces after a shrink of the generation. + void reset_survivors_after_shrink(); + + // Accessor + VirtualSpace* virtual_space() { return &_virtual_space; } + + virtual void adjust_desired_tenuring_threshold(); + + public: + + ASParNewGeneration(ReservedSpace rs, + size_t initial_byte_size, + size_t min_byte_size, + int level); + + virtual const char* short_name() const { return "ASParNew"; } + virtual const char* name() const; + virtual Generation::Name kind() { return ASParNew; } + + // Change the sizes of eden and the survivor spaces in + // the generation. The parameters are desired sizes + // and are not guaranteed to be met. For example, if + // the total is larger than the generation. + void resize(size_t eden_size, size_t survivor_size); + + virtual void compute_new_size(); + + size_t max_gen_size() { return _reserved.byte_size(); } + size_t min_gen_size() const { return _min_gen_size; } + + // Space boundary invariant checker + void space_invariants() PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp b/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp new file mode 100644 index 00000000000..fcbd6cc42e8 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_parCardTableModRefBS.cpp.incl" + +void CardTableModRefBS::par_non_clean_card_iterate_work(Space* sp, MemRegion mr, + DirtyCardToOopClosure* dcto_cl, + MemRegionClosure* cl, + bool clear, + int n_threads) { + if (n_threads > 0) { + assert(n_threads == (int)ParallelGCThreads, "# worker threads != # requested!"); + + // Make sure the LNC array is valid for the space. + jbyte** lowest_non_clean; + uintptr_t lowest_non_clean_base_chunk_index; + size_t lowest_non_clean_chunk_size; + get_LNC_array_for_space(sp, lowest_non_clean, + lowest_non_clean_base_chunk_index, + lowest_non_clean_chunk_size); + + int n_strides = n_threads * StridesPerThread; + SequentialSubTasksDone* pst = sp->par_seq_tasks(); + pst->set_par_threads(n_threads); + pst->set_n_tasks(n_strides); + + int stride = 0; + while (!pst->is_task_claimed(/* reference */ stride)) { + process_stride(sp, mr, stride, n_strides, dcto_cl, cl, clear, + lowest_non_clean, + lowest_non_clean_base_chunk_index, + lowest_non_clean_chunk_size); + } + if (pst->all_tasks_completed()) { + // Clear lowest_non_clean array for next time. + intptr_t first_chunk_index = addr_to_chunk_index(mr.start()); + uintptr_t last_chunk_index = addr_to_chunk_index(mr.last()); + for (uintptr_t ch = first_chunk_index; ch <= last_chunk_index; ch++) { + intptr_t ind = ch - lowest_non_clean_base_chunk_index; + assert(0 <= ind && ind < (intptr_t)lowest_non_clean_chunk_size, + "Bounds error"); + lowest_non_clean[ind] = NULL; + } + } + } +} + +void +CardTableModRefBS:: +process_stride(Space* sp, + MemRegion used, + jint stride, int n_strides, + DirtyCardToOopClosure* dcto_cl, + MemRegionClosure* cl, + bool clear, + jbyte** lowest_non_clean, + uintptr_t lowest_non_clean_base_chunk_index, + size_t lowest_non_clean_chunk_size) { + // We don't have to go downwards here; it wouldn't help anyway, + // because of parallelism. + + // Find the first card address of the first chunk in the stride that is + // at least "bottom" of the used region. + jbyte* start_card = byte_for(used.start()); + jbyte* end_card = byte_after(used.last()); + uintptr_t start_chunk = addr_to_chunk_index(used.start()); + uintptr_t start_chunk_stride_num = start_chunk % n_strides; + jbyte* chunk_card_start; + + if ((uintptr_t)stride >= start_chunk_stride_num) { + chunk_card_start = (jbyte*)(start_card + + (stride - start_chunk_stride_num) * + CardsPerStrideChunk); + } else { + // Go ahead to the next chunk group boundary, then to the requested stride. + chunk_card_start = (jbyte*)(start_card + + (n_strides - start_chunk_stride_num + stride) * + CardsPerStrideChunk); + } + + while (chunk_card_start < end_card) { + // We don't have to go downwards here; it wouldn't help anyway, + // because of parallelism. (We take care with "min_done"; see below.) + // Invariant: chunk_mr should be fully contained within the "used" region. + jbyte* chunk_card_end = chunk_card_start + CardsPerStrideChunk; + MemRegion chunk_mr = MemRegion(addr_for(chunk_card_start), + chunk_card_end >= end_card ? + used.end() : addr_for(chunk_card_end)); + assert(chunk_mr.word_size() > 0, "[chunk_card_start > used_end)"); + assert(used.contains(chunk_mr), "chunk_mr should be subset of used"); + + // Process the chunk. + process_chunk_boundaries(sp, + dcto_cl, + chunk_mr, + used, + lowest_non_clean, + lowest_non_clean_base_chunk_index, + lowest_non_clean_chunk_size); + + non_clean_card_iterate_work(chunk_mr, cl, clear); + + // Find the next chunk of the stride. + chunk_card_start += CardsPerStrideChunk * n_strides; + } +} + +void +CardTableModRefBS:: +process_chunk_boundaries(Space* sp, + DirtyCardToOopClosure* dcto_cl, + MemRegion chunk_mr, + MemRegion used, + jbyte** lowest_non_clean, + uintptr_t lowest_non_clean_base_chunk_index, + size_t lowest_non_clean_chunk_size) +{ + // We must worry about the chunk boundaries. + + // First, set our max_to_do: + HeapWord* max_to_do = NULL; + uintptr_t cur_chunk_index = addr_to_chunk_index(chunk_mr.start()); + cur_chunk_index = cur_chunk_index - lowest_non_clean_base_chunk_index; + + if (chunk_mr.end() < used.end()) { + // This is not the last chunk in the used region. What is the last + // object? + HeapWord* last_block = sp->block_start(chunk_mr.end()); + assert(last_block <= chunk_mr.end(), "In case this property changes."); + if (last_block == chunk_mr.end() + || !sp->block_is_obj(last_block)) { + max_to_do = chunk_mr.end(); + + } else { + // It is an object and starts before the end of the current chunk. + // last_obj_card is the card corresponding to the start of the last object + // in the chunk. Note that the last object may not start in + // the chunk. + jbyte* last_obj_card = byte_for(last_block); + if (!card_may_have_been_dirty(*last_obj_card)) { + // The card containing the head is not dirty. Any marks in + // subsequent cards still in this chunk must have been made + // precisely; we can cap processing at the end. + max_to_do = chunk_mr.end(); + } else { + // The last object must be considered dirty, and extends onto the + // following chunk. Look for a dirty card in that chunk that will + // bound our processing. + jbyte* limit_card = NULL; + size_t last_block_size = sp->block_size(last_block); + jbyte* last_card_of_last_obj = + byte_for(last_block + last_block_size - 1); + jbyte* first_card_of_next_chunk = byte_for(chunk_mr.end()); + // This search potentially goes a long distance looking + // for the next card that will be scanned. For example, + // an object that is an array of primitives will not + // have any cards covering regions interior to the array + // that will need to be scanned. The scan can be terminated + // at the last card of the next chunk. That would leave + // limit_card as NULL and would result in "max_to_do" + // being set with the LNC value or with the end + // of the last block. + jbyte* last_card_of_next_chunk = first_card_of_next_chunk + + CardsPerStrideChunk; + assert(byte_for(chunk_mr.end()) - byte_for(chunk_mr.start()) + == CardsPerStrideChunk, "last card of next chunk may be wrong"); + jbyte* last_card_to_check = (jbyte*) MIN2(last_card_of_last_obj, + last_card_of_next_chunk); + for (jbyte* cur = first_card_of_next_chunk; + cur <= last_card_to_check; cur++) { + if (card_will_be_scanned(*cur)) { + limit_card = cur; break; + } + } + assert(0 <= cur_chunk_index+1 && + cur_chunk_index+1 < lowest_non_clean_chunk_size, + "Bounds error."); + // LNC for the next chunk + jbyte* lnc_card = lowest_non_clean[cur_chunk_index+1]; + if (limit_card == NULL) { + limit_card = lnc_card; + } + if (limit_card != NULL) { + if (lnc_card != NULL) { + limit_card = (jbyte*)MIN2((intptr_t)limit_card, + (intptr_t)lnc_card); + } + max_to_do = addr_for(limit_card); + } else { + max_to_do = last_block + last_block_size; + } + } + } + assert(max_to_do != NULL, "OOPS!"); + } else { + max_to_do = used.end(); + } + // Now we can set the closure we're using so it doesn't to beyond + // max_to_do. + dcto_cl->set_min_done(max_to_do); +#ifndef PRODUCT + dcto_cl->set_last_bottom(max_to_do); +#endif + + // Now we set *our" lowest_non_clean entry. + // Find the object that spans our boundary, if one exists. + // Nothing to do on the first chunk. + if (chunk_mr.start() > used.start()) { + // first_block is the block possibly spanning the chunk start + HeapWord* first_block = sp->block_start(chunk_mr.start()); + // Does the block span the start of the chunk and is it + // an object? + if (first_block < chunk_mr.start() && + sp->block_is_obj(first_block)) { + jbyte* first_dirty_card = NULL; + jbyte* last_card_of_first_obj = + byte_for(first_block + sp->block_size(first_block) - 1); + jbyte* first_card_of_cur_chunk = byte_for(chunk_mr.start()); + jbyte* last_card_of_cur_chunk = byte_for(chunk_mr.last()); + jbyte* last_card_to_check = + (jbyte*) MIN2((intptr_t) last_card_of_cur_chunk, + (intptr_t) last_card_of_first_obj); + for (jbyte* cur = first_card_of_cur_chunk; + cur <= last_card_to_check; cur++) { + if (card_will_be_scanned(*cur)) { + first_dirty_card = cur; break; + } + } + if (first_dirty_card != NULL) { + assert(0 <= cur_chunk_index && + cur_chunk_index < lowest_non_clean_chunk_size, + "Bounds error."); + lowest_non_clean[cur_chunk_index] = first_dirty_card; + } + } + } +} + +void +CardTableModRefBS:: +get_LNC_array_for_space(Space* sp, + jbyte**& lowest_non_clean, + uintptr_t& lowest_non_clean_base_chunk_index, + size_t& lowest_non_clean_chunk_size) { + + int i = find_covering_region_containing(sp->bottom()); + MemRegion covered = _covered[i]; + size_t n_chunks = chunks_to_cover(covered); + + // Only the first thread to obtain the lock will resize the + // LNC array for the covered region. Any later expansion can't affect + // the used_at_save_marks region. + // (I observed a bug in which the first thread to execute this would + // resize, and then it would cause "expand_and_allocates" that would + // Increase the number of chunks in the covered region. Then a second + // thread would come and execute this, see that the size didn't match, + // and free and allocate again. So the first thread would be using a + // freed "_lowest_non_clean" array.) + + // Do a dirty read here. If we pass the conditional then take the rare + // event lock and do the read again in case some other thread had already + // succeeded and done the resize. + int cur_collection = Universe::heap()->total_collections(); + if (_last_LNC_resizing_collection[i] != cur_collection) { + MutexLocker x(ParGCRareEvent_lock); + if (_last_LNC_resizing_collection[i] != cur_collection) { + if (_lowest_non_clean[i] == NULL || + n_chunks != _lowest_non_clean_chunk_size[i]) { + + // Should we delete the old? + if (_lowest_non_clean[i] != NULL) { + assert(n_chunks != _lowest_non_clean_chunk_size[i], + "logical consequence"); + FREE_C_HEAP_ARRAY(CardPtr, _lowest_non_clean[i]); + _lowest_non_clean[i] = NULL; + } + // Now allocate a new one if necessary. + if (_lowest_non_clean[i] == NULL) { + _lowest_non_clean[i] = NEW_C_HEAP_ARRAY(CardPtr, n_chunks); + _lowest_non_clean_chunk_size[i] = n_chunks; + _lowest_non_clean_base_chunk_index[i] = addr_to_chunk_index(covered.start()); + for (int j = 0; j < (int)n_chunks; j++) + _lowest_non_clean[i][j] = NULL; + } + } + _last_LNC_resizing_collection[i] = cur_collection; + } + } + // In any case, now do the initialization. + lowest_non_clean = _lowest_non_clean[i]; + lowest_non_clean_base_chunk_index = _lowest_non_clean_base_chunk_index[i]; + lowest_non_clean_chunk_size = _lowest_non_clean_chunk_size[i]; +} diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parGCAllocBuffer.cpp b/hotspot/src/share/vm/gc_implementation/parNew/parGCAllocBuffer.cpp new file mode 100644 index 00000000000..faa3ce7ac7e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parNew/parGCAllocBuffer.cpp @@ -0,0 +1,342 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_parGCAllocBuffer.cpp.incl" + +ParGCAllocBuffer::ParGCAllocBuffer(size_t desired_plab_sz_) : + _word_sz(desired_plab_sz_), _bottom(NULL), _top(NULL), + _end(NULL), _hard_end(NULL), + _retained(false), _retained_filler(), + _allocated(0), _wasted(0) +{ + assert (min_size() > AlignmentReserve, "Inconsistency!"); +} + +const size_t ParGCAllocBuffer::FillerHeaderSize = + align_object_size(arrayOopDesc::header_size(T_INT)); + +// If the minimum object size is greater than MinObjAlignment, we can +// end up with a shard at the end of the buffer that's smaller than +// the smallest object. We can't allow that because the buffer must +// look like it's full of objects when we retire it, so we make +// sure we have enough space for a filler int array object. +const size_t ParGCAllocBuffer::AlignmentReserve = + oopDesc::header_size() > MinObjAlignment ? FillerHeaderSize : 0; + +void ParGCAllocBuffer::retire(bool end_of_gc, bool retain) { + assert(!retain || end_of_gc, "Can only retain at GC end."); + if (_retained) { + // If the buffer had been retained shorten the previous filler object. + assert(_retained_filler.end() <= _top, "INVARIANT"); + SharedHeap::fill_region_with_object(_retained_filler); + // Wasted space book-keeping, otherwise (normally) done in invalidate() + _wasted += _retained_filler.word_size(); + _retained = false; + } + assert(!end_of_gc || !_retained, "At this point, end_of_gc ==> !_retained."); + if (_top < _hard_end) { + SharedHeap::fill_region_with_object(MemRegion(_top, _hard_end)); + if (!retain) { + invalidate(); + } else { + // Is there wasted space we'd like to retain for the next GC? + if (pointer_delta(_end, _top) > FillerHeaderSize) { + _retained = true; + _retained_filler = MemRegion(_top, FillerHeaderSize); + _top = _top + FillerHeaderSize; + } else { + invalidate(); + } + } + } +} + +void ParGCAllocBuffer::flush_stats(PLABStats* stats) { + assert(ResizePLAB, "Wasted work"); + stats->add_allocated(_allocated); + stats->add_wasted(_wasted); + stats->add_unused(pointer_delta(_end, _top)); +} + +// Compute desired plab size and latch result for later +// use. This should be called once at the end of parallel +// scavenge; it clears the sensor accumulators. +void PLABStats::adjust_desired_plab_sz() { + assert(ResizePLAB, "Not set"); + if (_allocated == 0) { + assert(_unused == 0, "Inconsistency in PLAB stats"); + _allocated = 1; + } + double wasted_frac = (double)_unused/(double)_allocated; + size_t target_refills = (size_t)((wasted_frac*TargetSurvivorRatio)/ + TargetPLABWastePct); + if (target_refills == 0) { + target_refills = 1; + } + _used = _allocated - _wasted - _unused; + size_t plab_sz = _used/(target_refills*ParallelGCThreads); + if (PrintPLAB) gclog_or_tty->print(" (plab_sz = %d ", plab_sz); + // Take historical weighted average + _filter.sample(plab_sz); + // Clip from above and below, and align to object boundary + plab_sz = MAX2(min_size(), (size_t)_filter.average()); + plab_sz = MIN2(max_size(), plab_sz); + plab_sz = align_object_size(plab_sz); + // Latch the result + if (PrintPLAB) gclog_or_tty->print(" desired_plab_sz = %d) ", plab_sz); + if (ResizePLAB) { + _desired_plab_sz = plab_sz; + } + // Now clear the accumulators for next round: + // note this needs to be fixed in the case where we + // are retaining across scavenges. FIX ME !!! XXX + _allocated = 0; + _wasted = 0; + _unused = 0; +} + +#ifndef PRODUCT +void ParGCAllocBuffer::print() { + gclog_or_tty->print("parGCAllocBuffer: _bottom: %p _top: %p _end: %p _hard_end: %p" + "_retained: %c _retained_filler: [%p,%p)\n", + _bottom, _top, _end, _hard_end, + "FT"[_retained], _retained_filler.start(), _retained_filler.end()); +} +#endif // !PRODUCT + +const size_t ParGCAllocBufferWithBOT::ChunkSizeInWords = +MIN2(CardTableModRefBS::par_chunk_heapword_alignment(), + ((size_t)Generation::GenGrain)/HeapWordSize); +const size_t ParGCAllocBufferWithBOT::ChunkSizeInBytes = +MIN2(CardTableModRefBS::par_chunk_heapword_alignment() * HeapWordSize, + (size_t)Generation::GenGrain); + +ParGCAllocBufferWithBOT::ParGCAllocBufferWithBOT(size_t word_sz, + BlockOffsetSharedArray* bsa) : + ParGCAllocBuffer(word_sz), + _bsa(bsa), + _bt(bsa, MemRegion(_bottom, _hard_end)), + _true_end(_hard_end) +{} + +// The buffer comes with its own BOT, with a shared (obviously) underlying +// BlockOffsetSharedArray. We manipulate this BOT in the normal way +// as we would for any contiguous space. However, on accasion we +// need to do some buffer surgery at the extremities before we +// start using the body of the buffer for allocations. Such surgery +// (as explained elsewhere) is to prevent allocation on a card that +// is in the process of being walked concurrently by another GC thread. +// When such surgery happens at a point that is far removed (to the +// right of the current allocation point, top), we use the "contig" +// parameter below to directly manipulate the shared array without +// modifying the _next_threshold state in the BOT. +void ParGCAllocBufferWithBOT::fill_region_with_block(MemRegion mr, + bool contig) { + SharedHeap::fill_region_with_object(mr); + if (contig) { + _bt.alloc_block(mr.start(), mr.end()); + } else { + _bt.BlockOffsetArray::alloc_block(mr.start(), mr.end()); + } +} + +HeapWord* ParGCAllocBufferWithBOT::allocate_slow(size_t word_sz) { + HeapWord* res = NULL; + if (_true_end > _hard_end) { + assert((HeapWord*)align_size_down(intptr_t(_hard_end), + ChunkSizeInBytes) == _hard_end, + "or else _true_end should be equal to _hard_end"); + assert(_retained, "or else _true_end should be equal to _hard_end"); + assert(_retained_filler.end() <= _top, "INVARIANT"); + SharedHeap::fill_region_with_object(_retained_filler); + if (_top < _hard_end) { + fill_region_with_block(MemRegion(_top, _hard_end), true); + } + HeapWord* next_hard_end = MIN2(_true_end, _hard_end + ChunkSizeInWords); + _retained_filler = MemRegion(_hard_end, FillerHeaderSize); + _bt.alloc_block(_retained_filler.start(), _retained_filler.word_size()); + _top = _retained_filler.end(); + _hard_end = next_hard_end; + _end = _hard_end - AlignmentReserve; + res = ParGCAllocBuffer::allocate(word_sz); + if (res != NULL) { + _bt.alloc_block(res, word_sz); + } + } + return res; +} + +void +ParGCAllocBufferWithBOT::undo_allocation(HeapWord* obj, size_t word_sz) { + ParGCAllocBuffer::undo_allocation(obj, word_sz); + // This may back us up beyond the previous threshold, so reset. + _bt.set_region(MemRegion(_top, _hard_end)); + _bt.initialize_threshold(); +} + +void ParGCAllocBufferWithBOT::retire(bool end_of_gc, bool retain) { + assert(!retain || end_of_gc, "Can only retain at GC end."); + if (_retained) { + // We're about to make the retained_filler into a block. + _bt.BlockOffsetArray::alloc_block(_retained_filler.start(), + _retained_filler.end()); + } + // Reset _hard_end to _true_end (and update _end) + if (retain && _hard_end != NULL) { + assert(_hard_end <= _true_end, "Invariant."); + _hard_end = _true_end; + _end = MAX2(_top, _hard_end - AlignmentReserve); + assert(_end <= _hard_end, "Invariant."); + } + _true_end = _hard_end; + HeapWord* pre_top = _top; + + ParGCAllocBuffer::retire(end_of_gc, retain); + // Now any old _retained_filler is cut back to size, the free part is + // filled with a filler object, and top is past the header of that + // object. + + if (retain && _top < _end) { + assert(end_of_gc && retain, "Or else retain should be false."); + // If the lab does not start on a card boundary, we don't want to + // allocate onto that card, since that might lead to concurrent + // allocation and card scanning, which we don't support. So we fill + // the first card with a garbage object. + size_t first_card_index = _bsa->index_for(pre_top); + HeapWord* first_card_start = _bsa->address_for_index(first_card_index); + if (first_card_start < pre_top) { + HeapWord* second_card_start = + _bsa->address_for_index(first_card_index + 1); + + // Ensure enough room to fill with the smallest block + second_card_start = MAX2(second_card_start, pre_top + AlignmentReserve); + + // If the end is already in the first card, don't go beyond it! + // Or if the remainder is too small for a filler object, gobble it up. + if (_hard_end < second_card_start || + pointer_delta(_hard_end, second_card_start) < AlignmentReserve) { + second_card_start = _hard_end; + } + if (pre_top < second_card_start) { + MemRegion first_card_suffix(pre_top, second_card_start); + fill_region_with_block(first_card_suffix, true); + } + pre_top = second_card_start; + _top = pre_top; + _end = MAX2(_top, _hard_end - AlignmentReserve); + } + + // If the lab does not end on a card boundary, we don't want to + // allocate onto that card, since that might lead to concurrent + // allocation and card scanning, which we don't support. So we fill + // the last card with a garbage object. + size_t last_card_index = _bsa->index_for(_hard_end); + HeapWord* last_card_start = _bsa->address_for_index(last_card_index); + if (last_card_start < _hard_end) { + + // Ensure enough room to fill with the smallest block + last_card_start = MIN2(last_card_start, _hard_end - AlignmentReserve); + + // If the top is already in the last card, don't go back beyond it! + // Or if the remainder is too small for a filler object, gobble it up. + if (_top > last_card_start || + pointer_delta(last_card_start, _top) < AlignmentReserve) { + last_card_start = _top; + } + if (last_card_start < _hard_end) { + MemRegion last_card_prefix(last_card_start, _hard_end); + fill_region_with_block(last_card_prefix, false); + } + _hard_end = last_card_start; + _end = MAX2(_top, _hard_end - AlignmentReserve); + _true_end = _hard_end; + assert(_end <= _hard_end, "Invariant."); + } + + // At this point: + // 1) we had a filler object from the original top to hard_end. + // 2) We've filled in any partial cards at the front and back. + if (pre_top < _hard_end) { + // Now we can reset the _bt to do allocation in the given area. + MemRegion new_filler(pre_top, _hard_end); + fill_region_with_block(new_filler, false); + _top = pre_top + ParGCAllocBuffer::FillerHeaderSize; + // If there's no space left, don't retain. + if (_top >= _end) { + _retained = false; + invalidate(); + return; + } + _retained_filler = MemRegion(pre_top, _top); + _bt.set_region(MemRegion(_top, _hard_end)); + _bt.initialize_threshold(); + assert(_bt.threshold() > _top, "initialize_threshold failed!"); + + // There may be other reasons for queries into the middle of the + // filler object. When such queries are done in parallel with + // allocation, bad things can happen, if the query involves object + // iteration. So we ensure that such queries do not involve object + // iteration, by putting another filler object on the boundaries of + // such queries. One such is the object spanning a parallel card + // chunk boundary. + + // "chunk_boundary" is the address of the first chunk boundary less + // than "hard_end". + HeapWord* chunk_boundary = + (HeapWord*)align_size_down(intptr_t(_hard_end-1), ChunkSizeInBytes); + assert(chunk_boundary < _hard_end, "Or else above did not work."); + assert(pointer_delta(_true_end, chunk_boundary) >= AlignmentReserve, + "Consequence of last card handling above."); + + if (_top <= chunk_boundary) { + assert(_true_end == _hard_end, "Invariant."); + while (_top <= chunk_boundary) { + assert(pointer_delta(_hard_end, chunk_boundary) >= AlignmentReserve, + "Consequence of last card handling above."); + MemRegion chunk_portion(chunk_boundary, _hard_end); + _bt.BlockOffsetArray::alloc_block(chunk_portion.start(), + chunk_portion.end()); + SharedHeap::fill_region_with_object(chunk_portion); + _hard_end = chunk_portion.start(); + chunk_boundary -= ChunkSizeInWords; + } + _end = _hard_end - AlignmentReserve; + assert(_top <= _end, "Invariant."); + // Now reset the initial filler chunk so it doesn't overlap with + // the one(s) inserted above. + MemRegion new_filler(pre_top, _hard_end); + fill_region_with_block(new_filler, false); + } + } else { + _retained = false; + invalidate(); + } + } else { + assert(!end_of_gc || + (!_retained && _true_end == _hard_end), "Checking."); + } + assert(_end <= _hard_end, "Invariant."); + assert(_top < _end || _top == _hard_end, "Invariant"); +} diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parGCAllocBuffer.hpp b/hotspot/src/share/vm/gc_implementation/parNew/parGCAllocBuffer.hpp new file mode 100644 index 00000000000..73901f2bacd --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parNew/parGCAllocBuffer.hpp @@ -0,0 +1,241 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Forward decl. + +class PLABStats; + +// A per-thread allocation buffer used during GC. +class ParGCAllocBuffer: public CHeapObj { +protected: + char head[32]; + size_t _word_sz; // in HeapWord units + HeapWord* _bottom; + HeapWord* _top; + HeapWord* _end; // last allocatable address + 1 + HeapWord* _hard_end; // _end + AlignmentReserve + bool _retained; // whether we hold a _retained_filler + MemRegion _retained_filler; + // In support of ergonomic sizing of PLAB's + size_t _allocated; // in HeapWord units + size_t _wasted; // in HeapWord units + char tail[32]; + static const size_t FillerHeaderSize; + static const size_t AlignmentReserve; + +public: + // Initializes the buffer to be empty, but with the given "word_sz". + // Must get initialized with "set_buf" for an allocation to succeed. + ParGCAllocBuffer(size_t word_sz); + + static const size_t min_size() { + return ThreadLocalAllocBuffer::min_size(); + } + + static const size_t max_size() { + return ThreadLocalAllocBuffer::max_size(); + } + + // If an allocation of the given "word_sz" can be satisfied within the + // buffer, do the allocation, returning a pointer to the start of the + // allocated block. If the allocation request cannot be satisfied, + // return NULL. + HeapWord* allocate(size_t word_sz) { + HeapWord* res = _top; + HeapWord* new_top = _top + word_sz; + if (new_top <= _end) { + _top = new_top; + return res; + } else { + return NULL; + } + } + + // Undo the last allocation in the buffer, which is required to be of the + // "obj" of the given "word_sz". + void undo_allocation(HeapWord* obj, size_t word_sz) { + assert(_top - word_sz >= _bottom + && _top - word_sz == obj, + "Bad undo_allocation"); + _top = _top - word_sz; + } + + // The total (word) size of the buffer, including both allocated and + // unallocted space. + size_t word_sz() { return _word_sz; } + + // Should only be done if we are about to reset with a new buffer of the + // given size. + void set_word_size(size_t new_word_sz) { + assert(new_word_sz > AlignmentReserve, "Too small"); + _word_sz = new_word_sz; + } + + // The number of words of unallocated space remaining in the buffer. + size_t words_remaining() { + assert(_end >= _top, "Negative buffer"); + return pointer_delta(_end, _top, HeapWordSize); + } + + bool contains(void* addr) { + return (void*)_bottom <= addr && addr < (void*)_hard_end; + } + + // Sets the space of the buffer to be [buf, space+word_sz()). + void set_buf(HeapWord* buf) { + _bottom = buf; + _top = _bottom; + _hard_end = _bottom + word_sz(); + _end = _hard_end - AlignmentReserve; + assert(_end >= _top, "Negative buffer"); + // In support of ergonomic sizing + _allocated += word_sz(); + } + + // Flush the stats supporting ergonomic sizing of PLAB's + void flush_stats(PLABStats* stats); + void flush_stats_and_retire(PLABStats* stats, bool retain) { + // We flush the stats first in order to get a reading of + // unused space in the last buffer. + if (ResizePLAB) { + flush_stats(stats); + } + // Retire the last allocation buffer. + retire(true, retain); + } + + // Force future allocations to fail and queries for contains() + // to return false + void invalidate() { + assert(!_retained, "Shouldn't retain an invalidated buffer."); + _end = _hard_end; + _wasted += pointer_delta(_end, _top); // unused space + _top = _end; // force future allocations to fail + _bottom = _end; // force future contains() queries to return false + } + + // Fills in the unallocated portion of the buffer with a garbage object. + // If "end_of_gc" is TRUE, is after the last use in the GC. IF "retain" + // is true, attempt to re-use the unused portion in the next GC. + void retire(bool end_of_gc, bool retain); + + void print() PRODUCT_RETURN; +}; + +// PLAB stats book-keeping +class PLABStats VALUE_OBJ_CLASS_SPEC { + size_t _allocated; // total allocated + size_t _wasted; // of which wasted (internal fragmentation) + size_t _unused; // Unused in last buffer + size_t _used; // derived = allocated - wasted - unused + size_t _desired_plab_sz;// output of filter (below), suitably trimmed and quantized + AdaptiveWeightedAverage + _filter; // integrator with decay + + public: + PLABStats(size_t desired_plab_sz_, unsigned wt) : + _allocated(0), + _wasted(0), + _unused(0), + _used(0), + _desired_plab_sz(desired_plab_sz_), + _filter(wt) + { + size_t min_sz = min_size(); + size_t max_sz = max_size(); + size_t aligned_min_sz = align_object_size(min_sz); + size_t aligned_max_sz = align_object_size(max_sz); + assert(min_sz <= aligned_min_sz && max_sz >= aligned_max_sz && + min_sz <= max_sz, + "PLAB clipping computation in adjust_desired_plab_sz()" + " may be incorrect"); + } + + static const size_t min_size() { + return ParGCAllocBuffer::min_size(); + } + + static const size_t max_size() { + return ParGCAllocBuffer::max_size(); + } + + size_t desired_plab_sz() { + return _desired_plab_sz; + } + + void adjust_desired_plab_sz(); // filter computation, latches output to + // _desired_plab_sz, clears sensor accumulators + + void add_allocated(size_t v) { + Atomic::add_ptr(v, &_allocated); + } + + void add_unused(size_t v) { + Atomic::add_ptr(v, &_unused); + } + + void add_wasted(size_t v) { + Atomic::add_ptr(v, &_wasted); + } +}; + +class ParGCAllocBufferWithBOT: public ParGCAllocBuffer { + BlockOffsetArrayContigSpace _bt; + BlockOffsetSharedArray* _bsa; + HeapWord* _true_end; // end of the whole ParGCAllocBuffer + + static const size_t ChunkSizeInWords; + static const size_t ChunkSizeInBytes; + HeapWord* allocate_slow(size_t word_sz); + + void fill_region_with_block(MemRegion mr, bool contig); + +public: + ParGCAllocBufferWithBOT(size_t word_sz, BlockOffsetSharedArray* bsa); + + HeapWord* allocate(size_t word_sz) { + HeapWord* res = ParGCAllocBuffer::allocate(word_sz); + if (res != NULL) { + _bt.alloc_block(res, word_sz); + } else { + res = allocate_slow(word_sz); + } + return res; + } + + void undo_allocation(HeapWord* obj, size_t word_sz); + + void set_buf(HeapWord* buf_start) { + ParGCAllocBuffer::set_buf(buf_start); + _true_end = _hard_end; + _bt.set_region(MemRegion(buf_start, word_sz())); + _bt.initialize_threshold(); + } + + void retire(bool end_of_gc, bool retain); + + MemRegion range() { + return MemRegion(_top, _true_end); + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp new file mode 100644 index 00000000000..603d6493fef --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp @@ -0,0 +1,1201 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_parNewGeneration.cpp.incl" + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable:4355 ) // 'this' : used in base member initializer list +#endif +ParScanThreadState::ParScanThreadState(Space* to_space_, + ParNewGeneration* gen_, + Generation* old_gen_, + int thread_num_, + ObjToScanQueueSet* work_queue_set_, + size_t desired_plab_sz_, + ParallelTaskTerminator& term_) : + _to_space(to_space_), _old_gen(old_gen_), _thread_num(thread_num_), + _work_queue(work_queue_set_->queue(thread_num_)), _to_space_full(false), + _ageTable(false), // false ==> not the global age table, no perf data. + _to_space_alloc_buffer(desired_plab_sz_), + _to_space_closure(gen_, this), _old_gen_closure(gen_, this), + _to_space_root_closure(gen_, this), _old_gen_root_closure(gen_, this), + _older_gen_closure(gen_, this), + _evacuate_followers(this, &_to_space_closure, &_old_gen_closure, + &_to_space_root_closure, gen_, &_old_gen_root_closure, + work_queue_set_, &term_), + _is_alive_closure(gen_), _scan_weak_ref_closure(gen_, this), + _keep_alive_closure(&_scan_weak_ref_closure), + _pushes(0), _pops(0), _steals(0), _steal_attempts(0), _term_attempts(0), + _strong_roots_time(0.0), _term_time(0.0) +{ + _survivor_chunk_array = + (ChunkArray*) old_gen()->get_data_recorder(thread_num()); + _hash_seed = 17; // Might want to take time-based random value. + _start = os::elapsedTime(); + _old_gen_closure.set_generation(old_gen_); + _old_gen_root_closure.set_generation(old_gen_); +} +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +void ParScanThreadState::record_survivor_plab(HeapWord* plab_start, + size_t plab_word_size) { + ChunkArray* sca = survivor_chunk_array(); + if (sca != NULL) { + // A non-null SCA implies that we want the PLAB data recorded. + sca->record_sample(plab_start, plab_word_size); + } +} + +bool ParScanThreadState::should_be_partially_scanned(oop new_obj, oop old_obj) const { + return new_obj->is_objArray() && + arrayOop(new_obj)->length() > ParGCArrayScanChunk && + new_obj != old_obj; +} + +void ParScanThreadState::scan_partial_array_and_push_remainder(oop old) { + assert(old->is_objArray(), "must be obj array"); + assert(old->is_forwarded(), "must be forwarded"); + assert(Universe::heap()->is_in_reserved(old), "must be in heap."); + assert(!_old_gen->is_in(old), "must be in young generation."); + + objArrayOop obj = objArrayOop(old->forwardee()); + // Process ParGCArrayScanChunk elements now + // and push the remainder back onto queue + int start = arrayOop(old)->length(); + int end = obj->length(); + int remainder = end - start; + assert(start <= end, "just checking"); + if (remainder > 2 * ParGCArrayScanChunk) { + // Test above combines last partial chunk with a full chunk + end = start + ParGCArrayScanChunk; + arrayOop(old)->set_length(end); + // Push remainder. + bool ok = work_queue()->push(old); + assert(ok, "just popped, push must be okay"); + note_push(); + } else { + // Restore length so that it can be used if there + // is a promotion failure and forwarding pointers + // must be removed. + arrayOop(old)->set_length(end); + } + // process our set of indices (include header in first chunk) + oop* start_addr = start == 0 ? (oop*)obj : obj->obj_at_addr(start); + oop* end_addr = obj->base() + end; // obj_at_addr(end) asserts end < length + MemRegion mr((HeapWord*)start_addr, (HeapWord*)end_addr); + if ((HeapWord *)obj < young_old_boundary()) { + // object is in to_space + obj->oop_iterate(&_to_space_closure, mr); + } else { + // object is in old generation + obj->oop_iterate(&_old_gen_closure, mr); + } +} + + +void ParScanThreadState::trim_queues(int max_size) { + ObjToScanQueue* queue = work_queue(); + while (queue->size() > (juint)max_size) { + oop obj_to_scan; + if (queue->pop_local(obj_to_scan)) { + note_pop(); + + if ((HeapWord *)obj_to_scan < young_old_boundary()) { + if (obj_to_scan->is_objArray() && + obj_to_scan->is_forwarded() && + obj_to_scan->forwardee() != obj_to_scan) { + scan_partial_array_and_push_remainder(obj_to_scan); + } else { + // object is in to_space + obj_to_scan->oop_iterate(&_to_space_closure); + } + } else { + // object is in old generation + obj_to_scan->oop_iterate(&_old_gen_closure); + } + } + } +} + +HeapWord* ParScanThreadState::alloc_in_to_space_slow(size_t word_sz) { + + // Otherwise, if the object is small enough, try to reallocate the + // buffer. + HeapWord* obj = NULL; + if (!_to_space_full) { + ParGCAllocBuffer* const plab = to_space_alloc_buffer(); + Space* const sp = to_space(); + if (word_sz * 100 < + ParallelGCBufferWastePct * plab->word_sz()) { + // Is small enough; abandon this buffer and start a new one. + plab->retire(false, false); + size_t buf_size = plab->word_sz(); + HeapWord* buf_space = sp->par_allocate(buf_size); + if (buf_space == NULL) { + const size_t min_bytes = + ParGCAllocBuffer::min_size() << LogHeapWordSize; + size_t free_bytes = sp->free(); + while(buf_space == NULL && free_bytes >= min_bytes) { + buf_size = free_bytes >> LogHeapWordSize; + assert(buf_size == (size_t)align_object_size(buf_size), + "Invariant"); + buf_space = sp->par_allocate(buf_size); + free_bytes = sp->free(); + } + } + if (buf_space != NULL) { + plab->set_word_size(buf_size); + plab->set_buf(buf_space); + record_survivor_plab(buf_space, buf_size); + obj = plab->allocate(word_sz); + // Note that we cannot compare buf_size < word_sz below + // because of AlignmentReserve (see ParGCAllocBuffer::allocate()). + assert(obj != NULL || plab->words_remaining() < word_sz, + "Else should have been able to allocate"); + // It's conceivable that we may be able to use the + // buffer we just grabbed for subsequent small requests + // even if not for this one. + } else { + // We're used up. + _to_space_full = true; + } + + } else { + // Too large; allocate the object individually. + obj = sp->par_allocate(word_sz); + } + } + return obj; +} + + +void ParScanThreadState::undo_alloc_in_to_space(HeapWord* obj, + size_t word_sz) { + // Is the alloc in the current alloc buffer? + if (to_space_alloc_buffer()->contains(obj)) { + assert(to_space_alloc_buffer()->contains(obj + word_sz - 1), + "Should contain whole object."); + to_space_alloc_buffer()->undo_allocation(obj, word_sz); + } else { + SharedHeap::fill_region_with_object(MemRegion(obj, word_sz)); + } +} + +class ParScanThreadStateSet: private ResourceArray { +public: + // Initializes states for the specified number of threads; + ParScanThreadStateSet(int num_threads, + Space& to_space, + ParNewGeneration& gen, + Generation& old_gen, + ObjToScanQueueSet& queue_set, + size_t desired_plab_sz, + ParallelTaskTerminator& term); + inline ParScanThreadState& thread_sate(int i); + int pushes() { return _pushes; } + int pops() { return _pops; } + int steals() { return _steals; } + void reset(); + void flush(); +private: + ParallelTaskTerminator& _term; + ParNewGeneration& _gen; + Generation& _next_gen; + // staticstics + int _pushes; + int _pops; + int _steals; +}; + + +ParScanThreadStateSet::ParScanThreadStateSet( + int num_threads, Space& to_space, ParNewGeneration& gen, + Generation& old_gen, ObjToScanQueueSet& queue_set, + size_t desired_plab_sz, ParallelTaskTerminator& term) + : ResourceArray(sizeof(ParScanThreadState), num_threads), + _gen(gen), _next_gen(old_gen), _term(term), + _pushes(0), _pops(0), _steals(0) +{ + assert(num_threads > 0, "sanity check!"); + // Initialize states. + for (int i = 0; i < num_threads; ++i) { + new ((ParScanThreadState*)_data + i) + ParScanThreadState(&to_space, &gen, &old_gen, i, &queue_set, + desired_plab_sz, term); + } +} + +inline ParScanThreadState& ParScanThreadStateSet::thread_sate(int i) +{ + assert(i >= 0 && i < length(), "sanity check!"); + return ((ParScanThreadState*)_data)[i]; +} + + +void ParScanThreadStateSet::reset() +{ + _term.reset_for_reuse(); +} + +void ParScanThreadStateSet::flush() +{ + for (int i = 0; i < length(); ++i) { + ParScanThreadState& par_scan_state = thread_sate(i); + + // Flush stats related to To-space PLAB activity and + // retire the last buffer. + par_scan_state.to_space_alloc_buffer()-> + flush_stats_and_retire(_gen.plab_stats(), + false /* !retain */); + + // Every thread has its own age table. We need to merge + // them all into one. + ageTable *local_table = par_scan_state.age_table(); + _gen.age_table()->merge(local_table); + + // Inform old gen that we're done. + _next_gen.par_promote_alloc_done(i); + _next_gen.par_oop_since_save_marks_iterate_done(i); + + // Flush stats related to work queue activity (push/pop/steal) + // This could conceivably become a bottleneck; if so, we'll put the + // stat's gathering under the flag. + if (PAR_STATS_ENABLED) { + _pushes += par_scan_state.pushes(); + _pops += par_scan_state.pops(); + _steals += par_scan_state.steals(); + if (ParallelGCVerbose) { + gclog_or_tty->print("Thread %d complete:\n" + " Pushes: %7d Pops: %7d Steals %7d (in %d attempts)\n", + i, par_scan_state.pushes(), par_scan_state.pops(), + par_scan_state.steals(), par_scan_state.steal_attempts()); + if (par_scan_state.overflow_pushes() > 0 || + par_scan_state.overflow_refills() > 0) { + gclog_or_tty->print(" Overflow pushes: %7d " + "Overflow refills: %7d for %d objs.\n", + par_scan_state.overflow_pushes(), + par_scan_state.overflow_refills(), + par_scan_state.overflow_refill_objs()); + } + + double elapsed = par_scan_state.elapsed(); + double strong_roots = par_scan_state.strong_roots_time(); + double term = par_scan_state.term_time(); + gclog_or_tty->print( + " Elapsed: %7.2f ms.\n" + " Strong roots: %7.2f ms (%6.2f%%)\n" + " Termination: %7.2f ms (%6.2f%%) (in %d entries)\n", + elapsed * 1000.0, + strong_roots * 1000.0, (strong_roots*100.0/elapsed), + term * 1000.0, (term*100.0/elapsed), + par_scan_state.term_attempts()); + } + } + } +} + + +ParScanClosure::ParScanClosure(ParNewGeneration* g, + ParScanThreadState* par_scan_state) : + OopsInGenClosure(g), _par_scan_state(par_scan_state), _g(g) +{ + assert(_g->level() == 0, "Optimized for youngest generation"); + _boundary = _g->reserved().end(); +} + +ParScanWeakRefClosure::ParScanWeakRefClosure(ParNewGeneration* g, + ParScanThreadState* par_scan_state) + : ScanWeakRefClosure(g), _par_scan_state(par_scan_state) +{ +} + +#ifdef WIN32 +#pragma warning(disable: 4786) /* identifier was truncated to '255' characters in the browser information */ +#endif + +ParEvacuateFollowersClosure::ParEvacuateFollowersClosure( + ParScanThreadState* par_scan_state_, + ParScanWithoutBarrierClosure* to_space_closure_, + ParScanWithBarrierClosure* old_gen_closure_, + ParRootScanWithoutBarrierClosure* to_space_root_closure_, + ParNewGeneration* par_gen_, + ParRootScanWithBarrierTwoGensClosure* old_gen_root_closure_, + ObjToScanQueueSet* task_queues_, + ParallelTaskTerminator* terminator_) : + + _par_scan_state(par_scan_state_), + _to_space_closure(to_space_closure_), + _old_gen_closure(old_gen_closure_), + _to_space_root_closure(to_space_root_closure_), + _old_gen_root_closure(old_gen_root_closure_), + _par_gen(par_gen_), + _task_queues(task_queues_), + _terminator(terminator_) +{} + +void ParEvacuateFollowersClosure::do_void() { + ObjToScanQueue* work_q = par_scan_state()->work_queue(); + + while (true) { + + // Scan to-space and old-gen objs until we run out of both. + oop obj_to_scan; + par_scan_state()->trim_queues(0); + + // We have no local work, attempt to steal from other threads. + + // attempt to steal work from promoted. + par_scan_state()->note_steal_attempt(); + if (task_queues()->steal(par_scan_state()->thread_num(), + par_scan_state()->hash_seed(), + obj_to_scan)) { + par_scan_state()->note_steal(); + bool res = work_q->push(obj_to_scan); + assert(res, "Empty queue should have room for a push."); + + par_scan_state()->note_push(); + // if successful, goto Start. + continue; + + // try global overflow list. + } else if (par_gen()->take_from_overflow_list(par_scan_state())) { + continue; + } + + // Otherwise, offer termination. + par_scan_state()->start_term_time(); + if (terminator()->offer_termination()) break; + par_scan_state()->end_term_time(); + } + // Finish the last termination pause. + par_scan_state()->end_term_time(); +} + +ParNewGenTask::ParNewGenTask(ParNewGeneration* gen, Generation* next_gen, + HeapWord* young_old_boundary, ParScanThreadStateSet* state_set) : + AbstractGangTask("ParNewGeneration collection"), + _gen(gen), _next_gen(next_gen), + _young_old_boundary(young_old_boundary), + _state_set(state_set) + {} + +void ParNewGenTask::work(int i) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + // Since this is being done in a separate thread, need new resource + // and handle marks. + ResourceMark rm; + HandleMark hm; + // We would need multiple old-gen queues otherwise. + guarantee(gch->n_gens() == 2, + "Par young collection currently only works with one older gen."); + + Generation* old_gen = gch->next_gen(_gen); + + ParScanThreadState& par_scan_state = _state_set->thread_sate(i); + par_scan_state.set_young_old_boundary(_young_old_boundary); + + par_scan_state.start_strong_roots(); + gch->gen_process_strong_roots(_gen->level(), + true, // Process younger gens, if any, + // as strong roots. + false,// not collecting perm generation. + SharedHeap::SO_AllClasses, + &par_scan_state.older_gen_closure(), + &par_scan_state.to_space_root_closure()); + par_scan_state.end_strong_roots(); + + // "evacuate followers". + par_scan_state.evacuate_followers_closure().do_void(); +} + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable:4355 ) // 'this' : used in base member initializer list +#endif +ParNewGeneration:: +ParNewGeneration(ReservedSpace rs, size_t initial_byte_size, int level) + : DefNewGeneration(rs, initial_byte_size, level, "PCopy"), + _overflow_list(NULL), + _is_alive_closure(this), + _plab_stats(YoungPLABSize, PLABWeight) +{ + _task_queues = new ObjToScanQueueSet(ParallelGCThreads); + guarantee(_task_queues != NULL, "task_queues allocation failure."); + + for (uint i1 = 0; i1 < ParallelGCThreads; i1++) { + ObjToScanQueuePadded *q_padded = new ObjToScanQueuePadded(); + guarantee(q_padded != NULL, "work_queue Allocation failure."); + + _task_queues->register_queue(i1, &q_padded->work_queue); + } + + for (uint i2 = 0; i2 < ParallelGCThreads; i2++) + _task_queues->queue(i2)->initialize(); + + if (UsePerfData) { + EXCEPTION_MARK; + ResourceMark rm; + + const char* cname = + PerfDataManager::counter_name(_gen_counters->name_space(), "threads"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None, + ParallelGCThreads, CHECK); + } +} +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +// ParNewGeneration:: +ParKeepAliveClosure::ParKeepAliveClosure(ParScanWeakRefClosure* cl) : + DefNewGeneration::KeepAliveClosure(cl), _par_cl(cl) {} + +void +// ParNewGeneration:: +ParKeepAliveClosure::do_oop(oop* p) { + // We never expect to see a null reference being processed + // as a weak reference. + assert (*p != NULL, "expected non-null ref"); + assert ((*p)->is_oop(), "expected an oop while scanning weak refs"); + + _par_cl->do_oop_nv(p); + + if (Universe::heap()->is_in_reserved(p)) { + _rs->write_ref_field_gc_par(p, *p); + } +} + +// ParNewGeneration:: +KeepAliveClosure::KeepAliveClosure(ScanWeakRefClosure* cl) : + DefNewGeneration::KeepAliveClosure(cl) {} + +void +// ParNewGeneration:: +KeepAliveClosure::do_oop(oop* p) { + // We never expect to see a null reference being processed + // as a weak reference. + assert (*p != NULL, "expected non-null ref"); + assert ((*p)->is_oop(), "expected an oop while scanning weak refs"); + + _cl->do_oop_nv(p); + + if (Universe::heap()->is_in_reserved(p)) { + _rs->write_ref_field_gc_par(p, *p); + } +} + +void ScanClosureWithParBarrier::do_oop(oop* p) { + oop obj = *p; + // Should we copy the obj? + if (obj != NULL) { + if ((HeapWord*)obj < _boundary) { + assert(!_g->to()->is_in_reserved(obj), "Scanning field twice?"); + if (obj->is_forwarded()) { + *p = obj->forwardee(); + } else { + *p = _g->DefNewGeneration::copy_to_survivor_space(obj, p); + } + } + if (_gc_barrier) { + // If p points to a younger generation, mark the card. + if ((HeapWord*)obj < _gen_boundary) { + _rs->write_ref_field_gc_par(p, obj); + } + } + } +} + +class ParNewRefProcTaskProxy: public AbstractGangTask { + typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask; +public: + ParNewRefProcTaskProxy(ProcessTask& task, ParNewGeneration& gen, + Generation& next_gen, + HeapWord* young_old_boundary, + ParScanThreadStateSet& state_set); + +private: + virtual void work(int i); + +private: + ParNewGeneration& _gen; + ProcessTask& _task; + Generation& _next_gen; + HeapWord* _young_old_boundary; + ParScanThreadStateSet& _state_set; +}; + +ParNewRefProcTaskProxy::ParNewRefProcTaskProxy( + ProcessTask& task, ParNewGeneration& gen, + Generation& next_gen, + HeapWord* young_old_boundary, + ParScanThreadStateSet& state_set) + : AbstractGangTask("ParNewGeneration parallel reference processing"), + _gen(gen), + _task(task), + _next_gen(next_gen), + _young_old_boundary(young_old_boundary), + _state_set(state_set) +{ +} + +void ParNewRefProcTaskProxy::work(int i) +{ + ResourceMark rm; + HandleMark hm; + ParScanThreadState& par_scan_state = _state_set.thread_sate(i); + par_scan_state.set_young_old_boundary(_young_old_boundary); + _task.work(i, par_scan_state.is_alive_closure(), + par_scan_state.keep_alive_closure(), + par_scan_state.evacuate_followers_closure()); +} + +class ParNewRefEnqueueTaskProxy: public AbstractGangTask { + typedef AbstractRefProcTaskExecutor::EnqueueTask EnqueueTask; + EnqueueTask& _task; + +public: + ParNewRefEnqueueTaskProxy(EnqueueTask& task) + : AbstractGangTask("ParNewGeneration parallel reference enqueue"), + _task(task) + { } + + virtual void work(int i) + { + _task.work(i); + } +}; + + +void ParNewRefProcTaskExecutor::execute(ProcessTask& task) +{ + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->kind() == CollectedHeap::GenCollectedHeap, + "not a generational heap"); + WorkGang* workers = gch->workers(); + assert(workers != NULL, "Need parallel worker threads."); + ParNewRefProcTaskProxy rp_task(task, _generation, *_generation.next_gen(), + _generation.reserved().end(), _state_set); + workers->run_task(&rp_task); + _state_set.reset(); +} + +void ParNewRefProcTaskExecutor::execute(EnqueueTask& task) +{ + GenCollectedHeap* gch = GenCollectedHeap::heap(); + WorkGang* workers = gch->workers(); + assert(workers != NULL, "Need parallel worker threads."); + ParNewRefEnqueueTaskProxy enq_task(task); + workers->run_task(&enq_task); +} + +void ParNewRefProcTaskExecutor::set_single_threaded_mode() +{ + _state_set.flush(); + GenCollectedHeap* gch = GenCollectedHeap::heap(); + gch->set_par_threads(0); // 0 ==> non-parallel. + gch->save_marks(); +} + +ScanClosureWithParBarrier:: +ScanClosureWithParBarrier(ParNewGeneration* g, bool gc_barrier) : + ScanClosure(g, gc_barrier) {} + +EvacuateFollowersClosureGeneral:: +EvacuateFollowersClosureGeneral(GenCollectedHeap* gch, int level, + OopsInGenClosure* cur, + OopsInGenClosure* older) : + _gch(gch), _level(level), + _scan_cur_or_nonheap(cur), _scan_older(older) +{} + +void EvacuateFollowersClosureGeneral::do_void() { + do { + // Beware: this call will lead to closure applications via virtual + // calls. + _gch->oop_since_save_marks_iterate(_level, + _scan_cur_or_nonheap, + _scan_older); + } while (!_gch->no_allocs_since_save_marks(_level)); +} + + +bool ParNewGeneration::_avoid_promotion_undo = false; + +void ParNewGeneration::adjust_desired_tenuring_threshold() { + // Set the desired survivor size to half the real survivor space + _tenuring_threshold = + age_table()->compute_tenuring_threshold(to()->capacity()/HeapWordSize); +} + +// A Generation that does parallel young-gen collection. + +void ParNewGeneration::collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool is_tlab) { + assert(full || size > 0, "otherwise we don't want to collect"); + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->kind() == CollectedHeap::GenCollectedHeap, + "not a CMS generational heap"); + AdaptiveSizePolicy* size_policy = gch->gen_policy()->size_policy(); + WorkGang* workers = gch->workers(); + _next_gen = gch->next_gen(this); + assert(_next_gen != NULL, + "This must be the youngest gen, and not the only gen"); + assert(gch->n_gens() == 2, + "Par collection currently only works with single older gen."); + // Do we have to avoid promotion_undo? + if (gch->collector_policy()->is_concurrent_mark_sweep_policy()) { + set_avoid_promotion_undo(true); + } + + // If the next generation is too full to accomodate worst-case promotion + // from this generation, pass on collection; let the next generation + // do it. + if (!collection_attempt_is_safe()) { + gch->set_incremental_collection_will_fail(); + return; + } + assert(to()->is_empty(), "Else not collection_attempt_is_safe"); + + init_assuming_no_promotion_failure(); + + if (UseAdaptiveSizePolicy) { + set_survivor_overflow(false); + size_policy->minor_collection_begin(); + } + + TraceTime t1("GC", PrintGC && !PrintGCDetails, true, gclog_or_tty); + // Capture heap used before collection (for printing). + size_t gch_prev_used = gch->used(); + + SpecializationStats::clear(); + + age_table()->clear(); + to()->clear(); + + gch->save_marks(); + assert(workers != NULL, "Need parallel worker threads."); + ParallelTaskTerminator _term(workers->total_workers(), task_queues()); + ParScanThreadStateSet thread_state_set(workers->total_workers(), + *to(), *this, *_next_gen, *task_queues(), + desired_plab_sz(), _term); + + ParNewGenTask tsk(this, _next_gen, reserved().end(), &thread_state_set); + int n_workers = workers->total_workers(); + gch->set_par_threads(n_workers); + gch->change_strong_roots_parity(); + gch->rem_set()->prepare_for_younger_refs_iterate(true); + // It turns out that even when we're using 1 thread, doing the work in a + // separate thread causes wide variance in run times. We can't help this + // in the multi-threaded case, but we special-case n=1 here to get + // repeatable measurements of the 1-thread overhead of the parallel code. + if (n_workers > 1) { + workers->run_task(&tsk); + } else { + tsk.work(0); + } + thread_state_set.reset(); + + if (PAR_STATS_ENABLED && ParallelGCVerbose) { + gclog_or_tty->print("Thread totals:\n" + " Pushes: %7d Pops: %7d Steals %7d (sum = %7d).\n", + thread_state_set.pushes(), thread_state_set.pops(), + thread_state_set.steals(), + thread_state_set.pops()+thread_state_set.steals()); + } + assert(thread_state_set.pushes() == thread_state_set.pops() + thread_state_set.steals(), + "Or else the queues are leaky."); + + // For now, process discovered weak refs sequentially. +#ifdef COMPILER2 + ReferencePolicy *soft_ref_policy = new LRUMaxHeapPolicy(); +#else + ReferencePolicy *soft_ref_policy = new LRUCurrentHeapPolicy(); +#endif // COMPILER2 + + // Process (weak) reference objects found during scavenge. + IsAliveClosure is_alive(this); + ScanWeakRefClosure scan_weak_ref(this); + KeepAliveClosure keep_alive(&scan_weak_ref); + ScanClosure scan_without_gc_barrier(this, false); + ScanClosureWithParBarrier scan_with_gc_barrier(this, true); + set_promo_failure_scan_stack_closure(&scan_without_gc_barrier); + EvacuateFollowersClosureGeneral evacuate_followers(gch, _level, + &scan_without_gc_barrier, &scan_with_gc_barrier); + if (ref_processor()->processing_is_mt()) { + ParNewRefProcTaskExecutor task_executor(*this, thread_state_set); + ref_processor()->process_discovered_references( + soft_ref_policy, &is_alive, &keep_alive, &evacuate_followers, + &task_executor); + } else { + thread_state_set.flush(); + gch->set_par_threads(0); // 0 ==> non-parallel. + gch->save_marks(); + ref_processor()->process_discovered_references( + soft_ref_policy, &is_alive, &keep_alive, &evacuate_followers, + NULL); + } + if (!promotion_failed()) { + // Swap the survivor spaces. + eden()->clear(); + from()->clear(); + swap_spaces(); + + assert(to()->is_empty(), "to space should be empty now"); + } else { + assert(HandlePromotionFailure, + "Should only be here if promotion failure handling is on"); + if (_promo_failure_scan_stack != NULL) { + // Can be non-null because of reference processing. + // Free stack with its elements. + delete _promo_failure_scan_stack; + _promo_failure_scan_stack = NULL; + } + remove_forwarding_pointers(); + if (PrintGCDetails) { + gclog_or_tty->print(" (promotion failed)"); + } + // All the spaces are in play for mark-sweep. + swap_spaces(); // Make life simpler for CMS || rescan; see 6483690. + from()->set_next_compaction_space(to()); + gch->set_incremental_collection_will_fail(); + } + // set new iteration safe limit for the survivor spaces + from()->set_concurrent_iteration_safe_limit(from()->top()); + to()->set_concurrent_iteration_safe_limit(to()->top()); + + adjust_desired_tenuring_threshold(); + if (ResizePLAB) { + plab_stats()->adjust_desired_plab_sz(); + } + + if (PrintGC && !PrintGCDetails) { + gch->print_heap_change(gch_prev_used); + } + + if (UseAdaptiveSizePolicy) { + size_policy->minor_collection_end(gch->gc_cause()); + size_policy->avg_survived()->sample(from()->used()); + } + + update_time_of_last_gc(os::javaTimeMillis()); + + SpecializationStats::print(); + + ref_processor()->set_enqueuing_is_done(true); + if (ref_processor()->processing_is_mt()) { + ParNewRefProcTaskExecutor task_executor(*this, thread_state_set); + ref_processor()->enqueue_discovered_references(&task_executor); + } else { + ref_processor()->enqueue_discovered_references(NULL); + } + ref_processor()->verify_no_references_recorded(); +} + +static int sum; +void ParNewGeneration::waste_some_time() { + for (int i = 0; i < 100; i++) { + sum += i; + } +} + +static const oop ClaimedForwardPtr = oop(0x4); + +// Because of concurrency, there are times where an object for which +// "is_forwarded()" is true contains an "interim" forwarding pointer +// value. Such a value will soon be overwritten with a real value. +// This method requires "obj" to have a forwarding pointer, and waits, if +// necessary for a real one to be inserted, and returns it. + +oop ParNewGeneration::real_forwardee(oop obj) { + oop forward_ptr = obj->forwardee(); + if (forward_ptr != ClaimedForwardPtr) { + return forward_ptr; + } else { + return real_forwardee_slow(obj); + } +} + +oop ParNewGeneration::real_forwardee_slow(oop obj) { + // Spin-read if it is claimed but not yet written by another thread. + oop forward_ptr = obj->forwardee(); + while (forward_ptr == ClaimedForwardPtr) { + waste_some_time(); + assert(obj->is_forwarded(), "precondition"); + forward_ptr = obj->forwardee(); + } + return forward_ptr; +} + +#ifdef ASSERT +bool ParNewGeneration::is_legal_forward_ptr(oop p) { + return + (_avoid_promotion_undo && p == ClaimedForwardPtr) + || Universe::heap()->is_in_reserved(p); +} +#endif + +void ParNewGeneration::preserve_mark_if_necessary(oop obj, markOop m) { + if ((m != markOopDesc::prototype()) && + (!UseBiasedLocking || (m != markOopDesc::biased_locking_prototype()))) { + MutexLocker ml(ParGCRareEvent_lock); + DefNewGeneration::preserve_mark_if_necessary(obj, m); + } +} + +// Multiple GC threads may try to promote an object. If the object +// is successfully promoted, a forwarding pointer will be installed in +// the object in the young generation. This method claims the right +// to install the forwarding pointer before it copies the object, +// thus avoiding the need to undo the copy as in +// copy_to_survivor_space_avoiding_with_undo. + +oop ParNewGeneration::copy_to_survivor_space_avoiding_promotion_undo( + ParScanThreadState* par_scan_state, oop old, size_t sz, markOop m) { + // In the sequential version, this assert also says that the object is + // not forwarded. That might not be the case here. It is the case that + // the caller observed it to be not forwarded at some time in the past. + assert(is_in_reserved(old), "shouldn't be scavenging this oop"); + + // The sequential code read "old->age()" below. That doesn't work here, + // since the age is in the mark word, and that might be overwritten with + // a forwarding pointer by a parallel thread. So we must save the mark + // word in a local and then analyze it. + oopDesc dummyOld; + dummyOld.set_mark(m); + assert(!dummyOld.is_forwarded(), + "should not be called with forwarding pointer mark word."); + + oop new_obj = NULL; + oop forward_ptr; + + // Try allocating obj in to-space (unless too old) + if (dummyOld.age() < tenuring_threshold()) { + new_obj = (oop)par_scan_state->alloc_in_to_space(sz); + if (new_obj == NULL) { + set_survivor_overflow(true); + } + } + + if (new_obj == NULL) { + // Either to-space is full or we decided to promote + // try allocating obj tenured + + // Attempt to install a null forwarding pointer (atomically), + // to claim the right to install the real forwarding pointer. + forward_ptr = old->forward_to_atomic(ClaimedForwardPtr); + if (forward_ptr != NULL) { + // someone else beat us to it. + return real_forwardee(old); + } + + new_obj = _next_gen->par_promote(par_scan_state->thread_num(), + old, m, sz); + + if (new_obj == NULL) { + if (!HandlePromotionFailure) { + // A failed promotion likely means the MaxLiveObjectEvacuationRatio flag + // is incorrectly set. In any case, its seriously wrong to be here! + vm_exit_out_of_memory(sz*wordSize, "promotion"); + } + // promotion failed, forward to self + _promotion_failed = true; + new_obj = old; + + preserve_mark_if_necessary(old, m); + } + + old->forward_to(new_obj); + forward_ptr = NULL; + } else { + // Is in to-space; do copying ourselves. + Copy::aligned_disjoint_words((HeapWord*)old, (HeapWord*)new_obj, sz); + forward_ptr = old->forward_to_atomic(new_obj); + // Restore the mark word copied above. + new_obj->set_mark(m); + // Increment age if obj still in new generation + new_obj->incr_age(); + par_scan_state->age_table()->add(new_obj, sz); + } + assert(new_obj != NULL, "just checking"); + + if (forward_ptr == NULL) { + oop obj_to_push = new_obj; + if (par_scan_state->should_be_partially_scanned(obj_to_push, old)) { + // Length field used as index of next element to be scanned. + // Real length can be obtained from real_forwardee() + arrayOop(old)->set_length(0); + obj_to_push = old; + assert(obj_to_push->is_forwarded() && obj_to_push->forwardee() != obj_to_push, + "push forwarded object"); + } + // Push it on one of the queues of to-be-scanned objects. + if (!par_scan_state->work_queue()->push(obj_to_push)) { + // Add stats for overflow pushes. + if (Verbose && PrintGCDetails) { + gclog_or_tty->print("queue overflow!\n"); + } + push_on_overflow_list(old); + par_scan_state->note_overflow_push(); + } + par_scan_state->note_push(); + + return new_obj; + } + + // Oops. Someone beat us to it. Undo the allocation. Where did we + // allocate it? + if (is_in_reserved(new_obj)) { + // Must be in to_space. + assert(to()->is_in_reserved(new_obj), "Checking"); + if (forward_ptr == ClaimedForwardPtr) { + // Wait to get the real forwarding pointer value. + forward_ptr = real_forwardee(old); + } + par_scan_state->undo_alloc_in_to_space((HeapWord*)new_obj, sz); + } + + return forward_ptr; +} + + +// Multiple GC threads may try to promote the same object. If two +// or more GC threads copy the object, only one wins the race to install +// the forwarding pointer. The other threads have to undo their copy. + +oop ParNewGeneration::copy_to_survivor_space_with_undo( + ParScanThreadState* par_scan_state, oop old, size_t sz, markOop m) { + + // In the sequential version, this assert also says that the object is + // not forwarded. That might not be the case here. It is the case that + // the caller observed it to be not forwarded at some time in the past. + assert(is_in_reserved(old), "shouldn't be scavenging this oop"); + + // The sequential code read "old->age()" below. That doesn't work here, + // since the age is in the mark word, and that might be overwritten with + // a forwarding pointer by a parallel thread. So we must save the mark + // word here, install it in a local oopDesc, and then analyze it. + oopDesc dummyOld; + dummyOld.set_mark(m); + assert(!dummyOld.is_forwarded(), + "should not be called with forwarding pointer mark word."); + + bool failed_to_promote = false; + oop new_obj = NULL; + oop forward_ptr; + + // Try allocating obj in to-space (unless too old) + if (dummyOld.age() < tenuring_threshold()) { + new_obj = (oop)par_scan_state->alloc_in_to_space(sz); + if (new_obj == NULL) { + set_survivor_overflow(true); + } + } + + if (new_obj == NULL) { + // Either to-space is full or we decided to promote + // try allocating obj tenured + new_obj = _next_gen->par_promote(par_scan_state->thread_num(), + old, m, sz); + + if (new_obj == NULL) { + if (!HandlePromotionFailure) { + // A failed promotion likely means the MaxLiveObjectEvacuationRatio + // flag is incorrectly set. In any case, its seriously wrong to be + // here! + vm_exit_out_of_memory(sz*wordSize, "promotion"); + } + // promotion failed, forward to self + forward_ptr = old->forward_to_atomic(old); + new_obj = old; + + if (forward_ptr != NULL) { + return forward_ptr; // someone else succeeded + } + + _promotion_failed = true; + failed_to_promote = true; + + preserve_mark_if_necessary(old, m); + } + } else { + // Is in to-space; do copying ourselves. + Copy::aligned_disjoint_words((HeapWord*)old, (HeapWord*)new_obj, sz); + // Restore the mark word copied above. + new_obj->set_mark(m); + // Increment age if new_obj still in new generation + new_obj->incr_age(); + par_scan_state->age_table()->add(new_obj, sz); + } + assert(new_obj != NULL, "just checking"); + + // Now attempt to install the forwarding pointer (atomically). + // We have to copy the mark word before overwriting with forwarding + // ptr, so we can restore it below in the copy. + if (!failed_to_promote) { + forward_ptr = old->forward_to_atomic(new_obj); + } + + if (forward_ptr == NULL) { + oop obj_to_push = new_obj; + if (par_scan_state->should_be_partially_scanned(obj_to_push, old)) { + // Length field used as index of next element to be scanned. + // Real length can be obtained from real_forwardee() + arrayOop(old)->set_length(0); + obj_to_push = old; + assert(obj_to_push->is_forwarded() && obj_to_push->forwardee() != obj_to_push, + "push forwarded object"); + } + // Push it on one of the queues of to-be-scanned objects. + if (!par_scan_state->work_queue()->push(obj_to_push)) { + // Add stats for overflow pushes. + push_on_overflow_list(old); + par_scan_state->note_overflow_push(); + } + par_scan_state->note_push(); + + return new_obj; + } + + // Oops. Someone beat us to it. Undo the allocation. Where did we + // allocate it? + if (is_in_reserved(new_obj)) { + // Must be in to_space. + assert(to()->is_in_reserved(new_obj), "Checking"); + par_scan_state->undo_alloc_in_to_space((HeapWord*)new_obj, sz); + } else { + assert(!_avoid_promotion_undo, "Should not be here if avoiding."); + _next_gen->par_promote_alloc_undo(par_scan_state->thread_num(), + (HeapWord*)new_obj, sz); + } + + return forward_ptr; +} + +void ParNewGeneration::push_on_overflow_list(oop from_space_obj) { + oop cur_overflow_list = _overflow_list; + // if the object has been forwarded to itself, then we cannot + // use the klass pointer for the linked list. Instead we have + // to allocate an oopDesc in the C-Heap and use that for the linked list. + if (from_space_obj->forwardee() == from_space_obj) { + oopDesc* listhead = NEW_C_HEAP_ARRAY(oopDesc, 1); + listhead->forward_to(from_space_obj); + from_space_obj = listhead; + } + while (true) { + from_space_obj->set_klass_to_list_ptr(cur_overflow_list); + oop observed_overflow_list = + (oop)Atomic::cmpxchg_ptr(from_space_obj, &_overflow_list, cur_overflow_list); + if (observed_overflow_list == cur_overflow_list) break; + // Otherwise... + cur_overflow_list = observed_overflow_list; + } +} + +bool +ParNewGeneration::take_from_overflow_list(ParScanThreadState* par_scan_state) { + ObjToScanQueue* work_q = par_scan_state->work_queue(); + // How many to take? + int objsFromOverflow = MIN2(work_q->max_elems()/4, + (juint)ParGCDesiredObjsFromOverflowList); + + if (_overflow_list == NULL) return false; + + // Otherwise, there was something there; try claiming the list. + oop prefix = (oop)Atomic::xchg_ptr(NULL, &_overflow_list); + + if (prefix == NULL) { + return false; + } + // Trim off a prefix of at most objsFromOverflow items + int i = 1; + oop cur = prefix; + while (i < objsFromOverflow && cur->klass() != NULL) { + i++; cur = oop(cur->klass()); + } + + // Reattach remaining (suffix) to overflow list + if (cur->klass() != NULL) { + oop suffix = oop(cur->klass()); + cur->set_klass_to_list_ptr(NULL); + + // Find last item of suffix list + oop last = suffix; + while (last->klass() != NULL) { + last = oop(last->klass()); + } + // Atomically prepend suffix to current overflow list + oop cur_overflow_list = _overflow_list; + while (true) { + last->set_klass_to_list_ptr(cur_overflow_list); + oop observed_overflow_list = + (oop)Atomic::cmpxchg_ptr(suffix, &_overflow_list, cur_overflow_list); + if (observed_overflow_list == cur_overflow_list) break; + // Otherwise... + cur_overflow_list = observed_overflow_list; + } + } + + // Push objects on prefix list onto this thread's work queue + assert(cur != NULL, "program logic"); + cur = prefix; + int n = 0; + while (cur != NULL) { + oop obj_to_push = cur->forwardee(); + oop next = oop(cur->klass()); + cur->set_klass(obj_to_push->klass()); + if (par_scan_state->should_be_partially_scanned(obj_to_push, cur)) { + obj_to_push = cur; + assert(arrayOop(cur)->length() == 0, "entire array remaining to be scanned"); + } + work_q->push(obj_to_push); + cur = next; + n++; + } + par_scan_state->note_overflow_refill(n); + return true; +} + +void ParNewGeneration::ref_processor_init() +{ + if (_ref_processor == NULL) { + // Allocate and initialize a reference processor + _ref_processor = ReferenceProcessor::create_ref_processor( + _reserved, // span + refs_discovery_is_atomic(), // atomic_discovery + refs_discovery_is_mt(), // mt_discovery + NULL, // is_alive_non_header + ParallelGCThreads, + ParallelRefProcEnabled); + } +} + +const char* ParNewGeneration::name() const { + return "par new generation"; +} diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp new file mode 100644 index 00000000000..a41548b1a9e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp @@ -0,0 +1,388 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ChunkArray; +class ParScanWithoutBarrierClosure; +class ParScanWithBarrierClosure; +class ParRootScanWithoutBarrierClosure; +class ParRootScanWithBarrierTwoGensClosure; +class ParEvacuateFollowersClosure; + +// It would be better if these types could be kept local to the .cpp file, +// but they must be here to allow ParScanClosure::do_oop_work to be defined +// in genOopClosures.inline.hpp. + + +typedef OopTaskQueue ObjToScanQueue; +typedef OopTaskQueueSet ObjToScanQueueSet; + +// Enable this to get push/pop/steal stats. +const int PAR_STATS_ENABLED = 0; + +class ParKeepAliveClosure: public DefNewGeneration::KeepAliveClosure { + ParScanWeakRefClosure* _par_cl; + public: + ParKeepAliveClosure(ParScanWeakRefClosure* cl); + void do_oop(oop* p); +}; + +// The state needed by thread performing parallel young-gen collection. +class ParScanThreadState { + friend class ParScanThreadStateSet; + ObjToScanQueue *_work_queue; + + ParGCAllocBuffer _to_space_alloc_buffer; + + ParScanWithoutBarrierClosure _to_space_closure; // scan_without_gc_barrier + ParScanWithBarrierClosure _old_gen_closure; // scan_with_gc_barrier + ParRootScanWithoutBarrierClosure _to_space_root_closure; // scan_root_without_gc_barrier + // One of these two will be passed to process_strong_roots, which will + // set its generation. The first is for two-gen configs where the + // old gen collects the perm gen; the second is for arbitrary configs. + // The second isn't used right now (it used to be used for the train, an + // incremental collector) but the declaration has been left as a reminder. + ParRootScanWithBarrierTwoGensClosure _older_gen_closure; + // This closure will always be bound to the old gen; it will be used + // in evacuate_followers. + ParRootScanWithBarrierTwoGensClosure _old_gen_root_closure; // scan_old_root_with_gc_barrier + ParEvacuateFollowersClosure _evacuate_followers; + DefNewGeneration::IsAliveClosure _is_alive_closure; + ParScanWeakRefClosure _scan_weak_ref_closure; + ParKeepAliveClosure _keep_alive_closure; + + + Space* _to_space; + Space* to_space() { return _to_space; } + + Generation* _old_gen; + Generation* old_gen() { return _old_gen; } + + HeapWord *_young_old_boundary; + + int _hash_seed; + int _thread_num; + ageTable _ageTable; + + bool _to_space_full; + + int _pushes, _pops, _steals, _steal_attempts, _term_attempts; + int _overflow_pushes, _overflow_refills, _overflow_refill_objs; + + // Timing numbers. + double _start; + double _start_strong_roots; + double _strong_roots_time; + double _start_term; + double _term_time; + + // Helper for trim_queues. Scans subset of an array and makes + // remainder available for work stealing. + void scan_partial_array_and_push_remainder(oop obj); + + // In support of CMS' parallel rescan of survivor space. + ChunkArray* _survivor_chunk_array; + ChunkArray* survivor_chunk_array() { return _survivor_chunk_array; } + + void record_survivor_plab(HeapWord* plab_start, size_t plab_word_size); + + ParScanThreadState(Space* to_space_, ParNewGeneration* gen_, + Generation* old_gen_, int thread_num_, + ObjToScanQueueSet* work_queue_set_, size_t desired_plab_sz_, + ParallelTaskTerminator& term_); + +public: + ageTable* age_table() {return &_ageTable;} + + ObjToScanQueue* work_queue() { return _work_queue; } + + ParGCAllocBuffer* to_space_alloc_buffer() { + return &_to_space_alloc_buffer; + } + + ParEvacuateFollowersClosure& evacuate_followers_closure() { return _evacuate_followers; } + DefNewGeneration::IsAliveClosure& is_alive_closure() { return _is_alive_closure; } + ParScanWeakRefClosure& scan_weak_ref_closure() { return _scan_weak_ref_closure; } + ParKeepAliveClosure& keep_alive_closure() { return _keep_alive_closure; } + ParScanClosure& older_gen_closure() { return _older_gen_closure; } + ParRootScanWithoutBarrierClosure& to_space_root_closure() { return _to_space_root_closure; }; + + // Decrease queue size below "max_size". + void trim_queues(int max_size); + + // Is new_obj a candidate for scan_partial_array_and_push_remainder method. + inline bool should_be_partially_scanned(oop new_obj, oop old_obj) const; + + int* hash_seed() { return &_hash_seed; } + int thread_num() { return _thread_num; } + + // Allocate a to-space block of size "sz", or else return NULL. + HeapWord* alloc_in_to_space_slow(size_t word_sz); + + HeapWord* alloc_in_to_space(size_t word_sz) { + HeapWord* obj = to_space_alloc_buffer()->allocate(word_sz); + if (obj != NULL) return obj; + else return alloc_in_to_space_slow(word_sz); + } + + HeapWord* young_old_boundary() { return _young_old_boundary; } + + void set_young_old_boundary(HeapWord *boundary) { + _young_old_boundary = boundary; + } + + // Undo the most recent allocation ("obj", of "word_sz"). + void undo_alloc_in_to_space(HeapWord* obj, size_t word_sz); + + int pushes() { return _pushes; } + int pops() { return _pops; } + int steals() { return _steals; } + int steal_attempts() { return _steal_attempts; } + int term_attempts() { return _term_attempts; } + int overflow_pushes() { return _overflow_pushes; } + int overflow_refills() { return _overflow_refills; } + int overflow_refill_objs() { return _overflow_refill_objs; } + + void note_push() { if (PAR_STATS_ENABLED) _pushes++; } + void note_pop() { if (PAR_STATS_ENABLED) _pops++; } + void note_steal() { if (PAR_STATS_ENABLED) _steals++; } + void note_steal_attempt() { if (PAR_STATS_ENABLED) _steal_attempts++; } + void note_term_attempt() { if (PAR_STATS_ENABLED) _term_attempts++; } + void note_overflow_push() { if (PAR_STATS_ENABLED) _overflow_pushes++; } + void note_overflow_refill(int objs) { + if (PAR_STATS_ENABLED) { + _overflow_refills++; + _overflow_refill_objs += objs; + } + } + + void start_strong_roots() { + _start_strong_roots = os::elapsedTime(); + } + void end_strong_roots() { + _strong_roots_time += (os::elapsedTime() - _start_strong_roots); + } + double strong_roots_time() { return _strong_roots_time; } + void start_term_time() { + note_term_attempt(); + _start_term = os::elapsedTime(); + } + void end_term_time() { + _term_time += (os::elapsedTime() - _start_term); + } + double term_time() { return _term_time; } + + double elapsed() { + return os::elapsedTime() - _start; + } + +}; + +class ParNewGenTask: public AbstractGangTask { + ParNewGeneration* _gen; + Generation* _next_gen; + HeapWord* _young_old_boundary; + class ParScanThreadStateSet* _state_set; + +public: + ParNewGenTask(ParNewGeneration* gen, + Generation* next_gen, + HeapWord* young_old_boundary, + ParScanThreadStateSet* state_set); + + HeapWord* young_old_boundary() { return _young_old_boundary; } + + void work(int i); +}; + +class KeepAliveClosure: public DefNewGeneration::KeepAliveClosure { + public: + KeepAliveClosure(ScanWeakRefClosure* cl); + void do_oop(oop* p); +}; + +class EvacuateFollowersClosureGeneral: public VoidClosure { + GenCollectedHeap* _gch; + int _level; + OopsInGenClosure* _scan_cur_or_nonheap; + OopsInGenClosure* _scan_older; + public: + EvacuateFollowersClosureGeneral(GenCollectedHeap* gch, int level, + OopsInGenClosure* cur, + OopsInGenClosure* older); + void do_void(); +}; + +// Closure for scanning ParNewGeneration. +// Same as ScanClosure, except does parallel GC barrier. +class ScanClosureWithParBarrier: public ScanClosure { +public: + ScanClosureWithParBarrier(ParNewGeneration* g, bool gc_barrier); + void do_oop(oop* p); +}; + +// Implements AbstractRefProcTaskExecutor for ParNew. +class ParNewRefProcTaskExecutor: public AbstractRefProcTaskExecutor { +public: + + ParNewRefProcTaskExecutor(ParNewGeneration& generation, + ParScanThreadStateSet& state_set) + : _generation(generation), _state_set(state_set) + { } + + // Executes a task using worker threads. + virtual void execute(ProcessTask& task); + virtual void execute(EnqueueTask& task); + // Switch to single threaded mode. + virtual void set_single_threaded_mode(); +private: + ParNewGeneration& _generation; + ParScanThreadStateSet& _state_set; +}; + + +// A Generation that does parallel young-gen collection. + +class ParNewGeneration: public DefNewGeneration { + friend class ParNewGenTask; + friend class ParNewRefProcTask; + friend class ParNewRefProcTaskExecutor; + friend class ParScanThreadStateSet; + + // XXX use a global constant instead of 64! + struct ObjToScanQueuePadded { + ObjToScanQueue work_queue; + char pad[64 - sizeof(ObjToScanQueue)]; // prevent false sharing + }; + + // The per-thread work queues, available here for stealing. + ObjToScanQueueSet* _task_queues; + + // Desired size of survivor space plab's + PLABStats _plab_stats; + + // A list of from-space images of to-be-scanned objects, threaded through + // klass-pointers (klass information already copied to the forwarded + // image.) Manipulated with CAS. + oop _overflow_list; + + // If true, older generation does not support promotion undo, so avoid. + static bool _avoid_promotion_undo; + + // This closure is used by the reference processor to filter out + // references to live referent. + DefNewGeneration::IsAliveClosure _is_alive_closure; + + static oop real_forwardee_slow(oop obj); + static void waste_some_time(); + + // Preserve the mark of "obj", if necessary, in preparation for its mark + // word being overwritten with a self-forwarding-pointer. + void preserve_mark_if_necessary(oop obj, markOop m); + + protected: + + bool _survivor_overflow; + + bool avoid_promotion_undo() { return _avoid_promotion_undo; } + void set_avoid_promotion_undo(bool v) { _avoid_promotion_undo = v; } + + bool survivor_overflow() { return _survivor_overflow; } + void set_survivor_overflow(bool v) { _survivor_overflow = v; } + + // Adjust the tenuring threshold. See the implementation for + // the details of the policy. + virtual void adjust_desired_tenuring_threshold(); + +public: + ParNewGeneration(ReservedSpace rs, size_t initial_byte_size, int level); + + ~ParNewGeneration() { + for (uint i = 0; i < ParallelGCThreads; i++) + delete _task_queues->queue(i); + + delete _task_queues; + } + + virtual void ref_processor_init(); + virtual Generation::Name kind() { return Generation::ParNew; } + virtual const char* name() const; + virtual const char* short_name() const { return "ParNew"; } + + // override + virtual bool refs_discovery_is_mt() const { + assert(UseParNewGC, "ParNewGeneration only when UseParNewGC"); + return ParallelGCThreads > 1; + } + + // Make the collection virtual. + virtual void collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool is_tlab); + + // This needs to be visible to the closure function. + // "obj" is the object to be copied, "m" is a recent value of its mark + // that must not contain a forwarding pointer (though one might be + // inserted in "obj"s mark word by a parallel thread). + inline oop copy_to_survivor_space(ParScanThreadState* par_scan_state, + oop obj, size_t obj_sz, markOop m) { + if (_avoid_promotion_undo) { + return copy_to_survivor_space_avoiding_promotion_undo(par_scan_state, + obj, obj_sz, m); + } + + return copy_to_survivor_space_with_undo(par_scan_state, obj, obj_sz, m); + } + + oop copy_to_survivor_space_avoiding_promotion_undo(ParScanThreadState* par_scan_state, + oop obj, size_t obj_sz, markOop m); + + oop copy_to_survivor_space_with_undo(ParScanThreadState* par_scan_state, + oop obj, size_t obj_sz, markOop m); + + // Push the given (from-space) object on the global overflow list. + void push_on_overflow_list(oop from_space_obj); + + // If the global overflow list is non-empty, move some tasks from it + // onto "work_q" (which must be empty). No more than 1/4 of the + // max_elems of "work_q" are moved. + bool take_from_overflow_list(ParScanThreadState* par_scan_state); + + // The task queues to be used by parallel GC threads. + ObjToScanQueueSet* task_queues() { + return _task_queues; + } + + PLABStats* plab_stats() { + return &_plab_stats; + } + + size_t desired_plab_sz() { + return _plab_stats.desired_plab_sz(); + } + + static oop real_forwardee(oop obj); + + DEBUG_ONLY(static bool is_legal_forward_ptr(oop p);) +}; diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.hpp b/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.hpp new file mode 100644 index 00000000000..463127f1a7d --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Closures for ParNewGeneration + +class ParScanThreadState; +class ParNewGeneration; +template class GenericTaskQueueSet; +typedef GenericTaskQueueSet ObjToScanQueueSet; +class ParallelTaskTerminator; + +class ParScanClosure: public OopsInGenClosure { +protected: + ParScanThreadState* _par_scan_state; + ParNewGeneration* _g; + HeapWord* _boundary; + void do_oop_work(oop* p, + bool gc_barrier, + bool root_scan); + + void par_do_barrier(oop* p); + +public: + ParScanClosure(ParNewGeneration* g, ParScanThreadState* par_scan_state); +}; + +class ParScanWithBarrierClosure: public ParScanClosure { +public: + void do_oop(oop* p) { do_oop_work(p, true, false); } + void do_oop_nv(oop* p) { do_oop_work(p, true, false); } + ParScanWithBarrierClosure(ParNewGeneration* g, + ParScanThreadState* par_scan_state) : + ParScanClosure(g, par_scan_state) {} +}; + +class ParScanWithoutBarrierClosure: public ParScanClosure { +public: + ParScanWithoutBarrierClosure(ParNewGeneration* g, + ParScanThreadState* par_scan_state) : + ParScanClosure(g, par_scan_state) {} + void do_oop(oop* p) { do_oop_work(p, false, false); } + void do_oop_nv(oop* p) { do_oop_work(p, false, false); } +}; + +class ParRootScanWithBarrierTwoGensClosure: public ParScanClosure { +public: + ParRootScanWithBarrierTwoGensClosure(ParNewGeneration* g, + ParScanThreadState* par_scan_state) : + ParScanClosure(g, par_scan_state) {} + void do_oop(oop* p) { do_oop_work(p, true, true); } +}; + +class ParRootScanWithoutBarrierClosure: public ParScanClosure { +public: + ParRootScanWithoutBarrierClosure(ParNewGeneration* g, + ParScanThreadState* par_scan_state) : + ParScanClosure(g, par_scan_state) {} + void do_oop(oop* p) { do_oop_work(p, false, true); } +}; + +class ParScanWeakRefClosure: public ScanWeakRefClosure { +protected: + ParScanThreadState* _par_scan_state; +public: + ParScanWeakRefClosure(ParNewGeneration* g, + ParScanThreadState* par_scan_state); + void do_oop(oop* p); + void do_oop_nv(oop* p); +}; + +class ParEvacuateFollowersClosure: public VoidClosure { + ParScanThreadState* _par_scan_state; + ParScanThreadState* par_scan_state() { return _par_scan_state; } + + // We want to preserve the specific types here (rather than "OopClosure") + // for later de-virtualization of do_oop calls. + ParScanWithoutBarrierClosure* _to_space_closure; + ParScanWithoutBarrierClosure* to_space_closure() { + return _to_space_closure; + } + ParRootScanWithoutBarrierClosure* _to_space_root_closure; + ParRootScanWithoutBarrierClosure* to_space_root_closure() { + return _to_space_root_closure; + } + + ParScanWithBarrierClosure* _old_gen_closure; + ParScanWithBarrierClosure* old_gen_closure () { + return _old_gen_closure; + } + ParRootScanWithBarrierTwoGensClosure* _old_gen_root_closure; + ParRootScanWithBarrierTwoGensClosure* old_gen_root_closure () { + return _old_gen_root_closure; + } + + ParNewGeneration* _par_gen; + ParNewGeneration* par_gen() { return _par_gen; } + + ObjToScanQueueSet* _task_queues; + ObjToScanQueueSet* task_queues() { return _task_queues; } + + ParallelTaskTerminator* _terminator; + ParallelTaskTerminator* terminator() { return _terminator; } + +public: + ParEvacuateFollowersClosure( + ParScanThreadState* par_scan_state_, + ParScanWithoutBarrierClosure* to_space_closure_, + ParScanWithBarrierClosure* old_gen_closure_, + ParRootScanWithoutBarrierClosure* to_space_root_closure_, + ParNewGeneration* par_gen_, + ParRootScanWithBarrierTwoGensClosure* old_gen_root_closure_, + ObjToScanQueueSet* task_queues_, + ParallelTaskTerminator* terminator_); + void do_void(); +}; diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp b/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp new file mode 100644 index 00000000000..3b38b3a2df5 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void ParScanWeakRefClosure::do_oop(oop* p) +{ + oop obj = *p; + assert (obj != NULL, "null weak reference?"); + // weak references are sometimes scanned twice; must check + // that to-space doesn't already contain this object + if ((HeapWord*)obj < _boundary && !_g->to()->is_in_reserved(obj)) { + // we need to ensure that it is copied (see comment in + // ParScanClosure::do_oop_work). + klassOop objK = obj->klass(); + markOop m = obj->mark(); + if (m->is_marked()) { // Contains forwarding pointer. + *p = ParNewGeneration::real_forwardee(obj); + } else { + size_t obj_sz = obj->size_given_klass(objK->klass_part()); + *p = ((ParNewGeneration*)_g)->copy_to_survivor_space(_par_scan_state, + obj, obj_sz, m); + } + } +} + +inline void ParScanWeakRefClosure::do_oop_nv(oop* p) +{ + ParScanWeakRefClosure::do_oop(p); +} + +inline void ParScanClosure::par_do_barrier(oop* p) { + assert(generation()->is_in_reserved(p), "expected ref in generation"); + oop obj = *p; + assert(obj != NULL, "expected non-null object"); + // If p points to a younger generation, mark the card. + if ((HeapWord*)obj < gen_boundary()) { + rs()->write_ref_field_gc_par(p, obj); + } +} + +inline void ParScanClosure::do_oop_work(oop* p, + bool gc_barrier, + bool root_scan) { + oop obj = *p; + assert((!Universe::heap()->is_in_reserved(p) || + generation()->is_in_reserved(p)) + && (generation()->level() == 0 || gc_barrier), + "The gen must be right, and we must be doing the barrier " + "in older generations."); + if (obj != NULL) { + if ((HeapWord*)obj < _boundary) { + assert(!_g->to()->is_in_reserved(obj), "Scanning field twice?"); + // OK, we need to ensure that it is copied. + // We read the klass and mark in this order, so that we can reliably + // get the size of the object: if the mark we read is not a + // forwarding pointer, then the klass is valid: the klass is only + // overwritten with an overflow next pointer after the object is + // forwarded. + klassOop objK = obj->klass(); + markOop m = obj->mark(); + if (m->is_marked()) { // Contains forwarding pointer. + *p = ParNewGeneration::real_forwardee(obj); + } else { + size_t obj_sz = obj->size_given_klass(objK->klass_part()); + *p = _g->copy_to_survivor_space(_par_scan_state, obj, obj_sz, m); + if (root_scan) { + // This may have pushed an object. If we have a root + // category with a lot of roots, can't let the queue get too + // full: + (void)_par_scan_state->trim_queues(10 * ParallelGCThreads); + } + } + if (gc_barrier) { + // Now call parent closure + par_do_barrier(p); + } + } + } +} diff --git a/hotspot/src/share/vm/gc_implementation/parNew/vmStructs_parNew.hpp b/hotspot/src/share/vm/gc_implementation/parNew/vmStructs_parNew.hpp new file mode 100644 index 00000000000..7c1d08dcbf1 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parNew/vmStructs_parNew.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#define VM_TYPES_PARNEW(declare_type) \ + declare_type(ParNewGeneration, DefNewGeneration) + +#define VM_INT_CONSTANTS_PARNEW(declare_constant) \ + declare_constant(Generation::ParNew) diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningGenerations.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningGenerations.cpp new file mode 100644 index 00000000000..61500e19143 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningGenerations.cpp @@ -0,0 +1,278 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_adjoiningGenerations.cpp.incl" + +// If boundary moving is being used, create the young gen and old +// gen with ASPSYoungGen and ASPSOldGen, respectively. Revert to +// the old behavior otherwise (with PSYoungGen and PSOldGen). + +AdjoiningGenerations::AdjoiningGenerations(ReservedSpace old_young_rs, + size_t init_low_byte_size, + size_t min_low_byte_size, + size_t max_low_byte_size, + size_t init_high_byte_size, + size_t min_high_byte_size, + size_t max_high_byte_size, + size_t alignment) : + _virtual_spaces(old_young_rs, min_low_byte_size, + min_high_byte_size, alignment) { + assert(min_low_byte_size <= init_low_byte_size && + init_low_byte_size <= max_low_byte_size, "Parameter check"); + assert(min_high_byte_size <= init_high_byte_size && + init_high_byte_size <= max_high_byte_size, "Parameter check"); + // Create the generations differently based on the option to + // move the boundary. + if (UseAdaptiveGCBoundary) { + // Initialize the adjoining virtual spaces. Then pass the + // a virtual to each generation for initialization of the + // generation. + + // Does the actual creation of the virtual spaces + _virtual_spaces.initialize(max_low_byte_size, + init_low_byte_size, + init_high_byte_size); + + // Place the young gen at the high end. Passes in the virtual space. + _young_gen = new ASPSYoungGen(_virtual_spaces.high(), + _virtual_spaces.high()->committed_size(), + min_high_byte_size, + _virtual_spaces.high_byte_size_limit()); + + // Place the old gen at the low end. Passes in the virtual space. + _old_gen = new ASPSOldGen(_virtual_spaces.low(), + _virtual_spaces.low()->committed_size(), + min_low_byte_size, + _virtual_spaces.low_byte_size_limit(), + "old", 1); + + young_gen()->initialize_work(); + assert(young_gen()->reserved().byte_size() <= young_gen()->gen_size_limit(), + "Consistency check"); + assert(old_young_rs.size() >= young_gen()->gen_size_limit(), + "Consistency check"); + + old_gen()->initialize_work("old", 1); + assert(old_gen()->reserved().byte_size() <= old_gen()->gen_size_limit(), + "Consistency check"); + assert(old_young_rs.size() >= old_gen()->gen_size_limit(), + "Consistency check"); + } else { + + // Layout the reserved space for the generations. + ReservedSpace old_rs = + virtual_spaces()->reserved_space().first_part(max_low_byte_size); + ReservedSpace heap_rs = + virtual_spaces()->reserved_space().last_part(max_low_byte_size); + ReservedSpace young_rs = heap_rs.first_part(max_high_byte_size); + assert(young_rs.size() == heap_rs.size(), "Didn't reserve all of the heap"); + + // Create the generations. Virtual spaces are not passed in. + _young_gen = new PSYoungGen(init_high_byte_size, + min_high_byte_size, + max_high_byte_size); + _old_gen = new PSOldGen(init_low_byte_size, + min_low_byte_size, + max_low_byte_size, + "old", 1); + + // The virtual spaces are created by the initialization of the gens. + _young_gen->initialize(young_rs, alignment); + assert(young_gen()->gen_size_limit() == young_rs.size(), + "Consistency check"); + _old_gen->initialize(old_rs, alignment, "old", 1); + assert(old_gen()->gen_size_limit() == old_rs.size(), "Consistency check"); + } +} + +size_t AdjoiningGenerations::reserved_byte_size() { + return virtual_spaces()->reserved_space().size(); +} + + +// Make checks on the current sizes of the generations and +// the contraints on the sizes of the generations. Push +// up the boundary within the contraints. A partial +// push can occur. +void AdjoiningGenerations::request_old_gen_expansion(size_t expand_in_bytes) { + assert(UseAdaptiveSizePolicy && UseAdaptiveGCBoundary, "runtime check"); + + assert_lock_strong(ExpandHeap_lock); + assert_locked_or_safepoint(Heap_lock); + + // These sizes limit the amount the boundaries can move. Effectively, + // the generation says how much it is willing to yield to the other + // generation. + const size_t young_gen_available = young_gen()->available_for_contraction(); + const size_t old_gen_available = old_gen()->available_for_expansion(); + const size_t alignment = virtual_spaces()->alignment(); + size_t change_in_bytes = MIN3(young_gen_available, + old_gen_available, + align_size_up_(expand_in_bytes, alignment)); + + if (change_in_bytes == 0) { + return; + } + + if (TraceAdaptiveGCBoundary) { + gclog_or_tty->print_cr("Before expansion of old gen with boundary move"); + gclog_or_tty->print_cr(" Requested change: 0x%x Attempted change: 0x%x", + expand_in_bytes, change_in_bytes); + if (!PrintHeapAtGC) { + Universe::print_on(gclog_or_tty); + } + gclog_or_tty->print_cr(" PSOldGen max size: " SIZE_FORMAT "K", + old_gen()->max_gen_size()/K); + } + + // Move the boundary between the generations up (smaller young gen). + if (virtual_spaces()->adjust_boundary_up(change_in_bytes)) { + young_gen()->reset_after_change(); + old_gen()->reset_after_change(); + } + + // The total reserved for the generations should match the sum + // of the two even if the boundary is moving. + assert(reserved_byte_size() == + old_gen()->max_gen_size() + young_gen()->max_size(), + "Space is missing"); + young_gen()->space_invariants(); + old_gen()->space_invariants(); + + if (TraceAdaptiveGCBoundary) { + gclog_or_tty->print_cr("After expansion of old gen with boundary move"); + if (!PrintHeapAtGC) { + Universe::print_on(gclog_or_tty); + } + gclog_or_tty->print_cr(" PSOldGen max size: " SIZE_FORMAT "K", + old_gen()->max_gen_size()/K); + } +} + +// See comments on request_old_gen_expansion() +bool AdjoiningGenerations::request_young_gen_expansion(size_t expand_in_bytes) { + assert(UseAdaptiveSizePolicy && UseAdaptiveGCBoundary, "runtime check"); + + // If eden is not empty, the boundary can be moved but no advantage + // can be made of the move since eden cannot be moved. + if (!young_gen()->eden_space()->is_empty()) { + return false; + } + + + bool result = false; + const size_t young_gen_available = young_gen()->available_for_expansion(); + const size_t old_gen_available = old_gen()->available_for_contraction(); + const size_t alignment = virtual_spaces()->alignment(); + size_t change_in_bytes = MIN3(young_gen_available, + old_gen_available, + align_size_up_(expand_in_bytes, alignment)); + + if (change_in_bytes == 0) { + return false; + } + + if (TraceAdaptiveGCBoundary) { + gclog_or_tty->print_cr("Before expansion of young gen with boundary move"); + gclog_or_tty->print_cr(" Requested change: 0x%x Attempted change: 0x%x", + expand_in_bytes, change_in_bytes); + if (!PrintHeapAtGC) { + Universe::print_on(gclog_or_tty); + } + gclog_or_tty->print_cr(" PSYoungGen max size: " SIZE_FORMAT "K", + young_gen()->max_size()/K); + } + + // Move the boundary between the generations down (smaller old gen). + MutexLocker x(ExpandHeap_lock); + if (virtual_spaces()->adjust_boundary_down(change_in_bytes)) { + young_gen()->reset_after_change(); + old_gen()->reset_after_change(); + result = true; + } + + // The total reserved for the generations should match the sum + // of the two even if the boundary is moving. + assert(reserved_byte_size() == + old_gen()->max_gen_size() + young_gen()->max_size(), + "Space is missing"); + young_gen()->space_invariants(); + old_gen()->space_invariants(); + + if (TraceAdaptiveGCBoundary) { + gclog_or_tty->print_cr("After expansion of young gen with boundary move"); + if (!PrintHeapAtGC) { + Universe::print_on(gclog_or_tty); + } + gclog_or_tty->print_cr(" PSYoungGen max size: " SIZE_FORMAT "K", + young_gen()->max_size()/K); + } + + return result; +} + +// Additional space is needed in the old generation. Try to move the boundary +// up to meet the need. Moves boundary up only +void AdjoiningGenerations::adjust_boundary_for_old_gen_needs( + size_t desired_free_space) { + assert(UseAdaptiveSizePolicy && UseAdaptiveGCBoundary, "runtime check"); + + // Stress testing. + if (PSAdaptiveSizePolicyResizeVirtualSpaceAlot == 1) { + MutexLocker x(ExpandHeap_lock); + request_old_gen_expansion(virtual_spaces()->alignment() * 3 / 2); + } + + // Expand only if the entire generation is already committed. + if (old_gen()->virtual_space()->uncommitted_size() == 0) { + if (old_gen()->free_in_bytes() < desired_free_space) { + MutexLocker x(ExpandHeap_lock); + request_old_gen_expansion(desired_free_space); + } + } +} + +// See comment on adjust_boundary_for_old_gen_needss(). +// Adjust boundary down only. +void AdjoiningGenerations::adjust_boundary_for_young_gen_needs(size_t eden_size, + size_t survivor_size) { + + assert(UseAdaptiveSizePolicy && UseAdaptiveGCBoundary, "runtime check"); + + // Stress testing. + if (PSAdaptiveSizePolicyResizeVirtualSpaceAlot == 0) { + request_young_gen_expansion(virtual_spaces()->alignment() * 3 / 2); + eden_size = young_gen()->eden_space()->capacity_in_bytes(); + } + + // Expand only if the entire generation is already committed. + if (young_gen()->virtual_space()->uncommitted_size() == 0) { + size_t desired_size = eden_size + 2 * survivor_size; + const size_t committed = young_gen()->virtual_space()->committed_size(); + if (desired_size > committed) { + request_young_gen_expansion(desired_size - committed); + } + } +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningGenerations.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningGenerations.hpp new file mode 100644 index 00000000000..9b8ddd8d6b4 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningGenerations.hpp @@ -0,0 +1,77 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +// Contains two generations that both use an AdjoiningVirtualSpaces. +// The two generations are adjacent in the reserved space for the +// heap. Each generation has a virtual space and shrinking and +// expanding of the generations can still be down with that +// virtual space as was previously done. If expanding of reserved +// size of a generation is required, the adjacent generation +// must be shrunk. Adjusting the boundary between the generations +// is called for in this class. + +class AdjoiningGenerations : public CHeapObj { + friend class VMStructs; + private: + // The young generation and old generation, respectively + PSYoungGen* _young_gen; + PSOldGen* _old_gen; + + // The spaces used by the two generations. + AdjoiningVirtualSpaces _virtual_spaces; + + // Move boundary up to expand old gen. Checks are made to + // determine if the move can be done with specified limits. + void request_old_gen_expansion(size_t desired_change_in_bytes); + // Move boundary down to expand young gen. + bool request_young_gen_expansion(size_t desired_change_in_bytes); + + public: + AdjoiningGenerations(ReservedSpace rs, + size_t init_low_byte_size, + size_t min_low_byte_size, + size_t max_low_byte_size, + size_t init_high_byte_size, + size_t min_high_byte_size, + size_t max_high_bytes_size, + size_t alignment); + + // Accessors + PSYoungGen* young_gen() { return _young_gen; } + PSOldGen* old_gen() { return _old_gen; } + + AdjoiningVirtualSpaces* virtual_spaces() { return &_virtual_spaces; } + + // Additional space is needed in the old generation. Check + // the available space and attempt to move the boundary if more space + // is needed. The growth is not guaranteed to occur. + void adjust_boundary_for_old_gen_needs(size_t desired_change_in_bytes); + // Similary for a growth of the young generation. + void adjust_boundary_for_young_gen_needs(size_t eden_size, size_t survivor_size); + + // Return the total byte size of the reserved space + // for the adjoining generations. + size_t reserved_byte_size(); +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningVirtualSpaces.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningVirtualSpaces.cpp new file mode 100644 index 00000000000..38405f56065 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningVirtualSpaces.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_adjoiningVirtualSpaces.cpp.incl" + +AdjoiningVirtualSpaces::AdjoiningVirtualSpaces(ReservedSpace rs, + size_t min_low_byte_size, + size_t min_high_byte_size, + size_t alignment) : + _reserved_space(rs), _min_low_byte_size(min_low_byte_size), + _min_high_byte_size(min_high_byte_size), _low(0), _high(0), + _alignment(alignment) {} + +// The maximum byte sizes are for the initial layout of the +// virtual spaces and are not the limit on the maximum bytes sizes. +void AdjoiningVirtualSpaces::initialize(size_t max_low_byte_size, + size_t init_low_byte_size, + size_t init_high_byte_size) { + + // The reserved spaces for the two parts of the virtual space. + ReservedSpace old_rs = _reserved_space.first_part(max_low_byte_size); + ReservedSpace young_rs = _reserved_space.last_part(max_low_byte_size); + + _low = new PSVirtualSpace(old_rs, alignment()); + if (!_low->expand_by(init_low_byte_size)) { + vm_exit_during_initialization("Could not reserve enough space for " + "object heap"); + } + + _high = new PSVirtualSpaceHighToLow(young_rs, alignment()); + if (!_high->expand_by(init_high_byte_size)) { + vm_exit_during_initialization("Could not reserve enough space for " + "object heap"); + } +} + +bool AdjoiningVirtualSpaces::adjust_boundary_up(size_t change_in_bytes) { + assert(UseAdaptiveSizePolicy && UseAdaptiveGCBoundary, "runtime check"); + size_t actual_change = low()->expand_into(high(), change_in_bytes); + return actual_change != 0; +} + +bool AdjoiningVirtualSpaces::adjust_boundary_down(size_t change_in_bytes) { + assert(UseAdaptiveSizePolicy && UseAdaptiveGCBoundary, "runtime check"); + size_t actual_change = high()->expand_into(low(), change_in_bytes); + return actual_change != 0; +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningVirtualSpaces.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningVirtualSpaces.hpp new file mode 100644 index 00000000000..720b539a53d --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/adjoiningVirtualSpaces.hpp @@ -0,0 +1,108 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +// Contains two virtual spaces that each can individually span +// most of the reserved region but committed parts of which +// cannot overlap. +// +// +-------+ <--- high_boundary for H +// | | +// | H | +// | | +// | | +// | | +// --------- <--- low for H +// | | +// ========= <--- low_boundary for H, high_boundary for L +// | | +// | | +// | | +// --------- <--- high for L +// | | +// | L | +// | | +// | | +// | | +// +-------+ <--- low_boundary for L +// +// Each virtual space in the AdjoiningVirtualSpaces grows and shrink +// within its reserved region (between the low_boundary and the +// boundary) independently. If L want to grow above its high_boundary, +// then the high_boundary of L and the low_boundary of H must be +// moved up consistently. AdjoiningVirtualSpaces provide the +// interfaces for moving the this boundary. + +class AdjoiningVirtualSpaces { + // space at the high end and the low end, respectively + PSVirtualSpace* _high; + PSVirtualSpace* _low; + + // The reserved space spanned by the two spaces. + ReservedSpace _reserved_space; + + // The minimum byte size for the low space. It will not + // be shrunk below this value. + size_t _min_low_byte_size; + // Same for the high space + size_t _min_high_byte_size; + + const size_t _alignment; + + public: + // Allocates two virtual spaces that will be located at the + // high and low ends. Does no initialization. + AdjoiningVirtualSpaces(ReservedSpace rs, + size_t min_low_byte_size, + size_t min_high_byte_size, + size_t alignment); + + // accessors + PSVirtualSpace* high() { return _high; } + PSVirtualSpace* low() { return _low; } + ReservedSpace reserved_space() { return _reserved_space; } + size_t min_low_byte_size() { return _min_low_byte_size; } + size_t min_high_byte_size() { return _min_high_byte_size; } + size_t alignment() const { return _alignment; } + + // move boundary between the two spaces up + bool adjust_boundary_up(size_t size_in_bytes); + // and down + bool adjust_boundary_down(size_t size_in_bytes); + + // Maximum byte size for the high space. + size_t high_byte_size_limit() { + return _reserved_space.size() - _min_low_byte_size; + } + // Maximum byte size for the low space. + size_t low_byte_size_limit() { + return _reserved_space.size() - _min_high_byte_size; + } + + // Sets the boundaries for the virtual spaces and commits and + // initial size; + void initialize(size_t max_low_byte_size, + size_t init_low_byte_size, + size_t init_high_byte_size); +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSOldGen.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSOldGen.cpp new file mode 100644 index 00000000000..3a7161d4c45 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSOldGen.cpp @@ -0,0 +1,135 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_asPSOldGen.cpp.incl" + +// Whereas PSOldGen takes the maximum size of the generation +// (which doesn't change in the case of PSOldGen) as a parameter, +// ASPSOldGen takes the upper limit on the size of +// the generation as a parameter. In ASPSOldGen the +// maximum size of the generation can change as the boundary +// moves. The "maximum size of the generation" is still a valid +// concept since the generation can grow and shrink within that +// maximum. There are lots of useful checks that use that +// maximum. In PSOldGen the method max_gen_size() returns +// _max_gen_size (as set by the PSOldGen constructor). This +// is how it always worked. In ASPSOldGen max_gen_size() +// returned the size of the reserved space for the generation. +// That can change as the boundary moves. Below the limit of +// the size of the generation is passed to the PSOldGen constructor +// for "_max_gen_size" (have to pass something) but it is not used later. +// +ASPSOldGen::ASPSOldGen(size_t initial_size, + size_t min_size, + size_t size_limit, + const char* gen_name, + int level) : + PSOldGen(initial_size, min_size, size_limit, gen_name, level), + _gen_size_limit(size_limit) + +{} + +ASPSOldGen::ASPSOldGen(PSVirtualSpace* vs, + size_t initial_size, + size_t min_size, + size_t size_limit, + const char* gen_name, + int level) : + PSOldGen(initial_size, min_size, size_limit, gen_name, level), + _gen_size_limit(size_limit) + +{ + _virtual_space = vs; +} + +void ASPSOldGen::reset_after_change() { + _reserved = MemRegion((HeapWord*)virtual_space()->low_boundary(), + (HeapWord*)virtual_space()->high_boundary()); + post_resize(); +} + + +size_t ASPSOldGen::available_for_expansion() { + assert(virtual_space()->is_aligned(gen_size_limit()), "not aligned"); + assert(gen_size_limit() >= virtual_space()->committed_size(), "bad gen size"); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + size_t result = gen_size_limit() - virtual_space()->committed_size(); + size_t result_aligned = align_size_down(result, heap->old_gen_alignment()); + return result_aligned; +} + +size_t ASPSOldGen::available_for_contraction() { + size_t uncommitted_bytes = virtual_space()->uncommitted_size(); + if (uncommitted_bytes != 0) { + return uncommitted_bytes; + } + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + const size_t gen_alignment = heap->old_gen_alignment(); + PSAdaptiveSizePolicy* policy = heap->size_policy(); + const size_t working_size = + used_in_bytes() + (size_t) policy->avg_promoted()->padded_average(); + const size_t working_aligned = align_size_up(working_size, gen_alignment); + const size_t working_or_min = MAX2(working_aligned, min_gen_size()); + if (working_or_min > reserved().byte_size()) { + // If the used or minimum gen size (aligned up) is greater + // than the total reserved size, then the space available + // for contraction should (after proper alignment) be 0 + return 0; + } + const size_t max_contraction = + reserved().byte_size() - working_or_min; + + // Use the "increment" fraction instead of the "decrement" fraction + // to allow the other gen to expand more aggressively. The + // "decrement" fraction is conservative because its intent is to + // only reduce the footprint. + + size_t result = policy->promo_increment_aligned_down(max_contraction); + // Also adjust for inter-generational alignment + size_t result_aligned = align_size_down(result, gen_alignment); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("\nASPSOldGen::available_for_contraction:" + " %d K / 0x%x", result_aligned/K, result_aligned); + gclog_or_tty->print_cr(" reserved().byte_size() %d K / 0x%x ", + reserved().byte_size()/K, reserved().byte_size()); + size_t working_promoted = (size_t) policy->avg_promoted()->padded_average(); + gclog_or_tty->print_cr(" padded promoted %d K / 0x%x", + working_promoted/K, working_promoted); + gclog_or_tty->print_cr(" used %d K / 0x%x", + used_in_bytes()/K, used_in_bytes()); + gclog_or_tty->print_cr(" min_gen_size() %d K / 0x%x", + min_gen_size()/K, min_gen_size()); + gclog_or_tty->print_cr(" max_contraction %d K / 0x%x", + max_contraction/K, max_contraction); + gclog_or_tty->print_cr(" without alignment %d K / 0x%x", + policy->promo_increment(max_contraction)/K, + policy->promo_increment(max_contraction)); + gclog_or_tty->print_cr(" alignment 0x%x", gen_alignment); + } + assert(result_aligned <= max_contraction, "arithmetic is wrong"); + return result_aligned; +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSOldGen.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSOldGen.hpp new file mode 100644 index 00000000000..a7cb1ae0b93 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSOldGen.hpp @@ -0,0 +1,57 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ASPSOldGen : public PSOldGen { + friend class VMStructs; + size_t _gen_size_limit; // Largest size the generation's reserved size + // can grow. + public: + ASPSOldGen(size_t initial_byte_size, + size_t minimum_byte_size, + size_t byte_size_limit, + const char* gen_name, int level); + ASPSOldGen(PSVirtualSpace* vs, + size_t initial_byte_size, + size_t minimum_byte_size, + size_t byte_size_limit, + const char* gen_name, int level); + size_t gen_size_limit() { return _gen_size_limit; } + size_t max_gen_size() { return _reserved.byte_size(); } + void set_gen_size_limit(size_t v) { _gen_size_limit = v; } + + // After a shrink or expand reset the generation + void reset_after_change(); + + // Return number of bytes that the virtual space in the generation is willing + // to expand or contract. The results from these methods should feed into the + // decisions about adjusting the virtual space. + size_t available_for_expansion(); + size_t available_for_contraction(); + + // Accessors + void set_reserved(MemRegion v) { _reserved = v; } + + // Debugging support + virtual const char* short_name() const { return "ASPSOldGen"; } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSYoungGen.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSYoungGen.cpp new file mode 100644 index 00000000000..3d16cf68109 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSYoungGen.cpp @@ -0,0 +1,469 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_asPSYoungGen.cpp.incl" + +ASPSYoungGen::ASPSYoungGen(size_t init_byte_size, + size_t minimum_byte_size, + size_t byte_size_limit) : + PSYoungGen(init_byte_size, minimum_byte_size, byte_size_limit), + _gen_size_limit(byte_size_limit) { +} + + +ASPSYoungGen::ASPSYoungGen(PSVirtualSpace* vs, + size_t init_byte_size, + size_t minimum_byte_size, + size_t byte_size_limit) : + //PSYoungGen(init_byte_size, minimum_byte_size, byte_size_limit), + PSYoungGen(vs->committed_size(), minimum_byte_size, byte_size_limit), + _gen_size_limit(byte_size_limit) { + + assert(vs->committed_size() == init_byte_size, "Cannot replace with"); + + _virtual_space = vs; +} + +void ASPSYoungGen::initialize_virtual_space(ReservedSpace rs, + size_t alignment) { + assert(_init_gen_size != 0, "Should have a finite size"); + _virtual_space = new PSVirtualSpaceHighToLow(rs, alignment); + if (!_virtual_space->expand_by(_init_gen_size)) { + vm_exit_during_initialization("Could not reserve enough space for " + "object heap"); + } +} + +void ASPSYoungGen::initialize(ReservedSpace rs, size_t alignment) { + initialize_virtual_space(rs, alignment); + initialize_work(); +} + +size_t ASPSYoungGen::available_for_expansion() { + + size_t current_committed_size = virtual_space()->committed_size(); + assert((gen_size_limit() >= current_committed_size), + "generation size limit is wrong"); + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + size_t result = gen_size_limit() - current_committed_size; + size_t result_aligned = align_size_down(result, heap->young_gen_alignment()); + return result_aligned; +} + +// Return the number of bytes the young gen is willing give up. +// +// Future implementations could check the survivors and if to_space is in the +// right place (below from_space), take a chunk from to_space. +size_t ASPSYoungGen::available_for_contraction() { + + size_t uncommitted_bytes = virtual_space()->uncommitted_size(); + if (uncommitted_bytes != 0) { + return uncommitted_bytes; + } + + if (eden_space()->is_empty()) { + // Respect the minimum size for eden and for the young gen as a whole. + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + const size_t eden_alignment = heap->intra_generation_alignment(); + const size_t gen_alignment = heap->young_gen_alignment(); + + assert(eden_space()->capacity_in_bytes() >= eden_alignment, + "Alignment is wrong"); + size_t eden_avail = eden_space()->capacity_in_bytes() - eden_alignment; + eden_avail = align_size_down(eden_avail, gen_alignment); + + assert(virtual_space()->committed_size() >= min_gen_size(), + "minimum gen size is wrong"); + size_t gen_avail = virtual_space()->committed_size() - min_gen_size(); + assert(virtual_space()->is_aligned(gen_avail), "not aligned"); + + const size_t max_contraction = MIN2(eden_avail, gen_avail); + // See comment for ASPSOldGen::available_for_contraction() + // for reasons the "increment" fraction is used. + PSAdaptiveSizePolicy* policy = heap->size_policy(); + size_t result = policy->eden_increment_aligned_down(max_contraction); + size_t result_aligned = align_size_down(result, gen_alignment); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("ASPSYoungGen::available_for_contraction: %d K", + result_aligned/K); + gclog_or_tty->print_cr(" max_contraction %d K", max_contraction/K); + gclog_or_tty->print_cr(" eden_avail %d K", eden_avail/K); + gclog_or_tty->print_cr(" gen_avail %d K", gen_avail/K); + } + return result_aligned; + + } + + return 0; +} + +// The current implementation only considers to the end of eden. +// If to_space is below from_space, to_space is not considered. +// to_space can be. +size_t ASPSYoungGen::available_to_live() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + const size_t alignment = heap->intra_generation_alignment(); + + // Include any space that is committed but is not in eden. + size_t available = pointer_delta(eden_space()->bottom(), + virtual_space()->low(), + sizeof(char)); + + const size_t eden_capacity = eden_space()->capacity_in_bytes(); + if (eden_space()->is_empty() && eden_capacity > alignment) { + available += eden_capacity - alignment; + } + return available; +} + +// Similar to PSYoungGen::resize_generation() but +// allows sum of eden_size and 2 * survivor_size to exceed _max_gen_size +// expands at the low end of the virtual space +// moves the boundary between the generations in order to expand +// some additional diagnostics +// If no additional changes are required, this can be deleted +// and the changes factored back into PSYoungGen::resize_generation(). +bool ASPSYoungGen::resize_generation(size_t eden_size, size_t survivor_size) { + const size_t alignment = virtual_space()->alignment(); + size_t orig_size = virtual_space()->committed_size(); + bool size_changed = false; + + // There used to be a guarantee here that + // (eden_size + 2*survivor_size) <= _max_gen_size + // This requirement is enforced by the calculation of desired_size + // below. It may not be true on entry since the size of the + // eden_size is no bounded by the generation size. + + assert(max_size() == reserved().byte_size(), "max gen size problem?"); + assert(min_gen_size() <= orig_size && orig_size <= max_size(), + "just checking"); + + // Adjust new generation size + const size_t eden_plus_survivors = + align_size_up(eden_size + 2 * survivor_size, alignment); + size_t desired_size = MAX2(MIN2(eden_plus_survivors, gen_size_limit()), + min_gen_size()); + assert(desired_size <= gen_size_limit(), "just checking"); + + if (desired_size > orig_size) { + // Grow the generation + size_t change = desired_size - orig_size; + if (!virtual_space()->expand_by(change)) { + return false; + } + size_changed = true; + } else if (desired_size < orig_size) { + size_t desired_change = orig_size - desired_size; + + // How much is available for shrinking. + size_t available_bytes = limit_gen_shrink(desired_change); + size_t change = MIN2(desired_change, available_bytes); + virtual_space()->shrink_by(change); + size_changed = true; + } else { + if (Verbose && PrintGC) { + if (orig_size == gen_size_limit()) { + gclog_or_tty->print_cr("ASPSYoung generation size at maximum: " + SIZE_FORMAT "K", orig_size/K); + } else if (orig_size == min_gen_size()) { + gclog_or_tty->print_cr("ASPSYoung generation size at minium: " + SIZE_FORMAT "K", orig_size/K); + } + } + } + + if (size_changed) { + reset_after_change(); + if (Verbose && PrintGC) { + size_t current_size = virtual_space()->committed_size(); + gclog_or_tty->print_cr("ASPSYoung generation size changed: " + SIZE_FORMAT "K->" SIZE_FORMAT "K", + orig_size/K, current_size/K); + } + } + + guarantee(eden_plus_survivors <= virtual_space()->committed_size() || + virtual_space()->committed_size() == max_size(), "Sanity"); + + return true; +} + +// Similar to PSYoungGen::resize_spaces() but +// eden always starts at the low end of the committed virtual space +// current implementation does not allow holes between the spaces +// _young_generation_boundary has to be reset because it changes. +// so additional verification +void ASPSYoungGen::resize_spaces(size_t requested_eden_size, + size_t requested_survivor_size) { + assert(requested_eden_size > 0 && requested_survivor_size > 0, + "just checking"); + + space_invariants(); + + // We require eden and to space to be empty + if ((!eden_space()->is_empty()) || (!to_space()->is_empty())) { + return; + } + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("PSYoungGen::resize_spaces(requested_eden_size: " + SIZE_FORMAT + ", requested_survivor_size: " SIZE_FORMAT ")", + requested_eden_size, requested_survivor_size); + gclog_or_tty->print_cr(" eden: [" PTR_FORMAT ".." PTR_FORMAT ") " + SIZE_FORMAT, + eden_space()->bottom(), + eden_space()->end(), + pointer_delta(eden_space()->end(), + eden_space()->bottom(), + sizeof(char))); + gclog_or_tty->print_cr(" from: [" PTR_FORMAT ".." PTR_FORMAT ") " + SIZE_FORMAT, + from_space()->bottom(), + from_space()->end(), + pointer_delta(from_space()->end(), + from_space()->bottom(), + sizeof(char))); + gclog_or_tty->print_cr(" to: [" PTR_FORMAT ".." PTR_FORMAT ") " + SIZE_FORMAT, + to_space()->bottom(), + to_space()->end(), + pointer_delta( to_space()->end(), + to_space()->bottom(), + sizeof(char))); + } + + // There's nothing to do if the new sizes are the same as the current + if (requested_survivor_size == to_space()->capacity_in_bytes() && + requested_survivor_size == from_space()->capacity_in_bytes() && + requested_eden_size == eden_space()->capacity_in_bytes()) { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" capacities are the right sizes, returning"); + } + return; + } + + char* eden_start = (char*)virtual_space()->low(); + char* eden_end = (char*)eden_space()->end(); + char* from_start = (char*)from_space()->bottom(); + char* from_end = (char*)from_space()->end(); + char* to_start = (char*)to_space()->bottom(); + char* to_end = (char*)to_space()->end(); + + assert(eden_start < from_start, "Cannot push into from_space"); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + const size_t alignment = heap->intra_generation_alignment(); + + // Check whether from space is below to space + if (from_start < to_start) { + // Eden, from, to + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" Eden, from, to:"); + } + + // Set eden + // Compute how big eden can be, then adjust end. + // See comment in PSYoungGen::resize_spaces() on + // calculating eden_end. + const size_t eden_size = MIN2(requested_eden_size, + pointer_delta(from_start, + eden_start, + sizeof(char))); + eden_end = eden_start + eden_size; + assert(eden_end >= eden_start, "addition overflowed") + + // To may resize into from space as long as it is clear of live data. + // From space must remain page aligned, though, so we need to do some + // extra calculations. + + // First calculate an optimal to-space + to_end = (char*)virtual_space()->high(); + to_start = (char*)pointer_delta(to_end, + (char*)requested_survivor_size, + sizeof(char)); + + // Does the optimal to-space overlap from-space? + if (to_start < (char*)from_space()->end()) { + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + // Calculate the minimum offset possible for from_end + size_t from_size = + pointer_delta(from_space()->top(), from_start, sizeof(char)); + + // Should we be in this method if from_space is empty? Why not the set_space method? FIX ME! + if (from_size == 0) { + from_size = alignment; + } else { + from_size = align_size_up(from_size, alignment); + } + + from_end = from_start + from_size; + assert(from_end > from_start, "addition overflow or from_size problem"); + + guarantee(from_end <= (char*)from_space()->end(), + "from_end moved to the right"); + + // Now update to_start with the new from_end + to_start = MAX2(from_end, to_start); + } + + guarantee(to_start != to_end, "to space is zero sized"); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" [eden_start .. eden_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + eden_start, + eden_end, + pointer_delta(eden_end, eden_start, sizeof(char))); + gclog_or_tty->print_cr(" [from_start .. from_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + from_start, + from_end, + pointer_delta(from_end, from_start, sizeof(char))); + gclog_or_tty->print_cr(" [ to_start .. to_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + to_start, + to_end, + pointer_delta( to_end, to_start, sizeof(char))); + } + } else { + // Eden, to, from + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" Eden, to, from:"); + } + + // To space gets priority over eden resizing. Note that we position + // to space as if we were able to resize from space, even though from + // space is not modified. + // Giving eden priority was tried and gave poorer performance. + to_end = (char*)pointer_delta(virtual_space()->high(), + (char*)requested_survivor_size, + sizeof(char)); + to_end = MIN2(to_end, from_start); + to_start = (char*)pointer_delta(to_end, (char*)requested_survivor_size, + sizeof(char)); + // if the space sizes are to be increased by several times then + // 'to_start' will point beyond the young generation. In this case + // 'to_start' should be adjusted. + to_start = MAX2(to_start, eden_start + alignment); + + // Compute how big eden can be, then adjust end. + // See comment in PSYoungGen::resize_spaces() on + // calculating eden_end. + const size_t eden_size = MIN2(requested_eden_size, + pointer_delta(to_start, + eden_start, + sizeof(char))); + eden_end = eden_start + eden_size; + assert(eden_end >= eden_start, "addition overflowed") + + // Don't let eden shrink down to 0 or less. + eden_end = MAX2(eden_end, eden_start + alignment); + to_start = MAX2(to_start, eden_end); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" [eden_start .. eden_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + eden_start, + eden_end, + pointer_delta(eden_end, eden_start, sizeof(char))); + gclog_or_tty->print_cr(" [ to_start .. to_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + to_start, + to_end, + pointer_delta( to_end, to_start, sizeof(char))); + gclog_or_tty->print_cr(" [from_start .. from_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + from_start, + from_end, + pointer_delta(from_end, from_start, sizeof(char))); + } + } + + + guarantee((HeapWord*)from_start <= from_space()->bottom(), + "from start moved to the right"); + guarantee((HeapWord*)from_end >= from_space()->top(), + "from end moved into live data"); + assert(is_object_aligned((intptr_t)eden_start), "checking alignment"); + assert(is_object_aligned((intptr_t)from_start), "checking alignment"); + assert(is_object_aligned((intptr_t)to_start), "checking alignment"); + + MemRegion edenMR((HeapWord*)eden_start, (HeapWord*)eden_end); + MemRegion toMR ((HeapWord*)to_start, (HeapWord*)to_end); + MemRegion fromMR((HeapWord*)from_start, (HeapWord*)from_end); + + // Let's make sure the call to initialize doesn't reset "top"! + DEBUG_ONLY(HeapWord* old_from_top = from_space()->top();) + + // For PrintAdaptiveSizePolicy block below + size_t old_from = from_space()->capacity_in_bytes(); + size_t old_to = to_space()->capacity_in_bytes(); + + eden_space()->initialize(edenMR, true); + to_space()->initialize(toMR , true); + from_space()->initialize(fromMR, false); // Note, not cleared! + PSScavenge::set_young_generation_boundary(eden_space()->bottom()); + + assert(from_space()->top() == old_from_top, "from top changed!"); + + if (PrintAdaptiveSizePolicy) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + gclog_or_tty->print("AdaptiveSizePolicy::survivor space sizes: " + "collection: %d " + "(" SIZE_FORMAT ", " SIZE_FORMAT ") -> " + "(" SIZE_FORMAT ", " SIZE_FORMAT ") ", + heap->total_collections(), + old_from, old_to, + from_space()->capacity_in_bytes(), + to_space()->capacity_in_bytes()); + gclog_or_tty->cr(); + } + space_invariants(); +} + +void ASPSYoungGen::reset_after_change() { + assert_locked_or_safepoint(Heap_lock); + + _reserved = MemRegion((HeapWord*)virtual_space()->low_boundary(), + (HeapWord*)virtual_space()->high_boundary()); + PSScavenge::reference_processor()->set_span(_reserved); + + HeapWord* new_eden_bottom = (HeapWord*)virtual_space()->low(); + HeapWord* eden_bottom = eden_space()->bottom(); + if (new_eden_bottom != eden_bottom) { + MemRegion eden_mr(new_eden_bottom, eden_space()->end()); + eden_space()->initialize(eden_mr, true); + PSScavenge::set_young_generation_boundary(eden_space()->bottom()); + } + MemRegion cmr((HeapWord*)virtual_space()->low(), + (HeapWord*)virtual_space()->high()); + Universe::heap()->barrier_set()->resize_covered_region(cmr); + + space_invariants(); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSYoungGen.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSYoungGen.hpp new file mode 100644 index 00000000000..b255e583ca3 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/asPSYoungGen.hpp @@ -0,0 +1,64 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ASPSYoungGen : public PSYoungGen { + friend class VMStructs; + private: + size_t _gen_size_limit; + protected: + virtual size_t available_to_live(); + + public: + ASPSYoungGen(size_t initial_byte_size, + size_t minimum_byte_size, + size_t byte_size_limit); + + ASPSYoungGen(PSVirtualSpace* vs, + size_t initial_byte_size, + size_t minimum_byte_size, + size_t byte_size_limit); + + void initialize(ReservedSpace rs, size_t alignment); + void initialize_virtual_space(ReservedSpace rs, size_t alignment); + + size_t gen_size_limit() { return _gen_size_limit; } + void set_gen_size_limit(size_t v) { _gen_size_limit = v; } + + bool resize_generation(size_t eden_size, size_t survivor_size); + void resize_spaces(size_t eden_size, size_t survivor_size); + + // Adjust eden to be consistent with the virtual space. + void reset_after_change(); + + // Adaptive size policy support + // Return number of bytes that the generation can expand/contract. + size_t available_for_expansion(); + size_t available_for_contraction(); + + // Accessors + void set_reserved(MemRegion v) { _reserved = v; } + + // Printing support + virtual const char* short_name() const { return "ASPSYoungGen"; } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp new file mode 100644 index 00000000000..9857b4e6c6a --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp @@ -0,0 +1,779 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_cardTableExtension.cpp.incl" + +// Checks an individual oop for missing precise marks. Mark +// may be either dirty or newgen. +class CheckForUnmarkedOops : public OopClosure { + PSYoungGen* _young_gen; + CardTableExtension* _card_table; + HeapWord* _unmarked_addr; + jbyte* _unmarked_card; + + public: + CheckForUnmarkedOops( PSYoungGen* young_gen, CardTableExtension* card_table ) : + _young_gen(young_gen), _card_table(card_table), _unmarked_addr(NULL) { } + + virtual void do_oop(oop* p) { + if (_young_gen->is_in_reserved(*p) && + !_card_table->addr_is_marked_imprecise(p)) { + // Don't overwrite the first missing card mark + if (_unmarked_addr == NULL) { + _unmarked_addr = (HeapWord*)p; + _unmarked_card = _card_table->byte_for(p); + } + } + } + + bool has_unmarked_oop() { + return _unmarked_addr != NULL; + } +}; + +// Checks all objects for the existance of some type of mark, +// precise or imprecise, dirty or newgen. +class CheckForUnmarkedObjects : public ObjectClosure { + PSYoungGen* _young_gen; + CardTableExtension* _card_table; + + public: + CheckForUnmarkedObjects() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + _young_gen = heap->young_gen(); + _card_table = (CardTableExtension*)heap->barrier_set(); + // No point in asserting barrier set type here. Need to make CardTableExtension + // a unique barrier set type. + } + + // Card marks are not precise. The current system can leave us with + // a mismash of precise marks and begining of object marks. This means + // we test for missing precise marks first. If any are found, we don't + // fail unless the object head is also unmarked. + virtual void do_object(oop obj) { + CheckForUnmarkedOops object_check( _young_gen, _card_table ); + obj->oop_iterate(&object_check); + if (object_check.has_unmarked_oop()) { + assert(_card_table->addr_is_marked_imprecise(obj), "Found unmarked young_gen object"); + } + } +}; + +// Checks for precise marking of oops as newgen. +class CheckForPreciseMarks : public OopClosure { + PSYoungGen* _young_gen; + CardTableExtension* _card_table; + + public: + CheckForPreciseMarks( PSYoungGen* young_gen, CardTableExtension* card_table ) : + _young_gen(young_gen), _card_table(card_table) { } + + virtual void do_oop(oop* p) { + if (_young_gen->is_in_reserved(*p)) { + assert(_card_table->addr_is_marked_precise(p), "Found unmarked precise oop"); + _card_table->set_card_newgen(p); + } + } +}; + +// We get passed the space_top value to prevent us from traversing into +// the old_gen promotion labs, which cannot be safely parsed. +void CardTableExtension::scavenge_contents(ObjectStartArray* start_array, + MutableSpace* sp, + HeapWord* space_top, + PSPromotionManager* pm) +{ + assert(start_array != NULL && sp != NULL && pm != NULL, "Sanity"); + assert(start_array->covered_region().contains(sp->used_region()), + "ObjectStartArray does not cover space"); + bool depth_first = pm->depth_first(); + + if (sp->not_empty()) { + oop* sp_top = (oop*)space_top; + oop* prev_top = NULL; + jbyte* current_card = byte_for(sp->bottom()); + jbyte* end_card = byte_for(sp_top - 1); // sp_top is exclusive + // scan card marking array + while (current_card <= end_card) { + jbyte value = *current_card; + // skip clean cards + if (card_is_clean(value)) { + current_card++; + } else { + // we found a non-clean card + jbyte* first_nonclean_card = current_card++; + oop* bottom = (oop*)addr_for(first_nonclean_card); + // find object starting on card + oop* bottom_obj = (oop*)start_array->object_start((HeapWord*)bottom); + // bottom_obj = (oop*)start_array->object_start((HeapWord*)bottom); + assert(bottom_obj <= bottom, "just checking"); + // make sure we don't scan oops we already looked at + if (bottom < prev_top) bottom = prev_top; + // figure out when to stop scanning + jbyte* first_clean_card; + oop* top; + bool restart_scanning; + do { + restart_scanning = false; + // find a clean card + while (current_card <= end_card) { + value = *current_card; + if (card_is_clean(value)) break; + current_card++; + } + // check if we reached the end, if so we are done + if (current_card >= end_card) { + first_clean_card = end_card + 1; + current_card++; + top = sp_top; + } else { + // we have a clean card, find object starting on that card + first_clean_card = current_card++; + top = (oop*)addr_for(first_clean_card); + oop* top_obj = (oop*)start_array->object_start((HeapWord*)top); + // top_obj = (oop*)start_array->object_start((HeapWord*)top); + assert(top_obj <= top, "just checking"); + if (oop(top_obj)->is_objArray() || oop(top_obj)->is_typeArray()) { + // an arrayOop is starting on the clean card - since we do exact store + // checks for objArrays we are done + } else { + // otherwise, it is possible that the object starting on the clean card + // spans the entire card, and that the store happened on a later card. + // figure out where the object ends + top = top_obj + oop(top_obj)->size(); + jbyte* top_card = CardTableModRefBS::byte_for(top - 1); // top is exclusive + if (top_card > first_clean_card) { + // object ends a different card + current_card = top_card + 1; + if (card_is_clean(*top_card)) { + // the ending card is clean, we are done + first_clean_card = top_card; + } else { + // the ending card is not clean, continue scanning at start of do-while + restart_scanning = true; + } + } else { + // object ends on the clean card, we are done. + assert(first_clean_card == top_card, "just checking"); + } + } + } + } while (restart_scanning); + // we know which cards to scan, now clear them + while (first_nonclean_card < first_clean_card) { + *first_nonclean_card++ = clean_card; + } + // scan oops in objects + // hoisted the if (depth_first) check out of the loop + if (depth_first){ + do { + oop(bottom_obj)->push_contents(pm); + bottom_obj += oop(bottom_obj)->size(); + assert(bottom_obj <= sp_top, "just checking"); + } while (bottom_obj < top); + pm->drain_stacks_cond_depth(); + } else { + do { + oop(bottom_obj)->copy_contents(pm); + bottom_obj += oop(bottom_obj)->size(); + assert(bottom_obj <= sp_top, "just checking"); + } while (bottom_obj < top); + } + // remember top oop* scanned + prev_top = top; + } + } + } +} + +void CardTableExtension::scavenge_contents_parallel(ObjectStartArray* start_array, + MutableSpace* sp, + HeapWord* space_top, + PSPromotionManager* pm, + uint stripe_number) { + int ssize = 128; // Naked constant! Work unit = 64k. + int dirty_card_count = 0; + bool depth_first = pm->depth_first(); + + oop* sp_top = (oop*)space_top; + jbyte* start_card = byte_for(sp->bottom()); + jbyte* end_card = byte_for(sp_top - 1) + 1; + oop* last_scanned = NULL; // Prevent scanning objects more than once + for (jbyte* slice = start_card; slice < end_card; slice += ssize*ParallelGCThreads) { + jbyte* worker_start_card = slice + stripe_number * ssize; + if (worker_start_card >= end_card) + return; // We're done. + + jbyte* worker_end_card = worker_start_card + ssize; + if (worker_end_card > end_card) + worker_end_card = end_card; + + // We do not want to scan objects more than once. In order to accomplish + // this, we assert that any object with an object head inside our 'slice' + // belongs to us. We may need to extend the range of scanned cards if the + // last object continues into the next 'slice'. + // + // Note! ending cards are exclusive! + HeapWord* slice_start = addr_for(worker_start_card); + HeapWord* slice_end = MIN2((HeapWord*) sp_top, addr_for(worker_end_card)); + + // If there are not objects starting within the chunk, skip it. + if (!start_array->object_starts_in_range(slice_start, slice_end)) { + continue; + } + // Update our begining addr + HeapWord* first_object = start_array->object_start(slice_start); + debug_only(oop* first_object_within_slice = (oop*) first_object;) + if (first_object < slice_start) { + last_scanned = (oop*)(first_object + oop(first_object)->size()); + debug_only(first_object_within_slice = last_scanned;) + worker_start_card = byte_for(last_scanned); + } + + // Update the ending addr + if (slice_end < (HeapWord*)sp_top) { + // The subtraction is important! An object may start precisely at slice_end. + HeapWord* last_object = start_array->object_start(slice_end - 1); + slice_end = last_object + oop(last_object)->size(); + // worker_end_card is exclusive, so bump it one past the end of last_object's + // covered span. + worker_end_card = byte_for(slice_end) + 1; + + if (worker_end_card > end_card) + worker_end_card = end_card; + } + + assert(slice_end <= (HeapWord*)sp_top, "Last object in slice crosses space boundary"); + assert(is_valid_card_address(worker_start_card), "Invalid worker start card"); + assert(is_valid_card_address(worker_end_card), "Invalid worker end card"); + // Note that worker_start_card >= worker_end_card is legal, and happens when + // an object spans an entire slice. + assert(worker_start_card <= end_card, "worker start card beyond end card"); + assert(worker_end_card <= end_card, "worker end card beyond end card"); + + jbyte* current_card = worker_start_card; + while (current_card < worker_end_card) { + // Find an unclean card. + while (current_card < worker_end_card && card_is_clean(*current_card)) { + current_card++; + } + jbyte* first_unclean_card = current_card; + + // Find the end of a run of contiguous unclean cards + while (current_card < worker_end_card && !card_is_clean(*current_card)) { + while (current_card < worker_end_card && !card_is_clean(*current_card)) { + current_card++; + } + + if (current_card < worker_end_card) { + // Some objects may be large enough to span several cards. If such + // an object has more than one dirty card, separated by a clean card, + // we will attempt to scan it twice. The test against "last_scanned" + // prevents the redundant object scan, but it does not prevent newly + // marked cards from being cleaned. + HeapWord* last_object_in_dirty_region = start_array->object_start(addr_for(current_card)-1); + size_t size_of_last_object = oop(last_object_in_dirty_region)->size(); + HeapWord* end_of_last_object = last_object_in_dirty_region + size_of_last_object; + jbyte* ending_card_of_last_object = byte_for(end_of_last_object); + assert(ending_card_of_last_object <= worker_end_card, "ending_card_of_last_object is greater than worker_end_card"); + if (ending_card_of_last_object > current_card) { + // This means the object spans the next complete card. + // We need to bump the current_card to ending_card_of_last_object + current_card = ending_card_of_last_object; + } + } + } + jbyte* following_clean_card = current_card; + + if (first_unclean_card < worker_end_card) { + oop* p = (oop*) start_array->object_start(addr_for(first_unclean_card)); + assert((HeapWord*)p <= addr_for(first_unclean_card), "checking"); + // "p" should always be >= "last_scanned" because newly GC dirtied + // cards are no longer scanned again (see comment at end + // of loop on the increment of "current_card"). Test that + // hypothesis before removing this code. + // If this code is removed, deal with the first time through + // the loop when the last_scanned is the object starting in + // the previous slice. + assert((p >= last_scanned) || + (last_scanned == first_object_within_slice), + "Should no longer be possible"); + if (p < last_scanned) { + // Avoid scanning more than once; this can happen because + // newgen cards set by GC may a different set than the + // originally dirty set + p = last_scanned; + } + oop* to = (oop*)addr_for(following_clean_card); + + // Test slice_end first! + if ((HeapWord*)to > slice_end) { + to = (oop*)slice_end; + } else if (to > sp_top) { + to = sp_top; + } + + // we know which cards to scan, now clear them + if (first_unclean_card <= worker_start_card+1) + first_unclean_card = worker_start_card+1; + if (following_clean_card >= worker_end_card-1) + following_clean_card = worker_end_card-1; + + while (first_unclean_card < following_clean_card) { + *first_unclean_card++ = clean_card; + } + + const int interval = PrefetchScanIntervalInBytes; + // scan all objects in the range + if (interval != 0) { + // hoisted the if (depth_first) check out of the loop + if (depth_first) { + while (p < to) { + Prefetch::write(p, interval); + oop m = oop(p); + assert(m->is_oop_or_null(), "check for header"); + m->push_contents(pm); + p += m->size(); + } + pm->drain_stacks_cond_depth(); + } else { + while (p < to) { + Prefetch::write(p, interval); + oop m = oop(p); + assert(m->is_oop_or_null(), "check for header"); + m->copy_contents(pm); + p += m->size(); + } + } + } else { + // hoisted the if (depth_first) check out of the loop + if (depth_first) { + while (p < to) { + oop m = oop(p); + assert(m->is_oop_or_null(), "check for header"); + m->push_contents(pm); + p += m->size(); + } + pm->drain_stacks_cond_depth(); + } else { + while (p < to) { + oop m = oop(p); + assert(m->is_oop_or_null(), "check for header"); + m->copy_contents(pm); + p += m->size(); + } + } + } + last_scanned = p; + } + // "current_card" is still the "following_clean_card" or + // the current_card is >= the worker_end_card so the + // loop will not execute again. + assert((current_card == following_clean_card) || + (current_card >= worker_end_card), + "current_card should only be incremented if it still equals " + "following_clean_card"); + // Increment current_card so that it is not processed again. + // It may now be dirty because a old-to-young pointer was + // found on it an updated. If it is now dirty, it cannot be + // be safely cleaned in the next iteration. + current_card++; + } + } +} + +// This should be called before a scavenge. +void CardTableExtension::verify_all_young_refs_imprecise() { + CheckForUnmarkedObjects check; + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + PSOldGen* old_gen = heap->old_gen(); + PSPermGen* perm_gen = heap->perm_gen(); + + old_gen->object_iterate(&check); + perm_gen->object_iterate(&check); +} + +// This should be called immediately after a scavenge, before mutators resume. +void CardTableExtension::verify_all_young_refs_precise() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + PSOldGen* old_gen = heap->old_gen(); + PSPermGen* perm_gen = heap->perm_gen(); + + CheckForPreciseMarks check(heap->young_gen(), (CardTableExtension*)heap->barrier_set()); + + old_gen->oop_iterate(&check); + perm_gen->oop_iterate(&check); + + verify_all_young_refs_precise_helper(old_gen->object_space()->used_region()); + verify_all_young_refs_precise_helper(perm_gen->object_space()->used_region()); +} + +void CardTableExtension::verify_all_young_refs_precise_helper(MemRegion mr) { + CardTableExtension* card_table = (CardTableExtension*)Universe::heap()->barrier_set(); + // FIX ME ASSERT HERE + + jbyte* bot = card_table->byte_for(mr.start()); + jbyte* top = card_table->byte_for(mr.end()); + while(bot <= top) { + assert(*bot == clean_card || *bot == verify_card, "Found unwanted or unknown card mark"); + if (*bot == verify_card) + *bot = youngergen_card; + bot++; + } +} + +bool CardTableExtension::addr_is_marked_imprecise(void *addr) { + jbyte* p = byte_for(addr); + jbyte val = *p; + + if (card_is_dirty(val)) + return true; + + if (card_is_newgen(val)) + return true; + + if (card_is_clean(val)) + return false; + + assert(false, "Found unhandled card mark type"); + + return false; +} + +// Also includes verify_card +bool CardTableExtension::addr_is_marked_precise(void *addr) { + jbyte* p = byte_for(addr); + jbyte val = *p; + + if (card_is_newgen(val)) + return true; + + if (card_is_verify(val)) + return true; + + if (card_is_clean(val)) + return false; + + if (card_is_dirty(val)) + return false; + + assert(false, "Found unhandled card mark type"); + + return false; +} + +// Assumes that only the base or the end changes. This allows indentification +// of the region that is being resized. The +// CardTableModRefBS::resize_covered_region() is used for the normal case +// where the covered regions are growing or shrinking at the high end. +// The method resize_covered_region_by_end() is analogous to +// CardTableModRefBS::resize_covered_region() but +// for regions that grow or shrink at the low end. +void CardTableExtension::resize_covered_region(MemRegion new_region) { + + for (int i = 0; i < _cur_covered_regions; i++) { + if (_covered[i].start() == new_region.start()) { + // Found a covered region with the same start as the + // new region. The region is growing or shrinking + // from the start of the region. + resize_covered_region_by_start(new_region); + return; + } + if (_covered[i].start() > new_region.start()) { + break; + } + } + + int changed_region = -1; + for (int j = 0; j < _cur_covered_regions; j++) { + if (_covered[j].end() == new_region.end()) { + changed_region = j; + // This is a case where the covered region is growing or shrinking + // at the start of the region. + assert(changed_region != -1, "Don't expect to add a covered region"); + assert(_covered[changed_region].byte_size() != new_region.byte_size(), + "The sizes should be different here"); + resize_covered_region_by_end(changed_region, new_region); + return; + } + } + // This should only be a new covered region (where no existing + // covered region matches at the start or the end). + assert(_cur_covered_regions < _max_covered_regions, + "An existing region should have been found"); + resize_covered_region_by_start(new_region); +} + +void CardTableExtension::resize_covered_region_by_start(MemRegion new_region) { + CardTableModRefBS::resize_covered_region(new_region); + debug_only(verify_guard();) +} + +void CardTableExtension::resize_covered_region_by_end(int changed_region, + MemRegion new_region) { + assert(SafepointSynchronize::is_at_safepoint(), + "Only expect an expansion at the low end at a GC"); + debug_only(verify_guard();) +#ifdef ASSERT + for (int k = 0; k < _cur_covered_regions; k++) { + if (_covered[k].end() == new_region.end()) { + assert(changed_region == k, "Changed region is incorrect"); + break; + } + } +#endif + + // Commit new or uncommit old pages, if necessary. + resize_commit_uncommit(changed_region, new_region); + + // Update card table entries + resize_update_card_table_entries(changed_region, new_region); + + // Set the new start of the committed region + resize_update_committed_table(changed_region, new_region); + + // Update the covered region + resize_update_covered_table(changed_region, new_region); + + if (TraceCardTableModRefBS) { + int ind = changed_region; + gclog_or_tty->print_cr("CardTableModRefBS::resize_covered_region: "); + gclog_or_tty->print_cr(" " + " _covered[%d].start(): " INTPTR_FORMAT + " _covered[%d].last(): " INTPTR_FORMAT, + ind, _covered[ind].start(), + ind, _covered[ind].last()); + gclog_or_tty->print_cr(" " + " _committed[%d].start(): " INTPTR_FORMAT + " _committed[%d].last(): " INTPTR_FORMAT, + ind, _committed[ind].start(), + ind, _committed[ind].last()); + gclog_or_tty->print_cr(" " + " byte_for(start): " INTPTR_FORMAT + " byte_for(last): " INTPTR_FORMAT, + byte_for(_covered[ind].start()), + byte_for(_covered[ind].last())); + gclog_or_tty->print_cr(" " + " addr_for(start): " INTPTR_FORMAT + " addr_for(last): " INTPTR_FORMAT, + addr_for((jbyte*) _committed[ind].start()), + addr_for((jbyte*) _committed[ind].last())); + } + debug_only(verify_guard();) +} + +void CardTableExtension::resize_commit_uncommit(int changed_region, + MemRegion new_region) { + // Commit new or uncommit old pages, if necessary. + MemRegion cur_committed = _committed[changed_region]; + assert(_covered[changed_region].end() == new_region.end(), + "The ends of the regions are expected to match"); + // Extend the start of this _committed region to + // to cover the start of any previous _committed region. + // This forms overlapping regions, but never interior regions. + HeapWord* min_prev_start = lowest_prev_committed_start(changed_region); + if (min_prev_start < cur_committed.start()) { + // Only really need to set start of "cur_committed" to + // the new start (min_prev_start) but assertion checking code + // below use cur_committed.end() so make it correct. + MemRegion new_committed = + MemRegion(min_prev_start, cur_committed.end()); + cur_committed = new_committed; + } +#ifdef ASSERT + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(cur_committed.start() == + (HeapWord*) align_size_up((uintptr_t) cur_committed.start(), + os::vm_page_size()), + "Starts should have proper alignment"); +#endif + + jbyte* new_start = byte_for(new_region.start()); + // Round down because this is for the start address + HeapWord* new_start_aligned = + (HeapWord*)align_size_down((uintptr_t)new_start, os::vm_page_size()); + // The guard page is always committed and should not be committed over. + // This method is used in cases where the generation is growing toward + // lower addresses but the guard region is still at the end of the + // card table. That still makes sense when looking for writes + // off the end of the card table. + if (new_start_aligned < cur_committed.start()) { + // Expand the committed region + // + // Case A + // |+ guard +| + // |+ cur committed +++++++++| + // |+ new committed +++++++++++++++++| + // + // Case B + // |+ guard +| + // |+ cur committed +| + // |+ new committed +++++++| + // + // These are not expected because the calculation of the + // cur committed region and the new committed region + // share the same end for the covered region. + // Case C + // |+ guard +| + // |+ cur committed +| + // |+ new committed +++++++++++++++++| + // Case D + // |+ guard +| + // |+ cur committed +++++++++++| + // |+ new committed +++++++| + + HeapWord* new_end_for_commit = + MIN2(cur_committed.end(), _guard_region.start()); + MemRegion new_committed = + MemRegion(new_start_aligned, new_end_for_commit); + if(!new_committed.is_empty()) { + if (!os::commit_memory((char*)new_committed.start(), + new_committed.byte_size())) { + vm_exit_out_of_memory(new_committed.byte_size(), + "card table expansion"); + } + } + } else if (new_start_aligned > cur_committed.start()) { + // Shrink the committed region + MemRegion uncommit_region = committed_unique_to_self(changed_region, + MemRegion(cur_committed.start(), new_start_aligned)); + if (!uncommit_region.is_empty()) { + if (!os::uncommit_memory((char*)uncommit_region.start(), + uncommit_region.byte_size())) { + vm_exit_out_of_memory(uncommit_region.byte_size(), + "card table contraction"); + } + } + } + assert(_committed[changed_region].end() == cur_committed.end(), + "end should not change"); +} + +void CardTableExtension::resize_update_committed_table(int changed_region, + MemRegion new_region) { + + jbyte* new_start = byte_for(new_region.start()); + // Set the new start of the committed region + HeapWord* new_start_aligned = + (HeapWord*)align_size_down((uintptr_t)new_start, + os::vm_page_size()); + MemRegion new_committed = MemRegion(new_start_aligned, + _committed[changed_region].end()); + _committed[changed_region] = new_committed; + _committed[changed_region].set_start(new_start_aligned); +} + +void CardTableExtension::resize_update_card_table_entries(int changed_region, + MemRegion new_region) { + debug_only(verify_guard();) + MemRegion original_covered = _covered[changed_region]; + // Initialize the card entries. Only consider the + // region covered by the card table (_whole_heap) + jbyte* entry; + if (new_region.start() < _whole_heap.start()) { + entry = byte_for(_whole_heap.start()); + } else { + entry = byte_for(new_region.start()); + } + jbyte* end = byte_for(original_covered.start()); + // If _whole_heap starts at the original covered regions start, + // this loop will not execute. + while (entry < end) { *entry++ = clean_card; } +} + +void CardTableExtension::resize_update_covered_table(int changed_region, + MemRegion new_region) { + // Update the covered region + _covered[changed_region].set_start(new_region.start()); + _covered[changed_region].set_word_size(new_region.word_size()); + + // reorder regions. There should only be at most 1 out + // of order. + for (int i = _cur_covered_regions-1 ; i > 0; i--) { + if (_covered[i].start() < _covered[i-1].start()) { + MemRegion covered_mr = _covered[i-1]; + _covered[i-1] = _covered[i]; + _covered[i] = covered_mr; + MemRegion committed_mr = _committed[i-1]; + _committed[i-1] = _committed[i]; + _committed[i] = committed_mr; + break; + } + } +#ifdef ASSERT + for (int m = 0; m < _cur_covered_regions-1; m++) { + assert(_covered[m].start() <= _covered[m+1].start(), + "Covered regions out of order"); + assert(_committed[m].start() <= _committed[m+1].start(), + "Committed regions out of order"); + } +#endif +} + +// Returns the start of any committed region that is lower than +// the target committed region (index ind) and that intersects the +// target region. If none, return start of target region. +// +// ------------- +// | | +// ------------- +// ------------ +// | target | +// ------------ +// ------------- +// | | +// ------------- +// ^ returns this +// +// ------------- +// | | +// ------------- +// ------------ +// | target | +// ------------ +// ------------- +// | | +// ------------- +// ^ returns this + +HeapWord* CardTableExtension::lowest_prev_committed_start(int ind) const { + assert(_cur_covered_regions >= 0, "Expecting at least on region"); + HeapWord* min_start = _committed[ind].start(); + for (int j = 0; j < ind; j++) { + HeapWord* this_start = _committed[j].start(); + if ((this_start < min_start) && + !(_committed[j].intersection(_committed[ind])).is_empty()) { + min_start = this_start; + } + } + return min_start; +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.hpp new file mode 100644 index 00000000000..39c03a9412a --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.hpp @@ -0,0 +1,108 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class MutableSpace; +class ObjectStartArray; +class PSPromotionManager; +class GCTaskQueue; + +class CardTableExtension : public CardTableModRefBS { + private: + // Support methods for resizing the card table. + void resize_commit_uncommit(int changed_region, MemRegion new_region); + void resize_update_card_table_entries(int changed_region, + MemRegion new_region); + void resize_update_committed_table(int changed_region, MemRegion new_region); + void resize_update_covered_table(int changed_region, MemRegion new_region); + + protected: + + static void verify_all_young_refs_precise_helper(MemRegion mr); + + public: + enum ExtendedCardValue { + youngergen_card = CardTableModRefBS::CT_MR_BS_last_reserved + 1, + verify_card = CardTableModRefBS::CT_MR_BS_last_reserved + 5 + }; + + CardTableExtension(MemRegion whole_heap, int max_covered_regions) : + CardTableModRefBS(whole_heap, max_covered_regions) { } + + // Too risky for the 4/10/02 putback + // BarrierSet::Name kind() { return BarrierSet::CardTableExtension; } + + // Scavenge support + void scavenge_contents(ObjectStartArray* start_array, + MutableSpace* sp, + HeapWord* space_top, + PSPromotionManager* pm); + + void scavenge_contents_parallel(ObjectStartArray* start_array, + MutableSpace* sp, + HeapWord* space_top, + PSPromotionManager* pm, + uint stripe_number); + + // Verification + static void verify_all_young_refs_imprecise(); + static void verify_all_young_refs_precise(); + + bool addr_is_marked_imprecise(void *addr); + bool addr_is_marked_precise(void *addr); + + void set_card_newgen(void* addr) { jbyte* p = byte_for(addr); *p = verify_card; } + + // Testers for entries + static bool card_is_dirty(int value) { return value == dirty_card; } + static bool card_is_newgen(int value) { return value == youngergen_card; } + static bool card_is_clean(int value) { return value == clean_card; } + static bool card_is_verify(int value) { return value == verify_card; } + + // Card marking + void inline_write_ref_field_gc(oop* field, oop new_val) { + jbyte* byte = byte_for(field); + *byte = youngergen_card; + } + + // Adaptive size policy support + // Allows adjustment of the base and size of the covered regions + void resize_covered_region(MemRegion new_region); + // Finds the covered region to resize based on the start address + // of the covered regions. + void resize_covered_region_by_start(MemRegion new_region); + // Finds the covered region to resize based on the end address + // of the covered regions. + void resize_covered_region_by_end(int changed_region, MemRegion new_region); + // Finds the lowest start address of a covered region that is + // previous (i.e., lower index) to the covered region with index "ind". + HeapWord* lowest_prev_committed_start(int ind) const; + +#ifdef ASSERT + + bool is_valid_card_address(jbyte* addr) { + return (addr >= _byte_map) && (addr < _byte_map + _byte_map_size); + } + +#endif // ASSERT +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskManager.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskManager.cpp new file mode 100644 index 00000000000..297b8ea43ee --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskManager.cpp @@ -0,0 +1,919 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_gcTaskManager.cpp.incl" + +// +// GCTask +// + +const char* GCTask::Kind::to_string(kind value) { + const char* result = "unknown GCTask kind"; + switch (value) { + default: + result = "unknown GCTask kind"; + break; + case unknown_task: + result = "unknown task"; + break; + case ordinary_task: + result = "ordinary task"; + break; + case barrier_task: + result = "barrier task"; + break; + case noop_task: + result = "noop task"; + break; + } + return result; +}; + +GCTask::GCTask() : + _kind(Kind::ordinary_task), + _affinity(GCTaskManager::sentinel_worker()){ + initialize(); +} + +GCTask::GCTask(Kind::kind kind) : + _kind(kind), + _affinity(GCTaskManager::sentinel_worker()) { + initialize(); +} + +GCTask::GCTask(uint affinity) : + _kind(Kind::ordinary_task), + _affinity(affinity) { + initialize(); +} + +GCTask::GCTask(Kind::kind kind, uint affinity) : + _kind(kind), + _affinity(affinity) { + initialize(); +} + +void GCTask::initialize() { + _older = NULL; + _newer = NULL; +} + +void GCTask::destruct() { + assert(older() == NULL, "shouldn't have an older task"); + assert(newer() == NULL, "shouldn't have a newer task"); + // Nothing to do. +} + +NOT_PRODUCT( +void GCTask::print(const char* message) const { + tty->print(INTPTR_FORMAT " <- " INTPTR_FORMAT "(%u) -> " INTPTR_FORMAT, + newer(), this, affinity(), older()); +} +) + +// +// GCTaskQueue +// + +GCTaskQueue* GCTaskQueue::create() { + GCTaskQueue* result = new GCTaskQueue(false); + if (TraceGCTaskQueue) { + tty->print_cr("GCTaskQueue::create()" + " returns " INTPTR_FORMAT, result); + } + return result; +} + +GCTaskQueue* GCTaskQueue::create_on_c_heap() { + GCTaskQueue* result = new(ResourceObj::C_HEAP) GCTaskQueue(true); + if (TraceGCTaskQueue) { + tty->print_cr("GCTaskQueue::create_on_c_heap()" + " returns " INTPTR_FORMAT, + result); + } + return result; +} + +GCTaskQueue::GCTaskQueue(bool on_c_heap) : + _is_c_heap_obj(on_c_heap) { + initialize(); + if (TraceGCTaskQueue) { + tty->print_cr("[" INTPTR_FORMAT "]" + " GCTaskQueue::GCTaskQueue() constructor", + this); + } +} + +void GCTaskQueue::destruct() { + // Nothing to do. +} + +void GCTaskQueue::destroy(GCTaskQueue* that) { + if (TraceGCTaskQueue) { + tty->print_cr("[" INTPTR_FORMAT "]" + " GCTaskQueue::destroy()" + " is_c_heap_obj: %s", + that, + that->is_c_heap_obj() ? "true" : "false"); + } + // That instance may have been allocated as a CHeapObj, + // in which case we have to free it explicitly. + if (that != NULL) { + that->destruct(); + assert(that->is_empty(), "should be empty"); + if (that->is_c_heap_obj()) { + FreeHeap(that); + } + } +} + +void GCTaskQueue::initialize() { + set_insert_end(NULL); + set_remove_end(NULL); + set_length(0); +} + +// Enqueue one task. +void GCTaskQueue::enqueue(GCTask* task) { + if (TraceGCTaskQueue) { + tty->print_cr("[" INTPTR_FORMAT "]" + " GCTaskQueue::enqueue(task: " + INTPTR_FORMAT ")", + this, task); + print("before:"); + } + assert(task != NULL, "shouldn't have null task"); + assert(task->older() == NULL, "shouldn't be on queue"); + assert(task->newer() == NULL, "shouldn't be on queue"); + task->set_newer(NULL); + task->set_older(insert_end()); + if (is_empty()) { + set_remove_end(task); + } else { + insert_end()->set_newer(task); + } + set_insert_end(task); + increment_length(); + if (TraceGCTaskQueue) { + print("after:"); + } +} + +// Enqueue a whole list of tasks. Empties the argument list. +void GCTaskQueue::enqueue(GCTaskQueue* list) { + if (TraceGCTaskQueue) { + tty->print_cr("[" INTPTR_FORMAT "]" + " GCTaskQueue::enqueue(list: " + INTPTR_FORMAT ")", + this); + print("before:"); + list->print("list:"); + } + if (list->is_empty()) { + // Enqueuing the empty list: nothing to do. + return; + } + uint list_length = list->length(); + if (is_empty()) { + // Enqueuing to empty list: just acquire elements. + set_insert_end(list->insert_end()); + set_remove_end(list->remove_end()); + set_length(list_length); + } else { + // Prepend argument list to our queue. + list->remove_end()->set_older(insert_end()); + insert_end()->set_newer(list->remove_end()); + set_insert_end(list->insert_end()); + // empty the argument list. + } + set_length(length() + list_length); + list->initialize(); + if (TraceGCTaskQueue) { + print("after:"); + list->print("list:"); + } +} + +// Dequeue one task. +GCTask* GCTaskQueue::dequeue() { + if (TraceGCTaskQueue) { + tty->print_cr("[" INTPTR_FORMAT "]" + " GCTaskQueue::dequeue()", this); + print("before:"); + } + assert(!is_empty(), "shouldn't dequeue from empty list"); + GCTask* result = remove(); + assert(result != NULL, "shouldn't have NULL task"); + if (TraceGCTaskQueue) { + tty->print_cr(" return: " INTPTR_FORMAT, result); + print("after:"); + } + return result; +} + +// Dequeue one task, preferring one with affinity. +GCTask* GCTaskQueue::dequeue(uint affinity) { + if (TraceGCTaskQueue) { + tty->print_cr("[" INTPTR_FORMAT "]" + " GCTaskQueue::dequeue(%u)", this, affinity); + print("before:"); + } + assert(!is_empty(), "shouldn't dequeue from empty list"); + // Look down to the next barrier for a task with this affinity. + GCTask* result = NULL; + for (GCTask* element = remove_end(); + element != NULL; + element = element->newer()) { + if (element->is_barrier_task()) { + // Don't consider barrier tasks, nor past them. + result = NULL; + break; + } + if (element->affinity() == affinity) { + result = remove(element); + break; + } + } + // If we didn't find anything with affinity, just take the next task. + if (result == NULL) { + result = remove(); + } + if (TraceGCTaskQueue) { + tty->print_cr(" return: " INTPTR_FORMAT, result); + print("after:"); + } + return result; +} + +GCTask* GCTaskQueue::remove() { + // Dequeue from remove end. + GCTask* result = remove_end(); + assert(result != NULL, "shouldn't have null task"); + assert(result->older() == NULL, "not the remove_end"); + set_remove_end(result->newer()); + if (remove_end() == NULL) { + assert(insert_end() == result, "not a singleton"); + set_insert_end(NULL); + } else { + remove_end()->set_older(NULL); + } + result->set_newer(NULL); + decrement_length(); + assert(result->newer() == NULL, "shouldn't be on queue"); + assert(result->older() == NULL, "shouldn't be on queue"); + return result; +} + +GCTask* GCTaskQueue::remove(GCTask* task) { + // This is slightly more work, and has slightly fewer asserts + // than removing from the remove end. + assert(task != NULL, "shouldn't have null task"); + GCTask* result = task; + if (result->newer() != NULL) { + result->newer()->set_older(result->older()); + } else { + assert(insert_end() == result, "not youngest"); + set_insert_end(result->older()); + } + if (result->older() != NULL) { + result->older()->set_newer(result->newer()); + } else { + assert(remove_end() == result, "not oldest"); + set_remove_end(result->newer()); + } + result->set_newer(NULL); + result->set_older(NULL); + decrement_length(); + return result; +} + +NOT_PRODUCT( +void GCTaskQueue::print(const char* message) const { + tty->print_cr("[" INTPTR_FORMAT "] GCTaskQueue:" + " insert_end: " INTPTR_FORMAT + " remove_end: " INTPTR_FORMAT + " %s", + this, insert_end(), remove_end(), message); + for (GCTask* element = insert_end(); + element != NULL; + element = element->older()) { + element->print(" "); + tty->cr(); + } +} +) + +// +// SynchronizedGCTaskQueue +// + +SynchronizedGCTaskQueue::SynchronizedGCTaskQueue(GCTaskQueue* queue_arg, + Monitor * lock_arg) : + _unsynchronized_queue(queue_arg), + _lock(lock_arg) { + assert(unsynchronized_queue() != NULL, "null queue"); + assert(lock() != NULL, "null lock"); +} + +SynchronizedGCTaskQueue::~SynchronizedGCTaskQueue() { + // Nothing to do. +} + +// +// GCTaskManager +// +GCTaskManager::GCTaskManager(uint workers) : + _workers(workers), + _ndc(NULL) { + initialize(); +} + +GCTaskManager::GCTaskManager(uint workers, NotifyDoneClosure* ndc) : + _workers(workers), + _ndc(ndc) { + initialize(); +} + +void GCTaskManager::initialize() { + if (TraceGCTaskManager) { + tty->print_cr("GCTaskManager::initialize: workers: %u", workers()); + } + assert(workers() != 0, "no workers"); + _monitor = new Monitor(Mutex::barrier, // rank + "GCTaskManager monitor", // name + Mutex::_allow_vm_block_flag); // allow_vm_block + // The queue for the GCTaskManager must be a CHeapObj. + GCTaskQueue* unsynchronized_queue = GCTaskQueue::create_on_c_heap(); + _queue = SynchronizedGCTaskQueue::create(unsynchronized_queue, lock()); + _noop_task = NoopGCTask::create_on_c_heap(); + _resource_flag = NEW_C_HEAP_ARRAY(bool, workers()); + { + // Set up worker threads. + // Distribute the workers among the available processors, + // unless we were told not to, or if the os doesn't want to. + uint* processor_assignment = NEW_C_HEAP_ARRAY(uint, workers()); + if (!BindGCTaskThreadsToCPUs || + !os::distribute_processes(workers(), processor_assignment)) { + for (uint a = 0; a < workers(); a += 1) { + processor_assignment[a] = sentinel_worker(); + } + } + _thread = NEW_C_HEAP_ARRAY(GCTaskThread*, workers()); + for (uint t = 0; t < workers(); t += 1) { + set_thread(t, GCTaskThread::create(this, t, processor_assignment[t])); + } + if (TraceGCTaskThread) { + tty->print("GCTaskManager::initialize: distribution:"); + for (uint t = 0; t < workers(); t += 1) { + tty->print(" %u", processor_assignment[t]); + } + tty->cr(); + } + FREE_C_HEAP_ARRAY(uint, processor_assignment); + } + reset_busy_workers(); + set_unblocked(); + for (uint w = 0; w < workers(); w += 1) { + set_resource_flag(w, false); + } + reset_delivered_tasks(); + reset_completed_tasks(); + reset_noop_tasks(); + reset_barriers(); + reset_emptied_queue(); + for (uint s = 0; s < workers(); s += 1) { + thread(s)->start(); + } +} + +GCTaskManager::~GCTaskManager() { + assert(busy_workers() == 0, "still have busy workers"); + assert(queue()->is_empty(), "still have queued work"); + NoopGCTask::destroy(_noop_task); + _noop_task = NULL; + if (_thread != NULL) { + for (uint i = 0; i < workers(); i += 1) { + GCTaskThread::destroy(thread(i)); + set_thread(i, NULL); + } + FREE_C_HEAP_ARRAY(GCTaskThread*, _thread); + _thread = NULL; + } + if (_resource_flag != NULL) { + FREE_C_HEAP_ARRAY(bool, _resource_flag); + _resource_flag = NULL; + } + if (queue() != NULL) { + GCTaskQueue* unsynchronized_queue = queue()->unsynchronized_queue(); + GCTaskQueue::destroy(unsynchronized_queue); + SynchronizedGCTaskQueue::destroy(queue()); + _queue = NULL; + } + if (monitor() != NULL) { + delete monitor(); + _monitor = NULL; + } +} + +void GCTaskManager::print_task_time_stamps() { + for(uint i=0; iprint_task_time_stamps(); + } +} + +void GCTaskManager::print_threads_on(outputStream* st) { + uint num_thr = workers(); + for (uint i = 0; i < num_thr; i++) { + thread(i)->print_on(st); + st->cr(); + } +} + +void GCTaskManager::threads_do(ThreadClosure* tc) { + assert(tc != NULL, "Null ThreadClosure"); + uint num_thr = workers(); + for (uint i = 0; i < num_thr; i++) { + tc->do_thread(thread(i)); + } +} + +GCTaskThread* GCTaskManager::thread(uint which) { + assert(which < workers(), "index out of bounds"); + assert(_thread[which] != NULL, "shouldn't have null thread"); + return _thread[which]; +} + +void GCTaskManager::set_thread(uint which, GCTaskThread* value) { + assert(which < workers(), "index out of bounds"); + assert(value != NULL, "shouldn't have null thread"); + _thread[which] = value; +} + +void GCTaskManager::add_task(GCTask* task) { + assert(task != NULL, "shouldn't have null task"); + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + if (TraceGCTaskManager) { + tty->print_cr("GCTaskManager::add_task(" INTPTR_FORMAT " [%s])", + task, GCTask::Kind::to_string(task->kind())); + } + queue()->enqueue(task); + // Notify with the lock held to avoid missed notifies. + if (TraceGCTaskManager) { + tty->print_cr(" GCTaskManager::add_task (%s)->notify_all", + monitor()->name()); + } + (void) monitor()->notify_all(); + // Release monitor(). +} + +void GCTaskManager::add_list(GCTaskQueue* list) { + assert(list != NULL, "shouldn't have null task"); + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + if (TraceGCTaskManager) { + tty->print_cr("GCTaskManager::add_list(%u)", list->length()); + } + queue()->enqueue(list); + // Notify with the lock held to avoid missed notifies. + if (TraceGCTaskManager) { + tty->print_cr(" GCTaskManager::add_list (%s)->notify_all", + monitor()->name()); + } + (void) monitor()->notify_all(); + // Release monitor(). +} + +GCTask* GCTaskManager::get_task(uint which) { + GCTask* result = NULL; + // Grab the queue lock. + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + // Wait while the queue is block or + // there is nothing to do, except maybe release resources. + while (is_blocked() || + (queue()->is_empty() && !should_release_resources(which))) { + if (TraceGCTaskManager) { + tty->print_cr("GCTaskManager::get_task(%u)" + " blocked: %s" + " empty: %s" + " release: %s", + which, + is_blocked() ? "true" : "false", + queue()->is_empty() ? "true" : "false", + should_release_resources(which) ? "true" : "false"); + tty->print_cr(" => (%s)->wait()", + monitor()->name()); + } + monitor()->wait(Mutex::_no_safepoint_check_flag, 0); + } + // We've reacquired the queue lock here. + // Figure out which condition caused us to exit the loop above. + if (!queue()->is_empty()) { + if (UseGCTaskAffinity) { + result = queue()->dequeue(which); + } else { + result = queue()->dequeue(); + } + if (result->is_barrier_task()) { + assert(which != sentinel_worker(), + "blocker shouldn't be bogus"); + set_blocking_worker(which); + } + } else { + // The queue is empty, but we were woken up. + // Just hand back a Noop task, + // in case someone wanted us to release resources, or whatever. + result = noop_task(); + increment_noop_tasks(); + } + assert(result != NULL, "shouldn't have null task"); + if (TraceGCTaskManager) { + tty->print_cr("GCTaskManager::get_task(%u) => " INTPTR_FORMAT " [%s]", + which, result, GCTask::Kind::to_string(result->kind())); + tty->print_cr(" %s", result->name()); + } + increment_busy_workers(); + increment_delivered_tasks(); + return result; + // Release monitor(). +} + +void GCTaskManager::note_completion(uint which) { + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + if (TraceGCTaskManager) { + tty->print_cr("GCTaskManager::note_completion(%u)", which); + } + // If we are blocked, check if the completing thread is the blocker. + if (blocking_worker() == which) { + assert(blocking_worker() != sentinel_worker(), + "blocker shouldn't be bogus"); + increment_barriers(); + set_unblocked(); + } + increment_completed_tasks(); + uint active = decrement_busy_workers(); + if ((active == 0) && (queue()->is_empty())) { + increment_emptied_queue(); + if (TraceGCTaskManager) { + tty->print_cr(" GCTaskManager::note_completion(%u) done", which); + } + // Notify client that we are done. + NotifyDoneClosure* ndc = notify_done_closure(); + if (ndc != NULL) { + ndc->notify(this); + } + } + if (TraceGCTaskManager) { + tty->print_cr(" GCTaskManager::note_completion(%u) (%s)->notify_all", + which, monitor()->name()); + tty->print_cr(" " + " blocked: %s" + " empty: %s" + " release: %s", + is_blocked() ? "true" : "false", + queue()->is_empty() ? "true" : "false", + should_release_resources(which) ? "true" : "false"); + tty->print_cr(" " + " delivered: %u" + " completed: %u" + " barriers: %u" + " emptied: %u", + delivered_tasks(), + completed_tasks(), + barriers(), + emptied_queue()); + } + // Tell everyone that a task has completed. + (void) monitor()->notify_all(); + // Release monitor(). +} + +uint GCTaskManager::increment_busy_workers() { + assert(queue()->own_lock(), "don't own the lock"); + _busy_workers += 1; + return _busy_workers; +} + +uint GCTaskManager::decrement_busy_workers() { + assert(queue()->own_lock(), "don't own the lock"); + _busy_workers -= 1; + return _busy_workers; +} + +void GCTaskManager::release_all_resources() { + // If you want this to be done atomically, do it in a BarrierGCTask. + for (uint i = 0; i < workers(); i += 1) { + set_resource_flag(i, true); + } +} + +bool GCTaskManager::should_release_resources(uint which) { + // This can be done without a lock because each thread reads one element. + return resource_flag(which); +} + +void GCTaskManager::note_release(uint which) { + // This can be done without a lock because each thread writes one element. + set_resource_flag(which, false); +} + +void GCTaskManager::execute_and_wait(GCTaskQueue* list) { + WaitForBarrierGCTask* fin = WaitForBarrierGCTask::create(); + list->enqueue(fin); + add_list(list); + fin->wait_for(); + // We have to release the barrier tasks! + WaitForBarrierGCTask::destroy(fin); +} + +bool GCTaskManager::resource_flag(uint which) { + assert(which < workers(), "index out of bounds"); + return _resource_flag[which]; +} + +void GCTaskManager::set_resource_flag(uint which, bool value) { + assert(which < workers(), "index out of bounds"); + _resource_flag[which] = value; +} + +// +// NoopGCTask +// + +NoopGCTask* NoopGCTask::create() { + NoopGCTask* result = new NoopGCTask(false); + return result; +} + +NoopGCTask* NoopGCTask::create_on_c_heap() { + NoopGCTask* result = new(ResourceObj::C_HEAP) NoopGCTask(true); + return result; +} + +void NoopGCTask::destroy(NoopGCTask* that) { + if (that != NULL) { + that->destruct(); + if (that->is_c_heap_obj()) { + FreeHeap(that); + } + } +} + +void NoopGCTask::destruct() { + // This has to know it's superclass structure, just like the constructor. + this->GCTask::destruct(); + // Nothing else to do. +} + +// +// BarrierGCTask +// + +void BarrierGCTask::do_it(GCTaskManager* manager, uint which) { + // Wait for this to be the only busy worker. + // ??? I thought of having a StackObj class + // whose constructor would grab the lock and come to the barrier, + // and whose destructor would release the lock, + // but that seems like too much mechanism for two lines of code. + MutexLockerEx ml(manager->lock(), Mutex::_no_safepoint_check_flag); + do_it_internal(manager, which); + // Release manager->lock(). +} + +void BarrierGCTask::do_it_internal(GCTaskManager* manager, uint which) { + // Wait for this to be the only busy worker. + assert(manager->monitor()->owned_by_self(), "don't own the lock"); + assert(manager->is_blocked(), "manager isn't blocked"); + while (manager->busy_workers() > 1) { + if (TraceGCTaskManager) { + tty->print_cr("BarrierGCTask::do_it(%u) waiting on %u workers", + which, manager->busy_workers()); + } + manager->monitor()->wait(Mutex::_no_safepoint_check_flag, 0); + } +} + +void BarrierGCTask::destruct() { + this->GCTask::destruct(); + // Nothing else to do. +} + +// +// ReleasingBarrierGCTask +// + +void ReleasingBarrierGCTask::do_it(GCTaskManager* manager, uint which) { + MutexLockerEx ml(manager->lock(), Mutex::_no_safepoint_check_flag); + do_it_internal(manager, which); + manager->release_all_resources(); + // Release manager->lock(). +} + +void ReleasingBarrierGCTask::destruct() { + this->BarrierGCTask::destruct(); + // Nothing else to do. +} + +// +// NotifyingBarrierGCTask +// + +void NotifyingBarrierGCTask::do_it(GCTaskManager* manager, uint which) { + MutexLockerEx ml(manager->lock(), Mutex::_no_safepoint_check_flag); + do_it_internal(manager, which); + NotifyDoneClosure* ndc = notify_done_closure(); + if (ndc != NULL) { + ndc->notify(manager); + } + // Release manager->lock(). +} + +void NotifyingBarrierGCTask::destruct() { + this->BarrierGCTask::destruct(); + // Nothing else to do. +} + +// +// WaitForBarrierGCTask +// +WaitForBarrierGCTask* WaitForBarrierGCTask::create() { + WaitForBarrierGCTask* result = new WaitForBarrierGCTask(false); + return result; +} + +WaitForBarrierGCTask* WaitForBarrierGCTask::create_on_c_heap() { + WaitForBarrierGCTask* result = new WaitForBarrierGCTask(true); + return result; +} + +WaitForBarrierGCTask::WaitForBarrierGCTask(bool on_c_heap) : + _is_c_heap_obj(on_c_heap) { + _monitor = MonitorSupply::reserve(); + set_should_wait(true); + if (TraceGCTaskManager) { + tty->print_cr("[" INTPTR_FORMAT "]" + " WaitForBarrierGCTask::WaitForBarrierGCTask()" + " monitor: " INTPTR_FORMAT, + this, monitor()); + } +} + +void WaitForBarrierGCTask::destroy(WaitForBarrierGCTask* that) { + if (that != NULL) { + if (TraceGCTaskManager) { + tty->print_cr("[" INTPTR_FORMAT "]" + " WaitForBarrierGCTask::destroy()" + " is_c_heap_obj: %s" + " monitor: " INTPTR_FORMAT, + that, + that->is_c_heap_obj() ? "true" : "false", + that->monitor()); + } + that->destruct(); + if (that->is_c_heap_obj()) { + FreeHeap(that); + } + } +} + +void WaitForBarrierGCTask::destruct() { + assert(monitor() != NULL, "monitor should not be NULL"); + if (TraceGCTaskManager) { + tty->print_cr("[" INTPTR_FORMAT "]" + " WaitForBarrierGCTask::destruct()" + " monitor: " INTPTR_FORMAT, + this, monitor()); + } + this->BarrierGCTask::destruct(); + // Clean up that should be in the destructor, + // except that ResourceMarks don't call destructors. + if (monitor() != NULL) { + MonitorSupply::release(monitor()); + } + _monitor = (Monitor*) 0xDEAD000F; +} + +void WaitForBarrierGCTask::do_it(GCTaskManager* manager, uint which) { + if (TraceGCTaskManager) { + tty->print_cr("[" INTPTR_FORMAT "]" + " WaitForBarrierGCTask::do_it() waiting for idle" + " monitor: " INTPTR_FORMAT, + this, monitor()); + } + { + // First, wait for the barrier to arrive. + MutexLockerEx ml(manager->lock(), Mutex::_no_safepoint_check_flag); + do_it_internal(manager, which); + // Release manager->lock(). + } + { + // Then notify the waiter. + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + set_should_wait(false); + // Waiter doesn't miss the notify in the wait_for method + // since it checks the flag after grabbing the monitor. + if (TraceGCTaskManager) { + tty->print_cr("[" INTPTR_FORMAT "]" + " WaitForBarrierGCTask::do_it()" + " [" INTPTR_FORMAT "] (%s)->notify_all()", + this, monitor(), monitor()->name()); + } + monitor()->notify_all(); + // Release monitor(). + } +} + +void WaitForBarrierGCTask::wait_for() { + if (TraceGCTaskManager) { + tty->print_cr("[" INTPTR_FORMAT "]" + " WaitForBarrierGCTask::wait_for()" + " should_wait: %s", + this, should_wait() ? "true" : "false"); + } + { + // Grab the lock and check again. + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + while (should_wait()) { + if (TraceGCTaskManager) { + tty->print_cr("[" INTPTR_FORMAT "]" + " WaitForBarrierGCTask::wait_for()" + " [" INTPTR_FORMAT "] (%s)->wait()", + this, monitor(), monitor()->name()); + } + monitor()->wait(Mutex::_no_safepoint_check_flag, 0); + } + // Reset the flag in case someone reuses this task. + set_should_wait(true); + if (TraceGCTaskManager) { + tty->print_cr("[" INTPTR_FORMAT "]" + " WaitForBarrierGCTask::wait_for() returns" + " should_wait: %s", + this, should_wait() ? "true" : "false"); + } + // Release monitor(). + } +} + +Mutex* MonitorSupply::_lock = NULL; +GrowableArray* MonitorSupply::_freelist = NULL; + +Monitor* MonitorSupply::reserve() { + Monitor* result = NULL; + // Lazy initialization: possible race. + if (lock() == NULL) { + _lock = new Mutex(Mutex::barrier, // rank + "MonitorSupply mutex", // name + Mutex::_allow_vm_block_flag); // allow_vm_block + } + { + MutexLockerEx ml(lock()); + // Lazy initialization. + if (freelist() == NULL) { + _freelist = + new(ResourceObj::C_HEAP) GrowableArray(ParallelGCThreads, + true); + } + if (! freelist()->is_empty()) { + result = freelist()->pop(); + } else { + result = new Monitor(Mutex::barrier, // rank + "MonitorSupply monitor", // name + Mutex::_allow_vm_block_flag); // allow_vm_block + } + guarantee(result != NULL, "shouldn't return NULL"); + assert(!result->is_locked(), "shouldn't be locked"); + // release lock(). + } + return result; +} + +void MonitorSupply::release(Monitor* instance) { + assert(instance != NULL, "shouldn't release NULL"); + assert(!instance->is_locked(), "shouldn't be locked"); + { + MutexLockerEx ml(lock()); + freelist()->push(instance); + // release lock(). + } +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskManager.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskManager.hpp new file mode 100644 index 00000000000..ba3af5cad8d --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskManager.hpp @@ -0,0 +1,638 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// The GCTaskManager is a queue of GCTasks, and accessors +// to allow the queue to be accessed from many threads. +// + +// Forward declarations of types defined in this file. +class GCTask; +class GCTaskQueue; +class SynchronizedGCTaskQueue; +class GCTaskManager; +class NotifyDoneClosure; +// Some useful subclasses of GCTask. You can also make up your own. +class NoopGCTask; +class BarrierGCTask; +class ReleasingBarrierGCTask; +class NotifyingBarrierGCTask; +class WaitForBarrierGCTask; +// A free list of Monitor*'s. +class MonitorSupply; + +// Forward declarations of classes referenced in this file via pointer. +class GCTaskThread; +class Mutex; +class Monitor; +class ThreadClosure; + +// The abstract base GCTask. +class GCTask : public ResourceObj { +public: + // Known kinds of GCTasks, for predicates. + class Kind : AllStatic { + public: + enum kind { + unknown_task, + ordinary_task, + barrier_task, + noop_task + }; + static const char* to_string(kind value); + }; +private: + // Instance state. + const Kind::kind _kind; // For runtime type checking. + const uint _affinity; // Which worker should run task. + GCTask* _newer; // Tasks are on doubly-linked ... + GCTask* _older; // ... lists. +public: + virtual char* name() { return (char *)"task"; } + + // Abstract do_it method + virtual void do_it(GCTaskManager* manager, uint which) = 0; + // Accessors + Kind::kind kind() const { + return _kind; + } + uint affinity() const { + return _affinity; + } + GCTask* newer() const { + return _newer; + } + void set_newer(GCTask* n) { + _newer = n; + } + GCTask* older() const { + return _older; + } + void set_older(GCTask* p) { + _older = p; + } + // Predicates. + bool is_ordinary_task() const { + return kind()==Kind::ordinary_task; + } + bool is_barrier_task() const { + return kind()==Kind::barrier_task; + } + bool is_noop_task() const { + return kind()==Kind::noop_task; + } + void print(const char* message) const PRODUCT_RETURN; +protected: + // Constructors: Only create subclasses. + // An ordinary GCTask. + GCTask(); + // A GCTask of a particular kind, usually barrier or noop. + GCTask(Kind::kind kind); + // An ordinary GCTask with an affinity. + GCTask(uint affinity); + // A GCTask of a particular kind, with and affinity. + GCTask(Kind::kind kind, uint affinity); + // We want a virtual destructor because virtual methods, + // but since ResourceObj's don't have their destructors + // called, we don't have one at all. Instead we have + // this method, which gets called by subclasses to clean up. + virtual void destruct(); + // Methods. + void initialize(); +}; + +// A doubly-linked list of GCTasks. +// The list is not synchronized, because sometimes we want to +// build up a list and then make it available to other threads. +// See also: SynchronizedGCTaskQueue. +class GCTaskQueue : public ResourceObj { +private: + // Instance state. + GCTask* _insert_end; // Tasks are enqueued at this end. + GCTask* _remove_end; // Tasks are dequeued from this end. + uint _length; // The current length of the queue. + const bool _is_c_heap_obj; // Is this a CHeapObj? +public: + // Factory create and destroy methods. + // Create as ResourceObj. + static GCTaskQueue* create(); + // Create as CHeapObj. + static GCTaskQueue* create_on_c_heap(); + // Destroyer. + static void destroy(GCTaskQueue* that); + // Accessors. + // These just examine the state of the queue. + bool is_empty() const { + assert(((insert_end() == NULL && remove_end() == NULL) || + (insert_end() != NULL && remove_end() != NULL)), + "insert_end and remove_end don't match"); + return insert_end() == NULL; + } + uint length() const { + return _length; + } + // Methods. + // Enqueue one task. + void enqueue(GCTask* task); + // Enqueue a list of tasks. Empties the argument list. + void enqueue(GCTaskQueue* list); + // Dequeue one task. + GCTask* dequeue(); + // Dequeue one task, preferring one with affinity. + GCTask* dequeue(uint affinity); +protected: + // Constructor. Clients use factory, but there might be subclasses. + GCTaskQueue(bool on_c_heap); + // Destructor-like method. + // Because ResourceMark doesn't call destructors. + // This method cleans up like one. + virtual void destruct(); + // Accessors. + GCTask* insert_end() const { + return _insert_end; + } + void set_insert_end(GCTask* value) { + _insert_end = value; + } + GCTask* remove_end() const { + return _remove_end; + } + void set_remove_end(GCTask* value) { + _remove_end = value; + } + void increment_length() { + _length += 1; + } + void decrement_length() { + _length -= 1; + } + void set_length(uint value) { + _length = value; + } + bool is_c_heap_obj() const { + return _is_c_heap_obj; + } + // Methods. + void initialize(); + GCTask* remove(); // Remove from remove end. + GCTask* remove(GCTask* task); // Remove from the middle. + void print(const char* message) const PRODUCT_RETURN; +}; + +// A GCTaskQueue that can be synchronized. +// This "has-a" GCTaskQueue and a mutex to do the exclusion. +class SynchronizedGCTaskQueue : public CHeapObj { +private: + // Instance state. + GCTaskQueue* _unsynchronized_queue; // Has-a unsynchronized queue. + Monitor * _lock; // Lock to control access. +public: + // Factory create and destroy methods. + static SynchronizedGCTaskQueue* create(GCTaskQueue* queue, Monitor * lock) { + return new SynchronizedGCTaskQueue(queue, lock); + } + static void destroy(SynchronizedGCTaskQueue* that) { + if (that != NULL) { + delete that; + } + } + // Accessors + GCTaskQueue* unsynchronized_queue() const { + return _unsynchronized_queue; + } + Monitor * lock() const { + return _lock; + } + // GCTaskQueue wrapper methods. + // These check that you hold the lock + // and then call the method on the queue. + bool is_empty() const { + guarantee(own_lock(), "don't own the lock"); + return unsynchronized_queue()->is_empty(); + } + void enqueue(GCTask* task) { + guarantee(own_lock(), "don't own the lock"); + unsynchronized_queue()->enqueue(task); + } + void enqueue(GCTaskQueue* list) { + guarantee(own_lock(), "don't own the lock"); + unsynchronized_queue()->enqueue(list); + } + GCTask* dequeue() { + guarantee(own_lock(), "don't own the lock"); + return unsynchronized_queue()->dequeue(); + } + GCTask* dequeue(uint affinity) { + guarantee(own_lock(), "don't own the lock"); + return unsynchronized_queue()->dequeue(affinity); + } + uint length() const { + guarantee(own_lock(), "don't own the lock"); + return unsynchronized_queue()->length(); + } + // For guarantees. + bool own_lock() const { + return lock()->owned_by_self(); + } +protected: + // Constructor. Clients use factory, but there might be subclasses. + SynchronizedGCTaskQueue(GCTaskQueue* queue, Monitor * lock); + // Destructor. Not virtual because no virtuals. + ~SynchronizedGCTaskQueue(); +}; + +// This is an abstract base class for getting notifications +// when a GCTaskManager is done. +class NotifyDoneClosure : public CHeapObj { +public: + // The notification callback method. + virtual void notify(GCTaskManager* manager) = 0; +protected: + // Constructor. + NotifyDoneClosure() { + // Nothing to do. + } + // Virtual destructor because virtual methods. + virtual ~NotifyDoneClosure() { + // Nothing to do. + } +}; + +class GCTaskManager : public CHeapObj { + friend class ParCompactionManager; + friend class PSParallelCompact; + friend class PSScavenge; + friend class PSRefProcTaskExecutor; + friend class RefProcTaskExecutor; +private: + // Instance state. + NotifyDoneClosure* _ndc; // Notify on completion. + const uint _workers; // Number of workers. + Monitor* _monitor; // Notification of changes. + SynchronizedGCTaskQueue* _queue; // Queue of tasks. + GCTaskThread** _thread; // Array of worker threads. + uint _busy_workers; // Number of busy workers. + uint _blocking_worker; // The worker that's blocking. + bool* _resource_flag; // Array of flag per threads. + uint _delivered_tasks; // Count of delivered tasks. + uint _completed_tasks; // Count of completed tasks. + uint _barriers; // Count of barrier tasks. + uint _emptied_queue; // Times we emptied the queue. + NoopGCTask* _noop_task; // The NoopGCTask instance. + uint _noop_tasks; // Count of noop tasks. +public: + // Factory create and destroy methods. + static GCTaskManager* create(uint workers) { + return new GCTaskManager(workers); + } + static GCTaskManager* create(uint workers, NotifyDoneClosure* ndc) { + return new GCTaskManager(workers, ndc); + } + static void destroy(GCTaskManager* that) { + if (that != NULL) { + delete that; + } + } + // Accessors. + uint busy_workers() const { + return _busy_workers; + } + // Pun between Monitor* and Mutex* + Monitor* monitor() const { + return _monitor; + } + Monitor * lock() const { + return _monitor; + } + // Methods. + // Add the argument task to be run. + void add_task(GCTask* task); + // Add a list of tasks. Removes task from the argument list. + void add_list(GCTaskQueue* list); + // Claim a task for argument worker. + GCTask* get_task(uint which); + // Note the completion of a task by the argument worker. + void note_completion(uint which); + // Is the queue blocked from handing out new tasks? + bool is_blocked() const { + return (blocking_worker() != sentinel_worker()); + } + // Request that all workers release their resources. + void release_all_resources(); + // Ask if a particular worker should release its resources. + bool should_release_resources(uint which); // Predicate. + // Note the release of resources by the argument worker. + void note_release(uint which); + // Constants. + // A sentinel worker identifier. + static uint sentinel_worker() { + return (uint) -1; // Why isn't there a max_uint? + } + + // Execute the task queue and wait for the completion. + void execute_and_wait(GCTaskQueue* list); + + void print_task_time_stamps(); + void print_threads_on(outputStream* st); + void threads_do(ThreadClosure* tc); + +protected: + // Constructors. Clients use factory, but there might be subclasses. + // Create a GCTaskManager with the appropriate number of workers. + GCTaskManager(uint workers); + // Create a GCTaskManager that calls back when there's no more work. + GCTaskManager(uint workers, NotifyDoneClosure* ndc); + // Make virtual if necessary. + ~GCTaskManager(); + // Accessors. + uint workers() const { + return _workers; + } + NotifyDoneClosure* notify_done_closure() const { + return _ndc; + } + SynchronizedGCTaskQueue* queue() const { + return _queue; + } + NoopGCTask* noop_task() const { + return _noop_task; + } + // Bounds-checking per-thread data accessors. + GCTaskThread* thread(uint which); + void set_thread(uint which, GCTaskThread* value); + bool resource_flag(uint which); + void set_resource_flag(uint which, bool value); + // Modifier methods with some semantics. + // Is any worker blocking handing out new tasks? + uint blocking_worker() const { + return _blocking_worker; + } + void set_blocking_worker(uint value) { + _blocking_worker = value; + } + void set_unblocked() { + set_blocking_worker(sentinel_worker()); + } + // Count of busy workers. + void reset_busy_workers() { + _busy_workers = 0; + } + uint increment_busy_workers(); + uint decrement_busy_workers(); + // Count of tasks delivered to workers. + uint delivered_tasks() const { + return _delivered_tasks; + } + void increment_delivered_tasks() { + _delivered_tasks += 1; + } + void reset_delivered_tasks() { + _delivered_tasks = 0; + } + // Count of tasks completed by workers. + uint completed_tasks() const { + return _completed_tasks; + } + void increment_completed_tasks() { + _completed_tasks += 1; + } + void reset_completed_tasks() { + _completed_tasks = 0; + } + // Count of barrier tasks completed. + uint barriers() const { + return _barriers; + } + void increment_barriers() { + _barriers += 1; + } + void reset_barriers() { + _barriers = 0; + } + // Count of how many times the queue has emptied. + uint emptied_queue() const { + return _emptied_queue; + } + void increment_emptied_queue() { + _emptied_queue += 1; + } + void reset_emptied_queue() { + _emptied_queue = 0; + } + // Count of the number of noop tasks we've handed out, + // e.g., to handle resource release requests. + uint noop_tasks() const { + return _noop_tasks; + } + void increment_noop_tasks() { + _noop_tasks += 1; + } + void reset_noop_tasks() { + _noop_tasks = 0; + } + // Other methods. + void initialize(); +}; + +// +// Some exemplary GCTasks. +// + +// A noop task that does nothing, +// except take us around the GCTaskThread loop. +class NoopGCTask : public GCTask { +private: + const bool _is_c_heap_obj; // Is this a CHeapObj? +public: + // Factory create and destroy methods. + static NoopGCTask* create(); + static NoopGCTask* create_on_c_heap(); + static void destroy(NoopGCTask* that); + // Methods from GCTask. + void do_it(GCTaskManager* manager, uint which) { + // Nothing to do. + } +protected: + // Constructor. + NoopGCTask(bool on_c_heap) : + GCTask(GCTask::Kind::noop_task), + _is_c_heap_obj(on_c_heap) { + // Nothing to do. + } + // Destructor-like method. + void destruct(); + // Accessors. + bool is_c_heap_obj() const { + return _is_c_heap_obj; + } +}; + +// A BarrierGCTask blocks other tasks from starting, +// and waits until it is the only task running. +class BarrierGCTask : public GCTask { +public: + // Factory create and destroy methods. + static BarrierGCTask* create() { + return new BarrierGCTask(); + } + static void destroy(BarrierGCTask* that) { + if (that != NULL) { + that->destruct(); + delete that; + } + } + // Methods from GCTask. + void do_it(GCTaskManager* manager, uint which); +protected: + // Constructor. Clients use factory, but there might be subclasses. + BarrierGCTask() : + GCTask(GCTask::Kind::barrier_task) { + // Nothing to do. + } + // Destructor-like method. + void destruct(); + // Methods. + // Wait for this to be the only task running. + void do_it_internal(GCTaskManager* manager, uint which); +}; + +// A ReleasingBarrierGCTask is a BarrierGCTask +// that tells all the tasks to release their resource areas. +class ReleasingBarrierGCTask : public BarrierGCTask { +public: + // Factory create and destroy methods. + static ReleasingBarrierGCTask* create() { + return new ReleasingBarrierGCTask(); + } + static void destroy(ReleasingBarrierGCTask* that) { + if (that != NULL) { + that->destruct(); + delete that; + } + } + // Methods from GCTask. + void do_it(GCTaskManager* manager, uint which); +protected: + // Constructor. Clients use factory, but there might be subclasses. + ReleasingBarrierGCTask() : + BarrierGCTask() { + // Nothing to do. + } + // Destructor-like method. + void destruct(); +}; + +// A NotifyingBarrierGCTask is a BarrierGCTask +// that calls a notification method when it is the only task running. +class NotifyingBarrierGCTask : public BarrierGCTask { +private: + // Instance state. + NotifyDoneClosure* _ndc; // The callback object. +public: + // Factory create and destroy methods. + static NotifyingBarrierGCTask* create(NotifyDoneClosure* ndc) { + return new NotifyingBarrierGCTask(ndc); + } + static void destroy(NotifyingBarrierGCTask* that) { + if (that != NULL) { + that->destruct(); + delete that; + } + } + // Methods from GCTask. + void do_it(GCTaskManager* manager, uint which); +protected: + // Constructor. Clients use factory, but there might be subclasses. + NotifyingBarrierGCTask(NotifyDoneClosure* ndc) : + BarrierGCTask(), + _ndc(ndc) { + assert(notify_done_closure() != NULL, "can't notify on NULL"); + } + // Destructor-like method. + void destruct(); + // Accessor. + NotifyDoneClosure* notify_done_closure() const { return _ndc; } +}; + +// A WaitForBarrierGCTask is a BarrierGCTask +// with a method you can call to wait until +// the BarrierGCTask is done. +// This may cover many of the uses of NotifyingBarrierGCTasks. +class WaitForBarrierGCTask : public BarrierGCTask { +private: + // Instance state. + Monitor* _monitor; // Guard and notify changes. + bool _should_wait; // true=>wait, false=>proceed. + const bool _is_c_heap_obj; // Was allocated on the heap. +public: + virtual char* name() { return (char *) "waitfor-barrier-task"; } + + // Factory create and destroy methods. + static WaitForBarrierGCTask* create(); + static WaitForBarrierGCTask* create_on_c_heap(); + static void destroy(WaitForBarrierGCTask* that); + // Methods. + void do_it(GCTaskManager* manager, uint which); + void wait_for(); +protected: + // Constructor. Clients use factory, but there might be subclasses. + WaitForBarrierGCTask(bool on_c_heap); + // Destructor-like method. + void destruct(); + // Accessors. + Monitor* monitor() const { + return _monitor; + } + bool should_wait() const { + return _should_wait; + } + void set_should_wait(bool value) { + _should_wait = value; + } + bool is_c_heap_obj() { + return _is_c_heap_obj; + } +}; + +class MonitorSupply : public AllStatic { +private: + // State. + // Control multi-threaded access. + static Mutex* _lock; + // The list of available Monitor*'s. + static GrowableArray* _freelist; +public: + // Reserve a Monitor*. + static Monitor* reserve(); + // Release a Monitor*. + static void release(Monitor* instance); +private: + // Accessors. + static Mutex* lock() { + return _lock; + } + static GrowableArray* freelist() { + return _freelist; + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp new file mode 100644 index 00000000000..c49242ef1d6 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp @@ -0,0 +1,150 @@ + +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_gcTaskThread.cpp.incl" + +GCTaskThread::GCTaskThread(GCTaskManager* manager, + uint which, + uint processor_id) : + _manager(manager), + _processor_id(processor_id), + _time_stamps(NULL), + _time_stamp_index(0) +{ + if (!os::create_thread(this, os::pgc_thread)) + vm_exit_out_of_memory(0, "Cannot create GC thread. Out of system resources."); + + if (PrintGCTaskTimeStamps) { + _time_stamps = NEW_C_HEAP_ARRAY(GCTaskTimeStamp, GCTaskTimeStampEntries ); + + guarantee(_time_stamps != NULL, "Sanity"); + } + set_id(which); + set_name("GC task thread#%d (ParallelGC)", which); +} + +GCTaskThread::~GCTaskThread() { + if (_time_stamps != NULL) { + FREE_C_HEAP_ARRAY(GCTaskTimeStamp, _time_stamps); + } +} + +void GCTaskThread::start() { + os::start_thread(this); +} + +GCTaskTimeStamp* GCTaskThread::time_stamp_at(uint index) { + guarantee(index < GCTaskTimeStampEntries, "increase GCTaskTimeStampEntries"); + + return &(_time_stamps[index]); +} + +void GCTaskThread::print_task_time_stamps() { + assert(PrintGCTaskTimeStamps, "Sanity"); + assert(_time_stamps != NULL, "Sanity (Probably set PrintGCTaskTimeStamps late)"); + + tty->print_cr("GC-Thread %u entries: %d", id(), _time_stamp_index); + for(uint i=0; i<_time_stamp_index; i++) { + GCTaskTimeStamp* time_stamp = time_stamp_at(i); + tty->print_cr("\t[ %s " INT64_FORMAT " " INT64_FORMAT " ]", + time_stamp->name(), + time_stamp->entry_time(), + time_stamp->exit_time()); + } + + // Reset after dumping the data + _time_stamp_index = 0; +} + +void GCTaskThread::print_on(outputStream* st) const { + st->print("\"%s\" ", name()); + Thread::print_on(st); + st->cr(); +} + +void GCTaskThread::run() { + // Set up the thread for stack overflow support + this->record_stack_base_and_size(); + this->initialize_thread_local_storage(); + // Bind yourself to your processor. + if (processor_id() != GCTaskManager::sentinel_worker()) { + if (TraceGCTaskThread) { + tty->print_cr("GCTaskThread::run: " + " binding to processor %u", processor_id()); + } + if (!os::bind_to_processor(processor_id())) { + DEBUG_ONLY( + warning("Couldn't bind GCTaskThread %u to processor %u", + which(), processor_id()); + ) + } + } + // Part of thread setup. + // ??? Are these set up once here to make subsequent ones fast? + HandleMark hm_outer; + ResourceMark rm_outer; + + TimeStamp timer; + + for (;/* ever */;) { + // These are so we can flush the resources allocated in the inner loop. + HandleMark hm_inner; + ResourceMark rm_inner; + for (; /* break */; ) { + // This will block until there is a task to be gotten. + GCTask* task = manager()->get_task(which()); + + // In case the update is costly + if (PrintGCTaskTimeStamps) { + timer.update(); + } + + jlong entry_time = timer.ticks(); + char* name = task->name(); + + task->do_it(manager(), which()); + manager()->note_completion(which()); + + if (PrintGCTaskTimeStamps) { + assert(_time_stamps != NULL, "Sanity (PrintGCTaskTimeStamps set late?)"); + + timer.update(); + + GCTaskTimeStamp* time_stamp = time_stamp_at(_time_stamp_index++); + + time_stamp->set_name(name); + time_stamp->set_entry_time(entry_time); + time_stamp->set_exit_time(timer.ticks()); + } + + // Check if we should release our inner resources. + if (manager()->should_release_resources(which())) { + manager()->note_release(which()); + break; + } + } + } +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.hpp new file mode 100644 index 00000000000..ac1530953c5 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.hpp @@ -0,0 +1,99 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Forward declarations of classes defined here. +class GCTaskThread; +class GCTaskTimeStamp; + +// Declarations of classes referenced in this file via pointer. +class GCTaskManager; + +class GCTaskThread : public WorkerThread { +private: + // Instance state. + GCTaskManager* _manager; // Manager for worker. + const uint _processor_id; // Which processor the worker is on. + + GCTaskTimeStamp* _time_stamps; + uint _time_stamp_index; + + GCTaskTimeStamp* time_stamp_at(uint index); + + public: + // Factory create and destroy methods. + static GCTaskThread* create(GCTaskManager* manager, + uint which, + uint processor_id) { + return new GCTaskThread(manager, which, processor_id); + } + static void destroy(GCTaskThread* manager) { + if (manager != NULL) { + delete manager; + } + } + // Methods from Thread. + bool is_GC_task_thread() const { + return true; + } + virtual void run(); + // Methods. + void start(); + + void print_task_time_stamps(); + void print_on(outputStream* st) const; + void print() const { print_on(tty); } + +protected: + // Constructor. Clients use factory, but there could be subclasses. + GCTaskThread(GCTaskManager* manager, uint which, uint processor_id); + // Destructor: virtual destructor because of virtual methods. + virtual ~GCTaskThread(); + // Accessors. + GCTaskManager* manager() const { + return _manager; + } + uint which() const { + return id(); + } + uint processor_id() const { + return _processor_id; + } +}; + +class GCTaskTimeStamp : public CHeapObj +{ + private: + jlong _entry_time; + jlong _exit_time; + char* _name; + + public: + jlong entry_time() { return _entry_time; } + jlong exit_time() { return _exit_time; } + const char* name() const { return (const char*)_name; } + + void set_entry_time(jlong time) { _entry_time = time; } + void set_exit_time(jlong time) { _exit_time = time; } + void set_name(char* name) { _name = name; } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/generationSizer.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/generationSizer.hpp new file mode 100644 index 00000000000..9ca614c4823 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/generationSizer.hpp @@ -0,0 +1,69 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// There is a nice batch of tested generation sizing code in +// TwoGenerationCollectorPolicy. Lets reuse it! + +class GenerationSizer : public TwoGenerationCollectorPolicy { + public: + GenerationSizer() { + // Partial init only! + initialize_flags(); + initialize_size_info(); + } + + void initialize_flags() { + // Do basic sizing work + this->TwoGenerationCollectorPolicy::initialize_flags(); + + // If the user hasn't explicitly set the number of worker + // threads, set the count. + if (ParallelGCThreads == 0) { + assert(UseParallelGC, "Setting ParallelGCThreads without UseParallelGC"); + ParallelGCThreads = os::active_processor_count(); + } + + // The survivor ratio's are calculated "raw", unlike the + // default gc, which adds 2 to the ratio value. We need to + // make sure the values are valid before using them. + if (MinSurvivorRatio < 3) { + MinSurvivorRatio = 3; + } + + if (InitialSurvivorRatio < 3) { + InitialSurvivorRatio = 3; + } + } + + size_t min_young_gen_size() { return _min_gen0_size; } + size_t young_gen_size() { return _initial_gen0_size; } + size_t max_young_gen_size() { return _max_gen0_size; } + + size_t min_old_gen_size() { return _min_gen1_size; } + size_t old_gen_size() { return _initial_gen1_size; } + size_t max_old_gen_size() { return _max_gen1_size; } + + size_t perm_gen_size() { return PermSize; } + size_t max_perm_gen_size() { return MaxPermSize; } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp new file mode 100644 index 00000000000..d05a4b39a3e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp @@ -0,0 +1,137 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_objectStartArray.cpp.incl" + +void ObjectStartArray::initialize(MemRegion reserved_region) { + // We're based on the assumption that we use the same + // size blocks as the card table. + assert((int)block_size == (int)CardTableModRefBS::card_size, "Sanity"); + assert((int)block_size <= 512, "block_size must be less than or equal to 512"); + + // Calculate how much space must be reserved + _reserved_region = reserved_region; + + size_t bytes_to_reserve = reserved_region.word_size() / block_size_in_words; + assert(bytes_to_reserve > 0, "Sanity"); + + bytes_to_reserve = + align_size_up(bytes_to_reserve, os::vm_allocation_granularity()); + + // Do not use large-pages for the backing store. The one large page region + // will be used for the heap proper. + ReservedSpace backing_store(bytes_to_reserve); + if (!backing_store.is_reserved()) { + vm_exit_during_initialization("Could not reserve space for ObjectStartArray"); + } + + // We do not commit any memory initially + if (!_virtual_space.initialize(backing_store, 0)) { + vm_exit_during_initialization("Could not commit space for ObjectStartArray"); + } + + _raw_base = (jbyte*)_virtual_space.low_boundary(); + if (_raw_base == NULL) { + vm_exit_during_initialization("Could not get raw_base address"); + } + + _offset_base = _raw_base - (size_t(reserved_region.start()) >> block_shift); + + _covered_region.set_start(reserved_region.start()); + _covered_region.set_word_size(0); + + _blocks_region.set_start((HeapWord*)_raw_base); + _blocks_region.set_word_size(0); +} + +void ObjectStartArray::set_covered_region(MemRegion mr) { + assert(_reserved_region.contains(mr), "MemRegion outside of reserved space"); + assert(_reserved_region.start() == mr.start(), "Attempt to move covered region"); + + HeapWord* low_bound = mr.start(); + HeapWord* high_bound = mr.end(); + assert((uintptr_t(low_bound) & (block_size - 1)) == 0, "heap must start at block boundary"); + assert((uintptr_t(high_bound) & (block_size - 1)) == 0, "heap must end at block boundary"); + + size_t requested_blocks_size_in_bytes = mr.word_size() / block_size_in_words; + + // Only commit memory in page sized chunks + requested_blocks_size_in_bytes = + align_size_up(requested_blocks_size_in_bytes, os::vm_page_size()); + + _covered_region = mr; + + size_t current_blocks_size_in_bytes = _blocks_region.byte_size(); + + if (requested_blocks_size_in_bytes > current_blocks_size_in_bytes) { + // Expand + size_t expand_by = requested_blocks_size_in_bytes - current_blocks_size_in_bytes; + if (!_virtual_space.expand_by(expand_by)) { + vm_exit_out_of_memory(expand_by, "object start array expansion"); + } + // Clear *only* the newly allocated region + memset(_blocks_region.end(), clean_block, expand_by); + } + + if (requested_blocks_size_in_bytes < current_blocks_size_in_bytes) { + // Shrink + size_t shrink_by = current_blocks_size_in_bytes - requested_blocks_size_in_bytes; + _virtual_space.shrink_by(shrink_by); + } + + _blocks_region.set_word_size(requested_blocks_size_in_bytes / sizeof(HeapWord)); + + assert(requested_blocks_size_in_bytes % sizeof(HeapWord) == 0, "Block table not expanded in word sized increment"); + assert(requested_blocks_size_in_bytes == _blocks_region.byte_size(), "Sanity"); + assert(block_for_addr(low_bound) == &_raw_base[0], "Checking start of map"); + assert(block_for_addr(high_bound-1) <= &_raw_base[_blocks_region.byte_size()-1], "Checking end of map"); +} + +void ObjectStartArray::reset() { + memset(_blocks_region.start(), clean_block, _blocks_region.byte_size()); +} + + +bool ObjectStartArray::object_starts_in_range(HeapWord* start_addr, + HeapWord* end_addr) const { + assert(start_addr <= end_addr, "range is wrong"); + if (start_addr > end_addr) { + return false; + } + + jbyte* start_block = block_for_addr(start_addr); + jbyte* end_block = block_for_addr(end_addr); + + for (jbyte* block = start_block; block <= end_block; block++) { + if (*block != clean_block) { + return true; + } + } + // No object starts in this slice; verify this using + // more traditional methods: + assert(object_start(end_addr - 1) <= start_addr, + "Oops an object does start in this slice?"); + return false; +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.hpp new file mode 100644 index 00000000000..d587d0ae291 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.hpp @@ -0,0 +1,162 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// This class can be used to locate the beginning of an object in the +// covered region. +// + +class ObjectStartArray : public CHeapObj { + friend class VerifyObjectStartArrayClosure; + + private: + PSVirtualSpace _virtual_space; + MemRegion _reserved_region; + MemRegion _covered_region; + MemRegion _blocks_region; + jbyte* _raw_base; + jbyte* _offset_base; + + public: + + enum BlockValueConstants { + clean_block = -1 + }; + + enum BlockSizeConstants { + block_shift = 9, + block_size = 1 << block_shift, + block_size_in_words = block_size / sizeof(HeapWord) + }; + + protected: + + // Mapping from address to object start array entry + jbyte* block_for_addr(void* p) const { + assert(_covered_region.contains(p), + "out of bounds access to object start array"); + jbyte* result = &_offset_base[uintptr_t(p) >> block_shift]; + assert(_blocks_region.contains(result), + "out of bounds result in byte_for"); + return result; + } + + // Mapping from object start array entry to address of first word + HeapWord* addr_for_block(jbyte* p) { + assert(_blocks_region.contains(p), + "out of bounds access to object start array"); + size_t delta = pointer_delta(p, _offset_base, sizeof(jbyte)); + HeapWord* result = (HeapWord*) (delta << block_shift); + assert(_covered_region.contains(result), + "out of bounds accessor from card marking array"); + return result; + } + + // Mapping that includes the derived offset. + // If the block is clean, returns the last address in the covered region. + // If the block is < index 0, returns the start of the covered region. + HeapWord* offset_addr_for_block (jbyte* p) const { + // We have to do this before the assert + if (p < _raw_base) { + return _covered_region.start(); + } + + assert(_blocks_region.contains(p), + "out of bounds access to object start array"); + + if (*p == clean_block) { + return _covered_region.end(); + } + + size_t delta = pointer_delta(p, _offset_base, sizeof(jbyte)); + HeapWord* result = (HeapWord*) (delta << block_shift); + result += *p; + + assert(_covered_region.contains(result), + "out of bounds accessor from card marking array"); + + return result; + } + + public: + + // This method is in lieu of a constructor, so that this class can be + // embedded inline in other classes. + void initialize(MemRegion reserved_region); + + void set_covered_region(MemRegion mr); + + void reset(); + + MemRegion covered_region() { return _covered_region; } + + void allocate_block(HeapWord* p) { + assert(_covered_region.contains(p), "Must be in covered region"); + jbyte* block = block_for_addr(p); + HeapWord* block_base = addr_for_block(block); + size_t offset = pointer_delta(p, block_base, sizeof(HeapWord*)); + assert(offset < 128, "Sanity"); + // When doing MT offsets, we can't assert this. + //assert(offset > *block, "Found backwards allocation"); + *block = (jbyte)offset; + + // tty->print_cr("[%p]", p); + } + + // Optimized for finding the first object that crosses into + // a given block. The blocks contain the offset of the last + // object in that block. Scroll backwards by one, and the first + // object hit should be at the begining of the block + HeapWord* object_start(HeapWord* addr) const { + assert(_covered_region.contains(addr), "Must be in covered region"); + jbyte* block = block_for_addr(addr); + HeapWord* scroll_forward = offset_addr_for_block(block--); + while (scroll_forward > addr) { + scroll_forward = offset_addr_for_block(block--); + } + + HeapWord* next = scroll_forward; + while (next <= addr) { + scroll_forward = next; + next += oop(next)->size(); + } + assert(scroll_forward <= addr, "wrong order for current and arg"); + assert(addr <= next, "wrong order for arg and next"); + return scroll_forward; + } + + bool is_block_allocated(HeapWord* addr) { + assert(_covered_region.contains(addr), "Must be in covered region"); + jbyte* block = block_for_addr(addr); + if (*block == clean_block) + return false; + + return true; + } + + // Return true if an object starts in the range of heap addresses. + // If an object starts at an address corresponding to + // "start", the method will return true. + bool object_starts_in_range(HeapWord* start_addr, HeapWord* end_addr) const; +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp new file mode 100644 index 00000000000..614a16c4aed --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp @@ -0,0 +1,240 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_parMarkBitMap.cpp.incl" + +bool +ParMarkBitMap::initialize(MemRegion covered_region) +{ + const idx_t bits = bits_required(covered_region); + // The bits will be divided evenly between two bitmaps; each of them should be + // an integral number of words. + assert(bits % (BitsPerWord * 2) == 0, "region size unaligned"); + + const size_t words = bits / BitsPerWord; + const size_t raw_bytes = words * sizeof(idx_t); + const size_t page_sz = os::page_size_for_region(raw_bytes, raw_bytes, 10); + const size_t granularity = os::vm_allocation_granularity(); + const size_t bytes = align_size_up(raw_bytes, MAX2(page_sz, granularity)); + + const size_t rs_align = page_sz == (size_t) os::vm_page_size() ? 0 : + MAX2(page_sz, granularity); + ReservedSpace rs(bytes, rs_align, false); + os::trace_page_sizes("par bitmap", raw_bytes, raw_bytes, page_sz, + rs.base(), rs.size()); + _virtual_space = new PSVirtualSpace(rs, page_sz); + if (_virtual_space != NULL && _virtual_space->expand_by(bytes)) { + _region_start = covered_region.start(); + _region_size = covered_region.word_size(); + idx_t* map = (idx_t*)_virtual_space->reserved_low_addr(); + _beg_bits.set_map(map); + _beg_bits.set_size(bits / 2); + _end_bits.set_map(map + words / 2); + _end_bits.set_size(bits / 2); + return true; + } + + _region_start = 0; + _region_size = 0; + if (_virtual_space != NULL) { + delete _virtual_space; + _virtual_space = NULL; + } + return false; +} + +#ifdef ASSERT +extern size_t mark_bitmap_count; +extern size_t mark_bitmap_size; +#endif // #ifdef ASSERT + +bool +ParMarkBitMap::mark_obj(HeapWord* addr, size_t size) +{ + const idx_t beg_bit = addr_to_bit(addr); + if (_beg_bits.par_set_bit(beg_bit)) { + const idx_t end_bit = addr_to_bit(addr + size - 1); + bool end_bit_ok = _end_bits.par_set_bit(end_bit); + assert(end_bit_ok, "concurrency problem"); + DEBUG_ONLY(Atomic::inc_ptr(&mark_bitmap_count)); + DEBUG_ONLY(Atomic::add_ptr(size, &mark_bitmap_size)); + return true; + } + return false; +} + +size_t +ParMarkBitMap::live_words_in_range(HeapWord* beg_addr, HeapWord* end_addr) const +{ + assert(beg_addr <= end_addr, "bad range"); + + idx_t live_bits = 0; + + // The bitmap routines require the right boundary to be word-aligned. + const idx_t end_bit = addr_to_bit(end_addr); + const idx_t range_end = BitMap::word_align_up(end_bit); + + idx_t beg_bit = find_obj_beg(addr_to_bit(beg_addr), range_end); + while (beg_bit < end_bit) { + idx_t tmp_end = find_obj_end(beg_bit, range_end); + if (tmp_end < end_bit) { + live_bits += tmp_end - beg_bit + 1; + beg_bit = find_obj_beg(tmp_end + 1, range_end); + } else { + live_bits += end_bit - beg_bit; // No + 1 here; end_bit is not counted. + return bits_to_words(live_bits); + } + } + return bits_to_words(live_bits); +} + +size_t ParMarkBitMap::live_words_in_range(HeapWord* beg_addr, oop end_obj) const +{ + assert(beg_addr <= (HeapWord*)end_obj, "bad range"); + assert(is_marked(end_obj), "end_obj must be live"); + + idx_t live_bits = 0; + + // The bitmap routines require the right boundary to be word-aligned. + const idx_t end_bit = addr_to_bit((HeapWord*)end_obj); + const idx_t range_end = BitMap::word_align_up(end_bit); + + idx_t beg_bit = find_obj_beg(addr_to_bit(beg_addr), range_end); + while (beg_bit < end_bit) { + idx_t tmp_end = find_obj_end(beg_bit, range_end); + assert(tmp_end < end_bit, "missing end bit"); + live_bits += tmp_end - beg_bit + 1; + beg_bit = find_obj_beg(tmp_end + 1, range_end); + } + return bits_to_words(live_bits); +} + +ParMarkBitMap::IterationStatus +ParMarkBitMap::iterate(ParMarkBitMapClosure* live_closure, + idx_t range_beg, idx_t range_end) const +{ + DEBUG_ONLY(verify_bit(range_beg);) + DEBUG_ONLY(verify_bit(range_end);) + assert(range_beg <= range_end, "live range invalid"); + + // The bitmap routines require the right boundary to be word-aligned. + const idx_t search_end = BitMap::word_align_up(range_end); + + idx_t cur_beg = find_obj_beg(range_beg, search_end); + while (cur_beg < range_end) { + const idx_t cur_end = find_obj_end(cur_beg, search_end); + if (cur_end >= range_end) { + // The obj ends outside the range. + live_closure->set_source(bit_to_addr(cur_beg)); + return incomplete; + } + + const size_t size = obj_size(cur_beg, cur_end); + IterationStatus status = live_closure->do_addr(bit_to_addr(cur_beg), size); + if (status != incomplete) { + assert(status == would_overflow || status == full, "sanity"); + return status; + } + + // Successfully processed the object; look for the next object. + cur_beg = find_obj_beg(cur_end + 1, search_end); + } + + live_closure->set_source(bit_to_addr(range_end)); + return complete; +} + +ParMarkBitMap::IterationStatus +ParMarkBitMap::iterate(ParMarkBitMapClosure* live_closure, + ParMarkBitMapClosure* dead_closure, + idx_t range_beg, idx_t range_end, + idx_t dead_range_end) const +{ + DEBUG_ONLY(verify_bit(range_beg);) + DEBUG_ONLY(verify_bit(range_end);) + DEBUG_ONLY(verify_bit(dead_range_end);) + assert(range_beg <= range_end, "live range invalid"); + assert(range_end <= dead_range_end, "dead range invalid"); + + // The bitmap routines require the right boundary to be word-aligned. + const idx_t live_search_end = BitMap::word_align_up(range_end); + const idx_t dead_search_end = BitMap::word_align_up(dead_range_end); + + idx_t cur_beg = range_beg; + if (range_beg < range_end && is_unmarked(range_beg)) { + // The range starts with dead space. Look for the next object, then fill. + cur_beg = find_obj_beg(range_beg + 1, dead_search_end); + const idx_t dead_space_end = MIN2(cur_beg - 1, dead_range_end - 1); + const size_t size = obj_size(range_beg, dead_space_end); + dead_closure->do_addr(bit_to_addr(range_beg), size); + } + + while (cur_beg < range_end) { + const idx_t cur_end = find_obj_end(cur_beg, live_search_end); + if (cur_end >= range_end) { + // The obj ends outside the range. + live_closure->set_source(bit_to_addr(cur_beg)); + return incomplete; + } + + const size_t size = obj_size(cur_beg, cur_end); + IterationStatus status = live_closure->do_addr(bit_to_addr(cur_beg), size); + if (status != incomplete) { + assert(status == would_overflow || status == full, "sanity"); + return status; + } + + // Look for the start of the next object. + const idx_t dead_space_beg = cur_end + 1; + cur_beg = find_obj_beg(dead_space_beg, dead_search_end); + if (cur_beg > dead_space_beg) { + // Found dead space; compute the size and invoke the dead closure. + const idx_t dead_space_end = MIN2(cur_beg - 1, dead_range_end - 1); + const size_t size = obj_size(dead_space_beg, dead_space_end); + dead_closure->do_addr(bit_to_addr(dead_space_beg), size); + } + } + + live_closure->set_source(bit_to_addr(range_end)); + return complete; +} + +#ifndef PRODUCT +void ParMarkBitMap::reset_counters() +{ + _cas_tries = _cas_retries = _cas_by_another = 0; +} +#endif // #ifndef PRODUCT + +#ifdef ASSERT +void ParMarkBitMap::verify_clear() const +{ + const idx_t* const beg = (const idx_t*)_virtual_space->committed_low_addr(); + const idx_t* const end = (const idx_t*)_virtual_space->committed_high_addr(); + for (const idx_t* p = beg; p < end; ++p) { + assert(*p == 0, "bitmap not clear"); + } +} +#endif // #ifdef ASSERT diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.hpp new file mode 100644 index 00000000000..3363a01155d --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.hpp @@ -0,0 +1,427 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class oopDesc; +class ParMarkBitMapClosure; + +class ParMarkBitMap: public CHeapObj +{ +public: + typedef BitMap::idx_t idx_t; + + // Values returned by the iterate() methods. + enum IterationStatus { incomplete, complete, full, would_overflow }; + + inline ParMarkBitMap(); + inline ParMarkBitMap(MemRegion covered_region); + bool initialize(MemRegion covered_region); + + // Atomically mark an object as live. + bool mark_obj(HeapWord* addr, size_t size); + inline bool mark_obj(oop obj, int size); + inline bool mark_obj(oop obj); + + // Return whether the specified begin or end bit is set. + inline bool is_obj_beg(idx_t bit) const; + inline bool is_obj_end(idx_t bit) const; + + // Traditional interface for testing whether an object is marked or not (these + // test only the begin bits). + inline bool is_marked(idx_t bit) const; + inline bool is_marked(HeapWord* addr) const; + inline bool is_marked(oop obj) const; + + inline bool is_unmarked(idx_t bit) const; + inline bool is_unmarked(HeapWord* addr) const; + inline bool is_unmarked(oop obj) const; + + // Convert sizes from bits to HeapWords and back. An object that is n bits + // long will be bits_to_words(n) words long. An object that is m words long + // will take up words_to_bits(m) bits in the bitmap. + inline static size_t bits_to_words(idx_t bits); + inline static idx_t words_to_bits(size_t words); + + // Return the size in words of an object given a begin bit and an end bit, or + // the equivalent beg_addr and end_addr. + inline size_t obj_size(idx_t beg_bit, idx_t end_bit) const; + inline size_t obj_size(HeapWord* beg_addr, HeapWord* end_addr) const; + + // Return the size in words of the object (a search is done for the end bit). + inline size_t obj_size(idx_t beg_bit) const; + inline size_t obj_size(HeapWord* addr) const; + inline size_t obj_size(oop obj) const; + + // Synonyms for the above. + size_t obj_size_in_words(oop obj) const { return obj_size((HeapWord*)obj); } + size_t obj_size_in_words(HeapWord* addr) const { return obj_size(addr); } + + // Apply live_closure to each live object that lies completely within the + // range [live_range_beg, live_range_end). This is used to iterate over the + // compacted region of the heap. Return values: + // + // incomplete The iteration is not complete. The last object that + // begins in the range does not end in the range; + // closure->source() is set to the start of that object. + // + // complete The iteration is complete. All objects in the range + // were processed and the closure is not full; + // closure->source() is set one past the end of the range. + // + // full The closure is full; closure->source() is set to one + // past the end of the last object processed. + // + // would_overflow The next object in the range would overflow the closure; + // closure->source() is set to the start of that object. + IterationStatus iterate(ParMarkBitMapClosure* live_closure, + idx_t range_beg, idx_t range_end) const; + inline IterationStatus iterate(ParMarkBitMapClosure* live_closure, + HeapWord* range_beg, + HeapWord* range_end) const; + + // Apply live closure as above and additionally apply dead_closure to all dead + // space in the range [range_beg, dead_range_end). Note that dead_range_end + // must be >= range_end. This is used to iterate over the dense prefix. + // + // This method assumes that if the first bit in the range (range_beg) is not + // marked, then dead space begins at that point and the dead_closure is + // applied. Thus callers must ensure that range_beg is not in the middle of a + // live object. + IterationStatus iterate(ParMarkBitMapClosure* live_closure, + ParMarkBitMapClosure* dead_closure, + idx_t range_beg, idx_t range_end, + idx_t dead_range_end) const; + inline IterationStatus iterate(ParMarkBitMapClosure* live_closure, + ParMarkBitMapClosure* dead_closure, + HeapWord* range_beg, + HeapWord* range_end, + HeapWord* dead_range_end) const; + + // Return the number of live words in the range [beg_addr, end_addr) due to + // objects that start in the range. If a live object extends onto the range, + // the caller must detect and account for any live words due to that object. + // If a live object extends beyond the end of the range, only the words within + // the range are included in the result. + size_t live_words_in_range(HeapWord* beg_addr, HeapWord* end_addr) const; + + // Same as the above, except the end of the range must be a live object, which + // is the case when updating pointers. This allows a branch to be removed + // from inside the loop. + size_t live_words_in_range(HeapWord* beg_addr, oop end_obj) const; + + inline HeapWord* region_start() const; + inline HeapWord* region_end() const; + inline size_t region_size() const; + inline size_t size() const; + + // Convert a heap address to/from a bit index. + inline idx_t addr_to_bit(HeapWord* addr) const; + inline HeapWord* bit_to_addr(idx_t bit) const; + + // Return the bit index of the first marked object that begins (or ends, + // respectively) in the range [beg, end). If no object is found, return end. + inline idx_t find_obj_beg(idx_t beg, idx_t end) const; + inline idx_t find_obj_end(idx_t beg, idx_t end) const; + + inline HeapWord* find_obj_beg(HeapWord* beg, HeapWord* end) const; + inline HeapWord* find_obj_end(HeapWord* beg, HeapWord* end) const; + + // Clear a range of bits or the entire bitmap (both begin and end bits are + // cleared). + inline void clear_range(idx_t beg, idx_t end); + inline void clear() { clear_range(0, size()); } + + // Return the number of bits required to represent the specified number of + // HeapWords, or the specified region. + static inline idx_t bits_required(size_t words); + static inline idx_t bits_required(MemRegion covered_region); + static inline idx_t words_required(MemRegion covered_region); + +#ifndef PRODUCT + // CAS statistics. + size_t cas_tries() { return _cas_tries; } + size_t cas_retries() { return _cas_retries; } + size_t cas_by_another() { return _cas_by_another; } + + void reset_counters(); +#endif // #ifndef PRODUCT + +#ifdef ASSERT + void verify_clear() const; + inline void verify_bit(idx_t bit) const; + inline void verify_addr(HeapWord* addr) const; +#endif // #ifdef ASSERT + +private: + // Each bit in the bitmap represents one unit of 'object granularity.' Objects + // are double-word aligned in 32-bit VMs, but not in 64-bit VMs, so the 32-bit + // granularity is 2, 64-bit is 1. + static inline size_t obj_granularity() { return size_t(MinObjAlignment); } + + HeapWord* _region_start; + size_t _region_size; + BitMap _beg_bits; + BitMap _end_bits; + PSVirtualSpace* _virtual_space; + +#ifndef PRODUCT + size_t _cas_tries; + size_t _cas_retries; + size_t _cas_by_another; +#endif // #ifndef PRODUCT +}; + +inline ParMarkBitMap::ParMarkBitMap(): + _beg_bits(NULL, 0), + _end_bits(NULL, 0) +{ + _region_start = 0; + _virtual_space = 0; +} + +inline ParMarkBitMap::ParMarkBitMap(MemRegion covered_region): + _beg_bits(NULL, 0), + _end_bits(NULL, 0) +{ + initialize(covered_region); +} + +inline void ParMarkBitMap::clear_range(idx_t beg, idx_t end) +{ + _beg_bits.clear_range(beg, end); + _end_bits.clear_range(beg, end); +} + +inline ParMarkBitMap::idx_t +ParMarkBitMap::bits_required(size_t words) +{ + // Need two bits (one begin bit, one end bit) for each unit of 'object + // granularity' in the heap. + return words_to_bits(words * 2); +} + +inline ParMarkBitMap::idx_t +ParMarkBitMap::bits_required(MemRegion covered_region) +{ + return bits_required(covered_region.word_size()); +} + +inline ParMarkBitMap::idx_t +ParMarkBitMap::words_required(MemRegion covered_region) +{ + return bits_required(covered_region) / BitsPerWord; +} + +inline HeapWord* +ParMarkBitMap::region_start() const +{ + return _region_start; +} + +inline HeapWord* +ParMarkBitMap::region_end() const +{ + return region_start() + region_size(); +} + +inline size_t +ParMarkBitMap::region_size() const +{ + return _region_size; +} + +inline size_t +ParMarkBitMap::size() const +{ + return _beg_bits.size(); +} + +inline bool ParMarkBitMap::is_obj_beg(idx_t bit) const +{ + return _beg_bits.at(bit); +} + +inline bool ParMarkBitMap::is_obj_end(idx_t bit) const +{ + return _end_bits.at(bit); +} + +inline bool ParMarkBitMap::is_marked(idx_t bit) const +{ + return is_obj_beg(bit); +} + +inline bool ParMarkBitMap::is_marked(HeapWord* addr) const +{ + return is_marked(addr_to_bit(addr)); +} + +inline bool ParMarkBitMap::is_marked(oop obj) const +{ + return is_marked((HeapWord*)obj); +} + +inline bool ParMarkBitMap::is_unmarked(idx_t bit) const +{ + return !is_marked(bit); +} + +inline bool ParMarkBitMap::is_unmarked(HeapWord* addr) const +{ + return !is_marked(addr); +} + +inline bool ParMarkBitMap::is_unmarked(oop obj) const +{ + return !is_marked(obj); +} + +inline size_t +ParMarkBitMap::bits_to_words(idx_t bits) +{ + return bits * obj_granularity(); +} + +inline ParMarkBitMap::idx_t +ParMarkBitMap::words_to_bits(size_t words) +{ + return words / obj_granularity(); +} + +inline size_t ParMarkBitMap::obj_size(idx_t beg_bit, idx_t end_bit) const +{ + DEBUG_ONLY(verify_bit(beg_bit);) + DEBUG_ONLY(verify_bit(end_bit);) + return bits_to_words(end_bit - beg_bit + 1); +} + +inline size_t +ParMarkBitMap::obj_size(HeapWord* beg_addr, HeapWord* end_addr) const +{ + DEBUG_ONLY(verify_addr(beg_addr);) + DEBUG_ONLY(verify_addr(end_addr);) + return pointer_delta(end_addr, beg_addr) + obj_granularity(); +} + +inline size_t ParMarkBitMap::obj_size(idx_t beg_bit) const +{ + const idx_t end_bit = _end_bits.find_next_one_bit(beg_bit, size()); + assert(is_marked(beg_bit), "obj not marked"); + assert(end_bit < size(), "end bit missing"); + return obj_size(beg_bit, end_bit); +} + +inline size_t ParMarkBitMap::obj_size(HeapWord* addr) const +{ + return obj_size(addr_to_bit(addr)); +} + +inline size_t ParMarkBitMap::obj_size(oop obj) const +{ + return obj_size((HeapWord*)obj); +} + +inline ParMarkBitMap::IterationStatus +ParMarkBitMap::iterate(ParMarkBitMapClosure* live_closure, + HeapWord* range_beg, + HeapWord* range_end) const +{ + return iterate(live_closure, addr_to_bit(range_beg), addr_to_bit(range_end)); +} + +inline ParMarkBitMap::IterationStatus +ParMarkBitMap::iterate(ParMarkBitMapClosure* live_closure, + ParMarkBitMapClosure* dead_closure, + HeapWord* range_beg, + HeapWord* range_end, + HeapWord* dead_range_end) const +{ + return iterate(live_closure, dead_closure, + addr_to_bit(range_beg), addr_to_bit(range_end), + addr_to_bit(dead_range_end)); +} + +inline bool +ParMarkBitMap::mark_obj(oop obj, int size) +{ + return mark_obj((HeapWord*)obj, (size_t)size); +} + +inline BitMap::idx_t +ParMarkBitMap::addr_to_bit(HeapWord* addr) const +{ + DEBUG_ONLY(verify_addr(addr);) + return words_to_bits(pointer_delta(addr, region_start())); +} + +inline HeapWord* +ParMarkBitMap::bit_to_addr(idx_t bit) const +{ + DEBUG_ONLY(verify_bit(bit);) + return region_start() + bits_to_words(bit); +} + +inline ParMarkBitMap::idx_t +ParMarkBitMap::find_obj_beg(idx_t beg, idx_t end) const +{ + return _beg_bits.find_next_one_bit(beg, end); +} + +inline ParMarkBitMap::idx_t +ParMarkBitMap::find_obj_end(idx_t beg, idx_t end) const +{ + return _end_bits.find_next_one_bit(beg, end); +} + +inline HeapWord* +ParMarkBitMap::find_obj_beg(HeapWord* beg, HeapWord* end) const +{ + const idx_t beg_bit = addr_to_bit(beg); + const idx_t end_bit = addr_to_bit(end); + const idx_t search_end = BitMap::word_align_up(end_bit); + const idx_t res_bit = MIN2(find_obj_beg(beg_bit, search_end), end_bit); + return bit_to_addr(res_bit); +} + +inline HeapWord* +ParMarkBitMap::find_obj_end(HeapWord* beg, HeapWord* end) const +{ + const idx_t beg_bit = addr_to_bit(beg); + const idx_t end_bit = addr_to_bit(end); + const idx_t search_end = BitMap::word_align_up(end_bit); + const idx_t res_bit = MIN2(find_obj_end(beg_bit, search_end), end_bit); + return bit_to_addr(res_bit); +} + +#ifdef ASSERT +inline void ParMarkBitMap::verify_bit(idx_t bit) const { + // Allow one past the last valid bit; useful for loop bounds. + assert(bit <= _beg_bits.size(), "bit out of range"); +} + +inline void ParMarkBitMap::verify_addr(HeapWord* addr) const { + // Allow one past the last valid address; useful for loop bounds. + assert(addr >= region_start(), "addr too small"); + assert(addr <= region_start() + region_size(), "addr too big"); +} +#endif // #ifdef ASSERT diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.inline.hpp new file mode 100644 index 00000000000..2b54dc67c9c --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.inline.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline bool +ParMarkBitMap::mark_obj(oop obj) +{ + return mark_obj(obj, obj->size()); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp new file mode 100644 index 00000000000..7fde520423c --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp @@ -0,0 +1,909 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_parallelScavengeHeap.cpp.incl" + +PSYoungGen* ParallelScavengeHeap::_young_gen = NULL; +PSOldGen* ParallelScavengeHeap::_old_gen = NULL; +PSPermGen* ParallelScavengeHeap::_perm_gen = NULL; +PSAdaptiveSizePolicy* ParallelScavengeHeap::_size_policy = NULL; +PSGCAdaptivePolicyCounters* ParallelScavengeHeap::_gc_policy_counters = NULL; +ParallelScavengeHeap* ParallelScavengeHeap::_psh = NULL; +GCTaskManager* ParallelScavengeHeap::_gc_task_manager = NULL; + +static void trace_gen_sizes(const char* const str, + size_t pg_min, size_t pg_max, + size_t og_min, size_t og_max, + size_t yg_min, size_t yg_max) +{ + if (TracePageSizes) { + tty->print_cr("%s: " SIZE_FORMAT "," SIZE_FORMAT " " + SIZE_FORMAT "," SIZE_FORMAT " " + SIZE_FORMAT "," SIZE_FORMAT " " + SIZE_FORMAT, + str, pg_min / K, pg_max / K, + og_min / K, og_max / K, + yg_min / K, yg_max / K, + (pg_max + og_max + yg_max) / K); + } +} + +jint ParallelScavengeHeap::initialize() { + // Cannot be initialized until after the flags are parsed + GenerationSizer flag_parser; + + size_t yg_min_size = flag_parser.min_young_gen_size(); + size_t yg_max_size = flag_parser.max_young_gen_size(); + size_t og_min_size = flag_parser.min_old_gen_size(); + size_t og_max_size = flag_parser.max_old_gen_size(); + // Why isn't there a min_perm_gen_size()? + size_t pg_min_size = flag_parser.perm_gen_size(); + size_t pg_max_size = flag_parser.max_perm_gen_size(); + + trace_gen_sizes("ps heap raw", + pg_min_size, pg_max_size, + og_min_size, og_max_size, + yg_min_size, yg_max_size); + + // The ReservedSpace ctor used below requires that the page size for the perm + // gen is <= the page size for the rest of the heap (young + old gens). + const size_t og_page_sz = os::page_size_for_region(yg_min_size + og_min_size, + yg_max_size + og_max_size, + 8); + const size_t pg_page_sz = MIN2(os::page_size_for_region(pg_min_size, + pg_max_size, 16), + og_page_sz); + + const size_t pg_align = set_alignment(_perm_gen_alignment, pg_page_sz); + const size_t og_align = set_alignment(_old_gen_alignment, og_page_sz); + const size_t yg_align = set_alignment(_young_gen_alignment, og_page_sz); + + // Update sizes to reflect the selected page size(s). + // + // NEEDS_CLEANUP. The default TwoGenerationCollectorPolicy uses NewRatio; it + // should check UseAdaptiveSizePolicy. Changes from generationSizer could + // move to the common code. + yg_min_size = align_size_up(yg_min_size, yg_align); + yg_max_size = align_size_up(yg_max_size, yg_align); + size_t yg_cur_size = align_size_up(flag_parser.young_gen_size(), yg_align); + yg_cur_size = MAX2(yg_cur_size, yg_min_size); + + og_min_size = align_size_up(og_min_size, og_align); + og_max_size = align_size_up(og_max_size, og_align); + size_t og_cur_size = align_size_up(flag_parser.old_gen_size(), og_align); + og_cur_size = MAX2(og_cur_size, og_min_size); + + pg_min_size = align_size_up(pg_min_size, pg_align); + pg_max_size = align_size_up(pg_max_size, pg_align); + size_t pg_cur_size = pg_min_size; + + trace_gen_sizes("ps heap rnd", + pg_min_size, pg_max_size, + og_min_size, og_max_size, + yg_min_size, yg_max_size); + + // The main part of the heap (old gen + young gen) can often use a larger page + // size than is needed or wanted for the perm gen. Use the "compound + // alignment" ReservedSpace ctor to avoid having to use the same page size for + // all gens. + ReservedSpace heap_rs(pg_max_size, pg_align, og_max_size + yg_max_size, + og_align); + os::trace_page_sizes("ps perm", pg_min_size, pg_max_size, pg_page_sz, + heap_rs.base(), pg_max_size); + os::trace_page_sizes("ps main", og_min_size + yg_min_size, + og_max_size + yg_max_size, og_page_sz, + heap_rs.base() + pg_max_size, + heap_rs.size() - pg_max_size); + if (!heap_rs.is_reserved()) { + vm_shutdown_during_initialization( + "Could not reserve enough space for object heap"); + return JNI_ENOMEM; + } + + _reserved = MemRegion((HeapWord*)heap_rs.base(), + (HeapWord*)(heap_rs.base() + heap_rs.size())); + + CardTableExtension* const barrier_set = new CardTableExtension(_reserved, 3); + _barrier_set = barrier_set; + oopDesc::set_bs(_barrier_set); + if (_barrier_set == NULL) { + vm_shutdown_during_initialization( + "Could not reserve enough space for barrier set"); + return JNI_ENOMEM; + } + + // Initial young gen size is 4 Mb + // + // XXX - what about flag_parser.young_gen_size()? + const size_t init_young_size = align_size_up(4 * M, yg_align); + yg_cur_size = MAX2(MIN2(init_young_size, yg_max_size), yg_cur_size); + + // Split the reserved space into perm gen and the main heap (everything else). + // The main heap uses a different alignment. + ReservedSpace perm_rs = heap_rs.first_part(pg_max_size); + ReservedSpace main_rs = heap_rs.last_part(pg_max_size, og_align); + + // Make up the generations + // Calculate the maximum size that a generation can grow. This + // includes growth into the other generation. Note that the + // parameter _max_gen_size is kept as the maximum + // size of the generation as the boundaries currently stand. + // _max_gen_size is still used as that value. + double max_gc_pause_sec = ((double) MaxGCPauseMillis)/1000.0; + double max_gc_minor_pause_sec = ((double) MaxGCMinorPauseMillis)/1000.0; + + _gens = new AdjoiningGenerations(main_rs, + og_cur_size, + og_min_size, + og_max_size, + yg_cur_size, + yg_min_size, + yg_max_size, + yg_align); + + _old_gen = _gens->old_gen(); + _young_gen = _gens->young_gen(); + + const size_t eden_capacity = _young_gen->eden_space()->capacity_in_bytes(); + const size_t old_capacity = _old_gen->capacity_in_bytes(); + const size_t initial_promo_size = MIN2(eden_capacity, old_capacity); + _size_policy = + new PSAdaptiveSizePolicy(eden_capacity, + initial_promo_size, + young_gen()->to_space()->capacity_in_bytes(), + intra_generation_alignment(), + max_gc_pause_sec, + max_gc_minor_pause_sec, + GCTimeRatio + ); + + _perm_gen = new PSPermGen(perm_rs, + pg_align, + pg_cur_size, + pg_cur_size, + pg_max_size, + "perm", 2); + + assert(!UseAdaptiveGCBoundary || + (old_gen()->virtual_space()->high_boundary() == + young_gen()->virtual_space()->low_boundary()), + "Boundaries must meet"); + // initialize the policy counters - 2 collectors, 3 generations + _gc_policy_counters = + new PSGCAdaptivePolicyCounters("ParScav:MSC", 2, 3, _size_policy); + _psh = this; + + // Set up the GCTaskManager + _gc_task_manager = GCTaskManager::create(ParallelGCThreads); + + if (UseParallelOldGC && !PSParallelCompact::initialize()) { + return JNI_ENOMEM; + } + + return JNI_OK; +} + +void ParallelScavengeHeap::post_initialize() { + // Need to init the tenuring threshold + PSScavenge::initialize(); + if (UseParallelOldGC) { + PSParallelCompact::post_initialize(); + if (VerifyParallelOldWithMarkSweep) { + // Will be used for verification of par old. + PSMarkSweep::initialize(); + } + } else { + PSMarkSweep::initialize(); + } + PSPromotionManager::initialize(); +} + +void ParallelScavengeHeap::update_counters() { + young_gen()->update_counters(); + old_gen()->update_counters(); + perm_gen()->update_counters(); +} + +size_t ParallelScavengeHeap::capacity() const { + size_t value = young_gen()->capacity_in_bytes() + old_gen()->capacity_in_bytes(); + return value; +} + +size_t ParallelScavengeHeap::used() const { + size_t value = young_gen()->used_in_bytes() + old_gen()->used_in_bytes(); + return value; +} + +bool ParallelScavengeHeap::is_maximal_no_gc() const { + return old_gen()->is_maximal_no_gc() && young_gen()->is_maximal_no_gc(); +} + + +size_t ParallelScavengeHeap::permanent_capacity() const { + return perm_gen()->capacity_in_bytes(); +} + +size_t ParallelScavengeHeap::permanent_used() const { + return perm_gen()->used_in_bytes(); +} + +size_t ParallelScavengeHeap::max_capacity() const { + size_t estimated = reserved_region().byte_size(); + estimated -= perm_gen()->reserved().byte_size(); + if (UseAdaptiveSizePolicy) { + estimated -= _size_policy->max_survivor_size(young_gen()->max_size()); + } else { + estimated -= young_gen()->to_space()->capacity_in_bytes(); + } + return MAX2(estimated, capacity()); +} + +bool ParallelScavengeHeap::is_in(const void* p) const { + if (young_gen()->is_in(p)) { + return true; + } + + if (old_gen()->is_in(p)) { + return true; + } + + if (perm_gen()->is_in(p)) { + return true; + } + + return false; +} + +bool ParallelScavengeHeap::is_in_reserved(const void* p) const { + if (young_gen()->is_in_reserved(p)) { + return true; + } + + if (old_gen()->is_in_reserved(p)) { + return true; + } + + if (perm_gen()->is_in_reserved(p)) { + return true; + } + + return false; +} + +// Static method +bool ParallelScavengeHeap::is_in_young(oop* p) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, + "Must be ParallelScavengeHeap"); + + PSYoungGen* young_gen = heap->young_gen(); + + if (young_gen->is_in_reserved(p)) { + return true; + } + + return false; +} + +// Static method +bool ParallelScavengeHeap::is_in_old_or_perm(oop* p) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, + "Must be ParallelScavengeHeap"); + + PSOldGen* old_gen = heap->old_gen(); + PSPermGen* perm_gen = heap->perm_gen(); + + if (old_gen->is_in_reserved(p)) { + return true; + } + + if (perm_gen->is_in_reserved(p)) { + return true; + } + + return false; +} + +// There are two levels of allocation policy here. +// +// When an allocation request fails, the requesting thread must invoke a VM +// operation, transfer control to the VM thread, and await the results of a +// garbage collection. That is quite expensive, and we should avoid doing it +// multiple times if possible. +// +// To accomplish this, we have a basic allocation policy, and also a +// failed allocation policy. +// +// The basic allocation policy controls how you allocate memory without +// attempting garbage collection. It is okay to grab locks and +// expand the heap, if that can be done without coming to a safepoint. +// It is likely that the basic allocation policy will not be very +// aggressive. +// +// The failed allocation policy is invoked from the VM thread after +// the basic allocation policy is unable to satisfy a mem_allocate +// request. This policy needs to cover the entire range of collection, +// heap expansion, and out-of-memory conditions. It should make every +// attempt to allocate the requested memory. + +// Basic allocation policy. Should never be called at a safepoint, or +// from the VM thread. +// +// This method must handle cases where many mem_allocate requests fail +// simultaneously. When that happens, only one VM operation will succeed, +// and the rest will not be executed. For that reason, this method loops +// during failed allocation attempts. If the java heap becomes exhausted, +// we rely on the size_policy object to force a bail out. +HeapWord* ParallelScavengeHeap::mem_allocate( + size_t size, + bool is_noref, + bool is_tlab, + bool* gc_overhead_limit_was_exceeded) { + assert(!SafepointSynchronize::is_at_safepoint(), "should not be at safepoint"); + assert(Thread::current() != (Thread*)VMThread::vm_thread(), "should not be in vm thread"); + assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock"); + + HeapWord* result = young_gen()->allocate(size, is_tlab); + + uint loop_count = 0; + uint gc_count = 0; + + while (result == NULL) { + // We don't want to have multiple collections for a single filled generation. + // To prevent this, each thread tracks the total_collections() value, and if + // the count has changed, does not do a new collection. + // + // The collection count must be read only while holding the heap lock. VM + // operations also hold the heap lock during collections. There is a lock + // contention case where thread A blocks waiting on the Heap_lock, while + // thread B is holding it doing a collection. When thread A gets the lock, + // the collection count has already changed. To prevent duplicate collections, + // The policy MUST attempt allocations during the same period it reads the + // total_collections() value! + { + MutexLocker ml(Heap_lock); + gc_count = Universe::heap()->total_collections(); + + result = young_gen()->allocate(size, is_tlab); + + // (1) If the requested object is too large to easily fit in the + // young_gen, or + // (2) If GC is locked out via GCLocker, young gen is full and + // the need for a GC already signalled to GCLocker (done + // at a safepoint), + // ... then, rather than force a safepoint and (a potentially futile) + // collection (attempt) for each allocation, try allocation directly + // in old_gen. For case (2) above, we may in the future allow + // TLAB allocation directly in the old gen. + if (result != NULL) { + return result; + } + if (!is_tlab && + size >= (young_gen()->eden_space()->capacity_in_words() / 2)) { + result = old_gen()->allocate(size, is_tlab); + if (result != NULL) { + return result; + } + } + if (GC_locker::is_active_and_needs_gc()) { + // GC is locked out. If this is a TLAB allocation, + // return NULL; the requestor will retry allocation + // of an idividual object at a time. + if (is_tlab) { + return NULL; + } + + // If this thread is not in a jni critical section, we stall + // the requestor until the critical section has cleared and + // GC allowed. When the critical section clears, a GC is + // initiated by the last thread exiting the critical section; so + // we retry the allocation sequence from the beginning of the loop, + // rather than causing more, now probably unnecessary, GC attempts. + JavaThread* jthr = JavaThread::current(); + if (!jthr->in_critical()) { + MutexUnlocker mul(Heap_lock); + GC_locker::stall_until_clear(); + continue; + } else { + if (CheckJNICalls) { + fatal("Possible deadlock due to allocating while" + " in jni critical section"); + } + return NULL; + } + } + } + + if (result == NULL) { + + // Exit the loop if if the gc time limit has been exceeded. + // The allocation must have failed above (result must be NULL), + // and the most recent collection must have exceeded the + // gc time limit. Exit the loop so that an out-of-memory + // will be thrown (returning a NULL will do that), but + // clear gc_time_limit_exceeded so that the next collection + // will succeeded if the applications decides to handle the + // out-of-memory and tries to go on. + *gc_overhead_limit_was_exceeded = size_policy()->gc_time_limit_exceeded(); + if (size_policy()->gc_time_limit_exceeded()) { + size_policy()->set_gc_time_limit_exceeded(false); + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("ParallelScavengeHeap::mem_allocate: " + "return NULL because gc_time_limit_exceeded is set"); + } + return NULL; + } + + // Generate a VM operation + VM_ParallelGCFailedAllocation op(size, is_tlab, gc_count); + VMThread::execute(&op); + + // Did the VM operation execute? If so, return the result directly. + // This prevents us from looping until time out on requests that can + // not be satisfied. + if (op.prologue_succeeded()) { + assert(Universe::heap()->is_in_or_null(op.result()), + "result not in heap"); + + // If GC was locked out during VM operation then retry allocation + // and/or stall as necessary. + if (op.gc_locked()) { + assert(op.result() == NULL, "must be NULL if gc_locked() is true"); + continue; // retry and/or stall as necessary + } + // If a NULL result is being returned, an out-of-memory + // will be thrown now. Clear the gc_time_limit_exceeded + // flag to avoid the following situation. + // gc_time_limit_exceeded is set during a collection + // the collection fails to return enough space and an OOM is thrown + // the next GC is skipped because the gc_time_limit_exceeded + // flag is set and another OOM is thrown + if (op.result() == NULL) { + size_policy()->set_gc_time_limit_exceeded(false); + } + return op.result(); + } + } + + // The policy object will prevent us from looping forever. If the + // time spent in gc crosses a threshold, we will bail out. + loop_count++; + if ((result == NULL) && (QueuedAllocationWarningCount > 0) && + (loop_count % QueuedAllocationWarningCount == 0)) { + warning("ParallelScavengeHeap::mem_allocate retries %d times \n\t" + " size=%d %s", loop_count, size, is_tlab ? "(TLAB)" : ""); + } + } + + return result; +} + +// Failed allocation policy. Must be called from the VM thread, and +// only at a safepoint! Note that this method has policy for allocation +// flow, and NOT collection policy. So we do not check for gc collection +// time over limit here, that is the responsibility of the heap specific +// collection methods. This method decides where to attempt allocations, +// and when to attempt collections, but no collection specific policy. +HeapWord* ParallelScavengeHeap::failed_mem_allocate(size_t size, bool is_tlab) { + assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); + assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread"); + assert(!Universe::heap()->is_gc_active(), "not reentrant"); + assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock"); + + size_t mark_sweep_invocation_count = total_invocations(); + + // We assume (and assert!) that an allocation at this point will fail + // unless we collect. + + // First level allocation failure, scavenge and allocate in young gen. + GCCauseSetter gccs(this, GCCause::_allocation_failure); + PSScavenge::invoke(); + HeapWord* result = young_gen()->allocate(size, is_tlab); + + // Second level allocation failure. + // Mark sweep and allocate in young generation. + if (result == NULL) { + // There is some chance the scavenge method decided to invoke mark_sweep. + // Don't mark sweep twice if so. + if (mark_sweep_invocation_count == total_invocations()) { + invoke_full_gc(false); + result = young_gen()->allocate(size, is_tlab); + } + } + + // Third level allocation failure. + // After mark sweep and young generation allocation failure, + // allocate in old generation. + if (result == NULL && !is_tlab) { + result = old_gen()->allocate(size, is_tlab); + } + + // Fourth level allocation failure. We're running out of memory. + // More complete mark sweep and allocate in young generation. + if (result == NULL) { + invoke_full_gc(true); + result = young_gen()->allocate(size, is_tlab); + } + + // Fifth level allocation failure. + // After more complete mark sweep, allocate in old generation. + if (result == NULL && !is_tlab) { + result = old_gen()->allocate(size, is_tlab); + } + + return result; +} + +// +// This is the policy loop for allocating in the permanent generation. +// If the initial allocation fails, we create a vm operation which will +// cause a collection. +HeapWord* ParallelScavengeHeap::permanent_mem_allocate(size_t size) { + assert(!SafepointSynchronize::is_at_safepoint(), "should not be at safepoint"); + assert(Thread::current() != (Thread*)VMThread::vm_thread(), "should not be in vm thread"); + assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock"); + + HeapWord* result; + + uint loop_count = 0; + uint gc_count = 0; + uint full_gc_count = 0; + + do { + // We don't want to have multiple collections for a single filled generation. + // To prevent this, each thread tracks the total_collections() value, and if + // the count has changed, does not do a new collection. + // + // The collection count must be read only while holding the heap lock. VM + // operations also hold the heap lock during collections. There is a lock + // contention case where thread A blocks waiting on the Heap_lock, while + // thread B is holding it doing a collection. When thread A gets the lock, + // the collection count has already changed. To prevent duplicate collections, + // The policy MUST attempt allocations during the same period it reads the + // total_collections() value! + { + MutexLocker ml(Heap_lock); + gc_count = Universe::heap()->total_collections(); + full_gc_count = Universe::heap()->total_full_collections(); + + result = perm_gen()->allocate_permanent(size); + } + + if (result == NULL) { + + // Exit the loop if the gc time limit has been exceeded. + // The allocation must have failed above (result must be NULL), + // and the most recent collection must have exceeded the + // gc time limit. Exit the loop so that an out-of-memory + // will be thrown (returning a NULL will do that), but + // clear gc_time_limit_exceeded so that the next collection + // will succeeded if the applications decides to handle the + // out-of-memory and tries to go on. + if (size_policy()->gc_time_limit_exceeded()) { + size_policy()->set_gc_time_limit_exceeded(false); + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("ParallelScavengeHeap::permanent_mem_allocate: " + "return NULL because gc_time_limit_exceeded is set"); + } + assert(result == NULL, "Allocation did not fail"); + return NULL; + } + + // Generate a VM operation + VM_ParallelGCFailedPermanentAllocation op(size, gc_count, full_gc_count); + VMThread::execute(&op); + + // Did the VM operation execute? If so, return the result directly. + // This prevents us from looping until time out on requests that can + // not be satisfied. + if (op.prologue_succeeded()) { + assert(Universe::heap()->is_in_permanent_or_null(op.result()), + "result not in heap"); + // If a NULL results is being returned, an out-of-memory + // will be thrown now. Clear the gc_time_limit_exceeded + // flag to avoid the following situation. + // gc_time_limit_exceeded is set during a collection + // the collection fails to return enough space and an OOM is thrown + // the next GC is skipped because the gc_time_limit_exceeded + // flag is set and another OOM is thrown + if (op.result() == NULL) { + size_policy()->set_gc_time_limit_exceeded(false); + } + return op.result(); + } + } + + // The policy object will prevent us from looping forever. If the + // time spent in gc crosses a threshold, we will bail out. + loop_count++; + if ((QueuedAllocationWarningCount > 0) && + (loop_count % QueuedAllocationWarningCount == 0)) { + warning("ParallelScavengeHeap::permanent_mem_allocate retries %d times \n\t" + " size=%d", loop_count, size); + } + } while (result == NULL); + + return result; +} + +// +// This is the policy code for permanent allocations which have failed +// and require a collection. Note that just as in failed_mem_allocate, +// we do not set collection policy, only where & when to allocate and +// collect. +HeapWord* ParallelScavengeHeap::failed_permanent_mem_allocate(size_t size) { + assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); + assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread"); + assert(!Universe::heap()->is_gc_active(), "not reentrant"); + assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock"); + assert(size > perm_gen()->free_in_words(), "Allocation should fail"); + + // We assume (and assert!) that an allocation at this point will fail + // unless we collect. + + // First level allocation failure. Mark-sweep and allocate in perm gen. + GCCauseSetter gccs(this, GCCause::_allocation_failure); + invoke_full_gc(false); + HeapWord* result = perm_gen()->allocate_permanent(size); + + // Second level allocation failure. We're running out of memory. + if (result == NULL) { + invoke_full_gc(true); + result = perm_gen()->allocate_permanent(size); + } + + return result; +} + +void ParallelScavengeHeap::ensure_parsability(bool retire_tlabs) { + CollectedHeap::ensure_parsability(retire_tlabs); + young_gen()->eden_space()->ensure_parsability(); +} + +size_t ParallelScavengeHeap::unsafe_max_alloc() { + return young_gen()->eden_space()->free_in_bytes(); +} + +size_t ParallelScavengeHeap::tlab_capacity(Thread* thr) const { + return young_gen()->eden_space()->tlab_capacity(thr); +} + +size_t ParallelScavengeHeap::unsafe_max_tlab_alloc(Thread* thr) const { + return young_gen()->eden_space()->unsafe_max_tlab_alloc(thr); +} + +HeapWord* ParallelScavengeHeap::allocate_new_tlab(size_t size) { + return young_gen()->allocate(size, true); +} + +void ParallelScavengeHeap::fill_all_tlabs(bool retire) { + CollectedHeap::fill_all_tlabs(retire); +} + +void ParallelScavengeHeap::accumulate_statistics_all_tlabs() { + CollectedHeap::accumulate_statistics_all_tlabs(); +} + +void ParallelScavengeHeap::resize_all_tlabs() { + CollectedHeap::resize_all_tlabs(); +} + +// This method is used by System.gc() and JVMTI. +void ParallelScavengeHeap::collect(GCCause::Cause cause) { + assert(!Heap_lock->owned_by_self(), + "this thread should not own the Heap_lock"); + + unsigned int gc_count = 0; + unsigned int full_gc_count = 0; + { + MutexLocker ml(Heap_lock); + // This value is guarded by the Heap_lock + gc_count = Universe::heap()->total_collections(); + full_gc_count = Universe::heap()->total_full_collections(); + } + + VM_ParallelGCSystemGC op(gc_count, full_gc_count, cause); + VMThread::execute(&op); +} + +// This interface assumes that it's being called by the +// vm thread. It collects the heap assuming that the +// heap lock is already held and that we are executing in +// the context of the vm thread. +void ParallelScavengeHeap::collect_as_vm_thread(GCCause::Cause cause) { + assert(Thread::current()->is_VM_thread(), "Precondition#1"); + assert(Heap_lock->is_locked(), "Precondition#2"); + GCCauseSetter gcs(this, cause); + switch (cause) { + case GCCause::_heap_inspection: + case GCCause::_heap_dump: { + HandleMark hm; + invoke_full_gc(false); + break; + } + default: // XXX FIX ME + ShouldNotReachHere(); + } +} + + +void ParallelScavengeHeap::oop_iterate(OopClosure* cl) { + Unimplemented(); +} + +void ParallelScavengeHeap::object_iterate(ObjectClosure* cl) { + young_gen()->object_iterate(cl); + old_gen()->object_iterate(cl); + perm_gen()->object_iterate(cl); +} + +void ParallelScavengeHeap::permanent_oop_iterate(OopClosure* cl) { + Unimplemented(); +} + +void ParallelScavengeHeap::permanent_object_iterate(ObjectClosure* cl) { + perm_gen()->object_iterate(cl); +} + +HeapWord* ParallelScavengeHeap::block_start(const void* addr) const { + if (young_gen()->is_in_reserved(addr)) { + assert(young_gen()->is_in(addr), + "addr should be in allocated part of young gen"); + Unimplemented(); + } else if (old_gen()->is_in_reserved(addr)) { + assert(old_gen()->is_in(addr), + "addr should be in allocated part of old gen"); + return old_gen()->start_array()->object_start((HeapWord*)addr); + } else if (perm_gen()->is_in_reserved(addr)) { + assert(perm_gen()->is_in(addr), + "addr should be in allocated part of perm gen"); + return perm_gen()->start_array()->object_start((HeapWord*)addr); + } + return 0; +} + +size_t ParallelScavengeHeap::block_size(const HeapWord* addr) const { + return oop(addr)->size(); +} + +bool ParallelScavengeHeap::block_is_obj(const HeapWord* addr) const { + return block_start(addr) == addr; +} + +jlong ParallelScavengeHeap::millis_since_last_gc() { + return UseParallelOldGC ? + PSParallelCompact::millis_since_last_gc() : + PSMarkSweep::millis_since_last_gc(); +} + +void ParallelScavengeHeap::prepare_for_verify() { + ensure_parsability(false); // no need to retire TLABs for verification +} + +void ParallelScavengeHeap::print() const { print_on(tty); } + +void ParallelScavengeHeap::print_on(outputStream* st) const { + young_gen()->print_on(st); + old_gen()->print_on(st); + perm_gen()->print_on(st); +} + +void ParallelScavengeHeap::gc_threads_do(ThreadClosure* tc) const { + PSScavenge::gc_task_manager()->threads_do(tc); +} + +void ParallelScavengeHeap::print_gc_threads_on(outputStream* st) const { + PSScavenge::gc_task_manager()->print_threads_on(st); +} + +void ParallelScavengeHeap::print_tracing_info() const { + if (TraceGen0Time) { + double time = PSScavenge::accumulated_time()->seconds(); + tty->print_cr("[Accumulated GC generation 0 time %3.7f secs]", time); + } + if (TraceGen1Time) { + double time = PSMarkSweep::accumulated_time()->seconds(); + tty->print_cr("[Accumulated GC generation 1 time %3.7f secs]", time); + } +} + + +void ParallelScavengeHeap::verify(bool allow_dirty, bool silent) { + // Why do we need the total_collections()-filter below? + if (total_collections() > 0) { + if (!silent) { + gclog_or_tty->print("permanent "); + } + perm_gen()->verify(allow_dirty); + + if (!silent) { + gclog_or_tty->print("tenured "); + } + old_gen()->verify(allow_dirty); + + if (!silent) { + gclog_or_tty->print("eden "); + } + young_gen()->verify(allow_dirty); + } + if (!silent) { + gclog_or_tty->print("ref_proc "); + } + ReferenceProcessor::verify(); +} + +void ParallelScavengeHeap::print_heap_change(size_t prev_used) { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print(" " SIZE_FORMAT + "->" SIZE_FORMAT + "(" SIZE_FORMAT ")", + prev_used, used(), capacity()); + } else { + gclog_or_tty->print(" " SIZE_FORMAT "K" + "->" SIZE_FORMAT "K" + "(" SIZE_FORMAT "K)", + prev_used / K, used() / K, capacity() / K); + } +} + +ParallelScavengeHeap* ParallelScavengeHeap::heap() { + assert(_psh != NULL, "Uninitialized access to ParallelScavengeHeap::heap()"); + assert(_psh->kind() == CollectedHeap::ParallelScavengeHeap, "not a parallel scavenge heap"); + return _psh; +} + +// Before delegating the resize to the young generation, +// the reserved space for the young and old generations +// may be changed to accomodate the desired resize. +void ParallelScavengeHeap::resize_young_gen(size_t eden_size, + size_t survivor_size) { + if (UseAdaptiveGCBoundary) { + if (size_policy()->bytes_absorbed_from_eden() != 0) { + size_policy()->reset_bytes_absorbed_from_eden(); + return; // The generation changed size already. + } + gens()->adjust_boundary_for_young_gen_needs(eden_size, survivor_size); + } + + // Delegate the resize to the generation. + _young_gen->resize(eden_size, survivor_size); +} + +// Before delegating the resize to the old generation, +// the reserved space for the young and old generations +// may be changed to accomodate the desired resize. +void ParallelScavengeHeap::resize_old_gen(size_t desired_free_space) { + if (UseAdaptiveGCBoundary) { + if (size_policy()->bytes_absorbed_from_eden() != 0) { + size_policy()->reset_bytes_absorbed_from_eden(); + return; // The generation changed size already. + } + gens()->adjust_boundary_for_old_gen_needs(desired_free_space); + } + + // Delegate the resize to the generation. + _old_gen->resize(desired_free_space); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp new file mode 100644 index 00000000000..5c1a765c215 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp @@ -0,0 +1,222 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class AdjoiningGenerations; +class GCTaskManager; +class PSAdaptiveSizePolicy; + +class ParallelScavengeHeap : public CollectedHeap { + friend class VMStructs; + private: + static PSYoungGen* _young_gen; + static PSOldGen* _old_gen; + static PSPermGen* _perm_gen; + + // Sizing policy for entire heap + static PSAdaptiveSizePolicy* _size_policy; + static PSGCAdaptivePolicyCounters* _gc_policy_counters; + + static ParallelScavengeHeap* _psh; + + size_t _perm_gen_alignment; + size_t _young_gen_alignment; + size_t _old_gen_alignment; + + inline size_t set_alignment(size_t& var, size_t val); + + // Collection of generations that are adjacent in the + // space reserved for the heap. + AdjoiningGenerations* _gens; + + static GCTaskManager* _gc_task_manager; // The task manager. + + protected: + static inline size_t total_invocations(); + HeapWord* allocate_new_tlab(size_t size); + void fill_all_tlabs(bool retire); + + public: + ParallelScavengeHeap() : CollectedHeap() { + set_alignment(_perm_gen_alignment, intra_generation_alignment()); + set_alignment(_young_gen_alignment, intra_generation_alignment()); + set_alignment(_old_gen_alignment, intra_generation_alignment()); + } + + // For use by VM operations + enum CollectionType { + Scavenge, + MarkSweep + }; + + ParallelScavengeHeap::Name kind() const { + return CollectedHeap::ParallelScavengeHeap; + } + + static PSYoungGen* young_gen() { return _young_gen; } + static PSOldGen* old_gen() { return _old_gen; } + static PSPermGen* perm_gen() { return _perm_gen; } + + virtual PSAdaptiveSizePolicy* size_policy() { return _size_policy; } + + static PSGCAdaptivePolicyCounters* gc_policy_counters() { return _gc_policy_counters; } + + static ParallelScavengeHeap* heap(); + + static GCTaskManager* const gc_task_manager() { return _gc_task_manager; } + + AdjoiningGenerations* gens() { return _gens; } + + // Returns JNI_OK on success + virtual jint initialize(); + + void post_initialize(); + void update_counters(); + + // The alignment used for the various generations. + size_t perm_gen_alignment() const { return _perm_gen_alignment; } + size_t young_gen_alignment() const { return _young_gen_alignment; } + size_t old_gen_alignment() const { return _old_gen_alignment; } + + // The alignment used for eden and survivors within the young gen. + size_t intra_generation_alignment() const { return 64 * K; } + + size_t capacity() const; + size_t used() const; + + // Return "true" if all generations (but perm) have reached the + // maximal committed limit that they can reach, without a garbage + // collection. + virtual bool is_maximal_no_gc() const; + + // Does this heap support heap inspection? (+PrintClassHistogram) + bool supports_heap_inspection() const { return true; } + + size_t permanent_capacity() const; + size_t permanent_used() const; + + size_t max_capacity() const; + + // Whether p is in the allocated part of the heap + bool is_in(const void* p) const; + + bool is_in_reserved(const void* p) const; + bool is_in_permanent(const void *p) const { // reserved part + return perm_gen()->reserved().contains(p); + } + + bool is_permanent(const void *p) const { // committed part + return perm_gen()->is_in(p); + } + + static bool is_in_young(oop *p); // reserved part + static bool is_in_old_or_perm(oop *p); // reserved part + + // Memory allocation. "gc_time_limit_was_exceeded" will + // be set to true if the adaptive size policy determine that + // an excessive amount of time is being spent doing collections + // and caused a NULL to be returned. If a NULL is not returned, + // "gc_time_limit_was_exceeded" has an undefined meaning. + + HeapWord* mem_allocate(size_t size, + bool is_noref, + bool is_tlab, + bool* gc_overhead_limit_was_exceeded); + HeapWord* failed_mem_allocate(size_t size, bool is_tlab); + + HeapWord* permanent_mem_allocate(size_t size); + HeapWord* failed_permanent_mem_allocate(size_t size); + + // Support for System.gc() + void collect(GCCause::Cause cause); + + // This interface assumes that it's being called by the + // vm thread. It collects the heap assuming that the + // heap lock is already held and that we are executing in + // the context of the vm thread. + void collect_as_vm_thread(GCCause::Cause cause); + + // These also should be called by the vm thread at a safepoint (e.g., from a + // VM operation). + // + // The first collects the young generation only, unless the scavenge fails; it + // will then attempt a full gc. The second collects the entire heap; if + // maximum_compaction is true, it will compact everything and clear all soft + // references. + inline void invoke_scavenge(); + inline void invoke_full_gc(bool maximum_compaction); + + size_t large_typearray_limit() { return FastAllocateSizeLimit; } + + bool supports_inline_contig_alloc() const { return !UseNUMA; } + HeapWord** top_addr() const { return !UseNUMA ? young_gen()->top_addr() : NULL; } + HeapWord** end_addr() const { return !UseNUMA ? young_gen()->end_addr() : NULL; } + + void ensure_parsability(bool retire_tlabs); + void accumulate_statistics_all_tlabs(); + void resize_all_tlabs(); + + size_t unsafe_max_alloc(); + + bool supports_tlab_allocation() const { return true; } + + size_t tlab_capacity(Thread* thr) const; + size_t unsafe_max_tlab_alloc(Thread* thr) const; + + void oop_iterate(OopClosure* cl); + void object_iterate(ObjectClosure* cl); + void permanent_oop_iterate(OopClosure* cl); + void permanent_object_iterate(ObjectClosure* cl); + + HeapWord* block_start(const void* addr) const; + size_t block_size(const HeapWord* addr) const; + bool block_is_obj(const HeapWord* addr) const; + + jlong millis_since_last_gc(); + + void prepare_for_verify(); + void print() const; + void print_on(outputStream* st) const; + virtual void print_gc_threads_on(outputStream* st) const; + virtual void gc_threads_do(ThreadClosure* tc) const; + virtual void print_tracing_info() const; + + void verify(bool allow_dirty, bool silent); + + void print_heap_change(size_t prev_used); + + // Resize the young generation. The reserved space for the + // generation may be expanded in preparation for the resize. + void resize_young_gen(size_t eden_size, size_t survivor_size); + + // Resize the old generation. The reserved space for the + // generation may be expanded in preparation for the resize. + void resize_old_gen(size_t desired_free_space); +}; + +inline size_t ParallelScavengeHeap::set_alignment(size_t& var, size_t val) +{ + assert(is_power_of_2((intptr_t)val), "must be a power of 2"); + var = round_to(val, intra_generation_alignment()); + return var; +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp new file mode 100644 index 00000000000..bcc80ab630e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp @@ -0,0 +1,43 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline size_t ParallelScavengeHeap::total_invocations() +{ + return UseParallelOldGC ? PSParallelCompact::total_invocations() : + PSMarkSweep::total_invocations(); +} + +inline void ParallelScavengeHeap::invoke_scavenge() +{ + PSScavenge::invoke(); +} + +inline void ParallelScavengeHeap::invoke_full_gc(bool maximum_compaction) +{ + if (UseParallelOldGC) { + PSParallelCompact::invoke(maximum_compaction); + } else { + PSMarkSweep::invoke(maximum_compaction); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp new file mode 100644 index 00000000000..a4f8878b2c3 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp @@ -0,0 +1,283 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_pcTasks.cpp.incl" + +// +// ThreadRootsMarkingTask +// + +void ThreadRootsMarkingTask::do_it(GCTaskManager* manager, uint which) { + assert(Universe::heap()->is_gc_active(), "called outside gc"); + + ResourceMark rm; + + NOT_PRODUCT(TraceTime tm("ThreadRootsMarkingTask", + PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); + ParCompactionManager* cm = + ParCompactionManager::gc_thread_compaction_manager(which); + PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); + + if (_java_thread != NULL) + _java_thread->oops_do(&mark_and_push_closure); + + if (_vm_thread != NULL) + _vm_thread->oops_do(&mark_and_push_closure); + + // Do the real work + cm->drain_marking_stacks(&mark_and_push_closure); +} + + +void MarkFromRootsTask::do_it(GCTaskManager* manager, uint which) { + assert(Universe::heap()->is_gc_active(), "called outside gc"); + + NOT_PRODUCT(TraceTime tm("MarkFromRootsTask", + PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); + ParCompactionManager* cm = + ParCompactionManager::gc_thread_compaction_manager(which); + // cm->allocate_stacks(); + assert(cm->stacks_have_been_allocated(), + "Stack space has not been allocated"); + PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); + + switch (_root_type) { + case universe: + Universe::oops_do(&mark_and_push_closure); + break; + + case reference_processing: + ReferenceProcessor::oops_do(&mark_and_push_closure); + break; + + case jni_handles: + JNIHandles::oops_do(&mark_and_push_closure); + break; + + case threads: + { + ResourceMark rm; + Threads::oops_do(&mark_and_push_closure); + } + break; + + case object_synchronizer: + ObjectSynchronizer::oops_do(&mark_and_push_closure); + break; + + case flat_profiler: + FlatProfiler::oops_do(&mark_and_push_closure); + break; + + case management: + Management::oops_do(&mark_and_push_closure); + break; + + case jvmti: + JvmtiExport::oops_do(&mark_and_push_closure); + break; + + case system_dictionary: + SystemDictionary::always_strong_oops_do(&mark_and_push_closure); + break; + + case vm_symbols: + vmSymbols::oops_do(&mark_and_push_closure); + break; + + default: + fatal("Unknown root type"); + } + + // Do the real work + cm->drain_marking_stacks(&mark_and_push_closure); + // cm->deallocate_stacks(); +} + + +// +// RefProcTaskProxy +// + +void RefProcTaskProxy::do_it(GCTaskManager* manager, uint which) +{ + assert(Universe::heap()->is_gc_active(), "called outside gc"); + + NOT_PRODUCT(TraceTime tm("RefProcTask", + PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); + ParCompactionManager* cm = + ParCompactionManager::gc_thread_compaction_manager(which); + // cm->allocate_stacks(); + assert(cm->stacks_have_been_allocated(), + "Stack space has not been allocated"); + PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); + PSParallelCompact::FollowStackClosure follow_stack_closure(cm); + _rp_task.work(_work_id, *PSParallelCompact::is_alive_closure(), + mark_and_push_closure, follow_stack_closure); +} + +// +// RefProcTaskExecutor +// + +void RefProcTaskExecutor::execute(ProcessTask& task) +{ + ParallelScavengeHeap* heap = PSParallelCompact::gc_heap(); + uint parallel_gc_threads = heap->gc_task_manager()->workers(); + TaskQueueSetSuper* qset = ParCompactionManager::chunk_array(); + ParallelTaskTerminator terminator(parallel_gc_threads, qset); + GCTaskQueue* q = GCTaskQueue::create(); + for(uint i=0; ienqueue(new RefProcTaskProxy(task, i)); + } + if (task.marks_oops_alive()) { + if (parallel_gc_threads>1) { + for (uint j=0; jenqueue(new StealMarkingTask(&terminator)); + } + } + } + PSParallelCompact::gc_task_manager()->execute_and_wait(q); +} + +void RefProcTaskExecutor::execute(EnqueueTask& task) +{ + ParallelScavengeHeap* heap = PSParallelCompact::gc_heap(); + uint parallel_gc_threads = heap->gc_task_manager()->workers(); + GCTaskQueue* q = GCTaskQueue::create(); + for(uint i=0; ienqueue(new RefEnqueueTaskProxy(task, i)); + } + PSParallelCompact::gc_task_manager()->execute_and_wait(q); +} + +// +// StealMarkingTask +// + +StealMarkingTask::StealMarkingTask(ParallelTaskTerminator* t) : + _terminator(t) {} + +void StealMarkingTask::do_it(GCTaskManager* manager, uint which) { + assert(Universe::heap()->is_gc_active(), "called outside gc"); + + NOT_PRODUCT(TraceTime tm("StealMarkingTask", + PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); + + ParCompactionManager* cm = + ParCompactionManager::gc_thread_compaction_manager(which); + PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); + + oop obj = NULL; + int random_seed = 17; + while(true) { + if (ParCompactionManager::steal(which, &random_seed, obj)) { + obj->follow_contents(cm); + cm->drain_marking_stacks(&mark_and_push_closure); + } else { + if (terminator()->offer_termination()) { + break; + } + } + } +} + +// +// StealChunkCompactionTask +// + + +StealChunkCompactionTask::StealChunkCompactionTask(ParallelTaskTerminator* t) : + _terminator(t) {}; + +void StealChunkCompactionTask::do_it(GCTaskManager* manager, uint which) { + assert(Universe::heap()->is_gc_active(), "called outside gc"); + + NOT_PRODUCT(TraceTime tm("StealChunkCompactionTask", + PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); + + ParCompactionManager* cm = + ParCompactionManager::gc_thread_compaction_manager(which); + + // Has to drain stacks first because there may be chunks on + // preloaded onto the stack and this thread may never have + // done a draining task. Are the draining tasks needed? + + cm->drain_chunk_stacks(); + + size_t chunk_index = 0; + int random_seed = 17; + + // If we're the termination task, try 10 rounds of stealing before + // setting the termination flag + + while(true) { + if (ParCompactionManager::steal(which, &random_seed, chunk_index)) { + PSParallelCompact::fill_and_update_chunk(cm, chunk_index); + cm->drain_chunk_stacks(); + } else { + if (terminator()->offer_termination()) { + break; + } + // Go around again. + } + } + return; +} + +UpdateDensePrefixTask::UpdateDensePrefixTask( + PSParallelCompact::SpaceId space_id, + size_t chunk_index_start, + size_t chunk_index_end) : + _space_id(space_id), _chunk_index_start(chunk_index_start), + _chunk_index_end(chunk_index_end) +{} + +void UpdateDensePrefixTask::do_it(GCTaskManager* manager, uint which) { + + NOT_PRODUCT(TraceTime tm("UpdateDensePrefixTask", + PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); + + ParCompactionManager* cm = + ParCompactionManager::gc_thread_compaction_manager(which); + + PSParallelCompact::update_and_deadwood_in_dense_prefix(cm, + _space_id, + _chunk_index_start, + _chunk_index_end); +} + +void DrainStacksCompactionTask::do_it(GCTaskManager* manager, uint which) { + assert(Universe::heap()->is_gc_active(), "called outside gc"); + + NOT_PRODUCT(TraceTime tm("DrainStacksCompactionTask", + PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); + + ParCompactionManager* cm = + ParCompactionManager::gc_thread_compaction_manager(which); + + // Process any chunks already in the compaction managers stacks. + cm->drain_chunk_stacks(); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.hpp new file mode 100644 index 00000000000..69b23ac5b3c --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.hpp @@ -0,0 +1,247 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +// Tasks for parallel compaction of the old generation +// +// Tasks are created and enqueued on a task queue. The +// tasks for parallel old collector for marking objects +// are MarkFromRootsTask and ThreadRootsMarkingTask. +// +// MarkFromRootsTask's are created +// with a root group (e.g., jni_handles) and when the do_it() +// method of a MarkFromRootsTask is executed, it starts marking +// form it's root group. +// +// ThreadRootsMarkingTask's are created for each Java thread. When +// the do_it() method of a ThreadRootsMarkingTask is executed, it +// starts marking from the thread's roots. +// +// The enqueuing of the MarkFromRootsTask and ThreadRootsMarkingTask +// do little more than create the task and put it on a queue. The +// queue is a GCTaskQueue and threads steal tasks from this GCTaskQueue. +// +// In addition to the MarkFromRootsTask and ThreadRootsMarkingTask +// tasks there are StealMarkingTask tasks. The StealMarkingTask's +// steal a reference from the marking stack of another +// thread and transitively marks the object of the reference +// and internal references. After successfully stealing a reference +// and marking it, the StealMarkingTask drains its marking stack +// stack before attempting another steal. +// +// ThreadRootsMarkingTask +// +// This task marks from the roots of a single thread. This task +// enables marking of thread roots in parallel. +// + +class ParallelTaskTerminator; + +class ThreadRootsMarkingTask : public GCTask { + private: + JavaThread* _java_thread; + VMThread* _vm_thread; + public: + ThreadRootsMarkingTask(JavaThread* root) : _java_thread(root), _vm_thread(NULL) {} + ThreadRootsMarkingTask(VMThread* root) : _java_thread(NULL), _vm_thread(root) {} + + char* name() { return (char *)"thread-roots-marking-task"; } + + virtual void do_it(GCTaskManager* manager, uint which); +}; + + +// +// MarkFromRootsTask +// +// This task marks from all the roots to all live +// objects. +// +// + +class MarkFromRootsTask : public GCTask { + public: + enum RootType { + universe = 1, + jni_handles = 2, + threads = 3, + object_synchronizer = 4, + flat_profiler = 5, + management = 6, + jvmti = 7, + system_dictionary = 8, + vm_symbols = 9, + reference_processing = 10 + }; + private: + RootType _root_type; + public: + MarkFromRootsTask(RootType value) : _root_type(value) {} + + char* name() { return (char *)"mark-from-roots-task"; } + + virtual void do_it(GCTaskManager* manager, uint which); +}; + +// +// RefProcTaskProxy +// +// This task is used as a proxy to parallel reference processing tasks . +// + +class RefProcTaskProxy : public GCTask { + typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask; + ProcessTask & _rp_task; + uint _work_id; +public: + RefProcTaskProxy(ProcessTask & rp_task, uint work_id) + : _rp_task(rp_task), + _work_id(work_id) + { } + +private: + virtual char* name() { return (char *)"Process referents by policy in parallel"; } + + virtual void do_it(GCTaskManager* manager, uint which); +}; + + + +// +// RefEnqueueTaskProxy +// +// This task is used as a proxy to parallel reference processing tasks . +// + +class RefEnqueueTaskProxy: public GCTask { + typedef AbstractRefProcTaskExecutor::EnqueueTask EnqueueTask; + EnqueueTask& _enq_task; + uint _work_id; + +public: + RefEnqueueTaskProxy(EnqueueTask& enq_task, uint work_id) + : _enq_task(enq_task), + _work_id(work_id) + { } + + virtual char* name() { return (char *)"Enqueue reference objects in parallel"; } + virtual void do_it(GCTaskManager* manager, uint which) + { + _enq_task.work(_work_id); + } +}; + + +// +// RefProcTaskExecutor +// +// Task executor is an interface for the reference processor to run +// tasks using GCTaskManager. +// + +class RefProcTaskExecutor: public AbstractRefProcTaskExecutor { + virtual void execute(ProcessTask& task); + virtual void execute(EnqueueTask& task); +}; + + +// +// StealMarkingTask +// +// This task is used to distribute work to idle threads. +// + +class StealMarkingTask : public GCTask { + private: + ParallelTaskTerminator* const _terminator; + private: + + public: + char* name() { return (char *)"steal-marking-task"; } + + StealMarkingTask(ParallelTaskTerminator* t); + + ParallelTaskTerminator* terminator() { return _terminator; } + + virtual void do_it(GCTaskManager* manager, uint which); +}; + +// +// StealChunkCompactionTask +// +// This task is used to distribute work to idle threads. +// + +class StealChunkCompactionTask : public GCTask { + private: + ParallelTaskTerminator* const _terminator; + public: + StealChunkCompactionTask(ParallelTaskTerminator* t); + + char* name() { return (char *)"steal-chunk-task"; } + ParallelTaskTerminator* terminator() { return _terminator; } + + virtual void do_it(GCTaskManager* manager, uint which); +}; + +// +// UpdateDensePrefixTask +// +// This task is used to update the dense prefix +// of a space. +// + +class UpdateDensePrefixTask : public GCTask { + private: + PSParallelCompact::SpaceId _space_id; + size_t _chunk_index_start; + size_t _chunk_index_end; + + public: + char* name() { return (char *)"update-dense_prefix-task"; } + + UpdateDensePrefixTask(PSParallelCompact::SpaceId space_id, + size_t chunk_index_start, + size_t chunk_index_end); + + virtual void do_it(GCTaskManager* manager, uint which); +}; + +// +// DrainStacksCompactionTask +// +// This task processes chunks that have been added to the stacks of each +// compaction manager. +// +// Trying to use one draining thread does not work because there are no +// guarantees about which task will be picked up by which thread. For example, +// if thread A gets all the preloaded chunks, thread A may not get a draining +// task (they may all be done by other threads). +// + +class DrainStacksCompactionTask : public GCTask { + public: + char* name() { return (char *)"drain-chunk-task"; } + virtual void do_it(GCTaskManager* manager, uint which); +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/prefetchQueue.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/prefetchQueue.hpp new file mode 100644 index 00000000000..5bf0b398050 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/prefetchQueue.hpp @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// PrefetchQueue is a FIFO queue of variable length (currently 8). +// +// We need to examine the performance penalty of variable lengths. +// We may also want to split this into cpu dependant bits. +// + +const int PREFETCH_QUEUE_SIZE = 8; + +class PrefetchQueue : public CHeapObj { + private: + oop* _prefetch_queue[PREFETCH_QUEUE_SIZE]; + unsigned int _prefetch_index; + + public: + int length() { return PREFETCH_QUEUE_SIZE; } + + inline void clear() { + for(int i=0; imark_addr(), 0); + // This prefetch is intended to make sure the size field of array + // oops is in cache. It assumes the the object layout is + // mark -> klass -> size, and that mark and klass are heapword + // sized. If this should change, this prefetch will need updating! + Prefetch::write((*p)->mark_addr() + (HeapWordSize*2), 0); + _prefetch_queue[_prefetch_index++] = p; + _prefetch_index &= (PREFETCH_QUEUE_SIZE-1); + return _prefetch_queue[_prefetch_index]; + } + + // Stores a NULL pointer in the pop'd location. + inline oop* pop() { + _prefetch_queue[_prefetch_index++] = NULL; + _prefetch_index &= (PREFETCH_QUEUE_SIZE-1); + return _prefetch_queue[_prefetch_index]; + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp new file mode 100644 index 00000000000..44629831ffe --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp @@ -0,0 +1,1175 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_psAdaptiveSizePolicy.cpp.incl" + +#include + +PSAdaptiveSizePolicy::PSAdaptiveSizePolicy(size_t init_eden_size, + size_t init_promo_size, + size_t init_survivor_size, + size_t intra_generation_alignment, + double gc_pause_goal_sec, + double gc_minor_pause_goal_sec, + uint gc_cost_ratio) : + AdaptiveSizePolicy(init_eden_size, + init_promo_size, + init_survivor_size, + gc_pause_goal_sec, + gc_cost_ratio), + _collection_cost_margin_fraction(AdaptiveSizePolicyCollectionCostMargin/ + 100.0), + _intra_generation_alignment(intra_generation_alignment), + _live_at_last_full_gc(init_promo_size), + _gc_minor_pause_goal_sec(gc_minor_pause_goal_sec), + _latest_major_mutator_interval_seconds(0), + _young_gen_change_for_major_pause_count(0) +{ + // Sizing policy statistics + _avg_major_pause = + new AdaptivePaddedAverage(AdaptiveTimeWeight, PausePadding); + _avg_minor_interval = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_major_interval = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + + _avg_base_footprint = new AdaptiveWeightedAverage(AdaptiveSizePolicyWeight); + _major_pause_old_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + _major_pause_young_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + _major_collection_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + + _young_gen_size_increment_supplement = YoungGenerationSizeSupplement; + _old_gen_size_increment_supplement = TenuredGenerationSizeSupplement; + + // Start the timers + _major_timer.start(); + + _old_gen_policy_is_ready = false; +} + +void PSAdaptiveSizePolicy::major_collection_begin() { + // Update the interval time + _major_timer.stop(); + // Save most recent collection time + _latest_major_mutator_interval_seconds = _major_timer.seconds(); + _major_timer.reset(); + _major_timer.start(); +} + +void PSAdaptiveSizePolicy::update_minor_pause_old_estimator( + double minor_pause_in_ms) { + double promo_size_in_mbytes = ((double)_promo_size)/((double)M); + _minor_pause_old_estimator->update(promo_size_in_mbytes, + minor_pause_in_ms); +} + +void PSAdaptiveSizePolicy::major_collection_end(size_t amount_live, + GCCause::Cause gc_cause) { + // Update the pause time. + _major_timer.stop(); + + if (gc_cause != GCCause::_java_lang_system_gc || + UseAdaptiveSizePolicyWithSystemGC) { + double major_pause_in_seconds = _major_timer.seconds(); + double major_pause_in_ms = major_pause_in_seconds * MILLIUNITS; + + // Sample for performance counter + _avg_major_pause->sample(major_pause_in_seconds); + + // Cost of collection (unit-less) + double collection_cost = 0.0; + if ((_latest_major_mutator_interval_seconds > 0.0) && + (major_pause_in_seconds > 0.0)) { + double interval_in_seconds = + _latest_major_mutator_interval_seconds + major_pause_in_seconds; + collection_cost = + major_pause_in_seconds / interval_in_seconds; + avg_major_gc_cost()->sample(collection_cost); + + // Sample for performance counter + _avg_major_interval->sample(interval_in_seconds); + } + + // Calculate variables used to estimate pause time vs. gen sizes + double eden_size_in_mbytes = ((double)_eden_size)/((double)M); + double promo_size_in_mbytes = ((double)_promo_size)/((double)M); + _major_pause_old_estimator->update(promo_size_in_mbytes, + major_pause_in_ms); + _major_pause_young_estimator->update(eden_size_in_mbytes, + major_pause_in_ms); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print("psAdaptiveSizePolicy::major_collection_end: " + "major gc cost: %f average: %f", collection_cost, + avg_major_gc_cost()->average()); + gclog_or_tty->print_cr(" major pause: %f major period %f", + major_pause_in_ms, + _latest_major_mutator_interval_seconds * MILLIUNITS); + } + + // Calculate variable used to estimate collection cost vs. gen sizes + assert(collection_cost >= 0.0, "Expected to be non-negative"); + _major_collection_estimator->update(promo_size_in_mbytes, + collection_cost); + } + + // Update the amount live at the end of a full GC + _live_at_last_full_gc = amount_live; + + // The policy does not have enough data until at least some major collections + // have been done. + if (_avg_major_pause->count() >= AdaptiveSizePolicyReadyThreshold) { + _old_gen_policy_is_ready = true; + } + + // Interval times use this timer to measure the interval that + // the mutator runs. Reset after the GC pause has been measured. + _major_timer.reset(); + _major_timer.start(); +} + +// If the remaining free space in the old generation is less that +// that expected to be needed by the next collection, do a full +// collection now. +bool PSAdaptiveSizePolicy::should_full_GC(size_t old_free_in_bytes) { + + // A similar test is done in the scavenge's should_attempt_scavenge(). If + // this is changed, decide if that test should also be changed. + bool result = padded_average_promoted_in_bytes() > (float) old_free_in_bytes; + if (PrintGCDetails && Verbose) { + if (result) { + gclog_or_tty->print(" full after scavenge: "); + } else { + gclog_or_tty->print(" no full after scavenge: "); + } + gclog_or_tty->print_cr(" average_promoted " SIZE_FORMAT + " padded_average_promoted " SIZE_FORMAT + " free in old gen " SIZE_FORMAT, + (size_t) average_promoted_in_bytes(), + (size_t) padded_average_promoted_in_bytes(), + old_free_in_bytes); + } + return result; +} + +void PSAdaptiveSizePolicy::clear_generation_free_space_flags() { + + AdaptiveSizePolicy::clear_generation_free_space_flags(); + + set_change_old_gen_for_min_pauses(0); + + set_change_young_gen_for_maj_pauses(0); +} + + +// If this is not a full GC, only test and modify the young generation. + +void PSAdaptiveSizePolicy::compute_generation_free_space(size_t young_live, + size_t eden_live, + size_t old_live, + size_t perm_live, + size_t cur_eden, + size_t max_old_gen_size, + size_t max_eden_size, + bool is_full_gc, + GCCause::Cause gc_cause) { + + // Update statistics + // Time statistics are updated as we go, update footprint stats here + _avg_base_footprint->sample(BaseFootPrintEstimate + perm_live); + avg_young_live()->sample(young_live); + avg_eden_live()->sample(eden_live); + if (is_full_gc) { + // old_live is only accurate after a full gc + avg_old_live()->sample(old_live); + } + + // This code used to return if the policy was not ready , i.e., + // policy_is_ready() returning false. The intent was that + // decisions below needed major collection times and so could + // not be made before two major collections. A consequence was + // adjustments to the young generation were not done until after + // two major collections even if the minor collections times + // exceeded the requested goals. Now let the young generation + // adjust for the minor collection times. Major collection times + // will be zero for the first collection and will naturally be + // ignored. Tenured generation adjustments are only made at the + // full collections so until the second major collection has + // been reached, no tenured generation adjustments will be made. + + // Until we know better, desired promotion size uses the last calculation + size_t desired_promo_size = _promo_size; + + // Start eden at the current value. The desired value that is stored + // in _eden_size is not bounded by constraints of the heap and can + // run away. + // + // As expected setting desired_eden_size to the current + // value of desired_eden_size as a starting point + // caused desired_eden_size to grow way too large and caused + // an overflow down stream. It may have improved performance in + // some case but is dangerous. + size_t desired_eden_size = cur_eden; + +#ifdef ASSERT + size_t original_promo_size = desired_promo_size; + size_t original_eden_size = desired_eden_size; +#endif + + // Cache some values. There's a bit of work getting these, so + // we might save a little time. + const double major_cost = major_gc_cost(); + const double minor_cost = minor_gc_cost(); + + // Used for diagnostics + clear_generation_free_space_flags(); + + // Limits on our growth + size_t promo_limit = (size_t)(max_old_gen_size - avg_old_live()->average()); + + // This method sets the desired eden size. That plus the + // desired survivor space sizes sets the desired young generation + // size. This methods does not know what the desired survivor + // size is but expects that other policy will attempt to make + // the survivor sizes compatible with the live data in the + // young generation. This limit is an estimate of the space left + // in the young generation after the survivor spaces have been + // subtracted out. + size_t eden_limit = max_eden_size; + + // But don't force a promo size below the current promo size. Otherwise, + // the promo size will shrink for no good reason. + promo_limit = MAX2(promo_limit, _promo_size); + + const double gc_cost_limit = GCTimeLimit/100.0; + + // Which way should we go? + // if pause requirement is not met + // adjust size of any generation with average paus exceeding + // the pause limit. Adjust one pause at a time (the larger) + // and only make adjustments for the major pause at full collections. + // else if throughput requirement not met + // adjust the size of the generation with larger gc time. Only + // adjust one generation at a time. + // else + // adjust down the total heap size. Adjust down the larger of the + // generations. + + // Add some checks for a threshhold for a change. For example, + // a change less than the necessary alignment is probably not worth + // attempting. + + + if ((_avg_minor_pause->padded_average() > gc_pause_goal_sec()) || + (_avg_major_pause->padded_average() > gc_pause_goal_sec())) { + // + // Check pauses + // + // Make changes only to affect one of the pauses (the larger) + // at a time. + adjust_for_pause_time(is_full_gc, &desired_promo_size, &desired_eden_size); + + } else if (_avg_minor_pause->padded_average() > gc_minor_pause_goal_sec()) { + // Adjust only for the minor pause time goal + adjust_for_minor_pause_time(is_full_gc, &desired_promo_size, &desired_eden_size); + + } else if(adjusted_mutator_cost() < _throughput_goal) { + // This branch used to require that (mutator_cost() > 0.0 in 1.4.2. + // This sometimes resulted in skipping to the minimize footprint + // code. Change this to try and reduce GC time if mutator time is + // negative for whatever reason. Or for future consideration, + // bail out of the code if mutator time is negative. + // + // Throughput + // + assert(major_cost >= 0.0, "major cost is < 0.0"); + assert(minor_cost >= 0.0, "minor cost is < 0.0"); + // Try to reduce the GC times. + adjust_for_throughput(is_full_gc, &desired_promo_size, &desired_eden_size); + + } else { + + // Be conservative about reducing the footprint. + // Do a minimum number of major collections first. + // Have reasonable averages for major and minor collections costs. + if (UseAdaptiveSizePolicyFootprintGoal && + young_gen_policy_is_ready() && + avg_major_gc_cost()->average() >= 0.0 && + avg_minor_gc_cost()->average() >= 0.0) { + size_t desired_sum = desired_eden_size + desired_promo_size; + desired_eden_size = adjust_eden_for_footprint(desired_eden_size, + desired_sum); + if (is_full_gc) { + set_decide_at_full_gc(decide_at_full_gc_true); + desired_promo_size = adjust_promo_for_footprint(desired_promo_size, + desired_sum); + } + } + } + + // Note we make the same tests as in the code block below; the code + // seems a little easier to read with the printing in another block. + if (PrintAdaptiveSizePolicy) { + if (desired_promo_size > promo_limit) { + // "free_in_old_gen" was the original value for used for promo_limit + size_t free_in_old_gen = (size_t)(max_old_gen_size - avg_old_live()->average()); + gclog_or_tty->print_cr( + "PSAdaptiveSizePolicy::compute_generation_free_space limits:" + " desired_promo_size: " SIZE_FORMAT + " promo_limit: " SIZE_FORMAT + " free_in_old_gen: " SIZE_FORMAT + " max_old_gen_size: " SIZE_FORMAT + " avg_old_live: " SIZE_FORMAT, + desired_promo_size, promo_limit, free_in_old_gen, + max_old_gen_size, (size_t) avg_old_live()->average()); + } + if (desired_eden_size > eden_limit) { + gclog_or_tty->print_cr( + "AdaptiveSizePolicy::compute_generation_free_space limits:" + " desired_eden_size: " SIZE_FORMAT + " old_eden_size: " SIZE_FORMAT + " eden_limit: " SIZE_FORMAT + " cur_eden: " SIZE_FORMAT + " max_eden_size: " SIZE_FORMAT + " avg_young_live: " SIZE_FORMAT, + desired_eden_size, _eden_size, eden_limit, cur_eden, + max_eden_size, (size_t)avg_young_live()->average()); + } + if (gc_cost() > gc_cost_limit) { + gclog_or_tty->print_cr( + "AdaptiveSizePolicy::compute_generation_free_space: gc time limit" + " gc_cost: %f " + " GCTimeLimit: %d", + gc_cost(), GCTimeLimit); + } + } + + // Align everything and make a final limit check + const size_t alignment = _intra_generation_alignment; + desired_eden_size = align_size_up(desired_eden_size, alignment); + desired_eden_size = MAX2(desired_eden_size, alignment); + desired_promo_size = align_size_up(desired_promo_size, alignment); + desired_promo_size = MAX2(desired_promo_size, alignment); + + eden_limit = align_size_down(eden_limit, alignment); + promo_limit = align_size_down(promo_limit, alignment); + + // Is too much time being spent in GC? + // Is the heap trying to grow beyond it's limits? + + const size_t free_in_old_gen = (size_t)(max_old_gen_size - avg_old_live()->average()); + if (desired_promo_size > free_in_old_gen && desired_eden_size > eden_limit) { + + // eden_limit is the upper limit on the size of eden based on + // the maximum size of the young generation and the sizes + // of the survivor space. + // The question being asked is whether the gc costs are high + // and the space being recovered by a collection is low. + // free_in_young_gen is the free space in the young generation + // after a collection and promo_live is the free space in the old + // generation after a collection. + // + // Use the minimum of the current value of the live in the + // young gen or the average of the live in the young gen. + // If the current value drops quickly, that should be taken + // into account (i.e., don't trigger if the amount of free + // space has suddenly jumped up). If the current is much + // higher than the average, use the average since it represents + // the longer term behavor. + const size_t live_in_eden = MIN2(eden_live, (size_t) avg_eden_live()->average()); + const size_t free_in_eden = eden_limit > live_in_eden ? + eden_limit - live_in_eden : 0; + const size_t total_free_limit = free_in_old_gen + free_in_eden; + const size_t total_mem = max_old_gen_size + max_eden_size; + const double mem_free_limit = total_mem * (GCHeapFreeLimit/100.0); + if (PrintAdaptiveSizePolicy && (Verbose || + (total_free_limit < (size_t) mem_free_limit))) { + gclog_or_tty->print_cr( + "PSAdaptiveSizePolicy::compute_generation_free_space limits:" + " promo_limit: " SIZE_FORMAT + " eden_limit: " SIZE_FORMAT + " total_free_limit: " SIZE_FORMAT + " max_old_gen_size: " SIZE_FORMAT + " max_eden_size: " SIZE_FORMAT + " mem_free_limit: " SIZE_FORMAT, + promo_limit, eden_limit, total_free_limit, + max_old_gen_size, max_eden_size, + (size_t) mem_free_limit); + } + + if (is_full_gc) { + if (gc_cost() > gc_cost_limit && + total_free_limit < (size_t) mem_free_limit) { + // Collections, on average, are taking too much time, and + // gc_cost() > gc_cost_limit + // we have too little space available after a full gc. + // total_free_limit < mem_free_limit + // where + // total_free_limit is the free space available in + // both generations + // total_mem is the total space available for allocation + // in both generations (survivor spaces are not included + // just as they are not included in eden_limit). + // mem_free_limit is a fraction of total_mem judged to be an + // acceptable amount that is still unused. + // The heap can ask for the value of this variable when deciding + // whether to thrown an OutOfMemory error. + // Note that the gc time limit test only works for the collections + // of the young gen + tenured gen and not for collections of the + // permanent gen. That is because the calculation of the space + // freed by the collection is the free space in the young gen + + // tenured gen. + // Ignore explicit GC's. Ignoring explicit GC's at this level + // is the equivalent of the GC did not happen as far as the + // overhead calculation is concerted (i.e., the flag is not set + // and the count is not affected). Also the average will not + // have been updated unless UseAdaptiveSizePolicyWithSystemGC is on. + if (!GCCause::is_user_requested_gc(gc_cause) && + !GCCause::is_serviceability_requested_gc(gc_cause)) { + inc_gc_time_limit_count(); + if (UseGCOverheadLimit && + (gc_time_limit_count() > AdaptiveSizePolicyGCTimeLimitThreshold)){ + // All conditions have been met for throwing an out-of-memory + _gc_time_limit_exceeded = true; + // Avoid consecutive OOM due to the gc time limit by resetting + // the counter. + reset_gc_time_limit_count(); + } + _print_gc_time_limit_would_be_exceeded = true; + } + } else { + // Did not exceed overhead limits + reset_gc_time_limit_count(); + } + } + } + + + // And one last limit check, now that we've aligned things. + if (desired_eden_size > eden_limit) { + // If the policy says to get a larger eden but + // is hitting the limit, don't decrease eden. + // This can lead to a general drifting down of the + // eden size. Let the tenuring calculation push more + // into the old gen. + desired_eden_size = MAX2(eden_limit, cur_eden); + } + desired_promo_size = MIN2(desired_promo_size, promo_limit); + + + if (PrintAdaptiveSizePolicy) { + // Timing stats + gclog_or_tty->print( + "PSAdaptiveSizePolicy::compute_generation_free_space: costs" + " minor_time: %f" + " major_cost: %f" + " mutator_cost: %f" + " throughput_goal: %f", + minor_gc_cost(), major_gc_cost(), mutator_cost(), + _throughput_goal); + + // We give more details if Verbose is set + if (Verbose) { + gclog_or_tty->print( " minor_pause: %f" + " major_pause: %f" + " minor_interval: %f" + " major_interval: %f" + " pause_goal: %f", + _avg_minor_pause->padded_average(), + _avg_major_pause->padded_average(), + _avg_minor_interval->average(), + _avg_major_interval->average(), + gc_pause_goal_sec()); + } + + // Footprint stats + gclog_or_tty->print( " live_space: " SIZE_FORMAT + " free_space: " SIZE_FORMAT, + live_space(), free_space()); + // More detail + if (Verbose) { + gclog_or_tty->print( " base_footprint: " SIZE_FORMAT + " avg_young_live: " SIZE_FORMAT + " avg_old_live: " SIZE_FORMAT, + (size_t)_avg_base_footprint->average(), + (size_t)avg_young_live()->average(), + (size_t)avg_old_live()->average()); + } + + // And finally, our old and new sizes. + gclog_or_tty->print(" old_promo_size: " SIZE_FORMAT + " old_eden_size: " SIZE_FORMAT + " desired_promo_size: " SIZE_FORMAT + " desired_eden_size: " SIZE_FORMAT, + _promo_size, _eden_size, + desired_promo_size, desired_eden_size); + gclog_or_tty->cr(); + } + + decay_supplemental_growth(is_full_gc); + + set_promo_size(desired_promo_size); + set_eden_size(desired_eden_size); +}; + +void PSAdaptiveSizePolicy::decay_supplemental_growth(bool is_full_gc) { + // Decay the supplemental increment? Decay the supplement growth + // factor even if it is not used. It is only meant to give a boost + // to the initial growth and if it is not used, then it was not + // needed. + if (is_full_gc) { + // Don't wait for the threshold value for the major collections. If + // here, the supplemental growth term was used and should decay. + if ((_avg_major_pause->count() % TenuredGenerationSizeSupplementDecay) + == 0) { + _old_gen_size_increment_supplement = + _old_gen_size_increment_supplement >> 1; + } + } else { + if ((_avg_minor_pause->count() >= AdaptiveSizePolicyReadyThreshold) && + (_avg_minor_pause->count() % YoungGenerationSizeSupplementDecay) == 0) { + _young_gen_size_increment_supplement = + _young_gen_size_increment_supplement >> 1; + } + } +} + +void PSAdaptiveSizePolicy::adjust_for_minor_pause_time(bool is_full_gc, + size_t* desired_promo_size_ptr, size_t* desired_eden_size_ptr) { + + // Adjust the young generation size to reduce pause time of + // of collections. + // + // The AdaptiveSizePolicyInitializingSteps test is not used + // here. It has not seemed to be needed but perhaps should + // be added for consistency. + if (minor_pause_young_estimator()->decrement_will_decrease()) { + // reduce eden size + set_change_young_gen_for_min_pauses( + decrease_young_gen_for_min_pauses_true); + *desired_eden_size_ptr = *desired_eden_size_ptr - + eden_decrement_aligned_down(*desired_eden_size_ptr); + } else { + // EXPERIMENTAL ADJUSTMENT + // Only record that the estimator indicated such an action. + // *desired_eden_size_ptr = *desired_eden_size_ptr + eden_heap_delta; + set_change_young_gen_for_min_pauses( + increase_young_gen_for_min_pauses_true); + } + if (PSAdjustTenuredGenForMinorPause) { + // If the desired eden size is as small as it will get, + // try to adjust the old gen size. + if (*desired_eden_size_ptr <= _intra_generation_alignment) { + // Vary the old gen size to reduce the young gen pause. This + // may not be a good idea. This is just a test. + if (minor_pause_old_estimator()->decrement_will_decrease()) { + set_change_old_gen_for_min_pauses( + decrease_old_gen_for_min_pauses_true); + *desired_promo_size_ptr = + _promo_size - promo_decrement_aligned_down(*desired_promo_size_ptr); + } else { + set_change_old_gen_for_min_pauses( + increase_old_gen_for_min_pauses_true); + size_t promo_heap_delta = + promo_increment_with_supplement_aligned_up(*desired_promo_size_ptr); + if ((*desired_promo_size_ptr + promo_heap_delta) > + *desired_promo_size_ptr) { + *desired_promo_size_ptr = + _promo_size + promo_heap_delta; + } + } + } + } +} + +void PSAdaptiveSizePolicy::adjust_for_pause_time(bool is_full_gc, + size_t* desired_promo_size_ptr, + size_t* desired_eden_size_ptr) { + + size_t promo_heap_delta = 0; + size_t eden_heap_delta = 0; + // Add some checks for a threshhold for a change. For example, + // a change less than the required alignment is probably not worth + // attempting. + if (is_full_gc) { + set_decide_at_full_gc(decide_at_full_gc_true); + } + + if (_avg_minor_pause->padded_average() > _avg_major_pause->padded_average()) { + adjust_for_minor_pause_time(is_full_gc, + desired_promo_size_ptr, + desired_eden_size_ptr); + // major pause adjustments + } else if (is_full_gc) { + // Adjust for the major pause time only at full gc's because the + // affects of a change can only be seen at full gc's. + + // Reduce old generation size to reduce pause? + if (major_pause_old_estimator()->decrement_will_decrease()) { + // reduce old generation size + set_change_old_gen_for_maj_pauses(decrease_old_gen_for_maj_pauses_true); + promo_heap_delta = promo_decrement_aligned_down(*desired_promo_size_ptr); + *desired_promo_size_ptr = _promo_size - promo_heap_delta; + } else { + // EXPERIMENTAL ADJUSTMENT + // Only record that the estimator indicated such an action. + // *desired_promo_size_ptr = _promo_size + + // promo_increment_aligned_up(*desired_promo_size_ptr); + set_change_old_gen_for_maj_pauses(increase_old_gen_for_maj_pauses_true); + } + if (PSAdjustYoungGenForMajorPause) { + // If the promo size is at the minimum (i.e., the old gen + // size will not actually decrease), consider changing the + // young gen size. + if (*desired_promo_size_ptr < _intra_generation_alignment) { + // If increasing the young generation will decrease the old gen + // pause, do it. + // During startup there is noise in the statistics for deciding + // on whether to increase or decrease the young gen size. For + // some number of iterations, just try to increase the young + // gen size if the major pause is too long to try and establish + // good statistics for later decisions. + if (major_pause_young_estimator()->increment_will_decrease() || + (_young_gen_change_for_major_pause_count + <= AdaptiveSizePolicyInitializingSteps)) { + set_change_young_gen_for_maj_pauses( + increase_young_gen_for_maj_pauses_true); + eden_heap_delta = eden_increment_aligned_up(*desired_eden_size_ptr); + *desired_eden_size_ptr = _eden_size + eden_heap_delta; + _young_gen_change_for_major_pause_count++; + } else { + // Record that decreasing the young gen size would decrease + // the major pause + set_change_young_gen_for_maj_pauses( + decrease_young_gen_for_maj_pauses_true); + eden_heap_delta = eden_decrement_aligned_down(*desired_eden_size_ptr); + *desired_eden_size_ptr = _eden_size - eden_heap_delta; + } + } + } + } + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "AdaptiveSizePolicy::compute_generation_free_space " + "adjusting gen sizes for major pause (avg %f goal %f). " + "desired_promo_size " SIZE_FORMAT "desired_eden_size " + SIZE_FORMAT + " promo delta " SIZE_FORMAT " eden delta " SIZE_FORMAT, + _avg_major_pause->average(), gc_pause_goal_sec(), + *desired_promo_size_ptr, *desired_eden_size_ptr, + promo_heap_delta, eden_heap_delta); + } +} + +void PSAdaptiveSizePolicy::adjust_for_throughput(bool is_full_gc, + size_t* desired_promo_size_ptr, + size_t* desired_eden_size_ptr) { + + // Add some checks for a threshhold for a change. For example, + // a change less than the required alignment is probably not worth + // attempting. + if (is_full_gc) { + set_decide_at_full_gc(decide_at_full_gc_true); + } + + if ((gc_cost() + mutator_cost()) == 0.0) { + return; + } + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print("\nPSAdaptiveSizePolicy::adjust_for_throughput(" + "is_full: %d, promo: " SIZE_FORMAT ", cur_eden: " SIZE_FORMAT "): ", + is_full_gc, *desired_promo_size_ptr, *desired_eden_size_ptr); + gclog_or_tty->print_cr("mutator_cost %f major_gc_cost %f " + "minor_gc_cost %f", mutator_cost(), major_gc_cost(), minor_gc_cost()); + } + + // Tenured generation + if (is_full_gc) { + + // Calculate the change to use for the tenured gen. + size_t scaled_promo_heap_delta = 0; + // Can the increment to the generation be scaled? + if (gc_cost() >= 0.0 && major_gc_cost() >= 0.0) { + size_t promo_heap_delta = + promo_increment_with_supplement_aligned_up(*desired_promo_size_ptr); + double scale_by_ratio = major_gc_cost() / gc_cost(); + scaled_promo_heap_delta = + (size_t) (scale_by_ratio * (double) promo_heap_delta); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "Scaled tenured increment: " SIZE_FORMAT " by %f down to " + SIZE_FORMAT, + promo_heap_delta, scale_by_ratio, scaled_promo_heap_delta); + } + } else if (major_gc_cost() >= 0.0) { + // Scaling is not going to work. If the major gc time is the + // larger, give it a full increment. + if (major_gc_cost() >= minor_gc_cost()) { + scaled_promo_heap_delta = + promo_increment_with_supplement_aligned_up(*desired_promo_size_ptr); + } + } else { + // Don't expect to get here but it's ok if it does + // in the product build since the delta will be 0 + // and nothing will change. + assert(false, "Unexpected value for gc costs"); + } + + switch (AdaptiveSizeThroughPutPolicy) { + case 1: + // Early in the run the statistics might not be good. Until + // a specific number of collections have been, use the heuristic + // that a larger generation size means lower collection costs. + if (major_collection_estimator()->increment_will_decrease() || + (_old_gen_change_for_major_throughput + <= AdaptiveSizePolicyInitializingSteps)) { + // Increase tenured generation size to reduce major collection cost + if ((*desired_promo_size_ptr + scaled_promo_heap_delta) > + *desired_promo_size_ptr) { + *desired_promo_size_ptr = _promo_size + scaled_promo_heap_delta; + } + set_change_old_gen_for_throughput( + increase_old_gen_for_throughput_true); + _old_gen_change_for_major_throughput++; + } else { + // EXPERIMENTAL ADJUSTMENT + // Record that decreasing the old gen size would decrease + // the major collection cost but don't do it. + // *desired_promo_size_ptr = _promo_size - + // promo_decrement_aligned_down(*desired_promo_size_ptr); + set_change_old_gen_for_throughput( + decrease_old_gen_for_throughput_true); + } + + break; + default: + // Simplest strategy + if ((*desired_promo_size_ptr + scaled_promo_heap_delta) > + *desired_promo_size_ptr) { + *desired_promo_size_ptr = *desired_promo_size_ptr + + scaled_promo_heap_delta; + } + set_change_old_gen_for_throughput( + increase_old_gen_for_throughput_true); + _old_gen_change_for_major_throughput++; + } + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "adjusting tenured gen for throughput (avg %f goal %f). " + "desired_promo_size " SIZE_FORMAT " promo_delta " SIZE_FORMAT , + mutator_cost(), _throughput_goal, + *desired_promo_size_ptr, scaled_promo_heap_delta); + } + } + + // Young generation + size_t scaled_eden_heap_delta = 0; + // Can the increment to the generation be scaled? + if (gc_cost() >= 0.0 && minor_gc_cost() >= 0.0) { + size_t eden_heap_delta = + eden_increment_with_supplement_aligned_up(*desired_eden_size_ptr); + double scale_by_ratio = minor_gc_cost() / gc_cost(); + assert(scale_by_ratio <= 1.0 && scale_by_ratio >= 0.0, "Scaling is wrong"); + scaled_eden_heap_delta = + (size_t) (scale_by_ratio * (double) eden_heap_delta); + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "Scaled eden increment: " SIZE_FORMAT " by %f down to " + SIZE_FORMAT, + eden_heap_delta, scale_by_ratio, scaled_eden_heap_delta); + } + } else if (minor_gc_cost() >= 0.0) { + // Scaling is not going to work. If the minor gc time is the + // larger, give it a full increment. + if (minor_gc_cost() > major_gc_cost()) { + scaled_eden_heap_delta = + eden_increment_with_supplement_aligned_up(*desired_eden_size_ptr); + } + } else { + // Don't expect to get here but it's ok if it does + // in the product build since the delta will be 0 + // and nothing will change. + assert(false, "Unexpected value for gc costs"); + } + + // Use a heuristic for some number of collections to give + // the averages time to settle down. + switch (AdaptiveSizeThroughPutPolicy) { + case 1: + if (minor_collection_estimator()->increment_will_decrease() || + (_young_gen_change_for_minor_throughput + <= AdaptiveSizePolicyInitializingSteps)) { + // Expand young generation size to reduce frequency of + // of collections. + if ((*desired_eden_size_ptr + scaled_eden_heap_delta) > + *desired_eden_size_ptr) { + *desired_eden_size_ptr = + *desired_eden_size_ptr + scaled_eden_heap_delta; + } + set_change_young_gen_for_throughput( + increase_young_gen_for_througput_true); + _young_gen_change_for_minor_throughput++; + } else { + // EXPERIMENTAL ADJUSTMENT + // Record that decreasing the young gen size would decrease + // the minor collection cost but don't do it. + // *desired_eden_size_ptr = _eden_size - + // eden_decrement_aligned_down(*desired_eden_size_ptr); + set_change_young_gen_for_throughput( + decrease_young_gen_for_througput_true); + } + break; + default: + if ((*desired_eden_size_ptr + scaled_eden_heap_delta) > + *desired_eden_size_ptr) { + *desired_eden_size_ptr = + *desired_eden_size_ptr + scaled_eden_heap_delta; + } + set_change_young_gen_for_throughput( + increase_young_gen_for_througput_true); + _young_gen_change_for_minor_throughput++; + } + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "adjusting eden for throughput (avg %f goal %f). desired_eden_size " + SIZE_FORMAT " eden delta " SIZE_FORMAT "\n", + mutator_cost(), _throughput_goal, + *desired_eden_size_ptr, scaled_eden_heap_delta); + } +} + +size_t PSAdaptiveSizePolicy::adjust_promo_for_footprint( + size_t desired_promo_size, size_t desired_sum) { + assert(desired_promo_size <= desired_sum, "Inconsistent parameters"); + set_decrease_for_footprint(decrease_old_gen_for_footprint_true); + + size_t change = promo_decrement(desired_promo_size); + change = scale_down(change, desired_promo_size, desired_sum); + + size_t reduced_size = desired_promo_size - change; + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "AdaptiveSizePolicy::compute_generation_free_space " + "adjusting tenured gen for footprint. " + "starting promo size " SIZE_FORMAT + " reduced promo size " SIZE_FORMAT, + " promo delta " SIZE_FORMAT, + desired_promo_size, reduced_size, change ); + } + + assert(reduced_size <= desired_promo_size, "Inconsistent result"); + return reduced_size; +} + +size_t PSAdaptiveSizePolicy::adjust_eden_for_footprint( + size_t desired_eden_size, size_t desired_sum) { + assert(desired_eden_size <= desired_sum, "Inconsistent parameters"); + set_decrease_for_footprint(decrease_young_gen_for_footprint_true); + + size_t change = eden_decrement(desired_eden_size); + change = scale_down(change, desired_eden_size, desired_sum); + + size_t reduced_size = desired_eden_size - change; + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr( + "AdaptiveSizePolicy::compute_generation_free_space " + "adjusting eden for footprint. " + " starting eden size " SIZE_FORMAT + " reduced eden size " SIZE_FORMAT + " eden delta " SIZE_FORMAT, + desired_eden_size, reduced_size, change); + } + + assert(reduced_size <= desired_eden_size, "Inconsistent result"); + return reduced_size; +} + +// Scale down "change" by the factor +// part / total +// Don't align the results. + +size_t PSAdaptiveSizePolicy::scale_down(size_t change, + double part, + double total) { + assert(part <= total, "Inconsistent input"); + size_t reduced_change = change; + if (total > 0) { + double fraction = part / total; + reduced_change = (size_t) (fraction * (double) change); + } + assert(reduced_change <= change, "Inconsistent result"); + return reduced_change; +} + +size_t PSAdaptiveSizePolicy::eden_increment(size_t cur_eden, + uint percent_change) { + size_t eden_heap_delta; + eden_heap_delta = cur_eden / 100 * percent_change; + return eden_heap_delta; +} + +size_t PSAdaptiveSizePolicy::eden_increment(size_t cur_eden) { + return eden_increment(cur_eden, YoungGenerationSizeIncrement); +} + +size_t PSAdaptiveSizePolicy::eden_increment_aligned_up(size_t cur_eden) { + size_t result = eden_increment(cur_eden, YoungGenerationSizeIncrement); + return align_size_up(result, _intra_generation_alignment); +} + +size_t PSAdaptiveSizePolicy::eden_increment_aligned_down(size_t cur_eden) { + size_t result = eden_increment(cur_eden); + return align_size_down(result, _intra_generation_alignment); +} + +size_t PSAdaptiveSizePolicy::eden_increment_with_supplement_aligned_up( + size_t cur_eden) { + size_t result = eden_increment(cur_eden, + YoungGenerationSizeIncrement + _young_gen_size_increment_supplement); + return align_size_up(result, _intra_generation_alignment); +} + +size_t PSAdaptiveSizePolicy::eden_decrement_aligned_down(size_t cur_eden) { + size_t eden_heap_delta = eden_decrement(cur_eden); + return align_size_down(eden_heap_delta, _intra_generation_alignment); +} + +size_t PSAdaptiveSizePolicy::eden_decrement(size_t cur_eden) { + size_t eden_heap_delta = eden_increment(cur_eden) / + AdaptiveSizeDecrementScaleFactor; + return eden_heap_delta; +} + +size_t PSAdaptiveSizePolicy::promo_increment(size_t cur_promo, + uint percent_change) { + size_t promo_heap_delta; + promo_heap_delta = cur_promo / 100 * percent_change; + return promo_heap_delta; +} + +size_t PSAdaptiveSizePolicy::promo_increment(size_t cur_promo) { + return promo_increment(cur_promo, TenuredGenerationSizeIncrement); +} + +size_t PSAdaptiveSizePolicy::promo_increment_aligned_up(size_t cur_promo) { + size_t result = promo_increment(cur_promo, TenuredGenerationSizeIncrement); + return align_size_up(result, _intra_generation_alignment); +} + +size_t PSAdaptiveSizePolicy::promo_increment_aligned_down(size_t cur_promo) { + size_t result = promo_increment(cur_promo, TenuredGenerationSizeIncrement); + return align_size_down(result, _intra_generation_alignment); +} + +size_t PSAdaptiveSizePolicy::promo_increment_with_supplement_aligned_up( + size_t cur_promo) { + size_t result = promo_increment(cur_promo, + TenuredGenerationSizeIncrement + _old_gen_size_increment_supplement); + return align_size_up(result, _intra_generation_alignment); +} + +size_t PSAdaptiveSizePolicy::promo_decrement_aligned_down(size_t cur_promo) { + size_t promo_heap_delta = promo_decrement(cur_promo); + return align_size_down(promo_heap_delta, _intra_generation_alignment); +} + +size_t PSAdaptiveSizePolicy::promo_decrement(size_t cur_promo) { + size_t promo_heap_delta = promo_increment(cur_promo); + promo_heap_delta = promo_heap_delta / AdaptiveSizeDecrementScaleFactor; + return promo_heap_delta; +} + +int PSAdaptiveSizePolicy::compute_survivor_space_size_and_threshold( + bool is_survivor_overflow, + int tenuring_threshold, + size_t survivor_limit) { + assert(survivor_limit >= _intra_generation_alignment, + "survivor_limit too small"); + assert((size_t)align_size_down(survivor_limit, _intra_generation_alignment) + == survivor_limit, "survivor_limit not aligned"); + + // This method is called even if the tenuring threshold and survivor + // spaces are not adjusted so that the averages are sampled above. + if (!UsePSAdaptiveSurvivorSizePolicy || + !young_gen_policy_is_ready()) { + return tenuring_threshold; + } + + // We'll decide whether to increase or decrease the tenuring + // threshold based partly on the newly computed survivor size + // (if we hit the maximum limit allowed, we'll always choose to + // decrement the threshold). + bool incr_tenuring_threshold = false; + bool decr_tenuring_threshold = false; + + set_decrement_tenuring_threshold_for_gc_cost(false); + set_increment_tenuring_threshold_for_gc_cost(false); + set_decrement_tenuring_threshold_for_survivor_limit(false); + + if (!is_survivor_overflow) { + // Keep running averages on how much survived + + // We use the tenuring threshold to equalize the cost of major + // and minor collections. + // ThresholdTolerance is used to indicate how sensitive the + // tenuring threshold is to differences in cost betweent the + // collection types. + + // Get the times of interest. This involves a little work, so + // we cache the values here. + const double major_cost = major_gc_cost(); + const double minor_cost = minor_gc_cost(); + + if (minor_cost > major_cost * _threshold_tolerance_percent) { + // Minor times are getting too long; lower the threshold so + // less survives and more is promoted. + decr_tenuring_threshold = true; + set_decrement_tenuring_threshold_for_gc_cost(true); + } else if (major_cost > minor_cost * _threshold_tolerance_percent) { + // Major times are too long, so we want less promotion. + incr_tenuring_threshold = true; + set_increment_tenuring_threshold_for_gc_cost(true); + } + + } else { + // Survivor space overflow occurred, so promoted and survived are + // not accurate. We'll make our best guess by combining survived + // and promoted and count them as survivors. + // + // We'll lower the tenuring threshold to see if we can correct + // things. Also, set the survivor size conservatively. We're + // trying to avoid many overflows from occurring if defnew size + // is just too small. + + decr_tenuring_threshold = true; + } + + // The padded average also maintains a deviation from the average; + // we use this to see how good of an estimate we have of what survived. + // We're trying to pad the survivor size as little as possible without + // overflowing the survivor spaces. + size_t target_size = align_size_up((size_t)_avg_survived->padded_average(), + _intra_generation_alignment); + target_size = MAX2(target_size, _intra_generation_alignment); + + if (target_size > survivor_limit) { + // Target size is bigger than we can handle. Let's also reduce + // the tenuring threshold. + target_size = survivor_limit; + decr_tenuring_threshold = true; + set_decrement_tenuring_threshold_for_survivor_limit(true); + } + + // Finally, increment or decrement the tenuring threshold, as decided above. + // We test for decrementing first, as we might have hit the target size + // limit. + if (decr_tenuring_threshold && !(AlwaysTenure || NeverTenure)) { + if (tenuring_threshold > 1) { + tenuring_threshold--; + } + } else if (incr_tenuring_threshold && !(AlwaysTenure || NeverTenure)) { + if (tenuring_threshold < MaxTenuringThreshold) { + tenuring_threshold++; + } + } + + // We keep a running average of the amount promoted which is used + // to decide when we should collect the old generation (when + // the amount of old gen free space is less than what we expect to + // promote). + + if (PrintAdaptiveSizePolicy) { + // A little more detail if Verbose is on + if (Verbose) { + gclog_or_tty->print( " avg_survived: %f" + " avg_deviation: %f", + _avg_survived->average(), + _avg_survived->deviation()); + } + + gclog_or_tty->print( " avg_survived_padded_avg: %f", + _avg_survived->padded_average()); + + if (Verbose) { + gclog_or_tty->print( " avg_promoted_avg: %f" + " avg_promoted_dev: %f", + avg_promoted()->average(), + avg_promoted()->deviation()); + } + + gclog_or_tty->print( " avg_promoted_padded_avg: %f" + " avg_pretenured_padded_avg: %f" + " tenuring_thresh: %d" + " target_size: " SIZE_FORMAT, + avg_promoted()->padded_average(), + _avg_pretenured->padded_average(), + tenuring_threshold, target_size); + tty->cr(); + } + + set_survivor_size(target_size); + + return tenuring_threshold; +} + +void PSAdaptiveSizePolicy::update_averages(bool is_survivor_overflow, + size_t survived, + size_t promoted) { + // Update averages + if (!is_survivor_overflow) { + // Keep running averages on how much survived + _avg_survived->sample(survived); + } else { + size_t survived_guess = survived + promoted; + _avg_survived->sample(survived_guess); + } + avg_promoted()->sample(promoted + _avg_pretenured->padded_average()); + + if (PrintAdaptiveSizePolicy) { + gclog_or_tty->print( + "AdaptiveSizePolicy::compute_survivor_space_size_and_thresh:" + " survived: " SIZE_FORMAT + " promoted: " SIZE_FORMAT + " overflow: %s", + survived, promoted, is_survivor_overflow ? "true" : "false"); + } +} + +bool PSAdaptiveSizePolicy::print_adaptive_size_policy_on(outputStream* st) + const { + + if (!UseAdaptiveSizePolicy) return false; + + return AdaptiveSizePolicy::print_adaptive_size_policy_on( + st, + PSScavenge::tenuring_threshold()); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp new file mode 100644 index 00000000000..d6e002e3d9e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp @@ -0,0 +1,384 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This class keeps statistical information and computes the +// optimal free space for both the young and old generation +// based on current application characteristics (based on gc cost +// and application footprint). +// +// It also computes an optimal tenuring threshold between the young +// and old generations, so as to equalize the cost of collections +// of those generations, as well as optimial survivor space sizes +// for the young generation. +// +// While this class is specifically intended for a generational system +// consisting of a young gen (containing an Eden and two semi-spaces) +// and a tenured gen, as well as a perm gen for reflective data, it +// makes NO references to specific generations. +// +// 05/02/2003 Update +// The 1.5 policy makes use of data gathered for the costs of GC on +// specific generations. That data does reference specific +// generation. Also diagnostics specific to generations have +// been added. + +// Forward decls +class elapsedTimer; + +class PSAdaptiveSizePolicy : public AdaptiveSizePolicy { + friend class PSGCAdaptivePolicyCounters; + private: + // These values are used to record decisions made during the + // policy. For example, if the young generation was decreased + // to decrease the GC cost of minor collections the value + // decrease_young_gen_for_throughput_true is used. + + // Last calculated sizes, in bytes, and aligned + // NEEDS_CLEANUP should use sizes.hpp, but it works in ints, not size_t's + + // Time statistics + AdaptivePaddedAverage* _avg_major_pause; + + // Footprint statistics + AdaptiveWeightedAverage* _avg_base_footprint; + + // Statistical data gathered for GC + GCStats _gc_stats; + + size_t _survivor_size_limit; // Limit in bytes of survivor size + const double _collection_cost_margin_fraction; + + // Variable for estimating the major and minor pause times. + // These variables represent linear least-squares fits of + // the data. + // major pause time vs. old gen size + LinearLeastSquareFit* _major_pause_old_estimator; + // major pause time vs. young gen size + LinearLeastSquareFit* _major_pause_young_estimator; + + + // These record the most recent collection times. They + // are available as an alternative to using the averages + // for making ergonomic decisions. + double _latest_major_mutator_interval_seconds; + + const size_t _intra_generation_alignment; // alignment for eden, survivors + + const double _gc_minor_pause_goal_sec; // goal for maximum minor gc pause + + // The amount of live data in the heap at the last full GC, used + // as a baseline to help us determine when we need to perform the + // next full GC. + size_t _live_at_last_full_gc; + + // decrease/increase the old generation for minor pause time + int _change_old_gen_for_min_pauses; + + // increase/decrease the young generation for major pause time + int _change_young_gen_for_maj_pauses; + + + // Flag indicating that the adaptive policy is ready to use + bool _old_gen_policy_is_ready; + + // Changing the generation sizing depends on the data that is + // gathered about the effects of changes on the pause times and + // throughput. These variable count the number of data points + // gathered. The policy may use these counters as a threshhold + // for reliable data. + julong _young_gen_change_for_major_pause_count; + + // To facilitate faster growth at start up, supplement the normal + // growth percentage for the young gen eden and the + // old gen space for promotion with these value which decay + // with increasing collections. + uint _young_gen_size_increment_supplement; + uint _old_gen_size_increment_supplement; + + // The number of bytes absorbed from eden into the old gen by moving the + // boundary over live data. + size_t _bytes_absorbed_from_eden; + + private: + + // Accessors + AdaptivePaddedAverage* avg_major_pause() const { return _avg_major_pause; } + double gc_minor_pause_goal_sec() const { return _gc_minor_pause_goal_sec; } + + // Change the young generation size to achieve a minor GC pause time goal + void adjust_for_minor_pause_time(bool is_full_gc, + size_t* desired_promo_size_ptr, + size_t* desired_eden_size_ptr); + // Change the generation sizes to achieve a GC pause time goal + // Returned sizes are not necessarily aligned. + void adjust_for_pause_time(bool is_full_gc, + size_t* desired_promo_size_ptr, + size_t* desired_eden_size_ptr); + // Change the generation sizes to achieve an application throughput goal + // Returned sizes are not necessarily aligned. + void adjust_for_throughput(bool is_full_gc, + size_t* desired_promo_size_ptr, + size_t* desired_eden_size_ptr); + // Change the generation sizes to achieve minimum footprint + // Returned sizes are not aligned. + size_t adjust_promo_for_footprint(size_t desired_promo_size, + size_t desired_total); + size_t adjust_eden_for_footprint(size_t desired_promo_size, + size_t desired_total); + + // Size in bytes for an increment or decrement of eden. + virtual size_t eden_increment(size_t cur_eden, uint percent_change); + virtual size_t eden_decrement(size_t cur_eden); + size_t eden_decrement_aligned_down(size_t cur_eden); + size_t eden_increment_with_supplement_aligned_up(size_t cur_eden); + + // Size in bytes for an increment or decrement of the promotion area + virtual size_t promo_increment(size_t cur_promo, uint percent_change); + virtual size_t promo_decrement(size_t cur_promo); + size_t promo_decrement_aligned_down(size_t cur_promo); + size_t promo_increment_with_supplement_aligned_up(size_t cur_promo); + + // Decay the supplemental growth additive. + void decay_supplemental_growth(bool is_full_gc); + + // Returns a change that has been scaled down. Result + // is not aligned. (If useful, move to some shared + // location.) + size_t scale_down(size_t change, double part, double total); + + protected: + // Time accessors + + // Footprint accessors + size_t live_space() const { + return (size_t)(avg_base_footprint()->average() + + avg_young_live()->average() + + avg_old_live()->average()); + } + size_t free_space() const { + return _eden_size + _promo_size; + } + + void set_promo_size(size_t new_size) { + _promo_size = new_size; + } + void set_survivor_size(size_t new_size) { + _survivor_size = new_size; + } + + // Update estimators + void update_minor_pause_old_estimator(double minor_pause_in_ms); + + virtual GCPolicyKind kind() const { return _gc_ps_adaptive_size_policy; } + + public: + // Use by ASPSYoungGen and ASPSOldGen to limit boundary moving. + size_t eden_increment_aligned_up(size_t cur_eden); + size_t eden_increment_aligned_down(size_t cur_eden); + size_t promo_increment_aligned_up(size_t cur_promo); + size_t promo_increment_aligned_down(size_t cur_promo); + + virtual size_t eden_increment(size_t cur_eden); + virtual size_t promo_increment(size_t cur_promo); + + // Accessors for use by performance counters + AdaptivePaddedNoZeroDevAverage* avg_promoted() const { + return _gc_stats.avg_promoted(); + } + AdaptiveWeightedAverage* avg_base_footprint() const { + return _avg_base_footprint; + } + + // Input arguments are initial free space sizes for young and old + // generations, the initial survivor space size, the + // alignment values and the pause & throughput goals. + // + // NEEDS_CLEANUP this is a singleton object + PSAdaptiveSizePolicy(size_t init_eden_size, + size_t init_promo_size, + size_t init_survivor_size, + size_t intra_generation_alignment, + double gc_pause_goal_sec, + double gc_minor_pause_goal_sec, + uint gc_time_ratio); + + // Methods indicating events of interest to the adaptive size policy, + // called by GC algorithms. It is the responsibility of users of this + // policy to call these methods at the correct times! + void major_collection_begin(); + void major_collection_end(size_t amount_live, GCCause::Cause gc_cause); + + // + void tenured_allocation(size_t size) { + _avg_pretenured->sample(size); + } + + // Accessors + // NEEDS_CLEANUP should use sizes.hpp + + size_t calculated_old_free_size_in_bytes() const { + return (size_t)(_promo_size + avg_promoted()->padded_average()); + } + + size_t average_old_live_in_bytes() const { + return (size_t) avg_old_live()->average(); + } + + size_t average_promoted_in_bytes() const { + return (size_t)avg_promoted()->average(); + } + + size_t padded_average_promoted_in_bytes() const { + return (size_t)avg_promoted()->padded_average(); + } + + int change_young_gen_for_maj_pauses() { + return _change_young_gen_for_maj_pauses; + } + void set_change_young_gen_for_maj_pauses(int v) { + _change_young_gen_for_maj_pauses = v; + } + + int change_old_gen_for_min_pauses() { + return _change_old_gen_for_min_pauses; + } + void set_change_old_gen_for_min_pauses(int v) { + _change_old_gen_for_min_pauses = v; + } + + // Return true if the old generation size was changed + // to try to reach a pause time goal. + bool old_gen_changed_for_pauses() { + bool result = _change_old_gen_for_maj_pauses != 0 || + _change_old_gen_for_min_pauses != 0; + return result; + } + + // Return true if the young generation size was changed + // to try to reach a pause time goal. + bool young_gen_changed_for_pauses() { + bool result = _change_young_gen_for_min_pauses != 0 || + _change_young_gen_for_maj_pauses != 0; + return result; + } + // end flags for pause goal + + // Return true if the old generation size was changed + // to try to reach a throughput goal. + bool old_gen_changed_for_throughput() { + bool result = _change_old_gen_for_throughput != 0; + return result; + } + + // Return true if the young generation size was changed + // to try to reach a throughput goal. + bool young_gen_changed_for_throughput() { + bool result = _change_young_gen_for_throughput != 0; + return result; + } + + int decrease_for_footprint() { return _decrease_for_footprint; } + + + // Accessors for estimators. The slope of the linear fit is + // currently all that is used for making decisions. + + LinearLeastSquareFit* major_pause_old_estimator() { + return _major_pause_old_estimator; + } + + LinearLeastSquareFit* major_pause_young_estimator() { + return _major_pause_young_estimator; + } + + + virtual void clear_generation_free_space_flags(); + + float major_pause_old_slope() { return _major_pause_old_estimator->slope(); } + float major_pause_young_slope() { + return _major_pause_young_estimator->slope(); + } + float major_collection_slope() { return _major_collection_estimator->slope();} + + bool old_gen_policy_is_ready() { return _old_gen_policy_is_ready; } + + // Given the amount of live data in the heap, should we + // perform a Full GC? + bool should_full_GC(size_t live_in_old_gen); + + // Calculates optimial free space sizes for both the old and young + // generations. Stores results in _eden_size and _promo_size. + // Takes current used space in all generations as input, as well + // as an indication if a full gc has just been performed, for use + // in deciding if an OOM error should be thrown. + void compute_generation_free_space(size_t young_live, + size_t eden_live, + size_t old_live, + size_t perm_live, + size_t cur_eden, // current eden in bytes + size_t max_old_gen_size, + size_t max_eden_size, + bool is_full_gc, + GCCause::Cause gc_cause); + + // Calculates new survivor space size; returns a new tenuring threshold + // value. Stores new survivor size in _survivor_size. + int compute_survivor_space_size_and_threshold(bool is_survivor_overflow, + int tenuring_threshold, + size_t survivor_limit); + + // Return the maximum size of a survivor space if the young generation were of + // size gen_size. + size_t max_survivor_size(size_t gen_size) { + // Never allow the target survivor size to grow more than MinSurvivorRatio + // of the young generation size. We cannot grow into a two semi-space + // system, with Eden zero sized. Even if the survivor space grows, from() + // might grow by moving the bottom boundary "down" -- so from space will + // remain almost full anyway (top() will be near end(), but there will be a + // large filler object at the bottom). + const size_t sz = gen_size / MinSurvivorRatio; + const size_t alignment = _intra_generation_alignment; + return sz > alignment ? align_size_down(sz, alignment) : alignment; + } + + size_t live_at_last_full_gc() { + return _live_at_last_full_gc; + } + + size_t bytes_absorbed_from_eden() const { return _bytes_absorbed_from_eden; } + void reset_bytes_absorbed_from_eden() { _bytes_absorbed_from_eden = 0; } + + void set_bytes_absorbed_from_eden(size_t val) { + _bytes_absorbed_from_eden = val; + } + + // Update averages that are always used (even + // if adaptive sizing is turned off). + void update_averages(bool is_survivor_overflow, + size_t survived, + size_t promoted); + + // Printing support + virtual bool print_adaptive_size_policy_on(outputStream* st) const; +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp new file mode 100644 index 00000000000..ba3684a1977 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp @@ -0,0 +1,301 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_psCompactionManager.cpp.incl" + +PSOldGen* ParCompactionManager::_old_gen = NULL; +ParCompactionManager** ParCompactionManager::_manager_array = NULL; +OopTaskQueueSet* ParCompactionManager::_stack_array = NULL; +ObjectStartArray* ParCompactionManager::_start_array = NULL; +ParMarkBitMap* ParCompactionManager::_mark_bitmap = NULL; +ChunkTaskQueueSet* ParCompactionManager::_chunk_array = NULL; + +ParCompactionManager::ParCompactionManager() : + _action(CopyAndUpdate) { + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + _old_gen = heap->old_gen(); + _start_array = old_gen()->start_array(); + + + marking_stack()->initialize(); + + // We want the overflow stack to be permanent + _overflow_stack = new (ResourceObj::C_HEAP) GrowableArray(10, true); +#ifdef USE_ChunkTaskQueueWithOverflow + chunk_stack()->initialize(); +#else + chunk_stack()->initialize(); + + // We want the overflow stack to be permanent + _chunk_overflow_stack = + new (ResourceObj::C_HEAP) GrowableArray(10, true); +#endif + + // Note that _revisit_klass_stack is allocated out of the + // C heap (as opposed to out of ResourceArena). + int size = + (SystemDictionary::number_of_classes() * 2) * 2 / ParallelGCThreads; + _revisit_klass_stack = new (ResourceObj::C_HEAP) GrowableArray(size, true); + +} + +ParCompactionManager::~ParCompactionManager() { + delete _overflow_stack; + delete _revisit_klass_stack; + // _manager_array and _stack_array are statics + // shared with all instances of ParCompactionManager + // should not be deallocated. +} + +void ParCompactionManager::initialize(ParMarkBitMap* mbm) { + assert(PSParallelCompact::gc_task_manager() != NULL, + "Needed for initialization"); + + _mark_bitmap = mbm; + + uint parallel_gc_threads = PSParallelCompact::gc_task_manager()->workers(); + + assert(_manager_array == NULL, "Attempt to initialize twice"); + _manager_array = NEW_C_HEAP_ARRAY(ParCompactionManager*, parallel_gc_threads+1 ); + guarantee(_manager_array != NULL, "Could not initialize promotion manager"); + + _stack_array = new OopTaskQueueSet(parallel_gc_threads); + guarantee(_stack_array != NULL, "Count not initialize promotion manager"); + _chunk_array = new ChunkTaskQueueSet(parallel_gc_threads); + guarantee(_chunk_array != NULL, "Count not initialize promotion manager"); + + // Create and register the ParCompactionManager(s) for the worker threads. + for(uint i=0; iregister_queue(i, _manager_array[i]->marking_stack()); +#ifdef USE_ChunkTaskQueueWithOverflow + chunk_array()->register_queue(i, _manager_array[i]->chunk_stack()->task_queue()); +#else + chunk_array()->register_queue(i, _manager_array[i]->chunk_stack()); +#endif + } + + // The VMThread gets its own ParCompactionManager, which is not available + // for work stealing. + _manager_array[parallel_gc_threads] = new ParCompactionManager(); + guarantee(_manager_array[parallel_gc_threads] != NULL, + "Could not create ParCompactionManager"); + assert(PSParallelCompact::gc_task_manager()->workers() != 0, + "Not initialized?"); +} + +bool ParCompactionManager::should_update() { + assert(action() != NotValid, "Action is not set"); + return (action() == ParCompactionManager::Update) || + (action() == ParCompactionManager::CopyAndUpdate) || + (action() == ParCompactionManager::UpdateAndCopy); +} + +bool ParCompactionManager::should_copy() { + assert(action() != NotValid, "Action is not set"); + return (action() == ParCompactionManager::Copy) || + (action() == ParCompactionManager::CopyAndUpdate) || + (action() == ParCompactionManager::UpdateAndCopy); +} + +bool ParCompactionManager::should_verify_only() { + assert(action() != NotValid, "Action is not set"); + return action() == ParCompactionManager::VerifyUpdate; +} + +bool ParCompactionManager::should_reset_only() { + assert(action() != NotValid, "Action is not set"); + return action() == ParCompactionManager::ResetObjects; +} + +// For now save on a stack +void ParCompactionManager::save_for_scanning(oop m) { + stack_push(m); +} + +void ParCompactionManager::stack_push(oop obj) { + + if(!marking_stack()->push(obj)) { + overflow_stack()->push(obj); + } +} + +oop ParCompactionManager::retrieve_for_scanning() { + + // Should not be used in the parallel case + ShouldNotReachHere(); + return NULL; +} + +// Save chunk on a stack +void ParCompactionManager::save_for_processing(size_t chunk_index) { +#ifdef ASSERT + const ParallelCompactData& sd = PSParallelCompact::summary_data(); + ParallelCompactData::ChunkData* const chunk_ptr = sd.chunk(chunk_index); + assert(chunk_ptr->claimed(), "must be claimed"); + assert(chunk_ptr->_pushed++ == 0, "should only be pushed once"); +#endif + chunk_stack_push(chunk_index); +} + +void ParCompactionManager::chunk_stack_push(size_t chunk_index) { + +#ifdef USE_ChunkTaskQueueWithOverflow + chunk_stack()->save(chunk_index); +#else + if(!chunk_stack()->push(chunk_index)) { + chunk_overflow_stack()->push(chunk_index); + } +#endif +} + +bool ParCompactionManager::retrieve_for_processing(size_t& chunk_index) { +#ifdef USE_ChunkTaskQueueWithOverflow + return chunk_stack()->retrieve(chunk_index); +#else + // Should not be used in the parallel case + ShouldNotReachHere(); + return false; +#endif +} + +ParCompactionManager* +ParCompactionManager::gc_thread_compaction_manager(int index) { + assert(index >= 0 && index < (int)ParallelGCThreads, "index out of range"); + assert(_manager_array != NULL, "Sanity"); + return _manager_array[index]; +} + +void ParCompactionManager::reset() { + for(uint i=0; irevisit_klass_stack()->clear(); + } +} + +void ParCompactionManager::drain_marking_stacks(OopClosure* blk) { +#ifdef ASSERT + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + MutableSpace* to_space = heap->young_gen()->to_space(); + MutableSpace* old_space = heap->old_gen()->object_space(); + MutableSpace* perm_space = heap->perm_gen()->object_space(); +#endif /* ASSERT */ + + + do { + + // Drain overflow stack first, so other threads can steal from + // claimed stack while we work. + while(!overflow_stack()->is_empty()) { + oop obj = overflow_stack()->pop(); + obj->follow_contents(this); + } + + oop obj; + // obj is a reference!!! + while (marking_stack()->pop_local(obj)) { + // It would be nice to assert about the type of objects we might + // pop, but they can come from anywhere, unfortunately. + obj->follow_contents(this); + } + } while((marking_stack()->size() != 0) || (overflow_stack()->length() != 0)); + + assert(marking_stack()->size() == 0, "Sanity"); + assert(overflow_stack()->length() == 0, "Sanity"); +} + +void ParCompactionManager::drain_chunk_overflow_stack() { + size_t chunk_index = (size_t) -1; + while(chunk_stack()->retrieve_from_overflow(chunk_index)) { + PSParallelCompact::fill_and_update_chunk(this, chunk_index); + } +} + +void ParCompactionManager::drain_chunk_stacks() { +#ifdef ASSERT + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + MutableSpace* to_space = heap->young_gen()->to_space(); + MutableSpace* old_space = heap->old_gen()->object_space(); + MutableSpace* perm_space = heap->perm_gen()->object_space(); +#endif /* ASSERT */ + +#if 1 // def DO_PARALLEL - the serial code hasn't been updated + do { + +#ifdef USE_ChunkTaskQueueWithOverflow + // Drain overflow stack first, so other threads can steal from + // claimed stack while we work. + size_t chunk_index = (size_t) -1; + while(chunk_stack()->retrieve_from_overflow(chunk_index)) { + PSParallelCompact::fill_and_update_chunk(this, chunk_index); + } + + while (chunk_stack()->retrieve_from_stealable_queue(chunk_index)) { + PSParallelCompact::fill_and_update_chunk(this, chunk_index); + } + } while (!chunk_stack()->is_empty()); +#else + // Drain overflow stack first, so other threads can steal from + // claimed stack while we work. + while(!chunk_overflow_stack()->is_empty()) { + size_t chunk_index = chunk_overflow_stack()->pop(); + PSParallelCompact::fill_and_update_chunk(this, chunk_index); + } + + size_t chunk_index = -1; + // obj is a reference!!! + while (chunk_stack()->pop_local(chunk_index)) { + // It would be nice to assert about the type of objects we might + // pop, but they can come from anywhere, unfortunately. + PSParallelCompact::fill_and_update_chunk(this, chunk_index); + } + } while((chunk_stack()->size() != 0) || + (chunk_overflow_stack()->length() != 0)); +#endif + +#ifdef USE_ChunkTaskQueueWithOverflow + assert(chunk_stack()->is_empty(), "Sanity"); +#else + assert(chunk_stack()->size() == 0, "Sanity"); + assert(chunk_overflow_stack()->length() == 0, "Sanity"); +#endif +#else + oop obj; + while (obj = retrieve_for_scanning()) { + obj->follow_contents(this); + } +#endif +} + +#ifdef ASSERT +bool ParCompactionManager::stacks_have_been_allocated() { + return (revisit_klass_stack()->data_addr() != NULL); +} +#endif diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp new file mode 100644 index 00000000000..d09266c5434 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp @@ -0,0 +1,199 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// psPromotionManager is used by a single thread to manage object survival +// during a scavenge. The promotion manager contains thread local data only. +// +// NOTE! Be carefull when allocating the stacks on cheap. If you are going +// to use a promotion manager in more than one thread, the stacks MUST be +// on cheap. This can lead to memory leaks, though, as they are not auto +// deallocated. +// +// FIX ME FIX ME Add a destructor, and don't rely on the user to drain/flush/deallocate! +// + +// Move to some global location +#define HAS_BEEN_MOVED 0x1501d01d +// End move to some global location + + +class MutableSpace; +class PSOldGen; +class ParCompactionManager; +class ObjectStartArray; +class ParallelCompactData; +class ParMarkBitMap; + +// Move to it's own file if this works out. + +class ParCompactionManager : public CHeapObj { + friend class ParallelTaskTerminator; + friend class ParMarkBitMap; + friend class PSParallelCompact; + friend class StealChunkCompactionTask; + friend class UpdateAndFillClosure; + friend class RefProcTaskExecutor; + + public: + +// ------------------------ Don't putback if not needed + // Actions that the compaction manager should take. + enum Action { + Update, + Copy, + UpdateAndCopy, + CopyAndUpdate, + VerifyUpdate, + ResetObjects, + NotValid + }; +// ------------------------ End don't putback if not needed + + private: + static ParCompactionManager** _manager_array; + static OopTaskQueueSet* _stack_array; + static ObjectStartArray* _start_array; + static ChunkTaskQueueSet* _chunk_array; + static PSOldGen* _old_gen; + + OopTaskQueue _marking_stack; + GrowableArray* _overflow_stack; + // Is there a way to reuse the _marking_stack for the + // saving empty chunks? For now just create a different + // type of TaskQueue. + +#ifdef USE_ChunkTaskQueueWithOverflow + ChunkTaskQueueWithOverflow _chunk_stack; +#else + ChunkTaskQueue _chunk_stack; + GrowableArray* _chunk_overflow_stack; +#endif + +#if 1 // does this happen enough to need a per thread stack? + GrowableArray* _revisit_klass_stack; +#endif + static ParMarkBitMap* _mark_bitmap; + + Action _action; + + static PSOldGen* old_gen() { return _old_gen; } + static ObjectStartArray* start_array() { return _start_array; } + static OopTaskQueueSet* stack_array() { return _stack_array; } + + static void initialize(ParMarkBitMap* mbm); + + protected: + // Array of tasks. Needed by the ParallelTaskTerminator. + static ChunkTaskQueueSet* chunk_array() { return _chunk_array; } + + OopTaskQueue* marking_stack() { return &_marking_stack; } + GrowableArray* overflow_stack() { return _overflow_stack; } +#ifdef USE_ChunkTaskQueueWithOverflow + ChunkTaskQueueWithOverflow* chunk_stack() { return &_chunk_stack; } +#else + ChunkTaskQueue* chunk_stack() { return &_chunk_stack; } + GrowableArray* chunk_overflow_stack() { return _chunk_overflow_stack; } +#endif + + // Pushes onto the marking stack. If the marking stack is full, + // pushes onto the overflow stack. + void stack_push(oop obj); + // Do not implement an equivalent stack_pop. Deal with the + // marking stack and overflow stack directly. + + // Pushes onto the chunk stack. If the chunk stack is full, + // pushes onto the chunk overflow stack. + void chunk_stack_push(size_t chunk_index); + public: + + Action action() { return _action; } + void set_action(Action v) { _action = v; } + + inline static ParCompactionManager* manager_array(int index); + + ParCompactionManager(); + ~ParCompactionManager(); + + void allocate_stacks(); + void deallocate_stacks(); + ParMarkBitMap* mark_bitmap() { return _mark_bitmap; } + + // Take actions in preparation for a compaction. + static void reset(); + + // void drain_stacks(); + + bool should_update(); + bool should_copy(); + bool should_verify_only(); + bool should_reset_only(); + +#if 1 + // Probably stays as a growable array + GrowableArray* revisit_klass_stack() { return _revisit_klass_stack; } +#endif + + // Save oop for later processing. Must not fail. + void save_for_scanning(oop m); + // Get a oop for scanning. If returns null, no oop were found. + oop retrieve_for_scanning(); + + // Save chunk for later processing. Must not fail. + void save_for_processing(size_t chunk_index); + // Get a chunk for processing. If returns null, no chunk were found. + bool retrieve_for_processing(size_t& chunk_index); + + // Access function for compaction managers + static ParCompactionManager* gc_thread_compaction_manager(int index); + + static bool steal(int queue_num, int* seed, Task& t) { + return stack_array()->steal(queue_num, seed, t); + } + + static bool steal(int queue_num, int* seed, ChunkTask& t) { + return chunk_array()->steal(queue_num, seed, t); + } + + // Process tasks remaining on any stack + void drain_marking_stacks(OopClosure *blk); + + // Process tasks remaining on any stack + void drain_chunk_stacks(); + + // Process tasks remaining on any stack + void drain_chunk_overflow_stack(); + + // Debugging support +#ifdef ASSERT + bool stacks_have_been_allocated(); +#endif +}; + +inline ParCompactionManager* ParCompactionManager::manager_array(int index) { + assert(_manager_array != NULL, "access of NULL manager_array"); + assert(index >= 0 && index <= (int)ParallelGCThreads, + "out of range manager_array access"); + return _manager_array[index]; +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.cpp new file mode 100644 index 00000000000..02c6450a7a0 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.cpp @@ -0,0 +1,199 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_psGCAdaptivePolicyCounters.cpp.incl" + + + +PSGCAdaptivePolicyCounters::PSGCAdaptivePolicyCounters(const char* name_arg, + int collectors, + int generations, + PSAdaptiveSizePolicy* size_policy_arg) + : GCAdaptivePolicyCounters(name_arg, + collectors, + generations, + size_policy_arg) { + if (UsePerfData) { + EXCEPTION_MARK; + ResourceMark rm; + + const char* cname; + + cname = PerfDataManager::counter_name(name_space(), "oldPromoSize"); + _old_promo_size = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, ps_size_policy()->calculated_promo_size_in_bytes(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "oldEdenSize"); + _old_eden_size = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, ps_size_policy()->calculated_eden_size_in_bytes(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "oldCapacity"); + _old_capacity = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) Arguments::initial_heap_size(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "boundaryMoved"); + _boundary_moved = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) 0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgPromotedAvg"); + _avg_promoted_avg_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + ps_size_policy()->calculated_promo_size_in_bytes(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgPromotedDev"); + _avg_promoted_dev_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + (jlong) 0 , CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgPromotedPaddedAvg"); + _avg_promoted_padded_avg_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + ps_size_policy()->calculated_promo_size_in_bytes(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), + "avgPretenuredPaddedAvg"); + _avg_pretenured_padded_avg = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + (jlong) 0, CHECK); + + + cname = PerfDataManager::counter_name(name_space(), + "changeYoungGenForMajPauses"); + _change_young_gen_for_maj_pauses_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events, + (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), + "changeOldGenForMinPauses"); + _change_old_gen_for_min_pauses = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events, + (jlong)0, CHECK); + + + cname = PerfDataManager::counter_name(name_space(), "avgMajorPauseTime"); + _avg_major_pause = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, (jlong) ps_size_policy()->_avg_major_pause->average(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgMajorIntervalTime"); + _avg_major_interval = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, (jlong) ps_size_policy()->_avg_major_interval->average(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "majorGcCost"); + _major_gc_cost_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, (jlong) ps_size_policy()->major_gc_cost(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "liveSpace"); + _live_space = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, ps_size_policy()->live_space(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "freeSpace"); + _free_space = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, ps_size_policy()->free_space(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgBaseFootprint"); + _avg_base_footprint = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) ps_size_policy()->avg_base_footprint()->average(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "gcTimeLimitExceeded"); + _gc_time_limit_exceeded = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Events, ps_size_policy()->gc_time_limit_exceeded(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "liveAtLastFullGc"); + _live_at_last_full_gc = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, ps_size_policy()->live_at_last_full_gc(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "majorPauseOldSlope"); + _major_pause_old_slope = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_None, (jlong) 0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "minorPauseOldSlope"); + _minor_pause_old_slope = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_None, (jlong) 0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "majorPauseYoungSlope"); + _major_pause_young_slope = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_None, (jlong) 0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "scavengeSkipped"); + _scavenge_skipped = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) 0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "fullFollowsScavenge"); + _full_follows_scavenge = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) 0, CHECK); + + _counter_time_stamp.update(); + } + + assert(size_policy()->is_gc_ps_adaptive_size_policy(), + "Wrong type of size policy"); +} + +void PSGCAdaptivePolicyCounters::update_counters_from_policy() { + if (UsePerfData) { + GCAdaptivePolicyCounters::update_counters_from_policy(); + update_eden_size(); + update_promo_size(); + update_avg_old_live(); + update_survivor_size_counters(); + update_avg_promoted_avg(); + update_avg_promoted_dev(); + update_avg_promoted_padded_avg(); + update_avg_pretenured_padded_avg(); + + update_avg_major_pause(); + update_avg_major_interval(); + update_minor_gc_cost_counter(); + update_major_gc_cost_counter(); + update_mutator_cost_counter(); + update_decrement_tenuring_threshold_for_gc_cost(); + update_increment_tenuring_threshold_for_gc_cost(); + update_decrement_tenuring_threshold_for_survivor_limit(); + update_live_space(); + update_free_space(); + update_avg_base_footprint(); + + update_change_old_gen_for_maj_pauses(); + update_change_young_gen_for_maj_pauses(); + update_change_old_gen_for_min_pauses(); + + update_change_old_gen_for_throughput(); + update_change_young_gen_for_throughput(); + + update_decrease_for_footprint(); + update_decide_at_full_gc_counter(); + + update_major_pause_old_slope(); + update_minor_pause_old_slope(); + update_major_pause_young_slope(); + update_minor_collection_slope_counter(); + } +} + +void PSGCAdaptivePolicyCounters::update_counters() { + if (UsePerfData) { + update_counters_from_policy(); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.hpp new file mode 100644 index 00000000000..60b89a51c08 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.hpp @@ -0,0 +1,194 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// PSGCAdaptivePolicyCounters is a holder class for performance counters +// that track the data and decisions for the ergonomics policy for the +// parallel scavenge collector. + +class PSGCAdaptivePolicyCounters : public GCAdaptivePolicyCounters { + friend class VMStructs; + + private: + // survivor space vs. tenuring threshold + PerfVariable* _old_promo_size; + PerfVariable* _old_eden_size; + PerfVariable* _avg_promoted_avg_counter; + PerfVariable* _avg_promoted_dev_counter; + PerfVariable* _avg_promoted_padded_avg_counter; + PerfVariable* _avg_pretenured_padded_avg; + + // young gen vs. old gen sizing + PerfVariable* _avg_major_pause; + PerfVariable* _avg_major_interval; + PerfVariable* _live_space; + PerfVariable* _free_space; + PerfVariable* _avg_base_footprint; + PerfVariable* _gc_time_limit_exceeded; + PerfVariable* _live_at_last_full_gc; + PerfVariable* _old_capacity; + PerfVariable* _boundary_moved; + + PerfVariable* _change_old_gen_for_min_pauses; + PerfVariable* _change_young_gen_for_maj_pauses_counter; + + PerfVariable* _major_pause_old_slope; + PerfVariable* _minor_pause_old_slope; + PerfVariable* _major_pause_young_slope; + + PerfVariable* _scavenge_skipped; + PerfVariable* _full_follows_scavenge; + + // Use this time stamp if the gc time stamp is not available. + TimeStamp _counter_time_stamp; + + protected: + PSAdaptiveSizePolicy* ps_size_policy() { + return (PSAdaptiveSizePolicy*)_size_policy; + } + + public: + PSGCAdaptivePolicyCounters(const char* name, int collectors, int generations, + PSAdaptiveSizePolicy* size_policy); + inline void update_old_capacity(size_t size_in_bytes) { + _old_capacity->set_value(size_in_bytes); + } + inline void update_old_eden_size(size_t old_size) { + _old_eden_size->set_value(old_size); + } + inline void update_old_promo_size(size_t old_size) { + _old_promo_size->set_value(old_size); + } + inline void update_boundary_moved(int size_in_bytes) { + _boundary_moved->set_value(size_in_bytes); + } + inline void update_avg_promoted_avg() { + _avg_promoted_avg_counter->set_value( + (jlong)(ps_size_policy()->avg_promoted()->average()) + ); + } + inline void update_avg_promoted_dev() { + _avg_promoted_dev_counter->set_value( + (jlong)(ps_size_policy()->avg_promoted()->deviation()) + ); + } + inline void update_avg_promoted_padded_avg() { + _avg_promoted_padded_avg_counter->set_value( + (jlong)(ps_size_policy()->avg_promoted()->padded_average()) + ); + } + + inline void update_avg_pretenured_padded_avg() { + _avg_pretenured_padded_avg->set_value( + (jlong)(ps_size_policy()->_avg_pretenured->padded_average()) + ); + } + inline void update_change_young_gen_for_maj_pauses() { + _change_young_gen_for_maj_pauses_counter->set_value( + ps_size_policy()->change_young_gen_for_maj_pauses()); + } + inline void update_change_old_gen_for_min_pauses() { + _change_old_gen_for_min_pauses->set_value( + ps_size_policy()->change_old_gen_for_min_pauses()); + } + + // compute_generation_free_space() statistics + + inline void update_avg_major_pause() { + _avg_major_pause->set_value( + (jlong)(ps_size_policy()->_avg_major_pause->average() * 1000.0) + ); + } + inline void update_avg_major_interval() { + _avg_major_interval->set_value( + (jlong)(ps_size_policy()->_avg_major_interval->average() * 1000.0) + ); + } + + inline void update_major_gc_cost_counter() { + _major_gc_cost_counter->set_value( + (jlong)(ps_size_policy()->major_gc_cost() * 100.0) + ); + } + inline void update_mutator_cost_counter() { + _mutator_cost_counter->set_value( + (jlong)(ps_size_policy()->mutator_cost() * 100.0) + ); + } + + inline void update_live_space() { + _live_space->set_value(ps_size_policy()->live_space()); + } + inline void update_free_space() { + _free_space->set_value(ps_size_policy()->free_space()); + } + + inline void update_avg_base_footprint() { + _avg_base_footprint->set_value( + (jlong)(ps_size_policy()->avg_base_footprint()->average()) + ); + } + inline void update_avg_old_live() { + _avg_old_live_counter->set_value( + (jlong)(ps_size_policy()->avg_old_live()->average()) + ); + } + // Scale up all the slopes + inline void update_major_pause_old_slope() { + _major_pause_old_slope->set_value( + (jlong)(ps_size_policy()->major_pause_old_slope() * 1000) + ); + } + inline void update_minor_pause_old_slope() { + _minor_pause_old_slope->set_value( + (jlong)(ps_size_policy()->minor_pause_old_slope() * 1000) + ); + } + inline void update_major_pause_young_slope() { + _major_pause_young_slope->set_value( + (jlong)(ps_size_policy()->major_pause_young_slope() * 1000) + ); + } + + inline void update_scavenge_skipped(int cause) { + _scavenge_skipped->set_value(cause); + } + + inline void update_full_follows_scavenge(int event) { + _full_follows_scavenge->set_value(event); + } + + // Update all the counters that can be updated from the size policy. + // This should be called after all policy changes have been made + // and reflected internall in the size policy. + void update_counters_from_policy(); + + // Update counters that can be updated from fields internal to the + // counter or from globals. This is distinguished from counters + // that are updated via input parameters. + void update_counters(); + + virtual GCPolicyCounters::Name kind() const { + return GCPolicyCounters::PSGCAdaptivePolicyCountersKind; + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.cpp new file mode 100644 index 00000000000..d5a57238c5b --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.cpp @@ -0,0 +1,64 @@ + +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_psGenerationCounters.cpp.incl" + + +PSGenerationCounters::PSGenerationCounters(const char* name, + int ordinal, int spaces, + PSVirtualSpace* v): + _ps_virtual_space(v) { + + if (UsePerfData) { + + EXCEPTION_MARK; + ResourceMark rm; + + const char* cns = PerfDataManager::name_space("generation", ordinal); + + _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1); + strcpy(_name_space, cns); + + const char* cname = PerfDataManager::counter_name(_name_space, "name"); + PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "spaces"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None, + spaces, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "minCapacity"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, + _ps_virtual_space->committed_size(), CHECK); + + cname = PerfDataManager::counter_name(_name_space, "maxCapacity"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, + _ps_virtual_space->reserved_size(), CHECK); + + cname = PerfDataManager::counter_name(_name_space, "capacity"); + _current_size = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, _ps_virtual_space->committed_size(), CHECK); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.hpp new file mode 100644 index 00000000000..cebceae1b13 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.hpp @@ -0,0 +1,43 @@ + +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A PSGenerationCounter is a holder class for performance counters +// that track a generation + +class PSGenerationCounters: public GenerationCounters { + friend class VMStructs; + + private: + PSVirtualSpace* _ps_virtual_space; + + public: + PSGenerationCounters(const char* name, int ordinal, int spaces, + PSVirtualSpace* v); + + void update_all() { + assert(_virtual_space == NULL, "Only one should be in use"); + _current_size->set_value(_ps_virtual_space->committed_size()); + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp new file mode 100644 index 00000000000..3a817a2049e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp @@ -0,0 +1,670 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_psMarkSweep.cpp.incl" + +elapsedTimer PSMarkSweep::_accumulated_time; +unsigned int PSMarkSweep::_total_invocations = 0; +jlong PSMarkSweep::_time_of_last_gc = 0; +CollectorCounters* PSMarkSweep::_counters = NULL; + +void PSMarkSweep::initialize() { + MemRegion mr = Universe::heap()->reserved_region(); + _ref_processor = new ReferenceProcessor(mr, + true, // atomic_discovery + false); // mt_discovery + if (!UseParallelOldGC || !VerifyParallelOldWithMarkSweep) { + _counters = new CollectorCounters("PSMarkSweep", 1); + } +} + +// This method contains all heap specific policy for invoking mark sweep. +// PSMarkSweep::invoke_no_policy() will only attempt to mark-sweep-compact +// the heap. It will do nothing further. If we need to bail out for policy +// reasons, scavenge before full gc, or any other specialized behavior, it +// needs to be added here. +// +// Note that this method should only be called from the vm_thread while +// at a safepoint! +void PSMarkSweep::invoke(bool maximum_heap_compaction) { + assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); + assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread"); + assert(!Universe::heap()->is_gc_active(), "not reentrant"); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + GCCause::Cause gc_cause = heap->gc_cause(); + PSAdaptiveSizePolicy* policy = heap->size_policy(); + + // Before each allocation/collection attempt, find out from the + // policy object if GCs are, on the whole, taking too long. If so, + // bail out without attempting a collection. The exceptions are + // for explicitly requested GC's. + if (!policy->gc_time_limit_exceeded() || + GCCause::is_user_requested_gc(gc_cause) || + GCCause::is_serviceability_requested_gc(gc_cause)) { + IsGCActiveMark mark; + + if (ScavengeBeforeFullGC) { + PSScavenge::invoke_no_policy(); + } + + int count = (maximum_heap_compaction)?1:MarkSweepAlwaysCompactCount; + IntFlagSetting flag_setting(MarkSweepAlwaysCompactCount, count); + PSMarkSweep::invoke_no_policy(maximum_heap_compaction); + } +} + +// This method contains no policy. You should probably +// be calling invoke() instead. +void PSMarkSweep::invoke_no_policy(bool clear_all_softrefs) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint"); + assert(ref_processor() != NULL, "Sanity"); + + if (GC_locker::check_active_before_gc()) { + return; + } + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + GCCause::Cause gc_cause = heap->gc_cause(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + PSAdaptiveSizePolicy* size_policy = heap->size_policy(); + + PSYoungGen* young_gen = heap->young_gen(); + PSOldGen* old_gen = heap->old_gen(); + PSPermGen* perm_gen = heap->perm_gen(); + + // Increment the invocation count + heap->increment_total_collections(true /* full */); + + // We need to track unique mark sweep invocations as well. + _total_invocations++; + + AdaptiveSizePolicyOutput(size_policy, heap->total_collections()); + + if (PrintHeapAtGC) { + Universe::print_heap_before_gc(); + } + + // Fill in TLABs + heap->accumulate_statistics_all_tlabs(); + heap->ensure_parsability(true); // retire TLABs + + if (VerifyBeforeGC && heap->total_collections() >= VerifyGCStartAt) { + HandleMark hm; // Discard invalid handles created during verification + gclog_or_tty->print(" VerifyBeforeGC:"); + Universe::verify(true); + } + + // Verify object start arrays + if (VerifyObjectStartArray && + VerifyBeforeGC) { + old_gen->verify_object_start_array(); + perm_gen->verify_object_start_array(); + } + + // Filled in below to track the state of the young gen after the collection. + bool eden_empty; + bool survivors_empty; + bool young_gen_empty; + + { + HandleMark hm; + const bool is_system_gc = gc_cause == GCCause::_java_lang_system_gc; + // This is useful for debugging but don't change the output the + // the customer sees. + const char* gc_cause_str = "Full GC"; + if (is_system_gc && PrintGCDetails) { + gc_cause_str = "Full GC (System)"; + } + gclog_or_tty->date_stamp(PrintGC && PrintGCDateStamps); + TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); + TraceTime t1(gc_cause_str, PrintGC, !PrintGCDetails, gclog_or_tty); + TraceCollectorStats tcs(counters()); + TraceMemoryManagerStats tms(true /* Full GC */); + + if (TraceGen1Time) accumulated_time()->start(); + + // Let the size policy know we're starting + size_policy->major_collection_begin(); + + // When collecting the permanent generation methodOops may be moving, + // so we either have to flush all bcp data or convert it into bci. + CodeCache::gc_prologue(); + Threads::gc_prologue(); + BiasedLocking::preserve_marks(); + + // Capture heap size before collection for printing. + size_t prev_used = heap->used(); + + // Capture perm gen size before collection for sizing. + size_t perm_gen_prev_used = perm_gen->used_in_bytes(); + + // For PrintGCDetails + size_t old_gen_prev_used = old_gen->used_in_bytes(); + size_t young_gen_prev_used = young_gen->used_in_bytes(); + + allocate_stacks(); + + NOT_PRODUCT(ref_processor()->verify_no_references_recorded()); + COMPILER2_PRESENT(DerivedPointerTable::clear()); + + ref_processor()->enable_discovery(); + + mark_sweep_phase1(clear_all_softrefs); + + mark_sweep_phase2(); + + // Don't add any more derived pointers during phase3 + COMPILER2_PRESENT(assert(DerivedPointerTable::is_active(), "Sanity")); + COMPILER2_PRESENT(DerivedPointerTable::set_active(false)); + + mark_sweep_phase3(); + + mark_sweep_phase4(); + + restore_marks(); + + deallocate_stacks(); + + eden_empty = young_gen->eden_space()->is_empty(); + if (!eden_empty) { + eden_empty = absorb_live_data_from_eden(size_policy, young_gen, old_gen); + } + + // Update heap occupancy information which is used as + // input to soft ref clearing policy at the next gc. + Universe::update_heap_info_at_gc(); + + survivors_empty = young_gen->from_space()->is_empty() && + young_gen->to_space()->is_empty(); + young_gen_empty = eden_empty && survivors_empty; + + BarrierSet* bs = heap->barrier_set(); + if (bs->is_a(BarrierSet::ModRef)) { + ModRefBarrierSet* modBS = (ModRefBarrierSet*)bs; + MemRegion old_mr = heap->old_gen()->reserved(); + MemRegion perm_mr = heap->perm_gen()->reserved(); + assert(perm_mr.end() <= old_mr.start(), "Generations out of order"); + + if (young_gen_empty) { + modBS->clear(MemRegion(perm_mr.start(), old_mr.end())); + } else { + modBS->invalidate(MemRegion(perm_mr.start(), old_mr.end())); + } + } + + BiasedLocking::restore_marks(); + Threads::gc_epilogue(); + CodeCache::gc_epilogue(); + + COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); + + ref_processor()->enqueue_discovered_references(NULL); + + // Update time of last GC + reset_millis_since_last_gc(); + + // Let the size policy know we're done + size_policy->major_collection_end(old_gen->used_in_bytes(), gc_cause); + + if (UseAdaptiveSizePolicy) { + + if (PrintAdaptiveSizePolicy) { + gclog_or_tty->print("AdaptiveSizeStart: "); + gclog_or_tty->stamp(); + gclog_or_tty->print_cr(" collection: %d ", + heap->total_collections()); + if (Verbose) { + gclog_or_tty->print("old_gen_capacity: %d young_gen_capacity: %d" + " perm_gen_capacity: %d ", + old_gen->capacity_in_bytes(), young_gen->capacity_in_bytes(), + perm_gen->capacity_in_bytes()); + } + } + + // Don't check if the size_policy is ready here. Let + // the size_policy check that internally. + if (UseAdaptiveGenerationSizePolicyAtMajorCollection && + ((gc_cause != GCCause::_java_lang_system_gc) || + UseAdaptiveSizePolicyWithSystemGC)) { + // Calculate optimal free space amounts + assert(young_gen->max_size() > + young_gen->from_space()->capacity_in_bytes() + + young_gen->to_space()->capacity_in_bytes(), + "Sizes of space in young gen are out-of-bounds"); + size_t max_eden_size = young_gen->max_size() - + young_gen->from_space()->capacity_in_bytes() - + young_gen->to_space()->capacity_in_bytes(); + size_policy->compute_generation_free_space(young_gen->used_in_bytes(), + young_gen->eden_space()->used_in_bytes(), + old_gen->used_in_bytes(), + perm_gen->used_in_bytes(), + young_gen->eden_space()->capacity_in_bytes(), + old_gen->max_gen_size(), + max_eden_size, + true /* full gc*/, + gc_cause); + + heap->resize_old_gen(size_policy->calculated_old_free_size_in_bytes()); + + // Don't resize the young generation at an major collection. A + // desired young generation size may have been calculated but + // resizing the young generation complicates the code because the + // resizing of the old generation may have moved the boundary + // between the young generation and the old generation. Let the + // young generation resizing happen at the minor collections. + } + if (PrintAdaptiveSizePolicy) { + gclog_or_tty->print_cr("AdaptiveSizeStop: collection: %d ", + heap->total_collections()); + } + } + + if (UsePerfData) { + heap->gc_policy_counters()->update_counters(); + heap->gc_policy_counters()->update_old_capacity( + old_gen->capacity_in_bytes()); + heap->gc_policy_counters()->update_young_capacity( + young_gen->capacity_in_bytes()); + } + + heap->resize_all_tlabs(); + + // We collected the perm gen, so we'll resize it here. + perm_gen->compute_new_size(perm_gen_prev_used); + + if (TraceGen1Time) accumulated_time()->stop(); + + if (PrintGC) { + if (PrintGCDetails) { + // Don't print a GC timestamp here. This is after the GC so + // would be confusing. + young_gen->print_used_change(young_gen_prev_used); + old_gen->print_used_change(old_gen_prev_used); + } + heap->print_heap_change(prev_used); + // Do perm gen after heap becase prev_used does + // not include the perm gen (done this way in the other + // collectors). + if (PrintGCDetails) { + perm_gen->print_used_change(perm_gen_prev_used); + } + } + + // Track memory usage and detect low memory + MemoryService::track_memory_usage(); + heap->update_counters(); + + if (PrintGCDetails) { + if (size_policy->print_gc_time_limit_would_be_exceeded()) { + if (size_policy->gc_time_limit_exceeded()) { + gclog_or_tty->print_cr(" GC time is exceeding GCTimeLimit " + "of %d%%", GCTimeLimit); + } else { + gclog_or_tty->print_cr(" GC time would exceed GCTimeLimit " + "of %d%%", GCTimeLimit); + } + } + size_policy->set_print_gc_time_limit_would_be_exceeded(false); + } + } + + if (VerifyAfterGC && heap->total_collections() >= VerifyGCStartAt) { + HandleMark hm; // Discard invalid handles created during verification + gclog_or_tty->print(" VerifyAfterGC:"); + Universe::verify(false); + } + + // Re-verify object start arrays + if (VerifyObjectStartArray && + VerifyAfterGC) { + old_gen->verify_object_start_array(); + perm_gen->verify_object_start_array(); + } + + NOT_PRODUCT(ref_processor()->verify_no_references_recorded()); + + if (PrintHeapAtGC) { + Universe::print_heap_after_gc(); + } +} + +bool PSMarkSweep::absorb_live_data_from_eden(PSAdaptiveSizePolicy* size_policy, + PSYoungGen* young_gen, + PSOldGen* old_gen) { + MutableSpace* const eden_space = young_gen->eden_space(); + assert(!eden_space->is_empty(), "eden must be non-empty"); + assert(young_gen->virtual_space()->alignment() == + old_gen->virtual_space()->alignment(), "alignments do not match"); + + if (!(UseAdaptiveSizePolicy && UseAdaptiveGCBoundary)) { + return false; + } + + // Both generations must be completely committed. + if (young_gen->virtual_space()->uncommitted_size() != 0) { + return false; + } + if (old_gen->virtual_space()->uncommitted_size() != 0) { + return false; + } + + // Figure out how much to take from eden. Include the average amount promoted + // in the total; otherwise the next young gen GC will simply bail out to a + // full GC. + const size_t alignment = old_gen->virtual_space()->alignment(); + const size_t eden_used = eden_space->used_in_bytes(); + const size_t promoted = (size_t)(size_policy->avg_promoted()->padded_average()); + const size_t absorb_size = align_size_up(eden_used + promoted, alignment); + const size_t eden_capacity = eden_space->capacity_in_bytes(); + + if (absorb_size >= eden_capacity) { + return false; // Must leave some space in eden. + } + + const size_t new_young_size = young_gen->capacity_in_bytes() - absorb_size; + if (new_young_size < young_gen->min_gen_size()) { + return false; // Respect young gen minimum size. + } + + if (TraceAdaptiveGCBoundary && Verbose) { + gclog_or_tty->print(" absorbing " SIZE_FORMAT "K: " + "eden " SIZE_FORMAT "K->" SIZE_FORMAT "K " + "from " SIZE_FORMAT "K, to " SIZE_FORMAT "K " + "young_gen " SIZE_FORMAT "K->" SIZE_FORMAT "K ", + absorb_size / K, + eden_capacity / K, (eden_capacity - absorb_size) / K, + young_gen->from_space()->used_in_bytes() / K, + young_gen->to_space()->used_in_bytes() / K, + young_gen->capacity_in_bytes() / K, new_young_size / K); + } + + // Fill the unused part of the old gen. + MutableSpace* const old_space = old_gen->object_space(); + MemRegion old_gen_unused(old_space->top(), old_space->end()); + + // If the unused part of the old gen cannot be filled, skip + // absorbing eden. + if (old_gen_unused.word_size() < SharedHeap::min_fill_size()) { + return false; + } + + if (!old_gen_unused.is_empty()) { + SharedHeap::fill_region_with_object(old_gen_unused); + } + + // Take the live data from eden and set both top and end in the old gen to + // eden top. (Need to set end because reset_after_change() mangles the region + // from end to virtual_space->high() in debug builds). + HeapWord* const new_top = eden_space->top(); + old_gen->virtual_space()->expand_into(young_gen->virtual_space(), + absorb_size); + young_gen->reset_after_change(); + old_space->set_top(new_top); + old_space->set_end(new_top); + old_gen->reset_after_change(); + + // Update the object start array for the filler object and the data from eden. + ObjectStartArray* const start_array = old_gen->start_array(); + HeapWord* const start = old_gen_unused.start(); + for (HeapWord* addr = start; addr < new_top; addr += oop(addr)->size()) { + start_array->allocate_block(addr); + } + + // Could update the promoted average here, but it is not typically updated at + // full GCs and the value to use is unclear. Something like + // + // cur_promoted_avg + absorb_size / number_of_scavenges_since_last_full_gc. + + size_policy->set_bytes_absorbed_from_eden(absorb_size); + return true; +} + +void PSMarkSweep::allocate_stacks() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + PSYoungGen* young_gen = heap->young_gen(); + + MutableSpace* to_space = young_gen->to_space(); + _preserved_marks = (PreservedMark*)to_space->top(); + _preserved_count = 0; + + // We want to calculate the size in bytes first. + _preserved_count_max = pointer_delta(to_space->end(), to_space->top(), sizeof(jbyte)); + // Now divide by the size of a PreservedMark + _preserved_count_max /= sizeof(PreservedMark); + + _preserved_mark_stack = NULL; + _preserved_oop_stack = NULL; + + _marking_stack = new (ResourceObj::C_HEAP) GrowableArray(4000, true); + + int size = SystemDictionary::number_of_classes() * 2; + _revisit_klass_stack = new (ResourceObj::C_HEAP) GrowableArray(size, true); +} + + +void PSMarkSweep::deallocate_stacks() { + if (_preserved_oop_stack) { + delete _preserved_mark_stack; + _preserved_mark_stack = NULL; + delete _preserved_oop_stack; + _preserved_oop_stack = NULL; + } + + delete _marking_stack; + delete _revisit_klass_stack; +} + +void PSMarkSweep::mark_sweep_phase1(bool clear_all_softrefs) { + // Recursively traverse all live objects and mark them + EventMark m("1 mark object"); + TraceTime tm("phase 1", PrintGCDetails && Verbose, true, gclog_or_tty); + trace(" 1"); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + // General strong roots. + Universe::oops_do(mark_and_push_closure()); + ReferenceProcessor::oops_do(mark_and_push_closure()); + JNIHandles::oops_do(mark_and_push_closure()); // Global (strong) JNI handles + Threads::oops_do(mark_and_push_closure()); + ObjectSynchronizer::oops_do(mark_and_push_closure()); + FlatProfiler::oops_do(mark_and_push_closure()); + Management::oops_do(mark_and_push_closure()); + JvmtiExport::oops_do(mark_and_push_closure()); + SystemDictionary::always_strong_oops_do(mark_and_push_closure()); + vmSymbols::oops_do(mark_and_push_closure()); + + // Flush marking stack. + follow_stack(); + + // Process reference objects found during marking + + // Skipping the reference processing for VerifyParallelOldWithMarkSweep + // affects the marking (makes it different). + { + ReferencePolicy *soft_ref_policy; + if (clear_all_softrefs) { + soft_ref_policy = new AlwaysClearPolicy(); + } else { +#ifdef COMPILER2 + soft_ref_policy = new LRUMaxHeapPolicy(); +#else + soft_ref_policy = new LRUCurrentHeapPolicy(); +#endif // COMPILER2 + } + assert(soft_ref_policy != NULL,"No soft reference policy"); + ref_processor()->process_discovered_references( + soft_ref_policy, is_alive_closure(), mark_and_push_closure(), + follow_stack_closure(), NULL); + } + + // Follow system dictionary roots and unload classes + bool purged_class = SystemDictionary::do_unloading(is_alive_closure()); + + // Follow code cache roots + CodeCache::do_unloading(is_alive_closure(), mark_and_push_closure(), + purged_class); + follow_stack(); // Flush marking stack + + // Update subklass/sibling/implementor links of live klasses + follow_weak_klass_links(); + assert(_marking_stack->is_empty(), "just drained"); + + // Visit symbol and interned string tables and delete unmarked oops + SymbolTable::unlink(is_alive_closure()); + StringTable::unlink(is_alive_closure()); + + assert(_marking_stack->is_empty(), "stack should be empty by now"); +} + + +void PSMarkSweep::mark_sweep_phase2() { + EventMark m("2 compute new addresses"); + TraceTime tm("phase 2", PrintGCDetails && Verbose, true, gclog_or_tty); + trace("2"); + + // Now all live objects are marked, compute the new object addresses. + + // It is imperative that we traverse perm_gen LAST. If dead space is + // allowed a range of dead object may get overwritten by a dead int + // array. If perm_gen is not traversed last a klassOop may get + // overwritten. This is fine since it is dead, but if the class has dead + // instances we have to skip them, and in order to find their size we + // need the klassOop! + // + // It is not required that we traverse spaces in the same order in + // phase2, phase3 and phase4, but the ValidateMarkSweep live oops + // tracking expects us to do so. See comment under phase4. + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + PSOldGen* old_gen = heap->old_gen(); + PSPermGen* perm_gen = heap->perm_gen(); + + // Begin compacting into the old gen + PSMarkSweepDecorator::set_destination_decorator_tenured(); + + // This will also compact the young gen spaces. + old_gen->precompact(); + + // Compact the perm gen into the perm gen + PSMarkSweepDecorator::set_destination_decorator_perm_gen(); + + perm_gen->precompact(); +} + +// This should be moved to the shared markSweep code! +class PSAlwaysTrueClosure: public BoolObjectClosure { +public: + void do_object(oop p) { ShouldNotReachHere(); } + bool do_object_b(oop p) { return true; } +}; +static PSAlwaysTrueClosure always_true; + +void PSMarkSweep::mark_sweep_phase3() { + // Adjust the pointers to reflect the new locations + EventMark m("3 adjust pointers"); + TraceTime tm("phase 3", PrintGCDetails && Verbose, true, gclog_or_tty); + trace("3"); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + PSYoungGen* young_gen = heap->young_gen(); + PSOldGen* old_gen = heap->old_gen(); + PSPermGen* perm_gen = heap->perm_gen(); + + // General strong roots. + Universe::oops_do(adjust_root_pointer_closure()); + ReferenceProcessor::oops_do(adjust_root_pointer_closure()); + JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles + Threads::oops_do(adjust_root_pointer_closure()); + ObjectSynchronizer::oops_do(adjust_root_pointer_closure()); + FlatProfiler::oops_do(adjust_root_pointer_closure()); + Management::oops_do(adjust_root_pointer_closure()); + JvmtiExport::oops_do(adjust_root_pointer_closure()); + // SO_AllClasses + SystemDictionary::oops_do(adjust_root_pointer_closure()); + vmSymbols::oops_do(adjust_root_pointer_closure()); + + // Now adjust pointers in remaining weak roots. (All of which should + // have been cleared if they pointed to non-surviving objects.) + // Global (weak) JNI handles + JNIHandles::weak_oops_do(&always_true, adjust_root_pointer_closure()); + + CodeCache::oops_do(adjust_pointer_closure()); + SymbolTable::oops_do(adjust_root_pointer_closure()); + StringTable::oops_do(adjust_root_pointer_closure()); + ref_processor()->weak_oops_do(adjust_root_pointer_closure()); + PSScavenge::reference_processor()->weak_oops_do(adjust_root_pointer_closure()); + + adjust_marks(); + + young_gen->adjust_pointers(); + old_gen->adjust_pointers(); + perm_gen->adjust_pointers(); +} + +void PSMarkSweep::mark_sweep_phase4() { + EventMark m("4 compact heap"); + TraceTime tm("phase 4", PrintGCDetails && Verbose, true, gclog_or_tty); + trace("4"); + + // All pointers are now adjusted, move objects accordingly + + // It is imperative that we traverse perm_gen first in phase4. All + // classes must be allocated earlier than their instances, and traversing + // perm_gen first makes sure that all klassOops have moved to their new + // location before any instance does a dispatch through it's klass! + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + PSYoungGen* young_gen = heap->young_gen(); + PSOldGen* old_gen = heap->old_gen(); + PSPermGen* perm_gen = heap->perm_gen(); + + perm_gen->compact(); + old_gen->compact(); + young_gen->compact(); +} + +jlong PSMarkSweep::millis_since_last_gc() { + jlong ret_val = os::javaTimeMillis() - _time_of_last_gc; + // XXX See note in genCollectedHeap::millis_since_last_gc(). + if (ret_val < 0) { + NOT_PRODUCT(warning("time warp: %d", ret_val);) + return 0; + } + return ret_val; +} + +void PSMarkSweep::reset_millis_since_last_gc() { + _time_of_last_gc = os::javaTimeMillis(); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp new file mode 100644 index 00000000000..d60ca960aac --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp @@ -0,0 +1,85 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class PSAdaptiveSizePolicy; +class PSYoungGen; +class PSOldGen; + +class PSMarkSweep : public MarkSweep { + private: + static elapsedTimer _accumulated_time; + static unsigned int _total_invocations; + static jlong _time_of_last_gc; // ms + static CollectorCounters* _counters; + + // Closure accessors + static OopClosure* mark_and_push_closure() { return &MarkSweep::mark_and_push_closure; } + static VoidClosure* follow_stack_closure() { return (VoidClosure*)&MarkSweep::follow_stack_closure; } + static OopClosure* adjust_pointer_closure() { return (OopClosure*)&MarkSweep::adjust_pointer_closure; } + static OopClosure* adjust_root_pointer_closure() { return (OopClosure*)&MarkSweep::adjust_root_pointer_closure; } + static BoolObjectClosure* is_alive_closure() { return (BoolObjectClosure*)&MarkSweep::is_alive; } + + debug_only(public:) // Used for PSParallelCompact debugging + // Mark live objects + static void mark_sweep_phase1(bool clear_all_softrefs); + // Calculate new addresses + static void mark_sweep_phase2(); + debug_only(private:) // End used for PSParallelCompact debugging + // Update pointers + static void mark_sweep_phase3(); + // Move objects to new positions + static void mark_sweep_phase4(); + + debug_only(public:) // Used for PSParallelCompact debugging + // Temporary data structures for traversal and storing/restoring marks + static void allocate_stacks(); + static void deallocate_stacks(); + static void set_ref_processor(ReferenceProcessor* rp) { // delete this method + _ref_processor = rp; + } + debug_only(private:) // End used for PSParallelCompact debugging + + // If objects are left in eden after a collection, try to move the boundary + // and absorb them into the old gen. Returns true if eden was emptied. + static bool absorb_live_data_from_eden(PSAdaptiveSizePolicy* size_policy, + PSYoungGen* young_gen, + PSOldGen* old_gen); + + // Reset time since last full gc + static void reset_millis_since_last_gc(); + + public: + static void invoke(bool clear_all_softrefs); + static void invoke_no_policy(bool clear_all_softrefs); + + static void initialize(); + + // Public accessors + static elapsedTimer* accumulated_time() { return &_accumulated_time; } + static unsigned int total_invocations() { return _total_invocations; } + static CollectorCounters* counters() { return _counters; } + + // Time since last full gc (in milliseconds) + static jlong millis_since_last_gc(); +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweepDecorator.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweepDecorator.cpp new file mode 100644 index 00000000000..3afd47d0582 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweepDecorator.cpp @@ -0,0 +1,446 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_psMarkSweepDecorator.cpp.incl" + +PSMarkSweepDecorator* PSMarkSweepDecorator::_destination_decorator = NULL; + + +void PSMarkSweepDecorator::set_destination_decorator_tenured() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + _destination_decorator = heap->old_gen()->object_mark_sweep(); +} + +void PSMarkSweepDecorator::set_destination_decorator_perm_gen() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + _destination_decorator = heap->perm_gen()->object_mark_sweep(); +} + +void PSMarkSweepDecorator::advance_destination_decorator() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + assert(_destination_decorator != NULL, "Sanity"); + guarantee(_destination_decorator != heap->perm_gen()->object_mark_sweep(), "Cannot advance perm gen decorator"); + + PSMarkSweepDecorator* first = heap->old_gen()->object_mark_sweep(); + PSMarkSweepDecorator* second = heap->young_gen()->eden_mark_sweep(); + PSMarkSweepDecorator* third = heap->young_gen()->from_mark_sweep(); + PSMarkSweepDecorator* fourth = heap->young_gen()->to_mark_sweep(); + + if ( _destination_decorator == first ) { + _destination_decorator = second; + } else if ( _destination_decorator == second ) { + _destination_decorator = third; + } else if ( _destination_decorator == third ) { + _destination_decorator = fourth; + } else { + fatal("PSMarkSweep attempting to advance past last compaction area"); + } +} + +PSMarkSweepDecorator* PSMarkSweepDecorator::destination_decorator() { + assert(_destination_decorator != NULL, "Sanity"); + + return _destination_decorator; +} + +// FIX ME FIX ME FIX ME FIX ME!!!!!!!!! +// The object forwarding code is duplicated. Factor this out!!!!! +// +// This method "precompacts" objects inside its space to dest. It places forwarding +// pointers into markOops for use by adjust_pointers. If "dest" should overflow, we +// finish by compacting into our own space. + +void PSMarkSweepDecorator::precompact() { + // Reset our own compact top. + set_compaction_top(space()->bottom()); + + /* We allow some amount of garbage towards the bottom of the space, so + * we don't start compacting before there is a significant gain to be made. + * Occasionally, we want to ensure a full compaction, which is determined + * by the MarkSweepAlwaysCompactCount parameter. This is a significant + * performance improvement! + */ + bool skip_dead = ((PSMarkSweep::total_invocations() % MarkSweepAlwaysCompactCount) != 0); + + ssize_t allowed_deadspace = 0; + if (skip_dead) { + int ratio = allowed_dead_ratio(); + allowed_deadspace = (space()->capacity_in_bytes() * ratio / 100) / HeapWordSize; + } + + // Fetch the current destination decorator + PSMarkSweepDecorator* dest = destination_decorator(); + ObjectStartArray* start_array = dest->start_array(); + + HeapWord* compact_top = dest->compaction_top(); + HeapWord* compact_end = dest->space()->end(); + + HeapWord* q = space()->bottom(); + HeapWord* t = space()->top(); + + HeapWord* end_of_live= q; /* One byte beyond the last byte of the last + live object. */ + HeapWord* first_dead = space()->end(); /* The first dead object. */ + LiveRange* liveRange = NULL; /* The current live range, recorded in the + first header of preceding free area. */ + _first_dead = first_dead; + + const intx interval = PrefetchScanIntervalInBytes; + + while (q < t) { + assert(oop(q)->mark()->is_marked() || oop(q)->mark()->is_unlocked() || + oop(q)->mark()->has_bias_pattern(), + "these are the only valid states during a mark sweep"); + if (oop(q)->is_gc_marked()) { + /* prefetch beyond q */ + Prefetch::write(q, interval); + size_t size = oop(q)->size(); + + size_t compaction_max_size = pointer_delta(compact_end, compact_top); + + // This should only happen if a space in the young gen overflows the + // old gen. If that should happen, we null out the start_array, because + // the young spaces are not covered by one. + while(size > compaction_max_size) { + // First record the last compact_top + dest->set_compaction_top(compact_top); + + // Advance to the next compaction decorator + advance_destination_decorator(); + dest = destination_decorator(); + + // Update compaction info + start_array = dest->start_array(); + compact_top = dest->compaction_top(); + compact_end = dest->space()->end(); + assert(compact_top == dest->space()->bottom(), "Advanced to space already in use"); + assert(compact_end > compact_top, "Must always be space remaining"); + compaction_max_size = + pointer_delta(compact_end, compact_top); + } + + // store the forwarding pointer into the mark word + if (q != compact_top) { + oop(q)->forward_to(oop(compact_top)); + assert(oop(q)->is_gc_marked(), "encoding the pointer should preserve the mark"); + } else { + // Don't clear the mark since it's confuses parallel old + // verification. + if (!UseParallelOldGC || !VerifyParallelOldWithMarkSweep) { + // if the object isn't moving we can just set the mark to the default + // mark and handle it specially later on. + oop(q)->init_mark(); + } + assert(oop(q)->forwardee() == NULL, "should be forwarded to NULL"); + } + + // Update object start array + if (!UseParallelOldGC || !VerifyParallelOldWithMarkSweep) { + if (start_array) + start_array->allocate_block(compact_top); + } + + debug_only(MarkSweep::register_live_oop(oop(q), size)); + compact_top += size; + assert(compact_top <= dest->space()->end(), + "Exceeding space in destination"); + + q += size; + end_of_live = q; + } else { + /* run over all the contiguous dead objects */ + HeapWord* end = q; + do { + /* prefetch beyond end */ + Prefetch::write(end, interval); + end += oop(end)->size(); + } while (end < t && (!oop(end)->is_gc_marked())); + + /* see if we might want to pretend this object is alive so that + * we don't have to compact quite as often. + */ + if (allowed_deadspace > 0 && q == compact_top) { + size_t sz = pointer_delta(end, q); + if (insert_deadspace(allowed_deadspace, q, sz)) { + size_t compaction_max_size = pointer_delta(compact_end, compact_top); + + // This should only happen if a space in the young gen overflows the + // old gen. If that should happen, we null out the start_array, because + // the young spaces are not covered by one. + while (sz > compaction_max_size) { + // First record the last compact_top + dest->set_compaction_top(compact_top); + + // Advance to the next compaction decorator + advance_destination_decorator(); + dest = destination_decorator(); + + // Update compaction info + start_array = dest->start_array(); + compact_top = dest->compaction_top(); + compact_end = dest->space()->end(); + assert(compact_top == dest->space()->bottom(), "Advanced to space already in use"); + assert(compact_end > compact_top, "Must always be space remaining"); + compaction_max_size = + pointer_delta(compact_end, compact_top); + } + + // store the forwarding pointer into the mark word + if (q != compact_top) { + oop(q)->forward_to(oop(compact_top)); + assert(oop(q)->is_gc_marked(), "encoding the pointer should preserve the mark"); + } else { + // if the object isn't moving we can just set the mark to the default + // Don't clear the mark since it's confuses parallel old + // verification. + if (!UseParallelOldGC || !VerifyParallelOldWithMarkSweep) { + // mark and handle it specially later on. + oop(q)->init_mark(); + } + assert(oop(q)->forwardee() == NULL, "should be forwarded to NULL"); + } + + if (!UseParallelOldGC || !VerifyParallelOldWithMarkSweep) { + // Update object start array + if (start_array) + start_array->allocate_block(compact_top); + } + + debug_only(MarkSweep::register_live_oop(oop(q), sz)); + compact_top += sz; + assert(compact_top <= dest->space()->end(), + "Exceeding space in destination"); + + q = end; + end_of_live = end; + continue; + } + } + + /* for the previous LiveRange, record the end of the live objects. */ + if (liveRange) { + liveRange->set_end(q); + } + + /* record the current LiveRange object. + * liveRange->start() is overlaid on the mark word. + */ + liveRange = (LiveRange*)q; + liveRange->set_start(end); + liveRange->set_end(end); + + /* see if this is the first dead region. */ + if (q < first_dead) { + first_dead = q; + } + + /* move on to the next object */ + q = end; + } + } + + assert(q == t, "just checking"); + if (liveRange != NULL) { + liveRange->set_end(q); + } + _end_of_live = end_of_live; + if (end_of_live < first_dead) { + first_dead = end_of_live; + } + _first_dead = first_dead; + + // Update compaction top + dest->set_compaction_top(compact_top); +} + +bool PSMarkSweepDecorator::insert_deadspace(ssize_t& allowed_deadspace_words, + HeapWord* q, size_t deadlength) { + allowed_deadspace_words -= deadlength; + if (allowed_deadspace_words >= 0) { + oop(q)->set_mark(markOopDesc::prototype()->set_marked()); + const size_t aligned_min_int_array_size = + align_object_size(typeArrayOopDesc::header_size(T_INT)); + if (deadlength >= aligned_min_int_array_size) { + oop(q)->set_klass(Universe::intArrayKlassObj()); + assert(((deadlength - aligned_min_int_array_size) * (HeapWordSize/sizeof(jint))) < (size_t)max_jint, + "deadspace too big for Arrayoop"); + typeArrayOop(q)->set_length((int)((deadlength - aligned_min_int_array_size) + * (HeapWordSize/sizeof(jint)))); + } else { + assert((int) deadlength == instanceOopDesc::header_size(), + "size for smallest fake dead object doesn't match"); + oop(q)->set_klass(SystemDictionary::object_klass()); + } + assert((int) deadlength == oop(q)->size(), + "make sure size for fake dead object match"); + // Recall that we required "q == compaction_top". + return true; + } else { + allowed_deadspace_words = 0; + return false; + } +} + +void PSMarkSweepDecorator::adjust_pointers() { + // adjust all the interior pointers to point at the new locations of objects + // Used by MarkSweep::mark_sweep_phase3() + + HeapWord* q = space()->bottom(); + HeapWord* t = _end_of_live; // Established by "prepare_for_compaction". + + assert(_first_dead <= _end_of_live, "Stands to reason, no?"); + + if (q < t && _first_dead > q && + !oop(q)->is_gc_marked()) { + // we have a chunk of the space which hasn't moved and we've + // reinitialized the mark word during the previous pass, so we can't + // use is_gc_marked for the traversal. + HeapWord* end = _first_dead; + + while (q < end) { + debug_only(MarkSweep::track_interior_pointers(oop(q))); + + // point all the oops to the new location + size_t size = oop(q)->adjust_pointers(); + + debug_only(MarkSweep::check_interior_pointers()); + + debug_only(MarkSweep::validate_live_oop(oop(q), size)); + + q += size; + } + + if (_first_dead == t) { + q = t; + } else { + // $$$ This is funky. Using this to read the previously written + // LiveRange. See also use below. + q = (HeapWord*)oop(_first_dead)->mark()->decode_pointer(); + } + } + const intx interval = PrefetchScanIntervalInBytes; + + debug_only(HeapWord* prev_q = NULL); + while (q < t) { + // prefetch beyond q + Prefetch::write(q, interval); + if (oop(q)->is_gc_marked()) { + // q is alive + debug_only(MarkSweep::track_interior_pointers(oop(q))); + // point all the oops to the new location + size_t size = oop(q)->adjust_pointers(); + debug_only(MarkSweep::check_interior_pointers()); + debug_only(MarkSweep::validate_live_oop(oop(q), size)); + debug_only(prev_q = q); + q += size; + } else { + // q is not a live object, so its mark should point at the next + // live object + debug_only(prev_q = q); + q = (HeapWord*) oop(q)->mark()->decode_pointer(); + assert(q > prev_q, "we should be moving forward through memory"); + } + } + + assert(q == t, "just checking"); +} + +void PSMarkSweepDecorator::compact(bool mangle_free_space ) { + // Copy all live objects to their new location + // Used by MarkSweep::mark_sweep_phase4() + + HeapWord* q = space()->bottom(); + HeapWord* const t = _end_of_live; + debug_only(HeapWord* prev_q = NULL); + + if (q < t && _first_dead > q && + !oop(q)->is_gc_marked()) { +#ifdef ASSERT + // we have a chunk of the space which hasn't moved and we've reinitialized the + // mark word during the previous pass, so we can't use is_gc_marked for the + // traversal. + HeapWord* const end = _first_dead; + + while (q < end) { + size_t size = oop(q)->size(); + assert(!oop(q)->is_gc_marked(), "should be unmarked (special dense prefix handling)"); + debug_only(MarkSweep::live_oop_moved_to(q, size, q)); + debug_only(prev_q = q); + q += size; + } +#endif + + if (_first_dead == t) { + q = t; + } else { + // $$$ Funky + q = (HeapWord*) oop(_first_dead)->mark()->decode_pointer(); + } + } + + const intx scan_interval = PrefetchScanIntervalInBytes; + const intx copy_interval = PrefetchCopyIntervalInBytes; + + while (q < t) { + if (!oop(q)->is_gc_marked()) { + // mark is pointer to next marked oop + debug_only(prev_q = q); + q = (HeapWord*) oop(q)->mark()->decode_pointer(); + assert(q > prev_q, "we should be moving forward through memory"); + } else { + // prefetch beyond q + Prefetch::read(q, scan_interval); + + // size and destination + size_t size = oop(q)->size(); + HeapWord* compaction_top = (HeapWord*)oop(q)->forwardee(); + + // prefetch beyond compaction_top + Prefetch::write(compaction_top, copy_interval); + + // copy object and reinit its mark + debug_only(MarkSweep::live_oop_moved_to(q, size, compaction_top)); + assert(q != compaction_top, "everything in this pass should be moving"); + Copy::aligned_conjoint_words(q, compaction_top, size); + oop(compaction_top)->init_mark(); + assert(oop(compaction_top)->klass() != NULL, "should have a class"); + + debug_only(prev_q = q); + q += size; + } + } + + assert(compaction_top() >= space()->bottom() && compaction_top() <= space()->end(), + "should point inside space"); + space()->set_top(compaction_top()); + + if (mangle_free_space) space()->mangle_unused_area(); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweepDecorator.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweepDecorator.hpp new file mode 100644 index 00000000000..4c7cfdcb55c --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweepDecorator.hpp @@ -0,0 +1,73 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// A PSMarkSweepDecorator is used to add "ParallelScavenge" style mark sweep operations +// to a MutableSpace. +// + +class ObjectStartArray; + +class PSMarkSweepDecorator: public CHeapObj { + private: + static PSMarkSweepDecorator* _destination_decorator; + + protected: + MutableSpace* _space; + ObjectStartArray* _start_array; + HeapWord* _first_dead; + HeapWord* _end_of_live; + HeapWord* _compaction_top; + unsigned int _allowed_dead_ratio; + + bool insert_deadspace(ssize_t& allowed_deadspace_words, HeapWord* q, size_t word_len); + + public: + PSMarkSweepDecorator(MutableSpace* space, ObjectStartArray* start_array, + unsigned int allowed_dead_ratio) : + _space(space), _start_array(start_array), _allowed_dead_ratio(allowed_dead_ratio) { } + + // During a compacting collection, we need to collapse objects into + // spaces in a given order. We want to fill space A, space B, and so + // on. The code that controls that order is in the following methods. + static void set_destination_decorator_tenured(); + static void set_destination_decorator_perm_gen(); + static void advance_destination_decorator(); + static PSMarkSweepDecorator* destination_decorator(); + + // Accessors + MutableSpace* space() { return _space; } + ObjectStartArray* start_array() { return _start_array; } + + HeapWord* compaction_top() { return _compaction_top; } + void set_compaction_top(HeapWord* value) { _compaction_top = value; } + + unsigned int allowed_dead_ratio() { return _allowed_dead_ratio; } + void set_allowed_dead_ratio(unsigned int value) { _allowed_dead_ratio = value; } + + // Work methods + void adjust_pointers(); + void precompact(); + void compact(bool mangle_free_space); +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp new file mode 100644 index 00000000000..f92d291cc4e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp @@ -0,0 +1,464 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_psOldGen.cpp.incl" + +inline const char* PSOldGen::select_name() { + return UseParallelOldGC ? "ParOldGen" : "PSOldGen"; +} + +PSOldGen::PSOldGen(ReservedSpace rs, size_t alignment, + size_t initial_size, size_t min_size, size_t max_size, + const char* perf_data_name, int level): + _name(select_name()), _init_gen_size(initial_size), _min_gen_size(min_size), + _max_gen_size(max_size) +{ + initialize(rs, alignment, perf_data_name, level); +} + +PSOldGen::PSOldGen(size_t initial_size, + size_t min_size, size_t max_size, + const char* perf_data_name, int level): + _name(select_name()), _init_gen_size(initial_size), _min_gen_size(min_size), + _max_gen_size(max_size) +{} + +void PSOldGen::initialize(ReservedSpace rs, size_t alignment, + const char* perf_data_name, int level) { + initialize_virtual_space(rs, alignment); + initialize_work(perf_data_name, level); + // The old gen can grow to gen_size_limit(). _reserve reflects only + // the current maximum that can be committed. + assert(_reserved.byte_size() <= gen_size_limit(), "Consistency check"); +} + +void PSOldGen::initialize_virtual_space(ReservedSpace rs, size_t alignment) { + + _virtual_space = new PSVirtualSpace(rs, alignment); + if (!_virtual_space->expand_by(_init_gen_size)) { + vm_exit_during_initialization("Could not reserve enough space for " + "object heap"); + } +} + +void PSOldGen::initialize_work(const char* perf_data_name, int level) { + // + // Basic memory initialization + // + + MemRegion limit_reserved((HeapWord*)virtual_space()->low_boundary(), + heap_word_size(_max_gen_size)); + assert(limit_reserved.byte_size() == _max_gen_size, + "word vs bytes confusion"); + // + // Object start stuff + // + + start_array()->initialize(limit_reserved); + + _reserved = MemRegion((HeapWord*)virtual_space()->low_boundary(), + (HeapWord*)virtual_space()->high_boundary()); + + // + // Card table stuff + // + + MemRegion cmr((HeapWord*)virtual_space()->low(), + (HeapWord*)virtual_space()->high()); + Universe::heap()->barrier_set()->resize_covered_region(cmr); + + CardTableModRefBS* _ct = (CardTableModRefBS*)Universe::heap()->barrier_set(); + assert (_ct->kind() == BarrierSet::CardTableModRef, "Sanity"); + + // Verify that the start and end of this generation is the start of a card. + // If this wasn't true, a single card could span more than one generation, + // which would cause problems when we commit/uncommit memory, and when we + // clear and dirty cards. + guarantee(_ct->is_card_aligned(_reserved.start()), "generation must be card aligned"); + if (_reserved.end() != Universe::heap()->reserved_region().end()) { + // Don't check at the very end of the heap as we'll assert that we're probing off + // the end if we try. + guarantee(_ct->is_card_aligned(_reserved.end()), "generation must be card aligned"); + } + + // + // ObjectSpace stuff + // + + _object_space = new MutableSpace(); + + if (_object_space == NULL) + vm_exit_during_initialization("Could not allocate an old gen space"); + + object_space()->initialize(cmr, true); + + _object_mark_sweep = new PSMarkSweepDecorator(_object_space, start_array(), MarkSweepDeadRatio); + + if (_object_mark_sweep == NULL) + vm_exit_during_initialization("Could not complete allocation of old generation"); + + // Update the start_array + start_array()->set_covered_region(cmr); + + // Generation Counters, generation 'level', 1 subspace + _gen_counters = new PSGenerationCounters(perf_data_name, level, 1, + virtual_space()); + _space_counters = new SpaceCounters(perf_data_name, 0, + virtual_space()->reserved_size(), + _object_space, _gen_counters); +} + +// Assume that the generation has been allocated if its +// reserved size is not 0. +bool PSOldGen::is_allocated() { + return virtual_space()->reserved_size() != 0; +} + +void PSOldGen::precompact() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + // Reset start array first. + debug_only(if (!UseParallelOldGC || !VerifyParallelOldWithMarkSweep) {) + start_array()->reset(); + debug_only(}) + + object_mark_sweep()->precompact(); + + // Now compact the young gen + heap->young_gen()->precompact(); +} + +void PSOldGen::adjust_pointers() { + object_mark_sweep()->adjust_pointers(); +} + +void PSOldGen::compact() { + object_mark_sweep()->compact(ZapUnusedHeapArea); +} + +void PSOldGen::move_and_update(ParCompactionManager* cm) { + PSParallelCompact::move_and_update(cm, PSParallelCompact::old_space_id); +} + +size_t PSOldGen::contiguous_available() const { + return object_space()->free_in_bytes() + virtual_space()->uncommitted_size(); +} + +// Allocation. We report all successful allocations to the size policy +// Note that the perm gen does not use this method, and should not! +HeapWord* PSOldGen::allocate(size_t word_size, bool is_tlab) { + assert_locked_or_safepoint(Heap_lock); + HeapWord* res = allocate_noexpand(word_size, is_tlab); + + if (res == NULL) { + res = expand_and_allocate(word_size, is_tlab); + } + + // Allocations in the old generation need to be reported + if (res != NULL) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + heap->size_policy()->tenured_allocation(word_size); + } + + return res; +} + +HeapWord* PSOldGen::expand_and_allocate(size_t word_size, bool is_tlab) { + assert(!is_tlab, "TLAB's are not supported in PSOldGen"); + expand(word_size*HeapWordSize); + if (GCExpandToAllocateDelayMillis > 0) { + os::sleep(Thread::current(), GCExpandToAllocateDelayMillis, false); + } + return allocate_noexpand(word_size, is_tlab); +} + +HeapWord* PSOldGen::expand_and_cas_allocate(size_t word_size) { + expand(word_size*HeapWordSize); + if (GCExpandToAllocateDelayMillis > 0) { + os::sleep(Thread::current(), GCExpandToAllocateDelayMillis, false); + } + return cas_allocate_noexpand(word_size); +} + +void PSOldGen::expand(size_t bytes) { + MutexLocker x(ExpandHeap_lock); + const size_t alignment = virtual_space()->alignment(); + size_t aligned_bytes = align_size_up(bytes, alignment); + size_t aligned_expand_bytes = align_size_up(MinHeapDeltaBytes, alignment); + + bool success = false; + if (aligned_expand_bytes > aligned_bytes) { + success = expand_by(aligned_expand_bytes); + } + if (!success) { + success = expand_by(aligned_bytes); + } + if (!success) { + success = expand_to_reserved(); + } + + if (GC_locker::is_active()) { + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("Garbage collection disabled, expanded heap instead"); + } + } +} + +bool PSOldGen::expand_by(size_t bytes) { + assert_lock_strong(ExpandHeap_lock); + assert_locked_or_safepoint(Heap_lock); + bool result = virtual_space()->expand_by(bytes); + if (result) { + post_resize(); + if (UsePerfData) { + _space_counters->update_capacity(); + _gen_counters->update_all(); + } + } + + if (result && Verbose && PrintGC) { + size_t new_mem_size = virtual_space()->committed_size(); + size_t old_mem_size = new_mem_size - bytes; + gclog_or_tty->print_cr("Expanding %s from " SIZE_FORMAT "K by " + SIZE_FORMAT "K to " + SIZE_FORMAT "K", + name(), old_mem_size/K, bytes/K, new_mem_size/K); + } + + return result; +} + +bool PSOldGen::expand_to_reserved() { + assert_lock_strong(ExpandHeap_lock); + assert_locked_or_safepoint(Heap_lock); + + bool result = true; + const size_t remaining_bytes = virtual_space()->uncommitted_size(); + if (remaining_bytes > 0) { + result = expand_by(remaining_bytes); + DEBUG_ONLY(if (!result) warning("grow to reserve failed")); + } + return result; +} + +void PSOldGen::shrink(size_t bytes) { + assert_lock_strong(ExpandHeap_lock); + assert_locked_or_safepoint(Heap_lock); + + size_t size = align_size_down(bytes, virtual_space()->alignment()); + if (size > 0) { + assert_lock_strong(ExpandHeap_lock); + virtual_space()->shrink_by(bytes); + post_resize(); + + if (Verbose && PrintGC) { + size_t new_mem_size = virtual_space()->committed_size(); + size_t old_mem_size = new_mem_size + bytes; + gclog_or_tty->print_cr("Shrinking %s from " SIZE_FORMAT "K by " + SIZE_FORMAT "K to " + SIZE_FORMAT "K", + name(), old_mem_size/K, bytes/K, new_mem_size/K); + } + } +} + +void PSOldGen::resize(size_t desired_free_space) { + const size_t alignment = virtual_space()->alignment(); + const size_t size_before = virtual_space()->committed_size(); + size_t new_size = used_in_bytes() + desired_free_space; + if (new_size < used_in_bytes()) { + // Overflowed the addition. + new_size = gen_size_limit(); + } + // Adjust according to our min and max + new_size = MAX2(MIN2(new_size, gen_size_limit()), min_gen_size()); + + assert(gen_size_limit() >= reserved().byte_size(), "max new size problem?"); + new_size = align_size_up(new_size, alignment); + + const size_t current_size = capacity_in_bytes(); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("AdaptiveSizePolicy::old generation size: " + "desired free: " SIZE_FORMAT " used: " SIZE_FORMAT + " new size: " SIZE_FORMAT " current size " SIZE_FORMAT + " gen limits: " SIZE_FORMAT " / " SIZE_FORMAT, + desired_free_space, used_in_bytes(), new_size, current_size, + gen_size_limit(), min_gen_size()); + } + + if (new_size == current_size) { + // No change requested + return; + } + if (new_size > current_size) { + size_t change_bytes = new_size - current_size; + expand(change_bytes); + } else { + size_t change_bytes = current_size - new_size; + // shrink doesn't grab this lock, expand does. Is that right? + MutexLocker x(ExpandHeap_lock); + shrink(change_bytes); + } + + if (PrintAdaptiveSizePolicy) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + gclog_or_tty->print_cr("AdaptiveSizePolicy::old generation size: " + "collection: %d " + "(" SIZE_FORMAT ") -> (" SIZE_FORMAT ") ", + heap->total_collections(), + size_before, virtual_space()->committed_size()); + } +} + +// NOTE! We need to be careful about resizing. During a GC, multiple +// allocators may be active during heap expansion. If we allow the +// heap resizing to become visible before we have correctly resized +// all heap related data structures, we may cause program failures. +void PSOldGen::post_resize() { + // First construct a memregion representing the new size + MemRegion new_memregion((HeapWord*)virtual_space()->low(), + (HeapWord*)virtual_space()->high()); + size_t new_word_size = new_memregion.word_size(); + + start_array()->set_covered_region(new_memregion); + Universe::heap()->barrier_set()->resize_covered_region(new_memregion); + + // Did we expand? + HeapWord* const virtual_space_high = (HeapWord*) virtual_space()->high(); + if (object_space()->end() < virtual_space_high) { + // We need to mangle the newly expanded area. The memregion spans + // end -> new_end, we assume that top -> end is already mangled. + // This cannot be safely tested for, as allocation may be taking + // place. + MemRegion mangle_region(object_space()->end(), virtual_space_high); + object_space()->mangle_region(mangle_region); + } + + // ALWAYS do this last!! + object_space()->set_end(virtual_space_high); + + assert(new_word_size == heap_word_size(object_space()->capacity_in_bytes()), + "Sanity"); +} + +size_t PSOldGen::gen_size_limit() { + return _max_gen_size; +} + +void PSOldGen::reset_after_change() { + ShouldNotReachHere(); + return; +} + +size_t PSOldGen::available_for_expansion() { + ShouldNotReachHere(); + return 0; +} + +size_t PSOldGen::available_for_contraction() { + ShouldNotReachHere(); + return 0; +} + +void PSOldGen::print() const { print_on(tty);} +void PSOldGen::print_on(outputStream* st) const { + st->print(" %-15s", name()); + if (PrintGCDetails && Verbose) { + st->print(" total " SIZE_FORMAT ", used " SIZE_FORMAT, + capacity_in_bytes(), used_in_bytes()); + } else { + st->print(" total " SIZE_FORMAT "K, used " SIZE_FORMAT "K", + capacity_in_bytes()/K, used_in_bytes()/K); + } + st->print_cr(" [" INTPTR_FORMAT ", " INTPTR_FORMAT ", " INTPTR_FORMAT ")", + virtual_space()->low_boundary(), + virtual_space()->high(), + virtual_space()->high_boundary()); + + st->print(" object"); object_space()->print_on(st); +} + +void PSOldGen::print_used_change(size_t prev_used) const { + gclog_or_tty->print(" [%s:", name()); + gclog_or_tty->print(" " SIZE_FORMAT "K" + "->" SIZE_FORMAT "K" + "(" SIZE_FORMAT "K)", + prev_used / K, used_in_bytes() / K, + capacity_in_bytes() / K); + gclog_or_tty->print("]"); +} + +void PSOldGen::update_counters() { + if (UsePerfData) { + _space_counters->update_all(); + _gen_counters->update_all(); + } +} + +#ifndef PRODUCT + +void PSOldGen::space_invariants() { + assert(object_space()->end() == (HeapWord*) virtual_space()->high(), + "Space invariant"); + assert(object_space()->bottom() == (HeapWord*) virtual_space()->low(), + "Space invariant"); + assert(virtual_space()->low_boundary() <= virtual_space()->low(), + "Space invariant"); + assert(virtual_space()->high_boundary() >= virtual_space()->high(), + "Space invariant"); + assert(virtual_space()->low_boundary() == (char*) _reserved.start(), + "Space invariant"); + assert(virtual_space()->high_boundary() == (char*) _reserved.end(), + "Space invariant"); + assert(virtual_space()->committed_size() <= virtual_space()->reserved_size(), + "Space invariant"); +} +#endif + +void PSOldGen::verify(bool allow_dirty) { + object_space()->verify(allow_dirty); +} +class VerifyObjectStartArrayClosure : public ObjectClosure { + PSOldGen* _gen; + ObjectStartArray* _start_array; + + public: + VerifyObjectStartArrayClosure(PSOldGen* gen, ObjectStartArray* start_array) : + _gen(gen), _start_array(start_array) { } + + virtual void do_object(oop obj) { + HeapWord* test_addr = (HeapWord*)obj + 1; + guarantee(_start_array->object_start(test_addr) == (HeapWord*)obj, "ObjectStartArray cannot find start of object"); + guarantee(_start_array->is_block_allocated((HeapWord*)obj), "ObjectStartArray missing block allocation"); + } +}; + +void PSOldGen::verify_object_start_array() { + VerifyObjectStartArrayClosure check( this, &_start_array ); + object_iterate(&check); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.hpp new file mode 100644 index 00000000000..a7a68bdf68e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.hpp @@ -0,0 +1,188 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class PSMarkSweepDecorator; + +class PSOldGen : public CHeapObj { + friend class VMStructs; + friend class PSPromotionManager; // Uses the cas_allocate methods + friend class ParallelScavengeHeap; + friend class AdjoiningGenerations; + + protected: + MemRegion _reserved; // Used for simple containment tests + PSVirtualSpace* _virtual_space; // Controls mapping and unmapping of virtual mem + ObjectStartArray _start_array; // Keeps track of where objects start in a 512b block + MutableSpace* _object_space; // Where all the objects live + PSMarkSweepDecorator* _object_mark_sweep; // The mark sweep view of _object_space + const char* const _name; // Name of this generation. + + // Performance Counters + PSGenerationCounters* _gen_counters; + SpaceCounters* _space_counters; + + // Sizing information, in bytes, set in constructor + const size_t _init_gen_size; + const size_t _min_gen_size; + const size_t _max_gen_size; + + // Used when initializing the _name field. + static inline const char* select_name(); + + HeapWord* allocate_noexpand(size_t word_size, bool is_tlab) { + // We assume the heap lock is held here. + assert(!is_tlab, "Does not support TLAB allocation"); + assert_locked_or_safepoint(Heap_lock); + HeapWord* res = object_space()->allocate(word_size); + if (res != NULL) { + _start_array.allocate_block(res); + } + return res; + } + + // Support for MT garbage collection. CAS allocation is lower overhead than grabbing + // and releasing the heap lock, which is held during gc's anyway. This method is not + // safe for use at the same time as allocate_noexpand()! + HeapWord* cas_allocate_noexpand(size_t word_size) { + assert(SafepointSynchronize::is_at_safepoint(), "Must only be called at safepoint") + HeapWord* res = object_space()->cas_allocate(word_size); + if (res != NULL) { + _start_array.allocate_block(res); + } + return res; + } + + // Support for MT garbage collection. See above comment. + HeapWord* cas_allocate(size_t word_size) { + HeapWord* res = cas_allocate_noexpand(word_size); + return (res == NULL) ? expand_and_cas_allocate(word_size) : res; + } + + HeapWord* expand_and_allocate(size_t word_size, bool is_tlab); + HeapWord* expand_and_cas_allocate(size_t word_size); + void expand(size_t bytes); + bool expand_by(size_t bytes); + bool expand_to_reserved(); + + void shrink(size_t bytes); + + void post_resize(); + + public: + // Initialize the generation. + PSOldGen(ReservedSpace rs, size_t alignment, + size_t initial_size, size_t min_size, size_t max_size, + const char* perf_data_name, int level); + + PSOldGen(size_t initial_size, size_t min_size, size_t max_size, + const char* perf_data_name, int level); + + void initialize(ReservedSpace rs, size_t alignment, + const char* perf_data_name, int level); + void initialize_virtual_space(ReservedSpace rs, size_t alignment); + void initialize_work(const char* perf_data_name, int level); + + MemRegion reserved() const { return _reserved; } + virtual size_t max_gen_size() { return _max_gen_size; } + size_t min_gen_size() { return _min_gen_size; } + + // Returns limit on the maximum size of the generation. This + // is the same as _max_gen_size for PSOldGen but need not be + // for a derived class. + virtual size_t gen_size_limit(); + + bool is_in(const void* p) const { + return _virtual_space->contains((void *)p); + } + + bool is_in_reserved(const void* p) const { + return reserved().contains(p); + } + + MutableSpace* object_space() const { return _object_space; } + PSMarkSweepDecorator* object_mark_sweep() const { return _object_mark_sweep; } + ObjectStartArray* start_array() { return &_start_array; } + PSVirtualSpace* virtual_space() const { return _virtual_space;} + + // Has the generation been successfully allocated? + bool is_allocated(); + + // MarkSweep methods + virtual void precompact(); + void adjust_pointers(); + void compact(); + + // Parallel old + virtual void move_and_update(ParCompactionManager* cm); + + // Size info + size_t capacity_in_bytes() const { return object_space()->capacity_in_bytes(); } + size_t used_in_bytes() const { return object_space()->used_in_bytes(); } + size_t free_in_bytes() const { return object_space()->free_in_bytes(); } + + size_t capacity_in_words() const { return object_space()->capacity_in_words(); } + size_t used_in_words() const { return object_space()->used_in_words(); } + size_t free_in_words() const { return object_space()->free_in_words(); } + + // Includes uncommitted memory + size_t contiguous_available() const; + + bool is_maximal_no_gc() const { + return virtual_space()->uncommitted_size() == 0; + } + + // Calculating new sizes + void resize(size_t desired_free_space); + + // Allocation. We report all successful allocations to the size policy + // Note that the perm gen does not use this method, and should not! + HeapWord* allocate(size_t word_size, bool is_tlab); + + // Iteration. + void oop_iterate(OopClosure* cl) { object_space()->oop_iterate(cl); } + void object_iterate(ObjectClosure* cl) { object_space()->object_iterate(cl); } + + // Debugging - do not use for time critical operations + virtual void print() const; + virtual void print_on(outputStream* st) const; + void print_used_change(size_t prev_used) const; + + void verify(bool allow_dirty); + void verify_object_start_array(); + + // These should not used + virtual void reset_after_change(); + + // These should not used + virtual size_t available_for_expansion(); + virtual size_t available_for_contraction(); + + void space_invariants() PRODUCT_RETURN; + + // Performace Counter support + void update_counters(); + + // Printing support + virtual const char* name() const { return _name; } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp new file mode 100644 index 00000000000..81c2d47a647 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp @@ -0,0 +1,3752 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_psParallelCompact.cpp.incl" + +#include + +// All sizes are in HeapWords. +const size_t ParallelCompactData::Log2ChunkSize = 9; // 512 words +const size_t ParallelCompactData::ChunkSize = (size_t)1 << Log2ChunkSize; +const size_t ParallelCompactData::ChunkSizeBytes = ChunkSize << LogHeapWordSize; +const size_t ParallelCompactData::ChunkSizeOffsetMask = ChunkSize - 1; +const size_t ParallelCompactData::ChunkAddrOffsetMask = ChunkSizeBytes - 1; +const size_t ParallelCompactData::ChunkAddrMask = ~ChunkAddrOffsetMask; + +// 32-bit: 128 words covers 4 bitmap words +// 64-bit: 128 words covers 2 bitmap words +const size_t ParallelCompactData::Log2BlockSize = 7; // 128 words +const size_t ParallelCompactData::BlockSize = (size_t)1 << Log2BlockSize; +const size_t ParallelCompactData::BlockOffsetMask = BlockSize - 1; +const size_t ParallelCompactData::BlockMask = ~BlockOffsetMask; + +const size_t ParallelCompactData::BlocksPerChunk = ChunkSize / BlockSize; + +const ParallelCompactData::ChunkData::chunk_sz_t +ParallelCompactData::ChunkData::dc_shift = 27; + +const ParallelCompactData::ChunkData::chunk_sz_t +ParallelCompactData::ChunkData::dc_mask = ~0U << dc_shift; + +const ParallelCompactData::ChunkData::chunk_sz_t +ParallelCompactData::ChunkData::dc_one = 0x1U << dc_shift; + +const ParallelCompactData::ChunkData::chunk_sz_t +ParallelCompactData::ChunkData::los_mask = ~dc_mask; + +const ParallelCompactData::ChunkData::chunk_sz_t +ParallelCompactData::ChunkData::dc_claimed = 0x8U << dc_shift; + +const ParallelCompactData::ChunkData::chunk_sz_t +ParallelCompactData::ChunkData::dc_completed = 0xcU << dc_shift; + +#ifdef ASSERT +short ParallelCompactData::BlockData::_cur_phase = 0; +#endif + +SpaceInfo PSParallelCompact::_space_info[PSParallelCompact::last_space_id]; +bool PSParallelCompact::_print_phases = false; + +ReferenceProcessor* PSParallelCompact::_ref_processor = NULL; +klassOop PSParallelCompact::_updated_int_array_klass_obj = NULL; + +double PSParallelCompact::_dwl_mean; +double PSParallelCompact::_dwl_std_dev; +double PSParallelCompact::_dwl_first_term; +double PSParallelCompact::_dwl_adjustment; +#ifdef ASSERT +bool PSParallelCompact::_dwl_initialized = false; +#endif // #ifdef ASSERT + +#ifdef VALIDATE_MARK_SWEEP +GrowableArray* PSParallelCompact::_root_refs_stack = NULL; +GrowableArray * PSParallelCompact::_live_oops = NULL; +GrowableArray * PSParallelCompact::_live_oops_moved_to = NULL; +GrowableArray* PSParallelCompact::_live_oops_size = NULL; +size_t PSParallelCompact::_live_oops_index = 0; +size_t PSParallelCompact::_live_oops_index_at_perm = 0; +GrowableArray* PSParallelCompact::_other_refs_stack = NULL; +GrowableArray* PSParallelCompact::_adjusted_pointers = NULL; +bool PSParallelCompact::_pointer_tracking = false; +bool PSParallelCompact::_root_tracking = true; + +GrowableArray* PSParallelCompact::_cur_gc_live_oops = NULL; +GrowableArray* PSParallelCompact::_cur_gc_live_oops_moved_to = NULL; +GrowableArray * PSParallelCompact::_cur_gc_live_oops_size = NULL; +GrowableArray* PSParallelCompact::_last_gc_live_oops = NULL; +GrowableArray* PSParallelCompact::_last_gc_live_oops_moved_to = NULL; +GrowableArray * PSParallelCompact::_last_gc_live_oops_size = NULL; +#endif + +// XXX beg - verification code; only works while we also mark in object headers +static void +verify_mark_bitmap(ParMarkBitMap& _mark_bitmap) +{ + ParallelScavengeHeap* heap = PSParallelCompact::gc_heap(); + + PSPermGen* perm_gen = heap->perm_gen(); + PSOldGen* old_gen = heap->old_gen(); + PSYoungGen* young_gen = heap->young_gen(); + + MutableSpace* perm_space = perm_gen->object_space(); + MutableSpace* old_space = old_gen->object_space(); + MutableSpace* eden_space = young_gen->eden_space(); + MutableSpace* from_space = young_gen->from_space(); + MutableSpace* to_space = young_gen->to_space(); + + // 'from_space' here is the survivor space at the lower address. + if (to_space->bottom() < from_space->bottom()) { + from_space = to_space; + to_space = young_gen->from_space(); + } + + HeapWord* boundaries[12]; + unsigned int bidx = 0; + const unsigned int bidx_max = sizeof(boundaries) / sizeof(boundaries[0]); + + boundaries[0] = perm_space->bottom(); + boundaries[1] = perm_space->top(); + boundaries[2] = old_space->bottom(); + boundaries[3] = old_space->top(); + boundaries[4] = eden_space->bottom(); + boundaries[5] = eden_space->top(); + boundaries[6] = from_space->bottom(); + boundaries[7] = from_space->top(); + boundaries[8] = to_space->bottom(); + boundaries[9] = to_space->top(); + boundaries[10] = to_space->end(); + boundaries[11] = to_space->end(); + + BitMap::idx_t beg_bit = 0; + BitMap::idx_t end_bit; + BitMap::idx_t tmp_bit; + const BitMap::idx_t last_bit = _mark_bitmap.size(); + do { + HeapWord* addr = _mark_bitmap.bit_to_addr(beg_bit); + if (_mark_bitmap.is_marked(beg_bit)) { + oop obj = (oop)addr; + assert(obj->is_gc_marked(), "obj header is not marked"); + end_bit = _mark_bitmap.find_obj_end(beg_bit, last_bit); + const size_t size = _mark_bitmap.obj_size(beg_bit, end_bit); + assert(size == (size_t)obj->size(), "end bit wrong?"); + beg_bit = _mark_bitmap.find_obj_beg(beg_bit + 1, last_bit); + assert(beg_bit > end_bit, "bit set in middle of an obj"); + } else { + if (addr >= boundaries[bidx] && addr < boundaries[bidx + 1]) { + // a dead object in the current space. + oop obj = (oop)addr; + end_bit = _mark_bitmap.addr_to_bit(addr + obj->size()); + assert(!obj->is_gc_marked(), "obj marked in header, not in bitmap"); + tmp_bit = beg_bit + 1; + beg_bit = _mark_bitmap.find_obj_beg(tmp_bit, end_bit); + assert(beg_bit == end_bit, "beg bit set in unmarked obj"); + beg_bit = _mark_bitmap.find_obj_end(tmp_bit, end_bit); + assert(beg_bit == end_bit, "end bit set in unmarked obj"); + } else if (addr < boundaries[bidx + 2]) { + // addr is between top in the current space and bottom in the next. + end_bit = beg_bit + pointer_delta(boundaries[bidx + 2], addr); + tmp_bit = beg_bit; + beg_bit = _mark_bitmap.find_obj_beg(tmp_bit, end_bit); + assert(beg_bit == end_bit, "beg bit set above top"); + beg_bit = _mark_bitmap.find_obj_end(tmp_bit, end_bit); + assert(beg_bit == end_bit, "end bit set above top"); + bidx += 2; + } else if (bidx < bidx_max - 2) { + bidx += 2; // ??? + } else { + tmp_bit = beg_bit; + beg_bit = _mark_bitmap.find_obj_beg(tmp_bit, last_bit); + assert(beg_bit == last_bit, "beg bit set outside heap"); + beg_bit = _mark_bitmap.find_obj_end(tmp_bit, last_bit); + assert(beg_bit == last_bit, "end bit set outside heap"); + } + } + } while (beg_bit < last_bit); +} +// XXX end - verification code; only works while we also mark in object headers + +#ifndef PRODUCT +const char* PSParallelCompact::space_names[] = { + "perm", "old ", "eden", "from", "to " +}; + +void PSParallelCompact::print_chunk_ranges() +{ + tty->print_cr("space bottom top end new_top"); + tty->print_cr("------ ---------- ---------- ---------- ----------"); + + for (unsigned int id = 0; id < last_space_id; ++id) { + const MutableSpace* space = _space_info[id].space(); + tty->print_cr("%u %s " + SIZE_FORMAT_W("10") " " SIZE_FORMAT_W("10") " " + SIZE_FORMAT_W("10") " " SIZE_FORMAT_W("10") " ", + id, space_names[id], + summary_data().addr_to_chunk_idx(space->bottom()), + summary_data().addr_to_chunk_idx(space->top()), + summary_data().addr_to_chunk_idx(space->end()), + summary_data().addr_to_chunk_idx(_space_info[id].new_top())); + } +} + +void +print_generic_summary_chunk(size_t i, const ParallelCompactData::ChunkData* c) +{ +#define CHUNK_IDX_FORMAT SIZE_FORMAT_W("7") +#define CHUNK_DATA_FORMAT SIZE_FORMAT_W("5") + + ParallelCompactData& sd = PSParallelCompact::summary_data(); + size_t dci = c->destination() ? sd.addr_to_chunk_idx(c->destination()) : 0; + tty->print_cr(CHUNK_IDX_FORMAT " " PTR_FORMAT " " + CHUNK_IDX_FORMAT " " PTR_FORMAT " " + CHUNK_DATA_FORMAT " " CHUNK_DATA_FORMAT " " + CHUNK_DATA_FORMAT " " CHUNK_IDX_FORMAT " %d", + i, c->data_location(), dci, c->destination(), + c->partial_obj_size(), c->live_obj_size(), + c->data_size(), c->source_chunk(), c->destination_count()); + +#undef CHUNK_IDX_FORMAT +#undef CHUNK_DATA_FORMAT +} + +void +print_generic_summary_data(ParallelCompactData& summary_data, + HeapWord* const beg_addr, + HeapWord* const end_addr) +{ + size_t total_words = 0; + size_t i = summary_data.addr_to_chunk_idx(beg_addr); + const size_t last = summary_data.addr_to_chunk_idx(end_addr); + HeapWord* pdest = 0; + + while (i <= last) { + ParallelCompactData::ChunkData* c = summary_data.chunk(i); + if (c->data_size() != 0 || c->destination() != pdest) { + print_generic_summary_chunk(i, c); + total_words += c->data_size(); + pdest = c->destination(); + } + ++i; + } + + tty->print_cr("summary_data_bytes=" SIZE_FORMAT, total_words * HeapWordSize); +} + +void +print_generic_summary_data(ParallelCompactData& summary_data, + SpaceInfo* space_info) +{ + for (unsigned int id = 0; id < PSParallelCompact::last_space_id; ++id) { + const MutableSpace* space = space_info[id].space(); + print_generic_summary_data(summary_data, space->bottom(), + MAX2(space->top(), space_info[id].new_top())); + } +} + +void +print_initial_summary_chunk(size_t i, + const ParallelCompactData::ChunkData* c, + bool newline = true) +{ + tty->print(SIZE_FORMAT_W("5") " " PTR_FORMAT " " + SIZE_FORMAT_W("5") " " SIZE_FORMAT_W("5") " " + SIZE_FORMAT_W("5") " " SIZE_FORMAT_W("5") " %d", + i, c->destination(), + c->partial_obj_size(), c->live_obj_size(), + c->data_size(), c->source_chunk(), c->destination_count()); + if (newline) tty->cr(); +} + +void +print_initial_summary_data(ParallelCompactData& summary_data, + const MutableSpace* space) { + if (space->top() == space->bottom()) { + return; + } + + const size_t chunk_size = ParallelCompactData::ChunkSize; + HeapWord* const top_aligned_up = summary_data.chunk_align_up(space->top()); + const size_t end_chunk = summary_data.addr_to_chunk_idx(top_aligned_up); + const ParallelCompactData::ChunkData* c = summary_data.chunk(end_chunk - 1); + HeapWord* end_addr = c->destination() + c->data_size(); + const size_t live_in_space = pointer_delta(end_addr, space->bottom()); + + // Print (and count) the full chunks at the beginning of the space. + size_t full_chunk_count = 0; + size_t i = summary_data.addr_to_chunk_idx(space->bottom()); + while (i < end_chunk && summary_data.chunk(i)->data_size() == chunk_size) { + print_initial_summary_chunk(i, summary_data.chunk(i)); + ++full_chunk_count; + ++i; + } + + size_t live_to_right = live_in_space - full_chunk_count * chunk_size; + + double max_reclaimed_ratio = 0.0; + size_t max_reclaimed_ratio_chunk = 0; + size_t max_dead_to_right = 0; + size_t max_live_to_right = 0; + + // Print the 'reclaimed ratio' for chunks while there is something live in the + // chunk or to the right of it. The remaining chunks are empty (and + // uninteresting), and computing the ratio will result in division by 0. + while (i < end_chunk && live_to_right > 0) { + c = summary_data.chunk(i); + HeapWord* const chunk_addr = summary_data.chunk_to_addr(i); + const size_t used_to_right = pointer_delta(space->top(), chunk_addr); + const size_t dead_to_right = used_to_right - live_to_right; + const double reclaimed_ratio = double(dead_to_right) / live_to_right; + + if (reclaimed_ratio > max_reclaimed_ratio) { + max_reclaimed_ratio = reclaimed_ratio; + max_reclaimed_ratio_chunk = i; + max_dead_to_right = dead_to_right; + max_live_to_right = live_to_right; + } + + print_initial_summary_chunk(i, c, false); + tty->print_cr(" %12.10f " SIZE_FORMAT_W("10") " " SIZE_FORMAT_W("10"), + reclaimed_ratio, dead_to_right, live_to_right); + + live_to_right -= c->data_size(); + ++i; + } + + // Any remaining chunks are empty. Print one more if there is one. + if (i < end_chunk) { + print_initial_summary_chunk(i, summary_data.chunk(i)); + } + + tty->print_cr("max: " SIZE_FORMAT_W("4") " d2r=" SIZE_FORMAT_W("10") " " + "l2r=" SIZE_FORMAT_W("10") " max_ratio=%14.12f", + max_reclaimed_ratio_chunk, max_dead_to_right, + max_live_to_right, max_reclaimed_ratio); +} + +void +print_initial_summary_data(ParallelCompactData& summary_data, + SpaceInfo* space_info) { + unsigned int id = PSParallelCompact::perm_space_id; + const MutableSpace* space; + do { + space = space_info[id].space(); + print_initial_summary_data(summary_data, space); + } while (++id < PSParallelCompact::eden_space_id); + + do { + space = space_info[id].space(); + print_generic_summary_data(summary_data, space->bottom(), space->top()); + } while (++id < PSParallelCompact::last_space_id); +} +#endif // #ifndef PRODUCT + +#ifdef ASSERT +size_t add_obj_count; +size_t add_obj_size; +size_t mark_bitmap_count; +size_t mark_bitmap_size; +#endif // #ifdef ASSERT + +ParallelCompactData::ParallelCompactData() +{ + _region_start = 0; + + _chunk_vspace = 0; + _chunk_data = 0; + _chunk_count = 0; + + _block_vspace = 0; + _block_data = 0; + _block_count = 0; +} + +bool ParallelCompactData::initialize(MemRegion covered_region) +{ + _region_start = covered_region.start(); + const size_t region_size = covered_region.word_size(); + DEBUG_ONLY(_region_end = _region_start + region_size;) + + assert(chunk_align_down(_region_start) == _region_start, + "region start not aligned"); + assert((region_size & ChunkSizeOffsetMask) == 0, + "region size not a multiple of ChunkSize"); + + bool result = initialize_chunk_data(region_size); + + // Initialize the block data if it will be used for updating pointers, or if + // this is a debug build. + if (!UseParallelOldGCChunkPointerCalc || trueInDebug) { + result = result && initialize_block_data(region_size); + } + + return result; +} + +PSVirtualSpace* +ParallelCompactData::create_vspace(size_t count, size_t element_size) +{ + const size_t raw_bytes = count * element_size; + const size_t page_sz = os::page_size_for_region(raw_bytes, raw_bytes, 10); + const size_t granularity = os::vm_allocation_granularity(); + const size_t bytes = align_size_up(raw_bytes, MAX2(page_sz, granularity)); + + const size_t rs_align = page_sz == (size_t) os::vm_page_size() ? 0 : + MAX2(page_sz, granularity); + ReservedSpace rs(bytes, rs_align, false); + os::trace_page_sizes("par compact", raw_bytes, raw_bytes, page_sz, rs.base(), + rs.size()); + PSVirtualSpace* vspace = new PSVirtualSpace(rs, page_sz); + if (vspace != 0) { + if (vspace->expand_by(bytes)) { + return vspace; + } + delete vspace; + } + + return 0; +} + +bool ParallelCompactData::initialize_chunk_data(size_t region_size) +{ + const size_t count = (region_size + ChunkSizeOffsetMask) >> Log2ChunkSize; + _chunk_vspace = create_vspace(count, sizeof(ChunkData)); + if (_chunk_vspace != 0) { + _chunk_data = (ChunkData*)_chunk_vspace->reserved_low_addr(); + _chunk_count = count; + return true; + } + return false; +} + +bool ParallelCompactData::initialize_block_data(size_t region_size) +{ + const size_t count = (region_size + BlockOffsetMask) >> Log2BlockSize; + _block_vspace = create_vspace(count, sizeof(BlockData)); + if (_block_vspace != 0) { + _block_data = (BlockData*)_block_vspace->reserved_low_addr(); + _block_count = count; + return true; + } + return false; +} + +void ParallelCompactData::clear() +{ + if (_block_data) { + memset(_block_data, 0, _block_vspace->committed_size()); + } + memset(_chunk_data, 0, _chunk_vspace->committed_size()); +} + +void ParallelCompactData::clear_range(size_t beg_chunk, size_t end_chunk) { + assert(beg_chunk <= _chunk_count, "beg_chunk out of range"); + assert(end_chunk <= _chunk_count, "end_chunk out of range"); + assert(ChunkSize % BlockSize == 0, "ChunkSize not a multiple of BlockSize"); + + const size_t chunk_cnt = end_chunk - beg_chunk; + + if (_block_data) { + const size_t blocks_per_chunk = ChunkSize / BlockSize; + const size_t beg_block = beg_chunk * blocks_per_chunk; + const size_t block_cnt = chunk_cnt * blocks_per_chunk; + memset(_block_data + beg_block, 0, block_cnt * sizeof(BlockData)); + } + memset(_chunk_data + beg_chunk, 0, chunk_cnt * sizeof(ChunkData)); +} + +HeapWord* ParallelCompactData::partial_obj_end(size_t chunk_idx) const +{ + const ChunkData* cur_cp = chunk(chunk_idx); + const ChunkData* const end_cp = chunk(chunk_count() - 1); + + HeapWord* result = chunk_to_addr(chunk_idx); + if (cur_cp < end_cp) { + do { + result += cur_cp->partial_obj_size(); + } while (cur_cp->partial_obj_size() == ChunkSize && ++cur_cp < end_cp); + } + return result; +} + +void ParallelCompactData::add_obj(HeapWord* addr, size_t len) +{ + const size_t obj_ofs = pointer_delta(addr, _region_start); + const size_t beg_chunk = obj_ofs >> Log2ChunkSize; + const size_t end_chunk = (obj_ofs + len - 1) >> Log2ChunkSize; + + DEBUG_ONLY(Atomic::inc_ptr(&add_obj_count);) + DEBUG_ONLY(Atomic::add_ptr(len, &add_obj_size);) + + if (beg_chunk == end_chunk) { + // All in one chunk. + _chunk_data[beg_chunk].add_live_obj(len); + return; + } + + // First chunk. + const size_t beg_ofs = chunk_offset(addr); + _chunk_data[beg_chunk].add_live_obj(ChunkSize - beg_ofs); + + klassOop klass = ((oop)addr)->klass(); + // Middle chunks--completely spanned by this object. + for (size_t chunk = beg_chunk + 1; chunk < end_chunk; ++chunk) { + _chunk_data[chunk].set_partial_obj_size(ChunkSize); + _chunk_data[chunk].set_partial_obj_addr(addr); + } + + // Last chunk. + const size_t end_ofs = chunk_offset(addr + len - 1); + _chunk_data[end_chunk].set_partial_obj_size(end_ofs + 1); + _chunk_data[end_chunk].set_partial_obj_addr(addr); +} + +void +ParallelCompactData::summarize_dense_prefix(HeapWord* beg, HeapWord* end) +{ + assert(chunk_offset(beg) == 0, "not ChunkSize aligned"); + assert(chunk_offset(end) == 0, "not ChunkSize aligned"); + + size_t cur_chunk = addr_to_chunk_idx(beg); + const size_t end_chunk = addr_to_chunk_idx(end); + HeapWord* addr = beg; + while (cur_chunk < end_chunk) { + _chunk_data[cur_chunk].set_destination(addr); + _chunk_data[cur_chunk].set_destination_count(0); + _chunk_data[cur_chunk].set_source_chunk(cur_chunk); + _chunk_data[cur_chunk].set_data_location(addr); + + // Update live_obj_size so the chunk appears completely full. + size_t live_size = ChunkSize - _chunk_data[cur_chunk].partial_obj_size(); + _chunk_data[cur_chunk].set_live_obj_size(live_size); + + ++cur_chunk; + addr += ChunkSize; + } +} + +bool ParallelCompactData::summarize(HeapWord* target_beg, HeapWord* target_end, + HeapWord* source_beg, HeapWord* source_end, + HeapWord** target_next, + HeapWord** source_next) { + // This is too strict. + // assert(chunk_offset(source_beg) == 0, "not ChunkSize aligned"); + + if (TraceParallelOldGCSummaryPhase) { + tty->print_cr("tb=" PTR_FORMAT " te=" PTR_FORMAT " " + "sb=" PTR_FORMAT " se=" PTR_FORMAT " " + "tn=" PTR_FORMAT " sn=" PTR_FORMAT, + target_beg, target_end, + source_beg, source_end, + target_next != 0 ? *target_next : (HeapWord*) 0, + source_next != 0 ? *source_next : (HeapWord*) 0); + } + + size_t cur_chunk = addr_to_chunk_idx(source_beg); + const size_t end_chunk = addr_to_chunk_idx(chunk_align_up(source_end)); + + HeapWord *dest_addr = target_beg; + while (cur_chunk < end_chunk) { + size_t words = _chunk_data[cur_chunk].data_size(); + +#if 1 + assert(pointer_delta(target_end, dest_addr) >= words, + "source region does not fit into target region"); +#else + // XXX - need some work on the corner cases here. If the chunk does not + // fit, then must either make sure any partial_obj from the chunk fits, or + // 'undo' the initial part of the partial_obj that is in the previous chunk. + if (dest_addr + words >= target_end) { + // Let the caller know where to continue. + *target_next = dest_addr; + *source_next = chunk_to_addr(cur_chunk); + return false; + } +#endif // #if 1 + + _chunk_data[cur_chunk].set_destination(dest_addr); + + // Set the destination_count for cur_chunk, and if necessary, update + // source_chunk for a destination chunk. The source_chunk field is updated + // if cur_chunk is the first (left-most) chunk to be copied to a destination + // chunk. + // + // The destination_count calculation is a bit subtle. A chunk that has data + // that compacts into itself does not count itself as a destination. This + // maintains the invariant that a zero count means the chunk is available + // and can be claimed and then filled. + if (words > 0) { + HeapWord* const last_addr = dest_addr + words - 1; + const size_t dest_chunk_1 = addr_to_chunk_idx(dest_addr); + const size_t dest_chunk_2 = addr_to_chunk_idx(last_addr); +#if 0 + // Initially assume that the destination chunks will be the same and + // adjust the value below if necessary. Under this assumption, if + // cur_chunk == dest_chunk_2, then cur_chunk will be compacted completely + // into itself. + uint destination_count = cur_chunk == dest_chunk_2 ? 0 : 1; + if (dest_chunk_1 != dest_chunk_2) { + // Destination chunks differ; adjust destination_count. + destination_count += 1; + // Data from cur_chunk will be copied to the start of dest_chunk_2. + _chunk_data[dest_chunk_2].set_source_chunk(cur_chunk); + } else if (chunk_offset(dest_addr) == 0) { + // Data from cur_chunk will be copied to the start of the destination + // chunk. + _chunk_data[dest_chunk_1].set_source_chunk(cur_chunk); + } +#else + // Initially assume that the destination chunks will be different and + // adjust the value below if necessary. Under this assumption, if + // cur_chunk == dest_chunk2, then cur_chunk will be compacted partially + // into dest_chunk_1 and partially into itself. + uint destination_count = cur_chunk == dest_chunk_2 ? 1 : 2; + if (dest_chunk_1 != dest_chunk_2) { + // Data from cur_chunk will be copied to the start of dest_chunk_2. + _chunk_data[dest_chunk_2].set_source_chunk(cur_chunk); + } else { + // Destination chunks are the same; adjust destination_count. + destination_count -= 1; + if (chunk_offset(dest_addr) == 0) { + // Data from cur_chunk will be copied to the start of the destination + // chunk. + _chunk_data[dest_chunk_1].set_source_chunk(cur_chunk); + } + } +#endif // #if 0 + + _chunk_data[cur_chunk].set_destination_count(destination_count); + _chunk_data[cur_chunk].set_data_location(chunk_to_addr(cur_chunk)); + dest_addr += words; + } + + ++cur_chunk; + } + + *target_next = dest_addr; + return true; +} + +bool ParallelCompactData::partial_obj_ends_in_block(size_t block_index) { + HeapWord* block_addr = block_to_addr(block_index); + HeapWord* block_end_addr = block_addr + BlockSize; + size_t chunk_index = addr_to_chunk_idx(block_addr); + HeapWord* partial_obj_end_addr = partial_obj_end(chunk_index); + + // An object that ends at the end of the block, ends + // in the block (the last word of the object is to + // the left of the end). + if ((block_addr < partial_obj_end_addr) && + (partial_obj_end_addr <= block_end_addr)) { + return true; + } + + return false; +} + +HeapWord* ParallelCompactData::calc_new_pointer(HeapWord* addr) { + HeapWord* result = NULL; + if (UseParallelOldGCChunkPointerCalc) { + result = chunk_calc_new_pointer(addr); + } else { + result = block_calc_new_pointer(addr); + } + return result; +} + +// This method is overly complicated (expensive) to be called +// for every reference. +// Try to restructure this so that a NULL is returned if +// the object is dead. But don't wast the cycles to explicitly check +// that it is dead since only live objects should be passed in. + +HeapWord* ParallelCompactData::chunk_calc_new_pointer(HeapWord* addr) { + assert(addr != NULL, "Should detect NULL oop earlier"); + assert(PSParallelCompact::gc_heap()->is_in(addr), "addr not in heap"); +#ifdef ASSERT + if (PSParallelCompact::mark_bitmap()->is_unmarked(addr)) { + gclog_or_tty->print_cr("calc_new_pointer:: addr " PTR_FORMAT, addr); + } +#endif + assert(PSParallelCompact::mark_bitmap()->is_marked(addr), "obj not marked"); + + // Chunk covering the object. + size_t chunk_index = addr_to_chunk_idx(addr); + const ChunkData* const chunk_ptr = chunk(chunk_index); + HeapWord* const chunk_addr = chunk_align_down(addr); + + assert(addr < chunk_addr + ChunkSize, "Chunk does not cover object"); + assert(addr_to_chunk_ptr(chunk_addr) == chunk_ptr, "sanity check"); + + HeapWord* result = chunk_ptr->destination(); + + // If all the data in the chunk is live, then the new location of the object + // can be calculated from the destination of the chunk plus the offset of the + // object in the chunk. + if (chunk_ptr->data_size() == ChunkSize) { + result += pointer_delta(addr, chunk_addr); + return result; + } + + // The new location of the object is + // chunk destination + + // size of the partial object extending onto the chunk + + // sizes of the live objects in the Chunk that are to the left of addr + const size_t partial_obj_size = chunk_ptr->partial_obj_size(); + HeapWord* const search_start = chunk_addr + partial_obj_size; + + const ParMarkBitMap* bitmap = PSParallelCompact::mark_bitmap(); + size_t live_to_left = bitmap->live_words_in_range(search_start, oop(addr)); + + result += partial_obj_size + live_to_left; + assert(result <= addr, "object cannot move to the right"); + return result; +} + +HeapWord* ParallelCompactData::block_calc_new_pointer(HeapWord* addr) { + assert(addr != NULL, "Should detect NULL oop earlier"); + assert(PSParallelCompact::gc_heap()->is_in(addr), "addr not in heap"); +#ifdef ASSERT + if (PSParallelCompact::mark_bitmap()->is_unmarked(addr)) { + gclog_or_tty->print_cr("calc_new_pointer:: addr " PTR_FORMAT, addr); + } +#endif + assert(PSParallelCompact::mark_bitmap()->is_marked(addr), "obj not marked"); + + // Chunk covering the object. + size_t chunk_index = addr_to_chunk_idx(addr); + const ChunkData* const chunk_ptr = chunk(chunk_index); + HeapWord* const chunk_addr = chunk_align_down(addr); + + assert(addr < chunk_addr + ChunkSize, "Chunk does not cover object"); + assert(addr_to_chunk_ptr(chunk_addr) == chunk_ptr, "sanity check"); + + HeapWord* result = chunk_ptr->destination(); + + // If all the data in the chunk is live, then the new location of the object + // can be calculated from the destination of the chunk plus the offset of the + // object in the chunk. + if (chunk_ptr->data_size() == ChunkSize) { + result += pointer_delta(addr, chunk_addr); + return result; + } + + // The new location of the object is + // chunk destination + + // block offset + + // sizes of the live objects in the Block that are to the left of addr + const size_t block_offset = addr_to_block_ptr(addr)->offset(); + HeapWord* const search_start = chunk_addr + block_offset; + + const ParMarkBitMap* bitmap = PSParallelCompact::mark_bitmap(); + size_t live_to_left = bitmap->live_words_in_range(search_start, oop(addr)); + + result += block_offset + live_to_left; + assert(result <= addr, "object cannot move to the right"); + assert(result == chunk_calc_new_pointer(addr), "Should match"); + return result; +} + +klassOop ParallelCompactData::calc_new_klass(klassOop old_klass) { + klassOop updated_klass; + if (PSParallelCompact::should_update_klass(old_klass)) { + updated_klass = (klassOop) calc_new_pointer(old_klass); + } else { + updated_klass = old_klass; + } + + return updated_klass; +} + +#ifdef ASSERT +void ParallelCompactData::verify_clear(const PSVirtualSpace* vspace) +{ + const size_t* const beg = (const size_t*)vspace->committed_low_addr(); + const size_t* const end = (const size_t*)vspace->committed_high_addr(); + for (const size_t* p = beg; p < end; ++p) { + assert(*p == 0, "not zero"); + } +} + +void ParallelCompactData::verify_clear() +{ + verify_clear(_chunk_vspace); + verify_clear(_block_vspace); +} +#endif // #ifdef ASSERT + +#ifdef NOT_PRODUCT +ParallelCompactData::ChunkData* debug_chunk(size_t chunk_index) { + ParallelCompactData& sd = PSParallelCompact::summary_data(); + return sd.chunk(chunk_index); +} +#endif + +elapsedTimer PSParallelCompact::_accumulated_time; +unsigned int PSParallelCompact::_total_invocations = 0; +unsigned int PSParallelCompact::_maximum_compaction_gc_num = 0; +jlong PSParallelCompact::_time_of_last_gc = 0; +CollectorCounters* PSParallelCompact::_counters = NULL; +ParMarkBitMap PSParallelCompact::_mark_bitmap; +ParallelCompactData PSParallelCompact::_summary_data; + +PSParallelCompact::IsAliveClosure PSParallelCompact::_is_alive_closure; +PSParallelCompact::AdjustPointerClosure PSParallelCompact::_adjust_root_pointer_closure(true); +PSParallelCompact::AdjustPointerClosure PSParallelCompact::_adjust_pointer_closure(false); + +void PSParallelCompact::KeepAliveClosure::do_oop(oop* p) { +#ifdef VALIDATE_MARK_SWEEP + if (ValidateMarkSweep) { + if (!Universe::heap()->is_in_reserved(p)) { + _root_refs_stack->push(p); + } else { + _other_refs_stack->push(p); + } + } +#endif + mark_and_push(_compaction_manager, p); +} + +void PSParallelCompact::mark_and_follow(ParCompactionManager* cm, + oop* p) { + assert(Universe::heap()->is_in_reserved(p), + "we should only be traversing objects here"); + oop m = *p; + if (m != NULL && mark_bitmap()->is_unmarked(m)) { + if (mark_obj(m)) { + m->follow_contents(cm); // Follow contents of the marked object + } + } +} + +// Anything associated with this variable is temporary. + +void PSParallelCompact::mark_and_push_internal(ParCompactionManager* cm, + oop* p) { + // Push marked object, contents will be followed later + oop m = *p; + if (mark_obj(m)) { + // This thread marked the object and + // owns the subsequent processing of it. + cm->save_for_scanning(m); + } +} + +void PSParallelCompact::post_initialize() { + ParallelScavengeHeap* heap = gc_heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + MemRegion mr = heap->reserved_region(); + _ref_processor = ReferenceProcessor::create_ref_processor( + mr, // span + true, // atomic_discovery + true, // mt_discovery + &_is_alive_closure, + ParallelGCThreads, + ParallelRefProcEnabled); + _counters = new CollectorCounters("PSParallelCompact", 1); + + // Initialize static fields in ParCompactionManager. + ParCompactionManager::initialize(mark_bitmap()); +} + +bool PSParallelCompact::initialize() { + ParallelScavengeHeap* heap = gc_heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + MemRegion mr = heap->reserved_region(); + + // Was the old gen get allocated successfully? + if (!heap->old_gen()->is_allocated()) { + return false; + } + + initialize_space_info(); + initialize_dead_wood_limiter(); + + if (!_mark_bitmap.initialize(mr)) { + vm_shutdown_during_initialization("Unable to allocate bit map for " + "parallel garbage collection for the requested heap size."); + return false; + } + + if (!_summary_data.initialize(mr)) { + vm_shutdown_during_initialization("Unable to allocate tables for " + "parallel garbage collection for the requested heap size."); + return false; + } + + return true; +} + +void PSParallelCompact::initialize_space_info() +{ + memset(&_space_info, 0, sizeof(_space_info)); + + ParallelScavengeHeap* heap = gc_heap(); + PSYoungGen* young_gen = heap->young_gen(); + MutableSpace* perm_space = heap->perm_gen()->object_space(); + + _space_info[perm_space_id].set_space(perm_space); + _space_info[old_space_id].set_space(heap->old_gen()->object_space()); + _space_info[eden_space_id].set_space(young_gen->eden_space()); + _space_info[from_space_id].set_space(young_gen->from_space()); + _space_info[to_space_id].set_space(young_gen->to_space()); + + _space_info[perm_space_id].set_start_array(heap->perm_gen()->start_array()); + _space_info[old_space_id].set_start_array(heap->old_gen()->start_array()); + + _space_info[perm_space_id].set_min_dense_prefix(perm_space->top()); + if (TraceParallelOldGCDensePrefix) { + tty->print_cr("perm min_dense_prefix=" PTR_FORMAT, + _space_info[perm_space_id].min_dense_prefix()); + } +} + +void PSParallelCompact::initialize_dead_wood_limiter() +{ + const size_t max = 100; + _dwl_mean = double(MIN2(ParallelOldDeadWoodLimiterMean, max)) / 100.0; + _dwl_std_dev = double(MIN2(ParallelOldDeadWoodLimiterStdDev, max)) / 100.0; + _dwl_first_term = 1.0 / (sqrt(2.0 * M_PI) * _dwl_std_dev); + DEBUG_ONLY(_dwl_initialized = true;) + _dwl_adjustment = normal_distribution(1.0); +} + +// Simple class for storing info about the heap at the start of GC, to be used +// after GC for comparison/printing. +class PreGCValues { +public: + PreGCValues() { } + PreGCValues(ParallelScavengeHeap* heap) { fill(heap); } + + void fill(ParallelScavengeHeap* heap) { + _heap_used = heap->used(); + _young_gen_used = heap->young_gen()->used_in_bytes(); + _old_gen_used = heap->old_gen()->used_in_bytes(); + _perm_gen_used = heap->perm_gen()->used_in_bytes(); + }; + + size_t heap_used() const { return _heap_used; } + size_t young_gen_used() const { return _young_gen_used; } + size_t old_gen_used() const { return _old_gen_used; } + size_t perm_gen_used() const { return _perm_gen_used; } + +private: + size_t _heap_used; + size_t _young_gen_used; + size_t _old_gen_used; + size_t _perm_gen_used; +}; + +void +PSParallelCompact::clear_data_covering_space(SpaceId id) +{ + // At this point, top is the value before GC, new_top() is the value that will + // be set at the end of GC. The marking bitmap is cleared to top; nothing + // should be marked above top. The summary data is cleared to the larger of + // top & new_top. + MutableSpace* const space = _space_info[id].space(); + HeapWord* const bot = space->bottom(); + HeapWord* const top = space->top(); + HeapWord* const max_top = MAX2(top, _space_info[id].new_top()); + + const idx_t beg_bit = _mark_bitmap.addr_to_bit(bot); + const idx_t end_bit = BitMap::word_align_up(_mark_bitmap.addr_to_bit(top)); + _mark_bitmap.clear_range(beg_bit, end_bit); + + const size_t beg_chunk = _summary_data.addr_to_chunk_idx(bot); + const size_t end_chunk = + _summary_data.addr_to_chunk_idx(_summary_data.chunk_align_up(max_top)); + _summary_data.clear_range(beg_chunk, end_chunk); +} + +void PSParallelCompact::pre_compact(PreGCValues* pre_gc_values) +{ + // Update the from & to space pointers in space_info, since they are swapped + // at each young gen gc. Do the update unconditionally (even though a + // promotion failure does not swap spaces) because an unknown number of minor + // collections will have swapped the spaces an unknown number of times. + TraceTime tm("pre compact", print_phases(), true, gclog_or_tty); + ParallelScavengeHeap* heap = gc_heap(); + _space_info[from_space_id].set_space(heap->young_gen()->from_space()); + _space_info[to_space_id].set_space(heap->young_gen()->to_space()); + + pre_gc_values->fill(heap); + + ParCompactionManager::reset(); + NOT_PRODUCT(_mark_bitmap.reset_counters()); + DEBUG_ONLY(add_obj_count = add_obj_size = 0;) + DEBUG_ONLY(mark_bitmap_count = mark_bitmap_size = 0;) + + // Increment the invocation count + heap->increment_total_collections(); + + // We need to track unique mark sweep invocations as well. + _total_invocations++; + + if (PrintHeapAtGC) { + Universe::print_heap_before_gc(); + } + + // Fill in TLABs + heap->accumulate_statistics_all_tlabs(); + heap->ensure_parsability(true); // retire TLABs + + if (VerifyBeforeGC && heap->total_collections() >= VerifyGCStartAt) { + HandleMark hm; // Discard invalid handles created during verification + gclog_or_tty->print(" VerifyBeforeGC:"); + Universe::verify(true); + } + + // Verify object start arrays + if (VerifyObjectStartArray && + VerifyBeforeGC) { + heap->old_gen()->verify_object_start_array(); + heap->perm_gen()->verify_object_start_array(); + } + + DEBUG_ONLY(mark_bitmap()->verify_clear();) + DEBUG_ONLY(summary_data().verify_clear();) +} + +void PSParallelCompact::post_compact() +{ + TraceTime tm("post compact", print_phases(), true, gclog_or_tty); + + // Clear the marking bitmap and summary data and update top() in each space. + for (unsigned int id = perm_space_id; id < last_space_id; ++id) { + clear_data_covering_space(SpaceId(id)); + _space_info[id].space()->set_top(_space_info[id].new_top()); + } + + MutableSpace* const eden_space = _space_info[eden_space_id].space(); + MutableSpace* const from_space = _space_info[from_space_id].space(); + MutableSpace* const to_space = _space_info[to_space_id].space(); + + ParallelScavengeHeap* heap = gc_heap(); + bool eden_empty = eden_space->is_empty(); + if (!eden_empty) { + eden_empty = absorb_live_data_from_eden(heap->size_policy(), + heap->young_gen(), heap->old_gen()); + } + + // Update heap occupancy information which is used as input to the soft ref + // clearing policy at the next gc. + Universe::update_heap_info_at_gc(); + + bool young_gen_empty = eden_empty && from_space->is_empty() && + to_space->is_empty(); + + BarrierSet* bs = heap->barrier_set(); + if (bs->is_a(BarrierSet::ModRef)) { + ModRefBarrierSet* modBS = (ModRefBarrierSet*)bs; + MemRegion old_mr = heap->old_gen()->reserved(); + MemRegion perm_mr = heap->perm_gen()->reserved(); + assert(perm_mr.end() <= old_mr.start(), "Generations out of order"); + + if (young_gen_empty) { + modBS->clear(MemRegion(perm_mr.start(), old_mr.end())); + } else { + modBS->invalidate(MemRegion(perm_mr.start(), old_mr.end())); + } + } + + Threads::gc_epilogue(); + CodeCache::gc_epilogue(); + + COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); + + ref_processor()->enqueue_discovered_references(NULL); + + // Update time of last GC + reset_millis_since_last_gc(); +} + +HeapWord* +PSParallelCompact::compute_dense_prefix_via_density(const SpaceId id, + bool maximum_compaction) +{ + const size_t chunk_size = ParallelCompactData::ChunkSize; + const ParallelCompactData& sd = summary_data(); + + const MutableSpace* const space = _space_info[id].space(); + HeapWord* const top_aligned_up = sd.chunk_align_up(space->top()); + const ChunkData* const beg_cp = sd.addr_to_chunk_ptr(space->bottom()); + const ChunkData* const end_cp = sd.addr_to_chunk_ptr(top_aligned_up); + + // Skip full chunks at the beginning of the space--they are necessarily part + // of the dense prefix. + size_t full_count = 0; + const ChunkData* cp; + for (cp = beg_cp; cp < end_cp && cp->data_size() == chunk_size; ++cp) { + ++full_count; + } + + assert(total_invocations() >= _maximum_compaction_gc_num, "sanity"); + const size_t gcs_since_max = total_invocations() - _maximum_compaction_gc_num; + const bool interval_ended = gcs_since_max > HeapMaximumCompactionInterval; + if (maximum_compaction || cp == end_cp || interval_ended) { + _maximum_compaction_gc_num = total_invocations(); + return sd.chunk_to_addr(cp); + } + + HeapWord* const new_top = _space_info[id].new_top(); + const size_t space_live = pointer_delta(new_top, space->bottom()); + const size_t space_used = space->used_in_words(); + const size_t space_capacity = space->capacity_in_words(); + + const double cur_density = double(space_live) / space_capacity; + const double deadwood_density = + (1.0 - cur_density) * (1.0 - cur_density) * cur_density * cur_density; + const size_t deadwood_goal = size_t(space_capacity * deadwood_density); + + if (TraceParallelOldGCDensePrefix) { + tty->print_cr("cur_dens=%5.3f dw_dens=%5.3f dw_goal=" SIZE_FORMAT, + cur_density, deadwood_density, deadwood_goal); + tty->print_cr("space_live=" SIZE_FORMAT " " "space_used=" SIZE_FORMAT " " + "space_cap=" SIZE_FORMAT, + space_live, space_used, + space_capacity); + } + + // XXX - Use binary search? + HeapWord* dense_prefix = sd.chunk_to_addr(cp); + const ChunkData* full_cp = cp; + const ChunkData* const top_cp = sd.addr_to_chunk_ptr(space->top() - 1); + while (cp < end_cp) { + HeapWord* chunk_destination = cp->destination(); + const size_t cur_deadwood = pointer_delta(dense_prefix, chunk_destination); + if (TraceParallelOldGCDensePrefix && Verbose) { + tty->print_cr("c#=" SIZE_FORMAT_W("04") " dst=" PTR_FORMAT " " + "dp=" SIZE_FORMAT_W("08") " " "cdw=" SIZE_FORMAT_W("08"), + sd.chunk(cp), chunk_destination, + dense_prefix, cur_deadwood); + } + + if (cur_deadwood >= deadwood_goal) { + // Found the chunk that has the correct amount of deadwood to the left. + // This typically occurs after crossing a fairly sparse set of chunks, so + // iterate backwards over those sparse chunks, looking for the chunk that + // has the lowest density of live objects 'to the right.' + size_t space_to_left = sd.chunk(cp) * chunk_size; + size_t live_to_left = space_to_left - cur_deadwood; + size_t space_to_right = space_capacity - space_to_left; + size_t live_to_right = space_live - live_to_left; + double density_to_right = double(live_to_right) / space_to_right; + while (cp > full_cp) { + --cp; + const size_t prev_chunk_live_to_right = live_to_right - cp->data_size(); + const size_t prev_chunk_space_to_right = space_to_right + chunk_size; + double prev_chunk_density_to_right = + double(prev_chunk_live_to_right) / prev_chunk_space_to_right; + if (density_to_right <= prev_chunk_density_to_right) { + return dense_prefix; + } + if (TraceParallelOldGCDensePrefix && Verbose) { + tty->print_cr("backing up from c=" SIZE_FORMAT_W("4") " d2r=%10.8f " + "pc_d2r=%10.8f", sd.chunk(cp), density_to_right, + prev_chunk_density_to_right); + } + dense_prefix -= chunk_size; + live_to_right = prev_chunk_live_to_right; + space_to_right = prev_chunk_space_to_right; + density_to_right = prev_chunk_density_to_right; + } + return dense_prefix; + } + + dense_prefix += chunk_size; + ++cp; + } + + return dense_prefix; +} + +#ifndef PRODUCT +void PSParallelCompact::print_dense_prefix_stats(const char* const algorithm, + const SpaceId id, + const bool maximum_compaction, + HeapWord* const addr) +{ + const size_t chunk_idx = summary_data().addr_to_chunk_idx(addr); + ChunkData* const cp = summary_data().chunk(chunk_idx); + const MutableSpace* const space = _space_info[id].space(); + HeapWord* const new_top = _space_info[id].new_top(); + + const size_t space_live = pointer_delta(new_top, space->bottom()); + const size_t dead_to_left = pointer_delta(addr, cp->destination()); + const size_t space_cap = space->capacity_in_words(); + const double dead_to_left_pct = double(dead_to_left) / space_cap; + const size_t live_to_right = new_top - cp->destination(); + const size_t dead_to_right = space->top() - addr - live_to_right; + + tty->print_cr("%s=" PTR_FORMAT " dpc=" SIZE_FORMAT_W("05") " " + "spl=" SIZE_FORMAT " " + "d2l=" SIZE_FORMAT " d2l%%=%6.4f " + "d2r=" SIZE_FORMAT " l2r=" SIZE_FORMAT + " ratio=%10.8f", + algorithm, addr, chunk_idx, + space_live, + dead_to_left, dead_to_left_pct, + dead_to_right, live_to_right, + double(dead_to_right) / live_to_right); +} +#endif // #ifndef PRODUCT + +// Return a fraction indicating how much of the generation can be treated as +// "dead wood" (i.e., not reclaimed). The function uses a normal distribution +// based on the density of live objects in the generation to determine a limit, +// which is then adjusted so the return value is min_percent when the density is +// 1. +// +// The following table shows some return values for a different values of the +// standard deviation (ParallelOldDeadWoodLimiterStdDev); the mean is 0.5 and +// min_percent is 1. +// +// fraction allowed as dead wood +// ----------------------------------------------------------------- +// density std_dev=70 std_dev=75 std_dev=80 std_dev=85 std_dev=90 std_dev=95 +// ------- ---------- ---------- ---------- ---------- ---------- ---------- +// 0.00000 0.01000000 0.01000000 0.01000000 0.01000000 0.01000000 0.01000000 +// 0.05000 0.03193096 0.02836880 0.02550828 0.02319280 0.02130337 0.01974941 +// 0.10000 0.05247504 0.04547452 0.03988045 0.03537016 0.03170171 0.02869272 +// 0.15000 0.07135702 0.06111390 0.05296419 0.04641639 0.04110601 0.03676066 +// 0.20000 0.08831616 0.07509618 0.06461766 0.05622444 0.04943437 0.04388975 +// 0.25000 0.10311208 0.08724696 0.07471205 0.06469760 0.05661313 0.05002313 +// 0.30000 0.11553050 0.09741183 0.08313394 0.07175114 0.06257797 0.05511132 +// 0.35000 0.12538832 0.10545958 0.08978741 0.07731366 0.06727491 0.05911289 +// 0.40000 0.13253818 0.11128511 0.09459590 0.08132834 0.07066107 0.06199500 +// 0.45000 0.13687208 0.11481163 0.09750361 0.08375387 0.07270534 0.06373386 +// 0.50000 0.13832410 0.11599237 0.09847664 0.08456518 0.07338887 0.06431510 +// 0.55000 0.13687208 0.11481163 0.09750361 0.08375387 0.07270534 0.06373386 +// 0.60000 0.13253818 0.11128511 0.09459590 0.08132834 0.07066107 0.06199500 +// 0.65000 0.12538832 0.10545958 0.08978741 0.07731366 0.06727491 0.05911289 +// 0.70000 0.11553050 0.09741183 0.08313394 0.07175114 0.06257797 0.05511132 +// 0.75000 0.10311208 0.08724696 0.07471205 0.06469760 0.05661313 0.05002313 +// 0.80000 0.08831616 0.07509618 0.06461766 0.05622444 0.04943437 0.04388975 +// 0.85000 0.07135702 0.06111390 0.05296419 0.04641639 0.04110601 0.03676066 +// 0.90000 0.05247504 0.04547452 0.03988045 0.03537016 0.03170171 0.02869272 +// 0.95000 0.03193096 0.02836880 0.02550828 0.02319280 0.02130337 0.01974941 +// 1.00000 0.01000000 0.01000000 0.01000000 0.01000000 0.01000000 0.01000000 + +double PSParallelCompact::dead_wood_limiter(double density, size_t min_percent) +{ + assert(_dwl_initialized, "uninitialized"); + + // The raw limit is the value of the normal distribution at x = density. + const double raw_limit = normal_distribution(density); + + // Adjust the raw limit so it becomes the minimum when the density is 1. + // + // First subtract the adjustment value (which is simply the precomputed value + // normal_distribution(1.0)); this yields a value of 0 when the density is 1. + // Then add the minimum value, so the minimum is returned when the density is + // 1. Finally, prevent negative values, which occur when the mean is not 0.5. + const double min = double(min_percent) / 100.0; + const double limit = raw_limit - _dwl_adjustment + min; + return MAX2(limit, 0.0); +} + +ParallelCompactData::ChunkData* +PSParallelCompact::first_dead_space_chunk(const ChunkData* beg, + const ChunkData* end) +{ + const size_t chunk_size = ParallelCompactData::ChunkSize; + ParallelCompactData& sd = summary_data(); + size_t left = sd.chunk(beg); + size_t right = end > beg ? sd.chunk(end) - 1 : left; + + // Binary search. + while (left < right) { + // Equivalent to (left + right) / 2, but does not overflow. + const size_t middle = left + (right - left) / 2; + ChunkData* const middle_ptr = sd.chunk(middle); + HeapWord* const dest = middle_ptr->destination(); + HeapWord* const addr = sd.chunk_to_addr(middle); + assert(dest != NULL, "sanity"); + assert(dest <= addr, "must move left"); + + if (middle > left && dest < addr) { + right = middle - 1; + } else if (middle < right && middle_ptr->data_size() == chunk_size) { + left = middle + 1; + } else { + return middle_ptr; + } + } + return sd.chunk(left); +} + +ParallelCompactData::ChunkData* +PSParallelCompact::dead_wood_limit_chunk(const ChunkData* beg, + const ChunkData* end, + size_t dead_words) +{ + ParallelCompactData& sd = summary_data(); + size_t left = sd.chunk(beg); + size_t right = end > beg ? sd.chunk(end) - 1 : left; + + // Binary search. + while (left < right) { + // Equivalent to (left + right) / 2, but does not overflow. + const size_t middle = left + (right - left) / 2; + ChunkData* const middle_ptr = sd.chunk(middle); + HeapWord* const dest = middle_ptr->destination(); + HeapWord* const addr = sd.chunk_to_addr(middle); + assert(dest != NULL, "sanity"); + assert(dest <= addr, "must move left"); + + const size_t dead_to_left = pointer_delta(addr, dest); + if (middle > left && dead_to_left > dead_words) { + right = middle - 1; + } else if (middle < right && dead_to_left < dead_words) { + left = middle + 1; + } else { + return middle_ptr; + } + } + return sd.chunk(left); +} + +// The result is valid during the summary phase, after the initial summarization +// of each space into itself, and before final summarization. +inline double +PSParallelCompact::reclaimed_ratio(const ChunkData* const cp, + HeapWord* const bottom, + HeapWord* const top, + HeapWord* const new_top) +{ + ParallelCompactData& sd = summary_data(); + + assert(cp != NULL, "sanity"); + assert(bottom != NULL, "sanity"); + assert(top != NULL, "sanity"); + assert(new_top != NULL, "sanity"); + assert(top >= new_top, "summary data problem?"); + assert(new_top > bottom, "space is empty; should not be here"); + assert(new_top >= cp->destination(), "sanity"); + assert(top >= sd.chunk_to_addr(cp), "sanity"); + + HeapWord* const destination = cp->destination(); + const size_t dense_prefix_live = pointer_delta(destination, bottom); + const size_t compacted_region_live = pointer_delta(new_top, destination); + const size_t compacted_region_used = pointer_delta(top, sd.chunk_to_addr(cp)); + const size_t reclaimable = compacted_region_used - compacted_region_live; + + const double divisor = dense_prefix_live + 1.25 * compacted_region_live; + return double(reclaimable) / divisor; +} + +// Return the address of the end of the dense prefix, a.k.a. the start of the +// compacted region. The address is always on a chunk boundary. +// +// Completely full chunks at the left are skipped, since no compaction can occur +// in those chunks. Then the maximum amount of dead wood to allow is computed, +// based on the density (amount live / capacity) of the generation; the chunk +// with approximately that amount of dead space to the left is identified as the +// limit chunk. Chunks between the last completely full chunk and the limit +// chunk are scanned and the one that has the best (maximum) reclaimed_ratio() +// is selected. +HeapWord* +PSParallelCompact::compute_dense_prefix(const SpaceId id, + bool maximum_compaction) +{ + const size_t chunk_size = ParallelCompactData::ChunkSize; + const ParallelCompactData& sd = summary_data(); + + const MutableSpace* const space = _space_info[id].space(); + HeapWord* const top = space->top(); + HeapWord* const top_aligned_up = sd.chunk_align_up(top); + HeapWord* const new_top = _space_info[id].new_top(); + HeapWord* const new_top_aligned_up = sd.chunk_align_up(new_top); + HeapWord* const bottom = space->bottom(); + const ChunkData* const beg_cp = sd.addr_to_chunk_ptr(bottom); + const ChunkData* const top_cp = sd.addr_to_chunk_ptr(top_aligned_up); + const ChunkData* const new_top_cp = sd.addr_to_chunk_ptr(new_top_aligned_up); + + // Skip full chunks at the beginning of the space--they are necessarily part + // of the dense prefix. + const ChunkData* const full_cp = first_dead_space_chunk(beg_cp, new_top_cp); + assert(full_cp->destination() == sd.chunk_to_addr(full_cp) || + space->is_empty(), "no dead space allowed to the left"); + assert(full_cp->data_size() < chunk_size || full_cp == new_top_cp - 1, + "chunk must have dead space"); + + // The gc number is saved whenever a maximum compaction is done, and used to + // determine when the maximum compaction interval has expired. This avoids + // successive max compactions for different reasons. + assert(total_invocations() >= _maximum_compaction_gc_num, "sanity"); + const size_t gcs_since_max = total_invocations() - _maximum_compaction_gc_num; + const bool interval_ended = gcs_since_max > HeapMaximumCompactionInterval || + total_invocations() == HeapFirstMaximumCompactionCount; + if (maximum_compaction || full_cp == top_cp || interval_ended) { + _maximum_compaction_gc_num = total_invocations(); + return sd.chunk_to_addr(full_cp); + } + + const size_t space_live = pointer_delta(new_top, bottom); + const size_t space_used = space->used_in_words(); + const size_t space_capacity = space->capacity_in_words(); + + const double density = double(space_live) / double(space_capacity); + const size_t min_percent_free = + id == perm_space_id ? PermMarkSweepDeadRatio : MarkSweepDeadRatio; + const double limiter = dead_wood_limiter(density, min_percent_free); + const size_t dead_wood_max = space_used - space_live; + const size_t dead_wood_limit = MIN2(size_t(space_capacity * limiter), + dead_wood_max); + + if (TraceParallelOldGCDensePrefix) { + tty->print_cr("space_live=" SIZE_FORMAT " " "space_used=" SIZE_FORMAT " " + "space_cap=" SIZE_FORMAT, + space_live, space_used, + space_capacity); + tty->print_cr("dead_wood_limiter(%6.4f, %d)=%6.4f " + "dead_wood_max=" SIZE_FORMAT " dead_wood_limit=" SIZE_FORMAT, + density, min_percent_free, limiter, + dead_wood_max, dead_wood_limit); + } + + // Locate the chunk with the desired amount of dead space to the left. + const ChunkData* const limit_cp = + dead_wood_limit_chunk(full_cp, top_cp, dead_wood_limit); + + // Scan from the first chunk with dead space to the limit chunk and find the + // one with the best (largest) reclaimed ratio. + double best_ratio = 0.0; + const ChunkData* best_cp = full_cp; + for (const ChunkData* cp = full_cp; cp < limit_cp; ++cp) { + double tmp_ratio = reclaimed_ratio(cp, bottom, top, new_top); + if (tmp_ratio > best_ratio) { + best_cp = cp; + best_ratio = tmp_ratio; + } + } + +#if 0 + // Something to consider: if the chunk with the best ratio is 'close to' the + // first chunk w/free space, choose the first chunk with free space + // ("first-free"). The first-free chunk is usually near the start of the + // heap, which means we are copying most of the heap already, so copy a bit + // more to get complete compaction. + if (pointer_delta(best_cp, full_cp, sizeof(ChunkData)) < 4) { + _maximum_compaction_gc_num = total_invocations(); + best_cp = full_cp; + } +#endif // #if 0 + + return sd.chunk_to_addr(best_cp); +} + +void PSParallelCompact::summarize_spaces_quick() +{ + for (unsigned int i = 0; i < last_space_id; ++i) { + const MutableSpace* space = _space_info[i].space(); + bool result = _summary_data.summarize(space->bottom(), space->end(), + space->bottom(), space->top(), + _space_info[i].new_top_addr()); + assert(result, "should never fail"); + _space_info[i].set_dense_prefix(space->bottom()); + } +} + +void PSParallelCompact::fill_dense_prefix_end(SpaceId id) +{ + HeapWord* const dense_prefix_end = dense_prefix(id); + const ChunkData* chunk = _summary_data.addr_to_chunk_ptr(dense_prefix_end); + const idx_t dense_prefix_bit = _mark_bitmap.addr_to_bit(dense_prefix_end); + if (dead_space_crosses_boundary(chunk, dense_prefix_bit)) { + // Only enough dead space is filled so that any remaining dead space to the + // left is larger than the minimum filler object. (The remainder is filled + // during the copy/update phase.) + // + // The size of the dead space to the right of the boundary is not a + // concern, since compaction will be able to use whatever space is + // available. + // + // Here '||' is the boundary, 'x' represents a don't care bit and a box + // surrounds the space to be filled with an object. + // + // In the 32-bit VM, each bit represents two 32-bit words: + // +---+ + // a) beg_bits: ... x x x | 0 | || 0 x x ... + // end_bits: ... x x x | 0 | || 0 x x ... + // +---+ + // + // In the 64-bit VM, each bit represents one 64-bit word: + // +------------+ + // b) beg_bits: ... x x x | 0 || 0 | x x ... + // end_bits: ... x x 1 | 0 || 0 | x x ... + // +------------+ + // +-------+ + // c) beg_bits: ... x x | 0 0 | || 0 x x ... + // end_bits: ... x 1 | 0 0 | || 0 x x ... + // +-------+ + // +-----------+ + // d) beg_bits: ... x | 0 0 0 | || 0 x x ... + // end_bits: ... 1 | 0 0 0 | || 0 x x ... + // +-----------+ + // +-------+ + // e) beg_bits: ... 0 0 | 0 0 | || 0 x x ... + // end_bits: ... 0 0 | 0 0 | || 0 x x ... + // +-------+ + + // Initially assume case a, c or e will apply. + size_t obj_len = (size_t)oopDesc::header_size(); + HeapWord* obj_beg = dense_prefix_end - obj_len; + +#ifdef _LP64 + if (_mark_bitmap.is_obj_end(dense_prefix_bit - 2)) { + // Case b above. + obj_beg = dense_prefix_end - 1; + } else if (!_mark_bitmap.is_obj_end(dense_prefix_bit - 3) && + _mark_bitmap.is_obj_end(dense_prefix_bit - 4)) { + // Case d above. + obj_beg = dense_prefix_end - 3; + obj_len = 3; + } +#endif // #ifdef _LP64 + + MemRegion region(obj_beg, obj_len); + SharedHeap::fill_region_with_object(region); + _mark_bitmap.mark_obj(obj_beg, obj_len); + _summary_data.add_obj(obj_beg, obj_len); + assert(start_array(id) != NULL, "sanity"); + start_array(id)->allocate_block(obj_beg); + } +} + +void +PSParallelCompact::summarize_space(SpaceId id, bool maximum_compaction) +{ + assert(id < last_space_id, "id out of range"); + + const MutableSpace* space = _space_info[id].space(); + HeapWord** new_top_addr = _space_info[id].new_top_addr(); + + HeapWord* dense_prefix_end = compute_dense_prefix(id, maximum_compaction); + _space_info[id].set_dense_prefix(dense_prefix_end); + +#ifndef PRODUCT + if (TraceParallelOldGCDensePrefix) { + print_dense_prefix_stats("ratio", id, maximum_compaction, dense_prefix_end); + HeapWord* addr = compute_dense_prefix_via_density(id, maximum_compaction); + print_dense_prefix_stats("density", id, maximum_compaction, addr); + } +#endif // #ifndef PRODUCT + + // If dead space crosses the dense prefix boundary, it is (at least partially) + // filled with a dummy object, marked live and added to the summary data. + // This simplifies the copy/update phase and must be done before the final + // locations of objects are determined, to prevent leaving a fragment of dead + // space that is too small to fill with an object. + if (!maximum_compaction && dense_prefix_end != space->bottom()) { + fill_dense_prefix_end(id); + } + + // Compute the destination of each Chunk, and thus each object. + _summary_data.summarize_dense_prefix(space->bottom(), dense_prefix_end); + _summary_data.summarize(dense_prefix_end, space->end(), + dense_prefix_end, space->top(), + new_top_addr); + + if (TraceParallelOldGCSummaryPhase) { + const size_t chunk_size = ParallelCompactData::ChunkSize; + const size_t dp_chunk = _summary_data.addr_to_chunk_idx(dense_prefix_end); + const size_t dp_words = pointer_delta(dense_prefix_end, space->bottom()); + const HeapWord* nt_aligned_up = _summary_data.chunk_align_up(*new_top_addr); + const size_t cr_words = pointer_delta(nt_aligned_up, dense_prefix_end); + tty->print_cr("id=%d cap=" SIZE_FORMAT " dp=" PTR_FORMAT " " + "dp_chunk=" SIZE_FORMAT " " "dp_count=" SIZE_FORMAT " " + "cr_count=" SIZE_FORMAT " " "nt=" PTR_FORMAT, + id, space->capacity_in_words(), dense_prefix_end, + dp_chunk, dp_words / chunk_size, + cr_words / chunk_size, *new_top_addr); + } +} + +void PSParallelCompact::summary_phase(ParCompactionManager* cm, + bool maximum_compaction) +{ + EventMark m("2 summarize"); + TraceTime tm("summary phase", print_phases(), true, gclog_or_tty); + // trace("2"); + +#ifdef ASSERT + if (VerifyParallelOldWithMarkSweep && + (PSParallelCompact::total_invocations() % + VerifyParallelOldWithMarkSweepInterval) == 0) { + verify_mark_bitmap(_mark_bitmap); + } + if (TraceParallelOldGCMarkingPhase) { + tty->print_cr("add_obj_count=" SIZE_FORMAT " " + "add_obj_bytes=" SIZE_FORMAT, + add_obj_count, add_obj_size * HeapWordSize); + tty->print_cr("mark_bitmap_count=" SIZE_FORMAT " " + "mark_bitmap_bytes=" SIZE_FORMAT, + mark_bitmap_count, mark_bitmap_size * HeapWordSize); + } +#endif // #ifdef ASSERT + + // Quick summarization of each space into itself, to see how much is live. + summarize_spaces_quick(); + + if (TraceParallelOldGCSummaryPhase) { + tty->print_cr("summary_phase: after summarizing each space to self"); + Universe::print(); + NOT_PRODUCT(print_chunk_ranges()); + if (Verbose) { + NOT_PRODUCT(print_initial_summary_data(_summary_data, _space_info)); + } + } + + // The amount of live data that will end up in old space (assuming it fits). + size_t old_space_total_live = 0; + unsigned int id; + for (id = old_space_id; id < last_space_id; ++id) { + old_space_total_live += pointer_delta(_space_info[id].new_top(), + _space_info[id].space()->bottom()); + } + + const MutableSpace* old_space = _space_info[old_space_id].space(); + if (old_space_total_live > old_space->capacity_in_words()) { + // XXX - should also try to expand + maximum_compaction = true; + } else if (!UseParallelOldGCDensePrefix) { + maximum_compaction = true; + } + + // Permanent and Old generations. + summarize_space(perm_space_id, maximum_compaction); + summarize_space(old_space_id, maximum_compaction); + + // Summarize the remaining spaces (those in the young gen) into old space. If + // the live data from a space doesn't fit, the existing summarization is left + // intact, so the data is compacted down within the space itself. + HeapWord** new_top_addr = _space_info[old_space_id].new_top_addr(); + HeapWord* const target_space_end = old_space->end(); + for (id = eden_space_id; id < last_space_id; ++id) { + const MutableSpace* space = _space_info[id].space(); + const size_t live = pointer_delta(_space_info[id].new_top(), + space->bottom()); + const size_t available = pointer_delta(target_space_end, *new_top_addr); + if (live <= available) { + // All the live data will fit. + if (TraceParallelOldGCSummaryPhase) { + tty->print_cr("summarizing %d into old_space @ " PTR_FORMAT, + id, *new_top_addr); + } + _summary_data.summarize(*new_top_addr, target_space_end, + space->bottom(), space->top(), + new_top_addr); + + // Reset the new_top value for the space. + _space_info[id].set_new_top(space->bottom()); + + // Clear the source_chunk field for each chunk in the space. + ChunkData* beg_chunk = _summary_data.addr_to_chunk_ptr(space->bottom()); + ChunkData* end_chunk = _summary_data.addr_to_chunk_ptr(space->top() - 1); + while (beg_chunk <= end_chunk) { + beg_chunk->set_source_chunk(0); + ++beg_chunk; + } + } + } + + // Fill in the block data after any changes to the chunks have + // been made. +#ifdef ASSERT + summarize_blocks(cm, perm_space_id); + summarize_blocks(cm, old_space_id); +#else + if (!UseParallelOldGCChunkPointerCalc) { + summarize_blocks(cm, perm_space_id); + summarize_blocks(cm, old_space_id); + } +#endif + + if (TraceParallelOldGCSummaryPhase) { + tty->print_cr("summary_phase: after final summarization"); + Universe::print(); + NOT_PRODUCT(print_chunk_ranges()); + if (Verbose) { + NOT_PRODUCT(print_generic_summary_data(_summary_data, _space_info)); + } + } +} + +// Fill in the BlockData. +// Iterate over the spaces and within each space iterate over +// the chunks and fill in the BlockData for each chunk. + +void PSParallelCompact::summarize_blocks(ParCompactionManager* cm, + SpaceId first_compaction_space_id) { +#if 0 + DEBUG_ONLY(ParallelCompactData::BlockData::set_cur_phase(1);) + for (SpaceId cur_space_id = first_compaction_space_id; + cur_space_id != last_space_id; + cur_space_id = next_compaction_space_id(cur_space_id)) { + // Iterate over the chunks in the space + size_t start_chunk_index = + _summary_data.addr_to_chunk_idx(space(cur_space_id)->bottom()); + BitBlockUpdateClosure bbu(mark_bitmap(), + cm, + start_chunk_index); + // Iterate over blocks. + for (size_t chunk_index = start_chunk_index; + chunk_index < _summary_data.chunk_count() && + _summary_data.chunk_to_addr(chunk_index) < space(cur_space_id)->top(); + chunk_index++) { + + // Reset the closure for the new chunk. Note that the closure + // maintains some data that does not get reset for each chunk + // so a new instance of the closure is no appropriate. + bbu.reset_chunk(chunk_index); + + // Start the iteration with the first live object. This + // may return the end of the chunk. That is acceptable since + // it will properly limit the iterations. + ParMarkBitMap::idx_t left_offset = mark_bitmap()->addr_to_bit( + _summary_data.first_live_or_end_in_chunk(chunk_index)); + + // End the iteration at the end of the chunk. + HeapWord* chunk_addr = _summary_data.chunk_to_addr(chunk_index); + HeapWord* chunk_end = chunk_addr + ParallelCompactData::ChunkSize; + ParMarkBitMap::idx_t right_offset = + mark_bitmap()->addr_to_bit(chunk_end); + + // Blocks that have not objects starting in them can be + // skipped because their data will never be used. + if (left_offset < right_offset) { + + // Iterate through the objects in the chunk. + ParMarkBitMap::idx_t last_offset = + mark_bitmap()->pair_iterate(&bbu, left_offset, right_offset); + + // If last_offset is less than right_offset, then the iterations + // terminated while it was looking for an end bit. "last_offset" + // is then the offset for the last start bit. In this situation + // the "offset" field for the next block to the right (_cur_block + 1) + // will not have been update although there may be live data + // to the left of the chunk. + + size_t cur_block_plus_1 = bbu.cur_block() + 1; + HeapWord* cur_block_plus_1_addr = + _summary_data.block_to_addr(bbu.cur_block()) + + ParallelCompactData::BlockSize; + HeapWord* last_offset_addr = mark_bitmap()->bit_to_addr(last_offset); + #if 1 // This code works. The else doesn't but should. Why does it? + // The current block (cur_block()) has already been updated. + // The last block that may need to be updated is either the + // next block (current block + 1) or the block where the + // last object starts (which can be greater than the + // next block if there were no objects found in intervening + // blocks). + size_t last_block = + MAX2(bbu.cur_block() + 1, + _summary_data.addr_to_block_idx(last_offset_addr)); + #else + // The current block has already been updated. The only block + // that remains to be updated is the block where the last + // object in the chunk starts. + size_t last_block = _summary_data.addr_to_block_idx(last_offset_addr); + #endif + assert_bit_is_start(last_offset); + assert((last_block == _summary_data.block_count()) || + (_summary_data.block(last_block)->raw_offset() == 0), + "Should not have been set"); + // Is the last block still in the current chunk? If still + // in this chunk, update the last block (the counting that + // included the current block is meant for the offset of the last + // block). If not in this chunk, do nothing. Should not + // update a block in the next chunk. + if (ParallelCompactData::chunk_contains_block(bbu.chunk_index(), + last_block)) { + if (last_offset < right_offset) { + // The last object started in this chunk but ends beyond + // this chunk. Update the block for this last object. + assert(mark_bitmap()->is_marked(last_offset), "Should be marked"); + // No end bit was found. The closure takes care of + // the cases where + // an objects crosses over into the next block + // an objects starts and ends in the next block + // It does not handle the case where an object is + // the first object in a later block and extends + // past the end of the chunk (i.e., the closure + // only handles complete objects that are in the range + // it is given). That object is handed back here + // for any special consideration necessary. + // + // Is the first bit in the last block a start or end bit? + // + // If the partial object ends in the last block L, + // then the 1st bit in L may be an end bit. + // + // Else does the last object start in a block after the current + // block? A block AA will already have been updated if an + // object ends in the next block AA+1. An object found to end in + // the AA+1 is the trigger that updates AA. Objects are being + // counted in the current block for updaing a following + // block. An object may start in later block + // block but may extend beyond the last block in the chunk. + // Updates are only done when the end of an object has been + // found. If the last object (covered by block L) starts + // beyond the current block, then no object ends in L (otherwise + // L would be the current block). So the first bit in L is + // a start bit. + // + // Else the last objects start in the current block and ends + // beyond the chunk. The current block has already been + // updated and there is no later block (with an object + // starting in it) that needs to be updated. + // + if (_summary_data.partial_obj_ends_in_block(last_block)) { + _summary_data.block(last_block)->set_end_bit_offset( + bbu.live_data_left()); + } else if (last_offset_addr >= cur_block_plus_1_addr) { + // The start of the object is on a later block + // (to the right of the current block and there are no + // complete live objects to the left of this last object + // within the chunk. + // The first bit in the block is for the start of the + // last object. + _summary_data.block(last_block)->set_start_bit_offset( + bbu.live_data_left()); + } else { + // The start of the last object was found in + // the current chunk (which has already + // been updated). + assert(bbu.cur_block() == + _summary_data.addr_to_block_idx(last_offset_addr), + "Should be a block already processed"); + } +#ifdef ASSERT + // Is there enough block information to find this object? + // The destination of the chunk has not been set so the + // values returned by calc_new_pointer() and + // block_calc_new_pointer() will only be + // offsets. But they should agree. + HeapWord* moved_obj_with_chunks = + _summary_data.chunk_calc_new_pointer(last_offset_addr); + HeapWord* moved_obj_with_blocks = + _summary_data.calc_new_pointer(last_offset_addr); + assert(moved_obj_with_chunks == moved_obj_with_blocks, + "Block calculation is wrong"); +#endif + } else if (last_block < _summary_data.block_count()) { + // Iterations ended looking for a start bit (but + // did not run off the end of the block table). + _summary_data.block(last_block)->set_start_bit_offset( + bbu.live_data_left()); + } + } +#ifdef ASSERT + // Is there enough block information to find this object? + HeapWord* left_offset_addr = mark_bitmap()->bit_to_addr(left_offset); + HeapWord* moved_obj_with_chunks = + _summary_data.calc_new_pointer(left_offset_addr); + HeapWord* moved_obj_with_blocks = + _summary_data.calc_new_pointer(left_offset_addr); + assert(moved_obj_with_chunks == moved_obj_with_blocks, + "Block calculation is wrong"); +#endif + + // Is there another block after the end of this chunk? +#ifdef ASSERT + if (last_block < _summary_data.block_count()) { + // No object may have been found in a block. If that + // block is at the end of the chunk, the iteration will + // terminate without incrementing the current block so + // that the current block is not the last block in the + // chunk. That situation precludes asserting that the + // current block is the last block in the chunk. Assert + // the lesser condition that the current block does not + // exceed the chunk. + assert(_summary_data.block_to_addr(last_block) <= + (_summary_data.chunk_to_addr(chunk_index) + + ParallelCompactData::ChunkSize), + "Chunk and block inconsistency"); + assert(last_offset <= right_offset, "Iteration over ran end"); + } +#endif + } +#ifdef ASSERT + if (PrintGCDetails && Verbose) { + if (_summary_data.chunk(chunk_index)->partial_obj_size() == 1) { + size_t first_block = + chunk_index / ParallelCompactData::BlocksPerChunk; + gclog_or_tty->print_cr("first_block " PTR_FORMAT + " _offset " PTR_FORMAT + "_first_is_start_bit %d", + first_block, + _summary_data.block(first_block)->raw_offset(), + _summary_data.block(first_block)->first_is_start_bit()); + } + } +#endif + } + } + DEBUG_ONLY(ParallelCompactData::BlockData::set_cur_phase(16);) +#endif // #if 0 +} + +// This method should contain all heap-specific policy for invoking a full +// collection. invoke_no_policy() will only attempt to compact the heap; it +// will do nothing further. If we need to bail out for policy reasons, scavenge +// before full gc, or any other specialized behavior, it needs to be added here. +// +// Note that this method should only be called from the vm_thread while at a +// safepoint. +void PSParallelCompact::invoke(bool maximum_heap_compaction) { + assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); + assert(Thread::current() == (Thread*)VMThread::vm_thread(), + "should be in vm thread"); + ParallelScavengeHeap* heap = gc_heap(); + GCCause::Cause gc_cause = heap->gc_cause(); + assert(!heap->is_gc_active(), "not reentrant"); + + PSAdaptiveSizePolicy* policy = heap->size_policy(); + + // Before each allocation/collection attempt, find out from the + // policy object if GCs are, on the whole, taking too long. If so, + // bail out without attempting a collection. The exceptions are + // for explicitly requested GC's. + if (!policy->gc_time_limit_exceeded() || + GCCause::is_user_requested_gc(gc_cause) || + GCCause::is_serviceability_requested_gc(gc_cause)) { + IsGCActiveMark mark; + + if (ScavengeBeforeFullGC) { + PSScavenge::invoke_no_policy(); + } + + PSParallelCompact::invoke_no_policy(maximum_heap_compaction); + } +} + +bool ParallelCompactData::chunk_contains(size_t chunk_index, HeapWord* addr) { + size_t addr_chunk_index = addr_to_chunk_idx(addr); + return chunk_index == addr_chunk_index; +} + +bool ParallelCompactData::chunk_contains_block(size_t chunk_index, + size_t block_index) { + size_t first_block_in_chunk = chunk_index * BlocksPerChunk; + size_t last_block_in_chunk = (chunk_index + 1) * BlocksPerChunk - 1; + + return (first_block_in_chunk <= block_index) && + (block_index <= last_block_in_chunk); +} + +// This method contains no policy. You should probably +// be calling invoke() instead. +void PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint"); + assert(ref_processor() != NULL, "Sanity"); + + if (GC_locker::is_active()) { + return; + } + + TimeStamp marking_start; + TimeStamp compaction_start; + TimeStamp collection_exit; + + // "serial_CM" is needed until the parallel implementation + // of the move and update is done. + ParCompactionManager* serial_CM = new ParCompactionManager(); + // Don't initialize more than once. + // serial_CM->initialize(&summary_data(), mark_bitmap()); + + ParallelScavengeHeap* heap = gc_heap(); + GCCause::Cause gc_cause = heap->gc_cause(); + PSYoungGen* young_gen = heap->young_gen(); + PSOldGen* old_gen = heap->old_gen(); + PSPermGen* perm_gen = heap->perm_gen(); + PSAdaptiveSizePolicy* size_policy = heap->size_policy(); + + _print_phases = PrintGCDetails && PrintParallelOldGCPhaseTimes; + + // Make sure data structures are sane, make the heap parsable, and do other + // miscellaneous bookkeeping. + PreGCValues pre_gc_values; + pre_compact(&pre_gc_values); + + // Place after pre_compact() where the number of invocations is incremented. + AdaptiveSizePolicyOutput(size_policy, heap->total_collections()); + + { + ResourceMark rm; + HandleMark hm; + + const bool is_system_gc = gc_cause == GCCause::_java_lang_system_gc; + + // This is useful for debugging but don't change the output the + // the customer sees. + const char* gc_cause_str = "Full GC"; + if (is_system_gc && PrintGCDetails) { + gc_cause_str = "Full GC (System)"; + } + gclog_or_tty->date_stamp(PrintGC && PrintGCDateStamps); + TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); + TraceTime t1(gc_cause_str, PrintGC, !PrintGCDetails, gclog_or_tty); + TraceCollectorStats tcs(counters()); + TraceMemoryManagerStats tms(true /* Full GC */); + + if (TraceGen1Time) accumulated_time()->start(); + + // Let the size policy know we're starting + size_policy->major_collection_begin(); + + // When collecting the permanent generation methodOops may be moving, + // so we either have to flush all bcp data or convert it into bci. + CodeCache::gc_prologue(); + Threads::gc_prologue(); + + NOT_PRODUCT(ref_processor()->verify_no_references_recorded()); + COMPILER2_PRESENT(DerivedPointerTable::clear()); + + ref_processor()->enable_discovery(); + + bool marked_for_unloading = false; + + marking_start.update(); + marking_phase(serial_CM, maximum_heap_compaction); + +#ifndef PRODUCT + if (TraceParallelOldGCMarkingPhase) { + gclog_or_tty->print_cr("marking_phase: cas_tries %d cas_retries %d " + "cas_by_another %d", + mark_bitmap()->cas_tries(), mark_bitmap()->cas_retries(), + mark_bitmap()->cas_by_another()); + } +#endif // #ifndef PRODUCT + +#ifdef ASSERT + if (VerifyParallelOldWithMarkSweep && + (PSParallelCompact::total_invocations() % + VerifyParallelOldWithMarkSweepInterval) == 0) { + gclog_or_tty->print_cr("Verify marking with mark_sweep_phase1()"); + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("mark_sweep_phase1:"); + } + // Clear the discovered lists so that discovered objects + // don't look like they have been discovered twice. + ref_processor()->clear_discovered_references(); + + PSMarkSweep::allocate_stacks(); + MemRegion mr = Universe::heap()->reserved_region(); + PSMarkSweep::ref_processor()->enable_discovery(); + PSMarkSweep::mark_sweep_phase1(maximum_heap_compaction); + } +#endif + + bool max_on_system_gc = UseMaximumCompactionOnSystemGC && is_system_gc; + summary_phase(serial_CM, maximum_heap_compaction || max_on_system_gc); + +#ifdef ASSERT + if (VerifyParallelOldWithMarkSweep && + (PSParallelCompact::total_invocations() % + VerifyParallelOldWithMarkSweepInterval) == 0) { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("mark_sweep_phase2:"); + } + PSMarkSweep::mark_sweep_phase2(); + } +#endif + + COMPILER2_PRESENT(assert(DerivedPointerTable::is_active(), "Sanity")); + COMPILER2_PRESENT(DerivedPointerTable::set_active(false)); + + // adjust_roots() updates Universe::_intArrayKlassObj which is + // needed by the compaction for filling holes in the dense prefix. + adjust_roots(); + +#ifdef ASSERT + if (VerifyParallelOldWithMarkSweep && + (PSParallelCompact::total_invocations() % + VerifyParallelOldWithMarkSweepInterval) == 0) { + // Do a separate verify phase so that the verify + // code can use the the forwarding pointers to + // check the new pointer calculation. The restore_marks() + // has to be done before the real compact. + serial_CM->set_action(ParCompactionManager::VerifyUpdate); + compact_perm(serial_CM); + compact_serial(serial_CM); + serial_CM->set_action(ParCompactionManager::ResetObjects); + compact_perm(serial_CM); + compact_serial(serial_CM); + serial_CM->set_action(ParCompactionManager::UpdateAndCopy); + + // For debugging only + PSMarkSweep::restore_marks(); + PSMarkSweep::deallocate_stacks(); + } +#endif + + compaction_start.update(); + // Does the perm gen always have to be done serially because + // klasses are used in the update of an object? + compact_perm(serial_CM); + + if (UseParallelOldGCCompacting) { + compact(); + } else { + compact_serial(serial_CM); + } + + delete serial_CM; + + // Reset the mark bitmap, summary data, and do other bookkeeping. Must be + // done before resizing. + post_compact(); + + // Let the size policy know we're done + size_policy->major_collection_end(old_gen->used_in_bytes(), gc_cause); + + if (UseAdaptiveSizePolicy) { + if (PrintAdaptiveSizePolicy) { + gclog_or_tty->print("AdaptiveSizeStart: "); + gclog_or_tty->stamp(); + gclog_or_tty->print_cr(" collection: %d ", + heap->total_collections()); + if (Verbose) { + gclog_or_tty->print("old_gen_capacity: %d young_gen_capacity: %d" + " perm_gen_capacity: %d ", + old_gen->capacity_in_bytes(), young_gen->capacity_in_bytes(), + perm_gen->capacity_in_bytes()); + } + } + + // Don't check if the size_policy is ready here. Let + // the size_policy check that internally. + if (UseAdaptiveGenerationSizePolicyAtMajorCollection && + ((gc_cause != GCCause::_java_lang_system_gc) || + UseAdaptiveSizePolicyWithSystemGC)) { + // Calculate optimal free space amounts + assert(young_gen->max_size() > + young_gen->from_space()->capacity_in_bytes() + + young_gen->to_space()->capacity_in_bytes(), + "Sizes of space in young gen are out-of-bounds"); + size_t max_eden_size = young_gen->max_size() - + young_gen->from_space()->capacity_in_bytes() - + young_gen->to_space()->capacity_in_bytes(); + size_policy->compute_generation_free_space(young_gen->used_in_bytes(), + young_gen->eden_space()->used_in_bytes(), + old_gen->used_in_bytes(), + perm_gen->used_in_bytes(), + young_gen->eden_space()->capacity_in_bytes(), + old_gen->max_gen_size(), + max_eden_size, + true /* full gc*/, + gc_cause); + + heap->resize_old_gen(size_policy->calculated_old_free_size_in_bytes()); + + // Don't resize the young generation at an major collection. A + // desired young generation size may have been calculated but + // resizing the young generation complicates the code because the + // resizing of the old generation may have moved the boundary + // between the young generation and the old generation. Let the + // young generation resizing happen at the minor collections. + } + if (PrintAdaptiveSizePolicy) { + gclog_or_tty->print_cr("AdaptiveSizeStop: collection: %d ", + heap->total_collections()); + } + } + + if (UsePerfData) { + PSGCAdaptivePolicyCounters* const counters = heap->gc_policy_counters(); + counters->update_counters(); + counters->update_old_capacity(old_gen->capacity_in_bytes()); + counters->update_young_capacity(young_gen->capacity_in_bytes()); + } + + heap->resize_all_tlabs(); + + // We collected the perm gen, so we'll resize it here. + perm_gen->compute_new_size(pre_gc_values.perm_gen_used()); + + if (TraceGen1Time) accumulated_time()->stop(); + + if (PrintGC) { + if (PrintGCDetails) { + // No GC timestamp here. This is after GC so it would be confusing. + young_gen->print_used_change(pre_gc_values.young_gen_used()); + old_gen->print_used_change(pre_gc_values.old_gen_used()); + heap->print_heap_change(pre_gc_values.heap_used()); + // Print perm gen last (print_heap_change() excludes the perm gen). + perm_gen->print_used_change(pre_gc_values.perm_gen_used()); + } else { + heap->print_heap_change(pre_gc_values.heap_used()); + } + } + + // Track memory usage and detect low memory + MemoryService::track_memory_usage(); + heap->update_counters(); + + if (PrintGCDetails) { + if (size_policy->print_gc_time_limit_would_be_exceeded()) { + if (size_policy->gc_time_limit_exceeded()) { + gclog_or_tty->print_cr(" GC time is exceeding GCTimeLimit " + "of %d%%", GCTimeLimit); + } else { + gclog_or_tty->print_cr(" GC time would exceed GCTimeLimit " + "of %d%%", GCTimeLimit); + } + } + size_policy->set_print_gc_time_limit_would_be_exceeded(false); + } + } + + if (VerifyAfterGC && heap->total_collections() >= VerifyGCStartAt) { + HandleMark hm; // Discard invalid handles created during verification + gclog_or_tty->print(" VerifyAfterGC:"); + Universe::verify(false); + } + + // Re-verify object start arrays + if (VerifyObjectStartArray && + VerifyAfterGC) { + old_gen->verify_object_start_array(); + perm_gen->verify_object_start_array(); + } + + NOT_PRODUCT(ref_processor()->verify_no_references_recorded()); + + collection_exit.update(); + + if (PrintHeapAtGC) { + Universe::print_heap_after_gc(); + } + if (PrintGCTaskTimeStamps) { + gclog_or_tty->print_cr("VM-Thread " INT64_FORMAT " " INT64_FORMAT " " + INT64_FORMAT, + marking_start.ticks(), compaction_start.ticks(), + collection_exit.ticks()); + gc_task_manager()->print_task_time_stamps(); + } +} + +bool PSParallelCompact::absorb_live_data_from_eden(PSAdaptiveSizePolicy* size_policy, + PSYoungGen* young_gen, + PSOldGen* old_gen) { + MutableSpace* const eden_space = young_gen->eden_space(); + assert(!eden_space->is_empty(), "eden must be non-empty"); + assert(young_gen->virtual_space()->alignment() == + old_gen->virtual_space()->alignment(), "alignments do not match"); + + if (!(UseAdaptiveSizePolicy && UseAdaptiveGCBoundary)) { + return false; + } + + // Both generations must be completely committed. + if (young_gen->virtual_space()->uncommitted_size() != 0) { + return false; + } + if (old_gen->virtual_space()->uncommitted_size() != 0) { + return false; + } + + // Figure out how much to take from eden. Include the average amount promoted + // in the total; otherwise the next young gen GC will simply bail out to a + // full GC. + const size_t alignment = old_gen->virtual_space()->alignment(); + const size_t eden_used = eden_space->used_in_bytes(); + const size_t promoted = (size_t)size_policy->avg_promoted()->padded_average(); + const size_t absorb_size = align_size_up(eden_used + promoted, alignment); + const size_t eden_capacity = eden_space->capacity_in_bytes(); + + if (absorb_size >= eden_capacity) { + return false; // Must leave some space in eden. + } + + const size_t new_young_size = young_gen->capacity_in_bytes() - absorb_size; + if (new_young_size < young_gen->min_gen_size()) { + return false; // Respect young gen minimum size. + } + + if (TraceAdaptiveGCBoundary && Verbose) { + gclog_or_tty->print(" absorbing " SIZE_FORMAT "K: " + "eden " SIZE_FORMAT "K->" SIZE_FORMAT "K " + "from " SIZE_FORMAT "K, to " SIZE_FORMAT "K " + "young_gen " SIZE_FORMAT "K->" SIZE_FORMAT "K ", + absorb_size / K, + eden_capacity / K, (eden_capacity - absorb_size) / K, + young_gen->from_space()->used_in_bytes() / K, + young_gen->to_space()->used_in_bytes() / K, + young_gen->capacity_in_bytes() / K, new_young_size / K); + } + + // Fill the unused part of the old gen. + MutableSpace* const old_space = old_gen->object_space(); + MemRegion old_gen_unused(old_space->top(), old_space->end()); + if (!old_gen_unused.is_empty()) { + SharedHeap::fill_region_with_object(old_gen_unused); + } + + // Take the live data from eden and set both top and end in the old gen to + // eden top. (Need to set end because reset_after_change() mangles the region + // from end to virtual_space->high() in debug builds). + HeapWord* const new_top = eden_space->top(); + old_gen->virtual_space()->expand_into(young_gen->virtual_space(), + absorb_size); + young_gen->reset_after_change(); + old_space->set_top(new_top); + old_space->set_end(new_top); + old_gen->reset_after_change(); + + // Update the object start array for the filler object and the data from eden. + ObjectStartArray* const start_array = old_gen->start_array(); + HeapWord* const start = old_gen_unused.start(); + for (HeapWord* addr = start; addr < new_top; addr += oop(addr)->size()) { + start_array->allocate_block(addr); + } + + // Could update the promoted average here, but it is not typically updated at + // full GCs and the value to use is unclear. Something like + // + // cur_promoted_avg + absorb_size / number_of_scavenges_since_last_full_gc. + + size_policy->set_bytes_absorbed_from_eden(absorb_size); + return true; +} + +GCTaskManager* const PSParallelCompact::gc_task_manager() { + assert(ParallelScavengeHeap::gc_task_manager() != NULL, + "shouldn't return NULL"); + return ParallelScavengeHeap::gc_task_manager(); +} + +void PSParallelCompact::marking_phase(ParCompactionManager* cm, + bool maximum_heap_compaction) { + // Recursively traverse all live objects and mark them + EventMark m("1 mark object"); + TraceTime tm("marking phase", print_phases(), true, gclog_or_tty); + + ParallelScavengeHeap* heap = gc_heap(); + uint parallel_gc_threads = heap->gc_task_manager()->workers(); + TaskQueueSetSuper* qset = ParCompactionManager::chunk_array(); + ParallelTaskTerminator terminator(parallel_gc_threads, qset); + + PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); + PSParallelCompact::FollowStackClosure follow_stack_closure(cm); + + { + TraceTime tm_m("par mark", print_phases(), true, gclog_or_tty); + + GCTaskQueue* q = GCTaskQueue::create(); + + q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::universe)); + q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::jni_handles)); + // We scan the thread roots in parallel + Threads::create_thread_roots_marking_tasks(q); + q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::object_synchronizer)); + q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::flat_profiler)); + q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::management)); + q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::system_dictionary)); + q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::jvmti)); + q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::vm_symbols)); + + if (parallel_gc_threads > 1) { + for (uint j = 0; j < parallel_gc_threads; j++) { + q->enqueue(new StealMarkingTask(&terminator)); + } + } + + WaitForBarrierGCTask* fin = WaitForBarrierGCTask::create(); + q->enqueue(fin); + + gc_task_manager()->add_list(q); + + fin->wait_for(); + + // We have to release the barrier tasks! + WaitForBarrierGCTask::destroy(fin); + } + + // Process reference objects found during marking + { + TraceTime tm_r("reference processing", print_phases(), true, gclog_or_tty); + ReferencePolicy *soft_ref_policy; + if (maximum_heap_compaction) { + soft_ref_policy = new AlwaysClearPolicy(); + } else { +#ifdef COMPILER2 + soft_ref_policy = new LRUMaxHeapPolicy(); +#else + soft_ref_policy = new LRUCurrentHeapPolicy(); +#endif // COMPILER2 + } + assert(soft_ref_policy != NULL, "No soft reference policy"); + if (ref_processor()->processing_is_mt()) { + RefProcTaskExecutor task_executor; + ref_processor()->process_discovered_references( + soft_ref_policy, is_alive_closure(), &mark_and_push_closure, + &follow_stack_closure, &task_executor); + } else { + ref_processor()->process_discovered_references( + soft_ref_policy, is_alive_closure(), &mark_and_push_closure, + &follow_stack_closure, NULL); + } + } + + TraceTime tm_c("class unloading", print_phases(), true, gclog_or_tty); + // Follow system dictionary roots and unload classes. + bool purged_class = SystemDictionary::do_unloading(is_alive_closure()); + + // Follow code cache roots. + CodeCache::do_unloading(is_alive_closure(), &mark_and_push_closure, + purged_class); + follow_stack(cm); // Flush marking stack. + + // Update subklass/sibling/implementor links of live klasses + // revisit_klass_stack is used in follow_weak_klass_links(). + follow_weak_klass_links(cm); + + // Visit symbol and interned string tables and delete unmarked oops + SymbolTable::unlink(is_alive_closure()); + StringTable::unlink(is_alive_closure()); + + assert(cm->marking_stack()->size() == 0, "stack should be empty by now"); + assert(cm->overflow_stack()->is_empty(), "stack should be empty by now"); +} + +// This should be moved to the shared markSweep code! +class PSAlwaysTrueClosure: public BoolObjectClosure { +public: + void do_object(oop p) { ShouldNotReachHere(); } + bool do_object_b(oop p) { return true; } +}; +static PSAlwaysTrueClosure always_true; + +void PSParallelCompact::adjust_roots() { + // Adjust the pointers to reflect the new locations + EventMark m("3 adjust roots"); + TraceTime tm("adjust roots", print_phases(), true, gclog_or_tty); + + // General strong roots. + Universe::oops_do(adjust_root_pointer_closure()); + ReferenceProcessor::oops_do(adjust_root_pointer_closure()); + JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles + Threads::oops_do(adjust_root_pointer_closure()); + ObjectSynchronizer::oops_do(adjust_root_pointer_closure()); + FlatProfiler::oops_do(adjust_root_pointer_closure()); + Management::oops_do(adjust_root_pointer_closure()); + JvmtiExport::oops_do(adjust_root_pointer_closure()); + // SO_AllClasses + SystemDictionary::oops_do(adjust_root_pointer_closure()); + vmSymbols::oops_do(adjust_root_pointer_closure()); + + // Now adjust pointers in remaining weak roots. (All of which should + // have been cleared if they pointed to non-surviving objects.) + // Global (weak) JNI handles + JNIHandles::weak_oops_do(&always_true, adjust_root_pointer_closure()); + + CodeCache::oops_do(adjust_pointer_closure()); + SymbolTable::oops_do(adjust_root_pointer_closure()); + StringTable::oops_do(adjust_root_pointer_closure()); + ref_processor()->weak_oops_do(adjust_root_pointer_closure()); + // Roots were visited so references into the young gen in roots + // may have been scanned. Process them also. + // Should the reference processor have a span that excludes + // young gen objects? + PSScavenge::reference_processor()->weak_oops_do( + adjust_root_pointer_closure()); +} + +void PSParallelCompact::compact_perm(ParCompactionManager* cm) { + EventMark m("4 compact perm"); + TraceTime tm("compact perm gen", print_phases(), true, gclog_or_tty); + // trace("4"); + + gc_heap()->perm_gen()->start_array()->reset(); + move_and_update(cm, perm_space_id); +} + +void PSParallelCompact::enqueue_chunk_draining_tasks(GCTaskQueue* q, + uint parallel_gc_threads) { + TraceTime tm("drain task setup", print_phases(), true, gclog_or_tty); + + const unsigned int task_count = MAX2(parallel_gc_threads, 1U); + for (unsigned int j = 0; j < task_count; j++) { + q->enqueue(new DrainStacksCompactionTask()); + } + + // Find all chunks that are available (can be filled immediately) and + // distribute them to the thread stacks. The iteration is done in reverse + // order (high to low) so the chunks will be removed in ascending order. + + const ParallelCompactData& sd = PSParallelCompact::summary_data(); + + size_t fillable_chunks = 0; // A count for diagnostic purposes. + unsigned int which = 0; // The worker thread number. + + for (unsigned int id = to_space_id; id > perm_space_id; --id) { + SpaceInfo* const space_info = _space_info + id; + MutableSpace* const space = space_info->space(); + HeapWord* const new_top = space_info->new_top(); + + const size_t beg_chunk = sd.addr_to_chunk_idx(space_info->dense_prefix()); + const size_t end_chunk = sd.addr_to_chunk_idx(sd.chunk_align_up(new_top)); + assert(end_chunk > 0, "perm gen cannot be empty"); + + for (size_t cur = end_chunk - 1; cur >= beg_chunk; --cur) { + if (sd.chunk(cur)->claim_unsafe()) { + ParCompactionManager* cm = ParCompactionManager::manager_array(which); + cm->save_for_processing(cur); + + if (TraceParallelOldGCCompactionPhase && Verbose) { + const size_t count_mod_8 = fillable_chunks & 7; + if (count_mod_8 == 0) gclog_or_tty->print("fillable: "); + gclog_or_tty->print(" " SIZE_FORMAT_W("7"), cur); + if (count_mod_8 == 7) gclog_or_tty->cr(); + } + + NOT_PRODUCT(++fillable_chunks;) + + // Assign chunks to threads in round-robin fashion. + if (++which == task_count) { + which = 0; + } + } + } + } + + if (TraceParallelOldGCCompactionPhase) { + if (Verbose && (fillable_chunks & 7) != 0) gclog_or_tty->cr(); + gclog_or_tty->print_cr("%u initially fillable chunks", fillable_chunks); + } +} + +#define PAR_OLD_DENSE_PREFIX_OVER_PARTITIONING 4 + +void PSParallelCompact::enqueue_dense_prefix_tasks(GCTaskQueue* q, + uint parallel_gc_threads) { + TraceTime tm("dense prefix task setup", print_phases(), true, gclog_or_tty); + + ParallelCompactData& sd = PSParallelCompact::summary_data(); + + // Iterate over all the spaces adding tasks for updating + // chunks in the dense prefix. Assume that 1 gc thread + // will work on opening the gaps and the remaining gc threads + // will work on the dense prefix. + SpaceId space_id = old_space_id; + while (space_id != last_space_id) { + HeapWord* const dense_prefix_end = _space_info[space_id].dense_prefix(); + const MutableSpace* const space = _space_info[space_id].space(); + + if (dense_prefix_end == space->bottom()) { + // There is no dense prefix for this space. + space_id = next_compaction_space_id(space_id); + continue; + } + + // The dense prefix is before this chunk. + size_t chunk_index_end_dense_prefix = + sd.addr_to_chunk_idx(dense_prefix_end); + ChunkData* const dense_prefix_cp = sd.chunk(chunk_index_end_dense_prefix); + assert(dense_prefix_end == space->end() || + dense_prefix_cp->available() || + dense_prefix_cp->claimed(), + "The chunk after the dense prefix should always be ready to fill"); + + size_t chunk_index_start = sd.addr_to_chunk_idx(space->bottom()); + + // Is there dense prefix work? + size_t total_dense_prefix_chunks = + chunk_index_end_dense_prefix - chunk_index_start; + // How many chunks of the dense prefix should be given to + // each thread? + if (total_dense_prefix_chunks > 0) { + uint tasks_for_dense_prefix = 1; + if (UseParallelDensePrefixUpdate) { + if (total_dense_prefix_chunks <= + (parallel_gc_threads * PAR_OLD_DENSE_PREFIX_OVER_PARTITIONING)) { + // Don't over partition. This assumes that + // PAR_OLD_DENSE_PREFIX_OVER_PARTITIONING is a small integer value + // so there are not many chunks to process. + tasks_for_dense_prefix = parallel_gc_threads; + } else { + // Over partition + tasks_for_dense_prefix = parallel_gc_threads * + PAR_OLD_DENSE_PREFIX_OVER_PARTITIONING; + } + } + size_t chunks_per_thread = total_dense_prefix_chunks / + tasks_for_dense_prefix; + // Give each thread at least 1 chunk. + if (chunks_per_thread == 0) { + chunks_per_thread = 1; + } + + for (uint k = 0; k < tasks_for_dense_prefix; k++) { + if (chunk_index_start >= chunk_index_end_dense_prefix) { + break; + } + // chunk_index_end is not processed + size_t chunk_index_end = MIN2(chunk_index_start + chunks_per_thread, + chunk_index_end_dense_prefix); + q->enqueue(new UpdateDensePrefixTask( + space_id, + chunk_index_start, + chunk_index_end)); + chunk_index_start = chunk_index_end; + } + } + // This gets any part of the dense prefix that did not + // fit evenly. + if (chunk_index_start < chunk_index_end_dense_prefix) { + q->enqueue(new UpdateDensePrefixTask( + space_id, + chunk_index_start, + chunk_index_end_dense_prefix)); + } + space_id = next_compaction_space_id(space_id); + } // End tasks for dense prefix +} + +void PSParallelCompact::enqueue_chunk_stealing_tasks( + GCTaskQueue* q, + ParallelTaskTerminator* terminator_ptr, + uint parallel_gc_threads) { + TraceTime tm("steal task setup", print_phases(), true, gclog_or_tty); + + // Once a thread has drained it's stack, it should try to steal chunks from + // other threads. + if (parallel_gc_threads > 1) { + for (uint j = 0; j < parallel_gc_threads; j++) { + q->enqueue(new StealChunkCompactionTask(terminator_ptr)); + } + } +} + +void PSParallelCompact::compact() { + EventMark m("5 compact"); + // trace("5"); + TraceTime tm("compaction phase", print_phases(), true, gclog_or_tty); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + PSOldGen* old_gen = heap->old_gen(); + old_gen->start_array()->reset(); + uint parallel_gc_threads = heap->gc_task_manager()->workers(); + TaskQueueSetSuper* qset = ParCompactionManager::chunk_array(); + ParallelTaskTerminator terminator(parallel_gc_threads, qset); + + GCTaskQueue* q = GCTaskQueue::create(); + enqueue_chunk_draining_tasks(q, parallel_gc_threads); + enqueue_dense_prefix_tasks(q, parallel_gc_threads); + enqueue_chunk_stealing_tasks(q, &terminator, parallel_gc_threads); + + { + TraceTime tm_pc("par compact", print_phases(), true, gclog_or_tty); + + WaitForBarrierGCTask* fin = WaitForBarrierGCTask::create(); + q->enqueue(fin); + + gc_task_manager()->add_list(q); + + fin->wait_for(); + + // We have to release the barrier tasks! + WaitForBarrierGCTask::destroy(fin); + +#ifdef ASSERT + // Verify that all chunks have been processed before the deferred updates. + // Note that perm_space_id is skipped; this type of verification is not + // valid until the perm gen is compacted by chunks. + for (unsigned int id = old_space_id; id < last_space_id; ++id) { + verify_complete(SpaceId(id)); + } +#endif + } + + { + // Update the deferred objects, if any. Any compaction manager can be used. + TraceTime tm_du("deferred updates", print_phases(), true, gclog_or_tty); + ParCompactionManager* cm = ParCompactionManager::manager_array(0); + for (unsigned int id = old_space_id; id < last_space_id; ++id) { + update_deferred_objects(cm, SpaceId(id)); + } + } +} + +#ifdef ASSERT +void PSParallelCompact::verify_complete(SpaceId space_id) { + // All Chunks between space bottom() to new_top() should be marked as filled + // and all Chunks between new_top() and top() should be available (i.e., + // should have been emptied). + ParallelCompactData& sd = summary_data(); + SpaceInfo si = _space_info[space_id]; + HeapWord* new_top_addr = sd.chunk_align_up(si.new_top()); + HeapWord* old_top_addr = sd.chunk_align_up(si.space()->top()); + const size_t beg_chunk = sd.addr_to_chunk_idx(si.space()->bottom()); + const size_t new_top_chunk = sd.addr_to_chunk_idx(new_top_addr); + const size_t old_top_chunk = sd.addr_to_chunk_idx(old_top_addr); + + bool issued_a_warning = false; + + size_t cur_chunk; + for (cur_chunk = beg_chunk; cur_chunk < new_top_chunk; ++cur_chunk) { + const ChunkData* const c = sd.chunk(cur_chunk); + if (!c->completed()) { + warning("chunk " SIZE_FORMAT " not filled: " + "destination_count=" SIZE_FORMAT, + cur_chunk, c->destination_count()); + issued_a_warning = true; + } + } + + for (cur_chunk = new_top_chunk; cur_chunk < old_top_chunk; ++cur_chunk) { + const ChunkData* const c = sd.chunk(cur_chunk); + if (!c->available()) { + warning("chunk " SIZE_FORMAT " not empty: " + "destination_count=" SIZE_FORMAT, + cur_chunk, c->destination_count()); + issued_a_warning = true; + } + } + + if (issued_a_warning) { + print_chunk_ranges(); + } +} +#endif // #ifdef ASSERT + +void PSParallelCompact::compact_serial(ParCompactionManager* cm) { + EventMark m("5 compact serial"); + TraceTime tm("compact serial", print_phases(), true, gclog_or_tty); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + PSYoungGen* young_gen = heap->young_gen(); + PSOldGen* old_gen = heap->old_gen(); + + old_gen->start_array()->reset(); + old_gen->move_and_update(cm); + young_gen->move_and_update(cm); +} + +void PSParallelCompact::follow_root(ParCompactionManager* cm, oop* p) { + assert(!Universe::heap()->is_in_reserved(p), + "roots shouldn't be things within the heap"); +#ifdef VALIDATE_MARK_SWEEP + if (ValidateMarkSweep) { + guarantee(!_root_refs_stack->contains(p), "should only be in here once"); + _root_refs_stack->push(p); + } +#endif + oop m = *p; + if (m != NULL && mark_bitmap()->is_unmarked(m)) { + if (mark_obj(m)) { + m->follow_contents(cm); // Follow contents of the marked object + } + } + follow_stack(cm); +} + +void PSParallelCompact::follow_stack(ParCompactionManager* cm) { + while(!cm->overflow_stack()->is_empty()) { + oop obj = cm->overflow_stack()->pop(); + obj->follow_contents(cm); + } + + oop obj; + // obj is a reference!!! + while (cm->marking_stack()->pop_local(obj)) { + // It would be nice to assert about the type of objects we might + // pop, but they can come from anywhere, unfortunately. + obj->follow_contents(cm); + } +} + +void +PSParallelCompact::follow_weak_klass_links(ParCompactionManager* serial_cm) { + // All klasses on the revisit stack are marked at this point. + // Update and follow all subklass, sibling and implementor links. + for (uint i = 0; i < ParallelGCThreads+1; i++) { + ParCompactionManager* cm = ParCompactionManager::manager_array(i); + KeepAliveClosure keep_alive_closure(cm); + for (int i = 0; i < cm->revisit_klass_stack()->length(); i++) { + cm->revisit_klass_stack()->at(i)->follow_weak_klass_links( + is_alive_closure(), + &keep_alive_closure); + } + follow_stack(cm); + } +} + +void +PSParallelCompact::revisit_weak_klass_link(ParCompactionManager* cm, Klass* k) { + cm->revisit_klass_stack()->push(k); +} + +#ifdef VALIDATE_MARK_SWEEP + +void PSParallelCompact::track_adjusted_pointer(oop* p, oop newobj, bool isroot) { + if (!ValidateMarkSweep) + return; + + if (!isroot) { + if (_pointer_tracking) { + guarantee(_adjusted_pointers->contains(p), "should have seen this pointer"); + _adjusted_pointers->remove(p); + } + } else { + ptrdiff_t index = _root_refs_stack->find(p); + if (index != -1) { + int l = _root_refs_stack->length(); + if (l > 0 && l - 1 != index) { + oop* last = _root_refs_stack->pop(); + assert(last != p, "should be different"); + _root_refs_stack->at_put(index, last); + } else { + _root_refs_stack->remove(p); + } + } + } +} + + +void PSParallelCompact::check_adjust_pointer(oop* p) { + _adjusted_pointers->push(p); +} + + +class AdjusterTracker: public OopClosure { + public: + AdjusterTracker() {}; + void do_oop(oop* o) { PSParallelCompact::check_adjust_pointer(o); } +}; + + +void PSParallelCompact::track_interior_pointers(oop obj) { + if (ValidateMarkSweep) { + _adjusted_pointers->clear(); + _pointer_tracking = true; + + AdjusterTracker checker; + obj->oop_iterate(&checker); + } +} + + +void PSParallelCompact::check_interior_pointers() { + if (ValidateMarkSweep) { + _pointer_tracking = false; + guarantee(_adjusted_pointers->length() == 0, "should have processed the same pointers"); + } +} + + +void PSParallelCompact::reset_live_oop_tracking(bool at_perm) { + if (ValidateMarkSweep) { + guarantee((size_t)_live_oops->length() == _live_oops_index, "should be at end of live oops"); + _live_oops_index = at_perm ? _live_oops_index_at_perm : 0; + } +} + + +void PSParallelCompact::register_live_oop(oop p, size_t size) { + if (ValidateMarkSweep) { + _live_oops->push(p); + _live_oops_size->push(size); + _live_oops_index++; + } +} + +void PSParallelCompact::validate_live_oop(oop p, size_t size) { + if (ValidateMarkSweep) { + oop obj = _live_oops->at((int)_live_oops_index); + guarantee(obj == p, "should be the same object"); + guarantee(_live_oops_size->at((int)_live_oops_index) == size, "should be the same size"); + _live_oops_index++; + } +} + +void PSParallelCompact::live_oop_moved_to(HeapWord* q, size_t size, + HeapWord* compaction_top) { + assert(oop(q)->forwardee() == NULL || oop(q)->forwardee() == oop(compaction_top), + "should be moved to forwarded location"); + if (ValidateMarkSweep) { + PSParallelCompact::validate_live_oop(oop(q), size); + _live_oops_moved_to->push(oop(compaction_top)); + } + if (RecordMarkSweepCompaction) { + _cur_gc_live_oops->push(q); + _cur_gc_live_oops_moved_to->push(compaction_top); + _cur_gc_live_oops_size->push(size); + } +} + + +void PSParallelCompact::compaction_complete() { + if (RecordMarkSweepCompaction) { + GrowableArray* _tmp_live_oops = _cur_gc_live_oops; + GrowableArray* _tmp_live_oops_moved_to = _cur_gc_live_oops_moved_to; + GrowableArray * _tmp_live_oops_size = _cur_gc_live_oops_size; + + _cur_gc_live_oops = _last_gc_live_oops; + _cur_gc_live_oops_moved_to = _last_gc_live_oops_moved_to; + _cur_gc_live_oops_size = _last_gc_live_oops_size; + _last_gc_live_oops = _tmp_live_oops; + _last_gc_live_oops_moved_to = _tmp_live_oops_moved_to; + _last_gc_live_oops_size = _tmp_live_oops_size; + } +} + + +void PSParallelCompact::print_new_location_of_heap_address(HeapWord* q) { + if (!RecordMarkSweepCompaction) { + tty->print_cr("Requires RecordMarkSweepCompaction to be enabled"); + return; + } + + if (_last_gc_live_oops == NULL) { + tty->print_cr("No compaction information gathered yet"); + return; + } + + for (int i = 0; i < _last_gc_live_oops->length(); i++) { + HeapWord* old_oop = _last_gc_live_oops->at(i); + size_t sz = _last_gc_live_oops_size->at(i); + if (old_oop <= q && q < (old_oop + sz)) { + HeapWord* new_oop = _last_gc_live_oops_moved_to->at(i); + size_t offset = (q - old_oop); + tty->print_cr("Address " PTR_FORMAT, q); + tty->print_cr(" Was in oop " PTR_FORMAT ", size %d, at offset %d", old_oop, sz, offset); + tty->print_cr(" Now in oop " PTR_FORMAT ", actual address " PTR_FORMAT, new_oop, new_oop + offset); + return; + } + } + + tty->print_cr("Address " PTR_FORMAT " not found in live oop information from last GC", q); +} +#endif //VALIDATE_MARK_SWEEP + +void PSParallelCompact::adjust_pointer(oop* p, bool isroot) { + oop obj = *p; + VALIDATE_MARK_SWEEP_ONLY(oop saved_new_pointer = NULL); + if (obj != NULL) { + oop new_pointer = (oop) summary_data().calc_new_pointer(obj); + assert(new_pointer != NULL || // is forwarding ptr? + obj->is_shared(), // never forwarded? + "should have a new location"); + // Just always do the update unconditionally? + if (new_pointer != NULL) { + *p = new_pointer; + assert(Universe::heap()->is_in_reserved(new_pointer), + "should be in object space"); + VALIDATE_MARK_SWEEP_ONLY(saved_new_pointer = new_pointer); + } + } + VALIDATE_MARK_SWEEP_ONLY(track_adjusted_pointer(p, saved_new_pointer, isroot)); +} + +// Update interior oops in the ranges of chunks [beg_chunk, end_chunk). +void +PSParallelCompact::update_and_deadwood_in_dense_prefix(ParCompactionManager* cm, + SpaceId space_id, + size_t beg_chunk, + size_t end_chunk) { + ParallelCompactData& sd = summary_data(); + ParMarkBitMap* const mbm = mark_bitmap(); + + HeapWord* beg_addr = sd.chunk_to_addr(beg_chunk); + HeapWord* const end_addr = sd.chunk_to_addr(end_chunk); + assert(beg_chunk <= end_chunk, "bad chunk range"); + assert(end_addr <= dense_prefix(space_id), "not in the dense prefix"); + +#ifdef ASSERT + // Claim the chunks to avoid triggering an assert when they are marked as + // filled. + for (size_t claim_chunk = beg_chunk; claim_chunk < end_chunk; ++claim_chunk) { + assert(sd.chunk(claim_chunk)->claim_unsafe(), "claim() failed"); + } +#endif // #ifdef ASSERT + + if (beg_addr != space(space_id)->bottom()) { + // Find the first live object or block of dead space that *starts* in this + // range of chunks. If a partial object crosses onto the chunk, skip it; it + // will be marked for 'deferred update' when the object head is processed. + // If dead space crosses onto the chunk, it is also skipped; it will be + // filled when the prior chunk is processed. If neither of those apply, the + // first word in the chunk is the start of a live object or dead space. + assert(beg_addr > space(space_id)->bottom(), "sanity"); + const ChunkData* const cp = sd.chunk(beg_chunk); + if (cp->partial_obj_size() != 0) { + beg_addr = sd.partial_obj_end(beg_chunk); + } else if (dead_space_crosses_boundary(cp, mbm->addr_to_bit(beg_addr))) { + beg_addr = mbm->find_obj_beg(beg_addr, end_addr); + } + } + + if (beg_addr < end_addr) { + // A live object or block of dead space starts in this range of Chunks. + HeapWord* const dense_prefix_end = dense_prefix(space_id); + + // Create closures and iterate. + UpdateOnlyClosure update_closure(mbm, cm, space_id); + FillClosure fill_closure(cm, space_id); + ParMarkBitMap::IterationStatus status; + status = mbm->iterate(&update_closure, &fill_closure, beg_addr, end_addr, + dense_prefix_end); + if (status == ParMarkBitMap::incomplete) { + update_closure.do_addr(update_closure.source()); + } + } + + // Mark the chunks as filled. + ChunkData* const beg_cp = sd.chunk(beg_chunk); + ChunkData* const end_cp = sd.chunk(end_chunk); + for (ChunkData* cp = beg_cp; cp < end_cp; ++cp) { + cp->set_completed(); + } +} + +// Return the SpaceId for the space containing addr. If addr is not in the +// heap, last_space_id is returned. In debug mode it expects the address to be +// in the heap and asserts such. +PSParallelCompact::SpaceId PSParallelCompact::space_id(HeapWord* addr) { + assert(Universe::heap()->is_in_reserved(addr), "addr not in the heap"); + + for (unsigned int id = perm_space_id; id < last_space_id; ++id) { + if (_space_info[id].space()->contains(addr)) { + return SpaceId(id); + } + } + + assert(false, "no space contains the addr"); + return last_space_id; +} + +void PSParallelCompact::update_deferred_objects(ParCompactionManager* cm, + SpaceId id) { + assert(id < last_space_id, "bad space id"); + + ParallelCompactData& sd = summary_data(); + const SpaceInfo* const space_info = _space_info + id; + ObjectStartArray* const start_array = space_info->start_array(); + + const MutableSpace* const space = space_info->space(); + assert(space_info->dense_prefix() >= space->bottom(), "dense_prefix not set"); + HeapWord* const beg_addr = space_info->dense_prefix(); + HeapWord* const end_addr = sd.chunk_align_up(space_info->new_top()); + + const ChunkData* const beg_chunk = sd.addr_to_chunk_ptr(beg_addr); + const ChunkData* const end_chunk = sd.addr_to_chunk_ptr(end_addr); + const ChunkData* cur_chunk; + for (cur_chunk = beg_chunk; cur_chunk < end_chunk; ++cur_chunk) { + HeapWord* const addr = cur_chunk->deferred_obj_addr(); + if (addr != NULL) { + if (start_array != NULL) { + start_array->allocate_block(addr); + } + oop(addr)->update_contents(cm); + assert(oop(addr)->is_oop_or_null(), "should be an oop now"); + } + } +} + +// Skip over count live words starting from beg, and return the address of the +// next live word. Unless marked, the word corresponding to beg is assumed to +// be dead. Callers must either ensure beg does not correspond to the middle of +// an object, or account for those live words in some other way. Callers must +// also ensure that there are enough live words in the range [beg, end) to skip. +HeapWord* +PSParallelCompact::skip_live_words(HeapWord* beg, HeapWord* end, size_t count) +{ + assert(count > 0, "sanity"); + + ParMarkBitMap* m = mark_bitmap(); + idx_t bits_to_skip = m->words_to_bits(count); + idx_t cur_beg = m->addr_to_bit(beg); + const idx_t search_end = BitMap::word_align_up(m->addr_to_bit(end)); + + do { + cur_beg = m->find_obj_beg(cur_beg, search_end); + idx_t cur_end = m->find_obj_end(cur_beg, search_end); + const size_t obj_bits = cur_end - cur_beg + 1; + if (obj_bits > bits_to_skip) { + return m->bit_to_addr(cur_beg + bits_to_skip); + } + bits_to_skip -= obj_bits; + cur_beg = cur_end + 1; + } while (bits_to_skip > 0); + + // Skipping the desired number of words landed just past the end of an object. + // Find the start of the next object. + cur_beg = m->find_obj_beg(cur_beg, search_end); + assert(cur_beg < m->addr_to_bit(end), "not enough live words to skip"); + return m->bit_to_addr(cur_beg); +} + +HeapWord* +PSParallelCompact::first_src_addr(HeapWord* const dest_addr, + size_t src_chunk_idx) +{ + ParMarkBitMap* const bitmap = mark_bitmap(); + const ParallelCompactData& sd = summary_data(); + const size_t ChunkSize = ParallelCompactData::ChunkSize; + + assert(sd.is_chunk_aligned(dest_addr), "not aligned"); + + const ChunkData* const src_chunk_ptr = sd.chunk(src_chunk_idx); + const size_t partial_obj_size = src_chunk_ptr->partial_obj_size(); + HeapWord* const src_chunk_destination = src_chunk_ptr->destination(); + + assert(dest_addr >= src_chunk_destination, "wrong src chunk"); + assert(src_chunk_ptr->data_size() > 0, "src chunk cannot be empty"); + + HeapWord* const src_chunk_beg = sd.chunk_to_addr(src_chunk_idx); + HeapWord* const src_chunk_end = src_chunk_beg + ChunkSize; + + HeapWord* addr = src_chunk_beg; + if (dest_addr == src_chunk_destination) { + // Return the first live word in the source chunk. + if (partial_obj_size == 0) { + addr = bitmap->find_obj_beg(addr, src_chunk_end); + assert(addr < src_chunk_end, "no objects start in src chunk"); + } + return addr; + } + + // Must skip some live data. + size_t words_to_skip = dest_addr - src_chunk_destination; + assert(src_chunk_ptr->data_size() > words_to_skip, "wrong src chunk"); + + if (partial_obj_size >= words_to_skip) { + // All the live words to skip are part of the partial object. + addr += words_to_skip; + if (partial_obj_size == words_to_skip) { + // Find the first live word past the partial object. + addr = bitmap->find_obj_beg(addr, src_chunk_end); + assert(addr < src_chunk_end, "wrong src chunk"); + } + return addr; + } + + // Skip over the partial object (if any). + if (partial_obj_size != 0) { + words_to_skip -= partial_obj_size; + addr += partial_obj_size; + } + + // Skip over live words due to objects that start in the chunk. + addr = skip_live_words(addr, src_chunk_end, words_to_skip); + assert(addr < src_chunk_end, "wrong src chunk"); + return addr; +} + +void PSParallelCompact::decrement_destination_counts(ParCompactionManager* cm, + size_t beg_chunk, + HeapWord* end_addr) +{ + ParallelCompactData& sd = summary_data(); + ChunkData* const beg = sd.chunk(beg_chunk); + HeapWord* const end_addr_aligned_up = sd.chunk_align_up(end_addr); + ChunkData* const end = sd.addr_to_chunk_ptr(end_addr_aligned_up); + size_t cur_idx = beg_chunk; + for (ChunkData* cur = beg; cur < end; ++cur, ++cur_idx) { + assert(cur->data_size() > 0, "chunk must have live data"); + cur->decrement_destination_count(); + if (cur_idx <= cur->source_chunk() && cur->available() && cur->claim()) { + cm->save_for_processing(cur_idx); + } + } +} + +size_t PSParallelCompact::next_src_chunk(MoveAndUpdateClosure& closure, + SpaceId& src_space_id, + HeapWord*& src_space_top, + HeapWord* end_addr) +{ + typedef ParallelCompactData::ChunkData ChunkData; + + ParallelCompactData& sd = PSParallelCompact::summary_data(); + const size_t chunk_size = ParallelCompactData::ChunkSize; + + size_t src_chunk_idx = 0; + + // Skip empty chunks (if any) up to the top of the space. + HeapWord* const src_aligned_up = sd.chunk_align_up(end_addr); + ChunkData* src_chunk_ptr = sd.addr_to_chunk_ptr(src_aligned_up); + HeapWord* const top_aligned_up = sd.chunk_align_up(src_space_top); + const ChunkData* const top_chunk_ptr = sd.addr_to_chunk_ptr(top_aligned_up); + while (src_chunk_ptr < top_chunk_ptr && src_chunk_ptr->data_size() == 0) { + ++src_chunk_ptr; + } + + if (src_chunk_ptr < top_chunk_ptr) { + // The next source chunk is in the current space. Update src_chunk_idx and + // the source address to match src_chunk_ptr. + src_chunk_idx = sd.chunk(src_chunk_ptr); + HeapWord* const src_chunk_addr = sd.chunk_to_addr(src_chunk_idx); + if (src_chunk_addr > closure.source()) { + closure.set_source(src_chunk_addr); + } + return src_chunk_idx; + } + + // Switch to a new source space and find the first non-empty chunk. + unsigned int space_id = src_space_id + 1; + assert(space_id < last_space_id, "not enough spaces"); + + HeapWord* const destination = closure.destination(); + + do { + MutableSpace* space = _space_info[space_id].space(); + HeapWord* const bottom = space->bottom(); + const ChunkData* const bottom_cp = sd.addr_to_chunk_ptr(bottom); + + // Iterate over the spaces that do not compact into themselves. + if (bottom_cp->destination() != bottom) { + HeapWord* const top_aligned_up = sd.chunk_align_up(space->top()); + const ChunkData* const top_cp = sd.addr_to_chunk_ptr(top_aligned_up); + + for (const ChunkData* src_cp = bottom_cp; src_cp < top_cp; ++src_cp) { + if (src_cp->live_obj_size() > 0) { + // Found it. + assert(src_cp->destination() == destination, + "first live obj in the space must match the destination"); + assert(src_cp->partial_obj_size() == 0, + "a space cannot begin with a partial obj"); + + src_space_id = SpaceId(space_id); + src_space_top = space->top(); + const size_t src_chunk_idx = sd.chunk(src_cp); + closure.set_source(sd.chunk_to_addr(src_chunk_idx)); + return src_chunk_idx; + } else { + assert(src_cp->data_size() == 0, "sanity"); + } + } + } + } while (++space_id < last_space_id); + + assert(false, "no source chunk was found"); + return 0; +} + +void PSParallelCompact::fill_chunk(ParCompactionManager* cm, size_t chunk_idx) +{ + typedef ParMarkBitMap::IterationStatus IterationStatus; + const size_t ChunkSize = ParallelCompactData::ChunkSize; + ParMarkBitMap* const bitmap = mark_bitmap(); + ParallelCompactData& sd = summary_data(); + ChunkData* const chunk_ptr = sd.chunk(chunk_idx); + + // Get the items needed to construct the closure. + HeapWord* dest_addr = sd.chunk_to_addr(chunk_idx); + SpaceId dest_space_id = space_id(dest_addr); + ObjectStartArray* start_array = _space_info[dest_space_id].start_array(); + HeapWord* new_top = _space_info[dest_space_id].new_top(); + assert(dest_addr < new_top, "sanity"); + const size_t words = MIN2(pointer_delta(new_top, dest_addr), ChunkSize); + + // Get the source chunk and related info. + size_t src_chunk_idx = chunk_ptr->source_chunk(); + SpaceId src_space_id = space_id(sd.chunk_to_addr(src_chunk_idx)); + HeapWord* src_space_top = _space_info[src_space_id].space()->top(); + + MoveAndUpdateClosure closure(bitmap, cm, start_array, dest_addr, words); + closure.set_source(first_src_addr(dest_addr, src_chunk_idx)); + + // Adjust src_chunk_idx to prepare for decrementing destination counts (the + // destination count is not decremented when a chunk is copied to itself). + if (src_chunk_idx == chunk_idx) { + src_chunk_idx += 1; + } + + if (bitmap->is_unmarked(closure.source())) { + // The first source word is in the middle of an object; copy the remainder + // of the object or as much as will fit. The fact that pointer updates were + // deferred will be noted when the object header is processed. + HeapWord* const old_src_addr = closure.source(); + closure.copy_partial_obj(); + if (closure.is_full()) { + decrement_destination_counts(cm, src_chunk_idx, closure.source()); + chunk_ptr->set_deferred_obj_addr(NULL); + chunk_ptr->set_completed(); + return; + } + + HeapWord* const end_addr = sd.chunk_align_down(closure.source()); + if (sd.chunk_align_down(old_src_addr) != end_addr) { + // The partial object was copied from more than one source chunk. + decrement_destination_counts(cm, src_chunk_idx, end_addr); + + // Move to the next source chunk, possibly switching spaces as well. All + // args except end_addr may be modified. + src_chunk_idx = next_src_chunk(closure, src_space_id, src_space_top, + end_addr); + } + } + + do { + HeapWord* const cur_addr = closure.source(); + HeapWord* const end_addr = MIN2(sd.chunk_align_up(cur_addr + 1), + src_space_top); + IterationStatus status = bitmap->iterate(&closure, cur_addr, end_addr); + + if (status == ParMarkBitMap::incomplete) { + // The last obj that starts in the source chunk does not end in the chunk. + assert(closure.source() < end_addr, "sanity") + HeapWord* const obj_beg = closure.source(); + HeapWord* const range_end = MIN2(obj_beg + closure.words_remaining(), + src_space_top); + HeapWord* const obj_end = bitmap->find_obj_end(obj_beg, range_end); + if (obj_end < range_end) { + // The end was found; the entire object will fit. + status = closure.do_addr(obj_beg, bitmap->obj_size(obj_beg, obj_end)); + assert(status != ParMarkBitMap::would_overflow, "sanity"); + } else { + // The end was not found; the object will not fit. + assert(range_end < src_space_top, "obj cannot cross space boundary"); + status = ParMarkBitMap::would_overflow; + } + } + + if (status == ParMarkBitMap::would_overflow) { + // The last object did not fit. Note that interior oop updates were + // deferred, then copy enough of the object to fill the chunk. + chunk_ptr->set_deferred_obj_addr(closure.destination()); + status = closure.copy_until_full(); // copies from closure.source() + + decrement_destination_counts(cm, src_chunk_idx, closure.source()); + chunk_ptr->set_completed(); + return; + } + + if (status == ParMarkBitMap::full) { + decrement_destination_counts(cm, src_chunk_idx, closure.source()); + chunk_ptr->set_deferred_obj_addr(NULL); + chunk_ptr->set_completed(); + return; + } + + decrement_destination_counts(cm, src_chunk_idx, end_addr); + + // Move to the next source chunk, possibly switching spaces as well. All + // args except end_addr may be modified. + src_chunk_idx = next_src_chunk(closure, src_space_id, src_space_top, + end_addr); + } while (true); +} + +void +PSParallelCompact::move_and_update(ParCompactionManager* cm, SpaceId space_id) { + const MutableSpace* sp = space(space_id); + if (sp->is_empty()) { + return; + } + + ParallelCompactData& sd = PSParallelCompact::summary_data(); + ParMarkBitMap* const bitmap = mark_bitmap(); + HeapWord* const dp_addr = dense_prefix(space_id); + HeapWord* beg_addr = sp->bottom(); + HeapWord* end_addr = sp->top(); + +#ifdef ASSERT + assert(beg_addr <= dp_addr && dp_addr <= end_addr, "bad dense prefix"); + if (cm->should_verify_only()) { + VerifyUpdateClosure verify_update(cm, sp); + bitmap->iterate(&verify_update, beg_addr, end_addr); + return; + } + + if (cm->should_reset_only()) { + ResetObjectsClosure reset_objects(cm); + bitmap->iterate(&reset_objects, beg_addr, end_addr); + return; + } +#endif + + const size_t beg_chunk = sd.addr_to_chunk_idx(beg_addr); + const size_t dp_chunk = sd.addr_to_chunk_idx(dp_addr); + if (beg_chunk < dp_chunk) { + update_and_deadwood_in_dense_prefix(cm, space_id, beg_chunk, dp_chunk); + } + + // The destination of the first live object that starts in the chunk is one + // past the end of the partial object entering the chunk (if any). + HeapWord* const dest_addr = sd.partial_obj_end(dp_chunk); + HeapWord* const new_top = _space_info[space_id].new_top(); + assert(new_top >= dest_addr, "bad new_top value"); + const size_t words = pointer_delta(new_top, dest_addr); + + if (words > 0) { + ObjectStartArray* start_array = _space_info[space_id].start_array(); + MoveAndUpdateClosure closure(bitmap, cm, start_array, dest_addr, words); + + ParMarkBitMap::IterationStatus status; + status = bitmap->iterate(&closure, dest_addr, end_addr); + assert(status == ParMarkBitMap::full, "iteration not complete"); + assert(bitmap->find_obj_beg(closure.source(), end_addr) == end_addr, + "live objects skipped because closure is full"); + } +} + +jlong PSParallelCompact::millis_since_last_gc() { + jlong ret_val = os::javaTimeMillis() - _time_of_last_gc; + // XXX See note in genCollectedHeap::millis_since_last_gc(). + if (ret_val < 0) { + NOT_PRODUCT(warning("time warp: %d", ret_val);) + return 0; + } + return ret_val; +} + +void PSParallelCompact::reset_millis_since_last_gc() { + _time_of_last_gc = os::javaTimeMillis(); +} + +ParMarkBitMap::IterationStatus MoveAndUpdateClosure::copy_until_full() +{ + if (source() != destination()) { + assert(source() > destination(), "must copy to the left"); + Copy::aligned_conjoint_words(source(), destination(), words_remaining()); + } + update_state(words_remaining()); + assert(is_full(), "sanity"); + return ParMarkBitMap::full; +} + +void MoveAndUpdateClosure::copy_partial_obj() +{ + size_t words = words_remaining(); + + HeapWord* const range_end = MIN2(source() + words, bitmap()->region_end()); + HeapWord* const end_addr = bitmap()->find_obj_end(source(), range_end); + if (end_addr < range_end) { + words = bitmap()->obj_size(source(), end_addr); + } + + // This test is necessary; if omitted, the pointer updates to a partial object + // that crosses the dense prefix boundary could be overwritten. + if (source() != destination()) { + assert(source() > destination(), "must copy to the left"); + Copy::aligned_conjoint_words(source(), destination(), words); + } + update_state(words); +} + +ParMarkBitMapClosure::IterationStatus +MoveAndUpdateClosure::do_addr(HeapWord* addr, size_t words) { + assert(destination() != NULL, "sanity"); + assert(bitmap()->obj_size(addr) == words, "bad size"); + + _source = addr; + assert(PSParallelCompact::summary_data().calc_new_pointer(source()) == + destination(), "wrong destination"); + + if (words > words_remaining()) { + return ParMarkBitMap::would_overflow; + } + + // The start_array must be updated even if the object is not moving. + if (_start_array != NULL) { + _start_array->allocate_block(destination()); + } + + if (destination() != source()) { + assert(destination() < source(), "must copy to the left"); + Copy::aligned_conjoint_words(source(), destination(), words); + } + + oop moved_oop = (oop) destination(); + moved_oop->update_contents(compaction_manager()); + assert(moved_oop->is_oop_or_null(), "Object should be whole at this point"); + + update_state(words); + assert(destination() == (HeapWord*)moved_oop + moved_oop->size(), "sanity"); + return is_full() ? ParMarkBitMap::full : ParMarkBitMap::incomplete; +} + +UpdateOnlyClosure::UpdateOnlyClosure(ParMarkBitMap* mbm, + ParCompactionManager* cm, + PSParallelCompact::SpaceId space_id) : + ParMarkBitMapClosure(mbm, cm), + _space_id(space_id), + _start_array(PSParallelCompact::start_array(space_id)) +{ +} + +// Updates the references in the object to their new values. +ParMarkBitMapClosure::IterationStatus +UpdateOnlyClosure::do_addr(HeapWord* addr, size_t words) { + do_addr(addr); + return ParMarkBitMap::incomplete; +} + +BitBlockUpdateClosure::BitBlockUpdateClosure(ParMarkBitMap* mbm, + ParCompactionManager* cm, + size_t chunk_index) : + ParMarkBitMapClosure(mbm, cm), + _live_data_left(0), + _cur_block(0) { + _chunk_start = + PSParallelCompact::summary_data().chunk_to_addr(chunk_index); + _chunk_end = + PSParallelCompact::summary_data().chunk_to_addr(chunk_index) + + ParallelCompactData::ChunkSize; + _chunk_index = chunk_index; + _cur_block = + PSParallelCompact::summary_data().addr_to_block_idx(_chunk_start); +} + +bool BitBlockUpdateClosure::chunk_contains_cur_block() { + return ParallelCompactData::chunk_contains_block(_chunk_index, _cur_block); +} + +void BitBlockUpdateClosure::reset_chunk(size_t chunk_index) { + DEBUG_ONLY(ParallelCompactData::BlockData::set_cur_phase(7);) + ParallelCompactData& sd = PSParallelCompact::summary_data(); + _chunk_index = chunk_index; + _live_data_left = 0; + _chunk_start = sd.chunk_to_addr(chunk_index); + _chunk_end = sd.chunk_to_addr(chunk_index) + ParallelCompactData::ChunkSize; + + // The first block in this chunk + size_t first_block = sd.addr_to_block_idx(_chunk_start); + size_t partial_live_size = sd.chunk(chunk_index)->partial_obj_size(); + + // Set the offset to 0. By definition it should have that value + // but it may have been written while processing an earlier chunk. + if (partial_live_size == 0) { + // No live object extends onto the chunk. The first bit + // in the bit map for the first chunk must be a start bit. + // Although there may not be any marked bits, it is safe + // to set it as a start bit. + sd.block(first_block)->set_start_bit_offset(0); + sd.block(first_block)->set_first_is_start_bit(true); + } else if (sd.partial_obj_ends_in_block(first_block)) { + sd.block(first_block)->set_end_bit_offset(0); + sd.block(first_block)->set_first_is_start_bit(false); + } else { + // The partial object extends beyond the first block. + // There is no object starting in the first block + // so the offset and bit parity are not needed. + // Set the the bit parity to start bit so assertions + // work when not bit is found. + sd.block(first_block)->set_end_bit_offset(0); + sd.block(first_block)->set_first_is_start_bit(false); + } + _cur_block = first_block; +#ifdef ASSERT + if (sd.block(first_block)->first_is_start_bit()) { + assert(!sd.partial_obj_ends_in_block(first_block), + "Partial object cannot end in first block"); + } + + if (PrintGCDetails && Verbose) { + if (partial_live_size == 1) { + gclog_or_tty->print_cr("first_block " PTR_FORMAT + " _offset " PTR_FORMAT + " _first_is_start_bit %d", + first_block, + sd.block(first_block)->raw_offset(), + sd.block(first_block)->first_is_start_bit()); + } + } +#endif + DEBUG_ONLY(ParallelCompactData::BlockData::set_cur_phase(17);) +} + +// This method is called when a object has been found (both beginning +// and end of the object) in the range of iteration. This method is +// calculating the words of live data to the left of a block. That live +// data includes any object starting to the left of the block (i.e., +// the live-data-to-the-left of block AAA will include the full size +// of any object entering AAA). + +ParMarkBitMapClosure::IterationStatus +BitBlockUpdateClosure::do_addr(HeapWord* addr, size_t words) { + // add the size to the block data. + HeapWord* obj = addr; + ParallelCompactData& sd = PSParallelCompact::summary_data(); + + assert(bitmap()->obj_size(obj) == words, "bad size"); + assert(_chunk_start <= obj, "object is not in chunk"); + assert(obj + words <= _chunk_end, "object is not in chunk"); + + // Update the live data to the left + size_t prev_live_data_left = _live_data_left; + _live_data_left = _live_data_left + words; + + // Is this object in the current block. + size_t block_of_obj = sd.addr_to_block_idx(obj); + size_t block_of_obj_last = sd.addr_to_block_idx(obj + words - 1); + HeapWord* block_of_obj_last_addr = sd.block_to_addr(block_of_obj_last); + if (_cur_block < block_of_obj) { + + // + // No object crossed the block boundary and this object was found + // on the other side of the block boundary. Update the offset for + // the new block with the data size that does not include this object. + // + // The first bit in block_of_obj is a start bit except in the + // case where the partial object for the chunk extends into + // this block. + if (sd.partial_obj_ends_in_block(block_of_obj)) { + sd.block(block_of_obj)->set_end_bit_offset(prev_live_data_left); + } else { + sd.block(block_of_obj)->set_start_bit_offset(prev_live_data_left); + } + + // Does this object pass beyond the its block? + if (block_of_obj < block_of_obj_last) { + // Object crosses block boundary. Two blocks need to be udpated: + // the current block where the object started + // the block where the object ends + // + // The offset for blocks with no objects starting in them + // (e.g., blocks between _cur_block and block_of_obj_last) + // should not be needed. + // Note that block_of_obj_last may be in another chunk. If so, + // it should be overwritten later. This is a problem (writting + // into a block in a later chunk) for parallel execution. + assert(obj < block_of_obj_last_addr, + "Object should start in previous block"); + + // obj is crossing into block_of_obj_last so the first bit + // is and end bit. + sd.block(block_of_obj_last)->set_end_bit_offset(_live_data_left); + + _cur_block = block_of_obj_last; + } else { + // _first_is_start_bit has already been set correctly + // in the if-then-else above so don't reset it here. + _cur_block = block_of_obj; + } + } else { + // The current block only changes if the object extends beyound + // the block it starts in. + // + // The object starts in the current block. + // Does this object pass beyond the end of it? + if (block_of_obj < block_of_obj_last) { + // Object crosses block boundary. + // See note above on possible blocks between block_of_obj and + // block_of_obj_last + assert(obj < block_of_obj_last_addr, + "Object should start in previous block"); + + sd.block(block_of_obj_last)->set_end_bit_offset(_live_data_left); + + _cur_block = block_of_obj_last; + } + } + + // Return incomplete if there are more blocks to be done. + if (chunk_contains_cur_block()) { + return ParMarkBitMap::incomplete; + } + return ParMarkBitMap::complete; +} + +// Verify the new location using the forwarding pointer +// from MarkSweep::mark_sweep_phase2(). Set the mark_word +// to the initial value. +ParMarkBitMapClosure::IterationStatus +PSParallelCompact::VerifyUpdateClosure::do_addr(HeapWord* addr, size_t words) { + // The second arg (words) is not used. + oop obj = (oop) addr; + HeapWord* forwarding_ptr = (HeapWord*) obj->mark()->decode_pointer(); + HeapWord* new_pointer = summary_data().calc_new_pointer(obj); + if (forwarding_ptr == NULL) { + // The object is dead or not moving. + assert(bitmap()->is_unmarked(obj) || (new_pointer == (HeapWord*) obj), + "Object liveness is wrong."); + return ParMarkBitMap::incomplete; + } + assert(UseParallelOldGCDensePrefix || + (HeapMaximumCompactionInterval > 1) || + (MarkSweepAlwaysCompactCount > 1) || + (forwarding_ptr == new_pointer), + "Calculation of new location is incorrect"); + return ParMarkBitMap::incomplete; +} + +// Reset objects modified for debug checking. +ParMarkBitMapClosure::IterationStatus +PSParallelCompact::ResetObjectsClosure::do_addr(HeapWord* addr, size_t words) { + // The second arg (words) is not used. + oop obj = (oop) addr; + obj->init_mark(); + return ParMarkBitMap::incomplete; +} + +// Prepare for compaction. This method is executed once +// (i.e., by a single thread) before compaction. +// Save the updated location of the intArrayKlassObj for +// filling holes in the dense prefix. +void PSParallelCompact::compact_prologue() { + _updated_int_array_klass_obj = (klassOop) + summary_data().calc_new_pointer(Universe::intArrayKlassObj()); +} + +// The initial implementation of this method created a field +// _next_compaction_space_id in SpaceInfo and initialized +// that field in SpaceInfo::initialize_space_info(). That +// required that _next_compaction_space_id be declared a +// SpaceId in SpaceInfo and that would have required that +// either SpaceId be declared in a separate class or that +// it be declared in SpaceInfo. It didn't seem consistent +// to declare it in SpaceInfo (didn't really fit logically). +// Alternatively, defining a separate class to define SpaceId +// seem excessive. This implementation is simple and localizes +// the knowledge. + +PSParallelCompact::SpaceId +PSParallelCompact::next_compaction_space_id(SpaceId id) { + assert(id < last_space_id, "id out of range"); + switch (id) { + case perm_space_id : + return last_space_id; + case old_space_id : + return eden_space_id; + case eden_space_id : + return from_space_id; + case from_space_id : + return to_space_id; + case to_space_id : + return last_space_id; + default: + assert(false, "Bad space id"); + return last_space_id; + } +} + +// Here temporarily for debugging +#ifdef ASSERT + size_t ParallelCompactData::block_idx(BlockData* block) { + size_t index = pointer_delta(block, + PSParallelCompact::summary_data()._block_data, sizeof(BlockData)); + return index; + } +#endif diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp new file mode 100644 index 00000000000..f38ff2b985d --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp @@ -0,0 +1,1368 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ParallelScavengeHeap; +class PSAdaptiveSizePolicy; +class PSYoungGen; +class PSOldGen; +class PSPermGen; +class ParCompactionManager; +class ParallelTaskTerminator; +class PSParallelCompact; +class GCTaskManager; +class GCTaskQueue; +class PreGCValues; +class MoveAndUpdateClosure; +class RefProcTaskExecutor; + +class SpaceInfo +{ + public: + MutableSpace* space() const { return _space; } + + // Where the free space will start after the collection. Valid only after the + // summary phase completes. + HeapWord* new_top() const { return _new_top; } + + // Allows new_top to be set. + HeapWord** new_top_addr() { return &_new_top; } + + // Where the smallest allowable dense prefix ends (used only for perm gen). + HeapWord* min_dense_prefix() const { return _min_dense_prefix; } + + // Where the dense prefix ends, or the compacted region begins. + HeapWord* dense_prefix() const { return _dense_prefix; } + + // The start array for the (generation containing the) space, or NULL if there + // is no start array. + ObjectStartArray* start_array() const { return _start_array; } + + void set_space(MutableSpace* s) { _space = s; } + void set_new_top(HeapWord* addr) { _new_top = addr; } + void set_min_dense_prefix(HeapWord* addr) { _min_dense_prefix = addr; } + void set_dense_prefix(HeapWord* addr) { _dense_prefix = addr; } + void set_start_array(ObjectStartArray* s) { _start_array = s; } + + private: + MutableSpace* _space; + HeapWord* _new_top; + HeapWord* _min_dense_prefix; + HeapWord* _dense_prefix; + ObjectStartArray* _start_array; +}; + +class ParallelCompactData +{ +public: + // Sizes are in HeapWords, unless indicated otherwise. + static const size_t Log2ChunkSize; + static const size_t ChunkSize; + static const size_t ChunkSizeBytes; + + // Mask for the bits in a size_t to get an offset within a chunk. + static const size_t ChunkSizeOffsetMask; + // Mask for the bits in a pointer to get an offset within a chunk. + static const size_t ChunkAddrOffsetMask; + // Mask for the bits in a pointer to get the address of the start of a chunk. + static const size_t ChunkAddrMask; + + static const size_t Log2BlockSize; + static const size_t BlockSize; + static const size_t BlockOffsetMask; + static const size_t BlockMask; + + static const size_t BlocksPerChunk; + + class ChunkData + { + public: + // Destination address of the chunk. + HeapWord* destination() const { return _destination; } + + // The first chunk containing data destined for this chunk. + size_t source_chunk() const { return _source_chunk; } + + // The object (if any) starting in this chunk and ending in a different + // chunk that could not be updated during the main (parallel) compaction + // phase. This is different from _partial_obj_addr, which is an object that + // extends onto a source chunk. However, the two uses do not overlap in + // time, so the same field is used to save space. + HeapWord* deferred_obj_addr() const { return _partial_obj_addr; } + + // The starting address of the partial object extending onto the chunk. + HeapWord* partial_obj_addr() const { return _partial_obj_addr; } + + // Size of the partial object extending onto the chunk (words). + size_t partial_obj_size() const { return _partial_obj_size; } + + // Size of live data that lies within this chunk due to objects that start + // in this chunk (words). This does not include the partial object + // extending onto the chunk (if any), or the part of an object that extends + // onto the next chunk (if any). + size_t live_obj_size() const { return _dc_and_los & los_mask; } + + // Total live data that lies within the chunk (words). + size_t data_size() const { return partial_obj_size() + live_obj_size(); } + + // The destination_count is the number of other chunks to which data from + // this chunk will be copied. At the end of the summary phase, the valid + // values of destination_count are + // + // 0 - data from the chunk will be compacted completely into itself, or the + // chunk is empty. The chunk can be claimed and then filled. + // 1 - data from the chunk will be compacted into 1 other chunk; some + // data from the chunk may also be compacted into the chunk itself. + // 2 - data from the chunk will be copied to 2 other chunks. + // + // During compaction as chunks are emptied, the destination_count is + // decremented (atomically) and when it reaches 0, it can be claimed and + // then filled. + // + // A chunk is claimed for processing by atomically changing the + // destination_count to the claimed value (dc_claimed). After a chunk has + // been filled, the destination_count should be set to the completed value + // (dc_completed). + inline uint destination_count() const; + inline uint destination_count_raw() const; + + // The location of the java heap data that corresponds to this chunk. + inline HeapWord* data_location() const; + + // The highest address referenced by objects in this chunk. + inline HeapWord* highest_ref() const; + + // Whether this chunk is available to be claimed, has been claimed, or has + // been completed. + // + // Minor subtlety: claimed() returns true if the chunk is marked + // completed(), which is desirable since a chunk must be claimed before it + // can be completed. + bool available() const { return _dc_and_los < dc_one; } + bool claimed() const { return _dc_and_los >= dc_claimed; } + bool completed() const { return _dc_and_los >= dc_completed; } + + // These are not atomic. + void set_destination(HeapWord* addr) { _destination = addr; } + void set_source_chunk(size_t chunk) { _source_chunk = chunk; } + void set_deferred_obj_addr(HeapWord* addr) { _partial_obj_addr = addr; } + void set_partial_obj_addr(HeapWord* addr) { _partial_obj_addr = addr; } + void set_partial_obj_size(size_t words) { + _partial_obj_size = (chunk_sz_t) words; + } + + inline void set_destination_count(uint count); + inline void set_live_obj_size(size_t words); + inline void set_data_location(HeapWord* addr); + inline void set_completed(); + inline bool claim_unsafe(); + + // These are atomic. + inline void add_live_obj(size_t words); + inline void set_highest_ref(HeapWord* addr); + inline void decrement_destination_count(); + inline bool claim(); + + private: + // The type used to represent object sizes within a chunk. + typedef uint chunk_sz_t; + + // Constants for manipulating the _dc_and_los field, which holds both the + // destination count and live obj size. The live obj size lives at the + // least significant end so no masking is necessary when adding. + static const chunk_sz_t dc_shift; // Shift amount. + static const chunk_sz_t dc_mask; // Mask for destination count. + static const chunk_sz_t dc_one; // 1, shifted appropriately. + static const chunk_sz_t dc_claimed; // Chunk has been claimed. + static const chunk_sz_t dc_completed; // Chunk has been completed. + static const chunk_sz_t los_mask; // Mask for live obj size. + + HeapWord* _destination; + size_t _source_chunk; + HeapWord* _partial_obj_addr; + chunk_sz_t _partial_obj_size; + chunk_sz_t volatile _dc_and_los; +#ifdef ASSERT + // These enable optimizations that are only partially implemented. Use + // debug builds to prevent the code fragments from breaking. + HeapWord* _data_location; + HeapWord* _highest_ref; +#endif // #ifdef ASSERT + +#ifdef ASSERT + public: + uint _pushed; // 0 until chunk is pushed onto a worker's stack + private: +#endif + }; + + // 'Blocks' allow shorter sections of the bitmap to be searched. Each Block + // holds an offset, which is the amount of live data in the Chunk to the left + // of the first live object in the Block. This amount of live data will + // include any object extending into the block. The first block in + // a chunk does not include any partial object extending into the + // the chunk. + // + // The offset also encodes the + // 'parity' of the first 1 bit in the Block: a positive offset means the + // first 1 bit marks the start of an object, a negative offset means the first + // 1 bit marks the end of an object. + class BlockData + { + public: + typedef short int blk_ofs_t; + + blk_ofs_t offset() const { return _offset >= 0 ? _offset : -_offset; } + blk_ofs_t raw_offset() const { return _offset; } + void set_first_is_start_bit(bool v) { _first_is_start_bit = v; } + +#if 0 + // The need for this method was anticipated but it is + // never actually used. Do not include it for now. If + // it is needed, consider the problem of what is passed + // as "v". To avoid warning errors the method set_start_bit_offset() + // was changed to take a size_t as the parameter and to do the + // check for the possible overflow. Doing the cast in these + // methods better limits the potential problems because of + // the size of the field to this class. + void set_raw_offset(blk_ofs_t v) { _offset = v; } +#endif + void set_start_bit_offset(size_t val) { + assert(val >= 0, "sanity"); + _offset = (blk_ofs_t) val; + assert(val == (size_t) _offset, "Value is too large"); + _first_is_start_bit = true; + } + void set_end_bit_offset(size_t val) { + assert(val >= 0, "sanity"); + _offset = (blk_ofs_t) val; + assert(val == (size_t) _offset, "Value is too large"); + _offset = - _offset; + _first_is_start_bit = false; + } + bool first_is_start_bit() { + assert(_set_phase > 0, "Not initialized"); + return _first_is_start_bit; + } + bool first_is_end_bit() { + assert(_set_phase > 0, "Not initialized"); + return !_first_is_start_bit; + } + + private: + blk_ofs_t _offset; + // This is temporary until the mark_bitmap is separated into + // a start bit array and an end bit array. + bool _first_is_start_bit; +#ifdef ASSERT + short _set_phase; + static short _cur_phase; + public: + static void set_cur_phase(short v) { _cur_phase = v; } +#endif + }; + +public: + ParallelCompactData(); + bool initialize(MemRegion covered_region); + + size_t chunk_count() const { return _chunk_count; } + + // Convert chunk indices to/from ChunkData pointers. + inline ChunkData* chunk(size_t chunk_idx) const; + inline size_t chunk(const ChunkData* const chunk_ptr) const; + + // Returns true if the given address is contained within the chunk + bool chunk_contains(size_t chunk_index, HeapWord* addr); + + size_t block_count() const { return _block_count; } + inline BlockData* block(size_t n) const; + + // Returns true if the given block is in the given chunk. + static bool chunk_contains_block(size_t chunk_index, size_t block_index); + + void add_obj(HeapWord* addr, size_t len); + void add_obj(oop p, size_t len) { add_obj((HeapWord*)p, len); } + + // Fill in the chunks covering [beg, end) so that no data moves; i.e., the + // destination of chunk n is simply the start of chunk n. The argument beg + // must be chunk-aligned; end need not be. + void summarize_dense_prefix(HeapWord* beg, HeapWord* end); + + bool summarize(HeapWord* target_beg, HeapWord* target_end, + HeapWord* source_beg, HeapWord* source_end, + HeapWord** target_next, HeapWord** source_next = 0); + + void clear(); + void clear_range(size_t beg_chunk, size_t end_chunk); + void clear_range(HeapWord* beg, HeapWord* end) { + clear_range(addr_to_chunk_idx(beg), addr_to_chunk_idx(end)); + } + + // Return the number of words between addr and the start of the chunk + // containing addr. + inline size_t chunk_offset(const HeapWord* addr) const; + + // Convert addresses to/from a chunk index or chunk pointer. + inline size_t addr_to_chunk_idx(const HeapWord* addr) const; + inline ChunkData* addr_to_chunk_ptr(const HeapWord* addr) const; + inline HeapWord* chunk_to_addr(size_t chunk) const; + inline HeapWord* chunk_to_addr(size_t chunk, size_t offset) const; + inline HeapWord* chunk_to_addr(const ChunkData* chunk) const; + + inline HeapWord* chunk_align_down(HeapWord* addr) const; + inline HeapWord* chunk_align_up(HeapWord* addr) const; + inline bool is_chunk_aligned(HeapWord* addr) const; + + // Analogous to chunk_offset() for blocks. + size_t block_offset(const HeapWord* addr) const; + size_t addr_to_block_idx(const HeapWord* addr) const; + size_t addr_to_block_idx(const oop obj) const { + return addr_to_block_idx((HeapWord*) obj); + } + inline BlockData* addr_to_block_ptr(const HeapWord* addr) const; + inline HeapWord* block_to_addr(size_t block) const; + + // Return the address one past the end of the partial object. + HeapWord* partial_obj_end(size_t chunk_idx) const; + + // Return the new location of the object p after the + // the compaction. + HeapWord* calc_new_pointer(HeapWord* addr); + + // Same as calc_new_pointer() using blocks. + HeapWord* block_calc_new_pointer(HeapWord* addr); + + // Same as calc_new_pointer() using chunks. + HeapWord* chunk_calc_new_pointer(HeapWord* addr); + + HeapWord* calc_new_pointer(oop p) { + return calc_new_pointer((HeapWord*) p); + } + + // Return the updated address for the given klass + klassOop calc_new_klass(klassOop); + + // Given a block returns true if the partial object for the + // corresponding chunk ends in the block. Returns false, otherwise + // If there is no partial object, returns false. + bool partial_obj_ends_in_block(size_t block_index); + + // Returns the block index for the block + static size_t block_idx(BlockData* block); + +#ifdef ASSERT + void verify_clear(const PSVirtualSpace* vspace); + void verify_clear(); +#endif // #ifdef ASSERT + +private: + bool initialize_block_data(size_t region_size); + bool initialize_chunk_data(size_t region_size); + PSVirtualSpace* create_vspace(size_t count, size_t element_size); + +private: + HeapWord* _region_start; +#ifdef ASSERT + HeapWord* _region_end; +#endif // #ifdef ASSERT + + PSVirtualSpace* _chunk_vspace; + ChunkData* _chunk_data; + size_t _chunk_count; + + PSVirtualSpace* _block_vspace; + BlockData* _block_data; + size_t _block_count; +}; + +inline uint +ParallelCompactData::ChunkData::destination_count_raw() const +{ + return _dc_and_los & dc_mask; +} + +inline uint +ParallelCompactData::ChunkData::destination_count() const +{ + return destination_count_raw() >> dc_shift; +} + +inline void +ParallelCompactData::ChunkData::set_destination_count(uint count) +{ + assert(count <= (dc_completed >> dc_shift), "count too large"); + const chunk_sz_t live_sz = (chunk_sz_t) live_obj_size(); + _dc_and_los = (count << dc_shift) | live_sz; +} + +inline void ParallelCompactData::ChunkData::set_live_obj_size(size_t words) +{ + assert(words <= los_mask, "would overflow"); + _dc_and_los = destination_count_raw() | (chunk_sz_t)words; +} + +inline void ParallelCompactData::ChunkData::decrement_destination_count() +{ + assert(_dc_and_los < dc_claimed, "already claimed"); + assert(_dc_and_los >= dc_one, "count would go negative"); + Atomic::add((int)dc_mask, (volatile int*)&_dc_and_los); +} + +inline HeapWord* ParallelCompactData::ChunkData::data_location() const +{ + DEBUG_ONLY(return _data_location;) + NOT_DEBUG(return NULL;) +} + +inline HeapWord* ParallelCompactData::ChunkData::highest_ref() const +{ + DEBUG_ONLY(return _highest_ref;) + NOT_DEBUG(return NULL;) +} + +inline void ParallelCompactData::ChunkData::set_data_location(HeapWord* addr) +{ + DEBUG_ONLY(_data_location = addr;) +} + +inline void ParallelCompactData::ChunkData::set_completed() +{ + assert(claimed(), "must be claimed first"); + _dc_and_los = dc_completed | (chunk_sz_t) live_obj_size(); +} + +// MT-unsafe claiming of a chunk. Should only be used during single threaded +// execution. +inline bool ParallelCompactData::ChunkData::claim_unsafe() +{ + if (available()) { + _dc_and_los |= dc_claimed; + return true; + } + return false; +} + +inline void ParallelCompactData::ChunkData::add_live_obj(size_t words) +{ + assert(words <= (size_t)los_mask - live_obj_size(), "overflow"); + Atomic::add((int) words, (volatile int*) &_dc_and_los); +} + +inline void ParallelCompactData::ChunkData::set_highest_ref(HeapWord* addr) +{ +#ifdef ASSERT + HeapWord* tmp = _highest_ref; + while (addr > tmp) { + tmp = (HeapWord*)Atomic::cmpxchg_ptr(addr, &_highest_ref, tmp); + } +#endif // #ifdef ASSERT +} + +inline bool ParallelCompactData::ChunkData::claim() +{ + const int los = (int) live_obj_size(); + const int old = Atomic::cmpxchg(dc_claimed | los, + (volatile int*) &_dc_and_los, los); + return old == los; +} + +inline ParallelCompactData::ChunkData* +ParallelCompactData::chunk(size_t chunk_idx) const +{ + assert(chunk_idx <= chunk_count(), "bad arg"); + return _chunk_data + chunk_idx; +} + +inline size_t +ParallelCompactData::chunk(const ChunkData* const chunk_ptr) const +{ + assert(chunk_ptr >= _chunk_data, "bad arg"); + assert(chunk_ptr <= _chunk_data + chunk_count(), "bad arg"); + return pointer_delta(chunk_ptr, _chunk_data, sizeof(ChunkData)); +} + +inline ParallelCompactData::BlockData* +ParallelCompactData::block(size_t n) const { + assert(n < block_count(), "bad arg"); + return _block_data + n; +} + +inline size_t +ParallelCompactData::chunk_offset(const HeapWord* addr) const +{ + assert(addr >= _region_start, "bad addr"); + assert(addr <= _region_end, "bad addr"); + return (size_t(addr) & ChunkAddrOffsetMask) >> LogHeapWordSize; +} + +inline size_t +ParallelCompactData::addr_to_chunk_idx(const HeapWord* addr) const +{ + assert(addr >= _region_start, "bad addr"); + assert(addr <= _region_end, "bad addr"); + return pointer_delta(addr, _region_start) >> Log2ChunkSize; +} + +inline ParallelCompactData::ChunkData* +ParallelCompactData::addr_to_chunk_ptr(const HeapWord* addr) const +{ + return chunk(addr_to_chunk_idx(addr)); +} + +inline HeapWord* +ParallelCompactData::chunk_to_addr(size_t chunk) const +{ + assert(chunk <= _chunk_count, "chunk out of range"); + return _region_start + (chunk << Log2ChunkSize); +} + +inline HeapWord* +ParallelCompactData::chunk_to_addr(const ChunkData* chunk) const +{ + return chunk_to_addr(pointer_delta(chunk, _chunk_data, sizeof(ChunkData))); +} + +inline HeapWord* +ParallelCompactData::chunk_to_addr(size_t chunk, size_t offset) const +{ + assert(chunk <= _chunk_count, "chunk out of range"); + assert(offset < ChunkSize, "offset too big"); // This may be too strict. + return chunk_to_addr(chunk) + offset; +} + +inline HeapWord* +ParallelCompactData::chunk_align_down(HeapWord* addr) const +{ + assert(addr >= _region_start, "bad addr"); + assert(addr < _region_end + ChunkSize, "bad addr"); + return (HeapWord*)(size_t(addr) & ChunkAddrMask); +} + +inline HeapWord* +ParallelCompactData::chunk_align_up(HeapWord* addr) const +{ + assert(addr >= _region_start, "bad addr"); + assert(addr <= _region_end, "bad addr"); + return chunk_align_down(addr + ChunkSizeOffsetMask); +} + +inline bool +ParallelCompactData::is_chunk_aligned(HeapWord* addr) const +{ + return chunk_offset(addr) == 0; +} + +inline size_t +ParallelCompactData::block_offset(const HeapWord* addr) const +{ + assert(addr >= _region_start, "bad addr"); + assert(addr <= _region_end, "bad addr"); + return pointer_delta(addr, _region_start) & BlockOffsetMask; +} + +inline size_t +ParallelCompactData::addr_to_block_idx(const HeapWord* addr) const +{ + assert(addr >= _region_start, "bad addr"); + assert(addr <= _region_end, "bad addr"); + return pointer_delta(addr, _region_start) >> Log2BlockSize; +} + +inline ParallelCompactData::BlockData* +ParallelCompactData::addr_to_block_ptr(const HeapWord* addr) const +{ + return block(addr_to_block_idx(addr)); +} + +inline HeapWord* +ParallelCompactData::block_to_addr(size_t block) const +{ + assert(block < _block_count, "block out of range"); + return _region_start + (block << Log2BlockSize); +} + +// Abstract closure for use with ParMarkBitMap::iterate(), which will invoke the +// do_addr() method. +// +// The closure is initialized with the number of heap words to process +// (words_remaining()), and becomes 'full' when it reaches 0. The do_addr() +// methods in subclasses should update the total as words are processed. Since +// only one subclass actually uses this mechanism to terminate iteration, the +// default initial value is > 0. The implementation is here and not in the +// single subclass that uses it to avoid making is_full() virtual, and thus +// adding a virtual call per live object. + +class ParMarkBitMapClosure: public StackObj { + public: + typedef ParMarkBitMap::idx_t idx_t; + typedef ParMarkBitMap::IterationStatus IterationStatus; + + public: + inline ParMarkBitMapClosure(ParMarkBitMap* mbm, ParCompactionManager* cm, + size_t words = max_uintx); + + inline ParCompactionManager* compaction_manager() const; + inline ParMarkBitMap* bitmap() const; + inline size_t words_remaining() const; + inline bool is_full() const; + inline HeapWord* source() const; + + inline void set_source(HeapWord* addr); + + virtual IterationStatus do_addr(HeapWord* addr, size_t words) = 0; + + protected: + inline void decrement_words_remaining(size_t words); + + private: + ParMarkBitMap* const _bitmap; + ParCompactionManager* const _compaction_manager; + DEBUG_ONLY(const size_t _initial_words_remaining;) // Useful in debugger. + size_t _words_remaining; // Words left to copy. + + protected: + HeapWord* _source; // Next addr that would be read. +}; + +inline +ParMarkBitMapClosure::ParMarkBitMapClosure(ParMarkBitMap* bitmap, + ParCompactionManager* cm, + size_t words): + _bitmap(bitmap), _compaction_manager(cm) +#ifdef ASSERT + , _initial_words_remaining(words) +#endif +{ + _words_remaining = words; + _source = NULL; +} + +inline ParCompactionManager* ParMarkBitMapClosure::compaction_manager() const { + return _compaction_manager; +} + +inline ParMarkBitMap* ParMarkBitMapClosure::bitmap() const { + return _bitmap; +} + +inline size_t ParMarkBitMapClosure::words_remaining() const { + return _words_remaining; +} + +inline bool ParMarkBitMapClosure::is_full() const { + return words_remaining() == 0; +} + +inline HeapWord* ParMarkBitMapClosure::source() const { + return _source; +} + +inline void ParMarkBitMapClosure::set_source(HeapWord* addr) { + _source = addr; +} + +inline void ParMarkBitMapClosure::decrement_words_remaining(size_t words) { + assert(_words_remaining >= words, "processed too many words"); + _words_remaining -= words; +} + +// Closure for updating the block data during the summary phase. +class BitBlockUpdateClosure: public ParMarkBitMapClosure { + // ParallelCompactData::BlockData::blk_ofs_t _live_data_left; + size_t _live_data_left; + size_t _cur_block; + HeapWord* _chunk_start; + HeapWord* _chunk_end; + size_t _chunk_index; + + public: + BitBlockUpdateClosure(ParMarkBitMap* mbm, + ParCompactionManager* cm, + size_t chunk_index); + + size_t cur_block() { return _cur_block; } + size_t chunk_index() { return _chunk_index; } + size_t live_data_left() { return _live_data_left; } + // Returns true the first bit in the current block (cur_block) is + // a start bit. + // Returns true if the current block is within the chunk for the closure; + bool chunk_contains_cur_block(); + + // Set the chunk index and related chunk values for + // a new chunk. + void reset_chunk(size_t chunk_index); + + virtual IterationStatus do_addr(HeapWord* addr, size_t words); +}; + +class PSParallelCompact : AllStatic { + public: + // Convenient access to type names. + typedef ParMarkBitMap::idx_t idx_t; + typedef ParallelCompactData::ChunkData ChunkData; + typedef ParallelCompactData::BlockData BlockData; + + typedef enum { + perm_space_id, old_space_id, eden_space_id, + from_space_id, to_space_id, last_space_id + } SpaceId; + + public: + // In line closure decls + // + + class IsAliveClosure: public BoolObjectClosure { + public: + void do_object(oop p) { assert(false, "don't call"); } + bool do_object_b(oop p) { return mark_bitmap()->is_marked(p); } + }; + + class KeepAliveClosure: public OopClosure { + ParCompactionManager* _compaction_manager; + public: + KeepAliveClosure(ParCompactionManager* cm) { + _compaction_manager = cm; + } + void do_oop(oop* p); + }; + + class FollowRootClosure: public OopsInGenClosure{ + ParCompactionManager* _compaction_manager; + public: + FollowRootClosure(ParCompactionManager* cm) { + _compaction_manager = cm; + } + void do_oop(oop* p) { follow_root(_compaction_manager, p); } + virtual const bool do_nmethods() const { return true; } + }; + + class FollowStackClosure: public VoidClosure { + ParCompactionManager* _compaction_manager; + public: + FollowStackClosure(ParCompactionManager* cm) { + _compaction_manager = cm; + } + void do_void() { follow_stack(_compaction_manager); } + }; + + class AdjustPointerClosure: public OopsInGenClosure { + bool _is_root; + public: + AdjustPointerClosure(bool is_root) : _is_root(is_root) {} + void do_oop(oop* p) { adjust_pointer(p, _is_root); } + }; + + // Closure for verifying update of pointers. Does not + // have any side effects. + class VerifyUpdateClosure: public ParMarkBitMapClosure { + const MutableSpace* _space; // Is this ever used? + + public: + VerifyUpdateClosure(ParCompactionManager* cm, const MutableSpace* sp) : + ParMarkBitMapClosure(PSParallelCompact::mark_bitmap(), cm), _space(sp) + { } + + virtual IterationStatus do_addr(HeapWord* addr, size_t words); + + const MutableSpace* space() { return _space; } + }; + + // Closure for updating objects altered for debug checking + class ResetObjectsClosure: public ParMarkBitMapClosure { + public: + ResetObjectsClosure(ParCompactionManager* cm): + ParMarkBitMapClosure(PSParallelCompact::mark_bitmap(), cm) + { } + + virtual IterationStatus do_addr(HeapWord* addr, size_t words); + }; + + friend class KeepAliveClosure; + friend class FollowStackClosure; + friend class AdjustPointerClosure; + friend class FollowRootClosure; + friend class instanceKlassKlass; + friend class RefProcTaskProxy; + + static void mark_and_push_internal(ParCompactionManager* cm, oop* p); + + private: + static elapsedTimer _accumulated_time; + static unsigned int _total_invocations; + static unsigned int _maximum_compaction_gc_num; + static jlong _time_of_last_gc; // ms + static CollectorCounters* _counters; + static ParMarkBitMap _mark_bitmap; + static ParallelCompactData _summary_data; + static IsAliveClosure _is_alive_closure; + static SpaceInfo _space_info[last_space_id]; + static bool _print_phases; + static AdjustPointerClosure _adjust_root_pointer_closure; + static AdjustPointerClosure _adjust_pointer_closure; + + // Reference processing (used in ...follow_contents) + static ReferenceProcessor* _ref_processor; + + // Updated location of intArrayKlassObj. + static klassOop _updated_int_array_klass_obj; + + // Values computed at initialization and used by dead_wood_limiter(). + static double _dwl_mean; + static double _dwl_std_dev; + static double _dwl_first_term; + static double _dwl_adjustment; +#ifdef ASSERT + static bool _dwl_initialized; +#endif // #ifdef ASSERT + + private: + // Closure accessors + static OopClosure* adjust_pointer_closure() { return (OopClosure*)&_adjust_pointer_closure; } + static OopClosure* adjust_root_pointer_closure() { return (OopClosure*)&_adjust_root_pointer_closure; } + static BoolObjectClosure* is_alive_closure() { return (BoolObjectClosure*)&_is_alive_closure; } + + static void initialize_space_info(); + + // Return true if details about individual phases should be printed. + static inline bool print_phases(); + + // Clear the marking bitmap and summary data that cover the specified space. + static void clear_data_covering_space(SpaceId id); + + static void pre_compact(PreGCValues* pre_gc_values); + static void post_compact(); + + // Mark live objects + static void marking_phase(ParCompactionManager* cm, + bool maximum_heap_compaction); + static void follow_stack(ParCompactionManager* cm); + static void follow_weak_klass_links(ParCompactionManager* cm); + + static void adjust_pointer(oop* p, bool is_root); + static void adjust_root_pointer(oop* p) { adjust_pointer(p, true); } + + static void follow_root(ParCompactionManager* cm, oop* p); + + // Compute the dense prefix for the designated space. This is an experimental + // implementation currently not used in production. + static HeapWord* compute_dense_prefix_via_density(const SpaceId id, + bool maximum_compaction); + + // Methods used to compute the dense prefix. + + // Compute the value of the normal distribution at x = density. The mean and + // standard deviation are values saved by initialize_dead_wood_limiter(). + static inline double normal_distribution(double density); + + // Initialize the static vars used by dead_wood_limiter(). + static void initialize_dead_wood_limiter(); + + // Return the percentage of space that can be treated as "dead wood" (i.e., + // not reclaimed). + static double dead_wood_limiter(double density, size_t min_percent); + + // Find the first (left-most) chunk in the range [beg, end) that has at least + // dead_words of dead space to the left. The argument beg must be the first + // chunk in the space that is not completely live. + static ChunkData* dead_wood_limit_chunk(const ChunkData* beg, + const ChunkData* end, + size_t dead_words); + + // Return a pointer to the first chunk in the range [beg, end) that is not + // completely full. + static ChunkData* first_dead_space_chunk(const ChunkData* beg, + const ChunkData* end); + + // Return a value indicating the benefit or 'yield' if the compacted region + // were to start (or equivalently if the dense prefix were to end) at the + // candidate chunk. Higher values are better. + // + // The value is based on the amount of space reclaimed vs. the costs of (a) + // updating references in the dense prefix plus (b) copying objects and + // updating references in the compacted region. + static inline double reclaimed_ratio(const ChunkData* const candidate, + HeapWord* const bottom, + HeapWord* const top, + HeapWord* const new_top); + + // Compute the dense prefix for the designated space. + static HeapWord* compute_dense_prefix(const SpaceId id, + bool maximum_compaction); + + // Return true if dead space crosses onto the specified Chunk; bit must be the + // bit index corresponding to the first word of the Chunk. + static inline bool dead_space_crosses_boundary(const ChunkData* chunk, + idx_t bit); + + // Summary phase utility routine to fill dead space (if any) at the dense + // prefix boundary. Should only be called if the the dense prefix is + // non-empty. + static void fill_dense_prefix_end(SpaceId id); + + static void summarize_spaces_quick(); + static void summarize_space(SpaceId id, bool maximum_compaction); + static void summary_phase(ParCompactionManager* cm, bool maximum_compaction); + + static bool block_first_offset(size_t block_index, idx_t* block_offset_ptr); + + // Fill in the BlockData + static void summarize_blocks(ParCompactionManager* cm, + SpaceId first_compaction_space_id); + + // The space that is compacted after space_id. + static SpaceId next_compaction_space_id(SpaceId space_id); + + // Adjust addresses in roots. Does not adjust addresses in heap. + static void adjust_roots(); + + // Serial code executed in preparation for the compaction phase. + static void compact_prologue(); + + // Move objects to new locations. + static void compact_perm(ParCompactionManager* cm); + static void compact(); + + // Add available chunks to the stack and draining tasks to the task queue. + static void enqueue_chunk_draining_tasks(GCTaskQueue* q, + uint parallel_gc_threads); + + // Add dense prefix update tasks to the task queue. + static void enqueue_dense_prefix_tasks(GCTaskQueue* q, + uint parallel_gc_threads); + + // Add chunk stealing tasks to the task queue. + static void enqueue_chunk_stealing_tasks( + GCTaskQueue* q, + ParallelTaskTerminator* terminator_ptr, + uint parallel_gc_threads); + + // For debugging only - compacts the old gen serially + static void compact_serial(ParCompactionManager* cm); + + // If objects are left in eden after a collection, try to move the boundary + // and absorb them into the old gen. Returns true if eden was emptied. + static bool absorb_live_data_from_eden(PSAdaptiveSizePolicy* size_policy, + PSYoungGen* young_gen, + PSOldGen* old_gen); + + // Reset time since last full gc + static void reset_millis_since_last_gc(); + + protected: +#ifdef VALIDATE_MARK_SWEEP + static GrowableArray* _root_refs_stack; + static GrowableArray * _live_oops; + static GrowableArray * _live_oops_moved_to; + static GrowableArray* _live_oops_size; + static size_t _live_oops_index; + static size_t _live_oops_index_at_perm; + static GrowableArray* _other_refs_stack; + static GrowableArray* _adjusted_pointers; + static bool _pointer_tracking; + static bool _root_tracking; + + // The following arrays are saved since the time of the last GC and + // assist in tracking down problems where someone has done an errant + // store into the heap, usually to an oop that wasn't properly + // handleized across a GC. If we crash or otherwise fail before the + // next GC, we can query these arrays to find out the object we had + // intended to do the store to (assuming it is still alive) and the + // offset within that object. Covered under RecordMarkSweepCompaction. + static GrowableArray * _cur_gc_live_oops; + static GrowableArray * _cur_gc_live_oops_moved_to; + static GrowableArray* _cur_gc_live_oops_size; + static GrowableArray * _last_gc_live_oops; + static GrowableArray * _last_gc_live_oops_moved_to; + static GrowableArray* _last_gc_live_oops_size; +#endif + + public: + class MarkAndPushClosure: public OopClosure { + ParCompactionManager* _compaction_manager; + public: + MarkAndPushClosure(ParCompactionManager* cm) { + _compaction_manager = cm; + } + void do_oop(oop* p) { mark_and_push(_compaction_manager, p); } + virtual const bool do_nmethods() const { return true; } + }; + + PSParallelCompact(); + + // Convenient accessor for Universe::heap(). + static ParallelScavengeHeap* gc_heap() { + return (ParallelScavengeHeap*)Universe::heap(); + } + + static void invoke(bool maximum_heap_compaction); + static void invoke_no_policy(bool maximum_heap_compaction); + + static void post_initialize(); + // Perform initialization for PSParallelCompact that requires + // allocations. This should be called during the VM initialization + // at a pointer where it would be appropriate to return a JNI_ENOMEM + // in the event of a failure. + static bool initialize(); + + // Public accessors + static elapsedTimer* accumulated_time() { return &_accumulated_time; } + static unsigned int total_invocations() { return _total_invocations; } + static CollectorCounters* counters() { return _counters; } + + // Used to add tasks + static GCTaskManager* const gc_task_manager(); + static klassOop updated_int_array_klass_obj() { + return _updated_int_array_klass_obj; + } + + // Marking support + static inline bool mark_obj(oop obj); + static bool mark_obj(oop* p) { + if (*p != NULL) { + return mark_obj(*p); + } else { + return false; + } + } + static void mark_and_push(ParCompactionManager* cm, oop* p) { + // Check mark and maybe push on + // marking stack + oop m = *p; + if (m != NULL && mark_bitmap()->is_unmarked(m)) { + mark_and_push_internal(cm, p); + } + } + + // Compaction support. + // Return true if p is in the range [beg_addr, end_addr). + static inline bool is_in(HeapWord* p, HeapWord* beg_addr, HeapWord* end_addr); + static inline bool is_in(oop* p, HeapWord* beg_addr, HeapWord* end_addr); + + // Convenience wrappers for per-space data kept in _space_info. + static inline MutableSpace* space(SpaceId space_id); + static inline HeapWord* new_top(SpaceId space_id); + static inline HeapWord* dense_prefix(SpaceId space_id); + static inline ObjectStartArray* start_array(SpaceId space_id); + + // Return true if the klass should be updated. + static inline bool should_update_klass(klassOop k); + + // Move and update the live objects in the specified space. + static void move_and_update(ParCompactionManager* cm, SpaceId space_id); + + // Process the end of the given chunk range in the dense prefix. + // This includes saving any object not updated. + static void dense_prefix_chunks_epilogue(ParCompactionManager* cm, + size_t chunk_start_index, + size_t chunk_end_index, + idx_t exiting_object_offset, + idx_t chunk_offset_start, + idx_t chunk_offset_end); + + // Update a chunk in the dense prefix. For each live object + // in the chunk, update it's interior references. For each + // dead object, fill it with deadwood. Dead space at the end + // of a chunk range will be filled to the start of the next + // live object regardless of the chunk_index_end. None of the + // objects in the dense prefix move and dead space is dead + // (holds only dead objects that don't need any processing), so + // dead space can be filled in any order. + static void update_and_deadwood_in_dense_prefix(ParCompactionManager* cm, + SpaceId space_id, + size_t chunk_index_start, + size_t chunk_index_end); + + // Return the address of the count + 1st live word in the range [beg, end). + static HeapWord* skip_live_words(HeapWord* beg, HeapWord* end, size_t count); + + // Return the address of the word to be copied to dest_addr, which must be + // aligned to a chunk boundary. + static HeapWord* first_src_addr(HeapWord* const dest_addr, + size_t src_chunk_idx); + + // Determine the next source chunk, set closure.source() to the start of the + // new chunk return the chunk index. Parameter end_addr is the address one + // beyond the end of source range just processed. If necessary, switch to a + // new source space and set src_space_id (in-out parameter) and src_space_top + // (out parameter) accordingly. + static size_t next_src_chunk(MoveAndUpdateClosure& closure, + SpaceId& src_space_id, + HeapWord*& src_space_top, + HeapWord* end_addr); + + // Decrement the destination count for each non-empty source chunk in the + // range [beg_chunk, chunk(chunk_align_up(end_addr))). + static void decrement_destination_counts(ParCompactionManager* cm, + size_t beg_chunk, + HeapWord* end_addr); + + // Fill a chunk, copying objects from one or more source chunks. + static void fill_chunk(ParCompactionManager* cm, size_t chunk_idx); + static void fill_and_update_chunk(ParCompactionManager* cm, size_t chunk) { + fill_chunk(cm, chunk); + } + + // Update the deferred objects in the space. + static void update_deferred_objects(ParCompactionManager* cm, SpaceId id); + + // Mark pointer and follow contents. + static void mark_and_follow(ParCompactionManager* cm, oop* p); + + static ParMarkBitMap* mark_bitmap() { return &_mark_bitmap; } + static ParallelCompactData& summary_data() { return _summary_data; } + + static inline void adjust_pointer(oop* p) { adjust_pointer(p, false); } + static inline void adjust_pointer(oop* p, + HeapWord* beg_addr, + HeapWord* end_addr); + + // Reference Processing + static ReferenceProcessor* const ref_processor() { return _ref_processor; } + + // Return the SpaceId for the given address. + static SpaceId space_id(HeapWord* addr); + + // Time since last full gc (in milliseconds). + static jlong millis_since_last_gc(); + +#ifdef VALIDATE_MARK_SWEEP + static void track_adjusted_pointer(oop* p, oop newobj, bool isroot); + static void check_adjust_pointer(oop* p); // Adjust this pointer + static void track_interior_pointers(oop obj); + static void check_interior_pointers(); + + static void reset_live_oop_tracking(bool at_perm); + static void register_live_oop(oop p, size_t size); + static void validate_live_oop(oop p, size_t size); + static void live_oop_moved_to(HeapWord* q, size_t size, HeapWord* compaction_top); + static void compaction_complete(); + + // Querying operation of RecordMarkSweepCompaction results. + // Finds and prints the current base oop and offset for a word + // within an oop that was live during the last GC. Helpful for + // tracking down heap stomps. + static void print_new_location_of_heap_address(HeapWord* q); +#endif // #ifdef VALIDATE_MARK_SWEEP + + // Call backs for class unloading + // Update subklass/sibling/implementor links at end of marking. + static void revisit_weak_klass_link(ParCompactionManager* cm, Klass* k); + +#ifndef PRODUCT + // Debugging support. + static const char* space_names[last_space_id]; + static void print_chunk_ranges(); + static void print_dense_prefix_stats(const char* const algorithm, + const SpaceId id, + const bool maximum_compaction, + HeapWord* const addr); +#endif // #ifndef PRODUCT + +#ifdef ASSERT + // Verify that all the chunks have been emptied. + static void verify_complete(SpaceId space_id); +#endif // #ifdef ASSERT +}; + +bool PSParallelCompact::mark_obj(oop obj) { + const int obj_size = obj->size(); + if (mark_bitmap()->mark_obj(obj, obj_size)) { + _summary_data.add_obj(obj, obj_size); + return true; + } else { + return false; + } +} + +inline bool PSParallelCompact::print_phases() +{ + return _print_phases; +} + +inline double PSParallelCompact::normal_distribution(double density) +{ + assert(_dwl_initialized, "uninitialized"); + const double squared_term = (density - _dwl_mean) / _dwl_std_dev; + return _dwl_first_term * exp(-0.5 * squared_term * squared_term); +} + +inline bool +PSParallelCompact::dead_space_crosses_boundary(const ChunkData* chunk, + idx_t bit) +{ + assert(bit > 0, "cannot call this for the first bit/chunk"); + assert(_summary_data.chunk_to_addr(chunk) == _mark_bitmap.bit_to_addr(bit), + "sanity check"); + + // Dead space crosses the boundary if (1) a partial object does not extend + // onto the chunk, (2) an object does not start at the beginning of the chunk, + // and (3) an object does not end at the end of the prior chunk. + return chunk->partial_obj_size() == 0 && + !_mark_bitmap.is_obj_beg(bit) && + !_mark_bitmap.is_obj_end(bit - 1); +} + +inline bool +PSParallelCompact::is_in(HeapWord* p, HeapWord* beg_addr, HeapWord* end_addr) { + return p >= beg_addr && p < end_addr; +} + +inline bool +PSParallelCompact::is_in(oop* p, HeapWord* beg_addr, HeapWord* end_addr) { + return is_in((HeapWord*)p, beg_addr, end_addr); +} + +inline MutableSpace* PSParallelCompact::space(SpaceId id) { + assert(id < last_space_id, "id out of range"); + return _space_info[id].space(); +} + +inline HeapWord* PSParallelCompact::new_top(SpaceId id) { + assert(id < last_space_id, "id out of range"); + return _space_info[id].new_top(); +} + +inline HeapWord* PSParallelCompact::dense_prefix(SpaceId id) { + assert(id < last_space_id, "id out of range"); + return _space_info[id].dense_prefix(); +} + +inline ObjectStartArray* PSParallelCompact::start_array(SpaceId id) { + assert(id < last_space_id, "id out of range"); + return _space_info[id].start_array(); +} + +inline bool PSParallelCompact::should_update_klass(klassOop k) { + return ((HeapWord*) k) >= dense_prefix(perm_space_id); +} + +inline void PSParallelCompact::adjust_pointer(oop* p, + HeapWord* beg_addr, + HeapWord* end_addr) { + if (is_in(p, beg_addr, end_addr)) { + adjust_pointer(p); + } +} + +class MoveAndUpdateClosure: public ParMarkBitMapClosure { + public: + inline MoveAndUpdateClosure(ParMarkBitMap* bitmap, ParCompactionManager* cm, + ObjectStartArray* start_array, + HeapWord* destination, size_t words); + + // Accessors. + HeapWord* destination() const { return _destination; } + + // If the object will fit (size <= words_remaining()), copy it to the current + // destination, update the interior oops and the start array and return either + // full (if the closure is full) or incomplete. If the object will not fit, + // return would_overflow. + virtual IterationStatus do_addr(HeapWord* addr, size_t size); + + // Copy enough words to fill this closure, starting at source(). Interior + // oops and the start array are not updated. Return full. + IterationStatus copy_until_full(); + + // Copy enough words to fill this closure or to the end of an object, + // whichever is smaller, starting at source(). Interior oops and the start + // array are not updated. + void copy_partial_obj(); + + protected: + // Update variables to indicate that word_count words were processed. + inline void update_state(size_t word_count); + + protected: + ObjectStartArray* const _start_array; + HeapWord* _destination; // Next addr to be written. +}; + +inline +MoveAndUpdateClosure::MoveAndUpdateClosure(ParMarkBitMap* bitmap, + ParCompactionManager* cm, + ObjectStartArray* start_array, + HeapWord* destination, + size_t words) : + ParMarkBitMapClosure(bitmap, cm, words), _start_array(start_array) +{ + _destination = destination; +} + +inline void MoveAndUpdateClosure::update_state(size_t words) +{ + decrement_words_remaining(words); + _source += words; + _destination += words; +} + +class UpdateOnlyClosure: public ParMarkBitMapClosure { + private: + const PSParallelCompact::SpaceId _space_id; + ObjectStartArray* const _start_array; + + public: + UpdateOnlyClosure(ParMarkBitMap* mbm, + ParCompactionManager* cm, + PSParallelCompact::SpaceId space_id); + + // Update the object. + virtual IterationStatus do_addr(HeapWord* addr, size_t words); + + inline void do_addr(HeapWord* addr); +}; + +inline void UpdateOnlyClosure::do_addr(HeapWord* addr) { + _start_array->allocate_block(addr); + oop(addr)->update_contents(compaction_manager()); +} + +class FillClosure: public ParMarkBitMapClosure { +public: + FillClosure(ParCompactionManager* cm, PSParallelCompact::SpaceId space_id): + ParMarkBitMapClosure(PSParallelCompact::mark_bitmap(), cm), + _space_id(space_id), + _start_array(PSParallelCompact::start_array(space_id)) + { + assert(_space_id == PSParallelCompact::perm_space_id || + _space_id == PSParallelCompact::old_space_id, + "cannot use FillClosure in the young gen"); + assert(bitmap() != NULL, "need a bitmap"); + assert(_start_array != NULL, "need a start array"); + } + + void fill_region(HeapWord* addr, size_t size) { + MemRegion region(addr, size); + SharedHeap::fill_region_with_object(region); + _start_array->allocate_block(addr); + } + + virtual IterationStatus do_addr(HeapWord* addr, size_t size) { + fill_region(addr, size); + return ParMarkBitMap::incomplete; + } + +private: + const PSParallelCompact::SpaceId _space_id; + ObjectStartArray* const _start_array; +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPermGen.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPermGen.cpp new file mode 100644 index 00000000000..53b3010ecee --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPermGen.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_psPermGen.cpp.incl" + +PSPermGen::PSPermGen(ReservedSpace rs, size_t alignment, + size_t initial_size, size_t min_size, size_t max_size, + const char* gen_name, int level) : + PSOldGen(rs, alignment, initial_size, min_size, max_size, gen_name, level), + _last_used(0) +{ + assert(object_mark_sweep() != NULL, "Sanity"); + + object_mark_sweep()->set_allowed_dead_ratio(PermMarkSweepDeadRatio); + _avg_size = new AdaptivePaddedAverage(AdaptivePermSizeWeight, + PermGenPadding); +} + +HeapWord* PSPermGen::allocate_permanent(size_t size) { + assert_locked_or_safepoint(Heap_lock); + HeapWord* obj = allocate_noexpand(size, false); + + if (obj == NULL) { + obj = expand_and_allocate(size, false); + } + + return obj; +} + +void PSPermGen::compute_new_size(size_t used_before_collection) { + // Update our padded average of objects allocated in perm + // gen between collections. + assert(used_before_collection >= _last_used, + "negative allocation amount since last GC?"); + + const size_t alloc_since_last_gc = used_before_collection - _last_used; + _avg_size->sample(alloc_since_last_gc); + + const size_t current_live = used_in_bytes(); + // Stash away the current amount live for the next call to this method. + _last_used = current_live; + + // We have different alignment constraints than the rest of the heap. + const size_t alignment = MAX2(MinPermHeapExpansion, + virtual_space()->alignment()); + + // Compute the desired size: + // The free space is the newly computed padded average, + // so the desired size is what's live + the free space. + size_t desired_size = current_live + (size_t)_avg_size->padded_average(); + desired_size = align_size_up(desired_size, alignment); + + // ...and no larger or smaller than our max and min allowed. + desired_size = MAX2(MIN2(desired_size, _max_gen_size), _min_gen_size); + assert(desired_size <= _max_gen_size, "just checking"); + + const size_t size_before = _virtual_space->committed_size(); + + if (desired_size == size_before) { + // no change, we're done + return; + } + + { + // We'll be growing or shrinking the heap: in either case, + // we need to hold a lock. + MutexLocker x(ExpandHeap_lock); + if (desired_size > size_before) { + const size_t change_bytes = desired_size - size_before; + const size_t aligned_change_bytes = + align_size_up(change_bytes, alignment); + expand_by(aligned_change_bytes); + } else { + // Shrinking + const size_t change_bytes = + size_before - desired_size; + const size_t aligned_change_bytes = align_size_down(change_bytes, alignment); + shrink(aligned_change_bytes); + } + } + + // While this code isn't controlled by AdaptiveSizePolicy, it's + // convenient to see all resizing decsions under the same flag. + if (PrintAdaptiveSizePolicy) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + gclog_or_tty->print_cr("AdaptiveSizePolicy::perm generation size: " + "collection: %d " + "(" SIZE_FORMAT ") -> (" SIZE_FORMAT ") ", + heap->total_collections(), + size_before, _virtual_space->committed_size()); + } +} + + + +void PSPermGen::move_and_update(ParCompactionManager* cm) { + PSParallelCompact::move_and_update(cm, PSParallelCompact::perm_space_id); +} + +void PSPermGen::precompact() { + // Reset start array first. + debug_only(if (!UseParallelOldGC || !VerifyParallelOldWithMarkSweep) {) + _start_array.reset(); + debug_only(}) + object_mark_sweep()->precompact(); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPermGen.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPermGen.hpp new file mode 100644 index 00000000000..8beaac3fa8d --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPermGen.hpp @@ -0,0 +1,53 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class AdaptivePaddedAverage; + +class PSPermGen : public PSOldGen { + friend class VMStructs; + protected: + AdaptivePaddedAverage* _avg_size; // Used for sizing + size_t _last_used; // Amount used at last GC, used for sizing + + public: + // Initialize the generation. + PSPermGen(ReservedSpace rs, size_t alignment, size_t initial_byte_size, + size_t minimum_byte_size, size_t maximum_byte_size, + const char* gen_name, int level); + + // Permanent Gen special allocation. Uses the OldGen allocation + // routines, which should not be directly called on this generation. + HeapWord* allocate_permanent(size_t word_size); + + // Size calculation. + void compute_new_size(size_t used_before_collection); + + // MarkSweep code + virtual void precompact(); + + // Parallel old + virtual void move_and_update(ParCompactionManager* cm); + + virtual const char* name() const { return "PSPermGen"; } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.cpp new file mode 100644 index 00000000000..46b39edebd5 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.cpp @@ -0,0 +1,162 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_psPromotionLAB.cpp.incl" + +const size_t PSPromotionLAB::filler_header_size = align_object_size(typeArrayOopDesc::header_size(T_INT)); + +// This is the shared initialization code. It sets up the basic pointers, +// and allows enough extra space for a filler object. We call a virtual +// method, "lab_is_valid()" to handle the different asserts the old/young +// labs require. +void PSPromotionLAB::initialize(MemRegion lab) { + assert(lab_is_valid(lab), "Sanity"); + + HeapWord* bottom = lab.start(); + HeapWord* end = lab.end(); + + set_bottom(bottom); + set_end(end); + set_top(bottom); + + // We can be initialized to a zero size! + if (free() > 0) { + if (ZapUnusedHeapArea) { + debug_only(Copy::fill_to_words(top(), free()/HeapWordSize, badHeapWord)); + } + + // NOTE! We need to allow space for a filler object. + assert(lab.word_size() >= filler_header_size, "lab is too small"); + end = end - filler_header_size; + set_end(end); + + _state = needs_flush; + } else { + _state = zero_size; + } + + assert(this->top() <= this->end(), "pointers out of order"); +} + +// Fill all remaining lab space with an unreachable object. +// The goal is to leave a contiguous parseable span of objects. +void PSPromotionLAB::flush() { + assert(_state != flushed, "Attempt to flush PLAB twice"); + assert(top() <= end(), "pointers out of order"); + + // If we were initialized to a zero sized lab, there is + // nothing to flush + if (_state == zero_size) + return; + + // PLAB's never allocate the last aligned_header_size + // so they can always fill with an array. + HeapWord* tlab_end = end() + filler_header_size; + typeArrayOop filler_oop = (typeArrayOop) top(); + filler_oop->set_mark(markOopDesc::prototype()); + filler_oop->set_klass(Universe::intArrayKlassObj()); + const size_t array_length = + pointer_delta(tlab_end, top()) - typeArrayOopDesc::header_size(T_INT); + assert( (array_length * (HeapWordSize/sizeof(jint))) < (size_t)max_jint, "array too big in PSPromotionLAB"); + filler_oop->set_length((int)(array_length * (HeapWordSize/sizeof(jint)))); + +#ifdef ASSERT + // Note that we actually DO NOT want to use the aligned header size! + HeapWord* elt_words = ((HeapWord*)filler_oop) + typeArrayOopDesc::header_size(T_INT); + Copy::fill_to_words(elt_words, array_length, 0xDEAABABE); +#endif + + set_bottom(NULL); + set_end(NULL); + set_top(NULL); + + _state = flushed; +} + +bool PSPromotionLAB::unallocate_object(oop obj) { + assert(Universe::heap()->is_in(obj), "Object outside heap"); + + if (contains(obj)) { + HeapWord* object_end = (HeapWord*)obj + obj->size(); + assert(object_end <= top(), "Object crosses promotion LAB boundary"); + + if (object_end == top()) { + set_top((HeapWord*)obj); + return true; + } + } + + return false; +} + +// Fill all remaining lab space with an unreachable object. +// The goal is to leave a contiguous parseable span of objects. +void PSOldPromotionLAB::flush() { + assert(_state != flushed, "Attempt to flush PLAB twice"); + assert(top() <= end(), "pointers out of order"); + + if (_state == zero_size) + return; + + HeapWord* obj = top(); + + PSPromotionLAB::flush(); + + assert(_start_array != NULL, "Sanity"); + + _start_array->allocate_block(obj); +} + +#ifdef ASSERT + +bool PSYoungPromotionLAB::lab_is_valid(MemRegion lab) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + MutableSpace* to_space = heap->young_gen()->to_space(); + MemRegion used = to_space->used_region(); + if (used.contains(lab)) { + return true; + } + + return false; +} + +bool PSOldPromotionLAB::lab_is_valid(MemRegion lab) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + assert(_start_array->covered_region().contains(lab), "Sanity"); + + PSOldGen* old_gen = heap->old_gen(); + MemRegion used = old_gen->object_space()->used_region(); + + if (used.contains(lab)) { + return true; + } + + return false; +} + +#endif /* ASSERT */ diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.hpp new file mode 100644 index 00000000000..fea56055377 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.hpp @@ -0,0 +1,142 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// PSPromotionLAB is a parallel scavenge promotion lab. This class acts very +// much like a MutableSpace. We couldn't embed a MutableSpace, though, as +// it has a considerable number of asserts and invariants that are violated. +// + +class ObjectStartArray; + +class PSPromotionLAB : public CHeapObj { + protected: + static const size_t filler_header_size; + + enum LabState { + needs_flush, + flushed, + zero_size + }; + + HeapWord* _top; + HeapWord* _bottom; + HeapWord* _end; + LabState _state; + + void set_top(HeapWord* value) { _top = value; } + void set_bottom(HeapWord* value) { _bottom = value; } + void set_end(HeapWord* value) { _end = value; } + + // The shared initialize code invokes this. + debug_only(virtual bool lab_is_valid(MemRegion lab) { return false; }); + + PSPromotionLAB() : _top(NULL), _bottom(NULL), _end(NULL) { } + + public: + // Filling and flushing. + void initialize(MemRegion lab); + + virtual void flush(); + + // Accessors + HeapWord* bottom() const { return _bottom; } + HeapWord* end() const { return _end; } + HeapWord* top() const { return _top; } + + bool is_flushed() { return _state == flushed; } + + bool unallocate_object(oop obj); + + // Returns a subregion containing all objects in this space. + MemRegion used_region() { return MemRegion(bottom(), top()); } + + // Boolean querries. + bool is_empty() const { return used() == 0; } + bool not_empty() const { return used() > 0; } + bool contains(const void* p) const { return _bottom <= p && p < _end; } + + // Size computations. Sizes are in bytes. + size_t capacity() const { return byte_size(bottom(), end()); } + size_t used() const { return byte_size(bottom(), top()); } + size_t free() const { return byte_size(top(), end()); } +}; + +class PSYoungPromotionLAB : public PSPromotionLAB { + public: + PSYoungPromotionLAB() { } + + // Not MT safe + HeapWord* allocate(size_t size) { + // Can't assert this, when young fills, we keep the LAB around, but flushed. + // assert(_state != flushed, "Sanity"); + HeapWord* obj = top(); + HeapWord* new_top = obj + size; + // The 'new_top>obj' check is needed to detect overflow of obj+size. + if (new_top > obj && new_top <= end()) { + set_top(new_top); + assert(is_object_aligned((intptr_t)obj) && is_object_aligned((intptr_t)new_top), + "checking alignment"); + return obj; + } + + return NULL; + } + + debug_only(virtual bool lab_is_valid(MemRegion lab)); +}; + +class PSOldPromotionLAB : public PSPromotionLAB { + private: + ObjectStartArray* _start_array; + + public: + PSOldPromotionLAB() : _start_array(NULL) { } + PSOldPromotionLAB(ObjectStartArray* start_array) : _start_array(start_array) { } + + void set_start_array(ObjectStartArray* start_array) { _start_array = start_array; } + + void flush(); + + // Not MT safe + HeapWord* allocate(size_t size) { + // Cannot test for this now that we're doing promotion failures + // assert(_state != flushed, "Sanity"); + assert(_start_array != NULL, "Sanity"); + HeapWord* obj = top(); + HeapWord* new_top = obj + size; + // The 'new_top>obj' check is needed to detect overflow of obj+size. + if (new_top > obj && new_top <= end()) { + set_top(new_top); + assert(is_object_aligned((intptr_t)obj) && is_object_aligned((intptr_t)new_top), + "checking alignment"); + _start_array->allocate_block(obj); + return obj; + } + + return NULL; + } + + debug_only(virtual bool lab_is_valid(MemRegion lab)); +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp new file mode 100644 index 00000000000..ea31f391ad1 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp @@ -0,0 +1,625 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_psPromotionManager.cpp.incl" + +PSPromotionManager** PSPromotionManager::_manager_array = NULL; +OopStarTaskQueueSet* PSPromotionManager::_stack_array_depth = NULL; +OopTaskQueueSet* PSPromotionManager::_stack_array_breadth = NULL; +PSOldGen* PSPromotionManager::_old_gen = NULL; +MutableSpace* PSPromotionManager::_young_space = NULL; + +void PSPromotionManager::initialize() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + _old_gen = heap->old_gen(); + _young_space = heap->young_gen()->to_space(); + + assert(_manager_array == NULL, "Attempt to initialize twice"); + _manager_array = NEW_C_HEAP_ARRAY(PSPromotionManager*, ParallelGCThreads+1 ); + guarantee(_manager_array != NULL, "Could not initialize promotion manager"); + + if (UseDepthFirstScavengeOrder) { + _stack_array_depth = new OopStarTaskQueueSet(ParallelGCThreads); + guarantee(_stack_array_depth != NULL, "Count not initialize promotion manager"); + } else { + _stack_array_breadth = new OopTaskQueueSet(ParallelGCThreads); + guarantee(_stack_array_breadth != NULL, "Count not initialize promotion manager"); + } + + // Create and register the PSPromotionManager(s) for the worker threads. + for(uint i=0; iregister_queue(i, _manager_array[i]->claimed_stack_depth()); + } else { + stack_array_breadth()->register_queue(i, _manager_array[i]->claimed_stack_breadth()); + } + } + + // The VMThread gets its own PSPromotionManager, which is not available + // for work stealing. + _manager_array[ParallelGCThreads] = new PSPromotionManager(); + guarantee(_manager_array[ParallelGCThreads] != NULL, "Could not create PSPromotionManager"); +} + +PSPromotionManager* PSPromotionManager::gc_thread_promotion_manager(int index) { + assert(index >= 0 && index < (int)ParallelGCThreads, "index out of range"); + assert(_manager_array != NULL, "Sanity"); + return _manager_array[index]; +} + +PSPromotionManager* PSPromotionManager::vm_thread_promotion_manager() { + assert(_manager_array != NULL, "Sanity"); + return _manager_array[ParallelGCThreads]; +} + +void PSPromotionManager::pre_scavenge() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + _young_space = heap->young_gen()->to_space(); + + for(uint i=0; ireset(); + } +} + +void PSPromotionManager::post_scavenge() { +#if PS_PM_STATS + print_stats(); +#endif // PS_PM_STATS + + for(uint i=0; ioverflow_stack_depth()->length() <= 0), + "promotion manager overflow stack must be empty"); + guarantee((UseDepthFirstScavengeOrder || + manager->overflow_stack_breadth()->length() <= 0), + "promotion manager overflow stack must be empty"); + + guarantee((!UseDepthFirstScavengeOrder || + manager->claimed_stack_depth()->size() <= 0), + "promotion manager claimed stack must be empty"); + guarantee((UseDepthFirstScavengeOrder || + manager->claimed_stack_breadth()->size() <= 0), + "promotion manager claimed stack must be empty"); + } else { + guarantee((!UseDepthFirstScavengeOrder || + manager->overflow_stack_depth()->length() <= 0), + "VM Thread promotion manager overflow stack " + "must be empty"); + guarantee((UseDepthFirstScavengeOrder || + manager->overflow_stack_breadth()->length() <= 0), + "VM Thread promotion manager overflow stack " + "must be empty"); + + guarantee((!UseDepthFirstScavengeOrder || + manager->claimed_stack_depth()->size() <= 0), + "VM Thread promotion manager claimed stack " + "must be empty"); + guarantee((UseDepthFirstScavengeOrder || + manager->claimed_stack_breadth()->size() <= 0), + "VM Thread promotion manager claimed stack " + "must be empty"); + } + + manager->flush_labs(); + } +} + +#if PS_PM_STATS + +void +PSPromotionManager::print_stats(uint i) { + tty->print_cr("---- GC Worker %2d Stats", i); + tty->print_cr(" total pushes %8d", _total_pushes); + tty->print_cr(" masked pushes %8d", _masked_pushes); + tty->print_cr(" overflow pushes %8d", _overflow_pushes); + tty->print_cr(" max overflow length %8d", _max_overflow_length); + tty->print_cr(""); + tty->print_cr(" arrays chunked %8d", _arrays_chunked); + tty->print_cr(" array chunks processed %8d", _array_chunks_processed); + tty->print_cr(""); + tty->print_cr(" total steals %8d", _total_steals); + tty->print_cr(" masked steals %8d", _masked_steals); + tty->print_cr(""); +} + +void +PSPromotionManager::print_stats() { + tty->print_cr("== GC Tasks Stats (%s), GC %3d", + (UseDepthFirstScavengeOrder) ? "Depth-First" : "Breadth-First", + Universe::heap()->total_collections()); + + for (uint i = 0; i < ParallelGCThreads+1; ++i) { + PSPromotionManager* manager = manager_array(i); + manager->print_stats(i); + } +} + +#endif // PS_PM_STATS + +PSPromotionManager::PSPromotionManager() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + _depth_first = UseDepthFirstScavengeOrder; + + // We set the old lab's start array. + _old_lab.set_start_array(old_gen()->start_array()); + + uint queue_size; + if (depth_first()) { + claimed_stack_depth()->initialize(); + queue_size = claimed_stack_depth()->max_elems(); + // We want the overflow stack to be permanent + _overflow_stack_depth = new (ResourceObj::C_HEAP) GrowableArray(10, true); + _overflow_stack_breadth = NULL; + } else { + claimed_stack_breadth()->initialize(); + queue_size = claimed_stack_breadth()->max_elems(); + // We want the overflow stack to be permanent + _overflow_stack_breadth = new (ResourceObj::C_HEAP) GrowableArray(10, true); + _overflow_stack_depth = NULL; + } + + _totally_drain = (ParallelGCThreads == 1) || (GCDrainStackTargetSize == 0); + if (_totally_drain) { + _target_stack_size = 0; + } else { + // don't let the target stack size to be more than 1/4 of the entries + _target_stack_size = (uint) MIN2((uint) GCDrainStackTargetSize, + (uint) (queue_size / 4)); + } + + _array_chunk_size = ParGCArrayScanChunk; + // let's choose 1.5x the chunk size + _min_array_size_for_chunking = 3 * _array_chunk_size / 2; + + reset(); +} + +void PSPromotionManager::reset() { + assert(claimed_stack_empty(), "reset of non-empty claimed stack"); + assert(overflow_stack_empty(), "reset of non-empty overflow stack"); + + // We need to get an assert in here to make sure the labs are always flushed. + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + // Do not prefill the LAB's, save heap wastage! + HeapWord* lab_base = young_space()->top(); + _young_lab.initialize(MemRegion(lab_base, (size_t)0)); + _young_gen_is_full = false; + + lab_base = old_gen()->object_space()->top(); + _old_lab.initialize(MemRegion(lab_base, (size_t)0)); + _old_gen_is_full = false; + + _prefetch_queue.clear(); + +#if PS_PM_STATS + _total_pushes = 0; + _masked_pushes = 0; + _overflow_pushes = 0; + _max_overflow_length = 0; + _arrays_chunked = 0; + _array_chunks_processed = 0; + _total_steals = 0; + _masked_steals = 0; +#endif // PS_PM_STATS +} + +void PSPromotionManager::drain_stacks_depth(bool totally_drain) { + assert(depth_first(), "invariant"); + assert(overflow_stack_depth() != NULL, "invariant"); + totally_drain = totally_drain || _totally_drain; + +#ifdef ASSERT + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + MutableSpace* to_space = heap->young_gen()->to_space(); + MutableSpace* old_space = heap->old_gen()->object_space(); + MutableSpace* perm_space = heap->perm_gen()->object_space(); +#endif /* ASSERT */ + + do { + oop* p; + + // Drain overflow stack first, so other threads can steal from + // claimed stack while we work. + while(!overflow_stack_depth()->is_empty()) { + p = overflow_stack_depth()->pop(); + process_popped_location_depth(p); + } + + if (totally_drain) { + while (claimed_stack_depth()->pop_local(p)) { + process_popped_location_depth(p); + } + } else { + while (claimed_stack_depth()->size() > _target_stack_size && + claimed_stack_depth()->pop_local(p)) { + process_popped_location_depth(p); + } + } + } while( (totally_drain && claimed_stack_depth()->size() > 0) || + (overflow_stack_depth()->length() > 0) ); + + assert(!totally_drain || claimed_stack_empty(), "Sanity"); + assert(totally_drain || + claimed_stack_depth()->size() <= _target_stack_size, + "Sanity"); + assert(overflow_stack_empty(), "Sanity"); +} + +void PSPromotionManager::drain_stacks_breadth(bool totally_drain) { + assert(!depth_first(), "invariant"); + assert(overflow_stack_breadth() != NULL, "invariant"); + totally_drain = totally_drain || _totally_drain; + +#ifdef ASSERT + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + MutableSpace* to_space = heap->young_gen()->to_space(); + MutableSpace* old_space = heap->old_gen()->object_space(); + MutableSpace* perm_space = heap->perm_gen()->object_space(); +#endif /* ASSERT */ + + do { + oop obj; + + // Drain overflow stack first, so other threads can steal from + // claimed stack while we work. + while(!overflow_stack_breadth()->is_empty()) { + obj = overflow_stack_breadth()->pop(); + obj->copy_contents(this); + } + + if (totally_drain) { + // obj is a reference!!! + while (claimed_stack_breadth()->pop_local(obj)) { + // It would be nice to assert about the type of objects we might + // pop, but they can come from anywhere, unfortunately. + obj->copy_contents(this); + } + } else { + // obj is a reference!!! + while (claimed_stack_breadth()->size() > _target_stack_size && + claimed_stack_breadth()->pop_local(obj)) { + // It would be nice to assert about the type of objects we might + // pop, but they can come from anywhere, unfortunately. + obj->copy_contents(this); + } + } + + // If we could not find any other work, flush the prefetch queue + if (claimed_stack_breadth()->size() == 0 && + (overflow_stack_breadth()->length() == 0)) { + flush_prefetch_queue(); + } + } while((totally_drain && claimed_stack_breadth()->size() > 0) || + (overflow_stack_breadth()->length() > 0)); + + assert(!totally_drain || claimed_stack_empty(), "Sanity"); + assert(totally_drain || + claimed_stack_breadth()->size() <= _target_stack_size, + "Sanity"); + assert(overflow_stack_empty(), "Sanity"); +} + +void PSPromotionManager::flush_labs() { + assert(claimed_stack_empty(), "Attempt to flush lab with live stack"); + assert(overflow_stack_empty(), "Attempt to flush lab with live overflow stack"); + + // If either promotion lab fills up, we can flush the + // lab but not refill it, so check first. + assert(!_young_lab.is_flushed() || _young_gen_is_full, "Sanity"); + if (!_young_lab.is_flushed()) + _young_lab.flush(); + + assert(!_old_lab.is_flushed() || _old_gen_is_full, "Sanity"); + if (!_old_lab.is_flushed()) + _old_lab.flush(); + + // Let PSScavenge know if we overflowed + if (_young_gen_is_full) { + PSScavenge::set_survivor_overflow(true); + } +} + +// +// This method is pretty bulky. It would be nice to split it up +// into smaller submethods, but we need to be careful not to hurt +// performance. +// + +oop PSPromotionManager::copy_to_survivor_space(oop o, bool depth_first) { + assert(PSScavenge::should_scavenge(o), "Sanity"); + + oop new_obj = NULL; + + // NOTE! We must be very careful with any methods that access the mark + // in o. There may be multiple threads racing on it, and it may be forwarded + // at any time. Do not use oop methods for accessing the mark! + markOop test_mark = o->mark(); + + // The same test as "o->is_forwarded()" + if (!test_mark->is_marked()) { + bool new_obj_is_tenured = false; + size_t new_obj_size = o->size(); + + // Find the objects age, MT safe. + int age = (test_mark->has_displaced_mark_helper() /* o->has_displaced_mark() */) ? + test_mark->displaced_mark_helper()->age() : test_mark->age(); + + // Try allocating obj in to-space (unless too old) + if (age < PSScavenge::tenuring_threshold()) { + new_obj = (oop) _young_lab.allocate(new_obj_size); + if (new_obj == NULL && !_young_gen_is_full) { + // Do we allocate directly, or flush and refill? + if (new_obj_size > (YoungPLABSize / 2)) { + // Allocate this object directly + new_obj = (oop)young_space()->cas_allocate(new_obj_size); + } else { + // Flush and fill + _young_lab.flush(); + + HeapWord* lab_base = young_space()->cas_allocate(YoungPLABSize); + if (lab_base != NULL) { + _young_lab.initialize(MemRegion(lab_base, YoungPLABSize)); + // Try the young lab allocation again. + new_obj = (oop) _young_lab.allocate(new_obj_size); + } else { + _young_gen_is_full = true; + } + } + } + } + + // Otherwise try allocating obj tenured + if (new_obj == NULL) { +#ifndef PRODUCT + if (Universe::heap()->promotion_should_fail()) { + return oop_promotion_failed(o, test_mark); + } +#endif // #ifndef PRODUCT + + new_obj = (oop) _old_lab.allocate(new_obj_size); + new_obj_is_tenured = true; + + if (new_obj == NULL) { + if (!_old_gen_is_full) { + // Do we allocate directly, or flush and refill? + if (new_obj_size > (OldPLABSize / 2)) { + // Allocate this object directly + new_obj = (oop)old_gen()->cas_allocate(new_obj_size); + } else { + // Flush and fill + _old_lab.flush(); + + HeapWord* lab_base = old_gen()->cas_allocate(OldPLABSize); + if(lab_base != NULL) { + _old_lab.initialize(MemRegion(lab_base, OldPLABSize)); + // Try the old lab allocation again. + new_obj = (oop) _old_lab.allocate(new_obj_size); + } + } + } + + // This is the promotion failed test, and code handling. + // The code belongs here for two reasons. It is slightly + // different thatn the code below, and cannot share the + // CAS testing code. Keeping the code here also minimizes + // the impact on the common case fast path code. + + if (new_obj == NULL) { + _old_gen_is_full = true; + return oop_promotion_failed(o, test_mark); + } + } + } + + assert(new_obj != NULL, "allocation should have succeeded"); + + // Copy obj + Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)new_obj, new_obj_size); + + // Now we have to CAS in the header. + if (o->cas_forward_to(new_obj, test_mark)) { + // We won any races, we "own" this object. + assert(new_obj == o->forwardee(), "Sanity"); + + // Increment age if obj still in new generation. Now that + // we're dealing with a markOop that cannot change, it is + // okay to use the non mt safe oop methods. + if (!new_obj_is_tenured) { + new_obj->incr_age(); + assert(young_space()->contains(new_obj), "Attempt to push non-promoted obj"); + } + + if (depth_first) { + // Do the size comparison first with new_obj_size, which we + // already have. Hopefully, only a few objects are larger than + // _min_array_size_for_chunking, and most of them will be arrays. + // So, the is->objArray() test would be very infrequent. + if (new_obj_size > _min_array_size_for_chunking && + new_obj->is_objArray() && + PSChunkLargeArrays) { + // we'll chunk it +#if PS_PM_STATS + ++_arrays_chunked; +#endif // PS_PM_STATS + oop* const masked_o = mask_chunked_array_oop(o); + push_depth(masked_o); +#if PS_PM_STATS + ++_masked_pushes; +#endif // PS_PM_STATS + } else { + // we'll just push its contents + new_obj->push_contents(this); + } + } else { + push_breadth(new_obj); + } + } else { + // We lost, someone else "owns" this object + guarantee(o->is_forwarded(), "Object must be forwarded if the cas failed."); + + // Unallocate the space used. NOTE! We may have directly allocated + // the object. If so, we cannot deallocate it, so we have to test! + if (new_obj_is_tenured) { + if (!_old_lab.unallocate_object(new_obj)) { + // The promotion lab failed to unallocate the object. + // We need to overwrite the object with a filler that + // contains no interior pointers. + MemRegion mr((HeapWord*)new_obj, new_obj_size); + // Clean this up and move to oopFactory (see bug 4718422) + SharedHeap::fill_region_with_object(mr); + } + } else { + if (!_young_lab.unallocate_object(new_obj)) { + // The promotion lab failed to unallocate the object. + // We need to overwrite the object with a filler that + // contains no interior pointers. + MemRegion mr((HeapWord*)new_obj, new_obj_size); + // Clean this up and move to oopFactory (see bug 4718422) + SharedHeap::fill_region_with_object(mr); + } + } + + // don't update this before the unallocation! + new_obj = o->forwardee(); + } + } else { + assert(o->is_forwarded(), "Sanity"); + new_obj = o->forwardee(); + } + +#ifdef DEBUG + // This code must come after the CAS test, or it will print incorrect + // information. + if (TraceScavenge) { + gclog_or_tty->print_cr("{%s %s 0x%x -> 0x%x (%d)}", + PSScavenge::should_scavenge(new_obj) ? "copying" : "tenuring", + new_obj->blueprint()->internal_name(), o, new_obj, new_obj->size()); + + } +#endif + + return new_obj; +} + +void PSPromotionManager::process_array_chunk(oop old) { + assert(PSChunkLargeArrays, "invariant"); + assert(old->is_objArray(), "invariant"); + assert(old->is_forwarded(), "invariant"); + +#if PS_PM_STATS + ++_array_chunks_processed; +#endif // PS_PM_STATS + + oop const obj = old->forwardee(); + + int start; + int const end = arrayOop(old)->length(); + if (end > (int) _min_array_size_for_chunking) { + // we'll chunk more + start = end - _array_chunk_size; + assert(start > 0, "invariant"); + arrayOop(old)->set_length(start); + push_depth(mask_chunked_array_oop(old)); +#if PS_PM_STATS + ++_masked_pushes; +#endif // PS_PM_STATS + } else { + // this is the final chunk for this array + start = 0; + int const actual_length = arrayOop(obj)->length(); + arrayOop(old)->set_length(actual_length); + } + + assert(start < end, "invariant"); + oop* const base = objArrayOop(obj)->base(); + oop* p = base + start; + oop* const chunk_end = base + end; + while (p < chunk_end) { + if (PSScavenge::should_scavenge(*p)) { + claim_or_forward_depth(p); + } + ++p; + } +} + +oop PSPromotionManager::oop_promotion_failed(oop obj, markOop obj_mark) { + assert(_old_gen_is_full || PromotionFailureALot, "Sanity"); + + // Attempt to CAS in the header. + // This tests if the header is still the same as when + // this started. If it is the same (i.e., no forwarding + // pointer has been installed), then this thread owns + // it. + if (obj->cas_forward_to(obj, obj_mark)) { + // We won any races, we "own" this object. + assert(obj == obj->forwardee(), "Sanity"); + + if (depth_first()) { + obj->push_contents(this); + } else { + // Don't bother incrementing the age, just push + // onto the claimed_stack.. + push_breadth(obj); + } + + // Save the mark if needed + PSScavenge::oop_promotion_failed(obj, obj_mark); + } else { + // We lost, someone else "owns" this object + guarantee(obj->is_forwarded(), "Object must be forwarded if the cas failed."); + + // No unallocation to worry about. + obj = obj->forwardee(); + } + +#ifdef DEBUG + if (TraceScavenge) { + gclog_or_tty->print_cr("{%s %s 0x%x (%d)}", + "promotion-failure", + obj->blueprint()->internal_name(), + obj, obj->size()); + + } +#endif + + return obj; +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp new file mode 100644 index 00000000000..c40b016668f --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp @@ -0,0 +1,277 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// psPromotionManager is used by a single thread to manage object survival +// during a scavenge. The promotion manager contains thread local data only. +// +// NOTE! Be carefull when allocating the stacks on cheap. If you are going +// to use a promotion manager in more than one thread, the stacks MUST be +// on cheap. This can lead to memory leaks, though, as they are not auto +// deallocated. +// +// FIX ME FIX ME Add a destructor, and don't rely on the user to drain/flush/deallocate! +// + +// Move to some global location +#define HAS_BEEN_MOVED 0x1501d01d +// End move to some global location + +class MutableSpace; +class PSOldGen; +class ParCompactionManager; + +#define PS_CHUNKED_ARRAY_OOP_MASK 1 + +#define PS_PM_STATS 0 + +class PSPromotionManager : public CHeapObj { + friend class PSScavenge; + friend class PSRefProcTaskExecutor; + private: + static PSPromotionManager** _manager_array; + static OopStarTaskQueueSet* _stack_array_depth; + static OopTaskQueueSet* _stack_array_breadth; + static PSOldGen* _old_gen; + static MutableSpace* _young_space; + +#if PS_PM_STATS + uint _total_pushes; + uint _masked_pushes; + + uint _overflow_pushes; + uint _max_overflow_length; + + uint _arrays_chunked; + uint _array_chunks_processed; + + uint _total_steals; + uint _masked_steals; + + void print_stats(uint i); + static void print_stats(); +#endif // PS_PM_STATS + + PSYoungPromotionLAB _young_lab; + PSOldPromotionLAB _old_lab; + bool _young_gen_is_full; + bool _old_gen_is_full; + PrefetchQueue _prefetch_queue; + + OopStarTaskQueue _claimed_stack_depth; + GrowableArray* _overflow_stack_depth; + OopTaskQueue _claimed_stack_breadth; + GrowableArray* _overflow_stack_breadth; + + bool _depth_first; + bool _totally_drain; + uint _target_stack_size; + + uint _array_chunk_size; + uint _min_array_size_for_chunking; + + // Accessors + static PSOldGen* old_gen() { return _old_gen; } + static MutableSpace* young_space() { return _young_space; } + + inline static PSPromotionManager* manager_array(int index); + + GrowableArray* overflow_stack_depth() { return _overflow_stack_depth; } + GrowableArray* overflow_stack_breadth() { return _overflow_stack_breadth; } + + // On the task queues we push reference locations as well as + // partially-scanned arrays (in the latter case, we push an oop to + // the from-space image of the array and the length on the + // from-space image indicates how many entries on the array we still + // need to scan; this is basically how ParNew does partial array + // scanning too). To be able to distinguish between reference + // locations and partially-scanned array oops we simply mask the + // latter oops with 0x01. The next three methods do the masking, + // unmasking, and checking whether the oop is masked or not. Notice + // that the signature of the mask and unmask methods looks a bit + // strange, as they accept and return different types (oop and + // oop*). This is because of the difference in types between what + // the task queue holds (oop*) and oops to partially-scanned arrays + // (oop). We do all the necessary casting in the mask / unmask + // methods to avoid sprinkling the rest of the code with more casts. + + bool is_oop_masked(oop* p) { + return ((intptr_t) p & PS_CHUNKED_ARRAY_OOP_MASK) == PS_CHUNKED_ARRAY_OOP_MASK; + } + + oop* mask_chunked_array_oop(oop obj) { + assert(!is_oop_masked((oop*) obj), "invariant"); + oop* ret = (oop*) ((intptr_t) obj | PS_CHUNKED_ARRAY_OOP_MASK); + assert(is_oop_masked(ret), "invariant"); + return ret; + } + + oop unmask_chunked_array_oop(oop* p) { + assert(is_oop_masked(p), "invariant"); + oop ret = oop((intptr_t) p & ~PS_CHUNKED_ARRAY_OOP_MASK); + assert(!is_oop_masked((oop*) ret), "invariant"); + return ret; + } + + void process_array_chunk(oop old); + + void push_depth(oop* p) { + assert(depth_first(), "pre-condition"); + +#if PS_PM_STATS + ++_total_pushes; +#endif // PS_PM_STATS + + if (!claimed_stack_depth()->push(p)) { + overflow_stack_depth()->push(p); +#if PS_PM_STATS + ++_overflow_pushes; + uint stack_length = (uint) overflow_stack_depth()->length(); + if (stack_length > _max_overflow_length) { + _max_overflow_length = stack_length; + } +#endif // PS_PM_STATS + } + } + + void push_breadth(oop o) { + assert(!depth_first(), "pre-condition"); + +#if PS_PM_STATS + ++_total_pushes; +#endif // PS_PM_STATS + + if(!claimed_stack_breadth()->push(o)) { + overflow_stack_breadth()->push(o); +#if PS_PM_STATS + ++_overflow_pushes; + uint stack_length = (uint) overflow_stack_breadth()->length(); + if (stack_length > _max_overflow_length) { + _max_overflow_length = stack_length; + } +#endif // PS_PM_STATS + } + } + + protected: + static OopStarTaskQueueSet* stack_array_depth() { return _stack_array_depth; } + static OopTaskQueueSet* stack_array_breadth() { return _stack_array_breadth; } + + public: + // Static + static void initialize(); + + static void pre_scavenge(); + static void post_scavenge(); + + static PSPromotionManager* gc_thread_promotion_manager(int index); + static PSPromotionManager* vm_thread_promotion_manager(); + + static bool steal_depth(int queue_num, int* seed, StarTask& t) { + assert(stack_array_depth() != NULL, "invariant"); + return stack_array_depth()->steal(queue_num, seed, t); + } + + static bool steal_breadth(int queue_num, int* seed, Task& t) { + assert(stack_array_breadth() != NULL, "invariant"); + return stack_array_breadth()->steal(queue_num, seed, t); + } + + PSPromotionManager(); + + // Accessors + OopStarTaskQueue* claimed_stack_depth() { + return &_claimed_stack_depth; + } + OopTaskQueue* claimed_stack_breadth() { + return &_claimed_stack_breadth; + } + + bool young_gen_is_full() { return _young_gen_is_full; } + + bool old_gen_is_full() { return _old_gen_is_full; } + void set_old_gen_is_full(bool state) { _old_gen_is_full = state; } + + // Promotion methods + oop copy_to_survivor_space(oop o, bool depth_first); + oop oop_promotion_failed(oop obj, markOop obj_mark); + + void reset(); + + void flush_labs(); + void drain_stacks(bool totally_drain) { + if (depth_first()) { + drain_stacks_depth(totally_drain); + } else { + drain_stacks_breadth(totally_drain); + } + } + void drain_stacks_cond_depth() { + if (claimed_stack_depth()->size() > _target_stack_size) { + drain_stacks_depth(false); + } + } + void drain_stacks_depth(bool totally_drain); + void drain_stacks_breadth(bool totally_drain); + + bool claimed_stack_empty() { + if (depth_first()) { + return claimed_stack_depth()->size() <= 0; + } else { + return claimed_stack_breadth()->size() <= 0; + } + } + bool overflow_stack_empty() { + if (depth_first()) { + return overflow_stack_depth()->length() <= 0; + } else { + return overflow_stack_breadth()->length() <= 0; + } + } + bool stacks_empty() { + return claimed_stack_empty() && overflow_stack_empty(); + } + bool depth_first() { + return _depth_first; + } + + inline void process_popped_location_depth(oop* p); + + inline void flush_prefetch_queue(); + + inline void claim_or_forward_depth(oop* p); + inline void claim_or_forward_internal_depth(oop* p); + + inline void claim_or_forward_breadth(oop* p); + inline void claim_or_forward_internal_breadth(oop* p); + +#if PS_PM_STATS + void increment_steals(oop* p = NULL) { + _total_steals += 1; + if (p != NULL && is_oop_masked(p)) { + _masked_steals += 1; + } + } +#endif // PS_PM_STATS +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp new file mode 100644 index 00000000000..e900e0601cc --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp @@ -0,0 +1,117 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline PSPromotionManager* PSPromotionManager::manager_array(int index) { + assert(_manager_array != NULL, "access of NULL manager_array"); + assert(index >= 0 && index <= (int)ParallelGCThreads, "out of range manager_array access"); + return _manager_array[index]; +} + +inline void PSPromotionManager::claim_or_forward_internal_depth(oop* p) { + if (p != NULL) { + oop o = *p; + if (o->is_forwarded()) { + o = o->forwardee(); + + // Card mark + if (PSScavenge::is_obj_in_young((HeapWord*) o)) { + PSScavenge::card_table()->inline_write_ref_field_gc(p, o); + } + *p = o; + } else { + push_depth(p); + } + } +} + +inline void PSPromotionManager::claim_or_forward_internal_breadth(oop* p) { + if (p != NULL) { + oop o = *p; + if (o->is_forwarded()) { + o = o->forwardee(); + } else { + o = copy_to_survivor_space(o, false); + } + + // Card mark + if (PSScavenge::is_obj_in_young((HeapWord*) o)) { + PSScavenge::card_table()->inline_write_ref_field_gc(p, o); + } + *p = o; + } +} + +inline void PSPromotionManager::flush_prefetch_queue() { + assert(!depth_first(), "invariant"); + for (int i=0; i<_prefetch_queue.length(); i++) { + claim_or_forward_internal_breadth(_prefetch_queue.pop()); + } +} + +inline void PSPromotionManager::claim_or_forward_depth(oop* p) { + assert(depth_first(), "invariant"); + assert(PSScavenge::should_scavenge(*p, true), "revisiting object?"); + assert(Universe::heap()->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + assert(Universe::heap()->is_in(p), "pointer outside heap"); + + claim_or_forward_internal_depth(p); +} + +inline void PSPromotionManager::claim_or_forward_breadth(oop* p) { + assert(!depth_first(), "invariant"); + assert(PSScavenge::should_scavenge(*p, true), "revisiting object?"); + assert(Universe::heap()->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + assert(Universe::heap()->is_in(p), "pointer outside heap"); + + if (UsePrefetchQueue) { + claim_or_forward_internal_breadth(_prefetch_queue.push_and_pop(p)); + } else { + // This option is used for testing. The use of the prefetch + // queue can delay the processing of the objects and thus + // change the order of object scans. For example, remembered + // set updates are typically the clearing of the remembered + // set (the cards) followed by updates of the remembered set + // for young-to-old pointers. In a situation where there + // is an error in the sequence of clearing and updating + // (e.g. clear card A, update card A, erroneously clear + // card A again) the error can be obscured by a delay + // in the update due to the use of the prefetch queue + // (e.g., clear card A, erroneously clear card A again, + // update card A that was pushed into the prefetch queue + // and thus delayed until after the erronous clear). The + // length of the delay is random depending on the objects + // in the queue and the delay can be zero. + claim_or_forward_internal_breadth(p); + } +} + +inline void PSPromotionManager::process_popped_location_depth(oop* p) { + if (is_oop_masked(p)) { + assert(PSChunkLargeArrays, "invariant"); + oop const old = unmask_chunked_array_oop(p); + process_array_chunk(old); + } else { + PSScavenge::copy_and_push_safe_barrier(this, p); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp new file mode 100644 index 00000000000..426337ddccb --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp @@ -0,0 +1,788 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +# include "incls/_precompiled.incl" +# include "incls/_psScavenge.cpp.incl" + +HeapWord* PSScavenge::_to_space_top_before_gc = NULL; +int PSScavenge::_consecutive_skipped_scavenges = 0; +ReferenceProcessor* PSScavenge::_ref_processor = NULL; +CardTableExtension* PSScavenge::_card_table = NULL; +bool PSScavenge::_survivor_overflow = false; +int PSScavenge::_tenuring_threshold = 0; +HeapWord* PSScavenge::_young_generation_boundary = NULL; +elapsedTimer PSScavenge::_accumulated_time; +GrowableArray* PSScavenge::_preserved_mark_stack = NULL; +GrowableArray* PSScavenge::_preserved_oop_stack = NULL; +CollectorCounters* PSScavenge::_counters = NULL; + +// Define before use +class PSIsAliveClosure: public BoolObjectClosure { +public: + void do_object(oop p) { + assert(false, "Do not call."); + } + bool do_object_b(oop p) { + return (!PSScavenge::is_obj_in_young((HeapWord*) p)) || p->is_forwarded(); + } +}; + +PSIsAliveClosure PSScavenge::_is_alive_closure; + +class PSKeepAliveClosure: public OopClosure { +protected: + MutableSpace* _to_space; + PSPromotionManager* _promotion_manager; + +public: + PSKeepAliveClosure(PSPromotionManager* pm) : _promotion_manager(pm) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + _to_space = heap->young_gen()->to_space(); + + assert(_promotion_manager != NULL, "Sanity"); + } + + void do_oop(oop* p) { + assert (*p != NULL, "expected non-null ref"); + assert ((*p)->is_oop(), "expected an oop while scanning weak refs"); + + oop obj = oop(*p); + // Weak refs may be visited more than once. + if (PSScavenge::should_scavenge(obj, _to_space)) { + PSScavenge::copy_and_push_safe_barrier(_promotion_manager, p); + } + } +}; + +class PSEvacuateFollowersClosure: public VoidClosure { + private: + PSPromotionManager* _promotion_manager; + public: + PSEvacuateFollowersClosure(PSPromotionManager* pm) : _promotion_manager(pm) {} + + void do_void() { + assert(_promotion_manager != NULL, "Sanity"); + _promotion_manager->drain_stacks(true); + guarantee(_promotion_manager->stacks_empty(), + "stacks should be empty at this point"); + } +}; + +class PSPromotionFailedClosure : public ObjectClosure { + virtual void do_object(oop obj) { + if (obj->is_forwarded()) { + obj->init_mark(); + } + } +}; + +class PSRefProcTaskProxy: public GCTask { + typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask; + ProcessTask & _rp_task; + uint _work_id; +public: + PSRefProcTaskProxy(ProcessTask & rp_task, uint work_id) + : _rp_task(rp_task), + _work_id(work_id) + { } + +private: + virtual char* name() { return (char *)"Process referents by policy in parallel"; } + virtual void do_it(GCTaskManager* manager, uint which); +}; + +void PSRefProcTaskProxy::do_it(GCTaskManager* manager, uint which) +{ + PSPromotionManager* promotion_manager = + PSPromotionManager::gc_thread_promotion_manager(which); + assert(promotion_manager != NULL, "sanity check"); + PSKeepAliveClosure keep_alive(promotion_manager); + PSEvacuateFollowersClosure evac_followers(promotion_manager); + PSIsAliveClosure is_alive; + _rp_task.work(_work_id, is_alive, keep_alive, evac_followers); +} + +class PSRefEnqueueTaskProxy: public GCTask { + typedef AbstractRefProcTaskExecutor::EnqueueTask EnqueueTask; + EnqueueTask& _enq_task; + uint _work_id; + +public: + PSRefEnqueueTaskProxy(EnqueueTask& enq_task, uint work_id) + : _enq_task(enq_task), + _work_id(work_id) + { } + + virtual char* name() { return (char *)"Enqueue reference objects in parallel"; } + virtual void do_it(GCTaskManager* manager, uint which) + { + _enq_task.work(_work_id); + } +}; + +class PSRefProcTaskExecutor: public AbstractRefProcTaskExecutor { + virtual void execute(ProcessTask& task); + virtual void execute(EnqueueTask& task); +}; + +void PSRefProcTaskExecutor::execute(ProcessTask& task) +{ + GCTaskQueue* q = GCTaskQueue::create(); + for(uint i=0; ienqueue(new PSRefProcTaskProxy(task, i)); + } + ParallelTaskTerminator terminator( + ParallelScavengeHeap::gc_task_manager()->workers(), + UseDepthFirstScavengeOrder ? + (TaskQueueSetSuper*) PSPromotionManager::stack_array_depth() + : (TaskQueueSetSuper*) PSPromotionManager::stack_array_breadth()); + if (task.marks_oops_alive() && ParallelGCThreads > 1) { + for (uint j=0; jenqueue(new StealTask(&terminator)); + } + } + ParallelScavengeHeap::gc_task_manager()->execute_and_wait(q); +} + + +void PSRefProcTaskExecutor::execute(EnqueueTask& task) +{ + GCTaskQueue* q = GCTaskQueue::create(); + for(uint i=0; ienqueue(new PSRefEnqueueTaskProxy(task, i)); + } + ParallelScavengeHeap::gc_task_manager()->execute_and_wait(q); +} + +// This method contains all heap specific policy for invoking scavenge. +// PSScavenge::invoke_no_policy() will do nothing but attempt to +// scavenge. It will not clean up after failed promotions, bail out if +// we've exceeded policy time limits, or any other special behavior. +// All such policy should be placed here. +// +// Note that this method should only be called from the vm_thread while +// at a safepoint! +void PSScavenge::invoke() +{ + assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); + assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread"); + assert(!Universe::heap()->is_gc_active(), "not reentrant"); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + PSAdaptiveSizePolicy* policy = heap->size_policy(); + + // Before each allocation/collection attempt, find out from the + // policy object if GCs are, on the whole, taking too long. If so, + // bail out without attempting a collection. + if (!policy->gc_time_limit_exceeded()) { + IsGCActiveMark mark; + + bool scavenge_was_done = PSScavenge::invoke_no_policy(); + + PSGCAdaptivePolicyCounters* counters = heap->gc_policy_counters(); + if (UsePerfData) + counters->update_full_follows_scavenge(0); + if (!scavenge_was_done || + policy->should_full_GC(heap->old_gen()->free_in_bytes())) { + if (UsePerfData) + counters->update_full_follows_scavenge(full_follows_scavenge); + + GCCauseSetter gccs(heap, GCCause::_adaptive_size_policy); + if (UseParallelOldGC) { + PSParallelCompact::invoke_no_policy(false); + } else { + PSMarkSweep::invoke_no_policy(false); + } + } + } +} + +// This method contains no policy. You should probably +// be calling invoke() instead. +bool PSScavenge::invoke_no_policy() { + assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); + assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread"); + + TimeStamp scavenge_entry; + TimeStamp scavenge_midpoint; + TimeStamp scavenge_exit; + + scavenge_entry.update(); + + if (GC_locker::check_active_before_gc()) { + return false; + } + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + GCCause::Cause gc_cause = heap->gc_cause(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + // Check for potential problems. + if (!should_attempt_scavenge()) { + return false; + } + + bool promotion_failure_occurred = false; + + PSYoungGen* young_gen = heap->young_gen(); + PSOldGen* old_gen = heap->old_gen(); + PSPermGen* perm_gen = heap->perm_gen(); + PSAdaptiveSizePolicy* size_policy = heap->size_policy(); + heap->increment_total_collections(); + + AdaptiveSizePolicyOutput(size_policy, heap->total_collections()); + + if ((gc_cause != GCCause::_java_lang_system_gc) || + UseAdaptiveSizePolicyWithSystemGC) { + // Gather the feedback data for eden occupancy. + young_gen->eden_space()->accumulate_statistics(); + } + + if (PrintHeapAtGC) { + Universe::print_heap_before_gc(); + } + + assert(!NeverTenure || _tenuring_threshold == markOopDesc::max_age + 1, "Sanity"); + assert(!AlwaysTenure || _tenuring_threshold == 0, "Sanity"); + + size_t prev_used = heap->used(); + assert(promotion_failed() == false, "Sanity"); + + // Fill in TLABs + heap->accumulate_statistics_all_tlabs(); + heap->ensure_parsability(true); // retire TLABs + + if (VerifyBeforeGC && heap->total_collections() >= VerifyGCStartAt) { + HandleMark hm; // Discard invalid handles created during verification + gclog_or_tty->print(" VerifyBeforeGC:"); + Universe::verify(true); + } + + { + ResourceMark rm; + HandleMark hm; + + gclog_or_tty->date_stamp(PrintGC && PrintGCDateStamps); + TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); + TraceTime t1("GC", PrintGC, !PrintGCDetails, gclog_or_tty); + TraceCollectorStats tcs(counters()); + TraceMemoryManagerStats tms(false /* not full GC */); + + if (TraceGen0Time) accumulated_time()->start(); + + // Let the size policy know we're starting + size_policy->minor_collection_begin(); + + // Verify the object start arrays. + if (VerifyObjectStartArray && + VerifyBeforeGC) { + old_gen->verify_object_start_array(); + perm_gen->verify_object_start_array(); + } + + // Verify no unmarked old->young roots + if (VerifyRememberedSets) { + CardTableExtension::verify_all_young_refs_imprecise(); + } + + if (!ScavengeWithObjectsInToSpace) { + assert(young_gen->to_space()->is_empty(), + "Attempt to scavenge with live objects in to_space"); + young_gen->to_space()->clear(); + } else if (ZapUnusedHeapArea) { + young_gen->to_space()->mangle_unused_area(); + } + save_to_space_top_before_gc(); + + NOT_PRODUCT(reference_processor()->verify_no_references_recorded()); + COMPILER2_PRESENT(DerivedPointerTable::clear()); + + reference_processor()->enable_discovery(); + + // We track how much was promoted to the next generation for + // the AdaptiveSizePolicy. + size_t old_gen_used_before = old_gen->used_in_bytes(); + + // For PrintGCDetails + size_t young_gen_used_before = young_gen->used_in_bytes(); + + // Reset our survivor overflow. + set_survivor_overflow(false); + + // We need to save the old/perm top values before + // creating the promotion_manager. We pass the top + // values to the card_table, to prevent it from + // straying into the promotion labs. + HeapWord* old_top = old_gen->object_space()->top(); + HeapWord* perm_top = perm_gen->object_space()->top(); + + // Release all previously held resources + gc_task_manager()->release_all_resources(); + + PSPromotionManager::pre_scavenge(); + + // We'll use the promotion manager again later. + PSPromotionManager* promotion_manager = PSPromotionManager::vm_thread_promotion_manager(); + { + // TraceTime("Roots"); + + GCTaskQueue* q = GCTaskQueue::create(); + + for(uint i=0; ienqueue(new OldToYoungRootsTask(old_gen, old_top, i)); + } + + q->enqueue(new SerialOldToYoungRootsTask(perm_gen, perm_top)); + + q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::universe)); + q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jni_handles)); + // We scan the thread roots in parallel + Threads::create_thread_roots_tasks(q); + q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::object_synchronizer)); + q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::flat_profiler)); + q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::management)); + q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::system_dictionary)); + q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jvmti)); + + ParallelTaskTerminator terminator( + gc_task_manager()->workers(), + promotion_manager->depth_first() ? + (TaskQueueSetSuper*) promotion_manager->stack_array_depth() + : (TaskQueueSetSuper*) promotion_manager->stack_array_breadth()); + if (ParallelGCThreads>1) { + for (uint j=0; jenqueue(new StealTask(&terminator)); + } + } + + gc_task_manager()->execute_and_wait(q); + } + + scavenge_midpoint.update(); + + // Process reference objects discovered during scavenge + { +#ifdef COMPILER2 + ReferencePolicy *soft_ref_policy = new LRUMaxHeapPolicy(); +#else + ReferencePolicy *soft_ref_policy = new LRUCurrentHeapPolicy(); +#endif // COMPILER2 + + PSKeepAliveClosure keep_alive(promotion_manager); + PSEvacuateFollowersClosure evac_followers(promotion_manager); + assert(soft_ref_policy != NULL,"No soft reference policy"); + if (reference_processor()->processing_is_mt()) { + PSRefProcTaskExecutor task_executor; + reference_processor()->process_discovered_references( + soft_ref_policy, &_is_alive_closure, &keep_alive, &evac_followers, + &task_executor); + } else { + reference_processor()->process_discovered_references( + soft_ref_policy, &_is_alive_closure, &keep_alive, &evac_followers, + NULL); + } + } + + // Enqueue reference objects discovered during scavenge. + if (reference_processor()->processing_is_mt()) { + PSRefProcTaskExecutor task_executor; + reference_processor()->enqueue_discovered_references(&task_executor); + } else { + reference_processor()->enqueue_discovered_references(NULL); + } + + // Finally, flush the promotion_manager's labs, and deallocate its stacks. + assert(promotion_manager->claimed_stack_empty(), "Sanity"); + PSPromotionManager::post_scavenge(); + + promotion_failure_occurred = promotion_failed(); + if (promotion_failure_occurred) { + clean_up_failed_promotion(); + if (PrintGC) { + gclog_or_tty->print("--"); + } + } + + // Let the size policy know we're done. Note that we count promotion + // failure cleanup time as part of the collection (otherwise, we're + // implicitly saying it's mutator time). + size_policy->minor_collection_end(gc_cause); + + if (!promotion_failure_occurred) { + // Swap the survivor spaces. + young_gen->eden_space()->clear(); + young_gen->from_space()->clear(); + young_gen->swap_spaces(); + + size_t survived = young_gen->from_space()->used_in_bytes(); + size_t promoted = old_gen->used_in_bytes() - old_gen_used_before; + size_policy->update_averages(_survivor_overflow, survived, promoted); + + if (UseAdaptiveSizePolicy) { + // Calculate the new survivor size and tenuring threshold + + if (PrintAdaptiveSizePolicy) { + gclog_or_tty->print("AdaptiveSizeStart: "); + gclog_or_tty->stamp(); + gclog_or_tty->print_cr(" collection: %d ", + heap->total_collections()); + + if (Verbose) { + gclog_or_tty->print("old_gen_capacity: %d young_gen_capacity: %d" + " perm_gen_capacity: %d ", + old_gen->capacity_in_bytes(), young_gen->capacity_in_bytes(), + perm_gen->capacity_in_bytes()); + } + } + + + if (UsePerfData) { + PSGCAdaptivePolicyCounters* counters = heap->gc_policy_counters(); + counters->update_old_eden_size( + size_policy->calculated_eden_size_in_bytes()); + counters->update_old_promo_size( + size_policy->calculated_promo_size_in_bytes()); + counters->update_old_capacity(old_gen->capacity_in_bytes()); + counters->update_young_capacity(young_gen->capacity_in_bytes()); + counters->update_survived(survived); + counters->update_promoted(promoted); + counters->update_survivor_overflowed(_survivor_overflow); + } + + size_t survivor_limit = + size_policy->max_survivor_size(young_gen->max_size()); + _tenuring_threshold = + size_policy->compute_survivor_space_size_and_threshold( + _survivor_overflow, + _tenuring_threshold, + survivor_limit); + + if (PrintTenuringDistribution) { + gclog_or_tty->cr(); + gclog_or_tty->print_cr("Desired survivor size %ld bytes, new threshold %d (max %d)", + size_policy->calculated_survivor_size_in_bytes(), + _tenuring_threshold, MaxTenuringThreshold); + } + + if (UsePerfData) { + PSGCAdaptivePolicyCounters* counters = heap->gc_policy_counters(); + counters->update_tenuring_threshold(_tenuring_threshold); + counters->update_survivor_size_counters(); + } + + // Do call at minor collections? + // Don't check if the size_policy is ready at this + // level. Let the size_policy check that internally. + if (UseAdaptiveSizePolicy && + UseAdaptiveGenerationSizePolicyAtMinorCollection && + ((gc_cause != GCCause::_java_lang_system_gc) || + UseAdaptiveSizePolicyWithSystemGC)) { + + // Calculate optimial free space amounts + assert(young_gen->max_size() > + young_gen->from_space()->capacity_in_bytes() + + young_gen->to_space()->capacity_in_bytes(), + "Sizes of space in young gen are out-of-bounds"); + size_t max_eden_size = young_gen->max_size() - + young_gen->from_space()->capacity_in_bytes() - + young_gen->to_space()->capacity_in_bytes(); + size_policy->compute_generation_free_space(young_gen->used_in_bytes(), + young_gen->eden_space()->used_in_bytes(), + old_gen->used_in_bytes(), + perm_gen->used_in_bytes(), + young_gen->eden_space()->capacity_in_bytes(), + old_gen->max_gen_size(), + max_eden_size, + false /* full gc*/, + gc_cause); + + } + // Resize the young generation at every collection + // even if new sizes have not been calculated. This is + // to allow resizes that may have been inhibited by the + // relative location of the "to" and "from" spaces. + + // Resizing the old gen at minor collects can cause increases + // that don't feed back to the generation sizing policy until + // a major collection. Don't resize the old gen here. + + heap->resize_young_gen(size_policy->calculated_eden_size_in_bytes(), + size_policy->calculated_survivor_size_in_bytes()); + + if (PrintAdaptiveSizePolicy) { + gclog_or_tty->print_cr("AdaptiveSizeStop: collection: %d ", + heap->total_collections()); + } + } + + // Update the structure of the eden. With NUMA-eden CPU hotplugging or offlining can + // cause the change of the heap layout. Make sure eden is reshaped if that's the case. + // Also update() will case adaptive NUMA chunk resizing. + assert(young_gen->eden_space()->is_empty(), "eden space should be empty now"); + young_gen->eden_space()->update(); + + heap->gc_policy_counters()->update_counters(); + + heap->resize_all_tlabs(); + + assert(young_gen->to_space()->is_empty(), "to space should be empty now"); + } + + COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); + + NOT_PRODUCT(reference_processor()->verify_no_references_recorded()); + + // Re-verify object start arrays + if (VerifyObjectStartArray && + VerifyAfterGC) { + old_gen->verify_object_start_array(); + perm_gen->verify_object_start_array(); + } + + // Verify all old -> young cards are now precise + if (VerifyRememberedSets) { + // Precise verification will give false positives. Until this is fixed, + // use imprecise verification. + // CardTableExtension::verify_all_young_refs_precise(); + CardTableExtension::verify_all_young_refs_imprecise(); + } + + if (TraceGen0Time) accumulated_time()->stop(); + + if (PrintGC) { + if (PrintGCDetails) { + // Don't print a GC timestamp here. This is after the GC so + // would be confusing. + young_gen->print_used_change(young_gen_used_before); + } + heap->print_heap_change(prev_used); + } + + // Track memory usage and detect low memory + MemoryService::track_memory_usage(); + heap->update_counters(); + } + + if (VerifyAfterGC && heap->total_collections() >= VerifyGCStartAt) { + HandleMark hm; // Discard invalid handles created during verification + gclog_or_tty->print(" VerifyAfterGC:"); + Universe::verify(false); + } + + if (PrintHeapAtGC) { + Universe::print_heap_after_gc(); + } + + scavenge_exit.update(); + + if (PrintGCTaskTimeStamps) { + tty->print_cr("VM-Thread " INT64_FORMAT " " INT64_FORMAT " " INT64_FORMAT, + scavenge_entry.ticks(), scavenge_midpoint.ticks(), + scavenge_exit.ticks()); + gc_task_manager()->print_task_time_stamps(); + } + + return !promotion_failure_occurred; +} + +// This method iterates over all objects in the young generation, +// unforwarding markOops. It then restores any preserved mark oops, +// and clears the _preserved_mark_stack. +void PSScavenge::clean_up_failed_promotion() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + assert(promotion_failed(), "Sanity"); + + PSYoungGen* young_gen = heap->young_gen(); + + { + ResourceMark rm; + + // Unforward all pointers in the young gen. + PSPromotionFailedClosure unforward_closure; + young_gen->object_iterate(&unforward_closure); + + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("Restoring %d marks", + _preserved_oop_stack->length()); + } + + // Restore any saved marks. + for (int i=0; i < _preserved_oop_stack->length(); i++) { + oop obj = _preserved_oop_stack->at(i); + markOop mark = _preserved_mark_stack->at(i); + obj->set_mark(mark); + } + + // Deallocate the preserved mark and oop stacks. + // The stacks were allocated as CHeap objects, so + // we must call delete to prevent mem leaks. + delete _preserved_mark_stack; + _preserved_mark_stack = NULL; + delete _preserved_oop_stack; + _preserved_oop_stack = NULL; + } + + // Reset the PromotionFailureALot counters. + NOT_PRODUCT(Universe::heap()->reset_promotion_should_fail();) +} + +// This method is called whenever an attempt to promote an object +// fails. Some markOops will need preserving, some will not. Note +// that the entire eden is traversed after a failed promotion, with +// all forwarded headers replaced by the default markOop. This means +// it is not neccessary to preserve most markOops. +void PSScavenge::oop_promotion_failed(oop obj, markOop obj_mark) { + if (_preserved_mark_stack == NULL) { + ThreadCritical tc; // Lock and retest + if (_preserved_mark_stack == NULL) { + assert(_preserved_oop_stack == NULL, "Sanity"); + _preserved_mark_stack = new (ResourceObj::C_HEAP) GrowableArray(40, true); + _preserved_oop_stack = new (ResourceObj::C_HEAP) GrowableArray(40, true); + } + } + + // Because we must hold the ThreadCritical lock before using + // the stacks, we should be safe from observing partial allocations, + // which are also guarded by the ThreadCritical lock. + if (obj_mark->must_be_preserved_for_promotion_failure(obj)) { + ThreadCritical tc; + _preserved_oop_stack->push(obj); + _preserved_mark_stack->push(obj_mark); + } +} + +bool PSScavenge::should_attempt_scavenge() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + PSGCAdaptivePolicyCounters* counters = heap->gc_policy_counters(); + + if (UsePerfData) { + counters->update_scavenge_skipped(not_skipped); + } + + PSYoungGen* young_gen = heap->young_gen(); + PSOldGen* old_gen = heap->old_gen(); + + if (!ScavengeWithObjectsInToSpace) { + // Do not attempt to promote unless to_space is empty + if (!young_gen->to_space()->is_empty()) { + _consecutive_skipped_scavenges++; + if (UsePerfData) { + counters->update_scavenge_skipped(to_space_not_empty); + } + return false; + } + } + + // Test to see if the scavenge will likely fail. + PSAdaptiveSizePolicy* policy = heap->size_policy(); + + // A similar test is done in the policy's should_full_GC(). If this is + // changed, decide if that test should also be changed. + size_t avg_promoted = (size_t) policy->padded_average_promoted_in_bytes(); + size_t promotion_estimate = MIN2(avg_promoted, young_gen->used_in_bytes()); + bool result = promotion_estimate < old_gen->free_in_bytes(); + + if (PrintGCDetails && Verbose) { + gclog_or_tty->print(result ? " do scavenge: " : " skip scavenge: "); + gclog_or_tty->print_cr(" average_promoted " SIZE_FORMAT + " padded_average_promoted " SIZE_FORMAT + " free in old gen " SIZE_FORMAT, + (size_t) policy->average_promoted_in_bytes(), + (size_t) policy->padded_average_promoted_in_bytes(), + old_gen->free_in_bytes()); + if (young_gen->used_in_bytes() < + (size_t) policy->padded_average_promoted_in_bytes()) { + gclog_or_tty->print_cr(" padded_promoted_average is greater" + " than maximum promotion = " SIZE_FORMAT, young_gen->used_in_bytes()); + } + } + + if (result) { + _consecutive_skipped_scavenges = 0; + } else { + _consecutive_skipped_scavenges++; + if (UsePerfData) { + counters->update_scavenge_skipped(promoted_too_large); + } + } + return result; +} + + // Used to add tasks +GCTaskManager* const PSScavenge::gc_task_manager() { + assert(ParallelScavengeHeap::gc_task_manager() != NULL, + "shouldn't return NULL"); + return ParallelScavengeHeap::gc_task_manager(); +} + +void PSScavenge::initialize() { + // Arguments must have been parsed + + if (AlwaysTenure) { + _tenuring_threshold = 0; + } else if (NeverTenure) { + _tenuring_threshold = markOopDesc::max_age + 1; + } else { + // We want to smooth out our startup times for the AdaptiveSizePolicy + _tenuring_threshold = (UseAdaptiveSizePolicy) ? InitialTenuringThreshold : + MaxTenuringThreshold; + } + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + PSYoungGen* young_gen = heap->young_gen(); + PSOldGen* old_gen = heap->old_gen(); + PSPermGen* perm_gen = heap->perm_gen(); + + // Set boundary between young_gen and old_gen + assert(perm_gen->reserved().end() <= old_gen->object_space()->bottom(), + "perm above old"); + assert(old_gen->reserved().end() <= young_gen->eden_space()->bottom(), + "old above young"); + _young_generation_boundary = young_gen->eden_space()->bottom(); + + // Initialize ref handling object for scavenging. + MemRegion mr = young_gen->reserved(); + _ref_processor = ReferenceProcessor::create_ref_processor( + mr, // span + true, // atomic_discovery + true, // mt_discovery + NULL, // is_alive_non_header + ParallelGCThreads, + ParallelRefProcEnabled); + + // Cache the cardtable + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); + _card_table = (CardTableExtension*)bs; + + _counters = new CollectorCounters("PSScavenge", 0); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.hpp new file mode 100644 index 00000000000..ff20b5bf63c --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.hpp @@ -0,0 +1,137 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class GCTaskManager; +class GCTaskQueue; +class OopStack; +class ReferenceProcessor; +class ParallelScavengeHeap; +class PSIsAliveClosure; +class PSRefProcTaskExecutor; + +class PSScavenge: AllStatic { + friend class PSIsAliveClosure; + friend class PSKeepAliveClosure; + friend class PSPromotionManager; + + enum ScavengeSkippedCause { + not_skipped = 0, + to_space_not_empty, + promoted_too_large, + full_follows_scavenge + }; + + // Saved value of to_space->top(), used to prevent objects in to_space from + // being rescanned. + static HeapWord* _to_space_top_before_gc; + + // Number of consecutive attempts to scavenge that were skipped + static int _consecutive_skipped_scavenges; + + + protected: + // Flags/counters + static ReferenceProcessor* _ref_processor; // Reference processor for scavenging. + static PSIsAliveClosure _is_alive_closure; // Closure used for reference processing + static CardTableExtension* _card_table; // We cache the card table for fast access. + static bool _survivor_overflow; // Overflow this collection + static int _tenuring_threshold; // tenuring threshold for next scavenge + static elapsedTimer _accumulated_time; // total time spent on scavenge + static HeapWord* _young_generation_boundary; // The lowest address possible for the young_gen. + // This is used to decide if an oop should be scavenged, + // cards should be marked, etc. + static GrowableArray* _preserved_mark_stack; // List of marks to be restored after failed promotion + static GrowableArray* _preserved_oop_stack; // List of oops that need their mark restored. + static CollectorCounters* _counters; // collector performance counters + + static void clean_up_failed_promotion(); + + static bool should_attempt_scavenge(); + + static HeapWord* to_space_top_before_gc() { return _to_space_top_before_gc; } + static inline void save_to_space_top_before_gc(); + + // Private accessors + static CardTableExtension* const card_table() { assert(_card_table != NULL, "Sanity"); return _card_table; } + + public: + // Accessors + static int tenuring_threshold() { return _tenuring_threshold; } + static elapsedTimer* accumulated_time() { return &_accumulated_time; } + static bool promotion_failed() + { return _preserved_mark_stack != NULL; } + static int consecutive_skipped_scavenges() + { return _consecutive_skipped_scavenges; } + + // Performance Counters + static CollectorCounters* counters() { return _counters; } + + // Used by scavenge_contents && psMarkSweep + static ReferenceProcessor* const reference_processor() { + assert(_ref_processor != NULL, "Sanity"); + return _ref_processor; + } + // Used to add tasks + static GCTaskManager* const gc_task_manager(); + // The promotion managers tell us if they encountered overflow + static void set_survivor_overflow(bool state) { + _survivor_overflow = state; + } + // Adaptive size policy support. When the young generation/old generation + // boundary moves, _young_generation_boundary must be reset + static void set_young_generation_boundary(HeapWord* v) { + _young_generation_boundary = v; + } + + // Called by parallelScavengeHeap to init the tenuring threshold + static void initialize(); + + // Scavenge entry point + static void invoke(); + // Return true is a collection was done. Return + // false if the collection was skipped. + static bool invoke_no_policy(); + + // If an attempt to promote fails, this method is invoked + static void oop_promotion_failed(oop obj, markOop obj_mark); + + static inline bool should_scavenge(oop p); + + // These call should_scavenge() above and, if it returns true, also check that + // the object was not newly copied into to_space. The version with the bool + // argument is a convenience wrapper that fetches the to_space pointer from + // the heap and calls the other version (if the arg is true). + static inline bool should_scavenge(oop p, MutableSpace* to_space); + static inline bool should_scavenge(oop p, bool check_to_space); + + inline static void copy_and_push_safe_barrier(PSPromotionManager* pm, oop* p); + + // Is an object in the young generation + // This assumes that the HeapWord argument is in the heap, + // so it only checks one side of the complete predicate. + inline static bool is_obj_in_young(HeapWord* o) { + const bool result = (o >= _young_generation_boundary); + return result; + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp new file mode 100644 index 00000000000..ea61dc8f582 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +inline void PSScavenge::save_to_space_top_before_gc() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + _to_space_top_before_gc = heap->young_gen()->to_space()->top(); +} + +inline bool PSScavenge::should_scavenge(oop p) { + return p == NULL ? false : PSScavenge::is_obj_in_young((HeapWord*) p); +} + +inline bool PSScavenge::should_scavenge(oop p, MutableSpace* to_space) { + if (should_scavenge(p)) { + // Skip objects copied to to_space since the scavenge started. + HeapWord* const addr = (HeapWord*) p; + return addr < to_space_top_before_gc() || addr >= to_space->end(); + } + return false; +} + +inline bool PSScavenge::should_scavenge(oop p, bool check_to_space) { + if (check_to_space) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*) Universe::heap(); + return should_scavenge(p, heap->young_gen()->to_space()); + } + return should_scavenge(p); +} + +// Attempt to "claim" oop at p via CAS, push the new obj if successful +// This version tests the oop* to make sure it is within the heap before +// attempting marking. +inline void PSScavenge::copy_and_push_safe_barrier(PSPromotionManager* pm, + oop* p) { + assert(should_scavenge(*p, true), "revisiting object?"); + + oop o = *p; + if (o->is_forwarded()) { + *p = o->forwardee(); + } else { + *p = pm->copy_to_survivor_space(o, pm->depth_first()); + } + + // We cannot mark without test, as some code passes us pointers + // that are outside the heap. + if ((!PSScavenge::is_obj_in_young((HeapWord*) p)) && + Universe::heap()->is_in_reserved(p)) { + o = *p; + if (PSScavenge::is_obj_in_young((HeapWord*) o)) { + card_table()->inline_write_ref_field_gc(p, o); + } + } +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp new file mode 100644 index 00000000000..2e433358645 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp @@ -0,0 +1,221 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_psTasks.cpp.incl" + +// +// ScavengeRootsTask +// + +// Define before use +class PSScavengeRootsClosure: public OopClosure { + private: + PSPromotionManager* _promotion_manager; + + public: + PSScavengeRootsClosure(PSPromotionManager* pm) : _promotion_manager(pm) { } + + virtual void do_oop(oop* p) { + if (PSScavenge::should_scavenge(*p)) { + // We never card mark roots, maybe call a func without test? + PSScavenge::copy_and_push_safe_barrier(_promotion_manager, p); + } + } +}; + +void ScavengeRootsTask::do_it(GCTaskManager* manager, uint which) { + assert(Universe::heap()->is_gc_active(), "called outside gc"); + + PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(which); + PSScavengeRootsClosure roots_closure(pm); + + switch (_root_type) { + case universe: + Universe::oops_do(&roots_closure); + ReferenceProcessor::oops_do(&roots_closure); + break; + + case jni_handles: + JNIHandles::oops_do(&roots_closure); + break; + + case threads: + { + ResourceMark rm; + Threads::oops_do(&roots_closure); + } + break; + + case object_synchronizer: + ObjectSynchronizer::oops_do(&roots_closure); + break; + + case flat_profiler: + FlatProfiler::oops_do(&roots_closure); + break; + + case system_dictionary: + SystemDictionary::oops_do(&roots_closure); + break; + + case management: + Management::oops_do(&roots_closure); + break; + + case jvmti: + JvmtiExport::oops_do(&roots_closure); + break; + + default: + fatal("Unknown root type"); + } + + // Do the real work + pm->drain_stacks(false); +} + +// +// ThreadRootsTask +// + +void ThreadRootsTask::do_it(GCTaskManager* manager, uint which) { + assert(Universe::heap()->is_gc_active(), "called outside gc"); + + PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(which); + PSScavengeRootsClosure roots_closure(pm); + + if (_java_thread != NULL) + _java_thread->oops_do(&roots_closure); + + if (_vm_thread != NULL) + _vm_thread->oops_do(&roots_closure); + + // Do the real work + pm->drain_stacks(false); +} + +// +// StealTask +// + +StealTask::StealTask(ParallelTaskTerminator* t) : + _terminator(t) {} + +void StealTask::do_it(GCTaskManager* manager, uint which) { + assert(Universe::heap()->is_gc_active(), "called outside gc"); + + PSPromotionManager* pm = + PSPromotionManager::gc_thread_promotion_manager(which); + pm->drain_stacks(true); + guarantee(pm->stacks_empty(), + "stacks should be empty at this point"); + + int random_seed = 17; + if (pm->depth_first()) { + while(true) { + oop* p; + if (PSPromotionManager::steal_depth(which, &random_seed, p)) { +#if PS_PM_STATS + pm->increment_steals(p); +#endif // PS_PM_STATS + pm->process_popped_location_depth(p); + pm->drain_stacks_depth(true); + } else { + if (terminator()->offer_termination()) { + break; + } + } + } + } else { + while(true) { + oop obj; + if (PSPromotionManager::steal_breadth(which, &random_seed, obj)) { +#if PS_PM_STATS + pm->increment_steals(); +#endif // PS_PM_STATS + obj->copy_contents(pm); + pm->drain_stacks_breadth(true); + } else { + if (terminator()->offer_termination()) { + break; + } + } + } + } + guarantee(pm->stacks_empty(), + "stacks should be empty at this point"); +} + +// +// SerialOldToYoungRootsTask +// + +void SerialOldToYoungRootsTask::do_it(GCTaskManager* manager, uint which) { + assert(_gen != NULL, "Sanity"); + assert(_gen->object_space()->contains(_gen_top) || _gen_top == _gen->object_space()->top(), "Sanity"); + + { + PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(which); + + assert(Universe::heap()->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + CardTableExtension* card_table = (CardTableExtension *)Universe::heap()->barrier_set(); + // FIX ME! Assert that card_table is the type we believe it to be. + + card_table->scavenge_contents(_gen->start_array(), + _gen->object_space(), + _gen_top, + pm); + + // Do the real work + pm->drain_stacks(false); + } +} + +// +// OldToYoungRootsTask +// + +void OldToYoungRootsTask::do_it(GCTaskManager* manager, uint which) { + assert(_gen != NULL, "Sanity"); + assert(_gen->object_space()->contains(_gen_top) || _gen_top == _gen->object_space()->top(), "Sanity"); + assert(_stripe_number < ParallelGCThreads, "Sanity"); + + { + PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(which); + + assert(Universe::heap()->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + CardTableExtension* card_table = (CardTableExtension *)Universe::heap()->barrier_set(); + // FIX ME! Assert that card_table is the type we believe it to be. + + card_table->scavenge_contents_parallel(_gen->start_array(), + _gen->object_space(), + _gen_top, + pm, + _stripe_number); + + // Do the real work + pm->drain_stacks(false); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.hpp new file mode 100644 index 00000000000..e4a3dedc251 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.hpp @@ -0,0 +1,145 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// psTasks.hpp is a collection of GCTasks used by the +// parallelScavenge collector. +// + +class GCTask; +class OopClosure; +class OopStack; +class ObjectStartArray; +class ParallelTaskTerminator; +class MutableSpace; +class PSOldGen; +class Thread; +class VMThread; + +// +// ScavengeRootsTask +// +// This task scans all the roots of a given type. +// +// + +class ScavengeRootsTask : public GCTask { + public: + enum RootType { + universe = 1, + jni_handles = 2, + threads = 3, + object_synchronizer = 4, + flat_profiler = 5, + system_dictionary = 6, + management = 7, + jvmti = 8 + }; + private: + RootType _root_type; + public: + ScavengeRootsTask(RootType value) : _root_type(value) {} + + char* name() { return (char *)"scavenge-roots-task"; } + + virtual void do_it(GCTaskManager* manager, uint which); +}; + +// +// ThreadRootsTask +// +// This task scans the roots of a single thread. This task +// enables scanning of thread roots in parallel. +// + +class ThreadRootsTask : public GCTask { + private: + JavaThread* _java_thread; + VMThread* _vm_thread; + public: + ThreadRootsTask(JavaThread* root) : _java_thread(root), _vm_thread(NULL) {} + ThreadRootsTask(VMThread* root) : _java_thread(NULL), _vm_thread(root) {} + + char* name() { return (char *)"thread-roots-task"; } + + virtual void do_it(GCTaskManager* manager, uint which); +}; + +// +// StealTask +// +// This task is used to distribute work to idle threads. +// + +class StealTask : public GCTask { + private: + ParallelTaskTerminator* const _terminator; + public: + char* name() { return (char *)"steal-task"; } + + StealTask(ParallelTaskTerminator* t); + + ParallelTaskTerminator* terminator() { return _terminator; } + + virtual void do_it(GCTaskManager* manager, uint which); +}; + +// +// SerialOldToYoungRootsTask +// +// This task is used to scan for roots in the perm gen + +class SerialOldToYoungRootsTask : public GCTask { + private: + PSOldGen* _gen; + HeapWord* _gen_top; + + public: + SerialOldToYoungRootsTask(PSOldGen *gen, HeapWord* gen_top) : + _gen(gen), _gen_top(gen_top) { } + + char* name() { return (char *)"serial-old-to-young-roots-task"; } + + virtual void do_it(GCTaskManager* manager, uint which); +}; + +// +// OldToYoungRootsTask +// +// This task is used to scan old to young roots in parallel + +class OldToYoungRootsTask : public GCTask { + private: + PSOldGen* _gen; + HeapWord* _gen_top; + uint _stripe_number; + + public: + OldToYoungRootsTask(PSOldGen *gen, HeapWord* gen_top, uint stripe_number) : + _gen(gen), _gen_top(gen_top), _stripe_number(stripe_number) { } + + char* name() { return (char *)"old-to-young-roots-task"; } + + virtual void do_it(GCTaskManager* manager, uint which); +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp new file mode 100644 index 00000000000..912f5414cc9 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp @@ -0,0 +1,371 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_psVirtualspace.cpp.incl" + +// PSVirtualSpace + +PSVirtualSpace::PSVirtualSpace(ReservedSpace rs, size_t alignment) : + _alignment(alignment) +{ + set_reserved(rs); + set_committed(reserved_low_addr(), reserved_low_addr()); + DEBUG_ONLY(verify()); +} + +PSVirtualSpace::PSVirtualSpace(ReservedSpace rs) : + _alignment(os::vm_page_size()) +{ + set_reserved(rs); + set_committed(reserved_low_addr(), reserved_low_addr()); + DEBUG_ONLY(verify()); +} + +// Deprecated. +PSVirtualSpace::PSVirtualSpace(): _alignment(os::vm_page_size()) { +} + +// Deprecated. +bool PSVirtualSpace::initialize(ReservedSpace rs, + size_t commit_size) { + set_reserved(rs); + set_committed(reserved_low_addr(), reserved_low_addr()); + + // Commit to initial size. + assert(commit_size <= rs.size(), "commit_size too big"); + bool result = commit_size > 0 ? expand_by(commit_size) : true; + DEBUG_ONLY(verify()); + return result; +} + +PSVirtualSpace::~PSVirtualSpace() { + release(); +} + +bool PSVirtualSpace::contains(void* p) const { + char* const cp = (char*)p; + return cp >= committed_low_addr() && cp < committed_high_addr(); +} + +void PSVirtualSpace::release() { + DEBUG_ONLY(PSVirtualSpaceVerifier this_verifier(this)); + if (reserved_low_addr() != NULL) { + if (special()) { + os::release_memory_special(reserved_low_addr(), reserved_size()); + } else { + (void)os::release_memory(reserved_low_addr(), reserved_size()); + } + } + _reserved_low_addr = _reserved_high_addr = NULL; + _committed_low_addr = _committed_high_addr = NULL; + _special = false; +} + +bool PSVirtualSpace::expand_by(size_t bytes, bool pre_touch) { + assert(is_aligned(bytes), "arg not aligned"); + DEBUG_ONLY(PSVirtualSpaceVerifier this_verifier(this)); + + if (uncommitted_size() < bytes) { + return false; + } + + char* const base_addr = committed_high_addr(); + bool result = special() || os::commit_memory(base_addr, bytes, alignment()); + if (result) { + _committed_high_addr += bytes; + } + + if (pre_touch || AlwaysPreTouch) { + for (char* curr = base_addr; + curr < _committed_high_addr; + curr += os::vm_page_size()) { + char tmp = *curr; + *curr = 0; + } + } + + return result; +} + +bool PSVirtualSpace::shrink_by(size_t bytes) { + assert(is_aligned(bytes), "arg not aligned"); + DEBUG_ONLY(PSVirtualSpaceVerifier this_verifier(this)); + + if (committed_size() < bytes) { + return false; + } + + char* const base_addr = committed_high_addr() - bytes; + bool result = special() || os::uncommit_memory(base_addr, bytes); + if (result) { + _committed_high_addr -= bytes; + } + + return result; +} + +size_t +PSVirtualSpace::expand_into(PSVirtualSpace* other_space, size_t bytes) { + assert(is_aligned(bytes), "arg not aligned"); + assert(grows_up(), "this space must grow up"); + assert(other_space->grows_down(), "other space must grow down"); + assert(reserved_high_addr() == other_space->reserved_low_addr(), + "spaces not contiguous"); + assert(special() == other_space->special(), "one space is special, the other is not"); + DEBUG_ONLY(PSVirtualSpaceVerifier this_verifier(this)); + DEBUG_ONLY(PSVirtualSpaceVerifier other_verifier(other_space)); + + size_t bytes_needed = bytes; + + // First use the uncommitted region in this space. + size_t tmp_bytes = MIN2(uncommitted_size(), bytes_needed); + if (tmp_bytes > 0) { + if (expand_by(tmp_bytes)) { + bytes_needed -= tmp_bytes; + } else { + return 0; + } + } + + // Next take from the uncommitted region in the other space, and commit it. + tmp_bytes = MIN2(other_space->uncommitted_size(), bytes_needed); + if (tmp_bytes > 0) { + char* const commit_base = committed_high_addr(); + if (other_space->special() || + os::commit_memory(commit_base, tmp_bytes, alignment())) { + // Reduce the reserved region in the other space. + other_space->set_reserved(other_space->reserved_low_addr() + tmp_bytes, + other_space->reserved_high_addr(), + other_space->special()); + + // Grow both reserved and committed in this space. + _reserved_high_addr += tmp_bytes; + _committed_high_addr += tmp_bytes; + bytes_needed -= tmp_bytes; + } else { + return bytes - bytes_needed; + } + } + + // Finally take from the already committed region in the other space. + tmp_bytes = bytes_needed; + if (tmp_bytes > 0) { + // Reduce both committed and reserved in the other space. + other_space->set_committed(other_space->committed_low_addr() + tmp_bytes, + other_space->committed_high_addr()); + other_space->set_reserved(other_space->reserved_low_addr() + tmp_bytes, + other_space->reserved_high_addr(), + other_space->special()); + + // Grow both reserved and committed in this space. + _reserved_high_addr += tmp_bytes; + _committed_high_addr += tmp_bytes; + } + + return bytes; +} + +#ifndef PRODUCT +bool PSVirtualSpace::is_aligned(size_t value, size_t align) { + const size_t tmp_value = value + align - 1; + const size_t mask = ~(align - 1); + return (tmp_value & mask) == value; +} + +bool PSVirtualSpace::is_aligned(size_t value) const { + return is_aligned(value, alignment()); +} + +bool PSVirtualSpace::is_aligned(char* value) const { + return is_aligned((size_t)value); +} + +void PSVirtualSpace::verify() const { + assert(is_aligned(alignment(), os::vm_page_size()), "bad alignment"); + assert(is_aligned(reserved_low_addr()), "bad reserved_low_addr"); + assert(is_aligned(reserved_high_addr()), "bad reserved_high_addr"); + assert(is_aligned(committed_low_addr()), "bad committed_low_addr"); + assert(is_aligned(committed_high_addr()), "bad committed_high_addr"); + + // Reserved region must be non-empty or both addrs must be 0. + assert(reserved_low_addr() < reserved_high_addr() || + reserved_low_addr() == NULL && reserved_high_addr() == NULL, + "bad reserved addrs"); + assert(committed_low_addr() <= committed_high_addr(), "bad committed addrs"); + + if (grows_up()) { + assert(reserved_low_addr() == committed_low_addr(), "bad low addrs"); + assert(reserved_high_addr() >= committed_high_addr(), "bad high addrs"); + } else { + assert(reserved_high_addr() == committed_high_addr(), "bad high addrs"); + assert(reserved_low_addr() <= committed_low_addr(), "bad low addrs"); + } +} + +void PSVirtualSpace::print() const { + gclog_or_tty->print_cr("virtual space [" PTR_FORMAT "]: alignment=" + SIZE_FORMAT "K grows %s%s", + this, alignment() / K, grows_up() ? "up" : "down", + special() ? " (pinned in memory)" : ""); + gclog_or_tty->print_cr(" reserved=" SIZE_FORMAT "K" + " [" PTR_FORMAT "," PTR_FORMAT "]" + " committed=" SIZE_FORMAT "K" + " [" PTR_FORMAT "," PTR_FORMAT "]", + reserved_size() / K, + reserved_low_addr(), reserved_high_addr(), + committed_size() / K, + committed_low_addr(), committed_high_addr()); +} +#endif // #ifndef PRODUCT + +void PSVirtualSpace::print_space_boundaries_on(outputStream* st) const { + st->print_cr(" [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ")", + low_boundary(), high(), high_boundary()); +} + +PSVirtualSpaceHighToLow::PSVirtualSpaceHighToLow(ReservedSpace rs, + size_t alignment) : + PSVirtualSpace(alignment) +{ + set_reserved(rs); + set_committed(reserved_high_addr(), reserved_high_addr()); + DEBUG_ONLY(verify()); +} + +PSVirtualSpaceHighToLow::PSVirtualSpaceHighToLow(ReservedSpace rs) { + set_reserved(rs); + set_committed(reserved_high_addr(), reserved_high_addr()); + DEBUG_ONLY(verify()); +} + +bool PSVirtualSpaceHighToLow::expand_by(size_t bytes, bool pre_touch) { + assert(is_aligned(bytes), "arg not aligned"); + DEBUG_ONLY(PSVirtualSpaceVerifier this_verifier(this)); + + if (uncommitted_size() < bytes) { + return false; + } + + char* const base_addr = committed_low_addr() - bytes; + bool result = special() || os::commit_memory(base_addr, bytes, alignment()); + if (result) { + _committed_low_addr -= bytes; + } + + if (pre_touch || AlwaysPreTouch) { + for (char* curr = base_addr; + curr < _committed_high_addr; + curr += os::vm_page_size()) { + char tmp = *curr; + *curr = 0; + } + } + + return result; +} + +bool PSVirtualSpaceHighToLow::shrink_by(size_t bytes) { + assert(is_aligned(bytes), "arg not aligned"); + DEBUG_ONLY(PSVirtualSpaceVerifier this_verifier(this)); + + if (committed_size() < bytes) { + return false; + } + + char* const base_addr = committed_low_addr(); + bool result = special() || os::uncommit_memory(base_addr, bytes); + if (result) { + _committed_low_addr += bytes; + } + + return result; +} + +size_t PSVirtualSpaceHighToLow::expand_into(PSVirtualSpace* other_space, + size_t bytes) { + assert(is_aligned(bytes), "arg not aligned"); + assert(grows_down(), "this space must grow down"); + assert(other_space->grows_up(), "other space must grow up"); + assert(reserved_low_addr() == other_space->reserved_high_addr(), + "spaces not contiguous"); + assert(special() == other_space->special(), "one space is special in memory, the other is not"); + DEBUG_ONLY(PSVirtualSpaceVerifier this_verifier(this)); + DEBUG_ONLY(PSVirtualSpaceVerifier other_verifier(other_space)); + + size_t bytes_needed = bytes; + + // First use the uncommitted region in this space. + size_t tmp_bytes = MIN2(uncommitted_size(), bytes_needed); + if (tmp_bytes > 0) { + if (expand_by(tmp_bytes)) { + bytes_needed -= tmp_bytes; + } else { + return 0; + } + } + + // Next take from the uncommitted region in the other space, and commit it. + tmp_bytes = MIN2(other_space->uncommitted_size(), bytes_needed); + if (tmp_bytes > 0) { + char* const commit_base = committed_low_addr() - tmp_bytes; + if (other_space->special() || + os::commit_memory(commit_base, tmp_bytes, alignment())) { + // Reduce the reserved region in the other space. + other_space->set_reserved(other_space->reserved_low_addr(), + other_space->reserved_high_addr() - tmp_bytes, + other_space->special()); + + // Grow both reserved and committed in this space. + _reserved_low_addr -= tmp_bytes; + _committed_low_addr -= tmp_bytes; + bytes_needed -= tmp_bytes; + } else { + return bytes - bytes_needed; + } + } + + // Finally take from the already committed region in the other space. + tmp_bytes = bytes_needed; + if (tmp_bytes > 0) { + // Reduce both committed and reserved in the other space. + other_space->set_committed(other_space->committed_low_addr(), + other_space->committed_high_addr() - tmp_bytes); + other_space->set_reserved(other_space->reserved_low_addr(), + other_space->reserved_high_addr() - tmp_bytes, + other_space->special()); + + // Grow both reserved and committed in this space. + _reserved_low_addr -= tmp_bytes; + _committed_low_addr -= tmp_bytes; + } + + return bytes; +} + +void +PSVirtualSpaceHighToLow::print_space_boundaries_on(outputStream* st) const { + st->print_cr(" (" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT "]", + high_boundary(), low(), low_boundary()); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.hpp new file mode 100644 index 00000000000..8e1e03679e6 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.hpp @@ -0,0 +1,175 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// VirtualSpace for the parallel scavenge collector. +// +// VirtualSpace is data structure for committing a previously reserved address +// range in smaller chunks. + +class PSVirtualSpace : public CHeapObj { + friend class VMStructs; + protected: + // The space is committed/uncommited in chunks of size _alignment. The + // ReservedSpace passed to initialize() must be aligned to this value. + const size_t _alignment; + + // Reserved area + char* _reserved_low_addr; + char* _reserved_high_addr; + + // Committed area + char* _committed_low_addr; + char* _committed_high_addr; + + // The entire space has been committed and pinned in memory, no + // os::commit_memory() or os::uncommit_memory(). + bool _special; + + // Convenience wrapper. + inline static size_t pointer_delta(const char* left, const char* right); + + public: + PSVirtualSpace(ReservedSpace rs, size_t alignment); + PSVirtualSpace(ReservedSpace rs); + + ~PSVirtualSpace(); + + // Eventually all instances should be created with the above 1- or 2-arg + // constructors. Then the 1st constructor below should become protected and + // the 2nd ctor and initialize() removed. + PSVirtualSpace(size_t alignment): _alignment(alignment) { } + PSVirtualSpace(); + bool initialize(ReservedSpace rs, size_t commit_size); + + bool contains(void* p) const; + + // Accessors (all sizes are bytes). + size_t alignment() const { return _alignment; } + char* reserved_low_addr() const { return _reserved_low_addr; } + char* reserved_high_addr() const { return _reserved_high_addr; } + char* committed_low_addr() const { return _committed_low_addr; } + char* committed_high_addr() const { return _committed_high_addr; } + bool special() const { return _special; } + + inline size_t committed_size() const; + inline size_t reserved_size() const; + inline size_t uncommitted_size() const; + + // Operations. + inline void set_reserved(char* low_addr, char* high_addr, bool special); + inline void set_reserved(ReservedSpace rs); + inline void set_committed(char* low_addr, char* high_addr); + virtual bool expand_by(size_t bytes, bool pre_touch = false); + virtual bool shrink_by(size_t bytes); + virtual size_t expand_into(PSVirtualSpace* space, size_t bytes); + void release(); + +#ifndef PRODUCT + // Debugging + static bool is_aligned(size_t val, size_t align); + bool is_aligned(size_t val) const; + bool is_aligned(char* val) const; + void verify() const; + void print() const; + virtual bool grows_up() const { return true; } + bool grows_down() const { return !grows_up(); } + + // Helper class to verify a space when entering/leaving a block. + class PSVirtualSpaceVerifier: public StackObj { + private: + const PSVirtualSpace* const _space; + public: + PSVirtualSpaceVerifier(PSVirtualSpace* space): _space(space) { + _space->verify(); + } + ~PSVirtualSpaceVerifier() { _space->verify(); } + }; +#endif + + virtual void print_space_boundaries_on(outputStream* st) const; + + // Included for compatibility with the original VirtualSpace. + public: + // Committed area + char* low() const { return committed_low_addr(); } + char* high() const { return committed_high_addr(); } + + // Reserved area + char* low_boundary() const { return reserved_low_addr(); } + char* high_boundary() const { return reserved_high_addr(); } +}; + +// A virtual space that grows from high addresses to low addresses. +class PSVirtualSpaceHighToLow : public PSVirtualSpace { + friend class VMStructs; + public: + PSVirtualSpaceHighToLow(ReservedSpace rs, size_t alignment); + PSVirtualSpaceHighToLow(ReservedSpace rs); + + virtual bool expand_by(size_t bytes, bool pre_touch = false); + virtual bool shrink_by(size_t bytes); + virtual size_t expand_into(PSVirtualSpace* space, size_t bytes); + + virtual void print_space_boundaries_on(outputStream* st) const; + +#ifndef PRODUCT + // Debugging + virtual bool grows_up() const { return false; } +#endif +}; + +// +// PSVirtualSpace inlines. +// +inline size_t +PSVirtualSpace::pointer_delta(const char* left, const char* right) { + return ::pointer_delta((void *)left, (void*)right, sizeof(char)); +} + +inline size_t PSVirtualSpace::committed_size() const { + return pointer_delta(committed_high_addr(), committed_low_addr()); +} + +inline size_t PSVirtualSpace::reserved_size() const { + return pointer_delta(reserved_high_addr(), reserved_low_addr()); +} + +inline size_t PSVirtualSpace::uncommitted_size() const { + return reserved_size() - committed_size(); +} + +inline void PSVirtualSpace::set_reserved(char* low_addr, char* high_addr, bool special) { + _reserved_low_addr = low_addr; + _reserved_high_addr = high_addr; + _special = special; +} + +inline void PSVirtualSpace::set_reserved(ReservedSpace rs) { + set_reserved(rs.base(), rs.base() + rs.size(), rs.special()); +} + +inline void PSVirtualSpace::set_committed(char* low_addr, char* high_addr) { + _committed_low_addr = low_addr; + _committed_high_addr = high_addr; +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp new file mode 100644 index 00000000000..ac7f64eaa06 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp @@ -0,0 +1,811 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_psYoungGen.cpp.incl" + +PSYoungGen::PSYoungGen(size_t initial_size, + size_t min_size, + size_t max_size) : + _init_gen_size(initial_size), + _min_gen_size(min_size), + _max_gen_size(max_size) +{} + +void PSYoungGen::initialize_virtual_space(ReservedSpace rs, size_t alignment) { + assert(_init_gen_size != 0, "Should have a finite size"); + _virtual_space = new PSVirtualSpace(rs, alignment); + if (!_virtual_space->expand_by(_init_gen_size)) { + vm_exit_during_initialization("Could not reserve enough space for " + "object heap"); + } +} + +void PSYoungGen::initialize(ReservedSpace rs, size_t alignment) { + initialize_virtual_space(rs, alignment); + initialize_work(); +} + +void PSYoungGen::initialize_work() { + + _reserved = MemRegion((HeapWord*)_virtual_space->low_boundary(), + (HeapWord*)_virtual_space->high_boundary()); + + MemRegion cmr((HeapWord*)_virtual_space->low(), + (HeapWord*)_virtual_space->high()); + Universe::heap()->barrier_set()->resize_covered_region(cmr); + + if (UseNUMA) { + _eden_space = new MutableNUMASpace(); + } else { + _eden_space = new MutableSpace(); + } + _from_space = new MutableSpace(); + _to_space = new MutableSpace(); + + if (_eden_space == NULL || _from_space == NULL || _to_space == NULL) { + vm_exit_during_initialization("Could not allocate a young gen space"); + } + + // Allocate the mark sweep views of spaces + _eden_mark_sweep = + new PSMarkSweepDecorator(_eden_space, NULL, MarkSweepDeadRatio); + _from_mark_sweep = + new PSMarkSweepDecorator(_from_space, NULL, MarkSweepDeadRatio); + _to_mark_sweep = + new PSMarkSweepDecorator(_to_space, NULL, MarkSweepDeadRatio); + + if (_eden_mark_sweep == NULL || + _from_mark_sweep == NULL || + _to_mark_sweep == NULL) { + vm_exit_during_initialization("Could not complete allocation" + " of the young generation"); + } + + // Generation Counters - generation 0, 3 subspaces + _gen_counters = new PSGenerationCounters("new", 0, 3, _virtual_space); + + // Compute maximum space sizes for performance counters + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + size_t alignment = heap->intra_generation_alignment(); + size_t size = _virtual_space->reserved_size(); + + size_t max_survivor_size; + size_t max_eden_size; + + if (UseAdaptiveSizePolicy) { + max_survivor_size = size / MinSurvivorRatio; + + // round the survivor space size down to the nearest alignment + // and make sure its size is greater than 0. + max_survivor_size = align_size_down(max_survivor_size, alignment); + max_survivor_size = MAX2(max_survivor_size, alignment); + + // set the maximum size of eden to be the size of the young gen + // less two times the minimum survivor size. The minimum survivor + // size for UseAdaptiveSizePolicy is one alignment. + max_eden_size = size - 2 * alignment; + } else { + max_survivor_size = size / InitialSurvivorRatio; + + // round the survivor space size down to the nearest alignment + // and make sure its size is greater than 0. + max_survivor_size = align_size_down(max_survivor_size, alignment); + max_survivor_size = MAX2(max_survivor_size, alignment); + + // set the maximum size of eden to be the size of the young gen + // less two times the survivor size when the generation is 100% + // committed. The minimum survivor size for -UseAdaptiveSizePolicy + // is dependent on the committed portion (current capacity) of the + // generation - the less space committed, the smaller the survivor + // space, possibly as small as an alignment. However, we are interested + // in the case where the young generation is 100% committed, as this + // is the point where eden reachs its maximum size. At this point, + // the size of a survivor space is max_survivor_size. + max_eden_size = size - 2 * max_survivor_size; + } + + _eden_counters = new SpaceCounters("eden", 0, max_eden_size, _eden_space, + _gen_counters); + _from_counters = new SpaceCounters("s0", 1, max_survivor_size, _from_space, + _gen_counters); + _to_counters = new SpaceCounters("s1", 2, max_survivor_size, _to_space, + _gen_counters); + + compute_initial_space_boundaries(); +} + +void PSYoungGen::compute_initial_space_boundaries() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + // Compute sizes + size_t alignment = heap->intra_generation_alignment(); + size_t size = _virtual_space->committed_size(); + + size_t survivor_size = size / InitialSurvivorRatio; + survivor_size = align_size_down(survivor_size, alignment); + // ... but never less than an alignment + survivor_size = MAX2(survivor_size, alignment); + + // Young generation is eden + 2 survivor spaces + size_t eden_size = size - (2 * survivor_size); + + // Now go ahead and set 'em. + set_space_boundaries(eden_size, survivor_size); + space_invariants(); + + if (UsePerfData) { + _eden_counters->update_capacity(); + _from_counters->update_capacity(); + _to_counters->update_capacity(); + } +} + +void PSYoungGen::set_space_boundaries(size_t eden_size, size_t survivor_size) { + assert(eden_size < _virtual_space->committed_size(), "just checking"); + assert(eden_size > 0 && survivor_size > 0, "just checking"); + + // Initial layout is Eden, to, from. After swapping survivor spaces, + // that leaves us with Eden, from, to, which is step one in our two + // step resize-with-live-data procedure. + char *eden_start = _virtual_space->low(); + char *to_start = eden_start + eden_size; + char *from_start = to_start + survivor_size; + char *from_end = from_start + survivor_size; + + assert(from_end == _virtual_space->high(), "just checking"); + assert(is_object_aligned((intptr_t)eden_start), "checking alignment"); + assert(is_object_aligned((intptr_t)to_start), "checking alignment"); + assert(is_object_aligned((intptr_t)from_start), "checking alignment"); + + MemRegion eden_mr((HeapWord*)eden_start, (HeapWord*)to_start); + MemRegion to_mr ((HeapWord*)to_start, (HeapWord*)from_start); + MemRegion from_mr((HeapWord*)from_start, (HeapWord*)from_end); + + eden_space()->initialize(eden_mr, true); + to_space()->initialize(to_mr , true); + from_space()->initialize(from_mr, true); +} + +#ifndef PRODUCT +void PSYoungGen::space_invariants() { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + const size_t alignment = heap->intra_generation_alignment(); + + // Currently, our eden size cannot shrink to zero + guarantee(eden_space()->capacity_in_bytes() >= alignment, "eden too small"); + guarantee(from_space()->capacity_in_bytes() >= alignment, "from too small"); + guarantee(to_space()->capacity_in_bytes() >= alignment, "to too small"); + + // Relationship of spaces to each other + char* eden_start = (char*)eden_space()->bottom(); + char* eden_end = (char*)eden_space()->end(); + char* from_start = (char*)from_space()->bottom(); + char* from_end = (char*)from_space()->end(); + char* to_start = (char*)to_space()->bottom(); + char* to_end = (char*)to_space()->end(); + + guarantee(eden_start >= _virtual_space->low(), "eden bottom"); + guarantee(eden_start < eden_end, "eden space consistency"); + guarantee(from_start < from_end, "from space consistency"); + guarantee(to_start < to_end, "to space consistency"); + + // Check whether from space is below to space + if (from_start < to_start) { + // Eden, from, to + guarantee(eden_end <= from_start, "eden/from boundary"); + guarantee(from_end <= to_start, "from/to boundary"); + guarantee(to_end <= _virtual_space->high(), "to end"); + } else { + // Eden, to, from + guarantee(eden_end <= to_start, "eden/to boundary"); + guarantee(to_end <= from_start, "to/from boundary"); + guarantee(from_end <= _virtual_space->high(), "from end"); + } + + // More checks that the virtual space is consistent with the spaces + assert(_virtual_space->committed_size() >= + (eden_space()->capacity_in_bytes() + + to_space()->capacity_in_bytes() + + from_space()->capacity_in_bytes()), "Committed size is inconsistent"); + assert(_virtual_space->committed_size() <= _virtual_space->reserved_size(), + "Space invariant"); + char* eden_top = (char*)eden_space()->top(); + char* from_top = (char*)from_space()->top(); + char* to_top = (char*)to_space()->top(); + assert(eden_top <= _virtual_space->high(), "eden top"); + assert(from_top <= _virtual_space->high(), "from top"); + assert(to_top <= _virtual_space->high(), "to top"); + + _virtual_space->verify(); +} +#endif + +void PSYoungGen::resize(size_t eden_size, size_t survivor_size) { + // Resize the generation if needed. If the generation resize + // reports false, do not attempt to resize the spaces. + if (resize_generation(eden_size, survivor_size)) { + // Then we lay out the spaces inside the generation + resize_spaces(eden_size, survivor_size); + + space_invariants(); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("Young generation size: " + "desired eden: " SIZE_FORMAT " survivor: " SIZE_FORMAT + " used: " SIZE_FORMAT " capacity: " SIZE_FORMAT + " gen limits: " SIZE_FORMAT " / " SIZE_FORMAT, + eden_size, survivor_size, used_in_bytes(), capacity_in_bytes(), + _max_gen_size, min_gen_size()); + } + } +} + + +bool PSYoungGen::resize_generation(size_t eden_size, size_t survivor_size) { + const size_t alignment = _virtual_space->alignment(); + size_t orig_size = _virtual_space->committed_size(); + bool size_changed = false; + + // There used to be this guarantee there. + // guarantee ((eden_size + 2*survivor_size) <= _max_gen_size, "incorrect input arguments"); + // Code below forces this requirement. In addition the desired eden + // size and disired survivor sizes are desired goals and may + // exceed the total generation size. + + assert(min_gen_size() <= orig_size && orig_size <= max_size(), "just checking"); + + // Adjust new generation size + const size_t eden_plus_survivors = + align_size_up(eden_size + 2 * survivor_size, alignment); + size_t desired_size = MAX2(MIN2(eden_plus_survivors, max_size()), + min_gen_size()); + assert(desired_size <= max_size(), "just checking"); + + if (desired_size > orig_size) { + // Grow the generation + size_t change = desired_size - orig_size; + assert(change % alignment == 0, "just checking"); + if (!_virtual_space->expand_by(change)) { + return false; // Error if we fail to resize! + } + + size_changed = true; + } else if (desired_size < orig_size) { + size_t desired_change = orig_size - desired_size; + assert(desired_change % alignment == 0, "just checking"); + + desired_change = limit_gen_shrink(desired_change); + + if (desired_change > 0) { + virtual_space()->shrink_by(desired_change); + reset_survivors_after_shrink(); + + size_changed = true; + } + } else { + if (Verbose && PrintGC) { + if (orig_size == gen_size_limit()) { + gclog_or_tty->print_cr("PSYoung generation size at maximum: " + SIZE_FORMAT "K", orig_size/K); + } else if (orig_size == min_gen_size()) { + gclog_or_tty->print_cr("PSYoung generation size at minium: " + SIZE_FORMAT "K", orig_size/K); + } + } + } + + if (size_changed) { + post_resize(); + + if (Verbose && PrintGC) { + size_t current_size = _virtual_space->committed_size(); + gclog_or_tty->print_cr("PSYoung generation size changed: " + SIZE_FORMAT "K->" SIZE_FORMAT "K", + orig_size/K, current_size/K); + } + } + + guarantee(eden_plus_survivors <= _virtual_space->committed_size() || + _virtual_space->committed_size() == max_size(), "Sanity"); + + return true; +} + + +void PSYoungGen::resize_spaces(size_t requested_eden_size, + size_t requested_survivor_size) { + assert(UseAdaptiveSizePolicy, "sanity check"); + assert(requested_eden_size > 0 && requested_survivor_size > 0, + "just checking"); + + // We require eden and to space to be empty + if ((!eden_space()->is_empty()) || (!to_space()->is_empty())) { + return; + } + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr("PSYoungGen::resize_spaces(requested_eden_size: " + SIZE_FORMAT + ", requested_survivor_size: " SIZE_FORMAT ")", + requested_eden_size, requested_survivor_size); + gclog_or_tty->print_cr(" eden: [" PTR_FORMAT ".." PTR_FORMAT ") " + SIZE_FORMAT, + eden_space()->bottom(), + eden_space()->end(), + pointer_delta(eden_space()->end(), + eden_space()->bottom(), + sizeof(char))); + gclog_or_tty->print_cr(" from: [" PTR_FORMAT ".." PTR_FORMAT ") " + SIZE_FORMAT, + from_space()->bottom(), + from_space()->end(), + pointer_delta(from_space()->end(), + from_space()->bottom(), + sizeof(char))); + gclog_or_tty->print_cr(" to: [" PTR_FORMAT ".." PTR_FORMAT ") " + SIZE_FORMAT, + to_space()->bottom(), + to_space()->end(), + pointer_delta( to_space()->end(), + to_space()->bottom(), + sizeof(char))); + } + + // There's nothing to do if the new sizes are the same as the current + if (requested_survivor_size == to_space()->capacity_in_bytes() && + requested_survivor_size == from_space()->capacity_in_bytes() && + requested_eden_size == eden_space()->capacity_in_bytes()) { + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" capacities are the right sizes, returning"); + } + return; + } + + char* eden_start = (char*)eden_space()->bottom(); + char* eden_end = (char*)eden_space()->end(); + char* from_start = (char*)from_space()->bottom(); + char* from_end = (char*)from_space()->end(); + char* to_start = (char*)to_space()->bottom(); + char* to_end = (char*)to_space()->end(); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + const size_t alignment = heap->intra_generation_alignment(); + const bool maintain_minimum = + (requested_eden_size + 2 * requested_survivor_size) <= min_gen_size(); + + // Check whether from space is below to space + if (from_start < to_start) { + // Eden, from, to + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" Eden, from, to:"); + } + + // Set eden + // "requested_eden_size" is a goal for the size of eden + // and may not be attainable. "eden_size" below is + // calculated based on the location of from-space and + // the goal for the size of eden. from-space is + // fixed in place because it contains live data. + // The calculation is done this way to avoid 32bit + // overflow (i.e., eden_start + requested_eden_size + // may too large for representation in 32bits). + size_t eden_size; + if (maintain_minimum) { + // Only make eden larger than the requested size if + // the minimum size of the generation has to be maintained. + // This could be done in general but policy at a higher + // level is determining a requested size for eden and that + // should be honored unless there is a fundamental reason. + eden_size = pointer_delta(from_start, + eden_start, + sizeof(char)); + } else { + eden_size = MIN2(requested_eden_size, + pointer_delta(from_start, eden_start, sizeof(char))); + } + + eden_end = eden_start + eden_size; + assert(eden_end >= eden_start, "addition overflowed") + + // To may resize into from space as long as it is clear of live data. + // From space must remain page aligned, though, so we need to do some + // extra calculations. + + // First calculate an optimal to-space + to_end = (char*)_virtual_space->high(); + to_start = (char*)pointer_delta(to_end, (char*)requested_survivor_size, + sizeof(char)); + + // Does the optimal to-space overlap from-space? + if (to_start < (char*)from_space()->end()) { + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + // Calculate the minimum offset possible for from_end + size_t from_size = pointer_delta(from_space()->top(), from_start, sizeof(char)); + + // Should we be in this method if from_space is empty? Why not the set_space method? FIX ME! + if (from_size == 0) { + from_size = alignment; + } else { + from_size = align_size_up(from_size, alignment); + } + + from_end = from_start + from_size; + assert(from_end > from_start, "addition overflow or from_size problem"); + + guarantee(from_end <= (char*)from_space()->end(), "from_end moved to the right"); + + // Now update to_start with the new from_end + to_start = MAX2(from_end, to_start); + } + + guarantee(to_start != to_end, "to space is zero sized"); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" [eden_start .. eden_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + eden_start, + eden_end, + pointer_delta(eden_end, eden_start, sizeof(char))); + gclog_or_tty->print_cr(" [from_start .. from_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + from_start, + from_end, + pointer_delta(from_end, from_start, sizeof(char))); + gclog_or_tty->print_cr(" [ to_start .. to_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + to_start, + to_end, + pointer_delta( to_end, to_start, sizeof(char))); + } + } else { + // Eden, to, from + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" Eden, to, from:"); + } + + // To space gets priority over eden resizing. Note that we position + // to space as if we were able to resize from space, even though from + // space is not modified. + // Giving eden priority was tried and gave poorer performance. + to_end = (char*)pointer_delta(_virtual_space->high(), + (char*)requested_survivor_size, + sizeof(char)); + to_end = MIN2(to_end, from_start); + to_start = (char*)pointer_delta(to_end, (char*)requested_survivor_size, + sizeof(char)); + // if the space sizes are to be increased by several times then + // 'to_start' will point beyond the young generation. In this case + // 'to_start' should be adjusted. + to_start = MAX2(to_start, eden_start + alignment); + + // Compute how big eden can be, then adjust end. + // See comments above on calculating eden_end. + size_t eden_size; + if (maintain_minimum) { + eden_size = pointer_delta(to_start, eden_start, sizeof(char)); + } else { + eden_size = MIN2(requested_eden_size, + pointer_delta(to_start, eden_start, sizeof(char))); + } + eden_end = eden_start + eden_size; + assert(eden_end >= eden_start, "addition overflowed") + + // Could choose to not let eden shrink + // to_start = MAX2(to_start, eden_end); + + // Don't let eden shrink down to 0 or less. + eden_end = MAX2(eden_end, eden_start + alignment); + to_start = MAX2(to_start, eden_end); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print_cr(" [eden_start .. eden_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + eden_start, + eden_end, + pointer_delta(eden_end, eden_start, sizeof(char))); + gclog_or_tty->print_cr(" [ to_start .. to_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + to_start, + to_end, + pointer_delta( to_end, to_start, sizeof(char))); + gclog_or_tty->print_cr(" [from_start .. from_end): " + "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, + from_start, + from_end, + pointer_delta(from_end, from_start, sizeof(char))); + } + } + + + guarantee((HeapWord*)from_start <= from_space()->bottom(), + "from start moved to the right"); + guarantee((HeapWord*)from_end >= from_space()->top(), + "from end moved into live data"); + assert(is_object_aligned((intptr_t)eden_start), "checking alignment"); + assert(is_object_aligned((intptr_t)from_start), "checking alignment"); + assert(is_object_aligned((intptr_t)to_start), "checking alignment"); + + MemRegion edenMR((HeapWord*)eden_start, (HeapWord*)eden_end); + MemRegion toMR ((HeapWord*)to_start, (HeapWord*)to_end); + MemRegion fromMR((HeapWord*)from_start, (HeapWord*)from_end); + + // Let's make sure the call to initialize doesn't reset "top"! + HeapWord* old_from_top = from_space()->top(); + + // For PrintAdaptiveSizePolicy block below + size_t old_from = from_space()->capacity_in_bytes(); + size_t old_to = to_space()->capacity_in_bytes(); + + eden_space()->initialize(edenMR, true); + to_space()->initialize(toMR , true); + from_space()->initialize(fromMR, false); // Note, not cleared! + + assert(from_space()->top() == old_from_top, "from top changed!"); + + if (PrintAdaptiveSizePolicy) { + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); + + gclog_or_tty->print("AdaptiveSizePolicy::survivor space sizes: " + "collection: %d " + "(" SIZE_FORMAT ", " SIZE_FORMAT ") -> " + "(" SIZE_FORMAT ", " SIZE_FORMAT ") ", + heap->total_collections(), + old_from, old_to, + from_space()->capacity_in_bytes(), + to_space()->capacity_in_bytes()); + gclog_or_tty->cr(); + } +} + +void PSYoungGen::swap_spaces() { + MutableSpace* s = from_space(); + _from_space = to_space(); + _to_space = s; + + // Now update the decorators. + PSMarkSweepDecorator* md = from_mark_sweep(); + _from_mark_sweep = to_mark_sweep(); + _to_mark_sweep = md; + + assert(from_mark_sweep()->space() == from_space(), "Sanity"); + assert(to_mark_sweep()->space() == to_space(), "Sanity"); +} + +size_t PSYoungGen::capacity_in_bytes() const { + return eden_space()->capacity_in_bytes() + + from_space()->capacity_in_bytes(); // to_space() is only used during scavenge +} + + +size_t PSYoungGen::used_in_bytes() const { + return eden_space()->used_in_bytes() + + from_space()->used_in_bytes(); // to_space() is only used during scavenge +} + + +size_t PSYoungGen::free_in_bytes() const { + return eden_space()->free_in_bytes() + + from_space()->free_in_bytes(); // to_space() is only used during scavenge +} + +size_t PSYoungGen::capacity_in_words() const { + return eden_space()->capacity_in_words() + + from_space()->capacity_in_words(); // to_space() is only used during scavenge +} + + +size_t PSYoungGen::used_in_words() const { + return eden_space()->used_in_words() + + from_space()->used_in_words(); // to_space() is only used during scavenge +} + + +size_t PSYoungGen::free_in_words() const { + return eden_space()->free_in_words() + + from_space()->free_in_words(); // to_space() is only used during scavenge +} + +void PSYoungGen::object_iterate(ObjectClosure* blk) { + eden_space()->object_iterate(blk); + from_space()->object_iterate(blk); + to_space()->object_iterate(blk); +} + +void PSYoungGen::precompact() { + eden_mark_sweep()->precompact(); + from_mark_sweep()->precompact(); + to_mark_sweep()->precompact(); +} + +void PSYoungGen::adjust_pointers() { + eden_mark_sweep()->adjust_pointers(); + from_mark_sweep()->adjust_pointers(); + to_mark_sweep()->adjust_pointers(); +} + +void PSYoungGen::compact() { + eden_mark_sweep()->compact(ZapUnusedHeapArea); + from_mark_sweep()->compact(ZapUnusedHeapArea); + // Mark sweep stores preserved markOops in to space, don't disturb! + to_mark_sweep()->compact(false); +} + +void PSYoungGen::move_and_update(ParCompactionManager* cm) { + PSParallelCompact::move_and_update(cm, PSParallelCompact::eden_space_id); + PSParallelCompact::move_and_update(cm, PSParallelCompact::from_space_id); + PSParallelCompact::move_and_update(cm, PSParallelCompact::to_space_id); +} + +void PSYoungGen::print() const { print_on(tty); } +void PSYoungGen::print_on(outputStream* st) const { + st->print(" %-15s", "PSYoungGen"); + if (PrintGCDetails && Verbose) { + st->print(" total " SIZE_FORMAT ", used " SIZE_FORMAT, + capacity_in_bytes(), used_in_bytes()); + } else { + st->print(" total " SIZE_FORMAT "K, used " SIZE_FORMAT "K", + capacity_in_bytes()/K, used_in_bytes()/K); + } + _virtual_space->print_space_boundaries_on(st); + st->print(" eden"); eden_space()->print_on(st); + st->print(" from"); from_space()->print_on(st); + st->print(" to "); to_space()->print_on(st); +} + +void PSYoungGen::print_used_change(size_t prev_used) const { + gclog_or_tty->print(" [%s:", name()); + gclog_or_tty->print(" " SIZE_FORMAT "K" + "->" SIZE_FORMAT "K" + "(" SIZE_FORMAT "K)", + prev_used / K, used_in_bytes() / K, + capacity_in_bytes() / K); + gclog_or_tty->print("]"); +} + +size_t PSYoungGen::available_for_expansion() { + ShouldNotReachHere(); + return 0; +} + +size_t PSYoungGen::available_for_contraction() { + ShouldNotReachHere(); + return 0; +} + +size_t PSYoungGen::available_to_min_gen() { + assert(virtual_space()->committed_size() >= min_gen_size(), "Invariant"); + return virtual_space()->committed_size() - min_gen_size(); +} + +// This method assumes that from-space has live data and that +// any shrinkage of the young gen is limited by location of +// from-space. +size_t PSYoungGen::available_to_live() { + size_t delta_in_survivor = 0; + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + const size_t space_alignment = heap->intra_generation_alignment(); + const size_t gen_alignment = heap->young_gen_alignment(); + + MutableSpace* space_shrinking = NULL; + if (from_space()->end() > to_space()->end()) { + space_shrinking = from_space(); + } else { + space_shrinking = to_space(); + } + + // Include any space that is committed but not included in + // the survivor spaces. + assert(((HeapWord*)virtual_space()->high()) >= space_shrinking->end(), + "Survivor space beyond high end"); + size_t unused_committed = pointer_delta(virtual_space()->high(), + space_shrinking->end(), sizeof(char)); + + if (space_shrinking->is_empty()) { + // Don't let the space shrink to 0 + assert(space_shrinking->capacity_in_bytes() >= space_alignment, + "Space is too small"); + delta_in_survivor = space_shrinking->capacity_in_bytes() - space_alignment; + } else { + delta_in_survivor = pointer_delta(space_shrinking->end(), + space_shrinking->top(), + sizeof(char)); + } + + size_t delta_in_bytes = unused_committed + delta_in_survivor; + delta_in_bytes = align_size_down(delta_in_bytes, gen_alignment); + return delta_in_bytes; +} + +// Return the number of bytes available for resizing down the young +// generation. This is the minimum of +// input "bytes" +// bytes to the minimum young gen size +// bytes to the size currently being used + some small extra +size_t PSYoungGen::limit_gen_shrink(size_t bytes) { + // Allow shrinkage into the current eden but keep eden large enough + // to maintain the minimum young gen size + bytes = MIN3(bytes, available_to_min_gen(), available_to_live()); + return align_size_down(bytes, virtual_space()->alignment()); +} + +void PSYoungGen::reset_after_change() { + ShouldNotReachHere(); +} + +void PSYoungGen::reset_survivors_after_shrink() { + _reserved = MemRegion((HeapWord*)virtual_space()->low_boundary(), + (HeapWord*)virtual_space()->high_boundary()); + PSScavenge::reference_processor()->set_span(_reserved); + + MutableSpace* space_shrinking = NULL; + if (from_space()->end() > to_space()->end()) { + space_shrinking = from_space(); + } else { + space_shrinking = to_space(); + } + + HeapWord* new_end = (HeapWord*)virtual_space()->high(); + assert(new_end >= space_shrinking->bottom(), "Shrink was too large"); + // Was there a shrink of the survivor space? + if (new_end < space_shrinking->end()) { + MemRegion mr(space_shrinking->bottom(), new_end); + space_shrinking->initialize(mr, false /* clear */); + } +} + +// This method currently does not expect to expand into eden (i.e., +// the virtual space boundaries is expected to be consistent +// with the eden boundaries.. +void PSYoungGen::post_resize() { + assert_locked_or_safepoint(Heap_lock); + assert((eden_space()->bottom() < to_space()->bottom()) && + (eden_space()->bottom() < from_space()->bottom()), + "Eden is assumed to be below the survivor spaces"); + + MemRegion cmr((HeapWord*)virtual_space()->low(), + (HeapWord*)virtual_space()->high()); + Universe::heap()->barrier_set()->resize_covered_region(cmr); + space_invariants(); +} + + + +void PSYoungGen::update_counters() { + if (UsePerfData) { + _eden_counters->update_all(); + _from_counters->update_all(); + _to_counters->update_all(); + _gen_counters->update_all(); + } +} + +void PSYoungGen::verify(bool allow_dirty) { + eden_space()->verify(allow_dirty); + from_space()->verify(allow_dirty); + to_space()->verify(allow_dirty); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp new file mode 100644 index 00000000000..953d4309f20 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp @@ -0,0 +1,182 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class PSMarkSweepDecorator; + +class PSYoungGen : public CHeapObj { + friend class VMStructs; + friend class ParallelScavengeHeap; + friend class AdjoiningGenerations; + + protected: + MemRegion _reserved; + PSVirtualSpace* _virtual_space; + + // Spaces + MutableSpace* _eden_space; + MutableSpace* _from_space; + MutableSpace* _to_space; + + + // MarkSweep Decorators + PSMarkSweepDecorator* _eden_mark_sweep; + PSMarkSweepDecorator* _from_mark_sweep; + PSMarkSweepDecorator* _to_mark_sweep; + + // Sizing information, in bytes, set in constructor + const size_t _init_gen_size; + const size_t _min_gen_size; + const size_t _max_gen_size; + + // Performance counters + PSGenerationCounters* _gen_counters; + SpaceCounters* _eden_counters; + SpaceCounters* _from_counters; + SpaceCounters* _to_counters; + + // Initialize the space boundaries + void compute_initial_space_boundaries(); + + // Space boundary helper + void set_space_boundaries(size_t eden_size, size_t survivor_size); + + virtual bool resize_generation(size_t eden_size, size_t survivor_size); + virtual void resize_spaces(size_t eden_size, size_t survivor_size); + + // Adjust the spaces to be consistent with the virtual space. + void post_resize(); + + // Return number of bytes that the generation can change. + // These should not be used by PSYoungGen + virtual size_t available_for_expansion(); + virtual size_t available_for_contraction(); + + // Given a desired shrinkage in the size of the young generation, + // return the actual size available for shrinkage. + virtual size_t limit_gen_shrink(size_t desired_change); + // returns the number of bytes available from the current size + // down to the minimum generation size. + size_t available_to_min_gen(); + // Return the number of bytes available for shrinkage considering + // the location the live data in the generation. + virtual size_t available_to_live(); + + public: + // Initialize the generation. + PSYoungGen(size_t initial_byte_size, + size_t minimum_byte_size, + size_t maximum_byte_size); + void initialize_work(); + virtual void initialize(ReservedSpace rs, size_t alignment); + virtual void initialize_virtual_space(ReservedSpace rs, size_t alignment); + + MemRegion reserved() const { return _reserved; } + + bool is_in(const void* p) const { + return _virtual_space->contains((void *)p); + } + + bool is_in_reserved(const void* p) const { + return reserved().contains((void *)p); + } + + MutableSpace* eden_space() const { return _eden_space; } + MutableSpace* from_space() const { return _from_space; } + MutableSpace* to_space() const { return _to_space; } + PSVirtualSpace* virtual_space() const { return _virtual_space; } + + // For Adaptive size policy + size_t min_gen_size() { return _min_gen_size; } + + // MarkSweep support + PSMarkSweepDecorator* eden_mark_sweep() const { return _eden_mark_sweep; } + PSMarkSweepDecorator* from_mark_sweep() const { return _from_mark_sweep; } + PSMarkSweepDecorator* to_mark_sweep() const { return _to_mark_sweep; } + + void precompact(); + void adjust_pointers(); + void compact(); + + // Parallel Old + void move_and_update(ParCompactionManager* cm); + + // Called during/after gc + void swap_spaces(); + + // Resize generation using suggested free space size and survivor size + // NOTE: "eden_size" and "survivor_size" are suggestions only. Current + // heap layout (particularly, live objects in from space) might + // not allow us to use these values. + void resize(size_t eden_size, size_t survivor_size); + + // Size info + size_t capacity_in_bytes() const; + size_t used_in_bytes() const; + size_t free_in_bytes() const; + + size_t capacity_in_words() const; + size_t used_in_words() const; + size_t free_in_words() const; + + // The max this generation can grow to + size_t max_size() const { return _reserved.byte_size(); } + + // The max this generation can grow to if the boundary between + // the generations are allowed to move. + size_t gen_size_limit() const { return _max_gen_size; } + + bool is_maximal_no_gc() const { + return true; // never expands except at a GC + } + + // Allocation + HeapWord* allocate(size_t word_size, bool is_tlab) { + HeapWord* result = eden_space()->cas_allocate(word_size); + return result; + } + + HeapWord** top_addr() const { return eden_space()->top_addr(); } + HeapWord** end_addr() const { return eden_space()->end_addr(); } + + // Iteration. + void oop_iterate(OopClosure* cl); + void object_iterate(ObjectClosure* cl); + + virtual void reset_after_change(); + virtual void reset_survivors_after_shrink(); + + // Performance Counter support + void update_counters(); + + // Debugging - do not use for time critical operations + void print() const; + void print_on(outputStream* st) const; + void print_used_change(size_t prev_used) const; + virtual const char* name() const { return "PSYoungGen"; } + + void verify(bool allow_dirty); + + // Space boundary invariant checker + void space_invariants() PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp new file mode 100644 index 00000000000..7f12cf55c55 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vmPSOperations.cpp.incl" + +// The following methods are used by the parallel scavenge collector +VM_ParallelGCFailedAllocation::VM_ParallelGCFailedAllocation(size_t size, + bool is_tlab, unsigned int gc_count) : + VM_GC_Operation(gc_count), + _size(size), + _is_tlab(is_tlab), + _result(NULL) +{ +} + +void VM_ParallelGCFailedAllocation::doit() { + JvmtiGCForAllocationMarker jgcm; + notify_gc_begin(false); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "must be a ParallelScavengeHeap"); + + GCCauseSetter gccs(heap, _gc_cause); + _result = heap->failed_mem_allocate(_size, _is_tlab); + + if (_result == NULL && GC_locker::is_active_and_needs_gc()) { + set_gc_locked(); + } + + notify_gc_end(); +} + +VM_ParallelGCFailedPermanentAllocation::VM_ParallelGCFailedPermanentAllocation(size_t size, + unsigned int gc_count, unsigned int full_gc_count) : + VM_GC_Operation(gc_count, full_gc_count, true /* full */), + _size(size), + _result(NULL) +{ +} + +void VM_ParallelGCFailedPermanentAllocation::doit() { + JvmtiGCFullMarker jgcm; + notify_gc_begin(true); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "must be a ParallelScavengeHeap"); + + GCCauseSetter gccs(heap, _gc_cause); + _result = heap->failed_permanent_mem_allocate(_size); + notify_gc_end(); +} + +// Only used for System.gc() calls +VM_ParallelGCSystemGC::VM_ParallelGCSystemGC(unsigned int gc_count, + unsigned int full_gc_count, + GCCause::Cause gc_cause) : + VM_GC_Operation(gc_count, full_gc_count, true /* full */) +{ + _gc_cause = gc_cause; +} + +void VM_ParallelGCSystemGC::doit() { + JvmtiGCFullMarker jgcm; + notify_gc_begin(true); + + ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); + assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, + "must be a ParallelScavengeHeap"); + + GCCauseSetter gccs(heap, _gc_cause); + if (_gc_cause == GCCause::_gc_locker + DEBUG_ONLY(|| _gc_cause == GCCause::_scavenge_alot)) { + // If (and only if) the scavenge fails, this will invoke a full gc. + heap->invoke_scavenge(); + } else { + heap->invoke_full_gc(false); + } + notify_gc_end(); +} diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.hpp new file mode 100644 index 00000000000..ab8871914ed --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class VM_ParallelGCFailedAllocation: public VM_GC_Operation { + private: + size_t _size; + bool _is_tlab; + HeapWord* _result; + + public: + VM_ParallelGCFailedAllocation(size_t size, bool is_tlab, + unsigned int gc_count); + + virtual VMOp_Type type() const { + return VMOp_ParallelGCFailedAllocation; + } + virtual void doit(); + + HeapWord* result() const { return _result; } +}; + +class VM_ParallelGCFailedPermanentAllocation: public VM_GC_Operation { +private: + size_t _size; + HeapWord* _result; + + public: + VM_ParallelGCFailedPermanentAllocation(size_t size, + unsigned int gc_count, + unsigned int full_gc_count); + virtual VMOp_Type type() const { + return VMOp_ParallelGCFailedPermanentAllocation; + } + virtual void doit(); + HeapWord* result() const { return _result; } +}; + +class VM_ParallelGCSystemGC: public VM_GC_Operation { + public: + VM_ParallelGCSystemGC(unsigned int gc_count, unsigned int full_gc_count, + GCCause::Cause gc_cause); + virtual VMOp_Type type() const { return VMOp_ParallelGCSystemGC; } + virtual void doit(); +}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmStructs_parallelgc.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmStructs_parallelgc.hpp new file mode 100644 index 00000000000..114d0034154 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmStructs_parallelgc.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#define VM_STRUCTS_PARALLELGC(nonstatic_field, \ + static_field) \ + \ + /**********************/ \ + /* Parallel GC fields */ \ + /**********************/ \ + nonstatic_field(PSVirtualSpace, _alignment, const size_t) \ + nonstatic_field(PSVirtualSpace, _reserved_low_addr, char*) \ + nonstatic_field(PSVirtualSpace, _reserved_high_addr, char*) \ + nonstatic_field(PSVirtualSpace, _committed_low_addr, char*) \ + nonstatic_field(PSVirtualSpace, _committed_high_addr, char*) \ + \ + nonstatic_field(ImmutableSpace, _bottom, HeapWord*) \ + nonstatic_field(ImmutableSpace, _end, HeapWord*) \ + \ + nonstatic_field(MutableSpace, _top, HeapWord*) \ + \ + nonstatic_field(PSYoungGen, _reserved, MemRegion) \ + nonstatic_field(PSYoungGen, _virtual_space, PSVirtualSpace*) \ + nonstatic_field(PSYoungGen, _eden_space, MutableSpace*) \ + nonstatic_field(PSYoungGen, _from_space, MutableSpace*) \ + nonstatic_field(PSYoungGen, _to_space, MutableSpace*) \ + nonstatic_field(PSYoungGen, _init_gen_size, const size_t) \ + nonstatic_field(PSYoungGen, _min_gen_size, const size_t) \ + nonstatic_field(PSYoungGen, _max_gen_size, const size_t) \ + \ + nonstatic_field(PSOldGen, _reserved, MemRegion) \ + nonstatic_field(PSOldGen, _virtual_space, PSVirtualSpace*) \ + nonstatic_field(PSOldGen, _object_space, MutableSpace*) \ + nonstatic_field(PSOldGen, _init_gen_size, const size_t) \ + nonstatic_field(PSOldGen, _min_gen_size, const size_t) \ + nonstatic_field(PSOldGen, _max_gen_size, const size_t) \ + \ + nonstatic_field(PSPermGen, _last_used, size_t) \ + \ + static_field(ParallelScavengeHeap, _young_gen, PSYoungGen*) \ + static_field(ParallelScavengeHeap, _old_gen, PSOldGen*) \ + static_field(ParallelScavengeHeap, _perm_gen, PSPermGen*) \ + static_field(ParallelScavengeHeap, _psh, ParallelScavengeHeap*) \ + \ + +#define VM_TYPES_PARALLELGC(declare_type, \ + declare_toplevel_type) \ + \ + /*****************************************/ \ + /* Parallel GC - space, gen abstractions */ \ + /*****************************************/ \ + declare_type(ParallelScavengeHeap, CollectedHeap) \ + \ + declare_toplevel_type(PSVirtualSpace) \ + declare_toplevel_type(ImmutableSpace) \ + declare_type(MutableSpace, ImmutableSpace) \ + declare_toplevel_type(PSYoungGen) \ + declare_type(ASPSYoungGen, PSYoungGen) \ + declare_toplevel_type(PSOldGen) \ + declare_type(ASPSOldGen, PSOldGen) \ + declare_type(PSPermGen, PSOldGen) \ + \ + /*****************************/ \ + /* Parallel GC pointer types */ \ + /*****************************/ \ + \ + declare_toplevel_type(PSVirtualSpace*) \ + declare_toplevel_type(ImmutableSpace*) \ + declare_toplevel_type(MutableSpace*) \ + declare_toplevel_type(PSYoungGen*) \ + declare_toplevel_type(ASPSYoungGen*) \ + declare_toplevel_type(PSOldGen*) \ + declare_toplevel_type(ASPSOldGen*) \ + declare_toplevel_type(PSPermGen*) \ + declare_toplevel_type(ParallelScavengeHeap*) diff --git a/hotspot/src/share/vm/gc_implementation/shared/adaptiveSizePolicy.cpp b/hotspot/src/share/vm/gc_implementation/shared/adaptiveSizePolicy.cpp new file mode 100644 index 00000000000..acaa33818a4 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/adaptiveSizePolicy.cpp @@ -0,0 +1,392 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +#include "incls/_precompiled.incl" +#include "incls/_adaptiveSizePolicy.cpp.incl" + +elapsedTimer AdaptiveSizePolicy::_minor_timer; +elapsedTimer AdaptiveSizePolicy::_major_timer; + +// The throughput goal is implemented as +// _throughput_goal = 1 - ( 1 / (1 + gc_cost_ratio)) +// gc_cost_ratio is the ratio +// application cost / gc cost +// For example a gc_cost_ratio of 4 translates into a +// throughput goal of .80 + +AdaptiveSizePolicy::AdaptiveSizePolicy(size_t init_eden_size, + size_t init_promo_size, + size_t init_survivor_size, + double gc_pause_goal_sec, + uint gc_cost_ratio) : + _eden_size(init_eden_size), + _promo_size(init_promo_size), + _survivor_size(init_survivor_size), + _gc_pause_goal_sec(gc_pause_goal_sec), + _throughput_goal(1.0 - double(1.0 / (1.0 + (double) gc_cost_ratio))), + _gc_time_limit_exceeded(false), + _print_gc_time_limit_would_be_exceeded(false), + _gc_time_limit_count(0), + _latest_minor_mutator_interval_seconds(0), + _threshold_tolerance_percent(1.0 + ThresholdTolerance/100.0), + _young_gen_change_for_minor_throughput(0), + _old_gen_change_for_major_throughput(0) { + _avg_minor_pause = + new AdaptivePaddedAverage(AdaptiveTimeWeight, PausePadding); + _avg_minor_interval = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_minor_gc_cost = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + _avg_major_gc_cost = new AdaptiveWeightedAverage(AdaptiveTimeWeight); + + _avg_young_live = new AdaptiveWeightedAverage(AdaptiveSizePolicyWeight); + _avg_old_live = new AdaptiveWeightedAverage(AdaptiveSizePolicyWeight); + _avg_eden_live = new AdaptiveWeightedAverage(AdaptiveSizePolicyWeight); + + _avg_survived = new AdaptivePaddedAverage(AdaptiveSizePolicyWeight, + SurvivorPadding); + _avg_pretenured = new AdaptivePaddedNoZeroDevAverage( + AdaptiveSizePolicyWeight, + SurvivorPadding); + + _minor_pause_old_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + _minor_pause_young_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + _minor_collection_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + _major_collection_estimator = + new LinearLeastSquareFit(AdaptiveSizePolicyWeight); + + // Start the timers + _minor_timer.start(); + + _young_gen_policy_is_ready = false; +} + +bool AdaptiveSizePolicy::tenuring_threshold_change() const { + return decrement_tenuring_threshold_for_gc_cost() || + increment_tenuring_threshold_for_gc_cost() || + decrement_tenuring_threshold_for_survivor_limit(); +} + +void AdaptiveSizePolicy::minor_collection_begin() { + // Update the interval time + _minor_timer.stop(); + // Save most recent collection time + _latest_minor_mutator_interval_seconds = _minor_timer.seconds(); + _minor_timer.reset(); + _minor_timer.start(); +} + +void AdaptiveSizePolicy::update_minor_pause_young_estimator( + double minor_pause_in_ms) { + double eden_size_in_mbytes = ((double)_eden_size)/((double)M); + _minor_pause_young_estimator->update(eden_size_in_mbytes, + minor_pause_in_ms); +} + +void AdaptiveSizePolicy::minor_collection_end(GCCause::Cause gc_cause) { + // Update the pause time. + _minor_timer.stop(); + + if (gc_cause != GCCause::_java_lang_system_gc || + UseAdaptiveSizePolicyWithSystemGC) { + double minor_pause_in_seconds = _minor_timer.seconds(); + double minor_pause_in_ms = minor_pause_in_seconds * MILLIUNITS; + + // Sample for performance counter + _avg_minor_pause->sample(minor_pause_in_seconds); + + // Cost of collection (unit-less) + double collection_cost = 0.0; + if ((_latest_minor_mutator_interval_seconds > 0.0) && + (minor_pause_in_seconds > 0.0)) { + double interval_in_seconds = + _latest_minor_mutator_interval_seconds + minor_pause_in_seconds; + collection_cost = + minor_pause_in_seconds / interval_in_seconds; + _avg_minor_gc_cost->sample(collection_cost); + // Sample for performance counter + _avg_minor_interval->sample(interval_in_seconds); + } + + // The policy does not have enough data until at least some + // minor collections have been done. + _young_gen_policy_is_ready = + (_avg_minor_gc_cost->count() >= AdaptiveSizePolicyReadyThreshold); + + // Calculate variables used to estimate pause time vs. gen sizes + double eden_size_in_mbytes = ((double)_eden_size)/((double)M); + update_minor_pause_young_estimator(minor_pause_in_ms); + update_minor_pause_old_estimator(minor_pause_in_ms); + + if (PrintAdaptiveSizePolicy && Verbose) { + gclog_or_tty->print("AdaptiveSizePolicy::minor_collection_end: " + "minor gc cost: %f average: %f", collection_cost, + _avg_minor_gc_cost->average()); + gclog_or_tty->print_cr(" minor pause: %f minor period %f", + minor_pause_in_ms, + _latest_minor_mutator_interval_seconds * MILLIUNITS); + } + + // Calculate variable used to estimate collection cost vs. gen sizes + assert(collection_cost >= 0.0, "Expected to be non-negative"); + _minor_collection_estimator->update(eden_size_in_mbytes, collection_cost); + } + + // Interval times use this timer to measure the mutator time. + // Reset the timer after the GC pause. + _minor_timer.reset(); + _minor_timer.start(); +} + +size_t AdaptiveSizePolicy::eden_increment(size_t cur_eden, + uint percent_change) { + size_t eden_heap_delta; + eden_heap_delta = cur_eden / 100 * percent_change; + return eden_heap_delta; +} + +size_t AdaptiveSizePolicy::eden_increment(size_t cur_eden) { + return eden_increment(cur_eden, YoungGenerationSizeIncrement); +} + +size_t AdaptiveSizePolicy::eden_decrement(size_t cur_eden) { + size_t eden_heap_delta = eden_increment(cur_eden) / + AdaptiveSizeDecrementScaleFactor; + return eden_heap_delta; +} + +size_t AdaptiveSizePolicy::promo_increment(size_t cur_promo, + uint percent_change) { + size_t promo_heap_delta; + promo_heap_delta = cur_promo / 100 * percent_change; + return promo_heap_delta; +} + +size_t AdaptiveSizePolicy::promo_increment(size_t cur_promo) { + return promo_increment(cur_promo, TenuredGenerationSizeIncrement); +} + +size_t AdaptiveSizePolicy::promo_decrement(size_t cur_promo) { + size_t promo_heap_delta = promo_increment(cur_promo); + promo_heap_delta = promo_heap_delta / AdaptiveSizeDecrementScaleFactor; + return promo_heap_delta; +} + +double AdaptiveSizePolicy::time_since_major_gc() const { + _major_timer.stop(); + double result = _major_timer.seconds(); + _major_timer.start(); + return result; +} + +// Linear decay of major gc cost +double AdaptiveSizePolicy::decaying_major_gc_cost() const { + double major_interval = major_gc_interval_average_for_decay(); + double major_gc_cost_average = major_gc_cost(); + double decayed_major_gc_cost = major_gc_cost_average; + if(time_since_major_gc() > 0.0) { + decayed_major_gc_cost = major_gc_cost() * + (((double) AdaptiveSizeMajorGCDecayTimeScale) * major_interval) + / time_since_major_gc(); + } + + // The decayed cost should always be smaller than the + // average cost but the vagaries of finite arithmetic could + // produce a larger value in decayed_major_gc_cost so protect + // against that. + return MIN2(major_gc_cost_average, decayed_major_gc_cost); +} + +// Use a value of the major gc cost that has been decayed +// by the factor +// +// average-interval-between-major-gc * AdaptiveSizeMajorGCDecayTimeScale / +// time-since-last-major-gc +// +// if the average-interval-between-major-gc * AdaptiveSizeMajorGCDecayTimeScale +// is less than time-since-last-major-gc. +// +// In cases where there are initial major gc's that +// are of a relatively high cost but no later major +// gc's, the total gc cost can remain high because +// the major gc cost remains unchanged (since there are no major +// gc's). In such a situation the value of the unchanging +// major gc cost can keep the mutator throughput below +// the goal when in fact the major gc cost is becoming diminishingly +// small. Use the decaying gc cost only to decide whether to +// adjust for throughput. Using it also to determine the adjustment +// to be made for throughput also seems reasonable but there is +// no test case to use to decide if it is the right thing to do +// don't do it yet. + +double AdaptiveSizePolicy::decaying_gc_cost() const { + double decayed_major_gc_cost = major_gc_cost(); + double avg_major_interval = major_gc_interval_average_for_decay(); + if (UseAdaptiveSizeDecayMajorGCCost && + (AdaptiveSizeMajorGCDecayTimeScale > 0) && + (avg_major_interval > 0.00)) { + double time_since_last_major_gc = time_since_major_gc(); + + // Decay the major gc cost? + if (time_since_last_major_gc > + ((double) AdaptiveSizeMajorGCDecayTimeScale) * avg_major_interval) { + + // Decay using the time-since-last-major-gc + decayed_major_gc_cost = decaying_major_gc_cost(); + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("\ndecaying_gc_cost: major interval average:" + " %f time since last major gc: %f", + avg_major_interval, time_since_last_major_gc); + gclog_or_tty->print_cr(" major gc cost: %f decayed major gc cost: %f", + major_gc_cost(), decayed_major_gc_cost); + } + } + } + double result = MIN2(1.0, decayed_major_gc_cost + minor_gc_cost()); + return result; +} + + +void AdaptiveSizePolicy::clear_generation_free_space_flags() { + set_change_young_gen_for_min_pauses(0); + set_change_old_gen_for_maj_pauses(0); + + set_change_old_gen_for_throughput(0); + set_change_young_gen_for_throughput(0); + set_decrease_for_footprint(0); + set_decide_at_full_gc(0); +} + +// Printing + +bool AdaptiveSizePolicy::print_adaptive_size_policy_on(outputStream* st) const { + + // Should only be used with adaptive size policy turned on. + // Otherwise, there may be variables that are undefined. + if (!UseAdaptiveSizePolicy) return false; + + // Print goal for which action is needed. + char* action = NULL; + bool change_for_pause = false; + if ((change_old_gen_for_maj_pauses() == + decrease_old_gen_for_maj_pauses_true) || + (change_young_gen_for_min_pauses() == + decrease_young_gen_for_min_pauses_true)) { + action = (char*) " *** pause time goal ***"; + change_for_pause = true; + } else if ((change_old_gen_for_throughput() == + increase_old_gen_for_throughput_true) || + (change_young_gen_for_throughput() == + increase_young_gen_for_througput_true)) { + action = (char*) " *** throughput goal ***"; + } else if (decrease_for_footprint()) { + action = (char*) " *** reduced footprint ***"; + } else { + // No actions were taken. This can legitimately be the + // situation if not enough data has been gathered to make + // decisions. + return false; + } + + // Pauses + // Currently the size of the old gen is only adjusted to + // change the major pause times. + char* young_gen_action = NULL; + char* tenured_gen_action = NULL; + + char* shrink_msg = (char*) "(attempted to shrink)"; + char* grow_msg = (char*) "(attempted to grow)"; + char* no_change_msg = (char*) "(no change)"; + if (change_young_gen_for_min_pauses() == + decrease_young_gen_for_min_pauses_true) { + young_gen_action = shrink_msg; + } else if (change_for_pause) { + young_gen_action = no_change_msg; + } + + if (change_old_gen_for_maj_pauses() == decrease_old_gen_for_maj_pauses_true) { + tenured_gen_action = shrink_msg; + } else if (change_for_pause) { + tenured_gen_action = no_change_msg; + } + + // Throughput + if (change_old_gen_for_throughput() == increase_old_gen_for_throughput_true) { + assert(change_young_gen_for_throughput() == + increase_young_gen_for_througput_true, + "Both generations should be growing"); + young_gen_action = grow_msg; + tenured_gen_action = grow_msg; + } else if (change_young_gen_for_throughput() == + increase_young_gen_for_througput_true) { + // Only the young generation may grow at start up (before + // enough full collections have been done to grow the old generation). + young_gen_action = grow_msg; + tenured_gen_action = no_change_msg; + } + + // Minimum footprint + if (decrease_for_footprint() != 0) { + young_gen_action = shrink_msg; + tenured_gen_action = shrink_msg; + } + + st->print_cr(" UseAdaptiveSizePolicy actions to meet %s", action); + st->print_cr(" GC overhead (%%)"); + st->print_cr(" Young generation: %7.2f\t %s", + 100.0 * avg_minor_gc_cost()->average(), + young_gen_action); + st->print_cr(" Tenured generation: %7.2f\t %s", + 100.0 * avg_major_gc_cost()->average(), + tenured_gen_action); + return true; +} + +bool AdaptiveSizePolicy::print_adaptive_size_policy_on( + outputStream* st, + int tenuring_threshold_arg) const { + if (!AdaptiveSizePolicy::print_adaptive_size_policy_on(st)) { + return false; + } + + // Tenuring threshold + bool tenuring_threshold_changed = true; + if (decrement_tenuring_threshold_for_survivor_limit()) { + st->print(" Tenuring threshold: (attempted to decrease to avoid" + " survivor space overflow) = "); + } else if (decrement_tenuring_threshold_for_gc_cost()) { + st->print(" Tenuring threshold: (attempted to decrease to balance" + " GC costs) = "); + } else if (increment_tenuring_threshold_for_gc_cost()) { + st->print(" Tenuring threshold: (attempted to increase to balance" + " GC costs) = "); + } else { + tenuring_threshold_changed = false; + assert(!tenuring_threshold_change(), "(no change was attempted)"); + } + if (tenuring_threshold_changed) { + st->print_cr("%d", tenuring_threshold_arg); + } + return true; +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/adaptiveSizePolicy.hpp b/hotspot/src/share/vm/gc_implementation/shared/adaptiveSizePolicy.hpp new file mode 100644 index 00000000000..d07dcf1a388 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/adaptiveSizePolicy.hpp @@ -0,0 +1,492 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This class keeps statistical information and computes the +// size of the heap. + +// Forward decls +class elapsedTimer; + +class AdaptiveSizePolicy : public CHeapObj { + friend class GCAdaptivePolicyCounters; + friend class PSGCAdaptivePolicyCounters; + friend class CMSGCAdaptivePolicyCounters; + protected: + + enum GCPolicyKind { + _gc_adaptive_size_policy, + _gc_ps_adaptive_size_policy, + _gc_cms_adaptive_size_policy + }; + virtual GCPolicyKind kind() const { return _gc_adaptive_size_policy; } + + enum SizePolicyTrueValues { + decrease_old_gen_for_throughput_true = -7, + decrease_young_gen_for_througput_true = -6, + + increase_old_gen_for_min_pauses_true = -5, + decrease_old_gen_for_min_pauses_true = -4, + decrease_young_gen_for_maj_pauses_true = -3, + increase_young_gen_for_min_pauses_true = -2, + increase_old_gen_for_maj_pauses_true = -1, + + decrease_young_gen_for_min_pauses_true = 1, + decrease_old_gen_for_maj_pauses_true = 2, + increase_young_gen_for_maj_pauses_true = 3, + + increase_old_gen_for_throughput_true = 4, + increase_young_gen_for_througput_true = 5, + + decrease_young_gen_for_footprint_true = 6, + decrease_old_gen_for_footprint_true = 7, + decide_at_full_gc_true = 8 + }; + + // Goal for the fraction of the total time during which application + // threads run. + const double _throughput_goal; + + // Last calculated sizes, in bytes, and aligned + size_t _eden_size; // calculated eden free space in bytes + size_t _promo_size; // calculated cms gen free space in bytes + + size_t _survivor_size; // calculated survivor size in bytes + + // This is a hint for the heap: we've detected that gc times + // are taking longer than GCTimeLimit allows. + bool _gc_time_limit_exceeded; + // Use for diagnostics only. If UseGCTimeLimit is false, + // this variable is still set. + bool _print_gc_time_limit_would_be_exceeded; + // Count of consecutive GC that have exceeded the + // GC time limit criterion. + uint _gc_time_limit_count; + + // Minor collection timers used to determine both + // pause and interval times for collections. + static elapsedTimer _minor_timer; + + // Major collection timers, used to determine both + // pause and interval times for collections + static elapsedTimer _major_timer; + + // Time statistics + AdaptivePaddedAverage* _avg_minor_pause; + AdaptiveWeightedAverage* _avg_minor_interval; + AdaptiveWeightedAverage* _avg_minor_gc_cost; + + AdaptiveWeightedAverage* _avg_major_interval; + AdaptiveWeightedAverage* _avg_major_gc_cost; + + // Footprint statistics + AdaptiveWeightedAverage* _avg_young_live; + AdaptiveWeightedAverage* _avg_eden_live; + AdaptiveWeightedAverage* _avg_old_live; + + // Statistics for survivor space calculation for young generation + AdaptivePaddedAverage* _avg_survived; + + // Objects that have been directly allocated in the old generation. + AdaptivePaddedNoZeroDevAverage* _avg_pretenured; + + // Variable for estimating the major and minor pause times. + // These variables represent linear least-squares fits of + // the data. + // minor pause time vs. old gen size + LinearLeastSquareFit* _minor_pause_old_estimator; + // minor pause time vs. young gen size + LinearLeastSquareFit* _minor_pause_young_estimator; + + // Variables for estimating the major and minor collection costs + // minor collection time vs. young gen size + LinearLeastSquareFit* _minor_collection_estimator; + // major collection time vs. cms gen size + LinearLeastSquareFit* _major_collection_estimator; + + // These record the most recent collection times. They + // are available as an alternative to using the averages + // for making ergonomic decisions. + double _latest_minor_mutator_interval_seconds; + + // Allowed difference between major and minor gc times, used + // for computing tenuring_threshold. + const double _threshold_tolerance_percent; + + const double _gc_pause_goal_sec; // goal for maximum gc pause + + // Flag indicating that the adaptive policy is ready to use + bool _young_gen_policy_is_ready; + + // decrease/increase the young generation for minor pause time + int _change_young_gen_for_min_pauses; + + // decrease/increase the old generation for major pause time + int _change_old_gen_for_maj_pauses; + + // change old geneneration for throughput + int _change_old_gen_for_throughput; + + // change young generation for throughput + int _change_young_gen_for_throughput; + + // Flag indicating that the policy would + // increase the tenuring threshold because of the total major gc cost + // is greater than the total minor gc cost + bool _increment_tenuring_threshold_for_gc_cost; + // decrease the tenuring threshold because of the the total minor gc + // cost is greater than the total major gc cost + bool _decrement_tenuring_threshold_for_gc_cost; + // decrease due to survivor size limit + bool _decrement_tenuring_threshold_for_survivor_limit; + + // decrease generation sizes for footprint + int _decrease_for_footprint; + + // Set if the ergonomic decisions were made at a full GC. + int _decide_at_full_gc; + + // Changing the generation sizing depends on the data that is + // gathered about the effects of changes on the pause times and + // throughput. These variable count the number of data points + // gathered. The policy may use these counters as a threshhold + // for reliable data. + julong _young_gen_change_for_minor_throughput; + julong _old_gen_change_for_major_throughput; + + // Accessors + + double gc_pause_goal_sec() const { return _gc_pause_goal_sec; } + // The value returned is unitless: it's the proportion of time + // spent in a particular collection type. + // An interval time will be 0.0 if a collection type hasn't occurred yet. + // The 1.4.2 implementation put a floor on the values of major_gc_cost + // and minor_gc_cost. This was useful because of the way major_gc_cost + // and minor_gc_cost was used in calculating the sizes of the generations. + // Do not use a floor in this implementation because any finite value + // will put a limit on the throughput that can be achieved and any + // throughput goal above that limit will drive the generations sizes + // to extremes. + double major_gc_cost() const { + return MAX2(0.0F, _avg_major_gc_cost->average()); + } + + // The value returned is unitless: it's the proportion of time + // spent in a particular collection type. + // An interval time will be 0.0 if a collection type hasn't occurred yet. + // The 1.4.2 implementation put a floor on the values of major_gc_cost + // and minor_gc_cost. This was useful because of the way major_gc_cost + // and minor_gc_cost was used in calculating the sizes of the generations. + // Do not use a floor in this implementation because any finite value + // will put a limit on the throughput that can be achieved and any + // throughput goal above that limit will drive the generations sizes + // to extremes. + + double minor_gc_cost() const { + return MAX2(0.0F, _avg_minor_gc_cost->average()); + } + + // Because we're dealing with averages, gc_cost() can be + // larger than 1.0 if just the sum of the minor cost the + // the major cost is used. Worse than that is the + // fact that the minor cost and the major cost each + // tend toward 1.0 in the extreme of high gc costs. + // Limit the value of gc_cost to 1.0 so that the mutator + // cost stays non-negative. + virtual double gc_cost() const { + double result = MIN2(1.0, minor_gc_cost() + major_gc_cost()); + assert(result >= 0.0, "Both minor and major costs are non-negative"); + return result; + } + + // Elapsed time since the last major collection. + virtual double time_since_major_gc() const; + + // Average interval between major collections to be used + // in calculating the decaying major gc cost. An overestimate + // of this time would be a conservative estimate because + // this time is used to decide if the major GC cost + // should be decayed (i.e., if the time since the last + // major gc is long compared to the time returned here, + // then the major GC cost will be decayed). See the + // implementations for the specifics. + virtual double major_gc_interval_average_for_decay() const { + return _avg_major_interval->average(); + } + + // Return the cost of the GC where the major gc cost + // has been decayed based on the time since the last + // major collection. + double decaying_gc_cost() const; + + // Decay the major gc cost. Use this only for decisions on + // whether to adjust, not to determine by how much to adjust. + // This approximation is crude and may not be good enough for the + // latter. + double decaying_major_gc_cost() const; + + // Return the mutator cost using the decayed + // GC cost. + double adjusted_mutator_cost() const { + double result = 1.0 - decaying_gc_cost(); + assert(result >= 0.0, "adjusted mutator cost calculation is incorrect"); + return result; + } + + virtual double mutator_cost() const { + double result = 1.0 - gc_cost(); + assert(result >= 0.0, "mutator cost calculation is incorrect"); + return result; + } + + + bool young_gen_policy_is_ready() { return _young_gen_policy_is_ready; } + + void update_minor_pause_young_estimator(double minor_pause_in_ms); + virtual void update_minor_pause_old_estimator(double minor_pause_in_ms) { + // This is not meaningful for all policies but needs to be present + // to use minor_collection_end() in its current form. + } + + virtual size_t eden_increment(size_t cur_eden); + virtual size_t eden_increment(size_t cur_eden, uint percent_change); + virtual size_t eden_decrement(size_t cur_eden); + virtual size_t promo_increment(size_t cur_eden); + virtual size_t promo_increment(size_t cur_eden, uint percent_change); + virtual size_t promo_decrement(size_t cur_eden); + + virtual void clear_generation_free_space_flags(); + + int change_old_gen_for_throughput() const { + return _change_old_gen_for_throughput; + } + void set_change_old_gen_for_throughput(int v) { + _change_old_gen_for_throughput = v; + } + int change_young_gen_for_throughput() const { + return _change_young_gen_for_throughput; + } + void set_change_young_gen_for_throughput(int v) { + _change_young_gen_for_throughput = v; + } + + int change_old_gen_for_maj_pauses() const { + return _change_old_gen_for_maj_pauses; + } + void set_change_old_gen_for_maj_pauses(int v) { + _change_old_gen_for_maj_pauses = v; + } + + bool decrement_tenuring_threshold_for_gc_cost() const { + return _decrement_tenuring_threshold_for_gc_cost; + } + void set_decrement_tenuring_threshold_for_gc_cost(bool v) { + _decrement_tenuring_threshold_for_gc_cost = v; + } + bool increment_tenuring_threshold_for_gc_cost() const { + return _increment_tenuring_threshold_for_gc_cost; + } + void set_increment_tenuring_threshold_for_gc_cost(bool v) { + _increment_tenuring_threshold_for_gc_cost = v; + } + bool decrement_tenuring_threshold_for_survivor_limit() const { + return _decrement_tenuring_threshold_for_survivor_limit; + } + void set_decrement_tenuring_threshold_for_survivor_limit(bool v) { + _decrement_tenuring_threshold_for_survivor_limit = v; + } + // Return true if the policy suggested a change. + bool tenuring_threshold_change() const; + + public: + AdaptiveSizePolicy(size_t init_eden_size, + size_t init_promo_size, + size_t init_survivor_size, + double gc_pause_goal_sec, + uint gc_cost_ratio); + + bool is_gc_cms_adaptive_size_policy() { + return kind() == _gc_cms_adaptive_size_policy; + } + bool is_gc_ps_adaptive_size_policy() { + return kind() == _gc_ps_adaptive_size_policy; + } + + AdaptivePaddedAverage* avg_minor_pause() const { return _avg_minor_pause; } + AdaptiveWeightedAverage* avg_minor_interval() const { + return _avg_minor_interval; + } + AdaptiveWeightedAverage* avg_minor_gc_cost() const { + return _avg_minor_gc_cost; + } + + AdaptiveWeightedAverage* avg_major_gc_cost() const { + return _avg_major_gc_cost; + } + + AdaptiveWeightedAverage* avg_young_live() const { return _avg_young_live; } + AdaptiveWeightedAverage* avg_eden_live() const { return _avg_eden_live; } + AdaptiveWeightedAverage* avg_old_live() const { return _avg_old_live; } + + AdaptivePaddedAverage* avg_survived() const { return _avg_survived; } + AdaptivePaddedNoZeroDevAverage* avg_pretenured() { return _avg_pretenured; } + + // Methods indicating events of interest to the adaptive size policy, + // called by GC algorithms. It is the responsibility of users of this + // policy to call these methods at the correct times! + virtual void minor_collection_begin(); + virtual void minor_collection_end(GCCause::Cause gc_cause); + virtual LinearLeastSquareFit* minor_pause_old_estimator() const { + return _minor_pause_old_estimator; + } + + LinearLeastSquareFit* minor_pause_young_estimator() { + return _minor_pause_young_estimator; + } + LinearLeastSquareFit* minor_collection_estimator() { + return _minor_collection_estimator; + } + + LinearLeastSquareFit* major_collection_estimator() { + return _major_collection_estimator; + } + + float minor_pause_young_slope() { + return _minor_pause_young_estimator->slope(); + } + + float minor_collection_slope() { return _minor_collection_estimator->slope();} + float major_collection_slope() { return _major_collection_estimator->slope();} + + float minor_pause_old_slope() { + return _minor_pause_old_estimator->slope(); + } + + void set_eden_size(size_t new_size) { + _eden_size = new_size; + } + void set_survivor_size(size_t new_size) { + _survivor_size = new_size; + } + + size_t calculated_eden_size_in_bytes() const { + return _eden_size; + } + + size_t calculated_promo_size_in_bytes() const { + return _promo_size; + } + + size_t calculated_survivor_size_in_bytes() const { + return _survivor_size; + } + + // This is a hint for the heap: we've detected that gc times + // are taking longer than GCTimeLimit allows. + // Most heaps will choose to throw an OutOfMemoryError when + // this occurs but it is up to the heap to request this information + // of the policy + bool gc_time_limit_exceeded() { + return _gc_time_limit_exceeded; + } + void set_gc_time_limit_exceeded(bool v) { + _gc_time_limit_exceeded = v; + } + bool print_gc_time_limit_would_be_exceeded() { + return _print_gc_time_limit_would_be_exceeded; + } + void set_print_gc_time_limit_would_be_exceeded(bool v) { + _print_gc_time_limit_would_be_exceeded = v; + } + + uint gc_time_limit_count() { return _gc_time_limit_count; } + void reset_gc_time_limit_count() { _gc_time_limit_count = 0; } + void inc_gc_time_limit_count() { _gc_time_limit_count++; } + // accessors for flags recording the decisions to resize the + // generations to meet the pause goal. + + int change_young_gen_for_min_pauses() const { + return _change_young_gen_for_min_pauses; + } + void set_change_young_gen_for_min_pauses(int v) { + _change_young_gen_for_min_pauses = v; + } + void set_decrease_for_footprint(int v) { _decrease_for_footprint = v; } + int decrease_for_footprint() const { return _decrease_for_footprint; } + int decide_at_full_gc() { return _decide_at_full_gc; } + void set_decide_at_full_gc(int v) { _decide_at_full_gc = v; } + + // Printing support + virtual bool print_adaptive_size_policy_on(outputStream* st) const; + bool print_adaptive_size_policy_on(outputStream* st, int + tenuring_threshold) const; +}; + +// Class that can be used to print information about the +// adaptive size policy at intervals specified by +// AdaptiveSizePolicyOutputInterval. Only print information +// if an adaptive size policy is in use. +class AdaptiveSizePolicyOutput : StackObj { + AdaptiveSizePolicy* _size_policy; + bool _do_print; + bool print_test(uint count) { + // A count of zero is a special value that indicates that the + // interval test should be ignored. An interval is of zero is + // a special value that indicates that the interval test should + // always fail (never do the print based on the interval test). + return PrintGCDetails && + UseAdaptiveSizePolicy && + (UseParallelGC || UseConcMarkSweepGC) && + (AdaptiveSizePolicyOutputInterval > 0) && + ((count == 0) || + ((count % AdaptiveSizePolicyOutputInterval) == 0)); + } + public: + // The special value of a zero count can be used to ignore + // the count test. + AdaptiveSizePolicyOutput(uint count) { + if (UseAdaptiveSizePolicy && (AdaptiveSizePolicyOutputInterval > 0)) { + CollectedHeap* heap = Universe::heap(); + _size_policy = heap->size_policy(); + _do_print = print_test(count); + } else { + _size_policy = NULL; + _do_print = false; + } + } + AdaptiveSizePolicyOutput(AdaptiveSizePolicy* size_policy, + uint count) : + _size_policy(size_policy) { + if (UseAdaptiveSizePolicy && (AdaptiveSizePolicyOutputInterval > 0)) { + _do_print = print_test(count); + } else { + _do_print = false; + } + } + ~AdaptiveSizePolicyOutput() { + if (_do_print) { + assert(UseAdaptiveSizePolicy, "Should not be in use"); + _size_policy->print_adaptive_size_policy_on(gclog_or_tty); + } + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/ageTable.cpp b/hotspot/src/share/vm/gc_implementation/shared/ageTable.cpp new file mode 100644 index 00000000000..73197bbb1bf --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/ageTable.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* Copyright 1992 Sun Microsystems, Inc. and Stanford University. + See the LICENSE file for license information. */ + +# include "incls/_precompiled.incl" +# include "incls/_ageTable.cpp.incl" + +ageTable::ageTable(bool global) { + + clear(); + + if (UsePerfData && global) { + + ResourceMark rm; + EXCEPTION_MARK; + + const char* agetable_ns = "generation.0.agetable"; + const char* bytes_ns = PerfDataManager::name_space(agetable_ns, "bytes"); + + for(int age = 0; age < table_size; age ++) { + char age_name[10]; + jio_snprintf(age_name, sizeof(age_name), "%2.2d", age); + const char* cname = PerfDataManager::counter_name(bytes_ns, age_name); + _perf_sizes[age] = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, + CHECK); + } + + const char* cname = PerfDataManager::counter_name(agetable_ns, "size"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None, + table_size, CHECK); + } +} + +void ageTable::clear() { + for (size_t* p = sizes; p < sizes + table_size; ++p) { + *p = 0; + } +} + +void ageTable::merge(ageTable* subTable) { + for (int i = 0; i < table_size; i++) { + sizes[i]+= subTable->sizes[i]; + } +} + +int ageTable::compute_tenuring_threshold(size_t survivor_capacity) { + size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100); + size_t total = 0; + int age = 1; + assert(sizes[0] == 0, "no objects with age zero should be recorded"); + while (age < table_size) { + total += sizes[age]; + // check if including objects of age 'age' made us pass the desired + // size, if so 'age' is the new threshold + if (total > desired_survivor_size) break; + age++; + } + int result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold; + + if (PrintTenuringDistribution || UsePerfData) { + + if (PrintTenuringDistribution) { + gclog_or_tty->cr(); + gclog_or_tty->print_cr("Desired survivor size %ld bytes, new threshold %d (max %d)", + desired_survivor_size*oopSize, result, MaxTenuringThreshold); + } + + total = 0; + age = 1; + while (age < table_size) { + total += sizes[age]; + if (sizes[age] > 0) { + if (PrintTenuringDistribution) { + gclog_or_tty->print_cr("- age %3d: %10ld bytes, %10ld total", + age, sizes[age]*oopSize, total*oopSize); + } + } + if (UsePerfData) { + _perf_sizes[age]->set_value(sizes[age]*oopSize); + } + age++; + } + if (UsePerfData) { + SharedHeap* sh = SharedHeap::heap(); + CollectorPolicy* policy = sh->collector_policy(); + GCPolicyCounters* gc_counters = policy->counters(); + gc_counters->tenuring_threshold()->set_value(result); + gc_counters->desired_survivor_size()->set_value( + desired_survivor_size*oopSize); + } + } + + return result; +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/ageTable.hpp b/hotspot/src/share/vm/gc_implementation/shared/ageTable.hpp new file mode 100644 index 00000000000..932b792b914 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/ageTable.hpp @@ -0,0 +1,65 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* Copyright 1992 Sun Microsystems, Inc. and Stanford University. + See the LICENSE file for license information. */ + +// Age table for adaptive feedback-mediated tenuring (scavenging) +// +// Note: all sizes are in oops + +class ageTable VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + + public: + // constants + enum { table_size = markOopDesc::max_age + 1 }; + + // instance variables + size_t sizes[table_size]; + + // constructor. "global" indicates that this is the global age table + // (as opposed to gc-thread-local) + ageTable(bool global = true); + + // clear table + void clear(); + + // add entry + void add(oop p, size_t oop_size) { + int age = p->age(); + assert(age > 0 && age < table_size, "invalid age of object"); + sizes[age] += oop_size; + } + + // Merge another age table with the current one. Used + // for parallel young generation gc. + void merge(ageTable* subTable); + + // calculate new tenuring threshold based on age information + int compute_tenuring_threshold(size_t survivor_capacity); + + private: + PerfVariable* _perf_sizes[table_size]; +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/cSpaceCounters.cpp b/hotspot/src/share/vm/gc_implementation/shared/cSpaceCounters.cpp new file mode 100644 index 00000000000..69f4b2094b8 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/cSpaceCounters.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_cSpaceCounters.cpp.incl" + +CSpaceCounters::CSpaceCounters(const char* name, int ordinal, size_t max_size, + ContiguousSpace* s, GenerationCounters* gc) : + _space(s) { + + if (UsePerfData) { + EXCEPTION_MARK; + ResourceMark rm; + + const char* cns = PerfDataManager::name_space(gc->name_space(), "space", + ordinal); + + _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1); + strcpy(_name_space, cns); + + const char* cname = PerfDataManager::counter_name(_name_space, "name"); + PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "maxCapacity"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, + (jlong)max_size, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "capacity"); + _capacity = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, + _space->capacity(), CHECK); + + cname = PerfDataManager::counter_name(_name_space, "used"); + _used = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + new ContiguousSpaceUsedHelper(_space), + CHECK); + + cname = PerfDataManager::counter_name(_name_space, "initCapacity"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, + _space->capacity(), CHECK); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/cSpaceCounters.hpp b/hotspot/src/share/vm/gc_implementation/shared/cSpaceCounters.hpp new file mode 100644 index 00000000000..0b578687ae9 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/cSpaceCounters.hpp @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A CSpaceCounters is a holder class for performance counters +// that track a space; + +class CSpaceCounters: public CHeapObj { + friend class VMStructs; + + private: + PerfVariable* _capacity; + PerfVariable* _used; + + // Constant PerfData types don't need to retain a reference. + // However, it's a good idea to document them here. + // PerfConstant* _size; + + ContiguousSpace* _space; + char* _name_space; + + public: + + CSpaceCounters(const char* name, int ordinal, size_t max_size, + ContiguousSpace* s, GenerationCounters* gc); + + ~CSpaceCounters() { + if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); + } + + inline void update_capacity() { + _capacity->set_value(_space->capacity()); + } + + inline void update_used() { + _used->set_value(_space->used()); + } + + inline void update_all() { + update_used(); + update_capacity(); + } + + const char* name_space() const { return _name_space; } +}; + +class ContiguousSpaceUsedHelper : public PerfLongSampleHelper { + private: + ContiguousSpace* _space; + + public: + ContiguousSpaceUsedHelper(ContiguousSpace* space) : _space(space) { } + + inline jlong take_sample() { + return _space->used(); + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/collectorCounters.cpp b/hotspot/src/share/vm/gc_implementation/shared/collectorCounters.cpp new file mode 100644 index 00000000000..9a3ec7b5f8a --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/collectorCounters.cpp @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_collectorCounters.cpp.incl" + +CollectorCounters::CollectorCounters(const char* name, int ordinal) { + + if (UsePerfData) { + EXCEPTION_MARK; + ResourceMark rm; + + const char* cns = PerfDataManager::name_space("collector", ordinal); + + _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1); + strcpy(_name_space, cns); + + char* cname = PerfDataManager::counter_name(_name_space, "name"); + PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "invocations"); + _invocations = PerfDataManager::create_counter(SUN_GC, cname, + PerfData::U_Events, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "time"); + _time = PerfDataManager::create_counter(SUN_GC, cname, PerfData::U_Ticks, + CHECK); + + cname = PerfDataManager::counter_name(_name_space, "lastEntryTime"); + _last_entry_time = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, + CHECK); + + cname = PerfDataManager::counter_name(_name_space, "lastExitTime"); + _last_exit_time = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, + CHECK); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/collectorCounters.hpp b/hotspot/src/share/vm/gc_implementation/shared/collectorCounters.hpp new file mode 100644 index 00000000000..0e03bf7ba7b --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/collectorCounters.hpp @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// CollectorCounters is a holder class for performance counters +// that track a collector + +class CollectorCounters: public CHeapObj { + friend class VMStructs; + + private: + PerfCounter* _invocations; + PerfCounter* _time; + PerfVariable* _last_entry_time; + PerfVariable* _last_exit_time; + + // Constant PerfData types don't need to retain a reference. + // However, it's a good idea to document them here. + // PerfStringConstant* _name; + + char* _name_space; + + public: + + CollectorCounters(const char* name, int ordinal); + + ~CollectorCounters() { + if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); + } + + inline PerfCounter* invocation_counter() const { return _invocations; } + + inline PerfCounter* time_counter() const { return _time; } + + inline PerfVariable* last_entry_counter() const { return _last_entry_time; } + + inline PerfVariable* last_exit_counter() const { return _last_exit_time; } + + const char* name_space() const { return _name_space; } +}; + +class TraceCollectorStats: public PerfTraceTimedEvent { + + protected: + CollectorCounters* _c; + + public: + inline TraceCollectorStats(CollectorCounters* c) : + PerfTraceTimedEvent(c->time_counter(), c->invocation_counter()), + _c(c) { + + if (UsePerfData) { + _c->last_entry_counter()->set_value(os::elapsed_counter()); + } + } + + inline ~TraceCollectorStats() { + if (UsePerfData) _c->last_exit_counter()->set_value(os::elapsed_counter()); + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/gSpaceCounters.cpp b/hotspot/src/share/vm/gc_implementation/shared/gSpaceCounters.cpp new file mode 100644 index 00000000000..ffb9c866db3 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/gSpaceCounters.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_gSpaceCounters.cpp.incl" + +GSpaceCounters::GSpaceCounters(const char* name, int ordinal, size_t max_size, + Generation* g, GenerationCounters* gc, + bool sampled) : + _gen(g) { + + if (UsePerfData) { + EXCEPTION_MARK; + ResourceMark rm; + + const char* cns = PerfDataManager::name_space(gc->name_space(), "space", + ordinal); + + _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1); + strcpy(_name_space, cns); + + const char* cname = PerfDataManager::counter_name(_name_space, "name"); + PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "maxCapacity"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, + (jlong)max_size, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "capacity"); + _capacity = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, + _gen->capacity(), CHECK); + + cname = PerfDataManager::counter_name(_name_space, "used"); + if (sampled) { + _used = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + new GenerationUsedHelper(_gen), + CHECK); + } + else { + _used = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + (jlong)0, CHECK); + } + + cname = PerfDataManager::counter_name(_name_space, "initCapacity"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, + _gen->capacity(), CHECK); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/gSpaceCounters.hpp b/hotspot/src/share/vm/gc_implementation/shared/gSpaceCounters.hpp new file mode 100644 index 00000000000..9ea1cecfbcb --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/gSpaceCounters.hpp @@ -0,0 +1,102 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A GSpaceCounter is a holder class for performance counters +// that track a space; + +class GSpaceCounters: public CHeapObj { + friend class VMStructs; + + private: + PerfVariable* _capacity; + PerfVariable* _used; + + // Constant PerfData types don't need to retain a reference. + // However, it's a good idea to document them here. + // PerfConstant* _size; + + Generation* _gen; + char* _name_space; + + public: + + GSpaceCounters(const char* name, int ordinal, size_t max_size, Generation* g, + GenerationCounters* gc, bool sampled=true); + + ~GSpaceCounters() { + if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); + } + + inline void update_capacity() { + _capacity->set_value(_gen->capacity()); + } + + inline void update_used() { + _used->set_value(_gen->used()); + } + + // special version of update_used() to allow the used value to be + // passed as a parameter. This method can can be used in cases were + // the utilization is already known and/or when the _gen->used() + // method is known to be expensive and we want to avoid unnecessary + // calls to it. + // + inline void update_used(size_t used) { + _used->set_value(used); + } + + inline void inc_used(size_t size) { + _used->inc(size); + } + + debug_only( + // for security reasons, we do not allow arbitrary reads from + // the counters as they may live in shared memory. + jlong used() { + return _used->get_value(); + } + jlong capacity() { + return _used->get_value(); + } + ) + + inline void update_all() { + update_used(); + update_capacity(); + } + + const char* name_space() const { return _name_space; } +}; + +class GenerationUsedHelper : public PerfLongSampleHelper { + private: + Generation* _gen; + + public: + GenerationUsedHelper(Generation* g) : _gen(g) { } + + inline jlong take_sample() { + return _gen->used(); + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcAdaptivePolicyCounters.cpp b/hotspot/src/share/vm/gc_implementation/shared/gcAdaptivePolicyCounters.cpp new file mode 100644 index 00000000000..6a3678c2f97 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/gcAdaptivePolicyCounters.cpp @@ -0,0 +1,226 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_gcAdaptivePolicyCounters.cpp.incl" + +// This class keeps statistical information and computes the +// size of the heap. + +GCAdaptivePolicyCounters::GCAdaptivePolicyCounters(const char* name, + int collectors, + int generations, + AdaptiveSizePolicy* size_policy_arg) + : GCPolicyCounters(name, collectors, generations), + _size_policy(size_policy_arg) { + if (UsePerfData) { + EXCEPTION_MARK; + ResourceMark rm; + + const char* cname = PerfDataManager::counter_name(name_space(), "edenSize"); + _eden_size_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, _size_policy->calculated_eden_size_in_bytes(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "promoSize"); + _promo_size_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, size_policy()->calculated_promo_size_in_bytes(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "youngCapacity"); + size_t young_capacity_in_bytes = + _size_policy->calculated_eden_size_in_bytes() + + _size_policy->calculated_survivor_size_in_bytes(); + _young_capacity_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, young_capacity_in_bytes, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgSurvivedAvg"); + _avg_survived_avg_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, size_policy()->calculated_survivor_size_in_bytes(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgSurvivedDev"); + _avg_survived_dev_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) 0 , CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgSurvivedPaddedAvg"); + _avg_survived_padded_avg_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + size_policy()->calculated_survivor_size_in_bytes(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgMinorPauseTime"); + _avg_minor_pause_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, (jlong) _size_policy->_avg_minor_pause->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgMinorIntervalTime"); + _avg_minor_interval_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) _size_policy->_avg_minor_interval->average(), + CHECK); + +#ifdef NOT_PRODUCT + // This is a counter for the most recent minor pause time + // (the last sample, not the average). It is useful for + // verifying the average pause time but not worth putting + // into the product. + cname = PerfDataManager::counter_name(name_space(), "minorPauseTime"); + _minor_pause_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, (jlong) _size_policy->_avg_minor_pause->last_sample(), + CHECK); +#endif + + cname = PerfDataManager::counter_name(name_space(), "minorGcCost"); + _minor_gc_cost_counter = PerfDataManager::create_variable(SUN_GC, + cname, + PerfData::U_Ticks, + (jlong) _size_policy->minor_gc_cost(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "mutatorCost"); + _mutator_cost_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Ticks, (jlong) _size_policy->mutator_cost(), CHECK); + + cname = PerfDataManager::counter_name(name_space(), "survived"); + _survived_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) 0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "promoted"); + _promoted_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) 0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgYoungLive"); + _avg_young_live_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) size_policy()->avg_young_live()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "avgOldLive"); + _avg_old_live_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, (jlong) size_policy()->avg_old_live()->average(), + CHECK); + + cname = PerfDataManager::counter_name(name_space(), "survivorOverflowed"); + _survivor_overflowed_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Events, (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), + "decrementTenuringThresholdForGcCost"); + _decrement_tenuring_threshold_for_gc_cost_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events, + (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), + "incrementTenuringThresholdForGcCost"); + _increment_tenuring_threshold_for_gc_cost_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events, + (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), + "decrementTenuringThresholdForSurvivorLimit"); + _decrement_tenuring_threshold_for_survivor_limit_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events, + (jlong)0, CHECK); + cname = PerfDataManager::counter_name(name_space(), + "changeYoungGenForMinPauses"); + _change_young_gen_for_min_pauses_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events, + (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), + "changeOldGenForMajPauses"); + _change_old_gen_for_maj_pauses_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events, + (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), + "increaseOldGenForThroughput"); + _change_old_gen_for_throughput_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events, + (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), + "increaseYoungGenForThroughput"); + _change_young_gen_for_throughput_counter = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events, + (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), + "decreaseForFootprint"); + _decrease_for_footprint_counter = + PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Events, (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "decideAtFullGc"); + _decide_at_full_gc_counter = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_None, (jlong)0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "minorPauseYoungSlope"); + _minor_pause_young_slope_counter = + PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_None, (jlong) 0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "majorCollectionSlope"); + _major_collection_slope_counter = + PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_None, (jlong) 0, CHECK); + + cname = PerfDataManager::counter_name(name_space(), "minorCollectionSlope"); + _minor_collection_slope_counter = + PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_None, (jlong) 0, CHECK); + } +} + +void GCAdaptivePolicyCounters::update_counters_from_policy() { + if (UsePerfData && (size_policy() != NULL)) { + update_avg_minor_pause_counter(); + update_avg_minor_interval_counter(); +#ifdef NOT_PRODUCT + update_minor_pause_counter(); +#endif + update_minor_gc_cost_counter(); + update_avg_young_live_counter(); + + update_survivor_size_counters(); + update_avg_survived_avg_counters(); + update_avg_survived_dev_counters(); + update_avg_survived_padded_avg_counters(); + + update_change_old_gen_for_throughput(); + update_change_young_gen_for_throughput(); + update_decrease_for_footprint(); + update_change_young_gen_for_min_pauses(); + update_change_old_gen_for_maj_pauses(); + + update_minor_pause_young_slope_counter(); + update_minor_collection_slope_counter(); + update_major_collection_slope_counter(); + } +} + +void GCAdaptivePolicyCounters::update_counters() { + if (UsePerfData) { + update_counters_from_policy(); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcAdaptivePolicyCounters.hpp b/hotspot/src/share/vm/gc_implementation/shared/gcAdaptivePolicyCounters.hpp new file mode 100644 index 00000000000..f3539ec1b22 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/gcAdaptivePolicyCounters.hpp @@ -0,0 +1,224 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This class keeps statistical information and computes the +// size of the heap. + +class GCAdaptivePolicyCounters : public GCPolicyCounters { + protected: + PerfVariable* _eden_size_counter; + PerfVariable* _promo_size_counter; + + PerfVariable* _young_capacity_counter; + + PerfVariable* _minor_gc_cost_counter; + PerfVariable* _major_gc_cost_counter; + PerfVariable* _mutator_cost_counter; + + PerfVariable* _avg_young_live_counter; + PerfVariable* _avg_old_live_counter; + + PerfVariable* _avg_minor_pause_counter; + PerfVariable* _avg_minor_interval_counter; + +#ifdef NOT_PRODUCT + PerfVariable* _minor_pause_counter; +#endif + + PerfVariable* _change_young_gen_for_min_pauses_counter; + PerfVariable* _change_young_gen_for_throughput_counter; + PerfVariable* _change_old_gen_for_maj_pauses_counter; + PerfVariable* _change_old_gen_for_throughput_counter; + PerfVariable* _decrease_for_footprint_counter; + + PerfVariable* _minor_pause_young_slope_counter; + PerfVariable* _major_pause_old_slope_counter; + + PerfVariable* _decide_at_full_gc_counter; + + PerfVariable* _survived_counter; + PerfVariable* _promoted_counter; + + PerfVariable* _avg_survived_avg_counter; + PerfVariable* _avg_survived_dev_counter; + PerfVariable* _avg_survived_padded_avg_counter; + + PerfVariable* _survivor_overflowed_counter; + PerfVariable* _increment_tenuring_threshold_for_gc_cost_counter; + PerfVariable* _decrement_tenuring_threshold_for_gc_cost_counter; + PerfVariable* _decrement_tenuring_threshold_for_survivor_limit_counter; + + PerfVariable* _minor_collection_slope_counter; + PerfVariable* _major_collection_slope_counter; + + AdaptiveSizePolicy* _size_policy; + + inline void update_eden_size() { + size_t eden_size_in_bytes = size_policy()->calculated_eden_size_in_bytes(); + _eden_size_counter->set_value(eden_size_in_bytes); + } + + inline void update_promo_size() { + _promo_size_counter->set_value( + size_policy()->calculated_promo_size_in_bytes()); + } + + inline void update_avg_minor_pause_counter() { + _avg_minor_pause_counter->set_value((jlong) + (size_policy()->avg_minor_pause()->average() * 1000.0)); + } + inline void update_avg_minor_interval_counter() { + _avg_minor_interval_counter->set_value((jlong) + (size_policy()->avg_minor_interval()->average() * 1000.0)); + } + +#ifdef NOT_PRODUCT + inline void update_minor_pause_counter() { + _minor_pause_counter->set_value((jlong) + (size_policy()->avg_minor_pause()->last_sample() * 1000.0)); + } +#endif + inline void update_minor_gc_cost_counter() { + _minor_gc_cost_counter->set_value((jlong) + (size_policy()->minor_gc_cost() * 100.0)); + } + + inline void update_avg_young_live_counter() { + _avg_young_live_counter->set_value( + (jlong)(size_policy()->avg_young_live()->average()) + ); + } + + inline void update_avg_survived_avg_counters() { + _avg_survived_avg_counter->set_value( + (jlong)(size_policy()->_avg_survived->average()) + ); + } + inline void update_avg_survived_dev_counters() { + _avg_survived_dev_counter->set_value( + (jlong)(size_policy()->_avg_survived->deviation()) + ); + } + inline void update_avg_survived_padded_avg_counters() { + _avg_survived_padded_avg_counter->set_value( + (jlong)(size_policy()->_avg_survived->padded_average()) + ); + } + + inline void update_change_old_gen_for_throughput() { + _change_old_gen_for_throughput_counter->set_value( + size_policy()->change_old_gen_for_throughput()); + } + inline void update_change_young_gen_for_throughput() { + _change_young_gen_for_throughput_counter->set_value( + size_policy()->change_young_gen_for_throughput()); + } + inline void update_decrease_for_footprint() { + _decrease_for_footprint_counter->set_value( + size_policy()->decrease_for_footprint()); + } + + inline void update_decide_at_full_gc_counter() { + _decide_at_full_gc_counter->set_value( + size_policy()->decide_at_full_gc()); + } + + inline void update_minor_pause_young_slope_counter() { + _minor_pause_young_slope_counter->set_value( + (jlong)(size_policy()->minor_pause_young_slope() * 1000) + ); + } + + virtual void update_counters_from_policy(); + + protected: + virtual AdaptiveSizePolicy* size_policy() { return _size_policy; } + + public: + GCAdaptivePolicyCounters(const char* name, + int collectors, + int generations, + AdaptiveSizePolicy* size_policy); + + inline void update_survived(size_t survived) { + _survived_counter->set_value(survived); + } + inline void update_promoted(size_t promoted) { + _promoted_counter->set_value(promoted); + } + inline void update_young_capacity(size_t size_in_bytes) { + _young_capacity_counter->set_value(size_in_bytes); + } + + virtual void update_counters(); + + inline void update_survivor_size_counters() { + desired_survivor_size()->set_value( + size_policy()->calculated_survivor_size_in_bytes()); + } + inline void update_survivor_overflowed(bool survivor_overflowed) { + _survivor_overflowed_counter->set_value(survivor_overflowed); + } + inline void update_tenuring_threshold(int threshold) { + tenuring_threshold()->set_value(threshold); + } + inline void update_increment_tenuring_threshold_for_gc_cost() { + _increment_tenuring_threshold_for_gc_cost_counter->set_value( + size_policy()->increment_tenuring_threshold_for_gc_cost()); + } + inline void update_decrement_tenuring_threshold_for_gc_cost() { + _decrement_tenuring_threshold_for_gc_cost_counter->set_value( + size_policy()->decrement_tenuring_threshold_for_gc_cost()); + } + inline void update_decrement_tenuring_threshold_for_survivor_limit() { + _decrement_tenuring_threshold_for_survivor_limit_counter->set_value( + size_policy()->decrement_tenuring_threshold_for_survivor_limit()); + } + inline void update_change_young_gen_for_min_pauses() { + _change_young_gen_for_min_pauses_counter->set_value( + size_policy()->change_young_gen_for_min_pauses()); + } + inline void update_change_old_gen_for_maj_pauses() { + _change_old_gen_for_maj_pauses_counter->set_value( + size_policy()->change_old_gen_for_maj_pauses()); + } + + inline void update_minor_collection_slope_counter() { + _minor_collection_slope_counter->set_value( + (jlong)(size_policy()->minor_collection_slope() * 1000) + ); + } + + inline void update_major_collection_slope_counter() { + _major_collection_slope_counter->set_value( + (jlong)(size_policy()->major_collection_slope() * 1000) + ); + } + + void set_size_policy(AdaptiveSizePolicy* v) { _size_policy = v; } + + virtual GCPolicyCounters::Name kind() const { + return GCPolicyCounters::GCAdaptivePolicyCountersKind; + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcPolicyCounters.cpp b/hotspot/src/share/vm/gc_implementation/shared/gcPolicyCounters.cpp new file mode 100644 index 00000000000..9a8e2321793 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/gcPolicyCounters.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_gcPolicyCounters.cpp.incl" + +GCPolicyCounters::GCPolicyCounters(const char* name, int collectors, + int generations) { + + if (UsePerfData) { + EXCEPTION_MARK; + ResourceMark rm; + + _name_space = "policy"; + + char* cname = PerfDataManager::counter_name(_name_space, "name"); + PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "collectors"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None, + collectors, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "generations"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None, + generations, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "maxTenuringThreshold"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None, + MaxTenuringThreshold, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "tenuringThreshold"); + _tenuring_threshold = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, + MaxTenuringThreshold, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "desiredSurvivorSize"); + _desired_survivor_size = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + CHECK); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcPolicyCounters.hpp b/hotspot/src/share/vm/gc_implementation/shared/gcPolicyCounters.hpp new file mode 100644 index 00000000000..2bfd5c659cc --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/gcPolicyCounters.hpp @@ -0,0 +1,71 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// GCPolicyCounters is a holder class for performance counters +// that track a generation + +class GCPolicyCounters: public CHeapObj { + friend class VMStructs; + + private: + + // Constant PerfData types don't need to retain a reference. + // However, it's a good idea to document them here. + // PerfStringConstant* _name; + // PerfStringConstant* _collector_size; + // PerfStringConstant* _generation_size; + + PerfVariable* _tenuring_threshold; + PerfVariable* _desired_survivor_size; + + const char* _name_space; + + public: + + enum Name { + NONE, + GCPolicyCountersKind, + GCAdaptivePolicyCountersKind, + PSGCAdaptivePolicyCountersKind, + CMSGCAdaptivePolicyCountersKind + }; + + GCPolicyCounters(const char* name, int collectors, int generations); + + inline PerfVariable* tenuring_threshold() const { + return _tenuring_threshold; + } + + inline PerfVariable* desired_survivor_size() const { + return _desired_survivor_size; + } + + const char* name_space() const { return _name_space; } + + virtual void update_counters() {} + + virtual GCPolicyCounters::Name kind() const { + return GCPolicyCounters::GCPolicyCountersKind; + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcStats.cpp b/hotspot/src/share/vm/gc_implementation/shared/gcStats.cpp new file mode 100644 index 00000000000..c06628ba0cd --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/gcStats.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_gcStats.cpp.incl" + +GCStats::GCStats() { + _avg_promoted = new AdaptivePaddedNoZeroDevAverage( + AdaptiveSizePolicyWeight, + PromotedPadding); +} + +CMSGCStats::CMSGCStats() { + _avg_promoted = new AdaptivePaddedNoZeroDevAverage( + CMSExpAvgFactor, + PromotedPadding); +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcStats.hpp b/hotspot/src/share/vm/gc_implementation/shared/gcStats.hpp new file mode 100644 index 00000000000..65c52d04125 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/gcStats.hpp @@ -0,0 +1,63 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class GCStats : public CHeapObj { + protected: + // Avg amount promoted; used for avoiding promotion undo + // This class does not update deviations if the sample is zero. + AdaptivePaddedNoZeroDevAverage* _avg_promoted; + + public: + GCStats(); + + enum Name { + GCStatsKind, + CMSGCStatsKind + }; + + virtual Name kind() { + return GCStatsKind; + } + + AdaptivePaddedNoZeroDevAverage* avg_promoted() const { return _avg_promoted; } + + // Average in bytes + size_t average_promoted_in_bytes() const { + return (size_t)_avg_promoted->average(); + } + + // Padded average in bytes + size_t padded_average_promoted_in_bytes() const { + return (size_t)_avg_promoted->padded_average(); + } +}; + +class CMSGCStats : public GCStats { + public: + CMSGCStats(); + + virtual Name kind() { + return CMSGCStatsKind; + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcUtil.cpp b/hotspot/src/share/vm/gc_implementation/shared/gcUtil.cpp new file mode 100644 index 00000000000..9ae5e4a0d29 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/gcUtil.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_gcUtil.cpp.incl" + +// Catch-all file for utility classes + +float AdaptiveWeightedAverage::compute_adaptive_average(float new_sample, + float average) { + // We smooth the samples by not using weight() directly until we've + // had enough data to make it meaningful. We'd like the first weight + // used to be 1, the second to be 1/2, etc until we have 100/weight + // samples. + unsigned count_weight = 100/count(); + unsigned adaptive_weight = (MAX2(weight(), count_weight)); + + float new_avg = exp_avg(average, new_sample, adaptive_weight); + + return new_avg; +} + +void AdaptiveWeightedAverage::sample(float new_sample) { + increment_count(); + assert(count() != 0, + "Wraparound -- history would be incorrectly discarded"); + + // Compute the new weighted average + float new_avg = compute_adaptive_average(new_sample, average()); + set_average(new_avg); + _last_sample = new_sample; +} + +void AdaptivePaddedAverage::sample(float new_sample) { + // Compute our parent classes sample information + AdaptiveWeightedAverage::sample(new_sample); + + // Now compute the deviation and the new padded sample + float new_avg = average(); + float new_dev = compute_adaptive_average(fabsd(new_sample - new_avg), + deviation()); + set_deviation(new_dev); + set_padded_average(new_avg + padding() * new_dev); + _last_sample = new_sample; +} + +void AdaptivePaddedNoZeroDevAverage::sample(float new_sample) { + // Compute our parent classes sample information + AdaptiveWeightedAverage::sample(new_sample); + + float new_avg = average(); + if (new_sample != 0) { + // We only create a new deviation if the sample is non-zero + float new_dev = compute_adaptive_average(fabsd(new_sample - new_avg), + deviation()); + + set_deviation(new_dev); + } + set_padded_average(new_avg + padding() * deviation()); + _last_sample = new_sample; +} + +LinearLeastSquareFit::LinearLeastSquareFit(unsigned weight) : + _sum_x(0), _sum_y(0), _sum_xy(0), + _mean_x(weight), _mean_y(weight) {} + +void LinearLeastSquareFit::update(double x, double y) { + _sum_x = _sum_x + x; + _sum_x_squared = _sum_x_squared + x * x; + _sum_y = _sum_y + y; + _sum_xy = _sum_xy + x * y; + _mean_x.sample(x); + _mean_y.sample(y); + assert(_mean_x.count() == _mean_y.count(), "Incorrect count"); + if ( _mean_x.count() > 1 ) { + double slope_denominator; + slope_denominator = (_mean_x.count() * _sum_x_squared - _sum_x * _sum_x); + // Some tolerance should be injected here. A denominator that is + // nearly 0 should be avoided. + + if (slope_denominator != 0.0) { + double slope_numerator; + slope_numerator = (_mean_x.count() * _sum_xy - _sum_x * _sum_y); + _slope = slope_numerator / slope_denominator; + + // The _mean_y and _mean_x are decaying averages and can + // be used to discount earlier data. If they are used, + // first consider whether all the quantities should be + // kept as decaying averages. + // _intercept = _mean_y.average() - _slope * _mean_x.average(); + _intercept = (_sum_y - _slope * _sum_x) / ((double) _mean_x.count()); + } + } +} + +double LinearLeastSquareFit::y(double x) { + double new_y; + + if ( _mean_x.count() > 1 ) { + new_y = (_intercept + _slope * x); + return new_y; + } else { + return _mean_y.average(); + } +} + +// Both decrement_will_decrease() and increment_will_decrease() return +// true for a slope of 0. That is because a change is necessary before +// a slope can be calculated and a 0 slope will, in general, indicate +// that no calculation of the slope has yet been done. Returning true +// for a slope equal to 0 reflects the intuitive expectation of the +// dependence on the slope. Don't use the complement of these functions +// since that untuitive expectation is not built into the complement. +bool LinearLeastSquareFit::decrement_will_decrease() { + return (_slope >= 0.00); +} + +bool LinearLeastSquareFit::increment_will_decrease() { + return (_slope <= 0.00); +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcUtil.hpp b/hotspot/src/share/vm/gc_implementation/shared/gcUtil.hpp new file mode 100644 index 00000000000..83fdf871de7 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/gcUtil.hpp @@ -0,0 +1,176 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Catch-all file for utility classes + +// A weighted average maintains a running, weighted average +// of some float value (templates would be handy here if we +// need different types). +// +// The average is adaptive in that we smooth it for the +// initial samples; we don't use the weight until we have +// enough samples for it to be meaningful. +// +// This serves as our best estimate of a future unknown. +// +class AdaptiveWeightedAverage : public CHeapObj { + private: + float _average; // The last computed average + unsigned _sample_count; // How often we've sampled this average + unsigned _weight; // The weight used to smooth the averages + // A higher weight favors the most + // recent data. + + protected: + float _last_sample; // The last value sampled. + + void increment_count() { _sample_count++; } + void set_average(float avg) { _average = avg; } + + // Helper function, computes an adaptive weighted average + // given a sample and the last average + float compute_adaptive_average(float new_sample, float average); + + public: + // Input weight must be between 0 and 100 + AdaptiveWeightedAverage(unsigned weight) : + _average(0.0), _sample_count(0), _weight(weight), _last_sample(0.0) { + } + + // Accessors + float average() const { return _average; } + unsigned weight() const { return _weight; } + unsigned count() const { return _sample_count; } + float last_sample() const { return _last_sample; } + + // Update data with a new sample. + void sample(float new_sample); + + static inline float exp_avg(float avg, float sample, + unsigned int weight) { + assert(0 <= weight && weight <= 100, "weight must be a percent"); + return (100.0F - weight) * avg / 100.0F + weight * sample / 100.0F; + } + static inline size_t exp_avg(size_t avg, size_t sample, + unsigned int weight) { + // Convert to float and back to avoid integer overflow. + return (size_t)exp_avg((float)avg, (float)sample, weight); + } +}; + + +// A weighted average that includes a deviation from the average, +// some multiple of which is added to the average. +// +// This serves as our best estimate of an upper bound on a future +// unknown. +class AdaptivePaddedAverage : public AdaptiveWeightedAverage { + private: + float _padded_avg; // The last computed padded average + float _deviation; // Running deviation from the average + unsigned _padding; // A multiple which, added to the average, + // gives us an upper bound guess. + + protected: + void set_padded_average(float avg) { _padded_avg = avg; } + void set_deviation(float dev) { _deviation = dev; } + + public: + AdaptivePaddedAverage() : + AdaptiveWeightedAverage(0), + _padded_avg(0.0), _deviation(0.0), _padding(0) {} + + AdaptivePaddedAverage(unsigned weight, unsigned padding) : + AdaptiveWeightedAverage(weight), + _padded_avg(0.0), _deviation(0.0), _padding(padding) {} + + // Placement support + void* operator new(size_t ignored, void* p) { return p; } + // Allocator + void* operator new(size_t size) { return CHeapObj::operator new(size); } + + // Accessor + float padded_average() const { return _padded_avg; } + float deviation() const { return _deviation; } + unsigned padding() const { return _padding; } + + // Override + void sample(float new_sample); +}; + +// A weighted average that includes a deviation from the average, +// some multiple of which is added to the average. +// +// This serves as our best estimate of an upper bound on a future +// unknown. +// A special sort of padded average: it doesn't update deviations +// if the sample is zero. The average is allowed to change. We're +// preventing the zero samples from drastically changing our padded +// average. +class AdaptivePaddedNoZeroDevAverage : public AdaptivePaddedAverage { +public: + AdaptivePaddedNoZeroDevAverage(unsigned weight, unsigned padding) : + AdaptivePaddedAverage(weight, padding) {} + // Override + void sample(float new_sample); +}; +// Use a least squares fit to a set of data to generate a linear +// equation. +// y = intercept + slope * x + +class LinearLeastSquareFit : public CHeapObj { + double _sum_x; // sum of all independent data points x + double _sum_x_squared; // sum of all independent data points x**2 + double _sum_y; // sum of all dependent data points y + double _sum_xy; // sum of all x * y. + double _intercept; // constant term + double _slope; // slope + // The weighted averages are not currently used but perhaps should + // be used to get decaying averages. + AdaptiveWeightedAverage _mean_x; // weighted mean of independent variable + AdaptiveWeightedAverage _mean_y; // weighted mean of dependent variable + + public: + LinearLeastSquareFit(unsigned weight); + void update(double x, double y); + double y(double x); + double slope() { return _slope; } + // Methods to decide if a change in the dependent variable will + // achive a desired goal. Note that these methods are not + // complementary and both are needed. + bool decrement_will_decrease(); + bool increment_will_decrease(); +}; + +class GCPauseTimer : StackObj { + elapsedTimer* _timer; + public: + GCPauseTimer(elapsedTimer* timer) { + _timer = timer; + _timer->stop(); + } + ~GCPauseTimer() { + _timer->start(); + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/generationCounters.cpp b/hotspot/src/share/vm/gc_implementation/shared/generationCounters.cpp new file mode 100644 index 00000000000..6e457d3d009 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/generationCounters.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_generationCounters.cpp.incl" + + +GenerationCounters::GenerationCounters(const char* name, + int ordinal, int spaces, + VirtualSpace* v): + _virtual_space(v) { + + if (UsePerfData) { + + EXCEPTION_MARK; + ResourceMark rm; + + const char* cns = PerfDataManager::name_space("generation", ordinal); + + _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1); + strcpy(_name_space, cns); + + const char* cname = PerfDataManager::counter_name(_name_space, "name"); + PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "spaces"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None, + spaces, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "minCapacity"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, + _virtual_space->committed_size(), CHECK); + + cname = PerfDataManager::counter_name(_name_space, "maxCapacity"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, + _virtual_space->reserved_size(), CHECK); + + cname = PerfDataManager::counter_name(_name_space, "capacity"); + _current_size = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, + _virtual_space->committed_size(), CHECK); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/generationCounters.hpp b/hotspot/src/share/vm/gc_implementation/shared/generationCounters.hpp new file mode 100644 index 00000000000..7789e9b6204 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/generationCounters.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A GenerationCounter is a holder class for performance counters +// that track a generation + +class GenerationCounters: public CHeapObj { + friend class VMStructs; + + protected: + PerfVariable* _current_size; + VirtualSpace* _virtual_space; + + // Constant PerfData types don't need to retain a reference. + // However, it's a good idea to document them here. + // PerfStringConstant* _name; + // PerfConstant* _min_size; + // PerfConstant* _max_size; + // PerfConstant* _spaces; + + char* _name_space; + + // This constructor is only meant for use with the PSGenerationCounters + // constructor. The need for such an constructor should be eliminated + // when VirtualSpace and PSVirtualSpace are unified. + GenerationCounters() : _name_space(NULL), _current_size(NULL), _virtual_space(NULL) {} + public: + + GenerationCounters(const char* name, int ordinal, int spaces, + VirtualSpace* v); + + ~GenerationCounters() { + if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); + } + + virtual void update_all() { + _current_size->set_value(_virtual_space->committed_size()); + } + + const char* name_space() const { return _name_space; } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/immutableSpace.cpp b/hotspot/src/share/vm/gc_implementation/shared/immutableSpace.cpp new file mode 100644 index 00000000000..2485fee4a05 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/immutableSpace.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_immutableSpace.cpp.incl" + +void ImmutableSpace::initialize(MemRegion mr) { + HeapWord* bottom = mr.start(); + HeapWord* end = mr.end(); + + assert(Universe::on_page_boundary(bottom) && Universe::on_page_boundary(end), + "invalid space boundaries"); + + _bottom = bottom; + _end = end; +} + +void ImmutableSpace::oop_iterate(OopClosure* cl) { + HeapWord* obj_addr = bottom(); + HeapWord* t = end(); + // Could call objects iterate, but this is easier. + while (obj_addr < t) { + obj_addr += oop(obj_addr)->oop_iterate(cl); + } +} + +void ImmutableSpace::object_iterate(ObjectClosure* cl) { + HeapWord* p = bottom(); + while (p < end()) { + cl->do_object(oop(p)); + p += oop(p)->size(); + } +} + +#ifndef PRODUCT + +void ImmutableSpace::print_short() const { + tty->print(" space " SIZE_FORMAT "K, 100%% used", capacity_in_bytes() / K); +} + +void ImmutableSpace::print() const { + print_short(); + tty->print_cr(" [%#-6lx,%#-6lx)", bottom(), end()); +} + +#endif + +void ImmutableSpace::verify(bool allow_dirty) const { + HeapWord* p = bottom(); + HeapWord* t = end(); + HeapWord* prev_p = NULL; + while (p < t) { + oop(p)->verify(); + prev_p = p; + p += oop(p)->size(); + } + guarantee(p == end(), "end of last object must match end of space"); +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/immutableSpace.hpp b/hotspot/src/share/vm/gc_implementation/shared/immutableSpace.hpp new file mode 100644 index 00000000000..4d62bd8e7ae --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/immutableSpace.hpp @@ -0,0 +1,63 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// An ImmutableSpace is a viewport into a contiguous range +// (or subrange) of previously allocated objects. + +// Invariant: bottom() and end() are on page_size boundaries and +// bottom() <= end() + +class ImmutableSpace: public CHeapObj { + friend class VMStructs; + protected: + HeapWord* _bottom; + HeapWord* _end; + + public: + ImmutableSpace() { _bottom = NULL; _end = NULL; } + HeapWord* bottom() const { return _bottom; } + HeapWord* end() const { return _end; } + + MemRegion region() const { return MemRegion(bottom(), end()); } + + // Initialization + void initialize(MemRegion mr); + + bool contains(const void* p) const { return _bottom <= p && p < _end; } + + // Size computations. Sizes are in bytes. + size_t capacity_in_bytes() const { return capacity_in_words() * HeapWordSize; } + + // Size computations. Sizes are in heapwords. + size_t capacity_in_words() const { return pointer_delta(end(), bottom()); } + + // Iteration. + virtual void oop_iterate(OopClosure* cl); + virtual void object_iterate(ObjectClosure* cl); + + // Debugging + virtual void print() const PRODUCT_RETURN; + virtual void print_short() const PRODUCT_RETURN; + virtual void verify(bool allow_dirty) const; +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/isGCActiveMark.hpp b/hotspot/src/share/vm/gc_implementation/shared/isGCActiveMark.hpp new file mode 100644 index 00000000000..d91e8ee2c57 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/isGCActiveMark.hpp @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This class provides a method for block structured setting of the +// _is_gc_active state without requiring accessors in CollectedHeap + +class IsGCActiveMark : public StackObj { + public: + IsGCActiveMark() { + CollectedHeap* heap = Universe::heap(); + assert(!heap->is_gc_active(), "Not reentrant"); + heap->_is_gc_active = true; + } + + ~IsGCActiveMark() { + CollectedHeap* heap = Universe::heap(); + assert(heap->is_gc_active(), "Sanity"); + heap->_is_gc_active = false; + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/liveRange.hpp b/hotspot/src/share/vm/gc_implementation/shared/liveRange.hpp new file mode 100644 index 00000000000..2448fb8cf10 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/liveRange.hpp @@ -0,0 +1,48 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This is a shared helper class used during phase 3 and 4 to move all the objects +// Dead regions in a Space are linked together to keep track of the live regions +// so that the live data can be traversed quickly without having to look at each +// object. + +class LiveRange: public MemRegion { +public: + LiveRange(HeapWord* bottom, HeapWord* top): MemRegion(bottom, top) {} + + void set_end(HeapWord* e) { + assert(e >= start(), "should be a non-zero range"); + MemRegion::set_end(e); + } + void set_word_size(size_t ws) { + assert(ws >= 0, "should be a non-zero range"); + MemRegion::set_word_size(ws); + } + + LiveRange * next() { return (LiveRange *) end(); } + + void move_to(HeapWord* destination) { + Copy::aligned_conjoint_words(start(), destination, word_size()); + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp b/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp new file mode 100644 index 00000000000..e7d59db0bd0 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp @@ -0,0 +1,357 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_markSweep.cpp.incl" + +GrowableArray* MarkSweep::_marking_stack = NULL; +GrowableArray* MarkSweep::_revisit_klass_stack = NULL; + +GrowableArray* MarkSweep::_preserved_oop_stack = NULL; +GrowableArray* MarkSweep::_preserved_mark_stack= NULL; +size_t MarkSweep::_preserved_count = 0; +size_t MarkSweep::_preserved_count_max = 0; +PreservedMark* MarkSweep::_preserved_marks = NULL; +ReferenceProcessor* MarkSweep::_ref_processor = NULL; + +#ifdef VALIDATE_MARK_SWEEP +GrowableArray* MarkSweep::_root_refs_stack = NULL; +GrowableArray * MarkSweep::_live_oops = NULL; +GrowableArray * MarkSweep::_live_oops_moved_to = NULL; +GrowableArray* MarkSweep::_live_oops_size = NULL; +size_t MarkSweep::_live_oops_index = 0; +size_t MarkSweep::_live_oops_index_at_perm = 0; +GrowableArray* MarkSweep::_other_refs_stack = NULL; +GrowableArray* MarkSweep::_adjusted_pointers = NULL; +bool MarkSweep::_pointer_tracking = false; +bool MarkSweep::_root_tracking = true; + +GrowableArray* MarkSweep::_cur_gc_live_oops = NULL; +GrowableArray* MarkSweep::_cur_gc_live_oops_moved_to = NULL; +GrowableArray * MarkSweep::_cur_gc_live_oops_size = NULL; +GrowableArray* MarkSweep::_last_gc_live_oops = NULL; +GrowableArray* MarkSweep::_last_gc_live_oops_moved_to = NULL; +GrowableArray * MarkSweep::_last_gc_live_oops_size = NULL; +#endif + +void MarkSweep::revisit_weak_klass_link(Klass* k) { + _revisit_klass_stack->push(k); +} + + +void MarkSweep::follow_weak_klass_links() { + // All klasses on the revisit stack are marked at this point. + // Update and follow all subklass, sibling and implementor links. + for (int i = 0; i < _revisit_klass_stack->length(); i++) { + _revisit_klass_stack->at(i)->follow_weak_klass_links(&is_alive,&keep_alive); + } + follow_stack(); +} + + +void MarkSweep::mark_and_follow(oop* p) { + assert(Universe::heap()->is_in_reserved(p), + "we should only be traversing objects here"); + oop m = *p; + if (m != NULL && !m->mark()->is_marked()) { + mark_object(m); + m->follow_contents(); // Follow contents of the marked object + } +} + +void MarkSweep::_mark_and_push(oop* p) { + // Push marked object, contents will be followed later + oop m = *p; + mark_object(m); + _marking_stack->push(m); +} + +MarkSweep::MarkAndPushClosure MarkSweep::mark_and_push_closure; + +void MarkSweep::follow_root(oop* p) { + assert(!Universe::heap()->is_in_reserved(p), + "roots shouldn't be things within the heap"); +#ifdef VALIDATE_MARK_SWEEP + if (ValidateMarkSweep) { + guarantee(!_root_refs_stack->contains(p), "should only be in here once"); + _root_refs_stack->push(p); + } +#endif + oop m = *p; + if (m != NULL && !m->mark()->is_marked()) { + mark_object(m); + m->follow_contents(); // Follow contents of the marked object + } + follow_stack(); +} + +MarkSweep::FollowRootClosure MarkSweep::follow_root_closure; + +void MarkSweep::follow_stack() { + while (!_marking_stack->is_empty()) { + oop obj = _marking_stack->pop(); + assert (obj->is_gc_marked(), "p must be marked"); + obj->follow_contents(); + } +} + +MarkSweep::FollowStackClosure MarkSweep::follow_stack_closure; + + +// We preserve the mark which should be replaced at the end and the location that it +// will go. Note that the object that this markOop belongs to isn't currently at that +// address but it will be after phase4 +void MarkSweep::preserve_mark(oop obj, markOop mark) { + // we try to store preserved marks in the to space of the new generation since this + // is storage which should be available. Most of the time this should be sufficient + // space for the marks we need to preserve but if it isn't we fall back in using + // GrowableArrays to keep track of the overflow. + if (_preserved_count < _preserved_count_max) { + _preserved_marks[_preserved_count++].init(obj, mark); + } else { + if (_preserved_mark_stack == NULL) { + _preserved_mark_stack = new (ResourceObj::C_HEAP) GrowableArray(40, true); + _preserved_oop_stack = new (ResourceObj::C_HEAP) GrowableArray(40, true); + } + _preserved_mark_stack->push(mark); + _preserved_oop_stack->push(obj); + } +} + +MarkSweep::AdjustPointerClosure MarkSweep::adjust_root_pointer_closure(true); +MarkSweep::AdjustPointerClosure MarkSweep::adjust_pointer_closure(false); + +void MarkSweep::adjust_marks() { + assert(_preserved_oop_stack == NULL || + _preserved_oop_stack->length() == _preserved_mark_stack->length(), + "inconsistent preserved oop stacks"); + + // adjust the oops we saved earlier + for (size_t i = 0; i < _preserved_count; i++) { + _preserved_marks[i].adjust_pointer(); + } + + // deal with the overflow stack + if (_preserved_oop_stack) { + for (int i = 0; i < _preserved_oop_stack->length(); i++) { + oop* p = _preserved_oop_stack->adr_at(i); + adjust_pointer(p); + } + } +} + +void MarkSweep::restore_marks() { + assert(_preserved_oop_stack == NULL || + _preserved_oop_stack->length() == _preserved_mark_stack->length(), + "inconsistent preserved oop stacks"); + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("Restoring %d marks", _preserved_count + + (_preserved_oop_stack ? _preserved_oop_stack->length() : 0)); + } + + // restore the marks we saved earlier + for (size_t i = 0; i < _preserved_count; i++) { + _preserved_marks[i].restore(); + } + + // deal with the overflow + if (_preserved_oop_stack) { + for (int i = 0; i < _preserved_oop_stack->length(); i++) { + oop obj = _preserved_oop_stack->at(i); + markOop mark = _preserved_mark_stack->at(i); + obj->set_mark(mark); + } + } +} + +#ifdef VALIDATE_MARK_SWEEP + +void MarkSweep::track_adjusted_pointer(oop* p, oop newobj, bool isroot) { + if (!ValidateMarkSweep) + return; + + if (!isroot) { + if (_pointer_tracking) { + guarantee(_adjusted_pointers->contains(p), "should have seen this pointer"); + _adjusted_pointers->remove(p); + } + } else { + ptrdiff_t index = _root_refs_stack->find(p); + if (index != -1) { + int l = _root_refs_stack->length(); + if (l > 0 && l - 1 != index) { + oop* last = _root_refs_stack->pop(); + assert(last != p, "should be different"); + _root_refs_stack->at_put(index, last); + } else { + _root_refs_stack->remove(p); + } + } + } +} + + +void MarkSweep::check_adjust_pointer(oop* p) { + _adjusted_pointers->push(p); +} + + +class AdjusterTracker: public OopClosure { + public: + AdjusterTracker() {}; + void do_oop(oop* o) { MarkSweep::check_adjust_pointer(o); } +}; + + +void MarkSweep::track_interior_pointers(oop obj) { + if (ValidateMarkSweep) { + _adjusted_pointers->clear(); + _pointer_tracking = true; + + AdjusterTracker checker; + obj->oop_iterate(&checker); + } +} + + +void MarkSweep::check_interior_pointers() { + if (ValidateMarkSweep) { + _pointer_tracking = false; + guarantee(_adjusted_pointers->length() == 0, "should have processed the same pointers"); + } +} + + +void MarkSweep::reset_live_oop_tracking(bool at_perm) { + if (ValidateMarkSweep) { + guarantee((size_t)_live_oops->length() == _live_oops_index, "should be at end of live oops"); + _live_oops_index = at_perm ? _live_oops_index_at_perm : 0; + } +} + + +void MarkSweep::register_live_oop(oop p, size_t size) { + if (ValidateMarkSweep) { + _live_oops->push(p); + _live_oops_size->push(size); + _live_oops_index++; + } +} + +void MarkSweep::validate_live_oop(oop p, size_t size) { + if (ValidateMarkSweep) { + oop obj = _live_oops->at((int)_live_oops_index); + guarantee(obj == p, "should be the same object"); + guarantee(_live_oops_size->at((int)_live_oops_index) == size, "should be the same size"); + _live_oops_index++; + } +} + +void MarkSweep::live_oop_moved_to(HeapWord* q, size_t size, + HeapWord* compaction_top) { + assert(oop(q)->forwardee() == NULL || oop(q)->forwardee() == oop(compaction_top), + "should be moved to forwarded location"); + if (ValidateMarkSweep) { + MarkSweep::validate_live_oop(oop(q), size); + _live_oops_moved_to->push(oop(compaction_top)); + } + if (RecordMarkSweepCompaction) { + _cur_gc_live_oops->push(q); + _cur_gc_live_oops_moved_to->push(compaction_top); + _cur_gc_live_oops_size->push(size); + } +} + + +void MarkSweep::compaction_complete() { + if (RecordMarkSweepCompaction) { + GrowableArray* _tmp_live_oops = _cur_gc_live_oops; + GrowableArray* _tmp_live_oops_moved_to = _cur_gc_live_oops_moved_to; + GrowableArray * _tmp_live_oops_size = _cur_gc_live_oops_size; + + _cur_gc_live_oops = _last_gc_live_oops; + _cur_gc_live_oops_moved_to = _last_gc_live_oops_moved_to; + _cur_gc_live_oops_size = _last_gc_live_oops_size; + _last_gc_live_oops = _tmp_live_oops; + _last_gc_live_oops_moved_to = _tmp_live_oops_moved_to; + _last_gc_live_oops_size = _tmp_live_oops_size; + } +} + + +void MarkSweep::print_new_location_of_heap_address(HeapWord* q) { + if (!RecordMarkSweepCompaction) { + tty->print_cr("Requires RecordMarkSweepCompaction to be enabled"); + return; + } + + if (_last_gc_live_oops == NULL) { + tty->print_cr("No compaction information gathered yet"); + return; + } + + for (int i = 0; i < _last_gc_live_oops->length(); i++) { + HeapWord* old_oop = _last_gc_live_oops->at(i); + size_t sz = _last_gc_live_oops_size->at(i); + if (old_oop <= q && q < (old_oop + sz)) { + HeapWord* new_oop = _last_gc_live_oops_moved_to->at(i); + size_t offset = (q - old_oop); + tty->print_cr("Address " PTR_FORMAT, q); + tty->print_cr(" Was in oop " PTR_FORMAT ", size %d, at offset %d", old_oop, sz, offset); + tty->print_cr(" Now in oop " PTR_FORMAT ", actual address " PTR_FORMAT, new_oop, new_oop + offset); + return; + } + } + + tty->print_cr("Address " PTR_FORMAT " not found in live oop information from last GC", q); +} +#endif //VALIDATE_MARK_SWEEP + +MarkSweep::IsAliveClosure MarkSweep::is_alive; + +void MarkSweep::KeepAliveClosure::do_oop(oop* p) { +#ifdef VALIDATE_MARK_SWEEP + if (ValidateMarkSweep) { + if (!Universe::heap()->is_in_reserved(p)) { + _root_refs_stack->push(p); + } else { + _other_refs_stack->push(p); + } + } +#endif + mark_and_push(p); +} + +MarkSweep::KeepAliveClosure MarkSweep::keep_alive; + +void marksweep_init() { /* empty */ } + +#ifndef PRODUCT + +void MarkSweep::trace(const char* msg) { + if (TraceMarkSweep) + gclog_or_tty->print("%s", msg); +} + +#endif diff --git a/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp b/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp new file mode 100644 index 00000000000..8f8b681d180 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp @@ -0,0 +1,245 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ReferenceProcessor; + +// MarkSweep takes care of global mark-compact garbage collection for a +// GenCollectedHeap using a four-phase pointer forwarding algorithm. All +// generations are assumed to support marking; those that can also support +// compaction. +// +// Class unloading will only occur when a full gc is invoked. + +// If VALIDATE_MARK_SWEEP is defined, the -XX:+ValidateMarkSweep flag will +// be operational, and will provide slow but comprehensive self-checks within +// the GC. This is not enabled by default in product or release builds, +// since the extra call to track_adjusted_pointer() in _adjust_pointer() +// would be too much overhead, and would disturb performance measurement. +// However, debug builds are sometimes way too slow to run GC tests! +#ifdef ASSERT +#define VALIDATE_MARK_SWEEP 1 +#endif +#ifdef VALIDATE_MARK_SWEEP +#define VALIDATE_MARK_SWEEP_ONLY(code) code +#else +#define VALIDATE_MARK_SWEEP_ONLY(code) +#endif + + +// declared at end +class PreservedMark; + +class MarkSweep : AllStatic { + // + // In line closure decls + // + + class FollowRootClosure: public OopsInGenClosure{ + public: + void do_oop(oop* p) { follow_root(p); } + virtual const bool do_nmethods() const { return true; } + }; + + class MarkAndPushClosure: public OopClosure { + public: + void do_oop(oop* p) { mark_and_push(p); } + virtual const bool do_nmethods() const { return true; } + }; + + class FollowStackClosure: public VoidClosure { + public: + void do_void() { follow_stack(); } + }; + + class AdjustPointerClosure: public OopsInGenClosure { + bool _is_root; + public: + AdjustPointerClosure(bool is_root) : _is_root(is_root) {} + void do_oop(oop* p) { _adjust_pointer(p, _is_root); } + }; + + // Used for java/lang/ref handling + class IsAliveClosure: public BoolObjectClosure { + public: + void do_object(oop p) { assert(false, "don't call"); } + bool do_object_b(oop p) { return p->is_gc_marked(); } + }; + + class KeepAliveClosure: public OopClosure { + public: + void do_oop(oop* p); + }; + + // + // Friend decls + // + + friend class AdjustPointerClosure; + friend class KeepAliveClosure; + friend class VM_MarkSweep; + friend void marksweep_init(); + + // + // Vars + // + protected: + // Traversal stack used during phase1 + static GrowableArray* _marking_stack; + // Stack for live klasses to revisit at end of marking phase + static GrowableArray* _revisit_klass_stack; + + // Space for storing/restoring mark word + static GrowableArray* _preserved_mark_stack; + static GrowableArray* _preserved_oop_stack; + static size_t _preserved_count; + static size_t _preserved_count_max; + static PreservedMark* _preserved_marks; + + // Reference processing (used in ...follow_contents) + static ReferenceProcessor* _ref_processor; + +#ifdef VALIDATE_MARK_SWEEP + static GrowableArray* _root_refs_stack; + static GrowableArray * _live_oops; + static GrowableArray * _live_oops_moved_to; + static GrowableArray* _live_oops_size; + static size_t _live_oops_index; + static size_t _live_oops_index_at_perm; + static GrowableArray* _other_refs_stack; + static GrowableArray* _adjusted_pointers; + static bool _pointer_tracking; + static bool _root_tracking; + + // The following arrays are saved since the time of the last GC and + // assist in tracking down problems where someone has done an errant + // store into the heap, usually to an oop that wasn't properly + // handleized across a GC. If we crash or otherwise fail before the + // next GC, we can query these arrays to find out the object we had + // intended to do the store to (assuming it is still alive) and the + // offset within that object. Covered under RecordMarkSweepCompaction. + static GrowableArray * _cur_gc_live_oops; + static GrowableArray * _cur_gc_live_oops_moved_to; + static GrowableArray* _cur_gc_live_oops_size; + static GrowableArray * _last_gc_live_oops; + static GrowableArray * _last_gc_live_oops_moved_to; + static GrowableArray* _last_gc_live_oops_size; +#endif + + + // Non public closures + static IsAliveClosure is_alive; + static KeepAliveClosure keep_alive; + + // Class unloading. Update subklass/sibling/implementor links at end of marking phase. + static void follow_weak_klass_links(); + + // Debugging + static void trace(const char* msg) PRODUCT_RETURN; + + public: + // Public closures + static FollowRootClosure follow_root_closure; + static MarkAndPushClosure mark_and_push_closure; + static FollowStackClosure follow_stack_closure; + static AdjustPointerClosure adjust_root_pointer_closure; + static AdjustPointerClosure adjust_pointer_closure; + + // Reference Processing + static ReferenceProcessor* const ref_processor() { return _ref_processor; } + + // Call backs for marking + static void mark_object(oop obj); + static void follow_root(oop* p); // Mark pointer and follow contents. Empty marking + + // stack afterwards. + + static void mark_and_follow(oop* p); // Mark pointer and follow contents. + static void _mark_and_push(oop* p); // Mark pointer and push obj on + // marking stack. + + + static void mark_and_push(oop* p) { // Check mark and maybe push on + // marking stack + // assert(Universe::is_reserved_heap((oop)p), "we should only be traversing objects here"); + oop m = *p; + if (m != NULL && !m->mark()->is_marked()) { + _mark_and_push(p); + } + } + + static void follow_stack(); // Empty marking stack. + + + static void preserve_mark(oop p, markOop mark); // Save the mark word so it can be restored later + static void adjust_marks(); // Adjust the pointers in the preserved marks table + static void restore_marks(); // Restore the marks that we saved in preserve_mark + + static void _adjust_pointer(oop* p, bool isroot); + + static void adjust_root_pointer(oop* p) { _adjust_pointer(p, true); } + static void adjust_pointer(oop* p) { _adjust_pointer(p, false); } + +#ifdef VALIDATE_MARK_SWEEP + static void track_adjusted_pointer(oop* p, oop newobj, bool isroot); + static void check_adjust_pointer(oop* p); // Adjust this pointer + static void track_interior_pointers(oop obj); + static void check_interior_pointers(); + + static void reset_live_oop_tracking(bool at_perm); + static void register_live_oop(oop p, size_t size); + static void validate_live_oop(oop p, size_t size); + static void live_oop_moved_to(HeapWord* q, size_t size, HeapWord* compaction_top); + static void compaction_complete(); + + // Querying operation of RecordMarkSweepCompaction results. + // Finds and prints the current base oop and offset for a word + // within an oop that was live during the last GC. Helpful for + // tracking down heap stomps. + static void print_new_location_of_heap_address(HeapWord* q); +#endif + + // Call backs for class unloading + static void revisit_weak_klass_link(Klass* k); // Update subklass/sibling/implementor links at end of marking. +}; + + +class PreservedMark VALUE_OBJ_CLASS_SPEC { +private: + oop _obj; + markOop _mark; + +public: + void init(oop obj, markOop mark) { + _obj = obj; + _mark = mark; + } + + void adjust_pointer() { + MarkSweep::adjust_pointer(&_obj); + } + + void restore() { + _obj->set_mark(_mark); + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp b/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp new file mode 100644 index 00000000000..1418df7f953 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void MarkSweep::_adjust_pointer(oop* p, bool isroot) { + oop obj = *p; + VALIDATE_MARK_SWEEP_ONLY(oop saved_new_pointer = NULL); + if (obj != NULL) { + oop new_pointer = oop(obj->mark()->decode_pointer()); + assert(new_pointer != NULL || // is forwarding ptr? + obj->mark() == markOopDesc::prototype() || // not gc marked? + (UseBiasedLocking && obj->mark()->has_bias_pattern()) || // not gc marked? + obj->is_shared(), // never forwarded? + "should contain a forwarding pointer"); + if (new_pointer != NULL) { + *p = new_pointer; + assert(Universe::heap()->is_in_reserved(new_pointer), + "should be in object space"); + VALIDATE_MARK_SWEEP_ONLY(saved_new_pointer = new_pointer); + } + } + VALIDATE_MARK_SWEEP_ONLY(track_adjusted_pointer(p, saved_new_pointer, isroot)); +} + +inline void MarkSweep::mark_object(oop obj) { + +#ifndef SERIALGC + if (UseParallelOldGC && VerifyParallelOldWithMarkSweep) { + assert(PSParallelCompact::mark_bitmap()->is_marked(obj), + "Should be marked in the marking bitmap"); + } +#endif // SERIALGC + + // some marks may contain information we need to preserve so we store them away + // and overwrite the mark. We'll restore it at the end of markSweep. + markOop mark = obj->mark(); + obj->set_mark(markOopDesc::prototype()->set_marked()); + + if (mark->must_be_preserved(obj)) { + preserve_mark(obj, mark); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp b/hotspot/src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp new file mode 100644 index 00000000000..bccbcc600a1 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp @@ -0,0 +1,790 @@ + +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_mutableNUMASpace.cpp.incl" + + +MutableNUMASpace::MutableNUMASpace() { + _lgrp_spaces = new (ResourceObj::C_HEAP) GrowableArray(0, true); + _page_size = os::vm_page_size(); + _adaptation_cycles = 0; + _samples_count = 0; + update_layout(true); +} + +MutableNUMASpace::~MutableNUMASpace() { + for (int i = 0; i < lgrp_spaces()->length(); i++) { + delete lgrp_spaces()->at(i); + } + delete lgrp_spaces(); +} + +void MutableNUMASpace::mangle_unused_area() { + for (int i = 0; i < lgrp_spaces()->length(); i++) { + LGRPSpace *ls = lgrp_spaces()->at(i); + MutableSpace *s = ls->space(); + HeapWord *top = MAX2((HeapWord*)round_down((intptr_t)s->top(), page_size()), s->bottom()); + if (top < s->end()) { + ls->add_invalid_region(MemRegion(top, s->end())); + } + s->mangle_unused_area(); + } +} + +// There may be unallocated holes in the middle chunks +// that should be filled with dead objects to ensure parseability. +void MutableNUMASpace::ensure_parsability() { + for (int i = 0; i < lgrp_spaces()->length(); i++) { + LGRPSpace *ls = lgrp_spaces()->at(i); + MutableSpace *s = ls->space(); + if (!s->contains(top())) { + if (s->free_in_words() > 0) { + SharedHeap::fill_region_with_object(MemRegion(s->top(), s->end())); + size_t area_touched_words = pointer_delta(s->end(), s->top(), sizeof(HeapWordSize)); +#ifndef ASSERT + if (!ZapUnusedHeapArea) { + area_touched_words = MIN2((size_t)align_object_size(typeArrayOopDesc::header_size(T_INT)), + area_touched_words); + } +#endif + MemRegion invalid; + HeapWord *crossing_start = (HeapWord*)round_to((intptr_t)s->top(), os::vm_page_size()); + HeapWord *crossing_end = (HeapWord*)round_to((intptr_t)(s->top() + area_touched_words), + os::vm_page_size()); + if (crossing_start != crossing_end) { + // If object header crossed a small page boundary we mark the area + // as invalid rounding it to a page_size(). + HeapWord *start = MAX2((HeapWord*)round_down((intptr_t)s->top(), page_size()), s->bottom()); + HeapWord *end = MIN2((HeapWord*)round_to((intptr_t)(s->top() + area_touched_words), page_size()), + s->end()); + invalid = MemRegion(start, end); + } + + ls->add_invalid_region(invalid); + s->set_top(s->end()); + } + } else { +#ifdef ASSERT + MemRegion invalid(s->top(), s->end()); + ls->add_invalid_region(invalid); +#else + if (ZapUnusedHeapArea) { + MemRegion invalid(s->top(), s->end()); + ls->add_invalid_region(invalid); + } else break; +#endif + } + } +} + +size_t MutableNUMASpace::used_in_words() const { + size_t s = 0; + for (int i = 0; i < lgrp_spaces()->length(); i++) { + s += lgrp_spaces()->at(i)->space()->used_in_words(); + } + return s; +} + +size_t MutableNUMASpace::free_in_words() const { + size_t s = 0; + for (int i = 0; i < lgrp_spaces()->length(); i++) { + s += lgrp_spaces()->at(i)->space()->free_in_words(); + } + return s; +} + + +size_t MutableNUMASpace::tlab_capacity(Thread *thr) const { + guarantee(thr != NULL, "No thread"); + int lgrp_id = thr->lgrp_id(); + assert(lgrp_id != -1, "No lgrp_id set"); + int i = lgrp_spaces()->find(&lgrp_id, LGRPSpace::equals); + if (i == -1) { + return 0; + } + return lgrp_spaces()->at(i)->space()->capacity_in_bytes(); +} + +size_t MutableNUMASpace::unsafe_max_tlab_alloc(Thread *thr) const { + guarantee(thr != NULL, "No thread"); + int lgrp_id = thr->lgrp_id(); + assert(lgrp_id != -1, "No lgrp_id set"); + int i = lgrp_spaces()->find(&lgrp_id, LGRPSpace::equals); + if (i == -1) { + return 0; + } + return lgrp_spaces()->at(i)->space()->free_in_bytes(); +} + +// Check if the NUMA topology has changed. Add and remove spaces if needed. +// The update can be forced by setting the force parameter equal to true. +bool MutableNUMASpace::update_layout(bool force) { + // Check if the topology had changed. + bool changed = os::numa_topology_changed(); + if (force || changed) { + // Compute lgrp intersection. Add/remove spaces. + int lgrp_limit = (int)os::numa_get_groups_num(); + int *lgrp_ids = NEW_C_HEAP_ARRAY(int, lgrp_limit); + int lgrp_num = (int)os::numa_get_leaf_groups(lgrp_ids, lgrp_limit); + assert(lgrp_num > 0, "There should be at least one locality group"); + // Add new spaces for the new nodes + for (int i = 0; i < lgrp_num; i++) { + bool found = false; + for (int j = 0; j < lgrp_spaces()->length(); j++) { + if (lgrp_spaces()->at(j)->lgrp_id() == lgrp_ids[i]) { + found = true; + break; + } + } + if (!found) { + lgrp_spaces()->append(new LGRPSpace(lgrp_ids[i])); + } + } + + // Remove spaces for the removed nodes. + for (int i = 0; i < lgrp_spaces()->length();) { + bool found = false; + for (int j = 0; j < lgrp_num; j++) { + if (lgrp_spaces()->at(i)->lgrp_id() == lgrp_ids[j]) { + found = true; + break; + } + } + if (!found) { + delete lgrp_spaces()->at(i); + lgrp_spaces()->remove_at(i); + } else { + i++; + } + } + + FREE_C_HEAP_ARRAY(int, lgrp_ids); + + if (changed) { + for (JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + thread->set_lgrp_id(-1); + } + } + return true; + } + return false; +} + +// Bias region towards the first-touching lgrp. Set the right page sizes. +void MutableNUMASpace::bias_region(MemRegion mr) { + HeapWord *start = (HeapWord*)round_to((intptr_t)mr.start(), page_size()); + HeapWord *end = (HeapWord*)round_down((intptr_t)mr.end(), page_size()); + if (end > start) { + MemRegion aligned_region(start, end); + assert((intptr_t)aligned_region.start() % page_size() == 0 && + (intptr_t)aligned_region.byte_size() % page_size() == 0, "Bad alignment"); + assert(region().contains(aligned_region), "Sanity"); + os::free_memory((char*)aligned_region.start(), aligned_region.byte_size()); + os::realign_memory((char*)aligned_region.start(), aligned_region.byte_size(), page_size()); + os::numa_make_local((char*)aligned_region.start(), aligned_region.byte_size()); + } +} + +// Free all pages in the region. +void MutableNUMASpace::free_region(MemRegion mr) { + HeapWord *start = (HeapWord*)round_to((intptr_t)mr.start(), page_size()); + HeapWord *end = (HeapWord*)round_down((intptr_t)mr.end(), page_size()); + if (end > start) { + MemRegion aligned_region(start, end); + assert((intptr_t)aligned_region.start() % page_size() == 0 && + (intptr_t)aligned_region.byte_size() % page_size() == 0, "Bad alignment"); + assert(region().contains(aligned_region), "Sanity"); + os::free_memory((char*)aligned_region.start(), aligned_region.byte_size()); + } +} + +// Update space layout. Perform adaptation. +void MutableNUMASpace::update() { + if (update_layout(false)) { + // If the topology has changed, make all chunks zero-sized. + for (int i = 0; i < lgrp_spaces()->length(); i++) { + MutableSpace *s = lgrp_spaces()->at(i)->space(); + s->set_end(s->bottom()); + s->set_top(s->bottom()); + } + initialize(region(), true); + } else { + bool should_initialize = false; + for (int i = 0; i < lgrp_spaces()->length(); i++) { + if (!lgrp_spaces()->at(i)->invalid_region().is_empty()) { + should_initialize = true; + break; + } + } + + if (should_initialize || + (UseAdaptiveNUMAChunkSizing && adaptation_cycles() < samples_count())) { + initialize(region(), true); + } + } + + if (NUMAStats) { + for (int i = 0; i < lgrp_spaces()->length(); i++) { + lgrp_spaces()->at(i)->accumulate_statistics(page_size()); + } + } + + scan_pages(NUMAPageScanRate); +} + +// Scan pages. Free pages that have smaller size or wrong placement. +void MutableNUMASpace::scan_pages(size_t page_count) +{ + size_t pages_per_chunk = page_count / lgrp_spaces()->length(); + if (pages_per_chunk > 0) { + for (int i = 0; i < lgrp_spaces()->length(); i++) { + LGRPSpace *ls = lgrp_spaces()->at(i); + ls->scan_pages(page_size(), pages_per_chunk); + } + } +} + +// Accumulate statistics about the allocation rate of each lgrp. +void MutableNUMASpace::accumulate_statistics() { + if (UseAdaptiveNUMAChunkSizing) { + for (int i = 0; i < lgrp_spaces()->length(); i++) { + lgrp_spaces()->at(i)->sample(); + } + increment_samples_count(); + } + + if (NUMAStats) { + for (int i = 0; i < lgrp_spaces()->length(); i++) { + lgrp_spaces()->at(i)->accumulate_statistics(page_size()); + } + } +} + +// Get the current size of a chunk. +// This function computes the size of the chunk based on the +// difference between chunk ends. This allows it to work correctly in +// case the whole space is resized and during the process of adaptive +// chunk resizing. +size_t MutableNUMASpace::current_chunk_size(int i) { + HeapWord *cur_end, *prev_end; + if (i == 0) { + prev_end = bottom(); + } else { + prev_end = lgrp_spaces()->at(i - 1)->space()->end(); + } + if (i == lgrp_spaces()->length() - 1) { + cur_end = end(); + } else { + cur_end = lgrp_spaces()->at(i)->space()->end(); + } + if (cur_end > prev_end) { + return pointer_delta(cur_end, prev_end, sizeof(char)); + } + return 0; +} + +// Return the default chunk size by equally diving the space. +// page_size() aligned. +size_t MutableNUMASpace::default_chunk_size() { + return base_space_size() / lgrp_spaces()->length() * page_size(); +} + +// Produce a new chunk size. page_size() aligned. +size_t MutableNUMASpace::adaptive_chunk_size(int i, size_t limit) { + size_t pages_available = base_space_size(); + for (int j = 0; j < i; j++) { + pages_available -= round_down(current_chunk_size(j), page_size()) / page_size(); + } + pages_available -= lgrp_spaces()->length() - i - 1; + assert(pages_available > 0, "No pages left"); + float alloc_rate = 0; + for (int j = i; j < lgrp_spaces()->length(); j++) { + alloc_rate += lgrp_spaces()->at(j)->alloc_rate()->average(); + } + size_t chunk_size = 0; + if (alloc_rate > 0) { + LGRPSpace *ls = lgrp_spaces()->at(i); + chunk_size = (size_t)(ls->alloc_rate()->average() * pages_available / alloc_rate) * page_size(); + } + chunk_size = MAX2(chunk_size, page_size()); + + if (limit > 0) { + limit = round_down(limit, page_size()); + if (chunk_size > current_chunk_size(i)) { + chunk_size = MIN2((off_t)chunk_size, (off_t)current_chunk_size(i) + (off_t)limit); + } else { + chunk_size = MAX2((off_t)chunk_size, (off_t)current_chunk_size(i) - (off_t)limit); + } + } + assert(chunk_size <= pages_available * page_size(), "Chunk size out of range"); + return chunk_size; +} + + +// Return the bottom_region and the top_region. Align them to page_size() boundary. +// |------------------new_region---------------------------------| +// |----bottom_region--|---intersection---|------top_region------| +void MutableNUMASpace::select_tails(MemRegion new_region, MemRegion intersection, + MemRegion* bottom_region, MemRegion *top_region) { + // Is there bottom? + if (new_region.start() < intersection.start()) { // Yes + // Try to coalesce small pages into a large one. + if (UseLargePages && page_size() >= os::large_page_size()) { + HeapWord* p = (HeapWord*)round_to((intptr_t) intersection.start(), os::large_page_size()); + if (new_region.contains(p) + && pointer_delta(p, new_region.start(), sizeof(char)) >= os::large_page_size()) { + if (intersection.contains(p)) { + intersection = MemRegion(p, intersection.end()); + } else { + intersection = MemRegion(p, p); + } + } + } + *bottom_region = MemRegion(new_region.start(), intersection.start()); + } else { + *bottom_region = MemRegion(); + } + + // Is there top? + if (intersection.end() < new_region.end()) { // Yes + // Try to coalesce small pages into a large one. + if (UseLargePages && page_size() >= os::large_page_size()) { + HeapWord* p = (HeapWord*)round_down((intptr_t) intersection.end(), os::large_page_size()); + if (new_region.contains(p) + && pointer_delta(new_region.end(), p, sizeof(char)) >= os::large_page_size()) { + if (intersection.contains(p)) { + intersection = MemRegion(intersection.start(), p); + } else { + intersection = MemRegion(p, p); + } + } + } + *top_region = MemRegion(intersection.end(), new_region.end()); + } else { + *top_region = MemRegion(); + } +} + +// Try to merge the invalid region with the bottom or top region by decreasing +// the intersection area. Return the invalid_region aligned to the page_size() +// boundary if it's inside the intersection. Return non-empty invalid_region +// if it lies inside the intersection (also page-aligned). +// |------------------new_region---------------------------------| +// |----------------|-------invalid---|--------------------------| +// |----bottom_region--|---intersection---|------top_region------| +void MutableNUMASpace::merge_regions(MemRegion new_region, MemRegion* intersection, + MemRegion *invalid_region) { + if (intersection->start() >= invalid_region->start() && intersection->contains(invalid_region->end())) { + *intersection = MemRegion(invalid_region->end(), intersection->end()); + *invalid_region = MemRegion(); + } else + if (intersection->end() <= invalid_region->end() && intersection->contains(invalid_region->start())) { + *intersection = MemRegion(intersection->start(), invalid_region->start()); + *invalid_region = MemRegion(); + } else + if (intersection->equals(*invalid_region) || invalid_region->contains(*intersection)) { + *intersection = MemRegion(new_region.start(), new_region.start()); + *invalid_region = MemRegion(); + } else + if (intersection->contains(invalid_region)) { + // That's the only case we have to make an additional bias_region() call. + HeapWord* start = invalid_region->start(); + HeapWord* end = invalid_region->end(); + if (UseLargePages && page_size() >= os::large_page_size()) { + HeapWord *p = (HeapWord*)round_down((intptr_t) start, os::large_page_size()); + if (new_region.contains(p)) { + start = p; + } + p = (HeapWord*)round_to((intptr_t) end, os::large_page_size()); + if (new_region.contains(end)) { + end = p; + } + } + if (intersection->start() > start) { + *intersection = MemRegion(start, intersection->end()); + } + if (intersection->end() < end) { + *intersection = MemRegion(intersection->start(), end); + } + *invalid_region = MemRegion(start, end); + } +} + +void MutableNUMASpace::initialize(MemRegion mr, bool clear_space) { + assert(clear_space, "Reallocation will destory data!"); + assert(lgrp_spaces()->length() > 0, "There should be at least one space"); + + MemRegion old_region = region(), new_region; + set_bottom(mr.start()); + set_end(mr.end()); + MutableSpace::set_top(bottom()); + + // Compute chunk sizes + size_t prev_page_size = page_size(); + set_page_size(UseLargePages ? os::large_page_size() : os::vm_page_size()); + HeapWord* rounded_bottom = (HeapWord*)round_to((intptr_t) bottom(), page_size()); + HeapWord* rounded_end = (HeapWord*)round_down((intptr_t) end(), page_size()); + size_t base_space_size_pages = pointer_delta(rounded_end, rounded_bottom, sizeof(char)) / page_size(); + + // Try small pages if the chunk size is too small + if (base_space_size_pages / lgrp_spaces()->length() == 0 + && page_size() > (size_t)os::vm_page_size()) { + set_page_size(os::vm_page_size()); + rounded_bottom = (HeapWord*)round_to((intptr_t) bottom(), page_size()); + rounded_end = (HeapWord*)round_down((intptr_t) end(), page_size()); + base_space_size_pages = pointer_delta(rounded_end, rounded_bottom, sizeof(char)) / page_size(); + } + guarantee(base_space_size_pages / lgrp_spaces()->length() > 0, "Space too small"); + set_base_space_size(base_space_size_pages); + + // Handle space resize + MemRegion top_region, bottom_region; + if (!old_region.equals(region())) { + new_region = MemRegion(rounded_bottom, rounded_end); + MemRegion intersection = new_region.intersection(old_region); + if (intersection.start() == NULL || + intersection.end() == NULL || + prev_page_size > page_size()) { // If the page size got smaller we have to change + // the page size preference for the whole space. + intersection = MemRegion(new_region.start(), new_region.start()); + } + select_tails(new_region, intersection, &bottom_region, &top_region); + bias_region(bottom_region); + bias_region(top_region); + } + + // Check if the space layout has changed significantly? + // This happens when the space has been resized so that either head or tail + // chunk became less than a page. + bool layout_valid = UseAdaptiveNUMAChunkSizing && + current_chunk_size(0) > page_size() && + current_chunk_size(lgrp_spaces()->length() - 1) > page_size(); + + + for (int i = 0; i < lgrp_spaces()->length(); i++) { + LGRPSpace *ls = lgrp_spaces()->at(i); + MutableSpace *s = ls->space(); + old_region = s->region(); + + size_t chunk_byte_size = 0, old_chunk_byte_size = 0; + if (i < lgrp_spaces()->length() - 1) { + if (!UseAdaptiveNUMAChunkSizing || + (UseAdaptiveNUMAChunkSizing && NUMAChunkResizeWeight == 0) || + samples_count() < AdaptiveSizePolicyReadyThreshold) { + // No adaptation. Divide the space equally. + chunk_byte_size = default_chunk_size(); + } else + if (!layout_valid || NUMASpaceResizeRate == 0) { + // Fast adaptation. If no space resize rate is set, resize + // the chunks instantly. + chunk_byte_size = adaptive_chunk_size(i, 0); + } else { + // Slow adaptation. Resize the chunks moving no more than + // NUMASpaceResizeRate bytes per collection. + size_t limit = NUMASpaceResizeRate / + (lgrp_spaces()->length() * (lgrp_spaces()->length() + 1) / 2); + chunk_byte_size = adaptive_chunk_size(i, MAX2(limit * (i + 1), page_size())); + } + + assert(chunk_byte_size >= page_size(), "Chunk size too small"); + assert(chunk_byte_size <= capacity_in_bytes(), "Sanity check"); + } + + if (i == 0) { // Bottom chunk + if (i != lgrp_spaces()->length() - 1) { + new_region = MemRegion(bottom(), rounded_bottom + (chunk_byte_size >> LogHeapWordSize)); + } else { + new_region = MemRegion(bottom(), end()); + } + } else + if (i < lgrp_spaces()->length() - 1) { // Middle chunks + MutableSpace *ps = lgrp_spaces()->at(i - 1)->space(); + new_region = MemRegion(ps->end(), + ps->end() + (chunk_byte_size >> LogHeapWordSize)); + } else { // Top chunk + MutableSpace *ps = lgrp_spaces()->at(i - 1)->space(); + new_region = MemRegion(ps->end(), end()); + } + guarantee(region().contains(new_region), "Region invariant"); + + + // The general case: + // |---------------------|--invalid---|--------------------------| + // |------------------new_region---------------------------------| + // |----bottom_region--|---intersection---|------top_region------| + // |----old_region----| + // The intersection part has all pages in place we don't need to migrate them. + // Pages for the top and bottom part should be freed and then reallocated. + + MemRegion intersection = old_region.intersection(new_region); + + if (intersection.start() == NULL || intersection.end() == NULL) { + intersection = MemRegion(new_region.start(), new_region.start()); + } + + MemRegion invalid_region = ls->invalid_region().intersection(new_region); + if (!invalid_region.is_empty()) { + merge_regions(new_region, &intersection, &invalid_region); + free_region(invalid_region); + } + select_tails(new_region, intersection, &bottom_region, &top_region); + free_region(bottom_region); + free_region(top_region); + + // If we clear the region, we would mangle it in debug. That would cause page + // allocation in a different place. Hence setting the top directly. + s->initialize(new_region, false); + s->set_top(s->bottom()); + + ls->set_invalid_region(MemRegion()); + + set_adaptation_cycles(samples_count()); + } +} + +// Set the top of the whole space. +// Mark the the holes in chunks below the top() as invalid. +void MutableNUMASpace::set_top(HeapWord* value) { + bool found_top = false; + for (int i = 0; i < lgrp_spaces()->length(); i++) { + LGRPSpace *ls = lgrp_spaces()->at(i); + MutableSpace *s = ls->space(); + HeapWord *top = MAX2((HeapWord*)round_down((intptr_t)s->top(), page_size()), s->bottom()); + + if (s->contains(value)) { + if (top < value && top < s->end()) { + ls->add_invalid_region(MemRegion(top, value)); + } + s->set_top(value); + found_top = true; + } else { + if (found_top) { + s->set_top(s->bottom()); + } else { + if (top < s->end()) { + ls->add_invalid_region(MemRegion(top, s->end())); + } + s->set_top(s->end()); + } + } + } + MutableSpace::set_top(value); +} + +void MutableNUMASpace::clear() { + MutableSpace::set_top(bottom()); + for (int i = 0; i < lgrp_spaces()->length(); i++) { + lgrp_spaces()->at(i)->space()->clear(); + } +} + +HeapWord* MutableNUMASpace::allocate(size_t size) { + int lgrp_id = Thread::current()->lgrp_id(); + if (lgrp_id == -1) { + lgrp_id = os::numa_get_group_id(); + Thread::current()->set_lgrp_id(lgrp_id); + } + + int i = lgrp_spaces()->find(&lgrp_id, LGRPSpace::equals); + + // It is possible that a new CPU has been hotplugged and + // we haven't reshaped the space accordingly. + if (i == -1) { + i = os::random() % lgrp_spaces()->length(); + } + + MutableSpace *s = lgrp_spaces()->at(i)->space(); + HeapWord *p = s->allocate(size); + + if (p != NULL && s->free_in_words() < (size_t)oopDesc::header_size()) { + s->set_top(s->top() - size); + p = NULL; + } + if (p != NULL) { + if (top() < s->top()) { // Keep _top updated. + MutableSpace::set_top(s->top()); + } + } + // Make the page allocation happen here. + if (p != NULL) { + for (HeapWord *i = p; i < p + size; i += os::vm_page_size() >> LogHeapWordSize) { + *(int*)i = 0; + } + } + + return p; +} + +// This version is lock-free. +HeapWord* MutableNUMASpace::cas_allocate(size_t size) { + int lgrp_id = Thread::current()->lgrp_id(); + if (lgrp_id == -1) { + lgrp_id = os::numa_get_group_id(); + Thread::current()->set_lgrp_id(lgrp_id); + } + + int i = lgrp_spaces()->find(&lgrp_id, LGRPSpace::equals); + // It is possible that a new CPU has been hotplugged and + // we haven't reshaped the space accordingly. + if (i == -1) { + i = os::random() % lgrp_spaces()->length(); + } + MutableSpace *s = lgrp_spaces()->at(i)->space(); + HeapWord *p = s->cas_allocate(size); + if (p != NULL && s->free_in_words() < (size_t)oopDesc::header_size()) { + if (s->cas_deallocate(p, size)) { + // We were the last to allocate and created a fragment less than + // a minimal object. + p = NULL; + } + } + if (p != NULL) { + HeapWord* cur_top, *cur_chunk_top = p + size; + while ((cur_top = top()) < cur_chunk_top) { // Keep _top updated. + if (Atomic::cmpxchg_ptr(cur_chunk_top, top_addr(), cur_top) == cur_top) { + break; + } + } + } + + // Make the page allocation happen here. + if (p != NULL) { + for (HeapWord *i = p; i < p + size; i += os::vm_page_size() >> LogHeapWordSize) { + *(int*)i = 0; + } + } + return p; +} + +void MutableNUMASpace::print_short_on(outputStream* st) const { + MutableSpace::print_short_on(st); + st->print(" ("); + for (int i = 0; i < lgrp_spaces()->length(); i++) { + st->print("lgrp %d: ", lgrp_spaces()->at(i)->lgrp_id()); + lgrp_spaces()->at(i)->space()->print_short_on(st); + if (i < lgrp_spaces()->length() - 1) { + st->print(", "); + } + } + st->print(")"); +} + +void MutableNUMASpace::print_on(outputStream* st) const { + MutableSpace::print_on(st); + for (int i = 0; i < lgrp_spaces()->length(); i++) { + LGRPSpace *ls = lgrp_spaces()->at(i); + st->print(" lgrp %d", ls->lgrp_id()); + ls->space()->print_on(st); + if (NUMAStats) { + st->print(" local/remote/unbiased/uncommitted: %dK/%dK/%dK/%dK, large/small pages: %d/%d\n", + ls->space_stats()->_local_space / K, + ls->space_stats()->_remote_space / K, + ls->space_stats()->_unbiased_space / K, + ls->space_stats()->_uncommited_space / K, + ls->space_stats()->_large_pages, + ls->space_stats()->_small_pages); + } + } +} + +void MutableNUMASpace::verify(bool allow_dirty) const { + for (int i = 0; i < lgrp_spaces()->length(); i++) { + lgrp_spaces()->at(i)->space()->verify(allow_dirty); + } +} + +// Scan pages and gather stats about page placement and size. +void MutableNUMASpace::LGRPSpace::accumulate_statistics(size_t page_size) { + clear_space_stats(); + char *start = (char*)round_to((intptr_t) space()->bottom(), page_size); + char* end = (char*)round_down((intptr_t) space()->end(), page_size); + if (start < end) { + for (char *p = start; p < end;) { + os::page_info info; + if (os::get_page_info(p, &info)) { + if (info.size > 0) { + if (info.size > (size_t)os::vm_page_size()) { + space_stats()->_large_pages++; + } else { + space_stats()->_small_pages++; + } + if (info.lgrp_id == lgrp_id()) { + space_stats()->_local_space += info.size; + } else { + space_stats()->_remote_space += info.size; + } + p += info.size; + } else { + p += os::vm_page_size(); + space_stats()->_uncommited_space += os::vm_page_size(); + } + } else { + return; + } + } + } + space_stats()->_unbiased_space = pointer_delta(start, space()->bottom(), sizeof(char)) + + pointer_delta(space()->end(), end, sizeof(char)); + +} + +// Scan page_count pages and verify if they have the right size and right placement. +// If invalid pages are found they are freed in hope that subsequent reallocation +// will be more successful. +void MutableNUMASpace::LGRPSpace::scan_pages(size_t page_size, size_t page_count) +{ + char* range_start = (char*)round_to((intptr_t) space()->bottom(), page_size); + char* range_end = (char*)round_down((intptr_t) space()->end(), page_size); + + if (range_start > last_page_scanned() || last_page_scanned() >= range_end) { + set_last_page_scanned(range_start); + } + + char *scan_start = last_page_scanned(); + char* scan_end = MIN2(scan_start + page_size * page_count, range_end); + + os::page_info page_expected, page_found; + page_expected.size = page_size; + page_expected.lgrp_id = lgrp_id(); + + char *s = scan_start; + while (s < scan_end) { + char *e = os::scan_pages(s, (char*)scan_end, &page_expected, &page_found); + if (e == NULL) { + break; + } + if (e != scan_end) { + if ((page_expected.size != page_size || page_expected.lgrp_id != lgrp_id()) + && page_expected.size != 0) { + os::free_memory(s, pointer_delta(e, s, sizeof(char))); + } + page_expected = page_found; + } + s = e; + } + + set_last_page_scanned(scan_end); +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/mutableNUMASpace.hpp b/hotspot/src/share/vm/gc_implementation/shared/mutableNUMASpace.hpp new file mode 100644 index 00000000000..5f9954fcb8b --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/mutableNUMASpace.hpp @@ -0,0 +1,198 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * The NUMA-aware allocator (MutableNUMASpace) is basically a modification + * of MutableSpace which preserves interfaces but implements different + * functionality. The space is split into chunks for each locality group + * (resizing for adaptive size policy is also supported). For each thread + * allocations are performed in the chunk corresponding to the home locality + * group of the thread. Whenever any chunk fills-in the young generation + * collection occurs. + * The chunks can be also be adaptively resized. The idea behind the adaptive + * sizing is to reduce the loss of the space in the eden due to fragmentation. + * The main cause of fragmentation is uneven allocation rates of threads. + * The allocation rate difference between locality groups may be caused either by + * application specifics or by uneven LWP distribution by the OS. Besides, + * application can have less threads then the number of locality groups. + * In order to resize the chunk we measure the allocation rate of the + * application between collections. After that we reshape the chunks to reflect + * the allocation rate pattern. The AdaptiveWeightedAverage exponentially + * decaying average is used to smooth the measurements. The NUMASpaceResizeRate + * parameter is used to control the adaptation speed by restricting the number of + * bytes that can be moved during the adaptation phase. + * Chunks may contain pages from a wrong locality group. The page-scanner has + * been introduced to address the problem. Remote pages typically appear due to + * the memory shortage in the target locality group. Besides Solaris would + * allocate a large page from the remote locality group even if there are small + * local pages available. The page-scanner scans the pages right after the + * collection and frees remote pages in hope that subsequent reallocation would + * be more successful. This approach proved to be useful on systems with high + * load where multiple processes are competing for the memory. + */ + +class MutableNUMASpace : public MutableSpace { + friend class VMStructs; + + class LGRPSpace : public CHeapObj { + int _lgrp_id; + MutableSpace* _space; + MemRegion _invalid_region; + AdaptiveWeightedAverage *_alloc_rate; + + struct SpaceStats { + size_t _local_space, _remote_space, _unbiased_space, _uncommited_space; + size_t _large_pages, _small_pages; + + SpaceStats() { + _local_space = 0; + _remote_space = 0; + _unbiased_space = 0; + _uncommited_space = 0; + _large_pages = 0; + _small_pages = 0; + } + }; + + SpaceStats _space_stats; + + char* _last_page_scanned; + char* last_page_scanned() { return _last_page_scanned; } + void set_last_page_scanned(char* p) { _last_page_scanned = p; } + public: + LGRPSpace(int l) : _lgrp_id(l), _last_page_scanned(NULL) { + _space = new MutableSpace(); + _alloc_rate = new AdaptiveWeightedAverage(NUMAChunkResizeWeight); + } + ~LGRPSpace() { + delete _space; + delete _alloc_rate; + } + + void add_invalid_region(MemRegion r) { + if (!_invalid_region.is_empty()) { + _invalid_region.set_start(MIN2(_invalid_region.start(), r.start())); + _invalid_region.set_end(MAX2(_invalid_region.end(), r.end())); + } else { + _invalid_region = r; + } + } + + static bool equals(void* lgrp_id_value, LGRPSpace* p) { + return *(int*)lgrp_id_value == p->lgrp_id(); + } + + void sample() { + alloc_rate()->sample(space()->used_in_bytes()); + } + + MemRegion invalid_region() const { return _invalid_region; } + void set_invalid_region(MemRegion r) { _invalid_region = r; } + int lgrp_id() const { return _lgrp_id; } + MutableSpace* space() const { return _space; } + AdaptiveWeightedAverage* alloc_rate() const { return _alloc_rate; } + SpaceStats* space_stats() { return &_space_stats; } + void clear_space_stats() { _space_stats = SpaceStats(); } + + void accumulate_statistics(size_t page_size); + void scan_pages(size_t page_size, size_t page_count); + }; + + GrowableArray* _lgrp_spaces; + size_t _page_size; + unsigned _adaptation_cycles, _samples_count; + + void set_page_size(size_t psz) { _page_size = psz; } + size_t page_size() const { return _page_size; } + + unsigned adaptation_cycles() { return _adaptation_cycles; } + void set_adaptation_cycles(int v) { _adaptation_cycles = v; } + + unsigned samples_count() { return _samples_count; } + void increment_samples_count() { ++_samples_count; } + + size_t _base_space_size; + void set_base_space_size(size_t v) { _base_space_size = v; } + size_t base_space_size() const { return _base_space_size; } + + // Check if the NUMA topology has changed. Add and remove spaces if needed. + // The update can be forced by setting the force parameter equal to true. + bool update_layout(bool force); + // Bias region towards the first-touching lgrp. + void bias_region(MemRegion mr); + // Free pages in a given region. + void free_region(MemRegion mr); + // Get current chunk size. + size_t current_chunk_size(int i); + // Get default chunk size (equally divide the space). + size_t default_chunk_size(); + // Adapt the chunk size to follow the allocation rate. + size_t adaptive_chunk_size(int i, size_t limit); + // Scan and free invalid pages. + void scan_pages(size_t page_count); + // Return the bottom_region and the top_region. Align them to page_size() boundary. + // |------------------new_region---------------------------------| + // |----bottom_region--|---intersection---|------top_region------| + void select_tails(MemRegion new_region, MemRegion intersection, + MemRegion* bottom_region, MemRegion *top_region); + // Try to merge the invalid region with the bottom or top region by decreasing + // the intersection area. Return the invalid_region aligned to the page_size() + // boundary if it's inside the intersection. Return non-empty invalid_region + // if it lies inside the intersection (also page-aligned). + // |------------------new_region---------------------------------| + // |----------------|-------invalid---|--------------------------| + // |----bottom_region--|---intersection---|------top_region------| + void merge_regions(MemRegion new_region, MemRegion* intersection, + MemRegion *invalid_region); + + public: + GrowableArray* lgrp_spaces() const { return _lgrp_spaces; } + MutableNUMASpace(); + virtual ~MutableNUMASpace(); + // Space initialization. + virtual void initialize(MemRegion mr, bool clear_space); + // Update space layout if necessary. Do all adaptive resizing job. + virtual void update(); + // Update allocation rate averages. + virtual void accumulate_statistics(); + + virtual void clear(); + virtual void mangle_unused_area(); + virtual void ensure_parsability(); + virtual size_t used_in_words() const; + virtual size_t free_in_words() const; + virtual size_t tlab_capacity(Thread* thr) const; + virtual size_t unsafe_max_tlab_alloc(Thread* thr) const; + + // Allocation (return NULL if full) + virtual HeapWord* allocate(size_t word_size); + virtual HeapWord* cas_allocate(size_t word_size); + + // Debugging + virtual void print_on(outputStream* st) const; + virtual void print_short_on(outputStream* st) const; + virtual void verify(bool allow_dirty) const; + + virtual void set_top(HeapWord* value); +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/mutableSpace.cpp b/hotspot/src/share/vm/gc_implementation/shared/mutableSpace.cpp new file mode 100644 index 00000000000..5c3a9e01029 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/mutableSpace.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_mutableSpace.cpp.incl" + +void MutableSpace::initialize(MemRegion mr, bool clear_space) { + HeapWord* bottom = mr.start(); + HeapWord* end = mr.end(); + + assert(Universe::on_page_boundary(bottom) && Universe::on_page_boundary(end), + "invalid space boundaries"); + set_bottom(bottom); + set_end(end); + + if (clear_space) clear(); +} + +void MutableSpace::clear() { + set_top(bottom()); + if (ZapUnusedHeapArea) mangle_unused_area(); +} + +// This version requires locking. */ +HeapWord* MutableSpace::allocate(size_t size) { + assert(Heap_lock->owned_by_self() || + (SafepointSynchronize::is_at_safepoint() && + Thread::current()->is_VM_thread()), + "not locked"); + HeapWord* obj = top(); + if (pointer_delta(end(), obj) >= size) { + HeapWord* new_top = obj + size; + set_top(new_top); + assert(is_object_aligned((intptr_t)obj) && is_object_aligned((intptr_t)new_top), + "checking alignment"); + return obj; + } else { + return NULL; + } +} + +// This version is lock-free. +HeapWord* MutableSpace::cas_allocate(size_t size) { + do { + HeapWord* obj = top(); + if (pointer_delta(end(), obj) >= size) { + HeapWord* new_top = obj + size; + HeapWord* result = (HeapWord*)Atomic::cmpxchg_ptr(new_top, top_addr(), obj); + // result can be one of two: + // the old top value: the exchange succeeded + // otherwise: the new value of the top is returned. + if (result != obj) { + continue; // another thread beat us to the allocation, try again + } + assert(is_object_aligned((intptr_t)obj) && is_object_aligned((intptr_t)new_top), + "checking alignment"); + return obj; + } else { + return NULL; + } + } while (true); +} + +// Try to deallocate previous allocation. Returns true upon success. +bool MutableSpace::cas_deallocate(HeapWord *obj, size_t size) { + HeapWord* expected_top = obj + size; + return (HeapWord*)Atomic::cmpxchg_ptr(obj, top_addr(), expected_top) == expected_top; +} + +void MutableSpace::oop_iterate(OopClosure* cl) { + HeapWord* obj_addr = bottom(); + HeapWord* t = top(); + // Could call objects iterate, but this is easier. + while (obj_addr < t) { + obj_addr += oop(obj_addr)->oop_iterate(cl); + } +} + +void MutableSpace::object_iterate(ObjectClosure* cl) { + HeapWord* p = bottom(); + while (p < top()) { + cl->do_object(oop(p)); + p += oop(p)->size(); + } +} + +void MutableSpace::print_short() const { print_short_on(tty); } +void MutableSpace::print_short_on( outputStream* st) const { + st->print(" space " SIZE_FORMAT "K, %d%% used", capacity_in_bytes() / K, + (int) ((double) used_in_bytes() * 100 / capacity_in_bytes())); +} + +void MutableSpace::print() const { print_on(tty); } +void MutableSpace::print_on(outputStream* st) const { + MutableSpace::print_short_on(st); + st->print_cr(" [" INTPTR_FORMAT "," INTPTR_FORMAT "," INTPTR_FORMAT ")", + bottom(), top(), end()); +} + +void MutableSpace::verify(bool allow_dirty) const { + HeapWord* p = bottom(); + HeapWord* t = top(); + HeapWord* prev_p = NULL; + while (p < t) { + oop(p)->verify(); + prev_p = p; + p += oop(p)->size(); + } + guarantee(p == top(), "end of last object must match end of space"); +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/mutableSpace.hpp b/hotspot/src/share/vm/gc_implementation/shared/mutableSpace.hpp new file mode 100644 index 00000000000..f9d43ba262f --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/mutableSpace.hpp @@ -0,0 +1,102 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A MutableSpace is a subtype of ImmutableSpace that supports the +// concept of allocation. This includes the concepts that a space may +// be only partially full, and the querry methods that go with such +// an assumption. +// +// Invariant: (ImmutableSpace +) bottom() <= top() <= end() +// top() is inclusive and end() is exclusive. + +class MutableSpace: public ImmutableSpace { + friend class VMStructs; + protected: + HeapWord* _top; + + public: + virtual ~MutableSpace() {} + MutableSpace() { _top = NULL; } + // Accessors + HeapWord* top() const { return _top; } + virtual void set_top(HeapWord* value) { _top = value; } + + HeapWord** top_addr() { return &_top; } + HeapWord** end_addr() { return &_end; } + + virtual void set_bottom(HeapWord* value) { _bottom = value; } + virtual void set_end(HeapWord* value) { _end = value; } + + // Returns a subregion containing all objects in this space. + MemRegion used_region() { return MemRegion(bottom(), top()); } + + // Initialization + virtual void initialize(MemRegion mr, bool clear_space); + virtual void clear(); + virtual void update() { } + virtual void accumulate_statistics() { } + + // Overwrites the unused portion of this space. Note that some collectors + // may use this "scratch" space during collections. + virtual void mangle_unused_area() { + mangle_region(MemRegion(_top, _end)); + } + virtual void ensure_parsability() { } + + void mangle_region(MemRegion mr) { + debug_only(Copy::fill_to_words(mr.start(), mr.word_size(), badHeapWord)); + } + + // Boolean querries. + bool is_empty() const { return used_in_words() == 0; } + bool not_empty() const { return used_in_words() > 0; } + bool contains(const void* p) const { return _bottom <= p && p < _end; } + + // Size computations. Sizes are in bytes. + size_t used_in_bytes() const { return used_in_words() * HeapWordSize; } + size_t free_in_bytes() const { return free_in_words() * HeapWordSize; } + + // Size computations. Sizes are in heapwords. + virtual size_t used_in_words() const { return pointer_delta(top(), bottom()); } + virtual size_t free_in_words() const { return pointer_delta(end(), top()); } + virtual size_t tlab_capacity(Thread* thr) const { return capacity_in_bytes(); } + virtual size_t unsafe_max_tlab_alloc(Thread* thr) const { return free_in_bytes(); } + + // Allocation (return NULL if full) + virtual HeapWord* allocate(size_t word_size); + virtual HeapWord* cas_allocate(size_t word_size); + // Optional deallocation. Used in NUMA-allocator. + bool cas_deallocate(HeapWord *obj, size_t size); + + // Iteration. + void oop_iterate(OopClosure* cl); + void object_iterate(ObjectClosure* cl); + + // Debugging + virtual void print() const; + virtual void print_on(outputStream* st) const; + virtual void print_short() const; + virtual void print_short_on(outputStream* st) const; + virtual void verify(bool allow_dirty) const; +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/spaceCounters.cpp b/hotspot/src/share/vm/gc_implementation/shared/spaceCounters.cpp new file mode 100644 index 00000000000..b8875727957 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/spaceCounters.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_spaceCounters.cpp.incl" + +SpaceCounters::SpaceCounters(const char* name, int ordinal, size_t max_size, + MutableSpace* m, GenerationCounters* gc) : + _object_space(m) { + + if (UsePerfData) { + EXCEPTION_MARK; + ResourceMark rm; + + const char* cns = PerfDataManager::name_space(gc->name_space(), "space", + ordinal); + + _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1); + strcpy(_name_space, cns); + + const char* cname = PerfDataManager::counter_name(_name_space, "name"); + PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "maxCapacity"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, + (jlong)max_size, CHECK); + + cname = PerfDataManager::counter_name(_name_space, "capacity"); + _capacity = PerfDataManager::create_variable(SUN_GC, cname, + PerfData::U_Bytes, + _object_space->capacity_in_bytes(), CHECK); + + cname = PerfDataManager::counter_name(_name_space, "used"); + _used = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + new MutableSpaceUsedHelper(_object_space), + CHECK); + + cname = PerfDataManager::counter_name(_name_space, "initCapacity"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, + _object_space->capacity_in_bytes(), CHECK); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/spaceCounters.hpp b/hotspot/src/share/vm/gc_implementation/shared/spaceCounters.hpp new file mode 100644 index 00000000000..7d667174d22 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/spaceCounters.hpp @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A SpaceCounter is a holder class for performance counters +// that track a space; + +class SpaceCounters: public CHeapObj { + friend class VMStructs; + + private: + PerfVariable* _capacity; + PerfVariable* _used; + + // Constant PerfData types don't need to retain a reference. + // However, it's a good idea to document them here. + // PerfConstant* _size; + + MutableSpace* _object_space; + char* _name_space; + + public: + + SpaceCounters(const char* name, int ordinal, size_t max_size, + MutableSpace* m, GenerationCounters* gc); + + ~SpaceCounters() { + if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); + } + + inline void update_capacity() { + _capacity->set_value(_object_space->capacity_in_bytes()); + } + + inline void update_used() { + _used->set_value(_object_space->used_in_bytes()); + } + + inline void update_all() { + update_used(); + update_capacity(); + } + + const char* name_space() const { return _name_space; } +}; + +class MutableSpaceUsedHelper: public PerfLongSampleHelper { + private: + MutableSpace* _m; + + public: + MutableSpaceUsedHelper(MutableSpace* m) : _m(m) { } + + inline jlong take_sample() { + return _m->used_in_bytes(); + } +}; diff --git a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp new file mode 100644 index 00000000000..7108f318ec0 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp @@ -0,0 +1,146 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +# include "incls/_precompiled.incl" +# include "incls/_vmGCOperations.cpp.incl" + +HS_DTRACE_PROBE_DECL1(hotspot, gc__begin, bool); +HS_DTRACE_PROBE_DECL(hotspot, gc__end); + +// The same dtrace probe can't be inserted in two different files, so we +// have to call it here, so it's only in one file. Can't create new probes +// for the other file anymore. The dtrace probes have to remain stable. +void VM_GC_Operation::notify_gc_begin(bool full) { + HS_DTRACE_PROBE1(hotspot, gc__begin, full); +} + +void VM_GC_Operation::notify_gc_end() { + HS_DTRACE_PROBE(hotspot, gc__end); +} + +void VM_GC_Operation::acquire_pending_list_lock() { + // we may enter this with pending exception set + instanceRefKlass::acquire_pending_list_lock(&_pending_list_basic_lock); +} + + +void VM_GC_Operation::release_and_notify_pending_list_lock() { + + instanceRefKlass::release_and_notify_pending_list_lock(&_pending_list_basic_lock); +} + +// Allocations may fail in several threads at about the same time, +// resulting in multiple gc requests. We only want to do one of them. +// In case a GC locker is active and the need for a GC is already signalled, +// we want to skip this GC attempt altogether, without doing a futile +// safepoint operation. +bool VM_GC_Operation::skip_operation() const { + bool skip = (_gc_count_before != Universe::heap()->total_collections()); + if (_full && skip) { + skip = (_full_gc_count_before != Universe::heap()->total_full_collections()); + } + if (!skip && GC_locker::is_active_and_needs_gc()) { + skip = Universe::heap()->is_maximal_no_gc(); + assert(!(skip && (_gc_cause == GCCause::_gc_locker)), + "GC_locker cannot be active when initiating GC"); + } + return skip; +} + +bool VM_GC_Operation::doit_prologue() { + assert(Thread::current()->is_Java_thread(), "just checking"); + + acquire_pending_list_lock(); + // If the GC count has changed someone beat us to the collection + // Get the Heap_lock after the pending_list_lock. + Heap_lock->lock(); + // Check invocations + if (skip_operation()) { + // skip collection + Heap_lock->unlock(); + release_and_notify_pending_list_lock(); + _prologue_succeeded = false; + } else { + _prologue_succeeded = true; + } + return _prologue_succeeded; +} + + +void VM_GC_Operation::doit_epilogue() { + assert(Thread::current()->is_Java_thread(), "just checking"); + // Release the Heap_lock first. + Heap_lock->unlock(); + release_and_notify_pending_list_lock(); +} + +bool VM_GC_HeapInspection::doit_prologue() { + if (Universe::heap()->supports_heap_inspection()) { + return VM_GC_Operation::doit_prologue(); + } else { + return false; + } +} + +bool VM_GC_HeapInspection::skip_operation() const { + assert(Universe::heap()->supports_heap_inspection(), "huh?"); + return false; +} + +void VM_GC_HeapInspection::doit() { + HandleMark hm; + CollectedHeap* ch = Universe::heap(); + if (_full_gc) { + ch->collect_as_vm_thread(GCCause::_heap_inspection); + } else { + // make the heap parsable (no need to retire TLABs) + ch->ensure_parsability(false); + } + HeapInspection::heap_inspection(_out); +} + + +void VM_GenCollectForAllocation::doit() { + JvmtiGCForAllocationMarker jgcm; + notify_gc_begin(false); + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + GCCauseSetter gccs(gch, _gc_cause); + _res = gch->satisfy_failed_allocation(_size, _tlab); + assert(gch->is_in_reserved_or_null(_res), "result not in heap"); + + if (_res == NULL && GC_locker::is_active_and_needs_gc()) { + set_gc_locked(); + } + notify_gc_end(); +} + +void VM_GenCollectFull::doit() { + JvmtiGCFullMarker jgcm; + notify_gc_begin(true); + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + GCCauseSetter gccs(gch, _gc_cause); + gch->do_full_collection(gch->must_clear_all_soft_refs(), _max_level); + notify_gc_end(); +} diff --git a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.hpp b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.hpp new file mode 100644 index 00000000000..ee850f70e8f --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.hpp @@ -0,0 +1,168 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The following class hierarchy represents +// a set of operations (VM_Operation) related to GC. +// +// VM_Operation +// VM_GC_Operation +// VM_GC_HeapInspection +// VM_GenCollectForAllocation +// VM_GenCollectFull +// VM_GenCollectFullConcurrent +// VM_ParallelGCFailedAllocation +// VM_ParallelGCFailedPermanentAllocation +// VM_ParallelGCSystemGC +// VM_GC_Operation +// - implements methods common to all classes in the hierarchy: +// prevents multiple gc requests and manages lock on heap; +// +// VM_GC_HeapInspection +// - prints class histogram on SIGBREAK if PrintClassHistogram +// is specified; and also the attach "inspectheap" operation +// +// VM_GenCollectForAllocation +// VM_ParallelGCFailedAllocation +// VM_ParallelGCFailedPermanentAllocation +// - this operation is invoked when allocation is failed; +// operation performs garbage collection and tries to +// allocate afterwards; +// +// VM_GenCollectFull +// VM_GenCollectFullConcurrent +// VM_ParallelGCSystemGC +// - these operations preform full collection of heaps of +// different kind +// + +class VM_GC_Operation: public VM_Operation { + protected: + BasicLock _pending_list_basic_lock; // for refs pending list notification (PLL) + unsigned int _gc_count_before; // gc count before acquiring PLL + unsigned int _full_gc_count_before; // full gc count before acquiring PLL + bool _full; // whether a "full" collection + bool _prologue_succeeded; // whether doit_prologue succeeded + GCCause::Cause _gc_cause; // the putative cause for this gc op + bool _gc_locked; // will be set if gc was locked + + virtual bool skip_operation() const; + + // java.lang.ref.Reference support + void acquire_pending_list_lock(); + void release_and_notify_pending_list_lock(); + + public: + VM_GC_Operation(unsigned int gc_count_before, + unsigned int full_gc_count_before = 0, + bool full = false) { + _full = full; + _prologue_succeeded = false; + _gc_count_before = gc_count_before; + + // A subclass constructor will likely overwrite the following + _gc_cause = GCCause::_no_cause_specified; + + _gc_locked = false; + + if (full) { + _full_gc_count_before = full_gc_count_before; + } + } + ~VM_GC_Operation() {} + + // Acquire the reference synchronization lock + virtual bool doit_prologue(); + // Do notifyAll (if needed) and release held lock + virtual void doit_epilogue(); + + virtual bool allow_nested_vm_operations() const { return true; } + bool prologue_succeeded() const { return _prologue_succeeded; } + + void set_gc_locked() { _gc_locked = true; } + bool gc_locked() const { return _gc_locked; } + + static void notify_gc_begin(bool full = false); + static void notify_gc_end(); +}; + + +class VM_GC_HeapInspection: public VM_GC_Operation { + private: + outputStream* _out; + bool _full_gc; + public: + VM_GC_HeapInspection(outputStream* out, bool request_full_gc) : + VM_GC_Operation(0 /* total collections, dummy, ignored */, + 0 /* total full collections, dummy, ignored */, + request_full_gc) { + _out = out; + _full_gc = request_full_gc; + } + + ~VM_GC_HeapInspection() {} + virtual VMOp_Type type() const { return VMOp_GC_HeapInspection; } + virtual bool skip_operation() const; + virtual bool doit_prologue(); + virtual void doit(); +}; + + +class VM_GenCollectForAllocation: public VM_GC_Operation { + private: + HeapWord* _res; + size_t _size; // size of object to be allocated. + bool _tlab; // alloc is of a tlab. + public: + VM_GenCollectForAllocation(size_t size, + bool tlab, + unsigned int gc_count_before) + : VM_GC_Operation(gc_count_before), + _size(size), + _tlab(tlab) { + _res = NULL; + } + ~VM_GenCollectForAllocation() {} + virtual VMOp_Type type() const { return VMOp_GenCollectForAllocation; } + virtual void doit(); + HeapWord* result() const { return _res; } +}; + + +// VM operation to invoke a collection of the heap as a +// GenCollectedHeap heap. +class VM_GenCollectFull: public VM_GC_Operation { + private: + int _max_level; + public: + VM_GenCollectFull(unsigned int gc_count_before, + unsigned int full_gc_count_before, + GCCause::Cause gc_cause, + int max_level) + : VM_GC_Operation(gc_count_before, full_gc_count_before, true /* full */), + _max_level(max_level) + { _gc_cause = gc_cause; } + ~VM_GenCollectFull() {} + virtual VMOp_Type type() const { return VMOp_GenCollectFull; } + virtual void doit(); +}; diff --git a/hotspot/src/share/vm/gc_interface/collectedHeap.cpp b/hotspot/src/share/vm/gc_interface/collectedHeap.cpp new file mode 100644 index 00000000000..c4efcc4d965 --- /dev/null +++ b/hotspot/src/share/vm/gc_interface/collectedHeap.cpp @@ -0,0 +1,207 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_collectedHeap.cpp.incl" + + +#ifdef ASSERT +int CollectedHeap::_fire_out_of_memory_count = 0; +#endif + +// Memory state functions. + +CollectedHeap::CollectedHeap() : + _reserved(), _barrier_set(NULL), _is_gc_active(false), + _total_collections(0), _total_full_collections(0), + _max_heap_capacity(0), + _gc_cause(GCCause::_no_gc), _gc_lastcause(GCCause::_no_gc) { + NOT_PRODUCT(_promotion_failure_alot_count = 0;) + NOT_PRODUCT(_promotion_failure_alot_gc_number = 0;) + + if (UsePerfData) { + EXCEPTION_MARK; + + // create the gc cause jvmstat counters + _perf_gc_cause = PerfDataManager::create_string_variable(SUN_GC, "cause", + 80, GCCause::to_string(_gc_cause), CHECK); + + _perf_gc_lastcause = + PerfDataManager::create_string_variable(SUN_GC, "lastCause", + 80, GCCause::to_string(_gc_lastcause), CHECK); + } +} + + +#ifndef PRODUCT +void CollectedHeap::check_for_bad_heap_word_value(HeapWord* addr, size_t size) { + if (CheckMemoryInitialization && ZapUnusedHeapArea) { + for (size_t slot = 0; slot < size; slot += 1) { + assert((*(intptr_t*) (addr + slot)) != ((intptr_t) badHeapWordVal), + "Found badHeapWordValue in post-allocation check"); + } + } +} + +void CollectedHeap::check_for_non_bad_heap_word_value(HeapWord* addr, size_t size) + { + if (CheckMemoryInitialization && ZapUnusedHeapArea) { + for (size_t slot = 0; slot < size; slot += 1) { + assert((*(intptr_t*) (addr + slot)) == ((intptr_t) badHeapWordVal), + "Found non badHeapWordValue in pre-allocation check"); + } + } +} +#endif // PRODUCT + +#ifdef ASSERT +void CollectedHeap::check_for_valid_allocation_state() { + Thread *thread = Thread::current(); + // How to choose between a pending exception and a potential + // OutOfMemoryError? Don't allow pending exceptions. + // This is a VM policy failure, so how do we exhaustively test it? + assert(!thread->has_pending_exception(), + "shouldn't be allocating with pending exception"); + if (StrictSafepointChecks) { + assert(thread->allow_allocation(), + "Allocation done by thread for which allocation is blocked " + "by No_Allocation_Verifier!"); + // Allocation of an oop can always invoke a safepoint, + // hence, the true argument + thread->check_for_valid_safepoint_state(true); + } +} +#endif + +HeapWord* CollectedHeap::allocate_from_tlab_slow(Thread* thread, size_t size) { + + // Retain tlab and allocate object in shared space if + // the amount free in the tlab is too large to discard. + if (thread->tlab().free() > thread->tlab().refill_waste_limit()) { + thread->tlab().record_slow_allocation(size); + return NULL; + } + + // Discard tlab and allocate a new one. + // To minimize fragmentation, the last TLAB may be smaller than the rest. + size_t new_tlab_size = thread->tlab().compute_size(size); + + thread->tlab().clear_before_allocation(); + + if (new_tlab_size == 0) { + return NULL; + } + + // Allocate a new TLAB... + HeapWord* obj = Universe::heap()->allocate_new_tlab(new_tlab_size); + if (obj == NULL) { + return NULL; + } + if (ZeroTLAB) { + // ..and clear it. + Copy::zero_to_words(obj, new_tlab_size); + } else { + // ...and clear just the allocated object. + Copy::zero_to_words(obj, size); + } + thread->tlab().fill(obj, obj + size, new_tlab_size); + return obj; +} + +oop CollectedHeap::new_store_barrier(oop new_obj) { + // %%% This needs refactoring. (It was imported from the server compiler.) + guarantee(can_elide_tlab_store_barriers(), "store barrier elision not supported"); + BarrierSet* bs = this->barrier_set(); + assert(bs->has_write_region_opt(), "Barrier set does not have write_region"); + int new_size = new_obj->size(); + bs->write_region(MemRegion((HeapWord*)new_obj, new_size)); + return new_obj; +} + +bool CollectedHeap::can_elide_permanent_oop_store_barriers() const { + // %%% This needs refactoring. (It was gating logic from the server compiler.) + guarantee(kind() < CollectedHeap::G1CollectedHeap, ""); + return !UseConcMarkSweepGC; +} + + +HeapWord* CollectedHeap::allocate_new_tlab(size_t size) { + guarantee(false, "thread-local allocation buffers not supported"); + return NULL; +} + +void CollectedHeap::fill_all_tlabs(bool retire) { + assert(UseTLAB, "should not reach here"); + // See note in ensure_parsability() below. + assert(SafepointSynchronize::is_at_safepoint() || + !is_init_completed(), + "should only fill tlabs at safepoint"); + // The main thread starts allocating via a TLAB even before it + // has added itself to the threads list at vm boot-up. + assert(Threads::first() != NULL, + "Attempt to fill tlabs before main thread has been added" + " to threads list is doomed to failure!"); + for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + thread->tlab().make_parsable(retire); + } +} + +void CollectedHeap::ensure_parsability(bool retire_tlabs) { + // The second disjunct in the assertion below makes a concession + // for the start-up verification done while the VM is being + // created. Callers be careful that you know that mutators + // aren't going to interfere -- for instance, this is permissible + // if we are still single-threaded and have either not yet + // started allocating (nothing much to verify) or we have + // started allocating but are now a full-fledged JavaThread + // (and have thus made our TLAB's) available for filling. + assert(SafepointSynchronize::is_at_safepoint() || + !is_init_completed(), + "Should only be called at a safepoint or at start-up" + " otherwise concurrent mutator activity may make heap " + " unparsable again"); + if (UseTLAB) { + fill_all_tlabs(retire_tlabs); + } +} + +void CollectedHeap::accumulate_statistics_all_tlabs() { + if (UseTLAB) { + assert(SafepointSynchronize::is_at_safepoint() || + !is_init_completed(), + "should only accumulate statistics on tlabs at safepoint"); + + ThreadLocalAllocBuffer::accumulate_statistics_before_gc(); + } +} + +void CollectedHeap::resize_all_tlabs() { + if (UseTLAB) { + assert(SafepointSynchronize::is_at_safepoint() || + !is_init_completed(), + "should only resize tlabs at safepoint"); + + ThreadLocalAllocBuffer::resize_all_tlabs(); + } +} diff --git a/hotspot/src/share/vm/gc_interface/collectedHeap.hpp b/hotspot/src/share/vm/gc_interface/collectedHeap.hpp new file mode 100644 index 00000000000..cad60b36a43 --- /dev/null +++ b/hotspot/src/share/vm/gc_interface/collectedHeap.hpp @@ -0,0 +1,539 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A "CollectedHeap" is an implementation of a java heap for HotSpot. This +// is an abstract class: there may be many different kinds of heaps. This +// class defines the functions that a heap must implement, and contains +// infrastructure common to all heaps. + +class BarrierSet; +class ThreadClosure; +class AdaptiveSizePolicy; +class Thread; + +// +// CollectedHeap +// SharedHeap +// GenCollectedHeap +// G1CollectedHeap +// ParallelScavengeHeap +// +class CollectedHeap : public CHeapObj { + friend class VMStructs; + friend class IsGCActiveMark; // Block structured external access to _is_gc_active + +#ifdef ASSERT + static int _fire_out_of_memory_count; +#endif + + protected: + MemRegion _reserved; + BarrierSet* _barrier_set; + bool _is_gc_active; + unsigned int _total_collections; // ... started + unsigned int _total_full_collections; // ... started + size_t _max_heap_capacity; + NOT_PRODUCT(volatile size_t _promotion_failure_alot_count;) + NOT_PRODUCT(volatile size_t _promotion_failure_alot_gc_number;) + + // Reason for current garbage collection. Should be set to + // a value reflecting no collection between collections. + GCCause::Cause _gc_cause; + GCCause::Cause _gc_lastcause; + PerfStringVariable* _perf_gc_cause; + PerfStringVariable* _perf_gc_lastcause; + + // Constructor + CollectedHeap(); + + // Create a new tlab + virtual HeapWord* allocate_new_tlab(size_t size); + + // Fix up tlabs to make the heap well-formed again, + // optionally retiring the tlabs. + virtual void fill_all_tlabs(bool retire); + + // Accumulate statistics on all tlabs. + virtual void accumulate_statistics_all_tlabs(); + + // Reinitialize tlabs before resuming mutators. + virtual void resize_all_tlabs(); + + debug_only(static void check_for_valid_allocation_state();) + + protected: + // Allocate from the current thread's TLAB, with broken-out slow path. + inline static HeapWord* allocate_from_tlab(Thread* thread, size_t size); + static HeapWord* allocate_from_tlab_slow(Thread* thread, size_t size); + + // Allocate an uninitialized block of the given size, or returns NULL if + // this is impossible. + inline static HeapWord* common_mem_allocate_noinit(size_t size, bool is_noref, TRAPS); + + // Like allocate_init, but the block returned by a successful allocation + // is guaranteed initialized to zeros. + inline static HeapWord* common_mem_allocate_init(size_t size, bool is_noref, TRAPS); + + // Same as common_mem version, except memory is allocated in the permanent area + // If there is no permanent area, revert to common_mem_allocate_noinit + inline static HeapWord* common_permanent_mem_allocate_noinit(size_t size, TRAPS); + + // Same as common_mem version, except memory is allocated in the permanent area + // If there is no permanent area, revert to common_mem_allocate_init + inline static HeapWord* common_permanent_mem_allocate_init(size_t size, TRAPS); + + // Helper functions for (VM) allocation. + inline static void post_allocation_setup_common(KlassHandle klass, + HeapWord* obj, size_t size); + inline static void post_allocation_setup_no_klass_install(KlassHandle klass, + HeapWord* objPtr, + size_t size); + + inline static void post_allocation_setup_obj(KlassHandle klass, + HeapWord* obj, size_t size); + + inline static void post_allocation_setup_array(KlassHandle klass, + HeapWord* obj, size_t size, + int length); + + // Clears an allocated object. + inline static void init_obj(HeapWord* obj, size_t size); + + // Verification functions + virtual void check_for_bad_heap_word_value(HeapWord* addr, size_t size) + PRODUCT_RETURN; + virtual void check_for_non_bad_heap_word_value(HeapWord* addr, size_t size) + PRODUCT_RETURN; + + public: + enum Name { + Abstract, + SharedHeap, + GenCollectedHeap, + ParallelScavengeHeap, + G1CollectedHeap + }; + + virtual CollectedHeap::Name kind() const { return CollectedHeap::Abstract; } + + /** + * Returns JNI error code JNI_ENOMEM if memory could not be allocated, + * and JNI_OK on success. + */ + virtual jint initialize() = 0; + + // In many heaps, there will be a need to perform some initialization activities + // after the Universe is fully formed, but before general heap allocation is allowed. + // This is the correct place to place such initialization methods. + virtual void post_initialize() = 0; + + MemRegion reserved_region() const { return _reserved; } + + // Return the number of bytes currently reserved, committed, and used, + // respectively, for holding objects. + size_t reserved_obj_bytes() const { return _reserved.byte_size(); } + + // Future cleanup here. The following functions should specify bytes or + // heapwords as part of their signature. + virtual size_t capacity() const = 0; + virtual size_t used() const = 0; + + // Return "true" if the part of the heap that allocates Java + // objects has reached the maximal committed limit that it can + // reach, without a garbage collection. + virtual bool is_maximal_no_gc() const = 0; + + virtual size_t permanent_capacity() const = 0; + virtual size_t permanent_used() const = 0; + + // Support for java.lang.Runtime.maxMemory(): return the maximum amount of + // memory that the vm could make available for storing 'normal' java objects. + // This is based on the reserved address space, but should not include space + // that the vm uses internally for bookkeeping or temporary storage (e.g., + // perm gen space or, in the case of the young gen, one of the survivor + // spaces). + virtual size_t max_capacity() const = 0; + + // Returns "TRUE" if "p" points into the reserved area of the heap. + bool is_in_reserved(const void* p) const { + return _reserved.contains(p); + } + + bool is_in_reserved_or_null(const void* p) const { + return p == NULL || is_in_reserved(p); + } + + // Returns "TRUE" if "p" points to the head of an allocated object in the + // heap. Since this method can be expensive in general, we restrict its + // use to assertion checking only. + virtual bool is_in(const void* p) const = 0; + + bool is_in_or_null(const void* p) const { + return p == NULL || is_in(p); + } + + // Let's define some terms: a "closed" subset of a heap is one that + // + // 1) contains all currently-allocated objects, and + // + // 2) is closed under reference: no object in the closed subset + // references one outside the closed subset. + // + // Membership in a heap's closed subset is useful for assertions. + // Clearly, the entire heap is a closed subset, so the default + // implementation is to use "is_in_reserved". But this may not be too + // liberal to perform useful checking. Also, the "is_in" predicate + // defines a closed subset, but may be too expensive, since "is_in" + // verifies that its argument points to an object head. The + // "closed_subset" method allows a heap to define an intermediate + // predicate, allowing more precise checking than "is_in_reserved" at + // lower cost than "is_in." + + // One important case is a heap composed of disjoint contiguous spaces, + // such as the Garbage-First collector. Such heaps have a convenient + // closed subset consisting of the allocated portions of those + // contiguous spaces. + + // Return "TRUE" iff the given pointer points into the heap's defined + // closed subset (which defaults to the entire heap). + virtual bool is_in_closed_subset(const void* p) const { + return is_in_reserved(p); + } + + bool is_in_closed_subset_or_null(const void* p) const { + return p == NULL || is_in_closed_subset(p); + } + + // Returns "TRUE" if "p" is allocated as "permanent" data. + // If the heap does not use "permanent" data, returns the same + // value is_in_reserved() would return. + // NOTE: this actually returns true if "p" is in reserved space + // for the space not that it is actually allocated (i.e. in committed + // space). If you need the more conservative answer use is_permanent(). + virtual bool is_in_permanent(const void *p) const = 0; + + // Returns "TRUE" if "p" is in the committed area of "permanent" data. + // If the heap does not use "permanent" data, returns the same + // value is_in() would return. + virtual bool is_permanent(const void *p) const = 0; + + bool is_in_permanent_or_null(const void *p) const { + return p == NULL || is_in_permanent(p); + } + + // Returns "TRUE" if "p" is a method oop in the + // current heap, with high probability. This predicate + // is not stable, in general. + bool is_valid_method(oop p) const; + + void set_gc_cause(GCCause::Cause v) { + if (UsePerfData) { + _gc_lastcause = _gc_cause; + _perf_gc_lastcause->set_value(GCCause::to_string(_gc_lastcause)); + _perf_gc_cause->set_value(GCCause::to_string(v)); + } + _gc_cause = v; + } + GCCause::Cause gc_cause() { return _gc_cause; } + + // Preload classes into the shared portion of the heap, and then dump + // that data to a file so that it can be loaded directly by another + // VM (then terminate). + virtual void preload_and_dump(TRAPS) { ShouldNotReachHere(); } + + // General obj/array allocation facilities. + inline static oop obj_allocate(KlassHandle klass, int size, TRAPS); + inline static oop array_allocate(KlassHandle klass, int size, int length, TRAPS); + inline static oop large_typearray_allocate(KlassHandle klass, int size, int length, TRAPS); + + // Special obj/array allocation facilities. + // Some heaps may want to manage "permanent" data uniquely. These default + // to the general routines if the heap does not support such handling. + inline static oop permanent_obj_allocate(KlassHandle klass, int size, TRAPS); + // permanent_obj_allocate_no_klass_install() does not do the installation of + // the klass pointer in the newly created object (as permanent_obj_allocate() + // above does). This allows for a delay in the installation of the klass + // pointer that is needed during the create of klassKlass's. The + // method post_allocation_install_obj_klass() is used to install the + // klass pointer. + inline static oop permanent_obj_allocate_no_klass_install(KlassHandle klass, + int size, + TRAPS); + inline static void post_allocation_install_obj_klass(KlassHandle klass, + oop obj, + int size); + inline static oop permanent_array_allocate(KlassHandle klass, int size, int length, TRAPS); + + // Raw memory allocation facilities + // The obj and array allocate methods are covers for these methods. + // The permanent allocation method should default to mem_allocate if + // permanent memory isn't supported. + virtual HeapWord* mem_allocate(size_t size, + bool is_noref, + bool is_tlab, + bool* gc_overhead_limit_was_exceeded) = 0; + virtual HeapWord* permanent_mem_allocate(size_t size) = 0; + + // The boundary between a "large" and "small" array of primitives, in words. + virtual size_t large_typearray_limit() = 0; + + // Some heaps may offer a contiguous region for shared non-blocking + // allocation, via inlined code (by exporting the address of the top and + // end fields defining the extent of the contiguous allocation region.) + + // This function returns "true" iff the heap supports this kind of + // allocation. (Default is "no".) + virtual bool supports_inline_contig_alloc() const { + return false; + } + // These functions return the addresses of the fields that define the + // boundaries of the contiguous allocation area. (These fields should be + // physically near to one another.) + virtual HeapWord** top_addr() const { + guarantee(false, "inline contiguous allocation not supported"); + return NULL; + } + virtual HeapWord** end_addr() const { + guarantee(false, "inline contiguous allocation not supported"); + return NULL; + } + + // Some heaps may be in an unparseable state at certain times between + // collections. This may be necessary for efficient implementation of + // certain allocation-related activities. Calling this function before + // attempting to parse a heap ensures that the heap is in a parsable + // state (provided other concurrent activity does not introduce + // unparsability). It is normally expected, therefore, that this + // method is invoked with the world stopped. + // NOTE: if you override this method, make sure you call + // super::ensure_parsability so that the non-generational + // part of the work gets done. See implementation of + // CollectedHeap::ensure_parsability and, for instance, + // that of GenCollectedHeap::ensure_parsability(). + // The argument "retire_tlabs" controls whether existing TLABs + // are merely filled or also retired, thus preventing further + // allocation from them and necessitating allocation of new TLABs. + virtual void ensure_parsability(bool retire_tlabs); + + // Return an estimate of the maximum allocation that could be performed + // without triggering any collection or expansion activity. In a + // generational collector, for example, this is probably the largest + // allocation that could be supported (without expansion) in the youngest + // generation. It is "unsafe" because no locks are taken; the result + // should be treated as an approximation, not a guarantee, for use in + // heuristic resizing decisions. + virtual size_t unsafe_max_alloc() = 0; + + // Section on thread-local allocation buffers (TLABs) + // If the heap supports thread-local allocation buffers, it should override + // the following methods: + // Returns "true" iff the heap supports thread-local allocation buffers. + // The default is "no". + virtual bool supports_tlab_allocation() const { + return false; + } + // The amount of space available for thread-local allocation buffers. + virtual size_t tlab_capacity(Thread *thr) const { + guarantee(false, "thread-local allocation buffers not supported"); + return 0; + } + // An estimate of the maximum allocation that could be performed + // for thread-local allocation buffers without triggering any + // collection or expansion activity. + virtual size_t unsafe_max_tlab_alloc(Thread *thr) const { + guarantee(false, "thread-local allocation buffers not supported"); + return 0; + } + // Can a compiler initialize a new object without store barriers? + // This permission only extends from the creation of a new object + // via a TLAB up to the first subsequent safepoint. + virtual bool can_elide_tlab_store_barriers() const { + guarantee(kind() < CollectedHeap::G1CollectedHeap, "else change or refactor this"); + return true; + } + // If a compiler is eliding store barriers for TLAB-allocated objects, + // there is probably a corresponding slow path which can produce + // an object allocated anywhere. The compiler's runtime support + // promises to call this function on such a slow-path-allocated + // object before performing initializations that have elided + // store barriers. Returns new_obj, or maybe a safer copy thereof. + virtual oop new_store_barrier(oop new_obj); + + // Can a compiler elide a store barrier when it writes + // a permanent oop into the heap? Applies when the compiler + // is storing x to the heap, where x->is_perm() is true. + virtual bool can_elide_permanent_oop_store_barriers() const; + + // Does this heap support heap inspection (+PrintClassHistogram?) + virtual bool supports_heap_inspection() const { + return false; // Until RFE 5023697 is implemented + } + + // Perform a collection of the heap; intended for use in implementing + // "System.gc". This probably implies as full a collection as the + // "CollectedHeap" supports. + virtual void collect(GCCause::Cause cause) = 0; + + // This interface assumes that it's being called by the + // vm thread. It collects the heap assuming that the + // heap lock is already held and that we are executing in + // the context of the vm thread. + virtual void collect_as_vm_thread(GCCause::Cause cause) = 0; + + // Returns the barrier set for this heap + BarrierSet* barrier_set() { return _barrier_set; } + + // Returns "true" iff there is a stop-world GC in progress. (I assume + // that it should answer "false" for the concurrent part of a concurrent + // collector -- dld). + bool is_gc_active() const { return _is_gc_active; } + + // Total number of GC collections (started) + unsigned int total_collections() const { return _total_collections; } + unsigned int total_full_collections() const { return _total_full_collections;} + + // Increment total number of GC collections (started) + // Should be protected but used by PSMarkSweep - cleanup for 1.4.2 + void increment_total_collections(bool full = false) { + _total_collections++; + if (full) { + increment_total_full_collections(); + } + } + + void increment_total_full_collections() { _total_full_collections++; } + + // Return the AdaptiveSizePolicy for the heap. + virtual AdaptiveSizePolicy* size_policy() = 0; + + // Iterate over all the ref-containing fields of all objects, calling + // "cl.do_oop" on each. This includes objects in permanent memory. + virtual void oop_iterate(OopClosure* cl) = 0; + + // Iterate over all objects, calling "cl.do_object" on each. + // This includes objects in permanent memory. + virtual void object_iterate(ObjectClosure* cl) = 0; + + // Behaves the same as oop_iterate, except only traverses + // interior pointers contained in permanent memory. If there + // is no permanent memory, does nothing. + virtual void permanent_oop_iterate(OopClosure* cl) = 0; + + // Behaves the same as object_iterate, except only traverses + // object contained in permanent memory. If there is no + // permanent memory, does nothing. + virtual void permanent_object_iterate(ObjectClosure* cl) = 0; + + // NOTE! There is no requirement that a collector implement these + // functions. + // + // A CollectedHeap is divided into a dense sequence of "blocks"; that is, + // each address in the (reserved) heap is a member of exactly + // one block. The defining characteristic of a block is that it is + // possible to find its size, and thus to progress forward to the next + // block. (Blocks may be of different sizes.) Thus, blocks may + // represent Java objects, or they might be free blocks in a + // free-list-based heap (or subheap), as long as the two kinds are + // distinguishable and the size of each is determinable. + + // Returns the address of the start of the "block" that contains the + // address "addr". We say "blocks" instead of "object" since some heaps + // may not pack objects densely; a chunk may either be an object or a + // non-object. + virtual HeapWord* block_start(const void* addr) const = 0; + + // Requires "addr" to be the start of a chunk, and returns its size. + // "addr + size" is required to be the start of a new chunk, or the end + // of the active area of the heap. + virtual size_t block_size(const HeapWord* addr) const = 0; + + // Requires "addr" to be the start of a block, and returns "TRUE" iff + // the block is an object. + virtual bool block_is_obj(const HeapWord* addr) const = 0; + + // Returns the longest time (in ms) that has elapsed since the last + // time that any part of the heap was examined by a garbage collection. + virtual jlong millis_since_last_gc() = 0; + + // Perform any cleanup actions necessary before allowing a verification. + virtual void prepare_for_verify() = 0; + + virtual void print() const = 0; + virtual void print_on(outputStream* st) const = 0; + + // Print all GC threads (other than the VM thread) + // used by this heap. + virtual void print_gc_threads_on(outputStream* st) const = 0; + void print_gc_threads() { print_gc_threads_on(tty); } + // Iterator for all GC threads (other than VM thread) + virtual void gc_threads_do(ThreadClosure* tc) const = 0; + + // Print any relevant tracing info that flags imply. + // Default implementation does nothing. + virtual void print_tracing_info() const = 0; + + // Heap verification + virtual void verify(bool allow_dirty, bool silent) = 0; + + // Non product verification and debugging. +#ifndef PRODUCT + // Support for PromotionFailureALot. Return true if it's time to cause a + // promotion failure. The no-argument version uses + // this->_promotion_failure_alot_count as the counter. + inline bool promotion_should_fail(volatile size_t* count); + inline bool promotion_should_fail(); + + // Reset the PromotionFailureALot counters. Should be called at the end of a + // GC in which promotion failure ocurred. + inline void reset_promotion_should_fail(volatile size_t* count); + inline void reset_promotion_should_fail(); +#endif // #ifndef PRODUCT + +#ifdef ASSERT + static int fired_fake_oom() { + return (CIFireOOMAt > 1 && _fire_out_of_memory_count >= CIFireOOMAt); + } +#endif +}; + +// Class to set and reset the GC cause for a CollectedHeap. + +class GCCauseSetter : StackObj { + CollectedHeap* _heap; + GCCause::Cause _previous_cause; + public: + GCCauseSetter(CollectedHeap* heap, GCCause::Cause cause) { + assert(SafepointSynchronize::is_at_safepoint(), + "This method manipulates heap state without locking"); + _heap = heap; + _previous_cause = _heap->gc_cause(); + _heap->set_gc_cause(cause); + } + + ~GCCauseSetter() { + assert(SafepointSynchronize::is_at_safepoint(), + "This method manipulates heap state without locking"); + _heap->set_gc_cause(_previous_cause); + } +}; diff --git a/hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp b/hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp new file mode 100644 index 00000000000..5344802b74a --- /dev/null +++ b/hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp @@ -0,0 +1,362 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline allocation implementations. + +void CollectedHeap::post_allocation_setup_common(KlassHandle klass, + HeapWord* obj, + size_t size) { + post_allocation_setup_no_klass_install(klass, obj, size); + post_allocation_install_obj_klass(klass, oop(obj), (int) size); +} + +void CollectedHeap::post_allocation_setup_no_klass_install(KlassHandle klass, + HeapWord* objPtr, + size_t size) { + + oop obj = (oop)objPtr; + + assert(obj != NULL, "NULL object pointer"); + if (UseBiasedLocking && (klass() != NULL)) { + obj->set_mark(klass->prototype_header()); + } else { + // May be bootstrapping + obj->set_mark(markOopDesc::prototype()); + } + + // support low memory notifications (no-op if not enabled) + LowMemoryDetector::detect_low_memory_for_collected_pools(); +} + +void CollectedHeap::post_allocation_install_obj_klass(KlassHandle klass, + oop obj, + int size) { + // These asserts are kind of complicated because of klassKlass + // and the beginning of the world. + assert(klass() != NULL || !Universe::is_fully_initialized(), "NULL klass"); + assert(klass() == NULL || klass()->is_klass(), "not a klass"); + assert(klass() == NULL || klass()->klass_part() != NULL, "not a klass"); + assert(obj != NULL, "NULL object pointer"); + obj->set_klass(klass()); + assert(!Universe::is_fully_initialized() || obj->blueprint() != NULL, + "missing blueprint"); + + // support for JVMTI VMObjectAlloc event (no-op if not enabled) + JvmtiExport::vm_object_alloc_event_collector(obj); + + if (DTraceAllocProbes) { + // support for Dtrace object alloc event (no-op most of the time) + if (klass() != NULL && klass()->klass_part()->name() != NULL) { + SharedRuntime::dtrace_object_alloc(obj); + } + } +} + +void CollectedHeap::post_allocation_setup_obj(KlassHandle klass, + HeapWord* obj, + size_t size) { + post_allocation_setup_common(klass, obj, size); + assert(Universe::is_bootstrapping() || + !((oop)obj)->blueprint()->oop_is_array(), "must not be an array"); +} + +void CollectedHeap::post_allocation_setup_array(KlassHandle klass, + HeapWord* obj, + size_t size, + int length) { + // Set array length before posting jvmti object alloc event + // in post_allocation_setup_common() + assert(length >= 0, "length should be non-negative"); + ((arrayOop)obj)->set_length(length); + post_allocation_setup_common(klass, obj, size); + assert(((oop)obj)->blueprint()->oop_is_array(), "must be an array"); +} + +HeapWord* CollectedHeap::common_mem_allocate_noinit(size_t size, bool is_noref, TRAPS) { + + // Clear unhandled oops for memory allocation. Memory allocation might + // not take out a lock if from tlab, so clear here. + CHECK_UNHANDLED_OOPS_ONLY(THREAD->clear_unhandled_oops();) + + if (HAS_PENDING_EXCEPTION) { + NOT_PRODUCT(guarantee(false, "Should not allocate with exception pending")); + return NULL; // caller does a CHECK_0 too + } + + // We may want to update this, is_noref objects might not be allocated in TLABs. + HeapWord* result = NULL; + if (UseTLAB) { + result = CollectedHeap::allocate_from_tlab(THREAD, size); + if (result != NULL) { + assert(!HAS_PENDING_EXCEPTION, + "Unexpected exception, will result in uninitialized storage"); + return result; + } + } + bool gc_overhead_limit_was_exceeded; + result = Universe::heap()->mem_allocate(size, + is_noref, + false, + &gc_overhead_limit_was_exceeded); + if (result != NULL) { + NOT_PRODUCT(Universe::heap()-> + check_for_non_bad_heap_word_value(result, size)); + assert(!HAS_PENDING_EXCEPTION, + "Unexpected exception, will result in uninitialized storage"); + return result; + } + + + if (!gc_overhead_limit_was_exceeded) { + // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support + report_java_out_of_memory("Java heap space"); + + if (JvmtiExport::should_post_resource_exhausted()) { + JvmtiExport::post_resource_exhausted( + JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP, + "Java heap space"); + } + + THROW_OOP_0(Universe::out_of_memory_error_java_heap()); + } else { + // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support + report_java_out_of_memory("GC overhead limit exceeded"); + + if (JvmtiExport::should_post_resource_exhausted()) { + JvmtiExport::post_resource_exhausted( + JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP, + "GC overhead limit exceeded"); + } + + THROW_OOP_0(Universe::out_of_memory_error_gc_overhead_limit()); + } +} + +HeapWord* CollectedHeap::common_mem_allocate_init(size_t size, bool is_noref, TRAPS) { + HeapWord* obj = common_mem_allocate_noinit(size, is_noref, CHECK_NULL); + init_obj(obj, size); + return obj; +} + +// Need to investigate, do we really want to throw OOM exception here? +HeapWord* CollectedHeap::common_permanent_mem_allocate_noinit(size_t size, TRAPS) { + if (HAS_PENDING_EXCEPTION) { + NOT_PRODUCT(guarantee(false, "Should not allocate with exception pending")); + return NULL; // caller does a CHECK_NULL too + } + +#ifdef ASSERT + if (CIFireOOMAt > 0 && THREAD->is_Compiler_thread() && + ++_fire_out_of_memory_count >= CIFireOOMAt) { + // For testing of OOM handling in the CI throw an OOM and see how + // it does. Historically improper handling of these has resulted + // in crashes which we really don't want to have in the CI. + THROW_OOP_0(Universe::out_of_memory_error_perm_gen()); + } +#endif + + HeapWord* result = Universe::heap()->permanent_mem_allocate(size); + if (result != NULL) { + NOT_PRODUCT(Universe::heap()-> + check_for_non_bad_heap_word_value(result, size)); + assert(!HAS_PENDING_EXCEPTION, + "Unexpected exception, will result in uninitialized storage"); + return result; + } + // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support + report_java_out_of_memory("PermGen space"); + + if (JvmtiExport::should_post_resource_exhausted()) { + JvmtiExport::post_resource_exhausted( + JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR, + "PermGen space"); + } + + THROW_OOP_0(Universe::out_of_memory_error_perm_gen()); +} + +HeapWord* CollectedHeap::common_permanent_mem_allocate_init(size_t size, TRAPS) { + HeapWord* obj = common_permanent_mem_allocate_noinit(size, CHECK_NULL); + init_obj(obj, size); + return obj; +} + +HeapWord* CollectedHeap::allocate_from_tlab(Thread* thread, size_t size) { + assert(UseTLAB, "should use UseTLAB"); + + HeapWord* obj = thread->tlab().allocate(size); + if (obj != NULL) { + return obj; + } + // Otherwise... + return allocate_from_tlab_slow(thread, size); +} + +void CollectedHeap::init_obj(HeapWord* obj, size_t size) { + assert(obj != NULL, "cannot initialize NULL object"); + const size_t hs = oopDesc::header_size(); + assert(size >= hs, "unexpected object size"); + Copy::fill_to_aligned_words(obj + hs, size - hs); +} + +oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) { + debug_only(check_for_valid_allocation_state()); + assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); + assert(size >= 0, "int won't convert to size_t"); + HeapWord* obj = common_mem_allocate_init(size, false, CHECK_NULL); + post_allocation_setup_obj(klass, obj, size); + NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); + return (oop)obj; +} + +oop CollectedHeap::array_allocate(KlassHandle klass, + int size, + int length, + TRAPS) { + debug_only(check_for_valid_allocation_state()); + assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); + assert(size >= 0, "int won't convert to size_t"); + HeapWord* obj = common_mem_allocate_init(size, false, CHECK_NULL); + post_allocation_setup_array(klass, obj, size, length); + NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); + return (oop)obj; +} + +oop CollectedHeap::large_typearray_allocate(KlassHandle klass, + int size, + int length, + TRAPS) { + debug_only(check_for_valid_allocation_state()); + assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); + assert(size >= 0, "int won't convert to size_t"); + HeapWord* obj = common_mem_allocate_init(size, true, CHECK_NULL); + post_allocation_setup_array(klass, obj, size, length); + NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); + return (oop)obj; +} + +oop CollectedHeap::permanent_obj_allocate(KlassHandle klass, int size, TRAPS) { + oop obj = permanent_obj_allocate_no_klass_install(klass, size, CHECK_NULL); + post_allocation_install_obj_klass(klass, obj, size); + NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value((HeapWord*) obj, + size)); + return obj; +} + +oop CollectedHeap::permanent_obj_allocate_no_klass_install(KlassHandle klass, + int size, + TRAPS) { + debug_only(check_for_valid_allocation_state()); + assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); + assert(size >= 0, "int won't convert to size_t"); + HeapWord* obj = common_permanent_mem_allocate_init(size, CHECK_NULL); + post_allocation_setup_no_klass_install(klass, obj, size); + NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); + return (oop)obj; +} + +oop CollectedHeap::permanent_array_allocate(KlassHandle klass, + int size, + int length, + TRAPS) { + debug_only(check_for_valid_allocation_state()); + assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); + assert(size >= 0, "int won't convert to size_t"); + HeapWord* obj = common_permanent_mem_allocate_init(size, CHECK_NULL); + post_allocation_setup_array(klass, obj, size, length); + NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); + return (oop)obj; +} + +// Returns "TRUE" if "p" is a method oop in the +// current heap with high probability. NOTE: The main +// current consumers of this interface are Forte:: +// and ThreadProfiler::. In these cases, the +// interpreter frame from which "p" came, may be +// under construction when sampled asynchronously, so +// the clients want to check that it represents a +// valid method before using it. Nonetheless since +// the clients do not typically lock out GC, the +// predicate is_valid_method() is not stable, so +// it is possible that by the time "p" is used, it +// is no longer valid. +inline bool CollectedHeap::is_valid_method(oop p) const { + return + p != NULL && + + // Check whether it is aligned at a HeapWord boundary. + Space::is_aligned(p) && + + // Check whether "method" is in the allocated part of the + // permanent generation -- this needs to be checked before + // p->klass() below to avoid a SEGV (but see below + // for a potential window of vulnerability). + is_permanent((void*)p) && + + // See if GC is active; however, there is still an + // apparently unavoidable window after this call + // and before the client of this interface uses "p". + // If the client chooses not to lock out GC, then + // it's a risk the client must accept. + !is_gc_active() && + + // Check that p is a methodOop. + p->klass() == Universe::methodKlassObj(); +} + + +#ifndef PRODUCT + +inline bool +CollectedHeap::promotion_should_fail(volatile size_t* count) { + // Access to count is not atomic; the value does not have to be exact. + if (PromotionFailureALot) { + const size_t gc_num = total_collections(); + const size_t elapsed_gcs = gc_num - _promotion_failure_alot_gc_number; + if (elapsed_gcs >= PromotionFailureALotInterval) { + // Test for unsigned arithmetic wrap-around. + if (++*count >= PromotionFailureALotCount) { + *count = 0; + return true; + } + } + } + return false; +} + +inline bool CollectedHeap::promotion_should_fail() { + return promotion_should_fail(&_promotion_failure_alot_count); +} + +inline void CollectedHeap::reset_promotion_should_fail(volatile size_t* count) { + if (PromotionFailureALot) { + _promotion_failure_alot_gc_number = total_collections(); + *count = 0; + } +} + +inline void CollectedHeap::reset_promotion_should_fail() { + reset_promotion_should_fail(&_promotion_failure_alot_count); +} +#endif // #ifndef PRODUCT diff --git a/hotspot/src/share/vm/gc_interface/gcCause.cpp b/hotspot/src/share/vm/gc_interface/gcCause.cpp new file mode 100644 index 00000000000..0552966efc2 --- /dev/null +++ b/hotspot/src/share/vm/gc_interface/gcCause.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_gcCause.cpp.incl" + +const char* GCCause::to_string(GCCause::Cause cause) { + switch (cause) { + case _java_lang_system_gc: + return "System.gc()"; + + case _full_gc_alot: + return "FullGCAlot"; + + case _scavenge_alot: + return "ScavengeAlot"; + + case _allocation_profiler: + return "Allocation Profiler"; + + case _jvmti_force_gc: + return "JvmtiEnv ForceGarbageCollection"; + + case _no_gc: + return "No GC"; + + case _allocation_failure: + return "Allocation Failure"; + + case _gc_locker: + return "GCLocker Initiated GC"; + + case _heap_inspection: + return "Heap Inspection Initiated GC"; + + case _heap_dump: + return "Heap Dump Initiated GC"; + + case _tenured_generation_full: + return "Tenured Generation Full"; + + case _permanent_generation_full: + return "Permanent Generation Full"; + + case _cms_generation_full: + return "CMS Generation Full"; + + case _cms_initial_mark: + return "CMS Initial Mark"; + + case _cms_final_remark: + return "CMS Final Remark"; + + case _old_generation_expanded_on_last_scavenge: + return "Old Generation Expanded On Last Scavenge"; + + case _old_generation_too_full_to_scavenge: + return "Old Generation Too Full To Scavenge"; + + case _last_ditch_collection: + return "Last ditch collection"; + + case _last_gc_cause: + return "ILLEGAL VALUE - last gc cause - ILLEGAL VALUE"; + + default: + return "unknown GCCause"; + } + ShouldNotReachHere(); +} + +#ifndef PRODUCT + +bool GCCause::is_for_full_collection(GCCause::Cause cause) { + bool result; + + // There are more GCCause::Cause types than listed here. + // For brevity, we list only those that cause full collections. + switch (cause) { + case _allocation_failure: + case _tenured_generation_full: + case _permanent_generation_full: + case _cms_generation_full: + case _last_ditch_collection: + result = true; + break; + + default: + result = false; + break; + } + return result; +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/gc_interface/gcCause.hpp b/hotspot/src/share/vm/gc_interface/gcCause.hpp new file mode 100644 index 00000000000..e512b0265e8 --- /dev/null +++ b/hotspot/src/share/vm/gc_interface/gcCause.hpp @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// This class exposes implementation details of the various +// collector(s), and we need to be very careful with it. If +// use of this class grows, we should split it into public +// and implemenation-private "causes". +// + +class GCCause : public AllStatic { + public: + enum Cause { + /* public */ + _java_lang_system_gc, + _full_gc_alot, + _scavenge_alot, + _allocation_profiler, + _jvmti_force_gc, + _gc_locker, + _heap_inspection, + _heap_dump, + + /* implementation independent, but reserved for GC use */ + _no_gc, + _no_cause_specified, + _allocation_failure, + + /* implementation specific */ + + _tenured_generation_full, + _permanent_generation_full, + + _cms_generation_full, + _cms_initial_mark, + _cms_final_remark, + + _old_generation_expanded_on_last_scavenge, + _old_generation_too_full_to_scavenge, + _adaptive_size_policy, + + _last_ditch_collection, + _last_gc_cause + }; + + inline static bool is_user_requested_gc(GCCause::Cause cause) { + return (cause == GCCause::_java_lang_system_gc || + cause == GCCause::_jvmti_force_gc); + } + inline static bool is_serviceability_requested_gc(GCCause::Cause + cause) { + return (cause == GCCause::_jvmti_force_gc || + cause == GCCause::_heap_inspection || + cause == GCCause::_heap_dump); + } + // Return a string describing the GCCause. + static const char* to_string(GCCause::Cause cause); + // Return true if the GCCause is for a full collection. + static bool is_for_full_collection(GCCause::Cause cause) PRODUCT_RETURN0; +}; diff --git a/hotspot/src/share/vm/includeDB_compiler1 b/hotspot/src/share/vm/includeDB_compiler1 new file mode 100644 index 00000000000..37bb58ccc32 --- /dev/null +++ b/hotspot/src/share/vm/includeDB_compiler1 @@ -0,0 +1,435 @@ +// +// Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// NOTE: DO NOT CHANGE THIS COPYRIGHT TO NEW STYLE - IT WILL BREAK makeDeps! + +allocation.hpp c1_globals.hpp + +c1_CFGPrinter.cpp c1_CFGPrinter.hpp +c1_CFGPrinter.cpp c1_IR.hpp +c1_CFGPrinter.cpp c1_InstructionPrinter.hpp +c1_CFGPrinter.cpp c1_LIR.hpp +c1_CFGPrinter.cpp c1_LinearScan.hpp +c1_CFGPrinter.cpp c1_ValueStack.hpp + +c1_CFGPrinter.hpp c1_Compilation.hpp +c1_CFGPrinter.hpp c1_Instruction.hpp + +c1_Canonicalizer.cpp c1_Canonicalizer.hpp +c1_Canonicalizer.cpp c1_InstructionPrinter.hpp +c1_Canonicalizer.cpp ciArray.hpp +c1_Canonicalizer.cpp sharedRuntime.hpp + +c1_Canonicalizer.hpp c1_Instruction.hpp + +c1_CodeStubs.hpp array.hpp +c1_CodeStubs.hpp c1_FrameMap.hpp +c1_CodeStubs.hpp c1_IR.hpp +c1_CodeStubs.hpp c1_Instruction.hpp +c1_CodeStubs.hpp c1_LIR.hpp +c1_CodeStubs.hpp c1_Runtime1.hpp + +c1_CodeStubs_.cpp c1_CodeStubs.hpp +c1_CodeStubs_.cpp c1_FrameMap.hpp +c1_CodeStubs_.cpp c1_LIRAssembler.hpp +c1_CodeStubs_.cpp c1_MacroAssembler.hpp +c1_CodeStubs_.cpp c1_Runtime1.hpp +c1_CodeStubs_.cpp nativeInst_.hpp +c1_CodeStubs_.cpp sharedRuntime.hpp +c1_CodeStubs_.cpp vmreg_.inline.hpp + +c1_Compilation.cpp c1_CFGPrinter.hpp +c1_Compilation.cpp c1_Compilation.hpp +c1_Compilation.cpp c1_IR.hpp +c1_Compilation.cpp c1_LIRAssembler.hpp +c1_Compilation.cpp c1_LinearScan.hpp +c1_Compilation.cpp c1_MacroAssembler.hpp +c1_Compilation.cpp c1_ValueMap.hpp +c1_Compilation.cpp c1_ValueStack.hpp +c1_Compilation.cpp ciEnv.hpp +c1_Compilation.cpp debugInfoRec.hpp +c1_Compilation.hpp exceptionHandlerTable.hpp +c1_Compilation.hpp resourceArea.hpp + +c1_Compiler.cpp allocation.hpp +c1_Compiler.cpp allocation.inline.hpp +c1_Compiler.cpp arguments.hpp +c1_Compiler.cpp c1_Compilation.hpp +c1_Compiler.cpp c1_Compiler.hpp +c1_Compiler.cpp c1_FrameMap.hpp +c1_Compiler.cpp c1_MacroAssembler.hpp +c1_Compiler.cpp c1_Runtime1.hpp +c1_Compiler.cpp c1_ValueType.hpp +c1_Compiler.cpp compileBroker.hpp +c1_Compiler.cpp compilerOracle.hpp +c1_Compiler.cpp interfaceSupport.hpp +c1_Compiler.cpp linkResolver.hpp +c1_Compiler.cpp nativeLookup.hpp +c1_Compiler.cpp resourceArea.hpp +c1_Compiler.cpp sharedRuntime.hpp + +c1_Compiler.hpp abstractCompiler.hpp + +c1_Defs.cpp c1_Defs.hpp + +c1_Defs.hpp globalDefinitions.hpp +c1_Defs.hpp register_.hpp + +c1_Defs_.hpp generate_platform_dependent_include + +c1_FpuStackSim.hpp allocation.hpp +c1_FpuStackSim.hpp c1_FrameMap.hpp + +c1_FpuStackSim_.cpp array.hpp +c1_FpuStackSim_.cpp c1_FpuStackSim.hpp +c1_FpuStackSim_.cpp c1_FrameMap.hpp +c1_FpuStackSim_.cpp ostream.hpp + +c1_FpuStackSim_.hpp generate_platform_dependent_include + +c1_FrameMap.cpp c1_FrameMap.hpp +c1_FrameMap.cpp c1_LIR.hpp +c1_FrameMap.cpp sharedRuntime.hpp +c1_FrameMap.cpp vmreg_.inline.hpp + +c1_FrameMap.hpp allocation.hpp +c1_FrameMap.hpp assembler.hpp +c1_FrameMap.hpp c1_Defs.hpp +c1_FrameMap.hpp c1_LIR.hpp +c1_FrameMap.hpp frame.hpp +c1_FrameMap.hpp globalDefinitions.hpp +c1_FrameMap.hpp synchronizer.hpp +c1_FrameMap.hpp vmreg.hpp + +c1_FrameMap_.cpp c1_FrameMap.hpp +c1_FrameMap_.cpp c1_LIR.hpp +c1_FrameMap_.cpp sharedRuntime.hpp +c1_FrameMap_.cpp vmreg_.inline.hpp + +c1_FrameMap_.hpp generate_platform_dependent_include + +c1_globals.cpp c1_globals.hpp + +c1_globals.hpp c1_globals_.hpp +c1_globals.hpp c1_globals_.hpp +c1_globals.hpp globals.hpp + +c1_globals_.hpp globalDefinitions.hpp +c1_globals_.hpp macros.hpp + +c1_globals_.hpp globalDefinitions.hpp +c1_globals_.hpp macros.hpp + +c1_GraphBuilder.cpp bytecode.hpp +c1_GraphBuilder.cpp c1_CFGPrinter.hpp +c1_GraphBuilder.cpp c1_Canonicalizer.hpp +c1_GraphBuilder.cpp c1_Compilation.hpp +c1_GraphBuilder.cpp c1_GraphBuilder.hpp +c1_GraphBuilder.cpp c1_InstructionPrinter.hpp +c1_GraphBuilder.cpp ciField.hpp +c1_GraphBuilder.cpp ciKlass.hpp +c1_GraphBuilder.cpp sharedRuntime.hpp + +c1_GraphBuilder.hpp c1_IR.hpp +c1_GraphBuilder.hpp c1_Instruction.hpp +c1_GraphBuilder.hpp c1_ValueMap.hpp +c1_GraphBuilder.hpp c1_ValueStack.hpp +c1_GraphBuilder.hpp ciMethodData.hpp +c1_GraphBuilder.hpp ciStreams.hpp + +c1_IR.cpp c1_Compilation.hpp +c1_IR.cpp c1_FrameMap.hpp +c1_IR.cpp c1_GraphBuilder.hpp +c1_IR.cpp c1_IR.hpp +c1_IR.cpp c1_InstructionPrinter.hpp +c1_IR.cpp c1_Optimizer.hpp + +c1_IR.hpp allocation.hpp +c1_IR.hpp c1_Instruction.hpp +c1_IR.hpp ciExceptionHandler.hpp +c1_IR.hpp ciMethod.hpp +c1_IR.hpp ciStreams.hpp + +c1_Instruction.cpp c1_IR.hpp +c1_Instruction.cpp c1_Instruction.hpp +c1_Instruction.cpp c1_InstructionPrinter.hpp +c1_Instruction.cpp c1_ValueStack.hpp +c1_Instruction.cpp ciObjArrayKlass.hpp +c1_Instruction.cpp ciTypeArrayKlass.hpp + +c1_Instruction.hpp c1_Compilation.hpp +c1_Instruction.hpp c1_LIR.hpp +c1_Instruction.hpp c1_ValueType.hpp +c1_Instruction.hpp ciField.hpp + +c1_InstructionPrinter.cpp c1_InstructionPrinter.hpp +c1_InstructionPrinter.cpp c1_ValueStack.hpp +c1_InstructionPrinter.cpp ciArray.hpp +c1_InstructionPrinter.cpp ciInstance.hpp +c1_InstructionPrinter.cpp ciObject.hpp + +c1_InstructionPrinter.hpp c1_IR.hpp +c1_InstructionPrinter.hpp c1_Instruction.hpp +c1_InstructionPrinter.hpp c1_Runtime1.hpp + +c1_LIR.cpp c1_InstructionPrinter.hpp +c1_LIR.cpp c1_LIR.hpp +c1_LIR.cpp c1_LIRAssembler.hpp +c1_LIR.cpp ciInstance.hpp +c1_LIR.cpp sharedRuntime.hpp + +c1_LIR.hpp c1_ValueType.hpp + +c1_LIRAssembler.cpp c1_Compilation.hpp +c1_LIRAssembler.cpp c1_Instruction.hpp +c1_LIRAssembler.cpp c1_InstructionPrinter.hpp +c1_LIRAssembler.cpp c1_LIRAssembler.hpp +c1_LIRAssembler.cpp c1_MacroAssembler.hpp +c1_LIRAssembler.cpp c1_ValueStack.hpp +c1_LIRAssembler.cpp ciInstance.hpp +c1_LIRAssembler.cpp nativeInst_.hpp +c1_LIRAssembler.cpp vmreg_.inline.hpp + +c1_LIRAssembler.hpp c1_CodeStubs.hpp +c1_LIRAssembler.hpp ciMethodData.hpp +c1_LIRAssembler.hpp methodDataOop.hpp +c1_LIRAssembler.hpp top.hpp + +c1_LIRAssembler_.cpp barrierSet.hpp +c1_LIRAssembler_.cpp c1_Compilation.hpp +c1_LIRAssembler_.cpp c1_LIRAssembler.hpp +c1_LIRAssembler_.cpp c1_MacroAssembler.hpp +c1_LIRAssembler_.cpp c1_Runtime1.hpp +c1_LIRAssembler_.cpp c1_ValueStack.hpp +c1_LIRAssembler_.cpp cardTableModRefBS.hpp +c1_LIRAssembler_.cpp ciArrayKlass.hpp +c1_LIRAssembler_.cpp ciInstance.hpp +c1_LIRAssembler_.cpp collectedHeap.hpp +c1_LIRAssembler_.cpp nativeInst_.hpp +c1_LIRAssembler_.cpp objArrayKlass.hpp +c1_LIRAssembler_.cpp sharedRuntime.hpp + +c1_LIRAssembler_.hpp generate_platform_dependent_include + +c1_LIRGenerator.cpp c1_Compilation.hpp +c1_LIRGenerator.cpp c1_FrameMap.hpp +c1_LIRGenerator.cpp c1_Instruction.hpp +c1_LIRGenerator.cpp c1_LIRAssembler.hpp +c1_LIRGenerator.cpp c1_LIRGenerator.hpp +c1_LIRGenerator.cpp c1_ValueStack.hpp +c1_LIRGenerator.cpp ciArrayKlass.hpp +c1_LIRGenerator.cpp ciInstance.hpp +c1_LIRGenerator.cpp sharedRuntime.hpp + +c1_LIRGenerator.hpp c1_Instruction.hpp +c1_LIRGenerator.hpp c1_LIR.hpp +c1_LIRGenerator.hpp ciMethodData.hpp +c1_LIRGenerator.hpp sizes.hpp + +c1_LIRGenerator_.cpp c1_Compilation.hpp +c1_LIRGenerator_.cpp c1_FrameMap.hpp +c1_LIRGenerator_.cpp c1_Instruction.hpp +c1_LIRGenerator_.cpp c1_LIRAssembler.hpp +c1_LIRGenerator_.cpp c1_LIRGenerator.hpp +c1_LIRGenerator_.cpp c1_Runtime1.hpp +c1_LIRGenerator_.cpp c1_ValueStack.hpp +c1_LIRGenerator_.cpp ciArray.hpp +c1_LIRGenerator_.cpp ciObjArrayKlass.hpp +c1_LIRGenerator_.cpp ciTypeArrayKlass.hpp +c1_LIRGenerator_.cpp sharedRuntime.hpp + +c1_LinearScan.cpp c1_CFGPrinter.hpp +c1_LinearScan.cpp c1_Compilation.hpp +c1_LinearScan.cpp c1_FrameMap.hpp +c1_LinearScan.cpp c1_IR.hpp +c1_LinearScan.cpp c1_LIRGenerator.hpp +c1_LinearScan.cpp c1_LinearScan.hpp +c1_LinearScan.cpp c1_ValueStack.hpp +c1_LinearScan.cpp vmreg_.inline.hpp + +c1_LinearScan.hpp c1_FpuStackSim.hpp +c1_LinearScan.hpp c1_FrameMap.hpp +c1_LinearScan.hpp c1_IR.hpp +c1_LinearScan.hpp c1_Instruction.hpp +c1_LinearScan.hpp c1_LIR.hpp +c1_LinearScan.hpp c1_LIRGenerator.hpp + +c1_LinearScan_.cpp c1_Instruction.hpp +c1_LinearScan_.cpp c1_LinearScan.hpp + +c1_LinearScan_.hpp generate_platform_dependent_include + +c1_MacroAssembler.hpp assembler.hpp +c1_MacroAssembler.hpp assembler_.inline.hpp + +c1_MacroAssembler_.cpp arrayOop.hpp +c1_MacroAssembler_.cpp biasedLocking.hpp +c1_MacroAssembler_.cpp c1_MacroAssembler.hpp +c1_MacroAssembler_.cpp c1_Runtime1.hpp +c1_MacroAssembler_.cpp collectedHeap.hpp +c1_MacroAssembler_.cpp interpreter.hpp +c1_MacroAssembler_.cpp markOop.hpp +c1_MacroAssembler_.cpp os.hpp +c1_MacroAssembler_.cpp stubRoutines.hpp +c1_MacroAssembler_.cpp synchronizer.hpp +c1_MacroAssembler_.cpp systemDictionary.hpp + +c1_MacroAssembler_.hpp generate_platform_dependent_include + +c1_Optimizer.cpp c1_Canonicalizer.hpp +c1_Optimizer.cpp c1_Optimizer.hpp +c1_Optimizer.cpp c1_ValueMap.hpp +c1_Optimizer.cpp c1_ValueSet.hpp +c1_Optimizer.cpp c1_ValueStack.hpp + +c1_Optimizer.hpp allocation.hpp +c1_Optimizer.hpp c1_IR.hpp +c1_Optimizer.hpp c1_Instruction.hpp + +c1_Runtime1.cpp allocation.inline.hpp +c1_Runtime1.cpp barrierSet.hpp +c1_Runtime1.cpp biasedLocking.hpp +c1_Runtime1.cpp bytecode.hpp +c1_Runtime1.cpp c1_CodeStubs.hpp +c1_Runtime1.cpp c1_Defs.hpp +c1_Runtime1.cpp c1_FrameMap.hpp +c1_Runtime1.cpp c1_LIRAssembler.hpp +c1_Runtime1.cpp c1_MacroAssembler.hpp +c1_Runtime1.cpp c1_Runtime1.hpp +c1_Runtime1.cpp codeBlob.hpp +c1_Runtime1.cpp codeBuffer.hpp +c1_Runtime1.cpp collectedHeap.hpp +c1_Runtime1.cpp compilationPolicy.hpp +c1_Runtime1.cpp compiledIC.hpp +c1_Runtime1.cpp copy.hpp +c1_Runtime1.cpp disassembler_.hpp +c1_Runtime1.cpp events.hpp +c1_Runtime1.cpp interfaceSupport.hpp +c1_Runtime1.cpp interpreter.hpp +c1_Runtime1.cpp javaCalls.hpp +c1_Runtime1.cpp objArrayKlass.hpp +c1_Runtime1.cpp oop.inline.hpp +c1_Runtime1.cpp oopFactory.hpp +c1_Runtime1.cpp pcDesc.hpp +c1_Runtime1.cpp resourceArea.hpp +c1_Runtime1.cpp scopeDesc.hpp +c1_Runtime1.cpp sharedRuntime.hpp +c1_Runtime1.cpp systemDictionary.hpp +c1_Runtime1.cpp threadCritical.hpp +c1_Runtime1.cpp vframe.hpp +c1_Runtime1.cpp vframeArray.hpp +c1_Runtime1.cpp vmSymbols.hpp +c1_Runtime1.cpp vtableStubs.hpp + +c1_Runtime1.hpp allocation.hpp +c1_Runtime1.hpp c1_FrameMap.hpp +c1_Runtime1.hpp deoptimization.hpp +c1_Runtime1.hpp interpreter.hpp +c1_Runtime1.hpp stubs.hpp + +c1_Runtime1_.cpp c1_Defs.hpp +c1_Runtime1_.cpp c1_MacroAssembler.hpp +c1_Runtime1_.cpp c1_Runtime1.hpp +c1_Runtime1_.cpp compiledICHolderOop.hpp +c1_Runtime1_.cpp interpreter.hpp +c1_Runtime1_.cpp jvmtiExport.hpp +c1_Runtime1_.cpp nativeInst_.hpp +c1_Runtime1_.cpp oop.inline.hpp +c1_Runtime1_.cpp register_.hpp +c1_Runtime1_.cpp sharedRuntime.hpp +c1_Runtime1_.cpp signature.hpp +c1_Runtime1_.cpp vframeArray.hpp +c1_Runtime1_.cpp vmreg_.inline.hpp + +c1_ValueMap.cpp c1_Canonicalizer.hpp +c1_ValueMap.cpp c1_IR.hpp +c1_ValueMap.cpp c1_ValueMap.hpp + +c1_ValueMap.hpp allocation.hpp +c1_ValueMap.hpp c1_Instruction.hpp +c1_ValueMap.hpp c1_ValueSet.hpp + +c1_ValueSet.cpp c1_ValueSet.hpp + +c1_ValueSet.hpp allocation.hpp +c1_ValueSet.hpp bitMap.hpp +c1_ValueSet.hpp c1_Instruction.hpp + +c1_ValueStack.cpp c1_IR.hpp +c1_ValueStack.cpp c1_InstructionPrinter.hpp +c1_ValueStack.cpp c1_ValueStack.hpp + +c1_ValueStack.hpp c1_Instruction.hpp +c1_ValueType.cpp c1_ValueType.hpp +c1_ValueType.cpp ciArray.hpp +c1_ValueType.cpp ciInstance.hpp +c1_ValueType.cpp ciNullObject.hpp + +c1_ValueType.hpp c1_Compilation.hpp +c1_ValueType.hpp ciConstant.hpp + +ciEnv.cpp c1_Runtime1.hpp + +codeBlob.cpp c1_Runtime1.hpp + +compileBroker.cpp c1_Compiler.hpp + +frame.hpp c1_Defs.hpp + +frame_.cpp c1_Runtime1.hpp + +globals.cpp c1_globals.hpp + +globals.hpp c1_globals_.hpp +globals.hpp c1_globals_.hpp + +instanceKlass.cpp c1_Compiler.hpp + +interpreter_.cpp c1_Runtime1.hpp + +java.cpp c1_Compiler.hpp +java.cpp c1_Runtime1.hpp + +nativeInst_.cpp c1_Runtime1.hpp + +oopMap.cpp c1_Defs.hpp + +os_.cpp c1_Runtime1.hpp + +os_.cpp c1_Runtime1.hpp + +registerMap.hpp c1_Defs.hpp + +safepoint.cpp c1_globals.hpp + +sharedRuntime.cpp c1_Runtime1.hpp + +sharedRuntime_.cpp c1_Runtime1.hpp + +thread.cpp c1_Compiler.hpp + +top.hpp c1_globals.hpp + +vmStructs.hpp c1_Runtime1.hpp + diff --git a/hotspot/src/share/vm/includeDB_compiler2 b/hotspot/src/share/vm/includeDB_compiler2 new file mode 100644 index 00000000000..0138cc664f6 --- /dev/null +++ b/hotspot/src/share/vm/includeDB_compiler2 @@ -0,0 +1,1101 @@ +// +// Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +ad_.cpp adGlobals_.hpp +ad_.cpp ad_.hpp +ad_.cpp allocation.inline.hpp +ad_.cpp assembler.hpp +ad_.cpp assembler_.inline.hpp +ad_.cpp biasedLocking.hpp +ad_.cpp cfgnode.hpp +ad_.cpp collectedHeap.inline.hpp +ad_.cpp compiledICHolderOop.hpp +ad_.cpp growableArray.hpp +ad_.cpp locknode.hpp +ad_.cpp markOop.hpp +ad_.cpp methodOop.hpp +ad_.cpp nativeInst_.hpp +ad_.cpp oop.inline.hpp +ad_.cpp oop.inline2.hpp +ad_.cpp opcodes.hpp +ad_.cpp regalloc.hpp +ad_.cpp regmask.hpp +ad_.cpp runtime.hpp +ad_.cpp sharedRuntime.hpp +ad_.cpp stubRoutines.hpp +ad_.cpp vmreg.hpp +ad_.cpp vmreg_.inline.hpp + +ad_.hpp addnode.hpp +ad_.hpp machnode.hpp +ad_.hpp matcher.hpp +ad_.hpp opcodes.hpp +ad_.hpp regalloc.hpp +ad_.hpp resourceArea.hpp +ad_.hpp subnode.hpp +ad_.hpp vectornode.hpp + +ad__clone.cpp ad_.hpp + +ad__expand.cpp ad_.hpp + +ad__format.cpp ad_.hpp + +ad__gen.cpp ad_.hpp +ad__gen.cpp cfgnode.hpp +ad__gen.cpp locknode.hpp + +ad__misc.cpp ad_.hpp + +ad__peephole.cpp ad_.hpp + +ad__pipeline.cpp ad_.hpp + +addnode.cpp addnode.hpp +addnode.cpp allocation.inline.hpp +addnode.cpp cfgnode.hpp +addnode.cpp connode.hpp +addnode.cpp machnode.hpp +addnode.cpp mulnode.hpp +addnode.cpp phaseX.hpp +addnode.cpp subnode.hpp + +addnode.hpp node.hpp +addnode.hpp opcodes.hpp +addnode.hpp type.hpp + +adlcVMDeps.hpp allocation.hpp + +allocation.hpp c2_globals.hpp + +block.cpp allocation.inline.hpp +block.cpp block.hpp +block.cpp cfgnode.hpp +block.cpp chaitin.hpp +block.cpp copy.hpp +block.cpp loopnode.hpp +block.cpp machnode.hpp +block.cpp matcher.hpp +block.cpp opcodes.hpp +block.cpp rootnode.hpp +block.cpp vectset.hpp + +block.hpp multnode.hpp +block.hpp node.hpp +block.hpp phase.hpp + +buildOopMap.cpp addnode.hpp +buildOopMap.cpp callnode.hpp +buildOopMap.cpp compile.hpp +buildOopMap.cpp machnode.hpp +buildOopMap.cpp matcher.hpp +buildOopMap.cpp oopMap.hpp +buildOopMap.cpp phase.hpp +buildOopMap.cpp regalloc.hpp +buildOopMap.cpp rootnode.hpp +buildOopMap.cpp vmreg_.inline.hpp + +bytecodeInfo.cpp callGenerator.hpp +bytecodeInfo.cpp compileLog.hpp +bytecodeInfo.cpp handles.inline.hpp +bytecodeInfo.cpp linkResolver.hpp +bytecodeInfo.cpp objArrayKlass.hpp +bytecodeInfo.cpp parse.hpp +bytecodeInfo.cpp systemDictionary.hpp +bytecodeInfo.cpp vmSymbols.hpp + +bytecodeInterpreter.hpp methodDataOop.hpp + +c2_globals.cpp c2_globals.hpp + +c2_globals.hpp c2_globals_.hpp +c2_globals.hpp c2_globals_.hpp +c2_globals.hpp globals.hpp + +c2_globals_.hpp globalDefinitions.hpp +c2_globals_.hpp macros.hpp + +c2_globals_.hpp globalDefinitions.hpp +c2_globals_.hpp macros.hpp + +c2_init_.cpp compile.hpp + +c2compiler.cpp ad_.hpp +c2compiler.cpp c2compiler.hpp +c2compiler.cpp runtime.hpp + +c2compiler.hpp abstractCompiler.hpp + +callGenerator.cpp addnode.hpp +callGenerator.cpp callGenerator.hpp +callGenerator.cpp callnode.hpp +callGenerator.cpp cfgnode.hpp +callGenerator.cpp compileLog.hpp +callGenerator.cpp connode.hpp +callGenerator.cpp parse.hpp +callGenerator.cpp rootnode.hpp +callGenerator.cpp runtime.hpp +callGenerator.cpp subnode.hpp + +callGenerator.hpp callnode.hpp +callGenerator.hpp compile.hpp +callGenerator.hpp deoptimization.hpp +callGenerator.hpp type.hpp + +callnode.cpp callnode.hpp +callnode.cpp escape.hpp +callnode.cpp locknode.hpp +callnode.cpp machnode.hpp +callnode.cpp matcher.hpp +callnode.cpp oopMap.hpp +callnode.cpp parse.hpp +callnode.cpp regalloc.hpp +callnode.cpp regmask.hpp +callnode.cpp rootnode.hpp +callnode.cpp runtime.hpp + +callnode.hpp connode.hpp +callnode.hpp escape.hpp +callnode.hpp mulnode.hpp +callnode.hpp multnode.hpp +callnode.hpp opcodes.hpp +callnode.hpp phaseX.hpp +callnode.hpp type.hpp + +cfgnode.cpp addnode.hpp +cfgnode.cpp allocation.inline.hpp +cfgnode.cpp cfgnode.hpp +cfgnode.cpp connode.hpp +cfgnode.cpp loopnode.hpp +cfgnode.cpp machnode.hpp +cfgnode.cpp mulnode.hpp +cfgnode.cpp objArrayKlass.hpp +cfgnode.cpp phaseX.hpp +cfgnode.cpp regmask.hpp +cfgnode.cpp runtime.hpp +cfgnode.cpp subnode.hpp +cfgnode.cpp systemDictionary.hpp + +cfgnode.hpp multnode.hpp +cfgnode.hpp node.hpp +cfgnode.hpp opcodes.hpp +cfgnode.hpp type.hpp + +chaitin.cpp addnode.hpp +chaitin.cpp allocation.inline.hpp +chaitin.cpp block.hpp +chaitin.cpp callnode.hpp +chaitin.cpp cfgnode.hpp +chaitin.cpp chaitin.hpp +chaitin.cpp coalesce.hpp +chaitin.cpp compileLog.hpp +chaitin.cpp connode.hpp +chaitin.cpp indexSet.hpp +chaitin.cpp machnode.hpp +chaitin.cpp memnode.hpp +chaitin.cpp oopMap.hpp +chaitin.cpp opcodes.hpp +chaitin.cpp rootnode.hpp + +chaitin.hpp connode.hpp +chaitin.hpp live.hpp +chaitin.hpp matcher.hpp +chaitin.hpp phase.hpp +chaitin.hpp port.hpp +chaitin.hpp regalloc.hpp +chaitin.hpp regmask.hpp +chaitin.hpp resourceArea.hpp +chaitin.hpp vmreg.hpp + +chaitin_.cpp chaitin.hpp +chaitin_.cpp machnode.hpp + +ciEnv.cpp compileLog.hpp +ciEnv.cpp runtime.hpp + +ciMethod.cpp ciTypeFlow.hpp +ciMethod.cpp methodOop.hpp + +ciTypeFlow.cpp allocation.inline.hpp +ciTypeFlow.cpp bytecode.hpp +ciTypeFlow.cpp bytecodes.hpp +ciTypeFlow.cpp ciConstant.hpp +ciTypeFlow.cpp ciField.hpp +ciTypeFlow.cpp ciMethod.hpp +ciTypeFlow.cpp ciMethodData.hpp +ciTypeFlow.cpp ciObjArrayKlass.hpp +ciTypeFlow.cpp ciStreams.hpp +ciTypeFlow.cpp ciTypeArrayKlass.hpp +ciTypeFlow.cpp ciTypeFlow.hpp +ciTypeFlow.cpp compileLog.hpp +ciTypeFlow.cpp deoptimization.hpp +ciTypeFlow.cpp growableArray.hpp + +ciTypeFlow.hpp ciEnv.hpp +ciTypeFlow.hpp ciKlass.hpp +ciTypeFlow.hpp ciMethodBlocks.hpp + +classes.cpp addnode.hpp +classes.cpp callnode.hpp +classes.cpp cfgnode.hpp +classes.cpp connode.hpp +classes.cpp divnode.hpp +classes.cpp locknode.hpp +classes.cpp loopnode.hpp +classes.cpp machnode.hpp +classes.cpp memnode.hpp +classes.cpp mulnode.hpp +classes.cpp multnode.hpp +classes.cpp node.hpp +classes.cpp rootnode.hpp +classes.cpp subnode.hpp +classes.cpp vectornode.hpp + +classes.hpp top.hpp + +coalesce.cpp allocation.inline.hpp +coalesce.cpp block.hpp +coalesce.cpp cfgnode.hpp +coalesce.cpp chaitin.hpp +coalesce.cpp coalesce.hpp +coalesce.cpp connode.hpp +coalesce.cpp indexSet.hpp +coalesce.cpp machnode.hpp +coalesce.cpp matcher.hpp +coalesce.cpp regmask.hpp + +coalesce.hpp phase.hpp + +compile.cpp ad_.hpp +compile.cpp addnode.hpp +compile.cpp arguments.hpp +compile.cpp assembler.hpp +compile.cpp block.hpp +compile.cpp c2compiler.hpp +compile.cpp callGenerator.hpp +compile.cpp callnode.hpp +compile.cpp cfgnode.hpp +compile.cpp chaitin.hpp +compile.cpp compile.hpp +compile.cpp compileLog.hpp +compile.cpp connode.hpp +compile.cpp copy.hpp +compile.cpp divnode.hpp +compile.cpp escape.hpp +compile.cpp exceptionHandlerTable.hpp +compile.cpp loopnode.hpp +compile.cpp machnode.hpp +compile.cpp macro.hpp +compile.cpp matcher.hpp +compile.cpp memnode.hpp +compile.cpp mulnode.hpp +compile.cpp nmethod.hpp +compile.cpp node.hpp +compile.cpp oopMap.hpp +compile.cpp opcodes.hpp +compile.cpp output.hpp +compile.cpp parse.hpp +compile.cpp phaseX.hpp +compile.cpp rootnode.hpp +compile.cpp runtime.hpp +compile.cpp signature.hpp +compile.cpp stubRoutines.hpp +compile.cpp systemDictionary.hpp +compile.cpp timer.hpp +compile.cpp type.hpp +compile.cpp vectornode.hpp + +compile.hpp codeBuffer.hpp +compile.hpp compilerInterface.hpp +compile.hpp compilerOracle.hpp +compile.hpp debugInfoRec.hpp +compile.hpp deoptimization.hpp +compile.hpp dict.hpp +compile.hpp exceptionHandlerTable.hpp +compile.hpp idealGraphPrinter.hpp +compile.hpp phase.hpp +compile.hpp port.hpp +compile.hpp regmask.hpp +compile.hpp resourceArea.hpp +compile.hpp vectset.hpp +compile.hpp vmThread.hpp + +compileBroker.cpp c2compiler.hpp + +connode.cpp addnode.hpp +connode.cpp allocation.inline.hpp +connode.cpp compile.hpp +connode.cpp connode.hpp +connode.cpp escape.hpp +connode.cpp machnode.hpp +connode.cpp matcher.hpp +connode.cpp memnode.hpp +connode.cpp phaseX.hpp +connode.cpp sharedRuntime.hpp +connode.cpp subnode.hpp + +connode.hpp node.hpp +connode.hpp opcodes.hpp +connode.hpp type.hpp + +deoptimization.cpp ad_.hpp + +dfa_.cpp ad_.hpp +dfa_.cpp matcher.hpp +dfa_.cpp opcodes.hpp + +dict.cpp allocation.inline.hpp +dict.cpp dict.hpp +dict.cpp resourceArea.hpp +dict.cpp thread.hpp + +dict.hpp port.hpp + +divnode.cpp addnode.hpp +divnode.cpp allocation.inline.hpp +divnode.cpp connode.hpp +divnode.cpp divnode.hpp +divnode.cpp machnode.hpp +divnode.cpp matcher.hpp +divnode.cpp mulnode.hpp +divnode.cpp phaseX.hpp +divnode.cpp subnode.hpp + +divnode.hpp multnode.hpp +divnode.hpp node.hpp +divnode.hpp opcodes.hpp +divnode.hpp type.hpp + +doCall.cpp addnode.hpp +doCall.cpp callGenerator.hpp +doCall.cpp cfgnode.hpp +doCall.cpp compileLog.hpp +doCall.cpp linkResolver.hpp +doCall.cpp mulnode.hpp +doCall.cpp nativeLookup.hpp +doCall.cpp parse.hpp +doCall.cpp rootnode.hpp +doCall.cpp runtime.hpp +doCall.cpp sharedRuntime.hpp +doCall.cpp subnode.hpp +doCall.cpp vmSymbols.hpp + +domgraph.cpp allocation.hpp +domgraph.cpp block.hpp +domgraph.cpp machnode.hpp +domgraph.cpp phaseX.hpp +domgraph.cpp rootnode.hpp +domgraph.cpp vectset.hpp + +escape.cpp allocation.hpp +escape.cpp bcEscapeAnalyzer.hpp +escape.cpp callnode.hpp +escape.cpp cfgnode.hpp +escape.cpp compile.hpp +escape.cpp escape.hpp +escape.cpp phaseX.hpp +escape.cpp rootnode.hpp +escape.cpp vectset.hpp + +escape.hpp addnode.hpp +escape.hpp growableArray.hpp +escape.hpp node.hpp + +frame.hpp adGlobals_.hpp + +gcm.cpp ad_.hpp +gcm.cpp allocation.inline.hpp +gcm.cpp block.hpp +gcm.cpp c2compiler.hpp +gcm.cpp callnode.hpp +gcm.cpp cfgnode.hpp +gcm.cpp deoptimization.hpp +gcm.cpp machnode.hpp +gcm.cpp opcodes.hpp +gcm.cpp phaseX.hpp +gcm.cpp rootnode.hpp +gcm.cpp runtime.hpp +gcm.cpp vectset.hpp + +generateOptoStub.cpp addnode.hpp +generateOptoStub.cpp callnode.hpp +generateOptoStub.cpp cfgnode.hpp +generateOptoStub.cpp compile.hpp +generateOptoStub.cpp connode.hpp +generateOptoStub.cpp locknode.hpp +generateOptoStub.cpp memnode.hpp +generateOptoStub.cpp mulnode.hpp +generateOptoStub.cpp node.hpp +generateOptoStub.cpp parse.hpp +generateOptoStub.cpp phaseX.hpp +generateOptoStub.cpp rootnode.hpp +generateOptoStub.cpp runtime.hpp +generateOptoStub.cpp type.hpp + +globals.hpp c2_globals_.hpp +globals.hpp c2_globals_.hpp + +globals.cpp c2_globals.hpp + +graphKit.cpp addnode.hpp +graphKit.cpp barrierSet.hpp +graphKit.cpp cardTableModRefBS.hpp +graphKit.cpp collectedHeap.hpp +graphKit.cpp compileLog.hpp +graphKit.cpp deoptimization.hpp +graphKit.cpp graphKit.hpp +graphKit.cpp locknode.hpp +graphKit.cpp machnode.hpp +graphKit.cpp parse.hpp +graphKit.cpp rootnode.hpp +graphKit.cpp runtime.hpp +graphKit.cpp sharedRuntime.hpp + +graphKit.hpp callnode.hpp +graphKit.hpp cfgnode.hpp +graphKit.hpp ciEnv.hpp +graphKit.hpp compile.hpp +graphKit.hpp deoptimization.hpp +graphKit.hpp phaseX.hpp +graphKit.hpp type.hpp + +idealKit.cpp addnode.hpp +idealKit.cpp callnode.hpp +idealKit.cpp cfgnode.hpp +idealKit.cpp idealKit.hpp + +idealKit.hpp connode.hpp +idealKit.hpp mulnode.hpp +idealKit.hpp phaseX.hpp +idealKit.hpp subnode.hpp +idealKit.hpp type.hpp + +ifg.cpp addnode.hpp +ifg.cpp allocation.inline.hpp +ifg.cpp block.hpp +ifg.cpp callnode.hpp +ifg.cpp cfgnode.hpp +ifg.cpp chaitin.hpp +ifg.cpp coalesce.hpp +ifg.cpp connode.hpp +ifg.cpp indexSet.hpp +ifg.cpp machnode.hpp +ifg.cpp memnode.hpp +ifg.cpp oopMap.hpp +ifg.cpp opcodes.hpp + +ifnode.cpp addnode.hpp +ifnode.cpp allocation.inline.hpp +ifnode.cpp cfgnode.hpp +ifnode.cpp connode.hpp +ifnode.cpp phaseX.hpp +ifnode.cpp runtime.hpp +ifnode.cpp subnode.hpp + +indexSet.cpp allocation.inline.hpp +indexSet.cpp chaitin.hpp +indexSet.cpp compile.hpp +indexSet.cpp indexSet.hpp +indexSet.cpp regmask.hpp + +indexSet.hpp allocation.hpp +indexSet.hpp compile.hpp +indexSet.hpp regmask.hpp +indexSet.hpp resourceArea.hpp + +interpreterRuntime.cpp runtime.hpp + +java.cpp compile.hpp +java.cpp compiledIC.hpp +java.cpp indexSet.hpp +java.cpp methodLiveness.hpp +java.cpp runtime.hpp + +lcm.cpp ad_.hpp +lcm.cpp allocation.inline.hpp +lcm.cpp block.hpp +lcm.cpp c2compiler.hpp +lcm.cpp callnode.hpp +lcm.cpp cfgnode.hpp +lcm.cpp machnode.hpp +lcm.cpp runtime.hpp + +library_call.cpp addnode.hpp +library_call.cpp callGenerator.hpp +library_call.cpp cfgnode.hpp +library_call.cpp compileLog.hpp +library_call.cpp idealKit.hpp +library_call.cpp mulnode.hpp +library_call.cpp nativeLookup.hpp +library_call.cpp objArrayKlass.hpp +library_call.cpp parse.hpp +library_call.cpp runtime.hpp +library_call.cpp sharedRuntime.hpp +library_call.cpp subnode.hpp +library_call.cpp systemDictionary.hpp +library_call.cpp vmSymbols.hpp + +live.cpp allocation.inline.hpp +live.cpp callnode.hpp +live.cpp chaitin.hpp +live.cpp live.hpp +live.cpp machnode.hpp + +live.hpp block.hpp +live.hpp indexSet.hpp +live.hpp phase.hpp +live.hpp port.hpp +live.hpp regmask.hpp +live.hpp vectset.hpp + +locknode.cpp locknode.hpp +locknode.cpp parse.hpp +locknode.cpp rootnode.hpp +locknode.cpp runtime.hpp + +locknode.hpp ad_.hpp +locknode.hpp node.hpp +locknode.hpp opcodes.hpp +locknode.hpp subnode.hpp + +loopTransform.cpp addnode.hpp +loopTransform.cpp allocation.inline.hpp +loopTransform.cpp connode.hpp +loopTransform.cpp divnode.hpp +loopTransform.cpp loopnode.hpp +loopTransform.cpp mulnode.hpp +loopTransform.cpp rootnode.hpp +loopTransform.cpp subnode.hpp + +loopUnswitch.cpp allocation.inline.hpp +loopUnswitch.cpp connode.hpp +loopUnswitch.cpp loopnode.hpp +loopUnswitch.cpp rootnode.hpp + +loopnode.cpp addnode.hpp +loopnode.cpp allocation.inline.hpp +loopnode.cpp callnode.hpp +loopnode.cpp ciMethodData.hpp +loopnode.cpp connode.hpp +loopnode.cpp divnode.hpp +loopnode.cpp loopnode.hpp +loopnode.cpp mulnode.hpp +loopnode.cpp rootnode.hpp +loopnode.cpp superword.hpp +loopnode.cpp vectset.hpp + +loopnode.hpp cfgnode.hpp +loopnode.hpp multnode.hpp +loopnode.hpp phaseX.hpp +loopnode.hpp subnode.hpp +loopnode.hpp type.hpp + +loopopts.cpp addnode.hpp +loopopts.cpp allocation.inline.hpp +loopopts.cpp connode.hpp +loopopts.cpp divnode.hpp +loopopts.cpp loopnode.hpp +loopopts.cpp mulnode.hpp +loopopts.cpp rootnode.hpp +loopopts.cpp subnode.hpp + +machnode.cpp collectedHeap.hpp +machnode.cpp machnode.hpp +machnode.cpp regalloc.hpp + +machnode.hpp callnode.hpp +machnode.hpp matcher.hpp +machnode.hpp multnode.hpp +machnode.hpp node.hpp +machnode.hpp regmask.hpp + +macro.cpp addnode.hpp +macro.cpp callnode.hpp +macro.cpp cfgnode.hpp +macro.cpp compile.hpp +macro.cpp connode.hpp +macro.cpp locknode.hpp +macro.cpp loopnode.hpp +macro.cpp macro.hpp +macro.cpp memnode.hpp +macro.cpp node.hpp +macro.cpp phaseX.hpp +macro.cpp rootnode.hpp +macro.cpp runtime.hpp +macro.cpp sharedRuntime.hpp +macro.cpp subnode.hpp +macro.cpp type.hpp +macro.cpp vectset.hpp +macro.hpp phase.hpp + +matcher.cpp ad_.hpp +matcher.cpp addnode.hpp +matcher.cpp allocation.inline.hpp +matcher.cpp atomic.hpp +matcher.cpp callnode.hpp +matcher.cpp connode.hpp +matcher.cpp hpi.hpp +matcher.cpp matcher.hpp +matcher.cpp memnode.hpp +matcher.cpp opcodes.hpp +matcher.cpp os.hpp +matcher.cpp regmask.hpp +matcher.cpp rootnode.hpp +matcher.cpp runtime.hpp +matcher.cpp type.hpp + +matcher.hpp node.hpp +matcher.hpp phaseX.hpp +matcher.hpp regmask.hpp +matcher.hpp resourceArea.hpp +matcher.hpp vectset.hpp + +memnode.cpp addnode.hpp +memnode.cpp allocation.inline.hpp +memnode.cpp cfgnode.hpp +memnode.cpp compile.hpp +memnode.cpp compileLog.hpp +memnode.cpp connode.hpp +memnode.cpp loopnode.hpp +memnode.cpp machnode.hpp +memnode.cpp matcher.hpp +memnode.cpp memnode.hpp +memnode.cpp mulnode.hpp +memnode.cpp objArrayKlass.hpp +memnode.cpp phaseX.hpp +memnode.cpp regmask.hpp +memnode.cpp systemDictionary.hpp + +memnode.hpp multnode.hpp +memnode.hpp node.hpp +memnode.hpp opcodes.hpp +memnode.hpp type.hpp + +methodLiveness.cpp allocation.inline.hpp +methodLiveness.cpp bytecode.hpp +methodLiveness.cpp bytecodes.hpp +methodLiveness.cpp ciStreams.hpp +methodLiveness.cpp methodLiveness.hpp + +methodLiveness.hpp bitMap.hpp +methodLiveness.hpp growableArray.hpp + +mulnode.cpp addnode.hpp +mulnode.cpp allocation.inline.hpp +mulnode.cpp connode.hpp +mulnode.cpp memnode.hpp +mulnode.cpp mulnode.hpp +mulnode.cpp phaseX.hpp +mulnode.cpp subnode.hpp + +mulnode.hpp node.hpp +mulnode.hpp opcodes.hpp +mulnode.hpp type.hpp + +multnode.cpp matcher.hpp +multnode.cpp multnode.hpp +multnode.cpp opcodes.hpp +multnode.cpp phaseX.hpp +multnode.cpp regmask.hpp +multnode.cpp type.hpp + +multnode.hpp node.hpp + +node.cpp allocation.inline.hpp +node.cpp cfgnode.hpp +node.cpp connode.hpp +node.cpp copy.hpp +node.cpp machnode.hpp +node.cpp matcher.hpp +node.cpp node.hpp +node.cpp opcodes.hpp +node.cpp regmask.hpp +node.cpp type.hpp +node.cpp vectset.hpp + +node.hpp compile.hpp +node.hpp port.hpp +node.hpp type.hpp +node.hpp vectset.hpp + +opcodes.cpp classes.hpp +opcodes.cpp globalDefinitions.hpp +opcodes.cpp no_precompiled_headers + +os_.cpp runtime.hpp + +os_.cpp runtime.hpp + +output.cpp allocation.inline.hpp +output.cpp assembler.inline.hpp +output.cpp callnode.hpp +output.cpp cfgnode.hpp +output.cpp debugInfo.hpp +output.cpp debugInfoRec.hpp +output.cpp handles.inline.hpp +output.cpp locknode.hpp +output.cpp machnode.hpp +output.cpp oopMap.hpp +output.cpp output.hpp +output.cpp regalloc.hpp +output.cpp runtime.hpp +output.cpp subnode.hpp +output.cpp type.hpp +output.cpp xmlstream.hpp + +output.hpp ad_.hpp +output.hpp block.hpp +output.hpp node.hpp + +parse.hpp ciMethodData.hpp +parse.hpp ciTypeFlow.hpp +parse.hpp generateOopMap.hpp +parse.hpp graphKit.hpp +parse.hpp methodLiveness.hpp +parse.hpp subnode.hpp +parse.hpp vectset.hpp + +parse1.cpp addnode.hpp +parse1.cpp arguments.hpp +parse1.cpp compileLog.hpp +parse1.cpp copy.hpp +parse1.cpp handles.inline.hpp +parse1.cpp linkResolver.hpp +parse1.cpp locknode.hpp +parse1.cpp memnode.hpp +parse1.cpp methodOop.hpp +parse1.cpp parse.hpp +parse1.cpp rootnode.hpp +parse1.cpp runtime.hpp +parse1.cpp sharedRuntime.hpp + +parse2.cpp addnode.hpp +parse2.cpp ciMethodData.hpp +parse2.cpp compileLog.hpp +parse2.cpp deoptimization.hpp +parse2.cpp divnode.hpp +parse2.cpp linkResolver.hpp +parse2.cpp matcher.hpp +parse2.cpp memnode.hpp +parse2.cpp mulnode.hpp +parse2.cpp parse.hpp +parse2.cpp runtime.hpp +parse2.cpp sharedRuntime.hpp +parse2.cpp systemDictionary.hpp +parse2.cpp universe.inline.hpp +parse2.cpp vmSymbols.hpp + +parse3.cpp addnode.hpp +parse3.cpp compileLog.hpp +parse3.cpp deoptimization.hpp +parse3.cpp handles.inline.hpp +parse3.cpp linkResolver.hpp +parse3.cpp memnode.hpp +parse3.cpp objArrayKlass.hpp +parse3.cpp parse.hpp +parse3.cpp rootnode.hpp +parse3.cpp runtime.hpp +parse3.cpp subnode.hpp +parse3.cpp universe.inline.hpp + +parseHelper.cpp addnode.hpp +parseHelper.cpp compileLog.hpp +parseHelper.cpp memnode.hpp +parseHelper.cpp mulnode.hpp +parseHelper.cpp objArrayKlass.hpp +parseHelper.cpp parse.hpp +parseHelper.cpp rootnode.hpp +parseHelper.cpp runtime.hpp +parseHelper.cpp sharedRuntime.hpp +parseHelper.cpp systemDictionary.hpp + +phase.cpp compile.hpp +phase.cpp compileBroker.hpp +phase.cpp nmethod.hpp +phase.cpp phase.hpp + +phase.hpp port.hpp +phase.hpp timer.hpp + +phaseX.cpp allocation.inline.hpp +phaseX.cpp block.hpp +phaseX.cpp callnode.hpp +phaseX.cpp cfgnode.hpp +phaseX.cpp connode.hpp +phaseX.cpp escape.hpp +phaseX.cpp loopnode.hpp +phaseX.cpp machnode.hpp +phaseX.cpp opcodes.hpp +phaseX.cpp phaseX.hpp +phaseX.cpp regalloc.hpp +phaseX.cpp rootnode.hpp + +phaseX.hpp dict.hpp +phaseX.hpp memnode.hpp +phaseX.hpp node.hpp +phaseX.hpp phase.hpp +phaseX.hpp resourceArea.hpp +phaseX.hpp type.hpp +phaseX.hpp vectset.hpp + +port.cpp port.hpp + +port.hpp top.hpp + +postaloc.cpp allocation.inline.hpp +postaloc.cpp chaitin.hpp +postaloc.cpp machnode.hpp + +reg_split.cpp addnode.hpp +reg_split.cpp allocation.inline.hpp +reg_split.cpp callnode.hpp +reg_split.cpp cfgnode.hpp +reg_split.cpp chaitin.hpp +reg_split.cpp loopnode.hpp +reg_split.cpp machnode.hpp +reg_split.cpp vectset.hpp + +regalloc.cpp regalloc.hpp + +regalloc.hpp block.hpp +regalloc.hpp matcher.hpp +regalloc.hpp phase.hpp +regalloc.hpp vmreg.hpp + +regmask.cpp ad_.hpp +regmask.cpp compile.hpp +regmask.cpp regmask.hpp + +regmask.hpp adGlobals_.hpp +regmask.hpp optoreg.hpp +regmask.hpp port.hpp +regmask.hpp vmreg.hpp + +rootnode.cpp allocation.inline.hpp +rootnode.cpp callnode.hpp +rootnode.cpp cfgnode.hpp +rootnode.cpp phaseX.hpp +rootnode.cpp regmask.hpp +rootnode.cpp rootnode.hpp +rootnode.cpp subnode.hpp +rootnode.cpp type.hpp + +rootnode.hpp loopnode.hpp + +runtime.cpp ad_.hpp +runtime.cpp addnode.hpp +runtime.cpp barrierSet.hpp +runtime.cpp bytecode.hpp +runtime.cpp callnode.hpp +runtime.cpp cfgnode.hpp +runtime.cpp collectedHeap.hpp +runtime.cpp compileBroker.hpp +runtime.cpp compiledIC.hpp +runtime.cpp compilerOracle.hpp +runtime.cpp connode.hpp +runtime.cpp copy.hpp +runtime.cpp fprofiler.hpp +runtime.cpp gcLocker.inline.hpp +runtime.cpp graphKit.hpp +runtime.cpp handles.inline.hpp +runtime.cpp icBuffer.hpp +runtime.cpp interfaceSupport.hpp +runtime.cpp interpreter.hpp +runtime.cpp javaCalls.hpp +runtime.cpp linkResolver.hpp +runtime.cpp machnode.hpp +runtime.cpp matcher.hpp +runtime.cpp memnode.hpp +runtime.cpp mulnode.hpp +runtime.cpp nmethod.hpp +runtime.cpp objArrayKlass.hpp +runtime.cpp oop.inline.hpp +runtime.cpp oopFactory.hpp +runtime.cpp oopMap.hpp +runtime.cpp pcDesc.hpp +runtime.cpp preserveException.hpp +runtime.cpp runtime.hpp +runtime.cpp scopeDesc.hpp +runtime.cpp sharedRuntime.hpp +runtime.cpp signature.hpp +runtime.cpp subnode.hpp +runtime.cpp systemDictionary.hpp +runtime.cpp threadCritical.hpp +runtime.cpp vframe.hpp +runtime.cpp vframeArray.hpp +runtime.cpp vframe_hp.hpp +runtime.cpp vmSymbols.hpp +runtime.cpp vtableStubs.hpp + +runtime.hpp biasedLocking.hpp +runtime.hpp codeBlob.hpp +runtime.hpp deoptimization.hpp +runtime.hpp machnode.hpp +runtime.hpp type.hpp +runtime.hpp vframe.hpp + +runtime_.cpp adGlobals_.hpp +runtime_.cpp ad_.hpp +runtime_.cpp assembler.hpp +runtime_.cpp assembler_.inline.hpp +runtime_.cpp globalDefinitions.hpp +runtime_.cpp interfaceSupport.hpp +runtime_.cpp interpreter.hpp +runtime_.cpp nativeInst_.hpp +runtime_.cpp runtime.hpp +runtime_.cpp sharedRuntime.hpp +runtime_.cpp stubRoutines.hpp +runtime_.cpp systemDictionary.hpp +runtime_.cpp vframeArray.hpp +runtime_.cpp vmreg.hpp +runtime_.cpp vmreg_.inline.hpp + +set.cpp allocation.inline.hpp +set.cpp set.hpp + +set.hpp allocation.hpp +set.hpp port.hpp + +sharedRuntime_.cpp runtime.hpp + +split_if.cpp allocation.inline.hpp +split_if.cpp callnode.hpp +split_if.cpp connode.hpp +split_if.cpp loopnode.hpp + +stubGenerator_.cpp runtime.hpp + +stubRoutines.cpp runtime.hpp + +subnode.cpp addnode.hpp +subnode.cpp allocation.inline.hpp +subnode.cpp cfgnode.hpp +subnode.cpp compileLog.hpp +subnode.cpp connode.hpp +subnode.cpp loopnode.hpp +subnode.cpp matcher.hpp +subnode.cpp mulnode.hpp +subnode.cpp opcodes.hpp +subnode.cpp phaseX.hpp +subnode.cpp sharedRuntime.hpp +subnode.cpp subnode.hpp + +subnode.hpp node.hpp +subnode.hpp opcodes.hpp +subnode.hpp type.hpp + +superword.cpp addnode.hpp +superword.cpp allocation.inline.hpp +superword.cpp callnode.hpp +superword.cpp compileLog.hpp +superword.cpp divnode.hpp +superword.cpp matcher.hpp +superword.cpp memnode.hpp +superword.cpp mulnode.hpp +superword.cpp opcodes.hpp +superword.cpp superword.hpp +superword.cpp vectornode.hpp +superword.cpp vectset.hpp + +superword.hpp connode.hpp +superword.hpp growableArray.hpp +superword.hpp loopnode.hpp +superword.hpp node.hpp +superword.hpp phaseX.hpp +superword.hpp vectornode.hpp + +thread.cpp c2compiler.hpp + +top.hpp c2_globals.hpp + +type.cpp ciTypeFlow.hpp +type.cpp compileLog.hpp +type.cpp dict.hpp +type.cpp gcLocker.hpp +type.cpp instanceKlass.hpp +type.cpp klassKlass.hpp +type.cpp matcher.hpp +type.cpp node.hpp +type.cpp objArrayKlass.hpp +type.cpp oopFactory.hpp +type.cpp opcodes.hpp +type.cpp resourceArea.hpp +type.cpp symbolTable.hpp +type.cpp systemDictionary.hpp +type.cpp type.hpp +type.cpp typeArrayKlass.hpp + +type.hpp adlcVMDeps.hpp +type.hpp handles.hpp +type.hpp port.hpp + +vectornode.cpp allocation.inline.hpp +vectornode.cpp connode.hpp +vectornode.cpp vectornode.hpp + +vectornode.hpp matcher.hpp +vectornode.hpp memnode.hpp +vectornode.hpp node.hpp +vectornode.hpp opcodes.hpp + +vectset.cpp allocation.inline.hpp +vectset.cpp vectset.hpp + +vectset.hpp set.hpp + +vframeArray.cpp runtime.hpp + +vframe_hp.cpp matcher.hpp + +vmStructs.cpp adGlobals_.hpp +vmStructs.cpp matcher.hpp + +vmreg.hpp adGlobals_.hpp +vmreg.hpp adlcVMDeps.hpp +vmreg.hpp ostream.hpp + +vtableStubs.cpp matcher.hpp + +vtableStubs_.cpp ad_.hpp +vtableStubs_.cpp runtime.hpp + +idealGraphPrinter.hpp dict.hpp +idealGraphPrinter.hpp vectset.hpp +idealGraphPrinter.hpp growableArray.hpp +idealGraphPrinter.hpp ostream.hpp + +idealGraphPrinter.cpp idealGraphPrinter.hpp +idealGraphPrinter.cpp chaitin.hpp +idealGraphPrinter.cpp machnode.hpp +idealGraphPrinter.cpp parse.hpp +idealGraphPrinter.cpp threadCritical.hpp + +compile.cpp idealGraphPrinter.hpp +thread.cpp idealGraphPrinter.hpp +phaseX.cpp idealGraphPrinter.hpp +parse2.cpp idealGraphPrinter.hpp +parse1.cpp idealGraphPrinter.hpp +matcher.cpp idealGraphPrinter.hpp +loopnode.cpp idealGraphPrinter.hpp +chaitin.cpp idealGraphPrinter.hpp diff --git a/hotspot/src/share/vm/includeDB_core b/hotspot/src/share/vm/includeDB_core new file mode 100644 index 00000000000..cbf8e561a73 --- /dev/null +++ b/hotspot/src/share/vm/includeDB_core @@ -0,0 +1,4583 @@ +// +// Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// NOTE: DO NOT CHANGE THIS COPYRIGHT TO NEW STYLE - IT WILL BREAK makeDeps! + + +// includeDB format: +// a comment starts with '// ' and goes to the end of the line +// anything else is a pair of filenames. The line "x.cpp y.hpp" means +// "x.cpp must include y.hpp". Similarly, "y.hpp z.hpp" means "any file including +// y.hpp must also include z.hpp, and z.hpp must be included before y.hpp". +// +// Style hint: we try to keep the entries ordered alphabetically, both +// globally (left-hand sides) and within a given file (right-hand sides) +// +// To avoid unnecessary conflicts with the work of other programmers, +// do not delete, move, or reformat pre-existing lines. Do not attempt +// to "optimize" this file incrementally. +// +// ============ Platform dependent include files =========== +// +// Some header files occur in clusters. Header files which depend +// on the token "generate_platform_dependent_include" are included +// directly by other header files, and should not be explicitly declared +// as dependencies. Header files named H.inline.hpp generally contain +// bodies for inline functions declared in H.hpp. +// +// NOTE: Files that use the token "generate_platform_dependent_include" +// are expected to contain macro references like , , ... and +// makedeps has a dependency on these platform files looking like: +// foo_.trailing_string +// (where "trailing_string" can be any legal filename strings but typically +// is "hpp" or "inline.hpp"). +// +// The dependency in makedeps (and enforced) is that an underscore +// will precedure the macro invocation. Note that this restriction +// is only enforced on filenames that have the dependency token +// "generate_platform_dependent_include" so other files using macro +// expansion (typically .cpp files) have no requirement to have +// an underscore precede the macro although this is encouraged for +// readibility. +// +// ======= Circular dependencies and inline functions ========== +// +// (Sometimes, circular dependencies prevent complex function bodies +// from being defined directly in H.hpp. In such cases, a client S.cpp +// of H.hpp must always declare a dependency on H.inline.hpp, which in +// turn will declare a dependency on H.hpp. If by some mischance S.cpp +// declares a dependency on H.hpp, the compiler may complain about missing +// inline function bodies, or (perhaps) the program may fail to link. +// The solution is to have S.cpp depend on H.inline.hpp instead of H.hpp. +// +// Generally, if in response to a source code change the compiler +// issues an error in a file F (which may be either a header or a +// source file), you should consider if the error arises from a missing +// class definition C. If that is the case, find the header file H which +// contains C (often, H=C.hpp, but you may have to search for C's definition). +// Then, add a line to the includeDB file as appropriate. +// +// +// Here are some typical compiler errors that may require changes to includeDB. +// (Messages are taken from Sun's SPARC compiler.) +// +// "klassVtable.cpp", line 96: Error: No_GC_Verifier is not defined. +// Source code: +// No_GC_Verifier no_gc; +// +// The problem is that the class name No_GC_Verifier is not declared, +// so the compiler is confused by the syntax. The solution: +// klassVtable.cpp gcLocker.hpp +// +// Sometimes the compiler has only partial knowledge about a class: +// "privilegedStack.cpp", line 60: Error: cast is not a member of instanceKlass. +// Source code: +// if (_protection_domain != instanceKlass::cast(method->method_holder())->protection_domain()) return false; +// +// Here, instanceKlass is known to the compiler as a type, because of a +// forward declaration somewhere ("class instanceKlass;"). The problem +// is that the compiler has not seen the body of instanceKlass, and so it +// complains that it does not know about "instanceKlass::cast". Solution: +// privilegedStack.cpp instanceKlass.hpp +// +// Here's another example of a missing declaration: +// "privilegedStack.cpp", line 111: Error: The function AllocateHeap must have a prototype. +// Source code: +// _array = NEW_C_HEAP_ARRAY(PrivilegedElement, initial_size); +// +// The problem is that the macro call expands to use a heap function +// which is defined (for technical reasons) in a different file. Solution: +// privilegedStack.cpp allocation.inline.hpp +// The macro is defined in allocation.hpp, while the function is +// defined (as an inline) in allocation.inline.hpp. Generally, if you +// find you need a header H.hpp, and there is also a header +// H.inline.hpp use the latter, because it contains inline definitions +// you will require. + +abstractCompiler.cpp abstractCompiler.hpp +abstractCompiler.cpp mutexLocker.hpp + +abstractCompiler.hpp compilerInterface.hpp + +abstractInterpreter.hpp bytecodes.hpp +abstractInterpreter.hpp interp_masm_.hpp +abstractInterpreter.hpp stubs.hpp +abstractInterpreter.hpp thread_.inline.hpp +abstractInterpreter.hpp top.hpp +abstractInterpreter.hpp vmThread.hpp + +accessFlags.cpp accessFlags.hpp +accessFlags.cpp oop.inline.hpp +accessFlags.cpp os_.inline.hpp + +accessFlags.hpp jvm.h +accessFlags.hpp top.hpp + +allocation.cpp allocation.hpp +allocation.cpp allocation.inline.hpp +allocation.cpp os.hpp +allocation.cpp os_.inline.hpp +allocation.cpp ostream.hpp +allocation.cpp resourceArea.hpp +allocation.cpp task.hpp +allocation.cpp threadCritical.hpp + +allocation.hpp globalDefinitions.hpp +allocation.hpp globals.hpp + +allocation.inline.hpp os.hpp + +allocationStats.cpp allocationStats.hpp + +allocationStats.hpp allocation.hpp +allocationStats.hpp gcUtil.hpp +allocationStats.hpp globalDefinitions.hpp + +aprofiler.cpp aprofiler.hpp +aprofiler.cpp collectedHeap.inline.hpp +aprofiler.cpp oop.inline.hpp +aprofiler.cpp oop.inline2.hpp +aprofiler.cpp permGen.hpp +aprofiler.cpp resourceArea.hpp +aprofiler.cpp space.hpp +aprofiler.cpp systemDictionary.hpp + +aprofiler.hpp allocation.hpp +aprofiler.hpp klass.hpp +aprofiler.hpp klassOop.hpp +aprofiler.hpp top.hpp +aprofiler.hpp universe.hpp + +arguments.cpp allocation.inline.hpp +arguments.cpp arguments.hpp +arguments.cpp cardTableRS.hpp +arguments.cpp compilerOracle.hpp +arguments.cpp defaultStream.hpp +arguments.cpp globals_extension.hpp +arguments.cpp java.hpp +arguments.cpp javaAssertions.hpp +arguments.cpp jvmtiExport.hpp +arguments.cpp management.hpp +arguments.cpp oop.inline.hpp +arguments.cpp os_.inline.hpp +arguments.cpp universe.inline.hpp +arguments.cpp vm_version_.hpp + +arguments.hpp perfData.hpp +arguments.hpp top.hpp + +array.cpp array.hpp +array.cpp resourceArea.hpp +array.cpp thread_.inline.hpp + +array.hpp allocation.hpp +array.hpp allocation.inline.hpp + +arrayKlass.cpp arrayKlass.hpp +arrayKlass.cpp arrayKlassKlass.hpp +arrayKlass.cpp arrayOop.hpp +arrayKlass.cpp collectedHeap.hpp +arrayKlass.cpp collectedHeap.inline.hpp +arrayKlass.cpp gcLocker.hpp +arrayKlass.cpp instanceKlass.hpp +arrayKlass.cpp javaClasses.hpp +arrayKlass.cpp jvmti.h +arrayKlass.cpp objArrayOop.hpp +arrayKlass.cpp oop.inline.hpp +arrayKlass.cpp systemDictionary.hpp +arrayKlass.cpp universe.inline.hpp +arrayKlass.cpp vmSymbols.hpp + +arrayKlass.hpp klass.hpp +arrayKlass.hpp klassOop.hpp +arrayKlass.hpp klassVtable.hpp +arrayKlass.hpp universe.hpp + +arrayKlassKlass.cpp arrayKlassKlass.hpp +arrayKlassKlass.cpp handles.inline.hpp +arrayKlassKlass.cpp javaClasses.hpp +arrayKlassKlass.cpp oop.inline.hpp + +arrayKlassKlass.hpp arrayKlass.hpp +arrayKlassKlass.hpp klassKlass.hpp + +arrayOop.cpp arrayOop.hpp +arrayOop.cpp objArrayOop.hpp +arrayOop.cpp oop.inline.hpp +arrayOop.cpp symbolOop.hpp + +arrayOop.hpp oop.hpp +arrayOop.hpp universe.hpp +arrayOop.hpp universe.inline.hpp + +assembler.cpp assembler.hpp +assembler.cpp assembler.inline.hpp +assembler.cpp assembler_.inline.hpp +assembler.cpp codeBuffer.hpp +assembler.cpp icache.hpp +assembler.cpp os.hpp + +assembler.hpp allocation.hpp +assembler.hpp allocation.inline.hpp +assembler.hpp debug.hpp +assembler.hpp growableArray.hpp +assembler.hpp oopRecorder.hpp +assembler.hpp register_.hpp +assembler.hpp relocInfo.hpp +assembler.hpp top.hpp +assembler.hpp vm_version_.hpp + +assembler.inline.hpp assembler.hpp +assembler.inline.hpp codeBuffer.hpp +assembler.inline.hpp disassembler_.hpp +assembler.inline.hpp threadLocalStorage.hpp + +assembler_.cpp assembler_.inline.hpp +assembler_.cpp biasedLocking.hpp +assembler_.cpp cardTableModRefBS.hpp +assembler_.cpp collectedHeap.hpp +assembler_.cpp interfaceSupport.hpp +assembler_.cpp interpreter.hpp +assembler_.cpp objectMonitor.hpp +assembler_.cpp os.hpp +assembler_.cpp resourceArea.hpp +assembler_.cpp sharedRuntime.hpp +assembler_.cpp stubRoutines.hpp + +assembler_.hpp generate_platform_dependent_include + +assembler_.inline.hpp assembler.inline.hpp +assembler_.inline.hpp codeBuffer.hpp +assembler_.inline.hpp codeCache.hpp +assembler_.inline.hpp handles.inline.hpp + +assembler_.cpp assembler.hpp +assembler_.cpp assembler_.inline.hpp +assembler_.cpp os.hpp +assembler_.cpp threadLocalStorage.hpp + +atomic.cpp atomic.hpp +atomic.cpp atomic_.inline.hpp +atomic.cpp os_.inline.hpp + +atomic.hpp allocation.hpp + +atomic_.inline.hpp atomic.hpp +atomic_.inline.hpp os.hpp +atomic_.inline.hpp vm_version_.hpp + +// attachListener is jck optional, put cpp deps in includeDB_features + +attachListener.hpp allocation.hpp +attachListener.hpp debug.hpp +attachListener.hpp ostream.hpp + +barrierSet.hpp memRegion.hpp +barrierSet.hpp oopsHierarchy.hpp + +barrierSet.inline.hpp barrierSet.hpp +barrierSet.inline.hpp cardTableModRefBS.hpp + +bcEscapeAnalyzer.cpp bcEscapeAnalyzer.hpp +bcEscapeAnalyzer.cpp bitMap.hpp +bcEscapeAnalyzer.cpp bytecode.hpp +bcEscapeAnalyzer.cpp ciConstant.hpp +bcEscapeAnalyzer.cpp ciField.hpp +bcEscapeAnalyzer.cpp ciMethodBlocks.hpp +bcEscapeAnalyzer.cpp ciStreams.hpp + +bcEscapeAnalyzer.hpp allocation.hpp +bcEscapeAnalyzer.hpp ciMethod.hpp +bcEscapeAnalyzer.hpp ciMethodData.hpp +bcEscapeAnalyzer.hpp dependencies.hpp +bcEscapeAnalyzer.hpp growableArray.hpp + +biasedLocking.cpp biasedLocking.hpp +biasedLocking.cpp klass.inline.hpp +biasedLocking.cpp markOop.hpp +biasedLocking.cpp synchronizer.hpp +biasedLocking.cpp task.hpp +biasedLocking.cpp vframe.hpp +biasedLocking.cpp vmThread.hpp +biasedLocking.cpp vm_operations.hpp + +biasedLocking.hpp growableArray.hpp +biasedLocking.hpp handles.hpp + +bitMap.cpp bitMap.hpp +bitMap.cpp bitMap.inline.hpp +bitMap.cpp copy.hpp +bitMap.cpp os_.inline.hpp + +bitMap.hpp allocation.hpp +bitMap.hpp ostream.hpp +bitMap.hpp top.hpp + +bitMap.inline.hpp atomic.hpp +bitMap.inline.hpp bitMap.hpp + +blockOffsetTable.cpp blockOffsetTable.hpp +blockOffsetTable.cpp blockOffsetTable.inline.hpp +blockOffsetTable.cpp collectedHeap.hpp +blockOffsetTable.cpp iterator.hpp +blockOffsetTable.cpp java.hpp +blockOffsetTable.cpp oop.inline.hpp +blockOffsetTable.cpp space.hpp +blockOffsetTable.cpp universe.hpp + +blockOffsetTable.hpp globalDefinitions.hpp +blockOffsetTable.hpp memRegion.hpp +blockOffsetTable.hpp virtualspace.hpp + +blockOffsetTable.inline.hpp blockOffsetTable.hpp +blockOffsetTable.inline.hpp space.hpp + +bytecode.cpp bytecode.hpp +bytecode.cpp constantPoolOop.hpp +bytecode.cpp fieldType.hpp +bytecode.cpp handles.inline.hpp +bytecode.cpp linkResolver.hpp +bytecode.cpp oop.inline.hpp +bytecode.cpp safepoint.hpp +bytecode.cpp signature.hpp + +bytecode.hpp allocation.hpp +bytecode.hpp bytecodes.hpp +bytecode.hpp bytes_.hpp +bytecode.hpp methodOop.hpp + +bytecodeHistogram.cpp bytecodeHistogram.hpp +bytecodeHistogram.cpp growableArray.hpp +bytecodeHistogram.cpp os.hpp +bytecodeHistogram.cpp resourceArea.hpp + +bytecodeHistogram.hpp allocation.hpp +bytecodeHistogram.hpp bytecodes.hpp + +bytecodeInterpreter.cpp no_precompiled_headers +bytecodeInterpreter.cpp bytecodeHistogram.hpp +bytecodeInterpreter.cpp bytecodeInterpreter.hpp +bytecodeInterpreter.cpp bytecodeInterpreter.inline.hpp +bytecodeInterpreter.cpp cardTableModRefBS.hpp +bytecodeInterpreter.cpp collectedHeap.hpp +bytecodeInterpreter.cpp exceptions.hpp +bytecodeInterpreter.cpp frame.inline.hpp +bytecodeInterpreter.cpp handles.inline.hpp +bytecodeInterpreter.cpp interfaceSupport.hpp +bytecodeInterpreter.cpp interpreterRuntime.hpp +bytecodeInterpreter.cpp interpreter.hpp +bytecodeInterpreter.cpp jvmtiExport.hpp +bytecodeInterpreter.cpp objArrayKlass.hpp +bytecodeInterpreter.cpp oop.inline.hpp +bytecodeInterpreter.cpp orderAccess_.inline.hpp +bytecodeInterpreter.cpp resourceArea.hpp +bytecodeInterpreter.cpp sharedRuntime.hpp +bytecodeInterpreter.cpp threadCritical.hpp +bytecodeInterpreter.cpp vmSymbols.hpp + +bytecodeInterpreter_.cpp assembler.hpp +bytecodeInterpreter_.cpp bytecodeInterpreter.hpp +bytecodeInterpreter_.cpp bytecodeInterpreter.inline.hpp +bytecodeInterpreter_.cpp debug.hpp +bytecodeInterpreter_.cpp deoptimization.hpp +bytecodeInterpreter_.cpp frame.inline.hpp +bytecodeInterpreter_.cpp interp_masm_.hpp +bytecodeInterpreter_.cpp interpreterRuntime.hpp +bytecodeInterpreter_.cpp interpreter.hpp +bytecodeInterpreter_.cpp jvmtiExport.hpp +bytecodeInterpreter_.cpp jvmtiThreadState.hpp +bytecodeInterpreter_.cpp methodDataOop.hpp +bytecodeInterpreter_.cpp methodOop.hpp +bytecodeInterpreter_.cpp oop.inline.hpp +bytecodeInterpreter_.cpp sharedRuntime.hpp +bytecodeInterpreter_.cpp stubRoutines.hpp +bytecodeInterpreter_.cpp synchronizer.hpp +bytecodeInterpreter_.cpp vframeArray.hpp + +bytecodeInterpreterWithChecks.cpp bytecodeInterpreter.cpp + +bytecodeInterpreter.hpp allocation.hpp +bytecodeInterpreter.hpp bytes_.hpp +bytecodeInterpreter.hpp frame.hpp +bytecodeInterpreter.hpp globalDefinitions.hpp +bytecodeInterpreter.hpp globals.hpp +bytecodeInterpreter.hpp methodDataOop.hpp +bytecodeInterpreter.hpp methodOop.hpp +bytecodeInterpreter.hpp synchronizer.hpp + +bytecodeInterpreter.inline.hpp bytecodeInterpreter.hpp +bytecodeInterpreter.inline.hpp stubRoutines.hpp + +bytecodeInterpreter_.hpp generate_platform_dependent_include + +bytecodeInterpreter_.inline.hpp generate_platform_dependent_include + +bytecodeStream.cpp bytecodeStream.hpp +bytecodeStream.cpp bytecodes.hpp + +bytecodeStream.hpp allocation.hpp +bytecodeStream.hpp bytecode.hpp +bytecodeStream.hpp bytes_.hpp +bytecodeStream.hpp methodOop.hpp + +bytecodeTracer.cpp bytecodeHistogram.hpp +bytecodeTracer.cpp bytecodeTracer.hpp +bytecodeTracer.cpp bytecodes.hpp +bytecodeTracer.cpp interpreter.hpp +bytecodeTracer.cpp interpreterRuntime.hpp +bytecodeTracer.cpp methodDataOop.hpp +bytecodeTracer.cpp methodOop.hpp +bytecodeTracer.cpp mutexLocker.hpp +bytecodeTracer.cpp resourceArea.hpp +bytecodeTracer.cpp timer.hpp + +bytecodeTracer.hpp allocation.hpp + +bytecodes.cpp bytecodes.hpp +bytecodes.cpp bytes_.hpp +bytecodes.cpp methodOop.hpp +bytecodes.cpp resourceArea.hpp + +bytecodes.hpp allocation.hpp +bytecodes.hpp top.hpp + +bytecodes_.cpp bytecodes.hpp + +bytecodes_.hpp generate_platform_dependent_include + +bytes_.hpp allocation.hpp + +bytes_.inline.hpp generate_platform_dependent_include + +cardTableModRefBS.cpp allocation.inline.hpp +cardTableModRefBS.cpp cardTableModRefBS.hpp +cardTableModRefBS.cpp cardTableRS.hpp +cardTableModRefBS.cpp java.hpp +cardTableModRefBS.cpp mutexLocker.hpp +cardTableModRefBS.cpp sharedHeap.hpp +cardTableModRefBS.cpp space.hpp +cardTableModRefBS.cpp universe.hpp +cardTableModRefBS.cpp virtualspace.hpp + +cardTableModRefBS.hpp modRefBarrierSet.hpp +cardTableModRefBS.hpp oop.hpp +cardTableModRefBS.hpp oop.inline2.hpp + +cardTableRS.cpp allocation.inline.hpp +cardTableRS.cpp cardTableRS.hpp +cardTableRS.cpp genCollectedHeap.hpp +cardTableRS.cpp generation.hpp +cardTableRS.cpp java.hpp +cardTableRS.cpp oop.inline.hpp +cardTableRS.cpp os.hpp +cardTableRS.cpp space.hpp + +cardTableRS.hpp cardTableModRefBS.hpp +cardTableRS.hpp genRemSet.hpp +cardTableRS.hpp memRegion.hpp + +ciArray.cpp ciArray.hpp +ciArray.cpp ciKlass.hpp +ciArray.cpp ciUtilities.hpp + +ciArray.hpp arrayOop.hpp +ciArray.hpp ciObject.hpp +ciArray.hpp objArrayOop.hpp +ciArray.hpp typeArrayOop.hpp + +ciArrayKlass.cpp ciArrayKlass.hpp +ciArrayKlass.cpp ciObjArrayKlass.hpp +ciArrayKlass.cpp ciTypeArrayKlass.hpp +ciArrayKlass.cpp ciUtilities.hpp + +ciArrayKlass.hpp ciKlass.hpp + +ciArrayKlassKlass.hpp ciKlassKlass.hpp + +ciCallProfile.hpp ciClassList.hpp + +ciConstant.cpp allocation.hpp +ciConstant.cpp allocation.inline.hpp +ciConstant.cpp ciConstant.hpp +ciConstant.cpp ciUtilities.hpp + +ciConstant.hpp ciClassList.hpp +ciConstant.hpp ciNullObject.hpp + +ciConstantPoolCache.cpp allocation.hpp +ciConstantPoolCache.cpp allocation.inline.hpp +ciConstantPoolCache.cpp ciConstantPoolCache.hpp +ciConstantPoolCache.cpp ciUtilities.hpp + +ciConstantPoolCache.hpp growableArray.hpp +ciConstantPoolCache.hpp resourceArea.hpp + +ciEnv.cpp allocation.inline.hpp +ciEnv.cpp ciConstant.hpp +ciEnv.cpp ciEnv.hpp +ciEnv.cpp ciField.hpp +ciEnv.cpp ciInstance.hpp +ciEnv.cpp ciInstanceKlass.hpp +ciEnv.cpp ciInstanceKlassKlass.hpp +ciEnv.cpp ciMethod.hpp +ciEnv.cpp ciNullObject.hpp +ciEnv.cpp ciObjArrayKlassKlass.hpp +ciEnv.cpp ciTypeArrayKlassKlass.hpp +ciEnv.cpp ciUtilities.hpp +ciEnv.cpp collectedHeap.inline.hpp +ciEnv.cpp compileBroker.hpp +ciEnv.cpp compileLog.hpp +ciEnv.cpp compilerOracle.hpp +ciEnv.cpp dtrace.hpp +ciEnv.cpp init.hpp +ciEnv.cpp jvmtiExport.hpp +ciEnv.cpp linkResolver.hpp +ciEnv.cpp methodDataOop.hpp +ciEnv.cpp objArrayKlass.hpp +ciEnv.cpp oop.hpp +ciEnv.cpp oop.inline.hpp +ciEnv.cpp oop.inline2.hpp +ciEnv.cpp oopFactory.hpp +ciEnv.cpp reflection.hpp +ciEnv.cpp scopeDesc.hpp +ciEnv.cpp sharedRuntime.hpp +ciEnv.cpp systemDictionary.hpp +ciEnv.cpp universe.inline.hpp +ciEnv.cpp vmSymbols.hpp + +ciEnv.hpp ciClassList.hpp +ciEnv.hpp ciObjectFactory.hpp +ciEnv.hpp debugInfoRec.hpp +ciEnv.hpp dependencies.hpp +ciEnv.hpp exceptionHandlerTable.hpp +ciEnv.hpp oopMap.hpp +ciEnv.hpp thread.hpp + +ciExceptionHandler.cpp ciExceptionHandler.hpp +ciExceptionHandler.cpp ciUtilities.hpp + +ciExceptionHandler.hpp ciClassList.hpp +ciExceptionHandler.hpp ciInstanceKlass.hpp + +ciField.cpp ciField.hpp +ciField.cpp ciInstanceKlass.hpp +ciField.cpp ciUtilities.hpp +ciField.cpp collectedHeap.inline.hpp +ciField.cpp fieldDescriptor.hpp +ciField.cpp linkResolver.hpp +ciField.cpp oop.inline.hpp +ciField.cpp oop.inline2.hpp +ciField.cpp systemDictionary.hpp +ciField.cpp universe.inline.hpp + +ciField.hpp ciClassList.hpp +ciField.hpp ciConstant.hpp +ciField.hpp ciFlags.hpp + +ciFlags.cpp ciFlags.hpp + +ciFlags.hpp accessFlags.hpp +ciFlags.hpp allocation.hpp +ciFlags.hpp ciClassList.hpp +ciFlags.hpp jvm.h + +ciInstance.cpp ciConstant.hpp +ciInstance.cpp ciField.hpp +ciInstance.cpp ciInstance.hpp +ciInstance.cpp ciInstanceKlass.hpp +ciInstance.cpp ciUtilities.hpp +ciInstance.cpp oop.inline.hpp +ciInstance.cpp systemDictionary.hpp + +ciInstance.hpp ciObject.hpp +ciInstance.hpp instanceOop.hpp + +ciInstanceKlass.cpp allocation.hpp +ciInstanceKlass.cpp allocation.inline.hpp +ciInstanceKlass.cpp ciField.hpp +ciInstanceKlass.cpp ciInstance.hpp +ciInstanceKlass.cpp ciInstanceKlass.hpp +ciInstanceKlass.cpp ciUtilities.hpp +ciInstanceKlass.cpp fieldDescriptor.hpp +ciInstanceKlass.cpp oop.inline.hpp +ciInstanceKlass.cpp systemDictionary.hpp + +ciInstanceKlass.hpp ciConstantPoolCache.hpp +ciInstanceKlass.hpp ciFlags.hpp +ciInstanceKlass.hpp ciInstanceKlassKlass.hpp +ciInstanceKlass.hpp ciKlass.hpp +ciInstanceKlass.hpp ciSymbol.hpp + +ciInstanceKlassKlass.cpp ciInstanceKlassKlass.hpp +ciInstanceKlassKlass.cpp ciUtilities.hpp + +ciInstanceKlassKlass.hpp ciKlassKlass.hpp + +ciKlass.cpp ciKlass.hpp +ciKlass.cpp ciSymbol.hpp +ciKlass.cpp ciUtilities.hpp +ciKlass.cpp oop.inline.hpp + +ciKlass.hpp ciType.hpp +ciKlass.hpp klassOop.hpp + +ciKlassKlass.cpp ciKlassKlass.hpp +ciKlassKlass.cpp ciUtilities.hpp + +ciKlassKlass.hpp ciKlass.hpp +ciKlassKlass.hpp ciSymbol.hpp + +ciMethod.cpp abstractCompiler.hpp +ciMethod.cpp allocation.inline.hpp +ciMethod.cpp bcEscapeAnalyzer.hpp +ciMethod.cpp ciCallProfile.hpp +ciMethod.cpp ciExceptionHandler.hpp +ciMethod.cpp ciInstanceKlass.hpp +ciMethod.cpp ciMethod.hpp +ciMethod.cpp ciMethodBlocks.hpp +ciMethod.cpp ciMethodData.hpp +ciMethod.cpp ciMethodKlass.hpp +ciMethod.cpp ciStreams.hpp +ciMethod.cpp ciSymbol.hpp +ciMethod.cpp ciUtilities.hpp +ciMethod.cpp compilerOracle.hpp +ciMethod.cpp deoptimization.hpp +ciMethod.cpp generateOopMap.hpp +ciMethod.cpp interpreter.hpp +ciMethod.cpp linkResolver.hpp +ciMethod.cpp methodLiveness.hpp +ciMethod.cpp nativeLookup.hpp +ciMethod.cpp oop.inline.hpp +ciMethod.cpp oopMapCache.hpp +ciMethod.cpp resourceArea.hpp +ciMethod.cpp systemDictionary.hpp +ciMethod.cpp xmlstream.hpp + +ciMethod.hpp bitMap.hpp +ciMethod.hpp ciFlags.hpp +ciMethod.hpp ciInstanceKlass.hpp +ciMethod.hpp ciObject.hpp +ciMethod.hpp ciSignature.hpp +ciMethod.hpp methodLiveness.hpp + +ciMethodBlocks.cpp bytecode.hpp +ciMethodBlocks.cpp ciMethodBlocks.hpp +ciMethodBlocks.cpp ciStreams.hpp +ciMethodBlocks.cpp copy.hpp + +ciMethodBlocks.hpp ciMethod.hpp +ciMethodBlocks.hpp growableArray.hpp +ciMethodBlocks.hpp resourceArea.hpp + +ciMethodData.cpp allocation.inline.hpp +ciMethodData.cpp ciMethodData.hpp +ciMethodData.cpp ciUtilities.hpp +ciMethodData.cpp copy.hpp +ciMethodData.cpp deoptimization.hpp +ciMethodData.cpp resourceArea.hpp + +ciMethodData.hpp ciClassList.hpp +ciMethodData.hpp ciKlass.hpp +ciMethodData.hpp ciObject.hpp +ciMethodData.hpp ciUtilities.hpp +ciMethodData.hpp methodDataOop.hpp +ciMethodData.hpp oop.inline.hpp + +ciMethodKlass.cpp ciMethodKlass.hpp +ciMethodKlass.cpp ciUtilities.hpp + +ciMethodKlass.hpp ciKlass.hpp +ciMethodKlass.hpp ciSymbol.hpp + +ciNullObject.cpp ciNullObject.hpp + +ciNullObject.hpp ciClassList.hpp +ciNullObject.hpp ciObject.hpp +ciNullObject.hpp ciUtilities.hpp + +ciObjArray.hpp ciArray.hpp +ciObjArray.hpp ciClassList.hpp +ciObjArray.hpp objArrayOop.hpp + +ciObjArrayKlass.cpp ciInstanceKlass.hpp +ciObjArrayKlass.cpp ciObjArrayKlass.hpp +ciObjArrayKlass.cpp ciObjArrayKlassKlass.hpp +ciObjArrayKlass.cpp ciSymbol.hpp +ciObjArrayKlass.cpp ciUtilities.hpp +ciObjArrayKlass.cpp objArrayKlass.hpp + +ciObjArrayKlass.hpp ciArrayKlass.hpp + +ciObjArrayKlassKlass.cpp ciObjArrayKlassKlass.hpp +ciObjArrayKlassKlass.cpp ciUtilities.hpp + +ciObjArrayKlassKlass.hpp ciArrayKlassKlass.hpp + +ciObject.cpp ciObject.hpp +ciObject.cpp ciUtilities.hpp +ciObject.cpp collectedHeap.inline.hpp +ciObject.cpp oop.inline2.hpp + +ciObject.hpp allocation.hpp +ciObject.hpp ciClassList.hpp +ciObject.hpp handles.hpp +ciObject.hpp jniHandles.hpp + +ciObjectFactory.cpp allocation.inline.hpp +ciObjectFactory.cpp ciInstance.hpp +ciObjectFactory.cpp ciInstanceKlass.hpp +ciObjectFactory.cpp ciInstanceKlassKlass.hpp +ciObjectFactory.cpp ciMethod.hpp +ciObjectFactory.cpp ciMethodData.hpp +ciObjectFactory.cpp ciMethodKlass.hpp +ciObjectFactory.cpp ciNullObject.hpp +ciObjectFactory.cpp ciObjArray.hpp +ciObjectFactory.cpp ciObjArrayKlass.hpp +ciObjectFactory.cpp ciObjArrayKlassKlass.hpp +ciObjectFactory.cpp ciObjectFactory.hpp +ciObjectFactory.cpp ciSymbol.hpp +ciObjectFactory.cpp ciSymbolKlass.hpp +ciObjectFactory.cpp ciTypeArray.hpp +ciObjectFactory.cpp ciTypeArrayKlass.hpp +ciObjectFactory.cpp ciTypeArrayKlassKlass.hpp +ciObjectFactory.cpp ciUtilities.hpp +ciObjectFactory.cpp collectedHeap.inline.hpp +ciObjectFactory.cpp fieldType.hpp +ciObjectFactory.cpp oop.inline.hpp +ciObjectFactory.cpp oop.inline2.hpp +ciObjectFactory.cpp systemDictionary.hpp + +ciObjectFactory.hpp ciClassList.hpp +ciObjectFactory.hpp ciObject.hpp +ciObjectFactory.hpp growableArray.hpp + +ciSignature.cpp allocation.inline.hpp +ciSignature.cpp ciSignature.hpp +ciSignature.cpp ciUtilities.hpp +ciSignature.cpp oop.hpp +ciSignature.cpp oop.inline.hpp +ciSignature.cpp signature.hpp + +ciSignature.hpp ciClassList.hpp +ciSignature.hpp ciSymbol.hpp +ciSignature.hpp globalDefinitions.hpp +ciSignature.hpp growableArray.hpp + +ciStreams.cpp ciConstant.hpp +ciStreams.cpp ciField.hpp +ciStreams.cpp ciStreams.hpp +ciStreams.cpp ciUtilities.hpp + +ciStreams.hpp ciClassList.hpp +ciStreams.hpp ciExceptionHandler.hpp +ciStreams.hpp ciInstanceKlass.hpp +ciStreams.hpp ciMethod.hpp + +ciSymbol.cpp ciSymbol.hpp +ciSymbol.cpp ciUtilities.hpp +ciSymbol.cpp oopFactory.hpp + +ciSymbol.hpp ciObject.hpp +ciSymbol.hpp ciObjectFactory.hpp +ciSymbol.hpp symbolOop.hpp +ciSymbol.hpp vmSymbols.hpp + +ciSymbolKlass.cpp ciSymbolKlass.hpp +ciSymbolKlass.cpp ciUtilities.hpp + +ciSymbolKlass.hpp ciKlass.hpp +ciSymbolKlass.hpp ciSymbol.hpp + +ciType.cpp ciType.hpp +ciType.cpp ciUtilities.hpp +ciType.cpp oop.inline.hpp +ciType.cpp systemDictionary.hpp + +ciType.hpp ciObject.hpp +ciType.hpp klassOop.hpp + +ciTypeArray.cpp ciTypeArray.hpp +ciTypeArray.cpp ciUtilities.hpp + +ciTypeArray.hpp ciArray.hpp +ciTypeArray.hpp ciClassList.hpp +ciTypeArray.hpp typeArrayOop.hpp + +ciTypeArrayKlass.cpp ciTypeArrayKlass.hpp +ciTypeArrayKlass.cpp ciUtilities.hpp + +ciTypeArrayKlass.hpp ciArrayKlass.hpp + +ciTypeArrayKlassKlass.cpp ciTypeArrayKlassKlass.hpp +ciTypeArrayKlassKlass.cpp ciUtilities.hpp + +ciTypeArrayKlassKlass.hpp ciArrayKlassKlass.hpp + +ciUtilities.cpp ciUtilities.hpp + +ciUtilities.hpp ciEnv.hpp +ciUtilities.hpp interfaceSupport.hpp + +classFileError.cpp classFileParser.hpp +classFileError.cpp stackMapTable.hpp +classFileError.cpp verifier.hpp + +classFileParser.cpp allocation.hpp +classFileParser.cpp classFileParser.hpp +classFileParser.cpp classLoader.hpp +classFileParser.cpp classLoadingService.hpp +classFileParser.cpp constantPoolOop.hpp +classFileParser.cpp gcLocker.hpp +classFileParser.cpp instanceKlass.hpp +classFileParser.cpp javaCalls.hpp +classFileParser.cpp javaClasses.hpp +classFileParser.cpp jvmtiExport.hpp +classFileParser.cpp klass.inline.hpp +classFileParser.cpp klassOop.hpp +classFileParser.cpp klassVtable.hpp +classFileParser.cpp methodOop.hpp +classFileParser.cpp oopFactory.hpp +classFileParser.cpp perfData.hpp +classFileParser.cpp reflection.hpp +classFileParser.cpp signature.hpp +classFileParser.cpp symbolOop.hpp +classFileParser.cpp symbolTable.hpp +classFileParser.cpp systemDictionary.hpp +classFileParser.cpp timer.hpp +classFileParser.cpp universe.inline.hpp +classFileParser.cpp verificationType.hpp +classFileParser.cpp verifier.hpp +classFileParser.cpp vmSymbols.hpp + +classFileParser.hpp accessFlags.hpp +classFileParser.hpp classFileStream.hpp +classFileParser.hpp handles.inline.hpp +classFileParser.hpp oop.inline.hpp +classFileParser.hpp resourceArea.hpp +classFileParser.hpp typeArrayOop.hpp + +classFileStream.cpp classFileStream.hpp +classFileStream.cpp vmSymbols.hpp + +classFileStream.hpp bytes_.hpp +classFileStream.hpp top.hpp + +classLoader.cpp allocation.inline.hpp +classLoader.cpp arguments.hpp +classLoader.cpp classFileParser.hpp +classLoader.cpp classFileStream.hpp +classLoader.cpp classLoader.hpp +classLoader.cpp collectedHeap.inline.hpp +classLoader.cpp compilationPolicy.hpp +classLoader.cpp compileBroker.hpp +classLoader.cpp constantPoolKlass.hpp +classLoader.cpp events.hpp +classLoader.cpp fprofiler.hpp +classLoader.cpp generation.hpp +classLoader.cpp handles.hpp +classLoader.cpp handles.inline.hpp +classLoader.cpp hashtable.hpp +classLoader.cpp hashtable.inline.hpp +classLoader.cpp hpi.hpp +classLoader.cpp hpi_.hpp +classLoader.cpp init.hpp +classLoader.cpp instanceKlass.hpp +classLoader.cpp instanceRefKlass.hpp +classLoader.cpp interfaceSupport.hpp +classLoader.cpp java.hpp +classLoader.cpp javaCalls.hpp +classLoader.cpp javaClasses.hpp +classLoader.cpp jvm_misc.hpp +classLoader.cpp management.hpp +classLoader.cpp oop.inline.hpp +classLoader.cpp oopFactory.hpp +classLoader.cpp os_.inline.hpp +classLoader.cpp symbolOop.hpp +classLoader.cpp systemDictionary.hpp +classLoader.cpp threadCritical.hpp +classLoader.cpp timer.hpp +classLoader.cpp universe.inline.hpp +classLoader.cpp vmSymbols.hpp +classLoader.cpp vtune.hpp + +classLoader.hpp classFileParser.hpp +classLoader.hpp perfData.hpp + +classLoadingService.cpp allocation.hpp +classLoadingService.cpp classLoadingService.hpp +classLoadingService.cpp dtrace.hpp +classLoadingService.cpp memoryService.hpp +classLoadingService.cpp mutexLocker.hpp +classLoadingService.cpp oop.inline.hpp +classLoadingService.cpp systemDictionary.hpp +classLoadingService.cpp universe.hpp + +classLoadingService.hpp growableArray.hpp +classLoadingService.hpp handles.hpp +classLoadingService.hpp perfData.hpp + +classify.cpp classify.hpp +classify.cpp systemDictionary.hpp + +classify.hpp oop.hpp +classify.hpp oop.inline.hpp + +codeBlob.cpp allocation.inline.hpp +codeBlob.cpp bytecode.hpp +codeBlob.cpp codeBlob.hpp +codeBlob.cpp codeCache.hpp +codeBlob.cpp disassembler_.hpp +codeBlob.cpp forte.hpp +codeBlob.cpp handles.inline.hpp +codeBlob.cpp heap.hpp +codeBlob.cpp interfaceSupport.hpp +codeBlob.cpp memoryService.hpp +codeBlob.cpp mutexLocker.hpp +codeBlob.cpp nativeInst_.hpp +codeBlob.cpp oop.inline.hpp +codeBlob.cpp relocInfo.hpp +codeBlob.cpp safepoint.hpp +codeBlob.cpp sharedRuntime.hpp +codeBlob.cpp vframe.hpp +codeBlob.cpp vtune.hpp + +codeBlob.hpp codeBuffer.hpp +codeBlob.hpp frame.hpp +codeBlob.hpp handles.hpp +codeBlob.hpp oopMap.hpp + +codeBuffer.cpp codeBuffer.hpp +codeBuffer.cpp copy.hpp +codeBuffer.cpp disassembler_.hpp + +codeBuffer.hpp assembler.hpp +codeBuffer.hpp oopRecorder.hpp +codeBuffer.hpp relocInfo.hpp + +codeBuffer_.hpp generate_platform_dependent_include + +codeCache.cpp allocation.inline.hpp +codeCache.cpp codeBlob.hpp +codeCache.cpp codeCache.hpp +codeCache.cpp dependencies.hpp +codeCache.cpp gcLocker.hpp +codeCache.cpp icache.hpp +codeCache.cpp iterator.hpp +codeCache.cpp java.hpp +codeCache.cpp markSweep.hpp +codeCache.cpp memoryService.hpp +codeCache.cpp methodOop.hpp +codeCache.cpp mutexLocker.hpp +codeCache.cpp nmethod.hpp +codeCache.cpp objArrayOop.hpp +codeCache.cpp pcDesc.hpp +codeCache.cpp resourceArea.hpp + +codeCache.hpp allocation.hpp +codeCache.hpp codeBlob.hpp +codeCache.hpp heap.hpp +codeCache.hpp instanceKlass.hpp +codeCache.hpp oopsHierarchy.hpp + +collectorPolicy.cpp adaptiveSizePolicy.hpp +collectorPolicy.cpp arguments.hpp +collectorPolicy.cpp cardTableRS.hpp +collectorPolicy.cpp collectorPolicy.hpp +collectorPolicy.cpp gcLocker.inline.hpp +collectorPolicy.cpp genCollectedHeap.hpp +collectorPolicy.cpp gcPolicyCounters.hpp +collectorPolicy.cpp generationSpec.hpp +collectorPolicy.cpp globals_extension.hpp +collectorPolicy.cpp handles.inline.hpp +collectorPolicy.cpp java.hpp +collectorPolicy.cpp space.hpp +collectorPolicy.cpp thread_.inline.hpp +collectorPolicy.cpp universe.hpp +collectorPolicy.cpp vmGCOperations.hpp +collectorPolicy.cpp vmThread.hpp + +collectorPolicy.hpp barrierSet.hpp +collectorPolicy.hpp genRemSet.hpp +collectorPolicy.hpp permGen.hpp + +compactPermGen.hpp generation.hpp +compactPermGen.hpp permGen.hpp + +compactingPermGenGen.cpp compactingPermGenGen.hpp +compactingPermGenGen.cpp filemap.hpp +compactingPermGenGen.cpp genOopClosures.inline.hpp +compactingPermGenGen.cpp generation.inline.hpp +compactingPermGenGen.cpp generationSpec.hpp +compactingPermGenGen.cpp java.hpp +compactingPermGenGen.cpp oop.inline.hpp +compactingPermGenGen.cpp symbolTable.hpp +compactingPermGenGen.cpp systemDictionary.hpp + +compactingPermGenGen.hpp generationCounters.hpp +compactingPermGenGen.hpp space.hpp + +compilationPolicy.cpp compilationPolicy.hpp +compilationPolicy.cpp compiledIC.hpp +compilationPolicy.cpp compilerOracle.hpp +compilationPolicy.cpp events.hpp +compilationPolicy.cpp frame.hpp +compilationPolicy.cpp globalDefinitions.hpp +compilationPolicy.cpp handles.inline.hpp +compilationPolicy.cpp interpreter.hpp +compilationPolicy.cpp methodDataOop.hpp +compilationPolicy.cpp methodOop.hpp +compilationPolicy.cpp nativeLookup.hpp +compilationPolicy.cpp nmethod.hpp +compilationPolicy.cpp oop.inline.hpp +compilationPolicy.cpp rframe.hpp +compilationPolicy.cpp stubRoutines.hpp +compilationPolicy.cpp thread.hpp +compilationPolicy.cpp timer.hpp +compilationPolicy.cpp vframe.hpp +compilationPolicy.cpp vm_operations.hpp + +compilationPolicy.hpp allocation.hpp +compilationPolicy.hpp compileBroker.hpp +compilationPolicy.hpp growableArray.hpp +compilationPolicy.hpp nmethod.hpp +compilationPolicy.hpp vm_operations.hpp + +compileBroker.cpp allocation.inline.hpp +compileBroker.cpp arguments.hpp +compileBroker.cpp codeCache.hpp +compileBroker.cpp compilationPolicy.hpp +compileBroker.cpp compileBroker.hpp +compileBroker.cpp compileLog.hpp +compileBroker.cpp compilerOracle.hpp +compileBroker.cpp dtrace.hpp +compileBroker.cpp init.hpp +compileBroker.cpp interfaceSupport.hpp +compileBroker.cpp javaCalls.hpp +compileBroker.cpp linkResolver.hpp +compileBroker.cpp methodDataOop.hpp +compileBroker.cpp methodOop.hpp +compileBroker.cpp nativeLookup.hpp +compileBroker.cpp oop.inline.hpp +compileBroker.cpp os.hpp +compileBroker.cpp sharedRuntime.hpp +compileBroker.cpp systemDictionary.hpp +compileBroker.cpp vmSymbols.hpp + +compileBroker.hpp abstractCompiler.hpp +compileBroker.hpp compilerInterface.hpp +compileBroker.hpp perfData.hpp + +compileLog.cpp allocation.inline.hpp +compileLog.cpp ciMethod.hpp +compileLog.cpp compileLog.hpp +compileLog.cpp methodOop.hpp +compileLog.cpp mutexLocker.hpp +compileLog.cpp os.hpp + +compileLog.hpp xmlstream.hpp + +compiledIC.cpp codeCache.hpp +compiledIC.cpp compiledIC.hpp +compiledIC.cpp events.hpp +compiledIC.cpp icBuffer.hpp +compiledIC.cpp icache.hpp +compiledIC.cpp interpreter.hpp +compiledIC.cpp linkResolver.hpp +compiledIC.cpp methodOop.hpp +compiledIC.cpp nmethod.hpp +compiledIC.cpp oop.inline.hpp +compiledIC.cpp oopFactory.hpp +compiledIC.cpp sharedRuntime.hpp +compiledIC.cpp stubRoutines.hpp +compiledIC.cpp symbolOop.hpp +compiledIC.cpp systemDictionary.hpp +compiledIC.cpp vtableStubs.hpp + +compiledIC.hpp compiledICHolderKlass.hpp +compiledIC.hpp compiledICHolderOop.hpp +compiledIC.hpp klassOop.hpp +compiledIC.hpp linkResolver.hpp +compiledIC.hpp nativeInst_.hpp + +compiledICHolderKlass.cpp collectedHeap.hpp +compiledICHolderKlass.cpp collectedHeap.inline.hpp +compiledICHolderKlass.cpp compiledICHolderKlass.hpp +compiledICHolderKlass.cpp handles.inline.hpp +compiledICHolderKlass.cpp javaClasses.hpp +compiledICHolderKlass.cpp markSweep.hpp +compiledICHolderKlass.cpp oop.inline.hpp +compiledICHolderKlass.cpp oop.inline2.hpp +compiledICHolderKlass.cpp permGen.hpp +compiledICHolderKlass.cpp universe.inline.hpp + +compiledICHolderKlass.hpp compiledICHolderOop.hpp +compiledICHolderKlass.hpp klass.hpp +compiledICHolderKlass.hpp methodOop.hpp + +compiledICHolderOop.cpp compiledICHolderOop.hpp + +compiledICHolderOop.hpp oop.hpp + +compilerInterface.hpp ciArray.hpp +compilerInterface.hpp ciArrayKlass.hpp +compilerInterface.hpp ciArrayKlassKlass.hpp +compilerInterface.hpp ciCallProfile.hpp +compilerInterface.hpp ciConstant.hpp +compilerInterface.hpp ciEnv.hpp +compilerInterface.hpp ciExceptionHandler.hpp +compilerInterface.hpp ciField.hpp +compilerInterface.hpp ciFlags.hpp +compilerInterface.hpp ciInstance.hpp +compilerInterface.hpp ciInstanceKlass.hpp +compilerInterface.hpp ciInstanceKlassKlass.hpp +compilerInterface.hpp ciKlass.hpp +compilerInterface.hpp ciKlassKlass.hpp +compilerInterface.hpp ciMethod.hpp +compilerInterface.hpp ciMethodKlass.hpp +compilerInterface.hpp ciNullObject.hpp +compilerInterface.hpp ciObjArray.hpp +compilerInterface.hpp ciObjArrayKlass.hpp +compilerInterface.hpp ciObjArrayKlassKlass.hpp +compilerInterface.hpp ciObject.hpp +compilerInterface.hpp ciSignature.hpp +compilerInterface.hpp ciStreams.hpp +compilerInterface.hpp ciSymbol.hpp +compilerInterface.hpp ciSymbolKlass.hpp +compilerInterface.hpp ciTypeArray.hpp +compilerInterface.hpp ciTypeArrayKlass.hpp +compilerInterface.hpp ciTypeArrayKlassKlass.hpp + +compilerOracle.cpp allocation.inline.hpp +compilerOracle.cpp compilerOracle.hpp +compilerOracle.cpp handles.inline.hpp +compilerOracle.cpp jniHandles.hpp +compilerOracle.cpp klass.hpp +compilerOracle.cpp methodOop.hpp +compilerOracle.cpp oop.hpp +compilerOracle.cpp oop.inline.hpp +compilerOracle.cpp oopFactory.hpp +compilerOracle.cpp resourceArea.hpp +compilerOracle.cpp symbolOop.hpp + +compilerOracle.hpp allocation.hpp +compilerOracle.hpp oopsHierarchy.hpp + +compressedStream.cpp compressedStream.hpp +compressedStream.cpp ostream.hpp + +compressedStream.hpp allocation.hpp + +constMethodKlass.cpp constMethodKlass.hpp +constMethodKlass.cpp constMethodOop.hpp +constMethodKlass.cpp gcLocker.hpp +constMethodKlass.cpp handles.inline.hpp +constMethodKlass.cpp interpreter.hpp +constMethodKlass.cpp oop.inline.hpp +constMethodKlass.cpp oop.inline2.hpp +constMethodKlass.cpp resourceArea.hpp + +constMethodKlass.hpp oop.hpp +constMethodKlass.hpp klass.hpp +constMethodKlass.hpp orderAccess.hpp + +constMethodOop.cpp constMethodOop.hpp +constMethodOop.cpp methodOop.hpp + +constMethodOop.hpp oop.hpp +constMethodOop.hpp typeArrayOop.hpp + +constantPoolKlass.cpp collectedHeap.inline.hpp +constantPoolKlass.cpp constantPoolKlass.hpp +constantPoolKlass.cpp constantPoolOop.hpp +constantPoolKlass.cpp handles.inline.hpp +constantPoolKlass.cpp oop.inline.hpp +constantPoolKlass.cpp oop.inline2.hpp +constantPoolKlass.cpp oopFactory.hpp +constantPoolKlass.cpp permGen.hpp +constantPoolKlass.cpp symbolOop.hpp +constantPoolKlass.cpp thread_.inline.hpp +constantPoolKlass.cpp universe.inline.hpp + +constantPoolKlass.hpp arrayKlass.hpp +constantPoolKlass.hpp instanceKlass.hpp + +constantPoolOop.cpp constantPoolOop.hpp +constantPoolOop.cpp fieldType.hpp +constantPoolOop.cpp init.hpp +constantPoolOop.cpp instanceKlass.hpp +constantPoolOop.cpp javaClasses.hpp +constantPoolOop.cpp linkResolver.hpp +constantPoolOop.cpp objArrayKlass.hpp +constantPoolOop.cpp oop.inline.hpp +constantPoolOop.cpp signature.hpp +constantPoolOop.cpp symbolTable.hpp +constantPoolOop.cpp systemDictionary.hpp +constantPoolOop.cpp universe.inline.hpp +constantPoolOop.cpp vframe.hpp +constantPoolOop.cpp vmSymbols.hpp + +constantPoolOop.hpp arrayOop.hpp +constantPoolOop.hpp bytes_.hpp +constantPoolOop.hpp constantTag.hpp +constantPoolOop.hpp cpCacheOop.hpp +constantPoolOop.hpp typeArrayOop.hpp + +constantTag.cpp constantTag.hpp + +constantTag.hpp jvm.h +constantTag.hpp top.hpp + +copy.cpp copy.hpp +copy.cpp sharedRuntime.hpp + +copy.hpp stubRoutines.hpp + +copy_.hpp generate_platform_dependent_include + +copy_.inline.hpp generate_platform_dependent_include + +cpCacheKlass.cpp bytecodes.hpp +cpCacheKlass.cpp collectedHeap.hpp +cpCacheKlass.cpp constantPoolOop.hpp +cpCacheKlass.cpp cpCacheKlass.hpp +cpCacheKlass.cpp handles.inline.hpp +cpCacheKlass.cpp markSweep.hpp +cpCacheKlass.cpp oop.inline.hpp +cpCacheKlass.cpp permGen.hpp + +cpCacheKlass.hpp arrayKlass.hpp +cpCacheKlass.hpp cpCacheOop.hpp +cpCacheKlass.hpp instanceKlass.hpp + +cpCacheOop.cpp cpCacheOop.hpp +cpCacheOop.cpp handles.inline.hpp +cpCacheOop.cpp interpreter.hpp +cpCacheOop.cpp jvmtiRedefineClassesTrace.hpp +cpCacheOop.cpp markSweep.hpp +cpCacheOop.cpp markSweep.inline.hpp +cpCacheOop.cpp objArrayOop.hpp +cpCacheOop.cpp oop.inline.hpp +cpCacheOop.cpp universe.inline.hpp + +cpCacheOop.hpp allocation.hpp +cpCacheOop.hpp array.hpp +cpCacheOop.hpp arrayOop.hpp +cpCacheOop.hpp bytecodes.hpp + +cppInterpreter.cpp bytecodeInterpreter.hpp +cppInterpreter.cpp interpreter.hpp +cppInterpreter.cpp interpreterGenerator.hpp +cppInterpreter.cpp interpreterRuntime.hpp + +cppInterpreter.hpp abstractInterpreter.hpp + +cppInterpreter_.cpp arguments.hpp +cppInterpreter_.cpp arrayOop.hpp +cppInterpreter_.cpp assembler.hpp +cppInterpreter_.cpp bytecodeHistogram.hpp +cppInterpreter_.cpp debug.hpp +cppInterpreter_.cpp deoptimization.hpp +cppInterpreter_.cpp frame.inline.hpp +cppInterpreter_.cpp interpreterRuntime.hpp +cppInterpreter_.cpp interpreter.hpp +cppInterpreter_.cpp interpreterGenerator.hpp +cppInterpreter_.cpp jvmtiExport.hpp +cppInterpreter_.cpp jvmtiThreadState.hpp +cppInterpreter_.cpp methodDataOop.hpp +cppInterpreter_.cpp methodOop.hpp +cppInterpreter_.cpp oop.inline.hpp +cppInterpreter_.cpp sharedRuntime.hpp +cppInterpreter_.cpp stubRoutines.hpp +cppInterpreter_.cpp synchronizer.hpp +cppInterpreter_.cpp cppInterpreter.hpp +cppInterpreter_.cpp timer.hpp +cppInterpreter_.cpp vframeArray.hpp + +cppInterpreter_.hpp generate_platform_dependent_include + +cppInterpreterGenerator_.hpp generate_platform_dependent_include + +debug.cpp arguments.hpp +debug.cpp bytecodeHistogram.hpp +debug.cpp codeCache.hpp +debug.cpp collectedHeap.hpp +debug.cpp compileBroker.hpp +debug.cpp defaultStream.hpp +debug.cpp disassembler_.hpp +debug.cpp events.hpp +debug.cpp frame.hpp +debug.cpp heapDumper.hpp +debug.cpp icBuffer.hpp +debug.cpp interpreter.hpp +debug.cpp java.hpp +debug.cpp markSweep.hpp +debug.cpp nmethod.hpp +debug.cpp oop.inline.hpp +debug.cpp os_.inline.hpp +debug.cpp privilegedStack.hpp +debug.cpp resourceArea.hpp +debug.cpp sharedRuntime.hpp +debug.cpp stubCodeGenerator.hpp +debug.cpp stubRoutines.hpp +debug.cpp systemDictionary.hpp +debug.cpp thread_.inline.hpp +debug.cpp top.hpp +debug.cpp universe.hpp +debug.cpp vframe.hpp +debug.cpp vmError.hpp +debug.cpp vtableStubs.hpp + +debug.hpp globalDefinitions.hpp + +debugInfo.cpp debugInfo.hpp +debugInfo.cpp debugInfoRec.hpp +debugInfo.cpp handles.inline.hpp +debugInfo.cpp nmethod.hpp + +debugInfo.hpp compressedStream.hpp +debugInfo.hpp growableArray.hpp +debugInfo.hpp location.hpp +debugInfo.hpp nmethod.hpp +debugInfo.hpp oopRecorder.hpp +debugInfo.hpp stackValue.hpp + +debugInfoRec.cpp debugInfoRec.hpp +debugInfoRec.cpp jvmtiExport.hpp +debugInfoRec.cpp scopeDesc.hpp + +debugInfoRec.hpp ciClassList.hpp +debugInfoRec.hpp ciInstanceKlass.hpp +debugInfoRec.hpp ciMethod.hpp +debugInfoRec.hpp debugInfo.hpp +debugInfoRec.hpp growableArray.hpp +debugInfoRec.hpp location.hpp +debugInfoRec.hpp oop.hpp +debugInfoRec.hpp oopMap.hpp +debugInfoRec.hpp pcDesc.hpp + +debug_.cpp codeCache.hpp +debug_.cpp debug.hpp +debug_.cpp frame.hpp +debug_.cpp init.hpp +debug_.cpp nmethod.hpp +debug_.cpp os.hpp +debug_.cpp top.hpp + +defNewGeneration.cpp collectorCounters.hpp +defNewGeneration.cpp copy.hpp +defNewGeneration.cpp defNewGeneration.hpp +defNewGeneration.cpp defNewGeneration.inline.hpp +defNewGeneration.cpp gcLocker.inline.hpp +defNewGeneration.cpp gcPolicyCounters.hpp +defNewGeneration.cpp genCollectedHeap.hpp +defNewGeneration.cpp genOopClosures.inline.hpp +defNewGeneration.cpp generationSpec.hpp +defNewGeneration.cpp instanceRefKlass.hpp +defNewGeneration.cpp iterator.hpp +defNewGeneration.cpp java.hpp +defNewGeneration.cpp oop.inline.hpp +defNewGeneration.cpp referencePolicy.hpp +defNewGeneration.cpp space.hpp +defNewGeneration.cpp space.inline.hpp +defNewGeneration.cpp thread_.inline.hpp + +defNewGeneration.hpp ageTable.hpp +defNewGeneration.hpp cSpaceCounters.hpp +defNewGeneration.hpp generation.inline.hpp +defNewGeneration.hpp generationCounters.hpp + +defNewGeneration.inline.hpp defNewGeneration.hpp +defNewGeneration.inline.hpp space.hpp + +defaultStream.hpp xmlstream.hpp + +deoptimization.cpp allocation.inline.hpp +deoptimization.cpp biasedLocking.hpp +deoptimization.cpp bytecode.hpp +deoptimization.cpp debugInfoRec.hpp +deoptimization.cpp deoptimization.hpp +deoptimization.cpp events.hpp +deoptimization.cpp interfaceSupport.hpp +deoptimization.cpp interpreter.hpp +deoptimization.cpp jvmtiThreadState.hpp +deoptimization.cpp methodOop.hpp +deoptimization.cpp nmethod.hpp +deoptimization.cpp oop.inline.hpp +deoptimization.cpp oopFactory.hpp +deoptimization.cpp oopMapCache.hpp +deoptimization.cpp pcDesc.hpp +deoptimization.cpp resourceArea.hpp +deoptimization.cpp scopeDesc.hpp +deoptimization.cpp sharedRuntime.hpp +deoptimization.cpp signature.hpp +deoptimization.cpp stubRoutines.hpp +deoptimization.cpp systemDictionary.hpp +deoptimization.cpp thread.hpp +deoptimization.cpp vframe.hpp +deoptimization.cpp vframeArray.hpp +deoptimization.cpp vframe_hp.hpp +deoptimization.cpp xmlstream.hpp + +deoptimization.hpp allocation.hpp +deoptimization.hpp frame.inline.hpp + +depChecker_.cpp depChecker_.hpp +depChecker_.cpp disassembler_.hpp +depChecker_.cpp hpi.hpp + +dependencies.cpp ciArrayKlass.hpp +dependencies.cpp ciEnv.hpp +dependencies.cpp ciKlass.hpp +dependencies.cpp ciMethod.hpp +dependencies.cpp compileLog.hpp +dependencies.cpp copy.hpp +dependencies.cpp dependencies.hpp +dependencies.cpp handles.inline.hpp +dependencies.cpp oop.inline.hpp + +dependencies.hpp ciKlass.hpp +dependencies.hpp compressedStream.hpp +dependencies.hpp growableArray.hpp +dependencies.hpp nmethod.hpp + +dictionary.cpp classLoadingService.hpp +dictionary.cpp dictionary.hpp +dictionary.cpp hashtable.inline.hpp +dictionary.cpp jvmtiRedefineClassesTrace.hpp +dictionary.cpp oop.inline.hpp +dictionary.cpp systemDictionary.hpp + +dictionary.hpp hashtable.hpp +dictionary.hpp instanceKlass.hpp +dictionary.hpp oop.hpp +dictionary.hpp systemDictionary.hpp + +disassemblerEnv.hpp globals.hpp + +disassembler_.cpp cardTableModRefBS.hpp +disassembler_.cpp codeCache.hpp +disassembler_.cpp collectedHeap.hpp +disassembler_.cpp depChecker_.hpp +disassembler_.cpp disassembler_.hpp +disassembler_.cpp fprofiler.hpp +disassembler_.cpp handles.inline.hpp +disassembler_.cpp hpi.hpp +disassembler_.cpp stubCodeGenerator.hpp +disassembler_.cpp stubRoutines.hpp + +disassembler_.hpp disassemblerEnv.hpp +disassembler_.hpp os_.inline.hpp + +dtraceAttacher.cpp codeCache.hpp +dtraceAttacher.cpp deoptimization.hpp +dtraceAttacher.cpp dtraceAttacher.hpp +dtraceAttacher.cpp resourceArea.hpp +dtraceAttacher.cpp vmThread.hpp +dtraceAttacher.cpp vm_operations.hpp + +// dump is jck optional, put cpp deps in includeDB_features + +events.cpp allocation.inline.hpp +events.cpp events.hpp +events.cpp mutexLocker.hpp +events.cpp osThread.hpp +events.cpp threadLocalStorage.hpp +events.cpp thread_.inline.hpp +events.cpp timer.hpp + +events.hpp allocation.hpp +events.hpp top.hpp + +evmCompat.cpp debug.hpp + +exceptionHandlerTable.cpp allocation.inline.hpp +exceptionHandlerTable.cpp exceptionHandlerTable.hpp +exceptionHandlerTable.cpp nmethod.hpp + +exceptionHandlerTable.hpp allocation.hpp +exceptionHandlerTable.hpp methodOop.hpp + +exceptions.cpp compileBroker.hpp +exceptions.cpp events.hpp +exceptions.cpp exceptions.hpp +exceptions.cpp init.hpp +exceptions.cpp java.hpp +exceptions.cpp javaCalls.hpp +exceptions.cpp oop.inline.hpp +exceptions.cpp systemDictionary.hpp +exceptions.cpp threadCritical.hpp +exceptions.cpp thread_.inline.hpp +exceptions.cpp vmSymbols.hpp + +exceptions.hpp allocation.hpp +exceptions.hpp oopsHierarchy.hpp +exceptions.hpp sizes.hpp + +fieldDescriptor.cpp fieldDescriptor.hpp +fieldDescriptor.cpp handles.inline.hpp +fieldDescriptor.cpp instanceKlass.hpp +fieldDescriptor.cpp resourceArea.hpp +fieldDescriptor.cpp signature.hpp +fieldDescriptor.cpp systemDictionary.hpp +fieldDescriptor.cpp universe.inline.hpp +fieldDescriptor.cpp vmSymbols.hpp + +fieldDescriptor.hpp accessFlags.hpp +fieldDescriptor.hpp constantPoolOop.hpp +fieldDescriptor.hpp constantTag.hpp +fieldDescriptor.hpp fieldType.hpp +fieldDescriptor.hpp klassOop.hpp +fieldDescriptor.hpp oop.inline.hpp +fieldDescriptor.hpp symbolOop.hpp + +fieldType.cpp fieldType.hpp +fieldType.cpp oop.inline.hpp +fieldType.cpp oopFactory.hpp +fieldType.cpp signature.hpp +fieldType.cpp systemDictionary.hpp +fieldType.cpp typeArrayKlass.hpp + +fieldType.hpp allocation.hpp +fieldType.hpp symbolOop.hpp + +filemap.cpp arguments.hpp +filemap.cpp classLoader.hpp +filemap.cpp defaultStream.hpp +filemap.cpp filemap.hpp +filemap.cpp hpi_.hpp +filemap.cpp java.hpp +filemap.cpp os.hpp +filemap.cpp symbolTable.hpp + +filemap.hpp compactingPermGenGen.hpp +filemap.hpp space.hpp + +// forte is jck optional, put cpp deps in includeDB_features +// fprofiler is jck optional, put cpp deps in includeDB_features + +fprofiler.hpp thread_.inline.hpp +fprofiler.hpp timer.hpp + +frame.cpp collectedHeap.inline.hpp +frame.cpp frame.inline.hpp +frame.cpp handles.inline.hpp +frame.cpp interpreter.hpp +frame.cpp javaCalls.hpp +frame.cpp markOop.hpp +frame.cpp methodDataOop.hpp +frame.cpp methodOop.hpp +frame.cpp monitorChunk.hpp +frame.cpp nativeInst_.hpp +frame.cpp oop.hpp +frame.cpp oop.inline.hpp +frame.cpp oop.inline2.hpp +frame.cpp oopMapCache.hpp +frame.cpp resourceArea.hpp +frame.cpp sharedRuntime.hpp +frame.cpp signature.hpp +frame.cpp stubCodeGenerator.hpp +frame.cpp stubRoutines.hpp +frame.cpp universe.inline.hpp + +frame.hpp assembler.hpp +frame.hpp methodOop.hpp +frame.hpp monitorChunk.hpp +frame.hpp registerMap.hpp +frame.hpp synchronizer.hpp +frame.hpp top.hpp + +frame.inline.hpp bytecodeInterpreter.hpp +frame.inline.hpp bytecodeInterpreter.inline.hpp +frame.inline.hpp frame.hpp +frame.inline.hpp interpreter.hpp +frame.inline.hpp jniTypes_.hpp +frame.inline.hpp methodOop.hpp +frame.inline.hpp signature.hpp + +frame_.cpp frame.inline.hpp +frame_.cpp handles.inline.hpp +frame_.cpp interpreter.hpp +frame_.cpp javaCalls.hpp +frame_.cpp markOop.hpp +frame_.cpp methodOop.hpp +frame_.cpp monitorChunk.hpp +frame_.cpp oop.inline.hpp +frame_.cpp resourceArea.hpp +frame_.cpp signature.hpp +frame_.cpp stubCodeGenerator.hpp +frame_.cpp stubRoutines.hpp +frame_.cpp vmreg_.inline.hpp + +frame_.hpp generate_platform_dependent_include +frame_.hpp synchronizer.hpp +frame_.hpp top.hpp + +frame_.inline.hpp generate_platform_dependent_include + +gcLocker.cpp gcLocker.inline.hpp +gcLocker.cpp sharedHeap.hpp + +gcLocker.hpp collectedHeap.hpp +gcLocker.hpp genCollectedHeap.hpp +gcLocker.hpp oop.hpp +gcLocker.hpp os_.inline.hpp +gcLocker.hpp thread_.inline.hpp +gcLocker.hpp universe.hpp + +gcLocker.inline.hpp gcLocker.hpp + +genCollectedHeap.cpp aprofiler.hpp +genCollectedHeap.cpp biasedLocking.hpp +genCollectedHeap.cpp collectedHeap.inline.hpp +genCollectedHeap.cpp collectorCounters.hpp +genCollectedHeap.cpp compactPermGen.hpp +genCollectedHeap.cpp filemap.hpp +genCollectedHeap.cpp fprofiler.hpp +genCollectedHeap.cpp gcLocker.inline.hpp +genCollectedHeap.cpp genCollectedHeap.hpp +genCollectedHeap.cpp genOopClosures.inline.hpp +genCollectedHeap.cpp generation.inline.hpp +genCollectedHeap.cpp generationSpec.hpp +genCollectedHeap.cpp handles.hpp +genCollectedHeap.cpp handles.inline.hpp +genCollectedHeap.cpp icBuffer.hpp +genCollectedHeap.cpp java.hpp +genCollectedHeap.cpp memoryService.hpp +genCollectedHeap.cpp oop.inline.hpp +genCollectedHeap.cpp oop.inline2.hpp +genCollectedHeap.cpp permGen.hpp +genCollectedHeap.cpp resourceArea.hpp +genCollectedHeap.cpp sharedHeap.hpp +genCollectedHeap.cpp space.hpp +genCollectedHeap.cpp symbolTable.hpp +genCollectedHeap.cpp systemDictionary.hpp +genCollectedHeap.cpp vmGCOperations.hpp +genCollectedHeap.cpp vmSymbols.hpp +genCollectedHeap.cpp vmThread.hpp +genCollectedHeap.cpp workgroup.hpp + +genCollectedHeap.hpp adaptiveSizePolicy.hpp +genCollectedHeap.hpp collectorPolicy.hpp +genCollectedHeap.hpp generation.hpp +genCollectedHeap.hpp sharedHeap.hpp + +genMarkSweep.cpp codeCache.hpp +genMarkSweep.cpp collectedHeap.inline.hpp +genMarkSweep.cpp copy.hpp +genMarkSweep.cpp events.hpp +genMarkSweep.cpp fprofiler.hpp +genMarkSweep.cpp genCollectedHeap.hpp +genMarkSweep.cpp genMarkSweep.hpp +genMarkSweep.cpp genOopClosures.inline.hpp +genMarkSweep.cpp generation.inline.hpp +genMarkSweep.cpp handles.inline.hpp +genMarkSweep.cpp icBuffer.hpp +genMarkSweep.cpp instanceRefKlass.hpp +genMarkSweep.cpp javaClasses.hpp +genMarkSweep.cpp jvmtiExport.hpp +genMarkSweep.cpp modRefBarrierSet.hpp +genMarkSweep.cpp oop.inline.hpp +genMarkSweep.cpp referencePolicy.hpp +genMarkSweep.cpp space.hpp +genMarkSweep.cpp symbolTable.hpp +genMarkSweep.cpp synchronizer.hpp +genMarkSweep.cpp systemDictionary.hpp +genMarkSweep.cpp thread_.inline.hpp +genMarkSweep.cpp vmSymbols.hpp +genMarkSweep.cpp vmThread.hpp + +genMarkSweep.hpp markSweep.hpp + +genOopClosures.hpp iterator.hpp +genOopClosures.hpp oop.hpp + +genOopClosures.inline.hpp cardTableRS.hpp +genOopClosures.inline.hpp defNewGeneration.hpp +genOopClosures.inline.hpp genCollectedHeap.hpp +genOopClosures.inline.hpp genOopClosures.hpp +genOopClosures.inline.hpp genRemSet.hpp +genOopClosures.inline.hpp generation.hpp +genOopClosures.inline.hpp sharedHeap.hpp +genOopClosures.inline.hpp space.hpp + +genRemSet.cpp cardTableRS.hpp +genRemSet.cpp genRemSet.hpp + +genRemSet.hpp oop.hpp + +generateOopMap.cpp bitMap.hpp +generateOopMap.cpp bytecodeStream.hpp +generateOopMap.cpp generateOopMap.hpp +generateOopMap.cpp handles.inline.hpp +generateOopMap.cpp java.hpp +generateOopMap.cpp oop.inline.hpp +generateOopMap.cpp relocator.hpp +generateOopMap.cpp symbolOop.hpp + +generateOopMap.hpp allocation.inline.hpp +generateOopMap.hpp bytecodeStream.hpp +generateOopMap.hpp methodOop.hpp +generateOopMap.hpp oopsHierarchy.hpp +generateOopMap.hpp signature.hpp +generateOopMap.hpp universe.inline.hpp + +generation.cpp allocation.inline.hpp +generation.cpp blockOffsetTable.hpp +generation.cpp cardTableRS.hpp +generation.cpp collectedHeap.inline.hpp +generation.cpp copy.hpp +generation.cpp events.hpp +generation.cpp gcLocker.inline.hpp +generation.cpp genCollectedHeap.hpp +generation.cpp genMarkSweep.hpp +generation.cpp genOopClosures.hpp +generation.cpp genOopClosures.inline.hpp +generation.cpp generation.hpp +generation.cpp generation.inline.hpp +generation.cpp java.hpp +generation.cpp oop.hpp +generation.cpp oop.inline.hpp +generation.cpp space.inline.hpp + +generation.hpp allocation.hpp +generation.hpp collectorCounters.hpp +generation.hpp memRegion.hpp +generation.hpp mutex.hpp +generation.hpp perfData.hpp +generation.hpp referenceProcessor.hpp +generation.hpp universe.hpp +generation.hpp virtualspace.hpp +generation.hpp watermark.hpp + +generation.inline.hpp genCollectedHeap.hpp +generation.inline.hpp generation.hpp +generation.inline.hpp space.hpp + +generationSpec.cpp compactPermGen.hpp +generationSpec.cpp defNewGeneration.hpp +generationSpec.cpp filemap.hpp +generationSpec.cpp genRemSet.hpp +generationSpec.cpp generationSpec.hpp +generationSpec.cpp java.hpp +generationSpec.cpp tenuredGeneration.hpp + +generationSpec.hpp generation.hpp +generationSpec.hpp permGen.hpp + +globalDefinitions.cpp globalDefinitions.hpp +globalDefinitions.cpp os.hpp +globalDefinitions.cpp top.hpp + +globalDefinitions.hpp globalDefinitions_.hpp +globalDefinitions.hpp macros.hpp + +globalDefinitions_.hpp generate_platform_dependent_include + +globalDefinitions_.hpp jni.h + +globals.cpp allocation.inline.hpp +globals.cpp arguments.hpp +globals.cpp globals.hpp +globals.cpp globals_extension.hpp +globals.cpp oop.inline.hpp +globals.cpp ostream.hpp +globals.cpp top.hpp + +globals.hpp debug.hpp +globals.hpp globals_.hpp +globals.hpp globals_.hpp +globals.hpp globals_.hpp + +globals_extension.hpp globals.hpp +globals_extension.hpp top.hpp + +growableArray.cpp growableArray.hpp +growableArray.cpp resourceArea.hpp +growableArray.cpp thread_.inline.hpp + +growableArray.hpp allocation.hpp +growableArray.hpp allocation.inline.hpp +growableArray.hpp debug.hpp +growableArray.hpp globalDefinitions.hpp +growableArray.hpp top.hpp + +handles.cpp allocation.inline.hpp +handles.cpp handles.inline.hpp +handles.cpp oop.inline.hpp +handles.cpp os_.inline.hpp +handles.cpp thread_.inline.hpp + +handles.hpp klass.hpp +handles.hpp klassOop.hpp +handles.hpp top.hpp + +handles.inline.hpp handles.hpp +handles.inline.hpp thread_.inline.hpp + +hashtable.cpp allocation.inline.hpp +hashtable.cpp dtrace.hpp +hashtable.cpp hashtable.hpp +hashtable.cpp hashtable.inline.hpp +hashtable.cpp oop.inline.hpp +hashtable.cpp resourceArea.hpp +hashtable.cpp safepoint.hpp + +hashtable.hpp allocation.hpp +hashtable.hpp handles.hpp +hashtable.hpp oop.hpp +hashtable.hpp symbolOop.hpp + +hashtable.inline.hpp allocation.inline.hpp +hashtable.inline.hpp hashtable.hpp + +heap.cpp heap.hpp +heap.cpp oop.inline.hpp +heap.cpp os.hpp + +heap.hpp allocation.hpp +heap.hpp virtualspace.hpp + +// heapDumper is jck optional, put cpp deps in includeDB_features + +heapDumper.hpp allocation.hpp +heapDumper.hpp klassOop.hpp +heapDumper.hpp oop.hpp +heapDumper.hpp os.hpp + +// heapInspection is jck optional, put cpp deps in includeDB_features + +heapInspection.hpp allocation.inline.hpp +heapInspection.hpp oop.inline.hpp + +histogram.cpp histogram.hpp +histogram.cpp oop.inline.hpp + +histogram.hpp allocation.hpp +histogram.hpp growableArray.hpp +histogram.hpp os.hpp +histogram.hpp os_.inline.hpp + +hpi.cpp hpi.hpp +hpi.cpp jvm.h + +hpi.hpp globalDefinitions.hpp +hpi.hpp hpi_imported.h +hpi.hpp os.hpp +hpi.hpp top.hpp + +hpi_.cpp hpi.hpp +hpi_.cpp oop.inline.hpp +hpi_.cpp os.hpp + +hpi_imported.h jni.h + +icBuffer.cpp assembler_.inline.hpp +icBuffer.cpp collectedHeap.inline.hpp +icBuffer.cpp compiledIC.hpp +icBuffer.cpp icBuffer.hpp +icBuffer.cpp interpreter.hpp +icBuffer.cpp linkResolver.hpp +icBuffer.cpp methodOop.hpp +icBuffer.cpp mutexLocker.hpp +icBuffer.cpp nmethod.hpp +icBuffer.cpp oop.inline.hpp +icBuffer.cpp oop.inline2.hpp +icBuffer.cpp resourceArea.hpp +icBuffer.cpp scopeDesc.hpp +icBuffer.cpp stubRoutines.hpp +icBuffer.cpp universe.inline.hpp + +icBuffer.hpp allocation.hpp +icBuffer.hpp bytecodes.hpp +icBuffer.hpp stubs.hpp + +icBuffer_.cpp assembler.hpp +icBuffer_.cpp assembler_.inline.hpp +icBuffer_.cpp bytecodes.hpp +icBuffer_.cpp collectedHeap.inline.hpp +icBuffer_.cpp icBuffer.hpp +icBuffer_.cpp nativeInst_.hpp +icBuffer_.cpp oop.inline.hpp +icBuffer_.cpp oop.inline2.hpp +icBuffer_.cpp resourceArea.hpp + +icache.cpp icache.hpp +icache.cpp resourceArea.hpp + +icache.hpp allocation.hpp +icache.hpp stubCodeGenerator.hpp + +icache_.cpp assembler_.inline.hpp +icache_.cpp icache.hpp + +icache_.hpp generate_platform_dependent_include + +init.cpp bytecodes.hpp +init.cpp collectedHeap.hpp +init.cpp handles.inline.hpp +init.cpp icBuffer.hpp +init.cpp icache.hpp +init.cpp init.hpp +init.cpp safepoint.hpp +init.cpp sharedRuntime.hpp +init.cpp universe.hpp + +init.hpp top.hpp + +instanceKlass.cpp collectedHeap.inline.hpp +instanceKlass.cpp compileBroker.hpp +instanceKlass.cpp fieldDescriptor.hpp +instanceKlass.cpp genOopClosures.inline.hpp +instanceKlass.cpp handles.inline.hpp +instanceKlass.cpp instanceKlass.hpp +instanceKlass.cpp instanceOop.hpp +instanceKlass.cpp javaCalls.hpp +instanceKlass.cpp javaClasses.hpp +instanceKlass.cpp jvmti.h +instanceKlass.cpp jvmtiExport.hpp +instanceKlass.cpp jvmtiRedefineClassesTrace.hpp +instanceKlass.cpp methodOop.hpp +instanceKlass.cpp mutexLocker.hpp +instanceKlass.cpp objArrayKlassKlass.hpp +instanceKlass.cpp oop.inline.hpp +instanceKlass.cpp oopFactory.hpp +instanceKlass.cpp oopMapCache.hpp +instanceKlass.cpp permGen.hpp +instanceKlass.cpp rewriter.hpp +instanceKlass.cpp symbolOop.hpp +instanceKlass.cpp systemDictionary.hpp +instanceKlass.cpp threadService.hpp +instanceKlass.cpp thread_.inline.hpp +instanceKlass.cpp verifier.hpp +instanceKlass.cpp vmSymbols.hpp + +instanceKlass.hpp accessFlags.hpp +instanceKlass.hpp bitMap.hpp +instanceKlass.hpp constMethodOop.hpp +instanceKlass.hpp constantPoolOop.hpp +instanceKlass.hpp handles.hpp +instanceKlass.hpp instanceOop.hpp +instanceKlass.hpp klassOop.hpp +instanceKlass.hpp klassVtable.hpp +instanceKlass.hpp objArrayOop.hpp +instanceKlass.hpp os.hpp + +instanceKlassKlass.cpp collectedHeap.inline.hpp +instanceKlassKlass.cpp constantPoolOop.hpp +instanceKlassKlass.cpp fieldDescriptor.hpp +instanceKlassKlass.cpp gcLocker.hpp +instanceKlassKlass.cpp instanceKlass.hpp +instanceKlassKlass.cpp instanceKlassKlass.hpp +instanceKlassKlass.cpp instanceRefKlass.hpp +instanceKlassKlass.cpp javaClasses.hpp +instanceKlassKlass.cpp jvmtiExport.hpp +instanceKlassKlass.cpp objArrayKlassKlass.hpp +instanceKlassKlass.cpp objArrayOop.hpp +instanceKlassKlass.cpp oop.inline.hpp +instanceKlassKlass.cpp oop.inline2.hpp +instanceKlassKlass.cpp oopMapCache.hpp +instanceKlassKlass.cpp symbolOop.hpp +instanceKlassKlass.cpp systemDictionary.hpp +instanceKlassKlass.cpp typeArrayOop.hpp + +instanceKlassKlass.hpp klassKlass.hpp + +instanceOop.cpp instanceOop.hpp + +instanceOop.hpp oop.hpp + +instanceRefKlass.cpp collectedHeap.hpp +instanceRefKlass.cpp collectedHeap.inline.hpp +instanceRefKlass.cpp genCollectedHeap.hpp +instanceRefKlass.cpp genOopClosures.inline.hpp +instanceRefKlass.cpp instanceRefKlass.hpp +instanceRefKlass.cpp javaClasses.hpp +instanceRefKlass.cpp markSweep.hpp +instanceRefKlass.cpp oop.inline.hpp +instanceRefKlass.cpp preserveException.hpp +instanceRefKlass.cpp systemDictionary.hpp + +instanceRefKlass.hpp instanceKlass.hpp + +interfaceSupport.cpp collectedHeap.hpp +interfaceSupport.cpp collectedHeap.inline.hpp +interfaceSupport.cpp genCollectedHeap.hpp +interfaceSupport.cpp init.hpp +interfaceSupport.cpp interfaceSupport.hpp +interfaceSupport.cpp markSweep.hpp +interfaceSupport.cpp preserveException.hpp +interfaceSupport.cpp resourceArea.hpp +interfaceSupport.cpp threadLocalStorage.hpp +interfaceSupport.cpp vframe.hpp + +interfaceSupport.hpp gcLocker.hpp +interfaceSupport.hpp globalDefinitions.hpp +interfaceSupport.hpp handles.inline.hpp +interfaceSupport.hpp mutexLocker.hpp +interfaceSupport.hpp orderAccess.hpp +interfaceSupport.hpp os.hpp +interfaceSupport.hpp preserveException.hpp +interfaceSupport.hpp safepoint.hpp +interfaceSupport.hpp thread_.inline.hpp +interfaceSupport.hpp top.hpp +interfaceSupport.hpp vmThread.hpp + +interfaceSupport_.hpp generate_platform_dependent_include + +interp_masm_.cpp arrayOop.hpp +interp_masm_.cpp biasedLocking.hpp +interp_masm_.cpp interp_masm_.hpp +interp_masm_.cpp interpreterRuntime.hpp +interp_masm_.cpp interpreter.hpp +interp_masm_.cpp jvmtiExport.hpp +interp_masm_.cpp jvmtiThreadState.hpp +interp_masm_.cpp markOop.hpp +interp_masm_.cpp methodDataOop.hpp +interp_masm_.cpp methodOop.hpp +interp_masm_.cpp sharedRuntime.hpp +interp_masm_.cpp synchronizer.hpp +interp_masm_.cpp thread_.inline.hpp + +interp_masm_.hpp assembler_.inline.hpp +interp_masm_.hpp invocationCounter.hpp + +interpreter.cpp allocation.inline.hpp +interpreter.cpp arrayOop.hpp +interpreter.cpp assembler.hpp +interpreter.cpp bytecodeHistogram.hpp +interpreter.cpp bytecodeInterpreter.hpp +interpreter.cpp forte.hpp +interpreter.cpp handles.inline.hpp +interpreter.cpp interpreter.hpp +interpreter.cpp interpreterRuntime.hpp +interpreter.cpp interpreter.hpp +interpreter.cpp jvmtiExport.hpp +interpreter.cpp methodDataOop.hpp +interpreter.cpp methodOop.hpp +interpreter.cpp oop.inline.hpp +interpreter.cpp resourceArea.hpp +interpreter.cpp sharedRuntime.hpp +interpreter.cpp stubRoutines.hpp +interpreter.cpp templateTable.hpp +interpreter.cpp timer.hpp +interpreter.cpp vtune.hpp + +interpreter.hpp cppInterpreter.hpp +interpreter.hpp stubs.hpp +interpreter.hpp templateInterpreter.hpp + +interpreterRT_.cpp allocation.inline.hpp +interpreterRT_.cpp handles.inline.hpp +interpreterRT_.cpp icache.hpp +interpreterRT_.cpp interfaceSupport.hpp +interpreterRT_.cpp interpreterRuntime.hpp +interpreterRT_.cpp interpreter.hpp +interpreterRT_.cpp methodOop.hpp +interpreterRT_.cpp oop.inline.hpp +interpreterRT_.cpp signature.hpp +interpreterRT_.cpp universe.inline.hpp + +interpreterRT_.hpp allocation.hpp +interpreterRT_.hpp generate_platform_dependent_include + +interpreterRuntime.cpp biasedLocking.hpp +interpreterRuntime.cpp collectedHeap.hpp +interpreterRuntime.cpp compilationPolicy.hpp +interpreterRuntime.cpp constantPoolOop.hpp +interpreterRuntime.cpp cpCacheOop.hpp +interpreterRuntime.cpp deoptimization.hpp +interpreterRuntime.cpp events.hpp +interpreterRuntime.cpp fieldDescriptor.hpp +interpreterRuntime.cpp handles.inline.hpp +interpreterRuntime.cpp instanceKlass.hpp +interpreterRuntime.cpp interfaceSupport.hpp +interpreterRuntime.cpp interpreterRuntime.hpp +interpreterRuntime.cpp interpreter.hpp +interpreterRuntime.cpp java.hpp +interpreterRuntime.cpp jfieldIDWorkaround.hpp +interpreterRuntime.cpp jvmtiExport.hpp +interpreterRuntime.cpp linkResolver.hpp +interpreterRuntime.cpp methodDataOop.hpp +interpreterRuntime.cpp nativeLookup.hpp +interpreterRuntime.cpp objArrayKlass.hpp +interpreterRuntime.cpp oop.inline.hpp +interpreterRuntime.cpp oopFactory.hpp +interpreterRuntime.cpp osThread.hpp +interpreterRuntime.cpp sharedRuntime.hpp +interpreterRuntime.cpp stubRoutines.hpp +interpreterRuntime.cpp symbolOop.hpp +interpreterRuntime.cpp synchronizer.hpp +interpreterRuntime.cpp systemDictionary.hpp +interpreterRuntime.cpp templateTable.hpp +interpreterRuntime.cpp threadCritical.hpp +interpreterRuntime.cpp universe.inline.hpp +interpreterRuntime.cpp vmSymbols.hpp +interpreterRuntime.cpp vm_version_.hpp + +interpreterRuntime.hpp bytecode.hpp +interpreterRuntime.hpp frame.inline.hpp +interpreterRuntime.hpp linkResolver.hpp +interpreterRuntime.hpp methodOop.hpp +interpreterRuntime.hpp signature.hpp +interpreterRuntime.hpp thread_.inline.hpp +interpreterRuntime.hpp top.hpp +interpreterRuntime.hpp universe.hpp + +interpreter_.cpp arguments.hpp +interpreter_.cpp arrayOop.hpp +interpreter_.cpp assembler.hpp +interpreter_.cpp bytecodeHistogram.hpp +interpreter_.cpp debug.hpp +interpreter_.cpp deoptimization.hpp +interpreter_.cpp frame.inline.hpp +interpreter_.cpp interpreterRuntime.hpp +interpreter_.cpp interpreter.hpp +interpreter_.cpp interpreterGenerator.hpp +interpreter_.cpp jvmtiExport.hpp +interpreter_.cpp jvmtiThreadState.hpp +interpreter_.cpp methodDataOop.hpp +interpreter_.cpp methodOop.hpp +interpreter_.cpp oop.inline.hpp +interpreter_.cpp sharedRuntime.hpp +interpreter_.cpp stubRoutines.hpp +interpreter_.cpp synchronizer.hpp +interpreter_.cpp templateTable.hpp +interpreter_.cpp timer.hpp +interpreter_.cpp vframeArray.hpp + +interpreter_.hpp generate_platform_dependent_include + +interpreterGenerator.hpp cppInterpreter.hpp +interpreterGenerator.hpp cppInterpreterGenerator.hpp +interpreterGenerator.hpp templateInterpreter.hpp +interpreterGenerator.hpp templateInterpreterGenerator.hpp + +interpreterGenerator_.hpp generate_platform_dependent_include + +invocationCounter.cpp frame.hpp +invocationCounter.cpp handles.inline.hpp +invocationCounter.cpp invocationCounter.hpp + +invocationCounter.hpp allocation.hpp +invocationCounter.hpp exceptions.hpp +invocationCounter.hpp handles.hpp + +iterator.cpp iterator.hpp +iterator.cpp oop.inline.hpp + +iterator.hpp allocation.hpp +iterator.hpp memRegion.hpp +iterator.hpp prefetch.hpp +iterator.hpp top.hpp + +java.cpp aprofiler.hpp +java.cpp arguments.hpp +java.cpp biasedLocking.hpp +java.cpp bytecodeHistogram.hpp +java.cpp classLoader.hpp +java.cpp codeCache.hpp +java.cpp compilationPolicy.hpp +java.cpp compileBroker.hpp +java.cpp compilerOracle.hpp +java.cpp constantPoolOop.hpp +java.cpp dtrace.hpp +java.cpp fprofiler.hpp +java.cpp genCollectedHeap.hpp +java.cpp generateOopMap.hpp +java.cpp globalDefinitions.hpp +java.cpp histogram.hpp +java.cpp init.hpp +java.cpp instanceKlass.hpp +java.cpp instanceKlassKlass.hpp +java.cpp instanceOop.hpp +java.cpp interfaceSupport.hpp +java.cpp java.hpp +java.cpp jvmtiExport.hpp +java.cpp memprofiler.hpp +java.cpp methodOop.hpp +java.cpp objArrayOop.hpp +java.cpp oop.hpp +java.cpp oop.inline.hpp +java.cpp oopFactory.hpp +java.cpp sharedRuntime.hpp +java.cpp statSampler.hpp +java.cpp symbolOop.hpp +java.cpp symbolTable.hpp +java.cpp systemDictionary.hpp +java.cpp task.hpp +java.cpp thread_.inline.hpp +java.cpp timer.hpp +java.cpp universe.hpp +java.cpp vmError.hpp +java.cpp vm_operations.hpp +java.cpp vm_version_.hpp +java.cpp vtune.hpp + +java.hpp os.hpp + +javaAssertions.cpp allocation.inline.hpp +javaAssertions.cpp handles.inline.hpp +javaAssertions.cpp javaAssertions.hpp +javaAssertions.cpp javaClasses.hpp +javaAssertions.cpp oop.inline.hpp +javaAssertions.cpp oopFactory.hpp +javaAssertions.cpp systemDictionary.hpp +javaAssertions.cpp vmSymbols.hpp + +javaAssertions.hpp exceptions.hpp +javaAssertions.hpp objArrayOop.hpp +javaAssertions.hpp ostream.hpp +javaAssertions.hpp typeArrayOop.hpp + +javaCalls.cpp compilationPolicy.hpp +javaCalls.cpp compileBroker.hpp +javaCalls.cpp handles.inline.hpp +javaCalls.cpp interfaceSupport.hpp +javaCalls.cpp interpreter.hpp +javaCalls.cpp javaCalls.hpp +javaCalls.cpp linkResolver.hpp +javaCalls.cpp mutexLocker.hpp +javaCalls.cpp nmethod.hpp +javaCalls.cpp oop.inline.hpp +javaCalls.cpp signature.hpp +javaCalls.cpp stubRoutines.hpp +javaCalls.cpp systemDictionary.hpp +javaCalls.cpp thread_.inline.hpp +javaCalls.cpp universe.inline.hpp +javaCalls.cpp vmSymbols.hpp +javaCalls.hpp allocation.hpp + +javaCalls.hpp handles.hpp +javaCalls.hpp javaFrameAnchor.hpp +javaCalls.hpp jniTypes_.hpp +javaCalls.hpp methodOop.hpp +javaCalls.hpp thread_.inline.hpp +javaCalls.hpp vmThread.hpp + +javaClasses.cpp debugInfo.hpp +javaClasses.cpp fieldDescriptor.hpp +javaClasses.cpp handles.inline.hpp +javaClasses.cpp instanceKlass.hpp +javaClasses.cpp interfaceSupport.hpp +javaClasses.cpp interpreter.hpp +javaClasses.cpp java.hpp +javaClasses.cpp javaCalls.hpp +javaClasses.cpp javaClasses.hpp +javaClasses.cpp klass.hpp +javaClasses.cpp klassOop.hpp +javaClasses.cpp methodOop.hpp +javaClasses.cpp oopFactory.hpp +javaClasses.cpp pcDesc.hpp +javaClasses.cpp preserveException.hpp +javaClasses.cpp resourceArea.hpp +javaClasses.cpp safepoint.hpp +javaClasses.cpp symbolOop.hpp +javaClasses.cpp symbolTable.hpp +javaClasses.cpp thread_.inline.hpp +javaClasses.cpp typeArrayOop.hpp +javaClasses.cpp universe.inline.hpp +javaClasses.cpp vframe.hpp +javaClasses.cpp vmSymbols.hpp + +javaClasses.hpp jvmti.h +javaClasses.hpp oop.hpp +javaClasses.hpp os.hpp +javaClasses.hpp systemDictionary.hpp +javaClasses.hpp utf8.hpp + +javaFrameAnchor.hpp globalDefinitions.hpp +javaFrameAnchor.hpp orderAccess_.inline.hpp + +javaFrameAnchor_.hpp generate_platform_dependent_include + +jni.cpp allocation.inline.hpp +jni.cpp classLoader.hpp +jni.cpp compilationPolicy.hpp +jni.cpp defaultStream.hpp +jni.cpp dtrace.hpp +jni.cpp events.hpp +jni.cpp fieldDescriptor.hpp +jni.cpp fprofiler.hpp +jni.cpp gcLocker.inline.hpp +jni.cpp handles.inline.hpp +jni.cpp histogram.hpp +jni.cpp instanceKlass.hpp +jni.cpp instanceOop.hpp +jni.cpp interfaceSupport.hpp +jni.cpp java.hpp +jni.cpp javaCalls.hpp +jni.cpp javaClasses.hpp +jni.cpp jfieldIDWorkaround.hpp +jni.cpp jni.h +jni.cpp jniCheck.hpp +jni.cpp jniFastGetField.hpp +jni.cpp jniTypes_.hpp +jni.cpp jvm.h +jni.cpp jvm_misc.hpp +jni.cpp jvmtiExport.hpp +jni.cpp jvmtiThreadState.hpp +jni.cpp linkResolver.hpp +jni.cpp markOop.hpp +jni.cpp methodOop.hpp +jni.cpp objArrayKlass.hpp +jni.cpp objArrayOop.hpp +jni.cpp oop.inline.hpp +jni.cpp oopFactory.hpp +jni.cpp os_.inline.hpp +jni.cpp reflection.hpp +jni.cpp runtimeService.hpp +jni.cpp sharedRuntime.hpp +jni.cpp signature.hpp +jni.cpp symbolOop.hpp +jni.cpp symbolTable.hpp +jni.cpp systemDictionary.hpp +jni.cpp thread_.inline.hpp +jni.cpp typeArrayKlass.hpp +jni.cpp typeArrayOop.hpp +jni.cpp universe.inline.hpp +jni.cpp vmSymbols.hpp +jni.cpp vm_operations.hpp + +// jniCheck is jck optional, put cpp deps in includeDB_features + +jniFastGetField.cpp jniFastGetField.hpp + +jniFastGetField.hpp allocation.hpp +jniFastGetField.hpp jvm_misc.hpp + +jniFastGetField_.cpp assembler_.inline.hpp +jniFastGetField_.cpp jniFastGetField.hpp +jniFastGetField_.cpp jvm_misc.hpp +jniFastGetField_.cpp resourceArea.hpp +jniFastGetField_.cpp safepoint.hpp + +jniHandles.cpp jniHandles.hpp +jniHandles.cpp mutexLocker.hpp +jniHandles.cpp oop.inline.hpp +jniHandles.cpp systemDictionary.hpp +jniHandles.cpp thread_.inline.hpp + +jniHandles.hpp handles.hpp +jniHandles.hpp top.hpp + +jniPeriodicChecker.cpp allocation.inline.hpp +jniPeriodicChecker.cpp jniPeriodicChecker.hpp +jniPeriodicChecker.cpp task.hpp + +jniTypes_.hpp allocation.hpp +jniTypes_.hpp jni.h +jniTypes_.hpp oop.hpp + +jni_.h generate_platform_dependent_include + +jvm.cpp arguments.hpp +jvm.cpp attachListener.hpp +jvm.cpp classLoader.hpp +jvm.cpp collectedHeap.inline.hpp +jvm.cpp copy.hpp +jvm.cpp defaultStream.hpp +jvm.cpp events.hpp +jvm.cpp handles.inline.hpp +jvm.cpp histogram.hpp +jvm.cpp hpi.hpp +jvm.cpp hpi_.hpp +jvm.cpp init.hpp +jvm.cpp instanceKlass.hpp +jvm.cpp interfaceSupport.hpp +jvm.cpp java.hpp +jvm.cpp javaAssertions.hpp +jvm.cpp javaCalls.hpp +jvm.cpp javaClasses.hpp +jvm.cpp jfieldIDWorkaround.hpp +jvm.cpp jvm.h +jvm.cpp jvm_.h +jvm.cpp jvm_misc.hpp +jvm.cpp jvmtiExport.hpp +jvm.cpp jvmtiThreadState.hpp +jvm.cpp management.hpp +jvm.cpp nativeLookup.hpp +jvm.cpp objArrayKlass.hpp +jvm.cpp oopFactory.hpp +jvm.cpp os.hpp +jvm.cpp perfData.hpp +jvm.cpp privilegedStack.hpp +jvm.cpp reflection.hpp +jvm.cpp symbolTable.hpp +jvm.cpp systemDictionary.hpp +jvm.cpp threadService.hpp +jvm.cpp top.hpp +jvm.cpp universe.inline.hpp +jvm.cpp utf8.hpp +jvm.cpp vframe.hpp +jvm.cpp vmSymbols.hpp +jvm.cpp vm_operations.hpp + +jvm.h globalDefinitions.hpp +jvm.h jni.h +jvm.h jvm_.h +jvm.h reflectionCompat.hpp + +jvm_.cpp interfaceSupport.hpp +jvm_.cpp jvm.h +jvm_.cpp osThread.hpp + +jvm_misc.hpp handles.hpp +jvm_misc.hpp jni.h + +jvmtiExport.hpp allocation.hpp +jvmtiExport.hpp globalDefinitions.hpp +jvmtiExport.hpp growableArray.hpp +jvmtiExport.hpp handles.hpp +jvmtiExport.hpp iterator.hpp +jvmtiExport.hpp jvmti.h +jvmtiExport.hpp oop.hpp +jvmtiExport.hpp oopsHierarchy.hpp + +jvmtiThreadState.hpp allocation.hpp +jvmtiThreadState.hpp allocation.inline.hpp +jvmtiThreadState.hpp growableArray.hpp +jvmtiThreadState.hpp jvmti.h +jvmtiThreadState.hpp jvmtiEventController.hpp +jvmtiThreadState.hpp thread.hpp + +klass.cpp atomic.hpp +klass.cpp collectedHeap.inline.hpp +klass.cpp instanceKlass.hpp +klass.cpp klass.inline.hpp +klass.cpp klassOop.hpp +klass.cpp oop.inline.hpp +klass.cpp oop.inline2.hpp +klass.cpp oopFactory.hpp +klass.cpp resourceArea.hpp +klass.cpp systemDictionary.hpp +klass.cpp vmSymbols.hpp + +klass.hpp accessFlags.hpp +klass.hpp genOopClosures.hpp +klass.hpp iterator.hpp +klass.hpp klassOop.hpp +klass.hpp klassPS.hpp +klass.hpp memRegion.hpp +klass.hpp oop.hpp +klass.hpp specialized_oop_closures.hpp + +klass.inline.hpp klass.hpp +klass.inline.hpp markOop.hpp + +klassKlass.cpp collectedHeap.hpp +klassKlass.cpp collectedHeap.inline.hpp +klassKlass.cpp constantPoolKlass.hpp +klassKlass.cpp handles.inline.hpp +klassKlass.cpp instanceKlass.hpp +klassKlass.cpp instanceOop.hpp +klassKlass.cpp klassKlass.hpp +klassKlass.cpp klassOop.hpp +klassKlass.cpp markSweep.hpp +klassKlass.cpp methodKlass.hpp +klassKlass.cpp objArrayKlass.hpp +klassKlass.cpp oop.inline.hpp +klassKlass.cpp oop.inline2.hpp +klassKlass.cpp oopFactory.hpp +klassKlass.cpp permGen.hpp +klassKlass.cpp symbolKlass.hpp +klassKlass.cpp symbolOop.hpp +klassKlass.cpp typeArrayKlass.hpp + +klassKlass.hpp klass.hpp +klassKlass.hpp klassOop.hpp +klassKlass.hpp oopFactory.hpp + +klassOop.cpp klassOop.hpp + +klassOop.hpp oop.hpp + +klassVtable.cpp arguments.hpp +klassVtable.cpp copy.hpp +klassVtable.cpp gcLocker.hpp +klassVtable.cpp handles.inline.hpp +klassVtable.cpp instanceKlass.hpp +klassVtable.cpp jvmtiRedefineClassesTrace.hpp +klassVtable.cpp klassOop.hpp +klassVtable.cpp klassVtable.hpp +klassVtable.cpp markSweep.hpp +klassVtable.cpp methodOop.hpp +klassVtable.cpp objArrayOop.hpp +klassVtable.cpp oop.inline.hpp +klassVtable.cpp resourceArea.hpp +klassVtable.cpp systemDictionary.hpp +klassVtable.cpp universe.inline.hpp +klassVtable.cpp vmSymbols.hpp + +klassVtable.hpp allocation.hpp +klassVtable.hpp growableArray.hpp +klassVtable.hpp handles.hpp +klassVtable.hpp oopsHierarchy.hpp + +linkResolver.cpp bytecode.hpp +linkResolver.cpp collectedHeap.inline.hpp +linkResolver.cpp compilationPolicy.hpp +linkResolver.cpp compileBroker.hpp +linkResolver.cpp fieldDescriptor.hpp +linkResolver.cpp frame.inline.hpp +linkResolver.cpp handles.inline.hpp +linkResolver.cpp instanceKlass.hpp +linkResolver.cpp interpreterRuntime.hpp +linkResolver.cpp linkResolver.hpp +linkResolver.cpp nativeLookup.hpp +linkResolver.cpp objArrayOop.hpp +linkResolver.cpp reflection.hpp +linkResolver.cpp resourceArea.hpp +linkResolver.cpp signature.hpp +linkResolver.cpp systemDictionary.hpp +linkResolver.cpp thread_.inline.hpp +linkResolver.cpp universe.inline.hpp +linkResolver.cpp vmSymbols.hpp +linkResolver.cpp vmThread.hpp + +linkResolver.hpp methodOop.hpp +linkResolver.hpp top.hpp + +liveRange.hpp copy.hpp + +loaderConstraints.cpp handles.inline.hpp +loaderConstraints.cpp hashtable.inline.hpp +loaderConstraints.cpp loaderConstraints.hpp +loaderConstraints.cpp oop.inline.hpp +loaderConstraints.cpp resourceArea.hpp +loaderConstraints.cpp safepoint.hpp + +loaderConstraints.hpp dictionary.hpp +loaderConstraints.hpp hashtable.hpp + +location.cpp debugInfo.hpp +location.cpp location.hpp + +location.hpp allocation.hpp +location.hpp assembler.hpp +location.hpp vmreg.hpp + +lowMemoryDetector.cpp interfaceSupport.hpp +lowMemoryDetector.cpp java.hpp +lowMemoryDetector.cpp javaCalls.hpp +lowMemoryDetector.cpp lowMemoryDetector.hpp +lowMemoryDetector.cpp management.hpp +lowMemoryDetector.cpp mutex.hpp +lowMemoryDetector.cpp mutexLocker.hpp +lowMemoryDetector.cpp oop.inline.hpp +lowMemoryDetector.cpp systemDictionary.hpp +lowMemoryDetector.cpp vmSymbols.hpp + +lowMemoryDetector.hpp allocation.hpp +lowMemoryDetector.hpp memoryPool.hpp +lowMemoryDetector.hpp memoryService.hpp + +management.cpp arguments.hpp +management.cpp classLoadingService.hpp +management.cpp compileBroker.hpp +management.cpp handles.inline.hpp +management.cpp heapDumper.hpp +management.cpp interfaceSupport.hpp +management.cpp iterator.hpp +management.cpp javaCalls.hpp +management.cpp jniHandles.hpp +management.cpp klass.hpp +management.cpp klassOop.hpp +management.cpp lowMemoryDetector.hpp +management.cpp management.hpp +management.cpp memoryManager.hpp +management.cpp memoryPool.hpp +management.cpp memoryService.hpp +management.cpp objArrayKlass.hpp +management.cpp oop.inline.hpp +management.cpp oopFactory.hpp +management.cpp os.hpp +management.cpp resourceArea.hpp +management.cpp runtimeService.hpp +management.cpp systemDictionary.hpp +management.cpp threadService.hpp + +management.hpp allocation.hpp +management.hpp handles.hpp +management.hpp jmm.h +management.hpp timer.hpp + +markOop.cpp markOop.hpp +markOop.cpp thread_.inline.hpp + +markOop.hpp oop.hpp + +markOop.inline.hpp globals.hpp +markOop.inline.hpp klass.hpp +markOop.inline.hpp klassOop.hpp +markOop.inline.hpp markOop.hpp + +markSweep.cpp compileBroker.hpp +memRegion.cpp globals.hpp +memRegion.cpp memRegion.hpp + +memRegion.hpp allocation.hpp +memRegion.hpp debug.hpp +memRegion.hpp globalDefinitions.hpp + +memoryManager.cpp systemDictionary.hpp +memoryManager.cpp vmSymbols.hpp +memoryManager.cpp dtrace.hpp +memoryManager.cpp handles.inline.hpp +memoryManager.cpp javaCalls.hpp +memoryManager.cpp lowMemoryDetector.hpp +memoryManager.cpp management.hpp +memoryManager.cpp memoryManager.hpp +memoryManager.cpp memoryPool.hpp +memoryManager.cpp memoryService.hpp +memoryManager.cpp oop.inline.hpp + +memoryManager.hpp allocation.hpp +memoryManager.hpp memoryUsage.hpp +memoryManager.hpp timer.hpp + +memoryPool.cpp systemDictionary.hpp +memoryPool.cpp vmSymbols.hpp +memoryPool.cpp handles.inline.hpp +memoryPool.cpp javaCalls.hpp +memoryPool.cpp lowMemoryDetector.hpp +memoryPool.cpp management.hpp +memoryPool.cpp memoryManager.hpp +memoryPool.cpp memoryPool.hpp +memoryPool.cpp oop.inline.hpp + +memoryPool.hpp defNewGeneration.hpp +memoryPool.hpp heap.hpp +memoryPool.hpp memoryUsage.hpp +memoryPool.hpp mutableSpace.hpp +memoryPool.hpp space.hpp + +memoryService.cpp classLoadingService.hpp +memoryService.cpp collectorPolicy.hpp +memoryService.cpp defNewGeneration.hpp +memoryService.cpp genCollectedHeap.hpp +memoryService.cpp generation.hpp +memoryService.cpp generationSpec.hpp +memoryService.cpp growableArray.hpp +memoryService.cpp heap.hpp +memoryService.cpp javaCalls.hpp +memoryService.cpp lowMemoryDetector.hpp +memoryService.cpp management.hpp +memoryService.cpp memRegion.hpp +memoryService.cpp memoryManager.hpp +memoryService.cpp memoryPool.hpp +memoryService.cpp memoryService.hpp +memoryService.cpp mutableSpace.hpp +memoryService.cpp oop.inline.hpp +memoryService.cpp permGen.hpp +memoryService.cpp systemDictionary.hpp +memoryService.cpp tenuredGeneration.hpp +memoryService.cpp vmSymbols.hpp + +memoryService.hpp allocation.hpp +memoryService.hpp generation.hpp +memoryService.hpp handles.hpp +memoryService.hpp memoryUsage.hpp + +memoryUsage.hpp globalDefinitions.hpp + +memprofiler.cpp codeCache.hpp +memprofiler.cpp collectedHeap.inline.hpp +memprofiler.cpp generation.hpp +memprofiler.cpp handles.inline.hpp +memprofiler.cpp jniHandles.hpp +memprofiler.cpp memprofiler.hpp +memprofiler.cpp mutexLocker.hpp +memprofiler.cpp oopMapCache.hpp +memprofiler.cpp os.hpp +memprofiler.cpp permGen.hpp +memprofiler.cpp resourceArea.hpp +memprofiler.cpp systemDictionary.hpp +memprofiler.cpp task.hpp +memprofiler.cpp thread_.inline.hpp +memprofiler.cpp vmThread.hpp + +methodComparator.cpp globalDefinitions.hpp +methodComparator.cpp handles.inline.hpp +methodComparator.cpp jvmtiRedefineClassesTrace.hpp +methodComparator.cpp methodComparator.hpp +methodComparator.cpp oop.inline.hpp +methodComparator.cpp symbolOop.hpp + +methodComparator.hpp bytecodeStream.hpp +methodComparator.hpp constantPoolOop.hpp +methodComparator.hpp methodOop.hpp + +methodDataKlass.cpp collectedHeap.inline.hpp +methodDataKlass.cpp gcLocker.hpp +methodDataKlass.cpp handles.inline.hpp +methodDataKlass.cpp klassOop.hpp +methodDataKlass.cpp markSweep.hpp +methodDataKlass.cpp methodDataKlass.hpp +methodDataKlass.cpp methodDataOop.hpp +methodDataKlass.cpp oop.inline.hpp +methodDataKlass.cpp oop.inline2.hpp +methodDataKlass.cpp resourceArea.hpp +methodDataKlass.cpp universe.inline.hpp + +methodDataKlass.hpp klass.hpp + +methodDataOop.cpp bytecode.hpp +methodDataOop.cpp bytecodeStream.hpp +methodDataOop.cpp deoptimization.hpp +methodDataOop.cpp handles.inline.hpp +methodDataOop.cpp linkResolver.hpp +methodDataOop.cpp markSweep.hpp +methodDataOop.cpp markSweep.inline.hpp +methodDataOop.cpp methodDataOop.hpp +methodDataOop.cpp oop.inline.hpp +methodDataOop.cpp systemDictionary.hpp + +methodDataOop.hpp bytecodes.hpp +methodDataOop.hpp oop.hpp +methodDataOop.hpp orderAccess.hpp +methodDataOop.hpp universe.hpp + +methodKlass.cpp collectedHeap.inline.hpp +methodKlass.cpp constMethodKlass.hpp +methodKlass.cpp gcLocker.hpp +methodKlass.cpp handles.inline.hpp +methodKlass.cpp interpreter.hpp +methodKlass.cpp javaClasses.hpp +methodKlass.cpp klassOop.hpp +methodKlass.cpp markSweep.hpp +methodKlass.cpp methodDataOop.hpp +methodKlass.cpp methodKlass.hpp +methodKlass.cpp oop.inline.hpp +methodKlass.cpp oop.inline2.hpp +methodKlass.cpp resourceArea.hpp +methodKlass.cpp symbolOop.hpp +methodKlass.cpp universe.inline.hpp + +methodKlass.hpp klass.hpp +methodKlass.hpp klassOop.hpp +methodKlass.hpp methodOop.hpp + +methodLiveness.cpp allocation.inline.hpp +methodLiveness.cpp bytecode.hpp +methodLiveness.cpp bytecodes.hpp +methodLiveness.cpp ciMethod.hpp +methodLiveness.cpp ciMethodBlocks.hpp +methodLiveness.cpp ciStreams.hpp +methodLiveness.cpp methodLiveness.hpp + +methodLiveness.hpp bitMap.hpp +methodLiveness.hpp growableArray.hpp + +methodOop.cpp arguments.hpp +methodOop.cpp bytecodeStream.hpp +methodOop.cpp bytecodeTracer.hpp +methodOop.cpp bytecodes.hpp +methodOop.cpp collectedHeap.inline.hpp +methodOop.cpp debugInfoRec.hpp +methodOop.cpp frame.inline.hpp +methodOop.cpp gcLocker.hpp +methodOop.cpp gcTaskThread.hpp +methodOop.cpp generation.hpp +methodOop.cpp handles.inline.hpp +methodOop.cpp interpreter.hpp +methodOop.cpp jvmtiExport.hpp +methodOop.cpp klassOop.hpp +methodOop.cpp methodDataOop.hpp +methodOop.cpp methodOop.hpp +methodOop.cpp nativeLookup.hpp +methodOop.cpp oop.inline.hpp +methodOop.cpp oopFactory.hpp +methodOop.cpp oopMapCache.hpp +methodOop.cpp relocator.hpp +methodOop.cpp sharedRuntime.hpp +methodOop.cpp signature.hpp +methodOop.cpp symbolOop.hpp +methodOop.cpp systemDictionary.hpp +methodOop.cpp xmlstream.hpp + +methodOop.hpp accessFlags.hpp +methodOop.hpp compressedStream.hpp +methodOop.hpp constMethodOop.hpp +methodOop.hpp constantPoolOop.hpp +methodOop.hpp growableArray.hpp +methodOop.hpp instanceKlass.hpp +methodOop.hpp invocationCounter.hpp +methodOop.hpp oop.hpp +methodOop.hpp oopMap.hpp +methodOop.hpp typeArrayOop.hpp +methodOop.hpp vmSymbols.hpp + +modRefBarrierSet.hpp barrierSet.hpp + +monitorChunk.cpp allocation.inline.hpp +monitorChunk.cpp monitorChunk.hpp +monitorChunk.cpp oop.inline.hpp + +monitorChunk.hpp synchronizer.hpp + +mutex.cpp events.hpp +mutex.cpp mutex.hpp +mutex.cpp mutex_.inline.hpp +mutex.cpp osThread.hpp +mutex.cpp thread_.inline.hpp + +mutex.hpp allocation.hpp +mutex.hpp histogram.hpp +mutex.hpp os.hpp + +mutexLocker.cpp mutexLocker.hpp +mutexLocker.cpp safepoint.hpp +mutexLocker.cpp threadLocalStorage.hpp +mutexLocker.cpp thread_.inline.hpp +mutexLocker.cpp vmThread.hpp + +mutexLocker.hpp allocation.hpp +mutexLocker.hpp mutex.hpp +mutexLocker.hpp os_.inline.hpp + +mutex_.cpp events.hpp +mutex_.cpp interfaceSupport.hpp +mutex_.cpp mutex.hpp +mutex_.cpp mutex_.inline.hpp +mutex_.cpp thread_.inline.hpp + +mutex_.inline.hpp interfaceSupport.hpp +mutex_.inline.hpp os_.inline.hpp +mutex_.inline.hpp thread_.inline.hpp + +nativeInst_.cpp assembler_.inline.hpp +nativeInst_.cpp handles.hpp +nativeInst_.cpp nativeInst_.hpp +nativeInst_.cpp oop.hpp +nativeInst_.cpp ostream.hpp +nativeInst_.cpp resourceArea.hpp +nativeInst_.cpp sharedRuntime.hpp +nativeInst_.cpp stubRoutines.hpp + +nativeInst_.hpp allocation.hpp +nativeInst_.hpp assembler.hpp +nativeInst_.hpp icache.hpp +nativeInst_.hpp os.hpp +nativeInst_.hpp top.hpp + +nativeLookup.cpp arguments.hpp +nativeLookup.cpp handles.inline.hpp +nativeLookup.cpp hpi.hpp +nativeLookup.cpp instanceKlass.hpp +nativeLookup.cpp javaCalls.hpp +nativeLookup.cpp javaClasses.hpp +nativeLookup.cpp jvm_misc.hpp +nativeLookup.cpp methodOop.hpp +nativeLookup.cpp nativeLookup.hpp +nativeLookup.cpp oop.inline.hpp +nativeLookup.cpp oopFactory.hpp +nativeLookup.cpp os_.inline.hpp +nativeLookup.cpp resourceArea.hpp +nativeLookup.cpp sharedRuntime.hpp +nativeLookup.cpp signature.hpp +nativeLookup.cpp symbolOop.hpp +nativeLookup.cpp systemDictionary.hpp +nativeLookup.cpp universe.inline.hpp +nativeLookup.cpp vmSymbols.hpp + +nativeLookup.hpp handles.hpp +nativeLookup.hpp top.hpp + +nmethod.cpp abstractCompiler.hpp +nmethod.cpp bytecode.hpp +nmethod.cpp codeCache.hpp +nmethod.cpp compileLog.hpp +nmethod.cpp compiledIC.hpp +nmethod.cpp compilerOracle.hpp +nmethod.cpp disassembler_.hpp +nmethod.cpp dtrace.hpp +nmethod.cpp events.hpp +nmethod.cpp jvmtiRedefineClassesTrace.hpp +nmethod.cpp methodDataOop.hpp +nmethod.cpp nmethod.hpp +nmethod.cpp scopeDesc.hpp +nmethod.cpp sharedRuntime.hpp +nmethod.cpp sweeper.hpp +nmethod.cpp vtune.hpp +nmethod.cpp xmlstream.hpp + +nmethod.hpp codeBlob.hpp +nmethod.hpp pcDesc.hpp + +objArrayKlass.cpp collectedHeap.inline.hpp +objArrayKlass.cpp copy.hpp +objArrayKlass.cpp genOopClosures.inline.hpp +objArrayKlass.cpp handles.inline.hpp +objArrayKlass.cpp instanceKlass.hpp +objArrayKlass.cpp mutexLocker.hpp +objArrayKlass.cpp objArrayKlass.hpp +objArrayKlass.cpp objArrayKlassKlass.hpp +objArrayKlass.cpp objArrayOop.hpp +objArrayKlass.cpp oop.inline.hpp +objArrayKlass.cpp oop.inline2.hpp +objArrayKlass.cpp resourceArea.hpp +objArrayKlass.cpp symbolOop.hpp +objArrayKlass.cpp systemDictionary.hpp +objArrayKlass.cpp universe.inline.hpp +objArrayKlass.cpp vmSymbols.hpp + +objArrayKlass.hpp arrayKlass.hpp +objArrayKlass.hpp instanceKlass.hpp +objArrayKlass.hpp specialized_oop_closures.hpp + +objArrayKlassKlass.cpp collectedHeap.inline.hpp +objArrayKlassKlass.cpp instanceKlass.hpp +objArrayKlassKlass.cpp javaClasses.hpp +objArrayKlassKlass.cpp objArrayKlassKlass.hpp +objArrayKlassKlass.cpp oop.inline.hpp +objArrayKlassKlass.cpp oop.inline2.hpp +objArrayKlassKlass.cpp systemDictionary.hpp + +objArrayKlassKlass.hpp arrayKlassKlass.hpp +objArrayKlassKlass.hpp objArrayKlass.hpp + +objArrayOop.cpp objArrayOop.hpp +objArrayOop.cpp oop.inline.hpp + +objArrayOop.hpp arrayOop.hpp + +objectMonitor.hpp os.hpp + +objectMonitor_.cpp dtrace.hpp +objectMonitor_.cpp interfaceSupport.hpp +objectMonitor_.cpp objectMonitor.hpp +objectMonitor_.cpp objectMonitor.inline.hpp +objectMonitor_.cpp oop.inline.hpp +objectMonitor_.cpp osThread.hpp +objectMonitor_.cpp os_.inline.hpp +objectMonitor_.cpp threadService.hpp +objectMonitor_.cpp thread_.inline.hpp +objectMonitor_.cpp vmSymbols.hpp + +objectMonitor_.hpp generate_platform_dependent_include +objectMonitor_.hpp os_.inline.hpp +objectMonitor_.hpp thread_.inline.hpp +objectMonitor_.hpp top.hpp + +objectMonitor_.inline.hpp generate_platform_dependent_include + +oop.cpp copy.hpp +oop.cpp handles.inline.hpp +oop.cpp javaClasses.hpp +oop.cpp oop.inline.hpp +oop.cpp thread_.inline.hpp + +oop.hpp iterator.hpp +oop.hpp memRegion.hpp +oop.hpp specialized_oop_closures.hpp +oop.hpp top.hpp + +oop.inline.hpp ageTable.hpp +oop.inline.hpp arrayKlass.hpp +oop.inline.hpp arrayOop.hpp +oop.inline.hpp atomic.hpp +oop.inline.hpp barrierSet.inline.hpp +oop.inline.hpp cardTableModRefBS.hpp +oop.inline.hpp collectedHeap.inline.hpp +oop.inline.hpp compactingPermGenGen.hpp +oop.inline.hpp genCollectedHeap.hpp +oop.inline.hpp generation.hpp +oop.inline.hpp klass.hpp +oop.inline.hpp klassOop.hpp +oop.inline.hpp markOop.inline.hpp +oop.inline.hpp markSweep.hpp +oop.inline.hpp markSweep.inline.hpp +oop.inline.hpp oop.hpp +oop.inline.hpp os.hpp +oop.inline.hpp permGen.hpp +oop.inline.hpp specialized_oop_closures.hpp + +oop.inline2.hpp collectedHeap.hpp +oop.inline2.hpp generation.hpp +oop.inline2.hpp oop.hpp +oop.inline2.hpp permGen.hpp +oop.inline2.hpp universe.hpp + +oopFactory.cpp collectedHeap.inline.hpp +oopFactory.cpp compiledICHolderKlass.hpp +oopFactory.cpp constMethodKlass.hpp +oopFactory.cpp constantPoolKlass.hpp +oopFactory.cpp cpCacheKlass.hpp +oopFactory.cpp instanceKlass.hpp +oopFactory.cpp instanceKlassKlass.hpp +oopFactory.cpp instanceOop.hpp +oopFactory.cpp javaClasses.hpp +oopFactory.cpp klassKlass.hpp +oopFactory.cpp klassOop.hpp +oopFactory.cpp methodDataKlass.hpp +oopFactory.cpp methodKlass.hpp +oopFactory.cpp objArrayOop.hpp +oopFactory.cpp oop.inline.hpp +oopFactory.cpp oopFactory.hpp +oopFactory.cpp resourceArea.hpp +oopFactory.cpp symbolTable.hpp +oopFactory.cpp systemDictionary.hpp +oopFactory.cpp universe.inline.hpp +oopFactory.cpp vmSymbols.hpp + +oopFactory.hpp growableArray.hpp +oopFactory.hpp klassOop.hpp +oopFactory.hpp objArrayKlass.hpp +oopFactory.hpp oop.hpp +oopFactory.hpp symbolTable.hpp +oopFactory.hpp systemDictionary.hpp +oopFactory.hpp typeArrayKlass.hpp +oopFactory.hpp universe.hpp + +oopMap.cpp allocation.inline.hpp +oopMap.cpp codeBlob.hpp +oopMap.cpp codeCache.hpp +oopMap.cpp collectedHeap.hpp +oopMap.cpp frame.inline.hpp +oopMap.cpp nmethod.hpp +oopMap.cpp oopMap.hpp +oopMap.cpp resourceArea.hpp +oopMap.cpp scopeDesc.hpp +oopMap.cpp signature.hpp + +oopMap.hpp allocation.hpp +oopMap.hpp compressedStream.hpp +oopMap.hpp growableArray.hpp +oopMap.hpp vmreg.hpp + +oopMapCache.cpp allocation.inline.hpp +oopMapCache.cpp handles.inline.hpp +oopMapCache.cpp oop.inline.hpp +oopMapCache.cpp oopMapCache.hpp +oopMapCache.cpp resourceArea.hpp +oopMapCache.cpp signature.hpp + +oopMapCache.hpp generateOopMap.hpp + +oopRecorder.cpp allocation.inline.hpp +oopRecorder.cpp oop.inline.hpp +oopRecorder.cpp oopRecorder.hpp + +oopRecorder.hpp growableArray.hpp +oopRecorder.hpp handles.hpp + +oopsHierarchy.cpp collectedHeap.hpp +oopsHierarchy.cpp collectedHeap.inline.hpp +oopsHierarchy.cpp globalDefinitions.hpp +oopsHierarchy.cpp oopsHierarchy.hpp +oopsHierarchy.cpp thread.hpp +oopsHierarchy.cpp thread_.inline.hpp + +orderAccess.cpp orderAccess.hpp + +orderAccess.hpp allocation.hpp +orderAccess.hpp os.hpp + +orderAccess_.inline.hpp orderAccess.hpp + +os.cpp allocation.inline.hpp +os.cpp arguments.hpp +os.cpp attachListener.hpp +os.cpp classLoader.hpp +os.cpp defaultStream.hpp +os.cpp events.hpp +os.cpp frame.inline.hpp +os.cpp hpi.hpp +os.cpp interfaceSupport.hpp +os.cpp interpreter.hpp +os.cpp java.hpp +os.cpp javaCalls.hpp +os.cpp javaClasses.hpp +os.cpp jvm.h +os.cpp jvm_misc.hpp +os.cpp mutexLocker.hpp +os.cpp oop.inline.hpp +os.cpp os.hpp +os.cpp os_.inline.hpp +os.cpp stubRoutines.hpp +os.cpp systemDictionary.hpp +os.cpp threadService.hpp +os.cpp thread_.inline.hpp +os.cpp vmGCOperations.hpp +os.cpp vmSymbols.hpp +os.cpp vtableStubs.hpp + +os.hpp atomic.hpp +os.hpp extendedPC.hpp +os.hpp handles.hpp +os.hpp jvmti.h +os.hpp top.hpp + +os_.cpp allocation.inline.hpp +os_.cpp arguments.hpp +os_.cpp assembler_.inline.hpp +os_.cpp classLoader.hpp +os_.cpp events.hpp +os_.cpp extendedPC.hpp +os_.cpp frame.inline.hpp +os_.cpp hpi.hpp +os_.cpp icBuffer.hpp +os_.cpp interfaceSupport.hpp +os_.cpp interpreter.hpp +os_.cpp java.hpp +os_.cpp javaCalls.hpp +os_.cpp jniFastGetField.hpp +os_.cpp jvm.h +os_.cpp jvm_.h +os_.cpp jvm_misc.hpp +os_.cpp mutexLocker.hpp +os_.cpp mutex_.inline.hpp +os_.cpp nativeInst_.hpp +os_.cpp no_precompiled_headers +os_.cpp osThread.hpp +os_.cpp os_share_.hpp +os_.cpp sharedRuntime.hpp +os_.cpp stubRoutines.hpp +os_.cpp systemDictionary.hpp +os_.cpp thread_.inline.hpp +os_.cpp timer.hpp +os_.cpp vmError.hpp +os_.cpp vmSymbols.hpp +os_.cpp vtableStubs.hpp + +os_.hpp generate_platform_dependent_include + +os_.cpp allocation.inline.hpp +os_.cpp arguments.hpp +os_.cpp assembler_.inline.hpp +os_.cpp attachListener.hpp +os_.cpp classLoader.hpp +os_.cpp compileBroker.hpp +os_.cpp defaultStream.hpp +os_.cpp events.hpp +os_.cpp extendedPC.hpp +os_.cpp filemap.hpp +os_.cpp globals.hpp +os_.cpp hpi.hpp +os_.cpp icBuffer.hpp +os_.cpp interfaceSupport.hpp +os_.cpp interpreter.hpp +os_.cpp java.hpp +os_.cpp javaCalls.hpp +os_.cpp jniFastGetField.hpp +os_.cpp jvm.h +os_.cpp jvm_.h +os_.cpp jvm_misc.hpp +os_.cpp mutexLocker.hpp +os_.cpp mutex_.inline.hpp +os_.cpp nativeInst_.hpp +os_.cpp no_precompiled_headers +os_.cpp objectMonitor.hpp +os_.cpp objectMonitor.inline.hpp +os_.cpp oop.inline.hpp +os_.cpp osThread.hpp +os_.cpp os_share_.hpp +os_.cpp perfMemory.hpp +os_.cpp runtimeService.hpp +os_.cpp sharedRuntime.hpp +os_.cpp statSampler.hpp +os_.cpp stubRoutines.hpp +os_.cpp systemDictionary.hpp +os_.cpp threadCritical.hpp +os_.cpp thread_.inline.hpp +os_.cpp timer.hpp +os_.cpp vmError.hpp +os_.cpp vmSymbols.hpp +os_.cpp vtableStubs.hpp + +os_.hpp generate_platform_dependent_include + +os_.inline.hpp atomic.hpp +os_.inline.hpp atomic_.inline.hpp +os_.inline.hpp orderAccess_.inline.hpp +os_.inline.hpp os.hpp + +osThread.cpp oop.inline.hpp +osThread.cpp osThread.hpp + +osThread.hpp frame.hpp +osThread.hpp handles.hpp +osThread.hpp hpi.hpp +osThread.hpp javaFrameAnchor.hpp +osThread.hpp objectMonitor.hpp +osThread.hpp top.hpp + +osThread_.cpp assembler_.inline.hpp +osThread_.cpp atomic.hpp +osThread_.cpp handles.inline.hpp +osThread_.cpp mutexLocker.hpp +osThread_.cpp no_precompiled_headers +osThread_.cpp os.hpp +osThread_.cpp osThread.hpp +osThread_.cpp safepoint.hpp +osThread_.cpp vmThread.hpp + +osThread_.hpp generate_platform_dependent_include + +ostream.cpp arguments.hpp +ostream.cpp compileLog.hpp +ostream.cpp defaultStream.hpp +ostream.cpp oop.inline.hpp +ostream.cpp os_.inline.hpp +ostream.cpp hpi.hpp +ostream.cpp hpi_.hpp +ostream.cpp ostream.hpp +ostream.cpp top.hpp +ostream.cpp xmlstream.hpp + +ostream.hpp allocation.hpp +ostream.hpp timer.hpp + +pcDesc.cpp debugInfoRec.hpp +pcDesc.cpp nmethod.hpp +pcDesc.cpp pcDesc.hpp +pcDesc.cpp resourceArea.hpp +pcDesc.cpp scopeDesc.hpp + +pcDesc.hpp allocation.hpp + +perf.cpp allocation.inline.hpp +perf.cpp interfaceSupport.hpp +perf.cpp jni.h +perf.cpp jvm.h +perf.cpp oop.inline.hpp +perf.cpp perfData.hpp +perf.cpp perfMemory.hpp +perf.cpp resourceArea.hpp +perf.cpp vmSymbols.hpp + +perfData.cpp exceptions.hpp +perfData.cpp globalDefinitions.hpp +perfData.cpp handles.inline.hpp +perfData.cpp java.hpp +perfData.cpp mutex.hpp +perfData.cpp mutexLocker.hpp +perfData.cpp oop.inline.hpp +perfData.cpp os.hpp +perfData.cpp perfData.hpp +perfData.cpp vmSymbols.hpp + +perfData.hpp allocation.inline.hpp +perfData.hpp growableArray.hpp +perfData.hpp perfMemory.hpp +perfData.hpp timer.hpp + +perfMemory.cpp allocation.inline.hpp +perfMemory.cpp arguments.hpp +perfMemory.cpp globalDefinitions.hpp +perfMemory.cpp java.hpp +perfMemory.cpp mutex.hpp +perfMemory.cpp mutexLocker.hpp +perfMemory.cpp os.hpp +perfMemory.cpp perfData.hpp +perfMemory.cpp perfMemory.hpp +perfMemory.cpp statSampler.hpp + +perfMemory.hpp exceptions.hpp + +perfMemory_.cpp allocation.inline.hpp +perfMemory_.cpp exceptions.hpp +perfMemory_.cpp handles.inline.hpp +perfMemory_.cpp oop.inline.hpp +perfMemory_.cpp os_.inline.hpp +perfMemory_.cpp perfMemory.hpp +perfMemory_.cpp resourceArea.hpp +perfMemory_.cpp vmSymbols.hpp + +permGen.cpp blockOffsetTable.hpp +permGen.cpp cSpaceCounters.hpp +permGen.cpp collectedHeap.inline.hpp +permGen.cpp compactPermGen.hpp +permGen.cpp genCollectedHeap.hpp +permGen.cpp generation.inline.hpp +permGen.cpp java.hpp +permGen.cpp oop.inline.hpp +permGen.cpp permGen.hpp +permGen.cpp universe.hpp + +permGen.hpp gcCause.hpp +permGen.hpp generation.hpp +permGen.hpp handles.hpp +permGen.hpp iterator.hpp +permGen.hpp virtualspace.hpp + +placeholders.cpp fieldType.hpp +placeholders.cpp hashtable.inline.hpp +placeholders.cpp oop.inline.hpp +placeholders.cpp placeholders.hpp +placeholders.cpp systemDictionary.hpp + +placeholders.hpp hashtable.hpp + +prefetch.hpp allocation.hpp + +prefetch_.inline.hpp prefetch.hpp + +preserveException.cpp handles.inline.hpp +preserveException.cpp preserveException.hpp + +preserveException.hpp handles.hpp +preserveException.hpp thread_.inline.hpp + +privilegedStack.cpp allocation.inline.hpp +privilegedStack.cpp instanceKlass.hpp +privilegedStack.cpp methodOop.hpp +privilegedStack.cpp oop.inline.hpp +privilegedStack.cpp privilegedStack.hpp +privilegedStack.cpp vframe.hpp + +privilegedStack.hpp allocation.hpp +privilegedStack.hpp growableArray.hpp +privilegedStack.hpp oopsHierarchy.hpp +privilegedStack.hpp vframe.hpp + +referencePolicy.cpp arguments.hpp +referencePolicy.cpp globals.hpp +referencePolicy.cpp javaClasses.hpp +referencePolicy.cpp referencePolicy.hpp +referencePolicy.cpp universe.hpp + +referencePolicy.hpp oop.hpp + +referenceProcessor.cpp collectedHeap.hpp +referenceProcessor.cpp collectedHeap.inline.hpp +referenceProcessor.cpp java.hpp +referenceProcessor.cpp javaClasses.hpp +referenceProcessor.cpp jniHandles.hpp +referenceProcessor.cpp oop.inline.hpp +referenceProcessor.cpp referencePolicy.hpp +referenceProcessor.cpp referenceProcessor.hpp +referenceProcessor.cpp systemDictionary.hpp + +referenceProcessor.hpp instanceRefKlass.hpp + +reflection.cpp arguments.hpp +reflection.cpp handles.inline.hpp +reflection.cpp instanceKlass.hpp +reflection.cpp javaCalls.hpp +reflection.cpp javaClasses.hpp +reflection.cpp jvm.h +reflection.cpp linkResolver.hpp +reflection.cpp objArrayKlass.hpp +reflection.cpp objArrayOop.hpp +reflection.cpp oopFactory.hpp +reflection.cpp reflection.hpp +reflection.cpp reflectionUtils.hpp +reflection.cpp resourceArea.hpp +reflection.cpp signature.hpp +reflection.cpp symbolTable.hpp +reflection.cpp systemDictionary.hpp +reflection.cpp universe.inline.hpp +reflection.cpp verifier.hpp +reflection.cpp vframe.hpp +reflection.cpp vmSymbols.hpp + +reflection.hpp accessFlags.hpp +reflection.hpp fieldDescriptor.hpp +reflection.hpp growableArray.hpp +reflection.hpp oop.hpp +reflection.hpp reflectionCompat.hpp + +reflectionUtils.cpp javaClasses.hpp +reflectionUtils.cpp reflectionUtils.hpp +reflectionUtils.cpp universe.inline.hpp + +reflectionUtils.hpp accessFlags.hpp +reflectionUtils.hpp allocation.hpp +reflectionUtils.hpp globalDefinitions.hpp +reflectionUtils.hpp handles.inline.hpp +reflectionUtils.hpp instanceKlass.hpp +reflectionUtils.hpp objArrayOop.hpp +reflectionUtils.hpp oopsHierarchy.hpp +reflectionUtils.hpp reflection.hpp + +register.cpp register.hpp + +register.hpp top.hpp + +register_.cpp register_.hpp + +register_.hpp register.hpp +register_.hpp vm_version_.hpp + +registerMap.hpp globalDefinitions.hpp +registerMap.hpp register_.hpp +registerMap.hpp vmreg.hpp + +registerMap_.hpp generate_platform_dependent_include + +register_definitions_.cpp assembler.hpp +register_definitions_.cpp interp_masm_.hpp +register_definitions_.cpp register.hpp +register_definitions_.cpp register_.hpp + +relocInfo.cpp assembler_.inline.hpp +relocInfo.cpp compiledIC.hpp +relocInfo.cpp copy.hpp +relocInfo.cpp nativeInst_.hpp +relocInfo.cpp nmethod.hpp +relocInfo.cpp relocInfo.hpp +relocInfo.cpp resourceArea.hpp +relocInfo.cpp stubCodeGenerator.hpp + +relocInfo.hpp allocation.hpp +relocInfo.hpp top.hpp + +relocInfo_.cpp assembler.inline.hpp +relocInfo_.cpp assembler_.inline.hpp +relocInfo_.cpp nativeInst_.hpp +relocInfo_.cpp relocInfo.hpp +relocInfo_.cpp safepoint.hpp + +relocInfo_.hpp generate_platform_dependent_include + +relocator.cpp bytecodes.hpp +relocator.cpp handles.inline.hpp +relocator.cpp oop.inline.hpp +relocator.cpp relocator.hpp +relocator.cpp universe.inline.hpp + +relocator.hpp bytecodes.hpp +relocator.hpp bytes_.hpp +relocator.hpp methodOop.hpp + +resolutionErrors.cpp handles.inline.hpp +resolutionErrors.cpp hashtable.inline.hpp +resolutionErrors.cpp oop.inline.hpp +resolutionErrors.cpp resolutionErrors.hpp +resolutionErrors.cpp resourceArea.hpp +resolutionErrors.cpp safepoint.hpp + +resolutionErrors.hpp constantPoolOop.hpp +resolutionErrors.hpp hashtable.hpp + +resourceArea.cpp allocation.inline.hpp +resourceArea.cpp mutexLocker.hpp +resourceArea.cpp resourceArea.hpp +resourceArea.cpp thread_.inline.hpp + +resourceArea.hpp allocation.hpp +resourceArea.hpp thread_.inline.hpp + +// restore is jck optional, put cpp deps in includeDB_features + +rewriter.cpp bytecodes.hpp +rewriter.cpp gcLocker.hpp +rewriter.cpp generateOopMap.hpp +rewriter.cpp interpreter.hpp +rewriter.cpp objArrayOop.hpp +rewriter.cpp oop.inline.hpp +rewriter.cpp oopFactory.hpp +rewriter.cpp resourceArea.hpp +rewriter.cpp rewriter.hpp + +rewriter.hpp allocation.hpp +rewriter.hpp growableArray.hpp +rewriter.hpp handles.inline.hpp + +rframe.cpp frame.inline.hpp +rframe.cpp interpreter.hpp +rframe.cpp oop.inline.hpp +rframe.cpp rframe.hpp +rframe.cpp symbolOop.hpp +rframe.cpp vframe.hpp +rframe.cpp vframe_hp.hpp + +rframe.hpp allocation.hpp +rframe.hpp frame.inline.hpp + +runtimeService.cpp attachListener.hpp +runtimeService.cpp classLoader.hpp +runtimeService.cpp dtrace.hpp +runtimeService.cpp exceptions.hpp +runtimeService.cpp management.hpp +runtimeService.cpp runtimeService.hpp + +runtimeService.hpp perfData.hpp +runtimeService.hpp timer.hpp + +safepoint.cpp codeCache.hpp +safepoint.cpp collectedHeap.hpp +safepoint.cpp deoptimization.hpp +safepoint.cpp events.hpp +safepoint.cpp frame.inline.hpp +safepoint.cpp icBuffer.hpp +safepoint.cpp interfaceSupport.hpp +safepoint.cpp interpreter.hpp +safepoint.cpp mutexLocker.hpp +safepoint.cpp nativeInst_.hpp +safepoint.cpp nmethod.hpp +safepoint.cpp oop.inline.hpp +safepoint.cpp osThread.hpp +safepoint.cpp pcDesc.hpp +safepoint.cpp resourceArea.hpp +safepoint.cpp runtimeService.hpp +safepoint.cpp safepoint.hpp +safepoint.cpp scopeDesc.hpp +safepoint.cpp signature.hpp +safepoint.cpp stubCodeGenerator.hpp +safepoint.cpp stubRoutines.hpp +safepoint.cpp sweeper.hpp +safepoint.cpp symbolOop.hpp +safepoint.cpp synchronizer.hpp +safepoint.cpp systemDictionary.hpp +safepoint.cpp thread_.inline.hpp +safepoint.cpp universe.inline.hpp +safepoint.cpp vmreg_.inline.hpp + +safepoint.hpp allocation.hpp +safepoint.hpp assembler.hpp +safepoint.hpp extendedPC.hpp +safepoint.hpp nmethod.hpp +safepoint.hpp os.hpp +safepoint.hpp ostream.hpp + +scopeDesc.cpp debugInfoRec.hpp +scopeDesc.cpp handles.inline.hpp +scopeDesc.cpp oop.inline.hpp +scopeDesc.cpp pcDesc.hpp +scopeDesc.cpp resourceArea.hpp +scopeDesc.cpp scopeDesc.hpp + +scopeDesc.hpp debugInfo.hpp +scopeDesc.hpp growableArray.hpp +scopeDesc.hpp methodOop.hpp +scopeDesc.hpp pcDesc.hpp + +// serialize is jck optional, put cpp deps in includeDB_features + +serviceUtil.hpp objArrayOop.hpp +serviceUtil.hpp systemDictionary.hpp + +sharedHeap.cpp codeCache.hpp +sharedHeap.cpp collectedHeap.inline.hpp +sharedHeap.cpp copy.hpp +sharedHeap.cpp fprofiler.hpp +sharedHeap.cpp java.hpp +sharedHeap.cpp management.hpp +sharedHeap.cpp oop.inline.hpp +sharedHeap.cpp sharedHeap.hpp +sharedHeap.cpp symbolTable.hpp +sharedHeap.cpp systemDictionary.hpp +sharedHeap.cpp workgroup.hpp + +sharedHeap.hpp collectedHeap.hpp +sharedHeap.hpp generation.hpp +sharedHeap.hpp permGen.hpp + +sharedRuntime.cpp abstractCompiler.hpp +sharedRuntime.cpp arguments.hpp +sharedRuntime.cpp biasedLocking.hpp +sharedRuntime.cpp compiledIC.hpp +sharedRuntime.cpp compilerOracle.hpp +sharedRuntime.cpp copy.hpp +sharedRuntime.cpp dtrace.hpp +sharedRuntime.cpp events.hpp +sharedRuntime.cpp forte.hpp +sharedRuntime.cpp gcLocker.inline.hpp +sharedRuntime.cpp handles.inline.hpp +sharedRuntime.cpp init.hpp +sharedRuntime.cpp interfaceSupport.hpp +sharedRuntime.cpp interpreterRuntime.hpp +sharedRuntime.cpp interpreter.hpp +sharedRuntime.cpp javaCalls.hpp +sharedRuntime.cpp jvmtiExport.hpp +sharedRuntime.cpp nativeInst_.hpp +sharedRuntime.cpp nativeLookup.hpp +sharedRuntime.cpp oop.inline.hpp +sharedRuntime.cpp scopeDesc.hpp +sharedRuntime.cpp sharedRuntime.hpp +sharedRuntime.cpp stubRoutines.hpp +sharedRuntime.cpp systemDictionary.hpp +sharedRuntime.cpp universe.inline.hpp +sharedRuntime.cpp vframe.hpp +sharedRuntime.cpp vframeArray.hpp +sharedRuntime.cpp vmSymbols.hpp +sharedRuntime.cpp vmreg_.inline.hpp +sharedRuntime.cpp vtableStubs.hpp +sharedRuntime.cpp vtune.hpp +sharedRuntime.cpp xmlstream.hpp + +sharedRuntime.hpp allocation.hpp +sharedRuntime.hpp bytecodeHistogram.hpp +sharedRuntime.hpp bytecodeTracer.hpp +sharedRuntime.hpp linkResolver.hpp +sharedRuntime.hpp resourceArea.hpp +sharedRuntime.hpp threadLocalStorage.hpp + +sharedRuntime_.cpp assembler.hpp +sharedRuntime_.cpp assembler_.inline.hpp +sharedRuntime_.cpp compiledICHolderOop.hpp +sharedRuntime_.cpp debugInfoRec.hpp +sharedRuntime_.cpp icBuffer.hpp +sharedRuntime_.cpp interpreter.hpp +sharedRuntime_.cpp sharedRuntime.hpp +sharedRuntime_.cpp vframeArray.hpp +sharedRuntime_.cpp vmreg_.inline.hpp +sharedRuntime_.cpp vtableStubs.hpp + +sharedRuntimeTrans.cpp interfaceSupport.hpp +sharedRuntimeTrans.cpp jni.h +sharedRuntimeTrans.cpp sharedRuntime.hpp + +sharedRuntimeTrig.cpp interfaceSupport.hpp +sharedRuntimeTrig.cpp jni.h +sharedRuntimeTrig.cpp sharedRuntime.hpp + +signature.cpp instanceKlass.hpp +signature.cpp oop.inline.hpp +signature.cpp oopFactory.hpp +signature.cpp signature.hpp +signature.cpp symbolOop.hpp +signature.cpp symbolTable.hpp +signature.cpp systemDictionary.hpp +signature.cpp typeArrayKlass.hpp + +signature.hpp allocation.hpp +signature.hpp methodOop.hpp +signature.hpp top.hpp + +sizes.cpp sizes.hpp + +sizes.hpp allocation.hpp +sizes.hpp globalDefinitions.hpp + +space.cpp blockOffsetTable.hpp +space.cpp copy.hpp +space.cpp defNewGeneration.hpp +space.cpp genCollectedHeap.hpp +space.cpp globalDefinitions.hpp +space.cpp java.hpp +space.cpp liveRange.hpp +space.cpp markSweep.hpp +space.cpp oop.inline.hpp +space.cpp oop.inline2.hpp +space.cpp safepoint.hpp +space.cpp space.hpp +space.cpp space.inline.hpp +space.cpp systemDictionary.hpp +space.cpp universe.inline.hpp +space.cpp vmSymbols.hpp + +space.hpp allocation.hpp +space.hpp blockOffsetTable.hpp +space.hpp cardTableModRefBS.hpp +space.hpp iterator.hpp +space.hpp markOop.hpp +space.hpp memRegion.hpp +space.hpp mutexLocker.hpp +space.hpp os_.inline.hpp +space.hpp prefetch.hpp +space.hpp watermark.hpp +space.hpp workgroup.hpp + +space.inline.hpp blockOffsetTable.inline.hpp +space.inline.hpp collectedHeap.hpp +space.inline.hpp safepoint.hpp +space.inline.hpp space.hpp +space.inline.hpp universe.hpp + +specialized_oop_closures.cpp ostream.hpp +specialized_oop_closures.cpp specialized_oop_closures.hpp + +stackMapFrame.cpp globalDefinitions.hpp +stackMapFrame.cpp handles.inline.hpp +stackMapFrame.cpp oop.inline.hpp +stackMapFrame.cpp resourceArea.hpp +stackMapFrame.cpp stackMapFrame.hpp +stackMapFrame.cpp symbolOop.hpp +stackMapFrame.cpp verifier.hpp + +stackMapFrame.hpp exceptions.hpp +stackMapFrame.hpp handles.hpp +stackMapFrame.hpp methodOop.hpp +stackMapFrame.hpp signature.hpp +stackMapFrame.hpp verificationType.hpp +stackMapFrame.hpp verifier.hpp + +stackMapTable.cpp fieldType.hpp +stackMapTable.cpp handles.inline.hpp +stackMapTable.cpp oop.inline.hpp +stackMapTable.cpp resourceArea.hpp +stackMapTable.cpp stackMapTable.hpp +stackMapTable.cpp verifier.hpp + +stackMapTable.hpp allocation.hpp +stackMapTable.hpp bytes_.hpp +stackMapTable.hpp constantPoolOop.hpp +stackMapTable.hpp globalDefinitions.hpp +stackMapTable.hpp methodOop.hpp +stackMapTable.hpp stackMapFrame.hpp + +stackValue.cpp debugInfo.hpp +stackValue.cpp frame.inline.hpp +stackValue.cpp handles.inline.hpp +stackValue.cpp oop.hpp +stackValue.cpp stackValue.hpp + +stackValue.hpp handles.hpp +stackValue.hpp location.hpp +stackValue.hpp top.hpp + +stackValueCollection.cpp jniTypes_.hpp +stackValueCollection.cpp stackValueCollection.hpp + +stackValueCollection.hpp allocation.hpp +stackValueCollection.hpp growableArray.hpp +stackValueCollection.hpp stackValue.hpp + +statSampler.cpp allocation.inline.hpp +statSampler.cpp arguments.hpp +statSampler.cpp java.hpp +statSampler.cpp javaCalls.hpp +statSampler.cpp oop.inline.hpp +statSampler.cpp os.hpp +statSampler.cpp resourceArea.hpp +statSampler.cpp statSampler.hpp +statSampler.cpp systemDictionary.hpp +statSampler.cpp vmSymbols.hpp +statSampler.cpp vm_version_.hpp + +statSampler.hpp perfData.hpp +statSampler.hpp task.hpp + +stubCodeGenerator.cpp assembler_.inline.hpp +stubCodeGenerator.cpp disassembler_.hpp +stubCodeGenerator.cpp forte.hpp +stubCodeGenerator.cpp oop.inline.hpp +stubCodeGenerator.cpp stubCodeGenerator.hpp +stubCodeGenerator.cpp vtune.hpp + +stubCodeGenerator.hpp allocation.hpp +stubCodeGenerator.hpp assembler.hpp + +stubGenerator_.cpp assembler.hpp +stubGenerator_.cpp assembler_.inline.hpp +stubGenerator_.cpp frame.inline.hpp +stubGenerator_.cpp handles.inline.hpp +stubGenerator_.cpp instanceOop.hpp +stubGenerator_.cpp interpreter.hpp +stubGenerator_.cpp methodOop.hpp +stubGenerator_.cpp nativeInst_.hpp +stubGenerator_.cpp objArrayKlass.hpp +stubGenerator_.cpp oop.inline.hpp +stubGenerator_.cpp sharedRuntime.hpp +stubGenerator_.cpp stubCodeGenerator.hpp +stubGenerator_.cpp stubRoutines.hpp +stubGenerator_.cpp thread_.inline.hpp +stubGenerator_.cpp top.hpp + +stubRoutines.cpp codeBuffer.hpp +stubRoutines.cpp copy.hpp +stubRoutines.cpp interfaceSupport.hpp +stubRoutines.cpp oop.inline.hpp +stubRoutines.cpp resourceArea.hpp +stubRoutines.cpp sharedRuntime.hpp +stubRoutines.cpp stubRoutines.hpp +stubRoutines.cpp timer.hpp + +stubRoutines.hpp allocation.hpp +stubRoutines.hpp codeBlob.hpp +stubRoutines.hpp frame.hpp +stubRoutines.hpp mutexLocker.hpp +stubRoutines.hpp nativeInst_.hpp +stubRoutines.hpp stubCodeGenerator.hpp +stubRoutines.hpp top.hpp + +stubRoutines_.cpp deoptimization.hpp +stubRoutines_.cpp frame.inline.hpp +stubRoutines_.cpp stubRoutines.hpp +stubRoutines_.cpp thread_.inline.hpp + +stubRoutines_.hpp generate_platform_dependent_include + +stubRoutines_.cpp os.hpp +stubRoutines_.cpp stubRoutines.hpp + +stubs.cpp allocation.inline.hpp +stubs.cpp codeBlob.hpp +stubs.cpp mutexLocker.hpp +stubs.cpp oop.inline.hpp +stubs.cpp stubs.hpp + +stubs.hpp allocation.hpp +stubs.hpp os_.inline.hpp + +sweeper.cpp atomic.hpp +sweeper.cpp codeCache.hpp +sweeper.cpp events.hpp +sweeper.cpp methodOop.hpp +sweeper.cpp mutexLocker.hpp +sweeper.cpp nmethod.hpp +sweeper.cpp os.hpp +sweeper.cpp resourceArea.hpp +sweeper.cpp sweeper.hpp + +symbolKlass.cpp gcLocker.hpp +symbolKlass.cpp handles.inline.hpp +symbolKlass.cpp oop.inline.hpp +symbolKlass.cpp symbolKlass.hpp +symbolKlass.cpp symbolOop.hpp +symbolKlass.cpp symbolTable.hpp + +symbolKlass.hpp typeArrayKlass.hpp + +symbolOop.cpp oop.inline.hpp +symbolOop.cpp symbolOop.hpp + +symbolOop.hpp typeArrayOop.hpp +symbolOop.hpp utf8.hpp + +symbolTable.cpp collectedHeap.inline.hpp +symbolTable.cpp filemap.hpp +symbolTable.cpp gcLocker.inline.hpp +symbolTable.cpp hashtable.inline.hpp +symbolTable.cpp javaClasses.hpp +symbolTable.cpp mutexLocker.hpp +symbolTable.cpp oop.inline.hpp +symbolTable.cpp oop.inline2.hpp +symbolTable.cpp symbolKlass.hpp +symbolTable.cpp symbolTable.hpp +symbolTable.cpp systemDictionary.hpp + +symbolTable.hpp allocation.inline.hpp +symbolTable.hpp hashtable.hpp +symbolTable.hpp symbolOop.hpp + +synchronizer.cpp biasedLocking.hpp +synchronizer.cpp dtrace.hpp +synchronizer.cpp events.hpp +synchronizer.cpp handles.inline.hpp +synchronizer.cpp interfaceSupport.hpp +synchronizer.cpp markOop.hpp +synchronizer.cpp mutexLocker.hpp +synchronizer.cpp objectMonitor.hpp +synchronizer.cpp objectMonitor.inline.hpp +synchronizer.cpp oop.inline.hpp +synchronizer.cpp osThread.hpp +synchronizer.cpp os_.inline.hpp +synchronizer.cpp preserveException.hpp +synchronizer.cpp resourceArea.hpp +synchronizer.cpp stubRoutines.hpp +synchronizer.cpp synchronizer.hpp +synchronizer.cpp threadService.hpp +synchronizer.cpp thread_.inline.hpp +synchronizer.cpp vmSymbols.hpp + +synchronizer.hpp handles.hpp +synchronizer.hpp markOop.hpp +synchronizer.hpp perfData.hpp +synchronizer.hpp top.hpp + +systemDictionary.cpp biasedLocking.hpp +systemDictionary.cpp bytecodeStream.hpp +systemDictionary.cpp classLoadingService.hpp +systemDictionary.cpp dictionary.hpp +systemDictionary.cpp fieldType.hpp +systemDictionary.cpp gcLocker.hpp +systemDictionary.cpp handles.inline.hpp +systemDictionary.cpp instanceKlass.hpp +systemDictionary.cpp instanceRefKlass.hpp +systemDictionary.cpp interpreter.hpp +systemDictionary.cpp java.hpp +systemDictionary.cpp javaCalls.hpp +systemDictionary.cpp javaClasses.hpp +systemDictionary.cpp jvmtiEnvBase.hpp +systemDictionary.cpp klass.inline.hpp +systemDictionary.cpp loaderConstraints.hpp +systemDictionary.cpp methodDataOop.hpp +systemDictionary.cpp mutexLocker.hpp +systemDictionary.cpp objArrayKlass.hpp +systemDictionary.cpp oop.inline.hpp +systemDictionary.cpp oop.inline2.hpp +systemDictionary.cpp oopFactory.hpp +systemDictionary.cpp placeholders.hpp +systemDictionary.cpp resolutionErrors.hpp +systemDictionary.cpp signature.hpp +systemDictionary.cpp systemDictionary.hpp +systemDictionary.cpp typeArrayKlass.hpp +systemDictionary.cpp vmSymbols.hpp + +systemDictionary.hpp classFileStream.hpp +systemDictionary.hpp classLoader.hpp +systemDictionary.hpp hashtable.hpp +systemDictionary.hpp java.hpp +systemDictionary.hpp objArrayOop.hpp +systemDictionary.hpp reflectionUtils.hpp +systemDictionary.hpp symbolOop.hpp + +task.cpp allocation.hpp +task.cpp init.hpp +task.cpp os_.inline.hpp +task.cpp task.hpp +task.cpp thread_.inline.hpp +task.cpp timer.hpp + +task.hpp top.hpp + +taskqueue.cpp debug.hpp +taskqueue.cpp os.hpp +taskqueue.cpp taskqueue.hpp +taskqueue.cpp thread_.inline.hpp + +taskqueue.hpp allocation.hpp +taskqueue.hpp allocation.inline.hpp +taskqueue.hpp debug.hpp +taskqueue.hpp mutex.hpp +taskqueue.hpp orderAccess_.inline.hpp + +templateInterpreter.cpp interpreter.hpp +templateInterpreter.cpp interpreterGenerator.hpp +templateInterpreter.cpp interpreterRuntime.hpp +templateInterpreter.cpp templateTable.hpp + +templateInterpreter.hpp abstractInterpreter.hpp +templateInterpreter.hpp templateTable.hpp + +templateInterpreter_.cpp arguments.hpp +templateInterpreter_.cpp arrayOop.hpp +templateInterpreter_.cpp assembler.hpp +templateInterpreter_.cpp bytecodeHistogram.hpp +templateInterpreter_.cpp debug.hpp +templateInterpreter_.cpp deoptimization.hpp +templateInterpreter_.cpp frame.inline.hpp +templateInterpreter_.cpp interpreterRuntime.hpp +templateInterpreter_.cpp interpreter.hpp +templateInterpreter_.cpp interpreterGenerator.hpp +templateInterpreter_.cpp jvmtiExport.hpp +templateInterpreter_.cpp jvmtiThreadState.hpp +templateInterpreter_.cpp methodDataOop.hpp +templateInterpreter_.cpp methodOop.hpp +templateInterpreter_.cpp oop.inline.hpp +templateInterpreter_.cpp sharedRuntime.hpp +templateInterpreter_.cpp stubRoutines.hpp +templateInterpreter_.cpp synchronizer.hpp +templateInterpreter_.cpp templateTable.hpp +templateInterpreter_.cpp timer.hpp +templateInterpreter_.cpp vframeArray.hpp + +templateInterpreter_.hpp generate_platform_dependent_include + +templateInterpreterGenerator_.hpp generate_platform_dependent_include + +templateTable.cpp templateTable.hpp +templateTable.cpp timer.hpp + +templateTable.hpp allocation.hpp +templateTable.hpp bytecodes.hpp +templateTable.hpp frame.hpp +templateTable.hpp interp_masm_.hpp + +templateTable_.cpp interpreterRuntime.hpp +templateTable_.cpp interpreter.hpp +templateTable_.cpp methodDataOop.hpp +templateTable_.cpp objArrayKlass.hpp +templateTable_.cpp oop.inline.hpp +templateTable_.cpp sharedRuntime.hpp +templateTable_.cpp stubRoutines.hpp +templateTable_.cpp synchronizer.hpp +templateTable_.cpp templateTable.hpp +templateTable_.cpp universe.inline.hpp + +templateTable_.hpp generate_platform_dependent_include + +tenuredGeneration.cpp allocation.inline.hpp +tenuredGeneration.cpp blockOffsetTable.inline.hpp +tenuredGeneration.cpp collectorCounters.hpp +tenuredGeneration.cpp generation.inline.hpp +tenuredGeneration.cpp generationSpec.hpp +tenuredGeneration.cpp java.hpp +tenuredGeneration.cpp oop.inline.hpp +tenuredGeneration.cpp parGCAllocBuffer.hpp +tenuredGeneration.cpp space.hpp +tenuredGeneration.cpp tenuredGeneration.hpp + +tenuredGeneration.hpp cSpaceCounters.hpp +tenuredGeneration.hpp gcStats.hpp +tenuredGeneration.hpp generation.hpp +tenuredGeneration.hpp generationCounters.hpp + +thread.cpp aprofiler.hpp +thread.cpp arguments.hpp +thread.cpp attachListener.hpp +thread.cpp biasedLocking.hpp +thread.cpp classLoader.hpp +thread.cpp compileBroker.hpp +thread.cpp defaultStream.hpp +thread.cpp deoptimization.hpp +thread.cpp dtrace.hpp +thread.cpp events.hpp +thread.cpp fprofiler.hpp +thread.cpp frame.inline.hpp +thread.cpp gcTaskManager.hpp +thread.cpp hpi.hpp +thread.cpp init.hpp +thread.cpp instanceKlass.hpp +thread.cpp interfaceSupport.hpp +thread.cpp interpreter.hpp +thread.cpp interpreter.hpp +thread.cpp java.hpp +thread.cpp javaCalls.hpp +thread.cpp javaClasses.hpp +thread.cpp jniPeriodicChecker.hpp +thread.cpp jvm_misc.hpp +thread.cpp jvmtiExport.hpp +thread.cpp jvmtiThreadState.hpp +thread.cpp linkResolver.hpp +thread.cpp management.hpp +thread.cpp memprofiler.hpp +thread.cpp mutexLocker.hpp +thread.cpp objArrayOop.hpp +thread.cpp objectMonitor.hpp +thread.cpp objectMonitor.inline.hpp +thread.cpp oop.inline.hpp +thread.cpp oopFactory.hpp +thread.cpp osThread.hpp +thread.cpp os_.inline.hpp +thread.cpp preserveException.hpp +thread.cpp privilegedStack.hpp +thread.cpp safepoint.hpp +thread.cpp scopeDesc.hpp +thread.cpp sharedRuntime.hpp +thread.cpp statSampler.hpp +thread.cpp stubRoutines.hpp +thread.cpp symbolOop.hpp +thread.cpp systemDictionary.hpp +thread.cpp task.hpp +thread.cpp threadCritical.hpp +thread.cpp threadLocalStorage.hpp +thread.cpp threadService.hpp +thread.cpp thread_.inline.hpp +thread.cpp universe.inline.hpp +thread.cpp vframe.hpp +thread.cpp vframeArray.hpp +thread.cpp vframe_hp.hpp +thread.cpp vmSymbols.hpp +thread.cpp vmThread.hpp +thread.cpp vm_operations.hpp + +thread.hpp allocation.hpp +thread.hpp exceptions.hpp +thread.hpp frame.hpp +thread.hpp javaFrameAnchor.hpp +thread.hpp jni.h +thread.hpp jniHandles.hpp +thread.hpp jvmtiExport.hpp +thread.hpp mutexLocker.hpp +thread.hpp oop.hpp +thread.hpp os.hpp +thread.hpp osThread.hpp +thread.hpp safepoint.hpp +thread.hpp stubRoutines.hpp +thread.hpp threadLocalAllocBuffer.hpp +thread.hpp threadLocalStorage.hpp +thread.hpp top.hpp +thread.hpp unhandledOops.hpp + +thread_.cpp frame.inline.hpp +thread_.cpp thread_.inline.hpp + +thread_.hpp generate_platform_dependent_include + +thread_.inline.hpp atomic.hpp +thread_.inline.hpp atomic_.inline.hpp +thread_.inline.hpp orderAccess_.inline.hpp +thread_.inline.hpp prefetch.hpp +thread_.inline.hpp prefetch_.inline.hpp +thread_.inline.hpp thread.hpp +thread_.inline.hpp threadLocalStorage.hpp + +threadCritical.hpp allocation.hpp + +threadCritical_.cpp threadCritical.hpp +threadCritical_.cpp thread_.inline.hpp + +threadLS_.cpp threadLocalStorage.hpp +threadLS_.cpp thread_.inline.hpp + +threadLS_.hpp generate_platform_dependent_include + +threadLocalAllocBuffer.cpp copy.hpp +threadLocalAllocBuffer.cpp genCollectedHeap.hpp +threadLocalAllocBuffer.cpp oop.inline.hpp +threadLocalAllocBuffer.cpp resourceArea.hpp +threadLocalAllocBuffer.cpp threadLocalAllocBuffer.inline.hpp +threadLocalAllocBuffer.cpp thread_.inline.hpp +threadLocalAllocBuffer.cpp universe.inline.hpp + +threadLocalAllocBuffer.hpp gcUtil.hpp +threadLocalAllocBuffer.hpp perfData.hpp +threadLocalAllocBuffer.hpp typeArrayOop.hpp + +threadLocalAllocBuffer.inline.hpp atomic.hpp +threadLocalAllocBuffer.inline.hpp collectedHeap.hpp +threadLocalAllocBuffer.inline.hpp copy.hpp +threadLocalAllocBuffer.inline.hpp threadLocalAllocBuffer.hpp + +threadLocalStorage.cpp os_.inline.hpp +threadLocalStorage.cpp threadLocalStorage.hpp +threadLocalStorage.cpp thread_.inline.hpp + +threadLocalStorage.hpp gcUtil.hpp +threadLocalStorage.hpp os.hpp +threadLocalStorage.hpp top.hpp + +threadService.cpp allocation.hpp +threadService.cpp handles.inline.hpp +threadService.cpp heapInspection.hpp +threadService.cpp init.hpp +threadService.cpp instanceKlass.hpp +threadService.cpp oop.inline.hpp +threadService.cpp oopFactory.hpp +threadService.cpp systemDictionary.hpp +threadService.cpp thread.hpp +threadService.cpp threadService.hpp +threadService.cpp vframe.hpp +threadService.cpp vmThread.hpp +threadService.cpp vm_operations.hpp + +threadService.hpp handles.hpp +threadService.hpp init.hpp +threadService.hpp javaClasses.hpp +threadService.hpp jniHandles.hpp +threadService.hpp management.hpp +threadService.hpp objectMonitor.hpp +threadService.hpp objectMonitor.inline.hpp +threadService.hpp perfData.hpp +threadService.hpp serviceUtil.hpp + +timer.cpp oop.inline.hpp +timer.cpp os_.inline.hpp +timer.cpp ostream.hpp +timer.cpp timer.hpp + +timer.hpp globalDefinitions.hpp + +top.hpp debug.hpp +top.hpp exceptions.hpp +top.hpp globalDefinitions.hpp +top.hpp globals.hpp +top.hpp macros.hpp +top.hpp oopsHierarchy.hpp +top.hpp ostream.hpp +top.hpp sizes.hpp + +typeArrayKlass.cpp collectedHeap.hpp +typeArrayKlass.cpp collectedHeap.inline.hpp +typeArrayKlass.cpp handles.inline.hpp +typeArrayKlass.cpp instanceKlass.hpp +typeArrayKlass.cpp klassOop.hpp +typeArrayKlass.cpp objArrayKlassKlass.hpp +typeArrayKlass.cpp oop.inline.hpp +typeArrayKlass.cpp resourceArea.hpp +typeArrayKlass.cpp systemDictionary.hpp +typeArrayKlass.cpp typeArrayKlass.hpp +typeArrayKlass.cpp typeArrayOop.hpp +typeArrayKlass.cpp universe.hpp +typeArrayKlass.cpp universe.inline.hpp +typeArrayKlass.cpp vmSymbols.hpp + +typeArrayKlass.hpp arrayKlass.hpp + +typeArrayKlassKlass.cpp handles.inline.hpp +typeArrayKlassKlass.cpp javaClasses.hpp +typeArrayKlassKlass.cpp oop.inline.hpp +typeArrayKlassKlass.cpp typeArrayKlassKlass.hpp + +typeArrayKlassKlass.hpp arrayKlassKlass.hpp +typeArrayKlassKlass.hpp typeArrayKlass.hpp + +typeArrayOop.cpp oop.inline.hpp +typeArrayOop.cpp typeArrayOop.hpp + +typeArrayOop.hpp arrayOop.hpp +typeArrayOop.hpp orderAccess_.inline.hpp +typeArrayOop.hpp typeArrayKlass.hpp + +unhandledOops.cpp collectedHeap.hpp +unhandledOops.cpp gcLocker.inline.hpp +unhandledOops.cpp globalDefinitions.hpp +unhandledOops.cpp oop.hpp +unhandledOops.cpp oop.inline.hpp +unhandledOops.cpp thread.hpp +unhandledOops.cpp unhandledOops.hpp +unhandledOops.cpp universe.hpp + +universe.cpp aprofiler.hpp +universe.cpp arguments.hpp +universe.cpp arrayKlassKlass.hpp +universe.cpp cardTableModRefBS.hpp +universe.cpp classLoader.hpp +universe.cpp codeCache.hpp +universe.cpp collectedHeap.inline.hpp +universe.cpp compiledICHolderKlass.hpp +universe.cpp constMethodKlass.hpp +universe.cpp constantPoolKlass.hpp +universe.cpp constantPoolOop.hpp +universe.cpp copy.hpp +universe.cpp cpCacheKlass.hpp +universe.cpp cpCacheOop.hpp +universe.cpp deoptimization.hpp +universe.cpp dependencies.hpp +universe.cpp events.hpp +universe.cpp filemap.hpp +universe.cpp fprofiler.hpp +universe.cpp gcLocker.inline.hpp +universe.cpp genCollectedHeap.hpp +universe.cpp genRemSet.hpp +universe.cpp generation.hpp +universe.cpp handles.inline.hpp +universe.cpp hashtable.inline.hpp +universe.cpp instanceKlass.hpp +universe.cpp instanceKlassKlass.hpp +universe.cpp instanceRefKlass.hpp +universe.cpp interpreter.hpp +universe.cpp java.hpp +universe.cpp javaCalls.hpp +universe.cpp javaClasses.hpp +universe.cpp jvmtiRedefineClassesTrace.hpp +universe.cpp klassKlass.hpp +universe.cpp klassOop.hpp +universe.cpp memoryService.hpp +universe.cpp methodDataKlass.hpp +universe.cpp methodKlass.hpp +universe.cpp objArrayKlassKlass.hpp +universe.cpp oop.inline.hpp +universe.cpp oopFactory.hpp +universe.cpp permGen.hpp +universe.cpp preserveException.hpp +universe.cpp sharedRuntime.hpp +universe.cpp space.hpp +universe.cpp symbolKlass.hpp +universe.cpp symbolTable.hpp +universe.cpp synchronizer.hpp +universe.cpp systemDictionary.hpp +universe.cpp thread_.inline.hpp +universe.cpp timer.hpp +universe.cpp typeArrayKlass.hpp +universe.cpp typeArrayKlassKlass.hpp +universe.cpp universe.hpp +universe.cpp universe.inline.hpp +universe.cpp vmSymbols.hpp +universe.cpp vm_operations.hpp +universe.cpp vtune.hpp + +universe.hpp growableArray.hpp +universe.hpp handles.hpp + +universe.inline.hpp universe.hpp + +unsafe.cpp allocation.inline.hpp +unsafe.cpp copy.hpp +unsafe.cpp globals.hpp +unsafe.cpp interfaceSupport.hpp +unsafe.cpp jni.h +unsafe.cpp jvm.h +unsafe.cpp reflection.hpp +unsafe.cpp reflectionCompat.hpp +unsafe.cpp synchronizer.hpp +unsafe.cpp threadService.hpp +unsafe.cpp vmSymbols.hpp + +utf8.cpp utf8.hpp + +utf8.hpp allocation.hpp +utf8.hpp top.hpp + +verificationType.cpp symbolTable.hpp +verificationType.cpp verificationType.hpp + +verificationType.hpp allocation.hpp +verificationType.hpp handles.hpp +verificationType.hpp instanceKlass.hpp +verificationType.hpp oop.inline.hpp +verificationType.hpp signature.hpp +verificationType.hpp symbolOop.hpp +verificationType.hpp systemDictionary.hpp + +verifier.cpp bytecodeStream.hpp +verifier.cpp bytes_.hpp +verifier.cpp classFileStream.hpp +verifier.cpp fieldDescriptor.hpp +verifier.cpp handles.inline.hpp +verifier.cpp hpi.hpp +verifier.cpp instanceKlass.hpp +verifier.cpp interfaceSupport.hpp +verifier.cpp javaCalls.hpp +verifier.cpp javaClasses.hpp +verifier.cpp jvm.h +verifier.cpp oop.inline.hpp +verifier.cpp oopFactory.hpp +verifier.cpp orderAccess.hpp +verifier.cpp os.hpp +verifier.cpp resourceArea.hpp +verifier.cpp stackMapTable.hpp +verifier.cpp systemDictionary.hpp +verifier.cpp typeArrayOop.hpp +verifier.cpp verifier.hpp +verifier.cpp vmSymbols.hpp + +verifier.hpp exceptions.hpp +verifier.hpp gcLocker.hpp +verifier.hpp handles.hpp +verifier.hpp klass.hpp +verifier.hpp methodOop.hpp +verifier.hpp verificationType.hpp + +vframe.cpp codeCache.hpp +vframe.cpp debugInfoRec.hpp +vframe.cpp handles.inline.hpp +vframe.cpp instanceKlass.hpp +vframe.cpp interpreter.hpp +vframe.cpp javaClasses.hpp +vframe.cpp nmethod.hpp +vframe.cpp objectMonitor.hpp +vframe.cpp objectMonitor.inline.hpp +vframe.cpp oop.hpp +vframe.cpp oop.inline.hpp +vframe.cpp oopMapCache.hpp +vframe.cpp pcDesc.hpp +vframe.cpp resourceArea.hpp +vframe.cpp scopeDesc.hpp +vframe.cpp signature.hpp +vframe.cpp stubRoutines.hpp +vframe.cpp synchronizer.hpp +vframe.cpp systemDictionary.hpp +vframe.cpp vframe.hpp +vframe.cpp vframeArray.hpp +vframe.cpp vframe_hp.hpp +vframe.cpp vmSymbols.hpp + +vframe.hpp debugInfo.hpp +vframe.hpp debugInfoRec.hpp +vframe.hpp frame.hpp +vframe.hpp frame.inline.hpp +vframe.hpp growableArray.hpp +vframe.hpp location.hpp +vframe.hpp oop.hpp +vframe.hpp stackValue.hpp +vframe.hpp stackValueCollection.hpp + +vframeArray.cpp allocation.inline.hpp +vframeArray.cpp events.hpp +vframeArray.cpp handles.inline.hpp +vframeArray.cpp interpreter.hpp +vframeArray.cpp jvmtiThreadState.hpp +vframeArray.cpp methodDataOop.hpp +vframeArray.cpp monitorChunk.hpp +vframeArray.cpp oop.inline.hpp +vframeArray.cpp resourceArea.hpp +vframeArray.cpp sharedRuntime.hpp +vframeArray.cpp universe.inline.hpp +vframeArray.cpp vframe.hpp +vframeArray.cpp vframeArray.hpp +vframeArray.cpp vframe_hp.hpp +vframeArray.cpp vmSymbols.hpp + +vframeArray.hpp arrayOop.hpp +vframeArray.hpp deoptimization.hpp +vframeArray.hpp frame.inline.hpp +vframeArray.hpp growableArray.hpp +vframeArray.hpp monitorChunk.hpp + +vframe_hp.cpp codeCache.hpp +vframe_hp.cpp debugInfoRec.hpp +vframe_hp.cpp handles.inline.hpp +vframe_hp.cpp instanceKlass.hpp +vframe_hp.cpp interpreter.hpp +vframe_hp.cpp monitorChunk.hpp +vframe_hp.cpp nmethod.hpp +vframe_hp.cpp oop.inline.hpp +vframe_hp.cpp oopMapCache.hpp +vframe_hp.cpp pcDesc.hpp +vframe_hp.cpp scopeDesc.hpp +vframe_hp.cpp signature.hpp +vframe_hp.cpp stubRoutines.hpp +vframe_hp.cpp synchronizer.hpp +vframe_hp.cpp vframeArray.hpp +vframe_hp.cpp vframe_hp.hpp + +vframe_hp.hpp vframe.hpp + +virtualspace.cpp markOop.hpp +virtualspace.cpp oop.inline.hpp +virtualspace.cpp os_.inline.hpp +virtualspace.cpp virtualspace.hpp + +virtualspace.hpp allocation.hpp + +vmError.cpp arguments.hpp +vmError.cpp collectedHeap.hpp +vmError.cpp compileBroker.hpp +vmError.cpp debug.hpp +vmError.cpp defaultStream.hpp +vmError.cpp frame.inline.hpp +vmError.cpp init.hpp +vmError.cpp os.hpp +vmError.cpp thread.hpp +vmError.cpp top.hpp +vmError.cpp vmError.hpp +vmError.cpp vmThread.hpp +vmError.cpp vm_operations.hpp + +vmError.hpp globalDefinitions.hpp + +vmError_.cpp arguments.hpp +vmError_.cpp os.hpp +vmError_.cpp thread.hpp +vmError_.cpp vmError.hpp + +// vmStructs is jck optional, put cpp deps in includeDB_features + +vmStructs.hpp debug.hpp + +vmSymbols.cpp handles.inline.hpp +vmSymbols.cpp oop.inline.hpp +vmSymbols.cpp oopFactory.hpp +vmSymbols.cpp vmSymbols.hpp +vmSymbols.cpp xmlstream.hpp + +vmSymbols.hpp symbolOop.hpp + +vmThread.cpp collectedHeap.hpp +vmThread.cpp compileBroker.hpp +vmThread.cpp events.hpp +vmThread.cpp interfaceSupport.hpp +vmThread.cpp methodOop.hpp +vmThread.cpp mutexLocker.hpp +vmThread.cpp oop.hpp +vmThread.cpp oop.inline.hpp +vmThread.cpp os.hpp +vmThread.cpp resourceArea.hpp +vmThread.cpp runtimeService.hpp +vmThread.cpp thread_.inline.hpp +vmThread.cpp vmThread.hpp +vmThread.cpp vm_operations.hpp +vmThread.cpp xmlstream.hpp + +vmThread.hpp perfData.hpp +vmThread.hpp thread_.inline.hpp +vmThread.hpp vm_operations.hpp + +vm_operations.cpp arguments.hpp +vm_operations.cpp compileBroker.hpp +vm_operations.cpp compilerOracle.hpp +vm_operations.cpp deoptimization.hpp +vm_operations.cpp interfaceSupport.hpp +vm_operations.cpp resourceArea.hpp +vm_operations.cpp threadService.hpp +vm_operations.cpp thread_.inline.hpp +vm_operations.cpp vmSymbols.hpp +vm_operations.cpp vm_operations.hpp + +vm_operations.hpp allocation.hpp +vm_operations.hpp javaClasses.hpp +vm_operations.hpp oop.hpp +vm_operations.hpp thread.hpp +vm_operations.hpp top.hpp + +vm_version.cpp arguments.hpp +vm_version.cpp oop.inline.hpp +vm_version.cpp universe.hpp +vm_version.cpp vm_version_.hpp + +vm_version.hpp allocation.hpp +vm_version.hpp ostream.hpp + +vm_version_.cpp assembler_.inline.hpp +vm_version_.cpp java.hpp +vm_version_.cpp os_.inline.hpp +vm_version_.cpp resourceArea.hpp +vm_version_.cpp stubCodeGenerator.hpp +vm_version_.cpp vm_version_.hpp + +vm_version_.hpp globals_extension.hpp +vm_version_.hpp vm_version.hpp + +vm_version_.cpp vm_version_.hpp + +vmreg.cpp assembler.hpp +vmreg.cpp vmreg.hpp + +vmreg.hpp allocation.hpp +vmreg.hpp globalDefinitions.hpp +vmreg.hpp register_.hpp + +vmreg_.cpp assembler.hpp +vmreg_.cpp vmreg.hpp + +vmreg_.hpp generate_platform_dependent_include + +vtableStubs.cpp allocation.inline.hpp +vtableStubs.cpp disassembler_.hpp +vtableStubs.cpp forte.hpp +vtableStubs.cpp handles.inline.hpp +vtableStubs.cpp instanceKlass.hpp +vtableStubs.cpp jvmtiExport.hpp +vtableStubs.cpp klassVtable.hpp +vtableStubs.cpp mutexLocker.hpp +vtableStubs.cpp resourceArea.hpp +vtableStubs.cpp sharedRuntime.hpp +vtableStubs.cpp vtableStubs.hpp +vtableStubs.cpp vtune.hpp + +vtableStubs.hpp allocation.hpp + +vtableStubs_.cpp assembler.hpp +vtableStubs_.cpp assembler_.inline.hpp +vtableStubs_.cpp instanceKlass.hpp +vtableStubs_.cpp interp_masm_.hpp +vtableStubs_.cpp klassVtable.hpp +vtableStubs_.cpp resourceArea.hpp +vtableStubs_.cpp sharedRuntime.hpp +vtableStubs_.cpp vmreg_.inline.hpp +vtableStubs_.cpp vtableStubs.hpp + +vtune.hpp allocation.hpp + +vtune_.cpp interpreter.hpp +vtune_.cpp vtune.hpp + +watermark.hpp allocation.hpp +watermark.hpp globalDefinitions.hpp + +workgroup.cpp allocation.hpp +workgroup.cpp allocation.inline.hpp +workgroup.cpp os.hpp +workgroup.cpp workgroup.hpp + +workgroup.hpp thread_.inline.hpp + +xmlstream.cpp allocation.hpp +xmlstream.cpp allocation.inline.hpp +xmlstream.cpp deoptimization.hpp +xmlstream.cpp methodDataOop.hpp +xmlstream.cpp methodOop.hpp +xmlstream.cpp nmethod.hpp +xmlstream.cpp oop.inline.hpp +xmlstream.cpp vmThread.hpp +xmlstream.cpp xmlstream.hpp + +xmlstream.hpp handles.hpp +xmlstream.hpp ostream.hpp diff --git a/hotspot/src/share/vm/includeDB_features b/hotspot/src/share/vm/includeDB_features new file mode 100644 index 00000000000..eec1c35259e --- /dev/null +++ b/hotspot/src/share/vm/includeDB_features @@ -0,0 +1,319 @@ +// +// Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +attachListener.cpp arguments.hpp +attachListener.cpp attachListener.hpp +attachListener.cpp globals.hpp +attachListener.cpp heapDumper.hpp +attachListener.cpp java.hpp +attachListener.cpp javaCalls.hpp +attachListener.cpp javaClasses.hpp +attachListener.cpp jvmtiExport.hpp +attachListener.cpp os.hpp +attachListener.cpp resourceArea.hpp +attachListener.cpp systemDictionary.hpp +attachListener.cpp vmGCOperations.hpp + +attachListener_.cpp attachListener.hpp +attachListener_.cpp dtraceAttacher.hpp +attachListener_.cpp interfaceSupport.hpp +attachListener_.cpp os.hpp + +dump.cpp classify.hpp +dump.cpp copy.hpp +dump.cpp filemap.hpp +dump.cpp javaCalls.hpp +dump.cpp javaClasses.hpp +dump.cpp loaderConstraints.hpp +dump.cpp methodDataOop.hpp +dump.cpp oop.hpp +dump.cpp oopFactory.hpp +dump.cpp resourceArea.hpp +dump.cpp signature.hpp +dump.cpp symbolTable.hpp +dump.cpp systemDictionary.hpp +dump.cpp vmThread.hpp +dump.cpp vm_operations.hpp + +dump_.cpp assembler_.inline.hpp +dump_.cpp compactingPermGenGen.hpp + +forte.cpp collectedHeap.inline.hpp +forte.cpp debugInfoRec.hpp +forte.cpp forte.hpp +forte.cpp oop.inline.hpp +forte.cpp oop.inline2.hpp +forte.cpp pcDesc.hpp +forte.cpp space.hpp +forte.cpp thread.hpp +forte.cpp universe.inline.hpp +forte.cpp vframe.hpp +forte.cpp vframeArray.hpp + +fprofiler.cpp allocation.inline.hpp +fprofiler.cpp classLoader.hpp +fprofiler.cpp collectedHeap.inline.hpp +fprofiler.cpp deoptimization.hpp +fprofiler.cpp fprofiler.hpp +fprofiler.cpp interpreter.hpp +fprofiler.cpp macros.hpp +fprofiler.cpp mutexLocker.hpp +fprofiler.cpp oop.inline.hpp +fprofiler.cpp oop.inline2.hpp +fprofiler.cpp stubCodeGenerator.hpp +fprofiler.cpp stubRoutines.hpp +fprofiler.cpp symbolOop.hpp +fprofiler.cpp task.hpp +fprofiler.cpp universe.inline.hpp +fprofiler.cpp vframe.hpp +fprofiler.cpp vtableStubs.hpp + +heapDumper.cpp genCollectedHeap.hpp +heapDumper.cpp heapDumper.hpp +heapDumper.cpp javaCalls.hpp +heapDumper.cpp jniHandles.hpp +heapDumper.cpp objArrayKlass.hpp +heapDumper.cpp ostream.hpp +heapDumper.cpp reflectionUtils.hpp +heapDumper.cpp symbolTable.hpp +heapDumper.cpp systemDictionary.hpp +heapDumper.cpp universe.hpp +heapDumper.cpp vframe.hpp +heapDumper.cpp vmGCOperations.hpp +heapDumper.cpp vmSymbols.hpp +heapDumper.cpp vmThread.hpp +heapDumper.cpp vm_operations.hpp + +heapInspection.cpp collectedHeap.hpp +heapInspection.cpp genCollectedHeap.hpp +heapInspection.cpp globalDefinitions.hpp +heapInspection.cpp heapInspection.hpp +heapInspection.cpp klassOop.hpp +heapInspection.cpp os.hpp +heapInspection.cpp resourceArea.hpp + +jniCheck.cpp fieldDescriptor.hpp +jniCheck.cpp handles.hpp +jniCheck.cpp instanceKlass.hpp +jniCheck.cpp interfaceSupport.hpp +jniCheck.cpp jfieldIDWorkaround.hpp +jniCheck.cpp jni.h +jniCheck.cpp jniCheck.hpp +jniCheck.cpp jniTypes_.hpp +jniCheck.cpp jvm_misc.hpp +jniCheck.cpp oop.inline.hpp +jniCheck.cpp symbolOop.hpp +jniCheck.cpp systemDictionary.hpp +jniCheck.cpp thread.hpp +jniCheck.cpp vmSymbols.hpp + +jvmtiCodeBlobEvents.cpp codeBlob.hpp +jvmtiCodeBlobEvents.cpp codeCache.hpp +jvmtiCodeBlobEvents.cpp handles.hpp +jvmtiCodeBlobEvents.cpp handles.inline.hpp +jvmtiCodeBlobEvents.cpp jvmtiCodeBlobEvents.hpp +jvmtiCodeBlobEvents.cpp jvmtiExport.hpp +jvmtiCodeBlobEvents.cpp oop.inline.hpp +jvmtiCodeBlobEvents.cpp resourceArea.hpp +jvmtiCodeBlobEvents.cpp scopeDesc.hpp +jvmtiCodeBlobEvents.cpp vmThread.hpp + +jvmtiCodeBlobEvents.hpp jvmti.h + +jvmtiExtensions.cpp jvmtiExport.hpp +jvmtiExtensions.cpp jvmtiExtensions.hpp + +jvmtiExtensions.hpp allocation.hpp +jvmtiExtensions.hpp jvmti.h +jvmtiExtensions.hpp jvmtiEnv.hpp + +jvmtiImpl.cpp exceptions.hpp +jvmtiImpl.cpp handles.hpp +jvmtiImpl.cpp handles.inline.hpp +jvmtiImpl.cpp instanceKlass.hpp +jvmtiImpl.cpp interfaceSupport.hpp +jvmtiImpl.cpp interpreter.hpp +jvmtiImpl.cpp javaCalls.hpp +jvmtiImpl.cpp jvmtiAgentThread.hpp +jvmtiImpl.cpp jvmtiEnv.hpp +jvmtiImpl.cpp jvmtiEventController.inline.hpp +jvmtiImpl.cpp jvmtiImpl.hpp +jvmtiImpl.cpp jvmtiRedefineClasses.hpp +jvmtiImpl.cpp resourceArea.hpp +jvmtiImpl.cpp signature.hpp +jvmtiImpl.cpp systemDictionary.hpp +jvmtiImpl.cpp thread_.inline.hpp +jvmtiImpl.cpp vframe.hpp +jvmtiImpl.cpp vframe_hp.hpp +jvmtiImpl.cpp vm_operations.hpp + +jvmtiImpl.hpp jvmti.h +jvmtiImpl.hpp jvmtiEnvThreadState.hpp +jvmtiImpl.hpp jvmtiEventController.hpp +jvmtiImpl.hpp jvmtiTrace.hpp +jvmtiImpl.hpp jvmtiUtil.hpp +jvmtiImpl.hpp objArrayOop.hpp +jvmtiImpl.hpp stackValueCollection.hpp +jvmtiImpl.hpp systemDictionary.hpp +jvmtiImpl.hpp vm_operations.hpp + +jvmtiTagMap.cpp biasedLocking.hpp +jvmtiTagMap.cpp javaCalls.hpp +jvmtiTagMap.cpp jniHandles.hpp +jvmtiTagMap.cpp jvmtiEnv.hpp +jvmtiTagMap.cpp jvmtiEventController.hpp +jvmtiTagMap.cpp jvmtiEventController.inline.hpp +jvmtiTagMap.cpp jvmtiExport.hpp +jvmtiTagMap.cpp jvmtiImpl.hpp +jvmtiTagMap.cpp jvmtiTagMap.hpp +jvmtiTagMap.cpp mutex.hpp +jvmtiTagMap.cpp mutexLocker.hpp +jvmtiTagMap.cpp objArrayKlass.hpp +jvmtiTagMap.cpp oop.inline2.hpp +jvmtiTagMap.cpp reflectionUtils.hpp +jvmtiTagMap.cpp serviceUtil.hpp +jvmtiTagMap.cpp symbolTable.hpp +jvmtiTagMap.cpp systemDictionary.hpp +jvmtiTagMap.cpp vframe.hpp +jvmtiTagMap.cpp vmSymbols.hpp +jvmtiTagMap.cpp vmThread.hpp +jvmtiTagMap.cpp vm_operations.hpp + +jvmtiTagMap.hpp allocation.hpp +jvmtiTagMap.hpp collectedHeap.hpp +jvmtiTagMap.hpp genCollectedHeap.hpp +jvmtiTagMap.hpp jvmti.h +jvmtiTagMap.hpp jvmtiEnv.hpp +jvmtiTagMap.hpp universe.hpp + +jvmtiTrace.cpp jvmtiEnv.hpp +jvmtiTrace.cpp jvmtiTrace.hpp + +jvmtiTrace.hpp jvmti.h +jvmtiTrace.hpp jvmtiEnvThreadState.hpp +jvmtiTrace.hpp jvmtiEventController.hpp +jvmtiTrace.hpp jvmtiUtil.hpp +jvmtiTrace.hpp objArrayOop.hpp +jvmtiTrace.hpp stackValueCollection.hpp +jvmtiTrace.hpp systemDictionary.hpp +jvmtiTrace.hpp vm_operations.hpp + +restore.cpp filemap.hpp +restore.cpp hashtable.inline.hpp +restore.cpp oop.inline.hpp +restore.cpp symbolTable.hpp +restore.cpp systemDictionary.hpp + +serialize.cpp classify.hpp +serialize.cpp codeCache.hpp +serialize.cpp compactingPermGenGen.hpp +serialize.cpp compiledICHolderOop.hpp +serialize.cpp methodDataOop.hpp +serialize.cpp objArrayOop.hpp +serialize.cpp oop.hpp +serialize.cpp symbolTable.hpp +serialize.cpp systemDictionary.hpp + +vmStructs.cpp arguments.hpp +vmStructs.cpp arrayKlass.hpp +vmStructs.cpp arrayKlassKlass.hpp +vmStructs.cpp arrayOop.hpp +vmStructs.cpp bytecodes.hpp +vmStructs.cpp bytecodeInterpreter.hpp +vmStructs.cpp cardTableRS.hpp +vmStructs.cpp codeBlob.hpp +vmStructs.cpp codeCache.hpp +vmStructs.cpp collectedHeap.hpp +vmStructs.cpp compactPermGen.hpp +vmStructs.cpp compiledICHolderKlass.hpp +vmStructs.cpp compiledICHolderOop.hpp +vmStructs.cpp compressedStream.hpp +vmStructs.cpp constMethodKlass.hpp +vmStructs.cpp constMethodOop.hpp +vmStructs.cpp constantPoolKlass.hpp +vmStructs.cpp constantPoolOop.hpp +vmStructs.cpp cpCacheKlass.hpp +vmStructs.cpp cpCacheOop.hpp +vmStructs.cpp defNewGeneration.hpp +vmStructs.cpp dictionary.hpp +vmStructs.cpp freeBlockDictionary.hpp +vmStructs.cpp genCollectedHeap.hpp +vmStructs.cpp generation.hpp +vmStructs.cpp generationSpec.hpp +vmStructs.cpp globalDefinitions.hpp +vmStructs.cpp globals.hpp +vmStructs.cpp hashtable.hpp +vmStructs.cpp heap.hpp +vmStructs.cpp immutableSpace.hpp +vmStructs.cpp instanceKlass.hpp +vmStructs.cpp instanceKlassKlass.hpp +vmStructs.cpp instanceOop.hpp +vmStructs.cpp interpreter.hpp +vmStructs.cpp java.hpp +vmStructs.cpp javaCalls.hpp +vmStructs.cpp javaClasses.hpp +vmStructs.cpp jvmtiAgentThread.hpp +vmStructs.cpp klass.hpp +vmStructs.cpp klassOop.hpp +vmStructs.cpp loaderConstraints.hpp +vmStructs.cpp location.hpp +vmStructs.cpp markOop.hpp +vmStructs.cpp markSweep.hpp +vmStructs.cpp methodDataKlass.hpp +vmStructs.cpp methodDataOop.hpp +vmStructs.cpp methodKlass.hpp +vmStructs.cpp methodOop.hpp +vmStructs.cpp mutableSpace.hpp +vmStructs.cpp nmethod.hpp +vmStructs.cpp objArrayKlass.hpp +vmStructs.cpp objArrayKlassKlass.hpp +vmStructs.cpp objArrayOop.hpp +vmStructs.cpp oop.hpp +vmStructs.cpp oopMap.hpp +vmStructs.cpp pcDesc.hpp +vmStructs.cpp perfMemory.hpp +vmStructs.cpp permGen.hpp +vmStructs.cpp placeholders.hpp +vmStructs.cpp sharedRuntime.hpp +vmStructs.cpp space.hpp +vmStructs.cpp stubRoutines.hpp +vmStructs.cpp stubs.hpp +vmStructs.cpp symbolKlass.hpp +vmStructs.cpp symbolOop.hpp +vmStructs.cpp symbolTable.hpp +vmStructs.cpp systemDictionary.hpp +vmStructs.cpp tenuredGeneration.hpp +vmStructs.cpp thread_.inline.hpp +vmStructs.cpp typeArrayKlass.hpp +vmStructs.cpp typeArrayKlassKlass.hpp +vmStructs.cpp typeArrayOop.hpp +vmStructs.cpp universe.hpp +vmStructs.cpp virtualspace.hpp +vmStructs.cpp vmStructs.hpp +vmStructs.cpp vmStructs_.hpp +vmStructs.cpp vmStructs_.hpp +vmStructs.cpp vmreg.hpp +vmStructs.cpp watermark.hpp + +vmStructs.hpp debug.hpp diff --git a/hotspot/src/share/vm/includeDB_gc b/hotspot/src/share/vm/includeDB_gc new file mode 100644 index 00000000000..662805cc0d4 --- /dev/null +++ b/hotspot/src/share/vm/includeDB_gc @@ -0,0 +1,54 @@ +// +// Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// NOTE: DO NOT CHANGE THIS COPYRIGHT TO NEW STYLE - IT WILL BREAK makeDeps! + +collectedHeap.cpp collectedHeap.hpp +collectedHeap.cpp collectedHeap.inline.hpp +collectedHeap.cpp init.hpp +collectedHeap.cpp oop.inline.hpp +collectedHeap.cpp thread_.inline.hpp + +collectedHeap.hpp allocation.hpp +collectedHeap.hpp barrierSet.hpp +collectedHeap.hpp gcCause.hpp +collectedHeap.hpp handles.hpp +collectedHeap.hpp perfData.hpp +collectedHeap.hpp safepoint.hpp + +collectedHeap.inline.hpp arrayOop.hpp +collectedHeap.inline.hpp collectedHeap.hpp +collectedHeap.inline.hpp copy.hpp +collectedHeap.inline.hpp jvmtiExport.hpp +collectedHeap.inline.hpp lowMemoryDetector.hpp +collectedHeap.inline.hpp sharedRuntime.hpp +collectedHeap.inline.hpp thread.hpp +collectedHeap.inline.hpp threadLocalAllocBuffer.inline.hpp +collectedHeap.inline.hpp universe.hpp +collectedHeap.inline.hpp thread_.inline.hpp +collectedHeap.inline.hpp sharedRuntime.hpp + +gcCause.hpp allocation.hpp + +gcCause.cpp gcCause.hpp diff --git a/hotspot/src/share/vm/includeDB_gc_parallel b/hotspot/src/share/vm/includeDB_gc_parallel new file mode 100644 index 00000000000..31939d06567 --- /dev/null +++ b/hotspot/src/share/vm/includeDB_gc_parallel @@ -0,0 +1,143 @@ +// +// Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// + +collectorPolicy.cpp cmsAdaptiveSizePolicy.hpp +collectorPolicy.cpp cmsGCAdaptivePolicyCounters.hpp + +compiledICHolderKlass.cpp oop.pcgc.inline.hpp + +genCollectedHeap.cpp concurrentMarkSweepThread.hpp +genCollectedHeap.cpp vmCMSOperations.hpp + +generationSpec.cpp asParNewGeneration.hpp +generationSpec.cpp cmsPermGen.hpp +generationSpec.cpp parNewGeneration.hpp + +heapDumper.cpp parallelScavengeHeap.hpp + +heapInspection.cpp parallelScavengeHeap.hpp + +instanceKlass.cpp oop.pcgc.inline.hpp +instanceKlass.cpp psPromotionManager.inline.hpp +instanceKlass.cpp psScavenge.inline.hpp +instanceKlass.cpp parOopClosures.inline.hpp + +instanceKlassKlass.cpp cardTableRS.hpp +instanceKlassKlass.cpp oop.pcgc.inline.hpp +instanceKlassKlass.cpp psPromotionManager.inline.hpp +instanceKlassKlass.cpp psScavenge.inline.hpp +instanceKlassKlass.cpp parOopClosures.inline.hpp + +instanceRefKlass.cpp oop.pcgc.inline.hpp +instanceRefKlass.cpp psPromotionManager.inline.hpp +instanceRefKlass.cpp psScavenge.inline.hpp +instanceRefKlass.cpp parOopClosures.inline.hpp + +java.cpp concurrentMarkSweepThread.hpp +java.cpp psScavenge.hpp +java.cpp psScavenge.inline.hpp + +jvmtiExport.cpp psMarkSweep.hpp + +jvmtiTagMap.cpp parallelScavengeHeap.hpp + +klassKlass.cpp oop.pcgc.inline.hpp + +klass.hpp cmsOopClosures.hpp +klass.hpp parOopClosures.hpp + +memoryPool.hpp compactibleFreeListSpace.hpp + +memoryService.cpp cmsPermGen.hpp +memoryService.cpp concurrentMarkSweepGeneration.hpp +memoryService.cpp parNewGeneration.hpp +memoryService.cpp parallelScavengeHeap.hpp +memoryService.cpp psMemoryPool.hpp +memoryService.cpp psOldGen.hpp +memoryService.cpp psPermGen.hpp +memoryService.cpp psYoungGen.hpp + +methodDataKlass.cpp oop.pcgc.inline.hpp +methodDataKlass.cpp psScavenge.inline.hpp + +objArrayKlass.cpp oop.pcgc.inline.hpp +objArrayKlass.cpp psPromotionManager.inline.hpp +objArrayKlass.cpp psScavenge.inline.hpp +objArrayKlass.cpp parOopClosures.inline.hpp + +oop.pcgc.inline.hpp parNewGeneration.hpp +oop.pcgc.inline.hpp parallelScavengeHeap.hpp +oop.pcgc.inline.hpp psCompactionManager.hpp +oop.pcgc.inline.hpp psParallelCompact.hpp +oop.pcgc.inline.hpp psScavenge.hpp +oop.pcgc.inline.hpp psScavenge.inline.hpp + +oop.psgc.inline.hpp parallelScavengeHeap.hpp +oop.psgc.inline.hpp psScavenge.hpp +oop.psgc.inline.hpp psScavenge.inline.hpp + +psMemoryPool.cpp handles.inline.hpp +psMemoryPool.cpp javaCalls.hpp +psMemoryPool.cpp lowMemoryDetector.hpp +psMemoryPool.cpp management.hpp +psMemoryPool.cpp memoryManager.hpp +psMemoryPool.cpp oop.inline.hpp +psMemoryPool.cpp psMemoryPool.hpp +psMemoryPool.cpp psPermGen.hpp +psMemoryPool.cpp systemDictionary.hpp +psMemoryPool.cpp vmSymbols.hpp + +psMemoryPool.hpp defNewGeneration.hpp +psMemoryPool.hpp heap.hpp +psMemoryPool.hpp memoryUsage.hpp +psMemoryPool.hpp memoryPool.hpp +psMemoryPool.hpp mutableSpace.hpp +psMemoryPool.hpp psOldGen.hpp +psMemoryPool.hpp psYoungGen.hpp +psMemoryPool.hpp space.hpp + +safepoint.cpp concurrentGCThread.hpp +safepoint.cpp concurrentMarkSweepThread.hpp + +thread.cpp concurrentMarkSweepThread.hpp +thread.cpp pcTasks.hpp + +universe.cpp parallelScavengeHeap.hpp +universe.cpp cmsCollectorPolicy.hpp +universe.cpp cmsAdaptiveSizePolicy.hpp + +vmStructs.cpp asPSOldGen.hpp +vmStructs.cpp asPSYoungGen.hpp +vmStructs.cpp cmsPermGen.hpp +vmStructs.cpp compactibleFreeListSpace.hpp +vmStructs.cpp concurrentMarkSweepGeneration.hpp +vmStructs.cpp concurrentMarkSweepThread.hpp +vmStructs.cpp parNewGeneration.hpp +vmStructs.cpp parallelScavengeHeap.hpp +vmStructs.cpp psOldGen.hpp +vmStructs.cpp psPermGen.hpp +vmStructs.cpp psVirtualspace.hpp +vmStructs.cpp psYoungGen.hpp +vmStructs.cpp vmStructs_cms.hpp +vmStructs.cpp vmStructs_parallelgc.hpp +vmStructs.cpp vmStructs_parNew.hpp diff --git a/hotspot/src/share/vm/includeDB_jvmti b/hotspot/src/share/vm/includeDB_jvmti new file mode 100644 index 00000000000..7110bb1bf4e --- /dev/null +++ b/hotspot/src/share/vm/includeDB_jvmti @@ -0,0 +1,257 @@ +// +// Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +jvmtiAgentThread.hpp jvmtiEnv.hpp + +jvmtiClassFileReconstituter.cpp bytecodeStream.hpp +jvmtiClassFileReconstituter.cpp bytes_.hpp +jvmtiClassFileReconstituter.cpp jvmtiClassFileReconstituter.hpp +jvmtiClassFileReconstituter.cpp symbolTable.hpp + +jvmtiClassFileReconstituter.hpp jvmtiEnv.hpp + +// jvmtiCodeBlobEvents is jck optional, please put deps in includeDB_features + +jvmtiEnter.cpp jvmtiEnter.hpp +jvmtiEnter.cpp jvmtiUtil.hpp + +jvmtiEnter.hpp interfaceSupport.hpp +jvmtiEnter.hpp jvmtiEnv.hpp +jvmtiEnter.hpp jvmtiImpl.hpp +jvmtiEnter.hpp resourceArea.hpp +jvmtiEnter.hpp systemDictionary.hpp + +jvmtiEnterTrace.cpp jvmtiEnter.hpp +jvmtiEnterTrace.cpp jvmtiUtil.hpp + +jvmtiEnv.cpp arguments.hpp +jvmtiEnv.cpp bytecodeStream.hpp +jvmtiEnv.cpp cpCacheOop.hpp +jvmtiEnv.cpp deoptimization.hpp +jvmtiEnv.cpp exceptions.hpp +jvmtiEnv.cpp instanceKlass.hpp +jvmtiEnv.cpp interfaceSupport.hpp +jvmtiEnv.cpp interpreter.hpp +jvmtiEnv.cpp javaCalls.hpp +jvmtiEnv.cpp jfieldIDWorkaround.hpp +jvmtiEnv.cpp jniCheck.hpp +jvmtiEnv.cpp jvm_misc.hpp +jvmtiEnv.cpp jvmtiAgentThread.hpp +jvmtiEnv.cpp jvmtiClassFileReconstituter.hpp +jvmtiEnv.cpp jvmtiCodeBlobEvents.hpp +jvmtiEnv.cpp jvmtiEnv.hpp +jvmtiEnv.cpp jvmtiExtensions.hpp +jvmtiEnv.cpp jvmtiGetLoadedClasses.hpp +jvmtiEnv.cpp jvmtiImpl.hpp +jvmtiEnv.cpp jvmtiManageCapabilities.hpp +jvmtiEnv.cpp jvmtiRedefineClasses.hpp +jvmtiEnv.cpp jvmtiTagMap.hpp +jvmtiEnv.cpp jvmtiThreadState.inline.hpp +jvmtiEnv.cpp jvmtiUtil.hpp +jvmtiEnv.cpp objectMonitor.inline.hpp +jvmtiEnv.cpp osThread.hpp +jvmtiEnv.cpp preserveException.hpp +jvmtiEnv.cpp reflectionUtils.hpp +jvmtiEnv.cpp resourceArea.hpp +jvmtiEnv.cpp signature.hpp +jvmtiEnv.cpp systemDictionary.hpp +jvmtiEnv.cpp threadService.hpp +jvmtiEnv.cpp thread_.inline.hpp +jvmtiEnv.cpp universe.inline.hpp +jvmtiEnv.cpp vframe.hpp +jvmtiEnv.cpp vmSymbols.hpp +jvmtiEnv.cpp vmThread.hpp + +jvmtiEnv.hpp jvmtiEnvBase.hpp + +jvmtiEnvBase.cpp biasedLocking.hpp +jvmtiEnvBase.cpp interfaceSupport.hpp +jvmtiEnvBase.cpp jfieldIDWorkaround.hpp +jvmtiEnvBase.cpp jvmtiEnv.hpp +jvmtiEnvBase.cpp jvmtiEnvBase.hpp +jvmtiEnvBase.cpp jvmtiEventController.inline.hpp +jvmtiEnvBase.cpp jvmtiExtensions.hpp +jvmtiEnvBase.cpp jvmtiImpl.hpp +jvmtiEnvBase.cpp jvmtiManageCapabilities.hpp +jvmtiEnvBase.cpp jvmtiTagMap.hpp +jvmtiEnvBase.cpp jvmtiThreadState.inline.hpp +jvmtiEnvBase.cpp objArrayKlass.hpp +jvmtiEnvBase.cpp objArrayOop.hpp +jvmtiEnvBase.cpp objectMonitor.hpp +jvmtiEnvBase.cpp objectMonitor.inline.hpp +jvmtiEnvBase.cpp signature.hpp +jvmtiEnvBase.cpp systemDictionary.hpp +jvmtiEnvBase.cpp vframe.hpp +jvmtiEnvBase.cpp vframe_hp.hpp +jvmtiEnvBase.cpp vmThread.hpp +jvmtiEnvBase.cpp vm_operations.hpp + +jvmtiEnvBase.hpp classLoader.hpp +jvmtiEnvBase.hpp fieldDescriptor.hpp +jvmtiEnvBase.hpp frame.hpp +jvmtiEnvBase.hpp growableArray.hpp +jvmtiEnvBase.hpp handles.inline.hpp +jvmtiEnvBase.hpp jvmtiEnvThreadState.hpp +jvmtiEnvBase.hpp jvmtiEventController.hpp +jvmtiEnvBase.hpp jvmtiThreadState.hpp +jvmtiEnvBase.hpp thread.hpp +jvmtiEnvBase.hpp vm_operations.hpp + +jvmtiEnvThreadState.cpp handles.hpp +jvmtiEnvThreadState.cpp handles.inline.hpp +jvmtiEnvThreadState.cpp interfaceSupport.hpp +jvmtiEnvThreadState.cpp interpreter.hpp +jvmtiEnvThreadState.cpp javaCalls.hpp +jvmtiEnvThreadState.cpp jvmtiEnv.hpp +jvmtiEnvThreadState.cpp jvmtiEnvThreadState.hpp +jvmtiEnvThreadState.cpp jvmtiEventController.inline.hpp +jvmtiEnvThreadState.cpp jvmtiImpl.hpp +jvmtiEnvThreadState.cpp resourceArea.hpp +jvmtiEnvThreadState.cpp signature.hpp +jvmtiEnvThreadState.cpp systemDictionary.hpp +jvmtiEnvThreadState.cpp vframe.hpp +jvmtiEnvThreadState.cpp vm_operations.hpp + +jvmtiEnvThreadState.hpp allocation.hpp +jvmtiEnvThreadState.hpp allocation.inline.hpp +jvmtiEnvThreadState.hpp globalDefinitions.hpp +jvmtiEnvThreadState.hpp growableArray.hpp +jvmtiEnvThreadState.hpp instanceKlass.hpp +jvmtiEnvThreadState.hpp jvmti.h +jvmtiEnvThreadState.hpp jvmtiEventController.hpp + +jvmtiEventController.cpp frame.hpp +jvmtiEventController.cpp interpreter.hpp +jvmtiEventController.cpp jvmtiEnv.hpp +jvmtiEventController.cpp jvmtiEventController.hpp +jvmtiEventController.cpp jvmtiEventController.inline.hpp +jvmtiEventController.cpp jvmtiExport.hpp +jvmtiEventController.cpp jvmtiImpl.hpp +jvmtiEventController.cpp jvmtiThreadState.inline.hpp +jvmtiEventController.cpp resourceArea.hpp +jvmtiEventController.cpp thread.hpp +jvmtiEventController.cpp vframe.hpp +jvmtiEventController.cpp vframe_hp.hpp +jvmtiEventController.cpp vmThread.hpp +jvmtiEventController.cpp vm_operations.hpp + +jvmtiEventController.hpp allocation.hpp +jvmtiEventController.hpp allocation.inline.hpp +jvmtiEventController.hpp globalDefinitions.hpp +jvmtiEventController.hpp jvmti.h + +jvmtiEventController.inline.hpp jvmtiEventController.hpp +jvmtiEventController.inline.hpp jvmtiImpl.hpp +jvmtiEventController.inline.hpp jvmtiUtil.hpp + +jvmtiExport.cpp arguments.hpp +jvmtiExport.cpp attachListener.hpp +jvmtiExport.cpp handles.hpp +jvmtiExport.cpp interfaceSupport.hpp +jvmtiExport.cpp interpreter.hpp +jvmtiExport.cpp jvmtiCodeBlobEvents.hpp +jvmtiExport.cpp jvmtiEnv.hpp +jvmtiExport.cpp jvmtiEventController.hpp +jvmtiExport.cpp jvmtiEventController.inline.hpp +jvmtiExport.cpp jvmtiExport.hpp +jvmtiExport.cpp jvmtiImpl.hpp +jvmtiExport.cpp jvmtiManageCapabilities.hpp +jvmtiExport.cpp jvmtiTagMap.hpp +jvmtiExport.cpp jvmtiThreadState.inline.hpp +jvmtiExport.cpp nmethod.hpp +jvmtiExport.cpp objArrayKlass.hpp +jvmtiExport.cpp objArrayOop.hpp +jvmtiExport.cpp objectMonitor.inline.hpp +jvmtiExport.cpp pcDesc.hpp +jvmtiExport.cpp resourceArea.hpp +jvmtiExport.cpp scopeDesc.hpp +jvmtiExport.cpp serviceUtil.hpp +jvmtiExport.cpp systemDictionary.hpp +jvmtiExport.cpp thread.hpp +jvmtiExport.cpp vframe.hpp + +// jvmtiExtensions is jck optional, please put deps in includeDB_features + +jvmtiGetLoadedClasses.cpp jvmtiGetLoadedClasses.hpp +jvmtiGetLoadedClasses.cpp systemDictionary.hpp +jvmtiGetLoadedClasses.cpp thread.hpp +jvmtiGetLoadedClasses.cpp universe.inline.hpp + +jvmtiGetLoadedClasses.hpp jvmtiEnv.hpp + +// jvmtiImpl is jck optional, please put deps in includeDB_features + +jvmtiManageCapabilities.cpp jvmtiEnv.hpp +jvmtiManageCapabilities.cpp jvmtiExport.hpp +jvmtiManageCapabilities.cpp jvmtiManageCapabilities.hpp + +jvmtiManageCapabilities.hpp allocation.hpp +jvmtiManageCapabilities.hpp jvmti.h + +jvmtiRedefineClasses.cpp codeCache.hpp +jvmtiRedefineClasses.cpp deoptimization.hpp +jvmtiRedefineClasses.cpp gcLocker.hpp +jvmtiRedefineClasses.cpp jvmtiImpl.hpp +jvmtiRedefineClasses.cpp jvmtiRedefineClasses.hpp +jvmtiRedefineClasses.cpp klassVtable.hpp +jvmtiRedefineClasses.cpp methodComparator.hpp +jvmtiRedefineClasses.cpp oopMapCache.hpp +jvmtiRedefineClasses.cpp relocator.hpp +jvmtiRedefineClasses.cpp rewriter.hpp +jvmtiRedefineClasses.cpp systemDictionary.hpp +jvmtiRedefineClasses.cpp universe.inline.hpp +jvmtiRedefineClasses.cpp verifier.hpp + +jvmtiRedefineClasses.hpp jvmtiEnv.hpp +jvmtiRedefineClasses.hpp jvmtiRedefineClassesTrace.hpp +jvmtiRedefineClasses.hpp objArrayKlass.hpp +jvmtiRedefineClasses.hpp objArrayOop.hpp +jvmtiRedefineClasses.hpp oopFactory.hpp +jvmtiRedefineClasses.hpp resourceArea.hpp +jvmtiRedefineClasses.hpp vm_operations.hpp + +// jvmtiTagMap is jck optional, please put deps in includeDB_features +// jvmtiTrace is jck optional, please put deps in includeDB_features + +jvmtiThreadState.cpp gcLocker.hpp +jvmtiThreadState.cpp jvmtiEnv.hpp +jvmtiThreadState.cpp jvmtiEventController.inline.hpp +jvmtiThreadState.cpp jvmtiImpl.hpp +jvmtiThreadState.cpp jvmtiThreadState.inline.hpp +jvmtiThreadState.cpp resourceArea.hpp +jvmtiThreadState.cpp vframe.hpp + +jvmtiThreadState.inline.hpp jvmtiEnvThreadState.hpp +jvmtiThreadState.inline.hpp jvmtiThreadState.hpp + +jvmtiUtil.cpp exceptions.hpp +jvmtiUtil.cpp handles.hpp +jvmtiUtil.cpp handles.inline.hpp +jvmtiUtil.cpp interfaceSupport.hpp +jvmtiUtil.cpp jvmtiUtil.hpp +jvmtiUtil.cpp vm_operations.hpp + +jvmtiUtil.hpp jvmti.h +jvmtiUtil.hpp jvmtiEventController.hpp +jvmtiUtil.hpp resourceArea.hpp diff --git a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp new file mode 100644 index 00000000000..76fbb53b83c --- /dev/null +++ b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp @@ -0,0 +1,245 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file contains the platform-independant parts +// of the abstract interpreter and the abstract interpreter generator. + +// Organization of the interpreter(s). There exists two different interpreters in hotpot +// an assembly language version (aka template interpreter) and a high level language version +// (aka c++ interpreter). Th division of labor is as follows: + +// Template Interpreter C++ Interpreter Functionality +// +// templateTable* bytecodeInterpreter* actual interpretation of bytecodes +// +// templateInterpreter* cppInterpreter* generation of assembly code that creates +// and manages interpreter runtime frames. +// Also code for populating interpreter +// frames created during deoptimization. +// +// For both template and c++ interpreter. There are common files for aspects of the interpreter +// that are generic to both interpreters. This is the layout: +// +// abstractInterpreter.hpp: generic description of the interpreter. +// interpreter*: generic frame creation and handling. +// + +//------------------------------------------------------------------------------------------------------------------------ +// The C++ interface to the bytecode interpreter(s). + +class AbstractInterpreter: AllStatic { + friend class VMStructs; + friend class Interpreter; + friend class CppInterpreterGenerator; + public: + enum MethodKind { + zerolocals, // method needs locals initialization + zerolocals_synchronized, // method needs locals initialization & is synchronized + native, // native method + native_synchronized, // native method & is synchronized + empty, // empty method (code: _return) + accessor, // accessor method (code: _aload_0, _getfield, _(a|i)return) + abstract, // abstract method (throws an AbstractMethodException) + java_lang_math_sin, // implementation of java.lang.Math.sin (x) + java_lang_math_cos, // implementation of java.lang.Math.cos (x) + java_lang_math_tan, // implementation of java.lang.Math.tan (x) + java_lang_math_abs, // implementation of java.lang.Math.abs (x) + java_lang_math_sqrt, // implementation of java.lang.Math.sqrt (x) + java_lang_math_log, // implementation of java.lang.Math.log (x) + java_lang_math_log10, // implementation of java.lang.Math.log10 (x) + number_of_method_entries, + invalid = -1 + }; + + enum SomeConstants { + number_of_result_handlers = 10 // number of result handlers for native calls + }; + + protected: + static StubQueue* _code; // the interpreter code (codelets) + + static bool _notice_safepoints; // true if safepoints are activated + + static address _native_entry_begin; // Region for native entry code + static address _native_entry_end; + + // method entry points + static address _entry_table[number_of_method_entries]; // entry points for a given method + static address _native_abi_to_tosca[number_of_result_handlers]; // for native method result handlers + static address _slow_signature_handler; // the native method generic (slow) signature handler + + static address _rethrow_exception_entry; // rethrows an activation in previous frame + + + + friend class AbstractInterpreterGenerator; + friend class InterpreterGenerator; + friend class InterpreterMacroAssembler; + + public: + // Initialization/debugging + static void initialize(); + static StubQueue* code() { return _code; } + + + // Method activation + static MethodKind method_kind(methodHandle m); + static address entry_for_kind(MethodKind k) { assert(0 <= k && k < number_of_method_entries, "illegal kind"); return _entry_table[k]; } + static address entry_for_method(methodHandle m) { return _entry_table[method_kind(m)]; } + + static void print_method_kind(MethodKind kind) PRODUCT_RETURN; + + // Runtime support + + // length = invoke bytecode length (to advance to next bytecode) + static address deopt_entry (TosState state, int length) { ShouldNotReachHere(); return NULL; } + static address return_entry (TosState state, int length) { ShouldNotReachHere(); return NULL; } + + static address rethrow_exception_entry() { return _rethrow_exception_entry; } + + // Activation size in words for a method that is just being called. + // Parameters haven't been pushed so count them too. + static int size_top_interpreter_activation(methodOop method); + + // Deoptimization support + static address continuation_for(methodOop method, + address bcp, + int callee_parameters, + bool is_top_frame, + bool& use_next_mdp); + + // share implementation of size_activation and layout_activation: + static int size_activation(methodOop method, + int temps, + int popframe_args, + int monitors, + int callee_params, + int callee_locals, + bool is_top_frame); + + static int layout_activation(methodOop method, + int temps, + int popframe_args, + int monitors, + int callee_params, + int callee_locals, + frame* caller, + frame* interpreter_frame, + bool is_top_frame); + + // Runtime support + static bool is_not_reached( methodHandle method, int bci); + // Safepoint support + static void notice_safepoints() { ShouldNotReachHere(); } // stops the thread when reaching a safepoint + static void ignore_safepoints() { ShouldNotReachHere(); } // ignores safepoints + + // Support for native calls + static address slow_signature_handler() { return _slow_signature_handler; } + static address result_handler(BasicType type) { return _native_abi_to_tosca[BasicType_as_index(type)]; } + static int BasicType_as_index(BasicType type); // computes index into result_handler_by_index table + static bool in_native_entry(address pc) { return _native_entry_begin <= pc && pc < _native_entry_end; } + // Debugging/printing + static void print(); // prints the interpreter code + + // Support for Tagged Stacks + // + // Tags are stored on the Java Expression stack above the value: + // + // tag + // value + // + // For double values: + // + // tag2 + // high word + // tag1 + // low word + + public: + static int stackElementWords() { return TaggedStackInterpreter ? 2 : 1; } + static int stackElementSize() { return stackElementWords()*wordSize; } + static int logStackElementSize() { return + TaggedStackInterpreter? LogBytesPerWord+1 : LogBytesPerWord; } + + // Tag is at pointer, value is one below for a stack growing down + // (or above for stack growing up) + static int value_offset_in_bytes() { + return TaggedStackInterpreter ? + frame::interpreter_frame_expression_stack_direction() * wordSize : 0; + } + static int tag_offset_in_bytes() { + assert(TaggedStackInterpreter, "should not call this"); + return 0; + } + + // Tagged Locals + // Locals are stored relative to Llocals: + // + // tag <- Llocals[n] + // value + // + // Category 2 types are indexed as: + // + // tag <- Llocals[-n] + // high word + // tag <- Llocals[-n+1] + // low word + // + + // Local values relative to locals[n] + static int local_offset_in_bytes(int n) { + return ((frame::interpreter_frame_expression_stack_direction() * n) * + stackElementSize()) + value_offset_in_bytes(); + } + static int local_tag_offset_in_bytes(int n) { + assert(TaggedStackInterpreter, "should not call this"); + return ((frame::interpreter_frame_expression_stack_direction() * n) * + stackElementSize()) + tag_offset_in_bytes(); + } + +}; + +//------------------------------------------------------------------------------------------------------------------------ +// The interpreter generator. + +class Template; +class AbstractInterpreterGenerator: public StackObj { + protected: + InterpreterMacroAssembler* _masm; + + // shared code sequences + // Converter for native abi result to tosca result + address generate_result_handler_for(BasicType type); + address generate_slow_signature_handler(); + + // entry point generator + address generate_method_entry(AbstractInterpreter::MethodKind kind); + + void bang_stack_shadow_pages(bool native_call); + + void generate_all(); + + public: + AbstractInterpreterGenerator(StubQueue* _code); +}; diff --git a/hotspot/src/share/vm/interpreter/bytecode.cpp b/hotspot/src/share/vm/interpreter/bytecode.cpp new file mode 100644 index 00000000000..ea46acbc5e5 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecode.cpp @@ -0,0 +1,205 @@ +/* + * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_bytecode.cpp.incl" + +// Implementation of Bytecode +// Should eventually get rid of these functions and use ThisRelativeObj methods instead + +void Bytecode::set_code(Bytecodes::Code code) { + Bytecodes::check(code); + *addr_at(0) = u_char(code); +} + + +void Bytecode::set_fast_index(int i) { + assert(0 <= i && i < 0x10000, "illegal index value"); + Bytes::put_native_u2(addr_at(1), (jushort)i); +} + + +bool Bytecode::check_must_rewrite() const { + assert(Bytecodes::can_rewrite(code()), "post-check only"); + + // Some codes are conditionally rewriting. Look closely at them. + switch (code()) { + case Bytecodes::_aload_0: + // Even if RewriteFrequentPairs is turned on, + // the _aload_0 code might delay its rewrite until + // a following _getfield rewrites itself. + return false; + + case Bytecodes::_lookupswitch: + return false; // the rewrite is not done by the interpreter + + case Bytecodes::_new: + // (Could actually look at the class here, but the profit would be small.) + return false; // the rewrite is not always done + } + + // No other special cases. + return true; +} + + + +// Implementation of Bytecode_tableupswitch + +int Bytecode_tableswitch::dest_offset_at(int i) const { + address x = aligned_addr_at(1); + int x2 = aligned_offset(1 + (3 + i)*jintSize); + int val = java_signed_word_at(x2); + return java_signed_word_at(aligned_offset(1 + (3 + i)*jintSize)); +} + + +// Implementation of Bytecode_invoke + +void Bytecode_invoke::verify() const { + Bytecodes::Code bc = adjusted_invoke_code(); + assert(is_valid(), "check invoke"); +} + + +symbolOop Bytecode_invoke::signature() const { + constantPoolOop constants = method()->constants(); + return constants->signature_ref_at(index()); +} + + +symbolOop Bytecode_invoke::name() const { + constantPoolOop constants = method()->constants(); + return constants->name_ref_at(index()); +} + + +BasicType Bytecode_invoke::result_type(Thread *thread) const { + symbolHandle sh(thread, signature()); + ResultTypeFinder rts(sh); + rts.iterate(); + return rts.type(); +} + + +methodHandle Bytecode_invoke::static_target(TRAPS) { + methodHandle m; + KlassHandle resolved_klass; + constantPoolHandle constants(THREAD, _method->constants()); + + if (adjusted_invoke_code() != Bytecodes::_invokeinterface) { + LinkResolver::resolve_method(m, resolved_klass, constants, index(), CHECK_(methodHandle())); + } else { + LinkResolver::resolve_interface_method(m, resolved_klass, constants, index(), CHECK_(methodHandle())); + } + return m; +} + + +int Bytecode_invoke::index() const { + return Bytes::get_Java_u2(bcp() + 1); +} + + +// Implementation of Bytecode_static + +void Bytecode_static::verify() const { + assert(Bytecodes::java_code(code()) == Bytecodes::_putstatic + || Bytecodes::java_code(code()) == Bytecodes::_getstatic, "check static"); +} + + +BasicType Bytecode_static::result_type(methodOop method) const { + int index = java_hwrd_at(1); + constantPoolOop constants = method->constants(); + symbolOop field_type = constants->signature_ref_at(index); + BasicType basic_type = FieldType::basic_type(field_type); + return basic_type; +} + + +// Implementation of Bytecode_field + +void Bytecode_field::verify() const { + Bytecodes::Code stdc = Bytecodes::java_code(code()); + assert(stdc == Bytecodes::_putstatic || stdc == Bytecodes::_getstatic || + stdc == Bytecodes::_putfield || stdc == Bytecodes::_getfield, "check field"); +} + + +bool Bytecode_field::is_static() const { + Bytecodes::Code stdc = Bytecodes::java_code(code()); + return stdc == Bytecodes::_putstatic || stdc == Bytecodes::_getstatic; +} + + +int Bytecode_field::index() const { + return java_hwrd_at(1); +} + + +// Implementation of Bytecodes loac constant + +int Bytecode_loadconstant::index() const { + Bytecodes::Code stdc = Bytecodes::java_code(code()); + return stdc == Bytecodes::_ldc ? java_byte_at(1) : java_hwrd_at(1); +} + +//------------------------------------------------------------------------------ +// Non-product code + +#ifndef PRODUCT + +void Bytecode_lookupswitch::verify() const { + switch (Bytecodes::java_code(code())) { + case Bytecodes::_lookupswitch: + { int i = number_of_pairs() - 1; + while (i-- > 0) { + assert(pair_at(i)->match() < pair_at(i+1)->match(), "unsorted table entries"); + } + } + break; + default: + fatal("not a lookupswitch bytecode"); + } +} + +void Bytecode_tableswitch::verify() const { + switch (Bytecodes::java_code(code())) { + case Bytecodes::_tableswitch: + { int lo = low_key(); + int hi = high_key(); + assert (hi >= lo, "incorrect hi/lo values in tableswitch"); + int i = hi - lo - 1 ; + while (i-- > 0) { + // no special check needed + } + } + break; + default: + fatal("not a tableswitch bytecode"); + } +} + +#endif diff --git a/hotspot/src/share/vm/interpreter/bytecode.hpp b/hotspot/src/share/vm/interpreter/bytecode.hpp new file mode 100644 index 00000000000..b9f65b21d5d --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecode.hpp @@ -0,0 +1,376 @@ +/* + * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Base class for different kinds of abstractions working +// relative to an objects 'this' pointer. + +class ThisRelativeObj VALUE_OBJ_CLASS_SPEC { + private: + int sign_extend (int x, int size) const { const int s = (BytesPerInt - size)*BitsPerByte; return (x << s) >> s; } + + public: + // Address computation + address addr_at (int offset) const { return (address)this + offset; } + address aligned_addr_at (int offset) const { return (address)round_to((intptr_t)addr_at(offset), jintSize); } + int aligned_offset (int offset) const { return aligned_addr_at(offset) - addr_at(0); } + + // Java unsigned accessors (using Java spec byte ordering) + int java_byte_at (int offset) const { return *(jubyte*)addr_at(offset); } + int java_hwrd_at (int offset) const { return java_byte_at(offset) << (1 * BitsPerByte) | java_byte_at(offset + 1); } + int java_word_at (int offset) const { return java_hwrd_at(offset) << (2 * BitsPerByte) | java_hwrd_at(offset + 2); } + + // Java signed accessors (using Java spec byte ordering) + int java_signed_byte_at(int offset) const { return sign_extend(java_byte_at(offset), 1); } + int java_signed_hwrd_at(int offset) const { return sign_extend(java_hwrd_at(offset), 2); } + int java_signed_word_at(int offset) const { return java_word_at(offset) ; } + + // Fast accessors (using the machine's natural byte ordering) + int fast_byte_at (int offset) const { return *(jubyte *)addr_at(offset); } + int fast_hwrd_at (int offset) const { return *(jushort*)addr_at(offset); } + int fast_word_at (int offset) const { return *(juint *)addr_at(offset); } + + // Fast signed accessors (using the machine's natural byte ordering) + int fast_signed_byte_at(int offset) const { return *(jbyte *)addr_at(offset); } + int fast_signed_hwrd_at(int offset) const { return *(jshort*)addr_at(offset); } + int fast_signed_word_at(int offset) const { return *(jint *)addr_at(offset); } + + // Fast manipulators (using the machine's natural byte ordering) + void set_fast_byte_at (int offset, int x) const { *(jbyte *)addr_at(offset) = (jbyte )x; } + void set_fast_hwrd_at (int offset, int x) const { *(jshort*)addr_at(offset) = (jshort)x; } + void set_fast_word_at (int offset, int x) const { *(jint *)addr_at(offset) = (jint )x; } +}; + + +// The base class for different kinds of bytecode abstractions. +// Provides the primitive operations to manipulate code relative +// to an objects 'this' pointer. +// +// Note: Even though it seems that the fast_index & set_fast_index +// functions are machine specific, they're not. They only use +// the natural way to store a 16bit index on a given machine, +// independent of the particular byte ordering. Since all other +// places in the system that refer to these indices use the +// same method (the natural byte ordering on the platform) +// this will always work and be machine-independent). + +class Bytecode: public ThisRelativeObj { + protected: + u_char byte_at(int offset) const { return *addr_at(offset); } + bool check_must_rewrite() const; + + public: + // Attributes + address bcp() const { return addr_at(0); } + address next_bcp() const { return addr_at(0) + Bytecodes::length_at(bcp()); } + + Bytecodes::Code code() const { return Bytecodes::code_at(addr_at(0)); } + Bytecodes::Code java_code() const { return Bytecodes::java_code(code()); } + bool must_rewrite() const { return Bytecodes::can_rewrite(code()) && check_must_rewrite(); } + bool is_active_breakpoint() const { return Bytecodes::is_active_breakpoint_at(bcp()); } + + int one_byte_index() const { return byte_at(1); } + int two_byte_index() const { return (byte_at(1) << 8) + byte_at(2); } + int offset() const { return (two_byte_index() << 16) >> 16; } + address destination() const { return bcp() + offset(); } + int fast_index() const { return Bytes::get_native_u2(addr_at(1)); } + + // Attribute modification + void set_code(Bytecodes::Code code); + void set_fast_index(int i); + + // Creation + inline friend Bytecode* Bytecode_at(address bcp); +}; + +inline Bytecode* Bytecode_at(address bcp) { + return (Bytecode*)bcp; +} + + +// Abstractions for lookupswitch bytecode + +class LookupswitchPair: ThisRelativeObj { + private: + int _match; + int _offset; + + public: + int match() const { return java_signed_word_at(0 * jintSize); } + int offset() const { return java_signed_word_at(1 * jintSize); } +}; + + +class Bytecode_lookupswitch: public Bytecode { + public: + void verify() const PRODUCT_RETURN; + + // Attributes + int default_offset() const { return java_signed_word_at(aligned_offset(1 + 0*jintSize)); } + int number_of_pairs() const { return java_signed_word_at(aligned_offset(1 + 1*jintSize)); } + LookupswitchPair* pair_at(int i) const { assert(0 <= i && i < number_of_pairs(), "pair index out of bounds"); + return (LookupswitchPair*)aligned_addr_at(1 + (1 + i)*2*jintSize); } + // Creation + inline friend Bytecode_lookupswitch* Bytecode_lookupswitch_at(address bcp); +}; + +inline Bytecode_lookupswitch* Bytecode_lookupswitch_at(address bcp) { + Bytecode_lookupswitch* b = (Bytecode_lookupswitch*)bcp; + debug_only(b->verify()); + return b; +} + + +class Bytecode_tableswitch: public Bytecode { + public: + void verify() const PRODUCT_RETURN; + + // Attributes + int default_offset() const { return java_signed_word_at(aligned_offset(1 + 0*jintSize)); } + int low_key() const { return java_signed_word_at(aligned_offset(1 + 1*jintSize)); } + int high_key() const { return java_signed_word_at(aligned_offset(1 + 2*jintSize)); } + int dest_offset_at(int i) const; + int length() { return high_key()-low_key()+1; } + + // Creation + inline friend Bytecode_tableswitch* Bytecode_tableswitch_at(address bcp); +}; + +inline Bytecode_tableswitch* Bytecode_tableswitch_at(address bcp) { + Bytecode_tableswitch* b = (Bytecode_tableswitch*)bcp; + debug_only(b->verify()); + return b; +} + + +// Abstraction for invoke_{virtual, static, interface, special} + +class Bytecode_invoke: public ResourceObj { + protected: + methodHandle _method; // method containing the bytecode + int _bci; // position of the bytecode + + Bytecode_invoke(methodHandle method, int bci) : _method(method), _bci(bci) {} + + public: + void verify() const; + + // Attributes + methodHandle method() const { return _method; } + int bci() const { return _bci; } + address bcp() const { return _method->bcp_from(bci()); } + + int index() const; // the constant pool index for the invoke + symbolOop name() const; // returns the name of the invoked method + symbolOop signature() const; // returns the signature of the invoked method + BasicType result_type(Thread *thread) const; // returns the result type of the invoke + + Bytecodes::Code code() const { return Bytecodes::code_at(bcp(), _method()); } + Bytecodes::Code adjusted_invoke_code() const { return Bytecodes::java_code(code()); } + + methodHandle static_target(TRAPS); // "specified" method (from constant pool) + + // Testers + bool is_invokeinterface() const { return adjusted_invoke_code() == Bytecodes::_invokeinterface; } + bool is_invokevirtual() const { return adjusted_invoke_code() == Bytecodes::_invokevirtual; } + bool is_invokestatic() const { return adjusted_invoke_code() == Bytecodes::_invokestatic; } + bool is_invokespecial() const { return adjusted_invoke_code() == Bytecodes::_invokespecial; } + + bool is_valid() const { return is_invokeinterface() || + is_invokevirtual() || + is_invokestatic() || + is_invokespecial(); } + + // Creation + inline friend Bytecode_invoke* Bytecode_invoke_at(methodHandle method, int bci); + + // Like Bytecode_invoke_at. Instead it returns NULL if the bci is not at an invoke. + inline friend Bytecode_invoke* Bytecode_invoke_at_check(methodHandle method, int bci); +}; + +inline Bytecode_invoke* Bytecode_invoke_at(methodHandle method, int bci) { + Bytecode_invoke* b = new Bytecode_invoke(method, bci); + debug_only(b->verify()); + return b; +} + +inline Bytecode_invoke* Bytecode_invoke_at_check(methodHandle method, int bci) { + Bytecode_invoke* b = new Bytecode_invoke(method, bci); + return b->is_valid() ? b : NULL; +} + + +// Abstraction for all field accesses (put/get field/static_ +class Bytecode_field: public Bytecode { +public: + void verify() const; + + int index() const; + bool is_static() const; + + // Creation + inline friend Bytecode_field* Bytecode_field_at(const methodOop method, address bcp); +}; + +inline Bytecode_field* Bytecode_field_at(const methodOop method, address bcp) { + Bytecode_field* b = (Bytecode_field*)bcp; + debug_only(b->verify()); + return b; +} + + +// Abstraction for {get,put}static + +class Bytecode_static: public Bytecode { + public: + void verify() const; + + // Returns the result type of the send by inspecting the field ref + BasicType result_type(methodOop method) const; + + // Creation + inline friend Bytecode_static* Bytecode_static_at(const methodOop method, address bcp); +}; + +inline Bytecode_static* Bytecode_static_at(const methodOop method, address bcp) { + Bytecode_static* b = (Bytecode_static*)bcp; + debug_only(b->verify()); + return b; +} + + +// Abstraction for checkcast + +class Bytecode_checkcast: public Bytecode { + public: + void verify() const { assert(Bytecodes::java_code(code()) == Bytecodes::_checkcast, "check checkcast"); } + + // Returns index + long index() const { return java_hwrd_at(1); }; + + // Creation + inline friend Bytecode_checkcast* Bytecode_checkcast_at(address bcp); +}; + +inline Bytecode_checkcast* Bytecode_checkcast_at(address bcp) { + Bytecode_checkcast* b = (Bytecode_checkcast*)bcp; + debug_only(b->verify()); + return b; +} + + +// Abstraction for instanceof + +class Bytecode_instanceof: public Bytecode { + public: + void verify() const { assert(code() == Bytecodes::_instanceof, "check instanceof"); } + + // Returns index + long index() const { return java_hwrd_at(1); }; + + // Creation + inline friend Bytecode_instanceof* Bytecode_instanceof_at(address bcp); +}; + +inline Bytecode_instanceof* Bytecode_instanceof_at(address bcp) { + Bytecode_instanceof* b = (Bytecode_instanceof*)bcp; + debug_only(b->verify()); + return b; +} + + +class Bytecode_new: public Bytecode { + public: + void verify() const { assert(java_code() == Bytecodes::_new, "check new"); } + + // Returns index + long index() const { return java_hwrd_at(1); }; + + // Creation + inline friend Bytecode_new* Bytecode_new_at(address bcp); +}; + +inline Bytecode_new* Bytecode_new_at(address bcp) { + Bytecode_new* b = (Bytecode_new*)bcp; + debug_only(b->verify()); + return b; +} + + +class Bytecode_multianewarray: public Bytecode { + public: + void verify() const { assert(java_code() == Bytecodes::_multianewarray, "check new"); } + + // Returns index + long index() const { return java_hwrd_at(1); }; + + // Creation + inline friend Bytecode_multianewarray* Bytecode_multianewarray_at(address bcp); +}; + +inline Bytecode_multianewarray* Bytecode_multianewarray_at(address bcp) { + Bytecode_multianewarray* b = (Bytecode_multianewarray*)bcp; + debug_only(b->verify()); + return b; +} + + +class Bytecode_anewarray: public Bytecode { + public: + void verify() const { assert(java_code() == Bytecodes::_anewarray, "check anewarray"); } + + // Returns index + long index() const { return java_hwrd_at(1); }; + + // Creation + inline friend Bytecode_anewarray* Bytecode_anewarray_at(address bcp); +}; + +inline Bytecode_anewarray* Bytecode_anewarray_at(address bcp) { + Bytecode_anewarray* b = (Bytecode_anewarray*)bcp; + debug_only(b->verify()); + return b; +} + + +// Abstraction for ldc, ldc_w and ldc2_w + +class Bytecode_loadconstant: public Bytecode { + public: + void verify() const { + Bytecodes::Code stdc = Bytecodes::java_code(code()); + assert(stdc == Bytecodes::_ldc || + stdc == Bytecodes::_ldc_w || + stdc == Bytecodes::_ldc2_w, "load constant"); + } + + int index() const; + + inline friend Bytecode_loadconstant* Bytecode_loadconstant_at(const methodOop method, address bcp); +}; + +inline Bytecode_loadconstant* Bytecode_loadconstant_at(const methodOop method, address bcp) { + Bytecode_loadconstant* b = (Bytecode_loadconstant*)bcp; + debug_only(b->verify()); + return b; +} diff --git a/hotspot/src/share/vm/interpreter/bytecodeHistogram.cpp b/hotspot/src/share/vm/interpreter/bytecodeHistogram.cpp new file mode 100644 index 00000000000..ec0c51f649c --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeHistogram.cpp @@ -0,0 +1,186 @@ +/* + * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_bytecodeHistogram.cpp.incl" + +// ------------------------------------------------------------------------------------------------ +// Non-product code +#ifndef PRODUCT + +// Implementation of BytecodeCounter + +int BytecodeCounter::_counter_value = 0; +jlong BytecodeCounter::_reset_time = 0; + + +void BytecodeCounter::reset() { + _counter_value = 0; + _reset_time = os::elapsed_counter(); +} + + +double BytecodeCounter::elapsed_time() { + return (double)(os::elapsed_counter() - _reset_time) / (double)os::elapsed_frequency(); +} + + +double BytecodeCounter::frequency() { + return (double)counter_value() / elapsed_time(); +} + + +void BytecodeCounter::print() { + tty->print_cr( + "%d bytecodes executed in %.1fs (%.3fMHz)", + counter_value(), + elapsed_time(), + frequency() / 1000000.0 + ); +} + + +// Helper class for sorting + +class HistoEntry: public ResourceObj { + private: + int _index; + int _count; + + public: + HistoEntry(int index, int count) { _index = index; _count = count; } + int index() const { return _index; } + int count() const { return _count; } + + static int compare(HistoEntry** x, HistoEntry** y) { return (*x)->count() - (*y)->count(); } +}; + + +// Helper functions + +static GrowableArray* sorted_array(int* array, int length) { + GrowableArray* a = new GrowableArray(length); + int i = length; + while (i-- > 0) a->append(new HistoEntry(i, array[i])); + a->sort(HistoEntry::compare); + return a; +} + + +static int total_count(GrowableArray* profile) { + int sum = 0; + int i = profile->length(); + while (i-- > 0) sum += profile->at(i)->count(); + return sum; +} + + +static const char* name_for(int i) { + return Bytecodes::is_defined(i) ? Bytecodes::name(Bytecodes::cast(i)) : "xxxunusedxxx"; +} + + +// Implementation of BytecodeHistogram + +int BytecodeHistogram::_counters[Bytecodes::number_of_codes]; + + +void BytecodeHistogram::reset() { + int i = Bytecodes::number_of_codes; + while (i-- > 0) _counters[i] = 0; +} + + +void BytecodeHistogram::print(float cutoff) { + ResourceMark rm; + GrowableArray* profile = sorted_array(_counters, Bytecodes::number_of_codes); + // print profile + int tot = total_count(profile); + int abs_sum = 0; + tty->cr(); //0123456789012345678901234567890123456789012345678901234567890123456789 + tty->print_cr("Histogram of %d executed bytecodes:", tot); + tty->cr(); + tty->print_cr(" absolute relative code name"); + tty->print_cr("----------------------------------------------------------------------"); + int i = profile->length(); + while (i-- > 0) { + HistoEntry* e = profile->at(i); + int abs = e->count(); + float rel = abs * 100.0F / tot; + if (cutoff <= rel) { + tty->print_cr("%10d %7.2f%% %02x %s", abs, rel, e->index(), name_for(e->index())); + abs_sum += abs; + } + } + tty->print_cr("----------------------------------------------------------------------"); + float rel_sum = abs_sum * 100.0F / tot; + tty->print_cr("%10d %7.2f%% (cutoff = %.2f%%)", abs_sum, rel_sum, cutoff); + tty->cr(); +} + + +// Implementation of BytecodePairHistogram + +int BytecodePairHistogram::_index; +int BytecodePairHistogram::_counters[BytecodePairHistogram::number_of_pairs]; + + +void BytecodePairHistogram::reset() { + _index = Bytecodes::_nop << log2_number_of_codes; + + int i = number_of_pairs; + while (i-- > 0) _counters[i] = 0; +} + + +void BytecodePairHistogram::print(float cutoff) { + ResourceMark rm; + GrowableArray* profile = sorted_array(_counters, number_of_pairs); + // print profile + int tot = total_count(profile); + int abs_sum = 0; + tty->cr(); //0123456789012345678901234567890123456789012345678901234567890123456789 + tty->print_cr("Histogram of %d executed bytecode pairs:", tot); + tty->cr(); + tty->print_cr(" absolute relative codes 1st bytecode 2nd bytecode"); + tty->print_cr("----------------------------------------------------------------------"); + int i = profile->length(); + while (i-- > 0) { + HistoEntry* e = profile->at(i); + int abs = e->count(); + float rel = abs * 100.0F / tot; + if (cutoff <= rel) { + int c1 = e->index() % number_of_codes; + int c2 = e->index() / number_of_codes; + tty->print_cr("%10d %6.3f%% %02x %02x %-19s %s", abs, rel, c1, c2, name_for(c1), name_for(c2)); + abs_sum += abs; + } + } + tty->print_cr("----------------------------------------------------------------------"); + float rel_sum = abs_sum * 100.0F / tot; + tty->print_cr("%10d %6.3f%% (cutoff = %.3f%%)", abs_sum, rel_sum, cutoff); + tty->cr(); +} + +#endif diff --git a/hotspot/src/share/vm/interpreter/bytecodeHistogram.hpp b/hotspot/src/share/vm/interpreter/bytecodeHistogram.hpp new file mode 100644 index 00000000000..00eb84eb922 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeHistogram.hpp @@ -0,0 +1,92 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// BytecodeCounter counts the number of bytecodes executed + +class BytecodeCounter: AllStatic { + private: + NOT_PRODUCT(static int _counter_value;) + NOT_PRODUCT(static jlong _reset_time;) + + friend class TemplateInterpreterGenerator; + friend class BytecodeInterpreter; + + public: + // Initialization + static void reset() PRODUCT_RETURN; + + // Counter info (all info since last reset) + static int counter_value() PRODUCT_RETURN0 NOT_PRODUCT({ return _counter_value; }); + static double elapsed_time() PRODUCT_RETURN0; // in seconds + static double frequency() PRODUCT_RETURN0; // bytecodes/seconds + + // Counter printing + static void print() PRODUCT_RETURN; +}; + + +// BytecodeHistogram collects number of executions of bytecodes + +class BytecodeHistogram: AllStatic { + private: + NOT_PRODUCT(static int _counters[Bytecodes::number_of_codes];) // a counter for each bytecode + + friend class TemplateInterpreterGenerator; + friend class InterpreterGenerator; + friend class BytecodeInterpreter; + + public: + // Initialization + static void reset() PRODUCT_RETURN; // reset counters + + // Profile printing + static void print(float cutoff = 0.01F) PRODUCT_RETURN; // cutoff in percent +}; + + +// BytecodePairHistogram collects number of executions of bytecode pairs. +// A bytecode pair is any sequence of two consequtive bytecodes. + +class BytecodePairHistogram: AllStatic { + public: // for SparcWorks + enum Constants { + log2_number_of_codes = 8, // use a power of 2 for faster addressing + number_of_codes = 1 << log2_number_of_codes, // must be no less than Bytecodes::number_of_codes + number_of_pairs = number_of_codes * number_of_codes + }; + + private: + NOT_PRODUCT(static int _index;) // new bytecode is shifted in - used to index into _counters + NOT_PRODUCT(static int _counters[number_of_pairs];) // a counter for each pair + + friend class TemplateInterpreterGenerator; + friend class InterpreterGenerator; + + public: + // Initialization + static void reset() PRODUCT_RETURN; // reset counters + + // Profile printing + static void print(float cutoff = 0.01F) PRODUCT_RETURN; // cutoff in percent +}; diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp new file mode 100644 index 00000000000..2a03dc2d6d4 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp @@ -0,0 +1,3047 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +// no precompiled headers +#include "incls/_bytecodeInterpreter.cpp.incl" + +#ifdef CC_INTERP + +/* + * USELABELS - If using GCC, then use labels for the opcode dispatching + * rather -then a switch statement. This improves performance because it + * gives us the oportunity to have the instructions that calculate the + * next opcode to jump to be intermixed with the rest of the instructions + * that implement the opcode (see UPDATE_PC_AND_TOS_AND_CONTINUE macro). + */ +#undef USELABELS +#ifdef __GNUC__ +/* + ASSERT signifies debugging. It is much easier to step thru bytecodes if we + don't use the computed goto approach. +*/ +#ifndef ASSERT +#define USELABELS +#endif +#endif + +#undef CASE +#ifdef USELABELS +#define CASE(opcode) opc ## opcode +#define DEFAULT opc_default +#else +#define CASE(opcode) case Bytecodes:: opcode +#define DEFAULT default +#endif + +/* + * PREFETCH_OPCCODE - Some compilers do better if you prefetch the next + * opcode before going back to the top of the while loop, rather then having + * the top of the while loop handle it. This provides a better opportunity + * for instruction scheduling. Some compilers just do this prefetch + * automatically. Some actually end up with worse performance if you + * force the prefetch. Solaris gcc seems to do better, but cc does worse. + */ +#undef PREFETCH_OPCCODE +#define PREFETCH_OPCCODE + +/* + Interpreter safepoint: it is expected that the interpreter will have no live + handles of its own creation live at an interpreter safepoint. Therefore we + run a HandleMarkCleaner and trash all handles allocated in the call chain + since the JavaCalls::call_helper invocation that initiated the chain. + There really shouldn't be any handles remaining to trash but this is cheap + in relation to a safepoint. +*/ +#define SAFEPOINT \ + if ( SafepointSynchronize::is_synchronizing()) { \ + { \ + /* zap freed handles rather than GC'ing them */ \ + HandleMarkCleaner __hmc(THREAD); \ + } \ + CALL_VM(SafepointSynchronize::block(THREAD), handle_exception); \ + } + +/* + * VM_JAVA_ERROR - Macro for throwing a java exception from + * the interpreter loop. Should really be a CALL_VM but there + * is no entry point to do the transition to vm so we just + * do it by hand here. + */ +#define VM_JAVA_ERROR_NO_JUMP(name, msg) \ + DECACHE_STATE(); \ + SET_LAST_JAVA_FRAME(); \ + { \ + ThreadInVMfromJava trans(THREAD); \ + Exceptions::_throw_msg(THREAD, __FILE__, __LINE__, name, msg); \ + } \ + RESET_LAST_JAVA_FRAME(); \ + CACHE_STATE(); + +// Normal throw of a java error +#define VM_JAVA_ERROR(name, msg) \ + VM_JAVA_ERROR_NO_JUMP(name, msg) \ + goto handle_exception; + +#ifdef PRODUCT +#define DO_UPDATE_INSTRUCTION_COUNT(opcode) +#else +#define DO_UPDATE_INSTRUCTION_COUNT(opcode) \ +{ \ + BytecodeCounter::_counter_value++; \ + BytecodeHistogram::_counters[(Bytecodes::Code)opcode]++; \ + if (StopInterpreterAt && StopInterpreterAt == BytecodeCounter::_counter_value) os::breakpoint(); \ + if (TraceBytecodes) { \ + CALL_VM((void)SharedRuntime::trace_bytecode(THREAD, 0, \ + topOfStack[Interpreter::expr_index_at(1)], \ + topOfStack[Interpreter::expr_index_at(2)]), \ + handle_exception); \ + } \ +} +#endif + +#undef DEBUGGER_SINGLE_STEP_NOTIFY +#ifdef VM_JVMTI +/* NOTE: (kbr) This macro must be called AFTER the PC has been + incremented. JvmtiExport::at_single_stepping_point() may cause a + breakpoint opcode to get inserted at the current PC to allow the + debugger to coalesce single-step events. + + As a result if we call at_single_stepping_point() we refetch opcode + to get the current opcode. This will override any other prefetching + that might have occurred. +*/ +#define DEBUGGER_SINGLE_STEP_NOTIFY() \ +{ \ + if (_jvmti_interp_events) { \ + if (JvmtiExport::should_post_single_step()) { \ + DECACHE_STATE(); \ + SET_LAST_JAVA_FRAME(); \ + ThreadInVMfromJava trans(THREAD); \ + JvmtiExport::at_single_stepping_point(THREAD, \ + istate->method(), \ + pc); \ + RESET_LAST_JAVA_FRAME(); \ + CACHE_STATE(); \ + if (THREAD->pop_frame_pending() && \ + !THREAD->pop_frame_in_process()) { \ + goto handle_Pop_Frame; \ + } \ + opcode = *pc; \ + } \ + } \ +} +#else +#define DEBUGGER_SINGLE_STEP_NOTIFY() +#endif + +/* + * CONTINUE - Macro for executing the next opcode. + */ +#undef CONTINUE +#ifdef USELABELS +// Have to do this dispatch this way in C++ because otherwise gcc complains about crossing an +// initialization (which is is the initialization of the table pointer...) +#define DISPATCH(opcode) goto *dispatch_table[opcode] +#define CONTINUE { \ + opcode = *pc; \ + DO_UPDATE_INSTRUCTION_COUNT(opcode); \ + DEBUGGER_SINGLE_STEP_NOTIFY(); \ + DISPATCH(opcode); \ + } +#else +#ifdef PREFETCH_OPCCODE +#define CONTINUE { \ + opcode = *pc; \ + DO_UPDATE_INSTRUCTION_COUNT(opcode); \ + DEBUGGER_SINGLE_STEP_NOTIFY(); \ + continue; \ + } +#else +#define CONTINUE { \ + DO_UPDATE_INSTRUCTION_COUNT(opcode); \ + DEBUGGER_SINGLE_STEP_NOTIFY(); \ + continue; \ + } +#endif +#endif + +// JavaStack Implementation +#define MORE_STACK(count) \ + (topOfStack -= ((count) * Interpreter::stackElementWords())) + + +#define UPDATE_PC(opsize) {pc += opsize; } +/* + * UPDATE_PC_AND_TOS - Macro for updating the pc and topOfStack. + */ +#undef UPDATE_PC_AND_TOS +#define UPDATE_PC_AND_TOS(opsize, stack) \ + {pc += opsize; MORE_STACK(stack); } + +/* + * UPDATE_PC_AND_TOS_AND_CONTINUE - Macro for updating the pc and topOfStack, + * and executing the next opcode. It's somewhat similar to the combination + * of UPDATE_PC_AND_TOS and CONTINUE, but with some minor optimizations. + */ +#undef UPDATE_PC_AND_TOS_AND_CONTINUE +#ifdef USELABELS +#define UPDATE_PC_AND_TOS_AND_CONTINUE(opsize, stack) { \ + pc += opsize; opcode = *pc; MORE_STACK(stack); \ + DO_UPDATE_INSTRUCTION_COUNT(opcode); \ + DEBUGGER_SINGLE_STEP_NOTIFY(); \ + DISPATCH(opcode); \ + } + +#define UPDATE_PC_AND_CONTINUE(opsize) { \ + pc += opsize; opcode = *pc; \ + DO_UPDATE_INSTRUCTION_COUNT(opcode); \ + DEBUGGER_SINGLE_STEP_NOTIFY(); \ + DISPATCH(opcode); \ + } +#else +#ifdef PREFETCH_OPCCODE +#define UPDATE_PC_AND_TOS_AND_CONTINUE(opsize, stack) { \ + pc += opsize; opcode = *pc; MORE_STACK(stack); \ + DO_UPDATE_INSTRUCTION_COUNT(opcode); \ + DEBUGGER_SINGLE_STEP_NOTIFY(); \ + goto do_continue; \ + } + +#define UPDATE_PC_AND_CONTINUE(opsize) { \ + pc += opsize; opcode = *pc; \ + DO_UPDATE_INSTRUCTION_COUNT(opcode); \ + DEBUGGER_SINGLE_STEP_NOTIFY(); \ + goto do_continue; \ + } +#else +#define UPDATE_PC_AND_TOS_AND_CONTINUE(opsize, stack) { \ + pc += opsize; MORE_STACK(stack); \ + DO_UPDATE_INSTRUCTION_COUNT(opcode); \ + DEBUGGER_SINGLE_STEP_NOTIFY(); \ + goto do_continue; \ + } + +#define UPDATE_PC_AND_CONTINUE(opsize) { \ + pc += opsize; \ + DO_UPDATE_INSTRUCTION_COUNT(opcode); \ + DEBUGGER_SINGLE_STEP_NOTIFY(); \ + goto do_continue; \ + } +#endif /* PREFETCH_OPCCODE */ +#endif /* USELABELS */ + +// About to call a new method, update the save the adjusted pc and return to frame manager +#define UPDATE_PC_AND_RETURN(opsize) \ + DECACHE_TOS(); \ + istate->set_bcp(pc+opsize); \ + return; + + +#define METHOD istate->method() +#define INVOCATION_COUNT METHOD->invocation_counter() +#define BACKEDGE_COUNT METHOD->backedge_counter() + + +#define INCR_INVOCATION_COUNT INVOCATION_COUNT->increment() +#define OSR_REQUEST(res, branch_pc) \ + CALL_VM(res=InterpreterRuntime::frequency_counter_overflow(THREAD, branch_pc), handle_exception); +/* + * For those opcodes that need to have a GC point on a backwards branch + */ + +// Backedge counting is kind of strange. The asm interpreter will increment +// the backedge counter as a separate counter but it does it's comparisons +// to the sum (scaled) of invocation counter and backedge count to make +// a decision. Seems kind of odd to sum them together like that + +// skip is delta from current bcp/bci for target, branch_pc is pre-branch bcp + + +#define DO_BACKEDGE_CHECKS(skip, branch_pc) \ + if ((skip) <= 0) { \ + if (UseCompiler && UseLoopCounter) { \ + bool do_OSR = UseOnStackReplacement; \ + BACKEDGE_COUNT->increment(); \ + if (do_OSR) do_OSR = BACKEDGE_COUNT->reached_InvocationLimit(); \ + if (do_OSR) { \ + nmethod* osr_nmethod; \ + OSR_REQUEST(osr_nmethod, branch_pc); \ + if (osr_nmethod != NULL && osr_nmethod->osr_entry_bci() != InvalidOSREntryBci) { \ + intptr_t* buf; \ + CALL_VM(buf=SharedRuntime::OSR_migration_begin(THREAD), handle_exception); \ + istate->set_msg(do_osr); \ + istate->set_osr_buf((address)buf); \ + istate->set_osr_entry(osr_nmethod->osr_entry()); \ + return; \ + } \ + } else { \ + INCR_INVOCATION_COUNT; \ + SAFEPOINT; \ + } \ + } /* UseCompiler ... */ \ + INCR_INVOCATION_COUNT; \ + SAFEPOINT; \ + } + +/* + * For those opcodes that need to have a GC point on a backwards branch + */ + +/* + * Macros for caching and flushing the interpreter state. Some local + * variables need to be flushed out to the frame before we do certain + * things (like pushing frames or becomming gc safe) and some need to + * be recached later (like after popping a frame). We could use one + * macro to cache or decache everything, but this would be less then + * optimal because we don't always need to cache or decache everything + * because some things we know are already cached or decached. + */ +#undef DECACHE_TOS +#undef CACHE_TOS +#undef CACHE_PREV_TOS +#define DECACHE_TOS() istate->set_stack(topOfStack); + +#define CACHE_TOS() topOfStack = (intptr_t *)istate->stack(); + +#undef DECACHE_PC +#undef CACHE_PC +#define DECACHE_PC() istate->set_bcp(pc); +#define CACHE_PC() pc = istate->bcp(); +#define CACHE_CP() cp = istate->constants(); +#define CACHE_LOCALS() locals = istate->locals(); +#undef CACHE_FRAME +#define CACHE_FRAME() + +/* + * CHECK_NULL - Macro for throwing a NullPointerException if the object + * passed is a null ref. + * On some architectures/platforms it should be possible to do this implicitly + */ +#undef CHECK_NULL +#define CHECK_NULL(obj_) \ + if ((obj_) == 0) { \ + VM_JAVA_ERROR(vmSymbols::java_lang_NullPointerException(), ""); \ + } + +#define VMdoubleConstZero() 0.0 +#define VMdoubleConstOne() 1.0 +#define VMlongConstZero() (max_jlong-max_jlong) +#define VMlongConstOne() ((max_jlong-max_jlong)+1) + +/* + * Alignment + */ +#define VMalignWordUp(val) (((uintptr_t)(val) + 3) & ~3) + +// Decache the interpreter state that interpreter modifies directly (i.e. GC is indirect mod) +#define DECACHE_STATE() DECACHE_PC(); DECACHE_TOS(); + +// Reload interpreter state after calling the VM or a possible GC +#define CACHE_STATE() \ + CACHE_TOS(); \ + CACHE_PC(); \ + CACHE_CP(); \ + CACHE_LOCALS(); + +// Call the VM don't check for pending exceptions +#define CALL_VM_NOCHECK(func) \ + DECACHE_STATE(); \ + SET_LAST_JAVA_FRAME(); \ + func; \ + RESET_LAST_JAVA_FRAME(); \ + CACHE_STATE(); \ + if (THREAD->pop_frame_pending() && \ + !THREAD->pop_frame_in_process()) { \ + goto handle_Pop_Frame; \ + } + +// Call the VM and check for pending exceptions +#define CALL_VM(func, label) { \ + CALL_VM_NOCHECK(func); \ + if (THREAD->has_pending_exception()) goto label; \ + } + +/* + * BytecodeInterpreter::run(interpreterState istate) + * BytecodeInterpreter::runWithChecks(interpreterState istate) + * + * The real deal. This is where byte codes actually get interpreted. + * Basically it's a big while loop that iterates until we return from + * the method passed in. + * + * The runWithChecks is used if JVMTI is enabled. + * + */ +#if defined(VM_JVMTI) +void +BytecodeInterpreter::runWithChecks(interpreterState istate) { +#else +void +BytecodeInterpreter::run(interpreterState istate) { +#endif + + // In order to simplify some tests based on switches set at runtime + // we invoke the interpreter a single time after switches are enabled + // and set simpler to to test variables rather than method calls or complex + // boolean expressions. + + static int initialized = 0; + static int checkit = 0; + static intptr_t* c_addr = NULL; + static intptr_t c_value; + + if (checkit && *c_addr != c_value) { + os::breakpoint(); + } +#ifdef VM_JVMTI + static bool _jvmti_interp_events = 0; +#endif + + static int _compiling; // (UseCompiler || CountCompiledCalls) + +#ifdef ASSERT + if (istate->_msg != initialize) { + assert(abs(istate->_stack_base - istate->_stack_limit) == (istate->_method->max_stack() + 1), "bad stack limit"); + IA32_ONLY(assert(istate->_stack_limit == istate->_thread->last_Java_sp() + 1, "wrong")); + } + // Verify linkages. + interpreterState l = istate; + do { + assert(l == l->_self_link, "bad link"); + l = l->_prev_link; + } while (l != NULL); + // Screwups with stack management usually cause us to overwrite istate + // save a copy so we can verify it. + interpreterState orig = istate; +#endif + + static volatile jbyte* _byte_map_base; // adjusted card table base for oop store barrier + + register intptr_t* topOfStack = (intptr_t *)istate->stack(); /* access with STACK macros */ + register address pc = istate->bcp(); + register jubyte opcode; + register intptr_t* locals = istate->locals(); + register constantPoolCacheOop cp = istate->constants(); // method()->constants()->cache() +#ifdef LOTS_OF_REGS + register JavaThread* THREAD = istate->thread(); + register volatile jbyte* BYTE_MAP_BASE = _byte_map_base; +#else +#undef THREAD +#define THREAD istate->thread() +#undef BYTE_MAP_BASE +#define BYTE_MAP_BASE _byte_map_base +#endif + +#ifdef USELABELS + const static void* const opclabels_data[256] = { +/* 0x00 */ &&opc_nop, &&opc_aconst_null,&&opc_iconst_m1,&&opc_iconst_0, +/* 0x04 */ &&opc_iconst_1,&&opc_iconst_2, &&opc_iconst_3, &&opc_iconst_4, +/* 0x08 */ &&opc_iconst_5,&&opc_lconst_0, &&opc_lconst_1, &&opc_fconst_0, +/* 0x0C */ &&opc_fconst_1,&&opc_fconst_2, &&opc_dconst_0, &&opc_dconst_1, + +/* 0x10 */ &&opc_bipush, &&opc_sipush, &&opc_ldc, &&opc_ldc_w, +/* 0x14 */ &&opc_ldc2_w, &&opc_iload, &&opc_lload, &&opc_fload, +/* 0x18 */ &&opc_dload, &&opc_aload, &&opc_iload_0,&&opc_iload_1, +/* 0x1C */ &&opc_iload_2,&&opc_iload_3,&&opc_lload_0,&&opc_lload_1, + +/* 0x20 */ &&opc_lload_2,&&opc_lload_3,&&opc_fload_0,&&opc_fload_1, +/* 0x24 */ &&opc_fload_2,&&opc_fload_3,&&opc_dload_0,&&opc_dload_1, +/* 0x28 */ &&opc_dload_2,&&opc_dload_3,&&opc_aload_0,&&opc_aload_1, +/* 0x2C */ &&opc_aload_2,&&opc_aload_3,&&opc_iaload, &&opc_laload, + +/* 0x30 */ &&opc_faload, &&opc_daload, &&opc_aaload, &&opc_baload, +/* 0x34 */ &&opc_caload, &&opc_saload, &&opc_istore, &&opc_lstore, +/* 0x38 */ &&opc_fstore, &&opc_dstore, &&opc_astore, &&opc_istore_0, +/* 0x3C */ &&opc_istore_1,&&opc_istore_2,&&opc_istore_3,&&opc_lstore_0, + +/* 0x40 */ &&opc_lstore_1,&&opc_lstore_2,&&opc_lstore_3,&&opc_fstore_0, +/* 0x44 */ &&opc_fstore_1,&&opc_fstore_2,&&opc_fstore_3,&&opc_dstore_0, +/* 0x48 */ &&opc_dstore_1,&&opc_dstore_2,&&opc_dstore_3,&&opc_astore_0, +/* 0x4C */ &&opc_astore_1,&&opc_astore_2,&&opc_astore_3,&&opc_iastore, + +/* 0x50 */ &&opc_lastore,&&opc_fastore,&&opc_dastore,&&opc_aastore, +/* 0x54 */ &&opc_bastore,&&opc_castore,&&opc_sastore,&&opc_pop, +/* 0x58 */ &&opc_pop2, &&opc_dup, &&opc_dup_x1, &&opc_dup_x2, +/* 0x5C */ &&opc_dup2, &&opc_dup2_x1,&&opc_dup2_x2,&&opc_swap, + +/* 0x60 */ &&opc_iadd,&&opc_ladd,&&opc_fadd,&&opc_dadd, +/* 0x64 */ &&opc_isub,&&opc_lsub,&&opc_fsub,&&opc_dsub, +/* 0x68 */ &&opc_imul,&&opc_lmul,&&opc_fmul,&&opc_dmul, +/* 0x6C */ &&opc_idiv,&&opc_ldiv,&&opc_fdiv,&&opc_ddiv, + +/* 0x70 */ &&opc_irem, &&opc_lrem, &&opc_frem,&&opc_drem, +/* 0x74 */ &&opc_ineg, &&opc_lneg, &&opc_fneg,&&opc_dneg, +/* 0x78 */ &&opc_ishl, &&opc_lshl, &&opc_ishr,&&opc_lshr, +/* 0x7C */ &&opc_iushr,&&opc_lushr,&&opc_iand,&&opc_land, + +/* 0x80 */ &&opc_ior, &&opc_lor,&&opc_ixor,&&opc_lxor, +/* 0x84 */ &&opc_iinc,&&opc_i2l,&&opc_i2f, &&opc_i2d, +/* 0x88 */ &&opc_l2i, &&opc_l2f,&&opc_l2d, &&opc_f2i, +/* 0x8C */ &&opc_f2l, &&opc_f2d,&&opc_d2i, &&opc_d2l, + +/* 0x90 */ &&opc_d2f, &&opc_i2b, &&opc_i2c, &&opc_i2s, +/* 0x94 */ &&opc_lcmp, &&opc_fcmpl,&&opc_fcmpg,&&opc_dcmpl, +/* 0x98 */ &&opc_dcmpg,&&opc_ifeq, &&opc_ifne, &&opc_iflt, +/* 0x9C */ &&opc_ifge, &&opc_ifgt, &&opc_ifle, &&opc_if_icmpeq, + +/* 0xA0 */ &&opc_if_icmpne,&&opc_if_icmplt,&&opc_if_icmpge, &&opc_if_icmpgt, +/* 0xA4 */ &&opc_if_icmple,&&opc_if_acmpeq,&&opc_if_acmpne, &&opc_goto, +/* 0xA8 */ &&opc_jsr, &&opc_ret, &&opc_tableswitch,&&opc_lookupswitch, +/* 0xAC */ &&opc_ireturn, &&opc_lreturn, &&opc_freturn, &&opc_dreturn, + +/* 0xB0 */ &&opc_areturn, &&opc_return, &&opc_getstatic, &&opc_putstatic, +/* 0xB4 */ &&opc_getfield, &&opc_putfield, &&opc_invokevirtual,&&opc_invokespecial, +/* 0xB8 */ &&opc_invokestatic,&&opc_invokeinterface,NULL, &&opc_new, +/* 0xBC */ &&opc_newarray, &&opc_anewarray, &&opc_arraylength, &&opc_athrow, + +/* 0xC0 */ &&opc_checkcast, &&opc_instanceof, &&opc_monitorenter, &&opc_monitorexit, +/* 0xC4 */ &&opc_wide, &&opc_multianewarray, &&opc_ifnull, &&opc_ifnonnull, +/* 0xC8 */ &&opc_goto_w, &&opc_jsr_w, &&opc_breakpoint, &&opc_fast_igetfield, +/* 0xCC */ &&opc_fastagetfield,&&opc_fast_aload_0, &&opc_fast_iaccess_0, &&opc__fast_aaccess_0, + +/* 0xD0 */ &&opc_fast_linearswitch, &&opc_fast_binaryswitch, &&opc_return_register_finalizer, &&opc_default, +/* 0xD4 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, +/* 0xD8 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, +/* 0xDC */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, + +/* 0xE0 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, +/* 0xE4 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, +/* 0xE8 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, +/* 0xEC */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, + +/* 0xF0 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, +/* 0xF4 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, +/* 0xF8 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, +/* 0xFC */ &&opc_default, &&opc_default, &&opc_default, &&opc_default + }; + register uintptr_t *dispatch_table = (uintptr_t*)&opclabels_data[0]; +#endif /* USELABELS */ + +#ifdef ASSERT + // this will trigger a VERIFY_OOP on entry + if (istate->msg() != initialize && ! METHOD->is_static()) { + oop rcvr = LOCALS_OBJECT(0); + } +#endif +// #define HACK +#ifdef HACK + bool interesting = false; +#endif // HACK + + /* QQQ this should be a stack method so we don't know actual direction */ + assert(istate->msg() == initialize || + topOfStack >= istate->stack_limit() && + topOfStack < istate->stack_base(), + "Stack top out of range"); + + switch (istate->msg()) { + case initialize: { + if (initialized++) ShouldNotReachHere(); // Only one initialize call + _compiling = (UseCompiler || CountCompiledCalls); +#ifdef VM_JVMTI + _jvmti_interp_events = JvmtiExport::can_post_interpreter_events(); +#endif + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); + _byte_map_base = (volatile jbyte*)(((CardTableModRefBS*)bs)->byte_map_base); + return; + } + break; + case method_entry: { + THREAD->set_do_not_unlock(); + // count invocations + assert(initialized, "Interpreter not initialized"); + if (_compiling) { + if (ProfileInterpreter) { + METHOD->increment_interpreter_invocation_count(); + } + INCR_INVOCATION_COUNT; + if (INVOCATION_COUNT->reached_InvocationLimit()) { + CALL_VM((void)InterpreterRuntime::frequency_counter_overflow(THREAD, NULL), handle_exception); + + // We no longer retry on a counter overflow + + // istate->set_msg(retry_method); + // THREAD->clr_do_not_unlock(); + // return; + } + SAFEPOINT; + } + + if ((istate->_stack_base - istate->_stack_limit) != istate->method()->max_stack() + 1) { + // initialize + os::breakpoint(); + } + +#ifdef HACK + { + ResourceMark rm; + char *method_name = istate->method()->name_and_sig_as_C_string(); + if (strstr(method_name, "runThese$TestRunner.run()V") != NULL) { + tty->print_cr("entering: depth %d bci: %d", + (istate->_stack_base - istate->_stack), + istate->_bcp - istate->_method->code_base()); + interesting = true; + } + } +#endif // HACK + + + // lock method if synchronized + if (METHOD->is_synchronized()) { + // oop rcvr = locals[0].j.r; + oop rcvr; + if (METHOD->is_static()) { + rcvr = METHOD->constants()->pool_holder()->klass_part()->java_mirror(); + } else { + rcvr = LOCALS_OBJECT(0); + } + // The initial monitor is ours for the taking + BasicObjectLock* mon = &istate->monitor_base()[-1]; + oop monobj = mon->obj(); + assert(mon->obj() == rcvr, "method monitor mis-initialized"); + + bool success = UseBiasedLocking; + if (UseBiasedLocking) { + markOop mark = rcvr->mark(); + if (mark->has_bias_pattern()) { + // The bias pattern is present in the object's header. Need to check + // whether the bias owner and the epoch are both still current. + intptr_t xx = ((intptr_t) THREAD) ^ (intptr_t) mark; + xx = (intptr_t) rcvr->klass()->klass_part()->prototype_header() ^ xx; + intptr_t yy = (xx & ~((int) markOopDesc::age_mask_in_place)); + if (yy != 0 ) { + // At this point we know that the header has the bias pattern and + // that we are not the bias owner in the current epoch. We need to + // figure out more details about the state of the header in order to + // know what operations can be legally performed on the object's + // header. + + // If the low three bits in the xor result aren't clear, that means + // the prototype header is no longer biased and we have to revoke + // the bias on this object. + + if (yy & markOopDesc::biased_lock_mask_in_place == 0 ) { + // Biasing is still enabled for this data type. See whether the + // epoch of the current bias is still valid, meaning that the epoch + // bits of the mark word are equal to the epoch bits of the + // prototype header. (Note that the prototype header's epoch bits + // only change at a safepoint.) If not, attempt to rebias the object + // toward the current thread. Note that we must be absolutely sure + // that the current epoch is invalid in order to do this because + // otherwise the manipulations it performs on the mark word are + // illegal. + if (yy & markOopDesc::epoch_mask_in_place == 0) { + // The epoch of the current bias is still valid but we know nothing + // about the owner; it might be set or it might be clear. Try to + // acquire the bias of the object using an atomic operation. If this + // fails we will go in to the runtime to revoke the object's bias. + // Note that we first construct the presumed unbiased header so we + // don't accidentally blow away another thread's valid bias. + intptr_t unbiased = (intptr_t) mark & (markOopDesc::biased_lock_mask_in_place | + markOopDesc::age_mask_in_place | + markOopDesc::epoch_mask_in_place); + if (Atomic::cmpxchg_ptr((intptr_t)THREAD | unbiased, (intptr_t*) rcvr->mark_addr(), unbiased) != unbiased) { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); + } + } else { + try_rebias: + // At this point we know the epoch has expired, meaning that the + // current "bias owner", if any, is actually invalid. Under these + // circumstances _only_, we are allowed to use the current header's + // value as the comparison value when doing the cas to acquire the + // bias in the current epoch. In other words, we allow transfer of + // the bias from one thread to another directly in this situation. + xx = (intptr_t) rcvr->klass()->klass_part()->prototype_header() | (intptr_t) THREAD; + if (Atomic::cmpxchg_ptr((intptr_t)THREAD | (intptr_t) rcvr->klass()->klass_part()->prototype_header(), + (intptr_t*) rcvr->mark_addr(), + (intptr_t) mark) != (intptr_t) mark) { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); + } + } + } else { + try_revoke_bias: + // The prototype mark in the klass doesn't have the bias bit set any + // more, indicating that objects of this data type are not supposed + // to be biased any more. We are going to try to reset the mark of + // this object to the prototype value and fall through to the + // CAS-based locking scheme. Note that if our CAS fails, it means + // that another thread raced us for the privilege of revoking the + // bias of this particular object, so it's okay to continue in the + // normal locking code. + // + xx = (intptr_t) rcvr->klass()->klass_part()->prototype_header() | (intptr_t) THREAD; + if (Atomic::cmpxchg_ptr(rcvr->klass()->klass_part()->prototype_header(), + (intptr_t*) rcvr->mark_addr(), + mark) == mark) { + // (*counters->revoked_lock_entry_count_addr())++; + success = false; + } + } + } + } else { + cas_label: + success = false; + } + } + if (!success) { + markOop displaced = rcvr->mark()->set_unlocked(); + mon->lock()->set_displaced_header(displaced); + if (Atomic::cmpxchg_ptr(mon, rcvr->mark_addr(), displaced) != displaced) { + // Is it simple recursive case? + if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { + mon->lock()->set_displaced_header(NULL); + } else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); + } + } + } + } + THREAD->clr_do_not_unlock(); + + // Notify jvmti +#ifdef VM_JVMTI + if (_jvmti_interp_events) { + // Whenever JVMTI puts a thread in interp_only_mode, method + // entry/exit events are sent for that thread to track stack depth. + if (THREAD->is_interp_only_mode()) { + CALL_VM(InterpreterRuntime::post_method_entry(THREAD), + handle_exception); + } + } +#endif /* VM_JVMTI */ + + goto run; + } + + case popping_frame: { + // returned from a java call to pop the frame, restart the call + // clear the message so we don't confuse ourselves later + assert(THREAD->pop_frame_in_process(), "wrong frame pop state"); + istate->set_msg(no_request); + THREAD->clr_pop_frame_in_process(); + goto run; + } + + case method_resume: { + if ((istate->_stack_base - istate->_stack_limit) != istate->method()->max_stack() + 1) { + // resume + os::breakpoint(); + } +#ifdef HACK + { + ResourceMark rm; + char *method_name = istate->method()->name_and_sig_as_C_string(); + if (strstr(method_name, "runThese$TestRunner.run()V") != NULL) { + tty->print_cr("resume: depth %d bci: %d", + (istate->_stack_base - istate->_stack) , + istate->_bcp - istate->_method->code_base()); + interesting = true; + } + } +#endif // HACK + // returned from a java call, continue executing. + if (THREAD->pop_frame_pending() && !THREAD->pop_frame_in_process()) { + goto handle_Pop_Frame; + } + + if (THREAD->has_pending_exception()) goto handle_exception; + // Update the pc by the saved amount of the invoke bytecode size + UPDATE_PC(istate->bcp_advance()); + goto run; + } + + case deopt_resume2: { + // Returned from an opcode that will reexecute. Deopt was + // a result of a PopFrame request. + // + goto run; + } + + case deopt_resume: { + // Returned from an opcode that has completed. The stack has + // the result all we need to do is skip across the bytecode + // and continue (assuming there is no exception pending) + // + // compute continuation length + // + // Note: it is possible to deopt at a return_register_finalizer opcode + // because this requires entering the vm to do the registering. While the + // opcode is complete we can't advance because there are no more opcodes + // much like trying to deopt at a poll return. In that has we simply + // get out of here + // + if ( Bytecodes::code_at(pc, METHOD) == Bytecodes::_return_register_finalizer) { + // this will do the right thing even if an exception is pending. + goto handle_return; + } + UPDATE_PC(Bytecodes::length_at(pc)); + if (THREAD->has_pending_exception()) goto handle_exception; + goto run; + } + case got_monitors: { + // continue locking now that we have a monitor to use + // we expect to find newly allocated monitor at the "top" of the monitor stack. + oop lockee = STACK_OBJECT(-1); + // derefing's lockee ought to provoke implicit null check + // find a free monitor + BasicObjectLock* entry = (BasicObjectLock*) istate->stack_base(); + assert(entry->obj() == NULL, "Frame manager didn't allocate the monitor"); + entry->set_obj(lockee); + + markOop displaced = lockee->mark()->set_unlocked(); + entry->lock()->set_displaced_header(displaced); + if (Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) { + // Is it simple recursive case? + if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { + entry->lock()->set_displaced_header(NULL); + } else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); + } + } + UPDATE_PC_AND_TOS(1, -1); + goto run; + } + default: { + fatal("Unexpected message from frame manager"); + } + } + +run: + + DO_UPDATE_INSTRUCTION_COUNT(*pc) + DEBUGGER_SINGLE_STEP_NOTIFY(); +#ifdef PREFETCH_OPCCODE + opcode = *pc; /* prefetch first opcode */ +#endif + +#ifndef USELABELS + while (1) +#endif + { +#ifndef PREFETCH_OPCCODE + opcode = *pc; +#endif + // Seems like this happens twice per opcode. At worst this is only + // need at entry to the loop. + // DEBUGGER_SINGLE_STEP_NOTIFY(); + /* Using this labels avoids double breakpoints when quickening and + * when returing from transition frames. + */ + opcode_switch: + assert(istate == orig, "Corrupted istate"); + /* QQQ Hmm this has knowledge of direction, ought to be a stack method */ + assert(topOfStack >= istate->stack_limit(), "Stack overrun"); + assert(topOfStack < istate->stack_base(), "Stack underrun"); + +#ifdef USELABELS + DISPATCH(opcode); +#else + switch (opcode) +#endif + { + CASE(_nop): + UPDATE_PC_AND_CONTINUE(1); + + /* Push miscellaneous constants onto the stack. */ + + CASE(_aconst_null): + SET_STACK_OBJECT(NULL, 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + +#undef OPC_CONST_n +#define OPC_CONST_n(opcode, const_type, value) \ + CASE(opcode): \ + SET_STACK_ ## const_type(value, 0); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + + OPC_CONST_n(_iconst_m1, INT, -1); + OPC_CONST_n(_iconst_0, INT, 0); + OPC_CONST_n(_iconst_1, INT, 1); + OPC_CONST_n(_iconst_2, INT, 2); + OPC_CONST_n(_iconst_3, INT, 3); + OPC_CONST_n(_iconst_4, INT, 4); + OPC_CONST_n(_iconst_5, INT, 5); + OPC_CONST_n(_fconst_0, FLOAT, 0.0); + OPC_CONST_n(_fconst_1, FLOAT, 1.0); + OPC_CONST_n(_fconst_2, FLOAT, 2.0); + +#undef OPC_CONST2_n +#define OPC_CONST2_n(opcname, value, key, kind) \ + CASE(_##opcname): \ + { \ + SET_STACK_ ## kind(VM##key##Const##value(), 1); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); \ + } + OPC_CONST2_n(dconst_0, Zero, double, DOUBLE); + OPC_CONST2_n(dconst_1, One, double, DOUBLE); + OPC_CONST2_n(lconst_0, Zero, long, LONG); + OPC_CONST2_n(lconst_1, One, long, LONG); + + /* Load constant from constant pool: */ + + /* Push a 1-byte signed integer value onto the stack. */ + CASE(_bipush): + SET_STACK_INT((jbyte)(pc[1]), 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1); + + /* Push a 2-byte signed integer constant onto the stack. */ + CASE(_sipush): + SET_STACK_INT((int16_t)Bytes::get_Java_u2(pc + 1), 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1); + + /* load from local variable */ + + CASE(_aload): + SET_STACK_OBJECT(LOCALS_OBJECT(pc[1]), 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1); + + CASE(_iload): + CASE(_fload): + SET_STACK_SLOT(LOCALS_SLOT(pc[1]), 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1); + + CASE(_lload): + SET_STACK_LONG_FROM_ADDR(LOCALS_LONG_AT(pc[1]), 1); + UPDATE_PC_AND_TOS_AND_CONTINUE(2, 2); + + CASE(_dload): + SET_STACK_DOUBLE_FROM_ADDR(LOCALS_DOUBLE_AT(pc[1]), 1); + UPDATE_PC_AND_TOS_AND_CONTINUE(2, 2); + +#undef OPC_LOAD_n +#define OPC_LOAD_n(num) \ + CASE(_aload_##num): \ + SET_STACK_OBJECT(LOCALS_OBJECT(num), 0); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); \ + \ + CASE(_iload_##num): \ + CASE(_fload_##num): \ + SET_STACK_SLOT(LOCALS_SLOT(num), 0); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); \ + \ + CASE(_lload_##num): \ + SET_STACK_LONG_FROM_ADDR(LOCALS_LONG_AT(num), 1); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); \ + CASE(_dload_##num): \ + SET_STACK_DOUBLE_FROM_ADDR(LOCALS_DOUBLE_AT(num), 1); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); + + OPC_LOAD_n(0); + OPC_LOAD_n(1); + OPC_LOAD_n(2); + OPC_LOAD_n(3); + + /* store to a local variable */ + + CASE(_astore): + astore(topOfStack, -1, locals, pc[1]); + UPDATE_PC_AND_TOS_AND_CONTINUE(2, -1); + + CASE(_istore): + CASE(_fstore): + SET_LOCALS_SLOT(STACK_SLOT(-1), pc[1]); + UPDATE_PC_AND_TOS_AND_CONTINUE(2, -1); + + CASE(_lstore): + SET_LOCALS_LONG(STACK_LONG(-1), pc[1]); + UPDATE_PC_AND_TOS_AND_CONTINUE(2, -2); + + CASE(_dstore): + SET_LOCALS_DOUBLE(STACK_DOUBLE(-1), pc[1]); + UPDATE_PC_AND_TOS_AND_CONTINUE(2, -2); + + CASE(_wide): { + uint16_t reg = Bytes::get_Java_u2(pc + 2); + + opcode = pc[1]; + switch(opcode) { + case Bytecodes::_aload: + SET_STACK_OBJECT(LOCALS_OBJECT(reg), 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(4, 1); + + case Bytecodes::_iload: + case Bytecodes::_fload: + SET_STACK_SLOT(LOCALS_SLOT(reg), 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(4, 1); + + case Bytecodes::_lload: + SET_STACK_LONG_FROM_ADDR(LOCALS_LONG_AT(reg), 1); + UPDATE_PC_AND_TOS_AND_CONTINUE(4, 2); + + case Bytecodes::_dload: + SET_STACK_DOUBLE_FROM_ADDR(LOCALS_LONG_AT(reg), 1); + UPDATE_PC_AND_TOS_AND_CONTINUE(4, 2); + + case Bytecodes::_astore: + astore(topOfStack, -1, locals, reg); + UPDATE_PC_AND_TOS_AND_CONTINUE(4, -1); + + case Bytecodes::_istore: + case Bytecodes::_fstore: + SET_LOCALS_SLOT(STACK_SLOT(-1), reg); + UPDATE_PC_AND_TOS_AND_CONTINUE(4, -1); + + case Bytecodes::_lstore: + SET_LOCALS_LONG(STACK_LONG(-1), reg); + UPDATE_PC_AND_TOS_AND_CONTINUE(4, -2); + + case Bytecodes::_dstore: + SET_LOCALS_DOUBLE(STACK_DOUBLE(-1), reg); + UPDATE_PC_AND_TOS_AND_CONTINUE(4, -2); + + case Bytecodes::_iinc: { + int16_t offset = (int16_t)Bytes::get_Java_u2(pc+4); + // Be nice to see what this generates.... QQQ + SET_LOCALS_INT(LOCALS_INT(reg) + offset, reg); + UPDATE_PC_AND_CONTINUE(6); + } + case Bytecodes::_ret: + pc = istate->method()->code_base() + (intptr_t)(LOCALS_ADDR(reg)); + UPDATE_PC_AND_CONTINUE(0); + default: + VM_JAVA_ERROR(vmSymbols::java_lang_InternalError(), "undefined opcode"); + } + } + + +#undef OPC_STORE_n +#define OPC_STORE_n(num) \ + CASE(_astore_##num): \ + astore(topOfStack, -1, locals, num); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); \ + CASE(_istore_##num): \ + CASE(_fstore_##num): \ + SET_LOCALS_SLOT(STACK_SLOT(-1), num); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); + + OPC_STORE_n(0); + OPC_STORE_n(1); + OPC_STORE_n(2); + OPC_STORE_n(3); + +#undef OPC_DSTORE_n +#define OPC_DSTORE_n(num) \ + CASE(_dstore_##num): \ + SET_LOCALS_DOUBLE(STACK_DOUBLE(-1), num); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -2); \ + CASE(_lstore_##num): \ + SET_LOCALS_LONG(STACK_LONG(-1), num); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -2); + + OPC_DSTORE_n(0); + OPC_DSTORE_n(1); + OPC_DSTORE_n(2); + OPC_DSTORE_n(3); + + /* stack pop, dup, and insert opcodes */ + + + CASE(_pop): /* Discard the top item on the stack */ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); + + + CASE(_pop2): /* Discard the top 2 items on the stack */ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -2); + + + CASE(_dup): /* Duplicate the top item on the stack */ + dup(topOfStack); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + + CASE(_dup2): /* Duplicate the top 2 items on the stack */ + dup2(topOfStack); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); + + CASE(_dup_x1): /* insert top word two down */ + dup_x1(topOfStack); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + + CASE(_dup_x2): /* insert top word three down */ + dup_x2(topOfStack); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + + CASE(_dup2_x1): /* insert top 2 slots three down */ + dup2_x1(topOfStack); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); + + CASE(_dup2_x2): /* insert top 2 slots four down */ + dup2_x2(topOfStack); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); + + CASE(_swap): { /* swap top two elements on the stack */ + swap(topOfStack); + UPDATE_PC_AND_CONTINUE(1); + } + + /* Perform various binary integer operations */ + +#undef OPC_INT_BINARY +#define OPC_INT_BINARY(opcname, opname, test) \ + CASE(_i##opcname): \ + if (test && (STACK_INT(-1) == 0)) { \ + VM_JAVA_ERROR(vmSymbols::java_lang_ArithmeticException(), \ + "/ by int zero"); \ + } \ + SET_STACK_INT(VMint##opname(STACK_INT(-2), \ + STACK_INT(-1)), \ + -2); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); \ + CASE(_l##opcname): \ + { \ + if (test) { \ + jlong l1 = STACK_LONG(-1); \ + if (VMlongEqz(l1)) { \ + VM_JAVA_ERROR(vmSymbols::java_lang_ArithmeticException(), \ + "/ by long zero"); \ + } \ + } \ + /* First long at (-1,-2) next long at (-3,-4) */ \ + SET_STACK_LONG(VMlong##opname(STACK_LONG(-3), \ + STACK_LONG(-1)), \ + -3); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -2); \ + } + + OPC_INT_BINARY(add, Add, 0); + OPC_INT_BINARY(sub, Sub, 0); + OPC_INT_BINARY(mul, Mul, 0); + OPC_INT_BINARY(and, And, 0); + OPC_INT_BINARY(or, Or, 0); + OPC_INT_BINARY(xor, Xor, 0); + OPC_INT_BINARY(div, Div, 1); + OPC_INT_BINARY(rem, Rem, 1); + + + /* Perform various binary floating number operations */ + /* On some machine/platforms/compilers div zero check can be implicit */ + +#undef OPC_FLOAT_BINARY +#define OPC_FLOAT_BINARY(opcname, opname) \ + CASE(_d##opcname): { \ + SET_STACK_DOUBLE(VMdouble##opname(STACK_DOUBLE(-3), \ + STACK_DOUBLE(-1)), \ + -3); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -2); \ + } \ + CASE(_f##opcname): \ + SET_STACK_FLOAT(VMfloat##opname(STACK_FLOAT(-2), \ + STACK_FLOAT(-1)), \ + -2); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); + + + OPC_FLOAT_BINARY(add, Add); + OPC_FLOAT_BINARY(sub, Sub); + OPC_FLOAT_BINARY(mul, Mul); + OPC_FLOAT_BINARY(div, Div); + OPC_FLOAT_BINARY(rem, Rem); + + /* Shift operations + * Shift left int and long: ishl, lshl + * Logical shift right int and long w/zero extension: iushr, lushr + * Arithmetic shift right int and long w/sign extension: ishr, lshr + */ + +#undef OPC_SHIFT_BINARY +#define OPC_SHIFT_BINARY(opcname, opname) \ + CASE(_i##opcname): \ + SET_STACK_INT(VMint##opname(STACK_INT(-2), \ + STACK_INT(-1)), \ + -2); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); \ + CASE(_l##opcname): \ + { \ + SET_STACK_LONG(VMlong##opname(STACK_LONG(-2), \ + STACK_INT(-1)), \ + -2); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); \ + } + + OPC_SHIFT_BINARY(shl, Shl); + OPC_SHIFT_BINARY(shr, Shr); + OPC_SHIFT_BINARY(ushr, Ushr); + + /* Increment local variable by constant */ + CASE(_iinc): + { + // locals[pc[1]].j.i += (jbyte)(pc[2]); + SET_LOCALS_INT(LOCALS_INT(pc[1]) + (jbyte)(pc[2]), pc[1]); + UPDATE_PC_AND_CONTINUE(3); + } + + /* negate the value on the top of the stack */ + + CASE(_ineg): + SET_STACK_INT(VMintNeg(STACK_INT(-1)), -1); + UPDATE_PC_AND_CONTINUE(1); + + CASE(_fneg): + SET_STACK_FLOAT(VMfloatNeg(STACK_FLOAT(-1)), -1); + UPDATE_PC_AND_CONTINUE(1); + + CASE(_lneg): + { + SET_STACK_LONG(VMlongNeg(STACK_LONG(-1)), -1); + UPDATE_PC_AND_CONTINUE(1); + } + + CASE(_dneg): + { + SET_STACK_DOUBLE(VMdoubleNeg(STACK_DOUBLE(-1)), -1); + UPDATE_PC_AND_CONTINUE(1); + } + + /* Conversion operations */ + + CASE(_i2f): /* convert top of stack int to float */ + SET_STACK_FLOAT(VMint2Float(STACK_INT(-1)), -1); + UPDATE_PC_AND_CONTINUE(1); + + CASE(_i2l): /* convert top of stack int to long */ + { + // this is ugly QQQ + jlong r = VMint2Long(STACK_INT(-1)); + MORE_STACK(-1); // Pop + SET_STACK_LONG(r, 1); + + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); + } + + CASE(_i2d): /* convert top of stack int to double */ + { + // this is ugly QQQ (why cast to jlong?? ) + jdouble r = (jlong)STACK_INT(-1); + MORE_STACK(-1); // Pop + SET_STACK_DOUBLE(r, 1); + + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); + } + + CASE(_l2i): /* convert top of stack long to int */ + { + jint r = VMlong2Int(STACK_LONG(-1)); + MORE_STACK(-2); // Pop + SET_STACK_INT(r, 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + } + + CASE(_l2f): /* convert top of stack long to float */ + { + jlong r = STACK_LONG(-1); + MORE_STACK(-2); // Pop + SET_STACK_FLOAT(VMlong2Float(r), 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + } + + CASE(_l2d): /* convert top of stack long to double */ + { + jlong r = STACK_LONG(-1); + MORE_STACK(-2); // Pop + SET_STACK_DOUBLE(VMlong2Double(r), 1); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); + } + + CASE(_f2i): /* Convert top of stack float to int */ + SET_STACK_INT(SharedRuntime::f2i(STACK_FLOAT(-1)), -1); + UPDATE_PC_AND_CONTINUE(1); + + CASE(_f2l): /* convert top of stack float to long */ + { + jlong r = SharedRuntime::f2l(STACK_FLOAT(-1)); + MORE_STACK(-1); // POP + SET_STACK_LONG(r, 1); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); + } + + CASE(_f2d): /* convert top of stack float to double */ + { + jfloat f; + jdouble r; + f = STACK_FLOAT(-1); +#ifdef IA64 + // IA64 gcc bug + r = ( f == 0.0f ) ? (jdouble) f : (jdouble) f + ia64_double_zero; +#else + r = (jdouble) f; +#endif + MORE_STACK(-1); // POP + SET_STACK_DOUBLE(r, 1); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); + } + + CASE(_d2i): /* convert top of stack double to int */ + { + jint r1 = SharedRuntime::d2i(STACK_DOUBLE(-1)); + MORE_STACK(-2); + SET_STACK_INT(r1, 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + } + + CASE(_d2f): /* convert top of stack double to float */ + { + jfloat r1 = VMdouble2Float(STACK_DOUBLE(-1)); + MORE_STACK(-2); + SET_STACK_FLOAT(r1, 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + } + + CASE(_d2l): /* convert top of stack double to long */ + { + jlong r1 = SharedRuntime::d2l(STACK_DOUBLE(-1)); + MORE_STACK(-2); + SET_STACK_LONG(r1, 1); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); + } + + CASE(_i2b): + SET_STACK_INT(VMint2Byte(STACK_INT(-1)), -1); + UPDATE_PC_AND_CONTINUE(1); + + CASE(_i2c): + SET_STACK_INT(VMint2Char(STACK_INT(-1)), -1); + UPDATE_PC_AND_CONTINUE(1); + + CASE(_i2s): + SET_STACK_INT(VMint2Short(STACK_INT(-1)), -1); + UPDATE_PC_AND_CONTINUE(1); + + /* comparison operators */ + + +#define COMPARISON_OP(name, comparison) \ + CASE(_if_icmp##name): { \ + int skip = (STACK_INT(-2) comparison STACK_INT(-1)) \ + ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ + address branch_pc = pc; \ + UPDATE_PC_AND_TOS(skip, -2); \ + DO_BACKEDGE_CHECKS(skip, branch_pc); \ + CONTINUE; \ + } \ + CASE(_if##name): { \ + int skip = (STACK_INT(-1) comparison 0) \ + ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ + address branch_pc = pc; \ + UPDATE_PC_AND_TOS(skip, -1); \ + DO_BACKEDGE_CHECKS(skip, branch_pc); \ + CONTINUE; \ + } + +#define COMPARISON_OP2(name, comparison) \ + COMPARISON_OP(name, comparison) \ + CASE(_if_acmp##name): { \ + int skip = (STACK_OBJECT(-2) comparison STACK_OBJECT(-1)) \ + ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ + address branch_pc = pc; \ + UPDATE_PC_AND_TOS(skip, -2); \ + DO_BACKEDGE_CHECKS(skip, branch_pc); \ + CONTINUE; \ + } + +#define NULL_COMPARISON_NOT_OP(name) \ + CASE(_if##name): { \ + int skip = (!(STACK_OBJECT(-1) == 0)) \ + ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ + address branch_pc = pc; \ + UPDATE_PC_AND_TOS(skip, -1); \ + DO_BACKEDGE_CHECKS(skip, branch_pc); \ + CONTINUE; \ + } + +#define NULL_COMPARISON_OP(name) \ + CASE(_if##name): { \ + int skip = ((STACK_OBJECT(-1) == 0)) \ + ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ + address branch_pc = pc; \ + UPDATE_PC_AND_TOS(skip, -1); \ + DO_BACKEDGE_CHECKS(skip, branch_pc); \ + CONTINUE; \ + } + COMPARISON_OP(lt, <); + COMPARISON_OP(gt, >); + COMPARISON_OP(le, <=); + COMPARISON_OP(ge, >=); + COMPARISON_OP2(eq, ==); /* include ref comparison */ + COMPARISON_OP2(ne, !=); /* include ref comparison */ + NULL_COMPARISON_OP(null); + NULL_COMPARISON_NOT_OP(nonnull); + + /* Goto pc at specified offset in switch table. */ + + CASE(_tableswitch): { + jint* lpc = (jint*)VMalignWordUp(pc+1); + int32_t key = STACK_INT(-1); + int32_t low = Bytes::get_Java_u4((address)&lpc[1]); + int32_t high = Bytes::get_Java_u4((address)&lpc[2]); + int32_t skip; + key -= low; + skip = ((uint32_t) key > (uint32_t)(high - low)) + ? Bytes::get_Java_u4((address)&lpc[0]) + : Bytes::get_Java_u4((address)&lpc[key + 3]); + // Does this really need a full backedge check (osr?) + address branch_pc = pc; + UPDATE_PC_AND_TOS(skip, -1); + DO_BACKEDGE_CHECKS(skip, branch_pc); + CONTINUE; + } + + /* Goto pc whose table entry matches specified key */ + + CASE(_lookupswitch): { + jint* lpc = (jint*)VMalignWordUp(pc+1); + int32_t key = STACK_INT(-1); + int32_t skip = Bytes::get_Java_u4((address) lpc); /* default amount */ + int32_t npairs = Bytes::get_Java_u4((address) &lpc[1]); + while (--npairs >= 0) { + lpc += 2; + if (key == (int32_t)Bytes::get_Java_u4((address)lpc)) { + skip = Bytes::get_Java_u4((address)&lpc[1]); + break; + } + } + address branch_pc = pc; + UPDATE_PC_AND_TOS(skip, -1); + DO_BACKEDGE_CHECKS(skip, branch_pc); + CONTINUE; + } + + CASE(_fcmpl): + CASE(_fcmpg): + { + SET_STACK_INT(VMfloatCompare(STACK_FLOAT(-2), + STACK_FLOAT(-1), + (opcode == Bytecodes::_fcmpl ? -1 : 1)), + -2); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); + } + + CASE(_dcmpl): + CASE(_dcmpg): + { + int r = VMdoubleCompare(STACK_DOUBLE(-3), + STACK_DOUBLE(-1), + (opcode == Bytecodes::_dcmpl ? -1 : 1)); + MORE_STACK(-4); // Pop + SET_STACK_INT(r, 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + } + + CASE(_lcmp): + { + int r = VMlongCompare(STACK_LONG(-3), STACK_LONG(-1)); + MORE_STACK(-4); + SET_STACK_INT(r, 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); + } + + + /* Return from a method */ + + CASE(_areturn): + CASE(_ireturn): + CASE(_freturn): + { + // Allow a safepoint before returning to frame manager. + SAFEPOINT; + + goto handle_return; + } + + CASE(_lreturn): + CASE(_dreturn): + { + // Allow a safepoint before returning to frame manager. + SAFEPOINT; + goto handle_return; + } + + CASE(_return_register_finalizer): { + + oop rcvr = LOCALS_OBJECT(0); + if (rcvr->klass()->klass_part()->has_finalizer()) { + CALL_VM(InterpreterRuntime::register_finalizer(THREAD, rcvr), handle_exception); + } + goto handle_return; + } + CASE(_return): { + + // Allow a safepoint before returning to frame manager. + SAFEPOINT; + goto handle_return; + } + + /* Array access byte-codes */ + + /* Every array access byte-code starts out like this */ +// arrayOopDesc* arrObj = (arrayOopDesc*)STACK_OBJECT(arrayOff); +#define ARRAY_INTRO(arrayOff) \ + arrayOop arrObj = (arrayOop)STACK_OBJECT(arrayOff); \ + jint index = STACK_INT(arrayOff + 1); \ + char message[jintAsStringSize]; \ + CHECK_NULL(arrObj); \ + if ((uint32_t)index >= (uint32_t)arrObj->length()) { \ + sprintf(message, "%d", index); \ + VM_JAVA_ERROR(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), \ + message); \ + } + + /* 32-bit loads. These handle conversion from < 32-bit types */ +#define ARRAY_LOADTO32(T, T2, format, stackRes, extra) \ + { \ + ARRAY_INTRO(-2); \ + extra; \ + SET_ ## stackRes(*(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)), \ + -2); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); \ + } + + /* 64-bit loads */ +#define ARRAY_LOADTO64(T,T2, stackRes, extra) \ + { \ + ARRAY_INTRO(-2); \ + SET_ ## stackRes(*(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)), -1); \ + extra; \ + UPDATE_PC_AND_CONTINUE(1); \ + } + + CASE(_iaload): + ARRAY_LOADTO32(T_INT, jint, "%d", STACK_INT, 0); + CASE(_faload): + ARRAY_LOADTO32(T_FLOAT, jfloat, "%f", STACK_FLOAT, 0); + CASE(_aaload): + ARRAY_LOADTO32(T_OBJECT, oop, INTPTR_FORMAT, STACK_OBJECT, 0); + CASE(_baload): + ARRAY_LOADTO32(T_BYTE, jbyte, "%d", STACK_INT, 0); + CASE(_caload): + ARRAY_LOADTO32(T_CHAR, jchar, "%d", STACK_INT, 0); + CASE(_saload): + ARRAY_LOADTO32(T_SHORT, jshort, "%d", STACK_INT, 0); + CASE(_laload): + ARRAY_LOADTO64(T_LONG, jlong, STACK_LONG, 0); + CASE(_daload): + ARRAY_LOADTO64(T_DOUBLE, jdouble, STACK_DOUBLE, 0); + + /* 32-bit stores. These handle conversion to < 32-bit types */ +#define ARRAY_STOREFROM32(T, T2, format, stackSrc, extra) \ + { \ + ARRAY_INTRO(-3); \ + extra; \ + *(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)) = stackSrc( -1); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -3); \ + } + + /* 64-bit stores */ +#define ARRAY_STOREFROM64(T, T2, stackSrc, extra) \ + { \ + ARRAY_INTRO(-4); \ + extra; \ + *(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)) = stackSrc( -1); \ + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -4); \ + } + + CASE(_iastore): + ARRAY_STOREFROM32(T_INT, jint, "%d", STACK_INT, 0); + CASE(_fastore): + ARRAY_STOREFROM32(T_FLOAT, jfloat, "%f", STACK_FLOAT, 0); + /* + * This one looks different because of the assignability check + */ + CASE(_aastore): { + oop rhsObject = STACK_OBJECT(-1); + ARRAY_INTRO( -3); + // arrObj, index are set + if (rhsObject != NULL) { + /* Check assignability of rhsObject into arrObj */ + klassOop rhsKlassOop = rhsObject->klass(); // EBX (subclass) + assert(arrObj->klass()->klass()->klass_part()->oop_is_objArrayKlass(), "Ack not an objArrayKlass"); + klassOop elemKlassOop = ((objArrayKlass*) arrObj->klass()->klass_part())->element_klass(); // superklass EAX + // + // Check for compatibilty. This check must not GC!! + // Seems way more expensive now that we must dispatch + // + if (rhsKlassOop != elemKlassOop && !rhsKlassOop->klass_part()->is_subtype_of(elemKlassOop)) { // ebx->is... + VM_JAVA_ERROR(vmSymbols::java_lang_ArrayStoreException(), ""); + } + } + oop* elem_loc = (oop*)(((address) arrObj->base(T_OBJECT)) + index * sizeof(oop)); + // *(oop*)(((address) arrObj->base(T_OBJECT)) + index * sizeof(oop)) = rhsObject; + *elem_loc = rhsObject; + // Mark the card + OrderAccess::release_store(&BYTE_MAP_BASE[(uintptr_t)elem_loc >> CardTableModRefBS::card_shift], 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -3); + } + CASE(_bastore): + ARRAY_STOREFROM32(T_BYTE, jbyte, "%d", STACK_INT, 0); + CASE(_castore): + ARRAY_STOREFROM32(T_CHAR, jchar, "%d", STACK_INT, 0); + CASE(_sastore): + ARRAY_STOREFROM32(T_SHORT, jshort, "%d", STACK_INT, 0); + CASE(_lastore): + ARRAY_STOREFROM64(T_LONG, jlong, STACK_LONG, 0); + CASE(_dastore): + ARRAY_STOREFROM64(T_DOUBLE, jdouble, STACK_DOUBLE, 0); + + CASE(_arraylength): + { + arrayOop ary = (arrayOop) STACK_OBJECT(-1); + CHECK_NULL(ary); + SET_STACK_INT(ary->length(), -1); + UPDATE_PC_AND_CONTINUE(1); + } + + /* monitorenter and monitorexit for locking/unlocking an object */ + + CASE(_monitorenter): { + oop lockee = STACK_OBJECT(-1); + // derefing's lockee ought to provoke implicit null check + CHECK_NULL(lockee); + // find a free monitor or one already allocated for this object + // if we find a matching object then we need a new monitor + // since this is recursive enter + BasicObjectLock* limit = istate->monitor_base(); + BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base(); + BasicObjectLock* entry = NULL; + while (most_recent != limit ) { + if (most_recent->obj() == NULL) entry = most_recent; + else if (most_recent->obj() == lockee) break; + most_recent++; + } + if (entry != NULL) { + entry->set_obj(lockee); + markOop displaced = lockee->mark()->set_unlocked(); + entry->lock()->set_displaced_header(displaced); + if (Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) { + // Is it simple recursive case? + if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { + entry->lock()->set_displaced_header(NULL); + } else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); + } + } + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); + } else { + istate->set_msg(more_monitors); + UPDATE_PC_AND_RETURN(0); // Re-execute + } + } + + CASE(_monitorexit): { + oop lockee = STACK_OBJECT(-1); + CHECK_NULL(lockee); + // derefing's lockee ought to provoke implicit null check + // find our monitor slot + BasicObjectLock* limit = istate->monitor_base(); + BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base(); + while (most_recent != limit ) { + if ((most_recent)->obj() == lockee) { + BasicLock* lock = most_recent->lock(); + markOop header = lock->displaced_header(); + most_recent->set_obj(NULL); + // If it isn't recursive we either must swap old header or call the runtime + if (header != NULL) { + if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) { + // restore object for the slow case + most_recent->set_obj(lockee); + CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception); + } + } + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); + } + most_recent++; + } + // Need to throw illegal monitor state exception + CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception); + // Should never reach here... + assert(false, "Should have thrown illegal monitor exception"); + } + + /* All of the non-quick opcodes. */ + + /* -Set clobbersCpIndex true if the quickened opcode clobbers the + * constant pool index in the instruction. + */ + CASE(_getfield): + CASE(_getstatic): + { + u2 index; + ConstantPoolCacheEntry* cache; + index = Bytes::get_native_u2(pc+1); + + // QQQ Need to make this as inlined as possible. Probably need to + // split all the bytecode cases out so c++ compiler has a chance + // for constant prop to fold everything possible away. + + cache = cp->entry_at(index); + if (!cache->is_resolved((Bytecodes::Code)opcode)) { + CALL_VM(InterpreterRuntime::resolve_get_put(THREAD, (Bytecodes::Code)opcode), + handle_exception); + cache = cp->entry_at(index); + } + +#ifdef VM_JVMTI + if (_jvmti_interp_events) { + int *count_addr; + oop obj; + // Check to see if a field modification watch has been set + // before we take the time to call into the VM. + count_addr = (int *)JvmtiExport::get_field_access_count_addr(); + if ( *count_addr > 0 ) { + if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { + obj = (oop)NULL; + } else { + obj = (oop) STACK_OBJECT(-1); + } + CALL_VM(InterpreterRuntime::post_field_access(THREAD, + obj, + cache), + handle_exception); + } + } +#endif /* VM_JVMTI */ + + oop obj; + if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { + obj = (oop) cache->f1(); + MORE_STACK(1); // Assume single slot push + } else { + obj = (oop) STACK_OBJECT(-1); + CHECK_NULL(obj); + } + + // + // Now store the result on the stack + // + TosState tos_type = cache->flag_state(); + int field_offset = cache->f2(); + if (cache->is_volatile()) { + if (tos_type == atos) { + SET_STACK_OBJECT(obj->obj_field_acquire(field_offset), -1); + } else if (tos_type == itos) { + SET_STACK_INT(obj->int_field_acquire(field_offset), -1); + } else if (tos_type == ltos) { + SET_STACK_LONG(obj->long_field_acquire(field_offset), 0); + MORE_STACK(1); + } else if (tos_type == btos) { + SET_STACK_INT(obj->byte_field_acquire(field_offset), -1); + } else if (tos_type == ctos) { + SET_STACK_INT(obj->char_field_acquire(field_offset), -1); + } else if (tos_type == stos) { + SET_STACK_INT(obj->short_field_acquire(field_offset), -1); + } else if (tos_type == ftos) { + SET_STACK_FLOAT(obj->float_field_acquire(field_offset), -1); + } else { + SET_STACK_DOUBLE(obj->double_field_acquire(field_offset), 0); + MORE_STACK(1); + } + } else { + if (tos_type == atos) { + SET_STACK_OBJECT(obj->obj_field(field_offset), -1); + } else if (tos_type == itos) { + SET_STACK_INT(obj->int_field(field_offset), -1); + } else if (tos_type == ltos) { + SET_STACK_LONG(obj->long_field(field_offset), 0); + MORE_STACK(1); + } else if (tos_type == btos) { + SET_STACK_INT(obj->byte_field(field_offset), -1); + } else if (tos_type == ctos) { + SET_STACK_INT(obj->char_field(field_offset), -1); + } else if (tos_type == stos) { + SET_STACK_INT(obj->short_field(field_offset), -1); + } else if (tos_type == ftos) { + SET_STACK_FLOAT(obj->float_field(field_offset), -1); + } else { + SET_STACK_DOUBLE(obj->double_field(field_offset), 0); + MORE_STACK(1); + } + } + + UPDATE_PC_AND_CONTINUE(3); + } + + CASE(_putfield): + CASE(_putstatic): + { + u2 index = Bytes::get_native_u2(pc+1); + ConstantPoolCacheEntry* cache = cp->entry_at(index); + if (!cache->is_resolved((Bytecodes::Code)opcode)) { + CALL_VM(InterpreterRuntime::resolve_get_put(THREAD, (Bytecodes::Code)opcode), + handle_exception); + cache = cp->entry_at(index); + } + +#ifdef VM_JVMTI + if (_jvmti_interp_events) { + int *count_addr; + oop obj; + // Check to see if a field modification watch has been set + // before we take the time to call into the VM. + count_addr = (int *)JvmtiExport::get_field_modification_count_addr(); + if ( *count_addr > 0 ) { + if ((Bytecodes::Code)opcode == Bytecodes::_putstatic) { + obj = (oop)NULL; + } + else { + if (cache->is_long() || cache->is_double()) { + obj = (oop) STACK_OBJECT(-3); + } else { + obj = (oop) STACK_OBJECT(-2); + } + } + + CALL_VM(InterpreterRuntime::post_field_modification(THREAD, + obj, + cache, + (jvalue *)STACK_SLOT(-1)), + handle_exception); + } + } +#endif /* VM_JVMTI */ + + // QQQ Need to make this as inlined as possible. Probably need to split all the bytecode cases + // out so c++ compiler has a chance for constant prop to fold everything possible away. + + oop obj; + int count; + TosState tos_type = cache->flag_state(); + + count = -1; + if (tos_type == ltos || tos_type == dtos) { + --count; + } + if ((Bytecodes::Code)opcode == Bytecodes::_putstatic) { + obj = (oop) cache->f1(); + } else { + --count; + obj = (oop) STACK_OBJECT(count); + CHECK_NULL(obj); + } + + // + // Now store the result + // + int field_offset = cache->f2(); + if (cache->is_volatile()) { + if (tos_type == itos) { + obj->release_int_field_put(field_offset, STACK_INT(-1)); + } else if (tos_type == atos) { + obj->release_obj_field_put(field_offset, STACK_OBJECT(-1)); + OrderAccess::release_store(&BYTE_MAP_BASE[(uintptr_t)obj >> CardTableModRefBS::card_shift], 0); + } else if (tos_type == btos) { + obj->release_byte_field_put(field_offset, STACK_INT(-1)); + } else if (tos_type == ltos) { + obj->release_long_field_put(field_offset, STACK_LONG(-1)); + } else if (tos_type == ctos) { + obj->release_char_field_put(field_offset, STACK_INT(-1)); + } else if (tos_type == stos) { + obj->release_short_field_put(field_offset, STACK_INT(-1)); + } else if (tos_type == ftos) { + obj->release_float_field_put(field_offset, STACK_FLOAT(-1)); + } else { + obj->release_double_field_put(field_offset, STACK_DOUBLE(-1)); + } + OrderAccess::storeload(); + } else { + if (tos_type == itos) { + obj->int_field_put(field_offset, STACK_INT(-1)); + } else if (tos_type == atos) { + obj->obj_field_put(field_offset, STACK_OBJECT(-1)); + OrderAccess::release_store(&BYTE_MAP_BASE[(uintptr_t)obj >> CardTableModRefBS::card_shift], 0); + } else if (tos_type == btos) { + obj->byte_field_put(field_offset, STACK_INT(-1)); + } else if (tos_type == ltos) { + obj->long_field_put(field_offset, STACK_LONG(-1)); + } else if (tos_type == ctos) { + obj->char_field_put(field_offset, STACK_INT(-1)); + } else if (tos_type == stos) { + obj->short_field_put(field_offset, STACK_INT(-1)); + } else if (tos_type == ftos) { + obj->float_field_put(field_offset, STACK_FLOAT(-1)); + } else { + obj->double_field_put(field_offset, STACK_DOUBLE(-1)); + } + } + + UPDATE_PC_AND_TOS_AND_CONTINUE(3, count); + } + + CASE(_new): { + u2 index = Bytes::get_Java_u2(pc+1); + constantPoolOop constants = istate->method()->constants(); + if (!constants->tag_at(index).is_unresolved_klass()) { + // Make sure klass is initialized and doesn't have a finalizer + oop entry = (klassOop) *constants->obj_at_addr(index); + assert(entry->is_klass(), "Should be resolved klass"); + klassOop k_entry = (klassOop) entry; + assert(k_entry->klass_part()->oop_is_instance(), "Should be instanceKlass"); + instanceKlass* ik = (instanceKlass*) k_entry->klass_part(); + if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) { + size_t obj_size = ik->size_helper(); + oop result = NULL; + // If the TLAB isn't pre-zeroed then we'll have to do it + bool need_zero = !ZeroTLAB; + if (UseTLAB) { + result = (oop) THREAD->tlab().allocate(obj_size); + } + if (result == NULL) { + need_zero = true; + // Try allocate in shared eden + retry: + HeapWord* compare_to = *Universe::heap()->top_addr(); + HeapWord* new_top = compare_to + obj_size; + if (new_top <= *Universe::heap()->end_addr()) { + if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) { + goto retry; + } + result = (oop) compare_to; + } + } + if (result != NULL) { + // Initialize object (if nonzero size and need) and then the header + if (need_zero ) { + HeapWord* to_zero = (HeapWord*) result + sizeof(oopDesc) / oopSize; + obj_size -= sizeof(oopDesc) / oopSize; + if (obj_size > 0 ) { + memset(to_zero, 0, obj_size * HeapWordSize); + } + } + if (UseBiasedLocking) { + result->set_mark(ik->prototype_header()); + } else { + result->set_mark(markOopDesc::prototype()); + } + result->set_klass(k_entry); + SET_STACK_OBJECT(result, 0); + UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1); + } + } + } + // Slow case allocation + CALL_VM(InterpreterRuntime::_new(THREAD, METHOD->constants(), index), + handle_exception); + SET_STACK_OBJECT(THREAD->vm_result(), 0); + THREAD->set_vm_result(NULL); + UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1); + } + CASE(_anewarray): { + u2 index = Bytes::get_Java_u2(pc+1); + jint size = STACK_INT(-1); + CALL_VM(InterpreterRuntime::anewarray(THREAD, METHOD->constants(), index, size), + handle_exception); + SET_STACK_OBJECT(THREAD->vm_result(), -1); + THREAD->set_vm_result(NULL); + UPDATE_PC_AND_CONTINUE(3); + } + CASE(_multianewarray): { + jint dims = *(pc+3); + jint size = STACK_INT(-1); + // stack grows down, dimensions are up! + jint *dimarray = + (jint*)&topOfStack[dims * Interpreter::stackElementWords()+ + Interpreter::stackElementWords()-1]; + //adjust pointer to start of stack element + CALL_VM(InterpreterRuntime::multianewarray(THREAD, dimarray), + handle_exception); + SET_STACK_OBJECT(THREAD->vm_result(), -dims); + THREAD->set_vm_result(NULL); + UPDATE_PC_AND_TOS_AND_CONTINUE(4, -(dims-1)); + } + CASE(_checkcast): + if (STACK_OBJECT(-1) != NULL) { + u2 index = Bytes::get_Java_u2(pc+1); + if (ProfileInterpreter) { + // needs Profile_checkcast QQQ + ShouldNotReachHere(); + } + // Constant pool may have actual klass or unresolved klass. If it is + // unresolved we must resolve it + if (METHOD->constants()->tag_at(index).is_unresolved_klass()) { + CALL_VM(InterpreterRuntime::quicken_io_cc(THREAD), handle_exception); + } + klassOop klassOf = (klassOop) *(METHOD->constants()->obj_at_addr(index)); + klassOop objKlassOop = STACK_OBJECT(-1)->klass(); //ebx + // + // Check for compatibilty. This check must not GC!! + // Seems way more expensive now that we must dispatch + // + if (objKlassOop != klassOf && + !objKlassOop->klass_part()->is_subtype_of(klassOf)) { + ResourceMark rm(THREAD); + const char* objName = Klass::cast(objKlassOop)->external_name(); + const char* klassName = Klass::cast(klassOf)->external_name(); + char* message = SharedRuntime::generate_class_cast_message( + objName, klassName); + VM_JAVA_ERROR(vmSymbols::java_lang_ClassCastException(), message); + } + } else { + if (UncommonNullCast) { +// istate->method()->set_null_cast_seen(); +// [RGV] Not sure what to do here! + + } + } + UPDATE_PC_AND_CONTINUE(3); + + CASE(_instanceof): + if (STACK_OBJECT(-1) == NULL) { + SET_STACK_INT(0, -1); + } else { + u2 index = Bytes::get_Java_u2(pc+1); + // Constant pool may have actual klass or unresolved klass. If it is + // unresolved we must resolve it + if (METHOD->constants()->tag_at(index).is_unresolved_klass()) { + CALL_VM(InterpreterRuntime::quicken_io_cc(THREAD), handle_exception); + } + klassOop klassOf = (klassOop) *(METHOD->constants()->obj_at_addr(index)); + klassOop objKlassOop = STACK_OBJECT(-1)->klass(); + // + // Check for compatibilty. This check must not GC!! + // Seems way more expensive now that we must dispatch + // + if ( objKlassOop == klassOf || objKlassOop->klass_part()->is_subtype_of(klassOf)) { + SET_STACK_INT(1, -1); + } else { + SET_STACK_INT(0, -1); + } + } + UPDATE_PC_AND_CONTINUE(3); + + CASE(_ldc_w): + CASE(_ldc): + { + u2 index; + bool wide = false; + int incr = 2; // frequent case + if (opcode == Bytecodes::_ldc) { + index = pc[1]; + } else { + index = Bytes::get_Java_u2(pc+1); + incr = 3; + wide = true; + } + + constantPoolOop constants = METHOD->constants(); + switch (constants->tag_at(index).value()) { + case JVM_CONSTANT_Integer: + SET_STACK_INT(constants->int_at(index), 0); + break; + + case JVM_CONSTANT_Float: + SET_STACK_FLOAT(constants->float_at(index), 0); + break; + + case JVM_CONSTANT_String: + SET_STACK_OBJECT(constants->resolved_string_at(index), 0); + break; + + case JVM_CONSTANT_Class: + SET_STACK_OBJECT(constants->resolved_klass_at(index)->klass_part()->java_mirror(), 0); + break; + + case JVM_CONSTANT_UnresolvedString: + case JVM_CONSTANT_UnresolvedClass: + case JVM_CONSTANT_UnresolvedClassInError: + CALL_VM(InterpreterRuntime::ldc(THREAD, wide), handle_exception); + SET_STACK_OBJECT(THREAD->vm_result(), 0); + THREAD->set_vm_result(NULL); + break; + +#if 0 + CASE(_fast_igetfield): + CASE(_fastagetfield): + CASE(_fast_aload_0): + CASE(_fast_iaccess_0): + CASE(__fast_aaccess_0): + CASE(_fast_linearswitch): + CASE(_fast_binaryswitch): + fatal("unsupported fast bytecode"); +#endif + + default: ShouldNotReachHere(); + } + UPDATE_PC_AND_TOS_AND_CONTINUE(incr, 1); + } + + CASE(_ldc2_w): + { + u2 index = Bytes::get_Java_u2(pc+1); + + constantPoolOop constants = METHOD->constants(); + switch (constants->tag_at(index).value()) { + + case JVM_CONSTANT_Long: + SET_STACK_LONG(constants->long_at(index), 1); + break; + + case JVM_CONSTANT_Double: + SET_STACK_DOUBLE(constants->double_at(index), 1); + break; + default: ShouldNotReachHere(); + } + UPDATE_PC_AND_TOS_AND_CONTINUE(3, 2); + } + + CASE(_invokeinterface): { + u2 index = Bytes::get_native_u2(pc+1); + + // QQQ Need to make this as inlined as possible. Probably need to split all the bytecode cases + // out so c++ compiler has a chance for constant prop to fold everything possible away. + + ConstantPoolCacheEntry* cache = cp->entry_at(index); + if (!cache->is_resolved((Bytecodes::Code)opcode)) { + CALL_VM(InterpreterRuntime::resolve_invoke(THREAD, (Bytecodes::Code)opcode), + handle_exception); + cache = cp->entry_at(index); + } + + istate->set_msg(call_method); + + // Special case of invokeinterface called for virtual method of + // java.lang.Object. See cpCacheOop.cpp for details. + // This code isn't produced by javac, but could be produced by + // another compliant java compiler. + if (cache->is_methodInterface()) { + methodOop callee; + CHECK_NULL(STACK_OBJECT(-(cache->parameter_size()))); + if (cache->is_vfinal()) { + callee = (methodOop) cache->f2(); + } else { + // get receiver + int parms = cache->parameter_size(); + // Same comments as invokevirtual apply here + instanceKlass* rcvrKlass = (instanceKlass*) + STACK_OBJECT(-parms)->klass()->klass_part(); + callee = (methodOop) rcvrKlass->start_of_vtable()[ cache->f2()]; + } + istate->set_callee(callee); + istate->set_callee_entry_point(callee->from_interpreted_entry()); +#ifdef VM_JVMTI + if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) { + istate->set_callee_entry_point(callee->interpreter_entry()); + } +#endif /* VM_JVMTI */ + istate->set_bcp_advance(5); + UPDATE_PC_AND_RETURN(0); // I'll be back... + } + + // this could definitely be cleaned up QQQ + methodOop callee; + klassOop iclass = (klassOop)cache->f1(); + // instanceKlass* interface = (instanceKlass*) iclass->klass_part(); + // get receiver + int parms = cache->parameter_size(); + oop rcvr = STACK_OBJECT(-parms); + CHECK_NULL(rcvr); + instanceKlass* int2 = (instanceKlass*) rcvr->klass()->klass_part(); + itableOffsetEntry* ki = (itableOffsetEntry*) int2->start_of_itable(); + int i; + for ( i = 0 ; i < int2->itable_length() ; i++, ki++ ) { + if (ki->interface_klass() == iclass) break; + } + // If the interface isn't found, this class doesn't implement this + // interface. The link resolver checks this but only for the first + // time this interface is called. + if (i == int2->itable_length()) { + VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), ""); + } + int mindex = cache->f2(); + itableMethodEntry* im = ki->first_method_entry(rcvr->klass()); + callee = im[mindex].method(); + if (callee == NULL) { + VM_JAVA_ERROR(vmSymbols::java_lang_AbstractMethodError(), ""); + } + + istate->set_callee(callee); + istate->set_callee_entry_point(callee->from_interpreted_entry()); +#ifdef VM_JVMTI + if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) { + istate->set_callee_entry_point(callee->interpreter_entry()); + } +#endif /* VM_JVMTI */ + istate->set_bcp_advance(5); + UPDATE_PC_AND_RETURN(0); // I'll be back... + } + + CASE(_invokevirtual): + CASE(_invokespecial): + CASE(_invokestatic): { + u2 index = Bytes::get_native_u2(pc+1); + + ConstantPoolCacheEntry* cache = cp->entry_at(index); + // QQQ Need to make this as inlined as possible. Probably need to split all the bytecode cases + // out so c++ compiler has a chance for constant prop to fold everything possible away. + + if (!cache->is_resolved((Bytecodes::Code)opcode)) { + CALL_VM(InterpreterRuntime::resolve_invoke(THREAD, (Bytecodes::Code)opcode), + handle_exception); + cache = cp->entry_at(index); + } + + istate->set_msg(call_method); + { + methodOop callee; + if ((Bytecodes::Code)opcode == Bytecodes::_invokevirtual) { + CHECK_NULL(STACK_OBJECT(-(cache->parameter_size()))); + if (cache->is_vfinal()) callee = (methodOop) cache->f2(); + else { + // get receiver + int parms = cache->parameter_size(); + // this works but needs a resourcemark and seems to create a vtable on every call: + // methodOop callee = rcvr->klass()->klass_part()->vtable()->method_at(cache->f2()); + // + // this fails with an assert + // instanceKlass* rcvrKlass = instanceKlass::cast(STACK_OBJECT(-parms)->klass()); + // but this works + instanceKlass* rcvrKlass = (instanceKlass*) STACK_OBJECT(-parms)->klass()->klass_part(); + /* + Executing this code in java.lang.String: + public String(char value[]) { + this.count = value.length; + this.value = (char[])value.clone(); + } + + a find on rcvr->klass()->klass_part() reports: + {type array char}{type array class} + - klass: {other class} + + but using instanceKlass::cast(STACK_OBJECT(-parms)->klass()) causes in assertion failure + because rcvr->klass()->klass_part()->oop_is_instance() == 0 + However it seems to have a vtable in the right location. Huh? + + */ + callee = (methodOop) rcvrKlass->start_of_vtable()[ cache->f2()]; + } + } else { + if ((Bytecodes::Code)opcode == Bytecodes::_invokespecial) { + CHECK_NULL(STACK_OBJECT(-(cache->parameter_size()))); + } + callee = (methodOop) cache->f1(); + } + + istate->set_callee(callee); + istate->set_callee_entry_point(callee->from_interpreted_entry()); +#ifdef VM_JVMTI + if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) { + istate->set_callee_entry_point(callee->interpreter_entry()); + } +#endif /* VM_JVMTI */ + istate->set_bcp_advance(3); + UPDATE_PC_AND_RETURN(0); // I'll be back... + } + } + + /* Allocate memory for a new java object. */ + + CASE(_newarray): { + BasicType atype = (BasicType) *(pc+1); + jint size = STACK_INT(-1); + CALL_VM(InterpreterRuntime::newarray(THREAD, atype, size), + handle_exception); + SET_STACK_OBJECT(THREAD->vm_result(), -1); + THREAD->set_vm_result(NULL); + + UPDATE_PC_AND_CONTINUE(2); + } + + /* Throw an exception. */ + + CASE(_athrow): { + oop except_oop = STACK_OBJECT(-1); + CHECK_NULL(except_oop); + // set pending_exception so we use common code + THREAD->set_pending_exception(except_oop, NULL, 0); + goto handle_exception; + } + + /* goto and jsr. They are exactly the same except jsr pushes + * the address of the next instruction first. + */ + + CASE(_jsr): { + /* push bytecode index on stack */ + SET_STACK_ADDR(((address)pc - (intptr_t)(istate->method()->code_base()) + 3), 0); + MORE_STACK(1); + /* FALL THROUGH */ + } + + CASE(_goto): + { + int16_t offset = (int16_t)Bytes::get_Java_u2(pc + 1); + address branch_pc = pc; + UPDATE_PC(offset); + DO_BACKEDGE_CHECKS(offset, branch_pc); + CONTINUE; + } + + CASE(_jsr_w): { + /* push return address on the stack */ + SET_STACK_ADDR(((address)pc - (intptr_t)(istate->method()->code_base()) + 5), 0); + MORE_STACK(1); + /* FALL THROUGH */ + } + + CASE(_goto_w): + { + int32_t offset = Bytes::get_Java_u4(pc + 1); + address branch_pc = pc; + UPDATE_PC(offset); + DO_BACKEDGE_CHECKS(offset, branch_pc); + CONTINUE; + } + + /* return from a jsr or jsr_w */ + + CASE(_ret): { + pc = istate->method()->code_base() + (intptr_t)(LOCALS_ADDR(pc[1])); + UPDATE_PC_AND_CONTINUE(0); + } + + /* debugger breakpoint */ + + CASE(_breakpoint): { + Bytecodes::Code original_bytecode; + DECACHE_STATE(); + SET_LAST_JAVA_FRAME(); + original_bytecode = InterpreterRuntime::get_original_bytecode_at(THREAD, + METHOD, pc); + RESET_LAST_JAVA_FRAME(); + CACHE_STATE(); + if (THREAD->has_pending_exception()) goto handle_exception; + CALL_VM(InterpreterRuntime::_breakpoint(THREAD, METHOD, pc), + handle_exception); + + opcode = (jubyte)original_bytecode; + goto opcode_switch; + } + + DEFAULT: + fatal2("\t*** Unimplemented opcode: %d = %s\n", + opcode, Bytecodes::name((Bytecodes::Code)opcode)); + goto finish; + + } /* switch(opc) */ + + +#ifdef USELABELS + check_for_exception: +#endif + { + if (!THREAD->has_pending_exception()) { + CONTINUE; + } + /* We will be gcsafe soon, so flush our state. */ + DECACHE_PC(); + goto handle_exception; + } + do_continue: ; + + } /* while (1) interpreter loop */ + + + // An exception exists in the thread state see whether this activation can handle it + handle_exception: { + + HandleMarkCleaner __hmc(THREAD); + Handle except_oop(THREAD, THREAD->pending_exception()); + // Prevent any subsequent HandleMarkCleaner in the VM + // from freeing the except_oop handle. + HandleMark __hm(THREAD); + + THREAD->clear_pending_exception(); + assert(except_oop(), "No exception to process"); + intptr_t continuation_bci; + // expression stack is emptied + topOfStack = istate->stack_base() - Interpreter::stackElementWords(); + CALL_VM(continuation_bci = (intptr_t)InterpreterRuntime::exception_handler_for_exception(THREAD, except_oop()), + handle_exception); + + except_oop = (oop) THREAD->vm_result(); + THREAD->set_vm_result(NULL); + if (continuation_bci >= 0) { + // Place exception on top of stack + SET_STACK_OBJECT(except_oop(), 0); + MORE_STACK(1); + pc = METHOD->code_base() + continuation_bci; + if (TraceExceptions) { + ttyLocker ttyl; + ResourceMark rm; + tty->print_cr("Exception <%s> (" INTPTR_FORMAT ")", except_oop->print_value_string(), except_oop()); + tty->print_cr(" thrown in interpreter method <%s>", METHOD->print_value_string()); + tty->print_cr(" at bci %d, continuing at %d for thread " INTPTR_FORMAT, + pc - (intptr_t)METHOD->code_base(), + continuation_bci, THREAD); + } + // for AbortVMOnException flag + NOT_PRODUCT(Exceptions::debug_check_abort(except_oop)); + goto run; + } + if (TraceExceptions) { + ttyLocker ttyl; + ResourceMark rm; + tty->print_cr("Exception <%s> (" INTPTR_FORMAT ")", except_oop->print_value_string(), except_oop()); + tty->print_cr(" thrown in interpreter method <%s>", METHOD->print_value_string()); + tty->print_cr(" at bci %d, unwinding for thread " INTPTR_FORMAT, + pc - (intptr_t) METHOD->code_base(), + THREAD); + } + // for AbortVMOnException flag + NOT_PRODUCT(Exceptions::debug_check_abort(except_oop)); + // No handler in this activation, unwind and try again + THREAD->set_pending_exception(except_oop(), NULL, 0); + goto handle_return; + } /* handle_exception: */ + + + + // Return from an interpreter invocation with the result of the interpretation + // on the top of the Java Stack (or a pending exception) + +handle_Pop_Frame: + + // We don't really do anything special here except we must be aware + // that we can get here without ever locking the method (if sync). + // Also we skip the notification of the exit. + + istate->set_msg(popping_frame); + // Clear pending so while the pop is in process + // we don't start another one if a call_vm is done. + THREAD->clr_pop_frame_pending(); + // Let interpreter (only) see the we're in the process of popping a frame + THREAD->set_pop_frame_in_process(); + +handle_return: + { + DECACHE_STATE(); + + bool suppress_error = istate->msg() == popping_frame; + bool suppress_exit_event = THREAD->has_pending_exception() || suppress_error; + Handle original_exception(THREAD, THREAD->pending_exception()); + Handle illegal_state_oop(THREAD, NULL); + + // We'd like a HandleMark here to prevent any subsequent HandleMarkCleaner + // in any following VM entries from freeing our live handles, but illegal_state_oop + // isn't really allocated yet and so doesn't become live until later and + // in unpredicatable places. Instead we must protect the places where we enter the + // VM. It would be much simpler (and safer) if we could allocate a real handle with + // a NULL oop in it and then overwrite the oop later as needed. This isn't + // unfortunately isn't possible. + + THREAD->clear_pending_exception(); + + // + // As far as we are concerned we have returned. If we have a pending exception + // that will be returned as this invocation's result. However if we get any + // exception(s) while checking monitor state one of those IllegalMonitorStateExceptions + // will be our final result (i.e. monitor exception trumps a pending exception). + // + + // If we never locked the method (or really passed the point where we would have), + // there is no need to unlock it (or look for other monitors), since that + // could not have happened. + + if (THREAD->do_not_unlock()) { + + // Never locked, reset the flag now because obviously any caller must + // have passed their point of locking for us to have gotten here. + + THREAD->clr_do_not_unlock(); + } else { + // At this point we consider that we have returned. We now check that the + // locks were properly block structured. If we find that they were not + // used properly we will return with an illegal monitor exception. + // The exception is checked by the caller not the callee since this + // checking is considered to be part of the invocation and therefore + // in the callers scope (JVM spec 8.13). + // + // Another weird thing to watch for is if the method was locked + // recursively and then not exited properly. This means we must + // examine all the entries in reverse time(and stack) order and + // unlock as we find them. If we find the method monitor before + // we are at the initial entry then we should throw an exception. + // It is not clear the template based interpreter does this + // correctly + + BasicObjectLock* base = istate->monitor_base(); + BasicObjectLock* end = (BasicObjectLock*) istate->stack_base(); + bool method_unlock_needed = METHOD->is_synchronized(); + // We know the initial monitor was used for the method don't check that + // slot in the loop + if (method_unlock_needed) base--; + + // Check all the monitors to see they are unlocked. Install exception if found to be locked. + while (end < base) { + oop lockee = end->obj(); + if (lockee != NULL) { + BasicLock* lock = end->lock(); + markOop header = lock->displaced_header(); + end->set_obj(NULL); + // If it isn't recursive we either must swap old header or call the runtime + if (header != NULL) { + if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) { + // restore object for the slow case + end->set_obj(lockee); + { + // Prevent any HandleMarkCleaner from freeing our live handles + HandleMark __hm(THREAD); + CALL_VM_NOCHECK(InterpreterRuntime::monitorexit(THREAD, end)); + } + } + } + // One error is plenty + if (illegal_state_oop() == NULL && !suppress_error) { + { + // Prevent any HandleMarkCleaner from freeing our live handles + HandleMark __hm(THREAD); + CALL_VM_NOCHECK(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD)); + } + assert(THREAD->has_pending_exception(), "Lost our exception!"); + illegal_state_oop = THREAD->pending_exception(); + THREAD->clear_pending_exception(); + } + } + end++; + } + // Unlock the method if needed + if (method_unlock_needed) { + if (base->obj() == NULL) { + // The method is already unlocked this is not good. + if (illegal_state_oop() == NULL && !suppress_error) { + { + // Prevent any HandleMarkCleaner from freeing our live handles + HandleMark __hm(THREAD); + CALL_VM_NOCHECK(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD)); + } + assert(THREAD->has_pending_exception(), "Lost our exception!"); + illegal_state_oop = THREAD->pending_exception(); + THREAD->clear_pending_exception(); + } + } else { + // + // The initial monitor is always used for the method + // However if that slot is no longer the oop for the method it was unlocked + // and reused by something that wasn't unlocked! + // + // deopt can come in with rcvr dead because c2 knows + // its value is preserved in the monitor. So we can't use locals[0] at all + // and must use first monitor slot. + // + oop rcvr = base->obj(); + if (rcvr == NULL) { + if (!suppress_error) { + VM_JAVA_ERROR_NO_JUMP(vmSymbols::java_lang_NullPointerException(), ""); + illegal_state_oop = THREAD->pending_exception(); + THREAD->clear_pending_exception(); + } + } else { + BasicLock* lock = base->lock(); + markOop header = lock->displaced_header(); + base->set_obj(NULL); + // If it isn't recursive we either must swap old header or call the runtime + if (header != NULL) { + if (Atomic::cmpxchg_ptr(header, rcvr->mark_addr(), lock) != lock) { + // restore object for the slow case + base->set_obj(rcvr); + { + // Prevent any HandleMarkCleaner from freeing our live handles + HandleMark __hm(THREAD); + CALL_VM_NOCHECK(InterpreterRuntime::monitorexit(THREAD, base)); + } + if (THREAD->has_pending_exception()) { + if (!suppress_error) illegal_state_oop = THREAD->pending_exception(); + THREAD->clear_pending_exception(); + } + } + } + } + } + } + } + + // + // Notify jvmti/jvmdi + // + // NOTE: we do not notify a method_exit if we have a pending exception, + // including an exception we generate for unlocking checks. In the former + // case, JVMDI has already been notified by our call for the exception handler + // and in both cases as far as JVMDI is concerned we have already returned. + // If we notify it again JVMDI will be all confused about how many frames + // are still on the stack (4340444). + // + // NOTE Further! It turns out the the JVMTI spec in fact expects to see + // method_exit events whenever we leave an activation unless it was done + // for popframe. This is nothing like jvmdi. However we are passing the + // tests at the moment (apparently because they are jvmdi based) so rather + // than change this code and possibly fail tests we will leave it alone + // (with this note) in anticipation of changing the vm and the tests + // simultaneously. + + + // + suppress_exit_event = suppress_exit_event || illegal_state_oop() != NULL; + + + +#ifdef VM_JVMTI + if (_jvmti_interp_events) { + // Whenever JVMTI puts a thread in interp_only_mode, method + // entry/exit events are sent for that thread to track stack depth. + if ( !suppress_exit_event && THREAD->is_interp_only_mode() ) { + { + // Prevent any HandleMarkCleaner from freeing our live handles + HandleMark __hm(THREAD); + CALL_VM_NOCHECK(InterpreterRuntime::post_method_exit(THREAD)); + } + } + } +#endif /* VM_JVMTI */ + + // + // See if we are returning any exception + // A pending exception that was pending prior to a possible popping frame + // overrides the popping frame. + // + assert(!suppress_error || suppress_error && illegal_state_oop() == NULL, "Error was not suppressed"); + if (illegal_state_oop() != NULL || original_exception() != NULL) { + // inform the frame manager we have no result + istate->set_msg(throwing_exception); + if (illegal_state_oop() != NULL) + THREAD->set_pending_exception(illegal_state_oop(), NULL, 0); + else + THREAD->set_pending_exception(original_exception(), NULL, 0); + istate->set_return_kind((Bytecodes::Code)opcode); + UPDATE_PC_AND_RETURN(0); + } + + if (istate->msg() == popping_frame) { + // Make it simpler on the assembly code and set the message for the frame pop. + // returns + if (istate->prev() == NULL) { + // We must be returning to a deoptimized frame (because popframe only happens between + // two interpreted frames). We need to save the current arguments in C heap so that + // the deoptimized frame when it restarts can copy the arguments to its expression + // stack and re-execute the call. We also have to notify deoptimization that this + // has occured and to pick the preerved args copy them to the deoptimized frame's + // java expression stack. Yuck. + // + THREAD->popframe_preserve_args(in_ByteSize(METHOD->size_of_parameters() * wordSize), + LOCALS_SLOT(METHOD->size_of_parameters() - 1)); + THREAD->set_popframe_condition_bit(JavaThread::popframe_force_deopt_reexecution_bit); + } + UPDATE_PC_AND_RETURN(1); + } else { + // Normal return + // Advance the pc and return to frame manager + istate->set_msg(return_from_method); + istate->set_return_kind((Bytecodes::Code)opcode); + UPDATE_PC_AND_RETURN(1); + } + } /* handle_return: */ + +// This is really a fatal error return + +finish: + DECACHE_TOS(); + DECACHE_PC(); + + return; +} + +/* + * All the code following this point is only produced once and is not present + * in the JVMTI version of the interpreter +*/ + +#ifndef VM_JVMTI + +// This constructor should only be used to contruct the object to signal +// interpreter initialization. All other instances should be created by +// the frame manager. +BytecodeInterpreter::BytecodeInterpreter(messages msg) { + if (msg != initialize) ShouldNotReachHere(); + _msg = msg; + _self_link = this; + _prev_link = NULL; +} + +// Inline static functions for Java Stack and Local manipulation + +// The implementations are platform dependent. We have to worry about alignment +// issues on some machines which can change on the same platform depending on +// whether it is an LP64 machine also. +#ifdef ASSERT +void BytecodeInterpreter::verify_stack_tag(intptr_t *tos, frame::Tag tag, int offset) { + if (TaggedStackInterpreter) { + frame::Tag t = (frame::Tag)tos[Interpreter::expr_tag_index_at(-offset)]; + assert(t == tag, "stack tag mismatch"); + } +} +#endif // ASSERT + +address BytecodeInterpreter::stack_slot(intptr_t *tos, int offset) { + debug_only(verify_stack_tag(tos, frame::TagValue, offset)); + return (address) tos[Interpreter::expr_index_at(-offset)]; +} + +jint BytecodeInterpreter::stack_int(intptr_t *tos, int offset) { + debug_only(verify_stack_tag(tos, frame::TagValue, offset)); + return *((jint*) &tos[Interpreter::expr_index_at(-offset)]); +} + +jfloat BytecodeInterpreter::stack_float(intptr_t *tos, int offset) { + debug_only(verify_stack_tag(tos, frame::TagValue, offset)); + return *((jfloat *) &tos[Interpreter::expr_index_at(-offset)]); +} + +oop BytecodeInterpreter::stack_object(intptr_t *tos, int offset) { + debug_only(verify_stack_tag(tos, frame::TagReference, offset)); + return (oop)tos [Interpreter::expr_index_at(-offset)]; +} + +jdouble BytecodeInterpreter::stack_double(intptr_t *tos, int offset) { + debug_only(verify_stack_tag(tos, frame::TagValue, offset)); + debug_only(verify_stack_tag(tos, frame::TagValue, offset-1)); + return ((VMJavaVal64*) &tos[Interpreter::expr_index_at(-offset)])->d; +} + +jlong BytecodeInterpreter::stack_long(intptr_t *tos, int offset) { + debug_only(verify_stack_tag(tos, frame::TagValue, offset)); + debug_only(verify_stack_tag(tos, frame::TagValue, offset-1)); + return ((VMJavaVal64 *) &tos[Interpreter::expr_index_at(-offset)])->l; +} + +void BytecodeInterpreter::tag_stack(intptr_t *tos, frame::Tag tag, int offset) { + if (TaggedStackInterpreter) + tos[Interpreter::expr_tag_index_at(-offset)] = (intptr_t)tag; +} + +// only used for value types +void BytecodeInterpreter::set_stack_slot(intptr_t *tos, address value, + int offset) { + tag_stack(tos, frame::TagValue, offset); + *((address *)&tos[Interpreter::expr_index_at(-offset)]) = value; +} + +void BytecodeInterpreter::set_stack_int(intptr_t *tos, int value, + int offset) { + tag_stack(tos, frame::TagValue, offset); + *((jint *)&tos[Interpreter::expr_index_at(-offset)]) = value; +} + +void BytecodeInterpreter::set_stack_float(intptr_t *tos, jfloat value, + int offset) { + tag_stack(tos, frame::TagValue, offset); + *((jfloat *)&tos[Interpreter::expr_index_at(-offset)]) = value; +} + +void BytecodeInterpreter::set_stack_object(intptr_t *tos, oop value, + int offset) { + tag_stack(tos, frame::TagReference, offset); + *((oop *)&tos[Interpreter::expr_index_at(-offset)]) = value; +} + +// needs to be platform dep for the 32 bit platforms. +void BytecodeInterpreter::set_stack_double(intptr_t *tos, jdouble value, + int offset) { + tag_stack(tos, frame::TagValue, offset); + tag_stack(tos, frame::TagValue, offset-1); + ((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset)])->d = value; +} + +void BytecodeInterpreter::set_stack_double_from_addr(intptr_t *tos, + address addr, int offset) { + tag_stack(tos, frame::TagValue, offset); + tag_stack(tos, frame::TagValue, offset-1); + (((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset)])->d = + ((VMJavaVal64*)addr)->d); +} + +void BytecodeInterpreter::set_stack_long(intptr_t *tos, jlong value, + int offset) { + tag_stack(tos, frame::TagValue, offset); + ((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset+1)])->l = 0xdeedbeeb; + tag_stack(tos, frame::TagValue, offset-1); + ((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset)])->l = value; +} + +void BytecodeInterpreter::set_stack_long_from_addr(intptr_t *tos, + address addr, int offset) { + tag_stack(tos, frame::TagValue, offset); + ((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset+1)])->l = 0xdeedbeeb; + tag_stack(tos, frame::TagValue, offset-1); + ((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset)])->l = + ((VMJavaVal64*)addr)->l; +} + +// Locals + +#ifdef ASSERT +void BytecodeInterpreter::verify_locals_tag(intptr_t *locals, frame::Tag tag, + int offset) { + if (TaggedStackInterpreter) { + frame::Tag t = (frame::Tag)locals[Interpreter::local_tag_index_at(-offset)]; + assert(t == tag, "locals tag mismatch"); + } +} +#endif // ASSERT +address BytecodeInterpreter::locals_slot(intptr_t* locals, int offset) { + debug_only(verify_locals_tag(locals, frame::TagValue, offset)); + return (address)locals[Interpreter::local_index_at(-offset)]; +} +jint BytecodeInterpreter::locals_int(intptr_t* locals, int offset) { + debug_only(verify_locals_tag(locals, frame::TagValue, offset)); + return (jint)locals[Interpreter::local_index_at(-offset)]; +} +jfloat BytecodeInterpreter::locals_float(intptr_t* locals, int offset) { + debug_only(verify_locals_tag(locals, frame::TagValue, offset)); + return (jfloat)locals[Interpreter::local_index_at(-offset)]; +} +oop BytecodeInterpreter::locals_object(intptr_t* locals, int offset) { + debug_only(verify_locals_tag(locals, frame::TagReference, offset)); + return (oop)locals[Interpreter::local_index_at(-offset)]; +} +jdouble BytecodeInterpreter::locals_double(intptr_t* locals, int offset) { + debug_only(verify_locals_tag(locals, frame::TagValue, offset)); + debug_only(verify_locals_tag(locals, frame::TagValue, offset)); + return ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->d; +} +jlong BytecodeInterpreter::locals_long(intptr_t* locals, int offset) { + debug_only(verify_locals_tag(locals, frame::TagValue, offset)); + debug_only(verify_locals_tag(locals, frame::TagValue, offset+1)); + return ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->l; +} + +// Returns the address of locals value. +address BytecodeInterpreter::locals_long_at(intptr_t* locals, int offset) { + debug_only(verify_locals_tag(locals, frame::TagValue, offset)); + debug_only(verify_locals_tag(locals, frame::TagValue, offset+1)); + return ((address)&locals[Interpreter::local_index_at(-(offset+1))]); +} +address BytecodeInterpreter::locals_double_at(intptr_t* locals, int offset) { + debug_only(verify_locals_tag(locals, frame::TagValue, offset)); + debug_only(verify_locals_tag(locals, frame::TagValue, offset+1)); + return ((address)&locals[Interpreter::local_index_at(-(offset+1))]); +} + +void BytecodeInterpreter::tag_locals(intptr_t *locals, frame::Tag tag, int offset) { + if (TaggedStackInterpreter) + locals[Interpreter::local_tag_index_at(-offset)] = (intptr_t)tag; +} + +// Used for local value or returnAddress +void BytecodeInterpreter::set_locals_slot(intptr_t *locals, + address value, int offset) { + tag_locals(locals, frame::TagValue, offset); + *((address*)&locals[Interpreter::local_index_at(-offset)]) = value; +} +void BytecodeInterpreter::set_locals_int(intptr_t *locals, + jint value, int offset) { + tag_locals(locals, frame::TagValue, offset); + *((jint *)&locals[Interpreter::local_index_at(-offset)]) = value; +} +void BytecodeInterpreter::set_locals_float(intptr_t *locals, + jfloat value, int offset) { + tag_locals(locals, frame::TagValue, offset); + *((jfloat *)&locals[Interpreter::local_index_at(-offset)]) = value; +} +void BytecodeInterpreter::set_locals_object(intptr_t *locals, + oop value, int offset) { + tag_locals(locals, frame::TagReference, offset); + *((oop *)&locals[Interpreter::local_index_at(-offset)]) = value; +} +void BytecodeInterpreter::set_locals_double(intptr_t *locals, + jdouble value, int offset) { + tag_locals(locals, frame::TagValue, offset); + tag_locals(locals, frame::TagValue, offset+1); + ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->d = value; +} +void BytecodeInterpreter::set_locals_long(intptr_t *locals, + jlong value, int offset) { + tag_locals(locals, frame::TagValue, offset); + tag_locals(locals, frame::TagValue, offset+1); + ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->l = value; +} +void BytecodeInterpreter::set_locals_double_from_addr(intptr_t *locals, + address addr, int offset) { + tag_locals(locals, frame::TagValue, offset); + tag_locals(locals, frame::TagValue, offset+1); + ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->d = ((VMJavaVal64*)addr)->d; +} +void BytecodeInterpreter::set_locals_long_from_addr(intptr_t *locals, + address addr, int offset) { + tag_locals(locals, frame::TagValue, offset); + tag_locals(locals, frame::TagValue, offset+1); + ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->l = ((VMJavaVal64*)addr)->l; +} + +void BytecodeInterpreter::astore(intptr_t* tos, int stack_offset, + intptr_t* locals, int locals_offset) { + // Copy tag from stack to locals. astore's operand can be returnAddress + // and may not be TagReference + if (TaggedStackInterpreter) { + frame::Tag t = (frame::Tag) tos[Interpreter::expr_tag_index_at(-stack_offset)]; + locals[Interpreter::local_tag_index_at(-locals_offset)] = (intptr_t)t; + } + intptr_t value = tos[Interpreter::expr_index_at(-stack_offset)]; + locals[Interpreter::local_index_at(-locals_offset)] = value; +} + + +void BytecodeInterpreter::copy_stack_slot(intptr_t *tos, int from_offset, + int to_offset) { + if (TaggedStackInterpreter) { + tos[Interpreter::expr_tag_index_at(-to_offset)] = + (intptr_t)tos[Interpreter::expr_tag_index_at(-from_offset)]; + } + tos[Interpreter::expr_index_at(-to_offset)] = + (intptr_t)tos[Interpreter::expr_index_at(-from_offset)]; +} + +void BytecodeInterpreter::dup(intptr_t *tos) { + copy_stack_slot(tos, -1, 0); +} +void BytecodeInterpreter::dup2(intptr_t *tos) { + copy_stack_slot(tos, -2, 0); + copy_stack_slot(tos, -1, 1); +} + +void BytecodeInterpreter::dup_x1(intptr_t *tos) { + /* insert top word two down */ + copy_stack_slot(tos, -1, 0); + copy_stack_slot(tos, -2, -1); + copy_stack_slot(tos, 0, -2); +} + +void BytecodeInterpreter::dup_x2(intptr_t *tos) { + /* insert top word three down */ + copy_stack_slot(tos, -1, 0); + copy_stack_slot(tos, -2, -1); + copy_stack_slot(tos, -3, -2); + copy_stack_slot(tos, 0, -3); +} +void BytecodeInterpreter::dup2_x1(intptr_t *tos) { + /* insert top 2 slots three down */ + copy_stack_slot(tos, -1, 1); + copy_stack_slot(tos, -2, 0); + copy_stack_slot(tos, -3, -1); + copy_stack_slot(tos, 1, -2); + copy_stack_slot(tos, 0, -3); +} +void BytecodeInterpreter::dup2_x2(intptr_t *tos) { + /* insert top 2 slots four down */ + copy_stack_slot(tos, -1, 1); + copy_stack_slot(tos, -2, 0); + copy_stack_slot(tos, -3, -1); + copy_stack_slot(tos, -4, -2); + copy_stack_slot(tos, 1, -3); + copy_stack_slot(tos, 0, -4); +} + + +void BytecodeInterpreter::swap(intptr_t *tos) { + // swap top two elements + intptr_t val = tos[Interpreter::expr_index_at(1)]; + frame::Tag t; + if (TaggedStackInterpreter) { + t = (frame::Tag) tos[Interpreter::expr_tag_index_at(1)]; + } + // Copy -2 entry to -1 + copy_stack_slot(tos, -2, -1); + // Store saved -1 entry into -2 + if (TaggedStackInterpreter) { + tos[Interpreter::expr_tag_index_at(2)] = (intptr_t)t; + } + tos[Interpreter::expr_index_at(2)] = val; +} +// -------------------------------------------------------------------------------- +// Non-product code +#ifndef PRODUCT + +const char* BytecodeInterpreter::C_msg(BytecodeInterpreter::messages msg) { + switch (msg) { + case BytecodeInterpreter::no_request: return("no_request"); + case BytecodeInterpreter::initialize: return("initialize"); + // status message to C++ interpreter + case BytecodeInterpreter::method_entry: return("method_entry"); + case BytecodeInterpreter::method_resume: return("method_resume"); + case BytecodeInterpreter::got_monitors: return("got_monitors"); + case BytecodeInterpreter::rethrow_exception: return("rethrow_exception"); + // requests to frame manager from C++ interpreter + case BytecodeInterpreter::call_method: return("call_method"); + case BytecodeInterpreter::return_from_method: return("return_from_method"); + case BytecodeInterpreter::more_monitors: return("more_monitors"); + case BytecodeInterpreter::throwing_exception: return("throwing_exception"); + case BytecodeInterpreter::popping_frame: return("popping_frame"); + case BytecodeInterpreter::do_osr: return("do_osr"); + // deopt + case BytecodeInterpreter::deopt_resume: return("deopt_resume"); + case BytecodeInterpreter::deopt_resume2: return("deopt_resume2"); + default: return("BAD MSG"); + } +} +void +BytecodeInterpreter::print() { + tty->print_cr("thread: " INTPTR_FORMAT, (uintptr_t) this->_thread); + tty->print_cr("bcp: " INTPTR_FORMAT, (uintptr_t) this->_bcp); + tty->print_cr("locals: " INTPTR_FORMAT, (uintptr_t) this->_locals); + tty->print_cr("constants: " INTPTR_FORMAT, (uintptr_t) this->_constants); + { + ResourceMark rm; + char *method_name = _method->name_and_sig_as_C_string(); + tty->print_cr("method: " INTPTR_FORMAT "[ %s ]", (uintptr_t) this->_method, method_name); + } + tty->print_cr("mdx: " INTPTR_FORMAT, (uintptr_t) this->_mdx); + tty->print_cr("stack: " INTPTR_FORMAT, (uintptr_t) this->_stack); + tty->print_cr("msg: %s", C_msg(this->_msg)); + tty->print_cr("result_to_call._callee: " INTPTR_FORMAT, (uintptr_t) this->_result._to_call._callee); + tty->print_cr("result_to_call._callee_entry_point: " INTPTR_FORMAT, (uintptr_t) this->_result._to_call._callee_entry_point); + tty->print_cr("result_to_call._bcp_advance: %d ", this->_result._to_call._bcp_advance); + tty->print_cr("osr._osr_buf: " INTPTR_FORMAT, (uintptr_t) this->_result._osr._osr_buf); + tty->print_cr("osr._osr_entry: " INTPTR_FORMAT, (uintptr_t) this->_result._osr._osr_entry); + tty->print_cr("result_return_kind 0x%x ", (int) this->_result._return_kind); + tty->print_cr("prev_link: " INTPTR_FORMAT, (uintptr_t) this->_prev_link); + tty->print_cr("native_mirror: " INTPTR_FORMAT, (uintptr_t) this->_oop_temp); + tty->print_cr("stack_base: " INTPTR_FORMAT, (uintptr_t) this->_stack_base); + tty->print_cr("stack_limit: " INTPTR_FORMAT, (uintptr_t) this->_stack_limit); + tty->print_cr("monitor_base: " INTPTR_FORMAT, (uintptr_t) this->_monitor_base); +#ifdef SPARC + tty->print_cr("last_Java_pc: " INTPTR_FORMAT, (uintptr_t) this->_last_Java_pc); + tty->print_cr("frame_bottom: " INTPTR_FORMAT, (uintptr_t) this->_frame_bottom); + tty->print_cr("&native_fresult: " INTPTR_FORMAT, (uintptr_t) &this->_native_fresult); + tty->print_cr("native_lresult: " INTPTR_FORMAT, (uintptr_t) this->_native_lresult); +#endif +#ifdef IA64 + tty->print_cr("last_Java_fp: " INTPTR_FORMAT, (uintptr_t) this->_last_Java_fp); +#endif // IA64 + tty->print_cr("self_link: " INTPTR_FORMAT, (uintptr_t) this->_self_link); +} + +extern "C" { + void PI(uintptr_t arg) { + ((BytecodeInterpreter*)arg)->print(); + } +} +#endif // PRODUCT + +#endif // JVMTI +#endif // CC_INTERP diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.hpp b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.hpp new file mode 100644 index 00000000000..715acf671a2 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.hpp @@ -0,0 +1,572 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifdef CC_INTERP + +// CVM definitions find hotspot equivalents... + +union VMJavaVal64 { + jlong l; + jdouble d; + uint32_t v[2]; +}; + + +typedef class BytecodeInterpreter* interpreterState; + +struct call_message { + class methodOopDesc* _callee; /* method to call during call_method request */ + address _callee_entry_point; /* address to jump to for call_method request */ + int _bcp_advance; /* size of the invoke bytecode operation */ +}; + +struct osr_message { + address _osr_buf; /* the osr buffer */ + address _osr_entry; /* the entry to the osr method */ +}; + +struct osr_result { + nmethod* nm; /* osr nmethod */ + address return_addr; /* osr blob return address */ +}; + +// Result returned to frame manager +union frame_manager_message { + call_message _to_call; /* describes callee */ + Bytecodes::Code _return_kind; /* i_return, a_return, ... */ + osr_message _osr; /* describes the osr */ + osr_result _osr_result; /* result of OSR request */ +}; + +class BytecodeInterpreter : StackObj { +friend class SharedRuntime; +friend class AbstractInterpreterGenerator; +friend class CppInterpreterGenerator; +friend class InterpreterGenerator; +friend class InterpreterMacroAssembler; +friend class frame; +friend class SharedRuntime; +friend class VMStructs; + +public: + enum messages { + no_request = 0, // unused + initialize, // Perform one time interpreter initializations (assumes all switches set) + // status message to C++ interpreter + method_entry, // initial method entry to interpreter + method_resume, // frame manager response to return_from_method request (assuming a frame to resume) + deopt_resume, // returning from a native call into a deopted frame + deopt_resume2, // deopt resume as a result of a PopFrame + got_monitors, // frame manager response to more_monitors request + rethrow_exception, // unwinding and throwing exception + // requests to frame manager from C++ interpreter + call_method, // request for new frame from interpreter, manager responds with method_entry + return_from_method, // request from interpreter to unwind, manager responds with method_continue + more_monitors, // need a new monitor + throwing_exception, // unwind stack and rethrow + popping_frame, // unwind call and retry call + do_osr // request this invocation be OSR's + }; + +private: + JavaThread* _thread; // the vm's java thread pointer + address _bcp; // instruction pointer + intptr_t* _locals; // local variable pointer + constantPoolCacheOop _constants; // constant pool cache + methodOop _method; // method being executed + DataLayout* _mdx; // compiler profiling data for current bytecode + intptr_t* _stack; // expression stack + messages _msg; // frame manager <-> interpreter message + frame_manager_message _result; // result to frame manager + interpreterState _prev_link; // previous interpreter state + oop _oop_temp; // mirror for interpreted native, null otherwise + intptr_t* _stack_base; // base of expression stack + intptr_t* _stack_limit; // limit of expression stack + BasicObjectLock* _monitor_base; // base of monitors on the native stack + + +public: + // Constructor is only used by the initialization step. All other instances are created + // by the frame manager. + BytecodeInterpreter(messages msg); + +// +// Deoptimization support +// +static void layout_interpreterState(interpreterState to_fill, + frame* caller, + frame* interpreter_frame, + methodOop method, + intptr_t* locals, + intptr_t* stack, + intptr_t* stack_base, + intptr_t* monitor_base, + intptr_t* frame_bottom, + bool top_frame); + +/* + * Generic 32-bit wide "Java slot" definition. This type occurs + * in operand stacks, Java locals, object fields, constant pools. + */ +union VMJavaVal32 { + jint i; + jfloat f; + class oopDesc* r; + uint32_t raw; +}; + +/* + * Generic 64-bit Java value definition + */ +union VMJavaVal64 { + jlong l; + jdouble d; + uint32_t v[2]; +}; + +/* + * Generic 32-bit wide "Java slot" definition. This type occurs + * in Java locals, object fields, constant pools, and + * operand stacks (as a CVMStackVal32). + */ +typedef union VMSlotVal32 { + VMJavaVal32 j; /* For "Java" values */ + address a; /* a return created by jsr or jsr_w */ +} VMSlotVal32; + + +/* + * Generic 32-bit wide stack slot definition. + */ +union VMStackVal32 { + VMJavaVal32 j; /* For "Java" values */ + VMSlotVal32 s; /* any value from a "slot" or locals[] */ +}; + +inline JavaThread* thread() { return _thread; } + +inline address bcp() { return _bcp; } +inline void set_bcp(address new_bcp) { _bcp = new_bcp; } + +inline intptr_t* locals() { return _locals; } + +inline constantPoolCacheOop constants() { return _constants; } +inline methodOop method() { return _method; } +inline DataLayout* mdx() { return _mdx; } +inline void set_mdx(DataLayout *new_mdx) { _mdx = new_mdx; } + +inline messages msg() { return _msg; } +inline void set_msg(messages new_msg) { _msg = new_msg; } + +inline methodOop callee() { return _result._to_call._callee; } +inline void set_callee(methodOop new_callee) { _result._to_call._callee = new_callee; } +inline void set_callee_entry_point(address entry) { _result._to_call._callee_entry_point = entry; } +inline void set_osr_buf(address buf) { _result._osr._osr_buf = buf; } +inline void set_osr_entry(address entry) { _result._osr._osr_entry = entry; } +inline int bcp_advance() { return _result._to_call._bcp_advance; } +inline void set_bcp_advance(int count) { _result._to_call._bcp_advance = count; } + +inline void set_return_kind(Bytecodes::Code kind) { _result._return_kind = kind; } + +inline interpreterState prev() { return _prev_link; } + +inline intptr_t* stack() { return _stack; } +inline void set_stack(intptr_t* new_stack) { _stack = new_stack; } + + +inline intptr_t* stack_base() { return _stack_base; } +inline intptr_t* stack_limit() { return _stack_limit; } + +inline BasicObjectLock* monitor_base() { return _monitor_base; } + +/* + * 64-bit Arithmetic: + * + * The functions below follow the semantics of the + * ladd, land, ldiv, lmul, lor, lxor, and lrem bytecodes, + * respectively. + */ + +static jlong VMlongAdd(jlong op1, jlong op2); +static jlong VMlongAnd(jlong op1, jlong op2); +static jlong VMlongDiv(jlong op1, jlong op2); +static jlong VMlongMul(jlong op1, jlong op2); +static jlong VMlongOr (jlong op1, jlong op2); +static jlong VMlongSub(jlong op1, jlong op2); +static jlong VMlongXor(jlong op1, jlong op2); +static jlong VMlongRem(jlong op1, jlong op2); + +/* + * Shift: + * + * The functions below follow the semantics of the + * lushr, lshl, and lshr bytecodes, respectively. + */ + +static jlong VMlongUshr(jlong op1, jint op2); +static jlong VMlongShl (jlong op1, jint op2); +static jlong VMlongShr (jlong op1, jint op2); + +/* + * Unary: + * + * Return the negation of "op" (-op), according to + * the semantics of the lneg bytecode. + */ + +static jlong VMlongNeg(jlong op); + +/* + * Return the complement of "op" (~op) + */ + +static jlong VMlongNot(jlong op); + + +/* + * Comparisons to 0: + */ + +static int32_t VMlongLtz(jlong op); /* op <= 0 */ +static int32_t VMlongGez(jlong op); /* op >= 0 */ +static int32_t VMlongEqz(jlong op); /* op == 0 */ + +/* + * Between operands: + */ + +static int32_t VMlongEq(jlong op1, jlong op2); /* op1 == op2 */ +static int32_t VMlongNe(jlong op1, jlong op2); /* op1 != op2 */ +static int32_t VMlongGe(jlong op1, jlong op2); /* op1 >= op2 */ +static int32_t VMlongLe(jlong op1, jlong op2); /* op1 <= op2 */ +static int32_t VMlongLt(jlong op1, jlong op2); /* op1 < op2 */ +static int32_t VMlongGt(jlong op1, jlong op2); /* op1 > op2 */ + +/* + * Comparisons (returning an jint value: 0, 1, or -1) + * + * Between operands: + * + * Compare "op1" and "op2" according to the semantics of the + * "lcmp" bytecode. + */ + +static int32_t VMlongCompare(jlong op1, jlong op2); + +/* + * Convert int to long, according to "i2l" bytecode semantics + */ +static jlong VMint2Long(jint val); + +/* + * Convert long to int, according to "l2i" bytecode semantics + */ +static jint VMlong2Int(jlong val); + +/* + * Convert long to float, according to "l2f" bytecode semantics + */ +static jfloat VMlong2Float(jlong val); + +/* + * Convert long to double, according to "l2d" bytecode semantics + */ +static jdouble VMlong2Double(jlong val); + +/* + * Java floating-point float value manipulation. + * + * The result argument is, once again, an lvalue. + * + * Arithmetic: + * + * The functions below follow the semantics of the + * fadd, fsub, fmul, fdiv, and frem bytecodes, + * respectively. + */ + +static jfloat VMfloatAdd(jfloat op1, jfloat op2); +static jfloat VMfloatSub(jfloat op1, jfloat op2); +static jfloat VMfloatMul(jfloat op1, jfloat op2); +static jfloat VMfloatDiv(jfloat op1, jfloat op2); +static jfloat VMfloatRem(jfloat op1, jfloat op2); + +/* + * Unary: + * + * Return the negation of "op" (-op), according to + * the semantics of the fneg bytecode. + */ + +static jfloat VMfloatNeg(jfloat op); + +/* + * Comparisons (returning an int value: 0, 1, or -1) + * + * Between operands: + * + * Compare "op1" and "op2" according to the semantics of the + * "fcmpl" (direction is -1) or "fcmpg" (direction is 1) bytecodes. + */ + +static int32_t VMfloatCompare(jfloat op1, jfloat op2, + int32_t direction); +/* + * Conversion: + */ + +/* + * Convert float to double, according to "f2d" bytecode semantics + */ + +static jdouble VMfloat2Double(jfloat op); + +/* + ****************************************** + * Java double floating-point manipulation. + ****************************************** + * + * The result argument is, once again, an lvalue. + * + * Conversions: + */ + +/* + * Convert double to int, according to "d2i" bytecode semantics + */ + +static jint VMdouble2Int(jdouble val); + +/* + * Convert double to float, according to "d2f" bytecode semantics + */ + +static jfloat VMdouble2Float(jdouble val); + +/* + * Convert int to double, according to "i2d" bytecode semantics + */ + +static jdouble VMint2Double(jint val); + +/* + * Arithmetic: + * + * The functions below follow the semantics of the + * dadd, dsub, ddiv, dmul, and drem bytecodes, respectively. + */ + +static jdouble VMdoubleAdd(jdouble op1, jdouble op2); +static jdouble VMdoubleSub(jdouble op1, jdouble op2); +static jdouble VMdoubleDiv(jdouble op1, jdouble op2); +static jdouble VMdoubleMul(jdouble op1, jdouble op2); +static jdouble VMdoubleRem(jdouble op1, jdouble op2); + +/* + * Unary: + * + * Return the negation of "op" (-op), according to + * the semantics of the dneg bytecode. + */ + +static jdouble VMdoubleNeg(jdouble op); + +/* + * Comparisons (returning an int32_t value: 0, 1, or -1) + * + * Between operands: + * + * Compare "op1" and "op2" according to the semantics of the + * "dcmpl" (direction is -1) or "dcmpg" (direction is 1) bytecodes. + */ + +static int32_t VMdoubleCompare(jdouble op1, jdouble op2, int32_t direction); + +/* + * Copy two typeless 32-bit words from one location to another. + * This is semantically equivalent to: + * + * to[0] = from[0]; + * to[1] = from[1]; + * + * but this interface is provided for those platforms that could + * optimize this into a single 64-bit transfer. + */ + +static void VMmemCopy64(uint32_t to[2], const uint32_t from[2]); + + +// Arithmetic operations + +/* + * Java arithmetic methods. + * The functions below follow the semantics of the + * iadd, isub, imul, idiv, irem, iand, ior, ixor, + * and ineg bytecodes, respectively. + */ + +static jint VMintAdd(jint op1, jint op2); +static jint VMintSub(jint op1, jint op2); +static jint VMintMul(jint op1, jint op2); +static jint VMintDiv(jint op1, jint op2); +static jint VMintRem(jint op1, jint op2); +static jint VMintAnd(jint op1, jint op2); +static jint VMintOr (jint op1, jint op2); +static jint VMintXor(jint op1, jint op2); + +/* + * Shift Operation: + * The functions below follow the semantics of the + * iushr, ishl, and ishr bytecodes, respectively. + */ + +static jint VMintUshr(jint op, jint num); +static jint VMintShl (jint op, jint num); +static jint VMintShr (jint op, jint num); + +/* + * Unary Operation: + * + * Return the negation of "op" (-op), according to + * the semantics of the ineg bytecode. + */ + +static jint VMintNeg(jint op); + +/* + * Int Conversions: + */ + +/* + * Convert int to float, according to "i2f" bytecode semantics + */ + +static jfloat VMint2Float(jint val); + +/* + * Convert int to byte, according to "i2b" bytecode semantics + */ + +static jbyte VMint2Byte(jint val); + +/* + * Convert int to char, according to "i2c" bytecode semantics + */ + +static jchar VMint2Char(jint val); + +/* + * Convert int to short, according to "i2s" bytecode semantics + */ + +static jshort VMint2Short(jint val); + +/*========================================================================= + * Bytecode interpreter operations + *=======================================================================*/ + +static void dup(intptr_t *tos); +static void dup2(intptr_t *tos); +static void dup_x1(intptr_t *tos); /* insert top word two down */ +static void dup_x2(intptr_t *tos); /* insert top word three down */ +static void dup2_x1(intptr_t *tos); /* insert top 2 slots three down */ +static void dup2_x2(intptr_t *tos); /* insert top 2 slots four down */ +static void swap(intptr_t *tos); /* swap top two elements */ + +// umm don't like this method modifies its object + +// The Interpreter used when +static void run(interpreterState istate); +// The interpreter used if JVMTI needs interpreter events +static void runWithChecks(interpreterState istate); +static void End_Of_Interpreter(void); + +// Inline static functions for Java Stack and Local manipulation + +static address stack_slot(intptr_t *tos, int offset); +static jint stack_int(intptr_t *tos, int offset); +static jfloat stack_float(intptr_t *tos, int offset); +static oop stack_object(intptr_t *tos, int offset); +static jdouble stack_double(intptr_t *tos, int offset); +static jlong stack_long(intptr_t *tos, int offset); + +static void tag_stack(intptr_t *tos, frame::Tag tag, int offset); + +// only used for value types +static void set_stack_slot(intptr_t *tos, address value, int offset); +static void set_stack_int(intptr_t *tos, int value, int offset); +static void set_stack_float(intptr_t *tos, jfloat value, int offset); +static void set_stack_object(intptr_t *tos, oop value, int offset); + +// needs to be platform dep for the 32 bit platforms. +static void set_stack_double(intptr_t *tos, jdouble value, int offset); +static void set_stack_long(intptr_t *tos, jlong value, int offset); + +static void set_stack_double_from_addr(intptr_t *tos, address addr, int offset); +static void set_stack_long_from_addr(intptr_t *tos, address addr, int offset); + +// Locals + +static address locals_slot(intptr_t* locals, int offset); +static jint locals_int(intptr_t* locals, int offset); +static jfloat locals_float(intptr_t* locals, int offset); +static oop locals_object(intptr_t* locals, int offset); +static jdouble locals_double(intptr_t* locals, int offset); +static jlong locals_long(intptr_t* locals, int offset); + +static address locals_long_at(intptr_t* locals, int offset); +static address locals_double_at(intptr_t* locals, int offset); + +static void tag_locals(intptr_t *locals, frame::Tag tag, int offset); + +static void set_locals_slot(intptr_t *locals, address value, int offset); +static void set_locals_int(intptr_t *locals, jint value, int offset); +static void set_locals_float(intptr_t *locals, jfloat value, int offset); +static void set_locals_object(intptr_t *locals, oop value, int offset); +static void set_locals_double(intptr_t *locals, jdouble value, int offset); +static void set_locals_long(intptr_t *locals, jlong value, int offset); +static void set_locals_double_from_addr(intptr_t *locals, + address addr, int offset); +static void set_locals_long_from_addr(intptr_t *locals, + address addr, int offset); + +static void astore(intptr_t* topOfStack, int stack_offset, + intptr_t* locals, int locals_offset); + +// Support for dup and swap +static void copy_stack_slot(intptr_t *tos, int from_offset, int to_offset); + +#ifndef PRODUCT +static void verify_locals_tag(intptr_t *locals, frame::Tag tag, int offset); +static void verify_stack_tag(intptr_t *tos, frame::Tag tag, int offset); +static const char* C_msg(BytecodeInterpreter::messages msg); +void print(); +#endif // PRODUCT + + // Platform fields/methods +# include "incls/_bytecodeInterpreter_pd.hpp.incl" + +}; // BytecodeInterpreter + +#endif // CC_INTERP diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.inline.hpp b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.inline.hpp new file mode 100644 index 00000000000..b765dcebe98 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.inline.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds platform-independant bodies of inline functions for the C++ based interpreter + +#ifdef CC_INTERP + +#ifdef ASSERT +extern "C" { typedef void (*verify_oop_fn_t)(oop, const char *);}; +#define VERIFY_OOP(o) \ + /*{ verify_oop_fn_t verify_oop_entry = \ + *StubRoutines::verify_oop_subroutine_entry_address(); \ + if (verify_oop_entry) { \ + (*verify_oop_entry)((o), "Not an oop!"); \ + } \ + }*/ +#else +#define VERIFY_OOP(o) +#endif + +// Platform dependent data manipulation +# include "incls/_bytecodeInterpreter_pd.inline.hpp.incl" +#endif // CC_INTERP diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreterWithChecks.xml b/hotspot/src/share/vm/interpreter/bytecodeInterpreterWithChecks.xml new file mode 100644 index 00000000000..56d97c6ee29 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreterWithChecks.xml @@ -0,0 +1,10 @@ + + + +]> + + diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreterWithChecks.xsl b/hotspot/src/share/vm/interpreter/bytecodeInterpreterWithChecks.xsl new file mode 100644 index 00000000000..28cd8712d28 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreterWithChecks.xsl @@ -0,0 +1,21 @@ + + + + + + + +#define VM_JVMTI +#include "bytecodeInterpreter.cpp" + + + + + + + + + diff --git a/hotspot/src/share/vm/interpreter/bytecodeStream.cpp b/hotspot/src/share/vm/interpreter/bytecodeStream.cpp new file mode 100644 index 00000000000..9152907e199 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeStream.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_bytecodeStream.cpp.incl" + +Bytecodes::Code RawBytecodeStream::raw_next_special(Bytecodes::Code code) { + assert(!is_last_bytecode(), "should have been checked"); + // set next bytecode position + address bcp = RawBytecodeStream::bcp(); + int l = Bytecodes::raw_special_length_at(bcp); + if (l <= 0 || (_bci + l) > _end_bci) { + code = Bytecodes::_illegal; + } else { + _next_bci += l; + assert(_bci < _next_bci, "length must be > 0"); + // set attributes + _is_wide = false; + // check for special (uncommon) cases + if (code == Bytecodes::_wide) { + code = (Bytecodes::Code)bcp[1]; + _is_wide = true; + } + } + _code = code; + return code; +} diff --git a/hotspot/src/share/vm/interpreter/bytecodeStream.hpp b/hotspot/src/share/vm/interpreter/bytecodeStream.hpp new file mode 100644 index 00000000000..3616c9dc6ac --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeStream.hpp @@ -0,0 +1,172 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A BytecodeStream is used for fast iteration over the bytecodes +// of a methodOop. +// +// Usage: +// +// BytecodeStream s(method); +// Bytecodes::Code c; +// while ((c = s.next()) >= 0) { +// ... +// } +// +// A RawBytecodeStream is a simple version of BytecodeStream. +// It is used ONLY when we know the bytecodes haven't been rewritten +// yet, such as in the rewriter or the verifier. Currently only the +// verifier uses this class. + +class RawBytecodeStream: StackObj { + protected: + // stream buffer + methodHandle _method; // read from method directly + + // reading position + int _bci; // bci if current bytecode + int _next_bci; // bci of next bytecode + int _end_bci; // bci after the current iteration interval + + // last bytecode read + Bytecodes::Code _code; + bool _is_wide; + + public: + // Construction + RawBytecodeStream(methodHandle method) : _method(method) { + set_interval(0, _method->code_size()); + } + + // Iteration control + void set_interval(int beg_bci, int end_bci) { + // iterate over the interval [beg_bci, end_bci) + assert(0 <= beg_bci && beg_bci <= method()->code_size(), "illegal beg_bci"); + assert(0 <= end_bci && end_bci <= method()->code_size(), "illegal end_bci"); + // setup of iteration pointers + _bci = beg_bci; + _next_bci = beg_bci; + _end_bci = end_bci; + } + void set_start (int beg_bci) { + set_interval(beg_bci, _method->code_size()); + } + + // Iteration + // Use raw_next() rather than next() for faster method reference + Bytecodes::Code raw_next() { + Bytecodes::Code code; + // set reading position + _bci = _next_bci; + assert(!is_last_bytecode(), "caller should check is_last_bytecode()"); + + address bcp = RawBytecodeStream::bcp(); + code = Bytecodes::code_or_bp_at(bcp); + + // set next bytecode position + int l = Bytecodes::length_for(code); + if (l > 0 && (_bci + l) <= _end_bci) { + assert(code != Bytecodes::_wide && code != Bytecodes::_tableswitch + && code != Bytecodes::_lookupswitch, "can't be special bytecode"); + _is_wide = false; + _next_bci += l; + _code = code; + return code; + } else if (code == Bytecodes::_wide && _bci + 1 >= _end_bci) { + return Bytecodes::_illegal; + } else { + return raw_next_special(code); + } + } + Bytecodes::Code raw_next_special(Bytecodes::Code code); + + // Stream attributes + methodHandle method() const { return _method; } + + int bci() const { return _bci; } + int next_bci() const { return _next_bci; } + int end_bci() const { return _end_bci; } + + Bytecodes::Code code() const { return _code; } + bool is_wide() const { return _is_wide; } + bool is_last_bytecode() const { return _next_bci >= _end_bci; } + + address bcp() const { return method()->code_base() + _bci; } + address next_bcp() { return method()->code_base() + _next_bci; } + + // State changes + void set_next_bci(int bci) { assert(0 <= bci && bci <= method()->code_size(), "illegal bci"); _next_bci = bci; } + + // Bytecode-specific attributes + int dest() const { return bci() + (short)Bytes::get_Java_u2(bcp() + 1); } + int dest_w() const { return bci() + (int )Bytes::get_Java_u4(bcp() + 1); } + + // Unsigned indices, widening + int get_index() const { return (is_wide()) ? Bytes::get_Java_u2(bcp() + 2) : bcp()[1]; } + int get_index_big() const { return (int)Bytes::get_Java_u2(bcp() + 1); } +}; + +// In BytecodeStream, non-java bytecodes will be translated into the +// corresponding java bytecodes. + +class BytecodeStream: public RawBytecodeStream { + public: + // Construction + BytecodeStream(methodHandle method) : RawBytecodeStream(method) { } + + // Iteration + Bytecodes::Code next() { + Bytecodes::Code code; + // set reading position + _bci = _next_bci; + if (is_last_bytecode()) { + // indicate end of bytecode stream + code = Bytecodes::_illegal; + } else { + // get bytecode + address bcp = BytecodeStream::bcp(); + code = Bytecodes::java_code_at(bcp); + // set next bytecode position + // + // note that we cannot advance before having the + // tty bytecode otherwise the stepping is wrong! + // (carefull: length_for(...) must be used first!) + int l = Bytecodes::length_for(code); + if (l == 0) l = Bytecodes::length_at(bcp); + _next_bci += l; + assert(_bci < _next_bci, "length must be > 0"); + // set attributes + _is_wide = false; + // check for special (uncommon) cases + if (code == Bytecodes::_wide) { + code = (Bytecodes::Code)bcp[1]; + _is_wide = true; + } + assert(Bytecodes::is_java_code(code), "sanity check"); + } + _code = code; + return _code; + } + + bool is_active_breakpoint() const { return Bytecodes::is_active_breakpoint_at(bcp()); } +}; diff --git a/hotspot/src/share/vm/interpreter/bytecodeTracer.cpp b/hotspot/src/share/vm/interpreter/bytecodeTracer.cpp new file mode 100644 index 00000000000..7ac0eafc71c --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeTracer.cpp @@ -0,0 +1,419 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_bytecodeTracer.cpp.incl" + + +#ifndef PRODUCT + +// Standard closure for BytecodeTracer: prints the current bytecode +// and its attributes using bytecode-specific information. + +class BytecodePrinter: public BytecodeClosure { + private: + // %%% This field is not GC-ed, and so can contain garbage + // between critical sections. Use only pointer-comparison + // operations on the pointer, except within a critical section. + // (Also, ensure that occasional false positives are benign.) + methodOop _current_method; + bool _is_wide; + address _next_pc; // current decoding position + + void align() { _next_pc = (address)round_to((intptr_t)_next_pc, sizeof(jint)); } + int get_byte() { return *(jbyte*) _next_pc++; } // signed + short get_short() { short i=Bytes::get_Java_u2(_next_pc); _next_pc+=2; return i; } + int get_int() { int i=Bytes::get_Java_u4(_next_pc); _next_pc+=4; return i; } + + int get_index() { return *(address)_next_pc++; } + int get_big_index() { int i=Bytes::get_Java_u2(_next_pc); _next_pc+=2; return i; } + int get_index_special() { return (is_wide()) ? get_big_index() : get_index(); } + methodOop method() { return _current_method; } + bool is_wide() { return _is_wide; } + + + void print_constant(int i, outputStream* st = tty); + void print_attributes(Bytecodes::Code code, int bci, outputStream* st = tty); + void bytecode_epilog(int bci, outputStream* st = tty); + + public: + BytecodePrinter() { + _is_wide = false; + } + + // This method is called while executing the raw bytecodes, so none of + // the adjustments that BytecodeStream performs applies. + void trace(methodHandle method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st) { + ResourceMark rm; + if (_current_method != method()) { + // Note 1: This code will not work as expected with true MT/MP. + // Need an explicit lock or a different solution. + // It is possible for this block to be skipped, if a garbage + // _current_method pointer happens to have the same bits as + // the incoming method. We could lose a line of trace output. + // This is acceptable in a debug-only feature. + st->cr(); + st->print("[%d] ", (int) Thread::current()->osthread()->thread_id()); + method->print_name(st); + st->cr(); + _current_method = method(); + } + Bytecodes::Code code; + if (is_wide()) { + // bcp wasn't advanced if previous bytecode was _wide. + code = Bytecodes::code_at(bcp+1); + } else { + code = Bytecodes::code_at(bcp); + } + int bci = bcp - method->code_base(); + st->print("[%d] ", (int) Thread::current()->osthread()->thread_id()); + if (Verbose) { + st->print("%8d %4d " INTPTR_FORMAT " " INTPTR_FORMAT " %s", + BytecodeCounter::counter_value(), bci, tos, tos2, Bytecodes::name(code)); + } else { + st->print("%8d %4d %s", + BytecodeCounter::counter_value(), bci, Bytecodes::name(code)); + } + _next_pc = is_wide() ? bcp+2 : bcp+1; + print_attributes(code, bci); + // Set is_wide for the next one, since the caller of this doesn't skip + // the next bytecode. + _is_wide = (code == Bytecodes::_wide); + } + + // Used for methodOop::print_codes(). The input bcp comes from + // BytecodeStream, which will skip wide bytecodes. + void trace(methodHandle method, address bcp, outputStream* st) { + _current_method = method(); + ResourceMark rm; + Bytecodes::Code code = Bytecodes::code_at(bcp); + // Set is_wide + _is_wide = (code == Bytecodes::_wide); + if (is_wide()) { + code = Bytecodes::code_at(bcp+1); + } + int bci = bcp - method->code_base(); + // Print bytecode index and name + if (is_wide()) { + st->print("%d %s_w", bci, Bytecodes::name(code)); + } else { + st->print("%d %s", bci, Bytecodes::name(code)); + } + _next_pc = is_wide() ? bcp+2 : bcp+1; + print_attributes(code, bci, st); + bytecode_epilog(bci, st); + } +}; + + +// Implementation of BytecodeTracer + +// %%% This set_closure thing seems overly general, given that +// nobody uses it. Also, if BytecodePrinter weren't hidden +// then methodOop could use instances of it directly and it +// would be easier to remove races on _current_method and bcp. +// Since this is not product functionality, we can defer cleanup. + +BytecodeClosure* BytecodeTracer::_closure = NULL; + +static BytecodePrinter std_closure; +BytecodeClosure* BytecodeTracer::std_closure() { + return &::std_closure; +} + + +void BytecodeTracer::trace(methodHandle method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st) { + if (TraceBytecodes && BytecodeCounter::counter_value() >= TraceBytecodesAt) { + ttyLocker ttyl; // 5065316: keep the following output coherent + // The ttyLocker also prevents races between two threads + // trying to use the single instance of BytecodePrinter. + // Using the ttyLocker prevents the system from coming to + // a safepoint within this code, which is sensitive to methodOop + // movement. + // + // There used to be a leaf mutex here, but the ttyLocker will + // work just as well, as long as the printing operations never block. + // + // We put the locker on the static trace method, not the + // virtual one, because the clients of this module go through + // the static method. + _closure->trace(method, bcp, tos, tos2, st); + } +} + +void BytecodeTracer::trace(methodHandle method, address bcp, outputStream* st) { + ttyLocker ttyl; // 5065316: keep the following output coherent + _closure->trace(method, bcp, st); +} + +void print_oop(oop value, outputStream* st) { + if (value == NULL) { + st->print_cr(" NULL"); + } else { + EXCEPTION_MARK; + Handle h_value (THREAD, value); + symbolHandle sym = java_lang_String::as_symbol(h_value, CATCH); + if (sym->utf8_length() > 32) { + st->print_cr(" ...."); + } else { + sym->print_on(st); st->cr(); + } + } +} + +void BytecodePrinter::print_constant(int i, outputStream* st) { + constantPoolOop constants = method()->constants(); + constantTag tag = constants->tag_at(i); + + if (tag.is_int()) { + st->print_cr(" " INT32_FORMAT, constants->int_at(i)); + } else if (tag.is_long()) { + st->print_cr(" " INT64_FORMAT, constants->long_at(i)); + } else if (tag.is_float()) { + st->print_cr(" %f", constants->float_at(i)); + } else if (tag.is_double()) { + st->print_cr(" %f", constants->double_at(i)); + } else if (tag.is_string()) { + oop string = constants->resolved_string_at(i); + print_oop(string, st); + } else if (tag.is_unresolved_string()) { + st->print_cr(" ", i); + } else if (tag.is_klass()) { + st->print_cr(" %s", constants->resolved_klass_at(i)->klass_part()->external_name()); + } else if (tag.is_unresolved_klass()) { + st->print_cr(" ", i); + } else ShouldNotReachHere(); +} + + +void BytecodePrinter::print_attributes(Bytecodes::Code code, int bci, outputStream* st) { + // Show attributes of pre-rewritten codes + code = Bytecodes::java_code(code); + // If the code doesn't have any fields there's nothing to print. + // note this is ==1 because the tableswitch and lookupswitch are + // zero size (for some reason) and we want to print stuff out for them. + if (Bytecodes::length_for(code) == 1) { + st->cr(); + return; + } + + switch(code) { + // Java specific bytecodes only matter. + case Bytecodes::_bipush: + st->print_cr(" " INT32_FORMAT, get_byte()); + break; + case Bytecodes::_sipush: + st->print_cr(" " INT32_FORMAT, get_short()); + break; + case Bytecodes::_ldc: + print_constant(get_index(), st); + break; + + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + print_constant(get_big_index(), st); + break; + + case Bytecodes::_iload: + case Bytecodes::_lload: + case Bytecodes::_fload: + case Bytecodes::_dload: + case Bytecodes::_aload: + case Bytecodes::_istore: + case Bytecodes::_lstore: + case Bytecodes::_fstore: + case Bytecodes::_dstore: + case Bytecodes::_astore: + st->print_cr(" #%d", get_index_special()); + break; + + case Bytecodes::_iinc: + { int index = get_index_special(); + jint offset = is_wide() ? get_short(): get_byte(); + st->print_cr(" #%d " INT32_FORMAT, index, offset); + } + break; + + case Bytecodes::_newarray: { + BasicType atype = (BasicType)get_index(); + const char* str = type2name(atype); + if (str == NULL || atype == T_OBJECT || atype == T_ARRAY) { + assert(false, "Unidentified basic type"); + } + st->print_cr(" %s", str); + } + break; + case Bytecodes::_anewarray: { + int klass_index = get_big_index(); + constantPoolOop constants = method()->constants(); + symbolOop name = constants->klass_name_at(klass_index); + st->print_cr(" %s ", name->as_C_string()); + } + break; + case Bytecodes::_multianewarray: { + int klass_index = get_big_index(); + int nof_dims = get_index(); + constantPoolOop constants = method()->constants(); + symbolOop name = constants->klass_name_at(klass_index); + st->print_cr(" %s %d", name->as_C_string(), nof_dims); + } + break; + + case Bytecodes::_ifeq: + case Bytecodes::_ifnull: + case Bytecodes::_iflt: + case Bytecodes::_ifle: + case Bytecodes::_ifne: + case Bytecodes::_ifnonnull: + case Bytecodes::_ifgt: + case Bytecodes::_ifge: + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_goto: + case Bytecodes::_jsr: + st->print_cr(" %d", bci + get_short()); + break; + + case Bytecodes::_goto_w: + case Bytecodes::_jsr_w: + st->print_cr(" %d", bci + get_int()); + break; + + case Bytecodes::_ret: st->print_cr(" %d", get_index_special()); break; + + case Bytecodes::_tableswitch: + { align(); + int default_dest = bci + get_int(); + int lo = get_int(); + int hi = get_int(); + int len = hi - lo + 1; + jint* dest = NEW_RESOURCE_ARRAY(jint, len); + for (int i = 0; i < len; i++) { + dest[i] = bci + get_int(); + } + st->print(" %d " INT32_FORMAT " " INT32_FORMAT " ", + default_dest, lo, hi); + int first = true; + for (int ll = lo; ll <= hi; ll++, first = false) { + int idx = ll - lo; + const char *format = first ? " %d:" INT32_FORMAT " (delta: %d)" : + ", %d:" INT32_FORMAT " (delta: %d)"; + st->print(format, ll, dest[idx], dest[idx]-bci); + } + st->cr(); + } + break; + case Bytecodes::_lookupswitch: + { align(); + int default_dest = bci + get_int(); + int len = get_int(); + jint* key = NEW_RESOURCE_ARRAY(jint, len); + jint* dest = NEW_RESOURCE_ARRAY(jint, len); + for (int i = 0; i < len; i++) { + key [i] = get_int(); + dest[i] = bci + get_int(); + }; + st->print(" %d %d ", default_dest, len); + bool first = true; + for (int ll = 0; ll < len; ll++, first = false) { + const char *format = first ? " " INT32_FORMAT ":" INT32_FORMAT : + ", " INT32_FORMAT ":" INT32_FORMAT ; + st->print(format, key[ll], dest[ll]); + } + st->cr(); + } + break; + + case Bytecodes::_putstatic: + case Bytecodes::_getstatic: + case Bytecodes::_putfield: + case Bytecodes::_getfield: { + int i = get_big_index(); + constantPoolOop constants = method()->constants(); + symbolOop field = constants->name_ref_at(i); + st->print_cr(" %d <%s>", i, field->as_C_string()); + } + break; + + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + { int i = get_big_index(); + constantPoolOop constants = method()->constants(); + symbolOop name = constants->name_ref_at(i); + symbolOop signature = constants->signature_ref_at(i); + st->print_cr(" %d <%s> <%s> ", i, name->as_C_string(), signature->as_C_string()); + } + break; + + case Bytecodes::_invokeinterface: + { int i = get_big_index(); + int n = get_index(); + get_index(); + constantPoolOop constants = method()->constants(); + symbolOop name = constants->name_ref_at(i); + symbolOop signature = constants->signature_ref_at(i); + st->print_cr(" %d <%s> <%s> %d", i, name->as_C_string(), signature->as_C_string(), n); + } + break; + + case Bytecodes::_new: + case Bytecodes::_checkcast: + case Bytecodes::_instanceof: + { int i = get_big_index(); + constantPoolOop constants = method()->constants(); + symbolOop name = constants->klass_name_at(i); + st->print_cr(" %d <%s>", i, name->as_C_string()); + } + break; + + case Bytecodes::_wide: + // length is zero not one, but printed with no more info. + break; + + default: + ShouldNotReachHere(); + break; + } +} + + +void BytecodePrinter::bytecode_epilog(int bci, outputStream* st) { + methodDataOop mdo = method()->method_data(); + if (mdo != NULL) { + ProfileData* data = mdo->bci_to_data(bci); + if (data != NULL) { + st->print(" %d", mdo->dp_to_di(data->dp())); + st->fill_to(6); + data->print_data_on(st); + } + } +} +#endif // PRODUCT diff --git a/hotspot/src/share/vm/interpreter/bytecodeTracer.hpp b/hotspot/src/share/vm/interpreter/bytecodeTracer.hpp new file mode 100644 index 00000000000..d2bacfe1db3 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodeTracer.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The BytecodeTracer is a helper class used by the interpreter for run-time +// bytecode tracing. If bytecode tracing is turned on, trace() will be called +// for each bytecode. +// +// By specialising the BytecodeClosure, all kinds of bytecode traces can +// be done. + +#ifndef PRODUCT +// class BytecodeTracer is only used by TraceBytecodes option + +class BytecodeClosure; +class BytecodeTracer: AllStatic { + private: + static BytecodeClosure* _closure; + + public: + static BytecodeClosure* std_closure(); // a printing closure + static BytecodeClosure* closure() { return _closure; } + static void set_closure(BytecodeClosure* closure) { _closure = closure; } + + static void trace(methodHandle method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st = tty); + static void trace(methodHandle method, address bcp, outputStream* st = tty); +}; + + +// For each bytecode, a BytecodeClosure's trace() routine will be called. + +class BytecodeClosure { + public: + virtual void trace(methodHandle method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st) = 0; + virtual void trace(methodHandle method, address bcp, outputStream* st) = 0; +}; + +#endif // !PRODUCT diff --git a/hotspot/src/share/vm/interpreter/bytecodes.cpp b/hotspot/src/share/vm/interpreter/bytecodes.cpp new file mode 100644 index 00000000000..1060a0797a7 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodes.cpp @@ -0,0 +1,435 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_bytecodes.cpp.incl" + + +// Windows AMD64 Compiler Hangs compiling this file +// unless optimization is off +#ifdef _M_AMD64 +#pragma optimize ("", off) +#endif + + +bool Bytecodes::_is_initialized = false; +const char* Bytecodes::_name [Bytecodes::number_of_codes]; +const char* Bytecodes::_format [Bytecodes::number_of_codes]; +const char* Bytecodes::_wide_format [Bytecodes::number_of_codes]; +BasicType Bytecodes::_result_type [Bytecodes::number_of_codes]; +s_char Bytecodes::_depth [Bytecodes::number_of_codes]; +u_char Bytecodes::_length [Bytecodes::number_of_codes]; +bool Bytecodes::_can_trap [Bytecodes::number_of_codes]; +Bytecodes::Code Bytecodes::_java_code [Bytecodes::number_of_codes]; +bool Bytecodes::_can_rewrite [Bytecodes::number_of_codes]; + + +Bytecodes::Code Bytecodes::code_at(methodOop method, int bci) { + return code_at(method->bcp_from(bci), method); +} + +Bytecodes::Code Bytecodes::non_breakpoint_code_at(address bcp, methodOop method) { + if (method == NULL) method = methodOopDesc::method_from_bcp(bcp); + return method->orig_bytecode_at(method->bci_from(bcp)); +} + +int Bytecodes::special_length_at(address bcp) { + Code code = code_at(bcp); + switch (code) { + case _wide: + return wide_length_for(cast(*(bcp + 1))); + case _tableswitch: + { address aligned_bcp = (address)round_to((intptr_t)bcp + 1, jintSize); + jlong lo = (jint)Bytes::get_Java_u4(aligned_bcp + 1*jintSize); + jlong hi = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize); + jlong len = (aligned_bcp - bcp) + (3 + hi - lo + 1)*jintSize; + // only return len if it can be represented as a positive int; + // return -1 otherwise + return (len > 0 && len == (int)len) ? len : -1; + } + + case _lookupswitch: // fall through + case _fast_binaryswitch: // fall through + case _fast_linearswitch: + { address aligned_bcp = (address)round_to((intptr_t)bcp + 1, jintSize); + jlong npairs = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize); + jlong len = (aligned_bcp - bcp) + (2 + 2*npairs)*jintSize; + // only return len if it can be represented as a positive int; + // return -1 otherwise + return (len > 0 && len == (int)len) ? len : -1; + } + } + return 0; +} + +// At a breakpoint instruction, this returns the breakpoint's length, +// otherwise, it's the same as special_length_at(). This is used by +// the RawByteCodeStream, which wants to see the actual bytecode +// values (including breakpoint). RawByteCodeStream is used by the +// verifier when reading in bytecode to verify. Other mechanisms that +// run at runtime (such as generateOopMaps) need to iterate over the code +// and don't expect to see breakpoints: they want to see the instruction +// which was replaces so that they can get the correct length and find +// the next bytecode. +int Bytecodes::raw_special_length_at(address bcp) { + Code code = code_or_bp_at(bcp); + if (code == _breakpoint) { + return 1; + } else { + return special_length_at(bcp); + } +} + + + +void Bytecodes::def(Code code, const char* name, const char* format, const char* wide_format, BasicType result_type, int depth, bool can_trap) { + def(code, name, format, wide_format, result_type, depth, can_trap, code); +} + + +void Bytecodes::def(Code code, const char* name, const char* format, const char* wide_format, BasicType result_type, int depth, bool can_trap, Code java_code) { + assert(wide_format == NULL || format != NULL, "short form must exist if there's a wide form"); + _name [code] = name; + _format [code] = format; + _wide_format [code] = wide_format; + _result_type [code] = result_type; + _depth [code] = depth; + _can_trap [code] = can_trap; + _length [code] = format != NULL ? (u_char)strlen(format) : 0; + _java_code [code] = java_code; + if (java_code != code) _can_rewrite[java_code] = true; +} + + +// Format strings interpretation: +// +// b: bytecode +// c: signed constant, Java byte-ordering +// i: unsigned index , Java byte-ordering +// j: unsigned index , native byte-ordering +// o: branch offset , Java byte-ordering +// _: unused/ignored +// w: wide bytecode +// +// Note: Right now the format strings are used for 2 purposes: +// 1. to specify the length of the bytecode +// (= number of characters in format string) +// 2. to specify the bytecode attributes +// +// The bytecode attributes are currently used only for bytecode tracing +// (see BytecodeTracer); thus if more specific format information is +// used, one would also have to adjust the bytecode tracer. +// +// Note: For bytecodes with variable length, the format string is the empty string. + +void Bytecodes::initialize() { + if (_is_initialized) return; + assert(number_of_codes <= 256, "too many bytecodes"); + + // initialize bytecode tables - didn't use static array initializers + // (such as {}) so we can do additional consistency checks and init- + // code is independent of actual bytecode numbering. + // + // Note 1: NULL for the format string means the bytecode doesn't exist + // in that form. + // + // Note 2: The result type is T_ILLEGAL for bytecodes where the top of stack + // type after execution is not only determined by the bytecode itself. + + // Java bytecodes + // bytecode bytecode name format wide f. result tp stk traps + def(_nop , "nop" , "b" , NULL , T_VOID , 0, false); + def(_aconst_null , "aconst_null" , "b" , NULL , T_OBJECT , 1, false); + def(_iconst_m1 , "iconst_m1" , "b" , NULL , T_INT , 1, false); + def(_iconst_0 , "iconst_0" , "b" , NULL , T_INT , 1, false); + def(_iconst_1 , "iconst_1" , "b" , NULL , T_INT , 1, false); + def(_iconst_2 , "iconst_2" , "b" , NULL , T_INT , 1, false); + def(_iconst_3 , "iconst_3" , "b" , NULL , T_INT , 1, false); + def(_iconst_4 , "iconst_4" , "b" , NULL , T_INT , 1, false); + def(_iconst_5 , "iconst_5" , "b" , NULL , T_INT , 1, false); + def(_lconst_0 , "lconst_0" , "b" , NULL , T_LONG , 2, false); + def(_lconst_1 , "lconst_1" , "b" , NULL , T_LONG , 2, false); + def(_fconst_0 , "fconst_0" , "b" , NULL , T_FLOAT , 1, false); + def(_fconst_1 , "fconst_1" , "b" , NULL , T_FLOAT , 1, false); + def(_fconst_2 , "fconst_2" , "b" , NULL , T_FLOAT , 1, false); + def(_dconst_0 , "dconst_0" , "b" , NULL , T_DOUBLE , 2, false); + def(_dconst_1 , "dconst_1" , "b" , NULL , T_DOUBLE , 2, false); + def(_bipush , "bipush" , "bc" , NULL , T_INT , 1, false); + def(_sipush , "sipush" , "bcc" , NULL , T_INT , 1, false); + def(_ldc , "ldc" , "bi" , NULL , T_ILLEGAL, 1, true ); + def(_ldc_w , "ldc_w" , "bii" , NULL , T_ILLEGAL, 1, true ); + def(_ldc2_w , "ldc2_w" , "bii" , NULL , T_ILLEGAL, 2, true ); + def(_iload , "iload" , "bi" , "wbii" , T_INT , 1, false); + def(_lload , "lload" , "bi" , "wbii" , T_LONG , 2, false); + def(_fload , "fload" , "bi" , "wbii" , T_FLOAT , 1, false); + def(_dload , "dload" , "bi" , "wbii" , T_DOUBLE , 2, false); + def(_aload , "aload" , "bi" , "wbii" , T_OBJECT , 1, false); + def(_iload_0 , "iload_0" , "b" , NULL , T_INT , 1, false); + def(_iload_1 , "iload_1" , "b" , NULL , T_INT , 1, false); + def(_iload_2 , "iload_2" , "b" , NULL , T_INT , 1, false); + def(_iload_3 , "iload_3" , "b" , NULL , T_INT , 1, false); + def(_lload_0 , "lload_0" , "b" , NULL , T_LONG , 2, false); + def(_lload_1 , "lload_1" , "b" , NULL , T_LONG , 2, false); + def(_lload_2 , "lload_2" , "b" , NULL , T_LONG , 2, false); + def(_lload_3 , "lload_3" , "b" , NULL , T_LONG , 2, false); + def(_fload_0 , "fload_0" , "b" , NULL , T_FLOAT , 1, false); + def(_fload_1 , "fload_1" , "b" , NULL , T_FLOAT , 1, false); + def(_fload_2 , "fload_2" , "b" , NULL , T_FLOAT , 1, false); + def(_fload_3 , "fload_3" , "b" , NULL , T_FLOAT , 1, false); + def(_dload_0 , "dload_0" , "b" , NULL , T_DOUBLE , 2, false); + def(_dload_1 , "dload_1" , "b" , NULL , T_DOUBLE , 2, false); + def(_dload_2 , "dload_2" , "b" , NULL , T_DOUBLE , 2, false); + def(_dload_3 , "dload_3" , "b" , NULL , T_DOUBLE , 2, false); + def(_aload_0 , "aload_0" , "b" , NULL , T_OBJECT , 1, true ); // rewriting in interpreter + def(_aload_1 , "aload_1" , "b" , NULL , T_OBJECT , 1, false); + def(_aload_2 , "aload_2" , "b" , NULL , T_OBJECT , 1, false); + def(_aload_3 , "aload_3" , "b" , NULL , T_OBJECT , 1, false); + def(_iaload , "iaload" , "b" , NULL , T_INT , -1, true ); + def(_laload , "laload" , "b" , NULL , T_LONG , 0, true ); + def(_faload , "faload" , "b" , NULL , T_FLOAT , -1, true ); + def(_daload , "daload" , "b" , NULL , T_DOUBLE , 0, true ); + def(_aaload , "aaload" , "b" , NULL , T_OBJECT , -1, true ); + def(_baload , "baload" , "b" , NULL , T_INT , -1, true ); + def(_caload , "caload" , "b" , NULL , T_INT , -1, true ); + def(_saload , "saload" , "b" , NULL , T_INT , -1, true ); + def(_istore , "istore" , "bi" , "wbii" , T_VOID , -1, false); + def(_lstore , "lstore" , "bi" , "wbii" , T_VOID , -2, false); + def(_fstore , "fstore" , "bi" , "wbii" , T_VOID , -1, false); + def(_dstore , "dstore" , "bi" , "wbii" , T_VOID , -2, false); + def(_astore , "astore" , "bi" , "wbii" , T_VOID , -1, false); + def(_istore_0 , "istore_0" , "b" , NULL , T_VOID , -1, false); + def(_istore_1 , "istore_1" , "b" , NULL , T_VOID , -1, false); + def(_istore_2 , "istore_2" , "b" , NULL , T_VOID , -1, false); + def(_istore_3 , "istore_3" , "b" , NULL , T_VOID , -1, false); + def(_lstore_0 , "lstore_0" , "b" , NULL , T_VOID , -2, false); + def(_lstore_1 , "lstore_1" , "b" , NULL , T_VOID , -2, false); + def(_lstore_2 , "lstore_2" , "b" , NULL , T_VOID , -2, false); + def(_lstore_3 , "lstore_3" , "b" , NULL , T_VOID , -2, false); + def(_fstore_0 , "fstore_0" , "b" , NULL , T_VOID , -1, false); + def(_fstore_1 , "fstore_1" , "b" , NULL , T_VOID , -1, false); + def(_fstore_2 , "fstore_2" , "b" , NULL , T_VOID , -1, false); + def(_fstore_3 , "fstore_3" , "b" , NULL , T_VOID , -1, false); + def(_dstore_0 , "dstore_0" , "b" , NULL , T_VOID , -2, false); + def(_dstore_1 , "dstore_1" , "b" , NULL , T_VOID , -2, false); + def(_dstore_2 , "dstore_2" , "b" , NULL , T_VOID , -2, false); + def(_dstore_3 , "dstore_3" , "b" , NULL , T_VOID , -2, false); + def(_astore_0 , "astore_0" , "b" , NULL , T_VOID , -1, false); + def(_astore_1 , "astore_1" , "b" , NULL , T_VOID , -1, false); + def(_astore_2 , "astore_2" , "b" , NULL , T_VOID , -1, false); + def(_astore_3 , "astore_3" , "b" , NULL , T_VOID , -1, false); + def(_iastore , "iastore" , "b" , NULL , T_VOID , -3, true ); + def(_lastore , "lastore" , "b" , NULL , T_VOID , -4, true ); + def(_fastore , "fastore" , "b" , NULL , T_VOID , -3, true ); + def(_dastore , "dastore" , "b" , NULL , T_VOID , -4, true ); + def(_aastore , "aastore" , "b" , NULL , T_VOID , -3, true ); + def(_bastore , "bastore" , "b" , NULL , T_VOID , -3, true ); + def(_castore , "castore" , "b" , NULL , T_VOID , -3, true ); + def(_sastore , "sastore" , "b" , NULL , T_VOID , -3, true ); + def(_pop , "pop" , "b" , NULL , T_VOID , -1, false); + def(_pop2 , "pop2" , "b" , NULL , T_VOID , -2, false); + def(_dup , "dup" , "b" , NULL , T_VOID , 1, false); + def(_dup_x1 , "dup_x1" , "b" , NULL , T_VOID , 1, false); + def(_dup_x2 , "dup_x2" , "b" , NULL , T_VOID , 1, false); + def(_dup2 , "dup2" , "b" , NULL , T_VOID , 2, false); + def(_dup2_x1 , "dup2_x1" , "b" , NULL , T_VOID , 2, false); + def(_dup2_x2 , "dup2_x2" , "b" , NULL , T_VOID , 2, false); + def(_swap , "swap" , "b" , NULL , T_VOID , 0, false); + def(_iadd , "iadd" , "b" , NULL , T_INT , -1, false); + def(_ladd , "ladd" , "b" , NULL , T_LONG , -2, false); + def(_fadd , "fadd" , "b" , NULL , T_FLOAT , -1, false); + def(_dadd , "dadd" , "b" , NULL , T_DOUBLE , -2, false); + def(_isub , "isub" , "b" , NULL , T_INT , -1, false); + def(_lsub , "lsub" , "b" , NULL , T_LONG , -2, false); + def(_fsub , "fsub" , "b" , NULL , T_FLOAT , -1, false); + def(_dsub , "dsub" , "b" , NULL , T_DOUBLE , -2, false); + def(_imul , "imul" , "b" , NULL , T_INT , -1, false); + def(_lmul , "lmul" , "b" , NULL , T_LONG , -2, false); + def(_fmul , "fmul" , "b" , NULL , T_FLOAT , -1, false); + def(_dmul , "dmul" , "b" , NULL , T_DOUBLE , -2, false); + def(_idiv , "idiv" , "b" , NULL , T_INT , -1, true ); + def(_ldiv , "ldiv" , "b" , NULL , T_LONG , -2, true ); + def(_fdiv , "fdiv" , "b" , NULL , T_FLOAT , -1, false); + def(_ddiv , "ddiv" , "b" , NULL , T_DOUBLE , -2, false); + def(_irem , "irem" , "b" , NULL , T_INT , -1, true ); + def(_lrem , "lrem" , "b" , NULL , T_LONG , -2, true ); + def(_frem , "frem" , "b" , NULL , T_FLOAT , -1, false); + def(_drem , "drem" , "b" , NULL , T_DOUBLE , -2, false); + def(_ineg , "ineg" , "b" , NULL , T_INT , 0, false); + def(_lneg , "lneg" , "b" , NULL , T_LONG , 0, false); + def(_fneg , "fneg" , "b" , NULL , T_FLOAT , 0, false); + def(_dneg , "dneg" , "b" , NULL , T_DOUBLE , 0, false); + def(_ishl , "ishl" , "b" , NULL , T_INT , -1, false); + def(_lshl , "lshl" , "b" , NULL , T_LONG , -1, false); + def(_ishr , "ishr" , "b" , NULL , T_INT , -1, false); + def(_lshr , "lshr" , "b" , NULL , T_LONG , -1, false); + def(_iushr , "iushr" , "b" , NULL , T_INT , -1, false); + def(_lushr , "lushr" , "b" , NULL , T_LONG , -1, false); + def(_iand , "iand" , "b" , NULL , T_INT , -1, false); + def(_land , "land" , "b" , NULL , T_LONG , -2, false); + def(_ior , "ior" , "b" , NULL , T_INT , -1, false); + def(_lor , "lor" , "b" , NULL , T_LONG , -2, false); + def(_ixor , "ixor" , "b" , NULL , T_INT , -1, false); + def(_lxor , "lxor" , "b" , NULL , T_LONG , -2, false); + def(_iinc , "iinc" , "bic" , "wbiicc", T_VOID , 0, false); + def(_i2l , "i2l" , "b" , NULL , T_LONG , 1, false); + def(_i2f , "i2f" , "b" , NULL , T_FLOAT , 0, false); + def(_i2d , "i2d" , "b" , NULL , T_DOUBLE , 1, false); + def(_l2i , "l2i" , "b" , NULL , T_INT , -1, false); + def(_l2f , "l2f" , "b" , NULL , T_FLOAT , -1, false); + def(_l2d , "l2d" , "b" , NULL , T_DOUBLE , 0, false); + def(_f2i , "f2i" , "b" , NULL , T_INT , 0, false); + def(_f2l , "f2l" , "b" , NULL , T_LONG , 1, false); + def(_f2d , "f2d" , "b" , NULL , T_DOUBLE , 1, false); + def(_d2i , "d2i" , "b" , NULL , T_INT , -1, false); + def(_d2l , "d2l" , "b" , NULL , T_LONG , 0, false); + def(_d2f , "d2f" , "b" , NULL , T_FLOAT , -1, false); + def(_i2b , "i2b" , "b" , NULL , T_BYTE , 0, false); + def(_i2c , "i2c" , "b" , NULL , T_CHAR , 0, false); + def(_i2s , "i2s" , "b" , NULL , T_SHORT , 0, false); + def(_lcmp , "lcmp" , "b" , NULL , T_VOID , -3, false); + def(_fcmpl , "fcmpl" , "b" , NULL , T_VOID , -1, false); + def(_fcmpg , "fcmpg" , "b" , NULL , T_VOID , -1, false); + def(_dcmpl , "dcmpl" , "b" , NULL , T_VOID , -3, false); + def(_dcmpg , "dcmpg" , "b" , NULL , T_VOID , -3, false); + def(_ifeq , "ifeq" , "boo" , NULL , T_VOID , -1, false); + def(_ifne , "ifne" , "boo" , NULL , T_VOID , -1, false); + def(_iflt , "iflt" , "boo" , NULL , T_VOID , -1, false); + def(_ifge , "ifge" , "boo" , NULL , T_VOID , -1, false); + def(_ifgt , "ifgt" , "boo" , NULL , T_VOID , -1, false); + def(_ifle , "ifle" , "boo" , NULL , T_VOID , -1, false); + def(_if_icmpeq , "if_icmpeq" , "boo" , NULL , T_VOID , -2, false); + def(_if_icmpne , "if_icmpne" , "boo" , NULL , T_VOID , -2, false); + def(_if_icmplt , "if_icmplt" , "boo" , NULL , T_VOID , -2, false); + def(_if_icmpge , "if_icmpge" , "boo" , NULL , T_VOID , -2, false); + def(_if_icmpgt , "if_icmpgt" , "boo" , NULL , T_VOID , -2, false); + def(_if_icmple , "if_icmple" , "boo" , NULL , T_VOID , -2, false); + def(_if_acmpeq , "if_acmpeq" , "boo" , NULL , T_VOID , -2, false); + def(_if_acmpne , "if_acmpne" , "boo" , NULL , T_VOID , -2, false); + def(_goto , "goto" , "boo" , NULL , T_VOID , 0, false); + def(_jsr , "jsr" , "boo" , NULL , T_INT , 0, false); + def(_ret , "ret" , "bi" , "wbii" , T_VOID , 0, false); + def(_tableswitch , "tableswitch" , "" , NULL , T_VOID , -1, false); // may have backward branches + def(_lookupswitch , "lookupswitch" , "" , NULL , T_VOID , -1, false); // rewriting in interpreter + def(_ireturn , "ireturn" , "b" , NULL , T_INT , -1, true); + def(_lreturn , "lreturn" , "b" , NULL , T_LONG , -2, true); + def(_freturn , "freturn" , "b" , NULL , T_FLOAT , -1, true); + def(_dreturn , "dreturn" , "b" , NULL , T_DOUBLE , -2, true); + def(_areturn , "areturn" , "b" , NULL , T_OBJECT , -1, true); + def(_return , "return" , "b" , NULL , T_VOID , 0, true); + def(_getstatic , "getstatic" , "bjj" , NULL , T_ILLEGAL, 1, true ); + def(_putstatic , "putstatic" , "bjj" , NULL , T_ILLEGAL, -1, true ); + def(_getfield , "getfield" , "bjj" , NULL , T_ILLEGAL, 0, true ); + def(_putfield , "putfield" , "bjj" , NULL , T_ILLEGAL, -2, true ); + def(_invokevirtual , "invokevirtual" , "bjj" , NULL , T_ILLEGAL, -1, true); + def(_invokespecial , "invokespecial" , "bjj" , NULL , T_ILLEGAL, -1, true); + def(_invokestatic , "invokestatic" , "bjj" , NULL , T_ILLEGAL, 0, true); + def(_invokeinterface , "invokeinterface" , "bjj__", NULL , T_ILLEGAL, -1, true); + def(_xxxunusedxxx , "xxxunusedxxx" , NULL , NULL , T_VOID , 0, false); + def(_new , "new" , "bii" , NULL , T_OBJECT , 1, true ); + def(_newarray , "newarray" , "bc" , NULL , T_OBJECT , 0, true ); + def(_anewarray , "anewarray" , "bii" , NULL , T_OBJECT , 0, true ); + def(_arraylength , "arraylength" , "b" , NULL , T_VOID , 0, true ); + def(_athrow , "athrow" , "b" , NULL , T_VOID , -1, true ); + def(_checkcast , "checkcast" , "bii" , NULL , T_OBJECT , 0, true ); + def(_instanceof , "instanceof" , "bii" , NULL , T_INT , 0, true ); + def(_monitorenter , "monitorenter" , "b" , NULL , T_VOID , -1, true ); + def(_monitorexit , "monitorexit" , "b" , NULL , T_VOID , -1, true ); + def(_wide , "wide" , "" , NULL , T_VOID , 0, false); + def(_multianewarray , "multianewarray" , "biic" , NULL , T_OBJECT , 1, true ); + def(_ifnull , "ifnull" , "boo" , NULL , T_VOID , -1, false); + def(_ifnonnull , "ifnonnull" , "boo" , NULL , T_VOID , -1, false); + def(_goto_w , "goto_w" , "boooo", NULL , T_VOID , 0, false); + def(_jsr_w , "jsr_w" , "boooo", NULL , T_INT , 0, false); + def(_breakpoint , "breakpoint" , "" , NULL , T_VOID , 0, true); + + // JVM bytecodes + // bytecode bytecode name format wide f. result tp stk traps std code + + def(_fast_agetfield , "fast_agetfield" , "bjj" , NULL , T_OBJECT , 0, true , _getfield ); + def(_fast_bgetfield , "fast_bgetfield" , "bjj" , NULL , T_INT , 0, true , _getfield ); + def(_fast_cgetfield , "fast_cgetfield" , "bjj" , NULL , T_CHAR , 0, true , _getfield ); + def(_fast_dgetfield , "fast_dgetfield" , "bjj" , NULL , T_DOUBLE , 0, true , _getfield ); + def(_fast_fgetfield , "fast_fgetfield" , "bjj" , NULL , T_FLOAT , 0, true , _getfield ); + def(_fast_igetfield , "fast_igetfield" , "bjj" , NULL , T_INT , 0, true , _getfield ); + def(_fast_lgetfield , "fast_lgetfield" , "bjj" , NULL , T_LONG , 0, true , _getfield ); + def(_fast_sgetfield , "fast_sgetfield" , "bjj" , NULL , T_SHORT , 0, true , _getfield ); + + def(_fast_aputfield , "fast_aputfield" , "bjj" , NULL , T_OBJECT , 0, true , _putfield ); + def(_fast_bputfield , "fast_bputfield" , "bjj" , NULL , T_INT , 0, true , _putfield ); + def(_fast_cputfield , "fast_cputfield" , "bjj" , NULL , T_CHAR , 0, true , _putfield ); + def(_fast_dputfield , "fast_dputfield" , "bjj" , NULL , T_DOUBLE , 0, true , _putfield ); + def(_fast_fputfield , "fast_fputfield" , "bjj" , NULL , T_FLOAT , 0, true , _putfield ); + def(_fast_iputfield , "fast_iputfield" , "bjj" , NULL , T_INT , 0, true , _putfield ); + def(_fast_lputfield , "fast_lputfield" , "bjj" , NULL , T_LONG , 0, true , _putfield ); + def(_fast_sputfield , "fast_sputfield" , "bjj" , NULL , T_SHORT , 0, true , _putfield ); + + def(_fast_aload_0 , "fast_aload_0" , "b" , NULL , T_OBJECT , 1, true , _aload_0 ); + def(_fast_iaccess_0 , "fast_iaccess_0" , "b_jj" , NULL , T_INT , 1, true , _aload_0 ); + def(_fast_aaccess_0 , "fast_aaccess_0" , "b_jj" , NULL , T_OBJECT , 1, true , _aload_0 ); + def(_fast_faccess_0 , "fast_faccess_0" , "b_jj" , NULL , T_OBJECT , 1, true , _aload_0 ); + + def(_fast_iload , "fast_iload" , "bi" , NULL , T_INT , 1, false, _iload); + def(_fast_iload2 , "fast_iload2" , "bi_i" , NULL , T_INT , 2, false, _iload); + def(_fast_icaload , "fast_icaload" , "bi_" , NULL , T_INT , 0, false, _iload); + + // Faster method invocation. + def(_fast_invokevfinal , "fast_invokevfinal" , "bjj" , NULL , T_ILLEGAL, -1, true, _invokevirtual ); + + def(_fast_linearswitch , "fast_linearswitch" , "" , NULL , T_VOID , -1, false, _lookupswitch ); + def(_fast_binaryswitch , "fast_binaryswitch" , "" , NULL , T_VOID , -1, false, _lookupswitch ); + + def(_return_register_finalizer , "return_register_finalizer" , "b" , NULL , T_VOID , 0, true, _return); + + def(_shouldnotreachhere , "_shouldnotreachhere" , "b" , NULL , T_VOID , 0, false); + + // platform specific JVM bytecodes + pd_initialize(); + + // compare can_trap information for each bytecode with the + // can_trap information for the corresponding base bytecode + // (if a rewritten bytecode can trap, so must the base bytecode) + #ifdef ASSERT + { for (int i = 0; i < number_of_codes; i++) { + if (is_defined(i)) { + Code code = cast(i); + Code java = java_code(code); + if (can_trap(code) && !can_trap(java)) fatal2("%s can trap => %s can trap, too", name(code), name(java)); + } + } + } + #endif + + // initialization successful + _is_initialized = true; +} + + +void bytecodes_init() { + Bytecodes::initialize(); +} + +// Restore optimization +#ifdef _M_AMD64 +#pragma optimize ("", on) +#endif diff --git a/hotspot/src/share/vm/interpreter/bytecodes.hpp b/hotspot/src/share/vm/interpreter/bytecodes.hpp new file mode 100644 index 00000000000..16ddc863bd6 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/bytecodes.hpp @@ -0,0 +1,358 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Bytecodes specifies all bytecodes used in the VM and +// provides utility functions to get bytecode attributes. + +// NOTE: replicated in SA in vm/agent/sun/jvm/hotspot/interpreter/Bytecodes.java +class Bytecodes: AllStatic { + public: + enum Code { + _illegal = -1, + + // Java bytecodes + _nop = 0, // 0x00 + _aconst_null = 1, // 0x01 + _iconst_m1 = 2, // 0x02 + _iconst_0 = 3, // 0x03 + _iconst_1 = 4, // 0x04 + _iconst_2 = 5, // 0x05 + _iconst_3 = 6, // 0x06 + _iconst_4 = 7, // 0x07 + _iconst_5 = 8, // 0x08 + _lconst_0 = 9, // 0x09 + _lconst_1 = 10, // 0x0a + _fconst_0 = 11, // 0x0b + _fconst_1 = 12, // 0x0c + _fconst_2 = 13, // 0x0d + _dconst_0 = 14, // 0x0e + _dconst_1 = 15, // 0x0f + _bipush = 16, // 0x10 + _sipush = 17, // 0x11 + _ldc = 18, // 0x12 + _ldc_w = 19, // 0x13 + _ldc2_w = 20, // 0x14 + _iload = 21, // 0x15 + _lload = 22, // 0x16 + _fload = 23, // 0x17 + _dload = 24, // 0x18 + _aload = 25, // 0x19 + _iload_0 = 26, // 0x1a + _iload_1 = 27, // 0x1b + _iload_2 = 28, // 0x1c + _iload_3 = 29, // 0x1d + _lload_0 = 30, // 0x1e + _lload_1 = 31, // 0x1f + _lload_2 = 32, // 0x20 + _lload_3 = 33, // 0x21 + _fload_0 = 34, // 0x22 + _fload_1 = 35, // 0x23 + _fload_2 = 36, // 0x24 + _fload_3 = 37, // 0x25 + _dload_0 = 38, // 0x26 + _dload_1 = 39, // 0x27 + _dload_2 = 40, // 0x28 + _dload_3 = 41, // 0x29 + _aload_0 = 42, // 0x2a + _aload_1 = 43, // 0x2b + _aload_2 = 44, // 0x2c + _aload_3 = 45, // 0x2d + _iaload = 46, // 0x2e + _laload = 47, // 0x2f + _faload = 48, // 0x30 + _daload = 49, // 0x31 + _aaload = 50, // 0x32 + _baload = 51, // 0x33 + _caload = 52, // 0x34 + _saload = 53, // 0x35 + _istore = 54, // 0x36 + _lstore = 55, // 0x37 + _fstore = 56, // 0x38 + _dstore = 57, // 0x39 + _astore = 58, // 0x3a + _istore_0 = 59, // 0x3b + _istore_1 = 60, // 0x3c + _istore_2 = 61, // 0x3d + _istore_3 = 62, // 0x3e + _lstore_0 = 63, // 0x3f + _lstore_1 = 64, // 0x40 + _lstore_2 = 65, // 0x41 + _lstore_3 = 66, // 0x42 + _fstore_0 = 67, // 0x43 + _fstore_1 = 68, // 0x44 + _fstore_2 = 69, // 0x45 + _fstore_3 = 70, // 0x46 + _dstore_0 = 71, // 0x47 + _dstore_1 = 72, // 0x48 + _dstore_2 = 73, // 0x49 + _dstore_3 = 74, // 0x4a + _astore_0 = 75, // 0x4b + _astore_1 = 76, // 0x4c + _astore_2 = 77, // 0x4d + _astore_3 = 78, // 0x4e + _iastore = 79, // 0x4f + _lastore = 80, // 0x50 + _fastore = 81, // 0x51 + _dastore = 82, // 0x52 + _aastore = 83, // 0x53 + _bastore = 84, // 0x54 + _castore = 85, // 0x55 + _sastore = 86, // 0x56 + _pop = 87, // 0x57 + _pop2 = 88, // 0x58 + _dup = 89, // 0x59 + _dup_x1 = 90, // 0x5a + _dup_x2 = 91, // 0x5b + _dup2 = 92, // 0x5c + _dup2_x1 = 93, // 0x5d + _dup2_x2 = 94, // 0x5e + _swap = 95, // 0x5f + _iadd = 96, // 0x60 + _ladd = 97, // 0x61 + _fadd = 98, // 0x62 + _dadd = 99, // 0x63 + _isub = 100, // 0x64 + _lsub = 101, // 0x65 + _fsub = 102, // 0x66 + _dsub = 103, // 0x67 + _imul = 104, // 0x68 + _lmul = 105, // 0x69 + _fmul = 106, // 0x6a + _dmul = 107, // 0x6b + _idiv = 108, // 0x6c + _ldiv = 109, // 0x6d + _fdiv = 110, // 0x6e + _ddiv = 111, // 0x6f + _irem = 112, // 0x70 + _lrem = 113, // 0x71 + _frem = 114, // 0x72 + _drem = 115, // 0x73 + _ineg = 116, // 0x74 + _lneg = 117, // 0x75 + _fneg = 118, // 0x76 + _dneg = 119, // 0x77 + _ishl = 120, // 0x78 + _lshl = 121, // 0x79 + _ishr = 122, // 0x7a + _lshr = 123, // 0x7b + _iushr = 124, // 0x7c + _lushr = 125, // 0x7d + _iand = 126, // 0x7e + _land = 127, // 0x7f + _ior = 128, // 0x80 + _lor = 129, // 0x81 + _ixor = 130, // 0x82 + _lxor = 131, // 0x83 + _iinc = 132, // 0x84 + _i2l = 133, // 0x85 + _i2f = 134, // 0x86 + _i2d = 135, // 0x87 + _l2i = 136, // 0x88 + _l2f = 137, // 0x89 + _l2d = 138, // 0x8a + _f2i = 139, // 0x8b + _f2l = 140, // 0x8c + _f2d = 141, // 0x8d + _d2i = 142, // 0x8e + _d2l = 143, // 0x8f + _d2f = 144, // 0x90 + _i2b = 145, // 0x91 + _i2c = 146, // 0x92 + _i2s = 147, // 0x93 + _lcmp = 148, // 0x94 + _fcmpl = 149, // 0x95 + _fcmpg = 150, // 0x96 + _dcmpl = 151, // 0x97 + _dcmpg = 152, // 0x98 + _ifeq = 153, // 0x99 + _ifne = 154, // 0x9a + _iflt = 155, // 0x9b + _ifge = 156, // 0x9c + _ifgt = 157, // 0x9d + _ifle = 158, // 0x9e + _if_icmpeq = 159, // 0x9f + _if_icmpne = 160, // 0xa0 + _if_icmplt = 161, // 0xa1 + _if_icmpge = 162, // 0xa2 + _if_icmpgt = 163, // 0xa3 + _if_icmple = 164, // 0xa4 + _if_acmpeq = 165, // 0xa5 + _if_acmpne = 166, // 0xa6 + _goto = 167, // 0xa7 + _jsr = 168, // 0xa8 + _ret = 169, // 0xa9 + _tableswitch = 170, // 0xaa + _lookupswitch = 171, // 0xab + _ireturn = 172, // 0xac + _lreturn = 173, // 0xad + _freturn = 174, // 0xae + _dreturn = 175, // 0xaf + _areturn = 176, // 0xb0 + _return = 177, // 0xb1 + _getstatic = 178, // 0xb2 + _putstatic = 179, // 0xb3 + _getfield = 180, // 0xb4 + _putfield = 181, // 0xb5 + _invokevirtual = 182, // 0xb6 + _invokespecial = 183, // 0xb7 + _invokestatic = 184, // 0xb8 + _invokeinterface = 185, // 0xb9 + _xxxunusedxxx = 186, // 0xba + _new = 187, // 0xbb + _newarray = 188, // 0xbc + _anewarray = 189, // 0xbd + _arraylength = 190, // 0xbe + _athrow = 191, // 0xbf + _checkcast = 192, // 0xc0 + _instanceof = 193, // 0xc1 + _monitorenter = 194, // 0xc2 + _monitorexit = 195, // 0xc3 + _wide = 196, // 0xc4 + _multianewarray = 197, // 0xc5 + _ifnull = 198, // 0xc6 + _ifnonnull = 199, // 0xc7 + _goto_w = 200, // 0xc8 + _jsr_w = 201, // 0xc9 + _breakpoint = 202, // 0xca + + number_of_java_codes, + + // JVM bytecodes + _fast_agetfield = number_of_java_codes, + _fast_bgetfield , + _fast_cgetfield , + _fast_dgetfield , + _fast_fgetfield , + _fast_igetfield , + _fast_lgetfield , + _fast_sgetfield , + + _fast_aputfield , + _fast_bputfield , + _fast_cputfield , + _fast_dputfield , + _fast_fputfield , + _fast_iputfield , + _fast_lputfield , + _fast_sputfield , + + _fast_aload_0 , + _fast_iaccess_0 , + _fast_aaccess_0 , + _fast_faccess_0 , + + _fast_iload , + _fast_iload2 , + _fast_icaload , + + _fast_invokevfinal , + _fast_linearswitch , + _fast_binaryswitch , + + _return_register_finalizer , + + _shouldnotreachhere, // For debugging + + // Platform specific JVM bytecodes + #include "incls/_bytecodes_pd.hpp.incl" + + number_of_codes + }; + + private: + static bool _is_initialized; + static const char* _name [number_of_codes]; + static const char* _format [number_of_codes]; + static const char* _wide_format [number_of_codes]; + static BasicType _result_type [number_of_codes]; + static s_char _depth [number_of_codes]; + static u_char _length [number_of_codes]; + static bool _can_trap [number_of_codes]; + static Code _java_code [number_of_codes]; + static bool _can_rewrite [number_of_codes]; + + static void def(Code code, const char* name, const char* format, const char* wide_format, BasicType result_type, int depth, bool can_trap); + static void def(Code code, const char* name, const char* format, const char* wide_format, BasicType result_type, int depth, bool can_trap, Code java_code); + static void pd_initialize(); // platform specific initialization + static Code pd_base_code_for(Code code); // platform specific base_code_for implementation + + public: + // Conversion + static void check (Code code) { assert(is_defined(code), "illegal code"); } + static void wide_check (Code code) { assert(wide_is_defined(code), "illegal code"); } + static Code cast (int code) { return (Code)code; } + + + // Fetch a bytecode, hiding breakpoints as necessary: + static Code code_at(address bcp, methodOop method = NULL) { + Code code = cast(*bcp); return (code != _breakpoint) ? code : non_breakpoint_code_at(bcp, method); + } + static Code java_code_at(address bcp, methodOop method = NULL) { + return java_code(code_at(bcp, method)); + } + + // Fetch a bytecode or a breakpoint: + static Code code_or_bp_at(address bcp) { return (Code)cast(*bcp); } + + static Code code_at(methodOop method, int bci); + static bool is_active_breakpoint_at(address bcp) { return (Code)*bcp == _breakpoint; } + + // find a bytecode, behind a breakpoint if necessary: + static Code non_breakpoint_code_at(address bcp, methodOop method = NULL); + + // Bytecode attributes + static bool is_defined (int code) { return 0 <= code && code < number_of_codes && _format[code] != NULL; } + static bool wide_is_defined(int code) { return is_defined(code) && _wide_format[code] != NULL; } + static const char* name (Code code) { check(code); return _name [code]; } + static const char* format (Code code) { check(code); return _format [code]; } + static const char* wide_format (Code code) { return _wide_format[code]; } + static BasicType result_type (Code code) { check(code); return _result_type [code]; } + static int depth (Code code) { check(code); return _depth [code]; } + static int length_for (Code code) { return _length[code]; } + static bool can_trap (Code code) { check(code); return _can_trap [code]; } + static Code java_code (Code code) { check(code); return _java_code [code]; } + static bool can_rewrite (Code code) { check(code); return _can_rewrite [code]; } + static int wide_length_for(Code code) { + if (!is_defined(code)) { + return 0; + } + const char* wf = wide_format(code); + return (wf == NULL) ? 0 : (int)strlen(wf); + } + static int special_length_at(address bcp); + static int raw_special_length_at(address bcp); + static int length_at (address bcp) { int l = length_for(code_at(bcp)); return l > 0 ? l : special_length_at(bcp); } + static int java_length_at (address bcp) { int l = length_for(java_code_at(bcp)); return l > 0 ? l : special_length_at(bcp); } + static bool is_java_code (Code code) { return 0 <= code && code < number_of_java_codes; } + + static bool is_aload (Code code) { return (code == _aload || code == _aload_0 || code == _aload_1 + || code == _aload_2 || code == _aload_3); } + static bool is_astore (Code code) { return (code == _astore || code == _astore_0 || code == _astore_1 + || code == _astore_2 || code == _astore_3); } + + static bool is_zero_const (Code code) { return (code == _aconst_null || code == _iconst_0 + || code == _fconst_0 || code == _dconst_0); } + // Initialization + static void initialize (); +}; diff --git a/hotspot/src/share/vm/interpreter/cppInterpreter.cpp b/hotspot/src/share/vm/interpreter/cppInterpreter.cpp new file mode 100644 index 00000000000..d51e67803e3 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/cppInterpreter.cpp @@ -0,0 +1,135 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_cppInterpreter.cpp.incl" + +#ifdef CC_INTERP +# define __ _masm-> + +void CppInterpreter::initialize() { + if (_code != NULL) return; + AbstractInterpreter::initialize(); + + // generate interpreter + { ResourceMark rm; + TraceTime timer("Interpreter generation", TraceStartupTime); + int code_size = InterpreterCodeSize; + NOT_PRODUCT(code_size *= 4;) // debug uses extra interpreter code space + _code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL, + "Interpreter"); + InterpreterGenerator g(_code); + if (PrintInterpreter) print(); + } + + + // Allow c++ interpreter to do one initialization now that switches are set, etc. + BytecodeInterpreter start_msg(BytecodeInterpreter::initialize); + if (JvmtiExport::can_post_interpreter_events()) + BytecodeInterpreter::runWithChecks(&start_msg); + else + BytecodeInterpreter::run(&start_msg); +} + + +address CppInterpreter::_tosca_to_stack [AbstractInterpreter::number_of_result_handlers]; +address CppInterpreter::_stack_to_stack [AbstractInterpreter::number_of_result_handlers]; +address CppInterpreter::_stack_to_native_abi [AbstractInterpreter::number_of_result_handlers]; + +CppInterpreterGenerator::CppInterpreterGenerator(StubQueue* _code): AbstractInterpreterGenerator(_code) { +} + +static const BasicType types[Interpreter::number_of_result_handlers] = { + T_BOOLEAN, + T_CHAR , + T_BYTE , + T_SHORT , + T_INT , + T_LONG , + T_VOID , + T_FLOAT , + T_DOUBLE , + T_OBJECT +}; + +void CppInterpreterGenerator::generate_all() { + AbstractInterpreterGenerator::generate_all(); + + { CodeletMark cm(_masm, "result handlers for native calls"); + // The various result converter stublets. + int is_generated[Interpreter::number_of_result_handlers]; + memset(is_generated, 0, sizeof(is_generated)); + int _tosca_to_stack_is_generated[Interpreter::number_of_result_handlers]; + int _stack_to_stack_is_generated[Interpreter::number_of_result_handlers]; + int _stack_to_native_abi_is_generated[Interpreter::number_of_result_handlers]; + + memset(_tosca_to_stack_is_generated, 0, sizeof(_tosca_to_stack_is_generated)); + memset(_stack_to_stack_is_generated, 0, sizeof(_stack_to_stack_is_generated)); + memset(_stack_to_native_abi_is_generated, 0, sizeof(_stack_to_native_abi_is_generated)); + for (int i = 0; i < Interpreter::number_of_result_handlers; i++) { + BasicType type = types[i]; + if (!is_generated[Interpreter::BasicType_as_index(type)]++) { + Interpreter::_native_abi_to_tosca[Interpreter::BasicType_as_index(type)] = generate_result_handler_for(type); + } + if (!_tosca_to_stack_is_generated[Interpreter::BasicType_as_index(type)]++) { + Interpreter::_tosca_to_stack[Interpreter::BasicType_as_index(type)] = generate_tosca_to_stack_converter(type); + } + if (!_stack_to_stack_is_generated[Interpreter::BasicType_as_index(type)]++) { + Interpreter::_stack_to_stack[Interpreter::BasicType_as_index(type)] = generate_stack_to_stack_converter(type); + } + if (!_stack_to_native_abi_is_generated[Interpreter::BasicType_as_index(type)]++) { + Interpreter::_stack_to_native_abi[Interpreter::BasicType_as_index(type)] = generate_stack_to_native_abi_converter(type); + } + } + } + + +#define method_entry(kind) Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind) + + { CodeletMark cm(_masm, "(kind = frame_manager)"); + // all non-native method kinds + method_entry(zerolocals); + method_entry(zerolocals_synchronized); + method_entry(empty); + method_entry(accessor); + method_entry(abstract); + method_entry(java_lang_math_sin ); + method_entry(java_lang_math_cos ); + method_entry(java_lang_math_tan ); + method_entry(java_lang_math_abs ); + method_entry(java_lang_math_sqrt ); + method_entry(java_lang_math_log ); + method_entry(java_lang_math_log10 ); + Interpreter::_native_entry_begin = Interpreter::code()->code_end(); + method_entry(native); + method_entry(native_synchronized); + Interpreter::_native_entry_end = Interpreter::code()->code_end(); + } + + +#undef method_entry + +} + +#endif // CC_INTERP diff --git a/hotspot/src/share/vm/interpreter/cppInterpreter.hpp b/hotspot/src/share/vm/interpreter/cppInterpreter.hpp new file mode 100644 index 00000000000..9e4b87fba5a --- /dev/null +++ b/hotspot/src/share/vm/interpreter/cppInterpreter.hpp @@ -0,0 +1,83 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifdef CC_INTERP + +// This file contains the platform-independant parts +// of the c++ interpreter + +class CppInterpreter: public AbstractInterpreter { + friend class VMStructs; + friend class Interpreter; // contains() + friend class InterpreterGenerator; // result handlers + friend class CppInterpreterGenerator; // result handlers + public: + + + protected: + + // tosca result -> stack result + static address _tosca_to_stack[number_of_result_handlers]; // converts tosca to C++ interpreter stack result + // stack result -> stack result + static address _stack_to_stack[number_of_result_handlers]; // pass result between C++ interpreter calls + // stack result -> native abi result + static address _stack_to_native_abi[number_of_result_handlers]; // converts C++ interpreter results to native abi + + // this is to allow frame and only frame to use contains(). + friend class frame; + + public: + // Initialization/debugging + static void initialize(); + // this only returns whether a pc is within generated code for the interpreter. + + // This is a moderately dubious interface for the c++ interpreter. Only + // frame code and debug.cpp should be using it. + static bool contains(address pc); + + public: + + + // No displatch table to switch so no need for these to do anything special + static void notice_safepoints() {} + static void ignore_safepoints() {} + + static address native_result_to_tosca() { return (address)_native_abi_to_tosca; } // aka result handler + static address tosca_result_to_stack() { return (address)_tosca_to_stack; } + static address stack_result_to_stack() { return (address)_stack_to_stack; } + static address stack_result_to_native() { return (address)_stack_to_native_abi; } + + static address native_result_to_tosca(int index) { return _native_abi_to_tosca[index]; } // aka result handler + static address tosca_result_to_stack(int index) { return _tosca_to_stack[index]; } + static address stack_result_to_stack(int index) { return _stack_to_stack[index]; } + static address stack_result_to_native(int index) { return _stack_to_native_abi[index]; } + + static address return_entry (TosState state, int length); + static address deopt_entry (TosState state, int length); + +#include "incls/_cppInterpreter_pd.hpp.incl" + +}; + +#endif // CC_INTERP diff --git a/hotspot/src/share/vm/interpreter/cppInterpreterGenerator.hpp b/hotspot/src/share/vm/interpreter/cppInterpreterGenerator.hpp new file mode 100644 index 00000000000..3ce3ce15628 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/cppInterpreterGenerator.hpp @@ -0,0 +1,47 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file contains the platform-independant parts +// of the template interpreter generator. + +#ifdef CC_INTERP + +class CppInterpreterGenerator: public AbstractInterpreterGenerator { + protected: + // shared code sequences + // Converter for native abi result to tosca result + address generate_result_handler_for(BasicType type); + address generate_tosca_to_stack_converter(BasicType type); + address generate_stack_to_stack_converter(BasicType type); + address generate_stack_to_native_abi_converter(BasicType type); + + void generate_all(); + + public: + CppInterpreterGenerator(StubQueue* _code); + + #include "incls/_cppInterpreterGenerator_pd.hpp.incl" +}; + +#endif // CC_INTERP diff --git a/hotspot/src/share/vm/interpreter/interpreter.cpp b/hotspot/src/share/vm/interpreter/interpreter.cpp new file mode 100644 index 00000000000..e1fac953330 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/interpreter.cpp @@ -0,0 +1,409 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreter.cpp.incl" + +# define __ _masm-> + + +//------------------------------------------------------------------------------------------------------------------------ +// Implementation of InterpreterCodelet + +void InterpreterCodelet::initialize(const char* description, Bytecodes::Code bytecode) { + _description = description; + _bytecode = bytecode; +} + + +void InterpreterCodelet::verify() { +} + + +void InterpreterCodelet::print() { + if (PrintInterpreter) { + tty->cr(); + tty->print_cr("----------------------------------------------------------------------"); + } + + if (description() != NULL) tty->print("%s ", description()); + if (bytecode() >= 0 ) tty->print("%d %s ", bytecode(), Bytecodes::name(bytecode())); + tty->print_cr("[" INTPTR_FORMAT ", " INTPTR_FORMAT "] %d bytes", + code_begin(), code_end(), code_size()); + + if (PrintInterpreter) { + tty->cr(); + Disassembler::decode(code_begin(), code_end(), tty); + } +} + + +//------------------------------------------------------------------------------------------------------------------------ +// Implementation of platform independent aspects of Interpreter + +void AbstractInterpreter::initialize() { + if (_code != NULL) return; + + // make sure 'imported' classes are initialized + if (CountBytecodes || TraceBytecodes || StopInterpreterAt) BytecodeCounter::reset(); + if (PrintBytecodeHistogram) BytecodeHistogram::reset(); + if (PrintBytecodePairHistogram) BytecodePairHistogram::reset(); + + InvocationCounter::reinitialize(DelayCompilationDuringStartup); + +} + +void AbstractInterpreter::print() { + tty->cr(); + tty->print_cr("----------------------------------------------------------------------"); + tty->print_cr("Interpreter"); + tty->cr(); + tty->print_cr("code size = %6dK bytes", (int)_code->used_space()/1024); + tty->print_cr("total space = %6dK bytes", (int)_code->total_space()/1024); + tty->print_cr("wasted space = %6dK bytes", (int)_code->available_space()/1024); + tty->cr(); + tty->print_cr("# of codelets = %6d" , _code->number_of_stubs()); + tty->print_cr("avg codelet size = %6d bytes", _code->used_space() / _code->number_of_stubs()); + tty->cr(); + _code->print(); + tty->print_cr("----------------------------------------------------------------------"); + tty->cr(); +} + + +void interpreter_init() { + Interpreter::initialize(); +#ifndef PRODUCT + if (TraceBytecodes) BytecodeTracer::set_closure(BytecodeTracer::std_closure()); +#endif // PRODUCT + // need to hit every safepoint in order to call zapping routine + // register the interpreter + VTune::register_stub( + "Interpreter", + AbstractInterpreter::code()->code_start(), + AbstractInterpreter::code()->code_end() + ); + Forte::register_stub( + "Interpreter", + AbstractInterpreter::code()->code_start(), + AbstractInterpreter::code()->code_end() + ); + + // notify JVMTI profiler + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated("Interpreter", + AbstractInterpreter::code()->code_start(), + AbstractInterpreter::code()->code_end()); + } +} + +//------------------------------------------------------------------------------------------------------------------------ +// Implementation of interpreter + +StubQueue* AbstractInterpreter::_code = NULL; +bool AbstractInterpreter::_notice_safepoints = false; +address AbstractInterpreter::_rethrow_exception_entry = NULL; + +address AbstractInterpreter::_native_entry_begin = NULL; +address AbstractInterpreter::_native_entry_end = NULL; +address AbstractInterpreter::_slow_signature_handler; +address AbstractInterpreter::_entry_table [AbstractInterpreter::number_of_method_entries]; +address AbstractInterpreter::_native_abi_to_tosca [AbstractInterpreter::number_of_result_handlers]; + +//------------------------------------------------------------------------------------------------------------------------ +// Generation of complete interpreter + +AbstractInterpreterGenerator::AbstractInterpreterGenerator(StubQueue* _code) { + _masm = NULL; +} + + +static const BasicType types[Interpreter::number_of_result_handlers] = { + T_BOOLEAN, + T_CHAR , + T_BYTE , + T_SHORT , + T_INT , + T_LONG , + T_VOID , + T_FLOAT , + T_DOUBLE , + T_OBJECT +}; + +void AbstractInterpreterGenerator::generate_all() { + + + { CodeletMark cm(_masm, "slow signature handler"); + Interpreter::_slow_signature_handler = generate_slow_signature_handler(); + } + +} + +//------------------------------------------------------------------------------------------------------------------------ +// Entry points + +AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(methodHandle m) { + // Abstract method? + if (m->is_abstract()) return abstract; + + // Native method? + // Note: This test must come _before_ the test for intrinsic + // methods. See also comments below. + if (m->is_native()) { + return m->is_synchronized() ? native_synchronized : native; + } + + // Synchronized? + if (m->is_synchronized()) { + return zerolocals_synchronized; + } + + if (RegisterFinalizersAtInit && m->code_size() == 1 && + m->intrinsic_id() == vmIntrinsics::_Object_init) { + // We need to execute the special return bytecode to check for + // finalizer registration so create a normal frame. + return zerolocals; + } + + // Empty method? + if (m->is_empty_method()) { + return empty; + } + + // Accessor method? + if (m->is_accessor()) { + assert(m->size_of_parameters() == 1, "fast code for accessors assumes parameter size = 1"); + return accessor; + } + + // Special intrinsic method? + // Note: This test must come _after_ the test for native methods, + // otherwise we will run into problems with JDK 1.2, see also + // AbstractInterpreterGenerator::generate_method_entry() for + // for details. + switch (m->intrinsic_id()) { + case vmIntrinsics::_dsin : return java_lang_math_sin ; + case vmIntrinsics::_dcos : return java_lang_math_cos ; + case vmIntrinsics::_dtan : return java_lang_math_tan ; + case vmIntrinsics::_dabs : return java_lang_math_abs ; + case vmIntrinsics::_dsqrt : return java_lang_math_sqrt ; + case vmIntrinsics::_dlog : return java_lang_math_log ; + case vmIntrinsics::_dlog10: return java_lang_math_log10; + } + + // Note: for now: zero locals for all non-empty methods + return zerolocals; +} + + +// Return true if the interpreter can prove that the given bytecode has +// not yet been executed (in Java semantics, not in actual operation). +bool AbstractInterpreter::is_not_reached(methodHandle method, int bci) { + address bcp = method->bcp_from(bci); + + if (!Bytecode_at(bcp)->must_rewrite()) { + // might have been reached + return false; + } + + // the bytecode might not be rewritten if the method is an accessor, etc. + address ientry = method->interpreter_entry(); + if (ientry != entry_for_kind(AbstractInterpreter::zerolocals) && + ientry != entry_for_kind(AbstractInterpreter::zerolocals_synchronized)) + return false; // interpreter does not run this method! + + // otherwise, we can be sure this bytecode has never been executed + return true; +} + + +#ifndef PRODUCT +void AbstractInterpreter::print_method_kind(MethodKind kind) { + switch (kind) { + case zerolocals : tty->print("zerolocals" ); break; + case zerolocals_synchronized: tty->print("zerolocals_synchronized"); break; + case native : tty->print("native" ); break; + case native_synchronized : tty->print("native_synchronized" ); break; + case empty : tty->print("empty" ); break; + case accessor : tty->print("accessor" ); break; + case abstract : tty->print("abstract" ); break; + case java_lang_math_sin : tty->print("java_lang_math_sin" ); break; + case java_lang_math_cos : tty->print("java_lang_math_cos" ); break; + case java_lang_math_tan : tty->print("java_lang_math_tan" ); break; + case java_lang_math_abs : tty->print("java_lang_math_abs" ); break; + case java_lang_math_sqrt : tty->print("java_lang_math_sqrt" ); break; + case java_lang_math_log : tty->print("java_lang_math_log" ); break; + case java_lang_math_log10 : tty->print("java_lang_math_log10" ); break; + default : ShouldNotReachHere(); + } +} +#endif // PRODUCT + +static BasicType constant_pool_type(methodOop method, int index) { + constantTag tag = method->constants()->tag_at(index); + if (tag.is_int ()) return T_INT; + else if (tag.is_float ()) return T_FLOAT; + else if (tag.is_long ()) return T_LONG; + else if (tag.is_double ()) return T_DOUBLE; + else if (tag.is_string ()) return T_OBJECT; + else if (tag.is_unresolved_string()) return T_OBJECT; + else if (tag.is_klass ()) return T_OBJECT; + else if (tag.is_unresolved_klass ()) return T_OBJECT; + ShouldNotReachHere(); + return T_ILLEGAL; +} + + +//------------------------------------------------------------------------------------------------------------------------ +// Deoptimization support + +// If deoptimization happens, this method returns the point where to continue in +// interpreter. For calls (invokexxxx, newxxxx) the continuation is at next +// bci and the top of stack is in eax/edx/FPU tos. +// For putfield/getfield, put/getstatic, the continuation is at the same +// bci and the TOS is on stack. + +// Note: deopt_entry(type, 0) means reexecute bytecode +// deopt_entry(type, length) means continue at next bytecode + +address AbstractInterpreter::continuation_for(methodOop method, address bcp, int callee_parameters, bool is_top_frame, bool& use_next_mdp) { + assert(method->contains(bcp), "just checkin'"); + Bytecodes::Code code = Bytecodes::java_code_at(bcp); + int bci = method->bci_from(bcp); + int length = -1; // initial value for debugging + // compute continuation length + length = Bytecodes::length_at(bcp); + // compute result type + BasicType type = T_ILLEGAL; + // when continuing after a compiler safepoint, re-execute the bytecode + // (an invoke is continued after the safepoint) + use_next_mdp = true; + switch (code) { + case Bytecodes::_lookupswitch: + case Bytecodes::_tableswitch: + case Bytecodes::_fast_binaryswitch: + case Bytecodes::_fast_linearswitch: + // recompute condtional expression folded into _if + case Bytecodes::_lcmp : + case Bytecodes::_fcmpl : + case Bytecodes::_fcmpg : + case Bytecodes::_dcmpl : + case Bytecodes::_dcmpg : + case Bytecodes::_ifnull : + case Bytecodes::_ifnonnull : + case Bytecodes::_goto : + case Bytecodes::_goto_w : + case Bytecodes::_ifeq : + case Bytecodes::_ifne : + case Bytecodes::_iflt : + case Bytecodes::_ifge : + case Bytecodes::_ifgt : + case Bytecodes::_ifle : + case Bytecodes::_if_icmpeq : + case Bytecodes::_if_icmpne : + case Bytecodes::_if_icmplt : + case Bytecodes::_if_icmpge : + case Bytecodes::_if_icmpgt : + case Bytecodes::_if_icmple : + case Bytecodes::_if_acmpeq : + case Bytecodes::_if_acmpne : + // special cases + case Bytecodes::_getfield : + case Bytecodes::_putfield : + case Bytecodes::_getstatic : + case Bytecodes::_putstatic : + case Bytecodes::_aastore : + // reexecute the operation and TOS value is on stack + assert(is_top_frame, "must be top frame"); + use_next_mdp = false; + return Interpreter::deopt_entry(vtos, 0); + break; + +#ifdef COMPILER1 + case Bytecodes::_athrow : + assert(is_top_frame, "must be top frame"); + use_next_mdp = false; + return Interpreter::rethrow_exception_entry(); + break; +#endif /* COMPILER1 */ + + case Bytecodes::_invokevirtual : + case Bytecodes::_invokespecial : + case Bytecodes::_invokestatic : + case Bytecodes::_invokeinterface: { + Thread *thread = Thread::current(); + ResourceMark rm(thread); + methodHandle mh(thread, method); + type = Bytecode_invoke_at(mh, bci)->result_type(thread); + // since the cache entry might not be initialized: + // (NOT needed for the old calling convension) + if (!is_top_frame) { + int index = Bytes::get_native_u2(bcp+1); + method->constants()->cache()->entry_at(index)->set_parameter_size(callee_parameters); + } + break; + } + + case Bytecodes::_ldc : + type = constant_pool_type( method, *(bcp+1) ); + break; + + case Bytecodes::_ldc_w : // fall through + case Bytecodes::_ldc2_w: + type = constant_pool_type( method, Bytes::get_Java_u2(bcp+1) ); + break; + + default: + type = Bytecodes::result_type(code); + break; + } + + // return entry point for computed continuation state & bytecode length + return + is_top_frame + ? Interpreter::deopt_entry (as_TosState(type), length) + : Interpreter::return_entry(as_TosState(type), length); +} + +void AbstractInterpreterGenerator::bang_stack_shadow_pages(bool native_call) { + // Quick & dirty stack overflow checking: bang the stack & handle trap. + // Note that we do the banging after the frame is setup, since the exception + // handling code expects to find a valid interpreter frame on the stack. + // Doing the banging earlier fails if the caller frame is not an interpreter + // frame. + // (Also, the exception throwing code expects to unlock any synchronized + // method receiever, so do the banging after locking the receiver.) + + // Bang each page in the shadow zone. We can't assume it's been done for + // an interpreter frame with greater than a page of locals, so each page + // needs to be checked. Only true for non-native. + if (UseStackBanging) { + const int start_page = native_call ? StackShadowPages : 1; + const int page_size = os::vm_page_size(); + for (int pages = start_page; pages <= StackShadowPages ; pages++) { + __ bang_stack_with_offset(pages*page_size); + } + } +} diff --git a/hotspot/src/share/vm/interpreter/interpreter.hpp b/hotspot/src/share/vm/interpreter/interpreter.hpp new file mode 100644 index 00000000000..eb316c5bce7 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/interpreter.hpp @@ -0,0 +1,134 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file contains the platform-independant parts +// of the interpreter and the interpreter generator. + +//------------------------------------------------------------------------------------------------------------------------ +// An InterpreterCodelet is a piece of interpreter code. All +// interpreter code is generated into little codelets which +// contain extra information for debugging and printing purposes. + +class InterpreterCodelet: public Stub { + friend class VMStructs; + private: + int _size; // the size in bytes + const char* _description; // a description of the codelet, for debugging & printing + Bytecodes::Code _bytecode; // associated bytecode if any + + public: + // Initialization/finalization + void initialize(int size) { _size = size; } + void finalize() { ShouldNotCallThis(); } + + // General info/converters + int size() const { return _size; } + static int code_size_to_size(int code_size) { return round_to(sizeof(InterpreterCodelet), CodeEntryAlignment) + code_size; } + + // Code info + address code_begin() const { return (address)this + round_to(sizeof(InterpreterCodelet), CodeEntryAlignment); } + address code_end() const { return (address)this + size(); } + + // Debugging + void verify(); + void print(); + + // Interpreter-specific initialization + void initialize(const char* description, Bytecodes::Code bytecode); + + // Interpreter-specific attributes + int code_size() const { return code_end() - code_begin(); } + const char* description() const { return _description; } + Bytecodes::Code bytecode() const { return _bytecode; } +}; + +// Define a prototype interface +DEF_STUB_INTERFACE(InterpreterCodelet); + + +//------------------------------------------------------------------------------------------------------------------------ +// A CodeletMark serves as an automatic creator/initializer for Codelets +// (As a subclass of ResourceMark it automatically GC's the allocated +// code buffer and assemblers). + +class CodeletMark: ResourceMark { + private: + InterpreterCodelet* _clet; + InterpreterMacroAssembler** _masm; + CodeBuffer _cb; + + int codelet_size() { + // Request the whole code buffer (minus a little for alignment). + // The commit call below trims it back for each codelet. + int codelet_size = AbstractInterpreter::code()->available_space() - 2*K; + + // Guarantee there's a little bit of code space left. + guarantee (codelet_size > 0 && (size_t)codelet_size > 2*K, + "not enough space for interpreter generation"); + + return codelet_size; + } + + public: + CodeletMark( + InterpreterMacroAssembler*& masm, + const char* description, + Bytecodes::Code bytecode = Bytecodes::_illegal): + _clet((InterpreterCodelet*)AbstractInterpreter::code()->request(codelet_size())), + _cb(_clet->code_begin(), _clet->code_size()) + + { // request all space (add some slack for Codelet data) + assert (_clet != NULL, "we checked not enough space already"); + + // initialize Codelet attributes + _clet->initialize(description, bytecode); + // create assembler for code generation + masm = new InterpreterMacroAssembler(&_cb); + _masm = &masm; + } + + ~CodeletMark() { + // align so printing shows nop's instead of random code at the end (Codelets are aligned) + (*_masm)->align(wordSize); + // make sure all code is in code buffer + (*_masm)->flush(); + + + // commit Codelet + AbstractInterpreter::code()->commit((*_masm)->code()->pure_code_size()); + // make sure nobody can use _masm outside a CodeletMark lifespan + *_masm = NULL; + } +}; + +// Wrapper classes to produce Interpreter/InterpreterGenerator from either +// the c++ interpreter or the template interpreter. + +class Interpreter: public CC_INTERP_ONLY(CppInterpreter) NOT_CC_INTERP(TemplateInterpreter) { + + public: + // Debugging/printing + static InterpreterCodelet* codelet_containing(address pc) { return (InterpreterCodelet*)_code->stub_containing(pc); } +#include "incls/_interpreter_pd.hpp.incl" +}; diff --git a/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp b/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp new file mode 100644 index 00000000000..791b4777ae3 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp @@ -0,0 +1,38 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file contains the platform-independant parts +// of the interpreter generator. + + +class InterpreterGenerator: public CC_INTERP_ONLY(CppInterpreterGenerator) + NOT_CC_INTERP(TemplateInterpreterGenerator) { + +public: + +InterpreterGenerator(StubQueue* _code); + +#include "incls/_interpreterGenerator_pd.hpp.incl" + +}; diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp new file mode 100644 index 00000000000..afa87332321 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp @@ -0,0 +1,1151 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreterRuntime.cpp.incl" + +class UnlockFlagSaver { + private: + JavaThread* _thread; + bool _do_not_unlock; + public: + UnlockFlagSaver(JavaThread* t) { + _thread = t; + _do_not_unlock = t->do_not_unlock_if_synchronized(); + t->set_do_not_unlock_if_synchronized(false); + } + ~UnlockFlagSaver() { + _thread->set_do_not_unlock_if_synchronized(_do_not_unlock); + } +}; + +//------------------------------------------------------------------------------------------------------------------------ +// State accessors + +void InterpreterRuntime::set_bcp_and_mdp(address bcp, JavaThread *thread) { + last_frame(thread).interpreter_frame_set_bcp(bcp); + if (ProfileInterpreter) { + // ProfileTraps uses MDOs independently of ProfileInterpreter. + // That is why we must check both ProfileInterpreter and mdo != NULL. + methodDataOop mdo = last_frame(thread).interpreter_frame_method()->method_data(); + if (mdo != NULL) { + NEEDS_CLEANUP; + last_frame(thread).interpreter_frame_set_mdp(mdo->bci_to_dp(last_frame(thread).interpreter_frame_bci())); + } + } +} + +//------------------------------------------------------------------------------------------------------------------------ +// Constants + + +IRT_ENTRY(void, InterpreterRuntime::ldc(JavaThread* thread, bool wide)) + // access constant pool + constantPoolOop pool = method(thread)->constants(); + int index = wide ? two_byte_index(thread) : one_byte_index(thread); + constantTag tag = pool->tag_at(index); + + if (tag.is_unresolved_klass() || tag.is_klass()) { + klassOop klass = pool->klass_at(index, CHECK); + oop java_class = klass->klass_part()->java_mirror(); + thread->set_vm_result(java_class); + } else { +#ifdef ASSERT + // If we entered this runtime routine, we believed the tag contained + // an unresolved string, an unresolved class or a resolved class. + // However, another thread could have resolved the unresolved string + // or class by the time we go there. + assert(tag.is_unresolved_string()|| tag.is_string(), "expected string"); +#endif + oop s_oop = pool->string_at(index, CHECK); + thread->set_vm_result(s_oop); + } +IRT_END + + +//------------------------------------------------------------------------------------------------------------------------ +// Allocation + +IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, constantPoolOopDesc* pool, int index)) + klassOop k_oop = pool->klass_at(index, CHECK); + instanceKlassHandle klass (THREAD, k_oop); + + // Make sure we are not instantiating an abstract klass + klass->check_valid_for_instantiation(true, CHECK); + + // Make sure klass is initialized + klass->initialize(CHECK); + + // At this point the class may not be fully initialized + // because of recursive initialization. If it is fully + // initialized & has_finalized is not set, we rewrite + // it into its fast version (Note: no locking is needed + // here since this is an atomic byte write and can be + // done more than once). + // + // Note: In case of classes with has_finalized we don't + // rewrite since that saves us an extra check in + // the fast version which then would call the + // slow version anyway (and do a call back into + // Java). + // If we have a breakpoint, then we don't rewrite + // because the _breakpoint bytecode would be lost. + oop obj = klass->allocate_instance(CHECK); + thread->set_vm_result(obj); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::newarray(JavaThread* thread, BasicType type, jint size)) + oop obj = oopFactory::new_typeArray(type, size, CHECK); + thread->set_vm_result(obj); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::anewarray(JavaThread* thread, constantPoolOopDesc* pool, int index, jint size)) + // Note: no oopHandle for pool & klass needed since they are not used + // anymore after new_objArray() and no GC can happen before. + // (This may have to change if this code changes!) + klassOop klass = pool->klass_at(index, CHECK); + objArrayOop obj = oopFactory::new_objArray(klass, size, CHECK); + thread->set_vm_result(obj); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::multianewarray(JavaThread* thread, jint* first_size_address)) + // We may want to pass in more arguments - could make this slightly faster + constantPoolOop constants = method(thread)->constants(); + int i = two_byte_index(thread); + klassOop klass = constants->klass_at(i, CHECK); + int nof_dims = number_of_dimensions(thread); + assert(oop(klass)->is_klass(), "not a class"); + assert(nof_dims >= 1, "multianewarray rank must be nonzero"); + + // We must create an array of jints to pass to multi_allocate. + ResourceMark rm(thread); + const int small_dims = 10; + jint dim_array[small_dims]; + jint *dims = &dim_array[0]; + if (nof_dims > small_dims) { + dims = (jint*) NEW_RESOURCE_ARRAY(jint, nof_dims); + } + for (int index = 0; index < nof_dims; index++) { + // offset from first_size_address is addressed as local[index] + int n = Interpreter::local_offset_in_bytes(index)/jintSize; + dims[index] = first_size_address[n]; + } + oop obj = arrayKlass::cast(klass)->multi_allocate(nof_dims, dims, CHECK); + thread->set_vm_result(obj); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::register_finalizer(JavaThread* thread, oopDesc* obj)) + assert(obj->is_oop(), "must be a valid oop"); + assert(obj->klass()->klass_part()->has_finalizer(), "shouldn't be here otherwise"); + instanceKlass::register_finalizer(instanceOop(obj), CHECK); +IRT_END + + +// Quicken instance-of and check-cast bytecodes +IRT_ENTRY(void, InterpreterRuntime::quicken_io_cc(JavaThread* thread)) + // Force resolving; quicken the bytecode + int which = two_byte_index(thread); + constantPoolOop cpool = method(thread)->constants(); + // We'd expect to assert that we're only here to quicken bytecodes, but in a multithreaded + // program we might have seen an unquick'd bytecode in the interpreter but have another + // thread quicken the bytecode before we get here. + // assert( cpool->tag_at(which).is_unresolved_klass(), "should only come here to quicken bytecodes" ); + klassOop klass = cpool->klass_at(which, CHECK); + thread->set_vm_result(klass); +IRT_END + + +//------------------------------------------------------------------------------------------------------------------------ +// Exceptions + +// Assume the compiler is (or will be) interested in this event. +// If necessary, create an MDO to hold the information, and record it. +void InterpreterRuntime::note_trap(JavaThread* thread, int reason, TRAPS) { + assert(ProfileTraps, "call me only if profiling"); + methodHandle trap_method(thread, method(thread)); + if (trap_method.not_null()) { + methodDataHandle trap_mdo(thread, trap_method->method_data()); + if (trap_mdo.is_null()) { + methodOopDesc::build_interpreter_method_data(trap_method, THREAD); + if (HAS_PENDING_EXCEPTION) { + assert((PENDING_EXCEPTION->is_a(SystemDictionary::OutOfMemoryError_klass())), "we expect only an OOM error here"); + CLEAR_PENDING_EXCEPTION; + } + trap_mdo = methodDataHandle(thread, trap_method->method_data()); + // and fall through... + } + if (trap_mdo.not_null()) { + // Update per-method count of trap events. The interpreter + // is updating the MDO to simulate the effect of compiler traps. + int trap_bci = trap_method->bci_from(bcp(thread)); + Deoptimization::update_method_data_from_interpreter(trap_mdo, trap_bci, reason); + } + } +} + +static Handle get_preinitialized_exception(klassOop k, TRAPS) { + // get klass + instanceKlass* klass = instanceKlass::cast(k); + assert(klass->is_initialized(), + "this klass should have been initialized during VM initialization"); + // create instance - do not call constructor since we may have no + // (java) stack space left (should assert constructor is empty) + Handle exception; + oop exception_oop = klass->allocate_instance(CHECK_(exception)); + exception = Handle(THREAD, exception_oop); + if (StackTraceInThrowable) { + java_lang_Throwable::fill_in_stack_trace(exception); + } + return exception; +} + +// Special handling for stack overflow: since we don't have any (java) stack +// space left we use the pre-allocated & pre-initialized StackOverflowError +// klass to create an stack overflow error instance. We do not call its +// constructor for the same reason (it is empty, anyway). +IRT_ENTRY(void, InterpreterRuntime::throw_StackOverflowError(JavaThread* thread)) + Handle exception = get_preinitialized_exception( + SystemDictionary::StackOverflowError_klass(), + CHECK); + THROW_HANDLE(exception); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::create_exception(JavaThread* thread, char* name, char* message)) + // lookup exception klass + symbolHandle s = oopFactory::new_symbol_handle(name, CHECK); + if (ProfileTraps) { + if (s == vmSymbols::java_lang_ArithmeticException()) { + note_trap(thread, Deoptimization::Reason_div0_check, CHECK); + } else if (s == vmSymbols::java_lang_NullPointerException()) { + note_trap(thread, Deoptimization::Reason_null_check, CHECK); + } + } + // create exception + Handle exception = Exceptions::new_exception(thread, s(), message); + thread->set_vm_result(exception()); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::create_klass_exception(JavaThread* thread, char* name, oopDesc* obj)) + ResourceMark rm(thread); + const char* klass_name = Klass::cast(obj->klass())->external_name(); + // lookup exception klass + symbolHandle s = oopFactory::new_symbol_handle(name, CHECK); + if (ProfileTraps) { + note_trap(thread, Deoptimization::Reason_class_check, CHECK); + } + // create exception, with klass name as detail message + Handle exception = Exceptions::new_exception(thread, s(), klass_name); + thread->set_vm_result(exception()); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException(JavaThread* thread, char* name, jint index)) + char message[jintAsStringSize]; + // lookup exception klass + symbolHandle s = oopFactory::new_symbol_handle(name, CHECK); + if (ProfileTraps) { + note_trap(thread, Deoptimization::Reason_range_check, CHECK); + } + // create exception + sprintf(message, "%d", index); + THROW_MSG(s(), message); +IRT_END + +IRT_ENTRY(void, InterpreterRuntime::throw_ClassCastException( + JavaThread* thread, oopDesc* obj)) + + ResourceMark rm(thread); + char* message = SharedRuntime::generate_class_cast_message( + thread, Klass::cast(obj->klass())->external_name()); + + if (ProfileTraps) { + note_trap(thread, Deoptimization::Reason_class_check, CHECK); + } + + // create exception + THROW_MSG(vmSymbols::java_lang_ClassCastException(), message); +IRT_END + + +// exception_handler_for_exception(...) returns the continuation address, +// the exception oop (via TLS) and sets the bci/bcp for the continuation. +// The exception oop is returned to make sure it is preserved over GC (it +// is only on the stack if the exception was thrown explicitly via athrow). +// During this operation, the expression stack contains the values for the +// bci where the exception happened. If the exception was propagated back +// from a call, the expression stack contains the values for the bci at the +// invoke w/o arguments (i.e., as if one were inside the call). +IRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThread* thread, oopDesc* exception)) + + Handle h_exception(thread, exception); + methodHandle h_method (thread, method(thread)); + constantPoolHandle h_constants(thread, h_method->constants()); + typeArrayHandle h_extable (thread, h_method->exception_table()); + bool should_repeat; + int handler_bci; + int current_bci = bcp(thread) - h_method->code_base(); + + // Need to do this check first since when _do_not_unlock_if_synchronized + // is set, we don't want to trigger any classloading which may make calls + // into java, or surprisingly find a matching exception handler for bci 0 + // since at this moment the method hasn't been "officially" entered yet. + if (thread->do_not_unlock_if_synchronized()) { + ResourceMark rm; + assert(current_bci == 0, "bci isn't zero for do_not_unlock_if_synchronized"); + thread->set_vm_result(exception); +#ifdef CC_INTERP + return (address) -1; +#else + return Interpreter::remove_activation_entry(); +#endif + } + + do { + should_repeat = false; + + // assertions +#ifdef ASSERT + assert(h_exception.not_null(), "NULL exceptions should be handled by athrow"); + assert(h_exception->is_oop(), "just checking"); + // Check that exception is a subclass of Throwable, otherwise we have a VerifyError + if (!(h_exception->is_a(SystemDictionary::throwable_klass()))) { + if (ExitVMOnVerifyError) vm_exit(-1); + ShouldNotReachHere(); + } +#endif + + // tracing + if (TraceExceptions) { + ttyLocker ttyl; + ResourceMark rm(thread); + tty->print_cr("Exception <%s> (" INTPTR_FORMAT ")", h_exception->print_value_string(), (address)h_exception()); + tty->print_cr(" thrown in interpreter method <%s>", h_method->print_value_string()); + tty->print_cr(" at bci %d for thread " INTPTR_FORMAT, current_bci, thread); + } +// Don't go paging in something which won't be used. +// else if (h_extable->length() == 0) { +// // disabled for now - interpreter is not using shortcut yet +// // (shortcut is not to call runtime if we have no exception handlers) +// // warning("performance bug: should not call runtime if method has no exception handlers"); +// } + // for AbortVMOnException flag + NOT_PRODUCT(Exceptions::debug_check_abort(h_exception)); + + // exception handler lookup + KlassHandle h_klass(THREAD, h_exception->klass()); + handler_bci = h_method->fast_exception_handler_bci_for(h_klass, current_bci, THREAD); + if (HAS_PENDING_EXCEPTION) { + // We threw an exception while trying to find the exception handler. + // Transfer the new exception to the exception handle which will + // be set into thread local storage, and do another lookup for an + // exception handler for this exception, this time starting at the + // BCI of the exception handler which caused the exception to be + // thrown (bug 4307310). + h_exception = Handle(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + if (handler_bci >= 0) { + current_bci = handler_bci; + should_repeat = true; + } + } + } while (should_repeat == true); + + // notify JVMTI of an exception throw; JVMTI will detect if this is a first + // time throw or a stack unwinding throw and accordingly notify the debugger + if (JvmtiExport::can_post_exceptions()) { + JvmtiExport::post_exception_throw(thread, h_method(), bcp(thread), h_exception()); + } + +#ifdef CC_INTERP + address continuation = (address)(intptr_t) handler_bci; +#else + address continuation = NULL; +#endif + address handler_pc = NULL; + if (handler_bci < 0 || !thread->reguard_stack((address) &continuation)) { + // Forward exception to callee (leaving bci/bcp untouched) because (a) no + // handler in this method, or (b) after a stack overflow there is not yet + // enough stack space available to reprotect the stack. +#ifndef CC_INTERP + continuation = Interpreter::remove_activation_entry(); +#endif + // Count this for compilation purposes + h_method->interpreter_throwout_increment(); + } else { + // handler in this method => change bci/bcp to handler bci/bcp and continue there + handler_pc = h_method->code_base() + handler_bci; +#ifndef CC_INTERP + set_bcp_and_mdp(handler_pc, thread); + continuation = Interpreter::dispatch_table(vtos)[*handler_pc]; +#endif + } + // notify debugger of an exception catch + // (this is good for exceptions caught in native methods as well) + if (JvmtiExport::can_post_exceptions()) { + JvmtiExport::notice_unwind_due_to_exception(thread, h_method(), handler_pc, h_exception(), (handler_pc != NULL)); + } + + thread->set_vm_result(h_exception()); + return continuation; +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::throw_pending_exception(JavaThread* thread)) + assert(thread->has_pending_exception(), "must only ne called if there's an exception pending"); + // nothing to do - eventually we should remove this code entirely (see comments @ call sites) +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::throw_AbstractMethodError(JavaThread* thread)) + THROW(vmSymbols::java_lang_AbstractMethodError()); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::throw_IncompatibleClassChangeError(JavaThread* thread)) + THROW(vmSymbols::java_lang_IncompatibleClassChangeError()); +IRT_END + + +//------------------------------------------------------------------------------------------------------------------------ +// Fields +// + +IRT_ENTRY(void, InterpreterRuntime::resolve_get_put(JavaThread* thread, Bytecodes::Code bytecode)) + // resolve field + FieldAccessInfo info; + constantPoolHandle pool(thread, method(thread)->constants()); + bool is_static = (bytecode == Bytecodes::_getstatic || bytecode == Bytecodes::_putstatic); + + { + JvmtiHideSingleStepping jhss(thread); + LinkResolver::resolve_field(info, pool, two_byte_index(thread), + bytecode, false, CHECK); + } // end JvmtiHideSingleStepping + + // check if link resolution caused cpCache to be updated + if (already_resolved(thread)) return; + + // compute auxiliary field attributes + TosState state = as_TosState(info.field_type()); + + // We need to delay resolving put instructions on final fields + // until we actually invoke one. This is required so we throw + // exceptions at the correct place. If we do not resolve completely + // in the current pass, leaving the put_code set to zero will + // cause the next put instruction to reresolve. + bool is_put = (bytecode == Bytecodes::_putfield || + bytecode == Bytecodes::_putstatic); + Bytecodes::Code put_code = (Bytecodes::Code)0; + + // We also need to delay resolving getstatic instructions until the + // class is intitialized. This is required so that access to the static + // field will call the initialization function every time until the class + // is completely initialized ala. in 2.17.5 in JVM Specification. + instanceKlass *klass = instanceKlass::cast(info.klass()->as_klassOop()); + bool uninitialized_static = ((bytecode == Bytecodes::_getstatic || bytecode == Bytecodes::_putstatic) && + !klass->is_initialized()); + Bytecodes::Code get_code = (Bytecodes::Code)0; + + + if (!uninitialized_static) { + get_code = ((is_static) ? Bytecodes::_getstatic : Bytecodes::_getfield); + if (is_put || !info.access_flags().is_final()) { + put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield); + } + } + + cache_entry(thread)->set_field( + get_code, + put_code, + info.klass(), + info.field_index(), + info.field_offset(), + state, + info.access_flags().is_final(), + info.access_flags().is_volatile() + ); +IRT_END + + +//------------------------------------------------------------------------------------------------------------------------ +// Synchronization +// +// The interpreter's synchronization code is factored out so that it can +// be shared by method invocation and synchronized blocks. +//%note synchronization_3 + +static void trace_locking(Handle& h_locking_obj, bool is_locking) { + ObjectSynchronizer::trace_locking(h_locking_obj, false, true, is_locking); +} + + +//%note monitor_1 +IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem)) +#ifdef ASSERT + thread->last_frame().interpreter_frame_verify_monitor(elem); +#endif + if (PrintBiasedLockingStatistics) { + Atomic::inc(BiasedLocking::slow_path_entry_count_addr()); + } + Handle h_obj(thread, elem->obj()); + assert(Universe::heap()->is_in_reserved_or_null(h_obj()), + "must be NULL or an object"); + if (UseBiasedLocking) { + // Retry fast entry if bias is revoked to avoid unnecessary inflation + ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK); + } else { + ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK); + } + assert(Universe::heap()->is_in_reserved_or_null(elem->obj()), + "must be NULL or an object"); +#ifdef ASSERT + thread->last_frame().interpreter_frame_verify_monitor(elem); +#endif +IRT_END + + +//%note monitor_1 +IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorexit(JavaThread* thread, BasicObjectLock* elem)) +#ifdef ASSERT + thread->last_frame().interpreter_frame_verify_monitor(elem); +#endif + Handle h_obj(thread, elem->obj()); + assert(Universe::heap()->is_in_reserved_or_null(h_obj()), + "must be NULL or an object"); + if (elem == NULL || h_obj()->is_unlocked()) { + THROW(vmSymbols::java_lang_IllegalMonitorStateException()); + } + ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread); + // Free entry. This must be done here, since a pending exception might be installed on + // exit. If it is not cleared, the exception handling code will try to unlock the monitor again. + elem->set_obj(NULL); +#ifdef ASSERT + thread->last_frame().interpreter_frame_verify_monitor(elem); +#endif +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::throw_illegal_monitor_state_exception(JavaThread* thread)) + THROW(vmSymbols::java_lang_IllegalMonitorStateException()); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::new_illegal_monitor_state_exception(JavaThread* thread)) + // Returns an illegal exception to install into the current thread. The + // pending_exception flag is cleared so normal exception handling does not + // trigger. Any current installed exception will be overwritten. This + // method will be called during an exception unwind. + + assert(!HAS_PENDING_EXCEPTION, "no pending exception"); + Handle exception(thread, thread->vm_result()); + assert(exception() != NULL, "vm result should be set"); + thread->set_vm_result(NULL); // clear vm result before continuing (may cause memory leaks and assert failures) + if (!exception->is_a(SystemDictionary::threaddeath_klass())) { + exception = get_preinitialized_exception( + SystemDictionary::IllegalMonitorStateException_klass(), + CATCH); + } + thread->set_vm_result(exception()); +IRT_END + + +//------------------------------------------------------------------------------------------------------------------------ +// Invokes + +IRT_ENTRY(Bytecodes::Code, InterpreterRuntime::get_original_bytecode_at(JavaThread* thread, methodOopDesc* method, address bcp)) + return method->orig_bytecode_at(method->bci_from(bcp)); +IRT_END + +IRT_ENTRY(void, InterpreterRuntime::set_original_bytecode_at(JavaThread* thread, methodOopDesc* method, address bcp, Bytecodes::Code new_code)) + method->set_orig_bytecode_at(method->bci_from(bcp), new_code); +IRT_END + +IRT_ENTRY(void, InterpreterRuntime::_breakpoint(JavaThread* thread, methodOopDesc* method, address bcp)) + JvmtiExport::post_raw_breakpoint(thread, method, bcp); +IRT_END + +IRT_ENTRY(void, InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code bytecode)) + // extract receiver from the outgoing argument list if necessary + Handle receiver(thread, NULL); + if (bytecode == Bytecodes::_invokevirtual || bytecode == Bytecodes::_invokeinterface) { + ResourceMark rm(thread); + methodHandle m (thread, method(thread)); + int bci = m->bci_from(bcp(thread)); + Bytecode_invoke* call = Bytecode_invoke_at(m, bci); + symbolHandle signature (thread, call->signature()); + receiver = Handle(thread, + thread->last_frame().interpreter_callee_receiver(signature)); + assert(Universe::heap()->is_in_reserved_or_null(receiver()), + "sanity check"); + assert(receiver.is_null() || + Universe::heap()->is_in_reserved(receiver->klass()), + "sanity check"); + } + + // resolve method + CallInfo info; + constantPoolHandle pool(thread, method(thread)->constants()); + + { + JvmtiHideSingleStepping jhss(thread); + LinkResolver::resolve_invoke(info, receiver, pool, + two_byte_index(thread), bytecode, CHECK); + if (JvmtiExport::can_hotswap_or_post_breakpoint()) { + int retry_count = 0; + while (info.resolved_method()->is_old()) { + // It is very unlikely that method is redefined more than 100 times + // in the middle of resolve. If it is looping here more than 100 times + // means then there could be a bug here. + guarantee((retry_count++ < 100), + "Could not resolve to latest version of redefined method"); + // method is redefined in the middle of resolve so re-try. + LinkResolver::resolve_invoke(info, receiver, pool, + two_byte_index(thread), bytecode, CHECK); + } + } + } // end JvmtiHideSingleStepping + + // check if link resolution caused cpCache to be updated + if (already_resolved(thread)) return; + + if (bytecode == Bytecodes::_invokeinterface) { + + if (TraceItables && Verbose) { + ResourceMark rm(thread); + tty->print_cr("Resolving: klass: %s to method: %s", info.resolved_klass()->name()->as_C_string(), info.resolved_method()->name()->as_C_string()); + } + if (info.resolved_method()->method_holder() == + SystemDictionary::object_klass()) { + // NOTE: THIS IS A FIX FOR A CORNER CASE in the JVM spec + // (see also cpCacheOop.cpp for details) + methodHandle rm = info.resolved_method(); + assert(rm->is_final() || info.has_vtable_index(), + "should have been set already"); + cache_entry(thread)->set_method(bytecode, rm, info.vtable_index()); + } else { + // Setup itable entry + int index = klassItable::compute_itable_index(info.resolved_method()()); + cache_entry(thread)->set_interface_call(info.resolved_method(), index); + } + } else { + cache_entry(thread)->set_method( + bytecode, + info.resolved_method(), + info.vtable_index()); + } +IRT_END + + +//------------------------------------------------------------------------------------------------------------------------ +// Miscellaneous + + +#ifndef PRODUCT +static void trace_frequency_counter_overflow(methodHandle m, int branch_bci, int bci, address branch_bcp) { + if (TraceInvocationCounterOverflow) { + InvocationCounter* ic = m->invocation_counter(); + InvocationCounter* bc = m->backedge_counter(); + ResourceMark rm; + const char* msg = + branch_bcp == NULL + ? "comp-policy cntr ovfl @ %d in entry of " + : "comp-policy cntr ovfl @ %d in loop of "; + tty->print(msg, bci); + m->print_value(); + tty->cr(); + ic->print(); + bc->print(); + if (ProfileInterpreter) { + if (branch_bcp != NULL) { + methodDataOop mdo = m->method_data(); + if (mdo != NULL) { + int count = mdo->bci_to_data(branch_bci)->as_JumpData()->taken(); + tty->print_cr("back branch count = %d", count); + } + } + } + } +} + +static void trace_osr_request(methodHandle method, nmethod* osr, int bci) { + if (TraceOnStackReplacement) { + ResourceMark rm; + tty->print(osr != NULL ? "Reused OSR entry for " : "Requesting OSR entry for "); + method->print_short_name(tty); + tty->print_cr(" at bci %d", bci); + } +} +#endif // !PRODUCT + +IRT_ENTRY(nmethod*, + InterpreterRuntime::frequency_counter_overflow(JavaThread* thread, address branch_bcp)) + // use UnlockFlagSaver to clear and restore the _do_not_unlock_if_synchronized + // flag, in case this method triggers classloading which will call into Java. + UnlockFlagSaver fs(thread); + + frame fr = thread->last_frame(); + assert(fr.is_interpreted_frame(), "must come from interpreter"); + methodHandle method(thread, fr.interpreter_frame_method()); + const int branch_bci = branch_bcp != NULL ? method->bci_from(branch_bcp) : 0; + const int bci = method->bci_from(fr.interpreter_frame_bcp()); + NOT_PRODUCT(trace_frequency_counter_overflow(method, branch_bci, bci, branch_bcp);) + + if (JvmtiExport::can_post_interpreter_events()) { + if (thread->is_interp_only_mode()) { + // If certain JVMTI events (e.g. frame pop event) are requested then the + // thread is forced to remain in interpreted code. This is + // implemented partly by a check in the run_compiled_code + // section of the interpreter whether we should skip running + // compiled code, and partly by skipping OSR compiles for + // interpreted-only threads. + if (branch_bcp != NULL) { + CompilationPolicy::policy()->reset_counter_for_back_branch_event(method); + return NULL; + } + } + } + + if (branch_bcp == NULL) { + // when code cache is full, compilation gets switched off, UseCompiler + // is set to false + if (!method->has_compiled_code() && UseCompiler) { + CompilationPolicy::policy()->method_invocation_event(method, CHECK_NULL); + } else { + // Force counter overflow on method entry, even if no compilation + // happened. (The method_invocation_event call does this also.) + CompilationPolicy::policy()->reset_counter_for_invocation_event(method); + } + // compilation at an invocation overflow no longer goes and retries test for + // compiled method. We always run the loser of the race as interpreted. + // so return NULL + return NULL; + } else { + // counter overflow in a loop => try to do on-stack-replacement + nmethod* osr_nm = method->lookup_osr_nmethod_for(bci); + NOT_PRODUCT(trace_osr_request(method, osr_nm, bci);) + // when code cache is full, we should not compile any more... + if (osr_nm == NULL && UseCompiler) { + const int branch_bci = method->bci_from(branch_bcp); + CompilationPolicy::policy()->method_back_branch_event(method, branch_bci, bci, CHECK_NULL); + osr_nm = method->lookup_osr_nmethod_for(bci); + } + if (osr_nm == NULL) { + CompilationPolicy::policy()->reset_counter_for_back_branch_event(method); + return NULL; + } else { + // We may need to do on-stack replacement which requires that no + // monitors in the activation are biased because their + // BasicObjectLocks will need to migrate during OSR. Force + // unbiasing of all monitors in the activation now (even though + // the OSR nmethod might be invalidated) because we don't have a + // safepoint opportunity later once the migration begins. + if (UseBiasedLocking) { + ResourceMark rm; + GrowableArray* objects_to_revoke = new GrowableArray(); + for( BasicObjectLock *kptr = fr.interpreter_frame_monitor_end(); + kptr < fr.interpreter_frame_monitor_begin(); + kptr = fr.next_monitor_in_interpreter_frame(kptr) ) { + if( kptr->obj() != NULL ) { + objects_to_revoke->append(Handle(THREAD, kptr->obj())); + } + } + BiasedLocking::revoke(objects_to_revoke); + } + + return osr_nm; + } + } +IRT_END + +IRT_LEAF(jint, InterpreterRuntime::bcp_to_di(methodOopDesc* method, address cur_bcp)) + assert(ProfileInterpreter, "must be profiling interpreter"); + int bci = method->bci_from(cur_bcp); + methodDataOop mdo = method->method_data(); + if (mdo == NULL) return 0; + return mdo->bci_to_di(bci); +IRT_END + +IRT_ENTRY(jint, InterpreterRuntime::profile_method(JavaThread* thread, address cur_bcp)) + // use UnlockFlagSaver to clear and restore the _do_not_unlock_if_synchronized + // flag, in case this method triggers classloading which will call into Java. + UnlockFlagSaver fs(thread); + + assert(ProfileInterpreter, "must be profiling interpreter"); + frame fr = thread->last_frame(); + assert(fr.is_interpreted_frame(), "must come from interpreter"); + methodHandle method(thread, fr.interpreter_frame_method()); + int bci = method->bci_from(cur_bcp); + methodOopDesc::build_interpreter_method_data(method, THREAD); + if (HAS_PENDING_EXCEPTION) { + assert((PENDING_EXCEPTION->is_a(SystemDictionary::OutOfMemoryError_klass())), "we expect only an OOM error here"); + CLEAR_PENDING_EXCEPTION; + // and fall through... + } + methodDataOop mdo = method->method_data(); + if (mdo == NULL) return 0; + return mdo->bci_to_di(bci); +IRT_END + + +#ifdef ASSERT +IRT_LEAF(void, InterpreterRuntime::verify_mdp(methodOopDesc* method, address bcp, address mdp)) + assert(ProfileInterpreter, "must be profiling interpreter"); + + methodDataOop mdo = method->method_data(); + assert(mdo != NULL, "must not be null"); + + int bci = method->bci_from(bcp); + + address mdp2 = mdo->bci_to_dp(bci); + if (mdp != mdp2) { + ResourceMark rm; + ResetNoHandleMark rnm; // In a LEAF entry. + HandleMark hm; + tty->print_cr("FAILED verify : actual mdp %p expected mdp %p @ bci %d", mdp, mdp2, bci); + int current_di = mdo->dp_to_di(mdp); + int expected_di = mdo->dp_to_di(mdp2); + tty->print_cr(" actual di %d expected di %d", current_di, expected_di); + int expected_approx_bci = mdo->data_at(expected_di)->bci(); + int approx_bci = -1; + if (current_di >= 0) { + approx_bci = mdo->data_at(current_di)->bci(); + } + tty->print_cr(" actual bci is %d expected bci %d", approx_bci, expected_approx_bci); + mdo->print_on(tty); + method->print_codes(); + } + assert(mdp == mdp2, "wrong mdp"); +IRT_END +#endif // ASSERT + +IRT_ENTRY(void, InterpreterRuntime::update_mdp_for_ret(JavaThread* thread, int return_bci)) + assert(ProfileInterpreter, "must be profiling interpreter"); + ResourceMark rm(thread); + HandleMark hm(thread); + frame fr = thread->last_frame(); + assert(fr.is_interpreted_frame(), "must come from interpreter"); + methodDataHandle h_mdo(thread, fr.interpreter_frame_method()->method_data()); + + // Grab a lock to ensure atomic access to setting the return bci and + // the displacement. This can block and GC, invalidating all naked oops. + MutexLocker ml(RetData_lock); + + // ProfileData is essentially a wrapper around a derived oop, so we + // need to take the lock before making any ProfileData structures. + ProfileData* data = h_mdo->data_at(h_mdo->dp_to_di(fr.interpreter_frame_mdp())); + RetData* rdata = data->as_RetData(); + address new_mdp = rdata->fixup_ret(return_bci, h_mdo); + fr.interpreter_frame_set_mdp(new_mdp); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::at_safepoint(JavaThread* thread)) + // We used to need an explict preserve_arguments here for invoke bytecodes. However, + // stack traversal automatically takes care of preserving arguments for invoke, so + // this is no longer needed. + + // IRT_END does an implicit safepoint check, hence we are guaranteed to block + // if this is called during a safepoint + + if (JvmtiExport::should_post_single_step()) { + // We are called during regular safepoints and when the VM is + // single stepping. If any thread is marked for single stepping, + // then we may have JVMTI work to do. + JvmtiExport::at_single_stepping_point(thread, method(thread), bcp(thread)); + } +IRT_END + +IRT_ENTRY(void, InterpreterRuntime::post_field_access(JavaThread *thread, oopDesc* obj, +ConstantPoolCacheEntry *cp_entry)) + + // check the access_flags for the field in the klass + instanceKlass* ik = instanceKlass::cast((klassOop)cp_entry->f1()); + typeArrayOop fields = ik->fields(); + int index = cp_entry->field_index(); + assert(index < fields->length(), "holders field index is out of range"); + // bail out if field accesses are not watched + if ((fields->ushort_at(index) & JVM_ACC_FIELD_ACCESS_WATCHED) == 0) return; + + switch(cp_entry->flag_state()) { + case btos: // fall through + case ctos: // fall through + case stos: // fall through + case itos: // fall through + case ftos: // fall through + case ltos: // fall through + case dtos: // fall through + case atos: break; + default: ShouldNotReachHere(); return; + } + bool is_static = (obj == NULL); + HandleMark hm(thread); + + Handle h_obj; + if (!is_static) { + // non-static field accessors have an object, but we need a handle + h_obj = Handle(thread, obj); + } + instanceKlassHandle h_cp_entry_f1(thread, (klassOop)cp_entry->f1()); + jfieldID fid = jfieldIDWorkaround::to_jfieldID(h_cp_entry_f1, cp_entry->f2(), is_static); + JvmtiExport::post_field_access(thread, method(thread), bcp(thread), h_cp_entry_f1, h_obj, fid); +IRT_END + +IRT_ENTRY(void, InterpreterRuntime::post_field_modification(JavaThread *thread, + oopDesc* obj, ConstantPoolCacheEntry *cp_entry, jvalue *value)) + + klassOop k = (klassOop)cp_entry->f1(); + + // check the access_flags for the field in the klass + instanceKlass* ik = instanceKlass::cast(k); + typeArrayOop fields = ik->fields(); + int index = cp_entry->field_index(); + assert(index < fields->length(), "holders field index is out of range"); + // bail out if field modifications are not watched + if ((fields->ushort_at(index) & JVM_ACC_FIELD_MODIFICATION_WATCHED) == 0) return; + + char sig_type = '\0'; + + switch(cp_entry->flag_state()) { + case btos: sig_type = 'Z'; break; + case ctos: sig_type = 'C'; break; + case stos: sig_type = 'S'; break; + case itos: sig_type = 'I'; break; + case ftos: sig_type = 'F'; break; + case atos: sig_type = 'L'; break; + case ltos: sig_type = 'J'; break; + case dtos: sig_type = 'D'; break; + default: ShouldNotReachHere(); return; + } + bool is_static = (obj == NULL); + + HandleMark hm(thread); + instanceKlassHandle h_klass(thread, k); + jfieldID fid = jfieldIDWorkaround::to_jfieldID(h_klass, cp_entry->f2(), is_static); + jvalue fvalue; +#ifdef _LP64 + fvalue = *value; +#else + // Long/double values are stored unaligned and also noncontiguously with + // tagged stacks. We can't just do a simple assignment even in the non- + // J/D cases because a C++ compiler is allowed to assume that a jvalue is + // 8-byte aligned, and interpreter stack slots are only 4-byte aligned. + // We assume that the two halves of longs/doubles are stored in interpreter + // stack slots in platform-endian order. + jlong_accessor u; + jint* newval = (jint*)value; + u.words[0] = newval[0]; + u.words[1] = newval[Interpreter::stackElementWords()]; // skip if tag + fvalue.j = u.long_value; +#endif // _LP64 + + Handle h_obj; + if (!is_static) { + // non-static field accessors have an object, but we need a handle + h_obj = Handle(thread, obj); + } + + JvmtiExport::post_raw_field_modification(thread, method(thread), bcp(thread), h_klass, h_obj, + fid, sig_type, &fvalue); +IRT_END + +IRT_ENTRY(void, InterpreterRuntime::post_method_entry(JavaThread *thread)) + JvmtiExport::post_method_entry(thread, InterpreterRuntime::method(thread), InterpreterRuntime::last_frame(thread)); +IRT_END + + +IRT_ENTRY(void, InterpreterRuntime::post_method_exit(JavaThread *thread)) + JvmtiExport::post_method_exit(thread, InterpreterRuntime::method(thread), InterpreterRuntime::last_frame(thread)); +IRT_END + +IRT_LEAF(int, InterpreterRuntime::interpreter_contains(address pc)) +{ + return (Interpreter::contains(pc) ? 1 : 0); +} +IRT_END + + +// Implementation of SignatureHandlerLibrary + +address SignatureHandlerLibrary::set_handler_blob() { + BufferBlob* handler_blob = BufferBlob::create("native signature handlers", blob_size); + if (handler_blob == NULL) { + return NULL; + } + address handler = handler_blob->instructions_begin(); + _handler_blob = handler_blob; + _handler = handler; + return handler; +} + +void SignatureHandlerLibrary::initialize() { + if (_fingerprints != NULL) { + return; + } + if (set_handler_blob() == NULL) { + vm_exit_out_of_memory(blob_size, "native signature handlers"); + } + + BufferBlob* bb = BufferBlob::create("Signature Handler Temp Buffer", + SignatureHandlerLibrary::buffer_size); + _buffer = bb->instructions_begin(); + + _fingerprints = new(ResourceObj::C_HEAP)GrowableArray(32, true); + _handlers = new(ResourceObj::C_HEAP)GrowableArray
(32, true); +} + +address SignatureHandlerLibrary::set_handler(CodeBuffer* buffer) { + address handler = _handler; + int code_size = buffer->pure_code_size(); + if (handler + code_size > _handler_blob->instructions_end()) { + // get a new handler blob + handler = set_handler_blob(); + } + if (handler != NULL) { + memcpy(handler, buffer->code_begin(), code_size); + pd_set_handler(handler); + ICache::invalidate_range(handler, code_size); + _handler = handler + code_size; + } + return handler; +} + +void SignatureHandlerLibrary::add(methodHandle method) { + if (method->signature_handler() == NULL) { + // use slow signature handler if we can't do better + int handler_index = -1; + // check if we can use customized (fast) signature handler + if (UseFastSignatureHandlers && method->size_of_parameters() <= Fingerprinter::max_size_of_parameters) { + // use customized signature handler + MutexLocker mu(SignatureHandlerLibrary_lock); + // make sure data structure is initialized + initialize(); + // lookup method signature's fingerprint + uint64_t fingerprint = Fingerprinter(method).fingerprint(); + handler_index = _fingerprints->find(fingerprint); + // create handler if necessary + if (handler_index < 0) { + ResourceMark rm; + ptrdiff_t align_offset = (address) + round_to((intptr_t)_buffer, CodeEntryAlignment) - (address)_buffer; + CodeBuffer buffer((address)(_buffer + align_offset), + SignatureHandlerLibrary::buffer_size - align_offset); + InterpreterRuntime::SignatureHandlerGenerator(method, &buffer).generate(fingerprint); + // copy into code heap + address handler = set_handler(&buffer); + if (handler == NULL) { + // use slow signature handler + } else { + // debugging suppport + if (PrintSignatureHandlers) { + tty->cr(); + tty->print_cr("argument handler #%d for: %s %s (fingerprint = " UINT64_FORMAT ", %d bytes generated)", + _handlers->length(), + (method->is_static() ? "static" : "receiver"), + method->name_and_sig_as_C_string(), + fingerprint, + buffer.code_size()); + Disassembler::decode(handler, handler + buffer.code_size()); +#ifndef PRODUCT + tty->print_cr(" --- associated result handler ---"); + address rh_begin = Interpreter::result_handler(method()->result_type()); + address rh_end = rh_begin; + while (*(int*)rh_end != 0) { + rh_end += sizeof(int); + } + Disassembler::decode(rh_begin, rh_end); +#endif + } + // add handler to library + _fingerprints->append(fingerprint); + _handlers->append(handler); + // set handler index + assert(_fingerprints->length() == _handlers->length(), "sanity check"); + handler_index = _fingerprints->length() - 1; + } + } + } else { + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } + if (handler_index < 0) { + // use generic signature handler + method->set_signature_handler(Interpreter::slow_signature_handler()); + } else { + // set handler + method->set_signature_handler(_handlers->at(handler_index)); + } + } + assert(method->signature_handler() == Interpreter::slow_signature_handler() || + _handlers->find(method->signature_handler()) == _fingerprints->find(Fingerprinter(method).fingerprint()), + "sanity check"); +} + + +BufferBlob* SignatureHandlerLibrary::_handler_blob = NULL; +address SignatureHandlerLibrary::_handler = NULL; +GrowableArray* SignatureHandlerLibrary::_fingerprints = NULL; +GrowableArray
* SignatureHandlerLibrary::_handlers = NULL; +address SignatureHandlerLibrary::_buffer = NULL; + + +IRT_ENTRY(void, InterpreterRuntime::prepare_native_call(JavaThread* thread, methodOopDesc* method)) + methodHandle m(thread, method); + assert(m->is_native(), "sanity check"); + // lookup native function entry point if it doesn't exist + bool in_base_library; + if (!m->has_native_function()) { + NativeLookup::lookup(m, in_base_library, CHECK); + } + // make sure signature handler is installed + SignatureHandlerLibrary::add(m); + // The interpreter entry point checks the signature handler first, + // before trying to fetch the native entry point and klass mirror. + // We must set the signature handler last, so that multiple processors + // preparing the same method will be sure to see non-null entry & mirror. +IRT_END + +#if defined(IA32) || defined(AMD64) +IRT_LEAF(void, InterpreterRuntime::popframe_move_outgoing_args(JavaThread* thread, void* src_address, void* dest_address)) + if (src_address == dest_address) { + return; + } + ResetNoHandleMark rnm; // In a LEAF entry. + HandleMark hm; + ResourceMark rm; + frame fr = thread->last_frame(); + assert(fr.is_interpreted_frame(), ""); + jint bci = fr.interpreter_frame_bci(); + methodHandle mh(thread, fr.interpreter_frame_method()); + Bytecode_invoke* invoke = Bytecode_invoke_at(mh, bci); + ArgumentSizeComputer asc(invoke->signature()); + int size_of_arguments = (asc.size() + (invoke->is_invokestatic() ? 0 : 1)); // receiver + Copy::conjoint_bytes(src_address, dest_address, + size_of_arguments * Interpreter::stackElementSize()); +IRT_END +#endif diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp new file mode 100644 index 00000000000..0b071feb4c7 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp @@ -0,0 +1,148 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The InterpreterRuntime is called by the interpreter for everything +// that cannot/should not be dealt with in assembly and needs C support. + +class InterpreterRuntime: AllStatic { + friend class BytecodeClosure; // for method and bcp + friend class PrintingClosure; // for method and bcp + + private: + // Helper functions to access current interpreter state + static frame last_frame(JavaThread *thread) { return thread->last_frame(); } + static methodOop method(JavaThread *thread) { return last_frame(thread).interpreter_frame_method(); } + static address bcp(JavaThread *thread) { return last_frame(thread).interpreter_frame_bcp(); } + static void set_bcp_and_mdp(address bcp, JavaThread*thread); + static Bytecodes::Code code(JavaThread *thread) { return Bytecodes::code_at(bcp(thread)); } + static bool already_resolved(JavaThread *thread) { return cache_entry(thread)->is_resolved(code(thread)); } + static int one_byte_index(JavaThread *thread) { return bcp(thread)[1]; } + static int two_byte_index(JavaThread *thread) { return Bytes::get_Java_u2(bcp(thread) + 1); } + static int number_of_dimensions(JavaThread *thread) { return bcp(thread)[3]; } + static ConstantPoolCacheEntry* cache_entry(JavaThread *thread) { return method(thread)->constants()->cache()->entry_at(Bytes::get_native_u2(bcp(thread) + 1)); } + static void note_trap(JavaThread *thread, int reason, TRAPS); + + public: + // Constants + static void ldc (JavaThread* thread, bool wide); + + // Allocation + static void _new (JavaThread* thread, constantPoolOopDesc* pool, int index); + static void newarray (JavaThread* thread, BasicType type, jint size); + static void anewarray (JavaThread* thread, constantPoolOopDesc* pool, int index, jint size); + static void multianewarray(JavaThread* thread, jint* first_size_address); + static void register_finalizer(JavaThread* thread, oopDesc* obj); + + // Quicken instance-of and check-cast bytecodes + static void quicken_io_cc(JavaThread* thread); + + // Exceptions thrown by the interpreter + static void throw_AbstractMethodError(JavaThread* thread); + static void throw_IncompatibleClassChangeError(JavaThread* thread); + static void throw_StackOverflowError(JavaThread* thread); + static void throw_ArrayIndexOutOfBoundsException(JavaThread* thread, char* name, jint index); + static void throw_ClassCastException(JavaThread* thread, oopDesc* obj); + static void create_exception(JavaThread* thread, char* name, char* message); + static void create_klass_exception(JavaThread* thread, char* name, oopDesc* obj); + static address exception_handler_for_exception(JavaThread* thread, oopDesc* exception); + static void throw_pending_exception(JavaThread* thread); + + // Statics & fields + static void resolve_get_put(JavaThread* thread, Bytecodes::Code bytecode); + + // Synchronization + static void monitorenter(JavaThread* thread, BasicObjectLock* elem); + static void monitorexit (JavaThread* thread, BasicObjectLock* elem); + + static void throw_illegal_monitor_state_exception(JavaThread* thread); + static void new_illegal_monitor_state_exception(JavaThread* thread); + + // Calls + static void resolve_invoke (JavaThread* thread, Bytecodes::Code bytecode); + + // Breakpoints + static void _breakpoint(JavaThread* thread, methodOopDesc* method, address bcp); + static Bytecodes::Code get_original_bytecode_at(JavaThread* thread, methodOopDesc* method, address bcp); + static void set_original_bytecode_at(JavaThread* thread, methodOopDesc* method, address bcp, Bytecodes::Code new_code); + static bool is_breakpoint(JavaThread *thread) { return Bytecodes::code_or_bp_at(bcp(thread)) == Bytecodes::_breakpoint; } + + // Safepoints + static void at_safepoint(JavaThread* thread); + + // Debugger support + static void post_field_access(JavaThread *thread, oopDesc* obj, + ConstantPoolCacheEntry *cp_entry); + static void post_field_modification(JavaThread *thread, oopDesc* obj, + ConstantPoolCacheEntry *cp_entry, jvalue *value); + static void post_method_entry(JavaThread *thread); + static void post_method_exit (JavaThread *thread); + static int interpreter_contains(address pc); + + // Native signature handlers + static void prepare_native_call(JavaThread* thread, methodOopDesc* method); + static address slow_signature_handler(JavaThread* thread, + methodOopDesc* method, + intptr_t* from, intptr_t* to); + +#if defined(IA32) || defined(AMD64) + // Popframe support (only needed on x86 and AMD64) + static void popframe_move_outgoing_args(JavaThread* thread, void* src_address, void* dest_address); +#endif + + // Platform dependent stuff + #include "incls/_interpreterRT_pd.hpp.incl" + + // Interpreter's frequency counter overflow + static nmethod* frequency_counter_overflow(JavaThread* thread, address branch_bcp); + + // Interpreter profiling support + static jint bcp_to_di(methodOopDesc* method, address cur_bcp); + static jint profile_method(JavaThread* thread, address cur_bcp); + static void update_mdp_for_ret(JavaThread* thread, int bci); +#ifdef ASSERT + static void verify_mdp(methodOopDesc* method, address bcp, address mdp); +#endif // ASSERT +}; + + +class SignatureHandlerLibrary: public AllStatic { + public: + enum { buffer_size = 1*K }; // the size of the temporary code buffer + enum { blob_size = 32*K }; // the size of a handler code blob. + + private: + static BufferBlob* _handler_blob; // the current buffer blob containing the generated handlers + static address _handler; // next available address within _handler_blob; + static GrowableArray* _fingerprints; // the fingerprint collection + static GrowableArray
* _handlers; // the corresponding handlers + static address _buffer; // the temporary code buffer + + static address set_handler_blob(); + static void initialize(); + static address set_handler(CodeBuffer* buffer); + static void pd_set_handler(address handler); + + public: + static void add(methodHandle method); +}; diff --git a/hotspot/src/share/vm/interpreter/invocationCounter.cpp b/hotspot/src/share/vm/interpreter/invocationCounter.cpp new file mode 100644 index 00000000000..cb650778556 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/invocationCounter.cpp @@ -0,0 +1,170 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_invocationCounter.cpp.incl" + + +// Implementation of InvocationCounter + +void InvocationCounter::init() { + _counter = 0; // reset all the bits, including the sticky carry + reset(); +} + +void InvocationCounter::reset() { + // Only reset the state and don't make the method look like it's never + // been executed + set_state(wait_for_compile); +} + +void InvocationCounter::set_carry() { + _counter |= carry_mask; + + // The carry bit now indicates that this counter had achieved a very + // large value. Now reduce the value, so that the method can be + // executed many more times before re-entering the VM. + int old_count = count(); + int new_count = MIN2(old_count, (int) (CompileThreshold / 2)); + if (old_count != new_count) set(state(), new_count); +} + + +void InvocationCounter::set_state(State state) { + assert(0 <= state && state < number_of_states, "illegal state"); + int init = _init[state]; + // prevent from going to zero, to distinguish from never-executed methods + if (init == 0 && count() > 0) init = 1; + int carry = (_counter & carry_mask); // the carry bit is sticky + _counter = (init << number_of_noncount_bits) | carry | state; +} + + +void InvocationCounter::print() { + tty->print_cr("invocation count: up = %d, limit = %d, carry = %s, state = %s", + count(), limit(), + carry() ? "true" : "false", + state_as_string(state())); +} + +void InvocationCounter::print_short() { + tty->print(" [%d%s;%s]", count(), carry()?"+carry":"", state_as_short_string(state())); +} + +// Initialization + +int InvocationCounter::_init [InvocationCounter::number_of_states]; +InvocationCounter::Action InvocationCounter::_action[InvocationCounter::number_of_states]; +int InvocationCounter::InterpreterInvocationLimit; +int InvocationCounter::InterpreterBackwardBranchLimit; +int InvocationCounter::InterpreterProfileLimit; + +// Tier1 limits +int InvocationCounter::Tier1InvocationLimit; +int InvocationCounter::Tier1BackEdgeLimit; + + + +const char* InvocationCounter::state_as_string(State state) { + switch (state) { + case wait_for_nothing : return "wait_for_nothing"; + case wait_for_compile : return "wait_for_compile"; + } + ShouldNotReachHere(); + return NULL; +} + +const char* InvocationCounter::state_as_short_string(State state) { + switch (state) { + case wait_for_nothing : return "not comp."; + case wait_for_compile : return "compileable"; + } + ShouldNotReachHere(); + return NULL; +} + + +static address do_nothing(methodHandle method, TRAPS) { + // dummy action for inactive invocation counters + method->invocation_counter()->set_carry(); + method->invocation_counter()->set_state(InvocationCounter::wait_for_nothing); + return NULL; +} + + +static address do_decay(methodHandle method, TRAPS) { + // decay invocation counters so compilation gets delayed + method->invocation_counter()->decay(); + return NULL; +} + + +void InvocationCounter::def(State state, int init, Action action) { + assert(0 <= state && state < number_of_states, "illegal state"); + assert(0 <= init && init < count_limit, "initial value out of range"); + _init [state] = init; + _action[state] = action; +} + +address dummy_invocation_counter_overflow(methodHandle m, TRAPS) { + ShouldNotReachHere(); + return NULL; +} + +void InvocationCounter::reinitialize(bool delay_overflow) { + // define states + guarantee((int)number_of_states <= (int)state_limit, "adjust number_of_state_bits"); + def(wait_for_nothing, 0, do_nothing); + if (delay_overflow) { + def(wait_for_compile, 0, do_decay); + } else { + def(wait_for_compile, 0, dummy_invocation_counter_overflow); + } + + InterpreterInvocationLimit = CompileThreshold << number_of_noncount_bits; + InterpreterProfileLimit = ((CompileThreshold * InterpreterProfilePercentage) / 100)<< number_of_noncount_bits; + Tier1InvocationLimit = Tier2CompileThreshold << number_of_noncount_bits; + Tier1BackEdgeLimit = Tier2BackEdgeThreshold << number_of_noncount_bits; + + // When methodData is collected, the backward branch limit is compared against a + // methodData counter, rather than an InvocationCounter. In the former case, we + // don't need the shift by number_of_noncount_bits, but we do need to adjust + // the factor by which we scale the threshold. + if (ProfileInterpreter) { + InterpreterBackwardBranchLimit = (CompileThreshold * (OnStackReplacePercentage - InterpreterProfilePercentage)) / 100; + } else { + InterpreterBackwardBranchLimit = ((CompileThreshold * OnStackReplacePercentage) / 100) << number_of_noncount_bits; + } + + assert(0 <= InterpreterBackwardBranchLimit, + "OSR threshold should be non-negative"); + assert(0 <= InterpreterProfileLimit && + InterpreterProfileLimit <= InterpreterInvocationLimit, + "profile threshold should be less than the compilation threshold " + "and non-negative"); +} + +void invocationCounter_init() { + InvocationCounter::reinitialize(DelayCompilationDuringStartup); +} diff --git a/hotspot/src/share/vm/interpreter/invocationCounter.hpp b/hotspot/src/share/vm/interpreter/invocationCounter.hpp new file mode 100644 index 00000000000..78e278aa6c3 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/invocationCounter.hpp @@ -0,0 +1,137 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// InvocationCounters are used to trigger actions when a limit (threshold) is reached. +// For different states, different limits and actions can be defined in the initialization +// routine of InvocationCounters. +// +// Implementation notes: For space reasons, state & counter are both encoded in one word, +// The state is encoded using some of the least significant bits, the counter is using the +// more significant bits. The counter is incremented before a method is activated and an +// action is triggered when when count() > limit(). + +class InvocationCounter VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: // bit no: |31 3| 2 | 1 0 | + unsigned int _counter; // format: [count|carry|state] + + enum PrivateConstants { + number_of_state_bits = 2, + number_of_carry_bits = 1, + number_of_noncount_bits = number_of_state_bits + number_of_carry_bits, + number_of_count_bits = BitsPerInt - number_of_noncount_bits, + state_limit = nth_bit(number_of_state_bits), + count_grain = nth_bit(number_of_state_bits + number_of_carry_bits), + count_limit = nth_bit(number_of_count_bits - 1), + carry_mask = right_n_bits(number_of_carry_bits) << number_of_state_bits, + state_mask = right_n_bits(number_of_state_bits), + status_mask = right_n_bits(number_of_state_bits + number_of_carry_bits), + count_mask = ((int)(-1) ^ status_mask) + }; + + public: + static int InterpreterInvocationLimit; // CompileThreshold scaled for interpreter use + static int Tier1InvocationLimit; // CompileThreshold scaled for tier1 use + static int Tier1BackEdgeLimit; // BackEdgeThreshold scaled for tier1 use + + static int InterpreterBackwardBranchLimit; // A separate threshold for on stack replacement + + static int InterpreterProfileLimit; // Profiling threshold scaled for interpreter use + + typedef address (*Action)(methodHandle method, TRAPS); + + enum PublicConstants { + count_increment = count_grain, // use this value to increment the 32bit _counter word + count_mask_value = count_mask // use this value to mask the backedge counter + }; + + enum State { + wait_for_nothing, // do nothing when count() > limit() + wait_for_compile, // introduce nmethod when count() > limit() + number_of_states // must be <= state_limit + }; + + // Manipulation + void reset(); // sets state to wait state + void init(); // sets state into original state + void set_state(State state); // sets state and initializes counter correspondingly + inline void set(State state, int count); // sets state and counter + inline void decay(); // decay counter (divide by two) + void set_carry(); // set the sticky carry bit + + // Accessors + State state() const { return (State)(_counter & state_mask); } + bool carry() const { return (_counter & carry_mask) != 0; } + int limit() const { return CompileThreshold; } + Action action() const { return _action[state()]; } + int count() const { return _counter >> number_of_noncount_bits; } + + int get_InvocationLimit() const { return InterpreterInvocationLimit >> number_of_noncount_bits; } + int get_BackwardBranchLimit() const { return InterpreterBackwardBranchLimit >> number_of_noncount_bits; } + int get_ProfileLimit() const { return InterpreterProfileLimit >> number_of_noncount_bits; } + + // Test counter using scaled limits like the asm interpreter would do rather than doing + // the shifts to normalize the counter. + + bool reached_InvocationLimit() const { return _counter >= (unsigned int) InterpreterInvocationLimit; } + bool reached_BackwardBranchLimit() const { return _counter >= (unsigned int) InterpreterBackwardBranchLimit; } + + // Do this just like asm interpreter does for max speed + bool reached_ProfileLimit(InvocationCounter *back_edge_count) const { + return (_counter && count_mask) + back_edge_count->_counter >= (unsigned int) InterpreterProfileLimit; + } + + void increment() { _counter += count_increment; } + + + // Printing + void print(); + void print_short(); + + // Miscellaneous + static ByteSize counter_offset() { return byte_offset_of(InvocationCounter, _counter); } + static void reinitialize(bool delay_overflow); + + private: + static int _init [number_of_states]; // the counter limits + static Action _action[number_of_states]; // the actions + + static void def(State state, int init, Action action); + static const char* state_as_string(State state); + static const char* state_as_short_string(State state); +}; + +inline void InvocationCounter::set(State state, int count) { + assert(0 <= state && state < number_of_states, "illegal state"); + int carry = (_counter & carry_mask); // the carry bit is sticky + _counter = (count << number_of_noncount_bits) | carry | state; +} + +inline void InvocationCounter::decay() { + int c = count(); + int new_count = c >> 1; + // prevent from going to zero, to distinguish from never-executed methods + if (c > 0 && new_count == 0) new_count = 1; + set(state(), new_count); +} diff --git a/hotspot/src/share/vm/interpreter/linkResolver.cpp b/hotspot/src/share/vm/interpreter/linkResolver.cpp new file mode 100644 index 00000000000..393275f2a43 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/linkResolver.cpp @@ -0,0 +1,1000 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_linkResolver.cpp.incl" + +//------------------------------------------------------------------------------------------------------------------------ +// Implementation of FieldAccessInfo + +void FieldAccessInfo::set(KlassHandle klass, symbolHandle name, int field_index, int field_offset, +BasicType field_type, AccessFlags access_flags) { + _klass = klass; + _name = name; + _field_index = field_index; + _field_offset = field_offset; + _field_type = field_type; + _access_flags = access_flags; +} + + +//------------------------------------------------------------------------------------------------------------------------ +// Implementation of CallInfo + + +void CallInfo::set_static(KlassHandle resolved_klass, methodHandle resolved_method, TRAPS) { + int vtable_index = methodOopDesc::nonvirtual_vtable_index; + set_common(resolved_klass, resolved_klass, resolved_method, resolved_method, vtable_index, CHECK); +} + + +void CallInfo::set_interface(KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, TRAPS) { + // This is only called for interface methods. If the resolved_method + // comes from java/lang/Object, it can be the subject of a virtual call, so + // we should pick the vtable index from the resolved method. + // Other than that case, there is no valid vtable index to specify. + int vtable_index = methodOopDesc::invalid_vtable_index; + if (resolved_method->method_holder() == SystemDictionary::object_klass()) { + assert(resolved_method->vtable_index() == selected_method->vtable_index(), "sanity check"); + vtable_index = resolved_method->vtable_index(); + } + set_common(resolved_klass, selected_klass, resolved_method, selected_method, vtable_index, CHECK); +} + +void CallInfo::set_virtual(KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, int vtable_index, TRAPS) { + assert(vtable_index >= 0 || vtable_index == methodOopDesc::nonvirtual_vtable_index, "valid index"); + set_common(resolved_klass, selected_klass, resolved_method, selected_method, vtable_index, CHECK); +} + +void CallInfo::set_common(KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, int vtable_index, TRAPS) { + assert(resolved_method->signature() == selected_method->signature(), "signatures must correspond"); + _resolved_klass = resolved_klass; + _selected_klass = selected_klass; + _resolved_method = resolved_method; + _selected_method = selected_method; + _vtable_index = vtable_index; + if (CompilationPolicy::mustBeCompiled(selected_method)) { + // Note: with several active threads, the mustBeCompiled may be true + // while canBeCompiled is false; remove assert + // assert(CompilationPolicy::canBeCompiled(selected_method), "cannot compile"); + if (THREAD->is_Compiler_thread()) { + // don't force compilation, resolve was on behalf of compiler + return; + } + CompileBroker::compile_method(selected_method, InvocationEntryBci, + methodHandle(), 0, "mustBeCompiled", CHECK); + } +} + + +//------------------------------------------------------------------------------------------------------------------------ +// Klass resolution + +void LinkResolver::check_klass_accessability(KlassHandle ref_klass, KlassHandle sel_klass, TRAPS) { + if (!Reflection::verify_class_access(ref_klass->as_klassOop(), + sel_klass->as_klassOop(), + true)) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_IllegalAccessError(), + "tried to access class %s from class %s", + sel_klass->external_name(), + ref_klass->external_name() + ); + return; + } +} + +void LinkResolver::resolve_klass(KlassHandle& result, constantPoolHandle pool, int index, TRAPS) { + klassOop result_oop = pool->klass_ref_at(index, CHECK); + result = KlassHandle(THREAD, result_oop); +} + +void LinkResolver::resolve_klass_no_update(KlassHandle& result, constantPoolHandle pool, int index, TRAPS) { + klassOop result_oop = + constantPoolOopDesc::klass_ref_at_if_loaded_check(pool, index, CHECK); + result = KlassHandle(THREAD, result_oop); +} + + +//------------------------------------------------------------------------------------------------------------------------ +// Method resolution +// +// According to JVM spec. $5.4.3c & $5.4.3d + +void LinkResolver::lookup_method_in_klasses(methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS) { + methodOop result_oop = klass->uncached_lookup_method(name(), signature()); + result = methodHandle(THREAD, result_oop); +} + +// returns first instance method +void LinkResolver::lookup_instance_method_in_klasses(methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS) { + methodOop result_oop = klass->uncached_lookup_method(name(), signature()); + result = methodHandle(THREAD, result_oop); + while (!result.is_null() && result->is_static()) { + klass = KlassHandle(THREAD, Klass::cast(result->method_holder())->super()); + result = methodHandle(THREAD, klass->uncached_lookup_method(name(), signature())); + } +} + + +int LinkResolver::vtable_index_of_miranda_method(KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS) { + ResourceMark rm(THREAD); + klassVtable *vt = instanceKlass::cast(klass())->vtable(); + return vt->index_of_miranda(name(), signature()); +} + +void LinkResolver::lookup_method_in_interfaces(methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS) { + instanceKlass *ik = instanceKlass::cast(klass()); + result = methodHandle(THREAD, ik->lookup_method_in_all_interfaces(name(), signature())); +} + +void LinkResolver::check_method_accessability(KlassHandle ref_klass, + KlassHandle resolved_klass, + KlassHandle sel_klass, + methodHandle sel_method, + TRAPS) { + + AccessFlags flags = sel_method->access_flags(); + + // Special case: arrays always override "clone". JVMS 2.15. + // If the resolved klass is an array class, and the declaring class + // is java.lang.Object and the method is "clone", set the flags + // to public. + // + // We'll check for the method name first, as that's most likely + // to be false (so we'll short-circuit out of these tests). + if (sel_method->name() == vmSymbols::clone_name() && + sel_klass() == SystemDictionary::object_klass() && + resolved_klass->oop_is_array()) { + // We need to change "protected" to "public". + assert(flags.is_protected(), "clone not protected?"); + jint new_flags = flags.as_int(); + new_flags = new_flags & (~JVM_ACC_PROTECTED); + new_flags = new_flags | JVM_ACC_PUBLIC; + flags.set_flags(new_flags); + } + + if (!Reflection::verify_field_access(ref_klass->as_klassOop(), + resolved_klass->as_klassOop(), + sel_klass->as_klassOop(), + flags, + true)) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_IllegalAccessError(), + "tried to access method %s.%s%s from class %s", + sel_klass->external_name(), + sel_method->name()->as_C_string(), + sel_method->signature()->as_C_string(), + ref_klass->external_name() + ); + return; + } +} + +void LinkResolver::resolve_method(methodHandle& resolved_method, KlassHandle& resolved_klass, + constantPoolHandle pool, int index, TRAPS) { + + // resolve klass + resolve_klass(resolved_klass, pool, index, CHECK); + + symbolHandle method_name (THREAD, pool->name_ref_at(index)); + symbolHandle method_signature (THREAD, pool->signature_ref_at(index)); + KlassHandle current_klass(THREAD, pool->pool_holder()); + + resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); +} + +void LinkResolver::resolve_interface_method(methodHandle& resolved_method, KlassHandle& resolved_klass, constantPoolHandle pool, int index, TRAPS) { + + // resolve klass + resolve_klass(resolved_klass, pool, index, CHECK); + symbolHandle method_name (THREAD, pool->name_ref_at(index)); + symbolHandle method_signature (THREAD, pool->signature_ref_at(index)); + KlassHandle current_klass(THREAD, pool->pool_holder()); + + resolve_interface_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); +} + + +void LinkResolver::resolve_method(methodHandle& resolved_method, KlassHandle resolved_klass, + symbolHandle method_name, symbolHandle method_signature, + KlassHandle current_klass, bool check_access, TRAPS) { + + // 1. check if klass is not interface + if (resolved_klass->is_interface()) { + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Found interface %s, but class was expected", Klass::cast(resolved_klass())->external_name()); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } + + // 2. lookup method in resolved klass and its super klasses + lookup_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, CHECK); + + if (resolved_method.is_null()) { // not found in the class hierarchy + // 3. lookup method in all the interfaces implemented by the resolved klass + lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK); + + if (resolved_method.is_null()) { + // 4. method lookup failed + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + method_name(), + method_signature())); + } + } + + // 5. check if method is concrete + if (resolved_method->is_abstract() && !resolved_klass->is_abstract()) { + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_AbstractMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + method_name(), + method_signature())); + } + + // 6. access checks, access checking may be turned off when calling from within the VM. + if (check_access) { + assert(current_klass.not_null() , "current_klass should not be null"); + + // check if method can be accessed by the referring class + check_method_accessability(current_klass, + resolved_klass, + KlassHandle(THREAD, resolved_method->method_holder()), + resolved_method, + CHECK); + + // check loader constraints + Handle loader (THREAD, instanceKlass::cast(current_klass())->class_loader()); + Handle class_loader (THREAD, instanceKlass::cast(resolved_method->method_holder())->class_loader()); + { + ResourceMark rm(THREAD); + char* failed_type_name = + SystemDictionary::check_signature_loaders(method_signature, loader, + class_loader, true, CHECK); + if (failed_type_name != NULL) { + const char* msg = "loader constraint violation: when resolving method" + " \"%s\" the class loader (instance of %s) of the current class, %s," + " and the class loader (instance of %s) for resolved class, %s, have" + " different Class objects for the type %s used in the signature"; + char* sig = methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()),method_name(),method_signature()); + const char* loader1 = SystemDictionary::loader_name(loader()); + char* current = instanceKlass::cast(current_klass())->name()->as_C_string(); + const char* loader2 = SystemDictionary::loader_name(class_loader()); + char* resolved = instanceKlass::cast(resolved_klass())->name()->as_C_string(); + size_t buflen = strlen(msg) + strlen(sig) + strlen(loader1) + + strlen(current) + strlen(loader2) + strlen(resolved) + + strlen(failed_type_name); + char* buf = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, buflen); + jio_snprintf(buf, buflen, msg, sig, loader1, current, loader2, + resolved, failed_type_name); + THROW_MSG(vmSymbols::java_lang_LinkageError(), buf); + } + } + } +} + +void LinkResolver::resolve_interface_method(methodHandle& resolved_method, + KlassHandle resolved_klass, + symbolHandle method_name, + symbolHandle method_signature, + KlassHandle current_klass, + bool check_access, TRAPS) { + + // check if klass is interface + if (!resolved_klass->is_interface()) { + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Found class %s, but interface was expected", Klass::cast(resolved_klass())->external_name()); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } + + // lookup method in this interface or its super, java.lang.Object + lookup_instance_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, CHECK); + + if (resolved_method.is_null()) { + // lookup method in all the super-interfaces + lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK); + if (resolved_method.is_null()) { + // no method found + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + method_name(), + method_signature())); + } + } + + if (check_access) { + HandleMark hm(THREAD); + Handle loader (THREAD, instanceKlass::cast(current_klass())->class_loader()); + Handle class_loader (THREAD, instanceKlass::cast(resolved_method->method_holder())->class_loader()); + { + ResourceMark rm(THREAD); + char* failed_type_name = + SystemDictionary::check_signature_loaders(method_signature, loader, + class_loader, true, CHECK); + if (failed_type_name != NULL) { + const char* msg = "loader constraint violation: when resolving " + "interface method \"%s\" the class loader (instance of %s) of the " + "current class, %s, and the class loader (instance of %s) for " + "resolved class, %s, have different Class objects for the type %s " + "used in the signature"; + char* sig = methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()),method_name(),method_signature()); + const char* loader1 = SystemDictionary::loader_name(loader()); + char* current = instanceKlass::cast(current_klass())->name()->as_C_string(); + const char* loader2 = SystemDictionary::loader_name(class_loader()); + char* resolved = instanceKlass::cast(resolved_klass())->name()->as_C_string(); + size_t buflen = strlen(msg) + strlen(sig) + strlen(loader1) + + strlen(current) + strlen(loader2) + strlen(resolved) + + strlen(failed_type_name); + char* buf = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, buflen); + jio_snprintf(buf, buflen, msg, sig, loader1, current, loader2, + resolved, failed_type_name); + THROW_MSG(vmSymbols::java_lang_LinkageError(), buf); + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------ +// Field resolution + +void LinkResolver::check_field_accessability(KlassHandle ref_klass, + KlassHandle resolved_klass, + KlassHandle sel_klass, + fieldDescriptor& fd, + TRAPS) { + if (!Reflection::verify_field_access(ref_klass->as_klassOop(), + resolved_klass->as_klassOop(), + sel_klass->as_klassOop(), + fd.access_flags(), + true)) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_IllegalAccessError(), + "tried to access field %s.%s from class %s", + sel_klass->external_name(), + fd.name()->as_C_string(), + ref_klass->external_name() + ); + return; + } +} + +void LinkResolver::resolve_field(FieldAccessInfo& result, constantPoolHandle pool, int index, Bytecodes::Code byte, bool check_only, TRAPS) { + resolve_field(result, pool, index, byte, check_only, true, CHECK); +} + +void LinkResolver::resolve_field(FieldAccessInfo& result, constantPoolHandle pool, int index, Bytecodes::Code byte, bool check_only, bool update_pool, TRAPS) { + assert(byte == Bytecodes::_getstatic || byte == Bytecodes::_putstatic || + byte == Bytecodes::_getfield || byte == Bytecodes::_putfield, "bad bytecode"); + + bool is_static = (byte == Bytecodes::_getstatic || byte == Bytecodes::_putstatic); + bool is_put = (byte == Bytecodes::_putfield || byte == Bytecodes::_putstatic); + + // resolve specified klass + KlassHandle resolved_klass; + if (update_pool) { + resolve_klass(resolved_klass, pool, index, CHECK); + } else { + resolve_klass_no_update(resolved_klass, pool, index, CHECK); + } + // Load these early in case the resolve of the containing klass fails + symbolOop field = pool->name_ref_at(index); + symbolHandle field_h (THREAD, field); // preserve in case we need the name + symbolOop sig = pool->signature_ref_at(index); + // Check if there's a resolved klass containing the field + if( resolved_klass.is_null() ) { + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_NoSuchFieldError(), field->as_C_string()); + } + + // Resolve instance field + fieldDescriptor fd; // find_field initializes fd if found + KlassHandle sel_klass(THREAD, instanceKlass::cast(resolved_klass())->find_field(field, sig, &fd)); + // check if field exists; i.e., if a klass containing the field def has been selected + if (sel_klass.is_null()){ + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_NoSuchFieldError(), field->as_C_string()); + } + + // check access + KlassHandle ref_klass(THREAD, pool->pool_holder()); + check_field_accessability(ref_klass, resolved_klass, sel_klass, fd, CHECK); + + // check for errors + if (is_static != fd.is_static()) { + char msg[200]; + jio_snprintf(msg, sizeof(msg), "Expected %s field %s.%s", is_static ? "static" : "non-static", Klass::cast(resolved_klass())->external_name(), fd.name()->as_C_string()); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), msg); + } + + // Final fields can only be accessed from its own class. + if (is_put && fd.access_flags().is_final() && sel_klass() != pool->pool_holder()) { + THROW(vmSymbols::java_lang_IllegalAccessError()); + } + + // initialize resolved_klass if necessary + // note 1: the klass which declared the field must be initialized (i.e, sel_klass) + // according to the newest JVM spec (5.5, p.170) - was bug (gri 7/28/99) + // + // note 2: we don't want to force initialization if we are just checking + // if the field access is legal; e.g., during compilation + if (is_static && !check_only) { + sel_klass->initialize(CHECK); + } + + { + HandleMark hm(THREAD); + Handle ref_loader (THREAD, instanceKlass::cast(ref_klass())->class_loader()); + Handle sel_loader (THREAD, instanceKlass::cast(sel_klass())->class_loader()); + symbolHandle signature_ref (THREAD, pool->signature_ref_at(index)); + { + ResourceMark rm(THREAD); + char* failed_type_name = + SystemDictionary::check_signature_loaders(signature_ref, + ref_loader, sel_loader, + false, + CHECK); + if (failed_type_name != NULL) { + const char* msg = "loader constraint violation: when resolving field" + " \"%s\" the class loader (instance of %s) of the referring class, " + "%s, and the class loader (instance of %s) for the field's resolved " + "type, %s, have different Class objects for that type"; + char* field_name = field_h()->as_C_string(); + const char* loader1 = SystemDictionary::loader_name(ref_loader()); + char* sel = instanceKlass::cast(sel_klass())->name()->as_C_string(); + const char* loader2 = SystemDictionary::loader_name(sel_loader()); + size_t buflen = strlen(msg) + strlen(field_name) + strlen(loader1) + + strlen(sel) + strlen(loader2) + strlen(failed_type_name); + char* buf = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, buflen); + jio_snprintf(buf, buflen, msg, field_name, loader1, sel, loader2, + failed_type_name); + THROW_MSG(vmSymbols::java_lang_LinkageError(), buf); + } + } + } + + // return information. note that the klass is set to the actual klass containing the + // field, otherwise access of static fields in superclasses will not work. + KlassHandle holder (THREAD, fd.field_holder()); + symbolHandle name (THREAD, fd.name()); + result.set(holder, name, fd.index(), fd.offset(), fd.field_type(), fd.access_flags()); +} + + +//------------------------------------------------------------------------------------------------------------------------ +// Invoke resolution +// +// Naming conventions: +// +// resolved_method the specified method (i.e., static receiver specified via constant pool index) +// sel_method the selected method (selected via run-time lookup; e.g., based on dynamic receiver class) +// resolved_klass the specified klass (i.e., specified via constant pool index) +// recv_klass the receiver klass + + +void LinkResolver::resolve_static_call(CallInfo& result, KlassHandle& resolved_klass, symbolHandle method_name, + symbolHandle method_signature, KlassHandle current_klass, + bool check_access, bool initialize_class, TRAPS) { + methodHandle resolved_method; + linktime_resolve_static_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); + resolved_klass = KlassHandle(THREAD, Klass::cast(resolved_method->method_holder())); + + // Initialize klass (this should only happen if everything is ok) + if (initialize_class && resolved_klass->should_be_initialized()) { + resolved_klass->initialize(CHECK); + linktime_resolve_static_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); + } + + // setup result + result.set_static(resolved_klass, resolved_method, CHECK); +} + +// throws linktime exceptions +void LinkResolver::linktime_resolve_static_method(methodHandle& resolved_method, KlassHandle resolved_klass, + symbolHandle method_name, symbolHandle method_signature, + KlassHandle current_klass, bool check_access, TRAPS) { + + resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); + assert(resolved_method->name() != vmSymbols::class_initializer_name(), "should have been checked in verifier"); + + // check if static + if (!resolved_method->is_static()) { + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Expected static method %s", methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + resolved_method->name(), + resolved_method->signature())); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } +} + + +void LinkResolver::resolve_special_call(CallInfo& result, KlassHandle resolved_klass, symbolHandle method_name, + symbolHandle method_signature, KlassHandle current_klass, bool check_access, TRAPS) { + methodHandle resolved_method; + linktime_resolve_special_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); + runtime_resolve_special_method(result, resolved_method, resolved_klass, current_klass, check_access, CHECK); +} + +// throws linktime exceptions +void LinkResolver::linktime_resolve_special_method(methodHandle& resolved_method, KlassHandle resolved_klass, + symbolHandle method_name, symbolHandle method_signature, + KlassHandle current_klass, bool check_access, TRAPS) { + + resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); + + // check if method name is , that it is found in same klass as static type + if (resolved_method->name() == vmSymbols::object_initializer_name() && + resolved_method->method_holder() != resolved_klass()) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_NoSuchMethodError(), + "%s: method %s%s not found", + resolved_klass->external_name(), + resolved_method->name()->as_C_string(), + resolved_method->signature()->as_C_string() + ); + return; + } + + // check if not static + if (resolved_method->is_static()) { + char buf[200]; + jio_snprintf(buf, sizeof(buf), + "Expecting non-static method %s", + methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + resolved_method->name(), + resolved_method->signature())); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } +} + +// throws runtime exceptions +void LinkResolver::runtime_resolve_special_method(CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, + KlassHandle current_klass, bool check_access, TRAPS) { + + // resolved method is selected method unless we have an old-style lookup + methodHandle sel_method(THREAD, resolved_method()); + + // check if this is an old-style super call and do a new lookup if so + { KlassHandle method_klass = KlassHandle(THREAD, + resolved_method->method_holder()); + + if (check_access && + // a) check if ACC_SUPER flag is set for the current class + current_klass->is_super() && + // b) check if the method class is a superclass of the current class (superclass relation is not reflexive!) + current_klass->is_subtype_of(method_klass()) && current_klass() != method_klass() && + // c) check if the method is not + resolved_method->name() != vmSymbols::object_initializer_name()) { + // Lookup super method + KlassHandle super_klass(THREAD, current_klass->super()); + lookup_instance_method_in_klasses(sel_method, super_klass, + symbolHandle(THREAD, resolved_method->name()), + symbolHandle(THREAD, resolved_method->signature()), CHECK); + // check if found + if (sel_method.is_null()) { + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_AbstractMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + resolved_method->name(), + resolved_method->signature())); + } + } + } + + // check if not static + if (sel_method->is_static()) { + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Expecting non-static method %s", methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + resolved_method->name(), + resolved_method->signature())); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } + + // check if abstract + if (sel_method->is_abstract()) { + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_AbstractMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + sel_method->name(), + sel_method->signature())); + } + + // setup result + result.set_static(resolved_klass, sel_method, CHECK); +} + +void LinkResolver::resolve_virtual_call(CallInfo& result, Handle recv, KlassHandle receiver_klass, KlassHandle resolved_klass, + symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, + bool check_access, bool check_null_and_abstract, TRAPS) { + methodHandle resolved_method; + linktime_resolve_virtual_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); + runtime_resolve_virtual_method(result, resolved_method, resolved_klass, recv, receiver_klass, check_null_and_abstract, CHECK); +} + +// throws linktime exceptions +void LinkResolver::linktime_resolve_virtual_method(methodHandle &resolved_method, KlassHandle resolved_klass, + symbolHandle method_name, symbolHandle method_signature, + KlassHandle current_klass, bool check_access, TRAPS) { + // normal method resolution + resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); + + assert(resolved_method->name() != vmSymbols::object_initializer_name(), "should have been checked in verifier"); + assert(resolved_method->name() != vmSymbols::class_initializer_name (), "should have been checked in verifier"); + + // check if not static + if (resolved_method->is_static()) { + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Expecting non-static method %s", methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + resolved_method->name(), + resolved_method->signature())); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } +} + +// throws runtime exceptions +void LinkResolver::runtime_resolve_virtual_method(CallInfo& result, + methodHandle resolved_method, + KlassHandle resolved_klass, + Handle recv, + KlassHandle recv_klass, + bool check_null_and_abstract, + TRAPS) { + + // setup default return values + int vtable_index = methodOopDesc::invalid_vtable_index; + methodHandle selected_method; + + assert(recv.is_null() || recv->is_oop(), "receiver is not an oop"); + + // runtime method resolution + if (check_null_and_abstract && recv.is_null()) { // check if receiver exists + THROW(vmSymbols::java_lang_NullPointerException()); + } + + // Virtual methods cannot be resolved before its klass has been linked, for otherwise the methodOop's + // has not been rewritten, and the vtable initialized. + assert(instanceKlass::cast(resolved_method->method_holder())->is_linked(), "must be linked"); + + // Virtual methods cannot be resolved before its klass has been linked, for otherwise the methodOop's + // has not been rewritten, and the vtable initialized. Make sure to do this after the nullcheck, since + // a missing receiver might result in a bogus lookup. + assert(instanceKlass::cast(resolved_method->method_holder())->is_linked(), "must be linked"); + + // do lookup based on receiver klass using the vtable index + if (resolved_method->method_holder()->klass_part()->is_interface()) { // miranda method + vtable_index = vtable_index_of_miranda_method(resolved_klass, + symbolHandle(THREAD, resolved_method->name()), + symbolHandle(THREAD, resolved_method->signature()), CHECK); + assert(vtable_index >= 0 , "we should have valid vtable index at this point"); + + instanceKlass* inst = instanceKlass::cast(recv_klass()); + selected_method = methodHandle(THREAD, inst->method_at_vtable(vtable_index)); + } else { + // at this point we are sure that resolved_method is virtual and not + // a miranda method; therefore, it must have a valid vtable index. + vtable_index = resolved_method->vtable_index(); + // We could get a negative vtable_index for final methods, + // because as an optimization they are they are never put in the vtable, + // unless they override an existing method. + // If we do get a negative, it means the resolved method is the the selected + // method, and it can never be changed by an override. + if (vtable_index == methodOopDesc::nonvirtual_vtable_index) { + assert(resolved_method->can_be_statically_bound(), "cannot override this method"); + selected_method = resolved_method; + } else { + // recv_klass might be an arrayKlassOop but all vtables start at + // the same place. The cast is to avoid virtual call and assertion. + instanceKlass* inst = (instanceKlass*)recv_klass()->klass_part(); + selected_method = methodHandle(THREAD, inst->method_at_vtable(vtable_index)); + } + } + + // check if method exists + if (selected_method.is_null()) { + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_AbstractMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + resolved_method->name(), + resolved_method->signature())); + } + + // check if abstract + if (check_null_and_abstract && selected_method->is_abstract()) { + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_AbstractMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), + selected_method->name(), + selected_method->signature())); + } + + // setup result + result.set_virtual(resolved_klass, recv_klass, resolved_method, selected_method, vtable_index, CHECK); +} + +void LinkResolver::resolve_interface_call(CallInfo& result, Handle recv, KlassHandle recv_klass, KlassHandle resolved_klass, + symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, + bool check_access, bool check_null_and_abstract, TRAPS) { + methodHandle resolved_method; + linktime_resolve_interface_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); + runtime_resolve_interface_method(result, resolved_method, resolved_klass, recv, recv_klass, check_null_and_abstract, CHECK); +} + +// throws linktime exceptions +void LinkResolver::linktime_resolve_interface_method(methodHandle& resolved_method, KlassHandle resolved_klass, symbolHandle method_name, + symbolHandle method_signature, KlassHandle current_klass, bool check_access, TRAPS) { + // normal interface method resolution + resolve_interface_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); + + assert(resolved_method->name() != vmSymbols::object_initializer_name(), "should have been checked in verifier"); + assert(resolved_method->name() != vmSymbols::class_initializer_name (), "should have been checked in verifier"); +} + +// throws runtime exceptions +void LinkResolver::runtime_resolve_interface_method(CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, + Handle recv, KlassHandle recv_klass, bool check_null_and_abstract, TRAPS) { + // check if receiver exists + if (check_null_and_abstract && recv.is_null()) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + + // check if receiver klass implements the resolved interface + if (!recv_klass->is_subtype_of(resolved_klass())) { + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Class %s does not implement the requested interface %s", + (Klass::cast(recv_klass()))->external_name(), + (Klass::cast(resolved_klass()))->external_name()); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } + // do lookup based on receiver klass + methodHandle sel_method; + lookup_instance_method_in_klasses(sel_method, recv_klass, + symbolHandle(THREAD, resolved_method->name()), + symbolHandle(THREAD, resolved_method->signature()), CHECK); + // check if method exists + if (sel_method.is_null()) { + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_AbstractMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(recv_klass()), + resolved_method->name(), + resolved_method->signature())); + } + // check if public + if (!sel_method->is_public()) { + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(recv_klass()), + sel_method->name(), + sel_method->signature())); + } + // check if abstract + if (check_null_and_abstract && sel_method->is_abstract()) { + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_AbstractMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(recv_klass()), + sel_method->name(), + sel_method->signature())); + } + // setup result + result.set_interface(resolved_klass, recv_klass, resolved_method, sel_method, CHECK); +} + + +methodHandle LinkResolver::linktime_resolve_interface_method_or_null( + KlassHandle resolved_klass, + symbolHandle method_name, + symbolHandle method_signature, + KlassHandle current_klass, + bool check_access) { + EXCEPTION_MARK; + methodHandle method_result; + linktime_resolve_interface_method(method_result, resolved_klass, method_name, method_signature, current_klass, check_access, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return methodHandle(); + } else { + return method_result; + } +} + +methodHandle LinkResolver::linktime_resolve_virtual_method_or_null( + KlassHandle resolved_klass, + symbolHandle method_name, + symbolHandle method_signature, + KlassHandle current_klass, + bool check_access) { + EXCEPTION_MARK; + methodHandle method_result; + linktime_resolve_virtual_method(method_result, resolved_klass, method_name, method_signature, current_klass, check_access, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return methodHandle(); + } else { + return method_result; + } +} + +methodHandle LinkResolver::resolve_virtual_call_or_null( + KlassHandle receiver_klass, + KlassHandle resolved_klass, + symbolHandle name, + symbolHandle signature, + KlassHandle current_klass) { + EXCEPTION_MARK; + CallInfo info; + resolve_virtual_call(info, Handle(), receiver_klass, resolved_klass, name, signature, current_klass, true, false, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return methodHandle(); + } + return info.selected_method(); +} + +methodHandle LinkResolver::resolve_interface_call_or_null( + KlassHandle receiver_klass, + KlassHandle resolved_klass, + symbolHandle name, + symbolHandle signature, + KlassHandle current_klass) { + EXCEPTION_MARK; + CallInfo info; + resolve_interface_call(info, Handle(), receiver_klass, resolved_klass, name, signature, current_klass, true, false, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return methodHandle(); + } + return info.selected_method(); +} + +int LinkResolver::resolve_virtual_vtable_index( + KlassHandle receiver_klass, + KlassHandle resolved_klass, + symbolHandle name, + symbolHandle signature, + KlassHandle current_klass) { + EXCEPTION_MARK; + CallInfo info; + resolve_virtual_call(info, Handle(), receiver_klass, resolved_klass, name, signature, current_klass, true, false, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return methodOopDesc::invalid_vtable_index; + } + return info.vtable_index(); +} + +methodHandle LinkResolver::resolve_static_call_or_null( + KlassHandle resolved_klass, + symbolHandle name, + symbolHandle signature, + KlassHandle current_klass) { + EXCEPTION_MARK; + CallInfo info; + resolve_static_call(info, resolved_klass, name, signature, current_klass, true, false, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return methodHandle(); + } + return info.selected_method(); +} + +methodHandle LinkResolver::resolve_special_call_or_null(KlassHandle resolved_klass, symbolHandle name, symbolHandle signature, + KlassHandle current_klass) { + EXCEPTION_MARK; + CallInfo info; + resolve_special_call(info, resolved_klass, name, signature, current_klass, true, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return methodHandle(); + } + return info.selected_method(); +} + + + +//------------------------------------------------------------------------------------------------------------------------ +// ConstantPool entries + +void LinkResolver::resolve_invoke(CallInfo& result, Handle recv, constantPoolHandle pool, int index, Bytecodes::Code byte, TRAPS) { + switch (byte) { + case Bytecodes::_invokestatic : resolve_invokestatic (result, pool, index, CHECK); break; + case Bytecodes::_invokespecial : resolve_invokespecial (result, pool, index, CHECK); break; + case Bytecodes::_invokevirtual : resolve_invokevirtual (result, recv, pool, index, CHECK); break; + case Bytecodes::_invokeinterface: resolve_invokeinterface(result, recv, pool, index, CHECK); break; + } + return; +} + +void LinkResolver::resolve_pool(KlassHandle& resolved_klass, symbolHandle& method_name, symbolHandle& method_signature, + KlassHandle& current_klass, constantPoolHandle pool, int index, TRAPS) { + // resolve klass + resolve_klass(resolved_klass, pool, index, CHECK); + + // Get name, signature, and static klass + method_name = symbolHandle(THREAD, pool->name_ref_at(index)); + method_signature = symbolHandle(THREAD, pool->signature_ref_at(index)); + current_klass = KlassHandle(THREAD, pool->pool_holder()); +} + + +void LinkResolver::resolve_invokestatic(CallInfo& result, constantPoolHandle pool, int index, TRAPS) { + KlassHandle resolved_klass; + symbolHandle method_name; + symbolHandle method_signature; + KlassHandle current_klass; + resolve_pool(resolved_klass, method_name, method_signature, current_klass, pool, index, CHECK); + resolve_static_call(result, resolved_klass, method_name, method_signature, current_klass, true, true, CHECK); +} + + +void LinkResolver::resolve_invokespecial(CallInfo& result, constantPoolHandle pool, int index, TRAPS) { + KlassHandle resolved_klass; + symbolHandle method_name; + symbolHandle method_signature; + KlassHandle current_klass; + resolve_pool(resolved_klass, method_name, method_signature, current_klass, pool, index, CHECK); + resolve_special_call(result, resolved_klass, method_name, method_signature, current_klass, true, CHECK); +} + + +void LinkResolver::resolve_invokevirtual(CallInfo& result, Handle recv, + constantPoolHandle pool, int index, + TRAPS) { + + KlassHandle resolved_klass; + symbolHandle method_name; + symbolHandle method_signature; + KlassHandle current_klass; + resolve_pool(resolved_klass, method_name, method_signature, current_klass, pool, index, CHECK); + KlassHandle recvrKlass (THREAD, recv.is_null() ? (klassOop)NULL : recv->klass()); + resolve_virtual_call(result, recv, recvrKlass, resolved_klass, method_name, method_signature, current_klass, true, true, CHECK); +} + + +void LinkResolver::resolve_invokeinterface(CallInfo& result, Handle recv, constantPoolHandle pool, int index, TRAPS) { + KlassHandle resolved_klass; + symbolHandle method_name; + symbolHandle method_signature; + KlassHandle current_klass; + resolve_pool(resolved_klass, method_name, method_signature, current_klass, pool, index, CHECK); + KlassHandle recvrKlass (THREAD, recv.is_null() ? (klassOop)NULL : recv->klass()); + resolve_interface_call(result, recv, recvrKlass, resolved_klass, method_name, method_signature, current_klass, true, true, CHECK); +} + +//------------------------------------------------------------------------------------------------------------------------ +#ifndef PRODUCT + +void FieldAccessInfo::print() { + ResourceMark rm; + tty->print_cr("Field %s@%d", name()->as_C_string(), field_offset()); +} + +#endif diff --git a/hotspot/src/share/vm/interpreter/linkResolver.hpp b/hotspot/src/share/vm/interpreter/linkResolver.hpp new file mode 100644 index 00000000000..6280ff62615 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/linkResolver.hpp @@ -0,0 +1,171 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// All the necessary definitions for run-time link resolution. + +// LinkInfo & its subclasses provide all the information gathered +// for a particular link after resolving it. A link is any reference +// made from within the bytecodes of a method to an object outside of +// that method. If the info is invalid, the link has not been resolved +// successfully. + +class LinkInfo VALUE_OBJ_CLASS_SPEC { +}; + + +// Link information for getfield/putfield & getstatic/putstatic bytecodes. + +class FieldAccessInfo: public LinkInfo { + protected: + KlassHandle _klass; + symbolHandle _name; + AccessFlags _access_flags; + int _field_index; // original index in the klass + int _field_offset; + BasicType _field_type; + + public: + void set(KlassHandle klass, symbolHandle name, int field_index, int field_offset, + BasicType field_type, AccessFlags access_flags); + KlassHandle klass() const { return _klass; } + symbolHandle name() const { return _name; } + int field_index() const { return _field_index; } + int field_offset() const { return _field_offset; } + BasicType field_type() const { return _field_type; } + AccessFlags access_flags() const { return _access_flags; } + + // debugging + void print() PRODUCT_RETURN; +}; + + +// Link information for all calls. + +class CallInfo: public LinkInfo { + private: + KlassHandle _resolved_klass; // static receiver klass + KlassHandle _selected_klass; // dynamic receiver class (same as static, or subklass) + methodHandle _resolved_method; // static target method + methodHandle _selected_method; // dynamic (actual) target method + int _vtable_index; // vtable index of selected method + + void set_static( KlassHandle resolved_klass, methodHandle resolved_method , TRAPS); + void set_interface(KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method , TRAPS); + void set_virtual( KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, int vtable_index, TRAPS); + void set_common( KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, int vtable_index, TRAPS); + + friend class LinkResolver; + + public: + KlassHandle resolved_klass() const { return _resolved_klass; } + KlassHandle selected_klass() const { return _selected_klass; } + methodHandle resolved_method() const { return _resolved_method; } + methodHandle selected_method() const { return _selected_method; } + + BasicType result_type() const { return selected_method()->result_type(); } + bool has_vtable_index() const { return _vtable_index >= 0; } + bool is_statically_bound() const { return _vtable_index == methodOopDesc::nonvirtual_vtable_index; } + int vtable_index() const { + // Even for interface calls the vtable index could be non-negative. + // See CallInfo::set_interface. + assert(has_vtable_index() || is_statically_bound(), ""); + return _vtable_index; + } +}; + + +// The LinkResolver is used to resolve constant-pool references at run-time. +// It does all necessary link-time checks & throws exceptions if necessary. + +class LinkResolver: AllStatic { + private: + static void lookup_method_in_klasses (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); + static void lookup_instance_method_in_klasses (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); + static void lookup_method_in_interfaces (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); + + static int vtable_index_of_miranda_method(KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); + + static void resolve_klass (KlassHandle& result, constantPoolHandle pool, int index, TRAPS); + static void resolve_klass_no_update (KlassHandle& result, constantPoolHandle pool, int index, TRAPS); // no update of constantPool entry + + static void resolve_pool (KlassHandle& resolved_klass, symbolHandle& method_name, symbolHandle& method_signature, KlassHandle& current_klass, constantPoolHandle pool, int index, TRAPS); + + static void resolve_interface_method(methodHandle& resolved_method, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, TRAPS); + static void resolve_method (methodHandle& resolved_method, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, TRAPS); + + static void linktime_resolve_static_method (methodHandle& resolved_method, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, TRAPS); + static void linktime_resolve_special_method (methodHandle& resolved_method, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, TRAPS); + static void linktime_resolve_virtual_method (methodHandle &resolved_method, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature,KlassHandle current_klass, bool check_access, TRAPS); + static void linktime_resolve_interface_method (methodHandle& resolved_method, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, TRAPS); + + static void runtime_resolve_special_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, KlassHandle current_klass, bool check_access, TRAPS); + static void runtime_resolve_virtual_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, bool check_null_and_abstract, TRAPS); + static void runtime_resolve_interface_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, bool check_null_and_abstract, TRAPS); + + static void check_field_accessability (KlassHandle ref_klass, KlassHandle resolved_klass, KlassHandle sel_klass, fieldDescriptor& fd, TRAPS); + static void check_method_accessability (KlassHandle ref_klass, KlassHandle resolved_klass, KlassHandle sel_klass, methodHandle sel_method, TRAPS); + + public: + // constant pool resolving + static void check_klass_accessability(KlassHandle ref_klass, KlassHandle sel_klass, TRAPS); + + // static resolving for all calls except interface calls + static void resolve_method (methodHandle& method_result, KlassHandle& klass_result, constantPoolHandle pool, int index, TRAPS); + static void resolve_interface_method(methodHandle& method_result, KlassHandle& klass_result, constantPoolHandle pool, int index, TRAPS); + + // runtime/static resolving for fields + static void resolve_field(FieldAccessInfo& result, constantPoolHandle pool, int index, Bytecodes::Code byte, bool check_only, TRAPS); + // takes an extra bool argument "update_pool" to decide whether to update the constantPool during klass resolution. + static void resolve_field(FieldAccessInfo& result, constantPoolHandle pool, int index, Bytecodes::Code byte, bool check_only, bool update_pool, TRAPS); + + // runtime resolving: + // resolved_klass = specified class (i.e., static receiver class) + // current_klass = sending method holder (i.e., class containing the method containing the call being resolved) + static void resolve_static_call (CallInfo& result, KlassHandle& resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, bool initialize_klass, TRAPS); + static void resolve_special_call (CallInfo& result, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, TRAPS); + static void resolve_virtual_call (CallInfo& result, Handle recv, KlassHandle recv_klass, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, bool check_null_and_abstract, TRAPS); + static void resolve_interface_call(CallInfo& result, Handle recv, KlassHandle recv_klass, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, bool check_null_and_abstract, TRAPS); + + // same as above for compile-time resolution; but returns null handle instead of throwing an exception on error + // also, does not initialize klass (i.e., no side effects) + static methodHandle resolve_virtual_call_or_null (KlassHandle receiver_klass, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass); + static methodHandle resolve_interface_call_or_null(KlassHandle receiver_klass, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass); + static methodHandle resolve_static_call_or_null (KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass); + static methodHandle resolve_special_call_or_null (KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass); + + // same as above for compile-time resolution; returns vtable_index if current_klass if linked + static int resolve_virtual_vtable_index (KlassHandle receiver_klass, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass); + + // static resolving for compiler (does not throw exceptions, returns null handle if unsuccessful) + static methodHandle linktime_resolve_virtual_method_or_null (KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access); + static methodHandle linktime_resolve_interface_method_or_null(KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access); + + // runtime resolving from constant pool + static void resolve_invokestatic (CallInfo& result, constantPoolHandle pool, int index, TRAPS); + static void resolve_invokespecial (CallInfo& result, constantPoolHandle pool, int index, TRAPS); + static void resolve_invokevirtual (CallInfo& result, Handle recv, constantPoolHandle pool, int index, TRAPS); + static void resolve_invokeinterface(CallInfo& result, Handle recv, constantPoolHandle pool, int index, TRAPS); + + static void resolve_invoke (CallInfo& result, Handle recv, constantPoolHandle pool, int index, Bytecodes::Code byte, TRAPS); +}; diff --git a/hotspot/src/share/vm/interpreter/oopMapCache.cpp b/hotspot/src/share/vm/interpreter/oopMapCache.cpp new file mode 100644 index 00000000000..6a777dd789a --- /dev/null +++ b/hotspot/src/share/vm/interpreter/oopMapCache.cpp @@ -0,0 +1,643 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_oopMapCache.cpp.incl" + +class OopMapCacheEntry: private InterpreterOopMap { + friend class InterpreterOopMap; + friend class OopMapForCacheEntry; + friend class OopMapCache; + friend class VerifyClosure; + + protected: + // Initialization + void fill(methodHandle method, int bci); + // fills the bit mask for native calls + void fill_for_native(methodHandle method); + void set_mask(CellTypeState* vars, CellTypeState* stack, int stack_top); + + // Deallocate bit masks and initialize fields + void flush(); + + private: + void allocate_bit_mask(); // allocates the bit mask on C heap f necessary + void deallocate_bit_mask(); // allocates the bit mask on C heap f necessary + bool verify_mask(CellTypeState *vars, CellTypeState *stack, int max_locals, int stack_top); + + public: + OopMapCacheEntry() : InterpreterOopMap() { +#ifdef ASSERT + _resource_allocate_bit_mask = false; +#endif + } +}; + + +// Implementation of OopMapForCacheEntry +// (subclass of GenerateOopMap, initializes an OopMapCacheEntry for a given method and bci) + +class OopMapForCacheEntry: public GenerateOopMap { + OopMapCacheEntry *_entry; + int _bci; + int _stack_top; + + virtual bool report_results() const { return false; } + virtual bool possible_gc_point (BytecodeStream *bcs); + virtual void fill_stackmap_prolog (int nof_gc_points); + virtual void fill_stackmap_epilog (); + virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, + CellTypeState* vars, + CellTypeState* stack, + int stack_top); + virtual void fill_init_vars (GrowableArray *init_vars); + + public: + OopMapForCacheEntry(methodHandle method, int bci, OopMapCacheEntry *entry); + + // Computes stack map for (method,bci) and initialize entry + void compute_map(TRAPS); + int size(); +}; + + +OopMapForCacheEntry::OopMapForCacheEntry(methodHandle method, int bci, OopMapCacheEntry* entry) : GenerateOopMap(method) { + _bci = bci; + _entry = entry; + _stack_top = -1; +} + + +void OopMapForCacheEntry::compute_map(TRAPS) { + assert(!method()->is_native(), "cannot compute oop map for native methods"); + // First check if it is a method where the stackmap is always empty + if (method()->code_size() == 0 || method()->max_locals() + method()->max_stack() == 0) { + _entry->set_mask_size(0); + } else { + ResourceMark rm; + GenerateOopMap::compute_map(CATCH); + result_for_basicblock(_bci); + } +} + + +bool OopMapForCacheEntry::possible_gc_point(BytecodeStream *bcs) { + return false; // We are not reporting any result. We call result_for_basicblock directly +} + + +void OopMapForCacheEntry::fill_stackmap_prolog(int nof_gc_points) { + // Do nothing +} + + +void OopMapForCacheEntry::fill_stackmap_epilog() { + // Do nothing +} + + +void OopMapForCacheEntry::fill_init_vars(GrowableArray *init_vars) { + // Do nothing +} + + +void OopMapForCacheEntry::fill_stackmap_for_opcodes(BytecodeStream *bcs, + CellTypeState* vars, + CellTypeState* stack, + int stack_top) { + // Only interested in one specific bci + if (bcs->bci() == _bci) { + _entry->set_mask(vars, stack, stack_top); + _stack_top = stack_top; + } +} + + +int OopMapForCacheEntry::size() { + assert(_stack_top != -1, "compute_map must be called first"); + return ((method()->is_static()) ? 0 : 1) + method()->max_locals() + _stack_top; +} + + +// Implementation of InterpreterOopMap and OopMapCacheEntry + +class VerifyClosure : public OffsetClosure { + private: + OopMapCacheEntry* _entry; + bool _failed; + + public: + VerifyClosure(OopMapCacheEntry* entry) { _entry = entry; _failed = false; } + void offset_do(int offset) { if (!_entry->is_oop(offset)) _failed = true; } + bool failed() const { return _failed; } +}; + +InterpreterOopMap::InterpreterOopMap() { + initialize(); +#ifdef ASSERT + _resource_allocate_bit_mask = true; +#endif +} + +InterpreterOopMap::~InterpreterOopMap() { + // The expection is that the bit mask was allocated + // last in this resource area. That would make the free of the + // bit_mask effective (see how FREE_RESOURCE_ARRAY does a free). + // If it was not allocated last, there is not a correctness problem + // but the space for the bit_mask is not freed. + assert(_resource_allocate_bit_mask, "Trying to free C heap space"); + if (mask_size() > small_mask_limit) { + FREE_RESOURCE_ARRAY(uintptr_t, _bit_mask[0], mask_word_size()); + } +} + +bool InterpreterOopMap::is_empty() { + bool result = _method == NULL; + assert(_method != NULL || (_bci == 0 && + (_mask_size == 0 || _mask_size == USHRT_MAX) && + _bit_mask[0] == 0), "Should be completely empty"); + return result; +} + +void InterpreterOopMap::initialize() { + _method = NULL; + _mask_size = USHRT_MAX; // This value should cause a failure quickly + _bci = 0; + _expression_stack_size = 0; + for (int i = 0; i < N; i++) _bit_mask[i] = 0; +} + + +void InterpreterOopMap::oop_iterate(OopClosure *blk) { + if (method() != NULL) { + blk->do_oop((oop*) &_method); + } +} + +void InterpreterOopMap::oop_iterate(OopClosure *blk, MemRegion mr) { + if (method() != NULL && mr.contains(&_method)) { + blk->do_oop((oop*) &_method); + } +} + + + +void InterpreterOopMap::iterate_oop(OffsetClosure* oop_closure) { + int n = number_of_entries(); + int word_index = 0; + uintptr_t value = 0; + uintptr_t mask = 0; + // iterate over entries + for (int i = 0; i < n; i++, mask <<= bits_per_entry) { + // get current word + if (mask == 0) { + value = bit_mask()[word_index++]; + mask = 1; + } + // test for oop + if ((value & (mask << oop_bit_number)) != 0) oop_closure->offset_do(i); + } +} + +void InterpreterOopMap::verify() { + // If we are doing mark sweep _method may not have a valid header + // $$$ This used to happen only for m/s collections; we might want to + // think of an appropriate generalization of this distinction. + guarantee(Universe::heap()->is_gc_active() || + _method->is_oop_or_null(), "invalid oop in oopMapCache") +} + +#ifdef ENABLE_ZAP_DEAD_LOCALS + +void InterpreterOopMap::iterate_all(OffsetClosure* oop_closure, OffsetClosure* value_closure, OffsetClosure* dead_closure) { + int n = number_of_entries(); + int word_index = 0; + uintptr_t value = 0; + uintptr_t mask = 0; + // iterate over entries + for (int i = 0; i < n; i++, mask <<= bits_per_entry) { + // get current word + if (mask == 0) { + value = bit_mask()[word_index++]; + mask = 1; + } + // test for dead values & oops, and for live values + if ((value & (mask << dead_bit_number)) != 0) dead_closure->offset_do(i); // call this for all dead values or oops + else if ((value & (mask << oop_bit_number)) != 0) oop_closure->offset_do(i); // call this for all live oops + else value_closure->offset_do(i); // call this for all live values + } +} + +#endif + + +void InterpreterOopMap::print() { + int n = number_of_entries(); + tty->print("oop map for "); + method()->print_value(); + tty->print(" @ %d = [%d] { ", bci(), n); + for (int i = 0; i < n; i++) { +#ifdef ENABLE_ZAP_DEAD_LOCALS + if (is_dead(i)) tty->print("%d+ ", i); + else +#endif + if (is_oop(i)) tty->print("%d ", i); + } + tty->print_cr("}"); +} + +class MaskFillerForNative: public NativeSignatureIterator { + private: + uintptr_t * _mask; // the bit mask to be filled + int _size; // the mask size in bits + + void set_one(int i) { + i *= InterpreterOopMap::bits_per_entry; + assert(0 <= i && i < _size, "offset out of bounds"); + _mask[i / BitsPerWord] |= (((uintptr_t) 1 << InterpreterOopMap::oop_bit_number) << (i % BitsPerWord)); + } + + public: + void pass_int() { /* ignore */ } + void pass_long() { /* ignore */ } +#ifdef _LP64 + void pass_float() { /* ignore */ } +#endif + void pass_double() { /* ignore */ } + void pass_object() { set_one(offset()); } + + MaskFillerForNative(methodHandle method, uintptr_t* mask, int size) : NativeSignatureIterator(method) { + _mask = mask; + _size = size; + // initialize with 0 + int i = (size + BitsPerWord - 1) / BitsPerWord; + while (i-- > 0) _mask[i] = 0; + } + + void generate() { + NativeSignatureIterator::iterate(); + } +}; + +bool OopMapCacheEntry::verify_mask(CellTypeState* vars, CellTypeState* stack, int max_locals, int stack_top) { + // Check mask includes map + VerifyClosure blk(this); + iterate_oop(&blk); + if (blk.failed()) return false; + + // Check if map is generated correctly + // (Use ?: operator to make sure all 'true' & 'false' are represented exactly the same so we can use == afterwards) + if (TraceOopMapGeneration && Verbose) tty->print("Locals (%d): ", max_locals); + + for(int i = 0; i < max_locals; i++) { + bool v1 = is_oop(i) ? true : false; + bool v2 = vars[i].is_reference() ? true : false; + assert(v1 == v2, "locals oop mask generation error"); + if (TraceOopMapGeneration && Verbose) tty->print("%d", v1 ? 1 : 0); +#ifdef ENABLE_ZAP_DEAD_LOCALS + bool v3 = is_dead(i) ? true : false; + bool v4 = !vars[i].is_live() ? true : false; + assert(v3 == v4, "locals live mask generation error"); + assert(!(v1 && v3), "dead value marked as oop"); +#endif + } + + if (TraceOopMapGeneration && Verbose) { tty->cr(); tty->print("Stack (%d): ", stack_top); } + for(int j = 0; j < stack_top; j++) { + bool v1 = is_oop(max_locals + j) ? true : false; + bool v2 = stack[j].is_reference() ? true : false; + assert(v1 == v2, "stack oop mask generation error"); + if (TraceOopMapGeneration && Verbose) tty->print("%d", v1 ? 1 : 0); +#ifdef ENABLE_ZAP_DEAD_LOCALS + bool v3 = is_dead(max_locals + j) ? true : false; + bool v4 = !stack[j].is_live() ? true : false; + assert(v3 == v4, "stack live mask generation error"); + assert(!(v1 && v3), "dead value marked as oop"); +#endif + } + if (TraceOopMapGeneration && Verbose) tty->cr(); + return true; +} + +void OopMapCacheEntry::allocate_bit_mask() { + if (mask_size() > small_mask_limit) { + assert(_bit_mask[0] == 0, "bit mask should be new or just flushed"); + _bit_mask[0] = (intptr_t) + NEW_C_HEAP_ARRAY(uintptr_t, mask_word_size()); + } +} + +void OopMapCacheEntry::deallocate_bit_mask() { + if (mask_size() > small_mask_limit && _bit_mask[0] != 0) { + assert(!Thread::current()->resource_area()->contains((void*)_bit_mask[0]), + "This bit mask should not be in the resource area"); + FREE_C_HEAP_ARRAY(uintptr_t, _bit_mask[0]); + debug_only(_bit_mask[0] = 0;) + } +} + + +void OopMapCacheEntry::fill_for_native(methodHandle mh) { + assert(mh->is_native(), "method must be native method"); + set_mask_size(mh->size_of_parameters() * bits_per_entry); + allocate_bit_mask(); + // fill mask for parameters + MaskFillerForNative mf(mh, bit_mask(), mask_size()); + mf.generate(); +} + + +void OopMapCacheEntry::fill(methodHandle method, int bci) { + HandleMark hm; + // Flush entry to deallocate an existing entry + flush(); + set_method(method()); + set_bci(bci); + if (method->is_native()) { + // Native method activations have oops only among the parameters and one + // extra oop following the parameters (the mirror for static native methods). + fill_for_native(method); + } else { + EXCEPTION_MARK; + OopMapForCacheEntry gen(method, bci, this); + gen.compute_map(CATCH); + } + #ifdef ASSERT + verify(); + #endif +} + + +void OopMapCacheEntry::set_mask(CellTypeState *vars, CellTypeState *stack, int stack_top) { + // compute bit mask size + int max_locals = method()->max_locals(); + int n_entries = max_locals + stack_top; + set_mask_size(n_entries * bits_per_entry); + allocate_bit_mask(); + set_expression_stack_size(stack_top); + + // compute bits + int word_index = 0; + uintptr_t value = 0; + uintptr_t mask = 1; + + CellTypeState* cell = vars; + for (int entry_index = 0; entry_index < n_entries; entry_index++, mask <<= bits_per_entry, cell++) { + // store last word + if (mask == 0) { + bit_mask()[word_index++] = value; + value = 0; + mask = 1; + } + + // switch to stack when done with locals + if (entry_index == max_locals) { + cell = stack; + } + + // set oop bit + if ( cell->is_reference()) { + value |= (mask << oop_bit_number ); + } + + #ifdef ENABLE_ZAP_DEAD_LOCALS + // set dead bit + if (!cell->is_live()) { + value |= (mask << dead_bit_number); + assert(!cell->is_reference(), "dead value marked as oop"); + } + #endif + } + + // make sure last word is stored + bit_mask()[word_index] = value; + + // verify bit mask + assert(verify_mask(vars, stack, max_locals, stack_top), "mask could not be verified"); + + +} + +void OopMapCacheEntry::flush() { + deallocate_bit_mask(); + initialize(); +} + + +// Implementation of OopMapCache + +#ifndef PRODUCT + +static long _total_memory_usage = 0; + +long OopMapCache::memory_usage() { + return _total_memory_usage; +} + +#endif + +void InterpreterOopMap::resource_copy(OopMapCacheEntry* from) { + assert(_resource_allocate_bit_mask, + "Should not resource allocate the _bit_mask"); + assert(from->method()->is_oop(), "MethodOop is bad"); + + set_method(from->method()); + set_bci(from->bci()); + set_mask_size(from->mask_size()); + set_expression_stack_size(from->expression_stack_size()); + + // Is the bit mask contained in the entry? + if (from->mask_size() <= small_mask_limit) { + memcpy((void *)_bit_mask, (void *)from->_bit_mask, + mask_word_size() * BytesPerWord); + } else { + // The expectation is that this InterpreterOopMap is a recently created + // and empty. It is used to get a copy of a cached entry. + // If the bit mask has a value, it should be in the + // resource area. + assert(_bit_mask[0] == 0 || + Thread::current()->resource_area()->contains((void*)_bit_mask[0]), + "The bit mask should have been allocated from a resource area"); + // Allocate the bit_mask from a Resource area for performance. Allocating + // from the C heap as is done for OopMapCache has a significant + // performance impact. + _bit_mask[0] = (uintptr_t) NEW_RESOURCE_ARRAY(uintptr_t, mask_word_size()); + assert(_bit_mask[0] != 0, "bit mask was not allocated"); + memcpy((void*) _bit_mask[0], (void*) from->_bit_mask[0], + mask_word_size() * BytesPerWord); + } +} + +inline unsigned int OopMapCache::hash_value_for(methodHandle method, int bci) { + // We use method->code_size() rather than method->identity_hash() below since + // the mark may not be present if a pointer to the method is already reversed. + return ((unsigned int) bci) + ^ ((unsigned int) method->max_locals() << 2) + ^ ((unsigned int) method->code_size() << 4) + ^ ((unsigned int) method->size_of_parameters() << 6); +} + + +OopMapCache::OopMapCache() : + _mut(Mutex::leaf, "An OopMapCache lock", true) +{ + _array = NEW_C_HEAP_ARRAY(OopMapCacheEntry, _size); + // Cannot call flush for initialization, since flush + // will check if memory should be deallocated + for(int i = 0; i < _size; i++) _array[i].initialize(); + NOT_PRODUCT(_total_memory_usage += sizeof(OopMapCache) + (sizeof(OopMapCacheEntry) * _size);) +} + + +OopMapCache::~OopMapCache() { + assert(_array != NULL, "sanity check"); + // Deallocate oop maps that are allocated out-of-line + flush(); + // Deallocate array + NOT_PRODUCT(_total_memory_usage -= sizeof(OopMapCache) + (sizeof(OopMapCacheEntry) * _size);) + FREE_C_HEAP_ARRAY(OopMapCacheEntry, _array); +} + +OopMapCacheEntry* OopMapCache::entry_at(int i) const { + return &_array[i % _size]; +} + +void OopMapCache::flush() { + for (int i = 0; i < _size; i++) _array[i].flush(); +} + +void OopMapCache::flush_obsolete_entries() { + for (int i = 0; i < _size; i++) + if (!_array[i].is_empty() && _array[i].method()->is_old()) { + // Cache entry is occupied by an old redefined method and we don't want + // to pin it down so flush the entry. + _array[i].flush(); + } +} + +void OopMapCache::oop_iterate(OopClosure *blk) { + for (int i = 0; i < _size; i++) _array[i].oop_iterate(blk); +} + +void OopMapCache::oop_iterate(OopClosure *blk, MemRegion mr) { + for (int i = 0; i < _size; i++) _array[i].oop_iterate(blk, mr); +} + +void OopMapCache::verify() { + for (int i = 0; i < _size; i++) _array[i].verify(); +} + +void OopMapCache::lookup(methodHandle method, + int bci, + InterpreterOopMap* entry_for) { + MutexLocker x(&_mut); + + OopMapCacheEntry* entry = NULL; + int probe = hash_value_for(method, bci); + + // Search hashtable for match + int i; + for(i = 0; i < _probe_depth; i++) { + entry = entry_at(probe + i); + if (entry->match(method, bci)) { + entry_for->resource_copy(entry); + assert(!entry_for->is_empty(), "A non-empty oop map should be returned"); + return; + } + } + + if (TraceOopMapGeneration) { + static int count = 0; + ResourceMark rm; + tty->print("%d - Computing oopmap at bci %d for ", ++count, bci); + method->print_value(); tty->cr(); + } + + // Entry is not in hashtable. + // Compute entry and return it + + // First search for an empty slot + for(i = 0; i < _probe_depth; i++) { + entry = entry_at(probe + i); + if (entry->is_empty()) { + entry->fill(method, bci); + entry_for->resource_copy(entry); + assert(!entry_for->is_empty(), "A non-empty oop map should be returned"); + if (method->is_old()) { + // The caller of lookup() will receive a copy of the interesting + // info via entry_for, but we don't keep an old redefined method in + // the cache to avoid pinning down the method. + entry->flush(); + } + return; + } + } + + if (TraceOopMapGeneration) { + ResourceMark rm; + tty->print_cr("*** collision in oopmap cache - flushing item ***"); + } + + // No empty slot (uncommon case). Use (some approximation of a) LRU algorithm + //entry_at(probe + _probe_depth - 1)->flush(); + //for(i = _probe_depth - 1; i > 0; i--) { + // // Coping entry[i] = entry[i-1]; + // OopMapCacheEntry *to = entry_at(probe + i); + // OopMapCacheEntry *from = entry_at(probe + i - 1); + // to->copy(from); + // } + + assert(method->is_method(), "gaga"); + + entry = entry_at(probe + 0); + entry->fill(method, bci); + + // Copy the newly cached entry to input parameter + entry_for->resource_copy(entry); + + if (TraceOopMapGeneration) { + ResourceMark rm; + tty->print("Done with "); + method->print_value(); tty->cr(); + } + assert(!entry_for->is_empty(), "A non-empty oop map should be returned"); + + if (method->is_old()) { + // The caller of lookup() will receive a copy of the interesting + // info via entry_for, but we don't keep an old redefined method in + // the cache to avoid pinning down the method. + entry->flush(); + } + + return; +} + +void OopMapCache::compute_one_oop_map(methodHandle method, int bci, InterpreterOopMap* entry) { + // Due to the invariants above it's tricky to allocate a temporary OopMapCacheEntry on the stack + OopMapCacheEntry* tmp = NEW_C_HEAP_ARRAY(OopMapCacheEntry, 1); + tmp->initialize(); + tmp->fill(method, bci); + entry->resource_copy(tmp); + FREE_C_HEAP_ARRAY(OopMapCacheEntry, tmp); +} diff --git a/hotspot/src/share/vm/interpreter/oopMapCache.hpp b/hotspot/src/share/vm/interpreter/oopMapCache.hpp new file mode 100644 index 00000000000..2a3d3da411b --- /dev/null +++ b/hotspot/src/share/vm/interpreter/oopMapCache.hpp @@ -0,0 +1,190 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A Cache for storing (method, bci) -> oopMap. +// The memory management system uses the cache when locating object +// references in an interpreted frame. +// +// OopMapCache's are allocated lazily per instanceKlass. + +// The oopMap (InterpreterOopMap) is stored as a bit mask. If the +// bit_mask can fit into two words it is stored in +// the _bit_mask array, otherwise it is allocated on the heap. +// For OopMapCacheEntry the bit_mask is allocated in the C heap +// because these entries persist between garbage collections. +// For InterpreterOopMap the bit_mask is allocated in +// a resource area for better performance. InterpreterOopMap +// should only be created and deleted during same garbage collection. +// +// If ENABBLE_ZAP_DEAD_LOCALS is defined, two bits are used +// per entry instead of one. In all cases, +// the first bit is set to indicate oops as opposed to other +// values. If the second bit is available, +// it is set for dead values. We get the following encoding: +// +// 00 live value +// 01 live oop +// 10 dead value +// 11 (we cannot distinguish between dead oops or values with the current oop map generator) + + +class OffsetClosure { + public: + virtual void offset_do(int offset) = 0; +}; + + +class InterpreterOopMap: ResourceObj { + friend class OopMapCache; + + public: + enum { + N = 2, // the number of words reserved + // for inlined mask storage + small_mask_limit = N * BitsPerWord, // the maximum number of bits + // available for small masks, + // small_mask_limit can be set to 0 + // for testing bit_mask allocation + +#ifdef ENABLE_ZAP_DEAD_LOCALS + bits_per_entry = 2, + dead_bit_number = 1, +#else + bits_per_entry = 1, +#endif + oop_bit_number = 0 + }; + + private: + methodOop _method; // the method for which the mask is valid + unsigned short _bci; // the bci for which the mask is valid + int _mask_size; // the mask size in bits + int _expression_stack_size; // the size of the expression stack in slots + + protected: + intptr_t _bit_mask[N]; // the bit mask if + // mask_size <= small_mask_limit, + // ptr to bit mask otherwise + // "protected" so that sub classes can + // access it without using trickery in + // methd bit_mask(). +#ifdef ASSERT + bool _resource_allocate_bit_mask; +#endif + + // access methods + methodOop method() const { return _method; } + void set_method(methodOop v) { _method = v; } + int bci() const { return _bci; } + void set_bci(int v) { _bci = v; } + int mask_size() const { return _mask_size; } + void set_mask_size(int v) { _mask_size = v; } + int number_of_entries() const { return mask_size() / bits_per_entry; } + // Test bit mask size and return either the in-line bit mask or allocated + // bit mask. + uintptr_t* bit_mask() { return (uintptr_t*)(mask_size() <= small_mask_limit ? (intptr_t)_bit_mask : _bit_mask[0]); } + + // return the word size of_bit_mask. mask_size() <= 4 * MAX_USHORT + size_t mask_word_size() { + return (mask_size() + BitsPerWord - 1) / BitsPerWord; + } + + uintptr_t entry_at(int offset) { int i = offset * bits_per_entry; return bit_mask()[i / BitsPerWord] >> (i % BitsPerWord); } + + void set_expression_stack_size(int sz) { _expression_stack_size = sz; } + +#ifdef ENABLE_ZAP_DEAD_LOCALS + bool is_dead(int offset) { return (entry_at(offset) & (1 << dead_bit_number)) != 0; } +#endif + + // Lookup + bool match(methodHandle method, int bci) { return _method == method() && _bci == bci; } + bool is_empty(); + + // Initialization + void initialize(); + + public: + InterpreterOopMap(); + ~InterpreterOopMap(); + + // Copy the OopMapCacheEntry in parameter "from" into this + // InterpreterOopMap. If the _bit_mask[0] in "from" points to + // allocated space (i.e., the bit mask was to large to hold + // in-line), allocate the space from a Resource area. + void resource_copy(OopMapCacheEntry* from); + + void iterate_oop(OffsetClosure* oop_closure); + void oop_iterate(OopClosure * blk); + void oop_iterate(OopClosure * blk, MemRegion mr); + void verify(); + void print(); + + bool is_oop (int offset) { return (entry_at(offset) & (1 << oop_bit_number )) != 0; } + + int expression_stack_size() { return _expression_stack_size; } + +#ifdef ENABLE_ZAP_DEAD_LOCALS + void iterate_all(OffsetClosure* oop_closure, OffsetClosure* value_closure, OffsetClosure* dead_closure); +#endif +}; + +class OopMapCache : public CHeapObj { + private: + enum { _size = 32, // Use fixed size for now + _probe_depth = 3 // probe depth in case of collisions + }; + + OopMapCacheEntry* _array; + + unsigned int hash_value_for(methodHandle method, int bci); + OopMapCacheEntry* entry_at(int i) const; + + Mutex _mut; + + void flush(); + + public: + OopMapCache(); + ~OopMapCache(); // free up memory + + // flush cache entry is occupied by an obsolete method + void flush_obsolete_entries(); + + // Returns the oopMap for (method, bci) in parameter "entry". + // Returns false if an oop map was not found. + void lookup(methodHandle method, int bci, InterpreterOopMap* entry); + + // Compute an oop map without updating the cache or grabbing any locks (for debugging) + static void compute_one_oop_map(methodHandle method, int bci, InterpreterOopMap* entry); + + // Helpers + // Iterate over the entries in the cached OopMapCacheEntry's + void oop_iterate(OopClosure *blk); + void oop_iterate(OopClosure *blk, MemRegion mr); + void verify(); + + // Returns total no. of bytes allocated as part of OopMapCache's + static long memory_usage() PRODUCT_RETURN0; +}; diff --git a/hotspot/src/share/vm/interpreter/rewriter.cpp b/hotspot/src/share/vm/interpreter/rewriter.cpp new file mode 100644 index 00000000000..d000012b5cb --- /dev/null +++ b/hotspot/src/share/vm/interpreter/rewriter.cpp @@ -0,0 +1,246 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_rewriter.cpp.incl" + + +// Computes an index_map (new_index -> original_index) for contant pool entries +// that are referred to by the interpreter at runtime via the constant pool cache. +void Rewriter::compute_index_maps(constantPoolHandle pool, intArray*& index_map, intStack*& inverse_index_map) { + const int length = pool->length(); + index_map = new intArray(length, -1); + // Choose an initial value large enough that we don't get frequent + // calls to grow(). + inverse_index_map = new intStack(length / 2); + for (int i = 0; i < length; i++) { + switch (pool->tag_at(i).value()) { + case JVM_CONSTANT_Fieldref : // fall through + case JVM_CONSTANT_Methodref : // fall through + case JVM_CONSTANT_InterfaceMethodref: { + index_map->at_put(i, inverse_index_map->length()); + inverse_index_map->append(i); + } + } + } +} + + +// Creates a constant pool cache given an inverse_index_map +constantPoolCacheHandle Rewriter::new_constant_pool_cache(intArray& inverse_index_map, TRAPS) { + const int length = inverse_index_map.length(); + constantPoolCacheOop cache = oopFactory::new_constantPoolCache(length, CHECK_(constantPoolCacheHandle())); + cache->initialize(inverse_index_map); + return constantPoolCacheHandle(THREAD, cache); +} + + + +// The new finalization semantics says that registration of +// finalizable objects must be performed on successful return from the +// Object. constructor. We could implement this trivially if +// were never rewritten but since JVMTI allows this to occur, a +// more complicated solution is required. A special return bytecode +// is used only by Object. to signal the finalization +// registration point. Additionally local 0 must be preserved so it's +// available to pass to the registration function. For simplicty we +// require that local 0 is never overwritten so it's available as an +// argument for registration. + +void Rewriter::rewrite_Object_init(methodHandle method, TRAPS) { + RawBytecodeStream bcs(method); + while (!bcs.is_last_bytecode()) { + Bytecodes::Code opcode = bcs.raw_next(); + switch (opcode) { + case Bytecodes::_return: *bcs.bcp() = Bytecodes::_return_register_finalizer; break; + + case Bytecodes::_istore: + case Bytecodes::_lstore: + case Bytecodes::_fstore: + case Bytecodes::_dstore: + case Bytecodes::_astore: + if (bcs.get_index() != 0) continue; + + // fall through + case Bytecodes::_istore_0: + case Bytecodes::_lstore_0: + case Bytecodes::_fstore_0: + case Bytecodes::_dstore_0: + case Bytecodes::_astore_0: + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), + "can't overwrite local 0 in Object."); + break; + } + } +} + + +// Rewrites a method given the index_map information +methodHandle Rewriter::rewrite_method(methodHandle method, intArray& index_map, TRAPS) { + + int nof_jsrs = 0; + bool has_monitor_bytecodes = false; + + { + // We cannot tolerate a GC in this block, because we've + // cached the bytecodes in 'code_base'. If the methodOop + // moves, the bytecodes will also move. + No_Safepoint_Verifier nsv; + Bytecodes::Code c; + + // Bytecodes and their length + const address code_base = method->code_base(); + const int code_length = method->code_size(); + + int bc_length; + for (int bci = 0; bci < code_length; bci += bc_length) { + address bcp = code_base + bci; + c = (Bytecodes::Code)(*bcp); + + // Since we have the code, see if we can get the length + // directly. Some more complicated bytecodes will report + // a length of zero, meaning we need to make another method + // call to calculate the length. + bc_length = Bytecodes::length_for(c); + if (bc_length == 0) { + bc_length = Bytecodes::length_at(bcp); + + // length_at will put us at the bytecode after the one modified + // by 'wide'. We don't currently examine any of the bytecodes + // modified by wide, but in case we do in the future... + if (c == Bytecodes::_wide) { + c = (Bytecodes::Code)bcp[1]; + } + } + + assert(bc_length != 0, "impossible bytecode length"); + + switch (c) { + case Bytecodes::_lookupswitch : { +#ifndef CC_INTERP + Bytecode_lookupswitch* bc = Bytecode_lookupswitch_at(bcp); + bc->set_code( + bc->number_of_pairs() < BinarySwitchThreshold + ? Bytecodes::_fast_linearswitch + : Bytecodes::_fast_binaryswitch + ); +#endif + break; + } + case Bytecodes::_getstatic : // fall through + case Bytecodes::_putstatic : // fall through + case Bytecodes::_getfield : // fall through + case Bytecodes::_putfield : // fall through + case Bytecodes::_invokevirtual : // fall through + case Bytecodes::_invokespecial : // fall through + case Bytecodes::_invokestatic : // fall through + case Bytecodes::_invokeinterface: { + address p = bcp + 1; + Bytes::put_native_u2(p, index_map[Bytes::get_Java_u2(p)]); + break; + } + case Bytecodes::_jsr : // fall through + case Bytecodes::_jsr_w : nof_jsrs++; break; + case Bytecodes::_monitorenter : // fall through + case Bytecodes::_monitorexit : has_monitor_bytecodes = true; break; + } + } + } + + // Update access flags + if (has_monitor_bytecodes) { + method->set_has_monitor_bytecodes(); + } + + // The present of a jsr bytecode implies that the method might potentially + // have to be rewritten, so we run the oopMapGenerator on the method + if (nof_jsrs > 0) { + method->set_has_jsrs(); + ResolveOopMapConflicts romc(method); + methodHandle original_method = method; + method = romc.do_potential_rewrite(CHECK_(methodHandle())); + if (method() != original_method()) { + // Insert invalid bytecode into original methodOop and set + // interpreter entrypoint, so that a executing this method + // will manifest itself in an easy recognizable form. + address bcp = original_method->bcp_from(0); + *bcp = (u1)Bytecodes::_shouldnotreachhere; + int kind = Interpreter::method_kind(original_method); + original_method->set_interpreter_kind(kind); + } + + // Update monitor matching info. + if (romc.monitor_safe()) { + method->set_guaranteed_monitor_matching(); + } + } + + // Setup method entrypoints for compiler and interpreter + method->link_method(method, CHECK_(methodHandle())); + + return method; +} + + +void Rewriter::rewrite(instanceKlassHandle klass, TRAPS) { + // gather starting points + ResourceMark rm(THREAD); + constantPoolHandle pool (THREAD, klass->constants()); + objArrayHandle methods (THREAD, klass->methods()); + assert(pool->cache() == NULL, "constant pool cache must not be set yet"); + + // determine index maps for methodOop rewriting + intArray* index_map = NULL; + intStack* inverse_index_map = NULL; + compute_index_maps(pool, index_map, inverse_index_map); + + // allocate constant pool cache + constantPoolCacheHandle cache = new_constant_pool_cache(*inverse_index_map, CHECK); + pool->set_cache(cache()); + cache->set_constant_pool(pool()); + + if (RegisterFinalizersAtInit && klass->name() == vmSymbols::java_lang_Object()) { + int i = methods->length(); + while (i-- > 0) { + methodOop method = (methodOop)methods->obj_at(i); + if (method->intrinsic_id() == vmIntrinsics::_Object_init) { + // rewrite the return bytecodes of Object. to register the + // object for finalization if needed. + methodHandle m(THREAD, method); + rewrite_Object_init(m, CHECK); + break; + } + } + } + + // rewrite methods + { int i = methods->length(); + while (i-- > 0) { + methodHandle m(THREAD, (methodOop)methods->obj_at(i)); + m = rewrite_method(m, *index_map, CHECK); + // Method might have gotten rewritten. + methods->obj_at_put(i, m()); + } + } +} diff --git a/hotspot/src/share/vm/interpreter/rewriter.hpp b/hotspot/src/share/vm/interpreter/rewriter.hpp new file mode 100644 index 00000000000..847dad07852 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/rewriter.hpp @@ -0,0 +1,37 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The Rewriter adds caches to the constant pool and rewrites bytecode indices +// pointing into the constant pool for better interpreter performance. + +class Rewriter: public AllStatic { + private: + static void compute_index_maps(constantPoolHandle pool, intArray*& index_map, intStack*& inverse_index_map); + static constantPoolCacheHandle new_constant_pool_cache(intArray& inverse_index_map, TRAPS); + static methodHandle rewrite_method(methodHandle method, intArray& index_map, TRAPS); + static void rewrite_Object_init(methodHandle method, TRAPS); + + public: + static void rewrite(instanceKlassHandle klass, TRAPS); +}; diff --git a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp new file mode 100644 index 00000000000..2a3b2d2a6ee --- /dev/null +++ b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp @@ -0,0 +1,597 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_templateInterpreter.cpp.incl" + +#ifndef CC_INTERP + +# define __ _masm-> + +void TemplateInterpreter::initialize() { + if (_code != NULL) return; + // assertions + assert((int)Bytecodes::number_of_codes <= (int)DispatchTable::length, + "dispatch table too small"); + + AbstractInterpreter::initialize(); + + TemplateTable::initialize(); + + // generate interpreter + { ResourceMark rm; + TraceTime timer("Interpreter generation", TraceStartupTime); + int code_size = InterpreterCodeSize; + NOT_PRODUCT(code_size *= 4;) // debug uses extra interpreter code space + _code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL, + "Interpreter"); + InterpreterGenerator g(_code); + if (PrintInterpreter) print(); + } + + // initialize dispatch table + _active_table = _normal_table; +} + +//------------------------------------------------------------------------------------------------------------------------ +// Implementation of EntryPoint + +EntryPoint::EntryPoint() { + assert(number_of_states == 9, "check the code below"); + _entry[btos] = NULL; + _entry[ctos] = NULL; + _entry[stos] = NULL; + _entry[atos] = NULL; + _entry[itos] = NULL; + _entry[ltos] = NULL; + _entry[ftos] = NULL; + _entry[dtos] = NULL; + _entry[vtos] = NULL; +} + + +EntryPoint::EntryPoint(address bentry, address centry, address sentry, address aentry, address ientry, address lentry, address fentry, address dentry, address ventry) { + assert(number_of_states == 9, "check the code below"); + _entry[btos] = bentry; + _entry[ctos] = centry; + _entry[stos] = sentry; + _entry[atos] = aentry; + _entry[itos] = ientry; + _entry[ltos] = lentry; + _entry[ftos] = fentry; + _entry[dtos] = dentry; + _entry[vtos] = ventry; +} + + +void EntryPoint::set_entry(TosState state, address entry) { + assert(0 <= state && state < number_of_states, "state out of bounds"); + _entry[state] = entry; +} + + +address EntryPoint::entry(TosState state) const { + assert(0 <= state && state < number_of_states, "state out of bounds"); + return _entry[state]; +} + + +void EntryPoint::print() { + tty->print("["); + for (int i = 0; i < number_of_states; i++) { + if (i > 0) tty->print(", "); + tty->print(INTPTR_FORMAT, _entry[i]); + } + tty->print("]"); +} + + +bool EntryPoint::operator == (const EntryPoint& y) { + int i = number_of_states; + while (i-- > 0) { + if (_entry[i] != y._entry[i]) return false; + } + return true; +} + + +//------------------------------------------------------------------------------------------------------------------------ +// Implementation of DispatchTable + +EntryPoint DispatchTable::entry(int i) const { + assert(0 <= i && i < length, "index out of bounds"); + return + EntryPoint( + _table[btos][i], + _table[ctos][i], + _table[stos][i], + _table[atos][i], + _table[itos][i], + _table[ltos][i], + _table[ftos][i], + _table[dtos][i], + _table[vtos][i] + ); +} + + +void DispatchTable::set_entry(int i, EntryPoint& entry) { + assert(0 <= i && i < length, "index out of bounds"); + assert(number_of_states == 9, "check the code below"); + _table[btos][i] = entry.entry(btos); + _table[ctos][i] = entry.entry(ctos); + _table[stos][i] = entry.entry(stos); + _table[atos][i] = entry.entry(atos); + _table[itos][i] = entry.entry(itos); + _table[ltos][i] = entry.entry(ltos); + _table[ftos][i] = entry.entry(ftos); + _table[dtos][i] = entry.entry(dtos); + _table[vtos][i] = entry.entry(vtos); +} + + +bool DispatchTable::operator == (DispatchTable& y) { + int i = length; + while (i-- > 0) { + EntryPoint t = y.entry(i); // for compiler compatibility (BugId 4150096) + if (!(entry(i) == t)) return false; + } + return true; +} + +address TemplateInterpreter::_remove_activation_entry = NULL; +address TemplateInterpreter::_remove_activation_preserving_args_entry = NULL; + + +address TemplateInterpreter::_throw_ArrayIndexOutOfBoundsException_entry = NULL; +address TemplateInterpreter::_throw_ArrayStoreException_entry = NULL; +address TemplateInterpreter::_throw_ArithmeticException_entry = NULL; +address TemplateInterpreter::_throw_ClassCastException_entry = NULL; +address TemplateInterpreter::_throw_NullPointerException_entry = NULL; +address TemplateInterpreter::_throw_StackOverflowError_entry = NULL; +address TemplateInterpreter::_throw_exception_entry = NULL; + +#ifndef PRODUCT +EntryPoint TemplateInterpreter::_trace_code; +#endif // !PRODUCT +EntryPoint TemplateInterpreter::_return_entry[TemplateInterpreter::number_of_return_entries]; +EntryPoint TemplateInterpreter::_earlyret_entry; +EntryPoint TemplateInterpreter::_deopt_entry [TemplateInterpreter::number_of_deopt_entries ]; +EntryPoint TemplateInterpreter::_continuation_entry; +EntryPoint TemplateInterpreter::_safept_entry; + +address TemplateInterpreter::_return_3_addrs_by_index[TemplateInterpreter::number_of_return_addrs]; +address TemplateInterpreter::_return_5_addrs_by_index[TemplateInterpreter::number_of_return_addrs]; + +DispatchTable TemplateInterpreter::_active_table; +DispatchTable TemplateInterpreter::_normal_table; +DispatchTable TemplateInterpreter::_safept_table; +address TemplateInterpreter::_wentry_point[DispatchTable::length]; + +TemplateInterpreterGenerator::TemplateInterpreterGenerator(StubQueue* _code): AbstractInterpreterGenerator(_code) { + _unimplemented_bytecode = NULL; + _illegal_bytecode_sequence = NULL; +} + +static const BasicType types[Interpreter::number_of_result_handlers] = { + T_BOOLEAN, + T_CHAR , + T_BYTE , + T_SHORT , + T_INT , + T_LONG , + T_VOID , + T_FLOAT , + T_DOUBLE , + T_OBJECT +}; + +void TemplateInterpreterGenerator::generate_all() { + AbstractInterpreterGenerator::generate_all(); + + { CodeletMark cm(_masm, "error exits"); + _unimplemented_bytecode = generate_error_exit("unimplemented bytecode"); + _illegal_bytecode_sequence = generate_error_exit("illegal bytecode sequence - method not verified"); + } + +#ifndef PRODUCT + if (TraceBytecodes) { + CodeletMark cm(_masm, "bytecode tracing support"); + Interpreter::_trace_code = + EntryPoint( + generate_trace_code(btos), + generate_trace_code(ctos), + generate_trace_code(stos), + generate_trace_code(atos), + generate_trace_code(itos), + generate_trace_code(ltos), + generate_trace_code(ftos), + generate_trace_code(dtos), + generate_trace_code(vtos) + ); + } +#endif // !PRODUCT + + { CodeletMark cm(_masm, "return entry points"); + for (int i = 0; i < Interpreter::number_of_return_entries; i++) { + Interpreter::_return_entry[i] = + EntryPoint( + generate_return_entry_for(itos, i), + generate_return_entry_for(itos, i), + generate_return_entry_for(itos, i), + generate_return_entry_for(atos, i), + generate_return_entry_for(itos, i), + generate_return_entry_for(ltos, i), + generate_return_entry_for(ftos, i), + generate_return_entry_for(dtos, i), + generate_return_entry_for(vtos, i) + ); + } + } + + { CodeletMark cm(_masm, "earlyret entry points"); + Interpreter::_earlyret_entry = + EntryPoint( + generate_earlyret_entry_for(btos), + generate_earlyret_entry_for(ctos), + generate_earlyret_entry_for(stos), + generate_earlyret_entry_for(atos), + generate_earlyret_entry_for(itos), + generate_earlyret_entry_for(ltos), + generate_earlyret_entry_for(ftos), + generate_earlyret_entry_for(dtos), + generate_earlyret_entry_for(vtos) + ); + } + + { CodeletMark cm(_masm, "deoptimization entry points"); + for (int i = 0; i < Interpreter::number_of_deopt_entries; i++) { + Interpreter::_deopt_entry[i] = + EntryPoint( + generate_deopt_entry_for(itos, i), + generate_deopt_entry_for(itos, i), + generate_deopt_entry_for(itos, i), + generate_deopt_entry_for(atos, i), + generate_deopt_entry_for(itos, i), + generate_deopt_entry_for(ltos, i), + generate_deopt_entry_for(ftos, i), + generate_deopt_entry_for(dtos, i), + generate_deopt_entry_for(vtos, i) + ); + } + } + + { CodeletMark cm(_masm, "result handlers for native calls"); + // The various result converter stublets. + int is_generated[Interpreter::number_of_result_handlers]; + memset(is_generated, 0, sizeof(is_generated)); + + for (int i = 0; i < Interpreter::number_of_result_handlers; i++) { + BasicType type = types[i]; + if (!is_generated[Interpreter::BasicType_as_index(type)]++) { + Interpreter::_native_abi_to_tosca[Interpreter::BasicType_as_index(type)] = generate_result_handler_for(type); + } + } + } + + for (int j = 0; j < number_of_states; j++) { + const TosState states[] = {btos, ctos, stos, itos, ltos, ftos, dtos, atos, vtos}; + Interpreter::_return_3_addrs_by_index[Interpreter::TosState_as_index(states[j])] = Interpreter::return_entry(states[j], 3); + Interpreter::_return_5_addrs_by_index[Interpreter::TosState_as_index(states[j])] = Interpreter::return_entry(states[j], 5); + } + + { CodeletMark cm(_masm, "continuation entry points"); + Interpreter::_continuation_entry = + EntryPoint( + generate_continuation_for(btos), + generate_continuation_for(ctos), + generate_continuation_for(stos), + generate_continuation_for(atos), + generate_continuation_for(itos), + generate_continuation_for(ltos), + generate_continuation_for(ftos), + generate_continuation_for(dtos), + generate_continuation_for(vtos) + ); + } + + { CodeletMark cm(_masm, "safepoint entry points"); + Interpreter::_safept_entry = + EntryPoint( + generate_safept_entry_for(btos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), + generate_safept_entry_for(ctos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), + generate_safept_entry_for(stos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), + generate_safept_entry_for(atos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), + generate_safept_entry_for(itos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), + generate_safept_entry_for(ltos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), + generate_safept_entry_for(ftos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), + generate_safept_entry_for(dtos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), + generate_safept_entry_for(vtos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)) + ); + } + + { CodeletMark cm(_masm, "exception handling"); + // (Note: this is not safepoint safe because thread may return to compiled code) + generate_throw_exception(); + } + + { CodeletMark cm(_masm, "throw exception entrypoints"); + Interpreter::_throw_ArrayIndexOutOfBoundsException_entry = generate_ArrayIndexOutOfBounds_handler("java/lang/ArrayIndexOutOfBoundsException"); + Interpreter::_throw_ArrayStoreException_entry = generate_klass_exception_handler("java/lang/ArrayStoreException" ); + Interpreter::_throw_ArithmeticException_entry = generate_exception_handler("java/lang/ArithmeticException" , "/ by zero"); + Interpreter::_throw_ClassCastException_entry = generate_ClassCastException_handler(); + Interpreter::_throw_NullPointerException_entry = generate_exception_handler("java/lang/NullPointerException" , NULL ); + Interpreter::_throw_StackOverflowError_entry = generate_StackOverflowError_handler(); + } + + + +#define method_entry(kind) \ + { CodeletMark cm(_masm, "method entry point (kind = " #kind ")"); \ + Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind); \ + } + + // all non-native method kinds + method_entry(zerolocals) + method_entry(zerolocals_synchronized) + method_entry(empty) + method_entry(accessor) + method_entry(abstract) + method_entry(java_lang_math_sin ) + method_entry(java_lang_math_cos ) + method_entry(java_lang_math_tan ) + method_entry(java_lang_math_abs ) + method_entry(java_lang_math_sqrt ) + method_entry(java_lang_math_log ) + method_entry(java_lang_math_log10) + + // all native method kinds (must be one contiguous block) + Interpreter::_native_entry_begin = Interpreter::code()->code_end(); + method_entry(native) + method_entry(native_synchronized) + Interpreter::_native_entry_end = Interpreter::code()->code_end(); + +#undef method_entry + + // Bytecodes + set_entry_points_for_all_bytes(); + set_safepoints_for_all_bytes(); +} + +//------------------------------------------------------------------------------------------------------------------------ + +address TemplateInterpreterGenerator::generate_error_exit(const char* msg) { + address entry = __ pc(); + __ stop(msg); + return entry; +} + + +//------------------------------------------------------------------------------------------------------------------------ + +void TemplateInterpreterGenerator::set_entry_points_for_all_bytes() { + for (int i = 0; i < DispatchTable::length; i++) { + Bytecodes::Code code = (Bytecodes::Code)i; + if (Bytecodes::is_defined(code)) { + set_entry_points(code); + } else { + set_unimplemented(i); + } + } +} + + +void TemplateInterpreterGenerator::set_safepoints_for_all_bytes() { + for (int i = 0; i < DispatchTable::length; i++) { + Bytecodes::Code code = (Bytecodes::Code)i; + if (Bytecodes::is_defined(code)) Interpreter::_safept_table.set_entry(code, Interpreter::_safept_entry); + } +} + + +void TemplateInterpreterGenerator::set_unimplemented(int i) { + address e = _unimplemented_bytecode; + EntryPoint entry(e, e, e, e, e, e, e, e, e); + Interpreter::_normal_table.set_entry(i, entry); + Interpreter::_wentry_point[i] = _unimplemented_bytecode; +} + + +void TemplateInterpreterGenerator::set_entry_points(Bytecodes::Code code) { + CodeletMark cm(_masm, Bytecodes::name(code), code); + // initialize entry points + assert(_unimplemented_bytecode != NULL, "should have been generated before"); + assert(_illegal_bytecode_sequence != NULL, "should have been generated before"); + address bep = _illegal_bytecode_sequence; + address cep = _illegal_bytecode_sequence; + address sep = _illegal_bytecode_sequence; + address aep = _illegal_bytecode_sequence; + address iep = _illegal_bytecode_sequence; + address lep = _illegal_bytecode_sequence; + address fep = _illegal_bytecode_sequence; + address dep = _illegal_bytecode_sequence; + address vep = _unimplemented_bytecode; + address wep = _unimplemented_bytecode; + // code for short & wide version of bytecode + if (Bytecodes::is_defined(code)) { + Template* t = TemplateTable::template_for(code); + assert(t->is_valid(), "just checking"); + set_short_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, vep); + } + if (Bytecodes::wide_is_defined(code)) { + Template* t = TemplateTable::template_for_wide(code); + assert(t->is_valid(), "just checking"); + set_wide_entry_point(t, wep); + } + // set entry points + EntryPoint entry(bep, cep, sep, aep, iep, lep, fep, dep, vep); + Interpreter::_normal_table.set_entry(code, entry); + Interpreter::_wentry_point[code] = wep; +} + + +void TemplateInterpreterGenerator::set_wide_entry_point(Template* t, address& wep) { + assert(t->is_valid(), "template must exist"); + assert(t->tos_in() == vtos, "only vtos tos_in supported for wide instructions") + wep = __ pc(); generate_and_dispatch(t); +} + + +void TemplateInterpreterGenerator::set_short_entry_points(Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep) { + assert(t->is_valid(), "template must exist"); + switch (t->tos_in()) { + case btos: vep = __ pc(); __ pop(btos); bep = __ pc(); generate_and_dispatch(t); break; + case ctos: vep = __ pc(); __ pop(ctos); sep = __ pc(); generate_and_dispatch(t); break; + case stos: vep = __ pc(); __ pop(stos); sep = __ pc(); generate_and_dispatch(t); break; + case atos: vep = __ pc(); __ pop(atos); aep = __ pc(); generate_and_dispatch(t); break; + case itos: vep = __ pc(); __ pop(itos); iep = __ pc(); generate_and_dispatch(t); break; + case ltos: vep = __ pc(); __ pop(ltos); lep = __ pc(); generate_and_dispatch(t); break; + case ftos: vep = __ pc(); __ pop(ftos); fep = __ pc(); generate_and_dispatch(t); break; + case dtos: vep = __ pc(); __ pop(dtos); dep = __ pc(); generate_and_dispatch(t); break; + case vtos: set_vtos_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, vep); break; + default : ShouldNotReachHere(); break; + } +} + + +//------------------------------------------------------------------------------------------------------------------------ + +void TemplateInterpreterGenerator::generate_and_dispatch(Template* t, TosState tos_out) { + if (PrintBytecodeHistogram) histogram_bytecode(t); +#ifndef PRODUCT + // debugging code + if (CountBytecodes || TraceBytecodes || StopInterpreterAt > 0) count_bytecode(); + if (PrintBytecodePairHistogram) histogram_bytecode_pair(t); + if (TraceBytecodes) trace_bytecode(t); + if (StopInterpreterAt > 0) stop_interpreter_at(); + __ verify_FPU(1, t->tos_in()); +#endif // !PRODUCT + int step; + if (!t->does_dispatch()) { + step = t->is_wide() ? Bytecodes::wide_length_for(t->bytecode()) : Bytecodes::length_for(t->bytecode()); + if (tos_out == ilgl) tos_out = t->tos_out(); + // compute bytecode size + assert(step > 0, "just checkin'"); + // setup stuff for dispatching next bytecode + if (ProfileInterpreter && VerifyDataPointer + && methodDataOopDesc::bytecode_has_profile(t->bytecode())) { + __ verify_method_data_pointer(); + } + __ dispatch_prolog(tos_out, step); + } + // generate template + t->generate(_masm); + // advance + if (t->does_dispatch()) { +#ifdef ASSERT + // make sure execution doesn't go beyond this point if code is broken + __ should_not_reach_here(); +#endif // ASSERT + } else { + // dispatch to next bytecode + __ dispatch_epilog(tos_out, step); + } +} + +//------------------------------------------------------------------------------------------------------------------------ +// Entry points + +address TemplateInterpreter::return_entry(TosState state, int length) { + guarantee(0 <= length && length < Interpreter::number_of_return_entries, "illegal length"); + return _return_entry[length].entry(state); +} + + +address TemplateInterpreter::deopt_entry(TosState state, int length) { + guarantee(0 <= length && length < Interpreter::number_of_deopt_entries, "illegal length"); + return _deopt_entry[length].entry(state); +} + +//------------------------------------------------------------------------------------------------------------------------ +// Suport for invokes + +int TemplateInterpreter::TosState_as_index(TosState state) { + assert( state < number_of_states , "Invalid state in TosState_as_index"); + assert(0 <= (int)state && (int)state < TemplateInterpreter::number_of_return_addrs, "index out of bounds"); + return (int)state; +} + + +//------------------------------------------------------------------------------------------------------------------------ +// Safepoint suppport + +static inline void copy_table(address* from, address* to, int size) { + // Copy non-overlapping tables. The copy has to occur word wise for MT safety. + while (size-- > 0) *to++ = *from++; +} + +void TemplateInterpreter::notice_safepoints() { + if (!_notice_safepoints) { + // switch to safepoint dispatch table + _notice_safepoints = true; + copy_table((address*)&_safept_table, (address*)&_active_table, sizeof(_active_table) / sizeof(address)); + } +} + +// switch from the dispatch table which notices safepoints back to the +// normal dispatch table. So that we can notice single stepping points, +// keep the safepoint dispatch table if we are single stepping in JVMTI. +// Note that the should_post_single_step test is exactly as fast as the +// JvmtiExport::_enabled test and covers both cases. +void TemplateInterpreter::ignore_safepoints() { + if (_notice_safepoints) { + if (!JvmtiExport::should_post_single_step()) { + // switch to normal dispatch table + _notice_safepoints = false; + copy_table((address*)&_normal_table, (address*)&_active_table, sizeof(_active_table) / sizeof(address)); + } + } +} + +// If deoptimization happens, this method returns the point where to continue in +// interpreter. For calls (invokexxxx, newxxxx) the continuation is at next +// bci and the top of stack is in eax/edx/FPU tos. +// For putfield/getfield, put/getstatic, the continuation is at the same +// bci and the TOS is on stack. + +// Note: deopt_entry(type, 0) means reexecute bytecode +// deopt_entry(type, length) means continue at next bytecode + +address TemplateInterpreter::continuation_for(methodOop method, address bcp, int callee_parameters, bool is_top_frame, bool& use_next_mdp) { + assert(method->contains(bcp), "just checkin'"); + Bytecodes::Code code = Bytecodes::java_code_at(bcp); + if (code == Bytecodes::_return) { + // This is used for deopt during registration of finalizers + // during Object.. We simply need to resume execution at + // the standard return vtos bytecode to pop the frame normally. + // reexecuting the real bytecode would cause double registration + // of the finalizable object. + assert(is_top_frame, "must be on top"); + return _normal_table.entry(Bytecodes::_return).entry(vtos); + } else { + return AbstractInterpreter::continuation_for(method, bcp, callee_parameters, is_top_frame, use_next_mdp); + } +} + +#endif // !CC_INTERP diff --git a/hotspot/src/share/vm/interpreter/templateInterpreter.hpp b/hotspot/src/share/vm/interpreter/templateInterpreter.hpp new file mode 100644 index 00000000000..f6c89f9bd18 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/templateInterpreter.hpp @@ -0,0 +1,177 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file contains the platform-independant parts +// of the template interpreter and the template interpreter generator. + +#ifndef CC_INTERP + +//------------------------------------------------------------------------------------------------------------------------ +// A little wrapper class to group tosca-specific entry points into a unit. +// (tosca = Top-Of-Stack CAche) + +class EntryPoint VALUE_OBJ_CLASS_SPEC { + private: + address _entry[number_of_states]; + + public: + // Construction + EntryPoint(); + EntryPoint(address bentry, address centry, address sentry, address aentry, address ientry, address lentry, address fentry, address dentry, address ventry); + + // Attributes + address entry(TosState state) const; // return target address for a given tosca state + void set_entry(TosState state, address entry); // set target address for a given tosca state + void print(); + + // Comparison + bool operator == (const EntryPoint& y); // for debugging only +}; + + +//------------------------------------------------------------------------------------------------------------------------ +// A little wrapper class to group tosca-specific dispatch tables into a unit. + +class DispatchTable VALUE_OBJ_CLASS_SPEC { + public: + enum { length = 1 << BitsPerByte }; // an entry point for each byte value (also for undefined bytecodes) + + private: + address _table[number_of_states][length]; // dispatch tables, indexed by tosca and bytecode + + public: + // Attributes + EntryPoint entry(int i) const; // return entry point for a given bytecode i + void set_entry(int i, EntryPoint& entry); // set entry point for a given bytecode i + address* table_for(TosState state) { return _table[state]; } + address* table_for() { return table_for((TosState)0); } + int distance_from(address *table) { return table - table_for(); } + int distance_from(TosState state) { return distance_from(table_for(state)); } + + // Comparison + bool operator == (DispatchTable& y); // for debugging only +}; + +class TemplateInterpreter: public AbstractInterpreter { + friend class VMStructs; + friend class InterpreterMacroAssembler; + friend class TemplateInterpreterGenerator; + friend class TemplateTable; + // friend class Interpreter; + public: + + enum MoreConstants { + number_of_return_entries = 9, // number of return entry points + number_of_deopt_entries = 9, // number of deoptimization entry points + number_of_return_addrs = 9 // number of return addresses + }; + + protected: + + static address _throw_ArrayIndexOutOfBoundsException_entry; + static address _throw_ArrayStoreException_entry; + static address _throw_ArithmeticException_entry; + static address _throw_ClassCastException_entry; + static address _throw_NullPointerException_entry; + static address _throw_exception_entry; + + static address _throw_StackOverflowError_entry; + + static address _remove_activation_entry; // continuation address if an exception is not handled by current frame +#ifdef HOTSWAP + static address _remove_activation_preserving_args_entry; // continuation address when current frame is being popped +#endif // HOTSWAP + +#ifndef PRODUCT + static EntryPoint _trace_code; +#endif // !PRODUCT + static EntryPoint _return_entry[number_of_return_entries]; // entry points to return to from a call + static EntryPoint _earlyret_entry; // entry point to return early from a call + static EntryPoint _deopt_entry[number_of_deopt_entries]; // entry points to return to from a deoptimization + static EntryPoint _continuation_entry; + static EntryPoint _safept_entry; + + static address _return_3_addrs_by_index[number_of_return_addrs]; // for invokevirtual return entries + static address _return_5_addrs_by_index[number_of_return_addrs]; // for invokeinterface return entries + + static DispatchTable _active_table; // the active dispatch table (used by the interpreter for dispatch) + static DispatchTable _normal_table; // the normal dispatch table (used to set the active table in normal mode) + static DispatchTable _safept_table; // the safepoint dispatch table (used to set the active table for safepoints) + static address _wentry_point[DispatchTable::length]; // wide instructions only (vtos tosca always) + + + public: + // Initialization/debugging + static void initialize(); + // this only returns whether a pc is within generated code for the interpreter. + static bool contains(address pc) { return _code != NULL && _code->contains(pc); } + + public: + + static address remove_activation_early_entry(TosState state) { return _earlyret_entry.entry(state); } +#ifdef HOTSWAP + static address remove_activation_preserving_args_entry() { return _remove_activation_preserving_args_entry; } +#endif // HOTSWAP + + static address remove_activation_entry() { return _remove_activation_entry; } + static address throw_exception_entry() { return _throw_exception_entry; } + static address throw_ArithmeticException_entry() { return _throw_ArithmeticException_entry; } + static address throw_NullPointerException_entry() { return _throw_NullPointerException_entry; } + static address throw_StackOverflowError_entry() { return _throw_StackOverflowError_entry; } + + // Code generation +#ifndef PRODUCT + static address trace_code (TosState state) { return _trace_code.entry(state); } +#endif // !PRODUCT + static address continuation (TosState state) { return _continuation_entry.entry(state); } + static address* dispatch_table(TosState state) { return _active_table.table_for(state); } + static address* dispatch_table() { return _active_table.table_for(); } + static int distance_from_dispatch_table(TosState state){ return _active_table.distance_from(state); } + static address* normal_table(TosState state) { return _normal_table.table_for(state); } + static address* normal_table() { return _normal_table.table_for(); } + + // Support for invokes + static address* return_3_addrs_by_index_table() { return _return_3_addrs_by_index; } + static address* return_5_addrs_by_index_table() { return _return_5_addrs_by_index; } + static int TosState_as_index(TosState state); // computes index into return_3_entry_by_index table + + static address return_entry (TosState state, int length); + static address deopt_entry (TosState state, int length); + + // Safepoint support + static void notice_safepoints(); // stops the thread when reaching a safepoint + static void ignore_safepoints(); // ignores safepoints + + // Deoptimization support + static address continuation_for(methodOop method, + address bcp, + int callee_parameters, + bool is_top_frame, + bool& use_next_mdp); + +#include "incls/_templateInterpreter_pd.hpp.incl" + +}; + +#endif // !CC_INTERP diff --git a/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp b/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp new file mode 100644 index 00000000000..452e1c534ec --- /dev/null +++ b/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file contains the platform-independant parts +// of the template interpreter generator. + +#ifndef CC_INTERP + +class TemplateInterpreterGenerator: public AbstractInterpreterGenerator { + protected: + + // entry points for shared code sequence + address _unimplemented_bytecode; + address _illegal_bytecode_sequence; + + // shared code sequences + // Converter for native abi result to tosca result + address generate_result_handler_for(BasicType type); + address generate_slow_signature_handler(); + address generate_error_exit(const char* msg); + address generate_StackOverflowError_handler(); + address generate_exception_handler(const char* name, const char* message) { + return generate_exception_handler_common(name, message, false); + } + address generate_klass_exception_handler(const char* name) { + return generate_exception_handler_common(name, NULL, true); + } + address generate_exception_handler_common(const char* name, const char* message, bool pass_oop); + address generate_ClassCastException_handler(); + address generate_ArrayIndexOutOfBounds_handler(const char* name); + address generate_continuation_for(TosState state); + address generate_return_entry_for(TosState state, int step); + address generate_earlyret_entry_for(TosState state); + address generate_deopt_entry_for(TosState state, int step); + address generate_safept_entry_for(TosState state, address runtime_entry); + void generate_throw_exception(); + + // entry point generator +// address generate_method_entry(AbstractInterpreter::MethodKind kind); + + // Instruction generation + void generate_and_dispatch (Template* t, TosState tos_out = ilgl); + void set_vtos_entry_points (Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep); + void set_short_entry_points(Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep); + void set_wide_entry_point (Template* t, address& wep); + + void set_entry_points(Bytecodes::Code code); + void set_unimplemented(int i); + void set_entry_points_for_all_bytes(); + void set_safepoints_for_all_bytes(); + + // Helpers for generate_and_dispatch + address generate_trace_code(TosState state) PRODUCT_RETURN0; + void count_bytecode() PRODUCT_RETURN; + void histogram_bytecode(Template* t) PRODUCT_RETURN; + void histogram_bytecode_pair(Template* t) PRODUCT_RETURN; + void trace_bytecode(Template* t) PRODUCT_RETURN; + void stop_interpreter_at() PRODUCT_RETURN; + + void generate_all(); + + public: + TemplateInterpreterGenerator(StubQueue* _code); + + #include "incls/_templateInterpreterGenerator_pd.hpp.incl" + +}; + +#endif // !CC_INTERP diff --git a/hotspot/src/share/vm/interpreter/templateTable.cpp b/hotspot/src/share/vm/interpreter/templateTable.cpp new file mode 100644 index 00000000000..c302af297e1 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/templateTable.cpp @@ -0,0 +1,538 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_templateTable.cpp.incl" + + +#ifdef CC_INTERP + +void templateTable_init() { +} + +#else + +//---------------------------------------------------------------------------------------------------- +// Implementation of Template + + +void Template::initialize(int flags, TosState tos_in, TosState tos_out, generator gen, int arg) { + _flags = flags; + _tos_in = tos_in; + _tos_out = tos_out; + _gen = gen; + _arg = arg; +} + + +Bytecodes::Code Template::bytecode() const { + int i = this - TemplateTable::_template_table; + if (i < 0 || i >= Bytecodes::number_of_codes) i = this - TemplateTable::_template_table_wide; + return Bytecodes::cast(i); +} + + +void Template::generate(InterpreterMacroAssembler* masm) { + // parameter passing + TemplateTable::_desc = this; + TemplateTable::_masm = masm; + // code generation + _gen(_arg); + masm->flush(); +} + + +//---------------------------------------------------------------------------------------------------- +// Implementation of TemplateTable: Platform-independent helper routines + +void TemplateTable::call_VM(Register oop_result, address entry_point) { + assert(_desc->calls_vm(), "inconsistent calls_vm information"); + _masm->call_VM(oop_result, entry_point); +} + + +void TemplateTable::call_VM(Register oop_result, address entry_point, Register arg_1) { + assert(_desc->calls_vm(), "inconsistent calls_vm information"); + _masm->call_VM(oop_result, entry_point, arg_1); +} + + +void TemplateTable::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2) { + assert(_desc->calls_vm(), "inconsistent calls_vm information"); + _masm->call_VM(oop_result, entry_point, arg_1, arg_2); +} + + +void TemplateTable::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3) { + assert(_desc->calls_vm(), "inconsistent calls_vm information"); + _masm->call_VM(oop_result, entry_point, arg_1, arg_2, arg_3); +} + + +void TemplateTable::call_VM(Register oop_result, Register last_java_sp, address entry_point) { + assert(_desc->calls_vm(), "inconsistent calls_vm information"); + _masm->call_VM(oop_result, last_java_sp, entry_point); +} + + +void TemplateTable::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1) { + assert(_desc->calls_vm(), "inconsistent calls_vm information"); + _masm->call_VM(oop_result, last_java_sp, entry_point, arg_1); +} + + +void TemplateTable::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2) { + assert(_desc->calls_vm(), "inconsistent calls_vm information"); + _masm->call_VM(oop_result, last_java_sp, entry_point, arg_1, arg_2); +} + + +void TemplateTable::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, Register arg_3) { + assert(_desc->calls_vm(), "inconsistent calls_vm information"); + _masm->call_VM(oop_result, last_java_sp, entry_point, arg_1, arg_2, arg_3); +} + + +//---------------------------------------------------------------------------------------------------- +// Implementation of TemplateTable: Platform-independent bytecodes + +void TemplateTable::float_cmp(int unordered_result) { + transition(ftos, itos); + float_cmp(true, unordered_result); +} + + +void TemplateTable::double_cmp(int unordered_result) { + transition(dtos, itos); + float_cmp(false, unordered_result); +} + + +void TemplateTable::_goto() { + transition(vtos, vtos); + branch(false, false); +} + + +void TemplateTable::goto_w() { + transition(vtos, vtos); + branch(false, true); +} + + +void TemplateTable::jsr_w() { + transition(vtos, vtos); // result is not an oop, so do not transition to atos + branch(true, true); +} + + +void TemplateTable::jsr() { + transition(vtos, vtos); // result is not an oop, so do not transition to atos + branch(true, false); +} + + + +//---------------------------------------------------------------------------------------------------- +// Implementation of TemplateTable: Debugging + +void TemplateTable::transition(TosState tos_in, TosState tos_out) { + assert(_desc->tos_in() == tos_in , "inconsistent tos_in information"); + assert(_desc->tos_out() == tos_out, "inconsistent tos_out information"); +} + + +//---------------------------------------------------------------------------------------------------- +// Implementation of TemplateTable: Initialization + +bool TemplateTable::_is_initialized = false; +Template TemplateTable::_template_table [Bytecodes::number_of_codes]; +Template TemplateTable::_template_table_wide[Bytecodes::number_of_codes]; + +Template* TemplateTable::_desc; +InterpreterMacroAssembler* TemplateTable::_masm; + + +void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(), char filler) { + assert(filler == ' ', "just checkin'"); + def(code, flags, in, out, (Template::generator)gen, 0); +} + + +void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(int arg), int arg) { + // should factor out these constants + const int ubcp = 1 << Template::uses_bcp_bit; + const int disp = 1 << Template::does_dispatch_bit; + const int clvm = 1 << Template::calls_vm_bit; + const int iswd = 1 << Template::wide_bit; + // determine which table to use + bool is_wide = (flags & iswd) != 0; + // make sure that wide instructions have a vtos entry point + // (since they are executed extremely rarely, it doesn't pay out to have an + // extra set of 5 dispatch tables for the wide instructions - for simplicity + // they all go with one table) + assert(in == vtos || !is_wide, "wide instructions have vtos entry point only"); + Template* t = is_wide ? template_for_wide(code) : template_for(code); + // setup entry + t->initialize(flags, in, out, gen, arg); + assert(t->bytecode() == code, "just checkin'"); +} + + +void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(Operation op), Operation op) { + def(code, flags, in, out, (Template::generator)gen, (int)op); +} + + +void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(bool arg ), bool arg) { + def(code, flags, in, out, (Template::generator)gen, (int)arg); +} + + +void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(TosState tos), TosState tos) { + def(code, flags, in, out, (Template::generator)gen, (int)tos); +} + + +void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(Condition cc), Condition cc) { + def(code, flags, in, out, (Template::generator)gen, (int)cc); +} + +#if defined(TEMPLATE_TABLE_BUG) +// +// It appears that gcc (version 2.91) generates bad code for the template +// table init if this macro is not defined. My symptom was an assertion +// assert(Universe::heap()->is_in(obj), "sanity check") in handles.cpp line 24. +// when called from interpreterRuntime.resolve_invoke(). +// + #define iload TemplateTable::iload + #define lload TemplateTable::lload + #define fload TemplateTable::fload + #define dload TemplateTable::dload + #define aload TemplateTable::aload + #define istore TemplateTable::istore + #define lstore TemplateTable::lstore + #define fstore TemplateTable::fstore + #define dstore TemplateTable::dstore + #define astore TemplateTable::astore +#endif // TEMPLATE_TABLE_BUG + +void TemplateTable::initialize() { + if (_is_initialized) return; + + // Initialize table + TraceTime timer("TemplateTable initialization", TraceStartupTime); + + // For better readability + const char _ = ' '; + const int ____ = 0; + const int ubcp = 1 << Template::uses_bcp_bit; + const int disp = 1 << Template::does_dispatch_bit; + const int clvm = 1 << Template::calls_vm_bit; + const int iswd = 1 << Template::wide_bit; + // interpr. templates + // Java spec bytecodes ubcp|disp|clvm|iswd in out generator argument + def(Bytecodes::_nop , ____|____|____|____, vtos, vtos, nop , _ ); + def(Bytecodes::_aconst_null , ____|____|____|____, vtos, atos, aconst_null , _ ); + def(Bytecodes::_iconst_m1 , ____|____|____|____, vtos, itos, iconst , -1 ); + def(Bytecodes::_iconst_0 , ____|____|____|____, vtos, itos, iconst , 0 ); + def(Bytecodes::_iconst_1 , ____|____|____|____, vtos, itos, iconst , 1 ); + def(Bytecodes::_iconst_2 , ____|____|____|____, vtos, itos, iconst , 2 ); + def(Bytecodes::_iconst_3 , ____|____|____|____, vtos, itos, iconst , 3 ); + def(Bytecodes::_iconst_4 , ____|____|____|____, vtos, itos, iconst , 4 ); + def(Bytecodes::_iconst_5 , ____|____|____|____, vtos, itos, iconst , 5 ); + def(Bytecodes::_lconst_0 , ____|____|____|____, vtos, ltos, lconst , 0 ); + def(Bytecodes::_lconst_1 , ____|____|____|____, vtos, ltos, lconst , 1 ); + def(Bytecodes::_fconst_0 , ____|____|____|____, vtos, ftos, fconst , 0 ); + def(Bytecodes::_fconst_1 , ____|____|____|____, vtos, ftos, fconst , 1 ); + def(Bytecodes::_fconst_2 , ____|____|____|____, vtos, ftos, fconst , 2 ); + def(Bytecodes::_dconst_0 , ____|____|____|____, vtos, dtos, dconst , 0 ); + def(Bytecodes::_dconst_1 , ____|____|____|____, vtos, dtos, dconst , 1 ); + def(Bytecodes::_bipush , ubcp|____|____|____, vtos, itos, bipush , _ ); + def(Bytecodes::_sipush , ubcp|____|____|____, vtos, itos, sipush , _ ); + def(Bytecodes::_ldc , ubcp|____|clvm|____, vtos, vtos, ldc , false ); + def(Bytecodes::_ldc_w , ubcp|____|clvm|____, vtos, vtos, ldc , true ); + def(Bytecodes::_ldc2_w , ubcp|____|____|____, vtos, vtos, ldc2_w , _ ); + def(Bytecodes::_iload , ubcp|____|clvm|____, vtos, itos, iload , _ ); + def(Bytecodes::_lload , ubcp|____|____|____, vtos, ltos, lload , _ ); + def(Bytecodes::_fload , ubcp|____|____|____, vtos, ftos, fload , _ ); + def(Bytecodes::_dload , ubcp|____|____|____, vtos, dtos, dload , _ ); + def(Bytecodes::_aload , ubcp|____|clvm|____, vtos, atos, aload , _ ); + def(Bytecodes::_iload_0 , ____|____|____|____, vtos, itos, iload , 0 ); + def(Bytecodes::_iload_1 , ____|____|____|____, vtos, itos, iload , 1 ); + def(Bytecodes::_iload_2 , ____|____|____|____, vtos, itos, iload , 2 ); + def(Bytecodes::_iload_3 , ____|____|____|____, vtos, itos, iload , 3 ); + def(Bytecodes::_lload_0 , ____|____|____|____, vtos, ltos, lload , 0 ); + def(Bytecodes::_lload_1 , ____|____|____|____, vtos, ltos, lload , 1 ); + def(Bytecodes::_lload_2 , ____|____|____|____, vtos, ltos, lload , 2 ); + def(Bytecodes::_lload_3 , ____|____|____|____, vtos, ltos, lload , 3 ); + def(Bytecodes::_fload_0 , ____|____|____|____, vtos, ftos, fload , 0 ); + def(Bytecodes::_fload_1 , ____|____|____|____, vtos, ftos, fload , 1 ); + def(Bytecodes::_fload_2 , ____|____|____|____, vtos, ftos, fload , 2 ); + def(Bytecodes::_fload_3 , ____|____|____|____, vtos, ftos, fload , 3 ); + def(Bytecodes::_dload_0 , ____|____|____|____, vtos, dtos, dload , 0 ); + def(Bytecodes::_dload_1 , ____|____|____|____, vtos, dtos, dload , 1 ); + def(Bytecodes::_dload_2 , ____|____|____|____, vtos, dtos, dload , 2 ); + def(Bytecodes::_dload_3 , ____|____|____|____, vtos, dtos, dload , 3 ); + def(Bytecodes::_aload_0 , ubcp|____|clvm|____, vtos, atos, aload_0 , _ ); + def(Bytecodes::_aload_1 , ____|____|____|____, vtos, atos, aload , 1 ); + def(Bytecodes::_aload_2 , ____|____|____|____, vtos, atos, aload , 2 ); + def(Bytecodes::_aload_3 , ____|____|____|____, vtos, atos, aload , 3 ); + def(Bytecodes::_iaload , ____|____|____|____, itos, itos, iaload , _ ); + def(Bytecodes::_laload , ____|____|____|____, itos, ltos, laload , _ ); + def(Bytecodes::_faload , ____|____|____|____, itos, ftos, faload , _ ); + def(Bytecodes::_daload , ____|____|____|____, itos, dtos, daload , _ ); + def(Bytecodes::_aaload , ____|____|____|____, itos, atos, aaload , _ ); + def(Bytecodes::_baload , ____|____|____|____, itos, itos, baload , _ ); + def(Bytecodes::_caload , ____|____|____|____, itos, itos, caload , _ ); + def(Bytecodes::_saload , ____|____|____|____, itos, itos, saload , _ ); + def(Bytecodes::_istore , ubcp|____|clvm|____, itos, vtos, istore , _ ); + def(Bytecodes::_lstore , ubcp|____|____|____, ltos, vtos, lstore , _ ); + def(Bytecodes::_fstore , ubcp|____|____|____, ftos, vtos, fstore , _ ); + def(Bytecodes::_dstore , ubcp|____|____|____, dtos, vtos, dstore , _ ); + def(Bytecodes::_astore , ubcp|____|clvm|____, vtos, vtos, astore , _ ); + def(Bytecodes::_istore_0 , ____|____|____|____, itos, vtos, istore , 0 ); + def(Bytecodes::_istore_1 , ____|____|____|____, itos, vtos, istore , 1 ); + def(Bytecodes::_istore_2 , ____|____|____|____, itos, vtos, istore , 2 ); + def(Bytecodes::_istore_3 , ____|____|____|____, itos, vtos, istore , 3 ); + def(Bytecodes::_lstore_0 , ____|____|____|____, ltos, vtos, lstore , 0 ); + def(Bytecodes::_lstore_1 , ____|____|____|____, ltos, vtos, lstore , 1 ); + def(Bytecodes::_lstore_2 , ____|____|____|____, ltos, vtos, lstore , 2 ); + def(Bytecodes::_lstore_3 , ____|____|____|____, ltos, vtos, lstore , 3 ); + def(Bytecodes::_fstore_0 , ____|____|____|____, ftos, vtos, fstore , 0 ); + def(Bytecodes::_fstore_1 , ____|____|____|____, ftos, vtos, fstore , 1 ); + def(Bytecodes::_fstore_2 , ____|____|____|____, ftos, vtos, fstore , 2 ); + def(Bytecodes::_fstore_3 , ____|____|____|____, ftos, vtos, fstore , 3 ); + def(Bytecodes::_dstore_0 , ____|____|____|____, dtos, vtos, dstore , 0 ); + def(Bytecodes::_dstore_1 , ____|____|____|____, dtos, vtos, dstore , 1 ); + def(Bytecodes::_dstore_2 , ____|____|____|____, dtos, vtos, dstore , 2 ); + def(Bytecodes::_dstore_3 , ____|____|____|____, dtos, vtos, dstore , 3 ); + def(Bytecodes::_astore_0 , ____|____|____|____, vtos, vtos, astore , 0 ); + def(Bytecodes::_astore_1 , ____|____|____|____, vtos, vtos, astore , 1 ); + def(Bytecodes::_astore_2 , ____|____|____|____, vtos, vtos, astore , 2 ); + def(Bytecodes::_astore_3 , ____|____|____|____, vtos, vtos, astore , 3 ); + def(Bytecodes::_iastore , ____|____|____|____, itos, vtos, iastore , _ ); + def(Bytecodes::_lastore , ____|____|____|____, ltos, vtos, lastore , _ ); + def(Bytecodes::_fastore , ____|____|____|____, ftos, vtos, fastore , _ ); + def(Bytecodes::_dastore , ____|____|____|____, dtos, vtos, dastore , _ ); + def(Bytecodes::_aastore , ____|____|clvm|____, vtos, vtos, aastore , _ ); + def(Bytecodes::_bastore , ____|____|____|____, itos, vtos, bastore , _ ); + def(Bytecodes::_castore , ____|____|____|____, itos, vtos, castore , _ ); + def(Bytecodes::_sastore , ____|____|____|____, itos, vtos, sastore , _ ); + def(Bytecodes::_pop , ____|____|____|____, vtos, vtos, pop , _ ); + def(Bytecodes::_pop2 , ____|____|____|____, vtos, vtos, pop2 , _ ); + def(Bytecodes::_dup , ____|____|____|____, vtos, vtos, dup , _ ); + def(Bytecodes::_dup_x1 , ____|____|____|____, vtos, vtos, dup_x1 , _ ); + def(Bytecodes::_dup_x2 , ____|____|____|____, vtos, vtos, dup_x2 , _ ); + def(Bytecodes::_dup2 , ____|____|____|____, vtos, vtos, dup2 , _ ); + def(Bytecodes::_dup2_x1 , ____|____|____|____, vtos, vtos, dup2_x1 , _ ); + def(Bytecodes::_dup2_x2 , ____|____|____|____, vtos, vtos, dup2_x2 , _ ); + def(Bytecodes::_swap , ____|____|____|____, vtos, vtos, swap , _ ); + def(Bytecodes::_iadd , ____|____|____|____, itos, itos, iop2 , add ); + def(Bytecodes::_ladd , ____|____|____|____, ltos, ltos, lop2 , add ); + def(Bytecodes::_fadd , ____|____|____|____, ftos, ftos, fop2 , add ); + def(Bytecodes::_dadd , ____|____|____|____, dtos, dtos, dop2 , add ); + def(Bytecodes::_isub , ____|____|____|____, itos, itos, iop2 , sub ); + def(Bytecodes::_lsub , ____|____|____|____, ltos, ltos, lop2 , sub ); + def(Bytecodes::_fsub , ____|____|____|____, ftos, ftos, fop2 , sub ); + def(Bytecodes::_dsub , ____|____|____|____, dtos, dtos, dop2 , sub ); + def(Bytecodes::_imul , ____|____|____|____, itos, itos, iop2 , mul ); + def(Bytecodes::_lmul , ____|____|____|____, ltos, ltos, lmul , _ ); + def(Bytecodes::_fmul , ____|____|____|____, ftos, ftos, fop2 , mul ); + def(Bytecodes::_dmul , ____|____|____|____, dtos, dtos, dop2 , mul ); + def(Bytecodes::_idiv , ____|____|____|____, itos, itos, idiv , _ ); + def(Bytecodes::_ldiv , ____|____|____|____, ltos, ltos, ldiv , _ ); + def(Bytecodes::_fdiv , ____|____|____|____, ftos, ftos, fop2 , div ); + def(Bytecodes::_ddiv , ____|____|____|____, dtos, dtos, dop2 , div ); + def(Bytecodes::_irem , ____|____|____|____, itos, itos, irem , _ ); + def(Bytecodes::_lrem , ____|____|____|____, ltos, ltos, lrem , _ ); + def(Bytecodes::_frem , ____|____|____|____, ftos, ftos, fop2 , rem ); + def(Bytecodes::_drem , ____|____|____|____, dtos, dtos, dop2 , rem ); + def(Bytecodes::_ineg , ____|____|____|____, itos, itos, ineg , _ ); + def(Bytecodes::_lneg , ____|____|____|____, ltos, ltos, lneg , _ ); + def(Bytecodes::_fneg , ____|____|____|____, ftos, ftos, fneg , _ ); + def(Bytecodes::_dneg , ____|____|____|____, dtos, dtos, dneg , _ ); + def(Bytecodes::_ishl , ____|____|____|____, itos, itos, iop2 , shl ); + def(Bytecodes::_lshl , ____|____|____|____, itos, ltos, lshl , _ ); + def(Bytecodes::_ishr , ____|____|____|____, itos, itos, iop2 , shr ); + def(Bytecodes::_lshr , ____|____|____|____, itos, ltos, lshr , _ ); + def(Bytecodes::_iushr , ____|____|____|____, itos, itos, iop2 , ushr ); + def(Bytecodes::_lushr , ____|____|____|____, itos, ltos, lushr , _ ); + def(Bytecodes::_iand , ____|____|____|____, itos, itos, iop2 , _and ); + def(Bytecodes::_land , ____|____|____|____, ltos, ltos, lop2 , _and ); + def(Bytecodes::_ior , ____|____|____|____, itos, itos, iop2 , _or ); + def(Bytecodes::_lor , ____|____|____|____, ltos, ltos, lop2 , _or ); + def(Bytecodes::_ixor , ____|____|____|____, itos, itos, iop2 , _xor ); + def(Bytecodes::_lxor , ____|____|____|____, ltos, ltos, lop2 , _xor ); + def(Bytecodes::_iinc , ubcp|____|clvm|____, vtos, vtos, iinc , _ ); + def(Bytecodes::_i2l , ____|____|____|____, itos, ltos, convert , _ ); + def(Bytecodes::_i2f , ____|____|____|____, itos, ftos, convert , _ ); + def(Bytecodes::_i2d , ____|____|____|____, itos, dtos, convert , _ ); + def(Bytecodes::_l2i , ____|____|____|____, ltos, itos, convert , _ ); + def(Bytecodes::_l2f , ____|____|____|____, ltos, ftos, convert , _ ); + def(Bytecodes::_l2d , ____|____|____|____, ltos, dtos, convert , _ ); + def(Bytecodes::_f2i , ____|____|____|____, ftos, itos, convert , _ ); + def(Bytecodes::_f2l , ____|____|____|____, ftos, ltos, convert , _ ); + def(Bytecodes::_f2d , ____|____|____|____, ftos, dtos, convert , _ ); + def(Bytecodes::_d2i , ____|____|____|____, dtos, itos, convert , _ ); + def(Bytecodes::_d2l , ____|____|____|____, dtos, ltos, convert , _ ); + def(Bytecodes::_d2f , ____|____|____|____, dtos, ftos, convert , _ ); + def(Bytecodes::_i2b , ____|____|____|____, itos, itos, convert , _ ); + def(Bytecodes::_i2c , ____|____|____|____, itos, itos, convert , _ ); + def(Bytecodes::_i2s , ____|____|____|____, itos, itos, convert , _ ); + def(Bytecodes::_lcmp , ____|____|____|____, ltos, itos, lcmp , _ ); + def(Bytecodes::_fcmpl , ____|____|____|____, ftos, itos, float_cmp , -1 ); + def(Bytecodes::_fcmpg , ____|____|____|____, ftos, itos, float_cmp , 1 ); + def(Bytecodes::_dcmpl , ____|____|____|____, dtos, itos, double_cmp , -1 ); + def(Bytecodes::_dcmpg , ____|____|____|____, dtos, itos, double_cmp , 1 ); + def(Bytecodes::_ifeq , ubcp|____|clvm|____, itos, vtos, if_0cmp , equal ); + def(Bytecodes::_ifne , ubcp|____|clvm|____, itos, vtos, if_0cmp , not_equal ); + def(Bytecodes::_iflt , ubcp|____|clvm|____, itos, vtos, if_0cmp , less ); + def(Bytecodes::_ifge , ubcp|____|clvm|____, itos, vtos, if_0cmp , greater_equal); + def(Bytecodes::_ifgt , ubcp|____|clvm|____, itos, vtos, if_0cmp , greater ); + def(Bytecodes::_ifle , ubcp|____|clvm|____, itos, vtos, if_0cmp , less_equal ); + def(Bytecodes::_if_icmpeq , ubcp|____|clvm|____, itos, vtos, if_icmp , equal ); + def(Bytecodes::_if_icmpne , ubcp|____|clvm|____, itos, vtos, if_icmp , not_equal ); + def(Bytecodes::_if_icmplt , ubcp|____|clvm|____, itos, vtos, if_icmp , less ); + def(Bytecodes::_if_icmpge , ubcp|____|clvm|____, itos, vtos, if_icmp , greater_equal); + def(Bytecodes::_if_icmpgt , ubcp|____|clvm|____, itos, vtos, if_icmp , greater ); + def(Bytecodes::_if_icmple , ubcp|____|clvm|____, itos, vtos, if_icmp , less_equal ); + def(Bytecodes::_if_acmpeq , ubcp|____|clvm|____, atos, vtos, if_acmp , equal ); + def(Bytecodes::_if_acmpne , ubcp|____|clvm|____, atos, vtos, if_acmp , not_equal ); + def(Bytecodes::_goto , ubcp|disp|clvm|____, vtos, vtos, _goto , _ ); + def(Bytecodes::_jsr , ubcp|disp|____|____, vtos, vtos, jsr , _ ); // result is not an oop, so do not transition to atos + def(Bytecodes::_ret , ubcp|disp|____|____, vtos, vtos, ret , _ ); + def(Bytecodes::_tableswitch , ubcp|disp|____|____, itos, vtos, tableswitch , _ ); + def(Bytecodes::_lookupswitch , ubcp|disp|____|____, itos, itos, lookupswitch , _ ); + def(Bytecodes::_ireturn , ____|disp|clvm|____, itos, itos, _return , itos ); + def(Bytecodes::_lreturn , ____|disp|clvm|____, ltos, ltos, _return , ltos ); + def(Bytecodes::_freturn , ____|disp|clvm|____, ftos, ftos, _return , ftos ); + def(Bytecodes::_dreturn , ____|disp|clvm|____, dtos, dtos, _return , dtos ); + def(Bytecodes::_areturn , ____|disp|clvm|____, atos, atos, _return , atos ); + def(Bytecodes::_return , ____|disp|clvm|____, vtos, vtos, _return , vtos ); + def(Bytecodes::_getstatic , ubcp|____|clvm|____, vtos, vtos, getstatic , 1 ); + def(Bytecodes::_putstatic , ubcp|____|clvm|____, vtos, vtos, putstatic , 2 ); + def(Bytecodes::_getfield , ubcp|____|clvm|____, vtos, vtos, getfield , 1 ); + def(Bytecodes::_putfield , ubcp|____|clvm|____, vtos, vtos, putfield , 2 ); + def(Bytecodes::_invokevirtual , ubcp|disp|clvm|____, vtos, vtos, invokevirtual , 2 ); + def(Bytecodes::_invokespecial , ubcp|disp|clvm|____, vtos, vtos, invokespecial , 1 ); + def(Bytecodes::_invokestatic , ubcp|disp|clvm|____, vtos, vtos, invokestatic , 1 ); + def(Bytecodes::_invokeinterface , ubcp|disp|clvm|____, vtos, vtos, invokeinterface , 1 ); + def(Bytecodes::_new , ubcp|____|clvm|____, vtos, atos, _new , _ ); + def(Bytecodes::_newarray , ubcp|____|clvm|____, itos, atos, newarray , _ ); + def(Bytecodes::_anewarray , ubcp|____|clvm|____, itos, atos, anewarray , _ ); + def(Bytecodes::_arraylength , ____|____|____|____, atos, itos, arraylength , _ ); + def(Bytecodes::_athrow , ____|disp|____|____, atos, vtos, athrow , _ ); + def(Bytecodes::_checkcast , ubcp|____|clvm|____, atos, atos, checkcast , _ ); + def(Bytecodes::_instanceof , ubcp|____|clvm|____, atos, itos, instanceof , _ ); + def(Bytecodes::_monitorenter , ____|disp|clvm|____, atos, vtos, monitorenter , _ ); + def(Bytecodes::_monitorexit , ____|____|clvm|____, atos, vtos, monitorexit , _ ); + def(Bytecodes::_wide , ubcp|disp|____|____, vtos, vtos, wide , _ ); + def(Bytecodes::_multianewarray , ubcp|____|clvm|____, vtos, atos, multianewarray , _ ); + def(Bytecodes::_ifnull , ubcp|____|clvm|____, atos, vtos, if_nullcmp , equal ); + def(Bytecodes::_ifnonnull , ubcp|____|clvm|____, atos, vtos, if_nullcmp , not_equal ); + def(Bytecodes::_goto_w , ubcp|____|clvm|____, vtos, vtos, goto_w , _ ); + def(Bytecodes::_jsr_w , ubcp|____|____|____, vtos, vtos, jsr_w , _ ); + + // wide Java spec bytecodes + def(Bytecodes::_iload , ubcp|____|____|iswd, vtos, itos, wide_iload , _ ); + def(Bytecodes::_lload , ubcp|____|____|iswd, vtos, ltos, wide_lload , _ ); + def(Bytecodes::_fload , ubcp|____|____|iswd, vtos, ftos, wide_fload , _ ); + def(Bytecodes::_dload , ubcp|____|____|iswd, vtos, dtos, wide_dload , _ ); + def(Bytecodes::_aload , ubcp|____|____|iswd, vtos, atos, wide_aload , _ ); + def(Bytecodes::_istore , ubcp|____|____|iswd, vtos, vtos, wide_istore , _ ); + def(Bytecodes::_lstore , ubcp|____|____|iswd, vtos, vtos, wide_lstore , _ ); + def(Bytecodes::_fstore , ubcp|____|____|iswd, vtos, vtos, wide_fstore , _ ); + def(Bytecodes::_dstore , ubcp|____|____|iswd, vtos, vtos, wide_dstore , _ ); + def(Bytecodes::_astore , ubcp|____|____|iswd, vtos, vtos, wide_astore , _ ); + def(Bytecodes::_iinc , ubcp|____|____|iswd, vtos, vtos, wide_iinc , _ ); + def(Bytecodes::_ret , ubcp|disp|____|iswd, vtos, vtos, wide_ret , _ ); + def(Bytecodes::_breakpoint , ubcp|disp|clvm|____, vtos, vtos, _breakpoint , _ ); + + // JVM bytecodes + def(Bytecodes::_fast_agetfield , ubcp|____|____|____, atos, atos, fast_accessfield , atos ); + def(Bytecodes::_fast_bgetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos ); + def(Bytecodes::_fast_cgetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos ); + def(Bytecodes::_fast_dgetfield , ubcp|____|____|____, atos, dtos, fast_accessfield , dtos ); + def(Bytecodes::_fast_fgetfield , ubcp|____|____|____, atos, ftos, fast_accessfield , ftos ); + def(Bytecodes::_fast_igetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos ); + def(Bytecodes::_fast_lgetfield , ubcp|____|____|____, atos, ltos, fast_accessfield , ltos ); + def(Bytecodes::_fast_sgetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos ); + + def(Bytecodes::_fast_aputfield , ubcp|____|____|____, atos, vtos, fast_storefield , atos ); + def(Bytecodes::_fast_bputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos ); + def(Bytecodes::_fast_cputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos ); + def(Bytecodes::_fast_dputfield , ubcp|____|____|____, dtos, vtos, fast_storefield , dtos ); + def(Bytecodes::_fast_fputfield , ubcp|____|____|____, ftos, vtos, fast_storefield , ftos ); + def(Bytecodes::_fast_iputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos ); + def(Bytecodes::_fast_lputfield , ubcp|____|____|____, ltos, vtos, fast_storefield , ltos ); + def(Bytecodes::_fast_sputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos ); + + def(Bytecodes::_fast_aload_0 , ____|____|____|____, vtos, atos, aload , 0 ); + def(Bytecodes::_fast_iaccess_0 , ubcp|____|____|____, vtos, itos, fast_xaccess , itos ); + def(Bytecodes::_fast_aaccess_0 , ubcp|____|____|____, vtos, atos, fast_xaccess , atos ); + def(Bytecodes::_fast_faccess_0 , ubcp|____|____|____, vtos, ftos, fast_xaccess , ftos ); + + def(Bytecodes::_fast_iload , ubcp|____|____|____, vtos, itos, fast_iload , _ ); + def(Bytecodes::_fast_iload2 , ubcp|____|____|____, vtos, itos, fast_iload2 , _ ); + def(Bytecodes::_fast_icaload , ubcp|____|____|____, vtos, itos, fast_icaload , _ ); + + def(Bytecodes::_fast_invokevfinal , ubcp|disp|clvm|____, vtos, vtos, fast_invokevfinal , 2 ); + + + def(Bytecodes::_fast_linearswitch , ubcp|disp|____|____, itos, vtos, fast_linearswitch , _ ); + def(Bytecodes::_fast_binaryswitch , ubcp|disp|____|____, itos, vtos, fast_binaryswitch , _ ); + + def(Bytecodes::_return_register_finalizer , ____|disp|clvm|____, vtos, vtos, _return , vtos ); + + def(Bytecodes::_shouldnotreachhere , ____|____|____|____, vtos, vtos, shouldnotreachhere , _ ); + // platform specific bytecodes + pd_initialize(); + + _is_initialized = true; +} + +#if defined(TEMPLATE_TABLE_BUG) + #undef iload + #undef lload + #undef fload + #undef dload + #undef aload + #undef istore + #undef lstore + #undef fstore + #undef dstore + #undef astore +#endif // TEMPLATE_TABLE_BUG + + +void templateTable_init() { + TemplateTable::initialize(); +} + + +void TemplateTable::unimplemented_bc() { + _masm->unimplemented( Bytecodes::name(_desc->bytecode())); +} +#endif /* !CC_INTERP */ diff --git a/hotspot/src/share/vm/interpreter/templateTable.hpp b/hotspot/src/share/vm/interpreter/templateTable.hpp new file mode 100644 index 00000000000..4f40a7ac291 --- /dev/null +++ b/hotspot/src/share/vm/interpreter/templateTable.hpp @@ -0,0 +1,329 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef CC_INTERP +// All the necessary definitions used for (bytecode) template generation. Instead of +// spreading the implementation functionality for each bytecode in the interpreter +// and the snippet generator, a template is assigned to each bytecode which can be +// used to generate the bytecode's implementation if needed. + + +// A Template describes the properties of a code template for a given bytecode +// and provides a generator to generate the code template. + +class Template VALUE_OBJ_CLASS_SPEC { + private: + enum Flags { + uses_bcp_bit, // set if template needs the bcp pointing to bytecode + does_dispatch_bit, // set if template dispatches on its own + calls_vm_bit, // set if template calls the vm + wide_bit // set if template belongs to a wide instruction + }; + + typedef void (*generator)(int arg); + + int _flags; // describes interpreter template properties (bcp unknown) + TosState _tos_in; // tos cache state before template execution + TosState _tos_out; // tos cache state after template execution + generator _gen; // template code generator + int _arg; // argument for template code generator + + void initialize(int flags, TosState tos_in, TosState tos_out, generator gen, int arg); + + friend class TemplateTable; + + public: + Bytecodes::Code bytecode() const; + bool is_valid() const { return _gen != NULL; } + bool uses_bcp() const { return (_flags & (1 << uses_bcp_bit )) != 0; } + bool does_dispatch() const { return (_flags & (1 << does_dispatch_bit)) != 0; } + bool calls_vm() const { return (_flags & (1 << calls_vm_bit )) != 0; } + bool is_wide() const { return (_flags & (1 << wide_bit )) != 0; } + TosState tos_in() const { return _tos_in; } + TosState tos_out() const { return _tos_out; } + void generate(InterpreterMacroAssembler* masm); +}; + + +// The TemplateTable defines all Templates and provides accessor functions +// to get the template for a given bytecode. + +class TemplateTable: AllStatic { + public: + enum Operation { add, sub, mul, div, rem, _and, _or, _xor, shl, shr, ushr }; + enum Condition { equal, not_equal, less, less_equal, greater, greater_equal }; + + private: + static bool _is_initialized; // true if TemplateTable has been initialized + static Template _template_table [Bytecodes::number_of_codes]; + static Template _template_table_wide[Bytecodes::number_of_codes]; + + static Template* _desc; // the current template to be generated + static Bytecodes::Code bytecode() { return _desc->bytecode(); } + + public: + //%note templates_1 + static InterpreterMacroAssembler* _masm; // the assembler used when generating templates + + private: + + // special registers + static inline Address at_bcp(int offset); + + // helpers + static void unimplemented_bc(); + static void patch_bytecode(Bytecodes::Code bc, Register scratch1, + Register scratch2, bool load_bc_in_scratch = true); + + // C calls + static void call_VM(Register oop_result, address entry_point); + static void call_VM(Register oop_result, address entry_point, Register arg_1); + static void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2); + static void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3); + + // these overloadings are not presently used on SPARC: + static void call_VM(Register oop_result, Register last_java_sp, address entry_point); + static void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1); + static void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2); + static void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, Register arg_3); + + // bytecodes + static void nop(); + + static void aconst_null(); + static void iconst(int value); + static void lconst(int value); + static void fconst(int value); + static void dconst(int value); + + static void bipush(); + static void sipush(); + static void ldc(bool wide); + static void ldc2_w(); + + static void locals_index(Register reg, int offset = 1); + static void iload(); + static void fast_iload(); + static void fast_iload2(); + static void fast_icaload(); + static void lload(); + static void fload(); + static void dload(); + static void aload(); + + static void locals_index_wide(Register reg); + static void wide_iload(); + static void wide_lload(); + static void wide_fload(); + static void wide_dload(); + static void wide_aload(); + + static void iaload(); + static void laload(); + static void faload(); + static void daload(); + static void aaload(); + static void baload(); + static void caload(); + static void saload(); + + static void iload(int n); + static void lload(int n); + static void fload(int n); + static void dload(int n); + static void aload(int n); + static void aload_0(); + + static void istore(); + static void lstore(); + static void fstore(); + static void dstore(); + static void astore(); + + static void wide_istore(); + static void wide_lstore(); + static void wide_fstore(); + static void wide_dstore(); + static void wide_astore(); + + static void iastore(); + static void lastore(); + static void fastore(); + static void dastore(); + static void aastore(); + static void bastore(); + static void castore(); + static void sastore(); + + static void istore(int n); + static void lstore(int n); + static void fstore(int n); + static void dstore(int n); + static void astore(int n); + + static void pop(); + static void pop2(); + static void dup(); + static void dup_x1(); + static void dup_x2(); + static void dup2(); + static void dup2_x1(); + static void dup2_x2(); + static void swap(); + + static void iop2(Operation op); + static void lop2(Operation op); + static void fop2(Operation op); + static void dop2(Operation op); + + static void idiv(); + static void irem(); + + static void lmul(); + static void ldiv(); + static void lrem(); + static void lshl(); + static void lshr(); + static void lushr(); + + static void ineg(); + static void lneg(); + static void fneg(); + static void dneg(); + + static void iinc(); + static void wide_iinc(); + static void convert(); + static void lcmp(); + + static void float_cmp (bool is_float, int unordered_result); + static void float_cmp (int unordered_result); + static void double_cmp(int unordered_result); + + static void count_calls(Register method, Register temp); + static void branch(bool is_jsr, bool is_wide); + static void if_0cmp (Condition cc); + static void if_icmp (Condition cc); + static void if_nullcmp(Condition cc); + static void if_acmp (Condition cc); + + static void _goto(); + static void jsr(); + static void ret(); + static void wide_ret(); + + static void goto_w(); + static void jsr_w(); + + static void tableswitch(); + static void lookupswitch(); + static void fast_linearswitch(); + static void fast_binaryswitch(); + + static void _return(TosState state); + + static void resolve_cache_and_index(int byte_no, Register cache, Register index); + static void load_invoke_cp_cache_entry(int byte_no, + Register method, + Register itable_index, + Register flags, + bool is_invokevirtual = false, + bool is_virtual_final = false); + static void load_field_cp_cache_entry(Register obj, + Register cache, + Register index, + Register offset, + Register flags, + bool is_static); + static void invokevirtual(int byte_no); + static void invokespecial(int byte_no); + static void invokestatic(int byte_no); + static void invokeinterface(int byte_no); + static void fast_invokevfinal(int byte_no); + + static void getfield_or_static(int byte_no, bool is_static); + static void putfield_or_static(int byte_no, bool is_static); + static void getfield(int byte_no); + static void putfield(int byte_no); + static void getstatic(int byte_no); + static void putstatic(int byte_no); + static void pop_and_check_object(Register obj); + + static void _new(); + static void newarray(); + static void anewarray(); + static void arraylength(); + static void checkcast(); + static void instanceof(); + + static void athrow(); + + static void monitorenter(); + static void monitorexit(); + + static void wide(); + static void multianewarray(); + + static void fast_xaccess(TosState state); + static void fast_accessfield(TosState state); + static void fast_storefield(TosState state); + + static void _breakpoint(); + + static void shouldnotreachhere(); + + // jvmti support + static void jvmti_post_field_access(Register cache, Register index, bool is_static, bool has_tos); + static void jvmti_post_field_mod(Register cache, Register index, bool is_static); + static void jvmti_post_fast_field_mod(); + + // debugging of TemplateGenerator + static void transition(TosState tos_in, TosState tos_out);// checks if in/out states expected by template generator correspond to table entries + + // initialization helpers + static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)( ), char filler ); + static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(int arg ), int arg ); + static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(bool arg ), bool arg ); + static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(TosState tos), TosState tos); + static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(Operation op), Operation op); + static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(Condition cc), Condition cc); + + friend class Template; + + // InterpreterMacroAssembler::is_a(), etc., need TemplateTable::call_VM(). + friend class InterpreterMacroAssembler; + + public: + // Initialization + static void initialize(); + static void pd_initialize(); + + // Templates + static Template* template_for (Bytecodes::Code code) { Bytecodes::check (code); return &_template_table [code]; } + static Template* template_for_wide(Bytecodes::Code code) { Bytecodes::wide_check(code); return &_template_table_wide[code]; } + + // Platform specifics + #include "incls/_templateTable_pd.hpp.incl" +}; +#endif /* !CC_INTERP */ diff --git a/hotspot/src/share/vm/libadt/dict.cpp b/hotspot/src/share/vm/libadt/dict.cpp new file mode 100644 index 00000000000..3ac20d6c29e --- /dev/null +++ b/hotspot/src/share/vm/libadt/dict.cpp @@ -0,0 +1,383 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Dictionaries - An Abstract Data Type + +#include "incls/_precompiled.incl" +#include "incls/_dict.cpp.incl" + +// %%%%% includes not needed with AVM framework - Ungar + +// #include "port.hpp" +//IMPLEMENTATION +// #include "dict.hpp" + +#include + +// The iostream is not needed and it gets confused for gcc by the +// define of bool. +// +// #include + +//------------------------------data----------------------------------------- +// String hash tables +#define MAXID 20 +static byte initflag = 0; // True after 1st initialization +static const char shft[MAXID] = {1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6}; +static short xsum[MAXID]; + +//------------------------------bucket--------------------------------------- +class bucket : public ResourceObj { +public: + uint _cnt, _max; // Size of bucket + void **_keyvals; // Array of keys and values +}; + +//------------------------------Dict----------------------------------------- +// The dictionary is kept has a hash table. The hash table is a even power +// of two, for nice modulo operations. Each bucket in the hash table points +// to a linear list of key-value pairs; each key & value is just a (void *). +// The list starts with a count. A hash lookup finds the list head, then a +// simple linear scan finds the key. If the table gets too full, it's +// doubled in size; the total amount of EXTRA times all hash functions are +// computed for the doubling is no more than the current size - thus the +// doubling in size costs no more than a constant factor in speed. +Dict::Dict(CmpKey initcmp, Hash inithash) : _hash(inithash), _cmp(initcmp), + _arena(Thread::current()->resource_area()) { + int i; + + // Precompute table of null character hashes + if( !initflag ) { // Not initializated yet? + xsum[0] = (1<Amalloc_4(sizeof(bucket)*_size); + memset(_bin,0,sizeof(bucket)*_size); +} + +Dict::Dict(CmpKey initcmp, Hash inithash, Arena *arena, int size) +: _hash(inithash), _cmp(initcmp), _arena(arena) { + int i; + + // Precompute table of null character hashes + if( !initflag ) { // Not initializated yet? + xsum[0] = (1<Amalloc_4(sizeof(bucket)*_size); + memset(_bin,0,sizeof(bucket)*_size); +} + +//------------------------------~Dict------------------------------------------ +// Delete an existing dictionary. +Dict::~Dict() { + /* + tty->print("~Dict %d/%d: ",_cnt,_size); + for( uint i=0; i < _size; i++) // For complete new table do + tty->print("%d ",_bin[i]._cnt); + tty->print("\n");*/ + /*for( uint i=0; i<_size; i++ ) { + FREE_FAST( _bin[i]._keyvals ); + } */ +} + +//------------------------------Clear---------------------------------------- +// Zap to empty; ready for re-use +void Dict::Clear() { + _cnt = 0; // Empty contents + for( uint i=0; i<_size; i++ ) + _bin[i]._cnt = 0; // Empty buckets, but leave allocated + // Leave _size & _bin alone, under the assumption that dictionary will + // grow to this size again. +} + +//------------------------------doubhash--------------------------------------- +// Double hash table size. If can't do so, just suffer. If can, then run +// thru old hash table, moving things to new table. Note that since hash +// table doubled, exactly 1 new bit is exposed in the mask - so everything +// in the old table ends up on 1 of two lists in the new table; a hi and a +// lo list depending on the value of the bit. +void Dict::doubhash(void) { + uint oldsize = _size; + _size <<= 1; // Double in size + _bin = (bucket*)_arena->Arealloc( _bin, sizeof(bucket)*oldsize, sizeof(bucket)*_size ); + memset( &_bin[oldsize], 0, oldsize*sizeof(bucket) ); + // Rehash things to spread into new table + for( uint i=0; i < oldsize; i++) { // For complete OLD table do + bucket *b = &_bin[i]; // Handy shortcut for _bin[i] + if( !b->_keyvals ) continue; // Skip empties fast + + bucket *nb = &_bin[i+oldsize]; // New bucket shortcut + uint j = b->_max; // Trim new bucket to nearest power of 2 + while( j > b->_cnt ) j >>= 1; // above old bucket _cnt + if( !j ) j = 1; // Handle zero-sized buckets + nb->_max = j<<1; + // Allocate worst case space for key-value pairs + nb->_keyvals = (void**)_arena->Amalloc_4( sizeof(void *)*nb->_max*2 ); + uint nbcnt = 0; + + for( j=0; j_cnt; j++ ) { // Rehash all keys in this bucket + void *key = b->_keyvals[j+j]; + if( (_hash( key ) & (_size-1)) != i ) { // Moving to hi bucket? + nb->_keyvals[nbcnt+nbcnt] = key; + nb->_keyvals[nbcnt+nbcnt+1] = b->_keyvals[j+j+1]; + nb->_cnt = nbcnt = nbcnt+1; + b->_cnt--; // Remove key/value from lo bucket + b->_keyvals[j+j ] = b->_keyvals[b->_cnt+b->_cnt ]; + b->_keyvals[j+j+1] = b->_keyvals[b->_cnt+b->_cnt+1]; + j--; // Hash compacted element also + } + } // End of for all key-value pairs in bucket + } // End of for all buckets + + +} + +//------------------------------Dict----------------------------------------- +// Deep copy a dictionary. +Dict::Dict( const Dict &d ) : _size(d._size), _cnt(d._cnt), _hash(d._hash),_cmp(d._cmp), _arena(d._arena) { + _bin = (bucket*)_arena->Amalloc_4(sizeof(bucket)*_size); + memcpy( _bin, d._bin, sizeof(bucket)*_size ); + for( uint i=0; i<_size; i++ ) { + if( !_bin[i]._keyvals ) continue; + _bin[i]._keyvals=(void**)_arena->Amalloc_4( sizeof(void *)*_bin[i]._max*2); + memcpy( _bin[i]._keyvals, d._bin[i]._keyvals,_bin[i]._cnt*2*sizeof(void*)); + } +} + +//------------------------------Dict----------------------------------------- +// Deep copy a dictionary. +Dict &Dict::operator =( const Dict &d ) { + if( _size < d._size ) { // If must have more buckets + _arena = d._arena; + _bin = (bucket*)_arena->Arealloc( _bin, sizeof(bucket)*_size, sizeof(bucket)*d._size ); + memset( &_bin[_size], 0, (d._size-_size)*sizeof(bucket) ); + _size = d._size; + } + uint i; + for( i=0; i<_size; i++ ) // All buckets are empty + _bin[i]._cnt = 0; // But leave bucket allocations alone + _cnt = d._cnt; + *(Hash*)(&_hash) = d._hash; + *(CmpKey*)(&_cmp) = d._cmp; + for( i=0; i<_size; i++ ) { + bucket *b = &d._bin[i]; // Shortcut to source bucket + for( uint j=0; j_cnt; j++ ) + Insert( b->_keyvals[j+j], b->_keyvals[j+j+1] ); + } + return *this; +} + +//------------------------------Insert---------------------------------------- +// Insert or replace a key/value pair in the given dictionary. If the +// dictionary is too full, it's size is doubled. The prior value being +// replaced is returned (NULL if this is a 1st insertion of that key). If +// an old value is found, it's swapped with the prior key-value pair on the +// list. This moves a commonly searched-for value towards the list head. +void *Dict::Insert(void *key, void *val, bool replace) { + uint hash = _hash( key ); // Get hash key + uint i = hash & (_size-1); // Get hash key, corrected for size + bucket *b = &_bin[i]; // Handy shortcut + for( uint j=0; j_cnt; j++ ) { + if( !_cmp(key,b->_keyvals[j+j]) ) { + if (!replace) { + return b->_keyvals[j+j+1]; + } else { + void *prior = b->_keyvals[j+j+1]; + b->_keyvals[j+j ] = key; // Insert current key-value + b->_keyvals[j+j+1] = val; + return prior; // Return prior + } + } + } + if( ++_cnt > _size ) { // Hash table is full + doubhash(); // Grow whole table if too full + i = hash & (_size-1); // Rehash + b = &_bin[i]; // Handy shortcut + } + if( b->_cnt == b->_max ) { // Must grow bucket? + if( !b->_keyvals ) { + b->_max = 2; // Initial bucket size + b->_keyvals = (void**)_arena->Amalloc_4(sizeof(void*) * b->_max * 2); + } else { + b->_keyvals = (void**)_arena->Arealloc(b->_keyvals, sizeof(void*) * b->_max * 2, sizeof(void*) * b->_max * 4); + b->_max <<= 1; // Double bucket + } + } + b->_keyvals[b->_cnt+b->_cnt ] = key; + b->_keyvals[b->_cnt+b->_cnt+1] = val; + b->_cnt++; + return NULL; // Nothing found prior +} + +//------------------------------Delete--------------------------------------- +// Find & remove a value from dictionary. Return old value. +void *Dict::Delete(void *key) { + uint i = _hash( key ) & (_size-1); // Get hash key, corrected for size + bucket *b = &_bin[i]; // Handy shortcut + for( uint j=0; j_cnt; j++ ) + if( !_cmp(key,b->_keyvals[j+j]) ) { + void *prior = b->_keyvals[j+j+1]; + b->_cnt--; // Remove key/value from lo bucket + b->_keyvals[j+j ] = b->_keyvals[b->_cnt+b->_cnt ]; + b->_keyvals[j+j+1] = b->_keyvals[b->_cnt+b->_cnt+1]; + _cnt--; // One less thing in table + return prior; + } + return NULL; +} + +//------------------------------FindDict------------------------------------- +// Find a key-value pair in the given dictionary. If not found, return NULL. +// If found, move key-value pair towards head of list. +void *Dict::operator [](const void *key) const { + uint i = _hash( key ) & (_size-1); // Get hash key, corrected for size + bucket *b = &_bin[i]; // Handy shortcut + for( uint j=0; j_cnt; j++ ) + if( !_cmp(key,b->_keyvals[j+j]) ) + return b->_keyvals[j+j+1]; + return NULL; +} + +//------------------------------CmpDict-------------------------------------- +// CmpDict compares two dictionaries; they must have the same keys (their +// keys must match using CmpKey) and they must have the same values (pointer +// comparison). If so 1 is returned, if not 0 is returned. +int32 Dict::operator ==(const Dict &d2) const { + if( _cnt != d2._cnt ) return 0; + if( _hash != d2._hash ) return 0; + if( _cmp != d2._cmp ) return 0; + for( uint i=0; i < _size; i++) { // For complete hash table do + bucket *b = &_bin[i]; // Handy shortcut + if( b->_cnt != d2._bin[i]._cnt ) return 0; + if( memcmp(b->_keyvals, d2._bin[i]._keyvals, b->_cnt*2*sizeof(void*) ) ) + return 0; // Key-value pairs must match + } + return 1; // All match, is OK +} + +//------------------------------print------------------------------------------ +// Handier print routine +void Dict::print() { + DictI i(this); // Moved definition in iterator here because of g++. + tty->print("Dict@0x%lx[%d] = {", this, _cnt); + for( ; i.test(); ++i ) { + tty->print("(0x%lx,0x%lx),", i._key, i._value); + } + tty->print_cr("}"); +} + +//------------------------------Hashing Functions---------------------------- +// Convert string to hash key. This algorithm implements a universal hash +// function with the multipliers frozen (ok, so it's not universal). The +// multipliers (and allowable characters) are all odd, so the resultant sum +// is odd - guarenteed not divisible by any power of two, so the hash tables +// can be any power of two with good results. Also, I choose multipliers +// that have only 2 bits set (the low is always set to be odd) so +// multiplication requires only shifts and adds. Characters are required to +// be in the range 0-127 (I double & add 1 to force oddness). Keys are +// limited to MAXID characters in length. Experimental evidence on 150K of +// C text shows excellent spreading of values for any size hash table. +int hashstr(const void *t) { + register char c, k = 0; + register int32 sum = 0; + register const char *s = (const char *)t; + + while( ((c = *s++) != '\0') && (k < MAXID-1) ) { // Get characters till null or MAXID-1 + c = (c<<1)+1; // Characters are always odd! + sum += c + (c<> 1); // Hash key, un-modulo'd table size +} + +//------------------------------hashptr-------------------------------------- +// Slimey cheap hash function; no guarenteed performance. Better than the +// default for pointers, especially on MS-DOS machines. +int hashptr(const void *key) { +#ifdef __TURBOC__ + return ((intptr_t)key >> 16); +#else // __TURBOC__ + return ((intptr_t)key >> 2); +#endif +} + +// Slimey cheap hash function; no guarenteed performance. +int hashkey(const void *key) { + return (intptr_t)key; +} + +//------------------------------Key Comparator Functions--------------------- +int32 cmpstr(const void *k1, const void *k2) { + return strcmp((const char *)k1,(const char *)k2); +} + +// Slimey cheap key comparator. +int32 cmpkey(const void *key1, const void *key2) { + return (int32)((intptr_t)key1 - (intptr_t)key2); +} + +//============================================================================= +//------------------------------reset------------------------------------------ +// Create an iterator and initialize the first variables. +void DictI::reset( const Dict *dict ) { + _d = dict; // The dictionary + _i = (uint)-1; // Before the first bin + _j = 0; // Nothing left in the current bin + ++(*this); // Step to first real value +} + +//------------------------------next------------------------------------------- +// Find the next key-value pair in the dictionary, or return a NULL key and +// value. +void DictI::operator ++(void) { + if( _j-- ) { // Still working in current bin? + _key = _d->_bin[_i]._keyvals[_j+_j]; + _value = _d->_bin[_i]._keyvals[_j+_j+1]; + return; + } + + while( ++_i < _d->_size ) { // Else scan for non-zero bucket + _j = _d->_bin[_i]._cnt; + if( !_j ) continue; + _j--; + _key = _d->_bin[_i]._keyvals[_j+_j]; + _value = _d->_bin[_i]._keyvals[_j+_j+1]; + return; + } + _key = _value = NULL; +} diff --git a/hotspot/src/share/vm/libadt/dict.hpp b/hotspot/src/share/vm/libadt/dict.hpp new file mode 100644 index 00000000000..93f86829e09 --- /dev/null +++ b/hotspot/src/share/vm/libadt/dict.hpp @@ -0,0 +1,117 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _DICT_ +#define _DICT_ +// Dictionaries - An Abstract Data Type +//INTERFACE +class ostream; +class Dict; + +// These dictionaries define a key-value mapping. They can be inserted to, +// searched or deleted from. They grow and shrink as needed. The key is a +// pointer to something (or anything which can be stored in a pointer). A +// key comparison routine determines if two keys are equal or not. A hash +// function can be provided; if it's not provided the key itself is used +// instead. A nice string hash function is included. +typedef int32 (*CmpKey)(const void *key1, const void *key2); +typedef int (*Hash)(const void *key); +typedef void (*FuncDict)(const void *key, const void *val, Dict *d); + +class Dict : public ResourceObj { // Dictionary structure + private: + class Arena *_arena; // Where to draw storage from + class bucket *_bin; // Hash table is array of buckets + uint _size; // Size (# of slots) in hash table + uint32 _cnt; // Number of key-value pairs in hash table + const Hash _hash; // Hashing function + const CmpKey _cmp; // Key comparison function + void doubhash( void ); // Double hash table size + + public: + friend class DictI; // Friendly iterator function + + // cmp is a key comparision routine. hash is a routine to hash a key. + Dict( CmpKey cmp, Hash hash ); + Dict( CmpKey cmp, Hash hash, Arena *arena, int size=16 ); + ~Dict(); + + Dict( const Dict & ); // Deep-copy guts + Dict &operator =( const Dict & ); + + // Zap to empty; ready for re-use + void Clear(); + + // Return # of key-value pairs in dict + uint32 Size(void) const { return _cnt; } + + // Insert inserts the given key-value pair into the dictionary. The prior + // value of the key is returned; NULL if the key was not previously defined. + void *Insert(void *key, void *val, bool replace = true); // A new key-value + void *Delete(void *key); // Delete & return old + + // Find finds the value of a given key; or NULL if not found. + // The dictionary is NOT changed. + void *operator [](const void *key) const; // Do a lookup + + // == compares two dictionaries; they must have the same keys (their keys + // must match using CmpKey) and they must have the same values (pointer + // comparison). If so 1 is returned, if not 0 is returned. + int32 operator ==(const Dict &d) const; // Compare dictionaries for equal + + // Print out the dictionary contents as key-value pairs + void print(); +}; + +// Hashing functions +int hashstr(const void *s); // Nice string hash +// Slimey cheap hash function; no guarenteed performance. Better than the +// default for pointers, especially on MS-DOS machines. +int hashptr(const void *key); +// Slimey cheap hash function; no guarenteed performance. +int hashkey(const void *key); + +// Key comparators +int32 cmpstr(const void *k1, const void *k2); +// Slimey cheap key comparator. +int32 cmpkey(const void *key1, const void *key2); + +//------------------------------Iteration-------------------------------------- +// The class of dictionary iterators. Fails in the presences of modifications +// to the dictionary during iteration (including searches). +// Usage: for( DictI i(dict); i.test(); ++i ) { body = i.key; body = i.value;} +class DictI { + private: + const Dict *_d; // Dictionary being iterated over + uint _i; // Counter over the bins + uint _j; // Counter inside each bin + public: + const void *_key, *_value; // Easy access to the key-value pair + DictI( const Dict *d ) {reset(d);}; // Create a new iterator + void reset( const Dict *dict ); // Reset existing iterator + void operator ++(void); // Increment iterator + int test(void) { return _i<_d->_size;} // Test for end of iteration +}; + +#endif // _DICT_ diff --git a/hotspot/src/share/vm/libadt/port.cpp b/hotspot/src/share/vm/libadt/port.cpp new file mode 100644 index 00000000000..ad0f295bb22 --- /dev/null +++ b/hotspot/src/share/vm/libadt/port.cpp @@ -0,0 +1,123 @@ +/* + * Copyright 1997-1998 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Code for portable compiling + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "incls/_precompiled.incl" +#include "incls/_port.cpp.incl" + +// %%%%% includes not needed with AVM framework - Ungar +// #include "port.hpp" + +// This is only used if turboc is used and it causes problems with +// gcc. +#ifdef __TURBOC__ +#include +#endif + +#include + +//------------------------------gcd-------------------------------------------- +// Greatest common divisor +uint32 gcd( register uint32 x, register uint32 y ) +{ + register uint32 tmp; + while( x ) { // While not zero + tmp = x; // Hold onto smaller x value + x = y % x; // Compute modulus; since y>=x, 0 <= mod < x + y = tmp; // y = old x + } + return y; +} + +//----------------------------------------------------------------------------- +// Find first 1, or return 32 if empty +int ff1( uint32 mask ) +{ + unsigned i, n = 0; + + for( i=1, n=0; i; i<<=1, n++) + if( mask&i ) return n; + return 32; +} + +//----------------------------------------------------------------------------- +// Find highest 1, or return 32 if empty +int fh1( uint32 mask ) +{ + unsigned i, n = 0; + + for( i=((uint32)1<<31), n=31; i; i>>=1, n--) + if( mask&i ) return n; + return 32; +} + +//------------------------------rotate32--------------------------------------- +// Rotate 32bits. Postive rotates left (bits move toward high-order bit), +// negative rotates right. +uint32 rotate32( register uint32 x, register int32 cnt ) +{ + if( cnt >= 0 ) { // Positive rotates left + cnt &= 31; // Mask off extra shift bits + } else { // Negative rotates right + cnt = (-cnt)&31; // Flip sign; mask extra shift bits + cnt = 32-cnt; // Rotate right by big left rotation + } + return (x << cnt) | (x >> (32-cnt)); +} + +/* Disabled - we have another log2 in the system. + This function doesn't work if used as substitute + for the existing log2. Keep around until we have + verified all uses of log2 do the correct thing! +//------------------------------log2------------------------------------------- +// Log base 2. Might also be called 'count leading zeros'. Log2(x) returns +// an l such that (1L<= 0 ) // While high bit is clear + sx <<= 1, l--; // Shift bits left, count down log2 + return l; +} +*/ + +//------------------------------print------------------------------------------ +// Print a pointer without modifying the contents +#ifdef __TURBOC__ +ostream &ostream::operator << (const void *ptr) +{ + return (*this) << "0x" << hex << (uint)ptr << dec; +} +#else +/*ostream &operator << (ostream &os, const void *ptr) +{ + return os << "0x" << hex << (uint)ptr << dec; +}*/ +#endif diff --git a/hotspot/src/share/vm/libadt/port.hpp b/hotspot/src/share/vm/libadt/port.hpp new file mode 100644 index 00000000000..70034d09bbd --- /dev/null +++ b/hotspot/src/share/vm/libadt/port.hpp @@ -0,0 +1,218 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _PORT_ +#define _PORT_ +// Typedefs for portable compiling + +#if defined(__GNUC__) + +#define INTERFACE #pragma interface +#define IMPLEMENTATION #pragma implementation +//INTERFACE +#include +#include +#include +#undef bzero +inline void bzero(void *b, int len) { memset(b,0,len); } +#undef bcopy +inline void bcopy(const void *s, void *d, size_t len) { memmove(d,s,len); } +#undef bcmp +inline int bcmp(const void *s,const void *t,int len) { return memcmp(s,t,len);} +extern "C" unsigned long strtoul(const char *s, char **end, int base); + +// Definition for sys_errlist varies from Sun 4.1 & Solaris. +// We use the new Solaris definition. +#include + +// Access to the C++ class virtual function pointer +// Put the class in the macro +typedef void *VPTR; +// G++ puts it at the end of the base class +#define ACCESS_VPTR(class) VPTR&vptr(){return*(VPTR*)((char*)this+sizeof(class)-sizeof(void*));} + +#elif defined(__TURBOC__) + +#include +#include +extern "C" int stricmp(const char *, const char *); +inline void bcopy(const void *s, void *d, int l) { memmove(d,s,l); } +inline void bzero(void *p, int l) { memset(p,0,l); } +inline int bcmp(const void *s, const void *d, int l) { return memcmp(s,d,l); } +inline int min( int a, int b) { return a < b ? a : b; } +inline int max( int a, int b) { return a > b ? a : b; } +//strcasecmp moved to globalDefinitions_visCPP.hpp +//inline int strcasecmp(const char *s1, const char *s2) { return stricmp(s1,s2); } +inline long abs( long x ) { return x < 0 ? -x : x; } +// Access to the C++ class virtual function pointer +// Put the class in the macro +typedef void near *VPTR; +// BorlandC puts it up front +#define ACCESS_VPTR(class) VPTR&vptr(){return*(VPTR*)this;} + +#elif defined(__hpux) + +#define INTERFACE +#define IMPLEMENTATION +#define signed +#include +#include +inline long min( long a, long b) { return a < b ? a : b; } +inline long max( long a, long b) { return a > b ? a : b; } +inline int min( int a, int b) { return a < b ? a : b; } +inline int max( int a, int b) { return a > b ? a : b; } +inline long abs( long x ) { return x < 0 ? -x : x; } + +#elif defined(__MOTO__) +// Motorola's mcc +#define INTERFACE +#define IMPLEMENTATION +#include +#include +inline int min( int a, int b) { return a < b ? a : b; } +inline int max( int a, int b) { return a > b ? a : b; } + +#elif defined(_AIX) +// IBM's xlC compiler +#define INTERFACE +#define IMPLEMENTATION +#include +#include +inline int min( int a, int b) { return a < b ? a : b; } +inline int max( int a, int b) { return a > b ? a : b; } + +#elif defined(_MSC_VER) +// Microsoft Visual C++ +//#define INTERFACE +#define IMPLEMENTATION +#include +#undef small +//strcasecmp moved to globalDefinitions_visCPP.hpp +//inline int strcasecmp(const char *s1, const char *s2) { return stricmp(s1,s2); } + + +#elif defined(SPARC_WORKS) + +#define INTERFACE +#define IMPLEMENTATION + +#include +#include +#include + +#elif defined(SOLARIS) + +#define INTERFACE +#define IMPLEMENTATION + +#include +#include +#include + + +#elif defined(__TANDEM) + +// This case is for the Tandem Business Unit of Compaq Computer Corporation. +// The Tandem case must precede the AT&T case, +// because the Tandem c89 compiler also defines __cplusplus. + +#include "port_tandem.hpp" + +#elif defined(__cplusplus) +// AT&Ts cfront +#define INTERFACE +#define IMPLEMENTATION +#include +#define signed +// #include +inline int min( int a, int b) { return a < b ? a : b; } +inline int max( int a, int b) { return a > b ? a : b; } + +#else // All other machines + +#define signed +extern "C" void bcopy(void *b1, void *b2, int len); +inline int min( int a, int b) { return a < b ? a : b; } +inline int max( int a, int b) { return a > b ? a : b; } + +#endif + +//----------------------------------------------------------------------------- +// Safer memory allocations +#ifdef SAFE_MEMORY +#define malloc(size) safe_malloc(__FILE__,__LINE__,size) +#define free(ptr) safe_free(__FILE__,__LINE__,ptr) +#define realloc(ptr,size) safe_realloc(__FILE__,__LINE__,ptr,size) +#define calloc(nitems,size) safe_calloc(__FILE__,__LINE__,nitems,size) +#define strdup(ptr) safe_strdup(__FILE__,__LINE__,ptr) +extern void *safe_malloc (const char *file, unsigned line, unsigned size); +extern void safe_free (const char *file, unsigned line, void *ptr); +extern void *safe_calloc (const char *file, unsigned line, unsigned nitems, unsigned size); +extern void *safe_realloc(const char *file, unsigned line, void *ptr, unsigned size); +extern char *safe_strdup (const char *file, unsigned line, const char *src); +inline void *operator new( size_t size ) { return malloc(size); } +inline void operator delete( void *ptr ) { free(ptr); } +#endif + +//----------------------------------------------------------------------------- +// And now, the bit-size-specified integer sizes +typedef signed char int8; +typedef unsigned char uint8; +typedef unsigned char byte; + +// All uses of *int16 changed to 32-bit to speed up compiler on Intel +//typedef signed short int16; // Exactly 16bits signed +//typedef unsigned short uint16; // Exactly 16bits unsigned +//const unsigned int min_uint16 = 0x0000; // smallest uint16 +//const unsigned int max_uint16 = 0xFFFF; // largest uint16 + +typedef unsigned int uint; // When you need a fast >=16bit unsigned value +/*typedef int int; */ // When you need a fast >=16bit value +const unsigned int max_uint = (uint)-1; +typedef int32_t int32; // Exactly 32bits signed +typedef uint32_t uint32; // Exactly 32bits unsigned + +// Bit-sized floating point and long thingies +#ifndef __TANDEM +// Do not define these for Tandem, because they conflict with typedefs in softieee.h. +typedef float float32; // 32-bit float +typedef double float64; // 64-bit float +#endif // __TANDEM + +typedef jlong int64; // Java long for my 64-bit type +typedef julong uint64; // Java long for my 64-bit type + +//----------------------------------------------------------------------------- +// Nice constants +uint32 gcd( uint32 x, uint32 y ); +int ff1( uint32 mask ); +int fh1( uint32 mask ); +uint32 rotate32( uint32 x, int32 cnt ); + + +//----------------------------------------------------------------------------- +extern uint32 heap_totalmem; // Current total memory allocation +extern uint32 heap_highwater; // Highwater mark to date for memory usage + +#endif // _PORT_ diff --git a/hotspot/src/share/vm/libadt/set.cpp b/hotspot/src/share/vm/libadt/set.cpp new file mode 100644 index 00000000000..fd2a0bb4212 --- /dev/null +++ b/hotspot/src/share/vm/libadt/set.cpp @@ -0,0 +1,166 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Sets - An Abstract Data Type + +#include "incls/_precompiled.incl" +#include "incls/_set.cpp.incl" + +// %%%%% includes not needed with AVM framework - Ungar +// #include "port.hpp" +//IMPLEMENTATION +// #include "set.hpp" + +#include +#include +#include +#include + +// Not needed and it causes terouble for gcc. +// +// #include + +//-------------------------Virtual Functions----------------------------------- +// These functions MUST be implemented by the inheriting class. +class SparseSet; +/* Removed for MCC BUG + Set::operator const SparseSet*() const { assert(0); return NULL; } */ +const SparseSet *Set::asSparseSet() const { assert(0); return NULL; } +class VectorSet; +/* Removed for MCC BUG + Set::operator const VectorSet*() const { assert(0); return NULL; } */ +const VectorSet *Set::asVectorSet() const { assert(0); return NULL; } +class ListSet; +/* Removed for MCC BUG + Set::operator const ListSet*() const { assert(0); return NULL; } */ +const ListSet *Set::asListSet() const { assert(0); return NULL; } +class CoSet; +/* Removed for MCC BUG + Set::operator const CoSet*() const { assert(0); return NULL; } */ +const CoSet *Set::asCoSet() const { assert(0); return NULL; } + +//------------------------------setstr----------------------------------------- +// Create a string with a printable representation of a set. +// The caller must deallocate the string. +char *Set::setstr() const +{ + if( !this ) return os::strdup("{no set}"); + Set &set = clone(); // Virtually copy the basic set. + set.Sort(); // Sort elements for in-order retrieval + + uint len = 128; // Total string space + char *buf = NEW_C_HEAP_ARRAY(char,len);// Some initial string space + + register char *s = buf; // Current working string pointer + *s++ = '{'; + *s = '\0'; + + // For all elements of the Set + uint hi = (uint)-2, lo = (uint)-2; + for( SetI i(&set); i.test(); ++i ) { + if( hi+1 == i.elem ) { // Moving sequentially thru range? + hi = i.elem; // Yes, just update hi end of range + } else { // Else range ended + if( buf+len-s < 25 ) { // Generous trailing space for upcoming numbers + int offset = (int)(s-buf);// Not enuf space; compute offset into buffer + len <<= 1; // Double string size + buf = REALLOC_C_HEAP_ARRAY(char,buf,len); // Reallocate doubled size + s = buf+offset; // Get working pointer into new bigger buffer + } + if( lo != (uint)-2 ) { // Startup? No! Then print previous range. + if( lo != hi ) sprintf(s,"%d-%d,",lo,hi); + else sprintf(s,"%d,",lo); + s += strlen(s); // Advance working string + } + hi = lo = i.elem; + } + } + if( lo != (uint)-2 ) { + if( buf+len-s < 25 ) { // Generous trailing space for upcoming numbers + int offset = (int)(s-buf);// Not enuf space; compute offset into buffer + len <<= 1; // Double string size + buf = (char*)ReallocateHeap(buf,len); // Reallocate doubled size + s = buf+offset; // Get working pointer into new bigger buffer + } + if( lo != hi ) sprintf(s,"%d-%d}",lo,hi); + else sprintf(s,"%d}",lo); + } else strcat(s,"}"); + // Don't delete the clone 'set' since it is allocated on Arena. + return buf; +} + +//------------------------------print------------------------------------------ +// Handier print routine +void Set::print() const +{ + char *printable_set = setstr(); + tty->print_cr(printable_set); + FreeHeap(printable_set); +} + +//------------------------------parse------------------------------------------ +// Convert a textual representation of a Set, to a Set and union into "this" +// Set. Return the amount of text parsed in "len", or zero in "len". +int Set::parse(const char *s) +{ + register char c; // Parse character + register const char *t = s; // Save the starting position of s. + do c = *s++; // Skip characters + while( c && (c <= ' ') ); // Till no more whitespace or EOS + if( c != '{' ) return 0; // Oops, not a Set openner + if( *s == '}' ) return 2; // The empty Set + + // Sets are filled with values of the form "xx," or "xx-yy," with the comma + // a "}" at the very end. + while( 1 ) { // While have elements in the Set + char *u; // Pointer to character ending parse + uint hi, i; // Needed for range handling below + uint elem = (uint)strtoul(s,&u,10);// Get element + if( u == s ) return 0; // Bogus crude + s = u; // Skip over the number + c = *s++; // Get the number seperator + switch ( c ) { // Different seperators + case '}': // Last simple element + case ',': // Simple element + (*this) <<= elem; // Insert the simple element into the Set + break; // Go get next element + case '-': // Range + hi = (uint)strtoul(s,&u,10); // Get element + if( u == s ) return 0; // Bogus crude + for( i=elem; i<=hi; i++ ) + (*this) <<= i; // Insert the entire range into the Set + s = u; // Skip over the number + c = *s++; // Get the number seperator + break; + } + if( c == '}' ) break; // End of the Set + if( c != ',' ) return 0; // Bogus garbage + } + return (int)(s-t); // Return length parsed +} + +//------------------------------Iterator--------------------------------------- +SetI_::~SetI_() +{ +} diff --git a/hotspot/src/share/vm/libadt/set.hpp b/hotspot/src/share/vm/libadt/set.hpp new file mode 100644 index 00000000000..d135a512994 --- /dev/null +++ b/hotspot/src/share/vm/libadt/set.hpp @@ -0,0 +1,251 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _SET_ +#define _SET_ +// Sets - An Abstract Data Type + +// Should not manually include these in AVM framework. %%%%% - Ungar +// #ifndef _PORT_ +// #include "port.hpp" +// #endif // _PORT_ +//INTERFACE + +class SparseSet; +class VectorSet; +class ListSet; +class CoSet; + +class ostream; +class SetI_; + +// These sets can grow or shrink, based on the initial size and the largest +// element currently in them. Basically, they allow a bunch of bits to be +// grouped together, tested, set & cleared, intersected, etc. The basic +// Set class is an abstract class, and cannot be constructed. Instead, +// one of VectorSet, SparseSet, or ListSet is created. Each variation has +// different asymptotic running times for different operations, and different +// constants of proportionality as well. +// {n = number of elements, N = largest element} + +// VectorSet SparseSet ListSet +// Create O(N) O(1) O(1) +// Clear O(N) O(1) O(1) +// Insert O(1) O(1) O(log n) +// Delete O(1) O(1) O(log n) +// Member O(1) O(1) O(log n) +// Size O(N) O(1) O(1) +// Copy O(N) O(n) O(n) +// Union O(N) O(n) O(n log n) +// Intersect O(N) O(n) O(n log n) +// Difference O(N) O(n) O(n log n) +// Equal O(N) O(n) O(n log n) +// ChooseMember O(N) O(1) O(1) +// Sort O(1) O(n log n) O(1) +// Forall O(N) O(n) O(n) +// Complement O(1) O(1) O(1) + +// TIME: N/32 n 8*n Accesses +// SPACE: N/8 4*N+4*n 8*n Bytes + +// Create: Make an empty set +// Clear: Remove all the elements of a Set +// Insert: Insert an element into a Set; duplicates are ignored +// Delete: Removes an element from a Set +// Member: Tests for membership in a Set +// Size: Returns the number of members of a Set +// Copy: Copy or assign one Set to another +// Union: Union 2 sets together +// Intersect: Intersect 2 sets together +// Difference: Compute A & !B; remove from set A those elements in set B +// Equal: Test for equality between 2 sets +// ChooseMember Pick a random member +// Sort: If no other operation changes the set membership, a following +// Forall will iterate the members in ascending order. +// Forall: Iterate over the elements of a Set. Operations that modify +// the set membership during iteration work, but the iterator may +// skip any member or duplicate any member. +// Complement: Only supported in the Co-Set variations. It adds a small +// constant-time test to every Set operation. +// +// PERFORMANCE ISSUES: +// If you "cast away" the specific set variation you are using, and then do +// operations on the basic "Set" object you will pay a virtual function call +// to get back the specific set variation. On the other hand, using the +// generic Set means you can change underlying implementations by just +// changing the initial declaration. Examples: +// void foo(VectorSet vs1, VectorSet vs2) { vs1 |= vs2; } +// "foo" must be called with a VectorSet. The vector set union operation +// is called directly. +// void foo(Set vs1, Set vs2) { vs1 |= vs2; } +// "foo" may be called with *any* kind of sets; suppose it is called with +// VectorSets. Two virtual function calls are used to figure out the that vs1 +// and vs2 are VectorSets. In addition, if vs2 is not a VectorSet then a +// temporary VectorSet copy of vs2 will be made before the union proceeds. +// +// VectorSets have a small constant. Time and space are proportional to the +// largest element. Fine for dense sets and largest element < 10,000. +// SparseSets have a medium constant. Time is proportional to the number of +// elements, space is proportional to the largest element. +// Fine (but big) with the largest element < 100,000. +// ListSets have a big constant. Time *and space* are proportional to the +// number of elements. They work well for a few elements of *any* size +// (i.e. sets of pointers)! + +//------------------------------Set-------------------------------------------- +class Set : public ResourceObj { + public: + + // Creates a new, empty set. + // DO NOT CONSTRUCT A Set. THIS IS AN ABSTRACT CLASS, FOR INHERITENCE ONLY + Set(Arena *arena) : _set_arena(arena) {}; + + // Creates a new set from an existing set + // DO NOT CONSTRUCT A Set. THIS IS AN ABSTRACT CLASS, FOR INHERITENCE ONLY + Set(const Set &) {}; + + // Set assignment; deep-copy guts + virtual Set &operator =(const Set &s)=0; + virtual Set &clone(void) const=0; + + // Virtual destructor + virtual ~Set() {}; + + // Add member to set + virtual Set &operator <<=(uint elem)=0; + // virtual Set operator << (uint elem); + + // Delete member from set + virtual Set &operator >>=(uint elem)=0; + // virtual Set operator >> (uint elem); + + // Membership test. Result is Zero (absent)/ Non-Zero (present) + virtual int operator [](uint elem) const=0; + + // Intersect sets + virtual Set &operator &=(const Set &s)=0; + // virtual Set operator & (const Set &s) const; + + // Union sets + virtual Set &operator |=(const Set &s)=0; + // virtual Set operator | (const Set &s) const; + + // Difference sets + virtual Set &operator -=(const Set &s)=0; + // virtual Set operator - (const Set &s) const; + + // Tests for equality. Result is Zero (false)/ Non-Zero (true) + virtual int operator ==(const Set &s) const=0; + int operator !=(const Set &s) const { return !(*this == s); } + virtual int disjoint(const Set &s) const=0; + + // Tests for strict subset. Result is Zero (false)/ Non-Zero (true) + virtual int operator < (const Set &s) const=0; + int operator > (const Set &s) const { return s < *this; } + + // Tests for subset. Result is Zero (false)/ Non-Zero (true) + virtual int operator <=(const Set &s) const=0; + int operator >=(const Set &s) const { return s <= *this; } + + // Return any member of the Set. Undefined if the Set is empty. + virtual uint getelem(void) const=0; + + // Clear all the elements in the Set + virtual void Clear(void)=0; + + // Return the number of members in the Set + virtual uint Size(void) const=0; + + // If an iterator follows a "Sort()" without any Set-modifying operations + // inbetween then the iterator will visit the elements in ascending order. + virtual void Sort(void)=0; + + // Convert a set to printable string in an allocated buffer. + // The caller must deallocate the string. + virtual char *setstr(void) const; + + // Print the Set on "stdout". Can be conveniently called in the debugger + void print() const; + + // Parse text from the string into the Set. Return length parsed. + virtual int parse(const char *s); + + // Convert a generic Set to a specific Set + /* Removed for MCC BUG + virtual operator const SparseSet* (void) const; + virtual operator const VectorSet* (void) const; + virtual operator const ListSet * (void) const; + virtual operator const CoSet * (void) const; */ + virtual const SparseSet *asSparseSet(void) const; + virtual const VectorSet *asVectorSet(void) const; + virtual const ListSet *asListSet (void) const; + virtual const CoSet *asCoSet (void) const; + + // Hash the set. Sets of different types but identical elements will NOT + // hash the same. Same set type, same elements WILL hash the same. + virtual int hash() const = 0; + +protected: + friend class SetI; + friend class CoSet; + virtual class SetI_ *iterate(uint&) const=0; + + // Need storeage for the set + Arena *_set_arena; +}; +typedef Set&((*Set_Constructor)(Arena *arena)); +extern Set &ListSet_Construct(Arena *arena); +extern Set &VectorSet_Construct(Arena *arena); +extern Set &SparseSet_Construct(Arena *arena); + +//------------------------------Iteration-------------------------------------- +// Loop thru all elements of the set, setting "elem" to the element numbers +// in random order. Inserted or deleted elements during this operation may +// or may not be iterated over; untouched elements will be affected once. + +// Usage: for( SetI i(s); i.test(); i++ ) { body = i.elem; } ...OR... +// for( i.reset(s); i.test(); i++ ) { body = i.elem; } + +class SetI_ : public ResourceObj { +protected: + friend class SetI; + virtual ~SetI_(); + virtual uint next(void)=0; + virtual int test(void)=0; +}; + +class SetI { +protected: + SetI_ *impl; +public: + uint elem; // The publically accessible element + + SetI( const Set *s ) { impl = s->iterate(elem); } + ~SetI() { delete impl; } + void reset( const Set *s ) { delete impl; impl = s->iterate(elem); } + void operator ++(void) { elem = impl->next(); } + int test(void) { return impl->test(); } +}; + +#endif // _SET_ diff --git a/hotspot/src/share/vm/libadt/vectset.cpp b/hotspot/src/share/vm/libadt/vectset.cpp new file mode 100644 index 00000000000..1f0ccba7cc1 --- /dev/null +++ b/hotspot/src/share/vm/libadt/vectset.cpp @@ -0,0 +1,390 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Vector Sets - An Abstract Data Type + +#include "incls/_precompiled.incl" +#include "incls/_vectset.cpp.incl" + +// %%%%% includes not needed with AVM framework - Ungar +// #include "port.hpp" +//IMPLEMENTATION +// #include "vectset.hpp" + +// BitsInByte is a lookup table which tells the number of bits that +// are in the looked-up number. It is very useful in VectorSet_Size. + +uint8 bitsInByte[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + +//------------------------------VectorSet-------------------------------------- +// Create a new, empty Set. +VectorSet::VectorSet(Arena *arena) : Set(arena) { + size = 2; // Small initial size + data = (uint32 *)_set_arena->Amalloc(size*sizeof(uint32)); + data[0] = 0; // No elements + data[1] = 0; +} + +//------------------------------Construct-------------------------------------- +Set &VectorSet_Construct(Arena *arena) +{ + return *(new VectorSet(arena)); +} + +//------------------------------operator=-------------------------------------- +Set &VectorSet::operator = (const Set &set) +{ + if( &set == this ) return *this; + FREE_FAST(data); + // The cast is a virtual function that checks that "set" is a VectorSet. + slamin(*(set.asVectorSet())); + return *this; +} + +//------------------------------slamin----------------------------------------- +// Initialize one set with another. No regard is made to the existing Set. +void VectorSet::slamin(const VectorSet& s) +{ + size = s.size; // Use new size + data = (uint32*)s._set_arena->Amalloc(size*sizeof(uint32)); // Make array of required size + memcpy( data, s.data, size*sizeof(uint32) ); // Fill the array +} + +//------------------------------grow------------------------------------------- +// Expand the existing set to a bigger size +void VectorSet::grow( uint newsize ) +{ + newsize = (newsize+31) >> 5; // Convert to longwords + uint x = size; + while( x < newsize ) x <<= 1; + data = (uint32 *)_set_arena->Arealloc(data, size*sizeof(uint32), x*sizeof(uint32)); + memset((char *)(data + size), 0, (x - size)*sizeof(uint32)); + size = x; +} + +//------------------------------operator<<=------------------------------------ +// Insert a member into an existing Set. +Set &VectorSet::operator <<= (uint elem) +{ + register uint word = elem >> 5; // Get the longword offset + register uint32 mask = 1L << (elem & 31); // Get bit mask + + if( word >= size ) // Need to grow set? + grow(elem+1); // Then grow it + data[word] |= mask; // Set new bit + return *this; +} + +//------------------------------operator>>=------------------------------------ +// Delete a member from an existing Set. +Set &VectorSet::operator >>= (uint elem) +{ + register uint word = elem >> 5; // Get the longword offset + if( word >= size ) // Beyond the last? + return *this; // Then it's clear & return clear + register uint32 mask = 1L << (elem & 31); // Get bit mask + data[word] &= ~mask; // Clear bit + return *this; +} + +//------------------------------operator&=------------------------------------- +// Intersect one set into another. +VectorSet &VectorSet::operator &= (const VectorSet &s) +{ + // NOTE: The intersection is never any larger than the smallest set. + if( s.size < size ) size = s.size; // Get smaller size + register uint32 *u1 = data; // Pointer to the destination data + register uint32 *u2 = s.data; // Pointer to the source data + for( uint i=0; i> 5; // Get the longword offset + if( word >= size ) // Beyond the last? + return 0; // Then it's clear + register uint32 mask = 1L << (elem & 31); // Get bit mask + return ((data[word] & mask))!=0; // Return the sense of the bit +} + +//------------------------------getelem---------------------------------------- +// Get any element from the set. +uint VectorSet::getelem(void) const +{ + uint i; // Exit value of loop + for( i=0; i>=1 ); + return (i<<5)+j; +} + +//------------------------------Clear------------------------------------------ +// Clear a set +void VectorSet::Clear(void) +{ + if( size > 100 ) { // Reclaim storage only if huge + FREE_RESOURCE_ARRAY(uint32,data,size); + size = 2; // Small initial size + data = NEW_RESOURCE_ARRAY(uint32,size); + } + memset( data, 0, size*sizeof(uint32) ); +} + +//------------------------------Size------------------------------------------- +// Return number of elements in a Set +uint VectorSet::Size(void) const +{ + uint sum = 0; // Cumulative size so far. + uint8 *currByte = (uint8*)data; + for( uint32 i = 0; i < (size<<2); i++) // While have bytes to process + sum += bitsInByte[*currByte++]; // Add bits in current byte to size. + return sum; +} + +//------------------------------Sort------------------------------------------- +// Sort the elements for the next forall statement +void VectorSet::Sort(void) +{ +} + +//------------------------------hash------------------------------------------- +int VectorSet::hash() const +{ + uint32 _xor = 0; + uint lim = ((size<4)?size:4); + for( uint i = 0; i < lim; i++ ) + _xor ^= data[i]; + return (int)_xor; +} + +//------------------------------iterate---------------------------------------- +SetI_ *VectorSet::iterate(uint &elem) const +{ + VSetI_ *foo = (new(ResourceObj::C_HEAP) VSetI_(this)); + elem = foo->next(); + return foo; +} + +//============================================================================= +//------------------------------VSetI_----------------------------------------- +// Initialize the innards of a VectorSet iterator +VSetI_::VSetI_( const VectorSet *vset ) : s(vset) +{ + i = (uint)-1L; + j = (uint)-1L; + mask = (unsigned)(1L<<31); +} + +//------------------------------next------------------------------------------- +// Find and return the next element of a vector set, or return garbage and +// make "VSetI_::test()" fail. +uint VSetI_::next(void) +{ + j++; // Next element in word + mask = (mask & max_jint) << 1;// Next bit in word + do { // Do While still have words + while( mask ) { // While have bits in word + if( s->data[i] & mask ) { // If found a bit + return (i<<5)+j; // Return the bit address + } + j++; // Skip to next bit + mask = (mask & max_jint) << 1; + } + j = 0; // No more bits in word; setup for next word + mask = 1; + for( i++; (isize) && (!s->data[i]); i++ ); // Skip to non-zero word + } while( isize ); + return max_juint; // No element, iterated them all +} diff --git a/hotspot/src/share/vm/libadt/vectset.hpp b/hotspot/src/share/vm/libadt/vectset.hpp new file mode 100644 index 00000000000..17c48e3b047 --- /dev/null +++ b/hotspot/src/share/vm/libadt/vectset.hpp @@ -0,0 +1,176 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _VECTOR_SET_ +#define _VECTOR_SET_ +// Vector Sets - An Abstract Data Type +//INTERFACE + +// These sets can grow or shrink, based on the initial size and the largest +// element currently in them. Slow and bulky for sparse sets, these sets +// are super for dense sets. They are fast and compact when dense. + +// TIME: +// O(1) - Insert, Delete, Member, Sort +// O(max_element) - Create, Clear, Size, Copy, Union, Intersect, Difference, +// Equal, ChooseMember, Forall + +// SPACE: (max_element)/(8*sizeof(int)) + + +//------------------------------VectorSet-------------------------------------- +class VectorSet : public Set { +friend class VectorSetI; // Friendly iterator class +protected: + uint size; // Size of data IN LONGWORDS (32bits) + uint32 *data; // The data, bit packed + + void slamin( const VectorSet& s ); // Initialize one set with another + int compare(const VectorSet &s) const; // Compare set contents + void grow(uint newsize); // Grow vector to required bitsize + +public: + VectorSet(Arena *arena); // Creates a new, empty set. + VectorSet(const VectorSet &s) : Set(s._set_arena) {slamin(s);} // Set clone; deep-copy guts + Set &operator =(const Set &s); // Set clone; deep-copy guts + VectorSet &operator =(const VectorSet &s) // Set clone; deep-copy guts + { if( &s != this ) { slamin(s); } return *this; } + ~VectorSet() {} + Set &clone(void) const { return *(new VectorSet(*this)); } + + Set &operator <<=(uint elem); // Add member to set + VectorSet operator << (uint elem) // Add member to new set + { VectorSet foo(*this); foo <<= elem; return foo; } + Set &operator >>=(uint elem); // Delete member from set + VectorSet operator >> (uint elem) // Delete member from new set + { VectorSet foo(*this); foo >>= elem; return foo; } + + VectorSet &operator &=(const VectorSet &s); // Intersect sets into first set + Set &operator &=(const Set &s); // Intersect sets into first set + VectorSet operator & (const VectorSet &s) const + { VectorSet foo(*this); foo &= s; return foo; } + + VectorSet &operator |=(const VectorSet &s); // Intersect sets into first set + Set &operator |=(const Set &s); // Intersect sets into first set + VectorSet operator | (const VectorSet &s) const + { VectorSet foo(*this); foo |= s; return foo; } + + VectorSet &operator -=(const VectorSet &s); // Intersect sets into first set + Set &operator -=(const Set &s); // Intersect sets into first set + VectorSet operator - (const VectorSet &s) const + { VectorSet foo(*this); foo -= s; return foo; } + + int operator ==(const VectorSet &s) const; // True if sets are equal + int operator ==(const Set &s) const; // True if sets are equal + int operator < (const VectorSet &s) const; // True if strict subset + int operator < (const Set &s) const; // True if strict subset + int operator <=(const VectorSet &s) const; // True if subset relation holds. + int operator <=(const Set &s) const; // True if subset relation holds. + int disjoint (const Set &s) const; // True if sets are disjoint + + int operator [](uint elem) const; // Test for membership + uint getelem(void) const; // Return a random element + void Clear(void); // Clear a set + uint Size(void) const; // Number of elements in the Set. + void Sort(void); // Sort before iterating + int hash() const; // Hash function + + /* Removed for MCC BUG + operator const VectorSet* (void) const { return this; } */ + const VectorSet *asVectorSet() const { return this; } + + // Expose internals for speed-critical fast iterators + uint word_size() const { return size; } + uint32 *EXPOSE() const { return data; } + + // Fast inlined "test and set". Replaces the idiom: + // if( visited[idx] ) return; + // visited <<= idx; + // With: + // if( visited.test_set(idx) ) return; + // + int test_set( uint elem ) { + uint word = elem >> 5; // Get the longword offset + if( word >= size ) // Beyond the last? + return test_set_grow(elem); // Then grow; set; return 0; + uint32 mask = 1L << (elem & 31); // Get bit mask + uint32 datum = data[word] & mask;// Get bit + data[word] |= mask; // Set bit + return datum; // Return bit + } + int test_set_grow( uint elem ) { // Insert & return 0; + (*this) <<= elem; // Insert into set + return 0; // Return 0! + } + + // Fast inlined test + int test( uint elem ) const { + uint word = elem >> 5; // Get the longword offset + if( word >= size ) return 0; // Beyond the last? + uint32 mask = 1L << (elem & 31); // Get bit mask + return data[word] & mask; // Get bit + } + + // Fast inlined set + void set( uint elem ) { + uint word = elem >> 5; // Get the longword offset + if( word >= size ) { // Beyond the last? + test_set_grow(elem); // Then grow and set + } else { + uint32 mask = 1L << (elem & 31); // Get bit mask + data[word] |= mask; // Set bit + } + } + + +private: + friend class VSetI_; + SetI_ *iterate(uint&) const; +}; + +//------------------------------Iteration-------------------------------------- +// Loop thru all elements of the set, setting "elem" to the element numbers +// in random order. Inserted or deleted elements during this operation may +// or may not be iterated over; untouched elements will be affected once. +// Usage: for( VectorSetI i(s); i.test(); i++ ) { body = i.elem; } + +class VSetI_ : public SetI_ { + friend class VectorSet; + friend class VectorSetI; + const VectorSet *s; + uint i, j; + uint32 mask; + VSetI_(const VectorSet *vset); + uint next(void); + int test(void) { return i < s->size; } +}; + +class VectorSetI : public SetI { +public: + VectorSetI( const VectorSet *s ) : SetI(s) { } + void operator ++(void) { elem = ((VSetI_*)impl)->next(); } + int test(void) { return ((VSetI_*)impl)->test(); } +}; + +#endif // _VECTOR_SET_ diff --git a/hotspot/src/share/vm/memory/allocation.cpp b/hotspot/src/share/vm/memory/allocation.cpp new file mode 100644 index 00000000000..757e3932ea5 --- /dev/null +++ b/hotspot/src/share/vm/memory/allocation.cpp @@ -0,0 +1,539 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_allocation.cpp.incl" + +void* CHeapObj::operator new(size_t size){ + return (void *) AllocateHeap(size, "CHeapObj-new"); +} + +void CHeapObj::operator delete(void* p){ + FreeHeap(p); +} + +void* StackObj::operator new(size_t size) { ShouldNotCallThis(); return 0; }; +void StackObj::operator delete(void* p) { ShouldNotCallThis(); }; +void* _ValueObj::operator new(size_t size) { ShouldNotCallThis(); return 0; }; +void _ValueObj::operator delete(void* p) { ShouldNotCallThis(); }; + +void* ResourceObj::operator new(size_t size, allocation_type type) { + address res; + switch (type) { + case C_HEAP: + res = (address)AllocateHeap(size, "C_Heap: ResourceOBJ"); + break; + case RESOURCE_AREA: + res = (address)operator new(size); + break; + default: + ShouldNotReachHere(); + } + // Set allocation type in the resource object for assertion checks. + DEBUG_ONLY(((ResourceObj *)res)->_allocation = type;) + return res; +} + +void ResourceObj::operator delete(void* p) { + assert(((ResourceObj *)p)->allocated_on_C_heap(), + "delete only allowed for C_HEAP objects"); + FreeHeap(p); +} + +void trace_heap_malloc(size_t size, const char* name, void* p) { + // A lock is not needed here - tty uses a lock internally + tty->print_cr("Heap malloc " INTPTR_FORMAT " %7d %s", p, size, name == NULL ? "" : name); +} + + +void trace_heap_free(void* p) { + // A lock is not needed here - tty uses a lock internally + tty->print_cr("Heap free " INTPTR_FORMAT, p); +} + +bool warn_new_operator = false; // see vm_main + +//-------------------------------------------------------------------------------------- +// ChunkPool implementation + +// MT-safe pool of chunks to reduce malloc/free thrashing +// NB: not using Mutex because pools are used before Threads are initialized +class ChunkPool { + Chunk* _first; // first cached Chunk; its first word points to next chunk + size_t _num_chunks; // number of unused chunks in pool + size_t _num_used; // number of chunks currently checked out + const size_t _size; // size of each chunk (must be uniform) + + // Our three static pools + static ChunkPool* _large_pool; + static ChunkPool* _medium_pool; + static ChunkPool* _small_pool; + + // return first element or null + void* get_first() { + Chunk* c = _first; + if (_first) { + _first = _first->next(); + _num_chunks--; + } + return c; + } + + public: + // All chunks in a ChunkPool has the same size + ChunkPool(size_t size) : _size(size) { _first = NULL; _num_chunks = _num_used = 0; } + + // Allocate a new chunk from the pool (might expand the pool) + void* allocate(size_t bytes) { + assert(bytes == _size, "bad size"); + void* p = NULL; + { ThreadCritical tc; + _num_used++; + p = get_first(); + if (p == NULL) p = os::malloc(bytes); + } + if (p == NULL) + vm_exit_out_of_memory(bytes, "ChunkPool::allocate"); + + return p; + } + + // Return a chunk to the pool + void free(Chunk* chunk) { + assert(chunk->length() + Chunk::aligned_overhead_size() == _size, "bad size"); + ThreadCritical tc; + _num_used--; + + // Add chunk to list + chunk->set_next(_first); + _first = chunk; + _num_chunks++; + } + + // Prune the pool + void free_all_but(size_t n) { + // if we have more than n chunks, free all of them + ThreadCritical tc; + if (_num_chunks > n) { + // free chunks at end of queue, for better locality + Chunk* cur = _first; + for (size_t i = 0; i < (n - 1) && cur != NULL; i++) cur = cur->next(); + + if (cur != NULL) { + Chunk* next = cur->next(); + cur->set_next(NULL); + cur = next; + + // Free all remaining chunks + while(cur != NULL) { + next = cur->next(); + os::free(cur); + _num_chunks--; + cur = next; + } + } + } + } + + // Accessors to preallocated pool's + static ChunkPool* large_pool() { assert(_large_pool != NULL, "must be initialized"); return _large_pool; } + static ChunkPool* medium_pool() { assert(_medium_pool != NULL, "must be initialized"); return _medium_pool; } + static ChunkPool* small_pool() { assert(_small_pool != NULL, "must be initialized"); return _small_pool; } + + static void initialize() { + _large_pool = new ChunkPool(Chunk::size + Chunk::aligned_overhead_size()); + _medium_pool = new ChunkPool(Chunk::medium_size + Chunk::aligned_overhead_size()); + _small_pool = new ChunkPool(Chunk::init_size + Chunk::aligned_overhead_size()); + } +}; + +ChunkPool* ChunkPool::_large_pool = NULL; +ChunkPool* ChunkPool::_medium_pool = NULL; +ChunkPool* ChunkPool::_small_pool = NULL; + + +void chunkpool_init() { + ChunkPool::initialize(); +} + + +//-------------------------------------------------------------------------------------- +// ChunkPoolCleaner implementation + +class ChunkPoolCleaner : public PeriodicTask { + enum { CleaningInterval = 5000, // cleaning interval in ms + BlocksToKeep = 5 // # of extra blocks to keep + }; + + public: + ChunkPoolCleaner() : PeriodicTask(CleaningInterval) {} + void task() { + ChunkPool::small_pool()->free_all_but(BlocksToKeep); + ChunkPool::medium_pool()->free_all_but(BlocksToKeep); + ChunkPool::large_pool()->free_all_but(BlocksToKeep); + } +}; + +//-------------------------------------------------------------------------------------- +// Chunk implementation + +void* Chunk::operator new(size_t requested_size, size_t length) { + // requested_size is equal to sizeof(Chunk) but in order for the arena + // allocations to come out aligned as expected the size must be aligned + // to expected arean alignment. + // expect requested_size but if sizeof(Chunk) doesn't match isn't proper size we must align it. + assert(ARENA_ALIGN(requested_size) == aligned_overhead_size(), "Bad alignment"); + size_t bytes = ARENA_ALIGN(requested_size) + length; + switch (length) { + case Chunk::size: return ChunkPool::large_pool()->allocate(bytes); + case Chunk::medium_size: return ChunkPool::medium_pool()->allocate(bytes); + case Chunk::init_size: return ChunkPool::small_pool()->allocate(bytes); + default: { + void *p = os::malloc(bytes); + if (p == NULL) + vm_exit_out_of_memory(bytes, "Chunk::new"); + return p; + } + } +} + +void Chunk::operator delete(void* p) { + Chunk* c = (Chunk*)p; + switch (c->length()) { + case Chunk::size: ChunkPool::large_pool()->free(c); break; + case Chunk::medium_size: ChunkPool::medium_pool()->free(c); break; + case Chunk::init_size: ChunkPool::small_pool()->free(c); break; + default: os::free(c); + } +} + +Chunk::Chunk(size_t length) : _len(length) { + _next = NULL; // Chain on the linked list +} + + +void Chunk::chop() { + Chunk *k = this; + while( k ) { + Chunk *tmp = k->next(); + // clear out this chunk (to detect allocation bugs) + if (ZapResourceArea) memset(k->bottom(), badResourceValue, k->length()); + delete k; // Free chunk (was malloc'd) + k = tmp; + } +} + +void Chunk::next_chop() { + _next->chop(); + _next = NULL; +} + + +void Chunk::start_chunk_pool_cleaner_task() { +#ifdef ASSERT + static bool task_created = false; + assert(!task_created, "should not start chuck pool cleaner twice"); + task_created = true; +#endif + ChunkPoolCleaner* cleaner = new ChunkPoolCleaner(); + cleaner->enroll(); +} + +//------------------------------Arena------------------------------------------ + +Arena::Arena(size_t init_size) { + size_t round_size = (sizeof (char *)) - 1; + init_size = (init_size+round_size) & ~round_size; + _first = _chunk = new (init_size) Chunk(init_size); + _hwm = _chunk->bottom(); // Save the cached hwm, max + _max = _chunk->top(); + set_size_in_bytes(init_size); +} + +Arena::Arena() { + _first = _chunk = new (Chunk::init_size) Chunk(Chunk::init_size); + _hwm = _chunk->bottom(); // Save the cached hwm, max + _max = _chunk->top(); + set_size_in_bytes(Chunk::init_size); +} + +Arena::Arena(Arena *a) : _chunk(a->_chunk), _hwm(a->_hwm), _max(a->_max), _first(a->_first) { + set_size_in_bytes(a->size_in_bytes()); +} + +Arena *Arena::move_contents(Arena *copy) { + copy->destruct_contents(); + copy->_chunk = _chunk; + copy->_hwm = _hwm; + copy->_max = _max; + copy->_first = _first; + copy->set_size_in_bytes(size_in_bytes()); + // Destroy original arena + reset(); + return copy; // Return Arena with contents +} + +Arena::~Arena() { + destruct_contents(); +} + +// Destroy this arenas contents and reset to empty +void Arena::destruct_contents() { + if (UseMallocOnly && _first != NULL) { + char* end = _first->next() ? _first->top() : _hwm; + free_malloced_objects(_first, _first->bottom(), end, _hwm); + } + _first->chop(); + reset(); +} + + +// Total of all Chunks in arena +size_t Arena::used() const { + size_t sum = _chunk->length() - (_max-_hwm); // Size leftover in this Chunk + register Chunk *k = _first; + while( k != _chunk) { // Whilst have Chunks in a row + sum += k->length(); // Total size of this Chunk + k = k->next(); // Bump along to next Chunk + } + return sum; // Return total consumed space. +} + + +// Grow a new Chunk +void* Arena::grow( size_t x ) { + // Get minimal required size. Either real big, or even bigger for giant objs + size_t len = MAX2(x, (size_t) Chunk::size); + + Chunk *k = _chunk; // Get filled-up chunk address + _chunk = new (len) Chunk(len); + + if (_chunk == NULL) + vm_exit_out_of_memory(len * Chunk::aligned_overhead_size(), "Arena::grow"); + + if (k) k->set_next(_chunk); // Append new chunk to end of linked list + else _first = _chunk; + _hwm = _chunk->bottom(); // Save the cached hwm, max + _max = _chunk->top(); + set_size_in_bytes(size_in_bytes() + len); + void* result = _hwm; + _hwm += x; + return result; +} + + + +// Reallocate storage in Arena. +void *Arena::Arealloc(void* old_ptr, size_t old_size, size_t new_size) { + assert(new_size >= 0, "bad size"); + if (new_size == 0) return NULL; +#ifdef ASSERT + if (UseMallocOnly) { + // always allocate a new object (otherwise we'll free this one twice) + char* copy = (char*)Amalloc(new_size); + size_t n = MIN2(old_size, new_size); + if (n > 0) memcpy(copy, old_ptr, n); + Afree(old_ptr,old_size); // Mostly done to keep stats accurate + return copy; + } +#endif + char *c_old = (char*)old_ptr; // Handy name + // Stupid fast special case + if( new_size <= old_size ) { // Shrink in-place + if( c_old+old_size == _hwm) // Attempt to free the excess bytes + _hwm = c_old+new_size; // Adjust hwm + return c_old; + } + + // make sure that new_size is legal + size_t corrected_new_size = ARENA_ALIGN(new_size); + + // See if we can resize in-place + if( (c_old+old_size == _hwm) && // Adjusting recent thing + (c_old+corrected_new_size <= _max) ) { // Still fits where it sits + _hwm = c_old+corrected_new_size; // Adjust hwm + return c_old; // Return old pointer + } + + // Oops, got to relocate guts + void *new_ptr = Amalloc(new_size); + memcpy( new_ptr, c_old, old_size ); + Afree(c_old,old_size); // Mostly done to keep stats accurate + return new_ptr; +} + + +// Determine if pointer belongs to this Arena or not. +bool Arena::contains( const void *ptr ) const { +#ifdef ASSERT + if (UseMallocOnly) { + // really slow, but not easy to make fast + if (_chunk == NULL) return false; + char** bottom = (char**)_chunk->bottom(); + for (char** p = (char**)_hwm - 1; p >= bottom; p--) { + if (*p == ptr) return true; + } + for (Chunk *c = _first; c != NULL; c = c->next()) { + if (c == _chunk) continue; // current chunk has been processed + char** bottom = (char**)c->bottom(); + for (char** p = (char**)c->top() - 1; p >= bottom; p--) { + if (*p == ptr) return true; + } + } + return false; + } +#endif + if( (void*)_chunk->bottom() <= ptr && ptr < (void*)_hwm ) + return true; // Check for in this chunk + for (Chunk *c = _first; c; c = c->next()) { + if (c == _chunk) continue; // current chunk has been processed + if ((void*)c->bottom() <= ptr && ptr < (void*)c->top()) { + return true; // Check for every chunk in Arena + } + } + return false; // Not in any Chunk, so not in Arena +} + + +#ifdef ASSERT +void* Arena::malloc(size_t size) { + assert(UseMallocOnly, "shouldn't call"); + // use malloc, but save pointer in res. area for later freeing + char** save = (char**)internal_malloc_4(sizeof(char*)); + return (*save = (char*)os::malloc(size)); +} + +// for debugging with UseMallocOnly +void* Arena::internal_malloc_4(size_t x) { + assert( (x&(sizeof(char*)-1)) == 0, "misaligned size" ); + if (_hwm + x > _max) { + return grow(x); + } else { + char *old = _hwm; + _hwm += x; + return old; + } +} +#endif + + +//-------------------------------------------------------------------------------------- +// Non-product code + +#ifndef PRODUCT +// The global operator new should never be called since it will usually indicate +// a memory leak. Use CHeapObj as the base class of such objects to make it explicit +// that they're allocated on the C heap. +// Commented out in product version to avoid conflicts with third-party C++ native code. +// %% note this is causing a problem on solaris debug build. the global +// new is being called from jdk source and causing data corruption. +// src/share/native/sun/awt/font/fontmanager/textcache/hsMemory.cpp::hsSoftNew +// define CATCH_OPERATOR_NEW_USAGE if you want to use this. +#ifdef CATCH_OPERATOR_NEW_USAGE +void* operator new(size_t size){ + static bool warned = false; + if (!warned && warn_new_operator) + warning("should not call global (default) operator new"); + warned = true; + return (void *) AllocateHeap(size, "global operator new"); +} +#endif + +void AllocatedObj::print() const { print_on(tty); } +void AllocatedObj::print_value() const { print_value_on(tty); } + +void AllocatedObj::print_on(outputStream* st) const { + st->print_cr("AllocatedObj(" INTPTR_FORMAT ")", this); +} + +void AllocatedObj::print_value_on(outputStream* st) const { + st->print("AllocatedObj(" INTPTR_FORMAT ")", this); +} + +size_t Arena::_bytes_allocated = 0; + +AllocStats::AllocStats() { + start_mallocs = os::num_mallocs; + start_frees = os::num_frees; + start_malloc_bytes = os::alloc_bytes; + start_res_bytes = Arena::_bytes_allocated; +} + +int AllocStats::num_mallocs() { return os::num_mallocs - start_mallocs; } +size_t AllocStats::alloc_bytes() { return os::alloc_bytes - start_malloc_bytes; } +size_t AllocStats::resource_bytes() { return Arena::_bytes_allocated - start_res_bytes; } +int AllocStats::num_frees() { return os::num_frees - start_frees; } +void AllocStats::print() { + tty->print("%d mallocs (%ldK), %d frees, %ldK resrc", + num_mallocs(), alloc_bytes()/K, num_frees(), resource_bytes()/K); +} + + +// debugging code +inline void Arena::free_all(char** start, char** end) { + for (char** p = start; p < end; p++) if (*p) os::free(*p); +} + +void Arena::free_malloced_objects(Chunk* chunk, char* hwm, char* max, char* hwm2) { + assert(UseMallocOnly, "should not call"); + // free all objects malloced since resource mark was created; resource area + // contains their addresses + if (chunk->next()) { + // this chunk is full, and some others too + for (Chunk* c = chunk->next(); c != NULL; c = c->next()) { + char* top = c->top(); + if (c->next() == NULL) { + top = hwm2; // last junk is only used up to hwm2 + assert(c->contains(hwm2), "bad hwm2"); + } + free_all((char**)c->bottom(), (char**)top); + } + assert(chunk->contains(hwm), "bad hwm"); + assert(chunk->contains(max), "bad max"); + free_all((char**)hwm, (char**)max); + } else { + // this chunk was partially used + assert(chunk->contains(hwm), "bad hwm"); + assert(chunk->contains(hwm2), "bad hwm2"); + free_all((char**)hwm, (char**)hwm2); + } +} + + +ReallocMark::ReallocMark() { +#ifdef ASSERT + Thread *thread = ThreadLocalStorage::get_thread_slow(); + _nesting = thread->resource_area()->nesting(); +#endif +} + +void ReallocMark::check() { +#ifdef ASSERT + if (_nesting != Thread::current()->resource_area()->nesting()) { + fatal("allocation bug: array could grow within nested ResourceMark"); + } +#endif +} + +#endif // Non-product diff --git a/hotspot/src/share/vm/memory/allocation.hpp b/hotspot/src/share/vm/memory/allocation.hpp new file mode 100644 index 00000000000..4bdcbc1ccfa --- /dev/null +++ b/hotspot/src/share/vm/memory/allocation.hpp @@ -0,0 +1,409 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#define ARENA_ALIGN_M1 (((size_t)(ARENA_AMALLOC_ALIGNMENT)) - 1) +#define ARENA_ALIGN_MASK (~((size_t)ARENA_ALIGN_M1)) +#define ARENA_ALIGN(x) ((((size_t)(x)) + ARENA_ALIGN_M1) & ARENA_ALIGN_MASK) + +// All classes in the virtual machine must be subclassed +// by one of the following allocation classes: +// +// For objects allocated in the resource area (see resourceArea.hpp). +// - ResourceObj +// +// For objects allocated in the C-heap (managed by: free & malloc). +// - CHeapObj +// +// For objects allocated on the stack. +// - StackObj +// +// For embedded objects. +// - ValueObj +// +// For classes used as name spaces. +// - AllStatic +// +// The printable subclasses are used for debugging and define virtual +// member functions for printing. Classes that avoid allocating the +// vtbl entries in the objects should therefore not be the printable +// subclasses. +// +// The following macros and function should be used to allocate memory +// directly in the resource area or in the C-heap: +// +// NEW_RESOURCE_ARRAY(type,size) +// NEW_RESOURCE_OBJ(type) +// NEW_C_HEAP_ARRAY(type,size) +// NEW_C_HEAP_OBJ(type) +// char* AllocateHeap(size_t size, const char* name); +// void FreeHeap(void* p); +// +// C-heap allocation can be traced using +PrintHeapAllocation. +// malloc and free should therefore never called directly. + +// Base class for objects allocated in the C-heap. + +// In non product mode we introduce a super class for all allocation classes +// that supports printing. +// We avoid the superclass in product mode since some C++ compilers add +// a word overhead for empty super classes. + +#ifdef PRODUCT +#define ALLOCATION_SUPER_CLASS_SPEC +#else +#define ALLOCATION_SUPER_CLASS_SPEC : public AllocatedObj +class AllocatedObj { + public: + // Printing support + void print() const; + void print_value() const; + + virtual void print_on(outputStream* st) const; + virtual void print_value_on(outputStream* st) const; +}; +#endif + +class CHeapObj ALLOCATION_SUPER_CLASS_SPEC { + public: + void* operator new(size_t size); + void operator delete(void* p); + void* new_array(size_t size); +}; + +// Base class for objects allocated on the stack only. +// Calling new or delete will result in fatal error. + +class StackObj ALLOCATION_SUPER_CLASS_SPEC { + public: + void* operator new(size_t size); + void operator delete(void* p); +}; + +// Base class for objects used as value objects. +// Calling new or delete will result in fatal error. +// +// Portability note: Certain compilers (e.g. gcc) will +// always make classes bigger if it has a superclass, even +// if the superclass does not have any virtual methods or +// instance fields. The HotSpot implementation relies on this +// not to happen. So never make a ValueObj class a direct subclass +// of this object, but use the VALUE_OBJ_CLASS_SPEC class instead, e.g., +// like this: +// +// class A VALUE_OBJ_CLASS_SPEC { +// ... +// } +// +// With gcc and possible other compilers the VALUE_OBJ_CLASS_SPEC can +// be defined as a an empty string "". +// +class _ValueObj { + public: + void* operator new(size_t size); + void operator delete(void* p); +}; + +// Base class for classes that constitute name spaces. + +class AllStatic { + public: + AllStatic() { ShouldNotCallThis(); } + ~AllStatic() { ShouldNotCallThis(); } +}; + + +//------------------------------Chunk------------------------------------------ +// Linked list of raw memory chunks +class Chunk: public CHeapObj { + protected: + Chunk* _next; // Next Chunk in list + const size_t _len; // Size of this Chunk + public: + void* operator new(size_t size, size_t length); + void operator delete(void* p); + Chunk(size_t length); + + enum { + // default sizes; make them slightly smaller than 2**k to guard against + // buddy-system style malloc implementations +#ifdef _LP64 + slack = 40, // [RGV] Not sure if this is right, but make it + // a multiple of 8. +#else + slack = 20, // suspected sizeof(Chunk) + internal malloc headers +#endif + + init_size = 1*K - slack, // Size of first chunk + medium_size= 10*K - slack, // Size of medium-sized chunk + size = 32*K - slack, // Default size of an Arena chunk (following the first) + non_pool_size = init_size + 32 // An initial size which is not one of above + }; + + void chop(); // Chop this chunk + void next_chop(); // Chop next chunk + static size_t aligned_overhead_size(void) { return ARENA_ALIGN(sizeof(Chunk)); } + + size_t length() const { return _len; } + Chunk* next() const { return _next; } + void set_next(Chunk* n) { _next = n; } + // Boundaries of data area (possibly unused) + char* bottom() const { return ((char*) this) + aligned_overhead_size(); } + char* top() const { return bottom() + _len; } + bool contains(char* p) const { return bottom() <= p && p <= top(); } + + // Start the chunk_pool cleaner task + static void start_chunk_pool_cleaner_task(); +}; + + +//------------------------------Arena------------------------------------------ +// Fast allocation of memory +class Arena: public CHeapObj { +protected: + friend class ResourceMark; + friend class HandleMark; + friend class NoHandleMark; + Chunk *_first; // First chunk + Chunk *_chunk; // current chunk + char *_hwm, *_max; // High water mark and max in current chunk + void* grow(size_t x); // Get a new Chunk of at least size x + NOT_PRODUCT(size_t _size_in_bytes;) // Size of arena (used for memory usage tracing) + NOT_PRODUCT(static size_t _bytes_allocated;) // total #bytes allocated since start + friend class AllocStats; + debug_only(void* malloc(size_t size);) + debug_only(void* internal_malloc_4(size_t x);) + public: + Arena(); + Arena(size_t init_size); + Arena(Arena *old); + ~Arena(); + void destruct_contents(); + char* hwm() const { return _hwm; } + + // Fast allocate in the arena. Common case is: pointer test + increment. + void* Amalloc(size_t x) { + assert(is_power_of_2(ARENA_AMALLOC_ALIGNMENT) , "should be a power of 2"); + x = ARENA_ALIGN(x); + debug_only(if (UseMallocOnly) return malloc(x);) + NOT_PRODUCT(_bytes_allocated += x); + if (_hwm + x > _max) { + return grow(x); + } else { + char *old = _hwm; + _hwm += x; + return old; + } + } + // Further assume size is padded out to words + void *Amalloc_4(size_t x) { + assert( (x&(sizeof(char*)-1)) == 0, "misaligned size" ); + debug_only(if (UseMallocOnly) return malloc(x);) + NOT_PRODUCT(_bytes_allocated += x); + if (_hwm + x > _max) { + return grow(x); + } else { + char *old = _hwm; + _hwm += x; + return old; + } + } + + // Allocate with 'double' alignment. It is 8 bytes on sparc. + // In other cases Amalloc_D() should be the same as Amalloc_4(). + void* Amalloc_D(size_t x) { + assert( (x&(sizeof(char*)-1)) == 0, "misaligned size" ); + debug_only(if (UseMallocOnly) return malloc(x);) +#if defined(SPARC) && !defined(_LP64) +#define DALIGN_M1 7 + size_t delta = (((size_t)_hwm + DALIGN_M1) & ~DALIGN_M1) - (size_t)_hwm; + x += delta; +#endif + NOT_PRODUCT(_bytes_allocated += x); + if (_hwm + x > _max) { + return grow(x); // grow() returns a result aligned >= 8 bytes. + } else { + char *old = _hwm; + _hwm += x; +#if defined(SPARC) && !defined(_LP64) + old += delta; // align to 8-bytes +#endif + return old; + } + } + + // Fast delete in area. Common case is: NOP (except for storage reclaimed) + void Afree(void *ptr, size_t size) { +#ifdef ASSERT + if (ZapResourceArea) memset(ptr, badResourceValue, size); // zap freed memory + if (UseMallocOnly) return; +#endif + if (((char*)ptr) + size == _hwm) _hwm = (char*)ptr; + } + + void *Arealloc( void *old_ptr, size_t old_size, size_t new_size ); + + // Move contents of this arena into an empty arena + Arena *move_contents(Arena *empty_arena); + + // Determine if pointer belongs to this Arena or not. + bool contains( const void *ptr ) const; + + // Total of all chunks in use (not thread-safe) + size_t used() const; + + // Total # of bytes used + size_t size_in_bytes() const NOT_PRODUCT({ return _size_in_bytes; }) PRODUCT_RETURN0; + void set_size_in_bytes(size_t size) NOT_PRODUCT({ _size_in_bytes = size; }) PRODUCT_RETURN; + static void free_malloced_objects(Chunk* chunk, char* hwm, char* max, char* hwm2) PRODUCT_RETURN; + static void free_all(char** start, char** end) PRODUCT_RETURN; + +private: + // Reset this Arena to empty, access will trigger grow if necessary + void reset(void) { + _first = _chunk = NULL; + _hwm = _max = NULL; + } +}; + +// One of the following macros must be used when allocating +// an array or object from an arena +#define NEW_ARENA_ARRAY(arena, type, size)\ + (type*) arena->Amalloc((size) * sizeof(type)) + +#define REALLOC_ARENA_ARRAY(arena, type, old, old_size, new_size)\ + (type*) arena->Arealloc((char*)(old), (old_size) * sizeof(type), (new_size) * sizeof(type) ) + +#define FREE_ARENA_ARRAY(arena, type, old, size)\ + arena->Afree((char*)(old), (size) * sizeof(type)) + +#define NEW_ARENA_OBJ(arena, type)\ + NEW_ARENA_ARRAY(arena, type, 1) + + +//%note allocation_1 +extern char* resource_allocate_bytes(size_t size); +extern char* resource_allocate_bytes(Thread* thread, size_t size); +extern char* resource_reallocate_bytes( char *old, size_t old_size, size_t new_size); +extern void resource_free_bytes( char *old, size_t size ); + +//---------------------------------------------------------------------- +// Base class for objects allocated in the resource area per default. +// Optionally, objects may be allocated on the C heap with +// new(ResourceObj::C_HEAP) Foo(...) or in an Arena with new (&arena) +// ResourceObj's can be allocated within other objects, but don't use +// new or delete (allocation_type is unknown). If new is used to allocate, +// use delete to deallocate. +class ResourceObj ALLOCATION_SUPER_CLASS_SPEC { + public: + enum allocation_type { UNKNOWN = 0, C_HEAP, RESOURCE_AREA, ARENA }; +#ifdef ASSERT + private: + allocation_type _allocation; + public: + bool allocated_on_C_heap() { return _allocation == C_HEAP; } +#endif // ASSERT + + public: + void* operator new(size_t size, allocation_type type); + void* operator new(size_t size, Arena *arena) { + address res = (address)arena->Amalloc(size); + // Set allocation type in the resource object + DEBUG_ONLY(((ResourceObj *)res)->_allocation = ARENA;) + return res; + } + void* operator new(size_t size) { + address res = (address)resource_allocate_bytes(size); + // Set allocation type in the resource object + DEBUG_ONLY(((ResourceObj *)res)->_allocation = RESOURCE_AREA;) + return res; + } + void operator delete(void* p); +}; + +// One of the following macros must be used when allocating an array +// or object to determine whether it should reside in the C heap on in +// the resource area. + +#define NEW_RESOURCE_ARRAY(type, size)\ + (type*) resource_allocate_bytes((size) * sizeof(type)) + +#define NEW_RESOURCE_ARRAY_IN_THREAD(thread, type, size)\ + (type*) resource_allocate_bytes(thread, (size) * sizeof(type)) + +#define REALLOC_RESOURCE_ARRAY(type, old, old_size, new_size)\ + (type*) resource_reallocate_bytes((char*)(old), (old_size) * sizeof(type), (new_size) * sizeof(type) ) + +#define FREE_RESOURCE_ARRAY(type, old, size)\ + resource_free_bytes((char*)(old), (size) * sizeof(type)) + +#define FREE_FAST(old)\ + /* nop */ + +#define NEW_RESOURCE_OBJ(type)\ + NEW_RESOURCE_ARRAY(type, 1) + +#define NEW_C_HEAP_ARRAY(type, size)\ + (type*) (AllocateHeap((size) * sizeof(type), XSTR(type) " in " __FILE__)) + +#define REALLOC_C_HEAP_ARRAY(type, old, size)\ + (type*) (ReallocateHeap((char*)old, (size) * sizeof(type), XSTR(type) " in " __FILE__)) + +#define FREE_C_HEAP_ARRAY(type,old) \ + FreeHeap((char*)(old)) + +#define NEW_C_HEAP_OBJ(type)\ + NEW_C_HEAP_ARRAY(type, 1) + +extern bool warn_new_operator; + +// for statistics +#ifndef PRODUCT +class AllocStats : StackObj { + int start_mallocs, start_frees; + size_t start_malloc_bytes, start_res_bytes; + public: + AllocStats(); + + int num_mallocs(); // since creation of receiver + size_t alloc_bytes(); + size_t resource_bytes(); + int num_frees(); + void print(); +}; +#endif + + +//------------------------------ReallocMark--------------------------------- +// Code which uses REALLOC_RESOURCE_ARRAY should check an associated +// ReallocMark, which is declared in the same scope as the reallocated +// pointer. Any operation that could __potentially__ cause a reallocation +// should check the ReallocMark. +class ReallocMark: public StackObj { +protected: + NOT_PRODUCT(int _nesting;) + +public: + ReallocMark() PRODUCT_RETURN; + void check() PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/memory/allocation.inline.hpp b/hotspot/src/share/vm/memory/allocation.inline.hpp new file mode 100644 index 00000000000..063edfc0564 --- /dev/null +++ b/hotspot/src/share/vm/memory/allocation.inline.hpp @@ -0,0 +1,59 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Explicit C-heap memory management + +void trace_heap_malloc(size_t size, const char* name, void *p); +void trace_heap_free(void *p); + + +// allocate using malloc; will fail if no memory available +inline char* AllocateHeap(size_t size, const char* name = NULL) { + char* p = (char*) os::malloc(size); + #ifdef ASSERT + if (PrintMallocFree) trace_heap_malloc(size, name, p); + #else + Unused_Variable(name); + #endif + if (p == NULL) vm_exit_out_of_memory(size, name); + return p; +} + +inline char* ReallocateHeap(char *old, size_t size, const char* name = NULL) { + char* p = (char*) os::realloc(old,size); + #ifdef ASSERT + if (PrintMallocFree) trace_heap_malloc(size, name, p); + #else + Unused_Variable(name); + #endif + if (p == NULL) vm_exit_out_of_memory(size, name); + return p; +} + +inline void FreeHeap(void* p) { + #ifdef ASSERT + if (PrintMallocFree) trace_heap_free(p); + #endif + os::free(p); +} diff --git a/hotspot/src/share/vm/memory/allocationStats.cpp b/hotspot/src/share/vm/memory/allocationStats.cpp new file mode 100644 index 00000000000..2145394123b --- /dev/null +++ b/hotspot/src/share/vm/memory/allocationStats.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_allocationStats.cpp.incl" + +// Technically this should be derived from machine speed, and +// ideally it would be dynamically adjusted. +float AllocationStats::_threshold = ((float)CMS_SweepTimerThresholdMillis)/1000; diff --git a/hotspot/src/share/vm/memory/allocationStats.hpp b/hotspot/src/share/vm/memory/allocationStats.hpp new file mode 100644 index 00000000000..829a39cb5a0 --- /dev/null +++ b/hotspot/src/share/vm/memory/allocationStats.hpp @@ -0,0 +1,136 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class AllocationStats VALUE_OBJ_CLASS_SPEC { + // A duration threshold (in ms) used to filter + // possibly unreliable samples. + static float _threshold; + + // We measure the demand between the end of the previous sweep and + // beginning of this sweep: + // Count(end_last_sweep) - Count(start_this_sweep) + // + splitBirths(between) - splitDeaths(between) + // The above number divided by the time since the start [END???] of the + // previous sweep gives us a time rate of demand for blocks + // of this size. We compute a padded average of this rate as + // our current estimate for the time rate of demand for blocks + // of this size. Similarly, we keep a padded average for the time + // between sweeps. Our current estimate for demand for blocks of + // this size is then simply computed as the product of these two + // estimates. + AdaptivePaddedAverage _demand_rate_estimate; + + ssize_t _desired; // Estimate computed as described above + ssize_t _coalDesired; // desired +/- small-percent for tuning coalescing + + ssize_t _surplus; // count - (desired +/- small-percent), + // used to tune splitting in best fit + ssize_t _bfrSurp; // surplus at start of current sweep + ssize_t _prevSweep; // count from end of previous sweep + ssize_t _beforeSweep; // count from before current sweep + ssize_t _coalBirths; // additional chunks from coalescing + ssize_t _coalDeaths; // loss from coalescing + ssize_t _splitBirths; // additional chunks from splitting + ssize_t _splitDeaths; // loss from splitting + size_t _returnedBytes; // number of bytes returned to list. + public: + void initialize() { + AdaptivePaddedAverage* dummy = + new (&_demand_rate_estimate) AdaptivePaddedAverage(CMS_FLSWeight, + CMS_FLSPadding); + _desired = 0; + _coalDesired = 0; + _surplus = 0; + _bfrSurp = 0; + _prevSweep = 0; + _beforeSweep = 0; + _coalBirths = 0; + _coalDeaths = 0; + _splitBirths = 0; + _splitDeaths = 0; + _returnedBytes = 0; + } + + AllocationStats() { + initialize(); + } + // The rate estimate is in blocks per second. + void compute_desired(size_t count, + float inter_sweep_current, + float inter_sweep_estimate) { + // If the latest inter-sweep time is below our granularity + // of measurement, we may call in here with + // inter_sweep_current == 0. However, even for suitably small + // but non-zero inter-sweep durations, we may not trust the accuracy + // of accumulated data, since it has not been "integrated" + // (read "low-pass-filtered") long enough, and would be + // vulnerable to noisy glitches. In such cases, we + // ignore the current sample and use currently available + // historical estimates. + if (inter_sweep_current > _threshold) { + ssize_t demand = prevSweep() - count + splitBirths() - splitDeaths(); + float rate = ((float)demand)/inter_sweep_current; + _demand_rate_estimate.sample(rate); + _desired = (ssize_t)(_demand_rate_estimate.padded_average() + *inter_sweep_estimate); + } + } + + ssize_t desired() const { return _desired; } + ssize_t coalDesired() const { return _coalDesired; } + void set_coalDesired(ssize_t v) { _coalDesired = v; } + + ssize_t surplus() const { return _surplus; } + void set_surplus(ssize_t v) { _surplus = v; } + void increment_surplus() { _surplus++; } + void decrement_surplus() { _surplus--; } + + ssize_t bfrSurp() const { return _bfrSurp; } + void set_bfrSurp(ssize_t v) { _bfrSurp = v; } + ssize_t prevSweep() const { return _prevSweep; } + void set_prevSweep(ssize_t v) { _prevSweep = v; } + ssize_t beforeSweep() const { return _beforeSweep; } + void set_beforeSweep(ssize_t v) { _beforeSweep = v; } + + ssize_t coalBirths() const { return _coalBirths; } + void set_coalBirths(ssize_t v) { _coalBirths = v; } + void increment_coalBirths() { _coalBirths++; } + + ssize_t coalDeaths() const { return _coalDeaths; } + void set_coalDeaths(ssize_t v) { _coalDeaths = v; } + void increment_coalDeaths() { _coalDeaths++; } + + ssize_t splitBirths() const { return _splitBirths; } + void set_splitBirths(ssize_t v) { _splitBirths = v; } + void increment_splitBirths() { _splitBirths++; } + + ssize_t splitDeaths() const { return _splitDeaths; } + void set_splitDeaths(ssize_t v) { _splitDeaths = v; } + void increment_splitDeaths() { _splitDeaths++; } + + NOT_PRODUCT( + size_t returnedBytes() const { return _returnedBytes; } + void set_returnedBytes(size_t v) { _returnedBytes = v; } + ) +}; diff --git a/hotspot/src/share/vm/memory/barrierSet.hpp b/hotspot/src/share/vm/memory/barrierSet.hpp new file mode 100644 index 00000000000..bc3208be68e --- /dev/null +++ b/hotspot/src/share/vm/memory/barrierSet.hpp @@ -0,0 +1,170 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This class provides the interface between a barrier implementation and +// the rest of the system. + +class BarrierSet: public CHeapObj { + friend class VMStructs; +public: + enum Name { + ModRef, + CardTableModRef, + CardTableExtension, + Other, + Uninit + }; + +protected: + int _max_covered_regions; + Name _kind; + +public: + + // To get around prohibition on RTTI. + virtual BarrierSet::Name kind() { return _kind; } + virtual bool is_a(BarrierSet::Name bsn) = 0; + + // These operations indicate what kind of barriers the BarrierSet has. + virtual bool has_read_ref_barrier() = 0; + virtual bool has_read_prim_barrier() = 0; + virtual bool has_write_ref_barrier() = 0; + virtual bool has_write_prim_barrier() = 0; + + // These functions indicate whether a particular access of the given + // kinds requires a barrier. + virtual bool read_ref_needs_barrier(oop* field) = 0; + virtual bool read_prim_needs_barrier(HeapWord* field, size_t bytes) = 0; + virtual bool write_ref_needs_barrier(oop* field, oop new_val) = 0; + virtual bool write_prim_needs_barrier(HeapWord* field, size_t bytes, juint val1, juint val2) = 0; + + // The first four operations provide a direct implementation of the + // barrier set. An interpreter loop, for example, could call these + // directly, as appropriate. + + // Invoke the barrier, if any, necessary when reading the given ref field. + virtual void read_ref_field(oop* field) = 0; + + // Invoke the barrier, if any, necessary when reading the given primitive + // "field" of "bytes" bytes in "obj". + virtual void read_prim_field(HeapWord* field, size_t bytes) = 0; + + // Invoke the barrier, if any, necessary when writing "new_val" into the + // ref field at "offset" in "obj". + // (For efficiency reasons, this operation is specialized for certain + // barrier types. Semantically, it should be thought of as a call to the + // virtual "_work" function below, which must implement the barrier.) + inline void write_ref_field(oop* field, oop new_val); +protected: + virtual void write_ref_field_work(oop* field, oop new_val) = 0; +public: + + // Invoke the barrier, if any, necessary when writing the "bytes"-byte + // value(s) "val1" (and "val2") into the primitive "field". + virtual void write_prim_field(HeapWord* field, size_t bytes, + juint val1, juint val2) = 0; + + // Operations on arrays, or general regions (e.g., for "clone") may be + // optimized by some barriers. + + // The first six operations tell whether such an optimization exists for + // the particular barrier. + virtual bool has_read_ref_array_opt() = 0; + virtual bool has_read_prim_array_opt() = 0; + virtual bool has_write_ref_array_opt() = 0; + virtual bool has_write_prim_array_opt() = 0; + + virtual bool has_read_region_opt() = 0; + virtual bool has_write_region_opt() = 0; + + // These operations should assert false unless the correponding operation + // above returns true. Otherwise, they should perform an appropriate + // barrier for an array whose elements are all in the given memory region. + virtual void read_ref_array(MemRegion mr) = 0; + virtual void read_prim_array(MemRegion mr) = 0; + + inline void write_ref_array(MemRegion mr); +protected: + virtual void write_ref_array_work(MemRegion mr) = 0; +public: + virtual void write_prim_array(MemRegion mr) = 0; + + virtual void read_region(MemRegion mr) = 0; + + // (For efficiency reasons, this operation is specialized for certain + // barrier types. Semantically, it should be thought of as a call to the + // virtual "_work" function below, which must implement the barrier.) + inline void write_region(MemRegion mr); +protected: + virtual void write_region_work(MemRegion mr) = 0; +public: + + // The remaining sets of operations are called by compilers or other code + // generators to insert barriers into generated code. There may be + // several such code generators; the signatures of these + // barrier-generating functions may differ from generator to generator. + // There will be a set of four function signatures for each code + // generator, which accomplish the generation of barriers of the four + // kinds listed above. + +#ifdef TBD + // Generates code to invoke the barrier, if any, necessary when reading + // the ref field at "offset" in "obj". + virtual void gen_read_ref_field() = 0; + + // Generates code to invoke the barrier, if any, necessary when reading + // the primitive field of "bytes" bytes at offset" in "obj". + virtual void gen_read_prim_field() = 0; + + // Generates code to invoke the barrier, if any, necessary when writing + // "new_val" into the ref field at "offset" in "obj". + virtual void gen_write_ref_field() = 0; + + // Generates code to invoke the barrier, if any, necessary when writing + // the "bytes"-byte value "new_val" into the primitive field at "offset" + // in "obj". + virtual void gen_write_prim_field() = 0; +#endif + + // Some barrier sets create tables whose elements correspond to parts of + // the heap; the CardTableModRefBS is an example. Such barrier sets will + // normally reserve space for such tables, and commit parts of the table + // "covering" parts of the heap that are committed. The constructor is + // passed the maximum number of independently committable subregions to + // be covered, and the "resize_covoered_region" function allows the + // sub-parts of the heap to inform the barrier set of changes of their + // sizes. + BarrierSet(int max_covered_regions) : + _max_covered_regions(max_covered_regions) {} + + // Inform the BarrierSet that the the covered heap region that starts + // with "base" has been changed to have the given size (possibly from 0, + // for initialization.) + virtual void resize_covered_region(MemRegion new_region) = 0; + + // If the barrier set imposes any alignment restrictions on boundaries + // within the heap, this function tells whether they are met. + virtual bool is_aligned(HeapWord* addr) = 0; + +}; diff --git a/hotspot/src/share/vm/memory/barrierSet.inline.hpp b/hotspot/src/share/vm/memory/barrierSet.inline.hpp new file mode 100644 index 00000000000..082cb760968 --- /dev/null +++ b/hotspot/src/share/vm/memory/barrierSet.inline.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline functions of BarrierSet, which de-virtualize certain +// performance-critical calls when when the barrier is the most common +// card-table kind. + +void BarrierSet::write_ref_field(oop* field, oop new_val) { + if (kind() == CardTableModRef) { + ((CardTableModRefBS*)this)->inline_write_ref_field(field, new_val); + } else { + write_ref_field_work(field, new_val); + } +} + +void BarrierSet::write_ref_array(MemRegion mr) { + if (kind() == CardTableModRef) { + ((CardTableModRefBS*)this)->inline_write_ref_array(mr); + } else { + write_ref_array_work(mr); + } +} + +void BarrierSet::write_region(MemRegion mr) { + if (kind() == CardTableModRef) { + ((CardTableModRefBS*)this)->inline_write_region(mr); + } else { + write_region_work(mr); + } +} diff --git a/hotspot/src/share/vm/memory/blockOffsetTable.cpp b/hotspot/src/share/vm/memory/blockOffsetTable.cpp new file mode 100644 index 00000000000..b1d0c62b898 --- /dev/null +++ b/hotspot/src/share/vm/memory/blockOffsetTable.cpp @@ -0,0 +1,783 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_blockOffsetTable.cpp.incl" + +////////////////////////////////////////////////////////////////////// +// BlockOffsetSharedArray +////////////////////////////////////////////////////////////////////// + +BlockOffsetSharedArray::BlockOffsetSharedArray(MemRegion reserved, + size_t init_word_size): + _reserved(reserved), _end(NULL) +{ + size_t size = compute_size(reserved.word_size()); + ReservedSpace rs(size); + if (!rs.is_reserved()) { + vm_exit_during_initialization("Could not reserve enough space for heap offset array"); + } + if (!_vs.initialize(rs, 0)) { + vm_exit_during_initialization("Could not reserve enough space for heap offset array"); + } + _offset_array = (u_char*)_vs.low_boundary(); + resize(init_word_size); + if (TraceBlockOffsetTable) { + gclog_or_tty->print_cr("BlockOffsetSharedArray::BlockOffsetSharedArray: "); + gclog_or_tty->print_cr(" " + " rs.base(): " INTPTR_FORMAT + " rs.size(): " INTPTR_FORMAT + " rs end(): " INTPTR_FORMAT, + rs.base(), rs.size(), rs.base() + rs.size()); + gclog_or_tty->print_cr(" " + " _vs.low_boundary(): " INTPTR_FORMAT + " _vs.high_boundary(): " INTPTR_FORMAT, + _vs.low_boundary(), + _vs.high_boundary()); + } +} + +void BlockOffsetSharedArray::resize(size_t new_word_size) { + assert(new_word_size <= _reserved.word_size(), "Resize larger than reserved"); + size_t new_size = compute_size(new_word_size); + size_t old_size = _vs.committed_size(); + size_t delta; + char* high = _vs.high(); + _end = _reserved.start() + new_word_size; + if (new_size > old_size) { + delta = ReservedSpace::page_align_size_up(new_size - old_size); + assert(delta > 0, "just checking"); + if (!_vs.expand_by(delta)) { + // Do better than this for Merlin + vm_exit_out_of_memory(delta, "offset table expansion"); + } + assert(_vs.high() == high + delta, "invalid expansion"); + } else { + delta = ReservedSpace::page_align_size_down(old_size - new_size); + if (delta == 0) return; + _vs.shrink_by(delta); + assert(_vs.high() == high - delta, "invalid expansion"); + } +} + +bool BlockOffsetSharedArray::is_card_boundary(HeapWord* p) const { + assert(p >= _reserved.start(), "just checking"); + size_t delta = pointer_delta(p, _reserved.start()); + return (delta & right_n_bits(LogN_words)) == (size_t)NoBits; +} + + +void BlockOffsetSharedArray::serialize(SerializeOopClosure* soc, + HeapWord* start, HeapWord* end) { + assert(_offset_array[0] == 0, "objects can't cross covered areas"); + assert(start <= end, "bad address range"); + size_t start_index = index_for(start); + size_t end_index = index_for(end-1)+1; + soc->do_region(&_offset_array[start_index], + (end_index - start_index) * sizeof(_offset_array[0])); +} + +////////////////////////////////////////////////////////////////////// +// BlockOffsetArray +////////////////////////////////////////////////////////////////////// + +BlockOffsetArray::BlockOffsetArray(BlockOffsetSharedArray* array, + MemRegion mr, bool init_to_zero) : + BlockOffsetTable(mr.start(), mr.end()), + _array(array), + _init_to_zero(init_to_zero) +{ + assert(_bottom <= _end, "arguments out of order"); + if (!_init_to_zero) { + // initialize cards to point back to mr.start() + set_remainder_to_point_to_start(mr.start() + N_words, mr.end()); + _array->set_offset_array(0, 0); // set first card to 0 + } +} + + +// The arguments follow the normal convention of denoting +// a right-open interval: [start, end) +void +BlockOffsetArray:: +set_remainder_to_point_to_start(HeapWord* start, HeapWord* end) { + + if (start >= end) { + // The start address is equal to the end address (or to + // the right of the end address) so there are not cards + // that need to be updated.. + return; + } + + // Write the backskip value for each region. + // + // offset + // card 2nd 3rd + // | +- 1st | | + // v v v v + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+- + // |x|0|0|0|0|0|0|0|1|1|1|1|1|1| ... |1|1|1|1|2|2|2|2|2|2| ... + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+- + // 11 19 75 + // 12 + // + // offset card is the card that points to the start of an object + // x - offset value of offset card + // 1st - start of first logarithmic region + // 0 corresponds to logarithmic value N_words + 0 and 2**(3 * 0) = 1 + // 2nd - start of second logarithmic region + // 1 corresponds to logarithmic value N_words + 1 and 2**(3 * 1) = 8 + // 3rd - start of third logarithmic region + // 2 corresponds to logarithmic value N_words + 2 and 2**(3 * 2) = 64 + // + // integer below the block offset entry is an example of + // the index of the entry + // + // Given an address, + // Find the index for the address + // Find the block offset table entry + // Convert the entry to a back slide + // (e.g., with today's, offset = 0x81 => + // back slip = 2**(3*(0x81 - N_words)) = 2**3) = 8 + // Move back N (e.g., 8) entries and repeat with the + // value of the new entry + // + size_t start_card = _array->index_for(start); + size_t end_card = _array->index_for(end-1); + assert(start ==_array->address_for_index(start_card), "Precondition"); + assert(end ==_array->address_for_index(end_card)+N_words, "Precondition"); + set_remainder_to_point_to_start_incl(start_card, end_card); // closed interval +} + + +// Unlike the normal convention in this code, the argument here denotes +// a closed, inclusive interval: [start_card, end_card], cf set_remainder_to_point_to_start() +// above. +void +BlockOffsetArray::set_remainder_to_point_to_start_incl(size_t start_card, size_t end_card) { + if (start_card > end_card) { + return; + } + assert(start_card > _array->index_for(_bottom), "Cannot be first card"); + assert(_array->offset_array(start_card-1) <= N_words, + "Offset card has an unexpected value"); + size_t start_card_for_region = start_card; + u_char offset = max_jubyte; + for (int i = 0; i <= N_powers-1; i++) { + // -1 so that the the card with the actual offset is counted. Another -1 + // so that the reach ends in this region and not at the start + // of the next. + size_t reach = start_card - 1 + (power_to_cards_back(i+1) - 1); + offset = N_words + i; + if (reach >= end_card) { + _array->set_offset_array(start_card_for_region, end_card, offset); + start_card_for_region = reach + 1; + break; + } + _array->set_offset_array(start_card_for_region, reach, offset); + start_card_for_region = reach + 1; + } + assert(start_card_for_region > end_card, "Sanity check"); + DEBUG_ONLY(check_all_cards(start_card, end_card);) +} + +// The card-interval [start_card, end_card] is a closed interval; this +// is an expensive check -- use with care and only under protection of +// suitable flag. +void BlockOffsetArray::check_all_cards(size_t start_card, size_t end_card) const { + + if (end_card < start_card) { + return; + } + guarantee(_array->offset_array(start_card) == N_words, "Wrong value in second card"); + for (size_t c = start_card + 1; c <= end_card; c++ /* yeah! */) { + u_char entry = _array->offset_array(c); + if (c - start_card > power_to_cards_back(1)) { + guarantee(entry > N_words, "Should be in logarithmic region"); + } + size_t backskip = entry_to_cards_back(entry); + size_t landing_card = c - backskip; + guarantee(landing_card >= (start_card - 1), "Inv"); + if (landing_card >= start_card) { + guarantee(_array->offset_array(landing_card) <= entry, "monotonicity"); + } else { + guarantee(landing_card == start_card - 1, "Tautology"); + guarantee(_array->offset_array(landing_card) <= N_words, "Offset value"); + } + } +} + + +void +BlockOffsetArray::alloc_block(HeapWord* blk_start, HeapWord* blk_end) { + assert(blk_start != NULL && blk_end > blk_start, + "phantom block"); + single_block(blk_start, blk_end); +} + +// Action_mark - update the BOT for the block [blk_start, blk_end). +// Current typical use is for splitting a block. +// Action_single - udpate the BOT for an allocation. +// Action_verify - BOT verification. +void +BlockOffsetArray::do_block_internal(HeapWord* blk_start, + HeapWord* blk_end, + Action action) { + assert(Universe::heap()->is_in_reserved(blk_start), + "reference must be into the heap"); + assert(Universe::heap()->is_in_reserved(blk_end-1), + "limit must be within the heap"); + // This is optimized to make the test fast, assuming we only rarely + // cross boundaries. + uintptr_t end_ui = (uintptr_t)(blk_end - 1); + uintptr_t start_ui = (uintptr_t)blk_start; + // Calculate the last card boundary preceding end of blk + intptr_t boundary_before_end = (intptr_t)end_ui; + clear_bits(boundary_before_end, right_n_bits(LogN)); + if (start_ui <= (uintptr_t)boundary_before_end) { + // blk starts at or crosses a boundary + // Calculate index of card on which blk begins + size_t start_index = _array->index_for(blk_start); + // Index of card on which blk ends + size_t end_index = _array->index_for(blk_end - 1); + // Start address of card on which blk begins + HeapWord* boundary = _array->address_for_index(start_index); + assert(boundary <= blk_start, "blk should start at or after boundary"); + if (blk_start != boundary) { + // blk starts strictly after boundary + // adjust card boundary and start_index forward to next card + boundary += N_words; + start_index++; + } + assert(start_index <= end_index, "monotonicity of index_for()"); + assert(boundary <= (HeapWord*)boundary_before_end, "tautology"); + switch (action) { + case Action_mark: { + if (init_to_zero()) { + _array->set_offset_array(start_index, boundary, blk_start); + break; + } // Else fall through to the next case + } + case Action_single: { + _array->set_offset_array(start_index, boundary, blk_start); + // We have finished marking the "offset card". We need to now + // mark the subsequent cards that this blk spans. + if (start_index < end_index) { + HeapWord* rem_st = _array->address_for_index(start_index) + N_words; + HeapWord* rem_end = _array->address_for_index(end_index) + N_words; + set_remainder_to_point_to_start(rem_st, rem_end); + } + break; + } + case Action_check: { + _array->check_offset_array(start_index, boundary, blk_start); + // We have finished checking the "offset card". We need to now + // check the subsequent cards that this blk spans. + check_all_cards(start_index + 1, end_index); + break; + } + default: + ShouldNotReachHere(); + } + } +} + +// The range [blk_start, blk_end) represents a single contiguous block +// of storage; modify the block offset table to represent this +// information; Right-open interval: [blk_start, blk_end) +// NOTE: this method does _not_ adjust _unallocated_block. +void +BlockOffsetArray::single_block(HeapWord* blk_start, + HeapWord* blk_end) { + do_block_internal(blk_start, blk_end, Action_single); +} + +void BlockOffsetArray::verify() const { + // For each entry in the block offset table, verify that + // the entry correctly finds the start of an object at the + // first address covered by the block or to the left of that + // first address. + + size_t next_index = 1; + size_t last_index = last_active_index(); + + // Use for debugging. Initialize to NULL to distinguish the + // first iteration through the while loop. + HeapWord* last_p = NULL; + HeapWord* last_start = NULL; + oop last_o = NULL; + + while (next_index <= last_index) { + // Use an address past the start of the address for + // the entry. + HeapWord* p = _array->address_for_index(next_index) + 1; + if (p >= _end) { + // That's all of the allocated block table. + return; + } + // block_start() asserts that start <= p. + HeapWord* start = block_start(p); + // First check if the start is an allocated block and only + // then if it is a valid object. + oop o = oop(start); + assert(!Universe::is_fully_initialized() || + _sp->is_free_block(start) || + o->is_oop_or_null(), "Bad object was found"); + next_index++; + last_p = p; + last_start = start; + last_o = o; + } +} + +////////////////////////////////////////////////////////////////////// +// BlockOffsetArrayNonContigSpace +////////////////////////////////////////////////////////////////////// + +// The block [blk_start, blk_end) has been allocated; +// adjust the block offset table to represent this information; +// NOTE: Clients of BlockOffsetArrayNonContigSpace: consider using +// the somewhat more lightweight split_block() or +// (when init_to_zero()) mark_block() wherever possible. +// right-open interval: [blk_start, blk_end) +void +BlockOffsetArrayNonContigSpace::alloc_block(HeapWord* blk_start, + HeapWord* blk_end) { + assert(blk_start != NULL && blk_end > blk_start, + "phantom block"); + single_block(blk_start, blk_end); + allocated(blk_start, blk_end); +} + +// Adjust BOT to show that a previously whole block has been split +// into two. We verify the BOT for the first part (prefix) and +// update the BOT for the second part (suffix). +// blk is the start of the block +// blk_size is the size of the original block +// left_blk_size is the size of the first part of the split +void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk, + size_t blk_size, + size_t left_blk_size) { + // Verify that the BOT shows [blk, blk + blk_size) to be one block. + verify_single_block(blk, blk_size); + // Update the BOT to indicate that [blk + left_blk_size, blk + blk_size) + // is one single block. + assert(blk_size > 0, "Should be positive"); + assert(left_blk_size > 0, "Should be positive"); + assert(left_blk_size < blk_size, "Not a split"); + + // Start addresses of prefix block and suffix block. + HeapWord* pref_addr = blk; + HeapWord* suff_addr = blk + left_blk_size; + HeapWord* end_addr = blk + blk_size; + + // Indices for starts of prefix block and suffix block. + size_t pref_index = _array->index_for(pref_addr); + if (_array->address_for_index(pref_index) != pref_addr) { + // pref_addr deos not begin pref_index + pref_index++; + } + + size_t suff_index = _array->index_for(suff_addr); + if (_array->address_for_index(suff_index) != suff_addr) { + // suff_addr does not begin suff_index + suff_index++; + } + + // Definition: A block B, denoted [B_start, B_end) __starts__ + // a card C, denoted [C_start, C_end), where C_start and C_end + // are the heap addresses that card C covers, iff + // B_start <= C_start < B_end. + // + // We say that a card C "is started by" a block B, iff + // B "starts" C. + // + // Note that the cardinality of the set of cards {C} + // started by a block B can be 0, 1, or more. + // + // Below, pref_index and suff_index are, respectively, the + // first (least) card indices that the prefix and suffix of + // the split start; end_index is one more than the index of + // the last (greatest) card that blk starts. + size_t end_index = _array->index_for(end_addr - 1) + 1; + + // Calculate the # cards that the prefix and suffix affect. + size_t num_pref_cards = suff_index - pref_index; + + size_t num_suff_cards = end_index - suff_index; + // Change the cards that need changing + if (num_suff_cards > 0) { + HeapWord* boundary = _array->address_for_index(suff_index); + // Set the offset card for suffix block + _array->set_offset_array(suff_index, boundary, suff_addr); + // Change any further cards that need changing in the suffix + if (num_pref_cards > 0) { + if (num_pref_cards >= num_suff_cards) { + // Unilaterally fix all of the suffix cards: closed card + // index interval in args below. + set_remainder_to_point_to_start_incl(suff_index + 1, end_index - 1); + } else { + // Unilaterally fix the first (num_pref_cards - 1) following + // the "offset card" in the suffix block. + set_remainder_to_point_to_start_incl(suff_index + 1, + suff_index + num_pref_cards - 1); + // Fix the appropriate cards in the remainder of the + // suffix block -- these are the last num_pref_cards + // cards in each power block of the "new" range plumbed + // from suff_addr. + bool more = true; + uint i = 1; + while (more && (i < N_powers)) { + size_t back_by = power_to_cards_back(i); + size_t right_index = suff_index + back_by - 1; + size_t left_index = right_index - num_pref_cards + 1; + if (right_index >= end_index - 1) { // last iteration + right_index = end_index - 1; + more = false; + } + if (back_by > num_pref_cards) { + // Fill in the remainder of this "power block", if it + // is non-null. + if (left_index <= right_index) { + _array->set_offset_array(left_index, right_index, + N_words + i - 1); + } else { + more = false; // we are done + } + i++; + break; + } + i++; + } + while (more && (i < N_powers)) { + size_t back_by = power_to_cards_back(i); + size_t right_index = suff_index + back_by - 1; + size_t left_index = right_index - num_pref_cards + 1; + if (right_index >= end_index - 1) { // last iteration + right_index = end_index - 1; + if (left_index > right_index) { + break; + } + more = false; + } + assert(left_index <= right_index, "Error"); + _array->set_offset_array(left_index, right_index, N_words + i - 1); + i++; + } + } + } // else no more cards to fix in suffix + } // else nothing needs to be done + // Verify that we did the right thing + verify_single_block(pref_addr, left_blk_size); + verify_single_block(suff_addr, blk_size - left_blk_size); +} + + +// Mark the BOT such that if [blk_start, blk_end) straddles a card +// boundary, the card following the first such boundary is marked +// with the appropriate offset. +// NOTE: this method does _not_ adjust _unallocated_block or +// any cards subsequent to the first one. +void +BlockOffsetArrayNonContigSpace::mark_block(HeapWord* blk_start, + HeapWord* blk_end) { + do_block_internal(blk_start, blk_end, Action_mark); +} + +HeapWord* BlockOffsetArrayNonContigSpace::block_start_unsafe( + const void* addr) const { + assert(_array->offset_array(0) == 0, "objects can't cross covered areas"); + + assert(_bottom <= addr && addr < _end, + "addr must be covered by this Array"); + // Must read this exactly once because it can be modified by parallel + // allocation. + HeapWord* ub = _unallocated_block; + if (BlockOffsetArrayUseUnallocatedBlock && addr >= ub) { + assert(ub < _end, "tautology (see above)"); + return ub; + } + + // Otherwise, find the block start using the table. + size_t index = _array->index_for(addr); + HeapWord* q = _array->address_for_index(index); + + uint offset = _array->offset_array(index); // Extend u_char to uint. + while (offset >= N_words) { + // The excess of the offset from N_words indicates a power of Base + // to go back by. + size_t n_cards_back = entry_to_cards_back(offset); + q -= (N_words * n_cards_back); + assert(q >= _sp->bottom(), "Went below bottom!"); + index -= n_cards_back; + offset = _array->offset_array(index); + } + assert(offset < N_words, "offset too large"); + index--; + q -= offset; + HeapWord* n = q; + + while (n <= addr) { + debug_only(HeapWord* last = q); // for debugging + q = n; + n += _sp->block_size(n); + } + assert(q <= addr, "wrong order for current and arg"); + assert(addr <= n, "wrong order for arg and next"); + return q; +} + +HeapWord* BlockOffsetArrayNonContigSpace::block_start_careful( + const void* addr) const { + assert(_array->offset_array(0) == 0, "objects can't cross covered areas"); + + assert(_bottom <= addr && addr < _end, + "addr must be covered by this Array"); + // Must read this exactly once because it can be modified by parallel + // allocation. + HeapWord* ub = _unallocated_block; + if (BlockOffsetArrayUseUnallocatedBlock && addr >= ub) { + assert(ub < _end, "tautology (see above)"); + return ub; + } + + // Otherwise, find the block start using the table, but taking + // care (cf block_start_unsafe() above) not to parse any objects/blocks + // on the cards themsleves. + size_t index = _array->index_for(addr); + assert(_array->address_for_index(index) == addr, + "arg should be start of card"); + + HeapWord* q = (HeapWord*)addr; + uint offset; + do { + offset = _array->offset_array(index); + if (offset < N_words) { + q -= offset; + } else { + size_t n_cards_back = entry_to_cards_back(offset); + q -= (n_cards_back * N_words); + index -= n_cards_back; + } + } while (offset >= N_words); + assert(q <= addr, "block start should be to left of arg"); + return q; +} + +#ifndef PRODUCT +// Verification & debugging - ensure that the offset table reflects the fact +// that the block [blk_start, blk_end) or [blk, blk + size) is a +// single block of storage. NOTE: can't const this because of +// call to non-const do_block_internal() below. +void BlockOffsetArrayNonContigSpace::verify_single_block( + HeapWord* blk_start, HeapWord* blk_end) { + if (VerifyBlockOffsetArray) { + do_block_internal(blk_start, blk_end, Action_check); + } +} + +void BlockOffsetArrayNonContigSpace::verify_single_block( + HeapWord* blk, size_t size) { + verify_single_block(blk, blk + size); +} + +// Verify that the given block is before _unallocated_block +void BlockOffsetArrayNonContigSpace::verify_not_unallocated( + HeapWord* blk_start, HeapWord* blk_end) const { + if (BlockOffsetArrayUseUnallocatedBlock) { + assert(blk_start < blk_end, "Block inconsistency?"); + assert(blk_end <= _unallocated_block, "_unallocated_block problem"); + } +} + +void BlockOffsetArrayNonContigSpace::verify_not_unallocated( + HeapWord* blk, size_t size) const { + verify_not_unallocated(blk, blk + size); +} +#endif // PRODUCT + +size_t BlockOffsetArrayNonContigSpace::last_active_index() const { + if (_unallocated_block == _bottom) { + return 0; + } else { + return _array->index_for(_unallocated_block - 1); + } +} + +////////////////////////////////////////////////////////////////////// +// BlockOffsetArrayContigSpace +////////////////////////////////////////////////////////////////////// + +HeapWord* BlockOffsetArrayContigSpace::block_start_unsafe(const void* addr) const { + assert(_array->offset_array(0) == 0, "objects can't cross covered areas"); + + // Otherwise, find the block start using the table. + assert(_bottom <= addr && addr < _end, + "addr must be covered by this Array"); + size_t index = _array->index_for(addr); + // We must make sure that the offset table entry we use is valid. If + // "addr" is past the end, start at the last known one and go forward. + index = MIN2(index, _next_offset_index-1); + HeapWord* q = _array->address_for_index(index); + + uint offset = _array->offset_array(index); // Extend u_char to uint. + while (offset > N_words) { + // The excess of the offset from N_words indicates a power of Base + // to go back by. + size_t n_cards_back = entry_to_cards_back(offset); + q -= (N_words * n_cards_back); + assert(q >= _sp->bottom(), "Went below bottom!"); + index -= n_cards_back; + offset = _array->offset_array(index); + } + while (offset == N_words) { + assert(q >= _sp->bottom(), "Went below bottom!"); + q -= N_words; + index--; + offset = _array->offset_array(index); + } + assert(offset < N_words, "offset too large"); + q -= offset; + HeapWord* n = q; + + while (n <= addr) { + debug_only(HeapWord* last = q); // for debugging + q = n; + n += _sp->block_size(n); + } + assert(q <= addr, "wrong order for current and arg"); + assert(addr <= n, "wrong order for arg and next"); + return q; +} + +// +// _next_offset_threshold +// | _next_offset_index +// v v +// +-------+-------+-------+-------+-------+ +// | i-1 | i | i+1 | i+2 | i+3 | +// +-------+-------+-------+-------+-------+ +// ( ^ ] +// block-start +// + +void BlockOffsetArrayContigSpace::alloc_block_work(HeapWord* blk_start, + HeapWord* blk_end) { + assert(blk_start != NULL && blk_end > blk_start, + "phantom block"); + assert(blk_end > _next_offset_threshold, + "should be past threshold"); + assert(blk_start <= _next_offset_threshold, + "blk_start should be at or before threshold") + assert(pointer_delta(_next_offset_threshold, blk_start) <= N_words, + "offset should be <= BlockOffsetSharedArray::N"); + assert(Universe::heap()->is_in_reserved(blk_start), + "reference must be into the heap"); + assert(Universe::heap()->is_in_reserved(blk_end-1), + "limit must be within the heap"); + assert(_next_offset_threshold == + _array->_reserved.start() + _next_offset_index*N_words, + "index must agree with threshold"); + + debug_only(size_t orig_next_offset_index = _next_offset_index;) + + // Mark the card that holds the offset into the block. Note + // that _next_offset_index and _next_offset_threshold are not + // updated until the end of this method. + _array->set_offset_array(_next_offset_index, + _next_offset_threshold, + blk_start); + + // We need to now mark the subsequent cards that this blk spans. + + // Index of card on which blk ends. + size_t end_index = _array->index_for(blk_end - 1); + + // Are there more cards left to be updated? + if (_next_offset_index + 1 <= end_index) { + HeapWord* rem_st = _array->address_for_index(_next_offset_index + 1); + // Calculate rem_end this way because end_index + // may be the last valid index in the covered region. + HeapWord* rem_end = _array->address_for_index(end_index) + N_words; + set_remainder_to_point_to_start(rem_st, rem_end); + } + + // _next_offset_index and _next_offset_threshold updated here. + _next_offset_index = end_index + 1; + // Calculate _next_offset_threshold this way because end_index + // may be the last valid index in the covered region. + _next_offset_threshold = _array->address_for_index(end_index) + + N_words; + assert(_next_offset_threshold >= blk_end, "Incorrent offset threshold"); + +#ifdef ASSERT + // The offset can be 0 if the block starts on a boundary. That + // is checked by an assertion above. + size_t start_index = _array->index_for(blk_start); + HeapWord* boundary = _array->address_for_index(start_index); + assert((_array->offset_array(orig_next_offset_index) == 0 && + blk_start == boundary) || + (_array->offset_array(orig_next_offset_index) > 0 && + _array->offset_array(orig_next_offset_index) <= N_words), + "offset array should have been set"); + for (size_t j = orig_next_offset_index + 1; j <= end_index; j++) { + assert(_array->offset_array(j) > 0 && + _array->offset_array(j) <= (u_char) (N_words+N_powers-1), + "offset array should have been set"); + } +#endif +} + +HeapWord* BlockOffsetArrayContigSpace::initialize_threshold() { + assert(!Universe::heap()->is_in_reserved(_array->_offset_array), + "just checking"); + _next_offset_index = _array->index_for(_bottom); + _next_offset_index++; + _next_offset_threshold = + _array->address_for_index(_next_offset_index); + return _next_offset_threshold; +} + +void BlockOffsetArrayContigSpace::zero_bottom_entry() { + assert(!Universe::heap()->is_in_reserved(_array->_offset_array), + "just checking"); + size_t bottom_index = _array->index_for(_bottom); + _array->set_offset_array(bottom_index, 0); +} + + +void BlockOffsetArrayContigSpace::serialize(SerializeOopClosure* soc) { + if (soc->reading()) { + // Null these values so that the serializer won't object to updating them. + _next_offset_threshold = NULL; + _next_offset_index = 0; + } + soc->do_ptr(&_next_offset_threshold); + soc->do_size_t(&_next_offset_index); +} + +size_t BlockOffsetArrayContigSpace::last_active_index() const { + size_t result = _next_offset_index - 1; + return result >= 0 ? result : 0; +} diff --git a/hotspot/src/share/vm/memory/blockOffsetTable.hpp b/hotspot/src/share/vm/memory/blockOffsetTable.hpp new file mode 100644 index 00000000000..fc76fc9208c --- /dev/null +++ b/hotspot/src/share/vm/memory/blockOffsetTable.hpp @@ -0,0 +1,500 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The CollectedHeap type requires subtypes to implement a method +// "block_start". For some subtypes, notably generational +// systems using card-table-based write barriers, the efficiency of this +// operation may be important. Implementations of the "BlockOffsetArray" +// class may be useful in providing such efficient implementations. +// +// BlockOffsetTable (abstract) +// - BlockOffsetArray (abstract) +// - BlockOffsetArrayNonContigSpace +// - BlockOffsetArrayContigSpace +// + +class ContiguousSpace; +class SerializeOopClosure; + +////////////////////////////////////////////////////////////////////////// +// The BlockOffsetTable "interface" +////////////////////////////////////////////////////////////////////////// +class BlockOffsetTable VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; +protected: + // These members describe the region covered by the table. + + // The space this table is covering. + HeapWord* _bottom; // == reserved.start + HeapWord* _end; // End of currently allocated region. + +public: + // Initialize the table to cover the given space. + // The contents of the initial table are undefined. + BlockOffsetTable(HeapWord* bottom, HeapWord* end): + _bottom(bottom), _end(end) { + assert(_bottom <= _end, "arguments out of order"); + } + + // Note that the committed size of the covered space may have changed, + // so the table size might also wish to change. + virtual void resize(size_t new_word_size) = 0; + + virtual void set_bottom(HeapWord* new_bottom) { + assert(new_bottom <= _end, "new_bottom > _end"); + _bottom = new_bottom; + resize(pointer_delta(_end, _bottom)); + } + + // Requires "addr" to be contained by a block, and returns the address of + // the start of that block. + virtual HeapWord* block_start_unsafe(const void* addr) const = 0; + + // Returns the address of the start of the block containing "addr", or + // else "null" if it is covered by no block. + HeapWord* block_start(const void* addr) const; +}; + +////////////////////////////////////////////////////////////////////////// +// One implementation of "BlockOffsetTable," the BlockOffsetArray, +// divides the covered region into "N"-word subregions (where +// "N" = 2^"LogN". An array with an entry for each such subregion +// indicates how far back one must go to find the start of the +// chunk that includes the first word of the subregion. +// +// Each BlockOffsetArray is owned by a Space. However, the actual array +// may be shared by several BlockOffsetArrays; this is useful +// when a single resizable area (such as a generation) is divided up into +// several spaces in which contiguous allocation takes place. (Consider, +// for example, the garbage-first generation.) + +// Here is the shared array type. +////////////////////////////////////////////////////////////////////////// +// BlockOffsetSharedArray +////////////////////////////////////////////////////////////////////////// +class BlockOffsetSharedArray: public CHeapObj { + friend class BlockOffsetArray; + friend class BlockOffsetArrayNonContigSpace; + friend class BlockOffsetArrayContigSpace; + friend class VMStructs; + + private: + enum SomePrivateConstants { + LogN = 9, + LogN_words = LogN - LogHeapWordSize, + N_bytes = 1 << LogN, + N_words = 1 << LogN_words + }; + + // The reserved region covered by the shared array. + MemRegion _reserved; + + // End of the current committed region. + HeapWord* _end; + + // Array for keeping offsets for retrieving object start fast given an + // address. + VirtualSpace _vs; + u_char* _offset_array; // byte array keeping backwards offsets + + protected: + // Bounds checking accessors: + // For performance these have to devolve to array accesses in product builds. + u_char offset_array(size_t index) const { + assert(index < _vs.committed_size(), "index out of range"); + return _offset_array[index]; + } + void set_offset_array(size_t index, u_char offset) { + assert(index < _vs.committed_size(), "index out of range"); + _offset_array[index] = offset; + } + void set_offset_array(size_t index, HeapWord* high, HeapWord* low) { + assert(index < _vs.committed_size(), "index out of range"); + assert(high >= low, "addresses out of order"); + assert(pointer_delta(high, low) <= N_words, "offset too large"); + _offset_array[index] = (u_char)pointer_delta(high, low); + } + void set_offset_array(HeapWord* left, HeapWord* right, u_char offset) { + assert(index_for(right - 1) < _vs.committed_size(), + "right address out of range"); + assert(left < right, "Heap addresses out of order"); + size_t num_cards = pointer_delta(right, left) >> LogN_words; + memset(&_offset_array[index_for(left)], offset, num_cards); + } + + void set_offset_array(size_t left, size_t right, u_char offset) { + assert(right < _vs.committed_size(), "right address out of range"); + assert(left <= right, "indexes out of order"); + size_t num_cards = right - left + 1; + memset(&_offset_array[left], offset, num_cards); + } + + void check_offset_array(size_t index, HeapWord* high, HeapWord* low) const { + assert(index < _vs.committed_size(), "index out of range"); + assert(high >= low, "addresses out of order"); + assert(pointer_delta(high, low) <= N_words, "offset too large"); + assert(_offset_array[index] == pointer_delta(high, low), + "Wrong offset"); + } + + bool is_card_boundary(HeapWord* p) const; + + // Return the number of slots needed for an offset array + // that covers mem_region_words words. + // We always add an extra slot because if an object + // ends on a card boundary we put a 0 in the next + // offset array slot, so we want that slot always + // to be reserved. + + size_t compute_size(size_t mem_region_words) { + size_t number_of_slots = (mem_region_words / N_words) + 1; + return ReservedSpace::allocation_align_size_up(number_of_slots); + } + +public: + // Initialize the table to cover from "base" to (at least) + // "base + init_word_size". In the future, the table may be expanded + // (see "resize" below) up to the size of "_reserved" (which must be at + // least "init_word_size".) The contents of the initial table are + // undefined; it is the responsibility of the constituent + // BlockOffsetTable(s) to initialize cards. + BlockOffsetSharedArray(MemRegion reserved, size_t init_word_size); + + // Notes a change in the committed size of the region covered by the + // table. The "new_word_size" may not be larger than the size of the + // reserved region this table covers. + void resize(size_t new_word_size); + + void set_bottom(HeapWord* new_bottom); + + // Updates all the BlockOffsetArray's sharing this shared array to + // reflect the current "top"'s of their spaces. + void update_offset_arrays(); // Not yet implemented! + + // Return the appropriate index into "_offset_array" for "p". + size_t index_for(const void* p) const; + + // Return the address indicating the start of the region corresponding to + // "index" in "_offset_array". + HeapWord* address_for_index(size_t index) const; + + // Shared space support + void serialize(SerializeOopClosure* soc, HeapWord* start, HeapWord* end); +}; + +////////////////////////////////////////////////////////////////////////// +// The BlockOffsetArray whose subtypes use the BlockOffsetSharedArray. +////////////////////////////////////////////////////////////////////////// +class BlockOffsetArray: public BlockOffsetTable { + friend class VMStructs; + protected: + // The following enums are used by do_block_internal() below + enum Action { + Action_single, // BOT records a single block (see single_block()) + Action_mark, // BOT marks the start of a block (see mark_block()) + Action_check // Check that BOT records block correctly + // (see verify_single_block()). + }; + + enum SomePrivateConstants { + N_words = BlockOffsetSharedArray::N_words, + LogN = BlockOffsetSharedArray::LogN, + // entries "e" of at least N_words mean "go back by Base^(e-N_words)." + // All entries are less than "N_words + N_powers". + LogBase = 4, + Base = (1 << LogBase), + N_powers = 14 + }; + + static size_t power_to_cards_back(uint i) { + return 1 << (LogBase * i); + } + static size_t power_to_words_back(uint i) { + return power_to_cards_back(i) * N_words; + } + static size_t entry_to_cards_back(u_char entry) { + assert(entry >= N_words, "Precondition"); + return power_to_cards_back(entry - N_words); + } + static size_t entry_to_words_back(u_char entry) { + assert(entry >= N_words, "Precondition"); + return power_to_words_back(entry - N_words); + } + + // The shared array, which is shared with other BlockOffsetArray's + // corresponding to different spaces within a generation or span of + // memory. + BlockOffsetSharedArray* _array; + + // The space that owns this subregion. + Space* _sp; + + // If true, array entries are initialized to 0; otherwise, they are + // initialized to point backwards to the beginning of the covered region. + bool _init_to_zero; + + // Sets the entries + // corresponding to the cards starting at "start" and ending at "end" + // to point back to the card before "start": the interval [start, end) + // is right-open. + void set_remainder_to_point_to_start(HeapWord* start, HeapWord* end); + // Same as above, except that the args here are a card _index_ interval + // that is closed: [start_index, end_index] + void set_remainder_to_point_to_start_incl(size_t start, size_t end); + + // A helper function for BOT adjustment/verification work + void do_block_internal(HeapWord* blk_start, HeapWord* blk_end, Action action); + + public: + // The space may not have its bottom and top set yet, which is why the + // region is passed as a parameter. If "init_to_zero" is true, the + // elements of the array are initialized to zero. Otherwise, they are + // initialized to point backwards to the beginning. + BlockOffsetArray(BlockOffsetSharedArray* array, MemRegion mr, + bool init_to_zero); + + // Note: this ought to be part of the constructor, but that would require + // "this" to be passed as a parameter to a member constructor for + // the containing concrete subtype of Space. + // This would be legal C++, but MS VC++ doesn't allow it. + void set_space(Space* sp) { _sp = sp; } + + // Resets the covered region to the given "mr". + void set_region(MemRegion mr) { + _bottom = mr.start(); + _end = mr.end(); + } + + // Note that the committed size of the covered space may have changed, + // so the table size might also wish to change. + virtual void resize(size_t new_word_size) { + HeapWord* new_end = _bottom + new_word_size; + if (_end < new_end && !init_to_zero()) { + // verify that the old and new boundaries are also card boundaries + assert(_array->is_card_boundary(_end), + "_end not a card boundary"); + assert(_array->is_card_boundary(new_end), + "new _end would not be a card boundary"); + // set all the newly added cards + _array->set_offset_array(_end, new_end, N_words); + } + _end = new_end; // update _end + } + + // Adjust the BOT to show that it has a single block in the + // range [blk_start, blk_start + size). All necessary BOT + // cards are adjusted, but _unallocated_block isn't. + void single_block(HeapWord* blk_start, HeapWord* blk_end); + void single_block(HeapWord* blk, size_t size) { + single_block(blk, blk + size); + } + + // When the alloc_block() call returns, the block offset table should + // have enough information such that any subsequent block_start() call + // with an argument equal to an address that is within the range + // [blk_start, blk_end) would return the value blk_start, provided + // there have been no calls in between that reset this information + // (e.g. see BlockOffsetArrayNonContigSpace::single_block() call + // for an appropriate range covering the said interval). + // These methods expect to be called with [blk_start, blk_end) + // representing a block of memory in the heap. + virtual void alloc_block(HeapWord* blk_start, HeapWord* blk_end); + void alloc_block(HeapWord* blk, size_t size) { + alloc_block(blk, blk + size); + } + + // If true, initialize array slots with no allocated blocks to zero. + // Otherwise, make them point back to the front. + bool init_to_zero() { return _init_to_zero; } + + // Debugging + // Return the index of the last entry in the "active" region. + virtual size_t last_active_index() const = 0; + // Verify the block offset table + void verify() const; + void check_all_cards(size_t left_card, size_t right_card) const; +}; + +//////////////////////////////////////////////////////////////////////////// +// A subtype of BlockOffsetArray that takes advantage of the fact +// that its underlying space is a NonContiguousSpace, so that some +// specialized interfaces can be made available for spaces that +// manipulate the table. +//////////////////////////////////////////////////////////////////////////// +class BlockOffsetArrayNonContigSpace: public BlockOffsetArray { + friend class VMStructs; + private: + // The portion [_unallocated_block, _sp.end()) of the space that + // is a single block known not to contain any objects. + // NOTE: See BlockOffsetArrayUseUnallocatedBlock flag. + HeapWord* _unallocated_block; + + public: + BlockOffsetArrayNonContigSpace(BlockOffsetSharedArray* array, MemRegion mr): + BlockOffsetArray(array, mr, false), + _unallocated_block(_bottom) { } + + // accessor + HeapWord* unallocated_block() const { + assert(BlockOffsetArrayUseUnallocatedBlock, + "_unallocated_block is not being maintained"); + return _unallocated_block; + } + + void set_unallocated_block(HeapWord* block) { + assert(BlockOffsetArrayUseUnallocatedBlock, + "_unallocated_block is not being maintained"); + assert(block >= _bottom && block <= _end, "out of range"); + _unallocated_block = block; + } + + // These methods expect to be called with [blk_start, blk_end) + // representing a block of memory in the heap. + void alloc_block(HeapWord* blk_start, HeapWord* blk_end); + void alloc_block(HeapWord* blk, size_t size) { + alloc_block(blk, blk + size); + } + + // The following methods are useful and optimized for a + // non-contiguous space. + + // Given a block [blk_start, blk_start + full_blk_size), and + // a left_blk_size < full_blk_size, adjust the BOT to show two + // blocks [blk_start, blk_start + left_blk_size) and + // [blk_start + left_blk_size, blk_start + full_blk_size). + // It is assumed (and verified in the non-product VM) that the + // BOT was correct for the original block. + void split_block(HeapWord* blk_start, size_t full_blk_size, + size_t left_blk_size); + + // Adjust BOT to show that it has a block in the range + // [blk_start, blk_start + size). Only the first card + // of BOT is touched. It is assumed (and verified in the + // non-product VM) that the remaining cards of the block + // are correct. + void mark_block(HeapWord* blk_start, HeapWord* blk_end); + void mark_block(HeapWord* blk, size_t size) { + mark_block(blk, blk + size); + } + + // Adjust _unallocated_block to indicate that a particular + // block has been newly allocated or freed. It is assumed (and + // verified in the non-product VM) that the BOT is correct for + // the given block. + void allocated(HeapWord* blk_start, HeapWord* blk_end) { + // Verify that the BOT shows [blk, blk + blk_size) to be one block. + verify_single_block(blk_start, blk_end); + if (BlockOffsetArrayUseUnallocatedBlock) { + _unallocated_block = MAX2(_unallocated_block, blk_end); + } + } + + void allocated(HeapWord* blk, size_t size) { + allocated(blk, blk + size); + } + + void freed(HeapWord* blk_start, HeapWord* blk_end); + void freed(HeapWord* blk, size_t size) { + freed(blk, blk + size); + } + + HeapWord* block_start_unsafe(const void* addr) const; + + // Requires "addr" to be the start of a card and returns the + // start of the block that contains the given address. + HeapWord* block_start_careful(const void* addr) const; + + + // Verification & debugging: ensure that the offset table reflects + // the fact that the block [blk_start, blk_end) or [blk, blk + size) + // is a single block of storage. NOTE: can't const this because of + // call to non-const do_block_internal() below. + void verify_single_block(HeapWord* blk_start, HeapWord* blk_end) + PRODUCT_RETURN; + void verify_single_block(HeapWord* blk, size_t size) PRODUCT_RETURN; + + // Verify that the given block is before _unallocated_block + void verify_not_unallocated(HeapWord* blk_start, HeapWord* blk_end) + const PRODUCT_RETURN; + void verify_not_unallocated(HeapWord* blk, size_t size) + const PRODUCT_RETURN; + + // Debugging support + virtual size_t last_active_index() const; +}; + +//////////////////////////////////////////////////////////////////////////// +// A subtype of BlockOffsetArray that takes advantage of the fact +// that its underlying space is a ContiguousSpace, so that its "active" +// region can be more efficiently tracked (than for a non-contiguous space). +//////////////////////////////////////////////////////////////////////////// +class BlockOffsetArrayContigSpace: public BlockOffsetArray { + friend class VMStructs; + private: + // allocation boundary at which offset array must be updated + HeapWord* _next_offset_threshold; + size_t _next_offset_index; // index corresponding to that boundary + + // Work function when allocation start crosses threshold. + void alloc_block_work(HeapWord* blk_start, HeapWord* blk_end); + + public: + BlockOffsetArrayContigSpace(BlockOffsetSharedArray* array, MemRegion mr): + BlockOffsetArray(array, mr, true) { + _next_offset_threshold = NULL; + _next_offset_index = 0; + } + + void set_contig_space(ContiguousSpace* sp) { set_space((Space*)sp); } + + // Initialize the threshold for an empty heap. + HeapWord* initialize_threshold(); + // Zero out the entry for _bottom (offset will be zero) + void zero_bottom_entry(); + + // Return the next threshold, the point at which the table should be + // updated. + HeapWord* threshold() const { return _next_offset_threshold; } + + // In general, these methods expect to be called with + // [blk_start, blk_end) representing a block of memory in the heap. + // In this implementation, however, we are OK even if blk_start and/or + // blk_end are NULL because NULL is represented as 0, and thus + // never exceeds the "_next_offset_threshold". + void alloc_block(HeapWord* blk_start, HeapWord* blk_end) { + if (blk_end > _next_offset_threshold) { + alloc_block_work(blk_start, blk_end); + } + } + void alloc_block(HeapWord* blk, size_t size) { + alloc_block(blk, blk + size); + } + + HeapWord* block_start_unsafe(const void* addr) const; + + void serialize(SerializeOopClosure* soc); + + // Debugging support + virtual size_t last_active_index() const; +}; diff --git a/hotspot/src/share/vm/memory/blockOffsetTable.inline.hpp b/hotspot/src/share/vm/memory/blockOffsetTable.inline.hpp new file mode 100644 index 00000000000..b49fabbd551 --- /dev/null +++ b/hotspot/src/share/vm/memory/blockOffsetTable.inline.hpp @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +////////////////////////////////////////////////////////////////////////// +// BlockOffsetTable inlines +////////////////////////////////////////////////////////////////////////// +inline HeapWord* BlockOffsetTable::block_start(const void* addr) const { + if (addr >= _bottom && addr < _end) { + return block_start_unsafe(addr); + } else { + return NULL; + } +} + +////////////////////////////////////////////////////////////////////////// +// BlockOffsetSharedArray inlines +////////////////////////////////////////////////////////////////////////// +inline size_t BlockOffsetSharedArray::index_for(const void* p) const { + char* pc = (char*)p; + assert(pc >= (char*)_reserved.start() && + pc < (char*)_reserved.end(), + "p not in range."); + size_t delta = pointer_delta(pc, _reserved.start(), sizeof(char)); + size_t result = delta >> LogN; + assert(result < _vs.committed_size(), "bad index from address"); + return result; +} + +inline HeapWord* BlockOffsetSharedArray::address_for_index(size_t index) const { + assert(index < _vs.committed_size(), "bad index"); + HeapWord* result = _reserved.start() + (index << LogN_words); + assert(result >= _reserved.start() && result < _reserved.end(), + "bad address from index"); + return result; +} + + +////////////////////////////////////////////////////////////////////////// +// BlockOffsetArrayNonContigSpace inlines +////////////////////////////////////////////////////////////////////////// +inline void BlockOffsetArrayNonContigSpace::freed(HeapWord* blk_start, + HeapWord* blk_end) { + // Verify that the BOT shows [blk_start, blk_end) to be one block. + verify_single_block(blk_start, blk_end); + // adjust _unallocated_block upward or downward + // as appropriate + if (BlockOffsetArrayUseUnallocatedBlock) { + assert(_unallocated_block <= _end, + "Inconsistent value for _unallocated_block"); + if (blk_end >= _unallocated_block && blk_start <= _unallocated_block) { + // CMS-specific note: a block abutting _unallocated_block to + // its left is being freed, a new block is being added or + // we are resetting following a compaction + _unallocated_block = blk_start; + } + } +} diff --git a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp new file mode 100644 index 00000000000..db7ffffc74e --- /dev/null +++ b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp @@ -0,0 +1,547 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This kind of "BarrierSet" allows a "CollectedHeap" to detect and +// enumerate ref fields that have been modified (since the last +// enumeration.) + +# include "incls/_precompiled.incl" +# include "incls/_cardTableModRefBS.cpp.incl" + +size_t CardTableModRefBS::cards_required(size_t covered_words) +{ + // Add one for a guard card, used to detect errors. + const size_t words = align_size_up(covered_words, card_size_in_words); + return words / card_size_in_words + 1; +} + +size_t CardTableModRefBS::compute_byte_map_size() +{ + assert(_guard_index == cards_required(_whole_heap.word_size()) - 1, + "unitialized, check declaration order"); + assert(_page_size != 0, "unitialized, check declaration order"); + const size_t granularity = os::vm_allocation_granularity(); + return align_size_up(_guard_index + 1, MAX2(_page_size, granularity)); +} + +CardTableModRefBS::CardTableModRefBS(MemRegion whole_heap, + int max_covered_regions): + ModRefBarrierSet(max_covered_regions), + _whole_heap(whole_heap), + _guard_index(cards_required(whole_heap.word_size()) - 1), + _last_valid_index(_guard_index - 1), + _page_size(os::page_size_for_region(_guard_index + 1, _guard_index + 1, 1)), + _byte_map_size(compute_byte_map_size()) +{ + _kind = BarrierSet::CardTableModRef; + + HeapWord* low_bound = _whole_heap.start(); + HeapWord* high_bound = _whole_heap.end(); + assert((uintptr_t(low_bound) & (card_size - 1)) == 0, "heap must start at card boundary"); + assert((uintptr_t(high_bound) & (card_size - 1)) == 0, "heap must end at card boundary"); + + assert(card_size <= 512, "card_size must be less than 512"); // why? + + _covered = new MemRegion[max_covered_regions]; + _committed = new MemRegion[max_covered_regions]; + if (_covered == NULL || _committed == NULL) + vm_exit_during_initialization("couldn't alloc card table covered region set."); + int i; + for (i = 0; i < max_covered_regions; i++) { + _covered[i].set_word_size(0); + _committed[i].set_word_size(0); + } + _cur_covered_regions = 0; + + const size_t rs_align = _page_size == (size_t) os::vm_page_size() ? 0 : + MAX2(_page_size, (size_t) os::vm_allocation_granularity()); + ReservedSpace heap_rs(_byte_map_size, rs_align, false); + os::trace_page_sizes("card table", _guard_index + 1, _guard_index + 1, + _page_size, heap_rs.base(), heap_rs.size()); + if (!heap_rs.is_reserved()) { + vm_exit_during_initialization("Could not reserve enough space for the " + "card marking array"); + } + + // The assember store_check code will do an unsigned shift of the oop, + // then add it to byte_map_base, i.e. + // + // _byte_map = byte_map_base + (uintptr_t(low_bound) >> card_shift) + _byte_map = (jbyte*) heap_rs.base(); + byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift); + assert(byte_for(low_bound) == &_byte_map[0], "Checking start of map"); + assert(byte_for(high_bound-1) <= &_byte_map[_last_valid_index], "Checking end of map"); + + jbyte* guard_card = &_byte_map[_guard_index]; + uintptr_t guard_page = align_size_down((uintptr_t)guard_card, _page_size); + _guard_region = MemRegion((HeapWord*)guard_page, _page_size); + if (!os::commit_memory((char*)guard_page, _page_size, _page_size)) { + // Do better than this for Merlin + vm_exit_out_of_memory(_page_size, "card table last card"); + } + *guard_card = last_card; + + _lowest_non_clean = + NEW_C_HEAP_ARRAY(CardArr, max_covered_regions); + _lowest_non_clean_chunk_size = + NEW_C_HEAP_ARRAY(size_t, max_covered_regions); + _lowest_non_clean_base_chunk_index = + NEW_C_HEAP_ARRAY(uintptr_t, max_covered_regions); + _last_LNC_resizing_collection = + NEW_C_HEAP_ARRAY(int, max_covered_regions); + if (_lowest_non_clean == NULL + || _lowest_non_clean_chunk_size == NULL + || _lowest_non_clean_base_chunk_index == NULL + || _last_LNC_resizing_collection == NULL) + vm_exit_during_initialization("couldn't allocate an LNC array."); + for (i = 0; i < max_covered_regions; i++) { + _lowest_non_clean[i] = NULL; + _lowest_non_clean_chunk_size[i] = 0; + _last_LNC_resizing_collection[i] = -1; + } + + if (TraceCardTableModRefBS) { + gclog_or_tty->print_cr("CardTableModRefBS::CardTableModRefBS: "); + gclog_or_tty->print_cr(" " + " &_byte_map[0]: " INTPTR_FORMAT + " &_byte_map[_last_valid_index]: " INTPTR_FORMAT, + &_byte_map[0], + &_byte_map[_last_valid_index]); + gclog_or_tty->print_cr(" " + " byte_map_base: " INTPTR_FORMAT, + byte_map_base); + } +} + +int CardTableModRefBS::find_covering_region_by_base(HeapWord* base) { + int i; + for (i = 0; i < _cur_covered_regions; i++) { + if (_covered[i].start() == base) return i; + if (_covered[i].start() > base) break; + } + // If we didn't find it, create a new one. + assert(_cur_covered_regions < _max_covered_regions, + "too many covered regions"); + // Move the ones above up, to maintain sorted order. + for (int j = _cur_covered_regions; j > i; j--) { + _covered[j] = _covered[j-1]; + _committed[j] = _committed[j-1]; + } + int res = i; + _cur_covered_regions++; + _covered[res].set_start(base); + _covered[res].set_word_size(0); + jbyte* ct_start = byte_for(base); + uintptr_t ct_start_aligned = align_size_down((uintptr_t)ct_start, _page_size); + _committed[res].set_start((HeapWord*)ct_start_aligned); + _committed[res].set_word_size(0); + return res; +} + +int CardTableModRefBS::find_covering_region_containing(HeapWord* addr) { + for (int i = 0; i < _cur_covered_regions; i++) { + if (_covered[i].contains(addr)) { + return i; + } + } + assert(0, "address outside of heap?"); + return -1; +} + +HeapWord* CardTableModRefBS::largest_prev_committed_end(int ind) const { + HeapWord* max_end = NULL; + for (int j = 0; j < ind; j++) { + HeapWord* this_end = _committed[j].end(); + if (this_end > max_end) max_end = this_end; + } + return max_end; +} + +MemRegion CardTableModRefBS::committed_unique_to_self(int self, + MemRegion mr) const { + MemRegion result = mr; + for (int r = 0; r < _cur_covered_regions; r += 1) { + if (r != self) { + result = result.minus(_committed[r]); + } + } + // Never include the guard page. + result = result.minus(_guard_region); + return result; +} + +void CardTableModRefBS::resize_covered_region(MemRegion new_region) { + // We don't change the start of a region, only the end. + assert(_whole_heap.contains(new_region), + "attempt to cover area not in reserved area"); + debug_only(verify_guard();) + int ind = find_covering_region_by_base(new_region.start()); + MemRegion old_region = _covered[ind]; + assert(old_region.start() == new_region.start(), "just checking"); + if (new_region.word_size() != old_region.word_size()) { + // Commit new or uncommit old pages, if necessary. + MemRegion cur_committed = _committed[ind]; + // Extend the end of this _commited region + // to cover the end of any lower _committed regions. + // This forms overlapping regions, but never interior regions. + HeapWord* max_prev_end = largest_prev_committed_end(ind); + if (max_prev_end > cur_committed.end()) { + cur_committed.set_end(max_prev_end); + } + // Align the end up to a page size (starts are already aligned). + jbyte* new_end = byte_after(new_region.last()); + HeapWord* new_end_aligned = + (HeapWord*)align_size_up((uintptr_t)new_end, _page_size); + assert(new_end_aligned >= (HeapWord*) new_end, + "align up, but less"); + // The guard page is always committed and should not be committed over. + HeapWord* new_end_for_commit = MIN2(new_end_aligned, _guard_region.start()); + if (new_end_for_commit > cur_committed.end()) { + // Must commit new pages. + MemRegion new_committed = + MemRegion(cur_committed.end(), new_end_for_commit); + + assert(!new_committed.is_empty(), "Region should not be empty here"); + if (!os::commit_memory((char*)new_committed.start(), + new_committed.byte_size(), _page_size)) { + // Do better than this for Merlin + vm_exit_out_of_memory(new_committed.byte_size(), + "card table expansion"); + } + // Use new_end_aligned (as opposed to new_end_for_commit) because + // the cur_committed region may include the guard region. + } else if (new_end_aligned < cur_committed.end()) { + // Must uncommit pages. + MemRegion uncommit_region = + committed_unique_to_self(ind, MemRegion(new_end_aligned, + cur_committed.end())); + if (!uncommit_region.is_empty()) { + if (!os::uncommit_memory((char*)uncommit_region.start(), + uncommit_region.byte_size())) { + // Do better than this for Merlin + vm_exit_out_of_memory(uncommit_region.byte_size(), + "card table contraction"); + } + } + } + // In any case, we can reset the end of the current committed entry. + _committed[ind].set_end(new_end_aligned); + + // The default of 0 is not necessarily clean cards. + jbyte* entry; + if (old_region.last() < _whole_heap.start()) { + entry = byte_for(_whole_heap.start()); + } else { + entry = byte_after(old_region.last()); + } + assert(index_for(new_region.last()) < (int) _guard_index, + "The guard card will be overwritten"); + jbyte* end = byte_after(new_region.last()); + // do nothing if we resized downward. + if (entry < end) { + memset(entry, clean_card, pointer_delta(end, entry, sizeof(jbyte))); + } + } + // In any case, the covered size changes. + _covered[ind].set_word_size(new_region.word_size()); + if (TraceCardTableModRefBS) { + gclog_or_tty->print_cr("CardTableModRefBS::resize_covered_region: "); + gclog_or_tty->print_cr(" " + " _covered[%d].start(): " INTPTR_FORMAT + " _covered[%d].last(): " INTPTR_FORMAT, + ind, _covered[ind].start(), + ind, _covered[ind].last()); + gclog_or_tty->print_cr(" " + " _committed[%d].start(): " INTPTR_FORMAT + " _committed[%d].last(): " INTPTR_FORMAT, + ind, _committed[ind].start(), + ind, _committed[ind].last()); + gclog_or_tty->print_cr(" " + " byte_for(start): " INTPTR_FORMAT + " byte_for(last): " INTPTR_FORMAT, + byte_for(_covered[ind].start()), + byte_for(_covered[ind].last())); + gclog_or_tty->print_cr(" " + " addr_for(start): " INTPTR_FORMAT + " addr_for(last): " INTPTR_FORMAT, + addr_for((jbyte*) _committed[ind].start()), + addr_for((jbyte*) _committed[ind].last())); + } + debug_only(verify_guard();) +} + +// Note that these versions are precise! The scanning code has to handle the +// fact that the write barrier may be either precise or imprecise. + +void CardTableModRefBS::write_ref_field_work(oop* field, oop newVal) { + inline_write_ref_field(field, newVal); +} + + +void CardTableModRefBS::non_clean_card_iterate(Space* sp, + MemRegion mr, + DirtyCardToOopClosure* dcto_cl, + MemRegionClosure* cl, + bool clear) { + if (!mr.is_empty()) { + int n_threads = SharedHeap::heap()->n_par_threads(); + if (n_threads > 0) { +#ifndef SERIALGC + par_non_clean_card_iterate_work(sp, mr, dcto_cl, cl, clear, n_threads); +#else // SERIALGC + fatal("Parallel gc not supported here."); +#endif // SERIALGC + } else { + non_clean_card_iterate_work(mr, cl, clear); + } + } +} + +// NOTE: For this to work correctly, it is important that +// we look for non-clean cards below (so as to catch those +// marked precleaned), rather than look explicitly for dirty +// cards (and miss those marked precleaned). In that sense, +// the name precleaned is currently somewhat of a misnomer. +void CardTableModRefBS::non_clean_card_iterate_work(MemRegion mr, + MemRegionClosure* cl, + bool clear) { + // Figure out whether we have to worry about parallelism. + bool is_par = (SharedHeap::heap()->n_par_threads() > 1); + for (int i = 0; i < _cur_covered_regions; i++) { + MemRegion mri = mr.intersection(_covered[i]); + if (mri.word_size() > 0) { + jbyte* cur_entry = byte_for(mri.last()); + jbyte* limit = byte_for(mri.start()); + while (cur_entry >= limit) { + jbyte* next_entry = cur_entry - 1; + if (*cur_entry != clean_card) { + size_t non_clean_cards = 1; + // Should the next card be included in this range of dirty cards. + while (next_entry >= limit && *next_entry != clean_card) { + non_clean_cards++; + cur_entry = next_entry; + next_entry--; + } + // The memory region may not be on a card boundary. So that + // objects beyond the end of the region are not processed, make + // cur_cards precise with regard to the end of the memory region. + MemRegion cur_cards(addr_for(cur_entry), + non_clean_cards * card_size_in_words); + MemRegion dirty_region = cur_cards.intersection(mri); + if (clear) { + for (size_t i = 0; i < non_clean_cards; i++) { + // Clean the dirty cards (but leave the other non-clean + // alone.) If parallel, do the cleaning atomically. + jbyte cur_entry_val = cur_entry[i]; + if (card_is_dirty_wrt_gen_iter(cur_entry_val)) { + if (is_par) { + jbyte res = Atomic::cmpxchg(clean_card, &cur_entry[i], cur_entry_val); + assert(res != clean_card, + "Dirty card mysteriously cleaned"); + } else { + cur_entry[i] = clean_card; + } + } + } + } + cl->do_MemRegion(dirty_region); + } + cur_entry = next_entry; + } + } + } +} + +void CardTableModRefBS::mod_oop_in_space_iterate(Space* sp, + OopClosure* cl, + bool clear, + bool before_save_marks) { + // Note that dcto_cl is resource-allocated, so there is no + // corresponding "delete". + DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, precision()); + MemRegion used_mr; + if (before_save_marks) { + used_mr = sp->used_region_at_save_marks(); + } else { + used_mr = sp->used_region(); + } + non_clean_card_iterate(sp, used_mr, dcto_cl, dcto_cl, clear); +} + +void CardTableModRefBS::dirty_MemRegion(MemRegion mr) { + jbyte* cur = byte_for(mr.start()); + jbyte* last = byte_after(mr.last()); + while (cur < last) { + *cur = dirty_card; + cur++; + } +} + +void CardTableModRefBS::invalidate(MemRegion mr) { + for (int i = 0; i < _cur_covered_regions; i++) { + MemRegion mri = mr.intersection(_covered[i]); + if (!mri.is_empty()) dirty_MemRegion(mri); + } +} + +void CardTableModRefBS::clear_MemRegion(MemRegion mr) { + // Be conservative: only clean cards entirely contained within the + // region. + jbyte* cur; + if (mr.start() == _whole_heap.start()) { + cur = byte_for(mr.start()); + } else { + assert(mr.start() > _whole_heap.start(), "mr is not covered."); + cur = byte_after(mr.start() - 1); + } + jbyte* last = byte_after(mr.last()); + memset(cur, clean_card, pointer_delta(last, cur, sizeof(jbyte))); +} + +void CardTableModRefBS::clear(MemRegion mr) { + for (int i = 0; i < _cur_covered_regions; i++) { + MemRegion mri = mr.intersection(_covered[i]); + if (!mri.is_empty()) clear_MemRegion(mri); + } +} + +// NOTES: +// (1) Unlike mod_oop_in_space_iterate() above, dirty_card_iterate() +// iterates over dirty cards ranges in increasing address order. +// (2) Unlike, e.g., dirty_card_range_after_preclean() below, +// this method does not make the dirty cards prelceaned. +void CardTableModRefBS::dirty_card_iterate(MemRegion mr, + MemRegionClosure* cl) { + for (int i = 0; i < _cur_covered_regions; i++) { + MemRegion mri = mr.intersection(_covered[i]); + if (!mri.is_empty()) { + jbyte *cur_entry, *next_entry, *limit; + for (cur_entry = byte_for(mri.start()), limit = byte_for(mri.last()); + cur_entry <= limit; + cur_entry = next_entry) { + next_entry = cur_entry + 1; + if (*cur_entry == dirty_card) { + size_t dirty_cards; + // Accumulate maximal dirty card range, starting at cur_entry + for (dirty_cards = 1; + next_entry <= limit && *next_entry == dirty_card; + dirty_cards++, next_entry++); + MemRegion cur_cards(addr_for(cur_entry), + dirty_cards*card_size_in_words); + cl->do_MemRegion(cur_cards); + } + } + } + } +} + +MemRegion CardTableModRefBS::dirty_card_range_after_preclean(MemRegion mr) { + for (int i = 0; i < _cur_covered_regions; i++) { + MemRegion mri = mr.intersection(_covered[i]); + if (!mri.is_empty()) { + jbyte* cur_entry, *next_entry, *limit; + for (cur_entry = byte_for(mri.start()), limit = byte_for(mri.last()); + cur_entry <= limit; + cur_entry = next_entry) { + next_entry = cur_entry + 1; + if (*cur_entry == dirty_card) { + size_t dirty_cards; + // Accumulate maximal dirty card range, starting at cur_entry + for (dirty_cards = 1; + next_entry <= limit && *next_entry == dirty_card; + dirty_cards++, next_entry++); + MemRegion cur_cards(addr_for(cur_entry), + dirty_cards*card_size_in_words); + for (size_t i = 0; i < dirty_cards; i++) { + cur_entry[i] = precleaned_card; + } + return cur_cards; + } + } + } + } + return MemRegion(mr.end(), mr.end()); +} + +// Set all the dirty cards in the given region to "precleaned" state. +void CardTableModRefBS::preclean_dirty_cards(MemRegion mr) { + for (int i = 0; i < _cur_covered_regions; i++) { + MemRegion mri = mr.intersection(_covered[i]); + if (!mri.is_empty()) { + jbyte *cur_entry, *limit; + for (cur_entry = byte_for(mri.start()), limit = byte_for(mri.last()); + cur_entry <= limit; + cur_entry++) { + if (*cur_entry == dirty_card) { + *cur_entry = precleaned_card; + } + } + } + } +} + +uintx CardTableModRefBS::ct_max_alignment_constraint() { + return card_size * os::vm_page_size(); +} + +void CardTableModRefBS::verify_guard() { + // For product build verification + guarantee(_byte_map[_guard_index] == last_card, + "card table guard has been modified"); +} + +void CardTableModRefBS::verify() { + verify_guard(); +} + +#ifndef PRODUCT +class GuaranteeNotModClosure: public MemRegionClosure { + CardTableModRefBS* _ct; +public: + GuaranteeNotModClosure(CardTableModRefBS* ct) : _ct(ct) {} + void do_MemRegion(MemRegion mr) { + jbyte* entry = _ct->byte_for(mr.start()); + guarantee(*entry != CardTableModRefBS::clean_card, + "Dirty card in region that should be clean"); + } +}; + +void CardTableModRefBS::verify_clean_region(MemRegion mr) { + GuaranteeNotModClosure blk(this); + non_clean_card_iterate_work(mr, &blk, false); +} +#endif + +bool CardTableModRefBSForCTRS::card_will_be_scanned(jbyte cv) { + return + CardTableModRefBS::card_will_be_scanned(cv) || + _rs->is_prev_nonclean_card_val(cv); +}; + +bool CardTableModRefBSForCTRS::card_may_have_been_dirty(jbyte cv) { + return + cv != clean_card && + (CardTableModRefBS::card_may_have_been_dirty(cv) || + CardTableRS::youngergen_may_have_been_dirty(cv)); +}; diff --git a/hotspot/src/share/vm/memory/cardTableModRefBS.hpp b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp new file mode 100644 index 00000000000..393adcc951d --- /dev/null +++ b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp @@ -0,0 +1,421 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This kind of "BarrierSet" allows a "CollectedHeap" to detect and +// enumerate ref fields that have been modified (since the last +// enumeration.) + +// As it currently stands, this barrier is *imprecise*: when a ref field in +// an object "o" is modified, the card table entry for the card containing +// the head of "o" is dirtied, not necessarily the card containing the +// modified field itself. For object arrays, however, the barrier *is* +// precise; only the card containing the modified element is dirtied. +// Any MemRegionClosures used to scan dirty cards should take these +// considerations into account. + +class Generation; +class OopsInGenClosure; +class DirtyCardToOopClosure; + +class CardTableModRefBS: public ModRefBarrierSet { + // Some classes get to look at some private stuff. + friend class BytecodeInterpreter; + friend class VMStructs; + friend class CardTableRS; + friend class CheckForUnmarkedOops; // Needs access to raw card bytes. +#ifndef PRODUCT + // For debugging. + friend class GuaranteeNotModClosure; +#endif + protected: + + enum CardValues { + clean_card = -1, + dirty_card = 0, + precleaned_card = 1, + last_card = 4, + CT_MR_BS_last_reserved = 10 + }; + + // dirty and precleaned are equivalent wrt younger_refs_iter. + static bool card_is_dirty_wrt_gen_iter(jbyte cv) { + return cv == dirty_card || cv == precleaned_card; + } + + // Returns "true" iff the value "cv" will cause the card containing it + // to be scanned in the current traversal. May be overridden by + // subtypes. + virtual bool card_will_be_scanned(jbyte cv) { + return CardTableModRefBS::card_is_dirty_wrt_gen_iter(cv); + } + + // Returns "true" iff the value "cv" may have represented a dirty card at + // some point. + virtual bool card_may_have_been_dirty(jbyte cv) { + return card_is_dirty_wrt_gen_iter(cv); + } + + // The declaration order of these const fields is important; see the + // constructor before changing. + const MemRegion _whole_heap; // the region covered by the card table + const size_t _guard_index; // index of very last element in the card + // table; it is set to a guard value + // (last_card) and should never be modified + const size_t _last_valid_index; // index of the last valid element + const size_t _page_size; // page size used when mapping _byte_map + const size_t _byte_map_size; // in bytes + jbyte* _byte_map; // the card marking array + + int _cur_covered_regions; + // The covered regions should be in address order. + MemRegion* _covered; + // The committed regions correspond one-to-one to the covered regions. + // They represent the card-table memory that has been committed to service + // the corresponding covered region. It may be that committed region for + // one covered region corresponds to a larger region because of page-size + // roundings. Thus, a committed region for one covered region may + // actually extend onto the card-table space for the next covered region. + MemRegion* _committed; + + // The last card is a guard card, and we commit the page for it so + // we can use the card for verification purposes. We make sure we never + // uncommit the MemRegion for that page. + MemRegion _guard_region; + + protected: + // Initialization utilities; covered_words is the size of the covered region + // in, um, words. + inline size_t cards_required(size_t covered_words); + inline size_t compute_byte_map_size(); + + // Finds and return the index of the region, if any, to which the given + // region would be contiguous. If none exists, assign a new region and + // returns its index. Requires that no more than the maximum number of + // covered regions defined in the constructor are ever in use. + int find_covering_region_by_base(HeapWord* base); + + // Same as above, but finds the region containing the given address + // instead of starting at a given base address. + int find_covering_region_containing(HeapWord* addr); + + // Resize one of the regions covered by the remembered set. + void resize_covered_region(MemRegion new_region); + + // Returns the leftmost end of a committed region corresponding to a + // covered region before covered region "ind", or else "NULL" if "ind" is + // the first covered region. + HeapWord* largest_prev_committed_end(int ind) const; + + // Returns the part of the region mr that doesn't intersect with + // any committed region other than self. Used to prevent uncommitting + // regions that are also committed by other regions. Also protects + // against uncommitting the guard region. + MemRegion committed_unique_to_self(int self, MemRegion mr) const; + + // Mapping from address to card marking array entry + jbyte* byte_for(const void* p) const { + assert(_whole_heap.contains(p), + "out of bounds access to card marking array"); + jbyte* result = &byte_map_base[uintptr_t(p) >> card_shift]; + assert(result >= _byte_map && result < _byte_map + _byte_map_size, + "out of bounds accessor for card marking array"); + return result; + } + + // The card table byte one after the card marking array + // entry for argument address. Typically used for higher bounds + // for loops iterating through the card table. + jbyte* byte_after(const void* p) const { + return byte_for(p) + 1; + } + + // Mapping from card marking array entry to address of first word + HeapWord* addr_for(const jbyte* p) const { + assert(p >= _byte_map && p < _byte_map + _byte_map_size, + "out of bounds access to card marking array"); + size_t delta = pointer_delta(p, byte_map_base, sizeof(jbyte)); + HeapWord* result = (HeapWord*) (delta << card_shift); + assert(_whole_heap.contains(result), + "out of bounds accessor from card marking array"); + return result; + } + + // Iterate over the portion of the card-table which covers the given + // region mr in the given space and apply cl to any dirty sub-regions + // of mr. cl and dcto_cl must either be the same closure or cl must + // wrap dcto_cl. Both are required - neither may be NULL. Also, dcto_cl + // may be modified. Note that this function will operate in a parallel + // mode if worker threads are available. + void non_clean_card_iterate(Space* sp, MemRegion mr, + DirtyCardToOopClosure* dcto_cl, + MemRegionClosure* cl, + bool clear); + + // Utility function used to implement the other versions below. + void non_clean_card_iterate_work(MemRegion mr, MemRegionClosure* cl, + bool clear); + + void par_non_clean_card_iterate_work(Space* sp, MemRegion mr, + DirtyCardToOopClosure* dcto_cl, + MemRegionClosure* cl, + bool clear, + int n_threads); + + // Dirty the bytes corresponding to "mr" (not all of which must be + // covered.) + void dirty_MemRegion(MemRegion mr); + + // Clear (to clean_card) the bytes entirely contained within "mr" (not + // all of which must be covered.) + void clear_MemRegion(MemRegion mr); + + // *** Support for parallel card scanning. + + enum SomeConstantsForParallelism { + StridesPerThread = 2, + CardsPerStrideChunk = 256 + }; + + // This is an array, one element per covered region of the card table. + // Each entry is itself an array, with one element per chunk in the + // covered region. Each entry of these arrays is the lowest non-clean + // card of the corresponding chunk containing part of an object from the + // previous chunk, or else NULL. + typedef jbyte* CardPtr; + typedef CardPtr* CardArr; + CardArr* _lowest_non_clean; + size_t* _lowest_non_clean_chunk_size; + uintptr_t* _lowest_non_clean_base_chunk_index; + int* _last_LNC_resizing_collection; + + // Initializes "lowest_non_clean" to point to the array for the region + // covering "sp", and "lowest_non_clean_base_chunk_index" to the chunk + // index of the corresponding to the first element of that array. + // Ensures that these arrays are of sufficient size, allocating if necessary. + // May be called by several threads concurrently. + void get_LNC_array_for_space(Space* sp, + jbyte**& lowest_non_clean, + uintptr_t& lowest_non_clean_base_chunk_index, + size_t& lowest_non_clean_chunk_size); + + // Returns the number of chunks necessary to cover "mr". + size_t chunks_to_cover(MemRegion mr) { + return (size_t)(addr_to_chunk_index(mr.last()) - + addr_to_chunk_index(mr.start()) + 1); + } + + // Returns the index of the chunk in a stride which + // covers the given address. + uintptr_t addr_to_chunk_index(const void* addr) { + uintptr_t card = (uintptr_t) byte_for(addr); + return card / CardsPerStrideChunk; + } + + // Apply cl, which must either itself apply dcto_cl or be dcto_cl, + // to the cards in the stride (of n_strides) within the given space. + void process_stride(Space* sp, + MemRegion used, + jint stride, int n_strides, + DirtyCardToOopClosure* dcto_cl, + MemRegionClosure* cl, + bool clear, + jbyte** lowest_non_clean, + uintptr_t lowest_non_clean_base_chunk_index, + size_t lowest_non_clean_chunk_size); + + // Makes sure that chunk boundaries are handled appropriately, by + // adjusting the min_done of dcto_cl, and by using a special card-table + // value to indicate how min_done should be set. + void process_chunk_boundaries(Space* sp, + DirtyCardToOopClosure* dcto_cl, + MemRegion chunk_mr, + MemRegion used, + jbyte** lowest_non_clean, + uintptr_t lowest_non_clean_base_chunk_index, + size_t lowest_non_clean_chunk_size); + +public: + // Constants + enum SomePublicConstants { + card_shift = 9, + card_size = 1 << card_shift, + card_size_in_words = card_size / sizeof(HeapWord) + }; + + // For RTTI simulation. + BarrierSet::Name kind() { return BarrierSet::CardTableModRef; } + bool is_a(BarrierSet::Name bsn) { + return bsn == BarrierSet::CardTableModRef || bsn == BarrierSet::ModRef; + } + + CardTableModRefBS(MemRegion whole_heap, int max_covered_regions); + + // *** Barrier set functions. + + inline bool write_ref_needs_barrier(oop* field, oop new_val) { + // Note that this assumes the perm gen is the highest generation + // in the address space + return new_val != NULL && !new_val->is_perm(); + } + + // Record a reference update. Note that these versions are precise! + // The scanning code has to handle the fact that the write barrier may be + // either precise or imprecise. We make non-virtual inline variants of + // these functions here for performance. +protected: + void write_ref_field_work(oop obj, size_t offset, oop newVal); + void write_ref_field_work(oop* field, oop newVal); +public: + + bool has_write_ref_array_opt() { return true; } + bool has_write_region_opt() { return true; } + + inline void inline_write_region(MemRegion mr) { + dirty_MemRegion(mr); + } +protected: + void write_region_work(MemRegion mr) { + inline_write_region(mr); + } +public: + + inline void inline_write_ref_array(MemRegion mr) { + dirty_MemRegion(mr); + } +protected: + void write_ref_array_work(MemRegion mr) { + inline_write_ref_array(mr); + } +public: + + bool is_aligned(HeapWord* addr) { + return is_card_aligned(addr); + } + + // *** Card-table-barrier-specific things. + + inline void inline_write_ref_field(oop* field, oop newVal) { + jbyte* byte = byte_for(field); + *byte = dirty_card; + } + + // Card marking array base (adjusted for heap low boundary) + // This would be the 0th element of _byte_map, if the heap started at 0x0. + // But since the heap starts at some higher address, this points to somewhere + // before the beginning of the actual _byte_map. + jbyte* byte_map_base; + + // Return true if "p" is at the start of a card. + bool is_card_aligned(HeapWord* p) { + jbyte* pcard = byte_for(p); + return (addr_for(pcard) == p); + } + + // The kinds of precision a CardTableModRefBS may offer. + enum PrecisionStyle { + Precise, + ObjHeadPreciseArray + }; + + // Tells what style of precision this card table offers. + PrecisionStyle precision() { + return ObjHeadPreciseArray; // Only one supported for now. + } + + // ModRefBS functions. + void invalidate(MemRegion mr); + void clear(MemRegion mr); + void mod_oop_in_space_iterate(Space* sp, OopClosure* cl, + bool clear = false, + bool before_save_marks = false); + + // *** Card-table-RemSet-specific things. + + // Invoke "cl.do_MemRegion" on a set of MemRegions that collectively + // includes all the modified cards (expressing each card as a + // MemRegion). Thus, several modified cards may be lumped into one + // region. The regions are non-overlapping, and are visited in + // *decreasing* address order. (This order aids with imprecise card + // marking, where a dirty card may cause scanning, and summarization + // marking, of objects that extend onto subsequent cards.) + // If "clear" is true, the card is (conceptually) marked unmodified before + // applying the closure. + void mod_card_iterate(MemRegionClosure* cl, bool clear = false) { + non_clean_card_iterate_work(_whole_heap, cl, clear); + } + + // Like the "mod_cards_iterate" above, except only invokes the closure + // for cards within the MemRegion "mr" (which is required to be + // card-aligned and sized.) + void mod_card_iterate(MemRegion mr, MemRegionClosure* cl, + bool clear = false) { + non_clean_card_iterate_work(mr, cl, clear); + } + + static uintx ct_max_alignment_constraint(); + + // Apply closure cl to the dirty cards lying completely + // within MemRegion mr, setting the cards to precleaned. + void dirty_card_iterate(MemRegion mr, MemRegionClosure* cl); + + // Return the MemRegion corresponding to the first maximal run + // of dirty cards lying completely within MemRegion mr, after + // marking those cards precleaned. + MemRegion dirty_card_range_after_preclean(MemRegion mr); + + // Set all the dirty cards in the given region to precleaned state. + void preclean_dirty_cards(MemRegion mr); + + // Mapping from address to card marking array index. + int index_for(void* p) { + assert(_whole_heap.contains(p), + "out of bounds access to card marking array"); + return byte_for(p) - _byte_map; + } + + void verify(); + void verify_guard(); + + void verify_clean_region(MemRegion mr) PRODUCT_RETURN; + + static size_t par_chunk_heapword_alignment() { + return CardsPerStrideChunk * card_size_in_words; + } +}; + +class CardTableRS; + +// A specialization for the CardTableRS gen rem set. +class CardTableModRefBSForCTRS: public CardTableModRefBS { + CardTableRS* _rs; +protected: + bool card_will_be_scanned(jbyte cv); + bool card_may_have_been_dirty(jbyte cv); +public: + CardTableModRefBSForCTRS(MemRegion whole_heap, + int max_covered_regions) : + CardTableModRefBS(whole_heap, max_covered_regions) {} + + void set_CTRS(CardTableRS* rs) { _rs = rs; } +}; diff --git a/hotspot/src/share/vm/memory/cardTableRS.cpp b/hotspot/src/share/vm/memory/cardTableRS.cpp new file mode 100644 index 00000000000..6c762941be2 --- /dev/null +++ b/hotspot/src/share/vm/memory/cardTableRS.cpp @@ -0,0 +1,568 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_cardTableRS.cpp.incl" + +CardTableRS::CardTableRS(MemRegion whole_heap, + int max_covered_regions) : + GenRemSet(&_ct_bs), + _ct_bs(whole_heap, max_covered_regions), + _cur_youngergen_card_val(youngergenP1_card) +{ + _last_cur_val_in_gen = new jbyte[GenCollectedHeap::max_gens + 1]; + if (_last_cur_val_in_gen == NULL) { + vm_exit_during_initialization("Could not last_cur_val_in_gen array."); + } + for (int i = 0; i < GenCollectedHeap::max_gens + 1; i++) { + _last_cur_val_in_gen[i] = clean_card_val(); + } + _ct_bs.set_CTRS(this); +} + +void CardTableRS::resize_covered_region(MemRegion new_region) { + _ct_bs.resize_covered_region(new_region); +} + +jbyte CardTableRS::find_unused_youngergenP_card_value() { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + for (jbyte v = youngergenP1_card; + v < cur_youngergen_and_prev_nonclean_card; + v++) { + bool seen = false; + for (int g = 0; g < gch->n_gens()+1; g++) { + if (_last_cur_val_in_gen[g] == v) { + seen = true; + break; + } + } + if (!seen) return v; + } + ShouldNotReachHere(); + return 0; +} + +void CardTableRS::prepare_for_younger_refs_iterate(bool parallel) { + // Parallel or sequential, we must always set the prev to equal the + // last one written. + if (parallel) { + // Find a parallel value to be used next. + jbyte next_val = find_unused_youngergenP_card_value(); + set_cur_youngergen_card_val(next_val); + + } else { + // In an sequential traversal we will always write youngergen, so that + // the inline barrier is correct. + set_cur_youngergen_card_val(youngergen_card); + } +} + +void CardTableRS::younger_refs_iterate(Generation* g, + OopsInGenClosure* blk) { + _last_cur_val_in_gen[g->level()+1] = cur_youngergen_card_val(); + g->younger_refs_iterate(blk); +} + +class ClearNoncleanCardWrapper: public MemRegionClosure { + MemRegionClosure* _dirty_card_closure; + CardTableRS* _ct; + bool _is_par; +private: + // Clears the given card, return true if the corresponding card should be + // processed. + bool clear_card(jbyte* entry) { + if (_is_par) { + while (true) { + // In the parallel case, we may have to do this several times. + jbyte entry_val = *entry; + assert(entry_val != CardTableRS::clean_card_val(), + "We shouldn't be looking at clean cards, and this should " + "be the only place they get cleaned."); + if (CardTableRS::card_is_dirty_wrt_gen_iter(entry_val) + || _ct->is_prev_youngergen_card_val(entry_val)) { + jbyte res = + Atomic::cmpxchg(CardTableRS::clean_card_val(), entry, entry_val); + if (res == entry_val) { + break; + } else { + assert(res == CardTableRS::cur_youngergen_and_prev_nonclean_card, + "The CAS above should only fail if another thread did " + "a GC write barrier."); + } + } else if (entry_val == + CardTableRS::cur_youngergen_and_prev_nonclean_card) { + // Parallelism shouldn't matter in this case. Only the thread + // assigned to scan the card should change this value. + *entry = _ct->cur_youngergen_card_val(); + break; + } else { + assert(entry_val == _ct->cur_youngergen_card_val(), + "Should be the only possibility."); + // In this case, the card was clean before, and become + // cur_youngergen only because of processing of a promoted object. + // We don't have to look at the card. + return false; + } + } + return true; + } else { + jbyte entry_val = *entry; + assert(entry_val != CardTableRS::clean_card_val(), + "We shouldn't be looking at clean cards, and this should " + "be the only place they get cleaned."); + assert(entry_val != CardTableRS::cur_youngergen_and_prev_nonclean_card, + "This should be possible in the sequential case."); + *entry = CardTableRS::clean_card_val(); + return true; + } + } + +public: + ClearNoncleanCardWrapper(MemRegionClosure* dirty_card_closure, + CardTableRS* ct) : + _dirty_card_closure(dirty_card_closure), _ct(ct) { + _is_par = (SharedHeap::heap()->n_par_threads() > 0); + } + void do_MemRegion(MemRegion mr) { + // We start at the high end of "mr", walking backwards + // while accumulating a contiguous dirty range of cards in + // [start_of_non_clean, end_of_non_clean) which we then + // process en masse. + HeapWord* end_of_non_clean = mr.end(); + HeapWord* start_of_non_clean = end_of_non_clean; + jbyte* entry = _ct->byte_for(mr.last()); + const jbyte* first_entry = _ct->byte_for(mr.start()); + while (entry >= first_entry) { + HeapWord* cur = _ct->addr_for(entry); + if (!clear_card(entry)) { + // We hit a clean card; process any non-empty + // dirty range accumulated so far. + if (start_of_non_clean < end_of_non_clean) { + MemRegion mr2(start_of_non_clean, end_of_non_clean); + _dirty_card_closure->do_MemRegion(mr2); + } + // Reset the dirty window while continuing to + // look for the next dirty window to process. + end_of_non_clean = cur; + start_of_non_clean = end_of_non_clean; + } + // Open the left end of the window one card to the left. + start_of_non_clean = cur; + // Note that "entry" leads "start_of_non_clean" in + // its leftward excursion after this point + // in the loop and, when we hit the left end of "mr", + // will point off of the left end of the card-table + // for "mr". + entry--; + } + // If the first card of "mr" was dirty, we will have + // been left with a dirty window, co-initial with "mr", + // which we now process. + if (start_of_non_clean < end_of_non_clean) { + MemRegion mr2(start_of_non_clean, end_of_non_clean); + _dirty_card_closure->do_MemRegion(mr2); + } + } +}; +// clean (by dirty->clean before) ==> cur_younger_gen +// dirty ==> cur_youngergen_and_prev_nonclean_card +// precleaned ==> cur_youngergen_and_prev_nonclean_card +// prev-younger-gen ==> cur_youngergen_and_prev_nonclean_card +// cur-younger-gen ==> cur_younger_gen +// cur_youngergen_and_prev_nonclean_card ==> no change. +void CardTableRS::write_ref_field_gc_par(oop* field, oop new_val) { + jbyte* entry = ct_bs()->byte_for(field); + do { + jbyte entry_val = *entry; + // We put this first because it's probably the most common case. + if (entry_val == clean_card_val()) { + // No threat of contention with cleaning threads. + *entry = cur_youngergen_card_val(); + return; + } else if (card_is_dirty_wrt_gen_iter(entry_val) + || is_prev_youngergen_card_val(entry_val)) { + // Mark it as both cur and prev youngergen; card cleaning thread will + // eventually remove the previous stuff. + jbyte new_val = cur_youngergen_and_prev_nonclean_card; + jbyte res = Atomic::cmpxchg(new_val, entry, entry_val); + // Did the CAS succeed? + if (res == entry_val) return; + // Otherwise, retry, to see the new value. + continue; + } else { + assert(entry_val == cur_youngergen_and_prev_nonclean_card + || entry_val == cur_youngergen_card_val(), + "should be only possibilities."); + return; + } + } while (true); +} + +void CardTableRS::younger_refs_in_space_iterate(Space* sp, + OopsInGenClosure* cl) { + DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, _ct_bs.precision(), + cl->gen_boundary()); + ClearNoncleanCardWrapper clear_cl(dcto_cl, this); + + _ct_bs.non_clean_card_iterate(sp, sp->used_region_at_save_marks(), + dcto_cl, &clear_cl, false); +} + +void CardTableRS::clear_into_younger(Generation* gen, bool clear_perm) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + // Generations younger than gen have been evacuated. We can clear + // card table entries for gen (we know that it has no pointers + // to younger gens) and for those below. The card tables for + // the youngest gen need never be cleared, and those for perm gen + // will be cleared based on the parameter clear_perm. + // There's a bit of subtlety in the clear() and invalidate() + // methods that we exploit here and in invalidate_or_clear() + // below to avoid missing cards at the fringes. If clear() or + // invalidate() are changed in the future, this code should + // be revisited. 20040107.ysr + Generation* g = gen; + for(Generation* prev_gen = gch->prev_gen(g); + prev_gen != NULL; + g = prev_gen, prev_gen = gch->prev_gen(g)) { + MemRegion to_be_cleared_mr = g->prev_used_region(); + clear(to_be_cleared_mr); + } + // Clear perm gen cards if asked to do so. + if (clear_perm) { + MemRegion to_be_cleared_mr = gch->perm_gen()->prev_used_region(); + clear(to_be_cleared_mr); + } +} + +void CardTableRS::invalidate_or_clear(Generation* gen, bool younger, + bool perm) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + // For each generation gen (and younger and/or perm) + // invalidate the cards for the currently occupied part + // of that generation and clear the cards for the + // unoccupied part of the generation (if any, making use + // of that generation's prev_used_region to determine that + // region). No need to do anything for the youngest + // generation. Also see note#20040107.ysr above. + Generation* g = gen; + for(Generation* prev_gen = gch->prev_gen(g); prev_gen != NULL; + g = prev_gen, prev_gen = gch->prev_gen(g)) { + MemRegion used_mr = g->used_region(); + MemRegion to_be_cleared_mr = g->prev_used_region().minus(used_mr); + if (!to_be_cleared_mr.is_empty()) { + clear(to_be_cleared_mr); + } + invalidate(used_mr); + if (!younger) break; + } + // Clear perm gen cards if asked to do so. + if (perm) { + g = gch->perm_gen(); + MemRegion used_mr = g->used_region(); + MemRegion to_be_cleared_mr = g->prev_used_region().minus(used_mr); + if (!to_be_cleared_mr.is_empty()) { + clear(to_be_cleared_mr); + } + invalidate(used_mr); + } +} + + +class VerifyCleanCardClosure: public OopClosure { + HeapWord* boundary; + HeapWord* begin; HeapWord* end; +public: + void do_oop(oop* p) { + HeapWord* jp = (HeapWord*)p; + if (jp >= begin && jp < end) { + guarantee(*p == NULL || (HeapWord*)p < boundary + || (HeapWord*)(*p) >= boundary, + "pointer on clean card crosses boundary"); + } + } + VerifyCleanCardClosure(HeapWord* b, HeapWord* _begin, HeapWord* _end) : + boundary(b), begin(_begin), end(_end) {} +}; + +class VerifyCTSpaceClosure: public SpaceClosure { + CardTableRS* _ct; + HeapWord* _boundary; +public: + VerifyCTSpaceClosure(CardTableRS* ct, HeapWord* boundary) : + _ct(ct), _boundary(boundary) {} + void do_space(Space* s) { _ct->verify_space(s, _boundary); } +}; + +class VerifyCTGenClosure: public GenCollectedHeap::GenClosure { + CardTableRS* _ct; +public: + VerifyCTGenClosure(CardTableRS* ct) : _ct(ct) {} + void do_generation(Generation* gen) { + // Skip the youngest generation. + if (gen->level() == 0) return; + // Normally, we're interested in pointers to younger generations. + VerifyCTSpaceClosure blk(_ct, gen->reserved().start()); + gen->space_iterate(&blk, true); + } +}; + +void CardTableRS::verify_space(Space* s, HeapWord* gen_boundary) { + // We don't need to do young-gen spaces. + if (s->end() <= gen_boundary) return; + MemRegion used = s->used_region(); + + jbyte* cur_entry = byte_for(used.start()); + jbyte* limit = byte_after(used.last()); + while (cur_entry < limit) { + if (*cur_entry == CardTableModRefBS::clean_card) { + jbyte* first_dirty = cur_entry+1; + while (first_dirty < limit && + *first_dirty == CardTableModRefBS::clean_card) { + first_dirty++; + } + // If the first object is a regular object, and it has a + // young-to-old field, that would mark the previous card. + HeapWord* boundary = addr_for(cur_entry); + HeapWord* end = (first_dirty >= limit) ? used.end() : addr_for(first_dirty); + HeapWord* boundary_block = s->block_start(boundary); + HeapWord* begin = boundary; // Until proven otherwise. + HeapWord* start_block = boundary_block; // Until proven otherwise. + if (boundary_block < boundary) { + if (s->block_is_obj(boundary_block) && s->obj_is_alive(boundary_block)) { + oop boundary_obj = oop(boundary_block); + if (!boundary_obj->is_objArray() && + !boundary_obj->is_typeArray()) { + guarantee(cur_entry > byte_for(used.start()), + "else boundary would be boundary_block"); + if (*byte_for(boundary_block) != CardTableModRefBS::clean_card) { + begin = boundary_block + s->block_size(boundary_block); + start_block = begin; + } + } + } + } + // Now traverse objects until end. + HeapWord* cur = start_block; + VerifyCleanCardClosure verify_blk(gen_boundary, begin, end); + while (cur < end) { + if (s->block_is_obj(cur) && s->obj_is_alive(cur)) { + oop(cur)->oop_iterate(&verify_blk); + } + cur += s->block_size(cur); + } + cur_entry = first_dirty; + } else { + // We'd normally expect that cur_youngergen_and_prev_nonclean_card + // is a transient value, that cannot be in the card table + // except during GC, and thus assert that: + // guarantee(*cur_entry != cur_youngergen_and_prev_nonclean_card, + // "Illegal CT value"); + // That however, need not hold, as will become clear in the + // following... + + // We'd normally expect that if we are in the parallel case, + // we can't have left a prev value (which would be different + // from the current value) in the card table, and so we'd like to + // assert that: + // guarantee(cur_youngergen_card_val() == youngergen_card + // || !is_prev_youngergen_card_val(*cur_entry), + // "Illegal CT value"); + // That, however, may not hold occasionally, because of + // CMS or MSC in the old gen. To wit, consider the + // following two simple illustrative scenarios: + // (a) CMS: Consider the case where a large object L + // spanning several cards is allocated in the old + // gen, and has a young gen reference stored in it, dirtying + // some interior cards. A young collection scans the card, + // finds a young ref and installs a youngergenP_n value. + // L then goes dead. Now a CMS collection starts, + // finds L dead and sweeps it up. Assume that L is + // abutting _unallocated_blk, so _unallocated_blk is + // adjusted down to (below) L. Assume further that + // no young collection intervenes during this CMS cycle. + // The next young gen cycle will not get to look at this + // youngergenP_n card since it lies in the unoccupied + // part of the space. + // Some young collections later the blocks on this + // card can be re-allocated either due to direct allocation + // or due to absorbing promotions. At this time, the + // before-gc verification will fail the above assert. + // (b) MSC: In this case, an object L with a young reference + // is on a card that (therefore) holds a youngergen_n value. + // Suppose also that L lies towards the end of the used + // the used space before GC. An MSC collection + // occurs that compacts to such an extent that this + // card is no longer in the occupied part of the space. + // Since current code in MSC does not always clear cards + // in the unused part of old gen, this stale youngergen_n + // value is left behind and can later be covered by + // an object when promotion or direct allocation + // re-allocates that part of the heap. + // + // Fortunately, the presence of such stale card values is + // "only" a minor annoyance in that subsequent young collections + // might needlessly scan such cards, but would still never corrupt + // the heap as a result. However, it's likely not to be a significant + // performance inhibitor in practice. For instance, + // some recent measurements with unoccupied cards eagerly cleared + // out to maintain this invariant, showed next to no + // change in young collection times; of course one can construct + // degenerate examples where the cost can be significant.) + // Note, in particular, that if the "stale" card is modified + // after re-allocation, it would be dirty, not "stale". Thus, + // we can never have a younger ref in such a card and it is + // safe not to scan that card in any collection. [As we see + // below, we do some unnecessary scanning + // in some cases in the current parallel scanning algorithm.] + // + // The main point below is that the parallel card scanning code + // deals correctly with these stale card values. There are two main + // cases to consider where we have a stale "younger gen" value and a + // "derivative" case to consider, where we have a stale + // "cur_younger_gen_and_prev_non_clean" value, as will become + // apparent in the case analysis below. + // o Case 1. If the stale value corresponds to a younger_gen_n + // value other than the cur_younger_gen value then the code + // treats this as being tantamount to a prev_younger_gen + // card. This means that the card may be unnecessarily scanned. + // There are two sub-cases to consider: + // o Case 1a. Let us say that the card is in the occupied part + // of the generation at the time the collection begins. In + // that case the card will be either cleared when it is scanned + // for young pointers, or will be set to cur_younger_gen as a + // result of promotion. (We have elided the normal case where + // the scanning thread and the promoting thread interleave + // possibly resulting in a transient + // cur_younger_gen_and_prev_non_clean value before settling + // to cur_younger_gen. [End Case 1a.] + // o Case 1b. Consider now the case when the card is in the unoccupied + // part of the space which becomes occupied because of promotions + // into it during the current young GC. In this case the card + // will never be scanned for young references. The current + // code will set the card value to either + // cur_younger_gen_and_prev_non_clean or leave + // it with its stale value -- because the promotions didn't + // result in any younger refs on that card. Of these two + // cases, the latter will be covered in Case 1a during + // a subsequent scan. To deal with the former case, we need + // to further consider how we deal with a stale value of + // cur_younger_gen_and_prev_non_clean in our case analysis + // below. This we do in Case 3 below. [End Case 1b] + // [End Case 1] + // o Case 2. If the stale value corresponds to cur_younger_gen being + // a value not necessarily written by a current promotion, the + // card will not be scanned by the younger refs scanning code. + // (This is OK since as we argued above such cards cannot contain + // any younger refs.) The result is that this value will be + // treated as a prev_younger_gen value in a subsequent collection, + // which is addressed in Case 1 above. [End Case 2] + // o Case 3. We here consider the "derivative" case from Case 1b. above + // because of which we may find a stale + // cur_younger_gen_and_prev_non_clean card value in the table. + // Once again, as in Case 1, we consider two subcases, depending + // on whether the card lies in the occupied or unoccupied part + // of the space at the start of the young collection. + // o Case 3a. Let us say the card is in the occupied part of + // the old gen at the start of the young collection. In that + // case, the card will be scanned by the younger refs scanning + // code which will set it to cur_younger_gen. In a subsequent + // scan, the card will be considered again and get its final + // correct value. [End Case 3a] + // o Case 3b. Now consider the case where the card is in the + // unoccupied part of the old gen, and is occupied as a result + // of promotions during thus young gc. In that case, + // the card will not be scanned for younger refs. The presence + // of newly promoted objects on the card will then result in + // its keeping the value cur_younger_gen_and_prev_non_clean + // value, which we have dealt with in Case 3 here. [End Case 3b] + // [End Case 3] + // + // (Please refer to the code in the helper class + // ClearNonCleanCardWrapper and in CardTableModRefBS for details.) + // + // The informal arguments above can be tightened into a formal + // correctness proof and it behooves us to write up such a proof, + // or to use model checking to prove that there are no lingering + // concerns. + // + // Clearly because of Case 3b one cannot bound the time for + // which a card will retain what we have called a "stale" value. + // However, one can obtain a Loose upper bound on the redundant + // work as a result of such stale values. Note first that any + // time a stale card lies in the occupied part of the space at + // the start of the collection, it is scanned by younger refs + // code and we can define a rank function on card values that + // declines when this is so. Note also that when a card does not + // lie in the occupied part of the space at the beginning of a + // young collection, its rank can either decline or stay unchanged. + // In this case, no extra work is done in terms of redundant + // younger refs scanning of that card. + // Then, the case analysis above reveals that, in the worst case, + // any such stale card will be scanned unnecessarily at most twice. + // + // It is nonethelss advisable to try and get rid of some of this + // redundant work in a subsequent (low priority) re-design of + // the card-scanning code, if only to simplify the underlying + // state machine analysis/proof. ysr 1/28/2002. XXX + cur_entry++; + } + } +} + +void CardTableRS::verify() { + // At present, we only know how to verify the card table RS for + // generational heaps. + VerifyCTGenClosure blk(this); + CollectedHeap* ch = Universe::heap(); + // We will do the perm-gen portion of the card table, too. + Generation* pg = SharedHeap::heap()->perm_gen(); + HeapWord* pg_boundary = pg->reserved().start(); + + if (ch->kind() == CollectedHeap::GenCollectedHeap) { + GenCollectedHeap::heap()->generation_iterate(&blk, false); + _ct_bs.verify(); + + // If the old gen collections also collect perm, then we are only + // interested in perm-to-young pointers, not perm-to-old pointers. + GenCollectedHeap* gch = GenCollectedHeap::heap(); + CollectorPolicy* cp = gch->collector_policy(); + if (cp->is_mark_sweep_policy() || cp->is_concurrent_mark_sweep_policy()) { + pg_boundary = gch->get_gen(1)->reserved().start(); + } + } + VerifyCTSpaceClosure perm_space_blk(this, pg_boundary); + SharedHeap::heap()->perm_gen()->space_iterate(&perm_space_blk, true); +} + + +void CardTableRS::verify_empty(MemRegion mr) { + if (!mr.is_empty()) { + jbyte* cur_entry = byte_for(mr.start()); + jbyte* limit = byte_after(mr.last()); + for (;cur_entry < limit; cur_entry++) { + guarantee(*cur_entry == CardTableModRefBS::clean_card, + "Unexpected dirty card found"); + } + } +} diff --git a/hotspot/src/share/vm/memory/cardTableRS.hpp b/hotspot/src/share/vm/memory/cardTableRS.hpp new file mode 100644 index 00000000000..fa973aee45f --- /dev/null +++ b/hotspot/src/share/vm/memory/cardTableRS.hpp @@ -0,0 +1,156 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Space; +class OopsInGenClosure; +class DirtyCardToOopClosure; + +// This kind of "GenRemSet" uses a card table both as shared data structure +// for a mod ref barrier set and for the rem set information. + +class CardTableRS: public GenRemSet { + friend class VMStructs; + // Below are private classes used in impl. + friend class VerifyCTSpaceClosure; + friend class ClearNoncleanCardWrapper; + + static jbyte clean_card_val() { + return CardTableModRefBS::clean_card; + } + + static bool + card_is_dirty_wrt_gen_iter(jbyte cv) { + return CardTableModRefBS::card_is_dirty_wrt_gen_iter(cv); + } + + CardTableModRefBSForCTRS _ct_bs; + + virtual void younger_refs_in_space_iterate(Space* sp, OopsInGenClosure* cl); + + void verify_space(Space* s, HeapWord* gen_start); + + enum ExtendedCardValue { + youngergen_card = CardTableModRefBS::CT_MR_BS_last_reserved + 1, + // These are for parallel collection. + // There are three P (parallel) youngergen card values. In general, this + // needs to be more than the number of generations (including the perm + // gen) that might have younger_refs_do invoked on them separately. So + // if we add more gens, we have to add more values. + youngergenP1_card = CardTableModRefBS::CT_MR_BS_last_reserved + 2, + youngergenP2_card = CardTableModRefBS::CT_MR_BS_last_reserved + 3, + youngergenP3_card = CardTableModRefBS::CT_MR_BS_last_reserved + 4, + cur_youngergen_and_prev_nonclean_card = + CardTableModRefBS::CT_MR_BS_last_reserved + 5 + }; + + // An array that contains, for each generation, the card table value last + // used as the current value for a younger_refs_do iteration of that + // portion of the table. (The perm gen is index 0; other gens are at + // their level plus 1. They youngest gen is in the table, but will + // always have the value "clean_card".) + jbyte* _last_cur_val_in_gen; + + jbyte _cur_youngergen_card_val; + + jbyte cur_youngergen_card_val() { + return _cur_youngergen_card_val; + } + void set_cur_youngergen_card_val(jbyte v) { + _cur_youngergen_card_val = v; + } + bool is_prev_youngergen_card_val(jbyte v) { + return + youngergen_card <= v && + v < cur_youngergen_and_prev_nonclean_card && + v != _cur_youngergen_card_val; + } + // Return a youngergen_card_value that is not currently in use. + jbyte find_unused_youngergenP_card_value(); + +public: + CardTableRS(MemRegion whole_heap, int max_covered_regions); + + // *** GenRemSet functions. + GenRemSet::Name rs_kind() { return GenRemSet::CardTable; } + + CardTableRS* as_CardTableRS() { return this; } + + CardTableModRefBS* ct_bs() { return &_ct_bs; } + + // Override. + void prepare_for_younger_refs_iterate(bool parallel); + + // Card table entries are cleared before application; "blk" is + // responsible for dirtying if the oop is still older-to-younger after + // closure application. + void younger_refs_iterate(Generation* g, OopsInGenClosure* blk); + + void inline_write_ref_field_gc(oop* field, oop new_val) { + jbyte* byte = _ct_bs.byte_for(field); + *byte = youngergen_card; + } + void write_ref_field_gc_work(oop* field, oop new_val) { + inline_write_ref_field_gc(field, new_val); + } + + // Override. Might want to devirtualize this in the same fashion as + // above. Ensures that the value of the card for field says that it's + // a younger card in the current collection. + virtual void write_ref_field_gc_par(oop* field, oop new_val); + + void resize_covered_region(MemRegion new_region); + + bool is_aligned(HeapWord* addr) { + return _ct_bs.is_card_aligned(addr); + } + + void verify(); + void verify_empty(MemRegion mr); + + void clear(MemRegion mr) { _ct_bs.clear(mr); } + void clear_into_younger(Generation* gen, bool clear_perm); + + void invalidate(MemRegion mr) { _ct_bs.invalidate(mr); } + void invalidate_or_clear(Generation* gen, bool younger, bool perm); + + static uintx ct_max_alignment_constraint() { + return CardTableModRefBS::ct_max_alignment_constraint(); + } + + jbyte* byte_for(void* p) { return _ct_bs.byte_for(p); } + jbyte* byte_after(void* p) { return _ct_bs.byte_after(p); } + HeapWord* addr_for(jbyte* p) { return _ct_bs.addr_for(p); } + + bool is_prev_nonclean_card_val(jbyte v) { + return + youngergen_card <= v && + v <= cur_youngergen_and_prev_nonclean_card && + v != _cur_youngergen_card_val; + } + + static bool youngergen_may_have_been_dirty(jbyte cv) { + return cv == CardTableRS::cur_youngergen_and_prev_nonclean_card; + } + +}; diff --git a/hotspot/src/share/vm/memory/classify.cpp b/hotspot/src/share/vm/memory/classify.cpp new file mode 100644 index 00000000000..0cb6a572cb3 --- /dev/null +++ b/hotspot/src/share/vm/memory/classify.cpp @@ -0,0 +1,194 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_classify.cpp.incl" + + +const char* ClassifyObjectClosure::object_type_name[number_object_types] = { + "unknown", + "instance", + "instanceRef", + "objArray", + "symbol", + "klass", + "instanceKlass", + "method", + "constMethod", + "methodData", + "constantPool", + "constantPoolCache", + "typeArray", + "compiledICHolder" +}; + + +object_type ClassifyObjectClosure::classify_object(oop obj, bool count) { + object_type type = unknown_type; + + Klass* k = obj->blueprint(); + + if (k->as_klassOop() == SystemDictionary::object_klass()) { + tty->print_cr("Found the class!"); + } + + if (count) { + k->set_alloc_count(k->alloc_count() + 1); + } + + if (obj->is_instance()) { + if (k->oop_is_instanceRef()) { + type = instanceRef_type; + } else { + type = instance_type; + } + } else if (obj->is_typeArray()) { + type = typeArray_type; + } else if (obj->is_objArray()) { + type = objArray_type; + } else if (obj->is_symbol()) { + type = symbol_type; + } else if (obj->is_klass()) { + Klass* k = ((klassOop)obj)->klass_part(); + if (k->oop_is_instance()) { + type = instanceKlass_type; + } else { + type = klass_type; + } + } else if (obj->is_method()) { + type = method_type; + } else if (obj->is_constMethod()) { + type = constMethod_type; + } else if (obj->is_methodData()) { + ShouldNotReachHere(); + } else if (obj->is_constantPool()) { + type = constantPool_type; + } else if (obj->is_constantPoolCache()) { + type = constantPoolCache_type; + } else if (obj->is_compiledICHolder()) { + type = compiledICHolder_type; + } else { + ShouldNotReachHere(); + } + + assert(type != unknown_type, "found object of unknown type."); + return type; +} + + +void ClassifyObjectClosure::reset() { + for (int i = 0; i < number_object_types; ++i) { + object_count[i] = 0; + object_size[i] = 0; + } + total_object_count = 0; + total_object_size = 0; +} + + +void ClassifyObjectClosure::do_object(oop obj) { + int i = classify_object(obj, true); + ++object_count[i]; + ++total_object_count; + size_t size = obj->size() * HeapWordSize; + object_size[i] += size; + total_object_size += size; +} + + +size_t ClassifyObjectClosure::print() { + int num_objects = 0; + size_t size_objects = 0; + for (int i = 0; i < number_object_types; ++i) { + if (object_count[i] != 0) { + tty->print_cr("%8d %-22s (%8d bytes, %5.2f bytes/object)", + object_count[i], object_type_name[i], object_size[i], + (float)object_size[i]/(float)object_count[i]); + } + num_objects += object_count[i]; + size_objects += object_size[i]; + } + assert(num_objects == total_object_count, "Object count mismatch!"); + assert(size_objects == total_object_size, "Object size mismatch!"); + + tty->print_cr(" Total: %d objects, %d bytes", total_object_count, + total_object_size); + return total_object_size; +} + + +void ClassifyInstanceKlassClosure::do_object(oop obj) { + int type = classify_object(obj, false); + if (type == instanceKlass_type || type == klass_type) { + Klass* k = ((klassOop)obj)->klass_part(); + if (k->alloc_count() > 0) { + ResourceMark rm; + const char *name; + if (k->name() == NULL) { + + if (obj == Universe::klassKlassObj()) { + name = "_klassKlassObj"; + } else if (obj == Universe::arrayKlassKlassObj()) { + name = "_arrayKlassKlassObj"; + } else if (obj == Universe::objArrayKlassKlassObj()) { + name = "_objArrayKlassKlassObj"; + } else if (obj == Universe::typeArrayKlassKlassObj()) { + name = "_typeArrayKlassKlassObj"; + } else if (obj == Universe::instanceKlassKlassObj()) { + name = "_instanceKlassKlassObj"; + } else if (obj == Universe::symbolKlassObj()) { + name = "_symbolKlassObj"; + } else if (obj == Universe::methodKlassObj()) { + name = "_methodKlassObj"; + } else if (obj == Universe::constMethodKlassObj()) { + name = "_constMethodKlassObj"; + } else if (obj == Universe::constantPoolKlassObj()) { + name = "_constantPoolKlassObj"; + } else if (obj == Universe::constantPoolCacheKlassObj()) { + name = "_constantPoolCacheKlassObj"; + } else if (obj == Universe::compiledICHolderKlassObj()) { + name = "_compiledICHolderKlassObj"; + } else if (obj == Universe::systemObjArrayKlassObj()) { + name = "_systemObjArrayKlassObj"; + } else { + name = "[unnamed]"; + } + } else { + name = k->external_name(); + } + tty->print_cr("% 8d instances of %s", k->alloc_count(), name); + } + total_instances += k->alloc_count(); + } +} + + +void ClassifyInstanceKlassClosure::print() { + tty->print_cr(" Total instances: %d.", total_instances); +} + + +void ClassifyInstanceKlassClosure::reset() { + total_instances = 0; +} diff --git a/hotspot/src/share/vm/memory/classify.hpp b/hotspot/src/share/vm/memory/classify.hpp new file mode 100644 index 00000000000..8d363639d5e --- /dev/null +++ b/hotspot/src/share/vm/memory/classify.hpp @@ -0,0 +1,92 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +typedef enum oop_type { + unknown_type, + instance_type, + instanceRef_type, + objArray_type, + symbol_type, + klass_type, + instanceKlass_type, + method_type, + constMethod_type, + methodData_type, + constantPool_type, + constantPoolCache_type, + typeArray_type, + compiledICHolder_type, + number_object_types +} object_type; + + +// Classify objects by type and keep counts. +// Print the count and space taken for each type. + + +class ClassifyObjectClosure : public ObjectClosure { +private: + + static const char* object_type_name[number_object_types]; + + int total_object_count; + size_t total_object_size; + int object_count[number_object_types]; + size_t object_size[number_object_types]; + +public: + ClassifyObjectClosure() { reset(); } + void reset(); + void do_object(oop obj); + static object_type classify_object(oop obj, bool count); + size_t print(); +}; + + +// Count objects using the alloc_count field in the object's klass +// object. + +class ClassifyInstanceKlassClosure : public ClassifyObjectClosure { +private: + int total_instances; +public: + ClassifyInstanceKlassClosure() { reset(); } + void reset(); + void print(); + void do_object(oop obj); +}; + + +// Clear the alloc_count fields in all classes so that the count can be +// restarted. + +class ClearAllocCountClosure : public ObjectClosure { +public: + void do_object(oop obj) { + if (obj->is_klass()) { + Klass* k = Klass::cast((klassOop)obj); + k->set_alloc_count(0); + } + } +}; diff --git a/hotspot/src/share/vm/memory/collectorPolicy.cpp b/hotspot/src/share/vm/memory/collectorPolicy.cpp new file mode 100644 index 00000000000..de43e6aadf8 --- /dev/null +++ b/hotspot/src/share/vm/memory/collectorPolicy.cpp @@ -0,0 +1,550 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_collectorPolicy.cpp.incl" + +// CollectorPolicy methods. + +void CollectorPolicy::initialize_flags() { + if (PermSize > MaxPermSize) { + MaxPermSize = PermSize; + } + PermSize = align_size_down(PermSize, min_alignment()); + MaxPermSize = align_size_up(MaxPermSize, max_alignment()); + + MinPermHeapExpansion = align_size_down(MinPermHeapExpansion, min_alignment()); + MaxPermHeapExpansion = align_size_down(MaxPermHeapExpansion, min_alignment()); + + MinHeapDeltaBytes = align_size_up(MinHeapDeltaBytes, min_alignment()); + + SharedReadOnlySize = align_size_up(SharedReadOnlySize, max_alignment()); + SharedReadWriteSize = align_size_up(SharedReadWriteSize, max_alignment()); + SharedMiscDataSize = align_size_up(SharedMiscDataSize, max_alignment()); + + assert(PermSize % min_alignment() == 0, "permanent space alignment"); + assert(MaxPermSize % max_alignment() == 0, "maximum permanent space alignment"); + assert(SharedReadOnlySize % max_alignment() == 0, "read-only space alignment"); + assert(SharedReadWriteSize % max_alignment() == 0, "read-write space alignment"); + assert(SharedMiscDataSize % max_alignment() == 0, "misc-data space alignment"); + if (PermSize < M) { + vm_exit_during_initialization("Too small initial permanent heap"); + } +} + +void CollectorPolicy::initialize_size_info() { + // User inputs from -mx and ms are aligned + _initial_heap_byte_size = align_size_up(Arguments::initial_heap_size(), + min_alignment()); + _min_heap_byte_size = align_size_up(Arguments::min_heap_size(), + min_alignment()); + _max_heap_byte_size = align_size_up(MaxHeapSize, max_alignment()); + + // Check validity of heap parameters from launcher + if (_initial_heap_byte_size == 0) { + _initial_heap_byte_size = NewSize + OldSize; + } else { + Universe::check_alignment(_initial_heap_byte_size, min_alignment(), + "initial heap"); + } + if (_min_heap_byte_size == 0) { + _min_heap_byte_size = NewSize + OldSize; + } else { + Universe::check_alignment(_min_heap_byte_size, min_alignment(), + "initial heap"); + } + + // Check heap parameter properties + if (_initial_heap_byte_size < M) { + vm_exit_during_initialization("Too small initial heap"); + } + // Check heap parameter properties + if (_min_heap_byte_size < M) { + vm_exit_during_initialization("Too small minimum heap"); + } + if (_initial_heap_byte_size <= NewSize) { + // make sure there is at least some room in old space + vm_exit_during_initialization("Too small initial heap for new size specified"); + } + if (_max_heap_byte_size < _min_heap_byte_size) { + vm_exit_during_initialization("Incompatible minimum and maximum heap sizes specified"); + } + if (_initial_heap_byte_size < _min_heap_byte_size) { + vm_exit_during_initialization("Incompatible minimum and initial heap sizes specified"); + } + if (_max_heap_byte_size < _initial_heap_byte_size) { + vm_exit_during_initialization("Incompatible initial and maximum heap sizes specified"); + } +} + +void CollectorPolicy::initialize_perm_generation(PermGen::Name pgnm) { + _permanent_generation = + new PermanentGenerationSpec(pgnm, PermSize, MaxPermSize, + SharedReadOnlySize, + SharedReadWriteSize, + SharedMiscDataSize, + SharedMiscCodeSize); + if (_permanent_generation == NULL) { + vm_exit_during_initialization("Unable to allocate gen spec"); + } +} + + +GenRemSet* CollectorPolicy::create_rem_set(MemRegion whole_heap, + int max_covered_regions) { + switch (rem_set_name()) { + case GenRemSet::CardTable: { + if (barrier_set_name() != BarrierSet::CardTableModRef) + vm_exit_during_initialization("Mismatch between RS and BS."); + CardTableRS* res = new CardTableRS(whole_heap, max_covered_regions); + return res; + } + default: + guarantee(false, "unrecognized GenRemSet::Name"); + return NULL; + } +} + +// GenCollectorPolicy methods. + +void GenCollectorPolicy::initialize_size_policy(size_t init_eden_size, + size_t init_promo_size, + size_t init_survivor_size) { + double max_gc_minor_pause_sec = ((double) MaxGCMinorPauseMillis)/1000.0; + _size_policy = new AdaptiveSizePolicy(init_eden_size, + init_promo_size, + init_survivor_size, + max_gc_minor_pause_sec, + GCTimeRatio); +} + +size_t GenCollectorPolicy::compute_max_alignment() { + // The card marking array and the offset arrays for old generations are + // committed in os pages as well. Make sure they are entirely full (to + // avoid partial page problems), e.g. if 512 bytes heap corresponds to 1 + // byte entry and the os page size is 4096, the maximum heap size should + // be 512*4096 = 2MB aligned. + size_t alignment = GenRemSet::max_alignment_constraint(rem_set_name()); + + // Parallel GC does its own alignment of the generations to avoid requiring a + // large page (256M on some platforms) for the permanent generation. The + // other collectors should also be updated to do their own alignment and then + // this use of lcm() should be removed. + if (UseLargePages && !UseParallelGC) { + // in presence of large pages we have to make sure that our + // alignment is large page aware + alignment = lcm(os::large_page_size(), alignment); + } + + return alignment; +} + +void GenCollectorPolicy::initialize_flags() { + // All sizes must be multiples of the generation granularity. + set_min_alignment((uintx) Generation::GenGrain); + set_max_alignment(compute_max_alignment()); + assert(max_alignment() >= min_alignment() && + max_alignment() % min_alignment() == 0, + "invalid alignment constraints"); + + CollectorPolicy::initialize_flags(); + + // All generational heaps have a youngest gen; handle those flags here. + + // Adjust max size parameters + if (NewSize > MaxNewSize) { + MaxNewSize = NewSize; + } + NewSize = align_size_down(NewSize, min_alignment()); + MaxNewSize = align_size_down(MaxNewSize, min_alignment()); + + // Check validity of heap flags + assert(NewSize % min_alignment() == 0, "eden space alignment"); + assert(MaxNewSize % min_alignment() == 0, "survivor space alignment"); + + if (NewSize < 3*min_alignment()) { + // make sure there room for eden and two survivor spaces + vm_exit_during_initialization("Too small new size specified"); + } + if (SurvivorRatio < 1 || NewRatio < 1) { + vm_exit_during_initialization("Invalid heap ratio specified"); + } +} + +void TwoGenerationCollectorPolicy::initialize_flags() { + GenCollectorPolicy::initialize_flags(); + + OldSize = align_size_down(OldSize, min_alignment()); + if (NewSize + OldSize > MaxHeapSize) { + MaxHeapSize = NewSize + OldSize; + } + MaxHeapSize = align_size_up(MaxHeapSize, max_alignment()); + + always_do_update_barrier = UseConcMarkSweepGC; + BlockOffsetArrayUseUnallocatedBlock = + BlockOffsetArrayUseUnallocatedBlock || ParallelGCThreads > 0; + + // Check validity of heap flags + assert(OldSize % min_alignment() == 0, "old space alignment"); + assert(MaxHeapSize % max_alignment() == 0, "maximum heap alignment"); +} + +void GenCollectorPolicy::initialize_size_info() { + CollectorPolicy::initialize_size_info(); + + // Minimum sizes of the generations may be different than + // the initial sizes. + if (!FLAG_IS_DEFAULT(NewSize)) { + _min_gen0_size = NewSize; + } else { + _min_gen0_size = align_size_down(_min_heap_byte_size / (NewRatio+1), + min_alignment()); + // We bound the minimum size by NewSize below (since it historically + // would have been NewSize and because the NewRatio calculation could + // yield a size that is too small) and bound it by MaxNewSize above. + // This is not always best. The NewSize calculated by CMS (which has + // a fixed minimum of 16m) can sometimes be "too" large. Consider + // the case where -Xmx32m. The CMS calculated NewSize would be about + // half the entire heap which seems too large. But the counter + // example is seen when the client defaults for NewRatio are used. + // An initial young generation size of 640k was observed + // with -Xmx128m -XX:MaxNewSize=32m when NewSize was not used + // as a lower bound as with + // _min_gen0_size = MIN2(_min_gen0_size, MaxNewSize); + // and 640k seemed too small a young generation. + _min_gen0_size = MIN2(MAX2(_min_gen0_size, NewSize), MaxNewSize); + } + + // Parameters are valid, compute area sizes. + size_t max_new_size = align_size_down(_max_heap_byte_size / (NewRatio+1), + min_alignment()); + max_new_size = MIN2(MAX2(max_new_size, _min_gen0_size), MaxNewSize); + + // desired_new_size is used to set the initial size. The + // initial size must be greater than the minimum size. + size_t desired_new_size = + align_size_down(_initial_heap_byte_size / (NewRatio+1), + min_alignment()); + + size_t new_size = MIN2(MAX2(desired_new_size, _min_gen0_size), max_new_size); + + _initial_gen0_size = new_size; + _max_gen0_size = max_new_size; +} + +void TwoGenerationCollectorPolicy::initialize_size_info() { + GenCollectorPolicy::initialize_size_info(); + + // Minimum sizes of the generations may be different than + // the initial sizes. An inconsistently is permitted here + // in the total size that can be specified explicitly by + // command line specification of OldSize and NewSize and + // also a command line specification of -Xms. Issue a warning + // but allow the values to pass. + if (!FLAG_IS_DEFAULT(OldSize)) { + _min_gen1_size = OldSize; + // The generation minimums and the overall heap mimimum should + // be within one heap alignment. + if ((_min_gen1_size + _min_gen0_size + max_alignment()) < + _min_heap_byte_size) { + warning("Inconsistency between minimum heap size and minimum " + "generation sizes: using min heap = " SIZE_FORMAT, + _min_heap_byte_size); + } + } else { + _min_gen1_size = _min_heap_byte_size - _min_gen0_size; + } + + _initial_gen1_size = _initial_heap_byte_size - _initial_gen0_size; + _max_gen1_size = _max_heap_byte_size - _max_gen0_size; +} + +HeapWord* GenCollectorPolicy::mem_allocate_work(size_t size, + bool is_tlab, + bool* gc_overhead_limit_was_exceeded) { + GenCollectedHeap *gch = GenCollectedHeap::heap(); + + debug_only(gch->check_for_valid_allocation_state()); + assert(gch->no_gc_in_progress(), "Allocation during gc not allowed"); + HeapWord* result = NULL; + + // Loop until the allocation is satisified, + // or unsatisfied after GC. + for (int try_count = 1; /* return or throw */; try_count += 1) { + HandleMark hm; // discard any handles allocated in each iteration + + // First allocation attempt is lock-free. + Generation *gen0 = gch->get_gen(0); + assert(gen0->supports_inline_contig_alloc(), + "Otherwise, must do alloc within heap lock"); + if (gen0->should_allocate(size, is_tlab)) { + result = gen0->par_allocate(size, is_tlab); + if (result != NULL) { + assert(gch->is_in_reserved(result), "result not in heap"); + return result; + } + } + unsigned int gc_count_before; // read inside the Heap_lock locked region + { + MutexLocker ml(Heap_lock); + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("TwoGenerationCollectorPolicy::mem_allocate_work:" + " attempting locked slow path allocation"); + } + // Note that only large objects get a shot at being + // allocated in later generations. + bool first_only = ! should_try_older_generation_allocation(size); + + result = gch->attempt_allocation(size, is_tlab, first_only); + if (result != NULL) { + assert(gch->is_in_reserved(result), "result not in heap"); + return result; + } + + // There are NULL's returned for different circumstances below. + // In general gc_overhead_limit_was_exceeded should be false so + // set it so here and reset it to true only if the gc time + // limit is being exceeded as checked below. + *gc_overhead_limit_was_exceeded = false; + + if (GC_locker::is_active_and_needs_gc()) { + if (is_tlab) { + return NULL; // Caller will retry allocating individual object + } + if (!gch->is_maximal_no_gc()) { + // Try and expand heap to satisfy request + result = expand_heap_and_allocate(size, is_tlab); + // result could be null if we are out of space + if (result != NULL) { + return result; + } + } + + // If this thread is not in a jni critical section, we stall + // the requestor until the critical section has cleared and + // GC allowed. When the critical section clears, a GC is + // initiated by the last thread exiting the critical section; so + // we retry the allocation sequence from the beginning of the loop, + // rather than causing more, now probably unnecessary, GC attempts. + JavaThread* jthr = JavaThread::current(); + if (!jthr->in_critical()) { + MutexUnlocker mul(Heap_lock); + // Wait for JNI critical section to be exited + GC_locker::stall_until_clear(); + continue; + } else { + if (CheckJNICalls) { + fatal("Possible deadlock due to allocating while" + " in jni critical section"); + } + return NULL; + } + } + + // Read the gc count while the heap lock is held. + gc_count_before = Universe::heap()->total_collections(); + } + + // Allocation has failed and a collection is about + // to be done. If the gc time limit was exceeded the + // last time a collection was done, return NULL so + // that an out-of-memory will be thrown. Clear + // gc_time_limit_exceeded so that subsequent attempts + // at a collection will be made. + if (size_policy()->gc_time_limit_exceeded()) { + *gc_overhead_limit_was_exceeded = true; + size_policy()->set_gc_time_limit_exceeded(false); + return NULL; + } + + VM_GenCollectForAllocation op(size, + is_tlab, + gc_count_before); + VMThread::execute(&op); + if (op.prologue_succeeded()) { + result = op.result(); + if (op.gc_locked()) { + assert(result == NULL, "must be NULL if gc_locked() is true"); + continue; // retry and/or stall as necessary + } + assert(result == NULL || gch->is_in_reserved(result), + "result not in heap"); + return result; + } + + // Give a warning if we seem to be looping forever. + if ((QueuedAllocationWarningCount > 0) && + (try_count % QueuedAllocationWarningCount == 0)) { + warning("TwoGenerationCollectorPolicy::mem_allocate_work retries %d times \n\t" + " size=%d %s", try_count, size, is_tlab ? "(TLAB)" : ""); + } + } +} + +HeapWord* GenCollectorPolicy::expand_heap_and_allocate(size_t size, + bool is_tlab) { + GenCollectedHeap *gch = GenCollectedHeap::heap(); + HeapWord* result = NULL; + for (int i = number_of_generations() - 1; i >= 0 && result == NULL; i--) { + Generation *gen = gch->get_gen(i); + if (gen->should_allocate(size, is_tlab)) { + result = gen->expand_and_allocate(size, is_tlab); + } + } + assert(result == NULL || gch->is_in_reserved(result), "result not in heap"); + return result; +} + +HeapWord* GenCollectorPolicy::satisfy_failed_allocation(size_t size, + bool is_tlab) { + GenCollectedHeap *gch = GenCollectedHeap::heap(); + GCCauseSetter x(gch, GCCause::_allocation_failure); + HeapWord* result = NULL; + + assert(size != 0, "Precondition violated"); + if (GC_locker::is_active_and_needs_gc()) { + // GC locker is active; instead of a collection we will attempt + // to expand the heap, if there's room for expansion. + if (!gch->is_maximal_no_gc()) { + result = expand_heap_and_allocate(size, is_tlab); + } + return result; // could be null if we are out of space + } else if (!gch->incremental_collection_will_fail()) { + // The gc_prologues have not executed yet. The value + // for incremental_collection_will_fail() is the remanent + // of the last collection. + // Do an incremental collection. + gch->do_collection(false /* full */, + false /* clear_all_soft_refs */, + size /* size */, + is_tlab /* is_tlab */, + number_of_generations() - 1 /* max_level */); + } else { + // Try a full collection; see delta for bug id 6266275 + // for the original code and why this has been simplified + // with from-space allocation criteria modified and + // such allocation moved out of the safepoint path. + gch->do_collection(true /* full */, + false /* clear_all_soft_refs */, + size /* size */, + is_tlab /* is_tlab */, + number_of_generations() - 1 /* max_level */); + } + + result = gch->attempt_allocation(size, is_tlab, false /*first_only*/); + + if (result != NULL) { + assert(gch->is_in_reserved(result), "result not in heap"); + return result; + } + + // OK, collection failed, try expansion. + result = expand_heap_and_allocate(size, is_tlab); + if (result != NULL) { + return result; + } + + // If we reach this point, we're really out of memory. Try every trick + // we can to reclaim memory. Force collection of soft references. Force + // a complete compaction of the heap. Any additional methods for finding + // free memory should be here, especially if they are expensive. If this + // attempt fails, an OOM exception will be thrown. + { + IntFlagSetting flag_change(MarkSweepAlwaysCompactCount, 1); // Make sure the heap is fully compacted + + gch->do_collection(true /* full */, + true /* clear_all_soft_refs */, + size /* size */, + is_tlab /* is_tlab */, + number_of_generations() - 1 /* max_level */); + } + + result = gch->attempt_allocation(size, is_tlab, false /* first_only */); + if (result != NULL) { + assert(gch->is_in_reserved(result), "result not in heap"); + return result; + } + + // What else? We might try synchronous finalization later. If the total + // space available is large enough for the allocation, then a more + // complete compaction phase than we've tried so far might be + // appropriate. + return NULL; +} + +size_t GenCollectorPolicy::large_typearray_limit() { + return FastAllocateSizeLimit; +} + +// Return true if any of the following is true: +// . the allocation won't fit into the current young gen heap +// . gc locker is occupied (jni critical section) +// . heap memory is tight -- the most recent previous collection +// was a full collection because a partial collection (would +// have) failed and is likely to fail again +bool GenCollectorPolicy::should_try_older_generation_allocation( + size_t word_size) const { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + size_t gen0_capacity = gch->get_gen(0)->capacity_before_gc(); + return (word_size > heap_word_size(gen0_capacity)) + || (GC_locker::is_active_and_needs_gc()) + || ( gch->last_incremental_collection_failed() + && gch->incremental_collection_will_fail()); +} + + +// +// MarkSweepPolicy methods +// + +MarkSweepPolicy::MarkSweepPolicy() { + initialize_all(); +} + +void MarkSweepPolicy::initialize_generations() { + initialize_perm_generation(PermGen::MarkSweepCompact); + _generations = new GenerationSpecPtr[number_of_generations()]; + if (_generations == NULL) + vm_exit_during_initialization("Unable to allocate gen spec"); + + if (UseParNewGC && ParallelGCThreads > 0) { + _generations[0] = new GenerationSpec(Generation::ParNew, _initial_gen0_size, _max_gen0_size); + } else { + _generations[0] = new GenerationSpec(Generation::DefNew, _initial_gen0_size, _max_gen0_size); + } + _generations[1] = new GenerationSpec(Generation::MarkSweepCompact, _initial_gen1_size, _max_gen1_size); + + if (_generations[0] == NULL || _generations[1] == NULL) + vm_exit_during_initialization("Unable to allocate gen spec"); +} + +void MarkSweepPolicy::initialize_gc_policy_counters() { + // initialize the policy counters - 2 collectors, 3 generations + if (UseParNewGC && ParallelGCThreads > 0) { + _gc_policy_counters = new GCPolicyCounters("ParNew:MSC", 2, 3); + } + else { + _gc_policy_counters = new GCPolicyCounters("Copy:MSC", 2, 3); + } +} diff --git a/hotspot/src/share/vm/memory/collectorPolicy.hpp b/hotspot/src/share/vm/memory/collectorPolicy.hpp new file mode 100644 index 00000000000..fd00a800d32 --- /dev/null +++ b/hotspot/src/share/vm/memory/collectorPolicy.hpp @@ -0,0 +1,261 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This class (or more correctly, subtypes of this class) +// are used to define global garbage collector attributes. +// This includes initialization of generations and any other +// shared resources they may need. +// +// In general, all flag adjustment and validation should be +// done in initialize_flags(), which is called prior to +// initialize_size_info(). +// +// This class is not fully developed yet. As more collector(s) +// are added, it is expected that we will come across further +// behavior that requires global attention. The correct place +// to deal with those issues is this class. + +// Forward declarations. +class GenCollectorPolicy; +class TwoGenerationCollectorPolicy; +#ifndef SERIALGC +class ConcurrentMarkSweepPolicy; +#endif // SERIALGC +class AdaptiveSizePolicy; +class GCPolicyCounters; +class PermanentGenerationSpec; +class MarkSweepPolicy; + +class CollectorPolicy : public CHeapObj { + protected: + PermanentGenerationSpec *_permanent_generation; + GCPolicyCounters* _gc_policy_counters; + + // Requires that the concrete subclass sets the alignment constraints + // before calling. + virtual void initialize_flags(); + virtual void initialize_size_info() = 0; + // Initialize "_permanent_generation" to a spec for the given kind of + // Perm Gen. + void initialize_perm_generation(PermGen::Name pgnm); + + size_t _initial_heap_byte_size; + size_t _max_heap_byte_size; + size_t _min_heap_byte_size; + + size_t _min_alignment; + size_t _max_alignment; + + CollectorPolicy() : + _min_alignment(1), + _max_alignment(1), + _initial_heap_byte_size(0), + _max_heap_byte_size(0), + _min_heap_byte_size(0) + {} + + public: + void set_min_alignment(size_t align) { _min_alignment = align; } + size_t min_alignment() { return _min_alignment; } + void set_max_alignment(size_t align) { _max_alignment = align; } + size_t max_alignment() { return _max_alignment; } + + size_t initial_heap_byte_size() { return _initial_heap_byte_size; } + size_t max_heap_byte_size() { return _max_heap_byte_size; } + size_t min_heap_byte_size() { return _min_heap_byte_size; } + + enum Name { + CollectorPolicyKind, + TwoGenerationCollectorPolicyKind, + TrainPolicyKind, + ConcurrentMarkSweepPolicyKind, + ASConcurrentMarkSweepPolicyKind + }; + + // Identification methods. + virtual GenCollectorPolicy* as_generation_policy() { return NULL; } + virtual TwoGenerationCollectorPolicy* as_two_generation_policy() { return NULL; } + virtual MarkSweepPolicy* as_mark_sweep_policy() { return NULL; } +#ifndef SERIALGC + virtual ConcurrentMarkSweepPolicy* as_concurrent_mark_sweep_policy() { return NULL; } +#endif // SERIALGC + // Note that these are not virtual. + bool is_generation_policy() { return as_generation_policy() != NULL; } + bool is_two_generation_policy() { return as_two_generation_policy() != NULL; } + bool is_mark_sweep_policy() { return as_mark_sweep_policy() != NULL; } +#ifndef SERIALGC + bool is_concurrent_mark_sweep_policy() { return as_concurrent_mark_sweep_policy() != NULL; } +#else // SERIALGC + bool is_concurrent_mark_sweep_policy() { return false; } +#endif // SERIALGC + + virtual PermanentGenerationSpec *permanent_generation() { + assert(_permanent_generation != NULL, "Sanity check"); + return _permanent_generation; + } + + virtual BarrierSet::Name barrier_set_name() = 0; + virtual GenRemSet::Name rem_set_name() = 0; + + // Create the remembered set (to cover the given reserved region, + // allowing breaking up into at most "max_covered_regions"). + virtual GenRemSet* create_rem_set(MemRegion reserved, + int max_covered_regions); + + // This method controls how a collector satisfies a request + // for a block of memory. "gc_time_limit_was_exceeded" will + // be set to true if the adaptive size policy determine that + // an excessive amount of time is being spent doing collections + // and caused a NULL to be returned. If a NULL is not returned, + // "gc_time_limit_was_exceeded" has an undefined meaning. + virtual HeapWord* mem_allocate_work(size_t size, + bool is_tlab, + bool* gc_overhead_limit_was_exceeded) = 0; + + // This method controls how a collector handles one or more + // of its generations being fully allocated. + virtual HeapWord *satisfy_failed_allocation(size_t size, bool is_tlab) = 0; + // Performace Counter support + GCPolicyCounters* counters() { return _gc_policy_counters; } + + // Create the jstat counters for the GC policy. By default, policy's + // don't have associated counters, and we complain if this is invoked. + virtual void initialize_gc_policy_counters() { + ShouldNotReachHere(); + } + + virtual CollectorPolicy::Name kind() { + return CollectorPolicy::CollectorPolicyKind; + } + + // Returns true if a collector has eden space with soft end. + virtual bool has_soft_ended_eden() { + return false; + } + +}; + +class GenCollectorPolicy : public CollectorPolicy { + protected: + size_t _min_gen0_size; + size_t _initial_gen0_size; + size_t _max_gen0_size; + + GenerationSpec **_generations; + + // The sizing of the different generations in the heap are controlled + // by a sizing policy. + AdaptiveSizePolicy* _size_policy; + + // Return true if an allocation should be attempted in the older + // generation if it fails in the younger generation. Return + // false, otherwise. + virtual bool should_try_older_generation_allocation(size_t word_size) const; + + void initialize_flags(); + void initialize_size_info(); + + // Try to allocate space by expanding the heap. + virtual HeapWord* expand_heap_and_allocate(size_t size, bool is_tlab); + + // compute max heap alignment + size_t compute_max_alignment(); + + + public: + virtual int number_of_generations() = 0; + + virtual GenerationSpec **generations() { + assert(_generations != NULL, "Sanity check"); + return _generations; + } + + virtual GenCollectorPolicy* as_generation_policy() { return this; } + + virtual void initialize_generations() = 0; + + virtual void initialize_all() { + initialize_flags(); + initialize_size_info(); + initialize_generations(); + } + + HeapWord* mem_allocate_work(size_t size, + bool is_tlab, + bool* gc_overhead_limit_was_exceeded); + + HeapWord *satisfy_failed_allocation(size_t size, bool is_tlab); + + // The size that defines a "large array". + virtual size_t large_typearray_limit(); + + // Adaptive size policy + AdaptiveSizePolicy* size_policy() { return _size_policy; } + virtual void initialize_size_policy(size_t init_eden_size, + size_t init_promo_size, + size_t init_survivor_size); + +}; + + +// All of hotspot's current collectors are subtypes of this +// class. Currently, these collectors all use the same gen[0], +// but have different gen[1] types. If we add another subtype +// of CollectorPolicy, this class should be broken out into +// its own file. + +class TwoGenerationCollectorPolicy : public GenCollectorPolicy { + protected: + size_t _min_gen1_size; + size_t _initial_gen1_size; + size_t _max_gen1_size; + + void initialize_flags(); + void initialize_size_info(); + void initialize_generations() { ShouldNotReachHere(); } + + public: + // Inherited methods + TwoGenerationCollectorPolicy* as_two_generation_policy() { return this; } + + int number_of_generations() { return 2; } + BarrierSet::Name barrier_set_name() { return BarrierSet::CardTableModRef; } + GenRemSet::Name rem_set_name() { return GenRemSet::CardTable; } + + virtual CollectorPolicy::Name kind() { + return CollectorPolicy::TwoGenerationCollectorPolicyKind; + } +}; + +class MarkSweepPolicy : public TwoGenerationCollectorPolicy { + protected: + void initialize_generations(); + + public: + MarkSweepPolicy(); + + MarkSweepPolicy* as_mark_sweep_policy() { return this; } + + void initialize_gc_policy_counters(); +}; diff --git a/hotspot/src/share/vm/memory/compactPermGen.hpp b/hotspot/src/share/vm/memory/compactPermGen.hpp new file mode 100644 index 00000000000..d65c50b007d --- /dev/null +++ b/hotspot/src/share/vm/memory/compactPermGen.hpp @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ContigPermSpace; +class CardTableModRefBS; +class CompactingPermGenGen; +class PermanentGenerationSpec; + +// A PermGen implemented with a contiguous space. +class CompactingPermGen: public PermGen { + friend class VMStructs; +protected: + // The "generation" view. + OneContigSpaceCardGeneration* _gen; + +public: + CompactingPermGen(ReservedSpace rs, ReservedSpace shared_rs, + size_t initial_byte_size, GenRemSet* remset, + PermanentGenerationSpec* perm_spec); + + HeapWord* mem_allocate(size_t size); + + void compute_new_size(); + + Generation* as_gen() const { return _gen; } + +}; diff --git a/hotspot/src/share/vm/memory/compactingPermGenGen.cpp b/hotspot/src/share/vm/memory/compactingPermGenGen.cpp new file mode 100644 index 00000000000..990f514a739 --- /dev/null +++ b/hotspot/src/share/vm/memory/compactingPermGenGen.cpp @@ -0,0 +1,457 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_compactingPermGenGen.cpp.incl" + + +// Recursively adjust all pointers in an object and all objects by +// referenced it. Clear marks on objects in order to prevent visiting +// any object twice. + +class RecursiveAdjustSharedObjectClosure : public OopClosure { +public: + void do_oop(oop* o) { + oop obj = *o; + if (obj->is_shared_readwrite()) { + if (obj->mark()->is_marked()) { + obj->init_mark(); // Don't revisit this object. + obj->oop_iterate(this); // Recurse - adjust objects referenced. + obj->adjust_pointers(); // Adjust this object's references. + + // Special case: if a class has a read-only constant pool, + // then the read-write objects referenced by the pool must + // have their marks reset. + + if (obj->klass() == Universe::instanceKlassKlassObj()) { + instanceKlass* ik = instanceKlass::cast((klassOop)obj); + constantPoolOop cp = ik->constants(); + if (cp->is_shared_readonly()) { + cp->oop_iterate(this); + } + } + } + } + }; +}; + + +// We need to go through all placeholders in the system dictionary and +// try to resolve them into shared classes. Other threads might be in +// the process of loading a shared class and have strong roots on +// their stack to the class without having added the class to the +// dictionary yet. This means the class will be marked during phase 1 +// but will not be unmarked during the application of the +// RecursiveAdjustSharedObjectClosure to the SystemDictionary. Note +// that we must not call find_shared_class with non-read-only symbols +// as doing so can cause hash codes to be computed, destroying +// forwarding pointers. +class TraversePlaceholdersClosure : public OopClosure { + public: + void do_oop(oop* o) { + oop obj = *o; + if (obj->klass() == Universe::symbolKlassObj() && + obj->is_shared_readonly()) { + symbolHandle sym((symbolOop) obj); + oop k = SystemDictionary::find_shared_class(sym); + if (k != NULL) { + RecursiveAdjustSharedObjectClosure clo; + clo.do_oop(&k); + } + } + } +}; + + +void CompactingPermGenGen::initialize_performance_counters() { + + const char* gen_name = "perm"; + + // Generation Counters - generation 2, 1 subspace + _gen_counters = new GenerationCounters(gen_name, 2, 1, &_virtual_space); + + _space_counters = new CSpaceCounters(gen_name, 0, + _virtual_space.reserved_size(), + _the_space, _gen_counters); +} + +void CompactingPermGenGen::update_counters() { + if (UsePerfData) { + _space_counters->update_all(); + _gen_counters->update_all(); + } +} + + +CompactingPermGenGen::CompactingPermGenGen(ReservedSpace rs, + ReservedSpace shared_rs, + size_t initial_byte_size, + int level, GenRemSet* remset, + ContiguousSpace* space, + PermanentGenerationSpec* spec_) : + OneContigSpaceCardGeneration(rs, initial_byte_size, MinPermHeapExpansion, + level, remset, space) { + + set_spec(spec_); + if (!UseSharedSpaces && !DumpSharedSpaces) { + spec()->disable_sharing(); + } + + // Break virtual space into address ranges for all spaces. + + if (spec()->enable_shared_spaces()) { + shared_end = (HeapWord*)(shared_rs.base() + shared_rs.size()); + misccode_end = shared_end; + misccode_bottom = misccode_end - heap_word_size(spec()->misc_code_size()); + miscdata_end = misccode_bottom; + miscdata_bottom = miscdata_end - heap_word_size(spec()->misc_data_size()); + readwrite_end = miscdata_bottom; + readwrite_bottom = + readwrite_end - heap_word_size(spec()->read_write_size()); + readonly_end = readwrite_bottom; + readonly_bottom = + readonly_end - heap_word_size(spec()->read_only_size()); + shared_bottom = readonly_bottom; + unshared_end = shared_bottom; + assert((char*)shared_bottom == shared_rs.base(), "shared space mismatch"); + } else { + shared_end = (HeapWord*)(rs.base() + rs.size()); + misccode_end = shared_end; + misccode_bottom = shared_end; + miscdata_end = shared_end; + miscdata_bottom = shared_end; + readwrite_end = shared_end; + readwrite_bottom = shared_end; + readonly_end = shared_end; + readonly_bottom = shared_end; + shared_bottom = shared_end; + unshared_end = shared_bottom; + } + unshared_bottom = (HeapWord*) rs.base(); + + // Verify shared and unshared spaces adjacent. + assert((char*)shared_bottom == rs.base()+rs.size(), "shared space mismatch"); + assert(unshared_end > unshared_bottom, "shared space mismatch"); + + // Split reserved memory into pieces. + + ReservedSpace ro_rs = shared_rs.first_part(spec()->read_only_size(), + UseSharedSpaces); + ReservedSpace tmp_rs1 = shared_rs.last_part(spec()->read_only_size()); + ReservedSpace rw_rs = tmp_rs1.first_part(spec()->read_write_size(), + UseSharedSpaces); + ReservedSpace tmp_rs2 = tmp_rs1.last_part(spec()->read_write_size()); + ReservedSpace md_rs = tmp_rs2.first_part(spec()->misc_data_size(), + UseSharedSpaces); + ReservedSpace mc_rs = tmp_rs2.last_part(spec()->misc_data_size()); + + _shared_space_size = spec()->read_only_size() + + spec()->read_write_size() + + spec()->misc_data_size() + + spec()->misc_code_size(); + + // Allocate the unshared (default) space. + _the_space = new ContigPermSpace(_bts, + MemRegion(unshared_bottom, heap_word_size(initial_byte_size))); + if (_the_space == NULL) + vm_exit_during_initialization("Could not allocate an unshared" + " CompactingPermGen Space"); + + // Allocate shared spaces + if (spec()->enable_shared_spaces()) { + + // If mapping a shared file, the space is not committed, don't + // mangle. + NOT_PRODUCT(bool old_ZapUnusedHeapArea = ZapUnusedHeapArea;) + NOT_PRODUCT(if (UseSharedSpaces) ZapUnusedHeapArea = false;) + + // Commit the memory behind the shared spaces if dumping (not + // mapping). + if (DumpSharedSpaces) { + _ro_vs.initialize(ro_rs, spec()->read_only_size()); + _rw_vs.initialize(rw_rs, spec()->read_write_size()); + _md_vs.initialize(md_rs, spec()->misc_data_size()); + _mc_vs.initialize(mc_rs, spec()->misc_code_size()); + } + + // Allocate the shared spaces. + _ro_bts = new BlockOffsetSharedArray( + MemRegion(readonly_bottom, + heap_word_size(spec()->read_only_size())), + heap_word_size(spec()->read_only_size())); + _ro_space = new OffsetTableContigSpace(_ro_bts, + MemRegion(readonly_bottom, readonly_end)); + _rw_bts = new BlockOffsetSharedArray( + MemRegion(readwrite_bottom, + heap_word_size(spec()->read_write_size())), + heap_word_size(spec()->read_write_size())); + _rw_space = new OffsetTableContigSpace(_rw_bts, + MemRegion(readwrite_bottom, readwrite_end)); + + // Restore mangling flag. + NOT_PRODUCT(ZapUnusedHeapArea = old_ZapUnusedHeapArea;) + + if (_ro_space == NULL || _rw_space == NULL) + vm_exit_during_initialization("Could not allocate a shared space"); + + // Cover both shared spaces entirely with cards. + _rs->resize_covered_region(MemRegion(readonly_bottom, readwrite_end)); + + if (UseSharedSpaces) { + + // Map in the regions in the shared file. + FileMapInfo* mapinfo = FileMapInfo::current_info(); + size_t image_alignment = mapinfo->alignment(); + CollectedHeap* ch = Universe::heap(); + if ((!mapinfo->map_space(ro, ro_rs, _ro_space)) || + (!mapinfo->map_space(rw, rw_rs, _rw_space)) || + (!mapinfo->map_space(md, md_rs, NULL)) || + (!mapinfo->map_space(mc, mc_rs, NULL)) || + // check the alignment constraints + (ch == NULL || ch->kind() != CollectedHeap::GenCollectedHeap || + image_alignment != + ((GenCollectedHeap*)ch)->gen_policy()->max_alignment())) { + // Base addresses didn't match; skip sharing, but continue + shared_rs.release(); + spec()->disable_sharing(); + // If -Xshare:on is specified, print out the error message and exit VM, + // otherwise, set UseSharedSpaces to false and continue. + if (RequireSharedSpaces) { + vm_exit_during_initialization("Unable to use shared archive.", NULL); + } else { + FLAG_SET_DEFAULT(UseSharedSpaces, false); + } + + // Note: freeing the block offset array objects does not + // currently free up the underlying storage. + delete _ro_bts; + _ro_bts = NULL; + delete _ro_space; + _ro_space = NULL; + delete _rw_bts; + _rw_bts = NULL; + delete _rw_space; + _rw_space = NULL; + shared_end = (HeapWord*)(rs.base() + rs.size()); + _rs->resize_covered_region(MemRegion(shared_bottom, shared_bottom)); + } + } + + // Reserved region includes shared spaces for oop.is_in_reserved(). + _reserved.set_end(shared_end); + + } else { + _ro_space = NULL; + _rw_space = NULL; + } +} + + +// Do a complete scan of the shared read write space to catch all +// objects which contain references to any younger generation. Forward +// the pointers. Avoid space_iterate, as actually visiting all the +// objects in the space will page in more objects than we need. +// Instead, use the system dictionary as strong roots into the read +// write space. + +void CompactingPermGenGen::pre_adjust_pointers() { + if (spec()->enable_shared_spaces()) { + RecursiveAdjustSharedObjectClosure blk; + Universe::oops_do(&blk); + StringTable::oops_do(&blk); + SystemDictionary::always_strong_classes_do(&blk); + TraversePlaceholdersClosure tpc; + SystemDictionary::placeholders_do(&tpc); + } +} + + +#ifdef ASSERT +class VerifyMarksClearedClosure : public ObjectClosure { +public: + void do_object(oop obj) { + assert(SharedSkipVerify || !obj->mark()->is_marked(), + "Shared oop still marked?"); + } +}; +#endif + + +void CompactingPermGenGen::post_compact() { +#ifdef ASSERT + if (!SharedSkipVerify && spec()->enable_shared_spaces()) { + VerifyMarksClearedClosure blk; + rw_space()->object_iterate(&blk); + } +#endif +} + + +void CompactingPermGenGen::space_iterate(SpaceClosure* blk, bool usedOnly) { + OneContigSpaceCardGeneration::space_iterate(blk, usedOnly); + if (spec()->enable_shared_spaces()) { +#ifdef PRODUCT + // Making the rw_space walkable will page in the entire space, and + // is to be avoided. However, this is required for Verify options. + ShouldNotReachHere(); +#endif + + blk->do_space(ro_space()); + blk->do_space(rw_space()); + } +} + + +void CompactingPermGenGen::print_on(outputStream* st) const { + OneContigSpaceCardGeneration::print_on(st); + if (spec()->enable_shared_spaces()) { + st->print(" ro"); + ro_space()->print_on(st); + st->print(" rw"); + rw_space()->print_on(st); + } else { + st->print_cr("No shared spaces configured."); + } +} + + +// References from the perm gen to the younger generation objects may +// occur in static fields in Java classes or in constant pool references +// to String objects. + +void CompactingPermGenGen::younger_refs_iterate(OopsInGenClosure* blk) { + OneContigSpaceCardGeneration::younger_refs_iterate(blk); + if (spec()->enable_shared_spaces()) { + blk->set_generation(this); + // ro_space has no younger gen refs. + _rs->younger_refs_in_space_iterate(rw_space(), blk); + blk->reset_generation(); + } +} + + +// Shared spaces are addressed in pre_adjust_pointers. +void CompactingPermGenGen::adjust_pointers() { + the_space()->adjust_pointers(); +} + + +void CompactingPermGenGen::compact() { + the_space()->compact(); +} + + +size_t CompactingPermGenGen::contiguous_available() const { + // Don't include shared spaces. + return OneContigSpaceCardGeneration::contiguous_available() + - _shared_space_size; +} + +size_t CompactingPermGenGen::max_capacity() const { + // Don't include shared spaces. + assert(UseSharedSpaces || (_shared_space_size == 0), + "If not used, the size of shared spaces should be 0"); + return OneContigSpaceCardGeneration::max_capacity() + - _shared_space_size; +} + + + +bool CompactingPermGenGen::grow_by(size_t bytes) { + // Don't allow _virtual_size to expand into shared spaces. + size_t max_bytes = _virtual_space.uncommitted_size() - _shared_space_size; + if (bytes > _shared_space_size) { + bytes = _shared_space_size; + } + return OneContigSpaceCardGeneration::grow_by(bytes); +} + + +void CompactingPermGenGen::grow_to_reserved() { + // Don't allow _virtual_size to expand into shared spaces. + if (_virtual_space.uncommitted_size() > _shared_space_size) { + size_t remaining_bytes = + _virtual_space.uncommitted_size() - _shared_space_size; + bool success = OneContigSpaceCardGeneration::grow_by(remaining_bytes); + DEBUG_ONLY(if (!success) warning("grow to reserved failed");) + } +} + + +// No young generation references, clear this generation's main space's +// card table entries. Do NOT clear the card table entries for the +// read-only space (always clear) or the read-write space (valuable +// information). + +void CompactingPermGenGen::clear_remembered_set() { + _rs->clear(MemRegion(the_space()->bottom(), the_space()->end())); +} + + +// Objects in this generation's main space may have moved, invalidate +// that space's cards. Do NOT invalidate the card table entries for the +// read-only or read-write spaces, as those objects never move. + +void CompactingPermGenGen::invalidate_remembered_set() { + _rs->invalidate(used_region()); +} + + +void CompactingPermGenGen::verify(bool allow_dirty) { + the_space()->verify(allow_dirty); + if (!SharedSkipVerify && spec()->enable_shared_spaces()) { + ro_space()->verify(allow_dirty); + rw_space()->verify(allow_dirty); + } +} + + +HeapWord* CompactingPermGenGen::unshared_bottom; +HeapWord* CompactingPermGenGen::unshared_end; +HeapWord* CompactingPermGenGen::shared_bottom; +HeapWord* CompactingPermGenGen::shared_end; +HeapWord* CompactingPermGenGen::readonly_bottom; +HeapWord* CompactingPermGenGen::readonly_end; +HeapWord* CompactingPermGenGen::readwrite_bottom; +HeapWord* CompactingPermGenGen::readwrite_end; +HeapWord* CompactingPermGenGen::miscdata_bottom; +HeapWord* CompactingPermGenGen::miscdata_end; +HeapWord* CompactingPermGenGen::misccode_bottom; +HeapWord* CompactingPermGenGen::misccode_end; + +// JVM/TI RedefineClasses() support: +bool CompactingPermGenGen::remap_shared_readonly_as_readwrite() { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + + if (UseSharedSpaces) { + // remap the shared readonly space to shared readwrite, private + FileMapInfo* mapinfo = FileMapInfo::current_info(); + if (!mapinfo->remap_shared_readonly_as_readwrite()) { + return false; + } + } + return true; +} + +void** CompactingPermGenGen::_vtbl_list; diff --git a/hotspot/src/share/vm/memory/compactingPermGenGen.hpp b/hotspot/src/share/vm/memory/compactingPermGenGen.hpp new file mode 100644 index 00000000000..ea0bb185dab --- /dev/null +++ b/hotspot/src/share/vm/memory/compactingPermGenGen.hpp @@ -0,0 +1,248 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// All heaps contains a "permanent generation," containing permanent +// (reflective) objects. This is like a regular generation in some ways, +// but unlike one in others, and so is split apart. + +class PermanentGenerationSpec; + +// This is the "generation" view of a CompactingPermGen. +class CompactingPermGenGen: public OneContigSpaceCardGeneration { + friend class VMStructs; + // Abstractly, this is a subtype that gets access to protected fields. + friend class CompactingPermGen; + +private: + // Shared spaces + PermanentGenerationSpec* _spec; + size_t _shared_space_size; + VirtualSpace _ro_vs; + VirtualSpace _rw_vs; + VirtualSpace _md_vs; + VirtualSpace _mc_vs; + BlockOffsetSharedArray* _ro_bts; + BlockOffsetSharedArray* _rw_bts; + OffsetTableContigSpace* _ro_space; + OffsetTableContigSpace* _rw_space; + + // With shared spaces there is a dicotomy in the use of the + // _virtual_space of the generation. There is a portion of the + // _virtual_space that is used for the unshared part of the + // permanent generation and a portion that is reserved for the shared part. + // The _reserved field in the generation represents both the + // unshared and shared parts of the generation. The _reserved + // variable is initialized for only the unshared part but is + // later extended to include the shared part during initialization + // if shared spaces are being used. + // The reserved size for the _virtual_space for CompactingPermGenGen + // is the size of the space for the permanent generation including the + // the shared spaces. This can be seen by the use of MaxPermSize + // in the allocation of PermanentGenerationSpec. The space for the + // shared spaces is committed separately (???). + // In general at initialization only a part of the + // space for the unshared part of the permanent generation is + // committed and more is committed as the permanent generation is + // grown. In growing the permanent generation the capacity() and + // max_capacity() of the generation are used. For the permanent + // generation (implemented with a CompactingPermGenGen) the capacity() + // is taken from the capacity of the space (_the_space variable used for the + // unshared part of the generation) and the max_capacity() is based + // on the size of the _reserved variable (which includes the size of the + // shared spaces) minus the size of the shared spaces. + + // These values are redundant, but are called out separately to avoid + // going through heap/space/gen pointers for performance. + static HeapWord* unshared_bottom; + static HeapWord* unshared_end; + static HeapWord* shared_bottom; + static HeapWord* readonly_bottom; + static HeapWord* readonly_end; + static HeapWord* readwrite_bottom; + static HeapWord* readwrite_end; + static HeapWord* miscdata_bottom; + static HeapWord* miscdata_end; + static HeapWord* misccode_bottom; + static HeapWord* misccode_end; + static HeapWord* shared_end; + + // List of klassOops whose vtbl entries are used to patch others. + static void** _vtbl_list; + + // Performance Counters + GenerationCounters* _gen_counters; + CSpaceCounters* _space_counters; + + void initialize_performance_counters(); + +public: + + enum { + vtbl_list_size = 16, // number of entries in the shared space vtable list. + num_virtuals = 100 // number of virtual methods in Klass (or + // subclass) objects, or greater. + }; + + enum { + ro = 0, // read-only shared space in the heap + rw = 1, // read-write shared space in the heap + md = 2, // miscellaneous data for initializing tables, etc. + mc = 3, // miscellaneous code - vtable replacement. + n_regions = 4 + }; + + CompactingPermGenGen(ReservedSpace rs, ReservedSpace shared_rs, + size_t initial_byte_size, int level, GenRemSet* remset, + ContiguousSpace* space, + PermanentGenerationSpec* perm_spec); + + const char* name() const { + return "compacting perm gen"; + } + + const char* short_name() const { + return "Perm"; + } + + // Return the maximum capacity for the object space. This + // explicitly does not include the shared spaces. + size_t max_capacity() const; + + void update_counters(); + + void compute_new_size() { + assert(false, "Should not call this -- handled at PermGen level."); + } + + bool must_be_youngest() const { return false; } + bool must_be_oldest() const { return false; } + + OffsetTableContigSpace* ro_space() const { return _ro_space; } + OffsetTableContigSpace* rw_space() const { return _rw_space; } + VirtualSpace* md_space() { return &_md_vs; } + VirtualSpace* mc_space() { return &_mc_vs; } + ContiguousSpace* unshared_space() const { return _the_space; } + + static bool inline is_shared(const oopDesc* p) { + return (HeapWord*)p >= shared_bottom && (HeapWord*)p < shared_end; + } + // RedefineClasses note: this tester is used to check residence of + // the specified oop in the shared readonly space and not whether + // the oop is readonly. + static bool inline is_shared_readonly(const oopDesc* p) { + return (HeapWord*)p >= readonly_bottom && (HeapWord*)p < readonly_end; + } + // RedefineClasses note: this tester is used to check residence of + // the specified oop in the shared readwrite space and not whether + // the oop is readwrite. + static bool inline is_shared_readwrite(const oopDesc* p) { + return (HeapWord*)p >= readwrite_bottom && (HeapWord*)p < readwrite_end; + } + + bool is_in_unshared(const void* p) const { + return OneContigSpaceCardGeneration::is_in(p); + } + + bool is_in_shared(const void* p) const { + return p >= shared_bottom && p < shared_end; + } + + inline bool is_in(const void* p) const { + return is_in_unshared(p) || is_in_shared(p); + } + + inline PermanentGenerationSpec* spec() const { return _spec; } + inline void set_spec(PermanentGenerationSpec* spec) { _spec = spec; } + + void pre_adjust_pointers(); + void adjust_pointers(); + void space_iterate(SpaceClosure* blk, bool usedOnly = false); + void print_on(outputStream* st) const; + void younger_refs_iterate(OopsInGenClosure* blk); + void compact(); + void post_compact(); + size_t contiguous_available() const; + bool grow_by(size_t bytes); + void grow_to_reserved(); + + void clear_remembered_set(); + void invalidate_remembered_set(); + + inline bool block_is_obj(const HeapWord* addr) const { + if (addr < the_space()->top()) return true; + else if (addr < the_space()->end()) return false; + else if (addr < ro_space()->top()) return true; + else if (addr < ro_space()->end()) return false; + else if (addr < rw_space()->top()) return true; + else return false; + } + + + inline size_t block_size(const HeapWord* addr) const { + if (addr < the_space()->top()) { + return oop(addr)->size(); + } + else if (addr < the_space()->end()) { + assert(addr == the_space()->top(), "non-block head arg to block_size"); + return the_space()->end() - the_space()->top(); + } + + else if (addr < ro_space()->top()) { + return oop(addr)->size(); + } + else if (addr < ro_space()->end()) { + assert(addr == ro_space()->top(), "non-block head arg to block_size"); + return ro_space()->end() - ro_space()->top(); + } + + else if (addr < rw_space()->top()) { + return oop(addr)->size(); + } + else { + assert(addr == rw_space()->top(), "non-block head arg to block_size"); + return rw_space()->end() - rw_space()->top(); + } + } + + static void generate_vtable_methods(void** vtbl_list, + void** vtable, + char** md_top, char* md_end, + char** mc_top, char* mc_end); + + void verify(bool allow_dirty); + + // Serialization + static void initialize_oops() KERNEL_RETURN; + static void serialize_oops(SerializeOopClosure* soc); + void serialize_bts(SerializeOopClosure* soc); + + // Initiate dumping of shared file. + static jint dump_shared(GrowableArray* class_promote_order, TRAPS); + + // JVM/TI RedefineClasses() support: + // Remap the shared readonly space to shared readwrite, private if + // sharing is enabled. Simply returns true if sharing is not enabled + // or if the remapping has already been done by a prior call. + static bool remap_shared_readonly_as_readwrite(); +}; diff --git a/hotspot/src/share/vm/memory/defNewGeneration.cpp b/hotspot/src/share/vm/memory/defNewGeneration.cpp new file mode 100644 index 00000000000..f892ffdb5ad --- /dev/null +++ b/hotspot/src/share/vm/memory/defNewGeneration.cpp @@ -0,0 +1,864 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_defNewGeneration.cpp.incl" + +// +// DefNewGeneration functions. + +// Methods of protected closure types. + +DefNewGeneration::IsAliveClosure::IsAliveClosure(Generation* g) : _g(g) { + assert(g->level() == 0, "Optimized for youngest gen."); +} +void DefNewGeneration::IsAliveClosure::do_object(oop p) { + assert(false, "Do not call."); +} +bool DefNewGeneration::IsAliveClosure::do_object_b(oop p) { + return (HeapWord*)p >= _g->reserved().end() || p->is_forwarded(); +} + +DefNewGeneration::KeepAliveClosure:: +KeepAliveClosure(ScanWeakRefClosure* cl) : _cl(cl) { + GenRemSet* rs = GenCollectedHeap::heap()->rem_set(); + assert(rs->rs_kind() == GenRemSet::CardTable, "Wrong rem set kind."); + _rs = (CardTableRS*)rs; +} + +void DefNewGeneration::KeepAliveClosure::do_oop(oop* p) { + // We never expect to see a null reference being processed + // as a weak reference. + assert (*p != NULL, "expected non-null ref"); + assert ((*p)->is_oop(), "expected an oop while scanning weak refs"); + + _cl->do_oop_nv(p); + + // Card marking is trickier for weak refs. + // This oop is a 'next' field which was filled in while we + // were discovering weak references. While we might not need + // to take a special action to keep this reference alive, we + // will need to dirty a card as the field was modified. + // + // Alternatively, we could create a method which iterates through + // each generation, allowing them in turn to examine the modified + // field. + // + // We could check that p is also in an older generation, but + // dirty cards in the youngest gen are never scanned, so the + // extra check probably isn't worthwhile. + if (Universe::heap()->is_in_reserved(p)) { + _rs->inline_write_ref_field_gc(p, *p); + } +} + +DefNewGeneration::FastKeepAliveClosure:: +FastKeepAliveClosure(DefNewGeneration* g, ScanWeakRefClosure* cl) : + DefNewGeneration::KeepAliveClosure(cl) { + _boundary = g->reserved().end(); +} + +void DefNewGeneration::FastKeepAliveClosure::do_oop(oop* p) { + assert (*p != NULL, "expected non-null ref"); + assert ((*p)->is_oop(), "expected an oop while scanning weak refs"); + + _cl->do_oop_nv(p); + + // Optimized for Defnew generation if it's the youngest generation: + // we set a younger_gen card if we have an older->youngest + // generation pointer. + if (((HeapWord*)(*p) < _boundary) && Universe::heap()->is_in_reserved(p)) { + _rs->inline_write_ref_field_gc(p, *p); + } +} + +DefNewGeneration::EvacuateFollowersClosure:: +EvacuateFollowersClosure(GenCollectedHeap* gch, int level, + ScanClosure* cur, ScanClosure* older) : + _gch(gch), _level(level), + _scan_cur_or_nonheap(cur), _scan_older(older) +{} + +void DefNewGeneration::EvacuateFollowersClosure::do_void() { + do { + _gch->oop_since_save_marks_iterate(_level, _scan_cur_or_nonheap, + _scan_older); + } while (!_gch->no_allocs_since_save_marks(_level)); +} + +DefNewGeneration::FastEvacuateFollowersClosure:: +FastEvacuateFollowersClosure(GenCollectedHeap* gch, int level, + DefNewGeneration* gen, + FastScanClosure* cur, FastScanClosure* older) : + _gch(gch), _level(level), _gen(gen), + _scan_cur_or_nonheap(cur), _scan_older(older) +{} + +void DefNewGeneration::FastEvacuateFollowersClosure::do_void() { + do { + _gch->oop_since_save_marks_iterate(_level, _scan_cur_or_nonheap, + _scan_older); + } while (!_gch->no_allocs_since_save_marks(_level)); + guarantee(_gen->promo_failure_scan_stack() == NULL + || _gen->promo_failure_scan_stack()->length() == 0, + "Failed to finish scan"); +} + +ScanClosure::ScanClosure(DefNewGeneration* g, bool gc_barrier) : + OopsInGenClosure(g), _g(g), _gc_barrier(gc_barrier) +{ + assert(_g->level() == 0, "Optimized for youngest generation"); + _boundary = _g->reserved().end(); +} + +FastScanClosure::FastScanClosure(DefNewGeneration* g, bool gc_barrier) : + OopsInGenClosure(g), _g(g), _gc_barrier(gc_barrier) +{ + assert(_g->level() == 0, "Optimized for youngest generation"); + _boundary = _g->reserved().end(); +} + +ScanWeakRefClosure::ScanWeakRefClosure(DefNewGeneration* g) : + OopClosure(g->ref_processor()), _g(g) +{ + assert(_g->level() == 0, "Optimized for youngest generation"); + _boundary = _g->reserved().end(); +} + + +DefNewGeneration::DefNewGeneration(ReservedSpace rs, + size_t initial_size, + int level, + const char* policy) + : Generation(rs, initial_size, level), + _objs_with_preserved_marks(NULL), + _preserved_marks_of_objs(NULL), + _promo_failure_scan_stack(NULL), + _promo_failure_drain_in_progress(false), + _should_allocate_from_space(false) +{ + MemRegion cmr((HeapWord*)_virtual_space.low(), + (HeapWord*)_virtual_space.high()); + Universe::heap()->barrier_set()->resize_covered_region(cmr); + + if (GenCollectedHeap::heap()->collector_policy()->has_soft_ended_eden()) { + _eden_space = new ConcEdenSpace(this); + } else { + _eden_space = new EdenSpace(this); + } + _from_space = new ContiguousSpace(); + _to_space = new ContiguousSpace(); + + if (_eden_space == NULL || _from_space == NULL || _to_space == NULL) + vm_exit_during_initialization("Could not allocate a new gen space"); + + // Compute the maximum eden and survivor space sizes. These sizes + // are computed assuming the entire reserved space is committed. + // These values are exported as performance counters. + uintx alignment = GenCollectedHeap::heap()->collector_policy()->min_alignment(); + uintx size = _virtual_space.reserved_size(); + _max_survivor_size = compute_survivor_size(size, alignment); + _max_eden_size = size - (2*_max_survivor_size); + + // allocate the performance counters + + // Generation counters -- generation 0, 3 subspaces + _gen_counters = new GenerationCounters("new", 0, 3, &_virtual_space); + _gc_counters = new CollectorCounters(policy, 0); + + _eden_counters = new CSpaceCounters("eden", 0, _max_eden_size, _eden_space, + _gen_counters); + _from_counters = new CSpaceCounters("s0", 1, _max_survivor_size, _from_space, + _gen_counters); + _to_counters = new CSpaceCounters("s1", 2, _max_survivor_size, _to_space, + _gen_counters); + + compute_space_boundaries(0); + update_counters(); + _next_gen = NULL; + _tenuring_threshold = MaxTenuringThreshold; + _pretenure_size_threshold_words = PretenureSizeThreshold >> LogHeapWordSize; +} + +void DefNewGeneration::compute_space_boundaries(uintx minimum_eden_size) { + uintx alignment = GenCollectedHeap::heap()->collector_policy()->min_alignment(); + + // Compute sizes + uintx size = _virtual_space.committed_size(); + uintx survivor_size = compute_survivor_size(size, alignment); + uintx eden_size = size - (2*survivor_size); + assert(eden_size > 0 && survivor_size <= eden_size, "just checking"); + + if (eden_size < minimum_eden_size) { + // May happen due to 64Kb rounding, if so adjust eden size back up + minimum_eden_size = align_size_up(minimum_eden_size, alignment); + uintx maximum_survivor_size = (size - minimum_eden_size) / 2; + uintx unaligned_survivor_size = + align_size_down(maximum_survivor_size, alignment); + survivor_size = MAX2(unaligned_survivor_size, alignment); + eden_size = size - (2*survivor_size); + assert(eden_size > 0 && survivor_size <= eden_size, "just checking"); + assert(eden_size >= minimum_eden_size, "just checking"); + } + + char *eden_start = _virtual_space.low(); + char *from_start = eden_start + eden_size; + char *to_start = from_start + survivor_size; + char *to_end = to_start + survivor_size; + + assert(to_end == _virtual_space.high(), "just checking"); + assert(Space::is_aligned((HeapWord*)eden_start), "checking alignment"); + assert(Space::is_aligned((HeapWord*)from_start), "checking alignment"); + assert(Space::is_aligned((HeapWord*)to_start), "checking alignment"); + + MemRegion edenMR((HeapWord*)eden_start, (HeapWord*)from_start); + MemRegion fromMR((HeapWord*)from_start, (HeapWord*)to_start); + MemRegion toMR ((HeapWord*)to_start, (HeapWord*)to_end); + + eden()->initialize(edenMR, (minimum_eden_size == 0)); + // If minumum_eden_size != 0, we will not have cleared any + // portion of eden above its top. This can cause newly + // expanded space not to be mangled if using ZapUnusedHeapArea. + // We explicitly do such mangling here. + if (ZapUnusedHeapArea && (minimum_eden_size != 0)) { + eden()->mangle_unused_area(); + } + from()->initialize(fromMR, true); + to()->initialize(toMR , true); + eden()->set_next_compaction_space(from()); + // The to-space is normally empty before a compaction so need + // not be considered. The exception is during promotion + // failure handling when to-space can contain live objects. + from()->set_next_compaction_space(NULL); +} + +void DefNewGeneration::swap_spaces() { + ContiguousSpace* s = from(); + _from_space = to(); + _to_space = s; + eden()->set_next_compaction_space(from()); + // The to-space is normally empty before a compaction so need + // not be considered. The exception is during promotion + // failure handling when to-space can contain live objects. + from()->set_next_compaction_space(NULL); + + if (UsePerfData) { + CSpaceCounters* c = _from_counters; + _from_counters = _to_counters; + _to_counters = c; + } +} + +bool DefNewGeneration::expand(size_t bytes) { + MutexLocker x(ExpandHeap_lock); + bool success = _virtual_space.expand_by(bytes); + + // Do not attempt an expand-to-the reserve size. The + // request should properly observe the maximum size of + // the generation so an expand-to-reserve should be + // unnecessary. Also a second call to expand-to-reserve + // value potentially can cause an undue expansion. + // For example if the first expand fail for unknown reasons, + // but the second succeeds and expands the heap to its maximum + // value. + if (GC_locker::is_active()) { + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("Garbage collection disabled, expanded heap instead"); + } + } + + return success; +} + + +void DefNewGeneration::compute_new_size() { + // This is called after a gc that includes the following generation + // (which is required to exist.) So from-space will normally be empty. + // Note that we check both spaces, since if scavenge failed they revert roles. + // If not we bail out (otherwise we would have to relocate the objects) + if (!from()->is_empty() || !to()->is_empty()) { + return; + } + + int next_level = level() + 1; + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(next_level < gch->_n_gens, + "DefNewGeneration cannot be an oldest gen"); + + Generation* next_gen = gch->_gens[next_level]; + size_t old_size = next_gen->capacity(); + size_t new_size_before = _virtual_space.committed_size(); + size_t min_new_size = spec()->init_size(); + size_t max_new_size = reserved().byte_size(); + assert(min_new_size <= new_size_before && + new_size_before <= max_new_size, + "just checking"); + // All space sizes must be multiples of Generation::GenGrain. + size_t alignment = Generation::GenGrain; + + // Compute desired new generation size based on NewRatio and + // NewSizeThreadIncrease + size_t desired_new_size = old_size/NewRatio; + int threads_count = Threads::number_of_non_daemon_threads(); + size_t thread_increase_size = threads_count * NewSizeThreadIncrease; + desired_new_size = align_size_up(desired_new_size + thread_increase_size, alignment); + + // Adjust new generation size + desired_new_size = MAX2(MIN2(desired_new_size, max_new_size), min_new_size); + assert(desired_new_size <= max_new_size, "just checking"); + + bool changed = false; + if (desired_new_size > new_size_before) { + size_t change = desired_new_size - new_size_before; + assert(change % alignment == 0, "just checking"); + if (expand(change)) { + changed = true; + } + // If the heap failed to expand to the desired size, + // "changed" will be false. If the expansion failed + // (and at this point it was expected to succeed), + // ignore the failure (leaving "changed" as false). + } + if (desired_new_size < new_size_before && eden()->is_empty()) { + // bail out of shrinking if objects in eden + size_t change = new_size_before - desired_new_size; + assert(change % alignment == 0, "just checking"); + _virtual_space.shrink_by(change); + changed = true; + } + if (changed) { + compute_space_boundaries(eden()->used()); + MemRegion cmr((HeapWord*)_virtual_space.low(), (HeapWord*)_virtual_space.high()); + Universe::heap()->barrier_set()->resize_covered_region(cmr); + if (Verbose && PrintGC) { + size_t new_size_after = _virtual_space.committed_size(); + size_t eden_size_after = eden()->capacity(); + size_t survivor_size_after = from()->capacity(); + gclog_or_tty->print("New generation size " SIZE_FORMAT "K->" SIZE_FORMAT "K [eden=" + SIZE_FORMAT "K,survivor=" SIZE_FORMAT "K]", + new_size_before/K, new_size_after/K, eden_size_after/K, survivor_size_after/K); + if (WizardMode) { + gclog_or_tty->print("[allowed " SIZE_FORMAT "K extra for %d threads]", + thread_increase_size/K, threads_count); + } + gclog_or_tty->cr(); + } + } +} + +void DefNewGeneration::object_iterate_since_last_GC(ObjectClosure* cl) { + // $$$ This may be wrong in case of "scavenge failure"? + eden()->object_iterate(cl); +} + +void DefNewGeneration::younger_refs_iterate(OopsInGenClosure* cl) { + assert(false, "NYI -- are you sure you want to call this?"); +} + + +size_t DefNewGeneration::capacity() const { + return eden()->capacity() + + from()->capacity(); // to() is only used during scavenge +} + + +size_t DefNewGeneration::used() const { + return eden()->used() + + from()->used(); // to() is only used during scavenge +} + + +size_t DefNewGeneration::free() const { + return eden()->free() + + from()->free(); // to() is only used during scavenge +} + +size_t DefNewGeneration::max_capacity() const { + const size_t alignment = GenCollectedHeap::heap()->collector_policy()->min_alignment(); + const size_t reserved_bytes = reserved().byte_size(); + return reserved_bytes - compute_survivor_size(reserved_bytes, alignment); +} + +size_t DefNewGeneration::unsafe_max_alloc_nogc() const { + return eden()->free(); +} + +size_t DefNewGeneration::capacity_before_gc() const { + return eden()->capacity(); +} + +size_t DefNewGeneration::contiguous_available() const { + return eden()->free(); +} + + +HeapWord** DefNewGeneration::top_addr() const { return eden()->top_addr(); } +HeapWord** DefNewGeneration::end_addr() const { return eden()->end_addr(); } + +void DefNewGeneration::object_iterate(ObjectClosure* blk) { + eden()->object_iterate(blk); + from()->object_iterate(blk); +} + + +void DefNewGeneration::space_iterate(SpaceClosure* blk, + bool usedOnly) { + blk->do_space(eden()); + blk->do_space(from()); + blk->do_space(to()); +} + +// The last collection bailed out, we are running out of heap space, +// so we try to allocate the from-space, too. +HeapWord* DefNewGeneration::allocate_from_space(size_t size) { + HeapWord* result = NULL; + if (PrintGC && Verbose) { + gclog_or_tty->print("DefNewGeneration::allocate_from_space(%u):" + " will_fail: %s" + " heap_lock: %s" + " free: " SIZE_FORMAT, + size, + GenCollectedHeap::heap()->incremental_collection_will_fail() ? "true" : "false", + Heap_lock->is_locked() ? "locked" : "unlocked", + from()->free()); + } + if (should_allocate_from_space() || GC_locker::is_active_and_needs_gc()) { + if (Heap_lock->owned_by_self() || + (SafepointSynchronize::is_at_safepoint() && + Thread::current()->is_VM_thread())) { + // If the Heap_lock is not locked by this thread, this will be called + // again later with the Heap_lock held. + result = from()->allocate(size); + } else if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" Heap_lock is not owned by self"); + } + } else if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" should_allocate_from_space: NOT"); + } + if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" returns %s", result == NULL ? "NULL" : "object"); + } + return result; +} + +HeapWord* DefNewGeneration::expand_and_allocate(size_t size, + bool is_tlab, + bool parallel) { + // We don't attempt to expand the young generation (but perhaps we should.) + return allocate(size, is_tlab); +} + + +void DefNewGeneration::collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool is_tlab) { + assert(full || size > 0, "otherwise we don't want to collect"); + GenCollectedHeap* gch = GenCollectedHeap::heap(); + _next_gen = gch->next_gen(this); + assert(_next_gen != NULL, + "This must be the youngest gen, and not the only gen"); + + // If the next generation is too full to accomodate promotion + // from this generation, pass on collection; let the next generation + // do it. + if (!collection_attempt_is_safe()) { + gch->set_incremental_collection_will_fail(); + return; + } + assert(to()->is_empty(), "Else not collection_attempt_is_safe"); + + init_assuming_no_promotion_failure(); + + TraceTime t1("GC", PrintGC && !PrintGCDetails, true, gclog_or_tty); + // Capture heap used before collection (for printing). + size_t gch_prev_used = gch->used(); + + SpecializationStats::clear(); + + // These can be shared for all code paths + IsAliveClosure is_alive(this); + ScanWeakRefClosure scan_weak_ref(this); + + age_table()->clear(); + to()->clear(); + + gch->rem_set()->prepare_for_younger_refs_iterate(false); + + assert(gch->no_allocs_since_save_marks(0), + "save marks have not been newly set."); + + // Weak refs. + // FIXME: Are these storage leaks, or are they resource objects? +#ifdef COMPILER2 + ReferencePolicy *soft_ref_policy = new LRUMaxHeapPolicy(); +#else + ReferencePolicy *soft_ref_policy = new LRUCurrentHeapPolicy(); +#endif // COMPILER2 + + // Not very pretty. + CollectorPolicy* cp = gch->collector_policy(); + + FastScanClosure fsc_with_no_gc_barrier(this, false); + FastScanClosure fsc_with_gc_barrier(this, true); + + set_promo_failure_scan_stack_closure(&fsc_with_no_gc_barrier); + FastEvacuateFollowersClosure evacuate_followers(gch, _level, this, + &fsc_with_no_gc_barrier, + &fsc_with_gc_barrier); + + assert(gch->no_allocs_since_save_marks(0), + "save marks have not been newly set."); + + gch->gen_process_strong_roots(_level, + true, // Process younger gens, if any, as + // strong roots. + false,// not collecting permanent generation. + SharedHeap::SO_AllClasses, + &fsc_with_gc_barrier, + &fsc_with_no_gc_barrier); + + // "evacuate followers". + evacuate_followers.do_void(); + + FastKeepAliveClosure keep_alive(this, &scan_weak_ref); + ref_processor()->process_discovered_references( + soft_ref_policy, &is_alive, &keep_alive, &evacuate_followers, NULL); + if (!promotion_failed()) { + // Swap the survivor spaces. + eden()->clear(); + from()->clear(); + swap_spaces(); + + assert(to()->is_empty(), "to space should be empty now"); + + // Set the desired survivor size to half the real survivor space + _tenuring_threshold = + age_table()->compute_tenuring_threshold(to()->capacity()/HeapWordSize); + + if (PrintGC && !PrintGCDetails) { + gch->print_heap_change(gch_prev_used); + } + } else { + assert(HandlePromotionFailure, + "Should not be here unless promotion failure handling is on"); + assert(_promo_failure_scan_stack != NULL && + _promo_failure_scan_stack->length() == 0, "post condition"); + + // deallocate stack and it's elements + delete _promo_failure_scan_stack; + _promo_failure_scan_stack = NULL; + + remove_forwarding_pointers(); + if (PrintGCDetails) { + gclog_or_tty->print(" (promotion failed)"); + } + // Add to-space to the list of space to compact + // when a promotion failure has occurred. In that + // case there can be live objects in to-space + // as a result of a partial evacuation of eden + // and from-space. + swap_spaces(); // For the sake of uniformity wrt ParNewGeneration::collect(). + from()->set_next_compaction_space(to()); + gch->set_incremental_collection_will_fail(); + + // Reset the PromotionFailureALot counters. + NOT_PRODUCT(Universe::heap()->reset_promotion_should_fail();) + } + // set new iteration safe limit for the survivor spaces + from()->set_concurrent_iteration_safe_limit(from()->top()); + to()->set_concurrent_iteration_safe_limit(to()->top()); + SpecializationStats::print(); + update_time_of_last_gc(os::javaTimeMillis()); +} + +class RemoveForwardPointerClosure: public ObjectClosure { +public: + void do_object(oop obj) { + obj->init_mark(); + } +}; + +void DefNewGeneration::init_assuming_no_promotion_failure() { + _promotion_failed = false; + from()->set_next_compaction_space(NULL); +} + +void DefNewGeneration::remove_forwarding_pointers() { + RemoveForwardPointerClosure rspc; + eden()->object_iterate(&rspc); + from()->object_iterate(&rspc); + // Now restore saved marks, if any. + if (_objs_with_preserved_marks != NULL) { + assert(_preserved_marks_of_objs != NULL, "Both or none."); + assert(_objs_with_preserved_marks->length() == + _preserved_marks_of_objs->length(), "Both or none."); + for (int i = 0; i < _objs_with_preserved_marks->length(); i++) { + oop obj = _objs_with_preserved_marks->at(i); + markOop m = _preserved_marks_of_objs->at(i); + obj->set_mark(m); + } + delete _objs_with_preserved_marks; + delete _preserved_marks_of_objs; + _objs_with_preserved_marks = NULL; + _preserved_marks_of_objs = NULL; + } +} + +void DefNewGeneration::preserve_mark_if_necessary(oop obj, markOop m) { + if (m->must_be_preserved_for_promotion_failure(obj)) { + if (_objs_with_preserved_marks == NULL) { + assert(_preserved_marks_of_objs == NULL, "Both or none."); + _objs_with_preserved_marks = new (ResourceObj::C_HEAP) + GrowableArray(PreserveMarkStackSize, true); + _preserved_marks_of_objs = new (ResourceObj::C_HEAP) + GrowableArray(PreserveMarkStackSize, true); + } + _objs_with_preserved_marks->push(obj); + _preserved_marks_of_objs->push(m); + } +} + +void DefNewGeneration::handle_promotion_failure(oop old) { + preserve_mark_if_necessary(old, old->mark()); + // forward to self + old->forward_to(old); + _promotion_failed = true; + + push_on_promo_failure_scan_stack(old); + + if (!_promo_failure_drain_in_progress) { + // prevent recursion in copy_to_survivor_space() + _promo_failure_drain_in_progress = true; + drain_promo_failure_scan_stack(); + _promo_failure_drain_in_progress = false; + } +} + +oop DefNewGeneration::copy_to_survivor_space(oop old, oop* from) { + assert(is_in_reserved(old) && !old->is_forwarded(), + "shouldn't be scavenging this oop"); + size_t s = old->size(); + oop obj = NULL; + + // Try allocating obj in to-space (unless too old) + if (old->age() < tenuring_threshold()) { + obj = (oop) to()->allocate(s); + } + + // Otherwise try allocating obj tenured + if (obj == NULL) { + obj = _next_gen->promote(old, s, from); + if (obj == NULL) { + if (!HandlePromotionFailure) { + // A failed promotion likely means the MaxLiveObjectEvacuationRatio flag + // is incorrectly set. In any case, its seriously wrong to be here! + vm_exit_out_of_memory(s*wordSize, "promotion"); + } + + handle_promotion_failure(old); + return old; + } + } else { + // Prefetch beyond obj + const intx interval = PrefetchCopyIntervalInBytes; + Prefetch::write(obj, interval); + + // Copy obj + Copy::aligned_disjoint_words((HeapWord*)old, (HeapWord*)obj, s); + + // Increment age if obj still in new generation + obj->incr_age(); + age_table()->add(obj, s); + } + + // Done, insert forward pointer to obj in this header + old->forward_to(obj); + + return obj; +} + +void DefNewGeneration::push_on_promo_failure_scan_stack(oop obj) { + if (_promo_failure_scan_stack == NULL) { + _promo_failure_scan_stack = new (ResourceObj::C_HEAP) + GrowableArray(40, true); + } + + _promo_failure_scan_stack->push(obj); +} + +void DefNewGeneration::drain_promo_failure_scan_stack() { + assert(_promo_failure_scan_stack != NULL, "precondition"); + + while (_promo_failure_scan_stack->length() > 0) { + oop obj = _promo_failure_scan_stack->pop(); + obj->oop_iterate(_promo_failure_scan_stack_closure); + } +} + +void DefNewGeneration::save_marks() { + eden()->set_saved_mark(); + to()->set_saved_mark(); + from()->set_saved_mark(); +} + + +void DefNewGeneration::reset_saved_marks() { + eden()->reset_saved_mark(); + to()->reset_saved_mark(); + from()->reset_saved_mark(); +} + + +bool DefNewGeneration::no_allocs_since_save_marks() { + assert(eden()->saved_mark_at_top(), "Violated spec - alloc in eden"); + assert(from()->saved_mark_at_top(), "Violated spec - alloc in from"); + return to()->saved_mark_at_top(); +} + +#define DefNew_SINCE_SAVE_MARKS_DEFN(OopClosureType, nv_suffix) \ + \ +void DefNewGeneration:: \ +oop_since_save_marks_iterate##nv_suffix(OopClosureType* cl) { \ + cl->set_generation(this); \ + eden()->oop_since_save_marks_iterate##nv_suffix(cl); \ + to()->oop_since_save_marks_iterate##nv_suffix(cl); \ + from()->oop_since_save_marks_iterate##nv_suffix(cl); \ + cl->reset_generation(); \ + save_marks(); \ +} + +ALL_SINCE_SAVE_MARKS_CLOSURES(DefNew_SINCE_SAVE_MARKS_DEFN) + +#undef DefNew_SINCE_SAVE_MARKS_DEFN + +void DefNewGeneration::contribute_scratch(ScratchBlock*& list, Generation* requestor, + size_t max_alloc_words) { + if (requestor == this || _promotion_failed) return; + assert(requestor->level() > level(), "DefNewGeneration must be youngest"); + + /* $$$ Assert this? "trace" is a "MarkSweep" function so that's not appropriate. + if (to_space->top() > to_space->bottom()) { + trace("to_space not empty when contribute_scratch called"); + } + */ + + ContiguousSpace* to_space = to(); + assert(to_space->end() >= to_space->top(), "pointers out of order"); + size_t free_words = pointer_delta(to_space->end(), to_space->top()); + if (free_words >= MinFreeScratchWords) { + ScratchBlock* sb = (ScratchBlock*)to_space->top(); + sb->num_words = free_words; + sb->next = list; + list = sb; + } +} + +bool DefNewGeneration::collection_attempt_is_safe() { + if (!to()->is_empty()) { + return false; + } + if (_next_gen == NULL) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + _next_gen = gch->next_gen(this); + assert(_next_gen != NULL, + "This must be the youngest gen, and not the only gen"); + } + + // Decide if there's enough room for a full promotion + // When using extremely large edens, we effectively lose a + // large amount of old space. Use the "MaxLiveObjectEvacuationRatio" + // flag to reduce the minimum evacuation space requirements. If + // there is not enough space to evacuate eden during a scavenge, + // the VM will immediately exit with an out of memory error. + // This flag has not been tested + // with collectors other than simple mark & sweep. + // + // Note that with the addition of promotion failure handling, the + // VM will not immediately exit but will undo the young generation + // collection. The parameter is left here for compatibility. + const double evacuation_ratio = MaxLiveObjectEvacuationRatio / 100.0; + + // worst_case_evacuation is based on "used()". For the case where this + // method is called after a collection, this is still appropriate because + // the case that needs to be detected is one in which a full collection + // has been done and has overflowed into the young generation. In that + // case a minor collection will fail (the overflow of the full collection + // means there is no space in the old generation for any promotion). + size_t worst_case_evacuation = (size_t)(used() * evacuation_ratio); + + return _next_gen->promotion_attempt_is_safe(worst_case_evacuation, + HandlePromotionFailure); +} + +void DefNewGeneration::gc_epilogue(bool full) { + // Check if the heap is approaching full after a collection has + // been done. Generally the young generation is empty at + // a minimum at the end of a collection. If it is not, then + // the heap is approaching full. + GenCollectedHeap* gch = GenCollectedHeap::heap(); + clear_should_allocate_from_space(); + if (collection_attempt_is_safe()) { + gch->clear_incremental_collection_will_fail(); + } else { + gch->set_incremental_collection_will_fail(); + if (full) { // we seem to be running out of space + set_should_allocate_from_space(); + } + } + + // update the generation and space performance counters + update_counters(); + gch->collector_policy()->counters()->update_counters(); +} + +void DefNewGeneration::update_counters() { + if (UsePerfData) { + _eden_counters->update_all(); + _from_counters->update_all(); + _to_counters->update_all(); + _gen_counters->update_all(); + } +} + +void DefNewGeneration::verify(bool allow_dirty) { + eden()->verify(allow_dirty); + from()->verify(allow_dirty); + to()->verify(allow_dirty); +} + +void DefNewGeneration::print_on(outputStream* st) const { + Generation::print_on(st); + st->print(" eden"); + eden()->print_on(st); + st->print(" from"); + from()->print_on(st); + st->print(" to "); + to()->print_on(st); +} + + +const char* DefNewGeneration::name() const { + return "def new generation"; +} diff --git a/hotspot/src/share/vm/memory/defNewGeneration.hpp b/hotspot/src/share/vm/memory/defNewGeneration.hpp new file mode 100644 index 00000000000..289a5317e07 --- /dev/null +++ b/hotspot/src/share/vm/memory/defNewGeneration.hpp @@ -0,0 +1,332 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class EdenSpace; +class ContiguousSpace; + +// DefNewGeneration is a young generation containing eden, from- and +// to-space. + +class DefNewGeneration: public Generation { + friend class VMStructs; + +protected: + Generation* _next_gen; + int _tenuring_threshold; // Tenuring threshold for next collection. + ageTable _age_table; + // Size of object to pretenure in words; command line provides bytes + size_t _pretenure_size_threshold_words; + + ageTable* age_table() { return &_age_table; } + // Initialize state to optimistically assume no promotion failure will + // happen. + void init_assuming_no_promotion_failure(); + // True iff a promotion has failed in the current collection. + bool _promotion_failed; + bool promotion_failed() { return _promotion_failed; } + + // Handling promotion failure. A young generation collection + // can fail if a live object cannot be copied out of its + // location in eden or from-space during the collection. If + // a collection fails, the young generation is left in a + // consistent state such that it can be collected by a + // full collection. + // Before the collection + // Objects are in eden or from-space + // All roots into the young generation point into eden or from-space. + // + // After a failed collection + // Objects may be in eden, from-space, or to-space + // An object A in eden or from-space may have a copy B + // in to-space. If B exists, all roots that once pointed + // to A must now point to B. + // All objects in the young generation are unmarked. + // Eden, from-space, and to-space will all be collected by + // the full collection. + void handle_promotion_failure(oop); + + // In the absence of promotion failure, we wouldn't look at "from-space" + // objects after a young-gen collection. When promotion fails, however, + // the subsequent full collection will look at from-space objects: + // therefore we must remove their forwarding pointers. + void remove_forwarding_pointers(); + + // Preserve the mark of "obj", if necessary, in preparation for its mark + // word being overwritten with a self-forwarding-pointer. + void preserve_mark_if_necessary(oop obj, markOop m); + + // When one is non-null, so is the other. Together, they each pair is + // an object with a preserved mark, and its mark value. + GrowableArray* _objs_with_preserved_marks; + GrowableArray* _preserved_marks_of_objs; + + // Returns true if the collection can be safely attempted. + // If this method returns false, a collection is not + // guaranteed to fail but the system may not be able + // to recover from the failure. + bool collection_attempt_is_safe(); + + // Promotion failure handling + OopClosure *_promo_failure_scan_stack_closure; + void set_promo_failure_scan_stack_closure(OopClosure *scan_stack_closure) { + _promo_failure_scan_stack_closure = scan_stack_closure; + } + + GrowableArray* _promo_failure_scan_stack; + GrowableArray* promo_failure_scan_stack() const { + return _promo_failure_scan_stack; + } + void push_on_promo_failure_scan_stack(oop); + void drain_promo_failure_scan_stack(void); + bool _promo_failure_drain_in_progress; + + // Performance Counters + GenerationCounters* _gen_counters; + CSpaceCounters* _eden_counters; + CSpaceCounters* _from_counters; + CSpaceCounters* _to_counters; + + // sizing information + size_t _max_eden_size; + size_t _max_survivor_size; + + // Allocation support + bool _should_allocate_from_space; + bool should_allocate_from_space() const { + return _should_allocate_from_space; + } + void clear_should_allocate_from_space() { + _should_allocate_from_space = false; + } + void set_should_allocate_from_space() { + _should_allocate_from_space = true; + } + + protected: + // Spaces + EdenSpace* _eden_space; + ContiguousSpace* _from_space; + ContiguousSpace* _to_space; + + enum SomeProtectedConstants { + // Generations are GenGrain-aligned and have size that are multiples of + // GenGrain. + MinFreeScratchWords = 100 + }; + + // Return the size of a survivor space if this generation were of size + // gen_size. + size_t compute_survivor_size(size_t gen_size, size_t alignment) const { + size_t n = gen_size / (SurvivorRatio + 2); + return n > alignment ? align_size_down(n, alignment) : alignment; + } + + public: // was "protected" but caused compile error on win32 + class IsAliveClosure: public BoolObjectClosure { + Generation* _g; + public: + IsAliveClosure(Generation* g); + void do_object(oop p); + bool do_object_b(oop p); + }; + + class KeepAliveClosure: public OopClosure { + protected: + ScanWeakRefClosure* _cl; + CardTableRS* _rs; + public: + KeepAliveClosure(ScanWeakRefClosure* cl); + void do_oop(oop* p); + }; + + class FastKeepAliveClosure: public KeepAliveClosure { + protected: + HeapWord* _boundary; + public: + FastKeepAliveClosure(DefNewGeneration* g, ScanWeakRefClosure* cl); + void do_oop(oop* p); + }; + + class EvacuateFollowersClosure: public VoidClosure { + GenCollectedHeap* _gch; + int _level; + ScanClosure* _scan_cur_or_nonheap; + ScanClosure* _scan_older; + public: + EvacuateFollowersClosure(GenCollectedHeap* gch, int level, + ScanClosure* cur, ScanClosure* older); + void do_void(); + }; + + class FastEvacuateFollowersClosure; + friend class FastEvacuateFollowersClosure; + class FastEvacuateFollowersClosure: public VoidClosure { + GenCollectedHeap* _gch; + int _level; + DefNewGeneration* _gen; + FastScanClosure* _scan_cur_or_nonheap; + FastScanClosure* _scan_older; + public: + FastEvacuateFollowersClosure(GenCollectedHeap* gch, int level, + DefNewGeneration* gen, + FastScanClosure* cur, + FastScanClosure* older); + void do_void(); + }; + + public: + DefNewGeneration(ReservedSpace rs, size_t initial_byte_size, int level, + const char* policy="Copy"); + + virtual Generation::Name kind() { return Generation::DefNew; } + + // Accessing spaces + EdenSpace* eden() const { return _eden_space; } + ContiguousSpace* from() const { return _from_space; } + ContiguousSpace* to() const { return _to_space; } + + inline CompactibleSpace* first_compaction_space() const; + + // Space enquiries + size_t capacity() const; + size_t used() const; + size_t free() const; + size_t max_capacity() const; + size_t capacity_before_gc() const; + size_t unsafe_max_alloc_nogc() const; + size_t contiguous_available() const; + + size_t max_eden_size() const { return _max_eden_size; } + size_t max_survivor_size() const { return _max_survivor_size; } + + bool supports_inline_contig_alloc() const { return true; } + HeapWord** top_addr() const; + HeapWord** end_addr() const; + + // Thread-local allocation buffers + bool supports_tlab_allocation() const { return true; } + inline size_t tlab_capacity() const; + inline size_t unsafe_max_tlab_alloc() const; + + // Grow the generation by the specified number of bytes. + // The size of bytes is assumed to be properly aligned. + // Return true if the expansion was successful. + bool expand(size_t bytes); + + // DefNewGeneration cannot currently expand except at + // a GC. + virtual bool is_maximal_no_gc() const { return true; } + + // Iteration + void object_iterate(ObjectClosure* blk); + void object_iterate_since_last_GC(ObjectClosure* cl); + + void younger_refs_iterate(OopsInGenClosure* cl); + + void space_iterate(SpaceClosure* blk, bool usedOnly = false); + + // Allocation support + virtual bool should_allocate(size_t word_size, bool is_tlab) { + assert(UseTLAB || !is_tlab, "Should not allocate tlab"); + + size_t overflow_limit = (size_t)1 << (BitsPerSize_t - LogHeapWordSize); + + const bool non_zero = word_size > 0; + const bool overflows = word_size >= overflow_limit; + const bool check_too_big = _pretenure_size_threshold_words > 0; + const bool not_too_big = word_size < _pretenure_size_threshold_words; + const bool size_ok = is_tlab || !check_too_big || not_too_big; + + bool result = !overflows && + non_zero && + size_ok; + + return result; + } + + inline HeapWord* allocate(size_t word_size, bool is_tlab); + HeapWord* allocate_from_space(size_t word_size); + + inline HeapWord* par_allocate(size_t word_size, bool is_tlab); + + // Prologue & Epilogue + inline virtual void gc_prologue(bool full); + virtual void gc_epilogue(bool full); + + // Doesn't require additional work during GC prologue and epilogue + virtual bool performs_in_place_marking() const { return false; } + + // Accessing marks + void save_marks(); + void reset_saved_marks(); + bool no_allocs_since_save_marks(); + + // Need to declare the full complement of closures, whether we'll + // override them or not, or get message from the compiler: + // oop_since_save_marks_iterate_nv hides virtual function... +#define DefNew_SINCE_SAVE_MARKS_DECL(OopClosureType, nv_suffix) \ + void oop_since_save_marks_iterate##nv_suffix(OopClosureType* cl); + + ALL_SINCE_SAVE_MARKS_CLOSURES(DefNew_SINCE_SAVE_MARKS_DECL) + +#undef DefNew_SINCE_SAVE_MARKS_DECL + + // For non-youngest collection, the DefNewGeneration can contribute + // "to-space". + void contribute_scratch(ScratchBlock*& list, Generation* requestor, + size_t max_alloc_words); + + // GC support + virtual void compute_new_size(); + virtual void collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool is_tlab); + HeapWord* expand_and_allocate(size_t size, + bool is_tlab, + bool parallel = false); + + oop copy_to_survivor_space(oop old, oop* from); + int tenuring_threshold() { return _tenuring_threshold; } + + // Performance Counter support + void update_counters(); + + // Printing + virtual const char* name() const; + virtual const char* short_name() const { return "DefNew"; } + + bool must_be_youngest() const { return true; } + bool must_be_oldest() const { return false; } + + // PrintHeapAtGC support. + void print_on(outputStream* st) const; + + void verify(bool allow_dirty); + + protected: + void compute_space_boundaries(uintx minimum_eden_size); + // Scavenge support + void swap_spaces(); +}; diff --git a/hotspot/src/share/vm/memory/defNewGeneration.inline.hpp b/hotspot/src/share/vm/memory/defNewGeneration.inline.hpp new file mode 100644 index 00000000000..dffc86b5a19 --- /dev/null +++ b/hotspot/src/share/vm/memory/defNewGeneration.inline.hpp @@ -0,0 +1,88 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +CompactibleSpace* DefNewGeneration::first_compaction_space() const { + return eden(); +} + +HeapWord* DefNewGeneration::allocate(size_t word_size, + bool is_tlab) { + // This is the slow-path allocation for the DefNewGeneration. + // Most allocations are fast-path in compiled code. + // We try to allocate from the eden. If that works, we are happy. + // Note that since DefNewGeneration supports lock-free allocation, we + // have to use it here, as well. + HeapWord* result = eden()->par_allocate(word_size); + if (result != NULL) { + return result; + } + do { + HeapWord* old_limit = eden()->soft_end(); + if (old_limit < eden()->end()) { + // Tell the next generation we reached a limit. + HeapWord* new_limit = + next_gen()->allocation_limit_reached(eden(), eden()->top(), word_size); + if (new_limit != NULL) { + Atomic::cmpxchg_ptr(new_limit, eden()->soft_end_addr(), old_limit); + } else { + assert(eden()->soft_end() == eden()->end(), + "invalid state after allocation_limit_reached returned null"); + } + } else { + // The allocation failed and the soft limit is equal to the hard limit, + // there are no reasons to do an attempt to allocate + assert(old_limit == eden()->end(), "sanity check"); + break; + } + // Try to allocate until succeeded or the soft limit can't be adjusted + result = eden()->par_allocate(word_size); + } while (result == NULL); + + // If the eden is full and the last collection bailed out, we are running + // out of heap space, and we try to allocate the from-space, too. + // allocate_from_space can't be inlined because that would introduce a + // circular dependency at compile time. + if (result == NULL) { + result = allocate_from_space(word_size); + } + return result; +} + +HeapWord* DefNewGeneration::par_allocate(size_t word_size, + bool is_tlab) { + return eden()->par_allocate(word_size); +} + +void DefNewGeneration::gc_prologue(bool full) { + // Ensure that _end and _soft_end are the same in eden space. + eden()->set_soft_end(eden()->end()); +} + +size_t DefNewGeneration::tlab_capacity() const { + return eden()->capacity(); +} + +size_t DefNewGeneration::unsafe_max_tlab_alloc() const { + return unsafe_max_alloc_nogc(); +} diff --git a/hotspot/src/share/vm/memory/dump.cpp b/hotspot/src/share/vm/memory/dump.cpp new file mode 100644 index 00000000000..4f75ca8e7c9 --- /dev/null +++ b/hotspot/src/share/vm/memory/dump.cpp @@ -0,0 +1,1454 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_dump.cpp.incl" + + +// Closure to set up the fingerprint field for all methods. + +class FingerprintMethodsClosure: public ObjectClosure { +public: + void do_object(oop obj) { + if (obj->is_method()) { + methodOop mobj = (methodOop)obj; + ResourceMark rm; + (new Fingerprinter(mobj))->fingerprint(); + } + } +}; + + + +// Closure to set the hash value (String.hash field) in all of the +// String objects in the heap. Setting the hash value is not required. +// However, setting the value in advance prevents the value from being +// written later, increasing the likelihood that the shared page contain +// the hash can be shared. +// +// NOTE THAT the algorithm in StringTable::hash_string() MUST MATCH the +// algorithm in java.lang.String.hashCode(). + +class StringHashCodeClosure: public OopClosure { +private: + Thread* THREAD; + int hash_offset; +public: + StringHashCodeClosure(Thread* t) { + THREAD = t; + hash_offset = java_lang_String::hash_offset_in_bytes(); + } + + void do_oop(oop* pobj) { + if (pobj != NULL) { + oop obj = *pobj; + if (obj->klass() == SystemDictionary::string_klass()) { + + int hash; + typeArrayOop value = java_lang_String::value(obj); + int length = java_lang_String::length(obj); + if (length == 0) { + hash = 0; + } else { + int offset = java_lang_String::offset(obj); + jchar* s = value->char_at_addr(offset); + hash = StringTable::hash_string(s, length); + } + obj->int_field_put(hash_offset, hash); + } + } + } +}; + + +// Remove data from objects which should not appear in the shared file +// (as it pertains only to the current JVM). + +class RemoveUnshareableInfoClosure : public ObjectClosure { +public: + void do_object(oop obj) { + // Zap data from the objects which is pertains only to this JVM. We + // want that data recreated in new JVMs when the shared file is used. + if (obj->is_method()) { + ((methodOop)obj)->remove_unshareable_info(); + } + else if (obj->is_klass()) { + Klass::cast((klassOop)obj)->remove_unshareable_info(); + } + + // Don't save compiler related special oops (shouldn't be any yet). + if (obj->is_methodData() || obj->is_compiledICHolder()) { + ShouldNotReachHere(); + } + } +}; + + +static bool mark_object(oop obj) { + if (obj != NULL && + !obj->is_shared() && + !obj->is_forwarded() && + !obj->is_gc_marked()) { + obj->set_mark(markOopDesc::prototype()->set_marked()); + return true; + } + + return false; +} + +// Closure: mark objects closure. + +class MarkObjectsOopClosure : public OopClosure { +public: + void do_oop(oop* pobj) { + mark_object(*pobj); + } +}; + + +class MarkObjectsSkippingKlassesOopClosure : public OopClosure { +public: + void do_oop(oop* pobj) { + oop obj = *pobj; + if (obj != NULL && + !obj->is_klass()) { + mark_object(obj); + } + } +}; + + +static void mark_object_recursive_skipping_klasses(oop obj) { + mark_object(obj); + if (obj != NULL) { + MarkObjectsSkippingKlassesOopClosure mark_all; + obj->oop_iterate(&mark_all); + } +} + + +// Closure: mark common read-only objects, excluding symbols + +class MarkCommonReadOnly : public ObjectClosure { +private: + MarkObjectsOopClosure mark_all; +public: + void do_object(oop obj) { + + // Mark all constMethod objects. + + if (obj->is_constMethod()) { + mark_object(obj); + mark_object(constMethodOop(obj)->stackmap_data()); + // Exception tables are needed by ci code during compilation. + mark_object(constMethodOop(obj)->exception_table()); + } + + // Mark objects referenced by klass objects which are read-only. + + else if (obj->is_klass()) { + Klass* k = Klass::cast((klassOop)obj); + mark_object(k->secondary_supers()); + + // The METHODS() OBJARRAYS CANNOT BE MADE READ-ONLY, even though + // it is never modified. Otherwise, they will be pre-marked; the + // GC marking phase will skip them; and by skipping them will fail + // to mark the methods objects referenced by the array. + + if (obj->blueprint()->oop_is_instanceKlass()) { + instanceKlass* ik = instanceKlass::cast((klassOop)obj); + mark_object(ik->method_ordering()); + mark_object(ik->local_interfaces()); + mark_object(ik->transitive_interfaces()); + mark_object(ik->fields()); + + mark_object(ik->class_annotations()); + + mark_object_recursive_skipping_klasses(ik->fields_annotations()); + mark_object_recursive_skipping_klasses(ik->methods_annotations()); + mark_object_recursive_skipping_klasses(ik->methods_parameter_annotations()); + mark_object_recursive_skipping_klasses(ik->methods_default_annotations()); + + typeArrayOop inner_classes = ik->inner_classes(); + if (inner_classes != NULL) { + mark_object(inner_classes); + } + } + } + } +}; + + +// Closure: mark common symbols + +class MarkCommonSymbols : public ObjectClosure { +private: + MarkObjectsOopClosure mark_all; +public: + void do_object(oop obj) { + + // Mark symbols refered to by method objects. + + if (obj->is_method()) { + methodOop m = methodOop(obj); + mark_object(m->name()); + mark_object(m->signature()); + } + + // Mark symbols referenced by klass objects which are read-only. + + else if (obj->is_klass()) { + + if (obj->blueprint()->oop_is_instanceKlass()) { + instanceKlass* ik = instanceKlass::cast((klassOop)obj); + mark_object(ik->name()); + mark_object(ik->generic_signature()); + mark_object(ik->source_file_name()); + mark_object(ik->source_debug_extension()); + + typeArrayOop inner_classes = ik->inner_classes(); + if (inner_classes != NULL) { + int length = inner_classes->length(); + for (int i = 0; + i < length; + i += instanceKlass::inner_class_next_offset) { + int ioff = i + instanceKlass::inner_class_inner_name_offset; + int index = inner_classes->ushort_at(ioff); + if (index != 0) { + mark_object(ik->constants()->symbol_at(index)); + } + } + } + ik->field_names_and_sigs_iterate(&mark_all); + } + } + + // Mark symbols referenced by other constantpool entries. + + if (obj->is_constantPool()) { + constantPoolOop(obj)->shared_symbols_iterate(&mark_all); + } + } +}; + + +// Closure: mark char arrays used by strings + +class MarkStringValues : public ObjectClosure { +private: + MarkObjectsOopClosure mark_all; +public: + void do_object(oop obj) { + + // Character arrays referenced by String objects are read-only. + + if (java_lang_String::is_instance(obj)) { + mark_object(java_lang_String::value(obj)); + } + } +}; + + +#ifdef DEBUG +// Closure: Check for objects left in the heap which have not been moved. + +class CheckRemainingObjects : public ObjectClosure { +private: + int count; + +public: + CheckRemainingObjects() { + count = 0; + } + + void do_object(oop obj) { + if (!obj->is_shared() && + !obj->is_forwarded()) { + ++count; + if (Verbose) { + tty->print("Unreferenced object: "); + obj->print_on(tty); + } + } + } + + void status() { + tty->print_cr("%d objects no longer referenced, not shared.", count); + } +}; +#endif + + +// Closure: Mark remaining objects read-write, except Strings. + +class MarkReadWriteObjects : public ObjectClosure { +private: + MarkObjectsOopClosure mark_objects; +public: + void do_object(oop obj) { + + // The METHODS() OBJARRAYS CANNOT BE MADE READ-ONLY, even though + // it is never modified. Otherwise, they will be pre-marked; the + // GC marking phase will skip them; and by skipping them will fail + // to mark the methods objects referenced by the array. + + if (obj->is_klass()) { + mark_object(obj); + Klass* k = klassOop(obj)->klass_part(); + mark_object(k->java_mirror()); + if (obj->blueprint()->oop_is_instanceKlass()) { + instanceKlass* ik = (instanceKlass*)k; + mark_object(ik->methods()); + mark_object(ik->constants()); + } + if (obj->blueprint()->oop_is_javaArray()) { + arrayKlass* ak = (arrayKlass*)k; + mark_object(ak->component_mirror()); + } + return; + } + + // Mark constantPool tags and the constantPoolCache. + + else if (obj->is_constantPool()) { + constantPoolOop pool = constantPoolOop(obj); + mark_object(pool->cache()); + pool->shared_tags_iterate(&mark_objects); + return; + } + + // Mark all method objects. + + if (obj->is_method()) { + mark_object(obj); + } + } +}; + + +// Closure: Mark String objects read-write. + +class MarkStringObjects : public ObjectClosure { +private: + MarkObjectsOopClosure mark_objects; +public: + void do_object(oop obj) { + + // Mark String objects referenced by constant pool entries. + + if (obj->is_constantPool()) { + constantPoolOop pool = constantPoolOop(obj); + pool->shared_strings_iterate(&mark_objects); + return; + } + } +}; + + +// Move objects matching specified type (ie. lock_bits) to the specified +// space. + +class MoveMarkedObjects : public ObjectClosure { +private: + OffsetTableContigSpace* _space; + bool _read_only; + +public: + MoveMarkedObjects(OffsetTableContigSpace* space, bool read_only) { + _space = space; + _read_only = read_only; + } + + void do_object(oop obj) { + if (obj->is_shared()) { + return; + } + if (obj->is_gc_marked() && obj->forwardee() == NULL) { + int s = obj->size(); + oop sh_obj = (oop)_space->allocate(s); + if (sh_obj == NULL) { + if (_read_only) { + warning("\nThe permanent generation read only space is not large " + "enough to \npreload requested classes. Use " + "-XX:SharedReadOnlySize= to increase \nthe initial " + "size of the read only space.\n"); + } else { + warning("\nThe permanent generation read write space is not large " + "enough to \npreload requested classes. Use " + "-XX:SharedReadWriteSize= to increase \nthe initial " + "size of the read write space.\n"); + } + exit(2); + } + if (PrintSharedSpaces && Verbose && WizardMode) { + tty->print_cr("\nMoveMarkedObjects: " PTR_FORMAT " -> " PTR_FORMAT " %s", obj, sh_obj, + (_read_only ? "ro" : "rw")); + } + Copy::aligned_disjoint_words((HeapWord*)obj, (HeapWord*)sh_obj, s); + obj->forward_to(sh_obj); + if (_read_only) { + // Readonly objects: set hash value to self pointer and make gc_marked. + sh_obj->forward_to(sh_obj); + } else { + sh_obj->init_mark(); + } + } + } +}; + +static void mark_and_move(oop obj, MoveMarkedObjects* move) { + if (mark_object(obj)) move->do_object(obj); +} + +enum order_policy { + OP_favor_startup = 0, + OP_balanced = 1, + OP_favor_runtime = 2 +}; + +static void mark_and_move_for_policy(order_policy policy, oop obj, MoveMarkedObjects* move) { + if (SharedOptimizeColdStartPolicy >= policy) mark_and_move(obj, move); +} + +class MarkAndMoveOrderedReadOnly : public ObjectClosure { +private: + MoveMarkedObjects *_move_ro; + +public: + MarkAndMoveOrderedReadOnly(MoveMarkedObjects *move_ro) : _move_ro(move_ro) {} + + void do_object(oop obj) { + if (obj->is_klass() && obj->blueprint()->oop_is_instanceKlass()) { + instanceKlass* ik = instanceKlass::cast((klassOop)obj); + int i; + + mark_and_move_for_policy(OP_favor_startup, ik->name(), _move_ro); + + if (ik->super() != NULL) { + do_object(ik->super()); + } + + objArrayOop interfaces = ik->local_interfaces(); + mark_and_move_for_policy(OP_favor_startup, interfaces, _move_ro); + for(i = 0; i < interfaces->length(); i++) { + klassOop k = klassOop(interfaces->obj_at(i)); + mark_and_move_for_policy(OP_favor_startup, k->klass_part()->name(), _move_ro); + do_object(k); + } + + objArrayOop methods = ik->methods(); + for(i = 0; i < methods->length(); i++) { + methodOop m = methodOop(methods->obj_at(i)); + mark_and_move_for_policy(OP_favor_startup, m->constMethod(), _move_ro); + mark_and_move_for_policy(OP_favor_runtime, m->constMethod()->exception_table(), _move_ro); + mark_and_move_for_policy(OP_favor_runtime, m->constMethod()->stackmap_data(), _move_ro); + + // We don't move the name symbolOop here because it may invalidate + // method ordering, which is dependent on the address of the name + // symbolOop. It will get promoted later with the other symbols. + // Method name is rarely accessed during classloading anyway. + // mark_and_move_for_policy(OP_balanced, m->name(), _move_ro); + + mark_and_move_for_policy(OP_favor_startup, m->signature(), _move_ro); + } + + mark_and_move_for_policy(OP_favor_startup, ik->transitive_interfaces(), _move_ro); + mark_and_move_for_policy(OP_favor_startup, ik->fields(), _move_ro); + + mark_and_move_for_policy(OP_favor_runtime, ik->secondary_supers(), _move_ro); + mark_and_move_for_policy(OP_favor_runtime, ik->method_ordering(), _move_ro); + mark_and_move_for_policy(OP_favor_runtime, ik->class_annotations(), _move_ro); + mark_and_move_for_policy(OP_favor_runtime, ik->fields_annotations(), _move_ro); + mark_and_move_for_policy(OP_favor_runtime, ik->methods_annotations(), _move_ro); + mark_and_move_for_policy(OP_favor_runtime, ik->methods_parameter_annotations(), _move_ro); + mark_and_move_for_policy(OP_favor_runtime, ik->methods_default_annotations(), _move_ro); + mark_and_move_for_policy(OP_favor_runtime, ik->inner_classes(), _move_ro); + mark_and_move_for_policy(OP_favor_runtime, ik->secondary_supers(), _move_ro); + } + } +}; + +class MarkAndMoveOrderedReadWrite: public ObjectClosure { +private: + MoveMarkedObjects *_move_rw; + +public: + MarkAndMoveOrderedReadWrite(MoveMarkedObjects *move_rw) : _move_rw(move_rw) {} + + void do_object(oop obj) { + if (obj->is_klass() && obj->blueprint()->oop_is_instanceKlass()) { + instanceKlass* ik = instanceKlass::cast((klassOop)obj); + int i; + + mark_and_move_for_policy(OP_favor_startup, ik->as_klassOop(), _move_rw); + + if (ik->super() != NULL) { + do_object(ik->super()); + } + + objArrayOop interfaces = ik->local_interfaces(); + for(i = 0; i < interfaces->length(); i++) { + klassOop k = klassOop(interfaces->obj_at(i)); + mark_and_move_for_policy(OP_favor_startup, k, _move_rw); + do_object(k); + } + + objArrayOop methods = ik->methods(); + mark_and_move_for_policy(OP_favor_startup, methods, _move_rw); + for(i = 0; i < methods->length(); i++) { + methodOop m = methodOop(methods->obj_at(i)); + mark_and_move_for_policy(OP_favor_startup, m, _move_rw); + mark_and_move_for_policy(OP_favor_startup, ik->constants(), _move_rw); // idempotent + mark_and_move_for_policy(OP_balanced, ik->constants()->cache(), _move_rw); // idempotent + mark_and_move_for_policy(OP_balanced, ik->constants()->tags(), _move_rw); // idempotent + } + + mark_and_move_for_policy(OP_favor_startup, ik->as_klassOop()->klass(), _move_rw); + mark_and_move_for_policy(OP_favor_startup, ik->constants()->klass(), _move_rw); + + // Although Java mirrors are marked in MarkReadWriteObjects, + // apparently they were never moved into shared spaces since + // MoveMarkedObjects skips marked instance oops. This may + // be a bug in the original implementation or simply the vestige + // of an abandoned experiment. Nevertheless we leave a hint + // here in case this capability is ever correctly implemented. + // + // mark_and_move_for_policy(OP_favor_runtime, ik->java_mirror(), _move_rw); + } + } + +}; + +// Adjust references in oops to refer to shared spaces. + +class ResolveForwardingClosure: public OopClosure { +public: + void do_oop(oop* p) { + oop obj = *p; + if (!obj->is_shared()) { + if (obj != NULL) { + oop f = obj->forwardee(); + guarantee(f->is_shared(), "Oop doesn't refer to shared space."); + *p = f; + } + } + } +}; + + +void sort_methods(instanceKlass* ik, TRAPS) { + klassOop super = ik->super(); + if (super != NULL) { + sort_methods(instanceKlass::cast(super), THREAD); + } + + // The methods array must be ordered by symbolOop address. (See + // classFileParser.cpp where methods in a class are originally + // sorted.) Since objects have just be reordered, this must be + // corrected. + methodOopDesc::sort_methods(ik->methods(), + ik->methods_annotations(), + ik->methods_parameter_annotations(), + ik->methods_default_annotations(), + true /* idempotent, slow */); + + // Itable indices are calculated based on methods array order + // (see klassItable::compute_itable_index()). Must reinitialize. + // We assume that since checkconstraints is false, this method + // cannot throw an exception. An exception here would be + // problematic since this is the VMThread, not a JavaThread. + ik->itable()->initialize_itable(false, THREAD); +} + +// Sort methods if the oop is an instanceKlass. + +class SortMethodsClosure: public ObjectClosure { +private: + Thread* _thread; + +public: + SortMethodsClosure(Thread* thread) : _thread(thread) {} + + void do_object(oop obj) { + // instanceKlass objects need some adjustment. + if (obj->blueprint()->oop_is_instanceKlass()) { + instanceKlass* ik = instanceKlass::cast((klassOop)obj); + + sort_methods(ik, _thread); + } + } +}; + + +// Adjust references in oops to refer to shared spaces. + +class PatchOopsClosure: public ObjectClosure { +private: + Thread* _thread; + ResolveForwardingClosure resolve; + +public: + PatchOopsClosure(Thread* thread) : _thread(thread) {} + + void do_object(oop obj) { + obj->oop_iterate_header(&resolve); + obj->oop_iterate(&resolve); + + assert(obj->klass()->is_shared(), "Klass not pointing into shared space."); + + // If the object is a Java object or class which might (in the + // future) contain a reference to a young gen object, add it to the + // list. + + if (obj->is_klass() || obj->is_instance()) { + if (obj->is_klass() || + obj->is_a(SystemDictionary::class_klass()) || + obj->is_a(SystemDictionary::throwable_klass())) { + // Do nothing + } + else if (obj->is_a(SystemDictionary::string_klass())) { + // immutable objects. + } else { + // someone added an object we hadn't accounted for. + ShouldNotReachHere(); + } + } + } +}; + + +// Empty the young and old generations. + +class ClearSpaceClosure : public SpaceClosure { +public: + void do_space(Space* s) { + s->clear(); + } +}; + + +// Closure for serializing initialization data out to a data area to be +// written to the shared file. + +class WriteClosure : public SerializeOopClosure { +private: + oop* top; + char* end; + + void out_of_space() { + warning("\nThe shared miscellaneous data space is not large " + "enough to \npreload requested classes. Use " + "-XX:SharedMiscDataSize= to increase \nthe initial " + "size of the miscellaneous data space.\n"); + exit(2); + } + + + inline void check_space() { + if ((char*)top + sizeof(oop) > end) { + out_of_space(); + } + } + + +public: + WriteClosure(char* md_top, char* md_end) { + top = (oop*)md_top; + end = md_end; + } + + char* get_top() { return (char*)top; } + + void do_oop(oop* p) { + check_space(); + oop obj = *p; + assert(obj->is_oop_or_null(), "invalid oop"); + assert(obj == NULL || obj->is_shared(), + "Oop in shared space not pointing into shared space."); + *top = obj; + ++top; + } + + void do_int(int* p) { + check_space(); + *top = (oop)(intptr_t)*p; + ++top; + } + + void do_size_t(size_t* p) { + check_space(); + *top = (oop)(intptr_t)*p; + ++top; + } + + void do_ptr(void** p) { + check_space(); + *top = (oop)*p; + ++top; + } + + void do_ptr(HeapWord** p) { do_ptr((void **) p); } + + void do_tag(int tag) { + check_space(); + *top = (oop)(intptr_t)tag; + ++top; + } + + void do_region(u_char* start, size_t size) { + if ((char*)top + size > end) { + out_of_space(); + } + assert((intptr_t)start % sizeof(oop) == 0, "bad alignment"); + assert(size % sizeof(oop) == 0, "bad size"); + do_tag((int)size); + while (size > 0) { + *top = *(oop*)start; + ++top; + start += sizeof(oop); + size -= sizeof(oop); + } + } + + bool reading() const { return false; } +}; + + +class ResolveConstantPoolsClosure : public ObjectClosure { +private: + TRAPS; +public: + ResolveConstantPoolsClosure(Thread *t) { + __the_thread__ = t; + } + void do_object(oop obj) { + if (obj->is_constantPool()) { + constantPoolOop cpool = (constantPoolOop)obj; + int unresolved = cpool->pre_resolve_shared_klasses(THREAD); + } + } +}; + + +// Print a summary of the contents of the read/write spaces to help +// identify objects which might be able to be made read-only. At this +// point, the objects have been written, and we can trash them as +// needed. + +static void print_contents() { + if (PrintSharedSpaces) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + CompactingPermGenGen* gen = (CompactingPermGenGen*)gch->perm_gen(); + + // High level summary of the read-only space: + + ClassifyObjectClosure coc; + tty->cr(); tty->print_cr("ReadOnly space:"); + gen->ro_space()->object_iterate(&coc); + coc.print(); + + // High level summary of the read-write space: + + coc.reset(); + tty->cr(); tty->print_cr("ReadWrite space:"); + gen->rw_space()->object_iterate(&coc); + coc.print(); + + // Reset counters + + ClearAllocCountClosure cacc; + gen->ro_space()->object_iterate(&cacc); + gen->rw_space()->object_iterate(&cacc); + coc.reset(); + + // Lower level summary of the read-only space: + + gen->ro_space()->object_iterate(&coc); + tty->cr(); tty->print_cr("ReadOnly space:"); + ClassifyInstanceKlassClosure cikc; + gen->rw_space()->object_iterate(&cikc); + cikc.print(); + + // Reset counters + + gen->ro_space()->object_iterate(&cacc); + gen->rw_space()->object_iterate(&cacc); + coc.reset(); + + // Lower level summary of the read-write space: + + gen->rw_space()->object_iterate(&coc); + cikc.reset(); + tty->cr(); tty->print_cr("ReadWrite space:"); + gen->rw_space()->object_iterate(&cikc); + cikc.print(); + } +} + + +// Patch C++ vtable pointer in klass oops. + +// Klass objects contain references to c++ vtables in the JVM library. +// Fix them to point to our constructed vtables. However, don't iterate +// across the space while doing this, as that causes the vtables to be +// patched, undoing our useful work. Instead, iterate to make a list, +// then use the list to do the fixing. + +class PatchKlassVtables: public ObjectClosure { +private: + void* _vtbl_ptr; + VirtualSpace* _md_vs; + GrowableArray* _klass_objects; + +public: + + PatchKlassVtables(void* vtbl_ptr, VirtualSpace* md_vs) { + _vtbl_ptr = vtbl_ptr; + _md_vs = md_vs; + _klass_objects = new GrowableArray(); + } + + + void do_object(oop obj) { + if (obj->is_klass()) { + _klass_objects->append(klassOop(obj)); + } + } + + + void patch(void** vtbl_list, int vtbl_list_size) { + for (int i = 0; i < _klass_objects->length(); ++i) { + klassOop obj = (klassOop)_klass_objects->at(i); + Klass* k = obj->klass_part(); + void* v = *(void**)k; + + int n; + for (n = 0; n < vtbl_list_size; ++n) { + *(void**)k = NULL; + if (vtbl_list[n] == v) { + *(void**)k = (void**)_vtbl_ptr + + (n * CompactingPermGenGen::num_virtuals); + break; + } + } + guarantee(n < vtbl_list_size, "unable to find matching vtbl pointer"); + } + } +}; + + +// Populate the shared space. + +class VM_PopulateDumpSharedSpace: public VM_Operation { +private: + GrowableArray *_class_promote_order; + OffsetTableContigSpace* _ro_space; + OffsetTableContigSpace* _rw_space; + VirtualSpace* _md_vs; + VirtualSpace* _mc_vs; + +public: + VM_PopulateDumpSharedSpace(GrowableArray *class_promote_order, + OffsetTableContigSpace* ro_space, + OffsetTableContigSpace* rw_space, + VirtualSpace* md_vs, VirtualSpace* mc_vs) { + _class_promote_order = class_promote_order; + _ro_space = ro_space; + _rw_space = rw_space; + _md_vs = md_vs; + _mc_vs = mc_vs; + } + + VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; } + void doit() { + Thread* THREAD = VMThread::vm_thread(); + NOT_PRODUCT(SystemDictionary::verify();) + // The following guarantee is meant to ensure that no loader constraints + // exist yet, since the constraints table is not shared. This becomes + // more important now that we don't re-initialize vtables/itables for + // shared classes at runtime, where constraints were previously created. + guarantee(SystemDictionary::constraints()->number_of_entries() == 0, + "loader constraints are not saved"); + GenCollectedHeap* gch = GenCollectedHeap::heap(); + + // At this point, many classes have been loaded. + + // Update all the fingerprints in the shared methods. + + tty->print("Calculating fingerprints ... "); + FingerprintMethodsClosure fpmc; + gch->object_iterate(&fpmc); + tty->print_cr("done. "); + + // Remove all references outside the heap. + + tty->print("Removing unshareable information ... "); + RemoveUnshareableInfoClosure ruic; + gch->object_iterate(&ruic); + tty->print_cr("done. "); + + // Move the objects in three passes. + + MarkObjectsOopClosure mark_all; + MarkCommonReadOnly mark_common_ro; + MarkCommonSymbols mark_common_symbols; + MarkStringValues mark_string_values; + MarkReadWriteObjects mark_rw; + MarkStringObjects mark_strings; + MoveMarkedObjects move_ro(_ro_space, true); + MoveMarkedObjects move_rw(_rw_space, false); + + // The SharedOptimizeColdStart VM option governs the new layout + // algorithm for promoting classes into the shared archive. + // The general idea is to minimize cold start time by laying + // out the objects in the order they are accessed at startup time. + // By doing this we are trying to eliminate out-of-order accesses + // in the shared archive. This benefits cold startup time by making + // disk reads as sequential as possible during class loading and + // bootstrapping activities. There may also be a small secondary + // effect of better "packing" of more commonly used data on a smaller + // number of pages, although no direct benefit has been measured from + // this effect. + // + // At the class level of granularity, the promotion order is dictated + // by the classlist file whose generation is discussed elsewhere. + // + // At smaller granularity, optimal ordering was determined by an + // offline analysis of object access order in the shared archive. + // The dbx watchpoint facility, combined with SA post-processing, + // was used to observe common access patterns primarily during + // classloading. This information was used to craft the promotion + // order seen in the following closures. + // + // The observed access order is mostly governed by what happens + // in SystemDictionary::load_shared_class(). NOTE WELL - care + // should be taken when making changes to this method, because it + // may invalidate assumptions made about access order! + // + // (Ideally, there would be a better way to manage changes to + // the access order. Unfortunately a generic in-VM solution for + // dynamically observing access order and optimizing shared + // archive layout is pretty difficult. We go with the static + // analysis because the code is fairly mature at this point + // and we're betting that the access order won't change much.) + + MarkAndMoveOrderedReadOnly mark_and_move_ordered_ro(&move_ro); + MarkAndMoveOrderedReadWrite mark_and_move_ordered_rw(&move_rw); + + // Phase 1a: move commonly used read-only objects to the read-only space. + + if (SharedOptimizeColdStart) { + tty->print("Moving pre-ordered read-only objects to shared space at " PTR_FORMAT " ... ", + _ro_space->top()); + for (int i = 0; i < _class_promote_order->length(); i++) { + oop obj = _class_promote_order->at(i); + mark_and_move_ordered_ro.do_object(obj); + } + tty->print_cr("done. "); + } + + tty->print("Moving read-only objects to shared space at " PTR_FORMAT " ... ", + _ro_space->top()); + gch->object_iterate(&mark_common_ro); + gch->object_iterate(&move_ro); + tty->print_cr("done. "); + + // Phase 1b: move commonly used symbols to the read-only space. + + tty->print("Moving common symbols to shared space at " PTR_FORMAT " ... ", + _ro_space->top()); + gch->object_iterate(&mark_common_symbols); + gch->object_iterate(&move_ro); + tty->print_cr("done. "); + + // Phase 1c: move remaining symbols to the read-only space + // (e.g. String initializers). + + tty->print("Moving remaining symbols to shared space at " PTR_FORMAT " ... ", + _ro_space->top()); + vmSymbols::oops_do(&mark_all, true); + gch->object_iterate(&move_ro); + tty->print_cr("done. "); + + // Phase 1d: move String character arrays to the read-only space. + + tty->print("Moving string char arrays to shared space at " PTR_FORMAT " ... ", + _ro_space->top()); + gch->object_iterate(&mark_string_values); + gch->object_iterate(&move_ro); + tty->print_cr("done. "); + + // Phase 2: move all remaining symbols to the read-only space. The + // remaining symbols are assumed to be string initializers no longer + // referenced. + + void* extra_symbols = _ro_space->top(); + tty->print("Moving additional symbols to shared space at " PTR_FORMAT " ... ", + _ro_space->top()); + SymbolTable::oops_do(&mark_all); + gch->object_iterate(&move_ro); + tty->print_cr("done. "); + tty->print_cr("Read-only space ends at " PTR_FORMAT ", %d bytes.", + _ro_space->top(), _ro_space->used()); + + // Phase 3: move read-write objects to the read-write space, except + // Strings. + + if (SharedOptimizeColdStart) { + tty->print("Moving pre-ordered read-write objects to shared space at " PTR_FORMAT " ... ", + _rw_space->top()); + for (int i = 0; i < _class_promote_order->length(); i++) { + oop obj = _class_promote_order->at(i); + mark_and_move_ordered_rw.do_object(obj); + } + tty->print_cr("done. "); + } + tty->print("Moving read-write objects to shared space at " PTR_FORMAT " ... ", + _rw_space->top()); + Universe::oops_do(&mark_all, true); + SystemDictionary::oops_do(&mark_all); + oop tmp = Universe::arithmetic_exception_instance(); + mark_object(java_lang_Throwable::message(tmp)); + gch->object_iterate(&mark_rw); + gch->object_iterate(&move_rw); + tty->print_cr("done. "); + + // Phase 4: move String objects to the read-write space. + + tty->print("Moving String objects to shared space at " PTR_FORMAT " ... ", + _rw_space->top()); + StringTable::oops_do(&mark_all); + gch->object_iterate(&mark_strings); + gch->object_iterate(&move_rw); + tty->print_cr("done. "); + tty->print_cr("Read-write space ends at " PTR_FORMAT ", %d bytes.", + _rw_space->top(), _rw_space->used()); + +#ifdef DEBUG + // Check: scan for objects which were not moved. + + CheckRemainingObjects check_objects; + gch->object_iterate(&check_objects); + check_objects.status(); +#endif + + // Resolve forwarding in objects and saved C++ structures + tty->print("Updating references to shared objects ... "); + ResolveForwardingClosure resolve; + Universe::oops_do(&resolve); + SystemDictionary::oops_do(&resolve); + StringTable::oops_do(&resolve); + SymbolTable::oops_do(&resolve); + vmSymbols::oops_do(&resolve); + + // Set up the share data and shared code segments. + + char* md_top = _md_vs->low(); + char* md_end = _md_vs->high(); + char* mc_top = _mc_vs->low(); + char* mc_end = _mc_vs->high(); + + // Reserve space for the list of klassOops whose vtables are used + // for patching others as needed. + + void** vtbl_list = (void**)md_top; + int vtbl_list_size = CompactingPermGenGen::vtbl_list_size; + Universe::init_self_patching_vtbl_list(vtbl_list, vtbl_list_size); + + md_top += vtbl_list_size * sizeof(void*); + void* vtable = md_top; + + // Reserve space for a new dummy vtable for klass objects in the + // heap. Generate self-patching vtable entries. + + CompactingPermGenGen::generate_vtable_methods(vtbl_list, + &vtable, + &md_top, md_end, + &mc_top, mc_end); + + // Fix (forward) all of the references in these shared objects (which + // are required to point ONLY to objects in the shared spaces). + // Also, create a list of all objects which might later contain a + // reference to a younger generation object. + + CompactingPermGenGen* gen = (CompactingPermGenGen*)gch->perm_gen(); + PatchOopsClosure patch(THREAD); + gen->ro_space()->object_iterate(&patch); + gen->rw_space()->object_iterate(&patch); + + // Previously method sorting was done concurrently with forwarding + // pointer resolution in the shared spaces. This imposed an ordering + // restriction in that methods were required to be promoted/patched + // before their holder classes. (Because constant pool pointers in + // methodKlasses are required to be resolved before their holder class + // is visited for sorting, otherwise methods are sorted by incorrect, + // pre-forwarding addresses.) + // + // Now, we reorder methods as a separate step after ALL forwarding + // pointer resolution, so that methods can be promoted in any order + // with respect to their holder classes. + + SortMethodsClosure sort(THREAD); + gen->ro_space()->object_iterate(&sort); + gen->rw_space()->object_iterate(&sort); + tty->print_cr("done. "); + tty->cr(); + + // Reorder the system dictionary. (Moving the symbols opps affects + // how the hash table indices are calculated.) + + SystemDictionary::reorder_dictionary(); + + // Empty the non-shared heap (because most of the objects were + // copied out, and the remainder cannot be considered valid oops). + + ClearSpaceClosure csc; + for (int i = 0; i < gch->n_gens(); ++i) { + gch->get_gen(i)->space_iterate(&csc); + } + csc.do_space(gen->the_space()); + NOT_PRODUCT(SystemDictionary::verify();) + + // Copy the String table, the symbol table, and the system + // dictionary to the shared space in usable form. Copy the hastable + // buckets first [read-write], then copy the linked lists of entries + // [read-only]. + + SymbolTable::reverse(extra_symbols); + NOT_PRODUCT(SymbolTable::verify()); + SymbolTable::copy_buckets(&md_top, md_end); + + StringTable::reverse(); + NOT_PRODUCT(StringTable::verify()); + StringTable::copy_buckets(&md_top, md_end); + + SystemDictionary::reverse(); + SystemDictionary::copy_buckets(&md_top, md_end); + + ClassLoader::verify(); + ClassLoader::copy_package_info_buckets(&md_top, md_end); + ClassLoader::verify(); + + SymbolTable::copy_table(&md_top, md_end); + StringTable::copy_table(&md_top, md_end); + SystemDictionary::copy_table(&md_top, md_end); + ClassLoader::verify(); + ClassLoader::copy_package_info_table(&md_top, md_end); + ClassLoader::verify(); + + // Print debug data. + + if (PrintSharedSpaces) { + const char* fmt = "%s space: " PTR_FORMAT " out of " PTR_FORMAT " bytes allocated at " PTR_FORMAT "."; + tty->print_cr(fmt, "ro", _ro_space->used(), _ro_space->capacity(), + _ro_space->bottom()); + tty->print_cr(fmt, "rw", _rw_space->used(), _rw_space->capacity(), + _rw_space->bottom()); + } + + // Write the oop data to the output array. + + WriteClosure wc(md_top, md_end); + CompactingPermGenGen::serialize_oops(&wc); + md_top = wc.get_top(); + + // Update the vtable pointers in all of the Klass objects in the + // heap. They should point to newly generated vtable. + + PatchKlassVtables pkvt(vtable, _md_vs); + _rw_space->object_iterate(&pkvt); + pkvt.patch(vtbl_list, vtbl_list_size); + + char* saved_vtbl = (char*)malloc(vtbl_list_size * sizeof(void*)); + memmove(saved_vtbl, vtbl_list, vtbl_list_size * sizeof(void*)); + memset(vtbl_list, 0, vtbl_list_size * sizeof(void*)); + + // Create and write the archive file that maps the shared spaces. + + FileMapInfo* mapinfo = new FileMapInfo(); + mapinfo->populate_header(gch->gen_policy()->max_alignment()); + + // Pass 1 - update file offsets in header. + mapinfo->write_header(); + mapinfo->write_space(CompactingPermGenGen::ro, _ro_space, true); + _ro_space->set_saved_mark(); + mapinfo->write_space(CompactingPermGenGen::rw, _rw_space, false); + _rw_space->set_saved_mark(); + mapinfo->write_region(CompactingPermGenGen::md, _md_vs->low(), + md_top - _md_vs->low(), SharedMiscDataSize, + false, false); + mapinfo->write_region(CompactingPermGenGen::mc, _mc_vs->low(), + mc_top - _mc_vs->low(), SharedMiscCodeSize, + true, true); + + // Pass 2 - write data. + mapinfo->open_for_write(); + mapinfo->write_header(); + mapinfo->write_space(CompactingPermGenGen::ro, _ro_space, true); + mapinfo->write_space(CompactingPermGenGen::rw, _rw_space, false); + mapinfo->write_region(CompactingPermGenGen::md, _md_vs->low(), + md_top - _md_vs->low(), SharedMiscDataSize, + false, false); + mapinfo->write_region(CompactingPermGenGen::mc, _mc_vs->low(), + mc_top - _mc_vs->low(), SharedMiscCodeSize, + true, true); + mapinfo->close(); + + // Summarize heap. + memmove(vtbl_list, saved_vtbl, vtbl_list_size * sizeof(void*)); + print_contents(); + } +}; // class VM_PopulateDumpSharedSpace + + +// Populate the shared spaces and dump to a file. + +jint CompactingPermGenGen::dump_shared(GrowableArray* class_promote_order, TRAPS) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + + // Calculate hash values for all of the (interned) strings to avoid + // writes to shared pages in the future. + + tty->print("Calculating hash values for String objects .. "); + StringHashCodeClosure shcc(THREAD); + StringTable::oops_do(&shcc); + tty->print_cr("done. "); + + CompactingPermGenGen* gen = (CompactingPermGenGen*)gch->perm_gen(); + VM_PopulateDumpSharedSpace op(class_promote_order, + gen->ro_space(), gen->rw_space(), + gen->md_space(), gen->mc_space()); + VMThread::execute(&op); + return JNI_OK; +} + + +class LinkClassesClosure : public ObjectClosure { + private: + Thread* THREAD; + + public: + LinkClassesClosure(Thread* thread) : THREAD(thread) {} + + void do_object(oop obj) { + if (obj->is_klass()) { + Klass* k = Klass::cast((klassOop) obj); + if (k->oop_is_instance()) { + instanceKlass* ik = (instanceKlass*) k; + // Link the class to cause the bytecodes to be rewritten and the + // cpcache to be created. + if (ik->get_init_state() < instanceKlass::linked) { + ik->link_class(THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "exception in class rewriting"); + } + + // Create String objects from string initializer symbols. + ik->constants()->resolve_string_constants(THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "exception resolving string constants"); + } + } + } +}; + + +// Support for a simple checksum of the contents of the class list +// file to prevent trivial tampering. The algorithm matches that in +// the MakeClassList program used by the J2SE build process. +#define JSUM_SEED ((jlong)CONST64(0xcafebabebabecafe)) +static jlong +jsum(jlong start, const char *buf, const int len) +{ + jlong h = start; + char *p = (char *)buf, *e = p + len; + while (p < e) { + char c = *p++; + if (c <= ' ') { + /* Skip spaces and control characters */ + continue; + } + h = 31 * h + c; + } + return h; +} + + + + + +// Preload classes from a list, populate the shared spaces and dump to a +// file. + +void GenCollectedHeap::preload_and_dump(TRAPS) { + TraceTime timer("Dump Shared Spaces", TraceStartupTime); + ResourceMark rm; + + // Preload classes to be shared. + // Should use some hpi:: method rather than fopen() here. aB. + // Construct the path to the class list (in jre/lib) + // Walk up two directories from the location of the VM and + // optionally tack on "lib" (depending on platform) + char class_list_path[JVM_MAXPATHLEN]; + os::jvm_path(class_list_path, sizeof(class_list_path)); + for (int i = 0; i < 3; i++) { + char *end = strrchr(class_list_path, *os::file_separator()); + if (end != NULL) *end = '\0'; + } + int class_list_path_len = (int)strlen(class_list_path); + if (class_list_path_len >= 3) { + if (strcmp(class_list_path + class_list_path_len - 3, "lib") != 0) { + strcat(class_list_path, os::file_separator()); + strcat(class_list_path, "lib"); + } + } + strcat(class_list_path, os::file_separator()); + strcat(class_list_path, "classlist"); + + FILE* file = fopen(class_list_path, "r"); + if (file != NULL) { + jlong computed_jsum = JSUM_SEED; + jlong file_jsum = 0; + + char class_name[256]; + int class_count = 0; + GenCollectedHeap* gch = GenCollectedHeap::heap(); + gch->_preloading_shared_classes = true; + GrowableArray* class_promote_order = new GrowableArray(); + + // Preload (and intern) strings which will be used later. + + StringTable::intern("main", THREAD); + StringTable::intern("([Ljava/lang/String;)V", THREAD); + StringTable::intern("Ljava/lang/Class;", THREAD); + + StringTable::intern("I", THREAD); // Needed for StringBuffer persistence? + StringTable::intern("Z", THREAD); // Needed for StringBuffer persistence? + + // sun.io.Converters + static const char obj_array_sig[] = "[[Ljava/lang/Object;"; + SymbolTable::lookup(obj_array_sig, (int)strlen(obj_array_sig), THREAD); + + // java.util.HashMap + static const char map_entry_array_sig[] = "[Ljava/util/Map$Entry;"; + SymbolTable::lookup(map_entry_array_sig, (int)strlen(map_entry_array_sig), + THREAD); + + tty->print("Loading classes to share ... "); + while ((fgets(class_name, sizeof class_name, file)) != NULL) { + if (*class_name == '#') { + jint fsh, fsl; + if (sscanf(class_name, "# %8x%8x\n", &fsh, &fsl) == 2) { + file_jsum = ((jlong)(fsh) << 32) | (fsl & 0xffffffff); + } + + continue; + } + // Remove trailing newline + size_t name_len = strlen(class_name); + class_name[name_len-1] = '\0'; + + computed_jsum = jsum(computed_jsum, class_name, (const int)name_len - 1); + + // Got a class name - load it. + symbolHandle class_name_symbol = oopFactory::new_symbol(class_name, + THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "Exception creating a symbol."); + klassOop klass = SystemDictionary::resolve_or_null(class_name_symbol, + THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "Exception resolving a class."); + if (klass != NULL) { + if (PrintSharedSpaces) { + tty->print_cr("Shared spaces preloaded: %s", class_name); + } + + + instanceKlass* ik = instanceKlass::cast(klass); + + // Should be class load order as per -XX:+TraceClassLoadingPreorder + class_promote_order->append(ik->as_klassOop()); + + // Link the class to cause the bytecodes to be rewritten and the + // cpcache to be created. The linking is done as soon as classes + // are loaded in order that the related data structures (klass, + // cpCache, Sting constants) are located together. + + if (ik->get_init_state() < instanceKlass::linked) { + ik->link_class(THREAD); + guarantee(!(HAS_PENDING_EXCEPTION), "exception in class rewriting"); + } + + // Create String objects from string initializer symbols. + + ik->constants()->resolve_string_constants(THREAD); + + class_count++; + } else { + if (PrintSharedSpaces) { + tty->cr(); + tty->print_cr(" Preload failed: %s", class_name); + } + } + file_jsum = 0; // Checksum must be on last line of file + } + if (computed_jsum != file_jsum) { + tty->cr(); + tty->print_cr("Preload failed: checksum of class list was incorrect."); + exit(1); + } + + tty->print_cr("done. "); + + if (PrintSharedSpaces) { + tty->print_cr("Shared spaces: preloaded %d classes", class_count); + } + + // Rewrite and unlink classes. + tty->print("Rewriting and unlinking classes ... "); + // Make heap parsable + ensure_parsability(false); // arg is actually don't care + + // Link any classes which got missed. (It's not quite clear why + // they got missed.) This iteration would be unsafe if we weren't + // single-threaded at this point; however we can't do it on the VM + // thread because it requires object allocation. + LinkClassesClosure lcc(Thread::current()); + object_iterate(&lcc); + tty->print_cr("done. "); + + // Create and dump the shared spaces. + jint err = CompactingPermGenGen::dump_shared(class_promote_order, THREAD); + if (err != JNI_OK) { + fatal("Dumping shared spaces failed."); + } + + } else { + char errmsg[JVM_MAXPATHLEN]; + hpi::lasterror(errmsg, JVM_MAXPATHLEN); + tty->print_cr("Loading classlist failed: %s", errmsg); + exit(1); + } + + // Since various initialization steps have been undone by this process, + // it is not reasonable to continue running a java process. + exit(0); +} diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp new file mode 100644 index 00000000000..b68f94c0aeb --- /dev/null +++ b/hotspot/src/share/vm/memory/filemap.cpp @@ -0,0 +1,522 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_filemap.cpp.incl" +# include +# include + +#ifndef O_BINARY // if defined (Win32) use binary files. +#define O_BINARY 0 // otherwise do nothing. +#endif + + +extern address JVM_FunctionAtStart(); +extern address JVM_FunctionAtEnd(); + +// Complain and stop. All error conditions occuring during the writing of +// an archive file should stop the process. Unrecoverable errors during +// the reading of the archive file should stop the process. + +static void fail(const char *msg, va_list ap) { + // This occurs very early during initialization: tty is not initialized. + jio_fprintf(defaultStream::error_stream(), + "An error has occured while processing the" + " shared archive file.\n"); + jio_vfprintf(defaultStream::error_stream(), msg, ap); + jio_fprintf(defaultStream::error_stream(), "\n"); + vm_exit_during_initialization("Unable to use shared archive.", NULL); +} + + +void FileMapInfo::fail_stop(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + fail(msg, ap); // Never returns. + va_end(ap); // for completeness. +} + + +// Complain and continue. Recoverable errors during the reading of the +// archive file may continue (with sharing disabled). +// +// If we continue, then disable shared spaces and close the file. + +void FileMapInfo::fail_continue(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + if (RequireSharedSpaces) { + fail(msg, ap); + } + va_end(ap); + UseSharedSpaces = false; + close(); +} + + +// Fill in the fileMapInfo structure with data about this VM instance. + +void FileMapInfo::populate_header(size_t alignment) { + _header._magic = 0xf00baba2; + _header._version = _current_version; + _header._alignment = alignment; + + // The following fields are for sanity checks for whether this archive + // will function correctly with this JVM and the bootclasspath it's + // invoked with. + + // JVM version string ... changes on each build. + const char *vm_version = VM_Version::internal_vm_info_string(); + if (strlen(vm_version) < (JVM_IDENT_MAX-1)) { + strcpy(_header._jvm_ident, vm_version); + } else { + fail_stop("JVM Ident field for shared archive is too long" + " - truncated to <%s>", _header._jvm_ident); + } + + // Build checks on classpath and jar files + _header._num_jars = 0; + ClassPathEntry *cpe = ClassLoader::classpath_entry(0); + for ( ; cpe != NULL; cpe = cpe->next()) { + + if (cpe->is_jar_file()) { + if (_header._num_jars >= JVM_SHARED_JARS_MAX) { + fail_stop("Too many jar files to share.", NULL); + } + + // Jar file - record timestamp and file size. + struct stat st; + const char *path = cpe->name(); + if (os::stat(path, &st) != 0) { + // If we can't access a jar file in the boot path, then we can't + // make assumptions about where classes get loaded from. + fail_stop("Unable to open jar file %s.", path); + } + _header._jar[_header._num_jars]._timestamp = st.st_mtime; + _header._jar[_header._num_jars]._filesize = st.st_size; + _header._num_jars++; + } else { + + // If directories appear in boot classpath, they must be empty to + // avoid having to verify each individual class file. + const char* name = ((ClassPathDirEntry*)cpe)->name(); + if (!os::dir_is_empty(name)) { + fail_stop("Boot classpath directory %s is not empty.", name); + } + } + } +} + + +// Read the FileMapInfo information from the file. + +bool FileMapInfo::init_from_file(int fd) { + + size_t n = read(fd, &_header, sizeof(struct FileMapHeader)); + if (n != sizeof(struct FileMapHeader)) { + fail_continue("Unable to read the file header."); + return false; + } + if (_header._version != current_version()) { + fail_continue("The shared archive file has the wrong version."); + return false; + } + _file_offset = (long)n; + return true; +} + + +// Read the FileMapInfo information from the file. +bool FileMapInfo::open_for_read() { + _full_path = Arguments::GetSharedArchivePath(); + int fd = open(_full_path, O_RDONLY | O_BINARY, 0); + if (fd < 0) { + if (errno == ENOENT) { + // Not locating the shared archive is ok. + fail_continue("Specified shared archive not found."); + } else { + fail_continue("Failed to open shared archive file (%s).", + strerror(errno)); + } + return false; + } + + _fd = fd; + _file_open = true; + return true; +} + + +// Write the FileMapInfo information to the file. + +void FileMapInfo::open_for_write() { + _full_path = Arguments::GetSharedArchivePath(); + if (PrintSharedSpaces) { + tty->print_cr("Dumping shared data to file: "); + tty->print_cr(" %s", _full_path); + } + + // Remove the existing file in case another process has it open. + remove(_full_path); + int fd = open(_full_path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0444); + if (fd < 0) { + fail_stop("Unable to create shared archive file %s.", _full_path); + } + _fd = fd; + _file_offset = 0; + _file_open = true; +} + + +// Write the header to the file, seek to the next allocation boundary. + +void FileMapInfo::write_header() { + write_bytes_aligned(&_header, sizeof(FileMapHeader)); +} + + +// Dump shared spaces to file. + +void FileMapInfo::write_space(int i, CompactibleSpace* space, bool read_only) { + align_file_position(); + struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i]; + write_region(i, (char*)space->bottom(), space->used(), + space->capacity(), read_only, false); +} + + +// Dump region to file. + +void FileMapInfo::write_region(int region, char* base, size_t size, + size_t capacity, bool read_only, + bool allow_exec) { + struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[region]; + + if (_file_open) { + guarantee(si->_file_offset == _file_offset, "file offset mismatch."); + if (PrintSharedSpaces) { + tty->print_cr("Shared file region %d: 0x%x bytes, addr 0x%x," + " file offset 0x%x", region, size, base, _file_offset); + } + } else { + si->_file_offset = _file_offset; + } + si->_base = base; + si->_used = size; + si->_capacity = capacity; + si->_read_only = read_only; + si->_allow_exec = allow_exec; + write_bytes_aligned(base, (int)size); +} + + +// Dump bytes to file -- at the current file position. + +void FileMapInfo::write_bytes(const void* buffer, int nbytes) { + if (_file_open) { + int n = ::write(_fd, buffer, nbytes); + if (n != nbytes) { + // It is dangerous to leave the corrupted shared archive file around, + // close and remove the file. See bug 6372906. + close(); + remove(_full_path); + fail_stop("Unable to write to shared archive file.", NULL); + } + } + _file_offset += nbytes; +} + + +// Align file position to an allocation unit boundary. + +void FileMapInfo::align_file_position() { + long new_file_offset = align_size_up(_file_offset, os::vm_allocation_granularity()); + if (new_file_offset != _file_offset) { + _file_offset = new_file_offset; + if (_file_open) { + // Seek one byte back from the target and write a byte to insure + // that the written file is the correct length. + _file_offset -= 1; + if (lseek(_fd, _file_offset, SEEK_SET) < 0) { + fail_stop("Unable to seek.", NULL); + } + char zero = 0; + write_bytes(&zero, 1); + } + } +} + + +// Dump bytes to file -- at the current file position. + +void FileMapInfo::write_bytes_aligned(const void* buffer, int nbytes) { + align_file_position(); + write_bytes(buffer, nbytes); + align_file_position(); +} + + +// Close the shared archive file. This does NOT unmap mapped regions. + +void FileMapInfo::close() { + if (_file_open) { + if (::close(_fd) < 0) { + fail_stop("Unable to close the shared archive file."); + } + _file_open = false; + _fd = -1; + } +} + + +// Memory map a shared space from the archive file. + +bool FileMapInfo::map_space(int i, ReservedSpace rs, ContiguousSpace* space) { + struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i]; + if (space != NULL) { + if (si->_base != (char*)space->bottom() || + si->_capacity != space->capacity()) { + fail_continue("Shared space base address does not match."); + return false; + } + } + bool result = (map_region(i, rs) != NULL); + if (space != NULL && result) { + space->set_top((HeapWord*)(si->_base + si->_used)); + space->set_saved_mark(); + } + return result; +} + + +// JVM/TI RedefineClasses() support: +// Remap the shared readonly space to shared readwrite, private. +bool FileMapInfo::remap_shared_readonly_as_readwrite() { + struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[0]; + if (!si->_read_only) { + // the space is already readwrite so we are done + return true; + } + size_t used = si->_used; + size_t size = align_size_up(used, os::vm_allocation_granularity()); + if (!open_for_read()) { + return false; + } + char *base = os::remap_memory(_fd, _full_path, si->_file_offset, + si->_base, size, false /* !read_only */, + si->_allow_exec); + close(); + if (base == NULL) { + fail_continue("Unable to remap shared readonly space (errno=%d).", errno); + return false; + } + if (base != si->_base) { + fail_continue("Unable to remap shared readonly space at required address."); + return false; + } + si->_read_only = false; + return true; +} + + +// Memory map a region in the address space. + +char* FileMapInfo::map_region(int i, ReservedSpace rs) { + struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i]; + size_t used = si->_used; + size_t size = align_size_up(used, os::vm_allocation_granularity()); + + ReservedSpace mapped_rs = rs.first_part(size, true, true); + ReservedSpace unmapped_rs = rs.last_part(size); + mapped_rs.release(); + + return map_region(i, true); +} + + +// Memory map a region in the address space. + +char* FileMapInfo::map_region(int i, bool address_must_match) { + struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i]; + size_t used = si->_used; + size_t size = align_size_up(used, os::vm_allocation_granularity()); + char *requested_addr = 0; + if (address_must_match) { + requested_addr = si->_base; + } + char *base = os::map_memory(_fd, _full_path, si->_file_offset, + requested_addr, size, si->_read_only, + si->_allow_exec); + if (base == NULL) { + fail_continue("Unable to map shared space."); + return NULL; + } + if (address_must_match) { + if (base != si->_base) { + fail_continue("Unable to map shared space at required address."); + return NULL; + } + } else { + si->_base = base; // save mapped address for unmapping. + } + return base; +} + + +// Unmap a memory region in the address space. + +void FileMapInfo::unmap_region(int i) { + struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i]; + size_t used = si->_used; + size_t size = align_size_up(used, os::vm_allocation_granularity()); + if (!os::unmap_memory(si->_base, size)) { + fail_stop("Unable to unmap shared space."); + } +} + + +void FileMapInfo::assert_mark(bool check) { + if (!check) { + fail_stop("Mark mismatch while restoring from shared file.", NULL); + } +} + + +FileMapInfo* FileMapInfo::_current_info = NULL; + + +// Open the shared archive file, read and validate the header +// information (version, boot classpath, etc.). If initialization +// fails, shared spaces are disabled and the file is closed. [See +// fail_continue.] + + +bool FileMapInfo::initialize() { + assert(UseSharedSpaces, "UseSharedSpaces expected."); + + if (JvmtiExport::can_modify_any_class() || JvmtiExport::can_walk_any_space()) { + fail_continue("Tool agent requires sharing to be disabled."); + return false; + } + + if (!open_for_read()) { + return false; + } + + init_from_file(_fd); + if (!validate()) { + return false; + } + + SharedReadOnlySize = _header._space[0]._capacity; + SharedReadWriteSize = _header._space[1]._capacity; + SharedMiscDataSize = _header._space[2]._capacity; + SharedMiscCodeSize = _header._space[3]._capacity; + return true; +} + + +bool FileMapInfo::validate() { + if (_header._version != current_version()) { + fail_continue("The shared archive file is the wrong version."); + return false; + } + if (_header._magic != (int)0xf00baba2) { + fail_continue("The shared archive file has a bad magic number."); + return false; + } + if (strncmp(_header._jvm_ident, VM_Version::internal_vm_info_string(), + JVM_IDENT_MAX-1) != 0) { + fail_continue("The shared archive file was created by a different" + " version or build of HotSpot."); + return false; + } + + // Cannot verify interpreter yet, as it can only be created after the GC + // heap has been initialized. + + if (_header._num_jars >= JVM_SHARED_JARS_MAX) { + fail_continue("Too many jar files to share."); + return false; + } + + // Build checks on classpath and jar files + int num_jars_now = 0; + ClassPathEntry *cpe = ClassLoader::classpath_entry(0); + for ( ; cpe != NULL; cpe = cpe->next()) { + + if (cpe->is_jar_file()) { + if (num_jars_now < _header._num_jars) { + + // Jar file - verify timestamp and file size. + struct stat st; + const char *path = cpe->name(); + if (os::stat(path, &st) != 0) { + fail_continue("Unable to open jar file %s.", path); + return false; + } + if (_header._jar[num_jars_now]._timestamp != st.st_mtime || + _header._jar[num_jars_now]._filesize != st.st_size) { + fail_continue("A jar file is not the one used while building" + " the shared archive file."); + return false; + } + } + ++num_jars_now; + } else { + + // If directories appear in boot classpath, they must be empty to + // avoid having to verify each individual class file. + const char* name = ((ClassPathDirEntry*)cpe)->name(); + if (!os::dir_is_empty(name)) { + fail_continue("Boot classpath directory %s is not empty.", name); + return false; + } + } + } + if (num_jars_now < _header._num_jars) { + fail_continue("The number of jar files in the boot classpath is" + " less than the number the shared archive was created with."); + return false; + } + + return true; +} + +// The following method is provided to see whether a given pointer +// falls in the mapped shared space. +// Param: +// p, The given pointer +// Return: +// True if the p is within the mapped shared space, otherwise, false. +bool FileMapInfo::is_in_shared_space(const void* p) { + for (int i = 0; i < CompactingPermGenGen::n_regions; i++) { + if (p >= _header._space[i]._base && + p < _header._space[i]._base + _header._space[i]._used) { + return true; + } + } + + return false; +} diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp new file mode 100644 index 00000000000..9923bec24fc --- /dev/null +++ b/hotspot/src/share/vm/memory/filemap.hpp @@ -0,0 +1,139 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Layout of the file: +// header: dump of archive instance plus versioning info, datestamp, etc. +// [magic # = 0xF00BABA2] +// ... padding to align on page-boundary +// read-write space from CompactingPermGenGen +// read-only space from CompactingPermGenGen +// misc data (block offset table, string table, symbols, dictionary, etc.) +// tag(666) + +static const int JVM_SHARED_JARS_MAX = 128; +static const int JVM_SPACENAME_MAX = 128; +static const int JVM_IDENT_MAX = 256; +static const int JVM_ARCH_MAX = 12; + + + +class FileMapInfo : public CHeapObj { +private: + enum { + _invalid_version = -1, + _current_version = 1 + }; + + bool _file_open; + int _fd; + long _file_offset; + + // FileMapHeader describes the shared space data in the file to be + // mapped. This structure gets written to a file. It is not a class, so + // that the compilers don't add any compiler-private data to it. + + struct FileMapHeader { + int _magic; // identify file type. + int _version; // (from enum, above.) + size_t _alignment; // how shared archive should be aligned + + struct space_info { + int _file_offset; // sizeof(this) rounded to vm page size + char* _base; // copy-on-write base address + size_t _capacity; // for validity checking + size_t _used; // for setting space top on read + bool _read_only; // read only space? + bool _allow_exec; // executable code in space? + } _space[CompactingPermGenGen::n_regions]; + + // The following fields are all sanity checks for whether this archive + // will function correctly with this JVM and the bootclasspath it's + // invoked with. + char _arch[JVM_ARCH_MAX]; // architecture + char _jvm_ident[JVM_IDENT_MAX]; // identifier for jvm + int _num_jars; // Number of jars in bootclasspath + + // Per jar file data: timestamp, size. + + struct { + time_t _timestamp; // jar timestamp. + long _filesize; // jar file size. + } _jar[JVM_SHARED_JARS_MAX]; + } _header; + const char* _full_path; + + static FileMapInfo* _current_info; + + bool init_from_file(int fd); + void align_file_position(); + +public: + FileMapInfo() { + _file_offset = 0; + _file_open = false; + _header._version = _invalid_version; + } + + static int current_version() { return _current_version; } + void populate_header(size_t alignment); + bool validate(); + void invalidate(); + int version() { return _header._version; } + size_t alignment() { return _header._alignment; } + size_t space_capacity(int i) { return _header._space[i]._capacity; } + char* region_base(int i) { return _header._space[i]._base; } + struct FileMapHeader* header() { return &_header; } + + static void set_current_info(FileMapInfo* info) { _current_info = info; } + static FileMapInfo* current_info() { return _current_info; } + static void assert_mark(bool check); + + // File manipulation. + bool initialize(); + bool open_for_read(); + void open_for_write(); + void write_header(); + void write_space(int i, CompactibleSpace* space, bool read_only); + void write_region(int region, char* base, size_t size, + size_t capacity, bool read_only, bool allow_exec); + void write_bytes(const void* buffer, int count); + void write_bytes_aligned(const void* buffer, int count); + bool map_space(int i, ReservedSpace rs, ContiguousSpace *space); + char* map_region(int i, ReservedSpace rs); + char* map_region(int i, bool address_must_match); + void unmap_region(int i); + void close(); + bool is_open() { return _file_open; } + + // JVM/TI RedefineClasses() support: + // Remap the shared readonly space to shared readwrite, private. + bool remap_shared_readonly_as_readwrite(); + + // Errors. + static void fail_stop(const char *msg, ...); + void fail_continue(const char *msg, ...); + + // Return true if given address is in the mapped shared space. + bool is_in_shared_space(const void* p); +}; diff --git a/hotspot/src/share/vm/memory/gcLocker.cpp b/hotspot/src/share/vm/memory/gcLocker.cpp new file mode 100644 index 00000000000..428a5d27553 --- /dev/null +++ b/hotspot/src/share/vm/memory/gcLocker.cpp @@ -0,0 +1,160 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_gcLocker.cpp.incl" + +volatile jint GC_locker::_jni_lock_count = 0; +volatile jint GC_locker::_lock_count = 0; +volatile bool GC_locker::_needs_gc = false; +volatile bool GC_locker::_doing_gc = false; + +void GC_locker::stall_until_clear() { + assert(!JavaThread::current()->in_critical(), "Would deadlock"); + MutexLocker ml(JNICritical_lock); + // Wait for _needs_gc to be cleared + while (GC_locker::needs_gc()) { + JNICritical_lock->wait(); + } +} + +void GC_locker::jni_lock_slow() { + MutexLocker mu(JNICritical_lock); + // Block entering threads if we know at least one thread is in a + // JNI critical region and we need a GC. + // We check that at least one thread is in a critical region before + // blocking because blocked threads are woken up by a thread exiting + // a JNI critical region. + while ((is_jni_active() && needs_gc()) || _doing_gc) { + JNICritical_lock->wait(); + } + jni_lock(); +} + +void GC_locker::jni_unlock_slow() { + MutexLocker mu(JNICritical_lock); + jni_unlock(); + if (needs_gc() && !is_jni_active()) { + // We're the last thread out. Cause a GC to occur. + // GC will also check is_active, so this check is not + // strictly needed. It's added here to make it clear that + // the GC will NOT be performed if any other caller + // of GC_locker::lock() still needs GC locked. + if (!is_active()) { + _doing_gc = true; + { + // Must give up the lock while at a safepoint + MutexUnlocker munlock(JNICritical_lock); + Universe::heap()->collect(GCCause::_gc_locker); + } + _doing_gc = false; + } + clear_needs_gc(); + JNICritical_lock->notify_all(); + } +} + +// Implementation of No_GC_Verifier + +#ifdef ASSERT + +No_GC_Verifier::No_GC_Verifier(bool verifygc) { + _verifygc = verifygc; + if (_verifygc) { + CollectedHeap* h = Universe::heap(); + assert(!h->is_gc_active(), "GC active during No_GC_Verifier"); + _old_invocations = h->total_collections(); + } +} + + +No_GC_Verifier::~No_GC_Verifier() { + if (_verifygc) { + CollectedHeap* h = Universe::heap(); + assert(!h->is_gc_active(), "GC active during No_GC_Verifier"); + if (_old_invocations != h->total_collections()) { + fatal("collection in a No_GC_Verifier secured function"); + } + } +} + +Pause_No_GC_Verifier::Pause_No_GC_Verifier(No_GC_Verifier * ngcv) { + _ngcv = ngcv; + if (_ngcv->_verifygc) { + // if we were verifying, then make sure that nothing is + // wrong before we "pause" verification + CollectedHeap* h = Universe::heap(); + assert(!h->is_gc_active(), "GC active during No_GC_Verifier"); + if (_ngcv->_old_invocations != h->total_collections()) { + fatal("collection in a No_GC_Verifier secured function"); + } + } +} + + +Pause_No_GC_Verifier::~Pause_No_GC_Verifier() { + if (_ngcv->_verifygc) { + // if we were verifying before, then reenable verification + CollectedHeap* h = Universe::heap(); + assert(!h->is_gc_active(), "GC active during No_GC_Verifier"); + _ngcv->_old_invocations = h->total_collections(); + } +} + + +// JRT_LEAF rules: +// A JRT_LEAF method may not interfere with safepointing by +// 1) acquiring or blocking on a Mutex or JavaLock - checked +// 2) allocating heap memory - checked +// 3) executing a VM operation - checked +// 4) executing a system call (including malloc) that could block or grab a lock +// 5) invoking GC +// 6) reaching a safepoint +// 7) running too long +// Nor may any method it calls. +JRT_Leaf_Verifier::JRT_Leaf_Verifier() + : No_Safepoint_Verifier(true, JRT_Leaf_Verifier::should_verify_GC()) +{ +} + +JRT_Leaf_Verifier::~JRT_Leaf_Verifier() +{ +} + +bool JRT_Leaf_Verifier::should_verify_GC() { + switch (JavaThread::current()->thread_state()) { + case _thread_in_Java: + // is in a leaf routine, there must be no safepoint. + return true; + case _thread_in_native: + // A native thread is not subject to safepoints. + // Even while it is in a leaf routine, GC is ok + return false; + default: + // Leaf routines cannot be called from other contexts. + ShouldNotReachHere(); + return false; + } +} +#endif diff --git a/hotspot/src/share/vm/memory/gcLocker.hpp b/hotspot/src/share/vm/memory/gcLocker.hpp new file mode 100644 index 00000000000..00ba7ad89fd --- /dev/null +++ b/hotspot/src/share/vm/memory/gcLocker.hpp @@ -0,0 +1,285 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The direct lock/unlock calls do not force a collection if an unlock +// decrements the count to zero. Avoid calling these if at all possible. + +class GC_locker: public AllStatic { + private: + static volatile jint _jni_lock_count; // number of jni active instances + static volatile jint _lock_count; // number of other active instances + static volatile bool _needs_gc; // heap is filling, we need a GC + // note: bool is typedef'd as jint + static volatile bool _doing_gc; // unlock_critical() is doing a GC + + // Accessors + static bool is_jni_active() { + return _jni_lock_count > 0; + } + + static void set_needs_gc() { + assert(SafepointSynchronize::is_at_safepoint(), + "needs_gc is only set at a safepoint"); + _needs_gc = true; + } + + static void clear_needs_gc() { + assert_lock_strong(JNICritical_lock); + _needs_gc = false; + } + + static void jni_lock() { + Atomic::inc(&_jni_lock_count); + CHECK_UNHANDLED_OOPS_ONLY( + if (CheckUnhandledOops) { Thread::current()->_gc_locked_out_count++; }) + assert(Universe::heap() == NULL || !Universe::heap()->is_gc_active(), + "locking failed"); + } + + static void jni_unlock() { + Atomic::dec(&_jni_lock_count); + CHECK_UNHANDLED_OOPS_ONLY( + if (CheckUnhandledOops) { Thread::current()->_gc_locked_out_count--; }) + } + + static void jni_lock_slow(); + static void jni_unlock_slow(); + + public: + // Accessors + static bool is_active(); + static bool needs_gc() { return _needs_gc; } + // Shorthand + static bool is_active_and_needs_gc() { return is_active() && needs_gc();} + + // Calls set_needs_gc() if is_active() is true. Returns is_active(). + static bool check_active_before_gc(); + + // Stalls the caller (who should not be in a jni critical section) + // until needs_gc() clears. Note however that needs_gc() may be + // set at a subsequent safepoint and/or cleared under the + // JNICritical_lock, so the caller may not safely assert upon + // return from this method that "!needs_gc()" since that is + // not a stable predicate. + static void stall_until_clear(); + + // Non-structured GC locking: currently needed for JNI. Use with care! + static void lock(); + static void unlock(); + + // The following two methods are used for JNI critical regions. + // If we find that we failed to perform a GC because the GC_locker + // was active, arrange for one as soon as possible by allowing + // all threads in critical regions to complete, but not allowing + // other critical regions to be entered. The reasons for that are: + // 1) a GC request won't be starved by overlapping JNI critical + // region activities, which can cause unnecessary OutOfMemory errors. + // 2) even if allocation requests can still be satisfied before GC locker + // becomes inactive, for example, in tenured generation possibly with + // heap expansion, those allocations can trigger lots of safepointing + // attempts (ineffective GC attempts) and require Heap_lock which + // slow down allocations tremendously. + // + // Note that critical regions can be nested in a single thread, so + // we must allow threads already in critical regions to continue. + // + // JNI critical regions are the only participants in this scheme + // because they are, by spec, well bounded while in a critical region. + // + // Each of the following two method is split into a fast path and a slow + // path. JNICritical_lock is only grabbed in the slow path. + // _needs_gc is initially false and every java thread will go + // through the fast path (which does the same thing as the slow path + // when _needs_gc is false). When GC happens at a safepoint, + // GC_locker::is_active() is checked. Since there is no safepoint in the + // fast path of lock_critical() and unlock_critical(), there is no race + // condition between the fast path and GC. After _needs_gc is set at a + // safepoint, every thread will go through the slow path after the safepoint. + // Since after a safepoint, each of the following two methods is either + // entered from the method entry and falls into the slow path, or is + // resumed from the safepoints in the method, which only exist in the slow + // path. So when _needs_gc is set, the slow path is always taken, till + // _needs_gc is cleared. + static void lock_critical(JavaThread* thread); + static void unlock_critical(JavaThread* thread); +}; + + +// A No_GC_Verifier object can be placed in methods where one assumes that +// no garbage collection will occur. The destructor will verify this property +// unless the constructor is called with argument false (not verifygc). +// +// The check will only be done in debug mode and if verifygc true. + +class No_GC_Verifier: public StackObj { + friend class Pause_No_GC_Verifier; + + protected: + bool _verifygc; + unsigned int _old_invocations; + + public: +#ifdef ASSERT + No_GC_Verifier(bool verifygc = true); + ~No_GC_Verifier(); +#else + No_GC_Verifier(bool verifygc = true) {} + ~No_GC_Verifier() {} +#endif +}; + +// A Pause_No_GC_Verifier is used to temporarily pause the behavior +// of a No_GC_Verifier object. If we are not in debug mode or if the +// No_GC_Verifier object has a _verifygc value of false, then there +// is nothing to do. + +class Pause_No_GC_Verifier: public StackObj { + private: + No_GC_Verifier * _ngcv; + + public: +#ifdef ASSERT + Pause_No_GC_Verifier(No_GC_Verifier * ngcv); + ~Pause_No_GC_Verifier(); +#else + Pause_No_GC_Verifier(No_GC_Verifier * ngcv) {} + ~Pause_No_GC_Verifier() {} +#endif +}; + + +// A No_Safepoint_Verifier object will throw an assertion failure if +// the current thread passes a possible safepoint while this object is +// instantiated. A safepoint, will either be: an oop allocation, blocking +// on a Mutex or JavaLock, or executing a VM operation. +// +// If StrictSafepointChecks is turned off, it degrades into a No_GC_Verifier +// +class No_Safepoint_Verifier : public No_GC_Verifier { + friend class Pause_No_Safepoint_Verifier; + + private: + bool _activated; + Thread *_thread; + public: +#ifdef ASSERT + No_Safepoint_Verifier(bool activated = true, bool verifygc = true ) : No_GC_Verifier(verifygc) { + _thread = Thread::current(); + if (_activated) { + _thread->_allow_allocation_count++; + _thread->_allow_safepoint_count++; + } + } + + ~No_Safepoint_Verifier() { + if (_activated) { + _thread->_allow_allocation_count--; + _thread->_allow_safepoint_count--; + } + } +#else + No_Safepoint_Verifier(bool activated = true, bool verifygc = true) : No_GC_Verifier(verifygc){} + ~No_Safepoint_Verifier() {} +#endif +}; + +// A Pause_No_Safepoint_Verifier is used to temporarily pause the +// behavior of a No_Safepoint_Verifier object. If we are not in debug +// mode then there is nothing to do. If the No_Safepoint_Verifier +// object has an _activated value of false, then there is nothing to +// do for safepoint and allocation checking, but there may still be +// something to do for the underlying No_GC_Verifier object. + +class Pause_No_Safepoint_Verifier : public Pause_No_GC_Verifier { + private: + No_Safepoint_Verifier * _nsv; + + public: +#ifdef ASSERT + Pause_No_Safepoint_Verifier(No_Safepoint_Verifier * nsv) + : Pause_No_GC_Verifier(nsv) { + + _nsv = nsv; + if (_nsv->_activated) { + _nsv->_thread->_allow_allocation_count--; + _nsv->_thread->_allow_safepoint_count--; + } + } + + ~Pause_No_Safepoint_Verifier() { + if (_nsv->_activated) { + _nsv->_thread->_allow_allocation_count++; + _nsv->_thread->_allow_safepoint_count++; + } + } +#else + Pause_No_Safepoint_Verifier(No_Safepoint_Verifier * nsv) + : Pause_No_GC_Verifier(nsv) {} + ~Pause_No_Safepoint_Verifier() {} +#endif +}; + +// JRT_LEAF currently can be called from either _thread_in_Java or +// _thread_in_native mode. In _thread_in_native, it is ok +// for another thread to trigger GC. The rest of the JRT_LEAF +// rules apply. +class JRT_Leaf_Verifier : public No_Safepoint_Verifier { + static bool should_verify_GC(); + public: +#ifdef ASSERT + JRT_Leaf_Verifier(); + ~JRT_Leaf_Verifier(); +#else + JRT_Leaf_Verifier() {} + ~JRT_Leaf_Verifier() {} +#endif +}; + +// A No_Alloc_Verifier object can be placed in methods where one assumes that +// no allocation will occur. The destructor will verify this property +// unless the constructor is called with argument false (not activated). +// +// The check will only be done in debug mode and if activated. +// Note: this only makes sense at safepoints (otherwise, other threads may +// allocate concurrently.) + +class No_Alloc_Verifier : public StackObj { + private: + bool _activated; + + public: +#ifdef ASSERT + No_Alloc_Verifier(bool activated = true) { + _activated = activated; + if (_activated) Thread::current()->_allow_allocation_count++; + } + + ~No_Alloc_Verifier() { + if (_activated) Thread::current()->_allow_allocation_count--; + } +#else + No_Alloc_Verifier(bool activated = true) {} + ~No_Alloc_Verifier() {} +#endif +}; diff --git a/hotspot/src/share/vm/memory/gcLocker.inline.hpp b/hotspot/src/share/vm/memory/gcLocker.inline.hpp new file mode 100644 index 00000000000..a3550be1d45 --- /dev/null +++ b/hotspot/src/share/vm/memory/gcLocker.inline.hpp @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline bool GC_locker::is_active() { + return _lock_count > 0 || _jni_lock_count > 0; +} + +inline bool GC_locker::check_active_before_gc() { + if (is_active()) { + set_needs_gc(); + } + return is_active(); +} + +inline void GC_locker::lock() { + // cast away volatile + Atomic::inc(&_lock_count); + CHECK_UNHANDLED_OOPS_ONLY( + if (CheckUnhandledOops) { Thread::current()->_gc_locked_out_count++; }) + assert(Universe::heap() == NULL || + !Universe::heap()->is_gc_active(), "locking failed"); +} + +inline void GC_locker::unlock() { + // cast away volatile + Atomic::dec(&_lock_count); + CHECK_UNHANDLED_OOPS_ONLY( + if (CheckUnhandledOops) { Thread::current()->_gc_locked_out_count--; }) +} + +inline void GC_locker::lock_critical(JavaThread* thread) { + if (!thread->in_critical()) { + if (!needs_gc()) { + jni_lock(); + } else { + jni_lock_slow(); + } + } + thread->enter_critical(); +} + +inline void GC_locker::unlock_critical(JavaThread* thread) { + thread->exit_critical(); + if (!thread->in_critical()) { + if (!needs_gc()) { + jni_unlock(); + } else { + jni_unlock_slow(); + } + } +} diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.cpp b/hotspot/src/share/vm/memory/genCollectedHeap.cpp new file mode 100644 index 00000000000..afc4f52eacf --- /dev/null +++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp @@ -0,0 +1,1368 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_genCollectedHeap.cpp.incl" + +GenCollectedHeap* GenCollectedHeap::_gch; +NOT_PRODUCT(size_t GenCollectedHeap::_skip_header_HeapWords = 0;) + +// The set of potentially parallel tasks in strong root scanning. +enum GCH_process_strong_roots_tasks { + // We probably want to parallelize both of these internally, but for now... + GCH_PS_younger_gens, + // Leave this one last. + GCH_PS_NumElements +}; + +GenCollectedHeap::GenCollectedHeap(GenCollectorPolicy *policy) : + SharedHeap(policy), + _gen_policy(policy), + _gen_process_strong_tasks(new SubTasksDone(GCH_PS_NumElements)), + _full_collections_completed(0) +{ + if (_gen_process_strong_tasks == NULL || + !_gen_process_strong_tasks->valid()) { + vm_exit_during_initialization("Failed necessary allocation."); + } + assert(policy != NULL, "Sanity check"); + _preloading_shared_classes = false; +} + +jint GenCollectedHeap::initialize() { + int i; + _n_gens = gen_policy()->number_of_generations(); + + // While there are no constraints in the GC code that HeapWordSize + // be any particular value, there are multiple other areas in the + // system which believe this to be true (e.g. oop->object_size in some + // cases incorrectly returns the size in wordSize units rather than + // HeapWordSize). + guarantee(HeapWordSize == wordSize, "HeapWordSize must equal wordSize"); + + // The heap must be at least as aligned as generations. + size_t alignment = Generation::GenGrain; + + _gen_specs = gen_policy()->generations(); + PermanentGenerationSpec *perm_gen_spec = + collector_policy()->permanent_generation(); + + // Make sure the sizes are all aligned. + for (i = 0; i < _n_gens; i++) { + _gen_specs[i]->align(alignment); + } + perm_gen_spec->align(alignment); + + // If we are dumping the heap, then allocate a wasted block of address + // space in order to push the heap to a lower address. This extra + // address range allows for other (or larger) libraries to be loaded + // without them occupying the space required for the shared spaces. + + if (DumpSharedSpaces) { + uintx reserved = 0; + uintx block_size = 64*1024*1024; + while (reserved < SharedDummyBlockSize) { + char* dummy = os::reserve_memory(block_size); + reserved += block_size; + } + } + + // Allocate space for the heap. + + char* heap_address; + size_t total_reserved = 0; + int n_covered_regions = 0; + ReservedSpace heap_rs(0); + + heap_address = allocate(alignment, perm_gen_spec, &total_reserved, + &n_covered_regions, &heap_rs); + + if (UseSharedSpaces) { + if (!heap_rs.is_reserved() || heap_address != heap_rs.base()) { + if (heap_rs.is_reserved()) { + heap_rs.release(); + } + FileMapInfo* mapinfo = FileMapInfo::current_info(); + mapinfo->fail_continue("Unable to reserve shared region."); + allocate(alignment, perm_gen_spec, &total_reserved, &n_covered_regions, + &heap_rs); + } + } + + if (!heap_rs.is_reserved()) { + vm_shutdown_during_initialization( + "Could not reserve enough space for object heap"); + return JNI_ENOMEM; + } + + _reserved = MemRegion((HeapWord*)heap_rs.base(), + (HeapWord*)(heap_rs.base() + heap_rs.size())); + + // It is important to do this in a way such that concurrent readers can't + // temporarily think somethings in the heap. (Seen this happen in asserts.) + _reserved.set_word_size(0); + _reserved.set_start((HeapWord*)heap_rs.base()); + size_t actual_heap_size = heap_rs.size() - perm_gen_spec->misc_data_size() + - perm_gen_spec->misc_code_size(); + _reserved.set_end((HeapWord*)(heap_rs.base() + actual_heap_size)); + + _rem_set = collector_policy()->create_rem_set(_reserved, n_covered_regions); + set_barrier_set(rem_set()->bs()); + _gch = this; + + for (i = 0; i < _n_gens; i++) { + ReservedSpace this_rs = heap_rs.first_part(_gen_specs[i]->max_size(), + UseSharedSpaces, UseSharedSpaces); + _gens[i] = _gen_specs[i]->init(this_rs, i, rem_set()); + heap_rs = heap_rs.last_part(_gen_specs[i]->max_size()); + } + _perm_gen = perm_gen_spec->init(heap_rs, PermSize, rem_set()); + + clear_incremental_collection_will_fail(); + clear_last_incremental_collection_failed(); + +#ifndef SERIALGC + // If we are running CMS, create the collector responsible + // for collecting the CMS generations. + if (collector_policy()->is_concurrent_mark_sweep_policy()) { + bool success = create_cms_collector(); + if (!success) return JNI_ENOMEM; + } +#endif // SERIALGC + + return JNI_OK; +} + + +char* GenCollectedHeap::allocate(size_t alignment, + PermanentGenerationSpec* perm_gen_spec, + size_t* _total_reserved, + int* _n_covered_regions, + ReservedSpace* heap_rs){ + const char overflow_msg[] = "The size of the object heap + VM data exceeds " + "the maximum representable size"; + + // Now figure out the total size. + size_t total_reserved = 0; + int n_covered_regions = 0; + const size_t pageSize = UseLargePages ? + os::large_page_size() : os::vm_page_size(); + + for (int i = 0; i < _n_gens; i++) { + total_reserved += _gen_specs[i]->max_size(); + if (total_reserved < _gen_specs[i]->max_size()) { + vm_exit_during_initialization(overflow_msg); + } + n_covered_regions += _gen_specs[i]->n_covered_regions(); + } + assert(total_reserved % pageSize == 0, "Gen size"); + total_reserved += perm_gen_spec->max_size(); + assert(total_reserved % pageSize == 0, "Perm Gen size"); + + if (total_reserved < perm_gen_spec->max_size()) { + vm_exit_during_initialization(overflow_msg); + } + n_covered_regions += perm_gen_spec->n_covered_regions(); + + // Add the size of the data area which shares the same reserved area + // as the heap, but which is not actually part of the heap. + size_t s = perm_gen_spec->misc_data_size() + perm_gen_spec->misc_code_size(); + + total_reserved += s; + if (total_reserved < s) { + vm_exit_during_initialization(overflow_msg); + } + + if (UseLargePages) { + assert(total_reserved != 0, "total_reserved cannot be 0"); + total_reserved = round_to(total_reserved, os::large_page_size()); + if (total_reserved < os::large_page_size()) { + vm_exit_during_initialization(overflow_msg); + } + } + + // Calculate the address at which the heap must reside in order for + // the shared data to be at the required address. + + char* heap_address; + if (UseSharedSpaces) { + + // Calculate the address of the first word beyond the heap. + FileMapInfo* mapinfo = FileMapInfo::current_info(); + int lr = CompactingPermGenGen::n_regions - 1; + size_t capacity = align_size_up(mapinfo->space_capacity(lr), alignment); + heap_address = mapinfo->region_base(lr) + capacity; + + // Calculate the address of the first word of the heap. + heap_address -= total_reserved; + } else { + heap_address = NULL; // any address will do. + } + + *_total_reserved = total_reserved; + *_n_covered_regions = n_covered_regions; + *heap_rs = ReservedSpace(total_reserved, alignment, + UseLargePages, heap_address); + + return heap_address; +} + + +void GenCollectedHeap::post_initialize() { + SharedHeap::post_initialize(); + TwoGenerationCollectorPolicy *policy = + (TwoGenerationCollectorPolicy *)collector_policy(); + guarantee(policy->is_two_generation_policy(), "Illegal policy type"); + DefNewGeneration* def_new_gen = (DefNewGeneration*) get_gen(0); + assert(def_new_gen->kind() == Generation::DefNew || + def_new_gen->kind() == Generation::ParNew || + def_new_gen->kind() == Generation::ASParNew, + "Wrong generation kind"); + + Generation* old_gen = get_gen(1); + assert(old_gen->kind() == Generation::ConcurrentMarkSweep || + old_gen->kind() == Generation::ASConcurrentMarkSweep || + old_gen->kind() == Generation::MarkSweepCompact, + "Wrong generation kind"); + + policy->initialize_size_policy(def_new_gen->eden()->capacity(), + old_gen->capacity(), + def_new_gen->from()->capacity()); + policy->initialize_gc_policy_counters(); +} + +void GenCollectedHeap::ref_processing_init() { + SharedHeap::ref_processing_init(); + for (int i = 0; i < _n_gens; i++) { + _gens[i]->ref_processor_init(); + } +} + +size_t GenCollectedHeap::capacity() const { + size_t res = 0; + for (int i = 0; i < _n_gens; i++) { + res += _gens[i]->capacity(); + } + return res; +} + +size_t GenCollectedHeap::used() const { + size_t res = 0; + for (int i = 0; i < _n_gens; i++) { + res += _gens[i]->used(); + } + return res; +} + +// Save the "used_region" for generations level and lower, +// and, if perm is true, for perm gen. +void GenCollectedHeap::save_used_regions(int level, bool perm) { + assert(level < _n_gens, "Illegal level parameter"); + for (int i = level; i >= 0; i--) { + _gens[i]->save_used_region(); + } + if (perm) { + perm_gen()->save_used_region(); + } +} + +size_t GenCollectedHeap::max_capacity() const { + size_t res = 0; + for (int i = 0; i < _n_gens; i++) { + res += _gens[i]->max_capacity(); + } + return res; +} + +// Update the _full_collections_completed counter +// at the end of a stop-world full GC. +unsigned int GenCollectedHeap::update_full_collections_completed() { + MonitorLockerEx ml(FullGCCount_lock, Mutex::_no_safepoint_check_flag); + assert(_full_collections_completed <= _total_full_collections, + "Can't complete more collections than were started"); + _full_collections_completed = _total_full_collections; + ml.notify_all(); + return _full_collections_completed; +} + +// Update the _full_collections_completed counter, as appropriate, +// at the end of a concurrent GC cycle. Note the conditional update +// below to allow this method to be called by a concurrent collector +// without synchronizing in any manner with the VM thread (which +// may already have initiated a STW full collection "concurrently"). +unsigned int GenCollectedHeap::update_full_collections_completed(unsigned int count) { + MonitorLockerEx ml(FullGCCount_lock, Mutex::_no_safepoint_check_flag); + assert((_full_collections_completed <= _total_full_collections) && + (count <= _total_full_collections), + "Can't complete more collections than were started"); + if (count > _full_collections_completed) { + _full_collections_completed = count; + ml.notify_all(); + } + return _full_collections_completed; +} + + +#ifndef PRODUCT +// Override of memory state checking method in CollectedHeap: +// Some collectors (CMS for example) can't have badHeapWordVal written +// in the first two words of an object. (For instance , in the case of +// CMS these words hold state used to synchronize between certain +// (concurrent) GC steps and direct allocating mutators.) +// The skip_header_HeapWords() method below, allows us to skip +// over the requisite number of HeapWord's. Note that (for +// generational collectors) this means that those many words are +// skipped in each object, irrespective of the generation in which +// that object lives. The resultant loss of precision seems to be +// harmless and the pain of avoiding that imprecision appears somewhat +// higher than we are prepared to pay for such rudimentary debugging +// support. +void GenCollectedHeap::check_for_non_bad_heap_word_value(HeapWord* addr, + size_t size) { + if (CheckMemoryInitialization && ZapUnusedHeapArea) { + // We are asked to check a size in HeapWords, + // but the memory is mangled in juint words. + juint* start = (juint*) (addr + skip_header_HeapWords()); + juint* end = (juint*) (addr + size); + for (juint* slot = start; slot < end; slot += 1) { + assert(*slot == badHeapWordVal, + "Found non badHeapWordValue in pre-allocation check"); + } + } +} +#endif + +HeapWord* GenCollectedHeap::attempt_allocation(size_t size, + bool is_tlab, + bool first_only) { + HeapWord* res; + for (int i = 0; i < _n_gens; i++) { + if (_gens[i]->should_allocate(size, is_tlab)) { + res = _gens[i]->allocate(size, is_tlab); + if (res != NULL) return res; + else if (first_only) break; + } + } + // Otherwise... + return NULL; +} + +HeapWord* GenCollectedHeap::mem_allocate(size_t size, + bool is_large_noref, + bool is_tlab, + bool* gc_overhead_limit_was_exceeded) { + return collector_policy()->mem_allocate_work(size, + is_tlab, + gc_overhead_limit_was_exceeded); +} + +bool GenCollectedHeap::must_clear_all_soft_refs() { + return _gc_cause == GCCause::_last_ditch_collection; +} + +bool GenCollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) { + return (cause == GCCause::_java_lang_system_gc || + cause == GCCause::_gc_locker) && + UseConcMarkSweepGC && ExplicitGCInvokesConcurrent; +} + +void GenCollectedHeap::do_collection(bool full, + bool clear_all_soft_refs, + size_t size, + bool is_tlab, + int max_level) { + bool prepared_for_verification = false; + ResourceMark rm; + DEBUG_ONLY(Thread* my_thread = Thread::current();) + + assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); + assert(my_thread->is_VM_thread() || + my_thread->is_ConcurrentGC_thread(), + "incorrect thread type capability"); + assert(Heap_lock->is_locked(), "the requesting thread should have the Heap_lock"); + guarantee(!is_gc_active(), "collection is not reentrant"); + assert(max_level < n_gens(), "sanity check"); + + if (GC_locker::check_active_before_gc()) { + return; // GC is disabled (e.g. JNI GetXXXCritical operation) + } + + const size_t perm_prev_used = perm_gen()->used(); + + if (PrintHeapAtGC) { + Universe::print_heap_before_gc(); + if (Verbose) { + gclog_or_tty->print_cr("GC Cause: %s", GCCause::to_string(gc_cause())); + } + } + + { + FlagSetting fl(_is_gc_active, true); + + bool complete = full && (max_level == (n_gens()-1)); + const char* gc_cause_str = "GC "; + if (complete) { + GCCause::Cause cause = gc_cause(); + if (cause == GCCause::_java_lang_system_gc) { + gc_cause_str = "Full GC (System) "; + } else { + gc_cause_str = "Full GC "; + } + } + gclog_or_tty->date_stamp(PrintGC && PrintGCDateStamps); + TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); + TraceTime t(gc_cause_str, PrintGCDetails, false, gclog_or_tty); + + gc_prologue(complete); + increment_total_collections(complete); + + size_t gch_prev_used = used(); + + int starting_level = 0; + if (full) { + // Search for the oldest generation which will collect all younger + // generations, and start collection loop there. + for (int i = max_level; i >= 0; i--) { + if (_gens[i]->full_collects_younger_generations()) { + starting_level = i; + break; + } + } + } + + bool must_restore_marks_for_biased_locking = false; + + int max_level_collected = starting_level; + for (int i = starting_level; i <= max_level; i++) { + if (_gens[i]->should_collect(full, size, is_tlab)) { + // Timer for individual generations. Last argument is false: no CR + TraceTime t1(_gens[i]->short_name(), PrintGCDetails, false, gclog_or_tty); + TraceCollectorStats tcs(_gens[i]->counters()); + TraceMemoryManagerStats tmms(_gens[i]->kind()); + + size_t prev_used = _gens[i]->used(); + _gens[i]->stat_record()->invocations++; + _gens[i]->stat_record()->accumulated_time.start(); + + if (PrintGC && Verbose) { + gclog_or_tty->print("level=%d invoke=%d size=" SIZE_FORMAT, + i, + _gens[i]->stat_record()->invocations, + size*HeapWordSize); + } + + if (VerifyBeforeGC && i >= VerifyGCLevel && + total_collections() >= VerifyGCStartAt) { + HandleMark hm; // Discard invalid handles created during verification + if (!prepared_for_verification) { + prepare_for_verify(); + prepared_for_verification = true; + } + gclog_or_tty->print(" VerifyBeforeGC:"); + Universe::verify(true); + } + COMPILER2_PRESENT(DerivedPointerTable::clear()); + + if (!must_restore_marks_for_biased_locking && + _gens[i]->performs_in_place_marking()) { + // We perform this mark word preservation work lazily + // because it's only at this point that we know whether we + // absolutely have to do it; we want to avoid doing it for + // scavenge-only collections where it's unnecessary + must_restore_marks_for_biased_locking = true; + BiasedLocking::preserve_marks(); + } + + // Do collection work + { + // Note on ref discovery: For what appear to be historical reasons, + // GCH enables and disabled (by enqueing) refs discovery. + // In the future this should be moved into the generation's + // collect method so that ref discovery and enqueueing concerns + // are local to a generation. The collect method could return + // an appropriate indication in the case that notification on + // the ref lock was needed. This will make the treatment of + // weak refs more uniform (and indeed remove such concerns + // from GCH). XXX + + HandleMark hm; // Discard invalid handles created during gc + save_marks(); // save marks for all gens + // We want to discover references, but not process them yet. + // This mode is disabled in process_discovered_references if the + // generation does some collection work, or in + // enqueue_discovered_references if the generation returns + // without doing any work. + ReferenceProcessor* rp = _gens[i]->ref_processor(); + // If the discovery of ("weak") refs in this generation is + // atomic wrt other collectors in this configuration, we + // are guaranteed to have empty discovered ref lists. + if (rp->discovery_is_atomic()) { + rp->verify_no_references_recorded(); + rp->enable_discovery(); + } else { + // collect() will enable discovery as appropriate + } + _gens[i]->collect(full, clear_all_soft_refs, size, is_tlab); + if (!rp->enqueuing_is_done()) { + rp->enqueue_discovered_references(); + } else { + rp->set_enqueuing_is_done(false); + } + rp->verify_no_references_recorded(); + } + max_level_collected = i; + + // Determine if allocation request was met. + if (size > 0) { + if (!is_tlab || _gens[i]->supports_tlab_allocation()) { + if (size*HeapWordSize <= _gens[i]->unsafe_max_alloc_nogc()) { + size = 0; + } + } + } + + COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); + + _gens[i]->stat_record()->accumulated_time.stop(); + + update_gc_stats(i, full); + + if (VerifyAfterGC && i >= VerifyGCLevel && + total_collections() >= VerifyGCStartAt) { + HandleMark hm; // Discard invalid handles created during verification + gclog_or_tty->print(" VerifyAfterGC:"); + Universe::verify(false); + } + + if (PrintGCDetails) { + gclog_or_tty->print(":"); + _gens[i]->print_heap_change(prev_used); + } + } + } + + // Update "complete" boolean wrt what actually transpired -- + // for instance, a promotion failure could have led to + // a whole heap collection. + complete = complete || (max_level_collected == n_gens() - 1); + + if (PrintGCDetails) { + print_heap_change(gch_prev_used); + + // Print perm gen info for full GC with PrintGCDetails flag. + if (complete) { + print_perm_heap_change(perm_prev_used); + } + } + + for (int j = max_level_collected; j >= 0; j -= 1) { + // Adjust generation sizes. + _gens[j]->compute_new_size(); + } + + if (complete) { + // Ask the permanent generation to adjust size for full collections + perm()->compute_new_size(); + update_full_collections_completed(); + } + + // Track memory usage and detect low memory after GC finishes + MemoryService::track_memory_usage(); + + gc_epilogue(complete); + + if (must_restore_marks_for_biased_locking) { + BiasedLocking::restore_marks(); + } + } + + AdaptiveSizePolicy* sp = gen_policy()->size_policy(); + AdaptiveSizePolicyOutput(sp, total_collections()); + + if (PrintHeapAtGC) { + Universe::print_heap_after_gc(); + } + + if (ExitAfterGCNum > 0 && total_collections() == ExitAfterGCNum) { + tty->print_cr("Stopping after GC #%d", ExitAfterGCNum); + vm_exit(-1); + } +} + +HeapWord* GenCollectedHeap::satisfy_failed_allocation(size_t size, bool is_tlab) { + return collector_policy()->satisfy_failed_allocation(size, is_tlab); +} + +void GenCollectedHeap::set_par_threads(int t) { + SharedHeap::set_par_threads(t); + _gen_process_strong_tasks->set_par_threads(t); +} + +class AssertIsPermClosure: public OopClosure { +public: + void do_oop(oop* p) { + assert((*p) == NULL || (*p)->is_perm(), "Referent should be perm."); + } +}; +static AssertIsPermClosure assert_is_perm_closure; + +void GenCollectedHeap:: +gen_process_strong_roots(int level, + bool younger_gens_as_roots, + bool collecting_perm_gen, + SharedHeap::ScanningOption so, + OopsInGenClosure* older_gens, + OopsInGenClosure* not_older_gens) { + // General strong roots. + SharedHeap::process_strong_roots(collecting_perm_gen, so, + not_older_gens, older_gens); + + if (younger_gens_as_roots) { + if (!_gen_process_strong_tasks->is_task_claimed(GCH_PS_younger_gens)) { + for (int i = 0; i < level; i++) { + not_older_gens->set_generation(_gens[i]); + _gens[i]->oop_iterate(not_older_gens); + } + not_older_gens->reset_generation(); + } + } + // When collection is parallel, all threads get to cooperate to do + // older-gen scanning. + for (int i = level+1; i < _n_gens; i++) { + older_gens->set_generation(_gens[i]); + rem_set()->younger_refs_iterate(_gens[i], older_gens); + older_gens->reset_generation(); + } + + _gen_process_strong_tasks->all_tasks_completed(); +} + +void GenCollectedHeap::gen_process_weak_roots(OopClosure* root_closure, + OopClosure* non_root_closure) { + SharedHeap::process_weak_roots(root_closure, non_root_closure); + // "Local" "weak" refs + for (int i = 0; i < _n_gens; i++) { + _gens[i]->ref_processor()->weak_oops_do(root_closure); + } +} + +#define GCH_SINCE_SAVE_MARKS_ITERATE_DEFN(OopClosureType, nv_suffix) \ +void GenCollectedHeap:: \ +oop_since_save_marks_iterate(int level, \ + OopClosureType* cur, \ + OopClosureType* older) { \ + _gens[level]->oop_since_save_marks_iterate##nv_suffix(cur); \ + for (int i = level+1; i < n_gens(); i++) { \ + _gens[i]->oop_since_save_marks_iterate##nv_suffix(older); \ + } \ + perm_gen()->oop_since_save_marks_iterate##nv_suffix(older); \ +} + +ALL_SINCE_SAVE_MARKS_CLOSURES(GCH_SINCE_SAVE_MARKS_ITERATE_DEFN) + +#undef GCH_SINCE_SAVE_MARKS_ITERATE_DEFN + +bool GenCollectedHeap::no_allocs_since_save_marks(int level) { + for (int i = level; i < _n_gens; i++) { + if (!_gens[i]->no_allocs_since_save_marks()) return false; + } + return perm_gen()->no_allocs_since_save_marks(); +} + +bool GenCollectedHeap::supports_inline_contig_alloc() const { + return _gens[0]->supports_inline_contig_alloc(); +} + +HeapWord** GenCollectedHeap::top_addr() const { + return _gens[0]->top_addr(); +} + +HeapWord** GenCollectedHeap::end_addr() const { + return _gens[0]->end_addr(); +} + +size_t GenCollectedHeap::unsafe_max_alloc() { + return _gens[0]->unsafe_max_alloc_nogc(); +} + +// public collection interfaces + +void GenCollectedHeap::collect(GCCause::Cause cause) { + if (should_do_concurrent_full_gc(cause)) { +#ifndef SERIALGC + // mostly concurrent full collection + collect_mostly_concurrent(cause); +#else // SERIALGC + ShouldNotReachHere(); +#endif // SERIALGC + } else { +#ifdef ASSERT + if (cause == GCCause::_scavenge_alot) { + // minor collection only + collect(cause, 0); + } else { + // Stop-the-world full collection + collect(cause, n_gens() - 1); + } +#else + // Stop-the-world full collection + collect(cause, n_gens() - 1); +#endif + } +} + +void GenCollectedHeap::collect(GCCause::Cause cause, int max_level) { + // The caller doesn't have the Heap_lock + assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock"); + MutexLocker ml(Heap_lock); + collect_locked(cause, max_level); +} + +// This interface assumes that it's being called by the +// vm thread. It collects the heap assuming that the +// heap lock is already held and that we are executing in +// the context of the vm thread. +void GenCollectedHeap::collect_as_vm_thread(GCCause::Cause cause) { + assert(Thread::current()->is_VM_thread(), "Precondition#1"); + assert(Heap_lock->is_locked(), "Precondition#2"); + GCCauseSetter gcs(this, cause); + switch (cause) { + case GCCause::_heap_inspection: + case GCCause::_heap_dump: { + HandleMark hm; + do_full_collection(false, // don't clear all soft refs + n_gens() - 1); + break; + } + default: // XXX FIX ME + ShouldNotReachHere(); // Unexpected use of this function + } +} + +void GenCollectedHeap::collect_locked(GCCause::Cause cause) { + // The caller has the Heap_lock + assert(Heap_lock->owned_by_self(), "this thread should own the Heap_lock"); + collect_locked(cause, n_gens() - 1); +} + +// this is the private collection interface +// The Heap_lock is expected to be held on entry. + +void GenCollectedHeap::collect_locked(GCCause::Cause cause, int max_level) { + if (_preloading_shared_classes) { + warning("\nThe permanent generation is not large enough to preload " + "requested classes.\nUse -XX:PermSize= to increase the initial " + "size of the permanent generation.\n"); + vm_exit(2); + } + // Read the GC count while holding the Heap_lock + unsigned int gc_count_before = total_collections(); + unsigned int full_gc_count_before = total_full_collections(); + { + MutexUnlocker mu(Heap_lock); // give up heap lock, execute gets it back + VM_GenCollectFull op(gc_count_before, full_gc_count_before, + cause, max_level); + VMThread::execute(&op); + } +} + +#ifndef SERIALGC +bool GenCollectedHeap::create_cms_collector() { + + assert(((_gens[1]->kind() == Generation::ConcurrentMarkSweep) || + (_gens[1]->kind() == Generation::ASConcurrentMarkSweep)) && + _perm_gen->as_gen()->kind() == Generation::ConcurrentMarkSweep, + "Unexpected generation kinds"); + // Skip two header words in the block content verification + NOT_PRODUCT(_skip_header_HeapWords = CMSCollector::skip_header_HeapWords();) + CMSCollector* collector = new CMSCollector( + (ConcurrentMarkSweepGeneration*)_gens[1], + (ConcurrentMarkSweepGeneration*)_perm_gen->as_gen(), + _rem_set->as_CardTableRS(), + (ConcurrentMarkSweepPolicy*) collector_policy()); + + if (collector == NULL || !collector->completed_initialization()) { + if (collector) { + delete collector; // Be nice in embedded situation + } + vm_shutdown_during_initialization("Could not create CMS collector"); + return false; + } + return true; // success +} + +void GenCollectedHeap::collect_mostly_concurrent(GCCause::Cause cause) { + assert(!Heap_lock->owned_by_self(), "Should not own Heap_lock"); + + MutexLocker ml(Heap_lock); + // Read the GC counts while holding the Heap_lock + unsigned int full_gc_count_before = total_full_collections(); + unsigned int gc_count_before = total_collections(); + { + MutexUnlocker mu(Heap_lock); + VM_GenCollectFullConcurrent op(gc_count_before, full_gc_count_before, cause); + VMThread::execute(&op); + } +} +#endif // SERIALGC + + +void GenCollectedHeap::do_full_collection(bool clear_all_soft_refs, + int max_level) { + int local_max_level; + if (!incremental_collection_will_fail() && + gc_cause() == GCCause::_gc_locker) { + local_max_level = 0; + } else { + local_max_level = max_level; + } + + do_collection(true /* full */, + clear_all_soft_refs /* clear_all_soft_refs */, + 0 /* size */, + false /* is_tlab */, + local_max_level /* max_level */); + // Hack XXX FIX ME !!! + // A scavenge may not have been attempted, or may have + // been attempted and failed, because the old gen was too full + if (local_max_level == 0 && gc_cause() == GCCause::_gc_locker && + incremental_collection_will_fail()) { + if (PrintGCDetails) { + gclog_or_tty->print_cr("GC locker: Trying a full collection " + "because scavenge failed"); + } + // This time allow the old gen to be collected as well + do_collection(true /* full */, + clear_all_soft_refs /* clear_all_soft_refs */, + 0 /* size */, + false /* is_tlab */, + n_gens() - 1 /* max_level */); + } +} + +// Returns "TRUE" iff "p" points into the allocated area of the heap. +bool GenCollectedHeap::is_in(const void* p) const { + #ifndef ASSERT + guarantee(VerifyBeforeGC || + VerifyDuringGC || + VerifyBeforeExit || + VerifyAfterGC, "too expensive"); + #endif + // This might be sped up with a cache of the last generation that + // answered yes. + for (int i = 0; i < _n_gens; i++) { + if (_gens[i]->is_in(p)) return true; + } + if (_perm_gen->as_gen()->is_in(p)) return true; + // Otherwise... + return false; +} + +// Returns "TRUE" iff "p" points into the allocated area of the heap. +bool GenCollectedHeap::is_in_youngest(void* p) { + return _gens[0]->is_in(p); +} + +void GenCollectedHeap::oop_iterate(OopClosure* cl) { + for (int i = 0; i < _n_gens; i++) { + _gens[i]->oop_iterate(cl); + } +} + +void GenCollectedHeap::oop_iterate(MemRegion mr, OopClosure* cl) { + for (int i = 0; i < _n_gens; i++) { + _gens[i]->oop_iterate(mr, cl); + } +} + +void GenCollectedHeap::object_iterate(ObjectClosure* cl) { + for (int i = 0; i < _n_gens; i++) { + _gens[i]->object_iterate(cl); + } + perm_gen()->object_iterate(cl); +} + +void GenCollectedHeap::object_iterate_since_last_GC(ObjectClosure* cl) { + for (int i = 0; i < _n_gens; i++) { + _gens[i]->object_iterate_since_last_GC(cl); + } +} + +Space* GenCollectedHeap::space_containing(const void* addr) const { + for (int i = 0; i < _n_gens; i++) { + Space* res = _gens[i]->space_containing(addr); + if (res != NULL) return res; + } + Space* res = perm_gen()->space_containing(addr); + if (res != NULL) return res; + // Otherwise... + assert(false, "Could not find containing space"); + return NULL; +} + + +HeapWord* GenCollectedHeap::block_start(const void* addr) const { + assert(is_in_reserved(addr), "block_start of address outside of heap"); + for (int i = 0; i < _n_gens; i++) { + if (_gens[i]->is_in_reserved(addr)) { + assert(_gens[i]->is_in(addr), + "addr should be in allocated part of generation"); + return _gens[i]->block_start(addr); + } + } + if (perm_gen()->is_in_reserved(addr)) { + assert(perm_gen()->is_in(addr), + "addr should be in allocated part of perm gen"); + return perm_gen()->block_start(addr); + } + assert(false, "Some generation should contain the address"); + return NULL; +} + +size_t GenCollectedHeap::block_size(const HeapWord* addr) const { + assert(is_in_reserved(addr), "block_size of address outside of heap"); + for (int i = 0; i < _n_gens; i++) { + if (_gens[i]->is_in_reserved(addr)) { + assert(_gens[i]->is_in(addr), + "addr should be in allocated part of generation"); + return _gens[i]->block_size(addr); + } + } + if (perm_gen()->is_in_reserved(addr)) { + assert(perm_gen()->is_in(addr), + "addr should be in allocated part of perm gen"); + return perm_gen()->block_size(addr); + } + assert(false, "Some generation should contain the address"); + return 0; +} + +bool GenCollectedHeap::block_is_obj(const HeapWord* addr) const { + assert(is_in_reserved(addr), "block_is_obj of address outside of heap"); + assert(block_start(addr) == addr, "addr must be a block start"); + for (int i = 0; i < _n_gens; i++) { + if (_gens[i]->is_in_reserved(addr)) { + return _gens[i]->block_is_obj(addr); + } + } + if (perm_gen()->is_in_reserved(addr)) { + return perm_gen()->block_is_obj(addr); + } + assert(false, "Some generation should contain the address"); + return false; +} + +bool GenCollectedHeap::supports_tlab_allocation() const { + for (int i = 0; i < _n_gens; i += 1) { + if (_gens[i]->supports_tlab_allocation()) { + return true; + } + } + return false; +} + +size_t GenCollectedHeap::tlab_capacity(Thread* thr) const { + size_t result = 0; + for (int i = 0; i < _n_gens; i += 1) { + if (_gens[i]->supports_tlab_allocation()) { + result += _gens[i]->tlab_capacity(); + } + } + return result; +} + +size_t GenCollectedHeap::unsafe_max_tlab_alloc(Thread* thr) const { + size_t result = 0; + for (int i = 0; i < _n_gens; i += 1) { + if (_gens[i]->supports_tlab_allocation()) { + result += _gens[i]->unsafe_max_tlab_alloc(); + } + } + return result; +} + +HeapWord* GenCollectedHeap::allocate_new_tlab(size_t size) { + bool gc_overhead_limit_was_exceeded; + HeapWord* result = mem_allocate(size /* size */, + false /* is_large_noref */, + true /* is_tlab */, + &gc_overhead_limit_was_exceeded); + return result; +} + +// Requires "*prev_ptr" to be non-NULL. Deletes and a block of minimal size +// from the list headed by "*prev_ptr". +static ScratchBlock *removeSmallestScratch(ScratchBlock **prev_ptr) { + bool first = true; + size_t min_size = 0; // "first" makes this conceptually infinite. + ScratchBlock **smallest_ptr, *smallest; + ScratchBlock *cur = *prev_ptr; + while (cur) { + assert(*prev_ptr == cur, "just checking"); + if (first || cur->num_words < min_size) { + smallest_ptr = prev_ptr; + smallest = cur; + min_size = smallest->num_words; + first = false; + } + prev_ptr = &cur->next; + cur = cur->next; + } + smallest = *smallest_ptr; + *smallest_ptr = smallest->next; + return smallest; +} + +// Sort the scratch block list headed by res into decreasing size order, +// and set "res" to the result. +static void sort_scratch_list(ScratchBlock*& list) { + ScratchBlock* sorted = NULL; + ScratchBlock* unsorted = list; + while (unsorted) { + ScratchBlock *smallest = removeSmallestScratch(&unsorted); + smallest->next = sorted; + sorted = smallest; + } + list = sorted; +} + +ScratchBlock* GenCollectedHeap::gather_scratch(Generation* requestor, + size_t max_alloc_words) { + ScratchBlock* res = NULL; + for (int i = 0; i < _n_gens; i++) { + _gens[i]->contribute_scratch(res, requestor, max_alloc_words); + } + sort_scratch_list(res); + return res; +} + +size_t GenCollectedHeap::large_typearray_limit() { + return gen_policy()->large_typearray_limit(); +} + +class GenPrepareForVerifyClosure: public GenCollectedHeap::GenClosure { + void do_generation(Generation* gen) { + gen->prepare_for_verify(); + } +}; + +void GenCollectedHeap::prepare_for_verify() { + ensure_parsability(false); // no need to retire TLABs + GenPrepareForVerifyClosure blk; + generation_iterate(&blk, false); + perm_gen()->prepare_for_verify(); +} + + +void GenCollectedHeap::generation_iterate(GenClosure* cl, + bool old_to_young) { + if (old_to_young) { + for (int i = _n_gens-1; i >= 0; i--) { + cl->do_generation(_gens[i]); + } + } else { + for (int i = 0; i < _n_gens; i++) { + cl->do_generation(_gens[i]); + } + } +} + +void GenCollectedHeap::space_iterate(SpaceClosure* cl) { + for (int i = 0; i < _n_gens; i++) { + _gens[i]->space_iterate(cl, true); + } + perm_gen()->space_iterate(cl, true); +} + +bool GenCollectedHeap::is_maximal_no_gc() const { + for (int i = 0; i < _n_gens; i++) { // skip perm gen + if (!_gens[i]->is_maximal_no_gc()) { + return false; + } + } + return true; +} + +void GenCollectedHeap::save_marks() { + for (int i = 0; i < _n_gens; i++) { + _gens[i]->save_marks(); + } + perm_gen()->save_marks(); +} + +void GenCollectedHeap::compute_new_generation_sizes(int collectedGen) { + for (int i = 0; i <= collectedGen; i++) { + _gens[i]->compute_new_size(); + } +} + +GenCollectedHeap* GenCollectedHeap::heap() { + assert(_gch != NULL, "Uninitialized access to GenCollectedHeap::heap()"); + assert(_gch->kind() == CollectedHeap::GenCollectedHeap, "not a generational heap"); + return _gch; +} + + +void GenCollectedHeap::prepare_for_compaction() { + Generation* scanning_gen = _gens[_n_gens-1]; + // Start by compacting into same gen. + CompactPoint cp(scanning_gen, NULL, NULL); + while (scanning_gen != NULL) { + scanning_gen->prepare_for_compaction(&cp); + scanning_gen = prev_gen(scanning_gen); + } +} + +GCStats* GenCollectedHeap::gc_stats(int level) const { + return _gens[level]->gc_stats(); +} + +void GenCollectedHeap::verify(bool allow_dirty, bool silent) { + if (!silent) { + gclog_or_tty->print("permgen "); + } + perm_gen()->verify(allow_dirty); + for (int i = _n_gens-1; i >= 0; i--) { + Generation* g = _gens[i]; + if (!silent) { + gclog_or_tty->print(g->name()); + gclog_or_tty->print(" "); + } + g->verify(allow_dirty); + } + if (!silent) { + gclog_or_tty->print("remset "); + } + rem_set()->verify(); + if (!silent) { + gclog_or_tty->print("ref_proc "); + } + ReferenceProcessor::verify(); +} + +void GenCollectedHeap::print() const { print_on(tty); } +void GenCollectedHeap::print_on(outputStream* st) const { + for (int i = 0; i < _n_gens; i++) { + _gens[i]->print_on(st); + } + perm_gen()->print_on(st); +} + +void GenCollectedHeap::gc_threads_do(ThreadClosure* tc) const { + if (workers() != NULL) { + workers()->threads_do(tc); + } +#ifndef SERIALGC + if (UseConcMarkSweepGC) { + ConcurrentMarkSweepThread::threads_do(tc); + } +#endif // SERIALGC +} + +void GenCollectedHeap::print_gc_threads_on(outputStream* st) const { +#ifndef SERIALGC + if (UseParNewGC) { + workers()->print_worker_threads_on(st); + } + if (UseConcMarkSweepGC) { + ConcurrentMarkSweepThread::print_all_on(st); + } +#endif // SERIALGC +} + +void GenCollectedHeap::print_tracing_info() const { + if (TraceGen0Time) { + get_gen(0)->print_summary_info(); + } + if (TraceGen1Time) { + get_gen(1)->print_summary_info(); + } +} + +void GenCollectedHeap::print_heap_change(size_t prev_used) const { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print(" " SIZE_FORMAT + "->" SIZE_FORMAT + "(" SIZE_FORMAT ")", + prev_used, used(), capacity()); + } else { + gclog_or_tty->print(" " SIZE_FORMAT "K" + "->" SIZE_FORMAT "K" + "(" SIZE_FORMAT "K)", + prev_used / K, used() / K, capacity() / K); + } +} + +//New method to print perm gen info with PrintGCDetails flag +void GenCollectedHeap::print_perm_heap_change(size_t perm_prev_used) const { + gclog_or_tty->print(", [%s :", perm_gen()->short_name()); + perm_gen()->print_heap_change(perm_prev_used); + gclog_or_tty->print("]"); +} + +class GenGCPrologueClosure: public GenCollectedHeap::GenClosure { + private: + bool _full; + public: + void do_generation(Generation* gen) { + gen->gc_prologue(_full); + } + GenGCPrologueClosure(bool full) : _full(full) {}; +}; + +void GenCollectedHeap::gc_prologue(bool full) { + assert(InlineCacheBuffer::is_empty(), "should have cleaned up ICBuffer"); + + always_do_update_barrier = false; + // Fill TLAB's and such + CollectedHeap::accumulate_statistics_all_tlabs(); + ensure_parsability(true); // retire TLABs + + // Call allocation profiler + AllocationProfiler::iterate_since_last_gc(); + // Walk generations + GenGCPrologueClosure blk(full); + generation_iterate(&blk, false); // not old-to-young. + perm_gen()->gc_prologue(full); +}; + +class GenGCEpilogueClosure: public GenCollectedHeap::GenClosure { + private: + bool _full; + public: + void do_generation(Generation* gen) { + gen->gc_epilogue(_full); + } + GenGCEpilogueClosure(bool full) : _full(full) {}; +}; + +void GenCollectedHeap::gc_epilogue(bool full) { + // Remember if a partial collection of the heap failed, and + // we did a complete collection. + if (full && incremental_collection_will_fail()) { + set_last_incremental_collection_failed(); + } else { + clear_last_incremental_collection_failed(); + } + // Clear the flag, if set; the generation gc_epilogues will set the + // flag again if the condition persists despite the collection. + clear_incremental_collection_will_fail(); + +#ifdef COMPILER2 + assert(DerivedPointerTable::is_empty(), "derived pointer present"); + size_t actual_gap = pointer_delta((HeapWord*) (max_uintx-3), *(end_addr())); + guarantee(actual_gap > (size_t)FastAllocateSizeLimit, "inline allocation wraps"); +#endif /* COMPILER2 */ + + resize_all_tlabs(); + + GenGCEpilogueClosure blk(full); + generation_iterate(&blk, false); // not old-to-young. + perm_gen()->gc_epilogue(full); + + always_do_update_barrier = UseConcMarkSweepGC; +}; + +class GenEnsureParsabilityClosure: public GenCollectedHeap::GenClosure { + public: + void do_generation(Generation* gen) { + gen->ensure_parsability(); + } +}; + +void GenCollectedHeap::ensure_parsability(bool retire_tlabs) { + CollectedHeap::ensure_parsability(retire_tlabs); + GenEnsureParsabilityClosure ep_cl; + generation_iterate(&ep_cl, false); + perm_gen()->ensure_parsability(); +} + +oop GenCollectedHeap::handle_failed_promotion(Generation* gen, + oop obj, + size_t obj_size, + oop* ref) { + assert(obj_size == (size_t)obj->size(), "bad obj_size passed in"); + HeapWord* result = NULL; + + // First give each higher generation a chance to allocate the promoted object. + Generation* allocator = next_gen(gen); + if (allocator != NULL) { + do { + result = allocator->allocate(obj_size, false); + } while (result == NULL && (allocator = next_gen(allocator)) != NULL); + } + + if (result == NULL) { + // Then give gen and higher generations a chance to expand and allocate the + // object. + do { + result = gen->expand_and_allocate(obj_size, false); + } while (result == NULL && (gen = next_gen(gen)) != NULL); + } + + if (result != NULL) { + Copy::aligned_disjoint_words((HeapWord*)obj, result, obj_size); + } + return oop(result); +} + +class GenTimeOfLastGCClosure: public GenCollectedHeap::GenClosure { + jlong _time; // in ms + jlong _now; // in ms + + public: + GenTimeOfLastGCClosure(jlong now) : _time(now), _now(now) { } + + jlong time() { return _time; } + + void do_generation(Generation* gen) { + _time = MIN2(_time, gen->time_of_last_gc(_now)); + } +}; + +jlong GenCollectedHeap::millis_since_last_gc() { + jlong now = os::javaTimeMillis(); + GenTimeOfLastGCClosure tolgc_cl(now); + // iterate over generations getting the oldest + // time that a generation was collected + generation_iterate(&tolgc_cl, false); + tolgc_cl.do_generation(perm_gen()); + // XXX Despite the assert above, since javaTimeMillis() + // doesnot guarantee monotonically increasing return + // values (note, i didn't say "strictly monotonic"), + // we need to guard against getting back a time + // later than now. This should be fixed by basing + // on someting like gethrtime() which guarantees + // monotonicity. Note that cond_wait() is susceptible + // to a similar problem, because its interface is + // based on absolute time in the form of the + // system time's notion of UCT. See also 4506635 + // for yet another problem of similar nature. XXX + jlong retVal = now - tolgc_cl.time(); + if (retVal < 0) { + NOT_PRODUCT(warning("time warp: %d", retVal);) + return 0; + } + return retVal; +} diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.hpp b/hotspot/src/share/vm/memory/genCollectedHeap.hpp new file mode 100644 index 00000000000..b3cf2de0f4b --- /dev/null +++ b/hotspot/src/share/vm/memory/genCollectedHeap.hpp @@ -0,0 +1,491 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class SubTasksDone; + +// A "GenCollectedHeap" is a SharedHeap that uses generational +// collection. It is represented with a sequence of Generation's. +class GenCollectedHeap : public SharedHeap { + friend class GenCollectorPolicy; + friend class Generation; + friend class DefNewGeneration; + friend class TenuredGeneration; + friend class ConcurrentMarkSweepGeneration; + friend class CMSCollector; + friend class GenMarkSweep; + friend class VM_GenCollectForAllocation; + friend class VM_GenCollectFull; + friend class VM_GenCollectFullConcurrent; + friend class VM_GC_HeapInspection; + friend class VM_HeapDumper; + friend class HeapInspection; + friend class GCCauseSetter; + friend class VMStructs; +public: + enum SomeConstants { + max_gens = 10 + }; + + friend class VM_PopulateDumpSharedSpace; + + protected: + // Fields: + static GenCollectedHeap* _gch; + + private: + int _n_gens; + Generation* _gens[max_gens]; + GenerationSpec** _gen_specs; + + // The generational collector policy. + GenCollectorPolicy* _gen_policy; + + // If a generation would bail out of an incremental collection, + // it sets this flag. If the flag is set, satisfy_failed_allocation + // will attempt allocating in all generations before doing a full GC. + bool _incremental_collection_will_fail; + bool _last_incremental_collection_failed; + + // In support of ExplicitGCInvokesConcurrent functionality + unsigned int _full_collections_completed; + + // Data structure for claiming the (potentially) parallel tasks in + // (gen-specific) strong roots processing. + SubTasksDone* _gen_process_strong_tasks; + + // In block contents verification, the number of header words to skip + NOT_PRODUCT(static size_t _skip_header_HeapWords;) + + // GC is not allowed during the dump of the shared classes. Keep track + // of this in order to provide an reasonable error message when terminating. + bool _preloading_shared_classes; + +protected: + // Directs each generation up to and including "collectedGen" to recompute + // its desired size. + void compute_new_generation_sizes(int collectedGen); + + // Helper functions for allocation + HeapWord* attempt_allocation(size_t size, + bool is_tlab, + bool first_only); + + // Helper function for two callbacks below. + // Considers collection of the first max_level+1 generations. + void do_collection(bool full, + bool clear_all_soft_refs, + size_t size, + bool is_tlab, + int max_level); + + // Callback from VM_GenCollectForAllocation operation. + // This function does everything necessary/possible to satisfy an + // allocation request that failed in the youngest generation that should + // have handled it (including collection, expansion, etc.) + HeapWord* satisfy_failed_allocation(size_t size, bool is_tlab); + + // Callback from VM_GenCollectFull operation. + // Perform a full collection of the first max_level+1 generations. + void do_full_collection(bool clear_all_soft_refs, int max_level); + + // Does the "cause" of GC indicate that + // we absolutely __must__ clear soft refs? + bool must_clear_all_soft_refs(); + +public: + GenCollectedHeap(GenCollectorPolicy *policy); + + GCStats* gc_stats(int level) const; + + // Returns JNI_OK on success + virtual jint initialize(); + char* allocate(size_t alignment, PermanentGenerationSpec* perm_gen_spec, + size_t* _total_reserved, int* _n_covered_regions, + ReservedSpace* heap_rs); + + // Does operations required after initialization has been done. + void post_initialize(); + + // Initialize ("weak") refs processing support + virtual void ref_processing_init(); + + virtual CollectedHeap::Name kind() const { + return CollectedHeap::GenCollectedHeap; + } + + // The generational collector policy. + GenCollectorPolicy* gen_policy() const { return _gen_policy; } + + // Adaptive size policy + virtual AdaptiveSizePolicy* size_policy() { + return gen_policy()->size_policy(); + } + + size_t capacity() const; + size_t used() const; + + // Save the "used_region" for generations level and lower, + // and, if perm is true, for perm gen. + void save_used_regions(int level, bool perm); + + size_t max_capacity() const; + + HeapWord* mem_allocate(size_t size, + bool is_large_noref, + bool is_tlab, + bool* gc_overhead_limit_was_exceeded); + + // We may support a shared contiguous allocation area, if the youngest + // generation does. + bool supports_inline_contig_alloc() const; + HeapWord** top_addr() const; + HeapWord** end_addr() const; + + // Return an estimate of the maximum allocation that could be performed + // without triggering any collection activity. In a generational + // collector, for example, this is probably the largest allocation that + // could be supported in the youngest generation. It is "unsafe" because + // no locks are taken; the result should be treated as an approximation, + // not a guarantee. + size_t unsafe_max_alloc(); + + // Does this heap support heap inspection? (+PrintClassHistogram) + virtual bool supports_heap_inspection() const { return true; } + + // Perform a full collection of the heap; intended for use in implementing + // "System.gc". This implies as full a collection as the CollectedHeap + // supports. Caller does not hold the Heap_lock on entry. + void collect(GCCause::Cause cause); + + // This interface assumes that it's being called by the + // vm thread. It collects the heap assuming that the + // heap lock is already held and that we are executing in + // the context of the vm thread. + void collect_as_vm_thread(GCCause::Cause cause); + + // The same as above but assume that the caller holds the Heap_lock. + void collect_locked(GCCause::Cause cause); + + // Perform a full collection of the first max_level+1 generations. + // Mostly used for testing purposes. Caller does not hold the Heap_lock on entry. + void collect(GCCause::Cause cause, int max_level); + + // Returns "TRUE" iff "p" points into the allocated area of the heap. + // The methods is_in(), is_in_closed_subset() and is_in_youngest() may + // be expensive to compute in general, so, to prevent + // their inadvertent use in product jvm's, we restrict their use to + // assertion checking or verification only. + bool is_in(const void* p) const; + + // override + bool is_in_closed_subset(const void* p) const { + if (UseConcMarkSweepGC) { + return is_in_reserved(p); + } else { + return is_in(p); + } + } + + // Returns "TRUE" iff "p" points into the youngest generation. + bool is_in_youngest(void* p); + + // Iteration functions. + void oop_iterate(OopClosure* cl); + void oop_iterate(MemRegion mr, OopClosure* cl); + void object_iterate(ObjectClosure* cl); + void object_iterate_since_last_GC(ObjectClosure* cl); + Space* space_containing(const void* addr) const; + + // A CollectedHeap is divided into a dense sequence of "blocks"; that is, + // each address in the (reserved) heap is a member of exactly + // one block. The defining characteristic of a block is that it is + // possible to find its size, and thus to progress forward to the next + // block. (Blocks may be of different sizes.) Thus, blocks may + // represent Java objects, or they might be free blocks in a + // free-list-based heap (or subheap), as long as the two kinds are + // distinguishable and the size of each is determinable. + + // Returns the address of the start of the "block" that contains the + // address "addr". We say "blocks" instead of "object" since some heaps + // may not pack objects densely; a chunk may either be an object or a + // non-object. + virtual HeapWord* block_start(const void* addr) const; + + // Requires "addr" to be the start of a chunk, and returns its size. + // "addr + size" is required to be the start of a new chunk, or the end + // of the active area of the heap. Assumes (and verifies in non-product + // builds) that addr is in the allocated part of the heap and is + // the start of a chunk. + virtual size_t block_size(const HeapWord* addr) const; + + // Requires "addr" to be the start of a block, and returns "TRUE" iff + // the block is an object. Assumes (and verifies in non-product + // builds) that addr is in the allocated part of the heap and is + // the start of a chunk. + virtual bool block_is_obj(const HeapWord* addr) const; + + // Section on TLAB's. + virtual bool supports_tlab_allocation() const; + virtual size_t tlab_capacity(Thread* thr) const; + virtual size_t unsafe_max_tlab_alloc(Thread* thr) const; + virtual HeapWord* allocate_new_tlab(size_t size); + + // The "requestor" generation is performing some garbage collection + // action for which it would be useful to have scratch space. The + // requestor promises to allocate no more than "max_alloc_words" in any + // older generation (via promotion say.) Any blocks of space that can + // be provided are returned as a list of ScratchBlocks, sorted by + // decreasing size. + ScratchBlock* gather_scratch(Generation* requestor, size_t max_alloc_words); + + size_t large_typearray_limit(); + + // Ensure parsability: override + virtual void ensure_parsability(bool retire_tlabs); + + // Time in ms since the longest time a collector ran in + // in any generation. + virtual jlong millis_since_last_gc(); + + // Total number of full collections completed. + unsigned int total_full_collections_completed() { + assert(_full_collections_completed <= _total_full_collections, + "Can't complete more collections than were started"); + return _full_collections_completed; + } + + // Update above counter, as appropriate, at the end of a stop-world GC cycle + unsigned int update_full_collections_completed(); + // Update above counter, as appropriate, at the end of a concurrent GC cycle + unsigned int update_full_collections_completed(unsigned int count); + + // Update "time of last gc" for all constituent generations + // to "now". + void update_time_of_last_gc(jlong now) { + for (int i = 0; i < _n_gens; i++) { + _gens[i]->update_time_of_last_gc(now); + } + perm_gen()->update_time_of_last_gc(now); + } + + // Update the gc statistics for each generation. + // "level" is the level of the lastest collection + void update_gc_stats(int current_level, bool full) { + for (int i = 0; i < _n_gens; i++) { + _gens[i]->update_gc_stats(current_level, full); + } + perm_gen()->update_gc_stats(current_level, full); + } + + // Override. + bool no_gc_in_progress() { return !is_gc_active(); } + + // Override. + void prepare_for_verify(); + + // Override. + void verify(bool allow_dirty, bool silent); + + // Override. + void print() const; + void print_on(outputStream* st) const; + virtual void print_gc_threads_on(outputStream* st) const; + virtual void gc_threads_do(ThreadClosure* tc) const; + virtual void print_tracing_info() const; + + // PrintGC, PrintGCDetails support + void print_heap_change(size_t prev_used) const; + void print_perm_heap_change(size_t perm_prev_used) const; + + // The functions below are helper functions that a subclass of + // "CollectedHeap" can use in the implementation of its virtual + // functions. + + class GenClosure : public StackObj { + public: + virtual void do_generation(Generation* gen) = 0; + }; + + // Apply "cl.do_generation" to all generations in the heap (not including + // the permanent generation). If "old_to_young" determines the order. + void generation_iterate(GenClosure* cl, bool old_to_young); + + void space_iterate(SpaceClosure* cl); + + // Return "true" if all generations (but perm) have reached the + // maximal committed limit that they can reach, without a garbage + // collection. + virtual bool is_maximal_no_gc() const; + + // Return the generation before "gen", or else NULL. + Generation* prev_gen(Generation* gen) const { + int l = gen->level(); + if (l == 0) return NULL; + else return _gens[l-1]; + } + + // Return the generation after "gen", or else NULL. + Generation* next_gen(Generation* gen) const { + int l = gen->level() + 1; + if (l == _n_gens) return NULL; + else return _gens[l]; + } + + Generation* get_gen(int i) const { + if (i >= 0 && i < _n_gens) + return _gens[i]; + else + return NULL; + } + + int n_gens() const { + assert(_n_gens == gen_policy()->number_of_generations(), "Sanity"); + return _n_gens; + } + + // Convenience function to be used in situations where the heap type can be + // asserted to be this type. + static GenCollectedHeap* heap(); + + void set_par_threads(int t); + + + // Invoke the "do_oop" method of one of the closures "not_older_gens" + // or "older_gens" on root locations for the generation at + // "level". (The "older_gens" closure is used for scanning references + // from older generations; "not_older_gens" is used everywhere else.) + // If "younger_gens_as_roots" is false, younger generations are + // not scanned as roots; in this case, the caller must be arranging to + // scan the younger generations itself. (For example, a generation might + // explicitly mark reachable objects in younger generations, to avoid + // excess storage retention.) If "collecting_perm_gen" is false, then + // roots that may only contain references to permGen objects are not + // scanned. The "so" argument determines which of the roots + // the closure is applied to: + // "SO_None" does none; + // "SO_AllClasses" applies the closure to all entries in the SystemDictionary; + // "SO_SystemClasses" to all the "system" classes and loaders; + // "SO_Symbols_and_Strings" applies the closure to all entries in + // SymbolsTable and StringTable. + void gen_process_strong_roots(int level, bool younger_gens_as_roots, + bool collecting_perm_gen, + SharedHeap::ScanningOption so, + OopsInGenClosure* older_gens, + OopsInGenClosure* not_older_gens); + + // Apply "blk" to all the weak roots of the system. These include + // JNI weak roots, the code cache, system dictionary, symbol table, + // string table, and referents of reachable weak refs. + void gen_process_weak_roots(OopClosure* root_closure, + OopClosure* non_root_closure); + + // Set the saved marks of generations, if that makes sense. + // In particular, if any generation might iterate over the oops + // in other generations, it should call this method. + void save_marks(); + + // Apply "cur->do_oop" or "older->do_oop" to all the oops in objects + // allocated since the last call to save_marks in generations at or above + // "level" (including the permanent generation.) The "cur" closure is + // applied to references in the generation at "level", and the "older" + // closure to older (and permanent) generations. +#define GCH_SINCE_SAVE_MARKS_ITERATE_DECL(OopClosureType, nv_suffix) \ + void oop_since_save_marks_iterate(int level, \ + OopClosureType* cur, \ + OopClosureType* older); + + ALL_SINCE_SAVE_MARKS_CLOSURES(GCH_SINCE_SAVE_MARKS_ITERATE_DECL) + +#undef GCH_SINCE_SAVE_MARKS_ITERATE_DECL + + // Returns "true" iff no allocations have occurred in any generation at + // "level" or above (including the permanent generation) since the last + // call to "save_marks". + bool no_allocs_since_save_marks(int level); + + // If a generation bails out of an incremental collection, + // it sets this flag. + bool incremental_collection_will_fail() { + return _incremental_collection_will_fail; + } + void set_incremental_collection_will_fail() { + _incremental_collection_will_fail = true; + } + void clear_incremental_collection_will_fail() { + _incremental_collection_will_fail = false; + } + + bool last_incremental_collection_failed() const { + return _last_incremental_collection_failed; + } + void set_last_incremental_collection_failed() { + _last_incremental_collection_failed = true; + } + void clear_last_incremental_collection_failed() { + _last_incremental_collection_failed = false; + } + + // Promotion of obj into gen failed. Try to promote obj to higher non-perm + // gens in ascending order; return the new location of obj if successful. + // Otherwise, try expand-and-allocate for obj in each generation starting at + // gen; return the new location of obj if successful. Otherwise, return NULL. + oop handle_failed_promotion(Generation* gen, + oop obj, + size_t obj_size, + oop* ref); + +private: + // Accessor for memory state verification support + NOT_PRODUCT( + static size_t skip_header_HeapWords() { return _skip_header_HeapWords; } + ) + + // Override + void check_for_non_bad_heap_word_value(HeapWord* addr, + size_t size) PRODUCT_RETURN; + + // For use by mark-sweep. As implemented, mark-sweep-compact is global + // in an essential way: compaction is performed across generations, by + // iterating over spaces. + void prepare_for_compaction(); + + // Perform a full collection of the first max_level+1 generations. + // This is the low level interface used by the public versions of + // collect() and collect_locked(). Caller holds the Heap_lock on entry. + void collect_locked(GCCause::Cause cause, int max_level); + + // Returns success or failure. + bool create_cms_collector(); + + // In support of ExplicitGCInvokesConcurrent functionality + bool should_do_concurrent_full_gc(GCCause::Cause cause); + void collect_mostly_concurrent(GCCause::Cause cause); + +protected: + virtual void gc_prologue(bool full); + virtual void gc_epilogue(bool full); + +public: + virtual void preload_and_dump(TRAPS) KERNEL_RETURN; +}; diff --git a/hotspot/src/share/vm/memory/genMarkSweep.cpp b/hotspot/src/share/vm/memory/genMarkSweep.cpp new file mode 100644 index 00000000000..35e074403a6 --- /dev/null +++ b/hotspot/src/share/vm/memory/genMarkSweep.cpp @@ -0,0 +1,392 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_genMarkSweep.cpp.incl" + +void GenMarkSweep::invoke_at_safepoint(int level, ReferenceProcessor* rp, + bool clear_all_softrefs) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint"); + + // hook up weak ref data so it can be used during Mark-Sweep + assert(ref_processor() == NULL, "no stomping"); + _ref_processor = rp; + assert(rp != NULL, "should be non-NULL"); + + TraceTime t1("Full GC", PrintGC && !PrintGCDetails, true, gclog_or_tty); + + // When collecting the permanent generation methodOops may be moving, + // so we either have to flush all bcp data or convert it into bci. + CodeCache::gc_prologue(); + Threads::gc_prologue(); + + // Increment the invocation count for the permanent generation, since it is + // implicitly collected whenever we do a full mark sweep collection. + GenCollectedHeap* gch = GenCollectedHeap::heap(); + gch->perm_gen()->stat_record()->invocations++; + + // Capture heap size before collection for printing. + size_t gch_prev_used = gch->used(); + + // Some of the card table updates below assume that the perm gen is + // also being collected. + assert(level == gch->n_gens() - 1, + "All generations are being collected, ergo perm gen too."); + + // Capture used regions for each generation that will be + // subject to collection, so that card table adjustments can + // be made intelligently (see clear / invalidate further below). + gch->save_used_regions(level, true /* perm */); + + allocate_stacks(); + + mark_sweep_phase1(level, clear_all_softrefs); + + mark_sweep_phase2(); + + // Don't add any more derived pointers during phase3 + COMPILER2_PRESENT(assert(DerivedPointerTable::is_active(), "Sanity")); + COMPILER2_PRESENT(DerivedPointerTable::set_active(false)); + + mark_sweep_phase3(level); + + VALIDATE_MARK_SWEEP_ONLY( + if (ValidateMarkSweep) { + guarantee(_root_refs_stack->length() == 0, + "should be empty by now"); + } + ) + + mark_sweep_phase4(); + + VALIDATE_MARK_SWEEP_ONLY( + if (ValidateMarkSweep) { + guarantee(_live_oops->length() == _live_oops_moved_to->length(), + "should be the same size"); + } + ) + + restore_marks(); + + // Set saved marks for allocation profiler (and other things? -- dld) + // (Should this be in general part?) + gch->save_marks(); + + deallocate_stacks(); + + // If compaction completely evacuated all generations younger than this + // one, then we can clear the card table. Otherwise, we must invalidate + // it (consider all cards dirty). In the future, we might consider doing + // compaction within generations only, and doing card-table sliding. + bool all_empty = true; + for (int i = 0; all_empty && i < level; i++) { + Generation* g = gch->get_gen(i); + all_empty = all_empty && gch->get_gen(i)->used() == 0; + } + GenRemSet* rs = gch->rem_set(); + // Clear/invalidate below make use of the "prev_used_regions" saved earlier. + if (all_empty) { + // We've evacuated all generations below us. + Generation* g = gch->get_gen(level); + rs->clear_into_younger(g, true /* perm */); + } else { + // Invalidate the cards corresponding to the currently used + // region and clear those corresponding to the evacuated region + // of all generations just collected (i.e. level and younger). + rs->invalidate_or_clear(gch->get_gen(level), + true /* younger */, + true /* perm */); + } + + Threads::gc_epilogue(); + CodeCache::gc_epilogue(); + + if (PrintGC && !PrintGCDetails) { + gch->print_heap_change(gch_prev_used); + } + + // refs processing: clean slate + _ref_processor = NULL; + + // Update heap occupancy information which is used as + // input to soft ref clearing policy at the next gc. + Universe::update_heap_info_at_gc(); + + // Update time of last gc for all generations we collected + // (which curently is all the generations in the heap). + gch->update_time_of_last_gc(os::javaTimeMillis()); +} + +void GenMarkSweep::allocate_stacks() { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + // Scratch request on behalf of oldest generation; will do no + // allocation. + ScratchBlock* scratch = gch->gather_scratch(gch->_gens[gch->_n_gens-1], 0); + + // $$$ To cut a corner, we'll only use the first scratch block, and then + // revert to malloc. + if (scratch != NULL) { + _preserved_count_max = + scratch->num_words * HeapWordSize / sizeof(PreservedMark); + } else { + _preserved_count_max = 0; + } + + _preserved_marks = (PreservedMark*)scratch; + _preserved_count = 0; + _preserved_mark_stack = NULL; + _preserved_oop_stack = NULL; + + _marking_stack = new (ResourceObj::C_HEAP) GrowableArray(4000, true); + + int size = SystemDictionary::number_of_classes() * 2; + _revisit_klass_stack = new (ResourceObj::C_HEAP) GrowableArray(size, true); + +#ifdef VALIDATE_MARK_SWEEP + if (ValidateMarkSweep) { + _root_refs_stack = new (ResourceObj::C_HEAP) GrowableArray(100, true); + _other_refs_stack = new (ResourceObj::C_HEAP) GrowableArray(100, true); + _adjusted_pointers = new (ResourceObj::C_HEAP) GrowableArray(100, true); + _live_oops = new (ResourceObj::C_HEAP) GrowableArray(100, true); + _live_oops_moved_to = new (ResourceObj::C_HEAP) GrowableArray(100, true); + _live_oops_size = new (ResourceObj::C_HEAP) GrowableArray(100, true); + } + if (RecordMarkSweepCompaction) { + if (_cur_gc_live_oops == NULL) { + _cur_gc_live_oops = new(ResourceObj::C_HEAP) GrowableArray(100, true); + _cur_gc_live_oops_moved_to = new(ResourceObj::C_HEAP) GrowableArray(100, true); + _cur_gc_live_oops_size = new(ResourceObj::C_HEAP) GrowableArray(100, true); + _last_gc_live_oops = new(ResourceObj::C_HEAP) GrowableArray(100, true); + _last_gc_live_oops_moved_to = new(ResourceObj::C_HEAP) GrowableArray(100, true); + _last_gc_live_oops_size = new(ResourceObj::C_HEAP) GrowableArray(100, true); + } else { + _cur_gc_live_oops->clear(); + _cur_gc_live_oops_moved_to->clear(); + _cur_gc_live_oops_size->clear(); + } + } +#endif +} + + +void GenMarkSweep::deallocate_stacks() { + if (_preserved_oop_stack) { + delete _preserved_mark_stack; + _preserved_mark_stack = NULL; + delete _preserved_oop_stack; + _preserved_oop_stack = NULL; + } + + delete _marking_stack; + delete _revisit_klass_stack; + +#ifdef VALIDATE_MARK_SWEEP + if (ValidateMarkSweep) { + delete _root_refs_stack; + delete _other_refs_stack; + delete _adjusted_pointers; + delete _live_oops; + delete _live_oops_size; + delete _live_oops_moved_to; + _live_oops_index = 0; + _live_oops_index_at_perm = 0; + } +#endif +} + +void GenMarkSweep::mark_sweep_phase1(int level, + bool clear_all_softrefs) { + // Recursively traverse all live objects and mark them + EventMark m("1 mark object"); + TraceTime tm("phase 1", PrintGC && Verbose, true, gclog_or_tty); + trace(" 1"); + + VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false)); + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + + // Because follow_root_closure is created statically, cannot + // use OopsInGenClosure constructor which takes a generation, + // as the Universe has not been created when the static constructors + // are run. + follow_root_closure.set_orig_generation(gch->get_gen(level)); + + gch->gen_process_strong_roots(level, + false, // Younger gens are not roots. + true, // Collecting permanent generation. + SharedHeap::SO_SystemClasses, + &follow_root_closure, &follow_root_closure); + + // Process reference objects found during marking + { + ReferencePolicy *soft_ref_policy; + if (clear_all_softrefs) { + soft_ref_policy = new AlwaysClearPolicy(); + } else { +#ifdef COMPILER2 + soft_ref_policy = new LRUMaxHeapPolicy(); +#else + soft_ref_policy = new LRUCurrentHeapPolicy(); +#endif // COMPILER2 + } + assert(soft_ref_policy != NULL,"No soft reference policy"); + ref_processor()->process_discovered_references( + soft_ref_policy, &is_alive, &keep_alive, + &follow_stack_closure, NULL); + } + + // Follow system dictionary roots and unload classes + bool purged_class = SystemDictionary::do_unloading(&is_alive); + + // Follow code cache roots + CodeCache::do_unloading(&is_alive, &keep_alive, purged_class); + follow_stack(); // Flush marking stack + + // Update subklass/sibling/implementor links of live klasses + follow_weak_klass_links(); + assert(_marking_stack->is_empty(), "just drained"); + + // Visit symbol and interned string tables and delete unmarked oops + SymbolTable::unlink(&is_alive); + StringTable::unlink(&is_alive); + + assert(_marking_stack->is_empty(), "stack should be empty by now"); +} + + +void GenMarkSweep::mark_sweep_phase2() { + // Now all live objects are marked, compute the new object addresses. + + // It is imperative that we traverse perm_gen LAST. If dead space is + // allowed a range of dead object may get overwritten by a dead int + // array. If perm_gen is not traversed last a klassOop may get + // overwritten. This is fine since it is dead, but if the class has dead + // instances we have to skip them, and in order to find their size we + // need the klassOop! + // + // It is not required that we traverse spaces in the same order in + // phase2, phase3 and phase4, but the ValidateMarkSweep live oops + // tracking expects us to do so. See comment under phase4. + + GenCollectedHeap* gch = GenCollectedHeap::heap(); + Generation* pg = gch->perm_gen(); + + EventMark m("2 compute new addresses"); + TraceTime tm("phase 2", PrintGC && Verbose, true, gclog_or_tty); + trace("2"); + + VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false)); + + gch->prepare_for_compaction(); + + VALIDATE_MARK_SWEEP_ONLY(_live_oops_index_at_perm = _live_oops_index); + CompactPoint perm_cp(pg, NULL, NULL); + pg->prepare_for_compaction(&perm_cp); +} + +class GenAdjustPointersClosure: public GenCollectedHeap::GenClosure { +public: + void do_generation(Generation* gen) { + gen->adjust_pointers(); + } +}; + +void GenMarkSweep::mark_sweep_phase3(int level) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + Generation* pg = gch->perm_gen(); + + // Adjust the pointers to reflect the new locations + EventMark m("3 adjust pointers"); + TraceTime tm("phase 3", PrintGC && Verbose, true, gclog_or_tty); + trace("3"); + + VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false)); + + // Needs to be done before the system dictionary is adjusted. + pg->pre_adjust_pointers(); + + // Because the two closures below are created statically, cannot + // use OopsInGenClosure constructor which takes a generation, + // as the Universe has not been created when the static constructors + // are run. + adjust_root_pointer_closure.set_orig_generation(gch->get_gen(level)); + adjust_pointer_closure.set_orig_generation(gch->get_gen(level)); + + gch->gen_process_strong_roots(level, + false, // Younger gens are not roots. + true, // Collecting permanent generation. + SharedHeap::SO_AllClasses, + &adjust_root_pointer_closure, + &adjust_root_pointer_closure); + + // Now adjust pointers in remaining weak roots. (All of which should + // have been cleared if they pointed to non-surviving objects.) + gch->gen_process_weak_roots(&adjust_root_pointer_closure, + &adjust_pointer_closure); + + adjust_marks(); + GenAdjustPointersClosure blk; + gch->generation_iterate(&blk, true); + pg->adjust_pointers(); +} + +class GenCompactClosure: public GenCollectedHeap::GenClosure { +public: + void do_generation(Generation* gen) { + gen->compact(); + } +}; + +void GenMarkSweep::mark_sweep_phase4() { + // All pointers are now adjusted, move objects accordingly + + // It is imperative that we traverse perm_gen first in phase4. All + // classes must be allocated earlier than their instances, and traversing + // perm_gen first makes sure that all klassOops have moved to their new + // location before any instance does a dispatch through it's klass! + + // The ValidateMarkSweep live oops tracking expects us to traverse spaces + // in the same order in phase2, phase3 and phase4. We don't quite do that + // here (perm_gen first rather than last), so we tell the validate code + // to use a higher index (saved from phase2) when verifying perm_gen. + GenCollectedHeap* gch = GenCollectedHeap::heap(); + Generation* pg = gch->perm_gen(); + + EventMark m("4 compact heap"); + TraceTime tm("phase 4", PrintGC && Verbose, true, gclog_or_tty); + trace("4"); + + VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(true)); + + pg->compact(); + + VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false)); + + GenCompactClosure blk; + gch->generation_iterate(&blk, true); + + VALIDATE_MARK_SWEEP_ONLY(compaction_complete()); + + pg->post_compact(); // Shared spaces verification. +} diff --git a/hotspot/src/share/vm/memory/genMarkSweep.hpp b/hotspot/src/share/vm/memory/genMarkSweep.hpp new file mode 100644 index 00000000000..c2c43704fcc --- /dev/null +++ b/hotspot/src/share/vm/memory/genMarkSweep.hpp @@ -0,0 +1,45 @@ +/* + * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class GenMarkSweep : public MarkSweep { + friend class VM_MarkSweep; + public: + static void invoke_at_safepoint(int level, ReferenceProcessor* rp, + bool clear_all_softrefs); + + private: + + // Mark live objects + static void mark_sweep_phase1(int level, bool clear_all_softrefs); + // Calculate new addresses + static void mark_sweep_phase2(); + // Update pointers + static void mark_sweep_phase3(int level); + // Move objects to new positions + static void mark_sweep_phase4(); + + // Temporary data structures for traversal and storing/restoring marks + static void allocate_stacks(); + static void deallocate_stacks(); +}; diff --git a/hotspot/src/share/vm/memory/genOopClosures.hpp b/hotspot/src/share/vm/memory/genOopClosures.hpp new file mode 100644 index 00000000000..137482c3cca --- /dev/null +++ b/hotspot/src/share/vm/memory/genOopClosures.hpp @@ -0,0 +1,149 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Generation; +class HeapWord; +class CardTableRS; +class CardTableModRefBS; +class DefNewGeneration; + +// Closure for iterating roots from a particular generation +// Note: all classes deriving from this MUST call this do_barrier +// method at the end of their own do_oop method! +// Note: no do_oop defined, this is an abstract class. + +class OopsInGenClosure : public OopClosure { + private: + Generation* _orig_gen; // generation originally set in ctor + Generation* _gen; // generation being scanned + + protected: + // Some subtypes need access. + HeapWord* _gen_boundary; // start of generation + CardTableRS* _rs; // remembered set + + // For assertions + Generation* generation() { return _gen; } + CardTableRS* rs() { return _rs; } + + // Derived classes that modify oops so that they might be old-to-young + // pointers must call the method below. + void do_barrier(oop* p); + + public: + OopsInGenClosure() : OopClosure(NULL), + _orig_gen(NULL), _gen(NULL), _gen_boundary(NULL), _rs(NULL) {}; + + OopsInGenClosure(Generation* gen); + void set_generation(Generation* gen); + + void reset_generation() { _gen = _orig_gen; } + + // Problem with static closures: must have _gen_boundary set at some point, + // but cannot do this until after the heap is initialized. + void set_orig_generation(Generation* gen) { + _orig_gen = gen; + set_generation(gen); + } + + HeapWord* gen_boundary() { return _gen_boundary; } +}; + +// Closure for scanning DefNewGeneration. +// +// This closure will perform barrier store calls for ALL +// pointers in scanned oops. +class ScanClosure: public OopsInGenClosure { +protected: + DefNewGeneration* _g; + HeapWord* _boundary; + bool _gc_barrier; +public: + ScanClosure(DefNewGeneration* g, bool gc_barrier); + void do_oop(oop* p); + void do_oop_nv(oop* p); + bool do_header() { return false; } + Prefetch::style prefetch_style() { + return Prefetch::do_write; + } +}; + +// Closure for scanning DefNewGeneration. +// +// This closure only performs barrier store calls on +// pointers into the DefNewGeneration. This is less +// precise, but faster, than a ScanClosure +class FastScanClosure: public OopsInGenClosure { +protected: + DefNewGeneration* _g; + HeapWord* _boundary; + bool _gc_barrier; +public: + FastScanClosure(DefNewGeneration* g, bool gc_barrier); + void do_oop(oop* p); + void do_oop_nv(oop* p); + bool do_header() { return false; } + Prefetch::style prefetch_style() { + return Prefetch::do_write; + } +}; + +class FilteringClosure: public OopClosure { + HeapWord* _boundary; + OopClosure* _cl; +public: + FilteringClosure(HeapWord* boundary, OopClosure* cl) : + OopClosure(cl->_ref_processor), _boundary(boundary), + _cl(cl) {} + void do_oop(oop* p); + void do_oop_nv(oop* p) { + oop obj = *p; + if ((HeapWord*)obj < _boundary && obj != NULL) { + _cl->do_oop(p); + } + } + bool do_header() { return false; } +}; + +// Closure for scanning DefNewGeneration's weak references. +// NOTE: very much like ScanClosure but not derived from +// OopsInGenClosure -- weak references are processed all +// at once, with no notion of which generation they were in. +class ScanWeakRefClosure: public OopClosure { +protected: + DefNewGeneration* _g; + HeapWord* _boundary; +public: + ScanWeakRefClosure(DefNewGeneration* g); + void do_oop(oop* p); + void do_oop_nv(oop* p); +}; + +class VerifyOopClosure: public OopClosure { +public: + void do_oop(oop* p) { + guarantee((*p)->is_oop_or_null(), "invalid oop"); + } + static VerifyOopClosure verify_oop; +}; diff --git a/hotspot/src/share/vm/memory/genOopClosures.inline.hpp b/hotspot/src/share/vm/memory/genOopClosures.inline.hpp new file mode 100644 index 00000000000..3bbe7652792 --- /dev/null +++ b/hotspot/src/share/vm/memory/genOopClosures.inline.hpp @@ -0,0 +1,119 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline OopsInGenClosure::OopsInGenClosure(Generation* gen) : + OopClosure(gen->ref_processor()), _orig_gen(gen), _rs(NULL) { + set_generation(gen); +} + +inline void OopsInGenClosure::set_generation(Generation* gen) { + _gen = gen; + _gen_boundary = _gen->reserved().start(); + // Barrier set for the heap, must be set after heap is initialized + if (_rs == NULL) { + GenRemSet* rs = SharedHeap::heap()->rem_set(); + assert(rs->rs_kind() == GenRemSet::CardTable, "Wrong rem set kind"); + _rs = (CardTableRS*)rs; + } +} + +inline void OopsInGenClosure::do_barrier(oop* p) { + assert(generation()->is_in_reserved(p), "expected ref in generation"); + oop obj = *p; + assert(obj != NULL, "expected non-null object"); + // If p points to a younger generation, mark the card. + if ((HeapWord*)obj < _gen_boundary) { + _rs->inline_write_ref_field_gc(p, obj); + } +} + +// NOTE! Any changes made here should also be made +// in FastScanClosure::do_oop(); +inline void ScanClosure::do_oop(oop* p) { + oop obj = *p; + // Should we copy the obj? + if (obj != NULL) { + if ((HeapWord*)obj < _boundary) { + assert(!_g->to()->is_in_reserved(obj), "Scanning field twice?"); + if (obj->is_forwarded()) { + *p = obj->forwardee(); + } else { + *p = _g->copy_to_survivor_space(obj, p); + } + } + if (_gc_barrier) { + // Now call parent closure + do_barrier(p); + } + } +} + +inline void ScanClosure::do_oop_nv(oop* p) { + ScanClosure::do_oop(p); +} + +// NOTE! Any changes made here should also be made +// in ScanClosure::do_oop(); +inline void FastScanClosure::do_oop(oop* p) { + oop obj = *p; + // Should we copy the obj? + if (obj != NULL) { + if ((HeapWord*)obj < _boundary) { + assert(!_g->to()->is_in_reserved(obj), "Scanning field twice?"); + if (obj->is_forwarded()) { + *p = obj->forwardee(); + } else { + *p = _g->copy_to_survivor_space(obj, p); + } + if (_gc_barrier) { + // Now call parent closure + do_barrier(p); + } + } + } +} + +inline void FastScanClosure::do_oop_nv(oop* p) { + FastScanClosure::do_oop(p); +} + +// Note similarity to ScanClosure; the difference is that +// the barrier set is taken care of outside this closure. +inline void ScanWeakRefClosure::do_oop(oop* p) { + oop obj = *p; + assert (obj != NULL, "null weak reference?"); + // weak references are sometimes scanned twice; must check + // that to-space doesn't already contain this object + if ((HeapWord*)obj < _boundary && !_g->to()->is_in_reserved(obj)) { + if (obj->is_forwarded()) { + *p = obj->forwardee(); + } else { + *p = _g->copy_to_survivor_space(obj, p); + } + } +} + +inline void ScanWeakRefClosure::do_oop_nv(oop* p) { + ScanWeakRefClosure::do_oop(p); +} diff --git a/hotspot/src/share/vm/memory/genRemSet.cpp b/hotspot/src/share/vm/memory/genRemSet.cpp new file mode 100644 index 00000000000..79b16dce0b8 --- /dev/null +++ b/hotspot/src/share/vm/memory/genRemSet.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This kind of "BarrierSet" allows a "CollectedHeap" to detect and +// enumerate ref fields that have been modified (since the last +// enumeration.) + +# include "incls/_precompiled.incl" +# include "incls/_genRemSet.cpp.incl" + +uintx GenRemSet::max_alignment_constraint(Name nm) { + switch (nm) { + case GenRemSet::CardTable: + return CardTableRS::ct_max_alignment_constraint(); + default: + guarantee(false, "Unrecognized GenRemSet type."); + return (0); // Make Windows compiler happy + } +} diff --git a/hotspot/src/share/vm/memory/genRemSet.hpp b/hotspot/src/share/vm/memory/genRemSet.hpp new file mode 100644 index 00000000000..5de388cda52 --- /dev/null +++ b/hotspot/src/share/vm/memory/genRemSet.hpp @@ -0,0 +1,121 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A GenRemSet provides ways of iterating over pointers accross generations. +// (This is especially useful for older-to-younger.) + +class Generation; +class BarrierSet; +class OopsInGenClosure; +class CardTableRS; + +class GenRemSet: public CHeapObj { + friend class Generation; + + BarrierSet* _bs; + +public: + enum Name { + CardTable, + Other + }; + + GenRemSet(BarrierSet * bs) : _bs(bs) {} + + virtual Name rs_kind() = 0; + + // These are for dynamic downcasts. Unfortunately that it names the + // possible subtypes (but not that they are subtypes!) Return NULL if + // the cast is invalide. + virtual CardTableRS* as_CardTableRS() { return NULL; } + + // Return the barrier set associated with "this." + BarrierSet* bs() { return _bs; } + + // Do any (sequential) processing necessary to prepare for (possibly + // "parallel", if that arg is true) calls to younger_refs_iterate. + virtual void prepare_for_younger_refs_iterate(bool parallel) = 0; + + // Apply the "do_oop" method of "blk" to (exactly) all oop locations + // 1) that are in objects allocated in "g" at the time of the last call + // to "save_Marks", and + // 2) that point to objects in younger generations. + virtual void younger_refs_iterate(Generation* g, OopsInGenClosure* blk) = 0; + + virtual void younger_refs_in_space_iterate(Space* sp, + OopsInGenClosure* cl) = 0; + + // This method is used to notify the remembered set that "new_val" has + // been written into "field" by the garbage collector. + void write_ref_field_gc(oop* field, oop new_val); +protected: + virtual void write_ref_field_gc_work(oop* field, oop new_val) = 0; +public: + + // A version of the above suitable for use by parallel collectors. + virtual void write_ref_field_gc_par(oop* field, oop new_val) = 0; + + // Resize one of the regions covered by the remembered set. + virtual void resize_covered_region(MemRegion new_region) = 0; + + // If the rem set imposes any alignment restrictions on boundaries + // within the heap, this function tells whether they are met. + virtual bool is_aligned(HeapWord* addr) = 0; + + // If the RS (or BS) imposes an aligment constraint on maximum heap size. + // (This must be static, and dispatch on "nm", because it is called + // before an RS is created.) + static uintx max_alignment_constraint(Name nm); + + virtual void verify() = 0; + + // Verify that the remembered set has no entries for + // the heap interval denoted by mr. + virtual void verify_empty(MemRegion mr) = 0; + + // If appropriate, print some information about the remset on "tty". + virtual void print() {} + + // Informs the RS that the given memregion contains no references to + // younger generations. + virtual void clear(MemRegion mr) = 0; + + // Informs the RS that there are no references to generations + // younger than gen from generations gen and older. + // The parameter clear_perm indicates if the perm_gen's + // remembered set should also be processed/cleared. + virtual void clear_into_younger(Generation* gen, bool clear_perm) = 0; + + // Informs the RS that refs in the given "mr" may have changed + // arbitrarily, and therefore may contain old-to-young pointers. + virtual void invalidate(MemRegion mr) = 0; + + // Informs the RS that refs in this generation + // may have changed arbitrarily, and therefore may contain + // old-to-young pointers in arbitrary locations. The parameter + // younger indicates if the same should be done for younger generations + // as well. The parameter perm indicates if the same should be done for + // perm gen as well. + virtual void invalidate_or_clear(Generation* gen, bool younger, bool perm) = 0; +}; diff --git a/hotspot/src/share/vm/memory/genRemSet.inline.hpp b/hotspot/src/share/vm/memory/genRemSet.inline.hpp new file mode 100644 index 00000000000..448c18e22b5 --- /dev/null +++ b/hotspot/src/share/vm/memory/genRemSet.inline.hpp @@ -0,0 +1,35 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline functions of GenRemSet, which de-virtualize this +// performance-critical call when when the rem set is the most common +// card-table kind. + +void GenRemSet::write_ref_field_gc(oop* field, oop new_val) { + if (kind() == CardTableModRef) { + ((CardTableRS*)this)->inline_write_ref_field_gc(field, new_val); + } else { + write_ref_field_gc_work(field, new_val); + } +} diff --git a/hotspot/src/share/vm/memory/generation.cpp b/hotspot/src/share/vm/memory/generation.cpp new file mode 100644 index 00000000000..d09238c193b --- /dev/null +++ b/hotspot/src/share/vm/memory/generation.cpp @@ -0,0 +1,635 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_generation.cpp.incl" + +Generation::Generation(ReservedSpace rs, size_t initial_size, int level) : + _level(level), + _ref_processor(NULL) { + if (!_virtual_space.initialize(rs, initial_size)) { + vm_exit_during_initialization("Could not reserve enough space for " + "object heap"); + } + _reserved = MemRegion((HeapWord*)_virtual_space.low_boundary(), + (HeapWord*)_virtual_space.high_boundary()); +} + +GenerationSpec* Generation::spec() { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(0 <= level() && level() < gch->_n_gens, "Bad gen level"); + return gch->_gen_specs[level()]; +} + +size_t Generation::max_capacity() const { + return reserved().byte_size(); +} + +void Generation::print_heap_change(size_t prev_used) const { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print(" " SIZE_FORMAT + "->" SIZE_FORMAT + "(" SIZE_FORMAT ")", + prev_used, used(), capacity()); + } else { + gclog_or_tty->print(" " SIZE_FORMAT "K" + "->" SIZE_FORMAT "K" + "(" SIZE_FORMAT "K)", + prev_used / K, used() / K, capacity() / K); + } +} + +// By default we get a single threaded default reference processor; +// generations needing multi-threaded refs discovery override this method. +void Generation::ref_processor_init() { + assert(_ref_processor == NULL, "a reference processor already exists"); + assert(!_reserved.is_empty(), "empty generation?"); + _ref_processor = + new ReferenceProcessor(_reserved, // span + refs_discovery_is_atomic(), // atomic_discovery + refs_discovery_is_mt()); // mt_discovery + if (_ref_processor == NULL) { + vm_exit_during_initialization("Could not allocate ReferenceProcessor object"); + } +} + +void Generation::print() const { print_on(tty); } + +void Generation::print_on(outputStream* st) const { + st->print(" %-20s", name()); + st->print(" total " SIZE_FORMAT "K, used " SIZE_FORMAT "K", + capacity()/K, used()/K); + st->print_cr(" [" INTPTR_FORMAT ", " INTPTR_FORMAT ", " INTPTR_FORMAT ")", + _virtual_space.low_boundary(), + _virtual_space.high(), + _virtual_space.high_boundary()); +} + +void Generation::print_summary_info() { print_summary_info_on(tty); } + +void Generation::print_summary_info_on(outputStream* st) { + StatRecord* sr = stat_record(); + double time = sr->accumulated_time.seconds(); + st->print_cr("[Accumulated GC generation %d time %3.7f secs, " + "%d GC's, avg GC time %3.7f]", + level(), time, sr->invocations, + sr->invocations > 0 ? time / sr->invocations : 0.0); +} + +// Utility iterator classes + +class GenerationIsInReservedClosure : public SpaceClosure { + public: + const void* _p; + Space* sp; + virtual void do_space(Space* s) { + if (sp == NULL) { + if (s->is_in_reserved(_p)) sp = s; + } + } + GenerationIsInReservedClosure(const void* p) : _p(p), sp(NULL) {} +}; + +class GenerationIsInClosure : public SpaceClosure { + public: + const void* _p; + Space* sp; + virtual void do_space(Space* s) { + if (sp == NULL) { + if (s->is_in(_p)) sp = s; + } + } + GenerationIsInClosure(const void* p) : _p(p), sp(NULL) {} +}; + +bool Generation::is_in(const void* p) const { + GenerationIsInClosure blk(p); + ((Generation*)this)->space_iterate(&blk); + return blk.sp != NULL; +} + +DefNewGeneration* Generation::as_DefNewGeneration() { + assert((kind() == Generation::DefNew) || + (kind() == Generation::ParNew) || + (kind() == Generation::ASParNew), + "Wrong youngest generation type"); + return (DefNewGeneration*) this; +} + +Generation* Generation::next_gen() const { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + int next = level() + 1; + if (next < gch->_n_gens) { + return gch->_gens[next]; + } else { + return NULL; + } +} + +size_t Generation::max_contiguous_available() const { + // The largest number of contiguous free words in this or any higher generation. + size_t max = 0; + for (const Generation* gen = this; gen != NULL; gen = gen->next_gen()) { + size_t avail = gen->contiguous_available(); + if (avail > max) { + max = avail; + } + } + return max; +} + +bool Generation::promotion_attempt_is_safe(size_t promotion_in_bytes, + bool not_used) const { + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("Generation::promotion_attempt_is_safe" + " contiguous_available: " SIZE_FORMAT + " promotion_in_bytes: " SIZE_FORMAT, + max_contiguous_available(), promotion_in_bytes); + } + return max_contiguous_available() >= promotion_in_bytes; +} + +// Ignores "ref" and calls allocate(). +oop Generation::promote(oop obj, size_t obj_size, oop* ref) { + assert(obj_size == (size_t)obj->size(), "bad obj_size passed in"); + +#ifndef PRODUCT + if (Universe::heap()->promotion_should_fail()) { + return NULL; + } +#endif // #ifndef PRODUCT + + HeapWord* result = allocate(obj_size, false); + if (result != NULL) { + Copy::aligned_disjoint_words((HeapWord*)obj, result, obj_size); + return oop(result); + } else { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + return gch->handle_failed_promotion(this, obj, obj_size, ref); + } +} + +oop Generation::par_promote(int thread_num, + oop obj, markOop m, size_t word_sz) { + // Could do a bad general impl here that gets a lock. But no. + ShouldNotCallThis(); + return NULL; +} + +void Generation::par_promote_alloc_undo(int thread_num, + HeapWord* obj, size_t word_sz) { + // Could do a bad general impl here that gets a lock. But no. + guarantee(false, "No good general implementation."); +} + +Space* Generation::space_containing(const void* p) const { + GenerationIsInReservedClosure blk(p); + // Cast away const + ((Generation*)this)->space_iterate(&blk); + return blk.sp; +} + +// Some of these are mediocre general implementations. Should be +// overridden to get better performance. + +class GenerationBlockStartClosure : public SpaceClosure { + public: + const void* _p; + HeapWord* _start; + virtual void do_space(Space* s) { + if (_start == NULL && s->is_in_reserved(_p)) { + _start = s->block_start(_p); + } + } + GenerationBlockStartClosure(const void* p) { _p = p; _start = NULL; } +}; + +HeapWord* Generation::block_start(const void* p) const { + GenerationBlockStartClosure blk(p); + // Cast away const + ((Generation*)this)->space_iterate(&blk); + return blk._start; +} + +class GenerationBlockSizeClosure : public SpaceClosure { + public: + const HeapWord* _p; + size_t size; + virtual void do_space(Space* s) { + if (size == 0 && s->is_in_reserved(_p)) { + size = s->block_size(_p); + } + } + GenerationBlockSizeClosure(const HeapWord* p) { _p = p; size = 0; } +}; + +size_t Generation::block_size(const HeapWord* p) const { + GenerationBlockSizeClosure blk(p); + // Cast away const + ((Generation*)this)->space_iterate(&blk); + assert(blk.size > 0, "seems reasonable"); + return blk.size; +} + +class GenerationBlockIsObjClosure : public SpaceClosure { + public: + const HeapWord* _p; + bool is_obj; + virtual void do_space(Space* s) { + if (!is_obj && s->is_in_reserved(_p)) { + is_obj |= s->block_is_obj(_p); + } + } + GenerationBlockIsObjClosure(const HeapWord* p) { _p = p; is_obj = false; } +}; + +bool Generation::block_is_obj(const HeapWord* p) const { + GenerationBlockIsObjClosure blk(p); + // Cast away const + ((Generation*)this)->space_iterate(&blk); + return blk.is_obj; +} + +class GenerationOopIterateClosure : public SpaceClosure { + public: + OopClosure* cl; + MemRegion mr; + virtual void do_space(Space* s) { + s->oop_iterate(mr, cl); + } + GenerationOopIterateClosure(OopClosure* _cl, MemRegion _mr) : + cl(_cl), mr(_mr) {} +}; + +void Generation::oop_iterate(OopClosure* cl) { + GenerationOopIterateClosure blk(cl, _reserved); + space_iterate(&blk); +} + +void Generation::oop_iterate(MemRegion mr, OopClosure* cl) { + GenerationOopIterateClosure blk(cl, mr); + space_iterate(&blk); +} + +void Generation::younger_refs_in_space_iterate(Space* sp, + OopsInGenClosure* cl) { + GenRemSet* rs = SharedHeap::heap()->rem_set(); + rs->younger_refs_in_space_iterate(sp, cl); +} + +class GenerationObjIterateClosure : public SpaceClosure { + private: + ObjectClosure* _cl; + public: + virtual void do_space(Space* s) { + s->object_iterate(_cl); + } + GenerationObjIterateClosure(ObjectClosure* cl) : _cl(cl) {} +}; + +void Generation::object_iterate(ObjectClosure* cl) { + GenerationObjIterateClosure blk(cl); + space_iterate(&blk); +} + +void Generation::prepare_for_compaction(CompactPoint* cp) { + // Generic implementation, can be specialized + CompactibleSpace* space = first_compaction_space(); + while (space != NULL) { + space->prepare_for_compaction(cp); + space = space->next_compaction_space(); + } +} + +class AdjustPointersClosure: public SpaceClosure { + public: + void do_space(Space* sp) { + sp->adjust_pointers(); + } +}; + +void Generation::adjust_pointers() { + // Note that this is done over all spaces, not just the compactible + // ones. + AdjustPointersClosure blk; + space_iterate(&blk, true); +} + +void Generation::compact() { + CompactibleSpace* sp = first_compaction_space(); + while (sp != NULL) { + sp->compact(); + sp = sp->next_compaction_space(); + } +} + +CardGeneration::CardGeneration(ReservedSpace rs, size_t initial_byte_size, + int level, + GenRemSet* remset) : + Generation(rs, initial_byte_size, level), _rs(remset) +{ + HeapWord* start = (HeapWord*)rs.base(); + size_t reserved_byte_size = rs.size(); + assert((uintptr_t(start) & 3) == 0, "bad alignment"); + assert((reserved_byte_size & 3) == 0, "bad alignment"); + MemRegion reserved_mr(start, heap_word_size(reserved_byte_size)); + _bts = new BlockOffsetSharedArray(reserved_mr, + heap_word_size(initial_byte_size)); + MemRegion committed_mr(start, heap_word_size(initial_byte_size)); + _rs->resize_covered_region(committed_mr); + if (_bts == NULL) + vm_exit_during_initialization("Could not allocate a BlockOffsetArray"); + + // Verify that the start and end of this generation is the start of a card. + // If this wasn't true, a single card could span more than on generation, + // which would cause problems when we commit/uncommit memory, and when we + // clear and dirty cards. + guarantee(_rs->is_aligned(reserved_mr.start()), "generation must be card aligned"); + if (reserved_mr.end() != Universe::heap()->reserved_region().end()) { + // Don't check at the very end of the heap as we'll assert that we're probing off + // the end if we try. + guarantee(_rs->is_aligned(reserved_mr.end()), "generation must be card aligned"); + } +} + + +// No young generation references, clear this generation's cards. +void CardGeneration::clear_remembered_set() { + _rs->clear(reserved()); +} + + +// Objects in this generation may have moved, invalidate this +// generation's cards. +void CardGeneration::invalidate_remembered_set() { + _rs->invalidate(used_region()); +} + + +// Currently nothing to do. +void CardGeneration::prepare_for_verify() {} + + +void OneContigSpaceCardGeneration::collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool is_tlab) { + SpecializationStats::clear(); + // Temporarily expand the span of our ref processor, so + // refs discovery is over the entire heap, not just this generation + ReferenceProcessorSpanMutator + x(ref_processor(), GenCollectedHeap::heap()->reserved_region()); + GenMarkSweep::invoke_at_safepoint(_level, ref_processor(), clear_all_soft_refs); + SpecializationStats::print(); +} + +HeapWord* +OneContigSpaceCardGeneration::expand_and_allocate(size_t word_size, + bool is_tlab, + bool parallel) { + assert(!is_tlab, "OneContigSpaceCardGeneration does not support TLAB allocation"); + if (parallel) { + MutexLocker x(ParGCRareEvent_lock); + HeapWord* result = NULL; + size_t byte_size = word_size * HeapWordSize; + while (true) { + expand(byte_size, _min_heap_delta_bytes); + if (GCExpandToAllocateDelayMillis > 0) { + os::sleep(Thread::current(), GCExpandToAllocateDelayMillis, false); + } + result = _the_space->par_allocate(word_size); + if ( result != NULL) { + return result; + } else { + // If there's not enough expansion space available, give up. + if (_virtual_space.uncommitted_size() < byte_size) { + return NULL; + } + // else try again + } + } + } else { + expand(word_size*HeapWordSize, _min_heap_delta_bytes); + return _the_space->allocate(word_size); + } +} + +void OneContigSpaceCardGeneration::expand(size_t bytes, size_t expand_bytes) { + GCMutexLocker x(ExpandHeap_lock); + size_t aligned_bytes = ReservedSpace::page_align_size_up(bytes); + size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes); + bool success = false; + if (aligned_expand_bytes > aligned_bytes) { + success = grow_by(aligned_expand_bytes); + } + if (!success) { + success = grow_by(aligned_bytes); + } + if (!success) { + grow_to_reserved(); + } + if (GC_locker::is_active()) { + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("Garbage collection disabled, expanded heap instead"); + } + } +} + + +void OneContigSpaceCardGeneration::shrink(size_t bytes) { + assert_locked_or_safepoint(ExpandHeap_lock); + size_t size = ReservedSpace::page_align_size_down(bytes); + if (size > 0) { + shrink_by(size); + } +} + + +size_t OneContigSpaceCardGeneration::capacity() const { + return _the_space->capacity(); +} + + +size_t OneContigSpaceCardGeneration::used() const { + return _the_space->used(); +} + + +size_t OneContigSpaceCardGeneration::free() const { + return _the_space->free(); +} + +MemRegion OneContigSpaceCardGeneration::used_region() const { + return the_space()->used_region(); +} + +size_t OneContigSpaceCardGeneration::unsafe_max_alloc_nogc() const { + return _the_space->free(); +} + +size_t OneContigSpaceCardGeneration::contiguous_available() const { + return _the_space->free() + _virtual_space.uncommitted_size(); +} + +bool OneContigSpaceCardGeneration::grow_by(size_t bytes) { + assert_locked_or_safepoint(ExpandHeap_lock); + bool result = _virtual_space.expand_by(bytes); + if (result) { + size_t new_word_size = + heap_word_size(_virtual_space.committed_size()); + MemRegion mr(_the_space->bottom(), new_word_size); + // Expand card table + Universe::heap()->barrier_set()->resize_covered_region(mr); + // Expand shared block offset array + _bts->resize(new_word_size); + + // Fix for bug #4668531 + MemRegion mangle_region(_the_space->end(), (HeapWord*)_virtual_space.high()); + _the_space->mangle_region(mangle_region); + + // Expand space -- also expands space's BOT + // (which uses (part of) shared array above) + _the_space->set_end((HeapWord*)_virtual_space.high()); + + // update the space and generation capacity counters + update_counters(); + + if (Verbose && PrintGC) { + size_t new_mem_size = _virtual_space.committed_size(); + size_t old_mem_size = new_mem_size - bytes; + gclog_or_tty->print_cr("Expanding %s from " SIZE_FORMAT "K by " + SIZE_FORMAT "K to " SIZE_FORMAT "K", + name(), old_mem_size/K, bytes/K, new_mem_size/K); + } + } + return result; +} + + +bool OneContigSpaceCardGeneration::grow_to_reserved() { + assert_locked_or_safepoint(ExpandHeap_lock); + bool success = true; + const size_t remaining_bytes = _virtual_space.uncommitted_size(); + if (remaining_bytes > 0) { + success = grow_by(remaining_bytes); + DEBUG_ONLY(if (!success) warning("grow to reserved failed");) + } + return success; +} + +void OneContigSpaceCardGeneration::shrink_by(size_t bytes) { + assert_locked_or_safepoint(ExpandHeap_lock); + // Shrink committed space + _virtual_space.shrink_by(bytes); + // Shrink space; this also shrinks the space's BOT + _the_space->set_end((HeapWord*) _virtual_space.high()); + size_t new_word_size = heap_word_size(_the_space->capacity()); + // Shrink the shared block offset array + _bts->resize(new_word_size); + MemRegion mr(_the_space->bottom(), new_word_size); + // Shrink the card table + Universe::heap()->barrier_set()->resize_covered_region(mr); + + if (Verbose && PrintGC) { + size_t new_mem_size = _virtual_space.committed_size(); + size_t old_mem_size = new_mem_size + bytes; + gclog_or_tty->print_cr("Shrinking %s from " SIZE_FORMAT "K to " SIZE_FORMAT "K", + name(), old_mem_size/K, new_mem_size/K); + } +} + +// Currently nothing to do. +void OneContigSpaceCardGeneration::prepare_for_verify() {} + + +void OneContigSpaceCardGeneration::object_iterate(ObjectClosure* blk) { + _the_space->object_iterate(blk); +} + +void OneContigSpaceCardGeneration::space_iterate(SpaceClosure* blk, + bool usedOnly) { + blk->do_space(_the_space); +} + +void OneContigSpaceCardGeneration::object_iterate_since_last_GC(ObjectClosure* blk) { + // Deal with delayed initialization of _the_space, + // and lack of initialization of _last_gc. + if (_last_gc.space() == NULL) { + assert(the_space() != NULL, "shouldn't be NULL"); + _last_gc = the_space()->bottom_mark(); + } + the_space()->object_iterate_from(_last_gc, blk); +} + +void OneContigSpaceCardGeneration::younger_refs_iterate(OopsInGenClosure* blk) { + blk->set_generation(this); + younger_refs_in_space_iterate(_the_space, blk); + blk->reset_generation(); +} + +void OneContigSpaceCardGeneration::save_marks() { + _the_space->set_saved_mark(); +} + + +void OneContigSpaceCardGeneration::reset_saved_marks() { + _the_space->reset_saved_mark(); +} + + +bool OneContigSpaceCardGeneration::no_allocs_since_save_marks() { + return _the_space->saved_mark_at_top(); +} + +#define OneContig_SINCE_SAVE_MARKS_ITERATE_DEFN(OopClosureType, nv_suffix) \ + \ +void OneContigSpaceCardGeneration:: \ +oop_since_save_marks_iterate##nv_suffix(OopClosureType* blk) { \ + blk->set_generation(this); \ + _the_space->oop_since_save_marks_iterate##nv_suffix(blk); \ + blk->reset_generation(); \ + save_marks(); \ +} + +ALL_SINCE_SAVE_MARKS_CLOSURES(OneContig_SINCE_SAVE_MARKS_ITERATE_DEFN) + +#undef OneContig_SINCE_SAVE_MARKS_ITERATE_DEFN + + +void OneContigSpaceCardGeneration::gc_epilogue(bool full) { + _last_gc = WaterMark(the_space(), the_space()->top()); + + // update the generation and space performance counters + update_counters(); +} + +void OneContigSpaceCardGeneration::verify(bool allow_dirty) { + the_space()->verify(allow_dirty); +} + +void OneContigSpaceCardGeneration::print_on(outputStream* st) const { + Generation::print_on(st); + st->print(" the"); + the_space()->print_on(st); +} diff --git a/hotspot/src/share/vm/memory/generation.hpp b/hotspot/src/share/vm/memory/generation.hpp new file mode 100644 index 00000000000..b0921e76626 --- /dev/null +++ b/hotspot/src/share/vm/memory/generation.hpp @@ -0,0 +1,710 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A Generation models a heap area for similarly-aged objects. +// It will contain one ore more spaces holding the actual objects. +// +// The Generation class hierarchy: +// +// Generation - abstract base class +// - DefNewGeneration - allocation area (copy collected) +// - ParNewGeneration - a DefNewGeneration that is collected by +// several threads +// - CardGeneration - abstract class adding offset array behavior +// - OneContigSpaceCardGeneration - abstract class holding a single +// contiguous space with card marking +// - TenuredGeneration - tenured (old object) space (markSweepCompact) +// - CompactingPermGenGen - reflective object area (klasses, methods, symbols, ...) +// - ConcurrentMarkSweepGeneration - Mostly Concurrent Mark Sweep Generation +// (Detlefs-Printezis refinement of +// Boehm-Demers-Schenker) +// +// The system configurations currently allowed are: +// +// DefNewGeneration + TenuredGeneration + PermGeneration +// DefNewGeneration + ConcurrentMarkSweepGeneration + ConcurrentMarkSweepPermGen +// +// ParNewGeneration + TenuredGeneration + PermGeneration +// ParNewGeneration + ConcurrentMarkSweepGeneration + ConcurrentMarkSweepPermGen +// + +class DefNewGeneration; +class GenerationSpec; +class CompactibleSpace; +class ContiguousSpace; +class CompactPoint; +class OopsInGenClosure; +class OopClosure; +class ScanClosure; +class FastScanClosure; +class GenCollectedHeap; +class GenRemSet; +class GCStats; + +// A "ScratchBlock" represents a block of memory in one generation usable by +// another. It represents "num_words" free words, starting at and including +// the address of "this". +struct ScratchBlock { + ScratchBlock* next; + size_t num_words; + HeapWord scratch_space[1]; // Actually, of size "num_words-2" (assuming + // first two fields are word-sized.) +}; + + +class Generation: public CHeapObj { + friend class VMStructs; + private: + jlong _time_of_last_gc; // time when last gc on this generation happened (ms) + MemRegion _prev_used_region; // for collectors that want to "remember" a value for + // used region at some specific point during collection. + + protected: + // Minimum and maximum addresses for memory reserved (not necessarily + // committed) for generation. + // Used by card marking code. Must not overlap with address ranges of + // other generations. + MemRegion _reserved; + + // Memory area reserved for generation + VirtualSpace _virtual_space; + + // Level in the generation hierarchy. + int _level; + + // ("Weak") Reference processing support + ReferenceProcessor* _ref_processor; + + // Performance Counters + CollectorCounters* _gc_counters; + + // Statistics for garbage collection + GCStats* _gc_stats; + + // Returns the next generation in the configuration, or else NULL if this + // is the highest generation. + Generation* next_gen() const; + + // Initialize the generation. + Generation(ReservedSpace rs, size_t initial_byte_size, int level); + + // Apply "cl->do_oop" to (the address of) (exactly) all the ref fields in + // "sp" that point into younger generations. + // The iteration is only over objects allocated at the start of the + // iterations; objects allocated as a result of applying the closure are + // not included. + void younger_refs_in_space_iterate(Space* sp, OopsInGenClosure* cl); + + public: + // The set of possible generation kinds. + enum Name { + ASParNew, + ASConcurrentMarkSweep, + DefNew, + ParNew, + MarkSweepCompact, + ConcurrentMarkSweep, + Other + }; + + enum SomePublicConstants { + // Generations are GenGrain-aligned and have size that are multiples of + // GenGrain. + LogOfGenGrain = 16, + GenGrain = 1 << LogOfGenGrain + }; + + // allocate and initialize ("weak") refs processing support + virtual void ref_processor_init(); + void set_ref_processor(ReferenceProcessor* rp) { + assert(_ref_processor == NULL, "clobbering existing _ref_processor"); + _ref_processor = rp; + } + + virtual Generation::Name kind() { return Generation::Other; } + GenerationSpec* spec(); + + // This properly belongs in the collector, but for now this + // will do. + virtual bool refs_discovery_is_atomic() const { return true; } + virtual bool refs_discovery_is_mt() const { return false; } + + // Space enquiries (results in bytes) + virtual size_t capacity() const = 0; // The maximum number of object bytes the + // generation can currently hold. + virtual size_t used() const = 0; // The number of used bytes in the gen. + virtual size_t free() const = 0; // The number of free bytes in the gen. + + // Support for java.lang.Runtime.maxMemory(); see CollectedHeap. + // Returns the total number of bytes available in a generation + // for the allocation of objects. + virtual size_t max_capacity() const; + + // If this is a young generation, the maximum number of bytes that can be + // allocated in this generation before a GC is triggered. + virtual size_t capacity_before_gc() const { return 0; } + + // The largest number of contiguous free bytes in the generation, + // including expansion (Assumes called at a safepoint.) + virtual size_t contiguous_available() const = 0; + // The largest number of contiguous free bytes in this or any higher generation. + virtual size_t max_contiguous_available() const; + + // Returns true if promotions of the specified amount can + // be attempted safely (without a vm failure). + // Promotion of the full amount is not guaranteed but + // can be attempted. + // younger_handles_promotion_failure + // is true if the younger generation handles a promotion + // failure. + virtual bool promotion_attempt_is_safe(size_t promotion_in_bytes, + bool younger_handles_promotion_failure) const; + + // Return an estimate of the maximum allocation that could be performed + // in the generation without triggering any collection or expansion + // activity. It is "unsafe" because no locks are taken; the result + // should be treated as an approximation, not a guarantee, for use in + // heuristic resizing decisions. + virtual size_t unsafe_max_alloc_nogc() const = 0; + + // Returns true if this generation cannot be expanded further + // without a GC. Override as appropriate. + virtual bool is_maximal_no_gc() const { + return _virtual_space.uncommitted_size() == 0; + } + + MemRegion reserved() const { return _reserved; } + + // Returns a region guaranteed to contain all the objects in the + // generation. + virtual MemRegion used_region() const { return _reserved; } + + MemRegion prev_used_region() const { return _prev_used_region; } + virtual void save_used_region() { _prev_used_region = used_region(); } + + // Returns "TRUE" iff "p" points into an allocated object in the generation. + // For some kinds of generations, this may be an expensive operation. + // To avoid performance problems stemming from its inadvertent use in + // product jvm's, we restrict its use to assertion checking or + // verification only. + virtual bool is_in(const void* p) const; + + /* Returns "TRUE" iff "p" points into the reserved area of the generation. */ + bool is_in_reserved(const void* p) const { + return _reserved.contains(p); + } + + // Check that the generation kind is DefNewGeneration or a sub + // class of DefNewGeneration and return a DefNewGeneration* + DefNewGeneration* as_DefNewGeneration(); + + // If some space in the generation contains the given "addr", return a + // pointer to that space, else return "NULL". + virtual Space* space_containing(const void* addr) const; + + // Iteration - do not use for time critical operations + virtual void space_iterate(SpaceClosure* blk, bool usedOnly = false) = 0; + + // Returns the first space, if any, in the generation that can participate + // in compaction, or else "NULL". + virtual CompactibleSpace* first_compaction_space() const = 0; + + // Returns "true" iff this generation should be used to allocate an + // object of the given size. Young generations might + // wish to exclude very large objects, for example, since, if allocated + // often, they would greatly increase the frequency of young-gen + // collection. + virtual bool should_allocate(size_t word_size, bool is_tlab) { + bool result = false; + size_t overflow_limit = (size_t)1 << (BitsPerSize_t - LogHeapWordSize); + if (!is_tlab || supports_tlab_allocation()) { + result = (word_size > 0) && (word_size < overflow_limit); + } + return result; + } + + // Allocate and returns a block of the requested size, or returns "NULL". + // Assumes the caller has done any necessary locking. + virtual HeapWord* allocate(size_t word_size, bool is_tlab) = 0; + + // Like "allocate", but performs any necessary locking internally. + virtual HeapWord* par_allocate(size_t word_size, bool is_tlab) = 0; + + // A 'younger' gen has reached an allocation limit, and uses this to notify + // the next older gen. The return value is a new limit, or NULL if none. The + // caller must do the necessary locking. + virtual HeapWord* allocation_limit_reached(Space* space, HeapWord* top, + size_t word_size) { + return NULL; + } + + // Some generation may offer a region for shared, contiguous allocation, + // via inlined code (by exporting the address of the top and end fields + // defining the extent of the contiguous allocation region.) + + // This function returns "true" iff the heap supports this kind of + // allocation. (More precisely, this means the style of allocation that + // increments *top_addr()" with a CAS.) (Default is "no".) + // A generation that supports this allocation style must use lock-free + // allocation for *all* allocation, since there are times when lock free + // allocation will be concurrent with plain "allocate" calls. + virtual bool supports_inline_contig_alloc() const { return false; } + + // These functions return the addresses of the fields that define the + // boundaries of the contiguous allocation area. (These fields should be + // physicall near to one another.) + virtual HeapWord** top_addr() const { return NULL; } + virtual HeapWord** end_addr() const { return NULL; } + + // Thread-local allocation buffers + virtual bool supports_tlab_allocation() const { return false; } + virtual size_t tlab_capacity() const { + guarantee(false, "Generation doesn't support thread local allocation buffers"); + return 0; + } + virtual size_t unsafe_max_tlab_alloc() const { + guarantee(false, "Generation doesn't support thread local allocation buffers"); + return 0; + } + + // "obj" is the address of an object in a younger generation. Allocate space + // for "obj" in the current (or some higher) generation, and copy "obj" into + // the newly allocated space, if possible, returning the result (or NULL if + // the allocation failed). + // + // The "obj_size" argument is just obj->size(), passed along so the caller can + // avoid repeating the virtual call to retrieve it. + // + // The "ref" argument, if non-NULL, is the address of some reference to "obj" + // (that is "*ref == obj"); some generations may use this information to, for + // example, influence placement decisions. + // + // The default implementation ignores "ref" and calls allocate(). + virtual oop promote(oop obj, size_t obj_size, oop* ref); + + // Thread "thread_num" (0 <= i < ParalleGCThreads) wants to promote + // object "obj", whose original mark word was "m", and whose size is + // "word_sz". If possible, allocate space for "obj", copy obj into it + // (taking care to copy "m" into the mark word when done, since the mark + // word of "obj" may have been overwritten with a forwarding pointer, and + // also taking care to copy the klass pointer *last*. Returns the new + // object if successful, or else NULL. + virtual oop par_promote(int thread_num, + oop obj, markOop m, size_t word_sz); + + // Undo, if possible, the most recent par_promote_alloc allocation by + // "thread_num" ("obj", of "word_sz"). + virtual void par_promote_alloc_undo(int thread_num, + HeapWord* obj, size_t word_sz); + + // Informs the current generation that all par_promote_alloc's in the + // collection have been completed; any supporting data structures can be + // reset. Default is to do nothing. + virtual void par_promote_alloc_done(int thread_num) {} + + // Informs the current generation that all oop_since_save_marks_iterates + // performed by "thread_num" in the current collection, if any, have been + // completed; any supporting data structures can be reset. Default is to + // do nothing. + virtual void par_oop_since_save_marks_iterate_done(int thread_num) {} + + // This generation will collect all younger generations + // during a full collection. + virtual bool full_collects_younger_generations() const { return false; } + + // This generation does in-place marking, meaning that mark words + // are mutated during the marking phase and presumably reinitialized + // to a canonical value after the GC. This is currently used by the + // biased locking implementation to determine whether additional + // work is required during the GC prologue and epilogue. + virtual bool performs_in_place_marking() const { return true; } + + // Returns "true" iff collect() should subsequently be called on this + // this generation. See comment below. + // This is a generic implementation which can be overridden. + // + // Note: in the current (1.4) implementation, when genCollectedHeap's + // incremental_collection_will_fail flag is set, all allocations are + // slow path (the only fast-path place to allocate is DefNew, which + // will be full if the flag is set). + // Thus, older generations which collect younger generations should + // test this flag and collect if it is set. + virtual bool should_collect(bool full, + size_t word_size, + bool is_tlab) { + return (full || should_allocate(word_size, is_tlab)); + } + + // Perform a garbage collection. + // If full is true attempt a full garbage collection of this generation. + // Otherwise, attempting to (at least) free enough space to support an + // allocation of the given "word_size". + virtual void collect(bool full, + bool clear_all_soft_refs, + size_t word_size, + bool is_tlab) = 0; + + // Perform a heap collection, attempting to create (at least) enough + // space to support an allocation of the given "word_size". If + // successful, perform the allocation and return the resulting + // "oop" (initializing the allocated block). If the allocation is + // still unsuccessful, return "NULL". + virtual HeapWord* expand_and_allocate(size_t word_size, + bool is_tlab, + bool parallel = false) = 0; + + // Some generations may require some cleanup or preparation actions before + // allowing a collection. The default is to do nothing. + virtual void gc_prologue(bool full) {}; + + // Some generations may require some cleanup actions after a collection. + // The default is to do nothing. + virtual void gc_epilogue(bool full) {}; + + // Some generations may need to be "fixed-up" after some allocation + // activity to make them parsable again. The default is to do nothing. + virtual void ensure_parsability() {}; + + // Time (in ms) when we were last collected or now if a collection is + // in progress. + virtual jlong time_of_last_gc(jlong now) { + // XXX See note in genCollectedHeap::millis_since_last_gc() + NOT_PRODUCT( + if (now < _time_of_last_gc) { + warning("time warp: %d to %d", _time_of_last_gc, now); + } + ) + return _time_of_last_gc; + } + + virtual void update_time_of_last_gc(jlong now) { + _time_of_last_gc = now; + } + + // Generations may keep statistics about collection. This + // method updates those statistics. current_level is + // the level of the collection that has most recently + // occurred. This allows the generation to decide what + // statistics are valid to collect. For example, the + // generation can decide to gather the amount of promoted data + // if the collection of the younger generations has completed. + GCStats* gc_stats() const { return _gc_stats; } + virtual void update_gc_stats(int current_level, bool full) {} + + // Mark sweep support phase2 + virtual void prepare_for_compaction(CompactPoint* cp); + // Mark sweep support phase3 + virtual void pre_adjust_pointers() {ShouldNotReachHere();} + virtual void adjust_pointers(); + // Mark sweep support phase4 + virtual void compact(); + virtual void post_compact() {ShouldNotReachHere();} + + // Support for CMS's rescan. In this general form we return a pointer + // to an abstract object that can be used, based on specific previously + // decided protocols, to exchange information between generations, + // information that may be useful for speeding up certain types of + // garbage collectors. A NULL value indicates to the client that + // no data recording is expected by the provider. The data-recorder is + // expected to be GC worker thread-local, with the worker index + // indicated by "thr_num". + virtual void* get_data_recorder(int thr_num) { return NULL; } + + // Some generations may require some cleanup actions before allowing + // a verification. + virtual void prepare_for_verify() {}; + + // Accessing "marks". + + // This function gives a generation a chance to note a point between + // collections. For example, a contiguous generation might note the + // beginning allocation point post-collection, which might allow some later + // operations to be optimized. + virtual void save_marks() {} + + // This function allows generations to initialize any "saved marks". That + // is, should only be called when the generation is empty. + virtual void reset_saved_marks() {} + + // This function is "true" iff any no allocations have occurred in the + // generation since the last call to "save_marks". + virtual bool no_allocs_since_save_marks() = 0; + + // Apply "cl->apply" to (the addresses of) all reference fields in objects + // allocated in the current generation since the last call to "save_marks". + // If more objects are allocated in this generation as a result of applying + // the closure, iterates over reference fields in those objects as well. + // Calls "save_marks" at the end of the iteration. + // General signature... + virtual void oop_since_save_marks_iterate_v(OopsInGenClosure* cl) = 0; + // ...and specializations for de-virtualization. (The general + // implemention of the _nv versions call the virtual version. + // Note that the _nv suffix is not really semantically necessary, + // but it avoids some not-so-useful warnings on Solaris.) +#define Generation_SINCE_SAVE_MARKS_DECL(OopClosureType, nv_suffix) \ + virtual void oop_since_save_marks_iterate##nv_suffix(OopClosureType* cl) { \ + oop_since_save_marks_iterate_v((OopsInGenClosure*)cl); \ + } + SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES(Generation_SINCE_SAVE_MARKS_DECL) + +#undef Generation_SINCE_SAVE_MARKS_DECL + + // The "requestor" generation is performing some garbage collection + // action for which it would be useful to have scratch space. If + // the target is not the requestor, no gc actions will be required + // of the target. The requestor promises to allocate no more than + // "max_alloc_words" in the target generation (via promotion say, + // if the requestor is a young generation and the target is older). + // If the target generation can provide any scratch space, it adds + // it to "list", leaving "list" pointing to the head of the + // augmented list. The default is to offer no space. + virtual void contribute_scratch(ScratchBlock*& list, Generation* requestor, + size_t max_alloc_words) {} + + // When an older generation has been collected, and perhaps resized, + // this method will be invoked on all younger generations (from older to + // younger), allowing them to resize themselves as appropriate. + virtual void compute_new_size() = 0; + + // Printing + virtual const char* name() const = 0; + virtual const char* short_name() const = 0; + + int level() const { return _level; } + + // Attributes + + // True iff the given generation may only be the youngest generation. + virtual bool must_be_youngest() const = 0; + // True iff the given generation may only be the oldest generation. + virtual bool must_be_oldest() const = 0; + + // Reference Processing accessor + ReferenceProcessor* const ref_processor() { return _ref_processor; } + + // Iteration. + + // Iterate over all the ref-containing fields of all objects in the + // generation, calling "cl.do_oop" on each. + virtual void oop_iterate(OopClosure* cl); + + // Same as above, restricted to the intersection of a memory region and + // the generation. + virtual void oop_iterate(MemRegion mr, OopClosure* cl); + + // Iterate over all objects in the generation, calling "cl.do_object" on + // each. + virtual void object_iterate(ObjectClosure* cl); + + // Iterate over all objects allocated in the generation since the last + // collection, calling "cl.do_object" on each. The generation must have + // been initialized properly to support this function, or else this call + // will fail. + virtual void object_iterate_since_last_GC(ObjectClosure* cl) = 0; + + // Apply "cl->do_oop" to (the address of) all and only all the ref fields + // in the current generation that contain pointers to objects in younger + // generations. Objects allocated since the last "save_marks" call are + // excluded. + virtual void younger_refs_iterate(OopsInGenClosure* cl) = 0; + + // Inform a generation that it longer contains references to objects + // in any younger generation. [e.g. Because younger gens are empty, + // clear the card table.] + virtual void clear_remembered_set() { } + + // Inform a generation that some of its objects have moved. [e.g. The + // generation's spaces were compacted, invalidating the card table.] + virtual void invalidate_remembered_set() { } + + // Block abstraction. + + // Returns the address of the start of the "block" that contains the + // address "addr". We say "blocks" instead of "object" since some heaps + // may not pack objects densely; a chunk may either be an object or a + // non-object. + virtual HeapWord* block_start(const void* addr) const; + + // Requires "addr" to be the start of a chunk, and returns its size. + // "addr + size" is required to be the start of a new chunk, or the end + // of the active area of the heap. + virtual size_t block_size(const HeapWord* addr) const ; + + // Requires "addr" to be the start of a block, and returns "TRUE" iff + // the block is an object. + virtual bool block_is_obj(const HeapWord* addr) const; + + + // PrintGC, PrintGCDetails support + void print_heap_change(size_t prev_used) const; + + // PrintHeapAtGC support + virtual void print() const; + virtual void print_on(outputStream* st) const; + + virtual void verify(bool allow_dirty) = 0; + + struct StatRecord { + int invocations; + elapsedTimer accumulated_time; + StatRecord() : + invocations(0), + accumulated_time(elapsedTimer()) {} + }; +private: + StatRecord _stat_record; +public: + StatRecord* stat_record() { return &_stat_record; } + + virtual void print_summary_info(); + virtual void print_summary_info_on(outputStream* st); + + // Performance Counter support + virtual void update_counters() = 0; + virtual CollectorCounters* counters() { return _gc_counters; } +}; + +// Class CardGeneration is a generation that is covered by a card table, +// and uses a card-size block-offset array to implement block_start. + +// class BlockOffsetArray; +// class BlockOffsetArrayContigSpace; +class BlockOffsetSharedArray; + +class CardGeneration: public Generation { + friend class VMStructs; + protected: + // This is shared with other generations. + GenRemSet* _rs; + // This is local to this generation. + BlockOffsetSharedArray* _bts; + + CardGeneration(ReservedSpace rs, size_t initial_byte_size, int level, + GenRemSet* remset); + + public: + + virtual void clear_remembered_set(); + + virtual void invalidate_remembered_set(); + + virtual void prepare_for_verify(); +}; + +// OneContigSpaceCardGeneration models a heap of old objects contained in a single +// contiguous space. +// +// Garbage collection is performed using mark-compact. + +class OneContigSpaceCardGeneration: public CardGeneration { + friend class VMStructs; + // Abstractly, this is a subtype that gets access to protected fields. + friend class CompactingPermGen; + friend class VM_PopulateDumpSharedSpace; + + protected: + size_t _min_heap_delta_bytes; // Minimum amount to expand. + ContiguousSpace* _the_space; // actual space holding objects + WaterMark _last_gc; // watermark between objects allocated before + // and after last GC. + + // Grow generation with specified size (returns false if unable to grow) + bool grow_by(size_t bytes); + // Grow generation to reserved size. + bool grow_to_reserved(); + // Shrink generation with specified size (returns false if unable to shrink) + void shrink_by(size_t bytes); + + // Allocation failure + void expand(size_t bytes, size_t expand_bytes); + void shrink(size_t bytes); + + // Accessing spaces + ContiguousSpace* the_space() const { return _the_space; } + + public: + OneContigSpaceCardGeneration(ReservedSpace rs, size_t initial_byte_size, + size_t min_heap_delta_bytes, + int level, GenRemSet* remset, + ContiguousSpace* space) : + CardGeneration(rs, initial_byte_size, level, remset), + _the_space(space), _min_heap_delta_bytes(min_heap_delta_bytes) + {} + + inline bool is_in(const void* p) const; + + // Space enquiries + size_t capacity() const; + size_t used() const; + size_t free() const; + + MemRegion used_region() const; + + size_t unsafe_max_alloc_nogc() const; + size_t contiguous_available() const; + + // Iteration + void object_iterate(ObjectClosure* blk); + void space_iterate(SpaceClosure* blk, bool usedOnly = false); + void object_iterate_since_last_GC(ObjectClosure* cl); + + void younger_refs_iterate(OopsInGenClosure* blk); + + inline CompactibleSpace* first_compaction_space() const; + + virtual inline HeapWord* allocate(size_t word_size, bool is_tlab); + virtual inline HeapWord* par_allocate(size_t word_size, bool is_tlab); + + // Accessing marks + inline WaterMark top_mark(); + inline WaterMark bottom_mark(); + +#define OneContig_SINCE_SAVE_MARKS_DECL(OopClosureType, nv_suffix) \ + void oop_since_save_marks_iterate##nv_suffix(OopClosureType* cl); + OneContig_SINCE_SAVE_MARKS_DECL(OopsInGenClosure,_v) + SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES(OneContig_SINCE_SAVE_MARKS_DECL) + + void save_marks(); + void reset_saved_marks(); + bool no_allocs_since_save_marks(); + + inline size_t block_size(const HeapWord* addr) const; + + inline bool block_is_obj(const HeapWord* addr) const; + + virtual void collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool is_tlab); + HeapWord* expand_and_allocate(size_t size, + bool is_tlab, + bool parallel = false); + + virtual void prepare_for_verify(); + + virtual void gc_epilogue(bool full); + + virtual void verify(bool allow_dirty); + virtual void print_on(outputStream* st) const; +}; diff --git a/hotspot/src/share/vm/memory/generation.inline.hpp b/hotspot/src/share/vm/memory/generation.inline.hpp new file mode 100644 index 00000000000..0e884f4771e --- /dev/null +++ b/hotspot/src/share/vm/memory/generation.inline.hpp @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +bool OneContigSpaceCardGeneration::is_in(const void* p) const { + return the_space()->is_in(p); +} + + +WaterMark OneContigSpaceCardGeneration::top_mark() { + return the_space()->top_mark(); +} + +CompactibleSpace* +OneContigSpaceCardGeneration::first_compaction_space() const { + return the_space(); +} + +HeapWord* OneContigSpaceCardGeneration::allocate(size_t word_size, + bool is_tlab) { + assert(!is_tlab, "OneContigSpaceCardGeneration does not support TLAB allocation"); + return the_space()->allocate(word_size); +} + +HeapWord* OneContigSpaceCardGeneration::par_allocate(size_t word_size, + bool is_tlab) { + assert(!is_tlab, "OneContigSpaceCardGeneration does not support TLAB allocation"); + return the_space()->par_allocate(word_size); +} + +WaterMark OneContigSpaceCardGeneration::bottom_mark() { + return the_space()->bottom_mark(); +} + +size_t OneContigSpaceCardGeneration::block_size(const HeapWord* addr) const { + if (addr < the_space()->top()) return oop(addr)->size(); + else { + assert(addr == the_space()->top(), "non-block head arg to block_size"); + return the_space()->_end - the_space()->top(); + } +} + +bool OneContigSpaceCardGeneration::block_is_obj(const HeapWord* addr) const { + return addr < the_space()->top(); +} diff --git a/hotspot/src/share/vm/memory/generationSpec.cpp b/hotspot/src/share/vm/memory/generationSpec.cpp new file mode 100644 index 00000000000..9260c58b1ce --- /dev/null +++ b/hotspot/src/share/vm/memory/generationSpec.cpp @@ -0,0 +1,188 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_generationSpec.cpp.incl" + +Generation* GenerationSpec::init(ReservedSpace rs, int level, + GenRemSet* remset) { + switch (name()) { + case Generation::DefNew: + return new DefNewGeneration(rs, init_size(), level); + + case Generation::MarkSweepCompact: + return new TenuredGeneration(rs, init_size(), level, remset); + +#ifndef SERIALGC + case Generation::ParNew: + return new ParNewGeneration(rs, init_size(), level); + + case Generation::ASParNew: + return new ASParNewGeneration(rs, + init_size(), + init_size() /* min size */, + level); + + case Generation::ConcurrentMarkSweep: { + assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set"); + CardTableRS* ctrs = remset->as_CardTableRS(); + if (ctrs == NULL) { + vm_exit_during_initialization("Rem set incompatibility."); + } + // Otherwise + // The constructor creates the CMSCollector if needed, + // else registers with an existing CMSCollector + + ConcurrentMarkSweepGeneration* g = NULL; + g = new ConcurrentMarkSweepGeneration(rs, + init_size(), level, ctrs, UseCMSAdaptiveFreeLists, + (FreeBlockDictionary::DictionaryChoice)CMSDictionaryChoice); + + g->initialize_performance_counters(); + + return g; + } + + case Generation::ASConcurrentMarkSweep: { + assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set"); + CardTableRS* ctrs = remset->as_CardTableRS(); + if (ctrs == NULL) { + vm_exit_during_initialization("Rem set incompatibility."); + } + // Otherwise + // The constructor creates the CMSCollector if needed, + // else registers with an existing CMSCollector + + ASConcurrentMarkSweepGeneration* g = NULL; + g = new ASConcurrentMarkSweepGeneration(rs, + init_size(), level, ctrs, UseCMSAdaptiveFreeLists, + (FreeBlockDictionary::DictionaryChoice)CMSDictionaryChoice); + + g->initialize_performance_counters(); + + return g; + } +#endif // SERIALGC + + default: + guarantee(false, "unrecognized GenerationName"); + return NULL; + } +} + + +PermanentGenerationSpec::PermanentGenerationSpec(PermGen::Name name, + size_t init_size, size_t max_size, + size_t read_only_size, size_t read_write_size, + size_t misc_data_size, size_t misc_code_size) { + _name = name; + _init_size = init_size; + + if (UseSharedSpaces || DumpSharedSpaces) { + _enable_shared_spaces = true; + if (UseSharedSpaces) { + // Override shared space sizes from those in the file. + FileMapInfo* mapinfo = FileMapInfo::current_info(); + _read_only_size = mapinfo->space_capacity(CompactingPermGenGen::ro); + _read_write_size = mapinfo->space_capacity(CompactingPermGenGen::rw); + _misc_data_size = mapinfo->space_capacity(CompactingPermGenGen::md); + _misc_code_size = mapinfo->space_capacity(CompactingPermGenGen::mc); + } else { + _read_only_size = read_only_size; + _read_write_size = read_write_size; + _misc_data_size = misc_data_size; + _misc_code_size = misc_code_size; + } + } else { + _enable_shared_spaces = false; + _read_only_size = 0; + _read_write_size = 0; + _misc_data_size = 0; + _misc_code_size = 0; + } + + _max_size = max_size; +} + + +PermGen* PermanentGenerationSpec::init(ReservedSpace rs, + size_t init_size, + GenRemSet *remset) { + + // Break the reserved spaces into pieces for the permanent space + // and the shared spaces. + ReservedSpace perm_rs = rs.first_part(_max_size, UseSharedSpaces, + UseSharedSpaces); + ReservedSpace shared_rs = rs.last_part(_max_size); + + if (enable_shared_spaces()) { + if (!perm_rs.is_reserved() || + perm_rs.base() + perm_rs.size() != shared_rs.base()) { + FileMapInfo* mapinfo = FileMapInfo::current_info(); + mapinfo->fail_continue("Sharing disabled - unable to " + "reserve address space."); + shared_rs.release(); + disable_sharing(); + } + } + + switch (name()) { + case PermGen::MarkSweepCompact: + return new CompactingPermGen(perm_rs, shared_rs, init_size, remset, this); + +#ifndef SERIALGC + case PermGen::MarkSweep: + guarantee(false, "NYI"); + return NULL; + + case PermGen::ConcurrentMarkSweep: { + assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set"); + CardTableRS* ctrs = remset->as_CardTableRS(); + if (ctrs == NULL) { + vm_exit_during_initialization("RemSet/generation incompatibility."); + } + // XXXPERM + return new CMSPermGen(perm_rs, init_size, ctrs, + (FreeBlockDictionary::DictionaryChoice)CMSDictionaryChoice); + } +#endif // SERIALGC + default: + guarantee(false, "unrecognized GenerationName"); + return NULL; + } +} + + +// Alignment +void PermanentGenerationSpec::align(size_t alignment) { + _init_size = align_size_up(_init_size, alignment); + _max_size = align_size_up(_max_size, alignment); + _read_only_size = align_size_up(_read_only_size, alignment); + _read_write_size = align_size_up(_read_write_size, alignment); + _misc_data_size = align_size_up(_misc_data_size, alignment); + _misc_code_size = align_size_up(_misc_code_size, alignment); + + assert(enable_shared_spaces() || (_read_only_size + _read_write_size == 0), + "Shared space when disabled?"); +} diff --git a/hotspot/src/share/vm/memory/generationSpec.hpp b/hotspot/src/share/vm/memory/generationSpec.hpp new file mode 100644 index 00000000000..0740acb0aac --- /dev/null +++ b/hotspot/src/share/vm/memory/generationSpec.hpp @@ -0,0 +1,122 @@ +/* + * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The specification of a generation. This class also encapsulates +// some generation-specific behavior. This is done here rather than as a +// virtual function of Generation because these methods are needed in +// initialization of the Generations. +class GenerationSpec : public CHeapObj { + friend class VMStructs; +private: + Generation::Name _name; + size_t _init_size; + size_t _max_size; + +public: + GenerationSpec(Generation::Name name, size_t init_size, size_t max_size) { + _name = name; + _init_size = init_size; + _max_size = max_size; + } + + Generation* init(ReservedSpace rs, int level, GenRemSet* remset); + + // Accessors + Generation::Name name() const { return _name; } + size_t init_size() const { return _init_size; } + void set_init_size(size_t size) { _init_size = size; } + size_t max_size() const { return _max_size; } + void set_max_size(size_t size) { _max_size = size; } + + // Alignment + void align(size_t alignment) { + set_init_size(align_size_up(init_size(), alignment)); + set_max_size(align_size_up(max_size(), alignment)); + } + + // Return the number of regions contained in the generation which + // might need to be independently covered by a remembered set. + virtual int n_covered_regions() const { return 1; } +}; + +typedef GenerationSpec* GenerationSpecPtr; + +// The specification of a permanent generation. This class is very +// similar to GenerationSpec in use. Due to PermGen's not being a +// true Generation, we cannot combine the spec classes either. +class PermanentGenerationSpec : public CHeapObj { + friend class VMStructs; +private: + PermGen::Name _name; + size_t _init_size; + size_t _max_size; + size_t _read_only_size; + size_t _read_write_size; + size_t _misc_data_size; + size_t _misc_code_size; + bool _enable_shared_spaces; + + enum { + _n_spaces = 2 + }; + +public: + PermanentGenerationSpec(PermGen::Name name, size_t init_size, + size_t max_size, size_t read_only_size, + size_t read_write_size, size_t misc_data_size, + size_t misc_code_size); + + PermGen* init(ReservedSpace rs, size_t init_size, GenRemSet* remset); + + void disable_sharing() { + _enable_shared_spaces = false; + _read_only_size = 0; + _read_write_size = 0; + _misc_data_size = 0; + _misc_code_size = 0; + } + + // Accessors + PermGen::Name name() const { return _name; } + size_t init_size() const { return _init_size; } + void set_init_size(size_t size) { _init_size = size; } + + // Max size for user DOES NOT include shared spaces. + // Max size for space allocation DOES include shared spaces. + size_t max_size() const { + return _max_size + _read_only_size + _read_write_size; + } + + // Need one covered region for the main space, and one for the shared + // spaces (together). + int n_covered_regions() const { return 2; } + + void align(size_t alignment); + + size_t read_only_size() const { return _read_only_size; } + size_t read_write_size() const { return _read_write_size; } + size_t misc_data_size() const { return _misc_data_size; } + size_t misc_code_size() const { return _misc_code_size; } + bool enable_shared_spaces() const { return _enable_shared_spaces; } +}; diff --git a/hotspot/src/share/vm/memory/heap.cpp b/hotspot/src/share/vm/memory/heap.cpp new file mode 100644 index 00000000000..c271efb0d3e --- /dev/null +++ b/hotspot/src/share/vm/memory/heap.cpp @@ -0,0 +1,482 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_heap.cpp.incl" + + +size_t CodeHeap::header_size() { + return sizeof(HeapBlock); +} + + +// Implementation of Heap + +CodeHeap::CodeHeap() { + _number_of_committed_segments = 0; + _number_of_reserved_segments = 0; + _segment_size = 0; + _log2_segment_size = 0; + _next_segment = 0; + _freelist = NULL; + _free_segments = 0; +} + + +void CodeHeap::mark_segmap_as_free(size_t beg, size_t end) { + assert(0 <= beg && beg < _number_of_committed_segments, "interval begin out of bounds"); + assert(beg < end && end <= _number_of_committed_segments, "interval end out of bounds"); + // setup _segmap pointers for faster indexing + address p = (address)_segmap.low() + beg; + address q = (address)_segmap.low() + end; + // initialize interval + while (p < q) *p++ = 0xFF; +} + + +void CodeHeap::mark_segmap_as_used(size_t beg, size_t end) { + assert(0 <= beg && beg < _number_of_committed_segments, "interval begin out of bounds"); + assert(beg < end && end <= _number_of_committed_segments, "interval end out of bounds"); + // setup _segmap pointers for faster indexing + address p = (address)_segmap.low() + beg; + address q = (address)_segmap.low() + end; + // initialize interval + int i = 0; + while (p < q) { + *p++ = i++; + if (i == 0xFF) i = 1; + } +} + + +static size_t align_to_page_size(size_t size) { + const size_t alignment = (size_t)os::vm_page_size(); + assert(is_power_of_2(alignment), "no kidding ???"); + return (size + alignment - 1) & ~(alignment - 1); +} + + +static size_t align_to_allocation_size(size_t size) { + const size_t alignment = (size_t)os::vm_allocation_granularity(); + assert(is_power_of_2(alignment), "no kidding ???"); + return (size + alignment - 1) & ~(alignment - 1); +} + + +void CodeHeap::on_code_mapping(char* base, size_t size) { +#ifdef LINUX + extern void linux_wrap_code(char* base, size_t size); + linux_wrap_code(base, size); +#endif +} + + +bool CodeHeap::reserve(size_t reserved_size, size_t committed_size, + size_t segment_size) { + assert(reserved_size >= committed_size, "reserved < committed"); + assert(segment_size >= sizeof(FreeBlock), "segment size is too small"); + assert(is_power_of_2(segment_size), "segment_size must be a power of 2"); + + _segment_size = segment_size; + _log2_segment_size = exact_log2(segment_size); + + // Reserve and initialize space for _memory. + const size_t page_size = os::page_size_for_region(committed_size, + reserved_size, 8); + const size_t granularity = os::vm_allocation_granularity(); + const size_t r_align = MAX2(page_size, granularity); + const size_t r_size = align_size_up(reserved_size, r_align); + const size_t c_size = align_size_up(committed_size, page_size); + + const size_t rs_align = page_size == (size_t) os::vm_page_size() ? 0 : + MAX2(page_size, granularity); + ReservedSpace rs(r_size, rs_align, false); + os::trace_page_sizes("code heap", committed_size, reserved_size, page_size, + rs.base(), rs.size()); + if (!_memory.initialize(rs, c_size)) { + return false; + } + + on_code_mapping(_memory.low(), _memory.committed_size()); + _number_of_committed_segments = number_of_segments(_memory.committed_size()); + _number_of_reserved_segments = number_of_segments(_memory.reserved_size()); + assert(_number_of_reserved_segments >= _number_of_committed_segments, "just checking"); + + // reserve space for _segmap + if (!_segmap.initialize(align_to_page_size(_number_of_reserved_segments), align_to_page_size(_number_of_committed_segments))) { + return false; + } + assert(_segmap.committed_size() >= (size_t) _number_of_committed_segments, "could not commit enough space for segment map"); + assert(_segmap.reserved_size() >= (size_t) _number_of_reserved_segments , "could not reserve enough space for segment map"); + assert(_segmap.reserved_size() >= _segmap.committed_size() , "just checking"); + + // initialize remaining instance variables + clear(); + return true; +} + + +void CodeHeap::release() { + Unimplemented(); +} + + +bool CodeHeap::expand_by(size_t size) { + // expand _memory space + size_t dm = align_to_page_size(_memory.committed_size() + size) - _memory.committed_size(); + if (dm > 0) { + char* base = _memory.low() + _memory.committed_size(); + if (!_memory.expand_by(dm)) return false; + on_code_mapping(base, dm); + size_t i = _number_of_committed_segments; + _number_of_committed_segments = number_of_segments(_memory.committed_size()); + assert(_number_of_reserved_segments == number_of_segments(_memory.reserved_size()), "number of reserved segments should not change"); + assert(_number_of_reserved_segments >= _number_of_committed_segments, "just checking"); + // expand _segmap space + size_t ds = align_to_page_size(_number_of_committed_segments) - _segmap.committed_size(); + if (ds > 0) { + if (!_segmap.expand_by(ds)) return false; + } + assert(_segmap.committed_size() >= (size_t) _number_of_committed_segments, "just checking"); + // initialize additional segmap entries + mark_segmap_as_free(i, _number_of_committed_segments); + } + return true; +} + + +void CodeHeap::shrink_by(size_t size) { + Unimplemented(); +} + + +void CodeHeap::clear() { + _next_segment = 0; + mark_segmap_as_free(0, _number_of_committed_segments); +} + + +void* CodeHeap::allocate(size_t size) { + size_t length = number_of_segments(size + sizeof(HeapBlock)); + assert(length *_segment_size >= sizeof(FreeBlock), "not enough room for FreeList"); + + // First check if we can satify request from freelist + debug_only(verify()); + HeapBlock* block = search_freelist(length); + debug_only(if (VerifyCodeCacheOften) verify()); + if (block != NULL) { + assert(block->length() >= length && block->length() < length + CodeCacheMinBlockLength, "sanity check"); + assert(!block->free(), "must be marked free"); +#ifdef ASSERT + memset((void *)block->allocated_space(), badCodeHeapNewVal, size); +#endif + return block->allocated_space(); + } + + if (length < CodeCacheMinBlockLength) { + length = CodeCacheMinBlockLength; + } + if (_next_segment + length <= _number_of_committed_segments) { + mark_segmap_as_used(_next_segment, _next_segment + length); + HeapBlock* b = block_at(_next_segment); + b->initialize(length); + _next_segment += length; +#ifdef ASSERT + memset((void *)b->allocated_space(), badCodeHeapNewVal, size); +#endif + return b->allocated_space(); + } else { + return NULL; + } +} + + +void CodeHeap::deallocate(void* p) { + assert(p == find_start(p), "illegal deallocation"); + // Find start of HeapBlock + HeapBlock* b = (((HeapBlock *)p) - 1); + assert(b->allocated_space() == p, "sanity check"); +#ifdef ASSERT + memset((void *)b->allocated_space(), + badCodeHeapFreeVal, + size(b->length()) - sizeof(HeapBlock)); +#endif + add_to_freelist(b); + + debug_only(if (VerifyCodeCacheOften) verify()); +} + + +void* CodeHeap::find_start(void* p) const { + if (!contains(p)) { + return NULL; + } + size_t i = segment_for(p); + address b = (address)_segmap.low(); + if (b[i] == 0xFF) { + return NULL; + } + while (b[i] > 0) i -= (int)b[i]; + HeapBlock* h = block_at(i); + if (h->free()) { + return NULL; + } + return h->allocated_space(); +} + + +size_t CodeHeap::alignment_unit() const { + // this will be a power of two + return _segment_size; +} + + +size_t CodeHeap::alignment_offset() const { + // The lowest address in any allocated block will be + // equal to alignment_offset (mod alignment_unit). + return sizeof(HeapBlock) & (_segment_size - 1); +} + +// Finds the next free heapblock. If the current one is free, that it returned +void* CodeHeap::next_free(HeapBlock *b) const { + // Since free blocks are merged, there is max. on free block + // between two used ones + if (b != NULL && b->free()) b = next_block(b); + assert(b == NULL || !b->free(), "must be in use or at end of heap"); + return (b == NULL) ? NULL : b->allocated_space(); +} + +// Returns the first used HeapBlock +HeapBlock* CodeHeap::first_block() const { + if (_next_segment > 0) + return block_at(0); + return NULL; +} + +HeapBlock *CodeHeap::block_start(void *q) const { + HeapBlock* b = (HeapBlock*)find_start(q); + if (b == NULL) return NULL; + return b - 1; +} + +// Returns the next Heap block an offset into one +HeapBlock* CodeHeap::next_block(HeapBlock *b) const { + if (b == NULL) return NULL; + size_t i = segment_for(b) + b->length(); + if (i < _next_segment) + return block_at(i); + return NULL; +} + + +// Returns current capacity +size_t CodeHeap::capacity() const { + return _memory.committed_size(); +} + +size_t CodeHeap::max_capacity() const { + return _memory.reserved_size(); +} + +size_t CodeHeap::allocated_capacity() const { + // Start with the committed size in _memory; + size_t l = _memory.committed_size(); + + // Subtract the committed, but unused, segments + l -= size(_number_of_committed_segments - _next_segment); + + // Subtract the size of the freelist + l -= size(_free_segments); + + return l; +} + +// Free list management + +FreeBlock *CodeHeap::following_block(FreeBlock *b) { + return (FreeBlock*)(((address)b) + _segment_size * b->length()); +} + +// Inserts block b after a +void CodeHeap::insert_after(FreeBlock* a, FreeBlock* b) { + assert(a != NULL && b != NULL, "must be real pointers"); + + // Link b into the list after a + b->set_link(a->link()); + a->set_link(b); + + // See if we can merge blocks + merge_right(b); // Try to make b bigger + merge_right(a); // Try to make a include b +} + +// Try to merge this block with the following block +void CodeHeap::merge_right(FreeBlock *a) { + assert(a->free(), "must be a free block"); + if (following_block(a) == a->link()) { + assert(a->link() != NULL && a->link()->free(), "must be free too"); + // Update block a to include the following block + a->set_length(a->length() + a->link()->length()); + a->set_link(a->link()->link()); + // Update find_start map + size_t beg = segment_for(a); + mark_segmap_as_used(beg, beg + a->length()); + } +} + +void CodeHeap::add_to_freelist(HeapBlock *a) { + FreeBlock* b = (FreeBlock*)a; + assert(b != _freelist, "cannot be removed twice"); + + // Mark as free and update free space count + _free_segments += b->length(); + b->set_free(); + + // First element in list? + if (_freelist == NULL) { + _freelist = b; + b->set_link(NULL); + return; + } + + // Scan for right place to put into list. List + // is sorted by increasing addresseses + FreeBlock* prev = NULL; + FreeBlock* cur = _freelist; + while(cur != NULL && cur < b) { + assert(prev == NULL || prev < cur, "must be ordered"); + prev = cur; + cur = cur->link(); + } + + assert( (prev == NULL && b < _freelist) || + (prev < b && (cur == NULL || b < cur)), "list must be ordered"); + + if (prev == NULL) { + // Insert first in list + b->set_link(_freelist); + _freelist = b; + merge_right(_freelist); + } else { + insert_after(prev, b); + } +} + +// Search freelist for an entry on the list with the best fit +// Return NULL if no one was found +FreeBlock* CodeHeap::search_freelist(size_t length) { + FreeBlock *best_block = NULL; + FreeBlock *best_prev = NULL; + size_t best_length = 0; + + // Search for smallest block which is bigger than length + FreeBlock *prev = NULL; + FreeBlock *cur = _freelist; + while(cur != NULL) { + size_t l = cur->length(); + if (l >= length && (best_block == NULL || best_length > l)) { + // Remember best block, its previous element, and its length + best_block = cur; + best_prev = prev; + best_length = best_block->length(); + } + + // Next element in list + prev = cur; + cur = cur->link(); + } + + if (best_block == NULL) { + // None found + return NULL; + } + + assert((best_prev == NULL && _freelist == best_block ) || + (best_prev != NULL && best_prev->link() == best_block), "sanity check"); + + // Exact (or at least good enough) fit. Remove from list. + // Don't leave anything on the freelist smaller than CodeCacheMinBlockLength. + if (best_length < length + CodeCacheMinBlockLength) { + length = best_length; + if (best_prev == NULL) { + assert(_freelist == best_block, "sanity check"); + _freelist = _freelist->link(); + } else { + // Unmap element + best_prev->set_link(best_block->link()); + } + } else { + // Truncate block and return a pointer to the following block + best_block->set_length(best_length - length); + best_block = following_block(best_block); + // Set used bit and length on new block + size_t beg = segment_for(best_block); + mark_segmap_as_used(beg, beg + length); + best_block->set_length(length); + } + + best_block->set_used(); + _free_segments -= length; + return best_block; +} + +//---------------------------------------------------------------------------- +// Non-product code + +#ifndef PRODUCT + +void CodeHeap::print() { + tty->print_cr("The Heap"); +} + +#endif + +void CodeHeap::verify() { + // Count the number of blocks on the freelist, and the amount of space + // represented. + int count = 0; + size_t len = 0; + for(FreeBlock* b = _freelist; b != NULL; b = b->link()) { + len += b->length(); + count++; + } + + // Verify that freelist contains the right amount of free space + guarantee(len == _free_segments, "wrong freelist"); + + // Verify that the number of free blocks is not out of hand. + static int free_block_threshold = 10000; + if (count > free_block_threshold) { + warning("CodeHeap: # of free blocks > %d", free_block_threshold); + // Double the warning limit + free_block_threshold *= 2; + } + + // Verify that the freelist contains the same number of free blocks that is + // found on the full list. + for(HeapBlock *h = first_block(); h != NULL; h = next_block(h)) { + if (h->free()) count--; + } + guarantee(count == 0, "missing free blocks"); +} diff --git a/hotspot/src/share/vm/memory/heap.hpp b/hotspot/src/share/vm/memory/heap.hpp new file mode 100644 index 00000000000..913d9bc748b --- /dev/null +++ b/hotspot/src/share/vm/memory/heap.hpp @@ -0,0 +1,161 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Blocks + +class HeapBlock VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + + public: + struct Header { + size_t _length; // the length in segments + bool _used; // Used bit + }; + + protected: + union { + Header _header; + int64_t _padding[ (sizeof(Header) + sizeof(int64_t)-1) / sizeof(int64_t) ]; + // pad to 0 mod 8 + }; + + public: + // Initialization + void initialize(size_t length) { _header._length = length; set_used(); } + + // Accessors + void* allocated_space() const { return (void*)(this + 1); } + size_t length() const { return _header._length; } + + // Used/free + void set_used() { _header._used = true; } + void set_free() { _header._used = false; } + bool free() { return !_header._used; } +}; + +class FreeBlock: public HeapBlock { + friend class VMStructs; + protected: + FreeBlock* _link; + + public: + // Initialization + void initialize(size_t length) { HeapBlock::initialize(length); _link= NULL; } + + // Merging + void set_length(size_t l) { _header._length = l; } + + // Accessors + FreeBlock* link() const { return _link; } + void set_link(FreeBlock* link) { _link = link; } +}; + +class CodeHeap : public CHeapObj { + friend class VMStructs; + private: + VirtualSpace _memory; // the memory holding the blocks + VirtualSpace _segmap; // the memory holding the segment map + + size_t _number_of_committed_segments; + size_t _number_of_reserved_segments; + size_t _segment_size; + int _log2_segment_size; + + size_t _next_segment; + + FreeBlock* _freelist; + size_t _free_segments; // No. of segments in freelist + + // Helper functions + size_t number_of_segments(size_t size) const { return (size + _segment_size - 1) >> _log2_segment_size; } + size_t size(size_t number_of_segments) const { return number_of_segments << _log2_segment_size; } + + size_t segment_for(void* p) const { return ((char*)p - _memory.low()) >> _log2_segment_size; } + HeapBlock* block_at(size_t i) const { return (HeapBlock*)(_memory.low() + (i << _log2_segment_size)); } + + void mark_segmap_as_free(size_t beg, size_t end); + void mark_segmap_as_used(size_t beg, size_t end); + + // Freelist management helpers + FreeBlock* following_block(FreeBlock *b); + void insert_after(FreeBlock* a, FreeBlock* b); + void merge_right (FreeBlock* a); + + // Toplevel freelist management + void add_to_freelist(HeapBlock *b); + FreeBlock* search_freelist(size_t length); + + // Iteration helpers + void* next_free(HeapBlock* b) const; + HeapBlock* first_block() const; + HeapBlock* next_block(HeapBlock* b) const; + HeapBlock* block_start(void* p) const; + + // to perform additional actions on creation of executable code + void on_code_mapping(char* base, size_t size); + + public: + CodeHeap(); + + // Heap extents + bool reserve(size_t reserved_size, size_t committed_size, size_t segment_size); + void release(); // releases all allocated memory + bool expand_by(size_t size); // expands commited memory by size + void shrink_by(size_t size); // shrinks commited memory by size + void clear(); // clears all heap contents + + // Memory allocation + void* allocate (size_t size); // allocates a block of size or returns NULL + void deallocate(void* p); // deallocates a block + + // Attributes + void* begin() const { return _memory.low (); } + void* end() const { return _memory.high(); } + bool contains(void* p) const { return begin() <= p && p < end(); } + void* find_start(void* p) const; // returns the block containing p or NULL + size_t alignment_unit() const; // alignment of any block + size_t alignment_offset() const; // offset of first byte of any block, within the enclosing alignment unit + static size_t header_size(); // returns the header size for each heap block + + // Returns reserved area high and low addresses + char *low_boundary() const { return _memory.low_boundary (); } + char *high_boundary() const { return _memory.high_boundary(); } + + // Iteration + + // returns the first block or NULL + void* first() const { return next_free(first_block()); } + // returns the next block given a block p or NULL + void* next(void* p) const { return next_free(next_block(block_start(p))); } + + // Statistics + size_t capacity() const; + size_t max_capacity() const; + size_t allocated_capacity() const; + size_t unallocated_capacity() const { return max_capacity() - allocated_capacity(); } + + // Debugging + void verify(); + void print() PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/memory/heapInspection.cpp b/hotspot/src/share/vm/memory/heapInspection.cpp new file mode 100644 index 00000000000..ac8aa641fc2 --- /dev/null +++ b/hotspot/src/share/vm/memory/heapInspection.cpp @@ -0,0 +1,281 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_heapInspection.cpp.incl" + +// HeapInspection + +int KlassInfoEntry::compare(KlassInfoEntry* e1, KlassInfoEntry* e2) { + if(e1->_instance_words > e2->_instance_words) { + return -1; + } else if(e1->_instance_words < e2->_instance_words) { + return 1; + } + return 0; +} + +void KlassInfoEntry::print_on(outputStream* st) const { + ResourceMark rm; + const char* name;; + if (_klass->klass_part()->name() != NULL) { + name = _klass->klass_part()->external_name(); + } else { + if (_klass == Universe::klassKlassObj()) name = ""; else + if (_klass == Universe::arrayKlassKlassObj()) name = ""; else + if (_klass == Universe::objArrayKlassKlassObj()) name = ""; else + if (_klass == Universe::instanceKlassKlassObj()) name = ""; else + if (_klass == Universe::typeArrayKlassKlassObj()) name = ""; else + if (_klass == Universe::symbolKlassObj()) name = ""; else + if (_klass == Universe::boolArrayKlassObj()) name = ""; else + if (_klass == Universe::charArrayKlassObj()) name = ""; else + if (_klass == Universe::singleArrayKlassObj()) name = ""; else + if (_klass == Universe::doubleArrayKlassObj()) name = ""; else + if (_klass == Universe::byteArrayKlassObj()) name = ""; else + if (_klass == Universe::shortArrayKlassObj()) name = ""; else + if (_klass == Universe::intArrayKlassObj()) name = ""; else + if (_klass == Universe::longArrayKlassObj()) name = ""; else + if (_klass == Universe::methodKlassObj()) name = ""; else + if (_klass == Universe::constMethodKlassObj()) name = ""; else + if (_klass == Universe::methodDataKlassObj()) name = ""; else + if (_klass == Universe::constantPoolKlassObj()) name = ""; else + if (_klass == Universe::constantPoolCacheKlassObj()) name = ""; else + if (_klass == Universe::compiledICHolderKlassObj()) name = ""; else + name = ""; + } + // simplify the formatting (ILP32 vs LP64) - always cast the numbers to 64-bit + st->print_cr("%13" FORMAT64_MODIFIER "d %13" FORMAT64_MODIFIER "u %s", + (jlong) _instance_count, + (julong) _instance_words * HeapWordSize, + name); +} + +KlassInfoEntry* KlassInfoBucket::lookup(const klassOop k) { + KlassInfoEntry* elt = _list; + while (elt != NULL) { + if (elt->is_equal(k)) { + return elt; + } + elt = elt->next(); + } + elt = new KlassInfoEntry(k, list()); + set_list(elt); + return elt; +} + +void KlassInfoBucket::iterate(KlassInfoClosure* cic) { + KlassInfoEntry* elt = _list; + while (elt != NULL) { + cic->do_cinfo(elt); + elt = elt->next(); + } +} + +void KlassInfoBucket::empty() { + KlassInfoEntry* elt = _list; + _list = NULL; + while (elt != NULL) { + KlassInfoEntry* next = elt->next(); + delete elt; + elt = next; + } +} + +KlassInfoTable::KlassInfoTable(int size, HeapWord* ref) { + _size = size; + _ref = ref; + _buckets = NEW_C_HEAP_ARRAY(KlassInfoBucket, _size); + + for (int index = 0; index < _size; index++) { + _buckets[index].initialize(); + } +} + +KlassInfoTable::~KlassInfoTable() { + for (int index = 0; index < _size; index++) { + _buckets[index].empty(); + } + FREE_C_HEAP_ARRAY(KlassInfoBucket, _buckets); + _size = 0; +} + +uint KlassInfoTable::hash(klassOop p) { + assert(Universe::heap()->is_in_permanent((HeapWord*)p), "all klasses in permgen"); + return (uint)(((uintptr_t)p - (uintptr_t)_ref) >> 2); +} + +KlassInfoEntry* KlassInfoTable::lookup(const klassOop k) { + uint idx = hash(k) % _size; + KlassInfoEntry* e = _buckets[idx].lookup(k); + assert(k == e->klass(), "must be equal"); + return e; +} + +void KlassInfoTable::record_instance(const oop obj) { + klassOop k = obj->klass(); + KlassInfoEntry* elt = lookup(k); + elt->set_count(elt->count() + 1); + elt->set_words(elt->words() + obj->size()); +} + +void KlassInfoTable::iterate(KlassInfoClosure* cic) { + for (int index = 0; index < _size; index++) { + _buckets[index].iterate(cic); + } +} + +int KlassInfoHisto::sort_helper(KlassInfoEntry** e1, KlassInfoEntry** e2) { + return (*e1)->compare(*e1,*e2); +} + +KlassInfoHisto::KlassInfoHisto(const char* title, int estimatedCount) : + _title(title) { + _elements = new (ResourceObj::C_HEAP) GrowableArray(estimatedCount,true); +} + +KlassInfoHisto::~KlassInfoHisto() { + delete _elements; +} + +void KlassInfoHisto::add(KlassInfoEntry* cie) { + elements()->append(cie); +} + +void KlassInfoHisto::sort() { + elements()->sort(KlassInfoHisto::sort_helper); +} + +void KlassInfoHisto::print_elements(outputStream* st) const { + // simplify the formatting (ILP32 vs LP64) - store the sum in 64-bit + jlong total = 0; + julong totalw = 0; + for(int i=0; i < elements()->length(); i++) { + st->print("%4d: ", i+1); + elements()->at(i)->print_on(st); + total += elements()->at(i)->count(); + totalw += elements()->at(i)->words(); + } + st->print_cr("Total %13" FORMAT64_MODIFIER "d %13" FORMAT64_MODIFIER "u", + total, totalw * HeapWordSize); +} + +void KlassInfoHisto::print_on(outputStream* st) const { + st->print_cr("%s",title()); + print_elements(st); +} + +class HistoClosure : public KlassInfoClosure { + private: + KlassInfoHisto* _cih; + public: + HistoClosure(KlassInfoHisto* cih) : _cih(cih) {} + + void do_cinfo(KlassInfoEntry* cie) { + _cih->add(cie); + } +}; + +class RecordInstanceClosure : public ObjectClosure { + private: + KlassInfoTable* _cit; + public: + RecordInstanceClosure(KlassInfoTable* cit) : _cit(cit) {} + + void do_object(oop obj) { + _cit->record_instance(obj); + } +}; + +void HeapInspection::heap_inspection(outputStream* st) { + ResourceMark rm; + HeapWord* ref; + + CollectedHeap* heap = Universe::heap(); + switch (heap->kind()) { + case CollectedHeap::GenCollectedHeap: { + GenCollectedHeap* gch = (GenCollectedHeap*)heap; + gch->gc_prologue(false /* !full */); // get any necessary locks + ref = gch->perm_gen()->used_region().start(); + break; + } +#ifndef SERIALGC + case CollectedHeap::ParallelScavengeHeap: { + ParallelScavengeHeap* psh = (ParallelScavengeHeap*)heap; + ref = psh->perm_gen()->object_space()->used_region().start(); + break; + } +#endif // SERIALGC + default: + ShouldNotReachHere(); // Unexpected heap kind for this op + } + // Collect klass instance info + + // Iterate over objects in the heap + KlassInfoTable cit(KlassInfoTable::cit_size, ref); + RecordInstanceClosure ric(&cit); + Universe::heap()->object_iterate(&ric); + + // Sort and print klass instance info + KlassInfoHisto histo("\n" + " num #instances #bytes class name\n" + "----------------------------------------------", + KlassInfoHisto::histo_initial_size); + HistoClosure hc(&histo); + cit.iterate(&hc); + histo.sort(); + histo.print_on(st); + st->flush(); + + if (Universe::heap()->kind() == CollectedHeap::GenCollectedHeap) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + gch->gc_epilogue(false /* !full */); // release all acquired locks + } +} + +class FindInstanceClosure : public ObjectClosure { + private: + klassOop _klass; + GrowableArray* _result; + + public: + FindInstanceClosure(klassOop k, GrowableArray* result) : _klass(k), _result(result) {}; + + void do_object(oop obj) { + if (obj->is_a(_klass)) { + _result->append(obj); + } + } +}; + +void HeapInspection::find_instances_at_safepoint(klassOop k, GrowableArray* result) { + assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped"); + assert(Heap_lock->is_locked(), "should have the Heap_lock") + + // Ensure that the heap is parsable + Universe::heap()->ensure_parsability(false); // no need to retire TALBs + + // Iterate over objects in the heap + FindInstanceClosure fic(k, result); + Universe::heap()->object_iterate(&fic); +} diff --git a/hotspot/src/share/vm/memory/heapInspection.hpp b/hotspot/src/share/vm/memory/heapInspection.hpp new file mode 100644 index 00000000000..d56f8518c5f --- /dev/null +++ b/hotspot/src/share/vm/memory/heapInspection.hpp @@ -0,0 +1,131 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef SERVICES_KERNEL + + +// HeapInspection + +// KlassInfoTable is a bucket hash table that +// maps klassOops to extra information: +// instance count and instance word size. +// +// A KlassInfoBucket is the head of a link list +// of KlassInfoEntry's +// +// KlassInfoHisto is a growable array of pointers +// to KlassInfoEntry's and is used to sort +// the entries. + +class KlassInfoEntry: public CHeapObj { + private: + KlassInfoEntry* _next; + klassOop _klass; + long _instance_count; + size_t _instance_words; + + public: + KlassInfoEntry(klassOop k, KlassInfoEntry* next) : + _klass(k), _instance_count(0), _instance_words(0), _next(next) + {} + KlassInfoEntry* next() { return _next; } + bool is_equal(klassOop k) { return k == _klass; } + klassOop klass() { return _klass; } + long count() { return _instance_count; } + void set_count(long ct) { _instance_count = ct; } + size_t words() { return _instance_words; } + void set_words(size_t wds) { _instance_words = wds; } + int compare(KlassInfoEntry* e1, KlassInfoEntry* e2); + void print_on(outputStream* st) const; +}; + +class KlassInfoClosure: public StackObj { + public: + // Called for each KlassInfoEntry. + virtual void do_cinfo(KlassInfoEntry* cie) = 0; +}; + +class KlassInfoBucket: public CHeapObj { + private: + KlassInfoEntry* _list; + KlassInfoEntry* list() { return _list; } + void set_list(KlassInfoEntry* l) { _list = l; } + public: + KlassInfoEntry* lookup(const klassOop k); + void initialize() { _list = NULL; } + void empty(); + void iterate(KlassInfoClosure* cic); +}; + +class KlassInfoTable: public StackObj { + private: + int _size; + + // An aligned reference address (typically the least + // address in the perm gen) used for hashing klass + // objects. + HeapWord* _ref; + + KlassInfoBucket* _buckets; + uint hash(klassOop p); + KlassInfoEntry* lookup(const klassOop k); + + public: + // Table size + enum { + cit_size = 20011 + }; + KlassInfoTable(int size, HeapWord* ref); + ~KlassInfoTable(); + void record_instance(const oop obj); + void iterate(KlassInfoClosure* cic); +}; + +class KlassInfoHisto : public StackObj { + private: + GrowableArray* _elements; + GrowableArray* elements() const { return _elements; } + const char* _title; + const char* title() const { return _title; } + static int sort_helper(KlassInfoEntry** e1, KlassInfoEntry** e2); + void print_elements(outputStream* st) const; + public: + enum { + histo_initial_size = 1000 + }; + KlassInfoHisto(const char* title, + int estimatedCount); + ~KlassInfoHisto(); + void add(KlassInfoEntry* cie); + void print_on(outputStream* st) const; + void sort(); +}; + +#endif // SERVICES_KERNEL + +class HeapInspection : public AllStatic { + public: + static void heap_inspection(outputStream* st) KERNEL_RETURN; + static void find_instances_at_safepoint(klassOop k, GrowableArray* result) KERNEL_RETURN; +}; diff --git a/hotspot/src/share/vm/memory/iterator.cpp b/hotspot/src/share/vm/memory/iterator.cpp new file mode 100644 index 00000000000..d52eeb90481 --- /dev/null +++ b/hotspot/src/share/vm/memory/iterator.cpp @@ -0,0 +1,34 @@ +/* + * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_iterator.cpp.incl" + +void ObjectToOopClosure::do_object(oop obj) { + obj->oop_iterate(_cl); +} + +void VoidClosure::do_void() { + ShouldNotCallThis(); +} diff --git a/hotspot/src/share/vm/memory/iterator.hpp b/hotspot/src/share/vm/memory/iterator.hpp new file mode 100644 index 00000000000..29be107d681 --- /dev/null +++ b/hotspot/src/share/vm/memory/iterator.hpp @@ -0,0 +1,200 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The following classes are C++ `closures` for iterating over objects, roots and spaces + +class ReferenceProcessor; + +// OopClosure is used for iterating through roots (oop*) + +class OopClosure : public StackObj { + public: + ReferenceProcessor* _ref_processor; + OopClosure(ReferenceProcessor* rp) : _ref_processor(rp) { } + OopClosure() : _ref_processor(NULL) { } + virtual void do_oop(oop* o) = 0; + virtual void do_oop_v(oop* o) { do_oop(o); } + + // In support of post-processing of weak links of KlassKlass objects; + // see KlassKlass::oop_oop_iterate(). + virtual const bool should_remember_klasses() const { return false; } + virtual void remember_klass(Klass* k) { /* do nothing */ } + + // If "true", invoke on nmethods (when scanning compiled frames). + virtual const bool do_nmethods() const { return false; } + + // The methods below control how object iterations invoking this closure + // should be performed: + + // If "true", invoke on header klass field. + bool do_header() { return true; } // Note that this is non-virtual. + // Controls how prefetching is done for invocations of this closure. + Prefetch::style prefetch_style() { // Note that this is non-virtual. + return Prefetch::do_none; + } +}; + +// ObjectClosure is used for iterating through an object space + +class ObjectClosure : public StackObj { + public: + // Called for each object. + virtual void do_object(oop obj) = 0; +}; + + +class BoolObjectClosure : public ObjectClosure { + public: + virtual bool do_object_b(oop obj) = 0; +}; + +// Applies an oop closure to all ref fields in objects iterated over in an +// object iteration. +class ObjectToOopClosure: public ObjectClosure { + OopClosure* _cl; +public: + void do_object(oop obj); + ObjectToOopClosure(OopClosure* cl) : _cl(cl) {} +}; + +// A version of ObjectClosure with "memory" (see _previous_address below) +class UpwardsObjectClosure: public BoolObjectClosure { + HeapWord* _previous_address; + public: + UpwardsObjectClosure() : _previous_address(NULL) { } + void set_previous(HeapWord* addr) { _previous_address = addr; } + HeapWord* previous() { return _previous_address; } + // A return value of "true" can be used by the caller to decide + // if this object's end should *NOT* be recorded in + // _previous_address above. + virtual bool do_object_bm(oop obj, MemRegion mr) = 0; +}; + +// A version of ObjectClosure that is expected to be robust +// in the face of possibly uninitialized objects. +class ObjectClosureCareful : public ObjectClosure { + public: + virtual size_t do_object_careful_m(oop p, MemRegion mr) = 0; + virtual size_t do_object_careful(oop p) = 0; +}; + +// The following are used in CompactibleFreeListSpace and +// ConcurrentMarkSweepGeneration. + +// Blk closure (abstract class) +class BlkClosure : public StackObj { + public: + virtual size_t do_blk(HeapWord* addr) = 0; +}; + +// A version of BlkClosure that is expected to be robust +// in the face of possibly uninitialized objects. +class BlkClosureCareful : public BlkClosure { + public: + size_t do_blk(HeapWord* addr) { + guarantee(false, "call do_blk_careful instead"); + return 0; + } + virtual size_t do_blk_careful(HeapWord* addr) = 0; +}; + +// SpaceClosure is used for iterating over spaces + +class Space; +class CompactibleSpace; + +class SpaceClosure : public StackObj { + public: + // Called for each space + virtual void do_space(Space* s) = 0; +}; + +class CompactibleSpaceClosure : public StackObj { + public: + // Called for each compactible space + virtual void do_space(CompactibleSpace* s) = 0; +}; + + + +// MonitorClosure is used for iterating over monitors in the monitors cache + +class ObjectMonitor; + +class MonitorClosure : public StackObj { + public: + // called for each monitor in cache + virtual void do_monitor(ObjectMonitor* m) = 0; +}; + +// A closure that is applied without any arguments. +class VoidClosure : public StackObj { + public: + // I would have liked to declare this a pure virtual, but that breaks + // in mysterious ways, for unknown reasons. + virtual void do_void(); +}; + + +// YieldClosure is intended for use by iteration loops +// to incrementalize their work, allowing interleaving +// of an interruptable task so as to allow other +// threads to run (which may not otherwise be able to access +// exclusive resources, for instance). Additionally, the +// closure also allows for aborting an ongoing iteration +// by means of checking the return value from the polling +// call. +class YieldClosure : public StackObj { + public: + virtual bool should_return() = 0; +}; + +// Abstract closure for serializing data (read or write). + +class SerializeOopClosure : public OopClosure { +public: + // Return bool indicating whether closure implements read or write. + virtual bool reading() const = 0; + + // Read/write the int pointed to by i. + virtual void do_int(int* i) = 0; + + // Read/write the size_t pointed to by i. + virtual void do_size_t(size_t* i) = 0; + + // Read/write the void pointer pointed to by p. + virtual void do_ptr(void** p) = 0; + + // Read/write the HeapWord pointer pointed to be p. + virtual void do_ptr(HeapWord** p) = 0; + + // Read/write the region specified. + virtual void do_region(u_char* start, size_t size) = 0; + + // Check/write the tag. If reading, then compare the tag against + // the passed in value and fail is they don't match. This allows + // for verification that sections of the serialized data are of the + // correct length. + virtual void do_tag(int tag) = 0; +}; diff --git a/hotspot/src/share/vm/memory/memRegion.cpp b/hotspot/src/share/vm/memory/memRegion.cpp new file mode 100644 index 00000000000..508bf02960f --- /dev/null +++ b/hotspot/src/share/vm/memory/memRegion.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A very simple data structure representing a contigous word-aligned +// region of address space. + +#include "incls/_precompiled.incl" +#include "incls/_memRegion.cpp.incl" + +MemRegion MemRegion::intersection(const MemRegion mr2) const { + MemRegion res; + HeapWord* res_start = MAX2(start(), mr2.start()); + HeapWord* res_end = MIN2(end(), mr2.end()); + if (res_start < res_end) { + res.set_start(res_start); + res.set_end(res_end); + } + return res; +} + +MemRegion MemRegion::_union(const MemRegion mr2) const { + // If one region is empty, return the other + if (is_empty()) return mr2; + if (mr2.is_empty()) return MemRegion(start(), end()); + + // Otherwise, regions must overlap or be adjacent + assert(((start() <= mr2.start()) && (end() >= mr2.start())) || + ((mr2.start() <= start()) && (mr2.end() >= start())), + "non-adjacent or overlapping regions"); + MemRegion res; + HeapWord* res_start = MIN2(start(), mr2.start()); + HeapWord* res_end = MAX2(end(), mr2.end()); + res.set_start(res_start); + res.set_end(res_end); + return res; +} + +MemRegion MemRegion::minus(const MemRegion mr2) const { + // There seem to be 6 cases: + // |this MemRegion| + // |strictly below| + // |overlap beginning| + // |interior| + // |overlap ending| + // |strictly above| + // |completely overlapping| + // We can't deal with an interior case because it would + // produce two disjoint regions as a result. + // We aren't trying to be optimal in the number of tests below, + // but the order is important to distinguish the strictly cases + // from the overlapping cases. + if (mr2.end() <= start()) { + // strictly below + return MemRegion(start(), end()); + } + if (mr2.start() <= start() && mr2.end() <= end()) { + // overlap beginning + return MemRegion(mr2.end(), end()); + } + if (mr2.start() >= end()) { + // strictly above + return MemRegion(start(), end()); + } + if (mr2.start() >= start() && mr2.end() >= end()) { + // overlap ending + return MemRegion(start(), mr2.start()); + } + if (mr2.start() <= start() && mr2.end() >= end()) { + // completely overlapping + return MemRegion(); + } + if (mr2.start() > start() && mr2.end() < end()) { + // interior + guarantee(false, "MemRegion::minus, but interior"); + return MemRegion(); + } + ShouldNotReachHere(); + return MemRegion(); +} diff --git a/hotspot/src/share/vm/memory/memRegion.hpp b/hotspot/src/share/vm/memory/memRegion.hpp new file mode 100644 index 00000000000..4bcd7a59b97 --- /dev/null +++ b/hotspot/src/share/vm/memory/memRegion.hpp @@ -0,0 +1,106 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A very simple data structure representing a contigous region +// region of address space. + +// Note that MemRegions are passed by value, not by reference. +// The intent is that they remain very small and contain no +// objects. + +class MemRegion VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; +private: + HeapWord* _start; + size_t _word_size; + +public: + MemRegion() : _start(NULL), _word_size(0) {}; + MemRegion(HeapWord* start, size_t word_size) : + _start(start), _word_size(word_size) {}; + MemRegion(HeapWord* start, HeapWord* end) : + _start(start), _word_size(pointer_delta(end, start)) { + assert(end >= start, "incorrect constructor arguments"); + } + + MemRegion(const MemRegion& mr): _start(mr._start), _word_size(mr._word_size) {} + + MemRegion intersection(const MemRegion mr2) const; + // regions must overlap or be adjacent + MemRegion _union(const MemRegion mr2) const; + // minus will fail a guarantee if mr2 is interior to this, + // since there's no way to return 2 disjoint regions. + MemRegion minus(const MemRegion mr2) const; + + HeapWord* start() const { return _start; } + HeapWord* end() const { return _start + _word_size; } + HeapWord* last() const { return _start + _word_size - 1; } + + void set_start(HeapWord* start) { _start = start; } + void set_end(HeapWord* end) { _word_size = pointer_delta(end, _start); } + void set_word_size(size_t word_size) { + _word_size = word_size; + } + + bool contains(const MemRegion mr2) const { + return _start <= mr2._start && end() >= mr2.end(); + } + bool contains(const void* addr) const { + return addr >= (void*)_start && addr < (void*)end(); + } + bool equals(const MemRegion mr2) const { + // first disjunct since we do not have a canonical empty set + return ((is_empty() && mr2.is_empty()) || + (start() == mr2.start() && end() == mr2.end())); + } + + size_t byte_size() const { return _word_size * sizeof(HeapWord); } + size_t word_size() const { return _word_size; } + + bool is_empty() const { return word_size() == 0; } +}; + +// For iteration over MemRegion's. + +class MemRegionClosure : public StackObj { +public: + virtual void do_MemRegion(MemRegion mr) = 0; +}; + +// A ResourceObj version of MemRegionClosure + +class MemRegionClosureRO: public MemRegionClosure { +public: + void* operator new(size_t size, ResourceObj::allocation_type type) { + return ResourceObj::operator new(size, type); + } + void* operator new(size_t size, Arena *arena) { + return ResourceObj::operator new(size, arena); + } + void* operator new(size_t size) { + return ResourceObj::operator new(size); + } + + void operator delete(void* p) {} // nothing to do +}; diff --git a/hotspot/src/share/vm/memory/modRefBarrierSet.hpp b/hotspot/src/share/vm/memory/modRefBarrierSet.hpp new file mode 100644 index 00000000000..85a94397732 --- /dev/null +++ b/hotspot/src/share/vm/memory/modRefBarrierSet.hpp @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This kind of "BarrierSet" allows a "CollectedHeap" to detect and +// enumerate ref fields that have been modified (since the last +// enumeration), using a card table. + +class OopClosure; +class Generation; + +class ModRefBarrierSet: public BarrierSet { +public: + // Barriers only on ref writes. + bool has_read_ref_barrier() { return false; } + bool has_read_prim_barrier() { return false; } + bool has_write_ref_barrier() { return true; } + bool has_write_prim_barrier() { return false; } + + bool read_ref_needs_barrier(oop* field) { return false; } + bool read_prim_needs_barrier(HeapWord* field, size_t bytes) { return false; } + virtual bool write_ref_needs_barrier(oop* field, oop new_val) = 0; + bool write_prim_needs_barrier(HeapWord* field, size_t bytes, + juint val1, juint val2) { return false; } + + void write_prim_field(oop obj, size_t offset, size_t bytes, + juint val1, juint val2) {} + + void read_ref_field(oop* field) {} + void read_prim_field(HeapWord* field, size_t bytes) {} +protected: + virtual void write_ref_field_work(oop* field, oop new_val) = 0; +public: + void write_prim_field(HeapWord* field, size_t bytes, + juint val1, juint val2) {} + + bool has_read_ref_array_opt() { return false; } + bool has_read_prim_array_opt() { return false; } + bool has_write_prim_array_opt() { return false; } + + bool has_read_region_opt() { return false; } + + + // These operations should assert false unless the correponding operation + // above returns true. + void read_ref_array(MemRegion mr) { + assert(false, "can't call"); + } + void read_prim_array(MemRegion mr) { + assert(false, "can't call"); + } + void write_prim_array(MemRegion mr) { + assert(false, "can't call"); + } + void read_region(MemRegion mr) { + assert(false, "can't call"); + } + + // Invoke "cl->do_oop" on (the address of) every possibly-modifed + // reference field in objects in "sp". If "clear" is "true", the oops + // are no longer considered possibly modified after application of the + // closure. If' "before_save_marks" is true, oops in objects allocated + // after the last call to "save_marks" on "sp" will not be considered. + virtual void mod_oop_in_space_iterate(Space* sp, OopClosure* cl, + bool clear = false, + bool before_save_marks = false) = 0; + + // Causes all refs in "mr" to be assumed to be modified. + virtual void invalidate(MemRegion mr) = 0; + + // The caller guarantees that "mr" contains no references. (Perhaps it's + // objects have been moved elsewhere.) + virtual void clear(MemRegion mr) = 0; + + // Pass along the argument to the superclass. + ModRefBarrierSet(int max_covered_regions) : + BarrierSet(max_covered_regions) {} + +#ifndef PRODUCT + // Verifies that the given region contains no modified references. + virtual void verify_clean_region(MemRegion mr) = 0; +#endif + +}; diff --git a/hotspot/src/share/vm/memory/oopFactory.cpp b/hotspot/src/share/vm/memory/oopFactory.cpp new file mode 100644 index 00000000000..e9ea08d3b23 --- /dev/null +++ b/hotspot/src/share/vm/memory/oopFactory.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_oopFactory.cpp.incl" + + +typeArrayOop oopFactory::new_charArray(const char* utf8_str, TRAPS) { + int length = utf8_str == NULL ? 0 : UTF8::unicode_length(utf8_str); + typeArrayOop result = new_charArray(length, CHECK_NULL); + if (length > 0) { + UTF8::convert_to_unicode(utf8_str, result->char_at_addr(0), length); + } + return result; +} + +typeArrayOop oopFactory::new_permanent_charArray(int length, TRAPS) { + return typeArrayKlass::cast(Universe::charArrayKlassObj())->allocate_permanent(length, THREAD); +} + +typeArrayOop oopFactory::new_permanent_byteArray(int length, TRAPS) { + return typeArrayKlass::cast(Universe::byteArrayKlassObj())->allocate_permanent(length, THREAD); +} + + +typeArrayOop oopFactory::new_permanent_shortArray(int length, TRAPS) { + return typeArrayKlass::cast(Universe::shortArrayKlassObj())->allocate_permanent(length, THREAD); +} + + +typeArrayOop oopFactory::new_permanent_intArray(int length, TRAPS) { + return typeArrayKlass::cast(Universe::intArrayKlassObj())->allocate_permanent(length, THREAD); +} + + +typeArrayOop oopFactory::new_typeArray(BasicType type, int length, TRAPS) { + klassOop type_asKlassOop = Universe::typeArrayKlassObj(type); + typeArrayKlass* type_asArrayKlass = typeArrayKlass::cast(type_asKlassOop); + typeArrayOop result = type_asArrayKlass->allocate(length, THREAD); + return result; +} + + +objArrayOop oopFactory::new_objArray(klassOop klass, int length, TRAPS) { + assert(klass->is_klass(), "must be instance class"); + if (klass->klass_part()->oop_is_array()) { + return ((arrayKlass*)klass->klass_part())->allocate_arrayArray(1, length, THREAD); + } else { + assert (klass->klass_part()->oop_is_instance(), "new object array with klass not an instanceKlass"); + return ((instanceKlass*)klass->klass_part())->allocate_objArray(1, length, THREAD); + } +} + +objArrayOop oopFactory::new_system_objArray(int length, TRAPS) { + int size = objArrayOopDesc::object_size(length); + KlassHandle klass (THREAD, Universe::systemObjArrayKlassObj()); + objArrayOop o = (objArrayOop) + Universe::heap()->permanent_array_allocate(klass, size, length, CHECK_NULL); + // initialization not needed, allocated cleared + return o; +} + + +constantPoolOop oopFactory::new_constantPool(int length, TRAPS) { + constantPoolKlass* ck = constantPoolKlass::cast(Universe::constantPoolKlassObj()); + return ck->allocate(length, CHECK_NULL); +} + + +constantPoolCacheOop oopFactory::new_constantPoolCache(int length, TRAPS) { + constantPoolCacheKlass* ck = constantPoolCacheKlass::cast(Universe::constantPoolCacheKlassObj()); + return ck->allocate(length, CHECK_NULL); +} + + +klassOop oopFactory::new_instanceKlass(int vtable_len, int itable_len, int static_field_size, + int nonstatic_oop_map_size, ReferenceType rt, TRAPS) { + instanceKlassKlass* ikk = instanceKlassKlass::cast(Universe::instanceKlassKlassObj()); + return ikk->allocate_instance_klass(vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, CHECK_NULL); +} + + +constMethodOop oopFactory::new_constMethod(int byte_code_size, + int compressed_line_number_size, + int localvariable_table_length, + int checked_exceptions_length, + TRAPS) { + klassOop cmkObj = Universe::constMethodKlassObj(); + constMethodKlass* cmk = constMethodKlass::cast(cmkObj); + return cmk->allocate(byte_code_size, compressed_line_number_size, + localvariable_table_length, checked_exceptions_length, + CHECK_NULL); +} + + +methodOop oopFactory::new_method(int byte_code_size, AccessFlags access_flags, + int compressed_line_number_size, + int localvariable_table_length, + int checked_exceptions_length, TRAPS) { + methodKlass* mk = methodKlass::cast(Universe::methodKlassObj()); + assert(!access_flags.is_native() || byte_code_size == 0, + "native methods should not contain byte codes"); + constMethodOop cm = new_constMethod(byte_code_size, + compressed_line_number_size, + localvariable_table_length, + checked_exceptions_length, CHECK_NULL); + constMethodHandle rw(THREAD, cm); + return mk->allocate(rw, access_flags, CHECK_NULL); +} + + +methodDataOop oopFactory::new_methodData(methodHandle method, TRAPS) { + methodDataKlass* mdk = methodDataKlass::cast(Universe::methodDataKlassObj()); + return mdk->allocate(method, CHECK_NULL); +} + + +compiledICHolderOop oopFactory::new_compiledICHolder(methodHandle method, KlassHandle klass, TRAPS) { + compiledICHolderKlass* ck = (compiledICHolderKlass*) Universe::compiledICHolderKlassObj()->klass_part(); + compiledICHolderOop c = ck->allocate(CHECK_NULL); + c->set_holder_method(method()); + c->set_holder_klass(klass()); + return c; +} diff --git a/hotspot/src/share/vm/memory/oopFactory.hpp b/hotspot/src/share/vm/memory/oopFactory.hpp new file mode 100644 index 00000000000..855e0fcf43e --- /dev/null +++ b/hotspot/src/share/vm/memory/oopFactory.hpp @@ -0,0 +1,111 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// oopFactory is a class used for creating new objects. + +class vframeArray; + +class oopFactory: AllStatic { + public: + // Basic type leaf array allocation + static typeArrayOop new_boolArray (int length, TRAPS) { return typeArrayKlass::cast(Universe::boolArrayKlassObj ())->allocate(length, CHECK_NULL); } + static typeArrayOop new_charArray (int length, TRAPS) { return typeArrayKlass::cast(Universe::charArrayKlassObj ())->allocate(length, CHECK_NULL); } + static typeArrayOop new_singleArray(int length, TRAPS) { return typeArrayKlass::cast(Universe::singleArrayKlassObj())->allocate(length, CHECK_NULL); } + static typeArrayOop new_doubleArray(int length, TRAPS) { return typeArrayKlass::cast(Universe::doubleArrayKlassObj())->allocate(length, CHECK_NULL); } + static typeArrayOop new_byteArray (int length, TRAPS) { return typeArrayKlass::cast(Universe::byteArrayKlassObj ())->allocate(length, CHECK_NULL); } + static typeArrayOop new_shortArray (int length, TRAPS) { return typeArrayKlass::cast(Universe::shortArrayKlassObj ())->allocate(length, CHECK_NULL); } + static typeArrayOop new_intArray (int length, TRAPS) { return typeArrayKlass::cast(Universe::intArrayKlassObj ())->allocate(length, CHECK_NULL); } + static typeArrayOop new_longArray (int length, TRAPS) { return typeArrayKlass::cast(Universe::longArrayKlassObj ())->allocate(length, CHECK_NULL); } + + // create java.lang.Object[] + static objArrayOop new_objectArray(int length, TRAPS) { + return objArrayKlass:: + cast(Universe::objectArrayKlassObj())->allocate(length, CHECK_NULL); + } + + static typeArrayOop new_charArray (const char* utf8_str, TRAPS); + static typeArrayOop new_permanent_charArray (int length, TRAPS); + static typeArrayOop new_permanent_byteArray (int length, TRAPS); // used for class file structures + static typeArrayOop new_permanent_shortArray(int length, TRAPS); // used for class file structures + static typeArrayOop new_permanent_intArray (int length, TRAPS); // used for class file structures + + static typeArrayOop new_typeArray(BasicType type, int length, TRAPS); + + // Symbols + static symbolOop new_symbol(const char* utf8_buffer, int length, TRAPS) { + assert(utf8_buffer != NULL, "just checking"); + return SymbolTable::lookup(utf8_buffer, length, CHECK_NULL); + } + static void new_symbols(constantPoolHandle cp, int names_count, + const char** name, int* lengths, + int* cp_indices, unsigned int* hashValues, + TRAPS) { + SymbolTable::add(cp, names_count, name, lengths, cp_indices, + hashValues, CHECK); + } + + static symbolOop new_symbol(char* name, TRAPS) { return new_symbol(name, (int)strlen(name), CHECK_NULL); } + static symbolOop new_symbol(const char* name, TRAPS) { return new_symbol(name, (int)strlen(name), CHECK_NULL); } + static symbolOop new_symbol(symbolHandle sym, int begin, int end, TRAPS) { + assert(begin <= end && end <= sym->utf8_length(), "just checking"); + return SymbolTable::lookup(sym, begin, end, CHECK_NULL); + } + + // Create symbols as above but return a handle + static symbolHandle new_symbol_handle(const char* name, int length, TRAPS) { + symbolOop sym = new_symbol(name, length, THREAD); + return symbolHandle(THREAD, sym); + } + static symbolHandle new_symbol_handle(char* name, TRAPS) { return new_symbol_handle(name, (int)strlen(name), CHECK_(symbolHandle())); } + static symbolHandle new_symbol_handle(const char* name, TRAPS) { return new_symbol_handle(name, (int)strlen(name), CHECK_(symbolHandle())); } + + // Constant pools + static constantPoolOop new_constantPool (int length, TRAPS); + static constantPoolCacheOop new_constantPoolCache(int length, TRAPS); + + // Instance classes + static klassOop new_instanceKlass(int vtable_len, int itable_len, int static_field_size, + int nonstatic_oop_map_size, ReferenceType rt, TRAPS); + + // Methods +private: + static constMethodOop new_constMethod(int byte_code_size, + int compressed_line_number_size, + int localvariable_table_length, + int checked_exceptions_length, TRAPS); +public: + static methodOop new_method(int byte_code_size, AccessFlags access_flags, int compressed_line_number_size, int localvariable_table_length, int checked_exceptions_length, TRAPS); + + // Method Data containers + static methodDataOop new_methodData(methodHandle method, TRAPS); + + // System object arrays + static objArrayOop new_system_objArray(int length, TRAPS); + + // Regular object arrays + static objArrayOop new_objArray(klassOop klass, int length, TRAPS); + + // For compiled ICs + static compiledICHolderOop new_compiledICHolder(methodHandle method, KlassHandle klass, TRAPS); +}; diff --git a/hotspot/src/share/vm/memory/permGen.cpp b/hotspot/src/share/vm/memory/permGen.cpp new file mode 100644 index 00000000000..f611cc36e1a --- /dev/null +++ b/hotspot/src/share/vm/memory/permGen.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_permGen.cpp.incl" + +CompactingPermGen::CompactingPermGen(ReservedSpace rs, + ReservedSpace shared_rs, + size_t initial_byte_size, + GenRemSet* remset, + PermanentGenerationSpec* perm_spec) +{ + CompactingPermGenGen* g = + new CompactingPermGenGen(rs, shared_rs, initial_byte_size, -1, remset, + NULL, perm_spec); + if (g == NULL) + vm_exit_during_initialization("Could not allocate a CompactingPermGen"); + _gen = g; + + g->initialize_performance_counters(); + + _capacity_expansion_limit = g->capacity() + MaxPermHeapExpansion; +} + +HeapWord* CompactingPermGen::mem_allocate(size_t size) { + MutexLocker ml(Heap_lock); + HeapWord* obj = _gen->allocate(size, false); + bool tried_collection = false; + bool tried_expansion = false; + while (obj == NULL) { + if (_gen->capacity() >= _capacity_expansion_limit || tried_expansion) { + // Expansion limit reached, try collection before expanding further + // For now we force a full collection, this could be changed + SharedHeap::heap()->collect_locked(GCCause::_permanent_generation_full); + obj = _gen->allocate(size, false); + tried_collection = true; + tried_expansion = false; // ... following the collection: + // the collection may have shrunk the space. + } + if (obj == NULL && !tried_expansion) { + obj = _gen->expand_and_allocate(size, false); + tried_expansion = true; + } + if (obj == NULL && tried_collection && tried_expansion) { + // We have not been able to allocate despite a collection and + // an attempted space expansion. We now make a last-ditch collection + // attempt that will try to reclaim as much space as possible (for + // example by aggressively clearing all soft refs). + SharedHeap::heap()->collect_locked(GCCause::_last_ditch_collection); + obj = _gen->allocate(size, false); + if (obj == NULL) { + // An expansion attempt is necessary since the previous + // collection may have shrunk the space. + obj = _gen->expand_and_allocate(size, false); + } + break; + } + } + return obj; +} + +void CompactingPermGen::compute_new_size() { + size_t desired_capacity = align_size_up(_gen->used(), MinPermHeapExpansion); + if (desired_capacity < PermSize) { + desired_capacity = PermSize; + } + if (_gen->capacity() > desired_capacity) { + _gen->shrink(_gen->capacity() - desired_capacity); + } + _capacity_expansion_limit = _gen->capacity() + MaxPermHeapExpansion; +} diff --git a/hotspot/src/share/vm/memory/permGen.hpp b/hotspot/src/share/vm/memory/permGen.hpp new file mode 100644 index 00000000000..47f16b83b0f --- /dev/null +++ b/hotspot/src/share/vm/memory/permGen.hpp @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// All heaps contains a "permanent generation," containing permanent +// (reflective) objects. This is like a regular generation in some ways, +// but unlike one in others, and so is split apart. + +class Generation; +class GenRemSet; +class CSpaceCounters; + +// PermGen models the part of the heap + +class PermGen : public CHeapObj { + friend class VMStructs; + protected: + size_t _capacity_expansion_limit; // maximum expansion allowed without a + // full gc occuring + + public: + enum Name { + MarkSweepCompact, MarkSweep, ConcurrentMarkSweep + }; + + // Permanent allocation (initialized) + virtual HeapWord* mem_allocate(size_t size) = 0; + + // Mark sweep support + virtual void compute_new_size() = 0; + + // Ideally, we would use MI (IMHO) but we'll do delegation instead. + virtual Generation* as_gen() const = 0; + + virtual void oop_iterate(OopClosure* cl) { + Generation* g = as_gen(); + assert(g != NULL, "as_gen() NULL"); + g->oop_iterate(cl); + } + + virtual void object_iterate(ObjectClosure* cl) { + Generation* g = as_gen(); + assert(g != NULL, "as_gen() NULL"); + g->object_iterate(cl); + } + + // Performance Counter support + virtual void update_counters() { + Generation* g = as_gen(); + assert(g != NULL, "as_gen() NULL"); + g->update_counters(); + } +}; diff --git a/hotspot/src/share/vm/memory/referencePolicy.cpp b/hotspot/src/share/vm/memory/referencePolicy.cpp new file mode 100644 index 00000000000..08fa2a15767 --- /dev/null +++ b/hotspot/src/share/vm/memory/referencePolicy.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_referencePolicy.cpp.incl" + +LRUCurrentHeapPolicy::LRUCurrentHeapPolicy() { + _max_interval = (Universe::get_heap_free_at_last_gc() / M) * SoftRefLRUPolicyMSPerMB; + assert(_max_interval >= 0,"Sanity check"); +} + +// The oop passed in is the SoftReference object, and not +// the object the SoftReference points to. +bool LRUCurrentHeapPolicy::should_clear_reference(oop p) { + jlong interval = java_lang_ref_SoftReference::clock() - java_lang_ref_SoftReference::timestamp(p); + assert(interval >= 0, "Sanity check"); + + // The interval will be zero if the ref was accessed since the last scavenge/gc. + if(interval <= _max_interval) { + return false; + } + + return true; +} + +/////////////////////// MaxHeap ////////////////////// + +LRUMaxHeapPolicy::LRUMaxHeapPolicy() { + size_t max_heap = MaxHeapSize; + max_heap -= Universe::get_heap_used_at_last_gc(); + max_heap /= M; + + _max_interval = max_heap * SoftRefLRUPolicyMSPerMB; + assert(_max_interval >= 0,"Sanity check"); +} + +// The oop passed in is the SoftReference object, and not +// the object the SoftReference points to. +bool LRUMaxHeapPolicy::should_clear_reference(oop p) { + jlong interval = java_lang_ref_SoftReference::clock() - java_lang_ref_SoftReference::timestamp(p); + assert(interval >= 0, "Sanity check"); + + // The interval will be zero if the ref was accessed since the last scavenge/gc. + if(interval <= _max_interval) { + return false; + } + + return true; +} diff --git a/hotspot/src/share/vm/memory/referencePolicy.hpp b/hotspot/src/share/vm/memory/referencePolicy.hpp new file mode 100644 index 00000000000..5645d060d5f --- /dev/null +++ b/hotspot/src/share/vm/memory/referencePolicy.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// referencePolicy is used to determine when soft reference objects +// should be cleared. + + +class ReferencePolicy : public ResourceObj { + public: + virtual bool should_clear_reference(oop p) { ShouldNotReachHere(); return true; } +}; + +class NeverClearPolicy : public ReferencePolicy { + public: + bool should_clear_reference(oop p) { return false; } +}; + +class AlwaysClearPolicy : public ReferencePolicy { + public: + bool should_clear_reference(oop p) { return true; } +}; + +class LRUCurrentHeapPolicy : public ReferencePolicy { + private: + jlong _max_interval; + + public: + LRUCurrentHeapPolicy(); + + bool should_clear_reference(oop p); +}; + +class LRUMaxHeapPolicy : public ReferencePolicy { + private: + jlong _max_interval; + + public: + LRUMaxHeapPolicy(); + + bool should_clear_reference(oop p); +}; diff --git a/hotspot/src/share/vm/memory/referenceProcessor.cpp b/hotspot/src/share/vm/memory/referenceProcessor.cpp new file mode 100644 index 00000000000..24793bb4152 --- /dev/null +++ b/hotspot/src/share/vm/memory/referenceProcessor.cpp @@ -0,0 +1,1261 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_referenceProcessor.cpp.incl" + +// List of discovered references. +class DiscoveredList { +public: + DiscoveredList() : _head(NULL), _len(0) { } + oop head() const { return _head; } + oop* head_ptr() { return &_head; } + void set_head(oop o) { _head = o; } + bool empty() const { return _head == ReferenceProcessor::_sentinelRef; } + size_t length() { return _len; } + void set_length(size_t len) { _len = len; } +private: + size_t _len; + oop _head; +}; + +oop ReferenceProcessor::_sentinelRef = NULL; + +const int subclasses_of_ref = REF_PHANTOM - REF_OTHER; + +void referenceProcessor_init() { + ReferenceProcessor::init_statics(); +} + +void ReferenceProcessor::init_statics() { + assert(_sentinelRef == NULL, "should be initialized precsiely once"); + EXCEPTION_MARK; + _sentinelRef = instanceKlass::cast( + SystemDictionary::object_klass())-> + allocate_permanent_instance(THREAD); + + // Initialize the master soft ref clock. + java_lang_ref_SoftReference::set_clock(os::javaTimeMillis()); + + if (HAS_PENDING_EXCEPTION) { + Handle ex(THREAD, PENDING_EXCEPTION); + vm_exit_during_initialization(ex); + } + assert(_sentinelRef != NULL && _sentinelRef->is_oop(), + "Just constructed it!"); + guarantee(RefDiscoveryPolicy == ReferenceBasedDiscovery || + RefDiscoveryPolicy == ReferentBasedDiscovery, + "Unrecongnized RefDiscoveryPolicy"); +} + + +ReferenceProcessor* ReferenceProcessor::create_ref_processor( + MemRegion span, + bool atomic_discovery, + bool mt_discovery, + BoolObjectClosure* is_alive_non_header, + int parallel_gc_threads, + bool mt_processing) +{ + int mt_degree = 1; + if (parallel_gc_threads > 1) { + mt_degree = parallel_gc_threads; + } + ReferenceProcessor* rp = + new ReferenceProcessor(span, atomic_discovery, + mt_discovery, mt_degree, + mt_processing); + if (rp == NULL) { + vm_exit_during_initialization("Could not allocate ReferenceProcessor object"); + } + rp->set_is_alive_non_header(is_alive_non_header); + return rp; +} + + +ReferenceProcessor::ReferenceProcessor(MemRegion span, + bool atomic_discovery, bool mt_discovery, int mt_degree, + bool mt_processing) : + _discovering_refs(false), + _enqueuing_is_done(false), + _is_alive_non_header(NULL), + _processing_is_mt(mt_processing), + _next_id(0) +{ + _span = span; + _discovery_is_atomic = atomic_discovery; + _discovery_is_mt = mt_discovery; + _num_q = mt_degree; + _discoveredSoftRefs = NEW_C_HEAP_ARRAY(DiscoveredList, _num_q * subclasses_of_ref); + if (_discoveredSoftRefs == NULL) { + vm_exit_during_initialization("Could not allocated RefProc Array"); + } + _discoveredWeakRefs = &_discoveredSoftRefs[_num_q]; + _discoveredFinalRefs = &_discoveredWeakRefs[_num_q]; + _discoveredPhantomRefs = &_discoveredFinalRefs[_num_q]; + assert(_sentinelRef != NULL, "_sentinelRef is NULL"); + // Initialized all entries to _sentinelRef + for (int i = 0; i < _num_q * subclasses_of_ref; i++) { + _discoveredSoftRefs[i].set_head(_sentinelRef); + _discoveredSoftRefs[i].set_length(0); + } +} + +#ifndef PRODUCT +void ReferenceProcessor::verify_no_references_recorded() { + guarantee(!_discovering_refs, "Discovering refs?"); + for (int i = 0; i < _num_q * subclasses_of_ref; i++) { + guarantee(_discoveredSoftRefs[i].empty(), + "Found non-empty discovered list"); + } +} +#endif + +void ReferenceProcessor::weak_oops_do(OopClosure* f) { + for (int i = 0; i < _num_q * subclasses_of_ref; i++) { + f->do_oop(_discoveredSoftRefs[i].head_ptr()); + } +} + +void ReferenceProcessor::oops_do(OopClosure* f) { + f->do_oop(&_sentinelRef); +} + +void ReferenceProcessor::update_soft_ref_master_clock() +{ + // Update (advance) the soft ref master clock field. This must be done + // after processing the soft ref list. + jlong now = os::javaTimeMillis(); + jlong clock = java_lang_ref_SoftReference::clock(); + NOT_PRODUCT( + if (now < clock) { + warning("time warp: %d to %d", clock, now); + } + ) + // In product mode, protect ourselves from system time being adjusted + // externally and going backward; see note in the implementation of + // GenCollectedHeap::time_since_last_gc() for the right way to fix + // this uniformly throughout the VM; see bug-id 4741166. XXX + if (now > clock) { + java_lang_ref_SoftReference::set_clock(now); + } + // Else leave clock stalled at its old value until time progresses + // past clock value. +} + + +void +ReferenceProcessor::process_discovered_references( + ReferencePolicy* policy, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + AbstractRefProcTaskExecutor* task_executor) { + NOT_PRODUCT(verify_ok_to_handle_reflists()); + + assert(!enqueuing_is_done(), "If here enqueuing should not be complete"); + // Stop treating discovered references specially. + disable_discovery(); + + bool trace_time = PrintGCDetails && PrintReferenceGC; + // Soft references + { + TraceTime tt("SoftReference", trace_time, false, gclog_or_tty); + process_discovered_reflist(_discoveredSoftRefs, policy, true, + is_alive, keep_alive, complete_gc, task_executor); + } + + update_soft_ref_master_clock(); + + // Weak references + { + TraceTime tt("WeakReference", trace_time, false, gclog_or_tty); + process_discovered_reflist(_discoveredWeakRefs, NULL, true, + is_alive, keep_alive, complete_gc, task_executor); + } + + // Final references + { + TraceTime tt("FinalReference", trace_time, false, gclog_or_tty); + process_discovered_reflist(_discoveredFinalRefs, NULL, false, + is_alive, keep_alive, complete_gc, task_executor); + } + + // Phantom references + { + TraceTime tt("PhantomReference", trace_time, false, gclog_or_tty); + process_discovered_reflist(_discoveredPhantomRefs, NULL, false, + is_alive, keep_alive, complete_gc, task_executor); + } + + // Weak global JNI references. It would make more sense (semantically) to + // traverse these simultaneously with the regular weak references above, but + // that is not how the JDK1.2 specification is. See #4126360. Native code can + // thus use JNI weak references to circumvent the phantom references and + // resurrect a "post-mortem" object. + { + TraceTime tt("JNI Weak Reference", trace_time, false, gclog_or_tty); + if (task_executor != NULL) { + task_executor->set_single_threaded_mode(); + } + process_phaseJNI(is_alive, keep_alive, complete_gc); + } +} + + +#ifndef PRODUCT +// Calculate the number of jni handles. +unsigned int ReferenceProcessor::count_jni_refs() +{ + class AlwaysAliveClosure: public BoolObjectClosure { + public: + bool do_object_b(oop obj) { return true; } + void do_object(oop obj) { assert(false, "Don't call"); } + }; + + class CountHandleClosure: public OopClosure { + private: + int _count; + public: + CountHandleClosure(): _count(0) {} + void do_oop(oop* unused) { + _count++; + } + int count() { return _count; } + }; + CountHandleClosure global_handle_count; + AlwaysAliveClosure always_alive; + JNIHandles::weak_oops_do(&always_alive, &global_handle_count); + return global_handle_count.count(); +} +#endif + +void ReferenceProcessor::process_phaseJNI(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc) { +#ifndef PRODUCT + if (PrintGCDetails && PrintReferenceGC) { + unsigned int count = count_jni_refs(); + gclog_or_tty->print(", %u refs", count); + } +#endif + JNIHandles::weak_oops_do(is_alive, keep_alive); + // Finally remember to keep sentinel around + keep_alive->do_oop(&_sentinelRef); + complete_gc->do_void(); +} + +bool ReferenceProcessor::enqueue_discovered_references(AbstractRefProcTaskExecutor* task_executor) { + NOT_PRODUCT(verify_ok_to_handle_reflists()); + // Remember old value of pending references list + oop* pending_list_addr = java_lang_ref_Reference::pending_list_addr(); + oop old_pending_list_value = *pending_list_addr; + + // Enqueue references that are not made active again, and + // clear the decks for the next collection (cycle). + enqueue_discovered_reflists(pending_list_addr, task_executor); + // Do the oop-check on pending_list_addr missed in + // enqueue_discovered_reflist. We should probably + // do a raw oop_check so that future such idempotent + // oop_stores relying on the oop-check side-effect + // may be elided automatically and safely without + // affecting correctness. + oop_store(pending_list_addr, *(pending_list_addr)); + + // Stop treating discovered references specially. + disable_discovery(); + + // Return true if new pending references were added + return old_pending_list_value != *pending_list_addr; +} + +void ReferenceProcessor::enqueue_discovered_reflist(DiscoveredList& refs_list, + oop* pending_list_addr) { + // Given a list of refs linked through the "discovered" field + // (java.lang.ref.Reference.discovered) chain them through the + // "next" field (java.lang.ref.Reference.next) and prepend + // to the pending list. + if (TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr("ReferenceProcessor::enqueue_discovered_reflist list " + INTPTR_FORMAT, (address)refs_list.head()); + } + oop obj = refs_list.head(); + // Walk down the list, copying the discovered field into + // the next field and clearing it (except for the last + // non-sentinel object which is treated specially to avoid + // confusion with an active reference). + while (obj != _sentinelRef) { + assert(obj->is_instanceRef(), "should be reference object"); + oop next = java_lang_ref_Reference::discovered(obj); + if (TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr(" obj " INTPTR_FORMAT "/next " INTPTR_FORMAT, + (oopDesc*) obj, (oopDesc*) next); + } + assert(*java_lang_ref_Reference::next_addr(obj) == NULL, + "The reference should not be enqueued"); + if (next == _sentinelRef) { // obj is last + // Swap refs_list into pendling_list_addr and + // set obj's next to what we read from pending_list_addr. + oop old = (oop)Atomic::xchg_ptr(refs_list.head(), pending_list_addr); + // Need oop_check on pending_list_addr above; + // see special oop-check code at the end of + // enqueue_discovered_reflists() further below. + if (old == NULL) { + // obj should be made to point to itself, since + // pending list was empty. + java_lang_ref_Reference::set_next(obj, obj); + } else { + java_lang_ref_Reference::set_next(obj, old); + } + } else { + java_lang_ref_Reference::set_next(obj, next); + } + java_lang_ref_Reference::set_discovered(obj, (oop) NULL); + obj = next; + } +} + +// Parallel enqueue task +class RefProcEnqueueTask: public AbstractRefProcTaskExecutor::EnqueueTask { +public: + RefProcEnqueueTask(ReferenceProcessor& ref_processor, + DiscoveredList discovered_refs[], + oop* pending_list_addr, + oop sentinel_ref, + int n_queues) + : EnqueueTask(ref_processor, discovered_refs, + pending_list_addr, sentinel_ref, n_queues) + { } + + virtual void work(unsigned int work_id) + { + assert(work_id < (unsigned int)_ref_processor.num_q(), "Index out-of-bounds"); + // Simplest first cut: static partitioning. + int index = work_id; + for (int j = 0; j < subclasses_of_ref; j++, index += _n_queues) { + _ref_processor.enqueue_discovered_reflist( + _refs_lists[index], _pending_list_addr); + _refs_lists[index].set_head(_sentinel_ref); + _refs_lists[index].set_length(0); + } + } +}; + +// Enqueue references that are not made active again +void ReferenceProcessor::enqueue_discovered_reflists(oop* pending_list_addr, + AbstractRefProcTaskExecutor* task_executor) { + if (_processing_is_mt && task_executor != NULL) { + // Parallel code + RefProcEnqueueTask tsk(*this, _discoveredSoftRefs, + pending_list_addr, _sentinelRef, _num_q); + task_executor->execute(tsk); + } else { + // Serial code: call the parent class's implementation + for (int i = 0; i < _num_q * subclasses_of_ref; i++) { + enqueue_discovered_reflist(_discoveredSoftRefs[i], pending_list_addr); + _discoveredSoftRefs[i].set_head(_sentinelRef); + _discoveredSoftRefs[i].set_length(0); + } + } +} + +// Iterator for the list of discovered references. +class DiscoveredListIterator { +public: + inline DiscoveredListIterator(DiscoveredList& refs_list, + OopClosure* keep_alive, + BoolObjectClosure* is_alive); + + // End Of List. + inline bool has_next() const + { return _next != ReferenceProcessor::_sentinelRef; } + + // Get oop to the Reference object. + inline oop obj() const { return _ref; } + + // Get oop to the referent object. + inline oop referent() const { return _referent; } + + // Returns true if referent is alive. + inline bool is_referent_alive() const; + + // Loads data for the current reference. + // The "allow_null_referent" argument tells us to allow for the possibility + // of a NULL referent in the discovered Reference object. This typically + // happens in the case of concurrent collectors that may have done the + // discovery concurrently or interleaved with mutator execution. + inline void load_ptrs(DEBUG_ONLY(bool allow_null_referent)); + + // Move to the next discovered reference. + inline void next(); + + // Remove the current reference from the list and move to the next. + inline void remove(); + + // Make the Reference object active again. + inline void make_active() { java_lang_ref_Reference::set_next(_ref, NULL); } + + // Make the referent alive. + inline void make_referent_alive() { _keep_alive->do_oop(_referent_addr); } + + // Update the discovered field. + inline void update_discovered() { _keep_alive->do_oop(_prev_next); } + + // NULL out referent pointer. + inline void clear_referent() { *_referent_addr = NULL; } + + // Statistics + NOT_PRODUCT( + inline size_t processed() const { return _processed; } + inline size_t removed() const { return _removed; } + ) + +private: + inline void move_to_next(); + +private: + DiscoveredList& _refs_list; + oop* _prev_next; + oop _ref; + oop* _discovered_addr; + oop _next; + oop* _referent_addr; + oop _referent; + OopClosure* _keep_alive; + BoolObjectClosure* _is_alive; + DEBUG_ONLY( + oop _first_seen; // cyclic linked list check + ) + NOT_PRODUCT( + size_t _processed; + size_t _removed; + ) +}; + +inline DiscoveredListIterator::DiscoveredListIterator(DiscoveredList& refs_list, + OopClosure* keep_alive, + BoolObjectClosure* is_alive) + : _refs_list(refs_list), + _prev_next(refs_list.head_ptr()), + _ref(refs_list.head()), +#ifdef ASSERT + _first_seen(refs_list.head()), +#endif +#ifndef PRODUCT + _processed(0), + _removed(0), +#endif + _next(refs_list.head()), + _keep_alive(keep_alive), + _is_alive(is_alive) +{ } + +inline bool DiscoveredListIterator::is_referent_alive() const +{ + return _is_alive->do_object_b(_referent); +} + +inline void DiscoveredListIterator::load_ptrs(DEBUG_ONLY(bool allow_null_referent)) +{ + _discovered_addr = java_lang_ref_Reference::discovered_addr(_ref); + assert(_discovered_addr && (*_discovered_addr)->is_oop_or_null(), + "discovered field is bad"); + _next = *_discovered_addr; + _referent_addr = java_lang_ref_Reference::referent_addr(_ref); + _referent = *_referent_addr; + assert(Universe::heap()->is_in_reserved_or_null(_referent), + "Wrong oop found in java.lang.Reference object"); + assert(allow_null_referent ? + _referent->is_oop_or_null() + : _referent->is_oop(), + "bad referent"); +} + +inline void DiscoveredListIterator::next() +{ + _prev_next = _discovered_addr; + move_to_next(); +} + +inline void DiscoveredListIterator::remove() +{ + assert(_ref->is_oop(), "Dropping a bad reference"); + // Clear the discovered_addr field so that the object does + // not look like it has been discovered. + *_discovered_addr = NULL; + // Remove Reference object from list. + *_prev_next = _next; + NOT_PRODUCT(_removed++); + move_to_next(); +} + +inline void DiscoveredListIterator::move_to_next() +{ + _ref = _next; + assert(_ref != _first_seen, "cyclic ref_list found"); + NOT_PRODUCT(_processed++); +} + + +// NOTE: process_phase*() are largely similar, and at a high level +// merely iterate over the extant list applying a predicate to +// each of its elements and possibly removing that element from the +// list and applying some further closures to that element. +// We should consider the possibility of replacing these +// process_phase*() methods by abstracting them into +// a single general iterator invocation that receives appropriate +// closures that accomplish this work. + +// (SoftReferences only) Traverse the list and remove any SoftReferences whose +// referents are not alive, but that should be kept alive for policy reasons. +// Keep alive the transitive closure of all such referents. +void +ReferenceProcessor::process_phase1(DiscoveredList& refs_list_addr, + ReferencePolicy* policy, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc) { + assert(policy != NULL, "Must have a non-NULL policy"); + DiscoveredListIterator iter(refs_list_addr, keep_alive, is_alive); + // Decide which softly reachable refs should be kept alive. + while (iter.has_next()) { + iter.load_ptrs(DEBUG_ONLY(!discovery_is_atomic() /* allow_null_referent */)); + bool referent_is_dead = (iter.referent() != NULL) && !iter.is_referent_alive(); + if (referent_is_dead && !policy->should_clear_reference(iter.obj())) { + if (TraceReferenceGC) { + gclog_or_tty->print_cr("Dropping reference (" INTPTR_FORMAT ": %s" ") by policy", + (address)iter.obj(), iter.obj()->blueprint()->internal_name()); + } + // Make the Reference object active again + iter.make_active(); + // keep the referent around + iter.make_referent_alive(); + // Remove Reference object from list + iter.remove(); + } else { + iter.next(); + } + } + // Close the reachable set + complete_gc->do_void(); + NOT_PRODUCT( + if (PrintGCDetails && TraceReferenceGC) { + gclog_or_tty->print(" Dropped %d dead Refs out of %d " + "discovered Refs by policy ", iter.removed(), iter.processed()); + } + ) +} + +// Traverse the list and remove any Refs that are not active, or +// whose referents are either alive or NULL. +void +ReferenceProcessor::pp2_work(DiscoveredList& refs_list_addr, + BoolObjectClosure* is_alive, + OopClosure* keep_alive) +{ + assert(discovery_is_atomic(), "Error"); + DiscoveredListIterator iter(refs_list_addr, keep_alive, is_alive); + while (iter.has_next()) { + iter.load_ptrs(DEBUG_ONLY(false /* allow_null_referent */)); + DEBUG_ONLY(oop* next_addr = java_lang_ref_Reference::next_addr(iter.obj());) + assert(*next_addr == NULL, "Should not discover inactive Reference"); + if (iter.is_referent_alive()) { + if (TraceReferenceGC) { + gclog_or_tty->print_cr("Dropping strongly reachable reference (" INTPTR_FORMAT ": %s)", + (address)iter.obj(), iter.obj()->blueprint()->internal_name()); + } + // The referent is reachable after all. + // Update the referent pointer as necessary: Note that this + // should not entail any recursive marking because the + // referent must already have been traversed. + iter.make_referent_alive(); + // Remove Reference object from list + iter.remove(); + } else { + iter.next(); + } + } + NOT_PRODUCT( + if (PrintGCDetails && TraceReferenceGC) { + gclog_or_tty->print(" Dropped %d active Refs out of %d " + "Refs in discovered list ", iter.removed(), iter.processed()); + } + ) +} + +void +ReferenceProcessor::pp2_work_concurrent_discovery( + DiscoveredList& refs_list_addr, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc) +{ + assert(!discovery_is_atomic(), "Error"); + DiscoveredListIterator iter(refs_list_addr, keep_alive, is_alive); + while (iter.has_next()) { + iter.load_ptrs(DEBUG_ONLY(true /* allow_null_referent */)); + oop* next_addr = java_lang_ref_Reference::next_addr(iter.obj()); + if ((iter.referent() == NULL || iter.is_referent_alive() || + *next_addr != NULL)) { + assert((*next_addr)->is_oop_or_null(), "bad next field"); + // Remove Reference object from list + iter.remove(); + // Trace the cohorts + iter.make_referent_alive(); + keep_alive->do_oop(next_addr); + } else { + iter.next(); + } + } + // Now close the newly reachable set + complete_gc->do_void(); + NOT_PRODUCT( + if (PrintGCDetails && TraceReferenceGC) { + gclog_or_tty->print(" Dropped %d active Refs out of %d " + "Refs in discovered list ", iter.removed(), iter.processed()); + } + ) +} + +// Traverse the list and process the referents, by either +// either clearing them or keeping them (and their reachable +// closure) alive. +void +ReferenceProcessor::process_phase3(DiscoveredList& refs_list_addr, + bool clear_referent, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc) { + DiscoveredListIterator iter(refs_list_addr, keep_alive, is_alive); + while (iter.has_next()) { + iter.update_discovered(); + iter.load_ptrs(DEBUG_ONLY(false /* allow_null_referent */)); + if (clear_referent) { + // NULL out referent pointer + iter.clear_referent(); + } else { + // keep the referent around + iter.make_referent_alive(); + } + if (TraceReferenceGC) { + gclog_or_tty->print_cr("Adding %sreference (" INTPTR_FORMAT ": %s) as pending", + clear_referent ? "cleared " : "", + (address)iter.obj(), iter.obj()->blueprint()->internal_name()); + } + assert(iter.obj()->is_oop(UseConcMarkSweepGC), "Adding a bad reference"); + // If discovery is concurrent, we may have objects with null referents, + // being those that were concurrently cleared after they were discovered + // (and not subsequently precleaned). + assert( (discovery_is_atomic() && iter.referent()->is_oop()) + || (!discovery_is_atomic() && iter.referent()->is_oop_or_null(UseConcMarkSweepGC)), + "Adding a bad referent"); + iter.next(); + } + // Remember to keep sentinel pointer around + iter.update_discovered(); + // Close the reachable set + complete_gc->do_void(); +} + +void +ReferenceProcessor::abandon_partial_discovered_list(DiscoveredList& ref_list) { + oop obj = ref_list.head(); + while (obj != _sentinelRef) { + oop* discovered_addr = java_lang_ref_Reference::discovered_addr(obj); + obj = *discovered_addr; + *discovered_addr = NULL; + } + ref_list.set_head(_sentinelRef); + ref_list.set_length(0); +} + +void +ReferenceProcessor::abandon_partial_discovered_list_arr(DiscoveredList refs_lists[]) { + for (int i = 0; i < _num_q; i++) { + abandon_partial_discovered_list(refs_lists[i]); + } +} + +class RefProcPhase1Task: public AbstractRefProcTaskExecutor::ProcessTask { +public: + RefProcPhase1Task(ReferenceProcessor& ref_processor, + DiscoveredList refs_lists[], + ReferencePolicy* policy, + bool marks_oops_alive) + : ProcessTask(ref_processor, refs_lists, marks_oops_alive), + _policy(policy) + { } + virtual void work(unsigned int i, BoolObjectClosure& is_alive, + OopClosure& keep_alive, + VoidClosure& complete_gc) + { + _ref_processor.process_phase1(_refs_lists[i], _policy, + &is_alive, &keep_alive, &complete_gc); + } +private: + ReferencePolicy* _policy; +}; + +class RefProcPhase2Task: public AbstractRefProcTaskExecutor::ProcessTask { +public: + RefProcPhase2Task(ReferenceProcessor& ref_processor, + DiscoveredList refs_lists[], + bool marks_oops_alive) + : ProcessTask(ref_processor, refs_lists, marks_oops_alive) + { } + virtual void work(unsigned int i, BoolObjectClosure& is_alive, + OopClosure& keep_alive, + VoidClosure& complete_gc) + { + _ref_processor.process_phase2(_refs_lists[i], + &is_alive, &keep_alive, &complete_gc); + } +}; + +class RefProcPhase3Task: public AbstractRefProcTaskExecutor::ProcessTask { +public: + RefProcPhase3Task(ReferenceProcessor& ref_processor, + DiscoveredList refs_lists[], + bool clear_referent, + bool marks_oops_alive) + : ProcessTask(ref_processor, refs_lists, marks_oops_alive), + _clear_referent(clear_referent) + { } + virtual void work(unsigned int i, BoolObjectClosure& is_alive, + OopClosure& keep_alive, + VoidClosure& complete_gc) + { + _ref_processor.process_phase3(_refs_lists[i], _clear_referent, + &is_alive, &keep_alive, &complete_gc); + } +private: + bool _clear_referent; +}; + +// Balances reference queues. +void ReferenceProcessor::balance_queues(DiscoveredList ref_lists[]) +{ + // calculate total length + size_t total_refs = 0; + for (int i = 0; i < _num_q; ++i) { + total_refs += ref_lists[i].length(); + } + size_t avg_refs = total_refs / _num_q + 1; + int to_idx = 0; + for (int from_idx = 0; from_idx < _num_q; from_idx++) { + while (ref_lists[from_idx].length() > avg_refs) { + assert(to_idx < _num_q, "Sanity Check!"); + if (ref_lists[to_idx].length() < avg_refs) { + // move superfluous refs + size_t refs_to_move = + MIN2(ref_lists[from_idx].length() - avg_refs, + avg_refs - ref_lists[to_idx].length()); + oop move_head = ref_lists[from_idx].head(); + oop move_tail = move_head; + oop new_head = move_head; + // find an element to split the list on + for (size_t j = 0; j < refs_to_move; ++j) { + move_tail = new_head; + new_head = *java_lang_ref_Reference::discovered_addr(new_head); + } + java_lang_ref_Reference::set_discovered(move_tail, ref_lists[to_idx].head()); + ref_lists[to_idx].set_head(move_head); + ref_lists[to_idx].set_length(ref_lists[to_idx].length() + refs_to_move); + ref_lists[from_idx].set_head(new_head); + ref_lists[from_idx].set_length(ref_lists[from_idx].length() - refs_to_move); + } else { + ++to_idx; + } + } + } +} + +void +ReferenceProcessor::process_discovered_reflist( + DiscoveredList refs_lists[], + ReferencePolicy* policy, + bool clear_referent, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + AbstractRefProcTaskExecutor* task_executor) +{ + bool mt = task_executor != NULL && _processing_is_mt; + if (mt && ParallelRefProcBalancingEnabled) { + balance_queues(refs_lists); + } + if (PrintReferenceGC && PrintGCDetails) { + size_t total = 0; + for (int i = 0; i < _num_q; ++i) { + total += refs_lists[i].length(); + } + gclog_or_tty->print(", %u refs", total); + } + + // Phase 1 (soft refs only): + // . Traverse the list and remove any SoftReferences whose + // referents are not alive, but that should be kept alive for + // policy reasons. Keep alive the transitive closure of all + // such referents. + if (policy != NULL) { + if (mt) { + RefProcPhase1Task phase1(*this, refs_lists, policy, true /*marks_oops_alive*/); + task_executor->execute(phase1); + } else { + for (int i = 0; i < _num_q; i++) { + process_phase1(refs_lists[i], policy, + is_alive, keep_alive, complete_gc); + } + } + } else { // policy == NULL + assert(refs_lists != _discoveredSoftRefs, + "Policy must be specified for soft references."); + } + + // Phase 2: + // . Traverse the list and remove any refs whose referents are alive. + if (mt) { + RefProcPhase2Task phase2(*this, refs_lists, !discovery_is_atomic() /*marks_oops_alive*/); + task_executor->execute(phase2); + } else { + for (int i = 0; i < _num_q; i++) { + process_phase2(refs_lists[i], is_alive, keep_alive, complete_gc); + } + } + + // Phase 3: + // . Traverse the list and process referents as appropriate. + if (mt) { + RefProcPhase3Task phase3(*this, refs_lists, clear_referent, true /*marks_oops_alive*/); + task_executor->execute(phase3); + } else { + for (int i = 0; i < _num_q; i++) { + process_phase3(refs_lists[i], clear_referent, + is_alive, keep_alive, complete_gc); + } + } +} + +void ReferenceProcessor::clean_up_discovered_references() { + // loop over the lists + for (int i = 0; i < _num_q * subclasses_of_ref; i++) { + if (TraceReferenceGC && PrintGCDetails && ((i % _num_q) == 0)) { + gclog_or_tty->print_cr( + "\nScrubbing %s discovered list of Null referents", + list_name(i)); + } + clean_up_discovered_reflist(_discoveredSoftRefs[i]); + } +} + +void ReferenceProcessor::clean_up_discovered_reflist(DiscoveredList& refs_list) { + assert(!discovery_is_atomic(), "Else why call this method?"); + DiscoveredListIterator iter(refs_list, NULL, NULL); + size_t length = refs_list.length(); + while (iter.has_next()) { + iter.load_ptrs(DEBUG_ONLY(true /* allow_null_referent */)); + oop* next_addr = java_lang_ref_Reference::next_addr(iter.obj()); + assert((*next_addr)->is_oop_or_null(), "bad next field"); + // If referent has been cleared or Reference is not active, + // drop it. + if (iter.referent() == NULL || *next_addr != NULL) { + debug_only( + if (PrintGCDetails && TraceReferenceGC) { + gclog_or_tty->print_cr("clean_up_discovered_list: Dropping Reference: " + INTPTR_FORMAT " with next field: " INTPTR_FORMAT + " and referent: " INTPTR_FORMAT, + (address)iter.obj(), (address)*next_addr, (address)iter.referent()); + } + ) + // Remove Reference object from list + iter.remove(); + --length; + } else { + iter.next(); + } + } + refs_list.set_length(length); + NOT_PRODUCT( + if (PrintGCDetails && TraceReferenceGC) { + gclog_or_tty->print( + " Removed %d Refs with NULL referents out of %d discovered Refs", + iter.removed(), iter.processed()); + } + ) +} + +inline DiscoveredList* ReferenceProcessor::get_discovered_list(ReferenceType rt) { + int id = 0; + // Determine the queue index to use for this object. + if (_discovery_is_mt) { + // During a multi-threaded discovery phase, + // each thread saves to its "own" list. + Thread* thr = Thread::current(); + assert(thr->is_GC_task_thread(), + "Dubious cast from Thread* to WorkerThread*?"); + id = ((WorkerThread*)thr)->id(); + } else { + // single-threaded discovery, we save in round-robin + // fashion to each of the lists. + if (_processing_is_mt) { + id = next_id(); + } + } + assert(0 <= id && id < _num_q, "Id is out-of-bounds (call Freud?)"); + + // Get the discovered queue to which we will add + DiscoveredList* list = NULL; + switch (rt) { + case REF_OTHER: + // Unknown reference type, no special treatment + break; + case REF_SOFT: + list = &_discoveredSoftRefs[id]; + break; + case REF_WEAK: + list = &_discoveredWeakRefs[id]; + break; + case REF_FINAL: + list = &_discoveredFinalRefs[id]; + break; + case REF_PHANTOM: + list = &_discoveredPhantomRefs[id]; + break; + case REF_NONE: + // we should not reach here if we are an instanceRefKlass + default: + ShouldNotReachHere(); + } + return list; +} + +inline void ReferenceProcessor::add_to_discovered_list_mt(DiscoveredList& list, + oop obj, oop* discovered_addr) { + assert(_discovery_is_mt, "!_discovery_is_mt should have been handled by caller"); + // First we must make sure this object is only enqueued once. CAS in a non null + // discovered_addr. + oop retest = (oop)Atomic::cmpxchg_ptr(list.head(), discovered_addr, NULL); + if (retest == NULL) { + // This thread just won the right to enqueue the object. + // We have separate lists for enqueueing so no synchronization + // is necessary. + list.set_head(obj); + list.set_length(list.length() + 1); + } else { + // If retest was non NULL, another thread beat us to it: + // The reference has already been discovered... + if (TraceReferenceGC) { + gclog_or_tty->print_cr("Already enqueued reference (" INTPTR_FORMAT ": %s)", + obj, obj->blueprint()->internal_name()); + } + } +} + + +// We mention two of several possible choices here: +// #0: if the reference object is not in the "originating generation" +// (or part of the heap being collected, indicated by our "span" +// we don't treat it specially (i.e. we scan it as we would +// a normal oop, treating its references as strong references). +// This means that references can't be enqueued unless their +// referent is also in the same span. This is the simplest, +// most "local" and most conservative approach, albeit one +// that may cause weak references to be enqueued least promptly. +// We call this choice the "ReferenceBasedDiscovery" policy. +// #1: the reference object may be in any generation (span), but if +// the referent is in the generation (span) being currently collected +// then we can discover the reference object, provided +// the object has not already been discovered by +// a different concurrently running collector (as may be the +// case, for instance, if the reference object is in CMS and +// the referent in DefNewGeneration), and provided the processing +// of this reference object by the current collector will +// appear atomic to every other collector in the system. +// (Thus, for instance, a concurrent collector may not +// discover references in other generations even if the +// referent is in its own generation). This policy may, +// in certain cases, enqueue references somewhat sooner than +// might Policy #0 above, but at marginally increased cost +// and complexity in processing these references. +// We call this choice the "RefeferentBasedDiscovery" policy. +bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) { + // We enqueue references only if we are discovering refs + // (rather than processing discovered refs). + if (!_discovering_refs || !RegisterReferences) { + return false; + } + // We only enqueue active references. + oop* next_addr = java_lang_ref_Reference::next_addr(obj); + if (*next_addr != NULL) { + return false; + } + + HeapWord* obj_addr = (HeapWord*)obj; + if (RefDiscoveryPolicy == ReferenceBasedDiscovery && + !_span.contains(obj_addr)) { + // Reference is not in the originating generation; + // don't treat it specially (i.e. we want to scan it as a normal + // object with strong references). + return false; + } + + // We only enqueue references whose referents are not (yet) strongly + // reachable. + if (is_alive_non_header() != NULL) { + oop referent = java_lang_ref_Reference::referent(obj); + // We'd like to assert the following: + // assert(referent != NULL, "Refs with null referents already filtered"); + // However, since this code may be executed concurrently with + // mutators, which can clear() the referent, it is not + // guaranteed that the referent is non-NULL. + if (is_alive_non_header()->do_object_b(referent)) { + return false; // referent is reachable + } + } + + oop* discovered_addr = java_lang_ref_Reference::discovered_addr(obj); + assert(discovered_addr != NULL && (*discovered_addr)->is_oop_or_null(), + "bad discovered field"); + if (*discovered_addr != NULL) { + // The reference has already been discovered... + if (TraceReferenceGC) { + gclog_or_tty->print_cr("Already enqueued reference (" INTPTR_FORMAT ": %s)", + (oopDesc*)obj, obj->blueprint()->internal_name()); + } + if (RefDiscoveryPolicy == ReferentBasedDiscovery) { + // assumes that an object is not processed twice; + // if it's been already discovered it must be on another + // generation's discovered list; so we won't discover it. + return false; + } else { + assert(RefDiscoveryPolicy == ReferenceBasedDiscovery, + "Unrecognized policy"); + // Check assumption that an object is not potentially + // discovered twice except by concurrent collectors that potentially + // trace the same Reference object twice. + assert(UseConcMarkSweepGC, + "Only possible with a concurrent collector"); + return true; + } + } + + if (RefDiscoveryPolicy == ReferentBasedDiscovery) { + oop referent = java_lang_ref_Reference::referent(obj); + assert(referent->is_oop(), "bad referent"); + // enqueue if and only if either: + // reference is in our span or + // we are an atomic collector and referent is in our span + if (_span.contains(obj_addr) || + (discovery_is_atomic() && _span.contains(referent))) { + // should_enqueue = true; + } else { + return false; + } + } else { + assert(RefDiscoveryPolicy == ReferenceBasedDiscovery && + _span.contains(obj_addr), "code inconsistency"); + } + + // Get the right type of discovered queue head. + DiscoveredList* list = get_discovered_list(rt); + if (list == NULL) { + return false; // nothing special needs to be done + } + + // We do a raw store here, the field will be visited later when + // processing the discovered references. + if (_discovery_is_mt) { + add_to_discovered_list_mt(*list, obj, discovered_addr); + } else { + *discovered_addr = list->head(); + list->set_head(obj); + list->set_length(list->length() + 1); + } + + // In the MT discovery case, it is currently possible to see + // the following message multiple times if several threads + // discover a reference about the same time. Only one will + // however have actually added it to the disocvered queue. + // One could let add_to_discovered_list_mt() return an + // indication for success in queueing (by 1 thread) or + // failure (by all other threads), but I decided the extra + // code was not worth the effort for something that is + // only used for debugging support. + if (TraceReferenceGC) { + oop referent = java_lang_ref_Reference::referent(obj); + if (PrintGCDetails) { + gclog_or_tty->print_cr("Enqueued reference (" INTPTR_FORMAT ": %s)", + (oopDesc*) obj, obj->blueprint()->internal_name()); + } + assert(referent->is_oop(), "Enqueued a bad referent"); + } + assert(obj->is_oop(), "Enqueued a bad reference"); + return true; +} + +// Preclean the discovered references by removing those +// whose referents are alive, and by marking from those that +// are not active. These lists can be handled here +// in any order and, indeed, concurrently. +void ReferenceProcessor::preclean_discovered_references( + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + YieldClosure* yield) { + + NOT_PRODUCT(verify_ok_to_handle_reflists()); + + // Soft references + { + TraceTime tt("Preclean SoftReferences", PrintGCDetails && PrintReferenceGC, + false, gclog_or_tty); + for (int i = 0; i < _num_q; i++) { + preclean_discovered_reflist(_discoveredSoftRefs[i], is_alive, + keep_alive, complete_gc, yield); + } + } + if (yield->should_return()) { + return; + } + + // Weak references + { + TraceTime tt("Preclean WeakReferences", PrintGCDetails && PrintReferenceGC, + false, gclog_or_tty); + for (int i = 0; i < _num_q; i++) { + preclean_discovered_reflist(_discoveredWeakRefs[i], is_alive, + keep_alive, complete_gc, yield); + } + } + if (yield->should_return()) { + return; + } + + // Final references + { + TraceTime tt("Preclean FinalReferences", PrintGCDetails && PrintReferenceGC, + false, gclog_or_tty); + for (int i = 0; i < _num_q; i++) { + preclean_discovered_reflist(_discoveredFinalRefs[i], is_alive, + keep_alive, complete_gc, yield); + } + } + if (yield->should_return()) { + return; + } + + // Phantom references + { + TraceTime tt("Preclean PhantomReferences", PrintGCDetails && PrintReferenceGC, + false, gclog_or_tty); + for (int i = 0; i < _num_q; i++) { + preclean_discovered_reflist(_discoveredPhantomRefs[i], is_alive, + keep_alive, complete_gc, yield); + } + } +} + +// Walk the given discovered ref list, and remove all reference objects +// whose referents are still alive, whose referents are NULL or which +// are not active (have a non-NULL next field). NOTE: For this to work +// correctly, refs discovery can not be happening concurrently with this +// step. +void ReferenceProcessor::preclean_discovered_reflist( + DiscoveredList& refs_list, BoolObjectClosure* is_alive, + OopClosure* keep_alive, VoidClosure* complete_gc, YieldClosure* yield) { + + DiscoveredListIterator iter(refs_list, keep_alive, is_alive); + size_t length = refs_list.length(); + while (iter.has_next()) { + iter.load_ptrs(DEBUG_ONLY(true /* allow_null_referent */)); + oop* next_addr = java_lang_ref_Reference::next_addr(iter.obj()); + if (iter.referent() == NULL || iter.is_referent_alive() || + *next_addr != NULL) { + // The referent has been cleared, or is alive, or the Reference is not + // active; we need to trace and mark its cohort. + if (TraceReferenceGC) { + gclog_or_tty->print_cr("Precleaning Reference (" INTPTR_FORMAT ": %s)", + iter.obj(), iter.obj()->blueprint()->internal_name()); + } + // Remove Reference object from list + iter.remove(); + --length; + // Keep alive its cohort. + iter.make_referent_alive(); + keep_alive->do_oop(next_addr); + } else { + iter.next(); + } + } + refs_list.set_length(length); + + // Close the reachable set + complete_gc->do_void(); + + NOT_PRODUCT( + if (PrintGCDetails && PrintReferenceGC) { + gclog_or_tty->print(" Dropped %d Refs out of %d " + "Refs in discovered list ", iter.removed(), iter.processed()); + } + ) +} + +const char* ReferenceProcessor::list_name(int i) { + assert(i >= 0 && i <= _num_q * subclasses_of_ref, "Out of bounds index"); + int j = i / _num_q; + switch (j) { + case 0: return "SoftRef"; + case 1: return "WeakRef"; + case 2: return "FinalRef"; + case 3: return "PhantomRef"; + } + ShouldNotReachHere(); + return NULL; +} + +#ifndef PRODUCT +void ReferenceProcessor::verify_ok_to_handle_reflists() { + // empty for now +} +#endif + +void ReferenceProcessor::verify() { + guarantee(_sentinelRef != NULL && _sentinelRef->is_oop(), "Lost _sentinelRef"); +} + +#ifndef PRODUCT +void ReferenceProcessor::clear_discovered_references() { + guarantee(!_discovering_refs, "Discovering refs?"); + for (int i = 0; i < _num_q * subclasses_of_ref; i++) { + oop obj = _discoveredSoftRefs[i].head(); + while (obj != _sentinelRef) { + oop next = java_lang_ref_Reference::discovered(obj); + java_lang_ref_Reference::set_discovered(obj, (oop) NULL); + obj = next; + } + _discoveredSoftRefs[i].set_head(_sentinelRef); + _discoveredSoftRefs[i].set_length(0); + } +} +#endif // PRODUCT diff --git a/hotspot/src/share/vm/memory/referenceProcessor.hpp b/hotspot/src/share/vm/memory/referenceProcessor.hpp new file mode 100644 index 00000000000..29e2a7b7795 --- /dev/null +++ b/hotspot/src/share/vm/memory/referenceProcessor.hpp @@ -0,0 +1,499 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ReferenceProcessor class encapsulates the per-"collector" processing +// of "weak" references for GC. The interface is useful for supporting +// a generational abstraction, in particular when there are multiple +// generations that are being independently collected -- possibly +// concurrently and/or incrementally. Note, however, that the +// ReferenceProcessor class abstracts away from a generational setting +// by using only a heap interval (called "span" below), thus allowing +// its use in a straightforward manner in a general, non-generational +// setting. +// +// The basic idea is that each ReferenceProcessor object concerns +// itself with ("weak") reference processing in a specific "span" +// of the heap of interest to a specific collector. Currently, +// the span is a convex interval of the heap, but, efficiency +// apart, there seems to be no reason it couldn't be extended +// (with appropriate modifications) to any "non-convex interval". + +// forward references +class ReferencePolicy; +class AbstractRefProcTaskExecutor; +class DiscoveredList; + +class ReferenceProcessor : public CHeapObj { + friend class DiscoveredList; + friend class DiscoveredListIterator; + protected: + // End of list marker + static oop _sentinelRef; + MemRegion _span; // (right-open) interval of heap + // subject to wkref discovery + bool _discovering_refs; // true when discovery enabled + bool _discovery_is_atomic; // if discovery is atomic wrt + // other collectors in configuration + bool _discovery_is_mt; // true if reference discovery is MT. + bool _enqueuing_is_done; // true if all weak references enqueued + bool _processing_is_mt; // true during phases when + // reference processing is MT. + int _next_id; // round-robin counter in + // support of work distribution + + // For collectors that do not keep GC marking information + // in the object header, this field holds a closure that + // helps the reference processor determine the reachability + // of an oop (the field is currently initialized to NULL for + // all collectors but the CMS collector). + BoolObjectClosure* _is_alive_non_header; + + // The discovered ref lists themselves + int _num_q; // the MT'ness degree of the queues below + DiscoveredList* _discoveredSoftRefs; // pointer to array of oops + DiscoveredList* _discoveredWeakRefs; + DiscoveredList* _discoveredFinalRefs; + DiscoveredList* _discoveredPhantomRefs; + + public: + int num_q() { return _num_q; } + DiscoveredList* discovered_soft_refs() { return _discoveredSoftRefs; } + static oop* sentinel_ref() { return &_sentinelRef; } + + public: + // Process references with a certain reachability level. + void process_discovered_reflist(DiscoveredList refs_lists[], + ReferencePolicy* policy, + bool clear_referent, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + AbstractRefProcTaskExecutor* task_executor); + + void process_phaseJNI(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc); + + // Work methods used by the method process_discovered_reflist + // Phase1: keep alive all those referents that are otherwise + // dead but which must be kept alive by policy (and their closure). + void process_phase1(DiscoveredList& refs_list_addr, + ReferencePolicy* policy, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc); + // Phase2: remove all those references whose referents are + // reachable. + inline void process_phase2(DiscoveredList& refs_list_addr, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc) { + if (discovery_is_atomic()) { + // complete_gc is ignored in this case for this phase + pp2_work(refs_list_addr, is_alive, keep_alive); + } else { + assert(complete_gc != NULL, "Error"); + pp2_work_concurrent_discovery(refs_list_addr, is_alive, + keep_alive, complete_gc); + } + } + // Work methods in support of process_phase2 + void pp2_work(DiscoveredList& refs_list_addr, + BoolObjectClosure* is_alive, + OopClosure* keep_alive); + void pp2_work_concurrent_discovery( + DiscoveredList& refs_list_addr, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc); + // Phase3: process the referents by either clearing them + // or keeping them alive (and their closure) + void process_phase3(DiscoveredList& refs_list_addr, + bool clear_referent, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc); + + // Enqueue references with a certain reachability level + void enqueue_discovered_reflist(DiscoveredList& refs_list, oop* pending_list_addr); + + // "Preclean" all the discovered reference lists + // by removing references with strongly reachable referents. + // The first argument is a predicate on an oop that indicates + // its (strong) reachability and the second is a closure that + // may be used to incrementalize or abort the precleaning process. + // The caller is responsible for taking care of potential + // interference with concurrent operations on these lists + // (or predicates involved) by other threads. Currently + // only used by the CMS collector. + void preclean_discovered_references(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + YieldClosure* yield); + + // Delete entries in the discovered lists that have + // either a null referent or are not active. Such + // Reference objects can result from the clearing + // or enqueueing of Reference objects concurrent + // with their discovery by a (concurrent) collector. + // For a definition of "active" see java.lang.ref.Reference; + // Refs are born active, become inactive when enqueued, + // and never become active again. The state of being + // active is encoded as follows: A Ref is active + // if and only if its "next" field is NULL. + void clean_up_discovered_references(); + void clean_up_discovered_reflist(DiscoveredList& refs_list); + + // Returns the name of the discovered reference list + // occupying the i / _num_q slot. + const char* list_name(int i); + + protected: + // "Preclean" the given discovered reference list + // by removing references with strongly reachable referents. + // Currently used in support of CMS only. + void preclean_discovered_reflist(DiscoveredList& refs_list, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + YieldClosure* yield); + + void enqueue_discovered_reflists(oop* pending_list_addr, AbstractRefProcTaskExecutor* task_executor); + int next_id() { + int id = _next_id; + if (++_next_id == _num_q) { + _next_id = 0; + } + return id; + } + DiscoveredList* get_discovered_list(ReferenceType rt); + inline void add_to_discovered_list_mt(DiscoveredList& refs_list, oop obj, + oop* discovered_addr); + void verify_ok_to_handle_reflists() PRODUCT_RETURN; + + void abandon_partial_discovered_list(DiscoveredList& refs_list); + void abandon_partial_discovered_list_arr(DiscoveredList refs_lists[]); + + // Calculate the number of jni handles. + unsigned int count_jni_refs(); + + // Balances reference queues. + void balance_queues(DiscoveredList ref_lists[]); + + // Update (advance) the soft ref master clock field. + void update_soft_ref_master_clock(); + + public: + // constructor + ReferenceProcessor(): + _span((HeapWord*)NULL, (HeapWord*)NULL), + _discoveredSoftRefs(NULL), _discoveredWeakRefs(NULL), + _discoveredFinalRefs(NULL), _discoveredPhantomRefs(NULL), + _discovering_refs(false), + _discovery_is_atomic(true), + _enqueuing_is_done(false), + _discovery_is_mt(false), + _is_alive_non_header(NULL), + _num_q(0), + _processing_is_mt(false), + _next_id(0) + {} + + ReferenceProcessor(MemRegion span, bool atomic_discovery, + bool mt_discovery, int mt_degree = 1, + bool mt_processing = false); + + // Allocates and initializes a reference processor. + static ReferenceProcessor* create_ref_processor( + MemRegion span, + bool atomic_discovery, + bool mt_discovery, + BoolObjectClosure* is_alive_non_header = NULL, + int parallel_gc_threads = 1, + bool mt_processing = false); + + // RefDiscoveryPolicy values + enum { + ReferenceBasedDiscovery = 0, + ReferentBasedDiscovery = 1 + }; + + static void init_statics(); + + public: + // get and set "is_alive_non_header" field + BoolObjectClosure* is_alive_non_header() { + return _is_alive_non_header; + } + void set_is_alive_non_header(BoolObjectClosure* is_alive_non_header) { + _is_alive_non_header = is_alive_non_header; + } + + // get and set span + MemRegion span() { return _span; } + void set_span(MemRegion span) { _span = span; } + + // start and stop weak ref discovery + void enable_discovery() { _discovering_refs = true; } + void disable_discovery() { _discovering_refs = false; } + bool discovery_enabled() { return _discovering_refs; } + + // whether discovery is atomic wrt other collectors + bool discovery_is_atomic() const { return _discovery_is_atomic; } + void set_atomic_discovery(bool atomic) { _discovery_is_atomic = atomic; } + + // whether discovery is done by multiple threads same-old-timeously + bool discovery_is_mt() const { return _discovery_is_mt; } + void set_mt_discovery(bool mt) { _discovery_is_mt = mt; } + + // Whether we are in a phase when _processing_ is MT. + bool processing_is_mt() const { return _processing_is_mt; } + void set_mt_processing(bool mt) { _processing_is_mt = mt; } + + // whether all enqueuing of weak references is complete + bool enqueuing_is_done() { return _enqueuing_is_done; } + void set_enqueuing_is_done(bool v) { _enqueuing_is_done = v; } + + // iterate over oops + void weak_oops_do(OopClosure* f); // weak roots + static void oops_do(OopClosure* f); // strong root(s) + + // Discover a Reference object, using appropriate discovery criteria + bool discover_reference(oop obj, ReferenceType rt); + + // Process references found during GC (called by the garbage collector) + void process_discovered_references(ReferencePolicy* policy, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + AbstractRefProcTaskExecutor* task_executor); + + public: + // Enqueue references at end of GC (called by the garbage collector) + bool enqueue_discovered_references(AbstractRefProcTaskExecutor* task_executor = NULL); + + // debugging + void verify_no_references_recorded() PRODUCT_RETURN; + static void verify(); + + // clear the discovered lists (unlinking each entry). + void clear_discovered_references() PRODUCT_RETURN; +}; + +// A utility class to disable reference discovery in +// the scope which contains it, for given ReferenceProcessor. +class NoRefDiscovery: StackObj { + private: + ReferenceProcessor* _rp; + bool _was_discovering_refs; + public: + NoRefDiscovery(ReferenceProcessor* rp) : _rp(rp) { + if (_was_discovering_refs = _rp->discovery_enabled()) { + _rp->disable_discovery(); + } + } + + ~NoRefDiscovery() { + if (_was_discovering_refs) { + _rp->enable_discovery(); + } + } +}; + + +// A utility class to temporarily mutate the span of the +// given ReferenceProcessor in the scope that contains it. +class ReferenceProcessorSpanMutator: StackObj { + private: + ReferenceProcessor* _rp; + MemRegion _saved_span; + + public: + ReferenceProcessorSpanMutator(ReferenceProcessor* rp, + MemRegion span): + _rp(rp) { + _saved_span = _rp->span(); + _rp->set_span(span); + } + + ~ReferenceProcessorSpanMutator() { + _rp->set_span(_saved_span); + } +}; + +// A utility class to temporarily change the MT'ness of +// reference discovery for the given ReferenceProcessor +// in the scope that contains it. +class ReferenceProcessorMTMutator: StackObj { + private: + ReferenceProcessor* _rp; + bool _saved_mt; + + public: + ReferenceProcessorMTMutator(ReferenceProcessor* rp, + bool mt): + _rp(rp) { + _saved_mt = _rp->discovery_is_mt(); + _rp->set_mt_discovery(mt); + } + + ~ReferenceProcessorMTMutator() { + _rp->set_mt_discovery(_saved_mt); + } +}; + + +// A utility class to temporarily change the disposition +// of the "is_alive_non_header" closure field of the +// given ReferenceProcessor in the scope that contains it. +class ReferenceProcessorIsAliveMutator: StackObj { + private: + ReferenceProcessor* _rp; + BoolObjectClosure* _saved_cl; + + public: + ReferenceProcessorIsAliveMutator(ReferenceProcessor* rp, + BoolObjectClosure* cl): + _rp(rp) { + _saved_cl = _rp->is_alive_non_header(); + _rp->set_is_alive_non_header(cl); + } + + ~ReferenceProcessorIsAliveMutator() { + _rp->set_is_alive_non_header(_saved_cl); + } +}; + +// A utility class to temporarily change the disposition +// of the "discovery_is_atomic" field of the +// given ReferenceProcessor in the scope that contains it. +class ReferenceProcessorAtomicMutator: StackObj { + private: + ReferenceProcessor* _rp; + bool _saved_atomic_discovery; + + public: + ReferenceProcessorAtomicMutator(ReferenceProcessor* rp, + bool atomic): + _rp(rp) { + _saved_atomic_discovery = _rp->discovery_is_atomic(); + _rp->set_atomic_discovery(atomic); + } + + ~ReferenceProcessorAtomicMutator() { + _rp->set_atomic_discovery(_saved_atomic_discovery); + } +}; + + +// A utility class to temporarily change the MT processing +// disposition of the given ReferenceProcessor instance +// in the scope that contains it. +class ReferenceProcessorMTProcMutator: StackObj { + private: + ReferenceProcessor* _rp; + bool _saved_mt; + + public: + ReferenceProcessorMTProcMutator(ReferenceProcessor* rp, + bool mt): + _rp(rp) { + _saved_mt = _rp->processing_is_mt(); + _rp->set_mt_processing(mt); + } + + ~ReferenceProcessorMTProcMutator() { + _rp->set_mt_processing(_saved_mt); + } +}; + + +// This class is an interface used to implement task execution for the +// reference processing. +class AbstractRefProcTaskExecutor { +public: + + // Abstract tasks to execute. + class ProcessTask; + class EnqueueTask; + + // Executes a task using worker threads. + virtual void execute(ProcessTask& task) = 0; + virtual void execute(EnqueueTask& task) = 0; + + // Switch to single threaded mode. + virtual void set_single_threaded_mode() { }; +}; + +// Abstract reference processing task to execute. +class AbstractRefProcTaskExecutor::ProcessTask { +protected: + ProcessTask(ReferenceProcessor& ref_processor, + DiscoveredList refs_lists[], + bool marks_oops_alive) + : _ref_processor(ref_processor), + _refs_lists(refs_lists), + _marks_oops_alive(marks_oops_alive) + { } + +public: + virtual void work(unsigned int work_id, BoolObjectClosure& is_alive, + OopClosure& keep_alive, + VoidClosure& complete_gc) = 0; + + // Returns true if a task marks some oops as alive. + bool marks_oops_alive() const + { return _marks_oops_alive; } + +protected: + ReferenceProcessor& _ref_processor; + DiscoveredList* _refs_lists; + const bool _marks_oops_alive; +}; + +// Abstract reference processing task to execute. +class AbstractRefProcTaskExecutor::EnqueueTask { +protected: + EnqueueTask(ReferenceProcessor& ref_processor, + DiscoveredList refs_lists[], + oop* pending_list_addr, + oop sentinel_ref, + int n_queues) + : _ref_processor(ref_processor), + _refs_lists(refs_lists), + _pending_list_addr(pending_list_addr), + _sentinel_ref(sentinel_ref), + _n_queues(n_queues) + { } + +public: + virtual void work(unsigned int work_id) = 0; + +protected: + ReferenceProcessor& _ref_processor; + DiscoveredList* _refs_lists; + oop* _pending_list_addr; + oop _sentinel_ref; + int _n_queues; +}; diff --git a/hotspot/src/share/vm/memory/resourceArea.cpp b/hotspot/src/share/vm/memory/resourceArea.cpp new file mode 100644 index 00000000000..979ceab5530 --- /dev/null +++ b/hotspot/src/share/vm/memory/resourceArea.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_resourceArea.cpp.incl" + +//------------------------------ResourceMark----------------------------------- +debug_only(int ResourceArea::_warned;) // to suppress multiple warnings + +// The following routines are declared in allocation.hpp and used everywhere: + +// Allocation in thread-local resource area +extern char* resource_allocate_bytes(size_t size) { + return Thread::current()->resource_area()->allocate_bytes(size); +} +extern char* resource_allocate_bytes(Thread* thread, size_t size) { + return thread->resource_area()->allocate_bytes(size); +} + +extern char* resource_reallocate_bytes( char *old, size_t old_size, size_t new_size){ + return (char*)Thread::current()->resource_area()->Arealloc(old, old_size, new_size); +} + +extern void resource_free_bytes( char *old, size_t size ) { + Thread::current()->resource_area()->Afree(old, size); +} + +#ifdef ASSERT +ResourceMark::ResourceMark(Thread *thread) { + assert(thread == Thread::current(), "not the current thread"); + initialize(thread); +} + +DeoptResourceMark::DeoptResourceMark(Thread *thread) { + assert(thread == Thread::current(), "not the current thread"); + initialize(thread); +} +#endif + + +//------------------------------------------------------------------------------- +// Non-product code +#ifndef PRODUCT + +void ResourceMark::free_malloced_objects() { + Arena::free_malloced_objects(_chunk, _hwm, _max, _area->_hwm); +} + +void DeoptResourceMark::free_malloced_objects() { + Arena::free_malloced_objects(_chunk, _hwm, _max, _area->_hwm); +} + +#endif diff --git a/hotspot/src/share/vm/memory/resourceArea.hpp b/hotspot/src/share/vm/memory/resourceArea.hpp new file mode 100644 index 00000000000..fcd79dd5560 --- /dev/null +++ b/hotspot/src/share/vm/memory/resourceArea.hpp @@ -0,0 +1,226 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The resource area holds temporary data structures in the VM. +// The actual allocation areas are thread local. Typical usage: +// +// ... +// { +// ResourceMark rm; +// int foo[] = NEW_RESOURCE_ARRAY(int, 64); +// ... +// } +// ... + +//------------------------------ResourceArea----------------------------------- +// A ResourceArea is an Arena that supports safe usage of ResourceMark. +class ResourceArea: public Arena { + friend class ResourceMark; + friend class DeoptResourceMark; + debug_only(int _nesting;) // current # of nested ResourceMarks + debug_only(static int _warned;) // to suppress multiple warnings + +public: + ResourceArea() { + debug_only(_nesting = 0;) + } + + ResourceArea(size_t init_size) : Arena(init_size) { + debug_only(_nesting = 0;); + } + + char* allocate_bytes(size_t size) { +#ifdef ASSERT + if (_nesting < 1 && !_warned++) + fatal("memory leak: allocating without ResourceMark"); + if (UseMallocOnly) { + // use malloc, but save pointer in res. area for later freeing + char** save = (char**)internal_malloc_4(sizeof(char*)); + return (*save = (char*)os::malloc(size)); + } +#endif + return (char*)Amalloc(size); + } + + debug_only(int nesting() const { return _nesting; }); +}; + + +//------------------------------ResourceMark----------------------------------- +// A resource mark releases all resources allocated after it was constructed +// when the destructor is called. Typically used as a local variable. +class ResourceMark: public StackObj { +protected: + ResourceArea *_area; // Resource area to stack allocate + Chunk *_chunk; // saved arena chunk + char *_hwm, *_max; + NOT_PRODUCT(size_t _size_in_bytes;) + + void initialize(Thread *thread) { + _area = thread->resource_area(); + _chunk = _area->_chunk; + _hwm = _area->_hwm; + _max= _area->_max; + NOT_PRODUCT(_size_in_bytes = _area->size_in_bytes();) + debug_only(_area->_nesting++;) + assert( _area->_nesting > 0, "must stack allocate RMs" ); + } + + public: + +#ifndef ASSERT + ResourceMark(Thread *thread) { + assert(thread == Thread::current(), "not the current thread"); + initialize(thread); + } +#else + ResourceMark(Thread *thread); +#endif // ASSERT + + ResourceMark() { initialize(Thread::current()); } + + ResourceMark( ResourceArea *r ) : + _area(r), _chunk(r->_chunk), _hwm(r->_hwm), _max(r->_max) { + NOT_PRODUCT(_size_in_bytes = _area->size_in_bytes();) + debug_only(_area->_nesting++;) + assert( _area->_nesting > 0, "must stack allocate RMs" ); + } + + void reset_to_mark() { + if (UseMallocOnly) free_malloced_objects(); + + if( _chunk->next() ) // Delete later chunks + _chunk->next_chop(); + _area->_chunk = _chunk; // Roll back arena to saved chunk + _area->_hwm = _hwm; + _area->_max = _max; + + // clear out this chunk (to detect allocation bugs) + if (ZapResourceArea) memset(_hwm, badResourceValue, _max - _hwm); + _area->set_size_in_bytes(size_in_bytes()); + } + + ~ResourceMark() { + assert( _area->_nesting > 0, "must stack allocate RMs" ); + debug_only(_area->_nesting--;) + reset_to_mark(); + } + + + private: + void free_malloced_objects() PRODUCT_RETURN; + size_t size_in_bytes() NOT_PRODUCT({ return _size_in_bytes; }) PRODUCT_RETURN0; +}; + +//------------------------------DeoptResourceMark----------------------------------- +// A deopt resource mark releases all resources allocated after it was constructed +// when the destructor is called. Typically used as a local variable. It differs +// from a typical resource more in that it is C-Heap allocated so that deoptimization +// can use data structures that are arena based but are not amenable to vanilla +// ResourceMarks because deoptimization can not use a stack allocated mark. During +// deoptimization we go thru the following steps: +// +// 0: start in assembly stub and call either uncommon_trap/fetch_unroll_info +// 1: create the vframeArray (contains pointers to Resource allocated structures) +// This allocates the DeoptResourceMark. +// 2: return to assembly stub and remove stub frame and deoptee frame and create +// the new skeletal frames. +// 3: push new stub frame and call unpack_frames +// 4: retrieve information from the vframeArray to populate the skeletal frames +// 5: release the DeoptResourceMark +// 6: return to stub and eventually to interpreter +// +// With old style eager deoptimization the vframeArray was created by the vmThread there +// was no way for the vframeArray to contain resource allocated objects and so +// a complex set of data structures to simulate an array of vframes in CHeap memory +// was used. With new style lazy deoptimization the vframeArray is created in the +// the thread that will use it and we can use a much simpler scheme for the vframeArray +// leveraging existing data structures if we simply create a way to manage this one +// special need for a ResourceMark. If ResourceMark simply inherited from CHeapObj +// then existing ResourceMarks would work fine since no one use new to allocate them +// and they would be stack allocated. This leaves open the possibilty of accidental +// misuse so we simple duplicate the ResourceMark functionality here. + +class DeoptResourceMark: public CHeapObj { +protected: + ResourceArea *_area; // Resource area to stack allocate + Chunk *_chunk; // saved arena chunk + char *_hwm, *_max; + NOT_PRODUCT(size_t _size_in_bytes;) + + void initialize(Thread *thread) { + _area = thread->resource_area(); + _chunk = _area->_chunk; + _hwm = _area->_hwm; + _max= _area->_max; + NOT_PRODUCT(_size_in_bytes = _area->size_in_bytes();) + debug_only(_area->_nesting++;) + assert( _area->_nesting > 0, "must stack allocate RMs" ); + } + + public: + +#ifndef ASSERT + DeoptResourceMark(Thread *thread) { + assert(thread == Thread::current(), "not the current thread"); + initialize(thread); + } +#else + DeoptResourceMark(Thread *thread); +#endif // ASSERT + + DeoptResourceMark() { initialize(Thread::current()); } + + DeoptResourceMark( ResourceArea *r ) : + _area(r), _chunk(r->_chunk), _hwm(r->_hwm), _max(r->_max) { + NOT_PRODUCT(_size_in_bytes = _area->size_in_bytes();) + debug_only(_area->_nesting++;) + assert( _area->_nesting > 0, "must stack allocate RMs" ); + } + + void reset_to_mark() { + if (UseMallocOnly) free_malloced_objects(); + + if( _chunk->next() ) // Delete later chunks + _chunk->next_chop(); + _area->_chunk = _chunk; // Roll back arena to saved chunk + _area->_hwm = _hwm; + _area->_max = _max; + + // clear out this chunk (to detect allocation bugs) + if (ZapResourceArea) memset(_hwm, badResourceValue, _max - _hwm); + _area->set_size_in_bytes(size_in_bytes()); + } + + ~DeoptResourceMark() { + assert( _area->_nesting > 0, "must stack allocate RMs" ); + debug_only(_area->_nesting--;) + reset_to_mark(); + } + + + private: + void free_malloced_objects() PRODUCT_RETURN; + size_t size_in_bytes() NOT_PRODUCT({ return _size_in_bytes; }) PRODUCT_RETURN0; +}; diff --git a/hotspot/src/share/vm/memory/restore.cpp b/hotspot/src/share/vm/memory/restore.cpp new file mode 100644 index 00000000000..a677a8517f1 --- /dev/null +++ b/hotspot/src/share/vm/memory/restore.cpp @@ -0,0 +1,196 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_restore.cpp.incl" + + +// Closure for serializing initialization data in from a data area +// (oop_array) read from the shared file. + +class ReadClosure : public SerializeOopClosure { +private: + oop** _oop_array; + + inline oop nextOop() { + return *(*_oop_array)++; + } + +public: + ReadClosure(oop** oop_array) { _oop_array = oop_array; } + + void do_oop(oop* p) { + assert(SharedSkipVerify || *p == NULL || *p == Universe::klassKlassObj(), + "initializing previously initialized oop."); + oop obj = nextOop(); + assert(SharedSkipVerify || (intptr_t)obj >= 0 || (intptr_t)obj < -100, + "hit tag while initializing oops."); + assert(SharedSkipVerify || obj->is_oop_or_null(), "invalid oop"); + *p = obj; + } + + void do_ptr(void** p) { + assert(*p == NULL, "initializing previous initialized pointer."); + void* obj = nextOop(); + assert((intptr_t)obj >= 0 || (intptr_t)obj < -100, + "hit tag while initializing ptrs."); + *p = obj; + } + + void do_ptr(HeapWord** p) { do_ptr((void **) p); } + + void do_int(int* p) { + *p = (int)(intptr_t)nextOop(); + } + + void do_size_t(size_t* p) { + // Assumes that size_t and pointers are the same size. + *p = (size_t)nextOop(); + } + + void do_tag(int tag) { + int old_tag; + do_int(&old_tag); + FileMapInfo::assert_mark(tag == old_tag); + } + + void do_region(u_char* start, size_t size) { + assert((intptr_t)start % sizeof(oop) == 0, "bad alignment"); + assert(size % sizeof(oop) == 0, "bad size"); + do_tag((int)size); + while (size > 0) { + *(oop*)start = nextOop(); + start += sizeof(oop); + size -= sizeof(oop); + } + } + + bool reading() const { return true; } +}; + + +// Read the oop and miscellaneous data from the shared file, and +// serialize it out to its various destinations. + +void CompactingPermGenGen::initialize_oops() { + FileMapInfo *mapinfo = FileMapInfo::current_info(); + + char* buffer = mapinfo->region_base(md); + + // Skip over (reserve space for) a list of addresses of C++ vtables + // for Klass objects. They get filled in later. + + // Skip over (reserve space for) dummy C++ vtables Klass objects. + // They are used as is. + + void** vtbl_list = (void**)buffer; + buffer += vtbl_list_size * sizeof(void*); + intptr_t vtable_size = *(intptr_t*)buffer; + buffer += sizeof(intptr_t); + buffer += vtable_size; + + // Create the symbol table using the bucket array at this spot in the + // misc data space. Since the symbol table is often modified, this + // region (of mapped pages) will be copy-on-write. + + int symbolTableLen = *(intptr_t*)buffer; + buffer += sizeof(intptr_t); + int number_of_entries = *(intptr_t*)buffer; + buffer += sizeof(intptr_t); + SymbolTable::create_table((HashtableBucket*)buffer, symbolTableLen, + number_of_entries); + buffer += symbolTableLen; + + // Create the string table using the bucket array at this spot in the + // misc data space. Since the string table is often modified, this + // region (of mapped pages) will be copy-on-write. + + int stringTableLen = *(intptr_t*)buffer; + buffer += sizeof(intptr_t); + number_of_entries = *(intptr_t*)buffer; + buffer += sizeof(intptr_t); + StringTable::create_table((HashtableBucket*)buffer, stringTableLen, + number_of_entries); + buffer += stringTableLen; + + // Create the shared dictionary using the bucket array at this spot in + // the misc data space. Since the shared dictionary table is never + // modified, this region (of mapped pages) will be (effectively, if + // not explicitly) read-only. + + int sharedDictionaryLen = *(intptr_t*)buffer; + buffer += sizeof(intptr_t); + number_of_entries = *(intptr_t*)buffer; + buffer += sizeof(intptr_t); + SystemDictionary::set_shared_dictionary((HashtableBucket*)buffer, + sharedDictionaryLen, + number_of_entries); + buffer += sharedDictionaryLen; + + // Create the package info table using the bucket array at this spot in + // the misc data space. Since the package info table is never + // modified, this region (of mapped pages) will be (effectively, if + // not explicitly) read-only. + + int pkgInfoLen = *(intptr_t*)buffer; + buffer += sizeof(intptr_t); + number_of_entries = *(intptr_t*)buffer; + buffer += sizeof(intptr_t); + ClassLoader::create_package_info_table((HashtableBucket*)buffer, pkgInfoLen, + number_of_entries); + buffer += pkgInfoLen; + ClassLoader::verify(); + + // The following data in the shared misc data region are the linked + // list elements (HashtableEntry objects) for the symbol table, string + // table, and shared dictionary. The heap objects refered to by the + // symbol table, string table, and shared dictionary are permanent and + // unmovable. Since new entries added to the string and symbol tables + // are always added at the beginning of the linked lists, THESE LINKED + // LIST ELEMENTS ARE READ-ONLY. + + int len = *(intptr_t*)buffer; // skip over symbol table entries + buffer += sizeof(intptr_t); + buffer += len; + + len = *(intptr_t*)buffer; // skip over string table entries + buffer += sizeof(intptr_t); + buffer += len; + + len = *(intptr_t*)buffer; // skip over shared dictionary entries + buffer += sizeof(intptr_t); + buffer += len; + + len = *(intptr_t*)buffer; // skip over package info table entries + buffer += sizeof(intptr_t); + buffer += len; + + len = *(intptr_t*)buffer; // skip over package info table char[] arrays. + buffer += sizeof(intptr_t); + buffer += len; + + oop* oop_array = (oop*)buffer; + ReadClosure rc(&oop_array); + serialize_oops(&rc); +} diff --git a/hotspot/src/share/vm/memory/serialize.cpp b/hotspot/src/share/vm/memory/serialize.cpp new file mode 100644 index 00000000000..ec4688d48ac --- /dev/null +++ b/hotspot/src/share/vm/memory/serialize.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_serialize.cpp.incl" + + +// Serialize out the block offset shared array for the shared spaces. + +void CompactingPermGenGen::serialize_bts(SerializeOopClosure* soc) { + _ro_bts->serialize(soc, readonly_bottom, readonly_end); + _rw_bts->serialize(soc, readwrite_bottom, readwrite_end); +} + + +// Read/write a data stream for restoring/preserving oop pointers and +// miscellaneous data from/to the shared archive file. + +void CompactingPermGenGen::serialize_oops(SerializeOopClosure* soc) { + int tag = 0; + soc->do_tag(--tag); + + // Verify the sizes of various oops in the system. + soc->do_tag(sizeof(oopDesc)); + soc->do_tag(sizeof(instanceOopDesc)); + soc->do_tag(sizeof(methodOopDesc)); + soc->do_tag(sizeof(constMethodOopDesc)); + soc->do_tag(sizeof(methodDataOopDesc)); + soc->do_tag(sizeof(arrayOopDesc)); + soc->do_tag(sizeof(constantPoolOopDesc)); + soc->do_tag(sizeof(constantPoolCacheOopDesc)); + soc->do_tag(sizeof(objArrayOopDesc)); + soc->do_tag(sizeof(typeArrayOopDesc)); + soc->do_tag(sizeof(symbolOopDesc)); + soc->do_tag(sizeof(klassOopDesc)); + soc->do_tag(sizeof(markOopDesc)); + soc->do_tag(sizeof(compiledICHolderOopDesc)); + + // Dump the block offset table entries. + GenCollectedHeap* gch = GenCollectedHeap::heap(); + CompactingPermGenGen* pg = (CompactingPermGenGen*)gch->perm_gen(); + pg->serialize_bts(soc); + soc->do_tag(--tag); + pg->ro_space()->serialize_block_offset_array_offsets(soc); + soc->do_tag(--tag); + pg->rw_space()->serialize_block_offset_array_offsets(soc); + soc->do_tag(--tag); + + // Special case - this oop needed in oop->is_oop() assertions. + soc->do_ptr((void**)&Universe::_klassKlassObj); + soc->do_tag(--tag); + + // Dump/restore miscellaneous oops. + Universe::oops_do(soc, true); + soc->do_tag(--tag); + + vmSymbols::oops_do(soc, true); soc->do_tag(--tag); + CodeCache::oops_do(soc); soc->do_tag(--tag); + soc->do_tag(666); +} diff --git a/hotspot/src/share/vm/memory/sharedHeap.cpp b/hotspot/src/share/vm/memory/sharedHeap.cpp new file mode 100644 index 00000000000..c37bbf8a242 --- /dev/null +++ b/hotspot/src/share/vm/memory/sharedHeap.cpp @@ -0,0 +1,291 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_sharedHeap.cpp.incl" + +SharedHeap* SharedHeap::_sh; + +// The set of potentially parallel tasks in strong root scanning. +enum SH_process_strong_roots_tasks { + SH_PS_Universe_oops_do, + SH_PS_JNIHandles_oops_do, + SH_PS_ObjectSynchronizer_oops_do, + SH_PS_FlatProfiler_oops_do, + SH_PS_Management_oops_do, + SH_PS_SystemDictionary_oops_do, + SH_PS_jvmti_oops_do, + SH_PS_vmSymbols_oops_do, + SH_PS_SymbolTable_oops_do, + SH_PS_StringTable_oops_do, + SH_PS_CodeCache_oops_do, + // Leave this one last. + SH_PS_NumElements +}; + +SharedHeap::SharedHeap(CollectorPolicy* policy_) : + CollectedHeap(), + _collector_policy(policy_), + _perm_gen(NULL), _rem_set(NULL), + _strong_roots_parity(0), + _process_strong_tasks(new SubTasksDone(SH_PS_NumElements)), + _workers(NULL), _n_par_threads(0) +{ + if (_process_strong_tasks == NULL || !_process_strong_tasks->valid()) { + vm_exit_during_initialization("Failed necessary allocation."); + } + _sh = this; // ch is static, should be set only once. + if ((UseParNewGC || + (UseConcMarkSweepGC && CMSParallelRemarkEnabled)) && + ParallelGCThreads > 0) { + _workers = new WorkGang("Parallel GC Threads", ParallelGCThreads, true); + if (_workers == NULL) { + vm_exit_during_initialization("Failed necessary allocation."); + } + } +} + + +void SharedHeap::set_par_threads(int t) { + _n_par_threads = t; + _process_strong_tasks->set_par_threads(t); +} + +class AssertIsPermClosure: public OopClosure { +public: + void do_oop(oop* p) { + assert((*p) == NULL || (*p)->is_perm(), "Referent should be perm."); + } +}; +static AssertIsPermClosure assert_is_perm_closure; + +void SharedHeap::change_strong_roots_parity() { + // Also set the new collection parity. + assert(_strong_roots_parity >= 0 && _strong_roots_parity <= 2, + "Not in range."); + _strong_roots_parity++; + if (_strong_roots_parity == 3) _strong_roots_parity = 1; + assert(_strong_roots_parity >= 1 && _strong_roots_parity <= 2, + "Not in range."); +} + +void SharedHeap::process_strong_roots(bool collecting_perm_gen, + ScanningOption so, + OopClosure* roots, + OopsInGenClosure* perm_blk) { + // General strong roots. + if (n_par_threads() == 0) change_strong_roots_parity(); + if (!_process_strong_tasks->is_task_claimed(SH_PS_Universe_oops_do)) { + Universe::oops_do(roots); + ReferenceProcessor::oops_do(roots); + // Consider perm-gen discovered lists to be strong. + perm_gen()->ref_processor()->weak_oops_do(roots); + } + // Global (strong) JNI handles + if (!_process_strong_tasks->is_task_claimed(SH_PS_JNIHandles_oops_do)) + JNIHandles::oops_do(roots); + // All threads execute this; the individual threads are task groups. + if (ParallelGCThreads > 0) { + Threads::possibly_parallel_oops_do(roots); + } else { + Threads::oops_do(roots); + } + if (!_process_strong_tasks-> is_task_claimed(SH_PS_ObjectSynchronizer_oops_do)) + ObjectSynchronizer::oops_do(roots); + if (!_process_strong_tasks->is_task_claimed(SH_PS_FlatProfiler_oops_do)) + FlatProfiler::oops_do(roots); + if (!_process_strong_tasks->is_task_claimed(SH_PS_Management_oops_do)) + Management::oops_do(roots); + if (!_process_strong_tasks->is_task_claimed(SH_PS_jvmti_oops_do)) + JvmtiExport::oops_do(roots); + + if (!_process_strong_tasks->is_task_claimed(SH_PS_SystemDictionary_oops_do)) { + if (so & SO_AllClasses) { + SystemDictionary::oops_do(roots); + } else + if (so & SO_SystemClasses) { + SystemDictionary::always_strong_oops_do(roots); + } + } + + if (!_process_strong_tasks->is_task_claimed(SH_PS_SymbolTable_oops_do)) { + if (so & SO_Symbols) { + SymbolTable::oops_do(roots); + } + // Verify if the symbol table contents are in the perm gen + NOT_PRODUCT(SymbolTable::oops_do(&assert_is_perm_closure)); + } + + if (!_process_strong_tasks->is_task_claimed(SH_PS_StringTable_oops_do)) { + if (so & SO_Strings) { + StringTable::oops_do(roots); + } + // Verify if the string table contents are in the perm gen + NOT_PRODUCT(StringTable::oops_do(&assert_is_perm_closure)); + } + + if (!_process_strong_tasks->is_task_claimed(SH_PS_CodeCache_oops_do)) { + if (so & SO_CodeCache) { + CodeCache::oops_do(roots); + } + // Verify if the code cache contents are in the perm gen + NOT_PRODUCT(CodeCache::oops_do(&assert_is_perm_closure)); + } + + // Roots that should point only into permanent generation. + { + OopClosure* blk = NULL; + if (collecting_perm_gen) { + blk = roots; + } else { + debug_only(blk = &assert_is_perm_closure); + } + if (blk != NULL) { + if (!_process_strong_tasks->is_task_claimed(SH_PS_vmSymbols_oops_do)) + vmSymbols::oops_do(blk); + } + } + + if (!collecting_perm_gen) { + // All threads perform this; coordination is handled internally. + + rem_set()->younger_refs_iterate(perm_gen(), perm_blk); + } + _process_strong_tasks->all_tasks_completed(); +} + +class AlwaysTrueClosure: public BoolObjectClosure { +public: + void do_object(oop p) { ShouldNotReachHere(); } + bool do_object_b(oop p) { return true; } +}; +static AlwaysTrueClosure always_true; + +class SkipAdjustingSharedStrings: public OopClosure { + OopClosure* _clo; +public: + SkipAdjustingSharedStrings(OopClosure* clo) : _clo(clo) {} + + void do_oop(oop* p) { + oop o = (*p); + if (!o->is_shared_readwrite()) { + _clo->do_oop(p); + } + } +}; + +// Unmarked shared Strings in the StringTable (which got there due to +// being in the constant pools of as-yet unloaded shared classes) were +// not marked and therefore did not have their mark words preserved. +// These entries are also deliberately not purged from the string +// table during unloading of unmarked strings. If an identity hash +// code was computed for any of these objects, it will not have been +// cleared to zero during the forwarding process or by the +// RecursiveAdjustSharedObjectClosure, and will be confused by the +// adjusting process as a forwarding pointer. We need to skip +// forwarding StringTable entries which contain unmarked shared +// Strings. Actually, since shared strings won't be moving, we can +// just skip adjusting any shared entries in the string table. + +void SharedHeap::process_weak_roots(OopClosure* root_closure, + OopClosure* non_root_closure) { + // Global (weak) JNI handles + JNIHandles::weak_oops_do(&always_true, root_closure); + + CodeCache::oops_do(non_root_closure); + SymbolTable::oops_do(root_closure); + if (UseSharedSpaces && !DumpSharedSpaces) { + SkipAdjustingSharedStrings skip_closure(root_closure); + StringTable::oops_do(&skip_closure); + } else { + StringTable::oops_do(root_closure); + } +} + +void SharedHeap::set_barrier_set(BarrierSet* bs) { + _barrier_set = bs; + // Cached barrier set for fast access in oops + oopDesc::set_bs(bs); +} + +void SharedHeap::post_initialize() { + ref_processing_init(); +} + +void SharedHeap::ref_processing_init() { + perm_gen()->ref_processor_init(); +} + +void SharedHeap::fill_region_with_object(MemRegion mr) { + // Disable the posting of JVMTI VMObjectAlloc events as we + // don't want the filling of tlabs with filler arrays to be + // reported to the profiler. + NoJvmtiVMObjectAllocMark njm; + + // Disable low memory detector because there is no real allocation. + LowMemoryDetectorDisabler lmd_dis; + + // It turns out that post_allocation_setup_array takes a handle, so the + // call below contains an implicit conversion. Best to free that handle + // as soon as possible. + HandleMark hm; + + size_t word_size = mr.word_size(); + size_t aligned_array_header_size = + align_object_size(typeArrayOopDesc::header_size(T_INT)); + + if (word_size >= aligned_array_header_size) { + const size_t array_length = + pointer_delta(mr.end(), mr.start()) - + typeArrayOopDesc::header_size(T_INT); + const size_t array_length_words = + array_length * (HeapWordSize/sizeof(jint)); + post_allocation_setup_array(Universe::intArrayKlassObj(), + mr.start(), + mr.word_size(), + (int)array_length_words); +#ifdef ASSERT + HeapWord* elt_words = (mr.start() + typeArrayOopDesc::header_size(T_INT)); + Copy::fill_to_words(elt_words, array_length, 0xDEAFBABE); +#endif + } else { + assert(word_size == (size_t)oopDesc::header_size(), "Unaligned?"); + post_allocation_setup_obj(SystemDictionary::object_klass(), + mr.start(), + mr.word_size()); + } +} + +// Some utilities. +void SharedHeap::print_size_transition(size_t bytes_before, + size_t bytes_after, + size_t capacity) { + tty->print(" %d%s->%d%s(%d%s)", + byte_size_in_proper_unit(bytes_before), + proper_unit_for_byte_size(bytes_before), + byte_size_in_proper_unit(bytes_after), + proper_unit_for_byte_size(bytes_after), + byte_size_in_proper_unit(capacity), + proper_unit_for_byte_size(capacity)); +} diff --git a/hotspot/src/share/vm/memory/sharedHeap.hpp b/hotspot/src/share/vm/memory/sharedHeap.hpp new file mode 100644 index 00000000000..caf9e171f7a --- /dev/null +++ b/hotspot/src/share/vm/memory/sharedHeap.hpp @@ -0,0 +1,272 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A "SharedHeap" is an implementation of a java heap for HotSpot. This +// is an abstract class: there may be many different kinds of heaps. This +// class defines the functions that a heap must implement, and contains +// infrastructure common to all heaps. + +class PermGen; +class Generation; +class BarrierSet; +class GenRemSet; +class Space; +class SpaceClosure; +class OopClosure; +class OopsInGenClosure; +class ObjectClosure; +class SubTasksDone; +class WorkGang; +class CollectorPolicy; +class KlassHandle; + +class SharedHeap : public CollectedHeap { + friend class VMStructs; + +private: + // For claiming strong_roots tasks. + SubTasksDone* _process_strong_tasks; + +protected: + // There should be only a single instance of "SharedHeap" in a program. + // This is enforced with the protected constructor below, which will also + // set the static pointer "_sh" to that instance. + static SharedHeap* _sh; + + // All heaps contain a "permanent generation." This is some ways + // similar to a generation in a generational system, in other ways not. + // See the "PermGen" class. + PermGen* _perm_gen; + + // and the Gen Remembered Set, at least one good enough to scan the perm + // gen. + GenRemSet* _rem_set; + + // A gc policy, controls global gc resource issues + CollectorPolicy *_collector_policy; + + // See the discussion below, in the specification of the reader function + // for this variable. + int _strong_roots_parity; + + // If we're doing parallel GC, use this gang of threads. + WorkGang* _workers; + + // Number of parallel threads currently working on GC tasks. + // O indicates use sequential code; 1 means use parallel code even with + // only one thread, for performance testing purposes. + int _n_par_threads; + + // Full initialization is done in a concrete subtype's "initialize" + // function. + SharedHeap(CollectorPolicy* policy_); + +public: + static SharedHeap* heap() { return _sh; } + + CollectorPolicy *collector_policy() const { return _collector_policy; } + + void set_barrier_set(BarrierSet* bs); + + // Does operations required after initialization has been done. + virtual void post_initialize(); + + // Initialization of ("weak") reference processing support + virtual void ref_processing_init(); + + void set_perm(PermGen* perm_gen) { _perm_gen = perm_gen; } + + // A helper function that fills an allocated-but-not-yet-initialized + // region with a garbage object. + static void fill_region_with_object(MemRegion mr); + + // Minimum garbage fill object size + static size_t min_fill_size() { return (size_t)align_object_size(oopDesc::header_size()); } + static size_t min_fill_size_in_bytes() { return min_fill_size() * HeapWordSize; } + + // This function returns the "GenRemSet" object that allows us to scan + // generations; at least the perm gen, possibly more in a fully + // generational heap. + GenRemSet* rem_set() { return _rem_set; } + + // These function return the "permanent" generation, in which + // reflective objects are allocated and stored. Two versions, the second + // of which returns the view of the perm gen as a generation. + PermGen* perm() const { return _perm_gen; } + Generation* perm_gen() const { return _perm_gen->as_gen(); } + + // Iteration functions. + void oop_iterate(OopClosure* cl) = 0; + + // Same as above, restricted to a memory region. + virtual void oop_iterate(MemRegion mr, OopClosure* cl) = 0; + + // Iterate over all objects allocated since the last collection, calling + // "cl->do_object" on each. The heap must have been initialized properly + // to support this function, or else this call will fail. + virtual void object_iterate_since_last_GC(ObjectClosure* cl) = 0; + + // Iterate over all spaces in use in the heap, in an undefined order. + virtual void space_iterate(SpaceClosure* cl) = 0; + + // A SharedHeap will contain some number of spaces. This finds the + // space whose reserved area contains the given address, or else returns + // NULL. + virtual Space* space_containing(const void* addr) const = 0; + + bool no_gc_in_progress() { return !is_gc_active(); } + + // Some collectors will perform "process_strong_roots" in parallel. + // Such a call will involve claiming some fine-grained tasks, such as + // scanning of threads. To make this process simpler, we provide the + // "strong_roots_parity()" method. Collectors that start parallel tasks + // whose threads invoke "process_strong_roots" must + // call "change_strong_roots_parity" in sequential code starting such a + // task. (This also means that a parallel thread may only call + // process_strong_roots once.) + // + // For calls to process_strong_roots by sequential code, the parity is + // updated automatically. + // + // The idea is that objects representing fine-grained tasks, such as + // threads, will contain a "parity" field. A task will is claimed in the + // current "process_strong_roots" call only if its parity field is the + // same as the "strong_roots_parity"; task claiming is accomplished by + // updating the parity field to the strong_roots_parity with a CAS. + // + // If the client meats this spec, then strong_roots_parity() will have + // the following properties: + // a) to return a different value than was returned before the last + // call to change_strong_roots_parity, and + // c) to never return a distinguished value (zero) with which such + // task-claiming variables may be initialized, to indicate "never + // claimed". + void change_strong_roots_parity(); + int strong_roots_parity() { return _strong_roots_parity; } + + enum ScanningOption { + SO_None = 0x0, + SO_AllClasses = 0x1, + SO_SystemClasses = 0x2, + SO_Symbols = 0x4, + SO_Strings = 0x8, + SO_CodeCache = 0x10 + }; + + WorkGang* workers() const { return _workers; } + + // Sets the number of parallel threads that will be doing tasks + // (such as process strong roots) subsequently. + virtual void set_par_threads(int t); + + // Number of threads currently working on GC tasks. + int n_par_threads() { return _n_par_threads; } + + // Invoke the "do_oop" method the closure "roots" on all root locations. + // If "collecting_perm_gen" is false, then roots that may only contain + // references to permGen objects are not scanned. If true, the + // "perm_gen" closure is applied to all older-to-younger refs in the + // permanent generation. The "so" argument determines which of roots + // the closure is applied to: + // "SO_None" does none; + // "SO_AllClasses" applies the closure to all entries in the SystemDictionary; + // "SO_SystemClasses" to all the "system" classes and loaders; + // "SO_Symbols" applies the closure to all entries in SymbolsTable; + // "SO_Strings" applies the closure to all entries in StringTable; + // "SO_CodeCache" applies the closure to all elements of the CodeCache. + void process_strong_roots(bool collecting_perm_gen, + ScanningOption so, + OopClosure* roots, + OopsInGenClosure* perm_blk); + + // Apply "blk" to all the weak roots of the system. These include + // JNI weak roots, the code cache, system dictionary, symbol table, + // string table. + void process_weak_roots(OopClosure* root_closure, + OopClosure* non_root_closure); + + + // Like CollectedHeap::collect, but assume that the caller holds the Heap_lock. + virtual void collect_locked(GCCause::Cause cause) = 0; + + // The functions below are helper functions that a subclass of + // "SharedHeap" can use in the implementation of its virtual + // functions. + +protected: + + // Do anything common to GC's. + virtual void gc_prologue(bool full) = 0; + virtual void gc_epilogue(bool full) = 0; + +public: + // + // New methods from CollectedHeap + // + + size_t permanent_capacity() const { + assert(perm_gen(), "NULL perm gen"); + return perm_gen()->capacity(); + } + + size_t permanent_used() const { + assert(perm_gen(), "NULL perm gen"); + return perm_gen()->used(); + } + + bool is_in_permanent(const void *p) const { + assert(perm_gen(), "NULL perm gen"); + return perm_gen()->is_in_reserved(p); + } + + // Different from is_in_permanent in that is_in_permanent + // only checks if p is in the reserved area of the heap + // and this checks to see if it in the commited area. + // This is typically used by things like the forte stackwalker + // during verification of suspicious frame values. + bool is_permanent(const void *p) const { + assert(perm_gen(), "NULL perm gen"); + return perm_gen()->is_in(p); + } + + HeapWord* permanent_mem_allocate(size_t size) { + assert(perm_gen(), "NULL perm gen"); + return _perm_gen->mem_allocate(size); + } + + void permanent_oop_iterate(OopClosure* cl) { + assert(perm_gen(), "NULL perm gen"); + _perm_gen->oop_iterate(cl); + } + + void permanent_object_iterate(ObjectClosure* cl) { + assert(perm_gen(), "NULL perm gen"); + _perm_gen->object_iterate(cl); + } + + // Some utilities. + void print_size_transition(size_t bytes_before, + size_t bytes_after, + size_t capacity); +}; diff --git a/hotspot/src/share/vm/memory/space.cpp b/hotspot/src/share/vm/memory/space.cpp new file mode 100644 index 00000000000..dd4392091f9 --- /dev/null +++ b/hotspot/src/share/vm/memory/space.cpp @@ -0,0 +1,947 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_space.cpp.incl" + +HeapWord* DirtyCardToOopClosure::get_actual_top(HeapWord* top, + HeapWord* top_obj) { + if (top_obj != NULL) { + if (_sp->block_is_obj(top_obj)) { + if (_precision == CardTableModRefBS::ObjHeadPreciseArray) { + if (oop(top_obj)->is_objArray() || oop(top_obj)->is_typeArray()) { + // An arrayOop is starting on the dirty card - since we do exact + // store checks for objArrays we are done. + } else { + // Otherwise, it is possible that the object starting on the dirty + // card spans the entire card, and that the store happened on a + // later card. Figure out where the object ends. + // Use the block_size() method of the space over which + // the iteration is being done. That space (e.g. CMS) may have + // specific requirements on object sizes which will + // be reflected in the block_size() method. + top = top_obj + oop(top_obj)->size(); + } + } + } else { + top = top_obj; + } + } else { + assert(top == _sp->end(), "only case where top_obj == NULL"); + } + return top; +} + +void DirtyCardToOopClosure::walk_mem_region(MemRegion mr, + HeapWord* bottom, + HeapWord* top) { + // 1. Blocks may or may not be objects. + // 2. Even when a block_is_obj(), it may not entirely + // occupy the block if the block quantum is larger than + // the object size. + // We can and should try to optimize by calling the non-MemRegion + // version of oop_iterate() for all but the extremal objects + // (for which we need to call the MemRegion version of + // oop_iterate()) To be done post-beta XXX + for (; bottom < top; bottom += _sp->block_size(bottom)) { + // As in the case of contiguous space above, we'd like to + // just use the value returned by oop_iterate to increment the + // current pointer; unfortunately, that won't work in CMS because + // we'd need an interface change (it seems) to have the space + // "adjust the object size" (for instance pad it up to its + // block alignment or minimum block size restrictions. XXX + if (_sp->block_is_obj(bottom) && + !_sp->obj_allocated_since_save_marks(oop(bottom))) { + oop(bottom)->oop_iterate(_cl, mr); + } + } +} + +void DirtyCardToOopClosure::do_MemRegion(MemRegion mr) { + + // Some collectors need to do special things whenever their dirty + // cards are processed. For instance, CMS must remember mutator updates + // (i.e. dirty cards) so as to re-scan mutated objects. + // Such work can be piggy-backed here on dirty card scanning, so as to make + // it slightly more efficient than doing a complete non-detructive pre-scan + // of the card table. + MemRegionClosure* pCl = _sp->preconsumptionDirtyCardClosure(); + if (pCl != NULL) { + pCl->do_MemRegion(mr); + } + + HeapWord* bottom = mr.start(); + HeapWord* last = mr.last(); + HeapWord* top = mr.end(); + HeapWord* bottom_obj; + HeapWord* top_obj; + + assert(_precision == CardTableModRefBS::ObjHeadPreciseArray || + _precision == CardTableModRefBS::Precise, + "Only ones we deal with for now."); + + assert(_precision != CardTableModRefBS::ObjHeadPreciseArray || + _last_bottom == NULL || + top <= _last_bottom, + "Not decreasing"); + NOT_PRODUCT(_last_bottom = mr.start()); + + bottom_obj = _sp->block_start(bottom); + top_obj = _sp->block_start(last); + + assert(bottom_obj <= bottom, "just checking"); + assert(top_obj <= top, "just checking"); + + // Given what we think is the top of the memory region and + // the start of the object at the top, get the actual + // value of the top. + top = get_actual_top(top, top_obj); + + // If the previous call did some part of this region, don't redo. + if (_precision == CardTableModRefBS::ObjHeadPreciseArray && + _min_done != NULL && + _min_done < top) { + top = _min_done; + } + + // Top may have been reset, and in fact may be below bottom, + // e.g. the dirty card region is entirely in a now free object + // -- something that could happen with a concurrent sweeper. + bottom = MIN2(bottom, top); + mr = MemRegion(bottom, top); + assert(bottom <= top && + (_precision != CardTableModRefBS::ObjHeadPreciseArray || + _min_done == NULL || + top <= _min_done), + "overlap!"); + + // Walk the region if it is not empty; otherwise there is nothing to do. + if (!mr.is_empty()) { + walk_mem_region(mr, bottom_obj, top); + } + + _min_done = bottom; +} + +DirtyCardToOopClosure* Space::new_dcto_cl(OopClosure* cl, + CardTableModRefBS::PrecisionStyle precision, + HeapWord* boundary) { + return new DirtyCardToOopClosure(this, cl, precision, boundary); +} + +void FilteringClosure::do_oop(oop* p) { + do_oop_nv(p); +} + +HeapWord* ContiguousSpaceDCTOC::get_actual_top(HeapWord* top, + HeapWord* top_obj) { + if (top_obj != NULL && top_obj < (_sp->toContiguousSpace())->top()) { + if (_precision == CardTableModRefBS::ObjHeadPreciseArray) { + if (oop(top_obj)->is_objArray() || oop(top_obj)->is_typeArray()) { + // An arrayOop is starting on the dirty card - since we do exact + // store checks for objArrays we are done. + } else { + // Otherwise, it is possible that the object starting on the dirty + // card spans the entire card, and that the store happened on a + // later card. Figure out where the object ends. + assert(_sp->block_size(top_obj) == (size_t) oop(top_obj)->size(), + "Block size and object size mismatch"); + top = top_obj + oop(top_obj)->size(); + } + } + } else { + top = (_sp->toContiguousSpace())->top(); + } + return top; +} + +void Filtering_DCTOC::walk_mem_region(MemRegion mr, + HeapWord* bottom, + HeapWord* top) { + // Note that this assumption won't hold if we have a concurrent + // collector in this space, which may have freed up objects after + // they were dirtied and before the stop-the-world GC that is + // examining cards here. + assert(bottom < top, "ought to be at least one obj on a dirty card."); + + if (_boundary != NULL) { + // We have a boundary outside of which we don't want to look + // at objects, so create a filtering closure around the + // oop closure before walking the region. + FilteringClosure filter(_boundary, _cl); + walk_mem_region_with_cl(mr, bottom, top, &filter); + } else { + // No boundary, simply walk the heap with the oop closure. + walk_mem_region_with_cl(mr, bottom, top, _cl); + } + +} + +// We must replicate this so that the static type of "FilteringClosure" +// (see above) is apparent at the oop_iterate calls. +#define ContiguousSpaceDCTOC__walk_mem_region_with_cl_DEFN(ClosureType) \ +void ContiguousSpaceDCTOC::walk_mem_region_with_cl(MemRegion mr, \ + HeapWord* bottom, \ + HeapWord* top, \ + ClosureType* cl) { \ + bottom += oop(bottom)->oop_iterate(cl, mr); \ + if (bottom < top) { \ + HeapWord* next_obj = bottom + oop(bottom)->size(); \ + while (next_obj < top) { \ + /* Bottom lies entirely below top, so we can call the */ \ + /* non-memRegion version of oop_iterate below. */ \ + oop(bottom)->oop_iterate(cl); \ + bottom = next_obj; \ + next_obj = bottom + oop(bottom)->size(); \ + } \ + /* Last object. */ \ + oop(bottom)->oop_iterate(cl, mr); \ + } \ +} + +// (There are only two of these, rather than N, because the split is due +// only to the introduction of the FilteringClosure, a local part of the +// impl of this abstraction.) +ContiguousSpaceDCTOC__walk_mem_region_with_cl_DEFN(OopClosure) +ContiguousSpaceDCTOC__walk_mem_region_with_cl_DEFN(FilteringClosure) + +DirtyCardToOopClosure* +ContiguousSpace::new_dcto_cl(OopClosure* cl, + CardTableModRefBS::PrecisionStyle precision, + HeapWord* boundary) { + return new ContiguousSpaceDCTOC(this, cl, precision, boundary); +} + +void Space::initialize(MemRegion mr, bool clear_space) { + HeapWord* bottom = mr.start(); + HeapWord* end = mr.end(); + assert(Universe::on_page_boundary(bottom) && Universe::on_page_boundary(end), + "invalid space boundaries"); + set_bottom(bottom); + set_end(end); + if (clear_space) clear(); +} + +void Space::clear() { + if (ZapUnusedHeapArea) mangle_unused_area(); +} + +void ContiguousSpace::initialize(MemRegion mr, bool clear_space) +{ + CompactibleSpace::initialize(mr, clear_space); + _concurrent_iteration_safe_limit = top(); +} + +void ContiguousSpace::clear() { + set_top(bottom()); + set_saved_mark(); + Space::clear(); +} + +bool Space::is_in(const void* p) const { + HeapWord* b = block_start(p); + return b != NULL && block_is_obj(b); +} + +bool ContiguousSpace::is_in(const void* p) const { + return _bottom <= p && p < _top; +} + +bool ContiguousSpace::is_free_block(const HeapWord* p) const { + return p >= _top; +} + +void OffsetTableContigSpace::clear() { + ContiguousSpace::clear(); + _offsets.initialize_threshold(); +} + +void OffsetTableContigSpace::set_bottom(HeapWord* new_bottom) { + Space::set_bottom(new_bottom); + _offsets.set_bottom(new_bottom); +} + +void OffsetTableContigSpace::set_end(HeapWord* new_end) { + // Space should not advertize an increase in size + // until after the underlying offest table has been enlarged. + _offsets.resize(pointer_delta(new_end, bottom())); + Space::set_end(new_end); +} + +void ContiguousSpace::mangle_unused_area() { + // to-space is used for storing marks during mark-sweep + mangle_region(MemRegion(top(), end())); +} + +void ContiguousSpace::mangle_region(MemRegion mr) { + debug_only(Copy::fill_to_words(mr.start(), mr.word_size(), badHeapWord)); +} + +void CompactibleSpace::initialize(MemRegion mr, bool clear_space) { + Space::initialize(mr, clear_space); + _compaction_top = bottom(); + _next_compaction_space = NULL; +} + +HeapWord* CompactibleSpace::forward(oop q, size_t size, + CompactPoint* cp, HeapWord* compact_top) { + // q is alive + // First check if we should switch compaction space + assert(this == cp->space, "'this' should be current compaction space."); + size_t compaction_max_size = pointer_delta(end(), compact_top); + while (size > compaction_max_size) { + // switch to next compaction space + cp->space->set_compaction_top(compact_top); + cp->space = cp->space->next_compaction_space(); + if (cp->space == NULL) { + cp->gen = GenCollectedHeap::heap()->prev_gen(cp->gen); + assert(cp->gen != NULL, "compaction must succeed"); + cp->space = cp->gen->first_compaction_space(); + assert(cp->space != NULL, "generation must have a first compaction space"); + } + compact_top = cp->space->bottom(); + cp->space->set_compaction_top(compact_top); + cp->threshold = cp->space->initialize_threshold(); + compaction_max_size = pointer_delta(cp->space->end(), compact_top); + } + + // store the forwarding pointer into the mark word + if ((HeapWord*)q != compact_top) { + q->forward_to(oop(compact_top)); + assert(q->is_gc_marked(), "encoding the pointer should preserve the mark"); + } else { + // if the object isn't moving we can just set the mark to the default + // mark and handle it specially later on. + q->init_mark(); + assert(q->forwardee() == NULL, "should be forwarded to NULL"); + } + + debug_only(MarkSweep::register_live_oop(q, size)); + compact_top += size; + + // we need to update the offset table so that the beginnings of objects can be + // found during scavenge. Note that we are updating the offset table based on + // where the object will be once the compaction phase finishes. + if (compact_top > cp->threshold) + cp->threshold = + cp->space->cross_threshold(compact_top - size, compact_top); + return compact_top; +} + + +bool CompactibleSpace::insert_deadspace(size_t& allowed_deadspace_words, + HeapWord* q, size_t deadlength) { + if (allowed_deadspace_words >= deadlength) { + allowed_deadspace_words -= deadlength; + oop(q)->set_mark(markOopDesc::prototype()->set_marked()); + const size_t min_int_array_size = typeArrayOopDesc::header_size(T_INT); + if (deadlength >= min_int_array_size) { + oop(q)->set_klass(Universe::intArrayKlassObj()); + typeArrayOop(q)->set_length((int)((deadlength - min_int_array_size) + * (HeapWordSize/sizeof(jint)))); + } else { + assert((int) deadlength == instanceOopDesc::header_size(), + "size for smallest fake dead object doesn't match"); + oop(q)->set_klass(SystemDictionary::object_klass()); + } + assert((int) deadlength == oop(q)->size(), + "make sure size for fake dead object match"); + // Recall that we required "q == compaction_top". + return true; + } else { + allowed_deadspace_words = 0; + return false; + } +} + +#define block_is_always_obj(q) true +#define obj_size(q) oop(q)->size() +#define adjust_obj_size(s) s + +void CompactibleSpace::prepare_for_compaction(CompactPoint* cp) { + SCAN_AND_FORWARD(cp, end, block_is_obj, block_size); +} + +// Faster object search. +void ContiguousSpace::prepare_for_compaction(CompactPoint* cp) { + SCAN_AND_FORWARD(cp, top, block_is_always_obj, obj_size); +} + +void Space::adjust_pointers() { + // adjust all the interior pointers to point at the new locations of objects + // Used by MarkSweep::mark_sweep_phase3() + + // First check to see if there is any work to be done. + if (used() == 0) { + return; // Nothing to do. + } + + // Otherwise... + HeapWord* q = bottom(); + HeapWord* t = end(); + + debug_only(HeapWord* prev_q = NULL); + while (q < t) { + if (oop(q)->is_gc_marked()) { + // q is alive + + debug_only(MarkSweep::track_interior_pointers(oop(q))); + // point all the oops to the new location + size_t size = oop(q)->adjust_pointers(); + debug_only(MarkSweep::check_interior_pointers()); + + debug_only(prev_q = q); + debug_only(MarkSweep::validate_live_oop(oop(q), size)); + + q += size; + } else { + // q is not a live object. But we're not in a compactible space, + // So we don't have live ranges. + debug_only(prev_q = q); + q += block_size(q); + assert(q > prev_q, "we should be moving forward through memory"); + } + } + assert(q == t, "just checking"); +} + +void CompactibleSpace::adjust_pointers() { + // Check first is there is any work to do. + if (used() == 0) { + return; // Nothing to do. + } + + SCAN_AND_ADJUST_POINTERS(adjust_obj_size); +} + +void CompactibleSpace::compact() { + SCAN_AND_COMPACT(obj_size); +} + +void Space::print_short() const { print_short_on(tty); } + +void Space::print_short_on(outputStream* st) const { + st->print(" space " SIZE_FORMAT "K, %3d%% used", capacity() / K, + (int) ((double) used() * 100 / capacity())); +} + +void Space::print() const { print_on(tty); } + +void Space::print_on(outputStream* st) const { + print_short_on(st); + st->print_cr(" [" INTPTR_FORMAT ", " INTPTR_FORMAT ")", + bottom(), end()); +} + +void ContiguousSpace::print_on(outputStream* st) const { + print_short_on(st); + st->print_cr(" [" INTPTR_FORMAT ", " INTPTR_FORMAT ", " INTPTR_FORMAT ")", + bottom(), top(), end()); +} + +void OffsetTableContigSpace::print_on(outputStream* st) const { + print_short_on(st); + st->print_cr(" [" INTPTR_FORMAT ", " INTPTR_FORMAT ", " + INTPTR_FORMAT ", " INTPTR_FORMAT ")", + bottom(), top(), _offsets.threshold(), end()); +} + +void ContiguousSpace::verify(bool allow_dirty) const { + HeapWord* p = bottom(); + HeapWord* t = top(); + HeapWord* prev_p = NULL; + while (p < t) { + oop(p)->verify(); + prev_p = p; + p += oop(p)->size(); + } + guarantee(p == top(), "end of last object must match end of space"); + if (top() != end()) { + guarantee(top() == block_start(end()-1) && + top() == block_start(top()), + "top should be start of unallocated block, if it exists"); + } +} + +void Space::oop_iterate(OopClosure* blk) { + ObjectToOopClosure blk2(blk); + object_iterate(&blk2); +} + +HeapWord* Space::object_iterate_careful(ObjectClosureCareful* cl) { + guarantee(false, "NYI"); + return bottom(); +} + +HeapWord* Space::object_iterate_careful_m(MemRegion mr, + ObjectClosureCareful* cl) { + guarantee(false, "NYI"); + return bottom(); +} + + +void Space::object_iterate_mem(MemRegion mr, UpwardsObjectClosure* cl) { + assert(!mr.is_empty(), "Should be non-empty"); + // We use MemRegion(bottom(), end()) rather than used_region() below + // because the two are not necessarily equal for some kinds of + // spaces, in particular, certain kinds of free list spaces. + // We could use the more complicated but more precise: + // MemRegion(used_region().start(), round_to(used_region().end(), CardSize)) + // but the slight imprecision seems acceptable in the assertion check. + assert(MemRegion(bottom(), end()).contains(mr), + "Should be within used space"); + HeapWord* prev = cl->previous(); // max address from last time + if (prev >= mr.end()) { // nothing to do + return; + } + // This assert will not work when we go from cms space to perm + // space, and use same closure. Easy fix deferred for later. XXX YSR + // assert(prev == NULL || contains(prev), "Should be within space"); + + bool last_was_obj_array = false; + HeapWord *blk_start_addr, *region_start_addr; + if (prev > mr.start()) { + region_start_addr = prev; + blk_start_addr = prev; + assert(blk_start_addr == block_start(region_start_addr), "invariant"); + } else { + region_start_addr = mr.start(); + blk_start_addr = block_start(region_start_addr); + } + HeapWord* region_end_addr = mr.end(); + MemRegion derived_mr(region_start_addr, region_end_addr); + while (blk_start_addr < region_end_addr) { + const size_t size = block_size(blk_start_addr); + if (block_is_obj(blk_start_addr)) { + last_was_obj_array = cl->do_object_bm(oop(blk_start_addr), derived_mr); + } else { + last_was_obj_array = false; + } + blk_start_addr += size; + } + if (!last_was_obj_array) { + assert((bottom() <= blk_start_addr) && (blk_start_addr <= end()), + "Should be within (closed) used space"); + assert(blk_start_addr > prev, "Invariant"); + cl->set_previous(blk_start_addr); // min address for next time + } +} + +bool Space::obj_is_alive(const HeapWord* p) const { + assert (block_is_obj(p), "The address should point to an object"); + return true; +} + +void ContiguousSpace::object_iterate_mem(MemRegion mr, UpwardsObjectClosure* cl) { + assert(!mr.is_empty(), "Should be non-empty"); + assert(used_region().contains(mr), "Should be within used space"); + HeapWord* prev = cl->previous(); // max address from last time + if (prev >= mr.end()) { // nothing to do + return; + } + // See comment above (in more general method above) in case you + // happen to use this method. + assert(prev == NULL || is_in_reserved(prev), "Should be within space"); + + bool last_was_obj_array = false; + HeapWord *obj_start_addr, *region_start_addr; + if (prev > mr.start()) { + region_start_addr = prev; + obj_start_addr = prev; + assert(obj_start_addr == block_start(region_start_addr), "invariant"); + } else { + region_start_addr = mr.start(); + obj_start_addr = block_start(region_start_addr); + } + HeapWord* region_end_addr = mr.end(); + MemRegion derived_mr(region_start_addr, region_end_addr); + while (obj_start_addr < region_end_addr) { + oop obj = oop(obj_start_addr); + const size_t size = obj->size(); + last_was_obj_array = cl->do_object_bm(obj, derived_mr); + obj_start_addr += size; + } + if (!last_was_obj_array) { + assert((bottom() <= obj_start_addr) && (obj_start_addr <= end()), + "Should be within (closed) used space"); + assert(obj_start_addr > prev, "Invariant"); + cl->set_previous(obj_start_addr); // min address for next time + } +} + +#ifndef SERIALGC +#define ContigSpace_PAR_OOP_ITERATE_DEFN(OopClosureType, nv_suffix) \ + \ + void ContiguousSpace::par_oop_iterate(MemRegion mr, OopClosureType* blk) {\ + HeapWord* obj_addr = mr.start(); \ + HeapWord* t = mr.end(); \ + while (obj_addr < t) { \ + assert(oop(obj_addr)->is_oop(), "Should be an oop"); \ + obj_addr += oop(obj_addr)->oop_iterate(blk); \ + } \ + } + + ALL_PAR_OOP_ITERATE_CLOSURES(ContigSpace_PAR_OOP_ITERATE_DEFN) + +#undef ContigSpace_PAR_OOP_ITERATE_DEFN +#endif // SERIALGC + +void ContiguousSpace::oop_iterate(OopClosure* blk) { + if (is_empty()) return; + HeapWord* obj_addr = bottom(); + HeapWord* t = top(); + // Could call objects iterate, but this is easier. + while (obj_addr < t) { + obj_addr += oop(obj_addr)->oop_iterate(blk); + } +} + +void ContiguousSpace::oop_iterate(MemRegion mr, OopClosure* blk) { + if (is_empty()) { + return; + } + MemRegion cur = MemRegion(bottom(), top()); + mr = mr.intersection(cur); + if (mr.is_empty()) { + return; + } + if (mr.equals(cur)) { + oop_iterate(blk); + return; + } + assert(mr.end() <= top(), "just took an intersection above"); + HeapWord* obj_addr = block_start(mr.start()); + HeapWord* t = mr.end(); + + // Handle first object specially. + oop obj = oop(obj_addr); + SpaceMemRegionOopsIterClosure smr_blk(blk, mr); + obj_addr += obj->oop_iterate(&smr_blk); + while (obj_addr < t) { + oop obj = oop(obj_addr); + assert(obj->is_oop(), "expected an oop"); + obj_addr += obj->size(); + // If "obj_addr" is not greater than top, then the + // entire object "obj" is within the region. + if (obj_addr <= t) { + obj->oop_iterate(blk); + } else { + // "obj" extends beyond end of region + obj->oop_iterate(&smr_blk); + break; + } + }; +} + +void ContiguousSpace::object_iterate(ObjectClosure* blk) { + if (is_empty()) return; + WaterMark bm = bottom_mark(); + object_iterate_from(bm, blk); +} + +void ContiguousSpace::object_iterate_from(WaterMark mark, ObjectClosure* blk) { + assert(mark.space() == this, "Mark does not match space"); + HeapWord* p = mark.point(); + while (p < top()) { + blk->do_object(oop(p)); + p += oop(p)->size(); + } +} + +HeapWord* +ContiguousSpace::object_iterate_careful(ObjectClosureCareful* blk) { + HeapWord * limit = concurrent_iteration_safe_limit(); + assert(limit <= top(), "sanity check"); + for (HeapWord* p = bottom(); p < limit;) { + size_t size = blk->do_object_careful(oop(p)); + if (size == 0) { + return p; // failed at p + } else { + p += size; + } + } + return NULL; // all done +} + +#define ContigSpace_OOP_SINCE_SAVE_MARKS_DEFN(OopClosureType, nv_suffix) \ + \ +void ContiguousSpace:: \ +oop_since_save_marks_iterate##nv_suffix(OopClosureType* blk) { \ + HeapWord* t; \ + HeapWord* p = saved_mark_word(); \ + assert(p != NULL, "expected saved mark"); \ + \ + const intx interval = PrefetchScanIntervalInBytes; \ + do { \ + t = top(); \ + while (p < t) { \ + Prefetch::write(p, interval); \ + debug_only(HeapWord* prev = p); \ + oop m = oop(p); \ + p += m->oop_iterate(blk); \ + } \ + } while (t < top()); \ + \ + set_saved_mark_word(p); \ +} + +ALL_SINCE_SAVE_MARKS_CLOSURES(ContigSpace_OOP_SINCE_SAVE_MARKS_DEFN) + +#undef ContigSpace_OOP_SINCE_SAVE_MARKS_DEFN + +// Very general, slow implementation. +HeapWord* ContiguousSpace::block_start(const void* p) const { + assert(MemRegion(bottom(), end()).contains(p), "p not in space"); + if (p >= top()) { + return top(); + } else { + HeapWord* last = bottom(); + HeapWord* cur = last; + while (cur <= p) { + last = cur; + cur += oop(cur)->size(); + } + assert(oop(last)->is_oop(), "Should be an object start"); + return last; + } +} + +size_t ContiguousSpace::block_size(const HeapWord* p) const { + assert(MemRegion(bottom(), end()).contains(p), "p not in space"); + HeapWord* current_top = top(); + assert(p <= current_top, "p is not a block start"); + assert(p == current_top || oop(p)->is_oop(), "p is not a block start"); + if (p < current_top) + return oop(p)->size(); + else { + assert(p == current_top, "just checking"); + return pointer_delta(end(), (HeapWord*) p); + } +} + +// This version requires locking. +inline HeapWord* ContiguousSpace::allocate_impl(size_t size, + HeapWord* const end_value) { + assert(Heap_lock->owned_by_self() || + (SafepointSynchronize::is_at_safepoint() && + Thread::current()->is_VM_thread()), + "not locked"); + HeapWord* obj = top(); + if (pointer_delta(end_value, obj) >= size) { + HeapWord* new_top = obj + size; + set_top(new_top); + assert(is_aligned(obj) && is_aligned(new_top), "checking alignment"); + return obj; + } else { + return NULL; + } +} + +// This version is lock-free. +inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size, + HeapWord* const end_value) { + do { + HeapWord* obj = top(); + if (pointer_delta(end_value, obj) >= size) { + HeapWord* new_top = obj + size; + HeapWord* result = (HeapWord*)Atomic::cmpxchg_ptr(new_top, top_addr(), obj); + // result can be one of two: + // the old top value: the exchange succeeded + // otherwise: the new value of the top is returned. + if (result == obj) { + assert(is_aligned(obj) && is_aligned(new_top), "checking alignment"); + return obj; + } + } else { + return NULL; + } + } while (true); +} + +// Requires locking. +HeapWord* ContiguousSpace::allocate(size_t size) { + return allocate_impl(size, end()); +} + +// Lock-free. +HeapWord* ContiguousSpace::par_allocate(size_t size) { + return par_allocate_impl(size, end()); +} + +void ContiguousSpace::allocate_temporary_filler(int factor) { + // allocate temporary type array decreasing free size with factor 'factor' + assert(factor >= 0, "just checking"); + size_t size = pointer_delta(end(), top()); + + // if space is full, return + if (size == 0) return; + + if (factor > 0) { + size -= size/factor; + } + size = align_object_size(size); + + const size_t min_int_array_size = typeArrayOopDesc::header_size(T_INT); + if (size >= min_int_array_size) { + size_t length = (size - min_int_array_size) * (HeapWordSize / sizeof(jint)); + // allocate uninitialized int array + typeArrayOop t = (typeArrayOop) allocate(size); + assert(t != NULL, "allocation should succeed"); + t->set_mark(markOopDesc::prototype()); + t->set_klass(Universe::intArrayKlassObj()); + t->set_length((int)length); + } else { + assert((int) size == instanceOopDesc::header_size(), + "size for smallest fake object doesn't match"); + instanceOop obj = (instanceOop) allocate(size); + obj->set_mark(markOopDesc::prototype()); + obj->set_klass(SystemDictionary::object_klass()); + } +} + +void EdenSpace::clear() { + ContiguousSpace::clear(); + set_soft_end(end()); +} + +// Requires locking. +HeapWord* EdenSpace::allocate(size_t size) { + return allocate_impl(size, soft_end()); +} + +// Lock-free. +HeapWord* EdenSpace::par_allocate(size_t size) { + return par_allocate_impl(size, soft_end()); +} + +HeapWord* ConcEdenSpace::par_allocate(size_t size) +{ + do { + // The invariant is top() should be read before end() because + // top() can't be greater than end(), so if an update of _soft_end + // occurs between 'end_val = end();' and 'top_val = top();' top() + // also can grow up to the new end() and the condition + // 'top_val > end_val' is true. To ensure the loading order + // OrderAccess::loadload() is required after top() read. + HeapWord* obj = top(); + OrderAccess::loadload(); + if (pointer_delta(*soft_end_addr(), obj) >= size) { + HeapWord* new_top = obj + size; + HeapWord* result = (HeapWord*)Atomic::cmpxchg_ptr(new_top, top_addr(), obj); + // result can be one of two: + // the old top value: the exchange succeeded + // otherwise: the new value of the top is returned. + if (result == obj) { + assert(is_aligned(obj) && is_aligned(new_top), "checking alignment"); + return obj; + } + } else { + return NULL; + } + } while (true); +} + + +HeapWord* OffsetTableContigSpace::initialize_threshold() { + return _offsets.initialize_threshold(); +} + +HeapWord* OffsetTableContigSpace::cross_threshold(HeapWord* start, HeapWord* end) { + _offsets.alloc_block(start, end); + return _offsets.threshold(); +} + +OffsetTableContigSpace::OffsetTableContigSpace(BlockOffsetSharedArray* sharedOffsetArray, + MemRegion mr) : + _offsets(sharedOffsetArray, mr), + _par_alloc_lock(Mutex::leaf, "OffsetTableContigSpace par alloc lock", true) +{ + _offsets.set_contig_space(this); + initialize(mr, true); +} + + +class VerifyOldOopClosure : public OopClosure { + public: + oop the_obj; + bool allow_dirty; + void do_oop(oop* p) { + the_obj->verify_old_oop(p, allow_dirty); + } +}; + +#define OBJ_SAMPLE_INTERVAL 0 +#define BLOCK_SAMPLE_INTERVAL 100 + +void OffsetTableContigSpace::verify(bool allow_dirty) const { + HeapWord* p = bottom(); + HeapWord* prev_p = NULL; + VerifyOldOopClosure blk; // Does this do anything? + blk.allow_dirty = allow_dirty; + int objs = 0; + int blocks = 0; + + if (VerifyObjectStartArray) { + _offsets.verify(); + } + + while (p < top()) { + size_t size = oop(p)->size(); + // For a sampling of objects in the space, find it using the + // block offset table. + if (blocks == BLOCK_SAMPLE_INTERVAL) { + guarantee(p == block_start(p + (size/2)), "check offset computation"); + blocks = 0; + } else { + blocks++; + } + + if (objs == OBJ_SAMPLE_INTERVAL) { + oop(p)->verify(); + blk.the_obj = oop(p); + oop(p)->oop_iterate(&blk); + objs = 0; + } else { + objs++; + } + prev_p = p; + p += size; + } + guarantee(p == top(), "end of last object must match end of space"); +} + +void OffsetTableContigSpace::serialize_block_offset_array_offsets( + SerializeOopClosure* soc) { + _offsets.serialize(soc); +} + + +int TenuredSpace::allowed_dead_ratio() const { + return MarkSweepDeadRatio; +} + + +int ContigPermSpace::allowed_dead_ratio() const { + return PermMarkSweepDeadRatio; +} diff --git a/hotspot/src/share/vm/memory/space.hpp b/hotspot/src/share/vm/memory/space.hpp new file mode 100644 index 00000000000..e036004ce7b --- /dev/null +++ b/hotspot/src/share/vm/memory/space.hpp @@ -0,0 +1,1046 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A space is an abstraction for the "storage units" backing +// up the generation abstraction. It includes specific +// implementations for keeping track of free and used space, +// for iterating over objects and free blocks, etc. + +// Here's the Space hierarchy: +// +// - Space -- an asbtract base class describing a heap area +// - CompactibleSpace -- a space supporting compaction +// - CompactibleFreeListSpace -- (used for CMS generation) +// - ContiguousSpace -- a compactible space in which all free space +// is contiguous +// - EdenSpace -- contiguous space used as nursery +// - ConcEdenSpace -- contiguous space with a 'soft end safe' allocation +// - OffsetTableContigSpace -- contiguous space with a block offset array +// that allows "fast" block_start calls +// - TenuredSpace -- (used for TenuredGeneration) +// - ContigPermSpace -- an offset table contiguous space for perm gen + +// Forward decls. +class Space; +class BlockOffsetArray; +class BlockOffsetArrayContigSpace; +class Generation; +class CompactibleSpace; +class BlockOffsetTable; +class GenRemSet; +class CardTableRS; +class DirtyCardToOopClosure; + + +// An oop closure that is circumscribed by a filtering memory region. +class SpaceMemRegionOopsIterClosure: public virtual OopClosure { + OopClosure* cl; + MemRegion mr; +public: + void do_oop(oop* p) { + if (mr.contains(p)) { + cl->do_oop(p); + } + } + SpaceMemRegionOopsIterClosure(OopClosure* _cl, MemRegion _mr): cl(_cl), mr(_mr) {} +}; + + +// A Space describes a heap area. Class Space is an abstract +// base class. +// +// Space supports allocation, size computation and GC support is provided. +// +// Invariant: bottom() and end() are on page_size boundaries and +// bottom() <= top() <= end() +// top() is inclusive and end() is exclusive. + +class Space: public CHeapObj { + friend class VMStructs; + protected: + HeapWord* _bottom; + HeapWord* _end; + + // Used in support of save_marks() + HeapWord* _saved_mark_word; + + MemRegionClosure* _preconsumptionDirtyCardClosure; + + // A sequential tasks done structure. This supports + // parallel GC, where we have threads dynamically + // claiming sub-tasks from a larger parallel task. + SequentialSubTasksDone _par_seq_tasks; + + Space(): + _bottom(NULL), _end(NULL), _preconsumptionDirtyCardClosure(NULL) { } + + public: + // Accessors + HeapWord* bottom() const { return _bottom; } + HeapWord* end() const { return _end; } + virtual void set_bottom(HeapWord* value) { _bottom = value; } + virtual void set_end(HeapWord* value) { _end = value; } + + HeapWord* saved_mark_word() const { return _saved_mark_word; } + void set_saved_mark_word(HeapWord* p) { _saved_mark_word = p; } + + MemRegionClosure* preconsumptionDirtyCardClosure() const { + return _preconsumptionDirtyCardClosure; + } + void setPreconsumptionDirtyCardClosure(MemRegionClosure* cl) { + _preconsumptionDirtyCardClosure = cl; + } + + // Returns a subregion of the space containing all the objects in + // the space. + virtual MemRegion used_region() const { return MemRegion(bottom(), end()); } + + // Returns a region that is guaranteed to contain (at least) all objects + // allocated at the time of the last call to "save_marks". If the space + // initializes its DirtyCardToOopClosure's specifying the "contig" option + // (that is, if the space is contiguous), then this region must contain only + // such objects: the memregion will be from the bottom of the region to the + // saved mark. Otherwise, the "obj_allocated_since_save_marks" method of + // the space must distiguish between objects in the region allocated before + // and after the call to save marks. + virtual MemRegion used_region_at_save_marks() const { + return MemRegion(bottom(), saved_mark_word()); + } + + // Initialization + virtual void initialize(MemRegion mr, bool clear_space); + virtual void clear(); + + // For detecting GC bugs. Should only be called at GC boundaries, since + // some unused space may be used as scratch space during GC's. + // Default implementation does nothing. We also call this when expanding + // a space to satisfy an allocation request. See bug #4668531 + virtual void mangle_unused_area() {} + virtual void mangle_region(MemRegion mr) {} + + // Testers + bool is_empty() const { return used() == 0; } + bool not_empty() const { return used() > 0; } + + // Returns true iff the given the space contains the + // given address as part of an allocated object. For + // ceratin kinds of spaces, this might be a potentially + // expensive operation. To prevent performance problems + // on account of its inadvertent use in product jvm's, + // we restrict its use to assertion checks only. + virtual bool is_in(const void* p) const; + + // Returns true iff the given reserved memory of the space contains the + // given address. + bool is_in_reserved(const void* p) const { return _bottom <= p && p < _end; } + + // Returns true iff the given block is not allocated. + virtual bool is_free_block(const HeapWord* p) const = 0; + + // Test whether p is double-aligned + static bool is_aligned(void* p) { + return ((intptr_t)p & (sizeof(double)-1)) == 0; + } + + // Size computations. Sizes are in bytes. + size_t capacity() const { return byte_size(bottom(), end()); } + virtual size_t used() const = 0; + virtual size_t free() const = 0; + + // Iterate over all the ref-containing fields of all objects in the + // space, calling "cl.do_oop" on each. Fields in objects allocated by + // applications of the closure are not included in the iteration. + virtual void oop_iterate(OopClosure* cl); + + // Same as above, restricted to the intersection of a memory region and + // the space. Fields in objects allocated by applications of the closure + // are not included in the iteration. + virtual void oop_iterate(MemRegion mr, OopClosure* cl) = 0; + + // Iterate over all objects in the space, calling "cl.do_object" on + // each. Objects allocated by applications of the closure are not + // included in the iteration. + virtual void object_iterate(ObjectClosure* blk) = 0; + + // Iterate over all objects that intersect with mr, calling "cl->do_object" + // on each. There is an exception to this: if this closure has already + // been invoked on an object, it may skip such objects in some cases. This is + // Most likely to happen in an "upwards" (ascending address) iteration of + // MemRegions. + virtual void object_iterate_mem(MemRegion mr, UpwardsObjectClosure* cl); + + // Iterate over as many initialized objects in the space as possible, + // calling "cl.do_object_careful" on each. Return NULL if all objects + // in the space (at the start of the iteration) were iterated over. + // Return an address indicating the extent of the iteration in the + // event that the iteration had to return because of finding an + // uninitialized object in the space, or if the closure "cl" + // signalled early termination. + virtual HeapWord* object_iterate_careful(ObjectClosureCareful* cl); + virtual HeapWord* object_iterate_careful_m(MemRegion mr, + ObjectClosureCareful* cl); + + // Create and return a new dirty card to oop closure. Can be + // overriden to return the appropriate type of closure + // depending on the type of space in which the closure will + // operate. ResourceArea allocated. + virtual DirtyCardToOopClosure* new_dcto_cl(OopClosure* cl, + CardTableModRefBS::PrecisionStyle precision, + HeapWord* boundary = NULL); + + // If "p" is in the space, returns the address of the start of the + // "block" that contains "p". We say "block" instead of "object" since + // some heaps may not pack objects densely; a chunk may either be an + // object or a non-object. If "p" is not in the space, return NULL. + virtual HeapWord* block_start(const void* p) const = 0; + + // Requires "addr" to be the start of a chunk, and returns its size. + // "addr + size" is required to be the start of a new chunk, or the end + // of the active area of the heap. + virtual size_t block_size(const HeapWord* addr) const = 0; + + // Requires "addr" to be the start of a block, and returns "TRUE" iff + // the block is an object. + virtual bool block_is_obj(const HeapWord* addr) const = 0; + + // Requires "addr" to be the start of a block, and returns "TRUE" iff + // the block is an object and the object is alive. + virtual bool obj_is_alive(const HeapWord* addr) const; + + // Allocation (return NULL if full). Assumes the caller has established + // mutually exclusive access to the space. + virtual HeapWord* allocate(size_t word_size) = 0; + + // Allocation (return NULL if full). Enforces mutual exclusion internally. + virtual HeapWord* par_allocate(size_t word_size) = 0; + + // Returns true if this object has been allocated since a + // generation's "save_marks" call. + virtual bool obj_allocated_since_save_marks(const oop obj) const = 0; + + // Mark-sweep-compact support: all spaces can update pointers to objects + // moving as a part of compaction. + virtual void adjust_pointers(); + + // PrintHeapAtGC support + virtual void print() const; + virtual void print_on(outputStream* st) const; + virtual void print_short() const; + virtual void print_short_on(outputStream* st) const; + + + // Accessor for parallel sequential tasks. + SequentialSubTasksDone* par_seq_tasks() { return &_par_seq_tasks; } + + // IF "this" is a ContiguousSpace, return it, else return NULL. + virtual ContiguousSpace* toContiguousSpace() { + return NULL; + } + + // Debugging + virtual void verify(bool allow_dirty) const = 0; +}; + +// A MemRegionClosure (ResourceObj) whose "do_MemRegion" function applies an +// OopClosure to (the addresses of) all the ref-containing fields that could +// be modified by virtue of the given MemRegion being dirty. (Note that +// because of the imprecise nature of the write barrier, this may iterate +// over oops beyond the region.) +// This base type for dirty card to oop closures handles memory regions +// in non-contiguous spaces with no boundaries, and should be sub-classed +// to support other space types. See ContiguousDCTOC for a sub-class +// that works with ContiguousSpaces. + +class DirtyCardToOopClosure: public MemRegionClosureRO { +protected: + OopClosure* _cl; + Space* _sp; + CardTableModRefBS::PrecisionStyle _precision; + HeapWord* _boundary; // If non-NULL, process only non-NULL oops + // pointing below boundary. + HeapWord* _min_done; // ObjHeadPreciseArray precision requires + // a downwards traversal; this is the + // lowest location already done (or, + // alternatively, the lowest address that + // shouldn't be done again. NULL means infinity.) + NOT_PRODUCT(HeapWord* _last_bottom;) + + // Get the actual top of the area on which the closure will + // operate, given where the top is assumed to be (the end of the + // memory region passed to do_MemRegion) and where the object + // at the top is assumed to start. For example, an object may + // start at the top but actually extend past the assumed top, + // in which case the top becomes the end of the object. + virtual HeapWord* get_actual_top(HeapWord* top, HeapWord* top_obj); + + // Walk the given memory region from bottom to (actual) top + // looking for objects and applying the oop closure (_cl) to + // them. The base implementation of this treats the area as + // blocks, where a block may or may not be an object. Sub- + // classes should override this to provide more accurate + // or possibly more efficient walking. + virtual void walk_mem_region(MemRegion mr, HeapWord* bottom, HeapWord* top); + +public: + DirtyCardToOopClosure(Space* sp, OopClosure* cl, + CardTableModRefBS::PrecisionStyle precision, + HeapWord* boundary) : + _sp(sp), _cl(cl), _precision(precision), _boundary(boundary), + _min_done(NULL) { + NOT_PRODUCT(_last_bottom = NULL;) + } + + void do_MemRegion(MemRegion mr); + + void set_min_done(HeapWord* min_done) { + _min_done = min_done; + } +#ifndef PRODUCT + void set_last_bottom(HeapWord* last_bottom) { + _last_bottom = last_bottom; + } +#endif +}; + +// A structure to represent a point at which objects are being copied +// during compaction. +class CompactPoint : public StackObj { +public: + Generation* gen; + CompactibleSpace* space; + HeapWord* threshold; + CompactPoint(Generation* _gen, CompactibleSpace* _space, + HeapWord* _threshold) : + gen(_gen), space(_space), threshold(_threshold) {} +}; + + +// A space that supports compaction operations. This is usually, but not +// necessarily, a space that is normally contiguous. But, for example, a +// free-list-based space whose normal collection is a mark-sweep without +// compaction could still support compaction in full GC's. + +class CompactibleSpace: public Space { + friend class VMStructs; + friend class CompactibleFreeListSpace; + friend class CompactingPermGenGen; + friend class CMSPermGenGen; +private: + HeapWord* _compaction_top; + CompactibleSpace* _next_compaction_space; + +public: + virtual void initialize(MemRegion mr, bool clear_space); + + // Used temporarily during a compaction phase to hold the value + // top should have when compaction is complete. + HeapWord* compaction_top() const { return _compaction_top; } + + void set_compaction_top(HeapWord* value) { + assert(value == NULL || (value >= bottom() && value <= end()), + "should point inside space"); + _compaction_top = value; + } + + // Perform operations on the space needed after a compaction + // has been performed. + virtual void reset_after_compaction() {} + + // Returns the next space (in the current generation) to be compacted in + // the global compaction order. Also is used to select the next + // space into which to compact. + + virtual CompactibleSpace* next_compaction_space() const { + return _next_compaction_space; + } + + void set_next_compaction_space(CompactibleSpace* csp) { + _next_compaction_space = csp; + } + + // MarkSweep support phase2 + + // Start the process of compaction of the current space: compute + // post-compaction addresses, and insert forwarding pointers. The fields + // "cp->gen" and "cp->compaction_space" are the generation and space into + // which we are currently compacting. This call updates "cp" as necessary, + // and leaves the "compaction_top" of the final value of + // "cp->compaction_space" up-to-date. Offset tables may be updated in + // this phase as if the final copy had occurred; if so, "cp->threshold" + // indicates when the next such action should be taken. + virtual void prepare_for_compaction(CompactPoint* cp); + // MarkSweep support phase3 + virtual void adjust_pointers(); + // MarkSweep support phase4 + virtual void compact(); + + // The maximum percentage of objects that can be dead in the compacted + // live part of a compacted space ("deadwood" support.) + virtual int allowed_dead_ratio() const { return 0; }; + + // Some contiguous spaces may maintain some data structures that should + // be updated whenever an allocation crosses a boundary. This function + // returns the first such boundary. + // (The default implementation returns the end of the space, so the + // boundary is never crossed.) + virtual HeapWord* initialize_threshold() { return end(); } + + // "q" is an object of the given "size" that should be forwarded; + // "cp" names the generation ("gen") and containing "this" (which must + // also equal "cp->space"). "compact_top" is where in "this" the + // next object should be forwarded to. If there is room in "this" for + // the object, insert an appropriate forwarding pointer in "q". + // If not, go to the next compaction space (there must + // be one, since compaction must succeed -- we go to the first space of + // the previous generation if necessary, updating "cp"), reset compact_top + // and then forward. In either case, returns the new value of "compact_top". + // If the forwarding crosses "cp->threshold", invokes the "cross_threhold" + // function of the then-current compaction space, and updates "cp->threshold + // accordingly". + virtual HeapWord* forward(oop q, size_t size, CompactPoint* cp, + HeapWord* compact_top); + + // Return a size with adjusments as required of the space. + virtual size_t adjust_object_size_v(size_t size) const { return size; } + +protected: + // Used during compaction. + HeapWord* _first_dead; + HeapWord* _end_of_live; + + // Minimum size of a free block. + virtual size_t minimum_free_block_size() const = 0; + + // This the function is invoked when an allocation of an object covering + // "start" to "end occurs crosses the threshold; returns the next + // threshold. (The default implementation does nothing.) + virtual HeapWord* cross_threshold(HeapWord* start, HeapWord* the_end) { + return end(); + } + + // Requires "allowed_deadspace_words > 0", that "q" is the start of a + // free block of the given "word_len", and that "q", were it an object, + // would not move if forwared. If the size allows, fill the free + // block with an object, to prevent excessive compaction. Returns "true" + // iff the free region was made deadspace, and modifies + // "allowed_deadspace_words" to reflect the number of available deadspace + // words remaining after this operation. + bool insert_deadspace(size_t& allowed_deadspace_words, HeapWord* q, + size_t word_len); +}; + +#define SCAN_AND_FORWARD(cp,scan_limit,block_is_obj,block_size) { \ + /* Compute the new addresses for the live objects and store it in the mark \ + * Used by universe::mark_sweep_phase2() \ + */ \ + HeapWord* compact_top; /* This is where we are currently compacting to. */ \ + \ + /* We're sure to be here before any objects are compacted into this \ + * space, so this is a good time to initialize this: \ + */ \ + set_compaction_top(bottom()); \ + \ + if (cp->space == NULL) { \ + assert(cp->gen != NULL, "need a generation"); \ + assert(cp->threshold == NULL, "just checking"); \ + assert(cp->gen->first_compaction_space() == this, "just checking"); \ + cp->space = cp->gen->first_compaction_space(); \ + compact_top = cp->space->bottom(); \ + cp->space->set_compaction_top(compact_top); \ + cp->threshold = cp->space->initialize_threshold(); \ + } else { \ + compact_top = cp->space->compaction_top(); \ + } \ + \ + /* We allow some amount of garbage towards the bottom of the space, so \ + * we don't start compacting before there is a significant gain to be made.\ + * Occasionally, we want to ensure a full compaction, which is determined \ + * by the MarkSweepAlwaysCompactCount parameter. \ + */ \ + int invocations = SharedHeap::heap()->perm_gen()->stat_record()->invocations;\ + bool skip_dead = ((invocations % MarkSweepAlwaysCompactCount) != 0); \ + \ + size_t allowed_deadspace = 0; \ + if (skip_dead) { \ + int ratio = allowed_dead_ratio(); \ + allowed_deadspace = (capacity() * ratio / 100) / HeapWordSize; \ + } \ + \ + HeapWord* q = bottom(); \ + HeapWord* t = scan_limit(); \ + \ + HeapWord* end_of_live= q; /* One byte beyond the last byte of the last \ + live object. */ \ + HeapWord* first_dead = end();/* The first dead object. */ \ + LiveRange* liveRange = NULL; /* The current live range, recorded in the \ + first header of preceding free area. */ \ + _first_dead = first_dead; \ + \ + const intx interval = PrefetchScanIntervalInBytes; \ + \ + while (q < t) { \ + assert(!block_is_obj(q) || \ + oop(q)->mark()->is_marked() || oop(q)->mark()->is_unlocked() || \ + oop(q)->mark()->has_bias_pattern(), \ + "these are the only valid states during a mark sweep"); \ + if (block_is_obj(q) && oop(q)->is_gc_marked()) { \ + /* prefetch beyond q */ \ + Prefetch::write(q, interval); \ + /* size_t size = oop(q)->size(); changing this for cms for perm gen */\ + size_t size = block_size(q); \ + compact_top = cp->space->forward(oop(q), size, cp, compact_top); \ + q += size; \ + end_of_live = q; \ + } else { \ + /* run over all the contiguous dead objects */ \ + HeapWord* end = q; \ + do { \ + /* prefetch beyond end */ \ + Prefetch::write(end, interval); \ + end += block_size(end); \ + } while (end < t && (!block_is_obj(end) || !oop(end)->is_gc_marked()));\ + \ + /* see if we might want to pretend this object is alive so that \ + * we don't have to compact quite as often. \ + */ \ + if (allowed_deadspace > 0 && q == compact_top) { \ + size_t sz = pointer_delta(end, q); \ + if (insert_deadspace(allowed_deadspace, q, sz)) { \ + compact_top = cp->space->forward(oop(q), sz, cp, compact_top); \ + q = end; \ + end_of_live = end; \ + continue; \ + } \ + } \ + \ + /* otherwise, it really is a free region. */ \ + \ + /* for the previous LiveRange, record the end of the live objects. */ \ + if (liveRange) { \ + liveRange->set_end(q); \ + } \ + \ + /* record the current LiveRange object. \ + * liveRange->start() is overlaid on the mark word. \ + */ \ + liveRange = (LiveRange*)q; \ + liveRange->set_start(end); \ + liveRange->set_end(end); \ + \ + /* see if this is the first dead region. */ \ + if (q < first_dead) { \ + first_dead = q; \ + } \ + \ + /* move on to the next object */ \ + q = end; \ + } \ + } \ + \ + assert(q == t, "just checking"); \ + if (liveRange != NULL) { \ + liveRange->set_end(q); \ + } \ + _end_of_live = end_of_live; \ + if (end_of_live < first_dead) { \ + first_dead = end_of_live; \ + } \ + _first_dead = first_dead; \ + \ + /* save the compaction_top of the compaction space. */ \ + cp->space->set_compaction_top(compact_top); \ +} + +#define SCAN_AND_ADJUST_POINTERS(adjust_obj_size) { \ + /* adjust all the interior pointers to point at the new locations of objects \ + * Used by MarkSweep::mark_sweep_phase3() */ \ + \ + HeapWord* q = bottom(); \ + HeapWord* t = _end_of_live; /* Established by "prepare_for_compaction". */ \ + \ + assert(_first_dead <= _end_of_live, "Stands to reason, no?"); \ + \ + if (q < t && _first_dead > q && \ + !oop(q)->is_gc_marked()) { \ + /* we have a chunk of the space which hasn't moved and we've \ + * reinitialized the mark word during the previous pass, so we can't \ + * use is_gc_marked for the traversal. */ \ + HeapWord* end = _first_dead; \ + \ + while (q < end) { \ + /* I originally tried to conjoin "block_start(q) == q" to the \ + * assertion below, but that doesn't work, because you can't \ + * accurately traverse previous objects to get to the current one \ + * after their pointers (including pointers into permGen) have been \ + * updated, until the actual compaction is done. dld, 4/00 */ \ + assert(block_is_obj(q), \ + "should be at block boundaries, and should be looking at objs"); \ + \ + debug_only(MarkSweep::track_interior_pointers(oop(q))); \ + \ + /* point all the oops to the new location */ \ + size_t size = oop(q)->adjust_pointers(); \ + size = adjust_obj_size(size); \ + \ + debug_only(MarkSweep::check_interior_pointers()); \ + \ + debug_only(MarkSweep::validate_live_oop(oop(q), size)); \ + \ + q += size; \ + } \ + \ + if (_first_dead == t) { \ + q = t; \ + } else { \ + /* $$$ This is funky. Using this to read the previously written \ + * LiveRange. See also use below. */ \ + q = (HeapWord*)oop(_first_dead)->mark()->decode_pointer(); \ + } \ + } \ + \ + const intx interval = PrefetchScanIntervalInBytes; \ + \ + debug_only(HeapWord* prev_q = NULL); \ + while (q < t) { \ + /* prefetch beyond q */ \ + Prefetch::write(q, interval); \ + if (oop(q)->is_gc_marked()) { \ + /* q is alive */ \ + debug_only(MarkSweep::track_interior_pointers(oop(q))); \ + /* point all the oops to the new location */ \ + size_t size = oop(q)->adjust_pointers(); \ + size = adjust_obj_size(size); \ + debug_only(MarkSweep::check_interior_pointers()); \ + debug_only(MarkSweep::validate_live_oop(oop(q), size)); \ + debug_only(prev_q = q); \ + q += size; \ + } else { \ + /* q is not a live object, so its mark should point at the next \ + * live object */ \ + debug_only(prev_q = q); \ + q = (HeapWord*) oop(q)->mark()->decode_pointer(); \ + assert(q > prev_q, "we should be moving forward through memory"); \ + } \ + } \ + \ + assert(q == t, "just checking"); \ +} + +#define SCAN_AND_COMPACT(obj_size) { \ + /* Copy all live objects to their new location \ + * Used by MarkSweep::mark_sweep_phase4() */ \ + \ + HeapWord* q = bottom(); \ + HeapWord* const t = _end_of_live; \ + debug_only(HeapWord* prev_q = NULL); \ + \ + if (q < t && _first_dead > q && \ + !oop(q)->is_gc_marked()) { \ + debug_only( \ + /* we have a chunk of the space which hasn't moved and we've reinitialized the \ + * mark word during the previous pass, so we can't use is_gc_marked for the \ + * traversal. */ \ + HeapWord* const end = _first_dead; \ + \ + while (q < end) { \ + size_t size = obj_size(q); \ + assert(!oop(q)->is_gc_marked(), "should be unmarked (special dense prefix handling)"); \ + debug_only(MarkSweep::live_oop_moved_to(q, size, q)); \ + debug_only(prev_q = q); \ + q += size; \ + } \ + ) /* debug_only */ \ + \ + if (_first_dead == t) { \ + q = t; \ + } else { \ + /* $$$ Funky */ \ + q = (HeapWord*) oop(_first_dead)->mark()->decode_pointer(); \ + } \ + } \ + \ + const intx scan_interval = PrefetchScanIntervalInBytes; \ + const intx copy_interval = PrefetchCopyIntervalInBytes; \ + while (q < t) { \ + if (!oop(q)->is_gc_marked()) { \ + /* mark is pointer to next marked oop */ \ + debug_only(prev_q = q); \ + q = (HeapWord*) oop(q)->mark()->decode_pointer(); \ + assert(q > prev_q, "we should be moving forward through memory"); \ + } else { \ + /* prefetch beyond q */ \ + Prefetch::read(q, scan_interval); \ + \ + /* size and destination */ \ + size_t size = obj_size(q); \ + HeapWord* compaction_top = (HeapWord*)oop(q)->forwardee(); \ + \ + /* prefetch beyond compaction_top */ \ + Prefetch::write(compaction_top, copy_interval); \ + \ + /* copy object and reinit its mark */ \ + debug_only(MarkSweep::live_oop_moved_to(q, size, compaction_top)); \ + assert(q != compaction_top, "everything in this pass should be moving"); \ + Copy::aligned_conjoint_words(q, compaction_top, size); \ + oop(compaction_top)->init_mark(); \ + assert(oop(compaction_top)->klass() != NULL, "should have a class"); \ + \ + debug_only(prev_q = q); \ + q += size; \ + } \ + } \ + \ + /* Reset space after compaction is complete */ \ + reset_after_compaction(); \ + /* We do this clear, below, since it has overloaded meanings for some */ \ + /* space subtypes. For example, OffsetTableContigSpace's that were */ \ + /* compacted into will have had their offset table thresholds updated */ \ + /* continuously, but those that weren't need to have their thresholds */ \ + /* re-initialized. Also mangles unused area for debugging. */ \ + if (is_empty()) { \ + clear(); \ + } else { \ + if (ZapUnusedHeapArea) mangle_unused_area(); \ + } \ +} + +// A space in which the free area is contiguous. It therefore supports +// faster allocation, and compaction. +class ContiguousSpace: public CompactibleSpace { + friend class OneContigSpaceCardGeneration; + friend class VMStructs; + protected: + HeapWord* _top; + HeapWord* _concurrent_iteration_safe_limit; + + // Allocation helpers (return NULL if full). + inline HeapWord* allocate_impl(size_t word_size, HeapWord* end_value); + inline HeapWord* par_allocate_impl(size_t word_size, HeapWord* end_value); + + public: + virtual void initialize(MemRegion mr, bool clear_space); + + // Accessors + HeapWord* top() const { return _top; } + void set_top(HeapWord* value) { _top = value; } + + void set_saved_mark() { _saved_mark_word = top(); } + void reset_saved_mark() { _saved_mark_word = bottom(); } + + virtual void clear(); + + WaterMark bottom_mark() { return WaterMark(this, bottom()); } + WaterMark top_mark() { return WaterMark(this, top()); } + WaterMark saved_mark() { return WaterMark(this, saved_mark_word()); } + bool saved_mark_at_top() const { return saved_mark_word() == top(); } + + void mangle_unused_area(); + void mangle_region(MemRegion mr); + + // Size computations: sizes in bytes. + size_t capacity() const { return byte_size(bottom(), end()); } + size_t used() const { return byte_size(bottom(), top()); } + size_t free() const { return byte_size(top(), end()); } + + // Override from space. + bool is_in(const void* p) const; + + virtual bool is_free_block(const HeapWord* p) const; + + // In a contiguous space we have a more obvious bound on what parts + // contain objects. + MemRegion used_region() const { return MemRegion(bottom(), top()); } + + MemRegion used_region_at_save_marks() const { + return MemRegion(bottom(), saved_mark_word()); + } + + // Allocation (return NULL if full) + virtual HeapWord* allocate(size_t word_size); + virtual HeapWord* par_allocate(size_t word_size); + + virtual bool obj_allocated_since_save_marks(const oop obj) const { + return (HeapWord*)obj >= saved_mark_word(); + } + + // Iteration + void oop_iterate(OopClosure* cl); + void oop_iterate(MemRegion mr, OopClosure* cl); + void object_iterate(ObjectClosure* blk); + void object_iterate_mem(MemRegion mr, UpwardsObjectClosure* cl); + // iterates on objects up to the safe limit + HeapWord* object_iterate_careful(ObjectClosureCareful* cl); + inline HeapWord* concurrent_iteration_safe_limit(); + // changes the safe limit, all objects from bottom() to the new + // limit should be properly initialized + inline void set_concurrent_iteration_safe_limit(HeapWord* new_limit); + +#ifndef SERIALGC + // In support of parallel oop_iterate. + #define ContigSpace_PAR_OOP_ITERATE_DECL(OopClosureType, nv_suffix) \ + void par_oop_iterate(MemRegion mr, OopClosureType* blk); + + ALL_PAR_OOP_ITERATE_CLOSURES(ContigSpace_PAR_OOP_ITERATE_DECL) + #undef ContigSpace_PAR_OOP_ITERATE_DECL +#endif // SERIALGC + + // Compaction support + virtual void reset_after_compaction() { + assert(compaction_top() >= bottom() && compaction_top() <= end(), "should point inside space"); + set_top(compaction_top()); + // set new iteration safe limit + set_concurrent_iteration_safe_limit(compaction_top()); + } + virtual size_t minimum_free_block_size() const { return 0; } + + // Override. + DirtyCardToOopClosure* new_dcto_cl(OopClosure* cl, + CardTableModRefBS::PrecisionStyle precision, + HeapWord* boundary = NULL); + + // Apply "blk->do_oop" to the addresses of all reference fields in objects + // starting with the _saved_mark_word, which was noted during a generation's + // save_marks and is required to denote the head of an object. + // Fields in objects allocated by applications of the closure + // *are* included in the iteration. + // Updates _saved_mark_word to point to just after the last object + // iterated over. +#define ContigSpace_OOP_SINCE_SAVE_MARKS_DECL(OopClosureType, nv_suffix) \ + void oop_since_save_marks_iterate##nv_suffix(OopClosureType* blk); + + ALL_SINCE_SAVE_MARKS_CLOSURES(ContigSpace_OOP_SINCE_SAVE_MARKS_DECL) +#undef ContigSpace_OOP_SINCE_SAVE_MARKS_DECL + + // Same as object_iterate, but starting from "mark", which is required + // to denote the start of an object. Objects allocated by + // applications of the closure *are* included in the iteration. + virtual void object_iterate_from(WaterMark mark, ObjectClosure* blk); + + // Very inefficient implementation. + virtual HeapWord* block_start(const void* p) const; + size_t block_size(const HeapWord* p) const; + // If a block is in the allocated area, it is an object. + bool block_is_obj(const HeapWord* p) const { return p < top(); } + + // Addresses for inlined allocation + HeapWord** top_addr() { return &_top; } + HeapWord** end_addr() { return &_end; } + + // Overrides for more efficient compaction support. + void prepare_for_compaction(CompactPoint* cp); + + // PrintHeapAtGC support. + virtual void print_on(outputStream* st) const; + + // Checked dynamic downcasts. + virtual ContiguousSpace* toContiguousSpace() { + return this; + } + + // Debugging + virtual void verify(bool allow_dirty) const; + + // Used to increase collection frequency. "factor" of 0 means entire + // space. + void allocate_temporary_filler(int factor); + +}; + + +// A dirty card to oop closure that does filtering. +// It knows how to filter out objects that are outside of the _boundary. +class Filtering_DCTOC : public DirtyCardToOopClosure { +protected: + // Override. + void walk_mem_region(MemRegion mr, + HeapWord* bottom, HeapWord* top); + + // Walk the given memory region, from bottom to top, applying + // the given oop closure to (possibly) all objects found. The + // given oop closure may or may not be the same as the oop + // closure with which this closure was created, as it may + // be a filtering closure which makes use of the _boundary. + // We offer two signatures, so the FilteringClosure static type is + // apparent. + virtual void walk_mem_region_with_cl(MemRegion mr, + HeapWord* bottom, HeapWord* top, + OopClosure* cl) = 0; + virtual void walk_mem_region_with_cl(MemRegion mr, + HeapWord* bottom, HeapWord* top, + FilteringClosure* cl) = 0; + +public: + Filtering_DCTOC(Space* sp, OopClosure* cl, + CardTableModRefBS::PrecisionStyle precision, + HeapWord* boundary) : + DirtyCardToOopClosure(sp, cl, precision, boundary) {} +}; + +// A dirty card to oop closure for contiguous spaces +// (ContiguousSpace and sub-classes). +// It is a FilteringClosure, as defined above, and it knows: +// +// 1. That the actual top of any area in a memory region +// contained by the space is bounded by the end of the contiguous +// region of the space. +// 2. That the space is really made up of objects and not just +// blocks. + +class ContiguousSpaceDCTOC : public Filtering_DCTOC { +protected: + // Overrides. + HeapWord* get_actual_top(HeapWord* top, HeapWord* top_obj); + + virtual void walk_mem_region_with_cl(MemRegion mr, + HeapWord* bottom, HeapWord* top, + OopClosure* cl); + virtual void walk_mem_region_with_cl(MemRegion mr, + HeapWord* bottom, HeapWord* top, + FilteringClosure* cl); + +public: + ContiguousSpaceDCTOC(ContiguousSpace* sp, OopClosure* cl, + CardTableModRefBS::PrecisionStyle precision, + HeapWord* boundary) : + Filtering_DCTOC(sp, cl, precision, boundary) + {} +}; + + +// Class EdenSpace describes eden-space in new generation. + +class DefNewGeneration; + +class EdenSpace : public ContiguousSpace { + friend class VMStructs; + private: + DefNewGeneration* _gen; + + // _soft_end is used as a soft limit on allocation. As soft limits are + // reached, the slow-path allocation code can invoke other actions and then + // adjust _soft_end up to a new soft limit or to end(). + HeapWord* _soft_end; + + public: + EdenSpace(DefNewGeneration* gen) : _gen(gen) { _soft_end = NULL; } + + // Get/set just the 'soft' limit. + HeapWord* soft_end() { return _soft_end; } + HeapWord** soft_end_addr() { return &_soft_end; } + void set_soft_end(HeapWord* value) { _soft_end = value; } + + // Override. + void clear(); + + // Set both the 'hard' and 'soft' limits (_end and _soft_end). + void set_end(HeapWord* value) { + set_soft_end(value); + ContiguousSpace::set_end(value); + } + + // Allocation (return NULL if full) + HeapWord* allocate(size_t word_size); + HeapWord* par_allocate(size_t word_size); +}; + +// Class ConcEdenSpace extends EdenSpace for the sake of safe +// allocation while soft-end is being modified concurrently + +class ConcEdenSpace : public EdenSpace { + public: + ConcEdenSpace(DefNewGeneration* gen) : EdenSpace(gen) { } + + // Allocation (return NULL if full) + HeapWord* par_allocate(size_t word_size); +}; + + +// A ContigSpace that Supports an efficient "block_start" operation via +// a BlockOffsetArray (whose BlockOffsetSharedArray may be shared with +// other spaces.) This is the abstract base class for old generation +// (tenured, perm) spaces. + +class OffsetTableContigSpace: public ContiguousSpace { + friend class VMStructs; + protected: + BlockOffsetArrayContigSpace _offsets; + Mutex _par_alloc_lock; + + public: + // Constructor + OffsetTableContigSpace(BlockOffsetSharedArray* sharedOffsetArray, + MemRegion mr); + + void set_bottom(HeapWord* value); + void set_end(HeapWord* value); + + void clear(); + + inline HeapWord* block_start(const void* p) const; + + // Add offset table update. + virtual inline HeapWord* allocate(size_t word_size); + inline HeapWord* par_allocate(size_t word_size); + + // MarkSweep support phase3 + virtual HeapWord* initialize_threshold(); + virtual HeapWord* cross_threshold(HeapWord* start, HeapWord* end); + + virtual void print_on(outputStream* st) const; + + // Debugging + void verify(bool allow_dirty) const; + + // Shared space support + void serialize_block_offset_array_offsets(SerializeOopClosure* soc); +}; + + +// Class TenuredSpace is used by TenuredGeneration + +class TenuredSpace: public OffsetTableContigSpace { + friend class VMStructs; + protected: + // Mark sweep support + int allowed_dead_ratio() const; + public: + // Constructor + TenuredSpace(BlockOffsetSharedArray* sharedOffsetArray, + MemRegion mr) : + OffsetTableContigSpace(sharedOffsetArray, mr) {} +}; + + +// Class ContigPermSpace is used by CompactingPermGen + +class ContigPermSpace: public OffsetTableContigSpace { + friend class VMStructs; + protected: + // Mark sweep support + int allowed_dead_ratio() const; + public: + // Constructor + ContigPermSpace(BlockOffsetSharedArray* sharedOffsetArray, MemRegion mr) : + OffsetTableContigSpace(sharedOffsetArray, mr) {} +}; diff --git a/hotspot/src/share/vm/memory/space.inline.hpp b/hotspot/src/share/vm/memory/space.inline.hpp new file mode 100644 index 00000000000..6ca9bf2b1a0 --- /dev/null +++ b/hotspot/src/share/vm/memory/space.inline.hpp @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline HeapWord* OffsetTableContigSpace::allocate(size_t size) { + HeapWord* res = ContiguousSpace::allocate(size); + if (res != NULL) { + _offsets.alloc_block(res, size); + } + return res; +} + +// Because of the requirement of keeping "_offsets" up to date with the +// allocations, we sequentialize these with a lock. Therefore, best if +// this is used for larger LAB allocations only. +inline HeapWord* OffsetTableContigSpace::par_allocate(size_t size) { + MutexLocker x(&_par_alloc_lock); + // This ought to be just "allocate", because of the lock above, but that + // ContiguousSpace::allocate asserts that either the allocating thread + // holds the heap lock or it is the VM thread and we're at a safepoint. + // The best I (dld) could figure was to put a field in ContiguousSpace + // meaning "locking at safepoint taken care of", and set/reset that + // here. But this will do for now, especially in light of the comment + // above. Perhaps in the future some lock-free manner of keeping the + // coordination. + HeapWord* res = ContiguousSpace::par_allocate(size); + if (res != NULL) { + _offsets.alloc_block(res, size); + } + return res; +} + +inline HeapWord* OffsetTableContigSpace::block_start(const void* p) const { + return _offsets.block_start(p); +} + +inline HeapWord* ContiguousSpace::concurrent_iteration_safe_limit() +{ + assert(_concurrent_iteration_safe_limit <= top(), + "_concurrent_iteration_safe_limit update missed"); + return _concurrent_iteration_safe_limit; +} + +inline void ContiguousSpace::set_concurrent_iteration_safe_limit(HeapWord* new_limit) +{ + assert(new_limit <= top(), "uninitialized objects in the safe range"); + _concurrent_iteration_safe_limit = new_limit; +} diff --git a/hotspot/src/share/vm/memory/specialized_oop_closures.cpp b/hotspot/src/share/vm/memory/specialized_oop_closures.cpp new file mode 100644 index 00000000000..9682863a8a0 --- /dev/null +++ b/hotspot/src/share/vm/memory/specialized_oop_closures.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_specialized_oop_closures.cpp.incl" + +// For keeping stats on effectiveness. +#ifndef PRODUCT +#if ENABLE_SPECIALIZATION_STATS + +int SpecializationStats::_numCallsAll; + +int SpecializationStats::_numCallsTotal[NUM_Kinds]; +int SpecializationStats::_numCalls_nv[NUM_Kinds]; + +int SpecializationStats::_numDoOopCallsTotal[NUM_Kinds]; +int SpecializationStats::_numDoOopCalls_nv[NUM_Kinds]; + +void SpecializationStats::clear() { + _numCallsAll = 0; + for (int k = ik; k < NUM_Kinds; k++) { + _numCallsTotal[k] = 0; + _numCalls_nv[k] = 0; + + _numDoOopCallsTotal[k] = 0; + _numDoOopCalls_nv[k] = 0; + } +} + +void SpecializationStats::print() { + const char* header_format = " %20s %10s %11s %10s"; + const char* line_format = " %20s %10d %11d %9.2f%%"; + int all_numCallsTotal = + _numCallsTotal[ik] + _numCallsTotal[irk] + _numCallsTotal[oa]; + int all_numCalls_nv = + _numCalls_nv[ik] + _numCalls_nv[irk] + _numCalls_nv[oa]; + gclog_or_tty->print_cr("\nOf %d oop_oop_iterate calls %d (%6.3f%%) are in (ik, irk, oa).", + _numCallsAll, all_numCallsTotal, + 100.0 * (float)all_numCallsTotal / (float)_numCallsAll); + // irk calls are double-counted. + int real_ik_numCallsTotal = _numCallsTotal[ik] - _numCallsTotal[irk]; + int real_ik_numCalls_nv = _numCalls_nv[ik] - _numCalls_nv[irk]; + gclog_or_tty->print_cr(""); + gclog_or_tty->print_cr(header_format, "oop_oop_iterate:", "calls", "non-virtual", "pct"); + gclog_or_tty->print_cr(header_format, + "----------", + "----------", + "-----------", + "----------"); + gclog_or_tty->print_cr(line_format, "all", + all_numCallsTotal, + all_numCalls_nv, + 100.0 * (float)all_numCalls_nv / (float)all_numCallsTotal); + gclog_or_tty->print_cr(line_format, "ik", + real_ik_numCallsTotal, real_ik_numCalls_nv, + 100.0 * (float)real_ik_numCalls_nv / + (float)real_ik_numCallsTotal); + gclog_or_tty->print_cr(line_format, "irk", + _numCallsTotal[irk], _numCalls_nv[irk], + 100.0 * (float)_numCalls_nv[irk] / (float)_numCallsTotal[irk]); + gclog_or_tty->print_cr(line_format, "oa", + _numCallsTotal[oa], _numCalls_nv[oa], + 100.0 * (float)_numCalls_nv[oa] / (float)_numCallsTotal[oa]); + + + gclog_or_tty->print_cr(""); + gclog_or_tty->print_cr(header_format, "do_oop:", "calls", "non-virtual", "pct"); + gclog_or_tty->print_cr(header_format, + "----------", + "----------", + "-----------", + "----------"); + int all_numDoOopCallsTotal = + _numDoOopCallsTotal[ik] + _numDoOopCallsTotal[irk] + _numDoOopCallsTotal[oa]; + int all_numDoOopCalls_nv = + _numDoOopCalls_nv[ik] + _numDoOopCalls_nv[irk] + _numDoOopCalls_nv[oa]; + gclog_or_tty->print_cr(line_format, "all", + all_numDoOopCallsTotal, all_numDoOopCalls_nv, + 100.0 * (float)all_numDoOopCalls_nv / + (float)all_numDoOopCallsTotal); + const char* kind_names[] = { "ik", "irk", "oa" }; + for (int k = ik; k < NUM_Kinds; k++) { + gclog_or_tty->print_cr(line_format, kind_names[k], + _numDoOopCallsTotal[k], _numDoOopCalls_nv[k], + (_numDoOopCallsTotal[k] > 0 ? + 100.0 * (float)_numDoOopCalls_nv[k] / + (float)_numDoOopCallsTotal[k] + : 0.0)); + } +} + +#endif // ENABLE_SPECIALIZATION_STATS +#endif // !PRODUCT diff --git a/hotspot/src/share/vm/memory/specialized_oop_closures.hpp b/hotspot/src/share/vm/memory/specialized_oop_closures.hpp new file mode 100644 index 00000000000..9a3e774e74d --- /dev/null +++ b/hotspot/src/share/vm/memory/specialized_oop_closures.hpp @@ -0,0 +1,235 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The following OopClosure types get specialized versions of +// "oop_oop_iterate" that invoke the closures' do_oop methods +// non-virtually, using a mechanism defined in this file. Extend these +// macros in the obvious way to add specializations for new closures. + +// Forward declarations. +class OopClosure; +class OopsInGenClosure; +// DefNew +class ScanClosure; +class FastScanClosure; +class FilteringClosure; +// ParNew +class ParScanWithBarrierClosure; +class ParScanWithoutBarrierClosure; +// CMS +class MarkRefsIntoAndScanClosure; +class Par_MarkRefsIntoAndScanClosure; +class PushAndMarkClosure; +class Par_PushAndMarkClosure; +class PushOrMarkClosure; +class Par_PushOrMarkClosure; +class CMSKeepAliveClosure; +class CMSInnerParMarkAndPushClosure; + +// This macro applies an argument macro to all OopClosures for which we +// want specialized bodies of "oop_oop_iterate". The arguments to "f" are: +// "f(closureType, non_virtual)" +// where "closureType" is the name of the particular subclass of OopClosure, +// and "non_virtual" will be the string "_nv" if the closure type should +// have its "do_oop" method invoked non-virtually, or else the +// string "_v". ("OopClosure" itself will be the only class in the latter +// category.) + +// This is split into several because of a Visual C++ 6.0 compiler bug +// where very long macros cause the compiler to crash + +#define SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_S(f) \ + f(ScanClosure,_nv) \ + f(FastScanClosure,_nv) \ + f(FilteringClosure,_nv) + +#ifndef SERIALGC +#define SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_P(f) \ + f(ParScanWithBarrierClosure,_nv) \ + f(ParScanWithoutBarrierClosure,_nv) +#else // SERIALGC +#define SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_P(f) +#endif // SERIALGC + +#define SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_1(f) \ + SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_S(f) \ + SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_P(f) + +#ifndef SERIALGC +#define SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_3(f) \ + f(MarkRefsIntoAndScanClosure,_nv) \ + f(Par_MarkRefsIntoAndScanClosure,_nv) \ + f(PushAndMarkClosure,_nv) \ + f(Par_PushAndMarkClosure,_nv) \ + f(PushOrMarkClosure,_nv) \ + f(Par_PushOrMarkClosure,_nv) \ + f(CMSKeepAliveClosure,_nv) \ + f(CMSInnerParMarkAndPushClosure,_nv) +#else // SERIALGC +#define SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_3(f) +#endif // SERIALGC + +// We separate these out, because sometime the general one has +// a different definition from the specialized ones, and sometimes it +// doesn't. + +#define ALL_OOP_OOP_ITERATE_CLOSURES_1(f) \ + f(OopClosure,_v) \ + SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_1(f) + +#define ALL_OOP_OOP_ITERATE_CLOSURES_3(f) \ + SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_3(f) + +#ifndef SERIALGC +// This macro applies an argument macro to all OopClosures for which we +// want specialized bodies of a family of methods related to +// "par_oop_iterate". The arguments to f are the same as above. +// The "root_class" is the most general class to define; this may be +// "OopClosure" in some applications and "OopsInGenClosure" in others. + +#define SPECIALIZED_PAR_OOP_ITERATE_CLOSURES(f) \ + f(MarkRefsIntoAndScanClosure,_nv) \ + f(PushAndMarkClosure,_nv) \ + f(Par_MarkRefsIntoAndScanClosure,_nv) \ + f(Par_PushAndMarkClosure,_nv) + +#define ALL_PAR_OOP_ITERATE_CLOSURES(f) \ + f(OopClosure,_v) \ + SPECIALIZED_PAR_OOP_ITERATE_CLOSURES(f) +#endif // SERIALGC + +// This macro applies an argument macro to all OopClosures for which we +// want specialized bodies of a family of methods related to +// "oops_since_save_marks_do". The arguments to f are the same as above. +// The "root_class" is the most general class to define; this may be +// "OopClosure" in some applications and "OopsInGenClosure" in others. + +#define SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG_S(f) \ + f(ScanClosure,_nv) \ + f(FastScanClosure,_nv) + +#ifndef SERIALGC +#define SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG_P(f) \ + f(ParScanWithBarrierClosure,_nv) \ + f(ParScanWithoutBarrierClosure,_nv) +#else // SERIALGC +#define SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG_P(f) +#endif // SERIALGC + +#define SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG(f) \ + SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG_S(f) \ + SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG_P(f) + +#define SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES(f) \ + SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG(f) + +// We separate these out, because sometime the general one has +// a different definition from the specialized ones, and sometimes it +// doesn't. +// NOTE: One of the valid criticisms of this +// specialize-oop_oop_iterate-for-specific-closures idiom is that it is +// easy to have a silent performance bug: if you fail to de-virtualize, +// things still work, just slower. The "SpecializationStats" mode is +// intended to at least make such a failure easy to detect. +// *Not* using the ALL_SINCE_SAVE_MARKS_CLOSURES(f) macro defined +// below means that *only* closures for which oop_oop_iterate specializations +// exist above may be applied to "oops_since_save_marks". That is, +// this form of the performance bug is caught statically. When you add +// a definition for the general type, this property goes away. +// Make sure you test with SpecializationStats to find such bugs +// when introducing a new closure where you don't want virtual dispatch. + +#define ALL_SINCE_SAVE_MARKS_CLOSURES(f) \ + f(OopsInGenClosure,_v) \ + SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES(f) + +// For keeping stats on effectiveness. +#define ENABLE_SPECIALIZATION_STATS 0 + + +class SpecializationStats { +public: + enum Kind { + ik, // instanceKlass + irk, // instanceRefKlass + oa, // objArrayKlass + NUM_Kinds + }; + +#if ENABLE_SPECIALIZATION_STATS +private: + static int _numCallsAll; + + static int _numCallsTotal[NUM_Kinds]; + static int _numCalls_nv[NUM_Kinds]; + + static int _numDoOopCallsTotal[NUM_Kinds]; + static int _numDoOopCalls_nv[NUM_Kinds]; +public: +#endif + static void clear() PRODUCT_RETURN; + + static inline void record_call() PRODUCT_RETURN; + static inline void record_iterate_call_v(Kind k) PRODUCT_RETURN; + static inline void record_iterate_call_nv(Kind k) PRODUCT_RETURN; + static inline void record_do_oop_call_v(Kind k) PRODUCT_RETURN; + static inline void record_do_oop_call_nv(Kind k) PRODUCT_RETURN; + + static void print() PRODUCT_RETURN; +}; + +#ifndef PRODUCT +#if ENABLE_SPECIALIZATION_STATS + +inline void SpecializationStats::record_call() { + _numCallsAll++;; +} +inline void SpecializationStats::record_iterate_call_v(Kind k) { + _numCallsTotal[k]++; +} +inline void SpecializationStats::record_iterate_call_nv(Kind k) { + _numCallsTotal[k]++; + _numCalls_nv[k]++; +} + +inline void SpecializationStats::record_do_oop_call_v(Kind k) { + _numDoOopCallsTotal[k]++; +} +inline void SpecializationStats::record_do_oop_call_nv(Kind k) { + _numDoOopCallsTotal[k]++; + _numDoOopCalls_nv[k]++; +} + +#else // !ENABLE_SPECIALIZATION_STATS + +inline void SpecializationStats::record_call() {} +inline void SpecializationStats::record_iterate_call_v(Kind k) {} +inline void SpecializationStats::record_iterate_call_nv(Kind k) {} +inline void SpecializationStats::record_do_oop_call_v(Kind k) {} +inline void SpecializationStats::record_do_oop_call_nv(Kind k) {} +inline void SpecializationStats::clear() {} +inline void SpecializationStats::print() {} + +#endif // ENABLE_SPECIALIZATION_STATS +#endif // !PRODUCT diff --git a/hotspot/src/share/vm/memory/tenuredGeneration.cpp b/hotspot/src/share/vm/memory/tenuredGeneration.cpp new file mode 100644 index 00000000000..8c46169b7fe --- /dev/null +++ b/hotspot/src/share/vm/memory/tenuredGeneration.cpp @@ -0,0 +1,446 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_tenuredGeneration.cpp.incl" + +TenuredGeneration::TenuredGeneration(ReservedSpace rs, + size_t initial_byte_size, int level, + GenRemSet* remset) : + OneContigSpaceCardGeneration(rs, initial_byte_size, + MinHeapDeltaBytes, level, remset, NULL) +{ + HeapWord* bottom = (HeapWord*) _virtual_space.low(); + HeapWord* end = (HeapWord*) _virtual_space.high(); + _the_space = new TenuredSpace(_bts, MemRegion(bottom, end)); + _the_space->reset_saved_mark(); + _shrink_factor = 0; + _capacity_at_prologue = 0; + + _gc_stats = new GCStats(); + + // initialize performance counters + + const char* gen_name = "old"; + + // Generation Counters -- generation 1, 1 subspace + _gen_counters = new GenerationCounters(gen_name, 1, 1, &_virtual_space); + + _gc_counters = new CollectorCounters("MSC", 1); + + _space_counters = new CSpaceCounters(gen_name, 0, + _virtual_space.reserved_size(), + _the_space, _gen_counters); +#ifndef SERIALGC + if (UseParNewGC && ParallelGCThreads > 0) { + typedef ParGCAllocBufferWithBOT* ParGCAllocBufferWithBOTPtr; + _alloc_buffers = NEW_C_HEAP_ARRAY(ParGCAllocBufferWithBOTPtr, + ParallelGCThreads); + if (_alloc_buffers == NULL) + vm_exit_during_initialization("Could not allocate alloc_buffers"); + for (uint i = 0; i < ParallelGCThreads; i++) { + _alloc_buffers[i] = + new ParGCAllocBufferWithBOT(OldPLABSize, _bts); + if (_alloc_buffers[i] == NULL) + vm_exit_during_initialization("Could not allocate alloc_buffers"); + } + } else { + _alloc_buffers = NULL; + } +#endif // SERIALGC +} + + +const char* TenuredGeneration::name() const { + return "tenured generation"; +} + +void TenuredGeneration::compute_new_size() { + assert(_shrink_factor <= 100, "invalid shrink factor"); + size_t current_shrink_factor = _shrink_factor; + _shrink_factor = 0; + + // We don't have floating point command-line arguments + // Note: argument processing ensures that MinHeapFreeRatio < 100. + const double minimum_free_percentage = MinHeapFreeRatio / 100.0; + const double maximum_used_percentage = 1.0 - minimum_free_percentage; + + // Compute some numbers about the state of the heap. + const size_t used_after_gc = used(); + const size_t capacity_after_gc = capacity(); + + const double min_tmp = used_after_gc / maximum_used_percentage; + size_t minimum_desired_capacity = (size_t)MIN2(min_tmp, double(max_uintx)); + // Don't shrink less than the initial generation size + minimum_desired_capacity = MAX2(minimum_desired_capacity, + spec()->init_size()); + assert(used_after_gc <= minimum_desired_capacity, "sanity check"); + + if (PrintGC && Verbose) { + const size_t free_after_gc = free(); + const double free_percentage = ((double)free_after_gc) / capacity_after_gc; + gclog_or_tty->print_cr("TenuredGeneration::compute_new_size: "); + gclog_or_tty->print_cr(" " + " minimum_free_percentage: %6.2f" + " maximum_used_percentage: %6.2f", + minimum_free_percentage, + maximum_used_percentage); + gclog_or_tty->print_cr(" " + " free_after_gc : %6.1fK" + " used_after_gc : %6.1fK" + " capacity_after_gc : %6.1fK", + free_after_gc / (double) K, + used_after_gc / (double) K, + capacity_after_gc / (double) K); + gclog_or_tty->print_cr(" " + " free_percentage: %6.2f", + free_percentage); + } + + if (capacity_after_gc < minimum_desired_capacity) { + // If we have less free space than we want then expand + size_t expand_bytes = minimum_desired_capacity - capacity_after_gc; + // Don't expand unless it's significant + if (expand_bytes >= _min_heap_delta_bytes) { + expand(expand_bytes, 0); // safe if expansion fails + } + if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" expanding:" + " minimum_desired_capacity: %6.1fK" + " expand_bytes: %6.1fK" + " _min_heap_delta_bytes: %6.1fK", + minimum_desired_capacity / (double) K, + expand_bytes / (double) K, + _min_heap_delta_bytes / (double) K); + } + return; + } + + // No expansion, now see if we want to shrink + size_t shrink_bytes = 0; + // We would never want to shrink more than this + size_t max_shrink_bytes = capacity_after_gc - minimum_desired_capacity; + + if (MaxHeapFreeRatio < 100) { + const double maximum_free_percentage = MaxHeapFreeRatio / 100.0; + const double minimum_used_percentage = 1.0 - maximum_free_percentage; + const double max_tmp = used_after_gc / minimum_used_percentage; + size_t maximum_desired_capacity = (size_t)MIN2(max_tmp, double(max_uintx)); + maximum_desired_capacity = MAX2(maximum_desired_capacity, + spec()->init_size()); + if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" " + " maximum_free_percentage: %6.2f" + " minimum_used_percentage: %6.2f", + maximum_free_percentage, + minimum_used_percentage); + gclog_or_tty->print_cr(" " + " _capacity_at_prologue: %6.1fK" + " minimum_desired_capacity: %6.1fK" + " maximum_desired_capacity: %6.1fK", + _capacity_at_prologue / (double) K, + minimum_desired_capacity / (double) K, + maximum_desired_capacity / (double) K); + } + assert(minimum_desired_capacity <= maximum_desired_capacity, + "sanity check"); + + if (capacity_after_gc > maximum_desired_capacity) { + // Capacity too large, compute shrinking size + shrink_bytes = capacity_after_gc - maximum_desired_capacity; + // We don't want shrink all the way back to initSize if people call + // System.gc(), because some programs do that between "phases" and then + // we'd just have to grow the heap up again for the next phase. So we + // damp the shrinking: 0% on the first call, 10% on the second call, 40% + // on the third call, and 100% by the fourth call. But if we recompute + // size without shrinking, it goes back to 0%. + shrink_bytes = shrink_bytes / 100 * current_shrink_factor; + assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size"); + if (current_shrink_factor == 0) { + _shrink_factor = 10; + } else { + _shrink_factor = MIN2(current_shrink_factor * 4, (size_t) 100); + } + if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" " + " shrinking:" + " initSize: %.1fK" + " maximum_desired_capacity: %.1fK", + spec()->init_size() / (double) K, + maximum_desired_capacity / (double) K); + gclog_or_tty->print_cr(" " + " shrink_bytes: %.1fK" + " current_shrink_factor: %d" + " new shrink factor: %d" + " _min_heap_delta_bytes: %.1fK", + shrink_bytes / (double) K, + current_shrink_factor, + _shrink_factor, + _min_heap_delta_bytes / (double) K); + } + } + } + + if (capacity_after_gc > _capacity_at_prologue) { + // We might have expanded for promotions, in which case we might want to + // take back that expansion if there's room after GC. That keeps us from + // stretching the heap with promotions when there's plenty of room. + size_t expansion_for_promotion = capacity_after_gc - _capacity_at_prologue; + expansion_for_promotion = MIN2(expansion_for_promotion, max_shrink_bytes); + // We have two shrinking computations, take the largest + shrink_bytes = MAX2(shrink_bytes, expansion_for_promotion); + assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size"); + if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" " + " aggressive shrinking:" + " _capacity_at_prologue: %.1fK" + " capacity_after_gc: %.1fK" + " expansion_for_promotion: %.1fK" + " shrink_bytes: %.1fK", + capacity_after_gc / (double) K, + _capacity_at_prologue / (double) K, + expansion_for_promotion / (double) K, + shrink_bytes / (double) K); + } + } + // Don't shrink unless it's significant + if (shrink_bytes >= _min_heap_delta_bytes) { + shrink(shrink_bytes); + } + assert(used() == used_after_gc && used_after_gc <= capacity(), + "sanity check"); +} + +void TenuredGeneration::gc_prologue(bool full) { + _capacity_at_prologue = capacity(); + _used_at_prologue = used(); + if (VerifyBeforeGC) { + verify_alloc_buffers_clean(); + } +} + +void TenuredGeneration::gc_epilogue(bool full) { + if (VerifyAfterGC) { + verify_alloc_buffers_clean(); + } + OneContigSpaceCardGeneration::gc_epilogue(full); +} + + +bool TenuredGeneration::should_collect(bool full, + size_t size, + bool is_tlab) { + // This should be one big conditional or (||), but I want to be able to tell + // why it returns what it returns (without re-evaluating the conditionals + // in case they aren't idempotent), so I'm doing it this way. + // DeMorgan says it's okay. + bool result = false; + if (!result && full) { + result = true; + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("TenuredGeneration::should_collect: because" + " full"); + } + } + if (!result && should_allocate(size, is_tlab)) { + result = true; + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("TenuredGeneration::should_collect: because" + " should_allocate(" SIZE_FORMAT ")", + size); + } + } + // If we don't have very much free space. + // XXX: 10000 should be a percentage of the capacity!!! + if (!result && free() < 10000) { + result = true; + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("TenuredGeneration::should_collect: because" + " free(): " SIZE_FORMAT, + free()); + } + } + // If we had to expand to accomodate promotions from younger generations + if (!result && _capacity_at_prologue < capacity()) { + result = true; + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("TenuredGeneration::should_collect: because" + "_capacity_at_prologue: " SIZE_FORMAT " < capacity(): " SIZE_FORMAT, + _capacity_at_prologue, capacity()); + } + } + return result; +} + +void TenuredGeneration::collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool is_tlab) { + retire_alloc_buffers_before_full_gc(); + OneContigSpaceCardGeneration::collect(full, clear_all_soft_refs, + size, is_tlab); +} + +void TenuredGeneration::update_gc_stats(int current_level, + bool full) { + // If the next lower level(s) has been collected, gather any statistics + // that are of interest at this point. + if (!full && (current_level + 1) == level()) { + // Calculate size of data promoted from the younger generations + // before doing the collection. + size_t used_before_gc = used(); + + // If the younger gen collections were skipped, then the + // number of promoted bytes will be 0 and adding it to the + // average will incorrectly lessen the average. It is, however, + // also possible that no promotion was needed. + if (used_before_gc >= _used_at_prologue) { + size_t promoted_in_bytes = used_before_gc - _used_at_prologue; + gc_stats()->avg_promoted()->sample(promoted_in_bytes); + } + } +} + +void TenuredGeneration::update_counters() { + if (UsePerfData) { + _space_counters->update_all(); + _gen_counters->update_all(); + } +} + + +#ifndef SERIALGC +oop TenuredGeneration::par_promote(int thread_num, + oop old, markOop m, size_t word_sz) { + + ParGCAllocBufferWithBOT* buf = _alloc_buffers[thread_num]; + HeapWord* obj_ptr = buf->allocate(word_sz); + bool is_lab = true; + if (obj_ptr == NULL) { +#ifndef PRODUCT + if (Universe::heap()->promotion_should_fail()) { + return NULL; + } +#endif // #ifndef PRODUCT + + // Slow path: + if (word_sz * 100 < ParallelGCBufferWastePct * buf->word_sz()) { + // Is small enough; abandon this buffer and start a new one. + size_t buf_size = buf->word_sz(); + HeapWord* buf_space = + TenuredGeneration::par_allocate(buf_size, false); + if (buf_space == NULL) { + buf_space = expand_and_allocate(buf_size, false, true /* parallel*/); + } + if (buf_space != NULL) { + buf->retire(false, false); + buf->set_buf(buf_space); + obj_ptr = buf->allocate(word_sz); + assert(obj_ptr != NULL, "Buffer was definitely big enough..."); + } + }; + // Otherwise, buffer allocation failed; try allocating object + // individually. + if (obj_ptr == NULL) { + obj_ptr = TenuredGeneration::par_allocate(word_sz, false); + if (obj_ptr == NULL) { + obj_ptr = expand_and_allocate(word_sz, false, true /* parallel */); + } + } + if (obj_ptr == NULL) return NULL; + } + assert(obj_ptr != NULL, "program logic"); + Copy::aligned_disjoint_words((HeapWord*)old, obj_ptr, word_sz); + oop obj = oop(obj_ptr); + // Restore the mark word copied above. + obj->set_mark(m); + return obj; +} + +void TenuredGeneration::par_promote_alloc_undo(int thread_num, + HeapWord* obj, + size_t word_sz) { + ParGCAllocBufferWithBOT* buf = _alloc_buffers[thread_num]; + if (buf->contains(obj)) { + guarantee(buf->contains(obj + word_sz - 1), + "should contain whole object"); + buf->undo_allocation(obj, word_sz); + } else { + SharedHeap::fill_region_with_object(MemRegion(obj, word_sz)); + } +} + +void TenuredGeneration::par_promote_alloc_done(int thread_num) { + ParGCAllocBufferWithBOT* buf = _alloc_buffers[thread_num]; + buf->retire(true, ParallelGCRetainPLAB); +} + +void TenuredGeneration::retire_alloc_buffers_before_full_gc() { + if (UseParNewGC) { + for (uint i = 0; i < ParallelGCThreads; i++) { + _alloc_buffers[i]->retire(true /*end_of_gc*/, false /*retain*/); + } + } +} + +// Verify that any retained parallel allocation buffers do not +// intersect with dirty cards. +void TenuredGeneration::verify_alloc_buffers_clean() { + if (UseParNewGC) { + for (uint i = 0; i < ParallelGCThreads; i++) { + _rs->verify_empty(_alloc_buffers[i]->range()); + } + } +} +#else // SERIALGC +void TenuredGeneration::retire_alloc_buffers_before_full_gc() {} +void TenuredGeneration::verify_alloc_buffers_clean() {} +#endif // SERIALGC + +bool TenuredGeneration::promotion_attempt_is_safe( + size_t max_promotion_in_bytes, + bool younger_handles_promotion_failure) const { + + bool result = max_contiguous_available() >= max_promotion_in_bytes; + + if (younger_handles_promotion_failure && !result) { + result = max_contiguous_available() >= + (size_t) gc_stats()->avg_promoted()->padded_average(); + if (PrintGC && Verbose && result) { + gclog_or_tty->print_cr("TenuredGeneration::promotion_attempt_is_safe" + " contiguous_available: " SIZE_FORMAT + " avg_promoted: " SIZE_FORMAT, + max_contiguous_available(), + gc_stats()->avg_promoted()->padded_average()); + } + } else { + if (PrintGC && Verbose) { + gclog_or_tty->print_cr("TenuredGeneration::promotion_attempt_is_safe" + " contiguous_available: " SIZE_FORMAT + " promotion_in_bytes: " SIZE_FORMAT, + max_contiguous_available(), max_promotion_in_bytes); + } + } + return result; +} diff --git a/hotspot/src/share/vm/memory/tenuredGeneration.hpp b/hotspot/src/share/vm/memory/tenuredGeneration.hpp new file mode 100644 index 00000000000..18aa8b37fb3 --- /dev/null +++ b/hotspot/src/share/vm/memory/tenuredGeneration.hpp @@ -0,0 +1,109 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// TenuredGeneration models the heap containing old (promoted/tenured) objects. + +class ParGCAllocBufferWithBOT; + +class TenuredGeneration: public OneContigSpaceCardGeneration { + friend class VMStructs; + protected: + // current shrinking effect: this damps shrinking when the heap gets empty. + size_t _shrink_factor; + // Some statistics from before gc started. + // These are gathered in the gc_prologue (and should_collect) + // to control growing/shrinking policy in spite of promotions. + size_t _capacity_at_prologue; + size_t _used_at_prologue; + +#ifndef SERIALGC + // To support parallel promotion: an array of parallel allocation + // buffers, one per thread, initially NULL. + ParGCAllocBufferWithBOT** _alloc_buffers; +#endif // SERIALGC + + // Retire all alloc buffers before a full GC, so that they will be + // re-allocated at the start of the next young GC. + void retire_alloc_buffers_before_full_gc(); + + GenerationCounters* _gen_counters; + CSpaceCounters* _space_counters; + + public: + TenuredGeneration(ReservedSpace rs, size_t initial_byte_size, int level, + GenRemSet* remset); + + Generation::Name kind() { return Generation::MarkSweepCompact; } + + // Printing + const char* name() const; + const char* short_name() const { return "Tenured"; } + bool must_be_youngest() const { return false; } + bool must_be_oldest() const { return true; } + + // Does a "full" (forced) collection invoked on this generation collect + // all younger generations as well? Note that this is a + // hack to allow the collection of the younger gen first if the flag is + // set. This is better than using th policy's should_collect_gen0_first() + // since that causes us to do an extra unnecessary pair of restart-&-stop-world. + virtual bool full_collects_younger_generations() const { + return !CollectGen0First; + } + + // Mark sweep support + void compute_new_size(); + int allowed_dead_ratio() const; + + virtual void gc_prologue(bool full); + virtual void gc_epilogue(bool full); + bool should_collect(bool full, + size_t word_size, + bool is_tlab); + + virtual void collect(bool full, + bool clear_all_soft_refs, + size_t size, + bool is_tlab); + +#ifndef SERIALGC + // Overrides. + virtual oop par_promote(int thread_num, + oop obj, markOop m, size_t word_sz); + virtual void par_promote_alloc_undo(int thread_num, + HeapWord* obj, size_t word_sz); + virtual void par_promote_alloc_done(int thread_num); +#endif // SERIALGC + + // Performance Counter support + void update_counters(); + + // Statistics + + virtual void update_gc_stats(int level, bool full); + + virtual bool promotion_attempt_is_safe(size_t max_promoted_in_bytes, + bool younger_handles_promotion_failure) const; + + void verify_alloc_buffers_clean(); +}; diff --git a/hotspot/src/share/vm/memory/threadLocalAllocBuffer.cpp b/hotspot/src/share/vm/memory/threadLocalAllocBuffer.cpp new file mode 100644 index 00000000000..fdcfdd28e13 --- /dev/null +++ b/hotspot/src/share/vm/memory/threadLocalAllocBuffer.cpp @@ -0,0 +1,416 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Thread-Local Edens support + +# include "incls/_precompiled.incl" +# include "incls/_threadLocalAllocBuffer.cpp.incl" + +// static member initialization +unsigned ThreadLocalAllocBuffer::_target_refills = 0; +GlobalTLABStats* ThreadLocalAllocBuffer::_global_stats = NULL; + +void ThreadLocalAllocBuffer::clear_before_allocation() { + _slow_refill_waste += (unsigned)remaining(); + make_parsable(true); // also retire the TLAB +} + +void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() { + global_stats()->initialize(); + + for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + thread->tlab().accumulate_statistics(); + thread->tlab().initialize_statistics(); + } + + // Publish new stats if some allocation occurred. + if (global_stats()->allocation() != 0) { + global_stats()->publish(); + if (PrintTLAB) { + global_stats()->print(); + } + } +} + +void ThreadLocalAllocBuffer::accumulate_statistics() { + size_t capacity = Universe::heap()->tlab_capacity(myThread()) / HeapWordSize; + size_t unused = Universe::heap()->unsafe_max_tlab_alloc(myThread()) / HeapWordSize; + size_t used = capacity - unused; + + // Update allocation history if a reasonable amount of eden was allocated. + bool update_allocation_history = used > 0.5 * capacity; + + _gc_waste += (unsigned)remaining(); + + if (PrintTLAB && (_number_of_refills > 0 || Verbose)) { + print_stats("gc"); + } + + if (_number_of_refills > 0) { + + if (update_allocation_history) { + // Average the fraction of eden allocated in a tlab by this + // thread for use in the next resize operation. + // _gc_waste is not subtracted because it's included in + // "used". + size_t allocation = _number_of_refills * desired_size(); + double alloc_frac = allocation / (double) used; + _allocation_fraction.sample(alloc_frac); + } + global_stats()->update_allocating_threads(); + global_stats()->update_number_of_refills(_number_of_refills); + global_stats()->update_allocation(_number_of_refills * desired_size()); + global_stats()->update_gc_waste(_gc_waste); + global_stats()->update_slow_refill_waste(_slow_refill_waste); + global_stats()->update_fast_refill_waste(_fast_refill_waste); + + } else { + assert(_number_of_refills == 0 && _fast_refill_waste == 0 && + _slow_refill_waste == 0 && _gc_waste == 0, + "tlab stats == 0"); + } + global_stats()->update_slow_allocations(_slow_allocations); +} + +// Fills the current tlab with a dummy filler array to create +// an illusion of a contiguous Eden and optionally retires the tlab. +// Waste accounting should be done in caller as appropriate; see, +// for example, clear_before_allocation(). +void ThreadLocalAllocBuffer::make_parsable(bool retire) { + if (end() != NULL) { + invariants(); + MemRegion mr(top(), hard_end()); + SharedHeap::fill_region_with_object(mr); + + if (retire || ZeroTLAB) { // "Reset" the TLAB + set_start(NULL); + set_top(NULL); + set_pf_top(NULL); + set_end(NULL); + } + } + assert(!(retire || ZeroTLAB) || + (start() == NULL && end() == NULL && top() == NULL), + "TLAB must be reset"); +} + +void ThreadLocalAllocBuffer::resize_all_tlabs() { + for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + thread->tlab().resize(); + } +} + +void ThreadLocalAllocBuffer::resize() { + + if (ResizeTLAB) { + // Compute the next tlab size using expected allocation amount + size_t alloc = (size_t)(_allocation_fraction.average() * + (Universe::heap()->tlab_capacity(myThread()) / HeapWordSize)); + size_t new_size = alloc / _target_refills; + + new_size = MIN2(MAX2(new_size, min_size()), max_size()); + + size_t aligned_new_size = align_object_size(new_size); + + if (PrintTLAB && Verbose) { + gclog_or_tty->print("TLAB new size: thread: " INTPTR_FORMAT " [id: %2d]" + " refills %d alloc: %8.6f desired_size: " SIZE_FORMAT " -> " SIZE_FORMAT "\n", + myThread(), myThread()->osthread()->thread_id(), + _target_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); + } + set_desired_size(aligned_new_size); + + set_refill_waste_limit(initial_refill_waste_limit()); + } +} + +void ThreadLocalAllocBuffer::initialize_statistics() { + _number_of_refills = 0; + _fast_refill_waste = 0; + _slow_refill_waste = 0; + _gc_waste = 0; + _slow_allocations = 0; +} + +void ThreadLocalAllocBuffer::fill(HeapWord* start, + HeapWord* top, + size_t new_size) { + _number_of_refills++; + if (PrintTLAB && Verbose) { + print_stats("fill"); + } + assert(top <= start + new_size - alignment_reserve(), "size too small"); + initialize(start, top, start + new_size - alignment_reserve()); + + // Reset amount of internal fragmentation + set_refill_waste_limit(initial_refill_waste_limit()); +} + +void ThreadLocalAllocBuffer::initialize(HeapWord* start, + HeapWord* top, + HeapWord* end) { + set_start(start); + set_top(top); + set_pf_top(top); + set_end(end); + invariants(); +} + +void ThreadLocalAllocBuffer::initialize() { + initialize(NULL, // start + NULL, // top + NULL); // end + + set_desired_size(initial_desired_size()); + + // Following check is needed because at startup the main (primordial) + // thread is initialized before the heap is. The initialization for + // this thread is redone in startup_initialization below. + if (Universe::heap() != NULL) { + size_t capacity = Universe::heap()->tlab_capacity(myThread()) / HeapWordSize; + double alloc_frac = desired_size() * target_refills() / (double) capacity; + _allocation_fraction.sample(alloc_frac); + } + + set_refill_waste_limit(initial_refill_waste_limit()); + + initialize_statistics(); +} + +void ThreadLocalAllocBuffer::startup_initialization() { + + // Assuming each thread's active tlab is, on average, + // 1/2 full at a GC + _target_refills = 100 / (2 * TLABWasteTargetPercent); + _target_refills = MAX2(_target_refills, (unsigned)1U); + + _global_stats = new GlobalTLABStats(); + + // During jvm startup, the main (primordial) thread is initialized + // before the heap is initialized. So reinitialize it now. + guarantee(Thread::current()->is_Java_thread(), "tlab initialization thread not Java thread"); + Thread::current()->tlab().initialize(); + + if (PrintTLAB && Verbose) { + gclog_or_tty->print("TLAB min: " SIZE_FORMAT " initial: " SIZE_FORMAT " max: " SIZE_FORMAT "\n", + min_size(), Thread::current()->tlab().initial_desired_size(), max_size()); + } +} + +size_t ThreadLocalAllocBuffer::initial_desired_size() { + size_t init_sz; + + if (TLABSize > 0) { + init_sz = MIN2(TLABSize / HeapWordSize, max_size()); + } else if (global_stats() == NULL) { + // Startup issue - main thread initialized before heap initialized. + init_sz = min_size(); + } else { + // Initial size is a function of the average number of allocating threads. + unsigned nof_threads = global_stats()->allocating_threads_avg(); + + init_sz = (Universe::heap()->tlab_capacity(myThread()) / HeapWordSize) / + (nof_threads * target_refills()); + init_sz = align_object_size(init_sz); + init_sz = MIN2(MAX2(init_sz, min_size()), max_size()); + } + return init_sz; +} + +const size_t ThreadLocalAllocBuffer::max_size() { + + // TLABs can't be bigger than we can fill with a int[Integer.MAX_VALUE]. + // This restriction could be removed by enabling filling with multiple arrays. + // If we compute that the reasonable way as + // header_size + ((sizeof(jint) * max_jint) / HeapWordSize) + // we'll overflow on the multiply, so we do the divide first. + // We actually lose a little by dividing first, + // but that just makes the TLAB somewhat smaller than the biggest array, + // which is fine, since we'll be able to fill that. + + size_t unaligned_max_size = typeArrayOopDesc::header_size(T_INT) + + sizeof(jint) * + ((juint) max_jint / (size_t) HeapWordSize); + return align_size_down(unaligned_max_size, MinObjAlignment); +} + +void ThreadLocalAllocBuffer::print_stats(const char* tag) { + Thread* thrd = myThread(); + size_t waste = _gc_waste + _slow_refill_waste + _fast_refill_waste; + size_t alloc = _number_of_refills * _desired_size; + double waste_percent = alloc == 0 ? 0.0 : + 100.0 * waste / alloc; + size_t tlab_used = Universe::heap()->tlab_capacity(thrd) - + Universe::heap()->unsafe_max_tlab_alloc(thrd); + gclog_or_tty->print("TLAB: %s thread: " INTPTR_FORMAT " [id: %2d]" + " desired_size: " SIZE_FORMAT "KB" + " slow allocs: %d refill waste: " SIZE_FORMAT "B" + " alloc:%8.5f %8.0fKB refills: %d waste %4.1f%% gc: %dB" + " slow: %dB fast: %dB\n", + tag, thrd, thrd->osthread()->thread_id(), + _desired_size / (K / HeapWordSize), + _slow_allocations, _refill_waste_limit * HeapWordSize, + _allocation_fraction.average(), + _allocation_fraction.average() * tlab_used / K, + _number_of_refills, waste_percent, + _gc_waste * HeapWordSize, + _slow_refill_waste * HeapWordSize, + _fast_refill_waste * HeapWordSize); +} + +void ThreadLocalAllocBuffer::verify() { + HeapWord* p = start(); + HeapWord* t = top(); + HeapWord* prev_p = NULL; + while (p < t) { + oop(p)->verify(); + prev_p = p; + p += oop(p)->size(); + } + guarantee(p == top(), "end of last object must match end of space"); +} + +Thread* ThreadLocalAllocBuffer::myThread() { + return (Thread*)(((char *)this) + + in_bytes(start_offset()) - + in_bytes(Thread::tlab_start_offset())); +} + + +GlobalTLABStats::GlobalTLABStats() : + _allocating_threads_avg(TLABAllocationWeight) { + + initialize(); + + _allocating_threads_avg.sample(1); // One allocating thread at startup + + if (UsePerfData) { + + EXCEPTION_MARK; + ResourceMark rm; + + char* cname = PerfDataManager::counter_name("tlab", "allocThreads"); + _perf_allocating_threads = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, CHECK); + + cname = PerfDataManager::counter_name("tlab", "fills"); + _perf_total_refills = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, CHECK); + + cname = PerfDataManager::counter_name("tlab", "maxFills"); + _perf_max_refills = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, CHECK); + + cname = PerfDataManager::counter_name("tlab", "alloc"); + _perf_allocation = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); + + cname = PerfDataManager::counter_name("tlab", "gcWaste"); + _perf_gc_waste = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); + + cname = PerfDataManager::counter_name("tlab", "maxGcWaste"); + _perf_max_gc_waste = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); + + cname = PerfDataManager::counter_name("tlab", "slowWaste"); + _perf_slow_refill_waste = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); + + cname = PerfDataManager::counter_name("tlab", "maxSlowWaste"); + _perf_max_slow_refill_waste = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); + + cname = PerfDataManager::counter_name("tlab", "fastWaste"); + _perf_fast_refill_waste = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); + + cname = PerfDataManager::counter_name("tlab", "maxFastWaste"); + _perf_max_fast_refill_waste = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); + + cname = PerfDataManager::counter_name("tlab", "slowAlloc"); + _perf_slow_allocations = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, CHECK); + + cname = PerfDataManager::counter_name("tlab", "maxSlowAlloc"); + _perf_max_slow_allocations = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, CHECK); + } +} + +void GlobalTLABStats::initialize() { + // Clear counters summarizing info from all threads + _allocating_threads = 0; + _total_refills = 0; + _max_refills = 0; + _total_allocation = 0; + _total_gc_waste = 0; + _max_gc_waste = 0; + _total_slow_refill_waste = 0; + _max_slow_refill_waste = 0; + _total_fast_refill_waste = 0; + _max_fast_refill_waste = 0; + _total_slow_allocations = 0; + _max_slow_allocations = 0; +} + +void GlobalTLABStats::publish() { + _allocating_threads_avg.sample(_allocating_threads); + if (UsePerfData) { + _perf_allocating_threads ->set_value(_allocating_threads); + _perf_total_refills ->set_value(_total_refills); + _perf_max_refills ->set_value(_max_refills); + _perf_allocation ->set_value(_total_allocation); + _perf_gc_waste ->set_value(_total_gc_waste); + _perf_max_gc_waste ->set_value(_max_gc_waste); + _perf_slow_refill_waste ->set_value(_total_slow_refill_waste); + _perf_max_slow_refill_waste->set_value(_max_slow_refill_waste); + _perf_fast_refill_waste ->set_value(_total_fast_refill_waste); + _perf_max_fast_refill_waste->set_value(_max_fast_refill_waste); + _perf_slow_allocations ->set_value(_total_slow_allocations); + _perf_max_slow_allocations ->set_value(_max_slow_allocations); + } +} + +void GlobalTLABStats::print() { + size_t waste = _total_gc_waste + _total_slow_refill_waste + _total_fast_refill_waste; + double waste_percent = _total_allocation == 0 ? 0.0 : + 100.0 * waste / _total_allocation; + gclog_or_tty->print("TLAB totals: thrds: %d refills: %d max: %d" + " slow allocs: %d max %d waste: %4.1f%%" + " gc: " SIZE_FORMAT "B max: " SIZE_FORMAT "B" + " slow: " SIZE_FORMAT "B max: " SIZE_FORMAT "B" + " fast: " SIZE_FORMAT "B max: " SIZE_FORMAT "B\n", + _allocating_threads, + _total_refills, _max_refills, + _total_slow_allocations, _max_slow_allocations, + waste_percent, + _total_gc_waste * HeapWordSize, + _max_gc_waste * HeapWordSize, + _total_slow_refill_waste * HeapWordSize, + _max_slow_refill_waste * HeapWordSize, + _total_fast_refill_waste * HeapWordSize, + _max_fast_refill_waste * HeapWordSize); +} diff --git a/hotspot/src/share/vm/memory/threadLocalAllocBuffer.hpp b/hotspot/src/share/vm/memory/threadLocalAllocBuffer.hpp new file mode 100644 index 00000000000..8007cb497a5 --- /dev/null +++ b/hotspot/src/share/vm/memory/threadLocalAllocBuffer.hpp @@ -0,0 +1,242 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class GlobalTLABStats; + +// ThreadLocalAllocBuffer: a descriptor for thread-local storage used by +// the threads for allocation. +// It is thread-private at any time, but maybe multiplexed over +// time across multiple threads. The park()/unpark() pair is +// used to make it avaiable for such multiplexing. +class ThreadLocalAllocBuffer: public CHeapObj { + friend class VMStructs; +private: + HeapWord* _start; // address of TLAB + HeapWord* _top; // address after last allocation + HeapWord* _pf_top; // allocation prefetch watermark + HeapWord* _end; // allocation end (excluding alignment_reserve) + size_t _desired_size; // desired size (including alignment_reserve) + size_t _refill_waste_limit; // hold onto tlab if free() is larger than this + + static unsigned _target_refills; // expected number of refills between GCs + + unsigned _number_of_refills; + unsigned _fast_refill_waste; + unsigned _slow_refill_waste; + unsigned _gc_waste; + unsigned _slow_allocations; + + AdaptiveWeightedAverage _allocation_fraction; // fraction of eden allocated in tlabs + + void accumulate_statistics(); + void initialize_statistics(); + + void set_start(HeapWord* start) { _start = start; } + void set_end(HeapWord* end) { _end = end; } + void set_top(HeapWord* top) { _top = top; } + void set_pf_top(HeapWord* pf_top) { _pf_top = pf_top; } + void set_desired_size(size_t desired_size) { _desired_size = desired_size; } + void set_refill_waste_limit(size_t waste) { _refill_waste_limit = waste; } + + size_t initial_refill_waste_limit() { return desired_size() / TLABRefillWasteFraction; } + + static int target_refills() { return _target_refills; } + size_t initial_desired_size(); + + size_t remaining() const { return end() == NULL ? 0 : pointer_delta(hard_end(), top()); } + + // Make parsable and release it. + void reset(); + + // Resize based on amount of allocation, etc. + void resize(); + + void invariants() const { assert(top() >= start() && top() <= end(), "invalid tlab"); } + + void initialize(HeapWord* start, HeapWord* top, HeapWord* end); + + void print_stats(const char* tag); + + Thread* myThread(); + + // statistics + + int number_of_refills() const { return _number_of_refills; } + int fast_refill_waste() const { return _fast_refill_waste; } + int slow_refill_waste() const { return _slow_refill_waste; } + int gc_waste() const { return _gc_waste; } + int slow_allocations() const { return _slow_allocations; } + + static GlobalTLABStats* _global_stats; + static GlobalTLABStats* global_stats() { return _global_stats; } + +public: + ThreadLocalAllocBuffer() : _allocation_fraction(TLABAllocationWeight) { + // do nothing. tlabs must be inited by initialize() calls + } + + static const size_t min_size() { return align_object_size(MinTLABSize / HeapWordSize); } + static const size_t max_size(); + + HeapWord* start() const { return _start; } + HeapWord* end() const { return _end; } + HeapWord* hard_end() const { return _end + alignment_reserve(); } + HeapWord* top() const { return _top; } + HeapWord* pf_top() const { return _pf_top; } + size_t desired_size() const { return _desired_size; } + size_t free() const { return pointer_delta(end(), top()); } + // Don't discard tlab if remaining space is larger than this. + size_t refill_waste_limit() const { return _refill_waste_limit; } + + // Allocate size HeapWords. The memory is NOT initialized to zero. + inline HeapWord* allocate(size_t size); + static size_t alignment_reserve() { return align_object_size(typeArrayOopDesc::header_size(T_INT)); } + static size_t alignment_reserve_in_bytes() { return alignment_reserve() * HeapWordSize; } + + // Return tlab size or remaining space in eden such that the + // space is large enough to hold obj_size and necessary fill space. + // Otherwise return 0; + inline size_t compute_size(size_t obj_size); + + // Record slow allocation + inline void record_slow_allocation(size_t obj_size); + + // Initialization at startup + static void startup_initialization(); + + // Make an in-use tlab parsable, optionally also retiring it. + void make_parsable(bool retire); + + // Retire in-use tlab before allocation of a new tlab + void clear_before_allocation(); + + // Accumulate statistics across all tlabs before gc + static void accumulate_statistics_before_gc(); + + // Resize tlabs for all threads + static void resize_all_tlabs(); + + void fill(HeapWord* start, HeapWord* top, size_t new_size); + void initialize(); + + static size_t refill_waste_limit_increment() { return TLABWasteIncrement; } + + // Code generation support + static ByteSize start_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _start); } + static ByteSize end_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _end ); } + static ByteSize top_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _top ); } + static ByteSize pf_top_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _pf_top ); } + static ByteSize size_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _desired_size ); } + static ByteSize refill_waste_limit_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _refill_waste_limit ); } + + static ByteSize number_of_refills_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _number_of_refills ); } + static ByteSize fast_refill_waste_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _fast_refill_waste ); } + static ByteSize slow_allocations_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _slow_allocations ); } + + void verify(); +}; + +class GlobalTLABStats: public CHeapObj { +private: + + // Accumulate perfdata in private variables because + // PerfData should be write-only for security reasons + // (see perfData.hpp) + unsigned _allocating_threads; + unsigned _total_refills; + unsigned _max_refills; + size_t _total_allocation; + size_t _total_gc_waste; + size_t _max_gc_waste; + size_t _total_slow_refill_waste; + size_t _max_slow_refill_waste; + size_t _total_fast_refill_waste; + size_t _max_fast_refill_waste; + unsigned _total_slow_allocations; + unsigned _max_slow_allocations; + + PerfVariable* _perf_allocating_threads; + PerfVariable* _perf_total_refills; + PerfVariable* _perf_max_refills; + PerfVariable* _perf_allocation; + PerfVariable* _perf_gc_waste; + PerfVariable* _perf_max_gc_waste; + PerfVariable* _perf_slow_refill_waste; + PerfVariable* _perf_max_slow_refill_waste; + PerfVariable* _perf_fast_refill_waste; + PerfVariable* _perf_max_fast_refill_waste; + PerfVariable* _perf_slow_allocations; + PerfVariable* _perf_max_slow_allocations; + + AdaptiveWeightedAverage _allocating_threads_avg; + +public: + GlobalTLABStats(); + + // Initialize all counters + void initialize(); + + // Write all perf counters to the perf_counters + void publish(); + + void print(); + + // Accessors + unsigned allocating_threads_avg() { + return MAX2((unsigned)(_allocating_threads_avg.average() + 0.5), 1U); + } + + size_t allocation() { + return _total_allocation; + } + + // Update methods + + void update_allocating_threads() { + _allocating_threads++; + } + void update_number_of_refills(unsigned value) { + _total_refills += value; + _max_refills = MAX2(_max_refills, value); + } + void update_allocation(size_t value) { + _total_allocation += value; + } + void update_gc_waste(size_t value) { + _total_gc_waste += value; + _max_gc_waste = MAX2(_max_gc_waste, value); + } + void update_fast_refill_waste(size_t value) { + _total_fast_refill_waste += value; + _max_fast_refill_waste = MAX2(_max_fast_refill_waste, value); + } + void update_slow_refill_waste(size_t value) { + _total_slow_refill_waste += value; + _max_slow_refill_waste = MAX2(_max_slow_refill_waste, value); + } + void update_slow_allocations(unsigned value) { + _total_slow_allocations += value; + _max_slow_allocations = MAX2(_max_slow_allocations, value); + } +}; diff --git a/hotspot/src/share/vm/memory/threadLocalAllocBuffer.inline.hpp b/hotspot/src/share/vm/memory/threadLocalAllocBuffer.inline.hpp new file mode 100644 index 00000000000..68b2d92a1e3 --- /dev/null +++ b/hotspot/src/share/vm/memory/threadLocalAllocBuffer.inline.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline HeapWord* ThreadLocalAllocBuffer::allocate(size_t size) { + invariants(); + HeapWord* obj = top(); + if (pointer_delta(end(), obj) >= size) { + // successful thread-local allocation + + DEBUG_ONLY(Copy::fill_to_words(obj, size, badHeapWordVal)); + // This addition is safe because we know that top is + // at least size below end, so the add can't wrap. + set_top(obj + size); + + invariants(); + return obj; + } + return NULL; +} + +inline size_t ThreadLocalAllocBuffer::compute_size(size_t obj_size) { + const size_t aligned_obj_size = align_object_size(obj_size); + + // Compute the size for the new TLAB. + // The "last" tlab may be smaller to reduce fragmentation. + // unsafe_max_tlab_alloc is just a hint. + const size_t available_size = Universe::heap()->unsafe_max_tlab_alloc(myThread()) / + HeapWordSize; + size_t new_tlab_size = MIN2(available_size, desired_size() + aligned_obj_size); + + // Make sure there's enough room for object and filler int[]. + const size_t obj_plus_filler_size = aligned_obj_size + alignment_reserve(); + if (new_tlab_size < obj_plus_filler_size) { + // If there isn't enough room for the allocation, return failure. + if (PrintTLAB && Verbose) { + gclog_or_tty->print_cr("ThreadLocalAllocBuffer::compute_size(" SIZE_FORMAT ")" + " returns failure", + obj_size); + } + return 0; + } + if (PrintTLAB && Verbose) { + gclog_or_tty->print_cr("ThreadLocalAllocBuffer::compute_size(" SIZE_FORMAT ")" + " returns " SIZE_FORMAT, + obj_size, new_tlab_size); + } + return new_tlab_size; +} + + +void ThreadLocalAllocBuffer::record_slow_allocation(size_t obj_size) { + // Raise size required to bypass TLAB next time. Why? Else there's + // a risk that a thread that repeatedly allocates objects of one + // size will get stuck on this slow path. + + set_refill_waste_limit(refill_waste_limit() + refill_waste_limit_increment()); + + _slow_allocations++; + + if (PrintTLAB && Verbose) { + Thread* thrd = myThread(); + gclog_or_tty->print("TLAB: %s thread: "INTPTR_FORMAT" [id: %2d]" + " obj: "SIZE_FORMAT + " free: "SIZE_FORMAT + " waste: "SIZE_FORMAT"\n", + "slow", thrd, thrd->osthread()->thread_id(), + obj_size, free(), refill_waste_limit()); + } +} diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp new file mode 100644 index 00000000000..bbc3ee4aa15 --- /dev/null +++ b/hotspot/src/share/vm/memory/universe.cpp @@ -0,0 +1,1375 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_universe.cpp.incl" + +// Known objects +klassOop Universe::_boolArrayKlassObj = NULL; +klassOop Universe::_byteArrayKlassObj = NULL; +klassOop Universe::_charArrayKlassObj = NULL; +klassOop Universe::_intArrayKlassObj = NULL; +klassOop Universe::_shortArrayKlassObj = NULL; +klassOop Universe::_longArrayKlassObj = NULL; +klassOop Universe::_singleArrayKlassObj = NULL; +klassOop Universe::_doubleArrayKlassObj = NULL; +klassOop Universe::_typeArrayKlassObjs[T_VOID+1] = { NULL /*, NULL...*/ }; +klassOop Universe::_objectArrayKlassObj = NULL; +klassOop Universe::_symbolKlassObj = NULL; +klassOop Universe::_methodKlassObj = NULL; +klassOop Universe::_constMethodKlassObj = NULL; +klassOop Universe::_methodDataKlassObj = NULL; +klassOop Universe::_klassKlassObj = NULL; +klassOop Universe::_arrayKlassKlassObj = NULL; +klassOop Universe::_objArrayKlassKlassObj = NULL; +klassOop Universe::_typeArrayKlassKlassObj = NULL; +klassOop Universe::_instanceKlassKlassObj = NULL; +klassOop Universe::_constantPoolKlassObj = NULL; +klassOop Universe::_constantPoolCacheKlassObj = NULL; +klassOop Universe::_compiledICHolderKlassObj = NULL; +klassOop Universe::_systemObjArrayKlassObj = NULL; +oop Universe::_int_mirror = NULL; +oop Universe::_float_mirror = NULL; +oop Universe::_double_mirror = NULL; +oop Universe::_byte_mirror = NULL; +oop Universe::_bool_mirror = NULL; +oop Universe::_char_mirror = NULL; +oop Universe::_long_mirror = NULL; +oop Universe::_short_mirror = NULL; +oop Universe::_void_mirror = NULL; +oop Universe::_mirrors[T_VOID+1] = { NULL /*, NULL...*/ }; +oop Universe::_main_thread_group = NULL; +oop Universe::_system_thread_group = NULL; +typeArrayOop Universe::_the_empty_byte_array = NULL; +typeArrayOop Universe::_the_empty_short_array = NULL; +typeArrayOop Universe::_the_empty_int_array = NULL; +objArrayOop Universe::_the_empty_system_obj_array = NULL; +objArrayOop Universe::_the_empty_class_klass_array = NULL; +objArrayOop Universe::_the_array_interfaces_array = NULL; +LatestMethodOopCache* Universe::_finalizer_register_cache = NULL; +LatestMethodOopCache* Universe::_loader_addClass_cache = NULL; +ActiveMethodOopsCache* Universe::_reflect_invoke_cache = NULL; +oop Universe::_out_of_memory_error_java_heap = NULL; +oop Universe::_out_of_memory_error_perm_gen = NULL; +oop Universe::_out_of_memory_error_array_size = NULL; +oop Universe::_out_of_memory_error_gc_overhead_limit = NULL; +objArrayOop Universe::_preallocated_out_of_memory_error_array = NULL; +volatile jint Universe::_preallocated_out_of_memory_error_avail_count = 0; +bool Universe::_verify_in_progress = false; +oop Universe::_null_ptr_exception_instance = NULL; +oop Universe::_arithmetic_exception_instance = NULL; +oop Universe::_virtual_machine_error_instance = NULL; +oop Universe::_vm_exception = NULL; +oop Universe::_emptySymbol = NULL; + +// These variables are guarded by FullGCALot_lock. +debug_only(objArrayOop Universe::_fullgc_alot_dummy_array = NULL;) +debug_only(int Universe::_fullgc_alot_dummy_next = 0;) + + +// Heap +int Universe::_verify_count = 0; + +int Universe::_base_vtable_size = 0; +bool Universe::_bootstrapping = false; +bool Universe::_fully_initialized = false; + +size_t Universe::_heap_capacity_at_last_gc; +size_t Universe::_heap_used_at_last_gc; + +CollectedHeap* Universe::_collectedHeap = NULL; + + +void Universe::basic_type_classes_do(void f(klassOop)) { + f(boolArrayKlassObj()); + f(byteArrayKlassObj()); + f(charArrayKlassObj()); + f(intArrayKlassObj()); + f(shortArrayKlassObj()); + f(longArrayKlassObj()); + f(singleArrayKlassObj()); + f(doubleArrayKlassObj()); +} + + +void Universe::system_classes_do(void f(klassOop)) { + f(symbolKlassObj()); + f(methodKlassObj()); + f(constMethodKlassObj()); + f(methodDataKlassObj()); + f(klassKlassObj()); + f(arrayKlassKlassObj()); + f(objArrayKlassKlassObj()); + f(typeArrayKlassKlassObj()); + f(instanceKlassKlassObj()); + f(constantPoolKlassObj()); + f(systemObjArrayKlassObj()); +} + +void Universe::oops_do(OopClosure* f, bool do_all) { + + f->do_oop((oop*) &_int_mirror); + f->do_oop((oop*) &_float_mirror); + f->do_oop((oop*) &_double_mirror); + f->do_oop((oop*) &_byte_mirror); + f->do_oop((oop*) &_bool_mirror); + f->do_oop((oop*) &_char_mirror); + f->do_oop((oop*) &_long_mirror); + f->do_oop((oop*) &_short_mirror); + f->do_oop((oop*) &_void_mirror); + + // It's important to iterate over these guys even if they are null, + // since that's how shared heaps are restored. + for (int i = T_BOOLEAN; i < T_VOID+1; i++) { + f->do_oop((oop*) &_mirrors[i]); + } + assert(_mirrors[0] == NULL && _mirrors[T_BOOLEAN - 1] == NULL, "checking"); + + // %%% Consider moving those "shared oops" over here with the others. + f->do_oop((oop*)&_boolArrayKlassObj); + f->do_oop((oop*)&_byteArrayKlassObj); + f->do_oop((oop*)&_charArrayKlassObj); + f->do_oop((oop*)&_intArrayKlassObj); + f->do_oop((oop*)&_shortArrayKlassObj); + f->do_oop((oop*)&_longArrayKlassObj); + f->do_oop((oop*)&_singleArrayKlassObj); + f->do_oop((oop*)&_doubleArrayKlassObj); + f->do_oop((oop*)&_objectArrayKlassObj); + { + for (int i = 0; i < T_VOID+1; i++) { + if (_typeArrayKlassObjs[i] != NULL) { + assert(i >= T_BOOLEAN, "checking"); + f->do_oop((oop*)&_typeArrayKlassObjs[i]); + } else if (do_all) { + f->do_oop((oop*)&_typeArrayKlassObjs[i]); + } + } + } + f->do_oop((oop*)&_symbolKlassObj); + f->do_oop((oop*)&_methodKlassObj); + f->do_oop((oop*)&_constMethodKlassObj); + f->do_oop((oop*)&_methodDataKlassObj); + f->do_oop((oop*)&_klassKlassObj); + f->do_oop((oop*)&_arrayKlassKlassObj); + f->do_oop((oop*)&_objArrayKlassKlassObj); + f->do_oop((oop*)&_typeArrayKlassKlassObj); + f->do_oop((oop*)&_instanceKlassKlassObj); + f->do_oop((oop*)&_constantPoolKlassObj); + f->do_oop((oop*)&_constantPoolCacheKlassObj); + f->do_oop((oop*)&_compiledICHolderKlassObj); + f->do_oop((oop*)&_systemObjArrayKlassObj); + f->do_oop((oop*)&_the_empty_byte_array); + f->do_oop((oop*)&_the_empty_short_array); + f->do_oop((oop*)&_the_empty_int_array); + f->do_oop((oop*)&_the_empty_system_obj_array); + f->do_oop((oop*)&_the_empty_class_klass_array); + f->do_oop((oop*)&_the_array_interfaces_array); + _finalizer_register_cache->oops_do(f); + _loader_addClass_cache->oops_do(f); + _reflect_invoke_cache->oops_do(f); + f->do_oop((oop*)&_out_of_memory_error_java_heap); + f->do_oop((oop*)&_out_of_memory_error_perm_gen); + f->do_oop((oop*)&_out_of_memory_error_array_size); + f->do_oop((oop*)&_out_of_memory_error_gc_overhead_limit); + if (_preallocated_out_of_memory_error_array != (oop)NULL) { // NULL when DumpSharedSpaces + f->do_oop((oop*)&_preallocated_out_of_memory_error_array); + } + f->do_oop((oop*)&_null_ptr_exception_instance); + f->do_oop((oop*)&_arithmetic_exception_instance); + f->do_oop((oop*)&_virtual_machine_error_instance); + f->do_oop((oop*)&_main_thread_group); + f->do_oop((oop*)&_system_thread_group); + f->do_oop((oop*)&_vm_exception); + f->do_oop((oop*)&_emptySymbol); + debug_only(f->do_oop((oop*)&_fullgc_alot_dummy_array);) +} + + +void Universe::check_alignment(uintx size, uintx alignment, const char* name) { + if (size < alignment || size % alignment != 0) { + ResourceMark rm; + stringStream st; + st.print("Size of %s (%ld bytes) must be aligned to %ld bytes", name, size, alignment); + char* error = st.as_string(); + vm_exit_during_initialization(error); + } +} + + +void Universe::genesis(TRAPS) { + ResourceMark rm; + { FlagSetting fs(_bootstrapping, true); + + { MutexLocker mc(Compile_lock); + + // determine base vtable size; without that we cannot create the array klasses + compute_base_vtable_size(); + + if (!UseSharedSpaces) { + _klassKlassObj = klassKlass::create_klass(CHECK); + _arrayKlassKlassObj = arrayKlassKlass::create_klass(CHECK); + + _objArrayKlassKlassObj = objArrayKlassKlass::create_klass(CHECK); + _instanceKlassKlassObj = instanceKlassKlass::create_klass(CHECK); + _typeArrayKlassKlassObj = typeArrayKlassKlass::create_klass(CHECK); + + _symbolKlassObj = symbolKlass::create_klass(CHECK); + + _emptySymbol = oopFactory::new_symbol("", CHECK); + + _boolArrayKlassObj = typeArrayKlass::create_klass(T_BOOLEAN, sizeof(jboolean), CHECK); + _charArrayKlassObj = typeArrayKlass::create_klass(T_CHAR, sizeof(jchar), CHECK); + _singleArrayKlassObj = typeArrayKlass::create_klass(T_FLOAT, sizeof(jfloat), CHECK); + _doubleArrayKlassObj = typeArrayKlass::create_klass(T_DOUBLE, sizeof(jdouble), CHECK); + _byteArrayKlassObj = typeArrayKlass::create_klass(T_BYTE, sizeof(jbyte), CHECK); + _shortArrayKlassObj = typeArrayKlass::create_klass(T_SHORT, sizeof(jshort), CHECK); + _intArrayKlassObj = typeArrayKlass::create_klass(T_INT, sizeof(jint), CHECK); + _longArrayKlassObj = typeArrayKlass::create_klass(T_LONG, sizeof(jlong), CHECK); + + _typeArrayKlassObjs[T_BOOLEAN] = _boolArrayKlassObj; + _typeArrayKlassObjs[T_CHAR] = _charArrayKlassObj; + _typeArrayKlassObjs[T_FLOAT] = _singleArrayKlassObj; + _typeArrayKlassObjs[T_DOUBLE] = _doubleArrayKlassObj; + _typeArrayKlassObjs[T_BYTE] = _byteArrayKlassObj; + _typeArrayKlassObjs[T_SHORT] = _shortArrayKlassObj; + _typeArrayKlassObjs[T_INT] = _intArrayKlassObj; + _typeArrayKlassObjs[T_LONG] = _longArrayKlassObj; + + _methodKlassObj = methodKlass::create_klass(CHECK); + _constMethodKlassObj = constMethodKlass::create_klass(CHECK); + _methodDataKlassObj = methodDataKlass::create_klass(CHECK); + _constantPoolKlassObj = constantPoolKlass::create_klass(CHECK); + _constantPoolCacheKlassObj = constantPoolCacheKlass::create_klass(CHECK); + + _compiledICHolderKlassObj = compiledICHolderKlass::create_klass(CHECK); + _systemObjArrayKlassObj = objArrayKlassKlass::cast(objArrayKlassKlassObj())->allocate_system_objArray_klass(CHECK); + + _the_empty_byte_array = oopFactory::new_permanent_byteArray(0, CHECK); + _the_empty_short_array = oopFactory::new_permanent_shortArray(0, CHECK); + _the_empty_int_array = oopFactory::new_permanent_intArray(0, CHECK); + _the_empty_system_obj_array = oopFactory::new_system_objArray(0, CHECK); + + _the_array_interfaces_array = oopFactory::new_system_objArray(2, CHECK); + _vm_exception = oopFactory::new_symbol("vm exception holder", CHECK); + } else { + + FileMapInfo *mapinfo = FileMapInfo::current_info(); + char* buffer = mapinfo->region_base(CompactingPermGenGen::md); + void** vtbl_list = (void**)buffer; + init_self_patching_vtbl_list(vtbl_list, + CompactingPermGenGen::vtbl_list_size); + } + } + + vmSymbols::initialize(CHECK); + + SystemDictionary::initialize(CHECK); + + klassOop ok = SystemDictionary::object_klass(); + + if (UseSharedSpaces) { + // Verify shared interfaces array. + assert(_the_array_interfaces_array->obj_at(0) == + SystemDictionary::cloneable_klass(), "u3"); + assert(_the_array_interfaces_array->obj_at(1) == + SystemDictionary::serializable_klass(), "u3"); + + // Verify element klass for system obj array klass + assert(objArrayKlass::cast(_systemObjArrayKlassObj)->element_klass() == ok, "u1"); + assert(objArrayKlass::cast(_systemObjArrayKlassObj)->bottom_klass() == ok, "u2"); + + // Verify super class for the classes created above + assert(Klass::cast(boolArrayKlassObj() )->super() == ok, "u3"); + assert(Klass::cast(charArrayKlassObj() )->super() == ok, "u3"); + assert(Klass::cast(singleArrayKlassObj() )->super() == ok, "u3"); + assert(Klass::cast(doubleArrayKlassObj() )->super() == ok, "u3"); + assert(Klass::cast(byteArrayKlassObj() )->super() == ok, "u3"); + assert(Klass::cast(shortArrayKlassObj() )->super() == ok, "u3"); + assert(Klass::cast(intArrayKlassObj() )->super() == ok, "u3"); + assert(Klass::cast(longArrayKlassObj() )->super() == ok, "u3"); + assert(Klass::cast(constantPoolKlassObj() )->super() == ok, "u3"); + assert(Klass::cast(systemObjArrayKlassObj())->super() == ok, "u3"); + } else { + // Set up shared interfaces array. (Do this before supers are set up.) + _the_array_interfaces_array->obj_at_put(0, SystemDictionary::cloneable_klass()); + _the_array_interfaces_array->obj_at_put(1, SystemDictionary::serializable_klass()); + + // Set element klass for system obj array klass + objArrayKlass::cast(_systemObjArrayKlassObj)->set_element_klass(ok); + objArrayKlass::cast(_systemObjArrayKlassObj)->set_bottom_klass(ok); + + // Set super class for the classes created above + Klass::cast(boolArrayKlassObj() )->initialize_supers(ok, CHECK); + Klass::cast(charArrayKlassObj() )->initialize_supers(ok, CHECK); + Klass::cast(singleArrayKlassObj() )->initialize_supers(ok, CHECK); + Klass::cast(doubleArrayKlassObj() )->initialize_supers(ok, CHECK); + Klass::cast(byteArrayKlassObj() )->initialize_supers(ok, CHECK); + Klass::cast(shortArrayKlassObj() )->initialize_supers(ok, CHECK); + Klass::cast(intArrayKlassObj() )->initialize_supers(ok, CHECK); + Klass::cast(longArrayKlassObj() )->initialize_supers(ok, CHECK); + Klass::cast(constantPoolKlassObj() )->initialize_supers(ok, CHECK); + Klass::cast(systemObjArrayKlassObj())->initialize_supers(ok, CHECK); + Klass::cast(boolArrayKlassObj() )->set_super(ok); + Klass::cast(charArrayKlassObj() )->set_super(ok); + Klass::cast(singleArrayKlassObj() )->set_super(ok); + Klass::cast(doubleArrayKlassObj() )->set_super(ok); + Klass::cast(byteArrayKlassObj() )->set_super(ok); + Klass::cast(shortArrayKlassObj() )->set_super(ok); + Klass::cast(intArrayKlassObj() )->set_super(ok); + Klass::cast(longArrayKlassObj() )->set_super(ok); + Klass::cast(constantPoolKlassObj() )->set_super(ok); + Klass::cast(systemObjArrayKlassObj())->set_super(ok); + } + + Klass::cast(boolArrayKlassObj() )->append_to_sibling_list(); + Klass::cast(charArrayKlassObj() )->append_to_sibling_list(); + Klass::cast(singleArrayKlassObj() )->append_to_sibling_list(); + Klass::cast(doubleArrayKlassObj() )->append_to_sibling_list(); + Klass::cast(byteArrayKlassObj() )->append_to_sibling_list(); + Klass::cast(shortArrayKlassObj() )->append_to_sibling_list(); + Klass::cast(intArrayKlassObj() )->append_to_sibling_list(); + Klass::cast(longArrayKlassObj() )->append_to_sibling_list(); + Klass::cast(constantPoolKlassObj() )->append_to_sibling_list(); + Klass::cast(systemObjArrayKlassObj())->append_to_sibling_list(); + } // end of core bootstrapping + + // Initialize _objectArrayKlass after core bootstraping to make + // sure the super class is set up properly for _objectArrayKlass. + _objectArrayKlassObj = instanceKlass:: + cast(SystemDictionary::object_klass())->array_klass(1, CHECK); + // Add the class to the class hierarchy manually to make sure that + // its vtable is initialized after core bootstrapping is completed. + Klass::cast(_objectArrayKlassObj)->append_to_sibling_list(); + + // Compute is_jdk version flags. + // Only 1.3 or later has the java.lang.Shutdown class. + // Only 1.4 or later has the java.lang.CharSequence interface. + // Only 1.5 or later has the java.lang.management.MemoryUsage class. + if (JDK_Version::is_pre_jdk16_version()) { + klassOop k = SystemDictionary::resolve_or_null(vmSymbolHandles::java_lang_management_MemoryUsage(), THREAD); + CLEAR_PENDING_EXCEPTION; // ignore exceptions + if (k == NULL) { + k = SystemDictionary::resolve_or_null(vmSymbolHandles::java_lang_CharSequence(), THREAD); + CLEAR_PENDING_EXCEPTION; // ignore exceptions + if (k == NULL) { + k = SystemDictionary::resolve_or_null(vmSymbolHandles::java_lang_Shutdown(), THREAD); + CLEAR_PENDING_EXCEPTION; // ignore exceptions + if (k == NULL) { + JDK_Version::set_jdk12x_version(); + } else { + JDK_Version::set_jdk13x_version(); + } + } else { + JDK_Version::set_jdk14x_version(); + } + } else { + JDK_Version::set_jdk15x_version(); + } + } + + #ifdef ASSERT + if (FullGCALot) { + // Allocate an array of dummy objects. + // We'd like these to be at the bottom of the old generation, + // so that when we free one and then collect, + // (almost) the whole heap moves + // and we find out if we actually update all the oops correctly. + // But we can't allocate directly in the old generation, + // so we allocate wherever, and hope that the first collection + // moves these objects to the bottom of the old generation. + // We can allocate directly in the permanent generation, so we do. + int size; + if (UseConcMarkSweepGC) { + warning("Using +FullGCALot with concurrent mark sweep gc " + "will not force all objects to relocate"); + size = FullGCALotDummies; + } else { + size = FullGCALotDummies * 2; + } + objArrayOop naked_array = oopFactory::new_system_objArray(size, CHECK); + objArrayHandle dummy_array(THREAD, naked_array); + int i = 0; + while (i < size) { + if (!UseConcMarkSweepGC) { + // Allocate dummy in old generation + oop dummy = instanceKlass::cast(SystemDictionary::object_klass())->allocate_instance(CHECK); + dummy_array->obj_at_put(i++, dummy); + } + // Allocate dummy in permanent generation + oop dummy = instanceKlass::cast(SystemDictionary::object_klass())->allocate_permanent_instance(CHECK); + dummy_array->obj_at_put(i++, dummy); + } + { + // Only modify the global variable inside the mutex. + // If we had a race to here, the other dummy_array instances + // and their elements just get dropped on the floor, which is fine. + MutexLocker ml(FullGCALot_lock); + if (_fullgc_alot_dummy_array == NULL) { + _fullgc_alot_dummy_array = dummy_array(); + } + } + assert(i == _fullgc_alot_dummy_array->length(), "just checking"); + } + #endif +} + + +static inline void add_vtable(void** list, int* n, Klass* o, int count) { + list[(*n)++] = *(void**)&o->vtbl_value(); + guarantee((*n) <= count, "vtable list too small."); +} + + +void Universe::init_self_patching_vtbl_list(void** list, int count) { + int n = 0; + { klassKlass o; add_vtable(list, &n, &o, count); } + { arrayKlassKlass o; add_vtable(list, &n, &o, count); } + { objArrayKlassKlass o; add_vtable(list, &n, &o, count); } + { instanceKlassKlass o; add_vtable(list, &n, &o, count); } + { instanceKlass o; add_vtable(list, &n, &o, count); } + { instanceRefKlass o; add_vtable(list, &n, &o, count); } + { typeArrayKlassKlass o; add_vtable(list, &n, &o, count); } + { symbolKlass o; add_vtable(list, &n, &o, count); } + { typeArrayKlass o; add_vtable(list, &n, &o, count); } + { methodKlass o; add_vtable(list, &n, &o, count); } + { constMethodKlass o; add_vtable(list, &n, &o, count); } + { constantPoolKlass o; add_vtable(list, &n, &o, count); } + { constantPoolCacheKlass o; add_vtable(list, &n, &o, count); } + { objArrayKlass o; add_vtable(list, &n, &o, count); } + { methodDataKlass o; add_vtable(list, &n, &o, count); } + { compiledICHolderKlass o; add_vtable(list, &n, &o, count); } +} + + +class FixupMirrorClosure: public ObjectClosure { + public: + void do_object(oop obj) { + if (obj->is_klass()) { + EXCEPTION_MARK; + KlassHandle k(THREAD, klassOop(obj)); + // We will never reach the CATCH below since Exceptions::_throw will cause + // the VM to exit if an exception is thrown during initialization + java_lang_Class::create_mirror(k, CATCH); + // This call unconditionally creates a new mirror for k, + // and links in k's component_mirror field if k is an array. + // If k is an objArray, k's element type must already have + // a mirror. In other words, this closure must process + // the component type of an objArray k before it processes k. + // This works because the permgen iterator presents arrays + // and their component types in order of creation. + } + } +}; + +void Universe::initialize_basic_type_mirrors(TRAPS) { + if (UseSharedSpaces) { + assert(_int_mirror != NULL, "already loaded"); + assert(_void_mirror == _mirrors[T_VOID], "consistently loaded"); + } else { + + assert(_int_mirror==NULL, "basic type mirrors already initialized"); + _int_mirror = + java_lang_Class::create_basic_type_mirror("int", T_INT, CHECK); + _float_mirror = + java_lang_Class::create_basic_type_mirror("float", T_FLOAT, CHECK); + _double_mirror = + java_lang_Class::create_basic_type_mirror("double", T_DOUBLE, CHECK); + _byte_mirror = + java_lang_Class::create_basic_type_mirror("byte", T_BYTE, CHECK); + _bool_mirror = + java_lang_Class::create_basic_type_mirror("boolean",T_BOOLEAN, CHECK); + _char_mirror = + java_lang_Class::create_basic_type_mirror("char", T_CHAR, CHECK); + _long_mirror = + java_lang_Class::create_basic_type_mirror("long", T_LONG, CHECK); + _short_mirror = + java_lang_Class::create_basic_type_mirror("short", T_SHORT, CHECK); + _void_mirror = + java_lang_Class::create_basic_type_mirror("void", T_VOID, CHECK); + + _mirrors[T_INT] = _int_mirror; + _mirrors[T_FLOAT] = _float_mirror; + _mirrors[T_DOUBLE] = _double_mirror; + _mirrors[T_BYTE] = _byte_mirror; + _mirrors[T_BOOLEAN] = _bool_mirror; + _mirrors[T_CHAR] = _char_mirror; + _mirrors[T_LONG] = _long_mirror; + _mirrors[T_SHORT] = _short_mirror; + _mirrors[T_VOID] = _void_mirror; + //_mirrors[T_OBJECT] = instanceKlass::cast(_object_klass)->java_mirror(); + //_mirrors[T_ARRAY] = instanceKlass::cast(_object_klass)->java_mirror(); + } +} + +void Universe::fixup_mirrors(TRAPS) { + // Bootstrap problem: all classes gets a mirror (java.lang.Class instance) assigned eagerly, + // but we cannot do that for classes created before java.lang.Class is loaded. Here we simply + // walk over permanent objects created so far (mostly classes) and fixup their mirrors. Note + // that the number of objects allocated at this point is very small. + assert(SystemDictionary::class_klass_loaded(), "java.lang.Class should be loaded"); + FixupMirrorClosure blk; + Universe::heap()->permanent_object_iterate(&blk); +} + + +static bool has_run_finalizers_on_exit = false; + +void Universe::run_finalizers_on_exit() { + if (has_run_finalizers_on_exit) return; + has_run_finalizers_on_exit = true; + + // Called on VM exit. This ought to be run in a separate thread. + if (TraceReferenceGC) tty->print_cr("Callback to run finalizers on exit"); + { + PRESERVE_EXCEPTION_MARK; + KlassHandle finalizer_klass(THREAD, SystemDictionary::finalizer_klass()); + JavaValue result(T_VOID); + JavaCalls::call_static( + &result, + finalizer_klass, + vmSymbolHandles::run_finalizers_on_exit_name(), + vmSymbolHandles::void_method_signature(), + THREAD + ); + // Ignore any pending exceptions + CLEAR_PENDING_EXCEPTION; + } +} + + +// initialize_vtable could cause gc if +// 1) we specified true to initialize_vtable and +// 2) this ran after gc was enabled +// In case those ever change we use handles for oops +void Universe::reinitialize_vtable_of(KlassHandle k_h, TRAPS) { + // init vtable of k and all subclasses + Klass* ko = k_h()->klass_part(); + klassVtable* vt = ko->vtable(); + if (vt) vt->initialize_vtable(false, CHECK); + if (ko->oop_is_instance()) { + instanceKlass* ik = (instanceKlass*)ko; + for (KlassHandle s_h(THREAD, ik->subklass()); s_h() != NULL; s_h = (THREAD, s_h()->klass_part()->next_sibling())) { + reinitialize_vtable_of(s_h, CHECK); + } + } +} + + +void initialize_itable_for_klass(klassOop k, TRAPS) { + instanceKlass::cast(k)->itable()->initialize_itable(false, CHECK); +} + + +void Universe::reinitialize_itables(TRAPS) { + SystemDictionary::classes_do(initialize_itable_for_klass, CHECK); + +} + + +bool Universe::on_page_boundary(void* addr) { + return ((uintptr_t) addr) % os::vm_page_size() == 0; +} + + +bool Universe::should_fill_in_stack_trace(Handle throwable) { + // never attempt to fill in the stack trace of preallocated errors that do not have + // backtrace. These errors are kept alive forever and may be "re-used" when all + // preallocated errors with backtrace have been consumed. Also need to avoid + // a potential loop which could happen if an out of memory occurs when attempting + // to allocate the backtrace. + return ((throwable() != Universe::_out_of_memory_error_java_heap) && + (throwable() != Universe::_out_of_memory_error_perm_gen) && + (throwable() != Universe::_out_of_memory_error_array_size) && + (throwable() != Universe::_out_of_memory_error_gc_overhead_limit)); +} + + +oop Universe::gen_out_of_memory_error(oop default_err) { + // generate an out of memory error: + // - if there is a preallocated error with backtrace available then return it wth + // a filled in stack trace. + // - if there are no preallocated errors with backtrace available then return + // an error without backtrace. + int next; + if (_preallocated_out_of_memory_error_avail_count > 0) { + next = (int)Atomic::add(-1, &_preallocated_out_of_memory_error_avail_count); + assert(next < (int)PreallocatedOutOfMemoryErrorCount, "avail count is corrupt"); + } else { + next = -1; + } + if (next < 0) { + // all preallocated errors have been used. + // return default + return default_err; + } else { + // get the error object at the slot and set set it to NULL so that the + // array isn't keeping it alive anymore. + oop exc = preallocated_out_of_memory_errors()->obj_at(next); + assert(exc != NULL, "slot has been used already"); + preallocated_out_of_memory_errors()->obj_at_put(next, NULL); + + // use the message from the default error + oop msg = java_lang_Throwable::message(default_err); + assert(msg != NULL, "no message"); + java_lang_Throwable::set_message(exc, msg); + + // populate the stack trace and return it. + java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(exc); + return exc; + } +} + +static intptr_t non_oop_bits = 0; + +void* Universe::non_oop_word() { + // Neither the high bits nor the low bits of this value is allowed + // to look like (respectively) the high or low bits of a real oop. + // + // High and low are CPU-specific notions, but low always includes + // the low-order bit. Since oops are always aligned at least mod 4, + // setting the low-order bit will ensure that the low half of the + // word will never look like that of a real oop. + // + // Using the OS-supplied non-memory-address word (usually 0 or -1) + // will take care of the high bits, however many there are. + + if (non_oop_bits == 0) { + non_oop_bits = (intptr_t)os::non_memory_address_word() | 1; + } + + return (void*)non_oop_bits; +} + +jint universe_init() { + assert(!Universe::_fully_initialized, "called after initialize_vtables"); + guarantee(1 << LogHeapWordSize == sizeof(HeapWord), + "LogHeapWordSize is incorrect."); + guarantee(sizeof(oop) >= sizeof(HeapWord), "HeapWord larger than oop?"); + guarantee(sizeof(oop) % sizeof(HeapWord) == 0, + "oop size is not not a multiple of HeapWord size"); + TraceTime timer("Genesis", TraceStartupTime); + GC_locker::lock(); // do not allow gc during bootstrapping + JavaClasses::compute_hard_coded_offsets(); + + // Get map info from shared archive file. + if (DumpSharedSpaces) + UseSharedSpaces = false; + + FileMapInfo* mapinfo = NULL; + if (UseSharedSpaces) { + mapinfo = NEW_C_HEAP_OBJ(FileMapInfo); + memset(mapinfo, 0, sizeof(FileMapInfo)); + + // Open the shared archive file, read and validate the header. If + // initialization files, shared spaces [UseSharedSpaces] are + // disabled and the file is closed. + + if (mapinfo->initialize()) { + FileMapInfo::set_current_info(mapinfo); + } else { + assert(!mapinfo->is_open() && !UseSharedSpaces, + "archive file not closed or shared spaces not disabled."); + } + } + + jint status = Universe::initialize_heap(); + if (status != JNI_OK) { + return status; + } + + // We have a heap so create the methodOop caches before + // CompactingPermGenGen::initialize_oops() tries to populate them. + Universe::_finalizer_register_cache = new LatestMethodOopCache(); + Universe::_loader_addClass_cache = new LatestMethodOopCache(); + Universe::_reflect_invoke_cache = new ActiveMethodOopsCache(); + + if (UseSharedSpaces) { + + // Read the data structures supporting the shared spaces (shared + // system dictionary, symbol table, etc.). After that, access to + // the file (other than the mapped regions) is no longer needed, and + // the file is closed. Closing the file does not affect the + // currently mapped regions. + + CompactingPermGenGen::initialize_oops(); + mapinfo->close(); + + } else { + SymbolTable::create_table(); + StringTable::create_table(); + ClassLoader::create_package_info_table(); + } + + return JNI_OK; +} + +jint Universe::initialize_heap() { + + if (UseParallelGC) { +#ifndef SERIALGC + Universe::_collectedHeap = new ParallelScavengeHeap(); +#else // SERIALGC + fatal("UseParallelGC not supported in java kernel vm."); +#endif // SERIALGC + + } else { + GenCollectorPolicy *gc_policy; + + if (UseSerialGC) { + gc_policy = new MarkSweepPolicy(); + } else if (UseConcMarkSweepGC) { +#ifndef SERIALGC + if (UseAdaptiveSizePolicy) { + gc_policy = new ASConcurrentMarkSweepPolicy(); + } else { + gc_policy = new ConcurrentMarkSweepPolicy(); + } +#else // SERIALGC + fatal("UseConcMarkSweepGC not supported in java kernel vm."); +#endif // SERIALGC + } else { // default old generation + gc_policy = new MarkSweepPolicy(); + } + + Universe::_collectedHeap = new GenCollectedHeap(gc_policy); + } + + jint status = Universe::heap()->initialize(); + if (status != JNI_OK) { + return status; + } + + // We will never reach the CATCH below since Exceptions::_throw will cause + // the VM to exit if an exception is thrown during initialization + + if (UseTLAB) { + assert(Universe::heap()->supports_tlab_allocation(), + "Should support thread-local allocation buffers"); + ThreadLocalAllocBuffer::startup_initialization(); + } + return JNI_OK; +} + +// It's the caller's repsonsibility to ensure glitch-freedom +// (if required). +void Universe::update_heap_info_at_gc() { + _heap_capacity_at_last_gc = heap()->capacity(); + _heap_used_at_last_gc = heap()->used(); +} + + + +void universe2_init() { + EXCEPTION_MARK; + Universe::genesis(CATCH); + // Although we'd like to verify here that the state of the heap + // is good, we can't because the main thread has not yet added + // itself to the threads list (so, using current interfaces + // we can't "fill" its TLAB), unless TLABs are disabled. + if (VerifyBeforeGC && !UseTLAB && + Universe::heap()->total_collections() >= VerifyGCStartAt) { + Universe::heap()->prepare_for_verify(); + Universe::verify(); // make sure we're starting with a clean slate + } +} + + +// This function is defined in JVM.cpp +extern void initialize_converter_functions(); + +bool universe_post_init() { + Universe::_fully_initialized = true; + EXCEPTION_MARK; + { ResourceMark rm; + Interpreter::initialize(); // needed for interpreter entry points + if (!UseSharedSpaces) { + KlassHandle ok_h(THREAD, SystemDictionary::object_klass()); + Universe::reinitialize_vtable_of(ok_h, CHECK_false); + Universe::reinitialize_itables(CHECK_false); + } + } + + klassOop k; + instanceKlassHandle k_h; + if (!UseSharedSpaces) { + // Setup preallocated empty java.lang.Class array + Universe::_the_empty_class_klass_array = oopFactory::new_objArray(SystemDictionary::class_klass(), 0, CHECK_false); + // Setup preallocated OutOfMemoryError errors + k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_OutOfMemoryError(), true, CHECK_false); + k_h = instanceKlassHandle(THREAD, k); + Universe::_out_of_memory_error_java_heap = k_h->allocate_permanent_instance(CHECK_false); + Universe::_out_of_memory_error_perm_gen = k_h->allocate_permanent_instance(CHECK_false); + Universe::_out_of_memory_error_array_size = k_h->allocate_permanent_instance(CHECK_false); + Universe::_out_of_memory_error_gc_overhead_limit = + k_h->allocate_permanent_instance(CHECK_false); + + // Setup preallocated NullPointerException + // (this is currently used for a cheap & dirty solution in compiler exception handling) + k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_NullPointerException(), true, CHECK_false); + Universe::_null_ptr_exception_instance = instanceKlass::cast(k)->allocate_permanent_instance(CHECK_false); + // Setup preallocated ArithmeticException + // (this is currently used for a cheap & dirty solution in compiler exception handling) + k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_ArithmeticException(), true, CHECK_false); + Universe::_arithmetic_exception_instance = instanceKlass::cast(k)->allocate_permanent_instance(CHECK_false); + // Virtual Machine Error for when we get into a situation we can't resolve + k = SystemDictionary::resolve_or_fail( + vmSymbolHandles::java_lang_VirtualMachineError(), true, CHECK_false); + bool linked = instanceKlass::cast(k)->link_class_or_fail(CHECK_false); + if (!linked) { + tty->print_cr("Unable to link/verify VirtualMachineError class"); + return false; // initialization failed + } + Universe::_virtual_machine_error_instance = + instanceKlass::cast(k)->allocate_permanent_instance(CHECK_false); + } + if (!DumpSharedSpaces) { + // These are the only Java fields that are currently set during shared space dumping. + // We prefer to not handle this generally, so we always reinitialize these detail messages. + Handle msg = java_lang_String::create_from_str("Java heap space", CHECK_false); + java_lang_Throwable::set_message(Universe::_out_of_memory_error_java_heap, msg()); + + msg = java_lang_String::create_from_str("PermGen space", CHECK_false); + java_lang_Throwable::set_message(Universe::_out_of_memory_error_perm_gen, msg()); + + msg = java_lang_String::create_from_str("Requested array size exceeds VM limit", CHECK_false); + java_lang_Throwable::set_message(Universe::_out_of_memory_error_array_size, msg()); + + msg = java_lang_String::create_from_str("GC overhead limit exceeded", CHECK_false); + java_lang_Throwable::set_message(Universe::_out_of_memory_error_gc_overhead_limit, msg()); + + msg = java_lang_String::create_from_str("/ by zero", CHECK_false); + java_lang_Throwable::set_message(Universe::_arithmetic_exception_instance, msg()); + + // Setup the array of errors that have preallocated backtrace + k = Universe::_out_of_memory_error_java_heap->klass(); + assert(k->klass_part()->name() == vmSymbols::java_lang_OutOfMemoryError(), "should be out of memory error"); + k_h = instanceKlassHandle(THREAD, k); + + int len = (StackTraceInThrowable) ? (int)PreallocatedOutOfMemoryErrorCount : 0; + Universe::_preallocated_out_of_memory_error_array = oopFactory::new_objArray(k_h(), len, CHECK_false); + for (int i=0; iallocate_permanent_instance(CHECK_false); + Handle err_h = Handle(THREAD, err); + java_lang_Throwable::allocate_backtrace(err_h, CHECK_false); + Universe::preallocated_out_of_memory_errors()->obj_at_put(i, err_h()); + } + Universe::_preallocated_out_of_memory_error_avail_count = (jint)len; + } + + + // Setup static method for registering finalizers + // The finalizer klass must be linked before looking up the method, in + // case it needs to get rewritten. + instanceKlass::cast(SystemDictionary::finalizer_klass())->link_class(CHECK_false); + methodOop m = instanceKlass::cast(SystemDictionary::finalizer_klass())->find_method( + vmSymbols::register_method_name(), + vmSymbols::register_method_signature()); + if (m == NULL || !m->is_static()) { + THROW_MSG_(vmSymbols::java_lang_NoSuchMethodException(), + "java.lang.ref.Finalizer.register", false); + } + Universe::_finalizer_register_cache->init( + SystemDictionary::finalizer_klass(), m, CHECK_false); + + // Resolve on first use and initialize class. + // Note: No race-condition here, since a resolve will always return the same result + + // Setup method for security checks + k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_reflect_Method(), true, CHECK_false); + k_h = instanceKlassHandle(THREAD, k); + k_h->link_class(CHECK_false); + m = k_h->find_method(vmSymbols::invoke_name(), vmSymbols::object_array_object_object_signature()); + if (m == NULL || m->is_static()) { + THROW_MSG_(vmSymbols::java_lang_NoSuchMethodException(), + "java.lang.reflect.Method.invoke", false); + } + Universe::_reflect_invoke_cache->init(k_h(), m, CHECK_false); + + // Setup method for registering loaded classes in class loader vector + instanceKlass::cast(SystemDictionary::classloader_klass())->link_class(CHECK_false); + m = instanceKlass::cast(SystemDictionary::classloader_klass())->find_method(vmSymbols::addClass_name(), vmSymbols::class_void_signature()); + if (m == NULL || m->is_static()) { + THROW_MSG_(vmSymbols::java_lang_NoSuchMethodException(), + "java.lang.ClassLoader.addClass", false); + } + Universe::_loader_addClass_cache->init( + SystemDictionary::classloader_klass(), m, CHECK_false); + + // The folowing is initializing converter functions for serialization in + // JVM.cpp. If we clean up the StrictMath code above we may want to find + // a better solution for this as well. + initialize_converter_functions(); + + // This needs to be done before the first scavenge/gc, since + // it's an input to soft ref clearing policy. + Universe::update_heap_info_at_gc(); + + // ("weak") refs processing infrastructure initialization + Universe::heap()->post_initialize(); + + GC_locker::unlock(); // allow gc after bootstrapping + + MemoryService::set_universe_heap(Universe::_collectedHeap); + return true; +} + + +void Universe::compute_base_vtable_size() { + _base_vtable_size = ClassLoader::compute_Object_vtable(); +} + + +// %%% The Universe::flush_foo methods belong in CodeCache. + +// Flushes compiled methods dependent on dependee. +void Universe::flush_dependents_on(instanceKlassHandle dependee) { + assert_lock_strong(Compile_lock); + + if (CodeCache::number_of_nmethods_with_dependencies() == 0) return; + + // CodeCache can only be updated by a thread_in_VM and they will all be + // stopped dring the safepoint so CodeCache will be safe to update without + // holding the CodeCache_lock. + + DepChange changes(dependee); + + // Compute the dependent nmethods + if (CodeCache::mark_for_deoptimization(changes) > 0) { + // At least one nmethod has been marked for deoptimization + VM_Deoptimize op; + VMThread::execute(&op); + } +} + +#ifdef HOTSWAP +// Flushes compiled methods dependent on dependee in the evolutionary sense +void Universe::flush_evol_dependents_on(instanceKlassHandle ev_k_h) { + // --- Compile_lock is not held. However we are at a safepoint. + assert_locked_or_safepoint(Compile_lock); + if (CodeCache::number_of_nmethods_with_dependencies() == 0) return; + + // CodeCache can only be updated by a thread_in_VM and they will all be + // stopped dring the safepoint so CodeCache will be safe to update without + // holding the CodeCache_lock. + + // Compute the dependent nmethods + if (CodeCache::mark_for_evol_deoptimization(ev_k_h) > 0) { + // At least one nmethod has been marked for deoptimization + + // All this already happens inside a VM_Operation, so we'll do all the work here. + // Stuff copied from VM_Deoptimize and modified slightly. + + // We do not want any GCs to happen while we are in the middle of this VM operation + ResourceMark rm; + DeoptimizationMarker dm; + + // Deoptimize all activations depending on marked nmethods + Deoptimization::deoptimize_dependents(); + + // Make the dependent methods not entrant (in VM_Deoptimize they are made zombies) + CodeCache::make_marked_nmethods_not_entrant(); + } +} +#endif // HOTSWAP + + +// Flushes compiled methods dependent on dependee +void Universe::flush_dependents_on_method(methodHandle m_h) { + // --- Compile_lock is not held. However we are at a safepoint. + assert_locked_or_safepoint(Compile_lock); + + // CodeCache can only be updated by a thread_in_VM and they will all be + // stopped dring the safepoint so CodeCache will be safe to update without + // holding the CodeCache_lock. + + // Compute the dependent nmethods + if (CodeCache::mark_for_deoptimization(m_h()) > 0) { + // At least one nmethod has been marked for deoptimization + + // All this already happens inside a VM_Operation, so we'll do all the work here. + // Stuff copied from VM_Deoptimize and modified slightly. + + // We do not want any GCs to happen while we are in the middle of this VM operation + ResourceMark rm; + DeoptimizationMarker dm; + + // Deoptimize all activations depending on marked nmethods + Deoptimization::deoptimize_dependents(); + + // Make the dependent methods not entrant (in VM_Deoptimize they are made zombies) + CodeCache::make_marked_nmethods_not_entrant(); + } +} + +void Universe::print() { print_on(gclog_or_tty); } + +void Universe::print_on(outputStream* st) { + st->print_cr("Heap"); + heap()->print_on(st); +} + +void Universe::print_heap_at_SIGBREAK() { + if (PrintHeapAtSIGBREAK) { + MutexLocker hl(Heap_lock); + print_on(tty); + tty->cr(); + tty->flush(); + } +} + +void Universe::print_heap_before_gc(outputStream* st) { + st->print_cr("{Heap before GC invocations=%u (full %u):", + heap()->total_collections(), + heap()->total_full_collections()); + heap()->print_on(st); +} + +void Universe::print_heap_after_gc(outputStream* st) { + st->print_cr("Heap after GC invocations=%u (full %u):", + heap()->total_collections(), + heap()->total_full_collections()); + heap()->print_on(st); + st->print_cr("}"); +} + +void Universe::verify(bool allow_dirty, bool silent) { + if (SharedSkipVerify) { + return; + } + + // The use of _verify_in_progress is a temporary work around for + // 6320749. Don't bother with a creating a class to set and clear + // it since it is only used in this method and the control flow is + // straight forward. + _verify_in_progress = true; + + COMPILER2_PRESENT( + assert(!DerivedPointerTable::is_active(), + "DPT should not be active during verification " + "(of thread stacks below)"); + ) + + ResourceMark rm; + HandleMark hm; // Handles created during verification can be zapped + _verify_count++; + + if (!silent) gclog_or_tty->print("[Verifying "); + if (!silent) gclog_or_tty->print("threads "); + Threads::verify(); + heap()->verify(allow_dirty, silent); + + if (!silent) gclog_or_tty->print("syms "); + SymbolTable::verify(); + if (!silent) gclog_or_tty->print("strs "); + StringTable::verify(); + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + if (!silent) gclog_or_tty->print("zone "); + CodeCache::verify(); + } + if (!silent) gclog_or_tty->print("dict "); + SystemDictionary::verify(); + if (!silent) gclog_or_tty->print("hand "); + JNIHandles::verify(); + if (!silent) gclog_or_tty->print("C-heap "); + os::check_heap(); + if (!silent) gclog_or_tty->print_cr("]"); + + _verify_in_progress = false; +} + +// Oop verification (see MacroAssembler::verify_oop) + +static uintptr_t _verify_oop_data[2] = {0, (uintptr_t)-1}; +static uintptr_t _verify_klass_data[2] = {0, (uintptr_t)-1}; + + +static void calculate_verify_data(uintptr_t verify_data[2], + HeapWord* low_boundary, + HeapWord* high_boundary) { + assert(low_boundary < high_boundary, "bad interval"); + + // decide which low-order bits we require to be clear: + size_t alignSize = MinObjAlignmentInBytes; + size_t min_object_size = oopDesc::header_size(); + + // make an inclusive limit: + uintptr_t max = (uintptr_t)high_boundary - min_object_size*wordSize; + uintptr_t min = (uintptr_t)low_boundary; + assert(min < max, "bad interval"); + uintptr_t diff = max ^ min; + + // throw away enough low-order bits to make the diff vanish + uintptr_t mask = (uintptr_t)(-1); + while ((mask & diff) != 0) + mask <<= 1; + uintptr_t bits = (min & mask); + assert(bits == (max & mask), "correct mask"); + // check an intermediate value between min and max, just to make sure: + assert(bits == ((min + (max-min)/2) & mask), "correct mask"); + + // require address alignment, too: + mask |= (alignSize - 1); + + if (!(verify_data[0] == 0 && verify_data[1] == (uintptr_t)-1)) { + assert(verify_data[0] == mask && verify_data[1] == bits, "mask stability"); + } + verify_data[0] = mask; + verify_data[1] = bits; +} + + +// Oop verification (see MacroAssembler::verify_oop) +#ifndef PRODUCT + +uintptr_t Universe::verify_oop_mask() { + MemRegion m = heap()->reserved_region(); + calculate_verify_data(_verify_oop_data, + m.start(), + m.end()); + return _verify_oop_data[0]; +} + + + +uintptr_t Universe::verify_oop_bits() { + verify_oop_mask(); + return _verify_oop_data[1]; +} + + +uintptr_t Universe::verify_klass_mask() { + /* $$$ + // A klass can never live in the new space. Since the new and old + // spaces can change size, we must settle for bounds-checking against + // the bottom of the world, plus the smallest possible new and old + // space sizes that may arise during execution. + size_t min_new_size = Universe::new_size(); // in bytes + size_t min_old_size = Universe::old_size(); // in bytes + calculate_verify_data(_verify_klass_data, + (HeapWord*)((uintptr_t)_new_gen->low_boundary + min_new_size + min_old_size), + _perm_gen->high_boundary); + */ + // Why doesn't the above just say that klass's always live in the perm + // gen? I'll see if that seems to work... + MemRegion permanent_reserved; + switch (Universe::heap()->kind()) { + default: + // ???: What if a CollectedHeap doesn't have a permanent generation? + ShouldNotReachHere(); + break; + case CollectedHeap::GenCollectedHeap: { + GenCollectedHeap* gch = (GenCollectedHeap*) Universe::heap(); + permanent_reserved = gch->perm_gen()->reserved(); + break; + } +#ifndef SERIALGC + case CollectedHeap::ParallelScavengeHeap: { + ParallelScavengeHeap* psh = (ParallelScavengeHeap*) Universe::heap(); + permanent_reserved = psh->perm_gen()->reserved(); + break; + } +#endif // SERIALGC + } + calculate_verify_data(_verify_klass_data, + permanent_reserved.start(), + permanent_reserved.end()); + + return _verify_klass_data[0]; +} + + + +uintptr_t Universe::verify_klass_bits() { + verify_klass_mask(); + return _verify_klass_data[1]; +} + + +uintptr_t Universe::verify_mark_mask() { + return markOopDesc::lock_mask_in_place; +} + + + +uintptr_t Universe::verify_mark_bits() { + intptr_t mask = verify_mark_mask(); + intptr_t bits = (intptr_t)markOopDesc::prototype(); + assert((bits & ~mask) == 0, "no stray header bits"); + return bits; +} +#endif // PRODUCT + + +void Universe::compute_verify_oop_data() { + verify_oop_mask(); + verify_oop_bits(); + verify_mark_mask(); + verify_mark_bits(); + verify_klass_mask(); + verify_klass_bits(); +} + + +void CommonMethodOopCache::init(klassOop k, methodOop m, TRAPS) { + if (!UseSharedSpaces) { + _klass = k; + } +#ifndef PRODUCT + else { + // sharing initilization should have already set up _klass + assert(_klass != NULL, "just checking"); + } +#endif + + _method_idnum = m->method_idnum(); + assert(_method_idnum >= 0, "sanity check"); +} + + +ActiveMethodOopsCache::~ActiveMethodOopsCache() { + if (_prev_methods != NULL) { + for (int i = _prev_methods->length() - 1; i >= 0; i--) { + jweak method_ref = _prev_methods->at(i); + if (method_ref != NULL) { + JNIHandles::destroy_weak_global(method_ref); + } + } + delete _prev_methods; + _prev_methods = NULL; + } +} + + +void ActiveMethodOopsCache::add_previous_version(const methodOop method) { + assert(Thread::current()->is_VM_thread(), + "only VMThread can add previous versions"); + + if (_prev_methods == NULL) { + // This is the first previous version so make some space. + // Start with 2 elements under the assumption that the class + // won't be redefined much. + _prev_methods = new (ResourceObj::C_HEAP) GrowableArray(2, true); + } + + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00000100, + ("add: %s(%s): adding prev version ref for cached method @%d", + method->name()->as_C_string(), method->signature()->as_C_string(), + _prev_methods->length())); + + methodHandle method_h(method); + jweak method_ref = JNIHandles::make_weak_global(method_h); + _prev_methods->append(method_ref); + + // Using weak references allows previous versions of the cached + // method to be GC'ed when they are no longer needed. Since the + // caller is the VMThread and we are at a safepoint, this is a good + // time to clear out unused weak references. + + for (int i = _prev_methods->length() - 1; i >= 0; i--) { + jweak method_ref = _prev_methods->at(i); + assert(method_ref != NULL, "weak method ref was unexpectedly cleared"); + if (method_ref == NULL) { + _prev_methods->remove_at(i); + // Since we are traversing the array backwards, we don't have to + // do anything special with the index. + continue; // robustness + } + + methodOop m = (methodOop)JNIHandles::resolve(method_ref); + if (m == NULL) { + // this method entry has been GC'ed so remove it + JNIHandles::destroy_weak_global(method_ref); + _prev_methods->remove_at(i); + } else { + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00000400, ("add: %s(%s): previous cached method @%d is alive", + m->name()->as_C_string(), m->signature()->as_C_string(), i)); + } + } +} // end add_previous_version() + + +bool ActiveMethodOopsCache::is_same_method(const methodOop method) const { + instanceKlass* ik = instanceKlass::cast(klass()); + methodOop check_method = ik->method_with_idnum(method_idnum()); + assert(check_method != NULL, "sanity check"); + if (check_method == method) { + // done with the easy case + return true; + } + + if (_prev_methods != NULL) { + // The cached method has been redefined at least once so search + // the previous versions for a match. + for (int i = 0; i < _prev_methods->length(); i++) { + jweak method_ref = _prev_methods->at(i); + assert(method_ref != NULL, "weak method ref was unexpectedly cleared"); + if (method_ref == NULL) { + continue; // robustness + } + + check_method = (methodOop)JNIHandles::resolve(method_ref); + if (check_method == method) { + // a previous version matches + return true; + } + } + } + + // either no previous versions or no previous version matched + return false; +} + + +methodOop LatestMethodOopCache::get_methodOop() { + instanceKlass* ik = instanceKlass::cast(klass()); + methodOop m = ik->method_with_idnum(method_idnum()); + assert(m != NULL, "sanity check"); + return m; +} + + +#ifdef ASSERT +// Release dummy object(s) at bottom of heap +bool Universe::release_fullgc_alot_dummy() { + MutexLocker ml(FullGCALot_lock); + if (_fullgc_alot_dummy_array != NULL) { + if (_fullgc_alot_dummy_next >= _fullgc_alot_dummy_array->length()) { + // No more dummies to release, release entire array instead + _fullgc_alot_dummy_array = NULL; + return false; + } + if (!UseConcMarkSweepGC) { + // Release dummy at bottom of old generation + _fullgc_alot_dummy_array->obj_at_put(_fullgc_alot_dummy_next++, NULL); + } + // Release dummy at bottom of permanent generation + _fullgc_alot_dummy_array->obj_at_put(_fullgc_alot_dummy_next++, NULL); + } + return true; +} + +#endif // ASSERT diff --git a/hotspot/src/share/vm/memory/universe.hpp b/hotspot/src/share/vm/memory/universe.hpp new file mode 100644 index 00000000000..6fd557585fa --- /dev/null +++ b/hotspot/src/share/vm/memory/universe.hpp @@ -0,0 +1,419 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Universe is a name space holding known system classes and objects in the VM. +// +// Loaded classes are accessible through the SystemDictionary. +// +// The object heap is allocated and accessed through Universe, and various allocation +// support is provided. Allocation by the interpreter and compiled code is done inline +// and bails out to Scavenge::invoke_and_allocate. + +class CollectedHeap; +class DeferredObjAllocEvent; + + +// Common parts of a methodOop cache. This cache safely interacts with +// the RedefineClasses API. +// +class CommonMethodOopCache : public CHeapObj { + // We save the klassOop and the idnum of methodOop in order to get + // the current cached methodOop. + private: + klassOop _klass; + int _method_idnum; + + public: + CommonMethodOopCache() { _klass = NULL; _method_idnum = -1; } + ~CommonMethodOopCache() { _klass = NULL; _method_idnum = -1; } + + void init(klassOop k, methodOop m, TRAPS); + klassOop klass() const { return _klass; } + int method_idnum() const { return _method_idnum; } + + // GC support + void oops_do(OopClosure* f) { f->do_oop((oop*)&_klass); } +}; + + +// A helper class for caching a methodOop when the user of the cache +// cares about all versions of the methodOop. +// +class ActiveMethodOopsCache : public CommonMethodOopCache { + // This subclass adds weak references to older versions of the + // methodOop and a query method for a methodOop. + + private: + // If the cached methodOop has not been redefined, then + // _prev_methods will be NULL. If all of the previous + // versions of the method have been collected, then + // _prev_methods can have a length of zero. + GrowableArray* _prev_methods; + + public: + ActiveMethodOopsCache() { _prev_methods = NULL; } + ~ActiveMethodOopsCache(); + + void add_previous_version(const methodOop method); + bool is_same_method(const methodOop method) const; +}; + + +// A helper class for caching a methodOop when the user of the cache +// only cares about the latest version of the methodOop. +// +class LatestMethodOopCache : public CommonMethodOopCache { + // This subclass adds a getter method for the latest methodOop. + + public: + methodOop get_methodOop(); +}; + + +class Universe: AllStatic { + friend class MarkSweep; + friend class oopDesc; + friend class ClassLoader; + friend class Arguments; + friend class SystemDictionary; + friend class VMStructs; + friend class CompactingPermGenGen; + friend class VM_PopulateDumpSharedSpace; + + friend jint universe_init(); + friend void universe2_init(); + friend bool universe_post_init(); + + private: + // Known classes in the VM + static klassOop _boolArrayKlassObj; + static klassOop _byteArrayKlassObj; + static klassOop _charArrayKlassObj; + static klassOop _intArrayKlassObj; + static klassOop _shortArrayKlassObj; + static klassOop _longArrayKlassObj; + static klassOop _singleArrayKlassObj; + static klassOop _doubleArrayKlassObj; + static klassOop _typeArrayKlassObjs[T_VOID+1]; + + static klassOop _objectArrayKlassObj; + + static klassOop _symbolKlassObj; + static klassOop _methodKlassObj; + static klassOop _constMethodKlassObj; + static klassOop _methodDataKlassObj; + static klassOop _klassKlassObj; + static klassOop _arrayKlassKlassObj; + static klassOop _objArrayKlassKlassObj; + static klassOop _typeArrayKlassKlassObj; + static klassOop _instanceKlassKlassObj; + static klassOop _constantPoolKlassObj; + static klassOop _constantPoolCacheKlassObj; + static klassOop _compiledICHolderKlassObj; + static klassOop _systemObjArrayKlassObj; + + // Known objects in the VM + + // Primitive objects + static oop _int_mirror; + static oop _float_mirror; + static oop _double_mirror; + static oop _byte_mirror; + static oop _bool_mirror; + static oop _char_mirror; + static oop _long_mirror; + static oop _short_mirror; + static oop _void_mirror; + + static oop _main_thread_group; // Reference to the main thread group object + static oop _system_thread_group; // Reference to the system thread group object + + static typeArrayOop _the_empty_byte_array; // Canonicalized byte array + static typeArrayOop _the_empty_short_array; // Canonicalized short array + static typeArrayOop _the_empty_int_array; // Canonicalized int array + static objArrayOop _the_empty_system_obj_array; // Canonicalized system obj array + static objArrayOop _the_empty_class_klass_array; // Canonicalized obj array of type java.lang.Class + static objArrayOop _the_array_interfaces_array; // Canonicalized 2-array of cloneable & serializable klasses + static LatestMethodOopCache* _finalizer_register_cache; // static method for registering finalizable objects + static LatestMethodOopCache* _loader_addClass_cache; // method for registering loaded classes in class loader vector + static ActiveMethodOopsCache* _reflect_invoke_cache; // method for security checks + static oop _out_of_memory_error_java_heap; // preallocated error object (no backtrace) + static oop _out_of_memory_error_perm_gen; // preallocated error object (no backtrace) + static oop _out_of_memory_error_array_size;// preallocated error object (no backtrace) + static oop _out_of_memory_error_gc_overhead_limit; // preallocated error object (no backtrace) + + // array of preallocated error objects with backtrace + static objArrayOop _preallocated_out_of_memory_error_array; + + // number of preallocated error objects available for use + static volatile jint _preallocated_out_of_memory_error_avail_count; + + static oop _null_ptr_exception_instance; // preallocated exception object + static oop _arithmetic_exception_instance; // preallocated exception object + static oop _virtual_machine_error_instance; // preallocated exception object + // The object used as an exception dummy when exceptions are thrown for + // the vm thread. + static oop _vm_exception; + + static oop _emptySymbol; // Canonical empty string ("") symbol + + // The particular choice of collected heap. + static CollectedHeap* _collectedHeap; + + // array of dummy objects used with +FullGCAlot + debug_only(static objArrayOop _fullgc_alot_dummy_array;) + // index of next entry to clear + debug_only(static int _fullgc_alot_dummy_next;) + + // Compiler/dispatch support + static int _base_vtable_size; // Java vtbl size of klass Object (in words) + + // Initialization + static bool _bootstrapping; // true during genesis + static bool _fully_initialized; // true after universe_init and initialize_vtables called + + // the array of preallocated errors with backtraces + static objArrayOop preallocated_out_of_memory_errors() { return _preallocated_out_of_memory_error_array; } + + // generate an out of memory error; if possible using an error with preallocated backtrace; + // otherwise return the given default error. + static oop gen_out_of_memory_error(oop default_err); + + // Historic gc information + static size_t _heap_capacity_at_last_gc; + static size_t _heap_used_at_last_gc; + + static jint initialize_heap(); + static void initialize_basic_type_mirrors(TRAPS); + static void fixup_mirrors(TRAPS); + + static void reinitialize_vtable_of(KlassHandle h_k, TRAPS); + static void reinitialize_itables(TRAPS); + static void compute_base_vtable_size(); // compute vtable size of class Object + + static void genesis(TRAPS); // Create the initial world + + // Mirrors for primitive classes (created eagerly) + static oop check_mirror(oop m) { + assert(m != NULL, "mirror not initialized"); + return m; + } + + // Debugging + static int _verify_count; // number of verifies done + // True during call to verify(). Should only be set/cleared in verify(). + static bool _verify_in_progress; + + static void compute_verify_oop_data(); + + public: + // Known classes in the VM + static klassOop boolArrayKlassObj() { return _boolArrayKlassObj; } + static klassOop byteArrayKlassObj() { return _byteArrayKlassObj; } + static klassOop charArrayKlassObj() { return _charArrayKlassObj; } + static klassOop intArrayKlassObj() { return _intArrayKlassObj; } + static klassOop shortArrayKlassObj() { return _shortArrayKlassObj; } + static klassOop longArrayKlassObj() { return _longArrayKlassObj; } + static klassOop singleArrayKlassObj() { return _singleArrayKlassObj; } + static klassOop doubleArrayKlassObj() { return _doubleArrayKlassObj; } + + static klassOop objectArrayKlassObj() { + return _objectArrayKlassObj; + } + + static klassOop typeArrayKlassObj(BasicType t) { + assert((uint)t < T_VOID+1, "range check"); + assert(_typeArrayKlassObjs[t] != NULL, "domain check"); + return _typeArrayKlassObjs[t]; + } + + static klassOop symbolKlassObj() { return _symbolKlassObj; } + static klassOop methodKlassObj() { return _methodKlassObj; } + static klassOop constMethodKlassObj() { return _constMethodKlassObj; } + static klassOop methodDataKlassObj() { return _methodDataKlassObj; } + static klassOop klassKlassObj() { return _klassKlassObj; } + static klassOop arrayKlassKlassObj() { return _arrayKlassKlassObj; } + static klassOop objArrayKlassKlassObj() { return _objArrayKlassKlassObj; } + static klassOop typeArrayKlassKlassObj() { return _typeArrayKlassKlassObj; } + static klassOop instanceKlassKlassObj() { return _instanceKlassKlassObj; } + static klassOop constantPoolKlassObj() { return _constantPoolKlassObj; } + static klassOop constantPoolCacheKlassObj() { return _constantPoolCacheKlassObj; } + static klassOop compiledICHolderKlassObj() { return _compiledICHolderKlassObj; } + static klassOop systemObjArrayKlassObj() { return _systemObjArrayKlassObj; } + + // Known objects in tbe VM + static oop int_mirror() { return check_mirror(_int_mirror); +} + static oop float_mirror() { return check_mirror(_float_mirror); } + static oop double_mirror() { return check_mirror(_double_mirror); } + static oop byte_mirror() { return check_mirror(_byte_mirror); } + static oop bool_mirror() { return check_mirror(_bool_mirror); } + static oop char_mirror() { return check_mirror(_char_mirror); } + static oop long_mirror() { return check_mirror(_long_mirror); } + static oop short_mirror() { return check_mirror(_short_mirror); } + static oop void_mirror() { return check_mirror(_void_mirror); } + + // table of same + static oop _mirrors[T_VOID+1]; + + static oop java_mirror(BasicType t) { + assert((uint)t < T_VOID+1, "range check"); + return check_mirror(_mirrors[t]); + } + static oop main_thread_group() { return _main_thread_group; } + static void set_main_thread_group(oop group) { _main_thread_group = group;} + + static oop system_thread_group() { return _system_thread_group; } + static void set_system_thread_group(oop group) { _system_thread_group = group;} + + static typeArrayOop the_empty_byte_array() { return _the_empty_byte_array; } + static typeArrayOop the_empty_short_array() { return _the_empty_short_array; } + static typeArrayOop the_empty_int_array() { return _the_empty_int_array; } + static objArrayOop the_empty_system_obj_array () { return _the_empty_system_obj_array; } + static objArrayOop the_empty_class_klass_array () { return _the_empty_class_klass_array; } + static objArrayOop the_array_interfaces_array() { return _the_array_interfaces_array; } + static methodOop finalizer_register_method() { return _finalizer_register_cache->get_methodOop(); } + static methodOop loader_addClass_method() { return _loader_addClass_cache->get_methodOop(); } + static ActiveMethodOopsCache* reflect_invoke_cache() { return _reflect_invoke_cache; } + static oop null_ptr_exception_instance() { return _null_ptr_exception_instance; } + static oop arithmetic_exception_instance() { return _arithmetic_exception_instance; } + static oop virtual_machine_error_instance() { return _virtual_machine_error_instance; } + static oop vm_exception() { return _vm_exception; } + static oop emptySymbol() { return _emptySymbol; } + + // OutOfMemoryError support. Returns an error with the required message. The returned error + // may or may not have a backtrace. If error has a backtrace then the stack trace is already + // filled in. + static oop out_of_memory_error_java_heap() { return gen_out_of_memory_error(_out_of_memory_error_java_heap); } + static oop out_of_memory_error_perm_gen() { return gen_out_of_memory_error(_out_of_memory_error_perm_gen); } + static oop out_of_memory_error_array_size() { return gen_out_of_memory_error(_out_of_memory_error_array_size); } + static oop out_of_memory_error_gc_overhead_limit() { return gen_out_of_memory_error(_out_of_memory_error_gc_overhead_limit); } + + // Accessors needed for fast allocation + static klassOop* boolArrayKlassObj_addr() { return &_boolArrayKlassObj; } + static klassOop* byteArrayKlassObj_addr() { return &_byteArrayKlassObj; } + static klassOop* charArrayKlassObj_addr() { return &_charArrayKlassObj; } + static klassOop* intArrayKlassObj_addr() { return &_intArrayKlassObj; } + static klassOop* shortArrayKlassObj_addr() { return &_shortArrayKlassObj; } + static klassOop* longArrayKlassObj_addr() { return &_longArrayKlassObj; } + static klassOop* singleArrayKlassObj_addr() { return &_singleArrayKlassObj; } + static klassOop* doubleArrayKlassObj_addr() { return &_doubleArrayKlassObj; } + + // The particular choice of collected heap. + static CollectedHeap* heap() { return _collectedHeap; } + + // Historic gc information + static size_t get_heap_capacity_at_last_gc() { return _heap_capacity_at_last_gc; } + static size_t get_heap_free_at_last_gc() { return _heap_capacity_at_last_gc - _heap_used_at_last_gc; } + static size_t get_heap_used_at_last_gc() { return _heap_used_at_last_gc; } + static void update_heap_info_at_gc(); + + // Testers + static bool is_bootstrapping() { return _bootstrapping; } + static bool is_fully_initialized() { return _fully_initialized; } + + static inline bool element_type_should_be_aligned(BasicType type); + static inline bool field_type_should_be_aligned(BasicType type); + static bool on_page_boundary(void* addr); + static bool should_fill_in_stack_trace(Handle throwable); + static void check_alignment(uintx size, uintx alignment, const char* name); + + // Finalizer support. + static void run_finalizers_on_exit(); + + // Iteration + + // Apply "f" to the addresses of all the direct heap pointers maintained + // as static fields of "Universe". + static void oops_do(OopClosure* f, bool do_all = false); + + // Apply "f" to all klasses for basic types (classes not present in + // SystemDictionary). + static void basic_type_classes_do(void f(klassOop)); + + // Apply "f" to all system klasses (classes not present in SystemDictionary). + static void system_classes_do(void f(klassOop)); + + // For sharing -- fill in a list of known vtable pointers. + static void init_self_patching_vtbl_list(void** list, int count); + + // Debugging + static bool verify_in_progress() { return _verify_in_progress; } + static void verify(bool allow_dirty = true, bool silent = false); + static int verify_count() { return _verify_count; } + static void print(); + static void print_on(outputStream* st); + static void print_heap_at_SIGBREAK(); + static void print_heap_before_gc() { print_heap_before_gc(gclog_or_tty); } + static void print_heap_after_gc() { print_heap_after_gc(gclog_or_tty); } + static void print_heap_before_gc(outputStream* st); + static void print_heap_after_gc(outputStream* st); + + // Change the number of dummy objects kept reachable by the full gc dummy + // array; this should trigger relocation in a sliding compaction collector. + debug_only(static bool release_fullgc_alot_dummy();) + // The non-oop pattern (see compiledIC.hpp, etc) + static void* non_oop_word(); + + // Oop verification (see MacroAssembler::verify_oop) + static uintptr_t verify_oop_mask() PRODUCT_RETURN0; + static uintptr_t verify_oop_bits() PRODUCT_RETURN0; + static uintptr_t verify_mark_bits() PRODUCT_RETURN0; + static uintptr_t verify_mark_mask() PRODUCT_RETURN0; + static uintptr_t verify_klass_mask() PRODUCT_RETURN0; + static uintptr_t verify_klass_bits() PRODUCT_RETURN0; + + // Flushing and deoptimization + static void flush_dependents_on(instanceKlassHandle dependee); +#ifdef HOTSWAP + // Flushing and deoptimization in case of evolution + static void flush_evol_dependents_on(instanceKlassHandle dependee); +#endif // HOTSWAP + // Support for fullspeed debugging + static void flush_dependents_on_method(methodHandle dependee); + + // Compiler support + static int base_vtable_size() { return _base_vtable_size; } +}; + +class DeferredObjAllocEvent : public CHeapObj { + private: + oop _oop; + size_t _bytesize; + jint _arena_id; + + public: + DeferredObjAllocEvent(const oop o, const size_t s, const jint id) { + _oop = o; + _bytesize = s; + _arena_id = id; + } + + ~DeferredObjAllocEvent() { + } + + jint arena_id() { return _arena_id; } + size_t bytesize() { return _bytesize; } + oop get_oop() { return _oop; } +}; diff --git a/hotspot/src/share/vm/memory/universe.inline.hpp b/hotspot/src/share/vm/memory/universe.inline.hpp new file mode 100644 index 00000000000..a4ac36ea41e --- /dev/null +++ b/hotspot/src/share/vm/memory/universe.inline.hpp @@ -0,0 +1,37 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Check whether an element of a typeArrayOop with the given type must be +// aligned 0 mod 8. The typeArrayOop itself must be aligned at least this +// strongly. + +inline bool Universe::element_type_should_be_aligned(BasicType type) { + return type == T_DOUBLE || type == T_LONG; +} + +// Check whether an object field (static/non-static) of the given type must be aligned 0 mod 8. + +inline bool Universe::field_type_should_be_aligned(BasicType type) { + return type == T_DOUBLE || type == T_LONG; +} diff --git a/hotspot/src/share/vm/memory/watermark.hpp b/hotspot/src/share/vm/memory/watermark.hpp new file mode 100644 index 00000000000..05096e18e1c --- /dev/null +++ b/hotspot/src/share/vm/memory/watermark.hpp @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A water mark points into a space and is used during GC to keep track of +// progress. + +class Space; + +class WaterMark VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + HeapWord* _point; + Space* _space; + public: + // Accessors + Space* space() const { return _space; } + void set_space(Space* s) { _space = s; } + HeapWord* point() const { return _point; } + void set_point(HeapWord* p) { _point = p; } + + // Constructors + WaterMark(Space* s, HeapWord* p) : _space(s), _point(p) {}; + WaterMark() : _space(NULL), _point(NULL) {}; +}; + +inline bool operator==(const WaterMark& x, const WaterMark& y) { + return (x.point() == y.point()) && (x.space() == y.space()); +} + +inline bool operator!=(const WaterMark& x, const WaterMark& y) { + return !(x == y); +} diff --git a/hotspot/src/share/vm/oops/arrayKlass.cpp b/hotspot/src/share/vm/oops/arrayKlass.cpp new file mode 100644 index 00000000000..8dde4799a89 --- /dev/null +++ b/hotspot/src/share/vm/oops/arrayKlass.cpp @@ -0,0 +1,199 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_arrayKlass.cpp.incl" + +int arrayKlass::object_size(int header_size) const { + // size of an array klass object + assert(header_size <= instanceKlass::header_size(), "bad header size"); + // If this assert fails, see comments in base_create_array_klass. + header_size = instanceKlass::header_size(); +#ifdef _LP64 + int size = header_size + align_object_offset(vtable_length()); +#else + int size = header_size + vtable_length(); +#endif + return align_object_size(size); +} + + +klassOop arrayKlass::java_super() const { + if (super() == NULL) return NULL; // bootstrap case + // Array klasses have primary supertypes which are not reported to Java. + // Example super chain: String[][] -> Object[][] -> Object[] -> Object + return SystemDictionary::object_klass(); +} + + +oop arrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) { + ShouldNotReachHere(); + return NULL; +} + +methodOop arrayKlass::uncached_lookup_method(symbolOop name, symbolOop signature) const { + // There are no methods in an array klass but the super class (Object) has some + assert(super(), "super klass must be present"); + return Klass::cast(super())->uncached_lookup_method(name, signature); +} + + +arrayKlassHandle arrayKlass::base_create_array_klass( +const Klass_vtbl& cplusplus_vtbl, int header_size, KlassHandle klass, TRAPS) { + // Allocation + // Note: because the Java vtable must start at the same offset in all klasses, + // we must insert filler fields into arrayKlass to make it the same size as instanceKlass. + // If this assert fails, add filler to instanceKlass to make it bigger. + assert(header_size <= instanceKlass::header_size(), + "array klasses must be same size as instanceKlass"); + header_size = instanceKlass::header_size(); + // Arrays don't add any new methods, so their vtable is the same size as + // the vtable of klass Object. + int vtable_size = Universe::base_vtable_size(); + arrayKlassHandle k; + KlassHandle base_klass = Klass::base_create_klass(klass, + header_size + vtable_size, + cplusplus_vtbl, CHECK_(k)); + + // No safepoint should be possible until the handle's + // target below becomes parsable + No_Safepoint_Verifier no_safepoint; + k = arrayKlassHandle(THREAD, base_klass()); + + assert(!k()->is_parsable(), "not expecting parsability yet."); + k->set_super(Universe::is_bootstrapping() ? (klassOop)NULL : SystemDictionary::object_klass()); + k->set_layout_helper(Klass::_lh_neutral_value); + k->set_dimension(1); + k->set_higher_dimension(NULL); + k->set_lower_dimension(NULL); + k->set_component_mirror(NULL); + k->set_vtable_length(vtable_size); + k->set_is_cloneable(); // All arrays are considered to be cloneable (See JLS 20.1.5) + + assert(k()->is_parsable(), "should be parsable here."); + // Make sure size calculation is right + assert(k()->size() == align_object_size(header_size + vtable_size), "wrong size for object"); + + return k; +} + + +// Initialization of vtables and mirror object is done separatly from base_create_array_klass, +// since a GC can happen. At this point all instance variables of the arrayKlass must be setup. +void arrayKlass::complete_create_array_klass(arrayKlassHandle k, KlassHandle super_klass, TRAPS) { + ResourceMark rm(THREAD); + k->initialize_supers(super_klass(), CHECK); + k->vtable()->initialize_vtable(false, CHECK); + java_lang_Class::create_mirror(k, CHECK); +} + +objArrayOop arrayKlass::compute_secondary_supers(int num_extra_slots, TRAPS) { + // interfaces = { cloneable_klass, serializable_klass }; + assert(num_extra_slots == 0, "sanity of primitive array type"); + // Must share this for correct bootstrapping! + return Universe::the_array_interfaces_array(); +} + +bool arrayKlass::compute_is_subtype_of(klassOop k) { + // An array is a subtype of Serializable, Clonable, and Object + return k == SystemDictionary::object_klass() + || k == SystemDictionary::cloneable_klass() + || k == SystemDictionary::serializable_klass(); +} + + +inline intptr_t* arrayKlass::start_of_vtable() const { + // all vtables start at the same place, that's why we use instanceKlass::header_size here + return ((intptr_t*)as_klassOop()) + instanceKlass::header_size(); +} + + +klassVtable* arrayKlass::vtable() const { + KlassHandle kh(Thread::current(), as_klassOop()); + return new klassVtable(kh, start_of_vtable(), vtable_length() / vtableEntry::size()); +} + + +objArrayOop arrayKlass::allocate_arrayArray(int n, int length, TRAPS) { + if (length < 0) { + THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); + } + if (length > arrayOopDesc::max_array_length(T_ARRAY)) { + THROW_OOP_0(Universe::out_of_memory_error_array_size()); + } + int size = objArrayOopDesc::object_size(length); + klassOop k = array_klass(n+dimension(), CHECK_0); + arrayKlassHandle ak (THREAD, k); + objArrayOop o = + (objArrayOop)CollectedHeap::array_allocate(ak, size, length, CHECK_0); + // initialization to NULL not necessary, area already cleared + return o; +} + + +void arrayKlass::array_klasses_do(void f(klassOop k)) { + klassOop k = as_klassOop(); + // Iterate over this array klass and all higher dimensions + while (k != NULL) { + f(k); + k = arrayKlass::cast(k)->higher_dimension(); + } +} + + +void arrayKlass::with_array_klasses_do(void f(klassOop k)) { + array_klasses_do(f); +} + +// JVM support + +jint arrayKlass::compute_modifier_flags(TRAPS) const { + return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC; +} + +// JVMTI support + +jint arrayKlass::jvmti_class_status() const { + return JVMTI_CLASS_STATUS_ARRAY; +} + +#ifndef PRODUCT + +// Printing + +void arrayKlass::oop_print_on(oop obj, outputStream* st) { + assert(obj->is_array(), "must be array"); + Klass::oop_print_on(obj, st); + st->print_cr(" - length: %d", arrayOop(obj)->length()); +} + +#endif + +// Verification + +void arrayKlass::oop_verify_on(oop obj, outputStream* st) { + guarantee(obj->is_array(), "must be array"); + arrayOop a = arrayOop(obj); + guarantee(a->length() >= 0, "array with negative length?"); +} diff --git a/hotspot/src/share/vm/oops/arrayKlass.hpp b/hotspot/src/share/vm/oops/arrayKlass.hpp new file mode 100644 index 00000000000..b2bc0862c73 --- /dev/null +++ b/hotspot/src/share/vm/oops/arrayKlass.hpp @@ -0,0 +1,134 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// arrayKlass is the abstract baseclass for all array classes + +class arrayKlass: public Klass { + friend class VMStructs; + private: + int _dimension; // This is n'th-dimensional array. + klassOop _higher_dimension; // Refers the (n+1)'th-dimensional array (if present). + klassOop _lower_dimension; // Refers the (n-1)'th-dimensional array (if present). + int _vtable_len; // size of vtable for this klass + juint _alloc_size; // allocation profiling support + oop _component_mirror; // component type, as a java/lang/Class + + public: + // Testing operation + bool oop_is_array() const { return true; } + + // Instance variables + int dimension() const { return _dimension; } + void set_dimension(int dimension) { _dimension = dimension; } + + klassOop higher_dimension() const { return _higher_dimension; } + void set_higher_dimension(klassOop k) { oop_store_without_check((oop*) &_higher_dimension, (oop) k); } + oop* adr_higher_dimension() { return (oop*)&this->_higher_dimension;} + + klassOop lower_dimension() const { return _lower_dimension; } + void set_lower_dimension(klassOop k) { oop_store_without_check((oop*) &_lower_dimension, (oop) k); } + oop* adr_lower_dimension() { return (oop*)&this->_lower_dimension;} + + // Allocation profiling support + juint alloc_size() const { return _alloc_size; } + void set_alloc_size(juint n) { _alloc_size = n; } + + // offset of first element, including any padding for the sake of alignment + int array_header_in_bytes() const { return layout_helper_header_size(layout_helper()); } + int log2_element_size() const { return layout_helper_log2_element_size(layout_helper()); } + // type of elements (T_OBJECT for both oop arrays and array-arrays) + BasicType element_type() const { return layout_helper_element_type(layout_helper()); } + + oop component_mirror() const { return _component_mirror; } + void set_component_mirror(oop m) { oop_store((oop*) &_component_mirror, m); } + oop* adr_component_mirror() { return (oop*)&this->_component_mirror;} + + // Compiler/Interpreter offset + static ByteSize component_mirror_offset() { return byte_offset_of(arrayKlass, _component_mirror); } + + virtual klassOop java_super() const;//{ return SystemDictionary::object_klass(); } + + // Allocation + // Sizes points to the first dimension of the array, subsequent dimensions + // are always in higher memory. The callers of these set that up. + virtual oop multi_allocate(int rank, jint* sizes, TRAPS); + objArrayOop allocate_arrayArray(int n, int length, TRAPS); + + // Lookup operations + methodOop uncached_lookup_method(symbolOop name, symbolOop signature) const; + + // Casting from klassOop + static arrayKlass* cast(klassOop k) { + Klass* kp = k->klass_part(); + assert(kp->null_vtbl() || kp->oop_is_array(), "cast to arrayKlass"); + return (arrayKlass*) kp; + } + + objArrayOop compute_secondary_supers(int num_extra_slots, TRAPS); + bool compute_is_subtype_of(klassOop k); + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(arrayKlass)/HeapWordSize; } + int object_size(int header_size) const; + + bool object_is_parsable() const { return _vtable_len > 0; } + + // Java vtable + klassVtable* vtable() const; // return new klassVtable + int vtable_length() const { return _vtable_len; } + static int base_vtable_length() { return Universe::base_vtable_size(); } + void set_vtable_length(int len) { assert(len == base_vtable_length(), "bad length"); _vtable_len = len; } + protected: + inline intptr_t* start_of_vtable() const; + + public: + // Iterators + void array_klasses_do(void f(klassOop k)); + void with_array_klasses_do(void f(klassOop k)); + + // Shared creation method + static arrayKlassHandle base_create_array_klass( + const Klass_vtbl& vtbl, + int header_size, KlassHandle klass, + TRAPS); + // Return a handle. + static void complete_create_array_klass(arrayKlassHandle k, KlassHandle super_klass, TRAPS); + + public: + // jvm support + jint compute_modifier_flags(TRAPS) const; + + public: + // JVMTI support + jint jvmti_class_status() const; + +#ifndef PRODUCT + public: + // Printing + void oop_print_on(oop obj, outputStream* st); +#endif + public: + // Verification + void oop_verify_on(oop obj, outputStream* st); +}; diff --git a/hotspot/src/share/vm/oops/arrayKlassKlass.cpp b/hotspot/src/share/vm/oops/arrayKlassKlass.cpp new file mode 100644 index 00000000000..1757aae8a1f --- /dev/null +++ b/hotspot/src/share/vm/oops/arrayKlassKlass.cpp @@ -0,0 +1,190 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_arrayKlassKlass.cpp.incl" + + +klassOop arrayKlassKlass::create_klass(TRAPS) { + arrayKlassKlass o; + KlassHandle h_this_klass(THREAD, Universe::klassKlassObj()); + KlassHandle k = base_create_klass(h_this_klass, header_size(), o.vtbl_value(), CHECK_NULL); + // Make sure size calculation is right + assert(k()->size() == align_object_size(header_size()), "wrong size for object"); + java_lang_Class::create_mirror(k, CHECK_NULL); // Allocate mirror, make links + return k(); +} + +bool arrayKlassKlass::oop_is_parsable(oop obj) const { + assert(obj->is_klass(), "must be klass"); + arrayKlass* ak = arrayKlass::cast(klassOop(obj)); + return (!ak->null_vtbl()) && ak->object_is_parsable(); +} + +void arrayKlassKlass::oop_follow_contents(oop obj) { + assert(obj->is_klass(), "must be klass"); + arrayKlass* ak = arrayKlass::cast(klassOop(obj)); + MarkSweep::mark_and_push(ak->adr_component_mirror()); + MarkSweep::mark_and_push(ak->adr_lower_dimension()); + MarkSweep::mark_and_push(ak->adr_higher_dimension()); + { + HandleMark hm; + ak->vtable()->oop_follow_contents(); + } + klassKlass::oop_follow_contents(obj); +} + +#ifndef SERIALGC +void arrayKlassKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert(obj->is_klass(), "must be klass"); + arrayKlass* ak = arrayKlass::cast(klassOop(obj)); + PSParallelCompact::mark_and_push(cm, ak->adr_component_mirror()); + PSParallelCompact::mark_and_push(cm, ak->adr_lower_dimension()); + PSParallelCompact::mark_and_push(cm, ak->adr_higher_dimension()); + { + HandleMark hm; + ak->vtable()->oop_follow_contents(cm); + } + klassKlass::oop_follow_contents(cm, obj); +} +#endif // SERIALGC + + +int arrayKlassKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_klass(), "must be klass"); + arrayKlass* ak = arrayKlass::cast(klassOop(obj)); + MarkSweep::adjust_pointer(ak->adr_component_mirror()); + MarkSweep::adjust_pointer(ak->adr_lower_dimension()); + MarkSweep::adjust_pointer(ak->adr_higher_dimension()); + { + HandleMark hm; + ak->vtable()->oop_adjust_pointers(); + } + return klassKlass::oop_adjust_pointers(obj); +} + + +int arrayKlassKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert(obj->is_klass(), "must be klass"); + arrayKlass* ak = arrayKlass::cast(klassOop(obj)); + blk->do_oop(ak->adr_component_mirror()); + blk->do_oop(ak->adr_lower_dimension()); + blk->do_oop(ak->adr_higher_dimension()); + ak->vtable()->oop_oop_iterate(blk); + return klassKlass::oop_oop_iterate(obj, blk); +} + + +int arrayKlassKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + assert(obj->is_klass(), "must be klass"); + arrayKlass* ak = arrayKlass::cast(klassOop(obj)); + blk->do_oop(ak->adr_component_mirror()); + blk->do_oop(ak->adr_lower_dimension()); + blk->do_oop(ak->adr_higher_dimension()); + ak->vtable()->oop_oop_iterate_m(blk, mr); + return klassKlass::oop_oop_iterate_m(obj, blk, mr); +} + +#ifndef SERIALGC +void arrayKlassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(obj->blueprint()->oop_is_arrayKlass(),"must be an array klass"); +} + +void arrayKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(obj->blueprint()->oop_is_arrayKlass(),"must be an array klass"); +} + +int arrayKlassKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert(obj->is_klass(), "must be klass"); + arrayKlass* ak = arrayKlass::cast(klassOop(obj)); + PSParallelCompact::adjust_pointer(ak->adr_component_mirror()); + PSParallelCompact::adjust_pointer(ak->adr_lower_dimension()); + PSParallelCompact::adjust_pointer(ak->adr_higher_dimension()); + { + HandleMark hm; + ak->vtable()->oop_update_pointers(cm); + } + return klassKlass::oop_update_pointers(cm, obj); +} + +int +arrayKlassKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, HeapWord* end_addr) { + assert(obj->is_klass(), "must be klass"); + arrayKlass* ak = arrayKlass::cast(klassOop(obj)); + + oop* p; + p = ak->adr_component_mirror(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = ak->adr_lower_dimension(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = ak->adr_higher_dimension(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + + { + HandleMark hm; + ak->vtable()->oop_update_pointers(cm, beg_addr, end_addr); + } + return klassKlass::oop_update_pointers(cm, obj, beg_addr, end_addr); +} +#endif // SERIALGC + +#ifndef PRODUCT + +// Printing + +void arrayKlassKlass::oop_print_on(oop obj, outputStream* st) { + assert(obj->is_klass(), "must be klass"); + klassKlass::oop_print_on(obj, st); +} + + +void arrayKlassKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_klass(), "must be klass"); + arrayKlass* ak = arrayKlass::cast(klassOop(obj)); + for(int index = 0; index < ak->dimension(); index++) { + st->print("[]"); + } +} +#endif + + +const char* arrayKlassKlass::internal_name() const { + return "{array class}"; +} + +void arrayKlassKlass::oop_verify_on(oop obj, outputStream* st) { + klassKlass::oop_verify_on(obj, st); + + arrayKlass* ak = arrayKlass::cast(klassOop(obj)); + if (!obj->partially_loaded()) { + if (ak->component_mirror() != NULL) + guarantee(ak->component_mirror()->klass(), "should have a class"); + if (ak->lower_dimension() != NULL) + guarantee(ak->lower_dimension()->klass(), "should have a class"); + if (ak->higher_dimension() != NULL) + guarantee(ak->higher_dimension()->klass(), "should have a class"); + } +} diff --git a/hotspot/src/share/vm/oops/arrayKlassKlass.hpp b/hotspot/src/share/vm/oops/arrayKlassKlass.hpp new file mode 100644 index 00000000000..1d98d21745a --- /dev/null +++ b/hotspot/src/share/vm/oops/arrayKlassKlass.hpp @@ -0,0 +1,69 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// arrayKlassKlass is the abstract baseclass for all array class classes + +class arrayKlassKlass : public klassKlass { + public: + // Testing + bool oop_is_arrayKlass() const { return true; } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(arrayKlassKlass); + static klassOop create_klass(TRAPS); + + // Casting from klassOop + static arrayKlassKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_klass(), "cast to arrayKlassKlass"); + return (arrayKlassKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(arrayKlassKlass)/HeapWordSize; } + int object_size() const { return align_object_size(header_size()); } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + bool oop_is_parsable(oop obj) const; + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on(oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); +#endif + + public: + // Verification + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); +}; diff --git a/hotspot/src/share/vm/oops/arrayOop.cpp b/hotspot/src/share/vm/oops/arrayOop.cpp new file mode 100644 index 00000000000..28d293f6052 --- /dev/null +++ b/hotspot/src/share/vm/oops/arrayOop.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_arrayOop.cpp.incl" + +// <> diff --git a/hotspot/src/share/vm/oops/arrayOop.hpp b/hotspot/src/share/vm/oops/arrayOop.hpp new file mode 100644 index 00000000000..e9bde1d6c8e --- /dev/null +++ b/hotspot/src/share/vm/oops/arrayOop.hpp @@ -0,0 +1,70 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// arrayOopDesc is the abstract baseclass for all arrays. + +class arrayOopDesc : public oopDesc { + friend class VMStructs; + private: + int _length; // number of elements in the array + + public: + // Interpreter/Compiler offsets + static int length_offset_in_bytes() { return offset_of(arrayOopDesc, _length); } + static int base_offset_in_bytes(BasicType type) { return header_size(type) * HeapWordSize; } + + // Returns the address of the first element. + void* base(BasicType type) const { return (void*) (((intptr_t) this) + base_offset_in_bytes(type)); } + + // Tells whether index is within bounds. + bool is_within_bounds(int index) const { return 0 <= index && index < length(); } + + // Accessores for instance variable + int length() const { return _length; } + void set_length(int length) { _length = length; } + + // Header size computation. + // Should only be called with constants as argument (will not constant fold otherwise) + static int header_size(BasicType type) { + return Universe::element_type_should_be_aligned(type) + ? align_object_size(sizeof(arrayOopDesc)/HeapWordSize) + : sizeof(arrayOopDesc)/HeapWordSize; + } + + // This method returns the maximum length that can passed into + // typeArrayOop::object_size(scale, length, header_size) without causing an + // overflow. We substract an extra 2*wordSize to guard against double word + // alignments. It gets the scale from the type2aelembytes array. + static int32_t max_array_length(BasicType type) { + assert(type >= 0 && type < T_CONFLICT, "wrong type"); + assert(type2aelembytes[type] != 0, "wrong type"); + // We use max_jint, since object_size is internally represented by an 'int' + // This gives us an upper bound of max_jint words for the size of the oop. + int32_t max_words = (max_jint - header_size(type) - 2); + int elembytes = (type == T_OBJECT) ? T_OBJECT_aelem_bytes : type2aelembytes[type]; + jlong len = ((jlong)max_words * HeapWordSize) / elembytes; + return (len > max_jint) ? max_jint : (int32_t)len; + } + +}; diff --git a/hotspot/src/share/vm/oops/compiledICHolderKlass.cpp b/hotspot/src/share/vm/oops/compiledICHolderKlass.cpp new file mode 100644 index 00000000000..434205d3ed3 --- /dev/null +++ b/hotspot/src/share/vm/oops/compiledICHolderKlass.cpp @@ -0,0 +1,191 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_compiledICHolderKlass.cpp.incl" + +klassOop compiledICHolderKlass::create_klass(TRAPS) { + compiledICHolderKlass o; + KlassHandle h_this_klass(THREAD, Universe::klassKlassObj()); + KlassHandle k = base_create_klass(h_this_klass, header_size(), o.vtbl_value(), CHECK_NULL); + // Make sure size calculation is right + assert(k()->size() == align_object_size(header_size()), "wrong size for object"); + java_lang_Class::create_mirror(k, CHECK_NULL); // Allocate mirror + return k(); +} + + +compiledICHolderOop compiledICHolderKlass::allocate(TRAPS) { + KlassHandle h_k(THREAD, as_klassOop()); + int size = compiledICHolderOopDesc::object_size(); + compiledICHolderOop c = (compiledICHolderOop) + CollectedHeap::permanent_obj_allocate(h_k, size, CHECK_NULL); + c->set_holder_method(NULL); + c->set_holder_klass(NULL); + return c; +} + + +int compiledICHolderKlass::oop_size(oop obj) const { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); + return compiledICHolderOop(obj)->object_size(); +} + +void compiledICHolderKlass::oop_follow_contents(oop obj) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); + compiledICHolderOop c = compiledICHolderOop(obj); + + obj->follow_header(); + MarkSweep::mark_and_push(c->adr_holder_method()); + MarkSweep::mark_and_push(c->adr_holder_klass()); +} + +#ifndef SERIALGC +void compiledICHolderKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); + compiledICHolderOop c = compiledICHolderOop(obj); + + obj->follow_header(cm); + PSParallelCompact::mark_and_push(cm, c->adr_holder_method()); + PSParallelCompact::mark_and_push(cm, c->adr_holder_klass()); +} +#endif // SERIALGC + + +int compiledICHolderKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); + compiledICHolderOop c = compiledICHolderOop(obj); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = c->object_size(); + + obj->oop_iterate_header(blk); + blk->do_oop(c->adr_holder_method()); + blk->do_oop(c->adr_holder_klass()); + return size; +} + +int compiledICHolderKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, + MemRegion mr) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); + compiledICHolderOop c = compiledICHolderOop(obj); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = c->object_size(); + + obj->oop_iterate_header(blk, mr); + + oop* adr; + adr = c->adr_holder_method(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = c->adr_holder_klass(); + if (mr.contains(adr)) blk->do_oop(adr); + return size; +} + + +int compiledICHolderKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); + compiledICHolderOop c = compiledICHolderOop(obj); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = c->object_size(); + + MarkSweep::adjust_pointer(c->adr_holder_method()); + MarkSweep::adjust_pointer(c->adr_holder_klass()); + obj->adjust_header(); + return size; +} + +#ifndef SERIALGC +void compiledICHolderKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); +} + +void compiledICHolderKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); +} + +int compiledICHolderKlass::oop_update_pointers(ParCompactionManager* cm, + oop obj) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); + compiledICHolderOop c = compiledICHolderOop(obj); + + PSParallelCompact::adjust_pointer(c->adr_holder_method()); + PSParallelCompact::adjust_pointer(c->adr_holder_klass()); + return c->object_size(); +} + +int compiledICHolderKlass::oop_update_pointers(ParCompactionManager* cm, + oop obj, + HeapWord* beg_addr, + HeapWord* end_addr) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); + compiledICHolderOop c = compiledICHolderOop(obj); + + oop* p; + p = c->adr_holder_method(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = c->adr_holder_klass(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + return c->object_size(); +} +#endif // SERIALGC + +#ifndef PRODUCT + +// Printing + +void compiledICHolderKlass::oop_print_on(oop obj, outputStream* st) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); + Klass::oop_print_on(obj, st); + compiledICHolderOop c = compiledICHolderOop(obj); + st->print(" - method: "); c->holder_method()->print_value_on(st); st->cr(); + st->print(" - klass: "); c->holder_klass()->print_value_on(st); st->cr(); +} + + +void compiledICHolderKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_compiledICHolder(), "must be compiledICHolder"); + Klass::oop_print_value_on(obj, st); +} +#endif + +const char* compiledICHolderKlass::internal_name() const { + return "{compiledICHolder}"; +} + +// Verification + +void compiledICHolderKlass::oop_verify_on(oop obj, outputStream* st) { + Klass::oop_verify_on(obj, st); + guarantee(obj->is_compiledICHolder(), "must be compiledICHolder"); + compiledICHolderOop c = compiledICHolderOop(obj); + guarantee(c->is_perm(), "should be in permspace"); + guarantee(c->holder_method()->is_perm(), "should be in permspace"); + guarantee(c->holder_method()->is_method(), "should be method"); + guarantee(c->holder_klass()->is_perm(), "should be in permspace"); + guarantee(c->holder_klass()->is_klass(), "should be klass"); +} diff --git a/hotspot/src/share/vm/oops/compiledICHolderKlass.hpp b/hotspot/src/share/vm/oops/compiledICHolderKlass.hpp new file mode 100644 index 00000000000..3d704773945 --- /dev/null +++ b/hotspot/src/share/vm/oops/compiledICHolderKlass.hpp @@ -0,0 +1,83 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class CMSIsAliveClosure; + +// a compiledICHolderKlass is the klass of a compiledICHolderOop + +class compiledICHolderKlass : public Klass { + friend class VMStructs; + private: + juint _alloc_size; // allocation profiling support + public: + // Testing + bool oop_is_compiledICHolder() const { return true; } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(compiledICHolderKlass); + compiledICHolderOop allocate(TRAPS); + static klassOop create_klass(TRAPS); + + // Sizing + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + + // Casting from klassOop + static compiledICHolderKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_compiledICHolder(), "cast to compiledICHolderKlass"); + return (compiledICHolderKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(compiledICHolderKlass)/HeapWordSize; } + int object_size() const { return align_object_size(header_size()); } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Allocation profiling support + juint alloc_size() const { return _alloc_size; } + void set_alloc_size(juint n) { _alloc_size = n; } + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on (oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); +#endif + + public: + // Verification + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); + +}; diff --git a/hotspot/src/share/vm/oops/compiledICHolderOop.cpp b/hotspot/src/share/vm/oops/compiledICHolderOop.cpp new file mode 100644 index 00000000000..cb17561b0d5 --- /dev/null +++ b/hotspot/src/share/vm/oops/compiledICHolderOop.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_compiledICHolderOop.cpp.incl" + +// <> diff --git a/hotspot/src/share/vm/oops/compiledICHolderOop.hpp b/hotspot/src/share/vm/oops/compiledICHolderOop.hpp new file mode 100644 index 00000000000..dd96c56c997 --- /dev/null +++ b/hotspot/src/share/vm/oops/compiledICHolderOop.hpp @@ -0,0 +1,56 @@ +/* + * Copyright 1998-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A compiledICHolderOop is a helper object for the inline cache implementation. +// It holds an intermediate value (method+klass pair) used when converting from +// compiled to an interpreted call. +// +// compiledICHolderOops are always allocated permanent (to avoid traversing the +// codeCache during scavenge). + + +class compiledICHolderOopDesc : public oopDesc { + friend class VMStructs; + private: + methodOop _holder_method; + klassOop _holder_klass; // to avoid name conflict with oopDesc::_klass + public: + // accessors + methodOop holder_method() const { return _holder_method; } + klassOop holder_klass() const { return _holder_klass; } + + void set_holder_method(methodOop m) { oop_store_without_check((oop*)&_holder_method, (oop)m); } + void set_holder_klass(klassOop k) { oop_store_without_check((oop*)&_holder_klass, (oop)k); } + + static int header_size() { return sizeof(compiledICHolderOopDesc)/HeapWordSize; } + static int object_size() { return align_object_size(header_size()); } + + // interpreter support (offsets in bytes) + static int holder_method_offset() { return offset_of(compiledICHolderOopDesc, _holder_method); } + static int holder_klass_offset() { return offset_of(compiledICHolderOopDesc, _holder_klass); } + + // GC support + oop* adr_holder_method() const { return (oop*)&_holder_method; } + oop* adr_holder_klass() const { return (oop*)&_holder_klass; } +}; diff --git a/hotspot/src/share/vm/oops/constMethodKlass.cpp b/hotspot/src/share/vm/oops/constMethodKlass.cpp new file mode 100644 index 00000000000..802c4430afa --- /dev/null +++ b/hotspot/src/share/vm/oops/constMethodKlass.cpp @@ -0,0 +1,301 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_constMethodKlass.cpp.incl" + + +klassOop constMethodKlass::create_klass(TRAPS) { + constMethodKlass o; + KlassHandle h_this_klass(THREAD, Universe::klassKlassObj()); + KlassHandle k = base_create_klass(h_this_klass, header_size(), + o.vtbl_value(), CHECK_NULL); + // Make sure size calculation is right + assert(k()->size() == align_object_size(header_size()), + "wrong size for object"); + //java_lang_Class::create_mirror(k, CHECK_NULL); // Allocate mirror + return k(); +} + + +int constMethodKlass::oop_size(oop obj) const { + assert(obj->is_constMethod(), "must be constMethod oop"); + return constMethodOop(obj)->object_size(); +} + +bool constMethodKlass::oop_is_parsable(oop obj) const { + assert(obj->is_constMethod(), "must be constMethod oop"); + return constMethodOop(obj)->object_is_parsable(); +} + +constMethodOop constMethodKlass::allocate(int byte_code_size, + int compressed_line_number_size, + int localvariable_table_length, + int checked_exceptions_length, + TRAPS) { + + int size = constMethodOopDesc::object_size(byte_code_size, + compressed_line_number_size, + localvariable_table_length, + checked_exceptions_length); + KlassHandle h_k(THREAD, as_klassOop()); + constMethodOop cm = (constMethodOop) + CollectedHeap::permanent_obj_allocate(h_k, size, CHECK_NULL); + assert(!cm->is_parsable(), "Not yet safely parsable"); + No_Safepoint_Verifier no_safepoint; + cm->set_interpreter_kind(Interpreter::invalid); + cm->init_fingerprint(); + cm->set_method(NULL); + cm->set_stackmap_data(NULL); + cm->set_exception_table(NULL); + cm->set_code_size(byte_code_size); + cm->set_constMethod_size(size); + cm->set_inlined_tables_length(checked_exceptions_length, + compressed_line_number_size, + localvariable_table_length); + assert(cm->size() == size, "wrong size for object"); + cm->set_partially_loaded(); + assert(cm->is_parsable(), "Is safely parsable by gc"); + return cm; +} + +void constMethodKlass::oop_follow_contents(oop obj) { + assert (obj->is_constMethod(), "object must be constMethod"); + constMethodOop cm = constMethodOop(obj); + MarkSweep::mark_and_push(cm->adr_method()); + MarkSweep::mark_and_push(cm->adr_stackmap_data()); + MarkSweep::mark_and_push(cm->adr_exception_table()); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constMethodKlassObj never moves. +} + +#ifndef SERIALGC +void constMethodKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert (obj->is_constMethod(), "object must be constMethod"); + constMethodOop cm_oop = constMethodOop(obj); + PSParallelCompact::mark_and_push(cm, cm_oop->adr_method()); + PSParallelCompact::mark_and_push(cm, cm_oop->adr_stackmap_data()); + PSParallelCompact::mark_and_push(cm, cm_oop->adr_exception_table()); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constMethodKlassObj never moves. +} +#endif // SERIALGC + +int constMethodKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert (obj->is_constMethod(), "object must be constMethod"); + constMethodOop cm = constMethodOop(obj); + blk->do_oop(cm->adr_method()); + blk->do_oop(cm->adr_stackmap_data()); + blk->do_oop(cm->adr_exception_table()); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = cm->object_size(); + return size; +} + + +int constMethodKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + assert (obj->is_constMethod(), "object must be constMethod"); + constMethodOop cm = constMethodOop(obj); + oop* adr; + adr = cm->adr_method(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = cm->adr_stackmap_data(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = cm->adr_exception_table(); + if (mr.contains(adr)) blk->do_oop(adr); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = cm->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constMethodKlassObj never moves. + return size; +} + + +int constMethodKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_constMethod(), "should be constMethod"); + constMethodOop cm = constMethodOop(obj); + MarkSweep::adjust_pointer(cm->adr_method()); + MarkSweep::adjust_pointer(cm->adr_stackmap_data()); + MarkSweep::adjust_pointer(cm->adr_exception_table()); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = cm->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constMethodKlassObj never moves. + return size; +} + +#ifndef SERIALGC +void constMethodKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_constMethod(), "should be constMethod"); +} + +void constMethodKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_constMethod(), "should be constMethod"); +} + +int constMethodKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert(obj->is_constMethod(), "should be constMethod"); + constMethodOop cm_oop = constMethodOop(obj); +#if 0 + PSParallelCompact::adjust_pointer(cm_oop->adr_method()); + PSParallelCompact::adjust_pointer(cm_oop->adr_exception_table()); + PSParallelCompact::adjust_pointer(cm_oop->adr_stackmap_data()); +#endif + oop* const beg_oop = cm_oop->oop_block_beg(); + oop* const end_oop = cm_oop->oop_block_end(); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } + return cm_oop->object_size(); +} + +int constMethodKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, + HeapWord* end_addr) { + assert(obj->is_constMethod(), "should be constMethod"); + constMethodOop cm_oop = constMethodOop(obj); + + oop* const beg_oop = MAX2((oop*)beg_addr, cm_oop->oop_block_beg()); + oop* const end_oop = MIN2((oop*)end_addr, cm_oop->oop_block_end()); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } + + return cm_oop->object_size(); +} +#endif // SERIALGC + +#ifndef PRODUCT + +// Printing + +void constMethodKlass::oop_print_on(oop obj, outputStream* st) { + ResourceMark rm; + assert(obj->is_constMethod(), "must be constMethod"); + Klass::oop_print_on(obj, st); + constMethodOop m = constMethodOop(obj); + st->print(" - method: " INTPTR_FORMAT " ", (address)m->method()); + m->method()->print_value_on(st); st->cr(); + st->print(" - exceptions: " INTPTR_FORMAT "\n", (address)m->exception_table()); + if (m->has_stackmap_table()) { + st->print(" - stackmap data: "); + m->stackmap_data()->print_value_on(st); + st->cr(); + } +} + + +// Short version of printing constMethodOop - just print the name of the +// method it belongs to. +void constMethodKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_constMethod(), "must be constMethod"); + constMethodOop m = constMethodOop(obj); + st->print(" const part of method " ); + m->method()->print_value_on(st); +} + +#endif // PRODUCT + +const char* constMethodKlass::internal_name() const { + return "{constMethod}"; +} + + +// Verification + +void constMethodKlass::oop_verify_on(oop obj, outputStream* st) { + Klass::oop_verify_on(obj, st); + guarantee(obj->is_constMethod(), "object must be constMethod"); + constMethodOop m = constMethodOop(obj); + guarantee(m->is_perm(), "should be in permspace"); + + // Verification can occur during oop construction before the method or + // other fields have been initialized. + if (!obj->partially_loaded()) { + guarantee(m->method()->is_perm(), "should be in permspace"); + guarantee(m->method()->is_method(), "should be method"); + typeArrayOop stackmap_data = m->stackmap_data(); + guarantee(stackmap_data == NULL || + stackmap_data->is_perm(), "should be in permspace"); + guarantee(m->exception_table()->is_perm(), "should be in permspace"); + guarantee(m->exception_table()->is_typeArray(), "should be type array"); + + address m_end = (address)((oop*) m + m->size()); + address compressed_table_start = m->code_end(); + guarantee(compressed_table_start <= m_end, "invalid method layout"); + address compressed_table_end = compressed_table_start; + // Verify line number table + if (m->has_linenumber_table()) { + CompressedLineNumberReadStream stream(m->compressed_linenumber_table()); + while (stream.read_pair()) { + guarantee(stream.bci() >= 0 && stream.bci() <= m->code_size(), "invalid bci in line number table"); + } + compressed_table_end += stream.position(); + } + guarantee(compressed_table_end <= m_end, "invalid method layout"); + // Verify checked exceptions and local variable tables + if (m->has_checked_exceptions()) { + u2* addr = m->checked_exceptions_length_addr(); + guarantee(*addr > 0 && (address) addr >= compressed_table_end && (address) addr < m_end, "invalid method layout"); + } + if (m->has_localvariable_table()) { + u2* addr = m->localvariable_table_length_addr(); + guarantee(*addr > 0 && (address) addr >= compressed_table_end && (address) addr < m_end, "invalid method layout"); + } + // Check compressed_table_end relative to uncompressed_table_start + u2* uncompressed_table_start; + if (m->has_localvariable_table()) { + uncompressed_table_start = (u2*) m->localvariable_table_start(); + } else { + if (m->has_checked_exceptions()) { + uncompressed_table_start = (u2*) m->checked_exceptions_start(); + } else { + uncompressed_table_start = (u2*) m_end; + } + } + int gap = (intptr_t) uncompressed_table_start - (intptr_t) compressed_table_end; + int max_gap = align_object_size(1)*BytesPerWord; + guarantee(gap >= 0 && gap < max_gap, "invalid method layout"); + } +} + +bool constMethodKlass::oop_partially_loaded(oop obj) const { + assert(obj->is_constMethod(), "object must be klass"); + constMethodOop m = constMethodOop(obj); + // check whether exception_table points to self (flag for partially loaded) + return m->exception_table() == (typeArrayOop)obj; +} + + +// The exception_table is the last field set when loading an object. +void constMethodKlass::oop_set_partially_loaded(oop obj) { + assert(obj->is_constMethod(), "object must be klass"); + constMethodOop m = constMethodOop(obj); + // Temporarily set exception_table to point to self + m->set_exception_table((typeArrayOop)obj); +} diff --git a/hotspot/src/share/vm/oops/constMethodKlass.hpp b/hotspot/src/share/vm/oops/constMethodKlass.hpp new file mode 100644 index 00000000000..87fc9c3156d --- /dev/null +++ b/hotspot/src/share/vm/oops/constMethodKlass.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A constMethodKlass is the klass of a constMethodOop + +class constMethodKlass : public Klass { + friend class VMStructs; +private: + juint _alloc_size; // allocation profiling support +public: + // Testing + bool oop_is_constMethod() const { return true; } + virtual bool oop_is_parsable(oop obj) const; + + // Allocation + DEFINE_ALLOCATE_PERMANENT(constMethodKlass); + constMethodOop allocate(int byte_code_size, int compressed_line_number_size, + int localvariable_table_length, + int checked_exceptions_length, TRAPS); + static klassOop create_klass(TRAPS); + + // Sizing + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + + // Casting from klassOop + static constMethodKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_constMethod(), "cast to constMethodKlass"); + return (constMethodKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { + return oopDesc::header_size() + sizeof(constMethodKlass)/HeapWordSize; + } + int object_size() const { + return align_object_size(header_size()); + } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Allocation profiling support + juint alloc_size() const { return _alloc_size; } + void set_alloc_size(juint n) { _alloc_size = n; } + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on (oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); + +#endif + + public: + // Verify operations + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); + bool oop_partially_loaded(oop obj) const; + void oop_set_partially_loaded(oop obj); +}; diff --git a/hotspot/src/share/vm/oops/constMethodOop.cpp b/hotspot/src/share/vm/oops/constMethodOop.cpp new file mode 100644 index 00000000000..ce4cd2a5ed7 --- /dev/null +++ b/hotspot/src/share/vm/oops/constMethodOop.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_constMethodOop.cpp.incl" + +// Static initialization +const u2 constMethodOopDesc::MAX_IDNUM = 0xFFFE; +const u2 constMethodOopDesc::UNSET_IDNUM = 0xFFFF; + +// How big must this constMethodObject be? + +int constMethodOopDesc::object_size(int code_size, + int compressed_line_number_size, + int local_variable_table_length, + int checked_exceptions_length) { + int extra_bytes = code_size; + if (compressed_line_number_size > 0) { + extra_bytes += compressed_line_number_size; + } + if (checked_exceptions_length > 0) { + extra_bytes += sizeof(u2); + extra_bytes += checked_exceptions_length * sizeof(CheckedExceptionElement); + } + if (local_variable_table_length > 0) { + extra_bytes += sizeof(u2); + extra_bytes += + local_variable_table_length * sizeof(LocalVariableTableElement); + } + int extra_words = align_size_up(extra_bytes, BytesPerWord) / BytesPerWord; + return align_object_size(header_size() + extra_words); +} + + +// linenumber table - note that length is unknown until decompression, +// see class CompressedLineNumberReadStream. + +u_char* constMethodOopDesc::compressed_linenumber_table() const { + // Located immediately following the bytecodes. + assert(has_linenumber_table(), "called only if table is present"); + return code_end(); +} + +u2* constMethodOopDesc::checked_exceptions_length_addr() const { + // Located at the end of the constMethod. + assert(has_checked_exceptions(), "called only if table is present"); + return last_u2_element(); +} + +u2* constMethodOopDesc::localvariable_table_length_addr() const { + assert(has_localvariable_table(), "called only if table is present"); + if (has_checked_exceptions()) { + // If checked_exception present, locate immediately before them. + return (u2*) checked_exceptions_start() - 1; + } else { + // Else, the linenumber table is at the end of the constMethod. + return last_u2_element(); + } +} + + +// Update the flags to indicate the presence of these optional fields. +void constMethodOopDesc::set_inlined_tables_length( + int checked_exceptions_len, + int compressed_line_number_size, + int localvariable_table_len) { + // Must be done in the order below, otherwise length_addr accessors + // will not work. Only set bit in header if length is positive. + assert(_flags == 0, "Error"); + if (compressed_line_number_size > 0) { + _flags |= _has_linenumber_table; + } + if (checked_exceptions_len > 0) { + _flags |= _has_checked_exceptions; + *(checked_exceptions_length_addr()) = checked_exceptions_len; + } + if (localvariable_table_len > 0) { + _flags |= _has_localvariable_table; + *(localvariable_table_length_addr()) = localvariable_table_len; + } +} + + +int constMethodOopDesc::checked_exceptions_length() const { + return has_checked_exceptions() ? *(checked_exceptions_length_addr()) : 0; +} + + +CheckedExceptionElement* constMethodOopDesc::checked_exceptions_start() const { + u2* addr = checked_exceptions_length_addr(); + u2 length = *addr; + assert(length > 0, "should only be called if table is present"); + addr -= length * sizeof(CheckedExceptionElement) / sizeof(u2); + return (CheckedExceptionElement*) addr; +} + + +int constMethodOopDesc::localvariable_table_length() const { + return has_localvariable_table() ? *(localvariable_table_length_addr()) : 0; +} + + +LocalVariableTableElement* constMethodOopDesc::localvariable_table_start() const { + u2* addr = localvariable_table_length_addr(); + u2 length = *addr; + assert(length > 0, "should only be called if table is present"); + addr -= length * sizeof(LocalVariableTableElement) / sizeof(u2); + return (LocalVariableTableElement*) addr; +} diff --git a/hotspot/src/share/vm/oops/constMethodOop.hpp b/hotspot/src/share/vm/oops/constMethodOop.hpp new file mode 100644 index 00000000000..e9ffa4b66bc --- /dev/null +++ b/hotspot/src/share/vm/oops/constMethodOop.hpp @@ -0,0 +1,295 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// An constMethodOop represents portions of a Java method which +// do not vary. +// +// Memory layout (each line represents a word). Note that most +// applications load thousands of methods, so keeping the size of this +// structure small has a big impact on footprint. +// +// |------------------------------------------------------| +// | header | +// | klass | +// |------------------------------------------------------| +// | fingerprint 1 | +// | fingerprint 2 | +// | method (oop) | +// | stackmap_data (oop) | +// | exception_table (oop) | +// | constMethod_size | +// | interp_kind | flags | code_size | +// | name index | signature index | +// | method_idnum | generic_signature_index | +// |------------------------------------------------------| +// | | +// | byte codes | +// | | +// |------------------------------------------------------| +// | compressed linenumber table | +// | (see class CompressedLineNumberReadStream) | +// | (note that length is unknown until decompressed) | +// | (access flags bit tells whether table is present) | +// | (indexed from start of constMethodOop) | +// | (elements not necessarily sorted!) | +// |------------------------------------------------------| +// | localvariable table elements + length (length last) | +// | (length is u2, elements are 6-tuples of u2) | +// | (see class LocalVariableTableElement) | +// | (access flags bit tells whether table is present) | +// | (indexed from end of contMethodOop) | +// |------------------------------------------------------| +// | checked exceptions elements + length (length last) | +// | (length is u2, elements are u2) | +// | (see class CheckedExceptionElement) | +// | (access flags bit tells whether table is present) | +// | (indexed from end of constMethodOop) | +// |------------------------------------------------------| + + +// Utitily class decribing elements in checked exceptions table inlined in methodOop. +class CheckedExceptionElement VALUE_OBJ_CLASS_SPEC { + public: + u2 class_cp_index; +}; + + +// Utitily class decribing elements in local variable table inlined in methodOop. +class LocalVariableTableElement VALUE_OBJ_CLASS_SPEC { + public: + u2 start_bci; + u2 length; + u2 name_cp_index; + u2 descriptor_cp_index; + u2 signature_cp_index; + u2 slot; +}; + + +class constMethodOopDesc : public oopDesc { + friend class constMethodKlass; + friend class VMStructs; +private: + enum { + _has_linenumber_table = 1, + _has_checked_exceptions = 2, + _has_localvariable_table = 4 + }; + + // Bit vector of signature + // Callers interpret 0=not initialized yet and + // -1=too many args to fix, must parse the slow way. + // The real initial value is special to account for nonatomicity of 64 bit + // loads and stores. This value may updated and read without a lock by + // multiple threads, so is volatile. + volatile uint64_t _fingerprint; + +public: + oop* oop_block_beg() const { return adr_method(); } + oop* oop_block_end() const { return adr_exception_table() + 1; } + +private: + // + // The oop block. See comment in klass.hpp before making changes. + // + + // Backpointer to non-const methodOop (needed for some JVMTI operations) + methodOop _method; + + // Raw stackmap data for the method + typeArrayOop _stackmap_data; + + // The exception handler table. 4-tuples of ints [start_pc, end_pc, + // handler_pc, catch_type index] For methods with no exceptions the + // table is pointing to Universe::the_empty_int_array + typeArrayOop _exception_table; + + // + // End of the oop block. + // + + int _constMethod_size; + jbyte _interpreter_kind; + jbyte _flags; + + // Size of Java bytecodes allocated immediately after methodOop. + u2 _code_size; + u2 _name_index; // Method name (index in constant pool) + u2 _signature_index; // Method signature (index in constant pool) + u2 _method_idnum; // unique identification number for the method within the class + // initially corresponds to the index into the methods array. + // but this may change with redefinition + u2 _generic_signature_index; // Generic signature (index in constant pool, 0 if absent) + +public: + // Inlined tables + void set_inlined_tables_length(int checked_exceptions_len, + int compressed_line_number_size, + int localvariable_table_len); + + bool has_linenumber_table() const + { return (_flags & _has_linenumber_table) != 0; } + + bool has_checked_exceptions() const + { return (_flags & _has_checked_exceptions) != 0; } + + bool has_localvariable_table() const + { return (_flags & _has_localvariable_table) != 0; } + + void set_interpreter_kind(int kind) { _interpreter_kind = kind; } + int interpreter_kind(void) const { return _interpreter_kind; } + + // backpointer to non-const methodOop + methodOop method() const { return _method; } + void set_method(methodOop m) { oop_store_without_check((oop*)&_method, (oop) m); } + + + // stackmap table data + typeArrayOop stackmap_data() const { return _stackmap_data; } + void set_stackmap_data(typeArrayOop sd) { + oop_store_without_check((oop*)&_stackmap_data, (oop)sd); + } + bool has_stackmap_table() const { return _stackmap_data != NULL; } + + // exception handler table + typeArrayOop exception_table() const { return _exception_table; } + void set_exception_table(typeArrayOop e) { oop_store_without_check((oop*) &_exception_table, (oop) e); } + bool has_exception_handler() const { return exception_table() != NULL && exception_table()->length() > 0; } + + void init_fingerprint() { + const uint64_t initval = CONST64(0x8000000000000000); + _fingerprint = initval; + } + + uint64_t fingerprint() const { + // Since reads aren't atomic for 64 bits, if any of the high or low order + // word is the initial value, return 0. See init_fingerprint for initval. + uint high_fp = (uint)(_fingerprint >> 32); + if ((int) _fingerprint == 0 || high_fp == 0x80000000) { + return 0L; + } else { + return _fingerprint; + } + } + + uint64_t set_fingerprint(uint64_t new_fingerprint) { +#ifdef ASSERT + // Assert only valid if complete/valid 64 bit _fingerprint value is read. + uint64_t oldfp = fingerprint(); +#endif // ASSERT + _fingerprint = new_fingerprint; + assert(oldfp == 0L || new_fingerprint == oldfp, + "fingerprint cannot change"); + assert(((new_fingerprint >> 32) != 0x80000000) && (int)new_fingerprint !=0, + "fingerprint should call init to set initial value"); + return new_fingerprint; + } + + // name + int name_index() const { return _name_index; } + void set_name_index(int index) { _name_index = index; } + + // signature + int signature_index() const { return _signature_index; } + void set_signature_index(int index) { _signature_index = index; } + + // generics support + int generic_signature_index() const { return _generic_signature_index; } + void set_generic_signature_index(int index) { _generic_signature_index = index; } + + // Sizing + static int header_size() { + return sizeof(constMethodOopDesc)/HeapWordSize; + } + + // Object size needed + static int object_size(int code_size, int compressed_line_number_size, + int local_variable_table_length, + int checked_exceptions_length); + + int object_size() const { return _constMethod_size; } + void set_constMethod_size(int size) { _constMethod_size = size; } + // Is object parsable by gc + bool object_is_parsable() { return object_size() > 0; } + + // code size + int code_size() const { return _code_size; } + void set_code_size(int size) { + assert(max_method_code_size < (1 << 16), + "u2 is too small to hold method code size in general"); + assert(0 <= size && size <= max_method_code_size, "invalid code size"); + _code_size = size; + } + + // linenumber table - note that length is unknown until decompression, + // see class CompressedLineNumberReadStream. + u_char* compressed_linenumber_table() const; // not preserved by gc + u2* checked_exceptions_length_addr() const; + u2* localvariable_table_length_addr() const; + + // checked exceptions + int checked_exceptions_length() const; + CheckedExceptionElement* checked_exceptions_start() const; + + // localvariable table + int localvariable_table_length() const; + LocalVariableTableElement* localvariable_table_start() const; + + // byte codes + address code_base() const { return (address) (this+1); } + address code_end() const { return code_base() + code_size(); } + bool contains(address bcp) const { return code_base() <= bcp + && bcp < code_end(); } + // Offset to bytecodes + static ByteSize codes_offset() + { return in_ByteSize(sizeof(constMethodOopDesc)); } + + // interpreter support + static ByteSize exception_table_offset() + { return byte_offset_of(constMethodOopDesc, _exception_table); } + + // Garbage collection support + oop* adr_method() const { return (oop*)&_method; } + oop* adr_stackmap_data() const { return (oop*)&_stackmap_data; } + oop* adr_exception_table() const { return (oop*)&_exception_table; } + + // Unique id for the method + static const u2 MAX_IDNUM; + static const u2 UNSET_IDNUM; + u2 method_idnum() const { return _method_idnum; } + void set_method_idnum(u2 idnum) { _method_idnum = idnum; } + +private: + // Since the size of the compressed line number table is unknown, the + // offsets of the other variable sized sections are computed backwards + // from the end of the constMethodOop. + + // First byte after constMethodOop + address constMethod_end() const + { return (address)((oop*)this + _constMethod_size); } + + // Last short in constMethodOop + u2* last_u2_element() const + { return (u2*)constMethod_end() - 1; } +}; diff --git a/hotspot/src/share/vm/oops/constantPoolKlass.cpp b/hotspot/src/share/vm/oops/constantPoolKlass.cpp new file mode 100644 index 00000000000..b976187397c --- /dev/null +++ b/hotspot/src/share/vm/oops/constantPoolKlass.cpp @@ -0,0 +1,439 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_constantPoolKlass.cpp.incl" + +constantPoolOop constantPoolKlass::allocate(int length, TRAPS) { + int size = constantPoolOopDesc::object_size(length); + KlassHandle klass (THREAD, as_klassOop()); + constantPoolOop c = + (constantPoolOop)CollectedHeap::permanent_array_allocate(klass, size, length, CHECK_NULL); + + c->set_tags(NULL); + c->set_cache(NULL); + c->set_pool_holder(NULL); + // only set to non-zero if constant pool is merged by RedefineClasses + c->set_orig_length(0); + // all fields are initialized; needed for GC + + // initialize tag array + // Note: cannot introduce constant pool handle before since it is not + // completely initialized (no class) -> would cause assertion failure + constantPoolHandle pool (THREAD, c); + typeArrayOop t_oop = oopFactory::new_permanent_byteArray(length, CHECK_NULL); + typeArrayHandle tags (THREAD, t_oop); + for (int index = 0; index < length; index++) { + tags()->byte_at_put(index, JVM_CONSTANT_Invalid); + } + pool->set_tags(tags()); + + return pool(); +} + +klassOop constantPoolKlass::create_klass(TRAPS) { + constantPoolKlass o; + KlassHandle klassklass(THREAD, Universe::arrayKlassKlassObj()); + arrayKlassHandle k = base_create_array_klass(o.vtbl_value(), header_size(), klassklass, CHECK_NULL); + arrayKlassHandle super (THREAD, k->super()); + complete_create_array_klass(k, super, CHECK_NULL); + return k(); +} + + +int constantPoolKlass::oop_size(oop obj) const { + assert(obj->is_constantPool(), "must be constantPool"); + return constantPoolOop(obj)->object_size(); +} + + +void constantPoolKlass::oop_follow_contents(oop obj) { + assert (obj->is_constantPool(), "obj must be constant pool"); + constantPoolOop cp = (constantPoolOop) obj; + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constantPoolKlassObj never moves. + + // If the tags array is null we are in the middle of allocating this constant pool + if (cp->tags() != NULL) { + // gc of constant pool contents + oop* base = (oop*)cp->base(); + for (int i = 0; i < cp->length(); i++) { + if (cp->is_pointer_entry(i)) { + if (*base != NULL) MarkSweep::mark_and_push(base); + } + base++; + } + // gc of constant pool instance variables + MarkSweep::mark_and_push(cp->tags_addr()); + MarkSweep::mark_and_push(cp->cache_addr()); + MarkSweep::mark_and_push(cp->pool_holder_addr()); + } +} + +#ifndef SERIALGC +void constantPoolKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert (obj->is_constantPool(), "obj must be constant pool"); + constantPoolOop cp = (constantPoolOop) obj; + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constantPoolKlassObj never moves. + + // If the tags array is null we are in the middle of allocating this constant + // pool. + if (cp->tags() != NULL) { + // gc of constant pool contents + oop* base = (oop*)cp->base(); + for (int i = 0; i < cp->length(); i++) { + if (cp->is_pointer_entry(i)) { + if (*base != NULL) PSParallelCompact::mark_and_push(cm, base); + } + base++; + } + // gc of constant pool instance variables + PSParallelCompact::mark_and_push(cm, cp->tags_addr()); + PSParallelCompact::mark_and_push(cm, cp->cache_addr()); + PSParallelCompact::mark_and_push(cm, cp->pool_holder_addr()); + } +} +#endif // SERIALGC + + +int constantPoolKlass::oop_adjust_pointers(oop obj) { + assert (obj->is_constantPool(), "obj must be constant pool"); + constantPoolOop cp = (constantPoolOop) obj; + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = cp->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constantPoolKlassObj never moves. + + // If the tags array is null we are in the middle of allocating this constant + // pool. + if (cp->tags() != NULL) { + oop* base = (oop*)cp->base(); + for (int i = 0; i< cp->length(); i++) { + if (cp->is_pointer_entry(i)) { + MarkSweep::adjust_pointer(base); + } + base++; + } + } + MarkSweep::adjust_pointer(cp->tags_addr()); + MarkSweep::adjust_pointer(cp->cache_addr()); + MarkSweep::adjust_pointer(cp->pool_holder_addr()); + return size; +} + + +int constantPoolKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert (obj->is_constantPool(), "obj must be constant pool"); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constantPoolKlassObj never moves. + constantPoolOop cp = (constantPoolOop) obj; + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = cp->object_size(); + + // If the tags array is null we are in the middle of allocating this constant + // pool. + if (cp->tags() != NULL) { + oop* base = (oop*)cp->base(); + for (int i = 0; i < cp->length(); i++) { + if (cp->is_pointer_entry(i)) { + blk->do_oop(base); + } + base++; + } + } + blk->do_oop(cp->tags_addr()); + blk->do_oop(cp->cache_addr()); + blk->do_oop(cp->pool_holder_addr()); + return size; +} + + +int constantPoolKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + assert (obj->is_constantPool(), "obj must be constant pool"); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constantPoolKlassObj never moves. + constantPoolOop cp = (constantPoolOop) obj; + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = cp->object_size(); + + // If the tags array is null we are in the middle of allocating this constant + // pool. + if (cp->tags() != NULL) { + oop* base = (oop*)cp->base(); + for (int i = 0; i < cp->length(); i++) { + if (mr.contains(base)) { + if (cp->is_pointer_entry(i)) { + blk->do_oop(base); + } + } + base++; + } + } + oop* addr; + addr = cp->tags_addr(); + blk->do_oop(addr); + addr = cp->cache_addr(); + blk->do_oop(addr); + addr = cp->pool_holder_addr(); + blk->do_oop(addr); + return size; +} + +#ifndef SERIALGC +int constantPoolKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert (obj->is_constantPool(), "obj must be constant pool"); + constantPoolOop cp = (constantPoolOop) obj; + + // If the tags array is null we are in the middle of allocating this constant + // pool. + if (cp->tags() != NULL) { + oop* base = (oop*)cp->base(); + for (int i = 0; i < cp->length(); ++i, ++base) { + if (cp->is_pointer_entry(i)) { + PSParallelCompact::adjust_pointer(base); + } + } + } + PSParallelCompact::adjust_pointer(cp->tags_addr()); + PSParallelCompact::adjust_pointer(cp->cache_addr()); + PSParallelCompact::adjust_pointer(cp->pool_holder_addr()); + return cp->object_size(); +} + +int +constantPoolKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, HeapWord* end_addr) { + assert (obj->is_constantPool(), "obj must be constant pool"); + constantPoolOop cp = (constantPoolOop) obj; + + // If the tags array is null we are in the middle of allocating this constant + // pool. + if (cp->tags() != NULL) { + oop* base = (oop*)cp->base(); + oop* const beg_oop = MAX2((oop*)beg_addr, base); + oop* const end_oop = MIN2((oop*)end_addr, base + cp->length()); + const size_t beg_idx = pointer_delta(beg_oop, base, sizeof(oop*)); + const size_t end_idx = pointer_delta(end_oop, base, sizeof(oop*)); + for (size_t cur_idx = beg_idx; cur_idx < end_idx; ++cur_idx, ++base) { + if (cp->is_pointer_entry(int(cur_idx))) { + PSParallelCompact::adjust_pointer(base); + } + } + } + + oop* p; + p = cp->tags_addr(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = cp->cache_addr(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = cp->pool_holder_addr(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + + return cp->object_size(); +} + +void constantPoolKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_constantPool(), "should be constant pool"); +} + +void constantPoolKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_constantPool(), "should be constant pool"); +} +#endif // SERIALGC + +#ifndef PRODUCT + +// Printing + +void constantPoolKlass::oop_print_on(oop obj, outputStream* st) { + EXCEPTION_MARK; + oop anObj; + assert(obj->is_constantPool(), "must be constantPool"); + arrayKlass::oop_print_on(obj, st); + constantPoolOop cp = constantPoolOop(obj); + + // Temp. remove cache so we can do lookups with original indicies. + constantPoolCacheHandle cache (THREAD, cp->cache()); + cp->set_cache(NULL); + + for (int index = 1; index < cp->length(); index++) { // Index 0 is unused + st->print(" - %3d : ", index); + cp->tag_at(index).print_on(st); + st->print(" : "); + switch (cp->tag_at(index).value()) { + case JVM_CONSTANT_Class : + { anObj = cp->klass_at(index, CATCH); + anObj->print_value_on(st); + st->print(" {0x%lx}", (address)anObj); + } + break; + case JVM_CONSTANT_Fieldref : + case JVM_CONSTANT_Methodref : + case JVM_CONSTANT_InterfaceMethodref : + st->print("klass_index=%d", cp->klass_ref_index_at(index)); + st->print(" name_and_type_index=%d", cp->name_and_type_ref_index_at(index)); + break; + case JVM_CONSTANT_UnresolvedString : + case JVM_CONSTANT_String : + anObj = cp->string_at(index, CATCH); + anObj->print_value_on(st); + st->print(" {0x%lx}", (address)anObj); + break; + case JVM_CONSTANT_Integer : + st->print("%d", cp->int_at(index)); + break; + case JVM_CONSTANT_Float : + st->print("%f", cp->float_at(index)); + break; + case JVM_CONSTANT_Long : + st->print_jlong(cp->long_at(index)); + index++; // Skip entry following eigth-byte constant + break; + case JVM_CONSTANT_Double : + st->print("%lf", cp->double_at(index)); + index++; // Skip entry following eigth-byte constant + break; + case JVM_CONSTANT_NameAndType : + st->print("name_index=%d", cp->name_ref_index_at(index)); + st->print(" signature_index=%d", cp->signature_ref_index_at(index)); + break; + case JVM_CONSTANT_Utf8 : + cp->symbol_at(index)->print_value_on(st); + break; + case JVM_CONSTANT_UnresolvedClass : // fall-through + case JVM_CONSTANT_UnresolvedClassInError: { + // unresolved_klass_at requires lock or safe world. + oop entry = *cp->obj_at_addr(index); + entry->print_value_on(st); + } + break; + default: + ShouldNotReachHere(); + break; + } + st->cr(); + } + st->cr(); + + // Restore cache + cp->set_cache(cache()); +} + + +#endif + +const char* constantPoolKlass::internal_name() const { + return "{constant pool}"; +} + +// Verification + +void constantPoolKlass::oop_verify_on(oop obj, outputStream* st) { + Klass::oop_verify_on(obj, st); + guarantee(obj->is_constantPool(), "object must be constant pool"); + constantPoolOop cp = constantPoolOop(obj); + guarantee(cp->is_perm(), "should be in permspace"); + if (!cp->partially_loaded()) { + oop* base = (oop*)cp->base(); + for (int i = 0; i< cp->length(); i++) { + if (cp->tag_at(i).is_klass()) { + guarantee((*base)->is_perm(), "should be in permspace"); + guarantee((*base)->is_klass(), "should be klass"); + } + if (cp->tag_at(i).is_unresolved_klass()) { + guarantee((*base)->is_perm(), "should be in permspace"); + guarantee((*base)->is_symbol() || (*base)->is_klass(), + "should be symbol or klass"); + } + if (cp->tag_at(i).is_symbol()) { + guarantee((*base)->is_perm(), "should be in permspace"); + guarantee((*base)->is_symbol(), "should be symbol"); + } + if (cp->tag_at(i).is_unresolved_string()) { + guarantee((*base)->is_perm(), "should be in permspace"); + guarantee((*base)->is_symbol() || (*base)->is_instance(), + "should be symbol or instance"); + } + if (cp->tag_at(i).is_string()) { + guarantee((*base)->is_perm(), "should be in permspace"); + guarantee((*base)->is_instance(), "should be instance"); + } + base++; + } + guarantee(cp->tags()->is_perm(), "should be in permspace"); + guarantee(cp->tags()->is_typeArray(), "should be type array"); + if (cp->cache() != NULL) { + // Note: cache() can be NULL before a class is completely setup or + // in temporary constant pools used during constant pool merging + guarantee(cp->cache()->is_perm(), "should be in permspace"); + guarantee(cp->cache()->is_constantPoolCache(), "should be constant pool cache"); + } + if (cp->pool_holder() != NULL) { + // Note: pool_holder() can be NULL in temporary constant pools + // used during constant pool merging + guarantee(cp->pool_holder()->is_perm(), "should be in permspace"); + guarantee(cp->pool_holder()->is_klass(), "should be klass"); + } + } +} + +bool constantPoolKlass::oop_partially_loaded(oop obj) const { + assert(obj->is_constantPool(), "object must be constant pool"); + constantPoolOop cp = constantPoolOop(obj); + return cp->tags() == NULL || cp->pool_holder() == (klassOop) cp; // Check whether pool holder points to self +} + + +void constantPoolKlass::oop_set_partially_loaded(oop obj) { + assert(obj->is_constantPool(), "object must be constant pool"); + constantPoolOop cp = constantPoolOop(obj); + assert(cp->pool_holder() == NULL, "just checking"); + cp->set_pool_holder((klassOop) cp); // Temporarily set pool holder to point to self +} + +#ifndef PRODUCT +// CompileTheWorld support. Preload all classes loaded references in the passed in constantpool +void constantPoolKlass::preload_and_initialize_all_classes(oop obj, TRAPS) { + guarantee(obj->is_constantPool(), "object must be constant pool"); + constantPoolHandle cp(THREAD, (constantPoolOop)obj); + guarantee(!cp->partially_loaded(), "must be fully loaded"); + + for (int i = 0; i< cp->length(); i++) { + if (cp->tag_at(i).is_unresolved_klass()) { + // This will force loading of the class + klassOop klass = cp->klass_at(i, CHECK); + if (klass->is_instance()) { + // Force initialization of class + instanceKlass::cast(klass)->initialize(CHECK); + } + } + } +} + +#endif diff --git a/hotspot/src/share/vm/oops/constantPoolKlass.hpp b/hotspot/src/share/vm/oops/constantPoolKlass.hpp new file mode 100644 index 00000000000..ac01a7b715d --- /dev/null +++ b/hotspot/src/share/vm/oops/constantPoolKlass.hpp @@ -0,0 +1,77 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A constantPoolKlass is the klass of a constantPoolOop + +class constantPoolKlass : public arrayKlass { + public: + // Dispatched klass operations + bool oop_is_constantPool() const { return true; } + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(constantPoolKlass); + constantPoolOop allocate(int length, TRAPS); + static klassOop create_klass(TRAPS); + + // Casting from klassOop + static constantPoolKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_constantPool(), "cast to constantPoolKlass"); + return (constantPoolKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(constantPoolKlass)/HeapWordSize; } + int object_size() const { return arrayKlass::object_size(header_size()); } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on(oop obj, outputStream* st); +#endif + + public: + // Verification + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); + // tells whether obj is partially constructed (gc during class loading) + bool oop_partially_loaded(oop obj) const; + void oop_set_partially_loaded(oop obj); +#ifndef PRODUCT + // Compile the world support + static void preload_and_initialize_all_classes(oop constant_pool, TRAPS); +#endif +}; diff --git a/hotspot/src/share/vm/oops/constantPoolOop.cpp b/hotspot/src/share/vm/oops/constantPoolOop.cpp new file mode 100644 index 00000000000..cd8a9d16f59 --- /dev/null +++ b/hotspot/src/share/vm/oops/constantPoolOop.cpp @@ -0,0 +1,1269 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_constantPoolOop.cpp.incl" + +klassOop constantPoolOopDesc::klass_at_impl(constantPoolHandle this_oop, int which, TRAPS) { + // A resolved constantPool entry will contain a klassOop, otherwise a symbolOop. + // It is not safe to rely on the tag bit's here, since we don't have a lock, and the entry and + // tag is not updated atomicly. + oop entry = *(this_oop->obj_at_addr(which)); + if (entry->is_klass()) { + // Already resolved - return entry. + return (klassOop)entry; + } + + // Acquire lock on constant oop while doing update. After we get the lock, we check if another object + // already has updated the object + assert(THREAD->is_Java_thread(), "must be a Java thread"); + bool do_resolve = false; + bool in_error = false; + + symbolHandle name; + Handle loader; + { ObjectLocker ol(this_oop, THREAD); + + if (this_oop->tag_at(which).is_unresolved_klass()) { + if (this_oop->tag_at(which).is_unresolved_klass_in_error()) { + in_error = true; + } else { + do_resolve = true; + name = symbolHandle(THREAD, this_oop->unresolved_klass_at(which)); + loader = Handle(THREAD, instanceKlass::cast(this_oop->pool_holder())->class_loader()); + } + } + } // unlocking constantPool + + + // The original attempt to resolve this constant pool entry failed so find the + // original error and throw it again (JVMS 5.4.3). + if (in_error) { + symbolOop error = SystemDictionary::find_resolution_error(this_oop, which); + guarantee(error != (symbolOop)NULL, "tag mismatch with resolution error table"); + ResourceMark rm; + // exception text will be the class name + const char* className = this_oop->unresolved_klass_at(which)->as_C_string(); + THROW_MSG_0(error, className); + } + + if (do_resolve) { + // this_oop must be unlocked during resolve_or_fail + oop protection_domain = Klass::cast(this_oop->pool_holder())->protection_domain(); + Handle h_prot (THREAD, protection_domain); + klassOop k_oop = SystemDictionary::resolve_or_fail(name, loader, h_prot, true, THREAD); + KlassHandle k; + if (!HAS_PENDING_EXCEPTION) { + k = KlassHandle(THREAD, k_oop); + // Do access check for klasses + verify_constant_pool_resolve(this_oop, k, THREAD); + } + + // Failed to resolve class. We must record the errors so that subsequent attempts + // to resolve this constant pool entry fail with the same error (JVMS 5.4.3). + if (HAS_PENDING_EXCEPTION) { + ResourceMark rm; + symbolHandle error(PENDING_EXCEPTION->klass()->klass_part()->name()); + + bool throw_orig_error = false; + { + ObjectLocker ol (this_oop, THREAD); + + // some other thread has beaten us and has resolved the class. + if (this_oop->tag_at(which).is_klass()) { + CLEAR_PENDING_EXCEPTION; + entry = this_oop->resolved_klass_at(which); + return (klassOop)entry; + } + + if (!PENDING_EXCEPTION-> + is_a(SystemDictionary::linkageError_klass())) { + // Just throw the exception and don't prevent these classes from + // being loaded due to virtual machine errors like StackOverflow + // and OutOfMemoryError, etc, or if the thread was hit by stop() + // Needs clarification to section 5.4.3 of the VM spec (see 6308271) + } + else if (!this_oop->tag_at(which).is_unresolved_klass_in_error()) { + SystemDictionary::add_resolution_error(this_oop, which, error); + this_oop->tag_at_put(which, JVM_CONSTANT_UnresolvedClassInError); + } else { + // some other thread has put the class in error state. + error = symbolHandle(SystemDictionary::find_resolution_error(this_oop, which)); + assert(!error.is_null(), "checking"); + throw_orig_error = true; + } + } // unlocked + + if (throw_orig_error) { + CLEAR_PENDING_EXCEPTION; + ResourceMark rm; + const char* className = this_oop->unresolved_klass_at(which)->as_C_string(); + THROW_MSG_0(error, className); + } + + return 0; + } + + if (TraceClassResolution && !k()->klass_part()->oop_is_array()) { + // skip resolving the constant pool so that this code get's + // called the next time some bytecodes refer to this class. + ResourceMark rm; + int line_number = -1; + const char * source_file = NULL; + if (JavaThread::current()->has_last_Java_frame()) { + // try to identify the method which called this function. + vframeStream vfst(JavaThread::current()); + if (!vfst.at_end()) { + line_number = vfst.method()->line_number_from_bci(vfst.bci()); + symbolOop s = instanceKlass::cast(vfst.method()->method_holder())->source_file_name(); + if (s != NULL) { + source_file = s->as_C_string(); + } + } + } + if (k() != this_oop->pool_holder()) { + // only print something if the classes are different + if (source_file != NULL) { + tty->print("RESOLVE %s %s %s:%d\n", + instanceKlass::cast(this_oop->pool_holder())->external_name(), + instanceKlass::cast(k())->external_name(), source_file, line_number); + } else { + tty->print("RESOLVE %s %s\n", + instanceKlass::cast(this_oop->pool_holder())->external_name(), + instanceKlass::cast(k())->external_name()); + } + } + return k(); + } else { + ObjectLocker ol (this_oop, THREAD); + // Only updated constant pool - if it is resolved. + do_resolve = this_oop->tag_at(which).is_unresolved_klass(); + if (do_resolve) { + this_oop->klass_at_put(which, k()); + } + } + } + + entry = this_oop->resolved_klass_at(which); + assert(entry->is_klass(), "must be resolved at this point"); + return (klassOop)entry; +} + + +// Does not update constantPoolOop - to avoid any exception throwing. Used +// by compiler and exception handling. Also used to avoid classloads for +// instanceof operations. Returns NULL if the class has not been loaded or +// if the verification of constant pool failed +klassOop constantPoolOopDesc::klass_at_if_loaded(constantPoolHandle this_oop, int which) { + oop entry = *this_oop->obj_at_addr(which); + if (entry->is_klass()) { + return (klassOop)entry; + } else { + assert(entry->is_symbol(), "must be either symbol or klass"); + Thread *thread = Thread::current(); + symbolHandle name (thread, (symbolOop)entry); + oop loader = instanceKlass::cast(this_oop->pool_holder())->class_loader(); + oop protection_domain = Klass::cast(this_oop->pool_holder())->protection_domain(); + Handle h_prot (thread, protection_domain); + Handle h_loader (thread, loader); + klassOop k = SystemDictionary::find(name, h_loader, h_prot, thread); + + if (k != NULL) { + // Make sure that resolving is legal + EXCEPTION_MARK; + KlassHandle klass(THREAD, k); + // return NULL if verification fails + verify_constant_pool_resolve(this_oop, klass, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return NULL; + } + return klass(); + } else { + return k; + } + } +} + + +klassOop constantPoolOopDesc::klass_ref_at_if_loaded(constantPoolHandle this_oop, int which) { + return klass_at_if_loaded(this_oop, this_oop->klass_ref_index_at(which)); +} + + +// This is an interface for the compiler that allows accessing non-resolved entries +// in the constant pool - but still performs the validations tests. Must be used +// in a pre-parse of the compiler - to determine what it can do and not do. +// Note: We cannot update the ConstantPool from the vm_thread. +klassOop constantPoolOopDesc::klass_ref_at_if_loaded_check(constantPoolHandle this_oop, int index, TRAPS) { + int which = this_oop->klass_ref_index_at(index); + oop entry = *this_oop->obj_at_addr(which); + if (entry->is_klass()) { + return (klassOop)entry; + } else { + assert(entry->is_symbol(), "must be either symbol or klass"); + symbolHandle name (THREAD, (symbolOop)entry); + oop loader = instanceKlass::cast(this_oop->pool_holder())->class_loader(); + oop protection_domain = Klass::cast(this_oop->pool_holder())->protection_domain(); + Handle h_loader(THREAD, loader); + Handle h_prot (THREAD, protection_domain); + KlassHandle k(THREAD, SystemDictionary::find(name, h_loader, h_prot, THREAD)); + + // Do access check for klasses + if( k.not_null() ) verify_constant_pool_resolve(this_oop, k, CHECK_NULL); + return k(); + } +} + + +symbolOop constantPoolOopDesc::uncached_name_ref_at(int which) { + jint ref_index = name_and_type_at(uncached_name_and_type_ref_index_at(which)); + int name_index = extract_low_short_from_int(ref_index); + return symbol_at(name_index); +} + + +symbolOop constantPoolOopDesc::uncached_signature_ref_at(int which) { + jint ref_index = name_and_type_at(uncached_name_and_type_ref_index_at(which)); + int signature_index = extract_high_short_from_int(ref_index); + return symbol_at(signature_index); +} + + +int constantPoolOopDesc::uncached_name_and_type_ref_index_at(int which) { + jint ref_index = field_or_method_at(which, true); + return extract_high_short_from_int(ref_index); +} + + +int constantPoolOopDesc::uncached_klass_ref_index_at(int which) { + jint ref_index = field_or_method_at(which, true); + return extract_low_short_from_int(ref_index); +} + + +void constantPoolOopDesc::verify_constant_pool_resolve(constantPoolHandle this_oop, KlassHandle k, TRAPS) { + if (k->oop_is_instance() || k->oop_is_objArray()) { + instanceKlassHandle holder (THREAD, this_oop->pool_holder()); + klassOop elem_oop = k->oop_is_instance() ? k() : objArrayKlass::cast(k())->bottom_klass(); + KlassHandle element (THREAD, elem_oop); + + // The element type could be a typeArray - we only need the access check if it is + // an reference to another class + if (element->oop_is_instance()) { + LinkResolver::check_klass_accessability(holder, element, CHECK); + } + } +} + + +int constantPoolOopDesc::klass_ref_index_at(int which) { + jint ref_index = field_or_method_at(which, false); + return extract_low_short_from_int(ref_index); +} + + +int constantPoolOopDesc::name_and_type_ref_index_at(int which) { + jint ref_index = field_or_method_at(which, false); + return extract_high_short_from_int(ref_index); +} + + +int constantPoolOopDesc::name_ref_index_at(int which) { + jint ref_index = name_and_type_at(which); + return extract_low_short_from_int(ref_index); +} + + +int constantPoolOopDesc::signature_ref_index_at(int which) { + jint ref_index = name_and_type_at(which); + return extract_high_short_from_int(ref_index); +} + + +klassOop constantPoolOopDesc::klass_ref_at(int which, TRAPS) { + return klass_at(klass_ref_index_at(which), CHECK_NULL); +} + + +symbolOop constantPoolOopDesc::klass_name_at(int which) { + assert(tag_at(which).is_unresolved_klass() || tag_at(which).is_klass(), + "Corrupted constant pool"); + // A resolved constantPool entry will contain a klassOop, otherwise a symbolOop. + // It is not safe to rely on the tag bit's here, since we don't have a lock, and the entry and + // tag is not updated atomicly. + oop entry = *(obj_at_addr(which)); + if (entry->is_klass()) { + // Already resolved - return entry's name. + return klassOop(entry)->klass_part()->name(); + } else { + assert(entry->is_symbol(), "must be either symbol or klass"); + return (symbolOop)entry; + } +} + +symbolOop constantPoolOopDesc::klass_ref_at_noresolve(int which) { + jint ref_index = klass_ref_index_at(which); + return klass_at_noresolve(ref_index); +} + +char* constantPoolOopDesc::string_at_noresolve(int which) { + // Test entry type in case string is resolved while in here. + oop entry = *(obj_at_addr(which)); + if (entry->is_symbol()) { + return ((symbolOop)entry)->as_C_string(); + } else { + return java_lang_String::as_utf8_string(entry); + } +} + + +symbolOop constantPoolOopDesc::name_ref_at(int which) { + jint ref_index = name_and_type_at(name_and_type_ref_index_at(which)); + int name_index = extract_low_short_from_int(ref_index); + return symbol_at(name_index); +} + + +symbolOop constantPoolOopDesc::signature_ref_at(int which) { + jint ref_index = name_and_type_at(name_and_type_ref_index_at(which)); + int signature_index = extract_high_short_from_int(ref_index); + return symbol_at(signature_index); +} + + +BasicType constantPoolOopDesc::basic_type_for_signature_at(int which) { + return FieldType::basic_type(symbol_at(which)); +} + + +void constantPoolOopDesc::resolve_string_constants_impl(constantPoolHandle this_oop, TRAPS) { + for (int index = 1; index < this_oop->length(); index++) { // Index 0 is unused + if (this_oop->tag_at(index).is_unresolved_string()) { + this_oop->string_at(index, CHECK); + } + } +} + +oop constantPoolOopDesc::string_at_impl(constantPoolHandle this_oop, int which, TRAPS) { + oop entry = *(this_oop->obj_at_addr(which)); + if (entry->is_symbol()) { + ObjectLocker ol(this_oop, THREAD); + if (this_oop->tag_at(which).is_unresolved_string()) { + // Intern string + symbolOop sym = this_oop->unresolved_string_at(which); + entry = StringTable::intern(sym, CHECK_(constantPoolOop(NULL))); + this_oop->string_at_put(which, entry); + } else { + // Another thread beat us and interned string, read string from constant pool + entry = this_oop->resolved_string_at(which); + } + } + assert(java_lang_String::is_instance(entry), "must be string"); + return entry; +} + + +bool constantPoolOopDesc::klass_name_at_matches(instanceKlassHandle k, + int which) { + // Names are interned, so we can compare symbolOops directly + symbolOop cp_name = klass_name_at(which); + return (cp_name == k->name()); +} + + +int constantPoolOopDesc::pre_resolve_shared_klasses(TRAPS) { + ResourceMark rm; + int count = 0; + for (int index = 1; index < tags()->length(); index++) { // Index 0 is unused + if (tag_at(index).is_unresolved_string()) { + // Intern string + symbolOop sym = unresolved_string_at(index); + oop entry = StringTable::intern(sym, CHECK_(-1)); + string_at_put(index, entry); + } + } + return count; +} + + +// Iterate over symbols which are used as class, field, method names and +// signatures (in preparation for writing to the shared archive). + +void constantPoolOopDesc::shared_symbols_iterate(OopClosure* closure) { + for (int index = 1; index < length(); index++) { // Index 0 is unused + switch (tag_at(index).value()) { + + case JVM_CONSTANT_UnresolvedClass: + closure->do_oop(obj_at_addr(index)); + break; + + case JVM_CONSTANT_NameAndType: + { + int i = *int_at_addr(index); + closure->do_oop(obj_at_addr((unsigned)i >> 16)); + closure->do_oop(obj_at_addr((unsigned)i & 0xffff)); + } + break; + + case JVM_CONSTANT_Class: + case JVM_CONSTANT_InterfaceMethodref: + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_Integer: + case JVM_CONSTANT_Float: + // Do nothing! Not an oop. + // These constant types do not reference symbols at this point. + break; + + case JVM_CONSTANT_String: + // Do nothing! Not a symbol. + break; + + case JVM_CONSTANT_UnresolvedString: + case JVM_CONSTANT_Utf8: + // These constants are symbols, but unless these symbols are + // actually to be used for something, we don't want to mark them. + break; + + case JVM_CONSTANT_Long: + case JVM_CONSTANT_Double: + // Do nothing! Not an oop. (But takes two pool entries.) + ++index; + break; + + default: + ShouldNotReachHere(); + break; + } + } +} + + +// Iterate over the [one] tags array (in preparation for writing to the +// shared archive). + +void constantPoolOopDesc::shared_tags_iterate(OopClosure* closure) { + closure->do_oop(tags_addr()); +} + + +// Iterate over String objects (in preparation for writing to the shared +// archive). + +void constantPoolOopDesc::shared_strings_iterate(OopClosure* closure) { + for (int index = 1; index < length(); index++) { // Index 0 is unused + switch (tag_at(index).value()) { + + case JVM_CONSTANT_UnresolvedClass: + case JVM_CONSTANT_NameAndType: + // Do nothing! Not a String. + break; + + case JVM_CONSTANT_Class: + case JVM_CONSTANT_InterfaceMethodref: + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_Integer: + case JVM_CONSTANT_Float: + // Do nothing! Not an oop. + // These constant types do not reference symbols at this point. + break; + + case JVM_CONSTANT_String: + closure->do_oop(obj_at_addr(index)); + break; + + case JVM_CONSTANT_UnresolvedString: + case JVM_CONSTANT_Utf8: + // These constants are symbols, but unless these symbols are + // actually to be used for something, we don't want to mark them. + break; + + case JVM_CONSTANT_Long: + case JVM_CONSTANT_Double: + // Do nothing! Not an oop. (But takes two pool entries.) + ++index; + break; + + default: + ShouldNotReachHere(); + break; + } + } +} + + +// Compare this constant pool's entry at index1 to the constant pool +// cp2's entry at index2. +bool constantPoolOopDesc::compare_entry_to(int index1, constantPoolHandle cp2, + int index2, TRAPS) { + + jbyte t1 = tag_at(index1).value(); + jbyte t2 = cp2->tag_at(index2).value(); + + + // JVM_CONSTANT_UnresolvedClassInError is equal to JVM_CONSTANT_UnresolvedClass + // when comparing + if (t1 == JVM_CONSTANT_UnresolvedClassInError) { + t1 = JVM_CONSTANT_UnresolvedClass; + } + if (t2 == JVM_CONSTANT_UnresolvedClassInError) { + t2 = JVM_CONSTANT_UnresolvedClass; + } + + if (t1 != t2) { + // Not the same entry type so there is nothing else to check. Note + // that this style of checking will consider resolved/unresolved + // class pairs and resolved/unresolved string pairs as different. + // From the constantPoolOop API point of view, this is correct + // behavior. See constantPoolKlass::merge() to see how this plays + // out in the context of constantPoolOop merging. + return false; + } + + switch (t1) { + case JVM_CONSTANT_Class: + { + klassOop k1 = klass_at(index1, CHECK_false); + klassOop k2 = cp2->klass_at(index2, CHECK_false); + if (k1 == k2) { + return true; + } + } break; + + case JVM_CONSTANT_ClassIndex: + { + int recur1 = klass_index_at(index1); + int recur2 = cp2->klass_index_at(index2); + bool match = compare_entry_to(recur1, cp2, recur2, CHECK_false); + if (match) { + return true; + } + } break; + + case JVM_CONSTANT_Double: + { + jdouble d1 = double_at(index1); + jdouble d2 = cp2->double_at(index2); + if (d1 == d2) { + return true; + } + } break; + + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_InterfaceMethodref: + case JVM_CONSTANT_Methodref: + { + int recur1 = uncached_klass_ref_index_at(index1); + int recur2 = cp2->uncached_klass_ref_index_at(index2); + bool match = compare_entry_to(recur1, cp2, recur2, CHECK_false); + if (match) { + recur1 = uncached_name_and_type_ref_index_at(index1); + recur2 = cp2->uncached_name_and_type_ref_index_at(index2); + match = compare_entry_to(recur1, cp2, recur2, CHECK_false); + if (match) { + return true; + } + } + } break; + + case JVM_CONSTANT_Float: + { + jfloat f1 = float_at(index1); + jfloat f2 = cp2->float_at(index2); + if (f1 == f2) { + return true; + } + } break; + + case JVM_CONSTANT_Integer: + { + jint i1 = int_at(index1); + jint i2 = cp2->int_at(index2); + if (i1 == i2) { + return true; + } + } break; + + case JVM_CONSTANT_Long: + { + jlong l1 = long_at(index1); + jlong l2 = cp2->long_at(index2); + if (l1 == l2) { + return true; + } + } break; + + case JVM_CONSTANT_NameAndType: + { + int recur1 = name_ref_index_at(index1); + int recur2 = cp2->name_ref_index_at(index2); + bool match = compare_entry_to(recur1, cp2, recur2, CHECK_false); + if (match) { + recur1 = signature_ref_index_at(index1); + recur2 = cp2->signature_ref_index_at(index2); + match = compare_entry_to(recur1, cp2, recur2, CHECK_false); + if (match) { + return true; + } + } + } break; + + case JVM_CONSTANT_String: + { + oop s1 = string_at(index1, CHECK_false); + oop s2 = cp2->string_at(index2, CHECK_false); + if (s1 == s2) { + return true; + } + } break; + + case JVM_CONSTANT_StringIndex: + { + int recur1 = string_index_at(index1); + int recur2 = cp2->string_index_at(index2); + bool match = compare_entry_to(recur1, cp2, recur2, CHECK_false); + if (match) { + return true; + } + } break; + + case JVM_CONSTANT_UnresolvedClass: + { + symbolOop k1 = unresolved_klass_at(index1); + symbolOop k2 = cp2->unresolved_klass_at(index2); + if (k1 == k2) { + return true; + } + } break; + + case JVM_CONSTANT_UnresolvedString: + { + symbolOop s1 = unresolved_string_at(index1); + symbolOop s2 = cp2->unresolved_string_at(index2); + if (s1 == s2) { + return true; + } + } break; + + case JVM_CONSTANT_Utf8: + { + symbolOop s1 = symbol_at(index1); + symbolOop s2 = cp2->symbol_at(index2); + if (s1 == s2) { + return true; + } + } break; + + // Invalid is used as the tag for the second constant pool entry + // occupied by JVM_CONSTANT_Double or JVM_CONSTANT_Long. It should + // not be seen by itself. + case JVM_CONSTANT_Invalid: // fall through + + default: + ShouldNotReachHere(); + break; + } + + return false; +} // end compare_entry_to() + + +// Copy this constant pool's entries at start_i to end_i (inclusive) +// to the constant pool to_cp's entries starting at to_i. A total of +// (end_i - start_i) + 1 entries are copied. +void constantPoolOopDesc::copy_cp_to(int start_i, int end_i, + constantPoolHandle to_cp, int to_i, TRAPS) { + + int dest_i = to_i; // leave original alone for debug purposes + + for (int src_i = start_i; src_i <= end_i; /* see loop bottom */ ) { + copy_entry_to(src_i, to_cp, dest_i, CHECK); + + switch (tag_at(src_i).value()) { + case JVM_CONSTANT_Double: + case JVM_CONSTANT_Long: + // double and long take two constant pool entries + src_i += 2; + dest_i += 2; + break; + + default: + // all others take one constant pool entry + src_i++; + dest_i++; + break; + } + } +} // end copy_cp_to() + + +// Copy this constant pool's entry at from_i to the constant pool +// to_cp's entry at to_i. +void constantPoolOopDesc::copy_entry_to(int from_i, constantPoolHandle to_cp, + int to_i, TRAPS) { + + switch (tag_at(from_i).value()) { + case JVM_CONSTANT_Class: + { + klassOop k = klass_at(from_i, CHECK); + to_cp->klass_at_put(to_i, k); + } break; + + case JVM_CONSTANT_ClassIndex: + { + jint ki = klass_index_at(from_i); + to_cp->klass_index_at_put(to_i, ki); + } break; + + case JVM_CONSTANT_Double: + { + jdouble d = double_at(from_i); + to_cp->double_at_put(to_i, d); + // double takes two constant pool entries so init second entry's tag + to_cp->tag_at_put(to_i + 1, JVM_CONSTANT_Invalid); + } break; + + case JVM_CONSTANT_Fieldref: + { + int class_index = uncached_klass_ref_index_at(from_i); + int name_and_type_index = uncached_name_and_type_ref_index_at(from_i); + to_cp->field_at_put(to_i, class_index, name_and_type_index); + } break; + + case JVM_CONSTANT_Float: + { + jfloat f = float_at(from_i); + to_cp->float_at_put(to_i, f); + } break; + + case JVM_CONSTANT_Integer: + { + jint i = int_at(from_i); + to_cp->int_at_put(to_i, i); + } break; + + case JVM_CONSTANT_InterfaceMethodref: + { + int class_index = uncached_klass_ref_index_at(from_i); + int name_and_type_index = uncached_name_and_type_ref_index_at(from_i); + to_cp->interface_method_at_put(to_i, class_index, name_and_type_index); + } break; + + case JVM_CONSTANT_Long: + { + jlong l = long_at(from_i); + to_cp->long_at_put(to_i, l); + // long takes two constant pool entries so init second entry's tag + to_cp->tag_at_put(to_i + 1, JVM_CONSTANT_Invalid); + } break; + + case JVM_CONSTANT_Methodref: + { + int class_index = uncached_klass_ref_index_at(from_i); + int name_and_type_index = uncached_name_and_type_ref_index_at(from_i); + to_cp->method_at_put(to_i, class_index, name_and_type_index); + } break; + + case JVM_CONSTANT_NameAndType: + { + int name_ref_index = name_ref_index_at(from_i); + int signature_ref_index = signature_ref_index_at(from_i); + to_cp->name_and_type_at_put(to_i, name_ref_index, signature_ref_index); + } break; + + case JVM_CONSTANT_String: + { + oop s = string_at(from_i, CHECK); + to_cp->string_at_put(to_i, s); + } break; + + case JVM_CONSTANT_StringIndex: + { + jint si = string_index_at(from_i); + to_cp->string_index_at_put(to_i, si); + } break; + + case JVM_CONSTANT_UnresolvedClass: + { + symbolOop k = unresolved_klass_at(from_i); + to_cp->unresolved_klass_at_put(to_i, k); + } break; + + case JVM_CONSTANT_UnresolvedClassInError: + { + symbolOop k = unresolved_klass_at(from_i); + to_cp->unresolved_klass_at_put(to_i, k); + to_cp->tag_at_put(to_i, JVM_CONSTANT_UnresolvedClassInError); + } break; + + + case JVM_CONSTANT_UnresolvedString: + { + symbolOop s = unresolved_string_at(from_i); + to_cp->unresolved_string_at_put(to_i, s); + } break; + + case JVM_CONSTANT_Utf8: + { + symbolOop s = symbol_at(from_i); + to_cp->symbol_at_put(to_i, s); + } break; + + // Invalid is used as the tag for the second constant pool entry + // occupied by JVM_CONSTANT_Double or JVM_CONSTANT_Long. It should + // not be seen by itself. + case JVM_CONSTANT_Invalid: // fall through + + default: + { + jbyte bad_value = tag_at(from_i).value(); // leave a breadcrumb + ShouldNotReachHere(); + } break; + } +} // end copy_entry_to() + + +// Search constant pool search_cp for an entry that matches this +// constant pool's entry at pattern_i. Returns the index of a +// matching entry or zero (0) if there is no matching entry. +int constantPoolOopDesc::find_matching_entry(int pattern_i, + constantPoolHandle search_cp, TRAPS) { + + // index zero (0) is not used + for (int i = 1; i < search_cp->length(); i++) { + bool found = compare_entry_to(pattern_i, search_cp, i, CHECK_0); + if (found) { + return i; + } + } + + return 0; // entry not found; return unused index zero (0) +} // end find_matching_entry() + + +#ifndef PRODUCT + +const char* constantPoolOopDesc::printable_name_at(int which) { + + constantTag tag = tag_at(which); + + if (tag.is_unresolved_string() || tag.is_string()) { + return string_at_noresolve(which); + } else if (tag.is_klass() || tag.is_unresolved_klass()) { + return klass_name_at(which)->as_C_string(); + } else if (tag.is_symbol()) { + return symbol_at(which)->as_C_string(); + } + return ""; +} + +#endif // PRODUCT + + +// JVMTI GetConstantPool support + +// For temporary use until code is stable. +#define DBG(code) + +static const char* WARN_MSG = "Must not be such entry!"; + +static void print_cpool_bytes(jint cnt, u1 *bytes) { + jint size = 0; + u2 idx1, idx2; + + for (jint idx = 1; idx < cnt; idx++) { + jint ent_size = 0; + u1 tag = *bytes++; + size++; // count tag + + printf("const #%03d, tag: %02d ", idx, tag); + switch(tag) { + case JVM_CONSTANT_Invalid: { + printf("Invalid"); + break; + } + case JVM_CONSTANT_Unicode: { + printf("Unicode %s", WARN_MSG); + break; + } + case JVM_CONSTANT_Utf8: { + u2 len = Bytes::get_Java_u2(bytes); + char str[128]; + if (len > 127) { + len = 127; + } + strncpy(str, (char *) (bytes+2), len); + str[len] = '\0'; + printf("Utf8 \"%s\"", str); + ent_size = 2 + len; + break; + } + case JVM_CONSTANT_Integer: { + u4 val = Bytes::get_Java_u4(bytes); + printf("int %d", *(int *) &val); + ent_size = 4; + break; + } + case JVM_CONSTANT_Float: { + u4 val = Bytes::get_Java_u4(bytes); + printf("float %5.3ff", *(float *) &val); + ent_size = 4; + break; + } + case JVM_CONSTANT_Long: { + u8 val = Bytes::get_Java_u8(bytes); + printf("long %lldl", *(jlong *) &val); + ent_size = 8; + idx++; // Long takes two cpool slots + break; + } + case JVM_CONSTANT_Double: { + u8 val = Bytes::get_Java_u8(bytes); + printf("double %5.3fd", *(jdouble *)&val); + ent_size = 8; + idx++; // Double takes two cpool slots + break; + } + case JVM_CONSTANT_Class: { + idx1 = Bytes::get_Java_u2(bytes); + printf("class #%03d", idx1); + ent_size = 2; + break; + } + case JVM_CONSTANT_String: { + idx1 = Bytes::get_Java_u2(bytes); + printf("String #%03d", idx1); + ent_size = 2; + break; + } + case JVM_CONSTANT_Fieldref: { + idx1 = Bytes::get_Java_u2(bytes); + idx2 = Bytes::get_Java_u2(bytes+2); + printf("Field #%03d, #%03d", (int) idx1, (int) idx2); + ent_size = 4; + break; + } + case JVM_CONSTANT_Methodref: { + idx1 = Bytes::get_Java_u2(bytes); + idx2 = Bytes::get_Java_u2(bytes+2); + printf("Method #%03d, #%03d", idx1, idx2); + ent_size = 4; + break; + } + case JVM_CONSTANT_InterfaceMethodref: { + idx1 = Bytes::get_Java_u2(bytes); + idx2 = Bytes::get_Java_u2(bytes+2); + printf("InterfMethod #%03d, #%03d", idx1, idx2); + ent_size = 4; + break; + } + case JVM_CONSTANT_NameAndType: { + idx1 = Bytes::get_Java_u2(bytes); + idx2 = Bytes::get_Java_u2(bytes+2); + printf("NameAndType #%03d, #%03d", idx1, idx2); + ent_size = 4; + break; + } + case JVM_CONSTANT_ClassIndex: { + printf("ClassIndex %s", WARN_MSG); + break; + } + case JVM_CONSTANT_UnresolvedClass: { + printf("UnresolvedClass: %s", WARN_MSG); + break; + } + case JVM_CONSTANT_UnresolvedClassInError: { + printf("UnresolvedClassInErr: %s", WARN_MSG); + break; + } + case JVM_CONSTANT_StringIndex: { + printf("StringIndex: %s", WARN_MSG); + break; + } + case JVM_CONSTANT_UnresolvedString: { + printf("UnresolvedString: %s", WARN_MSG); + break; + } + } + printf(";\n"); + bytes += ent_size; + size += ent_size; + } + printf("Cpool size: %d\n", size); + fflush(0); + return; +} /* end print_cpool_bytes */ + + +// Returns size of constant pool entry. +jint constantPoolOopDesc::cpool_entry_size(jint idx) { + switch(tag_at(idx).value()) { + case JVM_CONSTANT_Invalid: + case JVM_CONSTANT_Unicode: + return 1; + + case JVM_CONSTANT_Utf8: + return 3 + symbol_at(idx)->utf8_length(); + + case JVM_CONSTANT_Class: + case JVM_CONSTANT_String: + case JVM_CONSTANT_ClassIndex: + case JVM_CONSTANT_UnresolvedClass: + case JVM_CONSTANT_UnresolvedClassInError: + case JVM_CONSTANT_StringIndex: + case JVM_CONSTANT_UnresolvedString: + return 3; + + case JVM_CONSTANT_Integer: + case JVM_CONSTANT_Float: + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: + case JVM_CONSTANT_NameAndType: + return 5; + + case JVM_CONSTANT_Long: + case JVM_CONSTANT_Double: + return 9; + } + assert(false, "cpool_entry_size: Invalid constant pool entry tag"); + return 1; +} /* end cpool_entry_size */ + + +// SymbolHashMap is used to find a constant pool index from a string. +// This function fills in SymbolHashMaps, one for utf8s and one for +// class names, returns size of the cpool raw bytes. +jint constantPoolOopDesc::hash_entries_to(SymbolHashMap *symmap, + SymbolHashMap *classmap) { + jint size = 0; + + for (u2 idx = 1; idx < length(); idx++) { + u2 tag = tag_at(idx).value(); + size += cpool_entry_size(idx); + + switch(tag) { + case JVM_CONSTANT_Utf8: { + symbolOop sym = symbol_at(idx); + symmap->add_entry(sym, idx); + DBG(printf("adding symbol entry %s = %d\n", sym->as_utf8(), idx)); + break; + } + case JVM_CONSTANT_Class: + case JVM_CONSTANT_UnresolvedClass: + case JVM_CONSTANT_UnresolvedClassInError: { + symbolOop sym = klass_name_at(idx); + classmap->add_entry(sym, idx); + DBG(printf("adding class entry %s = %d\n", sym->as_utf8(), idx)); + break; + } + case JVM_CONSTANT_Long: + case JVM_CONSTANT_Double: { + idx++; // Both Long and Double take two cpool slots + break; + } + } + } + return size; +} /* end hash_utf8_entries_to */ + + +// Copy cpool bytes. +// Returns: +// 0, in case of OutOfMemoryError +// -1, in case of internal error +// > 0, count of the raw cpool bytes that have been copied +int constantPoolOopDesc::copy_cpool_bytes(int cpool_size, + SymbolHashMap* tbl, + unsigned char *bytes) { + u2 idx1, idx2; + jint size = 0; + jint cnt = length(); + unsigned char *start_bytes = bytes; + + for (jint idx = 1; idx < cnt; idx++) { + u1 tag = tag_at(idx).value(); + jint ent_size = cpool_entry_size(idx); + + assert(size + ent_size <= cpool_size, "Size mismatch"); + + *bytes = tag; + DBG(printf("#%03hd tag=%03hd, ", idx, tag)); + switch(tag) { + case JVM_CONSTANT_Invalid: { + DBG(printf("JVM_CONSTANT_Invalid")); + break; + } + case JVM_CONSTANT_Unicode: { + assert(false, "Wrong constant pool tag: JVM_CONSTANT_Unicode"); + DBG(printf("JVM_CONSTANT_Unicode")); + break; + } + case JVM_CONSTANT_Utf8: { + symbolOop sym = symbol_at(idx); + char* str = sym->as_utf8(); + // Warning! It's crashing on x86 with len = sym->utf8_length() + int len = (int) strlen(str); + Bytes::put_Java_u2((address) (bytes+1), (u2) len); + for (int i = 0; i < len; i++) { + bytes[3+i] = (u1) str[i]; + } + DBG(printf("JVM_CONSTANT_Utf8: %s ", str)); + break; + } + case JVM_CONSTANT_Integer: { + jint val = int_at(idx); + Bytes::put_Java_u4((address) (bytes+1), *(u4*)&val); + break; + } + case JVM_CONSTANT_Float: { + jfloat val = float_at(idx); + Bytes::put_Java_u4((address) (bytes+1), *(u4*)&val); + break; + } + case JVM_CONSTANT_Long: { + jlong val = long_at(idx); + Bytes::put_Java_u8((address) (bytes+1), *(u8*)&val); + idx++; // Long takes two cpool slots + break; + } + case JVM_CONSTANT_Double: { + jdouble val = double_at(idx); + Bytes::put_Java_u8((address) (bytes+1), *(u8*)&val); + idx++; // Double takes two cpool slots + break; + } + case JVM_CONSTANT_Class: + case JVM_CONSTANT_UnresolvedClass: + case JVM_CONSTANT_UnresolvedClassInError: { + *bytes = JVM_CONSTANT_Class; + symbolOop sym = klass_name_at(idx); + idx1 = tbl->symbol_to_value(sym); + assert(idx1 != 0, "Have not found a hashtable entry"); + Bytes::put_Java_u2((address) (bytes+1), idx1); + DBG(printf("JVM_CONSTANT_Class: idx=#%03hd, %s", idx1, sym->as_utf8())); + break; + } + case JVM_CONSTANT_String: { + unsigned int hash; + char *str = string_at_noresolve(idx); + symbolOop sym = SymbolTable::lookup_only(str, (int) strlen(str), hash); + idx1 = tbl->symbol_to_value(sym); + assert(idx1 != 0, "Have not found a hashtable entry"); + Bytes::put_Java_u2((address) (bytes+1), idx1); + DBG(printf("JVM_CONSTANT_String: idx=#%03hd, %s", idx1, str)); + break; + } + case JVM_CONSTANT_UnresolvedString: { + *bytes = JVM_CONSTANT_String; + symbolOop sym = unresolved_string_at(idx); + idx1 = tbl->symbol_to_value(sym); + assert(idx1 != 0, "Have not found a hashtable entry"); + Bytes::put_Java_u2((address) (bytes+1), idx1); + DBG(char *str = sym->as_utf8()); + DBG(printf("JVM_CONSTANT_UnresolvedString: idx=#%03hd, %s", idx1, str)); + break; + } + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: { + idx1 = uncached_klass_ref_index_at(idx); + idx2 = uncached_name_and_type_ref_index_at(idx); + Bytes::put_Java_u2((address) (bytes+1), idx1); + Bytes::put_Java_u2((address) (bytes+3), idx2); + DBG(printf("JVM_CONSTANT_Methodref: %hd %hd", idx1, idx2)); + break; + } + case JVM_CONSTANT_NameAndType: { + idx1 = name_ref_index_at(idx); + idx2 = signature_ref_index_at(idx); + Bytes::put_Java_u2((address) (bytes+1), idx1); + Bytes::put_Java_u2((address) (bytes+3), idx2); + DBG(printf("JVM_CONSTANT_NameAndType: %hd %hd", idx1, idx2)); + break; + } + case JVM_CONSTANT_ClassIndex: { + *bytes = JVM_CONSTANT_Class; + idx1 = klass_index_at(idx); + Bytes::put_Java_u2((address) (bytes+1), idx1); + DBG(printf("JVM_CONSTANT_ClassIndex: %hd", idx1)); + break; + } + case JVM_CONSTANT_StringIndex: { + *bytes = JVM_CONSTANT_String; + idx1 = string_index_at(idx); + Bytes::put_Java_u2((address) (bytes+1), idx1); + DBG(printf("JVM_CONSTANT_StringIndex: %hd", idx1)); + break; + } + } + DBG(printf("\n")); + bytes += ent_size; + size += ent_size; + } + assert(size == cpool_size, "Size mismatch"); + + // Keep temorarily for debugging until it's stable. + DBG(print_cpool_bytes(cnt, start_bytes)); + return (int)(bytes - start_bytes); +} /* end copy_cpool_bytes */ + + +void SymbolHashMap::add_entry(symbolOop sym, u2 value) { + char *str = sym->as_utf8(); + unsigned int hash = compute_hash(str, sym->utf8_length()); + unsigned int index = hash % table_size(); + + // check if already in map + // we prefer the first entry since it is more likely to be what was used in + // the class file + for (SymbolHashMapEntry *en = bucket(index); en != NULL; en = en->next()) { + assert(en->symbol() != NULL, "SymbolHashMapEntry symbol is NULL"); + if (en->hash() == hash && en->symbol() == sym) { + return; // already there + } + } + + SymbolHashMapEntry* entry = new SymbolHashMapEntry(hash, sym, value); + entry->set_next(bucket(index)); + _buckets[index].set_entry(entry); + assert(entry->symbol() != NULL, "SymbolHashMapEntry symbol is NULL"); +} + +SymbolHashMapEntry* SymbolHashMap::find_entry(symbolOop sym) { + assert(sym != NULL, "SymbolHashMap::find_entry - symbol is NULL"); + char *str = sym->as_utf8(); + int len = sym->utf8_length(); + unsigned int hash = SymbolHashMap::compute_hash(str, len); + unsigned int index = hash % table_size(); + for (SymbolHashMapEntry *en = bucket(index); en != NULL; en = en->next()) { + assert(en->symbol() != NULL, "SymbolHashMapEntry symbol is NULL"); + if (en->hash() == hash && en->symbol() == sym) { + return en; + } + } + return NULL; +} diff --git a/hotspot/src/share/vm/oops/constantPoolOop.hpp b/hotspot/src/share/vm/oops/constantPoolOop.hpp new file mode 100644 index 00000000000..3083a3240c6 --- /dev/null +++ b/hotspot/src/share/vm/oops/constantPoolOop.hpp @@ -0,0 +1,528 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A constantPool is an array containing class constants as described in the +// class file. +// +// Most of the constant pool entries are written during class parsing, which +// is safe. For klass and string types, the constant pool entry is +// modified when the entry is resolved. If a klass or string constant pool +// entry is read without a lock, only the resolved state guarantees that +// the entry in the constant pool is a klass or String object and +// not a symbolOop. + +class SymbolHashMap; + +class constantPoolOopDesc : public arrayOopDesc { + friend class VMStructs; + friend class BytecodeInterpreter; // Directly extracts an oop in the pool for fast instanceof/checkcast + private: + typeArrayOop _tags; // the tag array describing the constant pool's contents + constantPoolCacheOop _cache; // the cache holding interpreter runtime information + klassOop _pool_holder; // the corresponding class + // only set to non-zero if constant pool is merged by RedefineClasses + int _orig_length; + + void set_tags(typeArrayOop tags) { oop_store_without_check((oop*)&_tags, tags); } + void tag_at_put(int which, jbyte t) { tags()->byte_at_put(which, t); } + void release_tag_at_put(int which, jbyte t) { tags()->release_byte_at_put(which, t); } + + private: + intptr_t* base() const { return (intptr_t*) (((char*) this) + sizeof(constantPoolOopDesc)); } + oop* tags_addr() { return (oop*)&_tags; } + oop* cache_addr() { return (oop*)&_cache; } + + oop* obj_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return (oop*) &base()[which]; + } + + jint* int_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return (jint*) &base()[which]; + } + + jlong* long_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return (jlong*) &base()[which]; + } + + jfloat* float_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return (jfloat*) &base()[which]; + } + + jdouble* double_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return (jdouble*) &base()[which]; + } + + public: + typeArrayOop tags() const { return _tags; } + + // Klass holding pool + klassOop pool_holder() const { return _pool_holder; } + void set_pool_holder(klassOop k) { oop_store_without_check((oop*)&_pool_holder, (oop) k); } + oop* pool_holder_addr() { return (oop*)&_pool_holder; } + + // Interpreter runtime support + constantPoolCacheOop cache() const { return _cache; } + void set_cache(constantPoolCacheOop cache){ oop_store((oop*)&_cache, cache); } + + // Assembly code support + static int tags_offset_in_bytes() { return offset_of(constantPoolOopDesc, _tags); } + static int cache_offset_in_bytes() { return offset_of(constantPoolOopDesc, _cache); } + static int pool_holder_offset_in_bytes() { return offset_of(constantPoolOopDesc, _pool_holder); } + + // Storing constants + + void klass_at_put(int which, klassOop k) { + oop_store_without_check((volatile oop *)obj_at_addr(which), oop(k)); + // The interpreter assumes when the tag is stored, the klass is resolved + // and the klassOop is a klass rather than a symbolOop, so we need + // hardware store ordering here. + release_tag_at_put(which, JVM_CONSTANT_Class); + if (UseConcMarkSweepGC) { + // In case the earlier card-mark was consumed by a concurrent + // marking thread before the tag was updated, redirty the card. + oop_store_without_check((volatile oop *)obj_at_addr(which), oop(k)); + } + } + + // For temporary use while constructing constant pool + void klass_index_at_put(int which, int name_index) { + tag_at_put(which, JVM_CONSTANT_ClassIndex); + *int_at_addr(which) = name_index; + } + + // Temporary until actual use + void unresolved_klass_at_put(int which, symbolOop s) { + // Overwrite the old index with a GC friendly value so + // that if GC looks during the transition it won't try + // to treat a small integer as oop. + *obj_at_addr(which) = NULL; + release_tag_at_put(which, JVM_CONSTANT_UnresolvedClass); + oop_store_without_check(obj_at_addr(which), oop(s)); + } + + // Temporary until actual use + void unresolved_string_at_put(int which, symbolOop s) { + *obj_at_addr(which) = NULL; + release_tag_at_put(which, JVM_CONSTANT_UnresolvedString); + oop_store_without_check(obj_at_addr(which), oop(s)); + } + + void int_at_put(int which, jint i) { + tag_at_put(which, JVM_CONSTANT_Integer); + *int_at_addr(which) = i; + } + + void long_at_put(int which, jlong l) { + tag_at_put(which, JVM_CONSTANT_Long); + // *long_at_addr(which) = l; + Bytes::put_native_u8((address)long_at_addr(which), *((u8*) &l)); + } + + void float_at_put(int which, jfloat f) { + tag_at_put(which, JVM_CONSTANT_Float); + *float_at_addr(which) = f; + } + + void double_at_put(int which, jdouble d) { + tag_at_put(which, JVM_CONSTANT_Double); + // *double_at_addr(which) = d; + // u8 temp = *(u8*) &d; + Bytes::put_native_u8((address) double_at_addr(which), *((u8*) &d)); + } + + void symbol_at_put(int which, symbolOop s) { + tag_at_put(which, JVM_CONSTANT_Utf8); + oop_store_without_check(obj_at_addr(which), oop(s)); + } + + void string_at_put(int which, oop str) { + oop_store((volatile oop*)obj_at_addr(which), str); + release_tag_at_put(which, JVM_CONSTANT_String); + if (UseConcMarkSweepGC) { + // In case the earlier card-mark was consumed by a concurrent + // marking thread before the tag was updated, redirty the card. + oop_store_without_check((volatile oop *)obj_at_addr(which), str); + } + } + + // For temporary use while constructing constant pool + void string_index_at_put(int which, int string_index) { + tag_at_put(which, JVM_CONSTANT_StringIndex); + *int_at_addr(which) = string_index; + } + + void field_at_put(int which, int class_index, int name_and_type_index) { + tag_at_put(which, JVM_CONSTANT_Fieldref); + *int_at_addr(which) = ((jint) name_and_type_index<<16) | class_index; + } + + void method_at_put(int which, int class_index, int name_and_type_index) { + tag_at_put(which, JVM_CONSTANT_Methodref); + *int_at_addr(which) = ((jint) name_and_type_index<<16) | class_index; + } + + void interface_method_at_put(int which, int class_index, int name_and_type_index) { + tag_at_put(which, JVM_CONSTANT_InterfaceMethodref); + *int_at_addr(which) = ((jint) name_and_type_index<<16) | class_index; // Not so nice + } + + void name_and_type_at_put(int which, int name_index, int signature_index) { + tag_at_put(which, JVM_CONSTANT_NameAndType); + *int_at_addr(which) = ((jint) signature_index<<16) | name_index; // Not so nice + } + + // Tag query + + constantTag tag_at(int which) const { return (constantTag)tags()->byte_at_acquire(which); } + + // Whether the entry is a pointer that must be GC'd. + bool is_pointer_entry(int which) { + constantTag tag = tag_at(which); + return tag.is_klass() || + tag.is_unresolved_klass() || + tag.is_symbol() || + tag.is_unresolved_string() || + tag.is_string(); + } + + // Fetching constants + + klassOop klass_at(int which, TRAPS) { + constantPoolHandle h_this(THREAD, this); + return klass_at_impl(h_this, which, CHECK_NULL); + } + + symbolOop klass_name_at(int which); // Returns the name, w/o resolving. + + klassOop resolved_klass_at(int which) { // Used by Compiler + guarantee(tag_at(which).is_klass(), "Corrupted constant pool"); + // Must do an acquire here in case another thread resolved the klass + // behind our back, lest we later load stale values thru the oop. + return klassOop((oop)OrderAccess::load_ptr_acquire(obj_at_addr(which))); + } + + // This method should only be used with a cpool lock or during parsing or gc + symbolOop unresolved_klass_at(int which) { // Temporary until actual use + symbolOop s = symbolOop((oop)OrderAccess::load_ptr_acquire(obj_at_addr(which))); + // check that the klass is still unresolved. + assert(tag_at(which).is_unresolved_klass(), "Corrupted constant pool"); + return s; + } + + // RedefineClasses() API support: + symbolOop klass_at_noresolve(int which) { return klass_name_at(which); } + + jint int_at(int which) { + assert(tag_at(which).is_int(), "Corrupted constant pool"); + return *int_at_addr(which); + } + + jlong long_at(int which) { + assert(tag_at(which).is_long(), "Corrupted constant pool"); + // return *long_at_addr(which); + u8 tmp = Bytes::get_native_u8((address)&base()[which]); + return *((jlong*)&tmp); + } + + jfloat float_at(int which) { + assert(tag_at(which).is_float(), "Corrupted constant pool"); + return *float_at_addr(which); + } + + jdouble double_at(int which) { + assert(tag_at(which).is_double(), "Corrupted constant pool"); + u8 tmp = Bytes::get_native_u8((address)&base()[which]); + return *((jdouble*)&tmp); + } + + symbolOop symbol_at(int which) { + assert(tag_at(which).is_utf8(), "Corrupted constant pool"); + return symbolOop(*obj_at_addr(which)); + } + + oop string_at(int which, TRAPS) { + constantPoolHandle h_this(THREAD, this); + return string_at_impl(h_this, which, CHECK_NULL); + } + + // only called when we are sure a string entry is already resolved (via an + // earlier string_at call. + oop resolved_string_at(int which) { + assert(tag_at(which).is_string(), "Corrupted constant pool"); + // Must do an acquire here in case another thread resolved the klass + // behind our back, lest we later load stale values thru the oop. + return (oop)OrderAccess::load_ptr_acquire(obj_at_addr(which)); + } + + // This method should only be used with a cpool lock or during parsing or gc + symbolOop unresolved_string_at(int which) { // Temporary until actual use + symbolOop s = symbolOop((oop)OrderAccess::load_ptr_acquire(obj_at_addr(which))); + // check that the string is still unresolved. + assert(tag_at(which).is_unresolved_string(), "Corrupted constant pool"); + return s; + } + + // Returns an UTF8 for a CONSTANT_String entry at a given index. + // UTF8 char* representation was chosen to avoid conversion of + // java_lang_Strings at resolved entries into symbolOops + // or vice versa. + char* string_at_noresolve(int which); + + jint name_and_type_at(int which) { + assert(tag_at(which).is_name_and_type(), "Corrupted constant pool"); + return *int_at_addr(which); + } + + // The following methods (klass_ref_at, klass_ref_at_noresolve, name_ref_at, + // signature_ref_at, klass_ref_index_at, name_and_type_ref_index_at, + // name_ref_index_at, signature_ref_index_at) all expect constant pool indices + // from the bytecodes to be passed in, which are actually potentially byte-swapped + // contstant pool cache indices. See field_or_method_at. + + // Lookup for entries consisting of (klass_index, name_and_type index) + klassOop klass_ref_at(int which, TRAPS); + symbolOop klass_ref_at_noresolve(int which); + symbolOop name_ref_at(int which); + symbolOop signature_ref_at(int which); // the type descriptor + + int klass_ref_index_at(int which); + int name_and_type_ref_index_at(int which); + + // Lookup for entries consisting of (name_index, signature_index) + int name_ref_index_at(int which); + int signature_ref_index_at(int which); + + BasicType basic_type_for_signature_at(int which); + + // Resolve string constants (to prevent allocation during compilation) + void resolve_string_constants(TRAPS) { + constantPoolHandle h_this(THREAD, this); + resolve_string_constants_impl(h_this, CHECK); + } + + // Klass name matches name at offset + bool klass_name_at_matches(instanceKlassHandle k, int which); + + // Sizing + static int header_size() { return sizeof(constantPoolOopDesc)/HeapWordSize; } + static int object_size(int length) { return align_object_size(header_size() + length); } + int object_size() { return object_size(length()); } + + friend class constantPoolKlass; + friend class ClassFileParser; + friend class SystemDictionary; + + // Used by compiler to prevent classloading. + static klassOop klass_at_if_loaded (constantPoolHandle this_oop, int which); + static klassOop klass_ref_at_if_loaded (constantPoolHandle this_oop, int which); + // Same as above - but does LinkResolving. + static klassOop klass_ref_at_if_loaded_check(constantPoolHandle this_oop, int which, TRAPS); + + // Routines currently used for annotations (only called by jvm.cpp) but which might be used in the + // future by other Java code. These take constant pool indices rather than possibly-byte-swapped + // constant pool cache indices as do the peer methods above. + symbolOop uncached_name_ref_at(int which); + symbolOop uncached_signature_ref_at(int which); + int uncached_klass_ref_index_at(int which); + int uncached_name_and_type_ref_index_at(int which); + + // Sharing + int pre_resolve_shared_klasses(TRAPS); + void shared_symbols_iterate(OopClosure* closure0); + void shared_tags_iterate(OopClosure* closure0); + void shared_strings_iterate(OopClosure* closure0); + + // Debugging + const char* printable_name_at(int which) PRODUCT_RETURN0; + + private: + + // Takes either a constant pool cache index in possibly byte-swapped + // byte order (which comes from the bytecodes after rewriting) or, + // if "uncached" is true, a vanilla constant pool index + jint field_or_method_at(int which, bool uncached) { + int i = -1; + if (uncached || cache() == NULL) { + i = which; + } else { + // change byte-ordering and go via cache + i = cache()->entry_at(Bytes::swap_u2(which))->constant_pool_index(); + } + assert(tag_at(i).is_field_or_method(), "Corrupted constant pool"); + return *int_at_addr(i); + } + + // Used while constructing constant pool (only by ClassFileParser) + jint klass_index_at(int which) { + assert(tag_at(which).is_klass_index(), "Corrupted constant pool"); + return *int_at_addr(which); + } + + jint string_index_at(int which) { + assert(tag_at(which).is_string_index(), "Corrupted constant pool"); + return *int_at_addr(which); + } + + // Performs the LinkResolver checks + static void verify_constant_pool_resolve(constantPoolHandle this_oop, KlassHandle klass, TRAPS); + + // Implementation of methods that needs an exposed 'this' pointer, in order to + // handle GC while executing the method + static klassOop klass_at_impl(constantPoolHandle this_oop, int which, TRAPS); + static oop string_at_impl(constantPoolHandle this_oop, int which, TRAPS); + + // Resolve string constants (to prevent allocation during compilation) + static void resolve_string_constants_impl(constantPoolHandle this_oop, TRAPS); + + public: + // Merging constantPoolOop support: + bool compare_entry_to(int index1, constantPoolHandle cp2, int index2, TRAPS); + void copy_cp_to(int start_i, int end_i, constantPoolHandle to_cp, int to_i, + TRAPS); + void copy_entry_to(int from_i, constantPoolHandle to_cp, int to_i, TRAPS); + int find_matching_entry(int pattern_i, constantPoolHandle search_cp, TRAPS); + int orig_length() const { return _orig_length; } + void set_orig_length(int orig_length) { _orig_length = orig_length; } + + + // JVMTI accesss - GetConstantPool, RetransformClasses, ... + friend class JvmtiConstantPoolReconstituter; + + private: + jint cpool_entry_size(jint idx); + jint hash_entries_to(SymbolHashMap *symmap, SymbolHashMap *classmap); + + // Copy cpool bytes into byte array. + // Returns: + // int > 0, count of the raw cpool bytes that have been copied + // 0, OutOfMemory error + // -1, Internal error + int copy_cpool_bytes(int cpool_size, + SymbolHashMap* tbl, + unsigned char *bytes); +}; + +class SymbolHashMapEntry : public CHeapObj { + private: + unsigned int _hash; // 32-bit hash for item + SymbolHashMapEntry* _next; // Next element in the linked list for this bucket + symbolOop _symbol; // 1-st part of the mapping: symbol => value + u2 _value; // 2-nd part of the mapping: symbol => value + + public: + unsigned int hash() const { return _hash; } + void set_hash(unsigned int hash) { _hash = hash; } + + SymbolHashMapEntry* next() const { return _next; } + void set_next(SymbolHashMapEntry* next) { _next = next; } + + symbolOop symbol() const { return _symbol; } + void set_symbol(symbolOop sym) { _symbol = sym; } + + u2 value() const { return _value; } + void set_value(u2 value) { _value = value; } + + SymbolHashMapEntry(unsigned int hash, symbolOop symbol, u2 value) + : _hash(hash), _symbol(symbol), _value(value), _next(NULL) {} + +}; // End SymbolHashMapEntry class + + +class SymbolHashMapBucket : public CHeapObj { + +private: + SymbolHashMapEntry* _entry; + +public: + SymbolHashMapEntry* entry() const { return _entry; } + void set_entry(SymbolHashMapEntry* entry) { _entry = entry; } + void clear() { _entry = NULL; } + +}; // End SymbolHashMapBucket class + + +class SymbolHashMap: public CHeapObj { + + private: + // Default number of entries in the table + enum SymbolHashMap_Constants { + _Def_HashMap_Size = 256 + }; + + int _table_size; + SymbolHashMapBucket* _buckets; + + void initialize_table(int table_size) { + _table_size = table_size; + _buckets = NEW_C_HEAP_ARRAY(SymbolHashMapBucket, table_size); + for (int index = 0; index < table_size; index++) { + _buckets[index].clear(); + } + } + + public: + + int table_size() const { return _table_size; } + + SymbolHashMap() { initialize_table(_Def_HashMap_Size); } + SymbolHashMap(int table_size) { initialize_table(table_size); } + + // hash P(31) from Kernighan & Ritchie + static unsigned int compute_hash(const char* str, int len) { + unsigned int hash = 0; + while (len-- > 0) { + hash = 31*hash + (unsigned) *str; + str++; + } + return hash; + } + + SymbolHashMapEntry* bucket(int i) { + return _buckets[i].entry(); + } + + void add_entry(symbolOop sym, u2 value); + SymbolHashMapEntry* find_entry(symbolOop sym); + + u2 symbol_to_value(symbolOop sym) { + SymbolHashMapEntry *entry = find_entry(sym); + return (entry == NULL) ? 0 : entry->value(); + } + + ~SymbolHashMap() { + SymbolHashMapEntry* next; + for (int i = 0; i < _table_size; i++) { + for (SymbolHashMapEntry* cur = bucket(i); cur != NULL; cur = next) { + next = cur->next(); + delete(cur); + } + } + delete _buckets; + } +}; // End SymbolHashMap class diff --git a/hotspot/src/share/vm/oops/cpCacheKlass.cpp b/hotspot/src/share/vm/oops/cpCacheKlass.cpp new file mode 100644 index 00000000000..c3f7d764f35 --- /dev/null +++ b/hotspot/src/share/vm/oops/cpCacheKlass.cpp @@ -0,0 +1,205 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_cpCacheKlass.cpp.incl" + + +int constantPoolCacheKlass::oop_size(oop obj) const { + assert(obj->is_constantPoolCache(), "must be constantPool"); + return constantPoolCacheOop(obj)->object_size(); +} + + +constantPoolCacheOop constantPoolCacheKlass::allocate(int length, TRAPS) { + // allocate memory + int size = constantPoolCacheOopDesc::object_size(length); + KlassHandle klass (THREAD, as_klassOop()); + constantPoolCacheOop cache = (constantPoolCacheOop) + CollectedHeap::permanent_array_allocate(klass, size, length, CHECK_NULL); + cache->set_constant_pool(NULL); + return cache; +} + + +klassOop constantPoolCacheKlass::create_klass(TRAPS) { + constantPoolCacheKlass o; + KlassHandle klassklass(THREAD, Universe::arrayKlassKlassObj()); + arrayKlassHandle k = base_create_array_klass(o.vtbl_value(), header_size(), klassklass, CHECK_NULL); + KlassHandle super (THREAD, k->super()); + complete_create_array_klass(k, super, CHECK_NULL); + return k(); +} + + +void constantPoolCacheKlass::oop_follow_contents(oop obj) { + assert(obj->is_constantPoolCache(), "obj must be constant pool cache"); + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constantPoolCacheKlassObj never moves. + // gc of constant pool cache instance variables + MarkSweep::mark_and_push((oop*)cache->constant_pool_addr()); + // gc of constant pool cache entries + int i = cache->length(); + while (i-- > 0) cache->entry_at(i)->follow_contents(); +} + +#ifndef SERIALGC +void constantPoolCacheKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert(obj->is_constantPoolCache(), "obj must be constant pool cache"); + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constantPoolCacheKlassObj never moves. + // gc of constant pool cache instance variables + PSParallelCompact::mark_and_push(cm, (oop*)cache->constant_pool_addr()); + // gc of constant pool cache entries + int i = cache->length(); + while (i-- > 0) cache->entry_at(i)->follow_contents(cm); +} +#endif // SERIALGC + + +int constantPoolCacheKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert(obj->is_constantPoolCache(), "obj must be constant pool cache"); + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = cache->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constantPoolCacheKlassObj never moves. + // iteration over constant pool cache instance variables + blk->do_oop((oop*)cache->constant_pool_addr()); + // iteration over constant pool cache entries + for (int i = 0; i < cache->length(); i++) cache->entry_at(i)->oop_iterate(blk); + return size; +} + + +int constantPoolCacheKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + assert(obj->is_constantPoolCache(), "obj must be constant pool cache"); + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = cache->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constantPoolCacheKlassObj never moves. + // iteration over constant pool cache instance variables + oop* addr = (oop*)cache->constant_pool_addr(); + if (mr.contains(addr)) blk->do_oop(addr); + // iteration over constant pool cache entries + for (int i = 0; i < cache->length(); i++) cache->entry_at(i)->oop_iterate_m(blk, mr); + return size; +} + + +int constantPoolCacheKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_constantPoolCache(), "obj must be constant pool cache"); + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = cache->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::constantPoolCacheKlassObj never moves. + // Iteration over constant pool cache instance variables + MarkSweep::adjust_pointer((oop*)cache->constant_pool_addr()); + // iteration over constant pool cache entries + for (int i = 0; i < cache->length(); i++) + cache->entry_at(i)->adjust_pointers(); + return size; +} + +#ifndef SERIALGC +void constantPoolCacheKlass::oop_copy_contents(PSPromotionManager* pm, + oop obj) { + assert(obj->is_constantPoolCache(), "should be constant pool"); +} + +void constantPoolCacheKlass::oop_push_contents(PSPromotionManager* pm, + oop obj) { + assert(obj->is_constantPoolCache(), "should be constant pool"); +} + +int +constantPoolCacheKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert(obj->is_constantPoolCache(), "obj must be constant pool cache"); + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + + // Iteration over constant pool cache instance variables + PSParallelCompact::adjust_pointer((oop*)cache->constant_pool_addr()); + + // iteration over constant pool cache entries + for (int i = 0; i < cache->length(); ++i) { + cache->entry_at(i)->update_pointers(); + } + + return cache->object_size(); +} + +int +constantPoolCacheKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, + HeapWord* end_addr) { + assert(obj->is_constantPoolCache(), "obj must be constant pool cache"); + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + + // Iteration over constant pool cache instance variables + oop* p; + p = (oop*)cache->constant_pool_addr(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + + // Iteration over constant pool cache entries + for (int i = 0; i < cache->length(); ++i) { + cache->entry_at(i)->update_pointers(beg_addr, end_addr); + } + return cache->object_size(); +} +#endif // SERIALGC + +#ifndef PRODUCT + +void constantPoolCacheKlass::oop_print_on(oop obj, outputStream* st) { + assert(obj->is_constantPoolCache(), "obj must be constant pool cache"); + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + // super print + arrayKlass::oop_print_on(obj, st); + // print constant pool cache entries + for (int i = 0; i < cache->length(); i++) cache->entry_at(i)->print(st, i); +} + +#endif + +void constantPoolCacheKlass::oop_verify_on(oop obj, outputStream* st) { + guarantee(obj->is_constantPoolCache(), "obj must be constant pool cache"); + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + // super verify + arrayKlass::oop_verify_on(obj, st); + // print constant pool cache entries + for (int i = 0; i < cache->length(); i++) cache->entry_at(i)->verify(st); +} + + +const char* constantPoolCacheKlass::internal_name() const { + return "{constant pool cache}"; +} diff --git a/hotspot/src/share/vm/oops/cpCacheKlass.hpp b/hotspot/src/share/vm/oops/cpCacheKlass.hpp new file mode 100644 index 00000000000..7eb8d44577a --- /dev/null +++ b/hotspot/src/share/vm/oops/cpCacheKlass.hpp @@ -0,0 +1,68 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class constantPoolCacheKlass: public arrayKlass { + public: + // Dispatched klass operations + bool oop_is_constantPoolCache() const { return true; } + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(constantPoolCacheKlass); + constantPoolCacheOop allocate(int length, TRAPS); + static klassOop create_klass(TRAPS); + + // Casting from klassOop + static constantPoolCacheKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_constantPoolCache(), "cast to constantPoolCacheKlass"); + return (constantPoolCacheKlass*)k->klass_part(); + } + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(constantPoolCacheKlass)/HeapWordSize; } + int object_size() const { return arrayKlass::object_size(header_size()); } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on(oop obj, outputStream* st); +#endif + + public: + // Verification + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); +}; diff --git a/hotspot/src/share/vm/oops/cpCacheOop.cpp b/hotspot/src/share/vm/oops/cpCacheOop.cpp new file mode 100644 index 00000000000..3ffee53be23 --- /dev/null +++ b/hotspot/src/share/vm/oops/cpCacheOop.cpp @@ -0,0 +1,449 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_cpCacheOop.cpp.incl" + + +// Implememtation of ConstantPoolCacheEntry + +void ConstantPoolCacheEntry::set_initial_state(int index) { + assert(0 <= index && index < 0x10000, "sanity check"); + _indices = index; +} + + +int ConstantPoolCacheEntry::as_flags(TosState state, bool is_final, + bool is_vfinal, bool is_volatile, + bool is_method_interface, bool is_method) { + int f = state; + + assert( state < number_of_states, "Invalid state in as_flags"); + + f <<= 1; + if (is_final) f |= 1; + f <<= 1; + if (is_vfinal) f |= 1; + f <<= 1; + if (is_volatile) f |= 1; + f <<= 1; + if (is_method_interface) f |= 1; + f <<= 1; + if (is_method) f |= 1; + f <<= ConstantPoolCacheEntry::hotSwapBit; + // Preserve existing flag bit values +#ifdef ASSERT + int old_state = ((_flags >> tosBits) & 0x0F); + assert(old_state == 0 || old_state == state, + "inconsistent cpCache flags state"); +#endif + return (_flags | f) ; +} + +void ConstantPoolCacheEntry::set_bytecode_1(Bytecodes::Code code) { +#ifdef ASSERT + // Read once. + volatile Bytecodes::Code c = bytecode_1(); + assert(c == 0 || c == code || code == 0, "update must be consistent"); +#endif + // Need to flush pending stores here before bytecode is written. + OrderAccess::release_store_ptr(&_indices, _indices | ((u_char)code << 16)); +} + +void ConstantPoolCacheEntry::set_bytecode_2(Bytecodes::Code code) { +#ifdef ASSERT + // Read once. + volatile Bytecodes::Code c = bytecode_2(); + assert(c == 0 || c == code || code == 0, "update must be consistent"); +#endif + // Need to flush pending stores here before bytecode is written. + OrderAccess::release_store_ptr(&_indices, _indices | ((u_char)code << 24)); +} + +#ifdef ASSERT +// It is possible to have two different dummy methodOops created +// when the resolve code for invoke interface executes concurrently +// Hence the assertion below is weakened a bit for the invokeinterface +// case. +bool ConstantPoolCacheEntry::same_methodOop(oop cur_f1, oop f1) { + return (cur_f1 == f1 || ((methodOop)cur_f1)->name() == + ((methodOop)f1)->name() || ((methodOop)cur_f1)->signature() == + ((methodOop)f1)->signature()); +} +#endif + +// Note that concurrent update of both bytecodes can leave one of them +// reset to zero. This is harmless; the interpreter will simply re-resolve +// the damaged entry. More seriously, the memory synchronization is needed +// to flush other fields (f1, f2) completely to memory before the bytecodes +// are updated, lest other processors see a non-zero bytecode but zero f1/f2. +void ConstantPoolCacheEntry::set_field(Bytecodes::Code get_code, + Bytecodes::Code put_code, + KlassHandle field_holder, + int orig_field_index, + int field_offset, + TosState field_type, + bool is_final, + bool is_volatile) { + set_f1(field_holder()); + set_f2(field_offset); + // The field index is used by jvm/ti and is the index into fields() array + // in holder instanceKlass. This is scaled by instanceKlass::next_offset. + assert((orig_field_index % instanceKlass::next_offset) == 0, "wierd index"); + const int field_index = orig_field_index / instanceKlass::next_offset; + assert(field_index <= field_index_mask, + "field index does not fit in low flag bits"); + set_flags(as_flags(field_type, is_final, false, is_volatile, false, false) | + (field_index & field_index_mask)); + set_bytecode_1(get_code); + set_bytecode_2(put_code); + NOT_PRODUCT(verify(tty)); +} + +int ConstantPoolCacheEntry::field_index() const { + return (_flags & field_index_mask) * instanceKlass::next_offset; +} + +void ConstantPoolCacheEntry::set_method(Bytecodes::Code invoke_code, + methodHandle method, + int vtable_index) { + + assert(method->interpreter_entry() != NULL, "should have been set at this point"); + assert(!method->is_obsolete(), "attempt to write obsolete method to cpCache"); + bool change_to_virtual = (invoke_code == Bytecodes::_invokeinterface); + + int byte_no = -1; + bool needs_vfinal_flag = false; + switch (invoke_code) { + case Bytecodes::_invokevirtual: + case Bytecodes::_invokeinterface: { + if (method->can_be_statically_bound()) { + set_f2((intptr_t)method()); + needs_vfinal_flag = true; + } else { + assert(vtable_index >= 0, "valid index"); + set_f2(vtable_index); + } + byte_no = 2; + break; + } + case Bytecodes::_invokespecial: + // Preserve the value of the vfinal flag on invokevirtual bytecode + // which may be shared with this constant pool cache entry. + needs_vfinal_flag = is_resolved(Bytecodes::_invokevirtual) && is_vfinal(); + // fall through + case Bytecodes::_invokestatic: + set_f1(method()); + byte_no = 1; + break; + default: + ShouldNotReachHere(); + break; + } + + set_flags(as_flags(as_TosState(method->result_type()), + method->is_final_method(), + needs_vfinal_flag, + false, + change_to_virtual, + true)| + method()->size_of_parameters()); + + // Note: byte_no also appears in TemplateTable::resolve. + if (byte_no == 1) { + set_bytecode_1(invoke_code); + } else if (byte_no == 2) { + if (change_to_virtual) { + // NOTE: THIS IS A HACK - BE VERY CAREFUL!!! + // + // Workaround for the case where we encounter an invokeinterface, but we + // should really have an _invokevirtual since the resolved method is a + // virtual method in java.lang.Object. This is a corner case in the spec + // but is presumably legal. javac does not generate this code. + // + // We set bytecode_1() to _invokeinterface, because that is the + // bytecode # used by the interpreter to see if it is resolved. + // We set bytecode_2() to _invokevirtual. + // See also interpreterRuntime.cpp. (8/25/2000) + // Only set resolved for the invokeinterface case if method is public. + // Otherwise, the method needs to be reresolved with caller for each + // interface call. + if (method->is_public()) set_bytecode_1(invoke_code); + set_bytecode_2(Bytecodes::_invokevirtual); + } else { + set_bytecode_2(invoke_code); + } + } else { + ShouldNotReachHere(); + } + NOT_PRODUCT(verify(tty)); +} + + +void ConstantPoolCacheEntry::set_interface_call(methodHandle method, int index) { + klassOop interf = method->method_holder(); + assert(instanceKlass::cast(interf)->is_interface(), "must be an interface"); + set_f1(interf); + set_f2(index); + set_flags(as_flags(as_TosState(method->result_type()), method->is_final_method(), false, false, false, true) | method()->size_of_parameters()); + set_bytecode_1(Bytecodes::_invokeinterface); +} + + +class LocalOopClosure: public OopClosure { + private: + void (*_f)(oop*); + + public: + LocalOopClosure(void f(oop*)) { _f = f; } + virtual void do_oop(oop* o) { _f(o); } +}; + + +void ConstantPoolCacheEntry::oops_do(void f(oop*)) { + LocalOopClosure blk(f); + oop_iterate(&blk); +} + + +void ConstantPoolCacheEntry::oop_iterate(OopClosure* blk) { + assert(in_words(size()) == 4, "check code below - may need adjustment"); + // field[1] is always oop or NULL + blk->do_oop((oop*)&_f1); + if (is_vfinal()) { + blk->do_oop((oop*)&_f2); + } +} + + +void ConstantPoolCacheEntry::oop_iterate_m(OopClosure* blk, MemRegion mr) { + assert(in_words(size()) == 4, "check code below - may need adjustment"); + // field[1] is always oop or NULL + if (mr.contains((oop *)&_f1)) blk->do_oop((oop*)&_f1); + if (is_vfinal()) { + if (mr.contains((oop *)&_f2)) blk->do_oop((oop*)&_f2); + } +} + + +void ConstantPoolCacheEntry::follow_contents() { + assert(in_words(size()) == 4, "check code below - may need adjustment"); + // field[1] is always oop or NULL + MarkSweep::mark_and_push((oop*)&_f1); + if (is_vfinal()) { + MarkSweep::mark_and_push((oop*)&_f2); + } +} + +#ifndef SERIALGC +void ConstantPoolCacheEntry::follow_contents(ParCompactionManager* cm) { + assert(in_words(size()) == 4, "check code below - may need adjustment"); + // field[1] is always oop or NULL + PSParallelCompact::mark_and_push(cm, (oop*)&_f1); + if (is_vfinal()) { + PSParallelCompact::mark_and_push(cm, (oop*)&_f2); + } +} +#endif // SERIALGC + +void ConstantPoolCacheEntry::adjust_pointers() { + assert(in_words(size()) == 4, "check code below - may need adjustment"); + // field[1] is always oop or NULL + MarkSweep::adjust_pointer((oop*)&_f1); + if (is_vfinal()) { + MarkSweep::adjust_pointer((oop*)&_f2); + } +} + +#ifndef SERIALGC +void ConstantPoolCacheEntry::update_pointers() { + assert(in_words(size()) == 4, "check code below - may need adjustment"); + // field[1] is always oop or NULL + PSParallelCompact::adjust_pointer((oop*)&_f1); + if (is_vfinal()) { + PSParallelCompact::adjust_pointer((oop*)&_f2); + } +} + +void ConstantPoolCacheEntry::update_pointers(HeapWord* beg_addr, + HeapWord* end_addr) { + assert(in_words(size()) == 4, "check code below - may need adjustment"); + // field[1] is always oop or NULL + PSParallelCompact::adjust_pointer((oop*)&_f1, beg_addr, end_addr); + if (is_vfinal()) { + PSParallelCompact::adjust_pointer((oop*)&_f2, beg_addr, end_addr); + } +} +#endif // SERIALGC + +// RedefineClasses() API support: +// If this constantPoolCacheEntry refers to old_method then update it +// to refer to new_method. +bool ConstantPoolCacheEntry::adjust_method_entry(methodOop old_method, + methodOop new_method, bool * trace_name_printed) { + + if (is_vfinal()) { + // virtual and final so f2() contains method ptr instead of vtable index + if (f2() == (intptr_t)old_method) { + // match old_method so need an update + _f2 = (intptr_t)new_method; + if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) { + if (!(*trace_name_printed)) { + // RC_TRACE_MESG macro has an embedded ResourceMark + RC_TRACE_MESG(("adjust: name=%s", + Klass::cast(old_method->method_holder())->external_name())); + *trace_name_printed = true; + } + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00400000, ("cpc vf-entry update: %s(%s)", + new_method->name()->as_C_string(), + new_method->signature()->as_C_string())); + } + + return true; + } + + // f1() is not used with virtual entries so bail out + return false; + } + + if ((oop)_f1 == NULL) { + // NULL f1() means this is a virtual entry so bail out + // We are assuming that the vtable index does not need change. + return false; + } + + if ((oop)_f1 == old_method) { + _f1 = new_method; + if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) { + if (!(*trace_name_printed)) { + // RC_TRACE_MESG macro has an embedded ResourceMark + RC_TRACE_MESG(("adjust: name=%s", + Klass::cast(old_method->method_holder())->external_name())); + *trace_name_printed = true; + } + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00400000, ("cpc entry update: %s(%s)", + new_method->name()->as_C_string(), + new_method->signature()->as_C_string())); + } + + return true; + } + + return false; +} + +bool ConstantPoolCacheEntry::is_interesting_method_entry(klassOop k) { + if (!is_method_entry()) { + // not a method entry so not interesting by default + return false; + } + + methodOop m = NULL; + if (is_vfinal()) { + // virtual and final so _f2 contains method ptr instead of vtable index + m = (methodOop)_f2; + } else if ((oop)_f1 == NULL) { + // NULL _f1 means this is a virtual entry so also not interesting + return false; + } else { + if (!((oop)_f1)->is_method()) { + // _f1 can also contain a klassOop for an interface + return false; + } + m = (methodOop)_f1; + } + + assert(m != NULL && m->is_method(), "sanity check"); + if (m == NULL || !m->is_method() || m->method_holder() != k) { + // robustness for above sanity checks or method is not in + // the interesting class + return false; + } + + // the method is in the interesting class so the entry is interesting + return true; +} + +void ConstantPoolCacheEntry::print(outputStream* st, int index) const { + // print separator + if (index == 0) tty->print_cr(" -------------"); + // print entry + tty->print_cr("%3d (%08x) [%02x|%02x|%5d]", index, this, bytecode_2(), bytecode_1(), constant_pool_index()); + tty->print_cr(" [ %08x]", (address)(oop)_f1); + tty->print_cr(" [ %08x]", _f2); + tty->print_cr(" [ %08x]", _flags); + tty->print_cr(" -------------"); +} + +void ConstantPoolCacheEntry::verify(outputStream* st) const { + // not implemented yet +} + +// Implementation of ConstantPoolCache + +void constantPoolCacheOopDesc::initialize(intArray& inverse_index_map) { + assert(inverse_index_map.length() == length(), "inverse index map must have same length as cache"); + for (int i = 0; i < length(); i++) entry_at(i)->set_initial_state(inverse_index_map[i]); +} + +// RedefineClasses() API support: +// If any entry of this constantPoolCache points to any of +// old_methods, replace it with the corresponding new_method. +void constantPoolCacheOopDesc::adjust_method_entries(methodOop* old_methods, methodOop* new_methods, + int methods_length, bool * trace_name_printed) { + + if (methods_length == 0) { + // nothing to do if there are no methods + return; + } + + // get shorthand for the interesting class + klassOop old_holder = old_methods[0]->method_holder(); + + for (int i = 0; i < length(); i++) { + if (!entry_at(i)->is_interesting_method_entry(old_holder)) { + // skip uninteresting methods + continue; + } + + // The constantPoolCache contains entries for several different + // things, but we only care about methods. In fact, we only care + // about methods in the same class as the one that contains the + // old_methods. At this point, we have an interesting entry. + + for (int j = 0; j < methods_length; j++) { + methodOop old_method = old_methods[j]; + methodOop new_method = new_methods[j]; + + if (entry_at(i)->adjust_method_entry(old_method, new_method, + trace_name_printed)) { + // current old_method matched this entry and we updated it so + // break out and get to the next interesting entry if there one + break; + } + } + } +} diff --git a/hotspot/src/share/vm/oops/cpCacheOop.hpp b/hotspot/src/share/vm/oops/cpCacheOop.hpp new file mode 100644 index 00000000000..55f7fcbba3a --- /dev/null +++ b/hotspot/src/share/vm/oops/cpCacheOop.hpp @@ -0,0 +1,325 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A ConstantPoolCacheEntry describes an individual entry of the constant +// pool cache. There's 2 principal kinds of entries: field entries for in- +// stance & static field access, and method entries for invokes. Some of +// the entry layout is shared and looks as follows: +// +// bit number |31 0| +// bit length |-8--|-8--|---16----| +// -------------------------------- +// _indices [ b2 | b1 | index ] +// _f1 [ entry specific ] +// _f2 [ entry specific ] +// _flags [t|f|vf|v|m|h|unused|field_index] (for field entries) +// bit length |4|1|1 |1|1|0|---7--|----16-----] +// _flags [t|f|vf|v|m|h|unused|eidx|psze] (for method entries) +// bit length |4|1|1 |1|1|1|---7--|-8--|-8--] + +// -------------------------------- +// +// with: +// index = original constant pool index +// b1 = bytecode 1 +// b2 = bytecode 2 +// psze = parameters size (method entries only) +// eidx = interpreter entry index (method entries only) +// field_index = index into field information in holder instanceKlass +// The index max is 0xffff (max number of fields in constant pool) +// and is multiplied by (instanceKlass::next_offset) when accessing. +// t = TosState (see below) +// f = field is marked final (see below) +// vf = virtual, final (method entries only : is_vfinal()) +// v = field is volatile (see below) +// m = invokeinterface used for method in class Object (see below) +// h = RedefineClasses/Hotswap bit (see below) +// +// The flags after TosState have the following interpretation: +// bit 27: f flag true if field is marked final +// bit 26: vf flag true if virtual final method +// bit 25: v flag true if field is volatile (only for fields) +// bit 24: m flag true if invokeinterface used for method in class Object +// bit 23: 0 for fields, 1 for methods +// +// The flags 31, 30, 29, 28 together build a 4 bit number 0 to 8 with the +// following mapping to the TosState states: +// +// btos: 0 +// ctos: 1 +// stos: 2 +// itos: 3 +// ltos: 4 +// ftos: 5 +// dtos: 6 +// atos: 7 +// vtos: 8 +// +// Entry specific: field entries: +// _indices = get (b1 section) and put (b2 section) bytecodes, original constant pool index +// _f1 = field holder +// _f2 = field offset in words +// _flags = field type information, original field index in field holder +// (field_index section) +// +// Entry specific: method entries: +// _indices = invoke code for f1 (b1 section), invoke code for f2 (b2 section), +// original constant pool index +// _f1 = method for all but virtual calls, unused by virtual calls +// (note: for interface calls, which are essentially virtual, +// contains klassOop for the corresponding interface. +// _f2 = method/vtable index for virtual calls only, unused by all other +// calls. The vf flag indicates this is a method pointer not an +// index. +// _flags = field type info (f section), +// virtual final entry (vf), +// interpreter entry index (eidx section), +// parameter size (psze section) +// +// Note: invokevirtual & invokespecial bytecodes can share the same constant +// pool entry and thus the same constant pool cache entry. All invoke +// bytecodes but invokevirtual use only _f1 and the corresponding b1 +// bytecode, while invokevirtual uses only _f2 and the corresponding +// b2 bytecode. The value of _flags is shared for both types of entries. +// +// The fields are volatile so that they are stored in the order written in the +// source code. The _indices field with the bytecode must be written last. + +class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + volatile intx _indices; // constant pool index & rewrite bytecodes + volatile oop _f1; // entry specific oop field + volatile intx _f2; // entry specific int/oop field + volatile intx _flags; // flags + + +#ifdef ASSERT + bool same_methodOop(oop cur_f1, oop f1); +#endif + + void set_bytecode_1(Bytecodes::Code code); + void set_bytecode_2(Bytecodes::Code code); + void set_f1(oop f1) { + oop existing_f1 = _f1; // read once + assert(existing_f1 == NULL || existing_f1 == f1, "illegal field change"); + oop_store(&_f1, f1); + } + void set_f2(intx f2) { assert(_f2 == 0 || _f2 == f2, "illegal field change"); _f2 = f2; } + int as_flags(TosState state, bool is_final, bool is_vfinal, bool is_volatile, + bool is_method_interface, bool is_method); + void set_flags(intx flags) { _flags = flags; } + + public: + // specific bit values in flag field + // Note: the interpreter knows this layout! + enum FlagBitValues { + hotSwapBit = 23, + methodInterface = 24, + volatileField = 25, + vfinalMethod = 26, + finalField = 27 + }; + + enum { field_index_mask = 0xFFFF }; + + // start of type bits in flags + // Note: the interpreter knows this layout! + enum FlagValues { + tosBits = 28 + }; + + // Initialization + void set_initial_state(int index); // sets entry to initial state + + void set_field( // sets entry to resolved field state + Bytecodes::Code get_code, // the bytecode used for reading the field + Bytecodes::Code put_code, // the bytecode used for writing the field + KlassHandle field_holder, // the object/klass holding the field + int orig_field_index, // the original field index in the field holder + int field_offset, // the field offset in words in the field holder + TosState field_type, // the (machine) field type + bool is_final, // the field is final + bool is_volatile // the field is volatile + ); + + void set_method( // sets entry to resolved method entry + Bytecodes::Code invoke_code, // the bytecode used for invoking the method + methodHandle method, // the method/prototype if any (NULL, otherwise) + int vtable_index // the vtable index if any, else negative + ); + + void set_interface_call( + methodHandle method, // Resolved method + int index // Method index into interface + ); + + void set_parameter_size(int value) { + assert(parameter_size() == 0 || parameter_size() == value, + "size must not change"); + // Setting the parameter size by itself is only safe if the + // current value of _flags is 0, otherwise another thread may have + // updated it and we don't want to overwrite that value. Don't + // bother trying to update it once it's nonzero but always make + // sure that the final parameter size agrees with what was passed. + if (_flags == 0) { + Atomic::cmpxchg_ptr((value & 0xFF), &_flags, 0); + } + guarantee(parameter_size() == value, "size must not change"); + } + + // Which bytecode number (1 or 2) in the index field is valid for this bytecode? + // Returns -1 if neither is valid. + static int bytecode_number(Bytecodes::Code code) { + switch (code) { + case Bytecodes::_getstatic : // fall through + case Bytecodes::_getfield : // fall through + case Bytecodes::_invokespecial : // fall through + case Bytecodes::_invokestatic : // fall through + case Bytecodes::_invokeinterface : return 1; + case Bytecodes::_putstatic : // fall through + case Bytecodes::_putfield : // fall through + case Bytecodes::_invokevirtual : return 2; + default : break; + } + return -1; + } + + // Has this bytecode been resolved? Only valid for invokes and get/put field/static. + bool is_resolved(Bytecodes::Code code) const { + switch (bytecode_number(code)) { + case 1: return (bytecode_1() == code); + case 2: return (bytecode_2() == code); + } + return false; // default: not resolved + } + + // Accessors + int constant_pool_index() const { return _indices & 0xFFFF; } + Bytecodes::Code bytecode_1() const { return Bytecodes::cast((_indices >> 16) & 0xFF); } + Bytecodes::Code bytecode_2() const { return Bytecodes::cast((_indices >> 24) & 0xFF); } + volatile oop f1() const { return _f1; } + intx f2() const { return _f2; } + int field_index() const; + int parameter_size() const { return _flags & 0xFF; } + bool is_vfinal() const { return ((_flags & (1 << vfinalMethod)) == (1 << vfinalMethod)); } + bool is_volatile() const { return ((_flags & (1 << volatileField)) == (1 << volatileField)); } + bool is_methodInterface() const { return ((_flags & (1 << methodInterface)) == (1 << methodInterface)); } + bool is_byte() const { return (((uintx) _flags >> tosBits) == btos); } + bool is_char() const { return (((uintx) _flags >> tosBits) == ctos); } + bool is_short() const { return (((uintx) _flags >> tosBits) == stos); } + bool is_int() const { return (((uintx) _flags >> tosBits) == itos); } + bool is_long() const { return (((uintx) _flags >> tosBits) == ltos); } + bool is_float() const { return (((uintx) _flags >> tosBits) == ftos); } + bool is_double() const { return (((uintx) _flags >> tosBits) == dtos); } + bool is_object() const { return (((uintx) _flags >> tosBits) == atos); } + TosState flag_state() const { assert( ( (_flags >> tosBits) & 0x0F ) < number_of_states, "Invalid state in as_flags"); + return (TosState)((_flags >> tosBits) & 0x0F); } + + // Code generation support + static WordSize size() { return in_WordSize(sizeof(ConstantPoolCacheEntry) / HeapWordSize); } + static ByteSize indices_offset() { return byte_offset_of(ConstantPoolCacheEntry, _indices); } + static ByteSize f1_offset() { return byte_offset_of(ConstantPoolCacheEntry, _f1); } + static ByteSize f2_offset() { return byte_offset_of(ConstantPoolCacheEntry, _f2); } + static ByteSize flags_offset() { return byte_offset_of(ConstantPoolCacheEntry, _flags); } + + // GC Support + void oops_do(void f(oop*)); + void oop_iterate(OopClosure* blk); + void oop_iterate_m(OopClosure* blk, MemRegion mr); + void follow_contents(); + void adjust_pointers(); + +#ifndef SERIALGC + // Parallel Old + void follow_contents(ParCompactionManager* cm); +#endif // SERIALGC + + void update_pointers(); + void update_pointers(HeapWord* beg_addr, HeapWord* end_addr); + + // RedefineClasses() API support: + // If this constantPoolCacheEntry refers to old_method then update it + // to refer to new_method. + // trace_name_printed is set to true if the current call has + // printed the klass name so that other routines in the adjust_* + // group don't print the klass name. + bool adjust_method_entry(methodOop old_method, methodOop new_method, + bool * trace_name_printed); + bool is_interesting_method_entry(klassOop k); + bool is_field_entry() const { return (_flags & (1 << hotSwapBit)) == 0; } + bool is_method_entry() const { return (_flags & (1 << hotSwapBit)) != 0; } + + // Debugging & Printing + void print (outputStream* st, int index) const; + void verify(outputStream* st) const; + + static void verify_tosBits() { + assert(tosBits == 28, "interpreter now assumes tosBits is 28"); + } +}; + + +// A constant pool cache is a runtime data structure set aside to a constant pool. The cache +// holds interpreter runtime information for all field access and invoke bytecodes. The cache +// is created and initialized before a class is actively used (i.e., initialized), the indivi- +// dual cache entries are filled at resolution (i.e., "link") time (see also: rewriter.*). + +class constantPoolCacheOopDesc: public arrayOopDesc { + friend class VMStructs; + private: + constantPoolOop _constant_pool; // the corresponding constant pool + + // Sizing + static int header_size() { return sizeof(constantPoolCacheOopDesc) / HeapWordSize; } + static int object_size(int length) { return align_object_size(header_size() + length * in_words(ConstantPoolCacheEntry::size())); } + int object_size() { return object_size(length()); } + + // Helpers + constantPoolOop* constant_pool_addr() { return &_constant_pool; } + ConstantPoolCacheEntry* base() const { return (ConstantPoolCacheEntry*)((address)this + in_bytes(base_offset())); } + + friend class constantPoolCacheKlass; + + public: + // Initialization + void initialize(intArray& inverse_index_map); + + // Accessors + void set_constant_pool(constantPoolOop pool) { oop_store_without_check((oop*)&_constant_pool, (oop)pool); } + constantPoolOop constant_pool() const { return _constant_pool; } + ConstantPoolCacheEntry* entry_at(int i) const { assert(0 <= i && i < length(), "index out of bounds"); return base() + i; } + + // Code generation + static ByteSize base_offset() { return in_ByteSize(sizeof(constantPoolCacheOopDesc)); } + + // RedefineClasses() API support: + // If any entry of this constantPoolCache points to any of + // old_methods, replace it with the corresponding new_method. + // trace_name_printed is set to true if the current call has + // printed the klass name so that other routines in the adjust_* + // group don't print the klass name. + void adjust_method_entries(methodOop* old_methods, methodOop* new_methods, + int methods_length, bool * trace_name_printed); +}; diff --git a/hotspot/src/share/vm/oops/generateOopMap.cpp b/hotspot/src/share/vm/oops/generateOopMap.cpp new file mode 100644 index 00000000000..bff8ccbecf4 --- /dev/null +++ b/hotspot/src/share/vm/oops/generateOopMap.cpp @@ -0,0 +1,2536 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// +// Compute stack layouts for each instruction in method. +// +// Problems: +// - What to do about jsr with different types of local vars? +// Need maps that are conditional on jsr path? +// - Jsr and exceptions should be done more efficiently (the retAddr stuff) +// +// Alternative: +// - Could extend verifier to provide this information. +// For: one fewer abstract interpreter to maintain. Against: the verifier +// solves a bigger problem so slower (undesirable to force verification of +// everything?). +// +// Algorithm: +// Partition bytecodes into basic blocks +// For each basic block: store entry state (vars, stack). For instructions +// inside basic blocks we do not store any state (instead we recompute it +// from state produced by previous instruction). +// +// Perform abstract interpretation of bytecodes over this lattice: +// +// _--'#'--_ +// / / \ \ +// / / \ \ +// / | | \ +// 'r' 'v' 'p' ' ' +// \ | | / +// \ \ / / +// \ \ / / +// -- '@' -- +// +// '#' top, result of conflict merge +// 'r' reference type +// 'v' value type +// 'p' pc type for jsr/ret +// ' ' uninitialized; never occurs on operand stack in Java +// '@' bottom/unexecuted; initial state each bytecode. +// +// Basic block headers are the only merge points. We use this iteration to +// compute the information: +// +// find basic blocks; +// initialize them with uninitialized state; +// initialize first BB according to method signature; +// mark first BB changed +// while (some BB is changed) do { +// perform abstract interpration of all bytecodes in BB; +// merge exit state of BB into entry state of all successor BBs, +// noting if any of these change; +// } +// +// One additional complication is necessary. The jsr instruction pushes +// a return PC on the stack (a 'p' type in the abstract interpretation). +// To be able to process "ret" bytecodes, we keep track of these return +// PC's in a 'retAddrs' structure in abstract interpreter context (when +// processing a "ret" bytecodes, it is not sufficient to know that it gets +// an argument of the right type 'p'; we need to know which address it +// returns to). +// +// (Note this comment is borrowed form the original author of the algorithm) + +#include "incls/_precompiled.incl" +#include "incls/_generateOopMap.cpp.incl" + +// ComputeCallStack +// +// Specialization of SignatureIterator - compute the effects of a call +// +class ComputeCallStack : public SignatureIterator { + CellTypeState *_effect; + int _idx; + + void setup(); + void set(CellTypeState state) { _effect[_idx++] = state; } + int length() { return _idx; }; + + virtual void do_bool () { set(CellTypeState::value); }; + virtual void do_char () { set(CellTypeState::value); }; + virtual void do_float () { set(CellTypeState::value); }; + virtual void do_byte () { set(CellTypeState::value); }; + virtual void do_short () { set(CellTypeState::value); }; + virtual void do_int () { set(CellTypeState::value); }; + virtual void do_void () { set(CellTypeState::bottom);}; + virtual void do_object(int begin, int end) { set(CellTypeState::ref); }; + virtual void do_array (int begin, int end) { set(CellTypeState::ref); }; + + void do_double() { set(CellTypeState::value); + set(CellTypeState::value); } + void do_long () { set(CellTypeState::value); + set(CellTypeState::value); } + +public: + ComputeCallStack(symbolOop signature) : SignatureIterator(signature) {}; + + // Compute methods + int compute_for_parameters(bool is_static, CellTypeState *effect) { + _idx = 0; + _effect = effect; + + if (!is_static) + effect[_idx++] = CellTypeState::ref; + + iterate_parameters(); + + return length(); + }; + + int compute_for_returntype(CellTypeState *effect) { + _idx = 0; + _effect = effect; + iterate_returntype(); + set(CellTypeState::bottom); // Always terminate with a bottom state, so ppush works + + return length(); + } +}; + +//========================================================================================= +// ComputeEntryStack +// +// Specialization of SignatureIterator - in order to set up first stack frame +// +class ComputeEntryStack : public SignatureIterator { + CellTypeState *_effect; + int _idx; + + void setup(); + void set(CellTypeState state) { _effect[_idx++] = state; } + int length() { return _idx; }; + + virtual void do_bool () { set(CellTypeState::value); }; + virtual void do_char () { set(CellTypeState::value); }; + virtual void do_float () { set(CellTypeState::value); }; + virtual void do_byte () { set(CellTypeState::value); }; + virtual void do_short () { set(CellTypeState::value); }; + virtual void do_int () { set(CellTypeState::value); }; + virtual void do_void () { set(CellTypeState::bottom);}; + virtual void do_object(int begin, int end) { set(CellTypeState::make_slot_ref(_idx)); } + virtual void do_array (int begin, int end) { set(CellTypeState::make_slot_ref(_idx)); } + + void do_double() { set(CellTypeState::value); + set(CellTypeState::value); } + void do_long () { set(CellTypeState::value); + set(CellTypeState::value); } + +public: + ComputeEntryStack(symbolOop signature) : SignatureIterator(signature) {}; + + // Compute methods + int compute_for_parameters(bool is_static, CellTypeState *effect) { + _idx = 0; + _effect = effect; + + if (!is_static) + effect[_idx++] = CellTypeState::make_slot_ref(0); + + iterate_parameters(); + + return length(); + }; + + int compute_for_returntype(CellTypeState *effect) { + _idx = 0; + _effect = effect; + iterate_returntype(); + set(CellTypeState::bottom); // Always terminate with a bottom state, so ppush works + + return length(); + } +}; + +//===================================================================================== +// +// Implementation of RetTable/RetTableEntry +// +// Contains function to itereate through all bytecodes +// and find all return entry points +// +int RetTable::_init_nof_entries = 10; +int RetTableEntry::_init_nof_jsrs = 5; + +void RetTableEntry::add_delta(int bci, int delta) { + if (_target_bci > bci) _target_bci += delta; + + for (int k = 0; k < _jsrs->length(); k++) { + int jsr = _jsrs->at(k); + if (jsr > bci) _jsrs->at_put(k, jsr+delta); + } +} + +void RetTable::compute_ret_table(methodHandle method) { + BytecodeStream i(method); + Bytecodes::Code bytecode; + + while( (bytecode = i.next()) >= 0) { + switch (bytecode) { + case Bytecodes::_jsr: + add_jsr(i.next_bci(), i.dest()); + break; + case Bytecodes::_jsr_w: + add_jsr(i.next_bci(), i.dest_w()); + break; + } + } +} + +void RetTable::add_jsr(int return_bci, int target_bci) { + RetTableEntry* entry = _first; + + // Scan table for entry + for (;entry && entry->target_bci() != target_bci; entry = entry->next()); + + if (!entry) { + // Allocate new entry and put in list + entry = new RetTableEntry(target_bci, _first); + _first = entry; + } + + // Now "entry" is set. Make sure that the entry is initialized + // and has room for the new jsr. + entry->add_jsr(return_bci); +} + +RetTableEntry* RetTable::find_jsrs_for_target(int targBci) { + RetTableEntry *cur = _first; + + while(cur) { + assert(cur->target_bci() != -1, "sanity check"); + if (cur->target_bci() == targBci) return cur; + cur = cur->next(); + } + ShouldNotReachHere(); + return NULL; +} + +// The instruction at bci is changing size by "delta". Update the return map. +void RetTable::update_ret_table(int bci, int delta) { + RetTableEntry *cur = _first; + while(cur) { + cur->add_delta(bci, delta); + cur = cur->next(); + } +} + +// +// Celltype state +// + +CellTypeState CellTypeState::bottom = CellTypeState::make_bottom(); +CellTypeState CellTypeState::uninit = CellTypeState::make_any(uninit_value); +CellTypeState CellTypeState::ref = CellTypeState::make_any(ref_conflict); +CellTypeState CellTypeState::value = CellTypeState::make_any(val_value); +CellTypeState CellTypeState::refUninit = CellTypeState::make_any(ref_conflict | uninit_value); +CellTypeState CellTypeState::top = CellTypeState::make_top(); +CellTypeState CellTypeState::addr = CellTypeState::make_any(addr_conflict); + +// Commonly used constants +static CellTypeState epsilonCTS[1] = { CellTypeState::bottom }; +static CellTypeState refCTS = CellTypeState::ref; +static CellTypeState valCTS = CellTypeState::value; +static CellTypeState vCTS[2] = { CellTypeState::value, CellTypeState::bottom }; +static CellTypeState rCTS[2] = { CellTypeState::ref, CellTypeState::bottom }; +static CellTypeState rrCTS[3] = { CellTypeState::ref, CellTypeState::ref, CellTypeState::bottom }; +static CellTypeState vrCTS[3] = { CellTypeState::value, CellTypeState::ref, CellTypeState::bottom }; +static CellTypeState vvCTS[3] = { CellTypeState::value, CellTypeState::value, CellTypeState::bottom }; +static CellTypeState rvrCTS[4] = { CellTypeState::ref, CellTypeState::value, CellTypeState::ref, CellTypeState::bottom }; +static CellTypeState vvrCTS[4] = { CellTypeState::value, CellTypeState::value, CellTypeState::ref, CellTypeState::bottom }; +static CellTypeState vvvCTS[4] = { CellTypeState::value, CellTypeState::value, CellTypeState::value, CellTypeState::bottom }; +static CellTypeState vvvrCTS[5] = { CellTypeState::value, CellTypeState::value, CellTypeState::value, CellTypeState::ref, CellTypeState::bottom }; +static CellTypeState vvvvCTS[5] = { CellTypeState::value, CellTypeState::value, CellTypeState::value, CellTypeState::value, CellTypeState::bottom }; + +char CellTypeState::to_char() const { + if (can_be_reference()) { + if (can_be_value() || can_be_address()) + return '#'; // Conflict that needs to be rewritten + else + return 'r'; + } else if (can_be_value()) + return 'v'; + else if (can_be_address()) + return 'p'; + else if (can_be_uninit()) + return ' '; + else + return '@'; +} + + +// Print a detailed CellTypeState. Indicate all bits that are set. If +// the CellTypeState represents an address or a reference, print the +// value of the additional information. +void CellTypeState::print(outputStream *os) { + if (can_be_address()) { + os->print("(p"); + } else { + os->print("( "); + } + if (can_be_reference()) { + os->print("r"); + } else { + os->print(" "); + } + if (can_be_value()) { + os->print("v"); + } else { + os->print(" "); + } + if (can_be_uninit()) { + os->print("u|"); + } else { + os->print(" |"); + } + if (is_info_top()) { + os->print("Top)"); + } else if (is_info_bottom()) { + os->print("Bot)"); + } else { + if (is_reference()) { + int info = get_info(); + int data = info & ~(ref_not_lock_bit | ref_slot_bit); + if (info & ref_not_lock_bit) { + // Not a monitor lock reference. + if (info & ref_slot_bit) { + // slot + os->print("slot%d)", data); + } else { + // line + os->print("line%d)", data); + } + } else { + // lock + os->print("lock%d)", data); + } + } else { + os->print("%d)", get_info()); + } + } +} + +// +// Basicblock handling methods +// + +void GenerateOopMap ::initialize_bb() { + _gc_points = 0; + _bb_count = 0; + int size = binsToHold(method()->code_size()); + _bb_hdr_bits = NEW_RESOURCE_ARRAY(uintptr_t,size); + memset(_bb_hdr_bits, 0, size*sizeof(uintptr_t)); +} + +void GenerateOopMap ::set_bbmark_bit(int bci) { + int idx = bci >> LogBitsPerWord; + uintptr_t bit = (uintptr_t)1 << (bci & (BitsPerWord-1)); + _bb_hdr_bits[idx] |= bit; +} + +void GenerateOopMap ::clear_bbmark_bit(int bci) { + int idx = bci >> LogBitsPerWord; + uintptr_t bit = (uintptr_t)1 << (bci & (BitsPerWord-1)); + _bb_hdr_bits[idx] &= (~bit); +} + +void GenerateOopMap::bb_mark_fct(GenerateOopMap *c, int bci, int *data) { + assert(bci>= 0 && bci < c->method()->code_size(), "index out of bounds"); + if (c->is_bb_header(bci)) + return; + + if (TraceNewOopMapGeneration) { + tty->print_cr("Basicblock#%d begins at: %d", c->_bb_count, bci); + } + c->set_bbmark_bit(bci); + c->_bb_count++; +} + + +void GenerateOopMap::mark_bbheaders_and_count_gc_points() { + initialize_bb(); + + bool fellThrough = false; // False to get first BB marked. + + // First mark all exception handlers as start of a basic-block + typeArrayOop excps = method()->exception_table(); + for(int i = 0; i < excps->length(); i += 4) { + int handler_pc_idx = i+2; + bb_mark_fct(this, excps->int_at(handler_pc_idx), NULL); + } + + // Then iterate through the code + BytecodeStream bcs(_method); + Bytecodes::Code bytecode; + + while( (bytecode = bcs.next()) >= 0) { + int bci = bcs.bci(); + + if (!fellThrough) + bb_mark_fct(this, bci, NULL); + + fellThrough = jump_targets_do(&bcs, &GenerateOopMap::bb_mark_fct, NULL); + + /* We will also mark successors of jsr's as basic block headers. */ + switch (bytecode) { + case Bytecodes::_jsr: + assert(!fellThrough, "should not happen"); + bb_mark_fct(this, bci + Bytecodes::length_for(bytecode), NULL); + break; + case Bytecodes::_jsr_w: + assert(!fellThrough, "should not happen"); + bb_mark_fct(this, bci + Bytecodes::length_for(bytecode), NULL); + break; + } + + if (possible_gc_point(&bcs)) + _gc_points++; + } +} + +void GenerateOopMap::reachable_basicblock(GenerateOopMap *c, int bci, int *data) { + assert(bci>= 0 && bci < c->method()->code_size(), "index out of bounds"); + BasicBlock* bb = c->get_basic_block_at(bci); + if (bb->is_dead()) { + bb->mark_as_alive(); + *data = 1; // Mark basicblock as changed + } +} + + +void GenerateOopMap::mark_reachable_code() { + int change = 1; // int to get function pointers to work + + // Mark entry basic block as alive and all exception handlers + _basic_blocks[0].mark_as_alive(); + typeArrayOop excps = method()->exception_table(); + for(int i = 0; i < excps->length(); i += 4) { + int handler_pc_idx = i+2; + BasicBlock *bb = get_basic_block_at(excps->int_at(handler_pc_idx)); + // If block is not already alive (due to multiple exception handlers to same bb), then + // make it alive + if (bb->is_dead()) bb->mark_as_alive(); + } + + BytecodeStream bcs(_method); + + // Iterate through all basic blocks until we reach a fixpoint + while (change) { + change = 0; + + for (int i = 0; i < _bb_count; i++) { + BasicBlock *bb = &_basic_blocks[i]; + if (bb->is_alive()) { + // Position bytecodestream at last bytecode in basicblock + bcs.set_start(bb->_end_bci); + bcs.next(); + Bytecodes::Code bytecode = bcs.code(); + int bci = bcs.bci(); + assert(bci == bb->_end_bci, "wrong bci"); + + bool fell_through = jump_targets_do(&bcs, &GenerateOopMap::reachable_basicblock, &change); + + // We will also mark successors of jsr's as alive. + switch (bytecode) { + case Bytecodes::_jsr: + case Bytecodes::_jsr_w: + assert(!fell_through, "should not happen"); + reachable_basicblock(this, bci + Bytecodes::length_for(bytecode), &change); + break; + } + if (fell_through) { + // Mark successor as alive + if (bb[1].is_dead()) { + bb[1].mark_as_alive(); + change = 1; + } + } + } + } + } +} + +/* If the current instruction in "c" has no effect on control flow, + returns "true". Otherwise, calls "jmpFct" one or more times, with + "c", an appropriate "pcDelta", and "data" as arguments, then + returns "false". There is one exception: if the current + instruction is a "ret", returns "false" without calling "jmpFct". + Arrangements for tracking the control flow of a "ret" must be made + externally. */ +bool GenerateOopMap::jump_targets_do(BytecodeStream *bcs, jmpFct_t jmpFct, int *data) { + int bci = bcs->bci(); + + switch (bcs->code()) { + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + (*jmpFct)(this, bcs->dest(), data); + (*jmpFct)(this, bci + 3, data); + break; + + case Bytecodes::_goto: + (*jmpFct)(this, bcs->dest(), data); + break; + case Bytecodes::_goto_w: + (*jmpFct)(this, bcs->dest_w(), data); + break; + case Bytecodes::_tableswitch: + { Bytecode_tableswitch *tableswitch = Bytecode_tableswitch_at(bcs->bcp()); + int len = tableswitch->length(); + + (*jmpFct)(this, bci + tableswitch->default_offset(), data); /* Default. jump address */ + while (--len >= 0) { + (*jmpFct)(this, bci + tableswitch->dest_offset_at(len), data); + } + break; + } + + case Bytecodes::_lookupswitch: + { Bytecode_lookupswitch *lookupswitch = Bytecode_lookupswitch_at(bcs->bcp()); + int npairs = lookupswitch->number_of_pairs(); + (*jmpFct)(this, bci + lookupswitch->default_offset(), data); /* Default. */ + while(--npairs >= 0) { + LookupswitchPair *pair = lookupswitch->pair_at(npairs); + (*jmpFct)(this, bci + pair->offset(), data); + } + break; + } + case Bytecodes::_jsr: + assert(bcs->is_wide()==false, "sanity check"); + (*jmpFct)(this, bcs->dest(), data); + + + + break; + case Bytecodes::_jsr_w: + (*jmpFct)(this, bcs->dest_w(), data); + break; + case Bytecodes::_wide: + ShouldNotReachHere(); + return true; + break; + case Bytecodes::_athrow: + case Bytecodes::_ireturn: + case Bytecodes::_lreturn: + case Bytecodes::_freturn: + case Bytecodes::_dreturn: + case Bytecodes::_areturn: + case Bytecodes::_return: + case Bytecodes::_ret: + break; + default: + return true; + } + return false; +} + +/* Requires "pc" to be the head of a basic block; returns that basic + block. */ +BasicBlock *GenerateOopMap::get_basic_block_at(int bci) const { + BasicBlock* bb = get_basic_block_containing(bci); + assert(bb->_bci == bci, "should have found BB"); + return bb; +} + +// Requires "pc" to be the start of an instruction; returns the basic +// block containing that instruction. */ +BasicBlock *GenerateOopMap::get_basic_block_containing(int bci) const { + BasicBlock *bbs = _basic_blocks; + int lo = 0, hi = _bb_count - 1; + + while (lo <= hi) { + int m = (lo + hi) / 2; + int mbci = bbs[m]._bci; + int nbci; + + if ( m == _bb_count-1) { + assert( bci >= mbci && bci < method()->code_size(), "sanity check failed"); + return bbs+m; + } else { + nbci = bbs[m+1]._bci; + } + + if ( mbci <= bci && bci < nbci) { + return bbs+m; + } else if (mbci < bci) { + lo = m + 1; + } else { + assert(mbci > bci, "sanity check"); + hi = m - 1; + } + } + + fatal("should have found BB"); + return NULL; +} + +void GenerateOopMap::restore_state(BasicBlock *bb) +{ + memcpy(_state, bb->_state, _state_len*sizeof(CellTypeState)); + _stack_top = bb->_stack_top; + _monitor_top = bb->_monitor_top; +} + +int GenerateOopMap::next_bb_start_pc(BasicBlock *bb) { + int bbNum = bb - _basic_blocks + 1; + if (bbNum == _bb_count) + return method()->code_size(); + + return _basic_blocks[bbNum]._bci; +} + +// +// CellType handling methods +// + +void GenerateOopMap::init_state() { + _state_len = _max_locals + _max_stack + _max_monitors; + _state = NEW_RESOURCE_ARRAY(CellTypeState, _state_len); + memset(_state, 0, _state_len * sizeof(CellTypeState)); + _state_vec_buf = NEW_RESOURCE_ARRAY(char, MAX3(_max_locals, _max_stack, _max_monitors) + 1/*for null terminator char */); +} + +void GenerateOopMap::make_context_uninitialized() { + CellTypeState* vs = vars(); + + for (int i = 0; i < _max_locals; i++) + vs[i] = CellTypeState::uninit; + + _stack_top = 0; + _monitor_top = 0; +} + +int GenerateOopMap::methodsig_to_effect(symbolOop signature, bool is_static, CellTypeState* effect) { + ComputeEntryStack ces(signature); + return ces.compute_for_parameters(is_static, effect); +} + +// Return result of merging cts1 and cts2. +CellTypeState CellTypeState::merge(CellTypeState cts, int slot) const { + CellTypeState result; + + assert(!is_bottom() && !cts.is_bottom(), + "merge of bottom values is handled elsewhere"); + + result._state = _state | cts._state; + + // If the top bit is set, we don't need to do any more work. + if (!result.is_info_top()) { + assert((result.can_be_address() || result.can_be_reference()), + "only addresses and references have non-top info"); + + if (!equal(cts)) { + // The two values being merged are different. Raise to top. + if (result.is_reference()) { + result = CellTypeState::make_slot_ref(slot); + } else { + result._state |= info_conflict; + } + } + } + assert(result.is_valid_state(), "checking that CTS merge maintains legal state"); + + return result; +} + +// Merge the variable state for locals and stack from cts into bbts. +bool GenerateOopMap::merge_local_state_vectors(CellTypeState* cts, + CellTypeState* bbts) { + int i; + int len = _max_locals + _stack_top; + bool change = false; + + for (i = len - 1; i >= 0; i--) { + CellTypeState v = cts[i].merge(bbts[i], i); + change = change || !v.equal(bbts[i]); + bbts[i] = v; + } + + return change; +} + +// Merge the monitor stack state from cts into bbts. +bool GenerateOopMap::merge_monitor_state_vectors(CellTypeState* cts, + CellTypeState* bbts) { + bool change = false; + if (_max_monitors > 0 && _monitor_top != bad_monitors) { + // If there are no monitors in the program, or there has been + // a monitor matching error before this point in the program, + // then we do not merge in the monitor state. + + int base = _max_locals + _max_stack; + int len = base + _monitor_top; + for (int i = len - 1; i >= base; i--) { + CellTypeState v = cts[i].merge(bbts[i], i); + + // Can we prove that, when there has been a change, it will already + // have been detected at this point? That would make this equal + // check here unnecessary. + change = change || !v.equal(bbts[i]); + bbts[i] = v; + } + } + + return change; +} + +void GenerateOopMap::copy_state(CellTypeState *dst, CellTypeState *src) { + int len = _max_locals + _stack_top; + for (int i = 0; i < len; i++) { + if (src[i].is_nonlock_reference()) { + dst[i] = CellTypeState::make_slot_ref(i); + } else { + dst[i] = src[i]; + } + } + if (_max_monitors > 0 && _monitor_top != bad_monitors) { + int base = _max_locals + _max_stack; + len = base + _monitor_top; + for (int i = base; i < len; i++) { + dst[i] = src[i]; + } + } +} + + +// Merge the states for the current block and the next. As long as a +// block is reachable the locals and stack must be merged. If the +// stack heights don't match then this is a verification error and +// it's impossible to interpret the code. Simultaneously monitor +// states are being check to see if they nest statically. If monitor +// depths match up then their states are merged. Otherwise the +// mismatch is simply recorded and interpretation continues since +// monitor matching is purely informational and doesn't say anything +// about the correctness of the code. +void GenerateOopMap::merge_state_into_bb(BasicBlock *bb) { + assert(bb->is_alive(), "merging state into a dead basicblock"); + + if (_stack_top == bb->_stack_top) { + // always merge local state even if monitors don't match. + if (merge_local_state_vectors(_state, bb->_state)) { + bb->set_changed(true); + } + if (_monitor_top == bb->_monitor_top) { + // monitors still match so continue merging monitor states. + if (merge_monitor_state_vectors(_state, bb->_state)) { + bb->set_changed(true); + } + } else { + if (TraceMonitorMismatch) { + report_monitor_mismatch("monitor stack height merge conflict"); + } + // When the monitor stacks are not matched, we set _monitor_top to + // bad_monitors. This signals that, from here on, the monitor stack cannot + // be trusted. In particular, monitorexit bytecodes may throw + // exceptions. We mark this block as changed so that the change + // propagates properly. + bb->_monitor_top = bad_monitors; + bb->set_changed(true); + _monitor_safe = false; + } + } else if (!bb->is_reachable()) { + // First time we look at this BB + copy_state(bb->_state, _state); + bb->_stack_top = _stack_top; + bb->_monitor_top = _monitor_top; + bb->set_changed(true); + } else { + verify_error("stack height conflict: %d vs. %d", _stack_top, bb->_stack_top); + } +} + +void GenerateOopMap::merge_state(GenerateOopMap *gom, int bci, int* data) { + gom->merge_state_into_bb(gom->get_basic_block_at(bci)); +} + +void GenerateOopMap::set_var(int localNo, CellTypeState cts) { + assert(cts.is_reference() || cts.is_value() || cts.is_address(), + "wrong celltypestate"); + if (localNo < 0 || localNo > _max_locals) { + verify_error("variable write error: r%d", localNo); + return; + } + vars()[localNo] = cts; +} + +CellTypeState GenerateOopMap::get_var(int localNo) { + assert(localNo < _max_locals + _nof_refval_conflicts, "variable read error") + if (localNo < 0 || localNo > _max_locals) { + verify_error("variable read error: r%d", localNo); + return valCTS; // just to pick something; + } + return vars()[localNo]; +} + +CellTypeState GenerateOopMap::pop() { + if ( _stack_top <= 0) { + verify_error("stack underflow"); + return valCTS; // just to pick something + } + return stack()[--_stack_top]; +} + +void GenerateOopMap::push(CellTypeState cts) { + if ( _stack_top >= _max_stack) { + verify_error("stack overflow"); + return; + } + stack()[_stack_top++] = cts; +} + +CellTypeState GenerateOopMap::monitor_pop() { + assert(_monitor_top != bad_monitors, "monitor_pop called on error monitor stack"); + if (_monitor_top == 0) { + // We have detected a pop of an empty monitor stack. + _monitor_safe = false; + _monitor_top = bad_monitors; + + if (TraceMonitorMismatch) { + report_monitor_mismatch("monitor stack underflow"); + } + return CellTypeState::ref; // just to keep the analysis going. + } + return monitors()[--_monitor_top]; +} + +void GenerateOopMap::monitor_push(CellTypeState cts) { + assert(_monitor_top != bad_monitors, "monitor_push called on error monitor stack"); + if (_monitor_top >= _max_monitors) { + // Some monitorenter is being executed more than once. + // This means that the monitor stack cannot be simulated. + _monitor_safe = false; + _monitor_top = bad_monitors; + + if (TraceMonitorMismatch) { + report_monitor_mismatch("monitor stack overflow"); + } + return; + } + monitors()[_monitor_top++] = cts; +} + +// +// Interpretation handling methods +// + +void GenerateOopMap::do_interpretation() +{ + // "i" is just for debugging, so we can detect cases where this loop is + // iterated more than once. + int i = 0; + do { +#ifndef PRODUCT + if (TraceNewOopMapGeneration) { + tty->print("\n\nIteration #%d of do_interpretation loop, method:\n", i); + method()->print_name(tty); + tty->print("\n\n"); + } +#endif + _conflict = false; + _monitor_safe = true; + // init_state is now called from init_basic_blocks. The length of a + // state vector cannot be determined until we have made a pass through + // the bytecodes counting the possible monitor entries. + if (!_got_error) init_basic_blocks(); + if (!_got_error) setup_method_entry_state(); + if (!_got_error) interp_all(); + if (!_got_error) rewrite_refval_conflicts(); + i++; + } while (_conflict && !_got_error); +} + +void GenerateOopMap::init_basic_blocks() { + // Note: Could consider reserving only the needed space for each BB's state + // (entry stack may not be of maximal height for every basic block). + // But cumbersome since we don't know the stack heights yet. (Nor the + // monitor stack heights...) + + _basic_blocks = NEW_RESOURCE_ARRAY(BasicBlock, _bb_count); + + // Make a pass through the bytecodes. Count the number of monitorenters. + // This can be used an upper bound on the monitor stack depth in programs + // which obey stack discipline with their monitor usage. Initialize the + // known information about basic blocks. + BytecodeStream j(_method); + Bytecodes::Code bytecode; + + int bbNo = 0; + int monitor_count = 0; + int prev_bci = -1; + while( (bytecode = j.next()) >= 0) { + if (j.code() == Bytecodes::_monitorenter) { + monitor_count++; + } + + int bci = j.bci(); + if (is_bb_header(bci)) { + // Initialize the basicblock structure + BasicBlock *bb = _basic_blocks + bbNo; + bb->_bci = bci; + bb->_max_locals = _max_locals; + bb->_max_stack = _max_stack; + bb->set_changed(false); + bb->_stack_top = BasicBlock::_dead_basic_block; // Initialize all basicblocks are dead. + bb->_monitor_top = bad_monitors; + + if (bbNo > 0) { + _basic_blocks[bbNo - 1]._end_bci = prev_bci; + } + + bbNo++; + } + // Remember prevous bci. + prev_bci = bci; + } + // Set + _basic_blocks[bbNo-1]._end_bci = prev_bci; + + + _max_monitors = monitor_count; + + // Now that we have a bound on the depth of the monitor stack, we can + // initialize the CellTypeState-related information. + init_state(); + + // We allocate space for all state-vectors for all basicblocks in one huge chuck. + // Then in the next part of the code, we set a pointer in each _basic_block that + // points to each piece. + CellTypeState *basicBlockState = NEW_RESOURCE_ARRAY(CellTypeState, bbNo * _state_len); + memset(basicBlockState, 0, bbNo * _state_len * sizeof(CellTypeState)); + + // Make a pass over the basicblocks and assign their state vectors. + for (int blockNum=0; blockNum < bbNo; blockNum++) { + BasicBlock *bb = _basic_blocks + blockNum; + bb->_state = basicBlockState + blockNum * _state_len; + +#ifdef ASSERT + if (blockNum + 1 < bbNo) { + address bcp = _method->bcp_from(bb->_end_bci); + int bc_len = Bytecodes::java_length_at(bcp); + assert(bb->_end_bci + bc_len == bb[1]._bci, "unmatched bci info in basicblock"); + } +#endif + } +#ifdef ASSERT + { BasicBlock *bb = &_basic_blocks[bbNo-1]; + address bcp = _method->bcp_from(bb->_end_bci); + int bc_len = Bytecodes::java_length_at(bcp); + assert(bb->_end_bci + bc_len == _method->code_size(), "wrong end bci"); + } +#endif + + // Check that the correct number of basicblocks was found + if (bbNo !=_bb_count) { + if (bbNo < _bb_count) { + verify_error("jump into the middle of instruction?"); + return; + } else { + verify_error("extra basic blocks - should not happen?"); + return; + } + } + + // Mark all alive blocks + mark_reachable_code(); +} + +void GenerateOopMap::setup_method_entry_state() { + + // Initialize all locals to 'uninit' and set stack-height to 0 + make_context_uninitialized(); + + // Initialize CellState type of arguments + methodsig_to_effect(method()->signature(), method()->is_static(), vars()); + + // If some references must be pre-assigned to null, then set that up + initialize_vars(); + + // This is the start state + merge_state_into_bb(&_basic_blocks[0]); + + assert(_basic_blocks[0].changed(), "we are not getting off the ground"); +} + +// The instruction at bci is changing size by "delta". Update the basic blocks. +void GenerateOopMap::update_basic_blocks(int bci, int delta, + int new_method_size) { + assert(new_method_size >= method()->code_size() + delta, + "new method size is too small"); + int newWords = binsToHold(new_method_size); + + uintptr_t * new_bb_hdr_bits = NEW_RESOURCE_ARRAY(uintptr_t, newWords); + + BitMap bb_bits(new_bb_hdr_bits, new_method_size); + bb_bits.clear(); + + for(int k = 0; k < _bb_count; k++) { + if (_basic_blocks[k]._bci > bci) { + _basic_blocks[k]._bci += delta; + _basic_blocks[k]._end_bci += delta; + } + bb_bits.at_put(_basic_blocks[k]._bci, true); + } + _bb_hdr_bits = new_bb_hdr_bits ; +} + +// +// Initvars handling +// + +void GenerateOopMap::initialize_vars() { + for (int k = 0; k < _init_vars->length(); k++) + _state[_init_vars->at(k)] = CellTypeState::make_slot_ref(k); +} + +void GenerateOopMap::add_to_ref_init_set(int localNo) { + + if (TraceNewOopMapGeneration) + tty->print_cr("Added init vars: %d", localNo); + + // Is it already in the set? + if (_init_vars->contains(localNo) ) + return; + + _init_vars->append(localNo); +} + +// +// Interpreration code +// + +void GenerateOopMap::interp_all() { + bool change = true; + + while (change && !_got_error) { + change = false; + for (int i = 0; i < _bb_count && !_got_error; i++) { + BasicBlock *bb = &_basic_blocks[i]; + if (bb->changed()) { + if (_got_error) return; + change = true; + bb->set_changed(false); + interp_bb(bb); + } + } + } +} + +void GenerateOopMap::interp_bb(BasicBlock *bb) { + + // We do not want to do anything in case the basic-block has not been initialized. This + // will happen in the case where there is dead-code hang around in a method. + assert(bb->is_reachable(), "should be reachable or deadcode exist"); + restore_state(bb); + + BytecodeStream itr(_method); + + // Set iterator interval to be the current basicblock + int lim_bci = next_bb_start_pc(bb); + itr.set_interval(bb->_bci, lim_bci); + assert(lim_bci != bb->_bci, "must be at least one instruction in a basicblock"); + itr.next(); // read first instruction + + // Iterates through all bytecodes except the last in a basic block. + // We handle the last one special, since there is controlflow change. + while(itr.next_bci() < lim_bci && !_got_error) { + if (_has_exceptions || _monitor_top != 0) { + // We do not need to interpret the results of exceptional + // continuation from this instruction when the method has no + // exception handlers and the monitor stack is currently + // empty. + do_exception_edge(&itr); + } + interp1(&itr); + itr.next(); + } + + // Handle last instruction. + if (!_got_error) { + assert(itr.next_bci() == lim_bci, "must point to end"); + if (_has_exceptions || _monitor_top != 0) { + do_exception_edge(&itr); + } + interp1(&itr); + + bool fall_through = jump_targets_do(&itr, GenerateOopMap::merge_state, NULL); + if (_got_error) return; + + if (itr.code() == Bytecodes::_ret) { + assert(!fall_through, "cannot be set if ret instruction"); + // Automatically handles 'wide' ret indicies + ret_jump_targets_do(&itr, GenerateOopMap::merge_state, itr.get_index(), NULL); + } else if (fall_through) { + // Hit end of BB, but the instr. was a fall-through instruction, + // so perform transition as if the BB ended in a "jump". + if (lim_bci != bb[1]._bci) { + verify_error("bytecodes fell through last instruction"); + return; + } + merge_state_into_bb(bb + 1); + } + } +} + +void GenerateOopMap::do_exception_edge(BytecodeStream* itr) { + // Only check exception edge, if bytecode can trap + if (!Bytecodes::can_trap(itr->code())) return; + switch (itr->code()) { + case Bytecodes::_aload_0: + // These bytecodes can trap for rewriting. We need to assume that + // they do not throw exceptions to make the monitor analysis work. + return; + + case Bytecodes::_ireturn: + case Bytecodes::_lreturn: + case Bytecodes::_freturn: + case Bytecodes::_dreturn: + case Bytecodes::_areturn: + case Bytecodes::_return: + // If the monitor stack height is not zero when we leave the method, + // then we are either exiting with a non-empty stack or we have + // found monitor trouble earlier in our analysis. In either case, + // assume an exception could be taken here. + if (_monitor_top == 0) { + return; + } + break; + + case Bytecodes::_monitorexit: + // If the monitor stack height is bad_monitors, then we have detected a + // monitor matching problem earlier in the analysis. If the + // monitor stack height is 0, we are about to pop a monitor + // off of an empty stack. In either case, the bytecode + // could throw an exception. + if (_monitor_top != bad_monitors && _monitor_top != 0) { + return; + } + break; + } + + if (_has_exceptions) { + int bci = itr->bci(); + typeArrayOop exct = method()->exception_table(); + for(int i = 0; i< exct->length(); i+=4) { + int start_pc = exct->int_at(i); + int end_pc = exct->int_at(i+1); + int handler_pc = exct->int_at(i+2); + int catch_type = exct->int_at(i+3); + + if (start_pc <= bci && bci < end_pc) { + BasicBlock *excBB = get_basic_block_at(handler_pc); + CellTypeState *excStk = excBB->stack(); + CellTypeState *cOpStck = stack(); + CellTypeState cOpStck_0 = cOpStck[0]; + int cOpStackTop = _stack_top; + + // Exception stacks are always the same. + assert(method()->max_stack() > 0, "sanity check"); + + // We remembered the size and first element of "cOpStck" + // above; now we temporarily set them to the appropriate + // values for an exception handler. */ + cOpStck[0] = CellTypeState::make_slot_ref(_max_locals); + _stack_top = 1; + + merge_state_into_bb(excBB); + + // Now undo the temporary change. + cOpStck[0] = cOpStck_0; + _stack_top = cOpStackTop; + + // If this is a "catch all" handler, then we do not need to + // consider any additional handlers. + if (catch_type == 0) { + return; + } + } + } + } + + // It is possible that none of the exception handlers would have caught + // the exception. In this case, we will exit the method. We must + // ensure that the monitor stack is empty in this case. + if (_monitor_top == 0) { + return; + } + + // We pessimistically assume that this exception can escape the + // method. (It is possible that it will always be caught, but + // we don't care to analyse the types of the catch clauses.) + + // We don't set _monitor_top to bad_monitors because there are no successors + // to this exceptional exit. + + if (TraceMonitorMismatch && _monitor_safe) { + // We check _monitor_safe so that we only report the first mismatched + // exceptional exit. + report_monitor_mismatch("non-empty monitor stack at exceptional exit"); + } + _monitor_safe = false; + +} + +void GenerateOopMap::report_monitor_mismatch(const char *msg) { +#ifndef PRODUCT + tty->print(" Monitor mismatch in method "); + method()->print_short_name(tty); + tty->print_cr(": %s", msg); +#endif +} + +void GenerateOopMap::print_states(outputStream *os, + CellTypeState* vec, int num) { + for (int i = 0; i < num; i++) { + vec[i].print(tty); + } +} + +// Print the state values at the current bytecode. +void GenerateOopMap::print_current_state(outputStream *os, + BytecodeStream *currentBC, + bool detailed) { + + if (detailed) { + os->print(" %4d vars = ", currentBC->bci()); + print_states(os, vars(), _max_locals); + os->print(" %s", Bytecodes::name(currentBC->code())); + switch(currentBC->code()) { + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokeinterface: + int idx = currentBC->get_index_big(); + constantPoolOop cp = method()->constants(); + int nameAndTypeIdx = cp->name_and_type_ref_index_at(idx); + int signatureIdx = cp->signature_ref_index_at(nameAndTypeIdx); + symbolOop signature = cp->symbol_at(signatureIdx); + os->print("%s", signature->as_C_string()); + } + os->cr(); + os->print(" stack = "); + print_states(os, stack(), _stack_top); + os->cr(); + if (_monitor_top != bad_monitors) { + os->print(" monitors = "); + print_states(os, monitors(), _monitor_top); + } else { + os->print(" [bad monitor stack]"); + } + os->cr(); + } else { + os->print(" %4d vars = '%s' ", currentBC->bci(), state_vec_to_string(vars(), _max_locals)); + os->print(" stack = '%s' ", state_vec_to_string(stack(), _stack_top)); + if (_monitor_top != bad_monitors) { + os->print(" monitors = '%s' \t%s", state_vec_to_string(monitors(), _monitor_top), Bytecodes::name(currentBC->code())); + } else { + os->print(" [bad monitor stack]"); + } + switch(currentBC->code()) { + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokeinterface: + int idx = currentBC->get_index_big(); + constantPoolOop cp = method()->constants(); + int nameAndTypeIdx = cp->name_and_type_ref_index_at(idx); + int signatureIdx = cp->signature_ref_index_at(nameAndTypeIdx); + symbolOop signature = cp->symbol_at(signatureIdx); + os->print("%s", signature->as_C_string()); + } + os->cr(); + } +} + +// Sets the current state to be the state after executing the +// current instruction, starting in the current state. +void GenerateOopMap::interp1(BytecodeStream *itr) { + if (TraceNewOopMapGeneration) { + print_current_state(tty, itr, TraceNewOopMapGenerationDetailed); + } + + // Should we report the results? Result is reported *before* the instruction at the current bci is executed. + // However, not for calls. For calls we do not want to include the arguments, so we postpone the reporting until + // they have been popped (in method ppl). + if (_report_result == true) { + switch(itr->code()) { + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokeinterface: + _itr_send = itr; + _report_result_for_send = true; + break; + default: + fill_stackmap_for_opcodes(itr, vars(), stack(), _stack_top); + break; + } + } + + // abstract interpretation of current opcode + switch(itr->code()) { + case Bytecodes::_nop: break; + case Bytecodes::_goto: break; + case Bytecodes::_goto_w: break; + case Bytecodes::_iinc: break; + case Bytecodes::_return: do_return_monitor_check(); + break; + + case Bytecodes::_aconst_null: + case Bytecodes::_new: ppush1(CellTypeState::make_line_ref(itr->bci())); + break; + + case Bytecodes::_iconst_m1: + case Bytecodes::_iconst_0: + case Bytecodes::_iconst_1: + case Bytecodes::_iconst_2: + case Bytecodes::_iconst_3: + case Bytecodes::_iconst_4: + case Bytecodes::_iconst_5: + case Bytecodes::_fconst_0: + case Bytecodes::_fconst_1: + case Bytecodes::_fconst_2: + case Bytecodes::_bipush: + case Bytecodes::_sipush: ppush1(valCTS); break; + + case Bytecodes::_lconst_0: + case Bytecodes::_lconst_1: + case Bytecodes::_dconst_0: + case Bytecodes::_dconst_1: ppush(vvCTS); break; + + case Bytecodes::_ldc2_w: ppush(vvCTS); break; + + case Bytecodes::_ldc: do_ldc(itr->get_index(), itr->bci()); break; + case Bytecodes::_ldc_w: do_ldc(itr->get_index_big(), itr->bci());break; + + case Bytecodes::_iload: + case Bytecodes::_fload: ppload(vCTS, itr->get_index()); break; + + case Bytecodes::_lload: + case Bytecodes::_dload: ppload(vvCTS,itr->get_index()); break; + + case Bytecodes::_aload: ppload(rCTS, itr->get_index()); break; + + case Bytecodes::_iload_0: + case Bytecodes::_fload_0: ppload(vCTS, 0); break; + case Bytecodes::_iload_1: + case Bytecodes::_fload_1: ppload(vCTS, 1); break; + case Bytecodes::_iload_2: + case Bytecodes::_fload_2: ppload(vCTS, 2); break; + case Bytecodes::_iload_3: + case Bytecodes::_fload_3: ppload(vCTS, 3); break; + + case Bytecodes::_lload_0: + case Bytecodes::_dload_0: ppload(vvCTS, 0); break; + case Bytecodes::_lload_1: + case Bytecodes::_dload_1: ppload(vvCTS, 1); break; + case Bytecodes::_lload_2: + case Bytecodes::_dload_2: ppload(vvCTS, 2); break; + case Bytecodes::_lload_3: + case Bytecodes::_dload_3: ppload(vvCTS, 3); break; + + case Bytecodes::_aload_0: ppload(rCTS, 0); break; + case Bytecodes::_aload_1: ppload(rCTS, 1); break; + case Bytecodes::_aload_2: ppload(rCTS, 2); break; + case Bytecodes::_aload_3: ppload(rCTS, 3); break; + + case Bytecodes::_iaload: + case Bytecodes::_faload: + case Bytecodes::_baload: + case Bytecodes::_caload: + case Bytecodes::_saload: pp(vrCTS, vCTS); break; + + case Bytecodes::_laload: pp(vrCTS, vvCTS); break; + case Bytecodes::_daload: pp(vrCTS, vvCTS); break; + + case Bytecodes::_aaload: pp_new_ref(vrCTS, itr->bci()); break; + + case Bytecodes::_istore: + case Bytecodes::_fstore: ppstore(vCTS, itr->get_index()); break; + + case Bytecodes::_lstore: + case Bytecodes::_dstore: ppstore(vvCTS, itr->get_index()); break; + + case Bytecodes::_astore: do_astore(itr->get_index()); break; + + case Bytecodes::_istore_0: + case Bytecodes::_fstore_0: ppstore(vCTS, 0); break; + case Bytecodes::_istore_1: + case Bytecodes::_fstore_1: ppstore(vCTS, 1); break; + case Bytecodes::_istore_2: + case Bytecodes::_fstore_2: ppstore(vCTS, 2); break; + case Bytecodes::_istore_3: + case Bytecodes::_fstore_3: ppstore(vCTS, 3); break; + + case Bytecodes::_lstore_0: + case Bytecodes::_dstore_0: ppstore(vvCTS, 0); break; + case Bytecodes::_lstore_1: + case Bytecodes::_dstore_1: ppstore(vvCTS, 1); break; + case Bytecodes::_lstore_2: + case Bytecodes::_dstore_2: ppstore(vvCTS, 2); break; + case Bytecodes::_lstore_3: + case Bytecodes::_dstore_3: ppstore(vvCTS, 3); break; + + case Bytecodes::_astore_0: do_astore(0); break; + case Bytecodes::_astore_1: do_astore(1); break; + case Bytecodes::_astore_2: do_astore(2); break; + case Bytecodes::_astore_3: do_astore(3); break; + + case Bytecodes::_iastore: + case Bytecodes::_fastore: + case Bytecodes::_bastore: + case Bytecodes::_castore: + case Bytecodes::_sastore: ppop(vvrCTS); break; + case Bytecodes::_lastore: + case Bytecodes::_dastore: ppop(vvvrCTS); break; + case Bytecodes::_aastore: ppop(rvrCTS); break; + + case Bytecodes::_pop: ppop_any(1); break; + case Bytecodes::_pop2: ppop_any(2); break; + + case Bytecodes::_dup: ppdupswap(1, "11"); break; + case Bytecodes::_dup_x1: ppdupswap(2, "121"); break; + case Bytecodes::_dup_x2: ppdupswap(3, "1321"); break; + case Bytecodes::_dup2: ppdupswap(2, "2121"); break; + case Bytecodes::_dup2_x1: ppdupswap(3, "21321"); break; + case Bytecodes::_dup2_x2: ppdupswap(4, "214321"); break; + case Bytecodes::_swap: ppdupswap(2, "12"); break; + + case Bytecodes::_iadd: + case Bytecodes::_fadd: + case Bytecodes::_isub: + case Bytecodes::_fsub: + case Bytecodes::_imul: + case Bytecodes::_fmul: + case Bytecodes::_idiv: + case Bytecodes::_fdiv: + case Bytecodes::_irem: + case Bytecodes::_frem: + case Bytecodes::_ishl: + case Bytecodes::_ishr: + case Bytecodes::_iushr: + case Bytecodes::_iand: + case Bytecodes::_ior: + case Bytecodes::_ixor: + case Bytecodes::_l2f: + case Bytecodes::_l2i: + case Bytecodes::_d2f: + case Bytecodes::_d2i: + case Bytecodes::_fcmpl: + case Bytecodes::_fcmpg: pp(vvCTS, vCTS); break; + + case Bytecodes::_ladd: + case Bytecodes::_dadd: + case Bytecodes::_lsub: + case Bytecodes::_dsub: + case Bytecodes::_lmul: + case Bytecodes::_dmul: + case Bytecodes::_ldiv: + case Bytecodes::_ddiv: + case Bytecodes::_lrem: + case Bytecodes::_drem: + case Bytecodes::_land: + case Bytecodes::_lor: + case Bytecodes::_lxor: pp(vvvvCTS, vvCTS); break; + + case Bytecodes::_ineg: + case Bytecodes::_fneg: + case Bytecodes::_i2f: + case Bytecodes::_f2i: + case Bytecodes::_i2c: + case Bytecodes::_i2s: + case Bytecodes::_i2b: pp(vCTS, vCTS); break; + + case Bytecodes::_lneg: + case Bytecodes::_dneg: + case Bytecodes::_l2d: + case Bytecodes::_d2l: pp(vvCTS, vvCTS); break; + + case Bytecodes::_lshl: + case Bytecodes::_lshr: + case Bytecodes::_lushr: pp(vvvCTS, vvCTS); break; + + case Bytecodes::_i2l: + case Bytecodes::_i2d: + case Bytecodes::_f2l: + case Bytecodes::_f2d: pp(vCTS, vvCTS); break; + + case Bytecodes::_lcmp: pp(vvvvCTS, vCTS); break; + case Bytecodes::_dcmpl: + case Bytecodes::_dcmpg: pp(vvvvCTS, vCTS); break; + + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + case Bytecodes::_tableswitch: ppop1(valCTS); + break; + case Bytecodes::_ireturn: + case Bytecodes::_freturn: do_return_monitor_check(); + ppop1(valCTS); + break; + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: ppop(vvCTS); + break; + + case Bytecodes::_lreturn: do_return_monitor_check(); + ppop(vvCTS); + break; + + case Bytecodes::_dreturn: do_return_monitor_check(); + ppop(vvCTS); + break; + + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: ppop(rrCTS); break; + + case Bytecodes::_jsr: do_jsr(itr->dest()); break; + case Bytecodes::_jsr_w: do_jsr(itr->dest_w()); break; + + case Bytecodes::_getstatic: do_field(true, true, + itr->get_index_big(), + itr->bci()); break; + case Bytecodes::_putstatic: do_field(false, true, itr->get_index_big(), itr->bci()); break; + case Bytecodes::_getfield: do_field(true, false, itr->get_index_big(), itr->bci()); break; + case Bytecodes::_putfield: do_field(false, false, itr->get_index_big(), itr->bci()); break; + + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: do_method(false, false, itr->get_index_big(), itr->bci()); break; + case Bytecodes::_invokestatic: do_method(true, false, itr->get_index_big(), itr->bci()); break; + case Bytecodes::_invokeinterface: do_method(false, true, itr->get_index_big(), itr->bci()); break; + case Bytecodes::_newarray: + case Bytecodes::_anewarray: pp_new_ref(vCTS, itr->bci()); break; + case Bytecodes::_checkcast: do_checkcast(); break; + case Bytecodes::_arraylength: + case Bytecodes::_instanceof: pp(rCTS, vCTS); break; + case Bytecodes::_monitorenter: do_monitorenter(itr->bci()); break; + case Bytecodes::_monitorexit: do_monitorexit(itr->bci()); break; + + case Bytecodes::_athrow: // handled by do_exception_edge() BUT ... + // vlh(apple): do_exception_edge() does not get + // called if method has no exception handlers + if ((!_has_exceptions) && (_monitor_top > 0)) { + _monitor_safe = false; + } + break; + + case Bytecodes::_areturn: do_return_monitor_check(); + ppop1(refCTS); + break; + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: ppop1(refCTS); break; + case Bytecodes::_multianewarray: do_multianewarray(*(itr->bcp()+3), itr->bci()); break; + + case Bytecodes::_wide: fatal("Iterator should skip this bytecode"); break; + case Bytecodes::_ret: break; + + // Java opcodes + case Bytecodes::_lookupswitch: ppop1(valCTS); break; + + default: + tty->print("unexpected opcode: %d\n", itr->code()); + ShouldNotReachHere(); + break; + } +} + +void GenerateOopMap::check_type(CellTypeState expected, CellTypeState actual) { + if (!expected.equal_kind(actual)) { + verify_error("wrong type on stack (found: %c expected: %c)", actual.to_char(), expected.to_char()); + } +} + +void GenerateOopMap::ppstore(CellTypeState *in, int loc_no) { + while(!(*in).is_bottom()) { + CellTypeState expected =*in++; + CellTypeState actual = pop(); + check_type(expected, actual); + assert(loc_no >= 0, "sanity check"); + set_var(loc_no++, actual); + } +} + +void GenerateOopMap::ppload(CellTypeState *out, int loc_no) { + while(!(*out).is_bottom()) { + CellTypeState out1 = *out++; + CellTypeState vcts = get_var(loc_no); + assert(out1.can_be_reference() || out1.can_be_value(), + "can only load refs. and values."); + if (out1.is_reference()) { + assert(loc_no>=0, "sanity check"); + if (!vcts.is_reference()) { + // We were asked to push a reference, but the type of the + // variable can be something else + _conflict = true; + if (vcts.can_be_uninit()) { + // It is a ref-uninit conflict (at least). If there are other + // problems, we'll get them in the next round + add_to_ref_init_set(loc_no); + vcts = out1; + } else { + // It wasn't a ref-uninit conflict. So must be a + // ref-val or ref-pc conflict. Split the variable. + record_refval_conflict(loc_no); + vcts = out1; + } + push(out1); // recover... + } else { + push(vcts); // preserve reference. + } + // Otherwise it is a conflict, but one that verification would + // have caught if illegal. In particular, it can't be a topCTS + // resulting from mergeing two difference pcCTS's since the verifier + // would have rejected any use of such a merge. + } else { + push(out1); // handle val/init conflict + } + loc_no++; + } +} + +void GenerateOopMap::ppdupswap(int poplen, const char *out) { + CellTypeState actual[5]; + assert(poplen < 5, "this must be less than length of actual vector"); + + // pop all arguments + for(int i = 0; i < poplen; i++) actual[i] = pop(); + + // put them back + char push_ch = *out++; + while (push_ch != '\0') { + int idx = push_ch - '1'; + assert(idx >= 0 && idx < poplen, "wrong arguments"); + push(actual[idx]); + push_ch = *out++; + } +} + +void GenerateOopMap::ppop1(CellTypeState out) { + CellTypeState actual = pop(); + check_type(out, actual); +} + +void GenerateOopMap::ppop(CellTypeState *out) { + while (!(*out).is_bottom()) { + ppop1(*out++); + } +} + +void GenerateOopMap::ppush1(CellTypeState in) { + assert(in.is_reference() | in.is_value(), "sanity check"); + push(in); +} + +void GenerateOopMap::ppush(CellTypeState *in) { + while (!(*in).is_bottom()) { + ppush1(*in++); + } +} + +void GenerateOopMap::pp(CellTypeState *in, CellTypeState *out) { + ppop(in); + ppush(out); +} + +void GenerateOopMap::pp_new_ref(CellTypeState *in, int bci) { + ppop(in); + ppush1(CellTypeState::make_line_ref(bci)); +} + +void GenerateOopMap::ppop_any(int poplen) { + if (_stack_top >= poplen) { + _stack_top -= poplen; + } else { + verify_error("stack underflow"); + } +} + +// Replace all occurences of the state 'match' with the state 'replace' +// in our current state vector. +void GenerateOopMap::replace_all_CTS_matches(CellTypeState match, + CellTypeState replace) { + int i; + int len = _max_locals + _stack_top; + bool change = false; + + for (i = len - 1; i >= 0; i--) { + if (match.equal(_state[i])) { + _state[i] = replace; + } + } + + if (_monitor_top > 0) { + int base = _max_locals + _max_stack; + len = base + _monitor_top; + for (i = len - 1; i >= base; i--) { + if (match.equal(_state[i])) { + _state[i] = replace; + } + } + } +} + +void GenerateOopMap::do_checkcast() { + CellTypeState actual = pop(); + check_type(refCTS, actual); + push(actual); +} + +void GenerateOopMap::do_monitorenter(int bci) { + CellTypeState actual = pop(); + if (_monitor_top == bad_monitors) { + return; + } + + // Bail out when we get repeated locks on an identical monitor. This case + // isn't too hard to handle and can be made to work if supporting nested + // redundant synchronized statements becomes a priority. + // + // See also "Note" in do_monitorexit(), below. + if (actual.is_lock_reference()) { + _monitor_top = bad_monitors; + _monitor_safe = false; + + if (TraceMonitorMismatch) { + report_monitor_mismatch("nested redundant lock -- bailout..."); + } + return; + } + + CellTypeState lock = CellTypeState::make_lock_ref(bci); + check_type(refCTS, actual); + if (!actual.is_info_top()) { + replace_all_CTS_matches(actual, lock); + monitor_push(lock); + } +} + +void GenerateOopMap::do_monitorexit(int bci) { + CellTypeState actual = pop(); + if (_monitor_top == bad_monitors) { + return; + } + check_type(refCTS, actual); + CellTypeState expected = monitor_pop(); + if (!actual.is_lock_reference() || !expected.equal(actual)) { + // The monitor we are exiting is not verifiably the one + // on the top of our monitor stack. This causes a monitor + // mismatch. + _monitor_top = bad_monitors; + _monitor_safe = false; + + // We need to mark this basic block as changed so that + // this monitorexit will be visited again. We need to + // do this to ensure that we have accounted for the + // possibility that this bytecode will throw an + // exception. + BasicBlock* bb = get_basic_block_containing(bci); + bb->set_changed(true); + bb->_monitor_top = bad_monitors; + + if (TraceMonitorMismatch) { + report_monitor_mismatch("improper monitor pair"); + } + } else { + // This code is a fix for the case where we have repeated + // locking of the same object in straightline code. We clear + // out the lock when it is popped from the monitor stack + // and replace it with an unobtrusive reference value that can + // be locked again. + // + // Note: when generateOopMap is fixed to properly handle repeated, + // nested, redundant locks on the same object, then this + // fix will need to be removed at that time. + replace_all_CTS_matches(actual, CellTypeState::make_line_ref(bci)); + } +} + +void GenerateOopMap::do_return_monitor_check() { + if (_monitor_top > 0) { + // The monitor stack must be empty when we leave the method + // for the monitors to be properly matched. + _monitor_safe = false; + + // Since there are no successors to the *return bytecode, it + // isn't necessary to set _monitor_top to bad_monitors. + + if (TraceMonitorMismatch) { + report_monitor_mismatch("non-empty monitor stack at return"); + } + } +} + +void GenerateOopMap::do_jsr(int targ_bci) { + push(CellTypeState::make_addr(targ_bci)); +} + + + +void GenerateOopMap::do_ldc(int idx, int bci) { + constantPoolOop cp = method()->constants(); + constantTag tag = cp->tag_at(idx); + + CellTypeState cts = (tag.is_string() || tag.is_unresolved_string() || + tag.is_klass() || tag.is_unresolved_klass()) + ? CellTypeState::make_line_ref(bci) : valCTS; + ppush1(cts); +} + +void GenerateOopMap::do_multianewarray(int dims, int bci) { + assert(dims >= 1, "sanity check"); + for(int i = dims -1; i >=0; i--) { + ppop1(valCTS); + } + ppush1(CellTypeState::make_line_ref(bci)); +} + +void GenerateOopMap::do_astore(int idx) { + CellTypeState r_or_p = pop(); + if (!r_or_p.is_address() && !r_or_p.is_reference()) { + // We actually expected ref or pc, but we only report that we expected a ref. It does not + // really matter (at least for now) + verify_error("wrong type on stack (found: %c, expected: {pr})", r_or_p.to_char()); + return; + } + set_var(idx, r_or_p); +} + +// Copies bottom/zero terminated CTS string from "src" into "dst". +// Does NOT terminate with a bottom. Returns the number of cells copied. +int GenerateOopMap::copy_cts(CellTypeState *dst, CellTypeState *src) { + int idx = 0; + while (!src[idx].is_bottom()) { + dst[idx] = src[idx]; + idx++; + } + return idx; +} + +void GenerateOopMap::do_field(int is_get, int is_static, int idx, int bci) { + // Dig up signature for field in constant pool + constantPoolOop cp = method()->constants(); + int nameAndTypeIdx = cp->name_and_type_ref_index_at(idx); + int signatureIdx = cp->signature_ref_index_at(nameAndTypeIdx); + symbolOop signature = cp->symbol_at(signatureIdx); + + // Parse signature (espcially simple for fields) + assert(signature->utf8_length() > 0, "field signatures cannot have zero length"); + // The signature is UFT8 encoded, but the first char is always ASCII for signatures. + char sigch = (char)*(signature->base()); + CellTypeState temp[4]; + CellTypeState *eff = sigchar_to_effect(sigch, bci, temp); + + CellTypeState in[4]; + CellTypeState *out; + int i = 0; + + if (is_get) { + out = eff; + } else { + out = epsilonCTS; + i = copy_cts(in, eff); + } + if (!is_static) in[i++] = CellTypeState::ref; + in[i] = CellTypeState::bottom; + assert(i<=3, "sanity check"); + pp(in, out); +} + +void GenerateOopMap::do_method(int is_static, int is_interface, int idx, int bci) { + // Dig up signature for field in constant pool + constantPoolOop cp = _method->constants(); + int nameAndTypeIdx = cp->name_and_type_ref_index_at(idx); + int signatureIdx = cp->signature_ref_index_at(nameAndTypeIdx); + symbolOop signature = cp->symbol_at(signatureIdx); + + // Parse method signature + CellTypeState out[4]; + CellTypeState in[MAXARGSIZE+1]; // Includes result + ComputeCallStack cse(signature); + + // Compute return type + int res_length= cse.compute_for_returntype(out); + + // Temporary hack. + if (out[0].equal(CellTypeState::ref) && out[1].equal(CellTypeState::bottom)) { + out[0] = CellTypeState::make_line_ref(bci); + } + + assert(res_length<=4, "max value should be vv"); + + // Compute arguments + int arg_length = cse.compute_for_parameters(is_static != 0, in); + assert(arg_length<=MAXARGSIZE, "too many locals"); + + // Pop arguments + for (int i = arg_length - 1; i >= 0; i--) ppop1(in[i]);// Do args in reverse order. + + // Report results + if (_report_result_for_send == true) { + fill_stackmap_for_opcodes(_itr_send, vars(), stack(), _stack_top); + _report_result_for_send = false; + } + + // Push return address + ppush(out); +} + +// This is used to parse the signature for fields, since they are very simple... +CellTypeState *GenerateOopMap::sigchar_to_effect(char sigch, int bci, CellTypeState *out) { + // Object and array + if (sigch=='L' || sigch=='[') { + out[0] = CellTypeState::make_line_ref(bci); + out[1] = CellTypeState::bottom; + return out; + } + if (sigch == 'J' || sigch == 'D' ) return vvCTS; // Long and Double + if (sigch == 'V' ) return epsilonCTS; // Void + return vCTS; // Otherwise +} + +long GenerateOopMap::_total_byte_count = 0; +elapsedTimer GenerateOopMap::_total_oopmap_time; + +// This function assumes "bcs" is at a "ret" instruction and that the vars +// state is valid for that instruction. Furthermore, the ret instruction +// must be the last instruction in "bb" (we store information about the +// "ret" in "bb"). +void GenerateOopMap::ret_jump_targets_do(BytecodeStream *bcs, jmpFct_t jmpFct, int varNo, int *data) { + CellTypeState ra = vars()[varNo]; + if (!ra.is_good_address()) { + verify_error("ret returns from two jsr subroutines?"); + return; + } + int target = ra.get_info(); + + RetTableEntry* rtEnt = _rt.find_jsrs_for_target(target); + int bci = bcs->bci(); + for (int i = 0; i < rtEnt->nof_jsrs(); i++) { + int target_bci = rtEnt->jsrs(i); + // Make sure a jrtRet does not set the changed bit for dead basicblock. + BasicBlock* jsr_bb = get_basic_block_containing(target_bci - 1); + debug_only(BasicBlock* target_bb = &jsr_bb[1];) + assert(target_bb == get_basic_block_at(target_bci), "wrong calc. of successor basicblock"); + bool alive = jsr_bb->is_alive(); + if (TraceNewOopMapGeneration) { + tty->print("pc = %d, ret -> %d alive: %s\n", bci, target_bci, alive ? "true" : "false"); + } + if (alive) jmpFct(this, target_bci, data); + } +} + +// +// Debug method +// +char* GenerateOopMap::state_vec_to_string(CellTypeState* vec, int len) { +#ifdef ASSERT + int checklen = MAX3(_max_locals, _max_stack, _max_monitors) + 1; + assert(len < checklen, "state_vec_buf overflow"); +#endif + for (int i = 0; i < len; i++) _state_vec_buf[i] = vec[i].to_char(); + _state_vec_buf[len] = 0; + return _state_vec_buf; +} + +void GenerateOopMap::print_time() { + tty->print_cr ("Accumulated oopmap times:"); + tty->print_cr ("---------------------------"); + tty->print_cr (" Total : %3.3f sec.", GenerateOopMap::_total_oopmap_time.seconds()); + tty->print_cr (" (%3.0f bytecodes per sec) ", + GenerateOopMap::_total_byte_count / GenerateOopMap::_total_oopmap_time.seconds()); +} + +// +// ============ Main Entry Point =========== +// +GenerateOopMap::GenerateOopMap(methodHandle method) { + // We have to initialize all variables here, that can be queried direcly + _method = method; + _max_locals=0; + _init_vars = NULL; + +#ifndef PRODUCT + // If we are doing a detailed trace, include the regular trace information. + if (TraceNewOopMapGenerationDetailed) { + TraceNewOopMapGeneration = true; + } +#endif +} + +void GenerateOopMap::compute_map(TRAPS) { +#ifndef PRODUCT + if (TimeOopMap2) { + method()->print_short_name(tty); + tty->print(" "); + } + if (TimeOopMap) { + _total_byte_count += method()->code_size(); + } +#endif + TraceTime t_single("oopmap time", TimeOopMap2); + TraceTime t_all(NULL, &_total_oopmap_time, TimeOopMap); + + // Initialize values + _got_error = false; + _conflict = false; + _max_locals = method()->max_locals(); + _max_stack = method()->max_stack(); + _has_exceptions = (method()->exception_table()->length() > 0); + _nof_refval_conflicts = 0; + _init_vars = new GrowableArray(5); // There are seldom more than 5 init_vars + _report_result = false; + _report_result_for_send = false; + _new_var_map = NULL; + _ret_adr_tos = new GrowableArray(5); // 5 seems like a good number; + _did_rewriting = false; + _did_relocation = false; + + if (TraceNewOopMapGeneration) { + tty->print("Method name: %s\n", method()->name()->as_C_string()); + if (Verbose) { + _method->print_codes(); + tty->print_cr("Exception table:"); + typeArrayOop excps = method()->exception_table(); + for(int i = 0; i < excps->length(); i += 4) { + tty->print_cr("[%d - %d] -> %d", excps->int_at(i + 0), excps->int_at(i + 1), excps->int_at(i + 2)); + } + } + } + + // if no code - do nothing + // compiler needs info + if (method()->code_size() == 0 || _max_locals + method()->max_stack() == 0) { + fill_stackmap_prolog(0); + fill_stackmap_epilog(); + return; + } + // Step 1: Compute all jump targets and their return value + if (!_got_error) + _rt.compute_ret_table(_method); + + // Step 2: Find all basic blocks and count GC points + if (!_got_error) + mark_bbheaders_and_count_gc_points(); + + // Step 3: Calculate stack maps + if (!_got_error) + do_interpretation(); + + // Step 4:Return results + if (!_got_error && report_results()) + report_result(); + + if (_got_error) { + THROW_HANDLE(_exception); + } +} + +// Error handling methods +// These methods create an exception for the current thread which is thrown +// at the bottom of the call stack, when it returns to compute_map(). The +// _got_error flag controls execution. NOT TODO: The VM exception propagation +// mechanism using TRAPS/CHECKs could be used here instead but it would need +// to be added as a parameter to every function and checked for every call. +// The tons of extra code it would generate didn't seem worth the change. +// +void GenerateOopMap::error_work(const char *format, va_list ap) { + _got_error = true; + char msg_buffer[512]; + vsnprintf(msg_buffer, sizeof(msg_buffer), format, ap); + // Append method name + char msg_buffer2[512]; + jio_snprintf(msg_buffer2, sizeof(msg_buffer2), "%s in method %s", msg_buffer, method()->name()->as_C_string()); + _exception = Exceptions::new_exception(Thread::current(), + vmSymbols::java_lang_LinkageError(), msg_buffer2); +} + +void GenerateOopMap::report_error(const char *format, ...) { + va_list ap; + va_start(ap, format); + error_work(format, ap); +} + +void GenerateOopMap::verify_error(const char *format, ...) { + // We do not distinguish between different types of errors for verification + // errors. Let the verifier give a better message. + const char *msg = "Illegal class file encountered. Try running with -Xverify:all"; + error_work(msg, NULL); +} + +// +// Report result opcodes +// +void GenerateOopMap::report_result() { + + if (TraceNewOopMapGeneration) tty->print_cr("Report result pass"); + + // We now want to report the result of the parse + _report_result = true; + + // Prolog code + fill_stackmap_prolog(_gc_points); + + // Mark everything changed, then do one interpretation pass. + for (int i = 0; i<_bb_count; i++) { + if (_basic_blocks[i].is_reachable()) { + _basic_blocks[i].set_changed(true); + interp_bb(&_basic_blocks[i]); + } + } + + // Note: Since we are skipping dead-code when we are reporting results, then + // the no. of encountered gc-points might be fewer than the previously number + // we have counted. (dead-code is a pain - it should be removed before we get here) + fill_stackmap_epilog(); + + // Report initvars + fill_init_vars(_init_vars); + + _report_result = false; +} + +void GenerateOopMap::result_for_basicblock(int bci) { + if (TraceNewOopMapGeneration) tty->print_cr("Report result pass for basicblock"); + + // We now want to report the result of the parse + _report_result = true; + + // Find basicblock and report results + BasicBlock* bb = get_basic_block_containing(bci); + assert(bb->is_reachable(), "getting result from unreachable basicblock"); + bb->set_changed(true); + interp_bb(bb); +} + +// +// Conflict handling code +// + +void GenerateOopMap::record_refval_conflict(int varNo) { + assert(varNo>=0 && varNo< _max_locals, "index out of range"); + + if (TraceOopMapRewrites) { + tty->print("### Conflict detected (local no: %d)\n", varNo); + } + + if (!_new_var_map) { + _new_var_map = NEW_RESOURCE_ARRAY(int, _max_locals); + for (int k = 0; k < _max_locals; k++) _new_var_map[k] = k; + } + + if ( _new_var_map[varNo] == varNo) { + // Check if max. number of locals has been reached + if (_max_locals + _nof_refval_conflicts >= MAX_LOCAL_VARS) { + report_error("Rewriting exceeded local variable limit"); + return; + } + _new_var_map[varNo] = _max_locals + _nof_refval_conflicts; + _nof_refval_conflicts++; + } +} + +void GenerateOopMap::rewrite_refval_conflicts() +{ + // We can get here two ways: Either a rewrite conflict was detected, or + // an uninitialize reference was detected. In the second case, we do not + // do any rewriting, we just want to recompute the reference set with the + // new information + + int nof_conflicts = 0; // Used for debugging only + + if ( _nof_refval_conflicts == 0 ) + return; + + // Check if rewrites are allowed in this parse. + if (!allow_rewrites() && !IgnoreRewrites) { + fatal("Rewriting method not allowed at this stage"); + } + + + // This following flag is to tempoary supress rewrites. The locals that might conflict will + // all be set to contain values. This is UNSAFE - however, until the rewriting has been completely + // tested it is nice to have. + if (IgnoreRewrites) { + if (Verbose) { + tty->print("rewrites suppressed for local no. "); + for (int l = 0; l < _max_locals; l++) { + if (_new_var_map[l] != l) { + tty->print("%d ", l); + vars()[l] = CellTypeState::value; + } + } + tty->cr(); + } + + // That was that... + _new_var_map = NULL; + _nof_refval_conflicts = 0; + _conflict = false; + + return; + } + + // Tracing flag + _did_rewriting = true; + + if (TraceOopMapRewrites) { + tty->print_cr("ref/value conflict for method %s - bytecodes are getting rewritten", method()->name()->as_C_string()); + method()->print(); + method()->print_codes(); + } + + assert(_new_var_map!=NULL, "nothing to rewrite"); + assert(_conflict==true, "We should not be here"); + + compute_ret_adr_at_TOS(); + if (!_got_error) { + for (int k = 0; k < _max_locals && !_got_error; k++) { + if (_new_var_map[k] != k) { + if (TraceOopMapRewrites) { + tty->print_cr("Rewriting: %d -> %d", k, _new_var_map[k]); + } + rewrite_refval_conflict(k, _new_var_map[k]); + if (_got_error) return; + nof_conflicts++; + } + } + } + + assert(nof_conflicts == _nof_refval_conflicts, "sanity check"); + + // Adjust the number of locals + method()->set_max_locals(_max_locals+_nof_refval_conflicts); + _max_locals += _nof_refval_conflicts; + + // That was that... + _new_var_map = NULL; + _nof_refval_conflicts = 0; +} + +void GenerateOopMap::rewrite_refval_conflict(int from, int to) { + bool startOver; + do { + // Make sure that the BytecodeStream is constructed in the loop, since + // during rewriting a new method oop is going to be used, and the next time + // around we want to use that. + BytecodeStream bcs(_method); + startOver = false; + + while( bcs.next() >=0 && !startOver && !_got_error) { + startOver = rewrite_refval_conflict_inst(&bcs, from, to); + } + } while (startOver && !_got_error); +} + +/* If the current instruction is one that uses local variable "from" + in a ref way, change it to use "to". There's a subtle reason why we + renumber the ref uses and not the non-ref uses: non-ref uses may be + 2 slots wide (double, long) which would necessitate keeping track of + whether we should add one or two variables to the method. If the change + affected the width of some instruction, returns "TRUE"; otherwise, returns "FALSE". + Another reason for moving ref's value is for solving (addr, ref) conflicts, which + both uses aload/astore methods. +*/ +bool GenerateOopMap::rewrite_refval_conflict_inst(BytecodeStream *itr, int from, int to) { + Bytecodes::Code bc = itr->code(); + int index; + int bci = itr->bci(); + + if (is_aload(itr, &index) && index == from) { + if (TraceOopMapRewrites) { + tty->print_cr("Rewriting aload at bci: %d", bci); + } + return rewrite_load_or_store(itr, Bytecodes::_aload, Bytecodes::_aload_0, to); + } + + if (is_astore(itr, &index) && index == from) { + if (!stack_top_holds_ret_addr(bci)) { + if (TraceOopMapRewrites) { + tty->print_cr("Rewriting astore at bci: %d", bci); + } + return rewrite_load_or_store(itr, Bytecodes::_astore, Bytecodes::_astore_0, to); + } else { + if (TraceOopMapRewrites) { + tty->print_cr("Supress rewriting of astore at bci: %d", bci); + } + } + } + + return false; +} + +// The argument to this method is: +// bc : Current bytecode +// bcN : either _aload or _astore +// bc0 : either _aload_0 or _astore_0 +bool GenerateOopMap::rewrite_load_or_store(BytecodeStream *bcs, Bytecodes::Code bcN, Bytecodes::Code bc0, unsigned int varNo) { + assert(bcN == Bytecodes::_astore || bcN == Bytecodes::_aload, "wrong argument (bcN)"); + assert(bc0 == Bytecodes::_astore_0 || bc0 == Bytecodes::_aload_0, "wrong argument (bc0)"); + int ilen = Bytecodes::length_at(bcs->bcp()); + int newIlen; + + if (ilen == 4) { + // Original instruction was wide; keep it wide for simplicity + newIlen = 4; + } else if (varNo < 4) + newIlen = 1; + else if (varNo >= 256) + newIlen = 4; + else + newIlen = 2; + + // If we need to relocate in order to patch the byte, we + // do the patching in a temp. buffer, that is passed to the reloc. + // The patching of the bytecode stream is then done by the Relocator. + // This is neccesary, since relocating the instruction at a certain bci, might + // also relocate that instruction, e.g., if a _goto before it gets widen to a _goto_w. + // Hence, we do not know which bci to patch after relocation. + + assert(newIlen <= 4, "sanity check"); + u_char inst_buffer[4]; // Max. instruction size is 4. + address bcp; + + if (newIlen != ilen) { + // Relocation needed do patching in temp. buffer + bcp = (address)inst_buffer; + } else { + bcp = _method->bcp_from(bcs->bci()); + } + + // Patch either directly in methodOop or in temp. buffer + if (newIlen == 1) { + assert(varNo < 4, "varNo too large"); + *bcp = bc0 + varNo; + } else if (newIlen == 2) { + assert(varNo < 256, "2-byte index needed!"); + *(bcp + 0) = bcN; + *(bcp + 1) = varNo; + } else { + assert(newIlen == 4, "Wrong instruction length"); + *(bcp + 0) = Bytecodes::_wide; + *(bcp + 1) = bcN; + Bytes::put_Java_u2(bcp+2, varNo); + } + + if (newIlen != ilen) { + expand_current_instr(bcs->bci(), ilen, newIlen, inst_buffer); + } + + + return (newIlen != ilen); +} + +class RelocCallback : public RelocatorListener { + private: + GenerateOopMap* _gom; + public: + RelocCallback(GenerateOopMap* gom) { _gom = gom; }; + + // Callback method + virtual void relocated(int bci, int delta, int new_code_length) { + _gom->update_basic_blocks (bci, delta, new_code_length); + _gom->update_ret_adr_at_TOS(bci, delta); + _gom->_rt.update_ret_table (bci, delta); + } +}; + +// Returns true if expanding was succesful. Otherwise, reports an error and +// returns false. +void GenerateOopMap::expand_current_instr(int bci, int ilen, int newIlen, u_char inst_buffer[]) { + Thread *THREAD = Thread::current(); // Could really have TRAPS argument. + RelocCallback rcb(this); + Relocator rc(_method, &rcb); + methodHandle m= rc.insert_space_at(bci, newIlen, inst_buffer, THREAD); + if (m.is_null() || HAS_PENDING_EXCEPTION) { + report_error("could not rewrite method - exception occurred or bytecode buffer overflow"); + return; + } + + // Relocator returns a new method oop. + _did_relocation = true; + _method = m; +} + + +bool GenerateOopMap::is_astore(BytecodeStream *itr, int *index) { + Bytecodes::Code bc = itr->code(); + switch(bc) { + case Bytecodes::_astore_0: + case Bytecodes::_astore_1: + case Bytecodes::_astore_2: + case Bytecodes::_astore_3: + *index = bc - Bytecodes::_astore_0; + return true; + case Bytecodes::_astore: + *index = itr->get_index(); + return true; + } + return false; +} + +bool GenerateOopMap::is_aload(BytecodeStream *itr, int *index) { + Bytecodes::Code bc = itr->code(); + switch(bc) { + case Bytecodes::_aload_0: + case Bytecodes::_aload_1: + case Bytecodes::_aload_2: + case Bytecodes::_aload_3: + *index = bc - Bytecodes::_aload_0; + return true; + + case Bytecodes::_aload: + *index = itr->get_index(); + return true; + } + return false; +} + + +// Return true iff the top of the operand stack holds a return address at +// the current instruction +bool GenerateOopMap::stack_top_holds_ret_addr(int bci) { + for(int i = 0; i < _ret_adr_tos->length(); i++) { + if (_ret_adr_tos->at(i) == bci) + return true; + } + + return false; +} + +void GenerateOopMap::compute_ret_adr_at_TOS() { + assert(_ret_adr_tos != NULL, "must be initialized"); + _ret_adr_tos->clear(); + + for (int i = 0; i < bb_count(); i++) { + BasicBlock* bb = &_basic_blocks[i]; + + // Make sure to only check basicblocks that are reachable + if (bb->is_reachable()) { + + // For each Basic block we check all instructions + BytecodeStream bcs(_method); + bcs.set_interval(bb->_bci, next_bb_start_pc(bb)); + + restore_state(bb); + + while (bcs.next()>=0 && !_got_error) { + // TDT: should this be is_good_address() ? + if (_stack_top > 0 && stack()[_stack_top-1].is_address()) { + _ret_adr_tos->append(bcs.bci()); + if (TraceNewOopMapGeneration) { + tty->print_cr("Ret_adr TOS at bci: %d", bcs.bci()); + } + } + interp1(&bcs); + } + } + } +} + +void GenerateOopMap::update_ret_adr_at_TOS(int bci, int delta) { + for(int i = 0; i < _ret_adr_tos->length(); i++) { + int v = _ret_adr_tos->at(i); + if (v > bci) _ret_adr_tos->at_put(i, v + delta); + } +} + +// =================================================================== + +#ifndef PRODUCT +int ResolveOopMapConflicts::_nof_invocations = 0; +int ResolveOopMapConflicts::_nof_rewrites = 0; +int ResolveOopMapConflicts::_nof_relocations = 0; +#endif + +methodHandle ResolveOopMapConflicts::do_potential_rewrite(TRAPS) { + compute_map(CHECK_(methodHandle())); + +#ifndef PRODUCT + // Tracking and statistics + if (PrintRewrites) { + _nof_invocations++; + if (did_rewriting()) { + _nof_rewrites++; + if (did_relocation()) _nof_relocations++; + tty->print("Method was rewritten %s: ", (did_relocation()) ? "and relocated" : ""); + method()->print_value(); tty->cr(); + tty->print_cr("Cand.: %d rewrts: %d (%d%%) reloc.: %d (%d%%)", + _nof_invocations, + _nof_rewrites, (_nof_rewrites * 100) / _nof_invocations, + _nof_relocations, (_nof_relocations * 100) / _nof_invocations); + } + } +#endif + return methodHandle(THREAD, method()); +} diff --git a/hotspot/src/share/vm/oops/generateOopMap.hpp b/hotspot/src/share/vm/oops/generateOopMap.hpp new file mode 100644 index 00000000000..f29982b3abf --- /dev/null +++ b/hotspot/src/share/vm/oops/generateOopMap.hpp @@ -0,0 +1,553 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Forward definition +class MethodOopMap; +class GenerateOopMap; +class BasicBlock; +class CellTypeState; +class StackMap; + +// These two should be removed. But requires som code to be cleaned up +#define MAXARGSIZE 256 // This should be enough +#define MAX_LOCAL_VARS 65536 // 16-bit entry + +typedef void (*jmpFct_t)(GenerateOopMap *c, int bcpDelta, int* data); + + +// RetTable +// +// Contains maping between jsr targets and there return addresses. One-to-many mapping +// +class RetTableEntry : public ResourceObj { + private: + static int _init_nof_jsrs; // Default size of jsrs list + int _target_bci; // Target PC address of jump (bytecode index) + GrowableArray * _jsrs; // List of return addresses (bytecode index) + RetTableEntry *_next; // Link to next entry + public: + RetTableEntry(int target, RetTableEntry *next) { _target_bci=target; _jsrs = new GrowableArray(_init_nof_jsrs); _next = next; } + + // Query + int target_bci() const { return _target_bci; } + int nof_jsrs() const { return _jsrs->length(); } + int jsrs(int i) const { assert(i>=0 && iat(i); } + + // Update entry + void add_jsr (int return_bci) { _jsrs->append(return_bci); } + void add_delta (int bci, int delta); + RetTableEntry * next() const { return _next; } +}; + + +class RetTable VALUE_OBJ_CLASS_SPEC { + private: + RetTableEntry *_first; + static int _init_nof_entries; + + void add_jsr(int return_bci, int target_bci); // Adds entry to list + public: + RetTable() { _first = NULL; } + void compute_ret_table(methodHandle method); + void update_ret_table(int bci, int delta); + RetTableEntry* find_jsrs_for_target(int targBci); +}; + +// +// CellTypeState +// +class CellTypeState VALUE_OBJ_CLASS_SPEC { + private: + unsigned int _state; + + // Masks for separating the BITS and INFO portions of a CellTypeState + enum { info_mask = right_n_bits(28), + bits_mask = (int)(~info_mask) }; + + // These constant are used for manipulating the BITS portion of a + // CellTypeState + enum { uninit_bit = (int)(nth_bit(31)), + ref_bit = nth_bit(30), + val_bit = nth_bit(29), + addr_bit = nth_bit(28), + live_bits_mask = (int)(bits_mask & ~uninit_bit) }; + + // These constants are used for manipulating the INFO portion of a + // CellTypeState + enum { top_info_bit = nth_bit(27), + not_bottom_info_bit = nth_bit(26), + info_data_mask = right_n_bits(26), + info_conflict = info_mask }; + + // Within the INFO data, these values are used to distinguish different + // kinds of references. + enum { ref_not_lock_bit = nth_bit(25), // 0 if this reference is locked as a monitor + ref_slot_bit = nth_bit(24), // 1 if this reference is a "slot" reference, + // 0 if it is a "line" reference. + ref_data_mask = right_n_bits(24) }; + + + // These values are used to initialize commonly used CellTypeState + // constants. + enum { bottom_value = 0, + uninit_value = (int)(uninit_bit | info_conflict), + ref_value = ref_bit, + ref_conflict = ref_bit | info_conflict, + val_value = val_bit | info_conflict, + addr_value = addr_bit, + addr_conflict = addr_bit | info_conflict }; + + public: + + // Since some C++ constructors generate poor code for declarations of the + // form... + // + // CellTypeState vector[length]; + // + // ...we avoid making a constructor for this class. CellTypeState values + // should be constructed using one of the make_* methods: + + static CellTypeState make_any(int state) { + CellTypeState s; + s._state = state; + // Causes SS10 warning. + // assert(s.is_valid_state(), "check to see if CellTypeState is valid"); + return s; + } + + static CellTypeState make_bottom() { + return make_any(0); + } + + static CellTypeState make_top() { + return make_any(AllBits); + } + + static CellTypeState make_addr(int bci) { + assert((bci >= 0) && (bci < info_data_mask), "check to see if ret addr is valid"); + return make_any(addr_bit | not_bottom_info_bit | (bci & info_data_mask)); + } + + static CellTypeState make_slot_ref(int slot_num) { + assert(slot_num >= 0 && slot_num < ref_data_mask, "slot out of range"); + return make_any(ref_bit | not_bottom_info_bit | ref_not_lock_bit | ref_slot_bit | + (slot_num & ref_data_mask)); + } + + static CellTypeState make_line_ref(int bci) { + assert(bci >= 0 && bci < ref_data_mask, "line out of range"); + return make_any(ref_bit | not_bottom_info_bit | ref_not_lock_bit | + (bci & ref_data_mask)); + } + + static CellTypeState make_lock_ref(int bci) { + assert(bci >= 0 && bci < ref_data_mask, "line out of range"); + return make_any(ref_bit | not_bottom_info_bit | (bci & ref_data_mask)); + } + + // Query methods: + bool is_bottom() const { return _state == 0; } + bool is_live() const { return ((_state & live_bits_mask) != 0); } + bool is_valid_state() const { + // Uninitialized and value cells must contain no data in their info field: + if ((can_be_uninit() || can_be_value()) && !is_info_top()) { + return false; + } + // The top bit is only set when all info bits are set: + if (is_info_top() && ((_state & info_mask) != info_mask)) { + return false; + } + // The not_bottom_bit must be set when any other info bit is set: + if (is_info_bottom() && ((_state & info_mask) != 0)) { + return false; + } + return true; + } + + bool is_address() const { return ((_state & bits_mask) == addr_bit); } + bool is_reference() const { return ((_state & bits_mask) == ref_bit); } + bool is_value() const { return ((_state & bits_mask) == val_bit); } + bool is_uninit() const { return ((_state & bits_mask) == (uint)uninit_bit); } + + bool can_be_address() const { return ((_state & addr_bit) != 0); } + bool can_be_reference() const { return ((_state & ref_bit) != 0); } + bool can_be_value() const { return ((_state & val_bit) != 0); } + bool can_be_uninit() const { return ((_state & uninit_bit) != 0); } + + bool is_info_bottom() const { return ((_state & not_bottom_info_bit) == 0); } + bool is_info_top() const { return ((_state & top_info_bit) != 0); } + int get_info() const { + assert((!is_info_top() && !is_info_bottom()), + "check to make sure top/bottom info is not used"); + return (_state & info_data_mask); + } + + bool is_good_address() const { return is_address() && !is_info_top(); } + bool is_lock_reference() const { + return ((_state & (bits_mask | top_info_bit | ref_not_lock_bit)) == ref_bit); + } + bool is_nonlock_reference() const { + return ((_state & (bits_mask | top_info_bit | ref_not_lock_bit)) == (ref_bit | ref_not_lock_bit)); + } + + bool equal(CellTypeState a) const { return _state == a._state; } + bool equal_kind(CellTypeState a) const { + return (_state & bits_mask) == (a._state & bits_mask); + } + + char to_char() const; + + // Merge + CellTypeState merge (CellTypeState cts, int slot) const; + + // Debugging output + void print(outputStream *os); + + // Default values of common values + static CellTypeState bottom; + static CellTypeState uninit; + static CellTypeState ref; + static CellTypeState value; + static CellTypeState refUninit; + static CellTypeState varUninit; + static CellTypeState top; + static CellTypeState addr; +}; + + +// +// BasicBlockStruct +// +class BasicBlock: ResourceObj { + private: + bool _changed; // Reached a fixpoint or not + public: + enum Constants { + _dead_basic_block = -2, + _unreached = -1 // Alive but not yet reached by analysis + // >=0 // Alive and has a merged state + }; + + int _bci; // Start of basic block + int _end_bci; // Bci of last instruction in basicblock + int _max_locals; // Determines split between vars and stack + int _max_stack; // Determines split between stack and monitors + CellTypeState* _state; // State (vars, stack) at entry. + int _stack_top; // -1 indicates bottom stack value. + int _monitor_top; // -1 indicates bottom monitor stack value. + + CellTypeState* vars() { return _state; } + CellTypeState* stack() { return _state + _max_locals; } + + bool changed() { return _changed; } + void set_changed(bool s) { _changed = s; } + + bool is_reachable() const { return _stack_top >= 0; } // Analysis has reached this basicblock + + // All basicblocks that are unreachable are going to have a _stack_top == _dead_basic_block. + // This info. is setup in a pre-parse before the real abstract interpretation starts. + bool is_dead() const { return _stack_top == _dead_basic_block; } + bool is_alive() const { return _stack_top != _dead_basic_block; } + void mark_as_alive() { assert(is_dead(), "must be dead"); _stack_top = _unreached; } +}; + + +// +// GenerateOopMap +// +// Main class used to compute the pointer-maps in a MethodOop +// +class GenerateOopMap VALUE_OBJ_CLASS_SPEC { + protected: + + // _monitor_top is set to this constant to indicate that a monitor matching + // problem was encountered prior to this point in control flow. + enum { bad_monitors = -1 }; + + // Main variables + methodHandle _method; // The method we are examine + RetTable _rt; // Contains the return address mappings + int _max_locals; // Cached value of no. of locals + int _max_stack; // Cached value of max. stack depth + int _max_monitors; // Cached value of max. monitor stack depth + int _has_exceptions; // True, if exceptions exist for method + bool _got_error; // True, if an error occured during interpretation. + Handle _exception; // Exception if got_error is true. + bool _did_rewriting; // was bytecodes rewritten + bool _did_relocation; // was relocation neccessary + bool _monitor_safe; // The monitors in this method have been determined + // to be safe. + + // Working Cell type state + int _state_len; // Size of states + CellTypeState *_state; // list of states + char *_state_vec_buf; // Buffer used to print a readable version of a state + int _stack_top; + int _monitor_top; + + // Timing and statistics + static elapsedTimer _total_oopmap_time; // Holds cumulative oopmap generation time + static long _total_byte_count; // Holds cumulative number of bytes inspected + + // Cell type methods + void init_state(); + void make_context_uninitialized (); + int methodsig_to_effect (symbolOop signature, bool isStatic, CellTypeState* effect); + bool merge_local_state_vectors (CellTypeState* cts, CellTypeState* bbts); + bool merge_monitor_state_vectors(CellTypeState* cts, CellTypeState* bbts); + void copy_state (CellTypeState *dst, CellTypeState *src); + void merge_state_into_bb (BasicBlock *bb); + static void merge_state (GenerateOopMap *gom, int bcidelta, int* data); + void set_var (int localNo, CellTypeState cts); + CellTypeState get_var (int localNo); + CellTypeState pop (); + void push (CellTypeState cts); + CellTypeState monitor_pop (); + void monitor_push (CellTypeState cts); + CellTypeState * vars () { return _state; } + CellTypeState * stack () { return _state+_max_locals; } + CellTypeState * monitors () { return _state+_max_locals+_max_stack; } + + void replace_all_CTS_matches (CellTypeState match, + CellTypeState replace); + void print_states (outputStream *os, CellTypeState *vector, int num); + void print_current_state (outputStream *os, + BytecodeStream *itr, + bool detailed); + void report_monitor_mismatch (const char *msg); + + // Basicblock info + BasicBlock * _basic_blocks; // Array of basicblock info + int _gc_points; + int _bb_count; + uintptr_t * _bb_hdr_bits; + + // Basicblocks methods + void initialize_bb (); + void mark_bbheaders_and_count_gc_points(); + bool is_bb_header (int bci) const { return (_bb_hdr_bits[bci >> LogBitsPerWord] & ((uintptr_t)1 << (bci & (BitsPerWord-1)))) != 0; } + int gc_points () const { return _gc_points; } + int bb_count () const { return _bb_count; } + void set_bbmark_bit (int bci); + void clear_bbmark_bit (int bci); + BasicBlock * get_basic_block_at (int bci) const; + BasicBlock * get_basic_block_containing (int bci) const; + void interp_bb (BasicBlock *bb); + void restore_state (BasicBlock *bb); + int next_bb_start_pc (BasicBlock *bb); + void update_basic_blocks (int bci, int delta, int new_method_size); + static void bb_mark_fct (GenerateOopMap *c, int deltaBci, int *data); + + // Dead code detection + void mark_reachable_code(); + static void reachable_basicblock (GenerateOopMap *c, int deltaBci, int *data); + + // Interpretation methods (primary) + void do_interpretation (); + void init_basic_blocks (); + void setup_method_entry_state (); + void interp_all (); + + // Interpretation methods (secondary) + void interp1 (BytecodeStream *itr); + void do_exception_edge (BytecodeStream *itr); + void check_type (CellTypeState expected, CellTypeState actual); + void ppstore (CellTypeState *in, int loc_no); + void ppload (CellTypeState *out, int loc_no); + void ppush1 (CellTypeState in); + void ppush (CellTypeState *in); + void ppop1 (CellTypeState out); + void ppop (CellTypeState *out); + void ppop_any (int poplen); + void pp (CellTypeState *in, CellTypeState *out); + void pp_new_ref (CellTypeState *in, int bci); + void ppdupswap (int poplen, const char *out); + void do_ldc (int idx, int bci); + void do_astore (int idx); + void do_jsr (int delta); + void do_field (int is_get, int is_static, int idx, int bci); + void do_method (int is_static, int is_interface, int idx, int bci); + void do_multianewarray (int dims, int bci); + void do_monitorenter (int bci); + void do_monitorexit (int bci); + void do_return_monitor_check (); + void do_checkcast (); + CellTypeState *sigchar_to_effect (char sigch, int bci, CellTypeState *out); + int copy_cts (CellTypeState *dst, CellTypeState *src); + + // Error handling + void error_work (const char *format, va_list ap); + void report_error (const char *format, ...); + void verify_error (const char *format, ...); + bool got_error() { return _got_error; } + + // Create result set + bool _report_result; + bool _report_result_for_send; // Unfortunatly, stackmaps for sends are special, so we need some extra + BytecodeStream *_itr_send; // variables to handle them properly. + + void report_result (); + + // Initvars + GrowableArray * _init_vars; + + void initialize_vars (); + void add_to_ref_init_set (int localNo); + + // Conflicts rewrite logic + bool _conflict; // True, if a conflict occured during interpretation + int _nof_refval_conflicts; // No. of conflicts that require rewrites + int * _new_var_map; + + void record_refval_conflict (int varNo); + void rewrite_refval_conflicts (); + void rewrite_refval_conflict (int from, int to); + bool rewrite_refval_conflict_inst (BytecodeStream *i, int from, int to); + bool rewrite_load_or_store (BytecodeStream *i, Bytecodes::Code bc, Bytecodes::Code bc0, unsigned int varNo); + + void expand_current_instr (int bci, int ilen, int newIlen, u_char inst_buffer[]); + bool is_astore (BytecodeStream *itr, int *index); + bool is_aload (BytecodeStream *itr, int *index); + + // List of bci's where a return address is on top of the stack + GrowableArray *_ret_adr_tos; + + bool stack_top_holds_ret_addr (int bci); + void compute_ret_adr_at_TOS (); + void update_ret_adr_at_TOS (int bci, int delta); + + int binsToHold (int no) { return ((no+(BitsPerWord-1))/BitsPerWord); } + char *state_vec_to_string (CellTypeState* vec, int len); + + // Helper method. Can be used in subclasses to fx. calculate gc_points. If the current instuction + // is a control transfer, then calls the jmpFct all possible destinations. + void ret_jump_targets_do (BytecodeStream *bcs, jmpFct_t jmpFct, int varNo,int *data); + bool jump_targets_do (BytecodeStream *bcs, jmpFct_t jmpFct, int *data); + + friend class RelocCallback; + public: + GenerateOopMap(methodHandle method); + + // Compute the map. + void compute_map(TRAPS); + void result_for_basicblock(int bci); // Do a callback on fill_stackmap_for_opcodes for basicblock containing bci + + // Query + int max_locals() const { return _max_locals; } + methodOop method() const { return _method(); } + methodHandle method_as_handle() const { return _method; } + + bool did_rewriting() { return _did_rewriting; } + bool did_relocation() { return _did_relocation; } + + static void print_time(); + + // Monitor query + bool monitor_safe() { return _monitor_safe; } + + // Specialization methods. Intended use: + // - possible_gc_point must return true for every bci for which the stackmaps must be returned + // - fill_stackmap_prolog is called just before the result is reported. The arguments tells the estimated + // number of gc points + // - fill_stackmap_for_opcodes is called once for each bytecode index in order (0...code_length-1) + // - fill_stackmap_epilog is called after all results has been reported. Note: Since the algorithm does not report + // stackmaps for deadcode, fewer gc_points might have been encounted than assumed during the epilog. It is the + // responsibility of the subclass to count the correct number. + // - fill_init_vars are called once with the result of the init_vars computation + // + // All these methods are used during a call to: compute_map. Note: Non of the return results are valid + // after compute_map returns, since all values are allocated as resource objects. + // + // All virtual method must be implemented in subclasses + virtual bool allow_rewrites () const { return false; } + virtual bool report_results () const { return true; } + virtual bool report_init_vars () const { return true; } + virtual bool possible_gc_point (BytecodeStream *bcs) { ShouldNotReachHere(); return false; } + virtual void fill_stackmap_prolog (int nof_gc_points) { ShouldNotReachHere(); } + virtual void fill_stackmap_epilog () { ShouldNotReachHere(); } + virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, + CellTypeState* vars, + CellTypeState* stack, + int stackTop) { ShouldNotReachHere(); } + virtual void fill_init_vars (GrowableArray *init_vars) { ShouldNotReachHere();; } +}; + +// +// Subclass of the GenerateOopMap Class that just do rewrites of the method, if needed. +// It does not store any oopmaps. +// +class ResolveOopMapConflicts: public GenerateOopMap { + private: + + bool _must_clear_locals; + + virtual bool report_results() const { return false; } + virtual bool report_init_vars() const { return true; } + virtual bool allow_rewrites() const { return true; } + virtual bool possible_gc_point (BytecodeStream *bcs) { return false; } + virtual void fill_stackmap_prolog (int nof_gc_points) {} + virtual void fill_stackmap_epilog () {} + virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, + CellTypeState* vars, + CellTypeState* stack, + int stack_top) {} + virtual void fill_init_vars (GrowableArray *init_vars) { _must_clear_locals = init_vars->length() > 0; } + +#ifndef PRODUCT + // Statistics + static int _nof_invocations; + static int _nof_rewrites; + static int _nof_relocations; +#endif + + public: + ResolveOopMapConflicts(methodHandle method) : GenerateOopMap(method) { _must_clear_locals = false; }; + + methodHandle do_potential_rewrite(TRAPS); + bool must_clear_locals() const { return _must_clear_locals; } +}; + + +// +// Subclass used by the compiler to generate pairing infomation +// +class GeneratePairingInfo: public GenerateOopMap { + private: + + virtual bool report_results() const { return false; } + virtual bool report_init_vars() const { return false; } + virtual bool allow_rewrites() const { return false; } + virtual bool possible_gc_point (BytecodeStream *bcs) { return false; } + virtual void fill_stackmap_prolog (int nof_gc_points) {} + virtual void fill_stackmap_epilog () {} + virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, + CellTypeState* vars, + CellTypeState* stack, + int stack_top) {} + virtual void fill_init_vars (GrowableArray *init_vars) {} + public: + GeneratePairingInfo(methodHandle method) : GenerateOopMap(method) {}; + + // Call compute_map(CHECK) to generate info. +}; diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp new file mode 100644 index 00000000000..a1105d49486 --- /dev/null +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp @@ -0,0 +1,2585 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_instanceKlass.cpp.incl" + +bool instanceKlass::should_be_initialized() const { + return !is_initialized(); +} + +klassVtable* instanceKlass::vtable() const { + return new klassVtable(as_klassOop(), start_of_vtable(), vtable_length() / vtableEntry::size()); +} + +klassItable* instanceKlass::itable() const { + return new klassItable(as_klassOop()); +} + +void instanceKlass::eager_initialize(Thread *thread) { + if (!EagerInitialization) return; + + if (this->is_not_initialized()) { + // abort if the the class has a class initializer + if (this->class_initializer() != NULL) return; + + // abort if it is java.lang.Object (initialization is handled in genesis) + klassOop super = this->super(); + if (super == NULL) return; + + // abort if the super class should be initialized + if (!instanceKlass::cast(super)->is_initialized()) return; + + // call body to expose the this pointer + instanceKlassHandle this_oop(thread, this->as_klassOop()); + eager_initialize_impl(this_oop); + } +} + + +void instanceKlass::eager_initialize_impl(instanceKlassHandle this_oop) { + EXCEPTION_MARK; + ObjectLocker ol(this_oop, THREAD); + + // abort if someone beat us to the initialization + if (!this_oop->is_not_initialized()) return; // note: not equivalent to is_initialized() + + ClassState old_state = this_oop->_init_state; + link_class_impl(this_oop, true, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + // Abort if linking the class throws an exception. + + // Use a test to avoid redundantly resetting the state if there's + // no change. Set_init_state() asserts that state changes make + // progress, whereas here we might just be spinning in place. + if( old_state != this_oop->_init_state ) + this_oop->set_init_state (old_state); + } else { + // linking successfull, mark class as initialized + this_oop->set_init_state (fully_initialized); + // trace + if (TraceClassInitialization) { + ResourceMark rm(THREAD); + tty->print_cr("[Initialized %s without side effects]", this_oop->external_name()); + } + } +} + + +// See "The Virtual Machine Specification" section 2.16.5 for a detailed explanation of the class initialization +// process. The step comments refers to the procedure described in that section. +// Note: implementation moved to static method to expose the this pointer. +void instanceKlass::initialize(TRAPS) { + if (this->should_be_initialized()) { + HandleMark hm(THREAD); + instanceKlassHandle this_oop(THREAD, this->as_klassOop()); + initialize_impl(this_oop, CHECK); + // Note: at this point the class may be initialized + // OR it may be in the state of being initialized + // in case of recursive initialization! + } else { + assert(is_initialized(), "sanity check"); + } +} + + +bool instanceKlass::verify_code( + instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) { + // 1) Verify the bytecodes + Verifier::Mode mode = + throw_verifyerror ? Verifier::ThrowException : Verifier::NoException; + return Verifier::verify(this_oop, mode, CHECK_false); +} + + +// Used exclusively by the shared spaces dump mechanism to prevent +// classes mapped into the shared regions in new VMs from appearing linked. + +void instanceKlass::unlink_class() { + assert(is_linked(), "must be linked"); + _init_state = loaded; +} + +void instanceKlass::link_class(TRAPS) { + assert(is_loaded(), "must be loaded"); + if (!is_linked()) { + instanceKlassHandle this_oop(THREAD, this->as_klassOop()); + link_class_impl(this_oop, true, CHECK); + } +} + +// Called to verify that a class can link during initialization, without +// throwing a VerifyError. +bool instanceKlass::link_class_or_fail(TRAPS) { + assert(is_loaded(), "must be loaded"); + if (!is_linked()) { + instanceKlassHandle this_oop(THREAD, this->as_klassOop()); + link_class_impl(this_oop, false, CHECK_false); + } + return is_linked(); +} + +bool instanceKlass::link_class_impl( + instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) { + // check for error state + if (this_oop->is_in_error_state()) { + ResourceMark rm(THREAD); + THROW_MSG_(vmSymbols::java_lang_NoClassDefFoundError(), + this_oop->external_name(), false); + } + // return if already verified + if (this_oop->is_linked()) { + return true; + } + + // Timing + // timer handles recursion + assert(THREAD->is_Java_thread(), "non-JavaThread in link_class_impl"); + JavaThread* jt = (JavaThread*)THREAD; + PerfTraceTimedEvent vmtimer(ClassLoader::perf_class_link_time(), + ClassLoader::perf_classes_linked(), + jt->get_thread_stat()->class_link_recursion_count_addr()); + + // link super class before linking this class + instanceKlassHandle super(THREAD, this_oop->super()); + if (super.not_null()) { + if (super->is_interface()) { // check if super class is an interface + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_IncompatibleClassChangeError(), + "class %s has interface %s as super class", + this_oop->external_name(), + super->external_name() + ); + return false; + } + + link_class_impl(super, throw_verifyerror, CHECK_false); + } + + // link all interfaces implemented by this class before linking this class + objArrayHandle interfaces (THREAD, this_oop->local_interfaces()); + int num_interfaces = interfaces->length(); + for (int index = 0; index < num_interfaces; index++) { + HandleMark hm(THREAD); + instanceKlassHandle ih(THREAD, klassOop(interfaces->obj_at(index))); + link_class_impl(ih, throw_verifyerror, CHECK_false); + } + + // in case the class is linked in the process of linking its superclasses + if (this_oop->is_linked()) { + return true; + } + + // verification & rewriting + { + ObjectLocker ol(this_oop, THREAD); + // rewritten will have been set if loader constraint error found + // on an earlier link attempt + // don't verify or rewrite if already rewritten + if (!this_oop->is_linked()) { + if (!this_oop->is_rewritten()) { + { + assert(THREAD->is_Java_thread(), "non-JavaThread in link_class_impl"); + JavaThread* jt = (JavaThread*)THREAD; + // Timer includes any side effects of class verification (resolution, + // etc), but not recursive entry into verify_code(). + PerfTraceTime timer(ClassLoader::perf_class_verify_time(), + jt->get_thread_stat()->class_verify_recursion_count_addr()); + bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD); + if (!verify_ok) { + return false; + } + } + + // Just in case a side-effect of verify linked this class already + // (which can sometimes happen since the verifier loads classes + // using custom class loaders, which are free to initialize things) + if (this_oop->is_linked()) { + return true; + } + + // also sets rewritten + this_oop->rewrite_class(CHECK_false); + } + + // Initialize the vtable and interface table after + // methods have been rewritten since rewrite may + // fabricate new methodOops. + // also does loader constraint checking + if (!this_oop()->is_shared()) { + ResourceMark rm(THREAD); + this_oop->vtable()->initialize_vtable(true, CHECK_false); + this_oop->itable()->initialize_itable(true, CHECK_false); + } +#ifdef ASSERT + else { + ResourceMark rm(THREAD); + this_oop->vtable()->verify(tty, true); + // In case itable verification is ever added. + // this_oop->itable()->verify(tty, true); + } +#endif + this_oop->set_init_state(linked); + if (JvmtiExport::should_post_class_prepare()) { + Thread *thread = THREAD; + assert(thread->is_Java_thread(), "thread->is_Java_thread()"); + JvmtiExport::post_class_prepare((JavaThread *) thread, this_oop()); + } + } + } + return true; +} + + +// Rewrite the byte codes of all of the methods of a class. +// Three cases: +// During the link of a newly loaded class. +// During the preloading of classes to be written to the shared spaces. +// - Rewrite the methods and update the method entry points. +// +// During the link of a class in the shared spaces. +// - The methods were already rewritten, update the metho entry points. +// +// The rewriter must be called exactly once. Rewriting must happen after +// verification but before the first method of the class is executed. + +void instanceKlass::rewrite_class(TRAPS) { + assert(is_loaded(), "must be loaded"); + instanceKlassHandle this_oop(THREAD, this->as_klassOop()); + if (this_oop->is_rewritten()) { + assert(this_oop()->is_shared(), "rewriting an unshared class?"); + return; + } + Rewriter::rewrite(this_oop, CHECK); // No exception can happen here + this_oop->set_rewritten(); +} + + +void instanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) { + // Make sure klass is linked (verified) before initialization + // A class could already be verified, since it has been reflected upon. + this_oop->link_class(CHECK); + + // refer to the JVM book page 47 for description of steps + // Step 1 + { ObjectLocker ol(this_oop, THREAD); + + Thread *self = THREAD; // it's passed the current thread + + // Step 2 + // If we were to use wait() instead of waitInterruptibly() then + // we might end up throwing IE from link/symbol resolution sites + // that aren't expected to throw. This would wreak havoc. See 6320309. + while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) { + ol.waitUninterruptibly(CHECK); + } + + // Step 3 + if (this_oop->is_being_initialized() && this_oop->is_reentrant_initialization(self)) + return; + + // Step 4 + if (this_oop->is_initialized()) + return; + + // Step 5 + if (this_oop->is_in_error_state()) { + ResourceMark rm(THREAD); + const char* desc = "Could not initialize class "; + const char* className = this_oop->external_name(); + size_t msglen = strlen(desc) + strlen(className) + 1; + char* message = NEW_C_HEAP_ARRAY(char, msglen); + if (NULL == message) { + // Out of memory: can't create detailed error message + THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className); + } else { + jio_snprintf(message, msglen, "%s%s", desc, className); + THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message); + } + } + + // Step 6 + this_oop->set_init_state(being_initialized); + this_oop->set_init_thread(self); + } + + // Step 7 + klassOop super_klass = this_oop->super(); + if (super_klass != NULL && !this_oop->is_interface() && Klass::cast(super_klass)->should_be_initialized()) { + Klass::cast(super_klass)->initialize(THREAD); + + if (HAS_PENDING_EXCEPTION) { + Handle e(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + { + EXCEPTION_MARK; + this_oop->set_initialization_state_and_notify(initialization_error, THREAD); // Locks object, set state, and notify all waiting threads + CLEAR_PENDING_EXCEPTION; // ignore any exception thrown, superclass initialization error is thrown below + } + THROW_OOP(e()); + } + } + + // Step 8 + { + assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl"); + JavaThread* jt = (JavaThread*)THREAD; + // Timer includes any side effects of class initialization (resolution, + // etc), but not recursive entry into call_class_initializer(). + PerfTraceTimedEvent timer(ClassLoader::perf_class_init_time(), + ClassLoader::perf_classes_inited(), + jt->get_thread_stat()->class_init_recursion_count_addr()); + this_oop->call_class_initializer(THREAD); + } + + // Step 9 + if (!HAS_PENDING_EXCEPTION) { + this_oop->set_initialization_state_and_notify(fully_initialized, CHECK); + { ResourceMark rm(THREAD); + debug_only(this_oop->vtable()->verify(tty, true);) + } + } + else { + // Step 10 and 11 + Handle e(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + { + EXCEPTION_MARK; + this_oop->set_initialization_state_and_notify(initialization_error, THREAD); + CLEAR_PENDING_EXCEPTION; // ignore any exception thrown, class initialization error is thrown below + } + if (e->is_a(SystemDictionary::error_klass())) { + THROW_OOP(e()); + } else { + JavaCallArguments args(e); + THROW_ARG(vmSymbolHandles::java_lang_ExceptionInInitializerError(), + vmSymbolHandles::throwable_void_signature(), + &args); + } + } +} + + +// Note: implementation moved to static method to expose the this pointer. +void instanceKlass::set_initialization_state_and_notify(ClassState state, TRAPS) { + instanceKlassHandle kh(THREAD, this->as_klassOop()); + set_initialization_state_and_notify_impl(kh, state, CHECK); +} + +void instanceKlass::set_initialization_state_and_notify_impl(instanceKlassHandle this_oop, ClassState state, TRAPS) { + ObjectLocker ol(this_oop, THREAD); + this_oop->set_init_state(state); + ol.notify_all(CHECK); +} + +void instanceKlass::add_implementor(klassOop k) { + assert(Compile_lock->owned_by_self(), ""); + // Filter out my subinterfaces. + // (Note: Interfaces are never on the subklass list.) + if (instanceKlass::cast(k)->is_interface()) return; + + // Filter out subclasses whose supers already implement me. + // (Note: CHA must walk subclasses of direct implementors + // in order to locate indirect implementors.) + klassOop sk = instanceKlass::cast(k)->super(); + if (sk != NULL && instanceKlass::cast(sk)->implements_interface(as_klassOop())) + // We only need to check one immediate superclass, since the + // implements_interface query looks at transitive_interfaces. + // Any supers of the super have the same (or fewer) transitive_interfaces. + return; + + // Update number of implementors + int i = _nof_implementors++; + + // Record this implementor, if there are not too many already + if (i < implementors_limit) { + assert(_implementors[i] == NULL, "should be exactly one implementor"); + oop_store_without_check((oop*)&_implementors[i], k); + } else if (i == implementors_limit) { + // clear out the list on first overflow + for (int i2 = 0; i2 < implementors_limit; i2++) + oop_store_without_check((oop*)&_implementors[i2], NULL); + } + + // The implementor also implements the transitive_interfaces + for (int index = 0; index < local_interfaces()->length(); index++) { + instanceKlass::cast(klassOop(local_interfaces()->obj_at(index)))->add_implementor(k); + } +} + +void instanceKlass::init_implementor() { + for (int i = 0; i < implementors_limit; i++) + oop_store_without_check((oop*)&_implementors[i], NULL); + _nof_implementors = 0; +} + + +void instanceKlass::process_interfaces(Thread *thread) { + // link this class into the implementors list of every interface it implements + KlassHandle this_as_oop (thread, this->as_klassOop()); + for (int i = local_interfaces()->length() - 1; i >= 0; i--) { + assert(local_interfaces()->obj_at(i)->is_klass(), "must be a klass"); + instanceKlass* interf = instanceKlass::cast(klassOop(local_interfaces()->obj_at(i))); + assert(interf->is_interface(), "expected interface"); + interf->add_implementor(this_as_oop()); + } +} + +bool instanceKlass::can_be_primary_super_slow() const { + if (is_interface()) + return false; + else + return Klass::can_be_primary_super_slow(); +} + +objArrayOop instanceKlass::compute_secondary_supers(int num_extra_slots, TRAPS) { + // The secondaries are the implemented interfaces. + instanceKlass* ik = instanceKlass::cast(as_klassOop()); + objArrayHandle interfaces (THREAD, ik->transitive_interfaces()); + int num_secondaries = num_extra_slots + interfaces->length(); + if (num_secondaries == 0) { + return Universe::the_empty_system_obj_array(); + } else if (num_extra_slots == 0) { + return interfaces(); + } else { + // a mix of both + objArrayOop secondaries = oopFactory::new_system_objArray(num_secondaries, CHECK_NULL); + for (int i = 0; i < interfaces->length(); i++) { + secondaries->obj_at_put(num_extra_slots+i, interfaces->obj_at(i)); + } + return secondaries; + } +} + +bool instanceKlass::compute_is_subtype_of(klassOop k) { + if (Klass::cast(k)->is_interface()) { + return implements_interface(k); + } else { + return Klass::compute_is_subtype_of(k); + } +} + +bool instanceKlass::implements_interface(klassOop k) const { + if (as_klassOop() == k) return true; + assert(Klass::cast(k)->is_interface(), "should be an interface class"); + for (int i = 0; i < transitive_interfaces()->length(); i++) { + if (transitive_interfaces()->obj_at(i) == k) { + return true; + } + } + return false; +} + +objArrayOop instanceKlass::allocate_objArray(int n, int length, TRAPS) { + if (length < 0) THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); + if (length > arrayOopDesc::max_array_length(T_OBJECT)) { + THROW_OOP_0(Universe::out_of_memory_error_array_size()); + } + int size = objArrayOopDesc::object_size(length); + klassOop ak = array_klass(n, CHECK_NULL); + KlassHandle h_ak (THREAD, ak); + objArrayOop o = + (objArrayOop)CollectedHeap::array_allocate(h_ak, size, length, CHECK_NULL); + return o; +} + +instanceOop instanceKlass::register_finalizer(instanceOop i, TRAPS) { + if (TraceFinalizerRegistration) { + tty->print("Registered "); + i->print_value_on(tty); + tty->print_cr(" (" INTPTR_FORMAT ") as finalizable", (address)i); + } + instanceHandle h_i(THREAD, i); + // Pass the handle as argument, JavaCalls::call expects oop as jobjects + JavaValue result(T_VOID); + JavaCallArguments args(h_i); + methodHandle mh (THREAD, Universe::finalizer_register_method()); + JavaCalls::call(&result, mh, &args, CHECK_NULL); + return h_i(); +} + +instanceOop instanceKlass::allocate_instance(TRAPS) { + bool has_finalizer_flag = has_finalizer(); // Query before possible GC + int size = size_helper(); // Query before forming handle. + + KlassHandle h_k(THREAD, as_klassOop()); + + instanceOop i; + + i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL); + if (has_finalizer_flag && !RegisterFinalizersAtInit) { + i = register_finalizer(i, CHECK_NULL); + } + return i; +} + +instanceOop instanceKlass::allocate_permanent_instance(TRAPS) { + // Finalizer registration occurs in the Object. constructor + // and constructors normally aren't run when allocating perm + // instances so simply disallow finalizable perm objects. This can + // be relaxed if a need for it is found. + assert(!has_finalizer(), "perm objects not allowed to have finalizers"); + int size = size_helper(); // Query before forming handle. + KlassHandle h_k(THREAD, as_klassOop()); + instanceOop i = (instanceOop) + CollectedHeap::permanent_obj_allocate(h_k, size, CHECK_NULL); + return i; +} + +void instanceKlass::check_valid_for_instantiation(bool throwError, TRAPS) { + if (is_interface() || is_abstract()) { + ResourceMark rm(THREAD); + THROW_MSG(throwError ? vmSymbols::java_lang_InstantiationError() + : vmSymbols::java_lang_InstantiationException(), external_name()); + } + if (as_klassOop() == SystemDictionary::class_klass()) { + ResourceMark rm(THREAD); + THROW_MSG(throwError ? vmSymbols::java_lang_IllegalAccessError() + : vmSymbols::java_lang_IllegalAccessException(), external_name()); + } +} + +klassOop instanceKlass::array_klass_impl(bool or_null, int n, TRAPS) { + instanceKlassHandle this_oop(THREAD, as_klassOop()); + return array_klass_impl(this_oop, or_null, n, THREAD); +} + +klassOop instanceKlass::array_klass_impl(instanceKlassHandle this_oop, bool or_null, int n, TRAPS) { + if (this_oop->array_klasses() == NULL) { + if (or_null) return NULL; + + ResourceMark rm; + JavaThread *jt = (JavaThread *)THREAD; + { + // Atomic creation of array_klasses + MutexLocker mc(Compile_lock, THREAD); // for vtables + MutexLocker ma(MultiArray_lock, THREAD); + + // Check if update has already taken place + if (this_oop->array_klasses() == NULL) { + objArrayKlassKlass* oakk = + (objArrayKlassKlass*)Universe::objArrayKlassKlassObj()->klass_part(); + + klassOop k = oakk->allocate_objArray_klass(1, this_oop, CHECK_NULL); + this_oop->set_array_klasses(k); + } + } + } + // _this will always be set at this point + objArrayKlass* oak = (objArrayKlass*)this_oop->array_klasses()->klass_part(); + if (or_null) { + return oak->array_klass_or_null(n); + } + return oak->array_klass(n, CHECK_NULL); +} + +klassOop instanceKlass::array_klass_impl(bool or_null, TRAPS) { + return array_klass_impl(or_null, 1, THREAD); +} + +void instanceKlass::call_class_initializer(TRAPS) { + instanceKlassHandle ik (THREAD, as_klassOop()); + call_class_initializer_impl(ik, THREAD); +} + +static int call_class_initializer_impl_counter = 0; // for debugging + +methodOop instanceKlass::class_initializer() { + return find_method(vmSymbols::class_initializer_name(), vmSymbols::void_method_signature()); +} + +void instanceKlass::call_class_initializer_impl(instanceKlassHandle this_oop, TRAPS) { + methodHandle h_method(THREAD, this_oop->class_initializer()); + assert(!this_oop->is_initialized(), "we cannot initialize twice"); + if (TraceClassInitialization) { + tty->print("%d Initializing ", call_class_initializer_impl_counter++); + this_oop->name()->print_value(); + tty->print_cr("%s (" INTPTR_FORMAT ")", h_method() == NULL ? "(no method)" : "", (address)this_oop()); + } + if (h_method() != NULL) { + JavaCallArguments args; // No arguments + JavaValue result(T_VOID); + JavaCalls::call(&result, h_method, &args, CHECK); // Static call (no args) + } +} + + +void instanceKlass::mask_for(methodHandle method, int bci, + InterpreterOopMap* entry_for) { + // Dirty read, then double-check under a lock. + if (_oop_map_cache == NULL) { + // Otherwise, allocate a new one. + MutexLocker x(OopMapCacheAlloc_lock); + // First time use. Allocate a cache in C heap + if (_oop_map_cache == NULL) { + _oop_map_cache = new OopMapCache(); + } + } + // _oop_map_cache is constant after init; lookup below does is own locking. + _oop_map_cache->lookup(method, bci, entry_for); +} + + +bool instanceKlass::find_local_field(symbolOop name, symbolOop sig, fieldDescriptor* fd) const { + const int n = fields()->length(); + for (int i = 0; i < n; i += next_offset ) { + int name_index = fields()->ushort_at(i + name_index_offset); + int sig_index = fields()->ushort_at(i + signature_index_offset); + symbolOop f_name = constants()->symbol_at(name_index); + symbolOop f_sig = constants()->symbol_at(sig_index); + if (f_name == name && f_sig == sig) { + fd->initialize(as_klassOop(), i); + return true; + } + } + return false; +} + + +void instanceKlass::field_names_and_sigs_iterate(OopClosure* closure) { + const int n = fields()->length(); + for (int i = 0; i < n; i += next_offset ) { + int name_index = fields()->ushort_at(i + name_index_offset); + symbolOop name = constants()->symbol_at(name_index); + closure->do_oop((oop*)&name); + + int sig_index = fields()->ushort_at(i + signature_index_offset); + symbolOop sig = constants()->symbol_at(sig_index); + closure->do_oop((oop*)&sig); + } +} + + +klassOop instanceKlass::find_interface_field(symbolOop name, symbolOop sig, fieldDescriptor* fd) const { + const int n = local_interfaces()->length(); + for (int i = 0; i < n; i++) { + klassOop intf1 = klassOop(local_interfaces()->obj_at(i)); + assert(Klass::cast(intf1)->is_interface(), "just checking type"); + // search for field in current interface + if (instanceKlass::cast(intf1)->find_local_field(name, sig, fd)) { + assert(fd->is_static(), "interface field must be static"); + return intf1; + } + // search for field in direct superinterfaces + klassOop intf2 = instanceKlass::cast(intf1)->find_interface_field(name, sig, fd); + if (intf2 != NULL) return intf2; + } + // otherwise field lookup fails + return NULL; +} + + +klassOop instanceKlass::find_field(symbolOop name, symbolOop sig, fieldDescriptor* fd) const { + // search order according to newest JVM spec (5.4.3.2, p.167). + // 1) search for field in current klass + if (find_local_field(name, sig, fd)) { + return as_klassOop(); + } + // 2) search for field recursively in direct superinterfaces + { klassOop intf = find_interface_field(name, sig, fd); + if (intf != NULL) return intf; + } + // 3) apply field lookup recursively if superclass exists + { klassOop supr = super(); + if (supr != NULL) return instanceKlass::cast(supr)->find_field(name, sig, fd); + } + // 4) otherwise field lookup fails + return NULL; +} + + +klassOop instanceKlass::find_field(symbolOop name, symbolOop sig, bool is_static, fieldDescriptor* fd) const { + // search order according to newest JVM spec (5.4.3.2, p.167). + // 1) search for field in current klass + if (find_local_field(name, sig, fd)) { + if (fd->is_static() == is_static) return as_klassOop(); + } + // 2) search for field recursively in direct superinterfaces + if (is_static) { + klassOop intf = find_interface_field(name, sig, fd); + if (intf != NULL) return intf; + } + // 3) apply field lookup recursively if superclass exists + { klassOop supr = super(); + if (supr != NULL) return instanceKlass::cast(supr)->find_field(name, sig, is_static, fd); + } + // 4) otherwise field lookup fails + return NULL; +} + + +bool instanceKlass::find_local_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const { + int length = fields()->length(); + for (int i = 0; i < length; i += next_offset) { + if (offset_from_fields( i ) == offset) { + fd->initialize(as_klassOop(), i); + if (fd->is_static() == is_static) return true; + } + } + return false; +} + + +bool instanceKlass::find_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const { + klassOop klass = as_klassOop(); + while (klass != NULL) { + if (instanceKlass::cast(klass)->find_local_field_from_offset(offset, is_static, fd)) { + return true; + } + klass = Klass::cast(klass)->super(); + } + return false; +} + + +void instanceKlass::methods_do(void f(methodOop method)) { + int len = methods()->length(); + for (int index = 0; index < len; index++) { + methodOop m = methodOop(methods()->obj_at(index)); + assert(m->is_method(), "must be method"); + f(m); + } +} + +void instanceKlass::do_local_static_fields(FieldClosure* cl) { + fieldDescriptor fd; + int length = fields()->length(); + for (int i = 0; i < length; i += next_offset) { + fd.initialize(as_klassOop(), i); + if (fd.is_static()) cl->do_field(&fd); + } +} + + +void instanceKlass::do_local_static_fields(void f(fieldDescriptor*, TRAPS), TRAPS) { + instanceKlassHandle h_this(THREAD, as_klassOop()); + do_local_static_fields_impl(h_this, f, CHECK); +} + + +void instanceKlass::do_local_static_fields_impl(instanceKlassHandle this_oop, void f(fieldDescriptor* fd, TRAPS), TRAPS) { + fieldDescriptor fd; + int length = this_oop->fields()->length(); + for (int i = 0; i < length; i += next_offset) { + fd.initialize(this_oop(), i); + if (fd.is_static()) { f(&fd, CHECK); } // Do NOT remove {}! (CHECK macro expands into several statements) + } +} + + +void instanceKlass::do_nonstatic_fields(FieldClosure* cl) { + fieldDescriptor fd; + instanceKlass* super = superklass(); + if (super != NULL) { + super->do_nonstatic_fields(cl); + } + int length = fields()->length(); + for (int i = 0; i < length; i += next_offset) { + fd.initialize(as_klassOop(), i); + if (!(fd.is_static())) cl->do_field(&fd); + } +} + + +void instanceKlass::array_klasses_do(void f(klassOop k)) { + if (array_klasses() != NULL) + arrayKlass::cast(array_klasses())->array_klasses_do(f); +} + + +void instanceKlass::with_array_klasses_do(void f(klassOop k)) { + f(as_klassOop()); + array_klasses_do(f); +} + +#ifdef ASSERT +static int linear_search(objArrayOop methods, symbolOop name, symbolOop signature) { + int len = methods->length(); + for (int index = 0; index < len; index++) { + methodOop m = (methodOop)(methods->obj_at(index)); + assert(m->is_method(), "must be method"); + if (m->signature() == signature && m->name() == name) { + return index; + } + } + return -1; +} +#endif + +methodOop instanceKlass::find_method(symbolOop name, symbolOop signature) const { + return instanceKlass::find_method(methods(), name, signature); +} + +methodOop instanceKlass::find_method(objArrayOop methods, symbolOop name, symbolOop signature) { + int len = methods->length(); + // methods are sorted, so do binary search + int l = 0; + int h = len - 1; + while (l <= h) { + int mid = (l + h) >> 1; + methodOop m = (methodOop)methods->obj_at(mid); + assert(m->is_method(), "must be method"); + int res = m->name()->fast_compare(name); + if (res == 0) { + // found matching name; do linear search to find matching signature + // first, quick check for common case + if (m->signature() == signature) return m; + // search downwards through overloaded methods + int i; + for (i = mid - 1; i >= l; i--) { + methodOop m = (methodOop)methods->obj_at(i); + assert(m->is_method(), "must be method"); + if (m->name() != name) break; + if (m->signature() == signature) return m; + } + // search upwards + for (i = mid + 1; i <= h; i++) { + methodOop m = (methodOop)methods->obj_at(i); + assert(m->is_method(), "must be method"); + if (m->name() != name) break; + if (m->signature() == signature) return m; + } + // not found +#ifdef ASSERT + int index = linear_search(methods, name, signature); + if (index != -1) fatal1("binary search bug: should have found entry %d", index); +#endif + return NULL; + } else if (res < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } +#ifdef ASSERT + int index = linear_search(methods, name, signature); + if (index != -1) fatal1("binary search bug: should have found entry %d", index); +#endif + return NULL; +} + +methodOop instanceKlass::uncached_lookup_method(symbolOop name, symbolOop signature) const { + klassOop klass = as_klassOop(); + while (klass != NULL) { + methodOop method = instanceKlass::cast(klass)->find_method(name, signature); + if (method != NULL) return method; + klass = instanceKlass::cast(klass)->super(); + } + return NULL; +} + +// lookup a method in all the interfaces that this class implements +methodOop instanceKlass::lookup_method_in_all_interfaces(symbolOop name, + symbolOop signature) const { + objArrayOop all_ifs = instanceKlass::cast(as_klassOop())->transitive_interfaces(); + int num_ifs = all_ifs->length(); + instanceKlass *ik = NULL; + for (int i = 0; i < num_ifs; i++) { + ik = instanceKlass::cast(klassOop(all_ifs->obj_at(i))); + methodOop m = ik->lookup_method(name, signature); + if (m != NULL) { + return m; + } + } + return NULL; +} + +/* jni_id_for_impl for jfieldIds only */ +JNIid* instanceKlass::jni_id_for_impl(instanceKlassHandle this_oop, int offset) { + MutexLocker ml(JfieldIdCreation_lock); + // Retry lookup after we got the lock + JNIid* probe = this_oop->jni_ids() == NULL ? NULL : this_oop->jni_ids()->find(offset); + if (probe == NULL) { + // Slow case, allocate new static field identifier + probe = new JNIid(this_oop->as_klassOop(), offset, this_oop->jni_ids()); + this_oop->set_jni_ids(probe); + } + return probe; +} + + +/* jni_id_for for jfieldIds only */ +JNIid* instanceKlass::jni_id_for(int offset) { + JNIid* probe = jni_ids() == NULL ? NULL : jni_ids()->find(offset); + if (probe == NULL) { + probe = jni_id_for_impl(this->as_klassOop(), offset); + } + return probe; +} + + +// Lookup or create a jmethodID. +// This code can be called by the VM thread. For this reason it is critical that +// there are no blocking operations (safepoints) while the lock is held -- or a +// deadlock can occur. +jmethodID instanceKlass::jmethod_id_for_impl(instanceKlassHandle ik_h, methodHandle method_h) { + size_t idnum = (size_t)method_h->method_idnum(); + jmethodID* jmeths = ik_h->methods_jmethod_ids_acquire(); + size_t length = 0; + jmethodID id = NULL; + // array length stored in first element, other elements offset by one + if (jmeths == NULL || // If there is no jmethodID array, + (length = (size_t)jmeths[0]) <= idnum || // or if it is too short, + (id = jmeths[idnum+1]) == NULL) { // or if this jmethodID isn't allocated + + // Do all the safepointing things (allocations) before grabbing the lock. + // These allocations will have to be freed if they are unused. + + // Allocate a new array of methods. + jmethodID* to_dealloc_jmeths = NULL; + jmethodID* new_jmeths = NULL; + if (length <= idnum) { + // A new array will be needed (unless some other thread beats us to it) + size_t size = MAX2(idnum+1, (size_t)ik_h->idnum_allocated_count()); + new_jmeths = NEW_C_HEAP_ARRAY(jmethodID, size+1); + memset(new_jmeths, 0, (size+1)*sizeof(jmethodID)); + new_jmeths[0] =(jmethodID)size; // array size held in the first element + } + + // Allocate a new method ID. + jmethodID to_dealloc_id = NULL; + jmethodID new_id = NULL; + if (method_h->is_old() && !method_h->is_obsolete()) { + // The method passed in is old (but not obsolete), we need to use the current version + methodOop current_method = ik_h->method_with_idnum((int)idnum); + assert(current_method != NULL, "old and but not obsolete, so should exist"); + methodHandle current_method_h(current_method == NULL? method_h() : current_method); + new_id = JNIHandles::make_jmethod_id(current_method_h); + } else { + // It is the current version of the method or an obsolete method, + // use the version passed in + new_id = JNIHandles::make_jmethod_id(method_h); + } + + { + MutexLocker ml(JmethodIdCreation_lock); + + // We must not go to a safepoint while holding this lock. + debug_only(No_Safepoint_Verifier nosafepoints;) + + // Retry lookup after we got the lock + jmeths = ik_h->methods_jmethod_ids_acquire(); + if (jmeths == NULL || (length = (size_t)jmeths[0]) <= idnum) { + if (jmeths != NULL) { + // We have grown the array: copy the existing entries, and delete the old array + for (size_t index = 0; index < length; index++) { + new_jmeths[index+1] = jmeths[index+1]; + } + to_dealloc_jmeths = jmeths; // using the new jmeths, deallocate the old one + } + ik_h->release_set_methods_jmethod_ids(jmeths = new_jmeths); + } else { + id = jmeths[idnum+1]; + to_dealloc_jmeths = new_jmeths; // using the old jmeths, deallocate the new one + } + if (id == NULL) { + id = new_id; + jmeths[idnum+1] = id; // install the new method ID + } else { + to_dealloc_id = new_id; // the new id wasn't used, mark it for deallocation + } + } + + // Free up unneeded or no longer needed resources + FreeHeap(to_dealloc_jmeths); + if (to_dealloc_id != NULL) { + JNIHandles::destroy_jmethod_id(to_dealloc_id); + } + } + return id; +} + + +// Lookup a jmethodID, NULL if not found. Do no blocking, no allocations, no handles +jmethodID instanceKlass::jmethod_id_or_null(methodOop method) { + size_t idnum = (size_t)method->method_idnum(); + jmethodID* jmeths = methods_jmethod_ids_acquire(); + size_t length; // length assigned as debugging crumb + jmethodID id = NULL; + if (jmeths != NULL && // If there is a jmethodID array, + (length = (size_t)jmeths[0]) > idnum) { // and if it is long enough, + id = jmeths[idnum+1]; // Look up the id (may be NULL) + } + return id; +} + + +// Cache an itable index +void instanceKlass::set_cached_itable_index(size_t idnum, int index) { + int* indices = methods_cached_itable_indices_acquire(); + if (indices == NULL || // If there is no index array, + ((size_t)indices[0]) <= idnum) { // or if it is too short + // Lock before we allocate the array so we don't leak + MutexLocker ml(JNICachedItableIndex_lock); + // Retry lookup after we got the lock + indices = methods_cached_itable_indices_acquire(); + size_t length = 0; + // array length stored in first element, other elements offset by one + if (indices == NULL || (length = (size_t)indices[0]) <= idnum) { + size_t size = MAX2(idnum+1, (size_t)idnum_allocated_count()); + int* new_indices = NEW_C_HEAP_ARRAY(int, size+1); + // Copy the existing entries, if any + size_t i; + for (i = 0; i < length; i++) { + new_indices[i+1] = indices[i+1]; + } + // Set all the rest to -1 + for (i = length; i < size; i++) { + new_indices[i+1] = -1; + } + if (indices != NULL) { + FreeHeap(indices); // delete any old indices + } + release_set_methods_cached_itable_indices(indices = new_indices); + } + } else { + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } + // This is a cache, if there is a race to set it, it doesn't matter + indices[idnum+1] = index; +} + + +// Retrieve a cached itable index +int instanceKlass::cached_itable_index(size_t idnum) { + int* indices = methods_cached_itable_indices_acquire(); + if (indices != NULL && ((size_t)indices[0]) > idnum) { + // indices exist and are long enough, retrieve possible cached + return indices[idnum+1]; + } + return -1; +} + + +// +// nmethodBucket is used to record dependent nmethods for +// deoptimization. nmethod dependencies are actually +// pairs but we really only care about the klass part for purposes of +// finding nmethods which might need to be deoptimized. Instead of +// recording the method, a count of how many times a particular nmethod +// was recorded is kept. This ensures that any recording errors are +// noticed since an nmethod should be removed as many times are it's +// added. +// +class nmethodBucket { + private: + nmethod* _nmethod; + int _count; + nmethodBucket* _next; + + public: + nmethodBucket(nmethod* nmethod, nmethodBucket* next) { + _nmethod = nmethod; + _next = next; + _count = 1; + } + int count() { return _count; } + int increment() { _count += 1; return _count; } + int decrement() { _count -= 1; assert(_count >= 0, "don't underflow"); return _count; } + nmethodBucket* next() { return _next; } + void set_next(nmethodBucket* b) { _next = b; } + nmethod* get_nmethod() { return _nmethod; } +}; + + +// +// Walk the list of dependent nmethods searching for nmethods which +// are dependent on the klassOop that was passed in and mark them for +// deoptimization. Returns the number of nmethods found. +// +int instanceKlass::mark_dependent_nmethods(DepChange& changes) { + assert_locked_or_safepoint(CodeCache_lock); + int found = 0; + nmethodBucket* b = _dependencies; + while (b != NULL) { + nmethod* nm = b->get_nmethod(); + // since dependencies aren't removed until an nmethod becomes a zombie, + // the dependency list may contain nmethods which aren't alive. + if (nm->is_alive() && !nm->is_marked_for_deoptimization() && nm->check_dependency_on(changes)) { + if (TraceDependencies) { + ResourceMark rm; + tty->print_cr("Marked for deoptimization"); + tty->print_cr(" context = %s", this->external_name()); + changes.print(); + nm->print(); + nm->print_dependencies(); + } + nm->mark_for_deoptimization(); + found++; + } + b = b->next(); + } + return found; +} + + +// +// Add an nmethodBucket to the list of dependencies for this nmethod. +// It's possible that an nmethod has multiple dependencies on this klass +// so a count is kept for each bucket to guarantee that creation and +// deletion of dependencies is consistent. +// +void instanceKlass::add_dependent_nmethod(nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + nmethodBucket* b = _dependencies; + nmethodBucket* last = NULL; + while (b != NULL) { + if (nm == b->get_nmethod()) { + b->increment(); + return; + } + b = b->next(); + } + _dependencies = new nmethodBucket(nm, _dependencies); +} + + +// +// Decrement count of the nmethod in the dependency list and remove +// the bucket competely when the count goes to 0. This method must +// find a corresponding bucket otherwise there's a bug in the +// recording of dependecies. +// +void instanceKlass::remove_dependent_nmethod(nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + nmethodBucket* b = _dependencies; + nmethodBucket* last = NULL; + while (b != NULL) { + if (nm == b->get_nmethod()) { + if (b->decrement() == 0) { + if (last == NULL) { + _dependencies = b->next(); + } else { + last->set_next(b->next()); + } + delete b; + } + return; + } + last = b; + b = b->next(); + } +#ifdef ASSERT + tty->print_cr("### %s can't find dependent nmethod:", this->external_name()); + nm->print(); +#endif // ASSERT + ShouldNotReachHere(); +} + + +#ifndef PRODUCT +void instanceKlass::print_dependent_nmethods(bool verbose) { + nmethodBucket* b = _dependencies; + int idx = 0; + while (b != NULL) { + nmethod* nm = b->get_nmethod(); + tty->print("[%d] count=%d { ", idx++, b->count()); + if (!verbose) { + nm->print_on(tty, "nmethod"); + tty->print_cr(" } "); + } else { + nm->print(); + nm->print_dependencies(); + tty->print_cr("--- } "); + } + b = b->next(); + } +} + + +bool instanceKlass::is_dependent_nmethod(nmethod* nm) { + nmethodBucket* b = _dependencies; + while (b != NULL) { + if (nm == b->get_nmethod()) { + return true; + } + b = b->next(); + } + return false; +} +#endif //PRODUCT + + +void instanceKlass::follow_static_fields() { + oop* start = start_of_static_fields(); + oop* end = start + static_oop_field_size(); + while (start < end) { + if (*start != NULL) { + assert(Universe::heap()->is_in_closed_subset(*start), + "should be in heap"); + MarkSweep::mark_and_push(start); + } + start++; + } +} + +#ifndef SERIALGC +void instanceKlass::follow_static_fields(ParCompactionManager* cm) { + oop* start = start_of_static_fields(); + oop* end = start + static_oop_field_size(); + while (start < end) { + if (*start != NULL) { + assert(Universe::heap()->is_in(*start), "should be in heap"); + PSParallelCompact::mark_and_push(cm, start); + } + start++; + } +} +#endif // SERIALGC + + +void instanceKlass::adjust_static_fields() { + oop* start = start_of_static_fields(); + oop* end = start + static_oop_field_size(); + while (start < end) { + MarkSweep::adjust_pointer(start); + start++; + } +} + +#ifndef SERIALGC +void instanceKlass::update_static_fields() { + oop* const start = start_of_static_fields(); + oop* const beg_oop = start; + oop* const end_oop = start + static_oop_field_size(); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } +} + +void +instanceKlass::update_static_fields(HeapWord* beg_addr, HeapWord* end_addr) { + oop* const start = start_of_static_fields(); + oop* const beg_oop = MAX2((oop*)beg_addr, start); + oop* const end_oop = MIN2((oop*)end_addr, start + static_oop_field_size()); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } +} +#endif // SERIALGC + +void instanceKlass::oop_follow_contents(oop obj) { + assert (obj!=NULL, "can't follow the content of NULL object"); + obj->follow_header(); + OopMapBlock* map = start_of_nonstatic_oop_maps(); + OopMapBlock* end_map = map + nonstatic_oop_map_size(); + while (map < end_map) { + oop* start = obj->obj_field_addr(map->offset()); + oop* end = start + map->length(); + while (start < end) { + if (*start != NULL) { + assert(Universe::heap()->is_in_closed_subset(*start), + "should be in heap"); + MarkSweep::mark_and_push(start); + } + start++; + } + map++; + } +} + +#ifndef SERIALGC +void instanceKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert (obj!=NULL, "can't follow the content of NULL object"); + obj->follow_header(cm); + OopMapBlock* map = start_of_nonstatic_oop_maps(); + OopMapBlock* end_map = map + nonstatic_oop_map_size(); + while (map < end_map) { + oop* start = obj->obj_field_addr(map->offset()); + oop* end = start + map->length(); + while (start < end) { + if (*start != NULL) { + assert(Universe::heap()->is_in(*start), "should be in heap"); + PSParallelCompact::mark_and_push(cm, start); + } + start++; + } + map++; + } +} +#endif // SERIALGC + +#define invoke_closure_on(start, closure, nv_suffix) { \ + oop obj = *(start); \ + if (obj != NULL) { \ + assert(Universe::heap()->is_in_closed_subset(obj), "should be in heap"); \ + (closure)->do_oop##nv_suffix(start); \ + } \ +} + +// closure's do_header() method dicates whether the given closure should be +// applied to the klass ptr in the object header. + +#define InstanceKlass_OOP_OOP_ITERATE_DEFN(OopClosureType, nv_suffix) \ + \ +int instanceKlass::oop_oop_iterate##nv_suffix(oop obj, \ + OopClosureType* closure) { \ + SpecializationStats::record_iterate_call##nv_suffix(SpecializationStats::ik); \ + /* header */ \ + if (closure->do_header()) { \ + obj->oop_iterate_header(closure); \ + } \ + /* instance variables */ \ + OopMapBlock* map = start_of_nonstatic_oop_maps(); \ + OopMapBlock* const end_map = map + nonstatic_oop_map_size(); \ + const intx field_offset = PrefetchFieldsAhead; \ + if (field_offset > 0) { \ + while (map < end_map) { \ + oop* start = obj->obj_field_addr(map->offset()); \ + oop* const end = start + map->length(); \ + while (start < end) { \ + prefetch_beyond(start, (oop*)end, field_offset, \ + closure->prefetch_style()); \ + SpecializationStats:: \ + record_do_oop_call##nv_suffix(SpecializationStats::ik); \ + invoke_closure_on(start, closure, nv_suffix); \ + start++; \ + } \ + map++; \ + } \ + } else { \ + while (map < end_map) { \ + oop* start = obj->obj_field_addr(map->offset()); \ + oop* const end = start + map->length(); \ + while (start < end) { \ + SpecializationStats:: \ + record_do_oop_call##nv_suffix(SpecializationStats::ik); \ + invoke_closure_on(start, closure, nv_suffix); \ + start++; \ + } \ + map++; \ + } \ + } \ + return size_helper(); \ +} + +#define InstanceKlass_OOP_OOP_ITERATE_DEFN_m(OopClosureType, nv_suffix) \ + \ +int instanceKlass::oop_oop_iterate##nv_suffix##_m(oop obj, \ + OopClosureType* closure, \ + MemRegion mr) { \ + SpecializationStats::record_iterate_call##nv_suffix(SpecializationStats::ik); \ + /* header */ \ + if (closure->do_header()) { \ + obj->oop_iterate_header(closure, mr); \ + } \ + /* instance variables */ \ + OopMapBlock* map = start_of_nonstatic_oop_maps(); \ + OopMapBlock* const end_map = map + nonstatic_oop_map_size(); \ + HeapWord* bot = mr.start(); \ + HeapWord* top = mr.end(); \ + oop* start = obj->obj_field_addr(map->offset()); \ + HeapWord* end = MIN2((HeapWord*)(start + map->length()), top); \ + /* Find the first map entry that extends onto mr. */ \ + while (map < end_map && end <= bot) { \ + map++; \ + start = obj->obj_field_addr(map->offset()); \ + end = MIN2((HeapWord*)(start + map->length()), top); \ + } \ + if (map != end_map) { \ + /* The current map's end is past the start of "mr". Skip up to the first \ + entry on "mr". */ \ + while ((HeapWord*)start < bot) { \ + start++; \ + } \ + const intx field_offset = PrefetchFieldsAhead; \ + for (;;) { \ + if (field_offset > 0) { \ + while ((HeapWord*)start < end) { \ + prefetch_beyond(start, (oop*)end, field_offset, \ + closure->prefetch_style()); \ + invoke_closure_on(start, closure, nv_suffix); \ + start++; \ + } \ + } else { \ + while ((HeapWord*)start < end) { \ + invoke_closure_on(start, closure, nv_suffix); \ + start++; \ + } \ + } \ + /* Go to the next map. */ \ + map++; \ + if (map == end_map) { \ + break; \ + } \ + /* Otherwise, */ \ + start = obj->obj_field_addr(map->offset()); \ + if ((HeapWord*)start >= top) { \ + break; \ + } \ + end = MIN2((HeapWord*)(start + map->length()), top); \ + } \ + } \ + return size_helper(); \ +} + +ALL_OOP_OOP_ITERATE_CLOSURES_1(InstanceKlass_OOP_OOP_ITERATE_DEFN) +ALL_OOP_OOP_ITERATE_CLOSURES_3(InstanceKlass_OOP_OOP_ITERATE_DEFN) +ALL_OOP_OOP_ITERATE_CLOSURES_1(InstanceKlass_OOP_OOP_ITERATE_DEFN_m) +ALL_OOP_OOP_ITERATE_CLOSURES_3(InstanceKlass_OOP_OOP_ITERATE_DEFN_m) + + +void instanceKlass::iterate_static_fields(OopClosure* closure) { + oop* start = start_of_static_fields(); + oop* end = start + static_oop_field_size(); + while (start < end) { + assert(Universe::heap()->is_in_reserved_or_null(*start), "should be in heap"); + closure->do_oop(start); + start++; + } +} + +void instanceKlass::iterate_static_fields(OopClosure* closure, + MemRegion mr) { + oop* start = start_of_static_fields(); + oop* end = start + static_oop_field_size(); + // I gather that the the static fields of reference types come first, + // hence the name of "oop_field_size", and that is what makes this safe. + assert((intptr_t)mr.start() == + align_size_up((intptr_t)mr.start(), sizeof(oop)) && + (intptr_t)mr.end() == align_size_up((intptr_t)mr.end(), sizeof(oop)), + "Memregion must be oop-aligned."); + if ((HeapWord*)start < mr.start()) start = (oop*)mr.start(); + if ((HeapWord*)end > mr.end()) end = (oop*)mr.end(); + while (start < end) { + invoke_closure_on(start, closure,_v); + start++; + } +} + + +int instanceKlass::oop_adjust_pointers(oop obj) { + int size = size_helper(); + + // Compute oopmap block range. The common case is nonstatic_oop_map_size == 1. + OopMapBlock* map = start_of_nonstatic_oop_maps(); + OopMapBlock* const end_map = map + nonstatic_oop_map_size(); + // Iterate over oopmap blocks + while (map < end_map) { + // Compute oop range for this block + oop* start = obj->obj_field_addr(map->offset()); + oop* end = start + map->length(); + // Iterate over oops + while (start < end) { + assert(Universe::heap()->is_in_or_null(*start), "should be in heap"); + MarkSweep::adjust_pointer(start); + start++; + } + map++; + } + + obj->adjust_header(); + return size; +} + +#ifndef SERIALGC +void instanceKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(!pm->depth_first(), "invariant"); + // Compute oopmap block range. The common case is nonstatic_oop_map_size == 1. + OopMapBlock* start_map = start_of_nonstatic_oop_maps(); + OopMapBlock* map = start_map + nonstatic_oop_map_size(); + + // Iterate over oopmap blocks + while (start_map < map) { + --map; + // Compute oop range for this block + oop* start = obj->obj_field_addr(map->offset()); + oop* curr = start + map->length(); + // Iterate over oops + while (start < curr) { + --curr; + if (PSScavenge::should_scavenge(*curr)) { + assert(Universe::heap()->is_in(*curr), "should be in heap"); + pm->claim_or_forward_breadth(curr); + } + } + } +} + +void instanceKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(pm->depth_first(), "invariant"); + // Compute oopmap block range. The common case is nonstatic_oop_map_size == 1. + OopMapBlock* start_map = start_of_nonstatic_oop_maps(); + OopMapBlock* map = start_map + nonstatic_oop_map_size(); + + // Iterate over oopmap blocks + while (start_map < map) { + --map; + // Compute oop range for this block + oop* start = obj->obj_field_addr(map->offset()); + oop* curr = start + map->length(); + // Iterate over oops + while (start < curr) { + --curr; + if (PSScavenge::should_scavenge(*curr)) { + assert(Universe::heap()->is_in(*curr), "should be in heap"); + pm->claim_or_forward_depth(curr); + } + } + } +} + +int instanceKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + // Compute oopmap block range. The common case is nonstatic_oop_map_size==1. + OopMapBlock* map = start_of_nonstatic_oop_maps(); + OopMapBlock* const end_map = map + nonstatic_oop_map_size(); + // Iterate over oopmap blocks + while (map < end_map) { + // Compute oop range for this oopmap block. + oop* const map_start = obj->obj_field_addr(map->offset()); + oop* const beg_oop = map_start; + oop* const end_oop = map_start + map->length(); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } + ++map; + } + + return size_helper(); +} + +int instanceKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, HeapWord* end_addr) { + // Compute oopmap block range. The common case is nonstatic_oop_map_size==1. + OopMapBlock* map = start_of_nonstatic_oop_maps(); + OopMapBlock* const end_map = map + nonstatic_oop_map_size(); + // Iterate over oopmap blocks + while (map < end_map) { + // Compute oop range for this oopmap block. + oop* const map_start = obj->obj_field_addr(map->offset()); + oop* const beg_oop = MAX2((oop*)beg_addr, map_start); + oop* const end_oop = MIN2((oop*)end_addr, map_start + map->length()); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } + ++map; + } + + return size_helper(); +} + +void instanceKlass::copy_static_fields(PSPromotionManager* pm) { + assert(!pm->depth_first(), "invariant"); + // Compute oop range + oop* start = start_of_static_fields(); + oop* end = start + static_oop_field_size(); + // Iterate over oops + while (start < end) { + if (PSScavenge::should_scavenge(*start)) { + assert(Universe::heap()->is_in(*start), "should be in heap"); + pm->claim_or_forward_breadth(start); + } + start++; + } +} + +void instanceKlass::push_static_fields(PSPromotionManager* pm) { + assert(pm->depth_first(), "invariant"); + // Compute oop range + oop* start = start_of_static_fields(); + oop* end = start + static_oop_field_size(); + // Iterate over oops + while (start < end) { + if (PSScavenge::should_scavenge(*start)) { + assert(Universe::heap()->is_in(*start), "should be in heap"); + pm->claim_or_forward_depth(start); + } + start++; + } +} + +void instanceKlass::copy_static_fields(ParCompactionManager* cm) { + // Compute oop range + oop* start = start_of_static_fields(); + oop* end = start + static_oop_field_size(); + // Iterate over oops + while (start < end) { + if (*start != NULL) { + assert(Universe::heap()->is_in(*start), "should be in heap"); + // *start = (oop) cm->summary_data()->calc_new_pointer(*start); + PSParallelCompact::adjust_pointer(start); + } + start++; + } +} +#endif // SERIALGC + +// This klass is alive but the implementor link is not followed/updated. +// Subklass and sibling links are handled by Klass::follow_weak_klass_links + +void instanceKlass::follow_weak_klass_links( + BoolObjectClosure* is_alive, OopClosure* keep_alive) { + assert(is_alive->do_object_b(as_klassOop()), "this oop should be live"); + if (ClassUnloading) { + for (int i = 0; i < implementors_limit; i++) { + klassOop impl = _implementors[i]; + if (impl == NULL) break; // no more in the list + if (!is_alive->do_object_b(impl)) { + // remove this guy from the list by overwriting him with the tail + int lasti = --_nof_implementors; + assert(lasti >= i && lasti < implementors_limit, "just checking"); + _implementors[i] = _implementors[lasti]; + _implementors[lasti] = NULL; + --i; // rerun the loop at this index + } + } + } else { + for (int i = 0; i < implementors_limit; i++) { + keep_alive->do_oop(&adr_implementors()[i]); + } + } + Klass::follow_weak_klass_links(is_alive, keep_alive); +} + + +void instanceKlass::remove_unshareable_info() { + Klass::remove_unshareable_info(); + init_implementor(); +} + + +static void clear_all_breakpoints(methodOop m) { + m->clear_all_breakpoints(); +} + + +void instanceKlass::release_C_heap_structures() { + // Deallocate oop map cache + if (_oop_map_cache != NULL) { + delete _oop_map_cache; + _oop_map_cache = NULL; + } + + // Deallocate JNI identifiers for jfieldIDs + JNIid::deallocate(jni_ids()); + set_jni_ids(NULL); + + jmethodID* jmeths = methods_jmethod_ids_acquire(); + if (jmeths != (jmethodID*)NULL) { + release_set_methods_jmethod_ids(NULL); + FreeHeap(jmeths); + } + + int* indices = methods_cached_itable_indices_acquire(); + if (indices != (int*)NULL) { + release_set_methods_cached_itable_indices(NULL); + FreeHeap(indices); + } + + // release dependencies + nmethodBucket* b = _dependencies; + _dependencies = NULL; + while (b != NULL) { + nmethodBucket* next = b->next(); + delete b; + b = next; + } + + // Deallocate breakpoint records + if (breakpoints() != 0x0) { + methods_do(clear_all_breakpoints); + assert(breakpoints() == 0x0, "should have cleared breakpoints"); + } + + // deallocate information about previous versions + if (_previous_versions != NULL) { + for (int i = _previous_versions->length() - 1; i >= 0; i--) { + PreviousVersionNode * pv_node = _previous_versions->at(i); + delete pv_node; + } + delete _previous_versions; + _previous_versions = NULL; + } + + // deallocate the cached class file + if (_cached_class_file_bytes != NULL) { + os::free(_cached_class_file_bytes); + _cached_class_file_bytes = NULL; + _cached_class_file_len = 0; + } +} + +char* instanceKlass::signature_name() const { + const char* src = (const char*) (name()->as_C_string()); + const int src_length = (int)strlen(src); + char* dest = NEW_RESOURCE_ARRAY(char, src_length + 3); + int src_index = 0; + int dest_index = 0; + dest[dest_index++] = 'L'; + while (src_index < src_length) { + dest[dest_index++] = src[src_index++]; + } + dest[dest_index++] = ';'; + dest[dest_index] = '\0'; + return dest; +} + +// different verisons of is_same_class_package +bool instanceKlass::is_same_class_package(klassOop class2) { + klassOop class1 = as_klassOop(); + oop classloader1 = instanceKlass::cast(class1)->class_loader(); + symbolOop classname1 = Klass::cast(class1)->name(); + + if (Klass::cast(class2)->oop_is_objArray()) { + class2 = objArrayKlass::cast(class2)->bottom_klass(); + } + oop classloader2; + if (Klass::cast(class2)->oop_is_instance()) { + classloader2 = instanceKlass::cast(class2)->class_loader(); + } else { + assert(Klass::cast(class2)->oop_is_typeArray(), "should be type array"); + classloader2 = NULL; + } + symbolOop classname2 = Klass::cast(class2)->name(); + + return instanceKlass::is_same_class_package(classloader1, classname1, + classloader2, classname2); +} + +bool instanceKlass::is_same_class_package(oop classloader2, symbolOop classname2) { + klassOop class1 = as_klassOop(); + oop classloader1 = instanceKlass::cast(class1)->class_loader(); + symbolOop classname1 = Klass::cast(class1)->name(); + + return instanceKlass::is_same_class_package(classloader1, classname1, + classloader2, classname2); +} + +// return true if two classes are in the same package, classloader +// and classname information is enough to determine a class's package +bool instanceKlass::is_same_class_package(oop class_loader1, symbolOop class_name1, + oop class_loader2, symbolOop class_name2) { + if (class_loader1 != class_loader2) { + return false; + } else { + ResourceMark rm; + + // The symbolOop's are in UTF8 encoding. Since we only need to check explicitly + // for ASCII characters ('/', 'L', '['), we can keep them in UTF8 encoding. + // Otherwise, we just compare jbyte values between the strings. + jbyte *name1 = class_name1->base(); + jbyte *name2 = class_name2->base(); + + jbyte *last_slash1 = UTF8::strrchr(name1, class_name1->utf8_length(), '/'); + jbyte *last_slash2 = UTF8::strrchr(name2, class_name2->utf8_length(), '/'); + + if ((last_slash1 == NULL) || (last_slash2 == NULL)) { + // One of the two doesn't have a package. Only return true + // if the other one also doesn't have a package. + return last_slash1 == last_slash2; + } else { + // Skip over '['s + if (*name1 == '[') { + do { + name1++; + } while (*name1 == '['); + if (*name1 != 'L') { + // Something is terribly wrong. Shouldn't be here. + return false; + } + } + if (*name2 == '[') { + do { + name2++; + } while (*name2 == '['); + if (*name2 != 'L') { + // Something is terribly wrong. Shouldn't be here. + return false; + } + } + + // Check that package part is identical + int length1 = last_slash1 - name1; + int length2 = last_slash2 - name2; + + return UTF8::equal(name1, length1, name2, length2); + } + } +} + + +jint instanceKlass::compute_modifier_flags(TRAPS) const { + klassOop k = as_klassOop(); + jint access = access_flags().as_int(); + + // But check if it happens to be member class. + typeArrayOop inner_class_list = inner_classes(); + int length = (inner_class_list == NULL) ? 0 : inner_class_list->length(); + assert (length % instanceKlass::inner_class_next_offset == 0, "just checking"); + if (length > 0) { + typeArrayHandle inner_class_list_h(THREAD, inner_class_list); + instanceKlassHandle ik(THREAD, k); + for (int i = 0; i < length; i += instanceKlass::inner_class_next_offset) { + int ioff = inner_class_list_h->ushort_at( + i + instanceKlass::inner_class_inner_class_info_offset); + + // Inner class attribute can be zero, skip it. + // Strange but true: JVM spec. allows null inner class refs. + if (ioff == 0) continue; + + // only look at classes that are already loaded + // since we are looking for the flags for our self. + symbolOop inner_name = ik->constants()->klass_name_at(ioff); + if ((ik->name() == inner_name)) { + // This is really a member class. + access = inner_class_list_h->ushort_at(i + instanceKlass::inner_class_access_flags_offset); + break; + } + } + } + // Remember to strip ACC_SUPER bit + return (access & (~JVM_ACC_SUPER)) & JVM_ACC_WRITTEN_FLAGS; +} + +jint instanceKlass::jvmti_class_status() const { + jint result = 0; + + if (is_linked()) { + result |= JVMTI_CLASS_STATUS_VERIFIED | JVMTI_CLASS_STATUS_PREPARED; + } + + if (is_initialized()) { + assert(is_linked(), "Class status is not consistent"); + result |= JVMTI_CLASS_STATUS_INITIALIZED; + } + if (is_in_error_state()) { + result |= JVMTI_CLASS_STATUS_ERROR; + } + return result; +} + +methodOop instanceKlass::method_at_itable(klassOop holder, int index, TRAPS) { + itableOffsetEntry* ioe = (itableOffsetEntry*)start_of_itable(); + int method_table_offset_in_words = ioe->offset()/wordSize; + int nof_interfaces = (method_table_offset_in_words - itable_offset_in_words()) + / itableOffsetEntry::size(); + + for (int cnt = 0 ; ; cnt ++, ioe ++) { + // If the interface isn't implemented by the reciever class, + // the VM should throw IncompatibleClassChangeError. + if (cnt >= nof_interfaces) { + THROW_OOP_0(vmSymbols::java_lang_IncompatibleClassChangeError()); + } + + klassOop ik = ioe->interface_klass(); + if (ik == holder) break; + } + + itableMethodEntry* ime = ioe->first_method_entry(as_klassOop()); + methodOop m = ime[index].method(); + if (m == NULL) { + THROW_OOP_0(vmSymbols::java_lang_AbstractMethodError()); + } + return m; +} + +// On-stack replacement stuff +void instanceKlass::add_osr_nmethod(nmethod* n) { + // only one compilation can be active + NEEDS_CLEANUP + // This is a short non-blocking critical region, so the no safepoint check is ok. + OsrList_lock->lock_without_safepoint_check(); + assert(n->is_osr_method(), "wrong kind of nmethod"); + n->set_link(osr_nmethods_head()); + set_osr_nmethods_head(n); + // Remember to unlock again + OsrList_lock->unlock(); +} + + +void instanceKlass::remove_osr_nmethod(nmethod* n) { + // This is a short non-blocking critical region, so the no safepoint check is ok. + OsrList_lock->lock_without_safepoint_check(); + assert(n->is_osr_method(), "wrong kind of nmethod"); + nmethod* last = NULL; + nmethod* cur = osr_nmethods_head(); + // Search for match + while(cur != NULL && cur != n) { + last = cur; + cur = cur->link(); + } + if (cur == n) { + if (last == NULL) { + // Remove first element + set_osr_nmethods_head(osr_nmethods_head()->link()); + } else { + last->set_link(cur->link()); + } + } + n->set_link(NULL); + // Remember to unlock again + OsrList_lock->unlock(); +} + +nmethod* instanceKlass::lookup_osr_nmethod(const methodOop m, int bci) const { + // This is a short non-blocking critical region, so the no safepoint check is ok. + OsrList_lock->lock_without_safepoint_check(); + nmethod* osr = osr_nmethods_head(); + while (osr != NULL) { + assert(osr->is_osr_method(), "wrong kind of nmethod found in chain"); + if (osr->method() == m && + (bci == InvocationEntryBci || osr->osr_entry_bci() == bci)) { + // Found a match - return it. + OsrList_lock->unlock(); + return osr; + } + osr = osr->link(); + } + OsrList_lock->unlock(); + return NULL; +} + +// ----------------------------------------------------------------------------------------------------- +#ifndef PRODUCT + +// Printing + +void FieldPrinter::do_field(fieldDescriptor* fd) { + if (fd->is_static() == (_obj == NULL)) { + _st->print(" - "); + fd->print_on(_st); + _st->cr(); + } else { + fd->print_on_for(_st, _obj); + _st->cr(); + } +} + + +void instanceKlass::oop_print_on(oop obj, outputStream* st) { + Klass::oop_print_on(obj, st); + + if (as_klassOop() == SystemDictionary::string_klass()) { + typeArrayOop value = java_lang_String::value(obj); + juint offset = java_lang_String::offset(obj); + juint length = java_lang_String::length(obj); + if (value != NULL && + value->is_typeArray() && + offset <= (juint) value->length() && + offset + length <= (juint) value->length()) { + st->print("string: "); + Handle h_obj(obj); + java_lang_String::print(h_obj, st); + st->cr(); + if (!WizardMode) return; // that is enough + } + } + + st->print_cr("fields:"); + FieldPrinter print_nonstatic_field(st, obj); + do_nonstatic_fields(&print_nonstatic_field); + + if (as_klassOop() == SystemDictionary::class_klass()) { + klassOop mirrored_klass = java_lang_Class::as_klassOop(obj); + st->print(" - fake entry for mirror: "); + mirrored_klass->print_value_on(st); + st->cr(); + st->print(" - fake entry resolved_constructor: "); + methodOop ctor = java_lang_Class::resolved_constructor(obj); + ctor->print_value_on(st); + klassOop array_klass = java_lang_Class::array_klass(obj); + st->print(" - fake entry for array: "); + array_klass->print_value_on(st); + st->cr(); + st->cr(); + } +} + +void instanceKlass::oop_print_value_on(oop obj, outputStream* st) { + st->print("a "); + name()->print_value_on(st); + obj->print_address_on(st); +} + +#endif + +const char* instanceKlass::internal_name() const { + return external_name(); +} + + + +// Verification + +class VerifyFieldClosure: public OopClosure { + public: + void do_oop(oop* p) { + guarantee(Universe::heap()->is_in_closed_subset(p), "should be in heap"); + if (!(*p)->is_oop_or_null()) { + tty->print_cr("Failed: %p -> %p",p,(address)*p); + Universe::print(); + guarantee(false, "boom"); + } + } +}; + + +void instanceKlass::oop_verify_on(oop obj, outputStream* st) { + Klass::oop_verify_on(obj, st); + VerifyFieldClosure blk; + oop_oop_iterate(obj, &blk); +} + +#ifndef PRODUCT + +void instanceKlass::verify_class_klass_nonstatic_oop_maps(klassOop k) { + // This verification code is disabled. JDK_Version::is_gte_jdk14x_version() + // cannot be called since this function is called before the VM is + // able to determine what JDK version is running with. + // The check below always is false since 1.4. + return; + + // This verification code temporarily disabled for the 1.4 + // reflection implementation since java.lang.Class now has + // Java-level instance fields. Should rewrite this to handle this + // case. + if (!(JDK_Version::is_gte_jdk14x_version() && UseNewReflection)) { + // Verify that java.lang.Class instances have a fake oop field added. + instanceKlass* ik = instanceKlass::cast(k); + + // Check that we have the right class + static bool first_time = true; + guarantee(k == SystemDictionary::class_klass() && first_time, "Invalid verify of maps"); + first_time = false; + const int extra = java_lang_Class::number_of_fake_oop_fields; + guarantee(ik->nonstatic_field_size() == extra, "just checking"); + guarantee(ik->nonstatic_oop_map_size() == 1, "just checking"); + guarantee(ik->size_helper() == align_object_size(instanceOopDesc::header_size() + extra), "just checking"); + + // Check that the map is (2,extra) + int offset = java_lang_Class::klass_offset; + + OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); + guarantee(map->offset() == offset && map->length() == extra, "just checking"); + } +} + +#endif + + +/* JNIid class for jfieldIDs only */ + JNIid::JNIid(klassOop holder, int offset, JNIid* next) { + _holder = holder; + _offset = offset; + _next = next; + debug_only(_is_static_field_id = false;) + } + + + JNIid* JNIid::find(int offset) { + JNIid* current = this; + while (current != NULL) { + if (current->offset() == offset) return current; + current = current->next(); + } + return NULL; + } + +void JNIid::oops_do(OopClosure* f) { + for (JNIid* cur = this; cur != NULL; cur = cur->next()) { + f->do_oop(cur->holder_addr()); + } +} + +void JNIid::deallocate(JNIid* current) { + while (current != NULL) { + JNIid* next = current->next(); + delete current; + current = next; + } + } + + + void JNIid::verify(klassOop holder) { + int first_field_offset = instanceKlass::cast(holder)->offset_of_static_fields(); + int end_field_offset; + end_field_offset = first_field_offset + (instanceKlass::cast(holder)->static_field_size() * wordSize); + + JNIid* current = this; + while (current != NULL) { + guarantee(current->holder() == holder, "Invalid klass in JNIid"); + #ifdef ASSERT + int o = current->offset(); + if (current->is_static_field_id()) { + guarantee(o >= first_field_offset && o < end_field_offset, "Invalid static field offset in JNIid"); + } + #endif + current = current->next(); + } + } + + +#ifdef ASSERT + void instanceKlass::set_init_state(ClassState state) { + bool good_state = as_klassOop()->is_shared() ? (_init_state <= state) + : (_init_state < state); + assert(good_state || state == allocated, "illegal state transition"); + _init_state = state; + } +#endif + + +// RedefineClasses() support for previous versions: + +// Add an information node that contains weak references to the +// interesting parts of the previous version of the_class. +void instanceKlass::add_previous_version(instanceKlassHandle ikh, + BitMap * emcp_methods, int emcp_method_count) { + assert(Thread::current()->is_VM_thread(), + "only VMThread can add previous versions"); + + if (_previous_versions == NULL) { + // This is the first previous version so make some space. + // Start with 2 elements under the assumption that the class + // won't be redefined much. + _previous_versions = new (ResourceObj::C_HEAP) + GrowableArray(2, true); + } + + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00000100, ("adding previous version ref for %s @%d, EMCP_cnt=%d", + ikh->external_name(), _previous_versions->length(), emcp_method_count)); + constantPoolHandle cp_h(ikh->constants()); + jweak cp_ref = JNIHandles::make_weak_global(cp_h); + PreviousVersionNode * pv_node = NULL; + objArrayOop old_methods = ikh->methods(); + + if (emcp_method_count == 0) { + pv_node = new PreviousVersionNode(cp_ref, NULL); + RC_TRACE(0x00000400, + ("add: all methods are obsolete; flushing any EMCP weak refs")); + } else { + int local_count = 0; + GrowableArray* method_refs = new (ResourceObj::C_HEAP) + GrowableArray(emcp_method_count, true); + for (int i = 0; i < old_methods->length(); i++) { + if (emcp_methods->at(i)) { + // this old method is EMCP so save a weak ref + methodOop old_method = (methodOop) old_methods->obj_at(i); + methodHandle old_method_h(old_method); + jweak method_ref = JNIHandles::make_weak_global(old_method_h); + method_refs->append(method_ref); + if (++local_count >= emcp_method_count) { + // no more EMCP methods so bail out now + break; + } + } + } + pv_node = new PreviousVersionNode(cp_ref, method_refs); + } + + _previous_versions->append(pv_node); + + // Using weak references allows the interesting parts of previous + // classes to be GC'ed when they are no longer needed. Since the + // caller is the VMThread and we are at a safepoint, this is a good + // time to clear out unused weak references. + + RC_TRACE(0x00000400, ("add: previous version length=%d", + _previous_versions->length())); + + // skip the last entry since we just added it + for (int i = _previous_versions->length() - 2; i >= 0; i--) { + // check the previous versions array for a GC'ed weak refs + pv_node = _previous_versions->at(i); + cp_ref = pv_node->prev_constant_pool(); + assert(cp_ref != NULL, "weak cp ref was unexpectedly cleared"); + if (cp_ref == NULL) { + delete pv_node; + _previous_versions->remove_at(i); + // Since we are traversing the array backwards, we don't have to + // do anything special with the index. + continue; // robustness + } + + constantPoolOop cp = (constantPoolOop)JNIHandles::resolve(cp_ref); + if (cp == NULL) { + // this entry has been GC'ed so remove it + delete pv_node; + _previous_versions->remove_at(i); + // Since we are traversing the array backwards, we don't have to + // do anything special with the index. + continue; + } else { + RC_TRACE(0x00000400, ("add: previous version @%d is alive", i)); + } + + GrowableArray* method_refs = pv_node->prev_EMCP_methods(); + if (method_refs != NULL) { + RC_TRACE(0x00000400, ("add: previous methods length=%d", + method_refs->length())); + for (int j = method_refs->length() - 1; j >= 0; j--) { + jweak method_ref = method_refs->at(j); + assert(method_ref != NULL, "weak method ref was unexpectedly cleared"); + if (method_ref == NULL) { + method_refs->remove_at(j); + // Since we are traversing the array backwards, we don't have to + // do anything special with the index. + continue; // robustness + } + + methodOop method = (methodOop)JNIHandles::resolve(method_ref); + if (method == NULL || emcp_method_count == 0) { + // This method entry has been GC'ed or the current + // RedefineClasses() call has made all methods obsolete + // so remove it. + JNIHandles::destroy_weak_global(method_ref); + method_refs->remove_at(j); + } else { + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00000400, + ("add: %s(%s): previous method @%d in version @%d is alive", + method->name()->as_C_string(), method->signature()->as_C_string(), + j, i)); + } + } + } + } + + int obsolete_method_count = old_methods->length() - emcp_method_count; + + if (emcp_method_count != 0 && obsolete_method_count != 0 && + _previous_versions->length() > 1) { + // We have a mix of obsolete and EMCP methods. If there is more + // than the previous version that we just added, then we have to + // clear out any matching EMCP method entries the hard way. + int local_count = 0; + for (int i = 0; i < old_methods->length(); i++) { + if (!emcp_methods->at(i)) { + // only obsolete methods are interesting + methodOop old_method = (methodOop) old_methods->obj_at(i); + symbolOop m_name = old_method->name(); + symbolOop m_signature = old_method->signature(); + + // skip the last entry since we just added it + for (int j = _previous_versions->length() - 2; j >= 0; j--) { + // check the previous versions array for a GC'ed weak refs + pv_node = _previous_versions->at(j); + cp_ref = pv_node->prev_constant_pool(); + assert(cp_ref != NULL, "weak cp ref was unexpectedly cleared"); + if (cp_ref == NULL) { + delete pv_node; + _previous_versions->remove_at(j); + // Since we are traversing the array backwards, we don't have to + // do anything special with the index. + continue; // robustness + } + + constantPoolOop cp = (constantPoolOop)JNIHandles::resolve(cp_ref); + if (cp == NULL) { + // this entry has been GC'ed so remove it + delete pv_node; + _previous_versions->remove_at(j); + // Since we are traversing the array backwards, we don't have to + // do anything special with the index. + continue; + } + + GrowableArray* method_refs = pv_node->prev_EMCP_methods(); + if (method_refs == NULL) { + // We have run into a PreviousVersion generation where + // all methods were made obsolete during that generation's + // RedefineClasses() operation. At the time of that + // operation, all EMCP methods were flushed so we don't + // have to go back any further. + // + // A NULL method_refs is different than an empty method_refs. + // We cannot infer any optimizations about older generations + // from an empty method_refs for the current generation. + break; + } + + for (int k = method_refs->length() - 1; k >= 0; k--) { + jweak method_ref = method_refs->at(k); + assert(method_ref != NULL, + "weak method ref was unexpectedly cleared"); + if (method_ref == NULL) { + method_refs->remove_at(k); + // Since we are traversing the array backwards, we don't + // have to do anything special with the index. + continue; // robustness + } + + methodOop method = (methodOop)JNIHandles::resolve(method_ref); + if (method == NULL) { + // this method entry has been GC'ed so skip it + JNIHandles::destroy_weak_global(method_ref); + method_refs->remove_at(k); + continue; + } + + if (method->name() == m_name && + method->signature() == m_signature) { + // The current RedefineClasses() call has made all EMCP + // versions of this method obsolete so mark it as obsolete + // and remove the weak ref. + RC_TRACE(0x00000400, + ("add: %s(%s): flush obsolete method @%d in version @%d", + m_name->as_C_string(), m_signature->as_C_string(), k, j)); + + method->set_is_obsolete(); + JNIHandles::destroy_weak_global(method_ref); + method_refs->remove_at(k); + break; + } + } + + // The previous loop may not find a matching EMCP method, but + // that doesn't mean that we can optimize and not go any + // further back in the PreviousVersion generations. The EMCP + // method for this generation could have already been GC'ed, + // but there still may be an older EMCP method that has not + // been GC'ed. + } + + if (++local_count >= obsolete_method_count) { + // no more obsolete methods so bail out now + break; + } + } + } + } +} // end add_previous_version() + + +// Determine if instanceKlass has a previous version. +bool instanceKlass::has_previous_version() const { + if (_previous_versions == NULL) { + // no previous versions array so answer is easy + return false; + } + + for (int i = _previous_versions->length() - 1; i >= 0; i--) { + // Check the previous versions array for an info node that hasn't + // been GC'ed + PreviousVersionNode * pv_node = _previous_versions->at(i); + + jweak cp_ref = pv_node->prev_constant_pool(); + assert(cp_ref != NULL, "weak reference was unexpectedly cleared"); + if (cp_ref == NULL) { + continue; // robustness + } + + constantPoolOop cp = (constantPoolOop)JNIHandles::resolve(cp_ref); + if (cp != NULL) { + // we have at least one previous version + return true; + } + + // We don't have to check the method refs. If the constant pool has + // been GC'ed then so have the methods. + } + + // all of the underlying nodes' info has been GC'ed + return false; +} // end has_previous_version() + +methodOop instanceKlass::method_with_idnum(int idnum) { + methodOop m = NULL; + if (idnum < methods()->length()) { + m = (methodOop) methods()->obj_at(idnum); + } + if (m == NULL || m->method_idnum() != idnum) { + for (int index = 0; index < methods()->length(); ++index) { + m = (methodOop) methods()->obj_at(index); + if (m->method_idnum() == idnum) { + return m; + } + } + } + return m; +} + + +// Set the annotation at 'idnum' to 'anno'. +// We don't want to create or extend the array if 'anno' is NULL, since that is the +// default value. However, if the array exists and is long enough, we must set NULL values. +void instanceKlass::set_methods_annotations_of(int idnum, typeArrayOop anno, objArrayOop* md_p) { + objArrayOop md = *md_p; + if (md != NULL && md->length() > idnum) { + md->obj_at_put(idnum, anno); + } else if (anno != NULL) { + // create the array + int length = MAX2(idnum+1, (int)_idnum_allocated_count); + md = oopFactory::new_system_objArray(length, Thread::current()); + if (*md_p != NULL) { + // copy the existing entries + for (int index = 0; index < (*md_p)->length(); index++) { + md->obj_at_put(index, (*md_p)->obj_at(index)); + } + } + set_annotations(md, md_p); + md->obj_at_put(idnum, anno); + } // if no array and idnum isn't included there is nothing to do +} + +// Construct a PreviousVersionNode entry for the array hung off +// the instanceKlass. +PreviousVersionNode::PreviousVersionNode(jweak prev_constant_pool, + GrowableArray* prev_EMCP_methods) { + + _prev_constant_pool = prev_constant_pool; + _prev_EMCP_methods = prev_EMCP_methods; +} + + +// Destroy a PreviousVersionNode +PreviousVersionNode::~PreviousVersionNode() { + if (_prev_constant_pool != NULL) { + JNIHandles::destroy_weak_global(_prev_constant_pool); + } + + if (_prev_EMCP_methods != NULL) { + for (int i = _prev_EMCP_methods->length() - 1; i >= 0; i--) { + jweak method_ref = _prev_EMCP_methods->at(i); + if (method_ref != NULL) { + JNIHandles::destroy_weak_global(method_ref); + } + } + delete _prev_EMCP_methods; + } +} + + +// Construct a PreviousVersionInfo entry +PreviousVersionInfo::PreviousVersionInfo(PreviousVersionNode *pv_node) { + _prev_constant_pool_handle = constantPoolHandle(); // NULL handle + _prev_EMCP_method_handles = NULL; + + jweak cp_ref = pv_node->prev_constant_pool(); + assert(cp_ref != NULL, "weak constant pool ref was unexpectedly cleared"); + if (cp_ref == NULL) { + return; // robustness + } + + constantPoolOop cp = (constantPoolOop)JNIHandles::resolve(cp_ref); + if (cp == NULL) { + // Weak reference has been GC'ed. Since the constant pool has been + // GC'ed, the methods have also been GC'ed. + return; + } + + // make the constantPoolOop safe to return + _prev_constant_pool_handle = constantPoolHandle(cp); + + GrowableArray* method_refs = pv_node->prev_EMCP_methods(); + if (method_refs == NULL) { + // the instanceKlass did not have any EMCP methods + return; + } + + _prev_EMCP_method_handles = new GrowableArray(10); + + int n_methods = method_refs->length(); + for (int i = 0; i < n_methods; i++) { + jweak method_ref = method_refs->at(i); + assert(method_ref != NULL, "weak method ref was unexpectedly cleared"); + if (method_ref == NULL) { + continue; // robustness + } + + methodOop method = (methodOop)JNIHandles::resolve(method_ref); + if (method == NULL) { + // this entry has been GC'ed so skip it + continue; + } + + // make the methodOop safe to return + _prev_EMCP_method_handles->append(methodHandle(method)); + } +} + + +// Destroy a PreviousVersionInfo +PreviousVersionInfo::~PreviousVersionInfo() { + // Since _prev_EMCP_method_handles is not C-heap allocated, we + // don't have to delete it. +} + + +// Construct a helper for walking the previous versions array +PreviousVersionWalker::PreviousVersionWalker(instanceKlass *ik) { + _previous_versions = ik->previous_versions(); + _current_index = 0; + // _hm needs no initialization + _current_p = NULL; +} + + +// Destroy a PreviousVersionWalker +PreviousVersionWalker::~PreviousVersionWalker() { + // Delete the current info just in case the caller didn't walk to + // the end of the previous versions list. No harm if _current_p is + // already NULL. + delete _current_p; + + // When _hm is destroyed, all the Handles returned in + // PreviousVersionInfo objects will be destroyed. + // Also, after this destructor is finished it will be + // safe to delete the GrowableArray allocated in the + // PreviousVersionInfo objects. +} + + +// Return the interesting information for the next previous version +// of the klass. Returns NULL if there are no more previous versions. +PreviousVersionInfo* PreviousVersionWalker::next_previous_version() { + if (_previous_versions == NULL) { + // no previous versions so nothing to return + return NULL; + } + + delete _current_p; // cleanup the previous info for the caller + _current_p = NULL; // reset to NULL so we don't delete same object twice + + int length = _previous_versions->length(); + + while (_current_index < length) { + PreviousVersionNode * pv_node = _previous_versions->at(_current_index++); + PreviousVersionInfo * pv_info = new (ResourceObj::C_HEAP) + PreviousVersionInfo(pv_node); + + constantPoolHandle cp_h = pv_info->prev_constant_pool_handle(); + if (cp_h.is_null()) { + delete pv_info; + + // The underlying node's info has been GC'ed so try the next one. + // We don't have to check the methods. If the constant pool has + // GC'ed then so have the methods. + continue; + } + + // Found a node with non GC'ed info so return it. The caller will + // need to delete pv_info when they are done with it. + _current_p = pv_info; + return pv_info; + } + + // all of the underlying nodes' info has been GC'ed + return NULL; +} // end next_previous_version() diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp new file mode 100644 index 00000000000..29a76dc2c3b --- /dev/null +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp @@ -0,0 +1,918 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// An instanceKlass is the VM level representation of a Java class. +// It contains all information needed for at class at execution runtime. + +// instanceKlass layout: +// [header ] klassOop +// [klass pointer ] klassOop +// [C++ vtbl pointer ] Klass +// [subtype cache ] Klass +// [instance size ] Klass +// [java mirror ] Klass +// [super ] Klass +// [access_flags ] Klass +// [name ] Klass +// [first subklass ] Klass +// [next sibling ] Klass +// [array klasses ] +// [methods ] +// [local interfaces ] +// [transitive interfaces ] +// [number of implementors ] +// [implementors ] klassOop[2] +// [fields ] +// [constants ] +// [class loader ] +// [protection domain ] +// [signers ] +// [source file name ] +// [inner classes ] +// [static field size ] +// [nonstatic field size ] +// [static oop fields size ] +// [nonstatic oop maps size ] +// [has finalize method ] +// [deoptimization mark bit ] +// [initialization state ] +// [initializing thread ] +// [Java vtable length ] +// [oop map cache (stack maps) ] +// [EMBEDDED Java vtable ] size in words = vtable_len +// [EMBEDDED static oop fields ] size in words = static_oop_fields_size +// [ static non-oop fields ] size in words = static_field_size - static_oop_fields_size +// [EMBEDDED nonstatic oop-map blocks] size in words = nonstatic_oop_map_size +// +// The embedded nonstatic oop-map blocks are short pairs (offset, length) indicating +// where oops are located in instances of this klass. + + +// forward declaration for class -- see below for definition +class SuperTypeClosure; +class OopMapBlock; +class JNIid; +class jniIdMapBase; +class BreakpointInfo; +class fieldDescriptor; +class DepChange; +class nmethodBucket; +class PreviousVersionNode; +class JvmtiCachedClassFieldMap; + +// This is used in iterators below. +class FieldClosure: public StackObj { +public: + virtual void do_field(fieldDescriptor* fd) = 0; +}; + +#ifndef PRODUCT +// Print fields. +// If "obj" argument to constructor is NULL, prints static fields, otherwise prints non-static fields. +class FieldPrinter: public FieldClosure { + oop _obj; + outputStream* _st; + public: + FieldPrinter(outputStream* st, oop obj = NULL) : _obj(obj), _st(st) {} + void do_field(fieldDescriptor* fd); +}; +#endif // !PRODUCT + +class instanceKlass: public Klass { + friend class VMStructs; + public: + // See "The Java Virtual Machine Specification" section 2.16.2-5 for a detailed description + // of the class loading & initialization procedure, and the use of the states. + enum ClassState { + unparsable_by_gc = 0, // object is not yet parsable by gc. Value of _init_state at object allocation. + allocated, // allocated (but not yet linked) + loaded, // loaded and inserted in class hierarchy (but not linked yet) + linked, // successfully linked/verified (but not initialized yet) + being_initialized, // currently running class initializer + fully_initialized, // initialized (successfull final state) + initialization_error // error happened during initialization + }; + + public: + oop* oop_block_beg() const { return adr_array_klasses(); } + oop* oop_block_end() const { return adr_methods_default_annotations() + 1; } + + enum { + implementors_limit = 2 // how many implems can we track? + }; + + protected: + // + // The oop block. See comment in klass.hpp before making changes. + // + + // Array classes holding elements of this class. + klassOop _array_klasses; + // Method array. + objArrayOop _methods; + // Int array containing the original order of method in the class file (for + // JVMTI). + typeArrayOop _method_ordering; + // Interface (klassOops) this class declares locally to implement. + objArrayOop _local_interfaces; + // Interface (klassOops) this class implements transitively. + objArrayOop _transitive_interfaces; + // Instance and static variable information, 5-tuples of shorts [access, name + // index, sig index, initval index, offset]. + typeArrayOop _fields; + // Constant pool for this class. + constantPoolOop _constants; + // Class loader used to load this class, NULL if VM loader used. + oop _class_loader; + // Protection domain. + oop _protection_domain; + // Class signers. + objArrayOop _signers; + // Name of source file containing this klass, NULL if not specified. + symbolOop _source_file_name; + // the source debug extension for this klass, NULL if not specified. + symbolOop _source_debug_extension; + // inner_classes attribute. + typeArrayOop _inner_classes; + // Implementors of this interface (not valid if it overflows) + klassOop _implementors[implementors_limit]; + // Generic signature, or null if none. + symbolOop _generic_signature; + // Annotations for this class, or null if none. + typeArrayOop _class_annotations; + // Annotation objects (byte arrays) for fields, or null if no annotations. + // Indices correspond to entries (not indices) in fields array. + objArrayOop _fields_annotations; + // Annotation objects (byte arrays) for methods, or null if no annotations. + // Index is the idnum, which is initially the same as the methods array index. + objArrayOop _methods_annotations; + // Annotation objects (byte arrays) for methods' parameters, or null if no + // such annotations. + // Index is the idnum, which is initially the same as the methods array index. + objArrayOop _methods_parameter_annotations; + // Annotation objects (byte arrays) for methods' default values, or null if no + // such annotations. + // Index is the idnum, which is initially the same as the methods array index. + objArrayOop _methods_default_annotations; + + // + // End of the oop block. + // + + int _nonstatic_field_size; // number of non-static fields in this klass (including inherited fields) + int _static_field_size; // number of static fields (oop and non-oop) in this klass + int _static_oop_field_size;// number of static oop fields in this klass + int _nonstatic_oop_map_size;// number of nonstatic oop-map blocks allocated at end of this klass + bool _is_marked_dependent; // used for marking during flushing and deoptimization + bool _rewritten; // methods rewritten. + u2 _minor_version; // minor version number of class file + u2 _major_version; // major version number of class file + ClassState _init_state; // state of class + Thread* _init_thread; // Pointer to current thread doing initialization (to handle recusive initialization) + int _vtable_len; // length of Java vtable (in words) + int _itable_len; // length of Java itable (in words) + ReferenceType _reference_type; // reference type + OopMapCache* volatile _oop_map_cache; // OopMapCache for all methods in the klass (allocated lazily) + JNIid* _jni_ids; // First JNI identifier for static fields in this class + jmethodID* _methods_jmethod_ids; // jmethodIDs corresponding to method_idnum, or NULL if none + int* _methods_cached_itable_indices; // itable_index cache for JNI invoke corresponding to methods idnum, or NULL + nmethodBucket* _dependencies; // list of dependent nmethods + nmethod* _osr_nmethods_head; // Head of list of on-stack replacement nmethods for this class + BreakpointInfo* _breakpoints; // bpt lists, managed by methodOop + int _nof_implementors; // No of implementors of this interface (zero if not an interface) + // Array of interesting part(s) of the previous version(s) of this + // instanceKlass. See PreviousVersionWalker below. + GrowableArray* _previous_versions; + u2 _enclosing_method_class_index; // Constant pool index for class of enclosing method, or 0 if none + u2 _enclosing_method_method_index; // Constant pool index for name and type of enclosing method, or 0 if none + // JVMTI fields can be moved to their own structure - see 6315920 + unsigned char * _cached_class_file_bytes; // JVMTI: cached class file, before retransformable agent modified it in CFLH + jint _cached_class_file_len; // JVMTI: length of above + JvmtiCachedClassFieldMap* _jvmti_cached_class_field_map; // JVMTI: used during heap iteration + volatile u2 _idnum_allocated_count; // JNI/JVMTI: increments with the addition of methods, old ids don't change + + // embedded Java vtable follows here + // embedded Java itables follows here + // embedded static fields follows here + // embedded nonstatic oop-map blocks follows here + + friend class instanceKlassKlass; + friend class SystemDictionary; + + public: + // field sizes + int nonstatic_field_size() const { return _nonstatic_field_size; } + void set_nonstatic_field_size(int size) { _nonstatic_field_size = size; } + + int static_field_size() const { return _static_field_size; } + void set_static_field_size(int size) { _static_field_size = size; } + + int static_oop_field_size() const { return _static_oop_field_size; } + void set_static_oop_field_size(int size) { _static_oop_field_size = size; } + + // Java vtable + int vtable_length() const { return _vtable_len; } + void set_vtable_length(int len) { _vtable_len = len; } + + // Java itable + int itable_length() const { return _itable_len; } + void set_itable_length(int len) { _itable_len = len; } + + // array klasses + klassOop array_klasses() const { return _array_klasses; } + void set_array_klasses(klassOop k) { oop_store_without_check((oop*) &_array_klasses, (oop) k); } + + // methods + objArrayOop methods() const { return _methods; } + void set_methods(objArrayOop a) { oop_store_without_check((oop*) &_methods, (oop) a); } + methodOop method_with_idnum(int idnum); + + // method ordering + typeArrayOop method_ordering() const { return _method_ordering; } + void set_method_ordering(typeArrayOop m) { oop_store_without_check((oop*) &_method_ordering, (oop) m); } + + // interfaces + objArrayOop local_interfaces() const { return _local_interfaces; } + void set_local_interfaces(objArrayOop a) { oop_store_without_check((oop*) &_local_interfaces, (oop) a); } + objArrayOop transitive_interfaces() const { return _transitive_interfaces; } + void set_transitive_interfaces(objArrayOop a) { oop_store_without_check((oop*) &_transitive_interfaces, (oop) a); } + + // fields + // Field info extracted from the class file and stored + // as an array of 7 shorts + enum FieldOffset { + access_flags_offset = 0, + name_index_offset = 1, + signature_index_offset = 2, + initval_index_offset = 3, + low_offset = 4, + high_offset = 5, + generic_signature_offset = 6, + next_offset = 7 + }; + + typeArrayOop fields() const { return _fields; } + int offset_from_fields( int index ) const { + return build_int_from_shorts( fields()->ushort_at(index + low_offset), + fields()->ushort_at(index + high_offset) ); + } + + void set_fields(typeArrayOop f) { oop_store_without_check((oop*) &_fields, (oop) f); } + + // inner classes + typeArrayOop inner_classes() const { return _inner_classes; } + void set_inner_classes(typeArrayOop f) { oop_store_without_check((oop*) &_inner_classes, (oop) f); } + + enum InnerClassAttributeOffset { + // From http://mirror.eng/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html#18814 + inner_class_inner_class_info_offset = 0, + inner_class_outer_class_info_offset = 1, + inner_class_inner_name_offset = 2, + inner_class_access_flags_offset = 3, + inner_class_next_offset = 4 + }; + + // package + bool is_same_class_package(klassOop class2); + bool is_same_class_package(oop classloader2, symbolOop classname2); + static bool is_same_class_package(oop class_loader1, symbolOop class_name1, oop class_loader2, symbolOop class_name2); + + // initialization state + bool is_loaded() const { return _init_state >= loaded; } + bool is_linked() const { return _init_state >= linked; } + bool is_initialized() const { return _init_state == fully_initialized; } + bool is_not_initialized() const { return _init_state < being_initialized; } + bool is_being_initialized() const { return _init_state == being_initialized; } + bool is_in_error_state() const { return _init_state == initialization_error; } + bool is_reentrant_initialization(Thread *thread) { return thread == _init_thread; } + int get_init_state() { return _init_state; } // Useful for debugging + bool is_rewritten() const { return _rewritten; } + + // marking + bool is_marked_dependent() const { return _is_marked_dependent; } + void set_is_marked_dependent(bool value) { _is_marked_dependent = value; } + + // initialization (virtuals from Klass) + bool should_be_initialized() const; // means that initialize should be called + void initialize(TRAPS); + void link_class(TRAPS); + bool link_class_or_fail(TRAPS); // returns false on failure + void unlink_class(); + void rewrite_class(TRAPS); + methodOop class_initializer(); + + // set the class to initialized if no static initializer is present + void eager_initialize(Thread *thread); + + // reference type + ReferenceType reference_type() const { return _reference_type; } + void set_reference_type(ReferenceType t) { _reference_type = t; } + + // find local field, returns true if found + bool find_local_field(symbolOop name, symbolOop sig, fieldDescriptor* fd) const; + // find field in direct superinterfaces, returns the interface in which the field is defined + klassOop find_interface_field(symbolOop name, symbolOop sig, fieldDescriptor* fd) const; + // find field according to JVM spec 5.4.3.2, returns the klass in which the field is defined + klassOop find_field(symbolOop name, symbolOop sig, fieldDescriptor* fd) const; + // find instance or static fields according to JVM spec 5.4.3.2, returns the klass in which the field is defined + klassOop find_field(symbolOop name, symbolOop sig, bool is_static, fieldDescriptor* fd) const; + + // find a non-static or static field given its offset within the class. + bool contains_field_offset(int offset) { + return ((offset/wordSize) >= instanceOopDesc::header_size() && + (offset/wordSize)-instanceOopDesc::header_size() < nonstatic_field_size()); + } + + bool find_local_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const; + bool find_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const; + + // find a local method (returns NULL if not found) + methodOop find_method(symbolOop name, symbolOop signature) const; + static methodOop find_method(objArrayOop methods, symbolOop name, symbolOop signature); + + // lookup operation (returns NULL if not found) + methodOop uncached_lookup_method(symbolOop name, symbolOop signature) const; + + // lookup a method in all the interfaces that this class implements + // (returns NULL if not found) + methodOop lookup_method_in_all_interfaces(symbolOop name, symbolOop signature) const; + + // constant pool + constantPoolOop constants() const { return _constants; } + void set_constants(constantPoolOop c) { oop_store_without_check((oop*) &_constants, (oop) c); } + + // class loader + oop class_loader() const { return _class_loader; } + void set_class_loader(oop l) { oop_store((oop*) &_class_loader, l); } + + // protection domain + oop protection_domain() { return _protection_domain; } + void set_protection_domain(oop pd) { oop_store((oop*) &_protection_domain, pd); } + + // signers + objArrayOop signers() const { return _signers; } + void set_signers(objArrayOop s) { oop_store((oop*) &_signers, oop(s)); } + + // source file name + symbolOop source_file_name() const { return _source_file_name; } + void set_source_file_name(symbolOop n) { oop_store_without_check((oop*) &_source_file_name, (oop) n); } + + // minor and major version numbers of class file + u2 minor_version() const { return _minor_version; } + void set_minor_version(u2 minor_version) { _minor_version = minor_version; } + u2 major_version() const { return _major_version; } + void set_major_version(u2 major_version) { _major_version = major_version; } + + // source debug extension + symbolOop source_debug_extension() const { return _source_debug_extension; } + void set_source_debug_extension(symbolOop n){ oop_store_without_check((oop*) &_source_debug_extension, (oop) n); } + + // nonstatic oop-map blocks + int nonstatic_oop_map_size() const { return _nonstatic_oop_map_size; } + void set_nonstatic_oop_map_size(int size) { _nonstatic_oop_map_size = size; } + + // RedefineClasses() support for previous versions: + void add_previous_version(instanceKlassHandle ikh, BitMap *emcp_methods, + int emcp_method_count); + bool has_previous_version() const; + void init_previous_versions() { + _previous_versions = NULL; + } + GrowableArray* previous_versions() const { + return _previous_versions; + } + + // JVMTI: Support for caching a class file before it is modified by an agent that can do retransformation + void set_cached_class_file(unsigned char *class_file_bytes, + jint class_file_len) { _cached_class_file_len = class_file_len; + _cached_class_file_bytes = class_file_bytes; } + jint get_cached_class_file_len() { return _cached_class_file_len; } + unsigned char * get_cached_class_file_bytes() { return _cached_class_file_bytes; } + + // JVMTI: Support for caching of field indices, types, and offsets + void set_jvmti_cached_class_field_map(JvmtiCachedClassFieldMap* descriptor) { + _jvmti_cached_class_field_map = descriptor; + } + JvmtiCachedClassFieldMap* jvmti_cached_class_field_map() const { + return _jvmti_cached_class_field_map; + } + + // for adding methods, constMethodOopDesc::UNSET_IDNUM means no more ids available + inline u2 next_method_idnum(); + void set_initial_method_idnum(u2 value) { _idnum_allocated_count = value; } + + // generics support + symbolOop generic_signature() const { return _generic_signature; } + void set_generic_signature(symbolOop sig) { oop_store_without_check((oop*)&_generic_signature, (oop)sig); } + u2 enclosing_method_class_index() const { return _enclosing_method_class_index; } + u2 enclosing_method_method_index() const { return _enclosing_method_method_index; } + void set_enclosing_method_indices(u2 class_index, + u2 method_index) { _enclosing_method_class_index = class_index; + _enclosing_method_method_index = method_index; } + + // jmethodID support + static jmethodID jmethod_id_for_impl(instanceKlassHandle ik_h, methodHandle method_h); + jmethodID jmethod_id_or_null(methodOop method); + + // cached itable index support + void set_cached_itable_index(size_t idnum, int index); + int cached_itable_index(size_t idnum); + + // annotations support + typeArrayOop class_annotations() const { return _class_annotations; } + objArrayOop fields_annotations() const { return _fields_annotations; } + objArrayOop methods_annotations() const { return _methods_annotations; } + objArrayOop methods_parameter_annotations() const { return _methods_parameter_annotations; } + objArrayOop methods_default_annotations() const { return _methods_default_annotations; } + void set_class_annotations(typeArrayOop md) { oop_store_without_check((oop*)&_class_annotations, (oop)md); } + void set_fields_annotations(objArrayOop md) { set_annotations(md, &_fields_annotations); } + void set_methods_annotations(objArrayOop md) { set_annotations(md, &_methods_annotations); } + void set_methods_parameter_annotations(objArrayOop md) { set_annotations(md, &_methods_parameter_annotations); } + void set_methods_default_annotations(objArrayOop md) { set_annotations(md, &_methods_default_annotations); } + typeArrayOop get_method_annotations_of(int idnum) + { return get_method_annotations_from(idnum, _methods_annotations); } + typeArrayOop get_method_parameter_annotations_of(int idnum) + { return get_method_annotations_from(idnum, _methods_parameter_annotations); } + typeArrayOop get_method_default_annotations_of(int idnum) + { return get_method_annotations_from(idnum, _methods_default_annotations); } + void set_method_annotations_of(int idnum, typeArrayOop anno) + { set_methods_annotations_of(idnum, anno, &_methods_annotations); } + void set_method_parameter_annotations_of(int idnum, typeArrayOop anno) + { set_methods_annotations_of(idnum, anno, &_methods_annotations); } + void set_method_default_annotations_of(int idnum, typeArrayOop anno) + { set_methods_annotations_of(idnum, anno, &_methods_annotations); } + + // allocation + DEFINE_ALLOCATE_PERMANENT(instanceKlass); + instanceOop allocate_instance(TRAPS); + instanceOop allocate_permanent_instance(TRAPS); + + // additional member function to return a handle + instanceHandle allocate_instance_handle(TRAPS) { return instanceHandle(THREAD, allocate_instance(THREAD)); } + + objArrayOop allocate_objArray(int n, int length, TRAPS); + // Helper function + static instanceOop register_finalizer(instanceOop i, TRAPS); + + // Check whether reflection/jni/jvm code is allowed to instantiate this class; + // if not, throw either an Error or an Exception. + virtual void check_valid_for_instantiation(bool throwError, TRAPS); + + // initialization + void call_class_initializer(TRAPS); + void set_initialization_state_and_notify(ClassState state, TRAPS); + + // OopMapCache support + OopMapCache* oop_map_cache() { return _oop_map_cache; } + void set_oop_map_cache(OopMapCache *cache) { _oop_map_cache = cache; } + void mask_for(methodHandle method, int bci, InterpreterOopMap* entry); + + // JNI identifier support (for static fields - for jni performance) + JNIid* jni_ids() { return _jni_ids; } + void set_jni_ids(JNIid* ids) { _jni_ids = ids; } + JNIid* jni_id_for(int offset); + + // maintenance of deoptimization dependencies + int mark_dependent_nmethods(DepChange& changes); + void add_dependent_nmethod(nmethod* nm); + void remove_dependent_nmethod(nmethod* nm); + + // On-stack replacement support + nmethod* osr_nmethods_head() const { return _osr_nmethods_head; }; + void set_osr_nmethods_head(nmethod* h) { _osr_nmethods_head = h; }; + void add_osr_nmethod(nmethod* n); + void remove_osr_nmethod(nmethod* n); + nmethod* lookup_osr_nmethod(const methodOop m, int bci) const; + + // Breakpoint support (see methods on methodOop for details) + BreakpointInfo* breakpoints() const { return _breakpoints; }; + void set_breakpoints(BreakpointInfo* bps) { _breakpoints = bps; }; + + // support for stub routines + static int init_state_offset_in_bytes() { return offset_of(instanceKlass, _init_state); } + static int init_thread_offset_in_bytes() { return offset_of(instanceKlass, _init_thread); } + + // subclass/subinterface checks + bool implements_interface(klassOop k) const; + + // Access to implementors of an interface. We only store the count + // of implementors, and in case, there are only a few + // implementors, we store them in a short list. + // This accessor returns NULL if we walk off the end of the list. + klassOop implementor(int i) const { + return (i < implementors_limit)? _implementors[i]: (klassOop) NULL; + } + int nof_implementors() const { return _nof_implementors; } + void add_implementor(klassOop k); // k is a new class that implements this interface + void init_implementor(); // initialize + + // link this class into the implementors list of every interface it implements + void process_interfaces(Thread *thread); + + // virtual operations from Klass + bool is_leaf_class() const { return _subklass == NULL; } + objArrayOop compute_secondary_supers(int num_extra_slots, TRAPS); + bool compute_is_subtype_of(klassOop k); + bool can_be_primary_super_slow() const; + klassOop java_super() const { return super(); } + int oop_size(oop obj) const { return size_helper(); } + int klass_oop_size() const { return object_size(); } + bool oop_is_instance_slow() const { return true; } + + // Iterators + void do_local_static_fields(FieldClosure* cl); + void do_nonstatic_fields(FieldClosure* cl); // including inherited fields + void do_local_static_fields(void f(fieldDescriptor*, TRAPS), TRAPS); + + void methods_do(void f(methodOop method)); + void array_klasses_do(void f(klassOop k)); + void with_array_klasses_do(void f(klassOop k)); + bool super_types_do(SuperTypeClosure* blk); + + // Casting from klassOop + static instanceKlass* cast(klassOop k) { + Klass* kp = k->klass_part(); + assert(kp->null_vtbl() || kp->oop_is_instance_slow(), "cast to instanceKlass"); + return (instanceKlass*) kp; + } + + // Sizing (in words) + static int header_size() { return align_object_offset(oopDesc::header_size() + sizeof(instanceKlass)/HeapWordSize); } + int object_size() const { return object_size(align_object_offset(vtable_length()) + align_object_offset(itable_length()) + static_field_size() + nonstatic_oop_map_size()); } + static int vtable_start_offset() { return header_size(); } + static int vtable_length_offset() { return oopDesc::header_size() + offset_of(instanceKlass, _vtable_len) / HeapWordSize; } + static int object_size(int extra) { return align_object_size(header_size() + extra); } + + intptr_t* start_of_vtable() const { return ((intptr_t*)as_klassOop()) + vtable_start_offset(); } + intptr_t* start_of_itable() const { return start_of_vtable() + align_object_offset(vtable_length()); } + int itable_offset_in_words() const { return start_of_itable() - (intptr_t*)as_klassOop(); } + + oop* start_of_static_fields() const { return (oop*)(start_of_itable() + align_object_offset(itable_length())); } + intptr_t* end_of_itable() const { return start_of_itable() + itable_length(); } + oop* end_of_static_fields() const { return start_of_static_fields() + static_field_size(); } + int offset_of_static_fields() const { return (intptr_t)start_of_static_fields() - (intptr_t)as_klassOop(); } + + OopMapBlock* start_of_nonstatic_oop_maps() const { return (OopMapBlock*) (start_of_static_fields() + static_field_size()); } + + // Allocation profiling support + juint alloc_size() const { return _alloc_count * size_helper(); } + void set_alloc_size(juint n) {} + + // Use this to return the size of an instance in heap words: + int size_helper() const { + return layout_helper_to_size_helper(layout_helper()); + } + + // This bit is initialized in classFileParser.cpp. + // It is false under any of the following conditions: + // - the class is abstract (including any interface) + // - the class has a finalizer (if !RegisterFinalizersAtInit) + // - the class size is larger than FastAllocateSizeLimit + // - the class is java/lang/Class, which cannot be allocated directly + bool can_be_fastpath_allocated() const { + return !layout_helper_needs_slow_path(layout_helper()); + } + + // Java vtable/itable + klassVtable* vtable() const; // return new klassVtable wrapper + inline methodOop method_at_vtable(int index); + klassItable* itable() const; // return new klassItable wrapper + methodOop method_at_itable(klassOop holder, int index, TRAPS); + + // Garbage collection + void oop_follow_contents(oop obj); + void follow_static_fields(); + void adjust_static_fields(); + int oop_adjust_pointers(oop obj); + bool object_is_parsable() const { return _init_state != unparsable_by_gc; } + // Value of _init_state must be zero (unparsable_by_gc) when klass field is set. + + void follow_weak_klass_links( + BoolObjectClosure* is_alive, OopClosure* keep_alive); + void release_C_heap_structures(); + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + +#ifndef SERIALGC + // Parallel Scavenge + void copy_static_fields(PSPromotionManager* pm); + void push_static_fields(PSPromotionManager* pm); + + // Parallel Old + void follow_static_fields(ParCompactionManager* cm); + void copy_static_fields(ParCompactionManager* cm); + void update_static_fields(); + void update_static_fields(HeapWord* beg_addr, HeapWord* end_addr); +#endif // SERIALGC + + // Naming + char* signature_name() const; + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk) { + return oop_oop_iterate_v(obj, blk); + } + + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + return oop_oop_iterate_v_m(obj, blk, mr); + } + +#define InstanceKlass_OOP_OOP_ITERATE_DECL(OopClosureType, nv_suffix) \ + int oop_oop_iterate##nv_suffix(oop obj, OopClosureType* blk); \ + int oop_oop_iterate##nv_suffix##_m(oop obj, OopClosureType* blk, \ + MemRegion mr); + + ALL_OOP_OOP_ITERATE_CLOSURES_1(InstanceKlass_OOP_OOP_ITERATE_DECL) + ALL_OOP_OOP_ITERATE_CLOSURES_3(InstanceKlass_OOP_OOP_ITERATE_DECL) + + void iterate_static_fields(OopClosure* closure); + void iterate_static_fields(OopClosure* closure, MemRegion mr); + +private: + // initialization state +#ifdef ASSERT + void set_init_state(ClassState state); +#else + void set_init_state(ClassState state) { _init_state = state; } +#endif + void set_rewritten() { _rewritten = true; } + void set_init_thread(Thread *thread) { _init_thread = thread; } + + u2 idnum_allocated_count() const { return _idnum_allocated_count; } + jmethodID* methods_jmethod_ids_acquire() const + { return (jmethodID*)OrderAccess::load_ptr_acquire(&_methods_jmethod_ids); } + void release_set_methods_jmethod_ids(jmethodID* jmeths) + { OrderAccess::release_store_ptr(&_methods_jmethod_ids, jmeths); } + + int* methods_cached_itable_indices_acquire() const + { return (int*)OrderAccess::load_ptr_acquire(&_methods_cached_itable_indices); } + void release_set_methods_cached_itable_indices(int* indices) + { OrderAccess::release_store_ptr(&_methods_cached_itable_indices, indices); } + + inline typeArrayOop get_method_annotations_from(int idnum, objArrayOop annos); + void set_annotations(objArrayOop md, objArrayOop* md_p) { oop_store_without_check((oop*)md_p, (oop)md); } + void set_methods_annotations_of(int idnum, typeArrayOop anno, objArrayOop* md_p); + + // Offsets for memory management + oop* adr_array_klasses() const { return (oop*)&this->_array_klasses;} + oop* adr_methods() const { return (oop*)&this->_methods;} + oop* adr_method_ordering() const { return (oop*)&this->_method_ordering;} + oop* adr_local_interfaces() const { return (oop*)&this->_local_interfaces;} + oop* adr_transitive_interfaces() const { return (oop*)&this->_transitive_interfaces;} + oop* adr_fields() const { return (oop*)&this->_fields;} + oop* adr_constants() const { return (oop*)&this->_constants;} + oop* adr_class_loader() const { return (oop*)&this->_class_loader;} + oop* adr_protection_domain() const { return (oop*)&this->_protection_domain;} + oop* adr_signers() const { return (oop*)&this->_signers;} + oop* adr_source_file_name() const { return (oop*)&this->_source_file_name;} + oop* adr_source_debug_extension() const { return (oop*)&this->_source_debug_extension;} + oop* adr_inner_classes() const { return (oop*)&this->_inner_classes;} + oop* adr_implementors() const { return (oop*)&this->_implementors[0];} + oop* adr_generic_signature() const { return (oop*)&this->_generic_signature;} + oop* adr_methods_jmethod_ids() const { return (oop*)&this->_methods_jmethod_ids;} + oop* adr_methods_cached_itable_indices() const { return (oop*)&this->_methods_cached_itable_indices;} + oop* adr_class_annotations() const { return (oop*)&this->_class_annotations;} + oop* adr_fields_annotations() const { return (oop*)&this->_fields_annotations;} + oop* adr_methods_annotations() const { return (oop*)&this->_methods_annotations;} + oop* adr_methods_parameter_annotations() const { return (oop*)&this->_methods_parameter_annotations;} + oop* adr_methods_default_annotations() const { return (oop*)&this->_methods_default_annotations;} + + // Static methods that are used to implement member methods where an exposed this pointer + // is needed due to possible GCs + static bool link_class_impl (instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS); + static bool verify_code (instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS); + static void initialize_impl (instanceKlassHandle this_oop, TRAPS); + static void eager_initialize_impl (instanceKlassHandle this_oop); + static void set_initialization_state_and_notify_impl (instanceKlassHandle this_oop, ClassState state, TRAPS); + static void call_class_initializer_impl (instanceKlassHandle this_oop, TRAPS); + static klassOop array_klass_impl (instanceKlassHandle this_oop, bool or_null, int n, TRAPS); + static void do_local_static_fields_impl (instanceKlassHandle this_oop, void f(fieldDescriptor* fd, TRAPS), TRAPS); + /* jni_id_for_impl for jfieldID only */ + static JNIid* jni_id_for_impl (instanceKlassHandle this_oop, int offset); + + // Returns the array class for the n'th dimension + klassOop array_klass_impl(bool or_null, int n, TRAPS); + + // Returns the array class with this class as element type + klassOop array_klass_impl(bool or_null, TRAPS); + +public: + // sharing support + virtual void remove_unshareable_info(); + void field_names_and_sigs_iterate(OopClosure* closure); + + // jvm support + jint compute_modifier_flags(TRAPS) const; + +public: + // JVMTI support + jint jvmti_class_status() const; + +#ifndef PRODUCT + public: + // Printing + void oop_print_on (oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); + + void print_dependent_nmethods(bool verbose = false); + bool is_dependent_nmethod(nmethod* nm); +#endif + + public: + // Verification + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); + +#ifndef PRODUCT + static void verify_class_klass_nonstatic_oop_maps(klassOop k) PRODUCT_RETURN; +#endif +}; + +inline methodOop instanceKlass::method_at_vtable(int index) { +#ifndef PRODUCT + assert(index >= 0, "valid vtable index"); + if (DebugVtables) { + verify_vtable_index(index); + } +#endif + vtableEntry* ve = (vtableEntry*)start_of_vtable(); + return ve[index].method(); +} + +inline typeArrayOop instanceKlass::get_method_annotations_from(int idnum, objArrayOop annos) { + if (annos == NULL || annos->length() <= idnum) { + return NULL; + } + return typeArrayOop(annos->obj_at(idnum)); +} + +// for adding methods +// UNSET_IDNUM return means no more ids available +inline u2 instanceKlass::next_method_idnum() { + if (_idnum_allocated_count == constMethodOopDesc::MAX_IDNUM) { + return constMethodOopDesc::UNSET_IDNUM; // no more ids available + } else { + return _idnum_allocated_count++; + } +} + + +// ValueObjs embedded in klass. Describes where oops are located in instances of this klass. + +class OopMapBlock VALUE_OBJ_CLASS_SPEC { + private: + jushort _offset; // Offset of first oop in oop-map block + jushort _length; // Length of oop-map block + public: + // Accessors + jushort offset() const { return _offset; } + void set_offset(jushort offset) { _offset = offset; } + + jushort length() const { return _length; } + void set_length(jushort length) { _length = length; } +}; + +/* JNIid class for jfieldIDs only */ +class JNIid: public CHeapObj { + friend class VMStructs; + private: + klassOop _holder; + JNIid* _next; + int _offset; +#ifdef ASSERT + bool _is_static_field_id; +#endif + + public: + // Accessors + klassOop holder() const { return _holder; } + int offset() const { return _offset; } + JNIid* next() { return _next; } + // Constructor + JNIid(klassOop holder, int offset, JNIid* next); + // Identifier lookup + JNIid* find(int offset); + + // Garbage collection support + oop* holder_addr() { return (oop*)&_holder; } + void oops_do(OopClosure* f); + static void deallocate(JNIid* id); + // Debugging +#ifdef ASSERT + bool is_static_field_id() const { return _is_static_field_id; } + void set_is_static_field_id() { _is_static_field_id = true; } +#endif + void verify(klassOop holder); +}; + + +// If breakpoints are more numerous than just JVMTI breakpoints, +// consider compressing this data structure. +// It is currently a simple linked list defined in methodOop.hpp. + +class BreakpointInfo; + + +// A collection point for interesting information about the previous +// version(s) of an instanceKlass. This class uses weak references to +// the information so that the information may be collected as needed +// by the system. A GrowableArray of PreviousVersionNodes is attached +// to the instanceKlass as needed. See PreviousVersionWalker below. +class PreviousVersionNode : public CHeapObj { + private: + jweak _prev_constant_pool; + // If the previous version of the instanceKlass doesn't have any + // EMCP methods, then _prev_EMCP_methods will be NULL. If all the + // EMCP methods have been collected, then _prev_EMCP_methods can + // have a length of zero. + GrowableArray* _prev_EMCP_methods; + +public: + PreviousVersionNode(jweak prev_constant_pool, + GrowableArray* prev_EMCP_methods); + ~PreviousVersionNode(); + jweak prev_constant_pool() const { + return _prev_constant_pool; + } + GrowableArray* prev_EMCP_methods() const { + return _prev_EMCP_methods; + } +}; + + +// A Handle-ized version of PreviousVersionNode. +class PreviousVersionInfo : public ResourceObj { + private: + constantPoolHandle _prev_constant_pool_handle; + // If the previous version of the instanceKlass doesn't have any + // EMCP methods, then _prev_EMCP_methods will be NULL. Since the + // methods cannot be collected while we hold a handle, + // _prev_EMCP_methods should never have a length of zero. + GrowableArray* _prev_EMCP_method_handles; + +public: + PreviousVersionInfo(PreviousVersionNode *pv_node); + ~PreviousVersionInfo(); + constantPoolHandle prev_constant_pool_handle() const { + return _prev_constant_pool_handle; + } + GrowableArray* prev_EMCP_method_handles() const { + return _prev_EMCP_method_handles; + } +}; + + +// Helper object for walking previous versions. This helper cleans up +// the Handles that it allocates when the helper object is destroyed. +// The PreviousVersionInfo object returned by next_previous_version() +// is only valid until a subsequent call to next_previous_version() or +// the helper object is destroyed. +class PreviousVersionWalker : public StackObj { + private: + GrowableArray* _previous_versions; + int _current_index; + // Fields for cleaning up when we are done walking the previous versions: + // A HandleMark for the PreviousVersionInfo handles: + HandleMark _hm; + + // It would be nice to have a ResourceMark field in this helper also, + // but the ResourceMark code says to be careful to delete handles held + // in GrowableArrays _before_ deleting the GrowableArray. Since we + // can't guarantee the order in which the fields are destroyed, we + // have to let the creator of the PreviousVersionWalker object do + // the right thing. Also, adding a ResourceMark here causes an + // include loop. + + // A pointer to the current info object so we can handle the deletes. + PreviousVersionInfo * _current_p; + + public: + PreviousVersionWalker(instanceKlass *ik); + ~PreviousVersionWalker(); + + // Return the interesting information for the next previous version + // of the klass. Returns NULL if there are no more previous versions. + PreviousVersionInfo* next_previous_version(); +}; diff --git a/hotspot/src/share/vm/oops/instanceKlassKlass.cpp b/hotspot/src/share/vm/oops/instanceKlassKlass.cpp new file mode 100644 index 00000000000..144ced69e1e --- /dev/null +++ b/hotspot/src/share/vm/oops/instanceKlassKlass.cpp @@ -0,0 +1,809 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_instanceKlassKlass.cpp.incl" + +klassOop instanceKlassKlass::create_klass(TRAPS) { + instanceKlassKlass o; + KlassHandle h_this_klass(THREAD, Universe::klassKlassObj()); + KlassHandle k = base_create_klass(h_this_klass, header_size(), o.vtbl_value(), CHECK_NULL); + // Make sure size calculation is right + assert(k()->size() == align_object_size(header_size()), "wrong size for object"); + java_lang_Class::create_mirror(k, CHECK_NULL); // Allocate mirror + return k(); +} + +int instanceKlassKlass::oop_size(oop obj) const { + assert(obj->is_klass(), "must be klass"); + return instanceKlass::cast(klassOop(obj))->object_size(); +} + +bool instanceKlassKlass::oop_is_parsable(oop obj) const { + assert(obj->is_klass(), "must be klass"); + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + return (!ik->null_vtbl()) && ik->object_is_parsable(); +} + +void instanceKlassKlass::iterate_c_heap_oops(instanceKlass* ik, + OopClosure* closure) { + if (ik->oop_map_cache() != NULL) { + ik->oop_map_cache()->oop_iterate(closure); + } + + if (ik->jni_ids() != NULL) { + ik->jni_ids()->oops_do(closure); + } +} + +void instanceKlassKlass::oop_follow_contents(oop obj) { + assert(obj->is_klass(),"must be a klass"); + assert(klassOop(obj)->klass_part()->oop_is_instance_slow(), "must be instance klass"); + + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + ik->follow_static_fields(); + { + HandleMark hm; + ik->vtable()->oop_follow_contents(); + ik->itable()->oop_follow_contents(); + } + + MarkSweep::mark_and_push(ik->adr_array_klasses()); + MarkSweep::mark_and_push(ik->adr_methods()); + MarkSweep::mark_and_push(ik->adr_method_ordering()); + MarkSweep::mark_and_push(ik->adr_local_interfaces()); + MarkSweep::mark_and_push(ik->adr_transitive_interfaces()); + MarkSweep::mark_and_push(ik->adr_fields()); + MarkSweep::mark_and_push(ik->adr_constants()); + MarkSweep::mark_and_push(ik->adr_class_loader()); + MarkSweep::mark_and_push(ik->adr_source_file_name()); + MarkSweep::mark_and_push(ik->adr_source_debug_extension()); + MarkSweep::mark_and_push(ik->adr_inner_classes()); + MarkSweep::mark_and_push(ik->adr_protection_domain()); + MarkSweep::mark_and_push(ik->adr_signers()); + MarkSweep::mark_and_push(ik->adr_generic_signature()); + MarkSweep::mark_and_push(ik->adr_class_annotations()); + MarkSweep::mark_and_push(ik->adr_fields_annotations()); + MarkSweep::mark_and_push(ik->adr_methods_annotations()); + MarkSweep::mark_and_push(ik->adr_methods_parameter_annotations()); + MarkSweep::mark_and_push(ik->adr_methods_default_annotations()); + + // We do not follow adr_implementors() here. It is followed later + // in instanceKlass::follow_weak_klass_links() + + klassKlass::oop_follow_contents(obj); + + iterate_c_heap_oops(ik, &MarkSweep::mark_and_push_closure); +} + +#ifndef SERIALGC +void instanceKlassKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert(obj->is_klass(),"must be a klass"); + assert(klassOop(obj)->klass_part()->oop_is_instance_slow(), "must be instance klass"); + + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + ik->follow_static_fields(cm); + ik->vtable()->oop_follow_contents(cm); + ik->itable()->oop_follow_contents(cm); + + PSParallelCompact::mark_and_push(cm, ik->adr_array_klasses()); + PSParallelCompact::mark_and_push(cm, ik->adr_methods()); + PSParallelCompact::mark_and_push(cm, ik->adr_method_ordering()); + PSParallelCompact::mark_and_push(cm, ik->adr_local_interfaces()); + PSParallelCompact::mark_and_push(cm, ik->adr_transitive_interfaces()); + PSParallelCompact::mark_and_push(cm, ik->adr_fields()); + PSParallelCompact::mark_and_push(cm, ik->adr_constants()); + PSParallelCompact::mark_and_push(cm, ik->adr_class_loader()); + PSParallelCompact::mark_and_push(cm, ik->adr_source_file_name()); + PSParallelCompact::mark_and_push(cm, ik->adr_source_debug_extension()); + PSParallelCompact::mark_and_push(cm, ik->adr_inner_classes()); + PSParallelCompact::mark_and_push(cm, ik->adr_protection_domain()); + PSParallelCompact::mark_and_push(cm, ik->adr_signers()); + PSParallelCompact::mark_and_push(cm, ik->adr_generic_signature()); + PSParallelCompact::mark_and_push(cm, ik->adr_class_annotations()); + PSParallelCompact::mark_and_push(cm, ik->adr_fields_annotations()); + PSParallelCompact::mark_and_push(cm, ik->adr_methods_annotations()); + PSParallelCompact::mark_and_push(cm, ik->adr_methods_parameter_annotations()); + PSParallelCompact::mark_and_push(cm, ik->adr_methods_default_annotations()); + + // We do not follow adr_implementor() here. It is followed later + // in instanceKlass::follow_weak_klass_links() + + klassKlass::oop_follow_contents(cm, obj); + + PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); + iterate_c_heap_oops(ik, &mark_and_push_closure); +} +#endif // SERIALGC + +int instanceKlassKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert(obj->is_klass(),"must be a klass"); + assert(klassOop(obj)->klass_part()->oop_is_instance_slow(), "must be instance klass"); + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = ik->object_size(); + + ik->iterate_static_fields(blk); + ik->vtable()->oop_oop_iterate(blk); + ik->itable()->oop_oop_iterate(blk); + + blk->do_oop(ik->adr_array_klasses()); + blk->do_oop(ik->adr_methods()); + blk->do_oop(ik->adr_method_ordering()); + blk->do_oop(ik->adr_local_interfaces()); + blk->do_oop(ik->adr_transitive_interfaces()); + blk->do_oop(ik->adr_fields()); + blk->do_oop(ik->adr_constants()); + blk->do_oop(ik->adr_class_loader()); + blk->do_oop(ik->adr_protection_domain()); + blk->do_oop(ik->adr_signers()); + blk->do_oop(ik->adr_source_file_name()); + blk->do_oop(ik->adr_source_debug_extension()); + blk->do_oop(ik->adr_inner_classes()); + for (int i = 0; i < instanceKlass::implementors_limit; i++) { + blk->do_oop(&ik->adr_implementors()[i]); + } + blk->do_oop(ik->adr_generic_signature()); + blk->do_oop(ik->adr_class_annotations()); + blk->do_oop(ik->adr_fields_annotations()); + blk->do_oop(ik->adr_methods_annotations()); + blk->do_oop(ik->adr_methods_parameter_annotations()); + blk->do_oop(ik->adr_methods_default_annotations()); + + klassKlass::oop_oop_iterate(obj, blk); + + if(ik->oop_map_cache() != NULL) ik->oop_map_cache()->oop_iterate(blk); + return size; +} + +int instanceKlassKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, + MemRegion mr) { + assert(obj->is_klass(),"must be a klass"); + assert(klassOop(obj)->klass_part()->oop_is_instance_slow(), "must be instance klass"); + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = ik->object_size(); + + ik->iterate_static_fields(blk, mr); + ik->vtable()->oop_oop_iterate_m(blk, mr); + ik->itable()->oop_oop_iterate_m(blk, mr); + + oop* adr; + adr = ik->adr_array_klasses(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_methods(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_method_ordering(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_local_interfaces(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_transitive_interfaces(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_fields(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_constants(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_class_loader(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_protection_domain(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_signers(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_source_file_name(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_source_debug_extension(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_inner_classes(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_implementors(); + for (int i = 0; i < instanceKlass::implementors_limit; i++) { + if (mr.contains(&adr[i])) blk->do_oop(&adr[i]); + } + adr = ik->adr_generic_signature(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_class_annotations(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_fields_annotations(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_methods_annotations(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_methods_parameter_annotations(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_methods_default_annotations(); + if (mr.contains(adr)) blk->do_oop(adr); + + klassKlass::oop_oop_iterate_m(obj, blk, mr); + + if(ik->oop_map_cache() != NULL) ik->oop_map_cache()->oop_iterate(blk, mr); + return size; +} + +int instanceKlassKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_klass(),"must be a klass"); + assert(klassOop(obj)->klass_part()->oop_is_instance_slow(), "must be instance klass"); + + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + ik->adjust_static_fields(); + ik->vtable()->oop_adjust_pointers(); + ik->itable()->oop_adjust_pointers(); + + MarkSweep::adjust_pointer(ik->adr_array_klasses()); + MarkSweep::adjust_pointer(ik->adr_methods()); + MarkSweep::adjust_pointer(ik->adr_method_ordering()); + MarkSweep::adjust_pointer(ik->adr_local_interfaces()); + MarkSweep::adjust_pointer(ik->adr_transitive_interfaces()); + MarkSweep::adjust_pointer(ik->adr_fields()); + MarkSweep::adjust_pointer(ik->adr_constants()); + MarkSweep::adjust_pointer(ik->adr_class_loader()); + MarkSweep::adjust_pointer(ik->adr_protection_domain()); + MarkSweep::adjust_pointer(ik->adr_signers()); + MarkSweep::adjust_pointer(ik->adr_source_file_name()); + MarkSweep::adjust_pointer(ik->adr_source_debug_extension()); + MarkSweep::adjust_pointer(ik->adr_inner_classes()); + for (int i = 0; i < instanceKlass::implementors_limit; i++) { + MarkSweep::adjust_pointer(&ik->adr_implementors()[i]); + } + MarkSweep::adjust_pointer(ik->adr_generic_signature()); + MarkSweep::adjust_pointer(ik->adr_class_annotations()); + MarkSweep::adjust_pointer(ik->adr_fields_annotations()); + MarkSweep::adjust_pointer(ik->adr_methods_annotations()); + MarkSweep::adjust_pointer(ik->adr_methods_parameter_annotations()); + MarkSweep::adjust_pointer(ik->adr_methods_default_annotations()); + + iterate_c_heap_oops(ik, &MarkSweep::adjust_root_pointer_closure); + + return klassKlass::oop_adjust_pointers(obj); +} + +#ifndef SERIALGC +void instanceKlassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(!pm->depth_first(), "invariant"); + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + ik->copy_static_fields(pm); + + oop* loader_addr = ik->adr_class_loader(); + if (PSScavenge::should_scavenge(*loader_addr)) { + pm->claim_or_forward_breadth(loader_addr); + } + + oop* pd_addr = ik->adr_protection_domain(); + if (PSScavenge::should_scavenge(*pd_addr)) { + pm->claim_or_forward_breadth(pd_addr); + } + + oop* sg_addr = ik->adr_signers(); + if (PSScavenge::should_scavenge(*sg_addr)) { + pm->claim_or_forward_breadth(sg_addr); + } + + klassKlass::oop_copy_contents(pm, obj); +} + +void instanceKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(pm->depth_first(), "invariant"); + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + ik->push_static_fields(pm); + + oop* loader_addr = ik->adr_class_loader(); + if (PSScavenge::should_scavenge(*loader_addr)) { + pm->claim_or_forward_depth(loader_addr); + } + + oop* pd_addr = ik->adr_protection_domain(); + if (PSScavenge::should_scavenge(*pd_addr)) { + pm->claim_or_forward_depth(pd_addr); + } + + oop* sg_addr = ik->adr_signers(); + if (PSScavenge::should_scavenge(*sg_addr)) { + pm->claim_or_forward_depth(sg_addr); + } + + klassKlass::oop_copy_contents(pm, obj); +} + +int instanceKlassKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert(obj->is_klass(),"must be a klass"); + assert(klassOop(obj)->klass_part()->oop_is_instance_slow(), + "must be instance klass"); + + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + ik->update_static_fields(); + ik->vtable()->oop_update_pointers(cm); + ik->itable()->oop_update_pointers(cm); + + oop* const beg_oop = ik->oop_block_beg(); + oop* const end_oop = ik->oop_block_end(); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } + + OopClosure* closure = PSParallelCompact::adjust_root_pointer_closure(); + iterate_c_heap_oops(ik, closure); + + klassKlass::oop_update_pointers(cm, obj); + return ik->object_size(); +} + +int instanceKlassKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, + HeapWord* end_addr) { + assert(obj->is_klass(),"must be a klass"); + assert(klassOop(obj)->klass_part()->oop_is_instance_slow(), + "must be instance klass"); + + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + ik->update_static_fields(beg_addr, end_addr); + ik->vtable()->oop_update_pointers(cm, beg_addr, end_addr); + ik->itable()->oop_update_pointers(cm, beg_addr, end_addr); + + oop* const beg_oop = MAX2((oop*)beg_addr, ik->oop_block_beg()); + oop* const end_oop = MIN2((oop*)end_addr, ik->oop_block_end()); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } + + // The oop_map_cache, jni_ids and jni_id_map are allocated from the C heap, + // and so don't lie within any 'Chunk' boundaries. Update them when the + // lowest addressed oop in the instanceKlass 'oop_block' is updated. + if (beg_oop == ik->oop_block_beg()) { + OopClosure* closure = PSParallelCompact::adjust_root_pointer_closure(); + iterate_c_heap_oops(ik, closure); + } + + klassKlass::oop_update_pointers(cm, obj, beg_addr, end_addr); + return ik->object_size(); +} +#endif // SERIALGC + +klassOop instanceKlassKlass::allocate_instance_klass(int vtable_len, int itable_len, int static_field_size, + int nonstatic_oop_map_size, ReferenceType rt, TRAPS) { + + int size = instanceKlass::object_size(align_object_offset(vtable_len) + align_object_offset(itable_len) + static_field_size + nonstatic_oop_map_size); + + // Allocation + KlassHandle h_this_klass(THREAD, as_klassOop()); + KlassHandle k; + if (rt == REF_NONE) { + // regular klass + instanceKlass o; + k = base_create_klass(h_this_klass, size, o.vtbl_value(), CHECK_NULL); + } else { + // reference klass + instanceRefKlass o; + k = base_create_klass(h_this_klass, size, o.vtbl_value(), CHECK_NULL); + } + { + No_Safepoint_Verifier no_safepoint; // until k becomes parsable + instanceKlass* ik = (instanceKlass*) k()->klass_part(); + assert(!k()->is_parsable(), "not expecting parsability yet."); + + // The sizes of these these three variables are used for determining the + // size of the instanceKlassOop. It is critical that these are set to the right + // sizes before the first GC, i.e., when we allocate the mirror. + ik->set_vtable_length(vtable_len); + ik->set_itable_length(itable_len); + ik->set_static_field_size(static_field_size); + ik->set_nonstatic_oop_map_size(nonstatic_oop_map_size); + assert(k()->size() == size, "wrong size for object"); + + ik->set_array_klasses(NULL); + ik->set_methods(NULL); + ik->set_method_ordering(NULL); + ik->set_local_interfaces(NULL); + ik->set_transitive_interfaces(NULL); + ik->init_implementor(); + ik->set_fields(NULL); + ik->set_constants(NULL); + ik->set_class_loader(NULL); + ik->set_protection_domain(NULL); + ik->set_signers(NULL); + ik->set_source_file_name(NULL); + ik->set_source_debug_extension(NULL); + ik->set_inner_classes(NULL); + ik->set_static_oop_field_size(0); + ik->set_nonstatic_field_size(0); + ik->set_is_marked_dependent(false); + ik->set_init_state(instanceKlass::allocated); + ik->set_init_thread(NULL); + ik->set_reference_type(rt); + ik->set_oop_map_cache(NULL); + ik->set_jni_ids(NULL); + ik->set_osr_nmethods_head(NULL); + ik->set_breakpoints(NULL); + ik->init_previous_versions(); + ik->set_generic_signature(NULL); + ik->release_set_methods_jmethod_ids(NULL); + ik->release_set_methods_cached_itable_indices(NULL); + ik->set_class_annotations(NULL); + ik->set_fields_annotations(NULL); + ik->set_methods_annotations(NULL); + ik->set_methods_parameter_annotations(NULL); + ik->set_methods_default_annotations(NULL); + ik->set_enclosing_method_indices(0, 0); + ik->set_jvmti_cached_class_field_map(NULL); + ik->set_initial_method_idnum(0); + assert(k()->is_parsable(), "should be parsable here."); + + // initialize the non-header words to zero + intptr_t* p = (intptr_t*)k(); + for (int index = instanceKlass::header_size(); index < size; index++) { + p[index] = NULL_WORD; + } + + // To get verify to work - must be set to partial loaded before first GC point. + k()->set_partially_loaded(); + } + + // GC can happen here + java_lang_Class::create_mirror(k, CHECK_NULL); // Allocate mirror + return k(); +} + + + +#ifndef PRODUCT + +// Printing + +static const char* state_names[] = { + "unparseable_by_gc", "allocated", "loaded", "linked", "being_initialized", "fully_initialized", "initialization_error" +}; + + +void instanceKlassKlass::oop_print_on(oop obj, outputStream* st) { + assert(obj->is_klass(), "must be klass"); + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + klassKlass::oop_print_on(obj, st); + + st->print(" - instance size: %d", ik->size_helper()); st->cr(); + st->print(" - klass size: %d", ik->object_size()); st->cr(); + st->print(" - access: "); ik->access_flags().print_on(st); st->cr(); + st->print(" - state: "); st->print_cr(state_names[ik->_init_state]); + st->print(" - name: "); ik->name()->print_value_on(st); st->cr(); + st->print(" - super: "); ik->super()->print_value_on(st); st->cr(); + st->print(" - sub: "); + Klass* sub = ik->subklass(); + int n; + for (n = 0; sub != NULL; n++, sub = sub->next_sibling()) { + if (n < MaxSubklassPrintSize) { + sub->as_klassOop()->print_value_on(st); + st->print(" "); + } + } + if (n >= MaxSubklassPrintSize) st->print("(%d more klasses...)", n - MaxSubklassPrintSize); + st->cr(); + + if (ik->is_interface()) { + st->print_cr(" - nof implementors: %d", ik->nof_implementors()); + int print_impl = 0; + for (int i = 0; i < instanceKlass::implementors_limit; i++) { + if (ik->implementor(i) != NULL) { + if (++print_impl == 1) + st->print_cr(" - implementor: "); + st->print(" "); + ik->implementor(i)->print_value_on(st); + } + } + if (print_impl > 0) st->cr(); + } + + st->print(" - arrays: "); ik->array_klasses()->print_value_on(st); st->cr(); + st->print(" - methods: "); ik->methods()->print_value_on(st); st->cr(); + if (Verbose) { + objArrayOop methods = ik->methods(); + for(int i = 0; i < methods->length(); i++) { + tty->print("%d : ", i); methods->obj_at(i)->print_value(); tty->cr(); + } + } + st->print(" - method ordering: "); ik->method_ordering()->print_value_on(st); st->cr(); + st->print(" - local interfaces: "); ik->local_interfaces()->print_value_on(st); st->cr(); + st->print(" - trans. interfaces: "); ik->transitive_interfaces()->print_value_on(st); st->cr(); + st->print(" - constants: "); ik->constants()->print_value_on(st); st->cr(); + st->print(" - class loader: "); ik->class_loader()->print_value_on(st); st->cr(); + st->print(" - protection domain: "); ik->protection_domain()->print_value_on(st); st->cr(); + st->print(" - signers: "); ik->signers()->print_value_on(st); st->cr(); + if (ik->source_file_name() != NULL) { + st->print(" - source file: "); + ik->source_file_name()->print_value_on(st); + st->cr(); + } + if (ik->source_debug_extension() != NULL) { + st->print(" - source debug extension: "); + ik->source_debug_extension()->print_value_on(st); + st->cr(); + } + + st->print_cr(" - previous version: "); + { + ResourceMark rm; + // PreviousVersionInfo objects returned via PreviousVersionWalker + // contain a GrowableArray of handles. We have to clean up the + // GrowableArray _after_ the PreviousVersionWalker destructor + // has destroyed the handles. + { + PreviousVersionWalker pvw(ik); + for (PreviousVersionInfo * pv_info = pvw.next_previous_version(); + pv_info != NULL; pv_info = pvw.next_previous_version()) { + pv_info->prev_constant_pool_handle()()->print_value_on(st); + } + st->cr(); + } // pvw is cleaned up + } // rm is cleaned up + + if (ik->generic_signature() != NULL) { + st->print(" - generic signature: "); + ik->generic_signature()->print_value_on(st); + } + st->print(" - inner classes: "); ik->inner_classes()->print_value_on(st); st->cr(); + st->print(" - java mirror: "); ik->java_mirror()->print_value_on(st); st->cr(); + st->print(" - vtable length %d (start addr: " INTPTR_FORMAT ")", ik->vtable_length(), ik->start_of_vtable()); st->cr(); + st->print(" - itable length %d (start addr: " INTPTR_FORMAT ")", ik->itable_length(), ik->start_of_itable()); st->cr(); + st->print_cr(" - static fields:"); + FieldPrinter print_static_field(st); + ik->do_local_static_fields(&print_static_field); + st->print_cr(" - non-static fields:"); + FieldPrinter print_nonstatic_field(st, obj); + ik->do_nonstatic_fields(&print_nonstatic_field); + + st->print(" - static oop maps: "); + if (ik->static_oop_field_size() > 0) { + int first_offset = ik->offset_of_static_fields(); + st->print("%d-%d", first_offset, first_offset + ik->static_oop_field_size() - 1); + } + st->cr(); + + st->print(" - non-static oop maps: "); + OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); + OopMapBlock* end_map = map + ik->nonstatic_oop_map_size(); + while (map < end_map) { + st->print("%d-%d ", map->offset(), map->offset() + oopSize*(map->length() - 1)); + map++; + } + st->cr(); +} + + +void instanceKlassKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_klass(), "must be klass"); + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + ik->name()->print_value_on(st); +} + +#endif // PRODUCT + +const char* instanceKlassKlass::internal_name() const { + return "{instance class}"; +} + +// Verification + + +class VerifyFieldClosure: public OopClosure { + public: + void do_oop(oop* p) { + guarantee(Universe::heap()->is_in(p), "should be in heap"); + guarantee((*p)->is_oop_or_null(), "should be in heap"); + } +}; + + +void instanceKlassKlass::oop_verify_on(oop obj, outputStream* st) { + klassKlass::oop_verify_on(obj, st); + if (!obj->partially_loaded()) { + Thread *thread = Thread::current(); + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + +#ifndef PRODUCT + // Avoid redundant verifies + if (ik->_verify_count == Universe::verify_count()) return; + ik->_verify_count = Universe::verify_count(); +#endif + // Verify that klass is present in SystemDictionary + if (ik->is_loaded()) { + symbolHandle h_name (thread, ik->name()); + Handle h_loader (thread, ik->class_loader()); + Handle h_obj(thread, obj); + SystemDictionary::verify_obj_klass_present(h_obj, h_name, h_loader); + } + + // Verify static fields + VerifyFieldClosure blk; + ik->iterate_static_fields(&blk); + + // Verify vtables + if (ik->is_linked()) { + ResourceMark rm(thread); + // $$$ This used to be done only for m/s collections. Doing it + // always seemed a valid generalization. (DLD -- 6/00) + ik->vtable()->verify(st); + } + + // Verify oop map cache + if (ik->oop_map_cache() != NULL) { + ik->oop_map_cache()->verify(); + } + + // Verify first subklass + if (ik->subklass_oop() != NULL) { + guarantee(ik->subklass_oop()->is_perm(), "should be in permspace"); + guarantee(ik->subklass_oop()->is_klass(), "should be klass"); + } + + // Verify siblings + klassOop super = ik->super(); + Klass* sib = ik->next_sibling(); + int sib_count = 0; + while (sib != NULL) { + if (sib == ik) { + fatal1("subclass cycle of length %d", sib_count); + } + if (sib_count >= 100000) { + fatal1("suspiciously long subclass list %d", sib_count); + } + guarantee(sib->as_klassOop()->is_klass(), "should be klass"); + guarantee(sib->as_klassOop()->is_perm(), "should be in permspace"); + guarantee(sib->super() == super, "siblings should have same superklass"); + sib = sib->next_sibling(); + } + + // Verify implementor fields + bool saw_null_impl = false; + for (int i = 0; i < instanceKlass::implementors_limit; i++) { + klassOop im = ik->implementor(i); + if (im == NULL) { saw_null_impl = true; continue; } + guarantee(!saw_null_impl, "non-nulls must preceded all nulls"); + guarantee(ik->is_interface(), "only interfaces should have implementor set"); + guarantee(i < ik->nof_implementors(), "should only have one implementor"); + guarantee(im->is_perm(), "should be in permspace"); + guarantee(im->is_klass(), "should be klass"); + guarantee(!Klass::cast(klassOop(im))->is_interface(), "implementors cannot be interfaces"); + } + + // Verify local interfaces + objArrayOop local_interfaces = ik->local_interfaces(); + guarantee(local_interfaces->is_perm(), "should be in permspace"); + guarantee(local_interfaces->is_objArray(), "should be obj array"); + int j; + for (j = 0; j < local_interfaces->length(); j++) { + oop e = local_interfaces->obj_at(j); + guarantee(e->is_klass() && Klass::cast(klassOop(e))->is_interface(), "invalid local interface"); + } + + // Verify transitive interfaces + objArrayOop transitive_interfaces = ik->transitive_interfaces(); + guarantee(transitive_interfaces->is_perm(), "should be in permspace"); + guarantee(transitive_interfaces->is_objArray(), "should be obj array"); + for (j = 0; j < transitive_interfaces->length(); j++) { + oop e = transitive_interfaces->obj_at(j); + guarantee(e->is_klass() && Klass::cast(klassOop(e))->is_interface(), "invalid transitive interface"); + } + + // Verify methods + objArrayOop methods = ik->methods(); + guarantee(methods->is_perm(), "should be in permspace"); + guarantee(methods->is_objArray(), "should be obj array"); + for (j = 0; j < methods->length(); j++) { + guarantee(methods->obj_at(j)->is_method(), "non-method in methods array"); + } + for (j = 0; j < methods->length() - 1; j++) { + methodOop m1 = methodOop(methods->obj_at(j)); + methodOop m2 = methodOop(methods->obj_at(j + 1)); + guarantee(m1->name()->fast_compare(m2->name()) <= 0, "methods not sorted correctly"); + } + + // Verify method ordering + typeArrayOop method_ordering = ik->method_ordering(); + guarantee(method_ordering->is_perm(), "should be in permspace"); + guarantee(method_ordering->is_typeArray(), "should be type array"); + int length = method_ordering->length(); + if (JvmtiExport::can_maintain_original_method_order()) { + guarantee(length == methods->length(), "invalid method ordering length"); + jlong sum = 0; + for (j = 0; j < length; j++) { + int original_index = method_ordering->int_at(j); + guarantee(original_index >= 0 && original_index < length, "invalid method ordering index"); + sum += original_index; + } + // Verify sum of indices 0,1,...,length-1 + guarantee(sum == ((jlong)length*(length-1))/2, "invalid method ordering sum"); + } else { + guarantee(length == 0, "invalid method ordering length"); + } + + // Verify JNI static field identifiers + if (ik->jni_ids() != NULL) { + ik->jni_ids()->verify(ik->as_klassOop()); + } + + // Verify other fields + if (ik->array_klasses() != NULL) { + guarantee(ik->array_klasses()->is_perm(), "should be in permspace"); + guarantee(ik->array_klasses()->is_klass(), "should be klass"); + } + guarantee(ik->fields()->is_perm(), "should be in permspace"); + guarantee(ik->fields()->is_typeArray(), "should be type array"); + guarantee(ik->constants()->is_perm(), "should be in permspace"); + guarantee(ik->constants()->is_constantPool(), "should be constant pool"); + guarantee(ik->inner_classes()->is_perm(), "should be in permspace"); + guarantee(ik->inner_classes()->is_typeArray(), "should be type array"); + if (ik->source_file_name() != NULL) { + guarantee(ik->source_file_name()->is_perm(), "should be in permspace"); + guarantee(ik->source_file_name()->is_symbol(), "should be symbol"); + } + if (ik->source_debug_extension() != NULL) { + guarantee(ik->source_debug_extension()->is_perm(), "should be in permspace"); + guarantee(ik->source_debug_extension()->is_symbol(), "should be symbol"); + } + if (ik->protection_domain() != NULL) { + guarantee(ik->protection_domain()->is_oop(), "should be oop"); + } + if (ik->signers() != NULL) { + guarantee(ik->signers()->is_objArray(), "should be obj array"); + } + if (ik->generic_signature() != NULL) { + guarantee(ik->generic_signature()->is_perm(), "should be in permspace"); + guarantee(ik->generic_signature()->is_symbol(), "should be symbol"); + } + if (ik->class_annotations() != NULL) { + guarantee(ik->class_annotations()->is_typeArray(), "should be type array"); + } + if (ik->fields_annotations() != NULL) { + guarantee(ik->fields_annotations()->is_objArray(), "should be obj array"); + } + if (ik->methods_annotations() != NULL) { + guarantee(ik->methods_annotations()->is_objArray(), "should be obj array"); + } + if (ik->methods_parameter_annotations() != NULL) { + guarantee(ik->methods_parameter_annotations()->is_objArray(), "should be obj array"); + } + if (ik->methods_default_annotations() != NULL) { + guarantee(ik->methods_default_annotations()->is_objArray(), "should be obj array"); + } + } +} + + +bool instanceKlassKlass::oop_partially_loaded(oop obj) const { + assert(obj->is_klass(), "object must be klass"); + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + assert(ik->oop_is_instance(), "object must be instanceKlass"); + return ik->transitive_interfaces() == (objArrayOop) obj; // Check whether transitive_interfaces points to self +} + + +// The transitive_interfaces is the last field set when loading an object. +void instanceKlassKlass::oop_set_partially_loaded(oop obj) { + assert(obj->is_klass(), "object must be klass"); + instanceKlass* ik = instanceKlass::cast(klassOop(obj)); + // Set the layout helper to a place-holder value, until fuller initialization. + // (This allows asserts in oop_is_instance to succeed.) + ik->set_layout_helper(Klass::instance_layout_helper(0, true)); + assert(ik->oop_is_instance(), "object must be instanceKlass"); + assert(ik->transitive_interfaces() == NULL, "just checking"); + ik->set_transitive_interfaces((objArrayOop) obj); // Temporarily set transitive_interfaces to point to self +} diff --git a/hotspot/src/share/vm/oops/instanceKlassKlass.hpp b/hotspot/src/share/vm/oops/instanceKlassKlass.hpp new file mode 100644 index 00000000000..8504a59da19 --- /dev/null +++ b/hotspot/src/share/vm/oops/instanceKlassKlass.hpp @@ -0,0 +1,86 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// An instanceKlassKlass is the klass of an instanceKlass + +class instanceKlassKlass : public klassKlass { + public: + // Dispatched operation + bool oop_is_klass() const { return true; } + bool oop_is_instanceKlass() const { return true; } + + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(instanceKlassKlass); + static klassOop create_klass(TRAPS); + klassOop allocate_instance_klass(int vtable_len, + int itable_len, + int static_field_size, + int nonstatic_oop_map_size, + ReferenceType rt, + TRAPS); + + // Casting from klassOop + static instanceKlassKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_klass(), "cast to instanceKlassKlass"); + return (instanceKlassKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(instanceKlassKlass)/HeapWordSize; } + int object_size() const { return align_object_size(header_size()); } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + bool oop_is_parsable(oop obj) const; + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + +private: + // Apply closure to the InstanceKlass oops that are outside the java heap. + inline void iterate_c_heap_oops(instanceKlass* ik, OopClosure* closure); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on(oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); +#endif + + public: + // Verification + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); + // tells whether obj is partially constructed (gc during class loading) + bool oop_partially_loaded(oop obj) const; + void oop_set_partially_loaded(oop obj); +}; diff --git a/hotspot/src/share/vm/oops/instanceOop.cpp b/hotspot/src/share/vm/oops/instanceOop.cpp new file mode 100644 index 00000000000..1d7c419d224 --- /dev/null +++ b/hotspot/src/share/vm/oops/instanceOop.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_instanceOop.cpp.incl" + +// <> diff --git a/hotspot/src/share/vm/oops/instanceOop.hpp b/hotspot/src/share/vm/oops/instanceOop.hpp new file mode 100644 index 00000000000..49cab9379d1 --- /dev/null +++ b/hotspot/src/share/vm/oops/instanceOop.hpp @@ -0,0 +1,31 @@ +/* + * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// An instanceOop is an instance of a Java Class +// Evaluating "new HashTable()" will create an instanceOop. + +class instanceOopDesc : public oopDesc { + public: + static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; } +}; diff --git a/hotspot/src/share/vm/oops/instanceRefKlass.cpp b/hotspot/src/share/vm/oops/instanceRefKlass.cpp new file mode 100644 index 00000000000..d98ecd2cf7a --- /dev/null +++ b/hotspot/src/share/vm/oops/instanceRefKlass.cpp @@ -0,0 +1,417 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_instanceRefKlass.cpp.incl" + +void instanceRefKlass::oop_follow_contents(oop obj) { + oop* referent_addr = java_lang_ref_Reference::referent_addr(obj); + oop referent = *referent_addr; + debug_only( + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr("instanceRefKlass::oop_follow_contents " INTPTR_FORMAT, (address)obj); + } + ) + if (referent != NULL) { + if (!referent->is_gc_marked() && + MarkSweep::ref_processor()-> + discover_reference(obj, reference_type())) { + // reference already enqueued, referent will be traversed later + instanceKlass::oop_follow_contents(obj); + debug_only( + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr(" Non NULL enqueued " INTPTR_FORMAT, (address)obj); + } + ) + return; + } else { + // treat referent as normal oop + debug_only( + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr(" Non NULL normal " INTPTR_FORMAT, (address)obj); + } + ) + MarkSweep::mark_and_push(referent_addr); + } + } + // treat next as normal oop. next is a link in the pending list. + oop* next_addr = java_lang_ref_Reference::next_addr(obj); + debug_only( + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr(" Process next as normal " INTPTR_FORMAT, next_addr); + } + ) + MarkSweep::mark_and_push(next_addr); + instanceKlass::oop_follow_contents(obj); +} + +#ifndef SERIALGC +void instanceRefKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + oop* referent_addr = java_lang_ref_Reference::referent_addr(obj); + oop referent = *referent_addr; + debug_only( + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr("instanceRefKlass::oop_follow_contents " INTPTR_FORMAT, (address)obj); + } + ) + if (referent != NULL) { + if (PSParallelCompact::mark_bitmap()->is_unmarked(referent) && + PSParallelCompact::ref_processor()-> + discover_reference(obj, reference_type())) { + // reference already enqueued, referent will be traversed later + instanceKlass::oop_follow_contents(cm, obj); + debug_only( + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr(" Non NULL enqueued " INTPTR_FORMAT, (address)obj); + } + ) + return; + } else { + // treat referent as normal oop + debug_only( + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr(" Non NULL normal " INTPTR_FORMAT, (address)obj); + } + ) + PSParallelCompact::mark_and_push(cm, referent_addr); + } + } + // treat next as normal oop. next is a link in the pending list. + oop* next_addr = java_lang_ref_Reference::next_addr(obj); + debug_only( + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr(" Process next as normal " INTPTR_FORMAT, next_addr); + } + ) + PSParallelCompact::mark_and_push(cm, next_addr); + instanceKlass::oop_follow_contents(cm, obj); +} +#endif // SERIALGC + + +int instanceRefKlass::oop_adjust_pointers(oop obj) { + int size = size_helper(); + instanceKlass::oop_adjust_pointers(obj); + + oop* referent_addr = java_lang_ref_Reference::referent_addr(obj); + MarkSweep::adjust_pointer(referent_addr); + oop* next_addr = java_lang_ref_Reference::next_addr(obj); + MarkSweep::adjust_pointer(next_addr); + oop* discovered_addr = java_lang_ref_Reference::discovered_addr(obj); + MarkSweep::adjust_pointer(discovered_addr); + +#ifdef ASSERT + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr("instanceRefKlass::oop_adjust_pointers obj " + INTPTR_FORMAT, (address)obj); + gclog_or_tty->print_cr(" referent_addr/* " INTPTR_FORMAT " / " + INTPTR_FORMAT, referent_addr, + referent_addr ? (address)*referent_addr : NULL); + gclog_or_tty->print_cr(" next_addr/* " INTPTR_FORMAT " / " + INTPTR_FORMAT, next_addr, + next_addr ? (address)*next_addr : NULL); + gclog_or_tty->print_cr(" discovered_addr/* " INTPTR_FORMAT " / " + INTPTR_FORMAT, discovered_addr, + discovered_addr ? (address)*discovered_addr : NULL); + } +#endif + + return size; +} + +#define InstanceRefKlass_OOP_OOP_ITERATE_DEFN(OopClosureType, nv_suffix) \ + \ +int instanceRefKlass:: \ +oop_oop_iterate##nv_suffix(oop obj, OopClosureType* closure) { \ + /* Get size before changing pointers */ \ + SpecializationStats::record_iterate_call##nv_suffix(SpecializationStats::irk);\ + \ + int size = instanceKlass::oop_oop_iterate##nv_suffix(obj, closure); \ + \ + oop* referent_addr = java_lang_ref_Reference::referent_addr(obj); \ + oop referent = *referent_addr; \ + if (referent != NULL) { \ + ReferenceProcessor* rp = closure->_ref_processor; \ + if (!referent->is_gc_marked() && (rp != NULL) && \ + rp->discover_reference(obj, reference_type())) { \ + return size; \ + } else { \ + /* treat referent as normal oop */ \ + SpecializationStats::record_do_oop_call##nv_suffix(SpecializationStats::irk);\ + closure->do_oop##nv_suffix(referent_addr); \ + } \ + } \ + \ + /* treat next as normal oop */ \ + oop* next_addr = java_lang_ref_Reference::next_addr(obj); \ + SpecializationStats::record_do_oop_call##nv_suffix(SpecializationStats::irk); \ + closure->do_oop##nv_suffix(next_addr); \ + return size; \ +} + +#define InstanceRefKlass_OOP_OOP_ITERATE_DEFN_m(OopClosureType, nv_suffix) \ + \ +int instanceRefKlass:: \ +oop_oop_iterate##nv_suffix##_m(oop obj, \ + OopClosureType* closure, \ + MemRegion mr) { \ + SpecializationStats::record_iterate_call##nv_suffix(SpecializationStats::irk);\ + \ + int size = instanceKlass::oop_oop_iterate##nv_suffix##_m(obj, closure, mr); \ + \ + oop* referent_addr = java_lang_ref_Reference::referent_addr(obj); \ + oop referent = *referent_addr; \ + if (referent != NULL && mr.contains(referent_addr)) { \ + ReferenceProcessor* rp = closure->_ref_processor; \ + if (!referent->is_gc_marked() && (rp != NULL) && \ + rp->discover_reference(obj, reference_type())) { \ + return size; \ + } else { \ + /* treat referent as normal oop */ \ + SpecializationStats::record_do_oop_call##nv_suffix(SpecializationStats::irk);\ + closure->do_oop##nv_suffix(referent_addr); \ + } \ + } \ + \ + /* treat next as normal oop */ \ + oop* next_addr = java_lang_ref_Reference::next_addr(obj); \ + if (mr.contains(next_addr)) { \ + SpecializationStats::record_do_oop_call##nv_suffix(SpecializationStats::irk);\ + closure->do_oop##nv_suffix(next_addr); \ + } \ + return size; \ +} + +ALL_OOP_OOP_ITERATE_CLOSURES_1(InstanceRefKlass_OOP_OOP_ITERATE_DEFN) +ALL_OOP_OOP_ITERATE_CLOSURES_3(InstanceRefKlass_OOP_OOP_ITERATE_DEFN) +ALL_OOP_OOP_ITERATE_CLOSURES_1(InstanceRefKlass_OOP_OOP_ITERATE_DEFN_m) +ALL_OOP_OOP_ITERATE_CLOSURES_3(InstanceRefKlass_OOP_OOP_ITERATE_DEFN_m) + + +#ifndef SERIALGC +void instanceRefKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(!pm->depth_first(), "invariant"); + oop* referent_addr = java_lang_ref_Reference::referent_addr(obj); + if (PSScavenge::should_scavenge(*referent_addr)) { + ReferenceProcessor* rp = PSScavenge::reference_processor(); + if (rp->discover_reference(obj, reference_type())) { + // reference already enqueued, referent and next will be traversed later + instanceKlass::oop_copy_contents(pm, obj); + return; + } else { + // treat referent as normal oop + pm->claim_or_forward_breadth(referent_addr); + } + } + // treat next as normal oop + oop* next_addr = java_lang_ref_Reference::next_addr(obj); + if (PSScavenge::should_scavenge(*next_addr)) { + pm->claim_or_forward_breadth(next_addr); + } + instanceKlass::oop_copy_contents(pm, obj); +} + +void instanceRefKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(pm->depth_first(), "invariant"); + oop* referent_addr = java_lang_ref_Reference::referent_addr(obj); + if (PSScavenge::should_scavenge(*referent_addr)) { + ReferenceProcessor* rp = PSScavenge::reference_processor(); + if (rp->discover_reference(obj, reference_type())) { + // reference already enqueued, referent and next will be traversed later + instanceKlass::oop_push_contents(pm, obj); + return; + } else { + // treat referent as normal oop + pm->claim_or_forward_depth(referent_addr); + } + } + // treat next as normal oop + oop* next_addr = java_lang_ref_Reference::next_addr(obj); + if (PSScavenge::should_scavenge(*next_addr)) { + pm->claim_or_forward_depth(next_addr); + } + instanceKlass::oop_push_contents(pm, obj); +} + +int instanceRefKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + instanceKlass::oop_update_pointers(cm, obj); + + oop* referent_addr = java_lang_ref_Reference::referent_addr(obj); + PSParallelCompact::adjust_pointer(referent_addr); + oop* next_addr = java_lang_ref_Reference::next_addr(obj); + PSParallelCompact::adjust_pointer(next_addr); + oop* discovered_addr = java_lang_ref_Reference::discovered_addr(obj); + PSParallelCompact::adjust_pointer(discovered_addr); + +#ifdef ASSERT + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr("instanceRefKlass::oop_update_pointers obj " + INTPTR_FORMAT, (oopDesc*) obj); + gclog_or_tty->print_cr(" referent_addr/* " INTPTR_FORMAT " / " + INTPTR_FORMAT, referent_addr, + referent_addr ? (oopDesc*) *referent_addr : NULL); + gclog_or_tty->print_cr(" next_addr/* " INTPTR_FORMAT " / " + INTPTR_FORMAT, next_addr, + next_addr ? (oopDesc*) *next_addr : NULL); + gclog_or_tty->print_cr(" discovered_addr/* " INTPTR_FORMAT " / " + INTPTR_FORMAT, discovered_addr, + discovered_addr ? (oopDesc*) *discovered_addr : NULL); + } +#endif + + return size_helper(); +} + +int +instanceRefKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, HeapWord* end_addr) { + instanceKlass::oop_update_pointers(cm, obj, beg_addr, end_addr); + + oop* p; + oop* referent_addr = p = java_lang_ref_Reference::referent_addr(obj); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + oop* next_addr = p = java_lang_ref_Reference::next_addr(obj); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + oop* discovered_addr = p = java_lang_ref_Reference::discovered_addr(obj); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + +#ifdef ASSERT + if(TraceReferenceGC && PrintGCDetails) { + gclog_or_tty->print_cr("instanceRefKlass::oop_update_pointers obj " + INTPTR_FORMAT, (oopDesc*) obj); + gclog_or_tty->print_cr(" referent_addr/* " INTPTR_FORMAT " / " + INTPTR_FORMAT, referent_addr, + referent_addr ? (oopDesc*) *referent_addr : NULL); + gclog_or_tty->print_cr(" next_addr/* " INTPTR_FORMAT " / " + INTPTR_FORMAT, next_addr, + next_addr ? (oopDesc*) *next_addr : NULL); + gclog_or_tty->print_cr(" discovered_addr/* " INTPTR_FORMAT " / " + INTPTR_FORMAT, discovered_addr, + discovered_addr ? (oopDesc*) *discovered_addr : NULL); + } +#endif + + return size_helper(); +} +#endif // SERIALGC + +void instanceRefKlass::update_nonstatic_oop_maps(klassOop k) { + // Clear the nonstatic oop-map entries corresponding to referent + // and nextPending field. They are treated specially by the + // garbage collector. + // The discovered field is used only by the garbage collector + // and is also treated specially. + instanceKlass* ik = instanceKlass::cast(k); + + // Check that we have the right class + debug_only(static bool first_time = true); + assert(k == SystemDictionary::reference_klass() && first_time, + "Invalid update of maps"); + debug_only(first_time = false); + assert(ik->nonstatic_oop_map_size() == 1, "just checking"); + + OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); + + // Check that the current map is (2,4) - currently points at field with + // offset 2 (words) and has 4 map entries. + debug_only(int offset = java_lang_ref_Reference::referent_offset); + debug_only(int length = ((java_lang_ref_Reference::discovered_offset - + java_lang_ref_Reference::referent_offset)/wordSize) + 1); + + if (UseSharedSpaces) { + assert(map->offset() == java_lang_ref_Reference::queue_offset && + map->length() == 1, "just checking"); + } else { + assert(map->offset() == offset && map->length() == length, + "just checking"); + + // Update map to (3,1) - point to offset of 3 (words) with 1 map entry. + map->set_offset(java_lang_ref_Reference::queue_offset); + map->set_length(1); + } +} + + +// Verification + +void instanceRefKlass::oop_verify_on(oop obj, outputStream* st) { + instanceKlass::oop_verify_on(obj, st); + // Verify referent field + oop referent = java_lang_ref_Reference::referent(obj); + + // We should make this general to all heaps + GenCollectedHeap* gch = NULL; + if (Universe::heap()->kind() == CollectedHeap::GenCollectedHeap) + gch = GenCollectedHeap::heap(); + + if (referent != NULL) { + guarantee(referent->is_oop(), "referent field heap failed"); + if (gch != NULL && !gch->is_in_youngest(obj)) + // We do a specific remembered set check here since the referent + // field is not part of the oop mask and therefore skipped by the + // regular verify code. + obj->verify_old_oop(java_lang_ref_Reference::referent_addr(obj), true); + } + // Verify next field + oop next = java_lang_ref_Reference::next(obj); + if (next != NULL) { + guarantee(next->is_oop(), "next field verify failed"); + guarantee(next->is_instanceRef(), "next field verify failed"); + if (gch != NULL && !gch->is_in_youngest(obj)) { + // We do a specific remembered set check here since the next field is + // not part of the oop mask and therefore skipped by the regular + // verify code. + obj->verify_old_oop(java_lang_ref_Reference::next_addr(obj), true); + } + } +} + +void instanceRefKlass::acquire_pending_list_lock(BasicLock *pending_list_basic_lock) { + // we may enter this with pending exception set + PRESERVE_EXCEPTION_MARK; // exceptions are never thrown, needed for TRAPS argument + Handle h_lock(THREAD, java_lang_ref_Reference::pending_list_lock()); + ObjectSynchronizer::fast_enter(h_lock, pending_list_basic_lock, false, THREAD); + assert(ObjectSynchronizer::current_thread_holds_lock( + JavaThread::current(), h_lock), + "Locking should have succeeded"); + if (HAS_PENDING_EXCEPTION) CLEAR_PENDING_EXCEPTION; +} + +void instanceRefKlass::release_and_notify_pending_list_lock( + BasicLock *pending_list_basic_lock) { + // we may enter this with pending exception set + PRESERVE_EXCEPTION_MARK; // exceptions are never thrown, needed for TRAPS argument + // + Handle h_lock(THREAD, java_lang_ref_Reference::pending_list_lock()); + assert(ObjectSynchronizer::current_thread_holds_lock( + JavaThread::current(), h_lock), + "Lock should be held"); + // Notify waiters on pending lists lock if there is any reference. + if (java_lang_ref_Reference::pending_list() != NULL) { + ObjectSynchronizer::notifyall(h_lock, THREAD); + } + ObjectSynchronizer::fast_exit(h_lock(), pending_list_basic_lock, THREAD); + if (HAS_PENDING_EXCEPTION) CLEAR_PENDING_EXCEPTION; +} diff --git a/hotspot/src/share/vm/oops/instanceRefKlass.hpp b/hotspot/src/share/vm/oops/instanceRefKlass.hpp new file mode 100644 index 00000000000..ed8b11998bc --- /dev/null +++ b/hotspot/src/share/vm/oops/instanceRefKlass.hpp @@ -0,0 +1,87 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// An instanceRefKlass is a specialized instanceKlass for Java +// classes that are subclasses of java/lang/ref/Reference. +// +// These classes are used to implement soft/weak/final/phantom +// references and finalization, and need special treatment by the +// garbage collector. +// +// During GC discovered reference objects are added (chained) to one +// of the four lists below, depending on the type of reference. +// The linked occurs through the next field in class java/lang/ref/Reference. +// +// Afterwards, the discovered references are processed in decreasing +// order of reachability. Reference objects eligible for notification +// are linked to the static pending_list in class java/lang/ref/Reference, +// and the pending list lock object in the same class is notified. + + +class instanceRefKlass: public instanceKlass { + public: + // Type testing + bool oop_is_instanceRef() const { return true; } + + // Casting from klassOop + static instanceRefKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_instanceRef(), "cast to instanceRefKlass"); + return (instanceRefKlass*) k->klass_part(); + } + + // allocation + DEFINE_ALLOCATE_PERMANENT(instanceRefKlass); + + // Garbage collection + int oop_adjust_pointers(oop obj); + void oop_follow_contents(oop obj); + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + int oop_oop_iterate(oop obj, OopClosure* blk) { + return oop_oop_iterate_v(obj, blk); + } + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + return oop_oop_iterate_v_m(obj, blk, mr); + } + +#define InstanceRefKlass_OOP_OOP_ITERATE_DECL(OopClosureType, nv_suffix) \ + int oop_oop_iterate##nv_suffix(oop obj, OopClosureType* blk); \ + int oop_oop_iterate##nv_suffix##_m(oop obj, OopClosureType* blk, MemRegion mr); + + ALL_OOP_OOP_ITERATE_CLOSURES_1(InstanceRefKlass_OOP_OOP_ITERATE_DECL) + ALL_OOP_OOP_ITERATE_CLOSURES_3(InstanceRefKlass_OOP_OOP_ITERATE_DECL) + + static void release_and_notify_pending_list_lock(BasicLock *pending_list_basic_lock); + static void acquire_pending_list_lock(BasicLock *pending_list_basic_lock); + + // Update non-static oop maps so 'referent', 'nextPending' and + // 'discovered' will look like non-oops + static void update_nonstatic_oop_maps(klassOop k); + + public: + // Verification + void oop_verify_on(oop obj, outputStream* st); +}; diff --git a/hotspot/src/share/vm/oops/klass.cpp b/hotspot/src/share/vm/oops/klass.cpp new file mode 100644 index 00000000000..c21393ce5a0 --- /dev/null +++ b/hotspot/src/share/vm/oops/klass.cpp @@ -0,0 +1,562 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_klass.cpp.incl" + + +bool Klass::is_subclass_of(klassOop k) const { + // Run up the super chain and check + klassOop t = as_klassOop(); + + if (t == k) return true; + t = Klass::cast(t)->super(); + + while (t != NULL) { + if (t == k) return true; + t = Klass::cast(t)->super(); + } + return false; +} + +bool Klass::search_secondary_supers(klassOop k) const { + // Put some extra logic here out-of-line, before the search proper. + // This cuts down the size of the inline method. + + // This is necessary, since I am never in my own secondary_super list. + if (this->as_klassOop() == k) + return true; + // Scan the array-of-objects for a match + int cnt = secondary_supers()->length(); + for (int i = 0; i < cnt; i++) { + if (secondary_supers()->obj_at(i) == k) { + ((Klass*)this)->set_secondary_super_cache(k); + return true; + } + } + return false; +} + +// Return self, except for abstract classes with exactly 1 +// implementor. Then return the 1 concrete implementation. +Klass *Klass::up_cast_abstract() { + Klass *r = this; + while( r->is_abstract() ) { // Receiver is abstract? + Klass *s = r->subklass(); // Check for exactly 1 subklass + if( !s || s->next_sibling() ) // Oops; wrong count; give up + return this; // Return 'this' as a no-progress flag + r = s; // Loop till find concrete class + } + return r; // Return the 1 concrete class +} + +// Find LCA in class heirarchy +Klass *Klass::LCA( Klass *k2 ) { + Klass *k1 = this; + while( 1 ) { + if( k1->is_subtype_of(k2->as_klassOop()) ) return k2; + if( k2->is_subtype_of(k1->as_klassOop()) ) return k1; + k1 = k1->super()->klass_part(); + k2 = k2->super()->klass_part(); + } +} + + +void Klass::check_valid_for_instantiation(bool throwError, TRAPS) { + ResourceMark rm(THREAD); + THROW_MSG(throwError ? vmSymbols::java_lang_InstantiationError() + : vmSymbols::java_lang_InstantiationException(), external_name()); +} + + +void Klass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS) { + THROW(vmSymbols::java_lang_ArrayStoreException()); +} + + +void Klass::initialize(TRAPS) { + ShouldNotReachHere(); +} + +bool Klass::compute_is_subtype_of(klassOop k) { + assert(k->is_klass(), "argument must be a class"); + return is_subclass_of(k); +} + + +methodOop Klass::uncached_lookup_method(symbolOop name, symbolOop signature) const { +#ifdef ASSERT + tty->print_cr("Error: uncached_lookup_method called on a klass oop." + " Likely error: reflection method does not correctly" + " wrap return value in a mirror object."); +#endif + ShouldNotReachHere(); + return NULL; +} + +klassOop Klass::base_create_klass_oop(KlassHandle& klass, int size, + const Klass_vtbl& vtbl, TRAPS) { + size = align_object_size(size); + // allocate and initialize vtable + Klass* kl = (Klass*) vtbl.allocate_permanent(klass, size, CHECK_NULL); + klassOop k = kl->as_klassOop(); + + { // Preinitialize supertype information. + // A later call to initialize_supers() may update these settings: + kl->set_super(NULL); + for (juint i = 0; i < Klass::primary_super_limit(); i++) { + kl->_primary_supers[i] = NULL; + } + kl->set_secondary_supers(NULL); + oop_store_without_check((oop*) &kl->_primary_supers[0], k); + kl->set_super_check_offset(primary_supers_offset_in_bytes() + sizeof(oopDesc)); + } + + kl->set_java_mirror(NULL); + kl->set_modifier_flags(0); + kl->set_layout_helper(Klass::_lh_neutral_value); + kl->set_name(NULL); + AccessFlags af; + af.set_flags(0); + kl->set_access_flags(af); + kl->set_subklass(NULL); + kl->set_next_sibling(NULL); + kl->set_alloc_count(0); + kl->set_alloc_size(0); + + kl->set_prototype_header(markOopDesc::prototype()); + kl->set_biased_lock_revocation_count(0); + kl->set_last_biased_lock_bulk_revocation_time(0); + + return k; +} + +KlassHandle Klass::base_create_klass(KlassHandle& klass, int size, + const Klass_vtbl& vtbl, TRAPS) { + klassOop ek = base_create_klass_oop(klass, size, vtbl, THREAD); + return KlassHandle(THREAD, ek); +} + +void Klass_vtbl::post_new_init_klass(KlassHandle& klass, + klassOop new_klass, + int size) const { + assert(!new_klass->klass_part()->null_vtbl(), "Not a complete klass"); + CollectedHeap::post_allocation_install_obj_klass(klass, new_klass, size); +} + +void* Klass_vtbl::operator new(size_t ignored, KlassHandle& klass, + int size, TRAPS) { + // The vtable pointer is installed during the execution of + // constructors in the call to permanent_obj_allocate(). Delay + // the installation of the klass pointer into the new klass "k" + // until after the vtable pointer has been installed (i.e., until + // after the return of permanent_obj_allocate(). + klassOop k = + (klassOop) CollectedHeap::permanent_obj_allocate_no_klass_install(klass, + size, CHECK_NULL); + return k->klass_part(); +} + +jint Klass::array_layout_helper(BasicType etype) { + assert(etype >= T_BOOLEAN && etype <= T_OBJECT, "valid etype"); + // Note that T_ARRAY is not allowed here. + int hsize = arrayOopDesc::base_offset_in_bytes(etype); + int esize = type2aelembytes[etype]; + bool isobj = (etype == T_OBJECT); + int tag = isobj ? _lh_array_tag_obj_value : _lh_array_tag_type_value; + int lh = array_layout_helper(tag, hsize, etype, exact_log2(esize)); + + assert(lh < (int)_lh_neutral_value, "must look like an array layout"); + assert(layout_helper_is_javaArray(lh), "correct kind"); + assert(layout_helper_is_objArray(lh) == isobj, "correct kind"); + assert(layout_helper_is_typeArray(lh) == !isobj, "correct kind"); + assert(layout_helper_header_size(lh) == hsize, "correct decode"); + assert(layout_helper_element_type(lh) == etype, "correct decode"); + assert(1 << layout_helper_log2_element_size(lh) == esize, "correct decode"); + + return lh; +} + +bool Klass::can_be_primary_super_slow() const { + if (super() == NULL) + return true; + else if (super()->klass_part()->super_depth() >= primary_super_limit()-1) + return false; + else + return true; +} + +void Klass::initialize_supers(klassOop k, TRAPS) { + if (FastSuperclassLimit == 0) { + // None of the other machinery matters. + set_super(k); + return; + } + if (k == NULL) { + set_super(NULL); + oop_store_without_check((oop*) &_primary_supers[0], (oop) this->as_klassOop()); + assert(super_depth() == 0, "Object must already be initialized properly"); + } else if (k != super() || k == SystemDictionary::object_klass()) { + assert(super() == NULL || super() == SystemDictionary::object_klass(), + "initialize this only once to a non-trivial value"); + set_super(k); + Klass* sup = k->klass_part(); + int sup_depth = sup->super_depth(); + juint my_depth = MIN2(sup_depth + 1, (int)primary_super_limit()); + if (!can_be_primary_super_slow()) + my_depth = primary_super_limit(); + for (juint i = 0; i < my_depth; i++) { + oop_store_without_check((oop*) &_primary_supers[i], (oop) sup->_primary_supers[i]); + } + klassOop *super_check_cell; + if (my_depth < primary_super_limit()) { + oop_store_without_check((oop*) &_primary_supers[my_depth], (oop) this->as_klassOop()); + super_check_cell = &_primary_supers[my_depth]; + } else { + // Overflow of the primary_supers array forces me to be secondary. + super_check_cell = &_secondary_super_cache; + } + set_super_check_offset((address)super_check_cell - (address) this->as_klassOop()); + +#ifdef ASSERT + { + juint j = super_depth(); + assert(j == my_depth, "computed accessor gets right answer"); + klassOop t = as_klassOop(); + while (!Klass::cast(t)->can_be_primary_super()) { + t = Klass::cast(t)->super(); + j = Klass::cast(t)->super_depth(); + } + for (juint j1 = j+1; j1 < primary_super_limit(); j1++) { + assert(primary_super_of_depth(j1) == NULL, "super list padding"); + } + while (t != NULL) { + assert(primary_super_of_depth(j) == t, "super list initialization"); + t = Klass::cast(t)->super(); + --j; + } + assert(j == (juint)-1, "correct depth count"); + } +#endif + } + + if (secondary_supers() == NULL) { + KlassHandle this_kh (THREAD, this); + + // Now compute the list of secondary supertypes. + // Secondaries can occasionally be on the super chain, + // if the inline "_primary_supers" array overflows. + int extras = 0; + klassOop p; + for (p = super(); !(p == NULL || p->klass_part()->can_be_primary_super()); p = p->klass_part()->super()) { + ++extras; + } + + // Compute the "real" non-extra secondaries. + objArrayOop secondary_oops = compute_secondary_supers(extras, CHECK); + objArrayHandle secondaries (THREAD, secondary_oops); + + // Store the extra secondaries in the first array positions: + int fillp = extras; + for (p = this_kh->super(); !(p == NULL || p->klass_part()->can_be_primary_super()); p = p->klass_part()->super()) { + int i; // Scan for overflow primaries being duplicates of 2nd'arys + + // This happens frequently for very deeply nested arrays: the + // primary superclass chain overflows into the secondary. The + // secondary list contains the element_klass's secondaries with + // an extra array dimension added. If the element_klass's + // secondary list already contains some primary overflows, they + // (with the extra level of array-ness) will collide with the + // normal primary superclass overflows. + for( i = extras; i < secondaries->length(); i++ ) + if( secondaries->obj_at(i) == p ) + break; + if( i < secondaries->length() ) + continue; // It's a dup, don't put it in + secondaries->obj_at_put(--fillp, p); + } + // See if we had some dup's, so the array has holes in it. + if( fillp > 0 ) { + // Pack the array. Drop the old secondaries array on the floor + // and let GC reclaim it. + objArrayOop s2 = oopFactory::new_system_objArray(secondaries->length() - fillp, CHECK); + for( int i = 0; i < s2->length(); i++ ) + s2->obj_at_put( i, secondaries->obj_at(i+fillp) ); + secondaries = objArrayHandle(THREAD, s2); + } + + #ifdef ASSERT + if (secondaries() != Universe::the_array_interfaces_array()) { + // We must not copy any NULL placeholders left over from bootstrap. + for (int j = 0; j < secondaries->length(); j++) { + assert(secondaries->obj_at(j) != NULL, "correct bootstrapping order"); + } + } + #endif + + this_kh->set_secondary_supers(secondaries()); + } +} + +objArrayOop Klass::compute_secondary_supers(int num_extra_slots, TRAPS) { + assert(num_extra_slots == 0, "override for complex klasses"); + return Universe::the_empty_system_obj_array(); +} + + +Klass* Klass::subklass() const { + return _subklass == NULL ? NULL : Klass::cast(_subklass); +} + +instanceKlass* Klass::superklass() const { + assert(super() == NULL || super()->klass_part()->oop_is_instance(), "must be instance klass"); + return _super == NULL ? NULL : instanceKlass::cast(_super); +} + +Klass* Klass::next_sibling() const { + return _next_sibling == NULL ? NULL : Klass::cast(_next_sibling); +} + +void Klass::set_subklass(klassOop s) { + assert(s != as_klassOop(), "sanity check"); + oop_store_without_check((oop*)&_subklass, s); +} + +void Klass::set_next_sibling(klassOop s) { + assert(s != as_klassOop(), "sanity check"); + oop_store_without_check((oop*)&_next_sibling, s); +} + +void Klass::append_to_sibling_list() { + debug_only(if (!SharedSkipVerify) as_klassOop()->verify();) + // add ourselves to superklass' subklass list + instanceKlass* super = superklass(); + if (super == NULL) return; // special case: class Object + assert(SharedSkipVerify || + (!super->is_interface() // interfaces cannot be supers + && (super->superklass() == NULL || !is_interface())), + "an interface can only be a subklass of Object"); + klassOop prev_first_subklass = super->subklass_oop(); + if (prev_first_subklass != NULL) { + // set our sibling to be the superklass' previous first subklass + set_next_sibling(prev_first_subklass); + } + // make ourselves the superklass' first subklass + super->set_subklass(as_klassOop()); + debug_only(if (!SharedSkipVerify) as_klassOop()->verify();) +} + +void Klass::remove_from_sibling_list() { + // remove receiver from sibling list + instanceKlass* super = superklass(); + assert(super != NULL || as_klassOop() == SystemDictionary::object_klass(), "should have super"); + if (super == NULL) return; // special case: class Object + if (super->subklass() == this) { + // first subklass + super->set_subklass(_next_sibling); + } else { + Klass* sib = super->subklass(); + while (sib->next_sibling() != this) { + sib = sib->next_sibling(); + }; + sib->set_next_sibling(_next_sibling); + } +} + +void Klass::follow_weak_klass_links( BoolObjectClosure* is_alive, OopClosure* keep_alive) { + // This klass is alive but the subklass and siblings are not followed/updated. + // We update the subklass link and the subklass' sibling links here. + // Our own sibling link will be updated by our superclass (which must be alive + // since we are). + assert(is_alive->do_object_b(as_klassOop()), "just checking, this should be live"); + if (ClassUnloading) { + klassOop sub = subklass_oop(); + if (sub != NULL && !is_alive->do_object_b(sub)) { + // first subklass not alive, find first one alive + do { +#ifndef PRODUCT + if (TraceClassUnloading && WizardMode) { + ResourceMark rm; + tty->print_cr("[Unlinking class (subclass) %s]", sub->klass_part()->external_name()); + } +#endif + sub = sub->klass_part()->next_sibling_oop(); + } while (sub != NULL && !is_alive->do_object_b(sub)); + set_subklass(sub); + } + // now update the subklass' sibling list + while (sub != NULL) { + klassOop next = sub->klass_part()->next_sibling_oop(); + if (next != NULL && !is_alive->do_object_b(next)) { + // first sibling not alive, find first one alive + do { +#ifndef PRODUCT + if (TraceClassUnloading && WizardMode) { + ResourceMark rm; + tty->print_cr("[Unlinking class (sibling) %s]", next->klass_part()->external_name()); + } +#endif + next = next->klass_part()->next_sibling_oop(); + } while (next != NULL && !is_alive->do_object_b(next)); + sub->klass_part()->set_next_sibling(next); + } + sub = next; + } + } else { + // Always follow subklass and sibling link. This will prevent any klasses from + // being unloaded (all classes are transitively linked from java.lang.Object). + keep_alive->do_oop(adr_subklass()); + keep_alive->do_oop(adr_next_sibling()); + } +} + + +void Klass::remove_unshareable_info() { + if (oop_is_instance()) { + instanceKlass* ik = (instanceKlass*)this; + if (ik->is_linked()) { + ik->unlink_class(); + } + } + set_subklass(NULL); + set_next_sibling(NULL); +} + + +klassOop Klass::array_klass_or_null(int rank) { + EXCEPTION_MARK; + // No exception can be thrown by array_klass_impl when called with or_null == true. + // (In anycase, the execption mark will fail if it do so) + return array_klass_impl(true, rank, THREAD); +} + + +klassOop Klass::array_klass_or_null() { + EXCEPTION_MARK; + // No exception can be thrown by array_klass_impl when called with or_null == true. + // (In anycase, the execption mark will fail if it do so) + return array_klass_impl(true, THREAD); +} + + +klassOop Klass::array_klass_impl(bool or_null, int rank, TRAPS) { + fatal("array_klass should be dispatched to instanceKlass, objArrayKlass or typeArrayKlass"); + return NULL; +} + + +klassOop Klass::array_klass_impl(bool or_null, TRAPS) { + fatal("array_klass should be dispatched to instanceKlass, objArrayKlass or typeArrayKlass"); + return NULL; +} + + +void Klass::with_array_klasses_do(void f(klassOop k)) { + f(as_klassOop()); +} + + +const char* Klass::external_name() const { + return name()->as_klass_external_name(); +} + + +char* Klass::signature_name() const { + return name()->as_C_string(); +} + +// Unless overridden, modifier_flags is 0. +jint Klass::compute_modifier_flags(TRAPS) const { + return 0; +} + +int Klass::atomic_incr_biased_lock_revocation_count() { + return (int) Atomic::add(1, &_biased_lock_revocation_count); +} + +// Unless overridden, jvmti_class_status has no flags set. +jint Klass::jvmti_class_status() const { + return 0; +} + +#ifndef PRODUCT + +// Printing + +void Klass::oop_print_on(oop obj, outputStream* st) { + ResourceMark rm; + // print title + st->print_cr("%s ", internal_name()); + obj->print_address_on(st); + + if (WizardMode) { + // print header + obj->mark()->print_on(st); + } + + // print class + st->print(" - klass: "); + obj->klass()->print_value_on(st); + st->cr(); +} + + +void Klass::oop_print_value_on(oop obj, outputStream* st) { + // print title + ResourceMark rm; // Cannot print in debug mode without this + st->print("%s", internal_name()); + obj->print_address_on(st); +} + +#endif + +// Verification + +void Klass::oop_verify_on(oop obj, outputStream* st) { + guarantee(obj->is_oop(), "should be oop"); + guarantee(obj->klass()->is_perm(), "should be in permspace"); + guarantee(obj->klass()->is_klass(), "klass field is not a klass"); +} + + +void Klass::oop_verify_old_oop(oop obj, oop* p, bool allow_dirty) { + /* $$$ I think this functionality should be handled by verification of + + RememberedSet::verify_old_oop(obj, p, allow_dirty, false); + + the card table. */ +} + +#ifndef PRODUCT + +void Klass::verify_vtable_index(int i) { + assert(oop_is_instance() || oop_is_array(), "only instanceKlass and arrayKlass have vtables"); + if (oop_is_instance()) { + assert(i>=0 && i<((instanceKlass*)this)->vtable_length()/vtableEntry::size(), "index out of bounds"); + } else { + assert(i>=0 && i<((arrayKlass*)this)->vtable_length()/vtableEntry::size(), "index out of bounds"); + } +} + +#endif diff --git a/hotspot/src/share/vm/oops/klass.hpp b/hotspot/src/share/vm/oops/klass.hpp new file mode 100644 index 00000000000..76473cba3ee --- /dev/null +++ b/hotspot/src/share/vm/oops/klass.hpp @@ -0,0 +1,767 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A Klass is the part of the klassOop that provides: +// 1: language level class object (method dictionary etc.) +// 2: provide vm dispatch behavior for the object +// Both functions are combined into one C++ class. The toplevel class "Klass" +// implements purpose 1 whereas all subclasses provide extra virtual functions +// for purpose 2. + +// One reason for the oop/klass dichotomy in the implementation is +// that we don't want a C++ vtbl pointer in every object. Thus, +// normal oops don't have any virtual functions. Instead, they +// forward all "virtual" functions to their klass, which does have +// a vtbl and does the C++ dispatch depending on the object's +// actual type. (See oop.inline.hpp for some of the forwarding code.) +// ALL FUNCTIONS IMPLEMENTING THIS DISPATCH ARE PREFIXED WITH "oop_"! + +// Klass layout: +// [header ] klassOop +// [klass pointer ] klassOop +// [C++ vtbl ptr ] (contained in Klass_vtbl) +// [layout_helper ] +// [super_check_offset ] for fast subtype checks +// [secondary_super_cache] for fast subtype checks +// [secondary_supers ] array of 2ndary supertypes +// [primary_supers 0] +// [primary_supers 1] +// [primary_supers 2] +// ... +// [primary_supers 7] +// [java_mirror ] +// [super ] +// [name ] +// [first subklass] +// [next_sibling ] link to chain additional subklasses +// [modifier_flags] +// [access_flags ] +// [verify_count ] - not in product +// [alloc_count ] +// [last_biased_lock_bulk_revocation_time] (64 bits) +// [prototype_header] +// [biased_lock_revocation_count] + + +// Forward declarations. +class klassVtable; +class KlassHandle; +class OrderAccess; + +// Holder (or cage) for the C++ vtable of each kind of Klass. +// We want to tightly constrain the location of the C++ vtable in the overall layout. +class Klass_vtbl { + protected: + // The following virtual exists only to force creation of a C++ vtable, + // so that this class truly is the location of the vtable of all Klasses. + virtual void unused_initial_virtual() { } + + public: + // The following virtual makes Klass_vtbl play a second role as a + // factory protocol for subclasses of Klass ("sub-Klasses"). + // Here's how it works.... + // + // This VM uses metaobjects as factories for their instances. + // + // In order to initialize the C++ vtable of a new instance, its + // metaobject is forced to use the C++ placed new operator to + // allocate the instance. In a typical C++-based system, each + // sub-class would have its own factory routine which + // directly uses the placed new operator on the desired class, + // and then calls the appropriate chain of C++ constructors. + // + // However, this system uses shared code to performs the first + // allocation and initialization steps for all sub-Klasses. + // (See base_create_klass() and base_create_array_klass().) + // This does not factor neatly into a hierarchy of C++ constructors. + // Each caller of these shared "base_create" routines knows + // exactly which sub-Klass it is creating, but the shared routine + // does not, even though it must perform the actual allocation. + // + // Therefore, the caller of the shared "base_create" must wrap + // the specific placed new call in a virtual function which + // performs the actual allocation and vtable set-up. That + // virtual function is here, Klass_vtbl::allocate_permanent. + // + // The arguments to Universe::allocate_permanent() are passed + // straight through the placed new operator, which in turn + // obtains them directly from this virtual call. + // + // This virtual is called on a temporary "example instance" of the + // sub-Klass being instantiated, a C++ auto variable. The "real" + // instance created by this virtual is on the VM heap, where it is + // equipped with a klassOopDesc header. + // + // It is merely an accident of implementation that we use "example + // instances", but that is why the virtual function which implements + // each sub-Klass factory happens to be defined by the same sub-Klass + // for which it creates instances. + // + // The vtbl_value() call (see below) is used to strip away the + // accidental Klass-ness from an "example instance" and present it as + // a factory. Think of each factory object as a mere container of the + // C++ vtable for the desired sub-Klass. Since C++ does not allow + // direct references to vtables, the factory must also be delegated + // the task of allocating the instance, but the essential point is + // that the factory knows how to initialize the C++ vtable with the + // right pointer value. All other common initializations are handled + // by the shared "base_create" subroutines. + // + virtual void* allocate_permanent(KlassHandle& klass, int size, TRAPS) const = 0; + void post_new_init_klass(KlassHandle& klass, klassOop obj, int size) const; + + // Every subclass on which vtbl_value is called must include this macro. + // Delay the installation of the klassKlass pointer until after the + // the vtable for a new klass has been installed (after the call to new()). +#define DEFINE_ALLOCATE_PERMANENT(thisKlass) \ + void* allocate_permanent(KlassHandle& klass_klass, int size, TRAPS) const { \ + void* result = new(klass_klass, size, THREAD) thisKlass(); \ + if (HAS_PENDING_EXCEPTION) return NULL; \ + klassOop new_klass = ((Klass*) result)->as_klassOop(); \ + OrderAccess::storestore(); \ + post_new_init_klass(klass_klass, new_klass, size); \ + return result; \ + } + + bool null_vtbl() { return *(intptr_t*)this == 0; } + + protected: + void* operator new(size_t ignored, KlassHandle& klass, int size, TRAPS); +}; + + +class Klass : public Klass_vtbl { + friend class VMStructs; + protected: + // note: put frequently-used fields together at start of klass structure + // for better cache behavior (may not make much of a difference but sure won't hurt) + enum { _primary_super_limit = 8 }; + + // The "layout helper" is a combined descriptor of object layout. + // For klasses which are neither instance nor array, the value is zero. + // + // For instances, layout helper is a positive number, the instance size. + // This size is already passed through align_object_size and scaled to bytes. + // The low order bit is set if instances of this class cannot be + // allocated using the fastpath. + // + // For arrays, layout helper is a negative number, containing four + // distinct bytes, as follows: + // MSB:[tag, hsz, ebt, log2(esz)]:LSB + // where: + // tag is 0x80 if the elements are oops, 0xC0 if non-oops + // hsz is array header size in bytes (i.e., offset of first element) + // ebt is the BasicType of the elements + // esz is the element size in bytes + // This packed word is arranged so as to be quickly unpacked by the + // various fast paths that use the various subfields. + // + // The esz bits can be used directly by a SLL instruction, without masking. + // + // Note that the array-kind tag looks like 0x00 for instance klasses, + // since their length in bytes is always less than 24Mb. + // + // Final note: This comes first, immediately after Klass_vtbl, + // because it is frequently queried. + jint _layout_helper; + + // The fields _super_check_offset, _secondary_super_cache, _secondary_supers + // and _primary_supers all help make fast subtype checks. See big discussion + // in doc/server_compiler/checktype.txt + // + // Where to look to observe a supertype (it is &_secondary_super_cache for + // secondary supers, else is &_primary_supers[depth()]. + juint _super_check_offset; + + public: + oop* oop_block_beg() const { return adr_secondary_super_cache(); } + oop* oop_block_end() const { return adr_next_sibling() + 1; } + + protected: + // + // The oop block. All oop fields must be declared here and only oop fields + // may be declared here. In addition, the first and last fields in this block + // must remain first and last, unless oop_block_beg() and/or oop_block_end() + // are updated. Grouping the oop fields in a single block simplifies oop + // iteration. + // + + // Cache of last observed secondary supertype + klassOop _secondary_super_cache; + // Array of all secondary supertypes + objArrayOop _secondary_supers; + // Ordered list of all primary supertypes + klassOop _primary_supers[_primary_super_limit]; + // java/lang/Class instance mirroring this class + oop _java_mirror; + // Superclass + klassOop _super; + // Class name. Instance classes: java/lang/String, etc. Array classes: [I, + // [Ljava/lang/String;, etc. Set to zero for all other kinds of classes. + symbolOop _name; + // First subclass (NULL if none); _subklass->next_sibling() is next one + klassOop _subklass; + // Sibling link (or NULL); links all subklasses of a klass + klassOop _next_sibling; + + // + // End of the oop block. + // + + jint _modifier_flags; // Processed access flags, for use by Class.getModifiers. + AccessFlags _access_flags; // Access flags. The class/interface distinction is stored here. + +#ifndef PRODUCT + int _verify_count; // to avoid redundant verifies +#endif + + juint _alloc_count; // allocation profiling support - update klass_size_in_bytes() if moved/deleted + + // Biased locking implementation and statistics + // (the 64-bit chunk goes first, to avoid some fragmentation) + jlong _last_biased_lock_bulk_revocation_time; + markOop _prototype_header; // Used when biased locking is both enabled and disabled for this type + jint _biased_lock_revocation_count; + + public: + + // returns the enclosing klassOop + klassOop as_klassOop() const { + // see klassOop.hpp for layout. + return (klassOop) (((char*) this) - sizeof(klassOopDesc)); + } + + public: + // Allocation + const Klass_vtbl& vtbl_value() const { return *this; } // used only on "example instances" + static KlassHandle base_create_klass(KlassHandle& klass, int size, const Klass_vtbl& vtbl, TRAPS); + static klassOop base_create_klass_oop(KlassHandle& klass, int size, const Klass_vtbl& vtbl, TRAPS); + + // super + klassOop super() const { return _super; } + void set_super(klassOop k) { oop_store_without_check((oop*) &_super, (oop) k); } + + // initializes _super link, _primary_supers & _secondary_supers arrays + void initialize_supers(klassOop k, TRAPS); + void initialize_supers_impl1(klassOop k); + void initialize_supers_impl2(klassOop k); + + // klass-specific helper for initializing _secondary_supers + virtual objArrayOop compute_secondary_supers(int num_extra_slots, TRAPS); + + // java_super is the Java-level super type as specified by Class.getSuperClass. + virtual klassOop java_super() const { return NULL; } + + juint super_check_offset() const { return _super_check_offset; } + void set_super_check_offset(juint o) { _super_check_offset = o; } + + klassOop secondary_super_cache() const { return _secondary_super_cache; } + void set_secondary_super_cache(klassOop k) { oop_store_without_check((oop*) &_secondary_super_cache, (oop) k); } + + objArrayOop secondary_supers() const { return _secondary_supers; } + void set_secondary_supers(objArrayOop k) { oop_store_without_check((oop*) &_secondary_supers, (oop) k); } + + // Return the element of the _super chain of the given depth. + // If there is no such element, return either NULL or this. + klassOop primary_super_of_depth(juint i) const { + assert(i < primary_super_limit(), "oob"); + klassOop super = _primary_supers[i]; + assert(super == NULL || super->klass_part()->super_depth() == i, "correct display"); + return super; + } + + // Can this klass be a primary super? False for interfaces and arrays of + // interfaces. False also for arrays or classes with long super chains. + bool can_be_primary_super() const { + const juint secondary_offset = secondary_super_cache_offset_in_bytes() + sizeof(oopDesc); + return super_check_offset() != secondary_offset; + } + virtual bool can_be_primary_super_slow() const; + + // Returns number of primary supers; may be a number in the inclusive range [0, primary_super_limit]. + juint super_depth() const { + if (!can_be_primary_super()) { + return primary_super_limit(); + } else { + juint d = (super_check_offset() - (primary_supers_offset_in_bytes() + sizeof(oopDesc))) / sizeof(klassOop); + assert(d < primary_super_limit(), "oob"); + assert(_primary_supers[d] == as_klassOop(), "proper init"); + return d; + } + } + + // java mirror + oop java_mirror() const { return _java_mirror; } + void set_java_mirror(oop m) { oop_store((oop*) &_java_mirror, m); } + + // modifier flags + jint modifier_flags() const { return _modifier_flags; } + void set_modifier_flags(jint flags) { _modifier_flags = flags; } + + // size helper + int layout_helper() const { return _layout_helper; } + void set_layout_helper(int lh) { _layout_helper = lh; } + + // Note: for instances layout_helper() may include padding. + // Use instanceKlass::contains_field_offset to classify field offsets. + + // sub/superklass links + instanceKlass* superklass() const; + Klass* subklass() const; + Klass* next_sibling() const; + void append_to_sibling_list(); // add newly created receiver to superklass' subklass list + void remove_from_sibling_list(); // remove receiver from sibling list + protected: // internal accessors + klassOop subklass_oop() const { return _subklass; } + klassOop next_sibling_oop() const { return _next_sibling; } + void set_subklass(klassOop s); + void set_next_sibling(klassOop s); + + oop* adr_super() const { return (oop*)&_super; } + oop* adr_primary_supers() const { return (oop*)&_primary_supers[0]; } + oop* adr_secondary_super_cache() const { return (oop*)&_secondary_super_cache; } + oop* adr_secondary_supers()const { return (oop*)&_secondary_supers; } + oop* adr_java_mirror() const { return (oop*)&_java_mirror; } + oop* adr_name() const { return (oop*)&_name; } + oop* adr_subklass() const { return (oop*)&_subklass; } + oop* adr_next_sibling() const { return (oop*)&_next_sibling; } + + public: + // Allocation profiling support + juint alloc_count() const { return _alloc_count; } + void set_alloc_count(juint n) { _alloc_count = n; } + virtual juint alloc_size() const = 0; + virtual void set_alloc_size(juint n) = 0; + + // Compiler support + static int super_offset_in_bytes() { return offset_of(Klass, _super); } + static int super_check_offset_offset_in_bytes() { return offset_of(Klass, _super_check_offset); } + static int primary_supers_offset_in_bytes(){ return offset_of(Klass, _primary_supers); } + static int secondary_super_cache_offset_in_bytes() { return offset_of(Klass, _secondary_super_cache); } + static int secondary_supers_offset_in_bytes() { return offset_of(Klass, _secondary_supers); } + static int java_mirror_offset_in_bytes() { return offset_of(Klass, _java_mirror); } + static int modifier_flags_offset_in_bytes(){ return offset_of(Klass, _modifier_flags); } + static int layout_helper_offset_in_bytes() { return offset_of(Klass, _layout_helper); } + static int access_flags_offset_in_bytes() { return offset_of(Klass, _access_flags); } + + // Unpacking layout_helper: + enum { + _lh_neutral_value = 0, // neutral non-array non-instance value + _lh_instance_slow_path_bit = 0x01, + _lh_log2_element_size_shift = BitsPerByte*0, + _lh_log2_element_size_mask = BitsPerLong-1, + _lh_element_type_shift = BitsPerByte*1, + _lh_element_type_mask = right_n_bits(BitsPerByte), // shifted mask + _lh_header_size_shift = BitsPerByte*2, + _lh_header_size_mask = right_n_bits(BitsPerByte), // shifted mask + _lh_array_tag_bits = 2, + _lh_array_tag_shift = BitsPerInt - _lh_array_tag_bits, + _lh_array_tag_type_value = ~0x00, // 0xC0000000 >> 30 + _lh_array_tag_obj_value = ~0x01 // 0x80000000 >> 30 + }; + + static int layout_helper_size_in_bytes(jint lh) { + assert(lh > (jint)_lh_neutral_value, "must be instance"); + return (int) lh & ~_lh_instance_slow_path_bit; + } + static bool layout_helper_needs_slow_path(jint lh) { + assert(lh > (jint)_lh_neutral_value, "must be instance"); + return (lh & _lh_instance_slow_path_bit) != 0; + } + static bool layout_helper_is_instance(jint lh) { + return (jint)lh > (jint)_lh_neutral_value; + } + static bool layout_helper_is_javaArray(jint lh) { + return (jint)lh < (jint)_lh_neutral_value; + } + static bool layout_helper_is_typeArray(jint lh) { + // _lh_array_tag_type_value == (lh >> _lh_array_tag_shift); + return (juint)lh >= (juint)(_lh_array_tag_type_value << _lh_array_tag_shift); + } + static bool layout_helper_is_objArray(jint lh) { + // _lh_array_tag_obj_value == (lh >> _lh_array_tag_shift); + return (jint)lh < (jint)(_lh_array_tag_type_value << _lh_array_tag_shift); + } + static int layout_helper_header_size(jint lh) { + assert(lh < (jint)_lh_neutral_value, "must be array"); + int hsize = (lh >> _lh_header_size_shift) & _lh_header_size_mask; + assert(hsize > 0 && hsize < (int)sizeof(oopDesc)*3, "sanity"); + return hsize; + } + static BasicType layout_helper_element_type(jint lh) { + assert(lh < (jint)_lh_neutral_value, "must be array"); + int btvalue = (lh >> _lh_element_type_shift) & _lh_element_type_mask; + assert(btvalue >= T_BOOLEAN && btvalue <= T_OBJECT, "sanity"); + return (BasicType) btvalue; + } + static int layout_helper_log2_element_size(jint lh) { + assert(lh < (jint)_lh_neutral_value, "must be array"); + int l2esz = (lh >> _lh_log2_element_size_shift) & _lh_log2_element_size_mask; + assert(l2esz <= LogBitsPerLong, "sanity"); + return l2esz; + } + static jint array_layout_helper(jint tag, int hsize, BasicType etype, int log2_esize) { + return (tag << _lh_array_tag_shift) + | (hsize << _lh_header_size_shift) + | ((int)etype << _lh_element_type_shift) + | (log2_esize << _lh_log2_element_size_shift); + } + static jint instance_layout_helper(jint size, bool slow_path_flag) { + return (size << LogHeapWordSize) + | (slow_path_flag ? _lh_instance_slow_path_bit : 0); + } + static int layout_helper_to_size_helper(jint lh) { + assert(lh > (jint)_lh_neutral_value, "must be instance"); + // Note that the following expression discards _lh_instance_slow_path_bit. + return lh >> LogHeapWordSize; + } + // Out-of-line version computes everything based on the etype: + static jint array_layout_helper(BasicType etype); + + // What is the maximum number of primary superclasses any klass can have? +#ifdef PRODUCT + static juint primary_super_limit() { return _primary_super_limit; } +#else + static juint primary_super_limit() { + assert(FastSuperclassLimit <= _primary_super_limit, "parameter oob"); + return FastSuperclassLimit; + } +#endif + + // vtables + virtual klassVtable* vtable() const { return NULL; } + + static int klass_size_in_bytes() { return offset_of(Klass, _alloc_count) + sizeof(juint); } // all "visible" fields + + // subclass check + bool is_subclass_of(klassOop k) const; + // subtype check: true if is_subclass_of, or if k is interface and receiver implements it + bool is_subtype_of(klassOop k) const { + juint off = k->klass_part()->super_check_offset(); + klassOop sup = *(klassOop*)( (address)as_klassOop() + off ); + const juint secondary_offset = secondary_super_cache_offset_in_bytes() + sizeof(oopDesc); + if (sup == k) { + return true; + } else if (off != secondary_offset) { + return false; + } else { + return search_secondary_supers(k); + } + } + bool search_secondary_supers(klassOop k) const; + + // Find LCA in class heirarchy + Klass *LCA( Klass *k ); + + // Check whether reflection/jni/jvm code is allowed to instantiate this class; + // if not, throw either an Error or an Exception. + virtual void check_valid_for_instantiation(bool throwError, TRAPS); + + // Casting + static Klass* cast(klassOop k) { + assert(k->is_klass(), "cast to Klass"); + return k->klass_part(); + } + + // array copying + virtual void copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS); + + // tells if the class should be initialized + virtual bool should_be_initialized() const { return false; } + // initializes the klass + virtual void initialize(TRAPS); + // lookup operation for MethodLookupCache + friend class MethodLookupCache; + virtual methodOop uncached_lookup_method(symbolOop name, symbolOop signature) const; + public: + methodOop lookup_method(symbolOop name, symbolOop signature) const { + return uncached_lookup_method(name, signature); + } + + // array class with specific rank + klassOop array_klass(int rank, TRAPS) { return array_klass_impl(false, rank, THREAD); } + + // array class with this klass as element type + klassOop array_klass(TRAPS) { return array_klass_impl(false, THREAD); } + + // These will return NULL instead of allocating on the heap: + // NB: these can block for a mutex, like other functions with TRAPS arg. + klassOop array_klass_or_null(int rank); + klassOop array_klass_or_null(); + + virtual oop protection_domain() { return NULL; } + virtual oop class_loader() const { return NULL; } + + protected: + virtual klassOop array_klass_impl(bool or_null, int rank, TRAPS); + virtual klassOop array_klass_impl(bool or_null, TRAPS); + + public: + virtual void remove_unshareable_info(); + + protected: + // computes the subtype relationship + virtual bool compute_is_subtype_of(klassOop k); + public: + // subclass accessor (here for convenience; undefined for non-klass objects) + virtual bool is_leaf_class() const { fatal("not a class"); return false; } + public: + // ALL FUNCTIONS BELOW THIS POINT ARE DISPATCHED FROM AN OOP + // These functions describe behavior for the oop not the KLASS. + + // actual oop size of obj in memory + virtual int oop_size(oop obj) const = 0; + + // actual oop size of this klass in memory + virtual int klass_oop_size() const = 0; + + // Returns the Java name for a class (Resource allocated) + // For arrays, this returns the name of the element with a leading '['. + // For classes, this returns the name with the package separators + // turned into '.'s. + const char* external_name() const; + // Returns the name for a class (Resource allocated) as the class + // would appear in a signature. + // For arrays, this returns the name of the element with a leading '['. + // For classes, this returns the name with a leading 'L' and a trailing ';' + // and the package separators as '/'. + virtual char* signature_name() const; + + // garbage collection support + virtual void oop_follow_contents(oop obj) = 0; + virtual int oop_adjust_pointers(oop obj) = 0; + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS_PV + + public: + // type testing operations + virtual bool oop_is_instance_slow() const { return false; } + virtual bool oop_is_instanceRef() const { return false; } + virtual bool oop_is_array() const { return false; } + virtual bool oop_is_objArray_slow() const { return false; } + virtual bool oop_is_symbol() const { return false; } + virtual bool oop_is_klass() const { return false; } + virtual bool oop_is_thread() const { return false; } + virtual bool oop_is_method() const { return false; } + virtual bool oop_is_constMethod() const { return false; } + virtual bool oop_is_methodData() const { return false; } + virtual bool oop_is_constantPool() const { return false; } + virtual bool oop_is_constantPoolCache() const { return false; } + virtual bool oop_is_typeArray_slow() const { return false; } + virtual bool oop_is_arrayKlass() const { return false; } + virtual bool oop_is_objArrayKlass() const { return false; } + virtual bool oop_is_typeArrayKlass() const { return false; } + virtual bool oop_is_compiledICHolder() const { return false; } + virtual bool oop_is_instanceKlass() const { return false; } + + bool oop_is_javaArray_slow() const { + return oop_is_objArray_slow() || oop_is_typeArray_slow(); + } + + // Fast non-virtual versions, used by oop.inline.hpp and elsewhere: + #ifndef ASSERT + #define assert_same_query(xval, xcheck) xval + #else + private: + static bool assert_same_query(bool xval, bool xslow) { + assert(xval == xslow, "slow and fast queries agree"); + return xval; + } + public: + #endif + inline bool oop_is_instance() const { return assert_same_query( + layout_helper_is_instance(layout_helper()), + oop_is_instance_slow()); } + inline bool oop_is_javaArray() const { return assert_same_query( + layout_helper_is_javaArray(layout_helper()), + oop_is_javaArray_slow()); } + inline bool oop_is_objArray() const { return assert_same_query( + layout_helper_is_objArray(layout_helper()), + oop_is_objArray_slow()); } + inline bool oop_is_typeArray() const { return assert_same_query( + layout_helper_is_typeArray(layout_helper()), + oop_is_typeArray_slow()); } + #undef assert_same_query + + // Unless overridden, oop is parsable if it has a klass pointer. + virtual bool oop_is_parsable(oop obj) const { return true; } + + // Access flags + AccessFlags access_flags() const { return _access_flags; } + void set_access_flags(AccessFlags flags) { _access_flags = flags; } + + bool is_public() const { return _access_flags.is_public(); } + bool is_final() const { return _access_flags.is_final(); } + bool is_interface() const { return _access_flags.is_interface(); } + bool is_abstract() const { return _access_flags.is_abstract(); } + bool is_super() const { return _access_flags.is_super(); } + bool is_synthetic() const { return _access_flags.is_synthetic(); } + void set_is_synthetic() { _access_flags.set_is_synthetic(); } + bool has_finalizer() const { return _access_flags.has_finalizer(); } + bool has_final_method() const { return _access_flags.has_final_method(); } + void set_has_finalizer() { _access_flags.set_has_finalizer(); } + void set_has_final_method() { _access_flags.set_has_final_method(); } + bool is_cloneable() const { return _access_flags.is_cloneable(); } + void set_is_cloneable() { _access_flags.set_is_cloneable(); } + bool has_vanilla_constructor() const { return _access_flags.has_vanilla_constructor(); } + void set_has_vanilla_constructor() { _access_flags.set_has_vanilla_constructor(); } + bool has_miranda_methods () const { return access_flags().has_miranda_methods(); } + void set_has_miranda_methods() { _access_flags.set_has_miranda_methods(); } + + // Biased locking support + // Note: the prototype header is always set up to be at least the + // prototype markOop. If biased locking is enabled it may further be + // biasable and have an epoch. + markOop prototype_header() const { return _prototype_header; } + // NOTE: once instances of this klass are floating around in the + // system, this header must only be updated at a safepoint. + // NOTE 2: currently we only ever set the prototype header to the + // biasable prototype for instanceKlasses. There is no technical + // reason why it could not be done for arrayKlasses aside from + // wanting to reduce the initial scope of this optimization. There + // are potential problems in setting the bias pattern for + // JVM-internal oops. + inline void set_prototype_header(markOop header); + static int prototype_header_offset_in_bytes() { return offset_of(Klass, _prototype_header); } + + int biased_lock_revocation_count() const { return (int) _biased_lock_revocation_count; } + // Atomically increments biased_lock_revocation_count and returns updated value + int atomic_incr_biased_lock_revocation_count(); + void set_biased_lock_revocation_count(int val) { _biased_lock_revocation_count = (jint) val; } + jlong last_biased_lock_bulk_revocation_time() { return _last_biased_lock_bulk_revocation_time; } + void set_last_biased_lock_bulk_revocation_time(jlong cur_time) { _last_biased_lock_bulk_revocation_time = cur_time; } + + + // garbage collection support + virtual void follow_weak_klass_links( + BoolObjectClosure* is_alive, OopClosure* keep_alive); + + // Prefetch within oop iterators. This is a macro because we + // can't guarantee that the compiler will inline it. In 64-bit + // it generally doesn't. Signature is + // + // static void prefetch_beyond(oop* const start, + // oop* const end, + // const intx foffset, + // const Prefetch::style pstyle); +#define prefetch_beyond(start, end, foffset, pstyle) { \ + const intx foffset_ = (foffset); \ + const Prefetch::style pstyle_ = (pstyle); \ + assert(foffset_ > 0, "prefetch beyond, not behind"); \ + if (pstyle_ != Prefetch::do_none) { \ + oop* ref = (start); \ + if (ref < (end)) { \ + switch (pstyle_) { \ + case Prefetch::do_read: \ + Prefetch::read(*ref, foffset_); \ + break; \ + case Prefetch::do_write: \ + Prefetch::write(*ref, foffset_); \ + break; \ + default: \ + ShouldNotReachHere(); \ + break; \ + } \ + } \ + } \ + } + + // iterators + virtual int oop_oop_iterate(oop obj, OopClosure* blk) = 0; + virtual int oop_oop_iterate_v(oop obj, OopClosure* blk) { + return oop_oop_iterate(obj, blk); + } + + // Iterates "blk" over all the oops in "obj" (of type "this") within "mr". + // (I don't see why the _m should be required, but without it the Solaris + // C++ gives warning messages about overridings of the "oop_oop_iterate" + // defined above "hiding" this virtual function. (DLD, 6/20/00)) */ + virtual int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) = 0; + virtual int oop_oop_iterate_v_m(oop obj, OopClosure* blk, MemRegion mr) { + return oop_oop_iterate_m(obj, blk, mr); + } + + // Versions of the above iterators specialized to particular subtypes + // of OopClosure, to avoid closure virtual calls. +#define Klass_OOP_OOP_ITERATE_DECL(OopClosureType, nv_suffix) \ + virtual int oop_oop_iterate##nv_suffix(oop obj, OopClosureType* blk) { \ + /* Default implementation reverts to general version. */ \ + return oop_oop_iterate(obj, blk); \ + } \ + \ + /* Iterates "blk" over all the oops in "obj" (of type "this") within "mr". \ + (I don't see why the _m should be required, but without it the Solaris \ + C++ gives warning messages about overridings of the "oop_oop_iterate" \ + defined above "hiding" this virtual function. (DLD, 6/20/00)) */ \ + virtual int oop_oop_iterate##nv_suffix##_m(oop obj, \ + OopClosureType* blk, \ + MemRegion mr) { \ + return oop_oop_iterate_m(obj, blk, mr); \ + } + + SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_1(Klass_OOP_OOP_ITERATE_DECL) + SPECIALIZED_OOP_OOP_ITERATE_CLOSURES_3(Klass_OOP_OOP_ITERATE_DECL) + + virtual void array_klasses_do(void f(klassOop k)) {} + virtual void with_array_klasses_do(void f(klassOop k)); + + // Return self, except for abstract classes with exactly 1 + // implementor. Then return the 1 concrete implementation. + Klass *up_cast_abstract(); + + // klass name + symbolOop name() const { return _name; } + void set_name(symbolOop n) { oop_store_without_check((oop*) &_name, (oop) n); } + + friend class klassKlass; + + public: + // jvm support + virtual jint compute_modifier_flags(TRAPS) const; + + public: + // JVMTI support + virtual jint jvmti_class_status() const; + +#ifndef PRODUCT + public: + // Printing + virtual void oop_print_on (oop obj, outputStream* st); + virtual void oop_print_value_on(oop obj, outputStream* st); +#endif + + public: + // Verification + virtual const char* internal_name() const = 0; + virtual void oop_verify_on(oop obj, outputStream* st); + virtual void oop_verify_old_oop(oop obj, oop* p, bool allow_dirty); + // tells whether obj is partially constructed (gc during class loading) + virtual bool oop_partially_loaded(oop obj) const { return false; } + virtual void oop_set_partially_loaded(oop obj) {}; + +#ifndef PRODUCT + void verify_vtable_index(int index); +#endif +}; diff --git a/hotspot/src/share/vm/oops/klass.inline.hpp b/hotspot/src/share/vm/oops/klass.inline.hpp new file mode 100644 index 00000000000..f9ed29c2bc8 --- /dev/null +++ b/hotspot/src/share/vm/oops/klass.inline.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void Klass::set_prototype_header(markOop header) { + assert(!header->has_bias_pattern() || oop_is_instance(), "biased locking currently only supported for Java instances"); + _prototype_header = header; +} diff --git a/hotspot/src/share/vm/oops/klassKlass.cpp b/hotspot/src/share/vm/oops/klassKlass.cpp new file mode 100644 index 00000000000..b38d55cf4e8 --- /dev/null +++ b/hotspot/src/share/vm/oops/klassKlass.cpp @@ -0,0 +1,252 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_klassKlass.cpp.incl" + +int klassKlass::oop_size(oop obj) const { + assert (obj->is_klass(), "must be a klassOop"); + return klassOop(obj)->klass_part()->klass_oop_size(); +} + +klassOop klassKlass::create_klass(TRAPS) { + KlassHandle h_this_klass; + klassKlass o; + // for bootstrapping, handles may not be available yet. + klassOop k = base_create_klass_oop(h_this_klass, header_size(), o.vtbl_value(), CHECK_NULL); + k->set_klass(k); // point to thyself + // Do not try to allocate mirror, java.lang.Class not loaded at this point. + // See Universe::fixup_mirrors() + return k; +} + +void klassKlass::oop_follow_contents(oop obj) { + Klass* k = Klass::cast(klassOop(obj)); + // If we are alive it is valid to keep our superclass and subtype caches alive + MarkSweep::mark_and_push(k->adr_super()); + for (juint i = 0; i < Klass::primary_super_limit(); i++) + MarkSweep::mark_and_push(k->adr_primary_supers()+i); + MarkSweep::mark_and_push(k->adr_secondary_super_cache()); + MarkSweep::mark_and_push(k->adr_secondary_supers()); + MarkSweep::mark_and_push(k->adr_java_mirror()); + MarkSweep::mark_and_push(k->adr_name()); + // We follow the subklass and sibling links at the end of the + // marking phase, since otherwise following them will prevent + // class unloading (all classes are transitively linked from + // java.lang.Object). + MarkSweep::revisit_weak_klass_link(k); + obj->follow_header(); +} + +#ifndef SERIALGC +void klassKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + Klass* k = Klass::cast(klassOop(obj)); + // If we are alive it is valid to keep our superclass and subtype caches alive + PSParallelCompact::mark_and_push(cm, k->adr_super()); + for (juint i = 0; i < Klass::primary_super_limit(); i++) + PSParallelCompact::mark_and_push(cm, k->adr_primary_supers()+i); + PSParallelCompact::mark_and_push(cm, k->adr_secondary_super_cache()); + PSParallelCompact::mark_and_push(cm, k->adr_secondary_supers()); + PSParallelCompact::mark_and_push(cm, k->adr_java_mirror()); + PSParallelCompact::mark_and_push(cm, k->adr_name()); + // We follow the subklass and sibling links at the end of the + // marking phase, since otherwise following them will prevent + // class unloading (all classes are transitively linked from + // java.lang.Object). + PSParallelCompact::revisit_weak_klass_link(cm, k); + obj->follow_header(cm); +} +#endif // SERIALGC + +int klassKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + // Get size before changing pointers + int size = oop_size(obj); + Klass* k = Klass::cast(klassOop(obj)); + blk->do_oop(k->adr_super()); + for (juint i = 0; i < Klass::primary_super_limit(); i++) + blk->do_oop(k->adr_primary_supers()+i); + blk->do_oop(k->adr_secondary_super_cache()); + blk->do_oop(k->adr_secondary_supers()); + blk->do_oop(k->adr_java_mirror()); + blk->do_oop(k->adr_name()); + // The following are in the perm gen and are treated + // specially in a later phase of a perm gen collection; ... + assert(oop(k)->is_perm(), "should be in perm"); + assert(oop(k->subklass())->is_perm_or_null(), "should be in perm"); + assert(oop(k->next_sibling())->is_perm_or_null(), "should be in perm"); + // ... don't scan them normally, but remember this klassKlass + // for later (see, for instance, oop_follow_contents above + // for what MarkSweep does with it. + if (blk->should_remember_klasses()) { + blk->remember_klass(k); + } + obj->oop_iterate_header(blk); + return size; +} + + +int klassKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + // Get size before changing pointers + int size = oop_size(obj); + Klass* k = Klass::cast(klassOop(obj)); + oop* adr; + adr = k->adr_super(); + if (mr.contains(adr)) blk->do_oop(adr); + for (juint i = 0; i < Klass::primary_super_limit(); i++) { + adr = k->adr_primary_supers()+i; + if (mr.contains(adr)) blk->do_oop(adr); + } + adr = k->adr_secondary_super_cache(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = k->adr_secondary_supers(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = k->adr_java_mirror(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = k->adr_name(); + if (mr.contains(adr)) blk->do_oop(adr); + // The following are "weak links" in the perm gen and are + // treated specially in a later phase of a perm gen collection. + assert(oop(k)->is_perm(), "should be in perm"); + assert(oop(k->adr_subklass())->is_perm(), "should be in perm"); + assert(oop(k->adr_next_sibling())->is_perm(), "should be in perm"); + if (blk->should_remember_klasses() + && (mr.contains(k->adr_subklass()) + || mr.contains(k->adr_next_sibling()))) { + blk->remember_klass(k); + } + obj->oop_iterate_header(blk, mr); + return size; +} + + +int klassKlass::oop_adjust_pointers(oop obj) { + // Get size before changing pointers + int size = oop_size(obj); + obj->adjust_header(); + + Klass* k = Klass::cast(klassOop(obj)); + + MarkSweep::adjust_pointer(k->adr_super()); + for (juint i = 0; i < Klass::primary_super_limit(); i++) + MarkSweep::adjust_pointer(k->adr_primary_supers()+i); + MarkSweep::adjust_pointer(k->adr_secondary_super_cache()); + MarkSweep::adjust_pointer(k->adr_secondary_supers()); + MarkSweep::adjust_pointer(k->adr_java_mirror()); + MarkSweep::adjust_pointer(k->adr_name()); + MarkSweep::adjust_pointer(k->adr_subklass()); + MarkSweep::adjust_pointer(k->adr_next_sibling()); + return size; +} + +#ifndef SERIALGC +void klassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { +} + +void klassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { +} + +int klassKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + Klass* k = Klass::cast(klassOop(obj)); + + oop* const beg_oop = k->oop_block_beg(); + oop* const end_oop = k->oop_block_end(); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } + + return oop_size(obj); +} + +int klassKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, HeapWord* end_addr) { + Klass* k = Klass::cast(klassOop(obj)); + + oop* const beg_oop = MAX2((oop*)beg_addr, k->oop_block_beg()); + oop* const end_oop = MIN2((oop*)end_addr, k->oop_block_end()); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } + + return oop_size(obj); +} +#endif // SERIALGC + + +#ifndef PRODUCT + +// Printing + +void klassKlass::oop_print_on(oop obj, outputStream* st) { + Klass::oop_print_on(obj, st); +} + + +void klassKlass::oop_print_value_on(oop obj, outputStream* st) { + Klass::oop_print_value_on(obj, st); +} + +#endif + +const char* klassKlass::internal_name() const { + return "{other class}"; +} + + +// Verification + +void klassKlass::oop_verify_on(oop obj, outputStream* st) { + Klass::oop_verify_on(obj, st); + guarantee(obj->is_perm(), "should be in permspace"); + guarantee(obj->is_klass(), "should be klass"); + + Klass* k = Klass::cast(klassOop(obj)); + if (k->super() != NULL) { + guarantee(k->super()->is_perm(), "should be in permspace"); + guarantee(k->super()->is_klass(), "should be klass"); + } + klassOop ko = k->secondary_super_cache(); + if( ko != NULL ) { + guarantee(ko->is_perm(), "should be in permspace"); + guarantee(ko->is_klass(), "should be klass"); + } + for( uint i = 0; i < primary_super_limit(); i++ ) { + oop ko = k->adr_primary_supers()[i]; // Cannot use normal accessor because it asserts + if( ko != NULL ) { + guarantee(ko->is_perm(), "should be in permspace"); + guarantee(ko->is_klass(), "should be klass"); + } + } + + if (k->java_mirror() != NULL || (k->oop_is_instance() && instanceKlass::cast(klassOop(obj))->is_loaded())) { + guarantee(k->java_mirror() != NULL, "should be allocated"); + guarantee(k->java_mirror()->is_perm(), "should be in permspace"); + guarantee(k->java_mirror()->is_instance(), "should be instance"); + } + if (k->name() != NULL) { + guarantee(Universe::heap()->is_in_permanent(k->name()), + "should be in permspace"); + guarantee(k->name()->is_symbol(), "should be symbol"); + } +} diff --git a/hotspot/src/share/vm/oops/klassKlass.hpp b/hotspot/src/share/vm/oops/klassKlass.hpp new file mode 100644 index 00000000000..c8b5a9a6510 --- /dev/null +++ b/hotspot/src/share/vm/oops/klassKlass.hpp @@ -0,0 +1,81 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A klassKlass serves as the fix point of the klass chain. +// The klass of klassKlass is itself. + +class klassKlass: public Klass { + friend class VMStructs; + private: + juint _alloc_size; // allocation profiling support + public: + // Testing + bool oop_is_klass() const { return true; } + bool is_leaf_class() const { return true; } + + // Sizing + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(klassKlass); + static klassOop create_klass(TRAPS ); + + // Casting from klassOop + static klassKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_klass(), "cast to klassKlass"); + return (klassKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(klassKlass)/HeapWordSize; } + int object_size() const { return align_object_size(header_size()); } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + + // Allocation profiling support + juint alloc_size() const { return _alloc_size; } + void set_alloc_size(juint n) { _alloc_size = n; } + +#ifndef PRODUCT + public: + // Printing + void oop_print_on (oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); +#endif + + public: + // Verification + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); +}; diff --git a/hotspot/src/share/vm/oops/klassOop.cpp b/hotspot/src/share/vm/oops/klassOop.cpp new file mode 100644 index 00000000000..b48eca74b15 --- /dev/null +++ b/hotspot/src/share/vm/oops/klassOop.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_klassOop.cpp.incl" diff --git a/hotspot/src/share/vm/oops/klassOop.hpp b/hotspot/src/share/vm/oops/klassOop.hpp new file mode 100644 index 00000000000..432c4e87b7f --- /dev/null +++ b/hotspot/src/share/vm/oops/klassOop.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A klassOop is the C++ equivalent of a Java class. +// Part of a klassOopDesc is a Klass which handle the +// dispatching for the C++ method calls. + +// klassOop object layout: +// [header ] +// [klass_field] +// [KLASS ] + +class klassOopDesc : public oopDesc { + public: + // size operation + static int header_size() { return sizeof(klassOopDesc)/HeapWordSize; } + + // support for code generation + static int klass_part_offset_in_bytes() { return sizeof(klassOopDesc); } + + // returns the Klass part containing dispatching behavior + Klass* klass_part() { return (Klass*)((address)this + klass_part_offset_in_bytes()); } +}; diff --git a/hotspot/src/share/vm/oops/klassPS.hpp b/hotspot/src/share/vm/oops/klassPS.hpp new file mode 100644 index 00000000000..05e731ec275 --- /dev/null +++ b/hotspot/src/share/vm/oops/klassPS.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +#ifndef KLASS_PS_H +#define KLASS_PS_H + + // Expands to Parallel Scavenge and Parallel Old declarations + +#ifndef SERIALGC +#define PARALLEL_GC_DECLS \ + virtual void oop_copy_contents(PSPromotionManager* pm, oop obj); \ + virtual void oop_push_contents(PSPromotionManager* pm, oop obj); \ + /* Parallel Old GC support \ + \ + The 2-arg version of oop_update_pointers is for objects that are \ + known not to cross chunk boundaries. The 4-arg version is for \ + objects that do (or may) cross chunk boundaries; it updates only those \ + oops that are in the region [beg_addr, end_addr). */ \ + virtual void oop_follow_contents(ParCompactionManager* cm, oop obj); \ + virtual int oop_update_pointers(ParCompactionManager* cm, oop obj); \ + virtual int oop_update_pointers(ParCompactionManager* cm, oop obj, \ + HeapWord* beg_addr, HeapWord* end_addr); + +// Pure virtual version for klass.hpp +#define PARALLEL_GC_DECLS_PV \ + virtual void oop_copy_contents(PSPromotionManager* pm, oop obj) = 0; \ + virtual void oop_push_contents(PSPromotionManager* pm, oop obj) = 0; \ + virtual void oop_follow_contents(ParCompactionManager* cm, oop obj) = 0; \ + virtual int oop_update_pointers(ParCompactionManager* cm, oop obj) = 0; \ + virtual int oop_update_pointers(ParCompactionManager* cm, oop obj, \ + HeapWord* beg_addr, HeapWord* end_addr) = 0; +#else // SERIALGC +#define PARALLEL_GC_DECLS +#define PARALLEL_GC_DECLS_PV +#endif // SERIALGC + +#endif // KLASS_PS_H diff --git a/hotspot/src/share/vm/oops/klassVtable.cpp b/hotspot/src/share/vm/oops/klassVtable.cpp new file mode 100644 index 00000000000..1b105cd2d87 --- /dev/null +++ b/hotspot/src/share/vm/oops/klassVtable.cpp @@ -0,0 +1,1322 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_klassVtable.cpp.incl" + +inline instanceKlass* klassVtable::ik() const { + Klass* k = _klass()->klass_part(); + assert(k->oop_is_instance(), "not an instanceKlass"); + return (instanceKlass*)k; +} + + +// this function computes the vtable size (including the size needed for miranda +// methods) and the number of miranda methods in this class +// Note on Miranda methods: Let's say there is a class C that implements +// interface I. Let's say there is a method m in I that neither C nor any +// of its super classes implement (i.e there is no method of any access, with +// the same name and signature as m), then m is a Miranda method which is +// entered as a public abstract method in C's vtable. From then on it should +// treated as any other public method in C for method over-ride purposes. +void klassVtable::compute_vtable_size_and_num_mirandas(int &vtable_length, + int &num_miranda_methods, + klassOop super, + objArrayOop methods, + AccessFlags class_flags, + oop classloader, + symbolOop classname, + objArrayOop local_interfaces + ) { + + No_Safepoint_Verifier nsv; + + // set up default result values + vtable_length = 0; + num_miranda_methods = 0; + + // start off with super's vtable length + instanceKlass* sk = (instanceKlass*)super->klass_part(); + vtable_length = super == NULL ? 0 : sk->vtable_length(); + + // go thru each method in the methods table to see if it needs a new entry + int len = methods->length(); + for (int i = 0; i < len; i++) { + assert(methods->obj_at(i)->is_method(), "must be a methodOop"); + methodOop m = methodOop(methods->obj_at(i)); + + if (needs_new_vtable_entry(m, super, classloader, classname, class_flags)) { + vtable_length += vtableEntry::size(); // we need a new entry + } + } + + // compute the number of mirandas methods that must be added to the end + num_miranda_methods = get_num_mirandas(super, methods, local_interfaces); + vtable_length += (num_miranda_methods * vtableEntry::size()); + + if (Universe::is_bootstrapping() && vtable_length == 0) { + // array classes don't have their superclass set correctly during + // bootstrapping + vtable_length = Universe::base_vtable_size(); + } + + if (super == NULL && !Universe::is_bootstrapping() && + vtable_length != Universe::base_vtable_size()) { + // Someone is attempting to redefine java.lang.Object incorrectly. The + // only way this should happen is from + // SystemDictionary::resolve_from_stream(), which will detect this later + // and throw a security exception. So don't assert here to let + // the exception occur. + vtable_length = Universe::base_vtable_size(); + } + assert(super != NULL || vtable_length == Universe::base_vtable_size(), + "bad vtable size for class Object"); + assert(vtable_length % vtableEntry::size() == 0, "bad vtable length"); + assert(vtable_length >= Universe::base_vtable_size(), "vtable too small"); +} + +int klassVtable::index_of(methodOop m, int len) const { + assert(m->vtable_index() >= 0, "do not ask this of non-vtable methods"); + return m->vtable_index(); +} + +int klassVtable::initialize_from_super(KlassHandle super) { + if (super.is_null()) { + return 0; + } else { + // copy methods from superKlass + // can't inherit from array class, so must be instanceKlass + assert(super->oop_is_instance(), "must be instance klass"); + instanceKlass* sk = (instanceKlass*)super()->klass_part(); + klassVtable* superVtable = sk->vtable(); + assert(superVtable->length() <= _length, "vtable too short"); +#ifdef ASSERT + superVtable->verify(tty, true); +#endif + superVtable->copy_vtable_to(table()); +#ifndef PRODUCT + if (PrintVtables && Verbose) { + tty->print_cr("copy vtable from %s to %s size %d", sk->internal_name(), klass()->internal_name(), _length); + } +#endif + return superVtable->length(); + } +} + +// Revised lookup semantics introduced 1.3 (Kestral beta) +void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) { + + // Note: Arrays can have intermediate array supers. Use java_super to skip them. + KlassHandle super (THREAD, klass()->java_super()); + int nofNewEntries = 0; + + + if (PrintVtables && !klass()->oop_is_array()) { + ResourceMark rm(THREAD); + tty->print_cr("Initializing: %s", _klass->name()->as_C_string()); + } + +#ifdef ASSERT + oop* end_of_obj = (oop*)_klass() + _klass()->size(); + oop* end_of_vtable = (oop*)&table()[_length]; + assert(end_of_vtable <= end_of_obj, "vtable extends beyond end"); +#endif + + if (Universe::is_bootstrapping()) { + // just clear everything + for (int i = 0; i < _length; i++) table()[i].clear(); + return; + } + + int super_vtable_len = initialize_from_super(super); + if (klass()->oop_is_array()) { + assert(super_vtable_len == _length, "arrays shouldn't introduce new methods"); + } else { + assert(_klass->oop_is_instance(), "must be instanceKlass"); + + objArrayHandle methods(THREAD, ik()->methods()); + int len = methods()->length(); + int initialized = super_vtable_len; + + // update_super_vtable can stop for gc - ensure using handles + for (int i = 0; i < len; i++) { + HandleMark hm(THREAD); + assert(methods()->obj_at(i)->is_method(), "must be a methodOop"); + methodHandle mh(THREAD, (methodOop)methods()->obj_at(i)); + + bool needs_new_entry = update_super_vtable(ik(), mh, super_vtable_len, checkconstraints, CHECK); + + if (needs_new_entry) { + put_method_at(mh(), initialized); + mh()->set_vtable_index(initialized); // set primary vtable index + initialized++; + } + } + + // add miranda methods; it will also update the value of initialized + fill_in_mirandas(initialized); + + // In class hierachieswhere the accesibility is not increasing (i.e., going from private -> + // package_private -> publicprotected), the vtable might actually be smaller than our initial + // calculation. + assert(initialized <= _length, "vtable initialization failed"); + for(;initialized < _length; initialized++) { + put_method_at(NULL, initialized); + } + NOT_PRODUCT(verify(tty, true)); + } +} + +// Interates through the vtables to find the broadest access level. This +// will always be monotomic for valid Java programs - but not neccesarily +// for incompatible class files. +klassVtable::AccessType klassVtable::vtable_accessibility_at(int i) { + // This vtable is not implementing the specific method + if (i >= length()) return acc_private; + + // Compute AccessType for current method. public or protected we are done. + methodOop m = method_at(i); + if (m->is_protected() || m->is_public()) return acc_publicprotected; + + AccessType acc = m->is_package_private() ? acc_package_private : acc_private; + + // Compute AccessType for method in super classes + klassOop super = klass()->super(); + AccessType super_acc = (super != NULL) ? instanceKlass::cast(klass()->super())->vtable()->vtable_accessibility_at(i) + : acc_private; + + // Merge + return (AccessType)MAX2((int)acc, (int)super_acc); +} + + +// Update child's copy of super vtable for overrides +// OR return true if a new vtable entry is required +// Only called for instanceKlass's, i.e. not for arrays +// If that changed, could not use _klass as handle for klass +bool klassVtable::update_super_vtable(instanceKlass* klass, methodHandle target_method, int super_vtable_len, bool checkconstraints, TRAPS) { + ResourceMark rm; + bool allocate_new = true; + assert(klass->oop_is_instance(), "must be instanceKlass"); + + // Initialize the method's vtable index to "nonvirtual". + // If we allocate a vtable entry, we will update it to a non-negative number. + target_method()->set_vtable_index(methodOopDesc::nonvirtual_vtable_index); + + // Static and methods are never in + if (target_method()->is_static() || target_method()->name() == vmSymbols::object_initializer_name()) { + return false; + } + + if (klass->is_final() || target_method()->is_final()) { + // a final method never needs a new entry; final methods can be statically + // resolved and they have to be present in the vtable only if they override + // a super's method, in which case they re-use its entry + allocate_new = false; + } + + // we need a new entry if there is no superclass + if (klass->super() == NULL) { + return allocate_new; + } + + // private methods always have a new entry in the vtable + if (target_method()->is_private()) { + return allocate_new; + } + + // search through the vtable and update overridden entries + // Since check_signature_loaders acquires SystemDictionary_lock + // which can block for gc, once we are in this loop, use handles, not + // unhandled oops unless they are reinitialized for each loop + // handles for name, signature, klass, target_method + // not for match_method, holder + + symbolHandle name(THREAD,target_method()->name()); + symbolHandle signature(THREAD,target_method()->signature()); + for(int i = 0; i < super_vtable_len; i++) { + methodOop match_method = method_at(i); + // Check if method name matches + if (match_method->name() == name() && match_method->signature() == signature()) { + + instanceKlass* holder = (THREAD, instanceKlass::cast(match_method->method_holder())); + + // Check if the match_method is accessable from current class + + bool same_package_init = false; + bool same_package_flag = false; + bool simple_match = match_method->is_public() || match_method->is_protected(); + if (!simple_match) { + same_package_init = true; + same_package_flag = holder->is_same_class_package(_klass->class_loader(), _klass->name()); + + simple_match = match_method->is_package_private() && same_package_flag; + } + // match_method is the superclass' method. Note we can't override + // and shouldn't access superclass' ACC_PRIVATE methods + // (although they have been copied into our vtable) + // A simple form of this statement is: + // if ( (match_method->is_public() || match_method->is_protected()) || + // (match_method->is_package_private() && holder->is_same_class_package(klass->class_loader(), klass->name()))) { + // + // The complexity is introduced it avoid recomputing 'is_same_class_package' which is expensive. + if (simple_match) { + // Check if target_method and match_method has same level of accessibility. The accesibility of the + // match method is the "most-general" visibility of all entries at it's particular vtable index for + // all superclasses. This check must be done before we override the current entry in the vtable. + AccessType at = vtable_accessibility_at(i); + bool same_access = false; + + if ( (at == acc_publicprotected && (target_method()->is_public() || target_method()->is_protected()) + || (at == acc_package_private && (target_method()->is_package_private() && + (( same_package_init && same_package_flag) || + (!same_package_init && holder->is_same_class_package(_klass->class_loader(), _klass->name()))))))) { + same_access = true; + } + + if (checkconstraints) { + // Override vtable entry if passes loader constraint check + // if loader constraint checking requested + // No need to visit his super, since he and his super + // have already made any needed loader constraints. + // Since loader constraints are transitive, it is enough + // to link to the first super, and we get all the others. + symbolHandle signature(THREAD, target_method()->signature()); + Handle this_loader(THREAD, _klass->class_loader()); + instanceKlassHandle super_klass(THREAD, _klass->super()); + Handle super_loader(THREAD, super_klass->class_loader()); + + if (this_loader() != super_loader()) { + ResourceMark rm(THREAD); + char* failed_type_name = + SystemDictionary::check_signature_loaders(signature, this_loader, + super_loader, true, + CHECK_(false)); + if (failed_type_name != NULL) { + const char* msg = "loader constraint violation: when resolving " + "overridden method \"%s\" the class loader (instance" + " of %s) of the current class, %s, and its superclass loader " + "(instance of %s), have different Class objects for the type " + "%s used in the signature"; + char* sig = target_method()->name_and_sig_as_C_string(); + const char* loader1 = SystemDictionary::loader_name(this_loader()); + char* current = _klass->name()->as_C_string(); + const char* loader2 = SystemDictionary::loader_name(super_loader()); + size_t buflen = strlen(msg) + strlen(sig) + strlen(loader1) + + strlen(current) + strlen(loader2) + strlen(failed_type_name); + char* buf = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, buflen); + jio_snprintf(buf, buflen, msg, sig, loader1, current, loader2, + failed_type_name); + THROW_MSG_(vmSymbols::java_lang_LinkageError(), buf, false); + } + } + } + put_method_at(target_method(), i); + + + if (same_access) { + // target and match has same accessiblity - share entry + allocate_new = false; + target_method()->set_vtable_index(i); +#ifndef PRODUCT + if (PrintVtables && Verbose) { + AccessType targetacc; + if (target_method()->is_protected() || + target_method()->is_public()) { + targetacc = acc_publicprotected; + } else { + targetacc = target_method()->is_package_private() ? acc_package_private : acc_private; + } + tty->print_cr("overriding with %s::%s index %d, original flags: %x overriders flags: %x", + _klass->internal_name(), (target_method() != NULL) ? + target_method()->name()->as_C_string() : "", i, + at, targetacc); + } +#endif /*PRODUCT*/ + } else { +#ifndef PRODUCT + if (PrintVtables && Verbose) { + AccessType targetacc; + if (target_method()->is_protected() || + target_method()->is_public()) { + targetacc = acc_publicprotected; + } else { + targetacc = target_method()->is_package_private() ? acc_package_private : acc_private; + } + tty->print_cr("override %s %s::%s at index %d, original flags: %x overriders flags: %x", + allocate_new ? "+ new" : "only", + _klass->internal_name(), (target_method() != NULL) ? + target_method()->name()->as_C_string() : "", i, + at, targetacc); + } +#endif /*PRODUCT*/ + } + } + } + } + return allocate_new; +} + + + +void klassVtable::put_method_at(methodOop m, int index) { + assert(m->is_oop_or_null(), "Not an oop or null"); +#ifndef PRODUCT + if (PrintVtables && Verbose) { + tty->print_cr("adding %s::%s at index %d", _klass->internal_name(), + (m != NULL) ? m->name()->as_C_string() : "", index); + } + assert(unchecked_method_at(index)->is_oop_or_null(), "Not an oop or null"); +#endif + table()[index].set(m); +} + +// Find out if a method "m" with superclass "super", loader "classloader" and +// name "classname" needs a new vtable entry. Let P be a class package defined +// by "classloader" and "classname". +// NOTE: The logic used here is very similar to the one used for computing +// the vtables indices for a method. We cannot directly use that function because, +// when the Universe is boostrapping, a super's vtable might not be initialized. +bool klassVtable::needs_new_vtable_entry(methodOop target_method, + klassOop super, + oop classloader, + symbolOop classname, + AccessFlags class_flags) { + if ((class_flags.is_final() || target_method->is_final()) || + // a final method never needs a new entry; final methods can be statically + // resolved and they have to be present in the vtable only if they override + // a super's method, in which case they re-use its entry + (target_method->is_static()) || + // static methods don't need to be in vtable + (target_method->name() == vmSymbols::object_initializer_name()) + // is never called dynamically-bound + ) { + return false; + } + + // we need a new entry if there is no superclass + if (super == NULL) { + return true; + } + + // private methods always have a new entry in the vtable + if (target_method->is_private()) { + return true; + } + + // search through the super class hierarchy to see if we need + // a new entry + symbolOop name = target_method->name(); + symbolOop signature = target_method->signature(); + klassOop k = super; + methodOop match_method = NULL; + instanceKlass *holder = NULL; + while (k != NULL) { + // lookup through the hierarchy for a method with matching name and sign. + match_method = instanceKlass::cast(k)->lookup_method(name, signature); + if (match_method == NULL) { + break; // we still have to search for a matching miranda method + } + // get the class holding the matching method + holder = instanceKlass::cast(match_method->method_holder()); + + if (!match_method->is_static()) { // we want only instance method matches + if ((target_method->is_public() || target_method->is_protected()) && + (match_method->is_public() || match_method->is_protected())) { + // target and match are public/protected; we do not need a new entry + return false; + } + + if (target_method->is_package_private() && + match_method->is_package_private() && + holder->is_same_class_package(classloader, classname)) { + // target and match are P private; we do not need a new entry + return false; + } + } + + k = holder->super(); // haven't found a match yet; continue to look + } + + // if the target method is public or protected it may have a matching + // miranda method in the super, whose entry it should re-use. + if (target_method->is_public() || target_method->is_protected()) { + instanceKlass *sk = instanceKlass::cast(super); + if (sk->has_miranda_methods()) { + if (sk->lookup_method_in_all_interfaces(name, signature) != NULL) { + return false; // found a matching miranda; we do not need a new entry + } + } + } + + return true; // found no match; we need a new entry +} + +// Support for miranda methods + +// get the vtable index of a miranda method with matching "name" and "signature" +int klassVtable::index_of_miranda(symbolOop name, symbolOop signature) { + // search from the bottom, might be faster + for (int i = (length() - 1); i >= 0; i--) { + methodOop m = table()[i].method(); + if (is_miranda_entry_at(i) && + m->name() == name && m->signature() == signature) { + return i; + } + } + return methodOopDesc::invalid_vtable_index; +} + +// check if an entry is miranda +bool klassVtable::is_miranda_entry_at(int i) { + methodOop m = method_at(i); + klassOop method_holder = m->method_holder(); + instanceKlass *mhk = instanceKlass::cast(method_holder); + + // miranda methods are interface methods in a class's vtable + if (mhk->is_interface()) { + assert(m->is_public() && m->is_abstract(), "should be public and abstract"); + assert(ik()->implements_interface(method_holder) , "this class should implement the interface"); + assert(is_miranda(m, ik()->methods(), ik()->super()), "should be a miranda_method"); + return true; + } + return false; +} + +// check if a method is a miranda method, given a class's methods table and it's super +// the caller must make sure that the method belongs to an interface implemented by the class +bool klassVtable::is_miranda(methodOop m, objArrayOop class_methods, klassOop super) { + symbolOop name = m->name(); + symbolOop signature = m->signature(); + if (instanceKlass::find_method(class_methods, name, signature) == NULL) { + // did not find it in the method table of the current class + if (super == NULL) { + // super doesn't exist + return true; + } else { + if (instanceKlass::cast(super)->lookup_method(name, signature) == NULL) { + // super class hierarchy does not implement it + return true; + } + } + } + return false; +} + +void klassVtable::add_new_mirandas_to_list(GrowableArray* list_of_current_mirandas, + objArrayOop current_interface_methods, + objArrayOop class_methods, + klassOop super) { + // iterate thru the current interface's method to see if it a miranda + int num_methods = current_interface_methods->length(); + for (int i = 0; i < num_methods; i++) { + methodOop im = methodOop(current_interface_methods->obj_at(i)); + bool is_duplicate = false; + int num_of_current_mirandas = list_of_current_mirandas->length(); + // check for duplicate mirandas in different interfaces we implement + for (int j = 0; j < num_of_current_mirandas; j++) { + methodOop miranda = list_of_current_mirandas->at(j); + if ((im->name() == miranda->name()) && + (im->signature() == miranda->signature())) { + is_duplicate = true; + break; + } + } + + if (!is_duplicate) { // we don't want duplicate miranda entries in the vtable + if (is_miranda(im, class_methods, super)) { // is it a miranda at all? + instanceKlass *sk = instanceKlass::cast(super); + // check if it is a duplicate of a super's miranda + if (sk->lookup_method_in_all_interfaces(im->name(), im->signature()) == NULL) { + list_of_current_mirandas->append(im); + } + } + } + } +} + +void klassVtable::get_mirandas(GrowableArray* mirandas, + klassOop super, objArrayOop class_methods, + objArrayOop local_interfaces) { + assert((mirandas->length() == 0) , "current mirandas must be 0"); + + // iterate thru the local interfaces looking for a miranda + int num_local_ifs = local_interfaces->length(); + for (int i = 0; i < num_local_ifs; i++) { + instanceKlass *ik = instanceKlass::cast(klassOop(local_interfaces->obj_at(i))); + add_new_mirandas_to_list(mirandas, ik->methods(), class_methods, super); + // iterate thru each local's super interfaces + objArrayOop super_ifs = ik->transitive_interfaces(); + int num_super_ifs = super_ifs->length(); + for (int j = 0; j < num_super_ifs; j++) { + instanceKlass *sik = instanceKlass::cast(klassOop(super_ifs->obj_at(j))); + add_new_mirandas_to_list(mirandas, sik->methods(), class_methods, super); + } + } +} + +// get number of mirandas +int klassVtable::get_num_mirandas(klassOop super, objArrayOop class_methods, objArrayOop local_interfaces) { + ResourceMark rm; + GrowableArray* mirandas = new GrowableArray(20); + get_mirandas(mirandas, super, class_methods, local_interfaces); + return mirandas->length(); +} + +// fill in mirandas +void klassVtable::fill_in_mirandas(int& initialized) { + ResourceMark rm; + GrowableArray* mirandas = new GrowableArray(20); + instanceKlass *this_ik = ik(); + get_mirandas(mirandas, this_ik->super(), this_ik->methods(), this_ik->local_interfaces()); + int num_mirandas = mirandas->length(); + for (int i = 0; i < num_mirandas; i++) { + put_method_at(mirandas->at(i), initialized); + initialized++; + } +} + +void klassVtable::copy_vtable_to(vtableEntry* start) { + Copy::disjoint_words((HeapWord*)table(), (HeapWord*)start, _length * vtableEntry::size()); +} + +void klassVtable::adjust_method_entries(methodOop* old_methods, methodOop* new_methods, + int methods_length, bool * trace_name_printed) { + // search the vtable for uses of either obsolete or EMCP methods + for (int j = 0; j < methods_length; j++) { + methodOop old_method = old_methods[j]; + methodOop new_method = new_methods[j]; + + // In the vast majority of cases we could get the vtable index + // by using: old_method->vtable_index() + // However, there are rare cases, eg. sun.awt.X11.XDecoratedPeer.getX() + // in sun.awt.X11.XFramePeer where methods occur more than once in the + // vtable, so, alas, we must do an exhaustive search. + for (int index = 0; index < length(); index++) { + if (unchecked_method_at(index) == old_method) { + put_method_at(new_method, index); + + if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) { + if (!(*trace_name_printed)) { + // RC_TRACE_MESG macro has an embedded ResourceMark + RC_TRACE_MESG(("adjust: name=%s", + Klass::cast(old_method->method_holder())->external_name())); + *trace_name_printed = true; + } + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00100000, ("vtable method update: %s(%s)", + new_method->name()->as_C_string(), + new_method->signature()->as_C_string())); + } + } + } + } +} + + +// Garbage collection +void klassVtable::oop_follow_contents() { + int len = length(); + for (int i = 0; i < len; i++) { + MarkSweep::mark_and_push(adr_method_at(i)); + } +} + +#ifndef SERIALGC +void klassVtable::oop_follow_contents(ParCompactionManager* cm) { + int len = length(); + for (int i = 0; i < len; i++) { + PSParallelCompact::mark_and_push(cm, adr_method_at(i)); + } +} +#endif // SERIALGC + +void klassVtable::oop_adjust_pointers() { + int len = length(); + for (int i = 0; i < len; i++) { + MarkSweep::adjust_pointer(adr_method_at(i)); + } +} + +#ifndef SERIALGC +void klassVtable::oop_update_pointers(ParCompactionManager* cm) { + const int n = length(); + for (int i = 0; i < n; i++) { + PSParallelCompact::adjust_pointer(adr_method_at(i)); + } +} + +void klassVtable::oop_update_pointers(ParCompactionManager* cm, + HeapWord* beg_addr, HeapWord* end_addr) { + const int n = length(); + const int entry_size = vtableEntry::size(); + + int beg_idx = 0; + HeapWord* const method_0 = (HeapWord*)adr_method_at(0); + if (beg_addr > method_0) { + // it's safe to use cast, as we have guarantees on vtable size to be sane + beg_idx = int((pointer_delta(beg_addr, method_0) + entry_size - 1) / entry_size); + } + + oop* const beg_oop = adr_method_at(beg_idx); + oop* const end_oop = MIN2((oop*)end_addr, adr_method_at(n)); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; cur_oop += entry_size) { + PSParallelCompact::adjust_pointer(cur_oop); + } +} +#endif // SERIALGC + +// Iterators +void klassVtable::oop_oop_iterate(OopClosure* blk) { + int len = length(); + for (int i = 0; i < len; i++) { + blk->do_oop(adr_method_at(i)); + } +} + +void klassVtable::oop_oop_iterate_m(OopClosure* blk, MemRegion mr) { + int len = length(); + int i; + for (i = 0; i < len; i++) { + if ((HeapWord*)adr_method_at(i) >= mr.start()) break; + } + for (; i < len; i++) { + oop* adr = adr_method_at(i); + if ((HeapWord*)adr < mr.end()) blk->do_oop(adr); + } +} + +//----------------------------------------------------------------------------------------- +// Itable code + +// Initialize a itableMethodEntry +void itableMethodEntry::initialize(methodOop m) { + if (m == NULL) return; + + _method = m; +} + +klassItable::klassItable(instanceKlassHandle klass) { + _klass = klass; + + if (klass->itable_length() > 0) { + itableOffsetEntry* offset_entry = (itableOffsetEntry*)klass->start_of_itable(); + if (offset_entry != NULL && offset_entry->interface_klass() != NULL) { // Check that itable is initialized + // First offset entry points to the first method_entry + intptr_t* method_entry = (intptr_t *)(((address)klass->as_klassOop()) + offset_entry->offset()); + intptr_t* end = klass->end_of_itable(); + + _table_offset = (intptr_t*)offset_entry - (intptr_t*)klass->as_klassOop(); + _size_offset_table = (method_entry - ((intptr_t*)offset_entry)) / itableOffsetEntry::size(); + _size_method_table = (end - method_entry) / itableMethodEntry::size(); + assert(_table_offset >= 0 && _size_offset_table >= 0 && _size_method_table >= 0, "wrong computation"); + return; + } + } + + // This lenght of the itable was either zero, or it has not yet been initialized. + _table_offset = 0; + _size_offset_table = 0; + _size_method_table = 0; +} + +// Garbage Collection + +void klassItable::oop_follow_contents() { + // offset table + itableOffsetEntry* ioe = offset_entry(0); + for(int i = 0; i < _size_offset_table; i++) { + MarkSweep::mark_and_push((oop*)&ioe->_interface); + ioe++; + } + + // method table + itableMethodEntry* ime = method_entry(0); + for(int j = 0; j < _size_method_table; j++) { + MarkSweep::mark_and_push((oop*)&ime->_method); + ime++; + } +} + +#ifndef SERIALGC +void klassItable::oop_follow_contents(ParCompactionManager* cm) { + // offset table + itableOffsetEntry* ioe = offset_entry(0); + for(int i = 0; i < _size_offset_table; i++) { + PSParallelCompact::mark_and_push(cm, (oop*)&ioe->_interface); + ioe++; + } + + // method table + itableMethodEntry* ime = method_entry(0); + for(int j = 0; j < _size_method_table; j++) { + PSParallelCompact::mark_and_push(cm, (oop*)&ime->_method); + ime++; + } +} +#endif // SERIALGC + +void klassItable::oop_adjust_pointers() { + // offset table + itableOffsetEntry* ioe = offset_entry(0); + for(int i = 0; i < _size_offset_table; i++) { + MarkSweep::adjust_pointer((oop*)&ioe->_interface); + ioe++; + } + + // method table + itableMethodEntry* ime = method_entry(0); + for(int j = 0; j < _size_method_table; j++) { + MarkSweep::adjust_pointer((oop*)&ime->_method); + ime++; + } +} + +#ifndef SERIALGC +void klassItable::oop_update_pointers(ParCompactionManager* cm) { + // offset table + itableOffsetEntry* ioe = offset_entry(0); + for(int i = 0; i < _size_offset_table; i++) { + PSParallelCompact::adjust_pointer((oop*)&ioe->_interface); + ioe++; + } + + // method table + itableMethodEntry* ime = method_entry(0); + for(int j = 0; j < _size_method_table; j++) { + PSParallelCompact::adjust_pointer((oop*)&ime->_method); + ime++; + } +} + +void klassItable::oop_update_pointers(ParCompactionManager* cm, + HeapWord* beg_addr, HeapWord* end_addr) { + // offset table + itableOffsetEntry* ioe = offset_entry(0); + for(int i = 0; i < _size_offset_table; i++) { + oop* p = (oop*)&ioe->_interface; + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + ioe++; + } + + // method table + itableMethodEntry* ime = method_entry(0); + for(int j = 0; j < _size_method_table; j++) { + oop* p = (oop*)&ime->_method; + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + ime++; + } +} +#endif // SERIALGC + +// Iterators +void klassItable::oop_oop_iterate(OopClosure* blk) { + // offset table + itableOffsetEntry* ioe = offset_entry(0); + for(int i = 0; i < _size_offset_table; i++) { + blk->do_oop((oop*)&ioe->_interface); + ioe++; + } + + // method table + itableMethodEntry* ime = method_entry(0); + for(int j = 0; j < _size_method_table; j++) { + blk->do_oop((oop*)&ime->_method); + ime++; + } +} + +void klassItable::oop_oop_iterate_m(OopClosure* blk, MemRegion mr) { + // offset table + itableOffsetEntry* ioe = offset_entry(0); + for(int i = 0; i < _size_offset_table; i++) { + oop* adr = (oop*)&ioe->_interface; + if (mr.contains(adr)) blk->do_oop(adr); + ioe++; + } + + // method table + itableMethodEntry* ime = method_entry(0); + for(int j = 0; j < _size_method_table; j++) { + oop* adr = (oop*)&ime->_method; + if (mr.contains(adr)) blk->do_oop(adr); + ime++; + } +} + + +static int initialize_count = 0; + +// Initialization +void klassItable::initialize_itable(bool checkconstraints, TRAPS) { + // Cannot be setup doing bootstrapping + if (Universe::is_bootstrapping()) return; + + int num_interfaces = nof_interfaces(); + if (num_interfaces > 0) { + if (TraceItables) tty->print_cr("%3d: Initializing itables for %s", ++initialize_count, _klass->name()->as_C_string()); + + // In debug mode, we got an extra NULL/NULL entry + debug_only(num_interfaces--); + assert(num_interfaces > 0, "to few interfaces in offset itable"); + + // Interate through all interfaces + int i; + for(i = 0; i < num_interfaces; i++) { + itableOffsetEntry* ioe = offset_entry(i); + KlassHandle interf_h (THREAD, ioe->interface_klass()); + assert(interf_h() != NULL && ioe->offset() != 0, "bad offset entry in itable"); + initialize_itable_for_interface(ioe->offset(), interf_h, checkconstraints, CHECK); + } + +#ifdef ASSERT + // Check that the last entry is empty + itableOffsetEntry* ioe = offset_entry(i); + assert(ioe->interface_klass() == NULL && ioe->offset() == 0, "terminator entry missing"); +#endif + } +} + + +void klassItable::initialize_itable_for_interface(int method_table_offset, KlassHandle interf_h, bool checkconstraints, TRAPS) { + objArrayHandle methods(THREAD, instanceKlass::cast(interf_h())->methods()); + int nof_methods = methods()->length(); + HandleMark hm; + KlassHandle klass = _klass; + assert(nof_methods > 0, "at least one method must exist for interface to be in vtable") + Handle interface_loader (THREAD, instanceKlass::cast(interf_h())->class_loader()); + int ime_num = 0; + + // Skip first methodOop if it is a class initializer + int i = ((methodOop)methods()->obj_at(0))->name() != vmSymbols::class_initializer_name() ? 0 : 1; + + // m, method_name, method_signature, klass reset each loop so they + // don't need preserving across check_signature_loaders call + // methods needs a handle in case of gc from check_signature_loaders + for(; i < nof_methods; i++) { + methodOop m = (methodOop)methods()->obj_at(i); + symbolOop method_name = m->name(); + symbolOop method_signature = m->signature(); + + // This is same code as in Linkresolver::lookup_instance_method_in_klasses + methodOop target = klass->uncached_lookup_method(method_name, method_signature); + while (target != NULL && target->is_static()) { + // continue with recursive lookup through the superclass + klassOop super = Klass::cast(target->method_holder())->super(); + target = (super == NULL) ? methodOop(NULL) : Klass::cast(super)->uncached_lookup_method(method_name, method_signature); + } + if (target == NULL || !target->is_public() || target->is_abstract()) { + // Entry do not resolve. Leave it empty + } else { + // Entry did resolve, check loader constraints before initializing + // if checkconstraints requested + methodHandle target_h (THREAD, target); // preserve across gc + if (checkconstraints) { + Handle method_holder_loader (THREAD, instanceKlass::cast(target->method_holder())->class_loader()); + if (method_holder_loader() != interface_loader()) { + ResourceMark rm(THREAD); + char* failed_type_name = + SystemDictionary::check_signature_loaders(method_signature, + method_holder_loader, + interface_loader, + true, CHECK); + if (failed_type_name != NULL) { + const char* msg = "loader constraint violation in interface " + "itable initialization: when resolving method \"%s\" the class" + " loader (instance of %s) of the current class, %s, " + "and the class loader (instance of %s) for interface " + "%s have different Class objects for the type %s " + "used in the signature"; + char* sig = target_h()->name_and_sig_as_C_string(); + const char* loader1 = SystemDictionary::loader_name(method_holder_loader()); + char* current = klass->name()->as_C_string(); + const char* loader2 = SystemDictionary::loader_name(interface_loader()); + char* iface = instanceKlass::cast(interf_h())->name()->as_C_string(); + size_t buflen = strlen(msg) + strlen(sig) + strlen(loader1) + + strlen(current) + strlen(loader2) + strlen(iface) + + strlen(failed_type_name); + char* buf = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, buflen); + jio_snprintf(buf, buflen, msg, sig, loader1, current, loader2, + iface, failed_type_name); + THROW_MSG(vmSymbols::java_lang_LinkageError(), buf); + } + } + } + + // ime may have moved during GC so recalculate address + itableOffsetEntry::method_entry(_klass(), method_table_offset)[ime_num].initialize(target_h()); + } + // Progress to next entry + ime_num++; + } +} + +// Update entry for specic methodOop +void klassItable::initialize_with_method(methodOop m) { + itableMethodEntry* ime = method_entry(0); + for(int i = 0; i < _size_method_table; i++) { + if (ime->method() == m) { + ime->initialize(m); + } + ime++; + } +} + +void klassItable::adjust_method_entries(methodOop* old_methods, methodOop* new_methods, + int methods_length, bool * trace_name_printed) { + // search the itable for uses of either obsolete or EMCP methods + for (int j = 0; j < methods_length; j++) { + methodOop old_method = old_methods[j]; + methodOop new_method = new_methods[j]; + itableMethodEntry* ime = method_entry(0); + + for (int i = 0; i < _size_method_table; i++) { + if (ime->method() == old_method) { + ime->initialize(new_method); + + if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) { + if (!(*trace_name_printed)) { + // RC_TRACE_MESG macro has an embedded ResourceMark + RC_TRACE_MESG(("adjust: name=%s", + Klass::cast(old_method->method_holder())->external_name())); + *trace_name_printed = true; + } + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00200000, ("itable method update: %s(%s)", + new_method->name()->as_C_string(), + new_method->signature()->as_C_string())); + } + break; + } + ime++; + } + } +} + + +// Setup +class InterfaceVisiterClosure : public StackObj { + public: + virtual void doit(klassOop intf, int method_count) = 0; +}; + +// Visit all interfaces with at-least one method (excluding ) +void visit_all_interfaces(objArrayOop transitive_intf, InterfaceVisiterClosure *blk) { + // Handle array argument + for(int i = 0; i < transitive_intf->length(); i++) { + klassOop intf = (klassOop)transitive_intf->obj_at(i); + assert(Klass::cast(intf)->is_interface(), "sanity check"); + + // Find no. of methods excluding a + int method_count = instanceKlass::cast(intf)->methods()->length(); + if (method_count > 0) { + methodOop m = (methodOop)instanceKlass::cast(intf)->methods()->obj_at(0); + assert(m != NULL && m->is_method(), "sanity check"); + if (m->name() == vmSymbols::object_initializer_name()) { + method_count--; + } + } + + // Only count interfaces with at least one method + if (method_count > 0) { + blk->doit(intf, method_count); + } + } +} + +class CountInterfacesClosure : public InterfaceVisiterClosure { + private: + int _nof_methods; + int _nof_interfaces; + public: + CountInterfacesClosure() { _nof_methods = 0; _nof_interfaces = 0; } + + int nof_methods() const { return _nof_methods; } + int nof_interfaces() const { return _nof_interfaces; } + + void doit(klassOop intf, int method_count) { _nof_methods += method_count; _nof_interfaces++; } +}; + +class SetupItableClosure : public InterfaceVisiterClosure { + private: + itableOffsetEntry* _offset_entry; + itableMethodEntry* _method_entry; + address _klass_begin; + public: + SetupItableClosure(address klass_begin, itableOffsetEntry* offset_entry, itableMethodEntry* method_entry) { + _klass_begin = klass_begin; + _offset_entry = offset_entry; + _method_entry = method_entry; + } + + itableMethodEntry* method_entry() const { return _method_entry; } + + void doit(klassOop intf, int method_count) { + int offset = ((address)_method_entry) - _klass_begin; + _offset_entry->initialize(intf, offset); + _offset_entry++; + _method_entry += method_count; + } +}; + +int klassItable::compute_itable_size(objArrayHandle transitive_interfaces) { + // Count no of interfaces and total number of interface methods + CountInterfacesClosure cic; + visit_all_interfaces(transitive_interfaces(), &cic); + + // Add one extra entry in debug mode, so we can null-terminate the table + int nof_methods = cic.nof_methods(); + int nof_interfaces = cic.nof_interfaces(); + debug_only(if (nof_interfaces > 0) nof_interfaces++); + + int itable_size = calc_itable_size(nof_interfaces, nof_methods); + + // Statistics + update_stats(itable_size * HeapWordSize); + + return itable_size; +} + + +// Fill out offset table and interface klasses into the itable space +void klassItable::setup_itable_offset_table(instanceKlassHandle klass) { + if (klass->itable_length() == 0) return; + assert(!klass->is_interface(), "Should have zero length itable"); + + // Count no of interfaces and total number of interface methods + CountInterfacesClosure cic; + visit_all_interfaces(klass->transitive_interfaces(), &cic); + int nof_methods = cic.nof_methods(); + int nof_interfaces = cic.nof_interfaces(); + + // Add one extra entry in debug mode, so we can null-terminate the table + debug_only(if (nof_interfaces > 0) nof_interfaces++); + + assert(compute_itable_size(objArrayHandle(klass->transitive_interfaces())) == + calc_itable_size(nof_interfaces, nof_methods), + "mismatch calculation of itable size"); + + // Fill-out offset table + itableOffsetEntry* ioe = (itableOffsetEntry*)klass->start_of_itable(); + itableMethodEntry* ime = (itableMethodEntry*)(ioe + nof_interfaces); + intptr_t* end = klass->end_of_itable(); + assert((oop*)(ime + nof_methods) <= klass->start_of_static_fields(), "wrong offset calculation (1)"); + assert((oop*)(end) == (oop*)(ime + nof_methods), "wrong offset calculation (2)"); + + // Visit all interfaces and initialize itable offset table + SetupItableClosure sic((address)klass->as_klassOop(), ioe, ime); + visit_all_interfaces(klass->transitive_interfaces(), &sic); + +#ifdef ASSERT + ime = sic.method_entry(); + oop* v = (oop*) klass->end_of_itable(); + assert( (oop*)(ime) == v, "wrong offset calculation (2)"); +#endif +} + + +// m must be a method in an interface +int klassItable::compute_itable_index(methodOop m) { + klassOop intf = m->method_holder(); + assert(instanceKlass::cast(intf)->is_interface(), "sanity check"); + objArrayOop methods = instanceKlass::cast(intf)->methods(); + int index = 0; + while(methods->obj_at(index) != m) { + index++; + assert(index < methods->length(), "should find index for resolve_invoke"); + } + // Adjust for , which is left out of table if first method + if (methods->length() > 0 && ((methodOop)methods->obj_at(0))->name() == vmSymbols::class_initializer_name()) { + index--; + } + return index; +} + +void klassVtable::verify(outputStream* st, bool forced) { + // make sure table is initialized + if (!Universe::is_fully_initialized()) return; +#ifndef PRODUCT + // avoid redundant verifies + if (!forced && _verify_count == Universe::verify_count()) return; + _verify_count = Universe::verify_count(); +#endif + oop* end_of_obj = (oop*)_klass() + _klass()->size(); + oop* end_of_vtable = (oop *)&table()[_length]; + if (end_of_vtable > end_of_obj) { + fatal1("klass %s: klass object too short (vtable extends beyond end)", + _klass->internal_name()); + } + + for (int i = 0; i < _length; i++) table()[i].verify(this, st); + // verify consistency with superKlass vtable + klassOop super = _klass->super(); + if (super != NULL) { + instanceKlass* sk = instanceKlass::cast(super); + klassVtable* vt = sk->vtable(); + for (int i = 0; i < vt->length(); i++) { + verify_against(st, vt, i); + } + } +} + +void klassVtable::verify_against(outputStream* st, klassVtable* vt, int index) { + vtableEntry* vte = &vt->table()[index]; + if (vte->method()->name() != table()[index].method()->name() || + vte->method()->signature() != table()[index].method()->signature()) { + fatal("mismatched name/signature of vtable entries"); + } +} + +#ifndef PRODUCT +void klassVtable::print() { + ResourceMark rm; + tty->print("klassVtable for klass %s (length %d):\n", _klass->internal_name(), length()); + for (int i = 0; i < length(); i++) { + table()[i].print(); + tty->cr(); + } +} +#endif + +void vtableEntry::verify(klassVtable* vt, outputStream* st) { + NOT_PRODUCT(FlagSetting fs(IgnoreLockingAssertions, true)); + assert(method() != NULL, "must have set method"); + method()->verify(); + // we sub_type, because it could be a miranda method + if (!vt->klass()->is_subtype_of(method()->method_holder())) { +#ifndef PRODUCT + print(); +#endif + fatal1("vtableEntry %#lx: method is from subclass", this); + } +} + +#ifndef PRODUCT + +void vtableEntry::print() { + ResourceMark rm; + tty->print("vtableEntry %s: ", method()->name()->as_C_string()); + if (Verbose) { + tty->print("m %#lx ", (address)method()); + } +} + +class VtableStats : AllStatic { + public: + static int no_klasses; // # classes with vtables + static int no_array_klasses; // # array classes + static int no_instance_klasses; // # instanceKlasses + static int sum_of_vtable_len; // total # of vtable entries + static int sum_of_array_vtable_len; // total # of vtable entries in array klasses only + static int fixed; // total fixed overhead in bytes + static int filler; // overhead caused by filler bytes + static int entries; // total bytes consumed by vtable entries + static int array_entries; // total bytes consumed by array vtable entries + + static void do_class(klassOop k) { + Klass* kl = k->klass_part(); + klassVtable* vt = kl->vtable(); + if (vt == NULL) return; + no_klasses++; + if (kl->oop_is_instance()) { + no_instance_klasses++; + kl->array_klasses_do(do_class); + } + if (kl->oop_is_array()) { + no_array_klasses++; + sum_of_array_vtable_len += vt->length(); + } + sum_of_vtable_len += vt->length(); + } + + static void compute() { + SystemDictionary::classes_do(do_class); + fixed = no_klasses * oopSize; // vtable length + // filler size is a conservative approximation + filler = oopSize * (no_klasses - no_instance_klasses) * (sizeof(instanceKlass) - sizeof(arrayKlass) - 1); + entries = sizeof(vtableEntry) * sum_of_vtable_len; + array_entries = sizeof(vtableEntry) * sum_of_array_vtable_len; + } +}; + +int VtableStats::no_klasses = 0; +int VtableStats::no_array_klasses = 0; +int VtableStats::no_instance_klasses = 0; +int VtableStats::sum_of_vtable_len = 0; +int VtableStats::sum_of_array_vtable_len = 0; +int VtableStats::fixed = 0; +int VtableStats::filler = 0; +int VtableStats::entries = 0; +int VtableStats::array_entries = 0; + +void klassVtable::print_statistics() { + ResourceMark rm; + HandleMark hm; + VtableStats::compute(); + tty->print_cr("vtable statistics:"); + tty->print_cr("%6d classes (%d instance, %d array)", VtableStats::no_klasses, VtableStats::no_instance_klasses, VtableStats::no_array_klasses); + int total = VtableStats::fixed + VtableStats::filler + VtableStats::entries; + tty->print_cr("%6d bytes fixed overhead (refs + vtable object header)", VtableStats::fixed); + tty->print_cr("%6d bytes filler overhead", VtableStats::filler); + tty->print_cr("%6d bytes for vtable entries (%d for arrays)", VtableStats::entries, VtableStats::array_entries); + tty->print_cr("%6d bytes total", total); +} + +bool klassVtable::check_no_old_entries() { + // Check that there really is no entry + for (int i = 0; i < length(); i++) { + methodOop m = unchecked_method_at(i); + if (m != NULL) { + if (m->is_old()) { + return false; + } + } + } + return true; +} + +void klassVtable::dump_vtable() { + tty->print_cr("vtable dump --"); + for (int i = 0; i < length(); i++) { + methodOop m = unchecked_method_at(i); + if (m != NULL) { + tty->print(" (%5d) ", i); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } + } +} + +int klassItable::_total_classes; // Total no. of classes with itables +long klassItable::_total_size; // Total no. of bytes used for itables + +void klassItable::print_statistics() { + tty->print_cr("itable statistics:"); + tty->print_cr("%6d classes with itables", _total_classes); + tty->print_cr("%6d K uses for itables (average by class: %d bytes)", _total_size / K, _total_size / _total_classes); +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/oops/klassVtable.hpp b/hotspot/src/share/vm/oops/klassVtable.hpp new file mode 100644 index 00000000000..221e1d21d66 --- /dev/null +++ b/hotspot/src/share/vm/oops/klassVtable.hpp @@ -0,0 +1,316 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A klassVtable abstracts the variable-length vtable that is embedded in instanceKlass +// and arrayKlass. klassVtable objects are used just as convenient transient accessors to the vtable, +// not to actually hold the vtable data. +// Note: the klassVtable should not be accessed before the class has been verified +// (until that point, the vtable is uninitialized). + +// Currently a klassVtable contains a direct reference to the vtable data, and is therefore +// not preserved across GCs. + +class vtableEntry; + +class klassVtable : public ResourceObj { + KlassHandle _klass; // my klass + int _tableOffset; // offset of start of vtable data within klass + int _length; // length of vtable (number of entries) +#ifndef PRODUCT + int _verify_count; // to make verify faster +#endif + + // Ordering important, so greater_than (>) can be used as an merge operator. + enum AccessType { + acc_private = 0, + acc_package_private = 1, + acc_publicprotected = 2 + }; + + public: + klassVtable(KlassHandle h_klass, void* base, int length) : _klass(h_klass) { + _tableOffset = (address)base - (address)h_klass(); _length = length; + } + + // accessors + vtableEntry* table() const { return (vtableEntry*)(address(_klass()) + _tableOffset); } + KlassHandle klass() const { return _klass; } + int length() const { return _length; } + inline methodOop method_at(int i) const; + inline methodOop unchecked_method_at(int i) const; + inline oop* adr_method_at(int i) const; + + // searching; all methods return -1 if not found + int index_of(methodOop m) const { return index_of(m, _length); } + int index_of_miranda(symbolOop name, symbolOop signature); + + void initialize_vtable(bool checkconstraints, TRAPS); // initialize vtable of a new klass + + // conputes vtable length (in words) and the number of miranda methods + static void compute_vtable_size_and_num_mirandas(int &vtable_length, int &num_miranda_methods, + klassOop super, objArrayOop methods, + AccessFlags class_flags, oop classloader, + symbolOop classname, objArrayOop local_interfaces); + + // RedefineClasses() API support: + // If any entry of this vtable points to any of old_methods, + // replace it with the corresponding new_method. + // trace_name_printed is set to true if the current call has + // printed the klass name so that other routines in the adjust_* + // group don't print the klass name. + void adjust_method_entries(methodOop* old_methods, methodOop* new_methods, + int methods_length, bool * trace_name_printed); + + // Garbage collection + void oop_follow_contents(); + void oop_adjust_pointers(); + +#ifndef SERIALGC + // Parallel Old + void oop_follow_contents(ParCompactionManager* cm); + void oop_update_pointers(ParCompactionManager* cm); + void oop_update_pointers(ParCompactionManager* cm, + HeapWord* beg_addr, HeapWord* end_addr); +#endif // SERIALGC + + // Iterators + void oop_oop_iterate(OopClosure* blk); + void oop_oop_iterate_m(OopClosure* blk, MemRegion mr); + + // Debugging code + void print() PRODUCT_RETURN; + void verify(outputStream* st, bool force = false); + static void print_statistics() PRODUCT_RETURN; + +#ifndef PRODUCT + bool check_no_old_entries(); + void dump_vtable(); +#endif + + protected: + friend class vtableEntry; + private: + void copy_vtable_to(vtableEntry* start); + int initialize_from_super(KlassHandle super); + int index_of(methodOop m, int len) const; // same as index_of, but search only up to len + void put_method_at(methodOop m, int index); + static bool needs_new_vtable_entry(methodOop m, klassOop super, oop classloader, symbolOop classname, AccessFlags access_flags); + AccessType vtable_accessibility_at(int i); + + bool update_super_vtable(instanceKlass* klass, methodHandle target_method, int super_vtable_len, bool checkconstraints, TRAPS); + + // support for miranda methods + bool is_miranda_entry_at(int i); + void fill_in_mirandas(int& initialized); + static bool is_miranda(methodOop m, objArrayOop class_methods, klassOop super); + static void add_new_mirandas_to_list(GrowableArray* list_of_current_mirandas, objArrayOop current_interface_methods, objArrayOop class_methods, klassOop super); + static void get_mirandas(GrowableArray* mirandas, klassOop super, objArrayOop class_methods, objArrayOop local_interfaces); + static int get_num_mirandas(klassOop super, objArrayOop class_methods, objArrayOop local_interfaces); + + + void verify_against(outputStream* st, klassVtable* vt, int index); + inline instanceKlass* ik() const; +}; + + +// private helper class for klassVtable +// description of entry points: +// destination is interpreted: +// from_compiled_code_entry_point -> c2iadapter +// from_interpreter_entry_point -> interpreter entry point +// destination is compiled: +// from_compiled_code_entry_point -> nmethod entry point +// from_interpreter_entry_point -> i2cadapter +class vtableEntry VALUE_OBJ_CLASS_SPEC { + public: + // size in words + static int size() { + return sizeof(vtableEntry) / sizeof(HeapWord); + } + static int method_offset_in_bytes() { return offset_of(vtableEntry, _method); } + methodOop method() const { return _method; } + + private: + methodOop _method; + void set(methodOop method) { assert(method != NULL, "use clear"); _method = method; } + void clear() { _method = NULL; } + void print() PRODUCT_RETURN; + void verify(klassVtable* vt, outputStream* st); + + friend class klassVtable; +}; + + +inline methodOop klassVtable::method_at(int i) const { + assert(i >= 0 && i < _length, "index out of bounds"); + assert(table()[i].method() != NULL, "should not be null"); + assert(oop(table()[i].method())->is_method(), "should be method"); + return table()[i].method(); +} + +inline methodOop klassVtable::unchecked_method_at(int i) const { + assert(i >= 0 && i < _length, "index out of bounds"); + return table()[i].method(); +} + +inline oop* klassVtable::adr_method_at(int i) const { + // Allow one past the last entry to be referenced; useful for loop bounds. + assert(i >= 0 && i <= _length, "index out of bounds"); + return (oop*)(address(table() + i) + vtableEntry::method_offset_in_bytes()); +} + +// -------------------------------------------------------------------------------- +class klassItable; +class itableMethodEntry; + +class itableOffsetEntry VALUE_OBJ_CLASS_SPEC { + private: + klassOop _interface; + int _offset; + public: + klassOop interface_klass() const { return _interface; } + int offset() const { return _offset; } + + static itableMethodEntry* method_entry(klassOop k, int offset) { return (itableMethodEntry*)(((address)k) + offset); } + itableMethodEntry* first_method_entry(klassOop k) { return method_entry(k, _offset); } + + void initialize(klassOop interf, int offset) { _interface = interf; _offset = offset; } + + // Static size and offset accessors + static int size() { return sizeof(itableOffsetEntry) / HeapWordSize; } // size in words + static int interface_offset_in_bytes() { return offset_of(itableOffsetEntry, _interface); } + static int offset_offset_in_bytes() { return offset_of(itableOffsetEntry, _offset); } + + friend class klassItable; +}; + + +class itableMethodEntry VALUE_OBJ_CLASS_SPEC { + private: + methodOop _method; + + public: + methodOop method() const { return _method; } + + void clear() { _method = NULL; } + + void initialize(methodOop method); + + // Static size and offset accessors + static int size() { return sizeof(itableMethodEntry) / HeapWordSize; } // size in words + static int method_offset_in_bytes() { return offset_of(itableMethodEntry, _method); } + + friend class klassItable; +}; + +// +// Format of an itable +// +// ---- offset table --- +// klassOop of interface 1 \ +// offset to vtable from start of oop / offset table entry +// ... +// klassOop of interface n \ +// offset to vtable from start of oop / offset table entry +// --- vtable for interface 1 --- +// methodOop \ +// compiler entry point / method table entry +// ... +// methodOop \ +// compiler entry point / method table entry +// -- vtable for interface 2 --- +// ... +// +class klassItable : public ResourceObj { + private: + instanceKlassHandle _klass; // my klass + int _table_offset; // offset of start of itable data within klass (in words) + int _size_offset_table; // size of offset table (in itableOffset entries) + int _size_method_table; // size of methodtable (in itableMethodEntry entries) + + void initialize_itable_for_interface(int method_table_offset, KlassHandle interf_h, bool checkconstraints, TRAPS); + public: + klassItable(instanceKlassHandle klass); + + itableOffsetEntry* offset_entry(int i) { assert(0 <= i && i <= _size_offset_table, "index out of bounds"); + return &((itableOffsetEntry*)vtable_start())[i]; } + + itableMethodEntry* method_entry(int i) { assert(0 <= i && i <= _size_method_table, "index out of bounds"); + return &((itableMethodEntry*)method_start())[i]; } + + int nof_interfaces() { return _size_offset_table; } + + // Initialization + void initialize_itable(bool checkconstraints, TRAPS); + + // Updates + void initialize_with_method(methodOop m); + + // RedefineClasses() API support: + // if any entry of this itable points to any of old_methods, + // replace it with the corresponding new_method. + // trace_name_printed is set to true if the current call has + // printed the klass name so that other routines in the adjust_* + // group don't print the klass name. + void adjust_method_entries(methodOop* old_methods, methodOop* new_methods, + int methods_length, bool * trace_name_printed); + + // Garbage collection + void oop_follow_contents(); + void oop_adjust_pointers(); + +#ifndef SERIALGC + // Parallel Old + void oop_follow_contents(ParCompactionManager* cm); + void oop_update_pointers(ParCompactionManager* cm); + void oop_update_pointers(ParCompactionManager* cm, + HeapWord* beg_addr, HeapWord* end_addr); +#endif // SERIALGC + + // Iterators + void oop_oop_iterate(OopClosure* blk); + void oop_oop_iterate_m(OopClosure* blk, MemRegion mr); + + // Setup of itable + static int compute_itable_size(objArrayHandle transitive_interfaces); + static void setup_itable_offset_table(instanceKlassHandle klass); + + // Resolving of method to index + static int compute_itable_index(methodOop m); + + // Debugging/Statistics + static void print_statistics() PRODUCT_RETURN; + private: + intptr_t* vtable_start() const { return ((intptr_t*)_klass()) + _table_offset; } + intptr_t* method_start() const { return vtable_start() + _size_offset_table * itableOffsetEntry::size(); } + + // Helper methods + static int calc_itable_size(int num_interfaces, int num_methods) { return (num_interfaces * itableOffsetEntry::size()) + (num_methods * itableMethodEntry::size()); } + + // Statistics + NOT_PRODUCT(static int _total_classes;) // Total no. of classes with itables + NOT_PRODUCT(static long _total_size;) // Total no. of bytes used for itables + + static void update_stats(int size) PRODUCT_RETURN NOT_PRODUCT({ _total_classes++; _total_size += size; }) +}; diff --git a/hotspot/src/share/vm/oops/markOop.cpp b/hotspot/src/share/vm/oops/markOop.cpp new file mode 100644 index 00000000000..855b525f4f7 --- /dev/null +++ b/hotspot/src/share/vm/oops/markOop.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_markOop.cpp.incl" + + +void markOopDesc::print_on(outputStream* st) const { + if (is_locked()) { + st->print("locked(0x%lx)->", value()); + markOop(*(markOop*)value())->print_on(st); + } else { + assert(is_unlocked(), "just checking"); + st->print("mark("); + st->print("hash %#lx,", hash()); + st->print("age %d)", age()); + } +} diff --git a/hotspot/src/share/vm/oops/markOop.hpp b/hotspot/src/share/vm/oops/markOop.hpp new file mode 100644 index 00000000000..def7c1844d3 --- /dev/null +++ b/hotspot/src/share/vm/oops/markOop.hpp @@ -0,0 +1,360 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The markOop describes the header of an object. +// +// Note that the mark is not a real oop but just a word. +// It is placed in the oop hierarchy for historical reasons. +// +// Bit-format of an object header (most significant first): +// +// +// unused:0/25 hash:25/31 age:4 biased_lock:1 lock:2 = 32/64 bits +// +// - hash contains the identity hash value: largest value is +// 31 bits, see os::random(). Also, 64-bit vm's require +// a hash value no bigger than 32 bits because they will not +// properly generate a mask larger than that: see library_call.cpp +// and c1_CodePatterns_sparc.cpp. +// +// - the biased lock pattern is used to bias a lock toward a given +// thread. When this pattern is set in the low three bits, the lock +// is either biased toward a given thread or "anonymously" biased, +// indicating that it is possible for it to be biased. When the +// lock is biased toward a given thread, locking and unlocking can +// be performed by that thread without using atomic operations. +// When a lock's bias is revoked, it reverts back to the normal +// locking scheme described below. +// +// Note that we are overloading the meaning of the "unlocked" state +// of the header. Because we steal a bit from the age we can +// guarantee that the bias pattern will never be seen for a truly +// unlocked object. +// +// Note also that the biased state contains the age bits normally +// contained in the object header. Large increases in scavenge +// times were seen when these bits were absent and an arbitrary age +// assigned to all biased objects, because they tended to consume a +// significant fraction of the eden semispaces and were not +// promoted promptly, causing an increase in the amount of copying +// performed. The runtime system aligns all JavaThread* pointers to +// a very large value (currently 128 bytes) to make room for the +// age bits when biased locking is enabled. +// +// [JavaThread* | epoch | age | 1 | 01] lock is biased toward given thread +// [0 | epoch | age | 1 | 01] lock is anonymously biased +// +// - the two lock bits are used to describe three states: locked/unlocked and monitor. +// +// [ptr | 00] locked ptr points to real header on stack +// [header | 0 | 01] unlocked regular object header +// [ptr | 10] monitor inflated lock (header is wapped out) +// [ptr | 11] marked used by markSweep to mark an object +// not valid at any other time +// +// We assume that stack/thread pointers have the lowest two bits cleared. + +class BasicLock; +class ObjectMonitor; +class JavaThread; + +class markOopDesc: public oopDesc { + private: + // Conversion + uintptr_t value() const { return (uintptr_t) this; } + + public: + // Constants + enum { age_bits = 4, + lock_bits = 2, + biased_lock_bits = 1, + max_hash_bits = BitsPerOop - age_bits - lock_bits - biased_lock_bits, + hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits, + epoch_bits = 2 + }; + + // The biased locking code currently requires that the age bits be + // contiguous to the lock bits. Class data sharing would prefer the + // hash bits to be lower down to provide more random hash codes for + // shared read-only symbolOop objects, because these objects' mark + // words are set to their own address with marked_value in the lock + // bit, and using lower bits would make their identity hash values + // more random. However, the performance decision was made in favor + // of the biased locking code. + + enum { lock_shift = 0, + biased_lock_shift = lock_bits, + age_shift = lock_bits + biased_lock_bits, + hash_shift = lock_bits + biased_lock_bits + age_bits, + epoch_shift = hash_shift + }; + + enum { lock_mask = right_n_bits(lock_bits), + lock_mask_in_place = lock_mask << lock_shift, + biased_lock_mask = right_n_bits(lock_bits + biased_lock_bits), + biased_lock_mask_in_place= biased_lock_mask << lock_shift, + biased_lock_bit_in_place = 1 << biased_lock_shift, + age_mask = right_n_bits(age_bits), + age_mask_in_place = age_mask << age_shift, + epoch_mask = right_n_bits(epoch_bits), + epoch_mask_in_place = epoch_mask << epoch_shift +#ifndef _WIN64 + ,hash_mask = right_n_bits(hash_bits), + hash_mask_in_place = (address_word)hash_mask << hash_shift +#endif + }; + + // Alignment of JavaThread pointers encoded in object header required by biased locking + enum { biased_lock_alignment = 2 << (epoch_shift + epoch_bits) + }; + +#ifdef _WIN64 + // These values are too big for Win64 + const static uintptr_t hash_mask = right_n_bits(hash_bits); + const static uintptr_t hash_mask_in_place = + (address_word)hash_mask << hash_shift; +#endif + + enum { locked_value = 0, + unlocked_value = 1, + monitor_value = 2, + marked_value = 3, + biased_lock_pattern = 5 + }; + + enum { no_hash = 0 }; // no hash value assigned + + enum { no_hash_in_place = (address_word)no_hash << hash_shift, + no_lock_in_place = unlocked_value + }; + + enum { max_age = age_mask }; + + enum { max_bias_epoch = epoch_mask }; + + // Biased Locking accessors. + // These must be checked by all code which calls into the + // ObjectSynchronizer and other code. The biasing is not understood + // by the lower-level CAS-based locking code, although the runtime + // fixes up biased locks to be compatible with it when a bias is + // revoked. + bool has_bias_pattern() const { + return (mask_bits(value(), biased_lock_mask_in_place) == biased_lock_pattern); + } + JavaThread* biased_locker() const { + assert(has_bias_pattern(), "should not call this otherwise"); + return (JavaThread*) ((intptr_t) (mask_bits(value(), ~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place)))); + } + // Indicates that the mark has the bias bit set but that it has not + // yet been biased toward a particular thread + bool is_biased_anonymously() const { + return (has_bias_pattern() && (biased_locker() == NULL)); + } + // Indicates epoch in which this bias was acquired. If the epoch + // changes due to too many bias revocations occurring, the biases + // from the previous epochs are all considered invalid. + int bias_epoch() const { + assert(has_bias_pattern(), "should not call this otherwise"); + return (mask_bits(value(), epoch_mask_in_place) >> epoch_shift); + } + markOop set_bias_epoch(int epoch) { + assert(has_bias_pattern(), "should not call this otherwise"); + assert((epoch & (~epoch_mask)) == 0, "epoch overflow"); + return markOop(mask_bits(value(), ~epoch_mask_in_place) | (epoch << epoch_shift)); + } + markOop incr_bias_epoch() { + return set_bias_epoch((1 + bias_epoch()) & epoch_mask); + } + // Prototype mark for initialization + static markOop biased_locking_prototype() { + return markOop( biased_lock_pattern ); + } + + // lock accessors (note that these assume lock_shift == 0) + bool is_locked() const { + return (mask_bits(value(), lock_mask_in_place) != unlocked_value); + } + bool is_unlocked() const { + return (mask_bits(value(), biased_lock_mask_in_place) == unlocked_value); + } + bool is_marked() const { + return (mask_bits(value(), lock_mask_in_place) == marked_value); + } + bool is_neutral() const { return (mask_bits(value(), biased_lock_mask_in_place) == unlocked_value); } + + // Special temporary state of the markOop while being inflated. + // Code that looks at mark outside a lock need to take this into account. + bool is_being_inflated() const { return (value() == 0); } + + // Distinguished markword value - used when inflating over + // an existing stacklock. 0 indicates the markword is "BUSY". + // Lockword mutators that use a LD...CAS idiom should always + // check for and avoid overwriting a 0 value installed by some + // other thread. (They should spin or block instead. The 0 value + // is transient and *should* be short-lived). + static markOop INFLATING() { return (markOop) 0; } // inflate-in-progress + + // Should this header be preserved during GC? + bool must_be_preserved(oop obj_containing_mark) const { + if (!UseBiasedLocking) + return (!is_unlocked() || !has_no_hash()); + return must_be_preserved_with_bias(obj_containing_mark); + } + inline bool must_be_preserved_with_bias(oop obj_containing_mark) const; + + // Should this header (including its age bits) be preserved in the + // case of a promotion failure during scavenge? + // Note that we special case this situation. We want to avoid + // calling BiasedLocking::preserve_marks()/restore_marks() (which + // decrease the number of mark words that need to be preserved + // during GC) during each scavenge. During scavenges in which there + // is no promotion failure, we actually don't need to call the above + // routines at all, since we don't mutate and re-initialize the + // marks of promoted objects using init_mark(). However, during + // scavenges which result in promotion failure, we do re-initialize + // the mark words of objects, meaning that we should have called + // these mark word preservation routines. Currently there's no good + // place in which to call them in any of the scavengers (although + // guarded by appropriate locks we could make one), but the + // observation is that promotion failures are quite rare and + // reducing the number of mark words preserved during them isn't a + // high priority. + bool must_be_preserved_for_promotion_failure(oop obj_containing_mark) const { + if (!UseBiasedLocking) + return (this != prototype()); + return must_be_preserved_with_bias_for_promotion_failure(obj_containing_mark); + } + inline bool must_be_preserved_with_bias_for_promotion_failure(oop obj_containing_mark) const; + + // Should this header be preserved during a scavenge where CMS is + // the old generation? + // (This is basically the same body as must_be_preserved_for_promotion_failure(), + // but takes the klassOop as argument instead) + bool must_be_preserved_for_cms_scavenge(klassOop klass_of_obj_containing_mark) const { + if (!UseBiasedLocking) + return (this != prototype()); + return must_be_preserved_with_bias_for_cms_scavenge(klass_of_obj_containing_mark); + } + inline bool must_be_preserved_with_bias_for_cms_scavenge(klassOop klass_of_obj_containing_mark) const; + + // WARNING: The following routines are used EXCLUSIVELY by + // synchronization functions. They are not really gc safe. + // They must get updated if markOop layout get changed. + markOop set_unlocked() const { + return markOop(value() | unlocked_value); + } + bool has_locker() const { + return ((value() & lock_mask_in_place) == locked_value); + } + BasicLock* locker() const { + assert(has_locker(), "check"); + return (BasicLock*) value(); + } + bool has_monitor() const { + return ((value() & monitor_value) != 0); + } + ObjectMonitor* monitor() const { + assert(has_monitor(), "check"); + // Use xor instead of &~ to provide one extra tag-bit check. + return (ObjectMonitor*) (value() ^ monitor_value); + } + bool has_displaced_mark_helper() const { + return ((value() & unlocked_value) == 0); + } + markOop displaced_mark_helper() const { + assert(has_displaced_mark_helper(), "check"); + intptr_t ptr = (value() & ~monitor_value); + return *(markOop*)ptr; + } + void set_displaced_mark_helper(markOop m) const { + assert(has_displaced_mark_helper(), "check"); + intptr_t ptr = (value() & ~monitor_value); + *(markOop*)ptr = m; + } + markOop copy_set_hash(intptr_t hash) const { + intptr_t tmp = value() & (~hash_mask_in_place); + tmp |= ((hash & hash_mask) << hash_shift); + return (markOop)tmp; + } + // it is only used to be stored into BasicLock as the + // indicator that the lock is using heavyweight monitor + static markOop unused_mark() { + return (markOop) marked_value; + } + // the following two functions create the markOop to be + // stored into object header, it encodes monitor info + static markOop encode(BasicLock* lock) { + return (markOop) lock; + } + static markOop encode(ObjectMonitor* monitor) { + intptr_t tmp = (intptr_t) monitor; + return (markOop) (tmp | monitor_value); + } + static markOop encode(JavaThread* thread, int age, int bias_epoch) { + intptr_t tmp = (intptr_t) thread; + assert(UseBiasedLocking && ((tmp & (epoch_mask_in_place | age_mask_in_place | biased_lock_mask_in_place)) == 0), "misaligned JavaThread pointer"); + assert(age <= max_age, "age too large"); + assert(bias_epoch <= max_bias_epoch, "bias epoch too large"); + return (markOop) (tmp | (bias_epoch << epoch_shift) | (age << age_shift) | biased_lock_pattern); + } + + // used to encode pointers during GC + markOop clear_lock_bits() { return markOop(value() & ~lock_mask_in_place); } + + // age operations + markOop set_marked() { return markOop((value() & ~lock_mask_in_place) | marked_value); } + + int age() const { return mask_bits(value() >> age_shift, age_mask); } + markOop set_age(int v) const { + assert((v & ~age_mask) == 0, "shouldn't overflow age field"); + return markOop((value() & ~age_mask_in_place) | (((intptr_t)v & age_mask) << age_shift)); + } + markOop incr_age() const { return age() == max_age ? markOop(this) : set_age(age() + 1); } + + // hash operations + intptr_t hash() const { + return mask_bits(value() >> hash_shift, hash_mask); + } + + bool has_no_hash() const { + return hash() == no_hash; + } + + // Prototype mark for initialization + static markOop prototype() { + return markOop( no_hash_in_place | no_lock_in_place ); + } + + // Helper function for restoration of unmarked mark oops during GC + static inline markOop prototype_for_object(oop obj); + + // Debugging + void print_on(outputStream* st) const; + + // Prepare address of oop for placement into mark + inline static markOop encode_pointer_as_mark(void* p) { return markOop(p)->set_marked(); } + + // Recover address of oop from encoded form used in mark + inline void* decode_pointer() { if (UseBiasedLocking && has_bias_pattern()) return NULL; return clear_lock_bits(); } +}; diff --git a/hotspot/src/share/vm/oops/markOop.inline.hpp b/hotspot/src/share/vm/oops/markOop.inline.hpp new file mode 100644 index 00000000000..46774b201d9 --- /dev/null +++ b/hotspot/src/share/vm/oops/markOop.inline.hpp @@ -0,0 +1,80 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Should this header be preserved during GC? +inline bool markOopDesc::must_be_preserved_with_bias(oop obj_containing_mark) const { + assert(UseBiasedLocking, "unexpected"); + if (has_bias_pattern()) { + // Will reset bias at end of collection + // Mark words of biased and currently locked objects are preserved separately + return false; + } + markOop prototype_header = prototype_for_object(obj_containing_mark); + if (prototype_header->has_bias_pattern()) { + // Individual instance which has its bias revoked; must return + // true for correctness + return true; + } + return (!is_unlocked() || !has_no_hash()); +} + +// Should this header (including its age bits) be preserved in the +// case of a promotion failure during scavenge? +inline bool markOopDesc::must_be_preserved_with_bias_for_promotion_failure(oop obj_containing_mark) const { + assert(UseBiasedLocking, "unexpected"); + // We don't explicitly save off the mark words of biased and + // currently-locked objects during scavenges, so if during a + // promotion failure we encounter either a biased mark word or a + // klass which still has a biasable prototype header, we have to + // preserve the mark word. This results in oversaving, but promotion + // failures are rare, and this avoids adding more complex logic to + // the scavengers to call new variants of + // BiasedLocking::preserve_marks() / restore_marks() in the middle + // of a scavenge when a promotion failure has first been detected. + if (has_bias_pattern() || + prototype_for_object(obj_containing_mark)->has_bias_pattern()) { + return true; + } + return (this != prototype()); +} + +// Should this header (including its age bits) be preserved in the +// case of a scavenge in which CMS is the old generation? +inline bool markOopDesc::must_be_preserved_with_bias_for_cms_scavenge(klassOop klass_of_obj_containing_mark) const { + assert(UseBiasedLocking, "unexpected"); + // CMS scavenges preserve mark words in similar fashion to promotion failures; see above + if (has_bias_pattern() || + klass_of_obj_containing_mark->klass_part()->prototype_header()->has_bias_pattern()) { + return true; + } + return (this != prototype()); +} + +inline markOop markOopDesc::prototype_for_object(oop obj) { +#ifdef ASSERT + markOop prototype_header = obj->klass()->klass_part()->prototype_header(); + assert(prototype_header == prototype() || prototype_header->has_bias_pattern(), "corrupt prototype header"); +#endif + return obj->klass()->klass_part()->prototype_header(); +} diff --git a/hotspot/src/share/vm/oops/methodDataKlass.cpp b/hotspot/src/share/vm/oops/methodDataKlass.cpp new file mode 100644 index 00000000000..f3ee24122b0 --- /dev/null +++ b/hotspot/src/share/vm/oops/methodDataKlass.cpp @@ -0,0 +1,238 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_methodDataKlass.cpp.incl" + +klassOop methodDataKlass::create_klass(TRAPS) { + methodDataKlass o; + KlassHandle h_this_klass(THREAD, Universe::klassKlassObj()); + KlassHandle k = base_create_klass(h_this_klass, header_size(), + o.vtbl_value(), CHECK_NULL); + // Make sure size calculation is right + assert(k()->size() == align_object_size(header_size()), + "wrong size for object"); + return k(); +} + + +int methodDataKlass::oop_size(oop obj) const { + assert(obj->is_methodData(), "must be method data oop"); + return methodDataOop(obj)->object_size(); +} + + +bool methodDataKlass::oop_is_parsable(oop obj) const { + assert(obj->is_methodData(), "must be method data oop"); + return methodDataOop(obj)->object_is_parsable(); +} + + +methodDataOop methodDataKlass::allocate(methodHandle method, TRAPS) { + int size = methodDataOopDesc::compute_allocation_size_in_words(method); + KlassHandle h_k(THREAD, as_klassOop()); + methodDataOop mdo = + (methodDataOop)CollectedHeap::permanent_obj_allocate(h_k, size, CHECK_NULL); + assert(!mdo->is_parsable(), "not expecting parsability yet."); + No_Safepoint_Verifier no_safepoint; // init function atomic wrt GC + mdo->initialize(method); + + assert(mdo->is_parsable(), "should be parsable here."); + assert(size == mdo->object_size(), "wrong size for methodDataOop"); + return mdo; +} + + +void methodDataKlass::oop_follow_contents(oop obj) { + assert (obj->is_methodData(), "object must be method data"); + methodDataOop m = methodDataOop(obj); + + obj->follow_header(); + MarkSweep::mark_and_push(m->adr_method()); + ResourceMark rm; + for (ProfileData* data = m->first_data(); + m->is_valid(data); + data = m->next_data(data)) { + data->follow_contents(); + } +} + +#ifndef SERIALGC +void methodDataKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert (obj->is_methodData(), "object must be method data"); + methodDataOop m = methodDataOop(obj); + + obj->follow_header(cm); + PSParallelCompact::mark_and_push(cm, m->adr_method()); + ResourceMark rm; + for (ProfileData* data = m->first_data(); + m->is_valid(data); + data = m->next_data(data)) { + data->follow_contents(cm); + } +} +#endif // SERIALGC + +int methodDataKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert (obj->is_methodData(), "object must be method data"); + methodDataOop m = methodDataOop(obj); + // Get size before changing pointers + // Don't call size() or oop_size() since that is a virtual call. + int size = m->object_size(); + + obj->oop_iterate_header(blk); + blk->do_oop(m->adr_method()); + ResourceMark rm; + for (ProfileData* data = m->first_data(); + m->is_valid(data); + data = m->next_data(data)) { + data->oop_iterate(blk); + } + return size; +} + + +int methodDataKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + assert (obj->is_methodData(), "object must be method data"); + methodDataOop m = methodDataOop(obj); + // Get size before changing pointers + // Don't call size() or oop_size() since that is a virtual call. + int size = m->object_size(); + + obj->oop_iterate_header(blk, mr); + oop* adr = m->adr_method(); + if (mr.contains(adr)) { + blk->do_oop(m->adr_method()); + } + ResourceMark rm; + for (ProfileData* data = m->first_data(); + m->is_valid(data); + data = m->next_data(data)) { + data->oop_iterate_m(blk, mr); + } + return size; +} + +int methodDataKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_methodData(), "should be method data"); + methodDataOop m = methodDataOop(obj); + // Get size before changing pointers + // Don't call size() or oop_size() since that is a virtual call. + int size = m->object_size(); + + obj->adjust_header(); + MarkSweep::adjust_pointer(m->adr_method()); + ResourceMark rm; + ProfileData* data; + for (data = m->first_data(); m->is_valid(data); data = m->next_data(data)) { + data->adjust_pointers(); + } + return size; +} + + +#ifndef SERIALGC +void methodDataKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert (obj->is_methodData(), "object must be method data"); + methodDataOop m = methodDataOop(obj); + // This should never point into the young gen. + assert(!PSScavenge::should_scavenge(oop(*m->adr_method())), "Sanity"); +} + +void methodDataKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert (obj->is_methodData(), "object must be method data"); + methodDataOop m = methodDataOop(obj); + // This should never point into the young gen. + assert(!PSScavenge::should_scavenge(oop(*m->adr_method())), "Sanity"); +} + +int methodDataKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert(obj->is_methodData(), "should be method data"); + methodDataOop m = methodDataOop(obj); + + PSParallelCompact::adjust_pointer(m->adr_method()); + + ResourceMark rm; + ProfileData* data; + for (data = m->first_data(); m->is_valid(data); data = m->next_data(data)) { + data->update_pointers(); + } + return m->object_size(); +} + +int +methodDataKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, HeapWord* end_addr) { + assert(obj->is_methodData(), "should be method data"); + + oop* p; + methodDataOop m = methodDataOop(obj); + + p = m->adr_method(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + + ResourceMark rm; + ProfileData* data; + for (data = m->first_data(); m->is_valid(data); data = m->next_data(data)) { + data->update_pointers(beg_addr, end_addr); + } + return m->object_size(); +} +#endif // SERIALGC + +#ifndef PRODUCT + +// Printing +void methodDataKlass::oop_print_on(oop obj, outputStream* st) { + assert(obj->is_methodData(), "should be method data"); + methodDataOop m = methodDataOop(obj); + st->print("method data for "); + m->method()->print_value_on(st); + st->cr(); + m->print_data_on(st); +} + +void methodDataKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_methodData(), "should be method data"); + methodDataOop m = methodDataOop(obj); + st->print("method data for "); + m->method()->print_value_on(st); +} + +#endif // !PRODUCT + +const char* methodDataKlass::internal_name() const { + return "{method data}"; +} + + +// Verification +void methodDataKlass::oop_verify_on(oop obj, outputStream* st) { + Klass::oop_verify_on(obj, st); + guarantee(obj->is_methodData(), "object must be method data"); + methodDataOop m = methodDataOop(obj); + guarantee(m->is_perm(), "should be in permspace"); + m->verify_data_on(st); +} diff --git a/hotspot/src/share/vm/oops/methodDataKlass.hpp b/hotspot/src/share/vm/oops/methodDataKlass.hpp new file mode 100644 index 00000000000..0b78000d46f --- /dev/null +++ b/hotspot/src/share/vm/oops/methodDataKlass.hpp @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// a methodDataKlass is the klass of a methodDataOop + +class methodDataKlass : public Klass { + friend class VMStructs; + private: + juint _alloc_size; // allocation profiling support + public: + // Testing + bool oop_is_methodData() const { return true; } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(methodDataKlass); + methodDataOop allocate(methodHandle method, TRAPS); + static klassOop create_klass(TRAPS); + + // Sizing + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + + // Casting from klassOop + static methodDataKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_methodData(), "cast to methodDataKlass"); + return (methodDataKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { + return oopDesc::header_size() + sizeof(methodDataKlass)/wordSize; + } + int object_size() const { + return align_object_size(header_size()); + } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + bool oop_is_parsable(oop obj) const; + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Allocation profiling support + juint alloc_size() const { return _alloc_size; } + void set_alloc_size(juint n) { _alloc_size = n; } + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on (oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); +#endif // !PRODUCT + + public: + // Verify operations + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); +}; diff --git a/hotspot/src/share/vm/oops/methodDataOop.cpp b/hotspot/src/share/vm/oops/methodDataOop.cpp new file mode 100644 index 00000000000..f149335940a --- /dev/null +++ b/hotspot/src/share/vm/oops/methodDataOop.cpp @@ -0,0 +1,816 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_methodDataOop.cpp.incl" + +// ================================================================== +// DataLayout +// +// Overlay for generic profiling data. + +// Some types of data layouts need a length field. +bool DataLayout::needs_array_len(u1 tag) { + return (tag == multi_branch_data_tag); +} + +// Perform generic initialization of the data. More specific +// initialization occurs in overrides of ProfileData::post_initialize. +void DataLayout::initialize(u1 tag, u2 bci, int cell_count) { + _header._bits = (intptr_t)0; + _header._struct._tag = tag; + _header._struct._bci = bci; + for (int i = 0; i < cell_count; i++) { + set_cell_at(i, (intptr_t)0); + } + if (needs_array_len(tag)) { + set_cell_at(ArrayData::array_len_off_set, cell_count - 1); // -1 for header. + } +} + +// ================================================================== +// ProfileData +// +// A ProfileData object is created to refer to a section of profiling +// data in a structured way. + +// Constructor for invalid ProfileData. +ProfileData::ProfileData() { + _data = NULL; +} + +#ifndef PRODUCT +void ProfileData::print_shared(outputStream* st, const char* name) { + st->print("bci: %d", bci()); + st->fill_to(tab_width_one); + st->print("%s", name); + tab(st); + int trap = trap_state(); + if (trap != 0) { + char buf[100]; + st->print("trap(%s) ", Deoptimization::format_trap_state(buf, sizeof(buf), trap)); + } + int flags = data()->flags(); + if (flags != 0) + st->print("flags(%d) ", flags); +} + +void ProfileData::tab(outputStream* st) { + st->fill_to(tab_width_two); +} +#endif // !PRODUCT + +// ================================================================== +// BitData +// +// A BitData corresponds to a one-bit flag. This is used to indicate +// whether a checkcast bytecode has seen a null value. + + +#ifndef PRODUCT +void BitData::print_data_on(outputStream* st) { + print_shared(st, "BitData"); +} +#endif // !PRODUCT + +// ================================================================== +// CounterData +// +// A CounterData corresponds to a simple counter. + +#ifndef PRODUCT +void CounterData::print_data_on(outputStream* st) { + print_shared(st, "CounterData"); + st->print_cr("count(%u)", count()); +} +#endif // !PRODUCT + +// ================================================================== +// JumpData +// +// A JumpData is used to access profiling information for a direct +// branch. It is a counter, used for counting the number of branches, +// plus a data displacement, used for realigning the data pointer to +// the corresponding target bci. + +void JumpData::post_initialize(BytecodeStream* stream, methodDataOop mdo) { + assert(stream->bci() == bci(), "wrong pos"); + int target; + Bytecodes::Code c = stream->code(); + if (c == Bytecodes::_goto_w || c == Bytecodes::_jsr_w) { + target = stream->dest_w(); + } else { + target = stream->dest(); + } + int my_di = mdo->dp_to_di(dp()); + int target_di = mdo->bci_to_di(target); + int offset = target_di - my_di; + set_displacement(offset); +} + +#ifndef PRODUCT +void JumpData::print_data_on(outputStream* st) { + print_shared(st, "JumpData"); + st->print_cr("taken(%u) displacement(%d)", taken(), displacement()); +} +#endif // !PRODUCT + +// ================================================================== +// ReceiverTypeData +// +// A ReceiverTypeData is used to access profiling information about a +// dynamic type check. It consists of a counter which counts the total times +// that the check is reached, and a series of (klassOop, count) pairs +// which are used to store a type profile for the receiver of the check. + +void ReceiverTypeData::follow_contents() { + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + MarkSweep::mark_and_push(adr_receiver(row)); + } + } +} + +#ifndef SERIALGC +void ReceiverTypeData::follow_contents(ParCompactionManager* cm) { + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + PSParallelCompact::mark_and_push(cm, adr_receiver(row)); + } + } +} +#endif // SERIALGC + +void ReceiverTypeData::oop_iterate(OopClosure* blk) { + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + blk->do_oop(adr_receiver(row)); + } + } +} + +void ReceiverTypeData::oop_iterate_m(OopClosure* blk, MemRegion mr) { + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + oop* adr = adr_receiver(row); + if (mr.contains(adr)) { + blk->do_oop(adr); + } + } + } +} + +void ReceiverTypeData::adjust_pointers() { + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + MarkSweep::adjust_pointer(adr_receiver(row)); + } + } +} + +#ifndef SERIALGC +void ReceiverTypeData::update_pointers() { + for (uint row = 0; row < row_limit(); row++) { + if (receiver_unchecked(row) != NULL) { + PSParallelCompact::adjust_pointer(adr_receiver(row)); + } + } +} + +void ReceiverTypeData::update_pointers(HeapWord* beg_addr, HeapWord* end_addr) { + // The loop bounds could be computed based on beg_addr/end_addr and the + // boundary test hoisted outside the loop (see klassVTable for an example); + // however, row_limit() is small enough (2) to make that less efficient. + for (uint row = 0; row < row_limit(); row++) { + if (receiver_unchecked(row) != NULL) { + PSParallelCompact::adjust_pointer(adr_receiver(row), beg_addr, end_addr); + } + } +} +#endif // SERIALGC + +#ifndef PRODUCT +void ReceiverTypeData::print_receiver_data_on(outputStream* st) { + uint row; + int entries = 0; + for (row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) entries++; + } + st->print_cr("count(%u) entries(%u)", count(), entries); + for (row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + tab(st); + receiver(row)->print_value_on(st); + st->print_cr("(%u)", receiver_count(row)); + } + } +} +void ReceiverTypeData::print_data_on(outputStream* st) { + print_shared(st, "ReceiverTypeData"); + print_receiver_data_on(st); +} +void VirtualCallData::print_data_on(outputStream* st) { + print_shared(st, "VirtualCallData"); + print_receiver_data_on(st); +} +#endif // !PRODUCT + +// ================================================================== +// RetData +// +// A RetData is used to access profiling information for a ret bytecode. +// It is composed of a count of the number of times that the ret has +// been executed, followed by a series of triples of the form +// (bci, count, di) which count the number of times that some bci was the +// target of the ret and cache a corresponding displacement. + +void RetData::post_initialize(BytecodeStream* stream, methodDataOop mdo) { + for (uint row = 0; row < row_limit(); row++) { + set_bci_displacement(row, -1); + set_bci(row, no_bci); + } + // release so other threads see a consistent state. bci is used as + // a valid flag for bci_displacement. + OrderAccess::release(); +} + +// This routine needs to atomically update the RetData structure, so the +// caller needs to hold the RetData_lock before it gets here. Since taking +// the lock can block (and allow GC) and since RetData is a ProfileData is a +// wrapper around a derived oop, taking the lock in _this_ method will +// basically cause the 'this' pointer's _data field to contain junk after the +// lock. We require the caller to take the lock before making the ProfileData +// structure. Currently the only caller is InterpreterRuntime::update_mdp_for_ret +address RetData::fixup_ret(int return_bci, methodDataHandle h_mdo) { + // First find the mdp which corresponds to the return bci. + address mdp = h_mdo->bci_to_dp(return_bci); + + // Now check to see if any of the cache slots are open. + for (uint row = 0; row < row_limit(); row++) { + if (bci(row) == no_bci) { + set_bci_displacement(row, mdp - dp()); + set_bci_count(row, DataLayout::counter_increment); + // Barrier to ensure displacement is written before the bci; allows + // the interpreter to read displacement without fear of race condition. + release_set_bci(row, return_bci); + break; + } + } + return mdp; +} + + +#ifndef PRODUCT +void RetData::print_data_on(outputStream* st) { + print_shared(st, "RetData"); + uint row; + int entries = 0; + for (row = 0; row < row_limit(); row++) { + if (bci(row) != no_bci) entries++; + } + st->print_cr("count(%u) entries(%u)", count(), entries); + for (row = 0; row < row_limit(); row++) { + if (bci(row) != no_bci) { + tab(st); + st->print_cr("bci(%d: count(%u) displacement(%d))", + bci(row), bci_count(row), bci_displacement(row)); + } + } +} +#endif // !PRODUCT + +// ================================================================== +// BranchData +// +// A BranchData is used to access profiling data for a two-way branch. +// It consists of taken and not_taken counts as well as a data displacement +// for the taken case. + +void BranchData::post_initialize(BytecodeStream* stream, methodDataOop mdo) { + assert(stream->bci() == bci(), "wrong pos"); + int target = stream->dest(); + int my_di = mdo->dp_to_di(dp()); + int target_di = mdo->bci_to_di(target); + int offset = target_di - my_di; + set_displacement(offset); +} + +#ifndef PRODUCT +void BranchData::print_data_on(outputStream* st) { + print_shared(st, "BranchData"); + st->print_cr("taken(%u) displacement(%d)", + taken(), displacement()); + tab(st); + st->print_cr("not taken(%u)", not_taken()); +} +#endif + +// ================================================================== +// MultiBranchData +// +// A MultiBranchData is used to access profiling information for +// a multi-way branch (*switch bytecodes). It consists of a series +// of (count, displacement) pairs, which count the number of times each +// case was taken and specify the data displacment for each branch target. + +int MultiBranchData::compute_cell_count(BytecodeStream* stream) { + int cell_count = 0; + if (stream->code() == Bytecodes::_tableswitch) { + Bytecode_tableswitch* sw = Bytecode_tableswitch_at(stream->bcp()); + cell_count = 1 + per_case_cell_count * (1 + sw->length()); // 1 for default + } else { + Bytecode_lookupswitch* sw = Bytecode_lookupswitch_at(stream->bcp()); + cell_count = 1 + per_case_cell_count * (sw->number_of_pairs() + 1); // 1 for default + } + return cell_count; +} + +void MultiBranchData::post_initialize(BytecodeStream* stream, + methodDataOop mdo) { + assert(stream->bci() == bci(), "wrong pos"); + int target; + int my_di; + int target_di; + int offset; + if (stream->code() == Bytecodes::_tableswitch) { + Bytecode_tableswitch* sw = Bytecode_tableswitch_at(stream->bcp()); + int len = sw->length(); + assert(array_len() == per_case_cell_count * (len + 1), "wrong len"); + for (int count = 0; count < len; count++) { + target = sw->dest_offset_at(count) + bci(); + my_di = mdo->dp_to_di(dp()); + target_di = mdo->bci_to_di(target); + offset = target_di - my_di; + set_displacement_at(count, offset); + } + target = sw->default_offset() + bci(); + my_di = mdo->dp_to_di(dp()); + target_di = mdo->bci_to_di(target); + offset = target_di - my_di; + set_default_displacement(offset); + + } else { + Bytecode_lookupswitch* sw = Bytecode_lookupswitch_at(stream->bcp()); + int npairs = sw->number_of_pairs(); + assert(array_len() == per_case_cell_count * (npairs + 1), "wrong len"); + for (int count = 0; count < npairs; count++) { + LookupswitchPair *pair = sw->pair_at(count); + target = pair->offset() + bci(); + my_di = mdo->dp_to_di(dp()); + target_di = mdo->bci_to_di(target); + offset = target_di - my_di; + set_displacement_at(count, offset); + } + target = sw->default_offset() + bci(); + my_di = mdo->dp_to_di(dp()); + target_di = mdo->bci_to_di(target); + offset = target_di - my_di; + set_default_displacement(offset); + } +} + +#ifndef PRODUCT +void MultiBranchData::print_data_on(outputStream* st) { + print_shared(st, "MultiBranchData"); + st->print_cr("default_count(%u) displacement(%d)", + default_count(), default_displacement()); + int cases = number_of_cases(); + for (int i = 0; i < cases; i++) { + tab(st); + st->print_cr("count(%u) displacement(%d)", + count_at(i), displacement_at(i)); + } +} +#endif + +// ================================================================== +// methodDataOop +// +// A methodDataOop holds information which has been collected about +// a method. + +int methodDataOopDesc::bytecode_cell_count(Bytecodes::Code code) { + switch (code) { + case Bytecodes::_checkcast: + case Bytecodes::_instanceof: + case Bytecodes::_aastore: + if (TypeProfileCasts) { + return ReceiverTypeData::static_cell_count(); + } else { + return BitData::static_cell_count(); + } + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + return CounterData::static_cell_count(); + case Bytecodes::_goto: + case Bytecodes::_goto_w: + case Bytecodes::_jsr: + case Bytecodes::_jsr_w: + return JumpData::static_cell_count(); + case Bytecodes::_invokevirtual: + case Bytecodes::_invokeinterface: + return VirtualCallData::static_cell_count(); + case Bytecodes::_ret: + return RetData::static_cell_count(); + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + return BranchData::static_cell_count(); + case Bytecodes::_lookupswitch: + case Bytecodes::_tableswitch: + return variable_cell_count; + } + return no_profile_data; +} + +// Compute the size of the profiling information corresponding to +// the current bytecode. +int methodDataOopDesc::compute_data_size(BytecodeStream* stream) { + int cell_count = bytecode_cell_count(stream->code()); + if (cell_count == no_profile_data) { + return 0; + } + if (cell_count == variable_cell_count) { + cell_count = MultiBranchData::compute_cell_count(stream); + } + // Note: cell_count might be zero, meaning that there is just + // a DataLayout header, with no extra cells. + assert(cell_count >= 0, "sanity"); + return DataLayout::compute_size_in_bytes(cell_count); +} + +int methodDataOopDesc::compute_extra_data_count(int data_size, int empty_bc_count) { + if (ProfileTraps) { + // Assume that up to 3% of BCIs with no MDP will need to allocate one. + int extra_data_count = (uint)(empty_bc_count * 3) / 128 + 1; + // If the method is large, let the extra BCIs grow numerous (to ~1%). + int one_percent_of_data + = (uint)data_size / (DataLayout::header_size_in_bytes()*128); + if (extra_data_count < one_percent_of_data) + extra_data_count = one_percent_of_data; + if (extra_data_count > empty_bc_count) + extra_data_count = empty_bc_count; // no need for more + return extra_data_count; + } else { + return 0; + } +} + +// Compute the size of the methodDataOop necessary to store +// profiling information about a given method. Size is in bytes. +int methodDataOopDesc::compute_allocation_size_in_bytes(methodHandle method) { + int data_size = 0; + BytecodeStream stream(method); + Bytecodes::Code c; + int empty_bc_count = 0; // number of bytecodes lacking data + while ((c = stream.next()) >= 0) { + int size_in_bytes = compute_data_size(&stream); + data_size += size_in_bytes; + if (size_in_bytes == 0) empty_bc_count += 1; + } + int object_size = in_bytes(data_offset()) + data_size; + + // Add some extra DataLayout cells (at least one) to track stray traps. + int extra_data_count = compute_extra_data_count(data_size, empty_bc_count); + object_size += extra_data_count * DataLayout::compute_size_in_bytes(0); + + return object_size; +} + +// Compute the size of the methodDataOop necessary to store +// profiling information about a given method. Size is in words +int methodDataOopDesc::compute_allocation_size_in_words(methodHandle method) { + int byte_size = compute_allocation_size_in_bytes(method); + int word_size = align_size_up(byte_size, BytesPerWord) / BytesPerWord; + return align_object_size(word_size); +} + +// Initialize an individual data segment. Returns the size of +// the segment in bytes. +int methodDataOopDesc::initialize_data(BytecodeStream* stream, + int data_index) { + int cell_count = -1; + int tag = DataLayout::no_tag; + DataLayout* data_layout = data_layout_at(data_index); + Bytecodes::Code c = stream->code(); + switch (c) { + case Bytecodes::_checkcast: + case Bytecodes::_instanceof: + case Bytecodes::_aastore: + if (TypeProfileCasts) { + cell_count = ReceiverTypeData::static_cell_count(); + tag = DataLayout::receiver_type_data_tag; + } else { + cell_count = BitData::static_cell_count(); + tag = DataLayout::bit_data_tag; + } + break; + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + cell_count = CounterData::static_cell_count(); + tag = DataLayout::counter_data_tag; + break; + case Bytecodes::_goto: + case Bytecodes::_goto_w: + case Bytecodes::_jsr: + case Bytecodes::_jsr_w: + cell_count = JumpData::static_cell_count(); + tag = DataLayout::jump_data_tag; + break; + case Bytecodes::_invokevirtual: + case Bytecodes::_invokeinterface: + cell_count = VirtualCallData::static_cell_count(); + tag = DataLayout::virtual_call_data_tag; + break; + case Bytecodes::_ret: + cell_count = RetData::static_cell_count(); + tag = DataLayout::ret_data_tag; + break; + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + cell_count = BranchData::static_cell_count(); + tag = DataLayout::branch_data_tag; + break; + case Bytecodes::_lookupswitch: + case Bytecodes::_tableswitch: + cell_count = MultiBranchData::compute_cell_count(stream); + tag = DataLayout::multi_branch_data_tag; + break; + } + assert(tag == DataLayout::multi_branch_data_tag || + cell_count == bytecode_cell_count(c), "cell counts must agree"); + if (cell_count >= 0) { + assert(tag != DataLayout::no_tag, "bad tag"); + assert(bytecode_has_profile(c), "agree w/ BHP"); + data_layout->initialize(tag, stream->bci(), cell_count); + return DataLayout::compute_size_in_bytes(cell_count); + } else { + assert(!bytecode_has_profile(c), "agree w/ !BHP"); + return 0; + } +} + +// Get the data at an arbitrary (sort of) data index. +ProfileData* methodDataOopDesc::data_at(int data_index) { + if (out_of_bounds(data_index)) { + return NULL; + } + DataLayout* data_layout = data_layout_at(data_index); + + switch (data_layout->tag()) { + case DataLayout::no_tag: + default: + ShouldNotReachHere(); + return NULL; + case DataLayout::bit_data_tag: + return new BitData(data_layout); + case DataLayout::counter_data_tag: + return new CounterData(data_layout); + case DataLayout::jump_data_tag: + return new JumpData(data_layout); + case DataLayout::receiver_type_data_tag: + return new ReceiverTypeData(data_layout); + case DataLayout::virtual_call_data_tag: + return new VirtualCallData(data_layout); + case DataLayout::ret_data_tag: + return new RetData(data_layout); + case DataLayout::branch_data_tag: + return new BranchData(data_layout); + case DataLayout::multi_branch_data_tag: + return new MultiBranchData(data_layout); + }; +} + +// Iteration over data. +ProfileData* methodDataOopDesc::next_data(ProfileData* current) { + int current_index = dp_to_di(current->dp()); + int next_index = current_index + current->size_in_bytes(); + ProfileData* next = data_at(next_index); + return next; +} + +// Give each of the data entries a chance to perform specific +// data initialization. +void methodDataOopDesc::post_initialize(BytecodeStream* stream) { + ResourceMark rm; + ProfileData* data; + for (data = first_data(); is_valid(data); data = next_data(data)) { + stream->set_start(data->bci()); + stream->next(); + data->post_initialize(stream, this); + } +} + +// Initialize the methodDataOop corresponding to a given method. +void methodDataOopDesc::initialize(methodHandle method) { + ResourceMark rm; + + // Set the method back-pointer. + _method = method(); + set_creation_mileage(mileage_of(method())); + + // Initialize flags and trap history. + _nof_decompiles = 0; + _nof_overflow_recompiles = 0; + _nof_overflow_traps = 0; + assert(sizeof(_trap_hist) % sizeof(HeapWord) == 0, "align"); + Copy::zero_to_words((HeapWord*) &_trap_hist, + sizeof(_trap_hist) / sizeof(HeapWord)); + + // Go through the bytecodes and allocate and initialize the + // corresponding data cells. + int data_size = 0; + int empty_bc_count = 0; // number of bytecodes lacking data + BytecodeStream stream(method); + Bytecodes::Code c; + while ((c = stream.next()) >= 0) { + int size_in_bytes = initialize_data(&stream, data_size); + data_size += size_in_bytes; + if (size_in_bytes == 0) empty_bc_count += 1; + } + _data_size = data_size; + int object_size = in_bytes(data_offset()) + data_size; + + // Add some extra DataLayout cells (at least one) to track stray traps. + int extra_data_count = compute_extra_data_count(data_size, empty_bc_count); + object_size += extra_data_count * DataLayout::compute_size_in_bytes(0); + + // Set an initial hint. Don't use set_hint_di() because + // first_di() may be out of bounds if data_size is 0. + // In that situation, _hint_di is never used, but at + // least well-defined. + _hint_di = first_di(); + + post_initialize(&stream); + + set_object_is_parsable(object_size); +} + +// Get a measure of how much mileage the method has on it. +int methodDataOopDesc::mileage_of(methodOop method) { + int mileage = 0; + int iic = method->interpreter_invocation_count(); + if (mileage < iic) mileage = iic; + + InvocationCounter* ic = method->invocation_counter(); + InvocationCounter* bc = method->backedge_counter(); + + int icval = ic->count(); + if (ic->carry()) icval += CompileThreshold; + if (mileage < icval) mileage = icval; + int bcval = bc->count(); + if (bc->carry()) bcval += CompileThreshold; + if (mileage < bcval) mileage = bcval; + return mileage; +} + +bool methodDataOopDesc::is_mature() const { + uint current = mileage_of(_method); + uint initial = creation_mileage(); + if (current < initial) + return true; // some sort of overflow + uint target; + if (ProfileMaturityPercentage <= 0) + target = (uint) -ProfileMaturityPercentage; // absolute value + else + target = (uint)( (ProfileMaturityPercentage * CompileThreshold) / 100 ); + return (current >= initial + target); +} + +// Translate a bci to its corresponding data index (di). +address methodDataOopDesc::bci_to_dp(int bci) { + ResourceMark rm; + ProfileData* data = data_before(bci); + ProfileData* prev = NULL; + for ( ; is_valid(data); data = next_data(data)) { + if (data->bci() >= bci) { + if (data->bci() == bci) set_hint_di(dp_to_di(data->dp())); + else if (prev != NULL) set_hint_di(dp_to_di(prev->dp())); + return data->dp(); + } + prev = data; + } + return (address)limit_data_position(); +} + +// Translate a bci to its corresponding data, or NULL. +ProfileData* methodDataOopDesc::bci_to_data(int bci) { + ProfileData* data = data_before(bci); + for ( ; is_valid(data); data = next_data(data)) { + if (data->bci() == bci) { + set_hint_di(dp_to_di(data->dp())); + return data; + } else if (data->bci() > bci) { + break; + } + } + return bci_to_extra_data(bci, false); +} + +// Translate a bci to its corresponding extra data, or NULL. +ProfileData* methodDataOopDesc::bci_to_extra_data(int bci, bool create_if_missing) { + DataLayout* dp = extra_data_base(); + DataLayout* end = extra_data_limit(); + DataLayout* avail = NULL; + for (; dp < end; dp = next_extra(dp)) { + // No need for "OrderAccess::load_acquire" ops, + // since the data structure is monotonic. + if (dp->tag() == DataLayout::no_tag) break; + if (dp->bci() == bci) { + assert(dp->tag() == DataLayout::bit_data_tag, "sane"); + return new BitData(dp); + } + } + if (create_if_missing && dp < end) { + // Allocate this one. There is no mutual exclusion, + // so two threads could allocate different BCIs to the + // same data layout. This means these extra data + // records, like most other MDO contents, must not be + // trusted too much. + DataLayout temp; + temp.initialize(DataLayout::bit_data_tag, bci, 0); + dp->release_set_header(temp.header()); + assert(dp->tag() == DataLayout::bit_data_tag, "sane"); + //NO: assert(dp->bci() == bci, "no concurrent allocation"); + return new BitData(dp); + } + return NULL; +} + +#ifndef PRODUCT +void methodDataOopDesc::print_data_on(outputStream* st) { + ResourceMark rm; + ProfileData* data = first_data(); + for ( ; is_valid(data); data = next_data(data)) { + st->print("%d", dp_to_di(data->dp())); + st->fill_to(6); + data->print_data_on(st); + } + DataLayout* dp = extra_data_base(); + DataLayout* end = extra_data_limit(); + for (; dp < end; dp = next_extra(dp)) { + // No need for "OrderAccess::load_acquire" ops, + // since the data structure is monotonic. + if (dp->tag() == DataLayout::no_tag) break; + if (dp == extra_data_base()) + st->print_cr("--- Extra data:"); + data = new BitData(dp); + st->print("%d", dp_to_di(data->dp())); + st->fill_to(6); + data->print_data_on(st); + } +} +#endif + +void methodDataOopDesc::verify_data_on(outputStream* st) { + NEEDS_CLEANUP; + // not yet implemented. +} diff --git a/hotspot/src/share/vm/oops/methodDataOop.hpp b/hotspot/src/share/vm/oops/methodDataOop.hpp new file mode 100644 index 00000000000..2896a1410aa --- /dev/null +++ b/hotspot/src/share/vm/oops/methodDataOop.hpp @@ -0,0 +1,1341 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class BytecodeStream; + +// The MethodData object collects counts and other profile information +// during zeroth-tier (interpretive) and first-tier execution. +// The profile is used later by compilation heuristics. Some heuristics +// enable use of aggressive (or "heroic") optimizations. An aggressive +// optimization often has a down-side, a corner case that it handles +// poorly, but which is thought to be rare. The profile provides +// evidence of this rarity for a given method or even BCI. It allows +// the compiler to back out of the optimization at places where it +// has historically been a poor choice. Other heuristics try to use +// specific information gathered about types observed at a given site. +// +// All data in the profile is approximate. It is expected to be accurate +// on the whole, but the system expects occasional inaccuraces, due to +// counter overflow, multiprocessor races during data collection, space +// limitations, missing MDO blocks, etc. Bad or missing data will degrade +// optimization quality but will not affect correctness. Also, each MDO +// is marked with its birth-date ("creation_mileage") which can be used +// to assess the quality ("maturity") of its data. +// +// Short (<32-bit) counters are designed to overflow to a known "saturated" +// state. Also, certain recorded per-BCI events are given one-bit counters +// which overflow to a saturated state which applied to all counters at +// that BCI. In other words, there is a small lattice which approximates +// the ideal of an infinite-precision counter for each event at each BCI, +// and the lattice quickly "bottoms out" in a state where all counters +// are taken to be indefinitely large. +// +// The reader will find many data races in profile gathering code, starting +// with invocation counter incrementation. None of these races harm correct +// execution of the compiled code. + +// DataLayout +// +// Overlay for generic profiling data. +class DataLayout VALUE_OBJ_CLASS_SPEC { +private: + // Every data layout begins with a header. This header + // contains a tag, which is used to indicate the size/layout + // of the data, 4 bits of flags, which can be used in any way, + // 4 bits of trap history (none/one reason/many reasons), + // and a bci, which is used to tie this piece of data to a + // specific bci in the bytecodes. + union { + intptr_t _bits; + struct { + u1 _tag; + u1 _flags; + u2 _bci; + } _struct; + } _header; + + // The data layout has an arbitrary number of cells, each sized + // to accomodate a pointer or an integer. + intptr_t _cells[1]; + + // Some types of data layouts need a length field. + static bool needs_array_len(u1 tag); + +public: + enum { + counter_increment = 1 + }; + + enum { + cell_size = sizeof(intptr_t) + }; + + // Tag values + enum { + no_tag, + bit_data_tag, + counter_data_tag, + jump_data_tag, + receiver_type_data_tag, + virtual_call_data_tag, + ret_data_tag, + branch_data_tag, + multi_branch_data_tag + }; + + enum { + // The _struct._flags word is formatted as [trap_state:4 | flags:4]. + // The trap state breaks down further as [recompile:1 | reason:3]. + // This further breakdown is defined in deoptimization.cpp. + // See Deoptimization::trap_state_reason for an assert that + // trap_bits is big enough to hold reasons < Reason_RECORDED_LIMIT. + // + // The trap_state is collected only if ProfileTraps is true. + trap_bits = 1+3, // 3: enough to distinguish [0..Reason_RECORDED_LIMIT]. + trap_shift = BitsPerByte - trap_bits, + trap_mask = right_n_bits(trap_bits), + trap_mask_in_place = (trap_mask << trap_shift), + flag_limit = trap_shift, + flag_mask = right_n_bits(flag_limit), + first_flag = 0 + }; + + // Size computation + static int header_size_in_bytes() { + return cell_size; + } + static int header_size_in_cells() { + return 1; + } + + static int compute_size_in_bytes(int cell_count) { + return header_size_in_bytes() + cell_count * cell_size; + } + + // Initialization + void initialize(u1 tag, u2 bci, int cell_count); + + // Accessors + u1 tag() { + return _header._struct._tag; + } + + // Return a few bits of trap state. Range is [0..trap_mask]. + // The state tells if traps with zero, one, or many reasons have occurred. + // It also tells whether zero or many recompilations have occurred. + // The associated trap histogram in the MDO itself tells whether + // traps are common or not. If a BCI shows that a trap X has + // occurred, and the MDO shows N occurrences of X, we make the + // simplifying assumption that all N occurrences can be blamed + // on that BCI. + int trap_state() { + return ((_header._struct._flags >> trap_shift) & trap_mask); + } + + void set_trap_state(int new_state) { + assert(ProfileTraps, "used only under +ProfileTraps"); + uint old_flags = (_header._struct._flags & flag_mask); + _header._struct._flags = (new_state << trap_shift) | old_flags; + assert(trap_state() == new_state, "sanity"); + } + + u1 flags() { + return _header._struct._flags; + } + + u2 bci() { + return _header._struct._bci; + } + + void set_header(intptr_t value) { + _header._bits = value; + } + void release_set_header(intptr_t value) { + OrderAccess::release_store_ptr(&_header._bits, value); + } + intptr_t header() { + return _header._bits; + } + void set_cell_at(int index, intptr_t value) { + _cells[index] = value; + } + void release_set_cell_at(int index, intptr_t value) { + OrderAccess::release_store_ptr(&_cells[index], value); + } + intptr_t cell_at(int index) { + return _cells[index]; + } + intptr_t* adr_cell_at(int index) { + return &_cells[index]; + } + oop* adr_oop_at(int index) { + return (oop*)&(_cells[index]); + } + + void set_flag_at(int flag_number) { + assert(flag_number < flag_limit, "oob"); + _header._struct._flags |= (0x1 << flag_number); + } + bool flag_at(int flag_number) { + assert(flag_number < flag_limit, "oob"); + return (_header._struct._flags & (0x1 << flag_number)) != 0; + } + + // Low-level support for code generation. + static ByteSize header_offset() { + return byte_offset_of(DataLayout, _header); + } + static ByteSize tag_offset() { + return byte_offset_of(DataLayout, _header._struct._tag); + } + static ByteSize flags_offset() { + return byte_offset_of(DataLayout, _header._struct._flags); + } + static ByteSize bci_offset() { + return byte_offset_of(DataLayout, _header._struct._bci); + } + static ByteSize cell_offset(int index) { + return byte_offset_of(DataLayout, _cells[index]); + } + // Return a value which, when or-ed as a byte into _flags, sets the flag. + static int flag_number_to_byte_constant(int flag_number) { + assert(0 <= flag_number && flag_number < flag_limit, "oob"); + DataLayout temp; temp.set_header(0); + temp.set_flag_at(flag_number); + return temp._header._struct._flags; + } + // Return a value which, when or-ed as a word into _header, sets the flag. + static intptr_t flag_mask_to_header_mask(int byte_constant) { + DataLayout temp; temp.set_header(0); + temp._header._struct._flags = byte_constant; + return temp._header._bits; + } +}; + + +// ProfileData class hierarchy +class ProfileData; +class BitData; +class CounterData; +class ReceiverTypeData; +class VirtualCallData; +class RetData; +class JumpData; +class BranchData; +class ArrayData; +class MultiBranchData; + + +// ProfileData +// +// A ProfileData object is created to refer to a section of profiling +// data in a structured way. +class ProfileData : public ResourceObj { +private: +#ifndef PRODUCT + enum { + tab_width_one = 16, + tab_width_two = 36 + }; +#endif // !PRODUCT + + // This is a pointer to a section of profiling data. + DataLayout* _data; + +protected: + DataLayout* data() { return _data; } + + enum { + cell_size = DataLayout::cell_size + }; + +public: + // How many cells are in this? + virtual int cell_count() { + ShouldNotReachHere(); + return -1; + } + + // Return the size of this data. + int size_in_bytes() { + return DataLayout::compute_size_in_bytes(cell_count()); + } + +protected: + // Low-level accessors for underlying data + void set_intptr_at(int index, intptr_t value) { + assert(0 <= index && index < cell_count(), "oob"); + data()->set_cell_at(index, value); + } + void release_set_intptr_at(int index, intptr_t value) { + assert(0 <= index && index < cell_count(), "oob"); + data()->release_set_cell_at(index, value); + } + intptr_t intptr_at(int index) { + assert(0 <= index && index < cell_count(), "oob"); + return data()->cell_at(index); + } + void set_uint_at(int index, uint value) { + set_intptr_at(index, (intptr_t) value); + } + void release_set_uint_at(int index, uint value) { + release_set_intptr_at(index, (intptr_t) value); + } + uint uint_at(int index) { + return (uint)intptr_at(index); + } + void set_int_at(int index, int value) { + set_intptr_at(index, (intptr_t) value); + } + void release_set_int_at(int index, int value) { + release_set_intptr_at(index, (intptr_t) value); + } + int int_at(int index) { + return (int)intptr_at(index); + } + int int_at_unchecked(int index) { + return (int)data()->cell_at(index); + } + void set_oop_at(int index, oop value) { + set_intptr_at(index, (intptr_t) value); + } + oop oop_at(int index) { + return (oop)intptr_at(index); + } + oop* adr_oop_at(int index) { + assert(0 <= index && index < cell_count(), "oob"); + return data()->adr_oop_at(index); + } + + void set_flag_at(int flag_number) { + data()->set_flag_at(flag_number); + } + bool flag_at(int flag_number) { + return data()->flag_at(flag_number); + } + + // two convenient imports for use by subclasses: + static ByteSize cell_offset(int index) { + return DataLayout::cell_offset(index); + } + static int flag_number_to_byte_constant(int flag_number) { + return DataLayout::flag_number_to_byte_constant(flag_number); + } + + ProfileData(DataLayout* data) { + _data = data; + } + +public: + // Constructor for invalid ProfileData. + ProfileData(); + + u2 bci() { + return data()->bci(); + } + + address dp() { + return (address)_data; + } + + int trap_state() { + return data()->trap_state(); + } + void set_trap_state(int new_state) { + data()->set_trap_state(new_state); + } + + // Type checking + virtual bool is_BitData() { return false; } + virtual bool is_CounterData() { return false; } + virtual bool is_JumpData() { return false; } + virtual bool is_ReceiverTypeData(){ return false; } + virtual bool is_VirtualCallData() { return false; } + virtual bool is_RetData() { return false; } + virtual bool is_BranchData() { return false; } + virtual bool is_ArrayData() { return false; } + virtual bool is_MultiBranchData() { return false; } + + BitData* as_BitData() { + assert(is_BitData(), "wrong type"); + return is_BitData() ? (BitData*) this : NULL; + } + CounterData* as_CounterData() { + assert(is_CounterData(), "wrong type"); + return is_CounterData() ? (CounterData*) this : NULL; + } + JumpData* as_JumpData() { + assert(is_JumpData(), "wrong type"); + return is_JumpData() ? (JumpData*) this : NULL; + } + ReceiverTypeData* as_ReceiverTypeData() { + assert(is_ReceiverTypeData(), "wrong type"); + return is_ReceiverTypeData() ? (ReceiverTypeData*)this : NULL; + } + VirtualCallData* as_VirtualCallData() { + assert(is_VirtualCallData(), "wrong type"); + return is_VirtualCallData() ? (VirtualCallData*)this : NULL; + } + RetData* as_RetData() { + assert(is_RetData(), "wrong type"); + return is_RetData() ? (RetData*) this : NULL; + } + BranchData* as_BranchData() { + assert(is_BranchData(), "wrong type"); + return is_BranchData() ? (BranchData*) this : NULL; + } + ArrayData* as_ArrayData() { + assert(is_ArrayData(), "wrong type"); + return is_ArrayData() ? (ArrayData*) this : NULL; + } + MultiBranchData* as_MultiBranchData() { + assert(is_MultiBranchData(), "wrong type"); + return is_MultiBranchData() ? (MultiBranchData*)this : NULL; + } + + + // Subclass specific initialization + virtual void post_initialize(BytecodeStream* stream, methodDataOop mdo) {} + + // GC support + virtual void follow_contents() {} + virtual void oop_iterate(OopClosure* blk) {} + virtual void oop_iterate_m(OopClosure* blk, MemRegion mr) {} + virtual void adjust_pointers() {} + +#ifndef SERIALGC + // Parallel old support + virtual void follow_contents(ParCompactionManager* cm) {} + virtual void update_pointers() {} + virtual void update_pointers(HeapWord* beg_addr, HeapWord* end_addr) {} +#endif // SERIALGC + + // CI translation: ProfileData can represent both MethodDataOop data + // as well as CIMethodData data. This function is provided for translating + // an oop in a ProfileData to the ci equivalent. Generally speaking, + // most ProfileData don't require any translation, so we provide the null + // translation here, and the required translators are in the ci subclasses. + virtual void translate_from(ProfileData* data) {} + + virtual void print_data_on(outputStream* st) { + ShouldNotReachHere(); + } + +#ifndef PRODUCT + void print_shared(outputStream* st, const char* name); + void tab(outputStream* st); +#endif +}; + +// BitData +// +// A BitData holds a flag or two in its header. +class BitData : public ProfileData { +protected: + enum { + // null_seen: + // saw a null operand (cast/aastore/instanceof) + null_seen_flag = DataLayout::first_flag + 0 + }; + enum { bit_cell_count = 0 }; // no additional data fields needed. +public: + BitData(DataLayout* layout) : ProfileData(layout) { + } + + virtual bool is_BitData() { return true; } + + static int static_cell_count() { + return bit_cell_count; + } + + virtual int cell_count() { + return static_cell_count(); + } + + // Accessor + + // The null_seen flag bit is specially known to the interpreter. + // Consulting it allows the compiler to avoid setting up null_check traps. + bool null_seen() { return flag_at(null_seen_flag); } + void set_null_seen() { set_flag_at(null_seen_flag); } + + + // Code generation support + static int null_seen_byte_constant() { + return flag_number_to_byte_constant(null_seen_flag); + } + + static ByteSize bit_data_size() { + return cell_offset(bit_cell_count); + } + +#ifndef PRODUCT + void print_data_on(outputStream* st); +#endif +}; + +// CounterData +// +// A CounterData corresponds to a simple counter. +class CounterData : public BitData { +protected: + enum { + count_off, + counter_cell_count + }; +public: + CounterData(DataLayout* layout) : BitData(layout) {} + + virtual bool is_CounterData() { return true; } + + static int static_cell_count() { + return counter_cell_count; + } + + virtual int cell_count() { + return static_cell_count(); + } + + // Direct accessor + uint count() { + return uint_at(count_off); + } + + // Code generation support + static ByteSize count_offset() { + return cell_offset(count_off); + } + static ByteSize counter_data_size() { + return cell_offset(counter_cell_count); + } + +#ifndef PRODUCT + void print_data_on(outputStream* st); +#endif +}; + +// JumpData +// +// A JumpData is used to access profiling information for a direct +// branch. It is a counter, used for counting the number of branches, +// plus a data displacement, used for realigning the data pointer to +// the corresponding target bci. +class JumpData : public ProfileData { +protected: + enum { + taken_off_set, + displacement_off_set, + jump_cell_count + }; + + void set_displacement(int displacement) { + set_int_at(displacement_off_set, displacement); + } + +public: + JumpData(DataLayout* layout) : ProfileData(layout) { + assert(layout->tag() == DataLayout::jump_data_tag || + layout->tag() == DataLayout::branch_data_tag, "wrong type"); + } + + virtual bool is_JumpData() { return true; } + + static int static_cell_count() { + return jump_cell_count; + } + + virtual int cell_count() { + return static_cell_count(); + } + + // Direct accessor + uint taken() { + return uint_at(taken_off_set); + } + // Saturating counter + uint inc_taken() { + uint cnt = taken() + 1; + // Did we wrap? Will compiler screw us?? + if (cnt == 0) cnt--; + set_uint_at(taken_off_set, cnt); + return cnt; + } + + int displacement() { + return int_at(displacement_off_set); + } + + // Code generation support + static ByteSize taken_offset() { + return cell_offset(taken_off_set); + } + + static ByteSize displacement_offset() { + return cell_offset(displacement_off_set); + } + + // Specific initialization. + void post_initialize(BytecodeStream* stream, methodDataOop mdo); + +#ifndef PRODUCT + void print_data_on(outputStream* st); +#endif +}; + +// ReceiverTypeData +// +// A ReceiverTypeData is used to access profiling information about a +// dynamic type check. It consists of a counter which counts the total times +// that the check is reached, and a series of (klassOop, count) pairs +// which are used to store a type profile for the receiver of the check. +class ReceiverTypeData : public CounterData { +protected: + enum { + receiver0_offset = counter_cell_count, + count0_offset, + receiver_type_row_cell_count = (count0_offset + 1) - receiver0_offset + }; + +public: + ReceiverTypeData(DataLayout* layout) : CounterData(layout) { + assert(layout->tag() == DataLayout::receiver_type_data_tag || + layout->tag() == DataLayout::virtual_call_data_tag, "wrong type"); + } + + virtual bool is_ReceiverTypeData() { return true; } + + static int static_cell_count() { + return counter_cell_count + (uint) TypeProfileWidth * receiver_type_row_cell_count; + } + + virtual int cell_count() { + return static_cell_count(); + } + + // Direct accessors + static uint row_limit() { + return TypeProfileWidth; + } + static int receiver_cell_index(uint row) { + return receiver0_offset + row * receiver_type_row_cell_count; + } + static int receiver_count_cell_index(uint row) { + return count0_offset + row * receiver_type_row_cell_count; + } + + // Get the receiver at row. The 'unchecked' version is needed by parallel old + // gc; it does not assert the receiver is a klass. During compaction of the + // perm gen, the klass may already have moved, so the is_klass() predicate + // would fail. The 'normal' version should be used whenever possible. + klassOop receiver_unchecked(uint row) { + assert(row < row_limit(), "oob"); + oop recv = oop_at(receiver_cell_index(row)); + return (klassOop)recv; + } + + klassOop receiver(uint row) { + klassOop recv = receiver_unchecked(row); + assert(recv == NULL || ((oop)recv)->is_klass(), "wrong type"); + return recv; + } + + uint receiver_count(uint row) { + assert(row < row_limit(), "oob"); + return uint_at(receiver_count_cell_index(row)); + } + + // Code generation support + static ByteSize receiver_offset(uint row) { + return cell_offset(receiver_cell_index(row)); + } + static ByteSize receiver_count_offset(uint row) { + return cell_offset(receiver_count_cell_index(row)); + } + static ByteSize receiver_type_data_size() { + return cell_offset(static_cell_count()); + } + + // GC support + virtual void follow_contents(); + virtual void oop_iterate(OopClosure* blk); + virtual void oop_iterate_m(OopClosure* blk, MemRegion mr); + virtual void adjust_pointers(); + +#ifndef SERIALGC + // Parallel old support + virtual void follow_contents(ParCompactionManager* cm); + virtual void update_pointers(); + virtual void update_pointers(HeapWord* beg_addr, HeapWord* end_addr); +#endif // SERIALGC + + oop* adr_receiver(uint row) { + return adr_oop_at(receiver_cell_index(row)); + } + +#ifndef PRODUCT + void print_receiver_data_on(outputStream* st); + void print_data_on(outputStream* st); +#endif +}; + +// VirtualCallData +// +// A VirtualCallData is used to access profiling information about a +// virtual call. For now, it has nothing more than a ReceiverTypeData. +class VirtualCallData : public ReceiverTypeData { +public: + VirtualCallData(DataLayout* layout) : ReceiverTypeData(layout) { + assert(layout->tag() == DataLayout::virtual_call_data_tag, "wrong type"); + } + + virtual bool is_VirtualCallData() { return true; } + + static int static_cell_count() { + // At this point we could add more profile state, e.g., for arguments. + // But for now it's the same size as the base record type. + return ReceiverTypeData::static_cell_count(); + } + + virtual int cell_count() { + return static_cell_count(); + } + + // Direct accessors + static ByteSize virtual_call_data_size() { + return cell_offset(static_cell_count()); + } + +#ifndef PRODUCT + void print_data_on(outputStream* st); +#endif +}; + +// RetData +// +// A RetData is used to access profiling information for a ret bytecode. +// It is composed of a count of the number of times that the ret has +// been executed, followed by a series of triples of the form +// (bci, count, di) which count the number of times that some bci was the +// target of the ret and cache a corresponding data displacement. +class RetData : public CounterData { +protected: + enum { + bci0_offset = counter_cell_count, + count0_offset, + displacement0_offset, + ret_row_cell_count = (displacement0_offset + 1) - bci0_offset + }; + + void set_bci(uint row, int bci) { + assert((uint)row < row_limit(), "oob"); + set_int_at(bci0_offset + row * ret_row_cell_count, bci); + } + void release_set_bci(uint row, int bci) { + assert((uint)row < row_limit(), "oob"); + // 'release' when setting the bci acts as a valid flag for other + // threads wrt bci_count and bci_displacement. + release_set_int_at(bci0_offset + row * ret_row_cell_count, bci); + } + void set_bci_count(uint row, uint count) { + assert((uint)row < row_limit(), "oob"); + set_uint_at(count0_offset + row * ret_row_cell_count, count); + } + void set_bci_displacement(uint row, int disp) { + set_int_at(displacement0_offset + row * ret_row_cell_count, disp); + } + +public: + RetData(DataLayout* layout) : CounterData(layout) { + assert(layout->tag() == DataLayout::ret_data_tag, "wrong type"); + } + + virtual bool is_RetData() { return true; } + + enum { + no_bci = -1 // value of bci when bci1/2 are not in use. + }; + + static int static_cell_count() { + return counter_cell_count + (uint) BciProfileWidth * ret_row_cell_count; + } + + virtual int cell_count() { + return static_cell_count(); + } + + static uint row_limit() { + return BciProfileWidth; + } + static int bci_cell_index(uint row) { + return bci0_offset + row * ret_row_cell_count; + } + static int bci_count_cell_index(uint row) { + return count0_offset + row * ret_row_cell_count; + } + static int bci_displacement_cell_index(uint row) { + return displacement0_offset + row * ret_row_cell_count; + } + + // Direct accessors + int bci(uint row) { + return int_at(bci_cell_index(row)); + } + uint bci_count(uint row) { + return uint_at(bci_count_cell_index(row)); + } + int bci_displacement(uint row) { + return int_at(bci_displacement_cell_index(row)); + } + + // Interpreter Runtime support + address fixup_ret(int return_bci, methodDataHandle mdo); + + // Code generation support + static ByteSize bci_offset(uint row) { + return cell_offset(bci_cell_index(row)); + } + static ByteSize bci_count_offset(uint row) { + return cell_offset(bci_count_cell_index(row)); + } + static ByteSize bci_displacement_offset(uint row) { + return cell_offset(bci_displacement_cell_index(row)); + } + + // Specific initialization. + void post_initialize(BytecodeStream* stream, methodDataOop mdo); + +#ifndef PRODUCT + void print_data_on(outputStream* st); +#endif +}; + +// BranchData +// +// A BranchData is used to access profiling data for a two-way branch. +// It consists of taken and not_taken counts as well as a data displacement +// for the taken case. +class BranchData : public JumpData { +protected: + enum { + not_taken_off_set = jump_cell_count, + branch_cell_count + }; + + void set_displacement(int displacement) { + set_int_at(displacement_off_set, displacement); + } + +public: + BranchData(DataLayout* layout) : JumpData(layout) { + assert(layout->tag() == DataLayout::branch_data_tag, "wrong type"); + } + + virtual bool is_BranchData() { return true; } + + static int static_cell_count() { + return branch_cell_count; + } + + virtual int cell_count() { + return static_cell_count(); + } + + // Direct accessor + uint not_taken() { + return uint_at(not_taken_off_set); + } + + uint inc_not_taken() { + uint cnt = not_taken() + 1; + // Did we wrap? Will compiler screw us?? + if (cnt == 0) cnt--; + set_uint_at(not_taken_off_set, cnt); + return cnt; + } + + // Code generation support + static ByteSize not_taken_offset() { + return cell_offset(not_taken_off_set); + } + static ByteSize branch_data_size() { + return cell_offset(branch_cell_count); + } + + // Specific initialization. + void post_initialize(BytecodeStream* stream, methodDataOop mdo); + +#ifndef PRODUCT + void print_data_on(outputStream* st); +#endif +}; + +// ArrayData +// +// A ArrayData is a base class for accessing profiling data which does +// not have a statically known size. It consists of an array length +// and an array start. +class ArrayData : public ProfileData { +protected: + friend class DataLayout; + + enum { + array_len_off_set, + array_start_off_set + }; + + uint array_uint_at(int index) { + int aindex = index + array_start_off_set; + return uint_at(aindex); + } + int array_int_at(int index) { + int aindex = index + array_start_off_set; + return int_at(aindex); + } + oop array_oop_at(int index) { + int aindex = index + array_start_off_set; + return oop_at(aindex); + } + void array_set_int_at(int index, int value) { + int aindex = index + array_start_off_set; + set_int_at(aindex, value); + } + + // Code generation support for subclasses. + static ByteSize array_element_offset(int index) { + return cell_offset(array_start_off_set + index); + } + +public: + ArrayData(DataLayout* layout) : ProfileData(layout) {} + + virtual bool is_ArrayData() { return true; } + + static int static_cell_count() { + return -1; + } + + int array_len() { + return int_at_unchecked(array_len_off_set); + } + + virtual int cell_count() { + return array_len() + 1; + } + + // Code generation support + static ByteSize array_len_offset() { + return cell_offset(array_len_off_set); + } + static ByteSize array_start_offset() { + return cell_offset(array_start_off_set); + } +}; + +// MultiBranchData +// +// A MultiBranchData is used to access profiling information for +// a multi-way branch (*switch bytecodes). It consists of a series +// of (count, displacement) pairs, which count the number of times each +// case was taken and specify the data displacment for each branch target. +class MultiBranchData : public ArrayData { +protected: + enum { + default_count_off_set, + default_disaplacement_off_set, + case_array_start + }; + enum { + relative_count_off_set, + relative_displacement_off_set, + per_case_cell_count + }; + + void set_default_displacement(int displacement) { + array_set_int_at(default_disaplacement_off_set, displacement); + } + void set_displacement_at(int index, int displacement) { + array_set_int_at(case_array_start + + index * per_case_cell_count + + relative_displacement_off_set, + displacement); + } + +public: + MultiBranchData(DataLayout* layout) : ArrayData(layout) { + assert(layout->tag() == DataLayout::multi_branch_data_tag, "wrong type"); + } + + virtual bool is_MultiBranchData() { return true; } + + static int compute_cell_count(BytecodeStream* stream); + + int number_of_cases() { + int alen = array_len() - 2; // get rid of default case here. + assert(alen % per_case_cell_count == 0, "must be even"); + return (alen / per_case_cell_count); + } + + uint default_count() { + return array_uint_at(default_count_off_set); + } + int default_displacement() { + return array_int_at(default_disaplacement_off_set); + } + + uint count_at(int index) { + return array_uint_at(case_array_start + + index * per_case_cell_count + + relative_count_off_set); + } + int displacement_at(int index) { + return array_int_at(case_array_start + + index * per_case_cell_count + + relative_displacement_off_set); + } + + // Code generation support + static ByteSize default_count_offset() { + return array_element_offset(default_count_off_set); + } + static ByteSize default_displacement_offset() { + return array_element_offset(default_disaplacement_off_set); + } + static ByteSize case_count_offset(int index) { + return case_array_offset() + + (per_case_size() * index) + + relative_count_offset(); + } + static ByteSize case_array_offset() { + return array_element_offset(case_array_start); + } + static ByteSize per_case_size() { + return in_ByteSize(per_case_cell_count) * cell_size; + } + static ByteSize relative_count_offset() { + return in_ByteSize(relative_count_off_set) * cell_size; + } + static ByteSize relative_displacement_offset() { + return in_ByteSize(relative_displacement_off_set) * cell_size; + } + + // Specific initialization. + void post_initialize(BytecodeStream* stream, methodDataOop mdo); + +#ifndef PRODUCT + void print_data_on(outputStream* st); +#endif +}; + +// methodDataOop +// +// A methodDataOop holds information which has been collected about +// a method. Its layout looks like this: +// +// ----------------------------- +// | header | +// | klass | +// ----------------------------- +// | method | +// | size of the methodDataOop | +// ----------------------------- +// | Data entries... | +// | (variable size) | +// | | +// . . +// . . +// . . +// | | +// ----------------------------- +// +// The data entry area is a heterogeneous array of DataLayouts. Each +// DataLayout in the array corresponds to a specific bytecode in the +// method. The entries in the array are sorted by the corresponding +// bytecode. Access to the data is via resource-allocated ProfileData, +// which point to the underlying blocks of DataLayout structures. +// +// During interpretation, if profiling in enabled, the interpreter +// maintains a method data pointer (mdp), which points at the entry +// in the array corresponding to the current bci. In the course of +// intepretation, when a bytecode is encountered that has profile data +// associated with it, the entry pointed to by mdp is updated, then the +// mdp is adjusted to point to the next appropriate DataLayout. If mdp +// is NULL to begin with, the interpreter assumes that the current method +// is not (yet) being profiled. +// +// In methodDataOop parlance, "dp" is a "data pointer", the actual address +// of a DataLayout element. A "di" is a "data index", the offset in bytes +// from the base of the data entry array. A "displacement" is the byte offset +// in certain ProfileData objects that indicate the amount the mdp must be +// adjusted in the event of a change in control flow. +// + +class methodDataOopDesc : public oopDesc { + friend class VMStructs; +private: + friend class ProfileData; + + // Back pointer to the methodOop + methodOop _method; + + // Size of this oop in bytes + int _size; + + // Cached hint for bci_to_dp and bci_to_data + int _hint_di; + + // Whole-method sticky bits and flags +public: + enum { + _trap_hist_limit = 16, // decoupled from Deoptimization::Reason_LIMIT + _trap_hist_mask = max_jubyte, + _extra_data_count = 4 // extra DataLayout headers, for trap history + }; // Public flag values +private: + uint _nof_decompiles; // count of all nmethod removals + uint _nof_overflow_recompiles; // recompile count, excluding recomp. bits + uint _nof_overflow_traps; // trap count, excluding _trap_hist + union { + intptr_t _align; + u1 _array[_trap_hist_limit]; + } _trap_hist; + + // Support for interprocedural escape analysis, from Thomas Kotzmann. + intx _eflags; // flags on escape information + intx _arg_local; // bit set of non-escaping arguments + intx _arg_stack; // bit set of stack-allocatable arguments + intx _arg_returned; // bit set of returned arguments + + int _creation_mileage; // method mileage at MDO creation + + // Size of _data array in bytes. (Excludes header and extra_data fields.) + int _data_size; + + // Beginning of the data entries + intptr_t _data[1]; + + // Helper for size computation + static int compute_data_size(BytecodeStream* stream); + static int bytecode_cell_count(Bytecodes::Code code); + enum { no_profile_data = -1, variable_cell_count = -2 }; + + // Helper for initialization + DataLayout* data_layout_at(int data_index) { + assert(data_index % sizeof(intptr_t) == 0, "unaligned"); + return (DataLayout*) (((address)_data) + data_index); + } + + // Initialize an individual data segment. Returns the size of + // the segment in bytes. + int initialize_data(BytecodeStream* stream, int data_index); + + // Helper for data_at + DataLayout* limit_data_position() { + return (DataLayout*)((address)data_base() + _data_size); + } + bool out_of_bounds(int data_index) { + return data_index >= data_size(); + } + + // Give each of the data entries a chance to perform specific + // data initialization. + void post_initialize(BytecodeStream* stream); + + // hint accessors + int hint_di() const { return _hint_di; } + void set_hint_di(int di) { + assert(!out_of_bounds(di), "hint_di out of bounds"); + _hint_di = di; + } + ProfileData* data_before(int bci) { + // avoid SEGV on this edge case + if (data_size() == 0) + return NULL; + int hint = hint_di(); + if (data_layout_at(hint)->bci() <= bci) + return data_at(hint); + return first_data(); + } + + // What is the index of the first data entry? + int first_di() { return 0; } + + // Find or create an extra ProfileData: + ProfileData* bci_to_extra_data(int bci, bool create_if_missing); + +public: + static int header_size() { + return sizeof(methodDataOopDesc)/wordSize; + } + + // Compute the size of a methodDataOop before it is created. + static int compute_allocation_size_in_bytes(methodHandle method); + static int compute_allocation_size_in_words(methodHandle method); + static int compute_extra_data_count(int data_size, int empty_bc_count); + + // Determine if a given bytecode can have profile information. + static bool bytecode_has_profile(Bytecodes::Code code) { + return bytecode_cell_count(code) != no_profile_data; + } + + // Perform initialization of a new methodDataOop + void initialize(methodHandle method); + + // My size + int object_size_in_bytes() { return _size; } + int object_size() { + return align_object_size(align_size_up(_size, BytesPerWord)/BytesPerWord); + } + + int creation_mileage() const { return _creation_mileage; } + void set_creation_mileage(int x) { _creation_mileage = x; } + bool is_mature() const; // consult mileage and ProfileMaturityPercentage + static int mileage_of(methodOop m); + + // Support for interprocedural escape analysis, from Thomas Kotzmann. + enum EscapeFlag { + estimated = 1 << 0, + return_local = 1 << 1 + }; + + intx eflags() { return _eflags; } + intx arg_local() { return _arg_local; } + intx arg_stack() { return _arg_stack; } + intx arg_returned() { return _arg_returned; } + + void set_eflags(intx v) { _eflags = v; } + void set_arg_local(intx v) { _arg_local = v; } + void set_arg_stack(intx v) { _arg_stack = v; } + void set_arg_returned(intx v) { _arg_returned = v; } + + void clear_escape_info() { _eflags = _arg_local = _arg_stack = _arg_returned = 0; } + + // Location and size of data area + address data_base() const { + return (address) _data; + } + int data_size() { + return _data_size; + } + + // Accessors + methodOop method() { return _method; } + + // Get the data at an arbitrary (sort of) data index. + ProfileData* data_at(int data_index); + + // Walk through the data in order. + ProfileData* first_data() { return data_at(first_di()); } + ProfileData* next_data(ProfileData* current); + bool is_valid(ProfileData* current) { return current != NULL; } + + // Convert a dp (data pointer) to a di (data index). + int dp_to_di(address dp) { + return dp - ((address)_data); + } + + address di_to_dp(int di) { + return (address)data_layout_at(di); + } + + // bci to di/dp conversion. + address bci_to_dp(int bci); + int bci_to_di(int bci) { + return dp_to_di(bci_to_dp(bci)); + } + + // Get the data at an arbitrary bci, or NULL if there is none. + ProfileData* bci_to_data(int bci); + + // Same, but try to create an extra_data record if one is needed: + ProfileData* allocate_bci_to_data(int bci) { + ProfileData* data = bci_to_data(bci); + return (data != NULL) ? data : bci_to_extra_data(bci, true); + } + + // Add a handful of extra data records, for trap tracking. + DataLayout* extra_data_base() { return limit_data_position(); } + DataLayout* extra_data_limit() { return (DataLayout*)((address)this + object_size_in_bytes()); } + int extra_data_size() { return (address)extra_data_limit() + - (address)extra_data_base(); } + static DataLayout* next_extra(DataLayout* dp) { return (DataLayout*)((address)dp + in_bytes(DataLayout::cell_offset(0))); } + + // Return (uint)-1 for overflow. + uint trap_count(int reason) const { + assert((uint)reason < _trap_hist_limit, "oob"); + return (int)((_trap_hist._array[reason]+1) & _trap_hist_mask) - 1; + } + // For loops: + static uint trap_reason_limit() { return _trap_hist_limit; } + static uint trap_count_limit() { return _trap_hist_mask; } + uint inc_trap_count(int reason) { + // Count another trap, anywhere in this method. + assert(reason >= 0, "must be single trap"); + if ((uint)reason < _trap_hist_limit) { + uint cnt1 = 1 + _trap_hist._array[reason]; + if ((cnt1 & _trap_hist_mask) != 0) { // if no counter overflow... + _trap_hist._array[reason] = cnt1; + return cnt1; + } else { + return _trap_hist_mask + (++_nof_overflow_traps); + } + } else { + // Could not represent the count in the histogram. + return (++_nof_overflow_traps); + } + } + + uint overflow_trap_count() const { + return _nof_overflow_traps; + } + uint overflow_recompile_count() const { + return _nof_overflow_recompiles; + } + void inc_overflow_recompile_count() { + _nof_overflow_recompiles += 1; + } + uint decompile_count() const { + return _nof_decompiles; + } + void inc_decompile_count() { + _nof_decompiles += 1; + } + + // Support for code generation + static ByteSize data_offset() { + return byte_offset_of(methodDataOopDesc, _data[0]); + } + + // GC support + oop* adr_method() const { return (oop*)&_method; } + bool object_is_parsable() const { return _size != 0; } + void set_object_is_parsable(int object_size_in_bytes) { _size = object_size_in_bytes; } + +#ifndef PRODUCT + // printing support for method data + void print_data_on(outputStream* st); +#endif + + // verification + void verify_data_on(outputStream* st); +}; diff --git a/hotspot/src/share/vm/oops/methodKlass.cpp b/hotspot/src/share/vm/oops/methodKlass.cpp new file mode 100644 index 00000000000..bcc9afb4f31 --- /dev/null +++ b/hotspot/src/share/vm/oops/methodKlass.cpp @@ -0,0 +1,361 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_methodKlass.cpp.incl" + +klassOop methodKlass::create_klass(TRAPS) { + methodKlass o; + KlassHandle h_this_klass(THREAD, Universe::klassKlassObj()); + KlassHandle k = base_create_klass(h_this_klass, header_size(), o.vtbl_value(), CHECK_NULL); + // Make sure size calculation is right + assert(k()->size() == align_object_size(header_size()), "wrong size for object"); + java_lang_Class::create_mirror(k, CHECK_NULL); // Allocate mirror + return k(); +} + + +int methodKlass::oop_size(oop obj) const { + assert(obj->is_method(), "must be method oop"); + return methodOop(obj)->object_size(); +} + + +bool methodKlass::oop_is_parsable(oop obj) const { + assert(obj->is_method(), "must be method oop"); + return methodOop(obj)->object_is_parsable(); +} + + +methodOop methodKlass::allocate(constMethodHandle xconst, + AccessFlags access_flags, TRAPS) { + int size = methodOopDesc::object_size(access_flags.is_native()); + KlassHandle h_k(THREAD, as_klassOop()); + assert(xconst()->is_parsable(), "possible publication protocol violation"); + methodOop m = (methodOop)CollectedHeap::permanent_obj_allocate(h_k, size, CHECK_NULL); + assert(!m->is_parsable(), "not expecting parsability yet."); + + No_Safepoint_Verifier no_safepoint; // until m becomes parsable below + m->set_constMethod(xconst()); + m->set_access_flags(access_flags); + m->set_method_size(size); + m->set_name_index(0); + m->set_signature_index(0); +#ifdef CC_INTERP + m->set_result_index(T_VOID); +#endif + m->set_constants(NULL); + m->set_max_stack(0); + m->set_max_locals(0); + m->clear_intrinsic_id_cache(); + m->set_method_data(NULL); + m->set_interpreter_throwout_count(0); + m->set_vtable_index(methodOopDesc::garbage_vtable_index); + + // Fix and bury in methodOop + m->set_interpreter_entry(NULL); // sets i2i entry and from_int + m->set_highest_tier_compile(CompLevel_none); + m->set_adapter_entry(NULL); + m->clear_code(); // from_c/from_i get set to c2i/i2i + + if (access_flags.is_native()) { + m->clear_native_function(); + m->set_signature_handler(NULL); + } + + NOT_PRODUCT(m->set_compiled_invocation_count(0);) + m->set_interpreter_invocation_count(0); + m->invocation_counter()->init(); + m->backedge_counter()->init(); + m->clear_number_of_breakpoints(); + assert(m->is_parsable(), "must be parsable here."); + assert(m->size() == size, "wrong size for object"); + // We should not publish an uprasable object's reference + // into one that is parsable, since that presents problems + // for the concurrent parallel marking and precleaning phases + // of concurrent gc (CMS). + xconst->set_method(m); + return m; +} + + +void methodKlass::oop_follow_contents(oop obj) { + assert (obj->is_method(), "object must be method"); + methodOop m = methodOop(obj); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::methodKlassObj never moves. + MarkSweep::mark_and_push(m->adr_constMethod()); + MarkSweep::mark_and_push(m->adr_constants()); + if (m->method_data() != NULL) { + MarkSweep::mark_and_push(m->adr_method_data()); + } +} + +#ifndef SERIALGC +void methodKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert (obj->is_method(), "object must be method"); + methodOop m = methodOop(obj); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::methodKlassObj never moves. + PSParallelCompact::mark_and_push(cm, m->adr_constMethod()); + PSParallelCompact::mark_and_push(cm, m->adr_constants()); +#ifdef COMPILER2 + if (m->method_data() != NULL) { + PSParallelCompact::mark_and_push(cm, m->adr_method_data()); + } +#endif // COMPILER2 +} +#endif // SERIALGC + +int methodKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert (obj->is_method(), "object must be method"); + methodOop m = methodOop(obj); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = m->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::methodKlassObj never moves + blk->do_oop(m->adr_constMethod()); + blk->do_oop(m->adr_constants()); + if (m->method_data() != NULL) { + blk->do_oop(m->adr_method_data()); + } + return size; +} + + +int methodKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + assert (obj->is_method(), "object must be method"); + methodOop m = methodOop(obj); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = m->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::methodKlassObj never moves. + oop* adr; + adr = m->adr_constMethod(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = m->adr_constants(); + if (mr.contains(adr)) blk->do_oop(adr); + if (m->method_data() != NULL) { + adr = m->adr_method_data(); + if (mr.contains(adr)) blk->do_oop(adr); + } + return size; +} + + +int methodKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_method(), "should be method"); + methodOop m = methodOop(obj); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = m->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::methodKlassObj never moves. + MarkSweep::adjust_pointer(m->adr_constMethod()); + MarkSweep::adjust_pointer(m->adr_constants()); + if (m->method_data() != NULL) { + MarkSweep::adjust_pointer(m->adr_method_data()); + } + return size; +} + +#ifndef SERIALGC +void methodKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_method(), "should be method"); +} + +void methodKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_method(), "should be method"); +} + +int methodKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert(obj->is_method(), "should be method"); + methodOop m = methodOop(obj); + PSParallelCompact::adjust_pointer(m->adr_constMethod()); + PSParallelCompact::adjust_pointer(m->adr_constants()); +#ifdef COMPILER2 + if (m->method_data() != NULL) { + PSParallelCompact::adjust_pointer(m->adr_method_data()); + } +#endif // COMPILER2 + return m->object_size(); +} + +int methodKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, HeapWord* end_addr) { + assert(obj->is_method(), "should be method"); + + oop* p; + methodOop m = methodOop(obj); + + p = m->adr_constMethod(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = m->adr_constants(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + +#ifdef COMPILER2 + if (m->method_data() != NULL) { + p = m->adr_method_data(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + } +#endif // COMPILER2 + return m->object_size(); +} +#endif // SERIALGC + +#ifndef PRODUCT + +// Printing + +void methodKlass::oop_print_on(oop obj, outputStream* st) { + ResourceMark rm; + assert(obj->is_method(), "must be method"); + Klass::oop_print_on(obj, st); + methodOop m = methodOop(obj); + st->print (" - method holder: "); m->method_holder()->print_value_on(st); st->cr(); + st->print (" - constants: " INTPTR_FORMAT, " ", (address)m->constants()); + m->constants()->print_value_on(st); st->cr(); + st->print (" - access: 0x%x ", m->access_flags().as_int()); m->access_flags().print_on(st); st->cr(); + st->print (" - name: "); m->name()->print_value_on(st); st->cr(); + st->print (" - signature: "); m->signature()->print_value_on(st); st->cr(); + st->print_cr(" - max stack: %d", m->max_stack()); + st->print_cr(" - max locals: %d", m->max_locals()); + st->print_cr(" - size of params: %d", m->size_of_parameters()); + st->print_cr(" - method size: %d", m->method_size()); + st->print_cr(" - vtable index: %d", m->_vtable_index); + st->print_cr(" - code size: %d", m->code_size()); + st->print_cr(" - code start: " INTPTR_FORMAT, m->code_base()); + st->print_cr(" - code end (excl): " INTPTR_FORMAT, m->code_base() + m->code_size()); + if (m->method_data() != NULL) { + st->print_cr(" - method data: " INTPTR_FORMAT, (address)m->method_data()); + } + st->print_cr(" - checked ex length: %d", m->checked_exceptions_length()); + if (m->checked_exceptions_length() > 0) { + CheckedExceptionElement* table = m->checked_exceptions_start(); + st->print_cr(" - checked ex start: " INTPTR_FORMAT, table); + if (Verbose) { + for (int i = 0; i < m->checked_exceptions_length(); i++) { + st->print_cr(" - throws %s", m->constants()->printable_name_at(table[i].class_cp_index)); + } + } + } + if (m->has_linenumber_table()) { + u_char* table = m->compressed_linenumber_table(); + st->print_cr(" - linenumber start: " INTPTR_FORMAT, table); + if (Verbose) { + CompressedLineNumberReadStream stream(table); + while (stream.read_pair()) { + st->print_cr(" - line %d: %d", stream.line(), stream.bci()); + } + } + } + st->print_cr(" - localvar length: %d", m->localvariable_table_length()); + if (m->localvariable_table_length() > 0) { + LocalVariableTableElement* table = m->localvariable_table_start(); + st->print_cr(" - localvar start: " INTPTR_FORMAT, table); + if (Verbose) { + for (int i = 0; i < m->localvariable_table_length(); i++) { + int bci = table[i].start_bci; + int len = table[i].length; + const char* name = m->constants()->printable_name_at(table[i].name_cp_index); + const char* desc = m->constants()->printable_name_at(table[i].descriptor_cp_index); + int slot = table[i].slot; + st->print_cr(" - %s %s bci=%d len=%d slot=%d", desc, name, bci, len, slot); + } + } + } + if (m->code() != NULL) { + st->print (" - compiled code: "); + m->code()->print_value_on(st); + st->cr(); + } +} + + +void methodKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_method(), "must be method"); + Klass::oop_print_value_on(obj, st); + methodOop m = methodOop(obj); + st->print(" "); + m->name()->print_value_on(st); + st->print(" "); + m->signature()->print_value_on(st); + st->print(" in "); + m->method_holder()->print_value_on(st); + if (WizardMode) st->print("[%d,%d]", m->size_of_parameters(), m->max_locals()); + if (WizardMode && m->code() != NULL) st->print(" ((nmethod*)%p)", m->code()); +} + +#endif // PRODUCT + +const char* methodKlass::internal_name() const { + return "{method}"; +} + + +// Verification + +void methodKlass::oop_verify_on(oop obj, outputStream* st) { + Klass::oop_verify_on(obj, st); + guarantee(obj->is_method(), "object must be method"); + if (!obj->partially_loaded()) { + methodOop m = methodOop(obj); + guarantee(m->is_perm(), "should be in permspace"); + guarantee(m->name()->is_perm(), "should be in permspace"); + guarantee(m->name()->is_symbol(), "should be symbol"); + guarantee(m->signature()->is_perm(), "should be in permspace"); + guarantee(m->signature()->is_symbol(), "should be symbol"); + guarantee(m->constants()->is_perm(), "should be in permspace"); + guarantee(m->constants()->is_constantPool(), "should be constant pool"); + guarantee(m->constMethod()->is_constMethod(), "should be constMethodOop"); + guarantee(m->constMethod()->is_perm(), "should be in permspace"); + methodDataOop method_data = m->method_data(); + guarantee(method_data == NULL || + method_data->is_perm(), "should be in permspace"); + guarantee(method_data == NULL || + method_data->is_methodData(), "should be method data"); + } +} + +bool methodKlass::oop_partially_loaded(oop obj) const { + assert(obj->is_method(), "object must be method"); + methodOop m = methodOop(obj); + constMethodOop xconst = m->constMethod(); + assert(xconst != NULL, "const method must be set"); + constMethodKlass* ck = constMethodKlass::cast(xconst->klass()); + return ck->oop_partially_loaded(xconst); +} + + +void methodKlass::oop_set_partially_loaded(oop obj) { + assert(obj->is_method(), "object must be method"); + methodOop m = methodOop(obj); + constMethodOop xconst = m->constMethod(); + assert(xconst != NULL, "const method must be set"); + constMethodKlass* ck = constMethodKlass::cast(xconst->klass()); + ck->oop_set_partially_loaded(xconst); +} diff --git a/hotspot/src/share/vm/oops/methodKlass.hpp b/hotspot/src/share/vm/oops/methodKlass.hpp new file mode 100644 index 00000000000..7c26114f744 --- /dev/null +++ b/hotspot/src/share/vm/oops/methodKlass.hpp @@ -0,0 +1,84 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// a methodKlass is the klass of a methodOop + +class methodKlass : public Klass { + friend class VMStructs; + private: + juint _alloc_size; // allocation profiling support + public: + // Testing + bool oop_is_method() const { return true; } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(methodKlass); + methodOop allocate(constMethodHandle xconst, AccessFlags access_flags, + TRAPS); + static klassOop create_klass(TRAPS); + + // Sizing + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + + // Casting from klassOop + static methodKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_method(), "cast to methodKlass"); + return (methodKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(methodKlass)/HeapWordSize; } + int object_size() const { return align_object_size(header_size()); } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + bool oop_is_parsable(oop obj) const; + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Allocation profiling support + juint alloc_size() const { return _alloc_size; } + void set_alloc_size(juint n) { _alloc_size = n; } + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on (oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); +#endif + + public: + // Verify operations + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); + bool oop_partially_loaded(oop obj) const; + void oop_set_partially_loaded(oop obj); +}; diff --git a/hotspot/src/share/vm/oops/methodOop.cpp b/hotspot/src/share/vm/oops/methodOop.cpp new file mode 100644 index 00000000000..bd23bfa772e --- /dev/null +++ b/hotspot/src/share/vm/oops/methodOop.cpp @@ -0,0 +1,1231 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_methodOop.cpp.incl" + + +// Implementation of methodOopDesc + +address methodOopDesc::get_i2c_entry() { + assert(_adapter != NULL, "must have"); + return _adapter->get_i2c_entry(); +} + +address methodOopDesc::get_c2i_entry() { + assert(_adapter != NULL, "must have"); + return _adapter->get_c2i_entry(); +} + +address methodOopDesc::get_c2i_unverified_entry() { + assert(_adapter != NULL, "must have"); + return _adapter->get_c2i_unverified_entry(); +} + +char* methodOopDesc::name_and_sig_as_C_string() { + return name_and_sig_as_C_string(Klass::cast(constants()->pool_holder()), name(), signature()); +} + +char* methodOopDesc::name_and_sig_as_C_string(char* buf, int size) { + return name_and_sig_as_C_string(Klass::cast(constants()->pool_holder()), name(), signature(), buf, size); +} + +char* methodOopDesc::name_and_sig_as_C_string(Klass* klass, symbolOop method_name, symbolOop signature) { + const char* klass_name = klass->external_name(); + int klass_name_len = (int)strlen(klass_name); + int method_name_len = method_name->utf8_length(); + int len = klass_name_len + 1 + method_name_len + signature->utf8_length(); + char* dest = NEW_RESOURCE_ARRAY(char, len + 1); + strcpy(dest, klass_name); + dest[klass_name_len] = '.'; + strcpy(&dest[klass_name_len + 1], method_name->as_C_string()); + strcpy(&dest[klass_name_len + 1 + method_name_len], signature->as_C_string()); + dest[len] = 0; + return dest; +} + +char* methodOopDesc::name_and_sig_as_C_string(Klass* klass, symbolOop method_name, symbolOop signature, char* buf, int size) { + symbolOop klass_name = klass->name(); + klass_name->as_klass_external_name(buf, size); + int len = (int)strlen(buf); + + if (len < size - 1) { + buf[len++] = '.'; + + method_name->as_C_string(&(buf[len]), size - len); + len = (int)strlen(buf); + + signature->as_C_string(&(buf[len]), size - len); + } + + return buf; +} + +int methodOopDesc::fast_exception_handler_bci_for(KlassHandle ex_klass, int throw_bci, TRAPS) { + // exception table holds quadruple entries of the form (beg_bci, end_bci, handler_bci, klass_index) + const int beg_bci_offset = 0; + const int end_bci_offset = 1; + const int handler_bci_offset = 2; + const int klass_index_offset = 3; + const int entry_size = 4; + // access exception table + typeArrayHandle table (THREAD, constMethod()->exception_table()); + int length = table->length(); + assert(length % entry_size == 0, "exception table format has changed"); + // iterate through all entries sequentially + constantPoolHandle pool(THREAD, constants()); + for (int i = 0; i < length; i += entry_size) { + int beg_bci = table->int_at(i + beg_bci_offset); + int end_bci = table->int_at(i + end_bci_offset); + assert(beg_bci <= end_bci, "inconsistent exception table"); + if (beg_bci <= throw_bci && throw_bci < end_bci) { + // exception handler bci range covers throw_bci => investigate further + int handler_bci = table->int_at(i + handler_bci_offset); + int klass_index = table->int_at(i + klass_index_offset); + if (klass_index == 0) { + return handler_bci; + } else if (ex_klass.is_null()) { + return handler_bci; + } else { + // we know the exception class => get the constraint class + // this may require loading of the constraint class; if verification + // fails or some other exception occurs, return handler_bci + klassOop k = pool->klass_at(klass_index, CHECK_(handler_bci)); + KlassHandle klass = KlassHandle(THREAD, k); + assert(klass.not_null(), "klass not loaded"); + if (ex_klass->is_subtype_of(klass())) { + return handler_bci; + } + } + } + } + + return -1; +} + +methodOop methodOopDesc::method_from_bcp(address bcp) { + debug_only(static int count = 0; count++); + assert(Universe::heap()->is_in_permanent(bcp), "bcp not in perm_gen"); + // TO DO: this may be unsafe in some configurations + HeapWord* p = Universe::heap()->block_start(bcp); + assert(Universe::heap()->block_is_obj(p), "must be obj"); + assert(oop(p)->is_constMethod(), "not a method"); + return constMethodOop(p)->method(); +} + + +void methodOopDesc::mask_for(int bci, InterpreterOopMap* mask) { + + Thread* myThread = Thread::current(); + methodHandle h_this(myThread, this); +#ifdef ASSERT + bool has_capability = myThread->is_VM_thread() || + myThread->is_ConcurrentGC_thread() || + myThread->is_GC_task_thread(); + + if (!has_capability) { + if (!VerifyStack && !VerifyLastFrame) { + // verify stack calls this outside VM thread + warning("oopmap should only be accessed by the " + "VM, GC task or CMS threads (or during debugging)"); + InterpreterOopMap local_mask; + instanceKlass::cast(method_holder())->mask_for(h_this, bci, &local_mask); + local_mask.print(); + } + } +#endif + instanceKlass::cast(method_holder())->mask_for(h_this, bci, mask); + return; +} + + +int methodOopDesc::bci_from(address bcp) const { + assert(is_native() && bcp == code_base() || contains(bcp), "bcp doesn't belong to this method"); + return bcp - code_base(); +} + + +// Return (int)bcx if it appears to be a valid BCI. +// Return bci_from((address)bcx) if it appears to be a valid BCP. +// Return -1 otherwise. +// Used by profiling code, when invalid data is a possibility. +// The caller is responsible for validating the methodOop itself. +int methodOopDesc::validate_bci_from_bcx(intptr_t bcx) const { + // keep bci as -1 if not a valid bci + int bci = -1; + if (bcx == 0 || (address)bcx == code_base()) { + // code_size() may return 0 and we allow 0 here + // the method may be native + bci = 0; + } else if (frame::is_bci(bcx)) { + if (bcx < code_size()) { + bci = (int)bcx; + } + } else if (contains((address)bcx)) { + bci = (address)bcx - code_base(); + } + // Assert that if we have dodged any asserts, bci is negative. + assert(bci == -1 || bci == bci_from(bcp_from(bci)), "sane bci if >=0"); + return bci; +} + +address methodOopDesc::bcp_from(int bci) const { + assert((is_native() && bci == 0) || (!is_native() && 0 <= bci && bci < code_size()), "illegal bci"); + address bcp = code_base() + bci; + assert(is_native() && bcp == code_base() || contains(bcp), "bcp doesn't belong to this method"); + return bcp; +} + + +int methodOopDesc::object_size(bool is_native) { + // If native, then include pointers for native_function and signature_handler + int extra_bytes = (is_native) ? 2*sizeof(address*) : 0; + int extra_words = align_size_up(extra_bytes, BytesPerWord) / BytesPerWord; + return align_object_size(header_size() + extra_words); +} + + +symbolOop methodOopDesc::klass_name() const { + klassOop k = method_holder(); + assert(k->is_klass(), "must be klass"); + instanceKlass* ik = (instanceKlass*) k->klass_part(); + return ik->name(); +} + + +void methodOopDesc::set_interpreter_kind() { + int kind = Interpreter::method_kind(methodOop(this)); + assert(kind != Interpreter::invalid, + "interpreter entry must be valid"); + set_interpreter_kind(kind); +} + + +// Attempt to return method oop to original state. Clear any pointers +// (to objects outside the shared spaces). We won't be able to predict +// where they should point in a new JVM. Further initialize some +// entries now in order allow them to be write protected later. + +void methodOopDesc::remove_unshareable_info() { + unlink_method(); + set_interpreter_kind(); +} + + +bool methodOopDesc::was_executed_more_than(int n) const { + // Invocation counter is reset when the methodOop is compiled. + // If the method has compiled code we therefore assume it has + // be excuted more than n times. + if (is_accessor() || is_empty_method() || (code() != NULL)) { + // interpreter doesn't bump invocation counter of trivial methods + // compiler does not bump invocation counter of compiled methods + return true; + } else if (_invocation_counter.carry()) { + // The carry bit is set when the counter overflows and causes + // a compilation to occur. We don't know how many times + // the counter has been reset, so we simply assume it has + // been executed more than n times. + return true; + } else { + return invocation_count() > n; + } +} + +#ifndef PRODUCT +void methodOopDesc::print_invocation_count() const { + if (is_static()) tty->print("static "); + if (is_final()) tty->print("final "); + if (is_synchronized()) tty->print("synchronized "); + if (is_native()) tty->print("native "); + method_holder()->klass_part()->name()->print_symbol_on(tty); + tty->print("."); + name()->print_symbol_on(tty); + signature()->print_symbol_on(tty); + + if (WizardMode) { + // dump the size of the byte codes + tty->print(" {%d}", code_size()); + } + tty->cr(); + + tty->print_cr (" interpreter_invocation_count: %8d ", interpreter_invocation_count()); + tty->print_cr (" invocation_counter: %8d ", invocation_count()); + tty->print_cr (" backedge_counter: %8d ", backedge_count()); + if (CountCompiledCalls) { + tty->print_cr (" compiled_invocation_count: %8d ", compiled_invocation_count()); + } + +} +#endif + +// Build a methodDataOop object to hold information about this method +// collected in the interpreter. +void methodOopDesc::build_interpreter_method_data(methodHandle method, TRAPS) { + // Grab a lock here to prevent multiple + // methodDataOops from being created. + MutexLocker ml(MethodData_lock, THREAD); + if (method->method_data() == NULL) { + methodDataOop method_data = oopFactory::new_methodData(method, CHECK); + method->set_method_data(method_data); + if (PrintMethodData && (Verbose || WizardMode)) { + ResourceMark rm(THREAD); + tty->print("build_interpreter_method_data for "); + method->print_name(tty); + tty->cr(); + // At the end of the run, the MDO, full of data, will be dumped. + } + } +} + +void methodOopDesc::cleanup_inline_caches() { + // The current system doesn't use inline caches in the interpreter + // => nothing to do (keep this method around for future use) +} + + +void methodOopDesc::compute_size_of_parameters(Thread *thread) { + symbolHandle h_signature(thread, signature()); + ArgumentSizeComputer asc(h_signature); + set_size_of_parameters(asc.size() + (is_static() ? 0 : 1)); +} + +#ifdef CC_INTERP +void methodOopDesc::set_result_index(BasicType type) { + _result_index = Interpreter::BasicType_as_index(type); +} +#endif + +BasicType methodOopDesc::result_type() const { + ResultTypeFinder rtf(signature()); + return rtf.type(); +} + + +bool methodOopDesc::is_empty_method() const { + return code_size() == 1 + && *code_base() == Bytecodes::_return; +} + + +bool methodOopDesc::is_vanilla_constructor() const { + // Returns true if this method is a vanilla constructor, i.e. an "" "()V" method + // which only calls the superclass vanilla constructor and possibly does stores of + // zero constants to local fields: + // + // aload_0 + // invokespecial + // indexbyte1 + // indexbyte2 + // + // followed by an (optional) sequence of: + // + // aload_0 + // aconst_null / iconst_0 / fconst_0 / dconst_0 + // putfield + // indexbyte1 + // indexbyte2 + // + // followed by: + // + // return + + assert(name() == vmSymbols::object_initializer_name(), "Should only be called for default constructors"); + assert(signature() == vmSymbols::void_method_signature(), "Should only be called for default constructors"); + int size = code_size(); + // Check if size match + if (size == 0 || size % 5 != 0) return false; + address cb = code_base(); + int last = size - 1; + if (cb[0] != Bytecodes::_aload_0 || cb[1] != Bytecodes::_invokespecial || cb[last] != Bytecodes::_return) { + // Does not call superclass default constructor + return false; + } + // Check optional sequence + for (int i = 4; i < last; i += 5) { + if (cb[i] != Bytecodes::_aload_0) return false; + if (!Bytecodes::is_zero_const(Bytecodes::cast(cb[i+1]))) return false; + if (cb[i+2] != Bytecodes::_putfield) return false; + } + return true; +} + + +bool methodOopDesc::compute_has_loops_flag() { + BytecodeStream bcs(methodOop(this)); + Bytecodes::Code bc; + + while ((bc = bcs.next()) >= 0) { + switch( bc ) { + case Bytecodes::_ifeq: + case Bytecodes::_ifnull: + case Bytecodes::_iflt: + case Bytecodes::_ifle: + case Bytecodes::_ifne: + case Bytecodes::_ifnonnull: + case Bytecodes::_ifgt: + case Bytecodes::_ifge: + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_goto: + case Bytecodes::_jsr: + if( bcs.dest() < bcs.next_bci() ) _access_flags.set_has_loops(); + break; + + case Bytecodes::_goto_w: + case Bytecodes::_jsr_w: + if( bcs.dest_w() < bcs.next_bci() ) _access_flags.set_has_loops(); + break; + } + } + _access_flags.set_loops_flag_init(); + return _access_flags.has_loops(); +} + + +bool methodOopDesc::is_final_method() const { + // %%% Should return true for private methods also, + // since there is no way to override them. + return is_final() || Klass::cast(method_holder())->is_final(); +} + + +bool methodOopDesc::is_strict_method() const { + return is_strict(); +} + + +bool methodOopDesc::can_be_statically_bound() const { + if (is_final_method()) return true; + return vtable_index() == nonvirtual_vtable_index; +} + + +bool methodOopDesc::is_accessor() const { + if (code_size() != 5) return false; + if (size_of_parameters() != 1) return false; + if (Bytecodes::java_code_at(code_base()+0) != Bytecodes::_aload_0 ) return false; + if (Bytecodes::java_code_at(code_base()+1) != Bytecodes::_getfield) return false; + Bytecodes::Code ret_bc = Bytecodes::java_code_at(code_base()+4); + if (Bytecodes::java_code_at(code_base()+4) != Bytecodes::_areturn && + Bytecodes::java_code_at(code_base()+4) != Bytecodes::_ireturn ) return false; + return true; +} + + +bool methodOopDesc::is_initializer() const { + return name() == vmSymbols::object_initializer_name() || name() == vmSymbols::class_initializer_name(); +} + + +objArrayHandle methodOopDesc::resolved_checked_exceptions_impl(methodOop this_oop, TRAPS) { + int length = this_oop->checked_exceptions_length(); + if (length == 0) { // common case + return objArrayHandle(THREAD, Universe::the_empty_class_klass_array()); + } else { + methodHandle h_this(THREAD, this_oop); + objArrayOop m_oop = oopFactory::new_objArray(SystemDictionary::class_klass(), length, CHECK_(objArrayHandle())); + objArrayHandle mirrors (THREAD, m_oop); + for (int i = 0; i < length; i++) { + CheckedExceptionElement* table = h_this->checked_exceptions_start(); // recompute on each iteration, not gc safe + klassOop k = h_this->constants()->klass_at(table[i].class_cp_index, CHECK_(objArrayHandle())); + assert(Klass::cast(k)->is_subclass_of(SystemDictionary::throwable_klass()), "invalid exception class"); + mirrors->obj_at_put(i, Klass::cast(k)->java_mirror()); + } + return mirrors; + } +}; + + +int methodOopDesc::line_number_from_bci(int bci) const { + if (bci == SynchronizationEntryBCI) bci = 0; + assert(bci == 0 || 0 <= bci && bci < code_size(), "illegal bci"); + int best_bci = 0; + int best_line = -1; + + if (has_linenumber_table()) { + // The line numbers are a short array of 2-tuples [start_pc, line_number]. + // Not necessarily sorted and not necessarily one-to-one. + CompressedLineNumberReadStream stream(compressed_linenumber_table()); + while (stream.read_pair()) { + if (stream.bci() == bci) { + // perfect match + return stream.line(); + } else { + // update best_bci/line + if (stream.bci() < bci && stream.bci() >= best_bci) { + best_bci = stream.bci(); + best_line = stream.line(); + } + } + } + } + return best_line; +} + + +bool methodOopDesc::is_klass_loaded_by_klass_index(int klass_index) const { + if( _constants->tag_at(klass_index).is_unresolved_klass() ) { + Thread *thread = Thread::current(); + symbolHandle klass_name(thread, _constants->klass_name_at(klass_index)); + Handle loader(thread, instanceKlass::cast(method_holder())->class_loader()); + Handle prot (thread, Klass::cast(method_holder())->protection_domain()); + return SystemDictionary::find(klass_name, loader, prot, thread) != NULL; + } else { + return true; + } +} + + +bool methodOopDesc::is_klass_loaded(int refinfo_index, bool must_be_resolved) const { + int klass_index = _constants->klass_ref_index_at(refinfo_index); + if (must_be_resolved) { + // Make sure klass is resolved in constantpool. + if (constants()->tag_at(klass_index).is_unresolved_klass()) return false; + } + return is_klass_loaded_by_klass_index(klass_index); +} + + +void methodOopDesc::set_native_function(address function, bool post_event_flag) { + assert(function != NULL, "use clear_native_function to unregister natives"); + address* native_function = native_function_addr(); + + // We can see racers trying to place the same native function into place. Once + // is plenty. + address current = *native_function; + if (current == function) return; + if (post_event_flag && JvmtiExport::should_post_native_method_bind() && + function != NULL) { + // native_method_throw_unsatisfied_link_error_entry() should only + // be passed when post_event_flag is false. + assert(function != + SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), + "post_event_flag mis-match"); + + // post the bind event, and possible change the bind function + JvmtiExport::post_native_method_bind(this, &function); + } + *native_function = function; + // This function can be called more than once. We must make sure that we always + // use the latest registered method -> check if a stub already has been generated. + // If so, we have to make it not_entrant. + nmethod* nm = code(); // Put it into local variable to guard against concurrent updates + if (nm != NULL) { + nm->make_not_entrant(); + } +} + + +bool methodOopDesc::has_native_function() const { + address func = native_function(); + return (func != NULL && func != SharedRuntime::native_method_throw_unsatisfied_link_error_entry()); +} + + +void methodOopDesc::clear_native_function() { + set_native_function( + SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), + !native_bind_event_is_interesting); + clear_code(); +} + + +void methodOopDesc::set_signature_handler(address handler) { + address* signature_handler = signature_handler_addr(); + *signature_handler = handler; +} + + +bool methodOopDesc::is_not_compilable(int comp_level) const { + methodDataOop mdo = method_data(); + if (mdo != NULL + && (uint)mdo->decompile_count() > (uint)PerMethodRecompilationCutoff) { + // Since (uint)-1 is large, -1 really means 'no cutoff'. + return true; + } +#ifdef COMPILER2 + if (is_tier1_compile(comp_level)) { + if (is_not_tier1_compilable()) { + return true; + } + } +#endif // COMPILER2 + return (_invocation_counter.state() == InvocationCounter::wait_for_nothing) + || (number_of_breakpoints() > 0); +} + +// call this when compiler finds that this method is not compilable +void methodOopDesc::set_not_compilable(int comp_level) { + if ((TraceDeoptimization || LogCompilation) && (xtty != NULL)) { + ttyLocker ttyl; + xtty->begin_elem("make_not_compilable thread='%d'", (int) os::current_thread_id()); + xtty->method(methodOop(this)); + xtty->stamp(); + xtty->end_elem(); + } +#ifdef COMPILER2 + if (is_tier1_compile(comp_level)) { + set_not_tier1_compilable(); + return; + } +#endif /* COMPILER2 */ + assert(comp_level == CompLevel_highest_tier, "unexpected compilation level"); + invocation_counter()->set_state(InvocationCounter::wait_for_nothing); + backedge_counter()->set_state(InvocationCounter::wait_for_nothing); +} + +// Revert to using the interpreter and clear out the nmethod +void methodOopDesc::clear_code() { + + // this may be NULL if c2i adapters have not been made yet + // Only should happen at allocate time. + if (_adapter == NULL) { + _from_compiled_entry = NULL; + } else { + _from_compiled_entry = _adapter->get_c2i_entry(); + } + OrderAccess::storestore(); + _from_interpreted_entry = _i2i_entry; + OrderAccess::storestore(); + _code = NULL; +} + +// Called by class data sharing to remove any entry points (which are not shared) +void methodOopDesc::unlink_method() { + _code = NULL; + _i2i_entry = NULL; + _from_interpreted_entry = NULL; + if (is_native()) { + *native_function_addr() = NULL; + set_signature_handler(NULL); + } + NOT_PRODUCT(set_compiled_invocation_count(0);) + invocation_counter()->reset(); + backedge_counter()->reset(); + _adapter = NULL; + _from_compiled_entry = NULL; + assert(_method_data == NULL, "unexpected method data?"); + set_method_data(NULL); + set_interpreter_throwout_count(0); + set_interpreter_invocation_count(0); + _highest_tier_compile = CompLevel_none; +} + +// Called when the method_holder is getting linked. Setup entrypoints so the method +// is ready to be called from interpreter, compiler, and vtables. +void methodOopDesc::link_method(methodHandle h_method, TRAPS) { + assert(_i2i_entry == NULL, "should only be called once"); + assert(_adapter == NULL, "init'd to NULL" ); + assert( _code == NULL, "nothing compiled yet" ); + + // Setup interpreter entrypoint + assert(this == h_method(), "wrong h_method()" ); + address entry = Interpreter::entry_for_method(h_method); + assert(entry != NULL, "interpreter entry must be non-null"); + // Sets both _i2i_entry and _from_interpreted_entry + set_interpreter_entry(entry); + if (is_native()) { + set_native_function( + SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), + !native_bind_event_is_interesting); + } + + // Setup compiler entrypoint. This is made eagerly, so we do not need + // special handling of vtables. An alternative is to make adapters more + // lazily by calling make_adapter() from from_compiled_entry() for the + // normal calls. For vtable calls life gets more complicated. When a + // call-site goes mega-morphic we need adapters in all methods which can be + // called from the vtable. We need adapters on such methods that get loaded + // later. Ditto for mega-morphic itable calls. If this proves to be a + // problem we'll make these lazily later. + (void) make_adapters(h_method, CHECK); + + // ONLY USE the h_method now as make_adapter may have blocked + +} + +address methodOopDesc::make_adapters(methodHandle mh, TRAPS) { + // If running -Xint we need no adapters. + if (Arguments::mode() == Arguments::_int) return NULL; + + // Adapters for compiled code are made eagerly here. They are fairly + // small (generally < 100 bytes) and quick to make (and cached and shared) + // so making them eagerly shouldn't be too expensive. + AdapterHandlerEntry* adapter = AdapterHandlerLibrary::get_adapter(mh); + if (adapter == NULL ) { + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + + mh->set_adapter_entry(adapter); + mh->_from_compiled_entry = adapter->get_c2i_entry(); + return adapter->get_c2i_entry(); +} + +// The verified_code_entry() must be called when a invoke is resolved +// on this method. + +// It returns the compiled code entry point, after asserting not null. +// This function is called after potential safepoints so that nmethod +// or adapter that it points to is still live and valid. +// This function must not hit a safepoint! +address methodOopDesc::verified_code_entry() { + debug_only(No_Safepoint_Verifier nsv;) + assert(_from_compiled_entry != NULL, "must be set"); + return _from_compiled_entry; +} + +// Check that if an nmethod ref exists, it has a backlink to this or no backlink at all +// (could be racing a deopt). +// Not inline to avoid circular ref. +bool methodOopDesc::check_code() const { + // cached in a register or local. There's a race on the value of the field. + nmethod *code = (nmethod *)OrderAccess::load_ptr_acquire(&_code); + return code == NULL || (code->method() == NULL) || (code->method() == (methodOop)this && !code->is_osr_method()); +} + +// Install compiled code. Instantly it can execute. +void methodOopDesc::set_code(methodHandle mh, nmethod *code) { + assert( code, "use clear_code to remove code" ); + assert( mh->check_code(), "" ); + + guarantee(mh->adapter() != NULL, "Adapter blob must already exist!"); + + // These writes must happen in this order, because the interpreter will + // directly jump to from_interpreted_entry which jumps to an i2c adapter + // which jumps to _from_compiled_entry. + mh->_code = code; // Assign before allowing compiled code to exec + + int comp_level = code->comp_level(); + // In theory there could be a race here. In practice it is unlikely + // and not worth worrying about. + if (comp_level > highest_tier_compile()) { + set_highest_tier_compile(comp_level); + } + + OrderAccess::storestore(); + mh->_from_compiled_entry = code->verified_entry_point(); + OrderAccess::storestore(); + // Instantly compiled code can execute. + mh->_from_interpreted_entry = mh->get_i2c_entry(); + +} + + +bool methodOopDesc::is_overridden_in(klassOop k) const { + instanceKlass* ik = instanceKlass::cast(k); + + if (ik->is_interface()) return false; + + // If method is an interface, we skip it - except if it + // is a miranda method + if (instanceKlass::cast(method_holder())->is_interface()) { + // Check that method is not a miranda method + if (ik->lookup_method(name(), signature()) == NULL) { + // No implementation exist - so miranda method + return false; + } + return true; + } + + assert(ik->is_subclass_of(method_holder()), "should be subklass"); + assert(ik->vtable() != NULL, "vtable should exist"); + if (vtable_index() == nonvirtual_vtable_index) { + return false; + } else { + methodOop vt_m = ik->method_at_vtable(vtable_index()); + return vt_m != methodOop(this); + } +} + + +methodHandle methodOopDesc:: clone_with_new_data(methodHandle m, u_char* new_code, int new_code_length, + u_char* new_compressed_linenumber_table, int new_compressed_linenumber_size, TRAPS) { + // Code below does not work for native methods - they should never get rewritten anyway + assert(!m->is_native(), "cannot rewrite native methods"); + // Allocate new methodOop + AccessFlags flags = m->access_flags(); + int checked_exceptions_len = m->checked_exceptions_length(); + int localvariable_len = m->localvariable_table_length(); + methodOop newm_oop = oopFactory::new_method(new_code_length, flags, new_compressed_linenumber_size, localvariable_len, checked_exceptions_len, CHECK_(methodHandle())); + methodHandle newm (THREAD, newm_oop); + int new_method_size = newm->method_size(); + // Create a shallow copy of methodOopDesc part, but be careful to preserve the new constMethodOop + constMethodOop newcm = newm->constMethod(); + int new_const_method_size = newm->constMethod()->object_size(); + memcpy(newm(), m(), sizeof(methodOopDesc)); + // Create shallow copy of constMethodOopDesc, but be careful to preserve the methodOop + memcpy(newcm, m->constMethod(), sizeof(constMethodOopDesc)); + // Reset correct method/const method, method size, and parameter info + newcm->set_method(newm()); + newm->set_constMethod(newcm); + assert(newcm->method() == newm(), "check"); + newm->constMethod()->set_code_size(new_code_length); + newm->constMethod()->set_constMethod_size(new_const_method_size); + newm->set_method_size(new_method_size); + assert(newm->code_size() == new_code_length, "check"); + assert(newm->checked_exceptions_length() == checked_exceptions_len, "check"); + assert(newm->localvariable_table_length() == localvariable_len, "check"); + // Copy new byte codes + memcpy(newm->code_base(), new_code, new_code_length); + // Copy line number table + if (new_compressed_linenumber_size > 0) { + memcpy(newm->compressed_linenumber_table(), + new_compressed_linenumber_table, + new_compressed_linenumber_size); + } + // Copy checked_exceptions + if (checked_exceptions_len > 0) { + memcpy(newm->checked_exceptions_start(), + m->checked_exceptions_start(), + checked_exceptions_len * sizeof(CheckedExceptionElement)); + } + // Copy local variable number table + if (localvariable_len > 0) { + memcpy(newm->localvariable_table_start(), + m->localvariable_table_start(), + localvariable_len * sizeof(LocalVariableTableElement)); + } + return newm; +} + +vmIntrinsics::ID methodOopDesc::compute_intrinsic_id() const { + assert(vmIntrinsics::_none == 0, "correct coding of default case"); + const uintptr_t max_cache_uint = right_n_bits((int)(sizeof(_intrinsic_id_cache) * BitsPerByte)); + assert((uintptr_t)vmIntrinsics::ID_LIMIT <= max_cache_uint, "else fix cache size"); + // if loader is not the default loader (i.e., != NULL), we can't know the intrinsics + // because we are not loading from core libraries + if (instanceKlass::cast(method_holder())->class_loader() != NULL) return vmIntrinsics::_none; + + // see if the klass name is well-known: + symbolOop klass_name = instanceKlass::cast(method_holder())->name(); + vmSymbols::SID klass_id = vmSymbols::find_sid(klass_name); + if (klass_id == vmSymbols::NO_SID) return vmIntrinsics::_none; + + // ditto for method and signature: + vmSymbols::SID name_id = vmSymbols::find_sid(name()); + if (name_id == vmSymbols::NO_SID) return vmIntrinsics::_none; + vmSymbols::SID sig_id = vmSymbols::find_sid(signature()); + if (sig_id == vmSymbols::NO_SID) return vmIntrinsics::_none; + jshort flags = access_flags().as_short(); + + // A few slightly irregular cases: + switch (klass_id) { + case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_StrictMath): + // Second chance: check in regular Math. + switch (name_id) { + case vmSymbols::VM_SYMBOL_ENUM_NAME(min_name): + case vmSymbols::VM_SYMBOL_ENUM_NAME(max_name): + case vmSymbols::VM_SYMBOL_ENUM_NAME(sqrt_name): + // pretend it is the corresponding method in the non-strict class: + klass_id = vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_Math); + break; + } + } + + // return intrinsic id if any + return vmIntrinsics::find_id(klass_id, name_id, sig_id, flags); +} + + +// These two methods are static since a GC may move the methodOopDesc +bool methodOopDesc::load_signature_classes(methodHandle m, TRAPS) { + bool sig_is_loaded = true; + Handle class_loader(THREAD, instanceKlass::cast(m->method_holder())->class_loader()); + Handle protection_domain(THREAD, Klass::cast(m->method_holder())->protection_domain()); + symbolHandle signature(THREAD, m->signature()); + for(SignatureStream ss(signature); !ss.is_done(); ss.next()) { + if (ss.is_object()) { + symbolOop sym = ss.as_symbol(CHECK_(false)); + symbolHandle name (THREAD, sym); + klassOop klass = SystemDictionary::resolve_or_null(name, class_loader, + protection_domain, THREAD); + // We are loading classes eagerly. If a ClassNotFoundException was generated, + // be sure to ignore it. + if (HAS_PENDING_EXCEPTION) { + if (PENDING_EXCEPTION->is_a(SystemDictionary::classNotFoundException_klass())) { + CLEAR_PENDING_EXCEPTION; + } else { + return false; + } + } + if( klass == NULL) { sig_is_loaded = false; } + } + } + return sig_is_loaded; +} + +bool methodOopDesc::has_unloaded_classes_in_signature(methodHandle m, TRAPS) { + Handle class_loader(THREAD, instanceKlass::cast(m->method_holder())->class_loader()); + Handle protection_domain(THREAD, Klass::cast(m->method_holder())->protection_domain()); + symbolHandle signature(THREAD, m->signature()); + for(SignatureStream ss(signature); !ss.is_done(); ss.next()) { + if (ss.type() == T_OBJECT) { + symbolHandle name(THREAD, ss.as_symbol_or_null()); + if (name() == NULL) return true; + klassOop klass = SystemDictionary::find(name, class_loader, protection_domain, THREAD); + if (klass == NULL) return true; + } + } + return false; +} + +// Exposed so field engineers can debug VM +void methodOopDesc::print_short_name(outputStream* st) { + ResourceMark rm; +#ifdef PRODUCT + st->print(" %s::", method_holder()->klass_part()->external_name()); +#else + st->print(" %s::", method_holder()->klass_part()->internal_name()); +#endif + name()->print_symbol_on(st); + if (WizardMode) signature()->print_symbol_on(st); +} + + +extern "C" { + static int method_compare(methodOop* a, methodOop* b) { + return (*a)->name()->fast_compare((*b)->name()); + } + + // Prevent qsort from reordering a previous valid sort by + // considering the address of the methodOops if two methods + // would otherwise compare as equal. Required to preserve + // optimal access order in the shared archive. Slower than + // method_compare, only used for shared archive creation. + static int method_compare_idempotent(methodOop* a, methodOop* b) { + int i = method_compare(a, b); + if (i != 0) return i; + return ( a < b ? -1 : (a == b ? 0 : 1)); + } + + typedef int (*compareFn)(const void*, const void*); +} + + +// This is only done during class loading, so it is OK to assume method_idnum matches the methods() array +static void reorder_based_on_method_index(objArrayOop methods, + objArrayOop annotations, + oop* temp_array) { + if (annotations == NULL) { + return; + } + + int length = methods->length(); + int i; + // Copy to temp array + memcpy(temp_array, annotations->obj_at_addr(0), length * sizeof(oop)); + + // Copy back using old method indices + for (i = 0; i < length; i++) { + methodOop m = (methodOop) methods->obj_at(i); + annotations->obj_at_put(i, temp_array[m->method_idnum()]); + } +} + + +// This is only done during class loading, so it is OK to assume method_idnum matches the methods() array +void methodOopDesc::sort_methods(objArrayOop methods, + objArrayOop methods_annotations, + objArrayOop methods_parameter_annotations, + objArrayOop methods_default_annotations, + bool idempotent) { + int length = methods->length(); + if (length > 1) { + bool do_annotations = false; + if (methods_annotations != NULL || + methods_parameter_annotations != NULL || + methods_default_annotations != NULL) { + do_annotations = true; + } + if (do_annotations) { + // Remember current method ordering so we can reorder annotations + for (int i = 0; i < length; i++) { + methodOop m = (methodOop) methods->obj_at(i); + m->set_method_idnum(i); + } + } + + // Use a simple bubble sort for small number of methods since + // qsort requires a functional pointer call for each comparison. + if (length < 8) { + bool sorted = true; + for (int i=length-1; i>0; i--) { + for (int j=0; jobj_at(j); + methodOop m2 = (methodOop)methods->obj_at(j+1); + if ((uintptr_t)m1->name() > (uintptr_t)m2->name()) { + methods->obj_at_put(j, m2); + methods->obj_at_put(j+1, m1); + sorted = false; + } + } + if (sorted) break; + sorted = true; + } + } else { + compareFn compare = (compareFn) (idempotent ? method_compare_idempotent : method_compare); + qsort(methods->obj_at_addr(0), length, oopSize, compare); + } + + // Sort annotations if necessary + assert(methods_annotations == NULL || methods_annotations->length() == methods->length(), ""); + assert(methods_parameter_annotations == NULL || methods_parameter_annotations->length() == methods->length(), ""); + assert(methods_default_annotations == NULL || methods_default_annotations->length() == methods->length(), ""); + if (do_annotations) { + // Allocate temporary storage + oop* temp_array = NEW_RESOURCE_ARRAY(oop, length); + reorder_based_on_method_index(methods, methods_annotations, temp_array); + reorder_based_on_method_index(methods, methods_parameter_annotations, temp_array); + reorder_based_on_method_index(methods, methods_default_annotations, temp_array); + } + + // Reset method ordering + for (int i = 0; i < length; i++) { + methodOop m = (methodOop) methods->obj_at(i); + m->set_method_idnum(i); + } + } +} + + +//----------------------------------------------------------------------------------- +// Non-product code + +#ifndef PRODUCT +class SignatureTypePrinter : public SignatureTypeNames { + private: + outputStream* _st; + bool _use_separator; + + void type_name(const char* name) { + if (_use_separator) _st->print(", "); + _st->print(name); + _use_separator = true; + } + + public: + SignatureTypePrinter(symbolHandle signature, outputStream* st) : SignatureTypeNames(signature) { + _st = st; + _use_separator = false; + } + + void print_parameters() { _use_separator = false; iterate_parameters(); } + void print_returntype() { _use_separator = false; iterate_returntype(); } +}; + + +void methodOopDesc::print_name(outputStream* st) { + Thread *thread = Thread::current(); + ResourceMark rm(thread); + SignatureTypePrinter sig(signature(), st); + st->print("%s ", is_static() ? "static" : "virtual"); + sig.print_returntype(); + st->print(" %s.", method_holder()->klass_part()->internal_name()); + name()->print_symbol_on(st); + st->print("("); + sig.print_parameters(); + st->print(")"); +} + + +void methodOopDesc::print_codes_on(outputStream* st) const { + print_codes_on(0, code_size(), st); +} + +void methodOopDesc::print_codes_on(int from, int to, outputStream* st) const { + Thread *thread = Thread::current(); + ResourceMark rm(thread); + methodHandle mh (thread, (methodOop)this); + BytecodeStream s(mh); + s.set_interval(from, to); + BytecodeTracer::set_closure(BytecodeTracer::std_closure()); + while (s.next() >= 0) BytecodeTracer::trace(mh, s.bcp(), st); +} +#endif // not PRODUCT + + +// Simple compression of line number tables. We use a regular compressed stream, except that we compress deltas +// between (bci,line) pairs since they are smaller. If (bci delta, line delta) fits in (5-bit unsigned, 3-bit unsigned) +// we save it as one byte, otherwise we write a 0xFF escape character and use regular compression. 0x0 is used +// as end-of-stream terminator. + +void CompressedLineNumberWriteStream::write_pair_regular(int bci_delta, int line_delta) { + // bci and line number does not compress into single byte. + // Write out escape character and use regular compression for bci and line number. + write_byte((jubyte)0xFF); + write_signed_int(bci_delta); + write_signed_int(line_delta); +} + +// See comment in methodOop.hpp which explains why this exists. +#if defined(_M_AMD64) && MSC_VER >= 1400 +#pragma optimize("", off) +void CompressedLineNumberWriteStream::write_pair(int bci, int line) { + write_pair_inline(bci, line); +} +#pragma optimize("", on) +#endif + +CompressedLineNumberReadStream::CompressedLineNumberReadStream(u_char* buffer) : CompressedReadStream(buffer) { + _bci = 0; + _line = 0; +}; + + +bool CompressedLineNumberReadStream::read_pair() { + jubyte next = read_byte(); + // Check for terminator + if (next == 0) return false; + if (next == 0xFF) { + // Escape character, regular compression used + _bci += read_signed_int(); + _line += read_signed_int(); + } else { + // Single byte compression used + _bci += next >> 3; + _line += next & 0x7; + } + return true; +} + + +Bytecodes::Code methodOopDesc::orig_bytecode_at(int bci) { + BreakpointInfo* bp = instanceKlass::cast(method_holder())->breakpoints(); + for (; bp != NULL; bp = bp->next()) { + if (bp->match(this, bci)) { + return bp->orig_bytecode(); + } + } + ShouldNotReachHere(); + return Bytecodes::_shouldnotreachhere; +} + +void methodOopDesc::set_orig_bytecode_at(int bci, Bytecodes::Code code) { + assert(code != Bytecodes::_breakpoint, "cannot patch breakpoints this way"); + BreakpointInfo* bp = instanceKlass::cast(method_holder())->breakpoints(); + for (; bp != NULL; bp = bp->next()) { + if (bp->match(this, bci)) { + bp->set_orig_bytecode(code); + // and continue, in case there is more than one + } + } +} + +void methodOopDesc::set_breakpoint(int bci) { + instanceKlass* ik = instanceKlass::cast(method_holder()); + BreakpointInfo *bp = new BreakpointInfo(this, bci); + bp->set_next(ik->breakpoints()); + ik->set_breakpoints(bp); + // do this last: + bp->set(this); +} + +static void clear_matches(methodOop m, int bci) { + instanceKlass* ik = instanceKlass::cast(m->method_holder()); + BreakpointInfo* prev_bp = NULL; + BreakpointInfo* next_bp; + for (BreakpointInfo* bp = ik->breakpoints(); bp != NULL; bp = next_bp) { + next_bp = bp->next(); + // bci value of -1 is used to delete all breakpoints in method m (ex: clear_all_breakpoint). + if (bci >= 0 ? bp->match(m, bci) : bp->match(m)) { + // do this first: + bp->clear(m); + // unhook it + if (prev_bp != NULL) + prev_bp->set_next(next_bp); + else + ik->set_breakpoints(next_bp); + delete bp; + // When class is redefined JVMTI sets breakpoint in all versions of EMCP methods + // at same location. So we have multiple matching (method_index and bci) + // BreakpointInfo nodes in BreakpointInfo list. We should just delete one + // breakpoint for clear_breakpoint request and keep all other method versions + // BreakpointInfo for future clear_breakpoint request. + // bcivalue of -1 is used to clear all breakpoints (see clear_all_breakpoints) + // which is being called when class is unloaded. We delete all the Breakpoint + // information for all versions of method. We may not correctly restore the original + // bytecode in all method versions, but that is ok. Because the class is being unloaded + // so these methods won't be used anymore. + if (bci >= 0) { + break; + } + } else { + // This one is a keeper. + prev_bp = bp; + } + } +} + +void methodOopDesc::clear_breakpoint(int bci) { + assert(bci >= 0, ""); + clear_matches(this, bci); +} + +void methodOopDesc::clear_all_breakpoints() { + clear_matches(this, -1); +} + + +BreakpointInfo::BreakpointInfo(methodOop m, int bci) { + _bci = bci; + _name_index = m->name_index(); + _signature_index = m->signature_index(); + _orig_bytecode = (Bytecodes::Code) *m->bcp_from(_bci); + if (_orig_bytecode == Bytecodes::_breakpoint) + _orig_bytecode = m->orig_bytecode_at(_bci); + _next = NULL; +} + +void BreakpointInfo::set(methodOop method) { +#ifdef ASSERT + { + Bytecodes::Code code = (Bytecodes::Code) *method->bcp_from(_bci); + if (code == Bytecodes::_breakpoint) + code = method->orig_bytecode_at(_bci); + assert(orig_bytecode() == code, "original bytecode must be the same"); + } +#endif + *method->bcp_from(_bci) = Bytecodes::_breakpoint; + method->incr_number_of_breakpoints(); + SystemDictionary::notice_modification(); + { + // Deoptimize all dependents on this method + Thread *thread = Thread::current(); + HandleMark hm(thread); + methodHandle mh(thread, method); + Universe::flush_dependents_on_method(mh); + } +} + +void BreakpointInfo::clear(methodOop method) { + *method->bcp_from(_bci) = orig_bytecode(); + assert(method->number_of_breakpoints() > 0, "must not go negative"); + method->decr_number_of_breakpoints(); +} diff --git a/hotspot/src/share/vm/oops/methodOop.hpp b/hotspot/src/share/vm/oops/methodOop.hpp new file mode 100644 index 00000000000..ec3d6b5e5ad --- /dev/null +++ b/hotspot/src/share/vm/oops/methodOop.hpp @@ -0,0 +1,739 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A methodOop represents a Java method. +// +// Memory layout (each line represents a word). Note that most applications load thousands of methods, +// so keeping the size of this structure small has a big impact on footprint. +// +// We put all oops and method_size first for better gc cache locality. +// +// The actual bytecodes are inlined after the end of the methodOopDesc struct. +// +// There are bits in the access_flags telling whether inlined tables are present. +// Note that accessing the line number and local variable tables is not performance critical at all. +// Accessing the checked exceptions table is used by reflection, so we put that last to make access +// to it fast. +// +// The line number table is compressed and inlined following the byte codes. It is found as the first +// byte following the byte codes. The checked exceptions table and the local variable table are inlined +// after the line number table, and indexed from the end of the method. We do not compress the checked +// exceptions table since the average length is less than 2, and do not bother to compress the local +// variable table either since it is mostly absent. +// +// Note that native_function and signature_handler has to be at fixed offsets (required by the interpreter) +// +// |------------------------------------------------------| +// | header | +// | klass | +// |------------------------------------------------------| +// | constMethodOop (oop) | +// | constants (oop) | +// |------------------------------------------------------| +// | methodData (oop) | +// | interp_invocation_count | +// |------------------------------------------------------| +// | access_flags | +// | vtable_index | +// |------------------------------------------------------| +// | result_index (C++ interpreter only) | +// |------------------------------------------------------| +// | method_size | max_stack | +// | max_locals | size_of_parameters | +// |------------------------------------------------------| +// | intrinsic_id, highest_tier | (unused) | +// |------------------------------------------------------| +// | throwout_count | num_breakpoints | +// |------------------------------------------------------| +// | invocation_counter | +// | backedge_counter | +// |------------------------------------------------------| +// | code (pointer) | +// | i2i (pointer) | +// | adapter (pointer) | +// | from_compiled_entry (pointer) | +// | from_interpreted_entry (pointer) | +// |------------------------------------------------------| +// | native_function (present only if native) | +// | signature_handler (present only if native) | +// |------------------------------------------------------| + + +class CheckedExceptionElement; +class LocalVariableTableElement; +class AdapterHandlerEntry; + +class methodDataOopDesc; + +class methodOopDesc : public oopDesc { + friend class methodKlass; + friend class VMStructs; + private: + constMethodOop _constMethod; // Method read-only data. + constantPoolOop _constants; // Constant pool + methodDataOop _method_data; + int _interpreter_invocation_count; // Count of times invoked + AccessFlags _access_flags; // Access flags + int _vtable_index; // vtable index of this method (see VtableIndexFlag) + // note: can have vtables with >2**16 elements (because of inheritance) +#ifdef CC_INTERP + int _result_index; // C++ interpreter needs for converting results to/from stack +#endif + u2 _method_size; // size of this object + u2 _max_stack; // Maximum number of entries on the expression stack + u2 _max_locals; // Number of local variables used by this method + u2 _size_of_parameters; // size of the parameter block (receiver + arguments) in words + u1 _intrinsic_id_cache; // Cache for intrinsic_id; 0 or 1+vmInt::ID + u1 _highest_tier_compile; // Highest compile level this method has ever seen. + u2 _interpreter_throwout_count; // Count of times method was exited via exception while interpreting + u2 _number_of_breakpoints; // fullspeed debugging support + InvocationCounter _invocation_counter; // Incremented before each activation of the method - used to trigger frequency-based optimizations + InvocationCounter _backedge_counter; // Incremented before each backedge taken - used to trigger frequencey-based optimizations +#ifndef PRODUCT + int _compiled_invocation_count; // Number of nmethod invocations so far (for perf. debugging) +#endif + // Entry point for calling both from and to the interpreter. + address _i2i_entry; // All-args-on-stack calling convention + // Adapter blob (i2c/c2i) for this methodOop. Set once when method is linked. + AdapterHandlerEntry* _adapter; + // Entry point for calling from compiled code, to compiled code if it exists + // or else the interpreter. + volatile address _from_compiled_entry; // Cache of: _code ? _code->entry_point() : _adapter->c2i_entry() + // The entry point for calling both from and to compiled code is + // "_code->entry_point()". Because of tiered compilation and de-opt, this + // field can come and go. It can transition from NULL to not-null at any + // time (whenever a compile completes). It can transition from not-null to + // NULL only at safepoints (because of a de-opt). + nmethod* volatile _code; // Points to the corresponding piece of native code + volatile address _from_interpreted_entry; // Cache of _code ? _adapter->i2c_entry() : _i2i_entry + + public: + // accessors for instance variables + constMethodOop constMethod() const { return _constMethod; } + void set_constMethod(constMethodOop xconst) { oop_store_without_check((oop*)&_constMethod, (oop)xconst); } + + + static address make_adapters(methodHandle mh, TRAPS); + volatile address from_compiled_entry() const { return (address)OrderAccess::load_ptr_acquire(&_from_compiled_entry); } + volatile address from_interpreted_entry() const{ return (address)OrderAccess::load_ptr_acquire(&_from_interpreted_entry); } + + // access flag + AccessFlags access_flags() const { return _access_flags; } + void set_access_flags(AccessFlags flags) { _access_flags = flags; } + + // name + symbolOop name() const { return _constants->symbol_at(name_index()); } + int name_index() const { return constMethod()->name_index(); } + void set_name_index(int index) { constMethod()->set_name_index(index); } + + // signature + symbolOop signature() const { return _constants->symbol_at(signature_index()); } + int signature_index() const { return constMethod()->signature_index(); } + void set_signature_index(int index) { constMethod()->set_signature_index(index); } + + // generics support + symbolOop generic_signature() const { int idx = generic_signature_index(); return ((idx != 0) ? _constants->symbol_at(idx) : (symbolOop)NULL); } + int generic_signature_index() const { return constMethod()->generic_signature_index(); } + void set_generic_signature_index(int index) { constMethod()->set_generic_signature_index(index); } + + // annotations support + typeArrayOop annotations() const { return instanceKlass::cast(method_holder())->get_method_annotations_of(method_idnum()); } + typeArrayOop parameter_annotations() const { return instanceKlass::cast(method_holder())->get_method_parameter_annotations_of(method_idnum()); } + typeArrayOop annotation_default() const { return instanceKlass::cast(method_holder())->get_method_default_annotations_of(method_idnum()); } + +#ifdef CC_INTERP + void set_result_index(BasicType type); + int result_index() { return _result_index; } +#endif + + // Helper routine: get klass name + "." + method name + signature as + // C string, for the purpose of providing more useful NoSuchMethodErrors + // and fatal error handling. The string is allocated in resource + // area if a buffer is not provided by the caller. + char* name_and_sig_as_C_string(); + char* name_and_sig_as_C_string(char* buf, int size); + + // Static routine in the situations we don't have a methodOop + static char* name_and_sig_as_C_string(Klass* klass, symbolOop method_name, symbolOop signature); + static char* name_and_sig_as_C_string(Klass* klass, symbolOop method_name, symbolOop signature, char* buf, int size); + + // JVMTI breakpoints + Bytecodes::Code orig_bytecode_at(int bci); + void set_orig_bytecode_at(int bci, Bytecodes::Code code); + void set_breakpoint(int bci); + void clear_breakpoint(int bci); + void clear_all_breakpoints(); + // Tracking number of breakpoints, for fullspeed debugging. + // Only mutated by VM thread. + u2 number_of_breakpoints() const { return _number_of_breakpoints; } + void incr_number_of_breakpoints() { ++_number_of_breakpoints; } + void decr_number_of_breakpoints() { --_number_of_breakpoints; } + // Initialization only + void clear_number_of_breakpoints() { _number_of_breakpoints = 0; } + + // index into instanceKlass methods() array + u2 method_idnum() const { return constMethod()->method_idnum(); } + void set_method_idnum(u2 idnum) { constMethod()->set_method_idnum(idnum); } + + // code size + int code_size() const { return constMethod()->code_size(); } + + // method size + int method_size() const { return _method_size; } + void set_method_size(int size) { + assert(0 <= size && size < (1 << 16), "invalid method size"); + _method_size = size; + } + + // constant pool for klassOop holding this method + constantPoolOop constants() const { return _constants; } + void set_constants(constantPoolOop c) { oop_store_without_check((oop*)&_constants, c); } + + // max stack + int max_stack() const { return _max_stack; } + void set_max_stack(int size) { _max_stack = size; } + + // max locals + int max_locals() const { return _max_locals; } + void set_max_locals(int size) { _max_locals = size; } + int highest_tier_compile() { return _highest_tier_compile;} + void set_highest_tier_compile(int level) { _highest_tier_compile = level;} + + void clear_intrinsic_id_cache() { _intrinsic_id_cache = 0; } + + // Count of times method was exited via exception while interpreting + void interpreter_throwout_increment() { + if (_interpreter_throwout_count < 65534) { + _interpreter_throwout_count++; + } + } + + int interpreter_throwout_count() const { return _interpreter_throwout_count; } + void set_interpreter_throwout_count(int count) { _interpreter_throwout_count = count; } + + // size of parameters + int size_of_parameters() const { return _size_of_parameters; } + + bool has_stackmap_table() const { + return constMethod()->has_stackmap_table(); + } + + typeArrayOop stackmap_data() const { + return constMethod()->stackmap_data(); + } + + // exception handler table + typeArrayOop exception_table() const + { return constMethod()->exception_table(); } + void set_exception_table(typeArrayOop e) + { constMethod()->set_exception_table(e); } + bool has_exception_handler() const + { return constMethod()->has_exception_handler(); } + + // Finds the first entry point bci of an exception handler for an + // exception of klass ex_klass thrown at throw_bci. A value of NULL + // for ex_klass indicates that the exception klass is not known; in + // this case it matches any constraint class. Returns -1 if the + // exception cannot be handled in this method. The handler + // constraint classes are loaded if necessary. Note that this may + // throw an exception if loading of the constraint classes causes + // an IllegalAccessError (bugid 4307310) or an OutOfMemoryError. + // If an exception is thrown, returns the bci of the + // exception handler which caused the exception to be thrown, which + // is needed for proper retries. See, for example, + // InterpreterRuntime::exception_handler_for_exception. + int fast_exception_handler_bci_for(KlassHandle ex_klass, int throw_bci, TRAPS); + + // method data access + methodDataOop method_data() const { + return _method_data; + } + void set_method_data(methodDataOop data) { + oop_store_without_check((oop*)&_method_data, (oop)data); + } + + // invocation counter + InvocationCounter* invocation_counter() { return &_invocation_counter; } + InvocationCounter* backedge_counter() { return &_backedge_counter; } + int invocation_count() const { return _invocation_counter.count(); } + int backedge_count() const { return _backedge_counter.count(); } + bool was_executed_more_than(int n) const; + bool was_never_executed() const { return !was_executed_more_than(0); } + + static void build_interpreter_method_data(methodHandle method, TRAPS); + + int interpreter_invocation_count() const { return _interpreter_invocation_count; } + void set_interpreter_invocation_count(int count) { _interpreter_invocation_count = count; } + int increment_interpreter_invocation_count() { return ++_interpreter_invocation_count; } + +#ifndef PRODUCT + int compiled_invocation_count() const { return _compiled_invocation_count; } + void set_compiled_invocation_count(int count) { _compiled_invocation_count = count; } +#endif // not PRODUCT + + // Clear (non-shared space) pointers which could not be relevent + // if this (shared) method were mapped into another JVM. + void remove_unshareable_info(); + + // nmethod/verified compiler entry + address verified_code_entry(); + bool check_code() const; // Not inline to avoid circular ref + nmethod* volatile code() const { assert( check_code(), "" ); return (nmethod *)OrderAccess::load_ptr_acquire(&_code); } + void clear_code(); // Clear out any compiled code + void set_code(methodHandle mh, nmethod* code); + void set_adapter_entry(AdapterHandlerEntry* adapter) { _adapter = adapter; } + address get_i2c_entry(); + address get_c2i_entry(); + address get_c2i_unverified_entry(); + AdapterHandlerEntry* adapter() { return _adapter; } + // setup entry points + void link_method(methodHandle method, TRAPS); + // clear entry points. Used by sharing code + void unlink_method(); + + // vtable index + enum VtableIndexFlag { + // Valid vtable indexes are non-negative (>= 0). + // These few negative values are used as sentinels. + invalid_vtable_index = -4, // distinct from any valid vtable index + garbage_vtable_index = -3, // not yet linked; no vtable layout yet + nonvirtual_vtable_index = -2 // there is no need for vtable dispatch + // 6330203 Note: Do not use -1, which was overloaded with many meanings. + }; + DEBUG_ONLY(bool valid_vtable_index() const { return _vtable_index >= nonvirtual_vtable_index; }) + int vtable_index() const { assert(valid_vtable_index(), ""); + return _vtable_index; } + void set_vtable_index(int index) { _vtable_index = index; } + + // interpreter entry + address interpreter_entry() const { return _i2i_entry; } + // Only used when first initialize so we can set _i2i_entry and _from_interpreted_entry + void set_interpreter_entry(address entry) { _i2i_entry = entry; _from_interpreted_entry = entry; } + int interpreter_kind(void) { + return constMethod()->interpreter_kind(); + } + void set_interpreter_kind(); + void set_interpreter_kind(int kind) { + constMethod()->set_interpreter_kind(kind); + } + + // native function (used for native methods only) + enum { + native_bind_event_is_interesting = true + }; + address native_function() const { return *(native_function_addr()); } + // Must specify a real function (not NULL). + // Use clear_native_function() to unregister. + void set_native_function(address function, bool post_event_flag); + bool has_native_function() const; + void clear_native_function(); + + // signature handler (used for native methods only) + address signature_handler() const { return *(signature_handler_addr()); } + void set_signature_handler(address handler); + + // Interpreter oopmap support + void mask_for(int bci, InterpreterOopMap* mask); + +#ifndef PRODUCT + // operations on invocation counter + void print_invocation_count() const; +#endif + + // byte codes + address code_base() const { return constMethod()->code_base(); } + bool contains(address bcp) const { return constMethod()->contains(bcp); } + + // prints byte codes + void print_codes() const { print_codes_on(tty); } + void print_codes_on(outputStream* st) const PRODUCT_RETURN; + void print_codes_on(int from, int to, outputStream* st) const PRODUCT_RETURN; + + // checked exceptions + int checked_exceptions_length() const + { return constMethod()->checked_exceptions_length(); } + CheckedExceptionElement* checked_exceptions_start() const + { return constMethod()->checked_exceptions_start(); } + + // localvariable table + bool has_localvariable_table() const + { return constMethod()->has_localvariable_table(); } + int localvariable_table_length() const + { return constMethod()->localvariable_table_length(); } + LocalVariableTableElement* localvariable_table_start() const + { return constMethod()->localvariable_table_start(); } + + bool has_linenumber_table() const + { return constMethod()->has_linenumber_table(); } + u_char* compressed_linenumber_table() const + { return constMethod()->compressed_linenumber_table(); } + + // method holder (the klassOop holding this method) + klassOop method_holder() const { return _constants->pool_holder(); } + + void compute_size_of_parameters(Thread *thread); // word size of parameters (receiver if any + arguments) + symbolOop klass_name() const; // returns the name of the method holder + BasicType result_type() const; // type of the method result + int result_type_index() const; // type index of the method result + bool is_returning_oop() const { BasicType r = result_type(); return (r == T_OBJECT || r == T_ARRAY); } + bool is_returning_fp() const { BasicType r = result_type(); return (r == T_FLOAT || r == T_DOUBLE); } + + // Checked exceptions thrown by this method (resolved to mirrors) + objArrayHandle resolved_checked_exceptions(TRAPS) { return resolved_checked_exceptions_impl(this, THREAD); } + + // Access flags + bool is_public() const { return access_flags().is_public(); } + bool is_private() const { return access_flags().is_private(); } + bool is_protected() const { return access_flags().is_protected(); } + bool is_package_private() const { return !is_public() && !is_private() && !is_protected(); } + bool is_static() const { return access_flags().is_static(); } + bool is_final() const { return access_flags().is_final(); } + bool is_synchronized() const { return access_flags().is_synchronized();} + bool is_native() const { return access_flags().is_native(); } + bool is_abstract() const { return access_flags().is_abstract(); } + bool is_strict() const { return access_flags().is_strict(); } + bool is_synthetic() const { return access_flags().is_synthetic(); } + + // returns true if contains only return operation + bool is_empty_method() const; + + // returns true if this is a vanilla constructor + bool is_vanilla_constructor() const; + + // checks method and its method holder + bool is_final_method() const; + bool is_strict_method() const; + + // true if method needs no dynamic dispatch (final and/or no vtable entry) + bool can_be_statically_bound() const; + + // returns true if the method has any backward branches. + bool has_loops() { + return access_flags().loops_flag_init() ? access_flags().has_loops() : compute_has_loops_flag(); + }; + + bool compute_has_loops_flag(); + + bool has_jsrs() { + return access_flags().has_jsrs(); + }; + void set_has_jsrs() { + _access_flags.set_has_jsrs(); + } + + // returns true if the method has any monitors. + bool has_monitors() const { return is_synchronized() || access_flags().has_monitor_bytecodes(); } + bool has_monitor_bytecodes() const { return access_flags().has_monitor_bytecodes(); } + + void set_has_monitor_bytecodes() { _access_flags.set_has_monitor_bytecodes(); } + + // monitor matching. This returns a conservative estimate of whether the monitorenter/monitorexit bytecodes + // propererly nest in the method. It might return false, even though they actually nest properly, since the info. + // has not been computed yet. + bool guaranteed_monitor_matching() const { return access_flags().is_monitor_matching(); } + void set_guaranteed_monitor_matching() { _access_flags.set_monitor_matching(); } + + // returns true if the method is an accessor function (setter/getter). + bool is_accessor() const; + + // returns true if the method is an initializer ( or ). + bool is_initializer() const; + + // compiled code support + // NOTE: code() is inherently racy as deopt can be clearing code + // simultaneously. Use with caution. + bool has_compiled_code() const { return code() != NULL; } + + // sizing + static int object_size(bool is_native); + static int header_size() { return sizeof(methodOopDesc)/HeapWordSize; } + int object_size() const { return method_size(); } + + bool object_is_parsable() const { return method_size() > 0; } + + // interpreter support + static ByteSize const_offset() { return byte_offset_of(methodOopDesc, _constMethod ); } + static ByteSize constants_offset() { return byte_offset_of(methodOopDesc, _constants ); } + static ByteSize access_flags_offset() { return byte_offset_of(methodOopDesc, _access_flags ); } +#ifdef CC_INTERP + static ByteSize result_index_offset() { return byte_offset_of(methodOopDesc, _result_index ); } +#endif /* CC_INTERP */ + static ByteSize size_of_locals_offset() { return byte_offset_of(methodOopDesc, _max_locals ); } + static ByteSize size_of_parameters_offset() { return byte_offset_of(methodOopDesc, _size_of_parameters); } + static ByteSize from_compiled_offset() { return byte_offset_of(methodOopDesc, _from_compiled_entry); } + static ByteSize code_offset() { return byte_offset_of(methodOopDesc, _code); } + static ByteSize invocation_counter_offset() { return byte_offset_of(methodOopDesc, _invocation_counter); } + static ByteSize backedge_counter_offset() { return byte_offset_of(methodOopDesc, _backedge_counter); } + static ByteSize method_data_offset() { + return byte_offset_of(methodOopDesc, _method_data); + } + static ByteSize interpreter_invocation_counter_offset() { return byte_offset_of(methodOopDesc, _interpreter_invocation_count); } +#ifndef PRODUCT + static ByteSize compiled_invocation_counter_offset() { return byte_offset_of(methodOopDesc, _compiled_invocation_count); } +#endif // not PRODUCT + static ByteSize native_function_offset() { return in_ByteSize(sizeof(methodOopDesc)); } + static ByteSize from_interpreted_offset() { return byte_offset_of(methodOopDesc, _from_interpreted_entry ); } + static ByteSize interpreter_entry_offset() { return byte_offset_of(methodOopDesc, _i2i_entry ); } + static ByteSize signature_handler_offset() { return in_ByteSize(sizeof(methodOopDesc) + wordSize); } + static ByteSize max_stack_offset() { return byte_offset_of(methodOopDesc, _max_stack ); } + + // for code generation + static int method_data_offset_in_bytes() { return offset_of(methodOopDesc, _method_data); } + static int interpreter_invocation_counter_offset_in_bytes() + { return offset_of(methodOopDesc, _interpreter_invocation_count); } + + // Static methods that are used to implement member methods where an exposed this pointer + // is needed due to possible GCs + static objArrayHandle resolved_checked_exceptions_impl(methodOop this_oop, TRAPS); + + // Returns the byte code index from the byte code pointer + int bci_from(address bcp) const; + address bcp_from(int bci) const; + int validate_bci_from_bcx(intptr_t bcx) const; + + // Returns the line number for a bci if debugging information for the method is prowided, + // -1 is returned otherwise. + int line_number_from_bci(int bci) const; + + // Reflection support + bool is_overridden_in(klassOop k) const; + + // RedefineClasses() support: + bool is_old() const { return access_flags().is_old(); } + void set_is_old() { _access_flags.set_is_old(); } + bool is_obsolete() const { return access_flags().is_obsolete(); } + void set_is_obsolete() { _access_flags.set_is_obsolete(); } + + // JVMTI Native method prefixing support: + bool is_prefixed_native() const { return access_flags().is_prefixed_native(); } + void set_is_prefixed_native() { _access_flags.set_is_prefixed_native(); } + + // Rewriting support + static methodHandle clone_with_new_data(methodHandle m, u_char* new_code, int new_code_length, + u_char* new_compressed_linenumber_table, int new_compressed_linenumber_size, TRAPS); + + // Get this method's jmethodID -- allocate if it doesn't exist + jmethodID jmethod_id() { methodHandle this_h(this); + return instanceKlass::jmethod_id_for_impl(method_holder(), this_h); } + + // Lookup the jmethodID for this method. Return NULL if not found. + // NOTE that this function can be called from a signal handler + // (see AsyncGetCallTrace support for Forte Analyzer) and this + // needs to be async-safe. No allocation should be done and + // so handles are not used to avoid deadlock. + jmethodID find_jmethod_id_or_null() { return instanceKlass::cast(method_holder())->jmethod_id_or_null(this); } + + // JNI static invoke cached itable index accessors + int cached_itable_index() { return instanceKlass::cast(method_holder())->cached_itable_index(method_idnum()); } + void set_cached_itable_index(int index) { instanceKlass::cast(method_holder())->set_cached_itable_index(method_idnum(), index); } + + // Support for inlining of intrinsic methods + vmIntrinsics::ID intrinsic_id() const { // returns zero if not an intrinsic + const u1& cache = _intrinsic_id_cache; + if (cache != 0) { + return (vmIntrinsics::ID)(cache - 1); + } else { + vmIntrinsics::ID id = compute_intrinsic_id(); + *(u1*)&cache = ((u1) id) + 1; // force the cache to be non-const + vmIntrinsics::verify_method(id, (methodOop) this); + assert((vmIntrinsics::ID)(cache - 1) == id, "proper conversion"); + return id; + } + } + + // On-stack replacement support + bool has_osr_nmethod() { return instanceKlass::cast(method_holder())->lookup_osr_nmethod(this, InvocationEntryBci) != NULL; } + nmethod* lookup_osr_nmethod_for(int bci) { return instanceKlass::cast(method_holder())->lookup_osr_nmethod(this, bci); } + + // Inline cache support + void cleanup_inline_caches(); + + // Find if klass for method is loaded + bool is_klass_loaded_by_klass_index(int klass_index) const; + bool is_klass_loaded(int refinfo_index, bool must_be_resolved = false) const; + + // Indicates whether compilation failed earlier for this method, or + // whether it is not compilable for another reason like having a + // breakpoint set in it. + bool is_not_compilable(int comp_level = CompLevel_highest_tier) const; + void set_not_compilable(int comp_level = CompLevel_highest_tier); + + bool is_not_osr_compilable() const { return is_not_compilable() || access_flags().is_not_osr_compilable(); } + void set_not_osr_compilable() { _access_flags.set_not_osr_compilable(); } + + bool is_not_tier1_compilable() const { return access_flags().is_not_tier1_compilable(); } + void set_not_tier1_compilable() { _access_flags.set_not_tier1_compilable(); } + + // Background compilation support + bool queued_for_compilation() const { return access_flags().queued_for_compilation(); } + void set_queued_for_compilation() { _access_flags.set_queued_for_compilation(); } + void clear_queued_for_compilation() { _access_flags.clear_queued_for_compilation(); } + + static methodOop method_from_bcp(address bcp); + + // Resolve all classes in signature, return 'true' if successful + static bool load_signature_classes(methodHandle m, TRAPS); + + // Return if true if not all classes references in signature, including return type, has been loaded + static bool has_unloaded_classes_in_signature(methodHandle m, TRAPS); + + // Printing + void print_short_name(outputStream* st) /*PRODUCT_RETURN*/; // prints as klassname::methodname; Exposed so field engineers can debug VM + void print_name(outputStream* st) PRODUCT_RETURN; // prints as "virtual void foo(int)" + + // Helper routine used for method sorting + static void sort_methods(objArrayOop methods, + objArrayOop methods_annotations, + objArrayOop methods_parameter_annotations, + objArrayOop methods_default_annotations, + bool idempotent = false); + + // size of parameters + void set_size_of_parameters(int size) { _size_of_parameters = size; } + private: + + // Helper routine for intrinsic_id(). + vmIntrinsics::ID compute_intrinsic_id() const; + + // Inlined elements + address* native_function_addr() const { assert(is_native(), "must be native"); return (address*) (this+1); } + address* signature_handler_addr() const { return native_function_addr() + 1; } + + // Garbage collection support + oop* adr_constMethod() const { return (oop*)&_constMethod; } + oop* adr_constants() const { return (oop*)&_constants; } + oop* adr_method_data() const { return (oop*)&_method_data; } +}; + + +// Utility class for compressing line number tables + +class CompressedLineNumberWriteStream: public CompressedWriteStream { + private: + int _bci; + int _line; + public: + // Constructor + CompressedLineNumberWriteStream(int initial_size) : CompressedWriteStream(initial_size), _bci(0), _line(0) {} + CompressedLineNumberWriteStream(u_char* buffer, int initial_size) : CompressedWriteStream(buffer, initial_size), _bci(0), _line(0) {} + + // Write (bci, line number) pair to stream + void write_pair_regular(int bci_delta, int line_delta); + + inline void write_pair_inline(int bci, int line) { + int bci_delta = bci - _bci; + int line_delta = line - _line; + _bci = bci; + _line = line; + // Skip (0,0) deltas - they do not add information and conflict with terminator. + if (bci_delta == 0 && line_delta == 0) return; + // Check if bci is 5-bit and line number 3-bit unsigned. + if (((bci_delta & ~0x1F) == 0) && ((line_delta & ~0x7) == 0)) { + // Compress into single byte. + jubyte value = ((jubyte) bci_delta << 3) | (jubyte) line_delta; + // Check that value doesn't match escape character. + if (value != 0xFF) { + write_byte(value); + return; + } + } + write_pair_regular(bci_delta, line_delta); + } + +// Windows AMD64 + Apr 2005 PSDK with /O2 generates bad code for write_pair. +// Disabling optimization doesn't work for methods in header files +// so we force it to call through the non-optimized version in the .cpp. +// It's gross, but it's the only way we can ensure that all callers are +// fixed. MSC_VER is defined in build/windows/makefiles/compile.make. +#if defined(_M_AMD64) && MSC_VER >= 1400 + void write_pair(int bci, int line); +#else + void write_pair(int bci, int line) { write_pair_inline(bci, line); } +#endif + + // Write end-of-stream marker + void write_terminator() { write_byte(0); } +}; + + +// Utility class for decompressing line number tables + +class CompressedLineNumberReadStream: public CompressedReadStream { + private: + int _bci; + int _line; + public: + // Constructor + CompressedLineNumberReadStream(u_char* buffer); + // Read (bci, line number) pair from stream. Returns false at end-of-stream. + bool read_pair(); + // Accessing bci and line number (after calling read_pair) + int bci() const { return _bci; } + int line() const { return _line; } +}; + + +/// Fast Breakpoints. + +// If this structure gets more complicated (because bpts get numerous), +// move it into its own header. + +// There is presently no provision for concurrent access +// to breakpoint lists, which is only OK for JVMTI because +// breakpoints are written only at safepoints, and are read +// concurrently only outside of safepoints. + +class BreakpointInfo : public CHeapObj { + friend class VMStructs; + private: + Bytecodes::Code _orig_bytecode; + int _bci; + u2 _name_index; // of method + u2 _signature_index; // of method + BreakpointInfo* _next; // simple storage allocation + + public: + BreakpointInfo(methodOop m, int bci); + + // accessors + Bytecodes::Code orig_bytecode() { return _orig_bytecode; } + void set_orig_bytecode(Bytecodes::Code code) { _orig_bytecode = code; } + int bci() { return _bci; } + + BreakpointInfo* next() const { return _next; } + void set_next(BreakpointInfo* n) { _next = n; } + + // helps for searchers + bool match(methodOop m, int bci) { + return bci == _bci && match(m); + } + + bool match(methodOop m) { + return _name_index == m->name_index() && + _signature_index == m->signature_index(); + } + + void set(methodOop method); + void clear(methodOop method); +}; diff --git a/hotspot/src/share/vm/oops/objArrayKlass.cpp b/hotspot/src/share/vm/oops/objArrayKlass.cpp new file mode 100644 index 00000000000..e83ecd7bf71 --- /dev/null +++ b/hotspot/src/share/vm/oops/objArrayKlass.cpp @@ -0,0 +1,511 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_objArrayKlass.cpp.incl" + +int objArrayKlass::oop_size(oop obj) const { + assert(obj->is_objArray(), "must be object array"); + return objArrayOop(obj)->object_size(); +} + +objArrayOop objArrayKlass::allocate(int length, TRAPS) { + if (length >= 0) { + if (length <= arrayOopDesc::max_array_length(T_OBJECT)) { + int size = objArrayOopDesc::object_size(length); + KlassHandle h_k(THREAD, as_klassOop()); + objArrayOop a = (objArrayOop)CollectedHeap::array_allocate(h_k, size, length, CHECK_NULL); + assert(a->is_parsable(), "Can't publish unless parsable"); + return a; + } else { + THROW_OOP_0(Universe::out_of_memory_error_array_size()); + } + } else { + THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); + } +} + +static int multi_alloc_counter = 0; + +oop objArrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) { + int length = *sizes; + // Call to lower_dimension uses this pointer, so most be called before a + // possible GC + KlassHandle h_lower_dimension(THREAD, lower_dimension()); + // If length < 0 allocate will throw an exception. + objArrayOop array = allocate(length, CHECK_NULL); + assert(array->is_parsable(), "Don't handlize unless parsable"); + objArrayHandle h_array (THREAD, array); + if (rank > 1) { + if (length != 0) { + for (int index = 0; index < length; index++) { + arrayKlass* ak = arrayKlass::cast(h_lower_dimension()); + oop sub_array = ak->multi_allocate(rank-1, &sizes[1], CHECK_NULL); + assert(sub_array->is_parsable(), "Don't publish until parsable"); + h_array->obj_at_put(index, sub_array); + } + } else { + // Since this array dimension has zero length, nothing will be + // allocated, however the lower dimension values must be checked + // for illegal values. + for (int i = 0; i < rank - 1; ++i) { + sizes += 1; + if (*sizes < 0) { + THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); + } + } + } + } + return h_array(); +} + +void objArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, + int dst_pos, int length, TRAPS) { + assert(s->is_objArray(), "must be obj array"); + + if (!d->is_objArray()) { + THROW(vmSymbols::java_lang_ArrayStoreException()); + } + + // Check is all offsets and lengths are non negative + if (src_pos < 0 || dst_pos < 0 || length < 0) { + THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + } + // Check if the ranges are valid + if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length()) + || (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) { + THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + } + + // Special case. Boundary cases must be checked first + // This allows the following call: copy_array(s, s.length(), d.length(), 0). + // This is correct, since the position is supposed to be an 'in between point', i.e., s.length(), + // points to the right of the last element. + if (length==0) { + return; + } + + oop* const src = objArrayOop(s)->obj_at_addr(src_pos); + oop* const dst = objArrayOop(d)->obj_at_addr(dst_pos); + const size_t word_len = length * HeapWordsPerOop; + + // For performance reasons, we assume we are using a card marking write + // barrier. The assert will fail if this is not the case. + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt"); + + if (s == d) { + // since source and destination are equal we do not need conversion checks. + assert(length > 0, "sanity check"); + Copy::conjoint_oops_atomic(src, dst, length); + } else { + // We have to make sure all elements conform to the destination array + klassOop bound = objArrayKlass::cast(d->klass())->element_klass(); + klassOop stype = objArrayKlass::cast(s->klass())->element_klass(); + if (stype == bound || Klass::cast(stype)->is_subtype_of(bound)) { + // elements are guaranteed to be subtypes, so no check necessary + Copy::conjoint_oops_atomic(src, dst, length); + } else { + // slow case: need individual subtype checks + // note: don't use obj_at_put below because it includes a redundant store check + oop* from = src; + oop* end = from + length; + for (oop* p = dst; from < end; from++, p++) { + oop element = *from; + if (element == NULL || Klass::cast(element->klass())->is_subtype_of(bound)) { + *p = element; + } else { + // We must do a barrier to cover the partial copy. + const size_t done_word_len = pointer_delta(p, dst, oopSize) * + HeapWordsPerOop; + bs->write_ref_array(MemRegion((HeapWord*)dst, done_word_len)); + THROW(vmSymbols::java_lang_ArrayStoreException()); + return; + } + } + } + } + bs->write_ref_array(MemRegion((HeapWord*)dst, word_len)); +} + + +klassOop objArrayKlass::array_klass_impl(bool or_null, int n, TRAPS) { + objArrayKlassHandle h_this(THREAD, as_klassOop()); + return array_klass_impl(h_this, or_null, n, CHECK_NULL); +} + + +klassOop objArrayKlass::array_klass_impl(objArrayKlassHandle this_oop, bool or_null, int n, TRAPS) { + + assert(this_oop->dimension() <= n, "check order of chain"); + int dimension = this_oop->dimension(); + if (dimension == n) + return this_oop(); + + objArrayKlassHandle ak (THREAD, this_oop->higher_dimension()); + if (ak.is_null()) { + if (or_null) return NULL; + + ResourceMark rm; + JavaThread *jt = (JavaThread *)THREAD; + { + MutexLocker mc(Compile_lock, THREAD); // for vtables + // Ensure atomic creation of higher dimensions + MutexLocker mu(MultiArray_lock, THREAD); + + // Check if another thread beat us + ak = objArrayKlassHandle(THREAD, this_oop->higher_dimension()); + if( ak.is_null() ) { + + // Create multi-dim klass object and link them together + klassOop new_klass = + objArrayKlassKlass::cast(Universe::objArrayKlassKlassObj())-> + allocate_objArray_klass(dimension + 1, this_oop, CHECK_NULL); + ak = objArrayKlassHandle(THREAD, new_klass); + this_oop->set_higher_dimension(ak()); + ak->set_lower_dimension(this_oop()); + assert(ak->oop_is_objArray(), "incorrect initialization of objArrayKlass"); + } + } + } else { + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } + + if (or_null) { + return ak->array_klass_or_null(n); + } + return ak->array_klass(n, CHECK_NULL); +} + +klassOop objArrayKlass::array_klass_impl(bool or_null, TRAPS) { + return array_klass_impl(or_null, dimension() + 1, CHECK_NULL); +} + +bool objArrayKlass::can_be_primary_super_slow() const { + if (!bottom_klass()->klass_part()->can_be_primary_super()) + // array of interfaces + return false; + else + return Klass::can_be_primary_super_slow(); +} + +objArrayOop objArrayKlass::compute_secondary_supers(int num_extra_slots, TRAPS) { + // interfaces = { cloneable_klass, serializable_klass, elemSuper[], ... }; + objArrayOop es = Klass::cast(element_klass())->secondary_supers(); + objArrayHandle elem_supers (THREAD, es); + int num_elem_supers = elem_supers.is_null() ? 0 : elem_supers->length(); + int num_secondaries = num_extra_slots + 2 + num_elem_supers; + if (num_secondaries == 2) { + // Must share this for correct bootstrapping! + return Universe::the_array_interfaces_array(); + } else { + objArrayOop sec_oop = oopFactory::new_system_objArray(num_secondaries, CHECK_NULL); + objArrayHandle secondaries(THREAD, sec_oop); + secondaries->obj_at_put(num_extra_slots+0, SystemDictionary::cloneable_klass()); + secondaries->obj_at_put(num_extra_slots+1, SystemDictionary::serializable_klass()); + for (int i = 0; i < num_elem_supers; i++) { + klassOop elem_super = (klassOop) elem_supers->obj_at(i); + klassOop array_super = elem_super->klass_part()->array_klass_or_null(); + assert(array_super != NULL, "must already have been created"); + secondaries->obj_at_put(num_extra_slots+2+i, array_super); + } + return secondaries(); + } +} + +bool objArrayKlass::compute_is_subtype_of(klassOop k) { + if (!k->klass_part()->oop_is_objArray()) + return arrayKlass::compute_is_subtype_of(k); + + objArrayKlass* oak = objArrayKlass::cast(k); + return element_klass()->klass_part()->is_subtype_of(oak->element_klass()); +} + + +void objArrayKlass::initialize(TRAPS) { + Klass::cast(bottom_klass())->initialize(THREAD); // dispatches to either instanceKlass or typeArrayKlass +} + + +void objArrayKlass::oop_follow_contents(oop obj) { + assert (obj->is_array(), "obj must be array"); + arrayOop a = arrayOop(obj); + a->follow_header(); + oop* base = (oop*)a->base(T_OBJECT); + oop* const end = base + a->length(); + while (base < end) { + if (*base != NULL) + // we call mark_and_follow here to avoid excessive marking stack usage + MarkSweep::mark_and_follow(base); + base++; + } +} + +#ifndef SERIALGC +void objArrayKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert (obj->is_array(), "obj must be array"); + arrayOop a = arrayOop(obj); + a->follow_header(cm); + oop* base = (oop*)a->base(T_OBJECT); + oop* const end = base + a->length(); + while (base < end) { + if (*base != NULL) + // we call mark_and_follow here to avoid excessive marking stack usage + PSParallelCompact::mark_and_follow(cm, base); + base++; + } +} +#endif // SERIALGC + +#define invoke_closure_on(base, closure, nv_suffix) { \ + if (*(base) != NULL) { \ + (closure)->do_oop##nv_suffix(base); \ + } \ +} + +#define ObjArrayKlass_OOP_OOP_ITERATE_DEFN(OopClosureType, nv_suffix) \ + \ +int objArrayKlass::oop_oop_iterate##nv_suffix(oop obj, \ + OopClosureType* closure) { \ + SpecializationStats::record_iterate_call##nv_suffix(SpecializationStats::oa); \ + assert (obj->is_array(), "obj must be array"); \ + objArrayOop a = objArrayOop(obj); \ + /* Get size before changing pointers. */ \ + /* Don't call size() or oop_size() since that is a virtual call. */ \ + int size = a->object_size(); \ + if (closure->do_header()) { \ + a->oop_iterate_header(closure); \ + } \ + oop* base = a->base(); \ + oop* const end = base + a->length(); \ + const intx field_offset = PrefetchFieldsAhead; \ + if (field_offset > 0) { \ + while (base < end) { \ + prefetch_beyond(base, end, field_offset, closure->prefetch_style()); \ + invoke_closure_on(base, closure, nv_suffix); \ + base++; \ + } \ + } else { \ + while (base < end) { \ + invoke_closure_on(base, closure, nv_suffix); \ + base++; \ + } \ + } \ + return size; \ +} + +#define ObjArrayKlass_OOP_OOP_ITERATE_DEFN_m(OopClosureType, nv_suffix) \ + \ +int objArrayKlass::oop_oop_iterate##nv_suffix##_m(oop obj, \ + OopClosureType* closure, \ + MemRegion mr) { \ + SpecializationStats::record_iterate_call##nv_suffix(SpecializationStats::oa); \ + assert(obj->is_array(), "obj must be array"); \ + objArrayOop a = objArrayOop(obj); \ + /* Get size before changing pointers. */ \ + /* Don't call size() or oop_size() since that is a virtual call */ \ + int size = a->object_size(); \ + if (closure->do_header()) { \ + a->oop_iterate_header(closure, mr); \ + } \ + oop* bottom = (oop*)mr.start(); \ + oop* top = (oop*)mr.end(); \ + oop* base = a->base(); \ + oop* end = base + a->length(); \ + if (base < bottom) { \ + base = bottom; \ + } \ + if (end > top) { \ + end = top; \ + } \ + const intx field_offset = PrefetchFieldsAhead; \ + if (field_offset > 0) { \ + while (base < end) { \ + prefetch_beyond(base, end, field_offset, closure->prefetch_style()); \ + invoke_closure_on(base, closure, nv_suffix); \ + base++; \ + } \ + } else { \ + while (base < end) { \ + invoke_closure_on(base, closure, nv_suffix); \ + base++; \ + } \ + } \ + return size; \ +} + +ALL_OOP_OOP_ITERATE_CLOSURES_1(ObjArrayKlass_OOP_OOP_ITERATE_DEFN) +ALL_OOP_OOP_ITERATE_CLOSURES_3(ObjArrayKlass_OOP_OOP_ITERATE_DEFN) +ALL_OOP_OOP_ITERATE_CLOSURES_1(ObjArrayKlass_OOP_OOP_ITERATE_DEFN_m) +ALL_OOP_OOP_ITERATE_CLOSURES_3(ObjArrayKlass_OOP_OOP_ITERATE_DEFN_m) + +int objArrayKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_objArray(), "obj must be obj array"); + objArrayOop a = objArrayOop(obj); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = a->object_size(); + a->adjust_header(); + oop* base = a->base(); + oop* const end = base + a->length(); + while (base < end) { + MarkSweep::adjust_pointer(base); + base++; + } + return size; +} + +#ifndef SERIALGC +void objArrayKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(!pm->depth_first(), "invariant"); + assert(obj->is_objArray(), "obj must be obj array"); + // Compute oop range + oop* curr = objArrayOop(obj)->base(); + oop* end = curr + objArrayOop(obj)->length(); + // assert(align_object_size(end - (oop*)obj) == oop_size(obj), "checking size"); + assert(align_object_size(pointer_delta(end, obj, sizeof(oop*))) + == oop_size(obj), "checking size"); + + // Iterate over oops + while (curr < end) { + if (PSScavenge::should_scavenge(*curr)) { + pm->claim_or_forward_breadth(curr); + } + ++curr; + } +} + +void objArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(pm->depth_first(), "invariant"); + assert(obj->is_objArray(), "obj must be obj array"); + // Compute oop range + oop* curr = objArrayOop(obj)->base(); + oop* end = curr + objArrayOop(obj)->length(); + // assert(align_object_size(end - (oop*)obj) == oop_size(obj), "checking size"); + assert(align_object_size(pointer_delta(end, obj, sizeof(oop*))) + == oop_size(obj), "checking size"); + + // Iterate over oops + while (curr < end) { + if (PSScavenge::should_scavenge(*curr)) { + pm->claim_or_forward_depth(curr); + } + ++curr; + } +} + +int objArrayKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert (obj->is_objArray(), "obj must be obj array"); + objArrayOop a = objArrayOop(obj); + + oop* const base = a->base(); + oop* const beg_oop = base; + oop* const end_oop = base + a->length(); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } + return a->object_size(); +} + +int objArrayKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, HeapWord* end_addr) { + assert (obj->is_objArray(), "obj must be obj array"); + objArrayOop a = objArrayOop(obj); + + oop* const base = a->base(); + oop* const beg_oop = MAX2((oop*)beg_addr, base); + oop* const end_oop = MIN2((oop*)end_addr, base + a->length()); + for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { + PSParallelCompact::adjust_pointer(cur_oop); + } + return a->object_size(); +} +#endif // SERIALGC + +// JVM support + +jint objArrayKlass::compute_modifier_flags(TRAPS) const { + // The modifier for an objectArray is the same as its element + if (element_klass() == NULL) { + assert(Universe::is_bootstrapping(), "partial objArray only at startup"); + return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC; + } + // Recurse down the element list + jint element_flags = Klass::cast(element_klass())->compute_modifier_flags(CHECK_0); + + return (element_flags & (JVM_ACC_PUBLIC | JVM_ACC_PRIVATE | JVM_ACC_PROTECTED)) + | (JVM_ACC_ABSTRACT | JVM_ACC_FINAL); +} + + +#ifndef PRODUCT +// Printing + +void objArrayKlass::oop_print_on(oop obj, outputStream* st) { + arrayKlass::oop_print_on(obj, st); + assert(obj->is_objArray(), "must be objArray"); + objArrayOop oa = objArrayOop(obj); + int print_len = MIN2((intx) oa->length(), MaxElementPrintSize); + for(int index = 0; index < print_len; index++) { + st->print(" - %3d : ", index); + oa->obj_at(index)->print_value_on(st); + st->cr(); + } + int remaining = oa->length() - print_len; + if (remaining > 0) { + tty->print_cr(" - <%d more elements, increase MaxElementPrintSize to print>", remaining); + } +} + + +void objArrayKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_objArray(), "must be objArray"); + element_klass()->print_value_on(st); + st->print("a [%d] ", objArrayOop(obj)->length()); + as_klassOop()->klass()->print_value_on(st); +} + +#endif // PRODUCT + +const char* objArrayKlass::internal_name() const { + return external_name(); +} + +// Verification + +void objArrayKlass::oop_verify_on(oop obj, outputStream* st) { + arrayKlass::oop_verify_on(obj, st); + guarantee(obj->is_objArray(), "must be objArray"); + objArrayOop oa = objArrayOop(obj); + for(int index = 0; index < oa->length(); index++) { + guarantee(oa->obj_at(index)->is_oop_or_null(), "should be oop"); + } +} + +void objArrayKlass::oop_verify_old_oop(oop obj, oop* p, bool allow_dirty) { + /* $$$ move into remembered set verification? + RememberedSet::verify_old_oop(obj, p, allow_dirty, true); + */ +} diff --git a/hotspot/src/share/vm/oops/objArrayKlass.hpp b/hotspot/src/share/vm/oops/objArrayKlass.hpp new file mode 100644 index 00000000000..a2915ef0d17 --- /dev/null +++ b/hotspot/src/share/vm/oops/objArrayKlass.hpp @@ -0,0 +1,128 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// objArrayKlass is the klass for objArrays + +class objArrayKlass : public arrayKlass { + friend class VMStructs; + private: + klassOop _element_klass; // The klass of the elements of this array type + klassOop _bottom_klass; // The one-dimensional type (instanceKlass or typeArrayKlass) + public: + // Instance variables + klassOop element_klass() const { return _element_klass; } + void set_element_klass(klassOop k) { oop_store_without_check((oop*) &_element_klass, (oop) k); } + oop* element_klass_addr() { return (oop*)&_element_klass; } + + klassOop bottom_klass() const { return _bottom_klass; } + void set_bottom_klass(klassOop k) { oop_store_without_check((oop*) &_bottom_klass, (oop) k); } + oop* bottom_klass_addr() { return (oop*)&_bottom_klass; } + + // Compiler/Interpreter offset + static int element_klass_offset_in_bytes() { return offset_of(objArrayKlass, _element_klass); } + + // Dispatched operation + bool can_be_primary_super_slow() const; + objArrayOop compute_secondary_supers(int num_extra_slots, TRAPS); + bool compute_is_subtype_of(klassOop k); + bool oop_is_objArray_slow() const { return true; } + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(objArrayKlass); + objArrayOop allocate(int length, TRAPS); + oop multi_allocate(int rank, jint* sizes, TRAPS); + + // Copying + void copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS); + + // Compute protection domain + oop protection_domain() { return Klass::cast(bottom_klass())->protection_domain(); } + // Compute class loader + oop class_loader() const { return Klass::cast(bottom_klass())->class_loader(); } + + protected: + // Returns the objArrayKlass for n'th dimension. + virtual klassOop array_klass_impl(bool or_null, int n, TRAPS); + + // Returns the array class with this class as element type. + virtual klassOop array_klass_impl(bool or_null, TRAPS); + + public: + // Casting from klassOop + static objArrayKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_objArray_slow(), "cast to objArrayKlass"); + return (objArrayKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(objArrayKlass)/HeapWordSize; } + int object_size() const { return arrayKlass::object_size(header_size()); } + + // Initialization (virtual from Klass) + void initialize(TRAPS); + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk) { + return oop_oop_iterate_v(obj, blk); + } + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + return oop_oop_iterate_v_m(obj, blk, mr); + } +#define ObjArrayKlass_OOP_OOP_ITERATE_DECL(OopClosureType, nv_suffix) \ + int oop_oop_iterate##nv_suffix(oop obj, OopClosureType* blk); \ + int oop_oop_iterate##nv_suffix##_m(oop obj, OopClosureType* blk, \ + MemRegion mr); + + ALL_OOP_OOP_ITERATE_CLOSURES_1(ObjArrayKlass_OOP_OOP_ITERATE_DECL) + ALL_OOP_OOP_ITERATE_CLOSURES_3(ObjArrayKlass_OOP_OOP_ITERATE_DECL) + + // JVM support + jint compute_modifier_flags(TRAPS) const; + + private: + static klassOop array_klass_impl (objArrayKlassHandle this_oop, bool or_null, int n, TRAPS); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on (oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); +#endif + + public: + // Verification + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); + void oop_verify_old_oop(oop obj, oop* p, bool allow_dirty); + +}; diff --git a/hotspot/src/share/vm/oops/objArrayKlassKlass.cpp b/hotspot/src/share/vm/oops/objArrayKlassKlass.cpp new file mode 100644 index 00000000000..06c7f39f5eb --- /dev/null +++ b/hotspot/src/share/vm/oops/objArrayKlassKlass.cpp @@ -0,0 +1,308 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_objArrayKlassKlass.cpp.incl" + +klassOop objArrayKlassKlass::create_klass(TRAPS) { + objArrayKlassKlass o; + KlassHandle h_this_klass(THREAD, Universe::klassKlassObj()); + KlassHandle k = base_create_klass(h_this_klass, header_size(), o.vtbl_value(), CHECK_0); + assert(k()->size() == align_object_size(header_size()), "wrong size for object"); + java_lang_Class::create_mirror(k, CHECK_0); // Allocate mirror + return k(); +} + +klassOop objArrayKlassKlass::allocate_system_objArray_klass(TRAPS) { + // system_objArrays have no instance klass, so allocate with fake class, then reset to NULL + KlassHandle kk(THREAD, Universe::intArrayKlassObj()); + klassOop k = allocate_objArray_klass(1, kk, CHECK_0); + objArrayKlass* tk = (objArrayKlass*) k->klass_part(); + tk->set_element_klass(NULL); + tk->set_bottom_klass(NULL); + return k; +} + + +klassOop objArrayKlassKlass::allocate_objArray_klass(int n, KlassHandle element_klass, TRAPS) { + objArrayKlassKlassHandle this_oop(THREAD, as_klassOop()); + return allocate_objArray_klass_impl(this_oop, n, element_klass, THREAD); +} + +klassOop objArrayKlassKlass::allocate_objArray_klass_impl(objArrayKlassKlassHandle this_oop, + int n, KlassHandle element_klass, TRAPS) { + + // Eagerly allocate the direct array supertype. + KlassHandle super_klass = KlassHandle(); + if (!Universe::is_bootstrapping()) { + KlassHandle element_super (THREAD, element_klass->super()); + if (element_super.not_null()) { + // The element type has a direct super. E.g., String[] has direct super of Object[]. + super_klass = KlassHandle(THREAD, element_super->array_klass_or_null()); + bool supers_exist = super_klass.not_null(); + // Also, see if the element has secondary supertypes. + // We need an array type for each. + objArrayHandle element_supers = objArrayHandle(THREAD, + element_klass->secondary_supers()); + for( int i = element_supers->length()-1; i >= 0; i-- ) { + klassOop elem_super = (klassOop) element_supers->obj_at(i); + if (Klass::cast(elem_super)->array_klass_or_null() == NULL) { + supers_exist = false; + break; + } + } + if (!supers_exist) { + // Oops. Not allocated yet. Back out, allocate it, and retry. +#ifndef PRODUCT + if (WizardMode) { + tty->print_cr("Must retry array klass creation for depth %d",n); + } +#endif + KlassHandle ek; + { + MutexUnlocker mu(MultiArray_lock); + MutexUnlocker mc(Compile_lock); // for vtables + klassOop sk = element_super->array_klass(CHECK_0); + super_klass = KlassHandle(THREAD, sk); + for( int i = element_supers->length()-1; i >= 0; i-- ) { + KlassHandle elem_super (THREAD, element_supers->obj_at(i)); + elem_super->array_klass(CHECK_0); + } + // Now retry from the beginning + klassOop klass_oop = element_klass->array_klass(n, CHECK_0); + // Create a handle because the enclosing brace, when locking + // can cause a gc. Better to have this function return a Handle. + ek = KlassHandle(THREAD, klass_oop); + } // re-lock + return ek(); + } + } else { + // The element type is already Object. Object[] has direct super of Object. + super_klass = KlassHandle(THREAD, SystemDictionary::object_klass()); + } + } + + // Create type name for klass (except for symbol arrays, since symbolKlass + // does not have a name). This will potentially allocate an object, cause + // GC, and all other kinds of things. Hence, this must be done before we + // get a handle to the new objArrayKlass we want to construct. We cannot + // block while holding a handling to a partly initialized object. + symbolHandle name = symbolHandle(); + + if (!element_klass->oop_is_symbol()) { + ResourceMark rm(THREAD); + char *name_str = element_klass->name()->as_C_string(); + int len = element_klass->name()->utf8_length(); + char *new_str = NEW_RESOURCE_ARRAY(char, len + 4); + int idx = 0; + new_str[idx++] = '['; + if (element_klass->oop_is_instance()) { // it could be an array or simple type + new_str[idx++] = 'L'; + } + memcpy(&new_str[idx], name_str, len * sizeof(char)); + idx += len; + if (element_klass->oop_is_instance()) { + new_str[idx++] = ';'; + } + new_str[idx++] = '\0'; + name = oopFactory::new_symbol_handle(new_str, CHECK_0); + } + + objArrayKlass o; + arrayKlassHandle k = arrayKlass::base_create_array_klass(o.vtbl_value(), + objArrayKlass::header_size(), + this_oop, + CHECK_0); + + + // Initialize instance variables + objArrayKlass* oak = objArrayKlass::cast(k()); + oak->set_dimension(n); + oak->set_element_klass(element_klass()); + oak->set_name(name()); + + klassOop bk; + if (element_klass->oop_is_objArray()) { + bk = objArrayKlass::cast(element_klass())->bottom_klass(); + } else { + bk = element_klass(); + } + assert(bk != NULL && (Klass::cast(bk)->oop_is_instance() || Klass::cast(bk)->oop_is_typeArray()), "invalid bottom klass"); + oak->set_bottom_klass(bk); + + oak->set_layout_helper(array_layout_helper(T_OBJECT)); + assert(oak->oop_is_javaArray(), "sanity"); + assert(oak->oop_is_objArray(), "sanity"); + + // Call complete_create_array_klass after all instance variables has been initialized. + arrayKlass::complete_create_array_klass(k, super_klass, CHECK_0); + + return k(); +} + + +void objArrayKlassKlass::oop_follow_contents(oop obj) { + assert(obj->is_klass(), "must be klass"); + assert(klassOop(obj)->klass_part()->oop_is_objArray_slow(), "must be obj array"); + + objArrayKlass* oak = objArrayKlass::cast((klassOop)obj); + MarkSweep::mark_and_push(oak->element_klass_addr()); + MarkSweep::mark_and_push(oak->bottom_klass_addr()); + + arrayKlassKlass::oop_follow_contents(obj); +} + +#ifndef SERIALGC +void objArrayKlassKlass::oop_follow_contents(ParCompactionManager* cm, + oop obj) { + assert(obj->is_klass(), "must be klass"); + assert(klassOop(obj)->klass_part()->oop_is_objArray_slow(), "must be obj array"); + + objArrayKlass* oak = objArrayKlass::cast((klassOop)obj); + PSParallelCompact::mark_and_push(cm, oak->element_klass_addr()); + PSParallelCompact::mark_and_push(cm, oak->bottom_klass_addr()); + + arrayKlassKlass::oop_follow_contents(cm, obj); +} +#endif // SERIALGC + + +int objArrayKlassKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_klass(), "must be klass"); + assert(klassOop(obj)->klass_part()->oop_is_objArray_slow(), "must be obj array"); + + objArrayKlass* oak = objArrayKlass::cast((klassOop)obj); + MarkSweep::adjust_pointer(oak->element_klass_addr()); + MarkSweep::adjust_pointer(oak->bottom_klass_addr()); + + return arrayKlassKlass::oop_adjust_pointers(obj); +} + + + +int objArrayKlassKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert(obj->is_klass(), "must be klass"); + assert(klassOop(obj)->klass_part()->oop_is_objArray_slow(), "must be obj array"); + + objArrayKlass* oak = objArrayKlass::cast((klassOop)obj); + blk->do_oop(oak->element_klass_addr()); + blk->do_oop(oak->bottom_klass_addr()); + + return arrayKlassKlass::oop_oop_iterate(obj, blk); +} + + +int +objArrayKlassKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + assert(obj->is_klass(), "must be klass"); + assert(klassOop(obj)->klass_part()->oop_is_objArray_slow(), "must be obj array"); + + objArrayKlass* oak = objArrayKlass::cast((klassOop)obj); + oop* addr; + addr = oak->element_klass_addr(); + if (mr.contains(addr)) blk->do_oop(addr); + addr = oak->bottom_klass_addr(); + if (mr.contains(addr)) blk->do_oop(addr); + + return arrayKlassKlass::oop_oop_iterate(obj, blk); +} + +#ifndef SERIALGC +void objArrayKlassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(obj->blueprint()->oop_is_objArrayKlass(),"must be an obj array klass"); +} + +void objArrayKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(obj->blueprint()->oop_is_objArrayKlass(),"must be an obj array klass"); +} + +int objArrayKlassKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert(obj->is_klass(), "must be klass"); + assert(klassOop(obj)->klass_part()->oop_is_objArray_slow(), "must be obj array"); + + objArrayKlass* oak = objArrayKlass::cast((klassOop)obj); + PSParallelCompact::adjust_pointer(oak->element_klass_addr()); + PSParallelCompact::adjust_pointer(oak->bottom_klass_addr()); + + return arrayKlassKlass::oop_update_pointers(cm, obj); +} + +int objArrayKlassKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, + HeapWord* end_addr) { + assert(obj->is_klass(), "must be klass"); + assert(klassOop(obj)->klass_part()->oop_is_objArray_slow(), "must be obj array"); + + oop* p; + objArrayKlass* oak = objArrayKlass::cast((klassOop)obj); + p = oak->element_klass_addr(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = oak->bottom_klass_addr(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + + return arrayKlassKlass::oop_update_pointers(cm, obj, beg_addr, end_addr); +} +#endif // SERIALGC + +#ifndef PRODUCT + +// Printing + +void objArrayKlassKlass::oop_print_on(oop obj, outputStream* st) { + assert(obj->is_klass(), "must be klass"); + objArrayKlass* oak = (objArrayKlass*) klassOop(obj)->klass_part(); + klassKlass::oop_print_on(obj, st); + st->print(" - instance klass: "); + oak->element_klass()->print_value_on(st); + st->cr(); +} + + +void objArrayKlassKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_klass(), "must be klass"); + objArrayKlass* oak = (objArrayKlass*) klassOop(obj)->klass_part(); + + oak->element_klass()->print_value_on(st); + st->print("[]"); +} + +#endif + +const char* objArrayKlassKlass::internal_name() const { + return "{object array class}"; +} + + +// Verification + +void objArrayKlassKlass::oop_verify_on(oop obj, outputStream* st) { + klassKlass::oop_verify_on(obj, st); + objArrayKlass* oak = objArrayKlass::cast((klassOop)obj); + guarantee(oak->element_klass()->is_perm(), "should be in permspace"); + guarantee(oak->element_klass()->is_klass(), "should be klass"); + guarantee(oak->bottom_klass()->is_perm(), "should be in permspace"); + guarantee(oak->bottom_klass()->is_klass(), "should be klass"); + Klass* bk = Klass::cast(oak->bottom_klass()); + guarantee(bk->oop_is_instance() || bk->oop_is_typeArray(), "invalid bottom klass"); +} diff --git a/hotspot/src/share/vm/oops/objArrayKlassKlass.hpp b/hotspot/src/share/vm/oops/objArrayKlassKlass.hpp new file mode 100644 index 00000000000..ff3e94a6016 --- /dev/null +++ b/hotspot/src/share/vm/oops/objArrayKlassKlass.hpp @@ -0,0 +1,79 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The objArrayKlassKlass is klass for all objArrayKlass' + +class objArrayKlassKlass : public arrayKlassKlass { + public: + // Testing + virtual bool oop_is_objArrayKlass() const { return true; } + + // Dispatched operation + int oop_size(oop obj) const { return objArrayKlass::cast(klassOop(obj))->object_size(); } + int klass_oop_size() const { return object_size(); } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(objArrayKlassKlass); + static klassOop create_klass(TRAPS); + klassOop allocate_objArray_klass(int n, KlassHandle element_klass, TRAPS); + klassOop allocate_system_objArray_klass(TRAPS); // Used for bootstrapping in Universe::genesis + + // Casting from klassOop + static objArrayKlassKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_klass(), "cast to objArrayKlassKlass"); + return (objArrayKlassKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(objArrayKlassKlass)/HeapWordSize; } + int object_size() const { return align_object_size(header_size()); } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + + private: + // helpers + static klassOop allocate_objArray_klass_impl(objArrayKlassKlassHandle this_oop, int n, KlassHandle element_klass, TRAPS); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on(oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); +#endif + + public: + // Verification + const char* internal_name() const; + void oop_verify_on(oop obj, outputStream* st); + +}; diff --git a/hotspot/src/share/vm/oops/objArrayOop.cpp b/hotspot/src/share/vm/oops/objArrayOop.cpp new file mode 100644 index 00000000000..c339e2cd8fc --- /dev/null +++ b/hotspot/src/share/vm/oops/objArrayOop.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_objArrayOop.cpp.incl" + +// <> diff --git a/hotspot/src/share/vm/oops/objArrayOop.hpp b/hotspot/src/share/vm/oops/objArrayOop.hpp new file mode 100644 index 00000000000..b61d7d4d6ba --- /dev/null +++ b/hotspot/src/share/vm/oops/objArrayOop.hpp @@ -0,0 +1,45 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// An objArrayOop is an array containing oops. +// Evaluating "String arg[10]" will create an objArrayOop. + +class objArrayOopDesc : public arrayOopDesc { + public: + // Accessing + oop obj_at(int index) const { return *obj_at_addr(index); } + void obj_at_put(int index, oop value) { oop_store(obj_at_addr(index), value); } + oop* base() const { return (oop*) arrayOopDesc::base(T_OBJECT); } + + // Sizing + static int header_size() { return arrayOopDesc::header_size(T_OBJECT); } + static int object_size(int length) { return align_object_size(header_size() + length); } + int object_size() { return object_size(length()); } + + // Returns the address of the index'th element + oop* obj_at_addr(int index) const { + assert(is_within_bounds(index), "index out of bounds"); + return &base()[index]; + } +}; diff --git a/hotspot/src/share/vm/oops/oop.cpp b/hotspot/src/share/vm/oops/oop.cpp new file mode 100644 index 00000000000..6fbecdaf391 --- /dev/null +++ b/hotspot/src/share/vm/oops/oop.cpp @@ -0,0 +1,132 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_oop.cpp.incl" + +bool always_do_update_barrier = false; + +BarrierSet* oopDesc::_bs = NULL; + +#ifdef PRODUCT +void oopDesc::print_on(outputStream* st) const {} +void oopDesc::print_value_on(outputStream* st) const {} +void oopDesc::print_address_on(outputStream* st) const {} +char* oopDesc::print_value_string() { return NULL; } +char* oopDesc::print_string() { return NULL; } +void oopDesc::print() {} +void oopDesc::print_value() {} +void oopDesc::print_address() {} +#else +void oopDesc::print_on(outputStream* st) const { + if (this == NULL) { + st->print_cr("NULL"); + } else { + blueprint()->oop_print_on(oop(this), st); + } +} + +void oopDesc::print_value_on(outputStream* st) const { + oop obj = oop(this); + if (this == NULL) { + st->print("NULL"); + } else if (java_lang_String::is_instance(obj)) { + java_lang_String::print(obj, st); + if (PrintOopAddress) print_address_on(st); +#ifdef ASSERT + } else if (!Universe::heap()->is_in(obj) || !Universe::heap()->is_in(klass())) { + st->print("### BAD OOP %p ###", (address)obj); +#endif + } else { + blueprint()->oop_print_value_on(obj, st); + } +} + +void oopDesc::print_address_on(outputStream* st) const { + if (PrintOopAddress) { + st->print("{"); + if (PrintOopAddress) { + st->print(INTPTR_FORMAT, this); + } + st->print("}"); + } +} + +void oopDesc::print() { print_on(tty); } + +void oopDesc::print_value() { print_value_on(tty); } + +void oopDesc::print_address() { print_address_on(tty); } + +char* oopDesc::print_string() { + stringStream* st = new stringStream(); + print_on(st); + return st->as_string(); +} + +char* oopDesc::print_value_string() { + stringStream* st = new stringStream(); + print_value_on(st); + return st->as_string(); +} + +#endif // PRODUCT + +void oopDesc::verify_on(outputStream* st) { + if (this != NULL) { + blueprint()->oop_verify_on(this, st); + } +} + + +void oopDesc::verify() { + verify_on(tty); +} + + +void oopDesc::verify_old_oop(oop* p, bool allow_dirty) { + blueprint()->oop_verify_old_oop(this, p, allow_dirty); +} + + +bool oopDesc::partially_loaded() { + return blueprint()->oop_partially_loaded(this); +} + + +void oopDesc::set_partially_loaded() { + blueprint()->oop_set_partially_loaded(this); +} + + +intptr_t oopDesc::slow_identity_hash() { + // slow case; we have to acquire the micro lock in order to locate the header + ResetNoHandleMark rnm; // Might be called from LEAF/QUICK ENTRY + HandleMark hm; + Handle object((oop)this); + assert(!is_shared_readonly(), "using identity hash on readonly object?"); + return ObjectSynchronizer::identity_hash_value_for(object); +} + +VerifyOopClosure VerifyOopClosure::verify_oop; diff --git a/hotspot/src/share/vm/oops/oop.hpp b/hotspot/src/share/vm/oops/oop.hpp new file mode 100644 index 00000000000..2c4b07d5491 --- /dev/null +++ b/hotspot/src/share/vm/oops/oop.hpp @@ -0,0 +1,322 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// oopDesc is the top baseclass for objects classes. The {name}Desc classes describe +// the format of Java objects so the fields can be accessed from C++. +// oopDesc is abstract. +// (see oopHierarchy for complete oop class hierarchy) +// +// no virtual functions allowed + +// store into oop with store check +void oop_store(oop* p, oop v); +void oop_store(volatile oop* p, oop v); + +// store into oop without store check +void oop_store_without_check(oop* p, oop v); +void oop_store_without_check(volatile oop* p, oop v); + + +extern bool always_do_update_barrier; + +// Forward declarations. +class OopClosure; +class ScanClosure; +class FastScanClosure; +class FilteringClosure; +class BarrierSet; +class CMSIsAliveClosure; + +class PSPromotionManager; +class ParCompactionManager; + +class oopDesc { + friend class VMStructs; + private: + volatile markOop _mark; + klassOop _klass; + + // Fast access to barrier set. Must be initialized. + static BarrierSet* _bs; + + public: + markOop mark() const { return _mark; } + markOop* mark_addr() const { return (markOop*) &_mark; } + + void set_mark(volatile markOop m) { _mark = m; } + + void release_set_mark(markOop m); + markOop cas_set_mark(markOop new_mark, markOop old_mark); + + // Used only to re-initialize the mark word (e.g., of promoted + // objects during a GC) -- requires a valid klass pointer + void init_mark(); + + klassOop klass() const { return _klass; } + oop* klass_addr() const { return (oop*) &_klass; } + + void set_klass(klassOop k); + // For when the klass pointer is being used as a linked list "next" field. + void set_klass_to_list_ptr(oop k); + + // size of object header + static int header_size() { return sizeof(oopDesc)/HeapWordSize; } + static int header_size_in_bytes() { return sizeof(oopDesc); } + + Klass* blueprint() const; + + // Returns whether this is an instance of k or an instance of a subclass of k + bool is_a(klassOop k) const; + + // Returns the actual oop size of the object + int size(); + + // Sometimes (for complicated concurrency-related reasons), it is useful + // to be able to figure out the size of an object knowing its klass. + int size_given_klass(Klass* klass); + + // Some perm gen objects are not parseble immediately after + // installation of their klass pointer. + bool is_parsable(); + + // type test operations (inlined in oop.inline.h) + bool is_instance() const; + bool is_instanceRef() const; + bool is_array() const; + bool is_objArray() const; + bool is_symbol() const; + bool is_klass() const; + bool is_thread() const; + bool is_method() const; + bool is_constMethod() const; + bool is_methodData() const; + bool is_constantPool() const; + bool is_constantPoolCache() const; + bool is_typeArray() const; + bool is_javaArray() const; + bool is_compiledICHolder() const; + + private: + // field addresses in oop + // byte/char/bool/short fields are always stored as full words + void* field_base(int offset) const; + + jbyte* byte_field_addr(int offset) const; + jchar* char_field_addr(int offset) const; + jboolean* bool_field_addr(int offset) const; + jint* int_field_addr(int offset) const; + jshort* short_field_addr(int offset) const; + jlong* long_field_addr(int offset) const; + jfloat* float_field_addr(int offset) const; + jdouble* double_field_addr(int offset) const; + + public: + // need this as public for garbage collection + oop* obj_field_addr(int offset) const; + + oop obj_field(int offset) const; + void obj_field_put(int offset, oop value); + + jbyte byte_field(int offset) const; + void byte_field_put(int offset, jbyte contents); + + jchar char_field(int offset) const; + void char_field_put(int offset, jchar contents); + + jboolean bool_field(int offset) const; + void bool_field_put(int offset, jboolean contents); + + jint int_field(int offset) const; + void int_field_put(int offset, jint contents); + + jshort short_field(int offset) const; + void short_field_put(int offset, jshort contents); + + jlong long_field(int offset) const; + void long_field_put(int offset, jlong contents); + + jfloat float_field(int offset) const; + void float_field_put(int offset, jfloat contents); + + jdouble double_field(int offset) const; + void double_field_put(int offset, jdouble contents); + + oop obj_field_acquire(int offset) const; + void release_obj_field_put(int offset, oop value); + + jbyte byte_field_acquire(int offset) const; + void release_byte_field_put(int offset, jbyte contents); + + jchar char_field_acquire(int offset) const; + void release_char_field_put(int offset, jchar contents); + + jboolean bool_field_acquire(int offset) const; + void release_bool_field_put(int offset, jboolean contents); + + jint int_field_acquire(int offset) const; + void release_int_field_put(int offset, jint contents); + + jshort short_field_acquire(int offset) const; + void release_short_field_put(int offset, jshort contents); + + jlong long_field_acquire(int offset) const; + void release_long_field_put(int offset, jlong contents); + + jfloat float_field_acquire(int offset) const; + void release_float_field_put(int offset, jfloat contents); + + jdouble double_field_acquire(int offset) const; + void release_double_field_put(int offset, jdouble contents); + + // printing functions for VM debugging + void print_on(outputStream* st) const; // First level print + void print_value_on(outputStream* st) const; // Second level print. + void print_address_on(outputStream* st) const; // Address printing + + // printing on default output stream + void print(); + void print_value(); + void print_address(); + + // return the print strings + char* print_string(); + char* print_value_string(); + + // verification operations + void verify_on(outputStream* st); + void verify(); + void verify_old_oop(oop* p, bool allow_dirty); + + // tells whether this oop is partially constructed (gc during class loading) + bool partially_loaded(); + void set_partially_loaded(); + + // locking operations + bool is_locked() const; + bool is_unlocked() const; + bool has_bias_pattern() const; + + // asserts + bool is_oop(bool ignore_mark_word = false) const; + bool is_oop_or_null(bool ignore_mark_word = false) const; +#ifndef PRODUCT + bool is_unlocked_oop() const; +#endif + + // garbage collection + bool is_gc_marked() const; + // Apply "MarkSweep::mark_and_push" to (the address of) every non-NULL + // reference field in "this". + void follow_contents(); + void follow_header(); + +#ifndef SERIALGC + // Parallel Scavenge + void copy_contents(PSPromotionManager* pm); + void push_contents(PSPromotionManager* pm); + + // Parallel Old + void update_contents(ParCompactionManager* cm); + void update_contents(ParCompactionManager* cm, + HeapWord* begin_limit, + HeapWord* end_limit); + void update_contents(ParCompactionManager* cm, + klassOop old_klass, + HeapWord* begin_limit, + HeapWord* end_limit); + + void follow_contents(ParCompactionManager* cm); + void follow_header(ParCompactionManager* cm); +#endif // SERIALGC + + bool is_perm() const; + bool is_perm_or_null() const; + bool is_shared() const; + bool is_shared_readonly() const; + bool is_shared_readwrite() const; + + // Forward pointer operations for scavenge + bool is_forwarded() const; + + void forward_to(oop p); + bool cas_forward_to(oop p, markOop compare); + +#ifndef SERIALGC + // Like "forward_to", but inserts the forwarding pointer atomically. + // Exactly one thread succeeds in inserting the forwarding pointer, and + // this call returns "NULL" for that thread; any other thread has the + // value of the forwarding pointer returned and does not modify "this". + oop forward_to_atomic(oop p); +#endif // SERIALGC + + oop forwardee() const; + + // Age of object during scavenge + int age() const; + void incr_age(); + + // Adjust all pointers in this object to point at it's forwarded location and + // return the size of this oop. This is used by the MarkSweep collector. + int adjust_pointers(); + void adjust_header(); + +#ifndef SERIALGC + // Parallel old + void update_header(); + void update_header(HeapWord* beg_addr, HeapWord* end_addr); +#endif // SERIALGC + + // mark-sweep support + void follow_body(int begin, int end); + + // Fast access to barrier set + static BarrierSet* bs() { return _bs; } + static void set_bs(BarrierSet* bs) { _bs = bs; } + + // iterators, returns size of object +#define OOP_ITERATE_DECL(OopClosureType, nv_suffix) \ + int oop_iterate(OopClosureType* blk); \ + int oop_iterate(OopClosureType* blk, MemRegion mr); // Only in mr. + + ALL_OOP_OOP_ITERATE_CLOSURES_1(OOP_ITERATE_DECL) + ALL_OOP_OOP_ITERATE_CLOSURES_3(OOP_ITERATE_DECL) + + void oop_iterate_header(OopClosure* blk); + void oop_iterate_header(OopClosure* blk, MemRegion mr); + + // identity hash; returns the identity hash key (computes it if necessary) + // NOTE with the introduction of UseBiasedLocking that identity_hash() might reach a + // safepoint if called on a biased object. Calling code must be aware of that. + intptr_t identity_hash(); + intptr_t slow_identity_hash(); + + // marks are forwarded to stack when object is locked + bool has_displaced_mark() const; + markOop displaced_mark() const; + void set_displaced_mark(markOop m); + + // for code generation + static int klass_offset_in_bytes() { return offset_of(oopDesc, _klass); } + static int mark_offset_in_bytes() { return offset_of(oopDesc, _mark); } +}; diff --git a/hotspot/src/share/vm/oops/oop.inline.hpp b/hotspot/src/share/vm/oops/oop.inline.hpp new file mode 100644 index 00000000000..f01dede106b --- /dev/null +++ b/hotspot/src/share/vm/oops/oop.inline.hpp @@ -0,0 +1,483 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Implementation of all inlined member functions defined in oop.hpp +// We need a separate file to avoid circular references + + +inline void oopDesc::release_set_mark(markOop m) { + OrderAccess::release_store_ptr(&_mark, m); +} + +inline markOop oopDesc::cas_set_mark(markOop new_mark, markOop old_mark) { + return (markOop) Atomic::cmpxchg_ptr(new_mark, &_mark, old_mark); +} + +inline void oopDesc::set_klass(klassOop k) { + // since klasses are promoted no store check is needed + assert(Universe::is_bootstrapping() || k != NULL, "must be a real klassOop"); + assert(Universe::is_bootstrapping() || k->is_klass(), "not a klassOop"); + oop_store_without_check((oop*) &_klass, (oop) k); +} + +inline void oopDesc::set_klass_to_list_ptr(oop k) { + // This is only to be used during GC, for from-space objects, so no + // barrier is needed. + _klass = (klassOop)k; +} + +inline void oopDesc::init_mark() { set_mark(markOopDesc::prototype_for_object(this)); } +inline Klass* oopDesc::blueprint() const { return klass()->klass_part(); } + +inline bool oopDesc::is_a(klassOop k) const { return blueprint()->is_subtype_of(k); } + +inline bool oopDesc::is_instance() const { return blueprint()->oop_is_instance(); } +inline bool oopDesc::is_instanceRef() const { return blueprint()->oop_is_instanceRef(); } +inline bool oopDesc::is_array() const { return blueprint()->oop_is_array(); } +inline bool oopDesc::is_objArray() const { return blueprint()->oop_is_objArray(); } +inline bool oopDesc::is_typeArray() const { return blueprint()->oop_is_typeArray(); } +inline bool oopDesc::is_javaArray() const { return blueprint()->oop_is_javaArray(); } +inline bool oopDesc::is_symbol() const { return blueprint()->oop_is_symbol(); } +inline bool oopDesc::is_klass() const { return blueprint()->oop_is_klass(); } +inline bool oopDesc::is_thread() const { return blueprint()->oop_is_thread(); } +inline bool oopDesc::is_method() const { return blueprint()->oop_is_method(); } +inline bool oopDesc::is_constMethod() const { return blueprint()->oop_is_constMethod(); } +inline bool oopDesc::is_methodData() const { return blueprint()->oop_is_methodData(); } +inline bool oopDesc::is_constantPool() const { return blueprint()->oop_is_constantPool(); } +inline bool oopDesc::is_constantPoolCache() const { return blueprint()->oop_is_constantPoolCache(); } +inline bool oopDesc::is_compiledICHolder() const { return blueprint()->oop_is_compiledICHolder(); } + +inline void* oopDesc::field_base(int offset) const { return (void*)&((char*)this)[offset]; } + +inline oop* oopDesc::obj_field_addr(int offset) const { return (oop*) field_base(offset); } +inline jbyte* oopDesc::byte_field_addr(int offset) const { return (jbyte*) field_base(offset); } +inline jchar* oopDesc::char_field_addr(int offset) const { return (jchar*) field_base(offset); } +inline jboolean* oopDesc::bool_field_addr(int offset) const { return (jboolean*)field_base(offset); } +inline jint* oopDesc::int_field_addr(int offset) const { return (jint*) field_base(offset); } +inline jshort* oopDesc::short_field_addr(int offset) const { return (jshort*) field_base(offset); } +inline jlong* oopDesc::long_field_addr(int offset) const { return (jlong*) field_base(offset); } +inline jfloat* oopDesc::float_field_addr(int offset) const { return (jfloat*) field_base(offset); } +inline jdouble* oopDesc::double_field_addr(int offset) const { return (jdouble*) field_base(offset); } + +inline oop oopDesc::obj_field(int offset) const { return *obj_field_addr(offset); } +inline void oopDesc::obj_field_put(int offset, oop value) { oop_store(obj_field_addr(offset), value); } + +inline jbyte oopDesc::byte_field(int offset) const { return (jbyte) *byte_field_addr(offset); } +inline void oopDesc::byte_field_put(int offset, jbyte contents) { *byte_field_addr(offset) = (jint) contents; } + +inline jboolean oopDesc::bool_field(int offset) const { return (jboolean) *bool_field_addr(offset); } +inline void oopDesc::bool_field_put(int offset, jboolean contents) { *bool_field_addr(offset) = (jint) contents; } + +inline jchar oopDesc::char_field(int offset) const { return (jchar) *char_field_addr(offset); } +inline void oopDesc::char_field_put(int offset, jchar contents) { *char_field_addr(offset) = (jint) contents; } + +inline jint oopDesc::int_field(int offset) const { return *int_field_addr(offset); } +inline void oopDesc::int_field_put(int offset, jint contents) { *int_field_addr(offset) = contents; } + +inline jshort oopDesc::short_field(int offset) const { return (jshort) *short_field_addr(offset); } +inline void oopDesc::short_field_put(int offset, jshort contents) { *short_field_addr(offset) = (jint) contents;} + +inline jlong oopDesc::long_field(int offset) const { return *long_field_addr(offset); } +inline void oopDesc::long_field_put(int offset, jlong contents) { *long_field_addr(offset) = contents; } + +inline jfloat oopDesc::float_field(int offset) const { return *float_field_addr(offset); } +inline void oopDesc::float_field_put(int offset, jfloat contents) { *float_field_addr(offset) = contents; } + +inline jdouble oopDesc::double_field(int offset) const { return *double_field_addr(offset); } +inline void oopDesc::double_field_put(int offset, jdouble contents) { *double_field_addr(offset) = contents; } + +inline oop oopDesc::obj_field_acquire(int offset) const { return (oop)OrderAccess::load_ptr_acquire(obj_field_addr(offset)); } +inline void oopDesc::release_obj_field_put(int offset, oop value) { oop_store((volatile oop*)obj_field_addr(offset), value); } + +inline jbyte oopDesc::byte_field_acquire(int offset) const { return OrderAccess::load_acquire(byte_field_addr(offset)); } +inline void oopDesc::release_byte_field_put(int offset, jbyte contents) { OrderAccess::release_store(byte_field_addr(offset), contents); } + +inline jboolean oopDesc::bool_field_acquire(int offset) const { return OrderAccess::load_acquire(bool_field_addr(offset)); } +inline void oopDesc::release_bool_field_put(int offset, jboolean contents) { OrderAccess::release_store(bool_field_addr(offset), contents); } + +inline jchar oopDesc::char_field_acquire(int offset) const { return OrderAccess::load_acquire(char_field_addr(offset)); } +inline void oopDesc::release_char_field_put(int offset, jchar contents) { OrderAccess::release_store(char_field_addr(offset), contents); } + +inline jint oopDesc::int_field_acquire(int offset) const { return OrderAccess::load_acquire(int_field_addr(offset)); } +inline void oopDesc::release_int_field_put(int offset, jint contents) { OrderAccess::release_store(int_field_addr(offset), contents); } + +inline jshort oopDesc::short_field_acquire(int offset) const { return (jshort)OrderAccess::load_acquire(short_field_addr(offset)); } +inline void oopDesc::release_short_field_put(int offset, jshort contents) { OrderAccess::release_store(short_field_addr(offset), contents); } + +inline jlong oopDesc::long_field_acquire(int offset) const { return OrderAccess::load_acquire(long_field_addr(offset)); } +inline void oopDesc::release_long_field_put(int offset, jlong contents) { OrderAccess::release_store(long_field_addr(offset), contents); } + +inline jfloat oopDesc::float_field_acquire(int offset) const { return OrderAccess::load_acquire(float_field_addr(offset)); } +inline void oopDesc::release_float_field_put(int offset, jfloat contents) { OrderAccess::release_store(float_field_addr(offset), contents); } + +inline jdouble oopDesc::double_field_acquire(int offset) const { return OrderAccess::load_acquire(double_field_addr(offset)); } +inline void oopDesc::release_double_field_put(int offset, jdouble contents) { OrderAccess::release_store(double_field_addr(offset), contents); } + + +inline int oopDesc::size_given_klass(Klass* klass) { + int lh = klass->layout_helper(); + int s = lh >> LogHeapWordSize; // deliver size scaled by wordSize + + // lh is now a value computed at class initialization that may hint + // at the size. For instances, this is positive and equal to the + // size. For arrays, this is negative and provides log2 of the + // array element size. For other oops, it is zero and thus requires + // a virtual call. + // + // We go to all this trouble because the size computation is at the + // heart of phase 2 of mark-compaction, and called for every object, + // alive or dead. So the speed here is equal in importance to the + // speed of allocation. + + if (lh <= Klass::_lh_neutral_value) { + // The most common case is instances; fall through if so. + if (lh < Klass::_lh_neutral_value) { + // Second most common case is arrays. We have to fetch the + // length of the array, shift (multiply) it appropriately, + // up to wordSize, add the header, and align to object size. + size_t size_in_bytes; +#ifdef _M_IA64 + // The Windows Itanium Aug 2002 SDK hoists this load above + // the check for s < 0. An oop at the end of the heap will + // cause an access violation if this load is performed on a non + // array oop. Making the reference volatile prohibits this. + // (%%% please explain by what magic the length is actually fetched!) + volatile int *array_length; + array_length = (volatile int *)( (intptr_t)this + + arrayOopDesc::length_offset_in_bytes() ); + assert(array_length > 0, "Integer arithmetic problem somewhere"); + // Put into size_t to avoid overflow. + size_in_bytes = (size_t) array_length; + size_in_bytes = size_in_bytes << Klass::layout_helper_log2_element_size(lh); +#else + size_t array_length = (size_t) ((arrayOop)this)->length(); + size_in_bytes = array_length << Klass::layout_helper_log2_element_size(lh); +#endif + size_in_bytes += Klass::layout_helper_header_size(lh); + + // This code could be simplified, but by keeping array_header_in_bytes + // in units of bytes and doing it this way we can round up just once, + // skipping the intermediate round to HeapWordSize. Cast the result + // of round_to to size_t to guarantee unsigned division == right shift. + s = (int)((size_t)round_to(size_in_bytes, MinObjAlignmentInBytes) / + HeapWordSize); + + // UseParNewGC can change the length field of an "old copy" of an object + // array in the young gen so it indicates the stealable portion of + // an already copied array. This will cause the first disjunct below + // to fail if the sizes are computed across such a concurrent change. + // UseParNewGC also runs with promotion labs (which look like int + // filler arrays) which are subject to changing their declared size + // when finally retiring a PLAB; this also can cause the first disjunct + // to fail for another worker thread that is concurrently walking the block + // offset table. Both these invariant failures are benign for their + // current uses; we relax the assertion checking to cover these two cases below: + // is_objArray() && is_forwarded() // covers first scenario above + // || is_typeArray() // covers second scenario above + // If and when UseParallelGC uses the same obj array oop stealing/chunking + // technique, or when G1 is integrated (and currently uses this array chunking + // technique) we will need to suitably modify the assertion. + assert((s == klass->oop_size(this)) || + (((UseParNewGC || UseParallelGC) && + Universe::heap()->is_gc_active()) && + (is_typeArray() || + (is_objArray() && is_forwarded()))), + "wrong array object size"); + } else { + // Must be zero, so bite the bullet and take the virtual call. + s = klass->oop_size(this); + } + } + + assert(s % MinObjAlignment == 0, "alignment check"); + assert(s > 0, "Bad size calculated"); + return s; +} + + +inline int oopDesc::size() { + return size_given_klass(blueprint()); +} + +inline bool oopDesc::is_parsable() { + return blueprint()->oop_is_parsable(this); +} + + +inline void update_barrier_set(oop *p, oop v) { + assert(oopDesc::bs() != NULL, "Uninitialized bs in oop!"); + oopDesc::bs()->write_ref_field(p, v); +} + + +inline void oop_store(oop* p, oop v) { + if (always_do_update_barrier) { + oop_store((volatile oop*)p, v); + } else { + *p = v; + update_barrier_set(p, v); + } +} + +inline void oop_store(volatile oop* p, oop v) { + // Used by release_obj_field_put, so use release_store_ptr. + OrderAccess::release_store_ptr(p, v); + update_barrier_set((oop *)p, v); +} + +inline void oop_store_without_check(oop* p, oop v) { + // XXX YSR FIX ME!!! + if (always_do_update_barrier) { + oop_store(p, v); + } else { + assert(!Universe::heap()->barrier_set()->write_ref_needs_barrier(p, v), + "oop store without store check failed"); + *p = v; + } +} + +// When it absolutely has to get there. +inline void oop_store_without_check(volatile oop* p, oop v) { + // XXX YSR FIX ME!!! + if (always_do_update_barrier) { + oop_store(p, v); + } else { + assert(!Universe::heap()->barrier_set()-> + write_ref_needs_barrier((oop *)p, v), + "oop store without store check failed"); + OrderAccess::release_store_ptr(p, v); + } +} + + +// Used only for markSweep, scavenging +inline bool oopDesc::is_gc_marked() const { + return mark()->is_marked(); +} + +inline bool oopDesc::is_locked() const { + return mark()->is_locked(); +} + +inline bool oopDesc::is_unlocked() const { + return mark()->is_unlocked(); +} + +inline bool oopDesc::has_bias_pattern() const { + return mark()->has_bias_pattern(); +} + +inline bool check_obj_alignment(oop obj) { + return (intptr_t)obj % MinObjAlignmentInBytes == 0; +} + + +// used only for asserts +inline bool oopDesc::is_oop(bool ignore_mark_word) const { + oop obj = (oop) this; + if (!check_obj_alignment(obj)) return false; + if (!Universe::heap()->is_in_reserved(obj)) return false; + // obj is aligned and accessible in heap + // try to find metaclass cycle safely without seg faulting on bad input + // we should reach klassKlassObj by following klass link at most 3 times + for (int i = 0; i < 3; i++) { + obj = obj->klass(); + // klass should be aligned and in permspace + if (!check_obj_alignment(obj)) return false; + if (!Universe::heap()->is_in_permanent(obj)) return false; + } + if (obj != Universe::klassKlassObj()) { + // During a dump, the _klassKlassObj moved to a shared space. + if (DumpSharedSpaces && Universe::klassKlassObj()->is_shared()) { + return true; + } + return false; + } + + // Header verification: the mark is typically non-NULL. If we're + // at a safepoint, it must not be null. + // Outside of a safepoint, the header could be changing (for example, + // another thread could be inflating a lock on this object). + if (ignore_mark_word) { + return true; + } + if (mark() != NULL) { + return true; + } + return !SafepointSynchronize::is_at_safepoint(); +} + + +// used only for asserts +inline bool oopDesc::is_oop_or_null(bool ignore_mark_word) const { + return this == NULL ? true : is_oop(ignore_mark_word); +} + +#ifndef PRODUCT +// used only for asserts +inline bool oopDesc::is_unlocked_oop() const { + if (!Universe::heap()->is_in_reserved(this)) return false; + return mark()->is_unlocked(); +} + + +#endif // PRODUCT + +inline void oopDesc::follow_header() { + MarkSweep::mark_and_push((oop*)&_klass); +} + +inline void oopDesc::follow_contents() { + assert (is_gc_marked(), "should be marked"); + blueprint()->oop_follow_contents(this); +} + + +// Used by scavengers + +inline bool oopDesc::is_forwarded() const { + // The extra heap check is needed since the obj might be locked, in which case the + // mark would point to a stack location and have the sentinel bit cleared + return mark()->is_marked(); +} + + +// Used by scavengers +inline void oopDesc::forward_to(oop p) { + assert(Universe::heap()->is_in_reserved(p), + "forwarding to something not in heap"); + markOop m = markOopDesc::encode_pointer_as_mark(p); + assert(m->decode_pointer() == p, "encoding must be reversable"); + set_mark(m); +} + +// Used by parallel scavengers +inline bool oopDesc::cas_forward_to(oop p, markOop compare) { + assert(Universe::heap()->is_in_reserved(p), + "forwarding to something not in heap"); + markOop m = markOopDesc::encode_pointer_as_mark(p); + assert(m->decode_pointer() == p, "encoding must be reversable"); + return cas_set_mark(m, compare) == compare; +} + +// Note that the forwardee is not the same thing as the displaced_mark. +// The forwardee is used when copying during scavenge and mark-sweep. +// It does need to clear the low two locking- and GC-related bits. +inline oop oopDesc::forwardee() const { return (oop) mark()->decode_pointer(); } + + +inline bool oopDesc::has_displaced_mark() const { + return mark()->has_displaced_mark_helper(); +} + +inline markOop oopDesc::displaced_mark() const { + return mark()->displaced_mark_helper(); +} + +inline void oopDesc::set_displaced_mark(markOop m) { + mark()->set_displaced_mark_helper(m); +} + +// The following method needs to be MT safe. +inline int oopDesc::age() const { + assert(!is_forwarded(), "Attempt to read age from forwarded mark"); + if (has_displaced_mark()) { + return displaced_mark()->age(); + } else { + return mark()->age(); + } +} + +inline void oopDesc::incr_age() { + assert(!is_forwarded(), "Attempt to increment age of forwarded mark"); + if (has_displaced_mark()) { + set_displaced_mark(displaced_mark()->incr_age()); + } else { + set_mark(mark()->incr_age()); + } +} + + +inline intptr_t oopDesc::identity_hash() { + // Fast case; if the object is unlocked and the hash value is set, no locking is needed + // Note: The mark must be read into local variable to avoid concurrent updates. + markOop mrk = mark(); + if (mrk->is_unlocked() && !mrk->has_no_hash()) { + return mrk->hash(); + } else if (mrk->is_marked()) { + return mrk->hash(); + } else { + return slow_identity_hash(); + } +} + + +inline void oopDesc::oop_iterate_header(OopClosure* blk) { + blk->do_oop((oop*)&_klass); +} + + +inline void oopDesc::oop_iterate_header(OopClosure* blk, MemRegion mr) { + if (mr.contains(&_klass)) blk->do_oop((oop*)&_klass); +} + + +inline int oopDesc::adjust_pointers() { + debug_only(int check_size = size()); + int s = blueprint()->oop_adjust_pointers(this); + assert(s == check_size, "should be the same"); + return s; +} + +inline void oopDesc::adjust_header() { + MarkSweep::adjust_pointer((oop*)&_klass); +} + +#define OOP_ITERATE_DEFN(OopClosureType, nv_suffix) \ + \ +inline int oopDesc::oop_iterate(OopClosureType* blk) { \ + SpecializationStats::record_call(); \ + return blueprint()->oop_oop_iterate##nv_suffix(this, blk); \ +} \ + \ +inline int oopDesc::oop_iterate(OopClosureType* blk, MemRegion mr) { \ + SpecializationStats::record_call(); \ + return blueprint()->oop_oop_iterate##nv_suffix##_m(this, blk, mr); \ +} + +ALL_OOP_OOP_ITERATE_CLOSURES_1(OOP_ITERATE_DEFN) +ALL_OOP_OOP_ITERATE_CLOSURES_3(OOP_ITERATE_DEFN) + + +inline bool oopDesc::is_shared() const { + return CompactingPermGenGen::is_shared(this); +} + +inline bool oopDesc::is_shared_readonly() const { + return CompactingPermGenGen::is_shared_readonly(this); +} + +inline bool oopDesc::is_shared_readwrite() const { + return CompactingPermGenGen::is_shared_readwrite(this); +} diff --git a/hotspot/src/share/vm/oops/oop.inline2.hpp b/hotspot/src/share/vm/oops/oop.inline2.hpp new file mode 100644 index 00000000000..f938474783a --- /dev/null +++ b/hotspot/src/share/vm/oops/oop.inline2.hpp @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Implementation of all inlined member functions defined in oop.hpp +// We need a separate file to avoid circular references + +// Separate this out to break dependency. +inline bool oopDesc::is_perm() const { + return Universe::heap()->is_in_permanent(this); +} + +// Check for NULL also. +inline bool oopDesc::is_perm_or_null() const { + return this == NULL || is_perm(); +} diff --git a/hotspot/src/share/vm/oops/oop.pcgc.inline.hpp b/hotspot/src/share/vm/oops/oop.pcgc.inline.hpp new file mode 100644 index 00000000000..13f93e27d5b --- /dev/null +++ b/hotspot/src/share/vm/oops/oop.pcgc.inline.hpp @@ -0,0 +1,122 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void oopDesc::update_contents(ParCompactionManager* cm) { + // The klass field must be updated before anything else + // can be done. + DEBUG_ONLY(klassOopDesc* original_klass = klass()); + + // Can the option to update and/or copy be moved up in the + // call chain to avoid calling into here? + + if (PSParallelCompact::should_update_klass(klass())) { + update_header(); + assert(klass()->is_klass(), "Not updated correctly"); + } else { + assert(klass()->is_klass(), "Not updated"); + } + + Klass* new_klass = blueprint(); + if (!new_klass->oop_is_typeArray()) { + // It might contain oops beyond the header, so take the virtual call. + new_klass->oop_update_pointers(cm, this); + } + // Else skip it. The typeArrayKlass in the header never needs scavenging. +} + +inline void oopDesc::update_contents(ParCompactionManager* cm, + HeapWord* begin_limit, + HeapWord* end_limit) { + // The klass field must be updated before anything else + // can be done. + debug_only(klassOopDesc* original_klass = klass()); + + update_contents(cm, klass(), begin_limit, end_limit); +} + +inline void oopDesc::update_contents(ParCompactionManager* cm, + klassOop old_klass, + HeapWord* begin_limit, + HeapWord* end_limit) { + + klassOop updated_klass = + PSParallelCompact::summary_data().calc_new_klass(old_klass); + + // Needs to be boundary aware for the 64 bit case + // update_header(); + // The klass has moved. Is the location of the klass + // within the limits? + if ((((HeapWord*)&_klass) >= begin_limit) && + (((HeapWord*)&_klass) < end_limit)) { + set_klass(updated_klass); + } + + Klass* klass = updated_klass->klass_part(); + if (!klass->oop_is_typeArray()) { + // It might contain oops beyond the header, so take the virtual call. + klass->oop_update_pointers(cm, this, begin_limit, end_limit); + } + // Else skip it. The typeArrayKlass in the header never needs scavenging. +} + +inline void oopDesc::follow_contents(ParCompactionManager* cm) { + assert (PSParallelCompact::mark_bitmap()->is_marked(this), + "should be marked"); + blueprint()->oop_follow_contents(cm, this); +} + +// Used by parallel old GC. + +inline void oopDesc::follow_header(ParCompactionManager* cm) { + PSParallelCompact::mark_and_push(cm, (oop*)&_klass); +} + +inline oop oopDesc::forward_to_atomic(oop p) { + assert(ParNewGeneration::is_legal_forward_ptr(p), + "illegal forwarding pointer value."); + markOop oldMark = mark(); + markOop forwardPtrMark = markOopDesc::encode_pointer_as_mark(p); + markOop curMark; + + assert(forwardPtrMark->decode_pointer() == p, "encoding must be reversable"); + assert(sizeof(markOop) == sizeof(intptr_t), "CAS below requires this."); + + while (!is_forwarded()) { + curMark = (markOop)Atomic::cmpxchg_ptr(forwardPtrMark, &_mark, oldMark); + if (curMark == oldMark) { + assert(is_forwarded(), "the CAS should have succeeded."); + return NULL; + } + oldMark = curMark; + } + return forwardee(); +} + +inline void oopDesc::update_header() { + PSParallelCompact::adjust_pointer((oop*)&_klass); +} + +inline void oopDesc::update_header(HeapWord* beg_addr, HeapWord* end_addr) { + PSParallelCompact::adjust_pointer((oop*)&_klass, beg_addr, end_addr); +} diff --git a/hotspot/src/share/vm/oops/oop.psgc.inline.hpp b/hotspot/src/share/vm/oops/oop.psgc.inline.hpp new file mode 100644 index 00000000000..47dffada4f7 --- /dev/null +++ b/hotspot/src/share/vm/oops/oop.psgc.inline.hpp @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ParallelScavengeHeap methods + +inline void oopDesc::copy_contents(PSPromotionManager* pm) { + Klass* klass = blueprint(); + if (!klass->oop_is_typeArray()) { + // It might contain oops beyond the header, so take the virtual call. + klass->oop_copy_contents(pm, this); + } + // Else skip it. The typeArrayKlass in the header never needs scavenging. +} + +inline void oopDesc::push_contents(PSPromotionManager* pm) { + Klass* klass = blueprint(); + if (!klass->oop_is_typeArray()) { + // It might contain oops beyond the header, so take the virtual call. + klass->oop_push_contents(pm, this); + } + // Else skip it. The typeArrayKlass in the header never needs scavenging. +} diff --git a/hotspot/src/share/vm/oops/oopsHierarchy.cpp b/hotspot/src/share/vm/oops/oopsHierarchy.cpp new file mode 100644 index 00000000000..56bed7ec777 --- /dev/null +++ b/hotspot/src/share/vm/oops/oopsHierarchy.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_oopsHierarchy.cpp.incl" + +#ifdef CHECK_UNHANDLED_OOPS + +void oop::register_oop() { + assert (CheckUnhandledOops, "should only call when CheckUnhandledOops"); + if (!Universe::is_fully_initialized()) return; + // This gets expensive, which is why checking unhandled oops is on a switch. + Thread* t = ThreadLocalStorage::thread(); + if (t != NULL && t->is_Java_thread()) { + frame fr = os::current_frame(); + // This points to the oop creator, I guess current frame points to caller + assert (fr.pc(), "should point to a vm frame"); + t->unhandled_oops()->register_unhandled_oop(this, fr.pc()); + } +} + +void oop::unregister_oop() { + assert (CheckUnhandledOops, "should only call when CheckUnhandledOops"); + if (!Universe::is_fully_initialized()) return; + // This gets expensive, which is why checking unhandled oops is on a switch. + Thread* t = ThreadLocalStorage::thread(); + if (t != NULL && t->is_Java_thread()) { + t->unhandled_oops()->unregister_unhandled_oop(this); + } +} +#endif // CHECK_UNHANDLED_OOPS diff --git a/hotspot/src/share/vm/oops/oopsHierarchy.hpp b/hotspot/src/share/vm/oops/oopsHierarchy.hpp new file mode 100644 index 00000000000..6aab383bb6f --- /dev/null +++ b/hotspot/src/share/vm/oops/oopsHierarchy.hpp @@ -0,0 +1,180 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// OBJECT hierarchy +// This hierarchy is a representation hierarchy, i.e. if A is a superclass +// of B, A's representation is a prefix of B's representation. + +#ifndef CHECK_UNHANDLED_OOPS + +typedef class oopDesc* oop; +typedef class instanceOopDesc* instanceOop; +typedef class methodOopDesc* methodOop; +typedef class constMethodOopDesc* constMethodOop; +typedef class methodDataOopDesc* methodDataOop; +typedef class arrayOopDesc* arrayOop; +typedef class constantPoolOopDesc* constantPoolOop; +typedef class constantPoolCacheOopDesc* constantPoolCacheOop; +typedef class objArrayOopDesc* objArrayOop; +typedef class typeArrayOopDesc* typeArrayOop; +typedef class symbolOopDesc* symbolOop; +typedef class klassOopDesc* klassOop; +typedef class markOopDesc* markOop; +typedef class compiledICHolderOopDesc* compiledICHolderOop; + +#else + + +// When CHECK_UNHANDLED_OOPS is defined, an "oop" is a class with a +// carefully chosen set of constructors and conversion operators to go +// to and from the underlying oopDesc pointer type. +// +// Because oop and its subclasses Oop are class types, arbitrary +// conversions are not accepted by the compiler, and you may get a message +// about overloading ambiguity (between long and int is common when converting +// from a constant in 64 bit mode), or unable to convert from type to 'oop'. +// Applying a cast to one of these conversion operators first will get to the +// underlying oopDesc* type if appropriate. +// Converting NULL to oop to Handle implicit is no longer accepted by the +// compiler because there are too many steps in the conversion. Use Handle() +// instead, which generates less code anyway. + +class Thread; +typedef class markOopDesc* markOop; +class PromotedObject; + + +class oop { + oopDesc* _o; + + void register_oop(); + void unregister_oop(); + + // friend class markOop; +public: + void set_obj(const void* p) { + raw_set_obj(p); + if (CheckUnhandledOops) register_oop(); + } + void raw_set_obj(const void* p) { _o = (oopDesc*)p; } + + oop() { set_obj(NULL); } + oop(const volatile oop& o) { set_obj(o.obj()); } + oop(const void* p) { set_obj(p); } + oop(intptr_t i) { set_obj((void *)i); } +#ifdef _LP64 + oop(int i) { set_obj((void *)i); } +#endif + ~oop() { + if (CheckUnhandledOops) unregister_oop(); + } + + oopDesc* obj() const volatile { return _o; } + + // General access + oopDesc* operator->() const { return obj(); } + bool operator==(const oop o) const { return obj() == o.obj(); } + bool operator==(void *p) const { return obj() == p; } + bool operator!=(const oop o) const { return obj() != o.obj(); } + bool operator!=(void *p) const { return obj() != p; } + bool operator==(intptr_t p) const { return obj() == (oopDesc*)p; } + bool operator!=(intptr_t p) const { return obj() != (oopDesc*)p; } + + bool operator<(oop o) const { return obj() < o.obj(); } + bool operator>(oop o) const { return obj() > o.obj(); } + bool operator<=(oop o) const { return obj() <= o.obj(); } + bool operator>=(oop o) const { return obj() >= o.obj(); } + bool operator!() const { return !obj(); } + + // Cast + operator void* () const { return (void *)obj(); } + operator HeapWord* () const { return (HeapWord*)obj(); } + operator oopDesc* () const { return obj(); } + operator intptr_t* () const { return (intptr_t*)obj(); } + operator PromotedObject* () const { return (PromotedObject*)obj(); } + operator markOop () const { return markOop(obj()); } + + operator address () const { return (address)obj(); } + operator intptr_t () const { return (intptr_t)obj(); } + + // from javaCalls.cpp + operator jobject () const { return (jobject)obj(); } + // from javaClasses.cpp + operator JavaThread* () const { return (JavaThread*)obj(); } + // from jvm.cpp + operator jlong* () const { return (jlong*)obj(); } + + // from parNewGeneration and other things that want to get to the end of + // an oop for stuff (like constMethodKlass.cpp, objArrayKlass.cpp) + operator oop* () const { return (oop *)obj(); } +}; + +#define DEF_OOP(type) \ + class type##OopDesc; \ + class type##Oop : public oop { \ + public: \ + type##Oop() : oop() {} \ + type##Oop(const volatile oop& o) : oop(o) {} \ + type##Oop(const void* p) : oop(p) {} \ + operator type##OopDesc* () const { return (type##OopDesc*)obj(); } \ + type##OopDesc* operator->() const { \ + return (type##OopDesc*)obj(); \ + } \ + }; \ + +DEF_OOP(instance); +DEF_OOP(method); +DEF_OOP(methodData); +DEF_OOP(array); +DEF_OOP(constMethod); +DEF_OOP(constantPool); +DEF_OOP(constantPoolCache); +DEF_OOP(objArray); +DEF_OOP(typeArray); +DEF_OOP(symbol); +DEF_OOP(klass); +DEF_OOP(compiledICHolder); + +#endif // CHECK_UNHANDLED_OOPS + +// The klass hierarchy is separate from the oop hierarchy. + +class Klass; +class instanceKlass; +class instanceRefKlass; +class methodKlass; +class constMethodKlass; +class methodDataKlass; +class klassKlass; +class instanceKlassKlass; +class arrayKlassKlass; +class objArrayKlassKlass; +class typeArrayKlassKlass; +class arrayKlass; +class constantPoolKlass; +class constantPoolCacheKlass; +class objArrayKlass; +class typeArrayKlass; +class symbolKlass; +class compiledICHolderKlass; diff --git a/hotspot/src/share/vm/oops/symbolKlass.cpp b/hotspot/src/share/vm/oops/symbolKlass.cpp new file mode 100644 index 00000000000..68732510c1b --- /dev/null +++ b/hotspot/src/share/vm/oops/symbolKlass.cpp @@ -0,0 +1,232 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_symbolKlass.cpp.incl" + +symbolOop symbolKlass::allocate_symbol(u1* name, int len, TRAPS) { + // Don't allow symbol oops to be created which cannot fit in a symbolOop. + if (len > symbolOopDesc::max_length()) { + THROW_MSG_0(vmSymbols::java_lang_InternalError(), + "name is too long to represent"); + } + int size = symbolOopDesc::object_size(len); + symbolKlassHandle h_k(THREAD, as_klassOop()); + symbolOop sym = (symbolOop) + CollectedHeap::permanent_obj_allocate(h_k, size, CHECK_NULL); + assert(!sym->is_parsable(), "not expecting parsability yet."); + No_Safepoint_Verifier no_safepoint; + sym->set_utf8_length(len); + for (int i = 0; i < len; i++) { + sym->byte_at_put(i, name[i]); + } + // Let the first emptySymbol be created and + // ensure only one is ever created. + assert(sym->is_parsable() || Universe::emptySymbol() == NULL, + "should be parsable here."); + return sym; +} + +bool symbolKlass::allocate_symbols(int names_count, const char** names, + int* lengths, symbolOop* sym_oops, TRAPS) { + if (UseConcMarkSweepGC || UseParallelGC) { + // Concurrent GC needs to mark all the allocated symbol oops after + // the remark phase which isn't done below (except the first symbol oop). + // So return false which will let the symbols be allocated one by one. + // The parallel collector uses an object start array to find the + // start of objects on a dirty card. The object start array is not + // updated for the start of each symbol so is not precise. During + // object array verification this causes a verification failure. + // In a product build this causes extra searching for the start of + // a symbol. As with the concurrent collector a return of false will + // cause each symbol to be allocated separately and in the case + // of the parallel collector will cause the object + // start array to be updated. + return false; + } + + assert(names_count > 0, "can't allocate 0 symbols"); + + int total_size = 0; + int i, sizes[SymbolTable::symbol_alloc_batch_size]; + for (i=0; i symbolOopDesc::max_length()) { + return false; + } + int sz = symbolOopDesc::object_size(len); + sizes[i] = sz * HeapWordSize; + total_size += sz; + } + symbolKlassHandle h_k(THREAD, as_klassOop()); + HeapWord* base = Universe::heap()->permanent_mem_allocate(total_size); + if (base == NULL) { + return false; + } + + // CAN'T take any safepoint during the initialization of the symbol oops ! + No_Safepoint_Verifier nosafepoint; + + klassOop sk = h_k(); + int pos = 0; + for (i=0; iset_mark(markOopDesc::prototype()); + s->set_klass(sk); + s->set_utf8_length(lengths[i]); + const char* name = names[i]; + for (int j=0; jbyte_at_put(j, name[j]); + } + + assert(s->is_parsable(), "should be parsable here."); + + sym_oops[i] = s; + pos += sizes[i]; + } + return true; +} + +klassOop symbolKlass::create_klass(TRAPS) { + symbolKlass o; + KlassHandle h_this_klass(THREAD, Universe::klassKlassObj()); + KlassHandle k = base_create_klass(h_this_klass, header_size(), o.vtbl_value(), CHECK_NULL); + // Make sure size calculation is right + assert(k()->size() == align_object_size(header_size()), "wrong size for object"); +// java_lang_Class::create_mirror(k, CHECK_NULL); // Allocate mirror + return k(); +} + +int symbolKlass::oop_size(oop obj) const { + assert(obj->is_symbol(),"must be a symbol"); + symbolOop s = symbolOop(obj); + int size = s->object_size(); + return size; +} + +bool symbolKlass::oop_is_parsable(oop obj) const { + assert(obj->is_symbol(),"must be a symbol"); + symbolOop s = symbolOop(obj); + return s->object_is_parsable(); +} + +void symbolKlass::oop_follow_contents(oop obj) { + assert (obj->is_symbol(), "object must be symbol"); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::symbolKlassObj never moves. + // Note: do not follow next link here (see SymbolTable::follow_contents) +} + +#ifndef SERIALGC +void symbolKlass::oop_follow_contents(ParCompactionManager* cm, oop obj) { + assert (obj->is_symbol(), "object must be symbol"); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::symbolKlassObj never moves. + // Note: do not follow next link here (see SymbolTable::follow_contents) +} +#endif // SERIALGC + +int symbolKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert(obj->is_symbol(), "object must be symbol"); + symbolOop s = symbolOop(obj); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = s->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::symbolKlassObj never moves. + return size; +} + + +int symbolKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + assert(obj->is_symbol(), "object must be symbol"); + symbolOop s = symbolOop(obj); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = s->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::symbolKlassObj never moves. + return size; +} + + +int symbolKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_symbol(), "should be symbol"); + symbolOop s = symbolOop(obj); + // Get size before changing pointers. + // Don't call size() or oop_size() since that is a virtual call. + int size = s->object_size(); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::symbolKlassObj never moves. + return size; +} + + +#ifndef SERIALGC +void symbolKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_symbol(), "should be symbol"); +} + +void symbolKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_symbol(), "should be symbol"); +} + +int symbolKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert(obj->is_symbol(), "should be symbol"); + return symbolOop(obj)->object_size(); +} + +int symbolKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, HeapWord* end_addr) { + assert(obj->is_symbol(), "should be symbol"); + return symbolOop(obj)->object_size(); +} +#endif // SERIALGC + +#ifndef PRODUCT +// Printing + +void symbolKlass::oop_print_on(oop obj, outputStream* st) { + st->print("Symbol: '"); + symbolOop sym = symbolOop(obj); + for (int i = 0; i < sym->utf8_length(); i++) { + st->print("%c", sym->byte_at(i)); + } + st->print("'"); +} + +void symbolKlass::oop_print_value_on(oop obj, outputStream* st) { + symbolOop sym = symbolOop(obj); + st->print("'"); + for (int i = 0; i < sym->utf8_length(); i++) { + st->print("%c", sym->byte_at(i)); + } + st->print("'"); +} + +#endif //PRODUCT + +const char* symbolKlass::internal_name() const { + return "{symbol}"; +} diff --git a/hotspot/src/share/vm/oops/symbolKlass.hpp b/hotspot/src/share/vm/oops/symbolKlass.hpp new file mode 100644 index 00000000000..aca3e27175b --- /dev/null +++ b/hotspot/src/share/vm/oops/symbolKlass.hpp @@ -0,0 +1,74 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// a symbolKlass is the klass for a symbolOop + +class symbolKlass : public Klass { + friend class VMStructs; + private: + juint _alloc_size; // allocation profiling support + public: + // Allocation + DEFINE_ALLOCATE_PERMANENT(symbolKlass); + static klassOop create_klass(TRAPS); + symbolOop allocate_symbol(u1* name, int len, TRAPS); // Assumes no characters larger than 0x7F + bool allocate_symbols(int names_count, const char** names, int* lengths, symbolOop* sym_oops, TRAPS); + + // Test operation + bool oop_is_symbol() const { return true; } + + // Casting from klassOop + static symbolKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_symbol(), "cast to symbolKlass"); + return (symbolKlass*) k->klass_part(); + } + + static int header_size() { return oopDesc::header_size() + sizeof(symbolKlass)/HeapWordSize; } + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + int object_size() const { return align_object_size(header_size()); } + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + bool oop_is_parsable(oop obj) const; + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + // Allocation profiling support + juint alloc_size() const { return _alloc_size; } + void set_alloc_size(juint n) { _alloc_size = n; } + + // Iterators + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + +#ifndef PRODUCT + // Printing + void oop_print_value_on(oop obj, outputStream* st); + void oop_print_on(oop obj, outputStream* st); +#endif + const char* internal_name() const; +}; diff --git a/hotspot/src/share/vm/oops/symbolOop.cpp b/hotspot/src/share/vm/oops/symbolOop.cpp new file mode 100644 index 00000000000..9d343257082 --- /dev/null +++ b/hotspot/src/share/vm/oops/symbolOop.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_symbolOop.cpp.incl" + +bool symbolOopDesc::equals(const char* str, int len) const { + int l = utf8_length(); + if (l != len) return false; + while (l-- > 0) { + if (str[l] != (char) byte_at(l)) + return false; + } + assert(l == -1, "we should be at the beginning"); + return true; +} + +char* symbolOopDesc::as_C_string(char* buf, int size) const { + if (size > 0) { + int len = MIN2(size - 1, utf8_length()); + for (int i = 0; i < len; i++) { + buf[i] = byte_at(i); + } + buf[len] = '\0'; + } + return buf; +} + +char* symbolOopDesc::as_C_string() const { + int len = utf8_length(); + char* str = NEW_RESOURCE_ARRAY(char, len + 1); + return as_C_string(str, len + 1); +} + +char* symbolOopDesc::as_C_string_flexible_buffer(Thread* t, + char* buf, int size) const { + char* str; + int len = utf8_length(); + int buf_len = len + 1; + if (size < buf_len) { + str = NEW_RESOURCE_ARRAY(char, buf_len); + } else { + str = buf; + } + return as_C_string(str, buf_len); +} + +void symbolOopDesc::print_symbol_on(outputStream* st) { + st = st ? st : tty; + for (int index = 0; index < utf8_length(); index++) + st->put((char)byte_at(index)); +} + +jchar* symbolOopDesc::as_unicode(int& length) const { + symbolOopDesc* this_ptr = (symbolOopDesc*)this; + length = UTF8::unicode_length((char*)this_ptr->bytes(), utf8_length()); + jchar* result = NEW_RESOURCE_ARRAY(jchar, length); + if (length > 0) { + UTF8::convert_to_unicode((char*)this_ptr->bytes(), result, length); + } + return result; +} + +const char* symbolOopDesc::as_klass_external_name(char* buf, int size) const { + if (size > 0) { + char* str = as_C_string(buf, size); + int length = (int)strlen(str); + // Turn all '/'s into '.'s (also for array klasses) + for (int index = 0; index < length; index++) { + if (str[index] == '/') { + str[index] = '.'; + } + } + return str; + } else { + return buf; + } +} + +const char* symbolOopDesc::as_klass_external_name() const { + char* str = as_C_string(); + int length = (int)strlen(str); + // Turn all '/'s into '.'s (also for array klasses) + for (int index = 0; index < length; index++) { + if (str[index] == '/') { + str[index] = '.'; + } + } + return str; +} diff --git a/hotspot/src/share/vm/oops/symbolOop.hpp b/hotspot/src/share/vm/oops/symbolOop.hpp new file mode 100644 index 00000000000..49f95ec510d --- /dev/null +++ b/hotspot/src/share/vm/oops/symbolOop.hpp @@ -0,0 +1,118 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A symbolOop is a canonicalized string. +// All symbolOops reside in global symbolTable. +// See oopFactory::new_symbol for how to allocate a symbolOop + +class symbolOopDesc : public oopDesc { + friend class VMStructs; + private: + unsigned short _length; // number of UTF8 characters in the symbol + jbyte _body[1]; + + enum { + // max_symbol_length is constrained by type of _length + max_symbol_length = (1 << 16) -1 + }; + public: + + // Low-level access (used with care, since not GC-safe) + jbyte* base() { return &_body[0]; } + + + // Returns the largest size symbol we can safely hold. + static int max_length() { + return max_symbol_length; + } + + static int object_size(int length) { + int size = header_size() + (sizeof(unsigned short) + length + HeapWordSize - 1) / HeapWordSize; + return align_object_size(size); + } + + int object_size() { return object_size(utf8_length()); } + + int byte_at(int index) const { + assert(index >=0 && index < _length, "symbol index overflow"); + return ((symbolOopDesc*)this)->base()[index]; + } + + void byte_at_put(int index, int value) { + assert(index >=0 && index < _length, "symbol index overflow"); + ((symbolOopDesc*)this)->base()[index] = value; + } + + jbyte* bytes() { return base(); } + + int utf8_length() const { return _length; } + + void set_utf8_length(int len) { _length = len; } + + // Compares the symbol with a string + bool equals(const char* str, int len) const; + + // Three-way compare for sorting; returns -1/0/1 if receiver is than arg + // note that the ordering is not alfabetical + inline int fast_compare(symbolOop other) const; + + // Returns receiver converted to null-terminated UTF-8 string; string is + // allocated in resource area, or in the char buffer provided by caller. + char* as_C_string() const; + char* as_C_string(char* buf, int size) const; + // Use buf if needed buffer length is <= size. + char* as_C_string_flexible_buffer(Thread* t, char* buf, int size) const; + + + // Returns a null terminated utf8 string in a resource array + char* as_utf8() const { return as_C_string(); } + char* as_utf8_flexible_buffer(Thread* t, char* buf, int size) const { + return as_C_string_flexible_buffer(t, buf, size); + } + + jchar* as_unicode(int& length) const; + + // Treating this symbol as a class name, returns the Java name for the class. + // String is allocated in resource area if buffer is not provided. + // See Klass::external_name() + const char* as_klass_external_name() const; + const char* as_klass_external_name(char* buf, int size) const; + + bool object_is_parsable() const { + return (utf8_length() > 0 || (oop)this == Universe::emptySymbol()); + } + + // Printing + void print_symbol_on(outputStream* st = NULL); +}; + + +// Note: this comparison is used for vtable sorting only; it doesn't matter +// what order it defines, as long as it is a total, time-invariant order +// Since symbolOops are in permSpace, their relative order in memory never changes, +// so use address comparison for speed +int symbolOopDesc::fast_compare(symbolOop other) const { + return (((uintptr_t)this < (uintptr_t)other) ? -1 + : ((uintptr_t)this == (uintptr_t) other) ? 0 : 1); +} diff --git a/hotspot/src/share/vm/oops/typeArrayKlass.cpp b/hotspot/src/share/vm/oops/typeArrayKlass.cpp new file mode 100644 index 00000000000..d48f8b4f25b --- /dev/null +++ b/hotspot/src/share/vm/oops/typeArrayKlass.cpp @@ -0,0 +1,360 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_typeArrayKlass.cpp.incl" + +bool typeArrayKlass::compute_is_subtype_of(klassOop k) { + if (!k->klass_part()->oop_is_typeArray()) { + return arrayKlass::compute_is_subtype_of(k); + } + + typeArrayKlass* tak = typeArrayKlass::cast(k); + if (dimension() != tak->dimension()) return false; + + return element_type() == tak->element_type(); +} + +klassOop typeArrayKlass::create_klass(BasicType type, int scale, TRAPS) { + typeArrayKlass o; + + symbolHandle sym(symbolOop(NULL)); + // bootstrapping: don't create sym if symbolKlass not created yet + if (Universe::symbolKlassObj() != NULL) { + sym = oopFactory::new_symbol_handle(external_name(type), CHECK_NULL); + } + KlassHandle klassklass (THREAD, Universe::typeArrayKlassKlassObj()); + + arrayKlassHandle k = base_create_array_klass(o.vtbl_value(), header_size(), klassklass, CHECK_NULL); + typeArrayKlass* ak = typeArrayKlass::cast(k()); + ak->set_name(sym()); + ak->set_layout_helper(array_layout_helper(type)); + assert(scale == (1 << ak->log2_element_size()), "scale must check out"); + assert(ak->oop_is_javaArray(), "sanity"); + assert(ak->oop_is_typeArray(), "sanity"); + ak->set_max_length(arrayOopDesc::max_array_length(type)); + assert(k()->size() > header_size(), "bad size"); + + // Call complete_create_array_klass after all instance variables have been initialized. + KlassHandle super (THREAD, k->super()); + complete_create_array_klass(k, super, CHECK_NULL); + + return k(); +} + +typeArrayOop typeArrayKlass::allocate(int length, TRAPS) { + assert(log2_element_size() >= 0, "bad scale"); + if (length >= 0) { + if (length <= max_length()) { + size_t size = typeArrayOopDesc::object_size(layout_helper(), length); + KlassHandle h_k(THREAD, as_klassOop()); + typeArrayOop t; + CollectedHeap* ch = Universe::heap(); + if (size < ch->large_typearray_limit()) { + t = (typeArrayOop)CollectedHeap::array_allocate(h_k, (int)size, length, CHECK_NULL); + } else { + t = (typeArrayOop)CollectedHeap::large_typearray_allocate(h_k, (int)size, length, CHECK_NULL); + } + assert(t->is_parsable(), "Don't publish unless parsable"); + return t; + } else { + THROW_OOP_0(Universe::out_of_memory_error_array_size()); + } + } else { + THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); + } +} + +typeArrayOop typeArrayKlass::allocate_permanent(int length, TRAPS) { + if (length < 0) THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); + int size = typeArrayOopDesc::object_size(layout_helper(), length); + KlassHandle h_k(THREAD, as_klassOop()); + typeArrayOop t = (typeArrayOop) + CollectedHeap::permanent_array_allocate(h_k, size, length, CHECK_NULL); + assert(t->is_parsable(), "Can't publish until parsable"); + return t; +} + +oop typeArrayKlass::multi_allocate(int rank, jint* last_size, TRAPS) { + // For typeArrays this is only called for the last dimension + assert(rank == 1, "just checking"); + int length = *last_size; + return allocate(length, THREAD); +} + + +void typeArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS) { + assert(s->is_typeArray(), "must be type array"); + + // Check destination + if (!d->is_typeArray() || element_type() != typeArrayKlass::cast(d->klass())->element_type()) { + THROW(vmSymbols::java_lang_ArrayStoreException()); + } + + // Check is all offsets and lengths are non negative + if (src_pos < 0 || dst_pos < 0 || length < 0) { + THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + } + // Check if the ranges are valid + if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length()) + || (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) { + THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + } + + // This is an attempt to make the copy_array fast. + // NB: memmove takes care of overlapping memory segments. + // Potential problem: memmove is not guaranteed to be word atomic + // Revisit in Merlin + int l2es = log2_element_size(); + int ihs = array_header_in_bytes() / wordSize; + char* src = (char*) ((oop*)s + ihs) + (src_pos << l2es); + char* dst = (char*) ((oop*)d + ihs) + (dst_pos << l2es); + memmove(dst, src, length << l2es); +} + + +// create a klass of array holding typeArrays +klassOop typeArrayKlass::array_klass_impl(bool or_null, int n, TRAPS) { + typeArrayKlassHandle h_this(THREAD, as_klassOop()); + return array_klass_impl(h_this, or_null, n, THREAD); +} + +klassOop typeArrayKlass::array_klass_impl(typeArrayKlassHandle h_this, bool or_null, int n, TRAPS) { + int dimension = h_this->dimension(); + assert(dimension <= n, "check order of chain"); + if (dimension == n) + return h_this(); + + objArrayKlassHandle h_ak(THREAD, h_this->higher_dimension()); + if (h_ak.is_null()) { + if (or_null) return NULL; + + ResourceMark rm; + JavaThread *jt = (JavaThread *)THREAD; + { + MutexLocker mc(Compile_lock, THREAD); // for vtables + // Atomic create higher dimension and link into list + MutexLocker mu(MultiArray_lock, THREAD); + + h_ak = objArrayKlassHandle(THREAD, h_this->higher_dimension()); + if (h_ak.is_null()) { + klassOop oak = objArrayKlassKlass::cast( + Universe::objArrayKlassKlassObj())->allocate_objArray_klass( + dimension + 1, h_this, CHECK_NULL); + h_ak = objArrayKlassHandle(THREAD, oak); + h_ak->set_lower_dimension(h_this()); + h_this->set_higher_dimension(h_ak()); + assert(h_ak->oop_is_objArray(), "incorrect initialization of objArrayKlass"); + } + } + } else { + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } + if (or_null) { + return h_ak->array_klass_or_null(n); + } + return h_ak->array_klass(n, CHECK_NULL); +} + +klassOop typeArrayKlass::array_klass_impl(bool or_null, TRAPS) { + return array_klass_impl(or_null, dimension() + 1, THREAD); +} + +int typeArrayKlass::oop_size(oop obj) const { + assert(obj->is_typeArray(),"must be a type array"); + typeArrayOop t = typeArrayOop(obj); + return t->object_size(); +} + +void typeArrayKlass::oop_follow_contents(oop obj) { + assert(obj->is_typeArray(),"must be a type array"); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::typeArrayKlass never moves. +} + +#ifndef SERIALGC +void typeArrayKlass::oop_follow_contents(ParCompactionManager* cm, oop obj) { + assert(obj->is_typeArray(),"must be a type array"); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::typeArrayKlass never moves. +} +#endif // SERIALGC + +int typeArrayKlass::oop_adjust_pointers(oop obj) { + assert(obj->is_typeArray(),"must be a type array"); + typeArrayOop t = typeArrayOop(obj); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::typeArrayKlass never moves. + return t->object_size(); +} + +int typeArrayKlass::oop_oop_iterate(oop obj, OopClosure* blk) { + assert(obj->is_typeArray(),"must be a type array"); + typeArrayOop t = typeArrayOop(obj); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::typeArrayKlass never moves. + return t->object_size(); +} + +int typeArrayKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { + assert(obj->is_typeArray(),"must be a type array"); + typeArrayOop t = typeArrayOop(obj); + // Performance tweak: We skip iterating over the klass pointer since we + // know that Universe::typeArrayKlass never moves. + return t->object_size(); +} + +#ifndef SERIALGC +void typeArrayKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_typeArray(),"must be a type array"); +} + +void typeArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_typeArray(),"must be a type array"); +} + +int +typeArrayKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { + assert(obj->is_typeArray(),"must be a type array"); + return typeArrayOop(obj)->object_size(); +} + +int +typeArrayKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, + HeapWord* beg_addr, HeapWord* end_addr) { + assert(obj->is_typeArray(),"must be a type array"); + return typeArrayOop(obj)->object_size(); +} +#endif // SERIALGC + +void typeArrayKlass::initialize(TRAPS) { + // Nothing to do. Having this function is handy since objArrayKlasses can be + // initialized by calling initialize on their bottom_klass, see objArrayKlass::initialize +} + +const char* typeArrayKlass::external_name(BasicType type) { + switch (type) { + case T_BOOLEAN: return "[Z"; + case T_CHAR: return "[C"; + case T_FLOAT: return "[F"; + case T_DOUBLE: return "[D"; + case T_BYTE: return "[B"; + case T_SHORT: return "[S"; + case T_INT: return "[I"; + case T_LONG: return "[J"; + default: ShouldNotReachHere(); + } + return NULL; +} + +#ifndef PRODUCT +// Printing + +static void print_boolean_array(typeArrayOop ta, int print_len, outputStream* st) { + for (int index = 0; index < print_len; index++) { + st->print_cr(" - %3d: %s", index, (ta->bool_at(index) == 0) ? "false" : "true"); + } +} + + +static void print_char_array(typeArrayOop ta, int print_len, outputStream* st) { + for (int index = 0; index < print_len; index++) { + jchar c = ta->char_at(index); + st->print_cr(" - %3d: %x %c", index, c, isprint(c) ? c : ' '); + } +} + + +static void print_float_array(typeArrayOop ta, int print_len, outputStream* st) { + for (int index = 0; index < print_len; index++) { + st->print_cr(" - %3d: %g", index, ta->float_at(index)); + } +} + + +static void print_double_array(typeArrayOop ta, int print_len, outputStream* st) { + for (int index = 0; index < print_len; index++) { + st->print_cr(" - %3d: %g", index, ta->double_at(index)); + } +} + + +static void print_byte_array(typeArrayOop ta, int print_len, outputStream* st) { + for (int index = 0; index < print_len; index++) { + jbyte c = ta->byte_at(index); + st->print_cr(" - %3d: %x %c", index, c, isprint(c) ? c : ' '); + } +} + + +static void print_short_array(typeArrayOop ta, int print_len, outputStream* st) { + for (int index = 0; index < print_len; index++) { + int v = ta->ushort_at(index); + st->print_cr(" - %3d: 0x%x\t %d", index, v, v); + } +} + + +static void print_int_array(typeArrayOop ta, int print_len, outputStream* st) { + for (int index = 0; index < print_len; index++) { + jint v = ta->int_at(index); + st->print_cr(" - %3d: 0x%x %d", index, v, v); + } +} + + +static void print_long_array(typeArrayOop ta, int print_len, outputStream* st) { + for (int index = 0; index < print_len; index++) { + jlong v = ta->long_at(index); + st->print_cr(" - %3d: 0x%x 0x%x", index, high(v), low(v)); + } +} + + +void typeArrayKlass::oop_print_on(oop obj, outputStream* st) { + arrayKlass::oop_print_on(obj, st); + typeArrayOop ta = typeArrayOop(obj); + int print_len = MIN2((intx) ta->length(), MaxElementPrintSize); + switch (element_type()) { + case T_BOOLEAN: print_boolean_array(ta, print_len, st); break; + case T_CHAR: print_char_array(ta, print_len, st); break; + case T_FLOAT: print_float_array(ta, print_len, st); break; + case T_DOUBLE: print_double_array(ta, print_len, st); break; + case T_BYTE: print_byte_array(ta, print_len, st); break; + case T_SHORT: print_short_array(ta, print_len, st); break; + case T_INT: print_int_array(ta, print_len, st); break; + case T_LONG: print_long_array(ta, print_len, st); break; + default: ShouldNotReachHere(); + } + int remaining = ta->length() - print_len; + if (remaining > 0) { + tty->print_cr(" - <%d more elements, increase MaxElementPrintSize to print>", remaining); + } +} + +#endif // PRODUCT + +const char* typeArrayKlass::internal_name() const { + return Klass::external_name(); +} diff --git a/hotspot/src/share/vm/oops/typeArrayKlass.hpp b/hotspot/src/share/vm/oops/typeArrayKlass.hpp new file mode 100644 index 00000000000..72336fb8db0 --- /dev/null +++ b/hotspot/src/share/vm/oops/typeArrayKlass.hpp @@ -0,0 +1,103 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A typeArrayKlass is the klass of a typeArray +// It contains the type and size of the elements + +class typeArrayKlass : public arrayKlass { + friend class VMStructs; + private: + jint _max_length; // maximum number of elements allowed in an array + public: + // instance variables + jint max_length() { return _max_length; } + void set_max_length(jint m) { _max_length = m; } + + // testers + bool oop_is_typeArray_slow() const { return true; } + + // klass allocation + DEFINE_ALLOCATE_PERMANENT(typeArrayKlass); + static klassOop create_klass(BasicType type, int scale, TRAPS); + + int oop_size(oop obj) const; + int klass_oop_size() const { return object_size(); } + + bool compute_is_subtype_of(klassOop k); + + // Allocation + typeArrayOop allocate(int length, TRAPS); + typeArrayOop allocate_permanent(int length, TRAPS); // used for class file structures + oop multi_allocate(int rank, jint* sizes, TRAPS); + + // Copying + void copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS); + + // Iteration + int oop_oop_iterate(oop obj, OopClosure* blk); + int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); + + // Garbage collection + void oop_follow_contents(oop obj); + int oop_adjust_pointers(oop obj); + + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + + protected: + // Find n'th dimensional array + virtual klassOop array_klass_impl(bool or_null, int n, TRAPS); + + // Returns the array class with this class as element type + virtual klassOop array_klass_impl(bool or_null, TRAPS); + + public: + // Casting from klassOop + static typeArrayKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_typeArray_slow(), "cast to typeArrayKlass"); + return (typeArrayKlass*) k->klass_part(); + } + + // Naming + static const char* external_name(BasicType type); + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(typeArrayKlass)/HeapWordSize; } + int object_size() const { return arrayKlass::object_size(header_size()); } + + // Initialization (virtual from Klass) + void initialize(TRAPS); + + private: + // Helpers + static klassOop array_klass_impl(typeArrayKlassHandle h_this, bool or_null, int n, TRAPS); + +#ifndef PRODUCT + public: + // Printing + void oop_print_on(oop obj, outputStream* st); +#endif + public: + const char* internal_name() const; +}; diff --git a/hotspot/src/share/vm/oops/typeArrayKlassKlass.cpp b/hotspot/src/share/vm/oops/typeArrayKlassKlass.cpp new file mode 100644 index 00000000000..d987f0d14be --- /dev/null +++ b/hotspot/src/share/vm/oops/typeArrayKlassKlass.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_typeArrayKlassKlass.cpp.incl" + +klassOop typeArrayKlassKlass::create_klass(TRAPS) { + typeArrayKlassKlass o; + KlassHandle h_this_klass(THREAD, Universe::klassKlassObj()); + KlassHandle k = base_create_klass(h_this_klass, header_size(), o.vtbl_value(), CHECK_NULL); + assert(k()->size() == align_object_size(header_size()), "wrong size for object"); + java_lang_Class::create_mirror(k, CHECK_NULL); // Allocate mirror + return k(); +} + + +#ifndef PRODUCT + +// Printing + +void typeArrayKlassKlass::oop_print_on(oop obj, outputStream* st) { + assert(obj->is_klass(), "must be klass"); + oop_print_value_on(obj, st); + Klass:: oop_print_on(obj, st); +} + + +void typeArrayKlassKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_klass(), "must be klass"); + st->print("{type array "); + switch (typeArrayKlass::cast(klassOop(obj))->element_type()) { + case T_BOOLEAN: st->print("bool"); break; + case T_CHAR: st->print("char"); break; + case T_FLOAT: st->print("float"); break; + case T_DOUBLE: st->print("double"); break; + case T_BYTE: st->print("byte"); break; + case T_SHORT: st->print("short"); break; + case T_INT: st->print("int"); break; + case T_LONG: st->print("long"); break; + default: ShouldNotReachHere(); + } + st->print("}"); +} + +#endif + +const char* typeArrayKlassKlass::internal_name() const { + return "{type array class}"; +} diff --git a/hotspot/src/share/vm/oops/typeArrayKlassKlass.hpp b/hotspot/src/share/vm/oops/typeArrayKlassKlass.hpp new file mode 100644 index 00000000000..fabda84467a --- /dev/null +++ b/hotspot/src/share/vm/oops/typeArrayKlassKlass.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A typeArrayKlassKlass is the klass of a typeArrayKlass + +class typeArrayKlassKlass : public arrayKlassKlass { + public: + // Testing + bool oop_is_typeArrayKlass() const { return true; } + + // Dispatched operation + int oop_size(oop obj) const { return typeArrayKlass::cast(klassOop(obj))->object_size(); } + int klass_oop_size() const { return object_size(); } + + // Allocation + DEFINE_ALLOCATE_PERMANENT(typeArrayKlassKlass); + static klassOop create_klass(TRAPS); + + // Casting from klassOop + static typeArrayKlassKlass* cast(klassOop k) { + assert(k->klass_part()->oop_is_klass(), "cast to typeArrayKlassKlass"); + return (typeArrayKlassKlass*) k->klass_part(); + } + + // Sizing + static int header_size() { return oopDesc::header_size() + sizeof(typeArrayKlassKlass)/HeapWordSize; } + int object_size() const { return align_object_size(header_size()); } + +#ifndef PRODUCT + public: + // Printing + void oop_print_on(oop obj, outputStream* st); + void oop_print_value_on(oop obj, outputStream* st); +#endif + public: + const char* internal_name() const; +}; diff --git a/hotspot/src/share/vm/oops/typeArrayOop.cpp b/hotspot/src/share/vm/oops/typeArrayOop.cpp new file mode 100644 index 00000000000..6184e822c41 --- /dev/null +++ b/hotspot/src/share/vm/oops/typeArrayOop.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_typeArrayOop.cpp.incl" + +// <> diff --git a/hotspot/src/share/vm/oops/typeArrayOop.hpp b/hotspot/src/share/vm/oops/typeArrayOop.hpp new file mode 100644 index 00000000000..8028d1eae50 --- /dev/null +++ b/hotspot/src/share/vm/oops/typeArrayOop.hpp @@ -0,0 +1,143 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A typeArrayOop is an array containing basic types (non oop elements). +// It is used for arrays of {characters, singles, doubles, bytes, shorts, integers, longs} +#include + +class typeArrayOopDesc : public arrayOopDesc { + protected: + jchar* char_base() const { return (jchar*) base(T_CHAR); } + jboolean* bool_base() const { return (jboolean*)base(T_BOOLEAN); } + jbyte* byte_base() const { return (jbyte*) base(T_BYTE); } + jint* int_base() const { return (jint*) base(T_INT); } + jlong* long_base() const { return (jlong*) base(T_LONG); } + jshort* short_base() const { return (jshort*) base(T_SHORT); } + jfloat* float_base() const { return (jfloat*) base(T_FLOAT); } + jdouble* double_base() const { return (jdouble*) base(T_DOUBLE); } + + friend class typeArrayKlass; + + public: + jbyte* byte_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return &byte_base()[which]; + } + + jboolean* bool_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return &bool_base()[which]; + } + + jchar* char_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return &char_base()[which]; + } + + jint* int_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return &int_base()[which]; + } + + jshort* short_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return &short_base()[which]; + } + + jushort* ushort_at_addr(int which) const { // for field descriptor arrays + assert(is_within_bounds(which), "index out of bounds"); + return (jushort*) &short_base()[which]; + } + + jlong* long_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return &long_base()[which]; + } + + jfloat* float_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return &float_base()[which]; + } + + jdouble* double_at_addr(int which) const { + assert(is_within_bounds(which), "index out of bounds"); + return &double_base()[which]; + } + + jbyte byte_at(int which) const { return *byte_at_addr(which); } + void byte_at_put(int which, jbyte contents) { *byte_at_addr(which) = contents; } + + jboolean bool_at(int which) const { return *bool_at_addr(which); } + void bool_at_put(int which, jboolean contents) { *bool_at_addr(which) = contents; } + + jchar char_at(int which) const { return *char_at_addr(which); } + void char_at_put(int which, jchar contents) { *char_at_addr(which) = contents; } + + jint int_at(int which) const { return *int_at_addr(which); } + void int_at_put(int which, jint contents) { *int_at_addr(which) = contents; } + + jshort short_at(int which) const { return *short_at_addr(which); } + void short_at_put(int which, jshort contents) { *short_at_addr(which) = contents; } + + jushort ushort_at(int which) const { return *ushort_at_addr(which); } + void ushort_at_put(int which, jushort contents) { *ushort_at_addr(which) = contents; } + + jlong long_at(int which) const { return *long_at_addr(which); } + void long_at_put(int which, jlong contents) { *long_at_addr(which) = contents; } + + jfloat float_at(int which) const { return *float_at_addr(which); } + void float_at_put(int which, jfloat contents) { *float_at_addr(which) = contents; } + + jdouble double_at(int which) const { return *double_at_addr(which); } + void double_at_put(int which, jdouble contents) { *double_at_addr(which) = contents; } + + jbyte byte_at_acquire(int which) const { return OrderAccess::load_acquire(byte_at_addr(which)); } + void release_byte_at_put(int which, jbyte contents) { OrderAccess::release_store(byte_at_addr(which), contents); } + + // Sizing + + // Returns the number of words necessary to hold an array of "len" + // elements each of the given "byte_size". + private: + static int object_size(int lh, int length) { + int instance_header_size = Klass::layout_helper_header_size(lh); + int element_shift = Klass::layout_helper_log2_element_size(lh); + DEBUG_ONLY(BasicType etype = Klass::layout_helper_element_type(lh)); + assert(length <= arrayOopDesc::max_array_length(etype), "no overflow"); + + julong size_in_bytes = length; + size_in_bytes <<= element_shift; + size_in_bytes += instance_header_size; + julong size_in_words = ((size_in_bytes + (HeapWordSize-1)) >> LogHeapWordSize); + assert(size_in_words <= (julong)max_jint, "no overflow"); + + return align_object_size((intptr_t)size_in_words); + } + + public: + int object_size() { + typeArrayKlass* tk = typeArrayKlass::cast(klass()); + return object_size(tk->layout_helper(), length()); + } +}; diff --git a/hotspot/src/share/vm/opto/addnode.cpp b/hotspot/src/share/vm/opto/addnode.cpp new file mode 100644 index 00000000000..42a17c997dc --- /dev/null +++ b/hotspot/src/share/vm/opto/addnode.cpp @@ -0,0 +1,871 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +#include "incls/_precompiled.incl" +#include "incls/_addnode.cpp.incl" + +#define MAXFLOAT ((float)3.40282346638528860e+38) + +// Classic Add functionality. This covers all the usual 'add' behaviors for +// an algebraic ring. Add-integer, add-float, add-double, and binary-or are +// all inherited from this class. The various identity values are supplied +// by virtual functions. + + +//============================================================================= +//------------------------------hash------------------------------------------- +// Hash function over AddNodes. Needs to be commutative; i.e., I swap +// (commute) inputs to AddNodes willy-nilly so the hash function must return +// the same value in the presence of edge swapping. +uint AddNode::hash() const { + return (uintptr_t)in(1) + (uintptr_t)in(2) + Opcode(); +} + +//------------------------------Identity--------------------------------------- +// If either input is a constant 0, return the other input. +Node *AddNode::Identity( PhaseTransform *phase ) { + const Type *zero = add_id(); // The additive identity + if( phase->type( in(1) )->higher_equal( zero ) ) return in(2); + if( phase->type( in(2) )->higher_equal( zero ) ) return in(1); + return this; +} + +//------------------------------commute---------------------------------------- +// Commute operands to move loads and constants to the right. +static bool commute( Node *add, int con_left, int con_right ) { + Node *in1 = add->in(1); + Node *in2 = add->in(2); + + // Convert "1+x" into "x+1". + // Right is a constant; leave it + if( con_right ) return false; + // Left is a constant; move it right. + if( con_left ) { + add->swap_edges(1, 2); + return true; + } + + // Convert "Load+x" into "x+Load". + // Now check for loads + if( in2->is_Load() ) return false; + // Left is a Load and Right is not; move it right. + if( in1->is_Load() ) { + add->swap_edges(1, 2); + return true; + } + + PhiNode *phi; + // Check for tight loop increments: Loop-phi of Add of loop-phi + if( in1->is_Phi() && (phi = in1->as_Phi()) && !phi->is_copy() && phi->region()->is_Loop() && phi->in(2)==add) + return false; + if( in2->is_Phi() && (phi = in2->as_Phi()) && !phi->is_copy() && phi->region()->is_Loop() && phi->in(2)==add){ + add->swap_edges(1, 2); + return true; + } + + // Otherwise, sort inputs (commutativity) to help value numbering. + if( in1->_idx > in2->_idx ) { + add->swap_edges(1, 2); + return true; + } + return false; +} + +//------------------------------Idealize--------------------------------------- +// If we get here, we assume we are associative! +Node *AddNode::Ideal(PhaseGVN *phase, bool can_reshape) { + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + int con_left = t1->singleton(); + int con_right = t2->singleton(); + + // Check for commutative operation desired + if( commute(this,con_left,con_right) ) return this; + + AddNode *progress = NULL; // Progress flag + + // Convert "(x+1)+2" into "x+(1+2)". If the right input is a + // constant, and the left input is an add of a constant, flatten the + // expression tree. + Node *add1 = in(1); + Node *add2 = in(2); + int add1_op = add1->Opcode(); + int this_op = Opcode(); + if( con_right && t2 != Type::TOP && // Right input is a constant? + add1_op == this_op ) { // Left input is an Add? + + // Type of left _in right input + const Type *t12 = phase->type( add1->in(2) ); + if( t12->singleton() && t12 != Type::TOP ) { // Left input is an add of a constant? + // Check for rare case of closed data cycle which can happen inside + // unreachable loops. In these cases the computation is undefined. +#ifdef ASSERT + Node *add11 = add1->in(1); + int add11_op = add11->Opcode(); + if( (add1 == add1->in(1)) + || (add11_op == this_op && add11->in(1) == add1) ) { + assert(false, "dead loop in AddNode::Ideal"); + } +#endif + // The Add of the flattened expression + Node *x1 = add1->in(1); + Node *x2 = phase->makecon( add1->as_Add()->add_ring( t2, t12 )); + PhaseIterGVN *igvn = phase->is_IterGVN(); + if( igvn ) { + set_req_X(2,x2,igvn); + set_req_X(1,x1,igvn); + } else { + set_req(2,x2); + set_req(1,x1); + } + progress = this; // Made progress + add1 = in(1); + add1_op = add1->Opcode(); + } + } + + // Convert "(x+1)+y" into "(x+y)+1". Push constants down the expression tree. + if( add1_op == this_op && !con_right ) { + Node *a12 = add1->in(2); + const Type *t12 = phase->type( a12 ); + if( t12->singleton() && t12 != Type::TOP && (add1 != add1->in(1)) ) { + add2 = add1->clone(); + add2->set_req(2, in(2)); + add2 = phase->transform(add2); + set_req(1, add2); + set_req(2, a12); + progress = this; + add2 = a12; + } + } + + // Convert "x+(y+1)" into "(x+y)+1". Push constants down the expression tree. + int add2_op = add2->Opcode(); + if( add2_op == this_op && !con_left ) { + Node *a22 = add2->in(2); + const Type *t22 = phase->type( a22 ); + if( t22->singleton() && t22 != Type::TOP && (add2 != add2->in(1)) ) { + Node *addx = add2->clone(); + addx->set_req(1, in(1)); + addx->set_req(2, add2->in(1)); + addx = phase->transform(addx); + set_req(1, addx); + set_req(2, a22); + progress = this; + } + } + + return progress; +} + +//------------------------------Value----------------------------------------- +// An add node sums it's two _in. If one input is an RSD, we must mixin +// the other input's symbols. +const Type *AddNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Either input is BOTTOM ==> the result is the local BOTTOM + const Type *bot = bottom_type(); + if( (t1 == bot) || (t2 == bot) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return bot; + + // Check for an addition involving the additive identity + const Type *tadd = add_of_identity( t1, t2 ); + if( tadd ) return tadd; + + return add_ring(t1,t2); // Local flavor of type addition +} + +//------------------------------add_identity----------------------------------- +// Check for addition of the identity +const Type *AddNode::add_of_identity( const Type *t1, const Type *t2 ) const { + const Type *zero = add_id(); // The additive identity + if( t1->higher_equal( zero ) ) return t2; + if( t2->higher_equal( zero ) ) return t1; + + return NULL; +} + + +//============================================================================= +//------------------------------Idealize--------------------------------------- +Node *AddINode::Ideal(PhaseGVN *phase, bool can_reshape) { + int op1 = in(1)->Opcode(); + int op2 = in(2)->Opcode(); + // Fold (con1-x)+con2 into (con1+con2)-x + if( op1 == Op_SubI ) { + const Type *t_sub1 = phase->type( in(1)->in(1) ); + const Type *t_2 = phase->type( in(2) ); + if( t_sub1->singleton() && t_2->singleton() && t_sub1 != Type::TOP && t_2 != Type::TOP ) + return new (phase->C, 3) SubINode(phase->makecon( add_ring( t_sub1, t_2 ) ), + in(1)->in(2) ); + // Convert "(a-b)+(c-d)" into "(a+c)-(b+d)" + if( op2 == Op_SubI ) { + // Check for dead cycle: d = (a-b)+(c-d) + assert( in(1)->in(2) != this && in(2)->in(2) != this, + "dead loop in AddINode::Ideal" ); + Node *sub = new (phase->C, 3) SubINode(NULL, NULL); + sub->init_req(1, phase->transform(new (phase->C, 3) AddINode(in(1)->in(1), in(2)->in(1) ) )); + sub->init_req(2, phase->transform(new (phase->C, 3) AddINode(in(1)->in(2), in(2)->in(2) ) )); + return sub; + } + } + + // Convert "x+(0-y)" into "(x-y)" + if( op2 == Op_SubI && phase->type(in(2)->in(1)) == TypeInt::ZERO ) + return new (phase->C, 3) SubINode(in(1), in(2)->in(2) ); + + // Convert "(0-y)+x" into "(x-y)" + if( op1 == Op_SubI && phase->type(in(1)->in(1)) == TypeInt::ZERO ) + return new (phase->C, 3) SubINode( in(2), in(1)->in(2) ); + + // Convert (x>>>z)+y into (x+(y<>>z for small constant z and y. + // Helps with array allocation math constant folding + // See 4790063: + // Unrestricted transformation is unsafe for some runtime values of 'x' + // ( x == 0, z == 1, y == -1 ) fails + // ( x == -5, z == 1, y == 1 ) fails + // Transform works for small z and small negative y when the addition + // (x + (y << z)) does not cross zero. + // Implement support for negative y and (x >= -(y << z)) + // Have not observed cases where type information exists to support + // positive y and (x <= -(y << z)) + if( op1 == Op_URShiftI && op2 == Op_ConI && + in(1)->in(2)->Opcode() == Op_ConI ) { + jint z = phase->type( in(1)->in(2) )->is_int()->get_con() & 0x1f; // only least significant 5 bits matter + jint y = phase->type( in(2) )->is_int()->get_con(); + + if( z < 5 && -5 < y && y < 0 ) { + const Type *t_in11 = phase->type(in(1)->in(1)); + if( t_in11 != Type::TOP && (t_in11->is_int()->_lo >= -(y << z)) ) { + Node *a = phase->transform( new (phase->C, 3) AddINode( in(1)->in(1), phase->intcon(y<C, 3) URShiftINode( a, in(1)->in(2) ); + } + } + } + + return AddNode::Ideal(phase, can_reshape); +} + + +//------------------------------Identity--------------------------------------- +// Fold (x-y)+y OR y+(x-y) into x +Node *AddINode::Identity( PhaseTransform *phase ) { + if( in(1)->Opcode() == Op_SubI && phase->eqv(in(1)->in(2),in(2)) ) { + return in(1)->in(1); + } + else if( in(2)->Opcode() == Op_SubI && phase->eqv(in(2)->in(2),in(1)) ) { + return in(2)->in(1); + } + return AddNode::Identity(phase); +} + + +//------------------------------add_ring--------------------------------------- +// Supplied function returns the sum of the inputs. Guaranteed never +// to be passed a TOP or BOTTOM type, these are filtered out by +// pre-check. +const Type *AddINode::add_ring( const Type *t0, const Type *t1 ) const { + const TypeInt *r0 = t0->is_int(); // Handy access + const TypeInt *r1 = t1->is_int(); + int lo = r0->_lo + r1->_lo; + int hi = r0->_hi + r1->_hi; + if( !(r0->is_con() && r1->is_con()) ) { + // Not both constants, compute approximate result + if( (r0->_lo & r1->_lo) < 0 && lo >= 0 ) { + lo = min_jint; hi = max_jint; // Underflow on the low side + } + if( (~(r0->_hi | r1->_hi)) < 0 && hi < 0 ) { + lo = min_jint; hi = max_jint; // Overflow on the high side + } + if( lo > hi ) { // Handle overflow + lo = min_jint; hi = max_jint; + } + } else { + // both constants, compute precise result using 'lo' and 'hi' + // Semantics define overflow and underflow for integer addition + // as expected. In particular: 0x80000000 + 0x80000000 --> 0x0 + } + return TypeInt::make( lo, hi, MAX2(r0->_widen,r1->_widen) ); +} + + +//============================================================================= +//------------------------------Idealize--------------------------------------- +Node *AddLNode::Ideal(PhaseGVN *phase, bool can_reshape) { + int op1 = in(1)->Opcode(); + int op2 = in(2)->Opcode(); + // Fold (con1-x)+con2 into (con1+con2)-x + if( op1 == Op_SubL ) { + const Type *t_sub1 = phase->type( in(1)->in(1) ); + const Type *t_2 = phase->type( in(2) ); + if( t_sub1->singleton() && t_2->singleton() && t_sub1 != Type::TOP && t_2 != Type::TOP ) + return new (phase->C, 3) SubLNode(phase->makecon( add_ring( t_sub1, t_2 ) ), + in(1)->in(2) ); + // Convert "(a-b)+(c-d)" into "(a+c)-(b+d)" + if( op2 == Op_SubL ) { + // Check for dead cycle: d = (a-b)+(c-d) + assert( in(1)->in(2) != this && in(2)->in(2) != this, + "dead loop in AddLNode::Ideal" ); + Node *sub = new (phase->C, 3) SubLNode(NULL, NULL); + sub->init_req(1, phase->transform(new (phase->C, 3) AddLNode(in(1)->in(1), in(2)->in(1) ) )); + sub->init_req(2, phase->transform(new (phase->C, 3) AddLNode(in(1)->in(2), in(2)->in(2) ) )); + return sub; + } + } + + // Convert "x+(0-y)" into "(x-y)" + if( op2 == Op_SubL && phase->type(in(2)->in(1)) == TypeLong::ZERO ) + return new (phase->C, 3) SubLNode(in(1), in(2)->in(2) ); + + // Convert "X+X+X+X+X...+X+Y" into "k*X+Y" or really convert "X+(X+Y)" + // into "(X<<1)+Y" and let shift-folding happen. + if( op2 == Op_AddL && + in(2)->in(1) == in(1) && + op1 != Op_ConL && + 0 ) { + Node *shift = phase->transform(new (phase->C, 3) LShiftLNode(in(1),phase->intcon(1))); + return new (phase->C, 3) AddLNode(shift,in(2)->in(2)); + } + + return AddNode::Ideal(phase, can_reshape); +} + + +//------------------------------Identity--------------------------------------- +// Fold (x-y)+y OR y+(x-y) into x +Node *AddLNode::Identity( PhaseTransform *phase ) { + if( in(1)->Opcode() == Op_SubL && phase->eqv(in(1)->in(2),in(2)) ) { + return in(1)->in(1); + } + else if( in(2)->Opcode() == Op_SubL && phase->eqv(in(2)->in(2),in(1)) ) { + return in(2)->in(1); + } + return AddNode::Identity(phase); +} + + +//------------------------------add_ring--------------------------------------- +// Supplied function returns the sum of the inputs. Guaranteed never +// to be passed a TOP or BOTTOM type, these are filtered out by +// pre-check. +const Type *AddLNode::add_ring( const Type *t0, const Type *t1 ) const { + const TypeLong *r0 = t0->is_long(); // Handy access + const TypeLong *r1 = t1->is_long(); + jlong lo = r0->_lo + r1->_lo; + jlong hi = r0->_hi + r1->_hi; + if( !(r0->is_con() && r1->is_con()) ) { + // Not both constants, compute approximate result + if( (r0->_lo & r1->_lo) < 0 && lo >= 0 ) { + lo =min_jlong; hi = max_jlong; // Underflow on the low side + } + if( (~(r0->_hi | r1->_hi)) < 0 && hi < 0 ) { + lo = min_jlong; hi = max_jlong; // Overflow on the high side + } + if( lo > hi ) { // Handle overflow + lo = min_jlong; hi = max_jlong; + } + } else { + // both constants, compute precise result using 'lo' and 'hi' + // Semantics define overflow and underflow for integer addition + // as expected. In particular: 0x80000000 + 0x80000000 --> 0x0 + } + return TypeLong::make( lo, hi, MAX2(r0->_widen,r1->_widen) ); +} + + +//============================================================================= +//------------------------------add_of_identity-------------------------------- +// Check for addition of the identity +const Type *AddFNode::add_of_identity( const Type *t1, const Type *t2 ) const { + // x ADD 0 should return x unless 'x' is a -zero + // + // const Type *zero = add_id(); // The additive identity + // jfloat f1 = t1->getf(); + // jfloat f2 = t2->getf(); + // + // if( t1->higher_equal( zero ) ) return t2; + // if( t2->higher_equal( zero ) ) return t1; + + return NULL; +} + +//------------------------------add_ring--------------------------------------- +// Supplied function returns the sum of the inputs. +// This also type-checks the inputs for sanity. Guaranteed never to +// be passed a TOP or BOTTOM type, these are filtered out by pre-check. +const Type *AddFNode::add_ring( const Type *t0, const Type *t1 ) const { + // We must be adding 2 float constants. + return TypeF::make( t0->getf() + t1->getf() ); +} + +//------------------------------Ideal------------------------------------------ +Node *AddFNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if( IdealizedNumerics && !phase->C->method()->is_strict() ) { + return AddNode::Ideal(phase, can_reshape); // commutative and associative transforms + } + + // Floating point additions are not associative because of boundary conditions (infinity) + return commute(this, + phase->type( in(1) )->singleton(), + phase->type( in(2) )->singleton() ) ? this : NULL; +} + + +//============================================================================= +//------------------------------add_of_identity-------------------------------- +// Check for addition of the identity +const Type *AddDNode::add_of_identity( const Type *t1, const Type *t2 ) const { + // x ADD 0 should return x unless 'x' is a -zero + // + // const Type *zero = add_id(); // The additive identity + // jfloat f1 = t1->getf(); + // jfloat f2 = t2->getf(); + // + // if( t1->higher_equal( zero ) ) return t2; + // if( t2->higher_equal( zero ) ) return t1; + + return NULL; +} +//------------------------------add_ring--------------------------------------- +// Supplied function returns the sum of the inputs. +// This also type-checks the inputs for sanity. Guaranteed never to +// be passed a TOP or BOTTOM type, these are filtered out by pre-check. +const Type *AddDNode::add_ring( const Type *t0, const Type *t1 ) const { + // We must be adding 2 double constants. + return TypeD::make( t0->getd() + t1->getd() ); +} + +//------------------------------Ideal------------------------------------------ +Node *AddDNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if( IdealizedNumerics && !phase->C->method()->is_strict() ) { + return AddNode::Ideal(phase, can_reshape); // commutative and associative transforms + } + + // Floating point additions are not associative because of boundary conditions (infinity) + return commute(this, + phase->type( in(1) )->singleton(), + phase->type( in(2) )->singleton() ) ? this : NULL; +} + + +//============================================================================= +//------------------------------Identity--------------------------------------- +// If one input is a constant 0, return the other input. +Node *AddPNode::Identity( PhaseTransform *phase ) { + return ( phase->type( in(Offset) )->higher_equal( TypeX_ZERO ) ) ? in(Address) : this; +} + +//------------------------------Idealize--------------------------------------- +Node *AddPNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Bail out if dead inputs + if( phase->type( in(Address) ) == Type::TOP ) return NULL; + + // If the left input is an add of a constant, flatten the expression tree. + const Node *n = in(Address); + if (n->is_AddP() && n->in(Base) == in(Base)) { + const AddPNode *addp = n->as_AddP(); // Left input is an AddP + assert( !addp->in(Address)->is_AddP() || + addp->in(Address)->as_AddP() != addp, + "dead loop in AddPNode::Ideal" ); + // Type of left input's right input + const Type *t = phase->type( addp->in(Offset) ); + if( t == Type::TOP ) return NULL; + const TypeX *t12 = t->is_intptr_t(); + if( t12->is_con() ) { // Left input is an add of a constant? + // If the right input is a constant, combine constants + const Type *temp_t2 = phase->type( in(Offset) ); + if( temp_t2 == Type::TOP ) return NULL; + const TypeX *t2 = temp_t2->is_intptr_t(); + if( t2->is_con() ) { + // The Add of the flattened expression + set_req(Address, addp->in(Address)); + set_req(Offset , phase->MakeConX(t2->get_con() + t12->get_con())); + return this; // Made progress + } + // Else move the constant to the right. ((A+con)+B) into ((A+B)+con) + set_req(Address, phase->transform(new (phase->C, 4) AddPNode(in(Base),addp->in(Address),in(Offset)))); + set_req(Offset , addp->in(Offset)); + return this; + } + } + + // Raw pointers? + if( in(Base)->bottom_type() == Type::TOP ) { + // If this is a NULL+long form (from unsafe accesses), switch to a rawptr. + if (phase->type(in(Address)) == TypePtr::NULL_PTR) { + Node* offset = in(Offset); + return new (phase->C, 2) CastX2PNode(offset); + } + } + + // If the right is an add of a constant, push the offset down. + // Convert: (ptr + (offset+con)) into (ptr+offset)+con. + // The idea is to merge array_base+scaled_index groups together, + // and only have different constant offsets from the same base. + const Node *add = in(Offset); + if( add->Opcode() == Op_AddX && add->in(1) != add ) { + const Type *t22 = phase->type( add->in(2) ); + if( t22->singleton() && (t22 != Type::TOP) ) { // Right input is an add of a constant? + set_req(Address, phase->transform(new (phase->C, 4) AddPNode(in(Base),in(Address),add->in(1)))); + set_req(Offset, add->in(2)); + return this; // Made progress + } + } + + return NULL; // No progress +} + +//------------------------------bottom_type------------------------------------ +// Bottom-type is the pointer-type with unknown offset. +const Type *AddPNode::bottom_type() const { + if (in(Address) == NULL) return TypePtr::BOTTOM; + const TypePtr *tp = in(Address)->bottom_type()->isa_ptr(); + if( !tp ) return Type::TOP; // TOP input means TOP output + assert( in(Offset)->Opcode() != Op_ConP, "" ); + const Type *t = in(Offset)->bottom_type(); + if( t == Type::TOP ) + return tp->add_offset(Type::OffsetTop); + const TypeX *tx = t->is_intptr_t(); + intptr_t txoffset = Type::OffsetBot; + if (tx->is_con()) { // Left input is an add of a constant? + txoffset = tx->get_con(); + if (txoffset != (int)txoffset) + txoffset = Type::OffsetBot; // oops: add_offset will choke on it + } + return tp->add_offset(txoffset); +} + +//------------------------------Value------------------------------------------ +const Type *AddPNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(Address) ); + const Type *t2 = phase->type( in(Offset) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Left input is a pointer + const TypePtr *p1 = t1->isa_ptr(); + // Right input is an int + const TypeX *p2 = t2->is_intptr_t(); + // Add 'em + intptr_t p2offset = Type::OffsetBot; + if (p2->is_con()) { // Left input is an add of a constant? + p2offset = p2->get_con(); + if (p2offset != (int)p2offset) + p2offset = Type::OffsetBot; // oops: add_offset will choke on it + } + return p1->add_offset(p2offset); +} + +//------------------------Ideal_base_and_offset-------------------------------- +// Split an oop pointer into a base and offset. +// (The offset might be Type::OffsetBot in the case of an array.) +// Return the base, or NULL if failure. +Node* AddPNode::Ideal_base_and_offset(Node* ptr, PhaseTransform* phase, + // second return value: + intptr_t& offset) { + if (ptr->is_AddP()) { + Node* base = ptr->in(AddPNode::Base); + Node* addr = ptr->in(AddPNode::Address); + Node* offs = ptr->in(AddPNode::Offset); + if (base == addr || base->is_top()) { + offset = phase->find_intptr_t_con(offs, Type::OffsetBot); + if (offset != Type::OffsetBot) { + return addr; + } + } + } + offset = Type::OffsetBot; + return NULL; +} + +//------------------------------match_edge------------------------------------- +// Do we Match on this edge index or not? Do not match base pointer edge +uint AddPNode::match_edge(uint idx) const { + return idx > Base; +} + +//---------------------------mach_bottom_type---------------------------------- +// Utility function for use by ADLC. Implements bottom_type for matched AddP. +const Type *AddPNode::mach_bottom_type( const MachNode* n) { + Node* base = n->in(Base); + const Type *t = base->bottom_type(); + if ( t == Type::TOP ) { + // an untyped pointer + return TypeRawPtr::BOTTOM; + } + const TypePtr* tp = t->isa_oopptr(); + if ( tp == NULL ) return t; + if ( tp->_offset == TypePtr::OffsetBot ) return tp; + + // We must carefully add up the various offsets... + intptr_t offset = 0; + const TypePtr* tptr = NULL; + + uint numopnds = n->num_opnds(); + uint index = n->oper_input_base(); + for ( uint i = 1; i < numopnds; i++ ) { + MachOper *opnd = n->_opnds[i]; + // Check for any interesting operand info. + // In particular, check for both memory and non-memory operands. + // %%%%% Clean this up: use xadd_offset + int con = opnd->constant(); + if ( con == TypePtr::OffsetBot ) goto bottom_out; + offset += con; + con = opnd->constant_disp(); + if ( con == TypePtr::OffsetBot ) goto bottom_out; + offset += con; + if( opnd->scale() != 0 ) goto bottom_out; + + // Check each operand input edge. Find the 1 allowed pointer + // edge. Other edges must be index edges; track exact constant + // inputs and otherwise assume the worst. + for ( uint j = opnd->num_edges(); j > 0; j-- ) { + Node* edge = n->in(index++); + const Type* et = edge->bottom_type(); + const TypeX* eti = et->isa_intptr_t(); + if ( eti == NULL ) { + // there must be one pointer among the operands + guarantee(tptr == NULL, "must be only one pointer operand"); + tptr = et->isa_oopptr(); + guarantee(tptr != NULL, "non-int operand must be pointer"); + continue; + } + if ( eti->_hi != eti->_lo ) goto bottom_out; + offset += eti->_lo; + } + } + guarantee(tptr != NULL, "must be exactly one pointer operand"); + return tptr->add_offset(offset); + + bottom_out: + return tp->add_offset(TypePtr::OffsetBot); +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +Node *OrINode::Identity( PhaseTransform *phase ) { + // x | x => x + if (phase->eqv(in(1), in(2))) { + return in(1); + } + + return AddNode::Identity(phase); +} + +//------------------------------add_ring--------------------------------------- +// Supplied function returns the sum of the inputs IN THE CURRENT RING. For +// the logical operations the ring's ADD is really a logical OR function. +// This also type-checks the inputs for sanity. Guaranteed never to +// be passed a TOP or BOTTOM type, these are filtered out by pre-check. +const Type *OrINode::add_ring( const Type *t0, const Type *t1 ) const { + const TypeInt *r0 = t0->is_int(); // Handy access + const TypeInt *r1 = t1->is_int(); + + // If both args are bool, can figure out better types + if ( r0 == TypeInt::BOOL ) { + if ( r1 == TypeInt::ONE) { + return TypeInt::ONE; + } else if ( r1 == TypeInt::BOOL ) { + return TypeInt::BOOL; + } + } else if ( r0 == TypeInt::ONE ) { + if ( r1 == TypeInt::BOOL ) { + return TypeInt::ONE; + } + } + + // If either input is not a constant, just return all integers. + if( !r0->is_con() || !r1->is_con() ) + return TypeInt::INT; // Any integer, but still no symbols. + + // Otherwise just OR them bits. + return TypeInt::make( r0->get_con() | r1->get_con() ); +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +Node *OrLNode::Identity( PhaseTransform *phase ) { + // x | x => x + if (phase->eqv(in(1), in(2))) { + return in(1); + } + + return AddNode::Identity(phase); +} + +//------------------------------add_ring--------------------------------------- +const Type *OrLNode::add_ring( const Type *t0, const Type *t1 ) const { + const TypeLong *r0 = t0->is_long(); // Handy access + const TypeLong *r1 = t1->is_long(); + + // If either input is not a constant, just return all integers. + if( !r0->is_con() || !r1->is_con() ) + return TypeLong::LONG; // Any integer, but still no symbols. + + // Otherwise just OR them bits. + return TypeLong::make( r0->get_con() | r1->get_con() ); +} + +//============================================================================= +//------------------------------add_ring--------------------------------------- +// Supplied function returns the sum of the inputs IN THE CURRENT RING. For +// the logical operations the ring's ADD is really a logical OR function. +// This also type-checks the inputs for sanity. Guaranteed never to +// be passed a TOP or BOTTOM type, these are filtered out by pre-check. +const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { + const TypeInt *r0 = t0->is_int(); // Handy access + const TypeInt *r1 = t1->is_int(); + + // Complementing a boolean? + if( r0 == TypeInt::BOOL && ( r1 == TypeInt::ONE + || r1 == TypeInt::BOOL)) + return TypeInt::BOOL; + + if( !r0->is_con() || !r1->is_con() ) // Not constants + return TypeInt::INT; // Any integer, but still no symbols. + + // Otherwise just XOR them bits. + return TypeInt::make( r0->get_con() ^ r1->get_con() ); +} + +//============================================================================= +//------------------------------add_ring--------------------------------------- +const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { + const TypeLong *r0 = t0->is_long(); // Handy access + const TypeLong *r1 = t1->is_long(); + + // If either input is not a constant, just return all integers. + if( !r0->is_con() || !r1->is_con() ) + return TypeLong::LONG; // Any integer, but still no symbols. + + // Otherwise just OR them bits. + return TypeLong::make( r0->get_con() ^ r1->get_con() ); +} + +//============================================================================= +//------------------------------add_ring--------------------------------------- +// Supplied function returns the sum of the inputs. +const Type *MaxINode::add_ring( const Type *t0, const Type *t1 ) const { + const TypeInt *r0 = t0->is_int(); // Handy access + const TypeInt *r1 = t1->is_int(); + + // Otherwise just MAX them bits. + return TypeInt::make( MAX2(r0->_lo,r1->_lo), MAX2(r0->_hi,r1->_hi), MAX2(r0->_widen,r1->_widen) ); +} + +//============================================================================= +//------------------------------Idealize--------------------------------------- +// MINs show up in range-check loop limit calculations. Look for +// "MIN2(x+c0,MIN2(y,x+c1))". Pick the smaller constant: "MIN2(x+c0,y)" +Node *MinINode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node *progress = NULL; + // Force a right-spline graph + Node *l = in(1); + Node *r = in(2); + // Transform MinI1( MinI2(a,b), c) into MinI1( a, MinI2(b,c) ) + // to force a right-spline graph for the rest of MinINode::Ideal(). + if( l->Opcode() == Op_MinI ) { + assert( l != l->in(1), "dead loop in MinINode::Ideal" ); + r = phase->transform(new (phase->C, 3) MinINode(l->in(2),r)); + l = l->in(1); + set_req(1, l); + set_req(2, r); + return this; + } + + // Get left input & constant + Node *x = l; + int x_off = 0; + if( x->Opcode() == Op_AddI && // Check for "x+c0" and collect constant + x->in(2)->is_Con() ) { + const Type *t = x->in(2)->bottom_type(); + if( t == Type::TOP ) return NULL; // No progress + x_off = t->is_int()->get_con(); + x = x->in(1); + } + + // Scan a right-spline-tree for MINs + Node *y = r; + int y_off = 0; + // Check final part of MIN tree + if( y->Opcode() == Op_AddI && // Check for "y+c1" and collect constant + y->in(2)->is_Con() ) { + const Type *t = y->in(2)->bottom_type(); + if( t == Type::TOP ) return NULL; // No progress + y_off = t->is_int()->get_con(); + y = y->in(1); + } + if( x->_idx > y->_idx && r->Opcode() != Op_MinI ) { + swap_edges(1, 2); + return this; + } + + + if( r->Opcode() == Op_MinI ) { + assert( r != r->in(2), "dead loop in MinINode::Ideal" ); + y = r->in(1); + // Check final part of MIN tree + if( y->Opcode() == Op_AddI &&// Check for "y+c1" and collect constant + y->in(2)->is_Con() ) { + const Type *t = y->in(2)->bottom_type(); + if( t == Type::TOP ) return NULL; // No progress + y_off = t->is_int()->get_con(); + y = y->in(1); + } + + if( x->_idx > y->_idx ) + return new (phase->C, 3) MinINode(r->in(1),phase->transform(new (phase->C, 3) MinINode(l,r->in(2)))); + + // See if covers: MIN2(x+c0,MIN2(y+c1,z)) + if( !phase->eqv(x,y) ) return NULL; + // If (y == x) transform MIN2(x+c0, MIN2(x+c1,z)) into + // MIN2(x+c0 or x+c1 which less, z). + return new (phase->C, 3) MinINode(phase->transform(new (phase->C, 3) AddINode(x,phase->intcon(MIN2(x_off,y_off)))),r->in(2)); + } else { + // See if covers: MIN2(x+c0,y+c1) + if( !phase->eqv(x,y) ) return NULL; + // If (y == x) transform MIN2(x+c0,x+c1) into x+c0 or x+c1 which less. + return new (phase->C, 3) AddINode(x,phase->intcon(MIN2(x_off,y_off))); + } + +} + +//------------------------------add_ring--------------------------------------- +// Supplied function returns the sum of the inputs. +const Type *MinINode::add_ring( const Type *t0, const Type *t1 ) const { + const TypeInt *r0 = t0->is_int(); // Handy access + const TypeInt *r1 = t1->is_int(); + + // Otherwise just MIN them bits. + return TypeInt::make( MIN2(r0->_lo,r1->_lo), MIN2(r0->_hi,r1->_hi), MAX2(r0->_widen,r1->_widen) ); +} diff --git a/hotspot/src/share/vm/opto/addnode.hpp b/hotspot/src/share/vm/opto/addnode.hpp new file mode 100644 index 00000000000..5170f50e17d --- /dev/null +++ b/hotspot/src/share/vm/opto/addnode.hpp @@ -0,0 +1,239 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +class PhaseTransform; + +//------------------------------AddNode---------------------------------------- +// Classic Add functionality. This covers all the usual 'add' behaviors for +// an algebraic ring. Add-integer, add-float, add-double, and binary-or are +// all inherited from this class. The various identity values are supplied +// by virtual functions. +class AddNode : public Node { + virtual uint hash() const; +public: + AddNode( Node *in1, Node *in2 ) : Node(0,in1,in2) { + init_class_id(Class_Add); + } + + // Handle algebraic identities here. If we have an identity, return the Node + // we are equivalent to. We look for "add of zero" as an identity. + virtual Node *Identity( PhaseTransform *phase ); + + // We also canonicalize the Node, moving constants to the right input, + // and flatten expressions (so that 1+x+2 becomes x+3). + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + + // Compute a new Type for this node. Basically we just do the pre-check, + // then call the virtual add() to set the type. + virtual const Type *Value( PhaseTransform *phase ) const; + + // Check if this addition involves the additive identity + virtual const Type *add_of_identity( const Type *t1, const Type *t2 ) const; + + // Supplied function returns the sum of the inputs. + // This also type-checks the inputs for sanity. Guaranteed never to + // be passed a TOP or BOTTOM type, these are filtered out by a pre-check. + virtual const Type *add_ring( const Type *, const Type * ) const = 0; + + // Supplied function to return the additive identity type + virtual const Type *add_id() const = 0; + +}; + +//------------------------------AddINode--------------------------------------- +// Add 2 integers +class AddINode : public AddNode { +public: + AddINode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *add_ring( const Type *, const Type * ) const; + virtual const Type *add_id() const { return TypeInt::ZERO; } + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node *Identity( PhaseTransform *phase ); + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------AddLNode--------------------------------------- +// Add 2 longs +class AddLNode : public AddNode { +public: + AddLNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *add_ring( const Type *, const Type * ) const; + virtual const Type *add_id() const { return TypeLong::ZERO; } + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node *Identity( PhaseTransform *phase ); + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------AddFNode--------------------------------------- +// Add 2 floats +class AddFNode : public AddNode { +public: + AddFNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *add_of_identity( const Type *t1, const Type *t2 ) const; + virtual const Type *add_ring( const Type *, const Type * ) const; + virtual const Type *add_id() const { return TypeF::ZERO; } + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual Node *Identity( PhaseTransform *phase ) { return this; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------AddDNode--------------------------------------- +// Add 2 doubles +class AddDNode : public AddNode { +public: + AddDNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *add_of_identity( const Type *t1, const Type *t2 ) const; + virtual const Type *add_ring( const Type *, const Type * ) const; + virtual const Type *add_id() const { return TypeD::ZERO; } + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual Node *Identity( PhaseTransform *phase ) { return this; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + +//------------------------------AddPNode--------------------------------------- +// Add pointer plus integer to get pointer. NOT commutative, really. +// So not really an AddNode. Lives here, because people associate it with +// an add. +class AddPNode : public Node { +public: + enum { Control, // When is it safe to do this add? + Base, // Base oop, for GC purposes + Address, // Actually address, derived from base + Offset } ; // Offset added to address + AddPNode( Node *base, Node *ptr, Node *off ) : Node(0,base,ptr,off) { + init_class_id(Class_AddP); + } + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const; + virtual uint ideal_reg() const { return Op_RegP; } + Node *base_node() { assert( req() > Base, "Missing base"); return in(Base); } + static Node* Ideal_base_and_offset(Node* ptr, PhaseTransform* phase, + // second return value: + intptr_t& offset); + // Do not match base-ptr edge + virtual uint match_edge(uint idx) const; + static const Type *mach_bottom_type(const MachNode* n); // used by ad_.hpp +}; + +//------------------------------OrINode---------------------------------------- +// Logically OR 2 integers. Included with the ADD nodes because it inherits +// all the behavior of addition on a ring. +class OrINode : public AddNode { +public: + OrINode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *add_ring( const Type *, const Type * ) const; + virtual const Type *add_id() const { return TypeInt::ZERO; } + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual Node *Identity( PhaseTransform *phase ); + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------OrLNode---------------------------------------- +// Logically OR 2 longs. Included with the ADD nodes because it inherits +// all the behavior of addition on a ring. +class OrLNode : public AddNode { +public: + OrLNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *add_ring( const Type *, const Type * ) const; + virtual const Type *add_id() const { return TypeLong::ZERO; } + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual Node *Identity( PhaseTransform *phase ); + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------XorINode--------------------------------------- +// XOR'ing 2 integers +class XorINode : public AddNode { +public: + XorINode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *add_ring( const Type *, const Type * ) const; + virtual const Type *add_id() const { return TypeInt::ZERO; } + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------XorINode--------------------------------------- +// XOR'ing 2 longs +class XorLNode : public AddNode { +public: + XorLNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *add_ring( const Type *, const Type * ) const; + virtual const Type *add_id() const { return TypeLong::ZERO; } + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------MaxNode---------------------------------------- +// Max (or min) of 2 values. Included with the ADD nodes because it inherits +// all the behavior of addition on a ring. Only new thing is that we allow +// 2 equal inputs to be equal. +class MaxNode : public AddNode { +public: + MaxNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + virtual int Opcode() const = 0; +}; + +//------------------------------MaxINode--------------------------------------- +// Maximum of 2 integers. Included with the ADD nodes because it inherits +// all the behavior of addition on a ring. +class MaxINode : public MaxNode { +public: + MaxINode( Node *in1, Node *in2 ) : MaxNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *add_ring( const Type *, const Type * ) const; + virtual const Type *add_id() const { return TypeInt::make(min_jint); } + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------MinINode--------------------------------------- +// MINimum of 2 integers. Included with the ADD nodes because it inherits +// all the behavior of addition on a ring. +class MinINode : public MaxNode { +public: + MinINode( Node *in1, Node *in2 ) : MaxNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *add_ring( const Type *, const Type * ) const; + virtual const Type *add_id() const { return TypeInt::make(max_jint); } + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); +}; diff --git a/hotspot/src/share/vm/opto/adlcVMDeps.hpp b/hotspot/src/share/vm/opto/adlcVMDeps.hpp new file mode 100644 index 00000000000..7d4f14ed9bd --- /dev/null +++ b/hotspot/src/share/vm/opto/adlcVMDeps.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Declare commonly known constant and data structures between the +// ADLC and the VM +// + +class AdlcVMDeps : public AllStatic { + public: + // Mirror of TypeFunc types + enum { Control, I_O, Memory, FramePtr, ReturnAdr, Parms }; + + enum Cisc_Status { Not_cisc_spillable = -1 }; + + // Mirror of OptoReg::Name names + enum Name { + Physical = 0 // Start of physical regs + }; + + // relocInfo + static const char* oop_reloc_type() { return "relocInfo::oop_type"; } + static const char* none_reloc_type() { return "relocInfo::none"; } +}; diff --git a/hotspot/src/share/vm/opto/block.cpp b/hotspot/src/share/vm/opto/block.cpp new file mode 100644 index 00000000000..c6b94a45af2 --- /dev/null +++ b/hotspot/src/share/vm/opto/block.cpp @@ -0,0 +1,952 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_block.cpp.incl" + + +//----------------------------------------------------------------------------- +void Block_Array::grow( uint i ) { + assert(i >= Max(), "must be an overflow"); + debug_only(_limit = i+1); + if( i < _size ) return; + if( !_size ) { + _size = 1; + _blocks = (Block**)_arena->Amalloc( _size * sizeof(Block*) ); + _blocks[0] = NULL; + } + uint old = _size; + while( i >= _size ) _size <<= 1; // Double to fit + _blocks = (Block**)_arena->Arealloc( _blocks, old*sizeof(Block*),_size*sizeof(Block*)); + Copy::zero_to_bytes( &_blocks[old], (_size-old)*sizeof(Block*) ); +} + +//============================================================================= +void Block_List::remove(uint i) { + assert(i < _cnt, "index out of bounds"); + Copy::conjoint_words_to_lower((HeapWord*)&_blocks[i+1], (HeapWord*)&_blocks[i], ((_cnt-i-1)*sizeof(Block*))); + pop(); // shrink list by one block +} + +void Block_List::insert(uint i, Block *b) { + push(b); // grow list by one block + Copy::conjoint_words_to_higher((HeapWord*)&_blocks[i], (HeapWord*)&_blocks[i+1], ((_cnt-i-1)*sizeof(Block*))); + _blocks[i] = b; +} + + +//============================================================================= + +uint Block::code_alignment() { + // Check for Root block + if( _pre_order == 0 ) return CodeEntryAlignment; + // Check for Start block + if( _pre_order == 1 ) return InteriorEntryAlignment; + // Check for loop alignment + Node *h = head(); + if( h->is_Loop() && h->as_Loop()->is_inner_loop() ) { + // Pre- and post-loops have low trip count so do not bother with + // NOPs for align loop head. The constants are hidden from tuning + // but only because my "divide by 4" heuristic surely gets nearly + // all possible gain (a "do not align at all" heuristic has a + // chance of getting a really tiny gain). + if( h->is_CountedLoop() && (h->as_CountedLoop()->is_pre_loop() || + h->as_CountedLoop()->is_post_loop()) ) + return (OptoLoopAlignment > 4) ? (OptoLoopAlignment>>2) : 1; + // Loops with low backedge frequency should not be aligned. + Node *n = h->in(LoopNode::LoopBackControl)->in(0); + if( n->is_MachIf() && n->as_MachIf()->_prob < 0.01 ) { + return 1; // Loop does not loop, more often than not! + } + return OptoLoopAlignment; // Otherwise align loop head + } + return 1; // no particular alignment +} + +//----------------------------------------------------------------------------- +// Compute the size of first 'inst_cnt' instructions in this block. +// Return the number of instructions left to compute if the block has +// less then 'inst_cnt' instructions. +uint Block::compute_first_inst_size(uint& sum_size, uint inst_cnt, + PhaseRegAlloc* ra) { + uint last_inst = _nodes.size(); + for( uint j = 0; j < last_inst && inst_cnt > 0; j++ ) { + uint inst_size = _nodes[j]->size(ra); + if( inst_size > 0 ) { + inst_cnt--; + uint sz = sum_size + inst_size; + if( sz <= (uint)OptoLoopAlignment ) { + // Compute size of instructions which fit into fetch buffer only + // since all inst_cnt instructions will not fit even if we align them. + sum_size = sz; + } else { + return 0; + } + } + } + return inst_cnt; +} + +//----------------------------------------------------------------------------- +uint Block::find_node( const Node *n ) const { + for( uint i = 0; i < _nodes.size(); i++ ) { + if( _nodes[i] == n ) + return i; + } + ShouldNotReachHere(); + return 0; +} + +// Find and remove n from block list +void Block::find_remove( const Node *n ) { + _nodes.remove(find_node(n)); +} + +//------------------------------is_Empty--------------------------------------- +// Return empty status of a block. Empty blocks contain only the head, other +// ideal nodes, and an optional trailing goto. +int Block::is_Empty() const { + + // Root or start block is not considered empty + if (head()->is_Root() || head()->is_Start()) { + return not_empty; + } + + int success_result = completely_empty; + int end_idx = _nodes.size()-1; + + // Check for ending goto + if ((end_idx > 0) && (_nodes[end_idx]->is_Goto())) { + success_result = empty_with_goto; + end_idx--; + } + + // Unreachable blocks are considered empty + if (num_preds() <= 1) { + return success_result; + } + + // Ideal nodes are allowable in empty blocks: skip them Only MachNodes + // turn directly into code, because only MachNodes have non-trivial + // emit() functions. + while ((end_idx > 0) && !_nodes[end_idx]->is_Mach()) { + end_idx--; + } + + // No room for any interesting instructions? + if (end_idx == 0) { + return success_result; + } + + return not_empty; +} + +//------------------------------has_uncommon_code------------------------------ +// Return true if the block's code implies that it is not likely to be +// executed infrequently. Check to see if the block ends in a Halt or +// a low probability call. +bool Block::has_uncommon_code() const { + Node* en = end(); + + if (en->is_Goto()) + en = en->in(0); + if (en->is_Catch()) + en = en->in(0); + if (en->is_Proj() && en->in(0)->is_MachCall()) { + MachCallNode* call = en->in(0)->as_MachCall(); + if (call->cnt() != COUNT_UNKNOWN && call->cnt() <= PROB_UNLIKELY_MAG(4)) { + // This is true for slow-path stubs like new_{instance,array}, + // slow_arraycopy, complete_monitor_locking, uncommon_trap. + // The magic number corresponds to the probability of an uncommon_trap, + // even though it is a count not a probability. + return true; + } + } + + int op = en->is_Mach() ? en->as_Mach()->ideal_Opcode() : en->Opcode(); + return op == Op_Halt; +} + +//------------------------------is_uncommon------------------------------------ +// True if block is low enough frequency or guarded by a test which +// mostly does not go here. +bool Block::is_uncommon( Block_Array &bbs ) const { + // Initial blocks must never be moved, so are never uncommon. + if (head()->is_Root() || head()->is_Start()) return false; + + // Check for way-low freq + if( _freq < BLOCK_FREQUENCY(0.00001f) ) return true; + + // Look for code shape indicating uncommon_trap or slow path + if (has_uncommon_code()) return true; + + const float epsilon = 0.05f; + const float guard_factor = PROB_UNLIKELY_MAG(4) / (1.f - epsilon); + uint uncommon_preds = 0; + uint freq_preds = 0; + uint uncommon_for_freq_preds = 0; + + for( uint i=1; i_idx]; + // Check to see if this block follows its guard 1 time out of 10000 + // or less. + // + // See list of magnitude-4 unlikely probabilities in cfgnode.hpp which + // we intend to be "uncommon", such as slow-path TLE allocation, + // predicted call failure, and uncommon trap triggers. + // + // Use an epsilon value of 5% to allow for variability in frequency + // predictions and floating point calculations. The net effect is + // that guard_factor is set to 9500. + // + // Ignore low-frequency blocks. + // The next check is (guard->_freq < 1.e-5 * 9500.). + if(guard->_freq*BLOCK_FREQUENCY(guard_factor) < BLOCK_FREQUENCY(0.00001f)) { + uncommon_preds++; + } else { + freq_preds++; + if( _freq < guard->_freq * guard_factor ) { + uncommon_for_freq_preds++; + } + } + } + if( num_preds() > 1 && + // The block is uncommon if all preds are uncommon or + (uncommon_preds == (num_preds()-1) || + // it is uncommon for all frequent preds. + uncommon_for_freq_preds == freq_preds) ) { + return true; + } + return false; +} + +//------------------------------dump------------------------------------------- +#ifndef PRODUCT +void Block::dump_bidx(const Block* orig) const { + if (_pre_order) tty->print("B%d",_pre_order); + else tty->print("N%d", head()->_idx); + + if (Verbose && orig != this) { + // Dump the original block's idx + tty->print(" ("); + orig->dump_bidx(orig); + tty->print(")"); + } +} + +void Block::dump_pred(const Block_Array *bbs, Block* orig) const { + if (is_connector()) { + for (uint i=1; i_idx]); + p->dump_pred(bbs, orig); + } + } else { + dump_bidx(orig); + tty->print(" "); + } +} + +void Block::dump_head( const Block_Array *bbs ) const { + // Print the basic block + dump_bidx(this); + tty->print(": #\t"); + + // Print the incoming CFG edges and the outgoing CFG edges + for( uint i=0; i<_num_succs; i++ ) { + non_connector_successor(i)->dump_bidx(_succs[i]); + tty->print(" "); + } + tty->print("<- "); + if( head()->is_block_start() ) { + for (uint i=1; i_idx]; + p->dump_pred(bbs, p); + } else { + while (!s->is_block_start()) + s = s->in(0); + tty->print("N%d ", s->_idx ); + } + } + } else + tty->print("BLOCK HEAD IS JUNK "); + + // Print loop, if any + const Block *bhead = this; // Head of self-loop + Node *bh = bhead->head(); + if( bbs && bh->is_Loop() && !head()->is_Root() ) { + LoopNode *loop = bh->as_Loop(); + const Block *bx = (*bbs)[loop->in(LoopNode::LoopBackControl)->_idx]; + while (bx->is_connector()) { + bx = (*bbs)[bx->pred(1)->_idx]; + } + tty->print("\tLoop: B%d-B%d ", bhead->_pre_order, bx->_pre_order); + // Dump any loop-specific bits, especially for CountedLoops. + loop->dump_spec(tty); + } + tty->print(" Freq: %g",_freq); + if( Verbose || WizardMode ) { + tty->print(" IDom: %d/#%d", _idom ? _idom->_pre_order : 0, _dom_depth); + tty->print(" RegPressure: %d",_reg_pressure); + tty->print(" IHRP Index: %d",_ihrp_index); + tty->print(" FRegPressure: %d",_freg_pressure); + tty->print(" FHRP Index: %d",_fhrp_index); + } + tty->print_cr(""); +} + +void Block::dump() const { dump(0); } + +void Block::dump( const Block_Array *bbs ) const { + dump_head(bbs); + uint cnt = _nodes.size(); + for( uint i=0; idump(); + tty->print("\n"); +} +#endif + +//============================================================================= +//------------------------------PhaseCFG--------------------------------------- +PhaseCFG::PhaseCFG( Arena *a, RootNode *r, Matcher &m ) : + Phase(CFG), + _bbs(a), + _root(r) +#ifndef PRODUCT + , _trace_opto_pipelining(TraceOptoPipelining || C->method_has_option("TraceOptoPipelining")) +#endif +{ + ResourceMark rm; + // I'll need a few machine-specific GotoNodes. Make an Ideal GotoNode, + // then Match it into a machine-specific Node. Then clone the machine + // Node on demand. + Node *x = new (C, 1) GotoNode(NULL); + x->init_req(0, x); + _goto = m.match_tree(x); + assert(_goto != NULL, ""); + _goto->set_req(0,_goto); + + // Build the CFG in Reverse Post Order + _num_blocks = build_cfg(); + _broot = _bbs[_root->_idx]; +} + +//------------------------------build_cfg-------------------------------------- +// Build a proper looking CFG. Make every block begin with either a StartNode +// or a RegionNode. Make every block end with either a Goto, If or Return. +// The RootNode both starts and ends it's own block. Do this with a recursive +// backwards walk over the control edges. +uint PhaseCFG::build_cfg() { + Arena *a = Thread::current()->resource_area(); + VectorSet visited(a); + + // Allocate stack with enough space to avoid frequent realloc + Node_Stack nstack(a, C->unique() >> 1); + nstack.push(_root, 0); + uint sum = 0; // Counter for blocks + + while (nstack.is_nonempty()) { + // node and in's index from stack's top + // 'np' is _root (see above) or RegionNode, StartNode: we push on stack + // only nodes which point to the start of basic block (see below). + Node *np = nstack.node(); + // idx > 0, except for the first node (_root) pushed on stack + // at the beginning when idx == 0. + // We will use the condition (idx == 0) later to end the build. + uint idx = nstack.index(); + Node *proj = np->in(idx); + const Node *x = proj->is_block_proj(); + // Does the block end with a proper block-ending Node? One of Return, + // If or Goto? (This check should be done for visited nodes also). + if (x == NULL) { // Does not end right... + Node *g = _goto->clone(); // Force it to end in a Goto + g->set_req(0, proj); + np->set_req(idx, g); + x = proj = g; + } + if (!visited.test_set(x->_idx)) { // Visit this block once + // Skip any control-pinned middle'in stuff + Node *p = proj; + do { + proj = p; // Update pointer to last Control + p = p->in(0); // Move control forward + } while( !p->is_block_proj() && + !p->is_block_start() ); + // Make the block begin with one of Region or StartNode. + if( !p->is_block_start() ) { + RegionNode *r = new (C, 2) RegionNode( 2 ); + r->init_req(1, p); // Insert RegionNode in the way + proj->set_req(0, r); // Insert RegionNode in the way + p = r; + } + // 'p' now points to the start of this basic block + + // Put self in array of basic blocks + Block *bb = new (_bbs._arena) Block(_bbs._arena,p); + _bbs.map(p->_idx,bb); + _bbs.map(x->_idx,bb); + if( x != p ) // Only for root is x == p + bb->_nodes.push((Node*)x); + + // Now handle predecessors + ++sum; // Count 1 for self block + uint cnt = bb->num_preds(); + for (int i = (cnt - 1); i > 0; i-- ) { // For all predecessors + Node *prevproj = p->in(i); // Get prior input + assert( !prevproj->is_Con(), "dead input not removed" ); + // Check to see if p->in(i) is a "control-dependent" CFG edge - + // i.e., it splits at the source (via an IF or SWITCH) and merges + // at the destination (via a many-input Region). + // This breaks critical edges. The RegionNode to start the block + // will be added when is pulled off the node stack + if ( cnt > 2 ) { // Merging many things? + assert( prevproj== bb->pred(i),""); + if(prevproj->is_block_proj() != prevproj) { // Control-dependent edge? + // Force a block on the control-dependent edge + Node *g = _goto->clone(); // Force it to end in a Goto + g->set_req(0,prevproj); + p->set_req(i,g); + } + } + nstack.push(p, i); // 'p' is RegionNode or StartNode + } + } else { // Post-processing visited nodes + nstack.pop(); // remove node from stack + // Check if it the fist node pushed on stack at the beginning. + if (idx == 0) break; // end of the build + // Find predecessor basic block + Block *pb = _bbs[x->_idx]; + // Insert into nodes array, if not already there + if( !_bbs.lookup(proj->_idx) ) { + assert( x != proj, "" ); + // Map basic block of projection + _bbs.map(proj->_idx,pb); + pb->_nodes.push(proj); + } + // Insert self as a child of my predecessor block + pb->_succs.map(pb->_num_succs++, _bbs[np->_idx]); + assert( pb->_nodes[ pb->_nodes.size() - pb->_num_succs ]->is_block_proj(), + "too many control users, not a CFG?" ); + } + } + // Return number of basic blocks for all children and self + return sum; +} + +//------------------------------insert_goto_at--------------------------------- +// Inserts a goto & corresponding basic block between +// block[block_no] and its succ_no'th successor block +void PhaseCFG::insert_goto_at(uint block_no, uint succ_no) { + // get block with block_no + assert(block_no < _num_blocks, "illegal block number"); + Block* in = _blocks[block_no]; + // get successor block succ_no + assert(succ_no < in->_num_succs, "illegal successor number"); + Block* out = in->_succs[succ_no]; + // get ProjNode corresponding to the succ_no'th successor of the in block + ProjNode* proj = in->_nodes[in->_nodes.size() - in->_num_succs + succ_no]->as_Proj(); + // create region for basic block + RegionNode* region = new (C, 2) RegionNode(2); + region->init_req(1, proj); + // setup corresponding basic block + Block* block = new (_bbs._arena) Block(_bbs._arena, region); + _bbs.map(region->_idx, block); + C->regalloc()->set_bad(region->_idx); + // add a goto node + Node* gto = _goto->clone(); // get a new goto node + gto->set_req(0, region); + // add it to the basic block + block->_nodes.push(gto); + _bbs.map(gto->_idx, block); + C->regalloc()->set_bad(gto->_idx); + // hook up successor block + block->_succs.map(block->_num_succs++, out); + // remap successor's predecessors if necessary + for (uint i = 1; i < out->num_preds(); i++) { + if (out->pred(i) == proj) out->head()->set_req(i, gto); + } + // remap predecessor's successor to new block + in->_succs.map(succ_no, block); + // add new basic block to basic block list + _blocks.insert(block_no + 1, block); + _num_blocks++; +} + +//------------------------------no_flip_branch--------------------------------- +// Does this block end in a multiway branch that cannot have the default case +// flipped for another case? +static bool no_flip_branch( Block *b ) { + int branch_idx = b->_nodes.size() - b->_num_succs-1; + if( branch_idx < 1 ) return false; + Node *bra = b->_nodes[branch_idx]; + if( bra->is_Catch() ) return true; + if( bra->is_Mach() ) { + if( bra->is_MachNullCheck() ) return true; + int iop = bra->as_Mach()->ideal_Opcode(); + if( iop == Op_FastLock || iop == Op_FastUnlock ) + return true; + } + return false; +} + +//------------------------------convert_NeverBranch_to_Goto-------------------- +// Check for NeverBranch at block end. This needs to become a GOTO to the +// true target. NeverBranch are treated as a conditional branch that always +// goes the same direction for most of the optimizer and are used to give a +// fake exit path to infinite loops. At this late stage they need to turn +// into Goto's so that when you enter the infinite loop you indeed hang. +void PhaseCFG::convert_NeverBranch_to_Goto(Block *b) { + // Find true target + int end_idx = b->end_idx(); + int idx = b->_nodes[end_idx+1]->as_Proj()->_con; + Block *succ = b->_succs[idx]; + Node* gto = _goto->clone(); // get a new goto node + gto->set_req(0, b->head()); + Node *bp = b->_nodes[end_idx]; + b->_nodes.map(end_idx,gto); // Slam over NeverBranch + _bbs.map(gto->_idx, b); + C->regalloc()->set_bad(gto->_idx); + b->_nodes.pop(); // Yank projections + b->_nodes.pop(); // Yank projections + b->_succs.map(0,succ); // Map only successor + b->_num_succs = 1; + // remap successor's predecessors if necessary + uint j; + for( j = 1; j < succ->num_preds(); j++) + if( succ->pred(j)->in(0) == bp ) + succ->head()->set_req(j, gto); + // Kill alternate exit path + Block *dead = b->_succs[1-idx]; + for( j = 1; j < dead->num_preds(); j++) + if( dead->pred(j)->in(0) == bp ) + break; + // Scan through block, yanking dead path from + // all regions and phis. + dead->head()->del_req(j); + for( int k = 1; dead->_nodes[k]->is_Phi(); k++ ) + dead->_nodes[k]->del_req(j); +} + +//------------------------------MoveToNext------------------------------------- +// Helper function to move block bx to the slot following b_index. Return +// true if the move is successful, otherwise false +bool PhaseCFG::MoveToNext(Block* bx, uint b_index) { + if (bx == NULL) return false; + + // Return false if bx is already scheduled. + uint bx_index = bx->_pre_order; + if ((bx_index <= b_index) && (_blocks[bx_index] == bx)) { + return false; + } + + // Find the current index of block bx on the block list + bx_index = b_index + 1; + while( bx_index < _num_blocks && _blocks[bx_index] != bx ) bx_index++; + assert(_blocks[bx_index] == bx, "block not found"); + + // If the previous block conditionally falls into bx, return false, + // because moving bx will create an extra jump. + for(uint k = 1; k < bx->num_preds(); k++ ) { + Block* pred = _bbs[bx->pred(k)->_idx]; + if (pred == _blocks[bx_index-1]) { + if (pred->_num_succs != 1) { + return false; + } + } + } + + // Reinsert bx just past block 'b' + _blocks.remove(bx_index); + _blocks.insert(b_index + 1, bx); + return true; +} + +//------------------------------MoveToEnd-------------------------------------- +// Move empty and uncommon blocks to the end. +void PhaseCFG::MoveToEnd(Block *b, uint i) { + int e = b->is_Empty(); + if (e != Block::not_empty) { + if (e == Block::empty_with_goto) { + // Remove the goto, but leave the block. + b->_nodes.pop(); + } + // Mark this block as a connector block, which will cause it to be + // ignored in certain functions such as non_connector_successor(). + b->set_connector(); + } + // Move the empty block to the end, and don't recheck. + _blocks.remove(i); + _blocks.push(b); +} + +//------------------------------RemoveEmpty------------------------------------ +// Remove empty basic blocks and useless branches. +void PhaseCFG::RemoveEmpty() { + // Move uncommon blocks to the end + uint last = _num_blocks; + uint i; + assert( _blocks[0] == _broot, "" ); + for( i = 1; i < last; i++ ) { + Block *b = _blocks[i]; + + // Check for NeverBranch at block end. This needs to become a GOTO to the + // true target. NeverBranch are treated as a conditional branch that + // always goes the same direction for most of the optimizer and are used + // to give a fake exit path to infinite loops. At this late stage they + // need to turn into Goto's so that when you enter the infinite loop you + // indeed hang. + if( b->_nodes[b->end_idx()]->Opcode() == Op_NeverBranch ) + convert_NeverBranch_to_Goto(b); + + // Look for uncommon blocks and move to end. + if( b->is_uncommon(_bbs) ) { + MoveToEnd(b, i); + last--; // No longer check for being uncommon! + if( no_flip_branch(b) ) { // Fall-thru case must follow? + b = _blocks[i]; // Find the fall-thru block + MoveToEnd(b, i); + last--; + } + i--; // backup block counter post-increment + } + } + + // Remove empty blocks + uint j1; + last = _num_blocks; + for( i=0; i < last; i++ ) { + Block *b = _blocks[i]; + if (i > 0) { + if (b->is_Empty() != Block::not_empty) { + MoveToEnd(b, i); + last--; + i--; + } + } + } // End of for all blocks + + // Fixup final control flow for the blocks. Remove jump-to-next + // block. If neither arm of a IF follows the conditional branch, we + // have to add a second jump after the conditional. We place the + // TRUE branch target in succs[0] for both GOTOs and IFs. + for( i=0; i < _num_blocks; i++ ) { + Block *b = _blocks[i]; + b->_pre_order = i; // turn pre-order into block-index + + // Connector blocks need no further processing. + if (b->is_connector()) { + assert((i+1) == _num_blocks || _blocks[i+1]->is_connector(), + "All connector blocks should sink to the end"); + continue; + } + assert(b->is_Empty() != Block::completely_empty, + "Empty blocks should be connectors"); + + Block *bnext = (i < _num_blocks-1) ? _blocks[i+1] : NULL; + Block *bs0 = b->non_connector_successor(0); + + // Check for multi-way branches where I cannot negate the test to + // exchange the true and false targets. + if( no_flip_branch( b ) ) { + // Find fall through case - if must fall into its target + int branch_idx = b->_nodes.size() - b->_num_succs; + for (uint j2 = 0; j2 < b->_num_succs; j2++) { + const ProjNode* p = b->_nodes[branch_idx + j2]->as_Proj(); + if (p->_con == 0) { + // successor j2 is fall through case + if (b->non_connector_successor(j2) != bnext) { + // but it is not the next block => insert a goto + insert_goto_at(i, j2); + } + // Put taken branch in slot 0 + if( j2 == 0 && b->_num_succs == 2) { + // Flip targets in succs map + Block *tbs0 = b->_succs[0]; + Block *tbs1 = b->_succs[1]; + b->_succs.map( 0, tbs1 ); + b->_succs.map( 1, tbs0 ); + } + break; + } + } + // Remove all CatchProjs + for (j1 = 0; j1 < b->_num_succs; j1++) b->_nodes.pop(); + + } else if (b->_num_succs == 1) { + // Block ends in a Goto? + if (bnext == bs0) { + // We fall into next block; remove the Goto + b->_nodes.pop(); + } + + } else if( b->_num_succs == 2 ) { // Block ends in a If? + // Get opcode of 1st projection (matches _succs[0]) + // Note: Since this basic block has 2 exits, the last 2 nodes must + // be projections (in any order), the 3rd last node must be + // the IfNode (we have excluded other 2-way exits such as + // CatchNodes already). + MachNode *iff = b->_nodes[b->_nodes.size()-3]->as_Mach(); + ProjNode *proj0 = b->_nodes[b->_nodes.size()-2]->as_Proj(); + ProjNode *proj1 = b->_nodes[b->_nodes.size()-1]->as_Proj(); + + // Assert that proj0 and succs[0] match up. Similarly for proj1 and succs[1]. + assert(proj0->raw_out(0) == b->_succs[0]->head(), "Mismatch successor 0"); + assert(proj1->raw_out(0) == b->_succs[1]->head(), "Mismatch successor 1"); + + Block *bs1 = b->non_connector_successor(1); + + // Check for neither successor block following the current + // block ending in a conditional. If so, move one of the + // successors after the current one, provided that the + // successor was previously unscheduled, but moveable + // (i.e., all paths to it involve a branch). + if( bnext != bs0 && bnext != bs1 ) { + + // Choose the more common successor based on the probability + // of the conditional branch. + Block *bx = bs0; + Block *by = bs1; + + // _prob is the probability of taking the true path. Make + // p the probability of taking successor #1. + float p = iff->as_MachIf()->_prob; + if( proj0->Opcode() == Op_IfTrue ) { + p = 1.0 - p; + } + + // Prefer successor #1 if p > 0.5 + if (p > PROB_FAIR) { + bx = bs1; + by = bs0; + } + + // Attempt the more common successor first + if (MoveToNext(bx, i)) { + bnext = bx; + } else if (MoveToNext(by, i)) { + bnext = by; + } + } + + // Check for conditional branching the wrong way. Negate + // conditional, if needed, so it falls into the following block + // and branches to the not-following block. + + // Check for the next block being in succs[0]. We are going to branch + // to succs[0], so we want the fall-thru case as the next block in + // succs[1]. + if (bnext == bs0) { + // Fall-thru case in succs[0], so flip targets in succs map + Block *tbs0 = b->_succs[0]; + Block *tbs1 = b->_succs[1]; + b->_succs.map( 0, tbs1 ); + b->_succs.map( 1, tbs0 ); + // Flip projection for each target + { ProjNode *tmp = proj0; proj0 = proj1; proj1 = tmp; } + + } else if( bnext == bs1 ) { // Fall-thru is already in succs[1] + + } else { // Else need a double-branch + + // The existing conditional branch need not change. + // Add a unconditional branch to the false target. + // Alas, it must appear in its own block and adding a + // block this late in the game is complicated. Sigh. + insert_goto_at(i, 1); + } + + // Make sure we TRUE branch to the target + if( proj0->Opcode() == Op_IfFalse ) + iff->negate(); + + b->_nodes.pop(); // Remove IfFalse & IfTrue projections + b->_nodes.pop(); + + } else { + // Multi-exit block, e.g. a switch statement + // But we don't need to do anything here + } + + } // End of for all blocks + +} + + +//------------------------------dump------------------------------------------- +#ifndef PRODUCT +void PhaseCFG::_dump_cfg( const Node *end, VectorSet &visited ) const { + const Node *x = end->is_block_proj(); + assert( x, "not a CFG" ); + + // Do not visit this block again + if( visited.test_set(x->_idx) ) return; + + // Skip through this block + const Node *p = x; + do { + p = p->in(0); // Move control forward + assert( !p->is_block_proj() || p->is_Root(), "not a CFG" ); + } while( !p->is_block_start() ); + + // Recursively visit + for( uint i=1; ireq(); i++ ) + _dump_cfg(p->in(i),visited); + + // Dump the block + _bbs[p->_idx]->dump(&_bbs); +} + +void PhaseCFG::dump( ) const { + tty->print("\n--- CFG --- %d BBs\n",_num_blocks); + if( _blocks.size() ) { // Did we do basic-block layout? + for( uint i=0; i<_num_blocks; i++ ) + _blocks[i]->dump(&_bbs); + } else { // Else do it with a DFS + VectorSet visited(_bbs._arena); + _dump_cfg(_root,visited); + } +} + +void PhaseCFG::dump_headers() { + for( uint i = 0; i < _num_blocks; i++ ) { + if( _blocks[i] == NULL ) continue; + _blocks[i]->dump_head(&_bbs); + } +} + +void PhaseCFG::verify( ) const { + // Verify sane CFG + for( uint i = 0; i < _num_blocks; i++ ) { + Block *b = _blocks[i]; + uint cnt = b->_nodes.size(); + uint j; + for( j = 0; j < cnt; j++ ) { + Node *n = b->_nodes[j]; + assert( _bbs[n->_idx] == b, "" ); + if( j >= 1 && n->is_Mach() && + n->as_Mach()->ideal_Opcode() == Op_CreateEx ) { + assert( j == 1 || b->_nodes[j-1]->is_Phi(), + "CreateEx must be first instruction in block" ); + } + for( uint k = 0; k < n->req(); k++ ) { + Node *use = n->in(k); + if( use && use != n ) { + assert( _bbs[use->_idx] || use->is_Con(), + "must have block; constants for debug info ok" ); + } + } + } + + j = b->end_idx(); + Node *bp = (Node*)b->_nodes[b->_nodes.size()-1]->is_block_proj(); + assert( bp, "last instruction must be a block proj" ); + assert( bp == b->_nodes[j], "wrong number of successors for this block" ); + if( bp->is_Catch() ) { + while( b->_nodes[--j]->Opcode() == Op_MachProj ) ; + assert( b->_nodes[j]->is_Call(), "CatchProj must follow call" ); + } + else if( bp->is_Mach() && bp->as_Mach()->ideal_Opcode() == Op_If ) { + assert( b->_num_succs == 2, "Conditional branch must have two targets"); + } + } +} +#endif + +//============================================================================= +//------------------------------UnionFind-------------------------------------- +UnionFind::UnionFind( uint max ) : _cnt(max), _max(max), _indices(NEW_RESOURCE_ARRAY(uint,max)) { + Copy::zero_to_bytes( _indices, sizeof(uint)*max ); +} + +void UnionFind::extend( uint from_idx, uint to_idx ) { + _nesting.check(); + if( from_idx >= _max ) { + uint size = 16; + while( size <= from_idx ) size <<=1; + _indices = REALLOC_RESOURCE_ARRAY( uint, _indices, _max, size ); + _max = size; + } + while( _cnt <= from_idx ) _indices[_cnt++] = 0; + _indices[from_idx] = to_idx; +} + +void UnionFind::reset( uint max ) { + assert( max <= max_uint, "Must fit within uint" ); + // Force the Union-Find mapping to be at least this large + extend(max,0); + // Initialize to be the ID mapping. + for( uint i=0; i<_max; i++ ) map(i,i); +} + +//------------------------------Find_compress---------------------------------- +// Straight out of Tarjan's union-find algorithm +uint UnionFind::Find_compress( uint idx ) { + uint cur = idx; + uint next = lookup(cur); + while( next != cur ) { // Scan chain of equivalences + assert( next < cur, "always union smaller" ); + cur = next; // until find a fixed-point + next = lookup(cur); + } + // Core of union-find algorithm: update chain of + // equivalences to be equal to the root. + while( idx != next ) { + uint tmp = lookup(idx); + map(idx, next); + idx = tmp; + } + return idx; +} + +//------------------------------Find_const------------------------------------- +// Like Find above, but no path compress, so bad asymptotic behavior +uint UnionFind::Find_const( uint idx ) const { + if( idx == 0 ) return idx; // Ignore the zero idx + // Off the end? This can happen during debugging dumps + // when data structures have not finished being updated. + if( idx >= _max ) return idx; + uint next = lookup(idx); + while( next != idx ) { // Scan chain of equivalences + assert( next < idx, "always union smaller" ); + idx = next; // until find a fixed-point + next = lookup(idx); + } + return next; +} + +//------------------------------Union------------------------------------------ +// union 2 sets together. +void UnionFind::Union( uint idx1, uint idx2 ) { + uint src = Find(idx1); + uint dst = Find(idx2); + assert( src, "" ); + assert( dst, "" ); + assert( src < _max, "oob" ); + assert( dst < _max, "oob" ); + assert( src < dst, "always union smaller" ); + map(dst,src); +} diff --git a/hotspot/src/share/vm/opto/block.hpp b/hotspot/src/share/vm/opto/block.hpp new file mode 100644 index 00000000000..8708a4dedf8 --- /dev/null +++ b/hotspot/src/share/vm/opto/block.hpp @@ -0,0 +1,510 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Optimization - Graph Style + +class Block; +class CFGLoop; +class MachCallNode; +class Matcher; +class RootNode; +class VectorSet; +struct Tarjan; + +//------------------------------Block_Array------------------------------------ +// Map dense integer indices to Blocks. Uses classic doubling-array trick. +// Abstractly provides an infinite array of Block*'s, initialized to NULL. +// Note that the constructor just zeros things, and since I use Arena +// allocation I do not need a destructor to reclaim storage. +class Block_Array : public ResourceObj { + uint _size; // allocated size, as opposed to formal limit + debug_only(uint _limit;) // limit to formal domain +protected: + Block **_blocks; + void grow( uint i ); // Grow array node to fit + +public: + Arena *_arena; // Arena to allocate in + + Block_Array(Arena *a) : _arena(a), _size(OptoBlockListSize) { + debug_only(_limit=0); + _blocks = NEW_ARENA_ARRAY( a, Block *, OptoBlockListSize ); + for( int i = 0; i < OptoBlockListSize; i++ ) { + _blocks[i] = NULL; + } + } + Block *lookup( uint i ) const // Lookup, or NULL for not mapped + { return (i=Max() ) grow(i); _blocks[i] = n; } + uint Max() const { debug_only(return _limit); return _size; } +}; + + +class Block_List : public Block_Array { +public: + uint _cnt; + Block_List() : Block_Array(Thread::current()->resource_area()), _cnt(0) {} + void push( Block *b ) { map(_cnt++,b); } + Block *pop() { return _blocks[--_cnt]; } + Block *rpop() { Block *b = _blocks[0]; _blocks[0]=_blocks[--_cnt]; return b;} + void remove( uint i ); + void insert( uint i, Block *n ); + uint size() const { return _cnt; } + void reset() { _cnt = 0; } +}; + + +class CFGElement : public ResourceObj { + public: + float _freq; // Execution frequency (estimate) + + CFGElement() : _freq(0.0f) {} + virtual bool is_block() { return false; } + virtual bool is_loop() { return false; } + Block* as_Block() { assert(is_block(), "must be block"); return (Block*)this; } + CFGLoop* as_CFGLoop() { assert(is_loop(), "must be loop"); return (CFGLoop*)this; } +}; + +//------------------------------Block------------------------------------------ +// This class defines a Basic Block. +// Basic blocks are used during the output routines, and are not used during +// any optimization pass. They are created late in the game. +class Block : public CFGElement { + public: + // Nodes in this block, in order + Node_List _nodes; + + // Basic blocks have a Node which defines Control for all Nodes pinned in + // this block. This Node is a RegionNode. Exception-causing Nodes + // (division, subroutines) and Phi functions are always pinned. Later, + // every Node will get pinned to some block. + Node *head() const { return _nodes[0]; } + + // CAUTION: num_preds() is ONE based, so that predecessor numbers match + // input edges to Regions and Phis. + uint num_preds() const { return head()->req(); } + Node *pred(uint i) const { return head()->in(i); } + + // Array of successor blocks, same size as projs array + Block_Array _succs; + + // Basic blocks have some number of Nodes which split control to all + // following blocks. These Nodes are always Projections. The field in + // the Projection and the block-ending Node determine which Block follows. + uint _num_succs; + + // Basic blocks also carry all sorts of good old fashioned DFS information + // used to find loops, loop nesting depth, dominators, etc. + uint _pre_order; // Pre-order DFS number + + // Dominator tree + uint _dom_depth; // Depth in dominator tree for fast LCA + Block* _idom; // Immediate dominator block + + CFGLoop *_loop; // Loop to which this block belongs + uint _rpo; // Number in reverse post order walk + + virtual bool is_block() { return true; } + float succ_prob(uint i); // return probability of i'th successor + + Block* dom_lca(Block* that); // Compute LCA in dominator tree. +#ifdef ASSERT + bool dominates(Block* that) { + int dom_diff = this->_dom_depth - that->_dom_depth; + if (dom_diff > 0) return false; + for (; dom_diff < 0; dom_diff++) that = that->_idom; + return this == that; + } +#endif + + // Report the alignment required by this block. Must be a power of 2. + // The previous block will insert nops to get this alignment. + uint code_alignment(); + + // BLOCK_FREQUENCY is a sentinel to mark uses of constant block frequencies. + // It is currently also used to scale such frequencies relative to + // FreqCountInvocations relative to the old value of 1500. +#define BLOCK_FREQUENCY(f) ((f * (float) 1500) / FreqCountInvocations) + + // Register Pressure (estimate) for Splitting heuristic + uint _reg_pressure; + uint _ihrp_index; + uint _freg_pressure; + uint _fhrp_index; + + // Mark and visited bits for an LCA calculation in insert_anti_dependences. + // Since they hold unique node indexes, they do not need reinitialization. + node_idx_t _raise_LCA_mark; + void set_raise_LCA_mark(node_idx_t x) { _raise_LCA_mark = x; } + node_idx_t raise_LCA_mark() const { return _raise_LCA_mark; } + node_idx_t _raise_LCA_visited; + void set_raise_LCA_visited(node_idx_t x) { _raise_LCA_visited = x; } + node_idx_t raise_LCA_visited() const { return _raise_LCA_visited; } + + // Estimated size in bytes of first instructions in a loop. + uint _first_inst_size; + uint first_inst_size() const { return _first_inst_size; } + void set_first_inst_size(uint s) { _first_inst_size = s; } + + // Compute the size of first instructions in this block. + uint compute_first_inst_size(uint& sum_size, uint inst_cnt, PhaseRegAlloc* ra); + + // Compute alignment padding if the block needs it. + // Align a loop if loop's padding is less or equal to padding limit + // or the size of first instructions in the loop > padding. + uint alignment_padding(int current_offset) { + int block_alignment = code_alignment(); + int max_pad = block_alignment-relocInfo::addr_unit(); + if( max_pad > 0 ) { + assert(is_power_of_2(max_pad+relocInfo::addr_unit()), ""); + int current_alignment = current_offset & max_pad; + if( current_alignment != 0 ) { + uint padding = (block_alignment-current_alignment) & max_pad; + if( !head()->is_Loop() || + padding <= (uint)MaxLoopPad || + first_inst_size() > padding ) { + return padding; + } + } + } + return 0; + } + + // Connector blocks. Connector blocks are basic blocks devoid of + // instructions, but may have relevant non-instruction Nodes, such as + // Phis or MergeMems. Such blocks are discovered and marked during the + // RemoveEmpty phase, and elided during Output. + bool _connector; + void set_connector() { _connector = true; } + bool is_connector() const { return _connector; }; + + // Create a new Block with given head Node. + // Creates the (empty) predecessor arrays. + Block( Arena *a, Node *headnode ) + : CFGElement(), + _nodes(a), + _succs(a), + _num_succs(0), + _pre_order(0), + _idom(0), + _loop(NULL), + _reg_pressure(0), + _ihrp_index(1), + _freg_pressure(0), + _fhrp_index(1), + _raise_LCA_mark(0), + _raise_LCA_visited(0), + _first_inst_size(999999), + _connector(false) { + _nodes.push(headnode); + } + + // Index of 'end' Node + uint end_idx() const { + // %%%%% add a proj after every goto + // so (last->is_block_proj() != last) always, then simplify this code + // This will not give correct end_idx for block 0 when it only contains root. + int last_idx = _nodes.size() - 1; + Node *last = _nodes[last_idx]; + assert(last->is_block_proj() == last || last->is_block_proj() == _nodes[last_idx - _num_succs], ""); + return (last->is_block_proj() == last) ? last_idx : (last_idx - _num_succs); + } + + // Basic blocks have a Node which ends them. This Node determines which + // basic block follows this one in the program flow. This Node is either an + // IfNode, a GotoNode, a JmpNode, or a ReturnNode. + Node *end() const { return _nodes[end_idx()]; } + + // Add an instruction to an existing block. It must go after the head + // instruction and before the end instruction. + void add_inst( Node *n ) { _nodes.insert(end_idx(),n); } + // Find node in block + uint find_node( const Node *n ) const; + // Find and remove n from block list + void find_remove( const Node *n ); + + // Schedule a call next in the block + uint sched_call(Matcher &matcher, Block_Array &bbs, uint node_cnt, Node_List &worklist, int *ready_cnt, MachCallNode *mcall, VectorSet &next_call); + + // Perform basic-block local scheduling + Node *select(PhaseCFG *cfg, Node_List &worklist, int *ready_cnt, VectorSet &next_call, uint sched_slot); + void set_next_call( Node *n, VectorSet &next_call, Block_Array &bbs ); + void needed_for_next_call(Node *this_call, VectorSet &next_call, Block_Array &bbs); + bool schedule_local(PhaseCFG *cfg, Matcher &m, int *ready_cnt, VectorSet &next_call); + // Cleanup if any code lands between a Call and his Catch + void call_catch_cleanup(Block_Array &bbs); + // Detect implicit-null-check opportunities. Basically, find NULL checks + // with suitable memory ops nearby. Use the memory op to do the NULL check. + // I can generate a memory op if there is not one nearby. + void implicit_null_check(PhaseCFG *cfg, Node *proj, Node *val, int allowed_reasons); + + // Return the empty status of a block + enum { not_empty, empty_with_goto, completely_empty }; + int is_Empty() const; + + // Forward through connectors + Block* non_connector() { + Block* s = this; + while (s->is_connector()) { + s = s->_succs[0]; + } + return s; + } + + // Successor block, after forwarding through connectors + Block* non_connector_successor(int i) const { + return _succs[i]->non_connector(); + } + + // Examine block's code shape to predict if it is not commonly executed. + bool has_uncommon_code() const; + + // Use frequency calculations and code shape to predict if the block + // is uncommon. + bool is_uncommon( Block_Array &bbs ) const; + +#ifndef PRODUCT + // Debugging print of basic block + void dump_bidx(const Block* orig) const; + void dump_pred(const Block_Array *bbs, Block* orig) const; + void dump_head( const Block_Array *bbs ) const; + void dump( ) const; + void dump( const Block_Array *bbs ) const; +#endif +}; + + +//------------------------------PhaseCFG--------------------------------------- +// Build an array of Basic Block pointers, one per Node. +class PhaseCFG : public Phase { + private: + // Build a proper looking cfg. Return count of basic blocks + uint build_cfg(); + + // Perform DFS search. + // Setup 'vertex' as DFS to vertex mapping. + // Setup 'semi' as vertex to DFS mapping. + // Set 'parent' to DFS parent. + uint DFS( Tarjan *tarjan ); + + // Helper function to insert a node into a block + void schedule_node_into_block( Node *n, Block *b ); + + // Set the basic block for pinned Nodes + void schedule_pinned_nodes( VectorSet &visited ); + + // I'll need a few machine-specific GotoNodes. Clone from this one. + MachNode *_goto; + void insert_goto_at(uint block_no, uint succ_no); + + Block* insert_anti_dependences(Block* LCA, Node* load, bool verify = false); + void verify_anti_dependences(Block* LCA, Node* load) { + assert(LCA == _bbs[load->_idx], "should already be scheduled"); + insert_anti_dependences(LCA, load, true); + } + + public: + PhaseCFG( Arena *a, RootNode *r, Matcher &m ); + + uint _num_blocks; // Count of basic blocks + Block_List _blocks; // List of basic blocks + RootNode *_root; // Root of whole program + Block_Array _bbs; // Map Nodes to owning Basic Block + Block *_broot; // Basic block of root + uint _rpo_ctr; + CFGLoop* _root_loop; + + // Per node latency estimation, valid only during GCM + GrowableArray _node_latency; + +#ifndef PRODUCT + bool _trace_opto_pipelining; // tracing flag +#endif + + // Build dominators + void Dominators(); + + // Estimate block frequencies based on IfNode probabilities + void Estimate_Block_Frequency(); + + // Global Code Motion. See Click's PLDI95 paper. Place Nodes in specific + // basic blocks; i.e. _bbs now maps _idx for all Nodes to some Block. + void GlobalCodeMotion( Matcher &m, uint unique, Node_List &proj_list ); + + // Compute the (backwards) latency of a node from the uses + void latency_from_uses(Node *n); + + // Compute the (backwards) latency of a node from a single use + int latency_from_use(Node *n, const Node *def, Node *use); + + // Compute the (backwards) latency of a node from the uses of this instruction + void partial_latency_of_defs(Node *n); + + // Schedule Nodes early in their basic blocks. + bool schedule_early(VectorSet &visited, Node_List &roots); + + // For each node, find the latest block it can be scheduled into + // and then select the cheapest block between the latest and earliest + // block to place the node. + void schedule_late(VectorSet &visited, Node_List &stack); + + // Pick a block between early and late that is a cheaper alternative + // to late. Helper for schedule_late. + Block* hoist_to_cheaper_block(Block* LCA, Block* early, Node* self); + + // Compute the instruction global latency with a backwards walk + void ComputeLatenciesBackwards(VectorSet &visited, Node_List &stack); + + // Remove empty basic blocks + void RemoveEmpty(); + bool MoveToNext(Block* bx, uint b_index); + void MoveToEnd(Block* bx, uint b_index); + + // Check for NeverBranch at block end. This needs to become a GOTO to the + // true target. NeverBranch are treated as a conditional branch that always + // goes the same direction for most of the optimizer and are used to give a + // fake exit path to infinite loops. At this late stage they need to turn + // into Goto's so that when you enter the infinite loop you indeed hang. + void convert_NeverBranch_to_Goto(Block *b); + + CFGLoop* create_loop_tree(); + + // Insert a node into a block, and update the _bbs + void insert( Block *b, uint idx, Node *n ) { + b->_nodes.insert( idx, n ); + _bbs.map( n->_idx, b ); + } + +#ifndef PRODUCT + bool trace_opto_pipelining() const { return _trace_opto_pipelining; } + + // Debugging print of CFG + void dump( ) const; // CFG only + void _dump_cfg( const Node *end, VectorSet &visited ) const; + void verify() const; + void dump_headers(); +#else + bool trace_opto_pipelining() const { return false; } +#endif +}; + + +//------------------------------UnionFindInfo---------------------------------- +// Map Block indices to a block-index for a cfg-cover. +// Array lookup in the optimized case. +class UnionFind : public ResourceObj { + uint _cnt, _max; + uint* _indices; + ReallocMark _nesting; // assertion check for reallocations +public: + UnionFind( uint max ); + void reset( uint max ); // Reset to identity map for [0..max] + + uint lookup( uint nidx ) const { + return _indices[nidx]; + } + uint operator[] (uint nidx) const { return lookup(nidx); } + + void map( uint from_idx, uint to_idx ) { + assert( from_idx < _cnt, "oob" ); + _indices[from_idx] = to_idx; + } + void extend( uint from_idx, uint to_idx ); + + uint Size() const { return _cnt; } + + uint Find( uint idx ) { + assert( idx < 65536, "Must fit into uint"); + uint uf_idx = lookup(idx); + return (uf_idx == idx) ? uf_idx : Find_compress(idx); + } + uint Find_compress( uint idx ); + uint Find_const( uint idx ) const; + void Union( uint idx1, uint idx2 ); + +}; + +//----------------------------BlockProbPair--------------------------- +// Ordered pair of Node*. +class BlockProbPair VALUE_OBJ_CLASS_SPEC { +protected: + Block* _target; // block target + float _prob; // probability of edge to block +public: + BlockProbPair() : _target(NULL), _prob(0.0) {} + BlockProbPair(Block* b, float p) : _target(b), _prob(p) {} + + Block* get_target() const { return _target; } + float get_prob() const { return _prob; } +}; + +//------------------------------CFGLoop------------------------------------------- +class CFGLoop : public CFGElement { + int _id; + int _depth; + CFGLoop *_parent; // root of loop tree is the method level "pseudo" loop, it's parent is null + CFGLoop *_sibling; // null terminated list + CFGLoop *_child; // first child, use child's sibling to visit all immediately nested loops + GrowableArray _members; // list of members of loop + GrowableArray _exits; // list of successor blocks and their probabilities + float _exit_prob; // probability any loop exit is taken on a single loop iteration + void update_succ_freq(Block* b, float freq); + + public: + CFGLoop(int id) : + CFGElement(), + _id(id), + _depth(0), + _parent(NULL), + _sibling(NULL), + _child(NULL), + _exit_prob(1.0f) {} + CFGLoop* parent() { return _parent; } + void push_pred(Block* blk, int i, Block_List& worklist, Block_Array& node_to_blk); + void add_member(CFGElement *s) { _members.push(s); } + void add_nested_loop(CFGLoop* cl); + Block* head() { + assert(_members.at(0)->is_block(), "head must be a block"); + Block* hd = _members.at(0)->as_Block(); + assert(hd->_loop == this, "just checking"); + assert(hd->head()->is_Loop(), "must begin with loop head node"); + return hd; + } + Block* backedge_block(); // Return the block on the backedge of the loop (else NULL) + void compute_loop_depth(int depth); + void compute_freq(); // compute frequency with loop assuming head freq 1.0f + void scale_freq(); // scale frequency by loop trip count (including outer loops) + bool in_loop_nest(Block* b); + float trip_count() const { return 1.0f / _exit_prob; } + virtual bool is_loop() { return true; } + int id() { return _id; } + +#ifndef PRODUCT + void dump( ) const; + void dump_tree() const; +#endif +}; diff --git a/hotspot/src/share/vm/opto/buildOopMap.cpp b/hotspot/src/share/vm/opto/buildOopMap.cpp new file mode 100644 index 00000000000..2116c404dda --- /dev/null +++ b/hotspot/src/share/vm/opto/buildOopMap.cpp @@ -0,0 +1,623 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_buildOopMap.cpp.incl" + +// The functions in this file builds OopMaps after all scheduling is done. +// +// OopMaps contain a list of all registers and stack-slots containing oops (so +// they can be updated by GC). OopMaps also contain a list of derived-pointer +// base-pointer pairs. When the base is moved, the derived pointer moves to +// follow it. Finally, any registers holding callee-save values are also +// recorded. These might contain oops, but only the caller knows. +// +// BuildOopMaps implements a simple forward reaching-defs solution. At each +// GC point we'll have the reaching-def Nodes. If the reaching Nodes are +// typed as pointers (no offset), then they are oops. Pointers+offsets are +// derived pointers, and bases can be found from them. Finally, we'll also +// track reaching callee-save values. Note that a copy of a callee-save value +// "kills" it's source, so that only 1 copy of a callee-save value is alive at +// a time. +// +// We run a simple bitvector liveness pass to help trim out dead oops. Due to +// irreducible loops, we can have a reaching def of an oop that only reaches +// along one path and no way to know if it's valid or not on the other path. +// The bitvectors are quite dense and the liveness pass is fast. +// +// At GC points, we consult this information to build OopMaps. All reaching +// defs typed as oops are added to the OopMap. Only 1 instance of a +// callee-save register can be recorded. For derived pointers, we'll have to +// find and record the register holding the base. +// +// The reaching def's is a simple 1-pass worklist approach. I tried a clever +// breadth-first approach but it was worse (showed O(n^2) in the +// pick-next-block code). +// +// The relevent data is kept in a struct of arrays (it could just as well be +// an array of structs, but the struct-of-arrays is generally a little more +// efficient). The arrays are indexed by register number (including +// stack-slots as registers) and so is bounded by 200 to 300 elements in +// practice. One array will map to a reaching def Node (or NULL for +// conflict/dead). The other array will map to a callee-saved register or +// OptoReg::Bad for not-callee-saved. + + +//------------------------------OopFlow---------------------------------------- +// Structure to pass around +struct OopFlow : public ResourceObj { + short *_callees; // Array mapping register to callee-saved + Node **_defs; // array mapping register to reaching def + // or NULL if dead/conflict + // OopFlow structs, when not being actively modified, describe the _end_ of + // this block. + Block *_b; // Block for this struct + OopFlow *_next; // Next free OopFlow + + OopFlow( short *callees, Node **defs ) : _callees(callees), _defs(defs), + _b(NULL), _next(NULL) { } + + // Given reaching-defs for this block start, compute it for this block end + void compute_reach( PhaseRegAlloc *regalloc, int max_reg, Dict *safehash ); + + // Merge these two OopFlows into the 'this' pointer. + void merge( OopFlow *flow, int max_reg ); + + // Copy a 'flow' over an existing flow + void clone( OopFlow *flow, int max_size); + + // Make a new OopFlow from scratch + static OopFlow *make( Arena *A, int max_size ); + + // Build an oopmap from the current flow info + OopMap *build_oop_map( Node *n, int max_reg, PhaseRegAlloc *regalloc, int* live ); +}; + +//------------------------------compute_reach---------------------------------- +// Given reaching-defs for this block start, compute it for this block end +void OopFlow::compute_reach( PhaseRegAlloc *regalloc, int max_reg, Dict *safehash ) { + + for( uint i=0; i<_b->_nodes.size(); i++ ) { + Node *n = _b->_nodes[i]; + + if( n->jvms() ) { // Build an OopMap here? + JVMState *jvms = n->jvms(); + // no map needed for leaf calls + if( n->is_MachSafePoint() && !n->is_MachCallLeaf() ) { + int *live = (int*) (*safehash)[n]; + assert( live, "must find live" ); + n->as_MachSafePoint()->set_oop_map( build_oop_map(n,max_reg,regalloc, live) ); + } + } + + // Assign new reaching def's. + // Note that I padded the _defs and _callees arrays so it's legal + // to index at _defs[OptoReg::Bad]. + OptoReg::Name first = regalloc->get_reg_first(n); + OptoReg::Name second = regalloc->get_reg_second(n); + _defs[first] = n; + _defs[second] = n; + + // Pass callee-save info around copies + int idx = n->is_Copy(); + if( idx ) { // Copies move callee-save info + OptoReg::Name old_first = regalloc->get_reg_first(n->in(idx)); + OptoReg::Name old_second = regalloc->get_reg_second(n->in(idx)); + int tmp_first = _callees[old_first]; + int tmp_second = _callees[old_second]; + _callees[old_first] = OptoReg::Bad; // callee-save is moved, dead in old location + _callees[old_second] = OptoReg::Bad; + _callees[first] = tmp_first; + _callees[second] = tmp_second; + } else if( n->is_Phi() ) { // Phis do not mod callee-saves + assert( _callees[first] == _callees[regalloc->get_reg_first(n->in(1))], "" ); + assert( _callees[second] == _callees[regalloc->get_reg_second(n->in(1))], "" ); + assert( _callees[first] == _callees[regalloc->get_reg_first(n->in(n->req()-1))], "" ); + assert( _callees[second] == _callees[regalloc->get_reg_second(n->in(n->req()-1))], "" ); + } else { + _callees[first] = OptoReg::Bad; // No longer holding a callee-save value + _callees[second] = OptoReg::Bad; + + // Find base case for callee saves + if( n->is_Proj() && n->in(0)->is_Start() ) { + if( OptoReg::is_reg(first) && + regalloc->_matcher.is_save_on_entry(first) ) + _callees[first] = first; + if( OptoReg::is_reg(second) && + regalloc->_matcher.is_save_on_entry(second) ) + _callees[second] = second; + } + } + } +} + +//------------------------------merge------------------------------------------ +// Merge the given flow into the 'this' flow +void OopFlow::merge( OopFlow *flow, int max_reg ) { + assert( _b == NULL, "merging into a happy flow" ); + assert( flow->_b, "this flow is still alive" ); + assert( flow != this, "no self flow" ); + + // Do the merge. If there are any differences, drop to 'bottom' which + // is OptoReg::Bad or NULL depending. + for( int i=0; i_callees[i] ) + _callees[i] = OptoReg::Bad; + // Merge the reaching defs + if( _defs[i] != flow->_defs[i] ) + _defs[i] = NULL; + } + +} + +//------------------------------clone------------------------------------------ +void OopFlow::clone( OopFlow *flow, int max_size ) { + _b = flow->_b; + memcpy( _callees, flow->_callees, sizeof(short)*max_size); + memcpy( _defs , flow->_defs , sizeof(Node*)*max_size); +} + +//------------------------------make------------------------------------------- +OopFlow *OopFlow::make( Arena *A, int max_size ) { + short *callees = NEW_ARENA_ARRAY(A,short,max_size+1); + Node **defs = NEW_ARENA_ARRAY(A,Node*,max_size+1); + debug_only( memset(defs,0,(max_size+1)*sizeof(Node*)) ); + OopFlow *flow = new (A) OopFlow(callees+1, defs+1); + assert( &flow->_callees[OptoReg::Bad] == callees, "Ok to index at OptoReg::Bad" ); + assert( &flow->_defs [OptoReg::Bad] == defs , "Ok to index at OptoReg::Bad" ); + return flow; +} + +//------------------------------bit twiddlers---------------------------------- +static int get_live_bit( int *live, int reg ) { + return live[reg>>LogBitsPerInt] & (1<<(reg&(BitsPerInt-1))); } +static void set_live_bit( int *live, int reg ) { + live[reg>>LogBitsPerInt] |= (1<<(reg&(BitsPerInt-1))); } +static void clr_live_bit( int *live, int reg ) { + live[reg>>LogBitsPerInt] &= ~(1<<(reg&(BitsPerInt-1))); } + +//------------------------------build_oop_map---------------------------------- +// Build an oopmap from the current flow info +OopMap *OopFlow::build_oop_map( Node *n, int max_reg, PhaseRegAlloc *regalloc, int* live ) { + int framesize = regalloc->_framesize; + int max_inarg_slot = OptoReg::reg2stack(regalloc->_matcher._new_SP); + debug_only( char *dup_check = NEW_RESOURCE_ARRAY(char,OptoReg::stack0()); + memset(dup_check,0,OptoReg::stack0()) ); + + OopMap *omap = new OopMap( framesize, max_inarg_slot ); + MachCallNode *mcall = n->is_MachCall() ? n->as_MachCall() : NULL; + JVMState* jvms = n->jvms(); + + // For all registers do... + for( int reg=0; regis_reg() && !r->is_concrete()) { + continue; + } + + // See if dead (no reaching def). + Node *def = _defs[reg]; // Get reaching def + assert( def, "since live better have reaching def" ); + + // Classify the reaching def as oop, derived, callee-save, dead, or other + const Type *t = def->bottom_type(); + if( t->isa_oop_ptr() ) { // Oop or derived? + assert( !OptoReg::is_valid(_callees[reg]), "oop can't be callee save" ); +#ifdef _LP64 + // 64-bit pointers record oop-ishness on 2 aligned adjacent registers. + // Make sure both are record from the same reaching def, but do not + // put both into the oopmap. + if( (reg&1) == 1 ) { // High half of oop-pair? + assert( _defs[reg-1] == _defs[reg], "both halves from same reaching def" ); + continue; // Do not record high parts in oopmap + } +#endif + + // Check for a legal reg name in the oopMap and bailout if it is not. + if (!omap->legal_vm_reg_name(r)) { + regalloc->C->record_method_not_compilable("illegal oopMap register name"); + continue; + } + if( t->is_ptr()->_offset == 0 ) { // Not derived? + if( mcall ) { + // Outgoing argument GC mask responsibility belongs to the callee, + // not the caller. Inspect the inputs to the call, to see if + // this live-range is one of them. + uint cnt = mcall->tf()->domain()->cnt(); + uint j; + for( j = TypeFunc::Parms; j < cnt; j++) + if( mcall->in(j) == def ) + break; // reaching def is an argument oop + if( j < cnt ) // arg oops dont go in GC map + continue; // Continue on to the next register + } + omap->set_oop(r); + } else { // Else it's derived. + // Find the base of the derived value. + uint i; + // Fast, common case, scan + for( i = jvms->oopoff(); i < n->req(); i+=2 ) + if( n->in(i) == def ) break; // Common case + if( i == n->req() ) { // Missed, try a more generous scan + // Scan again, but this time peek through copies + for( i = jvms->oopoff(); i < n->req(); i+=2 ) { + Node *m = n->in(i); // Get initial derived value + while( 1 ) { + Node *d = def; // Get initial reaching def + while( 1 ) { // Follow copies of reaching def to end + if( m == d ) goto found; // breaks 3 loops + int idx = d->is_Copy(); + if( !idx ) break; + d = d->in(idx); // Link through copy + } + int idx = m->is_Copy(); + if( !idx ) break; + m = m->in(idx); + } + } + guarantee( 0, "must find derived/base pair" ); + } + found: ; + Node *base = n->in(i+1); // Base is other half of pair + int breg = regalloc->get_reg_first(base); + VMReg b = OptoReg::as_VMReg(OptoReg::Name(breg), framesize, max_inarg_slot); + + // I record liveness at safepoints BEFORE I make the inputs + // live. This is because argument oops are NOT live at a + // safepoint (or at least they cannot appear in the oopmap). + // Thus bases of base/derived pairs might not be in the + // liveness data but they need to appear in the oopmap. + if( get_live_bit(live,breg) == 0 ) {// Not live? + // Flag it, so next derived pointer won't re-insert into oopmap + set_live_bit(live,breg); + // Already missed our turn? + if( breg < reg ) { + if (b->is_stack() || b->is_concrete() || true ) { + omap->set_oop( b); + } + } + } + if (b->is_stack() || b->is_concrete() || true ) { + omap->set_derived_oop( r, b); + } + } + + } else if( OptoReg::is_valid(_callees[reg])) { // callee-save? + // It's a callee-save value + assert( dup_check[_callees[reg]]==0, "trying to callee save same reg twice" ); + debug_only( dup_check[_callees[reg]]=1; ) + VMReg callee = OptoReg::as_VMReg(OptoReg::Name(_callees[reg])); + if ( callee->is_concrete() || true ) { + omap->set_callee_saved( r, callee); + } + + } else { + // Other - some reaching non-oop value + omap->set_value( r); + } + + } + +#ifdef ASSERT + /* Nice, Intel-only assert + int cnt_callee_saves=0; + int reg2 = 0; + while (OptoReg::is_reg(reg2)) { + if( dup_check[reg2] != 0) cnt_callee_saves++; + assert( cnt_callee_saves==3 || cnt_callee_saves==5, "missed some callee-save" ); + reg2++; + } + */ +#endif + + return omap; +} + +//------------------------------do_liveness------------------------------------ +// Compute backwards liveness on registers +static void do_liveness( PhaseRegAlloc *regalloc, PhaseCFG *cfg, Block_List *worklist, int max_reg_ints, Arena *A, Dict *safehash ) { + int *live = NEW_ARENA_ARRAY(A, int, (cfg->_num_blocks+1) * max_reg_ints); + int *tmp_live = &live[cfg->_num_blocks * max_reg_ints]; + Node *root = cfg->C->root(); + // On CISC platforms, get the node representing the stack pointer that regalloc + // used for spills + Node *fp = NodeSentinel; + if (UseCISCSpill && root->req() > 1) { + fp = root->in(1)->in(TypeFunc::FramePtr); + } + memset( live, 0, cfg->_num_blocks * (max_reg_ints<req(); i++ ) + worklist->push(cfg->_bbs[root->in(i)->_idx]); + + // ZKM.jar includes tiny infinite loops which are unreached from below. + // If we missed any blocks, we'll retry here after pushing all missed + // blocks on the worklist. Normally this outer loop never trips more + // than once. + while( 1 ) { + + while( worklist->size() ) { // Standard worklist algorithm + Block *b = worklist->rpop(); + + // Copy first successor into my tmp_live space + int s0num = b->_succs[0]->_pre_order; + int *t = &live[s0num*max_reg_ints]; + for( int i=0; i_num_succs; j++ ) { + uint sjnum = b->_succs[j]->_pre_order; + int *t = &live[sjnum*max_reg_ints]; + for( int i=0; i_nodes.size()-1; k>=0; k-- ) { + Node *n = b->_nodes[k]; + // KILL def'd bits + int first = regalloc->get_reg_first(n); + int second = regalloc->get_reg_second(n); + if( OptoReg::is_valid(first) ) clr_live_bit(tmp_live,first); + if( OptoReg::is_valid(second) ) clr_live_bit(tmp_live,second); + + MachNode *m = n->is_Mach() ? n->as_Mach() : NULL; + + // Check if m is potentially a CISC alternate instruction (i.e, possibly + // synthesized by RegAlloc from a conventional instruction and a + // spilled input) + bool is_cisc_alternate = false; + if (UseCISCSpill && m) { + is_cisc_alternate = m->is_cisc_alternate(); + } + + // GEN use'd bits + for( uint l=1; lreq(); l++ ) { + Node *def = n->in(l); + assert(def != 0, "input edge required"); + int first = regalloc->get_reg_first(def); + int second = regalloc->get_reg_second(def); + if( OptoReg::is_valid(first) ) set_live_bit(tmp_live,first); + if( OptoReg::is_valid(second) ) set_live_bit(tmp_live,second); + // If we use the stack pointer in a cisc-alternative instruction, + // check for use as a memory operand. Then reconstruct the RegName + // for this stack location, and set the appropriate bit in the + // live vector 4987749. + if (is_cisc_alternate && def == fp) { + const TypePtr *adr_type = NULL; + intptr_t offset; + const Node* base = m->get_base_and_disp(offset, adr_type); + if (base == NodeSentinel) { + // Machnode has multiple memory inputs. We are unable to reason + // with these, but are presuming (with trepidation) that not any of + // them are oops. This can be fixed by making get_base_and_disp() + // look at a specific input instead of all inputs. + assert(!def->bottom_type()->isa_oop_ptr(), "expecting non-oop mem input"); + } else if (base != fp || offset == Type::OffsetBot) { + // Do nothing: the fp operand is either not from a memory use + // (base == NULL) OR the fp is used in a non-memory context + // (base is some other register) OR the offset is not constant, + // so it is not a stack slot. + } else { + assert(offset >= 0, "unexpected negative offset"); + offset -= (offset % jintSize); // count the whole word + int stack_reg = regalloc->offset2reg(offset); + if (OptoReg::is_stack(stack_reg)) { + set_live_bit(tmp_live, stack_reg); + } else { + assert(false, "stack_reg not on stack?"); + } + } + } + } + + if( n->jvms() ) { // Record liveness at safepoint + + // This placement of this stanza means inputs to calls are + // considered live at the callsite's OopMap. Argument oops are + // hence live, but NOT included in the oopmap. See cutout in + // build_oop_map. Debug oops are live (and in OopMap). + int *n_live = NEW_ARENA_ARRAY(A, int, max_reg_ints); + for( int l=0; lInsert(n,n_live); + } + + } + + // Now at block top, see if we have any changes. If so, propagate + // to prior blocks. + int *old_live = &live[b->_pre_order*max_reg_ints]; + int l; + for( l=0; lnum_preds(); l++ ) + worklist->push(cfg->_bbs[b->pred(l)->_idx]); + } + } + + // Scan for any missing safepoints. Happens to infinite loops + // ala ZKM.jar + uint i; + for( i=1; i_num_blocks; i++ ) { + Block *b = cfg->_blocks[i]; + uint j; + for( j=1; j_nodes.size(); j++ ) + if( b->_nodes[j]->jvms() && + (*safehash)[b->_nodes[j]] == NULL ) + break; + if( j_nodes.size() ) break; + } + if( i == cfg->_num_blocks ) + break; // Got 'em all +#ifndef PRODUCT + if( PrintOpto && Verbose ) + tty->print_cr("retripping live calc"); +#endif + // Force the issue (expensively): recheck everybody + for( i=1; i_num_blocks; i++ ) + worklist->push(cfg->_blocks[i]); + } + +} + +//------------------------------BuildOopMaps----------------------------------- +// Collect GC mask info - where are all the OOPs? +void Compile::BuildOopMaps() { + NOT_PRODUCT( TracePhase t3("bldOopMaps", &_t_buildOopMaps, TimeCompiler); ) + // Can't resource-mark because I need to leave all those OopMaps around, + // or else I need to resource-mark some arena other than the default. + // ResourceMark rm; // Reclaim all OopFlows when done + int max_reg = _regalloc->_max_reg; // Current array extent + + Arena *A = Thread::current()->resource_area(); + Block_List worklist; // Worklist of pending blocks + + int max_reg_ints = round_to(max_reg, BitsPerInt)>>LogBitsPerInt; + Dict *safehash = NULL; // Used for assert only + // Compute a backwards liveness per register. Needs a bitarray of + // #blocks x (#registers, rounded up to ints) + safehash = new Dict(cmpkey,hashkey,A); + do_liveness( _regalloc, _cfg, &worklist, max_reg_ints, A, safehash ); + OopFlow *free_list = NULL; // Free, unused + + // Array mapping blocks to completed oopflows + OopFlow **flows = NEW_ARENA_ARRAY(A, OopFlow*, _cfg->_num_blocks); + memset( flows, 0, _cfg->_num_blocks*sizeof(OopFlow*) ); + + + // Do the first block 'by hand' to prime the worklist + Block *entry = _cfg->_blocks[1]; + OopFlow *rootflow = OopFlow::make(A,max_reg); + // Initialize to 'bottom' (not 'top') + memset( rootflow->_callees, OptoReg::Bad, max_reg*sizeof(short) ); + memset( rootflow->_defs , 0, max_reg*sizeof(Node*) ); + flows[entry->_pre_order] = rootflow; + + // Do the first block 'by hand' to prime the worklist + rootflow->_b = entry; + rootflow->compute_reach( _regalloc, max_reg, safehash ); + for( uint i=0; i_num_succs; i++ ) + worklist.push(entry->_succs[i]); + + // Now worklist contains blocks which have some, but perhaps not all, + // predecessors visited. + while( worklist.size() ) { + // Scan for a block with all predecessors visited, or any randoms slob + // otherwise. All-preds-visited order allows me to recycle OopFlow + // structures rapidly and cut down on the memory footprint. + // Note: not all predecessors might be visited yet (must happen for + // irreducible loops). This is OK, since every live value must have the + // SAME reaching def for the block, so any reaching def is OK. + uint i; + + Block *b = worklist.pop(); + // Ignore root block + if( b == _cfg->_broot ) continue; + // Block is already done? Happens if block has several predecessors, + // he can get on the worklist more than once. + if( flows[b->_pre_order] ) continue; + + // If this block has a visited predecessor AND that predecessor has this + // last block as his only undone child, we can move the OopFlow from the + // pred to this block. Otherwise we have to grab a new OopFlow. + OopFlow *flow = NULL; // Flag for finding optimized flow + Block *pred = (Block*)0xdeadbeef; + uint j; + // Scan this block's preds to find a done predecessor + for( j=1; jnum_preds(); j++ ) { + Block *p = _cfg->_bbs[b->pred(j)->_idx]; + OopFlow *p_flow = flows[p->_pre_order]; + if( p_flow ) { // Predecessor is done + assert( p_flow->_b == p, "cross check" ); + pred = p; // Record some predecessor + // If all successors of p are done except for 'b', then we can carry + // p_flow forward to 'b' without copying, otherwise we have to draw + // from the free_list and clone data. + uint k; + for( k=0; k_num_succs; k++ ) + if( !flows[p->_succs[k]->_pre_order] && + p->_succs[k] != b ) + break; + + // Either carry-forward the now-unused OopFlow for b's use + // or draw a new one from the free list + if( k==p->_num_succs ) { + flow = p_flow; + break; // Found an ideal pred, use him + } + } + } + + if( flow ) { + // We have an OopFlow that's the last-use of a predecessor. + // Carry it forward. + } else { // Draw a new OopFlow from the freelist + if( !free_list ) + free_list = OopFlow::make(A,max_reg); + flow = free_list; + assert( flow->_b == NULL, "oopFlow is not free" ); + free_list = flow->_next; + flow->_next = NULL; + + // Copy/clone over the data + flow->clone(flows[pred->_pre_order], max_reg); + } + + // Mark flow for block. Blocks can only be flowed over once, + // because after the first time they are guarded from entering + // this code again. + assert( flow->_b == pred, "have some prior flow" ); + flow->_b = NULL; + + // Now push flow forward + flows[b->_pre_order] = flow;// Mark flow for this block + flow->_b = b; + flow->compute_reach( _regalloc, max_reg, safehash ); + + // Now push children onto worklist + for( i=0; i_num_succs; i++ ) + worklist.push(b->_succs[i]); + + } +} diff --git a/hotspot/src/share/vm/opto/bytecodeInfo.cpp b/hotspot/src/share/vm/opto/bytecodeInfo.cpp new file mode 100644 index 00000000000..10648c654ac --- /dev/null +++ b/hotspot/src/share/vm/opto/bytecodeInfo.cpp @@ -0,0 +1,490 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_bytecodeInfo.cpp.incl" + +// These variables are declared in parse1.cpp +extern int explicit_null_checks_inserted; +extern int explicit_null_checks_elided; +extern int explicit_null_checks_inserted_old; +extern int explicit_null_checks_elided_old; +extern int nodes_created_old; +extern int nodes_created; +extern int methods_parsed_old; +extern int methods_parsed; +extern int methods_seen; +extern int methods_seen_old; + + +//============================================================================= +//------------------------------InlineTree------------------------------------- +InlineTree::InlineTree( Compile* c, const InlineTree *caller_tree, ciMethod* callee, JVMState* caller_jvms, int caller_bci, float site_invoke_ratio ) +: C(c), _caller_jvms(caller_jvms), + _caller_tree((InlineTree*)caller_tree), + _method(callee), _site_invoke_ratio(site_invoke_ratio), + _count_inline_bcs(method()->code_size()) { + NOT_PRODUCT(_count_inlines = 0;) + if (_caller_jvms != NULL) { + // Keep a private copy of the caller_jvms: + _caller_jvms = new (C) JVMState(caller_jvms->method(), caller_tree->caller_jvms()); + _caller_jvms->set_bci(caller_jvms->bci()); + } + assert(_caller_jvms->same_calls_as(caller_jvms), "consistent JVMS"); + assert((caller_tree == NULL ? 0 : caller_tree->inline_depth() + 1) == inline_depth(), "correct (redundant) depth parameter"); + assert(caller_bci == this->caller_bci(), "correct (redundant) bci parameter"); + if (UseOldInlining) { + // Update hierarchical counts, count_inline_bcs() and count_inlines() + InlineTree *caller = (InlineTree *)caller_tree; + for( ; caller != NULL; caller = ((InlineTree *)(caller->caller_tree())) ) { + caller->_count_inline_bcs += count_inline_bcs(); + NOT_PRODUCT(caller->_count_inlines++;) + } + } +} + +InlineTree::InlineTree(Compile* c, ciMethod* callee_method, JVMState* caller_jvms, float site_invoke_ratio) +: C(c), _caller_jvms(caller_jvms), _caller_tree(NULL), + _method(callee_method), _site_invoke_ratio(site_invoke_ratio), + _count_inline_bcs(method()->code_size()) { + NOT_PRODUCT(_count_inlines = 0;) + assert(!UseOldInlining, "do not use for old stuff"); +} + + + +static void print_indent(int depth) { + tty->print(" "); + for (int i = depth; i != 0; --i) tty->print(" "); +} + +// positive filter: should send be inlined? returns NULL, if yes, or rejection msg +const char* InlineTree::shouldInline(ciMethod* callee_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const { + // Allows targeted inlining + if(callee_method->should_inline()) { + *wci_result = *(WarmCallInfo::always_hot()); + if (PrintInlining && Verbose) { + print_indent(inline_depth()); + tty->print_cr("Inlined method is hot: "); + } + return NULL; + } + + // positive filter: should send be inlined? returns NULL (--> yes) + // or rejection msg + int max_size = C->max_inline_size(); + int size = callee_method->code_size(); + + // Check for too many throws (and not too huge) + if(callee_method->interpreter_throwout_count() > InlineThrowCount && size < InlineThrowMaxSize ) { + wci_result->set_profit(wci_result->profit() * 100); + if (PrintInlining && Verbose) { + print_indent(inline_depth()); + tty->print_cr("Inlined method with many throws (throws=%d):", callee_method->interpreter_throwout_count()); + } + return NULL; + } + + if (!UseOldInlining) { + return NULL; // size and frequency are represented in a new way + } + + int call_site_count = method()->scale_count(profile.count()); + int invoke_count = method()->interpreter_invocation_count(); + assert( invoke_count != 0, "Require invokation count greater than zero"); + int freq = call_site_count/invoke_count; + // bump the max size if the call is frequent + if ((freq >= InlineFrequencyRatio) || (call_site_count >= InlineFrequencyCount)) { + max_size = C->freq_inline_size(); + if (size <= max_size && TraceFrequencyInlining) { + print_indent(inline_depth()); + tty->print_cr("Inlined frequent method (freq=%d count=%d):", freq, call_site_count); + print_indent(inline_depth()); + callee_method->print(); + tty->cr(); + } + } else { + // Not hot. Check for medium-sized pre-existing nmethod at cold sites. + if (callee_method->has_compiled_code() && callee_method->instructions_size() > InlineSmallCode/4) + return "already compiled into a medium method"; + } + if (size > max_size) { + if (max_size > C->max_inline_size()) + return "hot method too big"; + return "too big"; + } + return NULL; +} + + +// negative filter: should send NOT be inlined? returns NULL, ok to inline, or rejection msg +const char* InlineTree::shouldNotInline(ciMethod *callee_method, WarmCallInfo* wci_result) const { + // negative filter: should send NOT be inlined? returns NULL (--> inline) or rejection msg + if (!UseOldInlining) { + const char* fail = NULL; + if (callee_method->is_abstract()) fail = "abstract method"; + // note: we allow ik->is_abstract() + if (!callee_method->holder()->is_initialized()) fail = "method holder not initialized"; + if (callee_method->is_native()) fail = "native method"; + + if (fail) { + *wci_result = *(WarmCallInfo::always_cold()); + return fail; + } + + if (callee_method->has_unloaded_classes_in_signature()) { + wci_result->set_profit(wci_result->profit() * 0.1); + } + + // don't inline exception code unless the top method belongs to an + // exception class + if (callee_method->holder()->is_subclass_of(C->env()->Throwable_klass())) { + ciMethod* top_method = caller_jvms() ? caller_jvms()->of_depth(1)->method() : method(); + if (!top_method->holder()->is_subclass_of(C->env()->Throwable_klass())) { + wci_result->set_profit(wci_result->profit() * 0.1); + } + } + + if (callee_method->has_compiled_code() && callee_method->instructions_size() > InlineSmallCode) { + wci_result->set_profit(wci_result->profit() * 0.1); + // %%% adjust wci_result->size()? + } + + return NULL; + } + + // First check all inlining restrictions which are required for correctness + if (callee_method->is_abstract()) return "abstract method"; + // note: we allow ik->is_abstract() + if (!callee_method->holder()->is_initialized()) return "method holder not initialized"; + if (callee_method->is_native()) return "native method"; + if (callee_method->has_unloaded_classes_in_signature()) return "unloaded signature classes"; + + if (callee_method->should_inline()) { + // ignore heuristic controls on inlining + return NULL; + } + + // Now perform checks which are heuristic + + if( callee_method->has_compiled_code() && callee_method->instructions_size() > InlineSmallCode ) + return "already compiled into a big method"; + + // don't inline exception code unless the top method belongs to an + // exception class + if (caller_tree() != NULL && + callee_method->holder()->is_subclass_of(C->env()->Throwable_klass())) { + const InlineTree *top = this; + while (top->caller_tree() != NULL) top = top->caller_tree(); + ciInstanceKlass* k = top->method()->holder(); + if (!k->is_subclass_of(C->env()->Throwable_klass())) + return "exception method"; + } + + // use frequency-based objections only for non-trivial methods + if (callee_method->code_size() <= MaxTrivialSize) return NULL; + if (UseInterpreter && !CompileTheWorld) { // don't use counts with -Xcomp or CTW + if (!callee_method->has_compiled_code() && !callee_method->was_executed_more_than(0)) return "never executed"; + if (!callee_method->was_executed_more_than(MIN2(MinInliningThreshold, CompileThreshold >> 1))) return "executed < MinInliningThreshold times"; + } + + if (callee_method->should_not_inline()) { + return "disallowed by CompilerOracle"; + } + + return NULL; +} + +//-----------------------------try_to_inline----------------------------------- +// return NULL if ok, reason for not inlining otherwise +// Relocated from "InliningClosure::try_to_inline" +const char* InlineTree::try_to_inline(ciMethod* callee_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) { + ciMethod* caller_method = method(); + + // Old algorithm had funny accumulating BC-size counters + if (UseOldInlining && ClipInlining + && (int)count_inline_bcs() >= DesiredMethodLimit) { + return "size > DesiredMethodLimit"; + } + + const char *msg = NULL; + if ((msg = shouldInline(callee_method, caller_bci, profile, wci_result)) != NULL) return msg; + if ((msg = shouldNotInline(callee_method, wci_result)) != NULL) return msg; + + bool is_accessor = InlineAccessors && callee_method->is_accessor(); + + // suppress a few checks for accessors and trivial methods + if (!is_accessor && callee_method->code_size() > MaxTrivialSize) { + // don't inline into giant methods + if (C->unique() > (uint)NodeCountInliningCutoff) return "NodeCountInliningCutoff"; + + // don't inline unreached call sites + if (profile.count() == 0) return "call site not reached"; + } + + if (!C->do_inlining() && InlineAccessors && !is_accessor) return "not an accessor"; + + if( inline_depth() > MaxInlineLevel ) return "inlining too deep"; + if( method() == callee_method && + inline_depth() > MaxRecursiveInlineLevel ) return "recursively inlining too deep"; + + int size = callee_method->code_size(); + + if (UseOldInlining && ClipInlining + && (int)count_inline_bcs() + size >= DesiredMethodLimit) { + return "size > DesiredMethodLimit"; + } + + // ok, inline this method + return NULL; +} + +//------------------------------pass_initial_checks---------------------------- +bool pass_initial_checks(ciMethod* caller_method, int caller_bci, ciMethod* callee_method) { + ciInstanceKlass *callee_holder = callee_method ? callee_method->holder() : NULL; + // Check if a callee_method was suggested + if( callee_method == NULL ) return false; + // Check if klass of callee_method is loaded + if( !callee_holder->is_loaded() ) return false; + if( !callee_holder->is_initialized() ) return false; + if( !UseInterpreter || CompileTheWorld /* running Xcomp or CTW */ ) { + // Checks that constant pool's call site has been visited + // stricter than callee_holder->is_initialized() + ciBytecodeStream iter(caller_method); + iter.force_bci(caller_bci); + int index = iter.get_index_big(); + if( !caller_method->is_klass_loaded(index, true) ) { + return false; + } + // Try to do constant pool resolution if running Xcomp + Bytecodes::Code call_bc = iter.cur_bc(); + if( !caller_method->check_call(index, call_bc == Bytecodes::_invokestatic) ) { + return false; + } + } + // We will attempt to see if a class/field/etc got properly loaded. If it + // did not, it may attempt to throw an exception during our probing. Catch + // and ignore such exceptions and do not attempt to compile the method. + if( callee_method->should_exclude() ) return false; + + return true; +} + +#ifndef PRODUCT +//------------------------------print_inlining--------------------------------- +// Really, the failure_msg can be a success message also. +void InlineTree::print_inlining(ciMethod *callee_method, int caller_bci, const char *failure_msg) const { + print_indent(inline_depth()); + tty->print("@ %d ", caller_bci); + if( callee_method ) callee_method->print_short_name(); + else tty->print(" callee not monotonic or profiled"); + tty->print(" %s", (failure_msg ? failure_msg : "inline")); + if( Verbose && callee_method ) { + const InlineTree *top = this; + while( top->caller_tree() != NULL ) { top = top->caller_tree(); } + tty->print(" bcs: %d+%d invoked: %d", top->count_inline_bcs(), callee_method->code_size(), callee_method->interpreter_invocation_count()); + } + tty->cr(); +} +#endif + +//------------------------------ok_to_inline----------------------------------- +WarmCallInfo* InlineTree::ok_to_inline(ciMethod* callee_method, JVMState* jvms, ciCallProfile& profile, WarmCallInfo* initial_wci) { + assert(callee_method != NULL, "caller checks for optimized virtual!"); +#ifdef ASSERT + // Make sure the incoming jvms has the same information content as me. + // This means that we can eventually make this whole class AllStatic. + if (jvms->caller() == NULL) { + assert(_caller_jvms == NULL, "redundant instance state"); + } else { + assert(_caller_jvms->same_calls_as(jvms->caller()), "redundant instance state"); + } + assert(_method == jvms->method(), "redundant instance state"); +#endif + const char *failure_msg = NULL; + int caller_bci = jvms->bci(); + ciMethod *caller_method = jvms->method(); + + if( !pass_initial_checks(caller_method, caller_bci, callee_method)) { + if( PrintInlining ) { + failure_msg = "failed_initial_checks"; + print_inlining( callee_method, caller_bci, failure_msg); + } + return NULL; + } + + // Check if inlining policy says no. + WarmCallInfo wci = *(initial_wci); + failure_msg = try_to_inline(callee_method, caller_bci, profile, &wci); + if (failure_msg != NULL && C->log() != NULL) { + C->log()->begin_elem("inline_fail reason='"); + C->log()->text("%s", failure_msg); + C->log()->end_elem("'"); + } + +#ifndef PRODUCT + if (UseOldInlining && InlineWarmCalls + && (PrintOpto || PrintOptoInlining || PrintInlining)) { + bool cold = wci.is_cold(); + bool hot = !cold && wci.is_hot(); + bool old_cold = (failure_msg != NULL); + if (old_cold != cold || (Verbose || WizardMode)) { + tty->print(" OldInlining= %4s : %s\n WCI=", + old_cold ? "cold" : "hot", failure_msg ? failure_msg : "OK"); + wci.print(); + } + } +#endif + if (UseOldInlining) { + if (failure_msg == NULL) + wci = *(WarmCallInfo::always_hot()); + else + wci = *(WarmCallInfo::always_cold()); + } + if (!InlineWarmCalls) { + if (!wci.is_cold() && !wci.is_hot()) { + // Do not inline the warm calls. + wci = *(WarmCallInfo::always_cold()); + } + } + + if (!wci.is_cold()) { + // In -UseOldInlining, the failure_msg may also be a success message. + if (failure_msg == NULL) failure_msg = "inline (hot)"; + + // Inline! + if( PrintInlining ) print_inlining( callee_method, caller_bci, failure_msg); + if (UseOldInlining) + build_inline_tree_for_callee(callee_method, jvms, caller_bci); + if (InlineWarmCalls && !wci.is_hot()) + return new (C) WarmCallInfo(wci); // copy to heap + return WarmCallInfo::always_hot(); + } + + // Do not inline + if (failure_msg == NULL) failure_msg = "too cold to inline"; + if( PrintInlining ) print_inlining( callee_method, caller_bci, failure_msg); + return NULL; +} + +//------------------------------compute_callee_frequency----------------------- +float InlineTree::compute_callee_frequency( int caller_bci ) const { + int count = method()->interpreter_call_site_count(caller_bci); + int invcnt = method()->interpreter_invocation_count(); + float freq = (float)count/(float)invcnt; + // Call-site count / interpreter invocation count, scaled recursively. + // Always between 0.0 and 1.0. Represents the percentage of the method's + // total execution time used at this call site. + + return freq; +} + +//------------------------------build_inline_tree_for_callee------------------- +InlineTree *InlineTree::build_inline_tree_for_callee( ciMethod* callee_method, JVMState* caller_jvms, int caller_bci) { + float recur_frequency = _site_invoke_ratio * compute_callee_frequency(caller_bci); + // Attempt inlining. + InlineTree* old_ilt = callee_at(caller_bci, callee_method); + if (old_ilt != NULL) { + return old_ilt; + } + InlineTree *ilt = new InlineTree( C, this, callee_method, caller_jvms, caller_bci, recur_frequency ); + _subtrees.append( ilt ); + + NOT_PRODUCT( _count_inlines += 1; ) + + return ilt; +} + + +//---------------------------------------callee_at----------------------------- +InlineTree *InlineTree::callee_at(int bci, ciMethod* callee) const { + for (int i = 0; i < _subtrees.length(); i++) { + InlineTree* sub = _subtrees.at(i); + if (sub->caller_bci() == bci && callee == sub->method()) { + return sub; + } + } + return NULL; +} + + +//------------------------------build_inline_tree_root------------------------- +InlineTree *InlineTree::build_inline_tree_root() { + Compile* C = Compile::current(); + + // Root of inline tree + InlineTree *ilt = new InlineTree(C, NULL, C->method(), NULL, -1, 1.0F); + + return ilt; +} + + +//-------------------------find_subtree_from_root----------------------------- +// Given a jvms, which determines a call chain from the root method, +// find the corresponding inline tree. +// Note: This method will be removed or replaced as InlineTree goes away. +InlineTree* InlineTree::find_subtree_from_root(InlineTree* root, JVMState* jvms, ciMethod* callee, bool create_if_not_found) { + InlineTree* iltp = root; + uint depth = jvms && jvms->has_method() ? jvms->depth() : 0; + for (uint d = 1; d <= depth; d++) { + JVMState* jvmsp = jvms->of_depth(d); + // Select the corresponding subtree for this bci. + assert(jvmsp->method() == iltp->method(), "tree still in sync"); + ciMethod* d_callee = (d == depth) ? callee : jvms->of_depth(d+1)->method(); + InlineTree* sub = iltp->callee_at(jvmsp->bci(), d_callee); + if (!sub) { + if (create_if_not_found && d == depth) { + return iltp->build_inline_tree_for_callee(d_callee, jvmsp, jvmsp->bci()); + } + assert(sub != NULL, "should be a sub-ilt here"); + return NULL; + } + iltp = sub; + } + return iltp; +} + +// ---------------------------------------------------------------------------- +#ifndef PRODUCT + +static void per_method_stats() { + // Compute difference between this method's cumulative totals and old totals + int explicit_null_checks_cur = explicit_null_checks_inserted - explicit_null_checks_inserted_old; + int elided_null_checks_cur = explicit_null_checks_elided - explicit_null_checks_elided_old; + + // Print differences + if( explicit_null_checks_cur ) + tty->print_cr("XXX Explicit NULL checks inserted: %d", explicit_null_checks_cur); + if( elided_null_checks_cur ) + tty->print_cr("XXX Explicit NULL checks removed at parse time: %d", elided_null_checks_cur); + + // Store the current cumulative totals + nodes_created_old = nodes_created; + methods_parsed_old = methods_parsed; + methods_seen_old = methods_seen; + explicit_null_checks_inserted_old = explicit_null_checks_inserted; + explicit_null_checks_elided_old = explicit_null_checks_elided; +} + +#endif diff --git a/hotspot/src/share/vm/opto/c2_globals.cpp b/hotspot/src/share/vm/opto/c2_globals.cpp new file mode 100644 index 00000000000..5715b24ba57 --- /dev/null +++ b/hotspot/src/share/vm/opto/c2_globals.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_c2_globals.cpp.incl" + +C2_FLAGS(MATERIALIZE_DEVELOPER_FLAG, MATERIALIZE_PD_DEVELOPER_FLAG, MATERIALIZE_PRODUCT_FLAG, MATERIALIZE_PD_PRODUCT_FLAG, MATERIALIZE_DIAGNOSTIC_FLAG, MATERIALIZE_NOTPRODUCT_FLAG) diff --git a/hotspot/src/share/vm/opto/c2_globals.hpp b/hotspot/src/share/vm/opto/c2_globals.hpp new file mode 100644 index 00000000000..360300255d7 --- /dev/null +++ b/hotspot/src/share/vm/opto/c2_globals.hpp @@ -0,0 +1,382 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Defines all globals flags used by the server compiler. +// + +#define C2_FLAGS(develop, develop_pd, product, product_pd, diagnostic, notproduct) \ + \ + notproduct(intx, CompileZapFirst, 0, \ + "If +ZapDeadCompiledLocals, " \ + "skip this many before compiling in zap calls") \ + \ + notproduct(intx, CompileZapLast, -1, \ + "If +ZapDeadCompiledLocals, " \ + "compile this many after skipping (incl. skip count, -1 = all)") \ + \ + notproduct(intx, ZapDeadCompiledLocalsFirst, 0, \ + "If +ZapDeadCompiledLocals, " \ + "skip this many before really doing it") \ + \ + notproduct(intx, ZapDeadCompiledLocalsLast, -1, \ + "If +ZapDeadCompiledLocals, " \ + "do this many after skipping (incl. skip count, -1 = all)") \ + \ + develop(intx, OptoPrologueNops, 0, \ + "Insert this many extra nop instructions " \ + "in the prologue of every nmethod") \ + \ + product_pd(intx, InteriorEntryAlignment, \ + "Code alignment for interior entry points " \ + "in generated code (in bytes)") \ + \ + product_pd(intx, OptoLoopAlignment, \ + "Align inner loops to zero relative to this modulus") \ + \ + product(intx, MaxLoopPad, (OptoLoopAlignment-1), \ + "Align a loop if padding size in bytes is less or equal to this value") \ + \ + product(intx, NumberOfLoopInstrToAlign, 4, \ + "Number of first instructions in a loop to align") \ + \ + notproduct(intx, IndexSetWatch, 0, \ + "Trace all operations on this IndexSet (-1 means all, 0 none)") \ + \ + develop(intx, OptoNodeListSize, 4, \ + "Starting allocation size of Node_List data structures") \ + \ + develop(intx, OptoBlockListSize, 8, \ + "Starting allocation size of Block_List data structures") \ + \ + develop(intx, OptoPeepholeAt, -1, \ + "Apply peephole optimizations to this peephole rule") \ + \ + notproduct(bool, PrintIdeal, false, \ + "Print ideal graph before code generation") \ + \ + notproduct(bool, PrintOpto, false, \ + "Print compiler2 attempts") \ + \ + notproduct(bool, PrintOptoInlining, false, \ + "Print compiler2 inlining decisions") \ + \ + notproduct(bool, VerifyOpto, false, \ + "Apply more time consuming verification during compilation") \ + \ + notproduct(bool, VerifyOptoOopOffsets, false, \ + "Check types of base addresses in field references") \ + \ + develop(bool, IdealizedNumerics, false, \ + "Check performance difference allowing FP " \ + "associativity and commutativity...") \ + \ + develop(bool, OptoBreakpoint, false, \ + "insert breakpoint at method entry") \ + \ + notproduct(bool, OptoBreakpointOSR, false, \ + "insert breakpoint at osr method entry") \ + \ + notproduct(intx, BreakAtNode, 0, \ + "Break at construction of this Node (either _idx or _debug_idx)") \ + \ + notproduct(bool, OptoBreakpointC2R, false, \ + "insert breakpoint at runtime stub entry") \ + \ + notproduct(bool, OptoNoExecute, false, \ + "Attempt to parse and compile but do not execute generated code") \ + \ + notproduct(bool, PrintOptoStatistics, false, \ + "Print New compiler statistics") \ + \ + notproduct(bool, PrintOptoAssembly, false, \ + "Print New compiler assembly output") \ + \ + develop_pd(bool, OptoPeephole, \ + "Apply peephole optimizations after register allocation") \ + \ + develop(bool, OptoRemoveUseless, true, \ + "Remove useless nodes after parsing") \ + \ + notproduct(bool, PrintFrameConverterAssembly, false, \ + "Print New compiler assembly output for frame converters") \ + \ + notproduct(bool, PrintParseStatistics, false, \ + "Print nodes, transforms and new values made per bytecode parsed")\ + \ + notproduct(bool, PrintOptoPeephole, false, \ + "Print New compiler peephole replacements") \ + \ + develop(bool, PrintCFGBlockFreq, false, \ + "Print CFG block freqencies") \ + \ + develop(bool, TraceOptoParse, false, \ + "Trace bytecode parse and control-flow merge") \ + \ + product_pd(intx, LoopUnrollLimit, \ + "Unroll loop bodies with node count less than this") \ + \ + product(intx, LoopUnrollMin, 4, \ + "Minimum number of unroll loop bodies before checking progress" \ + "of rounds of unroll,optimize,..") \ + \ + develop(intx, UnrollLimitForProfileCheck, 1, \ + "Don't use profile_trip_cnt() to restrict unrolling until " \ + "unrolling would push the number of unrolled iterations above " \ + "UnrollLimitForProfileCheck. A higher value allows more " \ + "unrolling. Zero acts as a very large value." ) \ + \ + product(intx, MultiArrayExpandLimit, 6, \ + "Maximum number of individual allocations in an inline-expanded " \ + "multianewarray instruction") \ + \ + notproduct(bool, TraceProfileTripCount, false, \ + "Trace profile loop trip count information") \ + \ + develop(bool, OptoCoalesce, true, \ + "Use Conservative Copy Coalescing in the Register Allocator") \ + \ + develop(bool, UseUniqueSubclasses, true, \ + "Narrow an abstract reference to the unique concrete subclass") \ + \ + develop(bool, UseExactTypes, true, \ + "Use exact types to eliminate array store checks and v-calls") \ + \ + product(intx, TrackedInitializationLimit, 50, \ + "When initializing fields, track up to this many words") \ + \ + product(bool, ReduceFieldZeroing, true, \ + "When initializing fields, try to avoid needless zeroing") \ + \ + product(bool, ReduceInitialCardMarks, true, \ + "When initializing fields, try to avoid needless card marks") \ + \ + product(bool, ReduceBulkZeroing, true, \ + "When bulk-initializing, try to avoid needless zeroing") \ + \ + develop_pd(intx, RegisterCostAreaRatio, \ + "Spill selection in reg allocator: scale area by (X/64K) before " \ + "adding cost") \ + \ + develop_pd(bool, UseCISCSpill, \ + "Use ADLC supplied cisc instructions during allocation") \ + \ + notproduct(bool, VerifyGraphEdges , false, \ + "Verify Bi-directional Edges") \ + \ + notproduct(bool, VerifyDUIterators, true, \ + "Verify the safety of all iterations of Bi-directional Edges") \ + \ + notproduct(bool, VerifyHashTableKeys, true, \ + "Verify the immutability of keys in the VN hash tables") \ + \ + develop_pd(intx, FLOATPRESSURE, \ + "Number of float LRG's that constitute high register pressure") \ + \ + develop_pd(intx, INTPRESSURE, \ + "Number of integer LRG's that constitute high register pressure") \ + \ + notproduct(bool, TraceOptoPipelining, false, \ + "Trace pipelining information") \ + \ + notproduct(bool, TraceOptoOutput, false, \ + "Trace pipelining information") \ + \ + product_pd(bool, OptoScheduling, \ + "Instruction Scheduling after register allocation") \ + \ + product(bool, PartialPeelLoop, true, \ + "Partial peel (rotate) loops") \ + \ + product(intx, PartialPeelNewPhiDelta, 0, \ + "Additional phis that can be created by partial peeling") \ + \ + notproduct(bool, TracePartialPeeling, false, \ + "Trace partial peeling (loop rotation) information") \ + \ + product(bool, PartialPeelAtUnsignedTests, true, \ + "Partial peel at unsigned tests if no signed test exists") \ + \ + product(bool, ReassociateInvariants, true, \ + "Enable reassociation of expressions with loop invariants.") \ + \ + product(bool, LoopUnswitching, true, \ + "Enable loop unswitching (a form of invariant test hoisting)") \ + \ + notproduct(bool, TraceLoopUnswitching, false, \ + "Trace loop unswitching") \ + \ + product(bool, UseSuperWord, true, \ + "Transform scalar operations into superword operations") \ + \ + develop(bool, SuperWordRTDepCheck, false, \ + "Enable runtime dependency checks.") \ + \ + product(bool, TraceSuperWord, false, \ + "Trace superword transforms") \ + \ + product_pd(bool, OptoBundling, \ + "Generate nops to fill i-cache lines") \ + \ + product_pd(intx, ConditionalMoveLimit, \ + "Limit of ops to make speculative when using CMOVE") \ + \ + /* Set BranchOnRegister == false. See 4965987. */ \ + product(bool, BranchOnRegister, false, \ + "Use Sparc V9 branch-on-register opcodes") \ + \ + develop(bool, SparcV9RegsHiBitsZero, true, \ + "Assume Sparc V9 I&L registers on V8+ systems are zero-extended") \ + \ + develop(intx, PrintIdealGraphLevel, 0, \ + "Print ideal graph to XML file / network interface. " \ + "By default attempts to connect to the visualizer on a socket.") \ + \ + develop(intx, PrintIdealGraphPort, 4444, \ + "Ideal graph printer to network port") \ + \ + develop(ccstr, PrintIdealGraphAddress, "127.0.0.1", \ + "IP address to connect to visualizer") \ + \ + develop(ccstr, PrintIdealGraphFile, NULL, \ + "File to dump ideal graph to. If set overrides the " \ + "use of the network") \ + \ + product(bool, UseOldInlining, true, \ + "Enable the 1.3 inlining strategy") \ + \ + product(bool, UseBimorphicInlining, true, \ + "Profiling based inlining for two receivers") \ + \ + product(bool, UseOnlyInlinedBimorphic, true, \ + "Don't use BimorphicInlining if can't inline a second method") \ + \ + product(bool, InsertMemBarAfterArraycopy, true, \ + "Insert memory barrier after arraycopy call") \ + \ + /* controls for tier 1 compilations */ \ + \ + develop(bool, Tier1CountInvocations, true, \ + "Generate code, during tier 1, to update invocation counter") \ + \ + product(intx, Tier1Inline, false, \ + "enable inlining during tier 1") \ + \ + product(intx, Tier1MaxInlineSize, 8, \ + "maximum bytecode size of a method to be inlined, during tier 1") \ + \ + product(intx, Tier1FreqInlineSize, 35, \ + "max bytecode size of a frequent method to be inlined, tier 1") \ + \ + develop(intx, ImplicitNullCheckThreshold, 3, \ + "Don't do implicit null checks if NPE's in a method exceeds limit") \ + \ + /* controls for loop optimization */ \ + product(intx, Tier1LoopOptsCount, 0, \ + "Set level of loop optimization for tier 1 compiles") \ + \ + product(intx, LoopOptsCount, 43, \ + "Set level of loop optimization for tier 1 compiles") \ + \ + /* controls for heat-based inlining */ \ + \ + develop(intx, NodeCountInliningCutoff, 18000, \ + "If parser node generation exceeds limit stop inlining") \ + \ + develop(intx, NodeCountInliningStep, 1000, \ + "Target size of warm calls inlined between optimization passes") \ + \ + develop(bool, InlineWarmCalls, false, \ + "Use a heat-based priority queue to govern inlining") \ + \ + develop(intx, HotCallCountThreshold, 999999, \ + "large numbers of calls (per method invocation) force hotness") \ + \ + develop(intx, HotCallProfitThreshold, 999999, \ + "highly profitable inlining opportunities force hotness") \ + \ + develop(intx, HotCallTrivialWork, -1, \ + "trivial execution time (no larger than this) forces hotness") \ + \ + develop(intx, HotCallTrivialSize, -1, \ + "trivial methods (no larger than this) force calls to be hot") \ + \ + develop(intx, WarmCallMinCount, -1, \ + "number of calls (per method invocation) to enable inlining") \ + \ + develop(intx, WarmCallMinProfit, -1, \ + "number of calls (per method invocation) to enable inlining") \ + \ + develop(intx, WarmCallMaxWork, 999999, \ + "execution time of the largest inlinable method") \ + \ + develop(intx, WarmCallMaxSize, 999999, \ + "size of the largest inlinable method") \ + \ + product(intx, MaxNodeLimit, 65000, \ + "Maximum number of nodes") \ + \ + product(intx, NodeLimitFudgeFactor, 1000, \ + "Fudge Factor for certain optimizations") \ + \ + product(bool, UseJumpTables, true, \ + "Use JumpTables instead of a binary search tree for switches") \ + \ + product(bool, UseDivMod, true, \ + "Use combined DivMod instruction if available") \ + \ + product(intx, MinJumpTableSize, 18, \ + "Minimum number of targets in a generated jump table") \ + \ + product(intx, MaxJumpTableSize, 65000, \ + "Maximum number of targets in a generated jump table") \ + \ + product(intx, MaxJumpTableSparseness, 5, \ + "Maximum sparseness for jumptables") \ + \ + product(bool, EliminateLocks, true, \ + "Coarsen locks when possible") \ + \ + notproduct(bool, PrintLockStatistics, false, \ + "Print precise statistics on the dynamic lock usage") \ + \ + diagnostic(bool, PrintPreciseBiasedLockingStatistics, false, \ + "Print per-lock-site statistics of biased locking in JVM") \ + \ + notproduct(bool, PrintEliminateLocks, false, \ + "Print out when locks are eliminated") \ + \ + product(bool, DoEscapeAnalysis, false, \ + "Perform escape analysis") \ + \ + notproduct(bool, PrintEscapeAnalysis, false, \ + "Print the results of escape analysis") \ + \ + product(bool, EliminateAllocations, true, \ + "Use escape analysis to eliminate allocations") \ + \ + product(intx, MaxLabelRootDepth, 1100, \ + "Maximum times call Label_Root to prevent stack overflow") \ + +C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_NOTPRODUCT_FLAG) diff --git a/hotspot/src/share/vm/opto/c2compiler.cpp b/hotspot/src/share/vm/opto/c2compiler.cpp new file mode 100644 index 00000000000..6543e692bde --- /dev/null +++ b/hotspot/src/share/vm/opto/c2compiler.cpp @@ -0,0 +1,129 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_c2compiler.cpp.incl" + + +volatile int C2Compiler::_runtimes = uninitialized; + +// register information defined by ADLC +extern const char register_save_policy[]; +extern const int register_save_type[]; + +const char* C2Compiler::retry_no_subsuming_loads() { + return "retry without subsuming loads"; +} +void C2Compiler::initialize_runtime() { + + // Check assumptions used while running ADLC + Compile::adlc_verification(); + assert(REG_COUNT <= ConcreteRegisterImpl::number_of_registers, "incompatible register counts"); + + for (int i = 0; i < ConcreteRegisterImpl::number_of_registers ; i++ ) { + OptoReg::vm2opto[i] = OptoReg::Bad; + } + + for( OptoReg::Name i=OptoReg::Name(0); iis_valid()) { + OptoReg::vm2opto[r->value()] = i; + } + } + + // Check that runtime and architecture description agree on callee-saved-floats + bool callee_saved_floats = false; + for( OptoReg::Name i=OptoReg::Name(0); ienv()); + +} + + +void C2Compiler::initialize() { + + // This method can only be called once per C2Compiler object + // The first compiler thread that gets here will initialize the + // small amount of global state (and runtime stubs) that c2 needs. + + // There is a race possible once at startup and then we're fine + + // Note that this is being called from a compiler thread not the + // main startup thread. + + if (_runtimes != initialized) { + initialize_runtimes( initialize_runtime, &_runtimes); + } + + // Mark this compiler object as ready to roll + mark_initialized(); +} + +void C2Compiler::compile_method(ciEnv* env, + ciMethod* target, + int entry_bci) { + if (!is_initialized()) { + initialize(); + } + bool subsume_loads = true; + while (!env->failing()) { + // Attempt to compile while subsuming loads into machine instructions. + Compile C(env, this, target, entry_bci, subsume_loads); + + // Check result and retry if appropriate. + if (C.failure_reason() != NULL) { + if (C.failure_reason_is(retry_no_subsuming_loads())) { + assert(subsume_loads, "must make progress"); + subsume_loads = false; + continue; // retry + } + // Pass any other failure reason up to the ciEnv. + // Note that serious, irreversible failures are already logged + // on the ciEnv via env->record_method_not_compilable(). + env->record_failure(C.failure_reason()); + } + + // No retry; just break the loop. + break; + } +} + + +void C2Compiler::print_timers() { + // do nothing +} diff --git a/hotspot/src/share/vm/opto/c2compiler.hpp b/hotspot/src/share/vm/opto/c2compiler.hpp new file mode 100644 index 00000000000..dc5851082a4 --- /dev/null +++ b/hotspot/src/share/vm/opto/c2compiler.hpp @@ -0,0 +1,56 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class C2Compiler : public AbstractCompiler { +private: + + static void initialize_runtime(); + +public: + // Name + const char *name() { return "C2"; } + + static volatile int _runtimes; + +#ifdef TIERED + virtual bool is_c2() { return true; }; +#endif // TIERED + + // Customization + bool needs_adapters () { return true; } + bool needs_stubs () { return true; } + + void initialize(); + + // Compilation entry point for methods + void compile_method(ciEnv* env, + ciMethod* target, + int entry_bci); + + // sentinel value used to trigger backtracking in compile_method(). + static const char* retry_no_subsuming_loads(); + + // Print compilation timers and statistics + void print_timers(); +}; diff --git a/hotspot/src/share/vm/opto/callGenerator.cpp b/hotspot/src/share/vm/opto/callGenerator.cpp new file mode 100644 index 00000000000..3131cf6b333 --- /dev/null +++ b/hotspot/src/share/vm/opto/callGenerator.cpp @@ -0,0 +1,744 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_callGenerator.cpp.incl" + +CallGenerator::CallGenerator(ciMethod* method) { + _method = method; +} + +// Utility function. +const TypeFunc* CallGenerator::tf() const { + return TypeFunc::make(method()); +} + +//-----------------------------ParseGenerator--------------------------------- +// Internal class which handles all direct bytecode traversal. +class ParseGenerator : public InlineCallGenerator { +private: + bool _is_osr; + float _expected_uses; + +public: + ParseGenerator(ciMethod* method, float expected_uses, bool is_osr = false) + : InlineCallGenerator(method) + { + _is_osr = is_osr; + _expected_uses = expected_uses; + assert(can_parse(method, is_osr), "parse must be possible"); + } + + // Can we build either an OSR or a regular parser for this method? + static bool can_parse(ciMethod* method, int is_osr = false); + + virtual bool is_parse() const { return true; } + virtual JVMState* generate(JVMState* jvms); + int is_osr() { return _is_osr; } + +}; + +JVMState* ParseGenerator::generate(JVMState* jvms) { + Compile* C = Compile::current(); + + if (is_osr()) { + // The JVMS for a OSR has a single argument (see its TypeFunc). + assert(jvms->depth() == 1, "no inline OSR"); + } + + if (C->failing()) { + return NULL; // bailing out of the compile; do not try to parse + } + + Parse parser(jvms, method(), _expected_uses); + // Grab signature for matching/allocation +#ifdef ASSERT + if (parser.tf() != (parser.depth() == 1 ? C->tf() : tf())) { + MutexLockerEx ml(Compile_lock, Mutex::_no_safepoint_check_flag); + assert(C->env()->system_dictionary_modification_counter_changed(), + "Must invalidate if TypeFuncs differ"); + } +#endif + + GraphKit& exits = parser.exits(); + + if (C->failing()) { + while (exits.pop_exception_state() != NULL) ; + return NULL; + } + + assert(exits.jvms()->same_calls_as(jvms), "sanity"); + + // Simply return the exit state of the parser, + // augmented by any exceptional states. + return exits.transfer_exceptions_into_jvms(); +} + +//---------------------------DirectCallGenerator------------------------------ +// Internal class which handles all out-of-line calls w/o receiver type checks. +class DirectCallGenerator : public CallGenerator { +public: + DirectCallGenerator(ciMethod* method) + : CallGenerator(method) + { + } + virtual JVMState* generate(JVMState* jvms); +}; + +JVMState* DirectCallGenerator::generate(JVMState* jvms) { + GraphKit kit(jvms); + bool is_static = method()->is_static(); + address target = is_static ? SharedRuntime::get_resolve_static_call_stub() + : SharedRuntime::get_resolve_opt_virtual_call_stub(); + + if (kit.C->log() != NULL) { + kit.C->log()->elem("direct_call bci='%d'", jvms->bci()); + } + + CallStaticJavaNode *call = new (kit.C, tf()->domain()->cnt()) CallStaticJavaNode(tf(), target, method(), kit.bci()); + if (!is_static) { + // Make an explicit receiver null_check as part of this call. + // Since we share a map with the caller, his JVMS gets adjusted. + kit.null_check_receiver(method()); + if (kit.stopped()) { + // And dump it back to the caller, decorated with any exceptions: + return kit.transfer_exceptions_into_jvms(); + } + // Mark the call node as virtual, sort of: + call->set_optimized_virtual(true); + } + kit.set_arguments_for_java_call(call); + kit.set_edges_for_java_call(call); + Node* ret = kit.set_results_for_java_call(call); + kit.push_node(method()->return_type()->basic_type(), ret); + return kit.transfer_exceptions_into_jvms(); +} + +class VirtualCallGenerator : public CallGenerator { +private: + int _vtable_index; +public: + VirtualCallGenerator(ciMethod* method, int vtable_index) + : CallGenerator(method), _vtable_index(vtable_index) + { + assert(vtable_index == methodOopDesc::invalid_vtable_index || + vtable_index >= 0, "either invalid or usable"); + } + virtual bool is_virtual() const { return true; } + virtual JVMState* generate(JVMState* jvms); +}; + +//--------------------------VirtualCallGenerator------------------------------ +// Internal class which handles all out-of-line calls checking receiver type. +JVMState* VirtualCallGenerator::generate(JVMState* jvms) { + GraphKit kit(jvms); + Node* receiver = kit.argument(0); + + if (kit.C->log() != NULL) { + kit.C->log()->elem("virtual_call bci='%d'", jvms->bci()); + } + + // If the receiver is a constant null, do not torture the system + // by attempting to call through it. The compile will proceed + // correctly, but may bail out in final_graph_reshaping, because + // the call instruction will have a seemingly deficient out-count. + // (The bailout says something misleading about an "infinite loop".) + if (kit.gvn().type(receiver)->higher_equal(TypePtr::NULL_PTR)) { + kit.inc_sp(method()->arg_size()); // restore arguments + kit.uncommon_trap(Deoptimization::Reason_null_check, + Deoptimization::Action_none, + NULL, "null receiver"); + return kit.transfer_exceptions_into_jvms(); + } + + // Ideally we would unconditionally do a null check here and let it + // be converted to an implicit check based on profile information. + // However currently the conversion to implicit null checks in + // Block::implicit_null_check() only looks for loads and stores, not calls. + ciMethod *caller = kit.method(); + ciMethodData *caller_md = (caller == NULL) ? NULL : caller->method_data(); + if (!UseInlineCaches || !ImplicitNullChecks || + ((ImplicitNullCheckThreshold > 0) && caller_md && + (caller_md->trap_count(Deoptimization::Reason_null_check) + >= (uint)ImplicitNullCheckThreshold))) { + // Make an explicit receiver null_check as part of this call. + // Since we share a map with the caller, his JVMS gets adjusted. + receiver = kit.null_check_receiver(method()); + if (kit.stopped()) { + // And dump it back to the caller, decorated with any exceptions: + return kit.transfer_exceptions_into_jvms(); + } + } + + assert(!method()->is_static(), "virtual call must not be to static"); + assert(!method()->is_final(), "virtual call should not be to final"); + assert(!method()->is_private(), "virtual call should not be to private"); + assert(_vtable_index == methodOopDesc::invalid_vtable_index || !UseInlineCaches, + "no vtable calls if +UseInlineCaches "); + address target = SharedRuntime::get_resolve_virtual_call_stub(); + // Normal inline cache used for call + CallDynamicJavaNode *call = new (kit.C, tf()->domain()->cnt()) CallDynamicJavaNode(tf(), target, method(), _vtable_index, kit.bci()); + kit.set_arguments_for_java_call(call); + kit.set_edges_for_java_call(call); + Node* ret = kit.set_results_for_java_call(call); + kit.push_node(method()->return_type()->basic_type(), ret); + + // Represent the effect of an implicit receiver null_check + // as part of this call. Since we share a map with the caller, + // his JVMS gets adjusted. + kit.cast_not_null(receiver); + return kit.transfer_exceptions_into_jvms(); +} + +bool ParseGenerator::can_parse(ciMethod* m, int entry_bci) { + // Certain methods cannot be parsed at all: + if (!m->can_be_compiled()) return false; + if (!m->has_balanced_monitors()) return false; + if (m->get_flow_analysis()->failing()) return false; + + // (Methods may bail out for other reasons, after the parser is run. + // We try to avoid this, but if forced, we must return (Node*)NULL. + // The user of the CallGenerator must check for this condition.) + return true; +} + +CallGenerator* CallGenerator::for_inline(ciMethod* m, float expected_uses) { + if (!ParseGenerator::can_parse(m)) return NULL; + return new ParseGenerator(m, expected_uses); +} + +// As a special case, the JVMS passed to this CallGenerator is +// for the method execution already in progress, not just the JVMS +// of the caller. Thus, this CallGenerator cannot be mixed with others! +CallGenerator* CallGenerator::for_osr(ciMethod* m, int osr_bci) { + if (!ParseGenerator::can_parse(m, true)) return NULL; + float past_uses = m->interpreter_invocation_count(); + float expected_uses = past_uses; + return new ParseGenerator(m, expected_uses, true); +} + +CallGenerator* CallGenerator::for_direct_call(ciMethod* m) { + assert(!m->is_abstract(), "for_direct_call mismatch"); + return new DirectCallGenerator(m); +} + +CallGenerator* CallGenerator::for_virtual_call(ciMethod* m, int vtable_index) { + assert(!m->is_static(), "for_virtual_call mismatch"); + return new VirtualCallGenerator(m, vtable_index); +} + + +//---------------------------WarmCallGenerator-------------------------------- +// Internal class which handles initial deferral of inlining decisions. +class WarmCallGenerator : public CallGenerator { + WarmCallInfo* _call_info; + CallGenerator* _if_cold; + CallGenerator* _if_hot; + bool _is_virtual; // caches virtuality of if_cold + bool _is_inline; // caches inline-ness of if_hot + +public: + WarmCallGenerator(WarmCallInfo* ci, + CallGenerator* if_cold, + CallGenerator* if_hot) + : CallGenerator(if_cold->method()) + { + assert(method() == if_hot->method(), "consistent choices"); + _call_info = ci; + _if_cold = if_cold; + _if_hot = if_hot; + _is_virtual = if_cold->is_virtual(); + _is_inline = if_hot->is_inline(); + } + + virtual bool is_inline() const { return _is_inline; } + virtual bool is_virtual() const { return _is_virtual; } + virtual bool is_deferred() const { return true; } + + virtual JVMState* generate(JVMState* jvms); +}; + + +CallGenerator* CallGenerator::for_warm_call(WarmCallInfo* ci, + CallGenerator* if_cold, + CallGenerator* if_hot) { + return new WarmCallGenerator(ci, if_cold, if_hot); +} + +JVMState* WarmCallGenerator::generate(JVMState* jvms) { + Compile* C = Compile::current(); + if (C->log() != NULL) { + C->log()->elem("warm_call bci='%d'", jvms->bci()); + } + jvms = _if_cold->generate(jvms); + if (jvms != NULL) { + Node* m = jvms->map()->control(); + if (m->is_CatchProj()) m = m->in(0); else m = C->top(); + if (m->is_Catch()) m = m->in(0); else m = C->top(); + if (m->is_Proj()) m = m->in(0); else m = C->top(); + if (m->is_CallJava()) { + _call_info->set_call(m->as_Call()); + _call_info->set_hot_cg(_if_hot); +#ifndef PRODUCT + if (PrintOpto || PrintOptoInlining) { + tty->print_cr("Queueing for warm inlining at bci %d:", jvms->bci()); + tty->print("WCI: "); + _call_info->print(); + } +#endif + _call_info->set_heat(_call_info->compute_heat()); + C->set_warm_calls(_call_info->insert_into(C->warm_calls())); + } + } + return jvms; +} + +void WarmCallInfo::make_hot() { + Compile* C = Compile::current(); + // Replace the callnode with something better. + CallJavaNode* call = this->call()->as_CallJava(); + ciMethod* method = call->method(); + int nargs = method->arg_size(); + JVMState* jvms = call->jvms()->clone_shallow(C); + uint size = TypeFunc::Parms + MAX2(2, nargs); + SafePointNode* map = new (C, size) SafePointNode(size, jvms); + for (uint i1 = 0; i1 < (uint)(TypeFunc::Parms + nargs); i1++) { + map->init_req(i1, call->in(i1)); + } + jvms->set_map(map); + jvms->set_offsets(map->req()); + jvms->set_locoff(TypeFunc::Parms); + jvms->set_stkoff(TypeFunc::Parms); + GraphKit kit(jvms); + + JVMState* new_jvms = _hot_cg->generate(kit.jvms()); + if (new_jvms == NULL) return; // no change + if (C->failing()) return; + + kit.set_jvms(new_jvms); + Node* res = C->top(); + int res_size = method->return_type()->size(); + if (res_size != 0) { + kit.inc_sp(-res_size); + res = kit.argument(0); + } + GraphKit ekit(kit.combine_and_pop_all_exception_states()->jvms()); + + // Replace the call: + for (DUIterator i = call->outs(); call->has_out(i); i++) { + Node* n = call->out(i); + Node* nn = NULL; // replacement + if (n->is_Proj()) { + ProjNode* nproj = n->as_Proj(); + assert(nproj->_con < (uint)(TypeFunc::Parms + (res_size ? 1 : 0)), "sane proj"); + if (nproj->_con == TypeFunc::Parms) { + nn = res; + } else { + nn = kit.map()->in(nproj->_con); + } + if (nproj->_con == TypeFunc::I_O) { + for (DUIterator j = nproj->outs(); nproj->has_out(j); j++) { + Node* e = nproj->out(j); + if (e->Opcode() == Op_CreateEx) { + e->replace_by(ekit.argument(0)); + } else if (e->Opcode() == Op_Catch) { + for (DUIterator k = e->outs(); e->has_out(k); k++) { + CatchProjNode* p = e->out(j)->as_CatchProj(); + if (p->is_handler_proj()) { + p->replace_by(ekit.control()); + } else { + p->replace_by(kit.control()); + } + } + } + } + } + } + NOT_PRODUCT(if (!nn) n->dump(2)); + assert(nn != NULL, "don't know what to do with this user"); + n->replace_by(nn); + } +} + +void WarmCallInfo::make_cold() { + // No action: Just dequeue. +} + + +//------------------------PredictedCallGenerator------------------------------ +// Internal class which handles all out-of-line calls checking receiver type. +class PredictedCallGenerator : public CallGenerator { + ciKlass* _predicted_receiver; + CallGenerator* _if_missed; + CallGenerator* _if_hit; + float _hit_prob; + +public: + PredictedCallGenerator(ciKlass* predicted_receiver, + CallGenerator* if_missed, + CallGenerator* if_hit, float hit_prob) + : CallGenerator(if_missed->method()) + { + // The call profile data may predict the hit_prob as extreme as 0 or 1. + // Remove the extremes values from the range. + if (hit_prob > PROB_MAX) hit_prob = PROB_MAX; + if (hit_prob < PROB_MIN) hit_prob = PROB_MIN; + + _predicted_receiver = predicted_receiver; + _if_missed = if_missed; + _if_hit = if_hit; + _hit_prob = hit_prob; + } + + virtual bool is_virtual() const { return true; } + virtual bool is_inline() const { return _if_hit->is_inline(); } + virtual bool is_deferred() const { return _if_hit->is_deferred(); } + + virtual JVMState* generate(JVMState* jvms); +}; + + +CallGenerator* CallGenerator::for_predicted_call(ciKlass* predicted_receiver, + CallGenerator* if_missed, + CallGenerator* if_hit, + float hit_prob) { + return new PredictedCallGenerator(predicted_receiver, if_missed, if_hit, hit_prob); +} + + +JVMState* PredictedCallGenerator::generate(JVMState* jvms) { + GraphKit kit(jvms); + PhaseGVN& gvn = kit.gvn(); + // We need an explicit receiver null_check before checking its type. + // We share a map with the caller, so his JVMS gets adjusted. + Node* receiver = kit.argument(0); + + CompileLog* log = kit.C->log(); + if (log != NULL) { + log->elem("predicted_call bci='%d' klass='%d'", + jvms->bci(), log->identify(_predicted_receiver)); + } + + receiver = kit.null_check_receiver(method()); + if (kit.stopped()) { + return kit.transfer_exceptions_into_jvms(); + } + + Node* exact_receiver = receiver; // will get updated in place... + Node* slow_ctl = kit.type_check_receiver(receiver, + _predicted_receiver, _hit_prob, + &exact_receiver); + + SafePointNode* slow_map = NULL; + JVMState* slow_jvms; + { PreserveJVMState pjvms(&kit); + kit.set_control(slow_ctl); + if (!kit.stopped()) { + slow_jvms = _if_missed->generate(kit.sync_jvms()); + assert(slow_jvms != NULL, "miss path must not fail to generate"); + kit.add_exception_states_from(slow_jvms); + kit.set_map(slow_jvms->map()); + if (!kit.stopped()) + slow_map = kit.stop(); + } + } + + // fall through if the instance exactly matches the desired type + kit.replace_in_map(receiver, exact_receiver); + + // Make the hot call: + JVMState* new_jvms = _if_hit->generate(kit.sync_jvms()); + if (new_jvms == NULL) { + // Inline failed, so make a direct call. + assert(_if_hit->is_inline(), "must have been a failed inline"); + CallGenerator* cg = CallGenerator::for_direct_call(_if_hit->method()); + new_jvms = cg->generate(kit.sync_jvms()); + } + kit.add_exception_states_from(new_jvms); + kit.set_jvms(new_jvms); + + // Need to merge slow and fast? + if (slow_map == NULL) { + // The fast path is the only path remaining. + return kit.transfer_exceptions_into_jvms(); + } + + if (kit.stopped()) { + // Inlined method threw an exception, so it's just the slow path after all. + kit.set_jvms(slow_jvms); + return kit.transfer_exceptions_into_jvms(); + } + + // Finish the diamond. + kit.C->set_has_split_ifs(true); // Has chance for split-if optimization + RegionNode* region = new (kit.C, 3) RegionNode(3); + region->init_req(1, kit.control()); + region->init_req(2, slow_map->control()); + kit.set_control(gvn.transform(region)); + Node* iophi = PhiNode::make(region, kit.i_o(), Type::ABIO); + iophi->set_req(2, slow_map->i_o()); + kit.set_i_o(gvn.transform(iophi)); + kit.merge_memory(slow_map->merged_memory(), region, 2); + uint tos = kit.jvms()->stkoff() + kit.sp(); + uint limit = slow_map->req(); + for (uint i = TypeFunc::Parms; i < limit; i++) { + // Skip unused stack slots; fast forward to monoff(); + if (i == tos) { + i = kit.jvms()->monoff(); + if( i >= limit ) break; + } + Node* m = kit.map()->in(i); + Node* n = slow_map->in(i); + if (m != n) { + const Type* t = gvn.type(m)->meet(gvn.type(n)); + Node* phi = PhiNode::make(region, m, t); + phi->set_req(2, n); + kit.map()->set_req(i, gvn.transform(phi)); + } + } + return kit.transfer_exceptions_into_jvms(); +} + + +//-------------------------UncommonTrapCallGenerator----------------------------- +// Internal class which handles all out-of-line calls checking receiver type. +class UncommonTrapCallGenerator : public CallGenerator { + Deoptimization::DeoptReason _reason; + Deoptimization::DeoptAction _action; + +public: + UncommonTrapCallGenerator(ciMethod* m, + Deoptimization::DeoptReason reason, + Deoptimization::DeoptAction action) + : CallGenerator(m) + { + _reason = reason; + _action = action; + } + + virtual bool is_virtual() const { ShouldNotReachHere(); return false; } + virtual bool is_trap() const { return true; } + + virtual JVMState* generate(JVMState* jvms); +}; + + +CallGenerator* +CallGenerator::for_uncommon_trap(ciMethod* m, + Deoptimization::DeoptReason reason, + Deoptimization::DeoptAction action) { + return new UncommonTrapCallGenerator(m, reason, action); +} + + +JVMState* UncommonTrapCallGenerator::generate(JVMState* jvms) { + GraphKit kit(jvms); + // Take the trap with arguments pushed on the stack. (Cf. null_check_receiver). + int nargs = method()->arg_size(); + kit.inc_sp(nargs); + assert(nargs <= kit.sp() && kit.sp() <= jvms->stk_size(), "sane sp w/ args pushed"); + if (_reason == Deoptimization::Reason_class_check && + _action == Deoptimization::Action_maybe_recompile) { + // Temp fix for 6529811 + // Don't allow uncommon_trap to override our decision to recompile in the event + // of a class cast failure for a monomorphic call as it will never let us convert + // the call to either bi-morphic or megamorphic and can lead to unc-trap loops + bool keep_exact_action = true; + kit.uncommon_trap(_reason, _action, NULL, "monomorphic vcall checkcast", false, keep_exact_action); + } else { + kit.uncommon_trap(_reason, _action); + } + return kit.transfer_exceptions_into_jvms(); +} + +// (Note: Moved hook_up_call to GraphKit::set_edges_for_java_call.) + +// (Node: Merged hook_up_exits into ParseGenerator::generate.) + +#define NODES_OVERHEAD_PER_METHOD (30.0) +#define NODES_PER_BYTECODE (9.5) + +void WarmCallInfo::init(JVMState* call_site, ciMethod* call_method, ciCallProfile& profile, float prof_factor) { + int call_count = profile.count(); + int code_size = call_method->code_size(); + + // Expected execution count is based on the historical count: + _count = call_count < 0 ? 1 : call_site->method()->scale_count(call_count, prof_factor); + + // Expected profit from inlining, in units of simple call-overheads. + _profit = 1.0; + + // Expected work performed by the call in units of call-overheads. + // %%% need an empirical curve fit for "work" (time in call) + float bytecodes_per_call = 3; + _work = 1.0 + code_size / bytecodes_per_call; + + // Expected size of compilation graph: + // -XX:+PrintParseStatistics once reported: + // Methods seen: 9184 Methods parsed: 9184 Nodes created: 1582391 + // Histogram of 144298 parsed bytecodes: + // %%% Need an better predictor for graph size. + _size = NODES_OVERHEAD_PER_METHOD + (NODES_PER_BYTECODE * code_size); +} + +// is_cold: Return true if the node should never be inlined. +// This is true if any of the key metrics are extreme. +bool WarmCallInfo::is_cold() const { + if (count() < WarmCallMinCount) return true; + if (profit() < WarmCallMinProfit) return true; + if (work() > WarmCallMaxWork) return true; + if (size() > WarmCallMaxSize) return true; + return false; +} + +// is_hot: Return true if the node should be inlined immediately. +// This is true if any of the key metrics are extreme. +bool WarmCallInfo::is_hot() const { + assert(!is_cold(), "eliminate is_cold cases before testing is_hot"); + if (count() >= HotCallCountThreshold) return true; + if (profit() >= HotCallProfitThreshold) return true; + if (work() <= HotCallTrivialWork) return true; + if (size() <= HotCallTrivialSize) return true; + return false; +} + +// compute_heat: +float WarmCallInfo::compute_heat() const { + assert(!is_cold(), "compute heat only on warm nodes"); + assert(!is_hot(), "compute heat only on warm nodes"); + int min_size = MAX2(0, (int)HotCallTrivialSize); + int max_size = MIN2(500, (int)WarmCallMaxSize); + float method_size = (size() - min_size) / MAX2(1, max_size - min_size); + float size_factor; + if (method_size < 0.05) size_factor = 4; // 2 sigmas better than avg. + else if (method_size < 0.15) size_factor = 2; // 1 sigma better than avg. + else if (method_size < 0.5) size_factor = 1; // better than avg. + else size_factor = 0.5; // worse than avg. + return (count() * profit() * size_factor); +} + +bool WarmCallInfo::warmer_than(WarmCallInfo* that) { + assert(this != that, "compare only different WCIs"); + assert(this->heat() != 0 && that->heat() != 0, "call compute_heat 1st"); + if (this->heat() > that->heat()) return true; + if (this->heat() < that->heat()) return false; + assert(this->heat() == that->heat(), "no NaN heat allowed"); + // Equal heat. Break the tie some other way. + if (!this->call() || !that->call()) return (address)this > (address)that; + return this->call()->_idx > that->call()->_idx; +} + +//#define UNINIT_NEXT ((WarmCallInfo*)badAddress) +#define UNINIT_NEXT ((WarmCallInfo*)NULL) + +WarmCallInfo* WarmCallInfo::insert_into(WarmCallInfo* head) { + assert(next() == UNINIT_NEXT, "not yet on any list"); + WarmCallInfo* prev_p = NULL; + WarmCallInfo* next_p = head; + while (next_p != NULL && next_p->warmer_than(this)) { + prev_p = next_p; + next_p = prev_p->next(); + } + // Install this between prev_p and next_p. + this->set_next(next_p); + if (prev_p == NULL) + head = this; + else + prev_p->set_next(this); + return head; +} + +WarmCallInfo* WarmCallInfo::remove_from(WarmCallInfo* head) { + WarmCallInfo* prev_p = NULL; + WarmCallInfo* next_p = head; + while (next_p != this) { + assert(next_p != NULL, "this must be in the list somewhere"); + prev_p = next_p; + next_p = prev_p->next(); + } + next_p = this->next(); + debug_only(this->set_next(UNINIT_NEXT)); + // Remove this from between prev_p and next_p. + if (prev_p == NULL) + head = next_p; + else + prev_p->set_next(next_p); + return head; +} + +WarmCallInfo* WarmCallInfo::_always_hot = NULL; +WarmCallInfo* WarmCallInfo::_always_cold = NULL; + +WarmCallInfo* WarmCallInfo::always_hot() { + if (_always_hot == NULL) { + static double bits[sizeof(WarmCallInfo) / sizeof(double) + 1] = {0}; + WarmCallInfo* ci = (WarmCallInfo*) bits; + ci->_profit = ci->_count = MAX_VALUE(); + ci->_work = ci->_size = MIN_VALUE(); + _always_hot = ci; + } + assert(_always_hot->is_hot(), "must always be hot"); + return _always_hot; +} + +WarmCallInfo* WarmCallInfo::always_cold() { + if (_always_cold == NULL) { + static double bits[sizeof(WarmCallInfo) / sizeof(double) + 1] = {0}; + WarmCallInfo* ci = (WarmCallInfo*) bits; + ci->_profit = ci->_count = MIN_VALUE(); + ci->_work = ci->_size = MAX_VALUE(); + _always_cold = ci; + } + assert(_always_cold->is_cold(), "must always be cold"); + return _always_cold; +} + + +#ifndef PRODUCT + +void WarmCallInfo::print() const { + tty->print("%s : C=%6.1f P=%6.1f W=%6.1f S=%6.1f H=%6.1f -> %p", + is_cold() ? "cold" : is_hot() ? "hot " : "warm", + count(), profit(), work(), size(), compute_heat(), next()); + tty->cr(); + if (call() != NULL) call()->dump(); +} + +void print_wci(WarmCallInfo* ci) { + ci->print(); +} + +void WarmCallInfo::print_all() const { + for (const WarmCallInfo* p = this; p != NULL; p = p->next()) + p->print(); +} + +int WarmCallInfo::count_all() const { + int cnt = 0; + for (const WarmCallInfo* p = this; p != NULL; p = p->next()) + cnt++; + return cnt; +} + +#endif //PRODUCT diff --git a/hotspot/src/share/vm/opto/callGenerator.hpp b/hotspot/src/share/vm/opto/callGenerator.hpp new file mode 100644 index 00000000000..bbd47ca4aab --- /dev/null +++ b/hotspot/src/share/vm/opto/callGenerator.hpp @@ -0,0 +1,266 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//---------------------------CallGenerator------------------------------------- +// The subclasses of this class handle generation of ideal nodes for +// call sites and method entry points. + +class CallGenerator : public ResourceObj { + public: + enum { + xxxunusedxxx + }; + + private: + ciMethod* _method; // The method being called. + + protected: + CallGenerator(ciMethod* method); + + public: + // Accessors + ciMethod* method() const { return _method; } + + // is_inline: At least some code implementing the method is copied here. + virtual bool is_inline() const { return false; } + // is_intrinsic: There's a method-specific way of generating the inline code. + virtual bool is_intrinsic() const { return false; } + // is_parse: Bytecodes implementing the specific method are copied here. + virtual bool is_parse() const { return false; } + // is_virtual: The call uses the receiver type to select or check the method. + virtual bool is_virtual() const { return false; } + // is_deferred: The decision whether to inline or not is deferred. + virtual bool is_deferred() const { return false; } + // is_predicted: Uses an explicit check against a predicted type. + virtual bool is_predicted() const { return false; } + // is_trap: Does not return to the caller. (E.g., uncommon trap.) + virtual bool is_trap() const { return false; } + + // Note: It is possible for a CG to be both inline and virtual. + // (The hashCode intrinsic does a vtable check and an inlined fast path.) + + // Utilities: + const TypeFunc* tf() const; + + // The given jvms has state and arguments for a call to my method. + // Edges after jvms->argoff() carry all (pre-popped) argument values. + // + // Update the map with state and return values (if any) and return it. + // The return values (0, 1, or 2) must be pushed on the map's stack, + // and the sp of the jvms incremented accordingly. + // + // The jvms is returned on success. Alternatively, a copy of the + // given jvms, suitably updated, may be returned, in which case the + // caller should discard the original jvms. + // + // The non-Parm edges of the returned map will contain updated global state, + // and one or two edges before jvms->sp() will carry any return values. + // Other map edges may contain locals or monitors, and should not + // be changed in meaning. + // + // If the call traps, the returned map must have a control edge of top. + // If the call can throw, the returned map must report has_exceptions(). + // + // If the result is NULL, it means that this CallGenerator was unable + // to handle the given call, and another CallGenerator should be consulted. + virtual JVMState* generate(JVMState* jvms) = 0; + + // How to generate a call site that is inlined: + static CallGenerator* for_inline(ciMethod* m, float expected_uses = -1); + // How to generate code for an on-stack replacement handler. + static CallGenerator* for_osr(ciMethod* m, int osr_bci); + + // How to generate vanilla out-of-line call sites: + static CallGenerator* for_direct_call(ciMethod* m); // static, special + static CallGenerator* for_virtual_call(ciMethod* m, int vtable_index); // virtual, interface + + // How to make a call but defer the decision whether to inline or not. + static CallGenerator* for_warm_call(WarmCallInfo* ci, + CallGenerator* if_cold, + CallGenerator* if_hot); + + // How to make a call that optimistically assumes a receiver type: + static CallGenerator* for_predicted_call(ciKlass* predicted_receiver, + CallGenerator* if_missed, + CallGenerator* if_hit, + float hit_prob); + + // How to make a call that gives up and goes back to the interpreter: + static CallGenerator* for_uncommon_trap(ciMethod* m, + Deoptimization::DeoptReason reason, + Deoptimization::DeoptAction action); + + // Registry for intrinsics: + static CallGenerator* for_intrinsic(ciMethod* m); + static void register_intrinsic(ciMethod* m, CallGenerator* cg); +}; + +class InlineCallGenerator : public CallGenerator { + virtual bool is_inline() const { return true; } + + protected: + InlineCallGenerator(ciMethod* method) : CallGenerator(method) { } +}; + + +//---------------------------WarmCallInfo-------------------------------------- +// A struct to collect information about a given call site. +// Helps sort call sites into "hot", "medium", and "cold". +// Participates in the queueing of "medium" call sites for possible inlining. +class WarmCallInfo : public ResourceObj { + private: + + CallNode* _call; // The CallNode which may be inlined. + CallGenerator* _hot_cg;// CG for expanding the call node + + // These are the metrics we use to evaluate call sites: + + float _count; // How often do we expect to reach this site? + float _profit; // How much time do we expect to save by inlining? + float _work; // How long do we expect the average call to take? + float _size; // How big do we expect the inlined code to be? + + float _heat; // Combined score inducing total order on call sites. + WarmCallInfo* _next; // Next cooler call info in pending queue. + + // Count is the number of times this call site is expected to be executed. + // Large count is favorable for inlining, because the extra compilation + // work will be amortized more completely. + + // Profit is a rough measure of the amount of time we expect to save + // per execution of this site if we inline it. (1.0 == call overhead) + // Large profit favors inlining. Negative profit disables inlining. + + // Work is a rough measure of the amount of time a typical out-of-line + // call from this site is expected to take. (1.0 == call, no-op, return) + // Small work is somewhat favorable for inlining, since methods with + // short "hot" traces are more likely to inline smoothly. + + // Size is the number of graph nodes we expect this method to produce, + // not counting the inlining of any further warm calls it may include. + // Small size favors inlining, since small methods are more likely to + // inline smoothly. The size is estimated by examining the native code + // if available. The method bytecodes are also examined, assuming + // empirically observed node counts for each kind of bytecode. + + // Heat is the combined "goodness" of a site's inlining. If we were + // omniscient, it would be the difference of two sums of future execution + // times of code emitted for this site (amortized across multiple sites if + // sharing applies). The two sums are for versions of this call site with + // and without inlining. + + // We approximate this mythical quantity by playing with averages, + // rough estimates, and assumptions that history repeats itself. + // The basic formula count * profit is heuristically adjusted + // by looking at the expected compilation and execution times of + // of the inlined call. + + // Note: Some of these metrics may not be present in the final product, + // but exist in development builds to experiment with inline policy tuning. + + // This heuristic framework does not model well the very significant + // effects of multiple-level inlining. It is possible to see no immediate + // profit from inlining X->Y, but to get great profit from a subsequent + // inlining X->Y->Z. + + // This framework does not take well into account the problem of N**2 code + // size in a clique of mutually inlinable methods. + + WarmCallInfo* next() const { return _next; } + void set_next(WarmCallInfo* n) { _next = n; } + + static WarmCallInfo* _always_hot; + static WarmCallInfo* _always_cold; + + public: + // Because WarmInfo objects live over the entire lifetime of the + // Compile object, they are allocated into the comp_arena, which + // does not get resource marked or reset during the compile process + void *operator new( size_t x, Compile* C ) { return C->comp_arena()->Amalloc(x); } + void operator delete( void * ) { } // fast deallocation + + static WarmCallInfo* always_hot(); + static WarmCallInfo* always_cold(); + + WarmCallInfo() { + _call = NULL; + _hot_cg = NULL; + _next = NULL; + _count = _profit = _work = _size = _heat = 0; + } + + CallNode* call() const { return _call; } + float count() const { return _count; } + float size() const { return _size; } + float work() const { return _work; } + float profit() const { return _profit; } + float heat() const { return _heat; } + + void set_count(float x) { _count = x; } + void set_size(float x) { _size = x; } + void set_work(float x) { _work = x; } + void set_profit(float x) { _profit = x; } + void set_heat(float x) { _heat = x; } + + // Load initial heuristics from profiles, etc. + // The heuristics can be tweaked further by the caller. + void init(JVMState* call_site, ciMethod* call_method, ciCallProfile& profile, float prof_factor); + + static float MAX_VALUE() { return +1.0e10; } + static float MIN_VALUE() { return -1.0e10; } + + float compute_heat() const; + + void set_call(CallNode* call) { _call = call; } + void set_hot_cg(CallGenerator* cg) { _hot_cg = cg; } + + // Do not queue very hot or very cold calls. + // Make very cold ones out of line immediately. + // Inline very hot ones immediately. + // These queries apply various tunable limits + // to the above metrics in a systematic way. + // Test for coldness before testing for hotness. + bool is_cold() const; + bool is_hot() const; + + // Force a warm call to be hot. This worklists the call node for inlining. + void make_hot(); + + // Force a warm call to be cold. This worklists the call node for out-of-lining. + void make_cold(); + + // A reproducible total ordering, in which heat is the major key. + bool warmer_than(WarmCallInfo* that); + + // List management. These methods are called with the list head, + // and return the new list head, inserting or removing the receiver. + WarmCallInfo* insert_into(WarmCallInfo* head); + WarmCallInfo* remove_from(WarmCallInfo* head); + +#ifndef PRODUCT + void print() const; + void print_all() const; + int count_all() const; +#endif +}; diff --git a/hotspot/src/share/vm/opto/callnode.cpp b/hotspot/src/share/vm/opto/callnode.cpp new file mode 100644 index 00000000000..7fb60044039 --- /dev/null +++ b/hotspot/src/share/vm/opto/callnode.cpp @@ -0,0 +1,1311 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_callnode.cpp.incl" + +//============================================================================= +uint StartNode::size_of() const { return sizeof(*this); } +uint StartNode::cmp( const Node &n ) const +{ return _domain == ((StartNode&)n)._domain; } +const Type *StartNode::bottom_type() const { return _domain; } +const Type *StartNode::Value(PhaseTransform *phase) const { return _domain; } +#ifndef PRODUCT +void StartNode::dump_spec(outputStream *st) const { st->print(" #"); _domain->dump_on(st);} +#endif + +//------------------------------Ideal------------------------------------------ +Node *StartNode::Ideal(PhaseGVN *phase, bool can_reshape){ + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + +//------------------------------calling_convention----------------------------- +void StartNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const { + Matcher::calling_convention( sig_bt, parm_regs, argcnt, false ); +} + +//------------------------------Registers-------------------------------------- +const RegMask &StartNode::in_RegMask(uint) const { + return RegMask::Empty; +} + +//------------------------------match------------------------------------------ +// Construct projections for incoming parameters, and their RegMask info +Node *StartNode::match( const ProjNode *proj, const Matcher *match ) { + switch (proj->_con) { + case TypeFunc::Control: + case TypeFunc::I_O: + case TypeFunc::Memory: + return new (match->C, 1) MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj); + case TypeFunc::FramePtr: + return new (match->C, 1) MachProjNode(this,proj->_con,Matcher::c_frame_ptr_mask, Op_RegP); + case TypeFunc::ReturnAdr: + return new (match->C, 1) MachProjNode(this,proj->_con,match->_return_addr_mask,Op_RegP); + case TypeFunc::Parms: + default: { + uint parm_num = proj->_con - TypeFunc::Parms; + const Type *t = _domain->field_at(proj->_con); + if (t->base() == Type::Half) // 2nd half of Longs and Doubles + return new (match->C, 1) ConNode(Type::TOP); + uint ideal_reg = Matcher::base2reg[t->base()]; + RegMask &rm = match->_calling_convention_mask[parm_num]; + return new (match->C, 1) MachProjNode(this,proj->_con,rm,ideal_reg); + } + } + return NULL; +} + +//------------------------------StartOSRNode---------------------------------- +// The method start node for an on stack replacement adapter + +//------------------------------osr_domain----------------------------- +const TypeTuple *StartOSRNode::osr_domain() { + const Type **fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = TypeRawPtr::BOTTOM; // address of osr buffer + + return TypeTuple::make(TypeFunc::Parms+1, fields); +} + +//============================================================================= +const char * const ParmNode::names[TypeFunc::Parms+1] = { + "Control", "I_O", "Memory", "FramePtr", "ReturnAdr", "Parms" +}; + +#ifndef PRODUCT +void ParmNode::dump_spec(outputStream *st) const { + if( _con < TypeFunc::Parms ) { + st->print(names[_con]); + } else { + st->print("Parm%d: ",_con-TypeFunc::Parms); + // Verbose and WizardMode dump bottom_type for all nodes + if( !Verbose && !WizardMode ) bottom_type()->dump_on(st); + } +} +#endif + +uint ParmNode::ideal_reg() const { + switch( _con ) { + case TypeFunc::Control : // fall through + case TypeFunc::I_O : // fall through + case TypeFunc::Memory : return 0; + case TypeFunc::FramePtr : // fall through + case TypeFunc::ReturnAdr: return Op_RegP; + default : assert( _con > TypeFunc::Parms, "" ); + // fall through + case TypeFunc::Parms : { + // Type of argument being passed + const Type *t = in(0)->as_Start()->_domain->field_at(_con); + return Matcher::base2reg[t->base()]; + } + } + ShouldNotReachHere(); + return 0; +} + +//============================================================================= +ReturnNode::ReturnNode(uint edges, Node *cntrl, Node *i_o, Node *memory, Node *frameptr, Node *retadr ) : Node(edges) { + init_req(TypeFunc::Control,cntrl); + init_req(TypeFunc::I_O,i_o); + init_req(TypeFunc::Memory,memory); + init_req(TypeFunc::FramePtr,frameptr); + init_req(TypeFunc::ReturnAdr,retadr); +} + +Node *ReturnNode::Ideal(PhaseGVN *phase, bool can_reshape){ + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + +const Type *ReturnNode::Value( PhaseTransform *phase ) const { + return ( phase->type(in(TypeFunc::Control)) == Type::TOP) + ? Type::TOP + : Type::BOTTOM; +} + +// Do we Match on this edge index or not? No edges on return nodes +uint ReturnNode::match_edge(uint idx) const { + return 0; +} + + +#ifndef PRODUCT +void ReturnNode::dump_req() const { + // Dump the required inputs, enclosed in '(' and ')' + uint i; // Exit value of loop + for( i=0; iprint("returns"); + if( in(i) ) tty->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); + else tty->print("_ "); + } +} +#endif + +//============================================================================= +RethrowNode::RethrowNode( + Node* cntrl, + Node* i_o, + Node* memory, + Node* frameptr, + Node* ret_adr, + Node* exception +) : Node(TypeFunc::Parms + 1) { + init_req(TypeFunc::Control , cntrl ); + init_req(TypeFunc::I_O , i_o ); + init_req(TypeFunc::Memory , memory ); + init_req(TypeFunc::FramePtr , frameptr ); + init_req(TypeFunc::ReturnAdr, ret_adr); + init_req(TypeFunc::Parms , exception); +} + +Node *RethrowNode::Ideal(PhaseGVN *phase, bool can_reshape){ + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + +const Type *RethrowNode::Value( PhaseTransform *phase ) const { + return (phase->type(in(TypeFunc::Control)) == Type::TOP) + ? Type::TOP + : Type::BOTTOM; +} + +uint RethrowNode::match_edge(uint idx) const { + return 0; +} + +#ifndef PRODUCT +void RethrowNode::dump_req() const { + // Dump the required inputs, enclosed in '(' and ')' + uint i; // Exit value of loop + for( i=0; iprint("exception"); + if( in(i) ) tty->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); + else tty->print("_ "); + } +} +#endif + +//============================================================================= +// Do we Match on this edge index or not? Match only target address & method +uint TailCallNode::match_edge(uint idx) const { + return TypeFunc::Parms <= idx && idx <= TypeFunc::Parms+1; +} + +//============================================================================= +// Do we Match on this edge index or not? Match only target address & oop +uint TailJumpNode::match_edge(uint idx) const { + return TypeFunc::Parms <= idx && idx <= TypeFunc::Parms+1; +} + +//============================================================================= +JVMState::JVMState(ciMethod* method, JVMState* caller) { + assert(method != NULL, "must be valid call site"); + _method = method; + debug_only(_bci = -99); // random garbage value + debug_only(_map = (SafePointNode*)-1); + _caller = caller; + _depth = 1 + (caller == NULL ? 0 : caller->depth()); + _locoff = TypeFunc::Parms; + _stkoff = _locoff + _method->max_locals(); + _monoff = _stkoff + _method->max_stack(); + _endoff = _monoff; + _sp = 0; +} +JVMState::JVMState(int stack_size) { + _method = NULL; + _bci = InvocationEntryBci; + debug_only(_map = (SafePointNode*)-1); + _caller = NULL; + _depth = 1; + _locoff = TypeFunc::Parms; + _stkoff = _locoff; + _monoff = _stkoff + stack_size; + _endoff = _monoff; + _sp = 0; +} + +//--------------------------------of_depth------------------------------------- +JVMState* JVMState::of_depth(int d) const { + const JVMState* jvmp = this; + assert(0 < d && (uint)d <= depth(), "oob"); + for (int skip = depth() - d; skip > 0; skip--) { + jvmp = jvmp->caller(); + } + assert(jvmp->depth() == (uint)d, "found the right one"); + return (JVMState*)jvmp; +} + +//-----------------------------same_calls_as----------------------------------- +bool JVMState::same_calls_as(const JVMState* that) const { + if (this == that) return true; + if (this->depth() != that->depth()) return false; + const JVMState* p = this; + const JVMState* q = that; + for (;;) { + if (p->_method != q->_method) return false; + if (p->_method == NULL) return true; // bci is irrelevant + if (p->_bci != q->_bci) return false; + p = p->caller(); + q = q->caller(); + if (p == q) return true; + assert(p != NULL && q != NULL, "depth check ensures we don't run off end"); + } +} + +//------------------------------debug_start------------------------------------ +uint JVMState::debug_start() const { + debug_only(JVMState* jvmroot = of_depth(1)); + assert(jvmroot->locoff() <= this->locoff(), "youngest JVMState must be last"); + return of_depth(1)->locoff(); +} + +//-------------------------------debug_end------------------------------------- +uint JVMState::debug_end() const { + debug_only(JVMState* jvmroot = of_depth(1)); + assert(jvmroot->endoff() <= this->endoff(), "youngest JVMState must be last"); + return endoff(); +} + +//------------------------------debug_depth------------------------------------ +uint JVMState::debug_depth() const { + uint total = 0; + for (const JVMState* jvmp = this; jvmp != NULL; jvmp = jvmp->caller()) { + total += jvmp->debug_size(); + } + return total; +} + +//------------------------------format_helper---------------------------------- +// Given an allocation (a Chaitin object) and a Node decide if the Node carries +// any defined value or not. If it does, print out the register or constant. +#ifndef PRODUCT +static void format_helper( PhaseRegAlloc *regalloc, outputStream* st, Node *n, const char *msg, uint i ) { + if (n == NULL) { st->print(" NULL"); return; } + if( OptoReg::is_valid(regalloc->get_reg_first(n))) { // Check for undefined + char buf[50]; + regalloc->dump_register(n,buf); + st->print(" %s%d]=%s",msg,i,buf); + } else { // No register, but might be constant + const Type *t = n->bottom_type(); + switch (t->base()) { + case Type::Int: + st->print(" %s%d]=#"INT32_FORMAT,msg,i,t->is_int()->get_con()); + break; + case Type::AnyPtr: + assert( t == TypePtr::NULL_PTR, "" ); + st->print(" %s%d]=#NULL",msg,i); + break; + case Type::AryPtr: + case Type::KlassPtr: + case Type::InstPtr: + st->print(" %s%d]=#Ptr" INTPTR_FORMAT,msg,i,t->isa_oopptr()->const_oop()); + break; + case Type::RawPtr: + st->print(" %s%d]=#Raw" INTPTR_FORMAT,msg,i,t->is_rawptr()); + break; + case Type::DoubleCon: + st->print(" %s%d]=#%fD",msg,i,t->is_double_constant()->_d); + break; + case Type::FloatCon: + st->print(" %s%d]=#%fF",msg,i,t->is_float_constant()->_f); + break; + case Type::Long: + st->print(" %s%d]=#"INT64_FORMAT,msg,i,t->is_long()->get_con()); + break; + case Type::Half: + case Type::Top: + st->print(" %s%d]=_",msg,i); + break; + default: ShouldNotReachHere(); + } + } +} +#endif + +//------------------------------format----------------------------------------- +#ifndef PRODUCT +void JVMState::format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st) const { + st->print(" #"); + if( _method ) { + _method->print_short_name(st); + st->print(" @ bci:%d ",_bci); + } else { + st->print_cr(" runtime stub "); + return; + } + if (n->is_MachSafePoint()) { + MachSafePointNode *mcall = n->as_MachSafePoint(); + uint i; + // Print locals + for( i = 0; i < (uint)loc_size(); i++ ) + format_helper( regalloc, st, mcall->local(this, i), "L[", i ); + // Print stack + for (i = 0; i < (uint)stk_size(); i++) { + if ((uint)(_stkoff + i) >= mcall->len()) + st->print(" oob "); + else + format_helper( regalloc, st, mcall->stack(this, i), "STK[", i ); + } + for (i = 0; (int)i < nof_monitors(); i++) { + Node *box = mcall->monitor_box(this, i); + Node *obj = mcall->monitor_obj(this, i); + if ( OptoReg::is_valid(regalloc->get_reg_first(box)) ) { + while( !box->is_BoxLock() ) box = box->in(1); + format_helper( regalloc, st, box, "MON-BOX[", i ); + } else { + OptoReg::Name box_reg = BoxLockNode::stack_slot(box); + st->print(" MON-BOX%d=%s+%d", + i, + OptoReg::regname(OptoReg::c_frame_pointer), + regalloc->reg2offset(box_reg)); + } + format_helper( regalloc, st, obj, "MON-OBJ[", i ); + } + } + st->print_cr(""); + if (caller() != NULL) caller()->format(regalloc, n, st); +} +#endif + +#ifndef PRODUCT +void JVMState::dump_spec(outputStream *st) const { + if (_method != NULL) { + bool printed = false; + if (!Verbose) { + // The JVMS dumps make really, really long lines. + // Take out the most boring parts, which are the package prefixes. + char buf[500]; + stringStream namest(buf, sizeof(buf)); + _method->print_short_name(&namest); + if (namest.count() < sizeof(buf)) { + const char* name = namest.base(); + if (name[0] == ' ') ++name; + const char* endcn = strchr(name, ':'); // end of class name + if (endcn == NULL) endcn = strchr(name, '('); + if (endcn == NULL) endcn = name + strlen(name); + while (endcn > name && endcn[-1] != '.' && endcn[-1] != '/') + --endcn; + st->print(" %s", endcn); + printed = true; + } + } + if (!printed) + _method->print_short_name(st); + st->print(" @ bci:%d",_bci); + } else { + st->print(" runtime stub"); + } + if (caller() != NULL) caller()->dump_spec(st); +} +#endif + +#ifndef PRODUCT +void JVMState::dump_on(outputStream* st) const { + if (_map && !((uintptr_t)_map & 1)) { + if (_map->len() > _map->req()) { // _map->has_exceptions() + Node* ex = _map->in(_map->req()); // _map->next_exception() + // skip the first one; it's already being printed + while (ex != NULL && ex->len() > ex->req()) { + ex = ex->in(ex->req()); // ex->next_exception() + ex->dump(1); + } + } + _map->dump(2); + } + st->print("JVMS depth=%d loc=%d stk=%d mon=%d end=%d mondepth=%d sp=%d bci=%d method=", + depth(), locoff(), stkoff(), monoff(), endoff(), monitor_depth(), sp(), bci()); + if (_method == NULL) { + st->print_cr("(none)"); + } else { + _method->print_name(st); + st->cr(); + if (bci() >= 0 && bci() < _method->code_size()) { + st->print(" bc: "); + _method->print_codes_on(bci(), bci()+1, st); + } + } + if (caller() != NULL) { + caller()->dump_on(st); + } +} + +// Extra way to dump a jvms from the debugger, +// to avoid a bug with C++ member function calls. +void dump_jvms(JVMState* jvms) { + jvms->dump(); +} +#endif + +//--------------------------clone_shallow-------------------------------------- +JVMState* JVMState::clone_shallow(Compile* C) const { + JVMState* n = has_method() ? new (C) JVMState(_method, _caller) : new (C) JVMState(0); + n->set_bci(_bci); + n->set_locoff(_locoff); + n->set_stkoff(_stkoff); + n->set_monoff(_monoff); + n->set_endoff(_endoff); + n->set_sp(_sp); + n->set_map(_map); + return n; +} + +//---------------------------clone_deep---------------------------------------- +JVMState* JVMState::clone_deep(Compile* C) const { + JVMState* n = clone_shallow(C); + for (JVMState* p = n; p->_caller != NULL; p = p->_caller) { + p->_caller = p->_caller->clone_shallow(C); + } + assert(n->depth() == depth(), "sanity"); + assert(n->debug_depth() == debug_depth(), "sanity"); + return n; +} + +//============================================================================= +uint CallNode::cmp( const Node &n ) const +{ return _tf == ((CallNode&)n)._tf && _jvms == ((CallNode&)n)._jvms; } +#ifndef PRODUCT +void CallNode::dump_req() const { + // Dump the required inputs, enclosed in '(' and ')' + uint i; // Exit value of loop + for( i=0; iprint("("); + if( in(i) ) tty->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); + else tty->print("_ "); + } + tty->print(")"); +} + +void CallNode::dump_spec(outputStream *st) const { + st->print(" "); + tf()->dump_on(st); + if (_cnt != COUNT_UNKNOWN) st->print(" C=%f",_cnt); + if (jvms() != NULL) jvms()->dump_spec(st); +} +#endif + +const Type *CallNode::bottom_type() const { return tf()->range(); } +const Type *CallNode::Value(PhaseTransform *phase) const { + if (phase->type(in(0)) == Type::TOP) return Type::TOP; + return tf()->range(); +} + +//------------------------------calling_convention----------------------------- +void CallNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const { + // Use the standard compiler calling convention + Matcher::calling_convention( sig_bt, parm_regs, argcnt, true ); +} + + +//------------------------------match------------------------------------------ +// Construct projections for control, I/O, memory-fields, ..., and +// return result(s) along with their RegMask info +Node *CallNode::match( const ProjNode *proj, const Matcher *match ) { + switch (proj->_con) { + case TypeFunc::Control: + case TypeFunc::I_O: + case TypeFunc::Memory: + return new (match->C, 1) MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj); + + case TypeFunc::Parms+1: // For LONG & DOUBLE returns + assert(tf()->_range->field_at(TypeFunc::Parms+1) == Type::HALF, ""); + // 2nd half of doubles and longs + return new (match->C, 1) MachProjNode(this,proj->_con, RegMask::Empty, (uint)OptoReg::Bad); + + case TypeFunc::Parms: { // Normal returns + uint ideal_reg = Matcher::base2reg[tf()->range()->field_at(TypeFunc::Parms)->base()]; + OptoRegPair regs = is_CallRuntime() + ? match->c_return_value(ideal_reg,true) // Calls into C runtime + : match-> return_value(ideal_reg,true); // Calls into compiled Java code + RegMask rm = RegMask(regs.first()); + if( OptoReg::is_valid(regs.second()) ) + rm.Insert( regs.second() ); + return new (match->C, 1) MachProjNode(this,proj->_con,rm,ideal_reg); + } + + case TypeFunc::ReturnAdr: + case TypeFunc::FramePtr: + default: + ShouldNotReachHere(); + } + return NULL; +} + +// Do we Match on this edge index or not? Match no edges +uint CallNode::match_edge(uint idx) const { + return 0; +} + +//============================================================================= +uint CallJavaNode::size_of() const { return sizeof(*this); } +uint CallJavaNode::cmp( const Node &n ) const { + CallJavaNode &call = (CallJavaNode&)n; + return CallNode::cmp(call) && _method == call._method; +} +#ifndef PRODUCT +void CallJavaNode::dump_spec(outputStream *st) const { + if( _method ) _method->print_short_name(st); + CallNode::dump_spec(st); +} +#endif + +//============================================================================= +uint CallStaticJavaNode::size_of() const { return sizeof(*this); } +uint CallStaticJavaNode::cmp( const Node &n ) const { + CallStaticJavaNode &call = (CallStaticJavaNode&)n; + return CallJavaNode::cmp(call); +} + +//----------------------------uncommon_trap_request---------------------------- +// If this is an uncommon trap, return the request code, else zero. +int CallStaticJavaNode::uncommon_trap_request() const { + if (_name != NULL && !strcmp(_name, "uncommon_trap")) { + return extract_uncommon_trap_request(this); + } + return 0; +} +int CallStaticJavaNode::extract_uncommon_trap_request(const Node* call) { +#ifndef PRODUCT + if (!(call->req() > TypeFunc::Parms && + call->in(TypeFunc::Parms) != NULL && + call->in(TypeFunc::Parms)->is_Con())) { + assert(_in_dump_cnt != 0, "OK if dumping"); + tty->print("[bad uncommon trap]"); + return 0; + } +#endif + return call->in(TypeFunc::Parms)->bottom_type()->is_int()->get_con(); +} + +#ifndef PRODUCT +void CallStaticJavaNode::dump_spec(outputStream *st) const { + st->print("# Static "); + if (_name != NULL) { + st->print("%s", _name); + int trap_req = uncommon_trap_request(); + if (trap_req != 0) { + char buf[100]; + st->print("(%s)", + Deoptimization::format_trap_request(buf, sizeof(buf), + trap_req)); + } + st->print(" "); + } + CallJavaNode::dump_spec(st); +} +#endif + +//============================================================================= +uint CallDynamicJavaNode::size_of() const { return sizeof(*this); } +uint CallDynamicJavaNode::cmp( const Node &n ) const { + CallDynamicJavaNode &call = (CallDynamicJavaNode&)n; + return CallJavaNode::cmp(call); +} +#ifndef PRODUCT +void CallDynamicJavaNode::dump_spec(outputStream *st) const { + st->print("# Dynamic "); + CallJavaNode::dump_spec(st); +} +#endif + +//============================================================================= +uint CallRuntimeNode::size_of() const { return sizeof(*this); } +uint CallRuntimeNode::cmp( const Node &n ) const { + CallRuntimeNode &call = (CallRuntimeNode&)n; + return CallNode::cmp(call) && !strcmp(_name,call._name); +} +#ifndef PRODUCT +void CallRuntimeNode::dump_spec(outputStream *st) const { + st->print("# "); + st->print(_name); + CallNode::dump_spec(st); +} +#endif + +//------------------------------calling_convention----------------------------- +void CallRuntimeNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const { + Matcher::c_calling_convention( sig_bt, parm_regs, argcnt ); +} + +//============================================================================= +//------------------------------calling_convention----------------------------- + + +//============================================================================= +#ifndef PRODUCT +void CallLeafNode::dump_spec(outputStream *st) const { + st->print("# "); + st->print(_name); + CallNode::dump_spec(st); +} +#endif + +//============================================================================= + +void SafePointNode::set_local(JVMState* jvms, uint idx, Node *c) { + assert(verify_jvms(jvms), "jvms must match"); + int loc = jvms->locoff() + idx; + if (in(loc)->is_top() && idx > 0 && !c->is_top() ) { + // If current local idx is top then local idx - 1 could + // be a long/double that needs to be killed since top could + // represent the 2nd half ofthe long/double. + uint ideal = in(loc -1)->ideal_reg(); + if (ideal == Op_RegD || ideal == Op_RegL) { + // set other (low index) half to top + set_req(loc - 1, in(loc)); + } + } + set_req(loc, c); +} + +uint SafePointNode::size_of() const { return sizeof(*this); } +uint SafePointNode::cmp( const Node &n ) const { + return (&n == this); // Always fail except on self +} + +//-------------------------set_next_exception---------------------------------- +void SafePointNode::set_next_exception(SafePointNode* n) { + assert(n == NULL || n->Opcode() == Op_SafePoint, "correct value for next_exception"); + if (len() == req()) { + if (n != NULL) add_prec(n); + } else { + set_prec(req(), n); + } +} + + +//----------------------------next_exception----------------------------------- +SafePointNode* SafePointNode::next_exception() const { + if (len() == req()) { + return NULL; + } else { + Node* n = in(req()); + assert(n == NULL || n->Opcode() == Op_SafePoint, "no other uses of prec edges"); + return (SafePointNode*) n; + } +} + + +//------------------------------Ideal------------------------------------------ +// Skip over any collapsed Regions +Node *SafePointNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if (remove_dead_region(phase, can_reshape)) return this; + + return NULL; +} + +//------------------------------Identity--------------------------------------- +// Remove obviously duplicate safepoints +Node *SafePointNode::Identity( PhaseTransform *phase ) { + + // If you have back to back safepoints, remove one + if( in(TypeFunc::Control)->is_SafePoint() ) + return in(TypeFunc::Control); + + if( in(0)->is_Proj() ) { + Node *n0 = in(0)->in(0); + // Check if he is a call projection (except Leaf Call) + if( n0->is_Catch() ) { + n0 = n0->in(0)->in(0); + assert( n0->is_Call(), "expect a call here" ); + } + if( n0->is_Call() && n0->as_Call()->guaranteed_safepoint() ) { + // Useless Safepoint, so remove it + return in(TypeFunc::Control); + } + } + + return this; +} + +//------------------------------Value------------------------------------------ +const Type *SafePointNode::Value( PhaseTransform *phase ) const { + if( phase->type(in(0)) == Type::TOP ) return Type::TOP; + if( phase->eqv( in(0), this ) ) return Type::TOP; // Dead infinite loop + return Type::CONTROL; +} + +#ifndef PRODUCT +void SafePointNode::dump_spec(outputStream *st) const { + st->print(" SafePoint "); +} +#endif + +const RegMask &SafePointNode::in_RegMask(uint idx) const { + if( idx < TypeFunc::Parms ) return RegMask::Empty; + // Values outside the domain represent debug info + return *(Compile::current()->matcher()->idealreg2debugmask[in(idx)->ideal_reg()]); +} +const RegMask &SafePointNode::out_RegMask() const { + return RegMask::Empty; +} + + +void SafePointNode::grow_stack(JVMState* jvms, uint grow_by) { + assert((int)grow_by > 0, "sanity"); + int monoff = jvms->monoff(); + int endoff = jvms->endoff(); + assert(endoff == (int)req(), "no other states or debug info after me"); + Node* top = Compile::current()->top(); + for (uint i = 0; i < grow_by; i++) { + ins_req(monoff, top); + } + jvms->set_monoff(monoff + grow_by); + jvms->set_endoff(endoff + grow_by); +} + +void SafePointNode::push_monitor(const FastLockNode *lock) { + // Add a LockNode, which points to both the original BoxLockNode (the + // stack space for the monitor) and the Object being locked. + const int MonitorEdges = 2; + assert(JVMState::logMonitorEdges == exact_log2(MonitorEdges), "correct MonitorEdges"); + assert(req() == jvms()->endoff(), "correct sizing"); + if (GenerateSynchronizationCode) { + add_req(lock->box_node()); + add_req(lock->obj_node()); + } else { + add_req(NULL); + add_req(NULL); + } + jvms()->set_endoff(req()); +} + +void SafePointNode::pop_monitor() { + // Delete last monitor from debug info + debug_only(int num_before_pop = jvms()->nof_monitors()); + const int MonitorEdges = (1<endoff(); + int new_endoff = endoff - MonitorEdges; + jvms()->set_endoff(new_endoff); + while (endoff > new_endoff) del_req(--endoff); + assert(jvms()->nof_monitors() == num_before_pop-1, ""); +} + +Node *SafePointNode::peek_monitor_box() const { + int mon = jvms()->nof_monitors() - 1; + assert(mon >= 0, "most have a monitor"); + return monitor_box(jvms(), mon); +} + +Node *SafePointNode::peek_monitor_obj() const { + int mon = jvms()->nof_monitors() - 1; + assert(mon >= 0, "most have a monitor"); + return monitor_obj(jvms(), mon); +} + +// Do we Match on this edge index or not? Match no edges +uint SafePointNode::match_edge(uint idx) const { + if( !needs_polling_address_input() ) + return 0; + + return (TypeFunc::Parms == idx); +} + +//============================================================================= +uint AllocateNode::size_of() const { return sizeof(*this); } + +AllocateNode::AllocateNode(Compile* C, const TypeFunc *atype, + Node *ctrl, Node *mem, Node *abio, + Node *size, Node *klass_node, Node *initial_test) + : CallNode(atype, NULL, TypeRawPtr::BOTTOM) +{ + init_class_id(Class_Allocate); + init_flags(Flag_is_macro); + Node *topnode = C->top(); + + init_req( TypeFunc::Control , ctrl ); + init_req( TypeFunc::I_O , abio ); + init_req( TypeFunc::Memory , mem ); + init_req( TypeFunc::ReturnAdr, topnode ); + init_req( TypeFunc::FramePtr , topnode ); + init_req( AllocSize , size); + init_req( KlassNode , klass_node); + init_req( InitialTest , initial_test); + init_req( ALength , topnode); + C->add_macro_node(this); +} + +//============================================================================= +uint AllocateArrayNode::size_of() const { return sizeof(*this); } + +//============================================================================= +uint LockNode::size_of() const { return sizeof(*this); } + +// Redundant lock elimination +// +// There are various patterns of locking where we release and +// immediately reacquire a lock in a piece of code where no operations +// occur in between that would be observable. In those cases we can +// skip releasing and reacquiring the lock without violating any +// fairness requirements. Doing this around a loop could cause a lock +// to be held for a very long time so we concentrate on non-looping +// control flow. We also require that the operations are fully +// redundant meaning that we don't introduce new lock operations on +// some paths so to be able to eliminate it on others ala PRE. This +// would probably require some more extensive graph manipulation to +// guarantee that the memory edges were all handled correctly. +// +// Assuming p is a simple predicate which can't trap in any way and s +// is a synchronized method consider this code: +// +// s(); +// if (p) +// s(); +// else +// s(); +// s(); +// +// 1. The unlocks of the first call to s can be eliminated if the +// locks inside the then and else branches are eliminated. +// +// 2. The unlocks of the then and else branches can be eliminated if +// the lock of the final call to s is eliminated. +// +// Either of these cases subsumes the simple case of sequential control flow +// +// Addtionally we can eliminate versions without the else case: +// +// s(); +// if (p) +// s(); +// s(); +// +// 3. In this case we eliminate the unlock of the first s, the lock +// and unlock in the then case and the lock in the final s. +// +// Note also that in all these cases the then/else pieces don't have +// to be trivial as long as they begin and end with synchronization +// operations. +// +// s(); +// if (p) +// s(); +// f(); +// s(); +// s(); +// +// The code will work properly for this case, leaving in the unlock +// before the call to f and the relock after it. +// +// A potentially interesting case which isn't handled here is when the +// locking is partially redundant. +// +// s(); +// if (p) +// s(); +// +// This could be eliminated putting unlocking on the else case and +// eliminating the first unlock and the lock in the then side. +// Alternatively the unlock could be moved out of the then side so it +// was after the merge and the first unlock and second lock +// eliminated. This might require less manipulation of the memory +// state to get correct. +// +// Additionally we might allow work between a unlock and lock before +// giving up eliminating the locks. The current code disallows any +// conditional control flow between these operations. A formulation +// similar to partial redundancy elimination computing the +// availability of unlocking and the anticipatability of locking at a +// program point would allow detection of fully redundant locking with +// some amount of work in between. I'm not sure how often I really +// think that would occur though. Most of the cases I've seen +// indicate it's likely non-trivial work would occur in between. +// There may be other more complicated constructs where we could +// eliminate locking but I haven't seen any others appear as hot or +// interesting. +// +// Locking and unlocking have a canonical form in ideal that looks +// roughly like this: +// +// +// | \\------+ +// | \ \ +// | BoxLock \ +// | | | \ +// | | \ \ +// | | FastLock +// | | / +// | | / +// | | | +// +// Lock +// | +// Proj #0 +// | +// MembarAcquire +// | +// Proj #0 +// +// MembarRelease +// | +// Proj #0 +// | +// Unlock +// | +// Proj #0 +// +// +// This code proceeds by processing Lock nodes during PhaseIterGVN +// and searching back through its control for the proper code +// patterns. Once it finds a set of lock and unlock operations to +// eliminate they are marked as eliminatable which causes the +// expansion of the Lock and Unlock macro nodes to make the operation a NOP +// +//============================================================================= + +// +// Utility function to skip over uninteresting control nodes. Nodes skipped are: +// - copy regions. (These may not have been optimized away yet.) +// - eliminated locking nodes +// +static Node *next_control(Node *ctrl) { + if (ctrl == NULL) + return NULL; + while (1) { + if (ctrl->is_Region()) { + RegionNode *r = ctrl->as_Region(); + Node *n = r->is_copy(); + if (n == NULL) + break; // hit a region, return it + else + ctrl = n; + } else if (ctrl->is_Proj()) { + Node *in0 = ctrl->in(0); + if (in0->is_AbstractLock() && in0->as_AbstractLock()->is_eliminated()) { + ctrl = in0->in(0); + } else { + break; + } + } else { + break; // found an interesting control + } + } + return ctrl; +} +// +// Given a control, see if it's the control projection of an Unlock which +// operating on the same object as lock. +// +bool AbstractLockNode::find_matching_unlock(const Node* ctrl, LockNode* lock, + GrowableArray &lock_ops) { + ProjNode *ctrl_proj = (ctrl->is_Proj()) ? ctrl->as_Proj() : NULL; + if (ctrl_proj != NULL && ctrl_proj->_con == TypeFunc::Control) { + Node *n = ctrl_proj->in(0); + if (n != NULL && n->is_Unlock()) { + UnlockNode *unlock = n->as_Unlock(); + if ((lock->obj_node() == unlock->obj_node()) && + (lock->box_node() == unlock->box_node()) && !unlock->is_eliminated()) { + lock_ops.append(unlock); + return true; + } + } + } + return false; +} + +// +// Find the lock matching an unlock. Returns null if a safepoint +// or complicated control is encountered first. +LockNode *AbstractLockNode::find_matching_lock(UnlockNode* unlock) { + LockNode *lock_result = NULL; + // find the matching lock, or an intervening safepoint + Node *ctrl = next_control(unlock->in(0)); + while (1) { + assert(ctrl != NULL, "invalid control graph"); + assert(!ctrl->is_Start(), "missing lock for unlock"); + if (ctrl->is_top()) break; // dead control path + if (ctrl->is_Proj()) ctrl = ctrl->in(0); + if (ctrl->is_SafePoint()) { + break; // found a safepoint (may be the lock we are searching for) + } else if (ctrl->is_Region()) { + // Check for a simple diamond pattern. Punt on anything more complicated + if (ctrl->req() == 3 && ctrl->in(1) != NULL && ctrl->in(2) != NULL) { + Node *in1 = next_control(ctrl->in(1)); + Node *in2 = next_control(ctrl->in(2)); + if (((in1->is_IfTrue() && in2->is_IfFalse()) || + (in2->is_IfTrue() && in1->is_IfFalse())) && (in1->in(0) == in2->in(0))) { + ctrl = next_control(in1->in(0)->in(0)); + } else { + break; + } + } else { + break; + } + } else { + ctrl = next_control(ctrl->in(0)); // keep searching + } + } + if (ctrl->is_Lock()) { + LockNode *lock = ctrl->as_Lock(); + if ((lock->obj_node() == unlock->obj_node()) && + (lock->box_node() == unlock->box_node())) { + lock_result = lock; + } + } + return lock_result; +} + +// This code corresponds to case 3 above. + +bool AbstractLockNode::find_lock_and_unlock_through_if(Node* node, LockNode* lock, + GrowableArray &lock_ops) { + Node* if_node = node->in(0); + bool if_true = node->is_IfTrue(); + + if (if_node->is_If() && if_node->outcnt() == 2 && (if_true || node->is_IfFalse())) { + Node *lock_ctrl = next_control(if_node->in(0)); + if (find_matching_unlock(lock_ctrl, lock, lock_ops)) { + Node* lock1_node = NULL; + ProjNode* proj = if_node->as_If()->proj_out(!if_true); + if (if_true) { + if (proj->is_IfFalse() && proj->outcnt() == 1) { + lock1_node = proj->unique_out(); + } + } else { + if (proj->is_IfTrue() && proj->outcnt() == 1) { + lock1_node = proj->unique_out(); + } + } + if (lock1_node != NULL && lock1_node->is_Lock()) { + LockNode *lock1 = lock1_node->as_Lock(); + if ((lock->obj_node() == lock1->obj_node()) && + (lock->box_node() == lock1->box_node()) && !lock1->is_eliminated()) { + lock_ops.append(lock1); + return true; + } + } + } + } + + lock_ops.trunc_to(0); + return false; +} + +bool AbstractLockNode::find_unlocks_for_region(const RegionNode* region, LockNode* lock, + GrowableArray &lock_ops) { + // check each control merging at this point for a matching unlock. + // in(0) should be self edge so skip it. + for (int i = 1; i < (int)region->req(); i++) { + Node *in_node = next_control(region->in(i)); + if (in_node != NULL) { + if (find_matching_unlock(in_node, lock, lock_ops)) { + // found a match so keep on checking. + continue; + } else if (find_lock_and_unlock_through_if(in_node, lock, lock_ops)) { + continue; + } + + // If we fall through to here then it was some kind of node we + // don't understand or there wasn't a matching unlock, so give + // up trying to merge locks. + lock_ops.trunc_to(0); + return false; + } + } + return true; + +} + +#ifndef PRODUCT +// +// Create a counter which counts the number of times this lock is acquired +// +void AbstractLockNode::create_lock_counter(JVMState* state) { + _counter = OptoRuntime::new_named_counter(state, NamedCounter::LockCounter); +} +#endif + +void AbstractLockNode::set_eliminated() { + _eliminate = true; +#ifndef PRODUCT + if (_counter) { + // Update the counter to indicate that this lock was eliminated. + // The counter update code will stay around even though the + // optimizer will eliminate the lock operation itself. + _counter->set_tag(NamedCounter::EliminatedLockCounter); + } +#endif +} + +//============================================================================= +Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) { + + // perform any generic optimizations first + Node *result = SafePointNode::Ideal(phase, can_reshape); + + // Now see if we can optimize away this lock. We don't actually + // remove the locking here, we simply set the _eliminate flag which + // prevents macro expansion from expanding the lock. Since we don't + // modify the graph, the value returned from this function is the + // one computed above. + if (EliminateLocks && !is_eliminated()) { + // + // Try lock coarsening + // + PhaseIterGVN* iter = phase->is_IterGVN(); + if (iter != NULL) { + + GrowableArray lock_ops; + + Node *ctrl = next_control(in(0)); + + // now search back for a matching Unlock + if (find_matching_unlock(ctrl, this, lock_ops)) { + // found an unlock directly preceding this lock. This is the + // case of single unlock directly control dependent on a + // single lock which is the trivial version of case 1 or 2. + } else if (ctrl->is_Region() ) { + if (find_unlocks_for_region(ctrl->as_Region(), this, lock_ops)) { + // found lock preceded by multiple unlocks along all paths + // joining at this point which is case 3 in description above. + } + } else { + // see if this lock comes from either half of an if and the + // predecessors merges unlocks and the other half of the if + // performs a lock. + if (find_lock_and_unlock_through_if(ctrl, this, lock_ops)) { + // found unlock splitting to an if with locks on both branches. + } + } + + if (lock_ops.length() > 0) { + // add ourselves to the list of locks to be eliminated. + lock_ops.append(this); + + #ifndef PRODUCT + if (PrintEliminateLocks) { + int locks = 0; + int unlocks = 0; + for (int i = 0; i < lock_ops.length(); i++) { + AbstractLockNode* lock = lock_ops.at(i); + if (lock->Opcode() == Op_Lock) locks++; + else unlocks++; + if (Verbose) { + lock->dump(1); + } + } + tty->print_cr("***Eliminated %d unlocks and %d locks", unlocks, locks); + } + #endif + + // for each of the identified locks, mark them + // as eliminatable + for (int i = 0; i < lock_ops.length(); i++) { + AbstractLockNode* lock = lock_ops.at(i); + + // Mark it eliminated to update any counters + lock->set_eliminated(); + } + } else if (result != NULL && ctrl->is_Region() && + iter->_worklist.member(ctrl)) { + // We weren't able to find any opportunities but the region this + // lock is control dependent on hasn't been processed yet so put + // this lock back on the worklist so we can check again once any + // region simplification has occurred. + iter->_worklist.push(this); + } + } + } + + return result; +} + +//============================================================================= +uint UnlockNode::size_of() const { return sizeof(*this); } + +//============================================================================= +Node *UnlockNode::Ideal(PhaseGVN *phase, bool can_reshape) { + + // perform any generic optimizations first + Node * result = SafePointNode::Ideal(phase, can_reshape); + + // Now see if we can optimize away this unlock. We don't actually + // remove the unlocking here, we simply set the _eliminate flag which + // prevents macro expansion from expanding the unlock. Since we don't + // modify the graph, the value returned from this function is the + // one computed above. + if (EliminateLocks && !is_eliminated()) { + // + // If we are unlocking an unescaped object, the lock/unlock is unnecessary + // We can eliminate them if there are no safepoints in the locked region. + // + ConnectionGraph *cgr = Compile::current()->congraph(); + if (cgr != NULL && cgr->escape_state(obj_node(), phase) == PointsToNode::NoEscape) { + GrowableArray lock_ops; + LockNode *lock = find_matching_lock(this); + if (lock != NULL) { + lock_ops.append(this); + lock_ops.append(lock); + // find other unlocks which pair with the lock we found and add them + // to the list + Node * box = box_node(); + + for (DUIterator_Fast imax, i = box->fast_outs(imax); i < imax; i++) { + Node *use = box->fast_out(i); + if (use->is_Unlock() && use != this) { + UnlockNode *unlock1 = use->as_Unlock(); + if (!unlock1->is_eliminated()) { + LockNode *lock1 = find_matching_lock(unlock1); + if (lock == lock1) + lock_ops.append(unlock1); + else if (lock1 == NULL) { + // we can't find a matching lock, we must assume the worst + lock_ops.trunc_to(0); + break; + } + } + } + } + if (lock_ops.length() > 0) { + + #ifndef PRODUCT + if (PrintEliminateLocks) { + int locks = 0; + int unlocks = 0; + for (int i = 0; i < lock_ops.length(); i++) { + AbstractLockNode* lock = lock_ops.at(i); + if (lock->Opcode() == Op_Lock) locks++; + else unlocks++; + if (Verbose) { + lock->dump(1); + } + } + tty->print_cr("***Eliminated %d unescaped unlocks and %d unescaped locks", unlocks, locks); + } + #endif + + // for each of the identified locks, mark them + // as eliminatable + for (int i = 0; i < lock_ops.length(); i++) { + AbstractLockNode* lock = lock_ops.at(i); + + // Mark it eliminated to update any counters + lock->set_eliminated(); + } + } + } + } + } + return result; +} diff --git a/hotspot/src/share/vm/opto/callnode.hpp b/hotspot/src/share/vm/opto/callnode.hpp new file mode 100644 index 00000000000..e1e6116b689 --- /dev/null +++ b/hotspot/src/share/vm/opto/callnode.hpp @@ -0,0 +1,814 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +class Chaitin; +class NamedCounter; +class MultiNode; +class SafePointNode; +class CallNode; +class CallJavaNode; +class CallStaticJavaNode; +class CallDynamicJavaNode; +class CallRuntimeNode; +class CallLeafNode; +class CallLeafNoFPNode; +class AllocateNode; +class AllocateArrayNode; +class LockNode; +class UnlockNode; +class JVMState; +class OopMap; +class State; +class StartNode; +class MachCallNode; +class FastLockNode; + +//------------------------------StartNode-------------------------------------- +// The method start node +class StartNode : public MultiNode { + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger +public: + const TypeTuple *_domain; + StartNode( Node *root, const TypeTuple *domain ) : MultiNode(2), _domain(domain) { + init_class_id(Class_Start); + init_flags(Flag_is_block_start); + init_req(0,this); + init_req(1,root); + } + virtual int Opcode() const; + virtual bool pinned() const { return true; }; + virtual const Type *bottom_type() const; + virtual const TypePtr *adr_type() const { return TypePtr::BOTTOM; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual void calling_convention( BasicType* sig_bt, VMRegPair *parm_reg, uint length ) const; + virtual const RegMask &in_RegMask(uint) const; + virtual Node *match( const ProjNode *proj, const Matcher *m ); + virtual uint ideal_reg() const { return 0; } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------StartOSRNode----------------------------------- +// The method start node for on stack replacement code +class StartOSRNode : public StartNode { +public: + StartOSRNode( Node *root, const TypeTuple *domain ) : StartNode(root, domain) {} + virtual int Opcode() const; + static const TypeTuple *osr_domain(); +}; + + +//------------------------------ParmNode--------------------------------------- +// Incoming parameters +class ParmNode : public ProjNode { + static const char * const names[TypeFunc::Parms+1]; +public: + ParmNode( StartNode *src, uint con ) : ProjNode(src,con) {} + virtual int Opcode() const; + virtual bool is_CFG() const { return (_con == TypeFunc::Control); } + virtual uint ideal_reg() const; +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + + +//------------------------------ReturnNode------------------------------------- +// Return from subroutine node +class ReturnNode : public Node { +public: + ReturnNode( uint edges, Node *cntrl, Node *i_o, Node *memory, Node *retadr, Node *frameptr ); + virtual int Opcode() const; + virtual bool is_CFG() const { return true; } + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual bool depends_only_on_test() const { return false; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual uint ideal_reg() const { return NotAMachineReg; } + virtual uint match_edge(uint idx) const; +#ifndef PRODUCT + virtual void dump_req() const; +#endif +}; + + +//------------------------------RethrowNode------------------------------------ +// Rethrow of exception at call site. Ends a procedure before rethrowing; +// ends the current basic block like a ReturnNode. Restores registers and +// unwinds stack. Rethrow happens in the caller's method. +class RethrowNode : public Node { + public: + RethrowNode( Node *cntrl, Node *i_o, Node *memory, Node *frameptr, Node *ret_adr, Node *exception ); + virtual int Opcode() const; + virtual bool is_CFG() const { return true; } + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual bool depends_only_on_test() const { return false; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual uint match_edge(uint idx) const; + virtual uint ideal_reg() const { return NotAMachineReg; } +#ifndef PRODUCT + virtual void dump_req() const; +#endif +}; + + +//------------------------------TailCallNode----------------------------------- +// Pop stack frame and jump indirect +class TailCallNode : public ReturnNode { +public: + TailCallNode( Node *cntrl, Node *i_o, Node *memory, Node *frameptr, Node *retadr, Node *target, Node *moop ) + : ReturnNode( TypeFunc::Parms+2, cntrl, i_o, memory, frameptr, retadr ) { + init_req(TypeFunc::Parms, target); + init_req(TypeFunc::Parms+1, moop); + } + + virtual int Opcode() const; + virtual uint match_edge(uint idx) const; +}; + +//------------------------------TailJumpNode----------------------------------- +// Pop stack frame and jump indirect +class TailJumpNode : public ReturnNode { +public: + TailJumpNode( Node *cntrl, Node *i_o, Node *memory, Node *frameptr, Node *target, Node *ex_oop) + : ReturnNode(TypeFunc::Parms+2, cntrl, i_o, memory, frameptr, Compile::current()->top()) { + init_req(TypeFunc::Parms, target); + init_req(TypeFunc::Parms+1, ex_oop); + } + + virtual int Opcode() const; + virtual uint match_edge(uint idx) const; +}; + +//-------------------------------JVMState------------------------------------- +// A linked list of JVMState nodes captures the whole interpreter state, +// plus GC roots, for all active calls at some call site in this compilation +// unit. (If there is no inlining, then the list has exactly one link.) +// This provides a way to map the optimized program back into the interpreter, +// or to let the GC mark the stack. +class JVMState : public ResourceObj { +private: + JVMState* _caller; // List pointer for forming scope chains + uint _depth; // One mroe than caller depth, or one. + uint _locoff; // Offset to locals in input edge mapping + uint _stkoff; // Offset to stack in input edge mapping + uint _monoff; // Offset to monitors in input edge mapping + uint _endoff; // Offset to end of input edge mapping + uint _sp; // Jave Expression Stack Pointer for this state + int _bci; // Byte Code Index of this JVM point + ciMethod* _method; // Method Pointer + SafePointNode* _map; // Map node associated with this scope +public: + friend class Compile; + + // Because JVMState objects live over the entire lifetime of the + // Compile object, they are allocated into the comp_arena, which + // does not get resource marked or reset during the compile process + void *operator new( size_t x, Compile* C ) { return C->comp_arena()->Amalloc(x); } + void operator delete( void * ) { } // fast deallocation + + // Create a new JVMState, ready for abstract interpretation. + JVMState(ciMethod* method, JVMState* caller); + JVMState(int stack_size); // root state; has a null method + + // Access functions for the JVM + uint locoff() const { return _locoff; } + uint stkoff() const { return _stkoff; } + uint argoff() const { return _stkoff + _sp; } + uint monoff() const { return _monoff; } + uint endoff() const { return _endoff; } + uint oopoff() const { return debug_end(); } + + int loc_size() const { return _stkoff - _locoff; } + int stk_size() const { return _monoff - _stkoff; } + int mon_size() const { return _endoff - _monoff; } + + bool is_loc(uint i) const { return i >= _locoff && i < _stkoff; } + bool is_stk(uint i) const { return i >= _stkoff && i < _monoff; } + bool is_mon(uint i) const { return i >= _monoff && i < _endoff; } + + uint sp() const { return _sp; } + int bci() const { return _bci; } + bool has_method() const { return _method != NULL; } + ciMethod* method() const { assert(has_method(), ""); return _method; } + JVMState* caller() const { return _caller; } + SafePointNode* map() const { return _map; } + uint depth() const { return _depth; } + uint debug_start() const; // returns locoff of root caller + uint debug_end() const; // returns endoff of self + uint debug_size() const { return loc_size() + sp() + mon_size(); } + uint debug_depth() const; // returns sum of debug_size values at all depths + + // Returns the JVM state at the desired depth (1 == root). + JVMState* of_depth(int d) const; + + // Tells if two JVM states have the same call chain (depth, methods, & bcis). + bool same_calls_as(const JVMState* that) const; + + // Monitors (monitors are stored as (boxNode, objNode) pairs + enum { logMonitorEdges = 1 }; + int nof_monitors() const { return mon_size() >> logMonitorEdges; } + int monitor_depth() const { return nof_monitors() + (caller() ? caller()->monitor_depth() : 0); } + int monitor_box_offset(int idx) const { return monoff() + (idx << logMonitorEdges) + 0; } + int monitor_obj_offset(int idx) const { return monoff() + (idx << logMonitorEdges) + 1; } + bool is_monitor_box(uint off) const { + assert(is_mon(off), "should be called only for monitor edge"); + return (0 == bitfield(off - monoff(), 0, logMonitorEdges)); + } + bool is_monitor_use(uint off) const { return (is_mon(off) + && is_monitor_box(off)) + || (caller() && caller()->is_monitor_use(off)); } + + // Initialization functions for the JVM + void set_locoff(uint off) { _locoff = off; } + void set_stkoff(uint off) { _stkoff = off; } + void set_monoff(uint off) { _monoff = off; } + void set_endoff(uint off) { _endoff = off; } + void set_offsets(uint off) { _locoff = _stkoff = _monoff = _endoff = off; } + void set_map(SafePointNode *map) { _map = map; } + void set_sp(uint sp) { _sp = sp; } + void set_bci(int bci) { _bci = bci; } + + // Miscellaneous utility functions + JVMState* clone_deep(Compile* C) const; // recursively clones caller chain + JVMState* clone_shallow(Compile* C) const; // retains uncloned caller + +#ifndef PRODUCT + void format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st) const; + void dump_spec(outputStream *st) const; + void dump_on(outputStream* st) const; + void dump() const { + dump_on(tty); + } +#endif +}; + +//------------------------------SafePointNode---------------------------------- +// A SafePointNode is a subclass of a MultiNode for convenience (and +// potential code sharing) only - conceptually it is independent of +// the Node semantics. +class SafePointNode : public MultiNode { + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger + +public: + SafePointNode(uint edges, JVMState* jvms, + // A plain safepoint advertises no memory effects (NULL): + const TypePtr* adr_type = NULL) + : MultiNode( edges ), + _jvms(jvms), + _oop_map(NULL), + _adr_type(adr_type) + { + init_class_id(Class_SafePoint); + } + + OopMap* _oop_map; // Array of OopMap info (8-bit char) for GC + JVMState* const _jvms; // Pointer to list of JVM State objects + const TypePtr* _adr_type; // What type of memory does this node produce? + + // Many calls take *all* of memory as input, + // but some produce a limited subset of that memory as output. + // The adr_type reports the call's behavior as a store, not a load. + + virtual JVMState* jvms() const { return _jvms; } + void set_jvms(JVMState* s) { + *(JVMState**)&_jvms = s; // override const attribute in the accessor + } + OopMap *oop_map() const { return _oop_map; } + void set_oop_map(OopMap *om) { _oop_map = om; } + + // Functionality from old debug nodes which has changed + Node *local(JVMState* jvms, uint idx) const { + assert(verify_jvms(jvms), "jvms must match"); + return in(jvms->locoff() + idx); + } + Node *stack(JVMState* jvms, uint idx) const { + assert(verify_jvms(jvms), "jvms must match"); + return in(jvms->stkoff() + idx); + } + Node *argument(JVMState* jvms, uint idx) const { + assert(verify_jvms(jvms), "jvms must match"); + return in(jvms->argoff() + idx); + } + Node *monitor_box(JVMState* jvms, uint idx) const { + assert(verify_jvms(jvms), "jvms must match"); + return in(jvms->monitor_box_offset(idx)); + } + Node *monitor_obj(JVMState* jvms, uint idx) const { + assert(verify_jvms(jvms), "jvms must match"); + return in(jvms->monitor_obj_offset(idx)); + } + + void set_local(JVMState* jvms, uint idx, Node *c); + + void set_stack(JVMState* jvms, uint idx, Node *c) { + assert(verify_jvms(jvms), "jvms must match"); + set_req(jvms->stkoff() + idx, c); + } + void set_argument(JVMState* jvms, uint idx, Node *c) { + assert(verify_jvms(jvms), "jvms must match"); + set_req(jvms->argoff() + idx, c); + } + void ensure_stack(JVMState* jvms, uint stk_size) { + assert(verify_jvms(jvms), "jvms must match"); + int grow_by = (int)stk_size - (int)jvms->stk_size(); + if (grow_by > 0) grow_stack(jvms, grow_by); + } + void grow_stack(JVMState* jvms, uint grow_by); + // Handle monitor stack + void push_monitor( const FastLockNode *lock ); + void pop_monitor (); + Node *peek_monitor_box() const; + Node *peek_monitor_obj() const; + + // Access functions for the JVM + Node *control () const { return in(TypeFunc::Control ); } + Node *i_o () const { return in(TypeFunc::I_O ); } + Node *memory () const { return in(TypeFunc::Memory ); } + Node *returnadr() const { return in(TypeFunc::ReturnAdr); } + Node *frameptr () const { return in(TypeFunc::FramePtr ); } + + void set_control ( Node *c ) { set_req(TypeFunc::Control,c); } + void set_i_o ( Node *c ) { set_req(TypeFunc::I_O ,c); } + void set_memory ( Node *c ) { set_req(TypeFunc::Memory ,c); } + + MergeMemNode* merged_memory() const { + return in(TypeFunc::Memory)->as_MergeMem(); + } + + // The parser marks useless maps as dead when it's done with them: + bool is_killed() { return in(TypeFunc::Control) == NULL; } + + // Exception states bubbling out of subgraphs such as inlined calls + // are recorded here. (There might be more than one, hence the "next".) + // This feature is used only for safepoints which serve as "maps" + // for JVM states during parsing, intrinsic expansion, etc. + SafePointNode* next_exception() const; + void set_next_exception(SafePointNode* n); + bool has_exceptions() const { return next_exception() != NULL; } + + // Standard Node stuff + virtual int Opcode() const; + virtual bool pinned() const { return true; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const { return Type::CONTROL; } + virtual const TypePtr *adr_type() const { return _adr_type; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node *Identity( PhaseTransform *phase ); + virtual uint ideal_reg() const { return 0; } + virtual const RegMask &in_RegMask(uint) const; + virtual const RegMask &out_RegMask() const; + virtual uint match_edge(uint idx) const; + + static bool needs_polling_address_input(); + +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------CallNode--------------------------------------- +// Call nodes now subsume the function of debug nodes at callsites, so they +// contain the functionality of a full scope chain of debug nodes. +class CallNode : public SafePointNode { +public: + const TypeFunc *_tf; // Function type + address _entry_point; // Address of method being called + float _cnt; // Estimate of number of times called + PointsToNode::EscapeState _escape_state; + + CallNode(const TypeFunc* tf, address addr, const TypePtr* adr_type) + : SafePointNode(tf->domain()->cnt(), NULL, adr_type), + _tf(tf), + _entry_point(addr), + _cnt(COUNT_UNKNOWN) + { + init_class_id(Class_Call); + init_flags(Flag_is_Call); + _escape_state = PointsToNode::UnknownEscape; + } + + const TypeFunc* tf() const { return _tf; } + const address entry_point() const { return _entry_point; } + const float cnt() const { return _cnt; } + + void set_tf(const TypeFunc* tf) { _tf = tf; } + void set_entry_point(address p) { _entry_point = p; } + void set_cnt(float c) { _cnt = c; } + + virtual const Type *bottom_type() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ) { return this; } + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const = 0; + virtual void calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const; + virtual Node *match( const ProjNode *proj, const Matcher *m ); + virtual uint ideal_reg() const { return NotAMachineReg; } + // Are we guaranteed that this node is a safepoint? Not true for leaf calls and + // for some macro nodes whose expansion does not have a safepoint on the fast path. + virtual bool guaranteed_safepoint() { return true; } + // For macro nodes, the JVMState gets modified during expansion, so when cloning + // the node the JVMState must be cloned. + virtual void clone_jvms() { } // default is not to clone + + virtual uint match_edge(uint idx) const; + +#ifndef PRODUCT + virtual void dump_req() const; + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------CallJavaNode----------------------------------- +// Make a static or dynamic subroutine call node using Java calling +// convention. (The "Java" calling convention is the compiler's calling +// convention, as opposed to the interpreter's or that of native C.) +class CallJavaNode : public CallNode { +protected: + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger + + bool _optimized_virtual; + ciMethod* _method; // Method being direct called +public: + const int _bci; // Byte Code Index of call byte code + CallJavaNode(const TypeFunc* tf , address addr, ciMethod* method, int bci) + : CallNode(tf, addr, TypePtr::BOTTOM), + _method(method), _bci(bci), _optimized_virtual(false) + { + init_class_id(Class_CallJava); + } + + virtual int Opcode() const; + ciMethod* method() const { return _method; } + void set_method(ciMethod *m) { _method = m; } + void set_optimized_virtual(bool f) { _optimized_virtual = f; } + bool is_optimized_virtual() const { return _optimized_virtual; } + +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------CallStaticJavaNode----------------------------- +// Make a direct subroutine call using Java calling convention (for static +// calls and optimized virtual calls, plus calls to wrappers for run-time +// routines); generates static stub. +class CallStaticJavaNode : public CallJavaNode { + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger +public: + CallStaticJavaNode(const TypeFunc* tf, address addr, ciMethod* method, int bci) + : CallJavaNode(tf, addr, method, bci), _name(NULL) { + init_class_id(Class_CallStaticJava); + } + CallStaticJavaNode(const TypeFunc* tf, address addr, const char* name, int bci, + const TypePtr* adr_type) + : CallJavaNode(tf, addr, NULL, bci), _name(name) { + init_class_id(Class_CallStaticJava); + // This node calls a runtime stub, which often has narrow memory effects. + _adr_type = adr_type; + } + const char *_name; // Runtime wrapper name + + // If this is an uncommon trap, return the request code, else zero. + int uncommon_trap_request() const; + static int extract_uncommon_trap_request(const Node* call); + + virtual int Opcode() const; +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------CallDynamicJavaNode---------------------------- +// Make a dispatched call using Java calling convention. +class CallDynamicJavaNode : public CallJavaNode { + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger +public: + CallDynamicJavaNode( const TypeFunc *tf , address addr, ciMethod* method, int vtable_index, int bci ) : CallJavaNode(tf,addr,method,bci), _vtable_index(vtable_index) { + init_class_id(Class_CallDynamicJava); + } + + int _vtable_index; + virtual int Opcode() const; +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------CallRuntimeNode-------------------------------- +// Make a direct subroutine call node into compiled C++ code. +class CallRuntimeNode : public CallNode { + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger +public: + CallRuntimeNode(const TypeFunc* tf, address addr, const char* name, + const TypePtr* adr_type) + : CallNode(tf, addr, adr_type), + _name(name) + { + init_class_id(Class_CallRuntime); + } + + const char *_name; // Printable name, if _method is NULL + virtual int Opcode() const; + virtual void calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const; + +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------CallLeafNode----------------------------------- +// Make a direct subroutine call node into compiled C++ code, without +// safepoints +class CallLeafNode : public CallRuntimeNode { +public: + CallLeafNode(const TypeFunc* tf, address addr, const char* name, + const TypePtr* adr_type) + : CallRuntimeNode(tf, addr, name, adr_type) + { + init_class_id(Class_CallLeaf); + } + virtual int Opcode() const; + virtual bool guaranteed_safepoint() { return false; } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------CallLeafNoFPNode------------------------------- +// CallLeafNode, not using floating point or using it in the same manner as +// the generated code +class CallLeafNoFPNode : public CallLeafNode { +public: + CallLeafNoFPNode(const TypeFunc* tf, address addr, const char* name, + const TypePtr* adr_type) + : CallLeafNode(tf, addr, name, adr_type) + { + } + virtual int Opcode() const; +}; + + +//------------------------------Allocate--------------------------------------- +// High-level memory allocation +// +// AllocateNode and AllocateArrayNode are subclasses of CallNode because they will +// get expanded into a code sequence containing a call. Unlike other CallNodes, +// they have 2 memory projections and 2 i_o projections (which are distinguished by +// the _is_io_use flag in the projection.) This is needed when expanding the node in +// order to differentiate the uses of the projection on the normal control path from +// those on the exception return path. +// +class AllocateNode : public CallNode { +public: + enum { + // Output: + RawAddress = TypeFunc::Parms, // the newly-allocated raw address + // Inputs: + AllocSize = TypeFunc::Parms, // size (in bytes) of the new object + KlassNode, // type (maybe dynamic) of the obj. + InitialTest, // slow-path test (may be constant) + ALength, // array length (or TOP if none) + ParmLimit + }; + + static const TypeFunc* alloc_type() { + const Type** fields = TypeTuple::fields(ParmLimit - TypeFunc::Parms); + fields[AllocSize] = TypeInt::POS; + fields[KlassNode] = TypeInstPtr::NOTNULL; + fields[InitialTest] = TypeInt::BOOL; + fields[ALength] = TypeInt::INT; // length (can be a bad length) + + const TypeTuple *domain = TypeTuple::make(ParmLimit, fields); + + // create result type (range) + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeRawPtr::NOTNULL; // Returned oop + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); + + return TypeFunc::make(domain, range); + } + + virtual uint size_of() const; // Size is bigger + AllocateNode(Compile* C, const TypeFunc *atype, Node *ctrl, Node *mem, Node *abio, + Node *size, Node *klass_node, Node *initial_test); + // Expansion modifies the JVMState, so we need to clone it + virtual void clone_jvms() { + set_jvms(jvms()->clone_deep(Compile::current())); + } + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegP; } + virtual bool guaranteed_safepoint() { return false; } + + // Pattern-match a possible usage of AllocateNode. + // Return null if no allocation is recognized. + // The operand is the pointer produced by the (possible) allocation. + // It must be a projection of the Allocate or its subsequent CastPP. + // (Note: This function is defined in file graphKit.cpp, near + // GraphKit::new_instance/new_array, whose output it recognizes.) + // The 'ptr' may not have an offset unless the 'offset' argument is given. + static AllocateNode* Ideal_allocation(Node* ptr, PhaseTransform* phase); + + // Fancy version which uses AddPNode::Ideal_base_and_offset to strip + // an offset, which is reported back to the caller. + // (Note: AllocateNode::Ideal_allocation is defined in graphKit.cpp.) + static AllocateNode* Ideal_allocation(Node* ptr, PhaseTransform* phase, + intptr_t& offset); + + // Dig the klass operand out of a (possible) allocation site. + static Node* Ideal_klass(Node* ptr, PhaseTransform* phase) { + AllocateNode* allo = Ideal_allocation(ptr, phase); + return (allo == NULL) ? NULL : allo->in(KlassNode); + } + + // Conservatively small estimate of offset of first non-header byte. + int minimum_header_size() { + return is_AllocateArray() ? sizeof(arrayOopDesc) : sizeof(oopDesc); + } + + // Return the corresponding initialization barrier (or null if none). + // Walks out edges to find it... + // (Note: Both InitializeNode::allocation and AllocateNode::initialization + // are defined in graphKit.cpp, which sets up the bidirectional relation.) + InitializeNode* initialization(); + + // Convenience for initialization->maybe_set_complete(phase) + bool maybe_set_complete(PhaseGVN* phase); +}; + +//------------------------------AllocateArray--------------------------------- +// +// High-level array allocation +// +class AllocateArrayNode : public AllocateNode { +public: + AllocateArrayNode(Compile* C, const TypeFunc *atype, Node *ctrl, Node *mem, Node *abio, + Node* size, Node* klass_node, Node* initial_test, + Node* count_val + ) + : AllocateNode(C, atype, ctrl, mem, abio, size, klass_node, + initial_test) + { + init_class_id(Class_AllocateArray); + set_req(AllocateNode::ALength, count_val); + } + virtual int Opcode() const; + virtual uint size_of() const; // Size is bigger + + // Pattern-match a possible usage of AllocateArrayNode. + // Return null if no allocation is recognized. + static AllocateArrayNode* Ideal_array_allocation(Node* ptr, PhaseTransform* phase) { + AllocateNode* allo = Ideal_allocation(ptr, phase); + return (allo == NULL || !allo->is_AllocateArray()) + ? NULL : allo->as_AllocateArray(); + } + + // Dig the length operand out of a (possible) array allocation site. + static Node* Ideal_length(Node* ptr, PhaseTransform* phase) { + AllocateArrayNode* allo = Ideal_array_allocation(ptr, phase); + return (allo == NULL) ? NULL : allo->in(AllocateNode::ALength); + } +}; + +//------------------------------AbstractLockNode----------------------------------- +class AbstractLockNode: public CallNode { +private: + bool _eliminate; // indicates this lock can be safely eliminated +#ifndef PRODUCT + NamedCounter* _counter; +#endif + +protected: + // helper functions for lock elimination + // + + bool find_matching_unlock(const Node* ctrl, LockNode* lock, + GrowableArray &lock_ops); + bool find_lock_and_unlock_through_if(Node* node, LockNode* lock, + GrowableArray &lock_ops); + bool find_unlocks_for_region(const RegionNode* region, LockNode* lock, + GrowableArray &lock_ops); + LockNode *find_matching_lock(UnlockNode* unlock); + + +public: + AbstractLockNode(const TypeFunc *tf) + : CallNode(tf, NULL, TypeRawPtr::BOTTOM), + _eliminate(false) + { +#ifndef PRODUCT + _counter = NULL; +#endif + } + virtual int Opcode() const = 0; + Node * obj_node() const {return in(TypeFunc::Parms + 0); } + Node * box_node() const {return in(TypeFunc::Parms + 1); } + Node * fastlock_node() const {return in(TypeFunc::Parms + 2); } + const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;} + + virtual uint size_of() const { return sizeof(*this); } + + bool is_eliminated() {return _eliminate; } + // mark node as eliminated and update the counter if there is one + void set_eliminated(); + +#ifndef PRODUCT + void create_lock_counter(JVMState* s); + NamedCounter* counter() const { return _counter; } +#endif +}; + +//------------------------------Lock--------------------------------------- +// High-level lock operation +// +// This is a subclass of CallNode because it is a macro node which gets expanded +// into a code sequence containing a call. This node takes 3 "parameters": +// 0 - object to lock +// 1 - a BoxLockNode +// 2 - a FastLockNode +// +class LockNode : public AbstractLockNode { +public: + + static const TypeFunc *lock_type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(3); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // Object to be Locked + fields[TypeFunc::Parms+1] = TypeRawPtr::BOTTOM; // Address of stack location for lock + fields[TypeFunc::Parms+2] = TypeInt::BOOL; // FastLock + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+3,fields); + + // create result type (range) + fields = TypeTuple::fields(0); + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields); + + return TypeFunc::make(domain,range); + } + + virtual int Opcode() const; + virtual uint size_of() const; // Size is bigger + LockNode(Compile* C, const TypeFunc *tf) : AbstractLockNode( tf ) { + init_class_id(Class_Lock); + init_flags(Flag_is_macro); + C->add_macro_node(this); + } + virtual bool guaranteed_safepoint() { return false; } + + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + // Expansion modifies the JVMState, so we need to clone it + virtual void clone_jvms() { + set_jvms(jvms()->clone_deep(Compile::current())); + } +}; + +//------------------------------Unlock--------------------------------------- +// High-level unlock operation +class UnlockNode : public AbstractLockNode { +public: + virtual int Opcode() const; + virtual uint size_of() const; // Size is bigger + UnlockNode(Compile* C, const TypeFunc *tf) : AbstractLockNode( tf ) { + init_class_id(Class_Unlock); + init_flags(Flag_is_macro); + C->add_macro_node(this); + } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + // unlock is never a safepoint + virtual bool guaranteed_safepoint() { return false; } +}; diff --git a/hotspot/src/share/vm/opto/cfgnode.cpp b/hotspot/src/share/vm/opto/cfgnode.cpp new file mode 100644 index 00000000000..1c91c6be809 --- /dev/null +++ b/hotspot/src/share/vm/opto/cfgnode.cpp @@ -0,0 +1,1954 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_cfgnode.cpp.incl" + +//============================================================================= +//------------------------------Value------------------------------------------ +// Compute the type of the RegionNode. +const Type *RegionNode::Value( PhaseTransform *phase ) const { + for( uint i=1; itype(n) == Type::CONTROL ) + return Type::CONTROL; + } + return Type::TOP; // All paths dead? Then so are we +} + +//------------------------------Identity--------------------------------------- +// Check for Region being Identity. +Node *RegionNode::Identity( PhaseTransform *phase ) { + // Cannot have Region be an identity, even if it has only 1 input. + // Phi users cannot have their Region input folded away for them, + // since they need to select the proper data input + return this; +} + +//------------------------------merge_region----------------------------------- +// If a Region flows into a Region, merge into one big happy merge. This is +// hard to do if there is stuff that has to happen +static Node *merge_region(RegionNode *region, PhaseGVN *phase) { + if( region->Opcode() != Op_Region ) // Do not do to LoopNodes + return NULL; + Node *progress = NULL; // Progress flag + PhaseIterGVN *igvn = phase->is_IterGVN(); + + uint rreq = region->req(); + for( uint i = 1; i < rreq; i++ ) { + Node *r = region->in(i); + if( r && r->Opcode() == Op_Region && // Found a region? + r->in(0) == r && // Not already collapsed? + r != region && // Avoid stupid situations + r->outcnt() == 2 ) { // Self user and 'region' user only? + assert(!r->as_Region()->has_phi(), "no phi users"); + if( !progress ) { // No progress + if (region->has_phi()) { + return NULL; // Only flatten if no Phi users + // igvn->hash_delete( phi ); + } + igvn->hash_delete( region ); + progress = region; // Making progress + } + igvn->hash_delete( r ); + + // Append inputs to 'r' onto 'region' + for( uint j = 1; j < r->req(); j++ ) { + // Move an input from 'r' to 'region' + region->add_req(r->in(j)); + r->set_req(j, phase->C->top()); + // Update phis of 'region' + //for( uint k = 0; k < max; k++ ) { + // Node *phi = region->out(k); + // if( phi->is_Phi() ) { + // phi->add_req(phi->in(i)); + // } + //} + + rreq++; // One more input to Region + } // Found a region to merge into Region + // Clobber pointer to the now dead 'r' + region->set_req(i, phase->C->top()); + } + } + + return progress; +} + + + +//--------------------------------has_phi-------------------------------------- +// Helper function: Return any PhiNode that uses this region or NULL +PhiNode* RegionNode::has_phi() const { + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { + Node* phi = fast_out(i); + if (phi->is_Phi()) { // Check for Phi users + assert(phi->in(0) == (Node*)this, "phi uses region only via in(0)"); + return phi->as_Phi(); // this one is good enough + } + } + + return NULL; +} + + +//-----------------------------has_unique_phi---------------------------------- +// Helper function: Return the only PhiNode that uses this region or NULL +PhiNode* RegionNode::has_unique_phi() const { + // Check that only one use is a Phi + PhiNode* only_phi = NULL; + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { + Node* phi = fast_out(i); + if (phi->is_Phi()) { // Check for Phi users + assert(phi->in(0) == (Node*)this, "phi uses region only via in(0)"); + if (only_phi == NULL) { + only_phi = phi->as_Phi(); + } else { + return NULL; // multiple phis + } + } + } + + return only_phi; +} + + +//------------------------------check_phi_clipping----------------------------- +// Helper function for RegionNode's identification of FP clipping +// Check inputs to the Phi +static bool check_phi_clipping( PhiNode *phi, ConNode * &min, uint &min_idx, ConNode * &max, uint &max_idx, Node * &val, uint &val_idx ) { + min = NULL; + max = NULL; + val = NULL; + min_idx = 0; + max_idx = 0; + val_idx = 0; + uint phi_max = phi->req(); + if( phi_max == 4 ) { + for( uint j = 1; j < phi_max; ++j ) { + Node *n = phi->in(j); + int opcode = n->Opcode(); + switch( opcode ) { + case Op_ConI: + { + if( min == NULL ) { + min = n->Opcode() == Op_ConI ? (ConNode*)n : NULL; + min_idx = j; + } else { + max = n->Opcode() == Op_ConI ? (ConNode*)n : NULL; + max_idx = j; + if( min->get_int() > max->get_int() ) { + // Swap min and max + ConNode *temp; + uint temp_idx; + temp = min; min = max; max = temp; + temp_idx = min_idx; min_idx = max_idx; max_idx = temp_idx; + } + } + } + break; + default: + { + val = n; + val_idx = j; + } + break; + } + } + } + return ( min && max && val && (min->get_int() <= 0) && (max->get_int() >=0) ); +} + + +//------------------------------check_if_clipping------------------------------ +// Helper function for RegionNode's identification of FP clipping +// Check that inputs to Region come from two IfNodes, +// +// If +// False True +// If | +// False True | +// | | | +// RegionNode_inputs +// +static bool check_if_clipping( const RegionNode *region, IfNode * &bot_if, IfNode * &top_if ) { + top_if = NULL; + bot_if = NULL; + + // Check control structure above RegionNode for (if ( if ) ) + Node *in1 = region->in(1); + Node *in2 = region->in(2); + Node *in3 = region->in(3); + // Check that all inputs are projections + if( in1->is_Proj() && in2->is_Proj() && in3->is_Proj() ) { + Node *in10 = in1->in(0); + Node *in20 = in2->in(0); + Node *in30 = in3->in(0); + // Check that #1 and #2 are ifTrue and ifFalse from same If + if( in10 != NULL && in10->is_If() && + in20 != NULL && in20->is_If() && + in30 != NULL && in30->is_If() && in10 == in20 && + (in1->Opcode() != in2->Opcode()) ) { + Node *in100 = in10->in(0); + Node *in1000 = (in100 != NULL && in100->is_Proj()) ? in100->in(0) : NULL; + // Check that control for in10 comes from other branch of IF from in3 + if( in1000 != NULL && in1000->is_If() && + in30 == in1000 && (in3->Opcode() != in100->Opcode()) ) { + // Control pattern checks + top_if = (IfNode*)in1000; + bot_if = (IfNode*)in10; + } + } + } + + return (top_if != NULL); +} + + +//------------------------------check_convf2i_clipping------------------------- +// Helper function for RegionNode's identification of FP clipping +// Verify that the value input to the phi comes from "ConvF2I; LShift; RShift" +static bool check_convf2i_clipping( PhiNode *phi, uint idx, ConvF2INode * &convf2i, Node *min, Node *max) { + convf2i = NULL; + + // Check for the RShiftNode + Node *rshift = phi->in(idx); + assert( rshift, "Previous checks ensure phi input is present"); + if( rshift->Opcode() != Op_RShiftI ) { return false; } + + // Check for the LShiftNode + Node *lshift = rshift->in(1); + assert( lshift, "Previous checks ensure phi input is present"); + if( lshift->Opcode() != Op_LShiftI ) { return false; } + + // Check for the ConvF2INode + Node *conv = lshift->in(1); + if( conv->Opcode() != Op_ConvF2I ) { return false; } + + // Check that shift amounts are only to get sign bits set after F2I + jint max_cutoff = max->get_int(); + jint min_cutoff = min->get_int(); + jint left_shift = lshift->in(2)->get_int(); + jint right_shift = rshift->in(2)->get_int(); + jint max_post_shift = nth_bit(BitsPerJavaInteger - left_shift - 1); + if( left_shift != right_shift || + 0 > left_shift || left_shift >= BitsPerJavaInteger || + max_post_shift < max_cutoff || + max_post_shift < -min_cutoff ) { + // Shifts are necessary but current transformation eliminates them + return false; + } + + // OK to return the result of ConvF2I without shifting + convf2i = (ConvF2INode*)conv; + return true; +} + + +//------------------------------check_compare_clipping------------------------- +// Helper function for RegionNode's identification of FP clipping +static bool check_compare_clipping( bool less_than, IfNode *iff, ConNode *limit, Node * & input ) { + Node *i1 = iff->in(1); + if ( !i1->is_Bool() ) { return false; } + BoolNode *bool1 = i1->as_Bool(); + if( less_than && bool1->_test._test != BoolTest::le ) { return false; } + else if( !less_than && bool1->_test._test != BoolTest::lt ) { return false; } + const Node *cmpF = bool1->in(1); + if( cmpF->Opcode() != Op_CmpF ) { return false; } + // Test that the float value being compared against + // is equivalent to the int value used as a limit + Node *nodef = cmpF->in(2); + if( nodef->Opcode() != Op_ConF ) { return false; } + jfloat conf = nodef->getf(); + jint coni = limit->get_int(); + if( ((int)conf) != coni ) { return false; } + input = cmpF->in(1); + return true; +} + +//------------------------------is_unreachable_region-------------------------- +// Find if the Region node is reachable from the root. +bool RegionNode::is_unreachable_region(PhaseGVN *phase) const { + assert(req() == 2, ""); + + // First, cut the simple case of fallthrough region when NONE of + // region's phis references itself directly or through a data node. + uint max = outcnt(); + uint i; + for (i = 0; i < max; i++) { + Node* phi = raw_out(i); + if (phi != NULL && phi->is_Phi()) { + assert(phase->eqv(phi->in(0), this) && phi->req() == 2, ""); + if (phi->outcnt() == 0) + continue; // Safe case - no loops + if (phi->outcnt() == 1) { + Node* u = phi->raw_out(0); + // Skip if only one use is an other Phi or Call or Uncommon trap. + // It is safe to consider this case as fallthrough. + if (u != NULL && (u->is_Phi() || u->is_CFG())) + continue; + } + // Check when phi references itself directly or through an other node. + if (phi->as_Phi()->simple_data_loop_check(phi->in(1)) >= PhiNode::Unsafe) + break; // Found possible unsafe data loop. + } + } + if (i >= max) + return false; // An unsafe case was NOT found - don't need graph walk. + + // Unsafe case - check if the Region node is reachable from root. + ResourceMark rm; + + Arena *a = Thread::current()->resource_area(); + Node_List nstack(a); + VectorSet visited(a); + + // Mark all control nodes reachable from root outputs + Node *n = (Node*)phase->C->root(); + nstack.push(n); + visited.set(n->_idx); + while (nstack.size() != 0) { + n = nstack.pop(); + uint max = n->outcnt(); + for (uint i = 0; i < max; i++) { + Node* m = n->raw_out(i); + if (m != NULL && m->is_CFG()) { + if (phase->eqv(m, this)) { + return false; // We reached the Region node - it is not dead. + } + if (!visited.test_set(m->_idx)) + nstack.push(m); + } + } + } + + return true; // The Region node is unreachable - it is dead. +} + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Must preserve +// the CFG, but we can still strip out dead paths. +Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if( !can_reshape && !in(0) ) return NULL; // Already degraded to a Copy + assert(!in(0) || !in(0)->is_Root(), "not a specially hidden merge"); + + // Check for RegionNode with no Phi users and both inputs come from either + // arm of the same IF. If found, then the control-flow split is useless. + bool has_phis = false; + if (can_reshape) { // Need DU info to check for Phi users + has_phis = (has_phi() != NULL); // Cache result + if (!has_phis) { // No Phi users? Nothing merging? + for (uint i = 1; i < req()-1; i++) { + Node *if1 = in(i); + if( !if1 ) continue; + Node *iff = if1->in(0); + if( !iff || !iff->is_If() ) continue; + for( uint j=i+1; jin(0) == iff && + if1->Opcode() != in(j)->Opcode() ) { + // Add the IF Projections to the worklist. They (and the IF itself) + // will be eliminated if dead. + phase->is_IterGVN()->add_users_to_worklist(iff); + set_req(i, iff->in(0));// Skip around the useless IF diamond + set_req(j, NULL); + return this; // Record progress + } + } + } + } + } + + // Remove TOP or NULL input paths. If only 1 input path remains, this Region + // degrades to a copy. + bool add_to_worklist = false; + int cnt = 0; // Count of values merging + DEBUG_ONLY( int cnt_orig = req(); ) // Save original inputs count + int del_it = 0; // The last input path we delete + // For all inputs... + for( uint i=1; iis_Region() && n->as_Region()->is_copy() ) { + set_req(i, n->nonnull_req()); + i--; + continue; + } + if( n->is_Proj() ) { // Remove useless rethrows + Node *call = n->in(0); + if (call->is_Call() && call->as_Call()->entry_point() == OptoRuntime::rethrow_stub()) { + set_req(i, call->in(0)); + i--; + continue; + } + } + if( phase->type(n) == Type::TOP ) { + set_req(i, NULL); // Ignore TOP inputs + i--; + continue; + } + cnt++; // One more value merging + + } else if (can_reshape) { // Else found dead path with DU info + PhaseIterGVN *igvn = phase->is_IterGVN(); + del_req(i); // Yank path from self + del_it = i; + uint max = outcnt(); + DUIterator j; + bool progress = true; + while(progress) { // Need to establish property over all users + progress = false; + for (j = outs(); has_out(j); j++) { + Node *n = out(j); + if( n->req() != req() && n->is_Phi() ) { + assert( n->in(0) == this, "" ); + igvn->hash_delete(n); // Yank from hash before hacking edges + n->set_req_X(i,NULL,igvn);// Correct DU info + n->del_req(i); // Yank path from Phis + if( max != outcnt() ) { + progress = true; + j = refresh_out_pos(j); + max = outcnt(); + } + } + } + } + add_to_worklist = true; + i--; + } + } + + if (can_reshape && cnt == 1) { + // Is it dead loop? + // If it is LoopNopde it had 2 (+1 itself) inputs and + // one of them was cut. The loop is dead if it was EntryContol. + assert(!this->is_Loop() || cnt_orig == 3, "Loop node should have 3 inputs"); + if (this->is_Loop() && del_it == LoopNode::EntryControl || + !this->is_Loop() && has_phis && is_unreachable_region(phase)) { + // Yes, the region will be removed during the next step below. + // Cut the backedge input and remove phis since no data paths left. + // We don't cut outputs to other nodes here since we need to put them + // on the worklist. + del_req(1); + cnt = 0; + assert( req() == 1, "no more inputs expected" ); + uint max = outcnt(); + bool progress = true; + Node *top = phase->C->top(); + PhaseIterGVN *igvn = phase->is_IterGVN(); + DUIterator j; + while(progress) { + progress = false; + for (j = outs(); has_out(j); j++) { + Node *n = out(j); + if( n->is_Phi() ) { + assert( igvn->eqv(n->in(0), this), "" ); + assert( n->req() == 2 && n->in(1) != NULL, "Only one data input expected" ); + // Break dead loop data path. + // Eagerly replace phis with top to avoid phis copies generation. + igvn->add_users_to_worklist(n); + igvn->hash_delete(n); // Yank from hash before hacking edges + igvn->subsume_node(n, top); + if( max != outcnt() ) { + progress = true; + j = refresh_out_pos(j); + max = outcnt(); + } + } + } + } + add_to_worklist = true; + } + } + if (add_to_worklist) { + phase->is_IterGVN()->add_users_to_worklist(this); // Revisit collapsed Phis + } + + if( cnt <= 1 ) { // Only 1 path in? + set_req(0, NULL); // Null control input for region copy + if( cnt == 0 && !can_reshape) { // Parse phase - leave the node as it is. + // No inputs or all inputs are NULL. + return NULL; + } else if (can_reshape) { // Optimization phase - remove the node + PhaseIterGVN *igvn = phase->is_IterGVN(); + Node *parent_ctrl; + if( cnt == 0 ) { + assert( req() == 1, "no inputs expected" ); + // During IGVN phase such region will be subsumed by TOP node + // so region's phis will have TOP as control node. + // Kill phis here to avoid it. PhiNode::is_copy() will be always false. + // Also set other user's input to top. + parent_ctrl = phase->C->top(); + } else { + // The fallthrough case since we already checked dead loops above. + parent_ctrl = in(1); + assert(parent_ctrl != NULL, "Region is a copy of some non-null control"); + assert(!igvn->eqv(parent_ctrl, this), "Close dead loop"); + } + if (!add_to_worklist) + igvn->add_users_to_worklist(this); // Check for further allowed opts + for (DUIterator_Last imin, i = last_outs(imin); i >= imin; --i) { + Node* n = last_out(i); + igvn->hash_delete(n); // Remove from worklist before modifying edges + if( n->is_Phi() ) { // Collapse all Phis + // Eagerly replace phis to avoid copies generation. + igvn->add_users_to_worklist(n); + igvn->hash_delete(n); // Yank from hash before hacking edges + if( cnt == 0 ) { + assert( n->req() == 1, "No data inputs expected" ); + igvn->subsume_node(n, parent_ctrl); // replaced by top + } else { + assert( n->req() == 2 && n->in(1) != NULL, "Only one data input expected" ); + Node* in1 = n->in(1); // replaced by unique input + if( n->as_Phi()->is_unsafe_data_reference(in1) ) + in1 = phase->C->top(); // replaced by top + igvn->subsume_node(n, in1); + } + } + else if( n->is_Region() ) { // Update all incoming edges + assert( !igvn->eqv(n, this), "Must be removed from DefUse edges"); + uint uses_found = 0; + for( uint k=1; k < n->req(); k++ ) { + if( n->in(k) == this ) { + n->set_req(k, parent_ctrl); + uses_found++; + } + } + if( uses_found > 1 ) { // (--i) done at the end of the loop. + i -= (uses_found - 1); + } + } + else { + assert( igvn->eqv(n->in(0), this), "Expect RegionNode to be control parent"); + n->set_req(0, parent_ctrl); + } +#ifdef ASSERT + for( uint k=0; k < n->req(); k++ ) { + assert( !igvn->eqv(n->in(k), this), "All uses of RegionNode should be gone"); + } +#endif + } + // Remove the RegionNode itself from DefUse info + igvn->remove_dead_node(this); + return NULL; + } + return this; // Record progress + } + + + // If a Region flows into a Region, merge into one big happy merge. + if (can_reshape) { + Node *m = merge_region(this, phase); + if (m != NULL) return m; + } + + // Check if this region is the root of a clipping idiom on floats + if( ConvertFloat2IntClipping && can_reshape && req() == 4 ) { + // Check that only one use is a Phi and that it simplifies to two constants + + PhiNode* phi = has_unique_phi(); + if (phi != NULL) { // One Phi user + // Check inputs to the Phi + ConNode *min; + ConNode *max; + Node *val; + uint min_idx; + uint max_idx; + uint val_idx; + if( check_phi_clipping( phi, min, min_idx, max, max_idx, val, val_idx ) ) { + IfNode *top_if; + IfNode *bot_if; + if( check_if_clipping( this, bot_if, top_if ) ) { + // Control pattern checks, now verify compares + Node *top_in = NULL; // value being compared against + Node *bot_in = NULL; + if( check_compare_clipping( true, bot_if, min, bot_in ) && + check_compare_clipping( false, top_if, max, top_in ) ) { + if( bot_in == top_in ) { + PhaseIterGVN *gvn = phase->is_IterGVN(); + assert( gvn != NULL, "Only had DefUse info in IterGVN"); + // Only remaining check is that bot_in == top_in == (Phi's val + mods) + + // Check for the ConvF2INode + ConvF2INode *convf2i; + if( check_convf2i_clipping( phi, val_idx, convf2i, min, max ) && + convf2i->in(1) == bot_in ) { + // Matched pattern, including LShiftI; RShiftI, replace with integer compares + // max test + Node *cmp = gvn->register_new_node_with_optimizer(new (phase->C, 3) CmpINode( convf2i, min )); + Node *boo = gvn->register_new_node_with_optimizer(new (phase->C, 2) BoolNode( cmp, BoolTest::lt )); + IfNode *iff = (IfNode*)gvn->register_new_node_with_optimizer(new (phase->C, 2) IfNode( top_if->in(0), boo, PROB_UNLIKELY_MAG(5), top_if->_fcnt )); + Node *if_min= gvn->register_new_node_with_optimizer(new (phase->C, 1) IfTrueNode (iff)); + Node *ifF = gvn->register_new_node_with_optimizer(new (phase->C, 1) IfFalseNode(iff)); + // min test + cmp = gvn->register_new_node_with_optimizer(new (phase->C, 3) CmpINode( convf2i, max )); + boo = gvn->register_new_node_with_optimizer(new (phase->C, 2) BoolNode( cmp, BoolTest::gt )); + iff = (IfNode*)gvn->register_new_node_with_optimizer(new (phase->C, 2) IfNode( ifF, boo, PROB_UNLIKELY_MAG(5), bot_if->_fcnt )); + Node *if_max= gvn->register_new_node_with_optimizer(new (phase->C, 1) IfTrueNode (iff)); + ifF = gvn->register_new_node_with_optimizer(new (phase->C, 1) IfFalseNode(iff)); + // update input edges to region node + set_req_X( min_idx, if_min, gvn ); + set_req_X( max_idx, if_max, gvn ); + set_req_X( val_idx, ifF, gvn ); + // remove unnecessary 'LShiftI; RShiftI' idiom + gvn->hash_delete(phi); + phi->set_req_X( val_idx, convf2i, gvn ); + gvn->hash_find_insert(phi); + // Return transformed region node + return this; + } + } + } + } + } + } + } + + return NULL; +} + + + +const RegMask &RegionNode::out_RegMask() const { + return RegMask::Empty; +} + +// Find the one non-null required input. RegionNode only +Node *Node::nonnull_req() const { + assert( is_Region(), "" ); + for( uint i = 1; i < _cnt; i++ ) + if( in(i) ) + return in(i); + ShouldNotReachHere(); + return NULL; +} + + +//============================================================================= +// note that these functions assume that the _adr_type field is flattened +uint PhiNode::hash() const { + const Type* at = _adr_type; + return TypeNode::hash() + (at ? at->hash() : 0); +} +uint PhiNode::cmp( const Node &n ) const { + return TypeNode::cmp(n) && _adr_type == ((PhiNode&)n)._adr_type; +} +static inline +const TypePtr* flatten_phi_adr_type(const TypePtr* at) { + if (at == NULL || at == TypePtr::BOTTOM) return at; + return Compile::current()->alias_type(at)->adr_type(); +} + +//----------------------------make--------------------------------------------- +// create a new phi with edges matching r and set (initially) to x +PhiNode* PhiNode::make(Node* r, Node* x, const Type *t, const TypePtr* at) { + uint preds = r->req(); // Number of predecessor paths + assert(t != Type::MEMORY || at == flatten_phi_adr_type(at), "flatten at"); + PhiNode* p = new (Compile::current(), preds) PhiNode(r, t, at); + for (uint j = 1; j < preds; j++) { + // Fill in all inputs, except those which the region does not yet have + if (r->in(j) != NULL) + p->init_req(j, x); + } + return p; +} +PhiNode* PhiNode::make(Node* r, Node* x) { + const Type* t = x->bottom_type(); + const TypePtr* at = NULL; + if (t == Type::MEMORY) at = flatten_phi_adr_type(x->adr_type()); + return make(r, x, t, at); +} +PhiNode* PhiNode::make_blank(Node* r, Node* x) { + const Type* t = x->bottom_type(); + const TypePtr* at = NULL; + if (t == Type::MEMORY) at = flatten_phi_adr_type(x->adr_type()); + return new (Compile::current(), r->req()) PhiNode(r, t, at); +} + + +//------------------------slice_memory----------------------------------------- +// create a new phi with narrowed memory type +PhiNode* PhiNode::slice_memory(const TypePtr* adr_type) const { + PhiNode* mem = (PhiNode*) clone(); + *(const TypePtr**)&mem->_adr_type = adr_type; + // convert self-loops, or else we get a bad graph + for (uint i = 1; i < req(); i++) { + if ((const Node*)in(i) == this) mem->set_req(i, mem); + } + mem->verify_adr_type(); + return mem; +} + +//------------------------verify_adr_type-------------------------------------- +#ifdef ASSERT +void PhiNode::verify_adr_type(VectorSet& visited, const TypePtr* at) const { + if (visited.test_set(_idx)) return; //already visited + + // recheck constructor invariants: + verify_adr_type(false); + + // recheck local phi/phi consistency: + assert(_adr_type == at || _adr_type == TypePtr::BOTTOM, + "adr_type must be consistent across phi nest"); + + // walk around + for (uint i = 1; i < req(); i++) { + Node* n = in(i); + if (n == NULL) continue; + const Node* np = in(i); + if (np->is_Phi()) { + np->as_Phi()->verify_adr_type(visited, at); + } else if (n->bottom_type() == Type::TOP + || (n->is_Mem() && n->in(MemNode::Address)->bottom_type() == Type::TOP)) { + // ignore top inputs + } else { + const TypePtr* nat = flatten_phi_adr_type(n->adr_type()); + // recheck phi/non-phi consistency at leaves: + assert((nat != NULL) == (at != NULL), ""); + assert(nat == at || nat == TypePtr::BOTTOM, + "adr_type must be consistent at leaves of phi nest"); + } + } +} + +// Verify a whole nest of phis rooted at this one. +void PhiNode::verify_adr_type(bool recursive) const { + if (is_error_reported()) return; // muzzle asserts when debugging an error + if (Node::in_dump()) return; // muzzle asserts when printing + + assert((_type == Type::MEMORY) == (_adr_type != NULL), "adr_type for memory phis only"); + + if (!VerifyAliases) return; // verify thoroughly only if requested + + assert(_adr_type == flatten_phi_adr_type(_adr_type), + "Phi::adr_type must be pre-normalized"); + + if (recursive) { + VectorSet visited(Thread::current()->resource_area()); + verify_adr_type(visited, _adr_type); + } +} +#endif + + +//------------------------------Value------------------------------------------ +// Compute the type of the PhiNode +const Type *PhiNode::Value( PhaseTransform *phase ) const { + Node *r = in(0); // RegionNode + if( !r ) // Copy or dead + return in(1) ? phase->type(in(1)) : Type::TOP; + + // Note: During parsing, phis are often transformed before their regions. + // This means we have to use type_or_null to defend against untyped regions. + if( phase->type_or_null(r) == Type::TOP ) // Dead code? + return Type::TOP; + + // Check for trip-counted loop. If so, be smarter. + CountedLoopNode *l = r->is_CountedLoop() ? r->as_CountedLoop() : NULL; + if( l && l->can_be_counted_loop(phase) && + ((const Node*)l->phi() == this) ) { // Trip counted loop! + // protect against init_trip() or limit() returning NULL + const Node *init = l->init_trip(); + const Node *limit = l->limit(); + if( init != NULL && limit != NULL && l->stride_is_con() ) { + const TypeInt *lo = init ->bottom_type()->isa_int(); + const TypeInt *hi = limit->bottom_type()->isa_int(); + if( lo && hi ) { // Dying loops might have TOP here + int stride = l->stride_con(); + if( stride < 0 ) { // Down-counter loop + const TypeInt *tmp = lo; lo = hi; hi = tmp; + stride = -stride; + } + if( lo->_hi < hi->_lo ) // Reversed endpoints are well defined :-( + return TypeInt::make(lo->_lo,hi->_hi,3); + } + } + } + + // Until we have harmony between classes and interfaces in the type + // lattice, we must tread carefully around phis which implicitly + // convert the one to the other. + const TypeInstPtr* ttip = _type->isa_instptr(); + bool is_intf = false; + if (ttip != NULL) { + ciKlass* k = ttip->klass(); + if (k->is_loaded() && k->is_interface()) + is_intf = true; + } + + // Default case: merge all inputs + const Type *t = Type::TOP; // Merged type starting value + for (uint i = 1; i < req(); ++i) {// For all paths in + // Reachable control path? + if (r->in(i) && phase->type(r->in(i)) == Type::CONTROL) { + const Type* ti = phase->type(in(i)); + // We assume that each input of an interface-valued Phi is a true + // subtype of that interface. This might not be true of the meet + // of all the input types. The lattice is not distributive in + // such cases. Ward off asserts in type.cpp by refusing to do + // meets between interfaces and proper classes. + const TypeInstPtr* tiip = ti->isa_instptr(); + if (tiip) { + bool ti_is_intf = false; + ciKlass* k = tiip->klass(); + if (k->is_loaded() && k->is_interface()) + ti_is_intf = true; + if (is_intf != ti_is_intf) + { t = _type; break; } + } + t = t->meet(ti); + } + } + + // The worst-case type (from ciTypeFlow) should be consistent with "t". + // That is, we expect that "t->higher_equal(_type)" holds true. + // There are various exceptions: + // - Inputs which are phis might in fact be widened unnecessarily. + // For example, an input might be a widened int while the phi is a short. + // - Inputs might be BotPtrs but this phi is dependent on a null check, + // and postCCP has removed the cast which encodes the result of the check. + // - The type of this phi is an interface, and the inputs are classes. + // - Value calls on inputs might produce fuzzy results. + // (Occurrences of this case suggest improvements to Value methods.) + // + // It is not possible to see Type::BOTTOM values as phi inputs, + // because the ciTypeFlow pre-pass produces verifier-quality types. + const Type* ft = t->filter(_type); // Worst case type + +#ifdef ASSERT + // The following logic has been moved into TypeOopPtr::filter. + const Type* jt = t->join(_type); + if( jt->empty() ) { // Emptied out??? + + // Check for evil case of 't' being a class and '_type' expecting an + // interface. This can happen because the bytecodes do not contain + // enough type info to distinguish a Java-level interface variable + // from a Java-level object variable. If we meet 2 classes which + // both implement interface I, but their meet is at 'j/l/O' which + // doesn't implement I, we have no way to tell if the result should + // be 'I' or 'j/l/O'. Thus we'll pick 'j/l/O'. If this then flows + // into a Phi which "knows" it's an Interface type we'll have to + // uplift the type. + if( !t->empty() && ttip && ttip->is_loaded() && ttip->klass()->is_interface() ) + { assert(ft == _type, ""); } // Uplift to interface + // Otherwise it's something stupid like non-overlapping int ranges + // found on dying counted loops. + else + { assert(ft == Type::TOP, ""); } // Canonical empty value + } + + else { + + // If we have an interface-typed Phi and we narrow to a class type, the join + // should report back the class. However, if we have a J/L/Object + // class-typed Phi and an interface flows in, it's possible that the meet & + // join report an interface back out. This isn't possible but happens + // because the type system doesn't interact well with interfaces. + const TypeInstPtr *jtip = jt->isa_instptr(); + if( jtip && ttip ) { + if( jtip->is_loaded() && jtip->klass()->is_interface() && + ttip->is_loaded() && !ttip->klass()->is_interface() ) + // Happens in a CTW of rt.jar, 320-341, no extra flags + { assert(ft == ttip->cast_to_ptr_type(jtip->ptr()), ""); jt = ft; } + } + if (jt != ft && jt->base() == ft->base()) { + if (jt->isa_int() && + jt->is_int()->_lo == ft->is_int()->_lo && + jt->is_int()->_hi == ft->is_int()->_hi) + jt = ft; + if (jt->isa_long() && + jt->is_long()->_lo == ft->is_long()->_lo && + jt->is_long()->_hi == ft->is_long()->_hi) + jt = ft; + } + if (jt != ft) { + tty->print("merge type: "); t->dump(); tty->cr(); + tty->print("kill type: "); _type->dump(); tty->cr(); + tty->print("join type: "); jt->dump(); tty->cr(); + tty->print("filter type: "); ft->dump(); tty->cr(); + } + assert(jt == ft, ""); + } +#endif //ASSERT + + // Deal with conversion problems found in data loops. + ft = phase->saturate(ft, phase->type_or_null(this), _type); + + return ft; +} + + +//------------------------------is_diamond_phi--------------------------------- +// Does this Phi represent a simple well-shaped diamond merge? Return the +// index of the true path or 0 otherwise. +int PhiNode::is_diamond_phi() const { + // Check for a 2-path merge + Node *region = in(0); + if( !region ) return 0; + if( region->req() != 3 ) return 0; + if( req() != 3 ) return 0; + // Check that both paths come from the same If + Node *ifp1 = region->in(1); + Node *ifp2 = region->in(2); + if( !ifp1 || !ifp2 ) return 0; + Node *iff = ifp1->in(0); + if( !iff || !iff->is_If() ) return 0; + if( iff != ifp2->in(0) ) return 0; + // Check for a proper bool/cmp + const Node *b = iff->in(1); + if( !b->is_Bool() ) return 0; + const Node *cmp = b->in(1); + if( !cmp->is_Cmp() ) return 0; + + // Check for branching opposite expected + if( ifp2->Opcode() == Op_IfTrue ) { + assert( ifp1->Opcode() == Op_IfFalse, "" ); + return 2; + } else { + assert( ifp1->Opcode() == Op_IfTrue, "" ); + return 1; + } +} + +//----------------------------check_cmove_id----------------------------------- +// Check for CMove'ing a constant after comparing against the constant. +// Happens all the time now, since if we compare equality vs a constant in +// the parser, we "know" the variable is constant on one path and we force +// it. Thus code like "if( x==0 ) {/*EMPTY*/}" ends up inserting a +// conditional move: "x = (x==0)?0:x;". Yucko. This fix is slightly more +// general in that we don't need constants. Since CMove's are only inserted +// in very special circumstances, we do it here on generic Phi's. +Node* PhiNode::is_cmove_id(PhaseTransform* phase, int true_path) { + assert(true_path !=0, "only diamond shape graph expected"); + + // is_diamond_phi() has guaranteed the correctness of the nodes sequence: + // phi->region->if_proj->ifnode->bool->cmp + Node* region = in(0); + Node* iff = region->in(1)->in(0); + BoolNode* b = iff->in(1)->as_Bool(); + Node* cmp = b->in(1); + Node* tval = in(true_path); + Node* fval = in(3-true_path); + Node* id = CMoveNode::is_cmove_id(phase, cmp, tval, fval, b); + if (id == NULL) + return NULL; + + // Either value might be a cast that depends on a branch of 'iff'. + // Since the 'id' value will float free of the diamond, either + // decast or return failure. + Node* ctl = id->in(0); + if (ctl != NULL && ctl->in(0) == iff) { + if (id->is_ConstraintCast()) { + return id->in(1); + } else { + // Don't know how to disentangle this value. + return NULL; + } + } + + return id; +} + +//------------------------------Identity--------------------------------------- +// Check for Region being Identity. +Node *PhiNode::Identity( PhaseTransform *phase ) { + // Check for no merging going on + // (There used to be special-case code here when this->region->is_Loop. + // It would check for a tributary phi on the backedge that the main phi + // trivially, perhaps with a single cast. The unique_input method + // does all this and more, by reducing such tributaries to 'this'.) + Node* uin = unique_input(phase); + if (uin != NULL) { + return uin; + } + + int true_path = is_diamond_phi(); + if (true_path != 0) { + Node* id = is_cmove_id(phase, true_path); + if (id != NULL) return id; + } + + return this; // No identity +} + +//-----------------------------unique_input------------------------------------ +// Find the unique value, discounting top, self-loops, and casts. +// Return top if there are no inputs, and self if there are multiple. +Node* PhiNode::unique_input(PhaseTransform* phase) { + // 1) One unique direct input, or + // 2) some of the inputs have an intervening ConstraintCast and + // the type of input is the same or sharper (more specific) + // than the phi's type. + // 3) an input is a self loop + // + // 1) input or 2) input or 3) input __ + // / \ / \ \ / \ + // \ / | cast phi cast + // phi \ / / \ / + // phi / -- + + Node* r = in(0); // RegionNode + if (r == NULL) return in(1); // Already degraded to a Copy + Node* uncasted_input = NULL; // The unique uncasted input (ConstraintCasts removed) + Node* direct_input = NULL; // The unique direct input + + for (uint i = 1, cnt = req(); i < cnt; ++i) { + Node* rc = r->in(i); + if (rc == NULL || phase->type(rc) == Type::TOP) + continue; // ignore unreachable control path + Node* n = in(i); + Node* un = n->uncast(); + if (un == NULL || un == this || phase->type(un) == Type::TOP) { + continue; // ignore if top, or in(i) and "this" are in a data cycle + } + // Check for a unique uncasted input + if (uncasted_input == NULL) { + uncasted_input = un; + } else if (uncasted_input != un) { + uncasted_input = NodeSentinel; // no unique uncasted input + } + // Check for a unique direct input + if (direct_input == NULL) { + direct_input = n; + } else if (direct_input != n) { + direct_input = NodeSentinel; // no unique direct input + } + } + if (direct_input == NULL) { + return phase->C->top(); // no inputs + } + assert(uncasted_input != NULL,""); + + if (direct_input != NodeSentinel) { + return direct_input; // one unique direct input + } + if (uncasted_input != NodeSentinel && + phase->type(uncasted_input)->higher_equal(type())) { + return uncasted_input; // one unique uncasted input + } + + // Nothing. + return NULL; +} + +//------------------------------is_x2logic------------------------------------- +// Check for simple convert-to-boolean pattern +// If:(C Bool) Region:(IfF IfT) Phi:(Region 0 1) +// Convert Phi to an ConvIB. +static Node *is_x2logic( PhaseGVN *phase, PhiNode *phi, int true_path ) { + assert(true_path !=0, "only diamond shape graph expected"); + // Convert the true/false index into an expected 0/1 return. + // Map 2->0 and 1->1. + int flipped = 2-true_path; + + // is_diamond_phi() has guaranteed the correctness of the nodes sequence: + // phi->region->if_proj->ifnode->bool->cmp + Node *region = phi->in(0); + Node *iff = region->in(1)->in(0); + BoolNode *b = (BoolNode*)iff->in(1); + const CmpNode *cmp = (CmpNode*)b->in(1); + + Node *zero = phi->in(1); + Node *one = phi->in(2); + const Type *tzero = phase->type( zero ); + const Type *tone = phase->type( one ); + + // Check for compare vs 0 + const Type *tcmp = phase->type(cmp->in(2)); + if( tcmp != TypeInt::ZERO && tcmp != TypePtr::NULL_PTR ) { + // Allow cmp-vs-1 if the other input is bounded by 0-1 + if( !(tcmp == TypeInt::ONE && phase->type(cmp->in(1)) == TypeInt::BOOL) ) + return NULL; + flipped = 1-flipped; // Test is vs 1 instead of 0! + } + + // Check for setting zero/one opposite expected + if( tzero == TypeInt::ZERO ) { + if( tone == TypeInt::ONE ) { + } else return NULL; + } else if( tzero == TypeInt::ONE ) { + if( tone == TypeInt::ZERO ) { + flipped = 1-flipped; + } else return NULL; + } else return NULL; + + // Check for boolean test backwards + if( b->_test._test == BoolTest::ne ) { + } else if( b->_test._test == BoolTest::eq ) { + flipped = 1-flipped; + } else return NULL; + + // Build int->bool conversion + Node *n = new (phase->C, 2) Conv2BNode( cmp->in(1) ); + if( flipped ) + n = new (phase->C, 3) XorINode( phase->transform(n), phase->intcon(1) ); + + return n; +} + +//------------------------------is_cond_add------------------------------------ +// Check for simple conditional add pattern: "(P < Q) ? X+Y : X;" +// To be profitable the control flow has to disappear; there can be no other +// values merging here. We replace the test-and-branch with: +// "(sgn(P-Q))&Y) + X". Basically, convert "(P < Q)" into 0 or -1 by +// moving the carry bit from (P-Q) into a register with 'sbb EAX,EAX'. +// Then convert Y to 0-or-Y and finally add. +// This is a key transform for SpecJava _201_compress. +static Node* is_cond_add(PhaseGVN *phase, PhiNode *phi, int true_path) { + assert(true_path !=0, "only diamond shape graph expected"); + + // is_diamond_phi() has guaranteed the correctness of the nodes sequence: + // phi->region->if_proj->ifnode->bool->cmp + RegionNode *region = (RegionNode*)phi->in(0); + Node *iff = region->in(1)->in(0); + BoolNode* b = iff->in(1)->as_Bool(); + const CmpNode *cmp = (CmpNode*)b->in(1); + + // Make sure only merging this one phi here + if (region->has_unique_phi() != phi) return NULL; + + // Make sure each arm of the diamond has exactly one output, which we assume + // is the region. Otherwise, the control flow won't disappear. + if (region->in(1)->outcnt() != 1) return NULL; + if (region->in(2)->outcnt() != 1) return NULL; + + // Check for "(P < Q)" of type signed int + if (b->_test._test != BoolTest::lt) return NULL; + if (cmp->Opcode() != Op_CmpI) return NULL; + + Node *p = cmp->in(1); + Node *q = cmp->in(2); + Node *n1 = phi->in( true_path); + Node *n2 = phi->in(3-true_path); + + int op = n1->Opcode(); + if( op != Op_AddI // Need zero as additive identity + /*&&op != Op_SubI && + op != Op_AddP && + op != Op_XorI && + op != Op_OrI*/ ) + return NULL; + + Node *x = n2; + Node *y = n1->in(1); + if( n2 == n1->in(1) ) { + y = n1->in(2); + } else if( n2 == n1->in(1) ) { + } else return NULL; + + // Not so profitable if compare and add are constants + if( q->is_Con() && phase->type(q) != TypeInt::ZERO && y->is_Con() ) + return NULL; + + Node *cmplt = phase->transform( new (phase->C, 3) CmpLTMaskNode(p,q) ); + Node *j_and = phase->transform( new (phase->C, 3) AndINode(cmplt,y) ); + return new (phase->C, 3) AddINode(j_and,x); +} + +//------------------------------is_absolute------------------------------------ +// Check for absolute value. +static Node* is_absolute( PhaseGVN *phase, PhiNode *phi_root, int true_path) { + assert(true_path !=0, "only diamond shape graph expected"); + + int cmp_zero_idx = 0; // Index of compare input where to look for zero + int phi_x_idx = 0; // Index of phi input where to find naked x + + // ABS ends with the merge of 2 control flow paths. + // Find the false path from the true path. With only 2 inputs, 3 - x works nicely. + int false_path = 3 - true_path; + + // is_diamond_phi() has guaranteed the correctness of the nodes sequence: + // phi->region->if_proj->ifnode->bool->cmp + BoolNode *bol = phi_root->in(0)->in(1)->in(0)->in(1)->as_Bool(); + + // Check bool sense + switch( bol->_test._test ) { + case BoolTest::lt: cmp_zero_idx = 1; phi_x_idx = true_path; break; + case BoolTest::le: cmp_zero_idx = 2; phi_x_idx = false_path; break; + case BoolTest::gt: cmp_zero_idx = 2; phi_x_idx = true_path; break; + case BoolTest::ge: cmp_zero_idx = 1; phi_x_idx = false_path; break; + default: return NULL; break; + } + + // Test is next + Node *cmp = bol->in(1); + const Type *tzero = NULL; + switch( cmp->Opcode() ) { + case Op_CmpF: tzero = TypeF::ZERO; break; // Float ABS + case Op_CmpD: tzero = TypeD::ZERO; break; // Double ABS + default: return NULL; + } + + // Find zero input of compare; the other input is being abs'd + Node *x = NULL; + bool flip = false; + if( phase->type(cmp->in(cmp_zero_idx)) == tzero ) { + x = cmp->in(3 - cmp_zero_idx); + } else if( phase->type(cmp->in(3 - cmp_zero_idx)) == tzero ) { + // The test is inverted, we should invert the result... + x = cmp->in(cmp_zero_idx); + flip = true; + } else { + return NULL; + } + + // Next get the 2 pieces being selected, one is the original value + // and the other is the negated value. + if( phi_root->in(phi_x_idx) != x ) return NULL; + + // Check other phi input for subtract node + Node *sub = phi_root->in(3 - phi_x_idx); + + // Allow only Sub(0,X) and fail out for all others; Neg is not OK + if( tzero == TypeF::ZERO ) { + if( sub->Opcode() != Op_SubF || + sub->in(2) != x || + phase->type(sub->in(1)) != tzero ) return NULL; + x = new (phase->C, 2) AbsFNode(x); + if (flip) { + x = new (phase->C, 3) SubFNode(sub->in(1), phase->transform(x)); + } + } else { + if( sub->Opcode() != Op_SubD || + sub->in(2) != x || + phase->type(sub->in(1)) != tzero ) return NULL; + x = new (phase->C, 2) AbsDNode(x); + if (flip) { + x = new (phase->C, 3) SubDNode(sub->in(1), phase->transform(x)); + } + } + + return x; +} + +//------------------------------split_once------------------------------------- +// Helper for split_flow_path +static void split_once(PhaseIterGVN *igvn, Node *phi, Node *val, Node *n, Node *newn) { + igvn->hash_delete(n); // Remove from hash before hacking edges + + uint j = 1; + for( uint i = phi->req()-1; i > 0; i-- ) { + if( phi->in(i) == val ) { // Found a path with val? + // Add to NEW Region/Phi, no DU info + newn->set_req( j++, n->in(i) ); + // Remove from OLD Region/Phi + n->del_req(i); + } + } + + // Register the new node but do not transform it. Cannot transform until the + // entire Region/Phi conglerate has been hacked as a single huge transform. + igvn->register_new_node_with_optimizer( newn ); + // Now I can point to the new node. + n->add_req(newn); + igvn->_worklist.push(n); +} + +//------------------------------split_flow_path-------------------------------- +// Check for merging identical values and split flow paths +static Node* split_flow_path(PhaseGVN *phase, PhiNode *phi) { + BasicType bt = phi->type()->basic_type(); + if( bt == T_ILLEGAL || type2size[bt] <= 0 ) + return NULL; // Bail out on funny non-value stuff + if( phi->req() <= 3 ) // Need at least 2 matched inputs and a + return NULL; // third unequal input to be worth doing + + // Scan for a constant + uint i; + for( i = 1; i < phi->req()-1; i++ ) { + Node *n = phi->in(i); + if( !n ) return NULL; + if( phase->type(n) == Type::TOP ) return NULL; + if( n->Opcode() == Op_ConP ) + break; + } + if( i >= phi->req() ) // Only split for constants + return NULL; + + Node *val = phi->in(i); // Constant to split for + uint hit = 0; // Number of times it occurs + + for( ; i < phi->req(); i++ ){ // Count occurances of constant + Node *n = phi->in(i); + if( !n ) return NULL; + if( phase->type(n) == Type::TOP ) return NULL; + if( phi->in(i) == val ) + hit++; + } + + if( hit <= 1 || // Make sure we find 2 or more + hit == phi->req()-1 ) // and not ALL the same value + return NULL; + + // Now start splitting out the flow paths that merge the same value. + // Split first the RegionNode. + PhaseIterGVN *igvn = phase->is_IterGVN(); + Node *r = phi->region(); + RegionNode *newr = new (phase->C, hit+1) RegionNode(hit+1); + split_once(igvn, phi, val, r, newr); + + // Now split all other Phis than this one + for (DUIterator_Fast kmax, k = r->fast_outs(kmax); k < kmax; k++) { + Node* phi2 = r->fast_out(k); + if( phi2->is_Phi() && phi2->as_Phi() != phi ) { + PhiNode *newphi = PhiNode::make_blank(newr, phi2); + split_once(igvn, phi, val, phi2, newphi); + } + } + + // Clean up this guy + igvn->hash_delete(phi); + for( i = phi->req()-1; i > 0; i-- ) { + if( phi->in(i) == val ) { + phi->del_req(i); + } + } + phi->add_req(val); + + return phi; +} + +//============================================================================= +//------------------------------simple_data_loop_check------------------------- +// Try to determing if the phi node in a simple safe/unsafe data loop. +// Returns: +// enum LoopSafety { Safe = 0, Unsafe, UnsafeLoop }; +// Safe - safe case when the phi and it's inputs reference only safe data +// nodes; +// Unsafe - the phi and it's inputs reference unsafe data nodes but there +// is no reference back to the phi - need a graph walk +// to determine if it is in a loop; +// UnsafeLoop - unsafe case when the phi references itself directly or through +// unsafe data node. +// Note: a safe data node is a node which could/never reference itself during +// GVN transformations. For now it is Con, Proj, Phi, CastPP, CheckCastPP. +// I mark Phi nodes as safe node not only because they can reference itself +// but also to prevent mistaking the fallthrough case inside an outer loop +// as dead loop when the phi references itselfs through an other phi. +PhiNode::LoopSafety PhiNode::simple_data_loop_check(Node *in) const { + // It is unsafe loop if the phi node references itself directly. + if (in == (Node*)this) + return UnsafeLoop; // Unsafe loop + // Unsafe loop if the phi node references itself through an unsafe data node. + // Exclude cases with null inputs or data nodes which could reference + // itself (safe for dead loops). + if (in != NULL && !in->is_dead_loop_safe()) { + // Check inputs of phi's inputs also. + // It is much less expensive then full graph walk. + uint cnt = in->req(); + for (uint i = 1; i < cnt; ++i) { + Node* m = in->in(i); + if (m == (Node*)this) + return UnsafeLoop; // Unsafe loop + if (m != NULL && !m->is_dead_loop_safe()) { + // Check the most common case (about 30% of all cases): + // phi->Load/Store->AddP->(ConP ConP Con)/(Parm Parm Con). + Node *m1 = (m->is_AddP() && m->req() > 3) ? m->in(1) : NULL; + if (m1 == (Node*)this) + return UnsafeLoop; // Unsafe loop + if (m1 != NULL && m1 == m->in(2) && + m1->is_dead_loop_safe() && m->in(3)->is_Con()) { + continue; // Safe case + } + // The phi references an unsafe node - need full analysis. + return Unsafe; + } + } + } + return Safe; // Safe case - we can optimize the phi node. +} + +//------------------------------is_unsafe_data_reference----------------------- +// If phi can be reached through the data input - it is data loop. +bool PhiNode::is_unsafe_data_reference(Node *in) const { + assert(req() > 1, ""); + // First, check simple cases when phi references itself directly or + // through an other node. + LoopSafety safety = simple_data_loop_check(in); + if (safety == UnsafeLoop) + return true; // phi references itself - unsafe loop + else if (safety == Safe) + return false; // Safe case - phi could be replaced with the unique input. + + // Unsafe case when we should go through data graph to determine + // if the phi references itself. + + ResourceMark rm; + + Arena *a = Thread::current()->resource_area(); + Node_List nstack(a); + VectorSet visited(a); + + nstack.push(in); // Start with unique input. + visited.set(in->_idx); + while (nstack.size() != 0) { + Node* n = nstack.pop(); + uint cnt = n->req(); + for (uint i = 1; i < cnt; i++) { // Only data paths + Node* m = n->in(i); + if (m == (Node*)this) { + return true; // Data loop + } + if (m != NULL && !m->is_dead_loop_safe()) { // Only look for unsafe cases. + if (!visited.test_set(m->_idx)) + nstack.push(m); + } + } + } + return false; // The phi is not reachable from its inputs +} + + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Must preserve +// the CFG, but we can still strip out dead paths. +Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // The next should never happen after 6297035 fix. + if( is_copy() ) // Already degraded to a Copy ? + return NULL; // No change + + Node *r = in(0); // RegionNode + assert(r->in(0) == NULL || !r->in(0)->is_Root(), "not a specially hidden merge"); + + // Note: During parsing, phis are often transformed before their regions. + // This means we have to use type_or_null to defend against untyped regions. + if( phase->type_or_null(r) == Type::TOP ) // Dead code? + return NULL; // No change + + Node *top = phase->C->top(); + + // The are 2 situations when only one valid phi's input is left + // (in addition to Region input). + // One: region is not loop - replace phi with this input. + // Two: region is loop - replace phi with top since this data path is dead + // and we need to break the dead data loop. + Node* progress = NULL; // Record if any progress made + for( uint j = 1; j < req(); ++j ){ // For all paths in + // Check unreachable control paths + Node* rc = r->in(j); + Node* n = in(j); // Get the input + if (rc == NULL || phase->type(rc) == Type::TOP) { + if (n != top) { // Not already top? + set_req(j, top); // Nuke it down + progress = this; // Record progress + } + } + } + + Node* uin = unique_input(phase); + if (uin == top) { // Simplest case: no alive inputs. + if (can_reshape) // IGVN transformation + return top; + else + return NULL; // Identity will return TOP + } else if (uin != NULL) { + // Only one not-NULL unique input path is left. + // Determine if this input is backedge of a loop. + // (Skip new phis which have no uses and dead regions). + if( outcnt() > 0 && r->in(0) != NULL ) { + // First, take the short cut when we know it is a loop and + // the EntryControl data path is dead. + assert(!r->is_Loop() || r->req() == 3, "Loop node should have 3 inputs"); + // Then, check if there is a data loop when phi references itself directly + // or through other data nodes. + if( r->is_Loop() && !phase->eqv_uncast(uin, in(LoopNode::EntryControl)) || + !r->is_Loop() && is_unsafe_data_reference(uin) ) { + // Break this data loop to avoid creation of a dead loop. + if (can_reshape) { + return top; + } else { + // We can't return top if we are in Parse phase - cut inputs only + // let Identity to handle the case. + replace_edge(uin, top); + return NULL; + } + } + } + + // One unique input. + debug_only(Node* ident = Identity(phase)); + // The unique input must eventually be detected by the Identity call. +#ifdef ASSERT + if (ident != uin && !ident->is_top()) { + // print this output before failing assert + r->dump(3); + this->dump(3); + ident->dump(); + uin->dump(); + } +#endif + assert(ident == uin || ident->is_top(), "Identity must clean this up"); + return NULL; + } + + + Node* opt = NULL; + int true_path = is_diamond_phi(); + if( true_path != 0 ) { + // Check for CMove'ing identity. If it would be unsafe, + // handle it here. In the safe case, let Identity handle it. + Node* unsafe_id = is_cmove_id(phase, true_path); + if( unsafe_id != NULL && is_unsafe_data_reference(unsafe_id) ) + opt = unsafe_id; + + // Check for simple convert-to-boolean pattern + if( opt == NULL ) + opt = is_x2logic(phase, this, true_path); + + // Check for absolute value + if( opt == NULL ) + opt = is_absolute(phase, this, true_path); + + // Check for conditional add + if( opt == NULL && can_reshape ) + opt = is_cond_add(phase, this, true_path); + + // These 4 optimizations could subsume the phi: + // have to check for a dead data loop creation. + if( opt != NULL ) { + if( opt == unsafe_id || is_unsafe_data_reference(opt) ) { + // Found dead loop. + if( can_reshape ) + return top; + // We can't return top if we are in Parse phase - cut inputs only + // to stop further optimizations for this phi. Identity will return TOP. + assert(req() == 3, "only diamond merge phi here"); + set_req(1, top); + set_req(2, top); + return NULL; + } else { + return opt; + } + } + } + + // Check for merging identical values and split flow paths + if (can_reshape) { + opt = split_flow_path(phase, this); + // This optimization only modifies phi - don't need to check for dead loop. + assert(opt == NULL || phase->eqv(opt, this), "do not elide phi"); + if (opt != NULL) return opt; + } + + if (in(1) != NULL && in(1)->Opcode() == Op_AddP && can_reshape) { + // Try to undo Phi of AddP: + // (Phi (AddP base base y) (AddP base2 base2 y)) + // becomes: + // newbase := (Phi base base2) + // (AddP newbase newbase y) + // + // This occurs as a result of unsuccessful split_thru_phi and + // interferes with taking advantage of addressing modes. See the + // clone_shift_expressions code in matcher.cpp + Node* addp = in(1); + const Type* type = addp->in(AddPNode::Base)->bottom_type(); + Node* y = addp->in(AddPNode::Offset); + if (y != NULL && addp->in(AddPNode::Base) == addp->in(AddPNode::Address)) { + // make sure that all the inputs are similar to the first one, + // i.e. AddP with base == address and same offset as first AddP + bool doit = true; + for (uint i = 2; i < req(); i++) { + if (in(i) == NULL || + in(i)->Opcode() != Op_AddP || + in(i)->in(AddPNode::Base) != in(i)->in(AddPNode::Address) || + in(i)->in(AddPNode::Offset) != y) { + doit = false; + break; + } + // Accumulate type for resulting Phi + type = type->meet(in(i)->in(AddPNode::Base)->bottom_type()); + } + Node* base = NULL; + if (doit) { + // Check for neighboring AddP nodes in a tree. + // If they have a base, use that it. + for (DUIterator_Fast kmax, k = this->fast_outs(kmax); k < kmax; k++) { + Node* u = this->fast_out(k); + if (u->is_AddP()) { + Node* base2 = u->in(AddPNode::Base); + if (base2 != NULL && !base2->is_top()) { + if (base == NULL) + base = base2; + else if (base != base2) + { doit = false; break; } + } + } + } + } + if (doit) { + if (base == NULL) { + base = new (phase->C, in(0)->req()) PhiNode(in(0), type, NULL); + for (uint i = 1; i < req(); i++) { + base->init_req(i, in(i)->in(AddPNode::Base)); + } + phase->is_IterGVN()->register_new_node_with_optimizer(base); + } + return new (phase->C, 4) AddPNode(base, base, y); + } + } + } + + // Split phis through memory merges, so that the memory merges will go away. + // Piggy-back this transformation on the search for a unique input.... + // It will be as if the merged memory is the unique value of the phi. + // (Do not attempt this optimization unless parsing is complete. + // It would make the parser's memory-merge logic sick.) + // (MergeMemNode is not dead_loop_safe - need to check for dead loop.) + if (progress == NULL && can_reshape && type() == Type::MEMORY) { + // see if this phi should be sliced + uint merge_width = 0; + bool saw_self = false; + for( uint i=1; iis_MergeMem()) { + MergeMemNode* n = ii->as_MergeMem(); + merge_width = MAX2(merge_width, n->req()); + saw_self = saw_self || phase->eqv(n->base_memory(), this); + } + } + + // This restriction is temporarily necessary to ensure termination: + if (!saw_self && adr_type() == TypePtr::BOTTOM) merge_width = 0; + + if (merge_width > Compile::AliasIdxRaw) { + // found at least one non-empty MergeMem + const TypePtr* at = adr_type(); + if (at != TypePtr::BOTTOM) { + // Patch the existing phi to select an input from the merge: + // Phi:AT1(...MergeMem(m0, m1, m2)...) into + // Phi:AT1(...m1...) + int alias_idx = phase->C->get_alias_index(at); + for (uint i=1; iis_MergeMem()) { + MergeMemNode* n = ii->as_MergeMem(); + // compress paths and change unreachable cycles to TOP + // If not, we can update the input infinitely along a MergeMem cycle + // Equivalent code is in MemNode::Ideal_common + Node *m = phase->transform(n); + // If tranformed to a MergeMem, get the desired slice + // Otherwise the returned node represents memory for every slice + Node *new_mem = (m->is_MergeMem()) ? + m->as_MergeMem()->memory_at(alias_idx) : m; + // Update input if it is progress over what we have now + if (new_mem != ii) { + set_req(i, new_mem); + progress = this; + } + } + } + } else { + // We know that at least one MergeMem->base_memory() == this + // (saw_self == true). If all other inputs also references this phi + // (directly or through data nodes) - it is dead loop. + bool saw_safe_input = false; + for (uint j = 1; j < req(); ++j) { + Node *n = in(j); + if (n->is_MergeMem() && n->as_MergeMem()->base_memory() == this) + continue; // skip known cases + if (!is_unsafe_data_reference(n)) { + saw_safe_input = true; // found safe input + break; + } + } + if (!saw_safe_input) + return top; // all inputs reference back to this phi - dead loop + + // Phi(...MergeMem(m0, m1:AT1, m2:AT2)...) into + // MergeMem(Phi(...m0...), Phi:AT1(...m1...), Phi:AT2(...m2...)) + PhaseIterGVN *igvn = phase->is_IterGVN(); + Node* hook = new (phase->C, 1) Node(1); + PhiNode* new_base = (PhiNode*) clone(); + // Must eagerly register phis, since they participate in loops. + if (igvn) { + igvn->register_new_node_with_optimizer(new_base); + hook->add_req(new_base); + } + MergeMemNode* result = MergeMemNode::make(phase->C, new_base); + for (uint i = 1; i < req(); ++i) { + Node *ii = in(i); + if (ii->is_MergeMem()) { + MergeMemNode* n = ii->as_MergeMem(); + for (MergeMemStream mms(result, n); mms.next_non_empty2(); ) { + // If we have not seen this slice yet, make a phi for it. + bool made_new_phi = false; + if (mms.is_empty()) { + Node* new_phi = new_base->slice_memory(mms.adr_type(phase->C)); + made_new_phi = true; + if (igvn) { + igvn->register_new_node_with_optimizer(new_phi); + hook->add_req(new_phi); + } + mms.set_memory(new_phi); + } + Node* phi = mms.memory(); + assert(made_new_phi || phi->in(i) == n, "replace the i-th merge by a slice"); + phi->set_req(i, mms.memory2()); + } + } + } + // Distribute all self-loops. + { // (Extra braces to hide mms.) + for (MergeMemStream mms(result); mms.next_non_empty(); ) { + Node* phi = mms.memory(); + for (uint i = 1; i < req(); ++i) { + if (phi->in(i) == this) phi->set_req(i, phi); + } + } + } + // now transform the new nodes, and return the mergemem + for (MergeMemStream mms(result); mms.next_non_empty(); ) { + Node* phi = mms.memory(); + mms.set_memory(phase->transform(phi)); + } + if (igvn) { // Unhook. + igvn->hash_delete(hook); + for (uint i = 1; i < hook->req(); i++) { + hook->set_req(i, NULL); + } + } + // Replace self with the result. + return result; + } + } + } + + return progress; // Return any progress +} + +//------------------------------out_RegMask------------------------------------ +const RegMask &PhiNode::in_RegMask(uint i) const { + return i ? out_RegMask() : RegMask::Empty; +} + +const RegMask &PhiNode::out_RegMask() const { + uint ideal_reg = Matcher::base2reg[_type->base()]; + assert( ideal_reg != Node::NotAMachineReg, "invalid type at Phi" ); + if( ideal_reg == 0 ) return RegMask::Empty; + return *(Compile::current()->matcher()->idealreg2spillmask[ideal_reg]); +} + +#ifndef PRODUCT +void PhiNode::dump_spec(outputStream *st) const { + TypeNode::dump_spec(st); + if (in(0) != NULL && + in(0)->is_CountedLoop() && + in(0)->as_CountedLoop()->phi() == this) { + st->print(" #tripcount"); + } +} +#endif + + +//============================================================================= +const Type *GotoNode::Value( PhaseTransform *phase ) const { + // If the input is reachable, then we are executed. + // If the input is not reachable, then we are not executed. + return phase->type(in(0)); +} + +Node *GotoNode::Identity( PhaseTransform *phase ) { + return in(0); // Simple copy of incoming control +} + +const RegMask &GotoNode::out_RegMask() const { + return RegMask::Empty; +} + +//============================================================================= +const RegMask &JumpNode::out_RegMask() const { + return RegMask::Empty; +} + +//============================================================================= +const RegMask &JProjNode::out_RegMask() const { + return RegMask::Empty; +} + +//============================================================================= +const RegMask &CProjNode::out_RegMask() const { + return RegMask::Empty; +} + + + +//============================================================================= + +uint PCTableNode::hash() const { return Node::hash() + _size; } +uint PCTableNode::cmp( const Node &n ) const +{ return _size == ((PCTableNode&)n)._size; } + +const Type *PCTableNode::bottom_type() const { + const Type** f = TypeTuple::fields(_size); + for( uint i = 0; i < _size; i++ ) f[i] = Type::CONTROL; + return TypeTuple::make(_size, f); +} + +//------------------------------Value------------------------------------------ +// Compute the type of the PCTableNode. If reachable it is a tuple of +// Control, otherwise the table targets are not reachable +const Type *PCTableNode::Value( PhaseTransform *phase ) const { + if( phase->type(in(0)) == Type::CONTROL ) + return bottom_type(); + return Type::TOP; // All paths dead? Then so are we +} + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node *PCTableNode::Ideal(PhaseGVN *phase, bool can_reshape) { + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + +//============================================================================= +uint JumpProjNode::hash() const { + return Node::hash() + _dest_bci; +} + +uint JumpProjNode::cmp( const Node &n ) const { + return ProjNode::cmp(n) && + _dest_bci == ((JumpProjNode&)n)._dest_bci; +} + +#ifndef PRODUCT +void JumpProjNode::dump_spec(outputStream *st) const { + ProjNode::dump_spec(st); + st->print("@bci %d ",_dest_bci); +} +#endif + +//============================================================================= +//------------------------------Value------------------------------------------ +// Check for being unreachable, or for coming from a Rethrow. Rethrow's cannot +// have the default "fall_through_index" path. +const Type *CatchNode::Value( PhaseTransform *phase ) const { + // Unreachable? Then so are all paths from here. + if( phase->type(in(0)) == Type::TOP ) return Type::TOP; + // First assume all paths are reachable + const Type** f = TypeTuple::fields(_size); + for( uint i = 0; i < _size; i++ ) f[i] = Type::CONTROL; + // Identify cases that will always throw an exception + // () rethrow call + // () virtual or interface call with NULL receiver + // () call is a check cast with incompatible arguments + if( in(1)->is_Proj() ) { + Node *i10 = in(1)->in(0); + if( i10->is_Call() ) { + CallNode *call = i10->as_Call(); + // Rethrows always throw exceptions, never return + if (call->entry_point() == OptoRuntime::rethrow_stub()) { + f[CatchProjNode::fall_through_index] = Type::TOP; + } else if( call->req() > TypeFunc::Parms ) { + const Type *arg0 = phase->type( call->in(TypeFunc::Parms) ); + // Check for null reciever to virtual or interface calls + if( call->is_CallDynamicJava() && + arg0->higher_equal(TypePtr::NULL_PTR) ) { + f[CatchProjNode::fall_through_index] = Type::TOP; + } + } // End of if not a runtime stub + } // End of if have call above me + } // End of slot 1 is not a projection + return TypeTuple::make(_size, f); +} + +//============================================================================= +uint CatchProjNode::hash() const { + return Node::hash() + _handler_bci; +} + + +uint CatchProjNode::cmp( const Node &n ) const { + return ProjNode::cmp(n) && + _handler_bci == ((CatchProjNode&)n)._handler_bci; +} + + +//------------------------------Identity--------------------------------------- +// If only 1 target is possible, choose it if it is the main control +Node *CatchProjNode::Identity( PhaseTransform *phase ) { + // If my value is control and no other value is, then treat as ID + const TypeTuple *t = phase->type(in(0))->is_tuple(); + if (t->field_at(_con) != Type::CONTROL) return this; + // If we remove the last CatchProj and elide the Catch/CatchProj, then we + // also remove any exception table entry. Thus we must know the call + // feeding the Catch will not really throw an exception. This is ok for + // the main fall-thru control (happens when we know a call can never throw + // an exception) or for "rethrow", because a further optimnization will + // yank the rethrow (happens when we inline a function that can throw an + // exception and the caller has no handler). Not legal, e.g., for passing + // a NULL receiver to a v-call, or passing bad types to a slow-check-cast. + // These cases MUST throw an exception via the runtime system, so the VM + // will be looking for a table entry. + Node *proj = in(0)->in(1); // Expect a proj feeding CatchNode + CallNode *call; + if (_con != TypeFunc::Control && // Bail out if not the main control. + !(proj->is_Proj() && // AND NOT a rethrow + proj->in(0)->is_Call() && + (call = proj->in(0)->as_Call()) && + call->entry_point() == OptoRuntime::rethrow_stub())) + return this; + + // Search for any other path being control + for (uint i = 0; i < t->cnt(); i++) { + if (i != _con && t->field_at(i) == Type::CONTROL) + return this; + } + // Only my path is possible; I am identity on control to the jump + return in(0)->in(0); +} + + +#ifndef PRODUCT +void CatchProjNode::dump_spec(outputStream *st) const { + ProjNode::dump_spec(st); + st->print("@bci %d ",_handler_bci); +} +#endif + +//============================================================================= +//------------------------------Identity--------------------------------------- +// Check for CreateEx being Identity. +Node *CreateExNode::Identity( PhaseTransform *phase ) { + if( phase->type(in(1)) == Type::TOP ) return in(1); + if( phase->type(in(0)) == Type::TOP ) return in(0); + // We only come from CatchProj, unless the CatchProj goes away. + // If the CatchProj is optimized away, then we just carry the + // exception oop through. + CallNode *call = in(1)->in(0)->as_Call(); + + return ( in(0)->is_CatchProj() && in(0)->in(0)->in(1) == in(1) ) + ? this + : call->in(TypeFunc::Parms); +} + +//============================================================================= +#ifndef PRODUCT +void NeverBranchNode::format( PhaseRegAlloc *ra_, outputStream *st) const { + st->print("%s", Name()); +} +#endif diff --git a/hotspot/src/share/vm/opto/cfgnode.hpp b/hotspot/src/share/vm/opto/cfgnode.hpp new file mode 100644 index 00000000000..e01e25258ec --- /dev/null +++ b/hotspot/src/share/vm/opto/cfgnode.hpp @@ -0,0 +1,481 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +class Matcher; +class Node; +class RegionNode; +class TypeNode; +class PhiNode; +class GotoNode; +class MultiNode; +class MultiBranchNode; +class IfNode; +class PCTableNode; +class JumpNode; +class CatchNode; +class NeverBranchNode; +class ProjNode; +class CProjNode; +class IfTrueNode; +class IfFalseNode; +class CatchProjNode; +class JProjNode; +class JumpProjNode; +class SCMemProjNode; +class PhaseIdealLoop; + +//------------------------------RegionNode------------------------------------- +// The class of RegionNodes, which can be mapped to basic blocks in the +// program. Their inputs point to Control sources. PhiNodes (described +// below) have an input point to a RegionNode. Merged data inputs to PhiNodes +// correspond 1-to-1 with RegionNode inputs. The zero input of a PhiNode is +// the RegionNode, and the zero input of the RegionNode is itself. +class RegionNode : public Node { +public: + // Node layout (parallels PhiNode): + enum { Region, // Generally points to self. + Control // Control arcs are [1..len) + }; + + RegionNode( uint required ) : Node(required) { + init_class_id(Class_Region); + init_req(0,this); + } + + Node* is_copy() const { + const Node* r = _in[Region]; + if (r == NULL) + return nonnull_req(); + return NULL; // not a copy! + } + PhiNode* has_phi() const; // returns an arbitrary phi user, or NULL + PhiNode* has_unique_phi() const; // returns the unique phi user, or NULL + // Is this region node unreachable from root? + bool is_unreachable_region(PhaseGVN *phase) const; + virtual int Opcode() const; + virtual bool pinned() const { return (const Node *)in(0) == this; } + virtual bool is_CFG () const { return true; } + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual bool depends_only_on_test() const { return false; } + virtual const Type *bottom_type() const { return Type::CONTROL; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const RegMask &out_RegMask() const; +}; + +//------------------------------JProjNode-------------------------------------- +// jump projection for node that produces multiple control-flow paths +class JProjNode : public ProjNode { + public: + JProjNode( Node* ctrl, uint idx ) : ProjNode(ctrl,idx) {} + virtual int Opcode() const; + virtual bool is_CFG() const { return true; } + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual const Node* is_block_proj() const { return in(0); } + virtual const RegMask& out_RegMask() const; + virtual uint ideal_reg() const { return 0; } +}; + +//------------------------------PhiNode---------------------------------------- +// PhiNodes merge values from different Control paths. Slot 0 points to the +// controlling RegionNode. Other slots map 1-for-1 with incoming control flow +// paths to the RegionNode. For speed reasons (to avoid another pass) we +// can turn PhiNodes into copys in-place by NULL'ing out their RegionNode +// input in slot 0. +class PhiNode : public TypeNode { + const TypePtr* const _adr_type; // non-null only for Type::MEMORY nodes. + // Size is bigger to hold the _adr_type field. + virtual uint hash() const; // Check the type + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const { return sizeof(*this); } + + // Determine a unique non-trivial input, if any. + // Ignore casts if it helps. Return NULL on failure. + Node* unique_input(PhaseTransform *phase); + // Determine if CMoveNode::is_cmove_id can be used at this join point. + Node* is_cmove_id(PhaseTransform* phase, int true_path); + +public: + // Node layout (parallels RegionNode): + enum { Region, // Control input is the Phi's region. + Input // Input values are [1..len) + }; + + PhiNode( Node *r, const Type *t, const TypePtr* at = NULL ) + : TypeNode(t,r->req()), _adr_type(at) { + init_class_id(Class_Phi); + init_req(0, r); + verify_adr_type(); + } + // create a new phi with in edges matching r and set (initially) to x + static PhiNode* make( Node* r, Node* x ); + // extra type arguments override the new phi's bottom_type and adr_type + static PhiNode* make( Node* r, Node* x, const Type *t, const TypePtr* at = NULL ); + // create a new phi with narrowed memory type + PhiNode* slice_memory(const TypePtr* adr_type) const; + // like make(r, x), but does not initialize the in edges to x + static PhiNode* make_blank( Node* r, Node* x ); + + // Accessors + RegionNode* region() const { Node* r = in(Region); assert(!r || r->is_Region(), ""); return (RegionNode*)r; } + + Node* is_copy() const { + // The node is a real phi if _in[0] is a Region node. + DEBUG_ONLY(const Node* r = _in[Region];) + assert(r != NULL && r->is_Region(), "Not valid control"); + return NULL; // not a copy! + } + + // Check for a simple dead loop. + enum LoopSafety { Safe = 0, Unsafe, UnsafeLoop }; + LoopSafety simple_data_loop_check(Node *in) const; + // Is it unsafe data loop? It becomes a dead loop if this phi node removed. + bool is_unsafe_data_reference(Node *in) const; + int is_diamond_phi() const; + virtual int Opcode() const; + virtual bool pinned() const { return in(0) != 0; } + virtual const TypePtr *adr_type() const { verify_adr_type(true); return _adr_type; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const RegMask &out_RegMask() const; + virtual const RegMask &in_RegMask(uint) const; +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +#ifdef ASSERT + void verify_adr_type(VectorSet& visited, const TypePtr* at) const; + void verify_adr_type(bool recursive = false) const; +#else //ASSERT + void verify_adr_type(bool recursive = false) const {} +#endif //ASSERT +}; + +//------------------------------GotoNode--------------------------------------- +// GotoNodes perform direct branches. +class GotoNode : public Node { +public: + GotoNode( Node *control ) : Node(control) { + init_flags(Flag_is_Goto); + } + virtual int Opcode() const; + virtual bool pinned() const { return true; } + virtual bool is_CFG() const { return true; } + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual const Node *is_block_proj() const { return this; } + virtual bool depends_only_on_test() const { return false; } + virtual const Type *bottom_type() const { return Type::CONTROL; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + virtual const RegMask &out_RegMask() const; +}; + +//------------------------------CProjNode-------------------------------------- +// control projection for node that produces multiple control-flow paths +class CProjNode : public ProjNode { +public: + CProjNode( Node *ctrl, uint idx ) : ProjNode(ctrl,idx) {} + virtual int Opcode() const; + virtual bool is_CFG() const { return true; } + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual const Node *is_block_proj() const { return in(0); } + virtual const RegMask &out_RegMask() const; + virtual uint ideal_reg() const { return 0; } +}; + +//---------------------------MultiBranchNode----------------------------------- +// This class defines a MultiBranchNode, a MultiNode which yields multiple +// control values. These are distinguished from other types of MultiNodes +// which yield multiple values, but control is always and only projection #0. +class MultiBranchNode : public MultiNode { +public: + MultiBranchNode( uint required ) : MultiNode(required) { + init_class_id(Class_MultiBranch); + } +}; + +//------------------------------IfNode----------------------------------------- +// Output selected Control, based on a boolean test +class IfNode : public MultiBranchNode { + // Size is bigger to hold the probability field. However, _prob does not + // change the semantics so it does not appear in the hash & cmp functions. + virtual uint size_of() const { return sizeof(*this); } +public: + + // Degrees of branch prediction probability by order of magnitude: + // PROB_UNLIKELY_1e(N) is a 1 in 1eN chance. + // PROB_LIKELY_1e(N) is a 1 - PROB_UNLIKELY_1e(N) +#define PROB_UNLIKELY_MAG(N) (1e- ## N ## f) +#define PROB_LIKELY_MAG(N) (1.0f-PROB_UNLIKELY_MAG(N)) + + // Maximum and minimum branch prediction probabilties + // 1 in 1,000,000 (magnitude 6) + // + // Although PROB_NEVER == PROB_MIN and PROB_ALWAYS == PROB_MAX + // they are used to distinguish different situations: + // + // The name PROB_MAX (PROB_MIN) is for probabilities which correspond to + // very likely (unlikely) but with a concrete possibility of a rare + // contrary case. These constants would be used for pinning + // measurements, and as measures for assertions that have high + // confidence, but some evidence of occasional failure. + // + // The name PROB_ALWAYS (PROB_NEVER) is to stand for situations for which + // there is no evidence at all that the contrary case has ever occurred. + +#define PROB_NEVER PROB_UNLIKELY_MAG(6) +#define PROB_ALWAYS PROB_LIKELY_MAG(6) + +#define PROB_MIN PROB_UNLIKELY_MAG(6) +#define PROB_MAX PROB_LIKELY_MAG(6) + + // Static branch prediction probabilities + // 1 in 10 (magnitude 1) +#define PROB_STATIC_INFREQUENT PROB_UNLIKELY_MAG(1) +#define PROB_STATIC_FREQUENT PROB_LIKELY_MAG(1) + + // Fair probability 50/50 +#define PROB_FAIR (0.5f) + + // Unknown probability sentinel +#define PROB_UNKNOWN (-1.0f) + + // Probability "constructors", to distinguish as a probability any manifest + // constant without a names +#define PROB_LIKELY(x) ((float) (x)) +#define PROB_UNLIKELY(x) (1.0f - (float)(x)) + + // Other probabilities in use, but without a unique name, are documented + // here for lack of a better place: + // + // 1 in 1000 probabilities (magnitude 3): + // threshold for converting to conditional move + // likelihood of null check failure if a null HAS been seen before + // likelihood of slow path taken in library calls + // + // 1 in 10,000 probabilities (magnitude 4): + // threshold for making an uncommon trap probability more extreme + // threshold for for making a null check implicit + // likelihood of needing a gc if eden top moves during an allocation + // likelihood of a predicted call failure + // + // 1 in 100,000 probabilities (magnitude 5): + // threshold for ignoring counts when estimating path frequency + // likelihood of FP clipping failure + // likelihood of catching an exception from a try block + // likelihood of null check failure if a null has NOT been seen before + // + // Magic manifest probabilities such as 0.83, 0.7, ... can be found in + // gen_subtype_check() and catch_inline_exceptions(). + + float _prob; // Probability of true path being taken. + float _fcnt; // Frequency counter + IfNode( Node *control, Node *b, float p, float fcnt ) + : MultiBranchNode(2), _prob(p), _fcnt(fcnt) { + init_class_id(Class_If); + init_req(0,control); + init_req(1,b); + } + virtual int Opcode() const; + virtual bool pinned() const { return true; } + virtual const Type *bottom_type() const { return TypeTuple::IFBOTH; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const RegMask &out_RegMask() const; + void dominated_by(Node* prev_dom, PhaseIterGVN* igvn); + int is_range_check(Node* &range, Node* &index, jint &offset); + static Node* up_one_dom(Node* curr, bool linear_only = false); + +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +class IfTrueNode : public CProjNode { +public: + IfTrueNode( IfNode *ifnode ) : CProjNode(ifnode,1) { + init_class_id(Class_IfTrue); + } + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); +}; + +class IfFalseNode : public CProjNode { +public: + IfFalseNode( IfNode *ifnode ) : CProjNode(ifnode,0) { + init_class_id(Class_IfFalse); + } + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); +}; + + +//------------------------------PCTableNode------------------------------------ +// Build an indirect branch table. Given a control and a table index, +// control is passed to the Projection matching the table index. Used to +// implement switch statements and exception-handling capabilities. +// Undefined behavior if passed-in index is not inside the table. +class PCTableNode : public MultiBranchNode { + virtual uint hash() const; // Target count; table size + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const { return sizeof(*this); } + +public: + const uint _size; // Number of targets + + PCTableNode( Node *ctrl, Node *idx, uint size ) : MultiBranchNode(2), _size(size) { + init_class_id(Class_PCTable); + init_req(0, ctrl); + init_req(1, idx); + } + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *bottom_type() const; + virtual bool pinned() const { return true; } +}; + +//------------------------------JumpNode--------------------------------------- +// Indirect branch. Uses PCTable above to implement a switch statement. +// It emits as a table load and local branch. +class JumpNode : public PCTableNode { +public: + JumpNode( Node* control, Node* switch_val, uint size) : PCTableNode(control, switch_val, size) { + init_class_id(Class_Jump); + } + virtual int Opcode() const; + virtual const RegMask& out_RegMask() const; + virtual const Node* is_block_proj() const { return this; } +}; + +class JumpProjNode : public JProjNode { + virtual uint hash() const; + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const { return sizeof(*this); } + + private: + const int _dest_bci; + const uint _proj_no; + const int _switch_val; + public: + JumpProjNode(Node* jumpnode, uint proj_no, int dest_bci, int switch_val) + : JProjNode(jumpnode, proj_no), _dest_bci(dest_bci), _proj_no(proj_no), _switch_val(switch_val) { + init_class_id(Class_JumpProj); + } + + virtual int Opcode() const; + virtual const Type* bottom_type() const { return Type::CONTROL; } + int dest_bci() const { return _dest_bci; } + int switch_val() const { return _switch_val; } + uint proj_no() const { return _proj_no; } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------CatchNode-------------------------------------- +// Helper node to fork exceptions. "Catch" catches any exceptions thrown by +// a just-prior call. Looks like a PCTableNode but emits no code - just the +// table. The table lookup and branch is implemented by RethrowNode. +class CatchNode : public PCTableNode { +public: + CatchNode( Node *ctrl, Node *idx, uint size ) : PCTableNode(ctrl,idx,size){ + init_class_id(Class_Catch); + } + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; +}; + +// CatchProjNode controls which exception handler is targetted after a call. +// It is passed in the bci of the target handler, or no_handler_bci in case +// the projection doesn't lead to an exception handler. +class CatchProjNode : public CProjNode { + virtual uint hash() const; + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const { return sizeof(*this); } + +private: + const int _handler_bci; + +public: + enum { + fall_through_index = 0, // the fall through projection index + catch_all_index = 1, // the projection index for catch-alls + no_handler_bci = -1 // the bci for fall through or catch-all projs + }; + + CatchProjNode(Node* catchnode, uint proj_no, int handler_bci) + : CProjNode(catchnode, proj_no), _handler_bci(handler_bci) { + init_class_id(Class_CatchProj); + assert(proj_no != fall_through_index || handler_bci < 0, "fall through case must have bci < 0"); + } + + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *bottom_type() const { return Type::CONTROL; } + int handler_bci() const { return _handler_bci; } + bool is_handler_proj() const { return _handler_bci >= 0; } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + + +//---------------------------------CreateExNode-------------------------------- +// Helper node to create the exception coming back from a call +class CreateExNode : public TypeNode { +public: + CreateExNode(const Type* t, Node* control, Node* i_o) : TypeNode(t, 2) { + init_req(0, control); + init_req(1, i_o); + } + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual bool pinned() const { return true; } + uint match_edge(uint idx) const { return 0; } + virtual uint ideal_reg() const { return Op_RegP; } +}; + +//------------------------------NeverBranchNode------------------------------- +// The never-taken branch. Used to give the appearance of exiting infinite +// loops to those algorithms that like all paths to be reachable. Encodes +// empty. +class NeverBranchNode : public MultiBranchNode { +public: + NeverBranchNode( Node *ctrl ) : MultiBranchNode(1) { init_req(0,ctrl); } + virtual int Opcode() const; + virtual bool pinned() const { return true; }; + virtual const Type *bottom_type() const { return TypeTuple::IFBOTH; } + + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { } + virtual uint size(PhaseRegAlloc *ra_) const { return 0; } +#ifndef PRODUCT + virtual void format( PhaseRegAlloc *, outputStream *st ) const; +#endif +}; diff --git a/hotspot/src/share/vm/opto/chaitin.cpp b/hotspot/src/share/vm/opto/chaitin.cpp new file mode 100644 index 00000000000..33ca24ba5b6 --- /dev/null +++ b/hotspot/src/share/vm/opto/chaitin.cpp @@ -0,0 +1,2042 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_chaitin.cpp.incl" + +//============================================================================= + +#ifndef PRODUCT +void LRG::dump( ) const { + ttyLocker ttyl; + tty->print("%d ",num_regs()); + _mask.dump(); + if( _msize_valid ) { + if( mask_size() == compute_mask_size() ) tty->print(", #%d ",_mask_size); + else tty->print(", #!!!_%d_vs_%d ",_mask_size,_mask.Size()); + } else { + tty->print(", #?(%d) ",_mask.Size()); + } + + tty->print("EffDeg: "); + if( _degree_valid ) tty->print( "%d ", _eff_degree ); + else tty->print("? "); + + if( _def == NodeSentinel ) { + tty->print("MultiDef "); + if (_defs != NULL) { + tty->print("("); + for (int i = 0; i < _defs->length(); i++) { + tty->print("N%d ", _defs->at(i)->_idx); + } + tty->print(") "); + } + } + else if( _def == 0 ) tty->print("Dead "); + else tty->print("Def: N%d ",_def->_idx); + + tty->print("Cost:%4.2g Area:%4.2g Score:%4.2g ",_cost,_area, score()); + // Flags + if( _is_oop ) tty->print("Oop "); + if( _is_float ) tty->print("Float "); + if( _was_spilled1 ) tty->print("Spilled "); + if( _was_spilled2 ) tty->print("Spilled2 "); + if( _direct_conflict ) tty->print("Direct_conflict "); + if( _fat_proj ) tty->print("Fat "); + if( _was_lo ) tty->print("Lo "); + if( _has_copy ) tty->print("Copy "); + if( _at_risk ) tty->print("Risk "); + + if( _must_spill ) tty->print("Must_spill "); + if( _is_bound ) tty->print("Bound "); + if( _msize_valid ) { + if( _degree_valid && lo_degree() ) tty->print("Trivial "); + } + + tty->cr(); +} +#endif + +//------------------------------score------------------------------------------ +// Compute score from cost and area. Low score is best to spill. +static double raw_score( double cost, double area ) { + return cost - (area*RegisterCostAreaRatio) * 1.52588e-5; +} + +double LRG::score() const { + // Scale _area by RegisterCostAreaRatio/64K then subtract from cost. + // Bigger area lowers score, encourages spilling this live range. + // Bigger cost raise score, prevents spilling this live range. + // (Note: 1/65536 is the magic constant below; I dont trust the C optimizer + // to turn a divide by a constant into a multiply by the reciprical). + double score = raw_score( _cost, _area); + + // Account for area. Basically, LRGs covering large areas are better + // to spill because more other LRGs get freed up. + if( _area == 0.0 ) // No area? Then no progress to spill + return 1e35; + + if( _was_spilled2 ) // If spilled once before, we are unlikely + return score + 1e30; // to make progress again. + + if( _cost >= _area*3.0 ) // Tiny area relative to cost + return score + 1e17; // Probably no progress to spill + + if( (_cost+_cost) >= _area*3.0 ) // Small area relative to cost + return score + 1e10; // Likely no progress to spill + + return score; +} + +//------------------------------LRG_List--------------------------------------- +LRG_List::LRG_List( uint max ) : _cnt(max), _max(max), _lidxs(NEW_RESOURCE_ARRAY(uint,max)) { + memset( _lidxs, 0, sizeof(uint)*max ); +} + +void LRG_List::extend( uint nidx, uint lidx ) { + _nesting.check(); + if( nidx >= _max ) { + uint size = 16; + while( size <= nidx ) size <<=1; + _lidxs = REALLOC_RESOURCE_ARRAY( uint, _lidxs, _max, size ); + _max = size; + } + while( _cnt <= nidx ) + _lidxs[_cnt++] = 0; + _lidxs[nidx] = lidx; +} + +#define NUMBUCKS 3 + +//------------------------------Chaitin---------------------------------------- +PhaseChaitin::PhaseChaitin(uint unique, PhaseCFG &cfg, Matcher &matcher) + : PhaseRegAlloc(unique, cfg, matcher, +#ifndef PRODUCT + print_chaitin_statistics +#else + NULL +#endif + ), + _names(unique), _uf_map(unique), + _maxlrg(0), _live(0), + _spilled_once(Thread::current()->resource_area()), + _spilled_twice(Thread::current()->resource_area()), + _lo_degree(0), _lo_stk_degree(0), _hi_degree(0), _simplified(0), + _oldphi(unique) +#ifndef PRODUCT + , _trace_spilling(TraceSpilling || C->method_has_option("TraceSpilling")) +#endif +{ + NOT_PRODUCT( Compile::TracePhase t3("ctorChaitin", &_t_ctorChaitin, TimeCompiler); ) + uint i,j; + // Build a list of basic blocks, sorted by frequency + _blks = NEW_RESOURCE_ARRAY( Block *, _cfg._num_blocks ); + // Experiment with sorting strategies to speed compilation + double cutoff = BLOCK_FREQUENCY(1.0); // Cutoff for high frequency bucket + Block **buckets[NUMBUCKS]; // Array of buckets + uint buckcnt[NUMBUCKS]; // Array of bucket counters + double buckval[NUMBUCKS]; // Array of bucket value cutoffs + for( i = 0; i < NUMBUCKS; i++ ) { + buckets[i] = NEW_RESOURCE_ARRAY( Block *, _cfg._num_blocks ); + buckcnt[i] = 0; + // Bump by three orders of magnitude each time + cutoff *= 0.001; + buckval[i] = cutoff; + for( j = 0; j < _cfg._num_blocks; j++ ) { + buckets[i][j] = NULL; + } + } + // Sort blocks into buckets + for( i = 0; i < _cfg._num_blocks; i++ ) { + for( j = 0; j < NUMBUCKS; j++ ) { + if( (j == NUMBUCKS-1) || (_cfg._blocks[i]->_freq > buckval[j]) ) { + // Assign block to end of list for appropriate bucket + buckets[j][buckcnt[j]++] = _cfg._blocks[i]; + break; // kick out of inner loop + } + } + } + // Dump buckets into final block array + uint blkcnt = 0; + for( i = 0; i < NUMBUCKS; i++ ) { + for( j = 0; j < buckcnt[i]; j++ ) { + _blks[blkcnt++] = buckets[i][j]; + } + } + + assert(blkcnt == _cfg._num_blocks, "Block array not totally filled"); +} + +void PhaseChaitin::Register_Allocate() { + + // Above the OLD FP (and in registers) are the incoming arguments. Stack + // slots in this area are called "arg_slots". Above the NEW FP (and in + // registers) is the outgoing argument area; above that is the spill/temp + // area. These are all "frame_slots". Arg_slots start at the zero + // stack_slots and count up to the known arg_size. Frame_slots start at + // the stack_slot #arg_size and go up. After allocation I map stack + // slots to actual offsets. Stack-slots in the arg_slot area are biased + // by the frame_size; stack-slots in the frame_slot area are biased by 0. + + _trip_cnt = 0; + _alternate = 0; + _matcher._allocation_started = true; + + ResourceArea live_arena; // Arena for liveness & IFG info + ResourceMark rm(&live_arena); + + // Need live-ness for the IFG; need the IFG for coalescing. If the + // liveness is JUST for coalescing, then I can get some mileage by renaming + // all copy-related live ranges low and then using the max copy-related + // live range as a cut-off for LIVE and the IFG. In other words, I can + // build a subset of LIVE and IFG just for copies. + PhaseLive live(_cfg,_names,&live_arena); + + // Need IFG for coalescing and coloring + PhaseIFG ifg( &live_arena ); + _ifg = &ifg; + + if (C->unique() > _names.Size()) _names.extend(C->unique()-1, 0); + + // Come out of SSA world to the Named world. Assign (virtual) registers to + // Nodes. Use the same register for all inputs and the output of PhiNodes + // - effectively ending SSA form. This requires either coalescing live + // ranges or inserting copies. For the moment, we insert "virtual copies" + // - we pretend there is a copy prior to each Phi in predecessor blocks. + // We will attempt to coalesce such "virtual copies" before we manifest + // them for real. + de_ssa(); + + { + NOT_PRODUCT( Compile::TracePhase t3("computeLive", &_t_computeLive, TimeCompiler); ) + _live = NULL; // Mark live as being not available + rm.reset_to_mark(); // Reclaim working storage + IndexSet::reset_memory(C, &live_arena); + ifg.init(_maxlrg); // Empty IFG + gather_lrg_masks( false ); // Collect LRG masks + live.compute( _maxlrg ); // Compute liveness + _live = &live; // Mark LIVE as being available + } + + // Base pointers are currently "used" by instructions which define new + // derived pointers. This makes base pointers live up to the where the + // derived pointer is made, but not beyond. Really, they need to be live + // across any GC point where the derived value is live. So this code looks + // at all the GC points, and "stretches" the live range of any base pointer + // to the GC point. + if( stretch_base_pointer_live_ranges(&live_arena) ) { + NOT_PRODUCT( Compile::TracePhase t3("computeLive (sbplr)", &_t_computeLive, TimeCompiler); ) + // Since some live range stretched, I need to recompute live + _live = NULL; + rm.reset_to_mark(); // Reclaim working storage + IndexSet::reset_memory(C, &live_arena); + ifg.init(_maxlrg); + gather_lrg_masks( false ); + live.compute( _maxlrg ); + _live = &live; + } + // Create the interference graph using virtual copies + build_ifg_virtual( ); // Include stack slots this time + + // Aggressive (but pessimistic) copy coalescing. + // This pass works on virtual copies. Any virtual copies which are not + // coalesced get manifested as actual copies + { + // The IFG is/was triangular. I am 'squaring it up' so Union can run + // faster. Union requires a 'for all' operation which is slow on the + // triangular adjacency matrix (quick reminder: the IFG is 'sparse' - + // meaning I can visit all the Nodes neighbors less than a Node in time + // O(# of neighbors), but I have to visit all the Nodes greater than a + // given Node and search them for an instance, i.e., time O(#MaxLRG)). + _ifg->SquareUp(); + + PhaseAggressiveCoalesce coalesce( *this ); + coalesce.coalesce_driver( ); + // Insert un-coalesced copies. Visit all Phis. Where inputs to a Phi do + // not match the Phi itself, insert a copy. + coalesce.insert_copies(_matcher); + } + + // After aggressive coalesce, attempt a first cut at coloring. + // To color, we need the IFG and for that we need LIVE. + { + NOT_PRODUCT( Compile::TracePhase t3("computeLive", &_t_computeLive, TimeCompiler); ) + _live = NULL; + rm.reset_to_mark(); // Reclaim working storage + IndexSet::reset_memory(C, &live_arena); + ifg.init(_maxlrg); + gather_lrg_masks( true ); + live.compute( _maxlrg ); + _live = &live; + } + + // Build physical interference graph + uint must_spill = 0; + must_spill = build_ifg_physical( &live_arena ); + // If we have a guaranteed spill, might as well spill now + if( must_spill ) { + if( !_maxlrg ) return; + // Bail out if unique gets too large (ie - unique > MaxNodeLimit) + C->check_node_count(10*must_spill, "out of nodes before split"); + if (C->failing()) return; + _maxlrg = Split( _maxlrg ); // Split spilling LRG everywhere + // Bail out if unique gets too large (ie - unique > MaxNodeLimit - 2*NodeLimitFudgeFactor) + // or we failed to split + C->check_node_count(2*NodeLimitFudgeFactor, "out of nodes after physical split"); + if (C->failing()) return; + +#ifdef ASSERT + if( VerifyOpto ) { + _cfg.verify(); + verify_base_ptrs(&live_arena); + } +#endif + NOT_PRODUCT( C->verify_graph_edges(); ) + + compact(); // Compact LRGs; return new lower max lrg + + { + NOT_PRODUCT( Compile::TracePhase t3("computeLive", &_t_computeLive, TimeCompiler); ) + _live = NULL; + rm.reset_to_mark(); // Reclaim working storage + IndexSet::reset_memory(C, &live_arena); + ifg.init(_maxlrg); // Build a new interference graph + gather_lrg_masks( true ); // Collect intersect mask + live.compute( _maxlrg ); // Compute LIVE + _live = &live; + } + build_ifg_physical( &live_arena ); + _ifg->SquareUp(); + _ifg->Compute_Effective_Degree(); + // Only do conservative coalescing if requested + if( OptoCoalesce ) { + // Conservative (and pessimistic) copy coalescing of those spills + PhaseConservativeCoalesce coalesce( *this ); + // If max live ranges greater than cutoff, don't color the stack. + // This cutoff can be larger than below since it is only done once. + coalesce.coalesce_driver( ); + } + compress_uf_map_for_nodes(); + +#ifdef ASSERT + if( VerifyOpto ) _ifg->verify(this); +#endif + } else { + ifg.SquareUp(); + ifg.Compute_Effective_Degree(); +#ifdef ASSERT + set_was_low(); +#endif + } + + // Prepare for Simplify & Select + cache_lrg_info(); // Count degree of LRGs + + // Simplify the InterFerence Graph by removing LRGs of low degree. + // LRGs of low degree are trivially colorable. + Simplify(); + + // Select colors by re-inserting LRGs back into the IFG in reverse order. + // Return whether or not something spills. + uint spills = Select( ); + + // If we spill, split and recycle the entire thing + while( spills ) { + if( _trip_cnt++ > 24 ) { + DEBUG_ONLY( dump_for_spill_split_recycle(); ) + if( _trip_cnt > 27 ) { + C->record_method_not_compilable("failed spill-split-recycle sanity check"); + return; + } + } + + if( !_maxlrg ) return; + _maxlrg = Split( _maxlrg ); // Split spilling LRG everywhere + // Bail out if unique gets too large (ie - unique > MaxNodeLimit - 2*NodeLimitFudgeFactor) + C->check_node_count(2*NodeLimitFudgeFactor, "out of nodes after split"); + if (C->failing()) return; +#ifdef ASSERT + if( VerifyOpto ) { + _cfg.verify(); + verify_base_ptrs(&live_arena); + } +#endif + + compact(); // Compact LRGs; return new lower max lrg + + // Nuke the live-ness and interference graph and LiveRanGe info + { + NOT_PRODUCT( Compile::TracePhase t3("computeLive", &_t_computeLive, TimeCompiler); ) + _live = NULL; + rm.reset_to_mark(); // Reclaim working storage + IndexSet::reset_memory(C, &live_arena); + ifg.init(_maxlrg); + + // Create LiveRanGe array. + // Intersect register masks for all USEs and DEFs + gather_lrg_masks( true ); + live.compute( _maxlrg ); + _live = &live; + } + must_spill = build_ifg_physical( &live_arena ); + _ifg->SquareUp(); + _ifg->Compute_Effective_Degree(); + + // Only do conservative coalescing if requested + if( OptoCoalesce ) { + // Conservative (and pessimistic) copy coalescing + PhaseConservativeCoalesce coalesce( *this ); + // Check for few live ranges determines how aggressive coalesce is. + coalesce.coalesce_driver( ); + } + compress_uf_map_for_nodes(); +#ifdef ASSERT + if( VerifyOpto ) _ifg->verify(this); +#endif + cache_lrg_info(); // Count degree of LRGs + + // Simplify the InterFerence Graph by removing LRGs of low degree. + // LRGs of low degree are trivially colorable. + Simplify(); + + // Select colors by re-inserting LRGs back into the IFG in reverse order. + // Return whether or not something spills. + spills = Select( ); + } + + // Count number of Simplify-Select trips per coloring success. + _allocator_attempts += _trip_cnt + 1; + _allocator_successes += 1; + + // Peephole remove copies + post_allocate_copy_removal(); + + // max_reg is past the largest *register* used. + // Convert that to a frame_slot number. + if( _max_reg <= _matcher._new_SP ) + _framesize = C->out_preserve_stack_slots(); + else _framesize = _max_reg -_matcher._new_SP; + assert((int)(_matcher._new_SP+_framesize) >= (int)_matcher._out_arg_limit, "framesize must be large enough"); + + // This frame must preserve the required fp alignment + const int stack_alignment_in_words = Matcher::stack_alignment_in_slots(); + if (stack_alignment_in_words > 0) + _framesize = round_to(_framesize, Matcher::stack_alignment_in_bytes()); + assert( _framesize >= 0 && _framesize <= 1000000, "sanity check" ); +#ifndef PRODUCT + _total_framesize += _framesize; + if( (int)_framesize > _max_framesize ) + _max_framesize = _framesize; +#endif + + // Convert CISC spills + fixup_spills(); + + // Log regalloc results + CompileLog* log = Compile::current()->log(); + if (log != NULL) { + log->elem("regalloc attempts='%d' success='%d'", _trip_cnt, !C->failing()); + } + + if (C->failing()) return; + + NOT_PRODUCT( C->verify_graph_edges(); ) + + // Move important info out of the live_arena to longer lasting storage. + alloc_node_regs(_names.Size()); + for( uint i=0; i < _names.Size(); i++ ) { + if( _names[i] ) { // Live range associated with Node? + LRG &lrg = lrgs( _names[i] ); + if( lrg.num_regs() == 1 ) { + _node_regs[i].set1( lrg.reg() ); + } else { // Must be a register-pair + if( !lrg._fat_proj ) { // Must be aligned adjacent register pair + // Live ranges record the highest register in their mask. + // We want the low register for the AD file writer's convenience. + _node_regs[i].set2( OptoReg::add(lrg.reg(),-1) ); + } else { // Misaligned; extract 2 bits + OptoReg::Name hi = lrg.reg(); // Get hi register + lrg.Remove(hi); // Yank from mask + int lo = lrg.mask().find_first_elem(); // Find lo + _node_regs[i].set_pair( hi, lo ); + } + } + if( lrg._is_oop ) _node_oops.set(i); + } else { + _node_regs[i].set_bad(); + } + } + + // Done! + _live = NULL; + _ifg = NULL; + C->set_indexSet_arena(NULL); // ResourceArea is at end of scope +} + +//------------------------------de_ssa----------------------------------------- +void PhaseChaitin::de_ssa() { + // Set initial Names for all Nodes. Most Nodes get the virtual register + // number. A few get the ZERO live range number. These do not + // get allocated, but instead rely on correct scheduling to ensure that + // only one instance is simultaneously live at a time. + uint lr_counter = 1; + for( uint i = 0; i < _cfg._num_blocks; i++ ) { + Block *b = _cfg._blocks[i]; + uint cnt = b->_nodes.size(); + + // Handle all the normal Nodes in the block + for( uint j = 0; j < cnt; j++ ) { + Node *n = b->_nodes[j]; + // Pre-color to the zero live range, or pick virtual register + const RegMask &rm = n->out_RegMask(); + _names.map( n->_idx, rm.is_NotEmpty() ? lr_counter++ : 0 ); + } + } + // Reset the Union-Find mapping to be identity + reset_uf_map(lr_counter); +} + + +//------------------------------gather_lrg_masks------------------------------- +// Gather LiveRanGe information, including register masks. Modification of +// cisc spillable in_RegMasks should not be done before AggressiveCoalesce. +void PhaseChaitin::gather_lrg_masks( bool after_aggressive ) { + + // Nail down the frame pointer live range + uint fp_lrg = n2lidx(_cfg._root->in(1)->in(TypeFunc::FramePtr)); + lrgs(fp_lrg)._cost += 1e12; // Cost is infinite + + // For all blocks + for( uint i = 0; i < _cfg._num_blocks; i++ ) { + Block *b = _cfg._blocks[i]; + + // For all instructions + for( uint j = 1; j < b->_nodes.size(); j++ ) { + Node *n = b->_nodes[j]; + uint input_edge_start =1; // Skip control most nodes + if( n->is_Mach() ) input_edge_start = n->as_Mach()->oper_input_base(); + uint idx = n->is_Copy(); + + // Get virtual register number, same as LiveRanGe index + uint vreg = n2lidx(n); + LRG &lrg = lrgs(vreg); + if( vreg ) { // No vreg means un-allocable (e.g. memory) + + // Collect has-copy bit + if( idx ) { + lrg._has_copy = 1; + uint clidx = n2lidx(n->in(idx)); + LRG ©_src = lrgs(clidx); + copy_src._has_copy = 1; + } + + // Check for float-vs-int live range (used in register-pressure + // calculations) + const Type *n_type = n->bottom_type(); + if( n_type->is_floatingpoint() ) + lrg._is_float = 1; + + // Check for twice prior spilling. Once prior spilling might have + // spilled 'soft', 2nd prior spill should have spilled 'hard' and + // further spilling is unlikely to make progress. + if( _spilled_once.test(n->_idx) ) { + lrg._was_spilled1 = 1; + if( _spilled_twice.test(n->_idx) ) + lrg._was_spilled2 = 1; + } + +#ifndef PRODUCT + if (trace_spilling() && lrg._def != NULL) { + // collect defs for MultiDef printing + if (lrg._defs == NULL) { + lrg._defs = new (_ifg->_arena) GrowableArray(); + lrg._defs->append(lrg._def); + } + lrg._defs->append(n); + } +#endif + + // Check for a single def LRG; these can spill nicely + // via rematerialization. Flag as NULL for no def found + // yet, or 'n' for single def or -1 for many defs. + lrg._def = lrg._def ? NodeSentinel : n; + + // Limit result register mask to acceptable registers + const RegMask &rm = n->out_RegMask(); + lrg.AND( rm ); + // Check for bound register masks + const RegMask &lrgmask = lrg.mask(); + if( lrgmask.is_bound1() || lrgmask.is_bound2() ) + lrg._is_bound = 1; + + // Check for maximum frequency value + if( lrg._maxfreq < b->_freq ) + lrg._maxfreq = b->_freq; + + int ireg = n->ideal_reg(); + assert( !n->bottom_type()->isa_oop_ptr() || ireg == Op_RegP, + "oops must be in Op_RegP's" ); + // Check for oop-iness, or long/double + // Check for multi-kill projection + switch( ireg ) { + case MachProjNode::fat_proj: + // Fat projections have size equal to number of registers killed + lrg.set_num_regs(rm.Size()); + lrg.set_reg_pressure(lrg.num_regs()); + lrg._fat_proj = 1; + lrg._is_bound = 1; + break; + case Op_RegP: +#ifdef _LP64 + lrg.set_num_regs(2); // Size is 2 stack words +#else + lrg.set_num_regs(1); // Size is 1 stack word +#endif + // Register pressure is tracked relative to the maximum values + // suggested for that platform, INTPRESSURE and FLOATPRESSURE, + // and relative to other types which compete for the same regs. + // + // The following table contains suggested values based on the + // architectures as defined in each .ad file. + // INTPRESSURE and FLOATPRESSURE may be tuned differently for + // compile-speed or performance. + // Note1: + // SPARC and SPARCV9 reg_pressures are at 2 instead of 1 + // since .ad registers are defined as high and low halves. + // These reg_pressure values remain compatible with the code + // in is_high_pressure() which relates get_invalid_mask_size(), + // Block::_reg_pressure and INTPRESSURE, FLOATPRESSURE. + // Note2: + // SPARC -d32 has 24 registers available for integral values, + // but only 10 of these are safe for 64-bit longs. + // Using set_reg_pressure(2) for both int and long means + // the allocator will believe it can fit 26 longs into + // registers. Using 2 for longs and 1 for ints means the + // allocator will attempt to put 52 integers into registers. + // The settings below limit this problem to methods with + // many long values which are being run on 32-bit SPARC. + // + // ------------------- reg_pressure -------------------- + // Each entry is reg_pressure_per_value,number_of_regs + // RegL RegI RegFlags RegF RegD INTPRESSURE FLOATPRESSURE + // IA32 2 1 1 1 1 6 6 + // IA64 1 1 1 1 1 50 41 + // SPARC 2 2 2 2 2 48 (24) 52 (26) + // SPARCV9 2 2 2 2 2 48 (24) 52 (26) + // AMD64 1 1 1 1 1 14 15 + // ----------------------------------------------------- +#if defined(SPARC) + lrg.set_reg_pressure(2); // use for v9 as well +#else + lrg.set_reg_pressure(1); // normally one value per register +#endif + if( n_type->isa_oop_ptr() ) { + lrg._is_oop = 1; + } + break; + case Op_RegL: // Check for long or double + case Op_RegD: + lrg.set_num_regs(2); + // Define platform specific register pressure +#ifdef SPARC + lrg.set_reg_pressure(2); +#elif defined(IA32) + if( ireg == Op_RegL ) { + lrg.set_reg_pressure(2); + } else { + lrg.set_reg_pressure(1); + } +#else + lrg.set_reg_pressure(1); // normally one value per register +#endif + // If this def of a double forces a mis-aligned double, + // flag as '_fat_proj' - really flag as allowing misalignment + // AND changes how we count interferences. A mis-aligned + // double can interfere with TWO aligned pairs, or effectively + // FOUR registers! + if( rm.is_misaligned_Pair() ) { + lrg._fat_proj = 1; + lrg._is_bound = 1; + } + break; + case Op_RegF: + case Op_RegI: + case Op_RegFlags: + case 0: // not an ideal register + lrg.set_num_regs(1); +#ifdef SPARC + lrg.set_reg_pressure(2); +#else + lrg.set_reg_pressure(1); +#endif + break; + default: + ShouldNotReachHere(); + } + } + + // Now do the same for inputs + uint cnt = n->req(); + // Setup for CISC SPILLING + uint inp = (uint)AdlcVMDeps::Not_cisc_spillable; + if( UseCISCSpill && after_aggressive ) { + inp = n->cisc_operand(); + if( inp != (uint)AdlcVMDeps::Not_cisc_spillable ) + // Convert operand number to edge index number + inp = n->as_Mach()->operand_index(inp); + } + // Prepare register mask for each input + for( uint k = input_edge_start; k < cnt; k++ ) { + uint vreg = n2lidx(n->in(k)); + if( !vreg ) continue; + + // If this instruction is CISC Spillable, add the flags + // bit to its appropriate input + if( UseCISCSpill && after_aggressive && inp == k ) { +#ifndef PRODUCT + if( TraceCISCSpill ) { + tty->print(" use_cisc_RegMask: "); + n->dump(); + } +#endif + n->as_Mach()->use_cisc_RegMask(); + } + + LRG &lrg = lrgs(vreg); + // // Testing for floating point code shape + // Node *test = n->in(k); + // if( test->is_Mach() ) { + // MachNode *m = test->as_Mach(); + // int op = m->ideal_Opcode(); + // if (n->is_Call() && (op == Op_AddF || op == Op_MulF) ) { + // int zzz = 1; + // } + // } + + // Limit result register mask to acceptable registers. + // Do not limit registers from uncommon uses before + // AggressiveCoalesce. This effectively pre-virtual-splits + // around uncommon uses of common defs. + const RegMask &rm = n->in_RegMask(k); + if( !after_aggressive && + _cfg._bbs[n->in(k)->_idx]->_freq > 1000*b->_freq ) { + // Since we are BEFORE aggressive coalesce, leave the register + // mask untrimmed by the call. This encourages more coalescing. + // Later, AFTER aggressive, this live range will have to spill + // but the spiller handles slow-path calls very nicely. + } else { + lrg.AND( rm ); + } + // Check for bound register masks + const RegMask &lrgmask = lrg.mask(); + if( lrgmask.is_bound1() || lrgmask.is_bound2() ) + lrg._is_bound = 1; + // If this use of a double forces a mis-aligned double, + // flag as '_fat_proj' - really flag as allowing misalignment + // AND changes how we count interferences. A mis-aligned + // double can interfere with TWO aligned pairs, or effectively + // FOUR registers! + if( lrg.num_regs() == 2 && !lrg._fat_proj && rm.is_misaligned_Pair() ) { + lrg._fat_proj = 1; + lrg._is_bound = 1; + } + // if the LRG is an unaligned pair, we will have to spill + // so clear the LRG's register mask if it is not already spilled + if ( !n->is_SpillCopy() && + (lrg._def == NULL || lrg._def == NodeSentinel || !lrg._def->is_SpillCopy()) && + lrgmask.is_misaligned_Pair()) { + lrg.Clear(); + } + + // Check for maximum frequency value + if( lrg._maxfreq < b->_freq ) + lrg._maxfreq = b->_freq; + + } // End for all allocated inputs + } // end for all instructions + } // end for all blocks + + // Final per-liverange setup + for( uint i2=0; i2<_maxlrg; i2++ ) { + LRG &lrg = lrgs(i2); + if( lrg.num_regs() == 2 && !lrg._fat_proj ) + lrg.ClearToPairs(); + lrg.compute_set_mask_size(); + if( lrg.not_free() ) { // Handle case where we lose from the start + lrg.set_reg(OptoReg::Name(LRG::SPILL_REG)); + lrg._direct_conflict = 1; + } + lrg.set_degree(0); // no neighbors in IFG yet + } +} + +//------------------------------set_was_low------------------------------------ +// Set the was-lo-degree bit. Conservative coalescing should not change the +// colorability of the graph. If any live range was of low-degree before +// coalescing, it should Simplify. This call sets the was-lo-degree bit. +// The bit is checked in Simplify. +void PhaseChaitin::set_was_low() { +#ifdef ASSERT + for( uint i = 1; i < _maxlrg; i++ ) { + int size = lrgs(i).num_regs(); + uint old_was_lo = lrgs(i)._was_lo; + lrgs(i)._was_lo = 0; + if( lrgs(i).lo_degree() ) { + lrgs(i)._was_lo = 1; // Trivially of low degree + } else { // Else check the Brigg's assertion + // Brigg's observation is that the lo-degree neighbors of a + // hi-degree live range will not interfere with the color choices + // of said hi-degree live range. The Simplify reverse-stack-coloring + // order takes care of the details. Hence you do not have to count + // low-degree neighbors when determining if this guy colors. + int briggs_degree = 0; + IndexSet *s = _ifg->neighbors(i); + IndexSetIterator elements(s); + uint lidx; + while((lidx = elements.next()) != 0) { + if( !lrgs(lidx).lo_degree() ) + briggs_degree += MAX2(size,lrgs(lidx).num_regs()); + } + if( briggs_degree < lrgs(i).degrees_of_freedom() ) + lrgs(i)._was_lo = 1; // Low degree via the briggs assertion + } + assert(old_was_lo <= lrgs(i)._was_lo, "_was_lo may not decrease"); + } +#endif +} + +#define REGISTER_CONSTRAINED 16 + +//------------------------------cache_lrg_info--------------------------------- +// Compute cost/area ratio, in case we spill. Build the lo-degree list. +void PhaseChaitin::cache_lrg_info( ) { + + for( uint i = 1; i < _maxlrg; i++ ) { + LRG &lrg = lrgs(i); + + // Check for being of low degree: means we can be trivially colored. + // Low degree, dead or must-spill guys just get to simplify right away + if( lrg.lo_degree() || + !lrg.alive() || + lrg._must_spill ) { + // Split low degree list into those guys that must get a + // register and those that can go to register or stack. + // The idea is LRGs that can go register or stack color first when + // they have a good chance of getting a register. The register-only + // lo-degree live ranges always get a register. + OptoReg::Name hi_reg = lrg.mask().find_last_elem(); + if( OptoReg::is_stack(hi_reg)) { // Can go to stack? + lrg._next = _lo_stk_degree; + _lo_stk_degree = i; + } else { + lrg._next = _lo_degree; + _lo_degree = i; + } + } else { // Else high degree + lrgs(_hi_degree)._prev = i; + lrg._next = _hi_degree; + lrg._prev = 0; + _hi_degree = i; + } + } +} + +//------------------------------Pre-Simplify----------------------------------- +// Simplify the IFG by removing LRGs of low degree that have NO copies +void PhaseChaitin::Pre_Simplify( ) { + + // Warm up the lo-degree no-copy list + int lo_no_copy = 0; + for( uint i = 1; i < _maxlrg; i++ ) { + if( (lrgs(i).lo_degree() && !lrgs(i)._has_copy) || + !lrgs(i).alive() || + lrgs(i)._must_spill ) { + lrgs(i)._next = lo_no_copy; + lo_no_copy = i; + } + } + + while( lo_no_copy ) { + uint lo = lo_no_copy; + lo_no_copy = lrgs(lo)._next; + int size = lrgs(lo).num_regs(); + + // Put the simplified guy on the simplified list. + lrgs(lo)._next = _simplified; + _simplified = lo; + + // Yank this guy from the IFG. + IndexSet *adj = _ifg->remove_node( lo ); + + // If any neighbors' degrees fall below their number of + // allowed registers, then put that neighbor on the low degree + // list. Note that 'degree' can only fall and 'numregs' is + // unchanged by this action. Thus the two are equal at most once, + // so LRGs hit the lo-degree worklists at most once. + IndexSetIterator elements(adj); + uint neighbor; + while ((neighbor = elements.next()) != 0) { + LRG *n = &lrgs(neighbor); + assert( _ifg->effective_degree(neighbor) == n->degree(), "" ); + + // Check for just becoming of-low-degree + if( n->just_lo_degree() && !n->_has_copy ) { + assert(!(*_ifg->_yanked)[neighbor],"Cannot move to lo degree twice"); + // Put on lo-degree list + n->_next = lo_no_copy; + lo_no_copy = neighbor; + } + } + } // End of while lo-degree no_copy worklist not empty + + // No more lo-degree no-copy live ranges to simplify +} + +//------------------------------Simplify--------------------------------------- +// Simplify the IFG by removing LRGs of low degree. +void PhaseChaitin::Simplify( ) { + + while( 1 ) { // Repeat till simplified it all + // May want to explore simplifying lo_degree before _lo_stk_degree. + // This might result in more spills coloring into registers during + // Select(). + while( _lo_degree || _lo_stk_degree ) { + // If possible, pull from lo_stk first + uint lo; + if( _lo_degree ) { + lo = _lo_degree; + _lo_degree = lrgs(lo)._next; + } else { + lo = _lo_stk_degree; + _lo_stk_degree = lrgs(lo)._next; + } + + // Put the simplified guy on the simplified list. + lrgs(lo)._next = _simplified; + _simplified = lo; + // If this guy is "at risk" then mark his current neighbors + if( lrgs(lo)._at_risk ) { + IndexSetIterator elements(_ifg->neighbors(lo)); + uint datum; + while ((datum = elements.next()) != 0) { + lrgs(datum)._risk_bias = lo; + } + } + + // Yank this guy from the IFG. + IndexSet *adj = _ifg->remove_node( lo ); + + // If any neighbors' degrees fall below their number of + // allowed registers, then put that neighbor on the low degree + // list. Note that 'degree' can only fall and 'numregs' is + // unchanged by this action. Thus the two are equal at most once, + // so LRGs hit the lo-degree worklist at most once. + IndexSetIterator elements(adj); + uint neighbor; + while ((neighbor = elements.next()) != 0) { + LRG *n = &lrgs(neighbor); +#ifdef ASSERT + if( VerifyOpto ) { + assert( _ifg->effective_degree(neighbor) == n->degree(), "" ); + } +#endif + + // Check for just becoming of-low-degree just counting registers. + // _must_spill live ranges are already on the low degree list. + if( n->just_lo_degree() && !n->_must_spill ) { + assert(!(*_ifg->_yanked)[neighbor],"Cannot move to lo degree twice"); + // Pull from hi-degree list + uint prev = n->_prev; + uint next = n->_next; + if( prev ) lrgs(prev)._next = next; + else _hi_degree = next; + lrgs(next)._prev = prev; + n->_next = _lo_degree; + _lo_degree = neighbor; + } + } + } // End of while lo-degree/lo_stk_degree worklist not empty + + // Check for got everything: is hi-degree list empty? + if( !_hi_degree ) break; + + // Time to pick a potential spill guy + uint lo_score = _hi_degree; + double score = lrgs(lo_score).score(); + double area = lrgs(lo_score)._area; + + // Find cheapest guy + debug_only( int lo_no_simplify=0; ); + for( uint i = _hi_degree; i; i = lrgs(i)._next ) { + assert( !(*_ifg->_yanked)[i], "" ); + // It's just vaguely possible to move hi-degree to lo-degree without + // going through a just-lo-degree stage: If you remove a double from + // a float live range it's degree will drop by 2 and you can skip the + // just-lo-degree stage. It's very rare (shows up after 5000+ methods + // in -Xcomp of Java2Demo). So just choose this guy to simplify next. + if( lrgs(i).lo_degree() ) { + lo_score = i; + break; + } + debug_only( if( lrgs(i)._was_lo ) lo_no_simplify=i; ); + double iscore = lrgs(i).score(); + double iarea = lrgs(i)._area; + + // Compare cost/area of i vs cost/area of lo_score. Smaller cost/area + // wins. Ties happen because all live ranges in question have spilled + // a few times before and the spill-score adds a huge number which + // washes out the low order bits. We are choosing the lesser of 2 + // evils; in this case pick largest area to spill. + if( iscore < score || + (iscore == score && iarea > area && lrgs(lo_score)._was_spilled2) ) { + lo_score = i; + score = iscore; + area = iarea; + } + } + LRG *lo_lrg = &lrgs(lo_score); + // The live range we choose for spilling is either hi-degree, or very + // rarely it can be low-degree. If we choose a hi-degree live range + // there better not be any lo-degree choices. + assert( lo_lrg->lo_degree() || !lo_no_simplify, "Live range was lo-degree before coalesce; should simplify" ); + + // Pull from hi-degree list + uint prev = lo_lrg->_prev; + uint next = lo_lrg->_next; + if( prev ) lrgs(prev)._next = next; + else _hi_degree = next; + lrgs(next)._prev = prev; + // Jam him on the lo-degree list, despite his high degree. + // Maybe he'll get a color, and maybe he'll spill. + // Only Select() will know. + lrgs(lo_score)._at_risk = true; + _lo_degree = lo_score; + lo_lrg->_next = 0; + + } // End of while not simplified everything + +} + +//------------------------------bias_color------------------------------------- +// Choose a color using the biasing heuristic +OptoReg::Name PhaseChaitin::bias_color( LRG &lrg, int chunk ) { + + // Check for "at_risk" LRG's + uint risk_lrg = Find(lrg._risk_bias); + if( risk_lrg != 0 ) { + // Walk the colored neighbors of the "at_risk" candidate + // Choose a color which is both legal and already taken by a neighbor + // of the "at_risk" candidate in order to improve the chances of the + // "at_risk" candidate of coloring + IndexSetIterator elements(_ifg->neighbors(risk_lrg)); + uint datum; + while ((datum = elements.next()) != 0) { + OptoReg::Name reg = lrgs(datum).reg(); + // If this LRG's register is legal for us, choose it + if( reg >= chunk && reg < chunk + RegMask::CHUNK_SIZE && + lrg.mask().Member(OptoReg::add(reg,-chunk)) && + (lrg.num_regs()==1 || // either size 1 + (reg&1) == 1) ) // or aligned (adjacent reg is available since we already cleared-to-pairs) + return reg; + } + } + + uint copy_lrg = Find(lrg._copy_bias); + if( copy_lrg != 0 ) { + // If he has a color, + if( !(*(_ifg->_yanked))[copy_lrg] ) { + OptoReg::Name reg = lrgs(copy_lrg).reg(); + // And it is legal for you, + if( reg >= chunk && reg < chunk + RegMask::CHUNK_SIZE && + lrg.mask().Member(OptoReg::add(reg,-chunk)) && + (lrg.num_regs()==1 || // either size 1 + (reg&1) == 1) ) // or aligned (adjacent reg is available since we already cleared-to-pairs) + return reg; + } else if( chunk == 0 ) { + // Choose a color which is legal for him + RegMask tempmask = lrg.mask(); + tempmask.AND(lrgs(copy_lrg).mask()); + OptoReg::Name reg; + if( lrg.num_regs() == 1 ) { + reg = tempmask.find_first_elem(); + } else { + tempmask.ClearToPairs(); + reg = tempmask.find_first_pair(); + } + if( OptoReg::is_valid(reg) ) + return reg; + } + } + + // If no bias info exists, just go with the register selection ordering + if( lrg.num_regs() == 2 ) { + // Find an aligned pair + return OptoReg::add(lrg.mask().find_first_pair(),chunk); + } + + // CNC - Fun hack. Alternate 1st and 2nd selection. Enables post-allocate + // copy removal to remove many more copies, by preventing a just-assigned + // register from being repeatedly assigned. + OptoReg::Name reg = lrg.mask().find_first_elem(); + if( (++_alternate & 1) && OptoReg::is_valid(reg) ) { + // This 'Remove; find; Insert' idiom is an expensive way to find the + // SECOND element in the mask. + lrg.Remove(reg); + OptoReg::Name reg2 = lrg.mask().find_first_elem(); + lrg.Insert(reg); + if( OptoReg::is_reg(reg2)) + reg = reg2; + } + return OptoReg::add( reg, chunk ); +} + +//------------------------------choose_color----------------------------------- +// Choose a color in the current chunk +OptoReg::Name PhaseChaitin::choose_color( LRG &lrg, int chunk ) { + assert( C->in_preserve_stack_slots() == 0 || chunk != 0 || lrg._is_bound || lrg.mask().is_bound1() || !lrg.mask().Member(OptoReg::Name(_matcher._old_SP-1)), "must not allocate stack0 (inside preserve area)"); + assert(C->out_preserve_stack_slots() == 0 || chunk != 0 || lrg._is_bound || lrg.mask().is_bound1() || !lrg.mask().Member(OptoReg::Name(_matcher._old_SP+0)), "must not allocate stack0 (inside preserve area)"); + + if( lrg.num_regs() == 1 || // Common Case + !lrg._fat_proj ) // Aligned+adjacent pairs ok + // Use a heuristic to "bias" the color choice + return bias_color(lrg, chunk); + + assert( lrg.num_regs() >= 2, "dead live ranges do not color" ); + + // Fat-proj case or misaligned double argument. + assert(lrg.compute_mask_size() == lrg.num_regs() || + lrg.num_regs() == 2,"fat projs exactly color" ); + assert( !chunk, "always color in 1st chunk" ); + // Return the highest element in the set. + return lrg.mask().find_last_elem(); +} + +//------------------------------Select----------------------------------------- +// Select colors by re-inserting LRGs back into the IFG. LRGs are re-inserted +// in reverse order of removal. As long as nothing of hi-degree was yanked, +// everything going back is guaranteed a color. Select that color. If some +// hi-degree LRG cannot get a color then we record that we must spill. +uint PhaseChaitin::Select( ) { + uint spill_reg = LRG::SPILL_REG; + _max_reg = OptoReg::Name(0); // Past max register used + while( _simplified ) { + // Pull next LRG from the simplified list - in reverse order of removal + uint lidx = _simplified; + LRG *lrg = &lrgs(lidx); + _simplified = lrg->_next; + + +#ifndef PRODUCT + if (trace_spilling()) { + ttyLocker ttyl; + tty->print_cr("L%d selecting degree %d degrees_of_freedom %d", lidx, lrg->degree(), + lrg->degrees_of_freedom()); + lrg->dump(); + } +#endif + + // Re-insert into the IFG + _ifg->re_insert(lidx); + if( !lrg->alive() ) continue; + // capture allstackedness flag before mask is hacked + const int is_allstack = lrg->mask().is_AllStack(); + + // Yeah, yeah, yeah, I know, I know. I can refactor this + // to avoid the GOTO, although the refactored code will not + // be much clearer. We arrive here IFF we have a stack-based + // live range that cannot color in the current chunk, and it + // has to move into the next free stack chunk. + int chunk = 0; // Current chunk is first chunk + retry_next_chunk: + + // Remove neighbor colors + IndexSet *s = _ifg->neighbors(lidx); + + debug_only(RegMask orig_mask = lrg->mask();) + IndexSetIterator elements(s); + uint neighbor; + while ((neighbor = elements.next()) != 0) { + // Note that neighbor might be a spill_reg. In this case, exclusion + // of its color will be a no-op, since the spill_reg chunk is in outer + // space. Also, if neighbor is in a different chunk, this exclusion + // will be a no-op. (Later on, if lrg runs out of possible colors in + // its chunk, a new chunk of color may be tried, in which case + // examination of neighbors is started again, at retry_next_chunk.) + LRG &nlrg = lrgs(neighbor); + OptoReg::Name nreg = nlrg.reg(); + // Only subtract masks in the same chunk + if( nreg >= chunk && nreg < chunk + RegMask::CHUNK_SIZE ) { +#ifndef PRODUCT + uint size = lrg->mask().Size(); + RegMask rm = lrg->mask(); +#endif + lrg->SUBTRACT(nlrg.mask()); +#ifndef PRODUCT + if (trace_spilling() && lrg->mask().Size() != size) { + ttyLocker ttyl; + tty->print("L%d ", lidx); + rm.dump(); + tty->print(" intersected L%d ", neighbor); + nlrg.mask().dump(); + tty->print(" removed "); + rm.SUBTRACT(lrg->mask()); + rm.dump(); + tty->print(" leaving "); + lrg->mask().dump(); + tty->cr(); + } +#endif + } + } + //assert(is_allstack == lrg->mask().is_AllStack(), "nbrs must not change AllStackedness"); + // Aligned pairs need aligned masks + if( lrg->num_regs() == 2 && !lrg->_fat_proj ) + lrg->ClearToPairs(); + + // Check if a color is available and if so pick the color + OptoReg::Name reg = choose_color( *lrg, chunk ); +#ifdef SPARC + debug_only(lrg->compute_set_mask_size()); + assert(lrg->num_regs() != 2 || lrg->is_bound() || is_even(reg-1), "allocate all doubles aligned"); +#endif + + //--------------- + // If we fail to color and the AllStack flag is set, trigger + // a chunk-rollover event + if(!OptoReg::is_valid(OptoReg::add(reg,-chunk)) && is_allstack) { + // Bump register mask up to next stack chunk + chunk += RegMask::CHUNK_SIZE; + lrg->Set_All(); + + goto retry_next_chunk; + } + + //--------------- + // Did we get a color? + else if( OptoReg::is_valid(reg)) { +#ifndef PRODUCT + RegMask avail_rm = lrg->mask(); +#endif + + // Record selected register + lrg->set_reg(reg); + + if( reg >= _max_reg ) // Compute max register limit + _max_reg = OptoReg::add(reg,1); + // Fold reg back into normal space + reg = OptoReg::add(reg,-chunk); + + // If the live range is not bound, then we actually had some choices + // to make. In this case, the mask has more bits in it than the colors + // choosen. Restrict the mask to just what was picked. + if( lrg->num_regs() == 1 ) { // Size 1 live range + lrg->Clear(); // Clear the mask + lrg->Insert(reg); // Set regmask to match selected reg + lrg->set_mask_size(1); + } else if( !lrg->_fat_proj ) { + // For pairs, also insert the low bit of the pair + assert( lrg->num_regs() == 2, "unbound fatproj???" ); + lrg->Clear(); // Clear the mask + lrg->Insert(reg); // Set regmask to match selected reg + lrg->Insert(OptoReg::add(reg,-1)); + lrg->set_mask_size(2); + } else { // Else fatproj + // mask must be equal to fatproj bits, by definition + } +#ifndef PRODUCT + if (trace_spilling()) { + ttyLocker ttyl; + tty->print("L%d selected ", lidx); + lrg->mask().dump(); + tty->print(" from "); + avail_rm.dump(); + tty->cr(); + } +#endif + // Note that reg is the highest-numbered register in the newly-bound mask. + } // end color available case + + //--------------- + // Live range is live and no colors available + else { + assert( lrg->alive(), "" ); + assert( !lrg->_fat_proj || lrg->_def == NodeSentinel || + lrg->_def->outcnt() > 0, "fat_proj cannot spill"); + assert( !orig_mask.is_AllStack(), "All Stack does not spill" ); + + // Assign the special spillreg register + lrg->set_reg(OptoReg::Name(spill_reg++)); + // Do not empty the regmask; leave mask_size lying around + // for use during Spilling +#ifndef PRODUCT + if( trace_spilling() ) { + ttyLocker ttyl; + tty->print("L%d spilling with neighbors: ", lidx); + s->dump(); + debug_only(tty->print(" original mask: ")); + debug_only(orig_mask.dump()); + dump_lrg(lidx); + } +#endif + } // end spill case + + } + + return spill_reg-LRG::SPILL_REG; // Return number of spills +} + + +//------------------------------copy_was_spilled------------------------------- +// Copy 'was_spilled'-edness from the source Node to the dst Node. +void PhaseChaitin::copy_was_spilled( Node *src, Node *dst ) { + if( _spilled_once.test(src->_idx) ) { + _spilled_once.set(dst->_idx); + lrgs(Find(dst))._was_spilled1 = 1; + if( _spilled_twice.test(src->_idx) ) { + _spilled_twice.set(dst->_idx); + lrgs(Find(dst))._was_spilled2 = 1; + } + } +} + +//------------------------------set_was_spilled-------------------------------- +// Set the 'spilled_once' or 'spilled_twice' flag on a node. +void PhaseChaitin::set_was_spilled( Node *n ) { + if( _spilled_once.test_set(n->_idx) ) + _spilled_twice.set(n->_idx); +} + +//------------------------------fixup_spills----------------------------------- +// Convert Ideal spill instructions into proper FramePtr + offset Loads and +// Stores. Use-def chains are NOT preserved, but Node->LRG->reg maps are. +void PhaseChaitin::fixup_spills() { + // This function does only cisc spill work. + if( !UseCISCSpill ) return; + + NOT_PRODUCT( Compile::TracePhase t3("fixupSpills", &_t_fixupSpills, TimeCompiler); ) + + // Grab the Frame Pointer + Node *fp = _cfg._broot->head()->in(1)->in(TypeFunc::FramePtr); + + // For all blocks + for( uint i = 0; i < _cfg._num_blocks; i++ ) { + Block *b = _cfg._blocks[i]; + + // For all instructions in block + uint last_inst = b->end_idx(); + for( uint j = 1; j <= last_inst; j++ ) { + Node *n = b->_nodes[j]; + + // Dead instruction??? + assert( n->outcnt() != 0 ||// Nothing dead after post alloc + C->top() == n || // Or the random TOP node + n->is_Proj(), // Or a fat-proj kill node + "No dead instructions after post-alloc" ); + + int inp = n->cisc_operand(); + if( inp != AdlcVMDeps::Not_cisc_spillable ) { + // Convert operand number to edge index number + MachNode *mach = n->as_Mach(); + inp = mach->operand_index(inp); + Node *src = n->in(inp); // Value to load or store + LRG &lrg_cisc = lrgs( Find_const(src) ); + OptoReg::Name src_reg = lrg_cisc.reg(); + // Doubles record the HIGH register of an adjacent pair. + src_reg = OptoReg::add(src_reg,1-lrg_cisc.num_regs()); + if( OptoReg::is_stack(src_reg) ) { // If input is on stack + // This is a CISC Spill, get stack offset and construct new node +#ifndef PRODUCT + if( TraceCISCSpill ) { + tty->print(" reg-instr: "); + n->dump(); + } +#endif + int stk_offset = reg2offset(src_reg); + // Bailout if we might exceed node limit when spilling this instruction + C->check_node_count(0, "out of nodes fixing spills"); + if (C->failing()) return; + // Transform node + MachNode *cisc = mach->cisc_version(stk_offset, C)->as_Mach(); + cisc->set_req(inp,fp); // Base register is frame pointer + if( cisc->oper_input_base() > 1 && mach->oper_input_base() <= 1 ) { + assert( cisc->oper_input_base() == 2, "Only adding one edge"); + cisc->ins_req(1,src); // Requires a memory edge + } + b->_nodes.map(j,cisc); // Insert into basic block + n->replace_by(cisc); // Correct graph + // + ++_used_cisc_instructions; +#ifndef PRODUCT + if( TraceCISCSpill ) { + tty->print(" cisc-instr: "); + cisc->dump(); + } +#endif + } else { +#ifndef PRODUCT + if( TraceCISCSpill ) { + tty->print(" using reg-instr: "); + n->dump(); + } +#endif + ++_unused_cisc_instructions; // input can be on stack + } + } + + } // End of for all instructions + + } // End of for all blocks +} + +//------------------------------find_base_for_derived-------------------------- +// Helper to stretch above; recursively discover the base Node for a +// given derived Node. Easy for AddP-related machine nodes, but needs +// to be recursive for derived Phis. +Node *PhaseChaitin::find_base_for_derived( Node **derived_base_map, Node *derived, uint &maxlrg ) { + // See if already computed; if so return it + if( derived_base_map[derived->_idx] ) + return derived_base_map[derived->_idx]; + + // See if this happens to be a base. + // NOTE: we use TypePtr instead of TypeOopPtr because we can have + // pointers derived from NULL! These are always along paths that + // can't happen at run-time but the optimizer cannot deduce it so + // we have to handle it gracefully. + const TypePtr *tj = derived->bottom_type()->isa_ptr(); + // If its an OOP with a non-zero offset, then it is derived. + if( tj->_offset == 0 ) { + derived_base_map[derived->_idx] = derived; + return derived; + } + // Derived is NULL+offset? Base is NULL! + if( derived->is_Con() ) { + Node *base = new (C, 1) ConPNode( TypePtr::NULL_PTR ); + uint no_lidx = 0; // an unmatched constant in debug info has no LRG + _names.extend(base->_idx, no_lidx); + derived_base_map[derived->_idx] = base; + return base; + } + + // Check for AddP-related opcodes + if( !derived->is_Phi() ) { + assert( derived->as_Mach()->ideal_Opcode() == Op_AddP, "" ); + Node *base = derived->in(AddPNode::Base); + derived_base_map[derived->_idx] = base; + return base; + } + + // Recursively find bases for Phis. + // First check to see if we can avoid a base Phi here. + Node *base = find_base_for_derived( derived_base_map, derived->in(1),maxlrg); + uint i; + for( i = 2; i < derived->req(); i++ ) + if( base != find_base_for_derived( derived_base_map,derived->in(i),maxlrg)) + break; + // Went to the end without finding any different bases? + if( i == derived->req() ) { // No need for a base Phi here + derived_base_map[derived->_idx] = base; + return base; + } + + // Now we see we need a base-Phi here to merge the bases + base = new (C, derived->req()) PhiNode( derived->in(0), base->bottom_type() ); + for( i = 1; i < derived->req(); i++ ) + base->init_req(i, find_base_for_derived(derived_base_map, derived->in(i), maxlrg)); + + // Search the current block for an existing base-Phi + Block *b = _cfg._bbs[derived->_idx]; + for( i = 1; i <= b->end_idx(); i++ ) {// Search for matching Phi + Node *phi = b->_nodes[i]; + if( !phi->is_Phi() ) { // Found end of Phis with no match? + b->_nodes.insert( i, base ); // Must insert created Phi here as base + _cfg._bbs.map( base->_idx, b ); + new_lrg(base,maxlrg++); + break; + } + // See if Phi matches. + uint j; + for( j = 1; j < base->req(); j++ ) + if( phi->in(j) != base->in(j) && + !(phi->in(j)->is_Con() && base->in(j)->is_Con()) ) // allow different NULLs + break; + if( j == base->req() ) { // All inputs match? + base = phi; // Then use existing 'phi' and drop 'base' + break; + } + } + + + // Cache info for later passes + derived_base_map[derived->_idx] = base; + return base; +} + + +//------------------------------stretch_base_pointer_live_ranges--------------- +// At each Safepoint, insert extra debug edges for each pair of derived value/ +// base pointer that is live across the Safepoint for oopmap building. The +// edge pairs get added in after sfpt->jvmtail()->oopoff(), but are in the +// required edge set. +bool PhaseChaitin::stretch_base_pointer_live_ranges( ResourceArea *a ) { + int must_recompute_live = false; + uint maxlrg = _maxlrg; + Node **derived_base_map = (Node**)a->Amalloc(sizeof(Node*)*C->unique()); + memset( derived_base_map, 0, sizeof(Node*)*C->unique() ); + + // For all blocks in RPO do... + for( uint i=0; i<_cfg._num_blocks; i++ ) { + Block *b = _cfg._blocks[i]; + // Note use of deep-copy constructor. I cannot hammer the original + // liveout bits, because they are needed by the following coalesce pass. + IndexSet liveout(_live->live(b)); + + for( uint j = b->end_idx() + 1; j > 1; j-- ) { + Node *n = b->_nodes[j-1]; + + // Pre-split compares of loop-phis. Loop-phis form a cycle we would + // like to see in the same register. Compare uses the loop-phi and so + // extends its live range BUT cannot be part of the cycle. If this + // extended live range overlaps with the update of the loop-phi value + // we need both alive at the same time -- which requires at least 1 + // copy. But because Intel has only 2-address registers we end up with + // at least 2 copies, one before the loop-phi update instruction and + // one after. Instead we split the input to the compare just after the + // phi. + if( n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_CmpI ) { + Node *phi = n->in(1); + if( phi->is_Phi() && phi->as_Phi()->region()->is_Loop() ) { + Block *phi_block = _cfg._bbs[phi->_idx]; + if( _cfg._bbs[phi_block->pred(2)->_idx] == b ) { + const RegMask *mask = C->matcher()->idealreg2spillmask[Op_RegI]; + Node *spill = new (C) MachSpillCopyNode( phi, *mask, *mask ); + insert_proj( phi_block, 1, spill, maxlrg++ ); + n->set_req(1,spill); + must_recompute_live = true; + } + } + } + + // Get value being defined + uint lidx = n2lidx(n); + if( lidx && lidx < _maxlrg /* Ignore the occasional brand-new live range */) { + // Remove from live-out set + liveout.remove(lidx); + + // Copies do not define a new value and so do not interfere. + // Remove the copies source from the liveout set before interfering. + uint idx = n->is_Copy(); + if( idx ) liveout.remove( n2lidx(n->in(idx)) ); + } + + // Found a safepoint? + JVMState *jvms = n->jvms(); + if( jvms ) { + // Now scan for a live derived pointer + IndexSetIterator elements(&liveout); + uint neighbor; + while ((neighbor = elements.next()) != 0) { + // Find reaching DEF for base and derived values + // This works because we are still in SSA during this call. + Node *derived = lrgs(neighbor)._def; + const TypePtr *tj = derived->bottom_type()->isa_ptr(); + // If its an OOP with a non-zero offset, then it is derived. + if( tj && tj->_offset != 0 && tj->isa_oop_ptr() ) { + Node *base = find_base_for_derived( derived_base_map, derived, maxlrg ); + assert( base->_idx < _names.Size(), "" ); + // Add reaching DEFs of derived pointer and base pointer as a + // pair of inputs + n->add_req( derived ); + n->add_req( base ); + + // See if the base pointer is already live to this point. + // Since I'm working on the SSA form, live-ness amounts to + // reaching def's. So if I find the base's live range then + // I know the base's def reaches here. + if( (n2lidx(base) >= _maxlrg ||// (Brand new base (hence not live) or + !liveout.member( n2lidx(base) ) ) && // not live) AND + (n2lidx(base) > 0) && // not a constant + _cfg._bbs[base->_idx] != b ) { // base not def'd in blk) + // Base pointer is not currently live. Since I stretched + // the base pointer to here and it crosses basic-block + // boundaries, the global live info is now incorrect. + // Recompute live. + must_recompute_live = true; + } // End of if base pointer is not live to debug info + } + } // End of scan all live data for derived ptrs crossing GC point + } // End of if found a GC point + + // Make all inputs live + if( !n->is_Phi() ) { // Phi function uses come from prior block + for( uint k = 1; k < n->req(); k++ ) { + uint lidx = n2lidx(n->in(k)); + if( lidx < _maxlrg ) + liveout.insert( lidx ); + } + } + + } // End of forall instructions in block + liveout.clear(); // Free the memory used by liveout. + + } // End of forall blocks + _maxlrg = maxlrg; + + // If I created a new live range I need to recompute live + if( maxlrg != _ifg->_maxlrg ) + must_recompute_live = true; + + return must_recompute_live != 0; +} + + +//------------------------------add_reference---------------------------------- +// Extend the node to LRG mapping +void PhaseChaitin::add_reference( const Node *node, const Node *old_node ) { + _names.extend( node->_idx, n2lidx(old_node) ); +} + +//------------------------------dump------------------------------------------- +#ifndef PRODUCT +void PhaseChaitin::dump( const Node *n ) const { + uint r = (n->_idx < _names.Size() ) ? Find_const(n) : 0; + tty->print("L%d",r); + if( r && n->Opcode() != Op_Phi ) { + if( _node_regs ) { // Got a post-allocation copy of allocation? + tty->print("["); + OptoReg::Name second = get_reg_second(n); + if( OptoReg::is_valid(second) ) { + if( OptoReg::is_reg(second) ) + tty->print("%s:",Matcher::regName[second]); + else + tty->print("%s+%d:",OptoReg::regname(OptoReg::c_frame_pointer), reg2offset_unchecked(second)); + } + OptoReg::Name first = get_reg_first(n); + if( OptoReg::is_reg(first) ) + tty->print("%s]",Matcher::regName[first]); + else + tty->print("%s+%d]",OptoReg::regname(OptoReg::c_frame_pointer), reg2offset_unchecked(first)); + } else + n->out_RegMask().dump(); + } + tty->print("/N%d\t",n->_idx); + tty->print("%s === ", n->Name()); + uint k; + for( k = 0; k < n->req(); k++) { + Node *m = n->in(k); + if( !m ) tty->print("_ "); + else { + uint r = (m->_idx < _names.Size() ) ? Find_const(m) : 0; + tty->print("L%d",r); + // Data MultiNode's can have projections with no real registers. + // Don't die while dumping them. + int op = n->Opcode(); + if( r && op != Op_Phi && op != Op_Proj && op != Op_SCMemProj) { + if( _node_regs ) { + tty->print("["); + OptoReg::Name second = get_reg_second(n->in(k)); + if( OptoReg::is_valid(second) ) { + if( OptoReg::is_reg(second) ) + tty->print("%s:",Matcher::regName[second]); + else + tty->print("%s+%d:",OptoReg::regname(OptoReg::c_frame_pointer), + reg2offset_unchecked(second)); + } + OptoReg::Name first = get_reg_first(n->in(k)); + if( OptoReg::is_reg(first) ) + tty->print("%s]",Matcher::regName[first]); + else + tty->print("%s+%d]",OptoReg::regname(OptoReg::c_frame_pointer), + reg2offset_unchecked(first)); + } else + n->in_RegMask(k).dump(); + } + tty->print("/N%d ",m->_idx); + } + } + if( k < n->len() && n->in(k) ) tty->print("| "); + for( ; k < n->len(); k++ ) { + Node *m = n->in(k); + if( !m ) break; + uint r = (m->_idx < _names.Size() ) ? Find_const(m) : 0; + tty->print("L%d",r); + tty->print("/N%d ",m->_idx); + } + if( n->is_Mach() ) n->as_Mach()->dump_spec(tty); + else n->dump_spec(tty); + if( _spilled_once.test(n->_idx ) ) { + tty->print(" Spill_1"); + if( _spilled_twice.test(n->_idx ) ) + tty->print(" Spill_2"); + } + tty->print("\n"); +} + +void PhaseChaitin::dump( const Block * b ) const { + b->dump_head( &_cfg._bbs ); + + // For all instructions + for( uint j = 0; j < b->_nodes.size(); j++ ) + dump(b->_nodes[j]); + // Print live-out info at end of block + if( _live ) { + tty->print("Liveout: "); + IndexSet *live = _live->live(b); + IndexSetIterator elements(live); + tty->print("{"); + uint i; + while ((i = elements.next()) != 0) { + tty->print("L%d ", Find_const(i)); + } + tty->print_cr("}"); + } + tty->print("\n"); +} + +void PhaseChaitin::dump() const { + tty->print( "--- Chaitin -- argsize: %d framesize: %d ---\n", + _matcher._new_SP, _framesize ); + + // For all blocks + for( uint i = 0; i < _cfg._num_blocks; i++ ) + dump(_cfg._blocks[i]); + // End of per-block dump + tty->print("\n"); + + if (!_ifg) { + tty->print("(No IFG.)\n"); + return; + } + + // Dump LRG array + tty->print("--- Live RanGe Array ---\n"); + for(uint i2 = 1; i2 < _maxlrg; i2++ ) { + tty->print("L%d: ",i2); + if( i2 < _ifg->_maxlrg ) lrgs(i2).dump( ); + else tty->print("new LRG"); + } + tty->print_cr(""); + + // Dump lo-degree list + tty->print("Lo degree: "); + for(uint i3 = _lo_degree; i3; i3 = lrgs(i3)._next ) + tty->print("L%d ",i3); + tty->print_cr(""); + + // Dump lo-stk-degree list + tty->print("Lo stk degree: "); + for(uint i4 = _lo_stk_degree; i4; i4 = lrgs(i4)._next ) + tty->print("L%d ",i4); + tty->print_cr(""); + + // Dump lo-degree list + tty->print("Hi degree: "); + for(uint i5 = _hi_degree; i5; i5 = lrgs(i5)._next ) + tty->print("L%d ",i5); + tty->print_cr(""); +} + +//------------------------------dump_degree_lists------------------------------ +void PhaseChaitin::dump_degree_lists() const { + // Dump lo-degree list + tty->print("Lo degree: "); + for( uint i = _lo_degree; i; i = lrgs(i)._next ) + tty->print("L%d ",i); + tty->print_cr(""); + + // Dump lo-stk-degree list + tty->print("Lo stk degree: "); + for(uint i2 = _lo_stk_degree; i2; i2 = lrgs(i2)._next ) + tty->print("L%d ",i2); + tty->print_cr(""); + + // Dump lo-degree list + tty->print("Hi degree: "); + for(uint i3 = _hi_degree; i3; i3 = lrgs(i3)._next ) + tty->print("L%d ",i3); + tty->print_cr(""); +} + +//------------------------------dump_simplified-------------------------------- +void PhaseChaitin::dump_simplified() const { + tty->print("Simplified: "); + for( uint i = _simplified; i; i = lrgs(i)._next ) + tty->print("L%d ",i); + tty->print_cr(""); +} + +static char *print_reg( OptoReg::Name reg, const PhaseChaitin *pc, char *buf ) { + if ((int)reg < 0) + sprintf(buf, "", (int)reg); + else if (OptoReg::is_reg(reg)) + strcpy(buf, Matcher::regName[reg]); + else + sprintf(buf,"%s + #%d",OptoReg::regname(OptoReg::c_frame_pointer), + pc->reg2offset(reg)); + return buf+strlen(buf); +} + +//------------------------------dump_register---------------------------------- +// Dump a register name into a buffer. Be intelligent if we get called +// before allocation is complete. +char *PhaseChaitin::dump_register( const Node *n, char *buf ) const { + if( !this ) { // Not got anything? + sprintf(buf,"N%d",n->_idx); // Then use Node index + } else if( _node_regs ) { + // Post allocation, use direct mappings, no LRG info available + print_reg( get_reg_first(n), this, buf ); + } else { + uint lidx = Find_const(n); // Grab LRG number + if( !_ifg ) { + sprintf(buf,"L%d",lidx); // No register binding yet + } else if( !lidx ) { // Special, not allocated value + strcpy(buf,"Special"); + } else if( (lrgs(lidx).num_regs() == 1) + ? !lrgs(lidx).mask().is_bound1() + : !lrgs(lidx).mask().is_bound2() ) { + sprintf(buf,"L%d",lidx); // No register binding yet + } else { // Hah! We have a bound machine register + print_reg( lrgs(lidx).reg(), this, buf ); + } + } + return buf+strlen(buf); +} + +//----------------------dump_for_spill_split_recycle-------------------------- +void PhaseChaitin::dump_for_spill_split_recycle() const { + if( WizardMode && (PrintCompilation || PrintOpto) ) { + // Display which live ranges need to be split and the allocator's state + tty->print_cr("Graph-Coloring Iteration %d will split the following live ranges", _trip_cnt); + for( uint bidx = 1; bidx < _maxlrg; bidx++ ) { + if( lrgs(bidx).alive() && lrgs(bidx).reg() >= LRG::SPILL_REG ) { + tty->print("L%d: ", bidx); + lrgs(bidx).dump(); + } + } + tty->cr(); + dump(); + } +} + +//------------------------------dump_frame------------------------------------ +void PhaseChaitin::dump_frame() const { + const char *fp = OptoReg::regname(OptoReg::c_frame_pointer); + const TypeTuple *domain = C->tf()->domain(); + const int argcnt = domain->cnt() - TypeFunc::Parms; + + // Incoming arguments in registers dump + for( int k = 0; k < argcnt; k++ ) { + OptoReg::Name parmreg = _matcher._parm_regs[k].first(); + if( OptoReg::is_reg(parmreg)) { + const char *reg_name = OptoReg::regname(parmreg); + tty->print("#r%3.3d %s", parmreg, reg_name); + parmreg = _matcher._parm_regs[k].second(); + if( OptoReg::is_reg(parmreg)) { + tty->print(":%s", OptoReg::regname(parmreg)); + } + tty->print(" : parm %d: ", k); + domain->field_at(k + TypeFunc::Parms)->dump(); + tty->print_cr(""); + } + } + + // Check for un-owned padding above incoming args + OptoReg::Name reg = _matcher._new_SP; + if( reg > _matcher._in_arg_limit ) { + reg = OptoReg::add(reg, -1); + tty->print_cr("#r%3.3d %s+%2d: pad0, owned by CALLER", reg, fp, reg2offset_unchecked(reg)); + } + + // Incoming argument area dump + OptoReg::Name begin_in_arg = OptoReg::add(_matcher._old_SP,C->out_preserve_stack_slots()); + while( reg > begin_in_arg ) { + reg = OptoReg::add(reg, -1); + tty->print("#r%3.3d %s+%2d: ",reg,fp,reg2offset_unchecked(reg)); + int j; + for( j = 0; j < argcnt; j++) { + if( _matcher._parm_regs[j].first() == reg || + _matcher._parm_regs[j].second() == reg ) { + tty->print("parm %d: ",j); + domain->field_at(j + TypeFunc::Parms)->dump(); + tty->print_cr(""); + break; + } + } + if( j >= argcnt ) + tty->print_cr("HOLE, owned by SELF"); + } + + // Old outgoing preserve area + while( reg > _matcher._old_SP ) { + reg = OptoReg::add(reg, -1); + tty->print_cr("#r%3.3d %s+%2d: old out preserve",reg,fp,reg2offset_unchecked(reg)); + } + + // Old SP + tty->print_cr("# -- Old %s -- Framesize: %d --",fp, + reg2offset_unchecked(OptoReg::add(_matcher._old_SP,-1)) - reg2offset_unchecked(_matcher._new_SP)+jintSize); + + // Preserve area dump + reg = OptoReg::add(reg, -1); + while( OptoReg::is_stack(reg)) { + tty->print("#r%3.3d %s+%2d: ",reg,fp,reg2offset_unchecked(reg)); + if( _matcher.return_addr() == reg ) + tty->print_cr("return address"); + else if( _matcher.return_addr() == OptoReg::add(reg,1) && + VerifyStackAtCalls ) + tty->print_cr("0xBADB100D +VerifyStackAtCalls"); + else if ((int)OptoReg::reg2stack(reg) < C->fixed_slots()) + tty->print_cr("Fixed slot %d", OptoReg::reg2stack(reg)); + else + tty->print_cr("pad2, in_preserve"); + reg = OptoReg::add(reg, -1); + } + + // Spill area dump + reg = OptoReg::add(_matcher._new_SP, _framesize ); + while( reg > _matcher._out_arg_limit ) { + reg = OptoReg::add(reg, -1); + tty->print_cr("#r%3.3d %s+%2d: spill",reg,fp,reg2offset_unchecked(reg)); + } + + // Outgoing argument area dump + while( reg > OptoReg::add(_matcher._new_SP, C->out_preserve_stack_slots()) ) { + reg = OptoReg::add(reg, -1); + tty->print_cr("#r%3.3d %s+%2d: outgoing argument",reg,fp,reg2offset_unchecked(reg)); + } + + // Outgoing new preserve area + while( reg > _matcher._new_SP ) { + reg = OptoReg::add(reg, -1); + tty->print_cr("#r%3.3d %s+%2d: new out preserve",reg,fp,reg2offset_unchecked(reg)); + } + tty->print_cr("#"); +} + +//------------------------------dump_bb---------------------------------------- +void PhaseChaitin::dump_bb( uint pre_order ) const { + tty->print_cr("---dump of B%d---",pre_order); + for( uint i = 0; i < _cfg._num_blocks; i++ ) { + Block *b = _cfg._blocks[i]; + if( b->_pre_order == pre_order ) + dump(b); + } +} + +//------------------------------dump_lrg--------------------------------------- +void PhaseChaitin::dump_lrg( uint lidx ) const { + tty->print_cr("---dump of L%d---",lidx); + + if( _ifg ) { + if( lidx >= _maxlrg ) { + tty->print("Attempt to print live range index beyond max live range.\n"); + return; + } + tty->print("L%d: ",lidx); + lrgs(lidx).dump( ); + } + if( _ifg ) { tty->print("Neighbors: %d - ", _ifg->neighbor_cnt(lidx)); + _ifg->neighbors(lidx)->dump(); + tty->cr(); + } + // For all blocks + for( uint i = 0; i < _cfg._num_blocks; i++ ) { + Block *b = _cfg._blocks[i]; + int dump_once = 0; + + // For all instructions + for( uint j = 0; j < b->_nodes.size(); j++ ) { + Node *n = b->_nodes[j]; + if( Find_const(n) == lidx ) { + if( !dump_once++ ) { + tty->cr(); + b->dump_head( &_cfg._bbs ); + } + dump(n); + continue; + } + uint cnt = n->req(); + for( uint k = 1; k < cnt; k++ ) { + Node *m = n->in(k); + if (!m) continue; // be robust in the dumper + if( Find_const(m) == lidx ) { + if( !dump_once++ ) { + tty->cr(); + b->dump_head( &_cfg._bbs ); + } + dump(n); + } + } + } + } // End of per-block dump + tty->cr(); +} +#endif // not PRODUCT + +//------------------------------print_chaitin_statistics------------------------------- +int PhaseChaitin::_final_loads = 0; +int PhaseChaitin::_final_stores = 0; +int PhaseChaitin::_final_memoves= 0; +int PhaseChaitin::_final_copies = 0; +double PhaseChaitin::_final_load_cost = 0; +double PhaseChaitin::_final_store_cost = 0; +double PhaseChaitin::_final_memove_cost= 0; +double PhaseChaitin::_final_copy_cost = 0; +int PhaseChaitin::_conserv_coalesce = 0; +int PhaseChaitin::_conserv_coalesce_pair = 0; +int PhaseChaitin::_conserv_coalesce_trie = 0; +int PhaseChaitin::_conserv_coalesce_quad = 0; +int PhaseChaitin::_post_alloc = 0; +int PhaseChaitin::_lost_opp_pp_coalesce = 0; +int PhaseChaitin::_lost_opp_cflow_coalesce = 0; +int PhaseChaitin::_used_cisc_instructions = 0; +int PhaseChaitin::_unused_cisc_instructions = 0; +int PhaseChaitin::_allocator_attempts = 0; +int PhaseChaitin::_allocator_successes = 0; + +#ifndef PRODUCT +uint PhaseChaitin::_high_pressure = 0; +uint PhaseChaitin::_low_pressure = 0; + +void PhaseChaitin::print_chaitin_statistics() { + tty->print_cr("Inserted %d spill loads, %d spill stores, %d mem-mem moves and %d copies.", _final_loads, _final_stores, _final_memoves, _final_copies); + tty->print_cr("Total load cost= %6.0f, store cost = %6.0f, mem-mem cost = %5.2f, copy cost = %5.0f.", _final_load_cost, _final_store_cost, _final_memove_cost, _final_copy_cost); + tty->print_cr("Adjusted spill cost = %7.0f.", + _final_load_cost*4.0 + _final_store_cost * 2.0 + + _final_copy_cost*1.0 + _final_memove_cost*12.0); + tty->print("Conservatively coalesced %d copies, %d pairs", + _conserv_coalesce, _conserv_coalesce_pair); + if( _conserv_coalesce_trie || _conserv_coalesce_quad ) + tty->print(", %d tries, %d quads", _conserv_coalesce_trie, _conserv_coalesce_quad); + tty->print_cr(", %d post alloc.", _post_alloc); + if( _lost_opp_pp_coalesce || _lost_opp_cflow_coalesce ) + tty->print_cr("Lost coalesce opportunity, %d private-private, and %d cflow interfered.", + _lost_opp_pp_coalesce, _lost_opp_cflow_coalesce ); + if( _used_cisc_instructions || _unused_cisc_instructions ) + tty->print_cr("Used cisc instruction %d, remained in register %d", + _used_cisc_instructions, _unused_cisc_instructions); + if( _allocator_successes != 0 ) + tty->print_cr("Average allocation trips %f", (float)_allocator_attempts/(float)_allocator_successes); + tty->print_cr("High Pressure Blocks = %d, Low Pressure Blocks = %d", _high_pressure, _low_pressure); +} +#endif // not PRODUCT diff --git a/hotspot/src/share/vm/opto/chaitin.hpp b/hotspot/src/share/vm/opto/chaitin.hpp new file mode 100644 index 00000000000..df848d72c5d --- /dev/null +++ b/hotspot/src/share/vm/opto/chaitin.hpp @@ -0,0 +1,501 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class LoopTree; +class MachCallNode; +class MachSafePointNode; +class Matcher; +class PhaseCFG; +class PhaseLive; +class PhaseRegAlloc; +class PhaseChaitin; + +#define OPTO_DEBUG_SPLIT_FREQ BLOCK_FREQUENCY(0.001) +#define OPTO_LRG_HIGH_FREQ BLOCK_FREQUENCY(0.25) + +//------------------------------LRG-------------------------------------------- +// Live-RanGe structure. +class LRG : public ResourceObj { +public: + enum { SPILL_REG=29999 }; // Register number of a spilled LRG + + double _cost; // 2 for loads/1 for stores times block freq + double _area; // Sum of all simultaneously live values + double score() const; // Compute score from cost and area + double _maxfreq; // Maximum frequency of any def or use + + Node *_def; // Check for multi-def live ranges +#ifndef PRODUCT + GrowableArray* _defs; +#endif + + uint _risk_bias; // Index of LRG which we want to avoid color + uint _copy_bias; // Index of LRG which we want to share color + + uint _next; // Index of next LRG in linked list + uint _prev; // Index of prev LRG in linked list +private: + uint _reg; // Chosen register; undefined if mask is plural +public: + // Return chosen register for this LRG. Error if the LRG is not bound to + // a single register. + OptoReg::Name reg() const { return OptoReg::Name(_reg); } + void set_reg( OptoReg::Name r ) { _reg = r; } + +private: + uint _eff_degree; // Effective degree: Sum of neighbors _num_regs +public: + int degree() const { assert( _degree_valid, "" ); return _eff_degree; } + // Degree starts not valid and any change to the IFG neighbor + // set makes it not valid. + void set_degree( uint degree ) { _eff_degree = degree; debug_only(_degree_valid = 1;) } + // Made a change that hammered degree + void invalid_degree() { debug_only(_degree_valid=0;) } + // Incrementally modify degree. If it was correct, it should remain correct + void inc_degree( uint mod ) { _eff_degree += mod; } + // Compute the degree between 2 live ranges + int compute_degree( LRG &l ) const; + +private: + RegMask _mask; // Allowed registers for this LRG + uint _mask_size; // cache of _mask.Size(); +public: + int compute_mask_size() const { return _mask.is_AllStack() ? 65535 : _mask.Size(); } + void set_mask_size( int size ) { + assert((size == 65535) || (size == (int)_mask.Size()), ""); + _mask_size = size; + debug_only(_msize_valid=1;) + debug_only( if( _num_regs == 2 && !_fat_proj ) _mask.VerifyPairs(); ) + } + void compute_set_mask_size() { set_mask_size(compute_mask_size()); } + int mask_size() const { assert( _msize_valid, "mask size not valid" ); + return _mask_size; } + // Get the last mask size computed, even if it does not match the + // count of bits in the current mask. + int get_invalid_mask_size() const { return _mask_size; } + const RegMask &mask() const { return _mask; } + void set_mask( const RegMask &rm ) { _mask = rm; debug_only(_msize_valid=0;)} + void AND( const RegMask &rm ) { _mask.AND(rm); debug_only(_msize_valid=0;)} + void SUBTRACT( const RegMask &rm ) { _mask.SUBTRACT(rm); debug_only(_msize_valid=0;)} + void Clear() { _mask.Clear() ; debug_only(_msize_valid=1); _mask_size = 0; } + void Set_All() { _mask.Set_All(); debug_only(_msize_valid=1); _mask_size = RegMask::CHUNK_SIZE; } + void Insert( OptoReg::Name reg ) { _mask.Insert(reg); debug_only(_msize_valid=0;) } + void Remove( OptoReg::Name reg ) { _mask.Remove(reg); debug_only(_msize_valid=0;) } + void ClearToPairs() { _mask.ClearToPairs(); debug_only(_msize_valid=0;) } + + // Number of registers this live range uses when it colors +private: + uint8 _num_regs; // 2 for Longs and Doubles, 1 for all else + // except _num_regs is kill count for fat_proj +public: + int num_regs() const { return _num_regs; } + void set_num_regs( int reg ) { assert( _num_regs == reg || !_num_regs, "" ); _num_regs = reg; } + +private: + // Number of physical registers this live range uses when it colors + // Architecture and register-set dependent + uint8 _reg_pressure; +public: + void set_reg_pressure(int i) { _reg_pressure = i; } + int reg_pressure() const { return _reg_pressure; } + + // How much 'wiggle room' does this live range have? + // How many color choices can it make (scaled by _num_regs)? + int degrees_of_freedom() const { return mask_size() - _num_regs; } + // Bound LRGs have ZERO degrees of freedom. We also count + // must_spill as bound. + bool is_bound () const { return _is_bound; } + // Negative degrees-of-freedom; even with no neighbors this + // live range must spill. + bool not_free() const { return degrees_of_freedom() < 0; } + // Is this live range of "low-degree"? Trivially colorable? + bool lo_degree () const { return degree() <= degrees_of_freedom(); } + // Is this live range just barely "low-degree"? Trivially colorable? + bool just_lo_degree () const { return degree() == degrees_of_freedom(); } + + uint _is_oop:1, // Live-range holds an oop + _is_float:1, // True if in float registers + _was_spilled1:1, // True if prior spilling on def + _was_spilled2:1, // True if twice prior spilling on def + _is_bound:1, // live range starts life with no + // degrees of freedom. + _direct_conflict:1, // True if def and use registers in conflict + _must_spill:1, // live range has lost all degrees of freedom + // If _fat_proj is set, live range does NOT require aligned, adjacent + // registers and has NO interferences. + // If _fat_proj is clear, live range requires num_regs() to be a power of + // 2, and it requires registers to form an aligned, adjacent set. + _fat_proj:1, // + _was_lo:1, // Was lo-degree prior to coalesce + _msize_valid:1, // _mask_size cache valid + _degree_valid:1, // _degree cache valid + _has_copy:1, // Adjacent to some copy instruction + _at_risk:1; // Simplify says this guy is at risk to spill + + + // Alive if non-zero, dead if zero + bool alive() const { return _def != NULL; } + +#ifndef PRODUCT + void dump( ) const; +#endif +}; + +//------------------------------LRG_List--------------------------------------- +// Map Node indices to Live RanGe indices. +// Array lookup in the optimized case. +class LRG_List : public ResourceObj { + uint _cnt, _max; + uint* _lidxs; + ReallocMark _nesting; // assertion check for reallocations +public: + LRG_List( uint max ); + + uint lookup( uint nidx ) const { + return _lidxs[nidx]; + } + uint operator[] (uint nidx) const { return lookup(nidx); } + + void map( uint nidx, uint lidx ) { + assert( nidx < _cnt, "oob" ); + _lidxs[nidx] = lidx; + } + void extend( uint nidx, uint lidx ); + + uint Size() const { return _cnt; } +}; + +//------------------------------IFG-------------------------------------------- +// InterFerence Graph +// An undirected graph implementation. Created with a fixed number of +// vertices. Edges can be added & tested. Vertices can be removed, then +// added back later with all edges intact. Can add edges between one vertex +// and a list of other vertices. Can union vertices (and their edges) +// together. The IFG needs to be really really fast, and also fairly +// abstract! It needs abstraction so I can fiddle with the implementation to +// get even more speed. +class PhaseIFG : public Phase { + // Current implementation: a triangular adjacency list. + + // Array of adjacency-lists, indexed by live-range number + IndexSet *_adjs; + + // Assertion bit for proper use of Squaring + bool _is_square; + + // Live range structure goes here + LRG *_lrgs; // Array of LRG structures + +public: + // Largest live-range number + uint _maxlrg; + + Arena *_arena; + + // Keep track of inserted and deleted Nodes + VectorSet *_yanked; + + PhaseIFG( Arena *arena ); + void init( uint maxlrg ); + + // Add edge between a and b. Returns true if actually addded. + int add_edge( uint a, uint b ); + + // Add edge between a and everything in the vector + void add_vector( uint a, IndexSet *vec ); + + // Test for edge existance + int test_edge( uint a, uint b ) const; + + // Square-up matrix for faster Union + void SquareUp(); + + // Return number of LRG neighbors + uint neighbor_cnt( uint a ) const { return _adjs[a].count(); } + // Union edges of b into a on Squared-up matrix + void Union( uint a, uint b ); + // Test for edge in Squared-up matrix + int test_edge_sq( uint a, uint b ) const; + // Yank a Node and all connected edges from the IFG. Be prepared to + // re-insert the yanked Node in reverse order of yanking. Return a + // list of neighbors (edges) yanked. + IndexSet *remove_node( uint a ); + // Reinsert a yanked Node + void re_insert( uint a ); + // Return set of neighbors + IndexSet *neighbors( uint a ) const { return &_adjs[a]; } + +#ifndef PRODUCT + // Dump the IFG + void dump() const; + void stats() const; + void verify( const PhaseChaitin * ) const; +#endif + + //--------------- Live Range Accessors + LRG &lrgs(uint idx) const { assert(idx < _maxlrg, "oob"); return _lrgs[idx]; } + + // Compute and set effective degree. Might be folded into SquareUp(). + void Compute_Effective_Degree(); + + // Compute effective degree as the sum of neighbors' _sizes. + int effective_degree( uint lidx ) const; +}; + +// TEMPORARILY REPLACED WITH COMMAND LINE FLAG + +//// !!!!! Magic Constants need to move into ad file +#ifdef SPARC +//#define FLOAT_PRESSURE 30 /* SFLT_REG_mask.Size() - 1 */ +//#define INT_PRESSURE 23 /* NOTEMP_I_REG_mask.Size() - 1 */ +#define FLOAT_INCREMENT(regs) regs +#else +//#define FLOAT_PRESSURE 6 +//#define INT_PRESSURE 6 +#define FLOAT_INCREMENT(regs) 1 +#endif + +//------------------------------Chaitin---------------------------------------- +// Briggs-Chaitin style allocation, mostly. +class PhaseChaitin : public PhaseRegAlloc { + + int _trip_cnt; + int _alternate; + + uint _maxlrg; // Max live range number + LRG &lrgs(uint idx) const { return _ifg->lrgs(idx); } + PhaseLive *_live; // Liveness, used in the interference graph + PhaseIFG *_ifg; // Interference graph (for original chunk) + Node_List **_lrg_nodes; // Array of node; lists for lrgs which spill + VectorSet _spilled_once; // Nodes that have been spilled + VectorSet _spilled_twice; // Nodes that have been spilled twice + + LRG_List _names; // Map from Nodes to Live RanGes + + // Union-find map. Declared as a short for speed. + // Indexed by live-range number, it returns the compacted live-range number + LRG_List _uf_map; + // Reset the Union-Find map to identity + void reset_uf_map( uint maxlrg ); + // Remove the need for the Union-Find mapping + void compress_uf_map_for_nodes( ); + + // Combine the Live Range Indices for these 2 Nodes into a single live + // range. Future requests for any Node in either live range will + // return the live range index for the combined live range. + void Union( const Node *src, const Node *dst ); + + void new_lrg( const Node *x, uint lrg ); + + // Compact live ranges, removing unused ones. Return new maxlrg. + void compact(); + + uint _lo_degree; // Head of lo-degree LRGs list + uint _lo_stk_degree; // Head of lo-stk-degree LRGs list + uint _hi_degree; // Head of hi-degree LRGs list + uint _simplified; // Linked list head of simplified LRGs + + // Helper functions for Split() + uint split_DEF( Node *def, Block *b, int loc, uint max, Node **Reachblock, Node **debug_defs, GrowableArray splits, int slidx ); + uint split_USE( Node *def, Block *b, Node *use, uint useidx, uint max, bool def_down, bool cisc_sp, GrowableArray splits, int slidx ); + int clone_projs( Block *b, uint idx, Node *con, Node *copy, uint &maxlrg ); + Node *split_Rematerialize( Node *def, Block *b, uint insidx, uint &maxlrg, GrowableArray splits, int slidx, uint *lrg2reach, Node **Reachblock, bool walkThru ); + // True if lidx is used before any real register is def'd in the block + bool prompt_use( Block *b, uint lidx ); + Node *get_spillcopy_wide( Node *def, Node *use, uint uidx ); + // Insert the spill at chosen location. Skip over any interveneing Proj's or + // Phis. Skip over a CatchNode and projs, inserting in the fall-through block + // instead. Update high-pressure indices. Create a new live range. + void insert_proj( Block *b, uint i, Node *spill, uint maxlrg ); + + bool is_high_pressure( Block *b, LRG *lrg, uint insidx ); + + uint _oldphi; // Node index which separates pre-allocation nodes + + Block **_blks; // Array of blocks sorted by frequency for coalescing + +#ifndef PRODUCT + bool _trace_spilling; +#endif + +public: + PhaseChaitin( uint unique, PhaseCFG &cfg, Matcher &matcher ); + ~PhaseChaitin() {} + + // Convert a Node into a Live Range Index - a lidx + uint Find( const Node *n ) { + uint lidx = n2lidx(n); + uint uf_lidx = _uf_map[lidx]; + return (uf_lidx == lidx) ? uf_lidx : Find_compress(n); + } + uint Find_const( uint lrg ) const; + uint Find_const( const Node *n ) const; + + // Do all the real work of allocate + void Register_Allocate(); + + uint n2lidx( const Node *n ) const { return _names[n->_idx]; } + +#ifndef PRODUCT + bool trace_spilling() const { return _trace_spilling; } +#endif + +private: + // De-SSA the world. Assign registers to Nodes. Use the same register for + // all inputs to a PhiNode, effectively coalescing live ranges. Insert + // copies as needed. + void de_ssa(); + uint Find_compress( const Node *n ); + uint Find( uint lidx ) { + uint uf_lidx = _uf_map[lidx]; + return (uf_lidx == lidx) ? uf_lidx : Find_compress(lidx); + } + uint Find_compress( uint lidx ); + + uint Find_id( const Node *n ) { + uint retval = n2lidx(n); + assert(retval == Find(n),"Invalid node to lidx mapping"); + return retval; + } + + // Add edge between reg and everything in the vector. + // Same as _ifg->add_vector(reg,live) EXCEPT use the RegMask + // information to trim the set of interferences. Return the + // count of edges added. + void interfere_with_live( uint reg, IndexSet *live ); + // Count register pressure for asserts + uint count_int_pressure( IndexSet *liveout ); + uint count_float_pressure( IndexSet *liveout ); + + // Build the interference graph using virtual registers only. + // Used for aggressive coalescing. + void build_ifg_virtual( ); + + // Build the interference graph using physical registers when available. + // That is, if 2 live ranges are simultaneously alive but in their + // acceptable register sets do not overlap, then they do not interfere. + uint build_ifg_physical( ResourceArea *a ); + + // Gather LiveRanGe information, including register masks and base pointer/ + // derived pointer relationships. + void gather_lrg_masks( bool mod_cisc_masks ); + + // Force the bases of derived pointers to be alive at GC points. + bool stretch_base_pointer_live_ranges( ResourceArea *a ); + // Helper to stretch above; recursively discover the base Node for + // a given derived Node. Easy for AddP-related machine nodes, but + // needs to be recursive for derived Phis. + Node *find_base_for_derived( Node **derived_base_map, Node *derived, uint &maxlrg ); + + // Set the was-lo-degree bit. Conservative coalescing should not change the + // colorability of the graph. If any live range was of low-degree before + // coalescing, it should Simplify. This call sets the was-lo-degree bit. + void set_was_low(); + + // Split live-ranges that must spill due to register conflicts (as opposed + // to capacity spills). Typically these are things def'd in a register + // and used on the stack or vice-versa. + void pre_spill(); + + // Init LRG caching of degree, numregs. Init lo_degree list. + void cache_lrg_info( ); + + // Simplify the IFG by removing LRGs of low degree with no copies + void Pre_Simplify(); + + // Simplify the IFG by removing LRGs of low degree + void Simplify(); + + // Select colors by re-inserting edges into the IFG. + // Return TRUE if any spills occured. + uint Select( ); + // Helper function for select which allows biased coloring + OptoReg::Name choose_color( LRG &lrg, int chunk ); + // Helper function which implements biasing heuristic + OptoReg::Name bias_color( LRG &lrg, int chunk ); + + // Split uncolorable live ranges + // Return new number of live ranges + uint Split( uint maxlrg ); + + // Copy 'was_spilled'-edness from one Node to another. + void copy_was_spilled( Node *src, Node *dst ); + // Set the 'spilled_once' or 'spilled_twice' flag on a node. + void set_was_spilled( Node *n ); + + // Convert ideal spill-nodes into machine loads & stores + // Set C->failing when fixup spills could not complete, node limit exceeded. + void fixup_spills(); + + // Post-Allocation peephole copy removal + void post_allocate_copy_removal(); + Node *skip_copies( Node *c ); + int yank_if_dead( Node *old, Block *current_block, Node_List *value, Node_List *regnd ); + int elide_copy( Node *n, int k, Block *current_block, Node_List &value, Node_List ®nd, bool can_change_regs ); + int use_prior_register( Node *copy, uint idx, Node *def, Block *current_block, Node_List &value, Node_List ®nd ); + bool may_be_copy_of_callee( Node *def ) const; + + // If nreg already contains the same constant as val then eliminate it + bool eliminate_copy_of_constant(Node* val, Block *current_block, Node_List& value, Node_List ®nd, + OptoReg::Name nreg, OptoReg::Name nreg2); + // Extend the node to LRG mapping + void add_reference( const Node *node, const Node *old_node); + +private: + + static int _final_loads, _final_stores, _final_copies, _final_memoves; + static double _final_load_cost, _final_store_cost, _final_copy_cost, _final_memove_cost; + static int _conserv_coalesce, _conserv_coalesce_pair; + static int _conserv_coalesce_trie, _conserv_coalesce_quad; + static int _post_alloc; + static int _lost_opp_pp_coalesce, _lost_opp_cflow_coalesce; + static int _used_cisc_instructions, _unused_cisc_instructions; + static int _allocator_attempts, _allocator_successes; + +#ifndef PRODUCT + static uint _high_pressure, _low_pressure; + + void dump() const; + void dump( const Node *n ) const; + void dump( const Block * b ) const; + void dump_degree_lists() const; + void dump_simplified() const; + void dump_lrg( uint lidx ) const; + void dump_bb( uint pre_order ) const; + + // Verify that base pointers and derived pointers are still sane + void verify_base_ptrs( ResourceArea *a ) const; + + void dump_for_spill_split_recycle() const; + +public: + void dump_frame() const; + char *dump_register( const Node *n, char *buf ) const; +private: + static void print_chaitin_statistics(); +#endif + friend class PhaseCoalesce; + friend class PhaseAggressiveCoalesce; + friend class PhaseConservativeCoalesce; +}; diff --git a/hotspot/src/share/vm/opto/classes.cpp b/hotspot/src/share/vm/opto/classes.cpp new file mode 100644 index 00000000000..f8cef8a478e --- /dev/null +++ b/hotspot/src/share/vm/opto/classes.cpp @@ -0,0 +1,34 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_classes.cpp.incl" + +// ---------------------------------------------------------------------------- +// Build a table of virtual functions to map from Nodes to dense integer +// opcode names. +int Node::Opcode() const { return Op_Node; } +#define macro(x) int x##Node::Opcode() const { return Op_##x; } +#include "classes.hpp" +#undef macro diff --git a/hotspot/src/share/vm/opto/classes.hpp b/hotspot/src/share/vm/opto/classes.hpp new file mode 100644 index 00000000000..26eff2b4eb9 --- /dev/null +++ b/hotspot/src/share/vm/opto/classes.hpp @@ -0,0 +1,308 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The giant table of Node classes. +// One entry per class, sorted by class name. + +macro(AbsD) +macro(AbsF) +macro(AbsI) +macro(AddD) +macro(AddF) +macro(AddI) +macro(AddL) +macro(AddP) +macro(Allocate) +macro(AllocateArray) +macro(AndI) +macro(AndL) +macro(AtanD) +macro(Binary) +macro(Bool) +macro(BoxLock) +macro(ReverseBytesI) +macro(ReverseBytesL) +macro(CProj) +macro(CallDynamicJava) +macro(CallJava) +macro(CallLeaf) +macro(CallLeafNoFP) +macro(CallRuntime) +macro(CallStaticJava) +macro(CastII) +macro(CastX2P) +macro(CastP2X) +macro(CastPP) +macro(Catch) +macro(CatchProj) +macro(CheckCastPP) +macro(ClearArray) +macro(ConstraintCast) +macro(CMoveD) +macro(CMoveF) +macro(CMoveI) +macro(CMoveL) +macro(CMoveP) +macro(CmpD) +macro(CmpD3) +macro(CmpF) +macro(CmpF3) +macro(CmpI) +macro(CmpL) +macro(CmpL3) +macro(CmpLTMask) +macro(CmpP) +macro(CmpU) +macro(CompareAndSwapI) +macro(CompareAndSwapL) +macro(CompareAndSwapP) +macro(Con) +macro(ConD) +macro(ConF) +macro(ConI) +macro(ConL) +macro(ConP) +macro(Conv2B) +macro(ConvD2F) +macro(ConvD2I) +macro(ConvD2L) +macro(ConvF2D) +macro(ConvF2I) +macro(ConvF2L) +macro(ConvI2D) +macro(ConvI2F) +macro(ConvI2L) +macro(ConvL2D) +macro(ConvL2F) +macro(ConvL2I) +macro(CosD) +macro(CountedLoop) +macro(CountedLoopEnd) +macro(CreateEx) +macro(DivD) +macro(DivF) +macro(DivI) +macro(DivL) +macro(DivMod) +macro(DivModI) +macro(DivModL) +macro(ExpD) +macro(FastLock) +macro(FastUnlock) +macro(Goto) +macro(Halt) +macro(If) +macro(IfFalse) +macro(IfTrue) +macro(Initialize) +macro(JProj) +macro(Jump) +macro(JumpProj) +macro(LShiftI) +macro(LShiftL) +macro(LoadB) +macro(LoadC) +macro(LoadD) +macro(LoadD_unaligned) +macro(LoadF) +macro(LoadI) +macro(LoadKlass) +macro(LoadL) +macro(LoadL_unaligned) +macro(LoadPLocked) +macro(LoadLLocked) +macro(LoadP) +macro(LoadRange) +macro(LoadS) +macro(Lock) +macro(LogD) +macro(Log10D) +macro(Loop) +macro(Mach) +macro(MachProj) +macro(MaxI) +macro(MemBarAcquire) +macro(MemBarCPUOrder) +macro(MemBarRelease) +macro(MemBarVolatile) +macro(MergeMem) +macro(MinI) +macro(ModD) +macro(ModF) +macro(ModI) +macro(ModL) +macro(MoveI2F) +macro(MoveF2I) +macro(MoveL2D) +macro(MoveD2L) +macro(MulD) +macro(MulF) +macro(MulI) +macro(MulL) +macro(Multi) +macro(NegD) +macro(NegF) +macro(NeverBranch) +macro(Opaque1) +macro(Opaque2) +macro(OrI) +macro(OrL) +macro(PCTable) +macro(Parm) +macro(PartialSubtypeCheck) +macro(Phi) +macro(PowD) +macro(PrefetchRead) +macro(PrefetchWrite) +macro(Proj) +macro(RShiftI) +macro(RShiftL) +macro(Region) +macro(Rethrow) +macro(Return) +macro(Root) +macro(RoundDouble) +macro(RoundFloat) +macro(SafePoint) +macro(SCMemProj) +macro(SinD) +macro(SqrtD) +macro(Start) +macro(StartOSR) +macro(StoreB) +macro(StoreC) +macro(StoreCM) +macro(StorePConditional) +macro(StoreLConditional) +macro(StoreD) +macro(StoreF) +macro(StoreI) +macro(StoreL) +macro(StoreP) +macro(StrComp) +macro(SubD) +macro(SubF) +macro(SubI) +macro(SubL) +macro(TailCall) +macro(TailJump) +macro(TanD) +macro(ThreadLocal) +macro(Unlock) +macro(URShiftI) +macro(URShiftL) +macro(XorI) +macro(XorL) +macro(Vector) +macro(AddVB) +macro(AddVC) +macro(AddVS) +macro(AddVI) +macro(AddVL) +macro(AddVF) +macro(AddVD) +macro(SubVB) +macro(SubVC) +macro(SubVS) +macro(SubVI) +macro(SubVL) +macro(SubVF) +macro(SubVD) +macro(MulVF) +macro(MulVD) +macro(DivVF) +macro(DivVD) +macro(LShiftVB) +macro(LShiftVC) +macro(LShiftVS) +macro(LShiftVI) +macro(URShiftVB) +macro(URShiftVC) +macro(URShiftVS) +macro(URShiftVI) +macro(AndV) +macro(OrV) +macro(XorV) +macro(VectorLoad) +macro(Load16B) +macro(Load8B) +macro(Load4B) +macro(Load8C) +macro(Load4C) +macro(Load2C) +macro(Load8S) +macro(Load4S) +macro(Load2S) +macro(Load4I) +macro(Load2I) +macro(Load2L) +macro(Load4F) +macro(Load2F) +macro(Load2D) +macro(VectorStore) +macro(Store16B) +macro(Store8B) +macro(Store4B) +macro(Store8C) +macro(Store4C) +macro(Store2C) +macro(Store4I) +macro(Store2I) +macro(Store2L) +macro(Store4F) +macro(Store2F) +macro(Store2D) +macro(Pack) +macro(PackB) +macro(PackS) +macro(PackC) +macro(PackI) +macro(PackL) +macro(PackF) +macro(PackD) +macro(Pack2x1B) +macro(Pack2x2B) +macro(Replicate16B) +macro(Replicate8B) +macro(Replicate4B) +macro(Replicate8S) +macro(Replicate4S) +macro(Replicate2S) +macro(Replicate8C) +macro(Replicate4C) +macro(Replicate2C) +macro(Replicate4I) +macro(Replicate2I) +macro(Replicate2L) +macro(Replicate4F) +macro(Replicate2F) +macro(Replicate2D) +macro(Extract) +macro(ExtractB) +macro(ExtractS) +macro(ExtractC) +macro(ExtractI) +macro(ExtractL) +macro(ExtractF) +macro(ExtractD) diff --git a/hotspot/src/share/vm/opto/coalesce.cpp b/hotspot/src/share/vm/opto/coalesce.cpp new file mode 100644 index 00000000000..20e9bd17992 --- /dev/null +++ b/hotspot/src/share/vm/opto/coalesce.cpp @@ -0,0 +1,915 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_coalesce.cpp.incl" + +//============================================================================= +//------------------------------reset_uf_map----------------------------------- +void PhaseChaitin::reset_uf_map( uint maxlrg ) { + _maxlrg = maxlrg; + // Force the Union-Find mapping to be at least this large + _uf_map.extend(_maxlrg,0); + // Initialize it to be the ID mapping. + for( uint i=0; i<_maxlrg; i++ ) + _uf_map.map(i,i); +} + +//------------------------------compress_uf_map-------------------------------- +// Make all Nodes map directly to their final live range; no need for +// the Union-Find mapping after this call. +void PhaseChaitin::compress_uf_map_for_nodes( ) { + // For all Nodes, compress mapping + uint unique = _names.Size(); + for( uint i=0; i_idx]); + _names.map(n->_idx,lrg); + return lrg; +} + +//------------------------------Find_const------------------------------------- +// Like Find above, but no path compress, so bad asymptotic behavior +uint PhaseChaitin::Find_const( uint lrg ) const { + if( !lrg ) return lrg; // Ignore the zero LRG + // Off the end? This happens during debugging dumps when you got + // brand new live ranges but have not told the allocator yet. + if( lrg >= _maxlrg ) return lrg; + uint next = _uf_map[lrg]; + while( next != lrg ) { // Scan chain of equivalences + assert( next < lrg, "always union smaller" ); + lrg = next; // until find a fixed-point + next = _uf_map[lrg]; + } + return next; +} + +//------------------------------Find------------------------------------------- +// Like Find above, but no path compress, so bad asymptotic behavior +uint PhaseChaitin::Find_const( const Node *n ) const { + if( n->_idx >= _names.Size() ) return 0; // not mapped, usual for debug dump + return Find_const( _names[n->_idx] ); +} + +//------------------------------Union------------------------------------------ +// union 2 sets together. +void PhaseChaitin::Union( const Node *src_n, const Node *dst_n ) { + uint src = Find(src_n); + uint dst = Find(dst_n); + assert( src, "" ); + assert( dst, "" ); + assert( src < _maxlrg, "oob" ); + assert( dst < _maxlrg, "oob" ); + assert( src < dst, "always union smaller" ); + _uf_map.map(dst,src); +} + +//------------------------------new_lrg---------------------------------------- +void PhaseChaitin::new_lrg( const Node *x, uint lrg ) { + // Make the Node->LRG mapping + _names.extend(x->_idx,lrg); + // Make the Union-Find mapping an identity function + _uf_map.extend(lrg,lrg); +} + +//------------------------------clone_projs------------------------------------ +// After cloning some rematierialized instruction, clone any MachProj's that +// follow it. Example: Intel zero is XOR, kills flags. Sparc FP constants +// use G3 as an address temp. +int PhaseChaitin::clone_projs( Block *b, uint idx, Node *con, Node *copy, uint &maxlrg ) { + Block *bcon = _cfg._bbs[con->_idx]; + uint cindex = bcon->find_node(con); + Node *con_next = bcon->_nodes[cindex+1]; + if( con_next->in(0) != con || con_next->Opcode() != Op_MachProj ) + return false; // No MachProj's follow + + // Copy kills after the cloned constant + Node *kills = con_next->clone(); + kills->set_req( 0, copy ); + b->_nodes.insert( idx, kills ); + _cfg._bbs.map( kills->_idx, b ); + new_lrg( kills, maxlrg++ ); + return true; +} + +//------------------------------compact---------------------------------------- +// Renumber the live ranges to compact them. Makes the IFG smaller. +void PhaseChaitin::compact() { + // Current the _uf_map contains a series of short chains which are headed + // by a self-cycle. All the chains run from big numbers to little numbers. + // The Find() call chases the chains & shortens them for the next Find call. + // We are going to change this structure slightly. Numbers above a moving + // wave 'i' are unchanged. Numbers below 'j' point directly to their + // compacted live range with no further chaining. There are no chains or + // cycles below 'i', so the Find call no longer works. + uint j=1; + uint i; + for( i=1; i < _maxlrg; i++ ) { + uint lr = _uf_map[i]; + // Ignore unallocated live ranges + if( !lr ) continue; + assert( lr <= i, "" ); + _uf_map.map(i, ( lr == i ) ? j++ : _uf_map[lr]); + } + if( false ) // PrintOptoCompactLiveRanges + printf("Compacted %d LRs from %d\n",i-j,i); + // Now change the Node->LR mapping to reflect the compacted names + uint unique = _names.Size(); + for( i=0; iprint("L%d/N%d ",r,n->_idx); +} + +//------------------------------dump------------------------------------------- +void PhaseCoalesce::dump() const { + // I know I have a block layout now, so I can print blocks in a loop + for( uint i=0; i<_phc._cfg._num_blocks; i++ ) { + uint j; + Block *b = _phc._cfg._blocks[i]; + // Print a nice block header + tty->print("B%d: ",b->_pre_order); + for( j=1; jnum_preds(); j++ ) + tty->print("B%d ", _phc._cfg._bbs[b->pred(j)->_idx]->_pre_order); + tty->print("-> "); + for( j=0; j_num_succs; j++ ) + tty->print("B%d ",b->_succs[j]->_pre_order); + tty->print(" IDom: B%d/#%d\n", b->_idom ? b->_idom->_pre_order : 0, b->_dom_depth); + uint cnt = b->_nodes.size(); + for( j=0; j_nodes[j]; + dump( n ); + tty->print("\t%s\t",n->Name()); + + // Dump the inputs + uint k; // Exit value of loop + for( k=0; kreq(); k++ ) // For all required inputs + if( n->in(k) ) dump( n->in(k) ); + else tty->print("_ "); + int any_prec = 0; + for( ; klen(); k++ ) // For all precedence inputs + if( n->in(k) ) { + if( !any_prec++ ) tty->print(" |"); + dump( n->in(k) ); + } + + // Dump node-specific info + n->dump_spec(tty); + tty->print("\n"); + + } + tty->print("\n"); + } +} +#endif + +//------------------------------combine_these_two------------------------------ +// Combine the live ranges def'd by these 2 Nodes. N2 is an input to N1. +void PhaseCoalesce::combine_these_two( Node *n1, Node *n2 ) { + uint lr1 = _phc.Find(n1); + uint lr2 = _phc.Find(n2); + if( lr1 != lr2 && // Different live ranges already AND + !_phc._ifg->test_edge_sq( lr1, lr2 ) ) { // Do not interfere + LRG *lrg1 = &_phc.lrgs(lr1); + LRG *lrg2 = &_phc.lrgs(lr2); + // Not an oop->int cast; oop->oop, int->int, AND int->oop are OK. + + // Now, why is int->oop OK? We end up declaring a raw-pointer as an oop + // and in general that's a bad thing. However, int->oop conversions only + // happen at GC points, so the lifetime of the misclassified raw-pointer + // is from the CheckCastPP (that converts it to an oop) backwards up + // through a merge point and into the slow-path call, and around the + // diamond up to the heap-top check and back down into the slow-path call. + // The misclassified raw pointer is NOT live across the slow-path call, + // and so does not appear in any GC info, so the fact that it is + // misclassified is OK. + + if( (lrg1->_is_oop || !lrg2->_is_oop) && // not an oop->int cast AND + // Compatible final mask + lrg1->mask().overlap( lrg2->mask() ) ) { + // Merge larger into smaller. + if( lr1 > lr2 ) { + uint tmp = lr1; lr1 = lr2; lr2 = tmp; + Node *n = n1; n1 = n2; n2 = n; + LRG *ltmp = lrg1; lrg1 = lrg2; lrg2 = ltmp; + } + // Union lr2 into lr1 + _phc.Union( n1, n2 ); + if (lrg1->_maxfreq < lrg2->_maxfreq) + lrg1->_maxfreq = lrg2->_maxfreq; + // Merge in the IFG + _phc._ifg->Union( lr1, lr2 ); + // Combine register restrictions + lrg1->AND(lrg2->mask()); + } + } +} + +//------------------------------coalesce_driver-------------------------------- +// Copy coalescing +void PhaseCoalesce::coalesce_driver( ) { + + verify(); + // Coalesce from high frequency to low + for( uint i=0; i<_phc._cfg._num_blocks; i++ ) + coalesce( _phc._blks[i] ); + +} + +//------------------------------insert_copy_with_overlap----------------------- +// I am inserting copies to come out of SSA form. In the general case, I am +// doing a parallel renaming. I'm in the Named world now, so I can't do a +// general parallel renaming. All the copies now use "names" (live-ranges) +// to carry values instead of the explicit use-def chains. Suppose I need to +// insert 2 copies into the same block. They copy L161->L128 and L128->L132. +// If I insert them in the wrong order then L128 will get clobbered before it +// can get used by the second copy. This cannot happen in the SSA model; +// direct use-def chains get me the right value. It DOES happen in the named +// model so I have to handle the reordering of copies. +// +// In general, I need to topo-sort the placed copies to avoid conflicts. +// Its possible to have a closed cycle of copies (e.g., recirculating the same +// values around a loop). In this case I need a temp to break the cycle. +void PhaseAggressiveCoalesce::insert_copy_with_overlap( Block *b, Node *copy, uint dst_name, uint src_name ) { + + // Scan backwards for the locations of the last use of the dst_name. + // I am about to clobber the dst_name, so the copy must be inserted + // after the last use. Last use is really first-use on a backwards scan. + uint i = b->end_idx()-1; + while( 1 ) { + Node *n = b->_nodes[i]; + // Check for end of virtual copies; this is also the end of the + // parallel renaming effort. + if( n->_idx < _unique ) break; + uint idx = n->is_Copy(); + assert( idx || n->is_Con() || n->Opcode() == Op_MachProj, "Only copies during parallel renaming" ); + if( idx && _phc.Find(n->in(idx)) == dst_name ) break; + i--; + } + uint last_use_idx = i; + + // Also search for any kill of src_name that exits the block. + // Since the copy uses src_name, I have to come before any kill. + uint kill_src_idx = b->end_idx(); + // There can be only 1 kill that exits any block and that is + // the last kill. Thus it is the first kill on a backwards scan. + i = b->end_idx()-1; + while( 1 ) { + Node *n = b->_nodes[i]; + // Check for end of virtual copies; this is also the end of the + // parallel renaming effort. + if( n->_idx < _unique ) break; + assert( n->is_Copy() || n->is_Con() || n->Opcode() == Op_MachProj, "Only copies during parallel renaming" ); + if( _phc.Find(n) == src_name ) { + kill_src_idx = i; + break; + } + i--; + } + // Need a temp? Last use of dst comes after the kill of src? + if( last_use_idx >= kill_src_idx ) { + // Need to break a cycle with a temp + uint idx = copy->is_Copy(); + Node *tmp = copy->clone(); + _phc.new_lrg(tmp,_phc._maxlrg++); + // Insert new temp between copy and source + tmp ->set_req(idx,copy->in(idx)); + copy->set_req(idx,tmp); + // Save source in temp early, before source is killed + b->_nodes.insert(kill_src_idx,tmp); + _phc._cfg._bbs.map( tmp->_idx, b ); + last_use_idx++; + } + + // Insert just after last use + b->_nodes.insert(last_use_idx+1,copy); +} + +//------------------------------insert_copies---------------------------------- +void PhaseAggressiveCoalesce::insert_copies( Matcher &matcher ) { + // We do LRGs compressing and fix a liveout data only here since the other + // place in Split() is guarded by the assert which we never hit. + _phc.compress_uf_map_for_nodes(); + // Fix block's liveout data for compressed live ranges. + for(uint lrg = 1; lrg < _phc._maxlrg; lrg++ ) { + uint compressed_lrg = _phc.Find(lrg); + if( lrg != compressed_lrg ) { + for( uint bidx = 0; bidx < _phc._cfg._num_blocks; bidx++ ) { + IndexSet *liveout = _phc._live->live(_phc._cfg._blocks[bidx]); + if( liveout->member(lrg) ) { + liveout->remove(lrg); + liveout->insert(compressed_lrg); + } + } + } + } + + // All new nodes added are actual copies to replace virtual copies. + // Nodes with index less than '_unique' are original, non-virtual Nodes. + _unique = C->unique(); + + for( uint i=0; i<_phc._cfg._num_blocks; i++ ) { + Block *b = _phc._cfg._blocks[i]; + uint cnt = b->num_preds(); // Number of inputs to the Phi + + for( uint l = 1; l_nodes.size(); l++ ) { + Node *n = b->_nodes[l]; + + // Do not use removed-copies, use copied value instead + uint ncnt = n->req(); + for( uint k = 1; kin(k); + uint cidx = copy->is_Copy(); + if( cidx ) { + Node *def = copy->in(cidx); + if( _phc.Find(copy) == _phc.Find(def) ) + n->set_req(k,def); + } + } + + // Remove any explicit copies that get coalesced. + uint cidx = n->is_Copy(); + if( cidx ) { + Node *def = n->in(cidx); + if( _phc.Find(n) == _phc.Find(def) ) { + n->replace_by(def); + n->set_req(cidx,NULL); + b->_nodes.remove(l); + l--; + continue; + } + } + + if( n->is_Phi() ) { + // Get the chosen name for the Phi + uint phi_name = _phc.Find( n ); + // Ignore the pre-allocated specials + if( !phi_name ) continue; + // Check for mismatch inputs to Phi + for( uint j = 1; jin(j); + uint src_name = _phc.Find(m); + if( src_name != phi_name ) { + Block *pred = _phc._cfg._bbs[b->pred(j)->_idx]; + Node *copy; + assert(!m->is_Con() || m->is_Mach(), "all Con must be Mach"); + // Rematerialize constants instead of copying them + if( m->is_Mach() && m->as_Mach()->is_Con() && + m->as_Mach()->rematerialize() ) { + copy = m->clone(); + // Insert the copy in the predecessor basic block + pred->add_inst(copy); + // Copy any flags as well + _phc.clone_projs( pred, pred->end_idx(), m, copy, _phc._maxlrg ); + } else { + const RegMask *rm = C->matcher()->idealreg2spillmask[m->ideal_reg()]; + copy = new (C) MachSpillCopyNode(m,*rm,*rm); + // Find a good place to insert. Kinda tricky, use a subroutine + insert_copy_with_overlap(pred,copy,phi_name,src_name); + } + // Insert the copy in the use-def chain + n->set_req( j, copy ); + _phc._cfg._bbs.map( copy->_idx, pred ); + // Extend ("register allocate") the names array for the copy. + _phc._names.extend( copy->_idx, phi_name ); + } // End of if Phi names do not match + } // End of for all inputs to Phi + } else { // End of if Phi + + // Now check for 2-address instructions + uint idx; + if( n->is_Mach() && (idx=n->as_Mach()->two_adr()) ) { + // Get the chosen name for the Node + uint name = _phc.Find( n ); + assert( name, "no 2-address specials" ); + // Check for name mis-match on the 2-address input + Node *m = n->in(idx); + if( _phc.Find(m) != name ) { + Node *copy; + assert(!m->is_Con() || m->is_Mach(), "all Con must be Mach"); + // At this point it is unsafe to extend live ranges (6550579). + // Rematerialize only constants as we do for Phi above. + if( m->is_Mach() && m->as_Mach()->is_Con() && + m->as_Mach()->rematerialize() ) { + copy = m->clone(); + // Insert the copy in the basic block, just before us + b->_nodes.insert( l++, copy ); + if( _phc.clone_projs( b, l, m, copy, _phc._maxlrg ) ) + l++; + } else { + const RegMask *rm = C->matcher()->idealreg2spillmask[m->ideal_reg()]; + copy = new (C) MachSpillCopyNode( m, *rm, *rm ); + // Insert the copy in the basic block, just before us + b->_nodes.insert( l++, copy ); + } + // Insert the copy in the use-def chain + n->set_req(idx, copy ); + // Extend ("register allocate") the names array for the copy. + _phc._names.extend( copy->_idx, name ); + _phc._cfg._bbs.map( copy->_idx, b ); + } + + } // End of is two-adr + + // Insert a copy at a debug use for a lrg which has high frequency + if( (b->_freq < OPTO_DEBUG_SPLIT_FREQ) && n->is_MachSafePoint() ) { + // Walk the debug inputs to the node and check for lrg freq + JVMState* jvms = n->jvms(); + uint debug_start = jvms ? jvms->debug_start() : 999999; + uint debug_end = jvms ? jvms->debug_end() : 999999; + for(uint inpidx = debug_start; inpidx < debug_end; inpidx++) { + // Do not split monitors; they are only needed for debug table + // entries and need no code. + if( jvms->is_monitor_use(inpidx) ) continue; + Node *inp = n->in(inpidx); + uint nidx = _phc.n2lidx(inp); + LRG &lrg = lrgs(nidx); + + // If this lrg has a high frequency use/def + if( lrg._maxfreq >= OPTO_LRG_HIGH_FREQ ) { + // If the live range is also live out of this block (like it + // would be for a fast/slow idiom), the normal spill mechanism + // does an excellent job. If it is not live out of this block + // (like it would be for debug info to uncommon trap) splitting + // the live range now allows a better allocation in the high + // frequency blocks. + // Build_IFG_virtual has converted the live sets to + // live-IN info, not live-OUT info. + uint k; + for( k=0; k < b->_num_succs; k++ ) + if( _phc._live->live(b->_succs[k])->member( nidx ) ) + break; // Live in to some successor block? + if( k < b->_num_succs ) + continue; // Live out; do not pre-split + // Split the lrg at this use + const RegMask *rm = C->matcher()->idealreg2spillmask[inp->ideal_reg()]; + Node *copy = new (C) MachSpillCopyNode( inp, *rm, *rm ); + // Insert the copy in the use-def chain + n->set_req(inpidx, copy ); + // Insert the copy in the basic block, just before us + b->_nodes.insert( l++, copy ); + // Extend ("register allocate") the names array for the copy. + _phc.new_lrg( copy, _phc._maxlrg++ ); + _phc._cfg._bbs.map( copy->_idx, b ); + //tty->print_cr("Split a debug use in Aggressive Coalesce"); + } // End of if high frequency use/def + } // End of for all debug inputs + } // End of if low frequency safepoint + + } // End of if Phi + + } // End of for all instructions + } // End of for all blocks +} + +//============================================================================= +//------------------------------coalesce--------------------------------------- +// Aggressive (but pessimistic) copy coalescing of a single block + +// The following coalesce pass represents a single round of aggressive +// pessimistic coalesce. "Aggressive" means no attempt to preserve +// colorability when coalescing. This occasionally means more spills, but +// it also means fewer rounds of coalescing for better code - and that means +// faster compiles. + +// "Pessimistic" means we do not hit the fixed point in one pass (and we are +// reaching for the least fixed point to boot). This is typically solved +// with a few more rounds of coalescing, but the compiler must run fast. We +// could optimistically coalescing everything touching PhiNodes together +// into one big live range, then check for self-interference. Everywhere +// the live range interferes with self it would have to be split. Finding +// the right split points can be done with some heuristics (based on +// expected frequency of edges in the live range). In short, it's a real +// research problem and the timeline is too short to allow such research. +// Further thoughts: (1) build the LR in a pass, (2) find self-interference +// in another pass, (3) per each self-conflict, split, (4) split by finding +// the low-cost cut (min-cut) of the LR, (5) edges in the LR are weighted +// according to the GCM algorithm (or just exec freq on CFG edges). + +void PhaseAggressiveCoalesce::coalesce( Block *b ) { + // Copies are still "virtual" - meaning we have not made them explicitly + // copies. Instead, Phi functions of successor blocks have mis-matched + // live-ranges. If I fail to coalesce, I'll have to insert a copy to line + // up the live-ranges. Check for Phis in successor blocks. + uint i; + for( i=0; i_num_succs; i++ ) { + Block *bs = b->_succs[i]; + // Find index of 'b' in 'bs' predecessors + uint j=1; + while( _phc._cfg._bbs[bs->pred(j)->_idx] != b ) j++; + // Visit all the Phis in successor block + for( uint k = 1; k_nodes.size(); k++ ) { + Node *n = bs->_nodes[k]; + if( !n->is_Phi() ) break; + combine_these_two( n, n->in(j) ); + } + } // End of for all successor blocks + + + // Check _this_ block for 2-address instructions and copies. + uint cnt = b->end_idx(); + for( i = 1; i_nodes[i]; + uint idx; + // 2-address instructions have a virtual Copy matching their input + // to their output + if( n->is_Mach() && (idx = n->as_Mach()->two_adr()) ) { + MachNode *mach = n->as_Mach(); + combine_these_two( mach, mach->in(idx) ); + } + } // End of for all instructions in block +} + +//============================================================================= +//------------------------------PhaseConservativeCoalesce---------------------- +PhaseConservativeCoalesce::PhaseConservativeCoalesce( PhaseChaitin &chaitin ) : PhaseCoalesce(chaitin) { + _ulr.initialize(_phc._maxlrg); +} + +//------------------------------verify----------------------------------------- +void PhaseConservativeCoalesce::verify() { +#ifdef ASSERT + _phc.set_was_low(); +#endif +} + +//------------------------------union_helper----------------------------------- +void PhaseConservativeCoalesce::union_helper( Node *lr1_node, Node *lr2_node, uint lr1, uint lr2, Node *src_def, Node *dst_copy, Node *src_copy, Block *b, uint bindex ) { + // Join live ranges. Merge larger into smaller. Union lr2 into lr1 in the + // union-find tree + _phc.Union( lr1_node, lr2_node ); + + // Single-def live range ONLY if both live ranges are single-def. + // If both are single def, then src_def powers one live range + // and def_copy powers the other. After merging, src_def powers + // the combined live range. + lrgs(lr1)._def = (lrgs(lr1)._def == NodeSentinel || + lrgs(lr2)._def == NodeSentinel ) + ? NodeSentinel : src_def; + lrgs(lr2)._def = NULL; // No def for lrg 2 + lrgs(lr2).Clear(); // Force empty mask for LRG 2 + //lrgs(lr2)._size = 0; // Live-range 2 goes dead + lrgs(lr1)._is_oop |= lrgs(lr2)._is_oop; + lrgs(lr2)._is_oop = 0; // In particular, not an oop for GC info + + if (lrgs(lr1)._maxfreq < lrgs(lr2)._maxfreq) + lrgs(lr1)._maxfreq = lrgs(lr2)._maxfreq; + + // Copy original value instead. Intermediate copies go dead, and + // the dst_copy becomes useless. + int didx = dst_copy->is_Copy(); + dst_copy->set_req( didx, src_def ); + // Add copy to free list + // _phc.free_spillcopy(b->_nodes[bindex]); + assert( b->_nodes[bindex] == dst_copy, "" ); + dst_copy->replace_by( dst_copy->in(didx) ); + dst_copy->set_req( didx, NULL); + b->_nodes.remove(bindex); + if( bindex < b->_ihrp_index ) b->_ihrp_index--; + if( bindex < b->_fhrp_index ) b->_fhrp_index--; + + // Stretched lr1; add it to liveness of intermediate blocks + Block *b2 = _phc._cfg._bbs[src_copy->_idx]; + while( b != b2 ) { + b = _phc._cfg._bbs[b->pred(1)->_idx]; + _phc._live->live(b)->insert(lr1); + } +} + +//------------------------------compute_separating_interferences--------------- +// Factored code from copy_copy that computes extra interferences from +// lengthening a live range by double-coalescing. +uint PhaseConservativeCoalesce::compute_separating_interferences(Node *dst_copy, Node *src_copy, Block *b, uint bindex, RegMask &rm, uint reg_degree, uint rm_size, uint lr1, uint lr2 ) { + + assert(!lrgs(lr1)._fat_proj, "cannot coalesce fat_proj"); + assert(!lrgs(lr2)._fat_proj, "cannot coalesce fat_proj"); + Node *prev_copy = dst_copy->in(dst_copy->is_Copy()); + Block *b2 = b; + uint bindex2 = bindex; + while( 1 ) { + // Find previous instruction + bindex2--; // Chain backwards 1 instruction + while( bindex2 == 0 ) { // At block start, find prior block + assert( b2->num_preds() == 2, "cannot double coalesce across c-flow" ); + b2 = _phc._cfg._bbs[b2->pred(1)->_idx]; + bindex2 = b2->end_idx()-1; + } + // Get prior instruction + assert(bindex2 < b2->_nodes.size(), "index out of bounds"); + Node *x = b2->_nodes[bindex2]; + if( x == prev_copy ) { // Previous copy in copy chain? + if( prev_copy == src_copy)// Found end of chain and all interferences + break; // So break out of loop + // Else work back one in copy chain + prev_copy = prev_copy->in(prev_copy->is_Copy()); + } else { // Else collect interferences + uint lidx = _phc.Find(x); + // Found another def of live-range being stretched? + if( lidx == lr1 ) return max_juint; + if( lidx == lr2 ) return max_juint; + + // If we attempt to coalesce across a bound def + if( lrgs(lidx).is_bound() ) { + // Do not let the coalesced LRG expect to get the bound color + rm.SUBTRACT( lrgs(lidx).mask() ); + // Recompute rm_size + rm_size = rm.Size(); + //if( rm._flags ) rm_size += 1000000; + if( reg_degree >= rm_size ) return max_juint; + } + if( rm.overlap(lrgs(lidx).mask()) ) { + // Insert lidx into union LRG; returns TRUE if actually inserted + if( _ulr.insert(lidx) ) { + // Infinite-stack neighbors do not alter colorability, as they + // can always color to some other color. + if( !lrgs(lidx).mask().is_AllStack() ) { + // If this coalesce will make any new neighbor uncolorable, + // do not coalesce. + if( lrgs(lidx).just_lo_degree() ) + return max_juint; + // Bump our degree + if( ++reg_degree >= rm_size ) + return max_juint; + } // End of if not infinite-stack neighbor + } // End of if actually inserted + } // End of if live range overlaps + } // End of else collect intereferences for 1 node + } // End of while forever, scan back for intereferences + return reg_degree; +} + +//------------------------------update_ifg------------------------------------- +void PhaseConservativeCoalesce::update_ifg(uint lr1, uint lr2, IndexSet *n_lr1, IndexSet *n_lr2) { + // Some original neighbors of lr1 might have gone away + // because the constrained register mask prevented them. + // Remove lr1 from such neighbors. + IndexSetIterator one(n_lr1); + uint neighbor; + LRG &lrg1 = lrgs(lr1); + while ((neighbor = one.next()) != 0) + if( !_ulr.member(neighbor) ) + if( _phc._ifg->neighbors(neighbor)->remove(lr1) ) + lrgs(neighbor).inc_degree( -lrg1.compute_degree(lrgs(neighbor)) ); + + + // lr2 is now called (coalesced into) lr1. + // Remove lr2 from the IFG. + IndexSetIterator two(n_lr2); + LRG &lrg2 = lrgs(lr2); + while ((neighbor = two.next()) != 0) + if( _phc._ifg->neighbors(neighbor)->remove(lr2) ) + lrgs(neighbor).inc_degree( -lrg2.compute_degree(lrgs(neighbor)) ); + + // Some neighbors of intermediate copies now interfere with the + // combined live range. + IndexSetIterator three(&_ulr); + while ((neighbor = three.next()) != 0) + if( _phc._ifg->neighbors(neighbor)->insert(lr1) ) + lrgs(neighbor).inc_degree( lrg1.compute_degree(lrgs(neighbor)) ); +} + +//------------------------------record_bias------------------------------------ +static void record_bias( const PhaseIFG *ifg, int lr1, int lr2 ) { + // Tag copy bias here + if( !ifg->lrgs(lr1)._copy_bias ) + ifg->lrgs(lr1)._copy_bias = lr2; + if( !ifg->lrgs(lr2)._copy_bias ) + ifg->lrgs(lr2)._copy_bias = lr1; +} + +//------------------------------copy_copy-------------------------------------- +// See if I can coalesce a series of multiple copies together. I need the +// final dest copy and the original src copy. They can be the same Node. +// Compute the compatible register masks. +bool PhaseConservativeCoalesce::copy_copy( Node *dst_copy, Node *src_copy, Block *b, uint bindex ) { + + if( !dst_copy->is_SpillCopy() ) return false; + if( !src_copy->is_SpillCopy() ) return false; + Node *src_def = src_copy->in(src_copy->is_Copy()); + uint lr1 = _phc.Find(dst_copy); + uint lr2 = _phc.Find(src_def ); + + // Same live ranges already? + if( lr1 == lr2 ) return false; + + // Interfere? + if( _phc._ifg->test_edge_sq( lr1, lr2 ) ) return false; + + // Not an oop->int cast; oop->oop, int->int, AND int->oop are OK. + if( !lrgs(lr1)._is_oop && lrgs(lr2)._is_oop ) // not an oop->int cast + return false; + + // Coalescing between an aligned live range and a mis-aligned live range? + // No, no! Alignment changes how we count degree. + if( lrgs(lr1)._fat_proj != lrgs(lr2)._fat_proj ) + return false; + + // Sort; use smaller live-range number + Node *lr1_node = dst_copy; + Node *lr2_node = src_def; + if( lr1 > lr2 ) { + uint tmp = lr1; lr1 = lr2; lr2 = tmp; + lr1_node = src_def; lr2_node = dst_copy; + } + + // Check for compatibility of the 2 live ranges by + // intersecting their allowed register sets. + RegMask rm = lrgs(lr1).mask(); + rm.AND(lrgs(lr2).mask()); + // Number of bits free + uint rm_size = rm.Size(); + + // If we can use any stack slot, then effective size is infinite + if( rm.is_AllStack() ) rm_size += 1000000; + // Incompatible masks, no way to coalesce + if( rm_size == 0 ) return false; + + // Another early bail-out test is when we are double-coalescing and the + // 2 copies are seperated by some control flow. + if( dst_copy != src_copy ) { + Block *src_b = _phc._cfg._bbs[src_copy->_idx]; + Block *b2 = b; + while( b2 != src_b ) { + if( b2->num_preds() > 2 ){// Found merge-point + _phc._lost_opp_cflow_coalesce++; + // extra record_bias commented out because Chris believes it is not + // productive. Since we can record only 1 bias, we want to choose one + // that stands a chance of working and this one probably does not. + //record_bias( _phc._lrgs, lr1, lr2 ); + return false; // To hard to find all interferences + } + b2 = _phc._cfg._bbs[b2->pred(1)->_idx]; + } + } + + // Union the two interference sets together into '_ulr' + uint reg_degree = _ulr.lrg_union( lr1, lr2, rm_size, _phc._ifg, rm ); + + if( reg_degree >= rm_size ) { + record_bias( _phc._ifg, lr1, lr2 ); + return false; + } + + // Now I need to compute all the interferences between dst_copy and + // src_copy. I'm not willing visit the entire interference graph, so + // I limit my search to things in dst_copy's block or in a straight + // line of previous blocks. I give up at merge points or when I get + // more interferences than my degree. I can stop when I find src_copy. + if( dst_copy != src_copy ) { + reg_degree = compute_separating_interferences(dst_copy, src_copy, b, bindex, rm, rm_size, reg_degree, lr1, lr2 ); + if( reg_degree == max_juint ) { + record_bias( _phc._ifg, lr1, lr2 ); + return false; + } + } // End of if dst_copy & src_copy are different + + + // ---- THE COMBINED LRG IS COLORABLE ---- + + // YEAH - Now coalesce this copy away + assert( lrgs(lr1).num_regs() == lrgs(lr2).num_regs(), "" ); + + IndexSet *n_lr1 = _phc._ifg->neighbors(lr1); + IndexSet *n_lr2 = _phc._ifg->neighbors(lr2); + + // Update the interference graph + update_ifg(lr1, lr2, n_lr1, n_lr2); + + _ulr.remove(lr1); + + // Uncomment the following code to trace Coalescing in great detail. + // + //if (false) { + // tty->cr(); + // tty->print_cr("#######################################"); + // tty->print_cr("union %d and %d", lr1, lr2); + // n_lr1->dump(); + // n_lr2->dump(); + // tty->print_cr("resulting set is"); + // _ulr.dump(); + //} + + // Replace n_lr1 with the new combined live range. _ulr will use + // n_lr1's old memory on the next iteration. n_lr2 is cleared to + // send its internal memory to the free list. + _ulr.swap(n_lr1); + _ulr.clear(); + n_lr2->clear(); + + lrgs(lr1).set_degree( _phc._ifg->effective_degree(lr1) ); + lrgs(lr2).set_degree( 0 ); + + // Join live ranges. Merge larger into smaller. Union lr2 into lr1 in the + // union-find tree + union_helper( lr1_node, lr2_node, lr1, lr2, src_def, dst_copy, src_copy, b, bindex ); + // Combine register restrictions + lrgs(lr1).set_mask(rm); + lrgs(lr1).compute_set_mask_size(); + lrgs(lr1)._cost += lrgs(lr2)._cost; + lrgs(lr1)._area += lrgs(lr2)._area; + + // While its uncommon to successfully coalesce live ranges that started out + // being not-lo-degree, it can happen. In any case the combined coalesced + // live range better Simplify nicely. + lrgs(lr1)._was_lo = 1; + + // kinda expensive to do all the time + //tty->print_cr("warning: slow verify happening"); + //_phc._ifg->verify( &_phc ); + return true; +} + +//------------------------------coalesce--------------------------------------- +// Conservative (but pessimistic) copy coalescing of a single block +void PhaseConservativeCoalesce::coalesce( Block *b ) { + // Bail out on infrequent blocks + if( b->is_uncommon(_phc._cfg._bbs) ) + return; + // Check this block for copies. + for( uint i = 1; iend_idx(); i++ ) { + // Check for actual copies on inputs. Coalesce a copy into its + // input if use and copy's input are compatible. + Node *copy1 = b->_nodes[i]; + uint idx1 = copy1->is_Copy(); + if( !idx1 ) continue; // Not a copy + + if( copy_copy(copy1,copy1,b,i) ) { + i--; // Retry, same location in block + PhaseChaitin::_conserv_coalesce++; // Collect stats on success + continue; + } + + /* do not attempt pairs. About 1/2 of all pairs can be removed by + post-alloc. The other set are too few to bother. + Node *copy2 = copy1->in(idx1); + uint idx2 = copy2->is_Copy(); + if( !idx2 ) continue; + if( copy_copy(copy1,copy2,b,i) ) { + i--; // Retry, same location in block + PhaseChaitin::_conserv_coalesce_pair++; // Collect stats on success + continue; + } + */ + } +} diff --git a/hotspot/src/share/vm/opto/coalesce.hpp b/hotspot/src/share/vm/opto/coalesce.hpp new file mode 100644 index 00000000000..b7cd9da876b --- /dev/null +++ b/hotspot/src/share/vm/opto/coalesce.hpp @@ -0,0 +1,109 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class LoopTree; +class LRG; +class LRG_List; +class Matcher; +class PhaseIFG; +class PhaseCFG; + +//------------------------------PhaseCoalesce---------------------------------- +class PhaseCoalesce : public Phase { +protected: + PhaseChaitin &_phc; + +public: + // Coalesce copies + PhaseCoalesce( PhaseChaitin &chaitin ) : Phase(Coalesce), _phc(chaitin) { } + + virtual void verify() = 0; + + // Coalesce copies + void coalesce_driver( ); + + // Coalesce copies in this block + virtual void coalesce( Block *b ) = 0; + + // Attempt to coalesce live ranges defined by these 2 + void combine_these_two( Node *n1, Node *n2 ); + + LRG &lrgs( uint lidx ) { return _phc.lrgs(lidx); } +#ifndef PRODUCT + // Dump internally name + void dump( Node *n ) const; + // Dump whole shebang + void dump() const; +#endif +}; + +//------------------------------PhaseAggressiveCoalesce------------------------ +// Aggressively, pessimistic coalesce copies. Aggressive means ignore graph +// colorability; perhaps coalescing to the point of forcing a spill. +// Pessimistic means we cannot coalesce if 2 live ranges interfere. This +// implies we do not hit a fixed point right away. +class PhaseAggressiveCoalesce : public PhaseCoalesce { + uint _unique; +public: + // Coalesce copies + PhaseAggressiveCoalesce( PhaseChaitin &chaitin ) : PhaseCoalesce(chaitin) {} + + virtual void verify() { }; + + // Aggressively coalesce copies in this block + virtual void coalesce( Block *b ); + + // Where I fail to coalesce, manifest virtual copies as the Real Thing + void insert_copies( Matcher &matcher ); + + // Copy insertion needs some smarts in case live ranges overlap + void insert_copy_with_overlap( Block *b, Node *copy, uint dst_name, uint src_name ); +}; + + +//------------------------------PhaseConservativeCoalesce---------------------- +// Conservatively, pessimistic coalesce copies. Conservative means do not +// coalesce if the resultant live range will be uncolorable. Pessimistic +// means we cannot coalesce if 2 live ranges interfere. This implies we do +// not hit a fixed point right away. +class PhaseConservativeCoalesce : public PhaseCoalesce { + IndexSet _ulr; // Union live range interferences +public: + // Coalesce copies + PhaseConservativeCoalesce( PhaseChaitin &chaitin ); + + virtual void verify(); + + // Conservatively coalesce copies in this block + virtual void coalesce( Block *b ); + + // Coalesce this chain of copies away + bool copy_copy( Node *dst_copy, Node *src_copy, Block *b, uint bindex ); + + void union_helper( Node *lr1_node, Node *lr2_node, uint lr1, uint lr2, Node *src_def, Node *dst_copy, Node *src_copy, Block *b, uint bindex ); + + uint compute_separating_interferences(Node *dst_copy, Node *src_copy, Block *b, uint bindex, RegMask &rm, uint rm_size, uint reg_degree, uint lr1, uint lr2); + + void update_ifg(uint lr1, uint lr2, IndexSet *n_lr1, IndexSet *n_lr2); +}; diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp new file mode 100644 index 00000000000..d62dbf3442f --- /dev/null +++ b/hotspot/src/share/vm/opto/compile.cpp @@ -0,0 +1,2384 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_compile.cpp.incl" + +/// Support for intrinsics. + +// Return the index at which m must be inserted (or already exists). +// The sort order is by the address of the ciMethod, with is_virtual as minor key. +int Compile::intrinsic_insertion_index(ciMethod* m, bool is_virtual) { +#ifdef ASSERT + for (int i = 1; i < _intrinsics->length(); i++) { + CallGenerator* cg1 = _intrinsics->at(i-1); + CallGenerator* cg2 = _intrinsics->at(i); + assert(cg1->method() != cg2->method() + ? cg1->method() < cg2->method() + : cg1->is_virtual() < cg2->is_virtual(), + "compiler intrinsics list must stay sorted"); + } +#endif + // Binary search sorted list, in decreasing intervals [lo, hi]. + int lo = 0, hi = _intrinsics->length()-1; + while (lo <= hi) { + int mid = (uint)(hi + lo) / 2; + ciMethod* mid_m = _intrinsics->at(mid)->method(); + if (m < mid_m) { + hi = mid-1; + } else if (m > mid_m) { + lo = mid+1; + } else { + // look at minor sort key + bool mid_virt = _intrinsics->at(mid)->is_virtual(); + if (is_virtual < mid_virt) { + hi = mid-1; + } else if (is_virtual > mid_virt) { + lo = mid+1; + } else { + return mid; // exact match + } + } + } + return lo; // inexact match +} + +void Compile::register_intrinsic(CallGenerator* cg) { + if (_intrinsics == NULL) { + _intrinsics = new GrowableArray(60); + } + // This code is stolen from ciObjectFactory::insert. + // Really, GrowableArray should have methods for + // insert_at, remove_at, and binary_search. + int len = _intrinsics->length(); + int index = intrinsic_insertion_index(cg->method(), cg->is_virtual()); + if (index == len) { + _intrinsics->append(cg); + } else { +#ifdef ASSERT + CallGenerator* oldcg = _intrinsics->at(index); + assert(oldcg->method() != cg->method() || oldcg->is_virtual() != cg->is_virtual(), "don't register twice"); +#endif + _intrinsics->append(_intrinsics->at(len-1)); + int pos; + for (pos = len-2; pos >= index; pos--) { + _intrinsics->at_put(pos+1,_intrinsics->at(pos)); + } + _intrinsics->at_put(index, cg); + } + assert(find_intrinsic(cg->method(), cg->is_virtual()) == cg, "registration worked"); +} + +CallGenerator* Compile::find_intrinsic(ciMethod* m, bool is_virtual) { + assert(m->is_loaded(), "don't try this on unloaded methods"); + if (_intrinsics != NULL) { + int index = intrinsic_insertion_index(m, is_virtual); + if (index < _intrinsics->length() + && _intrinsics->at(index)->method() == m + && _intrinsics->at(index)->is_virtual() == is_virtual) { + return _intrinsics->at(index); + } + } + // Lazily create intrinsics for intrinsic IDs well-known in the runtime. + if (m->intrinsic_id() != vmIntrinsics::_none) { + CallGenerator* cg = make_vm_intrinsic(m, is_virtual); + if (cg != NULL) { + // Save it for next time: + register_intrinsic(cg); + return cg; + } else { + gather_intrinsic_statistics(m->intrinsic_id(), is_virtual, _intrinsic_disabled); + } + } + return NULL; +} + +// Compile:: register_library_intrinsics and make_vm_intrinsic are defined +// in library_call.cpp. + + +#ifndef PRODUCT +// statistics gathering... + +juint Compile::_intrinsic_hist_count[vmIntrinsics::ID_LIMIT] = {0}; +jubyte Compile::_intrinsic_hist_flags[vmIntrinsics::ID_LIMIT] = {0}; + +bool Compile::gather_intrinsic_statistics(vmIntrinsics::ID id, bool is_virtual, int flags) { + assert(id > vmIntrinsics::_none && id < vmIntrinsics::ID_LIMIT, "oob"); + int oflags = _intrinsic_hist_flags[id]; + assert(flags != 0, "what happened?"); + if (is_virtual) { + flags |= _intrinsic_virtual; + } + bool changed = (flags != oflags); + if ((flags & _intrinsic_worked) != 0) { + juint count = (_intrinsic_hist_count[id] += 1); + if (count == 1) { + changed = true; // first time + } + // increment the overall count also: + _intrinsic_hist_count[vmIntrinsics::_none] += 1; + } + if (changed) { + if (((oflags ^ flags) & _intrinsic_virtual) != 0) { + // Something changed about the intrinsic's virtuality. + if ((flags & _intrinsic_virtual) != 0) { + // This is the first use of this intrinsic as a virtual call. + if (oflags != 0) { + // We already saw it as a non-virtual, so note both cases. + flags |= _intrinsic_both; + } + } else if ((oflags & _intrinsic_both) == 0) { + // This is the first use of this intrinsic as a non-virtual + flags |= _intrinsic_both; + } + } + _intrinsic_hist_flags[id] = (jubyte) (oflags | flags); + } + // update the overall flags also: + _intrinsic_hist_flags[vmIntrinsics::_none] |= (jubyte) flags; + return changed; +} + +static char* format_flags(int flags, char* buf) { + buf[0] = 0; + if ((flags & Compile::_intrinsic_worked) != 0) strcat(buf, ",worked"); + if ((flags & Compile::_intrinsic_failed) != 0) strcat(buf, ",failed"); + if ((flags & Compile::_intrinsic_disabled) != 0) strcat(buf, ",disabled"); + if ((flags & Compile::_intrinsic_virtual) != 0) strcat(buf, ",virtual"); + if ((flags & Compile::_intrinsic_both) != 0) strcat(buf, ",nonvirtual"); + if (buf[0] == 0) strcat(buf, ","); + assert(buf[0] == ',', "must be"); + return &buf[1]; +} + +void Compile::print_intrinsic_statistics() { + char flagsbuf[100]; + ttyLocker ttyl; + if (xtty != NULL) xtty->head("statistics type='intrinsic'"); + tty->print_cr("Compiler intrinsic usage:"); + juint total = _intrinsic_hist_count[vmIntrinsics::_none]; + if (total == 0) total = 1; // avoid div0 in case of no successes + #define PRINT_STAT_LINE(name, c, f) \ + tty->print_cr(" %4d (%4.1f%%) %s (%s)", (int)(c), ((c) * 100.0) / total, name, f); + for (int index = 1 + (int)vmIntrinsics::_none; index < (int)vmIntrinsics::ID_LIMIT; index++) { + vmIntrinsics::ID id = (vmIntrinsics::ID) index; + int flags = _intrinsic_hist_flags[id]; + juint count = _intrinsic_hist_count[id]; + if ((flags | count) != 0) { + PRINT_STAT_LINE(vmIntrinsics::name_at(id), count, format_flags(flags, flagsbuf)); + } + } + PRINT_STAT_LINE("total", total, format_flags(_intrinsic_hist_flags[vmIntrinsics::_none], flagsbuf)); + if (xtty != NULL) xtty->tail("statistics"); +} + +void Compile::print_statistics() { + { ttyLocker ttyl; + if (xtty != NULL) xtty->head("statistics type='opto'"); + Parse::print_statistics(); + PhaseCCP::print_statistics(); + PhaseRegAlloc::print_statistics(); + Scheduling::print_statistics(); + PhasePeephole::print_statistics(); + PhaseIdealLoop::print_statistics(); + if (xtty != NULL) xtty->tail("statistics"); + } + if (_intrinsic_hist_flags[vmIntrinsics::_none] != 0) { + // put this under its own element. + print_intrinsic_statistics(); + } +} +#endif //PRODUCT + +// Support for bundling info +Bundle* Compile::node_bundling(const Node *n) { + assert(valid_bundle_info(n), "oob"); + return &_node_bundling_base[n->_idx]; +} + +bool Compile::valid_bundle_info(const Node *n) { + return (_node_bundling_limit > n->_idx); +} + + +// Identify all nodes that are reachable from below, useful. +// Use breadth-first pass that records state in a Unique_Node_List, +// recursive traversal is slower. +void Compile::identify_useful_nodes(Unique_Node_List &useful) { + int estimated_worklist_size = unique(); + useful.map( estimated_worklist_size, NULL ); // preallocate space + + // Initialize worklist + if (root() != NULL) { useful.push(root()); } + // If 'top' is cached, declare it useful to preserve cached node + if( cached_top_node() ) { useful.push(cached_top_node()); } + + // Push all useful nodes onto the list, breadthfirst + for( uint next = 0; next < useful.size(); ++next ) { + assert( next < unique(), "Unique useful nodes < total nodes"); + Node *n = useful.at(next); + uint max = n->len(); + for( uint i = 0; i < max; ++i ) { + Node *m = n->in(i); + if( m == NULL ) continue; + useful.push(m); + } + } +} + +// Disconnect all useless nodes by disconnecting those at the boundary. +void Compile::remove_useless_nodes(Unique_Node_List &useful) { + uint next = 0; + while( next < useful.size() ) { + Node *n = useful.at(next++); + // Use raw traversal of out edges since this code removes out edges + int max = n->outcnt(); + for (int j = 0; j < max; ++j ) { + Node* child = n->raw_out(j); + if( ! useful.member(child) ) { + assert( !child->is_top() || child != top(), + "If top is cached in Compile object it is in useful list"); + // Only need to remove this out-edge to the useless node + n->raw_del_out(j); + --j; + --max; + } + } + if (n->outcnt() == 1 && n->has_special_unique_user()) { + record_for_igvn( n->unique_out() ); + } + } + debug_only(verify_graph_edges(true/*check for no_dead_code*/);) +} + +//------------------------------frame_size_in_words----------------------------- +// frame_slots in units of words +int Compile::frame_size_in_words() const { + // shift is 0 in LP32 and 1 in LP64 + const int shift = (LogBytesPerWord - LogBytesPerInt); + int words = _frame_slots >> shift; + assert( words << shift == _frame_slots, "frame size must be properly aligned in LP64" ); + return words; +} + +// ============================================================================ +//------------------------------CompileWrapper--------------------------------- +class CompileWrapper : public StackObj { + Compile *const _compile; + public: + CompileWrapper(Compile* compile); + + ~CompileWrapper(); +}; + +CompileWrapper::CompileWrapper(Compile* compile) : _compile(compile) { + // the Compile* pointer is stored in the current ciEnv: + ciEnv* env = compile->env(); + assert(env == ciEnv::current(), "must already be a ciEnv active"); + assert(env->compiler_data() == NULL, "compile already active?"); + env->set_compiler_data(compile); + assert(compile == Compile::current(), "sanity"); + + compile->set_type_dict(NULL); + compile->set_type_hwm(NULL); + compile->set_type_last_size(0); + compile->set_last_tf(NULL, NULL); + compile->set_indexSet_arena(NULL); + compile->set_indexSet_free_block_list(NULL); + compile->init_type_arena(); + Type::Initialize(compile); + _compile->set_scratch_buffer_blob(NULL); + _compile->begin_method(); +} +CompileWrapper::~CompileWrapper() { + if (_compile->failing()) { + _compile->print_method("Failed"); + } + _compile->end_method(); + if (_compile->scratch_buffer_blob() != NULL) + BufferBlob::free(_compile->scratch_buffer_blob()); + _compile->env()->set_compiler_data(NULL); +} + + +//----------------------------print_compile_messages--------------------------- +void Compile::print_compile_messages() { +#ifndef PRODUCT + // Check if recompiling + if (_subsume_loads == false && PrintOpto) { + // Recompiling without allowing machine instructions to subsume loads + tty->print_cr("*********************************************************"); + tty->print_cr("** Bailout: Recompile without subsuming loads **"); + tty->print_cr("*********************************************************"); + } + if (env()->break_at_compile()) { + // Open the debugger when compiing this method. + tty->print("### Breaking when compiling: "); + method()->print_short_name(); + tty->cr(); + BREAKPOINT; + } + + if( PrintOpto ) { + if (is_osr_compilation()) { + tty->print("[OSR]%3d", _compile_id); + } else { + tty->print("%3d", _compile_id); + } + } +#endif +} + + +void Compile::init_scratch_buffer_blob() { + if( scratch_buffer_blob() != NULL ) return; + + // Construct a temporary CodeBuffer to have it construct a BufferBlob + // Cache this BufferBlob for this compile. + ResourceMark rm; + int size = (MAX_inst_size + MAX_stubs_size + MAX_const_size); + BufferBlob* blob = BufferBlob::create("Compile::scratch_buffer", size); + // Record the buffer blob for next time. + set_scratch_buffer_blob(blob); + guarantee(scratch_buffer_blob() != NULL, "Need BufferBlob for code generation"); + + // Initialize the relocation buffers + relocInfo* locs_buf = (relocInfo*) blob->instructions_end() - MAX_locs_size; + set_scratch_locs_memory(locs_buf); +} + + +//-----------------------scratch_emit_size------------------------------------- +// Helper function that computes size by emitting code +uint Compile::scratch_emit_size(const Node* n) { + // Emit into a trash buffer and count bytes emitted. + // This is a pretty expensive way to compute a size, + // but it works well enough if seldom used. + // All common fixed-size instructions are given a size + // method by the AD file. + // Note that the scratch buffer blob and locs memory are + // allocated at the beginning of the compile task, and + // may be shared by several calls to scratch_emit_size. + // The allocation of the scratch buffer blob is particularly + // expensive, since it has to grab the code cache lock. + BufferBlob* blob = this->scratch_buffer_blob(); + assert(blob != NULL, "Initialize BufferBlob at start"); + assert(blob->size() > MAX_inst_size, "sanity"); + relocInfo* locs_buf = scratch_locs_memory(); + address blob_begin = blob->instructions_begin(); + address blob_end = (address)locs_buf; + assert(blob->instructions_contains(blob_end), "sanity"); + CodeBuffer buf(blob_begin, blob_end - blob_begin); + buf.initialize_consts_size(MAX_const_size); + buf.initialize_stubs_size(MAX_stubs_size); + assert(locs_buf != NULL, "sanity"); + int lsize = MAX_locs_size / 2; + buf.insts()->initialize_shared_locs(&locs_buf[0], lsize); + buf.stubs()->initialize_shared_locs(&locs_buf[lsize], lsize); + n->emit(buf, this->regalloc()); + return buf.code_size(); +} + +void Compile::record_for_escape_analysis(Node* n) { + if (_congraph != NULL) + _congraph->record_for_escape_analysis(n); +} + + +// ============================================================================ +//------------------------------Compile standard------------------------------- +debug_only( int Compile::_debug_idx = 100000; ) + +// Compile a method. entry_bci is -1 for normal compilations and indicates +// the continuation bci for on stack replacement. + + +Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr_bci, bool subsume_loads ) + : Phase(Compiler), + _env(ci_env), + _log(ci_env->log()), + _compile_id(ci_env->compile_id()), + _save_argument_registers(false), + _stub_name(NULL), + _stub_function(NULL), + _stub_entry_point(NULL), + _method(target), + _entry_bci(osr_bci), + _initial_gvn(NULL), + _for_igvn(NULL), + _warm_calls(NULL), + _subsume_loads(subsume_loads), + _failure_reason(NULL), + _code_buffer("Compile::Fill_buffer"), + _orig_pc_slot(0), + _orig_pc_slot_offset_in_bytes(0), + _node_bundling_limit(0), + _node_bundling_base(NULL), +#ifndef PRODUCT + _trace_opto_output(TraceOptoOutput || method()->has_option("TraceOptoOutput")), + _printer(IdealGraphPrinter::printer()), +#endif + _congraph(NULL) { + C = this; + + CompileWrapper cw(this); +#ifndef PRODUCT + if (TimeCompiler2) { + tty->print(" "); + target->holder()->name()->print(); + tty->print("."); + target->print_short_name(); + tty->print(" "); + } + TraceTime t1("Total compilation time", &_t_totalCompilation, TimeCompiler, TimeCompiler2); + TraceTime t2(NULL, &_t_methodCompilation, TimeCompiler, false); + set_print_assembly(PrintOptoAssembly || _method->should_print_assembly()); +#endif + + if (ProfileTraps) { + // Make sure the method being compiled gets its own MDO, + // so we can at least track the decompile_count(). + method()->build_method_data(); + } + + Init(::AliasLevel); + + + print_compile_messages(); + + if (UseOldInlining || PrintCompilation NOT_PRODUCT( || PrintOpto) ) + _ilt = InlineTree::build_inline_tree_root(); + else + _ilt = NULL; + + // Even if NO memory addresses are used, MergeMem nodes must have at least 1 slice + assert(num_alias_types() >= AliasIdxRaw, ""); + +#define MINIMUM_NODE_HASH 1023 + // Node list that Iterative GVN will start with + Unique_Node_List for_igvn(comp_arena()); + set_for_igvn(&for_igvn); + + // GVN that will be run immediately on new nodes + uint estimated_size = method()->code_size()*4+64; + estimated_size = (estimated_size < MINIMUM_NODE_HASH ? MINIMUM_NODE_HASH : estimated_size); + PhaseGVN gvn(node_arena(), estimated_size); + set_initial_gvn(&gvn); + + if (DoEscapeAnalysis) + _congraph = new ConnectionGraph(this); + + { // Scope for timing the parser + TracePhase t3("parse", &_t_parser, true); + + // Put top into the hash table ASAP. + initial_gvn()->transform_no_reclaim(top()); + + // Set up tf(), start(), and find a CallGenerator. + CallGenerator* cg; + if (is_osr_compilation()) { + const TypeTuple *domain = StartOSRNode::osr_domain(); + const TypeTuple *range = TypeTuple::make_range(method()->signature()); + init_tf(TypeFunc::make(domain, range)); + StartNode* s = new (this, 2) StartOSRNode(root(), domain); + initial_gvn()->set_type_bottom(s); + init_start(s); + cg = CallGenerator::for_osr(method(), entry_bci()); + } else { + // Normal case. + init_tf(TypeFunc::make(method())); + StartNode* s = new (this, 2) StartNode(root(), tf()->domain()); + initial_gvn()->set_type_bottom(s); + init_start(s); + float past_uses = method()->interpreter_invocation_count(); + float expected_uses = past_uses; + cg = CallGenerator::for_inline(method(), expected_uses); + } + if (failing()) return; + if (cg == NULL) { + record_method_not_compilable_all_tiers("cannot parse method"); + return; + } + JVMState* jvms = build_start_state(start(), tf()); + if ((jvms = cg->generate(jvms)) == NULL) { + record_method_not_compilable("method parse failed"); + return; + } + GraphKit kit(jvms); + + if (!kit.stopped()) { + // Accept return values, and transfer control we know not where. + // This is done by a special, unique ReturnNode bound to root. + return_values(kit.jvms()); + } + + if (kit.has_exceptions()) { + // Any exceptions that escape from this call must be rethrown + // to whatever caller is dynamically above us on the stack. + // This is done by a special, unique RethrowNode bound to root. + rethrow_exceptions(kit.transfer_exceptions_into_jvms()); + } + + // Remove clutter produced by parsing. + if (!failing()) { + ResourceMark rm; + PhaseRemoveUseless pru(initial_gvn(), &for_igvn); + } + } + + // Note: Large methods are capped off in do_one_bytecode(). + if (failing()) return; + + // After parsing, node notes are no longer automagic. + // They must be propagated by register_new_node_with_optimizer(), + // clone(), or the like. + set_default_node_notes(NULL); + + for (;;) { + int successes = Inline_Warm(); + if (failing()) return; + if (successes == 0) break; + } + + // Drain the list. + Finish_Warm(); +#ifndef PRODUCT + if (_printer) { + _printer->print_inlining(this); + } +#endif + + if (failing()) return; + NOT_PRODUCT( verify_graph_edges(); ) + + // Perform escape analysis + if (_congraph != NULL) { + NOT_PRODUCT( TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, TimeCompiler); ) + _congraph->compute_escape(); +#ifndef PRODUCT + if (PrintEscapeAnalysis) { + _congraph->dump(); + } +#endif + } + // Now optimize + Optimize(); + if (failing()) return; + NOT_PRODUCT( verify_graph_edges(); ) + +#ifndef PRODUCT + if (PrintIdeal) { + ttyLocker ttyl; // keep the following output all in one block + // This output goes directly to the tty, not the compiler log. + // To enable tools to match it up with the compilation activity, + // be sure to tag this tty output with the compile ID. + if (xtty != NULL) { + xtty->head("ideal compile_id='%d'%s", compile_id(), + is_osr_compilation() ? " compile_kind='osr'" : + ""); + } + root()->dump(9999); + if (xtty != NULL) { + xtty->tail("ideal"); + } + } +#endif + + // Now that we know the size of all the monitors we can add a fixed slot + // for the original deopt pc. + + _orig_pc_slot = fixed_slots(); + int next_slot = _orig_pc_slot + (sizeof(address) / VMRegImpl::stack_slot_size); + set_fixed_slots(next_slot); + + // Now generate code + Code_Gen(); + if (failing()) return; + + // Check if we want to skip execution of all compiled code. + { +#ifndef PRODUCT + if (OptoNoExecute) { + record_method_not_compilable("+OptoNoExecute"); // Flag as failed + return; + } + TracePhase t2("install_code", &_t_registerMethod, TimeCompiler); +#endif + + if (is_osr_compilation()) { + _code_offsets.set_value(CodeOffsets::Verified_Entry, 0); + _code_offsets.set_value(CodeOffsets::OSR_Entry, _first_block_size); + } else { + _code_offsets.set_value(CodeOffsets::Verified_Entry, _first_block_size); + _code_offsets.set_value(CodeOffsets::OSR_Entry, 0); + } + + env()->register_method(_method, _entry_bci, + &_code_offsets, + _orig_pc_slot_offset_in_bytes, + code_buffer(), + frame_size_in_words(), _oop_map_set, + &_handler_table, &_inc_table, + compiler, + env()->comp_level(), + true, /*has_debug_info*/ + has_unsafe_access() + ); + } +} + +//------------------------------Compile---------------------------------------- +// Compile a runtime stub +Compile::Compile( ciEnv* ci_env, + TypeFunc_generator generator, + address stub_function, + const char *stub_name, + int is_fancy_jump, + bool pass_tls, + bool save_arg_registers, + bool return_pc ) + : Phase(Compiler), + _env(ci_env), + _log(ci_env->log()), + _compile_id(-1), + _save_argument_registers(save_arg_registers), + _method(NULL), + _stub_name(stub_name), + _stub_function(stub_function), + _stub_entry_point(NULL), + _entry_bci(InvocationEntryBci), + _initial_gvn(NULL), + _for_igvn(NULL), + _warm_calls(NULL), + _orig_pc_slot(0), + _orig_pc_slot_offset_in_bytes(0), + _subsume_loads(true), + _failure_reason(NULL), + _code_buffer("Compile::Fill_buffer"), + _node_bundling_limit(0), + _node_bundling_base(NULL), +#ifndef PRODUCT + _trace_opto_output(TraceOptoOutput), + _printer(NULL), +#endif + _congraph(NULL) { + C = this; + +#ifndef PRODUCT + TraceTime t1(NULL, &_t_totalCompilation, TimeCompiler, false); + TraceTime t2(NULL, &_t_stubCompilation, TimeCompiler, false); + set_print_assembly(PrintFrameConverterAssembly); +#endif + CompileWrapper cw(this); + Init(/*AliasLevel=*/ 0); + init_tf((*generator)()); + + { + // The following is a dummy for the sake of GraphKit::gen_stub + Unique_Node_List for_igvn(comp_arena()); + set_for_igvn(&for_igvn); // not used, but some GraphKit guys push on this + PhaseGVN gvn(Thread::current()->resource_area(),255); + set_initial_gvn(&gvn); // not significant, but GraphKit guys use it pervasively + gvn.transform_no_reclaim(top()); + + GraphKit kit; + kit.gen_stub(stub_function, stub_name, is_fancy_jump, pass_tls, return_pc); + } + + NOT_PRODUCT( verify_graph_edges(); ) + Code_Gen(); + if (failing()) return; + + + // Entry point will be accessed using compile->stub_entry_point(); + if (code_buffer() == NULL) { + Matcher::soft_match_failure(); + } else { + if (PrintAssembly && (WizardMode || Verbose)) + tty->print_cr("### Stub::%s", stub_name); + + if (!failing()) { + assert(_fixed_slots == 0, "no fixed slots used for runtime stubs"); + + // Make the NMethod + // For now we mark the frame as never safe for profile stackwalking + RuntimeStub *rs = RuntimeStub::new_runtime_stub(stub_name, + code_buffer(), + CodeOffsets::frame_never_safe, + // _code_offsets.value(CodeOffsets::Frame_Complete), + frame_size_in_words(), + _oop_map_set, + save_arg_registers); + assert(rs != NULL && rs->is_runtime_stub(), "sanity check"); + + _stub_entry_point = rs->entry_point(); + } + } +} + +#ifndef PRODUCT +void print_opto_verbose_signature( const TypeFunc *j_sig, const char *stub_name ) { + if(PrintOpto && Verbose) { + tty->print("%s ", stub_name); j_sig->print_flattened(); tty->cr(); + } +} +#endif + +void Compile::print_codes() { +} + +//------------------------------Init------------------------------------------- +// Prepare for a single compilation +void Compile::Init(int aliaslevel) { + _unique = 0; + _regalloc = NULL; + + _tf = NULL; // filled in later + _top = NULL; // cached later + _matcher = NULL; // filled in later + _cfg = NULL; // filled in later + + set_24_bit_selection_and_mode(Use24BitFP, false); + + _node_note_array = NULL; + _default_node_notes = NULL; + + _immutable_memory = NULL; // filled in at first inquiry + + // Globally visible Nodes + // First set TOP to NULL to give safe behavior during creation of RootNode + set_cached_top_node(NULL); + set_root(new (this, 3) RootNode()); + // Now that you have a Root to point to, create the real TOP + set_cached_top_node( new (this, 1) ConNode(Type::TOP) ); + set_recent_alloc(NULL, NULL); + + // Create Debug Information Recorder to record scopes, oopmaps, etc. + env()->set_oop_recorder(new OopRecorder(comp_arena())); + env()->set_debug_info(new DebugInformationRecorder(env()->oop_recorder())); + env()->set_dependencies(new Dependencies(env())); + + _fixed_slots = 0; + set_has_split_ifs(false); + set_has_loops(has_method() && method()->has_loops()); // first approximation + _deopt_happens = true; // start out assuming the worst + _trap_can_recompile = false; // no traps emitted yet + _major_progress = true; // start out assuming good things will happen + set_has_unsafe_access(false); + Copy::zero_to_bytes(_trap_hist, sizeof(_trap_hist)); + set_decompile_count(0); + + // Compilation level related initialization + if (env()->comp_level() == CompLevel_fast_compile) { + set_num_loop_opts(Tier1LoopOptsCount); + set_do_inlining(Tier1Inline != 0); + set_max_inline_size(Tier1MaxInlineSize); + set_freq_inline_size(Tier1FreqInlineSize); + set_do_scheduling(false); + set_do_count_invocations(Tier1CountInvocations); + set_do_method_data_update(Tier1UpdateMethodData); + } else { + assert(env()->comp_level() == CompLevel_full_optimization, "unknown comp level"); + set_num_loop_opts(LoopOptsCount); + set_do_inlining(Inline); + set_max_inline_size(MaxInlineSize); + set_freq_inline_size(FreqInlineSize); + set_do_scheduling(OptoScheduling); + set_do_count_invocations(false); + set_do_method_data_update(false); + } + + if (debug_info()->recording_non_safepoints()) { + set_node_note_array(new(comp_arena()) GrowableArray + (comp_arena(), 8, 0, NULL)); + set_default_node_notes(Node_Notes::make(this)); + } + + // // -- Initialize types before each compile -- + // // Update cached type information + // if( _method && _method->constants() ) + // Type::update_loaded_types(_method, _method->constants()); + + // Init alias_type map. + if (!DoEscapeAnalysis && aliaslevel == 3) + aliaslevel = 2; // No unique types without escape analysis + _AliasLevel = aliaslevel; + const int grow_ats = 16; + _max_alias_types = grow_ats; + _alias_types = NEW_ARENA_ARRAY(comp_arena(), AliasType*, grow_ats); + AliasType* ats = NEW_ARENA_ARRAY(comp_arena(), AliasType, grow_ats); + Copy::zero_to_bytes(ats, sizeof(AliasType)*grow_ats); + { + for (int i = 0; i < grow_ats; i++) _alias_types[i] = &ats[i]; + } + // Initialize the first few types. + _alias_types[AliasIdxTop]->Init(AliasIdxTop, NULL); + _alias_types[AliasIdxBot]->Init(AliasIdxBot, TypePtr::BOTTOM); + _alias_types[AliasIdxRaw]->Init(AliasIdxRaw, TypeRawPtr::BOTTOM); + _num_alias_types = AliasIdxRaw+1; + // Zero out the alias type cache. + Copy::zero_to_bytes(_alias_cache, sizeof(_alias_cache)); + // A NULL adr_type hits in the cache right away. Preload the right answer. + probe_alias_cache(NULL)->_index = AliasIdxTop; + + _intrinsics = NULL; + _macro_nodes = new GrowableArray(comp_arena(), 8, 0, NULL); + register_library_intrinsics(); +} + +//---------------------------init_start---------------------------------------- +// Install the StartNode on this compile object. +void Compile::init_start(StartNode* s) { + if (failing()) + return; // already failing + assert(s == start(), ""); +} + +StartNode* Compile::start() const { + assert(!failing(), ""); + for (DUIterator_Fast imax, i = root()->fast_outs(imax); i < imax; i++) { + Node* start = root()->fast_out(i); + if( start->is_Start() ) + return start->as_Start(); + } + ShouldNotReachHere(); + return NULL; +} + +//-------------------------------immutable_memory------------------------------------- +// Access immutable memory +Node* Compile::immutable_memory() { + if (_immutable_memory != NULL) { + return _immutable_memory; + } + StartNode* s = start(); + for (DUIterator_Fast imax, i = s->fast_outs(imax); true; i++) { + Node *p = s->fast_out(i); + if (p != s && p->as_Proj()->_con == TypeFunc::Memory) { + _immutable_memory = p; + return _immutable_memory; + } + } + ShouldNotReachHere(); + return NULL; +} + +//----------------------set_cached_top_node------------------------------------ +// Install the cached top node, and make sure Node::is_top works correctly. +void Compile::set_cached_top_node(Node* tn) { + if (tn != NULL) verify_top(tn); + Node* old_top = _top; + _top = tn; + // Calling Node::setup_is_top allows the nodes the chance to adjust + // their _out arrays. + if (_top != NULL) _top->setup_is_top(); + if (old_top != NULL) old_top->setup_is_top(); + assert(_top == NULL || top()->is_top(), ""); +} + +#ifndef PRODUCT +void Compile::verify_top(Node* tn) const { + if (tn != NULL) { + assert(tn->is_Con(), "top node must be a constant"); + assert(((ConNode*)tn)->type() == Type::TOP, "top node must have correct type"); + assert(tn->in(0) != NULL, "must have live top node"); + } +} +#endif + + +///-------------------Managing Per-Node Debug & Profile Info------------------- + +void Compile::grow_node_notes(GrowableArray* arr, int grow_by) { + guarantee(arr != NULL, ""); + int num_blocks = arr->length(); + if (grow_by < num_blocks) grow_by = num_blocks; + int num_notes = grow_by * _node_notes_block_size; + Node_Notes* notes = NEW_ARENA_ARRAY(node_arena(), Node_Notes, num_notes); + Copy::zero_to_bytes(notes, num_notes * sizeof(Node_Notes)); + while (num_notes > 0) { + arr->append(notes); + notes += _node_notes_block_size; + num_notes -= _node_notes_block_size; + } + assert(num_notes == 0, "exact multiple, please"); +} + +bool Compile::copy_node_notes_to(Node* dest, Node* source) { + if (source == NULL || dest == NULL) return false; + + if (dest->is_Con()) + return false; // Do not push debug info onto constants. + +#ifdef ASSERT + // Leave a bread crumb trail pointing to the original node: + if (dest != NULL && dest != source && dest->debug_orig() == NULL) { + dest->set_debug_orig(source); + } +#endif + + if (node_note_array() == NULL) + return false; // Not collecting any notes now. + + // This is a copy onto a pre-existing node, which may already have notes. + // If both nodes have notes, do not overwrite any pre-existing notes. + Node_Notes* source_notes = node_notes_at(source->_idx); + if (source_notes == NULL || source_notes->is_clear()) return false; + Node_Notes* dest_notes = node_notes_at(dest->_idx); + if (dest_notes == NULL || dest_notes->is_clear()) { + return set_node_notes_at(dest->_idx, source_notes); + } + + Node_Notes merged_notes = (*source_notes); + // The order of operations here ensures that dest notes will win... + merged_notes.update_from(dest_notes); + return set_node_notes_at(dest->_idx, &merged_notes); +} + + +//--------------------------allow_range_check_smearing------------------------- +// Gating condition for coalescing similar range checks. +// Sometimes we try 'speculatively' replacing a series of a range checks by a +// single covering check that is at least as strong as any of them. +// If the optimization succeeds, the simplified (strengthened) range check +// will always succeed. If it fails, we will deopt, and then give up +// on the optimization. +bool Compile::allow_range_check_smearing() const { + // If this method has already thrown a range-check, + // assume it was because we already tried range smearing + // and it failed. + uint already_trapped = trap_count(Deoptimization::Reason_range_check); + return !already_trapped; +} + + +//------------------------------flatten_alias_type----------------------------- +const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const { + int offset = tj->offset(); + TypePtr::PTR ptr = tj->ptr(); + + // Process weird unsafe references. + if (offset == Type::OffsetBot && (tj->isa_instptr() /*|| tj->isa_klassptr()*/)) { + assert(InlineUnsafeOps, "indeterminate pointers come only from unsafe ops"); + tj = TypeOopPtr::BOTTOM; + ptr = tj->ptr(); + offset = tj->offset(); + } + + // Array pointers need some flattening + const TypeAryPtr *ta = tj->isa_aryptr(); + if( ta && _AliasLevel >= 2 ) { + // For arrays indexed by constant indices, we flatten the alias + // space to include all of the array body. Only the header, klass + // and array length can be accessed un-aliased. + if( offset != Type::OffsetBot ) { + if( ta->const_oop() ) { // methodDataOop or methodOop + offset = Type::OffsetBot; // Flatten constant access into array body + tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),ta->ary(),ta->klass(),false,Type::OffsetBot, ta->instance_id()); + } else if( offset == arrayOopDesc::length_offset_in_bytes() ) { + // range is OK as-is. + tj = ta = TypeAryPtr::RANGE; + } else if( offset == oopDesc::klass_offset_in_bytes() ) { + tj = TypeInstPtr::KLASS; // all klass loads look alike + ta = TypeAryPtr::RANGE; // generic ignored junk + ptr = TypePtr::BotPTR; + } else if( offset == oopDesc::mark_offset_in_bytes() ) { + tj = TypeInstPtr::MARK; + ta = TypeAryPtr::RANGE; // generic ignored junk + ptr = TypePtr::BotPTR; + } else { // Random constant offset into array body + offset = Type::OffsetBot; // Flatten constant access into array body + tj = ta = TypeAryPtr::make(ptr,ta->ary(),ta->klass(),false,Type::OffsetBot, ta->instance_id()); + } + } + // Arrays of fixed size alias with arrays of unknown size. + if (ta->size() != TypeInt::POS) { + const TypeAry *tary = TypeAry::make(ta->elem(), TypeInt::POS); + tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,ta->klass(),false,offset, ta->instance_id()); + } + // Arrays of known objects become arrays of unknown objects. + if (ta->elem()->isa_oopptr() && ta->elem() != TypeInstPtr::BOTTOM) { + const TypeAry *tary = TypeAry::make(TypeInstPtr::BOTTOM, ta->size()); + tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,NULL,false,offset, ta->instance_id()); + } + // Arrays of bytes and of booleans both use 'bastore' and 'baload' so + // cannot be distinguished by bytecode alone. + if (ta->elem() == TypeInt::BOOL) { + const TypeAry *tary = TypeAry::make(TypeInt::BYTE, ta->size()); + ciKlass* aklass = ciTypeArrayKlass::make(T_BYTE); + tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,aklass,false,offset, ta->instance_id()); + } + // During the 2nd round of IterGVN, NotNull castings are removed. + // Make sure the Bottom and NotNull variants alias the same. + // Also, make sure exact and non-exact variants alias the same. + if( ptr == TypePtr::NotNull || ta->klass_is_exact() ) { + if (ta->const_oop()) { + tj = ta = TypeAryPtr::make(TypePtr::Constant,ta->const_oop(),ta->ary(),ta->klass(),false,offset); + } else { + tj = ta = TypeAryPtr::make(TypePtr::BotPTR,ta->ary(),ta->klass(),false,offset); + } + } + } + + // Oop pointers need some flattening + const TypeInstPtr *to = tj->isa_instptr(); + if( to && _AliasLevel >= 2 && to != TypeOopPtr::BOTTOM ) { + if( ptr == TypePtr::Constant ) { + // No constant oop pointers (such as Strings); they alias with + // unknown strings. + tj = to = TypeInstPtr::make(TypePtr::BotPTR,to->klass(),false,0,offset); + } else if( ptr == TypePtr::NotNull || to->klass_is_exact() ) { + // During the 2nd round of IterGVN, NotNull castings are removed. + // Make sure the Bottom and NotNull variants alias the same. + // Also, make sure exact and non-exact variants alias the same. + tj = to = TypeInstPtr::make(TypePtr::BotPTR,to->klass(),false,0,offset, to->instance_id()); + } + // Canonicalize the holder of this field + ciInstanceKlass *k = to->klass()->as_instance_klass(); + if (offset >= 0 && offset < oopDesc::header_size() * wordSize) { + // First handle header references such as a LoadKlassNode, even if the + // object's klass is unloaded at compile time (4965979). + tj = to = TypeInstPtr::make(TypePtr::BotPTR, env()->Object_klass(), false, NULL, offset, to->instance_id()); + } else if (offset < 0 || offset >= k->size_helper() * wordSize) { + to = NULL; + tj = TypeOopPtr::BOTTOM; + offset = tj->offset(); + } else { + ciInstanceKlass *canonical_holder = k->get_canonical_holder(offset); + if (!k->equals(canonical_holder) || tj->offset() != offset) { + tj = to = TypeInstPtr::make(TypePtr::BotPTR, canonical_holder, false, NULL, offset, to->instance_id()); + } + } + } + + // Klass pointers to object array klasses need some flattening + const TypeKlassPtr *tk = tj->isa_klassptr(); + if( tk ) { + // If we are referencing a field within a Klass, we need + // to assume the worst case of an Object. Both exact and + // inexact types must flatten to the same alias class. + // Since the flattened result for a klass is defined to be + // precisely java.lang.Object, use a constant ptr. + if ( offset == Type::OffsetBot || (offset >= 0 && (size_t)offset < sizeof(Klass)) ) { + + tj = tk = TypeKlassPtr::make(TypePtr::Constant, + TypeKlassPtr::OBJECT->klass(), + offset); + } + + ciKlass* klass = tk->klass(); + if( klass->is_obj_array_klass() ) { + ciKlass* k = TypeAryPtr::OOPS->klass(); + if( !k || !k->is_loaded() ) // Only fails for some -Xcomp runs + k = TypeInstPtr::BOTTOM->klass(); + tj = tk = TypeKlassPtr::make( TypePtr::NotNull, k, offset ); + } + + // Check for precise loads from the primary supertype array and force them + // to the supertype cache alias index. Check for generic array loads from + // the primary supertype array and also force them to the supertype cache + // alias index. Since the same load can reach both, we need to merge + // these 2 disparate memories into the same alias class. Since the + // primary supertype array is read-only, there's no chance of confusion + // where we bypass an array load and an array store. + uint off2 = offset - Klass::primary_supers_offset_in_bytes(); + if( offset == Type::OffsetBot || + off2 < Klass::primary_super_limit()*wordSize ) { + offset = sizeof(oopDesc) +Klass::secondary_super_cache_offset_in_bytes(); + tj = tk = TypeKlassPtr::make( TypePtr::NotNull, tk->klass(), offset ); + } + } + + // Flatten all Raw pointers together. + if (tj->base() == Type::RawPtr) + tj = TypeRawPtr::BOTTOM; + + if (tj->base() == Type::AnyPtr) + tj = TypePtr::BOTTOM; // An error, which the caller must check for. + + // Flatten all to bottom for now + switch( _AliasLevel ) { + case 0: + tj = TypePtr::BOTTOM; + break; + case 1: // Flatten to: oop, static, field or array + switch (tj->base()) { + //case Type::AryPtr: tj = TypeAryPtr::RANGE; break; + case Type::RawPtr: tj = TypeRawPtr::BOTTOM; break; + case Type::AryPtr: // do not distinguish arrays at all + case Type::InstPtr: tj = TypeInstPtr::BOTTOM; break; + case Type::KlassPtr: tj = TypeKlassPtr::OBJECT; break; + case Type::AnyPtr: tj = TypePtr::BOTTOM; break; // caller checks it + default: ShouldNotReachHere(); + } + break; + case 2: // No collasping at level 2; keep all splits + case 3: // No collasping at level 3; keep all splits + break; + default: + Unimplemented(); + } + + offset = tj->offset(); + assert( offset != Type::OffsetTop, "Offset has fallen from constant" ); + + assert( (offset != Type::OffsetBot && tj->base() != Type::AryPtr) || + (offset == Type::OffsetBot && tj->base() == Type::AryPtr) || + (offset == Type::OffsetBot && tj == TypeOopPtr::BOTTOM) || + (offset == Type::OffsetBot && tj == TypePtr::BOTTOM) || + (offset == oopDesc::mark_offset_in_bytes() && tj->base() == Type::AryPtr) || + (offset == oopDesc::klass_offset_in_bytes() && tj->base() == Type::AryPtr) || + (offset == arrayOopDesc::length_offset_in_bytes() && tj->base() == Type::AryPtr) , + "For oops, klasses, raw offset must be constant; for arrays the offset is never known" ); + assert( tj->ptr() != TypePtr::TopPTR && + tj->ptr() != TypePtr::AnyNull && + tj->ptr() != TypePtr::Null, "No imprecise addresses" ); +// assert( tj->ptr() != TypePtr::Constant || +// tj->base() == Type::RawPtr || +// tj->base() == Type::KlassPtr, "No constant oop addresses" ); + + return tj; +} + +void Compile::AliasType::Init(int i, const TypePtr* at) { + _index = i; + _adr_type = at; + _field = NULL; + _is_rewritable = true; // default + const TypeOopPtr *atoop = (at != NULL) ? at->isa_oopptr() : NULL; + if (atoop != NULL && atoop->is_instance()) { + const TypeOopPtr *gt = atoop->cast_to_instance(TypeOopPtr::UNKNOWN_INSTANCE); + _general_index = Compile::current()->get_alias_index(gt); + } else { + _general_index = 0; + } +} + +//---------------------------------print_on------------------------------------ +#ifndef PRODUCT +void Compile::AliasType::print_on(outputStream* st) { + if (index() < 10) + st->print("@ <%d> ", index()); + else st->print("@ <%d>", index()); + st->print(is_rewritable() ? " " : " RO"); + int offset = adr_type()->offset(); + if (offset == Type::OffsetBot) + st->print(" +any"); + else st->print(" +%-3d", offset); + st->print(" in "); + adr_type()->dump_on(st); + const TypeOopPtr* tjp = adr_type()->isa_oopptr(); + if (field() != NULL && tjp) { + if (tjp->klass() != field()->holder() || + tjp->offset() != field()->offset_in_bytes()) { + st->print(" != "); + field()->print(); + st->print(" ***"); + } + } +} + +void print_alias_types() { + Compile* C = Compile::current(); + tty->print_cr("--- Alias types, AliasIdxBot .. %d", C->num_alias_types()-1); + for (int idx = Compile::AliasIdxBot; idx < C->num_alias_types(); idx++) { + C->alias_type(idx)->print_on(tty); + tty->cr(); + } +} +#endif + + +//----------------------------probe_alias_cache-------------------------------- +Compile::AliasCacheEntry* Compile::probe_alias_cache(const TypePtr* adr_type) { + intptr_t key = (intptr_t) adr_type; + key ^= key >> logAliasCacheSize; + return &_alias_cache[key & right_n_bits(logAliasCacheSize)]; +} + + +//-----------------------------grow_alias_types-------------------------------- +void Compile::grow_alias_types() { + const int old_ats = _max_alias_types; // how many before? + const int new_ats = old_ats; // how many more? + const int grow_ats = old_ats+new_ats; // how many now? + _max_alias_types = grow_ats; + _alias_types = REALLOC_ARENA_ARRAY(comp_arena(), AliasType*, _alias_types, old_ats, grow_ats); + AliasType* ats = NEW_ARENA_ARRAY(comp_arena(), AliasType, new_ats); + Copy::zero_to_bytes(ats, sizeof(AliasType)*new_ats); + for (int i = 0; i < new_ats; i++) _alias_types[old_ats+i] = &ats[i]; +} + + +//--------------------------------find_alias_type------------------------------ +Compile::AliasType* Compile::find_alias_type(const TypePtr* adr_type, bool no_create) { + if (_AliasLevel == 0) + return alias_type(AliasIdxBot); + + AliasCacheEntry* ace = probe_alias_cache(adr_type); + if (ace->_adr_type == adr_type) { + return alias_type(ace->_index); + } + + // Handle special cases. + if (adr_type == NULL) return alias_type(AliasIdxTop); + if (adr_type == TypePtr::BOTTOM) return alias_type(AliasIdxBot); + + // Do it the slow way. + const TypePtr* flat = flatten_alias_type(adr_type); + +#ifdef ASSERT + assert(flat == flatten_alias_type(flat), "idempotent"); + assert(flat != TypePtr::BOTTOM, "cannot alias-analyze an untyped ptr"); + if (flat->isa_oopptr() && !flat->isa_klassptr()) { + const TypeOopPtr* foop = flat->is_oopptr(); + const TypePtr* xoop = foop->cast_to_exactness(!foop->klass_is_exact())->is_ptr(); + assert(foop == flatten_alias_type(xoop), "exactness must not affect alias type"); + } + assert(flat == flatten_alias_type(flat), "exact bit doesn't matter"); +#endif + + int idx = AliasIdxTop; + for (int i = 0; i < num_alias_types(); i++) { + if (alias_type(i)->adr_type() == flat) { + idx = i; + break; + } + } + + if (idx == AliasIdxTop) { + if (no_create) return NULL; + // Grow the array if necessary. + if (_num_alias_types == _max_alias_types) grow_alias_types(); + // Add a new alias type. + idx = _num_alias_types++; + _alias_types[idx]->Init(idx, flat); + if (flat == TypeInstPtr::KLASS) alias_type(idx)->set_rewritable(false); + if (flat == TypeAryPtr::RANGE) alias_type(idx)->set_rewritable(false); + if (flat->isa_instptr()) { + if (flat->offset() == java_lang_Class::klass_offset_in_bytes() + && flat->is_instptr()->klass() == env()->Class_klass()) + alias_type(idx)->set_rewritable(false); + } + if (flat->isa_klassptr()) { + if (flat->offset() == Klass::super_check_offset_offset_in_bytes() + (int)sizeof(oopDesc)) + alias_type(idx)->set_rewritable(false); + if (flat->offset() == Klass::modifier_flags_offset_in_bytes() + (int)sizeof(oopDesc)) + alias_type(idx)->set_rewritable(false); + if (flat->offset() == Klass::access_flags_offset_in_bytes() + (int)sizeof(oopDesc)) + alias_type(idx)->set_rewritable(false); + if (flat->offset() == Klass::java_mirror_offset_in_bytes() + (int)sizeof(oopDesc)) + alias_type(idx)->set_rewritable(false); + } + // %%% (We would like to finalize JavaThread::threadObj_offset(), + // but the base pointer type is not distinctive enough to identify + // references into JavaThread.) + + // Check for final instance fields. + const TypeInstPtr* tinst = flat->isa_instptr(); + if (tinst && tinst->offset() >= oopDesc::header_size() * wordSize) { + ciInstanceKlass *k = tinst->klass()->as_instance_klass(); + ciField* field = k->get_field_by_offset(tinst->offset(), false); + // Set field() and is_rewritable() attributes. + if (field != NULL) alias_type(idx)->set_field(field); + } + const TypeKlassPtr* tklass = flat->isa_klassptr(); + // Check for final static fields. + if (tklass && tklass->klass()->is_instance_klass()) { + ciInstanceKlass *k = tklass->klass()->as_instance_klass(); + ciField* field = k->get_field_by_offset(tklass->offset(), true); + // Set field() and is_rewritable() attributes. + if (field != NULL) alias_type(idx)->set_field(field); + } + } + + // Fill the cache for next time. + ace->_adr_type = adr_type; + ace->_index = idx; + assert(alias_type(adr_type) == alias_type(idx), "type must be installed"); + + // Might as well try to fill the cache for the flattened version, too. + AliasCacheEntry* face = probe_alias_cache(flat); + if (face->_adr_type == NULL) { + face->_adr_type = flat; + face->_index = idx; + assert(alias_type(flat) == alias_type(idx), "flat type must work too"); + } + + return alias_type(idx); +} + + +Compile::AliasType* Compile::alias_type(ciField* field) { + const TypeOopPtr* t; + if (field->is_static()) + t = TypeKlassPtr::make(field->holder()); + else + t = TypeOopPtr::make_from_klass_raw(field->holder()); + AliasType* atp = alias_type(t->add_offset(field->offset_in_bytes())); + assert(field->is_final() == !atp->is_rewritable(), "must get the rewritable bits correct"); + return atp; +} + + +//------------------------------have_alias_type-------------------------------- +bool Compile::have_alias_type(const TypePtr* adr_type) { + AliasCacheEntry* ace = probe_alias_cache(adr_type); + if (ace->_adr_type == adr_type) { + return true; + } + + // Handle special cases. + if (adr_type == NULL) return true; + if (adr_type == TypePtr::BOTTOM) return true; + + return find_alias_type(adr_type, true) != NULL; +} + +//-----------------------------must_alias-------------------------------------- +// True if all values of the given address type are in the given alias category. +bool Compile::must_alias(const TypePtr* adr_type, int alias_idx) { + if (alias_idx == AliasIdxBot) return true; // the universal category + if (adr_type == NULL) return true; // NULL serves as TypePtr::TOP + if (alias_idx == AliasIdxTop) return false; // the empty category + if (adr_type->base() == Type::AnyPtr) return false; // TypePtr::BOTTOM or its twins + + // the only remaining possible overlap is identity + int adr_idx = get_alias_index(adr_type); + assert(adr_idx != AliasIdxBot && adr_idx != AliasIdxTop, ""); + assert(adr_idx == alias_idx || + (alias_type(alias_idx)->adr_type() != TypeOopPtr::BOTTOM + && adr_type != TypeOopPtr::BOTTOM), + "should not be testing for overlap with an unsafe pointer"); + return adr_idx == alias_idx; +} + +//------------------------------can_alias-------------------------------------- +// True if any values of the given address type are in the given alias category. +bool Compile::can_alias(const TypePtr* adr_type, int alias_idx) { + if (alias_idx == AliasIdxTop) return false; // the empty category + if (adr_type == NULL) return false; // NULL serves as TypePtr::TOP + if (alias_idx == AliasIdxBot) return true; // the universal category + if (adr_type->base() == Type::AnyPtr) return true; // TypePtr::BOTTOM or its twins + + // the only remaining possible overlap is identity + int adr_idx = get_alias_index(adr_type); + assert(adr_idx != AliasIdxBot && adr_idx != AliasIdxTop, ""); + return adr_idx == alias_idx; +} + + + +//---------------------------pop_warm_call------------------------------------- +WarmCallInfo* Compile::pop_warm_call() { + WarmCallInfo* wci = _warm_calls; + if (wci != NULL) _warm_calls = wci->remove_from(wci); + return wci; +} + +//----------------------------Inline_Warm-------------------------------------- +int Compile::Inline_Warm() { + // If there is room, try to inline some more warm call sites. + // %%% Do a graph index compaction pass when we think we're out of space? + if (!InlineWarmCalls) return 0; + + int calls_made_hot = 0; + int room_to_grow = NodeCountInliningCutoff - unique(); + int amount_to_grow = MIN2(room_to_grow, (int)NodeCountInliningStep); + int amount_grown = 0; + WarmCallInfo* call; + while (amount_to_grow > 0 && (call = pop_warm_call()) != NULL) { + int est_size = (int)call->size(); + if (est_size > (room_to_grow - amount_grown)) { + // This one won't fit anyway. Get rid of it. + call->make_cold(); + continue; + } + call->make_hot(); + calls_made_hot++; + amount_grown += est_size; + amount_to_grow -= est_size; + } + + if (calls_made_hot > 0) set_major_progress(); + return calls_made_hot; +} + + +//----------------------------Finish_Warm-------------------------------------- +void Compile::Finish_Warm() { + if (!InlineWarmCalls) return; + if (failing()) return; + if (warm_calls() == NULL) return; + + // Clean up loose ends, if we are out of space for inlining. + WarmCallInfo* call; + while ((call = pop_warm_call()) != NULL) { + call->make_cold(); + } +} + + +//------------------------------Optimize--------------------------------------- +// Given a graph, optimize it. +void Compile::Optimize() { + TracePhase t1("optimizer", &_t_optimizer, true); + +#ifndef PRODUCT + if (env()->break_at_compile()) { + BREAKPOINT; + } + +#endif + + ResourceMark rm; + int loop_opts_cnt; + + NOT_PRODUCT( verify_graph_edges(); ) + + print_method("Start"); + + { + // Iterative Global Value Numbering, including ideal transforms + // Initialize IterGVN with types and values from parse-time GVN + PhaseIterGVN igvn(initial_gvn()); + { + NOT_PRODUCT( TracePhase t2("iterGVN", &_t_iterGVN, TimeCompiler); ) + igvn.optimize(); + } + + print_method("Iter GVN 1", 2); + + if (failing()) return; + + // get rid of the connection graph since it's information is not + // updated by optimizations + _congraph = NULL; + + + // Loop transforms on the ideal graph. Range Check Elimination, + // peeling, unrolling, etc. + + // Set loop opts counter + loop_opts_cnt = num_loop_opts(); + if((loop_opts_cnt > 0) && (has_loops() || has_split_ifs())) { + { + TracePhase t2("idealLoop", &_t_idealLoop, true); + PhaseIdealLoop ideal_loop( igvn, NULL, true ); + loop_opts_cnt--; + if (major_progress()) print_method("PhaseIdealLoop 1", 2); + if (failing()) return; + } + // Loop opts pass if partial peeling occurred in previous pass + if(PartialPeelLoop && major_progress() && (loop_opts_cnt > 0)) { + TracePhase t3("idealLoop", &_t_idealLoop, true); + PhaseIdealLoop ideal_loop( igvn, NULL, false ); + loop_opts_cnt--; + if (major_progress()) print_method("PhaseIdealLoop 2", 2); + if (failing()) return; + } + // Loop opts pass for loop-unrolling before CCP + if(major_progress() && (loop_opts_cnt > 0)) { + TracePhase t4("idealLoop", &_t_idealLoop, true); + PhaseIdealLoop ideal_loop( igvn, NULL, false ); + loop_opts_cnt--; + if (major_progress()) print_method("PhaseIdealLoop 3", 2); + } + } + if (failing()) return; + + // Conditional Constant Propagation; + PhaseCCP ccp( &igvn ); + assert( true, "Break here to ccp.dump_nodes_and_types(_root,999,1)"); + { + TracePhase t2("ccp", &_t_ccp, true); + ccp.do_transform(); + } + print_method("PhaseCPP 1", 2); + + assert( true, "Break here to ccp.dump_old2new_map()"); + + // Iterative Global Value Numbering, including ideal transforms + { + NOT_PRODUCT( TracePhase t2("iterGVN2", &_t_iterGVN2, TimeCompiler); ) + igvn = ccp; + igvn.optimize(); + } + + print_method("Iter GVN 2", 2); + + if (failing()) return; + + // Loop transforms on the ideal graph. Range Check Elimination, + // peeling, unrolling, etc. + if(loop_opts_cnt > 0) { + debug_only( int cnt = 0; ); + while(major_progress() && (loop_opts_cnt > 0)) { + TracePhase t2("idealLoop", &_t_idealLoop, true); + assert( cnt++ < 40, "infinite cycle in loop optimization" ); + PhaseIdealLoop ideal_loop( igvn, NULL, true ); + loop_opts_cnt--; + if (major_progress()) print_method("PhaseIdealLoop iterations", 2); + if (failing()) return; + } + } + { + NOT_PRODUCT( TracePhase t2("macroExpand", &_t_macroExpand, TimeCompiler); ) + PhaseMacroExpand mex(igvn); + if (mex.expand_macro_nodes()) { + assert(failing(), "must bail out w/ explicit message"); + return; + } + } + + } // (End scope of igvn; run destructor if necessary for asserts.) + + // A method with only infinite loops has no edges entering loops from root + { + NOT_PRODUCT( TracePhase t2("graphReshape", &_t_graphReshaping, TimeCompiler); ) + if (final_graph_reshaping()) { + assert(failing(), "must bail out w/ explicit message"); + return; + } + } + + print_method("Optimize finished", 2); +} + + +//------------------------------Code_Gen--------------------------------------- +// Given a graph, generate code for it +void Compile::Code_Gen() { + if (failing()) return; + + // Perform instruction selection. You might think we could reclaim Matcher + // memory PDQ, but actually the Matcher is used in generating spill code. + // Internals of the Matcher (including some VectorSets) must remain live + // for awhile - thus I cannot reclaim Matcher memory lest a VectorSet usage + // set a bit in reclaimed memory. + + // In debug mode can dump m._nodes.dump() for mapping of ideal to machine + // nodes. Mapping is only valid at the root of each matched subtree. + NOT_PRODUCT( verify_graph_edges(); ) + + Node_List proj_list; + Matcher m(proj_list); + _matcher = &m; + { + TracePhase t2("matcher", &_t_matcher, true); + m.match(); + } + // In debug mode can dump m._nodes.dump() for mapping of ideal to machine + // nodes. Mapping is only valid at the root of each matched subtree. + NOT_PRODUCT( verify_graph_edges(); ) + + // If you have too many nodes, or if matching has failed, bail out + check_node_count(0, "out of nodes matching instructions"); + if (failing()) return; + + // Build a proper-looking CFG + PhaseCFG cfg(node_arena(), root(), m); + _cfg = &cfg; + { + NOT_PRODUCT( TracePhase t2("scheduler", &_t_scheduler, TimeCompiler); ) + cfg.Dominators(); + if (failing()) return; + + NOT_PRODUCT( verify_graph_edges(); ) + + cfg.Estimate_Block_Frequency(); + cfg.GlobalCodeMotion(m,unique(),proj_list); + + print_method("Global code motion", 2); + + if (failing()) return; + NOT_PRODUCT( verify_graph_edges(); ) + + debug_only( cfg.verify(); ) + } + NOT_PRODUCT( verify_graph_edges(); ) + + PhaseChaitin regalloc(unique(),cfg,m); + _regalloc = ®alloc; + { + TracePhase t2("regalloc", &_t_registerAllocation, true); + // Perform any platform dependent preallocation actions. This is used, + // for example, to avoid taking an implicit null pointer exception + // using the frame pointer on win95. + _regalloc->pd_preallocate_hook(); + + // Perform register allocation. After Chaitin, use-def chains are + // no longer accurate (at spill code) and so must be ignored. + // Node->LRG->reg mappings are still accurate. + _regalloc->Register_Allocate(); + + // Bail out if the allocator builds too many nodes + if (failing()) return; + } + + // Prior to register allocation we kept empty basic blocks in case the + // the allocator needed a place to spill. After register allocation we + // are not adding any new instructions. If any basic block is empty, we + // can now safely remove it. + { + NOT_PRODUCT( TracePhase t2("removeEmpty", &_t_removeEmptyBlocks, TimeCompiler); ) + cfg.RemoveEmpty(); + } + + // Perform any platform dependent postallocation verifications. + debug_only( _regalloc->pd_postallocate_verify_hook(); ) + + // Apply peephole optimizations + if( OptoPeephole ) { + NOT_PRODUCT( TracePhase t2("peephole", &_t_peephole, TimeCompiler); ) + PhasePeephole peep( _regalloc, cfg); + peep.do_transform(); + } + + // Convert Nodes to instruction bits in a buffer + { + // %%%% workspace merge brought two timers together for one job + TracePhase t2a("output", &_t_output, true); + NOT_PRODUCT( TraceTime t2b(NULL, &_t_codeGeneration, TimeCompiler, false); ) + Output(); + } + + print_method("End"); + + // He's dead, Jim. + _cfg = (PhaseCFG*)0xdeadbeef; + _regalloc = (PhaseChaitin*)0xdeadbeef; +} + + +//------------------------------dump_asm--------------------------------------- +// Dump formatted assembly +#ifndef PRODUCT +void Compile::dump_asm(int *pcs, uint pc_limit) { + bool cut_short = false; + tty->print_cr("#"); + tty->print("# "); _tf->dump(); tty->cr(); + tty->print_cr("#"); + + // For all blocks + int pc = 0x0; // Program counter + char starts_bundle = ' '; + _regalloc->dump_frame(); + + Node *n = NULL; + for( uint i=0; i<_cfg->_num_blocks; i++ ) { + if (VMThread::should_terminate()) { cut_short = true; break; } + Block *b = _cfg->_blocks[i]; + if (b->is_connector() && !Verbose) continue; + n = b->_nodes[0]; + if (pcs && n->_idx < pc_limit) + tty->print("%3.3x ", pcs[n->_idx]); + else + tty->print(" "); + b->dump_head( &_cfg->_bbs ); + if (b->is_connector()) { + tty->print_cr(" # Empty connector block"); + } else if (b->num_preds() == 2 && b->pred(1)->is_CatchProj() && b->pred(1)->as_CatchProj()->_con == CatchProjNode::fall_through_index) { + tty->print_cr(" # Block is sole successor of call"); + } + + // For all instructions + Node *delay = NULL; + for( uint j = 0; j_nodes.size(); j++ ) { + if (VMThread::should_terminate()) { cut_short = true; break; } + n = b->_nodes[j]; + if (valid_bundle_info(n)) { + Bundle *bundle = node_bundling(n); + if (bundle->used_in_unconditional_delay()) { + delay = n; + continue; + } + if (bundle->starts_bundle()) + starts_bundle = '+'; + } + + if( !n->is_Region() && // Dont print in the Assembly + !n->is_Phi() && // a few noisely useless nodes + !n->is_Proj() && + !n->is_MachTemp() && + !n->is_Catch() && // Would be nice to print exception table targets + !n->is_MergeMem() && // Not very interesting + !n->is_top() && // Debug info table constants + !(n->is_Con() && !n->is_Mach())// Debug info table constants + ) { + if (pcs && n->_idx < pc_limit) + tty->print("%3.3x", pcs[n->_idx]); + else + tty->print(" "); + tty->print(" %c ", starts_bundle); + starts_bundle = ' '; + tty->print("\t"); + n->format(_regalloc, tty); + tty->cr(); + } + + // If we have an instruction with a delay slot, and have seen a delay, + // then back up and print it + if (valid_bundle_info(n) && node_bundling(n)->use_unconditional_delay()) { + assert(delay != NULL, "no unconditional delay instruction"); + if (node_bundling(delay)->starts_bundle()) + starts_bundle = '+'; + if (pcs && n->_idx < pc_limit) + tty->print("%3.3x", pcs[n->_idx]); + else + tty->print(" "); + tty->print(" %c ", starts_bundle); + starts_bundle = ' '; + tty->print("\t"); + delay->format(_regalloc, tty); + tty->print_cr(""); + delay = NULL; + } + + // Dump the exception table as well + if( n->is_Catch() && (Verbose || WizardMode) ) { + // Print the exception table for this offset + _handler_table.print_subtable_for(pc); + } + } + + if (pcs && n->_idx < pc_limit) + tty->print_cr("%3.3x", pcs[n->_idx]); + else + tty->print_cr(""); + + assert(cut_short || delay == NULL, "no unconditional delay branch"); + + } // End of per-block dump + tty->print_cr(""); + + if (cut_short) tty->print_cr("*** disassembly is cut short ***"); +} +#endif + +//------------------------------Final_Reshape_Counts--------------------------- +// This class defines counters to help identify when a method +// may/must be executed using hardware with only 24-bit precision. +struct Final_Reshape_Counts : public StackObj { + int _call_count; // count non-inlined 'common' calls + int _float_count; // count float ops requiring 24-bit precision + int _double_count; // count double ops requiring more precision + int _java_call_count; // count non-inlined 'java' calls + VectorSet _visited; // Visitation flags + Node_List _tests; // Set of IfNodes & PCTableNodes + + Final_Reshape_Counts() : + _call_count(0), _float_count(0), _double_count(0), _java_call_count(0), + _visited( Thread::current()->resource_area() ) { } + + void inc_call_count () { _call_count ++; } + void inc_float_count () { _float_count ++; } + void inc_double_count() { _double_count++; } + void inc_java_call_count() { _java_call_count++; } + + int get_call_count () const { return _call_count ; } + int get_float_count () const { return _float_count ; } + int get_double_count() const { return _double_count; } + int get_java_call_count() const { return _java_call_count; } +}; + +static bool oop_offset_is_sane(const TypeInstPtr* tp) { + ciInstanceKlass *k = tp->klass()->as_instance_klass(); + // Make sure the offset goes inside the instance layout. + return (uint)tp->offset() < (uint)(oopDesc::header_size() + k->nonstatic_field_size())*wordSize; + // Note that OffsetBot and OffsetTop are very negative. +} + +//------------------------------final_graph_reshaping_impl---------------------- +// Implement items 1-5 from final_graph_reshaping below. +static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &fpu ) { + + uint nop = n->Opcode(); + + // Check for 2-input instruction with "last use" on right input. + // Swap to left input. Implements item (2). + if( n->req() == 3 && // two-input instruction + n->in(1)->outcnt() > 1 && // left use is NOT a last use + (!n->in(1)->is_Phi() || n->in(1)->in(2) != n) && // it is not data loop + n->in(2)->outcnt() == 1 &&// right use IS a last use + !n->in(2)->is_Con() ) { // right use is not a constant + // Check for commutative opcode + switch( nop ) { + case Op_AddI: case Op_AddF: case Op_AddD: case Op_AddL: + case Op_MaxI: case Op_MinI: + case Op_MulI: case Op_MulF: case Op_MulD: case Op_MulL: + case Op_AndL: case Op_XorL: case Op_OrL: + case Op_AndI: case Op_XorI: case Op_OrI: { + // Move "last use" input to left by swapping inputs + n->swap_edges(1, 2); + break; + } + default: + break; + } + } + + // Count FPU ops and common calls, implements item (3) + switch( nop ) { + // Count all float operations that may use FPU + case Op_AddF: + case Op_SubF: + case Op_MulF: + case Op_DivF: + case Op_NegF: + case Op_ModF: + case Op_ConvI2F: + case Op_ConF: + case Op_CmpF: + case Op_CmpF3: + // case Op_ConvL2F: // longs are split into 32-bit halves + fpu.inc_float_count(); + break; + + case Op_ConvF2D: + case Op_ConvD2F: + fpu.inc_float_count(); + fpu.inc_double_count(); + break; + + // Count all double operations that may use FPU + case Op_AddD: + case Op_SubD: + case Op_MulD: + case Op_DivD: + case Op_NegD: + case Op_ModD: + case Op_ConvI2D: + case Op_ConvD2I: + // case Op_ConvL2D: // handled by leaf call + // case Op_ConvD2L: // handled by leaf call + case Op_ConD: + case Op_CmpD: + case Op_CmpD3: + fpu.inc_double_count(); + break; + case Op_Opaque1: // Remove Opaque Nodes before matching + case Op_Opaque2: // Remove Opaque Nodes before matching + n->replace_by(n->in(1)); + break; + case Op_CallStaticJava: + case Op_CallJava: + case Op_CallDynamicJava: + fpu.inc_java_call_count(); // Count java call site; + case Op_CallRuntime: + case Op_CallLeaf: + case Op_CallLeafNoFP: { + assert( n->is_Call(), "" ); + CallNode *call = n->as_Call(); + // Count call sites where the FP mode bit would have to be flipped. + // Do not count uncommon runtime calls: + // uncommon_trap, _complete_monitor_locking, _complete_monitor_unlocking, + // _new_Java, _new_typeArray, _new_objArray, _rethrow_Java, ... + if( !call->is_CallStaticJava() || !call->as_CallStaticJava()->_name ) { + fpu.inc_call_count(); // Count the call site + } else { // See if uncommon argument is shared + Node *n = call->in(TypeFunc::Parms); + int nop = n->Opcode(); + // Clone shared simple arguments to uncommon calls, item (1). + if( n->outcnt() > 1 && + !n->is_Proj() && + nop != Op_CreateEx && + nop != Op_CheckCastPP && + !n->is_Mem() ) { + Node *x = n->clone(); + call->set_req( TypeFunc::Parms, x ); + } + } + break; + } + + case Op_StoreD: + case Op_LoadD: + case Op_LoadD_unaligned: + fpu.inc_double_count(); + goto handle_mem; + case Op_StoreF: + case Op_LoadF: + fpu.inc_float_count(); + goto handle_mem; + + case Op_StoreB: + case Op_StoreC: + case Op_StoreCM: + case Op_StorePConditional: + case Op_StoreI: + case Op_StoreL: + case Op_StoreLConditional: + case Op_CompareAndSwapI: + case Op_CompareAndSwapL: + case Op_CompareAndSwapP: + case Op_StoreP: + case Op_LoadB: + case Op_LoadC: + case Op_LoadI: + case Op_LoadKlass: + case Op_LoadL: + case Op_LoadL_unaligned: + case Op_LoadPLocked: + case Op_LoadLLocked: + case Op_LoadP: + case Op_LoadRange: + case Op_LoadS: { + handle_mem: +#ifdef ASSERT + if( VerifyOptoOopOffsets ) { + assert( n->is_Mem(), "" ); + MemNode *mem = (MemNode*)n; + // Check to see if address types have grounded out somehow. + const TypeInstPtr *tp = mem->in(MemNode::Address)->bottom_type()->isa_instptr(); + assert( !tp || oop_offset_is_sane(tp), "" ); + } +#endif + break; + } + case Op_If: + case Op_CountedLoopEnd: + fpu._tests.push(n); // Collect CFG split points + break; + + case Op_AddP: { // Assert sane base pointers + const Node *addp = n->in(AddPNode::Address); + assert( !addp->is_AddP() || + addp->in(AddPNode::Base)->is_top() || // Top OK for allocation + addp->in(AddPNode::Base) == n->in(AddPNode::Base), + "Base pointers must match" ); + break; + } + + case Op_ModI: + if (UseDivMod) { + // Check if a%b and a/b both exist + Node* d = n->find_similar(Op_DivI); + if (d) { + // Replace them with a fused divmod if supported + Compile* C = Compile::current(); + if (Matcher::has_match_rule(Op_DivModI)) { + DivModINode* divmod = DivModINode::make(C, n); + d->replace_by(divmod->div_proj()); + n->replace_by(divmod->mod_proj()); + } else { + // replace a%b with a-((a/b)*b) + Node* mult = new (C, 3) MulINode(d, d->in(2)); + Node* sub = new (C, 3) SubINode(d->in(1), mult); + n->replace_by( sub ); + } + } + } + break; + + case Op_ModL: + if (UseDivMod) { + // Check if a%b and a/b both exist + Node* d = n->find_similar(Op_DivL); + if (d) { + // Replace them with a fused divmod if supported + Compile* C = Compile::current(); + if (Matcher::has_match_rule(Op_DivModL)) { + DivModLNode* divmod = DivModLNode::make(C, n); + d->replace_by(divmod->div_proj()); + n->replace_by(divmod->mod_proj()); + } else { + // replace a%b with a-((a/b)*b) + Node* mult = new (C, 3) MulLNode(d, d->in(2)); + Node* sub = new (C, 3) SubLNode(d->in(1), mult); + n->replace_by( sub ); + } + } + } + break; + + case Op_Load16B: + case Op_Load8B: + case Op_Load4B: + case Op_Load8S: + case Op_Load4S: + case Op_Load2S: + case Op_Load8C: + case Op_Load4C: + case Op_Load2C: + case Op_Load4I: + case Op_Load2I: + case Op_Load2L: + case Op_Load4F: + case Op_Load2F: + case Op_Load2D: + case Op_Store16B: + case Op_Store8B: + case Op_Store4B: + case Op_Store8C: + case Op_Store4C: + case Op_Store2C: + case Op_Store4I: + case Op_Store2I: + case Op_Store2L: + case Op_Store4F: + case Op_Store2F: + case Op_Store2D: + break; + + case Op_PackB: + case Op_PackS: + case Op_PackC: + case Op_PackI: + case Op_PackF: + case Op_PackL: + case Op_PackD: + if (n->req()-1 > 2) { + // Replace many operand PackNodes with a binary tree for matching + PackNode* p = (PackNode*) n; + Node* btp = p->binaryTreePack(Compile::current(), 1, n->req()); + n->replace_by(btp); + } + break; + default: + assert( !n->is_Call(), "" ); + assert( !n->is_Mem(), "" ); + if( n->is_If() || n->is_PCTable() ) + fpu._tests.push(n); // Collect CFG split points + break; + } +} + +//------------------------------final_graph_reshaping_walk--------------------- +// Replacing Opaque nodes with their input in final_graph_reshaping_impl(), +// requires that the walk visits a node's inputs before visiting the node. +static void final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_Reshape_Counts &fpu ) { + fpu._visited.set(root->_idx); // first, mark node as visited + uint cnt = root->req(); + Node *n = root; + uint i = 0; + while (true) { + if (i < cnt) { + // Place all non-visited non-null inputs onto stack + Node* m = n->in(i); + ++i; + if (m != NULL && !fpu._visited.test_set(m->_idx)) { + cnt = m->req(); + nstack.push(n, i); // put on stack parent and next input's index + n = m; + i = 0; + } + } else { + // Now do post-visit work + final_graph_reshaping_impl( n, fpu ); + if (nstack.is_empty()) + break; // finished + n = nstack.node(); // Get node from stack + cnt = n->req(); + i = nstack.index(); + nstack.pop(); // Shift to the next node on stack + } + } +} + +//------------------------------final_graph_reshaping-------------------------- +// Final Graph Reshaping. +// +// (1) Clone simple inputs to uncommon calls, so they can be scheduled late +// and not commoned up and forced early. Must come after regular +// optimizations to avoid GVN undoing the cloning. Clone constant +// inputs to Loop Phis; these will be split by the allocator anyways. +// Remove Opaque nodes. +// (2) Move last-uses by commutative operations to the left input to encourage +// Intel update-in-place two-address operations and better register usage +// on RISCs. Must come after regular optimizations to avoid GVN Ideal +// calls canonicalizing them back. +// (3) Count the number of double-precision FP ops, single-precision FP ops +// and call sites. On Intel, we can get correct rounding either by +// forcing singles to memory (requires extra stores and loads after each +// FP bytecode) or we can set a rounding mode bit (requires setting and +// clearing the mode bit around call sites). The mode bit is only used +// if the relative frequency of single FP ops to calls is low enough. +// This is a key transform for SPEC mpeg_audio. +// (4) Detect infinite loops; blobs of code reachable from above but not +// below. Several of the Code_Gen algorithms fail on such code shapes, +// so we simply bail out. Happens a lot in ZKM.jar, but also happens +// from time to time in other codes (such as -Xcomp finalizer loops, etc). +// Detection is by looking for IfNodes where only 1 projection is +// reachable from below or CatchNodes missing some targets. +// (5) Assert for insane oop offsets in debug mode. + +bool Compile::final_graph_reshaping() { + // an infinite loop may have been eliminated by the optimizer, + // in which case the graph will be empty. + if (root()->req() == 1) { + record_method_not_compilable("trivial infinite loop"); + return true; + } + + Final_Reshape_Counts fpu; + + // Visit everybody reachable! + // Allocate stack of size C->unique()/2 to avoid frequent realloc + Node_Stack nstack(unique() >> 1); + final_graph_reshaping_walk(nstack, root(), fpu); + + // Check for unreachable (from below) code (i.e., infinite loops). + for( uint i = 0; i < fpu._tests.size(); i++ ) { + Node *n = fpu._tests[i]; + assert( n->is_PCTable() || n->is_If(), "either PCTables or IfNodes" ); + // Get number of CFG targets; 2 for IfNodes or _size for PCTables. + // Note that PCTables include exception targets after calls. + uint expected_kids = n->is_PCTable() ? n->as_PCTable()->_size : 2; + if (n->outcnt() != expected_kids) { + // Check for a few special cases. Rethrow Nodes never take the + // 'fall-thru' path, so expected kids is 1 less. + if (n->is_PCTable() && n->in(0) && n->in(0)->in(0)) { + if (n->in(0)->in(0)->is_Call()) { + CallNode *call = n->in(0)->in(0)->as_Call(); + if (call->entry_point() == OptoRuntime::rethrow_stub()) { + expected_kids--; // Rethrow always has 1 less kid + } else if (call->req() > TypeFunc::Parms && + call->is_CallDynamicJava()) { + // Check for null receiver. In such case, the optimizer has + // detected that the virtual call will always result in a null + // pointer exception. The fall-through projection of this CatchNode + // will not be populated. + Node *arg0 = call->in(TypeFunc::Parms); + if (arg0->is_Type() && + arg0->as_Type()->type()->higher_equal(TypePtr::NULL_PTR)) { + expected_kids--; + } + } else if (call->entry_point() == OptoRuntime::new_array_Java() && + call->req() > TypeFunc::Parms+1 && + call->is_CallStaticJava()) { + // Check for negative array length. In such case, the optimizer has + // detected that the allocation attempt will always result in an + // exception. There is no fall-through projection of this CatchNode . + Node *arg1 = call->in(TypeFunc::Parms+1); + if (arg1->is_Type() && + arg1->as_Type()->type()->join(TypeInt::POS)->empty()) { + expected_kids--; + } + } + } + } + // Recheck with a better notion of 'expected_kids' + if (n->outcnt() != expected_kids) { + record_method_not_compilable("malformed control flow"); + return true; // Not all targets reachable! + } + } + // Check that I actually visited all kids. Unreached kids + // must be infinite loops. + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) + if (!fpu._visited.test(n->fast_out(j)->_idx)) { + record_method_not_compilable("infinite loop"); + return true; // Found unvisited kid; must be unreach + } + } + + // If original bytecodes contained a mixture of floats and doubles + // check if the optimizer has made it homogenous, item (3). + if( Use24BitFPMode && Use24BitFP && + fpu.get_float_count() > 32 && + fpu.get_double_count() == 0 && + (10 * fpu.get_call_count() < fpu.get_float_count()) ) { + set_24_bit_selection_and_mode( false, true ); + } + + set_has_java_calls(fpu.get_java_call_count() > 0); + + // No infinite loops, no reason to bail out. + return false; +} + +//-----------------------------too_many_traps---------------------------------- +// Report if there are too many traps at the current method and bci. +// Return true if there was a trap, and/or PerMethodTrapLimit is exceeded. +bool Compile::too_many_traps(ciMethod* method, + int bci, + Deoptimization::DeoptReason reason) { + ciMethodData* md = method->method_data(); + if (md->is_empty()) { + // Assume the trap has not occurred, or that it occurred only + // because of a transient condition during start-up in the interpreter. + return false; + } + if (md->has_trap_at(bci, reason) != 0) { + // Assume PerBytecodeTrapLimit==0, for a more conservative heuristic. + // Also, if there are multiple reasons, or if there is no per-BCI record, + // assume the worst. + if (log()) + log()->elem("observe trap='%s' count='%d'", + Deoptimization::trap_reason_name(reason), + md->trap_count(reason)); + return true; + } else { + // Ignore method/bci and see if there have been too many globally. + return too_many_traps(reason, md); + } +} + +// Less-accurate variant which does not require a method and bci. +bool Compile::too_many_traps(Deoptimization::DeoptReason reason, + ciMethodData* logmd) { + if (trap_count(reason) >= (uint)PerMethodTrapLimit) { + // Too many traps globally. + // Note that we use cumulative trap_count, not just md->trap_count. + if (log()) { + int mcount = (logmd == NULL)? -1: (int)logmd->trap_count(reason); + log()->elem("observe trap='%s' count='0' mcount='%d' ccount='%d'", + Deoptimization::trap_reason_name(reason), + mcount, trap_count(reason)); + } + return true; + } else { + // The coast is clear. + return false; + } +} + +//--------------------------too_many_recompiles-------------------------------- +// Report if there are too many recompiles at the current method and bci. +// Consults PerBytecodeRecompilationCutoff and PerMethodRecompilationCutoff. +// Is not eager to return true, since this will cause the compiler to use +// Action_none for a trap point, to avoid too many recompilations. +bool Compile::too_many_recompiles(ciMethod* method, + int bci, + Deoptimization::DeoptReason reason) { + ciMethodData* md = method->method_data(); + if (md->is_empty()) { + // Assume the trap has not occurred, or that it occurred only + // because of a transient condition during start-up in the interpreter. + return false; + } + // Pick a cutoff point well within PerBytecodeRecompilationCutoff. + uint bc_cutoff = (uint) PerBytecodeRecompilationCutoff / 8; + uint m_cutoff = (uint) PerMethodRecompilationCutoff / 2 + 1; // not zero + Deoptimization::DeoptReason per_bc_reason + = Deoptimization::reason_recorded_per_bytecode_if_any(reason); + if ((per_bc_reason == Deoptimization::Reason_none + || md->has_trap_at(bci, reason) != 0) + // The trap frequency measure we care about is the recompile count: + && md->trap_recompiled_at(bci) + && md->overflow_recompile_count() >= bc_cutoff) { + // Do not emit a trap here if it has already caused recompilations. + // Also, if there are multiple reasons, or if there is no per-BCI record, + // assume the worst. + if (log()) + log()->elem("observe trap='%s recompiled' count='%d' recompiles2='%d'", + Deoptimization::trap_reason_name(reason), + md->trap_count(reason), + md->overflow_recompile_count()); + return true; + } else if (trap_count(reason) != 0 + && decompile_count() >= m_cutoff) { + // Too many recompiles globally, and we have seen this sort of trap. + // Use cumulative decompile_count, not just md->decompile_count. + if (log()) + log()->elem("observe trap='%s' count='%d' mcount='%d' decompiles='%d' mdecompiles='%d'", + Deoptimization::trap_reason_name(reason), + md->trap_count(reason), trap_count(reason), + md->decompile_count(), decompile_count()); + return true; + } else { + // The coast is clear. + return false; + } +} + + +#ifndef PRODUCT +//------------------------------verify_graph_edges--------------------------- +// Walk the Graph and verify that there is a one-to-one correspondence +// between Use-Def edges and Def-Use edges in the graph. +void Compile::verify_graph_edges(bool no_dead_code) { + if (VerifyGraphEdges) { + ResourceArea *area = Thread::current()->resource_area(); + Unique_Node_List visited(area); + // Call recursive graph walk to check edges + _root->verify_edges(visited); + if (no_dead_code) { + // Now make sure that no visited node is used by an unvisited node. + bool dead_nodes = 0; + Unique_Node_List checked(area); + while (visited.size() > 0) { + Node* n = visited.pop(); + checked.push(n); + for (uint i = 0; i < n->outcnt(); i++) { + Node* use = n->raw_out(i); + if (checked.member(use)) continue; // already checked + if (visited.member(use)) continue; // already in the graph + if (use->is_Con()) continue; // a dead ConNode is OK + // At this point, we have found a dead node which is DU-reachable. + if (dead_nodes++ == 0) + tty->print_cr("*** Dead nodes reachable via DU edges:"); + use->dump(2); + tty->print_cr("---"); + checked.push(use); // No repeats; pretend it is now checked. + } + } + assert(dead_nodes == 0, "using nodes must be reachable from root"); + } + } +} +#endif + +// The Compile object keeps track of failure reasons separately from the ciEnv. +// This is required because there is not quite a 1-1 relation between the +// ciEnv and its compilation task and the Compile object. Note that one +// ciEnv might use two Compile objects, if C2Compiler::compile_method decides +// to backtrack and retry without subsuming loads. Other than this backtracking +// behavior, the Compile's failure reason is quietly copied up to the ciEnv +// by the logic in C2Compiler. +void Compile::record_failure(const char* reason) { + if (log() != NULL) { + log()->elem("failure reason='%s' phase='compile'", reason); + } + if (_failure_reason == NULL) { + // Record the first failure reason. + _failure_reason = reason; + } + _root = NULL; // flush the graph, too +} + +Compile::TracePhase::TracePhase(const char* name, elapsedTimer* accumulator, bool dolog) + : TraceTime(NULL, accumulator, false NOT_PRODUCT( || TimeCompiler ), false) +{ + if (dolog) { + C = Compile::current(); + _log = C->log(); + } else { + C = NULL; + _log = NULL; + } + if (_log != NULL) { + _log->begin_head("phase name='%s' nodes='%d'", name, C->unique()); + _log->stamp(); + _log->end_head(); + } +} + +Compile::TracePhase::~TracePhase() { + if (_log != NULL) { + _log->done("phase nodes='%d'", C->unique()); + } +} diff --git a/hotspot/src/share/vm/opto/compile.hpp b/hotspot/src/share/vm/opto/compile.hpp new file mode 100644 index 00000000000..fcab3a37864 --- /dev/null +++ b/hotspot/src/share/vm/opto/compile.hpp @@ -0,0 +1,720 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Block; +class Bundle; +class C2Compiler; +class CallGenerator; +class ConnectionGraph; +class InlineTree; +class Int_Array; +class Matcher; +class MachNode; +class Node; +class Node_Array; +class Node_Notes; +class OptoReg; +class PhaseCFG; +class PhaseGVN; +class PhaseRegAlloc; +class PhaseCCP; +class PhaseCCP_DCE; +class RootNode; +class relocInfo; +class Scope; +class StartNode; +class SafePointNode; +class JVMState; +class TypeData; +class TypePtr; +class TypeFunc; +class Unique_Node_List; +class nmethod; +class WarmCallInfo; +#ifdef ENABLE_ZAP_DEAD_LOCALS +class MachSafePointNode; +#endif + +//------------------------------Compile---------------------------------------- +// This class defines a top-level Compiler invocation. + +class Compile : public Phase { + public: + // Fixed alias indexes. (See also MergeMemNode.) + enum { + AliasIdxTop = 1, // pseudo-index, aliases to nothing (used as sentinel value) + AliasIdxBot = 2, // pseudo-index, aliases to everything + AliasIdxRaw = 3 // hard-wired index for TypeRawPtr::BOTTOM + }; + + // Variant of TraceTime(NULL, &_t_accumulator, TimeCompiler); + // Integrated with logging. If logging is turned on, and dolog is true, + // then brackets are put into the log, with time stamps and node counts. + // (The time collection itself is always conditionalized on TimeCompiler.) + class TracePhase : public TraceTime { + private: + Compile* C; + CompileLog* _log; + public: + TracePhase(const char* name, elapsedTimer* accumulator, bool dolog); + ~TracePhase(); + }; + + // Information per category of alias (memory slice) + class AliasType { + private: + friend class Compile; + + int _index; // unique index, used with MergeMemNode + const TypePtr* _adr_type; // normalized address type + ciField* _field; // relevant instance field, or null if none + bool _is_rewritable; // false if the memory is write-once only + int _general_index; // if this is type is an instance, the general + // type that this is an instance of + + void Init(int i, const TypePtr* at); + + public: + int index() const { return _index; } + const TypePtr* adr_type() const { return _adr_type; } + ciField* field() const { return _field; } + bool is_rewritable() const { return _is_rewritable; } + bool is_volatile() const { return (_field ? _field->is_volatile() : false); } + int general_index() const { return (_general_index != 0) ? _general_index : _index; } + + void set_rewritable(bool z) { _is_rewritable = z; } + void set_field(ciField* f) { + assert(!_field,""); + _field = f; + if (f->is_final()) _is_rewritable = false; + } + + void print_on(outputStream* st) PRODUCT_RETURN; + }; + + enum { + logAliasCacheSize = 6, + AliasCacheSize = (1<* _intrinsics; // List of intrinsics. + GrowableArray* _macro_nodes; // List of nodes which need to be expanded before matching. + ConnectionGraph* _congraph; +#ifndef PRODUCT + IdealGraphPrinter* _printer; +#endif + + // Node management + uint _unique; // Counter for unique Node indices + debug_only(static int _debug_idx;) // Monotonic counter (not reset), use -XX:BreakAtNode= + Arena _node_arena; // Arena for new-space Nodes + Arena _old_arena; // Arena for old-space Nodes, lifetime during xform + RootNode* _root; // Unique root of compilation, or NULL after bail-out. + Node* _top; // Unique top node. (Reset by various phases.) + + Node* _immutable_memory; // Initial memory state + + Node* _recent_alloc_obj; + Node* _recent_alloc_ctl; + + // Blocked array of debugging and profiling information, + // tracked per node. + enum { _log2_node_notes_block_size = 8, + _node_notes_block_size = (1<<_log2_node_notes_block_size) + }; + GrowableArray* _node_note_array; + Node_Notes* _default_node_notes; // default notes for new nodes + + // After parsing and every bulk phase we hang onto the Root instruction. + // The RootNode instruction is where the whole program begins. It produces + // the initial Control and BOTTOM for everybody else. + + // Type management + Arena _Compile_types; // Arena for all types + Arena* _type_arena; // Alias for _Compile_types except in Initialize_shared() + Dict* _type_dict; // Intern table + void* _type_hwm; // Last allocation (see Type::operator new/delete) + size_t _type_last_size; // Last allocation size (see Type::operator new/delete) + ciMethod* _last_tf_m; // Cache for + const TypeFunc* _last_tf; // TypeFunc::make + AliasType** _alias_types; // List of alias types seen so far. + int _num_alias_types; // Logical length of _alias_types + int _max_alias_types; // Physical length of _alias_types + AliasCacheEntry _alias_cache[AliasCacheSize]; // Gets aliases w/o data structure walking + + // Parsing, optimization + PhaseGVN* _initial_gvn; // Results of parse-time PhaseGVN + Unique_Node_List* _for_igvn; // Initial work-list for next round of Iterative GVN + WarmCallInfo* _warm_calls; // Sorted work-list for heat-based inlining. + + // Matching, CFG layout, allocation, code generation + PhaseCFG* _cfg; // Results of CFG finding + bool _select_24_bit_instr; // We selected an instruction with a 24-bit result + bool _in_24_bit_fp_mode; // We are emitting instructions with 24-bit results + bool _has_java_calls; // True if the method has java calls + Matcher* _matcher; // Engine to map ideal to machine instructions + PhaseRegAlloc* _regalloc; // Results of register allocation. + int _frame_slots; // Size of total frame in stack slots + CodeOffsets _code_offsets; // Offsets into the code for various interesting entries + RegMask _FIRST_STACK_mask; // All stack slots usable for spills (depends on frame layout) + Arena* _indexSet_arena; // control IndexSet allocation within PhaseChaitin + void* _indexSet_free_block_list; // free list of IndexSet bit blocks + + uint _node_bundling_limit; + Bundle* _node_bundling_base; // Information for instruction bundling + + // Instruction bits passed off to the VM + int _method_size; // Size of nmethod code segment in bytes + CodeBuffer _code_buffer; // Where the code is assembled + int _first_block_size; // Size of unvalidated entry point code / OSR poison code + ExceptionHandlerTable _handler_table; // Table of native-code exception handlers + ImplicitExceptionTable _inc_table; // Table of implicit null checks in native code + OopMapSet* _oop_map_set; // Table of oop maps (one for each safepoint location) + static int _CompiledZap_count; // counter compared against CompileZap[First/Last] + BufferBlob* _scratch_buffer_blob; // For temporary code buffers. + relocInfo* _scratch_locs_memory; // For temporary code buffers. + + public: + // Accessors + + // The Compile instance currently active in this (compiler) thread. + static Compile* current() { + return (Compile*) ciEnv::current()->compiler_data(); + } + + // ID for this compilation. Useful for setting breakpoints in the debugger. + int compile_id() const { return _compile_id; } + + // Does this compilation allow instructions to subsume loads? User + // instructions that subsume a load may result in an unschedulable + // instruction sequence. + bool subsume_loads() const { return _subsume_loads; } + bool save_argument_registers() const { return _save_argument_registers; } + + + // Other fixed compilation parameters. + ciMethod* method() const { return _method; } + int entry_bci() const { return _entry_bci; } + bool is_osr_compilation() const { return _entry_bci != InvocationEntryBci; } + bool is_method_compilation() const { return (_method != NULL && !_method->flags().is_native()); } + const TypeFunc* tf() const { assert(_tf!=NULL, ""); return _tf; } + void init_tf(const TypeFunc* tf) { assert(_tf==NULL, ""); _tf = tf; } + InlineTree* ilt() const { return _ilt; } + address stub_function() const { return _stub_function; } + const char* stub_name() const { return _stub_name; } + address stub_entry_point() const { return _stub_entry_point; } + + // Control of this compilation. + int fixed_slots() const { assert(_fixed_slots >= 0, ""); return _fixed_slots; } + void set_fixed_slots(int n) { _fixed_slots = n; } + int major_progress() const { return _major_progress; } + void set_major_progress() { _major_progress++; } + void clear_major_progress() { _major_progress = 0; } + int num_loop_opts() const { return _num_loop_opts; } + void set_num_loop_opts(int n) { _num_loop_opts = n; } + int max_inline_size() const { return _max_inline_size; } + void set_freq_inline_size(int n) { _freq_inline_size = n; } + int freq_inline_size() const { return _freq_inline_size; } + void set_max_inline_size(int n) { _max_inline_size = n; } + bool deopt_happens() const { return _deopt_happens; } + bool has_loops() const { return _has_loops; } + void set_has_loops(bool z) { _has_loops = z; } + bool has_split_ifs() const { return _has_split_ifs; } + void set_has_split_ifs(bool z) { _has_split_ifs = z; } + bool has_unsafe_access() const { return _has_unsafe_access; } + void set_has_unsafe_access(bool z) { _has_unsafe_access = z; } + void set_trap_count(uint r, uint c) { assert(r < trapHistLength, "oob"); _trap_hist[r] = c; } + uint trap_count(uint r) const { assert(r < trapHistLength, "oob"); return _trap_hist[r]; } + bool trap_can_recompile() const { return _trap_can_recompile; } + void set_trap_can_recompile(bool z) { _trap_can_recompile = z; } + uint decompile_count() const { return _decompile_count; } + void set_decompile_count(uint c) { _decompile_count = c; } + bool allow_range_check_smearing() const; + bool do_inlining() const { return _do_inlining; } + void set_do_inlining(bool z) { _do_inlining = z; } + bool do_scheduling() const { return _do_scheduling; } + void set_do_scheduling(bool z) { _do_scheduling = z; } + bool do_count_invocations() const{ return _do_count_invocations; } + void set_do_count_invocations(bool z){ _do_count_invocations = z; } + bool do_method_data_update() const { return _do_method_data_update; } + void set_do_method_data_update(bool z) { _do_method_data_update = z; } + int AliasLevel() const { return _AliasLevel; } + bool print_assembly() const { return _print_assembly; } + void set_print_assembly(bool z) { _print_assembly = z; } + // check the CompilerOracle for special behaviours for this compile + bool method_has_option(const char * option) { + return method() != NULL && method()->has_option(option); + } +#ifndef PRODUCT + bool trace_opto_output() const { return _trace_opto_output; } +#endif + + void begin_method() { +#ifndef PRODUCT + if (_printer) _printer->begin_method(this); +#endif + } + void print_method(const char * name, int level = 1) { +#ifndef PRODUCT + if (_printer) _printer->print_method(this, name, level); +#endif + } + void end_method() { +#ifndef PRODUCT + if (_printer) _printer->end_method(); +#endif + } + + int macro_count() { return _macro_nodes->length(); } + Node* macro_node(int idx) { return _macro_nodes->at(idx); } + ConnectionGraph* congraph() { return _congraph;} + void add_macro_node(Node * n) { + //assert(n->is_macro(), "must be a macro node"); + assert(!_macro_nodes->contains(n), " duplicate entry in expand list"); + _macro_nodes->append(n); + } + void remove_macro_node(Node * n) { + // this function may be called twice for a node so check + // that the node is in the array before attempting to remove it + if (_macro_nodes->contains(n)) + _macro_nodes->remove(n); + } + + // Compilation environment. + Arena* comp_arena() { return &_comp_arena; } + ciEnv* env() const { return _env; } + CompileLog* log() const { return _log; } + bool failing() const { return _env->failing() || _failure_reason != NULL; } + const char* failure_reason() { return _failure_reason; } + bool failure_reason_is(const char* r) { return (r==_failure_reason) || (r!=NULL && _failure_reason!=NULL && strcmp(r, _failure_reason)==0); } + + void record_failure(const char* reason); + void record_method_not_compilable(const char* reason, bool all_tiers = false) { + // All bailouts cover "all_tiers" when TieredCompilation is off. + if (!TieredCompilation) all_tiers = true; + env()->record_method_not_compilable(reason, all_tiers); + // Record failure reason. + record_failure(reason); + } + void record_method_not_compilable_all_tiers(const char* reason) { + record_method_not_compilable(reason, true); + } + bool check_node_count(uint margin, const char* reason) { + if (unique() + margin > (uint)MaxNodeLimit) { + record_method_not_compilable(reason); + return true; + } else { + return false; + } + } + + // Node management + uint unique() const { return _unique; } + uint next_unique() { return _unique++; } + void set_unique(uint i) { _unique = i; } + static int debug_idx() { return debug_only(_debug_idx)+0; } + static void set_debug_idx(int i) { debug_only(_debug_idx = i); } + Arena* node_arena() { return &_node_arena; } + Arena* old_arena() { return &_old_arena; } + RootNode* root() const { return _root; } + void set_root(RootNode* r) { _root = r; } + StartNode* start() const; // (Derived from root.) + void init_start(StartNode* s); + Node* immutable_memory(); + + Node* recent_alloc_ctl() const { return _recent_alloc_ctl; } + Node* recent_alloc_obj() const { return _recent_alloc_obj; } + void set_recent_alloc(Node* ctl, Node* obj) { + _recent_alloc_ctl = ctl; + _recent_alloc_obj = obj; + } + + // Handy undefined Node + Node* top() const { return _top; } + + // these are used by guys who need to know about creation and transformation of top: + Node* cached_top_node() { return _top; } + void set_cached_top_node(Node* tn); + + GrowableArray* node_note_array() const { return _node_note_array; } + void set_node_note_array(GrowableArray* arr) { _node_note_array = arr; } + Node_Notes* default_node_notes() const { return _default_node_notes; } + void set_default_node_notes(Node_Notes* n) { _default_node_notes = n; } + + Node_Notes* node_notes_at(int idx) { + return locate_node_notes(_node_note_array, idx, false); + } + inline bool set_node_notes_at(int idx, Node_Notes* value); + + // Copy notes from source to dest, if they exist. + // Overwrite dest only if source provides something. + // Return true if information was moved. + bool copy_node_notes_to(Node* dest, Node* source); + + // Workhorse function to sort out the blocked Node_Notes array: + inline Node_Notes* locate_node_notes(GrowableArray* arr, + int idx, bool can_grow = false); + + void grow_node_notes(GrowableArray* arr, int grow_by); + + // Type management + Arena* type_arena() { return _type_arena; } + Dict* type_dict() { return _type_dict; } + void* type_hwm() { return _type_hwm; } + size_t type_last_size() { return _type_last_size; } + int num_alias_types() { return _num_alias_types; } + + void init_type_arena() { _type_arena = &_Compile_types; } + void set_type_arena(Arena* a) { _type_arena = a; } + void set_type_dict(Dict* d) { _type_dict = d; } + void set_type_hwm(void* p) { _type_hwm = p; } + void set_type_last_size(size_t sz) { _type_last_size = sz; } + + const TypeFunc* last_tf(ciMethod* m) { + return (m == _last_tf_m) ? _last_tf : NULL; + } + void set_last_tf(ciMethod* m, const TypeFunc* tf) { + assert(m != NULL || tf == NULL, ""); + _last_tf_m = m; + _last_tf = tf; + } + + AliasType* alias_type(int idx) { assert(idx < num_alias_types(), "oob"); return _alias_types[idx]; } + AliasType* alias_type(const TypePtr* adr_type) { return find_alias_type(adr_type, false); } + bool have_alias_type(const TypePtr* adr_type); + AliasType* alias_type(ciField* field); + + int get_alias_index(const TypePtr* at) { return alias_type(at)->index(); } + const TypePtr* get_adr_type(uint aidx) { return alias_type(aidx)->adr_type(); } + int get_general_index(uint aidx) { return alias_type(aidx)->general_index(); } + + // Building nodes + void rethrow_exceptions(JVMState* jvms); + void return_values(JVMState* jvms); + JVMState* build_start_state(StartNode* start, const TypeFunc* tf); + + // Decide how to build a call. + // The profile factor is a discount to apply to this site's interp. profile. + CallGenerator* call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual, JVMState* jvms, bool allow_inline, float profile_factor); + + // Report if there were too many traps at a current method and bci. + // Report if a trap was recorded, and/or PerMethodTrapLimit was exceeded. + // If there is no MDO at all, report no trap unless told to assume it. + bool too_many_traps(ciMethod* method, int bci, Deoptimization::DeoptReason reason); + // This version, unspecific to a particular bci, asks if + // PerMethodTrapLimit was exceeded for all inlined methods seen so far. + bool too_many_traps(Deoptimization::DeoptReason reason, + // Privately used parameter for logging: + ciMethodData* logmd = NULL); + // Report if there were too many recompiles at a method and bci. + bool too_many_recompiles(ciMethod* method, int bci, Deoptimization::DeoptReason reason); + + // Parsing, optimization + PhaseGVN* initial_gvn() { return _initial_gvn; } + Unique_Node_List* for_igvn() { return _for_igvn; } + inline void record_for_igvn(Node* n); // Body is after class Unique_Node_List. + void record_for_escape_analysis(Node* n); + void set_initial_gvn(PhaseGVN *gvn) { _initial_gvn = gvn; } + void set_for_igvn(Unique_Node_List *for_igvn) { _for_igvn = for_igvn; } + + void identify_useful_nodes(Unique_Node_List &useful); + void remove_useless_nodes (Unique_Node_List &useful); + + WarmCallInfo* warm_calls() const { return _warm_calls; } + void set_warm_calls(WarmCallInfo* l) { _warm_calls = l; } + WarmCallInfo* pop_warm_call(); + + // Matching, CFG layout, allocation, code generation + PhaseCFG* cfg() { return _cfg; } + bool select_24_bit_instr() const { return _select_24_bit_instr; } + bool in_24_bit_fp_mode() const { return _in_24_bit_fp_mode; } + bool has_java_calls() const { return _has_java_calls; } + Matcher* matcher() { return _matcher; } + PhaseRegAlloc* regalloc() { return _regalloc; } + int frame_slots() const { return _frame_slots; } + int frame_size_in_words() const; // frame_slots in units of the polymorphic 'words' + RegMask& FIRST_STACK_mask() { return _FIRST_STACK_mask; } + Arena* indexSet_arena() { return _indexSet_arena; } + void* indexSet_free_block_list() { return _indexSet_free_block_list; } + uint node_bundling_limit() { return _node_bundling_limit; } + Bundle* node_bundling_base() { return _node_bundling_base; } + void set_node_bundling_limit(uint n) { _node_bundling_limit = n; } + void set_node_bundling_base(Bundle* b) { _node_bundling_base = b; } + bool starts_bundle(const Node *n) const; + bool need_stack_bang(int frame_size_in_bytes) const; + bool need_register_stack_bang() const; + + void set_matcher(Matcher* m) { _matcher = m; } +//void set_regalloc(PhaseRegAlloc* ra) { _regalloc = ra; } + void set_indexSet_arena(Arena* a) { _indexSet_arena = a; } + void set_indexSet_free_block_list(void* p) { _indexSet_free_block_list = p; } + + // Remember if this compilation changes hardware mode to 24-bit precision + void set_24_bit_selection_and_mode(bool selection, bool mode) { + _select_24_bit_instr = selection; + _in_24_bit_fp_mode = mode; + } + + void set_has_java_calls(bool z) { _has_java_calls = z; } + + // Instruction bits passed off to the VM + int code_size() { return _method_size; } + CodeBuffer* code_buffer() { return &_code_buffer; } + int first_block_size() { return _first_block_size; } + void set_frame_complete(int off) { _code_offsets.set_value(CodeOffsets::Frame_Complete, off); } + ExceptionHandlerTable* handler_table() { return &_handler_table; } + ImplicitExceptionTable* inc_table() { return &_inc_table; } + OopMapSet* oop_map_set() { return _oop_map_set; } + DebugInformationRecorder* debug_info() { return env()->debug_info(); } + Dependencies* dependencies() { return env()->dependencies(); } + static int CompiledZap_count() { return _CompiledZap_count; } + BufferBlob* scratch_buffer_blob() { return _scratch_buffer_blob; } + void init_scratch_buffer_blob(); + void set_scratch_buffer_blob(BufferBlob* b) { _scratch_buffer_blob = b; } + relocInfo* scratch_locs_memory() { return _scratch_locs_memory; } + void set_scratch_locs_memory(relocInfo* b) { _scratch_locs_memory = b; } + + // emit to scratch blob, report resulting size + uint scratch_emit_size(const Node* n); + + enum ScratchBufferBlob { + MAX_inst_size = 1024, + MAX_locs_size = 128, // number of relocInfo elements + MAX_const_size = 128, + MAX_stubs_size = 128 + }; + + // Major entry point. Given a Scope, compile the associated method. + // For normal compilations, entry_bci is InvocationEntryBci. For on stack + // replacement, entry_bci indicates the bytecode for which to compile a + // continuation. + Compile(ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, + int entry_bci, bool subsume_loads); + + // Second major entry point. From the TypeFunc signature, generate code + // to pass arguments from the Java calling convention to the C calling + // convention. + Compile(ciEnv* ci_env, const TypeFunc *(*gen)(), + address stub_function, const char *stub_name, + int is_fancy_jump, bool pass_tls, + bool save_arg_registers, bool return_pc); + + // From the TypeFunc signature, generate code to pass arguments + // from Compiled calling convention to Interpreter's calling convention + void Generate_Compiled_To_Interpreter_Graph(const TypeFunc *tf, address interpreter_entry); + + // From the TypeFunc signature, generate code to pass arguments + // from Interpreter's calling convention to Compiler's calling convention + void Generate_Interpreter_To_Compiled_Graph(const TypeFunc *tf); + + // Are we compiling a method? + bool has_method() { return method() != NULL; } + + // Maybe print some information about this compile. + void print_compile_messages(); + + // Final graph reshaping, a post-pass after the regular optimizer is done. + bool final_graph_reshaping(); + + // returns true if adr is completely contained in the given alias category + bool must_alias(const TypePtr* adr, int alias_idx); + + // returns true if adr overlaps with the given alias category + bool can_alias(const TypePtr* adr, int alias_idx); + + // Driver for converting compiler's IR into machine code bits + void Output(); + + // Accessors for node bundling info. + Bundle* node_bundling(const Node *n); + bool valid_bundle_info(const Node *n); + + // Schedule and Bundle the instructions + void ScheduleAndBundle(); + + // Build OopMaps for each GC point + void BuildOopMaps(); + // Append debug info for the node to the array + void FillLocArray( int idx, Node *local, GrowableArray *array ); + + // Process an OopMap Element while emitting nodes + void Process_OopMap_Node(MachNode *mach, int code_offset); + + // Write out basic block data to code buffer + void Fill_buffer(); + + // Determine which variable sized branches can be shortened + void Shorten_branches(Label *labels, int& code_size, int& reloc_size, int& stub_size, int& const_size); + + // Compute the size of first NumberOfLoopInstrToAlign instructions + // at the head of a loop. + void compute_loop_first_inst_sizes(); + + // Compute the information for the exception tables + void FillExceptionTables(uint cnt, uint *call_returns, uint *inct_starts, Label *blk_labels); + + // Stack slots that may be unused by the calling convention but must + // otherwise be preserved. On Intel this includes the return address. + // On PowerPC it includes the 4 words holding the old TOC & LR glue. + uint in_preserve_stack_slots(); + + // "Top of Stack" slots that may be unused by the calling convention but must + // otherwise be preserved. + // On Intel these are not necessary and the value can be zero. + // On Sparc this describes the words reserved for storing a register window + // when an interrupt occurs. + static uint out_preserve_stack_slots(); + + // Number of outgoing stack slots killed above the out_preserve_stack_slots + // for calls to C. Supports the var-args backing area for register parms. + uint varargs_C_out_slots_killed() const; + + // Number of Stack Slots consumed by a synchronization entry + int sync_stack_slots() const; + + // Compute the name of old_SP. See .ad for frame layout. + OptoReg::Name compute_old_SP(); + +#ifdef ENABLE_ZAP_DEAD_LOCALS + static bool is_node_getting_a_safepoint(Node*); + void Insert_zap_nodes(); + Node* call_zap_node(MachSafePointNode* n, int block_no); +#endif + + private: + // Phase control: + void Init(int aliaslevel); // Prepare for a single compilation + int Inline_Warm(); // Find more inlining work. + void Finish_Warm(); // Give up on further inlines. + void Optimize(); // Given a graph, optimize it + void Code_Gen(); // Generate code from a graph + + // Management of the AliasType table. + void grow_alias_types(); + AliasCacheEntry* probe_alias_cache(const TypePtr* adr_type); + const TypePtr *flatten_alias_type(const TypePtr* adr_type) const; + AliasType* find_alias_type(const TypePtr* adr_type, bool no_create); + + void verify_top(Node*) const PRODUCT_RETURN; + + // Intrinsic setup. + void register_library_intrinsics(); // initializer + CallGenerator* make_vm_intrinsic(ciMethod* m, bool is_virtual); // constructor + int intrinsic_insertion_index(ciMethod* m, bool is_virtual); // helper + CallGenerator* find_intrinsic(ciMethod* m, bool is_virtual); // query fn + void register_intrinsic(CallGenerator* cg); // update fn + +#ifndef PRODUCT + static juint _intrinsic_hist_count[vmIntrinsics::ID_LIMIT]; + static jubyte _intrinsic_hist_flags[vmIntrinsics::ID_LIMIT]; +#endif + + public: + + // Note: Histogram array size is about 1 Kb. + enum { // flag bits: + _intrinsic_worked = 1, // succeeded at least once + _intrinsic_failed = 2, // tried it but it failed + _intrinsic_disabled = 4, // was requested but disabled (e.g., -XX:-InlineUnsafeOps) + _intrinsic_virtual = 8, // was seen in the virtual form (rare) + _intrinsic_both = 16 // was seen in the non-virtual form (usual) + }; + // Update histogram. Return boolean if this is a first-time occurrence. + static bool gather_intrinsic_statistics(vmIntrinsics::ID id, + bool is_virtual, int flags) PRODUCT_RETURN0; + static void print_intrinsic_statistics() PRODUCT_RETURN; + + // Graph verification code + // Walk the node list, verifying that there is a one-to-one + // correspondence between Use-Def edges and Def-Use edges + // The option no_dead_code enables stronger checks that the + // graph is strongly connected from root in both directions. + void verify_graph_edges(bool no_dead_code = false) PRODUCT_RETURN; + + // Print bytecodes, including the scope inlining tree + void print_codes(); + + // End-of-run dumps. + static void print_statistics() PRODUCT_RETURN; + + // Dump formatted assembly + void dump_asm(int *pcs = NULL, uint pc_limit = 0) PRODUCT_RETURN; + void dump_pc(int *pcs, int pc_limit, Node *n); + + // Verify ADLC assumptions during startup + static void adlc_verification() PRODUCT_RETURN; + + // Definitions of pd methods + static void pd_compiler2_init(); +}; diff --git a/hotspot/src/share/vm/opto/connode.cpp b/hotspot/src/share/vm/opto/connode.cpp new file mode 100644 index 00000000000..6896f34710d --- /dev/null +++ b/hotspot/src/share/vm/opto/connode.cpp @@ -0,0 +1,1227 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_connode.cpp.incl" + +//============================================================================= +//------------------------------hash------------------------------------------- +uint ConNode::hash() const { + return (uintptr_t)in(TypeFunc::Control) + _type->hash(); +} + +//------------------------------make------------------------------------------- +ConNode *ConNode::make( Compile* C, const Type *t ) { + switch( t->basic_type() ) { + case T_INT: return new (C, 1) ConINode( t->is_int() ); + case T_ARRAY: return new (C, 1) ConPNode( t->is_aryptr() ); + case T_LONG: return new (C, 1) ConLNode( t->is_long() ); + case T_FLOAT: return new (C, 1) ConFNode( t->is_float_constant() ); + case T_DOUBLE: return new (C, 1) ConDNode( t->is_double_constant() ); + case T_VOID: return new (C, 1) ConNode ( Type::TOP ); + case T_OBJECT: return new (C, 1) ConPNode( t->is_oopptr() ); + case T_ADDRESS: return new (C, 1) ConPNode( t->is_ptr() ); + // Expected cases: TypePtr::NULL_PTR, any is_rawptr() + // Also seen: AnyPtr(TopPTR *+top); from command line: + // r -XX:+PrintOpto -XX:CIStart=285 -XX:+CompileTheWorld -XX:CompileTheWorldStartAt=660 + // %%%% Stop using TypePtr::NULL_PTR to represent nulls: use either TypeRawPtr::NULL_PTR + // or else TypeOopPtr::NULL_PTR. Then set Type::_basic_type[AnyPtr] = T_ILLEGAL + } + ShouldNotReachHere(); + return NULL; +} + +//============================================================================= +/* +The major change is for CMoveP and StrComp. They have related but slightly +different problems. They both take in TWO oops which are both null-checked +independently before the using Node. After CCP removes the CastPP's they need +to pick up the guarding test edge - in this case TWO control edges. I tried +various solutions, all have problems: + +(1) Do nothing. This leads to a bug where we hoist a Load from a CMoveP or a +StrComp above a guarding null check. I've seen both cases in normal -Xcomp +testing. + +(2) Plug the control edge from 1 of the 2 oops in. Apparent problem here is +to figure out which test post-dominates. The real problem is that it doesn't +matter which one you pick. After you pick up, the dominating-test elider in +IGVN can remove the test and allow you to hoist up to the dominating test on +the choosen oop bypassing the test on the not-choosen oop. Seen in testing. +Oops. + +(3) Leave the CastPP's in. This makes the graph more accurate in some sense; +we get to keep around the knowledge that an oop is not-null after some test. +Alas, the CastPP's interfere with GVN (some values are the regular oop, some +are the CastPP of the oop, all merge at Phi's which cannot collapse, etc). +This cost us 10% on SpecJVM, even when I removed some of the more trivial +cases in the optimizer. Removing more useless Phi's started allowing Loads to +illegally float above null checks. I gave up on this approach. + +(4) Add BOTH control edges to both tests. Alas, too much code knows that +control edges are in slot-zero ONLY. Many quick asserts fail; no way to do +this one. Note that I really want to allow the CMoveP to float and add both +control edges to the dependent Load op - meaning I can select early but I +cannot Load until I pass both tests. + +(5) Do not hoist CMoveP and StrComp. To this end I added the v-call +depends_only_on_test(). No obvious performance loss on Spec, but we are +clearly conservative on CMoveP (also so on StrComp but that's unlikely to +matter ever). + +*/ + + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. +// Move constants to the right. +Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if( in(0) && remove_dead_region(phase, can_reshape) ) return this; + assert( !phase->eqv(in(Condition), this) && + !phase->eqv(in(IfFalse), this) && + !phase->eqv(in(IfTrue), this), "dead loop in CMoveNode::Ideal" ); + if( phase->type(in(Condition)) == Type::TOP ) + return NULL; // return NULL when Condition is dead + + if( in(IfFalse)->is_Con() && !in(IfTrue)->is_Con() ) { + if( in(Condition)->is_Bool() ) { + BoolNode* b = in(Condition)->as_Bool(); + BoolNode* b2 = b->negate(phase); + return make( phase->C, in(Control), phase->transform(b2), in(IfTrue), in(IfFalse), _type ); + } + } + return NULL; +} + +//------------------------------is_cmove_id------------------------------------ +// Helper function to check for CMOVE identity. Shared with PhiNode::Identity +Node *CMoveNode::is_cmove_id( PhaseTransform *phase, Node *cmp, Node *t, Node *f, BoolNode *b ) { + // Check for Cmp'ing and CMove'ing same values + if( (phase->eqv(cmp->in(1),f) && + phase->eqv(cmp->in(2),t)) || + // Swapped Cmp is OK + (phase->eqv(cmp->in(2),f) && + phase->eqv(cmp->in(1),t)) ) { + // Check for "(t==f)?t:f;" and replace with "f" + if( b->_test._test == BoolTest::eq ) + return f; + // Allow the inverted case as well + // Check for "(t!=f)?t:f;" and replace with "t" + if( b->_test._test == BoolTest::ne ) + return t; + } + return NULL; +} + +//------------------------------Identity--------------------------------------- +// Conditional-move is an identity if both inputs are the same, or the test +// true or false. +Node *CMoveNode::Identity( PhaseTransform *phase ) { + if( phase->eqv(in(IfFalse),in(IfTrue)) ) // C-moving identical inputs? + return in(IfFalse); // Then it doesn't matter + if( phase->type(in(Condition)) == TypeInt::ZERO ) + return in(IfFalse); // Always pick left(false) input + if( phase->type(in(Condition)) == TypeInt::ONE ) + return in(IfTrue); // Always pick right(true) input + + // Check for CMove'ing a constant after comparing against the constant. + // Happens all the time now, since if we compare equality vs a constant in + // the parser, we "know" the variable is constant on one path and we force + // it. Thus code like "if( x==0 ) {/*EMPTY*/}" ends up inserting a + // conditional move: "x = (x==0)?0:x;". Yucko. This fix is slightly more + // general in that we don't need constants. + if( in(Condition)->is_Bool() ) { + BoolNode *b = in(Condition)->as_Bool(); + Node *cmp = b->in(1); + if( cmp->is_Cmp() ) { + Node *id = is_cmove_id( phase, cmp, in(IfTrue), in(IfFalse), b ); + if( id ) return id; + } + } + + return this; +} + +//------------------------------Value------------------------------------------ +// Result is the meet of inputs +const Type *CMoveNode::Value( PhaseTransform *phase ) const { + if( phase->type(in(Condition)) == Type::TOP ) + return Type::TOP; + return phase->type(in(IfFalse))->meet(phase->type(in(IfTrue))); +} + +//------------------------------make------------------------------------------- +// Make a correctly-flavored CMove. Since _type is directly determined +// from the inputs we do not need to specify it here. +CMoveNode *CMoveNode::make( Compile *C, Node *c, Node *bol, Node *left, Node *right, const Type *t ) { + switch( t->basic_type() ) { + case T_INT: return new (C, 4) CMoveINode( bol, left, right, t->is_int() ); + case T_FLOAT: return new (C, 4) CMoveFNode( bol, left, right, t ); + case T_DOUBLE: return new (C, 4) CMoveDNode( bol, left, right, t ); + case T_LONG: return new (C, 4) CMoveLNode( bol, left, right, t->is_long() ); + case T_OBJECT: return new (C, 4) CMovePNode( c, bol, left, right, t->is_oopptr() ); + case T_ADDRESS: return new (C, 4) CMovePNode( c, bol, left, right, t->is_ptr() ); + default: + ShouldNotReachHere(); + return NULL; + } +} + +//============================================================================= +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. +// Check for conversions to boolean +Node *CMoveINode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Try generic ideal's first + Node *x = CMoveNode::Ideal(phase, can_reshape); + if( x ) return x; + + // If zero is on the left (false-case, no-move-case) it must mean another + // constant is on the right (otherwise the shared CMove::Ideal code would + // have moved the constant to the right). This situation is bad for Intel + // and a don't-care for Sparc. It's bad for Intel because the zero has to + // be manifested in a register with a XOR which kills flags, which are live + // on input to the CMoveI, leading to a situation which causes excessive + // spilling on Intel. For Sparc, if the zero in on the left the Sparc will + // zero a register via G0 and conditionally-move the other constant. If the + // zero is on the right, the Sparc will load the first constant with a + // 13-bit set-lo and conditionally move G0. See bug 4677505. + if( phase->type(in(IfFalse)) == TypeInt::ZERO && !(phase->type(in(IfTrue)) == TypeInt::ZERO) ) { + if( in(Condition)->is_Bool() ) { + BoolNode* b = in(Condition)->as_Bool(); + BoolNode* b2 = b->negate(phase); + return make( phase->C, in(Control), phase->transform(b2), in(IfTrue), in(IfFalse), _type ); + } + } + + // Now check for booleans + int flip = 0; + + // Check for picking from zero/one + if( phase->type(in(IfFalse)) == TypeInt::ZERO && phase->type(in(IfTrue)) == TypeInt::ONE ) { + flip = 1 - flip; + } else if( phase->type(in(IfFalse)) == TypeInt::ONE && phase->type(in(IfTrue)) == TypeInt::ZERO ) { + } else return NULL; + + // Check for eq/ne test + if( !in(1)->is_Bool() ) return NULL; + BoolNode *bol = in(1)->as_Bool(); + if( bol->_test._test == BoolTest::eq ) { + } else if( bol->_test._test == BoolTest::ne ) { + flip = 1-flip; + } else return NULL; + + // Check for vs 0 or 1 + if( !bol->in(1)->is_Cmp() ) return NULL; + const CmpNode *cmp = bol->in(1)->as_Cmp(); + if( phase->type(cmp->in(2)) == TypeInt::ZERO ) { + } else if( phase->type(cmp->in(2)) == TypeInt::ONE ) { + // Allow cmp-vs-1 if the other input is bounded by 0-1 + if( phase->type(cmp->in(1)) != TypeInt::BOOL ) + return NULL; + flip = 1 - flip; + } else return NULL; + + // Convert to a bool (flipped) + // Build int->bool conversion +#ifndef PRODUCT + if( PrintOpto ) tty->print_cr("CMOV to I2B"); +#endif + Node *n = new (phase->C, 2) Conv2BNode( cmp->in(1) ); + if( flip ) + n = new (phase->C, 3) XorINode( phase->transform(n), phase->intcon(1) ); + + return n; +} + +//============================================================================= +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. +// Check for absolute value +Node *CMoveFNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Try generic ideal's first + Node *x = CMoveNode::Ideal(phase, can_reshape); + if( x ) return x; + + int cmp_zero_idx = 0; // Index of compare input where to look for zero + int phi_x_idx = 0; // Index of phi input where to find naked x + + // Find the Bool + if( !in(1)->is_Bool() ) return NULL; + BoolNode *bol = in(1)->as_Bool(); + // Check bool sense + switch( bol->_test._test ) { + case BoolTest::lt: cmp_zero_idx = 1; phi_x_idx = IfTrue; break; + case BoolTest::le: cmp_zero_idx = 2; phi_x_idx = IfFalse; break; + case BoolTest::gt: cmp_zero_idx = 2; phi_x_idx = IfTrue; break; + case BoolTest::ge: cmp_zero_idx = 1; phi_x_idx = IfFalse; break; + default: return NULL; break; + } + + // Find zero input of CmpF; the other input is being abs'd + Node *cmpf = bol->in(1); + if( cmpf->Opcode() != Op_CmpF ) return NULL; + Node *X = NULL; + bool flip = false; + if( phase->type(cmpf->in(cmp_zero_idx)) == TypeF::ZERO ) { + X = cmpf->in(3 - cmp_zero_idx); + } else if (phase->type(cmpf->in(3 - cmp_zero_idx)) == TypeF::ZERO) { + // The test is inverted, we should invert the result... + X = cmpf->in(cmp_zero_idx); + flip = true; + } else { + return NULL; + } + + // If X is found on the appropriate phi input, find the subtract on the other + if( X != in(phi_x_idx) ) return NULL; + int phi_sub_idx = phi_x_idx == IfTrue ? IfFalse : IfTrue; + Node *sub = in(phi_sub_idx); + + // Allow only SubF(0,X) and fail out for all others; NegF is not OK + if( sub->Opcode() != Op_SubF || + sub->in(2) != X || + phase->type(sub->in(1)) != TypeF::ZERO ) return NULL; + + Node *abs = new (phase->C, 2) AbsFNode( X ); + if( flip ) + abs = new (phase->C, 3) SubFNode(sub->in(1), phase->transform(abs)); + + return abs; +} + +//============================================================================= +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. +// Check for absolute value +Node *CMoveDNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Try generic ideal's first + Node *x = CMoveNode::Ideal(phase, can_reshape); + if( x ) return x; + + int cmp_zero_idx = 0; // Index of compare input where to look for zero + int phi_x_idx = 0; // Index of phi input where to find naked x + + // Find the Bool + if( !in(1)->is_Bool() ) return NULL; + BoolNode *bol = in(1)->as_Bool(); + // Check bool sense + switch( bol->_test._test ) { + case BoolTest::lt: cmp_zero_idx = 1; phi_x_idx = IfTrue; break; + case BoolTest::le: cmp_zero_idx = 2; phi_x_idx = IfFalse; break; + case BoolTest::gt: cmp_zero_idx = 2; phi_x_idx = IfTrue; break; + case BoolTest::ge: cmp_zero_idx = 1; phi_x_idx = IfFalse; break; + default: return NULL; break; + } + + // Find zero input of CmpD; the other input is being abs'd + Node *cmpd = bol->in(1); + if( cmpd->Opcode() != Op_CmpD ) return NULL; + Node *X = NULL; + bool flip = false; + if( phase->type(cmpd->in(cmp_zero_idx)) == TypeD::ZERO ) { + X = cmpd->in(3 - cmp_zero_idx); + } else if (phase->type(cmpd->in(3 - cmp_zero_idx)) == TypeD::ZERO) { + // The test is inverted, we should invert the result... + X = cmpd->in(cmp_zero_idx); + flip = true; + } else { + return NULL; + } + + // If X is found on the appropriate phi input, find the subtract on the other + if( X != in(phi_x_idx) ) return NULL; + int phi_sub_idx = phi_x_idx == IfTrue ? IfFalse : IfTrue; + Node *sub = in(phi_sub_idx); + + // Allow only SubD(0,X) and fail out for all others; NegD is not OK + if( sub->Opcode() != Op_SubD || + sub->in(2) != X || + phase->type(sub->in(1)) != TypeD::ZERO ) return NULL; + + Node *abs = new (phase->C, 2) AbsDNode( X ); + if( flip ) + abs = new (phase->C, 3) SubDNode(sub->in(1), phase->transform(abs)); + + return abs; +} + + +//============================================================================= +// If input is already higher or equal to cast type, then this is an identity. +Node *ConstraintCastNode::Identity( PhaseTransform *phase ) { + return phase->type(in(1))->higher_equal(_type) ? in(1) : this; +} + +//------------------------------Value------------------------------------------ +// Take 'join' of input and cast-up type +const Type *ConstraintCastNode::Value( PhaseTransform *phase ) const { + if( in(0) && phase->type(in(0)) == Type::TOP ) return Type::TOP; + const Type* ft = phase->type(in(1))->filter(_type); + +#ifdef ASSERT + // Previous versions of this function had some special case logic, + // which is no longer necessary. Make sure of the required effects. + switch (Opcode()) { + case Op_CastII: + { + const Type* t1 = phase->type(in(1)); + if( t1 == Type::TOP ) assert(ft == Type::TOP, "special case #1"); + const Type* rt = t1->join(_type); + if (rt->empty()) assert(ft == Type::TOP, "special case #2"); + break; + } + case Op_CastPP: + if (phase->type(in(1)) == TypePtr::NULL_PTR && + _type->isa_ptr() && _type->is_ptr()->_ptr == TypePtr::NotNull) + assert(ft == Type::TOP, "special case #3"); + break; + } +#endif //ASSERT + + return ft; +} + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node *ConstraintCastNode::Ideal(PhaseGVN *phase, bool can_reshape){ + return (in(0) && remove_dead_region(phase, can_reshape)) ? this : NULL; +} + +//------------------------------Ideal_DU_postCCP------------------------------- +// Throw away cast after constant propagation +Node *ConstraintCastNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { + const Type *t = ccp->type(in(1)); + ccp->hash_delete(this); + set_type(t); // Turn into ID function + ccp->hash_insert(this); + return this; +} + + +//============================================================================= + +//------------------------------Ideal_DU_postCCP------------------------------- +// If not converting int->oop, throw away cast after constant propagation +Node *CastPPNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { + const Type *t = ccp->type(in(1)); + if (!t->isa_oop_ptr()) { + return NULL; // do not transform raw pointers + } + return ConstraintCastNode::Ideal_DU_postCCP(ccp); +} + + + +//============================================================================= +//------------------------------Identity--------------------------------------- +// If input is already higher or equal to cast type, then this is an identity. +Node *CheckCastPPNode::Identity( PhaseTransform *phase ) { + // Toned down to rescue meeting at a Phi 3 different oops all implementing + // the same interface. CompileTheWorld starting at 502, kd12rc1.zip. + return (phase->type(in(1)) == phase->type(this)) ? in(1) : this; +} + +// Determine whether "n" is a node which can cause an alias of one of its inputs. Node types +// which can create aliases are: CheckCastPP, Phi, and any store (if there is also a load from +// the location.) +// Note: this checks for aliases created in this compilation, not ones which may +// be potentially created at call sites. +static bool can_cause_alias(Node *n, PhaseTransform *phase) { + bool possible_alias = false; + + if (n->is_Store()) { + possible_alias = !n->as_Store()->value_never_loaded(phase); + } else { + int opc = n->Opcode(); + possible_alias = n->is_Phi() || + opc == Op_CheckCastPP || + opc == Op_StorePConditional || + opc == Op_CompareAndSwapP; + } + return possible_alias; +} + +//------------------------------Value------------------------------------------ +// Take 'join' of input and cast-up type, unless working with an Interface +const Type *CheckCastPPNode::Value( PhaseTransform *phase ) const { + if( in(0) && phase->type(in(0)) == Type::TOP ) return Type::TOP; + + const Type *inn = phase->type(in(1)); + if( inn == Type::TOP ) return Type::TOP; // No information yet + + const TypePtr *in_type = inn->isa_ptr(); + const TypePtr *my_type = _type->isa_ptr(); + const Type *result = _type; + if( in_type != NULL && my_type != NULL ) { + TypePtr::PTR in_ptr = in_type->ptr(); + if( in_ptr == TypePtr::Null ) { + result = in_type; + } else if( in_ptr == TypePtr::Constant ) { + // Casting a constant oop to an interface? + // (i.e., a String to a Comparable?) + // Then return the interface. + const TypeOopPtr *jptr = my_type->isa_oopptr(); + assert( jptr, "" ); + result = (jptr->klass()->is_interface() || !in_type->higher_equal(_type)) + ? my_type->cast_to_ptr_type( TypePtr::NotNull ) + : in_type; + } else { + result = my_type->cast_to_ptr_type( my_type->join_ptr(in_ptr) ); + } + } + return result; + + // JOIN NOT DONE HERE BECAUSE OF INTERFACE ISSUES. + // FIX THIS (DO THE JOIN) WHEN UNION TYPES APPEAR! + + // + // Remove this code after overnight run indicates no performance + // loss from not performing JOIN at CheckCastPPNode + // + // const TypeInstPtr *in_oop = in->isa_instptr(); + // const TypeInstPtr *my_oop = _type->isa_instptr(); + // // If either input is an 'interface', return destination type + // assert (in_oop == NULL || in_oop->klass() != NULL, ""); + // assert (my_oop == NULL || my_oop->klass() != NULL, ""); + // if( (in_oop && in_oop->klass()->klass_part()->is_interface()) + // ||(my_oop && my_oop->klass()->klass_part()->is_interface()) ) { + // TypePtr::PTR in_ptr = in->isa_ptr() ? in->is_ptr()->_ptr : TypePtr::BotPTR; + // // Preserve cast away nullness for interfaces + // if( in_ptr == TypePtr::NotNull && my_oop && my_oop->_ptr == TypePtr::BotPTR ) { + // return my_oop->cast_to_ptr_type(TypePtr::NotNull); + // } + // return _type; + // } + // + // // Neither the input nor the destination type is an interface, + // + // // history: JOIN used to cause weird corner case bugs + // // return (in == TypeOopPtr::NULL_PTR) ? in : _type; + // // JOIN picks up NotNull in common instance-of/check-cast idioms, both oops. + // // JOIN does not preserve NotNull in other cases, e.g. RawPtr vs InstPtr + // const Type *join = in->join(_type); + // // Check if join preserved NotNull'ness for pointers + // if( join->isa_ptr() && _type->isa_ptr() ) { + // TypePtr::PTR join_ptr = join->is_ptr()->_ptr; + // TypePtr::PTR type_ptr = _type->is_ptr()->_ptr; + // // If there isn't any NotNull'ness to preserve + // // OR if join preserved NotNull'ness then return it + // if( type_ptr == TypePtr::BotPTR || type_ptr == TypePtr::Null || + // join_ptr == TypePtr::NotNull || join_ptr == TypePtr::Constant ) { + // return join; + // } + // // ELSE return same old type as before + // return _type; + // } + // // Not joining two pointers + // return join; +} + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node *CheckCastPPNode::Ideal(PhaseGVN *phase, bool can_reshape){ + return (in(0) && remove_dead_region(phase, can_reshape)) ? this : NULL; +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +Node *Conv2BNode::Identity( PhaseTransform *phase ) { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return in(1); + if( t == TypeInt::ZERO ) return in(1); + if( t == TypeInt::ONE ) return in(1); + if( t == TypeInt::BOOL ) return in(1); + return this; +} + +//------------------------------Value------------------------------------------ +const Type *Conv2BNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + if( t == TypeInt::ZERO ) return TypeInt::ZERO; + if( t == TypePtr::NULL_PTR ) return TypeInt::ZERO; + const TypePtr *tp = t->isa_ptr(); + if( tp != NULL ) { + if( tp->ptr() == TypePtr::AnyNull ) return Type::TOP; + if( tp->ptr() == TypePtr::Constant) return TypeInt::ONE; + if (tp->ptr() == TypePtr::NotNull) return TypeInt::ONE; + return TypeInt::BOOL; + } + if (t->base() != Type::Int) return TypeInt::BOOL; + const TypeInt *ti = t->is_int(); + if( ti->_hi < 0 || ti->_lo > 0 ) return TypeInt::ONE; + return TypeInt::BOOL; +} + + +// The conversions operations are all Alpha sorted. Please keep it that way! +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvD2FNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + if( t == Type::DOUBLE ) return Type::FLOAT; + const TypeD *td = t->is_double_constant(); + return TypeF::make( (float)td->getd() ); +} + +//------------------------------Identity--------------------------------------- +// Float's can be converted to doubles with no loss of bits. Hence +// converting a float to a double and back to a float is a NOP. +Node *ConvD2FNode::Identity(PhaseTransform *phase) { + return (in(1)->Opcode() == Op_ConvF2D) ? in(1)->in(1) : this; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvD2INode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + if( t == Type::DOUBLE ) return TypeInt::INT; + const TypeD *td = t->is_double_constant(); + return TypeInt::make( SharedRuntime::d2i( td->getd() ) ); +} + +//------------------------------Ideal------------------------------------------ +// If converting to an int type, skip any rounding nodes +Node *ConvD2INode::Ideal(PhaseGVN *phase, bool can_reshape) { + if( in(1)->Opcode() == Op_RoundDouble ) + set_req(1,in(1)->in(1)); + return NULL; +} + +//------------------------------Identity--------------------------------------- +// Int's can be converted to doubles with no loss of bits. Hence +// converting an integer to a double and back to an integer is a NOP. +Node *ConvD2INode::Identity(PhaseTransform *phase) { + return (in(1)->Opcode() == Op_ConvI2D) ? in(1)->in(1) : this; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvD2LNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + if( t == Type::DOUBLE ) return TypeLong::LONG; + const TypeD *td = t->is_double_constant(); + return TypeLong::make( SharedRuntime::d2l( td->getd() ) ); +} + +//------------------------------Identity--------------------------------------- +Node *ConvD2LNode::Identity(PhaseTransform *phase) { + // Remove ConvD2L->ConvL2D->ConvD2L sequences. + if( in(1) ->Opcode() == Op_ConvL2D && + in(1)->in(1)->Opcode() == Op_ConvD2L ) + return in(1)->in(1); + return this; +} + +//------------------------------Ideal------------------------------------------ +// If converting to an int type, skip any rounding nodes +Node *ConvD2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if( in(1)->Opcode() == Op_RoundDouble ) + set_req(1,in(1)->in(1)); + return NULL; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvF2DNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + if( t == Type::FLOAT ) return Type::DOUBLE; + const TypeF *tf = t->is_float_constant(); +#ifndef IA64 + return TypeD::make( (double)tf->getf() ); +#else + float x = tf->getf(); + return TypeD::make( (x == 0.0f) ? (double)x : (double)x + ia64_double_zero ); +#endif +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvF2INode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + if( t == Type::FLOAT ) return TypeInt::INT; + const TypeF *tf = t->is_float_constant(); + return TypeInt::make( SharedRuntime::f2i( tf->getf() ) ); +} + +//------------------------------Identity--------------------------------------- +Node *ConvF2INode::Identity(PhaseTransform *phase) { + // Remove ConvF2I->ConvI2F->ConvF2I sequences. + if( in(1) ->Opcode() == Op_ConvI2F && + in(1)->in(1)->Opcode() == Op_ConvF2I ) + return in(1)->in(1); + return this; +} + +//------------------------------Ideal------------------------------------------ +// If converting to an int type, skip any rounding nodes +Node *ConvF2INode::Ideal(PhaseGVN *phase, bool can_reshape) { + if( in(1)->Opcode() == Op_RoundFloat ) + set_req(1,in(1)->in(1)); + return NULL; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvF2LNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + if( t == Type::FLOAT ) return TypeLong::LONG; + const TypeF *tf = t->is_float_constant(); + return TypeLong::make( SharedRuntime::f2l( tf->getf() ) ); +} + +//------------------------------Identity--------------------------------------- +Node *ConvF2LNode::Identity(PhaseTransform *phase) { + // Remove ConvF2L->ConvL2F->ConvF2L sequences. + if( in(1) ->Opcode() == Op_ConvL2F && + in(1)->in(1)->Opcode() == Op_ConvF2L ) + return in(1)->in(1); + return this; +} + +//------------------------------Ideal------------------------------------------ +// If converting to an int type, skip any rounding nodes +Node *ConvF2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if( in(1)->Opcode() == Op_RoundFloat ) + set_req(1,in(1)->in(1)); + return NULL; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvI2DNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + const TypeInt *ti = t->is_int(); + if( ti->is_con() ) return TypeD::make( (double)ti->get_con() ); + return bottom_type(); +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvI2FNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + const TypeInt *ti = t->is_int(); + if( ti->is_con() ) return TypeF::make( (float)ti->get_con() ); + return bottom_type(); +} + +//------------------------------Identity--------------------------------------- +Node *ConvI2FNode::Identity(PhaseTransform *phase) { + // Remove ConvI2F->ConvF2I->ConvI2F sequences. + if( in(1) ->Opcode() == Op_ConvF2I && + in(1)->in(1)->Opcode() == Op_ConvI2F ) + return in(1)->in(1); + return this; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvI2LNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + const TypeInt *ti = t->is_int(); + const Type* tl = TypeLong::make(ti->_lo, ti->_hi, ti->_widen); + // Join my declared type against my incoming type. + tl = tl->filter(_type); + return tl; +} + +#ifdef _LP64 +static inline bool long_ranges_overlap(jlong lo1, jlong hi1, + jlong lo2, jlong hi2) { + // Two ranges overlap iff one range's low point falls in the other range. + return (lo2 <= lo1 && lo1 <= hi2) || (lo1 <= lo2 && lo2 <= hi1); +} +#endif + +//------------------------------Ideal------------------------------------------ +Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { + const TypeLong* this_type = this->type()->is_long(); + Node* this_changed = NULL; + + // If _major_progress, then more loop optimizations follow. Do NOT + // remove this node's type assertion until no more loop ops can happen. + // The progress bit is set in the major loop optimizations THEN comes the + // call to IterGVN and any chance of hitting this code. Cf. Opaque1Node. + if (can_reshape && !phase->C->major_progress()) { + const TypeInt* in_type = phase->type(in(1))->isa_int(); + if (in_type != NULL && this_type != NULL && + (in_type->_lo != this_type->_lo || + in_type->_hi != this_type->_hi)) { + // Although this WORSENS the type, it increases GVN opportunities, + // because I2L nodes with the same input will common up, regardless + // of slightly differing type assertions. Such slight differences + // arise routinely as a result of loop unrolling, so this is a + // post-unrolling graph cleanup. Choose a type which depends only + // on my input. (Exception: Keep a range assertion of >=0 or <0.) + jlong lo1 = this_type->_lo; + jlong hi1 = this_type->_hi; + int w1 = this_type->_widen; + if (lo1 != (jint)lo1 || + hi1 != (jint)hi1 || + lo1 > hi1) { + // Overflow leads to wraparound, wraparound leads to range saturation. + lo1 = min_jint; hi1 = max_jint; + } else if (lo1 >= 0) { + // Keep a range assertion of >=0. + lo1 = 0; hi1 = max_jint; + } else if (hi1 < 0) { + // Keep a range assertion of <0. + lo1 = min_jint; hi1 = -1; + } else { + lo1 = min_jint; hi1 = max_jint; + } + const TypeLong* wtype = TypeLong::make(MAX2((jlong)in_type->_lo, lo1), + MIN2((jlong)in_type->_hi, hi1), + MAX2((int)in_type->_widen, w1)); + if (wtype != type()) { + set_type(wtype); + // Note: this_type still has old type value, for the logic below. + this_changed = this; + } + } + } + +#ifdef _LP64 + // Convert ConvI2L(AddI(x, y)) to AddL(ConvI2L(x), ConvI2L(y)) , + // but only if x and y have subranges that cannot cause 32-bit overflow, + // under the assumption that x+y is in my own subrange this->type(). + + // This assumption is based on a constraint (i.e., type assertion) + // established in Parse::array_addressing or perhaps elsewhere. + // This constraint has been adjoined to the "natural" type of + // the incoming argument in(0). We know (because of runtime + // checks) - that the result value I2L(x+y) is in the joined range. + // Hence we can restrict the incoming terms (x, y) to values such + // that their sum also lands in that range. + + // This optimization is useful only on 64-bit systems, where we hope + // the addition will end up subsumed in an addressing mode. + // It is necessary to do this when optimizing an unrolled array + // copy loop such as x[i++] = y[i++]. + + // On 32-bit systems, it's better to perform as much 32-bit math as + // possible before the I2L conversion, because 32-bit math is cheaper. + // There's no common reason to "leak" a constant offset through the I2L. + // Addressing arithmetic will not absorb it as part of a 64-bit AddL. + + Node* z = in(1); + int op = z->Opcode(); + if (op == Op_AddI || op == Op_SubI) { + Node* x = z->in(1); + Node* y = z->in(2); + assert (x != z && y != z, "dead loop in ConvI2LNode::Ideal"); + if (phase->type(x) == Type::TOP) return this_changed; + if (phase->type(y) == Type::TOP) return this_changed; + const TypeInt* tx = phase->type(x)->is_int(); + const TypeInt* ty = phase->type(y)->is_int(); + const TypeLong* tz = this_type; + jlong xlo = tx->_lo; + jlong xhi = tx->_hi; + jlong ylo = ty->_lo; + jlong yhi = ty->_hi; + jlong zlo = tz->_lo; + jlong zhi = tz->_hi; + jlong vbit = CONST64(1) << BitsPerInt; + int widen = MAX2(tx->_widen, ty->_widen); + if (op == Op_SubI) { + jlong ylo0 = ylo; + ylo = -yhi; + yhi = -ylo0; + } + // See if x+y can cause positive overflow into z+2**32 + if (long_ranges_overlap(xlo+ylo, xhi+yhi, zlo+vbit, zhi+vbit)) { + return this_changed; + } + // See if x+y can cause negative overflow into z-2**32 + if (long_ranges_overlap(xlo+ylo, xhi+yhi, zlo-vbit, zhi-vbit)) { + return this_changed; + } + // Now it's always safe to assume x+y does not overflow. + // This is true even if some pairs x,y might cause overflow, as long + // as that overflow value cannot fall into [zlo,zhi]. + + // Confident that the arithmetic is "as if infinite precision", + // we can now use z's range to put constraints on those of x and y. + // The "natural" range of x [xlo,xhi] can perhaps be narrowed to a + // more "restricted" range by intersecting [xlo,xhi] with the + // range obtained by subtracting y's range from the asserted range + // of the I2L conversion. Here's the interval arithmetic algebra: + // x == z-y == [zlo,zhi]-[ylo,yhi] == [zlo,zhi]+[-yhi,-ylo] + // => x in [zlo-yhi, zhi-ylo] + // => x in [zlo-yhi, zhi-ylo] INTERSECT [xlo,xhi] + // => x in [xlo MAX zlo-yhi, xhi MIN zhi-ylo] + jlong rxlo = MAX2(xlo, zlo - yhi); + jlong rxhi = MIN2(xhi, zhi - ylo); + // And similarly, x changing place with y: + jlong rylo = MAX2(ylo, zlo - xhi); + jlong ryhi = MIN2(yhi, zhi - xlo); + if (rxlo > rxhi || rylo > ryhi) { + return this_changed; // x or y is dying; don't mess w/ it + } + if (op == Op_SubI) { + jlong rylo0 = rylo; + rylo = -ryhi; + ryhi = -rylo0; + } + + Node* cx = phase->transform( new (phase->C, 2) ConvI2LNode(x, TypeLong::make(rxlo, rxhi, widen)) ); + Node* cy = phase->transform( new (phase->C, 2) ConvI2LNode(y, TypeLong::make(rylo, ryhi, widen)) ); + switch (op) { + case Op_AddI: return new (phase->C, 3) AddLNode(cx, cy); + case Op_SubI: return new (phase->C, 3) SubLNode(cx, cy); + default: ShouldNotReachHere(); + } + } +#endif //_LP64 + + return this_changed; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvL2DNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + const TypeLong *tl = t->is_long(); + if( tl->is_con() ) return TypeD::make( (double)tl->get_con() ); + return bottom_type(); +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ConvL2FNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + const TypeLong *tl = t->is_long(); + if( tl->is_con() ) return TypeF::make( (float)tl->get_con() ); + return bottom_type(); +} + +//============================================================================= +//----------------------------Identity----------------------------------------- +Node *ConvL2INode::Identity( PhaseTransform *phase ) { + // Convert L2I(I2L(x)) => x + if (in(1)->Opcode() == Op_ConvI2L) return in(1)->in(1); + return this; +} + +//------------------------------Value------------------------------------------ +const Type *ConvL2INode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + const TypeLong *tl = t->is_long(); + if (tl->is_con()) + // Easy case. + return TypeInt::make((jint)tl->get_con()); + return bottom_type(); +} + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. +// Blow off prior masking to int +Node *ConvL2INode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node *andl = in(1); + uint andl_op = andl->Opcode(); + if( andl_op == Op_AndL ) { + // Blow off prior masking to int + if( phase->type(andl->in(2)) == TypeLong::make( 0xFFFFFFFF ) ) { + set_req(1,andl->in(1)); + return this; + } + } + + // Swap with a prior add: convL2I(addL(x,y)) ==> addI(convL2I(x),convL2I(y)) + // This replaces an 'AddL' with an 'AddI'. + if( andl_op == Op_AddL ) { + // Don't do this for nodes which have more than one user since + // we'll end up computing the long add anyway. + if (andl->outcnt() > 1) return NULL; + + Node* x = andl->in(1); + Node* y = andl->in(2); + assert( x != andl && y != andl, "dead loop in ConvL2INode::Ideal" ); + if (phase->type(x) == Type::TOP) return NULL; + if (phase->type(y) == Type::TOP) return NULL; + Node *add1 = phase->transform(new (phase->C, 2) ConvL2INode(x)); + Node *add2 = phase->transform(new (phase->C, 2) ConvL2INode(y)); + return new (phase->C, 3) AddINode(add1,add2); + } + + // Fold up with a prior LoadL: LoadL->ConvL2I ==> LoadI + // Requires we understand the 'endianess' of Longs. + if( andl_op == Op_LoadL ) { + Node *adr = andl->in(MemNode::Address); + // VM_LITTLE_ENDIAN is #defined appropriately in the Makefiles +#ifndef VM_LITTLE_ENDIAN + // The transformation can cause problems on BIG_ENDIAN architectures + // where the jint is not the same address as the jlong. Specifically, we + // will fail to insert an anti-dependence in GCM between the LoadI and a + // subsequent StoreL because different memory offsets provoke + // flatten_alias_type() into indicating two different types. See bug + // 4755222. + + // Node *base = adr->is_AddP() ? adr->in(AddPNode::Base) : adr; + // adr = phase->transform( new (phase->C, 4) AddPNode(base,adr,phase->MakeConX(sizeof(jint)))); + return NULL; +#else + if (phase->C->alias_type(andl->adr_type())->is_volatile()) { + // Picking up the low half by itself bypasses the atomic load and we could + // end up with more than one non-atomic load. See bugs 4432655 and 4526490. + // We could go to the trouble of iterating over andl's output edges and + // punting only if there's more than one real use, but we don't bother. + return NULL; + } + return new (phase->C, 3) LoadINode(andl->in(MemNode::Control),andl->in(MemNode::Memory),adr,((LoadLNode*)andl)->raw_adr_type()); +#endif + } + + return NULL; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *CastX2PNode::Value( PhaseTransform *phase ) const { + const Type* t = phase->type(in(1)); + if (t->base() == Type_X && t->singleton()) { + uintptr_t bits = (uintptr_t) t->is_intptr_t()->get_con(); + if (bits == 0) return TypePtr::NULL_PTR; + return TypeRawPtr::make((address) bits); + } + return CastX2PNode::bottom_type(); +} + +//------------------------------Idealize--------------------------------------- +static inline bool fits_in_int(const Type* t, bool but_not_min_int = false) { + if (t == Type::TOP) return false; + const TypeX* tl = t->is_intptr_t(); + jint lo = min_jint; + jint hi = max_jint; + if (but_not_min_int) ++lo; // caller wants to negate the value w/o overflow + return (tl->_lo >= lo) && (tl->_hi <= hi); +} + +static inline Node* addP_of_X2P(PhaseGVN *phase, + Node* base, + Node* dispX, + bool negate = false) { + if (negate) { + dispX = new (phase->C, 3) SubXNode(phase->MakeConX(0), phase->transform(dispX)); + } + return new (phase->C, 4) AddPNode(phase->C->top(), + phase->transform(new (phase->C, 2) CastX2PNode(base)), + phase->transform(dispX)); +} + +Node *CastX2PNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // convert CastX2P(AddX(x, y)) to AddP(CastX2P(x), y) if y fits in an int + int op = in(1)->Opcode(); + Node* x; + Node* y; + switch (op) { + case Op_SubX: + x = in(1)->in(1); + y = in(1)->in(2); + if (fits_in_int(phase->type(y), true)) { + return addP_of_X2P(phase, x, y, true); + } + break; + case Op_AddX: + x = in(1)->in(1); + y = in(1)->in(2); + if (fits_in_int(phase->type(y))) { + return addP_of_X2P(phase, x, y); + } + if (fits_in_int(phase->type(x))) { + return addP_of_X2P(phase, y, x); + } + break; + } + return NULL; +} + +//------------------------------Identity--------------------------------------- +Node *CastX2PNode::Identity( PhaseTransform *phase ) { + if (in(1)->Opcode() == Op_CastP2X) return in(1)->in(1); + return this; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *CastP2XNode::Value( PhaseTransform *phase ) const { + const Type* t = phase->type(in(1)); + if (t->base() == Type::RawPtr && t->singleton()) { + uintptr_t bits = (uintptr_t) t->is_rawptr()->get_con(); + return TypeX::make(bits); + } + return CastP2XNode::bottom_type(); +} + +Node *CastP2XNode::Ideal(PhaseGVN *phase, bool can_reshape) { + return (in(0) && remove_dead_region(phase, can_reshape)) ? this : NULL; +} + +//------------------------------Identity--------------------------------------- +Node *CastP2XNode::Identity( PhaseTransform *phase ) { + if (in(1)->Opcode() == Op_CastX2P) return in(1)->in(1); + return this; +} + + +//============================================================================= +//------------------------------Identity--------------------------------------- +// Remove redundant roundings +Node *RoundFloatNode::Identity( PhaseTransform *phase ) { + assert(Matcher::strict_fp_requires_explicit_rounding, "should only generate for Intel"); + // Do not round constants + if (phase->type(in(1))->base() == Type::FloatCon) return in(1); + int op = in(1)->Opcode(); + // Redundant rounding + if( op == Op_RoundFloat ) return in(1); + // Already rounded + if( op == Op_Parm ) return in(1); + if( op == Op_LoadF ) return in(1); + return this; +} + +//------------------------------Value------------------------------------------ +const Type *RoundFloatNode::Value( PhaseTransform *phase ) const { + return phase->type( in(1) ); +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +// Remove redundant roundings. Incoming arguments are already rounded. +Node *RoundDoubleNode::Identity( PhaseTransform *phase ) { + assert(Matcher::strict_fp_requires_explicit_rounding, "should only generate for Intel"); + // Do not round constants + if (phase->type(in(1))->base() == Type::DoubleCon) return in(1); + int op = in(1)->Opcode(); + // Redundant rounding + if( op == Op_RoundDouble ) return in(1); + // Already rounded + if( op == Op_Parm ) return in(1); + if( op == Op_LoadD ) return in(1); + if( op == Op_ConvF2D ) return in(1); + if( op == Op_ConvI2D ) return in(1); + return this; +} + +//------------------------------Value------------------------------------------ +const Type *RoundDoubleNode::Value( PhaseTransform *phase ) const { + return phase->type( in(1) ); +} + + +//============================================================================= +// Do not allow value-numbering +uint Opaque1Node::hash() const { return NO_HASH; } +uint Opaque1Node::cmp( const Node &n ) const { + return (&n == this); // Always fail except on self +} + +//------------------------------Identity--------------------------------------- +// If _major_progress, then more loop optimizations follow. Do NOT remove +// the opaque Node until no more loop ops can happen. Note the timing of +// _major_progress; it's set in the major loop optimizations THEN comes the +// call to IterGVN and any chance of hitting this code. Hence there's no +// phase-ordering problem with stripping Opaque1 in IGVN followed by some +// more loop optimizations that require it. +Node *Opaque1Node::Identity( PhaseTransform *phase ) { + return phase->C->major_progress() ? this : in(1); +} + +//============================================================================= +// A node to prevent unwanted optimizations. Allows constant folding. Stops +// value-numbering, most Ideal calls or Identity functions. This Node is +// specifically designed to prevent the pre-increment value of a loop trip +// counter from being live out of the bottom of the loop (hence causing the +// pre- and post-increment values both being live and thus requiring an extra +// temp register and an extra move). If we "accidentally" optimize through +// this kind of a Node, we'll get slightly pessimal, but correct, code. Thus +// it's OK to be slightly sloppy on optimizations here. + +// Do not allow value-numbering +uint Opaque2Node::hash() const { return NO_HASH; } +uint Opaque2Node::cmp( const Node &n ) const { + return (&n == this); // Always fail except on self +} + + +//------------------------------Value------------------------------------------ +const Type *MoveL2DNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + const TypeLong *tl = t->is_long(); + if( !tl->is_con() ) return bottom_type(); + JavaValue v; + v.set_jlong(tl->get_con()); + return TypeD::make( v.get_jdouble() ); +} + +//------------------------------Value------------------------------------------ +const Type *MoveI2FNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + const TypeInt *ti = t->is_int(); + if( !ti->is_con() ) return bottom_type(); + JavaValue v; + v.set_jint(ti->get_con()); + return TypeF::make( v.get_jfloat() ); +} + +//------------------------------Value------------------------------------------ +const Type *MoveF2INode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + if( t == Type::FLOAT ) return TypeInt::INT; + const TypeF *tf = t->is_float_constant(); + JavaValue v; + v.set_jfloat(tf->getf()); + return TypeInt::make( v.get_jint() ); +} + +//------------------------------Value------------------------------------------ +const Type *MoveD2LNode::Value( PhaseTransform *phase ) const { + const Type *t = phase->type( in(1) ); + if( t == Type::TOP ) return Type::TOP; + if( t == Type::DOUBLE ) return TypeLong::LONG; + const TypeD *td = t->is_double_constant(); + JavaValue v; + v.set_jdouble(td->getd()); + return TypeLong::make( v.get_jlong() ); +} diff --git a/hotspot/src/share/vm/opto/connode.hpp b/hotspot/src/share/vm/opto/connode.hpp new file mode 100644 index 00000000000..1c1b96a19a0 --- /dev/null +++ b/hotspot/src/share/vm/opto/connode.hpp @@ -0,0 +1,578 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class PhaseTransform; +class MachNode; + +//------------------------------ConNode---------------------------------------- +// Simple constants +class ConNode : public TypeNode { +public: + ConNode( const Type *t ) : TypeNode(t,1) { + init_req(0, (Node*)Compile::current()->root()); + init_flags(Flag_is_Con); + } + virtual int Opcode() const; + virtual uint hash() const; + virtual const RegMask &out_RegMask() const { return RegMask::Empty; } + virtual const RegMask &in_RegMask(uint) const { return RegMask::Empty; } + + // Polymorphic factory method: + static ConNode* make( Compile* C, const Type *t ); +}; + +//------------------------------ConINode--------------------------------------- +// Simple integer constants +class ConINode : public ConNode { +public: + ConINode( const TypeInt *t ) : ConNode(t) {} + virtual int Opcode() const; + + // Factory method: + static ConINode* make( Compile* C, int con ) { + return new (C, 1) ConINode( TypeInt::make(con) ); + } + +}; + +//------------------------------ConPNode--------------------------------------- +// Simple pointer constants +class ConPNode : public ConNode { +public: + ConPNode( const TypePtr *t ) : ConNode(t) {} + virtual int Opcode() const; + + // Factory methods: + static ConPNode* make( Compile *C ,address con ) { + if (con == NULL) + return new (C, 1) ConPNode( TypePtr::NULL_PTR ) ; + else + return new (C, 1) ConPNode( TypeRawPtr::make(con) ); + } + + static ConPNode* make( Compile *C, ciObject* con ) { + return new (C, 1) ConPNode( TypeOopPtr::make_from_constant(con) ); + } + +}; + + +//------------------------------ConLNode--------------------------------------- +// Simple long constants +class ConLNode : public ConNode { +public: + ConLNode( const TypeLong *t ) : ConNode(t) {} + virtual int Opcode() const; + + // Factory method: + static ConLNode* make( Compile *C ,jlong con ) { + return new (C, 1) ConLNode( TypeLong::make(con) ); + } + +}; + +//------------------------------ConFNode--------------------------------------- +// Simple float constants +class ConFNode : public ConNode { +public: + ConFNode( const TypeF *t ) : ConNode(t) {} + virtual int Opcode() const; + + // Factory method: + static ConFNode* make( Compile *C, float con ) { + return new (C, 1) ConFNode( TypeF::make(con) ); + } + +}; + +//------------------------------ConDNode--------------------------------------- +// Simple double constants +class ConDNode : public ConNode { +public: + ConDNode( const TypeD *t ) : ConNode(t) {} + virtual int Opcode() const; + + // Factory method: + static ConDNode* make( Compile *C, double con ) { + return new (C, 1) ConDNode( TypeD::make(con) ); + } + +}; + +//------------------------------BinaryNode------------------------------------- +// Place holder for the 2 conditional inputs to a CMove. CMove needs 4 +// inputs: the Bool (for the lt/gt/eq/ne bits), the flags (result of some +// compare), and the 2 values to select between. The Matcher requires a +// binary tree so we break it down like this: +// (CMove (Binary bol cmp) (Binary src1 src2)) +class BinaryNode : public Node { +public: + BinaryNode( Node *n1, Node *n2 ) : Node(0,n1,n2) { } + virtual int Opcode() const; + virtual uint ideal_reg() const { return 0; } +}; + +//------------------------------CMoveNode-------------------------------------- +// Conditional move +class CMoveNode : public TypeNode { +public: + enum { Control, // When is it safe to do this cmove? + Condition, // Condition controlling the cmove + IfFalse, // Value if condition is false + IfTrue }; // Value if condition is true + CMoveNode( Node *bol, Node *left, Node *right, const Type *t ) : TypeNode(t,4) + { + init_class_id(Class_CMove); + // all inputs are nullified in Node::Node(int) + // init_req(Control,NULL); + init_req(Condition,bol); + init_req(IfFalse,left); + init_req(IfTrue,right); + } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + static CMoveNode *make( Compile *C, Node *c, Node *bol, Node *left, Node *right, const Type *t ); + // Helper function to spot cmove graph shapes + static Node *is_cmove_id( PhaseTransform *phase, Node *cmp, Node *t, Node *f, BoolNode *b ); +}; + +//------------------------------CMoveDNode------------------------------------- +class CMoveDNode : public CMoveNode { +public: + CMoveDNode( Node *bol, Node *left, Node *right, const Type* t) : CMoveNode(bol,left,right,t){} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); +}; + +//------------------------------CMoveFNode------------------------------------- +class CMoveFNode : public CMoveNode { +public: + CMoveFNode( Node *bol, Node *left, Node *right, const Type* t ) : CMoveNode(bol,left,right,t) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); +}; + +//------------------------------CMoveINode------------------------------------- +class CMoveINode : public CMoveNode { +public: + CMoveINode( Node *bol, Node *left, Node *right, const TypeInt *ti ) : CMoveNode(bol,left,right,ti){} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); +}; + +//------------------------------CMoveLNode------------------------------------- +class CMoveLNode : public CMoveNode { +public: + CMoveLNode(Node *bol, Node *left, Node *right, const TypeLong *tl ) : CMoveNode(bol,left,right,tl){} + virtual int Opcode() const; +}; + +//------------------------------CMovePNode------------------------------------- +class CMovePNode : public CMoveNode { +public: + CMovePNode( Node *c, Node *bol, Node *left, Node *right, const TypePtr* t ) : CMoveNode(bol,left,right,t) { init_req(Control,c); } + virtual int Opcode() const; +}; + +//------------------------------ConstraintCastNode------------------------------------- +// cast to a different range +class ConstraintCastNode: public TypeNode { +public: + ConstraintCastNode (Node *n, const Type *t ): TypeNode(t,2) { + init_class_id(Class_ConstraintCast); + init_req(1, n); + } + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual int Opcode() const; + virtual uint ideal_reg() const = 0; + virtual Node *Ideal_DU_postCCP( PhaseCCP * ); +}; + +//------------------------------CastIINode------------------------------------- +// cast integer to integer (different range) +class CastIINode: public ConstraintCastNode { +public: + CastIINode (Node *n, const Type *t ): ConstraintCastNode(n,t) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------CastPPNode------------------------------------- +// cast pointer to pointer (different type) +class CastPPNode: public ConstraintCastNode { +public: + CastPPNode (Node *n, const Type *t ): ConstraintCastNode(n, t) { + // Only CastPP is safe. CastII can cause optimizer loops. + init_flags(Flag_is_dead_loop_safe); + } + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegP; } + virtual Node *Ideal_DU_postCCP( PhaseCCP * ); +}; + +//------------------------------CheckCastPPNode-------------------------------- +// for _checkcast, cast pointer to pointer (different type), without JOIN, +class CheckCastPPNode: public TypeNode { +public: + CheckCastPPNode( Node *c, Node *n, const Type *t ) : TypeNode(t,2) { + init_class_id(Class_CheckCastPP); + init_flags(Flag_is_dead_loop_safe); + init_req(0, c); + init_req(1, n); + } + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegP; } + // No longer remove CheckCast after CCP as it gives me a place to hang + // the proper address type - which is required to compute anti-deps. + //virtual Node *Ideal_DU_postCCP( PhaseCCP * ); +}; + +//------------------------------Conv2BNode------------------------------------- +// Convert int/pointer to a Boolean. Map zero to zero, all else to 1. +class Conv2BNode : public Node { +public: + Conv2BNode( Node *i ) : Node(0,i) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::BOOL; } + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual uint ideal_reg() const { return Op_RegI; } +}; + +// The conversions operations are all Alpha sorted. Please keep it that way! +//------------------------------ConvD2FNode------------------------------------ +// Convert double to float +class ConvD2FNode : public Node { +public: + ConvD2FNode( Node *in1 ) : Node(0,in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------ConvD2INode------------------------------------ +// Convert Double to Integer +class ConvD2INode : public Node { +public: + ConvD2INode( Node *in1 ) : Node(0,in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------ConvD2LNode------------------------------------ +// Convert Double to Long +class ConvD2LNode : public Node { +public: + ConvD2LNode( Node *dbl ) : Node(0,dbl) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------ConvF2DNode------------------------------------ +// Convert Float to a Double. +class ConvF2DNode : public Node { +public: + ConvF2DNode( Node *in1 ) : Node(0,in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual uint ideal_reg() const { return Op_RegD; } +}; + +//------------------------------ConvF2INode------------------------------------ +// Convert float to integer +class ConvF2INode : public Node { +public: + ConvF2INode( Node *in1 ) : Node(0,in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------ConvF2LNode------------------------------------ +// Convert float to long +class ConvF2LNode : public Node { +public: + ConvF2LNode( Node *in1 ) : Node(0,in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------ConvI2DNode------------------------------------ +// Convert Integer to Double +class ConvI2DNode : public Node { +public: + ConvI2DNode( Node *in1 ) : Node(0,in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual uint ideal_reg() const { return Op_RegD; } +}; + +//------------------------------ConvI2FNode------------------------------------ +// Convert Integer to Float +class ConvI2FNode : public Node { +public: + ConvI2FNode( Node *in1 ) : Node(0,in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------ConvI2LNode------------------------------------ +// Convert integer to long +class ConvI2LNode : public TypeNode { +public: + ConvI2LNode(Node *in1, const TypeLong* t = TypeLong::INT) + : TypeNode(t, 2) + { init_req(1, in1); } + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------ConvL2DNode------------------------------------ +// Convert Long to Double +class ConvL2DNode : public Node { +public: + ConvL2DNode( Node *in1 ) : Node(0,in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual uint ideal_reg() const { return Op_RegD; } +}; + +//------------------------------ConvL2FNode------------------------------------ +// Convert Long to Float +class ConvL2FNode : public Node { +public: + ConvL2FNode( Node *in1 ) : Node(0,in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------ConvL2INode------------------------------------ +// Convert long to integer +class ConvL2INode : public Node { +public: + ConvL2INode( Node *in1 ) : Node(0,in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------CastX2PNode------------------------------------- +// convert a machine-pointer-sized integer to a raw pointer +class CastX2PNode : public Node { +public: + CastX2PNode( Node *n ) : Node(NULL, n) {} + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node *Identity( PhaseTransform *phase ); + virtual uint ideal_reg() const { return Op_RegP; } + virtual const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } +}; + +//------------------------------CastP2XNode------------------------------------- +// Used in both 32-bit and 64-bit land. +// Used for card-marks and unsafe pointer math. +class CastP2XNode : public Node { +public: + CastP2XNode( Node *ctrl, Node *n ) : Node(ctrl, n) {} + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node *Identity( PhaseTransform *phase ); + virtual uint ideal_reg() const { return Op_RegX; } + virtual const Type *bottom_type() const { return TypeX_X; } + // Return false to keep node from moving away from an associated card mark. + virtual bool depends_only_on_test() const { return false; } +}; + +//------------------------------MemMoveNode------------------------------------ +// Memory to memory move. Inserted very late, after allocation. +class MemMoveNode : public Node { +public: + MemMoveNode( Node *dst, Node *src ) : Node(0,dst,src) {} + virtual int Opcode() const; +}; + +//------------------------------ThreadLocalNode-------------------------------- +// Ideal Node which returns the base of ThreadLocalStorage. +class ThreadLocalNode : public Node { +public: + ThreadLocalNode( ) : Node((Node*)Compile::current()->root()) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeRawPtr::BOTTOM;} + virtual uint ideal_reg() const { return Op_RegP; } +}; + +//------------------------------LoadReturnPCNode------------------------------- +class LoadReturnPCNode: public Node { +public: + LoadReturnPCNode(Node *c) : Node(c) { } + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegP; } +}; + + +//-----------------------------RoundFloatNode---------------------------------- +class RoundFloatNode: public Node { +public: + RoundFloatNode(Node* c, Node *in1): Node(c, in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *Value( PhaseTransform *phase ) const; +}; + + +//-----------------------------RoundDoubleNode--------------------------------- +class RoundDoubleNode: public Node { +public: + RoundDoubleNode(Node* c, Node *in1): Node(c, in1) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *Value( PhaseTransform *phase ) const; +}; + +//------------------------------Opaque1Node------------------------------------ +// A node to prevent unwanted optimizations. Allows constant folding. +// Stops value-numbering, Ideal calls or Identity functions. +class Opaque1Node : public Node { + virtual uint hash() const ; // { return NO_HASH; } + virtual uint cmp( const Node &n ) const; +public: + Opaque1Node( Node *n ) : Node(0,n) {} + // Special version for the pre-loop to hold the original loop limit + // which is consumed by range check elimination. + Opaque1Node( Node *n, Node* orig_limit ) : Node(0,n,orig_limit) {} + Node* original_loop_limit() { return req()==3 ? in(2) : NULL; } + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual Node *Identity( PhaseTransform *phase ); +}; + +//------------------------------Opaque2Node------------------------------------ +// A node to prevent unwanted optimizations. Allows constant folding. Stops +// value-numbering, most Ideal calls or Identity functions. This Node is +// specifically designed to prevent the pre-increment value of a loop trip +// counter from being live out of the bottom of the loop (hence causing the +// pre- and post-increment values both being live and thus requiring an extra +// temp register and an extra move). If we "accidentally" optimize through +// this kind of a Node, we'll get slightly pessimal, but correct, code. Thus +// it's OK to be slightly sloppy on optimizations here. +class Opaque2Node : public Node { + virtual uint hash() const ; // { return NO_HASH; } + virtual uint cmp( const Node &n ) const; +public: + Opaque2Node( Node *n ) : Node(0,n) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::INT; } +}; + +//----------------------PartialSubtypeCheckNode-------------------------------- +// The 2nd slow-half of a subtype check. Scan the subklass's 2ndary superklass +// array for an instance of the superklass. Set a hidden internal cache on a +// hit (cache is checked with exposed code in gen_subtype_check()). Return +// not zero for a miss or zero for a hit. +class PartialSubtypeCheckNode : public Node { +public: + PartialSubtypeCheckNode(Node* c, Node* sub, Node* super) : Node(c,sub,super) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } + virtual uint ideal_reg() const { return Op_RegP; } +}; + +// +class MoveI2FNode : public Node { + public: + MoveI2FNode( Node *value ) : Node(0,value) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } + virtual const Type* Value( PhaseTransform *phase ) const; +}; + +class MoveL2DNode : public Node { + public: + MoveL2DNode( Node *value ) : Node(0,value) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } + virtual const Type* Value( PhaseTransform *phase ) const; +}; + +class MoveF2INode : public Node { + public: + MoveF2INode( Node *value ) : Node(0,value) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } + virtual const Type* Value( PhaseTransform *phase ) const; +}; + +class MoveD2LNode : public Node { + public: + MoveD2LNode( Node *value ) : Node(0,value) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } + virtual const Type* Value( PhaseTransform *phase ) const; +}; diff --git a/hotspot/src/share/vm/opto/divnode.cpp b/hotspot/src/share/vm/opto/divnode.cpp new file mode 100644 index 00000000000..5443495ff95 --- /dev/null +++ b/hotspot/src/share/vm/opto/divnode.cpp @@ -0,0 +1,1031 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_divnode.cpp.incl" +#include + +// Implement the integer constant divide -> long multiply transform found in +// "Division by Invariant Integers using Multiplication" +// by Granlund and Montgomery +static Node *transform_int_divide_to_long_multiply( PhaseGVN *phase, Node *dividend, int divisor ) { + + // Check for invalid divisors + assert( divisor != 0 && divisor != min_jint && divisor != 1, + "bad divisor for transforming to long multiply" ); + + // Compute l = ceiling(log2(d)) + // presumes d is more likely small + bool d_pos = divisor >= 0; + int d = d_pos ? divisor : -divisor; + unsigned ud = (unsigned)d; + const int N = 32; + int l = log2_intptr(d-1)+1; + int sh_post = l; + + const uint64_t U1 = (uint64_t)1; + + // Cliff pointed out how to prevent overflow (from the paper) + uint64_t m_low = (((U1 << l) - ud) << N) / ud + (U1 << N); + uint64_t m_high = ((((U1 << l) - ud) << N) + (U1 << (l+1))) / ud + (U1 << N); + + // Reduce to lowest terms + for ( ; sh_post > 0; sh_post-- ) { + uint64_t m_low_1 = m_low >> 1; + uint64_t m_high_1 = m_high >> 1; + if ( m_low_1 >= m_high_1 ) + break; + m_low = m_low_1; + m_high = m_high_1; + } + + // Result + Node *q; + + // division by +/- 1 + if (d == 1) { + // Filtered out as identity above + if (d_pos) + return NULL; + + // Just negate the value + else { + q = new (phase->C, 3) SubINode(phase->intcon(0), dividend); + } + } + + // division by +/- a power of 2 + else if ( is_power_of_2(d) ) { + + // See if we can simply do a shift without rounding + bool needs_rounding = true; + const Type *dt = phase->type(dividend); + const TypeInt *dti = dt->isa_int(); + + // we don't need to round a positive dividend + if (dti && dti->_lo >= 0) + needs_rounding = false; + + // An AND mask of sufficient size clears the low bits and + // I can avoid rounding. + else if( dividend->Opcode() == Op_AndI ) { + const TypeInt *andconi = phase->type( dividend->in(2) )->isa_int(); + if( andconi && andconi->is_con(-d) ) { + dividend = dividend->in(1); + needs_rounding = false; + } + } + + // Add rounding to the shift to handle the sign bit + if( needs_rounding ) { + Node *t1 = phase->transform(new (phase->C, 3) RShiftINode(dividend, phase->intcon(l - 1))); + Node *t2 = phase->transform(new (phase->C, 3) URShiftINode(t1, phase->intcon(N - l))); + dividend = phase->transform(new (phase->C, 3) AddINode(dividend, t2)); + } + + q = new (phase->C, 3) RShiftINode(dividend, phase->intcon(l)); + + if (!d_pos) + q = new (phase->C, 3) SubINode(phase->intcon(0), phase->transform(q)); + } + + // division by something else + else if (m_high < (U1 << (N-1))) { + Node *t1 = phase->transform(new (phase->C, 2) ConvI2LNode(dividend)); + Node *t2 = phase->transform(new (phase->C, 3) MulLNode(t1, phase->longcon(m_high))); + Node *t3 = phase->transform(new (phase->C, 3) RShiftLNode(t2, phase->intcon(sh_post+N))); + Node *t4 = phase->transform(new (phase->C, 2) ConvL2INode(t3)); + Node *t5 = phase->transform(new (phase->C, 3) RShiftINode(dividend, phase->intcon(N-1))); + + q = new (phase->C, 3) SubINode(d_pos ? t4 : t5, d_pos ? t5 : t4); + } + + // This handles that case where m_high is >= 2**(N-1). In that case, + // we subtract out 2**N from the multiply and add it in later as + // "dividend" in the equation (t5). This case computes the same result + // as the immediately preceeding case, save that rounding and overflow + // are accounted for. + else { + Node *t1 = phase->transform(new (phase->C, 2) ConvI2LNode(dividend)); + Node *t2 = phase->transform(new (phase->C, 3) MulLNode(t1, phase->longcon(m_high - (U1 << N)))); + Node *t3 = phase->transform(new (phase->C, 3) RShiftLNode(t2, phase->intcon(N))); + Node *t4 = phase->transform(new (phase->C, 2) ConvL2INode(t3)); + Node *t5 = phase->transform(new (phase->C, 3) AddINode(dividend, t4)); + Node *t6 = phase->transform(new (phase->C, 3) RShiftINode(t5, phase->intcon(sh_post))); + Node *t7 = phase->transform(new (phase->C, 3) RShiftINode(dividend, phase->intcon(N-1))); + + q = new (phase->C, 3) SubINode(d_pos ? t6 : t7, d_pos ? t7 : t6); + } + + return (q); +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +// If the divisor is 1, we are an identity on the dividend. +Node *DivINode::Identity( PhaseTransform *phase ) { + return (phase->type( in(2) )->higher_equal(TypeInt::ONE)) ? in(1) : this; +} + +//------------------------------Idealize--------------------------------------- +// Divides can be changed to multiplies and/or shifts +Node *DivINode::Ideal(PhaseGVN *phase, bool can_reshape) { + if (in(0) && remove_dead_region(phase, can_reshape)) return this; + + const Type *t = phase->type( in(2) ); + if( t == TypeInt::ONE ) // Identity? + return NULL; // Skip it + + const TypeInt *ti = t->isa_int(); + if( !ti ) return NULL; + if( !ti->is_con() ) return NULL; + int i = ti->get_con(); // Get divisor + + if (i == 0) return NULL; // Dividing by zero constant does not idealize + + set_req(0,NULL); // Dividing by a not-zero constant; no faulting + + // Dividing by MININT does not optimize as a power-of-2 shift. + if( i == min_jint ) return NULL; + + return transform_int_divide_to_long_multiply( phase, in(1), i ); +} + +//------------------------------Value------------------------------------------ +// A DivINode divides its inputs. The third input is a Control input, used to +// prevent hoisting the divide above an unsafe test. +const Type *DivINode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // x/x == 1 since we always generate the dynamic divisor check for 0. + if( phase->eqv( in(1), in(2) ) ) + return TypeInt::ONE; + + // Either input is BOTTOM ==> the result is the local BOTTOM + const Type *bot = bottom_type(); + if( (t1 == bot) || (t2 == bot) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return bot; + + // Divide the two numbers. We approximate. + // If divisor is a constant and not zero + const TypeInt *i1 = t1->is_int(); + const TypeInt *i2 = t2->is_int(); + int widen = MAX2(i1->_widen, i2->_widen); + + if( i2->is_con() && i2->get_con() != 0 ) { + int32 d = i2->get_con(); // Divisor + jint lo, hi; + if( d >= 0 ) { + lo = i1->_lo/d; + hi = i1->_hi/d; + } else { + if( d == -1 && i1->_lo == min_jint ) { + // 'min_jint/-1' throws arithmetic exception during compilation + lo = min_jint; + // do not support holes, 'hi' must go to either min_jint or max_jint: + // [min_jint, -10]/[-1,-1] ==> [min_jint] UNION [10,max_jint] + hi = i1->_hi == min_jint ? min_jint : max_jint; + } else { + lo = i1->_hi/d; + hi = i1->_lo/d; + } + } + return TypeInt::make(lo, hi, widen); + } + + // If the dividend is a constant + if( i1->is_con() ) { + int32 d = i1->get_con(); + if( d < 0 ) { + if( d == min_jint ) { + // (-min_jint) == min_jint == (min_jint / -1) + return TypeInt::make(min_jint, max_jint/2 + 1, widen); + } else { + return TypeInt::make(d, -d, widen); + } + } + return TypeInt::make(-d, d, widen); + } + + // Otherwise we give up all hope + return TypeInt::INT; +} + + +//============================================================================= +//------------------------------Identity--------------------------------------- +// If the divisor is 1, we are an identity on the dividend. +Node *DivLNode::Identity( PhaseTransform *phase ) { + return (phase->type( in(2) )->higher_equal(TypeLong::ONE)) ? in(1) : this; +} + +//------------------------------Idealize--------------------------------------- +// Dividing by a power of 2 is a shift. +Node *DivLNode::Ideal( PhaseGVN *phase, bool can_reshape) { + if (in(0) && remove_dead_region(phase, can_reshape)) return this; + + const Type *t = phase->type( in(2) ); + if( t == TypeLong::ONE ) // Identity? + return NULL; // Skip it + + const TypeLong *ti = t->isa_long(); + if( !ti ) return NULL; + if( !ti->is_con() ) return NULL; + jlong i = ti->get_con(); // Get divisor + if( i ) set_req(0, NULL); // Dividing by a not-zero constant; no faulting + + // Dividing by MININT does not optimize as a power-of-2 shift. + if( i == min_jlong ) return NULL; + + // Check for negative power of 2 divisor, if so, negate it and set a flag + // to indicate result needs to be negated. Note that negating the dividend + // here does not work when it has the value MININT + Node *dividend = in(1); + bool negate_res = false; + if (is_power_of_2_long(-i)) { + i = -i; // Flip divisor + negate_res = true; + } + + // Check for power of 2 + if (!is_power_of_2_long(i)) // Is divisor a power of 2? + return NULL; // Not a power of 2 + + // Compute number of bits to shift + int log_i = log2_long(i); + + // See if we can simply do a shift without rounding + bool needs_rounding = true; + const Type *dt = phase->type(dividend); + const TypeLong *dtl = dt->isa_long(); + + if (dtl && dtl->_lo > 0) { + // we don't need to round a positive dividend + needs_rounding = false; + } else if( dividend->Opcode() == Op_AndL ) { + // An AND mask of sufficient size clears the low bits and + // I can avoid rounding. + const TypeLong *andconi = phase->type( dividend->in(2) )->isa_long(); + if( andconi && + andconi->is_con() && + andconi->get_con() == -i ) { + dividend = dividend->in(1); + needs_rounding = false; + } + } + + if (!needs_rounding) { + Node *result = new (phase->C, 3) RShiftLNode(dividend, phase->intcon(log_i)); + if (negate_res) { + result = phase->transform(result); + result = new (phase->C, 3) SubLNode(phase->longcon(0), result); + } + return result; + } + + // Divide-by-power-of-2 can be made into a shift, but you have to do + // more math for the rounding. You need to add 0 for positive + // numbers, and "i-1" for negative numbers. Example: i=4, so the + // shift is by 2. You need to add 3 to negative dividends and 0 to + // positive ones. So (-7+3)>>2 becomes -1, (-4+3)>>2 becomes -1, + // (-2+3)>>2 becomes 0, etc. + + // Compute 0 or -1, based on sign bit + Node *sign = phase->transform(new (phase->C, 3) RShiftLNode(dividend,phase->intcon(63))); + // Mask sign bit to the low sign bits + Node *round = phase->transform(new (phase->C, 3) AndLNode(sign,phase->longcon(i-1))); + // Round up before shifting + Node *sum = phase->transform(new (phase->C, 3) AddLNode(dividend,round)); + // Shift for division + Node *result = new (phase->C, 3) RShiftLNode(sum, phase->intcon(log_i)); + if (negate_res) { + result = phase->transform(result); + result = new (phase->C, 3) SubLNode(phase->longcon(0), result); + } + + return result; +} + +//------------------------------Value------------------------------------------ +// A DivLNode divides its inputs. The third input is a Control input, used to +// prevent hoisting the divide above an unsafe test. +const Type *DivLNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // x/x == 1 since we always generate the dynamic divisor check for 0. + if( phase->eqv( in(1), in(2) ) ) + return TypeLong::ONE; + + // Either input is BOTTOM ==> the result is the local BOTTOM + const Type *bot = bottom_type(); + if( (t1 == bot) || (t2 == bot) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return bot; + + // Divide the two numbers. We approximate. + // If divisor is a constant and not zero + const TypeLong *i1 = t1->is_long(); + const TypeLong *i2 = t2->is_long(); + int widen = MAX2(i1->_widen, i2->_widen); + + if( i2->is_con() && i2->get_con() != 0 ) { + jlong d = i2->get_con(); // Divisor + jlong lo, hi; + if( d >= 0 ) { + lo = i1->_lo/d; + hi = i1->_hi/d; + } else { + if( d == CONST64(-1) && i1->_lo == min_jlong ) { + // 'min_jlong/-1' throws arithmetic exception during compilation + lo = min_jlong; + // do not support holes, 'hi' must go to either min_jlong or max_jlong: + // [min_jlong, -10]/[-1,-1] ==> [min_jlong] UNION [10,max_jlong] + hi = i1->_hi == min_jlong ? min_jlong : max_jlong; + } else { + lo = i1->_hi/d; + hi = i1->_lo/d; + } + } + return TypeLong::make(lo, hi, widen); + } + + // If the dividend is a constant + if( i1->is_con() ) { + jlong d = i1->get_con(); + if( d < 0 ) { + if( d == min_jlong ) { + // (-min_jlong) == min_jlong == (min_jlong / -1) + return TypeLong::make(min_jlong, max_jlong/2 + 1, widen); + } else { + return TypeLong::make(d, -d, widen); + } + } + return TypeLong::make(-d, d, widen); + } + + // Otherwise we give up all hope + return TypeLong::LONG; +} + + +//============================================================================= +//------------------------------Value------------------------------------------ +// An DivFNode divides its inputs. The third input is a Control input, used to +// prevent hoisting the divide above an unsafe test. +const Type *DivFNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Either input is BOTTOM ==> the result is the local BOTTOM + const Type *bot = bottom_type(); + if( (t1 == bot) || (t2 == bot) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return bot; + + // x/x == 1, we ignore 0/0. + // Note: if t1 and t2 are zero then result is NaN (JVMS page 213) + // does not work for variables because of NaN's + if( phase->eqv( in(1), in(2) ) && t1->base() == Type::FloatCon) + if (!g_isnan(t1->getf()) && g_isfinite(t1->getf()) && t1->getf() != 0.0) // could be negative ZERO or NaN + return TypeF::ONE; + + if( t2 == TypeF::ONE ) + return t1; + + // If divisor is a constant and not zero, divide them numbers + if( t1->base() == Type::FloatCon && + t2->base() == Type::FloatCon && + t2->getf() != 0.0 ) // could be negative zero + return TypeF::make( t1->getf()/t2->getf() ); + + // If the dividend is a constant zero + // Note: if t1 and t2 are zero then result is NaN (JVMS page 213) + // Test TypeF::ZERO is not sufficient as it could be negative zero + + if( t1 == TypeF::ZERO && !g_isnan(t2->getf()) && t2->getf() != 0.0 ) + return TypeF::ZERO; + + // Otherwise we give up all hope + return Type::FLOAT; +} + +//------------------------------isA_Copy--------------------------------------- +// Dividing by self is 1. +// If the divisor is 1, we are an identity on the dividend. +Node *DivFNode::Identity( PhaseTransform *phase ) { + return (phase->type( in(2) ) == TypeF::ONE) ? in(1) : this; +} + + +//------------------------------Idealize--------------------------------------- +Node *DivFNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if (in(0) && remove_dead_region(phase, can_reshape)) return this; + + const Type *t2 = phase->type( in(2) ); + if( t2 == TypeF::ONE ) // Identity? + return NULL; // Skip it + + const TypeF *tf = t2->isa_float_constant(); + if( !tf ) return NULL; + if( tf->base() != Type::FloatCon ) return NULL; + + // Check for out of range values + if( tf->is_nan() || !tf->is_finite() ) return NULL; + + // Get the value + float f = tf->getf(); + int exp; + + // Only for special case of dividing by a power of 2 + if( frexp((double)f, &exp) != 0.5 ) return NULL; + + // Limit the range of acceptable exponents + if( exp < -126 || exp > 126 ) return NULL; + + // Compute the reciprocal + float reciprocal = ((float)1.0) / f; + + assert( frexp((double)reciprocal, &exp) == 0.5, "reciprocal should be power of 2" ); + + // return multiplication by the reciprocal + return (new (phase->C, 3) MulFNode(in(1), phase->makecon(TypeF::make(reciprocal)))); +} + +//============================================================================= +//------------------------------Value------------------------------------------ +// An DivDNode divides its inputs. The third input is a Control input, used to +// prvent hoisting the divide above an unsafe test. +const Type *DivDNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Either input is BOTTOM ==> the result is the local BOTTOM + const Type *bot = bottom_type(); + if( (t1 == bot) || (t2 == bot) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return bot; + + // x/x == 1, we ignore 0/0. + // Note: if t1 and t2 are zero then result is NaN (JVMS page 213) + // Does not work for variables because of NaN's + if( phase->eqv( in(1), in(2) ) && t1->base() == Type::DoubleCon) + if (!g_isnan(t1->getd()) && g_isfinite(t1->getd()) && t1->getd() != 0.0) // could be negative ZERO or NaN + return TypeD::ONE; + + if( t2 == TypeD::ONE ) + return t1; + + // If divisor is a constant and not zero, divide them numbers + if( t1->base() == Type::DoubleCon && + t2->base() == Type::DoubleCon && + t2->getd() != 0.0 ) // could be negative zero + return TypeD::make( t1->getd()/t2->getd() ); + + // If the dividend is a constant zero + // Note: if t1 and t2 are zero then result is NaN (JVMS page 213) + // Test TypeF::ZERO is not sufficient as it could be negative zero + if( t1 == TypeD::ZERO && !g_isnan(t2->getd()) && t2->getd() != 0.0 ) + return TypeD::ZERO; + + // Otherwise we give up all hope + return Type::DOUBLE; +} + + +//------------------------------isA_Copy--------------------------------------- +// Dividing by self is 1. +// If the divisor is 1, we are an identity on the dividend. +Node *DivDNode::Identity( PhaseTransform *phase ) { + return (phase->type( in(2) ) == TypeD::ONE) ? in(1) : this; +} + +//------------------------------Idealize--------------------------------------- +Node *DivDNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if (in(0) && remove_dead_region(phase, can_reshape)) return this; + + const Type *t2 = phase->type( in(2) ); + if( t2 == TypeD::ONE ) // Identity? + return NULL; // Skip it + + const TypeD *td = t2->isa_double_constant(); + if( !td ) return NULL; + if( td->base() != Type::DoubleCon ) return NULL; + + // Check for out of range values + if( td->is_nan() || !td->is_finite() ) return NULL; + + // Get the value + double d = td->getd(); + int exp; + + // Only for special case of dividing by a power of 2 + if( frexp(d, &exp) != 0.5 ) return NULL; + + // Limit the range of acceptable exponents + if( exp < -1021 || exp > 1022 ) return NULL; + + // Compute the reciprocal + double reciprocal = 1.0 / d; + + assert( frexp(reciprocal, &exp) == 0.5, "reciprocal should be power of 2" ); + + // return multiplication by the reciprocal + return (new (phase->C, 3) MulDNode(in(1), phase->makecon(TypeD::make(reciprocal)))); +} + +//============================================================================= +//------------------------------Idealize--------------------------------------- +Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Check for dead control input + if( remove_dead_region(phase, can_reshape) ) return this; + + // Get the modulus + const Type *t = phase->type( in(2) ); + if( t == Type::TOP ) return NULL; + const TypeInt *ti = t->is_int(); + + // Check for useless control input + // Check for excluding mod-zero case + if( in(0) && (ti->_hi < 0 || ti->_lo > 0) ) { + set_req(0, NULL); // Yank control input + return this; + } + + // See if we are MOD'ing by 2^k or 2^k-1. + if( !ti->is_con() ) return NULL; + jint con = ti->get_con(); + + Node *hook = new (phase->C, 1) Node(1); + + // First, special check for modulo 2^k-1 + if( con >= 0 && con < max_jint && is_power_of_2(con+1) ) { + uint k = exact_log2(con+1); // Extract k + + // Basic algorithm by David Detlefs. See fastmod_int.java for gory details. + static int unroll_factor[] = { 999, 999, 29, 14, 9, 7, 5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 1 /*past here we assume 1 forever*/}; + int trip_count = 1; + if( k < ARRAY_SIZE(unroll_factor)) trip_count = unroll_factor[k]; + + // If the unroll factor is not too large, and if conditional moves are + // ok, then use this case + if( trip_count <= 5 && ConditionalMoveLimit != 0 ) { + Node *x = in(1); // Value being mod'd + Node *divisor = in(2); // Also is mask + + hook->init_req(0, x); // Add a use to x to prevent him from dying + // Generate code to reduce X rapidly to nearly 2^k-1. + for( int i = 0; i < trip_count; i++ ) { + Node *xl = phase->transform( new (phase->C, 3) AndINode(x,divisor) ); + Node *xh = phase->transform( new (phase->C, 3) RShiftINode(x,phase->intcon(k)) ); // Must be signed + x = phase->transform( new (phase->C, 3) AddINode(xh,xl) ); + hook->set_req(0, x); + } + + // Generate sign-fixup code. Was original value positive? + // int hack_res = (i >= 0) ? divisor : 1; + Node *cmp1 = phase->transform( new (phase->C, 3) CmpINode( in(1), phase->intcon(0) ) ); + Node *bol1 = phase->transform( new (phase->C, 2) BoolNode( cmp1, BoolTest::ge ) ); + Node *cmov1= phase->transform( new (phase->C, 4) CMoveINode(bol1, phase->intcon(1), divisor, TypeInt::POS) ); + // if( x >= hack_res ) x -= divisor; + Node *sub = phase->transform( new (phase->C, 3) SubINode( x, divisor ) ); + Node *cmp2 = phase->transform( new (phase->C, 3) CmpINode( x, cmov1 ) ); + Node *bol2 = phase->transform( new (phase->C, 2) BoolNode( cmp2, BoolTest::ge ) ); + // Convention is to not transform the return value of an Ideal + // since Ideal is expected to return a modified 'this' or a new node. + Node *cmov2= new (phase->C, 4) CMoveINode(bol2, x, sub, TypeInt::INT); + // cmov2 is now the mod + + // Now remove the bogus extra edges used to keep things alive + if (can_reshape) { + phase->is_IterGVN()->remove_dead_node(hook); + } else { + hook->set_req(0, NULL); // Just yank bogus edge during Parse phase + } + return cmov2; + } + } + + // Fell thru, the unroll case is not appropriate. Transform the modulo + // into a long multiply/int multiply/subtract case + + // Cannot handle mod 0, and min_jint isn't handled by the transform + if( con == 0 || con == min_jint ) return NULL; + + // Get the absolute value of the constant; at this point, we can use this + jint pos_con = (con >= 0) ? con : -con; + + // integer Mod 1 is always 0 + if( pos_con == 1 ) return new (phase->C, 1) ConINode(TypeInt::ZERO); + + int log2_con = -1; + + // If this is a power of two, they maybe we can mask it + if( is_power_of_2(pos_con) ) { + log2_con = log2_intptr((intptr_t)pos_con); + + const Type *dt = phase->type(in(1)); + const TypeInt *dti = dt->isa_int(); + + // See if this can be masked, if the dividend is non-negative + if( dti && dti->_lo >= 0 ) + return ( new (phase->C, 3) AndINode( in(1), phase->intcon( pos_con-1 ) ) ); + } + + // Save in(1) so that it cannot be changed or deleted + hook->init_req(0, in(1)); + + // Divide using the transform from DivI to MulL + Node *divide = phase->transform( transform_int_divide_to_long_multiply( phase, in(1), pos_con ) ); + + // Re-multiply, using a shift if this is a power of two + Node *mult = NULL; + + if( log2_con >= 0 ) + mult = phase->transform( new (phase->C, 3) LShiftINode( divide, phase->intcon( log2_con ) ) ); + else + mult = phase->transform( new (phase->C, 3) MulINode( divide, phase->intcon( pos_con ) ) ); + + // Finally, subtract the multiplied divided value from the original + Node *result = new (phase->C, 3) SubINode( in(1), mult ); + + // Now remove the bogus extra edges used to keep things alive + if (can_reshape) { + phase->is_IterGVN()->remove_dead_node(hook); + } else { + hook->set_req(0, NULL); // Just yank bogus edge during Parse phase + } + + // return the value + return result; +} + +//------------------------------Value------------------------------------------ +const Type *ModINode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // We always generate the dynamic check for 0. + // 0 MOD X is 0 + if( t1 == TypeInt::ZERO ) return TypeInt::ZERO; + // X MOD X is 0 + if( phase->eqv( in(1), in(2) ) ) return TypeInt::ZERO; + + // Either input is BOTTOM ==> the result is the local BOTTOM + const Type *bot = bottom_type(); + if( (t1 == bot) || (t2 == bot) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return bot; + + const TypeInt *i1 = t1->is_int(); + const TypeInt *i2 = t2->is_int(); + if( !i1->is_con() || !i2->is_con() ) { + if( i1->_lo >= 0 && i2->_lo >= 0 ) + return TypeInt::POS; + // If both numbers are not constants, we know little. + return TypeInt::INT; + } + // Mod by zero? Throw exception at runtime! + if( !i2->get_con() ) return TypeInt::POS; + + // We must be modulo'ing 2 float constants. + // Check for min_jint % '-1', result is defined to be '0'. + if( i1->get_con() == min_jint && i2->get_con() == -1 ) + return TypeInt::ZERO; + + return TypeInt::make( i1->get_con() % i2->get_con() ); +} + + +//============================================================================= +//------------------------------Idealize--------------------------------------- +Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Check for dead control input + if( remove_dead_region(phase, can_reshape) ) return this; + + // Get the modulus + const Type *t = phase->type( in(2) ); + if( t == Type::TOP ) return NULL; + const TypeLong *ti = t->is_long(); + + // Check for useless control input + // Check for excluding mod-zero case + if( in(0) && (ti->_hi < 0 || ti->_lo > 0) ) { + set_req(0, NULL); // Yank control input + return this; + } + + // See if we are MOD'ing by 2^k or 2^k-1. + if( !ti->is_con() ) return NULL; + jlong con = ti->get_con(); + bool m1 = false; + if( !is_power_of_2_long(con) ) { // Not 2^k + if( !is_power_of_2_long(con+1) ) // Not 2^k-1? + return NULL; // No interesting mod hacks + m1 = true; // Found 2^k-1 + con++; // Convert to 2^k form + } + uint k = log2_long(con); // Extract k + + // Expand mod + if( !m1 ) { // Case 2^k + } else { // Case 2^k-1 + // Basic algorithm by David Detlefs. See fastmod_long.java for gory details. + // Used to help a popular random number generator which does a long-mod + // of 2^31-1 and shows up in SpecJBB and SciMark. + static int unroll_factor[] = { 999, 999, 61, 30, 20, 15, 12, 10, 8, 7, 6, 6, 5, 5, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 /*past here we assume 1 forever*/}; + int trip_count = 1; + if( k < ARRAY_SIZE(unroll_factor)) trip_count = unroll_factor[k]; + if( trip_count > 4 ) return NULL; // Too much unrolling + if (ConditionalMoveLimit == 0) return NULL; // cmov is required + + Node *x = in(1); // Value being mod'd + Node *divisor = in(2); // Also is mask + + Node *hook = new (phase->C, 1) Node(x); + // Generate code to reduce X rapidly to nearly 2^k-1. + for( int i = 0; i < trip_count; i++ ) { + Node *xl = phase->transform( new (phase->C, 3) AndLNode(x,divisor) ); + Node *xh = phase->transform( new (phase->C, 3) RShiftLNode(x,phase->intcon(k)) ); // Must be signed + x = phase->transform( new (phase->C, 3) AddLNode(xh,xl) ); + hook->set_req(0, x); // Add a use to x to prevent him from dying + } + // Generate sign-fixup code. Was original value positive? + // long hack_res = (i >= 0) ? divisor : CONST64(1); + Node *cmp1 = phase->transform( new (phase->C, 3) CmpLNode( in(1), phase->longcon(0) ) ); + Node *bol1 = phase->transform( new (phase->C, 2) BoolNode( cmp1, BoolTest::ge ) ); + Node *cmov1= phase->transform( new (phase->C, 4) CMoveLNode(bol1, phase->longcon(1), divisor, TypeLong::LONG) ); + // if( x >= hack_res ) x -= divisor; + Node *sub = phase->transform( new (phase->C, 3) SubLNode( x, divisor ) ); + Node *cmp2 = phase->transform( new (phase->C, 3) CmpLNode( x, cmov1 ) ); + Node *bol2 = phase->transform( new (phase->C, 2) BoolNode( cmp2, BoolTest::ge ) ); + // Convention is to not transform the return value of an Ideal + // since Ideal is expected to return a modified 'this' or a new node. + Node *cmov2= new (phase->C, 4) CMoveLNode(bol2, x, sub, TypeLong::LONG); + // cmov2 is now the mod + + // Now remove the bogus extra edges used to keep things alive + if (can_reshape) { + phase->is_IterGVN()->remove_dead_node(hook); + } else { + hook->set_req(0, NULL); // Just yank bogus edge during Parse phase + } + return cmov2; + } + return NULL; +} + +//------------------------------Value------------------------------------------ +const Type *ModLNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // We always generate the dynamic check for 0. + // 0 MOD X is 0 + if( t1 == TypeLong::ZERO ) return TypeLong::ZERO; + // X MOD X is 0 + if( phase->eqv( in(1), in(2) ) ) return TypeLong::ZERO; + + // Either input is BOTTOM ==> the result is the local BOTTOM + const Type *bot = bottom_type(); + if( (t1 == bot) || (t2 == bot) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return bot; + + const TypeLong *i1 = t1->is_long(); + const TypeLong *i2 = t2->is_long(); + if( !i1->is_con() || !i2->is_con() ) { + if( i1->_lo >= CONST64(0) && i2->_lo >= CONST64(0) ) + return TypeLong::POS; + // If both numbers are not constants, we know little. + return TypeLong::LONG; + } + // Mod by zero? Throw exception at runtime! + if( !i2->get_con() ) return TypeLong::POS; + + // We must be modulo'ing 2 float constants. + // Check for min_jint % '-1', result is defined to be '0'. + if( i1->get_con() == min_jlong && i2->get_con() == -1 ) + return TypeLong::ZERO; + + return TypeLong::make( i1->get_con() % i2->get_con() ); +} + + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ModFNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Either input is BOTTOM ==> the result is the local BOTTOM + const Type *bot = bottom_type(); + if( (t1 == bot) || (t2 == bot) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return bot; + + // If either is a NaN, return an input NaN + if( g_isnan(t1->getf()) ) return t1; + if( g_isnan(t2->getf()) ) return t2; + + // It is not worth trying to constant fold this stuff! + return Type::FLOAT; + + /* + // If dividend is infinity or divisor is zero, or both, the result is NaN + if( !g_isfinite(t1->getf()) || ((t2->getf() == 0.0) || (jint_cast(t2->getf()) == 0x80000000)) ) + + // X MOD infinity = X + if( !g_isfinite(t2->getf()) && !g_isnan(t2->getf()) ) return t1; + // 0 MOD finite = dividend (positive or negative zero) + // Not valid for: NaN MOD any; any MOD nan; 0 MOD 0; or for 0 MOD NaN + // NaNs are handled previously. + if( !(t2->getf() == 0.0) && !((int)t2->getf() == 0x80000000)) { + if (((t1->getf() == 0.0) || ((int)t1->getf() == 0x80000000)) && g_isfinite(t2->getf()) ) { + return t1; + } + } + // X MOD X is 0 + // Does not work for variables because of NaN's + if( phase->eqv( in(1), in(2) ) && t1->base() == Type::FloatCon) + if (!g_isnan(t1->getf()) && (t1->getf() != 0.0) && ((int)t1->getf() != 0x80000000)) { + if(t1->getf() < 0.0) { + float result = jfloat_cast(0x80000000); + return TypeF::make( result ); + } + else + return TypeF::ZERO; + } + + // If both numbers are not constants, we know nothing. + if( (t1->base() != Type::FloatCon) || (t2->base() != Type::FloatCon) ) + return Type::FLOAT; + + // We must be modulo'ing 2 float constants. + // Make sure that the sign of the fmod is equal to the sign of the dividend + float result = (float)fmod( t1->getf(), t2->getf() ); + float dividend = t1->getf(); + if( (dividend < 0.0) || ((int)dividend == 0x80000000) ) { + if( result > 0.0 ) + result = 0.0 - result; + else if( result == 0.0 ) { + result = jfloat_cast(0x80000000); + } + } + return TypeF::make( result ); + */ +} + + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *ModDNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Either input is BOTTOM ==> the result is the local BOTTOM + const Type *bot = bottom_type(); + if( (t1 == bot) || (t2 == bot) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return bot; + + // If either is a NaN, return an input NaN + if( g_isnan(t1->getd()) ) return t1; + if( g_isnan(t2->getd()) ) return t2; + // X MOD infinity = X + if( !g_isfinite(t2->getd())) return t1; + // 0 MOD finite = dividend (positive or negative zero) + // Not valid for: NaN MOD any; any MOD nan; 0 MOD 0; or for 0 MOD NaN + // NaNs are handled previously. + if( !(t2->getd() == 0.0) ) { + if( t1->getd() == 0.0 && g_isfinite(t2->getd()) ) { + return t1; + } + } + + // X MOD X is 0 + // does not work for variables because of NaN's + if( phase->eqv( in(1), in(2) ) && t1->base() == Type::DoubleCon ) + if (!g_isnan(t1->getd()) && t1->getd() != 0.0) + return TypeD::ZERO; + + + // If both numbers are not constants, we know nothing. + if( (t1->base() != Type::DoubleCon) || (t2->base() != Type::DoubleCon) ) + return Type::DOUBLE; + + // We must be modulo'ing 2 double constants. + return TypeD::make( fmod( t1->getd(), t2->getd() ) ); +} + +//============================================================================= + +DivModNode::DivModNode( Node *c, Node *dividend, Node *divisor ) : MultiNode(3) { + init_req(0, c); + init_req(1, dividend); + init_req(2, divisor); +} + +//------------------------------make------------------------------------------ +DivModINode* DivModINode::make(Compile* C, Node* div_or_mod) { + Node* n = div_or_mod; + assert(n->Opcode() == Op_DivI || n->Opcode() == Op_ModI, + "only div or mod input pattern accepted"); + + DivModINode* divmod = new (C, 3) DivModINode(n->in(0), n->in(1), n->in(2)); + Node* dproj = new (C, 1) ProjNode(divmod, DivModNode::div_proj_num); + Node* mproj = new (C, 1) ProjNode(divmod, DivModNode::mod_proj_num); + return divmod; +} + +//------------------------------make------------------------------------------ +DivModLNode* DivModLNode::make(Compile* C, Node* div_or_mod) { + Node* n = div_or_mod; + assert(n->Opcode() == Op_DivL || n->Opcode() == Op_ModL, + "only div or mod input pattern accepted"); + + DivModLNode* divmod = new (C, 3) DivModLNode(n->in(0), n->in(1), n->in(2)); + Node* dproj = new (C, 1) ProjNode(divmod, DivModNode::div_proj_num); + Node* mproj = new (C, 1) ProjNode(divmod, DivModNode::mod_proj_num); + return divmod; +} + +//------------------------------match------------------------------------------ +// return result(s) along with their RegMask info +Node *DivModINode::match( const ProjNode *proj, const Matcher *match ) { + uint ideal_reg = proj->ideal_reg(); + RegMask rm; + if (proj->_con == div_proj_num) { + rm = match->divI_proj_mask(); + } else { + assert(proj->_con == mod_proj_num, "must be div or mod projection"); + rm = match->modI_proj_mask(); + } + return new (match->C, 1)MachProjNode(this, proj->_con, rm, ideal_reg); +} + + +//------------------------------match------------------------------------------ +// return result(s) along with their RegMask info +Node *DivModLNode::match( const ProjNode *proj, const Matcher *match ) { + uint ideal_reg = proj->ideal_reg(); + RegMask rm; + if (proj->_con == div_proj_num) { + rm = match->divL_proj_mask(); + } else { + assert(proj->_con == mod_proj_num, "must be div or mod projection"); + rm = match->modL_proj_mask(); + } + return new (match->C, 1)MachProjNode(this, proj->_con, rm, ideal_reg); +} diff --git a/hotspot/src/share/vm/opto/divnode.hpp b/hotspot/src/share/vm/opto/divnode.hpp new file mode 100644 index 00000000000..797d2cf4137 --- /dev/null +++ b/hotspot/src/share/vm/opto/divnode.hpp @@ -0,0 +1,177 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + + +//------------------------------DivINode--------------------------------------- +// Integer division +// Note: this is division as defined by JVMS, i.e., MinInt/-1 == MinInt. +// On processors which don't naturally support this special case (e.g., x86), +// the matcher or runtime system must take care of this. +class DivINode : public Node { +public: + DivINode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor ) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------DivLNode--------------------------------------- +// Long division +class DivLNode : public Node { +public: + DivLNode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor ) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------DivFNode--------------------------------------- +// Float division +class DivFNode : public Node { +public: + DivFNode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------DivDNode--------------------------------------- +// Double division +class DivDNode : public Node { +public: + DivDNode( Node *c, Node *dividend, Node *divisor ) : Node(c,dividend, divisor) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + +//------------------------------ModINode--------------------------------------- +// Integer modulus +class ModINode : public Node { +public: + ModINode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {} + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------ModLNode--------------------------------------- +// Long modulus +class ModLNode : public Node { +public: + ModLNode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {} + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------ModFNode--------------------------------------- +// Float Modulus +class ModFNode : public Node { +public: + ModFNode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {} + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------ModDNode--------------------------------------- +// Double Modulus +class ModDNode : public Node { +public: + ModDNode( Node *c, Node *in1, Node *in2 ) : Node(c, in1, in2) {} + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + +//------------------------------DivModNode--------------------------------------- +// Division with remainder result. +class DivModNode : public MultiNode { +protected: + DivModNode( Node *c, Node *dividend, Node *divisor ); +public: + enum { + div_proj_num = 0, // quotient + mod_proj_num = 1 // remainder + }; + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ) { return this; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape) { return NULL; } + virtual const Type *Value( PhaseTransform *phase ) const { return bottom_type(); } + virtual uint hash() const { return Node::hash(); } + virtual bool is_CFG() const { return false; } + virtual uint ideal_reg() const { return NotAMachineReg; } + + ProjNode* div_proj() { return proj_out(div_proj_num); } + ProjNode* mod_proj() { return proj_out(mod_proj_num); } +}; + +//------------------------------DivModINode--------------------------------------- +// Integer division with remainder result. +class DivModINode : public DivModNode { +public: + DivModINode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeTuple::INT_PAIR; } + virtual Node *match( const ProjNode *proj, const Matcher *m ); + + // Make a divmod and associated projections from a div or mod. + static DivModINode* make(Compile* C, Node* div_or_mod); +}; + +//------------------------------DivModLNode--------------------------------------- +// Long division with remainder result. +class DivModLNode : public DivModNode { +public: + DivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeTuple::LONG_PAIR; } + virtual Node *match( const ProjNode *proj, const Matcher *m ); + + // Make a divmod and associated projections from a div or mod. + static DivModLNode* make(Compile* C, Node* div_or_mod); +}; diff --git a/hotspot/src/share/vm/opto/doCall.cpp b/hotspot/src/share/vm/opto/doCall.cpp new file mode 100644 index 00000000000..ff85fb643e3 --- /dev/null +++ b/hotspot/src/share/vm/opto/doCall.cpp @@ -0,0 +1,862 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_doCall.cpp.incl" + +#ifndef PRODUCT +void trace_type_profile(ciMethod *method, int depth, int bci, ciMethod *prof_method, ciKlass *prof_klass, int site_count, int receiver_count) { + if (TraceTypeProfile || PrintInlining || PrintOptoInlining) { + tty->print(" "); + for( int i = 0; i < depth; i++ ) tty->print(" "); + if (!PrintOpto) { + method->print_short_name(); + tty->print(" ->"); + } + tty->print(" @ %d ", bci); + prof_method->print_short_name(); + tty->print(" >>TypeProfile (%d/%d counts) = ", receiver_count, site_count); + prof_klass->name()->print_symbol(); + tty->print_cr(" (%d bytes)", prof_method->code_size()); + } +} +#endif + +CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual, JVMState* jvms, bool allow_inline, float prof_factor) { + CallGenerator* cg; + + // Dtrace currently doesn't work unless all calls are vanilla + if (DTraceMethodProbes) { + allow_inline = false; + } + + // Note: When we get profiling during stage-1 compiles, we want to pull + // from more specific profile data which pertains to this inlining. + // Right now, ignore the information in jvms->caller(), and do method[bci]. + ciCallProfile profile = jvms->method()->call_profile_at_bci(jvms->bci()); + + // See how many times this site has been invoked. + int site_count = profile.count(); + int receiver_count = -1; + if (call_is_virtual && UseTypeProfile && profile.has_receiver(0)) { + // Receivers in the profile structure are ordered by call counts + // so that the most called (major) receiver is profile.receiver(0). + receiver_count = profile.receiver_count(0); + } + + CompileLog* log = this->log(); + if (log != NULL) { + int rid = (receiver_count >= 0)? log->identify(profile.receiver(0)): -1; + int r2id = (profile.morphism() == 2)? log->identify(profile.receiver(1)):-1; + log->begin_elem("call method='%d' count='%d' prof_factor='%g'", + log->identify(call_method), site_count, prof_factor); + if (call_is_virtual) log->print(" virtual='1'"); + if (allow_inline) log->print(" inline='1'"); + if (receiver_count >= 0) { + log->print(" receiver='%d' receiver_count='%d'", rid, receiver_count); + if (profile.has_receiver(1)) { + log->print(" receiver2='%d' receiver2_count='%d'", r2id, profile.receiver_count(1)); + } + } + log->end_elem(); + } + + // Special case the handling of certain common, profitable library + // methods. If these methods are replaced with specialized code, + // then we return it as the inlined version of the call. + // We do this before the strict f.p. check below because the + // intrinsics handle strict f.p. correctly. + if (allow_inline) { + cg = find_intrinsic(call_method, call_is_virtual); + if (cg != NULL) return cg; + } + + // Do not inline strict fp into non-strict code, or the reverse + bool caller_method_is_strict = jvms->method()->is_strict(); + if( caller_method_is_strict ^ call_method->is_strict() ) { + allow_inline = false; + } + + // Attempt to inline... + if (allow_inline) { + // The profile data is only partly attributable to this caller, + // scale back the call site information. + float past_uses = jvms->method()->scale_count(site_count, prof_factor); + // This is the number of times we expect the call code to be used. + float expected_uses = past_uses; + + // Try inlining a bytecoded method: + if (!call_is_virtual) { + InlineTree* ilt; + if (UseOldInlining) { + ilt = InlineTree::find_subtree_from_root(this->ilt(), jvms->caller(), jvms->method()); + } else { + // Make a disembodied, stateless ILT. + // TO DO: When UseOldInlining is removed, copy the ILT code elsewhere. + float site_invoke_ratio = prof_factor; + // Note: ilt is for the root of this parse, not the present call site. + ilt = new InlineTree(this, jvms->method(), jvms->caller(), site_invoke_ratio); + } + WarmCallInfo scratch_ci; + if (!UseOldInlining) + scratch_ci.init(jvms, call_method, profile, prof_factor); + WarmCallInfo* ci = ilt->ok_to_inline(call_method, jvms, profile, &scratch_ci); + assert(ci != &scratch_ci, "do not let this pointer escape"); + bool allow_inline = (ci != NULL && !ci->is_cold()); + bool require_inline = (allow_inline && ci->is_hot()); + + if (allow_inline) { + CallGenerator* cg = CallGenerator::for_inline(call_method, expected_uses); + if (cg == NULL) { + // Fall through. + } else if (require_inline || !InlineWarmCalls) { + return cg; + } else { + CallGenerator* cold_cg = call_generator(call_method, vtable_index, call_is_virtual, jvms, false, prof_factor); + return CallGenerator::for_warm_call(ci, cold_cg, cg); + } + } + } + + // Try using the type profile. + if (call_is_virtual && site_count > 0 && receiver_count > 0) { + // The major receiver's count >= TypeProfileMajorReceiverPercent of site_count. + bool have_major_receiver = (100.*profile.receiver_prob(0) >= (float)TypeProfileMajorReceiverPercent); + ciMethod* receiver_method = NULL; + if (have_major_receiver || profile.morphism() == 1 || + (profile.morphism() == 2 && UseBimorphicInlining)) { + // receiver_method = profile.method(); + // Profiles do not suggest methods now. Look it up in the major receiver. + receiver_method = call_method->resolve_invoke(jvms->method()->holder(), + profile.receiver(0)); + } + if (receiver_method != NULL) { + // The single majority receiver sufficiently outweighs the minority. + CallGenerator* hit_cg = this->call_generator(receiver_method, + vtable_index, !call_is_virtual, jvms, allow_inline, prof_factor); + if (hit_cg != NULL) { + // Look up second receiver. + CallGenerator* next_hit_cg = NULL; + ciMethod* next_receiver_method = NULL; + if (profile.morphism() == 2 && UseBimorphicInlining) { + next_receiver_method = call_method->resolve_invoke(jvms->method()->holder(), + profile.receiver(1)); + if (next_receiver_method != NULL) { + next_hit_cg = this->call_generator(next_receiver_method, + vtable_index, !call_is_virtual, jvms, + allow_inline, prof_factor); + if (next_hit_cg != NULL && !next_hit_cg->is_inline() && + have_major_receiver && UseOnlyInlinedBimorphic) { + // Skip if we can't inline second receiver's method + next_hit_cg = NULL; + } + } + } + CallGenerator* miss_cg; + if (( profile.morphism() == 1 || + (profile.morphism() == 2 && next_hit_cg != NULL) ) && + + !too_many_traps(Deoptimization::Reason_class_check) + + // Check only total number of traps per method to allow + // the transition from monomorphic to bimorphic case between + // compilations without falling into virtual call. + // A monomorphic case may have the class_check trap flag is set + // due to the time gap between the uncommon trap processing + // when flags are set in MDO and the call site bytecode execution + // in Interpreter when MDO counters are updated. + // There was also class_check trap in monomorphic case due to + // the bug 6225440. + + ) { + // Generate uncommon trap for class check failure path + // in case of monomorphic or bimorphic virtual call site. + miss_cg = CallGenerator::for_uncommon_trap(call_method, + Deoptimization::Reason_class_check, + Deoptimization::Action_maybe_recompile); + } else { + // Generate virtual call for class check failure path + // in case of polymorphic virtual call site. + miss_cg = CallGenerator::for_virtual_call(call_method, vtable_index); + } + if (miss_cg != NULL) { + if (next_hit_cg != NULL) { + NOT_PRODUCT(trace_type_profile(jvms->method(), jvms->depth(), jvms->bci(), next_receiver_method, profile.receiver(1), site_count, profile.receiver_count(1))); + // We don't need to record dependency on a receiver here and below. + // Whenever we inline, the dependency is added by Parse::Parse(). + miss_cg = CallGenerator::for_predicted_call(profile.receiver(1), miss_cg, next_hit_cg, PROB_MAX); + } + if (miss_cg != NULL) { + NOT_PRODUCT(trace_type_profile(jvms->method(), jvms->depth(), jvms->bci(), receiver_method, profile.receiver(0), site_count, receiver_count)); + cg = CallGenerator::for_predicted_call(profile.receiver(0), miss_cg, hit_cg, profile.receiver_prob(0)); + if (cg != NULL) return cg; + } + } + } + } + } + } + + // There was no special inlining tactic, or it bailed out. + // Use a more generic tactic, like a simple call. + if (call_is_virtual) { + return CallGenerator::for_virtual_call(call_method, vtable_index); + } else { + // Class Hierarchy Analysis or Type Profile reveals a unique target, + // or it is a static or special call. + return CallGenerator::for_direct_call(call_method); + } +} + + +// uncommon-trap call-sites where callee is unloaded, uninitialized or will not link +bool Parse::can_not_compile_call_site(ciMethod *dest_method, ciInstanceKlass* klass) { + // Additional inputs to consider... + // bc = bc() + // caller = method() + // iter().get_method_holder_index() + assert( dest_method->is_loaded(), "ciTypeFlow should not let us get here" ); + // Interface classes can be loaded & linked and never get around to + // being initialized. Uncommon-trap for not-initialized static or + // v-calls. Let interface calls happen. + ciInstanceKlass* holder_klass = dest_method->holder(); + if (!holder_klass->is_initialized() && + !holder_klass->is_interface()) { + uncommon_trap(Deoptimization::Reason_uninitialized, + Deoptimization::Action_reinterpret, + holder_klass); + return true; + } + + assert(dest_method->will_link(method()->holder(), klass, bc()), "dest_method: typeflow responsibility"); + return false; +} + + +//------------------------------do_call---------------------------------------- +// Handle your basic call. Inline if we can & want to, else just setup call. +void Parse::do_call() { + // It's likely we are going to add debug info soon. + // Also, if we inline a guy who eventually needs debug info for this JVMS, + // our contribution to it is cleaned up right here. + kill_dead_locals(); + + // Set frequently used booleans + bool is_virtual = bc() == Bytecodes::_invokevirtual; + bool is_virtual_or_interface = is_virtual || bc() == Bytecodes::_invokeinterface; + bool has_receiver = is_virtual_or_interface || bc() == Bytecodes::_invokespecial; + + // Find target being called + bool will_link; + ciMethod* dest_method = iter().get_method(will_link); + ciInstanceKlass* holder_klass = dest_method->holder(); + ciKlass* holder = iter().get_declared_method_holder(); + ciInstanceKlass* klass = ciEnv::get_instance_klass_for_declared_method_holder(holder); + + int nargs = dest_method->arg_size(); + + // uncommon-trap when callee is unloaded, uninitialized or will not link + // bailout when too many arguments for register representation + if (!will_link || can_not_compile_call_site(dest_method, klass)) { +#ifndef PRODUCT + if (PrintOpto && (Verbose || WizardMode)) { + method()->print_name(); tty->print_cr(" can not compile call at bci %d to:", bci()); + dest_method->print_name(); tty->cr(); + } +#endif + return; + } + assert(holder_klass->is_loaded(), ""); + assert(dest_method->is_static() == !has_receiver, "must match bc"); + // Note: this takes into account invokeinterface of methods declared in java/lang/Object, + // which should be invokevirtuals but according to the VM spec may be invokeinterfaces + assert(holder_klass->is_interface() || holder_klass->super() == NULL || (bc() != Bytecodes::_invokeinterface), "must match bc"); + // Note: In the absence of miranda methods, an abstract class K can perform + // an invokevirtual directly on an interface method I.m if K implements I. + + // --------------------- + // Does Class Hierarchy Analysis reveal only a single target of a v-call? + // Then we may inline or make a static call, but become dependent on there being only 1 target. + // Does the call-site type profile reveal only one receiver? + // Then we may introduce a run-time check and inline on the path where it succeeds. + // The other path may uncommon_trap, check for another receiver, or do a v-call. + + // Choose call strategy. + bool call_is_virtual = is_virtual_or_interface; + int vtable_index = methodOopDesc::invalid_vtable_index; + ciMethod* call_method = dest_method; + + // Try to get the most accurate receiver type + if (is_virtual_or_interface) { + Node* receiver_node = stack(sp() - nargs); + const TypeOopPtr* receiver_type = _gvn.type(receiver_node)->isa_oopptr(); + ciMethod* optimized_virtual_method = optimize_inlining(method(), bci(), klass, dest_method, receiver_type); + + // Have the call been sufficiently improved such that it is no longer a virtual? + if (optimized_virtual_method != NULL) { + call_method = optimized_virtual_method; + call_is_virtual = false; + } else if (!UseInlineCaches && is_virtual && call_method->is_loaded()) { + // We can make a vtable call at this site + vtable_index = call_method->resolve_vtable_index(method()->holder(), klass); + } + } + + // Note: It's OK to try to inline a virtual call. + // The call generator will not attempt to inline a polymorphic call + // unless it knows how to optimize the receiver dispatch. + bool try_inline = (C->do_inlining() || InlineAccessors); + + // --------------------- + inc_sp(- nargs); // Temporarily pop args for JVM state of call + JVMState* jvms = sync_jvms(); + + // --------------------- + // Decide call tactic. + // This call checks with CHA, the interpreter profile, intrinsics table, etc. + // It decides whether inlining is desirable or not. + CallGenerator* cg = C->call_generator(call_method, vtable_index, call_is_virtual, jvms, try_inline, prof_factor()); + + // --------------------- + // Round double arguments before call + round_double_arguments(dest_method); + +#ifndef PRODUCT + // bump global counters for calls + count_compiled_calls(false/*at_method_entry*/, cg->is_inline()); + + // Record first part of parsing work for this call + parse_histogram()->record_change(); +#endif // not PRODUCT + + assert(jvms == this->jvms(), "still operating on the right JVMS"); + assert(jvms_in_sync(), "jvms must carry full info into CG"); + + // save across call, for a subsequent cast_not_null. + Node* receiver = has_receiver ? argument(0) : NULL; + + // Bump method data counters (We profile *before* the call is made + // because exceptions don't return to the call site.) + profile_call(receiver); + + JVMState* new_jvms; + if ((new_jvms = cg->generate(jvms)) == NULL) { + // When inlining attempt fails (e.g., too many arguments), + // it may contaminate the current compile state, making it + // impossible to pull back and try again. Once we call + // cg->generate(), we are committed. If it fails, the whole + // compilation task is compromised. + if (failing()) return; +#ifndef PRODUCT + if (PrintOpto || PrintOptoInlining || PrintInlining) { + // Only one fall-back, so if an intrinsic fails, ignore any bytecodes. + if (cg->is_intrinsic() && call_method->code_size() > 0) { + tty->print("Bailed out of intrinsic, will not inline: "); + call_method->print_name(); tty->cr(); + } + } +#endif + // This can happen if a library intrinsic is available, but refuses + // the call site, perhaps because it did not match a pattern the + // intrinsic was expecting to optimize. The fallback position is + // to call out-of-line. + try_inline = false; // Inline tactic bailed out. + cg = C->call_generator(call_method, vtable_index, call_is_virtual, jvms, try_inline, prof_factor()); + if ((new_jvms = cg->generate(jvms)) == NULL) { + guarantee(failing(), "call failed to generate: calls should work"); + return; + } + } + + if (cg->is_inline()) { + C->env()->notice_inlined_method(call_method); + } + + // Reset parser state from [new_]jvms, which now carries results of the call. + // Return value (if any) is already pushed on the stack by the cg. + add_exception_states_from(new_jvms); + if (new_jvms->map()->control() == top()) { + stop_and_kill_map(); + } else { + assert(new_jvms->same_calls_as(jvms), "method/bci left unchanged"); + set_jvms(new_jvms); + } + + if (!stopped()) { + // This was some sort of virtual call, which did a null check for us. + // Now we can assert receiver-not-null, on the normal return path. + if (receiver != NULL && cg->is_virtual()) { + Node* cast = cast_not_null(receiver); + // %%% assert(receiver == cast, "should already have cast the receiver"); + } + + // Round double result after a call from strict to non-strict code + round_double_result(dest_method); + + // If the return type of the method is not loaded, assert that the + // value we got is a null. Otherwise, we need to recompile. + if (!dest_method->return_type()->is_loaded()) { +#ifndef PRODUCT + if (PrintOpto && (Verbose || WizardMode)) { + method()->print_name(); tty->print_cr(" asserting nullness of result at bci: %d", bci()); + dest_method->print_name(); tty->cr(); + } +#endif + if (C->log() != NULL) { + C->log()->elem("assert_null reason='return' klass='%d'", + C->log()->identify(dest_method->return_type())); + } + // If there is going to be a trap, put it at the next bytecode: + set_bci(iter().next_bci()); + do_null_assert(peek(), T_OBJECT); + set_bci(iter().cur_bci()); // put it back + } + } + + // Restart record of parsing work after possible inlining of call +#ifndef PRODUCT + parse_histogram()->set_initial_state(bc()); +#endif +} + +//---------------------------catch_call_exceptions----------------------------- +// Put a Catch and CatchProj nodes behind a just-created call. +// Send their caught exceptions to the proper handler. +// This may be used after a call to the rethrow VM stub, +// when it is needed to process unloaded exception classes. +void Parse::catch_call_exceptions(ciExceptionHandlerStream& handlers) { + // Exceptions are delivered through this channel: + Node* i_o = this->i_o(); + + // Add a CatchNode. + GrowableArray* bcis = new (C->node_arena()) GrowableArray(C->node_arena(), 8, 0, -1); + GrowableArray* extypes = new (C->node_arena()) GrowableArray(C->node_arena(), 8, 0, NULL); + GrowableArray* saw_unloaded = new (C->node_arena()) GrowableArray(C->node_arena(), 8, 0, 0); + + for (; !handlers.is_done(); handlers.next()) { + ciExceptionHandler* h = handlers.handler(); + int h_bci = h->handler_bci(); + ciInstanceKlass* h_klass = h->is_catch_all() ? env()->Throwable_klass() : h->catch_klass(); + // Do not introduce unloaded exception types into the graph: + if (!h_klass->is_loaded()) { + if (saw_unloaded->contains(h_bci)) { + /* We've already seen an unloaded exception with h_bci, + so don't duplicate. Duplication will cause the CatchNode to be + unnecessarily large. See 4713716. */ + continue; + } else { + saw_unloaded->append(h_bci); + } + } + const Type* h_extype = TypeOopPtr::make_from_klass(h_klass); + // (We use make_from_klass because it respects UseUniqueSubclasses.) + h_extype = h_extype->join(TypeInstPtr::NOTNULL); + assert(!h_extype->empty(), "sanity"); + // Note: It's OK if the BCIs repeat themselves. + bcis->append(h_bci); + extypes->append(h_extype); + } + + int len = bcis->length(); + CatchNode *cn = new (C, 2) CatchNode(control(), i_o, len+1); + Node *catch_ = _gvn.transform(cn); + + // now branch with the exception state to each of the (potential) + // handlers + for(int i=0; i < len; i++) { + // Setup JVM state to enter the handler. + PreserveJVMState pjvms(this); + // Locals are just copied from before the call. + // Get control from the CatchNode. + int handler_bci = bcis->at(i); + Node* ctrl = _gvn.transform( new (C, 1) CatchProjNode(catch_, i+1,handler_bci)); + // This handler cannot happen? + if (ctrl == top()) continue; + set_control(ctrl); + + // Create exception oop + const TypeInstPtr* extype = extypes->at(i)->is_instptr(); + Node *ex_oop = _gvn.transform(new (C, 2) CreateExNode(extypes->at(i), ctrl, i_o)); + + // Handle unloaded exception classes. + if (saw_unloaded->contains(handler_bci)) { + // An unloaded exception type is coming here. Do an uncommon trap. +#ifndef PRODUCT + // We do not expect the same handler bci to take both cold unloaded + // and hot loaded exceptions. But, watch for it. + if (extype->is_loaded()) { + tty->print_cr("Warning: Handler @%d takes mixed loaded/unloaded exceptions in "); + method()->print_name(); tty->cr(); + } else if (PrintOpto && (Verbose || WizardMode)) { + tty->print("Bailing out on unloaded exception type "); + extype->klass()->print_name(); + tty->print(" at bci:%d in ", bci()); + method()->print_name(); tty->cr(); + } +#endif + // Emit an uncommon trap instead of processing the block. + set_bci(handler_bci); + push_ex_oop(ex_oop); + uncommon_trap(Deoptimization::Reason_unloaded, + Deoptimization::Action_reinterpret, + extype->klass(), "!loaded exception"); + set_bci(iter().cur_bci()); // put it back + continue; + } + + // go to the exception handler + if (handler_bci < 0) { // merge with corresponding rethrow node + throw_to_exit(make_exception_state(ex_oop)); + } else { // Else jump to corresponding handle + push_ex_oop(ex_oop); // Clear stack and push just the oop. + merge_exception(handler_bci); + } + } + + // The first CatchProj is for the normal return. + // (Note: If this is a call to rethrow_Java, this node goes dead.) + set_control(_gvn.transform( new (C, 1) CatchProjNode(catch_, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci))); +} + + +//----------------------------catch_inline_exceptions-------------------------- +// Handle all exceptions thrown by an inlined method or individual bytecode. +// Common case 1: we have no handler, so all exceptions merge right into +// the rethrow case. +// Case 2: we have some handlers, with loaded exception klasses that have +// no subklasses. We do a Deutsch-Shiffman style type-check on the incoming +// exception oop and branch to the handler directly. +// Case 3: We have some handlers with subklasses or are not loaded at +// compile-time. We have to call the runtime to resolve the exception. +// So we insert a RethrowCall and all the logic that goes with it. +void Parse::catch_inline_exceptions(SafePointNode* ex_map) { + // Caller is responsible for saving away the map for normal control flow! + assert(stopped(), "call set_map(NULL) first"); + assert(method()->has_exception_handlers(), "don't come here w/o work to do"); + + Node* ex_node = saved_ex_oop(ex_map); + if (ex_node == top()) { + // No action needed. + return; + } + const TypeInstPtr* ex_type = _gvn.type(ex_node)->isa_instptr(); + NOT_PRODUCT(if (ex_type==NULL) tty->print_cr("*** Exception not InstPtr")); + if (ex_type == NULL) + ex_type = TypeOopPtr::make_from_klass(env()->Throwable_klass())->is_instptr(); + + // determine potential exception handlers + ciExceptionHandlerStream handlers(method(), bci(), + ex_type->klass()->as_instance_klass(), + ex_type->klass_is_exact()); + + // Start executing from the given throw state. (Keep its stack, for now.) + // Get the exception oop as known at compile time. + ex_node = use_exception_state(ex_map); + + // Get the exception oop klass from its header + Node* ex_klass_node = NULL; + if (has_ex_handler() && !ex_type->klass_is_exact()) { + Node* p = basic_plus_adr( ex_node, ex_node, oopDesc::klass_offset_in_bytes()); + ex_klass_node = _gvn.transform(new (C, 3) LoadKlassNode(NULL, immutable_memory(), p, TypeInstPtr::KLASS, TypeKlassPtr::OBJECT)); + + // Compute the exception klass a little more cleverly. + // Obvious solution is to simple do a LoadKlass from the 'ex_node'. + // However, if the ex_node is a PhiNode, I'm going to do a LoadKlass for + // each arm of the Phi. If I know something clever about the exceptions + // I'm loading the class from, I can replace the LoadKlass with the + // klass constant for the exception oop. + if( ex_node->is_Phi() ) { + ex_klass_node = new (C, ex_node->req()) PhiNode( ex_node->in(0), TypeKlassPtr::OBJECT ); + for( uint i = 1; i < ex_node->req(); i++ ) { + Node* p = basic_plus_adr( ex_node->in(i), ex_node->in(i), oopDesc::klass_offset_in_bytes() ); + Node* k = _gvn.transform(new (C, 3) LoadKlassNode(0, immutable_memory(), p, TypeInstPtr::KLASS, TypeKlassPtr::OBJECT)); + ex_klass_node->init_req( i, k ); + } + _gvn.set_type(ex_klass_node, TypeKlassPtr::OBJECT); + + } + } + + // Scan the exception table for applicable handlers. + // If none, we can call rethrow() and be done! + // If precise (loaded with no subklasses), insert a D.S. style + // pointer compare to the correct handler and loop back. + // If imprecise, switch to the Rethrow VM-call style handling. + + int remaining = handlers.count_remaining(); + + // iterate through all entries sequentially + for (;!handlers.is_done(); handlers.next()) { + // Do nothing if turned off + if( !DeutschShiffmanExceptions ) break; + ciExceptionHandler* handler = handlers.handler(); + + if (handler->is_rethrow()) { + // If we fell off the end of the table without finding an imprecise + // exception klass (and without finding a generic handler) then we + // know this exception is not handled in this method. We just rethrow + // the exception into the caller. + throw_to_exit(make_exception_state(ex_node)); + return; + } + + // exception handler bci range covers throw_bci => investigate further + int handler_bci = handler->handler_bci(); + + if (remaining == 1) { + push_ex_oop(ex_node); // Push exception oop for handler +#ifndef PRODUCT + if (PrintOpto && WizardMode) { + tty->print_cr(" Catching every inline exception bci:%d -> handler_bci:%d", bci(), handler_bci); + } +#endif + merge_exception(handler_bci); // jump to handler + return; // No more handling to be done here! + } + + // %%% The following logic replicates make_from_klass_unique. + // TO DO: Replace by a subroutine call. Then generalize + // the type check, as noted in the next "%%%" comment. + + ciInstanceKlass* klass = handler->catch_klass(); + if (UseUniqueSubclasses) { + // (We use make_from_klass because it respects UseUniqueSubclasses.) + const TypeOopPtr* tp = TypeOopPtr::make_from_klass(klass); + klass = tp->klass()->as_instance_klass(); + } + + // Get the handler's klass + if (!klass->is_loaded()) // klass is not loaded? + break; // Must call Rethrow! + if (klass->is_interface()) // should not happen, but... + break; // bail out + // See if the loaded exception klass has no subtypes + if (klass->has_subklass()) + break; // Cannot easily do precise test ==> Rethrow + + // %%% Now that subclass checking is very fast, we need to rewrite + // this section and remove the option "DeutschShiffmanExceptions". + // The exception processing chain should be a normal typecase pattern, + // with a bailout to the interpreter only in the case of unloaded + // classes. (The bailout should mark the method non-entrant.) + // This rewrite should be placed in GraphKit::, not Parse::. + + // Add a dependence; if any subclass added we need to recompile + // %%% should use stronger assert_unique_concrete_subtype instead + if (!klass->is_final()) { + C->dependencies()->assert_leaf_type(klass); + } + + // Implement precise test + const TypeKlassPtr *tk = TypeKlassPtr::make(klass); + Node* con = _gvn.makecon(tk); + Node* cmp = _gvn.transform( new (C, 3) CmpPNode(ex_klass_node, con) ); + Node* bol = _gvn.transform( new (C, 2) BoolNode(cmp, BoolTest::ne) ); + { BuildCutout unless(this, bol, PROB_LIKELY(0.7f)); + const TypeInstPtr* tinst = TypeInstPtr::make_exact(TypePtr::NotNull, klass); + Node* ex_oop = _gvn.transform(new (C, 2) CheckCastPPNode(control(), ex_node, tinst)); + push_ex_oop(ex_oop); // Push exception oop for handler +#ifndef PRODUCT + if (PrintOpto && WizardMode) { + tty->print(" Catching inline exception bci:%d -> handler_bci:%d -- ", bci(), handler_bci); + klass->print_name(); + tty->cr(); + } +#endif + merge_exception(handler_bci); + } + + // Come here if exception does not match handler. + // Carry on with more handler checks. + --remaining; + } + + assert(!stopped(), "you should return if you finish the chain"); + + if (remaining == 1) { + // Further checks do not matter. + } + + if (can_rerun_bytecode()) { + // Do not push_ex_oop here! + // Re-executing the bytecode will reproduce the throwing condition. + bool must_throw = true; + uncommon_trap(Deoptimization::Reason_unhandled, + Deoptimization::Action_none, + (ciKlass*)NULL, (const char*)NULL, // default args + must_throw); + return; + } + + // Oops, need to call into the VM to resolve the klasses at runtime. + // Note: This call must not deoptimize, since it is not a real at this bci! + kill_dead_locals(); + + make_runtime_call(RC_NO_LEAF | RC_MUST_THROW, + OptoRuntime::rethrow_Type(), + OptoRuntime::rethrow_stub(), + NULL, NULL, + ex_node); + + // Rethrow is a pure call, no side effects, only a result. + // The result cannot be allocated, so we use I_O + + // Catch exceptions from the rethrow + catch_call_exceptions(handlers); +} + + +// (Note: Moved add_debug_info into GraphKit::add_safepoint_edges.) + + +#ifndef PRODUCT +void Parse::count_compiled_calls(bool at_method_entry, bool is_inline) { + if( CountCompiledCalls ) { + if( at_method_entry ) { + // bump invocation counter if top method (for statistics) + if (CountCompiledCalls && depth() == 1) { + const TypeInstPtr* addr_type = TypeInstPtr::make(method()); + Node* adr1 = makecon(addr_type); + Node* adr2 = basic_plus_adr(adr1, adr1, in_bytes(methodOopDesc::compiled_invocation_counter_offset())); + increment_counter(adr2); + } + } else if (is_inline) { + switch (bc()) { + case Bytecodes::_invokevirtual: increment_counter(SharedRuntime::nof_inlined_calls_addr()); break; + case Bytecodes::_invokeinterface: increment_counter(SharedRuntime::nof_inlined_interface_calls_addr()); break; + case Bytecodes::_invokestatic: + case Bytecodes::_invokespecial: increment_counter(SharedRuntime::nof_inlined_static_calls_addr()); break; + default: fatal("unexpected call bytecode"); + } + } else { + switch (bc()) { + case Bytecodes::_invokevirtual: increment_counter(SharedRuntime::nof_normal_calls_addr()); break; + case Bytecodes::_invokeinterface: increment_counter(SharedRuntime::nof_interface_calls_addr()); break; + case Bytecodes::_invokestatic: + case Bytecodes::_invokespecial: increment_counter(SharedRuntime::nof_static_calls_addr()); break; + default: fatal("unexpected call bytecode"); + } + } + } +} +#endif //PRODUCT + + +// Identify possible target method and inlining style +ciMethod* Parse::optimize_inlining(ciMethod* caller, int bci, ciInstanceKlass* klass, + ciMethod *dest_method, const TypeOopPtr* receiver_type) { + // only use for virtual or interface calls + + // If it is obviously final, do not bother to call find_monomorphic_target, + // because the class hierarchy checks are not needed, and may fail due to + // incompletely loaded classes. Since we do our own class loading checks + // in this module, we may confidently bind to any method. + if (dest_method->can_be_statically_bound()) { + return dest_method; + } + + // Attempt to improve the receiver + bool actual_receiver_is_exact = false; + ciInstanceKlass* actual_receiver = klass; + if (receiver_type != NULL) { + // Array methods are all inherited from Object, and are monomorphic. + if (receiver_type->isa_aryptr() && + dest_method->holder() == env()->Object_klass()) { + return dest_method; + } + + // All other interesting cases are instance klasses. + if (!receiver_type->isa_instptr()) { + return NULL; + } + + ciInstanceKlass *ikl = receiver_type->klass()->as_instance_klass(); + if (ikl->is_loaded() && ikl->is_initialized() && !ikl->is_interface() && + (ikl == actual_receiver || ikl->is_subclass_of(actual_receiver))) { + // ikl is a same or better type than the original actual_receiver, + // e.g. static receiver from bytecodes. + actual_receiver = ikl; + // Is the actual_receiver exact? + actual_receiver_is_exact = receiver_type->klass_is_exact(); + } + } + + ciInstanceKlass* calling_klass = caller->holder(); + ciMethod* cha_monomorphic_target = dest_method->find_monomorphic_target(calling_klass, klass, actual_receiver); + if (cha_monomorphic_target != NULL) { + assert(!cha_monomorphic_target->is_abstract(), ""); + // Look at the method-receiver type. Does it add "too much information"? + ciKlass* mr_klass = cha_monomorphic_target->holder(); + const Type* mr_type = TypeInstPtr::make(TypePtr::BotPTR, mr_klass); + if (receiver_type == NULL || !receiver_type->higher_equal(mr_type)) { + // Calling this method would include an implicit cast to its holder. + // %%% Not yet implemented. Would throw minor asserts at present. + // %%% The most common wins are already gained by +UseUniqueSubclasses. + // To fix, put the higher_equal check at the call of this routine, + // and add a CheckCastPP to the receiver. + if (TraceDependencies) { + tty->print_cr("found unique CHA method, but could not cast up"); + tty->print(" method = "); + cha_monomorphic_target->print(); + tty->cr(); + } + if (C->log() != NULL) { + C->log()->elem("missed_CHA_opportunity klass='%d' method='%d'", + C->log()->identify(klass), + C->log()->identify(cha_monomorphic_target)); + } + cha_monomorphic_target = NULL; + } + } + if (cha_monomorphic_target != NULL) { + // Hardwiring a virtual. + // If we inlined because CHA revealed only a single target method, + // then we are dependent on that target method not getting overridden + // by dynamic class loading. Be sure to test the "static" receiver + // dest_method here, as opposed to the actual receiver, which may + // falsely lead us to believe that the receiver is final or private. + C->dependencies()->assert_unique_concrete_method(actual_receiver, cha_monomorphic_target); + return cha_monomorphic_target; + } + + // If the type is exact, we can still bind the method w/o a vcall. + // (This case comes after CHA so we can see how much extra work it does.) + if (actual_receiver_is_exact) { + // In case of evolution, there is a dependence on every inlined method, since each + // such method can be changed when its class is redefined. + ciMethod* exact_method = dest_method->resolve_invoke(calling_klass, actual_receiver); + if (exact_method != NULL) { +#ifndef PRODUCT + if (PrintOpto) { + tty->print(" Calling method via exact type @%d --- ", bci); + exact_method->print_name(); + tty->cr(); + } +#endif + return exact_method; + } + } + + return NULL; +} diff --git a/hotspot/src/share/vm/opto/domgraph.cpp b/hotspot/src/share/vm/opto/domgraph.cpp new file mode 100644 index 00000000000..2ef02fd0cec --- /dev/null +++ b/hotspot/src/share/vm/opto/domgraph.cpp @@ -0,0 +1,664 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_domgraph.cpp.incl" + +//------------------------------Tarjan----------------------------------------- +// A data structure that holds all the information needed to find dominators. +struct Tarjan { + Block *_block; // Basic block for this info + + uint _semi; // Semi-dominators + uint _size; // Used for faster LINK and EVAL + Tarjan *_parent; // Parent in DFS + Tarjan *_label; // Used for LINK and EVAL + Tarjan *_ancestor; // Used for LINK and EVAL + Tarjan *_child; // Used for faster LINK and EVAL + Tarjan *_dom; // Parent in dominator tree (immediate dom) + Tarjan *_bucket; // Set of vertices with given semidominator + + Tarjan *_dom_child; // Child in dominator tree + Tarjan *_dom_next; // Next in dominator tree + + // Fast union-find work + void COMPRESS(); + Tarjan *EVAL(void); + void LINK( Tarjan *w, Tarjan *tarjan0 ); + + void setdepth( uint size ); + +}; + +//------------------------------Dominator-------------------------------------- +// Compute the dominator tree of the CFG. The CFG must already have been +// constructed. This is the Lengauer & Tarjan O(E-alpha(E,V)) algorithm. +void PhaseCFG::Dominators( ) { + // Pre-grow the blocks array, prior to the ResourceMark kicking in + _blocks.map(_num_blocks,0); + + ResourceMark rm; + // Setup mappings from my Graph to Tarjan's stuff and back + // Note: Tarjan uses 1-based arrays + Tarjan *tarjan = NEW_RESOURCE_ARRAY(Tarjan,_num_blocks+1); + + // Tarjan's algorithm, almost verbatim: + // Step 1: + _rpo_ctr = _num_blocks; + uint dfsnum = DFS( tarjan ); + if( dfsnum-1 != _num_blocks ) {// Check for unreachable loops! + // If the returned dfsnum does not match the number of blocks, then we + // must have some unreachable loops. These can be made at any time by + // IterGVN. They are cleaned up by CCP or the loop opts, but the last + // IterGVN can always make more that are not cleaned up. Highly unlikely + // except in ZKM.jar, where endless irreducible loops cause the loop opts + // to not get run. + // + // Having found unreachable loops, we have made a bad RPO _block layout. + // We can re-run the above DFS pass with the correct number of blocks, + // and hack the Tarjan algorithm below to be robust in the presence of + // such dead loops (as was done for the NTarjan code farther below). + // Since this situation is so unlikely, instead I've decided to bail out. + // CNC 7/24/2001 + C->record_method_not_compilable("unreachable loop"); + return; + } + _blocks._cnt = _num_blocks; + + // Tarjan is using 1-based arrays, so these are some initialize flags + tarjan[0]._size = tarjan[0]._semi = 0; + tarjan[0]._label = &tarjan[0]; + + uint i; + for( i=_num_blocks; i>=2; i-- ) { // For all vertices in DFS order + Tarjan *w = &tarjan[i]; // Get vertex from DFS + + // Step 2: + Node *whead = w->_block->head(); + for( uint j=1; j < whead->req(); j++ ) { + Block *b = _bbs[whead->in(j)->_idx]; + Tarjan *vx = &tarjan[b->_pre_order]; + Tarjan *u = vx->EVAL(); + if( u->_semi < w->_semi ) + w->_semi = u->_semi; + } + + // w is added to a bucket here, and only here. + // Thus w is in at most one bucket and the sum of all bucket sizes is O(n). + // Thus bucket can be a linked list. + // Thus we do not need a small integer name for each Block. + w->_bucket = tarjan[w->_semi]._bucket; + tarjan[w->_semi]._bucket = w; + + w->_parent->LINK( w, &tarjan[0] ); + + // Step 3: + for( Tarjan *vx = w->_parent->_bucket; vx; vx = vx->_bucket ) { + Tarjan *u = vx->EVAL(); + vx->_dom = (u->_semi < vx->_semi) ? u : w->_parent; + } + } + + // Step 4: + for( i=2; i <= _num_blocks; i++ ) { + Tarjan *w = &tarjan[i]; + if( w->_dom != &tarjan[w->_semi] ) + w->_dom = w->_dom->_dom; + w->_dom_next = w->_dom_child = NULL; // Initialize for building tree later + } + // No immediate dominator for the root + Tarjan *w = &tarjan[_broot->_pre_order]; + w->_dom = NULL; + w->_dom_next = w->_dom_child = NULL; // Initialize for building tree later + + // Convert the dominator tree array into my kind of graph + for( i=1; i<=_num_blocks;i++){// For all Tarjan vertices + Tarjan *t = &tarjan[i]; // Handy access + Tarjan *tdom = t->_dom; // Handy access to immediate dominator + if( tdom ) { // Root has no immediate dominator + t->_block->_idom = tdom->_block; // Set immediate dominator + t->_dom_next = tdom->_dom_child; // Make me a sibling of parent's child + tdom->_dom_child = t; // Make me a child of my parent + } else + t->_block->_idom = NULL; // Root + } + w->setdepth( _num_blocks+1 ); // Set depth in dominator tree + +} + +//----------------------------Block_Stack-------------------------------------- +class Block_Stack { + private: + struct Block_Descr { + Block *block; // Block + int index; // Index of block's successor pushed on stack + int freq_idx; // Index of block's most frequent successor + }; + Block_Descr *_stack_top; + Block_Descr *_stack_max; + Block_Descr *_stack; + Tarjan *_tarjan; + uint most_frequent_successor( Block *b ); + public: + Block_Stack(Tarjan *tarjan, int size) : _tarjan(tarjan) { + _stack = NEW_RESOURCE_ARRAY(Block_Descr, size); + _stack_max = _stack + size; + _stack_top = _stack - 1; // stack is empty + } + void push(uint pre_order, Block *b) { + Tarjan *t = &_tarjan[pre_order]; // Fast local access + b->_pre_order = pre_order; // Flag as visited + t->_block = b; // Save actual block + t->_semi = pre_order; // Block to DFS map + t->_label = t; // DFS to vertex map + t->_ancestor = NULL; // Fast LINK & EVAL setup + t->_child = &_tarjan[0]; // Sentenial + t->_size = 1; + t->_bucket = NULL; + if (pre_order == 1) + t->_parent = NULL; // first block doesn't have parent + else { + // Save parent (currernt top block on stack) in DFS + t->_parent = &_tarjan[_stack_top->block->_pre_order]; + } + // Now put this block on stack + ++_stack_top; + assert(_stack_top < _stack_max, ""); // assert if stack have to grow + _stack_top->block = b; + _stack_top->index = -1; + // Find the index into b->succs[] array of the most frequent successor. + _stack_top->freq_idx = most_frequent_successor(b); // freq_idx >= 0 + } + Block* pop() { Block* b = _stack_top->block; _stack_top--; return b; } + bool is_nonempty() { return (_stack_top >= _stack); } + bool last_successor() { return (_stack_top->index == _stack_top->freq_idx); } + Block* next_successor() { + int i = _stack_top->index; + i++; + if (i == _stack_top->freq_idx) i++; + if (i >= (int)(_stack_top->block->_num_succs)) { + i = _stack_top->freq_idx; // process most frequent successor last + } + _stack_top->index = i; + return _stack_top->block->_succs[ i ]; + } +}; + +//-------------------------most_frequent_successor----------------------------- +// Find the index into the b->succs[] array of the most frequent successor. +uint Block_Stack::most_frequent_successor( Block *b ) { + uint freq_idx = 0; + int eidx = b->end_idx(); + Node *n = b->_nodes[eidx]; + int op = n->is_Mach() ? n->as_Mach()->ideal_Opcode() : n->Opcode(); + switch( op ) { + case Op_CountedLoopEnd: + case Op_If: { // Split frequency amongst children + float prob = n->as_MachIf()->_prob; + // Is succ[0] the TRUE branch or the FALSE branch? + if( b->_nodes[eidx+1]->Opcode() == Op_IfFalse ) + prob = 1.0f - prob; + freq_idx = prob < PROB_FAIR; // freq=1 for succ[0] < 0.5 prob + break; + } + case Op_Catch: // Split frequency amongst children + for( freq_idx = 0; freq_idx < b->_num_succs; freq_idx++ ) + if( b->_nodes[eidx+1+freq_idx]->as_CatchProj()->_con == CatchProjNode::fall_through_index ) + break; + // Handle case of no fall-thru (e.g., check-cast MUST throw an exception) + if( freq_idx == b->_num_succs ) freq_idx = 0; + break; + // Currently there is no support for finding out the most + // frequent successor for jumps, so lets just make it the first one + case Op_Jump: + case Op_Root: + case Op_Goto: + case Op_NeverBranch: + freq_idx = 0; // fall thru + break; + case Op_TailCall: + case Op_TailJump: + case Op_Return: + case Op_Halt: + case Op_Rethrow: + break; + default: + ShouldNotReachHere(); + } + return freq_idx; +} + +//------------------------------DFS-------------------------------------------- +// Perform DFS search. Setup 'vertex' as DFS to vertex mapping. Setup +// 'semi' as vertex to DFS mapping. Set 'parent' to DFS parent. +uint PhaseCFG::DFS( Tarjan *tarjan ) { + Block *b = _broot; + uint pre_order = 1; + // Allocate stack of size _num_blocks+1 to avoid frequent realloc + Block_Stack bstack(tarjan, _num_blocks+1); + + // Push on stack the state for the first block + bstack.push(pre_order, b); + ++pre_order; + + while (bstack.is_nonempty()) { + if (!bstack.last_successor()) { + // Walk over all successors in pre-order (DFS). + Block *s = bstack.next_successor(); + if (s->_pre_order == 0) { // Check for no-pre-order, not-visited + // Push on stack the state of successor + bstack.push(pre_order, s); + ++pre_order; + } + } + else { + // Build a reverse post-order in the CFG _blocks array + Block *stack_top = bstack.pop(); + stack_top->_rpo = --_rpo_ctr; + _blocks.map(stack_top->_rpo, stack_top); + } + } + return pre_order; +} + +//------------------------------COMPRESS--------------------------------------- +void Tarjan::COMPRESS() +{ + assert( _ancestor != 0, "" ); + if( _ancestor->_ancestor != 0 ) { + _ancestor->COMPRESS( ); + if( _ancestor->_label->_semi < _label->_semi ) + _label = _ancestor->_label; + _ancestor = _ancestor->_ancestor; + } +} + +//------------------------------EVAL------------------------------------------- +Tarjan *Tarjan::EVAL() { + if( !_ancestor ) return _label; + COMPRESS(); + return (_ancestor->_label->_semi >= _label->_semi) ? _label : _ancestor->_label; +} + +//------------------------------LINK------------------------------------------- +void Tarjan::LINK( Tarjan *w, Tarjan *tarjan0 ) { + Tarjan *s = w; + while( w->_label->_semi < s->_child->_label->_semi ) { + if( s->_size + s->_child->_child->_size >= (s->_child->_size << 1) ) { + s->_child->_ancestor = s; + s->_child = s->_child->_child; + } else { + s->_child->_size = s->_size; + s = s->_ancestor = s->_child; + } + } + s->_label = w->_label; + _size += w->_size; + if( _size < (w->_size << 1) ) { + Tarjan *tmp = s; s = _child; _child = tmp; + } + while( s != tarjan0 ) { + s->_ancestor = this; + s = s->_child; + } +} + +//------------------------------setdepth--------------------------------------- +void Tarjan::setdepth( uint stack_size ) { + Tarjan **top = NEW_RESOURCE_ARRAY(Tarjan*, stack_size); + Tarjan **next = top; + Tarjan **last; + uint depth = 0; + *top = this; + ++top; + do { + // next level + ++depth; + last = top; + do { + // Set current depth for all tarjans on this level + Tarjan *t = *next; // next tarjan from stack + ++next; + do { + t->_block->_dom_depth = depth; // Set depth in dominator tree + Tarjan *dom_child = t->_dom_child; + t = t->_dom_next; // next tarjan + if (dom_child != NULL) { + *top = dom_child; // save child on stack + ++top; + } + } while (t != NULL); + } while (next < last); + } while (last < top); +} + +//*********************** DOMINATORS ON THE SEA OF NODES*********************** +//------------------------------NTarjan---------------------------------------- +// A data structure that holds all the information needed to find dominators. +struct NTarjan { + Node *_control; // Control node associated with this info + + uint _semi; // Semi-dominators + uint _size; // Used for faster LINK and EVAL + NTarjan *_parent; // Parent in DFS + NTarjan *_label; // Used for LINK and EVAL + NTarjan *_ancestor; // Used for LINK and EVAL + NTarjan *_child; // Used for faster LINK and EVAL + NTarjan *_dom; // Parent in dominator tree (immediate dom) + NTarjan *_bucket; // Set of vertices with given semidominator + + NTarjan *_dom_child; // Child in dominator tree + NTarjan *_dom_next; // Next in dominator tree + + // Perform DFS search. + // Setup 'vertex' as DFS to vertex mapping. + // Setup 'semi' as vertex to DFS mapping. + // Set 'parent' to DFS parent. + static int DFS( NTarjan *ntarjan, VectorSet &visited, PhaseIdealLoop *pil, uint *dfsorder ); + void setdepth( uint size, uint *dom_depth ); + + // Fast union-find work + void COMPRESS(); + NTarjan *EVAL(void); + void LINK( NTarjan *w, NTarjan *ntarjan0 ); +#ifndef PRODUCT + void dump(int offset) const; +#endif +}; + +//------------------------------Dominator-------------------------------------- +// Compute the dominator tree of the sea of nodes. This version walks all CFG +// nodes (using the is_CFG() call) and places them in a dominator tree. Thus, +// it needs a count of the CFG nodes for the mapping table. This is the +// Lengauer & Tarjan O(E-alpha(E,V)) algorithm. +void PhaseIdealLoop::Dominators( ) { + ResourceMark rm; + // Setup mappings from my Graph to Tarjan's stuff and back + // Note: Tarjan uses 1-based arrays + NTarjan *ntarjan = NEW_RESOURCE_ARRAY(NTarjan,C->unique()+1); + // Initialize _control field for fast reference + int i; + for( i= C->unique()-1; i>=0; i-- ) + ntarjan[i]._control = NULL; + + // Store the DFS order for the main loop + uint *dfsorder = NEW_RESOURCE_ARRAY(uint,C->unique()+1); + memset(dfsorder, max_uint, (C->unique()+1) * sizeof(uint)); + + // Tarjan's algorithm, almost verbatim: + // Step 1: + VectorSet visited(Thread::current()->resource_area()); + int dfsnum = NTarjan::DFS( ntarjan, visited, this, dfsorder); + + // Tarjan is using 1-based arrays, so these are some initialize flags + ntarjan[0]._size = ntarjan[0]._semi = 0; + ntarjan[0]._label = &ntarjan[0]; + + for( i = dfsnum-1; i>1; i-- ) { // For all nodes in reverse DFS order + NTarjan *w = &ntarjan[i]; // Get Node from DFS + assert(w->_control != NULL,"bad DFS walk"); + + // Step 2: + Node *whead = w->_control; + for( uint j=0; j < whead->req(); j++ ) { // For each predecessor + if( whead->in(j) == NULL || !whead->in(j)->is_CFG() ) + continue; // Only process control nodes + uint b = dfsorder[whead->in(j)->_idx]; + if(b == max_uint) continue; + NTarjan *vx = &ntarjan[b]; + NTarjan *u = vx->EVAL(); + if( u->_semi < w->_semi ) + w->_semi = u->_semi; + } + + // w is added to a bucket here, and only here. + // Thus w is in at most one bucket and the sum of all bucket sizes is O(n). + // Thus bucket can be a linked list. + w->_bucket = ntarjan[w->_semi]._bucket; + ntarjan[w->_semi]._bucket = w; + + w->_parent->LINK( w, &ntarjan[0] ); + + // Step 3: + for( NTarjan *vx = w->_parent->_bucket; vx; vx = vx->_bucket ) { + NTarjan *u = vx->EVAL(); + vx->_dom = (u->_semi < vx->_semi) ? u : w->_parent; + } + + // Cleanup any unreachable loops now. Unreachable loops are loops that + // flow into the main graph (and hence into ROOT) but are not reachable + // from above. Such code is dead, but requires a global pass to detect + // it; this global pass was the 'build_loop_tree' pass run just prior. + if( whead->is_Region() ) { + for( uint i = 1; i < whead->req(); i++ ) { + if (!has_node(whead->in(i))) { + // Kill dead input path + assert( !visited.test(whead->in(i)->_idx), + "input with no loop must be dead" ); + _igvn.hash_delete(whead); + whead->del_req(i); + _igvn._worklist.push(whead); + for (DUIterator_Fast jmax, j = whead->fast_outs(jmax); j < jmax; j++) { + Node* p = whead->fast_out(j); + if( p->is_Phi() ) { + _igvn.hash_delete(p); + p->del_req(i); + _igvn._worklist.push(p); + } + } + i--; // Rerun same iteration + } // End of if dead input path + } // End of for all input paths + } // End if if whead is a Region + } // End of for all Nodes in reverse DFS order + + // Step 4: + for( i=2; i < dfsnum; i++ ) { // DFS order + NTarjan *w = &ntarjan[i]; + assert(w->_control != NULL,"Bad DFS walk"); + if( w->_dom != &ntarjan[w->_semi] ) + w->_dom = w->_dom->_dom; + w->_dom_next = w->_dom_child = NULL; // Initialize for building tree later + } + // No immediate dominator for the root + NTarjan *w = &ntarjan[dfsorder[C->root()->_idx]]; + w->_dom = NULL; + w->_parent = NULL; + w->_dom_next = w->_dom_child = NULL; // Initialize for building tree later + + // Convert the dominator tree array into my kind of graph + for( i=1; i_control != NULL,"Bad DFS walk"); + NTarjan *tdom = t->_dom; // Handy access to immediate dominator + if( tdom ) { // Root has no immediate dominator + _idom[t->_control->_idx] = tdom->_control; // Set immediate dominator + t->_dom_next = tdom->_dom_child; // Make me a sibling of parent's child + tdom->_dom_child = t; // Make me a child of my parent + } else + _idom[C->root()->_idx] = NULL; // Root + } + w->setdepth( C->unique()+1, _dom_depth ); // Set depth in dominator tree + // Pick up the 'top' node as well + _idom [C->top()->_idx] = C->root(); + _dom_depth[C->top()->_idx] = 1; + + // Debug Print of Dominator tree + if( PrintDominators ) { +#ifndef PRODUCT + w->dump(0); +#endif + } +} + +//------------------------------DFS-------------------------------------------- +// Perform DFS search. Setup 'vertex' as DFS to vertex mapping. Setup +// 'semi' as vertex to DFS mapping. Set 'parent' to DFS parent. +int NTarjan::DFS( NTarjan *ntarjan, VectorSet &visited, PhaseIdealLoop *pil, uint *dfsorder) { + // Allocate stack of size C->unique()/8 to avoid frequent realloc + GrowableArray dfstack(pil->C->unique() >> 3); + Node *b = pil->C->root(); + int dfsnum = 1; + dfsorder[b->_idx] = dfsnum; // Cache parent's dfsnum for a later use + dfstack.push(b); + + while (dfstack.is_nonempty()) { + b = dfstack.pop(); + if( !visited.test_set(b->_idx) ) { // Test node and flag it as visited + NTarjan *w = &ntarjan[dfsnum]; + // Only fully process control nodes + w->_control = b; // Save actual node + // Use parent's cached dfsnum to identify "Parent in DFS" + w->_parent = &ntarjan[dfsorder[b->_idx]]; + dfsorder[b->_idx] = dfsnum; // Save DFS order info + w->_semi = dfsnum; // Node to DFS map + w->_label = w; // DFS to vertex map + w->_ancestor = NULL; // Fast LINK & EVAL setup + w->_child = &ntarjan[0]; // Sentinal + w->_size = 1; + w->_bucket = NULL; + + // Need DEF-USE info for this pass + for ( int i = b->outcnt(); i-- > 0; ) { // Put on stack backwards + Node* s = b->raw_out(i); // Get a use + // CFG nodes only and not dead stuff + if( s->is_CFG() && pil->has_node(s) && !visited.test(s->_idx) ) { + dfsorder[s->_idx] = dfsnum; // Cache parent's dfsnum for a later use + dfstack.push(s); + } + } + dfsnum++; // update after parent's dfsnum has been cached. + } + } + + return dfsnum; +} + +//------------------------------COMPRESS--------------------------------------- +void NTarjan::COMPRESS() +{ + assert( _ancestor != 0, "" ); + if( _ancestor->_ancestor != 0 ) { + _ancestor->COMPRESS( ); + if( _ancestor->_label->_semi < _label->_semi ) + _label = _ancestor->_label; + _ancestor = _ancestor->_ancestor; + } +} + +//------------------------------EVAL------------------------------------------- +NTarjan *NTarjan::EVAL() { + if( !_ancestor ) return _label; + COMPRESS(); + return (_ancestor->_label->_semi >= _label->_semi) ? _label : _ancestor->_label; +} + +//------------------------------LINK------------------------------------------- +void NTarjan::LINK( NTarjan *w, NTarjan *ntarjan0 ) { + NTarjan *s = w; + while( w->_label->_semi < s->_child->_label->_semi ) { + if( s->_size + s->_child->_child->_size >= (s->_child->_size << 1) ) { + s->_child->_ancestor = s; + s->_child = s->_child->_child; + } else { + s->_child->_size = s->_size; + s = s->_ancestor = s->_child; + } + } + s->_label = w->_label; + _size += w->_size; + if( _size < (w->_size << 1) ) { + NTarjan *tmp = s; s = _child; _child = tmp; + } + while( s != ntarjan0 ) { + s->_ancestor = this; + s = s->_child; + } +} + +//------------------------------setdepth--------------------------------------- +void NTarjan::setdepth( uint stack_size, uint *dom_depth ) { + NTarjan **top = NEW_RESOURCE_ARRAY(NTarjan*, stack_size); + NTarjan **next = top; + NTarjan **last; + uint depth = 0; + *top = this; + ++top; + do { + // next level + ++depth; + last = top; + do { + // Set current depth for all tarjans on this level + NTarjan *t = *next; // next tarjan from stack + ++next; + do { + dom_depth[t->_control->_idx] = depth; // Set depth in dominator tree + NTarjan *dom_child = t->_dom_child; + t = t->_dom_next; // next tarjan + if (dom_child != NULL) { + *top = dom_child; // save child on stack + ++top; + } + } while (t != NULL); + } while (next < last); + } while (last < top); +} + +//------------------------------dump------------------------------------------- +#ifndef PRODUCT +void NTarjan::dump(int offset) const { + // Dump the data from this node + int i; + for(i = offset; i >0; i--) // Use indenting for tree structure + tty->print(" "); + tty->print("Dominator Node: "); + _control->dump(); // Control node for this dom node + tty->print("\n"); + for(i = offset; i >0; i--) // Use indenting for tree structure + tty->print(" "); + tty->print("semi:%d, size:%d\n",_semi, _size); + for(i = offset; i >0; i--) // Use indenting for tree structure + tty->print(" "); + tty->print("DFS Parent: "); + if(_parent != NULL) + _parent->_control->dump(); // Parent in DFS + tty->print("\n"); + for(i = offset; i >0; i--) // Use indenting for tree structure + tty->print(" "); + tty->print("Dom Parent: "); + if(_dom != NULL) + _dom->_control->dump(); // Parent in Dominator Tree + tty->print("\n"); + + // Recurse over remaining tree + if( _dom_child ) _dom_child->dump(offset+2); // Children in dominator tree + if( _dom_next ) _dom_next ->dump(offset ); // Siblings in dominator tree + +} +#endif diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp new file mode 100644 index 00000000000..62d0c0f1ac5 --- /dev/null +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -0,0 +1,1346 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_escape.cpp.incl" + +uint PointsToNode::edge_target(uint e) const { + assert(_edges != NULL && e < (uint)_edges->length(), "valid edge index"); + return (_edges->at(e) >> EdgeShift); +} + +PointsToNode::EdgeType PointsToNode::edge_type(uint e) const { + assert(_edges != NULL && e < (uint)_edges->length(), "valid edge index"); + return (EdgeType) (_edges->at(e) & EdgeMask); +} + +void PointsToNode::add_edge(uint targIdx, PointsToNode::EdgeType et) { + uint v = (targIdx << EdgeShift) + ((uint) et); + if (_edges == NULL) { + Arena *a = Compile::current()->comp_arena(); + _edges = new(a) GrowableArray(a, INITIAL_EDGE_COUNT, 0, 0); + } + _edges->append_if_missing(v); +} + +void PointsToNode::remove_edge(uint targIdx, PointsToNode::EdgeType et) { + uint v = (targIdx << EdgeShift) + ((uint) et); + + _edges->remove(v); +} + +#ifndef PRODUCT +static char *node_type_names[] = { + "UnknownType", + "JavaObject", + "LocalVar", + "Field" +}; + +static char *esc_names[] = { + "UnknownEscape", + "NoEscape ", + "ArgEscape ", + "GlobalEscape " +}; + +static char *edge_type_suffix[] = { + "?", // UnknownEdge + "P", // PointsToEdge + "D", // DeferredEdge + "F" // FieldEdge +}; + +void PointsToNode::dump() const { + NodeType nt = node_type(); + EscapeState es = escape_state(); + tty->print("%s %s [[", node_type_names[(int) nt], esc_names[(int) es]); + for (uint i = 0; i < edge_count(); i++) { + tty->print(" %d%s", edge_target(i), edge_type_suffix[(int) edge_type(i)]); + } + tty->print("]] "); + if (_node == NULL) + tty->print_cr(""); + else + _node->dump(); +} +#endif + +ConnectionGraph::ConnectionGraph(Compile * C) : _processed(C->comp_arena()), _node_map(C->comp_arena()) { + _collecting = true; + this->_compile = C; + const PointsToNode &dummy = PointsToNode(); + _nodes = new(C->comp_arena()) GrowableArray(C->comp_arena(), (int) INITIAL_NODE_COUNT, 0, dummy); + _phantom_object = C->top()->_idx; + PointsToNode *phn = ptnode_adr(_phantom_object); + phn->set_node_type(PointsToNode::JavaObject); + phn->set_escape_state(PointsToNode::GlobalEscape); +} + +void ConnectionGraph::add_pointsto_edge(uint from_i, uint to_i) { + PointsToNode *f = ptnode_adr(from_i); + PointsToNode *t = ptnode_adr(to_i); + + assert(f->node_type() != PointsToNode::UnknownType && t->node_type() != PointsToNode::UnknownType, "node types must be set"); + assert(f->node_type() == PointsToNode::LocalVar || f->node_type() == PointsToNode::Field, "invalid source of PointsTo edge"); + assert(t->node_type() == PointsToNode::JavaObject, "invalid destination of PointsTo edge"); + f->add_edge(to_i, PointsToNode::PointsToEdge); +} + +void ConnectionGraph::add_deferred_edge(uint from_i, uint to_i) { + PointsToNode *f = ptnode_adr(from_i); + PointsToNode *t = ptnode_adr(to_i); + + assert(f->node_type() != PointsToNode::UnknownType && t->node_type() != PointsToNode::UnknownType, "node types must be set"); + assert(f->node_type() == PointsToNode::LocalVar || f->node_type() == PointsToNode::Field, "invalid source of Deferred edge"); + assert(t->node_type() == PointsToNode::LocalVar || t->node_type() == PointsToNode::Field, "invalid destination of Deferred edge"); + // don't add a self-referential edge, this can occur during removal of + // deferred edges + if (from_i != to_i) + f->add_edge(to_i, PointsToNode::DeferredEdge); +} + +int ConnectionGraph::type_to_offset(const Type *t) { + const TypePtr *t_ptr = t->isa_ptr(); + assert(t_ptr != NULL, "must be a pointer type"); + return t_ptr->offset(); +} + +void ConnectionGraph::add_field_edge(uint from_i, uint to_i, int offset) { + PointsToNode *f = ptnode_adr(from_i); + PointsToNode *t = ptnode_adr(to_i); + + assert(f->node_type() != PointsToNode::UnknownType && t->node_type() != PointsToNode::UnknownType, "node types must be set"); + assert(f->node_type() == PointsToNode::JavaObject, "invalid destination of Field edge"); + assert(t->node_type() == PointsToNode::Field, "invalid destination of Field edge"); + assert (t->offset() == -1 || t->offset() == offset, "conflicting field offsets"); + t->set_offset(offset); + + f->add_edge(to_i, PointsToNode::FieldEdge); +} + +void ConnectionGraph::set_escape_state(uint ni, PointsToNode::EscapeState es) { + PointsToNode *npt = ptnode_adr(ni); + PointsToNode::EscapeState old_es = npt->escape_state(); + if (es > old_es) + npt->set_escape_state(es); +} + +PointsToNode::EscapeState ConnectionGraph::escape_state(Node *n, PhaseTransform *phase) { + uint idx = n->_idx; + PointsToNode::EscapeState es; + + // If we are still collecting we don't know the answer yet + if (_collecting) + return PointsToNode::UnknownEscape; + + // if the node was created after the escape computation, return + // UnknownEscape + if (idx >= (uint)_nodes->length()) + return PointsToNode::UnknownEscape; + + es = _nodes->at_grow(idx).escape_state(); + + // if we have already computed a value, return it + if (es != PointsToNode::UnknownEscape) + return es; + + // compute max escape state of anything this node could point to + VectorSet ptset(Thread::current()->resource_area()); + PointsTo(ptset, n, phase); + for( VectorSetI i(&ptset); i.test() && es != PointsToNode::GlobalEscape; ++i ) { + uint pt = i.elem; + PointsToNode::EscapeState pes = _nodes->at(pt).escape_state(); + if (pes > es) + es = pes; + } + // cache the computed escape state + assert(es != PointsToNode::UnknownEscape, "should have computed an escape state"); + _nodes->adr_at(idx)->set_escape_state(es); + return es; +} + +void ConnectionGraph::PointsTo(VectorSet &ptset, Node * n, PhaseTransform *phase) { + VectorSet visited(Thread::current()->resource_area()); + GrowableArray worklist; + + n = skip_casts(n); + PointsToNode npt = _nodes->at_grow(n->_idx); + + // If we have a JavaObject, return just that object + if (npt.node_type() == PointsToNode::JavaObject) { + ptset.set(n->_idx); + return; + } + // we may have a Phi which has not been processed + if (npt._node == NULL) { + assert(n->is_Phi(), "unprocessed node must be a Phi"); + record_for_escape_analysis(n); + npt = _nodes->at(n->_idx); + } + worklist.push(n->_idx); + while(worklist.length() > 0) { + int ni = worklist.pop(); + PointsToNode pn = _nodes->at_grow(ni); + if (!visited.test(ni)) { + visited.set(ni); + + // ensure that all inputs of a Phi have been processed + if (_collecting && pn._node->is_Phi()) { + PhiNode *phi = pn._node->as_Phi(); + process_phi_escape(phi, phase); + } + + int edges_processed = 0; + for (uint e = 0; e < pn.edge_count(); e++) { + PointsToNode::EdgeType et = pn.edge_type(e); + if (et == PointsToNode::PointsToEdge) { + ptset.set(pn.edge_target(e)); + edges_processed++; + } else if (et == PointsToNode::DeferredEdge) { + worklist.push(pn.edge_target(e)); + edges_processed++; + } + } + if (edges_processed == 0) { + // no deferred or pointsto edges found. Assume the value was set outside + // this method. Add the phantom object to the pointsto set. + ptset.set(_phantom_object); + } + } + } +} + +void ConnectionGraph::remove_deferred(uint ni) { + VectorSet visited(Thread::current()->resource_area()); + + uint i = 0; + PointsToNode *ptn = ptnode_adr(ni); + + while(i < ptn->edge_count()) { + if (ptn->edge_type(i) != PointsToNode::DeferredEdge) { + i++; + } else { + uint t = ptn->edge_target(i); + PointsToNode *ptt = ptnode_adr(t); + ptn->remove_edge(t, PointsToNode::DeferredEdge); + if(!visited.test(t)) { + visited.set(t); + for (uint j = 0; j < ptt->edge_count(); j++) { + uint n1 = ptt->edge_target(j); + PointsToNode *pt1 = ptnode_adr(n1); + switch(ptt->edge_type(j)) { + case PointsToNode::PointsToEdge: + add_pointsto_edge(ni, n1); + break; + case PointsToNode::DeferredEdge: + add_deferred_edge(ni, n1); + break; + case PointsToNode::FieldEdge: + assert(false, "invalid connection graph"); + break; + } + } + } + } + } +} + + +// Add an edge to node given by "to_i" from any field of adr_i whose offset +// matches "offset" A deferred edge is added if to_i is a LocalVar, and +// a pointsto edge is added if it is a JavaObject + +void ConnectionGraph::add_edge_from_fields(uint adr_i, uint to_i, int offs) { + PointsToNode an = _nodes->at_grow(adr_i); + PointsToNode to = _nodes->at_grow(to_i); + bool deferred = (to.node_type() == PointsToNode::LocalVar); + + for (uint fe = 0; fe < an.edge_count(); fe++) { + assert(an.edge_type(fe) == PointsToNode::FieldEdge, "expecting a field edge"); + int fi = an.edge_target(fe); + PointsToNode pf = _nodes->at_grow(fi); + int po = pf.offset(); + if (po == offs || po == Type::OffsetBot || offs == Type::OffsetBot) { + if (deferred) + add_deferred_edge(fi, to_i); + else + add_pointsto_edge(fi, to_i); + } + } +} + +// Add a deferred edge from node given by "from_i" to any field of adr_i whose offset +// matches "offset" +void ConnectionGraph::add_deferred_edge_to_fields(uint from_i, uint adr_i, int offs) { + PointsToNode an = _nodes->at_grow(adr_i); + for (uint fe = 0; fe < an.edge_count(); fe++) { + assert(an.edge_type(fe) == PointsToNode::FieldEdge, "expecting a field edge"); + int fi = an.edge_target(fe); + PointsToNode pf = _nodes->at_grow(fi); + int po = pf.offset(); + if (pf.edge_count() == 0) { + // we have not seen any stores to this field, assume it was set outside this method + add_pointsto_edge(fi, _phantom_object); + } + if (po == offs || po == Type::OffsetBot || offs == Type::OffsetBot) { + add_deferred_edge(from_i, fi); + } + } +} + +// +// Search memory chain of "mem" to find a MemNode whose address +// is the specified alias index. Returns the MemNode found or the +// first non-MemNode encountered. +// +Node *ConnectionGraph::find_mem(Node *mem, int alias_idx, PhaseGVN *igvn) { + if (mem == NULL) + return mem; + while (mem->is_Mem()) { + const Type *at = igvn->type(mem->in(MemNode::Address)); + if (at != Type::TOP) { + assert (at->isa_ptr() != NULL, "pointer type required."); + int idx = _compile->get_alias_index(at->is_ptr()); + if (idx == alias_idx) + break; + } + mem = mem->in(MemNode::Memory); + } + return mem; +} + +// +// Adjust the type and inputs of an AddP which computes the +// address of a field of an instance +// +void ConnectionGraph::split_AddP(Node *addp, Node *base, PhaseGVN *igvn) { + const TypeOopPtr *t = igvn->type(addp)->isa_oopptr(); + const TypeOopPtr *base_t = igvn->type(base)->isa_oopptr(); + assert(t != NULL, "expecting oopptr"); + assert(base_t != NULL && base_t->is_instance(), "expecting instance oopptr"); + uint inst_id = base_t->instance_id(); + assert(!t->is_instance() || t->instance_id() == inst_id, + "old type must be non-instance or match new type"); + const TypeOopPtr *tinst = base_t->add_offset(t->offset())->is_oopptr(); + // ensure an alias index is allocated for the instance type + int alias_idx = _compile->get_alias_index(tinst); + igvn->set_type(addp, tinst); + // record the allocation in the node map + set_map(addp->_idx, get_map(base->_idx)); + // if the Address input is not the appropriate instance type (due to intervening + // casts,) insert a cast + Node *adr = addp->in(AddPNode::Address); + const TypeOopPtr *atype = igvn->type(adr)->isa_oopptr(); + if (atype->instance_id() != inst_id) { + assert(!atype->is_instance(), "no conflicting instances"); + const TypeOopPtr *new_atype = base_t->add_offset(atype->offset())->isa_oopptr(); + Node *acast = new (_compile, 2) CastPPNode(adr, new_atype); + acast->set_req(0, adr->in(0)); + igvn->set_type(acast, new_atype); + record_for_optimizer(acast); + Node *bcast = acast; + Node *abase = addp->in(AddPNode::Base); + if (abase != adr) { + bcast = new (_compile, 2) CastPPNode(abase, base_t); + bcast->set_req(0, abase->in(0)); + igvn->set_type(bcast, base_t); + record_for_optimizer(bcast); + } + igvn->hash_delete(addp); + addp->set_req(AddPNode::Base, bcast); + addp->set_req(AddPNode::Address, acast); + igvn->hash_insert(addp); + record_for_optimizer(addp); + } +} + +// +// Create a new version of orig_phi if necessary. Returns either the newly +// created phi or an existing phi. Sets create_new to indicate wheter a new +// phi was created. Cache the last newly created phi in the node map. +// +PhiNode *ConnectionGraph::create_split_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, PhaseGVN *igvn, bool &new_created) { + Compile *C = _compile; + new_created = false; + int phi_alias_idx = C->get_alias_index(orig_phi->adr_type()); + // nothing to do if orig_phi is bottom memory or matches alias_idx + if (phi_alias_idx == Compile::AliasIdxBot || phi_alias_idx == alias_idx) { + return orig_phi; + } + // have we already created a Phi for this alias index? + PhiNode *result = get_map_phi(orig_phi->_idx); + const TypePtr *atype = C->get_adr_type(alias_idx); + if (result != NULL && C->get_alias_index(result->adr_type()) == alias_idx) { + return result; + } + + orig_phi_worklist.append_if_missing(orig_phi); + result = PhiNode::make(orig_phi->in(0), NULL, Type::MEMORY, atype); + set_map_phi(orig_phi->_idx, result); + igvn->set_type(result, result->bottom_type()); + record_for_optimizer(result); + new_created = true; + return result; +} + +// +// Return a new version of Memory Phi "orig_phi" with the inputs having the +// specified alias index. +// +PhiNode *ConnectionGraph::split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, PhaseGVN *igvn) { + + assert(alias_idx != Compile::AliasIdxBot, "can't split out bottom memory"); + Compile *C = _compile; + bool new_phi_created; + PhiNode *result = create_split_phi(orig_phi, alias_idx, orig_phi_worklist, igvn, new_phi_created); + if (!new_phi_created) { + return result; + } + + GrowableArray phi_list; + GrowableArray cur_input; + + PhiNode *phi = orig_phi; + uint idx = 1; + bool finished = false; + while(!finished) { + while (idx < phi->req()) { + Node *mem = find_mem(phi->in(idx), alias_idx, igvn); + if (mem != NULL && mem->is_Phi()) { + PhiNode *nphi = create_split_phi(mem->as_Phi(), alias_idx, orig_phi_worklist, igvn, new_phi_created); + if (new_phi_created) { + // found an phi for which we created a new split, push current one on worklist and begin + // processing new one + phi_list.push(phi); + cur_input.push(idx); + phi = mem->as_Phi(); + result = nphi; + idx = 1; + continue; + } else { + mem = nphi; + } + } + result->set_req(idx++, mem); + } +#ifdef ASSERT + // verify that the new Phi has an input for each input of the original + assert( phi->req() == result->req(), "must have same number of inputs."); + assert( result->in(0) != NULL && result->in(0) == phi->in(0), "regions must match"); + for (uint i = 1; i < phi->req(); i++) { + assert((phi->in(i) == NULL) == (result->in(i) == NULL), "inputs must correspond."); + } +#endif + // we have finished processing a Phi, see if there are any more to do + finished = (phi_list.length() == 0 ); + if (!finished) { + phi = phi_list.pop(); + idx = cur_input.pop(); + PhiNode *prev_phi = get_map_phi(phi->_idx); + prev_phi->set_req(idx++, result); + result = prev_phi; + } + } + return result; +} + +// +// Convert the types of unescaped object to instance types where possible, +// propagate the new type information through the graph, and update memory +// edges and MergeMem inputs to reflect the new type. +// +// We start with allocations (and calls which may be allocations) on alloc_worklist. +// The processing is done in 4 phases: +// +// Phase 1: Process possible allocations from alloc_worklist. Create instance +// types for the CheckCastPP for allocations where possible. +// Propagate the the new types through users as follows: +// casts and Phi: push users on alloc_worklist +// AddP: cast Base and Address inputs to the instance type +// push any AddP users on alloc_worklist and push any memnode +// users onto memnode_worklist. +// Phase 2: Process MemNode's from memnode_worklist. compute new address type and +// search the Memory chain for a store with the appropriate type +// address type. If a Phi is found, create a new version with +// the approriate memory slices from each of the Phi inputs. +// For stores, process the users as follows: +// MemNode: push on memnode_worklist +// MergeMem: push on mergemem_worklist +// Phase 3: Process MergeMem nodes from mergemem_worklist. Walk each memory slice +// moving the first node encountered of each instance type to the +// the input corresponding to its alias index. +// appropriate memory slice. +// Phase 4: Update the inputs of non-instance memory Phis and the Memory input of memnodes. +// +// In the following example, the CheckCastPP nodes are the cast of allocation +// results and the allocation of node 29 is unescaped and eligible to be an +// instance type. +// +// We start with: +// +// 7 Parm #memory +// 10 ConI "12" +// 19 CheckCastPP "Foo" +// 20 AddP _ 19 19 10 Foo+12 alias_index=4 +// 29 CheckCastPP "Foo" +// 30 AddP _ 29 29 10 Foo+12 alias_index=4 +// +// 40 StoreP 25 7 20 ... alias_index=4 +// 50 StoreP 35 40 30 ... alias_index=4 +// 60 StoreP 45 50 20 ... alias_index=4 +// 70 LoadP _ 60 30 ... alias_index=4 +// 80 Phi 75 50 60 Memory alias_index=4 +// 90 LoadP _ 80 30 ... alias_index=4 +// 100 LoadP _ 80 20 ... alias_index=4 +// +// +// Phase 1 creates an instance type for node 29 assigning it an instance id of 24 +// and creating a new alias index for node 30. This gives: +// +// 7 Parm #memory +// 10 ConI "12" +// 19 CheckCastPP "Foo" +// 20 AddP _ 19 19 10 Foo+12 alias_index=4 +// 29 CheckCastPP "Foo" iid=24 +// 30 AddP _ 29 29 10 Foo+12 alias_index=6 iid=24 +// +// 40 StoreP 25 7 20 ... alias_index=4 +// 50 StoreP 35 40 30 ... alias_index=6 +// 60 StoreP 45 50 20 ... alias_index=4 +// 70 LoadP _ 60 30 ... alias_index=6 +// 80 Phi 75 50 60 Memory alias_index=4 +// 90 LoadP _ 80 30 ... alias_index=6 +// 100 LoadP _ 80 20 ... alias_index=4 +// +// In phase 2, new memory inputs are computed for the loads and stores, +// And a new version of the phi is created. In phase 4, the inputs to +// node 80 are updated and then the memory nodes are updated with the +// values computed in phase 2. This results in: +// +// 7 Parm #memory +// 10 ConI "12" +// 19 CheckCastPP "Foo" +// 20 AddP _ 19 19 10 Foo+12 alias_index=4 +// 29 CheckCastPP "Foo" iid=24 +// 30 AddP _ 29 29 10 Foo+12 alias_index=6 iid=24 +// +// 40 StoreP 25 7 20 ... alias_index=4 +// 50 StoreP 35 7 30 ... alias_index=6 +// 60 StoreP 45 40 20 ... alias_index=4 +// 70 LoadP _ 50 30 ... alias_index=6 +// 80 Phi 75 40 60 Memory alias_index=4 +// 120 Phi 75 50 50 Memory alias_index=6 +// 90 LoadP _ 120 30 ... alias_index=6 +// 100 LoadP _ 80 20 ... alias_index=4 +// +void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) { + GrowableArray memnode_worklist; + GrowableArray mergemem_worklist; + GrowableArray orig_phis; + PhaseGVN *igvn = _compile->initial_gvn(); + uint new_index_start = (uint) _compile->num_alias_types(); + VectorSet visited(Thread::current()->resource_area()); + VectorSet ptset(Thread::current()->resource_area()); + + // Phase 1: Process possible allocations from alloc_worklist. Create instance + // types for the CheckCastPP for allocations where possible. + while (alloc_worklist.length() != 0) { + Node *n = alloc_worklist.pop(); + uint ni = n->_idx; + if (n->is_Call()) { + CallNode *alloc = n->as_Call(); + // copy escape information to call node + PointsToNode ptn = _nodes->at(alloc->_idx); + PointsToNode::EscapeState es = escape_state(alloc, igvn); + alloc->_escape_state = es; + // find CheckCastPP of call return value + n = alloc->proj_out(TypeFunc::Parms); + if (n != NULL && n->outcnt() == 1) { + n = n->unique_out(); + if (n->Opcode() != Op_CheckCastPP) { + continue; + } + } else { + continue; + } + // we have an allocation or call which returns a Java object, see if it is unescaped + if (es != PointsToNode::NoEscape || !ptn._unique_type) { + continue; // can't make a unique type + } + set_map(alloc->_idx, n); + set_map(n->_idx, alloc); + const TypeInstPtr *t = igvn->type(n)->isa_instptr(); + // Unique types which are arrays are not currently supported. + // The check for AllocateArray is needed in case an array + // allocation is immediately cast to Object + if (t == NULL || alloc->is_AllocateArray()) + continue; // not a TypeInstPtr + const TypeOopPtr *tinst = t->cast_to_instance(ni); + igvn->hash_delete(n); + igvn->set_type(n, tinst); + n->raise_bottom_type(tinst); + igvn->hash_insert(n); + } else if (n->is_AddP()) { + ptset.Clear(); + PointsTo(ptset, n->in(AddPNode::Address), igvn); + assert(ptset.Size() == 1, "AddP address is unique"); + Node *base = get_map(ptset.getelem()); + split_AddP(n, base, igvn); + } else if (n->is_Phi() || n->Opcode() == Op_CastPP || n->Opcode() == Op_CheckCastPP) { + if (visited.test_set(n->_idx)) { + assert(n->is_Phi(), "loops only through Phi's"); + continue; // already processed + } + ptset.Clear(); + PointsTo(ptset, n, igvn); + if (ptset.Size() == 1) { + TypeNode *tn = n->as_Type(); + Node *val = get_map(ptset.getelem()); + const TypeInstPtr *val_t = igvn->type(val)->isa_instptr();; + assert(val_t != NULL && val_t->is_instance(), "instance type expected."); + const TypeInstPtr *tn_t = igvn->type(tn)->isa_instptr();; + + if (tn_t != NULL && val_t->cast_to_instance(TypeOopPtr::UNKNOWN_INSTANCE)->higher_equal(tn_t)) { + igvn->hash_delete(tn); + igvn->set_type(tn, val_t); + tn->set_type(val_t); + igvn->hash_insert(tn); + } + } + } else { + continue; + } + // push users on appropriate worklist + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node *use = n->fast_out(i); + if(use->is_Mem() && use->in(MemNode::Address) == n) { + memnode_worklist.push(use); + } else if (use->is_AddP() || use->is_Phi() || use->Opcode() == Op_CastPP || use->Opcode() == Op_CheckCastPP) { + alloc_worklist.push(use); + } + } + + } + uint new_index_end = (uint) _compile->num_alias_types(); + + // Phase 2: Process MemNode's from memnode_worklist. compute new address type and + // compute new values for Memory inputs (the Memory inputs are not + // actually updated until phase 4.) + if (memnode_worklist.length() == 0) + return; // nothing to do + + + while (memnode_worklist.length() != 0) { + Node *n = memnode_worklist.pop(); + if (n->is_Phi()) { + assert(n->as_Phi()->adr_type() != TypePtr::BOTTOM, "narrow memory slice required"); + // we don't need to do anything, but the users must be pushed if we haven't processed + // this Phi before + if (visited.test_set(n->_idx)) + continue; + } else { + assert(n->is_Mem(), "memory node required."); + Node *addr = n->in(MemNode::Address); + const Type *addr_t = igvn->type(addr); + if (addr_t == Type::TOP) + continue; + assert (addr_t->isa_ptr() != NULL, "pointer type required."); + int alias_idx = _compile->get_alias_index(addr_t->is_ptr()); + Node *mem = find_mem(n->in(MemNode::Memory), alias_idx, igvn); + if (mem->is_Phi()) { + mem = split_memory_phi(mem->as_Phi(), alias_idx, orig_phis, igvn); + } + if (mem != n->in(MemNode::Memory)) + set_map(n->_idx, mem); + if (n->is_Load()) { + continue; // don't push users + } else if (n->is_LoadStore()) { + // get the memory projection + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node *use = n->fast_out(i); + if (use->Opcode() == Op_SCMemProj) { + n = use; + break; + } + } + assert(n->Opcode() == Op_SCMemProj, "memory projection required"); + } + } + // push user on appropriate worklist + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node *use = n->fast_out(i); + if (use->is_Phi()) { + memnode_worklist.push(use); + } else if(use->is_Mem() && use->in(MemNode::Memory) == n) { + memnode_worklist.push(use); + } else if (use->is_MergeMem()) { + mergemem_worklist.push(use); + } + } + } + + // Phase 3: Process MergeMem nodes from mergemem_worklist. Walk each memory slice + // moving the first node encountered of each instance type to the + // the input corresponding to its alias index. + while (mergemem_worklist.length() != 0) { + Node *n = mergemem_worklist.pop(); + assert(n->is_MergeMem(), "MergeMem node required."); + MergeMemNode *nmm = n->as_MergeMem(); + // Note: we don't want to use MergeMemStream here because we only want to + // scan inputs which exist at the start, not ones we add during processing + uint nslices = nmm->req(); + igvn->hash_delete(nmm); + for (uint i = Compile::AliasIdxRaw+1; i < nslices; i++) { + Node * mem = nmm->in(i); + Node * cur = NULL; + if (mem == NULL || mem->is_top()) + continue; + while (mem->is_Mem()) { + const Type *at = igvn->type(mem->in(MemNode::Address)); + if (at != Type::TOP) { + assert (at->isa_ptr() != NULL, "pointer type required."); + uint idx = (uint)_compile->get_alias_index(at->is_ptr()); + if (idx == i) { + if (cur == NULL) + cur = mem; + } else { + if (idx >= nmm->req() || nmm->is_empty_memory(nmm->in(idx))) { + nmm->set_memory_at(idx, mem); + } + } + } + mem = mem->in(MemNode::Memory); + } + nmm->set_memory_at(i, (cur != NULL) ? cur : mem); + if (mem->is_Phi()) { + // We have encountered a Phi, we need to split the Phi for + // any instance of the current type if we haven't encountered + // a value of the instance along the chain. + for (uint ni = new_index_start; ni < new_index_end; ni++) { + if((uint)_compile->get_general_index(ni) == i) { + Node *m = (ni >= nmm->req()) ? nmm->empty_memory() : nmm->in(ni); + if (nmm->is_empty_memory(m)) { + nmm->set_memory_at(ni, split_memory_phi(mem->as_Phi(), ni, orig_phis, igvn)); + } + } + } + } + } + igvn->hash_insert(nmm); + record_for_optimizer(nmm); + } + + // Phase 4: Update the inputs of non-instance memory Phis and the Memory input of memnodes + // + // First update the inputs of any non-instance Phi's from + // which we split out an instance Phi. Note we don't have + // to recursively process Phi's encounted on the input memory + // chains as is done in split_memory_phi() since they will + // also be processed here. + while (orig_phis.length() != 0) { + PhiNode *phi = orig_phis.pop(); + int alias_idx = _compile->get_alias_index(phi->adr_type()); + igvn->hash_delete(phi); + for (uint i = 1; i < phi->req(); i++) { + Node *mem = phi->in(i); + Node *new_mem = find_mem(mem, alias_idx, igvn); + if (mem != new_mem) { + phi->set_req(i, new_mem); + } + } + igvn->hash_insert(phi); + record_for_optimizer(phi); + } + + // Update the memory inputs of MemNodes with the value we computed + // in Phase 2. + for (int i = 0; i < _nodes->length(); i++) { + Node *nmem = get_map(i); + if (nmem != NULL) { + Node *n = _nodes->at(i)._node; + if (n != NULL && n->is_Mem()) { + igvn->hash_delete(n); + n->set_req(MemNode::Memory, nmem); + igvn->hash_insert(n); + record_for_optimizer(n); + } + } + } +} + +void ConnectionGraph::compute_escape() { + GrowableArray worklist; + GrowableArray alloc_worklist; + VectorSet visited(Thread::current()->resource_area()); + PhaseGVN *igvn = _compile->initial_gvn(); + + // process Phi nodes from the deferred list, they may not have + while(_deferred.size() > 0) { + Node * n = _deferred.pop(); + PhiNode * phi = n->as_Phi(); + + process_phi_escape(phi, igvn); + } + + VectorSet ptset(Thread::current()->resource_area()); + + // remove deferred edges from the graph and collect + // information we will need for type splitting + for (uint ni = 0; ni < (uint)_nodes->length(); ni++) { + PointsToNode * ptn = _nodes->adr_at(ni); + PointsToNode::NodeType nt = ptn->node_type(); + + if (nt == PointsToNode::UnknownType) { + continue; // not a node we are interested in + } + Node *n = ptn->_node; + if (nt == PointsToNode::LocalVar || nt == PointsToNode::Field) { + remove_deferred(ni); + if (n->is_AddP()) { + // if this AddP computes an address which may point to more that one + // object, nothing the address points to can be a unique type. + Node *base = n->in(AddPNode::Base); + ptset.Clear(); + PointsTo(ptset, base, igvn); + if (ptset.Size() > 1) { + for( VectorSetI j(&ptset); j.test(); ++j ) { + PointsToNode *ptaddr = _nodes->adr_at(j.elem); + ptaddr->_unique_type = false; + } + } + } + } else if (n->is_Call()) { + // initialize _escape_state of calls to GlobalEscape + n->as_Call()->_escape_state = PointsToNode::GlobalEscape; + // push call on alloc_worlist (alocations are calls) + // for processing by split_unique_types() + alloc_worklist.push(n); + } + } + // push all GlobalEscape nodes on the worklist + for (uint nj = 0; nj < (uint)_nodes->length(); nj++) { + if (_nodes->at(nj).escape_state() == PointsToNode::GlobalEscape) { + worklist.append(nj); + } + } + // mark all node reachable from GlobalEscape nodes + while(worklist.length() > 0) { + PointsToNode n = _nodes->at(worklist.pop()); + for (uint ei = 0; ei < n.edge_count(); ei++) { + uint npi = n.edge_target(ei); + PointsToNode *np = ptnode_adr(npi); + if (np->escape_state() != PointsToNode::GlobalEscape) { + np->set_escape_state(PointsToNode::GlobalEscape); + worklist.append_if_missing(npi); + } + } + } + + // push all ArgEscape nodes on the worklist + for (uint nk = 0; nk < (uint)_nodes->length(); nk++) { + if (_nodes->at(nk).escape_state() == PointsToNode::ArgEscape) + worklist.push(nk); + } + // mark all node reachable from ArgEscape nodes + while(worklist.length() > 0) { + PointsToNode n = _nodes->at(worklist.pop()); + + for (uint ei = 0; ei < n.edge_count(); ei++) { + uint npi = n.edge_target(ei); + PointsToNode *np = ptnode_adr(npi); + if (np->escape_state() != PointsToNode::ArgEscape) { + np->set_escape_state(PointsToNode::ArgEscape); + worklist.append_if_missing(npi); + } + } + } + _collecting = false; + + // Now use the escape information to create unique types for + // unescaped objects + split_unique_types(alloc_worklist); +} + +Node * ConnectionGraph::skip_casts(Node *n) { + while(n->Opcode() == Op_CastPP || n->Opcode() == Op_CheckCastPP) { + n = n->in(1); + } + return n; +} + +void ConnectionGraph::process_phi_escape(PhiNode *phi, PhaseTransform *phase) { + + if (phi->type()->isa_oopptr() == NULL) + return; // nothing to do if not an oop + + PointsToNode *ptadr = ptnode_adr(phi->_idx); + int incount = phi->req(); + int non_null_inputs = 0; + + for (int i = 1; i < incount ; i++) { + if (phi->in(i) != NULL) + non_null_inputs++; + } + if (non_null_inputs == ptadr->_inputs_processed) + return; // no new inputs since the last time this node was processed, + // the current information is valid + + ptadr->_inputs_processed = non_null_inputs; // prevent recursive processing of this node + for (int j = 1; j < incount ; j++) { + Node * n = phi->in(j); + if (n == NULL) + continue; // ignore NULL + n = skip_casts(n); + if (n->is_top() || n == phi) + continue; // ignore top or inputs which go back this node + int nopc = n->Opcode(); + PointsToNode npt = _nodes->at(n->_idx); + if (_nodes->at(n->_idx).node_type() == PointsToNode::JavaObject) { + add_pointsto_edge(phi->_idx, n->_idx); + } else { + add_deferred_edge(phi->_idx, n->_idx); + } + } +} + +void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *phase) { + + _processed.set(call->_idx); + switch (call->Opcode()) { + + // arguments to allocation and locking don't escape + case Op_Allocate: + case Op_AllocateArray: + case Op_Lock: + case Op_Unlock: + break; + + case Op_CallStaticJava: + // For a static call, we know exactly what method is being called. + // Use bytecode estimator to record the call's escape affects + { + ciMethod *meth = call->as_CallJava()->method(); + if (meth != NULL) { + const TypeTuple * d = call->tf()->domain(); + BCEscapeAnalyzer call_analyzer(meth); + VectorSet ptset(Thread::current()->resource_area()); + for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { + const Type* at = d->field_at(i); + int k = i - TypeFunc::Parms; + + if (at->isa_oopptr() != NULL) { + Node *arg = skip_casts(call->in(i)); + + if (!call_analyzer.is_arg_stack(k)) { + // The argument global escapes, mark everything it could point to + ptset.Clear(); + PointsTo(ptset, arg, phase); + for( VectorSetI j(&ptset); j.test(); ++j ) { + uint pt = j.elem; + + set_escape_state(pt, PointsToNode::GlobalEscape); + } + } else if (!call_analyzer.is_arg_local(k)) { + // The argument itself doesn't escape, but any fields might + ptset.Clear(); + PointsTo(ptset, arg, phase); + for( VectorSetI j(&ptset); j.test(); ++j ) { + uint pt = j.elem; + add_edge_from_fields(pt, _phantom_object, Type::OffsetBot); + } + } + } + } + call_analyzer.copy_dependencies(C()->dependencies()); + break; + } + // fall-through if not a Java method + } + + default: + // Some other type of call, assume the worst case: all arguments + // globally escape. + { + // adjust escape state for outgoing arguments + const TypeTuple * d = call->tf()->domain(); + VectorSet ptset(Thread::current()->resource_area()); + for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { + const Type* at = d->field_at(i); + + if (at->isa_oopptr() != NULL) { + Node *arg = skip_casts(call->in(i)); + ptset.Clear(); + PointsTo(ptset, arg, phase); + for( VectorSetI j(&ptset); j.test(); ++j ) { + uint pt = j.elem; + + set_escape_state(pt, PointsToNode::GlobalEscape); + } + } + } + } + } +} +void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *phase) { + CallNode *call = resproj->in(0)->as_Call(); + + PointsToNode *ptadr = ptnode_adr(resproj->_idx); + + ptadr->_node = resproj; + ptadr->set_node_type(PointsToNode::LocalVar); + set_escape_state(resproj->_idx, PointsToNode::UnknownEscape); + _processed.set(resproj->_idx); + + switch (call->Opcode()) { + case Op_Allocate: + { + Node *k = call->in(AllocateNode::KlassNode); + const TypeKlassPtr *kt; + if (k->Opcode() == Op_LoadKlass) { + kt = k->as_Load()->type()->isa_klassptr(); + } else { + kt = k->as_Type()->type()->isa_klassptr(); + } + assert(kt != NULL, "TypeKlassPtr required."); + ciKlass* cik = kt->klass(); + ciInstanceKlass* ciik = cik->as_instance_klass(); + + PointsToNode *ptadr = ptnode_adr(call->_idx); + ptadr->set_node_type(PointsToNode::JavaObject); + if (cik->is_subclass_of(_compile->env()->Thread_klass()) || ciik->has_finalizer()) { + set_escape_state(call->_idx, PointsToNode::GlobalEscape); + add_pointsto_edge(resproj->_idx, _phantom_object); + } else { + set_escape_state(call->_idx, PointsToNode::NoEscape); + add_pointsto_edge(resproj->_idx, call->_idx); + } + _processed.set(call->_idx); + break; + } + + case Op_AllocateArray: + { + PointsToNode *ptadr = ptnode_adr(call->_idx); + ptadr->set_node_type(PointsToNode::JavaObject); + set_escape_state(call->_idx, PointsToNode::NoEscape); + _processed.set(call->_idx); + add_pointsto_edge(resproj->_idx, call->_idx); + break; + } + + case Op_Lock: + case Op_Unlock: + break; + + case Op_CallStaticJava: + // For a static call, we know exactly what method is being called. + // Use bytecode estimator to record whether the call's return value escapes + { + const TypeTuple *r = call->tf()->range(); + const Type* ret_type = NULL; + + if (r->cnt() > TypeFunc::Parms) + ret_type = r->field_at(TypeFunc::Parms); + + // Note: we use isa_ptr() instead of isa_oopptr() here because the + // _multianewarray functions return a TypeRawPtr. + if (ret_type == NULL || ret_type->isa_ptr() == NULL) + break; // doesn't return a pointer type + + ciMethod *meth = call->as_CallJava()->method(); + if (meth == NULL) { + // not a Java method, assume global escape + set_escape_state(call->_idx, PointsToNode::GlobalEscape); + if (resproj != NULL) + add_pointsto_edge(resproj->_idx, _phantom_object); + } else { + BCEscapeAnalyzer call_analyzer(meth); + VectorSet ptset(Thread::current()->resource_area()); + + if (call_analyzer.is_return_local() && resproj != NULL) { + // determine whether any arguments are returned + const TypeTuple * d = call->tf()->domain(); + set_escape_state(call->_idx, PointsToNode::NoEscape); + for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { + const Type* at = d->field_at(i); + + if (at->isa_oopptr() != NULL) { + Node *arg = skip_casts(call->in(i)); + + if (call_analyzer.is_arg_returned(i - TypeFunc::Parms)) { + PointsToNode *arg_esp = _nodes->adr_at(arg->_idx); + if (arg_esp->node_type() == PointsToNode::JavaObject) + add_pointsto_edge(resproj->_idx, arg->_idx); + else + add_deferred_edge(resproj->_idx, arg->_idx); + arg_esp->_hidden_alias = true; + } + } + } + } else { + set_escape_state(call->_idx, PointsToNode::GlobalEscape); + if (resproj != NULL) + add_pointsto_edge(resproj->_idx, _phantom_object); + } + call_analyzer.copy_dependencies(C()->dependencies()); + } + break; + } + + default: + // Some other type of call, assume the worst case that the + // returned value, if any, globally escapes. + { + const TypeTuple *r = call->tf()->range(); + + if (r->cnt() > TypeFunc::Parms) { + const Type* ret_type = r->field_at(TypeFunc::Parms); + + // Note: we use isa_ptr() instead of isa_oopptr() here because the + // _multianewarray functions return a TypeRawPtr. + if (ret_type->isa_ptr() != NULL) { + PointsToNode *ptadr = ptnode_adr(call->_idx); + ptadr->set_node_type(PointsToNode::JavaObject); + set_escape_state(call->_idx, PointsToNode::GlobalEscape); + if (resproj != NULL) + add_pointsto_edge(resproj->_idx, _phantom_object); + } + } + } + } +} + +void ConnectionGraph::record_for_escape_analysis(Node *n) { + if (_collecting) { + if (n->is_Phi()) { + PhiNode *phi = n->as_Phi(); + const Type *pt = phi->type(); + if ((pt->isa_oopptr() != NULL) || pt == TypePtr::NULL_PTR) { + PointsToNode *ptn = ptnode_adr(phi->_idx); + ptn->set_node_type(PointsToNode::LocalVar); + ptn->_node = n; + _deferred.push(n); + } + } + } +} + +void ConnectionGraph::record_escape_work(Node *n, PhaseTransform *phase) { + + int opc = n->Opcode(); + PointsToNode *ptadr = ptnode_adr(n->_idx); + + if (_processed.test(n->_idx)) + return; + + ptadr->_node = n; + if (n->is_Call()) { + CallNode *call = n->as_Call(); + process_call_arguments(call, phase); + return; + } + + switch (opc) { + case Op_AddP: + { + Node *base = skip_casts(n->in(AddPNode::Base)); + ptadr->set_node_type(PointsToNode::Field); + + // create a field edge to this node from everything adr could point to + VectorSet ptset(Thread::current()->resource_area()); + PointsTo(ptset, base, phase); + for( VectorSetI i(&ptset); i.test(); ++i ) { + uint pt = i.elem; + add_field_edge(pt, n->_idx, type_to_offset(phase->type(n))); + } + break; + } + case Op_Parm: + { + ProjNode *nproj = n->as_Proj(); + uint con = nproj->_con; + if (con < TypeFunc::Parms) + return; + const Type *t = nproj->in(0)->as_Start()->_domain->field_at(con); + if (t->isa_ptr() == NULL) + return; + ptadr->set_node_type(PointsToNode::JavaObject); + if (t->isa_oopptr() != NULL) { + set_escape_state(n->_idx, PointsToNode::ArgEscape); + } else { + // this must be the incoming state of an OSR compile, we have to assume anything + // passed in globally escapes + assert(_compile->is_osr_compilation(), "bad argument type for non-osr compilation"); + set_escape_state(n->_idx, PointsToNode::GlobalEscape); + } + _processed.set(n->_idx); + break; + } + case Op_Phi: + { + PhiNode *phi = n->as_Phi(); + if (phi->type()->isa_oopptr() == NULL) + return; // nothing to do if not an oop + ptadr->set_node_type(PointsToNode::LocalVar); + process_phi_escape(phi, phase); + break; + } + case Op_CreateEx: + { + // assume that all exception objects globally escape + ptadr->set_node_type(PointsToNode::JavaObject); + set_escape_state(n->_idx, PointsToNode::GlobalEscape); + _processed.set(n->_idx); + break; + } + case Op_ConP: + { + const Type *t = phase->type(n); + ptadr->set_node_type(PointsToNode::JavaObject); + // assume all pointer constants globally escape except for null + if (t == TypePtr::NULL_PTR) + set_escape_state(n->_idx, PointsToNode::NoEscape); + else + set_escape_state(n->_idx, PointsToNode::GlobalEscape); + _processed.set(n->_idx); + break; + } + case Op_LoadKlass: + { + ptadr->set_node_type(PointsToNode::JavaObject); + set_escape_state(n->_idx, PointsToNode::GlobalEscape); + _processed.set(n->_idx); + break; + } + case Op_LoadP: + { + const Type *t = phase->type(n); + if (!t->isa_oopptr()) + return; + ptadr->set_node_type(PointsToNode::LocalVar); + set_escape_state(n->_idx, PointsToNode::UnknownEscape); + + Node *adr = skip_casts(n->in(MemNode::Address)); + const Type *adr_type = phase->type(adr); + Node *adr_base = skip_casts((adr->Opcode() == Op_AddP) ? adr->in(AddPNode::Base) : adr); + + // For everything "adr" could point to, create a deferred edge from + // this node to each field with the same offset as "adr_type" + VectorSet ptset(Thread::current()->resource_area()); + PointsTo(ptset, adr_base, phase); + // If ptset is empty, then this value must have been set outside + // this method, so we add the phantom node + if (ptset.Size() == 0) + ptset.set(_phantom_object); + for( VectorSetI i(&ptset); i.test(); ++i ) { + uint pt = i.elem; + add_deferred_edge_to_fields(n->_idx, pt, type_to_offset(adr_type)); + } + break; + } + case Op_StoreP: + case Op_StorePConditional: + case Op_CompareAndSwapP: + { + Node *adr = n->in(MemNode::Address); + Node *val = skip_casts(n->in(MemNode::ValueIn)); + const Type *adr_type = phase->type(adr); + if (!adr_type->isa_oopptr()) + return; + + assert(adr->Opcode() == Op_AddP, "expecting an AddP"); + Node *adr_base = adr->in(AddPNode::Base); + + // For everything "adr_base" could point to, create a deferred edge to "val" from each field + // with the same offset as "adr_type" + VectorSet ptset(Thread::current()->resource_area()); + PointsTo(ptset, adr_base, phase); + for( VectorSetI i(&ptset); i.test(); ++i ) { + uint pt = i.elem; + add_edge_from_fields(pt, val->_idx, type_to_offset(adr_type)); + } + break; + } + case Op_Proj: + { + ProjNode *nproj = n->as_Proj(); + Node *n0 = nproj->in(0); + // we are only interested in the result projection from a call + if (nproj->_con == TypeFunc::Parms && n0->is_Call() ) { + process_call_result(nproj, phase); + } + + break; + } + case Op_CastPP: + case Op_CheckCastPP: + { + ptadr->set_node_type(PointsToNode::LocalVar); + int ti = n->in(1)->_idx; + if (_nodes->at(ti).node_type() == PointsToNode::JavaObject) { + add_pointsto_edge(n->_idx, ti); + } else { + add_deferred_edge(n->_idx, ti); + } + break; + } + default: + ; + // nothing to do + } +} + +void ConnectionGraph::record_escape(Node *n, PhaseTransform *phase) { + if (_collecting) + record_escape_work(n, phase); +} + +#ifndef PRODUCT +void ConnectionGraph::dump() { + PhaseGVN *igvn = _compile->initial_gvn(); + bool first = true; + + for (uint ni = 0; ni < (uint)_nodes->length(); ni++) { + PointsToNode *esp = _nodes->adr_at(ni); + if (esp->node_type() == PointsToNode::UnknownType || esp->_node == NULL) + continue; + PointsToNode::EscapeState es = escape_state(esp->_node, igvn); + if (es == PointsToNode::NoEscape || (Verbose && + (es != PointsToNode::UnknownEscape || esp->edge_count() != 0))) { + // don't print null pointer node which almost every method has + if (esp->_node->Opcode() != Op_ConP || igvn->type(esp->_node) != TypePtr::NULL_PTR) { + if (first) { + tty->print("======== Connection graph for "); + C()->method()->print_short_name(); + tty->cr(); + first = false; + } + tty->print("%4d ", ni); + esp->dump(); + } + } + } +} +#endif diff --git a/hotspot/src/share/vm/opto/escape.hpp b/hotspot/src/share/vm/opto/escape.hpp new file mode 100644 index 00000000000..3cd879257e7 --- /dev/null +++ b/hotspot/src/share/vm/opto/escape.hpp @@ -0,0 +1,319 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Adaptation for C2 of the escape analysis algorithm described in: +// +// [Choi99] Jong-Deok Shoi, Manish Gupta, Mauricio Seffano, Vugranam C. Sreedhar, +// Sam Midkiff, "Escape Analysis for Java", Procedings of ACM SIGPLAN +// OOPSLA Conference, November 1, 1999 +// +// The flow-insensitive analysis described in the paper has been implemented. +// +// The analysis requires construction of a "connection graph" (CG) for the method being +// analyzed. The nodes of the connection graph are: +// +// - Java objects (JO) +// - Local variables (LV) +// - Fields of an object (OF), these also include array elements +// +// The CG contains 3 types of edges: +// +// - PointsTo (-P>) {LV,OF} to JO +// - Deferred (-D>) from {LV, OF} to {LV, OF} +// - Field (-F>) from JO to OF +// +// The following utility functions is used by the algorithm: +// +// PointsTo(n) - n is any CG node, it returns the set of JO that n could +// point to. +// +// The algorithm describes how to construct the connection graph in the following 4 cases: +// +// Case Edges Created +// +// (1) p = new T() LV -P> JO +// (2) p = q LV -D> LV +// (3) p.f = q JO -F> OF, OF -D> LV +// (4) p = q.f JO -F> OF, LV -D> OF +// +// In all these cases, p and q are local variables. For static field references, we can +// construct a local variable containing a reference to the static memory. +// +// C2 does not have local variables. However for the purposes of constructing +// the connection graph, the following IR nodes are treated as local variables: +// Phi (pointer values) +// LoadP +// Proj (value returned from callnodes including allocations) +// CheckCastPP +// +// The LoadP, Proj and CheckCastPP behave like variables assigned to only once. Only +// a Phi can have multiple assignments. Each input to a Phi is treated +// as an assignment to it. +// +// The following note types are JavaObject: +// +// top() +// Allocate +// AllocateArray +// Parm (for incoming arguments) +// CreateEx +// ConP +// LoadKlass +// +// AddP nodes are fields. +// +// After building the graph, a pass is made over the nodes, deleting deferred +// nodes and copying the edges from the target of the deferred edge to the +// source. This results in a graph with no deferred edges, only: +// +// LV -P> JO +// OF -P> JO +// JO -F> OF +// +// Then, for each node which is GlobalEscape, anything it could point to +// is marked GlobalEscape. Finally, for any node marked ArgEscape, anything +// it could point to is marked ArgEscape. +// + +class Compile; +class Node; +class CallNode; +class PhiNode; +class PhaseTransform; +class Type; +class TypePtr; +class VectorSet; + +class PointsToNode { +friend class ConnectionGraph; +public: + typedef enum { + UnknownType = 0, + JavaObject = 1, + LocalVar = 2, + Field = 3 + } NodeType; + + typedef enum { + UnknownEscape = 0, + NoEscape = 1, + ArgEscape = 2, + GlobalEscape = 3 + } EscapeState; + + typedef enum { + UnknownEdge = 0, + PointsToEdge = 1, + DeferredEdge = 2, + FieldEdge = 3 + } EdgeType; + +private: + enum { + EdgeMask = 3, + EdgeShift = 2, + + INITIAL_EDGE_COUNT = 4 + }; + + NodeType _type; + EscapeState _escape; + GrowableArray* _edges; // outgoing edges + int _offset; // for fields + + bool _unique_type; // For allocated objects, this node may be a unique type +public: + Node* _node; // Ideal node corresponding to this PointsTo node + int _inputs_processed; // the number of Phi inputs that have been processed so far + bool _hidden_alias; // this node is an argument to a function which may return it + // creating a hidden alias + + + PointsToNode(): _offset(-1), _type(UnknownType), _escape(UnknownEscape), _edges(NULL), _node(NULL), _inputs_processed(0), _hidden_alias(false), _unique_type(true) {} + + EscapeState escape_state() const { return _escape; } + NodeType node_type() const { return _type;} + int offset() { return _offset;} + + void set_offset(int offs) { _offset = offs;} + void set_escape_state(EscapeState state) { _escape = state; } + void set_node_type(NodeType ntype) { + assert(_type == UnknownType || _type == ntype, "Can't change node type"); + _type = ntype; + } + + // count of outgoing edges + uint edge_count() const { return (_edges == NULL) ? 0 : _edges->length(); } + // node index of target of outgoing edge "e" + uint edge_target(uint e) const; + // type of outgoing edge "e" + EdgeType edge_type(uint e) const; + // add a edge of the specified type pointing to the specified target + void add_edge(uint targIdx, EdgeType et); + // remove an edge of the specified type pointing to the specified target + void remove_edge(uint targIdx, EdgeType et); +#ifndef PRODUCT + void dump() const; +#endif + +}; + +class ConnectionGraph: public ResourceObj { +private: + enum { + INITIAL_NODE_COUNT = 100 // initial size of _nodes array + }; + + + GrowableArray* _nodes; // connection graph nodes Indexed by ideal + // node index + Unique_Node_List _deferred; // Phi's to be processed after parsing + VectorSet _processed; // records which nodes have been processed + bool _collecting; // indicates whether escape information is + // still being collected. If false, no new + // nodes will be processed + uint _phantom_object; // index of globally escaping object that + // pointer values loaded from a field which + // has not been set are assumed to point to + Compile * _compile; // Compile object for current compilation + + // address of an element in _nodes. Used when the element is to be modified + PointsToNode *ptnode_adr(uint idx) { + if ((uint)_nodes->length() <= idx) { + // expand _nodes array + PointsToNode dummy = _nodes->at_grow(idx); + } + return _nodes->adr_at(idx); + } + + // offset of a field reference + int type_to_offset(const Type *t); + + // compute the escape state for arguments to a call + void process_call_arguments(CallNode *call, PhaseTransform *phase); + + // compute the escape state for the return value of a call + void process_call_result(ProjNode *resproj, PhaseTransform *phase); + + // compute the escape state of a Phi. This may be called multiple + // times as new inputs are added to the Phi. + void process_phi_escape(PhiNode *phi, PhaseTransform *phase); + + // compute the escape state of an ideal node. + void record_escape_work(Node *n, PhaseTransform *phase); + + // walk the connection graph starting at the node corresponding to "n" and + // add the index of everything it could point to, to "ptset". This may cause + // Phi's encountered to get (re)processed (which requires "phase".) + void PointsTo(VectorSet &ptset, Node * n, PhaseTransform *phase); + + // Edge manipulation. The "from_i" and "to_i" arguments are the + // node indices of the source and destination of the edge + void add_pointsto_edge(uint from_i, uint to_i); + void add_deferred_edge(uint from_i, uint to_i); + void add_field_edge(uint from_i, uint to_i, int offs); + + + // Add an edge to node given by "to_i" from any field of adr_i whose offset + // matches "offset" A deferred edge is added if to_i is a LocalVar, and + // a pointsto edge is added if it is a JavaObject + void add_edge_from_fields(uint adr, uint to_i, int offs); + + // Add a deferred edge from node given by "from_i" to any field of adr_i whose offset + // matches "offset" + void add_deferred_edge_to_fields(uint from_i, uint adr, int offs); + + + // Remove outgoing deferred edges from the node referenced by "ni". + // Any outgoing edges from the target of the deferred edge are copied + // to "ni". + void remove_deferred(uint ni); + + Node_Array _node_map; // used for bookeeping during type splitting + // Used for the following purposes: + // Memory Phi - most recent unique Phi split out + // from this Phi + // MemNode - new memory input for this node + // ChecCastPP - allocation that this is a cast of + // allocation - CheckCastPP of the allocation + void split_AddP(Node *addp, Node *base, PhaseGVN *igvn); + PhiNode *create_split_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, PhaseGVN *igvn, bool &new_created); + PhiNode *split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, PhaseGVN *igvn); + Node *find_mem(Node *mem, int alias_idx, PhaseGVN *igvn); + // Propagate unique types created for unescaped allocated objects + // through the graph + void split_unique_types(GrowableArray &alloc_worklist); + + // manage entries in _node_map + void set_map(int idx, Node *n) { _node_map.map(idx, n); } + void set_map_phi(int idx, PhiNode *p) { _node_map.map(idx, (Node *) p); } + Node *get_map(int idx) { return _node_map[idx]; } + PhiNode *get_map_phi(int idx) { + Node *phi = _node_map[idx]; + return (phi == NULL) ? NULL : phi->as_Phi(); + } + + // Notify optimizer that a node has been modified + // Node: This assumes that escape analysis is run before + // PhaseIterGVN creation + void record_for_optimizer(Node *n) { + _compile->record_for_igvn(n); + } + + // Set the escape state of a node + void set_escape_state(uint ni, PointsToNode::EscapeState es); + + // bypass any casts and return the node they refer to + Node * skip_casts(Node *n); + + // Get Compile object for current compilation. + Compile *C() const { return _compile; } + +public: + ConnectionGraph(Compile *C); + + // record a Phi for later processing. + void record_for_escape_analysis(Node *n); + + // process a node and fill in its connection graph node + void record_escape(Node *n, PhaseTransform *phase); + + // All nodes have been recorded, compute the escape information + void compute_escape(); + + // escape state of a node + PointsToNode::EscapeState escape_state(Node *n, PhaseTransform *phase); + + bool hidden_alias(Node *n) { + if (_collecting) + return true; + PointsToNode ptn = _nodes->at_grow(n->_idx); + return (ptn.escape_state() != PointsToNode::NoEscape) || ptn._hidden_alias; + } + +#ifndef PRODUCT + void dump(); +#endif +}; diff --git a/hotspot/src/share/vm/opto/gcm.cpp b/hotspot/src/share/vm/opto/gcm.cpp new file mode 100644 index 00000000000..88af191a1a3 --- /dev/null +++ b/hotspot/src/share/vm/opto/gcm.cpp @@ -0,0 +1,1767 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_gcm.cpp.incl" + +//----------------------------schedule_node_into_block------------------------- +// Insert node n into block b. Look for projections of n and make sure they +// are in b also. +void PhaseCFG::schedule_node_into_block( Node *n, Block *b ) { + // Set basic block of n, Add n to b, + _bbs.map(n->_idx, b); + b->add_inst(n); + + // After Matching, nearly any old Node may have projections trailing it. + // These are usually machine-dependent flags. In any case, they might + // float to another block below this one. Move them up. + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* use = n->fast_out(i); + if (use->is_Proj()) { + Block* buse = _bbs[use->_idx]; + if (buse != b) { // In wrong block? + if (buse != NULL) + buse->find_remove(use); // Remove from wrong block + _bbs.map(use->_idx, b); // Re-insert in this block + b->add_inst(use); + } + } + } +} + + +//------------------------------schedule_pinned_nodes-------------------------- +// Set the basic block for Nodes pinned into blocks +void PhaseCFG::schedule_pinned_nodes( VectorSet &visited ) { + // Allocate node stack of size C->unique()+8 to avoid frequent realloc + GrowableArray spstack(C->unique()+8); + spstack.push(_root); + while ( spstack.is_nonempty() ) { + Node *n = spstack.pop(); + if( !visited.test_set(n->_idx) ) { // Test node and flag it as visited + if( n->pinned() && !_bbs.lookup(n->_idx) ) { // Pinned? Nail it down! + Node *input = n->in(0); + assert( input, "pinned Node must have Control" ); + while( !input->is_block_start() ) + input = input->in(0); + Block *b = _bbs[input->_idx]; // Basic block of controlling input + schedule_node_into_block(n, b); + } + for( int i = n->req() - 1; i >= 0; --i ) { // For all inputs + if( n->in(i) != NULL ) + spstack.push(n->in(i)); + } + } + } +} + +#ifdef ASSERT +// Assert that new input b2 is dominated by all previous inputs. +// Check this by by seeing that it is dominated by b1, the deepest +// input observed until b2. +static void assert_dom(Block* b1, Block* b2, Node* n, Block_Array &bbs) { + if (b1 == NULL) return; + assert(b1->_dom_depth < b2->_dom_depth, "sanity"); + Block* tmp = b2; + while (tmp != b1 && tmp != NULL) { + tmp = tmp->_idom; + } + if (tmp != b1) { + // Detected an unschedulable graph. Print some nice stuff and die. + tty->print_cr("!!! Unschedulable graph !!!"); + for (uint j=0; jlen(); j++) { // For all inputs + Node* inn = n->in(j); // Get input + if (inn == NULL) continue; // Ignore NULL, missing inputs + Block* inb = bbs[inn->_idx]; + tty->print("B%d idom=B%d depth=%2d ",inb->_pre_order, + inb->_idom ? inb->_idom->_pre_order : 0, inb->_dom_depth); + inn->dump(); + } + tty->print("Failing node: "); + n->dump(); + assert(false, "unscheduable graph"); + } +} +#endif + +static Block* find_deepest_input(Node* n, Block_Array &bbs) { + // Find the last input dominated by all other inputs. + Block* deepb = NULL; // Deepest block so far + int deepb_dom_depth = 0; + for (uint k = 0; k < n->len(); k++) { // For all inputs + Node* inn = n->in(k); // Get input + if (inn == NULL) continue; // Ignore NULL, missing inputs + Block* inb = bbs[inn->_idx]; + assert(inb != NULL, "must already have scheduled this input"); + if (deepb_dom_depth < (int) inb->_dom_depth) { + // The new inb must be dominated by the previous deepb. + // The various inputs must be linearly ordered in the dom + // tree, or else there will not be a unique deepest block. + DEBUG_ONLY(assert_dom(deepb, inb, n, bbs)); + deepb = inb; // Save deepest block + deepb_dom_depth = deepb->_dom_depth; + } + } + assert(deepb != NULL, "must be at least one input to n"); + return deepb; +} + + +//------------------------------schedule_early--------------------------------- +// Find the earliest Block any instruction can be placed in. Some instructions +// are pinned into Blocks. Unpinned instructions can appear in last block in +// which all their inputs occur. +bool PhaseCFG::schedule_early(VectorSet &visited, Node_List &roots) { + // Allocate stack with enough space to avoid frequent realloc + Node_Stack nstack(roots.Size() + 8); // (unique >> 1) + 24 from Java2D stats + // roots.push(_root); _root will be processed among C->top() inputs + roots.push(C->top()); + visited.set(C->top()->_idx); + + while (roots.size() != 0) { + // Use local variables nstack_top_n & nstack_top_i to cache values + // on stack's top. + Node *nstack_top_n = roots.pop(); + uint nstack_top_i = 0; +//while_nstack_nonempty: + while (true) { + // Get parent node and next input's index from stack's top. + Node *n = nstack_top_n; + uint i = nstack_top_i; + + if (i == 0) { + // Special control input processing. + // While I am here, go ahead and look for Nodes which are taking control + // from a is_block_proj Node. After I inserted RegionNodes to make proper + // blocks, the control at a is_block_proj more properly comes from the + // Region being controlled by the block_proj Node. + const Node *in0 = n->in(0); + if (in0 != NULL) { // Control-dependent? + const Node *p = in0->is_block_proj(); + if (p != NULL && p != n) { // Control from a block projection? + // Find trailing Region + Block *pb = _bbs[in0->_idx]; // Block-projection already has basic block + uint j = 0; + if (pb->_num_succs != 1) { // More then 1 successor? + // Search for successor + uint max = pb->_nodes.size(); + assert( max > 1, "" ); + uint start = max - pb->_num_succs; + // Find which output path belongs to projection + for (j = start; j < max; j++) { + if( pb->_nodes[j] == in0 ) + break; + } + assert( j < max, "must find" ); + // Change control to match head of successor basic block + j -= start; + } + n->set_req(0, pb->_succs[j]->head()); + } + } else { // n->in(0) == NULL + if (n->req() == 1) { // This guy is a constant with NO inputs? + n->set_req(0, _root); + } + } + } + + // First, visit all inputs and force them to get a block. If an + // input is already in a block we quit following inputs (to avoid + // cycles). Instead we put that Node on a worklist to be handled + // later (since IT'S inputs may not have a block yet). + bool done = true; // Assume all n's inputs will be processed + while (i < n->len()) { // For all inputs + Node *in = n->in(i); // Get input + ++i; + if (in == NULL) continue; // Ignore NULL, missing inputs + int is_visited = visited.test_set(in->_idx); + if (!_bbs.lookup(in->_idx)) { // Missing block selection? + if (is_visited) { + // assert( !visited.test(in->_idx), "did not schedule early" ); + return false; + } + nstack.push(n, i); // Save parent node and next input's index. + nstack_top_n = in; // Process current input now. + nstack_top_i = 0; + done = false; // Not all n's inputs processed. + break; // continue while_nstack_nonempty; + } else if (!is_visited) { // Input not yet visited? + roots.push(in); // Visit this guy later, using worklist + } + } + if (done) { + // All of n's inputs have been processed, complete post-processing. + + // Some instructions are pinned into a block. These include Region, + // Phi, Start, Return, and other control-dependent instructions and + // any projections which depend on them. + if (!n->pinned()) { + // Set earliest legal block. + _bbs.map(n->_idx, find_deepest_input(n, _bbs)); + } + + if (nstack.is_empty()) { + // Finished all nodes on stack. + // Process next node on the worklist 'roots'. + break; + } + // Get saved parent node and next input's index. + nstack_top_n = nstack.node(); + nstack_top_i = nstack.index(); + nstack.pop(); + } // if (done) + } // while (true) + } // while (roots.size() != 0) + return true; +} + +//------------------------------dom_lca---------------------------------------- +// Find least common ancestor in dominator tree +// LCA is a current notion of LCA, to be raised above 'this'. +// As a convenient boundary condition, return 'this' if LCA is NULL. +// Find the LCA of those two nodes. +Block* Block::dom_lca(Block* LCA) { + if (LCA == NULL || LCA == this) return this; + + Block* anc = this; + while (anc->_dom_depth > LCA->_dom_depth) + anc = anc->_idom; // Walk up till anc is as high as LCA + + while (LCA->_dom_depth > anc->_dom_depth) + LCA = LCA->_idom; // Walk up till LCA is as high as anc + + while (LCA != anc) { // Walk both up till they are the same + LCA = LCA->_idom; + anc = anc->_idom; + } + + return LCA; +} + +//--------------------------raise_LCA_above_use-------------------------------- +// We are placing a definition, and have been given a def->use edge. +// The definition must dominate the use, so move the LCA upward in the +// dominator tree to dominate the use. If the use is a phi, adjust +// the LCA only with the phi input paths which actually use this def. +static Block* raise_LCA_above_use(Block* LCA, Node* use, Node* def, Block_Array &bbs) { + Block* buse = bbs[use->_idx]; + if (buse == NULL) return LCA; // Unused killing Projs have no use block + if (!use->is_Phi()) return buse->dom_lca(LCA); + uint pmax = use->req(); // Number of Phi inputs + // Why does not this loop just break after finding the matching input to + // the Phi? Well...it's like this. I do not have true def-use/use-def + // chains. Means I cannot distinguish, from the def-use direction, which + // of many use-defs lead from the same use to the same def. That is, this + // Phi might have several uses of the same def. Each use appears in a + // different predecessor block. But when I enter here, I cannot distinguish + // which use-def edge I should find the predecessor block for. So I find + // them all. Means I do a little extra work if a Phi uses the same value + // more than once. + for (uint j=1; jin(j) == def) { // Found matching input? + Block* pred = bbs[buse->pred(j)->_idx]; + LCA = pred->dom_lca(LCA); + } + } + return LCA; +} + +//----------------------------raise_LCA_above_marks---------------------------- +// Return a new LCA that dominates LCA and any of its marked predecessors. +// Search all my parents up to 'early' (exclusive), looking for predecessors +// which are marked with the given index. Return the LCA (in the dom tree) +// of all marked blocks. If there are none marked, return the original +// LCA. +static Block* raise_LCA_above_marks(Block* LCA, node_idx_t mark, + Block* early, Block_Array &bbs) { + Block_List worklist; + worklist.push(LCA); + while (worklist.size() > 0) { + Block* mid = worklist.pop(); + if (mid == early) continue; // stop searching here + + // Test and set the visited bit. + if (mid->raise_LCA_visited() == mark) continue; // already visited + mid->set_raise_LCA_visited(mark); + + // Don't process the current LCA, otherwise the search may terminate early + if (mid != LCA && mid->raise_LCA_mark() == mark) { + // Raise the LCA. + LCA = mid->dom_lca(LCA); + if (LCA == early) break; // stop searching everywhere + assert(early->dominates(LCA), "early is high enough"); + // Resume searching at that point, skipping intermediate levels. + worklist.push(LCA); + } else { + // Keep searching through this block's predecessors. + for (uint j = 1, jmax = mid->num_preds(); j < jmax; j++) { + Block* mid_parent = bbs[ mid->pred(j)->_idx ]; + worklist.push(mid_parent); + } + } + } + return LCA; +} + +//--------------------------memory_early_block-------------------------------- +// This is a variation of find_deepest_input, the heart of schedule_early. +// Find the "early" block for a load, if we considered only memory and +// address inputs, that is, if other data inputs were ignored. +// +// Because a subset of edges are considered, the resulting block will +// be earlier (at a shallower dom_depth) than the true schedule_early +// point of the node. We compute this earlier block as a more permissive +// site for anti-dependency insertion, but only if subsume_loads is enabled. +static Block* memory_early_block(Node* load, Block* early, Block_Array &bbs) { + Node* base; + Node* index; + Node* store = load->in(MemNode::Memory); + load->as_Mach()->memory_inputs(base, index); + + assert(base != NodeSentinel && index != NodeSentinel, + "unexpected base/index inputs"); + + Node* mem_inputs[4]; + int mem_inputs_length = 0; + if (base != NULL) mem_inputs[mem_inputs_length++] = base; + if (index != NULL) mem_inputs[mem_inputs_length++] = index; + if (store != NULL) mem_inputs[mem_inputs_length++] = store; + + // In the comparision below, add one to account for the control input, + // which may be null, but always takes up a spot in the in array. + if (mem_inputs_length + 1 < (int) load->req()) { + // This "load" has more inputs than just the memory, base and index inputs. + // For purposes of checking anti-dependences, we need to start + // from the early block of only the address portion of the instruction, + // and ignore other blocks that may have factored into the wider + // schedule_early calculation. + if (load->in(0) != NULL) mem_inputs[mem_inputs_length++] = load->in(0); + + Block* deepb = NULL; // Deepest block so far + int deepb_dom_depth = 0; + for (int i = 0; i < mem_inputs_length; i++) { + Block* inb = bbs[mem_inputs[i]->_idx]; + if (deepb_dom_depth < (int) inb->_dom_depth) { + // The new inb must be dominated by the previous deepb. + // The various inputs must be linearly ordered in the dom + // tree, or else there will not be a unique deepest block. + DEBUG_ONLY(assert_dom(deepb, inb, load, bbs)); + deepb = inb; // Save deepest block + deepb_dom_depth = deepb->_dom_depth; + } + } + early = deepb; + } + + return early; +} + +//--------------------------insert_anti_dependences--------------------------- +// A load may need to witness memory that nearby stores can overwrite. +// For each nearby store, either insert an "anti-dependence" edge +// from the load to the store, or else move LCA upward to force the +// load to (eventually) be scheduled in a block above the store. +// +// Do not add edges to stores on distinct control-flow paths; +// only add edges to stores which might interfere. +// +// Return the (updated) LCA. There will not be any possibly interfering +// store between the load's "early block" and the updated LCA. +// Any stores in the updated LCA will have new precedence edges +// back to the load. The caller is expected to schedule the load +// in the LCA, in which case the precedence edges will make LCM +// preserve anti-dependences. The caller may also hoist the load +// above the LCA, if it is not the early block. +Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { + assert(load->needs_anti_dependence_check(), "must be a load of some sort"); + assert(LCA != NULL, ""); + DEBUG_ONLY(Block* LCA_orig = LCA); + + // Compute the alias index. Loads and stores with different alias indices + // do not need anti-dependence edges. + uint load_alias_idx = C->get_alias_index(load->adr_type()); +#ifdef ASSERT + if (load_alias_idx == Compile::AliasIdxBot && C->AliasLevel() > 0 && + (PrintOpto || VerifyAliases || + PrintMiscellaneous && (WizardMode || Verbose))) { + // Load nodes should not consume all of memory. + // Reporting a bottom type indicates a bug in adlc. + // If some particular type of node validly consumes all of memory, + // sharpen the preceding "if" to exclude it, so we can catch bugs here. + tty->print_cr("*** Possible Anti-Dependence Bug: Load consumes all of memory."); + load->dump(2); + if (VerifyAliases) assert(load_alias_idx != Compile::AliasIdxBot, ""); + } +#endif + assert(load_alias_idx || (load->is_Mach() && load->as_Mach()->ideal_Opcode() == Op_StrComp), + "String compare is only known 'load' that does not conflict with any stores"); + + if (!C->alias_type(load_alias_idx)->is_rewritable()) { + // It is impossible to spoil this load by putting stores before it, + // because we know that the stores will never update the value + // which 'load' must witness. + return LCA; + } + + node_idx_t load_index = load->_idx; + + // Note the earliest legal placement of 'load', as determined by + // by the unique point in the dom tree where all memory effects + // and other inputs are first available. (Computed by schedule_early.) + // For normal loads, 'early' is the shallowest place (dom graph wise) + // to look for anti-deps between this load and any store. + Block* early = _bbs[load_index]; + + // If we are subsuming loads, compute an "early" block that only considers + // memory or address inputs. This block may be different than the + // schedule_early block in that it could be at an even shallower depth in the + // dominator tree, and allow for a broader discovery of anti-dependences. + if (C->subsume_loads()) { + early = memory_early_block(load, early, _bbs); + } + + ResourceArea *area = Thread::current()->resource_area(); + Node_List worklist_mem(area); // prior memory state to store + Node_List worklist_store(area); // possible-def to explore + Node_List non_early_stores(area); // all relevant stores outside of early + bool must_raise_LCA = false; + DEBUG_ONLY(VectorSet should_not_repeat(area)); + +#ifdef TRACK_PHI_INPUTS + // %%% This extra checking fails because MergeMem nodes are not GVNed. + // Provide "phi_inputs" to check if every input to a PhiNode is from the + // original memory state. This indicates a PhiNode for which should not + // prevent the load from sinking. For such a block, set_raise_LCA_mark + // may be overly conservative. + // Mechanism: count inputs seen for each Phi encountered in worklist_store. + DEBUG_ONLY(GrowableArray phi_inputs(area, C->unique(),0,0)); +#endif + + // 'load' uses some memory state; look for users of the same state. + // Recurse through MergeMem nodes to the stores that use them. + + // Each of these stores is a possible definition of memory + // that 'load' needs to use. We need to force 'load' + // to occur before each such store. When the store is in + // the same block as 'load', we insert an anti-dependence + // edge load->store. + + // The relevant stores "nearby" the load consist of a tree rooted + // at initial_mem, with internal nodes of type MergeMem. + // Therefore, the branches visited by the worklist are of this form: + // initial_mem -> (MergeMem ->)* store + // The anti-dependence constraints apply only to the fringe of this tree. + + Node* initial_mem = load->in(MemNode::Memory); + worklist_store.push(initial_mem); + worklist_mem.push(NULL); + DEBUG_ONLY(should_not_repeat.test_set(initial_mem->_idx)); + while (worklist_store.size() > 0) { + // Examine a nearby store to see if it might interfere with our load. + Node* mem = worklist_mem.pop(); + Node* store = worklist_store.pop(); + uint op = store->Opcode(); + + // MergeMems do not directly have anti-deps. + // Treat them as internal nodes in a forward tree of memory states, + // the leaves of which are each a 'possible-def'. + if (store == initial_mem // root (exclusive) of tree we are searching + || op == Op_MergeMem // internal node of tree we are searching + ) { + mem = store; // It's not a possibly interfering store. + for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) { + store = mem->fast_out(i); + if (store->is_MergeMem()) { + // Be sure we don't get into combinatorial problems. + // (Allow phis to be repeated; they can merge two relevant states.) + uint i = worklist_store.size(); + for (; i > 0; i--) { + if (worklist_store.at(i-1) == store) break; + } + if (i > 0) continue; // already on work list; do not repeat + DEBUG_ONLY(int repeated = should_not_repeat.test_set(store->_idx)); + assert(!repeated, "do not walk merges twice"); + } + worklist_mem.push(mem); + worklist_store.push(store); + } + continue; + } + + if (op == Op_MachProj || op == Op_Catch) continue; + if (store->needs_anti_dependence_check()) continue; // not really a store + + // Compute the alias index. Loads and stores with different alias + // indices do not need anti-dependence edges. Wide MemBar's are + // anti-dependent on everything (except immutable memories). + const TypePtr* adr_type = store->adr_type(); + if (!C->can_alias(adr_type, load_alias_idx)) continue; + + // Most slow-path runtime calls do NOT modify Java memory, but + // they can block and so write Raw memory. + if (store->is_Mach()) { + MachNode* mstore = store->as_Mach(); + if (load_alias_idx != Compile::AliasIdxRaw) { + // Check for call into the runtime using the Java calling + // convention (and from there into a wrapper); it has no + // _method. Can't do this optimization for Native calls because + // they CAN write to Java memory. + if (mstore->ideal_Opcode() == Op_CallStaticJava) { + assert(mstore->is_MachSafePoint(), ""); + MachSafePointNode* ms = (MachSafePointNode*) mstore; + assert(ms->is_MachCallJava(), ""); + MachCallJavaNode* mcj = (MachCallJavaNode*) ms; + if (mcj->_method == NULL) { + // These runtime calls do not write to Java visible memory + // (other than Raw) and so do not require anti-dependence edges. + continue; + } + } + // Same for SafePoints: they read/write Raw but only read otherwise. + // This is basically a workaround for SafePoints only defining control + // instead of control + memory. + if (mstore->ideal_Opcode() == Op_SafePoint) + continue; + } else { + // Some raw memory, such as the load of "top" at an allocation, + // can be control dependent on the previous safepoint. See + // comments in GraphKit::allocate_heap() about control input. + // Inserting an anti-dep between such a safepoint and a use + // creates a cycle, and will cause a subsequent failure in + // local scheduling. (BugId 4919904) + // (%%% How can a control input be a safepoint and not a projection??) + if (mstore->ideal_Opcode() == Op_SafePoint && load->in(0) == mstore) + continue; + } + } + + // Identify a block that the current load must be above, + // or else observe that 'store' is all the way up in the + // earliest legal block for 'load'. In the latter case, + // immediately insert an anti-dependence edge. + Block* store_block = _bbs[store->_idx]; + assert(store_block != NULL, "unused killing projections skipped above"); + + if (store->is_Phi()) { + // 'load' uses memory which is one (or more) of the Phi's inputs. + // It must be scheduled not before the Phi, but rather before + // each of the relevant Phi inputs. + // + // Instead of finding the LCA of all inputs to a Phi that match 'mem', + // we mark each corresponding predecessor block and do a combined + // hoisting operation later (raise_LCA_above_marks). + // + // Do not assert(store_block != early, "Phi merging memory after access") + // PhiNode may be at start of block 'early' with backedge to 'early' + DEBUG_ONLY(bool found_match = false); + for (uint j = PhiNode::Input, jmax = store->req(); j < jmax; j++) { + if (store->in(j) == mem) { // Found matching input? + DEBUG_ONLY(found_match = true); + Block* pred_block = _bbs[store_block->pred(j)->_idx]; + if (pred_block != early) { + // If any predecessor of the Phi matches the load's "early block", + // we do not need a precedence edge between the Phi and 'load' + // since the load will be forced into a block preceeding the Phi. + pred_block->set_raise_LCA_mark(load_index); + assert(!LCA_orig->dominates(pred_block) || + early->dominates(pred_block), "early is high enough"); + must_raise_LCA = true; + } + } + } + assert(found_match, "no worklist bug"); +#ifdef TRACK_PHI_INPUTS +#ifdef ASSERT + // This assert asks about correct handling of PhiNodes, which may not + // have all input edges directly from 'mem'. See BugId 4621264 + int num_mem_inputs = phi_inputs.at_grow(store->_idx,0) + 1; + // Increment by exactly one even if there are multiple copies of 'mem' + // coming into the phi, because we will run this block several times + // if there are several copies of 'mem'. (That's how DU iterators work.) + phi_inputs.at_put(store->_idx, num_mem_inputs); + assert(PhiNode::Input + num_mem_inputs < store->req(), + "Expect at least one phi input will not be from original memory state"); +#endif //ASSERT +#endif //TRACK_PHI_INPUTS + } else if (store_block != early) { + // 'store' is between the current LCA and earliest possible block. + // Label its block, and decide later on how to raise the LCA + // to include the effect on LCA of this store. + // If this store's block gets chosen as the raised LCA, we + // will find him on the non_early_stores list and stick him + // with a precedence edge. + // (But, don't bother if LCA is already raised all the way.) + if (LCA != early) { + store_block->set_raise_LCA_mark(load_index); + must_raise_LCA = true; + non_early_stores.push(store); + } + } else { + // Found a possibly-interfering store in the load's 'early' block. + // This means 'load' cannot sink at all in the dominator tree. + // Add an anti-dep edge, and squeeze 'load' into the highest block. + assert(store != load->in(0), "dependence cycle found"); + if (verify) { + assert(store->find_edge(load) != -1, "missing precedence edge"); + } else { + store->add_prec(load); + } + LCA = early; + // This turns off the process of gathering non_early_stores. + } + } + // (Worklist is now empty; all nearby stores have been visited.) + + // Finished if 'load' must be scheduled in its 'early' block. + // If we found any stores there, they have already been given + // precedence edges. + if (LCA == early) return LCA; + + // We get here only if there are no possibly-interfering stores + // in the load's 'early' block. Move LCA up above all predecessors + // which contain stores we have noted. + // + // The raised LCA block can be a home to such interfering stores, + // but its predecessors must not contain any such stores. + // + // The raised LCA will be a lower bound for placing the load, + // preventing the load from sinking past any block containing + // a store that may invalidate the memory state required by 'load'. + if (must_raise_LCA) + LCA = raise_LCA_above_marks(LCA, load->_idx, early, _bbs); + if (LCA == early) return LCA; + + // Insert anti-dependence edges from 'load' to each store + // in the non-early LCA block. + // Mine the non_early_stores list for such stores. + if (LCA->raise_LCA_mark() == load_index) { + while (non_early_stores.size() > 0) { + Node* store = non_early_stores.pop(); + Block* store_block = _bbs[store->_idx]; + if (store_block == LCA) { + // add anti_dependence from store to load in its own block + assert(store != load->in(0), "dependence cycle found"); + if (verify) { + assert(store->find_edge(load) != -1, "missing precedence edge"); + } else { + store->add_prec(load); + } + } else { + assert(store_block->raise_LCA_mark() == load_index, "block was marked"); + // Any other stores we found must be either inside the new LCA + // or else outside the original LCA. In the latter case, they + // did not interfere with any use of 'load'. + assert(LCA->dominates(store_block) + || !LCA_orig->dominates(store_block), "no stray stores"); + } + } + } + + // Return the highest block containing stores; any stores + // within that block have been given anti-dependence edges. + return LCA; +} + +// This class is used to iterate backwards over the nodes in the graph. + +class Node_Backward_Iterator { + +private: + Node_Backward_Iterator(); + +public: + // Constructor for the iterator + Node_Backward_Iterator(Node *root, VectorSet &visited, Node_List &stack, Block_Array &bbs); + + // Postincrement operator to iterate over the nodes + Node *next(); + +private: + VectorSet &_visited; + Node_List &_stack; + Block_Array &_bbs; +}; + +// Constructor for the Node_Backward_Iterator +Node_Backward_Iterator::Node_Backward_Iterator( Node *root, VectorSet &visited, Node_List &stack, Block_Array &bbs ) + : _visited(visited), _stack(stack), _bbs(bbs) { + // The stack should contain exactly the root + stack.clear(); + stack.push(root); + + // Clear the visited bits + visited.Clear(); +} + +// Iterator for the Node_Backward_Iterator +Node *Node_Backward_Iterator::next() { + + // If the _stack is empty, then just return NULL: finished. + if ( !_stack.size() ) + return NULL; + + // '_stack' is emulating a real _stack. The 'visit-all-users' loop has been + // made stateless, so I do not need to record the index 'i' on my _stack. + // Instead I visit all users each time, scanning for unvisited users. + // I visit unvisited not-anti-dependence users first, then anti-dependent + // children next. + Node *self = _stack.pop(); + + // I cycle here when I am entering a deeper level of recursion. + // The key variable 'self' was set prior to jumping here. + while( 1 ) { + + _visited.set(self->_idx); + + // Now schedule all uses as late as possible. + uint src = self->is_Proj() ? self->in(0)->_idx : self->_idx; + uint src_rpo = _bbs[src]->_rpo; + + // Schedule all nodes in a post-order visit + Node *unvisited = NULL; // Unvisited anti-dependent Node, if any + + // Scan for unvisited nodes + for (DUIterator_Fast imax, i = self->fast_outs(imax); i < imax; i++) { + // For all uses, schedule late + Node* n = self->fast_out(i); // Use + + // Skip already visited children + if ( _visited.test(n->_idx) ) + continue; + + // do not traverse backward control edges + Node *use = n->is_Proj() ? n->in(0) : n; + uint use_rpo = _bbs[use->_idx]->_rpo; + + if ( use_rpo < src_rpo ) + continue; + + // Phi nodes always precede uses in a basic block + if ( use_rpo == src_rpo && use->is_Phi() ) + continue; + + unvisited = n; // Found unvisited + + // Check for possible-anti-dependent + if( !n->needs_anti_dependence_check() ) + break; // Not visited, not anti-dep; schedule it NOW + } + + // Did I find an unvisited not-anti-dependent Node? + if ( !unvisited ) + break; // All done with children; post-visit 'self' + + // Visit the unvisited Node. Contains the obvious push to + // indicate I'm entering a deeper level of recursion. I push the + // old state onto the _stack and set a new state and loop (recurse). + _stack.push(self); + self = unvisited; + } // End recursion loop + + return self; +} + +//------------------------------ComputeLatenciesBackwards---------------------- +// Compute the latency of all the instructions. +void PhaseCFG::ComputeLatenciesBackwards(VectorSet &visited, Node_List &stack) { +#ifndef PRODUCT + if (trace_opto_pipelining()) + tty->print("\n#---- ComputeLatenciesBackwards ----\n"); +#endif + + Node_Backward_Iterator iter((Node *)_root, visited, stack, _bbs); + Node *n; + + // Walk over all the nodes from last to first + while (n = iter.next()) { + // Set the latency for the definitions of this instruction + partial_latency_of_defs(n); + } +} // end ComputeLatenciesBackwards + +//------------------------------partial_latency_of_defs------------------------ +// Compute the latency impact of this node on all defs. This computes +// a number that increases as we approach the beginning of the routine. +void PhaseCFG::partial_latency_of_defs(Node *n) { + // Set the latency for this instruction +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print("# latency_to_inputs: node_latency[%d] = %d for node", + n->_idx, _node_latency.at_grow(n->_idx)); + dump(); + } +#endif + + if (n->is_Proj()) + n = n->in(0); + + if (n->is_Root()) + return; + + uint nlen = n->len(); + uint use_latency = _node_latency.at_grow(n->_idx); + uint use_pre_order = _bbs[n->_idx]->_pre_order; + + for ( uint j=0; jin(j); + + if (!def || def == n) + continue; + + // Walk backwards thru projections + if (def->is_Proj()) + def = def->in(0); + +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print("# in(%2d): ", j); + def->dump(); + } +#endif + + // If the defining block is not known, assume it is ok + Block *def_block = _bbs[def->_idx]; + uint def_pre_order = def_block ? def_block->_pre_order : 0; + + if ( (use_pre_order < def_pre_order) || + (use_pre_order == def_pre_order && n->is_Phi()) ) + continue; + + uint delta_latency = n->latency(j); + uint current_latency = delta_latency + use_latency; + + if (_node_latency.at_grow(def->_idx) < current_latency) { + _node_latency.at_put_grow(def->_idx, current_latency); + } + +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print_cr("# %d + edge_latency(%d) == %d -> %d, node_latency[%d] = %d", + use_latency, j, delta_latency, current_latency, def->_idx, + _node_latency.at_grow(def->_idx)); + } +#endif + } +} + +//------------------------------latency_from_use------------------------------- +// Compute the latency of a specific use +int PhaseCFG::latency_from_use(Node *n, const Node *def, Node *use) { + // If self-reference, return no latency + if (use == n || use->is_Root()) + return 0; + + uint def_pre_order = _bbs[def->_idx]->_pre_order; + uint latency = 0; + + // If the use is not a projection, then it is simple... + if (!use->is_Proj()) { +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print("# out(): "); + use->dump(); + } +#endif + + uint use_pre_order = _bbs[use->_idx]->_pre_order; + + if (use_pre_order < def_pre_order) + return 0; + + if (use_pre_order == def_pre_order && use->is_Phi()) + return 0; + + uint nlen = use->len(); + uint nl = _node_latency.at_grow(use->_idx); + + for ( uint j=0; jin(j) == n) { + // Change this if we want local latencies + uint ul = use->latency(j); + uint l = ul + nl; + if (latency < l) latency = l; +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print_cr("# %d + edge_latency(%d) == %d -> %d, latency = %d", + nl, j, ul, l, latency); + } +#endif + } + } + } else { + // This is a projection, just grab the latency of the use(s) + for (DUIterator_Fast jmax, j = use->fast_outs(jmax); j < jmax; j++) { + uint l = latency_from_use(use, def, use->fast_out(j)); + if (latency < l) latency = l; + } + } + + return latency; +} + +//------------------------------latency_from_uses------------------------------ +// Compute the latency of this instruction relative to all of it's uses. +// This computes a number that increases as we approach the beginning of the +// routine. +void PhaseCFG::latency_from_uses(Node *n) { + // Set the latency for this instruction +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print("# latency_from_outputs: node_latency[%d] = %d for node", + n->_idx, _node_latency.at_grow(n->_idx)); + dump(); + } +#endif + uint latency=0; + const Node *def = n->is_Proj() ? n->in(0): n; + + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + uint l = latency_from_use(n, def, n->fast_out(i)); + + if (latency < l) latency = l; + } + + _node_latency.at_put_grow(n->_idx, latency); +} + +//------------------------------hoist_to_cheaper_block------------------------- +// Pick a block for node self, between early and LCA, that is a cheaper +// alternative to LCA. +Block* PhaseCFG::hoist_to_cheaper_block(Block* LCA, Block* early, Node* self) { + const double delta = 1+PROB_UNLIKELY_MAG(4); + Block* least = LCA; + double least_freq = least->_freq; + uint target = _node_latency.at_grow(self->_idx); + uint start_latency = _node_latency.at_grow(LCA->_nodes[0]->_idx); + uint end_latency = _node_latency.at_grow(LCA->_nodes[LCA->end_idx()]->_idx); + bool in_latency = (target <= start_latency); + const Block* root_block = _bbs[_root->_idx]; + + // Turn off latency scheduling if scheduling is just plain off + if (!C->do_scheduling()) + in_latency = true; + + // Do not hoist (to cover latency) instructions which target a + // single register. Hoisting stretches the live range of the + // single register and may force spilling. + MachNode* mach = self->is_Mach() ? self->as_Mach() : NULL; + if (mach && mach->out_RegMask().is_bound1() && mach->out_RegMask().is_NotEmpty()) + in_latency = true; + +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print("# Find cheaper block for latency %d: ", + _node_latency.at_grow(self->_idx)); + self->dump(); + tty->print_cr("# B%d: start latency for [%4d]=%d, end latency for [%4d]=%d, freq=%g", + LCA->_pre_order, + LCA->_nodes[0]->_idx, + start_latency, + LCA->_nodes[LCA->end_idx()]->_idx, + end_latency, + least_freq); + } +#endif + + // Walk up the dominator tree from LCA (Lowest common ancestor) to + // the earliest legal location. Capture the least execution frequency. + while (LCA != early) { + LCA = LCA->_idom; // Follow up the dominator tree + + if (LCA == NULL) { + // Bailout without retry + C->record_method_not_compilable("late schedule failed: LCA == NULL"); + return least; + } + + // Don't hoist machine instructions to the root basic block + if (mach && LCA == root_block) + break; + + uint start_lat = _node_latency.at_grow(LCA->_nodes[0]->_idx); + uint end_idx = LCA->end_idx(); + uint end_lat = _node_latency.at_grow(LCA->_nodes[end_idx]->_idx); + double LCA_freq = LCA->_freq; +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print_cr("# B%d: start latency for [%4d]=%d, end latency for [%4d]=%d, freq=%g", + LCA->_pre_order, LCA->_nodes[0]->_idx, start_lat, end_idx, end_lat, LCA_freq); + } +#endif + if (LCA_freq < least_freq || // Better Frequency + ( !in_latency && // No block containing latency + LCA_freq < least_freq * delta && // No worse frequency + target >= end_lat && // within latency range + !self->is_iteratively_computed() ) // But don't hoist IV increments + // because they may end up above other uses of their phi forcing + // their result register to be different from their input. + ) { + least = LCA; // Found cheaper block + least_freq = LCA_freq; + start_latency = start_lat; + end_latency = end_lat; + if (target <= start_lat) + in_latency = true; + } + } + +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print_cr("# Choose block B%d with start latency=%d and freq=%g", + least->_pre_order, start_latency, least_freq); + } +#endif + + // See if the latency needs to be updated + if (target < end_latency) { +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print_cr("# Change latency for [%4d] from %d to %d", self->_idx, target, end_latency); + } +#endif + _node_latency.at_put_grow(self->_idx, end_latency); + partial_latency_of_defs(self); + } + + return least; +} + + +//------------------------------schedule_late----------------------------------- +// Now schedule all codes as LATE as possible. This is the LCA in the +// dominator tree of all USES of a value. Pick the block with the least +// loop nesting depth that is lowest in the dominator tree. +extern const char must_clone[]; +void PhaseCFG::schedule_late(VectorSet &visited, Node_List &stack) { +#ifndef PRODUCT + if (trace_opto_pipelining()) + tty->print("\n#---- schedule_late ----\n"); +#endif + + Node_Backward_Iterator iter((Node *)_root, visited, stack, _bbs); + Node *self; + + // Walk over all the nodes from last to first + while (self = iter.next()) { + Block* early = _bbs[self->_idx]; // Earliest legal placement + + if (self->is_top()) { + // Top node goes in bb #2 with other constants. + // It must be special-cased, because it has no out edges. + early->add_inst(self); + continue; + } + + // No uses, just terminate + if (self->outcnt() == 0) { + assert(self->Opcode() == Op_MachProj, "sanity"); + continue; // Must be a dead machine projection + } + + // If node is pinned in the block, then no scheduling can be done. + if( self->pinned() ) // Pinned in block? + continue; + + MachNode* mach = self->is_Mach() ? self->as_Mach() : NULL; + if (mach) { + switch (mach->ideal_Opcode()) { + case Op_CreateEx: + // Don't move exception creation + early->add_inst(self); + continue; + break; + case Op_CheckCastPP: + // Don't move CheckCastPP nodes away from their input, if the input + // is a rawptr (5071820). + Node *def = self->in(1); + if (def != NULL && def->bottom_type()->base() == Type::RawPtr) { + early->add_inst(self); + continue; + } + break; + } + } + + // Gather LCA of all uses + Block *LCA = NULL; + { + for (DUIterator_Fast imax, i = self->fast_outs(imax); i < imax; i++) { + // For all uses, find LCA + Node* use = self->fast_out(i); + LCA = raise_LCA_above_use(LCA, use, self, _bbs); + } + } // (Hide defs of imax, i from rest of block.) + + // Place temps in the block of their use. This isn't a + // requirement for correctness but it reduces useless + // interference between temps and other nodes. + if (mach != NULL && mach->is_MachTemp()) { + _bbs.map(self->_idx, LCA); + LCA->add_inst(self); + continue; + } + + // Check if 'self' could be anti-dependent on memory + if (self->needs_anti_dependence_check()) { + // Hoist LCA above possible-defs and insert anti-dependences to + // defs in new LCA block. + LCA = insert_anti_dependences(LCA, self); + } + + if (early->_dom_depth > LCA->_dom_depth) { + // Somehow the LCA has moved above the earliest legal point. + // (One way this can happen is via memory_early_block.) + if (C->subsume_loads() == true && !C->failing()) { + // Retry with subsume_loads == false + // If this is the first failure, the sentinel string will "stick" + // to the Compile object, and the C2Compiler will see it and retry. + C->record_failure(C2Compiler::retry_no_subsuming_loads()); + } else { + // Bailout without retry when (early->_dom_depth > LCA->_dom_depth) + C->record_method_not_compilable("late schedule failed: incorrect graph"); + } + return; + } + + // If there is no opportunity to hoist, then we're done. + bool try_to_hoist = (LCA != early); + + // Must clone guys stay next to use; no hoisting allowed. + // Also cannot hoist guys that alter memory or are otherwise not + // allocatable (hoisting can make a value live longer, leading to + // anti and output dependency problems which are normally resolved + // by the register allocator giving everyone a different register). + if (mach != NULL && must_clone[mach->ideal_Opcode()]) + try_to_hoist = false; + + Block* late = NULL; + if (try_to_hoist) { + // Now find the block with the least execution frequency. + // Start at the latest schedule and work up to the earliest schedule + // in the dominator tree. Thus the Node will dominate all its uses. + late = hoist_to_cheaper_block(LCA, early, self); + } else { + // Just use the LCA of the uses. + late = LCA; + } + + // Put the node into target block + schedule_node_into_block(self, late); + +#ifdef ASSERT + if (self->needs_anti_dependence_check()) { + // since precedence edges are only inserted when we're sure they + // are needed make sure that after placement in a block we don't + // need any new precedence edges. + verify_anti_dependences(late, self); + } +#endif + } // Loop until all nodes have been visited + +} // end ScheduleLate + +//------------------------------GlobalCodeMotion------------------------------- +void PhaseCFG::GlobalCodeMotion( Matcher &matcher, uint unique, Node_List &proj_list ) { + ResourceMark rm; + +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print("\n---- Start GlobalCodeMotion ----\n"); + } +#endif + + // Initialize the bbs.map for things on the proj_list + uint i; + for( i=0; i < proj_list.size(); i++ ) + _bbs.map(proj_list[i]->_idx, NULL); + + // Set the basic block for Nodes pinned into blocks + Arena *a = Thread::current()->resource_area(); + VectorSet visited(a); + schedule_pinned_nodes( visited ); + + // Find the earliest Block any instruction can be placed in. Some + // instructions are pinned into Blocks. Unpinned instructions can + // appear in last block in which all their inputs occur. + visited.Clear(); + Node_List stack(a); + stack.map( (unique >> 1) + 16, NULL); // Pre-grow the list + if (!schedule_early(visited, stack)) { + // Bailout without retry + C->record_method_not_compilable("early schedule failed"); + return; + } + + // Build Def-Use edges. + proj_list.push(_root); // Add real root as another root + proj_list.pop(); + + // Compute the latency information (via backwards walk) for all the + // instructions in the graph + GrowableArray node_latency; + _node_latency = node_latency; + + if( C->do_scheduling() ) + ComputeLatenciesBackwards(visited, stack); + + // Now schedule all codes as LATE as possible. This is the LCA in the + // dominator tree of all USES of a value. Pick the block with the least + // loop nesting depth that is lowest in the dominator tree. + // ( visited.Clear() called in schedule_late()->Node_Backward_Iterator() ) + schedule_late(visited, stack); + if( C->failing() ) { + // schedule_late fails only when graph is incorrect. + assert(!VerifyGraphEdges, "verification should have failed"); + return; + } + + unique = C->unique(); + +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print("\n---- Detect implicit null checks ----\n"); + } +#endif + + // Detect implicit-null-check opportunities. Basically, find NULL checks + // with suitable memory ops nearby. Use the memory op to do the NULL check. + // I can generate a memory op if there is not one nearby. + if (C->is_method_compilation()) { + // Don't do it for natives, adapters, or runtime stubs + int allowed_reasons = 0; + // ...and don't do it when there have been too many traps, globally. + for (int reason = (int)Deoptimization::Reason_none+1; + reason < Compile::trapHistLength; reason++) { + assert(reason < BitsPerInt, "recode bit map"); + if (!C->too_many_traps((Deoptimization::DeoptReason) reason)) + allowed_reasons |= nth_bit(reason); + } + // By reversing the loop direction we get a very minor gain on mpegaudio. + // Feel free to revert to a forward loop for clarity. + // for( int i=0; i < (int)matcher._null_check_tests.size(); i+=2 ) { + for( int i= matcher._null_check_tests.size()-2; i>=0; i-=2 ) { + Node *proj = matcher._null_check_tests[i ]; + Node *val = matcher._null_check_tests[i+1]; + _bbs[proj->_idx]->implicit_null_check(this, proj, val, allowed_reasons); + // The implicit_null_check will only perform the transformation + // if the null branch is truly uncommon, *and* it leads to an + // uncommon trap. Combined with the too_many_traps guards + // above, this prevents SEGV storms reported in 6366351, + // by recompiling offending methods without this optimization. + } + } + +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print("\n---- Start Local Scheduling ----\n"); + } +#endif + + // Schedule locally. Right now a simple topological sort. + // Later, do a real latency aware scheduler. + int *ready_cnt = NEW_RESOURCE_ARRAY(int,C->unique()); + memset( ready_cnt, -1, C->unique() * sizeof(int) ); + visited.Clear(); + for (i = 0; i < _num_blocks; i++) { + if (!_blocks[i]->schedule_local(this, matcher, ready_cnt, visited)) { + if (!C->failure_reason_is(C2Compiler::retry_no_subsuming_loads())) { + C->record_method_not_compilable("local schedule failed"); + } + return; + } + } + + // If we inserted any instructions between a Call and his CatchNode, + // clone the instructions on all paths below the Catch. + for( i=0; i < _num_blocks; i++ ) + _blocks[i]->call_catch_cleanup(_bbs); + +#ifndef PRODUCT + if (trace_opto_pipelining()) { + tty->print("\n---- After GlobalCodeMotion ----\n"); + for (uint i = 0; i < _num_blocks; i++) { + _blocks[i]->dump(); + } + } +#endif +} + + +//------------------------------Estimate_Block_Frequency----------------------- +// Estimate block frequencies based on IfNode probabilities. +void PhaseCFG::Estimate_Block_Frequency() { + int cnts = C->method() ? C->method()->interpreter_invocation_count() : 1; + // Most of our algorithms will die horribly if frequency can become + // negative so make sure cnts is a sane value. + if( cnts <= 0 ) cnts = 1; + float f = (float)cnts/(float)FreqCountInvocations; + + // Create the loop tree and calculate loop depth. + _root_loop = create_loop_tree(); + _root_loop->compute_loop_depth(0); + + // Compute block frequency of each block, relative to a single loop entry. + _root_loop->compute_freq(); + + // Adjust all frequencies to be relative to a single method entry + _root_loop->_freq = f * 1.0; + _root_loop->scale_freq(); + + // force paths ending at uncommon traps to be infrequent + Block_List worklist; + Block* root_blk = _blocks[0]; + for (uint i = 0; i < root_blk->num_preds(); i++) { + Block *pb = _bbs[root_blk->pred(i)->_idx]; + if (pb->has_uncommon_code()) { + worklist.push(pb); + } + } + while (worklist.size() > 0) { + Block* uct = worklist.pop(); + uct->_freq = PROB_MIN; + for (uint i = 0; i < uct->num_preds(); i++) { + Block *pb = _bbs[uct->pred(i)->_idx]; + if (pb->_num_succs == 1 && pb->_freq > PROB_MIN) { + worklist.push(pb); + } + } + } + +#ifndef PRODUCT + if (PrintCFGBlockFreq) { + tty->print_cr("CFG Block Frequencies"); + _root_loop->dump_tree(); + if (Verbose) { + tty->print_cr("PhaseCFG dump"); + dump(); + tty->print_cr("Node dump"); + _root->dump(99999); + } + } +#endif +} + +//----------------------------create_loop_tree-------------------------------- +// Create a loop tree from the CFG +CFGLoop* PhaseCFG::create_loop_tree() { + +#ifdef ASSERT + assert( _blocks[0] == _broot, "" ); + for (uint i = 0; i < _num_blocks; i++ ) { + Block *b = _blocks[i]; + // Check that _loop field are clear...we could clear them if not. + assert(b->_loop == NULL, "clear _loop expected"); + // Sanity check that the RPO numbering is reflected in the _blocks array. + // It doesn't have to be for the loop tree to be built, but if it is not, + // then the blocks have been reordered since dom graph building...which + // may question the RPO numbering + assert(b->_rpo == i, "unexpected reverse post order number"); + } +#endif + + int idct = 0; + CFGLoop* root_loop = new CFGLoop(idct++); + + Block_List worklist; + + // Assign blocks to loops + for(uint i = _num_blocks - 1; i > 0; i-- ) { // skip Root block + Block *b = _blocks[i]; + + if (b->head()->is_Loop()) { + Block* loop_head = b; + assert(loop_head->num_preds() - 1 == 2, "loop must have 2 predecessors"); + Node* tail_n = loop_head->pred(LoopNode::LoopBackControl); + Block* tail = _bbs[tail_n->_idx]; + + // Defensively filter out Loop nodes for non-single-entry loops. + // For all reasonable loops, the head occurs before the tail in RPO. + if (i <= tail->_rpo) { + + // The tail and (recursive) predecessors of the tail + // are made members of a new loop. + + assert(worklist.size() == 0, "nonempty worklist"); + CFGLoop* nloop = new CFGLoop(idct++); + assert(loop_head->_loop == NULL, "just checking"); + loop_head->_loop = nloop; + // Add to nloop so push_pred() will skip over inner loops + nloop->add_member(loop_head); + nloop->push_pred(loop_head, LoopNode::LoopBackControl, worklist, _bbs); + + while (worklist.size() > 0) { + Block* member = worklist.pop(); + if (member != loop_head) { + for (uint j = 1; j < member->num_preds(); j++) { + nloop->push_pred(member, j, worklist, _bbs); + } + } + } + } + } + } + + // Create a member list for each loop consisting + // of both blocks and (immediate child) loops. + for (uint i = 0; i < _num_blocks; i++) { + Block *b = _blocks[i]; + CFGLoop* lp = b->_loop; + if (lp == NULL) { + // Not assigned to a loop. Add it to the method's pseudo loop. + b->_loop = root_loop; + lp = root_loop; + } + if (lp == root_loop || b != lp->head()) { // loop heads are already members + lp->add_member(b); + } + if (lp != root_loop) { + if (lp->parent() == NULL) { + // Not a nested loop. Make it a child of the method's pseudo loop. + root_loop->add_nested_loop(lp); + } + if (b == lp->head()) { + // Add nested loop to member list of parent loop. + lp->parent()->add_member(lp); + } + } + } + + return root_loop; +} + +//------------------------------push_pred-------------------------------------- +void CFGLoop::push_pred(Block* blk, int i, Block_List& worklist, Block_Array& node_to_blk) { + Node* pred_n = blk->pred(i); + Block* pred = node_to_blk[pred_n->_idx]; + CFGLoop *pred_loop = pred->_loop; + if (pred_loop == NULL) { + // Filter out blocks for non-single-entry loops. + // For all reasonable loops, the head occurs before the tail in RPO. + if (pred->_rpo > head()->_rpo) { + pred->_loop = this; + worklist.push(pred); + } + } else if (pred_loop != this) { + // Nested loop. + while (pred_loop->_parent != NULL && pred_loop->_parent != this) { + pred_loop = pred_loop->_parent; + } + // Make pred's loop be a child + if (pred_loop->_parent == NULL) { + add_nested_loop(pred_loop); + // Continue with loop entry predecessor. + Block* pred_head = pred_loop->head(); + assert(pred_head->num_preds() - 1 == 2, "loop must have 2 predecessors"); + assert(pred_head != head(), "loop head in only one loop"); + push_pred(pred_head, LoopNode::EntryControl, worklist, node_to_blk); + } else { + assert(pred_loop->_parent == this && _parent == NULL, "just checking"); + } + } +} + +//------------------------------add_nested_loop-------------------------------- +// Make cl a child of the current loop in the loop tree. +void CFGLoop::add_nested_loop(CFGLoop* cl) { + assert(_parent == NULL, "no parent yet"); + assert(cl != this, "not my own parent"); + cl->_parent = this; + CFGLoop* ch = _child; + if (ch == NULL) { + _child = cl; + } else { + while (ch->_sibling != NULL) { ch = ch->_sibling; } + ch->_sibling = cl; + } +} + +//------------------------------compute_loop_depth----------------------------- +// Store the loop depth in each CFGLoop object. +// Recursively walk the children to do the same for them. +void CFGLoop::compute_loop_depth(int depth) { + _depth = depth; + CFGLoop* ch = _child; + while (ch != NULL) { + ch->compute_loop_depth(depth + 1); + ch = ch->_sibling; + } +} + +//------------------------------compute_freq----------------------------------- +// Compute the frequency of each block and loop, relative to a single entry +// into the dominating loop head. +void CFGLoop::compute_freq() { + // Bottom up traversal of loop tree (visit inner loops first.) + // Set loop head frequency to 1.0, then transitively + // compute frequency for all successors in the loop, + // as well as for each exit edge. Inner loops are + // treated as single blocks with loop exit targets + // as the successor blocks. + + // Nested loops first + CFGLoop* ch = _child; + while (ch != NULL) { + ch->compute_freq(); + ch = ch->_sibling; + } + assert (_members.length() > 0, "no empty loops"); + Block* hd = head(); + hd->_freq = 1.0f; + for (int i = 0; i < _members.length(); i++) { + CFGElement* s = _members.at(i); + float freq = s->_freq; + if (s->is_block()) { + Block* b = s->as_Block(); + for (uint j = 0; j < b->_num_succs; j++) { + Block* sb = b->_succs[j]; + update_succ_freq(sb, freq * b->succ_prob(j)); + } + } else { + CFGLoop* lp = s->as_CFGLoop(); + assert(lp->_parent == this, "immediate child"); + for (int k = 0; k < lp->_exits.length(); k++) { + Block* eb = lp->_exits.at(k).get_target(); + float prob = lp->_exits.at(k).get_prob(); + update_succ_freq(eb, freq * prob); + } + } + } + +#if 0 + // Raise frequency of the loop backedge block, in an effort + // to keep it empty. Skip the method level "loop". + if (_parent != NULL) { + CFGElement* s = _members.at(_members.length() - 1); + if (s->is_block()) { + Block* bk = s->as_Block(); + if (bk->_num_succs == 1 && bk->_succs[0] == hd) { + // almost any value >= 1.0f works + // FIXME: raw constant + bk->_freq = 1.05f; + } + } + } +#endif + + // For all loops other than the outer, "method" loop, + // sum and normalize the exit probability. The "method" loop + // should keep the initial exit probability of 1, so that + // inner blocks do not get erroneously scaled. + if (_depth != 0) { + // Total the exit probabilities for this loop. + float exits_sum = 0.0f; + for (int i = 0; i < _exits.length(); i++) { + exits_sum += _exits.at(i).get_prob(); + } + + // Normalize the exit probabilities. Until now, the + // probabilities estimate the possibility of exit per + // a single loop iteration; afterward, they estimate + // the probability of exit per loop entry. + for (int i = 0; i < _exits.length(); i++) { + Block* et = _exits.at(i).get_target(); + float new_prob = _exits.at(i).get_prob() / exits_sum; + BlockProbPair bpp(et, new_prob); + _exits.at_put(i, bpp); + } + + // Save the total, but guard against unreasoable probability, + // as the value is used to estimate the loop trip count. + // An infinite trip count would blur relative block + // frequencies. + if (exits_sum > 1.0f) exits_sum = 1.0; + if (exits_sum < PROB_MIN) exits_sum = PROB_MIN; + _exit_prob = exits_sum; + } +} + +//------------------------------succ_prob------------------------------------- +// Determine the probability of reaching successor 'i' from the receiver block. +float Block::succ_prob(uint i) { + int eidx = end_idx(); + Node *n = _nodes[eidx]; // Get ending Node + int op = n->is_Mach() ? n->as_Mach()->ideal_Opcode() : n->Opcode(); + + // Switch on branch type + switch( op ) { + case Op_CountedLoopEnd: + case Op_If: { + assert (i < 2, "just checking"); + // Conditionals pass on only part of their frequency + float prob = n->as_MachIf()->_prob; + assert(prob >= 0.0 && prob <= 1.0, "out of range probability"); + // If succ[i] is the FALSE branch, invert path info + if( _nodes[i + eidx + 1]->Opcode() == Op_IfFalse ) { + return 1.0f - prob; // not taken + } else { + return prob; // taken + } + } + + case Op_Jump: + // Divide the frequency between all successors evenly + return 1.0f/_num_succs; + + case Op_Catch: { + const CatchProjNode *ci = _nodes[i + eidx + 1]->as_CatchProj(); + if (ci->_con == CatchProjNode::fall_through_index) { + // Fall-thru path gets the lion's share. + return 1.0f - PROB_UNLIKELY_MAG(5)*_num_succs; + } else { + // Presume exceptional paths are equally unlikely + return PROB_UNLIKELY_MAG(5); + } + } + + case Op_Root: + case Op_Goto: + // Pass frequency straight thru to target + return 1.0f; + + case Op_NeverBranch: + return 0.0f; + + case Op_TailCall: + case Op_TailJump: + case Op_Return: + case Op_Halt: + case Op_Rethrow: + // Do not push out freq to root block + return 0.0f; + + default: + ShouldNotReachHere(); + } + + return 0.0f; +} + +//------------------------------update_succ_freq------------------------------- +// Update the appropriate frequency associated with block 'b', a succesor of +// a block in this loop. +void CFGLoop::update_succ_freq(Block* b, float freq) { + if (b->_loop == this) { + if (b == head()) { + // back branch within the loop + // Do nothing now, the loop carried frequency will be + // adjust later in scale_freq(). + } else { + // simple branch within the loop + b->_freq += freq; + } + } else if (!in_loop_nest(b)) { + // branch is exit from this loop + BlockProbPair bpp(b, freq); + _exits.append(bpp); + } else { + // branch into nested loop + CFGLoop* ch = b->_loop; + ch->_freq += freq; + } +} + +//------------------------------in_loop_nest----------------------------------- +// Determine if block b is in the receiver's loop nest. +bool CFGLoop::in_loop_nest(Block* b) { + int depth = _depth; + CFGLoop* b_loop = b->_loop; + int b_depth = b_loop->_depth; + if (depth == b_depth) { + return true; + } + while (b_depth > depth) { + b_loop = b_loop->_parent; + b_depth = b_loop->_depth; + } + return b_loop == this; +} + +//------------------------------scale_freq------------------------------------- +// Scale frequency of loops and blocks by trip counts from outer loops +// Do a top down traversal of loop tree (visit outer loops first.) +void CFGLoop::scale_freq() { + float loop_freq = _freq * trip_count(); + for (int i = 0; i < _members.length(); i++) { + CFGElement* s = _members.at(i); + s->_freq *= loop_freq; + } + CFGLoop* ch = _child; + while (ch != NULL) { + ch->scale_freq(); + ch = ch->_sibling; + } +} + +#ifndef PRODUCT +//------------------------------dump_tree-------------------------------------- +void CFGLoop::dump_tree() const { + dump(); + if (_child != NULL) _child->dump_tree(); + if (_sibling != NULL) _sibling->dump_tree(); +} + +//------------------------------dump------------------------------------------- +void CFGLoop::dump() const { + for (int i = 0; i < _depth; i++) tty->print(" "); + tty->print("%s: %d trip_count: %6.0f freq: %6.0f\n", + _depth == 0 ? "Method" : "Loop", _id, trip_count(), _freq); + for (int i = 0; i < _depth; i++) tty->print(" "); + tty->print(" members:", _id); + int k = 0; + for (int i = 0; i < _members.length(); i++) { + if (k++ >= 6) { + tty->print("\n "); + for (int j = 0; j < _depth+1; j++) tty->print(" "); + k = 0; + } + CFGElement *s = _members.at(i); + if (s->is_block()) { + Block *b = s->as_Block(); + tty->print(" B%d(%6.3f)", b->_pre_order, b->_freq); + } else { + CFGLoop* lp = s->as_CFGLoop(); + tty->print(" L%d(%6.3f)", lp->_id, lp->_freq); + } + } + tty->print("\n"); + for (int i = 0; i < _depth; i++) tty->print(" "); + tty->print(" exits: "); + k = 0; + for (int i = 0; i < _exits.length(); i++) { + if (k++ >= 7) { + tty->print("\n "); + for (int j = 0; j < _depth+1; j++) tty->print(" "); + k = 0; + } + Block *blk = _exits.at(i).get_target(); + float prob = _exits.at(i).get_prob(); + tty->print(" ->%d@%d%%", blk->_pre_order, (int)(prob*100)); + } + tty->print("\n"); +} +#endif diff --git a/hotspot/src/share/vm/opto/generateOptoStub.cpp b/hotspot/src/share/vm/opto/generateOptoStub.cpp new file mode 100644 index 00000000000..490c4e85559 --- /dev/null +++ b/hotspot/src/share/vm/opto/generateOptoStub.cpp @@ -0,0 +1,291 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_generateOptoStub.cpp.incl" + +//--------------------gen_stub------------------------------- +void GraphKit::gen_stub(address C_function, + const char *name, + int is_fancy_jump, + bool pass_tls, + bool return_pc) { + ResourceMark rm; + + const TypeTuple *jdomain = C->tf()->domain(); + const TypeTuple *jrange = C->tf()->range(); + + // The procedure start + StartNode* start = new (C, 2) StartNode(root(), jdomain); + _gvn.set_type_bottom(start); + + // Make a map, with JVM state + uint parm_cnt = jdomain->cnt(); + uint max_map = MAX2(2*parm_cnt+1, jrange->cnt()); + // %%% SynchronizationEntryBCI is redundant; use InvocationEntryBci in interfaces + assert(SynchronizationEntryBCI == InvocationEntryBci, ""); + JVMState* jvms = new (C) JVMState(0); + jvms->set_bci(InvocationEntryBci); + jvms->set_monoff(max_map); + jvms->set_endoff(max_map); + { + SafePointNode *map = new (C, max_map) SafePointNode( max_map, jvms ); + jvms->set_map(map); + set_jvms(jvms); + assert(map == this->map(), "kit.map is set"); + } + + // Make up the parameters + uint i; + for( i = 0; i < parm_cnt; i++ ) + map()->init_req(i, _gvn.transform(new (C, 1) ParmNode(start, i))); + for( ; ireq(); i++ ) + map()->init_req(i, top()); // For nicer debugging + + // GraphKit requires memory to be a MergeMemNode: + set_all_memory(map()->memory()); + + // Get base of thread-local storage area + Node* thread = _gvn.transform( new (C, 1) ThreadLocalNode() ); + + const int NoAlias = Compile::AliasIdxBot; + + Node* adr_last_Java_pc = basic_plus_adr(top(), + thread, + in_bytes(JavaThread::frame_anchor_offset()) + + in_bytes(JavaFrameAnchor::last_Java_pc_offset())); +#if defined(SPARC) || defined(IA64) + Node* adr_flags = basic_plus_adr(top(), + thread, + in_bytes(JavaThread::frame_anchor_offset()) + + in_bytes(JavaFrameAnchor::flags_offset())); +#endif /* defined(SPARC) || defined(IA64) */ + + + // Drop in the last_Java_sp. last_Java_fp is not touched. + // Always do this after the other "last_Java_frame" fields are set since + // as soon as last_Java_sp != NULL the has_last_Java_frame is true and + // users will look at the other fields. + // + Node *adr_sp = basic_plus_adr(top(), thread, in_bytes(JavaThread::last_Java_sp_offset())); +#ifndef IA64 + Node *last_sp = basic_plus_adr(top(), frameptr(), (intptr_t) STACK_BIAS); + store_to_memory(NULL, adr_sp, last_sp, T_ADDRESS, NoAlias); +#endif + + // Set _thread_in_native + // The order of stores into TLS is critical! Setting _thread_in_native MUST + // be last, because a GC is allowed at any time after setting it and the GC + // will require last_Java_pc and last_Java_sp. + Node* adr_state = basic_plus_adr(top(), thread, in_bytes(JavaThread::thread_state_offset())); + + //----------------------------- + // Compute signature for C call. Varies from the Java signature! + const Type **fields = TypeTuple::fields(2*parm_cnt+2); + uint cnt = TypeFunc::Parms; + // The C routines gets the base of thread-local storage passed in as an + // extra argument. Not all calls need it, but its cheap to add here. + for( ; cntfield_at(cnt); + fields[cnt++] = TypeRawPtr::BOTTOM; // Thread-local storage + // Also pass in the caller's PC, if asked for. + if( return_pc ) + fields[cnt++] = TypeRawPtr::BOTTOM; // Return PC + + const TypeTuple* domain = TypeTuple::make(cnt,fields); + // The C routine we are about to call cannot return an oop; it can block on + // exit and a GC will trash the oop while it sits in C-land. Instead, we + // return the oop through TLS for runtime calls. + // Also, C routines returning integer subword values leave the high + // order bits dirty; these must be cleaned up by explicit sign extension. + const Type* retval = (jrange->cnt() == TypeFunc::Parms) ? Type::TOP : jrange->field_at(TypeFunc::Parms); + // Make a private copy of jrange->fields(); + const Type **rfields = TypeTuple::fields(jrange->cnt() - TypeFunc::Parms); + // Fixup oop returns + int retval_ptr = retval->isa_oop_ptr(); + if( retval_ptr ) { + assert( pass_tls, "Oop must be returned thru TLS" ); + // Fancy-jumps return address; others return void + rfields[TypeFunc::Parms] = is_fancy_jump ? TypeRawPtr::BOTTOM : Type::TOP; + + } else if( retval->isa_int() ) { // Returning any integer subtype? + // "Fatten" byte, char & short return types to 'int' to show that + // the native C code can return values with junk high order bits. + // We'll sign-extend it below later. + rfields[TypeFunc::Parms] = TypeInt::INT; // It's "dirty" and needs sign-ext + + } else if( jrange->cnt() >= TypeFunc::Parms+1 ) { // Else copy other types + rfields[TypeFunc::Parms] = jrange->field_at(TypeFunc::Parms); + if( jrange->cnt() == TypeFunc::Parms+2 ) + rfields[TypeFunc::Parms+1] = jrange->field_at(TypeFunc::Parms+1); + } + const TypeTuple* range = TypeTuple::make(jrange->cnt(),rfields); + + // Final C signature + const TypeFunc *c_sig = TypeFunc::make(domain,range); + + //----------------------------- + // Make the call node + CallRuntimeNode *call = new (C, c_sig->domain()->cnt()) + CallRuntimeNode(c_sig, C_function, name, TypePtr::BOTTOM); + //----------------------------- + + // Fix-up the debug info for the call + call->set_jvms( new (C) JVMState(0) ); + call->jvms()->set_bci(0); + call->jvms()->set_offsets(cnt); + + // Set fixed predefined input arguments + cnt = 0; + for( i=0; iinit_req( cnt++, map()->in(i) ); + // A little too aggressive on the parm copy; return address is not an input + call->set_req(TypeFunc::ReturnAdr, top()); + for( ; iinit_req( cnt++, map()->in(i) ); + + call->init_req( cnt++, thread ); + if( return_pc ) // Return PC, if asked for + call->init_req( cnt++, returnadr() ); + _gvn.transform_no_reclaim(call); + + + //----------------------------- + // Now set up the return results + set_control( _gvn.transform( new (C, 1) ProjNode(call,TypeFunc::Control)) ); + set_i_o( _gvn.transform( new (C, 1) ProjNode(call,TypeFunc::I_O )) ); + set_all_memory_call(call); + if (range->cnt() > TypeFunc::Parms) { + Node* retnode = _gvn.transform( new (C, 1) ProjNode(call,TypeFunc::Parms) ); + // C-land is allowed to return sub-word values. Convert to integer type. + assert( retval != Type::TOP, "" ); + if (retval == TypeInt::BOOL) { + retnode = _gvn.transform( new (C, 3) AndINode(retnode, intcon(0xFF)) ); + } else if (retval == TypeInt::CHAR) { + retnode = _gvn.transform( new (C, 3) AndINode(retnode, intcon(0xFFFF)) ); + } else if (retval == TypeInt::BYTE) { + retnode = _gvn.transform( new (C, 3) LShiftINode(retnode, intcon(24)) ); + retnode = _gvn.transform( new (C, 3) RShiftINode(retnode, intcon(24)) ); + } else if (retval == TypeInt::SHORT) { + retnode = _gvn.transform( new (C, 3) LShiftINode(retnode, intcon(16)) ); + retnode = _gvn.transform( new (C, 3) RShiftINode(retnode, intcon(16)) ); + } + map()->set_req( TypeFunc::Parms, retnode ); + } + + //----------------------------- + + // Clear last_Java_sp +#ifdef IA64 + if( os::is_MP() ) insert_mem_bar(Op_MemBarRelease); +#endif + + store_to_memory(NULL, adr_sp, null(), T_ADDRESS, NoAlias); +#ifdef IA64 + if (os::is_MP() && UseMembar) insert_mem_bar(new MemBarVolatileNode()); +#endif // def IA64 + // Clear last_Java_pc and (optionally)_flags + store_to_memory(NULL, adr_last_Java_pc, null(), T_ADDRESS, NoAlias); +#if defined(SPARC) || defined(IA64) + store_to_memory(NULL, adr_flags, intcon(0), T_INT, NoAlias); +#endif /* defined(SPARC) || defined(IA64) */ +#ifdef IA64 + Node* adr_last_Java_fp = basic_plus_adr(top(), thread, in_bytes(JavaThread::last_Java_fp_offset())); + if( os::is_MP() ) insert_mem_bar(Op_MemBarRelease); + store_to_memory(NULL, adr_last_Java_fp, null(), T_ADDRESS, NoAlias); +#endif + + // For is-fancy-jump, the C-return value is also the branch target + Node* target = map()->in(TypeFunc::Parms); + // Runtime call returning oop in TLS? Fetch it out + if( pass_tls ) { + Node* adr = basic_plus_adr(top(), thread, in_bytes(JavaThread::vm_result_offset())); + Node* vm_result = make_load(NULL, adr, TypeOopPtr::BOTTOM, T_OBJECT, NoAlias, false); + map()->set_req(TypeFunc::Parms, vm_result); // vm_result passed as result + // clear thread-local-storage(tls) + store_to_memory(NULL, adr, null(), T_ADDRESS, NoAlias); + } + + //----------------------------- + // check exception + Node* adr = basic_plus_adr(top(), thread, in_bytes(Thread::pending_exception_offset())); + Node* pending = make_load(NULL, adr, TypeOopPtr::BOTTOM, T_OBJECT, NoAlias, false); + + Node* exit_memory = reset_memory(); + + Node* cmp = _gvn.transform( new (C, 3) CmpPNode(pending, null()) ); + Node* bo = _gvn.transform( new (C, 2) BoolNode(cmp, BoolTest::ne) ); + IfNode *iff = create_and_map_if(control(), bo, PROB_MIN, COUNT_UNKNOWN); + + Node* if_null = _gvn.transform( new (C, 1) IfFalseNode(iff) ); + Node* if_not_null = _gvn.transform( new (C, 1) IfTrueNode(iff) ); + + assert (StubRoutines::forward_exception_entry() != NULL, "must be generated before"); + Node *exc_target = makecon(TypeRawPtr::make( StubRoutines::forward_exception_entry() )); + Node *to_exc = new (C, TypeFunc::Parms+2) TailCallNode(if_not_null, + i_o(), + exit_memory, + frameptr(), + returnadr(), + exc_target, null()); + root()->add_req(_gvn.transform(to_exc)); // bind to root to keep live + C->init_start(start); + + //----------------------------- + // If this is a normal subroutine return, issue the return and be done. + Node *ret; + switch( is_fancy_jump ) { + case 0: // Make a return instruction + // Return to caller, free any space for return address + ret = new (C, TypeFunc::Parms) ReturnNode(TypeFunc::Parms, if_null, + i_o(), + exit_memory, + frameptr(), + returnadr()); + if (C->tf()->range()->cnt() > TypeFunc::Parms) + ret->add_req( map()->in(TypeFunc::Parms) ); + break; + case 1: // This is a fancy tail-call jump. Jump to computed address. + // Jump to new callee; leave old return address alone. + ret = new (C, TypeFunc::Parms+2) TailCallNode(if_null, + i_o(), + exit_memory, + frameptr(), + returnadr(), + target, map()->in(TypeFunc::Parms)); + break; + case 2: // Pop return address & jump + // Throw away old return address; jump to new computed address + //assert(C_function == CAST_FROM_FN_PTR(address, OptoRuntime::rethrow_C), "fancy_jump==2 only for rethrow"); + ret = new (C, TypeFunc::Parms+2) TailJumpNode(if_null, + i_o(), + exit_memory, + frameptr(), + target, map()->in(TypeFunc::Parms)); + break; + default: + ShouldNotReachHere(); + } + root()->add_req(_gvn.transform(ret)); +} diff --git a/hotspot/src/share/vm/opto/graphKit.cpp b/hotspot/src/share/vm/opto/graphKit.cpp new file mode 100644 index 00000000000..8df5f4272f4 --- /dev/null +++ b/hotspot/src/share/vm/opto/graphKit.cpp @@ -0,0 +1,3146 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_graphKit.cpp.incl" + +//----------------------------GraphKit----------------------------------------- +// Main utility constructor. +GraphKit::GraphKit(JVMState* jvms) + : Phase(Phase::Parser), + _env(C->env()), + _gvn(*C->initial_gvn()) +{ + _exceptions = jvms->map()->next_exception(); + if (_exceptions != NULL) jvms->map()->set_next_exception(NULL); + set_jvms(jvms); +} + +// Private constructor for parser. +GraphKit::GraphKit() + : Phase(Phase::Parser), + _env(C->env()), + _gvn(*C->initial_gvn()) +{ + _exceptions = NULL; + set_map(NULL); + debug_only(_sp = -99); + debug_only(set_bci(-99)); +} + + + +//---------------------------clean_stack--------------------------------------- +// Clear away rubbish from the stack area of the JVM state. +// This destroys any arguments that may be waiting on the stack. +void GraphKit::clean_stack(int from_sp) { + SafePointNode* map = this->map(); + JVMState* jvms = this->jvms(); + int stk_size = jvms->stk_size(); + int stkoff = jvms->stkoff(); + Node* top = this->top(); + for (int i = from_sp; i < stk_size; i++) { + if (map->in(stkoff + i) != top) { + map->set_req(stkoff + i, top); + } + } +} + + +//--------------------------------sync_jvms----------------------------------- +// Make sure our current jvms agrees with our parse state. +JVMState* GraphKit::sync_jvms() const { + JVMState* jvms = this->jvms(); + jvms->set_bci(bci()); // Record the new bci in the JVMState + jvms->set_sp(sp()); // Record the new sp in the JVMState + assert(jvms_in_sync(), "jvms is now in sync"); + return jvms; +} + +#ifdef ASSERT +bool GraphKit::jvms_in_sync() const { + Parse* parse = is_Parse(); + if (parse == NULL) { + if (bci() != jvms()->bci()) return false; + if (sp() != (int)jvms()->sp()) return false; + return true; + } + if (jvms()->method() != parse->method()) return false; + if (jvms()->bci() != parse->bci()) return false; + int jvms_sp = jvms()->sp(); + if (jvms_sp != parse->sp()) return false; + int jvms_depth = jvms()->depth(); + if (jvms_depth != parse->depth()) return false; + return true; +} + +// Local helper checks for special internal merge points +// used to accumulate and merge exception states. +// They are marked by the region's in(0) edge being the map itself. +// Such merge points must never "escape" into the parser at large, +// until they have been handed to gvn.transform. +static bool is_hidden_merge(Node* reg) { + if (reg == NULL) return false; + if (reg->is_Phi()) { + reg = reg->in(0); + if (reg == NULL) return false; + } + return reg->is_Region() && reg->in(0) != NULL && reg->in(0)->is_Root(); +} + +void GraphKit::verify_map() const { + if (map() == NULL) return; // null map is OK + assert(map()->req() <= jvms()->endoff(), "no extra garbage on map"); + assert(!map()->has_exceptions(), "call add_exception_states_from 1st"); + assert(!is_hidden_merge(control()), "call use_exception_state, not set_map"); +} + +void GraphKit::verify_exception_state(SafePointNode* ex_map) { + assert(ex_map->next_exception() == NULL, "not already part of a chain"); + assert(has_saved_ex_oop(ex_map), "every exception state has an ex_oop"); +} +#endif + +//---------------------------stop_and_kill_map--------------------------------- +// Set _map to NULL, signalling a stop to further bytecode execution. +// First smash the current map's control to a constant, to mark it dead. +void GraphKit::stop_and_kill_map() { + SafePointNode* dead_map = stop(); + if (dead_map != NULL) { + dead_map->disconnect_inputs(NULL); // Mark the map as killed. + assert(dead_map->is_killed(), "must be so marked"); + } +} + + +//--------------------------------stopped-------------------------------------- +// Tell if _map is NULL, or control is top. +bool GraphKit::stopped() { + if (map() == NULL) return true; + else if (control() == top()) return true; + else return false; +} + + +//-----------------------------has_ex_handler---------------------------------- +// Tell if this method or any caller method has exception handlers. +bool GraphKit::has_ex_handler() { + for (JVMState* jvmsp = jvms(); jvmsp != NULL; jvmsp = jvmsp->caller()) { + if (jvmsp->has_method() && jvmsp->method()->has_exception_handlers()) { + return true; + } + } + return false; +} + +//------------------------------save_ex_oop------------------------------------ +// Save an exception without blowing stack contents or other JVM state. +void GraphKit::set_saved_ex_oop(SafePointNode* ex_map, Node* ex_oop) { + assert(!has_saved_ex_oop(ex_map), "clear ex-oop before setting again"); + ex_map->add_req(ex_oop); + debug_only(verify_exception_state(ex_map)); +} + +inline static Node* common_saved_ex_oop(SafePointNode* ex_map, bool clear_it) { + assert(GraphKit::has_saved_ex_oop(ex_map), "ex_oop must be there"); + Node* ex_oop = ex_map->in(ex_map->req()-1); + if (clear_it) ex_map->del_req(ex_map->req()-1); + return ex_oop; +} + +//-----------------------------saved_ex_oop------------------------------------ +// Recover a saved exception from its map. +Node* GraphKit::saved_ex_oop(SafePointNode* ex_map) { + return common_saved_ex_oop(ex_map, false); +} + +//--------------------------clear_saved_ex_oop--------------------------------- +// Erase a previously saved exception from its map. +Node* GraphKit::clear_saved_ex_oop(SafePointNode* ex_map) { + return common_saved_ex_oop(ex_map, true); +} + +#ifdef ASSERT +//---------------------------has_saved_ex_oop---------------------------------- +// Erase a previously saved exception from its map. +bool GraphKit::has_saved_ex_oop(SafePointNode* ex_map) { + return ex_map->req() == ex_map->jvms()->endoff()+1; +} +#endif + +//-------------------------make_exception_state-------------------------------- +// Turn the current JVM state into an exception state, appending the ex_oop. +SafePointNode* GraphKit::make_exception_state(Node* ex_oop) { + sync_jvms(); + SafePointNode* ex_map = stop(); // do not manipulate this map any more + set_saved_ex_oop(ex_map, ex_oop); + return ex_map; +} + + +//--------------------------add_exception_state-------------------------------- +// Add an exception to my list of exceptions. +void GraphKit::add_exception_state(SafePointNode* ex_map) { + if (ex_map == NULL || ex_map->control() == top()) { + return; + } +#ifdef ASSERT + verify_exception_state(ex_map); + if (has_exceptions()) { + assert(ex_map->jvms()->same_calls_as(_exceptions->jvms()), "all collected exceptions must come from the same place"); + } +#endif + + // If there is already an exception of exactly this type, merge with it. + // In particular, null-checks and other low-level exceptions common up here. + Node* ex_oop = saved_ex_oop(ex_map); + const Type* ex_type = _gvn.type(ex_oop); + if (ex_oop == top()) { + // No action needed. + return; + } + assert(ex_type->isa_instptr(), "exception must be an instance"); + for (SafePointNode* e2 = _exceptions; e2 != NULL; e2 = e2->next_exception()) { + const Type* ex_type2 = _gvn.type(saved_ex_oop(e2)); + // We check sp also because call bytecodes can generate exceptions + // both before and after arguments are popped! + if (ex_type2 == ex_type + && e2->_jvms->sp() == ex_map->_jvms->sp()) { + combine_exception_states(ex_map, e2); + return; + } + } + + // No pre-existing exception of the same type. Chain it on the list. + push_exception_state(ex_map); +} + +//-----------------------add_exception_states_from----------------------------- +void GraphKit::add_exception_states_from(JVMState* jvms) { + SafePointNode* ex_map = jvms->map()->next_exception(); + if (ex_map != NULL) { + jvms->map()->set_next_exception(NULL); + for (SafePointNode* next_map; ex_map != NULL; ex_map = next_map) { + next_map = ex_map->next_exception(); + ex_map->set_next_exception(NULL); + add_exception_state(ex_map); + } + } +} + +//-----------------------transfer_exceptions_into_jvms------------------------- +JVMState* GraphKit::transfer_exceptions_into_jvms() { + if (map() == NULL) { + // We need a JVMS to carry the exceptions, but the map has gone away. + // Create a scratch JVMS, cloned from any of the exception states... + if (has_exceptions()) { + _map = _exceptions; + _map = clone_map(); + _map->set_next_exception(NULL); + clear_saved_ex_oop(_map); + debug_only(verify_map()); + } else { + // ...or created from scratch + JVMState* jvms = new (C) JVMState(_method, NULL); + jvms->set_bci(_bci); + jvms->set_sp(_sp); + jvms->set_map(new (C, TypeFunc::Parms) SafePointNode(TypeFunc::Parms, jvms)); + set_jvms(jvms); + for (uint i = 0; i < map()->req(); i++) map()->init_req(i, top()); + set_all_memory(top()); + while (map()->req() < jvms->endoff()) map()->add_req(top()); + } + // (This is a kludge, in case you didn't notice.) + set_control(top()); + } + JVMState* jvms = sync_jvms(); + assert(!jvms->map()->has_exceptions(), "no exceptions on this map yet"); + jvms->map()->set_next_exception(_exceptions); + _exceptions = NULL; // done with this set of exceptions + return jvms; +} + +static inline void add_n_reqs(Node* dstphi, Node* srcphi) { + assert(is_hidden_merge(dstphi), "must be a special merge node"); + assert(is_hidden_merge(srcphi), "must be a special merge node"); + uint limit = srcphi->req(); + for (uint i = PhiNode::Input; i < limit; i++) { + dstphi->add_req(srcphi->in(i)); + } +} +static inline void add_one_req(Node* dstphi, Node* src) { + assert(is_hidden_merge(dstphi), "must be a special merge node"); + assert(!is_hidden_merge(src), "must not be a special merge node"); + dstphi->add_req(src); +} + +//-----------------------combine_exception_states------------------------------ +// This helper function combines exception states by building phis on a +// specially marked state-merging region. These regions and phis are +// untransformed, and can build up gradually. The region is marked by +// having a control input of its exception map, rather than NULL. Such +// regions do not appear except in this function, and in use_exception_state. +void GraphKit::combine_exception_states(SafePointNode* ex_map, SafePointNode* phi_map) { + if (failing()) return; // dying anyway... + JVMState* ex_jvms = ex_map->_jvms; + assert(ex_jvms->same_calls_as(phi_map->_jvms), "consistent call chains"); + assert(ex_jvms->stkoff() == phi_map->_jvms->stkoff(), "matching locals"); + assert(ex_jvms->sp() == phi_map->_jvms->sp(), "matching stack sizes"); + assert(ex_jvms->monoff() == phi_map->_jvms->monoff(), "matching JVMS"); + assert(ex_map->req() == phi_map->req(), "matching maps"); + uint tos = ex_jvms->stkoff() + ex_jvms->sp(); + Node* hidden_merge_mark = root(); + Node* region = phi_map->control(); + MergeMemNode* phi_mem = phi_map->merged_memory(); + MergeMemNode* ex_mem = ex_map->merged_memory(); + if (region->in(0) != hidden_merge_mark) { + // The control input is not (yet) a specially-marked region in phi_map. + // Make it so, and build some phis. + region = new (C, 2) RegionNode(2); + _gvn.set_type(region, Type::CONTROL); + region->set_req(0, hidden_merge_mark); // marks an internal ex-state + region->init_req(1, phi_map->control()); + phi_map->set_control(region); + Node* io_phi = PhiNode::make(region, phi_map->i_o(), Type::ABIO); + record_for_igvn(io_phi); + _gvn.set_type(io_phi, Type::ABIO); + phi_map->set_i_o(io_phi); + for (MergeMemStream mms(phi_mem); mms.next_non_empty(); ) { + Node* m = mms.memory(); + Node* m_phi = PhiNode::make(region, m, Type::MEMORY, mms.adr_type(C)); + record_for_igvn(m_phi); + _gvn.set_type(m_phi, Type::MEMORY); + mms.set_memory(m_phi); + } + } + + // Either or both of phi_map and ex_map might already be converted into phis. + Node* ex_control = ex_map->control(); + // if there is special marking on ex_map also, we add multiple edges from src + bool add_multiple = (ex_control->in(0) == hidden_merge_mark); + // how wide was the destination phi_map, originally? + uint orig_width = region->req(); + + if (add_multiple) { + add_n_reqs(region, ex_control); + add_n_reqs(phi_map->i_o(), ex_map->i_o()); + } else { + // ex_map has no merges, so we just add single edges everywhere + add_one_req(region, ex_control); + add_one_req(phi_map->i_o(), ex_map->i_o()); + } + for (MergeMemStream mms(phi_mem, ex_mem); mms.next_non_empty2(); ) { + if (mms.is_empty()) { + // get a copy of the base memory, and patch some inputs into it + const TypePtr* adr_type = mms.adr_type(C); + Node* phi = mms.force_memory()->as_Phi()->slice_memory(adr_type); + assert(phi->as_Phi()->region() == mms.base_memory()->in(0), ""); + mms.set_memory(phi); + // Prepare to append interesting stuff onto the newly sliced phi: + while (phi->req() > orig_width) phi->del_req(phi->req()-1); + } + // Append stuff from ex_map: + if (add_multiple) { + add_n_reqs(mms.memory(), mms.memory2()); + } else { + add_one_req(mms.memory(), mms.memory2()); + } + } + uint limit = ex_map->req(); + for (uint i = TypeFunc::Parms; i < limit; i++) { + // Skip everything in the JVMS after tos. (The ex_oop follows.) + if (i == tos) i = ex_jvms->monoff(); + Node* src = ex_map->in(i); + Node* dst = phi_map->in(i); + if (src != dst) { + PhiNode* phi; + if (dst->in(0) != region) { + dst = phi = PhiNode::make(region, dst, _gvn.type(dst)); + record_for_igvn(phi); + _gvn.set_type(phi, phi->type()); + phi_map->set_req(i, dst); + // Prepare to append interesting stuff onto the new phi: + while (dst->req() > orig_width) dst->del_req(dst->req()-1); + } else { + assert(dst->is_Phi(), "nobody else uses a hidden region"); + phi = (PhiNode*)dst; + } + if (add_multiple && src->in(0) == ex_control) { + // Both are phis. + add_n_reqs(dst, src); + } else { + while (dst->req() < region->req()) add_one_req(dst, src); + } + const Type* srctype = _gvn.type(src); + if (phi->type() != srctype) { + const Type* dsttype = phi->type()->meet(srctype); + if (phi->type() != dsttype) { + phi->set_type(dsttype); + _gvn.set_type(phi, dsttype); + } + } + } + } +} + +//--------------------------use_exception_state-------------------------------- +Node* GraphKit::use_exception_state(SafePointNode* phi_map) { + if (failing()) { stop(); return top(); } + Node* region = phi_map->control(); + Node* hidden_merge_mark = root(); + assert(phi_map->jvms()->map() == phi_map, "sanity: 1-1 relation"); + Node* ex_oop = clear_saved_ex_oop(phi_map); + if (region->in(0) == hidden_merge_mark) { + // Special marking for internal ex-states. Process the phis now. + region->set_req(0, region); // now it's an ordinary region + set_jvms(phi_map->jvms()); // ...so now we can use it as a map + // Note: Setting the jvms also sets the bci and sp. + set_control(_gvn.transform(region)); + uint tos = jvms()->stkoff() + sp(); + for (uint i = 1; i < tos; i++) { + Node* x = phi_map->in(i); + if (x->in(0) == region) { + assert(x->is_Phi(), "expected a special phi"); + phi_map->set_req(i, _gvn.transform(x)); + } + } + for (MergeMemStream mms(merged_memory()); mms.next_non_empty(); ) { + Node* x = mms.memory(); + if (x->in(0) == region) { + assert(x->is_Phi(), "nobody else uses a hidden region"); + mms.set_memory(_gvn.transform(x)); + } + } + if (ex_oop->in(0) == region) { + assert(ex_oop->is_Phi(), "expected a special phi"); + ex_oop = _gvn.transform(ex_oop); + } + } else { + set_jvms(phi_map->jvms()); + } + + assert(!is_hidden_merge(phi_map->control()), "hidden ex. states cleared"); + assert(!is_hidden_merge(phi_map->i_o()), "hidden ex. states cleared"); + return ex_oop; +} + +//---------------------------------java_bc------------------------------------- +Bytecodes::Code GraphKit::java_bc() const { + ciMethod* method = this->method(); + int bci = this->bci(); + if (method != NULL && bci != InvocationEntryBci) + return method->java_code_at_bci(bci); + else + return Bytecodes::_illegal; +} + +//------------------------------builtin_throw---------------------------------- +void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, Node* arg) { + bool must_throw = true; + + if (JvmtiExport::can_post_exceptions()) { + // Do not try anything fancy if we're notifying the VM on every throw. + // Cf. case Bytecodes::_athrow in parse2.cpp. + uncommon_trap(reason, Deoptimization::Action_none, + (ciKlass*)NULL, (char*)NULL, must_throw); + return; + } + + // If this particular condition has not yet happened at this + // bytecode, then use the uncommon trap mechanism, and allow for + // a future recompilation if several traps occur here. + // If the throw is hot, try to use a more complicated inline mechanism + // which keeps execution inside the compiled code. + bool treat_throw_as_hot = false; + ciMethodData* md = method()->method_data(); + + if (ProfileTraps) { + if (too_many_traps(reason)) { + treat_throw_as_hot = true; + } + // (If there is no MDO at all, assume it is early in + // execution, and that any deopts are part of the + // startup transient, and don't need to be remembered.) + + // Also, if there is a local exception handler, treat all throws + // as hot if there has been at least one in this method. + if (C->trap_count(reason) != 0 + && method()->method_data()->trap_count(reason) != 0 + && has_ex_handler()) { + treat_throw_as_hot = true; + } + } + + // If this throw happens frequently, an uncommon trap might cause + // a performance pothole. If there is a local exception handler, + // and if this particular bytecode appears to be deoptimizing often, + // let us handle the throw inline, with a preconstructed instance. + // Note: If the deopt count has blown up, the uncommon trap + // runtime is going to flush this nmethod, not matter what. + if (treat_throw_as_hot + && (!StackTraceInThrowable || OmitStackTraceInFastThrow)) { + // If the throw is local, we use a pre-existing instance and + // punt on the backtrace. This would lead to a missing backtrace + // (a repeat of 4292742) if the backtrace object is ever asked + // for its backtrace. + // Fixing this remaining case of 4292742 requires some flavor of + // escape analysis. Leave that for the future. + ciInstance* ex_obj = NULL; + switch (reason) { + case Deoptimization::Reason_null_check: + ex_obj = env()->NullPointerException_instance(); + break; + case Deoptimization::Reason_div0_check: + ex_obj = env()->ArithmeticException_instance(); + break; + case Deoptimization::Reason_range_check: + ex_obj = env()->ArrayIndexOutOfBoundsException_instance(); + break; + case Deoptimization::Reason_class_check: + if (java_bc() == Bytecodes::_aastore) { + ex_obj = env()->ArrayStoreException_instance(); + } else { + ex_obj = env()->ClassCastException_instance(); + } + break; + } + if (failing()) { stop(); return; } // exception allocation might fail + if (ex_obj != NULL) { + // Cheat with a preallocated exception object. + if (C->log() != NULL) + C->log()->elem("hot_throw preallocated='1' reason='%s'", + Deoptimization::trap_reason_name(reason)); + const TypeInstPtr* ex_con = TypeInstPtr::make(ex_obj); + Node* ex_node = _gvn.transform(new (C, 1) ConPNode(ex_con)); + + // Clear the detail message of the preallocated exception object. + // Weblogic sometimes mutates the detail message of exceptions + // using reflection. + int offset = java_lang_Throwable::get_detailMessage_offset(); + const TypePtr* adr_typ = ex_con->add_offset(offset); + + Node *adr = basic_plus_adr(ex_node, ex_node, offset); + Node *store = store_oop_to_object(control(), ex_node, adr, adr_typ, null(), ex_con, T_OBJECT); + + add_exception_state(make_exception_state(ex_node)); + return; + } + } + + // %%% Maybe add entry to OptoRuntime which directly throws the exc.? + // It won't be much cheaper than bailing to the interp., since we'll + // have to pass up all the debug-info, and the runtime will have to + // create the stack trace. + + // Usual case: Bail to interpreter. + // Reserve the right to recompile if we haven't seen anything yet. + + Deoptimization::DeoptAction action = Deoptimization::Action_maybe_recompile; + if (treat_throw_as_hot + && (method()->method_data()->trap_recompiled_at(bci()) + || C->too_many_traps(reason))) { + // We cannot afford to take more traps here. Suffer in the interpreter. + if (C->log() != NULL) + C->log()->elem("hot_throw preallocated='0' reason='%s' mcount='%d'", + Deoptimization::trap_reason_name(reason), + C->trap_count(reason)); + action = Deoptimization::Action_none; + } + + // "must_throw" prunes the JVM state to include only the stack, if there + // are no local exception handlers. This should cut down on register + // allocation time and code size, by drastically reducing the number + // of in-edges on the call to the uncommon trap. + + uncommon_trap(reason, action, (ciKlass*)NULL, (char*)NULL, must_throw); +} + + +//----------------------------PreserveJVMState--------------------------------- +PreserveJVMState::PreserveJVMState(GraphKit* kit, bool clone_map) { + debug_only(kit->verify_map()); + _kit = kit; + _map = kit->map(); // preserve the map + _sp = kit->sp(); + kit->set_map(clone_map ? kit->clone_map() : NULL); +#ifdef ASSERT + _bci = kit->bci(); + Parse* parser = kit->is_Parse(); + int block = (parser == NULL || parser->block() == NULL) ? -1 : parser->block()->pre_order(); + _block = block; +#endif +} +PreserveJVMState::~PreserveJVMState() { + GraphKit* kit = _kit; +#ifdef ASSERT + assert(kit->bci() == _bci, "bci must not shift"); + Parse* parser = kit->is_Parse(); + int block = (parser == NULL || parser->block() == NULL) ? -1 : parser->block()->pre_order(); + assert(block == _block, "block must not shift"); +#endif + kit->set_map(_map); + kit->set_sp(_sp); +} + + +//-----------------------------BuildCutout------------------------------------- +BuildCutout::BuildCutout(GraphKit* kit, Node* p, float prob, float cnt) + : PreserveJVMState(kit) +{ + assert(p->is_Con() || p->is_Bool(), "test must be a bool"); + SafePointNode* outer_map = _map; // preserved map is caller's + SafePointNode* inner_map = kit->map(); + IfNode* iff = kit->create_and_map_if(outer_map->control(), p, prob, cnt); + outer_map->set_control(kit->gvn().transform( new (kit->C, 1) IfTrueNode(iff) )); + inner_map->set_control(kit->gvn().transform( new (kit->C, 1) IfFalseNode(iff) )); +} +BuildCutout::~BuildCutout() { + GraphKit* kit = _kit; + assert(kit->stopped(), "cutout code must stop, throw, return, etc."); +} + + +//------------------------------clone_map-------------------------------------- +// Implementation of PreserveJVMState +// +// Only clone_map(...) here. If this function is only used in the +// PreserveJVMState class we may want to get rid of this extra +// function eventually and do it all there. + +SafePointNode* GraphKit::clone_map() { + if (map() == NULL) return NULL; + + // Clone the memory edge first + Node* mem = MergeMemNode::make(C, map()->memory()); + gvn().set_type_bottom(mem); + + SafePointNode *clonemap = (SafePointNode*)map()->clone(); + JVMState* jvms = this->jvms(); + JVMState* clonejvms = jvms->clone_shallow(C); + clonemap->set_memory(mem); + clonemap->set_jvms(clonejvms); + clonejvms->set_map(clonemap); + record_for_igvn(clonemap); + gvn().set_type_bottom(clonemap); + return clonemap; +} + + +//-----------------------------set_map_clone----------------------------------- +void GraphKit::set_map_clone(SafePointNode* m) { + _map = m; + _map = clone_map(); + _map->set_next_exception(NULL); + debug_only(verify_map()); +} + + +//----------------------------kill_dead_locals--------------------------------- +// Detect any locals which are known to be dead, and force them to top. +void GraphKit::kill_dead_locals() { + // Consult the liveness information for the locals. If any + // of them are unused, then they can be replaced by top(). This + // should help register allocation time and cut down on the size + // of the deoptimization information. + + // This call is made from many of the bytecode handling + // subroutines called from the Big Switch in do_one_bytecode. + // Every bytecode which might include a slow path is responsible + // for killing its dead locals. The more consistent we + // are about killing deads, the fewer useless phis will be + // constructed for them at various merge points. + + // bci can be -1 (InvocationEntryBci). We return the entry + // liveness for the method. + + if (method() == NULL || method()->code_size() == 0) { + // We are building a graph for a call to a native method. + // All locals are live. + return; + } + + ResourceMark rm; + + // Consult the liveness information for the locals. If any + // of them are unused, then they can be replaced by top(). This + // should help register allocation time and cut down on the size + // of the deoptimization information. + MethodLivenessResult live_locals = method()->liveness_at_bci(bci()); + + int len = (int)live_locals.size(); + assert(len <= jvms()->loc_size(), "too many live locals"); + for (int local = 0; local < len; local++) { + if (!live_locals.at(local)) { + set_local(local, top()); + } + } +} + +#ifdef ASSERT +//-------------------------dead_locals_are_killed------------------------------ +// Return true if all dead locals are set to top in the map. +// Used to assert "clean" debug info at various points. +bool GraphKit::dead_locals_are_killed() { + if (method() == NULL || method()->code_size() == 0) { + // No locals need to be dead, so all is as it should be. + return true; + } + + // Make sure somebody called kill_dead_locals upstream. + ResourceMark rm; + for (JVMState* jvms = this->jvms(); jvms != NULL; jvms = jvms->caller()) { + if (jvms->loc_size() == 0) continue; // no locals to consult + SafePointNode* map = jvms->map(); + ciMethod* method = jvms->method(); + int bci = jvms->bci(); + if (jvms == this->jvms()) { + bci = this->bci(); // it might not yet be synched + } + MethodLivenessResult live_locals = method->liveness_at_bci(bci); + int len = (int)live_locals.size(); + if (!live_locals.is_valid() || len == 0) + // This method is trivial, or is poisoned by a breakpoint. + return true; + assert(len == jvms->loc_size(), "live map consistent with locals map"); + for (int local = 0; local < len; local++) { + if (!live_locals.at(local) && map->local(jvms, local) != top()) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + tty->print_cr("Zombie local %d: ", local); + jvms->dump(); + } + return false; + } + } + } + return true; +} + +#endif //ASSERT + +// Helper function for adding JVMState and debug information to node +void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { + // Add the safepoint edges to the call (or other safepoint). + + // Make sure dead locals are set to top. This + // should help register allocation time and cut down on the size + // of the deoptimization information. + assert(dead_locals_are_killed(), "garbage in debug info before safepoint"); + + // Walk the inline list to fill in the correct set of JVMState's + // Also fill in the associated edges for each JVMState. + + JVMState* youngest_jvms = sync_jvms(); + + // Do we need debug info here? If it is a SafePoint and this method + // cannot de-opt, then we do NOT need any debug info. + bool full_info = (C->deopt_happens() || call->Opcode() != Op_SafePoint); + + // If we are guaranteed to throw, we can prune everything but the + // input to the current bytecode. + bool can_prune_locals = false; + uint stack_slots_not_pruned = 0; + int inputs = 0, depth = 0; + if (must_throw) { + assert(method() == youngest_jvms->method(), "sanity"); + if (compute_stack_effects(inputs, depth)) { + can_prune_locals = true; + stack_slots_not_pruned = inputs; + } + } + + if (JvmtiExport::can_examine_or_deopt_anywhere()) { + // At any safepoint, this method can get breakpointed, which would + // then require an immediate deoptimization. + full_info = true; + can_prune_locals = false; // do not prune locals + stack_slots_not_pruned = 0; + } + + // do not scribble on the input jvms + JVMState* out_jvms = youngest_jvms->clone_deep(C); + call->set_jvms(out_jvms); // Start jvms list for call node + + // Presize the call: + debug_only(uint non_debug_edges = call->req()); + call->add_req_batch(top(), youngest_jvms->debug_depth()); + assert(call->req() == non_debug_edges + youngest_jvms->debug_depth(), ""); + + // Set up edges so that the call looks like this: + // Call [state:] ctl io mem fptr retadr + // [parms:] parm0 ... parmN + // [root:] loc0 ... locN stk0 ... stkSP mon0 obj0 ... monN objN + // [...mid:] loc0 ... locN stk0 ... stkSP mon0 obj0 ... monN objN [...] + // [young:] loc0 ... locN stk0 ... stkSP mon0 obj0 ... monN objN + // Note that caller debug info precedes callee debug info. + + // Fill pointer walks backwards from "young:" to "root:" in the diagram above: + uint debug_ptr = call->req(); + + // Loop over the map input edges associated with jvms, add them + // to the call node, & reset all offsets to match call node array. + for (JVMState* in_jvms = youngest_jvms; in_jvms != NULL; ) { + uint debug_end = debug_ptr; + uint debug_start = debug_ptr - in_jvms->debug_size(); + debug_ptr = debug_start; // back up the ptr + + uint p = debug_start; // walks forward in [debug_start, debug_end) + uint j, k, l; + SafePointNode* in_map = in_jvms->map(); + out_jvms->set_map(call); + + if (can_prune_locals) { + assert(in_jvms->method() == out_jvms->method(), "sanity"); + // If the current throw can reach an exception handler in this JVMS, + // then we must keep everything live that can reach that handler. + // As a quick and dirty approximation, we look for any handlers at all. + if (in_jvms->method()->has_exception_handlers()) { + can_prune_locals = false; + } + } + + // Add the Locals + k = in_jvms->locoff(); + l = in_jvms->loc_size(); + out_jvms->set_locoff(p); + if (full_info && !can_prune_locals) { + for (j = 0; j < l; j++) + call->set_req(p++, in_map->in(k+j)); + } else { + p += l; // already set to top above by add_req_batch + } + + // Add the Expression Stack + k = in_jvms->stkoff(); + l = in_jvms->sp(); + out_jvms->set_stkoff(p); + if (full_info && !can_prune_locals) { + for (j = 0; j < l; j++) + call->set_req(p++, in_map->in(k+j)); + } else if (can_prune_locals && stack_slots_not_pruned != 0) { + // Divide stack into {S0,...,S1}, where S0 is set to top. + uint s1 = stack_slots_not_pruned; + stack_slots_not_pruned = 0; // for next iteration + if (s1 > l) s1 = l; + uint s0 = l - s1; + p += s0; // skip the tops preinstalled by add_req_batch + for (j = s0; j < l; j++) + call->set_req(p++, in_map->in(k+j)); + } else { + p += l; // already set to top above by add_req_batch + } + + // Add the Monitors + k = in_jvms->monoff(); + l = in_jvms->mon_size(); + out_jvms->set_monoff(p); + for (j = 0; j < l; j++) + call->set_req(p++, in_map->in(k+j)); + + // Finish the new jvms. + out_jvms->set_endoff(p); + + assert(out_jvms->endoff() == debug_end, "fill ptr must match"); + assert(out_jvms->depth() == in_jvms->depth(), "depth must match"); + assert(out_jvms->loc_size() == in_jvms->loc_size(), "size must match"); + assert(out_jvms->mon_size() == in_jvms->mon_size(), "size must match"); + assert(out_jvms->debug_size() == in_jvms->debug_size(), "size must match"); + + // Update the two tail pointers in parallel. + out_jvms = out_jvms->caller(); + in_jvms = in_jvms->caller(); + } + + assert(debug_ptr == non_debug_edges, "debug info must fit exactly"); + + // Test the correctness of JVMState::debug_xxx accessors: + assert(call->jvms()->debug_start() == non_debug_edges, ""); + assert(call->jvms()->debug_end() == call->req(), ""); + assert(call->jvms()->debug_depth() == call->req() - non_debug_edges, ""); +} + +bool GraphKit::compute_stack_effects(int& inputs, int& depth) { + Bytecodes::Code code = java_bc(); + if (code == Bytecodes::_wide) { + code = method()->java_code_at_bci(bci() + 1); + } + + BasicType rtype = T_ILLEGAL; + int rsize = 0; + + if (code != Bytecodes::_illegal) { + depth = Bytecodes::depth(code); // checkcast=0, athrow=-1 + rtype = Bytecodes::result_type(code); // checkcast=P, athrow=V + if (rtype < T_CONFLICT) + rsize = type2size[rtype]; + } + + switch (code) { + case Bytecodes::_illegal: + return false; + + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + inputs = 0; + break; + + case Bytecodes::_dup: inputs = 1; break; + case Bytecodes::_dup_x1: inputs = 2; break; + case Bytecodes::_dup_x2: inputs = 3; break; + case Bytecodes::_dup2: inputs = 2; break; + case Bytecodes::_dup2_x1: inputs = 3; break; + case Bytecodes::_dup2_x2: inputs = 4; break; + case Bytecodes::_swap: inputs = 2; break; + case Bytecodes::_arraylength: inputs = 1; break; + + case Bytecodes::_getstatic: + case Bytecodes::_putstatic: + case Bytecodes::_getfield: + case Bytecodes::_putfield: + { + bool is_get = (depth >= 0), is_static = (depth & 1); + bool ignore; + ciBytecodeStream iter(method()); + iter.reset_to_bci(bci()); + iter.next(); + ciField* field = iter.get_field(ignore); + int size = field->type()->size(); + inputs = (is_static ? 0 : 1); + if (is_get) { + depth = size - inputs; + } else { + inputs += size; // putxxx pops the value from the stack + depth = - inputs; + } + } + break; + + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokeinterface: + { + bool is_static = (depth == 0); + bool ignore; + ciBytecodeStream iter(method()); + iter.reset_to_bci(bci()); + iter.next(); + ciMethod* method = iter.get_method(ignore); + inputs = method->arg_size_no_receiver(); + if (!is_static) inputs += 1; + int size = method->return_type()->size(); + depth = size - inputs; + } + break; + + case Bytecodes::_multianewarray: + { + ciBytecodeStream iter(method()); + iter.reset_to_bci(bci()); + iter.next(); + inputs = iter.get_dimensions(); + assert(rsize == 1, ""); + depth = rsize - inputs; + } + break; + + case Bytecodes::_ireturn: + case Bytecodes::_lreturn: + case Bytecodes::_freturn: + case Bytecodes::_dreturn: + case Bytecodes::_areturn: + assert(rsize = -depth, ""); + inputs = rsize; + break; + + case Bytecodes::_jsr: + case Bytecodes::_jsr_w: + inputs = 0; + depth = 1; // S.B. depth=1, not zero + break; + + default: + // bytecode produces a typed result + inputs = rsize - depth; + assert(inputs >= 0, ""); + break; + } + +#ifdef ASSERT + // spot check + int outputs = depth + inputs; + assert(outputs >= 0, "sanity"); + switch (code) { + case Bytecodes::_checkcast: assert(inputs == 1 && outputs == 1, ""); break; + case Bytecodes::_athrow: assert(inputs == 1 && outputs == 0, ""); break; + case Bytecodes::_aload_0: assert(inputs == 0 && outputs == 1, ""); break; + case Bytecodes::_return: assert(inputs == 0 && outputs == 0, ""); break; + case Bytecodes::_drem: assert(inputs == 4 && outputs == 2, ""); break; + } +#endif //ASSERT + + return true; +} + + + +//------------------------------basic_plus_adr--------------------------------- +Node* GraphKit::basic_plus_adr(Node* base, Node* ptr, Node* offset) { + // short-circuit a common case + if (offset == intcon(0)) return ptr; + return _gvn.transform( new (C, 4) AddPNode(base, ptr, offset) ); +} + +Node* GraphKit::ConvI2L(Node* offset) { + // short-circuit a common case + jint offset_con = find_int_con(offset, Type::OffsetBot); + if (offset_con != Type::OffsetBot) { + return longcon((long) offset_con); + } + return _gvn.transform( new (C, 2) ConvI2LNode(offset)); +} +Node* GraphKit::ConvL2I(Node* offset) { + // short-circuit a common case + jlong offset_con = find_long_con(offset, (jlong)Type::OffsetBot); + if (offset_con != (jlong)Type::OffsetBot) { + return intcon((int) offset_con); + } + return _gvn.transform( new (C, 2) ConvL2INode(offset)); +} + +//-------------------------load_object_klass----------------------------------- +Node* GraphKit::load_object_klass(Node* obj) { + // Special-case a fresh allocation to avoid building nodes: + Node* akls = AllocateNode::Ideal_klass(obj, &_gvn); + if (akls != NULL) return akls; + Node* k_adr = basic_plus_adr(obj, oopDesc::klass_offset_in_bytes()); + return _gvn.transform( new (C, 3) LoadKlassNode(0, immutable_memory(), k_adr, TypeInstPtr::KLASS) ); +} + +//-------------------------load_array_length----------------------------------- +Node* GraphKit::load_array_length(Node* array) { + // Special-case a fresh allocation to avoid building nodes: + Node* alen = AllocateArrayNode::Ideal_length(array, &_gvn); + if (alen != NULL) return alen; + Node *r_adr = basic_plus_adr(array, arrayOopDesc::length_offset_in_bytes()); + return _gvn.transform( new (C, 3) LoadRangeNode(0, immutable_memory(), r_adr, TypeInt::POS)); +} + +//------------------------------do_null_check---------------------------------- +// Helper function to do a NULL pointer check. Returned value is +// the incoming address with NULL casted away. You are allowed to use the +// not-null value only if you are control dependent on the test. +extern int explicit_null_checks_inserted, + explicit_null_checks_elided; +Node* GraphKit::null_check_common(Node* value, BasicType type, + // optional arguments for variations: + bool assert_null, + Node* *null_control) { + assert(!assert_null || null_control == NULL, "not both at once"); + if (stopped()) return top(); + if (!GenerateCompilerNullChecks && !assert_null && null_control == NULL) { + // For some performance testing, we may wish to suppress null checking. + value = cast_not_null(value); // Make it appear to be non-null (4962416). + return value; + } + explicit_null_checks_inserted++; + + // Construct NULL check + Node *chk = NULL; + switch(type) { + case T_LONG : chk = new (C, 3) CmpLNode(value, _gvn.zerocon(T_LONG)); break; + case T_INT : chk = new (C, 3) CmpINode( value, _gvn.intcon(0)); break; + case T_ARRAY : // fall through + type = T_OBJECT; // simplify further tests + case T_OBJECT : { + const Type *t = _gvn.type( value ); + + const TypeInstPtr* tp = t->isa_instptr(); + if (tp != NULL && !tp->klass()->is_loaded() + // Only for do_null_check, not any of its siblings: + && !assert_null && null_control == NULL) { + // Usually, any field access or invocation on an unloaded oop type + // will simply fail to link, since the statically linked class is + // likely also to be unloaded. However, in -Xcomp mode, sometimes + // the static class is loaded but the sharper oop type is not. + // Rather than checking for this obscure case in lots of places, + // we simply observe that a null check on an unloaded class + // will always be followed by a nonsense operation, so we + // can just issue the uncommon trap here. + // Our access to the unloaded class will only be correct + // after it has been loaded and initialized, which requires + // a trip through the interpreter. +#ifndef PRODUCT + if (WizardMode) { tty->print("Null check of unloaded "); tp->klass()->print(); tty->cr(); } +#endif + uncommon_trap(Deoptimization::Reason_unloaded, + Deoptimization::Action_reinterpret, + tp->klass(), "!loaded"); + return top(); + } + + if (assert_null) { + // See if the type is contained in NULL_PTR. + // If so, then the value is already null. + if (t->higher_equal(TypePtr::NULL_PTR)) { + explicit_null_checks_elided++; + return value; // Elided null assert quickly! + } + } else { + // See if mixing in the NULL pointer changes type. + // If so, then the NULL pointer was not allowed in the original + // type. In other words, "value" was not-null. + if (t->meet(TypePtr::NULL_PTR) != t) { + // same as: if (!TypePtr::NULL_PTR->higher_equal(t)) ... + explicit_null_checks_elided++; + return value; // Elided null check quickly! + } + } + chk = new (C, 3) CmpPNode( value, null() ); + break; + } + + default : ShouldNotReachHere(); + } + assert(chk != NULL, "sanity check"); + chk = _gvn.transform(chk); + + BoolTest::mask btest = assert_null ? BoolTest::eq : BoolTest::ne; + BoolNode *btst = new (C, 2) BoolNode( chk, btest); + Node *tst = _gvn.transform( btst ); + + //----------- + // if peephole optimizations occured, a prior test existed. + // If a prior test existed, maybe it dominates as we can avoid this test. + if (tst != btst && type == T_OBJECT) { + // At this point we want to scan up the CFG to see if we can + // find an identical test (and so avoid this test altogether). + Node *cfg = control(); + int depth = 0; + while( depth < 16 ) { // Limit search depth for speed + if( cfg->Opcode() == Op_IfTrue && + cfg->in(0)->in(1) == tst ) { + // Found prior test. Use "cast_not_null" to construct an identical + // CastPP (and hence hash to) as already exists for the prior test. + // Return that casted value. + if (assert_null) { + replace_in_map(value, null()); + return null(); // do not issue the redundant test + } + Node *oldcontrol = control(); + set_control(cfg); + Node *res = cast_not_null(value); + set_control(oldcontrol); + explicit_null_checks_elided++; + return res; + } + cfg = IfNode::up_one_dom(cfg, /*linear_only=*/ true); + if (cfg == NULL) break; // Quit at region nodes + depth++; + } + } + + //----------- + // Branch to failure if null + float ok_prob = PROB_MAX; // a priori estimate: nulls never happen + Deoptimization::DeoptReason reason; + if (assert_null) + reason = Deoptimization::Reason_null_assert; + else if (type == T_OBJECT) + reason = Deoptimization::Reason_null_check; + else + reason = Deoptimization::Reason_div0_check; + + // To cause an implicit null check, we set the not-null probability + // to the maximum (PROB_MAX). For an explicit check the probablity + // is set to a smaller value. + if (null_control != NULL || too_many_traps(reason)) { + // probability is less likely + ok_prob = PROB_LIKELY_MAG(3); + } else if (!assert_null && + (ImplicitNullCheckThreshold > 0) && + method() != NULL && + (method()->method_data()->trap_count(reason) + >= (uint)ImplicitNullCheckThreshold)) { + ok_prob = PROB_LIKELY_MAG(3); + } + + if (null_control != NULL) { + IfNode* iff = create_and_map_if(control(), tst, ok_prob, COUNT_UNKNOWN); + Node* null_true = _gvn.transform( new (C, 1) IfFalseNode(iff)); + set_control( _gvn.transform( new (C, 1) IfTrueNode(iff))); + if (null_true == top()) + explicit_null_checks_elided++; + (*null_control) = null_true; + } else { + BuildCutout unless(this, tst, ok_prob); + // Check for optimizer eliding test at parse time + if (stopped()) { + // Failure not possible; do not bother making uncommon trap. + explicit_null_checks_elided++; + } else if (assert_null) { + uncommon_trap(reason, + Deoptimization::Action_make_not_entrant, + NULL, "assert_null"); + } else { + builtin_throw(reason); + } + } + + // Must throw exception, fall-thru not possible? + if (stopped()) { + return top(); // No result + } + + if (assert_null) { + // Cast obj to null on this path. + replace_in_map(value, zerocon(type)); + return zerocon(type); + } + + // Cast obj to not-null on this path, if there is no null_control. + // (If there is a null_control, a non-null value may come back to haunt us.) + if (type == T_OBJECT) { + Node* cast = cast_not_null(value, false); + if (null_control == NULL || (*null_control) == top()) + replace_in_map(value, cast); + value = cast; + } + + return value; +} + + +//------------------------------cast_not_null---------------------------------- +// Cast obj to not-null on this path +Node* GraphKit::cast_not_null(Node* obj, bool do_replace_in_map) { + const Type *t = _gvn.type(obj); + const Type *t_not_null = t->join(TypePtr::NOTNULL); + // Object is already not-null? + if( t == t_not_null ) return obj; + + Node *cast = new (C, 2) CastPPNode(obj,t_not_null); + cast->init_req(0, control()); + cast = _gvn.transform( cast ); + + // Scan for instances of 'obj' in the current JVM mapping. + // These instances are known to be not-null after the test. + if (do_replace_in_map) + replace_in_map(obj, cast); + + return cast; // Return casted value +} + + +//--------------------------replace_in_map------------------------------------- +void GraphKit::replace_in_map(Node* old, Node* neww) { + this->map()->replace_edge(old, neww); + + // Note: This operation potentially replaces any edge + // on the map. This includes locals, stack, and monitors + // of the current (innermost) JVM state. + + // We can consider replacing in caller maps. + // The idea would be that an inlined function's null checks + // can be shared with the entire inlining tree. + // The expense of doing this is that the PreserveJVMState class + // would have to preserve caller states too, with a deep copy. +} + + + +//============================================================================= +//--------------------------------memory--------------------------------------- +Node* GraphKit::memory(uint alias_idx) { + MergeMemNode* mem = merged_memory(); + Node* p = mem->memory_at(alias_idx); + _gvn.set_type(p, Type::MEMORY); // must be mapped + return p; +} + +//-----------------------------reset_memory------------------------------------ +Node* GraphKit::reset_memory() { + Node* mem = map()->memory(); + // do not use this node for any more parsing! + debug_only( map()->set_memory((Node*)NULL) ); + return _gvn.transform( mem ); +} + +//------------------------------set_all_memory--------------------------------- +void GraphKit::set_all_memory(Node* newmem) { + Node* mergemem = MergeMemNode::make(C, newmem); + gvn().set_type_bottom(mergemem); + map()->set_memory(mergemem); +} + +//------------------------------set_all_memory_call---------------------------- +void GraphKit::set_all_memory_call(Node* call) { + Node* newmem = _gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory) ); + set_all_memory(newmem); +} + +//============================================================================= +// +// parser factory methods for MemNodes +// +// These are layered on top of the factory methods in LoadNode and StoreNode, +// and integrate with the parser's memory state and _gvn engine. +// + +// factory methods in "int adr_idx" +Node* GraphKit::make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, + int adr_idx, + bool require_atomic_access) { + assert(adr_idx != Compile::AliasIdxTop, "use other make_load factory" ); + const TypePtr* adr_type = NULL; // debug-mode-only argument + debug_only(adr_type = C->get_adr_type(adr_idx)); + Node* mem = memory(adr_idx); + Node* ld; + if (require_atomic_access && bt == T_LONG) { + ld = LoadLNode::make_atomic(C, ctl, mem, adr, adr_type, t); + } else { + ld = LoadNode::make(C, ctl, mem, adr, adr_type, t, bt); + } + return _gvn.transform(ld); +} + +Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt, + int adr_idx, + bool require_atomic_access) { + assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); + const TypePtr* adr_type = NULL; + debug_only(adr_type = C->get_adr_type(adr_idx)); + Node *mem = memory(adr_idx); + Node* st; + if (require_atomic_access && bt == T_LONG) { + st = StoreLNode::make_atomic(C, ctl, mem, adr, adr_type, val); + } else { + st = StoreNode::make(C, ctl, mem, adr, adr_type, val, bt); + } + st = _gvn.transform(st); + set_memory(st, adr_idx); + // Back-to-back stores can only remove intermediate store with DU info + // so push on worklist for optimizer. + if (mem->req() > MemNode::Address && adr == mem->in(MemNode::Address)) + record_for_igvn(st); + + return st; +} + +void GraphKit::pre_barrier(Node* ctl, + Node* obj, + Node* adr, + uint adr_idx, + Node *val, + const Type* val_type, + BasicType bt) { + BarrierSet* bs = Universe::heap()->barrier_set(); + set_control(ctl); + switch (bs->kind()) { + + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + case BarrierSet::ModRef: + break; + + case BarrierSet::Other: + default : + ShouldNotReachHere(); + + } +} + +void GraphKit::post_barrier(Node* ctl, + Node* store, + Node* obj, + Node* adr, + uint adr_idx, + Node *val, + BasicType bt, + bool use_precise) { + BarrierSet* bs = Universe::heap()->barrier_set(); + set_control(ctl); + switch (bs->kind()) { + + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + write_barrier_post(store, obj, adr, val, use_precise); + break; + + case BarrierSet::ModRef: + break; + + case BarrierSet::Other: + default : + ShouldNotReachHere(); + + } +} + +Node* GraphKit::store_oop_to_object(Node* ctl, + Node* obj, + Node* adr, + const TypePtr* adr_type, + Node *val, + const Type* val_type, + BasicType bt) { + uint adr_idx = C->get_alias_index(adr_type); + Node* store; + pre_barrier(ctl, obj, adr, adr_idx, val, val_type, bt); + store = store_to_memory(control(), adr, val, bt, adr_idx); + post_barrier(control(), store, obj, adr, adr_idx, val, bt, false); + return store; +} + +Node* GraphKit::store_oop_to_array(Node* ctl, + Node* obj, + Node* adr, + const TypePtr* adr_type, + Node *val, + const Type* val_type, + BasicType bt) { + uint adr_idx = C->get_alias_index(adr_type); + Node* store; + pre_barrier(ctl, obj, adr, adr_idx, val, val_type, bt); + store = store_to_memory(control(), adr, val, bt, adr_idx); + post_barrier(control(), store, obj, adr, adr_idx, val, bt, true); + return store; +} + +Node* GraphKit::store_oop_to_unknown(Node* ctl, + Node* obj, + Node* adr, + const TypePtr* adr_type, + Node *val, + const Type* val_type, + BasicType bt) { + uint adr_idx = C->get_alias_index(adr_type); + Node* store; + pre_barrier(ctl, obj, adr, adr_idx, val, val_type, bt); + store = store_to_memory(control(), adr, val, bt, adr_idx); + post_barrier(control(), store, obj, adr, adr_idx, val, bt, true); + return store; +} + + +//-------------------------array_element_address------------------------- +Node* GraphKit::array_element_address(Node* ary, Node* idx, BasicType elembt, + const TypeInt* sizetype) { + uint shift = exact_log2(type2aelembytes[elembt]); + uint header = arrayOopDesc::base_offset_in_bytes(elembt); + + // short-circuit a common case (saves lots of confusing waste motion) + jint idx_con = find_int_con(idx, -1); + if (idx_con >= 0) { + intptr_t offset = header + ((intptr_t)idx_con << shift); + return basic_plus_adr(ary, offset); + } + + // must be correct type for alignment purposes + Node* base = basic_plus_adr(ary, header); +#ifdef _LP64 + // The scaled index operand to AddP must be a clean 64-bit value. + // Java allows a 32-bit int to be incremented to a negative + // value, which appears in a 64-bit register as a large + // positive number. Using that large positive number as an + // operand in pointer arithmetic has bad consequences. + // On the other hand, 32-bit overflow is rare, and the possibility + // can often be excluded, if we annotate the ConvI2L node with + // a type assertion that its value is known to be a small positive + // number. (The prior range check has ensured this.) + // This assertion is used by ConvI2LNode::Ideal. + int index_max = max_jint - 1; // array size is max_jint, index is one less + if (sizetype != NULL) index_max = sizetype->_hi - 1; + const TypeLong* lidxtype = TypeLong::make(CONST64(0), index_max, Type::WidenMax); + idx = _gvn.transform( new (C, 2) ConvI2LNode(idx, lidxtype) ); +#endif + Node* scale = _gvn.transform( new (C, 3) LShiftXNode(idx, intcon(shift)) ); + return basic_plus_adr(ary, base, scale); +} + +//-------------------------load_array_element------------------------- +Node* GraphKit::load_array_element(Node* ctl, Node* ary, Node* idx, const TypeAryPtr* arytype) { + const Type* elemtype = arytype->elem(); + BasicType elembt = elemtype->array_element_basic_type(); + Node* adr = array_element_address(ary, idx, elembt, arytype->size()); + Node* ld = make_load(ctl, adr, elemtype, elembt, arytype); + return ld; +} + +//-------------------------set_arguments_for_java_call------------------------- +// Arguments (pre-popped from the stack) are taken from the JVMS. +void GraphKit::set_arguments_for_java_call(CallJavaNode* call) { + // Add the call arguments: + uint nargs = call->method()->arg_size(); + for (uint i = 0; i < nargs; i++) { + Node* arg = argument(i); + call->init_req(i + TypeFunc::Parms, arg); + } +} + +//---------------------------set_edges_for_java_call--------------------------- +// Connect a newly created call into the current JVMS. +// A return value node (if any) is returned from set_edges_for_java_call. +void GraphKit::set_edges_for_java_call(CallJavaNode* call, bool must_throw) { + + // Add the predefined inputs: + call->init_req( TypeFunc::Control, control() ); + call->init_req( TypeFunc::I_O , i_o() ); + call->init_req( TypeFunc::Memory , reset_memory() ); + call->init_req( TypeFunc::FramePtr, frameptr() ); + call->init_req( TypeFunc::ReturnAdr, top() ); + + add_safepoint_edges(call, must_throw); + + Node* xcall = _gvn.transform(call); + + if (xcall == top()) { + set_control(top()); + return; + } + assert(xcall == call, "call identity is stable"); + + // Re-use the current map to produce the result. + + set_control(_gvn.transform(new (C, 1) ProjNode(call, TypeFunc::Control))); + set_i_o( _gvn.transform(new (C, 1) ProjNode(call, TypeFunc::I_O ))); + set_all_memory_call(xcall); + + //return xcall; // no need, caller already has it +} + +Node* GraphKit::set_results_for_java_call(CallJavaNode* call) { + if (stopped()) return top(); // maybe the call folded up? + + // Capture the return value, if any. + Node* ret; + if (call->method() == NULL || + call->method()->return_type()->basic_type() == T_VOID) + ret = top(); + else ret = _gvn.transform(new (C, 1) ProjNode(call, TypeFunc::Parms)); + + // Note: Since any out-of-line call can produce an exception, + // we always insert an I_O projection from the call into the result. + + make_slow_call_ex(call, env()->Throwable_klass(), false); + + return ret; +} + +//--------------------set_predefined_input_for_runtime_call-------------------- +// Reading and setting the memory state is way conservative here. +// The real problem is that I am not doing real Type analysis on memory, +// so I cannot distinguish card mark stores from other stores. Across a GC +// point the Store Barrier and the card mark memory has to agree. I cannot +// have a card mark store and its barrier split across the GC point from +// either above or below. Here I get that to happen by reading ALL of memory. +// A better answer would be to separate out card marks from other memory. +// For now, return the input memory state, so that it can be reused +// after the call, if this call has restricted memory effects. +Node* GraphKit::set_predefined_input_for_runtime_call(SafePointNode* call) { + // Set fixed predefined input arguments + Node* memory = reset_memory(); + call->init_req( TypeFunc::Control, control() ); + call->init_req( TypeFunc::I_O, top() ); // does no i/o + call->init_req( TypeFunc::Memory, memory ); // may gc ptrs + call->init_req( TypeFunc::FramePtr, frameptr() ); + call->init_req( TypeFunc::ReturnAdr, top() ); + return memory; +} + +//-------------------set_predefined_output_for_runtime_call-------------------- +// Set control and memory (not i_o) from the call. +// If keep_mem is not NULL, use it for the output state, +// except for the RawPtr output of the call, if hook_mem is TypeRawPtr::BOTTOM. +// If hook_mem is NULL, this call produces no memory effects at all. +// If hook_mem is a Java-visible memory slice (such as arraycopy operands), +// then only that memory slice is taken from the call. +// In the last case, we must put an appropriate memory barrier before +// the call, so as to create the correct anti-dependencies on loads +// preceding the call. +void GraphKit::set_predefined_output_for_runtime_call(Node* call, + Node* keep_mem, + const TypePtr* hook_mem) { + // no i/o + set_control(_gvn.transform( new (C, 1) ProjNode(call,TypeFunc::Control) )); + if (keep_mem) { + // First clone the existing memory state + set_all_memory(keep_mem); + if (hook_mem != NULL) { + // Make memory for the call + Node* mem = _gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory) ); + // Set the RawPtr memory state only. This covers all the heap top/GC stuff + // We also use hook_mem to extract specific effects from arraycopy stubs. + set_memory(mem, hook_mem); + } + // ...else the call has NO memory effects. + + // Make sure the call advertises its memory effects precisely. + // This lets us build accurate anti-dependences in gcm.cpp. + assert(C->alias_type(call->adr_type()) == C->alias_type(hook_mem), + "call node must be constructed correctly"); + } else { + assert(hook_mem == NULL, ""); + // This is not a "slow path" call; all memory comes from the call. + set_all_memory_call(call); + } +} + +//------------------------------increment_counter------------------------------ +// for statistics: increment a VM counter by 1 + +void GraphKit::increment_counter(address counter_addr) { + Node* adr1 = makecon(TypeRawPtr::make(counter_addr)); + increment_counter(adr1); +} + +void GraphKit::increment_counter(Node* counter_addr) { + int adr_type = Compile::AliasIdxRaw; + Node* cnt = make_load(NULL, counter_addr, TypeInt::INT, T_INT, adr_type); + Node* incr = _gvn.transform(new (C, 3) AddINode(cnt, _gvn.intcon(1))); + store_to_memory( NULL, counter_addr, incr, T_INT, adr_type ); +} + + +//------------------------------uncommon_trap---------------------------------- +// Bail out to the interpreter in mid-method. Implemented by calling the +// uncommon_trap blob. This helper function inserts a runtime call with the +// right debug info. +void GraphKit::uncommon_trap(int trap_request, + ciKlass* klass, const char* comment, + bool must_throw, + bool keep_exact_action) { + if (failing()) stop(); + if (stopped()) return; // trap reachable? + + // Note: If ProfileTraps is true, and if a deopt. actually + // occurs here, the runtime will make sure an MDO exists. There is + // no need to call method()->build_method_data() at this point. + +#ifdef ASSERT + if (!must_throw) { + // Make sure the stack has at least enough depth to execute + // the current bytecode. + int inputs, ignore; + if (compute_stack_effects(inputs, ignore)) { + assert(sp() >= inputs, "must have enough JVMS stack to execute"); + // It is a frequent error in library_call.cpp to issue an + // uncommon trap with the _sp value already popped. + } + } +#endif + + Deoptimization::DeoptReason reason = Deoptimization::trap_request_reason(trap_request); + Deoptimization::DeoptAction action = Deoptimization::trap_request_action(trap_request); + + switch (action) { + case Deoptimization::Action_maybe_recompile: + case Deoptimization::Action_reinterpret: + // Temporary fix for 6529811 to allow virtual calls to be sure they + // get the chance to go from mono->bi->mega + if (!keep_exact_action && + Deoptimization::trap_request_index(trap_request) < 0 && + too_many_recompiles(reason)) { + // This BCI is causing too many recompilations. + action = Deoptimization::Action_none; + trap_request = Deoptimization::make_trap_request(reason, action); + } else { + C->set_trap_can_recompile(true); + } + break; + case Deoptimization::Action_make_not_entrant: + C->set_trap_can_recompile(true); + break; +#ifdef ASSERT + case Deoptimization::Action_none: + case Deoptimization::Action_make_not_compilable: + break; + default: + assert(false, "bad action"); +#endif + } + + if (TraceOptoParse) { + char buf[100]; + tty->print_cr("Uncommon trap %s at bci:%d", + Deoptimization::format_trap_request(buf, sizeof(buf), + trap_request), bci()); + } + + CompileLog* log = C->log(); + if (log != NULL) { + int kid = (klass == NULL)? -1: log->identify(klass); + log->begin_elem("uncommon_trap bci='%d'", bci()); + char buf[100]; + log->print(" %s", Deoptimization::format_trap_request(buf, sizeof(buf), + trap_request)); + if (kid >= 0) log->print(" klass='%d'", kid); + if (comment != NULL) log->print(" comment='%s'", comment); + log->end_elem(); + } + + // Make sure any guarding test views this path as very unlikely + Node *i0 = control()->in(0); + if (i0 != NULL && i0->is_If()) { // Found a guarding if test? + IfNode *iff = i0->as_If(); + float f = iff->_prob; // Get prob + if (control()->Opcode() == Op_IfTrue) { + if (f > PROB_UNLIKELY_MAG(4)) + iff->_prob = PROB_MIN; + } else { + if (f < PROB_LIKELY_MAG(4)) + iff->_prob = PROB_MAX; + } + } + + // Clear out dead values from the debug info. + kill_dead_locals(); + + // Now insert the uncommon trap subroutine call + address call_addr = SharedRuntime::uncommon_trap_blob()->instructions_begin(); + const TypePtr* no_memory_effects = NULL; + // Pass the index of the class to be loaded + Node* call = make_runtime_call(RC_NO_LEAF | RC_UNCOMMON | + (must_throw ? RC_MUST_THROW : 0), + OptoRuntime::uncommon_trap_Type(), + call_addr, "uncommon_trap", no_memory_effects, + intcon(trap_request)); + assert(call->as_CallStaticJava()->uncommon_trap_request() == trap_request, + "must extract request correctly from the graph"); + assert(trap_request != 0, "zero value reserved by uncommon_trap_request"); + + call->set_req(TypeFunc::ReturnAdr, returnadr()); + // The debug info is the only real input to this call. + + // Halt-and-catch fire here. The above call should never return! + HaltNode* halt = new(C, TypeFunc::Parms) HaltNode(control(), frameptr()); + _gvn.set_type_bottom(halt); + root()->add_req(halt); + + stop_and_kill_map(); +} + + +//--------------------------just_allocated_object------------------------------ +// Report the object that was just allocated. +// It must be the case that there are no intervening safepoints. +// We use this to determine if an object is so "fresh" that +// it does not require card marks. +Node* GraphKit::just_allocated_object(Node* current_control) { + if (C->recent_alloc_ctl() == current_control) + return C->recent_alloc_obj(); + return NULL; +} + + +//------------------------------store_barrier---------------------------------- +// Insert a write-barrier store. This is to let generational GC work; we have +// to flag all oop-stores before the next GC point. +void GraphKit::write_barrier_post(Node* oop_store, Node* obj, Node* adr, + Node* val, bool use_precise) { + // No store check needed if we're storing a NULL or an old object + // (latter case is probably a string constant). The concurrent + // mark sweep garbage collector, however, needs to have all nonNull + // oop updates flagged via card-marks. + if (val != NULL && val->is_Con()) { + // must be either an oop or NULL + const Type* t = val->bottom_type(); + if (t == TypePtr::NULL_PTR || t == Type::TOP) + // stores of null never (?) need barriers + return; + ciObject* con = t->is_oopptr()->const_oop(); + if (con != NULL + && con->is_perm() + && Universe::heap()->can_elide_permanent_oop_store_barriers()) + // no store barrier needed, because no old-to-new ref created + return; + } + + if (use_ReduceInitialCardMarks() + && obj == just_allocated_object(control())) { + // We can skip marks on a freshly-allocated object. + // Keep this code in sync with do_eager_card_mark in runtime.cpp. + // That routine eagerly marks the occasional object which is produced + // by the slow path, so that we don't have to do it here. + return; + } + + if (!use_precise) { + // All card marks for a (non-array) instance are in one place: + adr = obj; + } + // (Else it's an array (or unknown), and we want more precise card marks.) + assert(adr != NULL, ""); + + // Get the alias_index for raw card-mark memory + int adr_type = Compile::AliasIdxRaw; + // Convert the pointer to an int prior to doing math on it + Node* cast = _gvn.transform(new (C, 2) CastP2XNode(control(), adr)); + // Divide by card size + assert(Universe::heap()->barrier_set()->kind() == BarrierSet::CardTableModRef, + "Only one we handle so far."); + CardTableModRefBS* ct = + (CardTableModRefBS*)(Universe::heap()->barrier_set()); + Node *b = _gvn.transform(new (C, 3) URShiftXNode( cast, _gvn.intcon(CardTableModRefBS::card_shift) )); + // We store into a byte array, so do not bother to left-shift by zero + // Get base of card map + assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), + "adjust this code"); + Node *c = makecon(TypeRawPtr::make((address)ct->byte_map_base)); + // Combine + Node *sb_ctl = control(); + Node *sb_adr = _gvn.transform(new (C, 4) AddPNode( top()/*no base ptr*/, c, b )); + Node *sb_val = _gvn.intcon(0); + // Smash zero into card + if( !UseConcMarkSweepGC ) { + BasicType bt = T_BYTE; + store_to_memory(sb_ctl, sb_adr, sb_val, bt, adr_type); + } else { + // Specialized path for CM store barrier + cms_card_mark( sb_ctl, sb_adr, sb_val, oop_store); + } +} + +// Specialized path for CMS store barrier +void GraphKit::cms_card_mark(Node* ctl, Node* adr, Node* val, Node *oop_store) { + BasicType bt = T_BYTE; + int adr_idx = Compile::AliasIdxRaw; + Node* mem = memory(adr_idx); + + // The type input is NULL in PRODUCT builds + const TypePtr* type = NULL; + debug_only(type = C->get_adr_type(adr_idx)); + + // Add required edge to oop_store, optimizer does not support precedence edges. + // Convert required edge to precedence edge before allocation. + Node *store = _gvn.transform( new (C, 5) StoreCMNode(ctl, mem, adr, type, val, oop_store) ); + set_memory(store, adr_idx); + + // For CMS, back-to-back card-marks can only remove the first one + // and this requires DU info. Push on worklist for optimizer. + if (mem->req() > MemNode::Address && adr == mem->in(MemNode::Address)) + record_for_igvn(store); +} + + +void GraphKit::round_double_arguments(ciMethod* dest_method) { + // (Note: TypeFunc::make has a cache that makes this fast.) + const TypeFunc* tf = TypeFunc::make(dest_method); + int nargs = tf->_domain->_cnt - TypeFunc::Parms; + for (int j = 0; j < nargs; j++) { + const Type *targ = tf->_domain->field_at(j + TypeFunc::Parms); + if( targ->basic_type() == T_DOUBLE ) { + // If any parameters are doubles, they must be rounded before + // the call, dstore_rounding does gvn.transform + Node *arg = argument(j); + arg = dstore_rounding(arg); + set_argument(j, arg); + } + } +} + +void GraphKit::round_double_result(ciMethod* dest_method) { + // A non-strict method may return a double value which has an extended + // exponent, but this must not be visible in a caller which is 'strict' + // If a strict caller invokes a non-strict callee, round a double result + + BasicType result_type = dest_method->return_type()->basic_type(); + assert( method() != NULL, "must have caller context"); + if( result_type == T_DOUBLE && method()->is_strict() && !dest_method->is_strict() ) { + // Destination method's return value is on top of stack + // dstore_rounding() does gvn.transform + Node *result = pop_pair(); + result = dstore_rounding(result); + push_pair(result); + } +} + +// rounding for strict float precision conformance +Node* GraphKit::precision_rounding(Node* n) { + return UseStrictFP && _method->flags().is_strict() + && UseSSE == 0 && Matcher::strict_fp_requires_explicit_rounding + ? _gvn.transform( new (C, 2) RoundFloatNode(0, n) ) + : n; +} + +// rounding for strict double precision conformance +Node* GraphKit::dprecision_rounding(Node *n) { + return UseStrictFP && _method->flags().is_strict() + && UseSSE <= 1 && Matcher::strict_fp_requires_explicit_rounding + ? _gvn.transform( new (C, 2) RoundDoubleNode(0, n) ) + : n; +} + +// rounding for non-strict double stores +Node* GraphKit::dstore_rounding(Node* n) { + return Matcher::strict_fp_requires_explicit_rounding + && UseSSE <= 1 + ? _gvn.transform( new (C, 2) RoundDoubleNode(0, n) ) + : n; +} + +//============================================================================= +// Generate a fast path/slow path idiom. Graph looks like: +// [foo] indicates that 'foo' is a parameter +// +// [in] NULL +// \ / +// CmpP +// Bool ne +// If +// / \ +// True False-<2> +// / | +// / cast_not_null +// Load | | ^ +// [fast_test] | | +// gvn to opt_test | | +// / \ | <1> +// True False | +// | \\ | +// [slow_call] \[fast_result] +// Ctl Val \ \ +// | \ \ +// Catch <1> \ \ +// / \ ^ \ \ +// Ex No_Ex | \ \ +// | \ \ | \ <2> \ +// ... \ [slow_res] | | \ [null_result] +// \ \--+--+--- | | +// \ | / \ | / +// --------Region Phi +// +//============================================================================= +// Code is structured as a series of driver functions all called 'do_XXX' that +// call a set of helper functions. Helper functions first, then drivers. + +//------------------------------null_check_oop--------------------------------- +// Null check oop. Set null-path control into Region in slot 3. +// Make a cast-not-nullness use the other not-null control. Return cast. +Node* GraphKit::null_check_oop(Node* value, Node* *null_control, + bool never_see_null) { + // Initial NULL check taken path + (*null_control) = top(); + Node* cast = null_check_common(value, T_OBJECT, false, null_control); + + // Generate uncommon_trap: + if (never_see_null && (*null_control) != top()) { + // If we see an unexpected null at a check-cast we record it and force a + // recompile; the offending check-cast will be compiled to handle NULLs. + // If we see more than one offending BCI, then all checkcasts in the + // method will be compiled to handle NULLs. + PreserveJVMState pjvms(this); + set_control(*null_control); + uncommon_trap(Deoptimization::Reason_null_check, + Deoptimization::Action_make_not_entrant); + (*null_control) = top(); // NULL path is dead + } + + // Cast away null-ness on the result + return cast; +} + +//------------------------------opt_iff---------------------------------------- +// Optimize the fast-check IfNode. Set the fast-path region slot 2. +// Return slow-path control. +Node* GraphKit::opt_iff(Node* region, Node* iff) { + IfNode *opt_iff = _gvn.transform(iff)->as_If(); + + // Fast path taken; set region slot 2 + Node *fast_taken = _gvn.transform( new (C, 1) IfFalseNode(opt_iff) ); + region->init_req(2,fast_taken); // Capture fast-control + + // Fast path not-taken, i.e. slow path + Node *slow_taken = _gvn.transform( new (C, 1) IfTrueNode(opt_iff) ); + return slow_taken; +} + +//-----------------------------make_runtime_call------------------------------- +Node* GraphKit::make_runtime_call(int flags, + const TypeFunc* call_type, address call_addr, + const char* call_name, + const TypePtr* adr_type, + // The following parms are all optional. + // The first NULL ends the list. + Node* parm0, Node* parm1, + Node* parm2, Node* parm3, + Node* parm4, Node* parm5, + Node* parm6, Node* parm7) { + // Slow-path call + int size = call_type->domain()->cnt(); + bool is_leaf = !(flags & RC_NO_LEAF); + bool has_io = (!is_leaf && !(flags & RC_NO_IO)); + if (call_name == NULL) { + assert(!is_leaf, "must supply name for leaf"); + call_name = OptoRuntime::stub_name(call_addr); + } + CallNode* call; + if (!is_leaf) { + call = new(C, size) CallStaticJavaNode(call_type, call_addr, call_name, + bci(), adr_type); + } else if (flags & RC_NO_FP) { + call = new(C, size) CallLeafNoFPNode(call_type, call_addr, call_name, adr_type); + } else { + call = new(C, size) CallLeafNode(call_type, call_addr, call_name, adr_type); + } + + // The following is similar to set_edges_for_java_call, + // except that the memory effects of the call are restricted to AliasIdxRaw. + + // Slow path call has no side-effects, uses few values + bool wide_in = !(flags & RC_NARROW_MEM); + bool wide_out = (C->get_alias_index(adr_type) == Compile::AliasIdxBot); + + Node* prev_mem = NULL; + if (wide_in) { + prev_mem = set_predefined_input_for_runtime_call(call); + } else { + assert(!wide_out, "narrow in => narrow out"); + Node* narrow_mem = memory(adr_type); + prev_mem = reset_memory(); + map()->set_memory(narrow_mem); + set_predefined_input_for_runtime_call(call); + } + + // Hook each parm in order. Stop looking at the first NULL. + if (parm0 != NULL) { call->init_req(TypeFunc::Parms+0, parm0); + if (parm1 != NULL) { call->init_req(TypeFunc::Parms+1, parm1); + if (parm2 != NULL) { call->init_req(TypeFunc::Parms+2, parm2); + if (parm3 != NULL) { call->init_req(TypeFunc::Parms+3, parm3); + if (parm4 != NULL) { call->init_req(TypeFunc::Parms+4, parm4); + if (parm5 != NULL) { call->init_req(TypeFunc::Parms+5, parm5); + if (parm6 != NULL) { call->init_req(TypeFunc::Parms+6, parm6); + if (parm7 != NULL) { call->init_req(TypeFunc::Parms+7, parm7); + /* close each nested if ===> */ } } } } } } } } + assert(call->in(call->req()-1) != NULL, "must initialize all parms"); + + if (!is_leaf) { + // Non-leaves can block and take safepoints: + add_safepoint_edges(call, ((flags & RC_MUST_THROW) != 0)); + } + // Non-leaves can throw exceptions: + if (has_io) { + call->set_req(TypeFunc::I_O, i_o()); + } + + if (flags & RC_UNCOMMON) { + // Set the count to a tiny probability. Cf. Estimate_Block_Frequency. + // (An "if" probability corresponds roughly to an unconditional count. + // Sort of.) + call->set_cnt(PROB_UNLIKELY_MAG(4)); + } + + Node* c = _gvn.transform(call); + assert(c == call, "cannot disappear"); + + if (wide_out) { + // Slow path call has full side-effects. + set_predefined_output_for_runtime_call(call); + } else { + // Slow path call has few side-effects, and/or sets few values. + set_predefined_output_for_runtime_call(call, prev_mem, adr_type); + } + + if (has_io) { + set_i_o(_gvn.transform(new (C, 1) ProjNode(call, TypeFunc::I_O))); + } + return call; + +} + +//------------------------------merge_memory----------------------------------- +// Merge memory from one path into the current memory state. +void GraphKit::merge_memory(Node* new_mem, Node* region, int new_path) { + for (MergeMemStream mms(merged_memory(), new_mem->as_MergeMem()); mms.next_non_empty2(); ) { + Node* old_slice = mms.force_memory(); + Node* new_slice = mms.memory2(); + if (old_slice != new_slice) { + PhiNode* phi; + if (new_slice->is_Phi() && new_slice->as_Phi()->region() == region) { + phi = new_slice->as_Phi(); + #ifdef ASSERT + if (old_slice->is_Phi() && old_slice->as_Phi()->region() == region) + old_slice = old_slice->in(new_path); + // Caller is responsible for ensuring that any pre-existing + // phis are already aware of old memory. + int old_path = (new_path > 1) ? 1 : 2; // choose old_path != new_path + assert(phi->in(old_path) == old_slice, "pre-existing phis OK"); + #endif + mms.set_memory(phi); + } else { + phi = PhiNode::make(region, old_slice, Type::MEMORY, mms.adr_type(C)); + _gvn.set_type(phi, Type::MEMORY); + phi->set_req(new_path, new_slice); + mms.set_memory(_gvn.transform(phi)); // assume it is complete + } + } + } +} + +//------------------------------make_slow_call_ex------------------------------ +// Make the exception handler hookups for the slow call +void GraphKit::make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool separate_io_proj) { + if (stopped()) return; + + // Make a catch node with just two handlers: fall-through and catch-all + Node* i_o = _gvn.transform( new (C, 1) ProjNode(call, TypeFunc::I_O, separate_io_proj) ); + Node* catc = _gvn.transform( new (C, 2) CatchNode(control(), i_o, 2) ); + Node* norm = _gvn.transform( new (C, 1) CatchProjNode(catc, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci) ); + Node* excp = _gvn.transform( new (C, 1) CatchProjNode(catc, CatchProjNode::catch_all_index, CatchProjNode::no_handler_bci) ); + + { PreserveJVMState pjvms(this); + set_control(excp); + set_i_o(i_o); + + if (excp != top()) { + // Create an exception state also. + // Use an exact type if the caller has specified a specific exception. + const Type* ex_type = TypeOopPtr::make_from_klass_unique(ex_klass)->cast_to_ptr_type(TypePtr::NotNull); + Node* ex_oop = new (C, 2) CreateExNode(ex_type, control(), i_o); + add_exception_state(make_exception_state(_gvn.transform(ex_oop))); + } + } + + // Get the no-exception control from the CatchNode. + set_control(norm); +} + + +//-------------------------------gen_subtype_check----------------------------- +// Generate a subtyping check. Takes as input the subtype and supertype. +// Returns 2 values: sets the default control() to the true path and returns +// the false path. Only reads invariant memory; sets no (visible) memory. +// The PartialSubtypeCheckNode sets the hidden 1-word cache in the encoding +// but that's not exposed to the optimizer. This call also doesn't take in an +// Object; if you wish to check an Object you need to load the Object's class +// prior to coming here. +Node* GraphKit::gen_subtype_check(Node* subklass, Node* superklass) { + // Fast check for identical types, perhaps identical constants. + // The types can even be identical non-constants, in cases + // involving Array.newInstance, Object.clone, etc. + if (subklass == superklass) + return top(); // false path is dead; no test needed. + + if (_gvn.type(superklass)->singleton()) { + ciKlass* superk = _gvn.type(superklass)->is_klassptr()->klass(); + ciKlass* subk = _gvn.type(subklass)->is_klassptr()->klass(); + + // In the common case of an exact superklass, try to fold up the + // test before generating code. You may ask, why not just generate + // the code and then let it fold up? The answer is that the generated + // code will necessarily include null checks, which do not always + // completely fold away. If they are also needless, then they turn + // into a performance loss. Example: + // Foo[] fa = blah(); Foo x = fa[0]; fa[1] = x; + // Here, the type of 'fa' is often exact, so the store check + // of fa[1]=x will fold up, without testing the nullness of x. + switch (static_subtype_check(superk, subk)) { + case SSC_always_false: + { + Node* always_fail = control(); + set_control(top()); + return always_fail; + } + case SSC_always_true: + return top(); + case SSC_easy_test: + { + // Just do a direct pointer compare and be done. + Node* cmp = _gvn.transform( new(C, 3) CmpPNode(subklass, superklass) ); + Node* bol = _gvn.transform( new(C, 2) BoolNode(cmp, BoolTest::eq) ); + IfNode* iff = create_and_xform_if(control(), bol, PROB_STATIC_FREQUENT, COUNT_UNKNOWN); + set_control( _gvn.transform( new(C, 1) IfTrueNode (iff) ) ); + return _gvn.transform( new(C, 1) IfFalseNode(iff) ); + } + case SSC_full_test: + break; + default: + ShouldNotReachHere(); + } + } + + // %%% Possible further optimization: Even if the superklass is not exact, + // if the subklass is the unique subtype of the superklass, the check + // will always succeed. We could leave a dependency behind to ensure this. + + // First load the super-klass's check-offset + Node *p1 = basic_plus_adr( superklass, superklass, sizeof(oopDesc) + Klass::super_check_offset_offset_in_bytes() ); + Node *chk_off = _gvn.transform( new (C, 3) LoadINode( NULL, memory(p1), p1, _gvn.type(p1)->is_ptr() ) ); + int cacheoff_con = sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes(); + bool might_be_cache = (find_int_con(chk_off, cacheoff_con) == cacheoff_con); + + // Load from the sub-klass's super-class display list, or a 1-word cache of + // the secondary superclass list, or a failing value with a sentinel offset + // if the super-klass is an interface or exceptionally deep in the Java + // hierarchy and we have to scan the secondary superclass list the hard way. + // Worst-case type is a little odd: NULL is allowed as a result (usually + // klass loads can never produce a NULL). + Node *chk_off_X = ConvI2X(chk_off); + Node *p2 = _gvn.transform( new (C, 4) AddPNode(subklass,subklass,chk_off_X) ); + // For some types like interfaces the following loadKlass is from a 1-word + // cache which is mutable so can't use immutable memory. Other + // types load from the super-class display table which is immutable. + Node *kmem = might_be_cache ? memory(p2) : immutable_memory(); + Node *nkls = _gvn.transform( new (C, 3) LoadKlassNode( NULL, kmem, p2, _gvn.type(p2)->is_ptr(), TypeKlassPtr::OBJECT_OR_NULL ) ); + + // Compile speed common case: ARE a subtype and we canNOT fail + if( superklass == nkls ) + return top(); // false path is dead; no test needed. + + // See if we get an immediate positive hit. Happens roughly 83% of the + // time. Test to see if the value loaded just previously from the subklass + // is exactly the superklass. + Node *cmp1 = _gvn.transform( new (C, 3) CmpPNode( superklass, nkls ) ); + Node *bol1 = _gvn.transform( new (C, 2) BoolNode( cmp1, BoolTest::eq ) ); + IfNode *iff1 = create_and_xform_if( control(), bol1, PROB_LIKELY(0.83f), COUNT_UNKNOWN ); + Node *iftrue1 = _gvn.transform( new (C, 1) IfTrueNode ( iff1 ) ); + set_control( _gvn.transform( new (C, 1) IfFalseNode( iff1 ) ) ); + + // Compile speed common case: Check for being deterministic right now. If + // chk_off is a constant and not equal to cacheoff then we are NOT a + // subklass. In this case we need exactly the 1 test above and we can + // return those results immediately. + if (!might_be_cache) { + Node* not_subtype_ctrl = control(); + set_control(iftrue1); // We need exactly the 1 test above + return not_subtype_ctrl; + } + + // Gather the various success & failures here + RegionNode *r_ok_subtype = new (C, 4) RegionNode(4); + record_for_igvn(r_ok_subtype); + RegionNode *r_not_subtype = new (C, 3) RegionNode(3); + record_for_igvn(r_not_subtype); + + r_ok_subtype->init_req(1, iftrue1); + + // Check for immediate negative hit. Happens roughly 11% of the time (which + // is roughly 63% of the remaining cases). Test to see if the loaded + // check-offset points into the subklass display list or the 1-element + // cache. If it points to the display (and NOT the cache) and the display + // missed then it's not a subtype. + Node *cacheoff = _gvn.intcon(cacheoff_con); + Node *cmp2 = _gvn.transform( new (C, 3) CmpINode( chk_off, cacheoff ) ); + Node *bol2 = _gvn.transform( new (C, 2) BoolNode( cmp2, BoolTest::ne ) ); + IfNode *iff2 = create_and_xform_if( control(), bol2, PROB_LIKELY(0.63f), COUNT_UNKNOWN ); + r_not_subtype->init_req(1, _gvn.transform( new (C, 1) IfTrueNode (iff2) ) ); + set_control( _gvn.transform( new (C, 1) IfFalseNode(iff2) ) ); + + // Check for self. Very rare to get here, but its taken 1/3 the time. + // No performance impact (too rare) but allows sharing of secondary arrays + // which has some footprint reduction. + Node *cmp3 = _gvn.transform( new (C, 3) CmpPNode( subklass, superklass ) ); + Node *bol3 = _gvn.transform( new (C, 2) BoolNode( cmp3, BoolTest::eq ) ); + IfNode *iff3 = create_and_xform_if( control(), bol3, PROB_LIKELY(0.36f), COUNT_UNKNOWN ); + r_ok_subtype->init_req(2, _gvn.transform( new (C, 1) IfTrueNode ( iff3 ) ) ); + set_control( _gvn.transform( new (C, 1) IfFalseNode( iff3 ) ) ); + + // Now do a linear scan of the secondary super-klass array. Again, no real + // performance impact (too rare) but it's gotta be done. + // (The stub also contains the self-check of subklass == superklass. + // Since the code is rarely used, there is no penalty for moving it + // out of line, and it can only improve I-cache density.) + Node* psc = _gvn.transform( + new (C, 3) PartialSubtypeCheckNode(control(), subklass, superklass) ); + + Node *cmp4 = _gvn.transform( new (C, 3) CmpPNode( psc, null() ) ); + Node *bol4 = _gvn.transform( new (C, 2) BoolNode( cmp4, BoolTest::ne ) ); + IfNode *iff4 = create_and_xform_if( control(), bol4, PROB_FAIR, COUNT_UNKNOWN ); + r_not_subtype->init_req(2, _gvn.transform( new (C, 1) IfTrueNode (iff4) ) ); + r_ok_subtype ->init_req(3, _gvn.transform( new (C, 1) IfFalseNode(iff4) ) ); + + // Return false path; set default control to true path. + set_control( _gvn.transform(r_ok_subtype) ); + return _gvn.transform(r_not_subtype); +} + +//----------------------------static_subtype_check----------------------------- +// Shortcut important common cases when superklass is exact: +// (0) superklass is java.lang.Object (can occur in reflective code) +// (1) subklass is already limited to a subtype of superklass => always ok +// (2) subklass does not overlap with superklass => always fail +// (3) superklass has NO subtypes and we can check with a simple compare. +int GraphKit::static_subtype_check(ciKlass* superk, ciKlass* subk) { + if (StressReflectiveCode) { + return SSC_full_test; // Let caller generate the general case. + } + + if (superk == env()->Object_klass()) { + return SSC_always_true; // (0) this test cannot fail + } + + ciType* superelem = superk; + if (superelem->is_array_klass()) + superelem = superelem->as_array_klass()->base_element_type(); + + if (!subk->is_interface()) { // cannot trust static interface types yet + if (subk->is_subtype_of(superk)) { + return SSC_always_true; // (1) false path dead; no dynamic test needed + } + if (!(superelem->is_klass() && superelem->as_klass()->is_interface()) && + !superk->is_subtype_of(subk)) { + return SSC_always_false; + } + } + + // If casting to an instance klass, it must have no subtypes + if (superk->is_interface()) { + // Cannot trust interfaces yet. + // %%% S.B. superk->nof_implementors() == 1 + } else if (superelem->is_instance_klass()) { + ciInstanceKlass* ik = superelem->as_instance_klass(); + if (!ik->has_subklass() && !ik->is_interface()) { + if (!ik->is_final()) { + // Add a dependency if there is a chance of a later subclass. + C->dependencies()->assert_leaf_type(ik); + } + return SSC_easy_test; // (3) caller can do a simple ptr comparison + } + } else { + // A primitive array type has no subtypes. + return SSC_easy_test; // (3) caller can do a simple ptr comparison + } + + return SSC_full_test; +} + +// Profile-driven exact type check: +Node* GraphKit::type_check_receiver(Node* receiver, ciKlass* klass, + float prob, + Node* *casted_receiver) { + const TypeKlassPtr* tklass = TypeKlassPtr::make(klass); + Node* recv_klass = load_object_klass(receiver); + Node* want_klass = makecon(tklass); + Node* cmp = _gvn.transform( new(C, 3) CmpPNode(recv_klass, want_klass) ); + Node* bol = _gvn.transform( new(C, 2) BoolNode(cmp, BoolTest::eq) ); + IfNode* iff = create_and_xform_if(control(), bol, prob, COUNT_UNKNOWN); + set_control( _gvn.transform( new(C, 1) IfTrueNode (iff) )); + Node* fail = _gvn.transform( new(C, 1) IfFalseNode(iff) ); + + const TypeOopPtr* recv_xtype = tklass->as_instance_type(); + assert(recv_xtype->klass_is_exact(), ""); + + // Subsume downstream occurrences of receiver with a cast to + // recv_xtype, since now we know what the type will be. + Node* cast = new(C, 2) CheckCastPPNode(control(), receiver, recv_xtype); + (*casted_receiver) = _gvn.transform(cast); + // (User must make the replace_in_map call.) + + return fail; +} + + +//-------------------------------gen_instanceof-------------------------------- +// Generate an instance-of idiom. Used by both the instance-of bytecode +// and the reflective instance-of call. +Node* GraphKit::gen_instanceof( Node *subobj, Node* superklass ) { + C->set_has_split_ifs(true); // Has chance for split-if optimization + assert( !stopped(), "dead parse path should be checked in callers" ); + assert(!TypePtr::NULL_PTR->higher_equal(_gvn.type(superklass)->is_klassptr()), + "must check for not-null not-dead klass in callers"); + + // Make the merge point + enum { _obj_path = 1, _fail_path, _null_path, PATH_LIMIT }; + RegionNode* region = new(C, PATH_LIMIT) RegionNode(PATH_LIMIT); + Node* phi = new(C, PATH_LIMIT) PhiNode(region, TypeInt::BOOL); + C->set_has_split_ifs(true); // Has chance for split-if optimization + + // Null check; get casted pointer; set region slot 3 + Node* null_ctl = top(); + Node* not_null_obj = null_check_oop(subobj, &null_ctl); + + // If not_null_obj is dead, only null-path is taken + if (stopped()) { // Doing instance-of on a NULL? + set_control(null_ctl); + return intcon(0); + } + region->init_req(_null_path, null_ctl); + phi ->init_req(_null_path, intcon(0)); // Set null path value + + // Load the object's klass + Node* obj_klass = load_object_klass(not_null_obj); + + // Generate the subtype check + Node* not_subtype_ctrl = gen_subtype_check(obj_klass, superklass); + + // Plug in the success path to the general merge in slot 1. + region->init_req(_obj_path, control()); + phi ->init_req(_obj_path, intcon(1)); + + // Plug in the failing path to the general merge in slot 2. + region->init_req(_fail_path, not_subtype_ctrl); + phi ->init_req(_fail_path, intcon(0)); + + // Return final merged results + set_control( _gvn.transform(region) ); + record_for_igvn(region); + return _gvn.transform(phi); +} + +//-------------------------------gen_checkcast--------------------------------- +// Generate a checkcast idiom. Used by both the checkcast bytecode and the +// array store bytecode. Stack must be as-if BEFORE doing the bytecode so the +// uncommon-trap paths work. Adjust stack after this call. +// If failure_control is supplied and not null, it is filled in with +// the control edge for the cast failure. Otherwise, an appropriate +// uncommon trap or exception is thrown. +Node* GraphKit::gen_checkcast(Node *obj, Node* superklass, + Node* *failure_control) { + kill_dead_locals(); // Benefit all the uncommon traps + const TypeKlassPtr *tk = _gvn.type(superklass)->is_klassptr(); + const Type *toop = TypeOopPtr::make_from_klass(tk->klass()); + + // Fast cutout: Check the case that the cast is vacuously true. + // This detects the common cases where the test will short-circuit + // away completely. We do this before we perform the null check, + // because if the test is going to turn into zero code, we don't + // want a residual null check left around. (Causes a slowdown, + // for example, in some objArray manipulations, such as a[i]=a[j].) + if (tk->singleton()) { + const TypeOopPtr* objtp = _gvn.type(obj)->isa_oopptr(); + if (objtp != NULL && objtp->klass() != NULL) { + switch (static_subtype_check(tk->klass(), objtp->klass())) { + case SSC_always_true: + return obj; + case SSC_always_false: + // It needs a null check because a null will *pass* the cast check. + // A non-null value will always produce an exception. + return do_null_assert(obj, T_OBJECT); + } + } + } + + ciProfileData* data = NULL; + if (failure_control == NULL) { // use MDO in regular case only + assert(java_bc() == Bytecodes::_aastore || + java_bc() == Bytecodes::_checkcast, + "interpreter profiles type checks only for these BCs"); + data = method()->method_data()->bci_to_data(bci()); + } + + // Make the merge point + enum { _obj_path = 1, _null_path, PATH_LIMIT }; + RegionNode* region = new (C, PATH_LIMIT) RegionNode(PATH_LIMIT); + Node* phi = new (C, PATH_LIMIT) PhiNode(region, toop); + C->set_has_split_ifs(true); // Has chance for split-if optimization + + // Use null-cast information if it is available + bool never_see_null = false; + // If we see an unexpected null at a check-cast we record it and force a + // recompile; the offending check-cast will be compiled to handle NULLs. + // If we see several offending BCIs, then all checkcasts in the + // method will be compiled to handle NULLs. + if (UncommonNullCast // Cutout for this technique + && failure_control == NULL // regular case + && obj != null() // And not the -Xcomp stupid case? + && !too_many_traps(Deoptimization::Reason_null_check)) { + // Finally, check the "null_seen" bit from the interpreter. + if (data == NULL || !data->as_BitData()->null_seen()) { + never_see_null = true; + } + } + + // Null check; get casted pointer; set region slot 3 + Node* null_ctl = top(); + Node* not_null_obj = null_check_oop(obj, &null_ctl, never_see_null); + + // If not_null_obj is dead, only null-path is taken + if (stopped()) { // Doing instance-of on a NULL? + set_control(null_ctl); + return null(); + } + region->init_req(_null_path, null_ctl); + phi ->init_req(_null_path, null()); // Set null path value + + Node* cast_obj = NULL; // the casted version of the object + + // If the profile has seen exactly one type, narrow to that type. + // (The subsequent subtype check will always fold up.) + if (UseTypeProfile && TypeProfileCasts && data != NULL && + // Counter has never been decremented (due to cast failure). + // ...This is a reasonable thing to expect. It is true of + // all casts inserted by javac to implement generic types. + data->as_CounterData()->count() >= 0 && + !too_many_traps(Deoptimization::Reason_class_check)) { + // (No, this isn't a call, but it's enough like a virtual call + // to use the same ciMethod accessor to get the profile info...) + ciCallProfile profile = method()->call_profile_at_bci(bci()); + if (profile.count() >= 0 && // no cast failures here + profile.has_receiver(0) && + profile.morphism() == 1) { + ciKlass* exact_kls = profile.receiver(0); + int ssc = static_subtype_check(tk->klass(), exact_kls); + if (ssc == SSC_always_true) { + // If we narrow the type to match what the type profile sees, + // we can then remove the rest of the cast. + // This is a win, even if the exact_kls is very specific, + // because downstream operations, such as method calls, + // will often benefit from the sharper type. + Node* exact_obj = not_null_obj; // will get updated in place... + Node* slow_ctl = type_check_receiver(exact_obj, exact_kls, 1.0, + &exact_obj); + { PreserveJVMState pjvms(this); + set_control(slow_ctl); + uncommon_trap(Deoptimization::Reason_class_check, + Deoptimization::Action_maybe_recompile); + } + if (failure_control != NULL) // failure is now impossible + (*failure_control) = top(); + replace_in_map(not_null_obj, exact_obj); + // adjust the type of the phi to the exact klass: + phi->raise_bottom_type(_gvn.type(exact_obj)->meet(TypePtr::NULL_PTR)); + cast_obj = exact_obj; + } + // assert(cast_obj != NULL)... except maybe the profile lied to us. + } + } + + if (cast_obj == NULL) { + // Load the object's klass + Node* obj_klass = load_object_klass(not_null_obj); + + // Generate the subtype check + Node* not_subtype_ctrl = gen_subtype_check( obj_klass, superklass ); + + // Plug in success path into the merge + cast_obj = _gvn.transform(new (C, 2) CheckCastPPNode(control(), + not_null_obj, toop)); + // Failure path ends in uncommon trap (or may be dead - failure impossible) + if (failure_control == NULL) { + if (not_subtype_ctrl != top()) { // If failure is possible + PreserveJVMState pjvms(this); + set_control(not_subtype_ctrl); + builtin_throw(Deoptimization::Reason_class_check, obj_klass); + } + } else { + (*failure_control) = not_subtype_ctrl; + } + } + + region->init_req(_obj_path, control()); + phi ->init_req(_obj_path, cast_obj); + + // A merge of NULL or Casted-NotNull obj + Node* res = _gvn.transform(phi); + + // Note I do NOT always 'replace_in_map(obj,result)' here. + // if( tk->klass()->can_be_primary_super() ) + // This means that if I successfully store an Object into an array-of-String + // I 'forget' that the Object is really now known to be a String. I have to + // do this because we don't have true union types for interfaces - if I store + // a Baz into an array-of-Interface and then tell the optimizer it's an + // Interface, I forget that it's also a Baz and cannot do Baz-like field + // references to it. FIX THIS WHEN UNION TYPES APPEAR! + // replace_in_map( obj, res ); + + // Return final merged results + set_control( _gvn.transform(region) ); + record_for_igvn(region); + return res; +} + +//------------------------------next_monitor----------------------------------- +// What number should be given to the next monitor? +int GraphKit::next_monitor() { + int current = jvms()->monitor_depth()* C->sync_stack_slots(); + int next = current + C->sync_stack_slots(); + // Keep the toplevel high water mark current: + if (C->fixed_slots() < next) C->set_fixed_slots(next); + return current; +} + +//------------------------------insert_mem_bar--------------------------------- +// Memory barrier to avoid floating things around +// The membar serves as a pinch point between both control and all memory slices. +Node* GraphKit::insert_mem_bar(int opcode, Node* precedent) { + MemBarNode* mb = MemBarNode::make(C, opcode, Compile::AliasIdxBot, precedent); + mb->init_req(TypeFunc::Control, control()); + mb->init_req(TypeFunc::Memory, reset_memory()); + Node* membar = _gvn.transform(mb); + set_control(_gvn.transform(new (C, 1) ProjNode(membar,TypeFunc::Control) )); + set_all_memory_call(membar); + return membar; +} + +//-------------------------insert_mem_bar_volatile---------------------------- +// Memory barrier to avoid floating things around +// The membar serves as a pinch point between both control and memory(alias_idx). +// If you want to make a pinch point on all memory slices, do not use this +// function (even with AliasIdxBot); use insert_mem_bar() instead. +Node* GraphKit::insert_mem_bar_volatile(int opcode, int alias_idx, Node* precedent) { + // When Parse::do_put_xxx updates a volatile field, it appends a series + // of MemBarVolatile nodes, one for *each* volatile field alias category. + // The first membar is on the same memory slice as the field store opcode. + // This forces the membar to follow the store. (Bug 6500685 broke this.) + // All the other membars (for other volatile slices, including AliasIdxBot, + // which stands for all unknown volatile slices) are control-dependent + // on the first membar. This prevents later volatile loads or stores + // from sliding up past the just-emitted store. + + MemBarNode* mb = MemBarNode::make(C, opcode, alias_idx, precedent); + mb->set_req(TypeFunc::Control,control()); + if (alias_idx == Compile::AliasIdxBot) { + mb->set_req(TypeFunc::Memory, merged_memory()->base_memory()); + } else { + assert(!(opcode == Op_Initialize && alias_idx != Compile::AliasIdxRaw), "fix caller"); + mb->set_req(TypeFunc::Memory, memory(alias_idx)); + } + Node* membar = _gvn.transform(mb); + set_control(_gvn.transform(new (C, 1) ProjNode(membar, TypeFunc::Control))); + if (alias_idx == Compile::AliasIdxBot) { + merged_memory()->set_base_memory(_gvn.transform(new (C, 1) ProjNode(membar, TypeFunc::Memory))); + } else { + set_memory(_gvn.transform(new (C, 1) ProjNode(membar, TypeFunc::Memory)),alias_idx); + } + return membar; +} + +//------------------------------shared_lock------------------------------------ +// Emit locking code. +FastLockNode* GraphKit::shared_lock(Node* obj) { + // bci is either a monitorenter bc or InvocationEntryBci + // %%% SynchronizationEntryBCI is redundant; use InvocationEntryBci in interfaces + assert(SynchronizationEntryBCI == InvocationEntryBci, ""); + + if( !GenerateSynchronizationCode ) + return NULL; // Not locking things? + if (stopped()) // Dead monitor? + return NULL; + + assert(dead_locals_are_killed(), "should kill locals before sync. point"); + + // Box the stack location + Node* box = _gvn.transform(new (C, 1) BoxLockNode(next_monitor())); + Node* mem = reset_memory(); + + FastLockNode * flock = _gvn.transform(new (C, 3) FastLockNode(0, obj, box) )->as_FastLock(); + if (PrintPreciseBiasedLockingStatistics) { + // Create the counters for this fast lock. + flock->create_lock_counter(sync_jvms()); // sync_jvms used to get current bci + } + // Add monitor to debug info for the slow path. If we block inside the + // slow path and de-opt, we need the monitor hanging around + map()->push_monitor( flock ); + + const TypeFunc *tf = LockNode::lock_type(); + LockNode *lock = new (C, tf->domain()->cnt()) LockNode(C, tf); + + lock->init_req( TypeFunc::Control, control() ); + lock->init_req( TypeFunc::Memory , mem ); + lock->init_req( TypeFunc::I_O , top() ) ; // does no i/o + lock->init_req( TypeFunc::FramePtr, frameptr() ); + lock->init_req( TypeFunc::ReturnAdr, top() ); + + lock->init_req(TypeFunc::Parms + 0, obj); + lock->init_req(TypeFunc::Parms + 1, box); + lock->init_req(TypeFunc::Parms + 2, flock); + add_safepoint_edges(lock); + + lock = _gvn.transform( lock )->as_Lock(); + + // lock has no side-effects, sets few values + set_predefined_output_for_runtime_call(lock, mem, TypeRawPtr::BOTTOM); + + insert_mem_bar(Op_MemBarAcquire); + + // Add this to the worklist so that the lock can be eliminated + record_for_igvn(lock); + +#ifndef PRODUCT + if (PrintLockStatistics) { + // Update the counter for this lock. Don't bother using an atomic + // operation since we don't require absolute accuracy. + lock->create_lock_counter(map()->jvms()); + int adr_type = Compile::AliasIdxRaw; + Node* counter_addr = makecon(TypeRawPtr::make(lock->counter()->addr())); + Node* cnt = make_load(NULL, counter_addr, TypeInt::INT, T_INT, adr_type); + Node* incr = _gvn.transform(new (C, 3) AddINode(cnt, _gvn.intcon(1))); + store_to_memory(control(), counter_addr, incr, T_INT, adr_type); + } +#endif + + return flock; +} + + +//------------------------------shared_unlock---------------------------------- +// Emit unlocking code. +void GraphKit::shared_unlock(Node* box, Node* obj) { + // bci is either a monitorenter bc or InvocationEntryBci + // %%% SynchronizationEntryBCI is redundant; use InvocationEntryBci in interfaces + assert(SynchronizationEntryBCI == InvocationEntryBci, ""); + + if( !GenerateSynchronizationCode ) + return; + if (stopped()) { // Dead monitor? + map()->pop_monitor(); // Kill monitor from debug info + return; + } + + // Memory barrier to avoid floating things down past the locked region + insert_mem_bar(Op_MemBarRelease); + + const TypeFunc *tf = OptoRuntime::complete_monitor_exit_Type(); + UnlockNode *unlock = new (C, tf->domain()->cnt()) UnlockNode(C, tf); + uint raw_idx = Compile::AliasIdxRaw; + unlock->init_req( TypeFunc::Control, control() ); + unlock->init_req( TypeFunc::Memory , memory(raw_idx) ); + unlock->init_req( TypeFunc::I_O , top() ) ; // does no i/o + unlock->init_req( TypeFunc::FramePtr, frameptr() ); + unlock->init_req( TypeFunc::ReturnAdr, top() ); + + unlock->init_req(TypeFunc::Parms + 0, obj); + unlock->init_req(TypeFunc::Parms + 1, box); + unlock = _gvn.transform(unlock)->as_Unlock(); + + Node* mem = reset_memory(); + + // unlock has no side-effects, sets few values + set_predefined_output_for_runtime_call(unlock, mem, TypeRawPtr::BOTTOM); + + // Kill monitor from debug info + map()->pop_monitor( ); +} + +//-------------------------------get_layout_helper----------------------------- +// If the given klass is a constant or known to be an array, +// fetch the constant layout helper value into constant_value +// and return (Node*)NULL. Otherwise, load the non-constant +// layout helper value, and return the node which represents it. +// This two-faced routine is useful because allocation sites +// almost always feature constant types. +Node* GraphKit::get_layout_helper(Node* klass_node, jint& constant_value) { + const TypeKlassPtr* inst_klass = _gvn.type(klass_node)->isa_klassptr(); + if (!StressReflectiveCode && inst_klass != NULL) { + ciKlass* klass = inst_klass->klass(); + bool xklass = inst_klass->klass_is_exact(); + if (xklass || klass->is_array_klass()) { + jint lhelper = klass->layout_helper(); + if (lhelper != Klass::_lh_neutral_value) { + constant_value = lhelper; + return (Node*) NULL; + } + } + } + constant_value = Klass::_lh_neutral_value; // put in a known value + Node* lhp = basic_plus_adr(klass_node, klass_node, Klass::layout_helper_offset_in_bytes() + sizeof(oopDesc)); + return make_load(NULL, lhp, TypeInt::INT, T_INT); +} + +// We just put in an allocate/initialize with a big raw-memory effect. +// Hook selected additional alias categories on the initialization. +static void hook_memory_on_init(GraphKit& kit, int alias_idx, + MergeMemNode* init_in_merge, + Node* init_out_raw) { + DEBUG_ONLY(Node* init_in_raw = init_in_merge->base_memory()); + assert(init_in_merge->memory_at(alias_idx) == init_in_raw, ""); + + Node* prevmem = kit.memory(alias_idx); + init_in_merge->set_memory_at(alias_idx, prevmem); + kit.set_memory(init_out_raw, alias_idx); +} + +//---------------------------set_output_for_allocation------------------------- +Node* GraphKit::set_output_for_allocation(AllocateNode* alloc, + const TypeOopPtr* oop_type, + bool raw_mem_only) { + int rawidx = Compile::AliasIdxRaw; + alloc->set_req( TypeFunc::FramePtr, frameptr() ); + add_safepoint_edges(alloc); + Node* allocx = _gvn.transform(alloc); + set_control( _gvn.transform(new (C, 1) ProjNode(allocx, TypeFunc::Control) ) ); + // create memory projection for i_o + set_memory ( _gvn.transform( new (C, 1) ProjNode(allocx, TypeFunc::Memory, true) ), rawidx ); + make_slow_call_ex(allocx, env()->OutOfMemoryError_klass(), true); + + // create a memory projection as for the normal control path + Node* malloc = _gvn.transform(new (C, 1) ProjNode(allocx, TypeFunc::Memory)); + set_memory(malloc, rawidx); + + // a normal slow-call doesn't change i_o, but an allocation does + // we create a separate i_o projection for the normal control path + set_i_o(_gvn.transform( new (C, 1) ProjNode(allocx, TypeFunc::I_O, false) ) ); + Node* rawoop = _gvn.transform( new (C, 1) ProjNode(allocx, TypeFunc::Parms) ); + + // put in an initialization barrier + InitializeNode* init = insert_mem_bar_volatile(Op_Initialize, rawidx, + rawoop)->as_Initialize(); + assert(alloc->initialization() == init, "2-way macro link must work"); + assert(init ->allocation() == alloc, "2-way macro link must work"); + if (ReduceFieldZeroing && !raw_mem_only) { + // Extract memory strands which may participate in the new object's + // initialization, and source them from the new InitializeNode. + // This will allow us to observe initializations when they occur, + // and link them properly (as a group) to the InitializeNode. + Node* klass_node = alloc->in(AllocateNode::KlassNode); + assert(init->in(InitializeNode::Memory) == malloc, ""); + MergeMemNode* minit_in = MergeMemNode::make(C, malloc); + init->set_req(InitializeNode::Memory, minit_in); + record_for_igvn(minit_in); // fold it up later, if possible + Node* minit_out = memory(rawidx); + assert(minit_out->is_Proj() && minit_out->in(0) == init, ""); + if (oop_type->isa_aryptr()) { + const TypePtr* telemref = oop_type->add_offset(Type::OffsetBot); + int elemidx = C->get_alias_index(telemref); + hook_memory_on_init(*this, elemidx, minit_in, minit_out); + } else if (oop_type->isa_instptr()) { + ciInstanceKlass* ik = oop_type->klass()->as_instance_klass(); + for (int i = 0, len = ik->nof_nonstatic_fields(); i < len; i++) { + ciField* field = ik->nonstatic_field_at(i); + if (field->offset() >= TrackedInitializationLimit) + continue; // do not bother to track really large numbers of fields + // Find (or create) the alias category for this field: + int fieldidx = C->alias_type(field)->index(); + hook_memory_on_init(*this, fieldidx, minit_in, minit_out); + } + } + } + + // Cast raw oop to the real thing... + Node* javaoop = new (C, 2) CheckCastPPNode(control(), rawoop, oop_type); + javaoop = _gvn.transform(javaoop); + C->set_recent_alloc(control(), javaoop); + assert(just_allocated_object(control()) == javaoop, "just allocated"); + +#ifdef ASSERT + { // Verify that the AllocateNode::Ideal_foo recognizers work: + Node* kn = alloc->in(AllocateNode::KlassNode); + Node* ln = alloc->in(AllocateNode::ALength); + assert(AllocateNode::Ideal_klass(rawoop, &_gvn) == kn, + "Ideal_klass works"); + assert(AllocateNode::Ideal_klass(javaoop, &_gvn) == kn, + "Ideal_klass works"); + if (alloc->is_AllocateArray()) { + assert(AllocateArrayNode::Ideal_length(rawoop, &_gvn) == ln, + "Ideal_length works"); + assert(AllocateArrayNode::Ideal_length(javaoop, &_gvn) == ln, + "Ideal_length works"); + } else { + assert(ln->is_top(), "no length, please"); + } + } +#endif //ASSERT + + return javaoop; +} + +//---------------------------new_instance-------------------------------------- +// This routine takes a klass_node which may be constant (for a static type) +// or may be non-constant (for reflective code). It will work equally well +// for either, and the graph will fold nicely if the optimizer later reduces +// the type to a constant. +// The optional arguments are for specialized use by intrinsics: +// - If 'extra_slow_test' if not null is an extra condition for the slow-path. +// - If 'raw_mem_only', do not cast the result to an oop. +// - If 'return_size_val', report the the total object size to the caller. +Node* GraphKit::new_instance(Node* klass_node, + Node* extra_slow_test, + bool raw_mem_only, // affect only raw memory + Node* *return_size_val) { + // Compute size in doublewords + // The size is always an integral number of doublewords, represented + // as a positive bytewise size stored in the klass's layout_helper. + // The layout_helper also encodes (in a low bit) the need for a slow path. + jint layout_con = Klass::_lh_neutral_value; + Node* layout_val = get_layout_helper(klass_node, layout_con); + int layout_is_con = (layout_val == NULL); + + if (extra_slow_test == NULL) extra_slow_test = intcon(0); + // Generate the initial go-slow test. It's either ALWAYS (return a + // Node for 1) or NEVER (return a NULL) or perhaps (in the reflective + // case) a computed value derived from the layout_helper. + Node* initial_slow_test = NULL; + if (layout_is_con) { + assert(!StressReflectiveCode, "stress mode does not use these paths"); + bool must_go_slow = Klass::layout_helper_needs_slow_path(layout_con); + initial_slow_test = must_go_slow? intcon(1): extra_slow_test; + + } else { // reflective case + // This reflective path is used by Unsafe.allocateInstance. + // (It may be stress-tested by specifying StressReflectiveCode.) + // Basically, we want to get into the VM is there's an illegal argument. + Node* bit = intcon(Klass::_lh_instance_slow_path_bit); + initial_slow_test = _gvn.transform( new (C, 3) AndINode(layout_val, bit) ); + if (extra_slow_test != intcon(0)) { + initial_slow_test = _gvn.transform( new (C, 3) OrINode(initial_slow_test, extra_slow_test) ); + } + // (Macro-expander will further convert this to a Bool, if necessary.) + } + + // Find the size in bytes. This is easy; it's the layout_helper. + // The size value must be valid even if the slow path is taken. + Node* size = NULL; + if (layout_is_con) { + size = MakeConX(Klass::layout_helper_size_in_bytes(layout_con)); + } else { // reflective case + // This reflective path is used by clone and Unsafe.allocateInstance. + size = ConvI2X(layout_val); + + // Clear the low bits to extract layout_helper_size_in_bytes: + assert((int)Klass::_lh_instance_slow_path_bit < BytesPerLong, "clear bit"); + Node* mask = MakeConX(~ (intptr_t)right_n_bits(LogBytesPerLong)); + size = _gvn.transform( new (C, 3) AndXNode(size, mask) ); + } + if (return_size_val != NULL) { + (*return_size_val) = size; + } + + // This is a precise notnull oop of the klass. + // (Actually, it need not be precise if this is a reflective allocation.) + // It's what we cast the result to. + const TypeKlassPtr* tklass = _gvn.type(klass_node)->isa_klassptr(); + if (!tklass) tklass = TypeKlassPtr::OBJECT; + const TypeOopPtr* oop_type = tklass->as_instance_type(); + + // Now generate allocation code + AllocateNode* alloc + = new (C, AllocateNode::ParmLimit) + AllocateNode(C, AllocateNode::alloc_type(), + control(), memory(Compile::AliasIdxRaw), i_o(), + size, klass_node, + initial_slow_test); + + return set_output_for_allocation(alloc, oop_type, raw_mem_only); +} + +//-------------------------------new_array------------------------------------- +// helper for both newarray and anewarray +// The 'length' parameter is (obviously) the length of the array. +// See comments on new_instance for the meaning of the other arguments. +Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable) + Node* length, // number of array elements + bool raw_mem_only, // affect only raw memory + Node* *return_size_val) { + jint layout_con = Klass::_lh_neutral_value; + Node* layout_val = get_layout_helper(klass_node, layout_con); + int layout_is_con = (layout_val == NULL); + + if (!layout_is_con && !StressReflectiveCode && + !too_many_traps(Deoptimization::Reason_class_check)) { + // This is a reflective array creation site. + // Optimistically assume that it is a subtype of Object[], + // so that we can fold up all the address arithmetic. + layout_con = Klass::array_layout_helper(T_OBJECT); + Node* cmp_lh = _gvn.transform( new(C, 3) CmpINode(layout_val, intcon(layout_con)) ); + Node* bol_lh = _gvn.transform( new(C, 2) BoolNode(cmp_lh, BoolTest::eq) ); + { BuildCutout unless(this, bol_lh, PROB_MAX); + uncommon_trap(Deoptimization::Reason_class_check, + Deoptimization::Action_maybe_recompile); + } + layout_val = NULL; + layout_is_con = true; + } + + // Generate the initial go-slow test. Make sure we do not overflow + // if length is huge (near 2Gig) or negative! We do not need + // exact double-words here, just a close approximation of needed + // double-words. We can't add any offset or rounding bits, lest we + // take a size -1 of bytes and make it positive. Use an unsigned + // compare, so negative sizes look hugely positive. + int fast_size_limit = FastAllocateSizeLimit; + if (layout_is_con) { + assert(!StressReflectiveCode, "stress mode does not use these paths"); + // Increase the size limit if we have exact knowledge of array type. + int log2_esize = Klass::layout_helper_log2_element_size(layout_con); + fast_size_limit <<= (LogBytesPerLong - log2_esize); + } + + Node* initial_slow_cmp = _gvn.transform( new (C, 3) CmpUNode( length, intcon( fast_size_limit ) ) ); + Node* initial_slow_test = _gvn.transform( new (C, 2) BoolNode( initial_slow_cmp, BoolTest::gt ) ); + if (initial_slow_test->is_Bool()) { + // Hide it behind a CMoveI, or else PhaseIdealLoop::split_up will get sick. + initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn); + } + + // --- Size Computation --- + // array_size = round_to_heap(array_header + (length << elem_shift)); + // where round_to_heap(x) == round_to(x, MinObjAlignmentInBytes) + // and round_to(x, y) == ((x + y-1) & ~(y-1)) + // The rounding mask is strength-reduced, if possible. + int round_mask = MinObjAlignmentInBytes - 1; + Node* header_size = NULL; + int header_size_min = arrayOopDesc::base_offset_in_bytes(T_BYTE); + // (T_BYTE has the weakest alignment and size restrictions...) + if (layout_is_con) { + int hsize = Klass::layout_helper_header_size(layout_con); + int eshift = Klass::layout_helper_log2_element_size(layout_con); + BasicType etype = Klass::layout_helper_element_type(layout_con); + if ((round_mask & ~right_n_bits(eshift)) == 0) + round_mask = 0; // strength-reduce it if it goes away completely + assert((hsize & right_n_bits(eshift)) == 0, "hsize is pre-rounded"); + assert(header_size_min <= hsize, "generic minimum is smallest"); + header_size_min = hsize; + header_size = intcon(hsize + round_mask); + } else { + Node* hss = intcon(Klass::_lh_header_size_shift); + Node* hsm = intcon(Klass::_lh_header_size_mask); + Node* hsize = _gvn.transform( new(C, 3) URShiftINode(layout_val, hss) ); + hsize = _gvn.transform( new(C, 3) AndINode(hsize, hsm) ); + Node* mask = intcon(round_mask); + header_size = _gvn.transform( new(C, 3) AddINode(hsize, mask) ); + } + + Node* elem_shift = NULL; + if (layout_is_con) { + int eshift = Klass::layout_helper_log2_element_size(layout_con); + if (eshift != 0) + elem_shift = intcon(eshift); + } else { + // There is no need to mask or shift this value. + // The semantics of LShiftINode include an implicit mask to 0x1F. + assert(Klass::_lh_log2_element_size_shift == 0, "use shift in place"); + elem_shift = layout_val; + } + + // Transition to native address size for all offset calculations: + Node* lengthx = ConvI2X(length); + Node* headerx = ConvI2X(header_size); +#ifdef _LP64 + { const TypeLong* tllen = _gvn.find_long_type(lengthx); + if (tllen != NULL && tllen->_lo < 0) { + // Add a manual constraint to a positive range. Cf. array_element_address. + jlong size_max = arrayOopDesc::max_array_length(T_BYTE); + if (size_max > tllen->_hi) size_max = tllen->_hi; + const TypeLong* tlcon = TypeLong::make(CONST64(0), size_max, Type::WidenMin); + lengthx = _gvn.transform( new (C, 2) ConvI2LNode(length, tlcon)); + } + } +#endif + + // Combine header size (plus rounding) and body size. Then round down. + // This computation cannot overflow, because it is used only in two + // places, one where the length is sharply limited, and the other + // after a successful allocation. + Node* abody = lengthx; + if (elem_shift != NULL) + abody = _gvn.transform( new(C, 3) LShiftXNode(lengthx, elem_shift) ); + Node* size = _gvn.transform( new(C, 3) AddXNode(headerx, abody) ); + if (round_mask != 0) { + Node* mask = MakeConX(~round_mask); + size = _gvn.transform( new(C, 3) AndXNode(size, mask) ); + } + // else if round_mask == 0, the size computation is self-rounding + + if (return_size_val != NULL) { + // This is the size + (*return_size_val) = size; + } + + // Now generate allocation code + // Create the AllocateArrayNode and its result projections + AllocateArrayNode* alloc + = new (C, AllocateArrayNode::ParmLimit) + AllocateArrayNode(C, AllocateArrayNode::alloc_type(), + control(), memory(Compile::AliasIdxRaw), i_o(), + size, klass_node, + initial_slow_test, + length); + + // Cast to correct type. Note that the klass_node may be constant or not, + // and in the latter case the actual array type will be inexact also. + // (This happens via a non-constant argument to inline_native_newArray.) + // In any case, the value of klass_node provides the desired array type. + const TypeInt* length_type = _gvn.find_int_type(length); + const TypeInt* narrow_length_type = NULL; + const TypeOopPtr* ary_type = _gvn.type(klass_node)->is_klassptr()->as_instance_type(); + if (ary_type->isa_aryptr() && length_type != NULL) { + // Try to get a better type than POS for the size + ary_type = ary_type->is_aryptr()->cast_to_size(length_type); + narrow_length_type = ary_type->is_aryptr()->size(); + if (narrow_length_type == length_type) + narrow_length_type = NULL; + } + + Node* javaoop = set_output_for_allocation(alloc, ary_type, raw_mem_only); + + // Cast length on remaining path to be positive: + if (narrow_length_type != NULL) { + Node* ccast = new (C, 2) CastIINode(length, narrow_length_type); + ccast->set_req(0, control()); + _gvn.set_type_bottom(ccast); + record_for_igvn(ccast); + if (map()->find_edge(length) >= 0) { + replace_in_map(length, ccast); + } + } + + return javaoop; +} + +// The following "Ideal_foo" functions are placed here because they recognize +// the graph shapes created by the functions immediately above. + +//---------------------------Ideal_allocation---------------------------------- +// Given an oop pointer or raw pointer, see if it feeds from an AllocateNode. +AllocateNode* AllocateNode::Ideal_allocation(Node* ptr, PhaseTransform* phase) { + if (ptr == NULL) { // reduce dumb test in callers + return NULL; + } + if (ptr->is_CheckCastPP()) { // strip a raw-to-oop cast + ptr = ptr->in(1); + if (ptr == NULL) return NULL; + } + if (ptr->is_Proj()) { + Node* allo = ptr->in(0); + if (allo != NULL && allo->is_Allocate()) { + return allo->as_Allocate(); + } + } + // Report failure to match. + return NULL; +} + +// Fancy version which also strips off an offset (and reports it to caller). +AllocateNode* AllocateNode::Ideal_allocation(Node* ptr, PhaseTransform* phase, + intptr_t& offset) { + Node* base = AddPNode::Ideal_base_and_offset(ptr, phase, offset); + if (base == NULL) return NULL; + return Ideal_allocation(base, phase); +} + +// Trace Initialize <- Proj[Parm] <- Allocate +AllocateNode* InitializeNode::allocation() { + Node* rawoop = in(InitializeNode::RawAddress); + if (rawoop->is_Proj()) { + Node* alloc = rawoop->in(0); + if (alloc->is_Allocate()) { + return alloc->as_Allocate(); + } + } + return NULL; +} + +// Trace Allocate -> Proj[Parm] -> Initialize +InitializeNode* AllocateNode::initialization() { + ProjNode* rawoop = proj_out(AllocateNode::RawAddress); + if (rawoop == NULL) return NULL; + for (DUIterator_Fast imax, i = rawoop->fast_outs(imax); i < imax; i++) { + Node* init = rawoop->fast_out(i); + if (init->is_Initialize()) { + assert(init->as_Initialize()->allocation() == this, "2-way link"); + return init->as_Initialize(); + } + } + return NULL; +} diff --git a/hotspot/src/share/vm/opto/graphKit.hpp b/hotspot/src/share/vm/opto/graphKit.hpp new file mode 100644 index 00000000000..c9ea0262561 --- /dev/null +++ b/hotspot/src/share/vm/opto/graphKit.hpp @@ -0,0 +1,720 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class FastLockNode; +class FastUnlockNode; +class Parse; +class RootNode; + +//----------------------------------------------------------------------------- +//----------------------------GraphKit----------------------------------------- +// Toolkit for building the common sorts of subgraphs. +// Does not know about bytecode parsing or type-flow results. +// It is able to create graphs implementing the semantics of most +// or all bytecodes, so that it can expand intrinsics and calls. +// It may depend on JVMState structure, but it must not depend +// on specific bytecode streams. +class GraphKit : public Phase { + friend class PreserveJVMState; + + protected: + ciEnv* _env; // Compilation environment + PhaseGVN &_gvn; // Some optimizations while parsing + SafePointNode* _map; // Parser map from JVM to Nodes + SafePointNode* _exceptions;// Parser map(s) for exception state(s) + int _sp; // JVM Expression Stack Pointer + int _bci; // JVM Bytecode Pointer + ciMethod* _method; // JVM Current Method + + private: + SafePointNode* map_not_null() const { + assert(_map != NULL, "must call stopped() to test for reset compiler map"); + return _map; + } + + public: + GraphKit(); // empty constructor + GraphKit(JVMState* jvms); // the JVM state on which to operate + +#ifdef ASSERT + ~GraphKit() { + assert(!has_exceptions(), "user must call transfer_exceptions_into_jvms"); + } +#endif + + virtual Parse* is_Parse() const { return NULL; } + + ciEnv* env() const { return _env; } + PhaseGVN& gvn() const { return _gvn; } + + void record_for_igvn(Node* n) const { C->record_for_igvn(n); } // delegate to Compile + + // Handy well-known nodes: + Node* null() const { return zerocon(T_OBJECT); } + Node* top() const { return C->top(); } + RootNode* root() const { return C->root(); } + + // Create or find a constant node + Node* intcon(jint con) const { return _gvn.intcon(con); } + Node* longcon(jlong con) const { return _gvn.longcon(con); } + Node* makecon(const Type *t) const { return _gvn.makecon(t); } + Node* zerocon(BasicType bt) const { return _gvn.zerocon(bt); } + // (See also macro MakeConX in type.hpp, which uses intcon or longcon.) + + jint find_int_con(Node* n, jint value_if_unknown) { + return _gvn.find_int_con(n, value_if_unknown); + } + jlong find_long_con(Node* n, jlong value_if_unknown) { + return _gvn.find_long_con(n, value_if_unknown); + } + // (See also macro find_intptr_t_con in type.hpp, which uses one of these.) + + // JVM State accessors: + // Parser mapping from JVM indices into Nodes. + // Low slots are accessed by the StartNode::enum. + // Then come the locals at StartNode::Parms to StartNode::Parms+max_locals(); + // Then come JVM stack slots. + // Finally come the monitors, if any. + // See layout accessors in class JVMState. + + SafePointNode* map() const { return _map; } + bool has_exceptions() const { return _exceptions != NULL; } + JVMState* jvms() const { return map_not_null()->_jvms; } + int sp() const { return _sp; } + int bci() const { return _bci; } + Bytecodes::Code java_bc() const; + ciMethod* method() const { return _method; } + + void set_jvms(JVMState* jvms) { set_map(jvms->map()); + assert(jvms == this->jvms(), "sanity"); + _sp = jvms->sp(); + _bci = jvms->bci(); + _method = jvms->has_method() ? jvms->method() : NULL; } + void set_map(SafePointNode* m) { _map = m; debug_only(verify_map()); } + void set_sp(int i) { assert(i >= 0, "must be non-negative"); _sp = i; } + void clean_stack(int from_sp); // clear garbage beyond from_sp to top + + void inc_sp(int i) { set_sp(sp() + i); } + void set_bci(int bci) { _bci = bci; } + + // Make sure jvms has current bci & sp. + JVMState* sync_jvms() const; +#ifdef ASSERT + // Make sure JVMS has an updated copy of bci and sp. + // Also sanity-check method, depth, and monitor depth. + bool jvms_in_sync() const; + + // Make sure the map looks OK. + void verify_map() const; + + // Make sure a proposed exception state looks OK. + static void verify_exception_state(SafePointNode* ex_map); +#endif + + // Clone the existing map state. (Implements PreserveJVMState.) + SafePointNode* clone_map(); + + // Set the map to a clone of the given one. + void set_map_clone(SafePointNode* m); + + // Tell if the compilation is failing. + bool failing() const { return C->failing(); } + + // Set _map to NULL, signalling a stop to further bytecode execution. + // Preserve the map intact for future use, and return it back to the caller. + SafePointNode* stop() { SafePointNode* m = map(); set_map(NULL); return m; } + + // Stop, but first smash the map's inputs to NULL, to mark it dead. + void stop_and_kill_map(); + + // Tell if _map is NULL, or control is top. + bool stopped(); + + // Tell if this method or any caller method has exception handlers. + bool has_ex_handler(); + + // Save an exception without blowing stack contents or other JVM state. + // (The extra pointer is stuck with add_req on the map, beyond the JVMS.) + static void set_saved_ex_oop(SafePointNode* ex_map, Node* ex_oop); + + // Recover a saved exception from its map. + static Node* saved_ex_oop(SafePointNode* ex_map); + + // Recover a saved exception from its map, and remove it from the map. + static Node* clear_saved_ex_oop(SafePointNode* ex_map); + +#ifdef ASSERT + // Recover a saved exception from its map, and remove it from the map. + static bool has_saved_ex_oop(SafePointNode* ex_map); +#endif + + // Push an exception in the canonical position for handlers (stack(0)). + void push_ex_oop(Node* ex_oop) { + ensure_stack(1); // ensure room to push the exception + set_stack(0, ex_oop); + set_sp(1); + clean_stack(1); + } + + // Detach and return an exception state. + SafePointNode* pop_exception_state() { + SafePointNode* ex_map = _exceptions; + if (ex_map != NULL) { + _exceptions = ex_map->next_exception(); + ex_map->set_next_exception(NULL); + debug_only(verify_exception_state(ex_map)); + } + return ex_map; + } + + // Add an exception, using the given JVM state, without commoning. + void push_exception_state(SafePointNode* ex_map) { + debug_only(verify_exception_state(ex_map)); + ex_map->set_next_exception(_exceptions); + _exceptions = ex_map; + } + + // Turn the current JVM state into an exception state, appending the ex_oop. + SafePointNode* make_exception_state(Node* ex_oop); + + // Add an exception, using the given JVM state. + // Combine all exceptions with a common exception type into a single state. + // (This is done via combine_exception_states.) + void add_exception_state(SafePointNode* ex_map); + + // Combine all exceptions of any sort whatever into a single master state. + SafePointNode* combine_and_pop_all_exception_states() { + if (_exceptions == NULL) return NULL; + SafePointNode* phi_map = pop_exception_state(); + SafePointNode* ex_map; + while ((ex_map = pop_exception_state()) != NULL) { + combine_exception_states(ex_map, phi_map); + } + return phi_map; + } + + // Combine the two exception states, building phis as necessary. + // The second argument is updated to include contributions from the first. + void combine_exception_states(SafePointNode* ex_map, SafePointNode* phi_map); + + // Reset the map to the given state. If there are any half-finished phis + // in it (created by combine_exception_states), transform them now. + // Returns the exception oop. (Caller must call push_ex_oop if required.) + Node* use_exception_state(SafePointNode* ex_map); + + // Collect exceptions from a given JVM state into my exception list. + void add_exception_states_from(JVMState* jvms); + + // Collect all raised exceptions into the current JVM state. + // Clear the current exception list and map, returns the combined states. + JVMState* transfer_exceptions_into_jvms(); + + // Helper to throw a built-in exception. + // Range checks take the offending index. + // Cast and array store checks take the offending class. + // Others do not take the optional argument. + // The JVMS must allow the bytecode to be re-executed + // via an uncommon trap. + void builtin_throw(Deoptimization::DeoptReason reason, Node* arg = NULL); + + // Helper Functions for adding debug information + void kill_dead_locals(); +#ifdef ASSERT + bool dead_locals_are_killed(); +#endif + // The call may deoptimize. Supply required JVM state as debug info. + // If must_throw is true, the call is guaranteed not to return normally. + void add_safepoint_edges(SafePointNode* call, + bool must_throw = false); + + // How many stack inputs does the current BC consume? + // And, how does the stack change after the bytecode? + // Returns false if unknown. + bool compute_stack_effects(int& inputs, int& depth); + + // Add a fixed offset to a pointer + Node* basic_plus_adr(Node* base, Node* ptr, intptr_t offset) { + return basic_plus_adr(base, ptr, MakeConX(offset)); + } + Node* basic_plus_adr(Node* base, intptr_t offset) { + return basic_plus_adr(base, base, MakeConX(offset)); + } + // Add a variable offset to a pointer + Node* basic_plus_adr(Node* base, Node* offset) { + return basic_plus_adr(base, base, offset); + } + Node* basic_plus_adr(Node* base, Node* ptr, Node* offset); + + // Convert between int and long, and size_t. + // (See macros ConvI2X, etc., in type.hpp for ConvI2X, etc.) + Node* ConvI2L(Node* offset); + Node* ConvL2I(Node* offset); + // Find out the klass of an object. + Node* load_object_klass(Node* object); + // Find out the length of an array. + Node* load_array_length(Node* array); + // Helper function to do a NULL pointer check or ZERO check based on type. + Node* null_check_common(Node* value, BasicType type, + bool assert_null, Node* *null_control); + // Throw an exception if a given value is null. + // Return the value cast to not-null. + // Be clever about equivalent dominating null checks. + Node* do_null_check(Node* value, BasicType type) { + return null_check_common(value, type, false, NULL); + } + // Throw an uncommon trap if a given value is __not__ null. + // Return the value cast to null, and be clever about dominating checks. + Node* do_null_assert(Node* value, BasicType type) { + return null_check_common(value, type, true, NULL); + } + // Null check oop. Return null-path control into (*null_control). + // Return a cast-not-null node which depends on the not-null control. + // If never_see_null, use an uncommon trap (*null_control sees a top). + // The cast is not valid along the null path; keep a copy of the original. + Node* null_check_oop(Node* value, Node* *null_control, + bool never_see_null = false); + + // Cast obj to not-null on this path + Node* cast_not_null(Node* obj, bool do_replace_in_map = true); + // Replace all occurrences of one node by another. + void replace_in_map(Node* old, Node* neww); + + void push(Node* n) { map_not_null(); _map->set_stack(_map->_jvms,_sp++,n); } + Node* pop() { map_not_null(); return _map->stack(_map->_jvms,--_sp); } + Node* peek(int off=0) { map_not_null(); return _map->stack(_map->_jvms, _sp - off - 1); } + + void push_pair(Node* ldval) { + push(ldval); + push(top()); // the halfword is merely a placeholder + } + void push_pair_local(int i) { + // longs are stored in locals in "push" order + push( local(i+0) ); // the real value + assert(local(i+1) == top(), ""); + push(top()); // halfword placeholder + } + Node* pop_pair() { + // the second half is pushed last & popped first; it contains exactly nothing + Node* halfword = pop(); + assert(halfword == top(), ""); + // the long bits are pushed first & popped last: + return pop(); + } + void set_pair_local(int i, Node* lval) { + // longs are stored in locals as a value/half pair (like doubles) + set_local(i+0, lval); + set_local(i+1, top()); + } + + // Push the node, which may be zero, one, or two words. + void push_node(BasicType n_type, Node* n) { + int n_size = type2size[n_type]; + if (n_size == 1) push( n ); // T_INT, ... + else if (n_size == 2) push_pair( n ); // T_DOUBLE, T_LONG + else { assert(n_size == 0, "must be T_VOID"); } + } + + Node* pop_node(BasicType n_type) { + int n_size = type2size[n_type]; + if (n_size == 1) return pop(); + else if (n_size == 2) return pop_pair(); + else return NULL; + } + + Node* control() const { return map_not_null()->control(); } + Node* i_o() const { return map_not_null()->i_o(); } + Node* returnadr() const { return map_not_null()->returnadr(); } + Node* frameptr() const { return map_not_null()->frameptr(); } + Node* local(uint idx) const { map_not_null(); return _map->local( _map->_jvms, idx); } + Node* stack(uint idx) const { map_not_null(); return _map->stack( _map->_jvms, idx); } + Node* argument(uint idx) const { map_not_null(); return _map->argument( _map->_jvms, idx); } + Node* monitor_box(uint idx) const { map_not_null(); return _map->monitor_box(_map->_jvms, idx); } + Node* monitor_obj(uint idx) const { map_not_null(); return _map->monitor_obj(_map->_jvms, idx); } + + void set_control (Node* c) { map_not_null()->set_control(c); } + void set_i_o (Node* c) { map_not_null()->set_i_o(c); } + void set_local(uint idx, Node* c) { map_not_null(); _map->set_local( _map->_jvms, idx, c); } + void set_stack(uint idx, Node* c) { map_not_null(); _map->set_stack( _map->_jvms, idx, c); } + void set_argument(uint idx, Node* c){ map_not_null(); _map->set_argument(_map->_jvms, idx, c); } + void ensure_stack(uint stk_size) { map_not_null(); _map->ensure_stack(_map->_jvms, stk_size); } + + // Access unaliased memory + Node* memory(uint alias_idx); + Node* memory(const TypePtr *tp) { return memory(C->get_alias_index(tp)); } + Node* memory(Node* adr) { return memory(_gvn.type(adr)->is_ptr()); } + + // Access immutable memory + Node* immutable_memory() { return C->immutable_memory(); } + + // Set unaliased memory + void set_memory(Node* c, uint alias_idx) { merged_memory()->set_memory_at(alias_idx, c); } + void set_memory(Node* c, const TypePtr *tp) { set_memory(c,C->get_alias_index(tp)); } + void set_memory(Node* c, Node* adr) { set_memory(c,_gvn.type(adr)->is_ptr()); } + + // Get the entire memory state (probably a MergeMemNode), and reset it + // (The resetting prevents somebody from using the dangling Node pointer.) + Node* reset_memory(); + + // Get the entire memory state, asserted to be a MergeMemNode. + MergeMemNode* merged_memory() { + Node* mem = map_not_null()->memory(); + assert(mem->is_MergeMem(), "parse memory is always pre-split"); + return mem->as_MergeMem(); + } + + // Set the entire memory state; produce a new MergeMemNode. + void set_all_memory(Node* newmem); + + // Create a memory projection from the call, then set_all_memory. + void set_all_memory_call(Node* call); + + // Create a LoadNode, reading from the parser's memory state. + // (Note: require_atomic_access is useful only with T_LONG.) + Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, + bool require_atomic_access = false) { + // This version computes alias_index from bottom_type + return make_load(ctl, adr, t, bt, adr->bottom_type()->is_ptr(), + require_atomic_access); + } + Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, const TypePtr* adr_type, bool require_atomic_access = false) { + // This version computes alias_index from an address type + assert(adr_type != NULL, "use other make_load factory"); + return make_load(ctl, adr, t, bt, C->get_alias_index(adr_type), + require_atomic_access); + } + // This is the base version which is given an alias index. + Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, int adr_idx, bool require_atomic_access = false); + + // Create & transform a StoreNode and store the effect into the + // parser's memory state. + Node* store_to_memory(Node* ctl, Node* adr, Node* val, BasicType bt, + const TypePtr* adr_type, + bool require_atomic_access = false) { + // This version computes alias_index from an address type + assert(adr_type != NULL, "use other store_to_memory factory"); + return store_to_memory(ctl, adr, val, bt, + C->get_alias_index(adr_type), + require_atomic_access); + } + // This is the base version which is given alias index + // Return the new StoreXNode + Node* store_to_memory(Node* ctl, Node* adr, Node* val, BasicType bt, + int adr_idx, + bool require_atomic_access = false); + + + // All in one pre-barrier, store, post_barrier + // Insert a write-barrier'd store. This is to let generational GC + // work; we have to flag all oop-stores before the next GC point. + // + // It comes in 3 flavors of store to an object, array, or unknown. + // We use precise card marks for arrays to avoid scanning the entire + // array. We use imprecise for object. We use precise for unknown + // since we don't know if we have an array or and object or even + // where the object starts. + // + // If val==NULL, it is taken to be a completely unknown value. QQQ + + Node* store_oop_to_object(Node* ctl, + Node* obj, // containing obj + Node* adr, // actual adress to store val at + const TypePtr* adr_type, + Node* val, + const Type* val_type, + BasicType bt); + + Node* store_oop_to_array(Node* ctl, + Node* obj, // containing obj + Node* adr, // actual adress to store val at + const TypePtr* adr_type, + Node* val, + const Type* val_type, + BasicType bt); + + // Could be an array or object we don't know at compile time (unsafe ref.) + Node* store_oop_to_unknown(Node* ctl, + Node* obj, // containing obj + Node* adr, // actual adress to store val at + const TypePtr* adr_type, + Node* val, + const Type* val_type, + BasicType bt); + + // For the few case where the barriers need special help + void pre_barrier(Node* ctl, Node* obj, Node* adr, uint adr_idx, + Node* val, const Type* val_type, BasicType bt); + + void post_barrier(Node* ctl, Node* store, Node* obj, Node* adr, uint adr_idx, + Node* val, BasicType bt, bool use_precise); + + // Return addressing for an array element. + Node* array_element_address(Node* ary, Node* idx, BasicType elembt, + // Optional constraint on the array size: + const TypeInt* sizetype = NULL); + + // Return a load of array element at idx. + Node* load_array_element(Node* ctl, Node* ary, Node* idx, const TypeAryPtr* arytype); + + // CMS card-marks have an input from the corresponding oop_store + void cms_card_mark(Node* ctl, Node* adr, Node* val, Node* oop_store); + + //---------------- Dtrace support -------------------- + void make_dtrace_method_entry_exit(ciMethod* method, bool is_entry); + void make_dtrace_method_entry(ciMethod* method) { + make_dtrace_method_entry_exit(method, true); + } + void make_dtrace_method_exit(ciMethod* method) { + make_dtrace_method_entry_exit(method, false); + } + + //--------------- stub generation ------------------- + public: + void gen_stub(address C_function, + const char *name, + int is_fancy_jump, + bool pass_tls, + bool return_pc); + + //---------- help for generating calls -------------- + + // Do a null check on the receiver, which is in argument(0). + Node* null_check_receiver(ciMethod* callee) { + assert(!callee->is_static(), "must be a virtual method"); + int nargs = 1 + callee->signature()->size(); + // Null check on self without removing any arguments. The argument + // null check technically happens in the wrong place, which can lead to + // invalid stack traces when the primitive is inlined into a method + // which handles NullPointerExceptions. + Node* receiver = argument(0); + _sp += nargs; + receiver = do_null_check(receiver, T_OBJECT); + _sp -= nargs; + return receiver; + } + + // Fill in argument edges for the call from argument(0), argument(1), ... + // (The next step is to call set_edges_for_java_call.) + void set_arguments_for_java_call(CallJavaNode* call); + + // Fill in non-argument edges for the call. + // Transform the call, and update the basics: control, i_o, memory. + // (The next step is usually to call set_results_for_java_call.) + void set_edges_for_java_call(CallJavaNode* call, + bool must_throw = false); + + // Finish up a java call that was started by set_edges_for_java_call. + // Call add_exception on any throw arising from the call. + // Return the call result (transformed). + Node* set_results_for_java_call(CallJavaNode* call); + + // Similar to set_edges_for_java_call, but simplified for runtime calls. + void set_predefined_output_for_runtime_call(Node* call) { + set_predefined_output_for_runtime_call(call, NULL, NULL); + } + void set_predefined_output_for_runtime_call(Node* call, + Node* keep_mem, + const TypePtr* hook_mem); + Node* set_predefined_input_for_runtime_call(SafePointNode* call); + + // helper functions for statistics + void increment_counter(address counter_addr); // increment a debug counter + void increment_counter(Node* counter_addr); // increment a debug counter + + // Bail out to the interpreter right now + // The optional klass is the one causing the trap. + // The optional reason is debug information written to the compile log. + // Optional must_throw is the same as with add_safepoint_edges. + void uncommon_trap(int trap_request, + ciKlass* klass = NULL, const char* reason_string = NULL, + bool must_throw = false, bool keep_exact_action = false); + + // Shorthand, to avoid saying "Deoptimization::" so many times. + void uncommon_trap(Deoptimization::DeoptReason reason, + Deoptimization::DeoptAction action, + ciKlass* klass = NULL, const char* reason_string = NULL, + bool must_throw = false, bool keep_exact_action = false) { + uncommon_trap(Deoptimization::make_trap_request(reason, action), + klass, reason_string, must_throw, keep_exact_action); + } + + // Report if there were too many traps at the current method and bci. + // Report if a trap was recorded, and/or PerMethodTrapLimit was exceeded. + // If there is no MDO at all, report no trap unless told to assume it. + bool too_many_traps(Deoptimization::DeoptReason reason) { + return C->too_many_traps(method(), bci(), reason); + } + + // Report if there were too many recompiles at the current method and bci. + bool too_many_recompiles(Deoptimization::DeoptReason reason) { + return C->too_many_recompiles(method(), bci(), reason); + } + + // vanilla/CMS post barrier + void write_barrier_post(Node *store, Node* obj, Node* adr, Node* val, bool use_precise); + + // Returns the object (if any) which was created the moment before. + Node* just_allocated_object(Node* current_control); + + static bool use_ReduceInitialCardMarks() { + return (ReduceInitialCardMarks + && Universe::heap()->can_elide_tlab_store_barriers()); + } + + // Helper function to round double arguments before a call + void round_double_arguments(ciMethod* dest_method); + void round_double_result(ciMethod* dest_method); + + // rounding for strict float precision conformance + Node* precision_rounding(Node* n); + + // rounding for strict double precision conformance + Node* dprecision_rounding(Node* n); + + // rounding for non-strict double stores + Node* dstore_rounding(Node* n); + + // Helper functions for fast/slow path codes + Node* opt_iff(Node* region, Node* iff); + Node* make_runtime_call(int flags, + const TypeFunc* call_type, address call_addr, + const char* call_name, + const TypePtr* adr_type, // NULL if no memory effects + Node* parm0 = NULL, Node* parm1 = NULL, + Node* parm2 = NULL, Node* parm3 = NULL, + Node* parm4 = NULL, Node* parm5 = NULL, + Node* parm6 = NULL, Node* parm7 = NULL); + enum { // flag values for make_runtime_call + RC_NO_FP = 1, // CallLeafNoFPNode + RC_NO_IO = 2, // do not hook IO edges + RC_NO_LEAF = 4, // CallStaticJavaNode + RC_MUST_THROW = 8, // flag passed to add_safepoint_edges + RC_NARROW_MEM = 16, // input memory is same as output + RC_UNCOMMON = 32, // freq. expected to be like uncommon trap + RC_LEAF = 0 // null value: no flags set + }; + + // merge in all memory slices from new_mem, along the given path + void merge_memory(Node* new_mem, Node* region, int new_path); + void make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool separate_io_proj); + + // Helper functions to build synchronizations + int next_monitor(); + Node* insert_mem_bar(int opcode, Node* precedent = NULL); + Node* insert_mem_bar_volatile(int opcode, int alias_idx, Node* precedent = NULL); + // Optional 'precedent' is appended as an extra edge, to force ordering. + FastLockNode* shared_lock(Node* obj); + void shared_unlock(Node* box, Node* obj); + + // helper functions for the fast path/slow path idioms + Node* fast_and_slow(Node* in, const Type *result_type, Node* null_result, IfNode* fast_test, Node* fast_result, address slow_call, const TypeFunc *slow_call_type, Node* slow_arg, klassOop ex_klass, Node* slow_result); + + // Generate an instance-of idiom. Used by both the instance-of bytecode + // and the reflective instance-of call. + Node* gen_instanceof( Node *subobj, Node* superkls ); + + // Generate a check-cast idiom. Used by both the check-cast bytecode + // and the array-store bytecode + Node* gen_checkcast( Node *subobj, Node* superkls, + Node* *failure_control = NULL ); + + // Generate a subtyping check. Takes as input the subtype and supertype. + // Returns 2 values: sets the default control() to the true path and + // returns the false path. Only reads from constant memory taken from the + // default memory; does not write anything. It also doesn't take in an + // Object; if you wish to check an Object you need to load the Object's + // class prior to coming here. + Node* gen_subtype_check(Node* subklass, Node* superklass); + + // Static parse-time type checking logic for gen_subtype_check: + enum { SSC_always_false, SSC_always_true, SSC_easy_test, SSC_full_test }; + int static_subtype_check(ciKlass* superk, ciKlass* subk); + + // Exact type check used for predicted calls and casts. + // Rewrites (*casted_receiver) to be casted to the stronger type. + // (Caller is responsible for doing replace_in_map.) + Node* type_check_receiver(Node* receiver, ciKlass* klass, float prob, + Node* *casted_receiver); + + // implementation of object creation + Node* set_output_for_allocation(AllocateNode* alloc, + const TypeOopPtr* oop_type, + bool raw_mem_only); + Node* get_layout_helper(Node* klass_node, jint& constant_value); + Node* new_instance(Node* klass_node, + Node* slow_test = NULL, + bool raw_mem_only = false, + Node* *return_size_val = NULL); + Node* new_array(Node* klass_node, Node* count_val, + bool raw_mem_only = false, Node* *return_size_val = NULL); + + // Handy for making control flow + IfNode* create_and_map_if(Node* ctrl, Node* tst, float prob, float cnt) { + IfNode* iff = new (C, 2) IfNode(ctrl, tst, prob, cnt);// New IfNode's + _gvn.set_type(iff, iff->Value(&_gvn)); // Value may be known at parse-time + // Place 'if' on worklist if it will be in graph + if (!tst->is_Con()) record_for_igvn(iff); // Range-check and Null-check removal is later + return iff; + } + + IfNode* create_and_xform_if(Node* ctrl, Node* tst, float prob, float cnt) { + IfNode* iff = new (C, 2) IfNode(ctrl, tst, prob, cnt);// New IfNode's + _gvn.transform(iff); // Value may be known at parse-time + // Place 'if' on worklist if it will be in graph + if (!tst->is_Con()) record_for_igvn(iff); // Range-check and Null-check removal is later + return iff; + } +}; + +// Helper class to support building of control flow branches. Upon +// creation the map and sp at bci are cloned and restored upon de- +// struction. Typical use: +// +// { PreserveJVMState pjvms(this); +// // code of new branch +// } +// // here the JVM state at bci is established + +class PreserveJVMState: public StackObj { + protected: + GraphKit* _kit; +#ifdef ASSERT + int _block; // PO of current block, if a Parse + int _bci; +#endif + SafePointNode* _map; + uint _sp; + + public: + PreserveJVMState(GraphKit* kit, bool clone_map = true); + ~PreserveJVMState(); +}; + +// Helper class to build cutouts of the form if (p) ; else {x...}. +// The code {x...} must not fall through. +// The kit's main flow of control is set to the "then" continuation of if(p). +class BuildCutout: public PreserveJVMState { + public: + BuildCutout(GraphKit* kit, Node* p, float prob, float cnt = COUNT_UNKNOWN); + ~BuildCutout(); +}; diff --git a/hotspot/src/share/vm/opto/idealGraphPrinter.cpp b/hotspot/src/share/vm/opto/idealGraphPrinter.cpp new file mode 100644 index 00000000000..e65cc72b43f --- /dev/null +++ b/hotspot/src/share/vm/opto/idealGraphPrinter.cpp @@ -0,0 +1,1919 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_idealGraphPrinter.cpp.incl" + +#ifndef PRODUCT + +// Constants +// Keep consistent with Java constants +const char *IdealGraphPrinter::INDENT = " "; +const char *IdealGraphPrinter::TOP_ELEMENT = "graphDocument"; +const char *IdealGraphPrinter::GROUP_ELEMENT = "group"; +const char *IdealGraphPrinter::GRAPH_ELEMENT = "graph"; +const char *IdealGraphPrinter::PROPERTIES_ELEMENT = "properties"; +const char *IdealGraphPrinter::EDGES_ELEMENT = "edges"; +const char *IdealGraphPrinter::PROPERTY_ELEMENT = "p"; +const char *IdealGraphPrinter::EDGE_ELEMENT = "edge"; +const char *IdealGraphPrinter::NODE_ELEMENT = "node"; +const char *IdealGraphPrinter::NODES_ELEMENT = "nodes"; +const char *IdealGraphPrinter::REMOVE_EDGE_ELEMENT = "removeEdge"; +const char *IdealGraphPrinter::REMOVE_NODE_ELEMENT = "removeNode"; +const char *IdealGraphPrinter::METHOD_NAME_PROPERTY = "name"; +const char *IdealGraphPrinter::METHOD_IS_PUBLIC_PROPERTY = "public"; +const char *IdealGraphPrinter::METHOD_IS_STATIC_PROPERTY = "static"; +const char *IdealGraphPrinter::TRUE_VALUE = "true"; +const char *IdealGraphPrinter::NODE_NAME_PROPERTY = "name"; +const char *IdealGraphPrinter::EDGE_NAME_PROPERTY = "name"; +const char *IdealGraphPrinter::NODE_ID_PROPERTY = "id"; +const char *IdealGraphPrinter::FROM_PROPERTY = "from"; +const char *IdealGraphPrinter::TO_PROPERTY = "to"; +const char *IdealGraphPrinter::PROPERTY_NAME_PROPERTY = "name"; +const char *IdealGraphPrinter::GRAPH_NAME_PROPERTY = "name"; +const char *IdealGraphPrinter::INDEX_PROPERTY = "index"; +const char *IdealGraphPrinter::METHOD_ELEMENT = "method"; +const char *IdealGraphPrinter::INLINE_ELEMENT = "inline"; +const char *IdealGraphPrinter::BYTECODES_ELEMENT = "bytecodes"; +const char *IdealGraphPrinter::METHOD_BCI_PROPERTY = "bci"; +const char *IdealGraphPrinter::METHOD_SHORT_NAME_PROPERTY = "shortName"; +const char *IdealGraphPrinter::CONTROL_FLOW_ELEMENT = "controlFlow"; +const char *IdealGraphPrinter::BLOCK_NAME_PROPERTY = "name"; +const char *IdealGraphPrinter::BLOCK_DOMINATOR_PROPERTY = "dom"; +const char *IdealGraphPrinter::BLOCK_ELEMENT = "block"; +const char *IdealGraphPrinter::SUCCESSORS_ELEMENT = "successors"; +const char *IdealGraphPrinter::SUCCESSOR_ELEMENT = "successor"; +const char *IdealGraphPrinter::ASSEMBLY_ELEMENT = "assembly"; + +int IdealGraphPrinter::_file_count = 0; + +IdealGraphPrinter *IdealGraphPrinter::printer() { + if (PrintIdealGraphLevel == 0) return NULL; + + JavaThread *thread = JavaThread::current(); + if (!thread->is_Compiler_thread()) return NULL; + + CompilerThread *compiler_thread = (CompilerThread *)thread; + if (compiler_thread->ideal_graph_printer() == NULL) { + IdealGraphPrinter *printer = new IdealGraphPrinter(); + compiler_thread->set_ideal_graph_printer(printer); + } + + return compiler_thread->ideal_graph_printer(); +} + +void IdealGraphPrinter::clean_up() { + JavaThread *p; + for (p = Threads::first(); p; p = p->next()) { + if (p->is_Compiler_thread()) { + CompilerThread *c = (CompilerThread *)p; + IdealGraphPrinter *printer = c->ideal_graph_printer(); + if (printer) { + delete printer; + } + c->set_ideal_graph_printer(NULL); + } + } +} + +// Constructor, either file or network output +IdealGraphPrinter::IdealGraphPrinter() { + + _traverse_outs = false; + _should_send_method = true; + _output = NULL; + buffer[0] = 0; + _depth = 0; + _current_method = NULL; + assert(!_current_method, "current method must be initialized to NULL"); + _arena = new Arena(); + + _stream = new (ResourceObj::C_HEAP) networkStream(); + + if (PrintIdealGraphFile != NULL) { + ThreadCritical tc; + // User wants all output to go to files + if (_file_count != 0) { + ResourceMark rm; + stringStream st; + const char* dot = strrchr(PrintIdealGraphFile, '.'); + if (dot) { + st.write(PrintIdealGraphFile, dot - PrintIdealGraphFile); + st.print("%d%s", _file_count, dot); + } else { + st.print("%s%d", PrintIdealGraphFile, _file_count); + } + _output = new (ResourceObj::C_HEAP) fileStream(st.as_string()); + } else { + _output = new (ResourceObj::C_HEAP) fileStream(PrintIdealGraphFile); + } + _file_count++; + } else { + // Try to connect to visualizer + if (_stream->connect(PrintIdealGraphAddress, PrintIdealGraphPort)) { + char c = 0; + _stream->read(&c, 1); + if (c != 'y') { + tty->print_cr("Client available, but does not want to receive data!"); + _stream->close(); + delete _stream; + _stream = NULL; + return; + } + _output = _stream; + } else { + // It would be nice if we could shut down cleanly but it should + // be an error if we can't connect to the visualizer. + fatal2("Couldn't connect to visualizer at %s:%d", PrintIdealGraphAddress, PrintIdealGraphPort); + } + } + + start_element(TOP_ELEMENT); +} + +// Destructor, close file or network stream +IdealGraphPrinter::~IdealGraphPrinter() { + + end_element(TOP_ELEMENT); + + if (_stream) { + delete _stream; + if (_stream == _output) { + _output = NULL; + } + _stream = NULL; + } + + if (_output) { + delete _output; + _output = NULL; + } +} + +void IdealGraphPrinter::print_ifg(PhaseIFG* ifg) { + + // Code to print an interference graph to tty, currently not used + + /* + if (!_current_method) return; + // Remove neighbor colors + + for (uint i = 0; i < ifg._maxlrg; i++) { + + IndexSet *s = ifg.neighbors(i); + IndexSetIterator elements(s); + uint neighbor; + while ((neighbor = elements.next()) != 0) { + tty->print_cr("Edge between %d and %d\n", i, neighbor); + } + } + + + for (uint i = 0; i < ifg._maxlrg; i++) { + LRG &l = ifg.lrgs(i); + if (l._def) { + OptoReg::Name name = l.reg(); + tty->print("OptoReg::dump: "); + OptoReg::dump(name); + tty->print_cr(""); + tty->print_cr("name=%d\n", name); + if (name) { + if (OptoReg::is_stack(name)) { + tty->print_cr("Stack number %d\n", OptoReg::reg2stack(name)); + + } else if (!OptoReg::is_valid(name)) { + tty->print_cr("BAD!!!"); + } else { + + if (OptoReg::is_reg(name)) { + tty->print_cr(OptoReg::regname(name)); + } else { + int x = 0; + } + } + int x = 0; + } + + if (l._def == NodeSentinel) { + tty->print("multiple mapping from %d: ", i); + for (int j=0; jlength(); j++) { + tty->print("%d ", l._defs->at(j)->_idx); + } + tty->print_cr(""); + } else { + tty->print_cr("mapping between %d and %d\n", i, l._def->_idx); + } + } + }*/ +} + +void IdealGraphPrinter::print_method(ciMethod *method, int bci, InlineTree *tree) { + + Properties properties; + stringStream str; + method->print_name(&str); + + stringStream shortStr; + method->print_short_name(&shortStr); + + + properties.add(new Property(METHOD_NAME_PROPERTY, str.as_string())); + properties.add(new Property(METHOD_SHORT_NAME_PROPERTY, shortStr.as_string())); + properties.add(new Property(METHOD_BCI_PROPERTY, bci)); + start_element(METHOD_ELEMENT, &properties); + + start_element(BYTECODES_ELEMENT); + output()->print_cr("print_codes_on(output()); + output()->print_cr("]]>"); + end_element(BYTECODES_ELEMENT); + + start_element(INLINE_ELEMENT); + if (tree != NULL) { + GrowableArray subtrees = tree->subtrees(); + for (int i = 0; i < subtrees.length(); i++) { + print_inline_tree(subtrees.at(i)); + } + } + end_element(INLINE_ELEMENT); + + end_element(METHOD_ELEMENT); + output()->flush(); +} + +void IdealGraphPrinter::print_inline_tree(InlineTree *tree) { + + if (tree == NULL) return; + + ciMethod *method = tree->method(); + print_method(tree->method(), tree->caller_bci(), tree); + +} + +void IdealGraphPrinter::clear_nodes() { + // for (int i = 0; i < _nodes.length(); i++) { + // _nodes.at(i)->clear_node(); + // } +} + +void IdealGraphPrinter::print_inlining(Compile* compile) { + + // Print inline tree + if (_should_send_method) { + InlineTree *inlineTree = compile->ilt(); + if (inlineTree != NULL) { + print_inline_tree(inlineTree); + } else { + // print this method only + } + } +} + +// Has to be called whenever a method is compiled +void IdealGraphPrinter::begin_method(Compile* compile) { + + ciMethod *method = compile->method(); + assert(_output, "output stream must exist!"); + assert(method, "null methods are not allowed!"); + assert(!_current_method, "current method must be null!"); + + _arena->destruct_contents(); + + start_element(GROUP_ELEMENT); + + // Print properties + Properties properties; + + // Add method name + stringStream strStream; + method->print_name(&strStream); + properties.add(new Property(METHOD_NAME_PROPERTY, strStream.as_string())); + + if (method->flags().is_public()) { + properties.add(new Property(METHOD_IS_PUBLIC_PROPERTY, TRUE_VALUE)); + } + + if (method->flags().is_static()) { + properties.add(new Property(METHOD_IS_STATIC_PROPERTY, TRUE_VALUE)); + } + + properties.print(this); + + if (_stream) { + char answer = 0; + _stream->flush(); + int result = _stream->read(&answer, 1); + _should_send_method = (answer == 'y'); + } + + this->_nodes = GrowableArray(_arena, 2, 0, NULL); + this->_edges = GrowableArray< EdgeDescription * >(_arena, 2, 0, NULL); + + + this->_current_method = method; + + + + _output->flush(); +} + +// Has to be called whenever a method has finished compilation +void IdealGraphPrinter::end_method() { + +// if (finish && !in_method) return; + + nmethod* method = (nmethod*)this->_current_method->code(); + + start_element(ASSEMBLY_ELEMENT); + // Disassembler::decode(method, _output); + end_element(ASSEMBLY_ELEMENT); + + + end_element(GROUP_ELEMENT); + _current_method = NULL; + _output->flush(); + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc) { + delete desc; + _nodes.at_put(i, NULL); + } + } + this->_nodes.clear(); + + + for (int i = 0; i < _edges.length(); i++) { + // for (int j=0; j<_edges.at(i)->length(); j++) { + EdgeDescription *conn = _edges.at(i); + conn->print(this); + if (conn) { + delete conn; + _edges.at_put(i, NULL); + } + //} + //_edges.at(i)->clear(); + //delete _edges.at(i); + //_edges.at_put(i, NULL); + } + this->_edges.clear(); + +// in_method = false; +} + +// Outputs an XML start element +void IdealGraphPrinter::start_element(const char *s, Properties *properties /* = NULL */, bool print_indent /* = false */, bool print_return /* = true */) { + + start_element_helper(s, properties, false, print_indent, print_return); + _depth++; + +} + +// Outputs an XML start element without body +void IdealGraphPrinter::simple_element(const char *s, Properties *properties /* = NULL */, bool print_indent /* = false */) { + start_element_helper(s, properties, true, print_indent, true); +} + +// Outputs an XML start element. If outputEnd is true, the element has no body. +void IdealGraphPrinter::start_element_helper(const char *s, Properties *properties, bool outputEnd, bool print_indent /* = false */, bool print_return /* = true */) { + + assert(_output, "output stream must exist!"); + + if (print_indent) this->print_indent(); + _output->print("<"); + _output->print(s); + if (properties) properties->print_as_attributes(this); + + if (outputEnd) { + _output->print("/"); + } + + _output->print(">"); + if (print_return) _output->print_cr(""); + +} + +// Print indent +void IdealGraphPrinter::print_indent() { + for (int i = 0; i < _depth; i++) { + _output->print(INDENT); + } +} + +// Outputs an XML end element +void IdealGraphPrinter::end_element(const char *s, bool print_indent /* = true */, bool print_return /* = true */) { + + assert(_output, "output stream must exist!"); + + _depth--; + + if (print_indent) this->print_indent(); + _output->print("print(s); + _output->print(">"); + if (print_return) _output->print_cr(""); + +} + +bool IdealGraphPrinter::traverse_outs() { + return _traverse_outs; +} + +void IdealGraphPrinter::set_traverse_outs(bool b) { + _traverse_outs = b; +} + +void IdealGraphPrinter::walk(Node *start) { + + + VectorSet visited(Thread::current()->resource_area()); + GrowableArray nodeStack(Thread::current()->resource_area(), 0, 0, NULL); + nodeStack.push(start); + visited.test_set(start->_idx); + while(nodeStack.length() > 0) { + + Node *n = nodeStack.pop(); + IdealGraphPrinter::pre_node(n, this); + + if (_traverse_outs) { + for (DUIterator i = n->outs(); n->has_out(i); i++) { + Node* p = n->out(i); + if (!visited.test_set(p->_idx)) { + nodeStack.push(p); + } + } + } + + for ( uint i = 0; i < n->len(); i++ ) { + if ( n->in(i) ) { + if (!visited.test_set(n->in(i)->_idx)) { + nodeStack.push(n->in(i)); + } + } + } + } +} + +void IdealGraphPrinter::compress(int index, GrowableArray* blocks) { + Block *block = blocks->adr_at(index); + + int ancestor = block->ancestor(); + assert(ancestor != -1, ""); + + Block *ancestor_block = blocks->adr_at(ancestor); + if (ancestor_block->ancestor() != -1) { + compress(ancestor, blocks); + + int label = block->label(); + Block *label_block = blocks->adr_at(label); + + int ancestor_label = ancestor_block->label(); + Block *ancestor_label_block = blocks->adr_at(label); + if (ancestor_label_block->semi() < label_block->semi()) { + block->set_label(ancestor_label); + } + + block->set_ancestor(ancestor_block->ancestor()); + } +} + +int IdealGraphPrinter::eval(int index, GrowableArray* blocks) { + Block *block = blocks->adr_at(index); + if (block->ancestor() == -1) { + return index; + } else { + compress(index, blocks); + return block->label(); + } +} + +void IdealGraphPrinter::link(int index1, int index2, GrowableArray* blocks) { + Block *block2 = blocks->adr_at(index2); + block2->set_ancestor(index1); +} + +void IdealGraphPrinter::build_dominators(GrowableArray* blocks) { + + if (blocks->length() == 0) return; + + GrowableArray stack; + stack.append(0); + + GrowableArray array; + + assert(blocks->length() > 0, ""); + blocks->adr_at(0)->set_dominator(0); + + int n = 0; + while(!stack.is_empty()) { + int index = stack.pop(); + Block *block = blocks->adr_at(index); + block->set_semi(n); + array.append(block); + n = n + 1; + for (int i = 0; i < block->succs()->length(); i++) { + int succ_index = block->succs()->at(i); + Block *succ = blocks->adr_at(succ_index); + if (succ->semi() == -1) { + succ->set_parent(index); + stack.push(succ_index); + } + succ->add_pred(index); + } + } + + for (int i=n-1; i>0; i--) { + Block *block = array.at(i); + int block_index = block->index(); + for (int j=0; jpred()->length(); j++) { + int pred_index = block->pred()->at(j); + int cur_index = eval(pred_index, blocks); + + Block *cur_block = blocks->adr_at(cur_index); + if (cur_block->semi() < block->semi()) { + block->set_semi(cur_block->semi()); + } + } + + int semi_index = block->semi(); + Block *semi_block = array.at(semi_index); + semi_block->add_to_bucket(block_index); + + link(block->parent(), block_index, blocks); + Block *parent_block = blocks->adr_at(block->parent()); + + for (int j=0; jbucket()->length(); j++) { + int cur_index = parent_block->bucket()->at(j); + int new_index = eval(cur_index, blocks); + Block *cur_block = blocks->adr_at(cur_index); + Block *new_block = blocks->adr_at(new_index); + int dom = block->parent(); + + if (new_block->semi() < cur_block->semi()) { + dom = new_index; + } + + cur_block->set_dominator(dom); + } + + parent_block->clear_bucket(); + } + + for (int i=1; i < n; i++) { + + Block *block = array.at(i); + int block_index = block->index(); + + int semi_index = block->semi(); + Block *semi_block = array.at(semi_index); + + if (block->dominator() != semi_block->index()) { + int new_dom = blocks->adr_at(block->dominator())->dominator(); + block->set_dominator(new_dom); + } + } + + for (int i = 0; i < blocks->length(); i++) { + if (blocks->adr_at(i)->dominator() == -1) { + blocks->adr_at(i)->set_dominator(0); + } + } + + // Build dominates array + for (int i=1; i < blocks->length(); i++) { + Block *block = blocks->adr_at(i); + int dominator = block->dominator(); + Block *dom_block = blocks->adr_at(dominator); + dom_block->add_dominates(i); + dom_block->add_child(i); + + while(dominator != 0) { + dominator = dom_block->dominator(); + dom_block = blocks->adr_at(dominator); + dom_block->add_child(i); + } + } +} + +void IdealGraphPrinter::build_common_dominator(int **common_dominator, int index, GrowableArray* blocks) { + + common_dominator[index][index] = index; + Block *block = blocks->adr_at(index); + for (int i = 0; i < block->dominates()->length(); i++) { + Block *dominated = blocks->adr_at(block->dominates()->at(i)); + + for (int j=0; jchildren()->length(); j++) { + Block *child = blocks->adr_at(dominated->children()->at(j)); + common_dominator[index][child->index()] = common_dominator[child->index()][index] = index; + + for (int k=0; kadr_at(block->dominates()->at(k)); + common_dominator[child->index()][other_dominated->index()] = common_dominator[other_dominated->index()][child->index()] = index; + + for (int l=0 ; lchildren()->length(); l++) { + Block *other_child = blocks->adr_at(other_dominated->children()->at(l)); + common_dominator[child->index()][other_child->index()] = common_dominator[other_child->index()][child->index()] = index; + } + } + } + + build_common_dominator(common_dominator, dominated->index(), blocks); + } +} + +void IdealGraphPrinter::schedule_latest(int **common_dominator, GrowableArray* blocks) { + + int queue_size = _nodes.length() + 1; + NodeDescription **queue = NEW_RESOURCE_ARRAY(NodeDescription *, queue_size); + int queue_start = 0; + int queue_end = 0; + Arena *a = new Arena(); + VectorSet on_queue(a); + + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc) { + desc->init_succs(); + } + } + + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc) { + for (uint j=0; jnode()->len(); j++) { + Node *n = desc->node()->in(j); + if (n) { + NodeDescription *other_desc = _nodes.at(n->_idx); + other_desc->add_succ(desc); + } + } + } + } + + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc && desc->block_index() == -1) { + + // Put Phi into same block as region + if (desc->node()->is_Phi() && desc->node()->in(0) && _nodes.at(desc->node()->in(0)->_idx)->block_index() != -1) { + int index = _nodes.at(desc->node()->in(0)->_idx)->block_index(); + desc->set_block_index(index); + blocks->adr_at(index)->add_node(desc); + + // Put Projections to same block as parent + } else if (desc->node()->is_block_proj() && _nodes.at(desc->node()->is_block_proj()->_idx)->block_index() != -1) { + int index = _nodes.at(desc->node()->is_block_proj()->_idx)->block_index(); + desc->set_block_index(index); + blocks->adr_at(index)->add_node(desc); + } else { + queue[queue_end] = desc; + queue_end++; + on_queue.set(desc->node()->_idx); + } + } + } + + + int z = 0; + while(queue_start != queue_end && z < 10000) { + + NodeDescription *desc = queue[queue_start]; + queue_start = (queue_start + 1) % queue_size; + on_queue >>= desc->node()->_idx; + + Node* node = desc->node(); + + if (desc->succs()->length() == 0) { + int x = 0; + } + + int block_index = -1; + if (desc->succs()->length() != 0) { + for (int i = 0; i < desc->succs()->length(); i++) { + NodeDescription *cur_desc = desc->succs()->at(i); + if (cur_desc != desc) { + if (cur_desc->succs()->length() == 0) { + + // Ignore nodes with 0 successors + + } else if (cur_desc->block_index() == -1) { + + // Let this node schedule first + block_index = -1; + break; + + } else if (cur_desc->node()->is_Phi()){ + + // Special treatment for Phi functions + PhiNode *phi = cur_desc->node()->as_Phi(); + assert(phi->in(0) && phi->in(0)->is_Region(), "Must have region node in first input"); + RegionNode *region = phi->in(0)->as_Region(); + + for (uint j=1; jlen(); j++) { + Node *cur_phi_input = phi->in(j); + if (cur_phi_input == desc->node() && region->in(j)) { + NodeDescription *cur_region_input = _nodes.at(region->in(j)->_idx); + if (cur_region_input->block_index() == -1) { + + // Let this node schedule first + block_index = -1; + break; + } else { + if (block_index == -1) { + block_index = cur_region_input->block_index(); + } else { + block_index = common_dominator[block_index][cur_region_input->block_index()]; + } + } + } + } + + } else { + if (block_index == -1) { + block_index = cur_desc->block_index(); + } else { + block_index = common_dominator[block_index][cur_desc->block_index()]; + } + } + } + } + } + + if (block_index == -1) { + queue[queue_end] = desc; + queue_end = (queue_end + 1) % queue_size; + on_queue.set(desc->node()->_idx); + z++; + } else { + assert(desc->block_index() == -1, ""); + desc->set_block_index(block_index); + blocks->adr_at(block_index)->add_node(desc); + z = 0; + } + } + + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc && desc->block_index() == -1) { + + //if (desc->node()->is_Proj() || desc->node()->is_Con()) { + Node *parent = desc->node()->in(0); + uint cur = 1; + while(!parent && cur < desc->node()->len()) { + parent = desc->node()->in(cur); + cur++; + } + + if (parent && _nodes.at(parent->_idx)->block_index() != -1) { + int index = _nodes.at(parent->_idx)->block_index(); + desc->set_block_index(index); + blocks->adr_at(index)->add_node(desc); + } else { + desc->set_block_index(0); + blocks->adr_at(0)->add_node(desc); + //ShouldNotReachHere(); + } + //} + /* + if (desc->node()->is_block_proj() && _nodes.at(desc->node()->is_block_proj()->_idx)->block_index() != -1) { + int index = _nodes.at(desc->node()->is_block_proj()->_idx)->block_index(); + desc->set_block_index(index); + blocks->adr_at(index)->add_node(desc); + } */ + } + } + + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc) { + desc->clear_succs(); + } + } + + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc) { + int block_index = desc->block_index(); + + assert(block_index >= 0 && block_index < blocks->length(), "Block index must be in range"); + assert(blocks->adr_at(block_index)->nodes()->contains(desc), "Node must be child of block"); + } + } + a->destruct_contents(); +} + +void IdealGraphPrinter::build_blocks(Node *root) { + + Arena *a = new Arena(); + Node_Stack stack(a, 100); + + VectorSet visited(a); + stack.push(root, 0); + GrowableArray blocks(a, 2, 0, Block(0)); + + for (int i = 0; i < _nodes.length(); i++) { + if (_nodes.at(i)) _nodes.at(i)->set_block_index(-1); + } + + + // Order nodes such that node index is equal to idx + for (int i = 0; i < _nodes.length(); i++) { + + if (_nodes.at(i)) { + NodeDescription *node = _nodes.at(i); + int index = node->node()->_idx; + if (index != i) { + _nodes.at_grow(index); + NodeDescription *tmp = _nodes.at(index); + *(_nodes.adr_at(index)) = node; + *(_nodes.adr_at(i)) = tmp; + i--; + } + } + } + + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *node = _nodes.at(i); + if (node) { + assert(node->node()->_idx == (uint)i, ""); + } + } + + while(stack.is_nonempty()) { + + //Node *n = stack.node(); + //int index = stack.index(); + Node *proj = stack.node();//n->in(index); + const Node *parent = proj->is_block_proj(); + if (parent == NULL) { + parent = proj; + } + + if (!visited.test_set(parent->_idx)) { + + NodeDescription *end_desc = _nodes.at(parent->_idx); + int block_index = blocks.length(); + Block block(block_index); + blocks.append(block); + Block *b = blocks.adr_at(block_index); + b->set_start(end_desc); + // assert(end_desc->block_index() == -1, ""); + end_desc->set_block_index(block_index); + b->add_node(end_desc); + + // Skip any control-pinned middle'in stuff + Node *p = proj; + NodeDescription *start_desc = NULL; + do { + proj = p; // Update pointer to last Control + if (p->in(0) == NULL) { + start_desc = end_desc; + break; + } + p = p->in(0); // Move control forward + start_desc = _nodes.at(p->_idx); + assert(start_desc, ""); + + if (start_desc != end_desc && start_desc->block_index() == -1) { + assert(start_desc->block_index() == -1, ""); + assert(block_index < blocks.length(), ""); + start_desc->set_block_index(block_index); + b->add_node(start_desc); + } + } while( !p->is_block_proj() && + !p->is_block_start() ); + + for (uint i = 0; i < start_desc->node()->len(); i++) { + + Node *pred_node = start_desc->node()->in(i); + + + if (pred_node && pred_node != start_desc->node()) { + const Node *cur_parent = pred_node->is_block_proj(); + if (cur_parent != NULL) { + pred_node = (Node *)cur_parent; + } + + NodeDescription *pred_node_desc = _nodes.at(pred_node->_idx); + if (pred_node_desc->block_index() != -1) { + blocks.adr_at(pred_node_desc->block_index())->add_succ(block_index); + } + } + } + + for (DUIterator_Fast dmax, i = end_desc->node()->fast_outs(dmax); i < dmax; i++) { + Node* cur_succ = end_desc->node()->fast_out(i); + NodeDescription *cur_succ_desc = _nodes.at(cur_succ->_idx); + + DUIterator_Fast dmax2, i2 = cur_succ->fast_outs(dmax2); + if (cur_succ->is_block_proj() && i2 < dmax2 && !cur_succ->is_Root()) { + + for (; i2fast_out(i2); + if (cur_succ2) { + cur_succ_desc = _nodes.at(cur_succ2->_idx); + if (cur_succ_desc == NULL) { + // dead node so skip it + continue; + } + if (cur_succ2 != end_desc->node() && cur_succ_desc->block_index() != -1) { + b->add_succ(cur_succ_desc->block_index()); + } + } + } + + } else { + + if (cur_succ != end_desc->node() && cur_succ_desc && cur_succ_desc->block_index() != -1) { + b->add_succ(cur_succ_desc->block_index()); + } + } + } + + + int num_preds = p->len(); + int bottom = -1; + if (p->is_Region() || p->is_Phi()) { + bottom = 0; + } + + int pushed = 0; + for (int i=num_preds - 1; i > bottom; i--) { + if (p->in(i) != NULL && p->in(i) != p) { + stack.push(p->in(i), 0); + pushed++; + } + } + + if (pushed == 0 && p->is_Root() && !_matcher) { + // Special case when backedges to root are not yet built + for (int i = 0; i < _nodes.length(); i++) { + if (_nodes.at(i) && _nodes.at(i)->node()->is_SafePoint() && _nodes.at(i)->node()->outcnt() == 0) { + stack.push(_nodes.at(i)->node(), 0); + } + } + } + + } else { + stack.pop(); + } + } + + build_dominators(&blocks); + + int **common_dominator = NEW_RESOURCE_ARRAY(int *, blocks.length()); + for (int i = 0; i < blocks.length(); i++) { + int *cur = NEW_RESOURCE_ARRAY(int, blocks.length()); + common_dominator[i] = cur; + + for (int j=0; jadd_child(blocks.adr_at(i)->index()); + } + build_common_dominator(common_dominator, 0, &blocks); + + schedule_latest(common_dominator, &blocks); + + start_element(CONTROL_FLOW_ELEMENT); + + for (int i = 0; i < blocks.length(); i++) { + Block *block = blocks.adr_at(i); + + Properties props; + props.add(new Property(BLOCK_NAME_PROPERTY, i)); + props.add(new Property(BLOCK_DOMINATOR_PROPERTY, block->dominator())); + start_element(BLOCK_ELEMENT, &props); + + if (block->succs()->length() > 0) { + start_element(SUCCESSORS_ELEMENT); + for (int j=0; jsuccs()->length(); j++) { + int cur_index = block->succs()->at(j); + if (cur_index != 0 /* start_block has must not have inputs */) { + Properties properties; + properties.add(new Property(BLOCK_NAME_PROPERTY, cur_index)); + simple_element(SUCCESSOR_ELEMENT, &properties); + } + } + end_element(SUCCESSORS_ELEMENT); + } + + start_element(NODES_ELEMENT); + + for (int j=0; jnodes()->length(); j++) { + NodeDescription *n = block->nodes()->at(j); + Properties properties; + properties.add(new Property(NODE_ID_PROPERTY, n->id())); + simple_element(NODE_ELEMENT, &properties); + } + + end_element(NODES_ELEMENT); + + end_element(BLOCK_ELEMENT); + } + + + end_element(CONTROL_FLOW_ELEMENT); + + a->destruct_contents(); +} + +void IdealGraphPrinter::print_method(Compile* compile, const char *name, int level, bool clear_nodes) { + print(compile, name, (Node *)compile->root(), level, clear_nodes); +} + +// Print current ideal graph +void IdealGraphPrinter::print(Compile* compile, const char *name, Node *node, int level, bool clear_nodes) { + +// if (finish && !in_method) return; + if (!_current_method || !_should_send_method || level > PrintIdealGraphLevel) return; + + assert(_current_method, "newMethod has to be called first!"); + + if (clear_nodes) { + int x = 0; + } + + _clear_nodes = clear_nodes; + + // Warning, unsafe cast? + _chaitin = (PhaseChaitin *)compile->regalloc(); + _matcher = compile->matcher(); + + + // Update nodes + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc) { + desc->set_state(Invalid); + } + } + Node *n = node; + walk(n); + + // Update edges + for (int i = 0; i < _edges.length(); i++) { + _edges.at(i)->set_state(Invalid); + } + + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc && desc->state() != Invalid) { + + int to = desc->id(); + uint len = desc->node()->len(); + for (uint j=0; jnode()->in(j); + + if (n) { + + + intptr_t from = (intptr_t)n; + + // Assert from node is valid + /* + bool ok = false; + for (int k=0; k<_nodes.length(); k++) { + NodeDescription *desc = _nodes.at(k); + if (desc && desc->id() == from) { + assert(desc->state() != Invalid, ""); + ok = true; + } + } + assert(ok, "");*/ + + uint index = j; + if (index >= desc->node()->req()) { + index = desc->node()->req(); + } + + print_edge(from, to, index); + } + } + } + } + + bool is_different = false; + + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc && desc->state() != Valid) { + is_different = true; + break; + } + } + + if (!is_different) { + for (int i = 0; i < _edges.length(); i++) { + EdgeDescription *conn = _edges.at(i); + if (conn && conn->state() != Valid) { + is_different = true; + break; + } + } + } + + // No changes -> do not print graph + if (!is_different) return; + + Properties properties; + properties.add(new Property(GRAPH_NAME_PROPERTY, (const char *)name)); + start_element(GRAPH_ELEMENT, &properties); + + start_element(NODES_ELEMENT); + for (int i = 0; i < _nodes.length(); i++) { + NodeDescription *desc = _nodes.at(i); + if (desc) { + desc->print(this); + if (desc->state() == Invalid) { + delete desc; + _nodes.at_put(i, NULL); + } else { + desc->set_state(Valid); + } + } + } + end_element(NODES_ELEMENT); + + build_blocks(node); + + start_element(EDGES_ELEMENT); + for (int i = 0; i < _edges.length(); i++) { + EdgeDescription *conn = _edges.at(i); + + // Assert from and to nodes are valid + /* + if (!conn->state() == Invalid) { + bool ok1 = false; + bool ok2 = false; + for (int j=0; j<_nodes.length(); j++) { + NodeDescription *desc = _nodes.at(j); + if (desc && desc->id() == conn->from()) { + ok1 = true; + } + + if (desc && desc->id() == conn->to()) { + ok2 = true; + } + } + + assert(ok1, "from node not found!"); + assert(ok2, "to node not found!"); + }*/ + + conn->print(this); + if (conn->state() == Invalid) { + _edges.remove_at(i); + delete conn; + i--; + } + } + + end_element(EDGES_ELEMENT); + + end_element(GRAPH_ELEMENT); + + _output->flush(); +} + +// Print edge +void IdealGraphPrinter::print_edge(int from, int to, int index) { + + EdgeDescription *conn = new EdgeDescription(from, to, index); + for (int i = 0; i < _edges.length(); i++) { + if (_edges.at(i)->equals(conn)) { + conn->set_state(Valid); + delete _edges.at(i); + _edges.at_put(i, conn); + return; + } + } + + _edges.append(conn); +} + +extern const char *NodeClassNames[]; + +// Create node description +IdealGraphPrinter::NodeDescription *IdealGraphPrinter::create_node_description(Node* node) { + +#ifndef PRODUCT + node->_in_dump_cnt++; + NodeDescription *desc = new NodeDescription(node); + desc->properties()->add(new Property(NODE_NAME_PROPERTY, (const char *)node->Name())); + + const Type *t = node->bottom_type(); + desc->properties()->add(new Property("type", (const char *)Type::msg[t->base()])); + + desc->properties()->add(new Property("idx", node->_idx)); +#ifdef ASSERT + desc->properties()->add(new Property("debug_idx", node->_debug_idx)); +#endif + + + const jushort flags = node->flags(); + if (flags & Node::Flag_is_Copy) { + desc->properties()->add(new Property("is_copy", "true")); + } + if (flags & Node::Flag_is_Call) { + desc->properties()->add(new Property("is_call", "true")); + } + if (flags & Node::Flag_rematerialize) { + desc->properties()->add(new Property("rematerialize", "true")); + } + if (flags & Node::Flag_needs_anti_dependence_check) { + desc->properties()->add(new Property("needs_anti_dependence_check", "true")); + } + if (flags & Node::Flag_is_macro) { + desc->properties()->add(new Property("is_macro", "true")); + } + if (flags & Node::Flag_is_Con) { + desc->properties()->add(new Property("is_con", "true")); + } + if (flags & Node::Flag_is_cisc_alternate) { + desc->properties()->add(new Property("is_cisc_alternate", "true")); + } + if (flags & Node::Flag_is_Branch) { + desc->properties()->add(new Property("is_branch", "true")); + } + if (flags & Node::Flag_is_block_start) { + desc->properties()->add(new Property("is_block_start", "true")); + } + if (flags & Node::Flag_is_Goto) { + desc->properties()->add(new Property("is_goto", "true")); + } + if (flags & Node::Flag_is_dead_loop_safe) { + desc->properties()->add(new Property("is_dead_loop_safe", "true")); + } + if (flags & Node::Flag_may_be_short_branch) { + desc->properties()->add(new Property("may_be_short_branch", "true")); + } + if (flags & Node::Flag_is_safepoint_node) { + desc->properties()->add(new Property("is_safepoint_node", "true")); + } + if (flags & Node::Flag_is_pc_relative) { + desc->properties()->add(new Property("is_pc_relative", "true")); + } + + if (_matcher) { + if (_matcher->is_shared(desc->node())) { + desc->properties()->add(new Property("is_shared", "true")); + } else { + desc->properties()->add(new Property("is_shared", "false")); + } + + if (_matcher->is_dontcare(desc->node())) { + desc->properties()->add(new Property("is_dontcare", "true")); + } else { + desc->properties()->add(new Property("is_dontcare", "false")); + } + } + + if (node->is_Proj()) { + desc->properties()->add(new Property("con", (int)node->as_Proj()->_con)); + } + + if (node->is_Mach()) { + desc->properties()->add(new Property("idealOpcode", (const char *)NodeClassNames[node->as_Mach()->ideal_Opcode()])); + } + + + + + + outputStream *oldTty = tty; + buffer[0] = 0; + stringStream s2(buffer, sizeof(buffer) - 1); + + node->dump_spec(&s2); + assert(s2.size() < sizeof(buffer), "size in range"); + desc->properties()->add(new Property("dump_spec", buffer)); + + if (node->is_block_proj()) { + desc->properties()->add(new Property("is_block_proj", "true")); + } + + if (node->is_block_start()) { + desc->properties()->add(new Property("is_block_start", "true")); + } + + const char *short_name = "short_name"; + if (strcmp(node->Name(), "Parm") == 0 && node->as_Proj()->_con >= TypeFunc::Parms) { + int index = node->as_Proj()->_con - TypeFunc::Parms; + if (index >= 10) { + desc->properties()->add(new Property(short_name, "PA")); + } else { + sprintf(buffer, "P%d", index); + desc->properties()->add(new Property(short_name, buffer)); + } + } else if (strcmp(node->Name(), "IfTrue") == 0) { + desc->properties()->add(new Property(short_name, "T")); + } else if (strcmp(node->Name(), "IfFalse") == 0) { + desc->properties()->add(new Property(short_name, "F")); + } else if ((node->is_Con() && node->is_Type()) || node->is_Proj()) { + + if (t->base() == Type::Int && t->is_int()->is_con()) { + const TypeInt *typeInt = t->is_int(); + assert(typeInt->is_con(), "must be constant"); + jint value = typeInt->get_con(); + + // max. 2 chars allowed + if (value >= -9 && value <= 99) { + sprintf(buffer, "%d", value); + desc->properties()->add(new Property(short_name, buffer)); + } + else + { + desc->properties()->add(new Property(short_name, "I")); + } + } else if (t == Type::TOP) { + desc->properties()->add(new Property(short_name, "^")); + } else if (t->base() == Type::Long && t->is_long()->is_con()) { + const TypeLong *typeLong = t->is_long(); + assert(typeLong->is_con(), "must be constant"); + jlong value = typeLong->get_con(); + + // max. 2 chars allowed + if (value >= -9 && value <= 99) { + sprintf(buffer, "%d", value); + desc->properties()->add(new Property(short_name, buffer)); + } + else + { + desc->properties()->add(new Property(short_name, "L")); + } + } else if (t->base() == Type::KlassPtr) { + const TypeKlassPtr *typeKlass = t->is_klassptr(); + desc->properties()->add(new Property(short_name, "CP")); + } else if (t->base() == Type::Control) { + desc->properties()->add(new Property(short_name, "C")); + } else if (t->base() == Type::Memory) { + desc->properties()->add(new Property(short_name, "M")); + } else if (t->base() == Type::Abio) { + desc->properties()->add(new Property(short_name, "IO")); + } else if (t->base() == Type::Return_Address) { + desc->properties()->add(new Property(short_name, "RA")); + } else if (t->base() == Type::AnyPtr) { + desc->properties()->add(new Property(short_name, "P")); + } else if (t->base() == Type::RawPtr) { + desc->properties()->add(new Property(short_name, "RP")); + } else if (t->base() == Type::AryPtr) { + desc->properties()->add(new Property(short_name, "AP")); + } + } + + if (node->is_SafePoint()) { + SafePointNode *safePointNode = node->as_SafePoint(); + if (safePointNode->jvms()) { + stringStream bciStream; + bciStream.print("%d ", safePointNode->jvms()->bci()); + JVMState *caller = safePointNode->jvms()->caller(); + while(caller) { + bciStream.print("%d ", caller->bci()); + + caller = caller->caller(); + } + desc->properties()->add(new Property("bci", bciStream.as_string())); + } + } + + if (_chaitin && _chaitin != (PhaseChaitin *)0xdeadbeef) { + buffer[0] = 0; + _chaitin->dump_register(node, buffer); + desc->properties()->add(new Property("reg", buffer)); + desc->properties()->add(new Property("lrg", _chaitin->n2lidx(node))); + } + + + node->_in_dump_cnt--; + return desc; +#else + return NULL; +#endif +} + +void IdealGraphPrinter::pre_node(Node* node, void *env) { + + IdealGraphPrinter *printer = (IdealGraphPrinter *)env; + + NodeDescription *newDesc = printer->create_node_description(node); + + if (printer->_clear_nodes) { + + printer->_nodes.append(newDesc); + } else { + + NodeDescription *desc = printer->_nodes.at_grow(node->_idx, NULL); + + if (desc && desc->equals(newDesc)) { + //desc->set_state(Valid); + //desc->set_node(node); + delete desc; + printer->_nodes.at_put(node->_idx, NULL); + newDesc->set_state(Valid); + //printer->_nodes.at_put(node->_idx, newDesc); + } else { + + if (desc && desc->id() == newDesc->id()) { + delete desc; + printer->_nodes.at_put(node->_idx, NULL); + newDesc->set_state(New); + + } + + //if (desc) { + // delete desc; + //} + + //printer->_nodes.at_put(node->_idx, newDesc); + } + + printer->_nodes.append(newDesc); + } +} + +void IdealGraphPrinter::post_node(Node* node, void *env) { +} + +outputStream *IdealGraphPrinter::output() { + return _output; +} + +IdealGraphPrinter::Description::Description() { + _state = New; +} + +void IdealGraphPrinter::Description::print(IdealGraphPrinter *printer) { + if (_state == Invalid) { + print_removed(printer); + } else if (_state == New) { + print_changed(printer); + } +} + +void IdealGraphPrinter::Description::set_state(State s) { + _state = s; +} + +IdealGraphPrinter::State IdealGraphPrinter::Description::state() { + return _state; +} + +void IdealGraphPrinter::Block::set_proj(NodeDescription *n) { + _proj = n; +} + +void IdealGraphPrinter::Block::set_start(NodeDescription *n) { + _start = n; +} + +int IdealGraphPrinter::Block::semi() { + return _semi; +} + +int IdealGraphPrinter::Block::parent() { + return _parent; +} + +GrowableArray* IdealGraphPrinter::Block::bucket() { + return &_bucket; +} + +GrowableArray* IdealGraphPrinter::Block::children() { + return &_children; +} + +void IdealGraphPrinter::Block::add_child(int i) { + _children.append(i); +} + +GrowableArray* IdealGraphPrinter::Block::dominates() { + return &_dominates; +} + +void IdealGraphPrinter::Block::add_dominates(int i) { + _dominates.append(i); +} + +void IdealGraphPrinter::Block::add_to_bucket(int i) { + _bucket.append(i); +} + +void IdealGraphPrinter::Block::clear_bucket() { + _bucket.clear(); +} + +void IdealGraphPrinter::Block::set_dominator(int i) { + _dominator = i; +} + +void IdealGraphPrinter::Block::set_label(int i) { + _label = i; +} + +int IdealGraphPrinter::Block::label() { + return _label; +} + +int IdealGraphPrinter::Block::ancestor() { + return _ancestor; +} + +void IdealGraphPrinter::Block::set_ancestor(int i) { + _ancestor = i; +} + +int IdealGraphPrinter::Block::dominator() { + return _dominator; +} + +int IdealGraphPrinter::Block::index() { + return _index; +} + +void IdealGraphPrinter::Block::set_parent(int i) { + _parent = i; +} + +GrowableArray* IdealGraphPrinter::Block::pred() { + return &_pred; +} + +void IdealGraphPrinter::Block::set_semi(int i) { + _semi = i; +} + +IdealGraphPrinter::Block::Block() { +} + +IdealGraphPrinter::Block::Block(int index) { + _index = index; + _label = index; + _semi = -1; + _ancestor = -1; + _dominator = -1; +} + +void IdealGraphPrinter::Block::add_pred(int i) { + _pred.append(i); +} + +IdealGraphPrinter::NodeDescription *IdealGraphPrinter::Block::proj() { + return _proj; +} + +IdealGraphPrinter::NodeDescription *IdealGraphPrinter::Block::start() { + return _start; +} + +GrowableArray* IdealGraphPrinter::Block::succs() { + return &_succs; +} + +void IdealGraphPrinter::Block::add_succ(int index) { + + if (this->_index == 16 && index == 15) { + int x = 0; + } + + if (!_succs.contains(index)) { + _succs.append(index); + } +} + + +void IdealGraphPrinter::Block::add_node(NodeDescription *n) { + if (!_nodes.contains(n)) { + _nodes.append(n); + } +} + +GrowableArray* IdealGraphPrinter::Block::nodes() { + return &_nodes; +} + +int IdealGraphPrinter::NodeDescription::count = 0; + +IdealGraphPrinter::NodeDescription::NodeDescription(Node* node) : _node(node) { + _id = (intptr_t)(node); + _block_index = -1; +} + +IdealGraphPrinter::NodeDescription::~NodeDescription() { + _properties.clean(); +} + +// void IdealGraphPrinter::NodeDescription::set_node(Node* node) { +// //this->_node = node; +// } + +int IdealGraphPrinter::NodeDescription::block_index() { + return _block_index; +} + + +GrowableArray* IdealGraphPrinter::NodeDescription::succs() { + return &_succs; +} + +void IdealGraphPrinter::NodeDescription::clear_succs() { + _succs.clear(); +} + +void IdealGraphPrinter::NodeDescription::init_succs() { + _succs = GrowableArray(); +} + +void IdealGraphPrinter::NodeDescription::add_succ(NodeDescription *desc) { + _succs.append(desc); +} + +void IdealGraphPrinter::NodeDescription::set_block_index(int i) { + _block_index = i; +} + +bool IdealGraphPrinter::NodeDescription::equals(NodeDescription *desc) { + if (desc == NULL) return false; + if (desc->id() != id()) return false; + return properties()->equals(desc->properties()); +} + +Node* IdealGraphPrinter::NodeDescription::node() { + return _node; +} + +IdealGraphPrinter::Properties* IdealGraphPrinter::NodeDescription::properties() { + return &_properties; +} + +uint IdealGraphPrinter::NodeDescription::id() { + return _id; +} + +void IdealGraphPrinter::NodeDescription::print_changed(IdealGraphPrinter *printer) { + + + Properties properties; + properties.add(new Property(NODE_ID_PROPERTY, id())); + printer->start_element(NODE_ELEMENT, &properties); + + this->properties()->print(printer); + + + printer->end_element(NODE_ELEMENT); +} + +void IdealGraphPrinter::NodeDescription::print_removed(IdealGraphPrinter *printer) { + + Properties properties; + properties.add(new Property(NODE_ID_PROPERTY, id())); + printer->simple_element(REMOVE_NODE_ELEMENT, &properties); +} + +IdealGraphPrinter::EdgeDescription::EdgeDescription(int from, int to, int index) { + this->_from = from; + this->_to = to; + this->_index = index; +} + +IdealGraphPrinter::EdgeDescription::~EdgeDescription() { +} + +int IdealGraphPrinter::EdgeDescription::from() { + return _from; +} + +int IdealGraphPrinter::EdgeDescription::to() { + return _to; +} + +void IdealGraphPrinter::EdgeDescription::print_changed(IdealGraphPrinter *printer) { + + Properties properties; + properties.add(new Property(INDEX_PROPERTY, _index)); + properties.add(new Property(FROM_PROPERTY, _from)); + properties.add(new Property(TO_PROPERTY, _to)); + printer->simple_element(EDGE_ELEMENT, &properties); +} + +void IdealGraphPrinter::EdgeDescription::print_removed(IdealGraphPrinter *printer) { + + Properties properties; + properties.add(new Property(INDEX_PROPERTY, _index)); + properties.add(new Property(FROM_PROPERTY, _from)); + properties.add(new Property(TO_PROPERTY, _to)); + printer->simple_element(REMOVE_EDGE_ELEMENT, &properties); +} + +bool IdealGraphPrinter::EdgeDescription::equals(IdealGraphPrinter::EdgeDescription *desc) { + if (desc == NULL) return false; + return (_from == desc->_from && _to == desc->_to && _index == desc->_index); +} + +IdealGraphPrinter::Properties::Properties() : list(new (ResourceObj::C_HEAP) GrowableArray(2, 0, NULL, true)) { +} + +IdealGraphPrinter::Properties::~Properties() { + clean(); + delete list; +} + +void IdealGraphPrinter::Properties::add(Property *p) { + assert(p != NULL, "Property not NULL"); + list->append(p); +} + +void IdealGraphPrinter::Properties::print(IdealGraphPrinter *printer) { + printer->start_element(PROPERTIES_ELEMENT); + + for (int i = 0; i < list->length(); i++) { + list->at(i)->print(printer); + } + + printer->end_element(PROPERTIES_ELEMENT); +} + +void IdealGraphPrinter::Properties::clean() { + for (int i = 0; i < list->length(); i++) { + delete list->at(i); + list->at_put(i, NULL); + } + list->clear(); + assert(list->length() == 0, "List cleared"); +} + +void IdealGraphPrinter::Properties::remove(const char *name) { + for (int i = 0; i < list->length(); i++) { + if (strcmp(list->at(i)->name(), name) == 0) { + delete list->at(i); + list->remove_at(i); + i--; + } + } +} + +void IdealGraphPrinter::Properties::print_as_attributes(IdealGraphPrinter *printer) { + + for (int i = 0; i < list->length(); i++) { + assert(list->at(i) != NULL, "Property not null!"); + printer->output()->print(" "); + list->at(i)->print_as_attribute(printer); + } +} + +bool IdealGraphPrinter::Properties::equals(Properties* p) { + if (p->list->length() != this->list->length()) return false; + + for (int i = 0; i < list->length(); i++) { + assert(list->at(i) != NULL, "Property not null!"); + if (!list->at(i)->equals(p->list->at(i))) return false; + } + + return true; +} + +IdealGraphPrinter::Property::Property() { + _name = NULL; + _value = NULL; +} + +const char *IdealGraphPrinter::Property::name() { + return _name; +} + +IdealGraphPrinter::Property::Property(const Property* p) { + + this->_name = NULL; + this->_value = NULL; + + if (p->_name != NULL) { + _name = dup(p->_name); + } + + if (p->_value) { + _value = dup(p->_value); + } +} + +IdealGraphPrinter::Property::~Property() { + + clean(); +} + +IdealGraphPrinter::Property::Property(const char *name, const char *value) { + + assert(name, "Name must not be null!"); + assert(value, "Value must not be null!"); + + _name = dup(name); + _value = dup(value); +} + +IdealGraphPrinter::Property::Property(const char *name, int intValue) { + _name = dup(name); + + stringStream stream; + stream.print("%d", intValue); + _value = dup(stream.as_string()); +} + +void IdealGraphPrinter::Property::clean() { + if (_name) { + delete _name; + _name = NULL; + } + + if (_value) { + delete _value; + _value = NULL; + } +} + + +bool IdealGraphPrinter::Property::is_null() { + return _name == NULL; +} + +void IdealGraphPrinter::Property::print(IdealGraphPrinter *printer) { + + assert(!is_null(), "null properties cannot be printed!"); + Properties properties; + properties.add(new Property(PROPERTY_NAME_PROPERTY, _name)); + printer->start_element(PROPERTY_ELEMENT, &properties, false, false); + printer->print_xml(_value); + printer->end_element(PROPERTY_ELEMENT, false, true); +} + +void IdealGraphPrinter::Property::print_as_attribute(IdealGraphPrinter *printer) { + + printer->output()->print(_name); + printer->output()->print("=\""); + printer->print_xml(_value); + printer->output()->print("\""); +} + + +bool IdealGraphPrinter::Property::equals(Property* p) { + + if (is_null() && p->is_null()) return true; + if (is_null()) return false; + if (p->is_null()) return false; + + int cmp1 = strcmp(p->_name, _name); + if (cmp1 != 0) return false; + + int cmp2 = strcmp(p->_value, _value); + if (cmp2 != 0) return false; + + return true; +} + +void IdealGraphPrinter::print_xml(const char *value) { + size_t len = strlen(value); + + char buf[2]; + buf[1] = 0; + for (size_t i = 0; i < len; i++) { + char c = value[i]; + + switch(c) { + case '<': + output()->print("<"); + break; + + case '>': + output()->print(">"); + break; + + default: + buf[0] = c; + output()->print(buf); + break; + } + } +} + +#endif diff --git a/hotspot/src/share/vm/opto/idealGraphPrinter.hpp b/hotspot/src/share/vm/opto/idealGraphPrinter.hpp new file mode 100644 index 00000000000..b73493e195e --- /dev/null +++ b/hotspot/src/share/vm/opto/idealGraphPrinter.hpp @@ -0,0 +1,323 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef PRODUCT + +class Compile; +class PhaseIFG; +class PhaseChaitin; +class Matcher; +class Node; +class InlineTree; +class ciMethod; + +class IdealGraphPrinter +{ +private: + + enum State + { + Invalid, + Valid, + New + }; + +private: + + static const char *INDENT; + static const char *TOP_ELEMENT; + static const char *GROUP_ELEMENT; + static const char *GRAPH_ELEMENT; + static const char *PROPERTIES_ELEMENT; + static const char *EDGES_ELEMENT; + static const char *PROPERTY_ELEMENT; + static const char *EDGE_ELEMENT; + static const char *NODE_ELEMENT; + static const char *NODES_ELEMENT; + static const char *CONTROL_FLOW_ELEMENT; + static const char *REMOVE_EDGE_ELEMENT; + static const char *REMOVE_NODE_ELEMENT; + static const char *METHOD_NAME_PROPERTY; + static const char *BLOCK_NAME_PROPERTY; + static const char *BLOCK_DOMINATOR_PROPERTY; + static const char *BLOCK_ELEMENT; + static const char *SUCCESSORS_ELEMENT; + static const char *SUCCESSOR_ELEMENT; + static const char *METHOD_IS_PUBLIC_PROPERTY; + static const char *METHOD_IS_STATIC_PROPERTY; + static const char *TRUE_VALUE; + static const char *NODE_NAME_PROPERTY; + static const char *EDGE_NAME_PROPERTY; + static const char *NODE_ID_PROPERTY; + static const char *FROM_PROPERTY; + static const char *TO_PROPERTY; + static const char *PROPERTY_NAME_PROPERTY; + static const char *GRAPH_NAME_PROPERTY; + static const char *INDEX_PROPERTY; + static const char *METHOD_ELEMENT; + static const char *INLINE_ELEMENT; + static const char *BYTECODES_ELEMENT; + static const char *METHOD_BCI_PROPERTY; + static const char *METHOD_SHORT_NAME_PROPERTY; + static const char *ASSEMBLY_ELEMENT; + + class Property { + + private: + + const char *_name; + const char *_value; + + public: + + Property(); + Property(const Property* p); + ~Property(); + Property(const char *name, const char *value); + Property(const char *name, int value); + bool equals(Property* p); + void print(IdealGraphPrinter *printer); + void print_as_attribute(IdealGraphPrinter *printer); + bool is_null(); + void clean(); + const char *name(); + + static const char* dup(const char *str) { + char * copy = new char[strlen(str)+1]; + strcpy(copy, str); + return copy; + } + + }; + + class Properties { + + private: + + GrowableArray *list; + + public: + + Properties(); + ~Properties(); + void add(Property *p); + void remove(const char *name); + bool equals(Properties* p); + void print(IdealGraphPrinter *printer); + void print_as_attributes(IdealGraphPrinter *printer); + void clean(); + + }; + + + class Description { + + private: + + State _state; + + public: + + Description(); + + State state(); + void set_state(State s); + void print(IdealGraphPrinter *printer); + virtual void print_changed(IdealGraphPrinter *printer) = 0; + virtual void print_removed(IdealGraphPrinter *printer) = 0; + + }; + + class NodeDescription : public Description{ + + public: + + static int count; + + private: + + GrowableArray _succs; + int _block_index; + uintptr_t _id; + Properties _properties; + Node* _node; + + public: + + NodeDescription(Node* node); + ~NodeDescription(); + Node* node(); + + // void set_node(Node* node); + GrowableArray* succs(); + void init_succs(); + void clear_succs(); + void add_succ(NodeDescription *desc); + int block_index(); + void set_block_index(int i); + Properties* properties(); + virtual void print_changed(IdealGraphPrinter *printer); + virtual void print_removed(IdealGraphPrinter *printer); + bool equals(NodeDescription *desc); + uint id(); + + }; + + class Block { + + private: + + NodeDescription *_start; + NodeDescription *_proj; + GrowableArray _succs; + GrowableArray _nodes; + GrowableArray _dominates; + GrowableArray _children; + int _semi; + int _parent; + GrowableArray _pred; + GrowableArray _bucket; + int _index; + int _dominator; + int _ancestor; + int _label; + + public: + + Block(); + Block(int index); + + void add_node(NodeDescription *n); + GrowableArray* nodes(); + GrowableArray* children(); + void add_child(int i); + void add_succ(int index); + GrowableArray* succs(); + GrowableArray* dominates(); + void add_dominates(int i); + NodeDescription *start(); + NodeDescription *proj(); + void set_start(NodeDescription *n); + void set_proj(NodeDescription *n); + + int label(); + void set_label(int i); + int ancestor(); + void set_ancestor(int i); + int index(); + int dominator(); + void set_dominator(int i); + int parent(); + void set_parent(int i); + int semi(); + GrowableArray* bucket(); + void add_to_bucket(int i); + void clear_bucket(); + GrowableArray* pred(); + void set_semi(int i); + void add_pred(int i); + + }; + + class EdgeDescription : public Description { + + private: + + int _from; + int _to; + int _index; + public: + + EdgeDescription(int from, int to, int index); + ~EdgeDescription(); + + virtual void print_changed(IdealGraphPrinter *printer); + virtual void print_removed(IdealGraphPrinter *printer); + bool equals(EdgeDescription *desc); + int from(); + int to(); + }; + + + static int _file_count; + networkStream *_stream; + outputStream *_output; + ciMethod *_current_method; + GrowableArray _nodes; + GrowableArray _edges; + int _depth; + Arena *_arena; + char buffer[128]; + bool _should_send_method; + PhaseChaitin* _chaitin; + bool _clear_nodes; + Matcher* _matcher; + bool _traverse_outs; + + void start_element_helper(const char *name, Properties *properties, bool endElement, bool print_indent = false, bool print_return = true); + NodeDescription *create_node_description(Node* node); + + static void pre_node(Node* node, void *env); + static void post_node(Node* node, void *env); + + void schedule_latest(int **common_dominator, GrowableArray* blocks); + void build_common_dominator(int **common_dominator, int index, GrowableArray* blocks); + void compress(int index, GrowableArray* blocks); + int eval(int index, GrowableArray* blocks); + void link(int index1, int index2, GrowableArray* blocks); + void build_dominators(GrowableArray* blocks); + void build_blocks(Node *node); + void walk(Node *n); + void start_element(const char *name, Properties *properties = NULL, bool print_indent = false, bool print_return = true); + void simple_element(const char *name, Properties *properties = NULL, bool print_indent = false); + void end_element(const char *name, bool print_indent = false, bool print_return = true); + void print_edge(int from, int to, int index); + void print_indent(); + void print_method(ciMethod *method, int bci, InlineTree *tree); + void print_inline_tree(InlineTree *tree); + void clear_nodes(); + + IdealGraphPrinter(); + ~IdealGraphPrinter(); + +public: + + static void clean_up(); + static IdealGraphPrinter *printer(); + + bool traverse_outs(); + void set_traverse_outs(bool b); + void print_ifg(PhaseIFG* ifg); + outputStream *output(); + void print_inlining(Compile* compile); + void begin_method(Compile* compile); + void end_method(); + void print_method(Compile* compile, const char *name, int level=1, bool clear_nodes = false); + void print(Compile* compile, const char *name, Node *root, int level=1, bool clear_nodes = false); + void print_xml(const char *name); + + +}; + +#endif diff --git a/hotspot/src/share/vm/opto/idealKit.cpp b/hotspot/src/share/vm/opto/idealKit.cpp new file mode 100644 index 00000000000..ae65319f091 --- /dev/null +++ b/hotspot/src/share/vm/opto/idealKit.cpp @@ -0,0 +1,503 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_idealKit.cpp.incl" + +// Static initialization + +// This declares the position where vars are kept in the cvstate +// For some degree of consistency we use the TypeFunc enum to +// soak up spots in the inputs even though we only use early Control +// and Memory slots. (So far.) +const uint IdealKit::first_var = TypeFunc::Parms + 1; + +//----------------------------IdealKit----------------------------------------- +IdealKit::IdealKit(PhaseGVN &gvn, Node* control, Node* mem, bool delay_all_transforms) : + _gvn(gvn), C(gvn.C) { + _initial_ctrl = control; + _initial_memory = mem; + _delay_all_transforms = delay_all_transforms; + _var_ct = 0; + _cvstate = NULL; + // We can go memory state free or else we need the entire memory state + assert(mem == NULL || mem->Opcode() == Op_MergeMem, "memory must be pre-split"); + int init_size = 5; + _pending_cvstates = new (C->node_arena()) GrowableArray(C->node_arena(), init_size, 0, 0); + _delay_transform = new (C->node_arena()) GrowableArray(C->node_arena(), init_size, 0, 0); + DEBUG_ONLY(_state = new (C->node_arena()) GrowableArray(C->node_arena(), init_size, 0, 0)); +} + +//-------------------------------if_then------------------------------------- +// Create: if(left relop right) +// / \ +// iffalse iftrue +// Push the iffalse cvstate onto the stack. The iftrue becomes the current cvstate. +void IdealKit::if_then(Node* left, BoolTest::mask relop, + Node* right, float prob, float cnt, bool push_new_state) { + assert((state() & (BlockS|LoopS|IfThenS|ElseS)), "bad state for new If"); + Node* bol; + if (left->bottom_type()->isa_ptr() == NULL) { + if (left->bottom_type()->isa_int() != NULL) { + bol = Bool(CmpI(left, right), relop); + } else { + assert(left->bottom_type()->isa_long() != NULL, "what else?"); + bol = Bool(CmpL(left, right), relop); + } + + } else { + bol = Bool(CmpP(left, right), relop); + } + // Delay gvn.tranform on if-nodes until construction is finished + // to prevent a constant bool input from discarding a control output. + IfNode* iff = delay_transform(new (C, 2) IfNode(ctrl(), bol, prob, cnt))->as_If(); + Node* then = IfTrue(iff); + Node* elsen = IfFalse(iff); + Node* else_cvstate = copy_cvstate(); + else_cvstate->set_req(TypeFunc::Control, elsen); + _pending_cvstates->push(else_cvstate); + DEBUG_ONLY(if (push_new_state) _state->push(IfThenS)); + set_ctrl(then); +} + +//-------------------------------else_------------------------------------- +// Pop the else cvstate off the stack, and push the (current) then cvstate. +// The else cvstate becomes the current cvstate. +void IdealKit::else_() { + assert(state() == IfThenS, "bad state for new Else"); + Node* else_cvstate = _pending_cvstates->pop(); + DEBUG_ONLY(_state->pop()); + // save current (then) cvstate for later use at endif + _pending_cvstates->push(_cvstate); + DEBUG_ONLY(_state->push(ElseS)); + _cvstate = else_cvstate; +} + +//-------------------------------end_if------------------------------------- +// Merge the "then" and "else" cvstates. +// +// The if_then() pushed the current state for later use +// as the initial state for a future "else" clause. The +// current state then became the initial state for the +// then clause. If an "else" clause was encountered, it will +// pop the top state and use it for it's initial state. +// It will also push the current state (the state at the end of +// the "then" clause) for latter use at the end_if. +// +// At the endif, the states are: +// 1) else exists a) current state is end of "else" clause +// b) top stack state is end of "then" clause +// +// 2) no else: a) current state is end of "then" clause +// b) top stack state is from the "if_then" which +// would have been the initial state of the else. +// +// Merging the states is accomplished by: +// 1) make a label for the merge +// 2) terminate the current state with a goto to the label +// 3) pop the top state from the stack and make it the +// current state +// 4) bind the label at the current state. Binding a label +// terminates the current state with a goto to the +// label and makes the label's state the current state. +// +void IdealKit::end_if() { + assert(state() & (IfThenS|ElseS), "bad state for new Endif"); + Node* lab = make_label(1); + + // Node* join_state = _pending_cvstates->pop(); + /* merging, join */ + goto_(lab); + _cvstate = _pending_cvstates->pop(); + + bind(lab); + DEBUG_ONLY(_state->pop()); +} + +//-------------------------------loop------------------------------------- +// Create the loop head portion (*) of: +// * iv = init +// * top: (region node) +// * if (iv relop limit) { +// loop body +// i = i + 1 +// goto top +// * } else // exits loop +// +// Pushes the loop top cvstate first, then the else (loop exit) cvstate +// onto the stack. +void IdealKit::loop(IdealVariable& iv, Node* init, BoolTest::mask relop, Node* limit, float prob, float cnt) { + assert((state() & (BlockS|LoopS|IfThenS|ElseS)), "bad state for new loop"); + set(iv, init); + Node* head = make_label(1); + bind(head); + _pending_cvstates->push(head); // push for use at end_loop + _cvstate = copy_cvstate(); + if_then(value(iv), relop, limit, prob, cnt, false /* no new state */); + DEBUG_ONLY(_state->push(LoopS)); + assert(ctrl()->is_IfTrue(), "true branch stays in loop"); + assert(_pending_cvstates->top()->in(TypeFunc::Control)->is_IfFalse(), "false branch exits loop"); +} + +//-------------------------------end_loop------------------------------------- +// Creates the goto top label. +// Expects the else (loop exit) cvstate to be on top of the +// stack, and the loop top cvstate to be 2nd. +void IdealKit::end_loop() { + assert((state() == LoopS), "bad state for new end_loop"); + Node* exit = _pending_cvstates->pop(); + Node* head = _pending_cvstates->pop(); + goto_(head); + clear(head); + DEBUG_ONLY(_state->pop()); + _cvstate = exit; +} + +//-------------------------------make_label------------------------------------- +// Creates a label. The number of goto's +// must be specified (which should be 1 less than +// the number of precedessors.) +Node* IdealKit::make_label(int goto_ct) { + assert(_cvstate != NULL, "must declare variables before labels"); + Node* lab = new_cvstate(); + int sz = 1 + goto_ct + 1 /* fall thru */; + Node* reg = delay_transform(new (C, sz) RegionNode(sz)); + lab->init_req(TypeFunc::Control, reg); + return lab; +} + +//-------------------------------bind------------------------------------- +// Bind a label at the current cvstate by simulating +// a goto to the label. +void IdealKit::bind(Node* lab) { + goto_(lab, true /* bind */); + _cvstate = lab; +} + +//-------------------------------goto_------------------------------------- +// Make the current cvstate a predecessor of the label, +// creating phi's to merge values. If bind is true and +// this is not the last control edge, then ensure that +// all live values have phis created. Used to create phis +// at loop-top regions. +void IdealKit::goto_(Node* lab, bool bind) { + Node* reg = lab->in(TypeFunc::Control); + // find next empty slot in region + uint slot = 1; + while (slot < reg->req() && reg->in(slot) != NULL) slot++; + assert(slot < reg->req(), "too many gotos"); + // If this is last predecessor, then don't force phi creation + if (slot == reg->req() - 1) bind = false; + reg->init_req(slot, ctrl()); + assert(first_var + _var_ct == _cvstate->req(), "bad _cvstate size"); + for (uint i = first_var; i < _cvstate->req(); i++) { + + // l is the value of var reaching the label. Could be a single value + // reaching the label, or a phi that merges multiples values reaching + // the label. The latter is true if the label's input: in(..) is + // a phi whose control input is the region node for the label. + + Node* l = lab->in(i); + // Get the current value of the var + Node* m = _cvstate->in(i); + // If the var went unused no need for a phi + if (m == NULL) { + continue; + } else if (l == NULL || m == l) { + // Only one unique value "m" is known to reach this label so a phi + // is not yet necessary unless: + // the label is being bound and all predecessors have not been seen, + // in which case "bind" will be true. + if (bind) { + m = promote_to_phi(m, reg); + } + // Record the phi/value used for this var in the label's cvstate + lab->set_req(i, m); + } else { + // More than one value for the variable reaches this label so + // a create a phi if one does not already exist. + if (!was_promoted_to_phi(l, reg)) { + l = promote_to_phi(l, reg); + lab->set_req(i, l); + } + // Record in the phi, the var's value from the current state + l->set_req(slot, m); + } + } + do_memory_merge(_cvstate, lab); + stop(); +} + +//-----------------------------promote_to_phi----------------------------------- +Node* IdealKit::promote_to_phi(Node* n, Node* reg) { + assert(!was_promoted_to_phi(n, reg), "n already promoted to phi on this region"); + // Get a conservative type for the phi + const BasicType bt = n->bottom_type()->basic_type(); + const Type* ct = Type::get_const_basic_type(bt); + return delay_transform(PhiNode::make(reg, n, ct)); +} + +//-----------------------------declares_done----------------------------------- +void IdealKit::declares_done() { + _cvstate = new_cvstate(); // initialize current cvstate + set_ctrl(_initial_ctrl); // initialize control in current cvstate + set_all_memory(_initial_memory);// initialize memory in current cvstate + DEBUG_ONLY(_state->push(BlockS)); +} + +//-----------------------------transform----------------------------------- +Node* IdealKit::transform(Node* n) { + if (_delay_all_transforms) { + return delay_transform(n); + } else { + return gvn().transform(n); + } +} + +//-----------------------------delay_transform----------------------------------- +Node* IdealKit::delay_transform(Node* n) { + gvn().set_type(n, n->bottom_type()); + _delay_transform->push(n); + return n; +} + +//-----------------------------new_cvstate----------------------------------- +Node* IdealKit::new_cvstate() { + uint sz = _var_ct + first_var; + return new (C, sz) Node(sz); +} + +//-----------------------------copy_cvstate----------------------------------- +Node* IdealKit::copy_cvstate() { + Node* ns = new_cvstate(); + for (uint i = 0; i < ns->req(); i++) ns->init_req(i, _cvstate->in(i)); + // We must clone memory since it will be updated as we do stores. + ns->set_req(TypeFunc::Memory, MergeMemNode::make(C, ns->in(TypeFunc::Memory))); + return ns; +} + +//-----------------------------clear----------------------------------- +void IdealKit::clear(Node* m) { + for (uint i = 0; i < m->req(); i++) m->set_req(i, NULL); +} + +//-----------------------------drain_delay_transform---------------------------- +void IdealKit::drain_delay_transform() { + while (_delay_transform->length() > 0) { + Node* n = _delay_transform->pop(); + gvn().transform(n); + if (!gvn().is_IterGVN()) { + C->record_for_igvn(n); + } + } +} + +//-----------------------------IdealVariable---------------------------- +IdealVariable::IdealVariable(IdealKit &k) { + k.declare(this); +} + +Node* IdealKit::memory(uint alias_idx) { + MergeMemNode* mem = merged_memory(); + Node* p = mem->memory_at(alias_idx); + _gvn.set_type(p, Type::MEMORY); // must be mapped + return p; +} + +void IdealKit::set_memory(Node* mem, uint alias_idx) { + merged_memory()->set_memory_at(alias_idx, mem); +} + +//----------------------------- make_load ---------------------------- +Node* IdealKit::load(Node* ctl, + Node* adr, + const Type* t, + BasicType bt, + int adr_idx, + bool require_atomic_access) { + + assert(adr_idx != Compile::AliasIdxTop, "use other make_load factory" ); + const TypePtr* adr_type = NULL; // debug-mode-only argument + debug_only(adr_type = C->get_adr_type(adr_idx)); + Node* mem = memory(adr_idx); + Node* ld; + if (require_atomic_access && bt == T_LONG) { + ld = LoadLNode::make_atomic(C, ctl, mem, adr, adr_type, t); + } else { + ld = LoadNode::make(C, ctl, mem, adr, adr_type, t, bt); + } + return transform(ld); +} + +Node* IdealKit::store(Node* ctl, Node* adr, Node *val, BasicType bt, + int adr_idx, + bool require_atomic_access) { + assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); + const TypePtr* adr_type = NULL; + debug_only(adr_type = C->get_adr_type(adr_idx)); + Node *mem = memory(adr_idx); + Node* st; + if (require_atomic_access && bt == T_LONG) { + st = StoreLNode::make_atomic(C, ctl, mem, adr, adr_type, val); + } else { + st = StoreNode::make(C, ctl, mem, adr, adr_type, val, bt); + } + st = transform(st); + set_memory(st, adr_idx); + + return st; +} + +// Card mark store. Must be ordered so that it will come after the store of +// the oop. +Node* IdealKit::storeCM(Node* ctl, Node* adr, Node *val, Node* oop_store, + BasicType bt, + int adr_idx) { + assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); + const TypePtr* adr_type = NULL; + debug_only(adr_type = C->get_adr_type(adr_idx)); + Node *mem = memory(adr_idx); + + // Add required edge to oop_store, optimizer does not support precedence edges. + // Convert required edge to precedence edge before allocation. + Node* st = new (C, 5) StoreCMNode(ctl, mem, adr, adr_type, val, oop_store); + + st = transform(st); + set_memory(st, adr_idx); + + return st; +} + +//---------------------------- do_memory_merge -------------------------------- +// The memory from one merging cvstate needs to be merged with the memory for another +// join cvstate. If the join cvstate doesn't have a merged memory yet then we +// can just copy the state from the merging cvstate + +// Merge one slow path into the rest of memory. +void IdealKit::do_memory_merge(Node* merging, Node* join) { + + // Get the region for the join state + Node* join_region = join->in(TypeFunc::Control); + assert(join_region != NULL, "join region must exist"); + if (join->in(TypeFunc::Memory) == NULL ) { + join->set_req(TypeFunc::Memory, merging->in(TypeFunc::Memory)); + return; + } + + // The control flow for merging must have already been attached to the join region + // we need its index for the phis. + uint slot; + for (slot = 1; slot < join_region->req() ; slot ++ ) { + if (join_region->in(slot) == merging->in(TypeFunc::Control)) break; + } + assert(slot != join_region->req(), "edge must already exist"); + + MergeMemNode* join_m = join->in(TypeFunc::Memory)->as_MergeMem(); + MergeMemNode* merging_m = merging->in(TypeFunc::Memory)->as_MergeMem(); + + // join_m should be an ancestor mergemem of merging + // Slow path memory comes from the current map (which is from a slow call) + // Fast path/null path memory comes from the call's input + + // Merge the other fast-memory inputs with the new slow-default memory. + // for (MergeMemStream mms(merged_memory(), fast_mem->as_MergeMem()); mms.next_non_empty2(); ) { + for (MergeMemStream mms(join_m, merging_m); mms.next_non_empty2(); ) { + Node* join_slice = mms.force_memory(); + Node* merging_slice = mms.memory2(); + if (join_slice != merging_slice) { + PhiNode* phi; + // bool new_phi = false; + // Is the phi for this slice one that we created for this join region or simply + // one we copied? If it is ours then add + if (join_slice->is_Phi() && join_slice->as_Phi()->region() == join_region) { + phi = join_slice->as_Phi(); + } else { + // create the phi with join_slice filling supplying memory for all of the + // control edges to the join region + phi = PhiNode::make(join_region, join_slice, Type::MEMORY, mms.adr_type(C)); + phi = (PhiNode*) delay_transform(phi); + // gvn().set_type(phi, Type::MEMORY); + // new_phi = true; + } + // Now update the phi with the slice for the merging slice + phi->set_req(slot, merging_slice/* slow_path, slow_slice */); + // this updates join_m with the phi + mms.set_memory(phi); + } + } +} + + +//----------------------------- make_call ---------------------------- +// Trivial runtime call +void IdealKit::make_leaf_call(const TypeFunc *slow_call_type, + address slow_call, + const char *leaf_name, + Node* parm0, + Node* parm1, + Node* parm2) { + + // We only handle taking in RawMem and modifying RawMem + const TypePtr* adr_type = TypeRawPtr::BOTTOM; + uint adr_idx = C->get_alias_index(adr_type); + + // Clone initial memory + MergeMemNode* cloned_mem = MergeMemNode::make(C, merged_memory()); + + // Slow-path leaf call + int size = slow_call_type->domain()->cnt(); + CallNode *call = (CallNode*)new (C, size) CallLeafNode( slow_call_type, slow_call, leaf_name, adr_type); + + // Set fixed predefined input arguments + call->init_req( TypeFunc::Control, ctrl() ); + call->init_req( TypeFunc::I_O , top() ) ; // does no i/o + // Narrow memory as only memory input + call->init_req( TypeFunc::Memory , memory(adr_idx)); + call->init_req( TypeFunc::FramePtr, top() /* frameptr() */ ); + call->init_req( TypeFunc::ReturnAdr, top() ); + + if (parm0 != NULL) call->init_req(TypeFunc::Parms+0, parm0); + if (parm1 != NULL) call->init_req(TypeFunc::Parms+1, parm1); + if (parm2 != NULL) call->init_req(TypeFunc::Parms+2, parm2); + + // Node *c = _gvn.transform(call); + call = (CallNode *) _gvn.transform(call); + Node *c = call; // dbx gets confused with call call->dump() + + // Slow leaf call has no side-effects, sets few values + + set_ctrl(transform( new (C, 1) ProjNode(call,TypeFunc::Control) )); + + // Set the incoming clone of memory as current memory + set_all_memory(cloned_mem); + + // Make memory for the call + Node* mem = _gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory) ); + + // Set the RawPtr memory state only. + set_memory(mem, adr_idx); + + assert(C->alias_type(call->adr_type()) == C->alias_type(adr_type), + "call node must be constructed correctly"); +} diff --git a/hotspot/src/share/vm/opto/idealKit.hpp b/hotspot/src/share/vm/opto/idealKit.hpp new file mode 100644 index 00000000000..5ccdb77b3b7 --- /dev/null +++ b/hotspot/src/share/vm/opto/idealKit.hpp @@ -0,0 +1,230 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//----------------------------------------------------------------------------- +//----------------------------IdealKit----------------------------------------- +// Set of utilities for creating control flow and scalar SSA data flow. +// Control: +// if_then(left, relop, right) +// else_ (optional) +// end_if +// loop(iv variable, initial, relop, limit) +// - sets iv to initial for first trip +// - exits when relation on limit is true +// - the values of initial and limit should be loop invariant +// - no increment, must be explicitly coded +// - final value of iv is available after end_loop (until dead()) +// end_loop +// make_label(number of gotos) +// goto_(label) +// bind(label) +// Data: +// ConI(integer constant) - create an integer constant +// set(variable, value) - assignment +// value(variable) - reference value +// dead(variable) - variable's value is no longer live +// increment(variable, value) - increment variable by value +// simple operations: AddI, SubI, AndI, LShiftI, etc. +// Example: +// Node* limit = ?? +// IdealVariable i(kit), j(kit); +// declares_done(); +// Node* exit = make_label(1); // 1 goto +// set(j, ConI(0)); +// loop(i, ConI(0), BoolTest::lt, limit); { +// if_then(value(i), BoolTest::gt, ConI(5)) { +// set(j, ConI(1)); +// goto_(exit); dead(i); +// } end_if(); +// increment(i, ConI(1)); +// } end_loop(); dead(i); +// bind(exit); +// +// See string_indexOf for a more complete example. + +class IdealKit; + +// Variable definition for IdealKit +class IdealVariable: public StackObj { + friend class IdealKit; + private: + int _id; + void set_id(int id) { _id = id; } + public: + IdealVariable(IdealKit &k); + int id() { assert(has_id(),"uninitialized id"); return _id; } + bool has_id() { return _id >= 0; } +}; + +class IdealKit: public StackObj { + friend class IdealVariable; + // The main state (called a cvstate for Control and Variables) + // contains both the current values of the variables and the + // current set of predecessor control edges. The variable values + // are managed via a Node [in(1)..in(_var_ct)], and the predecessor + // control edges managed via a RegionNode. The in(0) of the Node + // for variables points to the RegionNode for the control edges. + protected: + Compile * const C; + PhaseGVN &_gvn; + GrowableArray* _pending_cvstates; // stack of cvstates + GrowableArray* _delay_transform; // delay invoking gvn.transform until drain + Node* _cvstate; // current cvstate (control, memory and variables) + uint _var_ct; // number of variables + bool _delay_all_transforms; // flag forcing all transforms to be delayed + Node* _initial_ctrl; // saves initial control until variables declared + Node* _initial_memory; // saves initial memory until variables declared + + PhaseGVN& gvn() const { return _gvn; } + // Create a new cvstate filled with nulls + Node* new_cvstate(); // Create a new cvstate + Node* cvstate() { return _cvstate; } // current cvstate + Node* copy_cvstate(); // copy current cvstate + void set_ctrl(Node* ctrl) { _cvstate->set_req(TypeFunc::Control, ctrl); } + + // Should this assert this is a MergeMem??? + void set_all_memory(Node* mem){ _cvstate->set_req(TypeFunc::Memory, mem); } + void set_memory(Node* mem, uint alias_idx ); + void do_memory_merge(Node* merging, Node* join); + void clear(Node* m); // clear a cvstate + void stop() { clear(_cvstate); } // clear current cvstate + Node* delay_transform(Node* n); + Node* transform(Node* n); // gvn.transform or push node on delay list + Node* promote_to_phi(Node* n, Node* reg);// Promote "n" to a phi on region "reg" + bool was_promoted_to_phi(Node* n, Node* reg) { + return (n->is_Phi() && n->in(0) == reg); + } + void declare(IdealVariable* v) { v->set_id(_var_ct++); } + // This declares the position where vars are kept in the cvstate + // For some degree of consistency we use the TypeFunc enum to + // soak up spots in the inputs even though we only use early Control + // and Memory slots. (So far.) + static const uint first_var; // = TypeFunc::Parms + 1; + +#ifdef ASSERT + enum State { NullS=0, BlockS=1, LoopS=2, IfThenS=4, ElseS=8, EndifS= 16 }; + GrowableArray* _state; + State state() { return (State)(_state->top()); } +#endif + + // Users should not care about slices only MergedMem so no access for them. + Node* memory(uint alias_idx); + + public: + IdealKit(PhaseGVN &gvn, Node* control, Node* memory, bool delay_all_transforms = false); + ~IdealKit() { + stop(); + drain_delay_transform(); + } + // Control + Node* ctrl() { return _cvstate->in(TypeFunc::Control); } + Node* top() { return C->top(); } + MergeMemNode* merged_memory() { return _cvstate->in(TypeFunc::Memory)->as_MergeMem(); } + void set(IdealVariable& v, Node* rhs) { _cvstate->set_req(first_var + v.id(), rhs); } + Node* value(IdealVariable& v) { return _cvstate->in(first_var + v.id()); } + void dead(IdealVariable& v) { set(v, (Node*)NULL); } + void if_then(Node* left, BoolTest::mask relop, Node* right, + float prob = PROB_FAIR, float cnt = COUNT_UNKNOWN, + bool push_new_state = true); + void else_(); + void end_if(); + void loop(IdealVariable& iv, Node* init, BoolTest::mask cmp, Node* limit, + float prob = PROB_LIKELY(0.9), float cnt = COUNT_UNKNOWN); + void end_loop(); + Node* make_label(int goto_ct); + void bind(Node* lab); + void goto_(Node* lab, bool bind = false); + void declares_done(); + void drain_delay_transform(); + + Node* IfTrue(IfNode* iff) { return transform(new (C,1) IfTrueNode(iff)); } + Node* IfFalse(IfNode* iff) { return transform(new (C,1) IfFalseNode(iff)); } + + // Data + Node* ConI(jint k) { return (Node*)gvn().intcon(k); } + Node* makecon(const Type *t) const { return _gvn.makecon(t); } + + Node* AddI(Node* l, Node* r) { return transform(new (C,3) AddINode(l, r)); } + Node* SubI(Node* l, Node* r) { return transform(new (C,3) SubINode(l, r)); } + Node* AndI(Node* l, Node* r) { return transform(new (C,3) AndINode(l, r)); } + Node* MaxI(Node* l, Node* r) { return transform(new (C,3) MaxINode(l, r)); } + Node* LShiftI(Node* l, Node* r) { return transform(new (C,3) LShiftINode(l, r)); } + Node* CmpI(Node* l, Node* r) { return transform(new (C,3) CmpINode(l, r)); } + Node* Bool(Node* cmp, BoolTest::mask relop) { return transform(new (C,2) BoolNode(cmp, relop)); } + void increment(IdealVariable& v, Node* j) { set(v, AddI(value(v), j)); } + void decrement(IdealVariable& v, Node* j) { set(v, SubI(value(v), j)); } + + Node* CmpL(Node* l, Node* r) { return transform(new (C,3) CmpLNode(l, r)); } + + // TLS + Node* thread() { return gvn().transform(new (C, 1) ThreadLocalNode()); } + + // Pointers + Node* AddP(Node *base, Node *ptr, Node *off) { return transform(new (C,4) AddPNode(base, ptr, off)); } + Node* CmpP(Node* l, Node* r) { return transform(new (C,3) CmpPNode(l, r)); } +#ifdef _LP64 + Node* XorX(Node* l, Node* r) { return transform(new (C,3) XorLNode(l, r)); } +#else // _LP64 + Node* XorX(Node* l, Node* r) { return transform(new (C,3) XorINode(l, r)); } +#endif // _LP64 + Node* URShiftX(Node* l, Node* r) { return transform(new (C,3) URShiftXNode(l, r)); } + Node* ConX(jint k) { return (Node*)gvn().MakeConX(k); } + Node* CastPX(Node* ctl, Node* p) { return transform(new (C,2) CastP2XNode(ctl, p)); } + // Add a fixed offset to a pointer + Node* basic_plus_adr(Node* base, Node* ptr, intptr_t offset); + + // Memory operations + + // This is the base version which is given an alias index. + Node* load(Node* ctl, + Node* adr, + const Type* t, + BasicType bt, + int adr_idx, + bool require_atomic_access = false); + + // Return the new StoreXNode + Node* store(Node* ctl, + Node* adr, + Node* val, + BasicType bt, + int adr_idx, + bool require_atomic_access = false); + + // Store a card mark ordered after store_oop + Node* storeCM(Node* ctl, + Node* adr, + Node* val, + Node* oop_store, + BasicType bt, + int adr_idx); + + // Trivial call + void make_leaf_call(const TypeFunc *slow_call_type, + address slow_call, + const char *leaf_name, + Node* parm0, + Node* parm1 = NULL, + Node* parm2 = NULL); +}; diff --git a/hotspot/src/share/vm/opto/ifg.cpp b/hotspot/src/share/vm/opto/ifg.cpp new file mode 100644 index 00000000000..2c6cd665fea --- /dev/null +++ b/hotspot/src/share/vm/opto/ifg.cpp @@ -0,0 +1,813 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ifg.cpp.incl" + +#define EXACT_PRESSURE 1 + +//============================================================================= +//------------------------------IFG-------------------------------------------- +PhaseIFG::PhaseIFG( Arena *arena ) : Phase(Interference_Graph), _arena(arena) { +} + +//------------------------------init------------------------------------------- +void PhaseIFG::init( uint maxlrg ) { + _maxlrg = maxlrg; + _yanked = new (_arena) VectorSet(_arena); + _is_square = false; + // Make uninitialized adjacency lists + _adjs = (IndexSet*)_arena->Amalloc(sizeof(IndexSet)*maxlrg); + // Also make empty live range structures + _lrgs = (LRG *)_arena->Amalloc( maxlrg * sizeof(LRG) ); + memset(_lrgs,0,sizeof(LRG)*maxlrg); + // Init all to empty + for( uint i = 0; i < maxlrg; i++ ) { + _adjs[i].initialize(maxlrg); + _lrgs[i].Set_All(); + } +} + +//------------------------------add-------------------------------------------- +// Add edge between vertices a & b. These are sorted (triangular matrix), +// then the smaller number is inserted in the larger numbered array. +int PhaseIFG::add_edge( uint a, uint b ) { + lrgs(a).invalid_degree(); + lrgs(b).invalid_degree(); + // Sort a and b, so that a is bigger + assert( !_is_square, "only on triangular" ); + if( a < b ) { uint tmp = a; a = b; b = tmp; } + return _adjs[a].insert( b ); +} + +//------------------------------add_vector------------------------------------- +// Add an edge between 'a' and everything in the vector. +void PhaseIFG::add_vector( uint a, IndexSet *vec ) { + // IFG is triangular, so do the inserts where 'a' < 'b'. + assert( !_is_square, "only on triangular" ); + IndexSet *adjs_a = &_adjs[a]; + if( !vec->count() ) return; + + IndexSetIterator elements(vec); + uint neighbor; + while ((neighbor = elements.next()) != 0) { + add_edge( a, neighbor ); + } +} + +//------------------------------test------------------------------------------- +// Is there an edge between a and b? +int PhaseIFG::test_edge( uint a, uint b ) const { + // Sort a and b, so that a is larger + assert( !_is_square, "only on triangular" ); + if( a < b ) { uint tmp = a; a = b; b = tmp; } + return _adjs[a].member(b); +} + +//------------------------------SquareUp--------------------------------------- +// Convert triangular matrix to square matrix +void PhaseIFG::SquareUp() { + assert( !_is_square, "only on triangular" ); + + // Simple transpose + for( uint i = 0; i < _maxlrg; i++ ) { + IndexSetIterator elements(&_adjs[i]); + uint datum; + while ((datum = elements.next()) != 0) { + _adjs[datum].insert( i ); + } + } + _is_square = true; +} + +//------------------------------Compute_Effective_Degree----------------------- +// Compute effective degree in bulk +void PhaseIFG::Compute_Effective_Degree() { + assert( _is_square, "only on square" ); + + for( uint i = 0; i < _maxlrg; i++ ) + lrgs(i).set_degree(effective_degree(i)); +} + +//------------------------------test_edge_sq----------------------------------- +int PhaseIFG::test_edge_sq( uint a, uint b ) const { + assert( _is_square, "only on square" ); + // Swap, so that 'a' has the lesser count. Then binary search is on + // the smaller of a's list and b's list. + if( neighbor_cnt(a) > neighbor_cnt(b) ) { uint tmp = a; a = b; b = tmp; } + //return _adjs[a].unordered_member(b); + return _adjs[a].member(b); +} + +//------------------------------Union------------------------------------------ +// Union edges of B into A +void PhaseIFG::Union( uint a, uint b ) { + assert( _is_square, "only on square" ); + IndexSet *A = &_adjs[a]; + IndexSetIterator b_elements(&_adjs[b]); + uint datum; + while ((datum = b_elements.next()) != 0) { + if(A->insert(datum)) { + _adjs[datum].insert(a); + lrgs(a).invalid_degree(); + lrgs(datum).invalid_degree(); + } + } +} + +//------------------------------remove_node------------------------------------ +// Yank a Node and all connected edges from the IFG. Return a +// list of neighbors (edges) yanked. +IndexSet *PhaseIFG::remove_node( uint a ) { + assert( _is_square, "only on square" ); + assert( !_yanked->test(a), "" ); + _yanked->set(a); + + // I remove the LRG from all neighbors. + IndexSetIterator elements(&_adjs[a]); + LRG &lrg_a = lrgs(a); + uint datum; + while ((datum = elements.next()) != 0) { + _adjs[datum].remove(a); + lrgs(datum).inc_degree( -lrg_a.compute_degree(lrgs(datum)) ); + } + return neighbors(a); +} + +//------------------------------re_insert-------------------------------------- +// Re-insert a yanked Node. +void PhaseIFG::re_insert( uint a ) { + assert( _is_square, "only on square" ); + assert( _yanked->test(a), "" ); + (*_yanked) >>= a; + + IndexSetIterator elements(&_adjs[a]); + uint datum; + while ((datum = elements.next()) != 0) { + _adjs[datum].insert(a); + lrgs(datum).invalid_degree(); + } +} + +//------------------------------compute_degree--------------------------------- +// Compute the degree between 2 live ranges. If both live ranges are +// aligned-adjacent powers-of-2 then we use the MAX size. If either is +// mis-aligned (or for Fat-Projections, not-adjacent) then we have to +// MULTIPLY the sizes. Inspect Brigg's thesis on register pairs to see why +// this is so. +int LRG::compute_degree( LRG &l ) const { + int tmp; + int num_regs = _num_regs; + int nregs = l.num_regs(); + tmp = (_fat_proj || l._fat_proj) // either is a fat-proj? + ? (num_regs * nregs) // then use product + : MAX2(num_regs,nregs); // else use max + return tmp; +} + +//------------------------------effective_degree------------------------------- +// Compute effective degree for this live range. If both live ranges are +// aligned-adjacent powers-of-2 then we use the MAX size. If either is +// mis-aligned (or for Fat-Projections, not-adjacent) then we have to +// MULTIPLY the sizes. Inspect Brigg's thesis on register pairs to see why +// this is so. +int PhaseIFG::effective_degree( uint lidx ) const { + int eff = 0; + int num_regs = lrgs(lidx).num_regs(); + int fat_proj = lrgs(lidx)._fat_proj; + IndexSet *s = neighbors(lidx); + IndexSetIterator elements(s); + uint nidx; + while((nidx = elements.next()) != 0) { + LRG &lrgn = lrgs(nidx); + int nregs = lrgn.num_regs(); + eff += (fat_proj || lrgn._fat_proj) // either is a fat-proj? + ? (num_regs * nregs) // then use product + : MAX2(num_regs,nregs); // else use max + } + return eff; +} + + +#ifndef PRODUCT +//------------------------------dump------------------------------------------- +void PhaseIFG::dump() const { + tty->print_cr("-- Interference Graph --%s--", + _is_square ? "square" : "triangular" ); + if( _is_square ) { + for( uint i = 0; i < _maxlrg; i++ ) { + tty->print( (*_yanked)[i] ? "XX " : " "); + tty->print("L%d: { ",i); + IndexSetIterator elements(&_adjs[i]); + uint datum; + while ((datum = elements.next()) != 0) { + tty->print("L%d ", datum); + } + tty->print_cr("}"); + + } + return; + } + + // Triangular + for( uint i = 0; i < _maxlrg; i++ ) { + uint j; + tty->print( (*_yanked)[i] ? "XX " : " "); + tty->print("L%d: { ",i); + for( j = _maxlrg; j > i; j-- ) + if( test_edge(j - 1,i) ) { + tty->print("L%d ",j - 1); + } + tty->print("| "); + IndexSetIterator elements(&_adjs[i]); + uint datum; + while ((datum = elements.next()) != 0) { + tty->print("L%d ", datum); + } + tty->print("}\n"); + } + tty->print("\n"); +} + +//------------------------------stats------------------------------------------ +void PhaseIFG::stats() const { + ResourceMark rm; + int *h_cnt = NEW_RESOURCE_ARRAY(int,_maxlrg*2); + memset( h_cnt, 0, sizeof(int)*_maxlrg*2 ); + uint i; + for( i = 0; i < _maxlrg; i++ ) { + h_cnt[neighbor_cnt(i)]++; + } + tty->print_cr("--Histogram of counts--"); + for( i = 0; i < _maxlrg*2; i++ ) + if( h_cnt[i] ) + tty->print("%d/%d ",i,h_cnt[i]); + tty->print_cr(""); +} + +//------------------------------verify----------------------------------------- +void PhaseIFG::verify( const PhaseChaitin *pc ) const { + // IFG is square, sorted and no need for Find + for( uint i = 0; i < _maxlrg; i++ ) { + assert(!((*_yanked)[i]) || !neighbor_cnt(i), "Is removed completely" ); + IndexSet *set = &_adjs[i]; + IndexSetIterator elements(set); + uint idx; + uint last = 0; + while ((idx = elements.next()) != 0) { + assert( idx != i, "Must have empty diagonal"); + assert( pc->Find_const(idx) == idx, "Must not need Find" ); + assert( _adjs[idx].member(i), "IFG not square" ); + assert( !(*_yanked)[idx], "No yanked neighbors" ); + assert( last < idx, "not sorted increasing"); + last = idx; + } + assert( !lrgs(i)._degree_valid || + effective_degree(i) == lrgs(i).degree(), "degree is valid but wrong" ); + } +} +#endif + +//------------------------------interfere_with_live---------------------------- +// Interfere this register with everything currently live. Use the RegMasks +// to trim the set of possible interferences. Return a count of register-only +// inteferences as an estimate of register pressure. +void PhaseChaitin::interfere_with_live( uint r, IndexSet *liveout ) { + uint retval = 0; + // Interfere with everything live. + const RegMask &rm = lrgs(r).mask(); + // Check for interference by checking overlap of regmasks. + // Only interfere if acceptable register masks overlap. + IndexSetIterator elements(liveout); + uint l; + while( (l = elements.next()) != 0 ) + if( rm.overlap( lrgs(l).mask() ) ) + _ifg->add_edge( r, l ); +} + +//------------------------------build_ifg_virtual------------------------------ +// Actually build the interference graph. Uses virtual registers only, no +// physical register masks. This allows me to be very aggressive when +// coalescing copies. Some of this aggressiveness will have to be undone +// later, but I'd rather get all the copies I can now (since unremoved copies +// at this point can end up in bad places). Copies I re-insert later I have +// more opportunity to insert them in low-frequency locations. +void PhaseChaitin::build_ifg_virtual( ) { + + // For all blocks (in any order) do... + for( uint i=0; i<_cfg._num_blocks; i++ ) { + Block *b = _cfg._blocks[i]; + IndexSet *liveout = _live->live(b); + + // The IFG is built by a single reverse pass over each basic block. + // Starting with the known live-out set, we remove things that get + // defined and add things that become live (essentially executing one + // pass of a standard LIVE analysis). Just before a Node defines a value + // (and removes it from the live-ness set) that value is certainly live. + // The defined value interferes with everything currently live. The + // value is then removed from the live-ness set and it's inputs are + // added to the live-ness set. + for( uint j = b->end_idx() + 1; j > 1; j-- ) { + Node *n = b->_nodes[j-1]; + + // Get value being defined + uint r = n2lidx(n); + + // Some special values do not allocate + if( r ) { + + // Remove from live-out set + liveout->remove(r); + + // Copies do not define a new value and so do not interfere. + // Remove the copies source from the liveout set before interfering. + uint idx = n->is_Copy(); + if( idx ) liveout->remove( n2lidx(n->in(idx)) ); + + // Interfere with everything live + interfere_with_live( r, liveout ); + } + + // Make all inputs live + if( !n->is_Phi() ) { // Phi function uses come from prior block + for( uint k = 1; k < n->req(); k++ ) + liveout->insert( n2lidx(n->in(k)) ); + } + + // 2-address instructions always have the defined value live + // on entry to the instruction, even though it is being defined + // by the instruction. We pretend a virtual copy sits just prior + // to the instruction and kills the src-def'd register. + // In other words, for 2-address instructions the defined value + // interferes with all inputs. + uint idx; + if( n->is_Mach() && (idx = n->as_Mach()->two_adr()) ) { + const MachNode *mach = n->as_Mach(); + // Sometimes my 2-address ADDs are commuted in a bad way. + // We generally want the USE-DEF register to refer to the + // loop-varying quantity, to avoid a copy. + uint op = mach->ideal_Opcode(); + // Check that mach->num_opnds() == 3 to ensure instruction is + // not subsuming constants, effectively excludes addI_cin_imm + // Can NOT swap for instructions like addI_cin_imm since it + // is adding zero to yhi + carry and the second ideal-input + // points to the result of adding low-halves. + // Checking req() and num_opnds() does NOT distinguish addI_cout from addI_cout_imm + if( (op == Op_AddI && mach->req() == 3 && mach->num_opnds() == 3) && + n->in(1)->bottom_type()->base() == Type::Int && + // See if the ADD is involved in a tight data loop the wrong way + n->in(2)->is_Phi() && + n->in(2)->in(2) == n ) { + Node *tmp = n->in(1); + n->set_req( 1, n->in(2) ); + n->set_req( 2, tmp ); + } + // Defined value interferes with all inputs + uint lidx = n2lidx(n->in(idx)); + for( uint k = 1; k < n->req(); k++ ) { + uint kidx = n2lidx(n->in(k)); + if( kidx != lidx ) + _ifg->add_edge( r, kidx ); + } + } + } // End of forall instructions in block + } // End of forall blocks +} + +//------------------------------count_int_pressure----------------------------- +uint PhaseChaitin::count_int_pressure( IndexSet *liveout ) { + IndexSetIterator elements(liveout); + uint lidx; + uint cnt = 0; + while ((lidx = elements.next()) != 0) { + if( lrgs(lidx).mask().is_UP() && + lrgs(lidx).mask_size() && + !lrgs(lidx)._is_float && + lrgs(lidx).mask().overlap(*Matcher::idealreg2regmask[Op_RegI]) ) + cnt += lrgs(lidx).reg_pressure(); + } + return cnt; +} + +//------------------------------count_float_pressure--------------------------- +uint PhaseChaitin::count_float_pressure( IndexSet *liveout ) { + IndexSetIterator elements(liveout); + uint lidx; + uint cnt = 0; + while ((lidx = elements.next()) != 0) { + if( lrgs(lidx).mask().is_UP() && + lrgs(lidx).mask_size() && + lrgs(lidx)._is_float ) + cnt += lrgs(lidx).reg_pressure(); + } + return cnt; +} + +//------------------------------lower_pressure--------------------------------- +// Adjust register pressure down by 1. Capture last hi-to-low transition, +static void lower_pressure( LRG *lrg, uint where, Block *b, uint *pressure, uint *hrp_index ) { + if( lrg->mask().is_UP() && lrg->mask_size() ) { + if( lrg->_is_float ) { + pressure[1] -= lrg->reg_pressure(); + if( pressure[1] == (uint)FLOATPRESSURE ) { + hrp_index[1] = where; +#ifdef EXACT_PRESSURE + if( pressure[1] > b->_freg_pressure ) + b->_freg_pressure = pressure[1]+1; +#else + b->_freg_pressure = (uint)FLOATPRESSURE+1; +#endif + } + } else if( lrg->mask().overlap(*Matcher::idealreg2regmask[Op_RegI]) ) { + pressure[0] -= lrg->reg_pressure(); + if( pressure[0] == (uint)INTPRESSURE ) { + hrp_index[0] = where; +#ifdef EXACT_PRESSURE + if( pressure[0] > b->_reg_pressure ) + b->_reg_pressure = pressure[0]+1; +#else + b->_reg_pressure = (uint)INTPRESSURE+1; +#endif + } + } + } +} + +//------------------------------build_ifg_physical----------------------------- +// Build the interference graph using physical registers when available. +// That is, if 2 live ranges are simultaneously alive but in their acceptable +// register sets do not overlap, then they do not interfere. +uint PhaseChaitin::build_ifg_physical( ResourceArea *a ) { + NOT_PRODUCT( Compile::TracePhase t3("buildIFG", &_t_buildIFGphysical, TimeCompiler); ) + + uint spill_reg = LRG::SPILL_REG; + uint must_spill = 0; + + // For all blocks (in any order) do... + for( uint i = 0; i < _cfg._num_blocks; i++ ) { + Block *b = _cfg._blocks[i]; + // Clone (rather than smash in place) the liveout info, so it is alive + // for the "collect_gc_info" phase later. + IndexSet liveout(_live->live(b)); + uint last_inst = b->end_idx(); + // Compute last phi index + uint last_phi; + for( last_phi = 1; last_phi < last_inst; last_phi++ ) + if( !b->_nodes[last_phi]->is_Phi() ) + break; + + // Reset block's register pressure values for each ifg construction + uint pressure[2], hrp_index[2]; + pressure[0] = pressure[1] = 0; + hrp_index[0] = hrp_index[1] = last_inst+1; + b->_reg_pressure = b->_freg_pressure = 0; + // Liveout things are presumed live for the whole block. We accumulate + // 'area' accordingly. If they get killed in the block, we'll subtract + // the unused part of the block from the area. + double cost = b->_freq * double(last_inst-last_phi); + assert( cost >= 0, "negative spill cost" ); + IndexSetIterator elements(&liveout); + uint lidx; + while ((lidx = elements.next()) != 0) { + LRG &lrg = lrgs(lidx); + lrg._area += cost; + // Compute initial register pressure + if( lrg.mask().is_UP() && lrg.mask_size() ) { + if( lrg._is_float ) { // Count float pressure + pressure[1] += lrg.reg_pressure(); +#ifdef EXACT_PRESSURE + if( pressure[1] > b->_freg_pressure ) + b->_freg_pressure = pressure[1]; +#endif + // Count int pressure, but do not count the SP, flags + } else if( lrgs(lidx).mask().overlap(*Matcher::idealreg2regmask[Op_RegI]) ) { + pressure[0] += lrg.reg_pressure(); +#ifdef EXACT_PRESSURE + if( pressure[0] > b->_reg_pressure ) + b->_reg_pressure = pressure[0]; +#endif + } + } + } + assert( pressure[0] == count_int_pressure (&liveout), "" ); + assert( pressure[1] == count_float_pressure(&liveout), "" ); + + // The IFG is built by a single reverse pass over each basic block. + // Starting with the known live-out set, we remove things that get + // defined and add things that become live (essentially executing one + // pass of a standard LIVE analysis). Just before a Node defines a value + // (and removes it from the live-ness set) that value is certainly live. + // The defined value interferes with everything currently live. The + // value is then removed from the live-ness set and it's inputs are added + // to the live-ness set. + uint j; + for( j = last_inst + 1; j > 1; j-- ) { + Node *n = b->_nodes[j - 1]; + + // Get value being defined + uint r = n2lidx(n); + + // Some special values do not allocate + if( r ) { + // A DEF normally costs block frequency; rematerialized values are + // removed from the DEF sight, so LOWER costs here. + lrgs(r)._cost += n->rematerialize() ? 0 : b->_freq; + + // If it is not live, then this instruction is dead. Probably caused + // by spilling and rematerialization. Who cares why, yank this baby. + if( !liveout.member(r) && n->Opcode() != Op_SafePoint ) { + Node *def = n->in(0); + if( !n->is_Proj() || + // Could also be a flags-projection of a dead ADD or such. + (n2lidx(def) && !liveout.member(n2lidx(def)) ) ) { + b->_nodes.remove(j - 1); + if( lrgs(r)._def == n ) lrgs(r)._def = 0; + n->disconnect_inputs(NULL); + _cfg._bbs.map(n->_idx,NULL); + n->replace_by(C->top()); + // Since yanking a Node from block, high pressure moves up one + hrp_index[0]--; + hrp_index[1]--; + continue; + } + + // Fat-projections kill many registers which cannot be used to + // hold live ranges. + if( lrgs(r)._fat_proj ) { + // Count the int-only registers + RegMask itmp = lrgs(r).mask(); + itmp.AND(*Matcher::idealreg2regmask[Op_RegI]); + int iregs = itmp.Size(); +#ifdef EXACT_PRESSURE + if( pressure[0]+iregs > b->_reg_pressure ) + b->_reg_pressure = pressure[0]+iregs; +#endif + if( pressure[0] <= (uint)INTPRESSURE && + pressure[0]+iregs > (uint)INTPRESSURE ) { +#ifndef EXACT_PRESSURE + b->_reg_pressure = (uint)INTPRESSURE+1; +#endif + hrp_index[0] = j-1; + } + // Count the float-only registers + RegMask ftmp = lrgs(r).mask(); + ftmp.AND(*Matcher::idealreg2regmask[Op_RegD]); + int fregs = ftmp.Size(); +#ifdef EXACT_PRESSURE + if( pressure[1]+fregs > b->_freg_pressure ) + b->_freg_pressure = pressure[1]+fregs; +#endif + if( pressure[1] <= (uint)FLOATPRESSURE && + pressure[1]+fregs > (uint)FLOATPRESSURE ) { +#ifndef EXACT_PRESSURE + b->_freg_pressure = (uint)FLOATPRESSURE+1; +#endif + hrp_index[1] = j-1; + } + } + + } else { // Else it is live + // A DEF also ends 'area' partway through the block. + lrgs(r)._area -= cost; + assert( lrgs(r)._area >= 0, "negative spill area" ); + + // Insure high score for immediate-use spill copies so they get a color + if( n->is_SpillCopy() + && lrgs(r)._def != NodeSentinel // MultiDef live range can still split + && n->outcnt() == 1 // and use must be in this block + && _cfg._bbs[n->unique_out()->_idx] == b ) { + // All single-use MachSpillCopy(s) that immediately precede their + // use must color early. If a longer live range steals their + // color, the spill copy will split and may push another spill copy + // further away resulting in an infinite spill-split-retry cycle. + // Assigning a zero area results in a high score() and a good + // location in the simplify list. + // + + Node *single_use = n->unique_out(); + assert( b->find_node(single_use) >= j, "Use must be later in block"); + // Use can be earlier in block if it is a Phi, but then I should be a MultiDef + + // Find first non SpillCopy 'm' that follows the current instruction + // (j - 1) is index for current instruction 'n' + Node *m = n; + for( uint i = j; i <= last_inst && m->is_SpillCopy(); ++i ) { m = b->_nodes[i]; } + if( m == single_use ) { + lrgs(r)._area = 0.0; + } + } + + // Remove from live-out set + if( liveout.remove(r) ) { + // Adjust register pressure. + // Capture last hi-to-lo pressure transition + lower_pressure( &lrgs(r), j-1, b, pressure, hrp_index ); + assert( pressure[0] == count_int_pressure (&liveout), "" ); + assert( pressure[1] == count_float_pressure(&liveout), "" ); + } + + // Copies do not define a new value and so do not interfere. + // Remove the copies source from the liveout set before interfering. + uint idx = n->is_Copy(); + if( idx ) { + uint x = n2lidx(n->in(idx)); + if( liveout.remove( x ) ) { + lrgs(x)._area -= cost; + // Adjust register pressure. + lower_pressure( &lrgs(x), j-1, b, pressure, hrp_index ); + assert( pressure[0] == count_int_pressure (&liveout), "" ); + assert( pressure[1] == count_float_pressure(&liveout), "" ); + } + } + } // End of if live or not + + // Interfere with everything live. If the defined value must + // go in a particular register, just remove that register from + // all conflicting parties and avoid the interference. + + // Make exclusions for rematerializable defs. Since rematerializable + // DEFs are not bound but the live range is, some uses must be bound. + // If we spill live range 'r', it can rematerialize at each use site + // according to its bindings. + const RegMask &rmask = lrgs(r).mask(); + if( lrgs(r).is_bound() && !(n->rematerialize()) && rmask.is_NotEmpty() ) { + // Smear odd bits; leave only aligned pairs of bits. + RegMask r2mask = rmask; + r2mask.SmearToPairs(); + // Check for common case + int r_size = lrgs(r).num_regs(); + OptoReg::Name r_reg = (r_size == 1) ? rmask.find_first_elem() : OptoReg::Physical; + + IndexSetIterator elements(&liveout); + uint l; + while ((l = elements.next()) != 0) { + LRG &lrg = lrgs(l); + // If 'l' must spill already, do not further hack his bits. + // He'll get some interferences and be forced to spill later. + if( lrg._must_spill ) continue; + // Remove bound register(s) from 'l's choices + RegMask old = lrg.mask(); + uint old_size = lrg.mask_size(); + // Remove the bits from LRG 'r' from LRG 'l' so 'l' no + // longer interferes with 'r'. If 'l' requires aligned + // adjacent pairs, subtract out bit pairs. + if( lrg.num_regs() == 2 && !lrg._fat_proj ) { + lrg.SUBTRACT( r2mask ); + lrg.compute_set_mask_size(); + } else if( r_size != 1 ) { + lrg.SUBTRACT( rmask ); + lrg.compute_set_mask_size(); + } else { // Common case: size 1 bound removal + if( lrg.mask().Member(r_reg) ) { + lrg.Remove(r_reg); + lrg.set_mask_size(lrg.mask().is_AllStack() ? 65535:old_size-1); + } + } + // If 'l' goes completely dry, it must spill. + if( lrg.not_free() ) { + // Give 'l' some kind of reasonable mask, so he picks up + // interferences (and will spill later). + lrg.set_mask( old ); + lrg.set_mask_size(old_size); + must_spill++; + lrg._must_spill = 1; + lrg.set_reg(OptoReg::Name(LRG::SPILL_REG)); + } + } + } // End of if bound + + // Now interference with everything that is live and has + // compatible register sets. + interfere_with_live(r,&liveout); + + } // End of if normal register-allocated value + + cost -= b->_freq; // Area remaining in the block + if( cost < 0.0 ) cost = 0.0; // Cost goes negative in the Phi area + + // Make all inputs live + if( !n->is_Phi() ) { // Phi function uses come from prior block + JVMState* jvms = n->jvms(); + uint debug_start = jvms ? jvms->debug_start() : 999999; + // Start loop at 1 (skip control edge) for most Nodes. + // SCMemProj's might be the sole use of a StoreLConditional. + // While StoreLConditionals set memory (the SCMemProj use) + // they also def flags; if that flag def is unused the + // allocator sees a flag-setting instruction with no use of + // the flags and assumes it's dead. This keeps the (useless) + // flag-setting behavior alive while also keeping the (useful) + // memory update effect. + for( uint k = ((n->Opcode() == Op_SCMemProj) ? 0:1); k < n->req(); k++ ) { + Node *def = n->in(k); + uint x = n2lidx(def); + if( !x ) continue; + LRG &lrg = lrgs(x); + // No use-side cost for spilling debug info + if( k < debug_start ) + // A USE costs twice block frequency (once for the Load, once + // for a Load-delay). Rematerialized uses only cost once. + lrg._cost += (def->rematerialize() ? b->_freq : (b->_freq + b->_freq)); + // It is live now + if( liveout.insert( x ) ) { + // Newly live things assumed live from here to top of block + lrg._area += cost; + // Adjust register pressure + if( lrg.mask().is_UP() && lrg.mask_size() ) { + if( lrg._is_float ) { + pressure[1] += lrg.reg_pressure(); +#ifdef EXACT_PRESSURE + if( pressure[1] > b->_freg_pressure ) + b->_freg_pressure = pressure[1]; +#endif + } else if( lrg.mask().overlap(*Matcher::idealreg2regmask[Op_RegI]) ) { + pressure[0] += lrg.reg_pressure(); +#ifdef EXACT_PRESSURE + if( pressure[0] > b->_reg_pressure ) + b->_reg_pressure = pressure[0]; +#endif + } + } + assert( pressure[0] == count_int_pressure (&liveout), "" ); + assert( pressure[1] == count_float_pressure(&liveout), "" ); + } + assert( lrg._area >= 0, "negative spill area" ); + } + } + } // End of reverse pass over all instructions in block + + // If we run off the top of the block with high pressure and + // never see a hi-to-low pressure transition, just record that + // the whole block is high pressure. + if( pressure[0] > (uint)INTPRESSURE ) { + hrp_index[0] = 0; +#ifdef EXACT_PRESSURE + if( pressure[0] > b->_reg_pressure ) + b->_reg_pressure = pressure[0]; +#else + b->_reg_pressure = (uint)INTPRESSURE+1; +#endif + } + if( pressure[1] > (uint)FLOATPRESSURE ) { + hrp_index[1] = 0; +#ifdef EXACT_PRESSURE + if( pressure[1] > b->_freg_pressure ) + b->_freg_pressure = pressure[1]; +#else + b->_freg_pressure = (uint)FLOATPRESSURE+1; +#endif + } + + // Compute high pressure indice; avoid landing in the middle of projnodes + j = hrp_index[0]; + if( j < b->_nodes.size() && j < b->end_idx()+1 ) { + Node *cur = b->_nodes[j]; + while( cur->is_Proj() || (cur->is_MachNullCheck()) || cur->is_Catch() ) { + j--; + cur = b->_nodes[j]; + } + } + b->_ihrp_index = j; + j = hrp_index[1]; + if( j < b->_nodes.size() && j < b->end_idx()+1 ) { + Node *cur = b->_nodes[j]; + while( cur->is_Proj() || (cur->is_MachNullCheck()) || cur->is_Catch() ) { + j--; + cur = b->_nodes[j]; + } + } + b->_fhrp_index = j; + +#ifndef PRODUCT + // Gather Register Pressure Statistics + if( PrintOptoStatistics ) { + if( b->_reg_pressure > (uint)INTPRESSURE || b->_freg_pressure > (uint)FLOATPRESSURE ) + _high_pressure++; + else + _low_pressure++; + } +#endif + } // End of for all blocks + + return must_spill; +} diff --git a/hotspot/src/share/vm/opto/ifnode.cpp b/hotspot/src/share/vm/opto/ifnode.cpp new file mode 100644 index 00000000000..48031ef3d5a --- /dev/null +++ b/hotspot/src/share/vm/opto/ifnode.cpp @@ -0,0 +1,922 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_ifnode.cpp.incl" + + +extern int explicit_null_checks_elided; + +//============================================================================= +//------------------------------Value------------------------------------------ +// Return a tuple for whichever arm of the IF is reachable +const Type *IfNode::Value( PhaseTransform *phase ) const { + if( !in(0) ) return Type::TOP; + if( phase->type(in(0)) == Type::TOP ) + return Type::TOP; + const Type *t = phase->type(in(1)); + if( t == Type::TOP ) // data is undefined + return TypeTuple::IFNEITHER; // unreachable altogether + if( t == TypeInt::ZERO ) // zero, or false + return TypeTuple::IFFALSE; // only false branch is reachable + if( t == TypeInt::ONE ) // 1, or true + return TypeTuple::IFTRUE; // only true branch is reachable + assert( t == TypeInt::BOOL, "expected boolean type" ); + + return TypeTuple::IFBOTH; // No progress +} + +const RegMask &IfNode::out_RegMask() const { + return RegMask::Empty; +} + +//------------------------------split_if--------------------------------------- +// Look for places where we merge constants, then test on the merged value. +// If the IF test will be constant folded on the path with the constant, we +// win by splitting the IF to before the merge point. +static Node* split_if(IfNode *iff, PhaseIterGVN *igvn) { + // I could be a lot more general here, but I'm trying to squeeze this + // in before the Christmas '98 break so I'm gonna be kinda restrictive + // on the patterns I accept. CNC + + // Look for a compare of a constant and a merged value + Node *i1 = iff->in(1); + if( !i1->is_Bool() ) return NULL; + BoolNode *b = i1->as_Bool(); + Node *cmp = b->in(1); + if( !cmp->is_Cmp() ) return NULL; + i1 = cmp->in(1); + if( i1 == NULL || !i1->is_Phi() ) return NULL; + PhiNode *phi = i1->as_Phi(); + if( phi->is_copy() ) return NULL; + Node *con2 = cmp->in(2); + if( !con2->is_Con() ) return NULL; + // See that the merge point contains some constants + Node *con1=NULL; + uint i4; + for( i4 = 1; i4 < phi->req(); i4++ ) { + con1 = phi->in(i4); + if( !con1 ) return NULL; // Do not optimize partially collaped merges + if( con1->is_Con() ) break; // Found a constant + // Also allow null-vs-not-null checks + const TypePtr *tp = igvn->type(con1)->isa_ptr(); + if( tp && tp->_ptr == TypePtr::NotNull ) + break; + } + if( i4 >= phi->req() ) return NULL; // Found no constants + + igvn->C->set_has_split_ifs(true); // Has chance for split-if + + // Make sure that the compare can be constant folded away + Node *cmp2 = cmp->clone(); + cmp2->set_req(1,con1); + cmp2->set_req(2,con2); + const Type *t = cmp2->Value(igvn); + // This compare is dead, so whack it! + igvn->remove_dead_node(cmp2); + if( !t->singleton() ) return NULL; + + // No intervening control, like a simple Call + Node *r = iff->in(0); + if( !r->is_Region() ) return NULL; + if( phi->region() != r ) return NULL; + // No other users of the cmp/bool + if (b->outcnt() != 1 || cmp->outcnt() != 1) { + //tty->print_cr("many users of cmp/bool"); + return NULL; + } + + // Make sure we can determine where all the uses of merged values go + for (DUIterator_Fast jmax, j = r->fast_outs(jmax); j < jmax; j++) { + Node* u = r->fast_out(j); + if( u == r ) continue; + if( u == iff ) continue; + if( u->outcnt() == 0 ) continue; // use is dead & ignorable + if( !u->is_Phi() ) { + /* + if( u->is_Start() ) { + tty->print_cr("Region has inlined start use"); + } else { + tty->print_cr("Region has odd use"); + u->dump(2); + }*/ + return NULL; + } + if( u != phi ) { + // CNC - do not allow any other merged value + //tty->print_cr("Merging another value"); + //u->dump(2); + return NULL; + } + // Make sure we can account for all Phi uses + for (DUIterator_Fast kmax, k = u->fast_outs(kmax); k < kmax; k++) { + Node* v = u->fast_out(k); // User of the phi + // CNC - Allow only really simple patterns. + // In particular I disallow AddP of the Phi, a fairly common pattern + if( v == cmp ) continue; // The compare is OK + if( (v->is_ConstraintCast()) && + v->in(0)->in(0) == iff ) + continue; // CastPP/II of the IfNode is OK + // Disabled following code because I cannot tell if exactly one + // path dominates without a real dominator check. CNC 9/9/1999 + //uint vop = v->Opcode(); + //if( vop == Op_Phi ) { // Phi from another merge point might be OK + // Node *r = v->in(0); // Get controlling point + // if( !r ) return NULL; // Degraded to a copy + // // Find exactly one path in (either True or False doms, but not IFF) + // int cnt = 0; + // for( uint i = 1; i < r->req(); i++ ) + // if( r->in(i) && r->in(i)->in(0) == iff ) + // cnt++; + // if( cnt == 1 ) continue; // Exactly one of True or False guards Phi + //} + if( !v->is_Call() ) { + /* + if( v->Opcode() == Op_AddP ) { + tty->print_cr("Phi has AddP use"); + } else if( v->Opcode() == Op_CastPP ) { + tty->print_cr("Phi has CastPP use"); + } else if( v->Opcode() == Op_CastII ) { + tty->print_cr("Phi has CastII use"); + } else { + tty->print_cr("Phi has use I cant be bothered with"); + } + */ + } + return NULL; + + /* CNC - Cut out all the fancy acceptance tests + // Can we clone this use when doing the transformation? + // If all uses are from Phis at this merge or constants, then YES. + if( !v->in(0) && v != cmp ) { + tty->print_cr("Phi has free-floating use"); + v->dump(2); + return NULL; + } + for( uint l = 1; l < v->req(); l++ ) { + if( (!v->in(l)->is_Phi() || v->in(l)->in(0) != r) && + !v->in(l)->is_Con() ) { + tty->print_cr("Phi has use"); + v->dump(2); + return NULL; + } // End of if Phi-use input is neither Phi nor Constant + } // End of for all inputs to Phi-use + */ + } // End of for all uses of Phi + } // End of for all uses of Region + + // Only do this if the IF node is in a sane state + if (iff->outcnt() != 2) + return NULL; + + // Got a hit! Do the Mondo Hack! + // + //ABC a1c def ghi B 1 e h A C a c d f g i + // R - Phi - Phi - Phi Rc - Phi - Phi - Phi Rx - Phi - Phi - Phi + // cmp - 2 cmp - 2 cmp - 2 + // bool bool_c bool_x + // if if_c if_x + // T F T F T F + // ..s.. ..t .. ..s.. ..t.. ..s.. ..t.. + // + // Split the paths coming into the merge point into 2 seperate groups of + // merges. On the left will be all the paths feeding constants into the + // Cmp's Phi. On the right will be the remaining paths. The Cmp's Phi + // will fold up into a constant; this will let the Cmp fold up as well as + // all the control flow. Below the original IF we have 2 control + // dependent regions, 's' and 't'. Now we will merge the two paths + // just prior to 's' and 't' from the two IFs. At least 1 path (and quite + // likely 2 or more) will promptly constant fold away. + PhaseGVN *phase = igvn; + + // Make a region merging constants and a region merging the rest + uint req_c = 0; + for (uint ii = 1; ii < r->req(); ii++) { + if( phi->in(ii) == con1 ) { + req_c++; + } + } + Node *region_c = new (igvn->C, req_c + 1) RegionNode(req_c + 1); + Node *phi_c = con1; + uint len = r->req(); + Node *region_x = new (igvn->C, len - req_c + 1) RegionNode(len - req_c + 1); + Node *phi_x = PhiNode::make_blank(region_x, phi); + for (uint i = 1, i_c = 1, i_x = 1; i < len; i++) { + if( phi->in(i) == con1 ) { + region_c->init_req( i_c++, r ->in(i) ); + } else { + region_x->init_req( i_x, r ->in(i) ); + phi_x ->init_req( i_x++, phi->in(i) ); + } + } + + // Register the new RegionNodes but do not transform them. Cannot + // transform until the entire Region/Phi conglerate has been hacked + // as a single huge transform. + igvn->register_new_node_with_optimizer( region_c ); + igvn->register_new_node_with_optimizer( region_x ); + phi_x = phase->transform( phi_x ); + // Prevent the untimely death of phi_x. Currently he has no uses. He is + // about to get one. If this only use goes away, then phi_x will look dead. + // However, he will be picking up some more uses down below. + Node *hook = new (igvn->C, 4) Node(4); + hook->init_req(0, phi_x); + hook->init_req(1, phi_c); + + // Make the compare + Node *cmp_c = phase->makecon(t); + Node *cmp_x = cmp->clone(); + cmp_x->set_req(1,phi_x); + cmp_x->set_req(2,con2); + cmp_x = phase->transform(cmp_x); + // Make the bool + Node *b_c = phase->transform(new (igvn->C, 2) BoolNode(cmp_c,b->_test._test)); + Node *b_x = phase->transform(new (igvn->C, 2) BoolNode(cmp_x,b->_test._test)); + // Make the IfNode + IfNode *iff_c = new (igvn->C, 2) IfNode(region_c,b_c,iff->_prob,iff->_fcnt); + igvn->set_type_bottom(iff_c); + igvn->_worklist.push(iff_c); + hook->init_req(2, iff_c); + + IfNode *iff_x = new (igvn->C, 2) IfNode(region_x,b_x,iff->_prob, iff->_fcnt); + igvn->set_type_bottom(iff_x); + igvn->_worklist.push(iff_x); + hook->init_req(3, iff_x); + + // Make the true/false arms + Node *iff_c_t = phase->transform(new (igvn->C, 1) IfTrueNode (iff_c)); + Node *iff_c_f = phase->transform(new (igvn->C, 1) IfFalseNode(iff_c)); + Node *iff_x_t = phase->transform(new (igvn->C, 1) IfTrueNode (iff_x)); + Node *iff_x_f = phase->transform(new (igvn->C, 1) IfFalseNode(iff_x)); + + // Merge the TRUE paths + Node *region_s = new (igvn->C, 3) RegionNode(3); + igvn->_worklist.push(region_s); + region_s->init_req(1, iff_c_t); + region_s->init_req(2, iff_x_t); + igvn->register_new_node_with_optimizer( region_s ); + + // Merge the FALSE paths + Node *region_f = new (igvn->C, 3) RegionNode(3); + igvn->_worklist.push(region_f); + region_f->init_req(1, iff_c_f); + region_f->init_req(2, iff_x_f); + igvn->register_new_node_with_optimizer( region_f ); + + igvn->hash_delete(cmp);// Remove soon-to-be-dead node from hash table. + cmp->set_req(1,NULL); // Whack the inputs to cmp because it will be dead + cmp->set_req(2,NULL); + // Check for all uses of the Phi and give them a new home. + // The 'cmp' got cloned, but CastPP/IIs need to be moved. + Node *phi_s = NULL; // do not construct unless needed + Node *phi_f = NULL; // do not construct unless needed + for (DUIterator_Last i2min, i2 = phi->last_outs(i2min); i2 >= i2min; --i2) { + Node* v = phi->last_out(i2);// User of the phi + igvn->hash_delete(v); // Have to fixup other Phi users + igvn->_worklist.push(v); + uint vop = v->Opcode(); + Node *proj = NULL; + if( vop == Op_Phi ) { // Remote merge point + Node *r = v->in(0); + for (uint i3 = 1; i3 < r->req(); i3++) + if (r->in(i3) && r->in(i3)->in(0) == iff) { + proj = r->in(i3); + break; + } + } else if( v->is_ConstraintCast() ) { + proj = v->in(0); // Controlling projection + } else { + assert( 0, "do not know how to handle this guy" ); + } + + Node *proj_path_data, *proj_path_ctrl; + if( proj->Opcode() == Op_IfTrue ) { + if( phi_s == NULL ) { + // Only construct phi_s if needed, otherwise provides + // interfering use. + phi_s = PhiNode::make_blank(region_s,phi); + phi_s->init_req( 1, phi_c ); + phi_s->init_req( 2, phi_x ); + phi_s = phase->transform(phi_s); + } + proj_path_data = phi_s; + proj_path_ctrl = region_s; + } else { + if( phi_f == NULL ) { + // Only construct phi_f if needed, otherwise provides + // interfering use. + phi_f = PhiNode::make_blank(region_f,phi); + phi_f->init_req( 1, phi_c ); + phi_f->init_req( 2, phi_x ); + phi_f = phase->transform(phi_f); + } + proj_path_data = phi_f; + proj_path_ctrl = region_f; + } + + // Fixup 'v' for for the split + if( vop == Op_Phi ) { // Remote merge point + uint i; + for( i = 1; i < v->req(); i++ ) + if( v->in(i) == phi ) + break; + v->set_req(i, proj_path_data ); + } else if( v->is_ConstraintCast() ) { + v->set_req(0, proj_path_ctrl ); + v->set_req(1, proj_path_data ); + } else + ShouldNotReachHere(); + } + + // Now replace the original iff's True/False with region_s/region_t. + // This makes the original iff go dead. + for (DUIterator_Last i3min, i3 = iff->last_outs(i3min); i3 >= i3min; --i3) { + Node* p = iff->last_out(i3); + assert( p->Opcode() == Op_IfTrue || p->Opcode() == Op_IfFalse, "" ); + Node *u = (p->Opcode() == Op_IfTrue) ? region_s : region_f; + // Replace p with u + igvn->add_users_to_worklist(p); + for (DUIterator_Last lmin, l = p->last_outs(lmin); l >= lmin;) { + Node* x = p->last_out(l); + igvn->hash_delete(x); + uint uses_found = 0; + for( uint j = 0; j < x->req(); j++ ) { + if( x->in(j) == p ) { + x->set_req(j, u); + uses_found++; + } + } + l -= uses_found; // we deleted 1 or more copies of this edge + } + igvn->remove_dead_node(p); + } + + // Force the original merge dead + igvn->hash_delete(r); + r->set_req_X(0,NULL,igvn); + + // Now remove the bogus extra edges used to keep things alive + igvn->remove_dead_node( hook ); + + // Must return either the original node (now dead) or a new node + // (Do not return a top here, since that would break the uniqueness of top.) + return new (igvn->C, 1) ConINode(TypeInt::ZERO); +} + +//------------------------------is_range_check--------------------------------- +// Return 0 if not a range check. Return 1 if a range check and set index and +// offset. Return 2 if we had to negate the test. Index is NULL if the check +// is versus a constant. +int IfNode::is_range_check(Node* &range, Node* &index, jint &offset) { + Node* b = in(1); + if (b == NULL || !b->is_Bool()) return 0; + BoolNode* bn = b->as_Bool(); + Node* cmp = bn->in(1); + if (cmp == NULL) return 0; + if (cmp->Opcode() != Op_CmpU) return 0; + + Node* l = cmp->in(1); + Node* r = cmp->in(2); + int flip_test = 1; + if (bn->_test._test == BoolTest::le) { + l = cmp->in(2); + r = cmp->in(1); + flip_test = 2; + } else if (bn->_test._test != BoolTest::lt) { + return 0; + } + if (l->is_top()) return 0; // Top input means dead test + if (r->Opcode() != Op_LoadRange) return 0; + + // We have recognized one of these forms: + // Flip 1: If (Bool[<] CmpU(l, LoadRange)) ... + // Flip 2: If (Bool[<=] CmpU(LoadRange, l)) ... + + // Make sure it's a real range check by requiring an uncommon trap + // along the OOB path. Otherwise, it's possible that the user wrote + // something which optimized to look like a range check but behaves + // in some other way. + Node* iftrap = proj_out(flip_test == 2 ? true : false); + bool found_trap = false; + if (iftrap != NULL) { + Node* u = iftrap->unique_ctrl_out(); + if (u != NULL) { + // It could be a merge point (Region) for uncommon trap. + if (u->is_Region()) { + Node* c = u->unique_ctrl_out(); + if (c != NULL) { + iftrap = u; + u = c; + } + } + if (u->in(0) == iftrap && u->is_CallStaticJava()) { + int req = u->as_CallStaticJava()->uncommon_trap_request(); + if (Deoptimization::trap_request_reason(req) == + Deoptimization::Reason_range_check) { + found_trap = true; + } + } + } + } + if (!found_trap) return 0; // sorry, no cigar + + // Look for index+offset form + Node* ind = l; + jint off = 0; + if (l->is_top()) { + return 0; + } else if (l->is_Add()) { + if ((off = l->in(1)->find_int_con(0)) != 0) { + ind = l->in(2); + } else if ((off = l->in(2)->find_int_con(0)) != 0) { + ind = l->in(1); + } + } else if ((off = l->find_int_con(-1)) >= 0) { + // constant offset with no variable index + ind = NULL; + } else { + // variable index with no constant offset (or dead negative index) + off = 0; + } + + // Return all the values: + index = ind; + offset = off; + range = r; + return flip_test; +} + +//------------------------------adjust_check----------------------------------- +// Adjust (widen) a prior range check +static void adjust_check(Node* proj, Node* range, Node* index, + int flip, jint off_lo, PhaseIterGVN* igvn) { + PhaseGVN *gvn = igvn; + // Break apart the old check + Node *iff = proj->in(0); + Node *bol = iff->in(1); + if( bol->is_top() ) return; // In case a partially dead range check appears + // bail (or bomb[ASSERT/DEBUG]) if NOT projection-->IfNode-->BoolNode + DEBUG_ONLY( if( !bol->is_Bool() ) { proj->dump(3); fatal("Expect projection-->IfNode-->BoolNode"); } ) + if( !bol->is_Bool() ) return; + + Node *cmp = bol->in(1); + // Compute a new check + Node *new_add = gvn->intcon(off_lo); + if( index ) { + new_add = off_lo ? gvn->transform(new (gvn->C, 3) AddINode( index, new_add )) : index; + } + Node *new_cmp = (flip == 1) + ? new (gvn->C, 3) CmpUNode( new_add, range ) + : new (gvn->C, 3) CmpUNode( range, new_add ); + new_cmp = gvn->transform(new_cmp); + // See if no need to adjust the existing check + if( new_cmp == cmp ) return; + // Else, adjust existing check + Node *new_bol = gvn->transform( new (gvn->C, 2) BoolNode( new_cmp, bol->as_Bool()->_test._test ) ); + igvn->hash_delete( iff ); + iff->set_req_X( 1, new_bol, igvn ); +} + +//------------------------------up_one_dom------------------------------------- +// Walk up the dominator tree one step. Return NULL at root or true +// complex merges. Skips through small diamonds. +Node* IfNode::up_one_dom(Node *curr, bool linear_only) { + Node *dom = curr->in(0); + if( !dom ) // Found a Region degraded to a copy? + return curr->nonnull_req(); // Skip thru it + + if( curr != dom ) // Normal walk up one step? + return dom; + + // Use linear_only if we are still parsing, since we cannot + // trust the regions to be fully filled in. + if (linear_only) + return NULL; + + // Else hit a Region. Check for a loop header + if( dom->is_Loop() ) + return dom->in(1); // Skip up thru loops + + // Check for small diamonds + Node *din1, *din2, *din3, *din4; + if( dom->req() == 3 && // 2-path merge point + (din1 = dom ->in(1)) && // Left path exists + (din2 = dom ->in(2)) && // Right path exists + (din3 = din1->in(0)) && // Left path up one + (din4 = din2->in(0)) ) { // Right path up one + if( din3->is_Call() && // Handle a slow-path call on either arm + (din3 = din3->in(0)) ) + din3 = din3->in(0); + if( din4->is_Call() && // Handle a slow-path call on either arm + (din4 = din4->in(0)) ) + din4 = din4->in(0); + if( din3 == din4 && din3->is_If() ) + return din3; // Skip around diamonds + } + + // Give up the search at true merges + return NULL; // Dead loop? Or hit root? +} + +//------------------------------remove_useless_bool---------------------------- +// Check for people making a useless boolean: things like +// if( (x < y ? true : false) ) { ... } +// Replace with if( x < y ) { ... } +static Node *remove_useless_bool(IfNode *iff, PhaseGVN *phase) { + Node *i1 = iff->in(1); + if( !i1->is_Bool() ) return NULL; + BoolNode *bol = i1->as_Bool(); + + Node *cmp = bol->in(1); + if( cmp->Opcode() != Op_CmpI ) return NULL; + + // Must be comparing against a bool + const Type *cmp2_t = phase->type( cmp->in(2) ); + if( cmp2_t != TypeInt::ZERO && + cmp2_t != TypeInt::ONE ) + return NULL; + + // Find a prior merge point merging the boolean + i1 = cmp->in(1); + if( !i1->is_Phi() ) return NULL; + PhiNode *phi = i1->as_Phi(); + if( phase->type( phi ) != TypeInt::BOOL ) + return NULL; + + // Check for diamond pattern + int true_path = phi->is_diamond_phi(); + if( true_path == 0 ) return NULL; + + // phi->region->if_proj->ifnode->bool->cmp + BoolNode *bol2 = phi->in(0)->in(1)->in(0)->in(1)->as_Bool(); + + // Now get the 'sense' of the test correct so we can plug in + // either iff2->in(1) or its complement. + int flip = 0; + if( bol->_test._test == BoolTest::ne ) flip = 1-flip; + else if( bol->_test._test != BoolTest::eq ) return NULL; + if( cmp2_t == TypeInt::ZERO ) flip = 1-flip; + + const Type *phi1_t = phase->type( phi->in(1) ); + const Type *phi2_t = phase->type( phi->in(2) ); + // Check for Phi(0,1) and flip + if( phi1_t == TypeInt::ZERO ) { + if( phi2_t != TypeInt::ONE ) return NULL; + flip = 1-flip; + } else { + // Check for Phi(1,0) + if( phi1_t != TypeInt::ONE ) return NULL; + if( phi2_t != TypeInt::ZERO ) return NULL; + } + if( true_path == 2 ) { + flip = 1-flip; + } + + Node* new_bol = (flip ? phase->transform( bol2->negate(phase) ) : bol2); + iff->set_req(1, new_bol); + // Intervening diamond probably goes dead + phase->C->set_major_progress(); + return iff; +} + +static IfNode* idealize_test(PhaseGVN* phase, IfNode* iff); + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if (remove_dead_region(phase, can_reshape)) return this; + // No Def-Use info? + if (!can_reshape) return NULL; + PhaseIterGVN *igvn = phase->is_IterGVN(); + + // Don't bother trying to transform a dead if + if (in(0)->is_top()) return NULL; + // Don't bother trying to transform an if with a dead test + if (in(1)->is_top()) return NULL; + // Another variation of a dead test + if (in(1)->is_Con()) return NULL; + // Another variation of a dead if + if (outcnt() < 2) return NULL; + + // Canonicalize the test. + Node* idt_if = idealize_test(phase, this); + if (idt_if != NULL) return idt_if; + + // Try to split the IF + Node *s = split_if(this, igvn); + if (s != NULL) return s; + + // Check for people making a useless boolean: things like + // if( (x < y ? true : false) ) { ... } + // Replace with if( x < y ) { ... } + Node *bol2 = remove_useless_bool(this, phase); + if( bol2 ) return bol2; + + // Setup to scan up the CFG looking for a dominating test + Node *dom = in(0); + Node *prev_dom = this; + + // Check for range-check vs other kinds of tests + Node *index1, *range1; + jint offset1; + int flip1 = is_range_check(range1, index1, offset1); + if( flip1 ) { + Node *first_prev_dom = NULL; + + // Try to remove extra range checks. All 'up_one_dom' gives up at merges + // so all checks we inspect post-dominate the top-most check we find. + // If we are going to fail the current check and we reach the top check + // then we are guarenteed to fail, so just start interpreting there. + // We 'expand' the top 2 range checks to include all post-dominating + // checks. + + // The top 2 range checks seen + Node *prev_chk1 = NULL; + Node *prev_chk2 = NULL; + // Low and high offsets seen so far + jint off_lo = offset1; + jint off_hi = offset1; + + // Scan for the top 2 checks and collect range of offsets + for( int dist = 0; dist < 999; dist++ ) { // Range-Check scan limit + if( dom->Opcode() == Op_If && // Not same opcode? + prev_dom->in(0) == dom ) { // One path of test does dominate? + if( dom == this ) return NULL; // dead loop + // See if this is a range check + Node *index2, *range2; + jint offset2; + int flip2 = dom->as_If()->is_range_check(range2, index2, offset2); + // See if this is a _matching_ range check, checking against + // the same array bounds. + if( flip2 == flip1 && range2 == range1 && index2 == index1 && + dom->outcnt() == 2 ) { + // Gather expanded bounds + off_lo = MIN2(off_lo,offset2); + off_hi = MAX2(off_hi,offset2); + // Record top 2 range checks + prev_chk2 = prev_chk1; + prev_chk1 = prev_dom; + // If we match the test exactly, then the top test covers + // both our lower and upper bounds. + if( dom->in(1) == in(1) ) + prev_chk2 = prev_chk1; + } + } + prev_dom = dom; + dom = up_one_dom( dom ); + if( !dom ) break; + } + + + // Attempt to widen the dominating range check to cover some later + // ones. Since range checks "fail" by uncommon-trapping to the + // interpreter, widening a check can make us speculative enter the + // interpreter. If we see range-check deopt's, do not widen! + if (!phase->C->allow_range_check_smearing()) return NULL; + + // Constant indices only need to check the upper bound. + // Non-constance indices must check both low and high. + if( index1 ) { + // Didn't find 2 prior covering checks, so cannot remove anything. + if( !prev_chk2 ) return NULL; + // 'Widen' the offsets of the 1st and 2nd covering check + adjust_check( prev_chk1, range1, index1, flip1, off_lo, igvn ); + // Do not call adjust_check twice on the same projection + // as the first call may have transformed the BoolNode to a ConI + if( prev_chk1 != prev_chk2 ) { + adjust_check( prev_chk2, range1, index1, flip1, off_hi, igvn ); + } + // Test is now covered by prior checks, dominate it out + prev_dom = prev_chk2; + } else { + // Didn't find prior covering check, so cannot remove anything. + if( !prev_chk1 ) return NULL; + // 'Widen' the offset of the 1st and only covering check + adjust_check( prev_chk1, range1, index1, flip1, off_hi, igvn ); + // Test is now covered by prior checks, dominate it out + prev_dom = prev_chk1; + } + + + } else { // Scan for an equivalent test + + Node *cmp; + int dist = 0; // Cutoff limit for search + int op = Opcode(); + if( op == Op_If && + (cmp=in(1)->in(1))->Opcode() == Op_CmpP ) { + if( cmp->in(2) != NULL && // make sure cmp is not already dead + cmp->in(2)->bottom_type() == TypePtr::NULL_PTR ) { + dist = 64; // Limit for null-pointer scans + } else { + dist = 4; // Do not bother for random pointer tests + } + } else { + dist = 4; // Limit for random junky scans + } + + // Normal equivalent-test check. + if( !dom ) return NULL; // Dead loop? + + // Search up the dominator tree for an If with an identical test + while( dom->Opcode() != op || // Not same opcode? + dom->in(1) != in(1) || // Not same input 1? + (req() == 3 && dom->in(2) != in(2)) || // Not same input 2? + prev_dom->in(0) != dom ) { // One path of test does not dominate? + if( dist < 0 ) return NULL; + + dist--; + prev_dom = dom; + dom = up_one_dom( dom ); + if( !dom ) return NULL; + } + + // Check that we did not follow a loop back to ourselves + if( this == dom ) + return NULL; + + if( dist > 2 ) // Add to count of NULL checks elided + explicit_null_checks_elided++; + + } // End of Else scan for an equivalent test + + // Hit! Remove this IF +#ifndef PRODUCT + if( TraceIterativeGVN ) { + tty->print(" Removing IfNode: "); this->dump(); + } + if( VerifyOpto && !phase->allow_progress() ) { + // Found an equivalent dominating test, + // we can not guarantee reaching a fix-point for these during iterativeGVN + // since intervening nodes may not change. + return NULL; + } +#endif + + // Replace dominated IfNode + dominated_by( prev_dom, igvn ); + + // Must return either the original node (now dead) or a new node + // (Do not return a top here, since that would break the uniqueness of top.) + return new (phase->C, 1) ConINode(TypeInt::ZERO); +} + +//------------------------------dominated_by----------------------------------- +void IfNode::dominated_by( Node *prev_dom, PhaseIterGVN *igvn ) { + igvn->hash_delete(this); // Remove self to prevent spurious V-N + Node *idom = in(0); + // Need opcode to decide which way 'this' test goes + int prev_op = prev_dom->Opcode(); + Node *top = igvn->C->top(); // Shortcut to top + + // Now walk the current IfNode's projections. + // Loop ends when 'this' has no more uses. + for (DUIterator_Last imin, i = last_outs(imin); i >= imin; --i) { + Node *ifp = last_out(i); // Get IfTrue/IfFalse + igvn->add_users_to_worklist(ifp); + // Check which projection it is and set target. + // Data-target is either the dominating projection of the same type + // or TOP if the dominating projection is of opposite type. + // Data-target will be used as the new control edge for the non-CFG + // nodes like Casts and Loads. + Node *data_target = (ifp->Opcode() == prev_op ) ? prev_dom : top; + // Control-target is just the If's immediate dominator or TOP. + Node *ctrl_target = (ifp->Opcode() == prev_op ) ? idom : top; + + // For each child of an IfTrue/IfFalse projection, reroute. + // Loop ends when projection has no more uses. + for (DUIterator_Last jmin, j = ifp->last_outs(jmin); j >= jmin; --j) { + Node* s = ifp->last_out(j); // Get child of IfTrue/IfFalse + igvn->hash_delete(s); // Yank from hash table before edge hacking + if( !s->depends_only_on_test() ) { + // Find the control input matching this def-use edge. + // For Regions it may not be in slot 0. + uint l; + for( l = 0; s->in(l) != ifp; l++ ) { } + s->set_req(l, ctrl_target); + } else { // Else, for control producers, + s->set_req(0, data_target); // Move child to data-target + } + igvn->_worklist.push(s); // Revisit collapsed Phis + } // End for each child of a projection + + igvn->remove_dead_node(ifp); + } // End for each IfTrue/IfFalse child of If + + // Kill the IfNode + igvn->remove_dead_node(this); +} + +//------------------------------Identity--------------------------------------- +// If the test is constant & we match, then we are the input Control +Node *IfTrueNode::Identity( PhaseTransform *phase ) { + // Can only optimize if cannot go the other way + const TypeTuple *t = phase->type(in(0))->is_tuple(); + return ( t == TypeTuple::IFNEITHER || t == TypeTuple::IFTRUE ) + ? in(0)->in(0) // IfNode control + : this; // no progress +} + +//------------------------------dump_spec-------------------------------------- +#ifndef PRODUCT +void IfNode::dump_spec(outputStream *st) const { + st->print("P=%f, C=%f",_prob,_fcnt); +} +#endif + +//------------------------------idealize_test---------------------------------- +// Try to canonicalize tests better. Peek at the Cmp/Bool/If sequence and +// come up with a canonical sequence. Bools getting 'eq', 'gt' and 'ge' forms +// converted to 'ne', 'le' and 'lt' forms. IfTrue/IfFalse get swapped as +// needed. +static IfNode* idealize_test(PhaseGVN* phase, IfNode* iff) { + assert(iff->in(0) != NULL, "If must be live"); + + if (iff->outcnt() != 2) return NULL; // Malformed projections. + Node* old_if_f = iff->proj_out(false); + Node* old_if_t = iff->proj_out(true); + + // CountedLoopEnds want the back-control test to be TRUE, irregardless of + // whether they are testing a 'gt' or 'lt' condition. The 'gt' condition + // happens in count-down loops + if (iff->is_CountedLoopEnd()) return NULL; + if (!iff->in(1)->is_Bool()) return NULL; // Happens for partially optimized IF tests + BoolNode *b = iff->in(1)->as_Bool(); + BoolTest bt = b->_test; + // Test already in good order? + if( bt.is_canonical() ) + return NULL; + + // Flip test to be canonical. Requires flipping the IfFalse/IfTrue and + // cloning the IfNode. + Node* new_b = phase->transform( new (phase->C, 2) BoolNode(b->in(1), bt.negate()) ); + if( !new_b->is_Bool() ) return NULL; + b = new_b->as_Bool(); + + PhaseIterGVN *igvn = phase->is_IterGVN(); + assert( igvn, "Test is not canonical in parser?" ); + + // The IF node never really changes, but it needs to be cloned + iff = new (phase->C, 2) IfNode( iff->in(0), b, 1.0-iff->_prob, iff->_fcnt); + + Node *prior = igvn->hash_find_insert(iff); + if( prior ) { + igvn->remove_dead_node(iff); + iff = (IfNode*)prior; + } else { + // Cannot call transform on it just yet + igvn->set_type_bottom(iff); + } + igvn->_worklist.push(iff); + + // Now handle projections. Cloning not required. + Node* new_if_f = (Node*)(new (phase->C, 1) IfFalseNode( iff )); + Node* new_if_t = (Node*)(new (phase->C, 1) IfTrueNode ( iff )); + + igvn->register_new_node_with_optimizer(new_if_f); + igvn->register_new_node_with_optimizer(new_if_t); + igvn->hash_delete(old_if_f); + igvn->hash_delete(old_if_t); + // Flip test, so flip trailing control + igvn->subsume_node(old_if_f, new_if_t); + igvn->subsume_node(old_if_t, new_if_f); + + // Progress + return iff; +} + +//------------------------------Identity--------------------------------------- +// If the test is constant & we match, then we are the input Control +Node *IfFalseNode::Identity( PhaseTransform *phase ) { + // Can only optimize if cannot go the other way + const TypeTuple *t = phase->type(in(0))->is_tuple(); + return ( t == TypeTuple::IFNEITHER || t == TypeTuple::IFFALSE ) + ? in(0)->in(0) // IfNode control + : this; // no progress +} diff --git a/hotspot/src/share/vm/opto/indexSet.cpp b/hotspot/src/share/vm/opto/indexSet.cpp new file mode 100644 index 00000000000..078315bee52 --- /dev/null +++ b/hotspot/src/share/vm/opto/indexSet.cpp @@ -0,0 +1,573 @@ +/* + * Copyright 1998-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file defines the IndexSet class, a set of sparse integer indices. +// This data structure is used by the compiler in its liveness analysis and +// during register allocation. It also defines an iterator for this class. + +#include "incls/_precompiled.incl" +#include "incls/_indexSet.cpp.incl" + +//-------------------------------- Initializations ------------------------------ + +IndexSet::BitBlock IndexSet::_empty_block = IndexSet::BitBlock(); + +#ifdef ASSERT +// Initialize statistics counters +uint IndexSet::_alloc_new = 0; +uint IndexSet::_alloc_total = 0; + +long IndexSet::_total_bits = 0; +long IndexSet::_total_used_blocks = 0; +long IndexSet::_total_unused_blocks = 0; + +// Per set, or all sets operation tracing +int IndexSet::_serial_count = 1; +#endif + +// What is the first set bit in a 5 bit integer? +const byte IndexSetIterator::_first_bit[32] = { + 0, 0, 1, 0, + 2, 0, 1, 0, + 3, 0, 1, 0, + 2, 0, 1, 0, + 4, 0, 1, 0, + 2, 0, 1, 0, + 3, 0, 1, 0, + 2, 0, 1, 0 +}; + +// What is the second set bit in a 5 bit integer? +const byte IndexSetIterator::_second_bit[32] = { + 5, 5, 5, 1, + 5, 2, 2, 1, + 5, 3, 3, 1, + 3, 2, 2, 1, + 5, 4, 4, 1, + 4, 2, 2, 1, + 4, 3, 3, 1, + 3, 2, 2, 1 +}; + +// I tried implementing the IndexSetIterator with a window_size of 8 and +// didn't seem to get a noticeable speedup. I am leaving in the tables +// in case we want to switch back. + +/*const byte IndexSetIterator::_first_bit[256] = { + 8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +const byte IndexSetIterator::_second_bit[256] = { + 8, 8, 8, 1, 8, 2, 2, 1, 8, 3, 3, 1, 3, 2, 2, 1, + 8, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, + 8, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1, + 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, + 8, 6, 6, 1, 6, 2, 2, 1, 6, 3, 3, 1, 3, 2, 2, 1, + 6, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, + 6, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1, + 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, + 8, 7, 7, 1, 7, 2, 2, 1, 7, 3, 3, 1, 3, 2, 2, 1, + 7, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, + 7, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1, + 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, + 7, 6, 6, 1, 6, 2, 2, 1, 6, 3, 3, 1, 3, 2, 2, 1, + 6, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, + 6, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1, + 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1 +};*/ + +//---------------------------- IndexSet::populate_free_list() ----------------------------- +// Populate the free BitBlock list with a batch of BitBlocks. The BitBlocks +// are 32 bit aligned. + +void IndexSet::populate_free_list() { + Compile *compile = Compile::current(); + BitBlock *free = (BitBlock*)compile->indexSet_free_block_list(); + + char *mem = (char*)arena()->Amalloc_4(sizeof(BitBlock) * + bitblock_alloc_chunk_size + 32); + + // Align the pointer to a 32 bit boundary. + BitBlock *new_blocks = (BitBlock*)(((uintptr_t)mem + 32) & ~0x001F); + + // Add the new blocks to the free list. + for (int i = 0; i < bitblock_alloc_chunk_size; i++) { + new_blocks->set_next(free); + free = new_blocks; + new_blocks++; + } + + compile->set_indexSet_free_block_list(free); + +#ifdef ASSERT + if (CollectIndexSetStatistics) { + _alloc_new += bitblock_alloc_chunk_size; + } +#endif +} + + +//---------------------------- IndexSet::alloc_block() ------------------------ +// Allocate a BitBlock from the free list. If the free list is empty, +// prime it. + +IndexSet::BitBlock *IndexSet::alloc_block() { +#ifdef ASSERT + if (CollectIndexSetStatistics) { + _alloc_total++; + } +#endif + Compile *compile = Compile::current(); + BitBlock* free_list = (BitBlock*)compile->indexSet_free_block_list(); + if (free_list == NULL) { + populate_free_list(); + free_list = (BitBlock*)compile->indexSet_free_block_list(); + } + BitBlock *block = free_list; + compile->set_indexSet_free_block_list(block->next()); + + block->clear(); + return block; +} + +//---------------------------- IndexSet::alloc_block_containing() ------------- +// Allocate a new BitBlock and put it into the position in the _blocks array +// corresponding to element. + +IndexSet::BitBlock *IndexSet::alloc_block_containing(uint element) { + BitBlock *block = alloc_block(); + uint bi = get_block_index(element); + _blocks[bi] = block; + return block; +} + +//---------------------------- IndexSet::free_block() ------------------------- +// Add a BitBlock to the free list. + +void IndexSet::free_block(uint i) { + debug_only(check_watch("free block", i)); + assert(i < _max_blocks, "block index too large"); + BitBlock *block = _blocks[i]; + assert(block != &_empty_block, "cannot free the empty block"); + block->set_next((IndexSet::BitBlock*)Compile::current()->indexSet_free_block_list()); + Compile::current()->set_indexSet_free_block_list(block); + set_block(i,&_empty_block); +} + +//------------------------------lrg_union-------------------------------------- +// Compute the union of all elements of one and two which interfere with +// the RegMask mask. If the degree of the union becomes exceeds +// fail_degree, the union bails out. The underlying set is cleared before +// the union is performed. + +uint IndexSet::lrg_union(uint lr1, uint lr2, + const uint fail_degree, + const PhaseIFG *ifg, + const RegMask &mask ) { + IndexSet *one = ifg->neighbors(lr1); + IndexSet *two = ifg->neighbors(lr2); + LRG &lrg1 = ifg->lrgs(lr1); + LRG &lrg2 = ifg->lrgs(lr2); +#ifdef ASSERT + assert(_max_elements == one->_max_elements, "max element mismatch"); + check_watch("union destination"); + one->check_watch("union source"); + two->check_watch("union source"); +#endif + + // Compute the degree of the combined live-range. The combined + // live-range has the union of the original live-ranges' neighbors set as + // well as the neighbors of all intermediate copies, minus those neighbors + // that can not use the intersected allowed-register-set. + + // Copy the larger set. Insert the smaller set into the larger. + if (two->count() > one->count()) { + IndexSet *temp = one; + one = two; + two = temp; + } + + clear(); + + // Used to compute degree of register-only interferences. Infinite-stack + // neighbors do not alter colorability, as they can always color to some + // other color. (A variant of the Briggs assertion) + uint reg_degree = 0; + + uint element; + // Load up the combined interference set with the neighbors of one + IndexSetIterator elements(one); + while ((element = elements.next()) != 0) { + LRG &lrg = ifg->lrgs(element); + if (mask.overlap(lrg.mask())) { + insert(element); + if( !lrg.mask().is_AllStack() ) { + reg_degree += lrg1.compute_degree(lrg); + if( reg_degree >= fail_degree ) return reg_degree; + } else { + // !!!!! Danger! No update to reg_degree despite having a neighbor. + // A variant of the Briggs assertion. + // Not needed if I simplify during coalesce, ala George/Appel. + assert( lrg.lo_degree(), "" ); + } + } + } + // Add neighbors of two as well + IndexSetIterator elements2(two); + while ((element = elements2.next()) != 0) { + LRG &lrg = ifg->lrgs(element); + if (mask.overlap(lrg.mask())) { + if (insert(element)) { + if( !lrg.mask().is_AllStack() ) { + reg_degree += lrg2.compute_degree(lrg); + if( reg_degree >= fail_degree ) return reg_degree; + } else { + // !!!!! Danger! No update to reg_degree despite having a neighbor. + // A variant of the Briggs assertion. + // Not needed if I simplify during coalesce, ala George/Appel. + assert( lrg.lo_degree(), "" ); + } + } + } + } + + return reg_degree; +} + +//---------------------------- IndexSet() ----------------------------- +// A deep copy constructor. This is used when you need a scratch copy of this set. + +IndexSet::IndexSet (IndexSet *set) { +#ifdef ASSERT + _serial_number = _serial_count++; + set->check_watch("copied", _serial_number); + check_watch("initialized by copy", set->_serial_number); + _max_elements = set->_max_elements; +#endif + _count = set->_count; + _max_blocks = set->_max_blocks; + if (_max_blocks <= preallocated_block_list_size) { + _blocks = _preallocated_block_list; + } else { + _blocks = + (IndexSet::BitBlock**) arena()->Amalloc_4(sizeof(IndexSet::BitBlock**) * _max_blocks); + } + for (uint i = 0; i < _max_blocks; i++) { + BitBlock *block = set->_blocks[i]; + if (block == &_empty_block) { + set_block(i, &_empty_block); + } else { + BitBlock *new_block = alloc_block(); + memcpy(new_block->words(), block->words(), sizeof(uint32) * words_per_block); + set_block(i, new_block); + } + } +} + +//---------------------------- IndexSet::initialize() ----------------------------- +// Prepare an IndexSet for use. + +void IndexSet::initialize(uint max_elements) { +#ifdef ASSERT + _serial_number = _serial_count++; + check_watch("initialized", max_elements); + _max_elements = max_elements; +#endif + _count = 0; + _max_blocks = (max_elements + bits_per_block - 1) / bits_per_block; + + if (_max_blocks <= preallocated_block_list_size) { + _blocks = _preallocated_block_list; + } else { + _blocks = (IndexSet::BitBlock**) arena()->Amalloc_4(sizeof(IndexSet::BitBlock**) * _max_blocks); + } + for (uint i = 0; i < _max_blocks; i++) { + set_block(i, &_empty_block); + } +} + +//---------------------------- IndexSet::initialize()------------------------------ +// Prepare an IndexSet for use. If it needs to allocate its _blocks array, it does +// so from the Arena passed as a parameter. BitBlock allocation is still done from +// the static Arena which was set with reset_memory(). + +void IndexSet::initialize(uint max_elements, Arena *arena) { +#ifdef ASSERT + _serial_number = _serial_count++; + check_watch("initialized2", max_elements); + _max_elements = max_elements; +#endif // ASSERT + _count = 0; + _max_blocks = (max_elements + bits_per_block - 1) / bits_per_block; + + if (_max_blocks <= preallocated_block_list_size) { + _blocks = _preallocated_block_list; + } else { + _blocks = (IndexSet::BitBlock**) arena->Amalloc_4(sizeof(IndexSet::BitBlock**) * _max_blocks); + } + for (uint i = 0; i < _max_blocks; i++) { + set_block(i, &_empty_block); + } +} + +//---------------------------- IndexSet::swap() ----------------------------- +// Exchange two IndexSets. + +void IndexSet::swap(IndexSet *set) { +#ifdef ASSERT + assert(_max_elements == set->_max_elements, "must have same universe size to swap"); + check_watch("swap", set->_serial_number); + set->check_watch("swap", _serial_number); +#endif + + for (uint i = 0; i < _max_blocks; i++) { + BitBlock *temp = _blocks[i]; + set_block(i, set->_blocks[i]); + set->set_block(i, temp); + } + uint temp = _count; + _count = set->_count; + set->_count = temp; +} + +//---------------------------- IndexSet::dump() ----------------------------- +// Print this set. Used for debugging. + +#ifndef PRODUCT +void IndexSet::dump() const { + IndexSetIterator elements(this); + + tty->print("{"); + uint i; + while ((i = elements.next()) != 0) { + tty->print("L%d ", i); + } + tty->print_cr("}"); +} +#endif + +#ifdef ASSERT +//---------------------------- IndexSet::tally_iteration_statistics() ----------------------------- +// Update block/bit counts to reflect that this set has been iterated over. + +void IndexSet::tally_iteration_statistics() const { + _total_bits += count(); + + for (uint i = 0; i < _max_blocks; i++) { + if (_blocks[i] != &_empty_block) { + _total_used_blocks++; + } else { + _total_unused_blocks++; + } + } +} + +//---------------------------- IndexSet::print_statistics() ----------------------------- +// Print statistics about IndexSet usage. + +void IndexSet::print_statistics() { + long total_blocks = _total_used_blocks + _total_unused_blocks; + tty->print_cr ("Accumulated IndexSet usage statistics:"); + tty->print_cr ("--------------------------------------"); + tty->print_cr (" Iteration:"); + tty->print_cr (" blocks visited: %d", total_blocks); + tty->print_cr (" blocks empty: %4.2f%%", 100.0*_total_unused_blocks/total_blocks); + tty->print_cr (" bit density (bits/used blocks): %4.2f%%", (double)_total_bits/_total_used_blocks); + tty->print_cr (" bit density (bits/all blocks): %4.2f%%", (double)_total_bits/total_blocks); + tty->print_cr (" Allocation:"); + tty->print_cr (" blocks allocated: %d", _alloc_new); + tty->print_cr (" blocks used/reused: %d", _alloc_total); +} + +//---------------------------- IndexSet::verify() ----------------------------- +// Expensive test of IndexSet sanity. Ensure that the count agrees with the +// number of bits in the blocks. Make sure the iterator is seeing all elements +// of the set. Meant for use during development. + +void IndexSet::verify() const { + assert(!member(0), "zero cannot be a member"); + uint count = 0; + uint i; + for (i = 1; i < _max_elements; i++) { + if (member(i)) { + count++; + assert(count <= _count, "_count is messed up"); + } + } + + IndexSetIterator elements(this); + count = 0; + while ((i = elements.next()) != 0) { + count++; + assert(member(i), "returned a non member"); + assert(count <= _count, "iterator returned wrong number of elements"); + } +} +#endif + +//---------------------------- IndexSetIterator() ----------------------------- +// Create an iterator for a set. If empty blocks are detected when iterating +// over the set, these blocks are replaced. + +IndexSetIterator::IndexSetIterator(IndexSet *set) { +#ifdef ASSERT + if (CollectIndexSetStatistics) { + set->tally_iteration_statistics(); + } + set->check_watch("traversed", set->count()); +#endif + if (set->is_empty()) { + _current = 0; + _next_word = IndexSet::words_per_block; + _next_block = 1; + _max_blocks = 1; + + // We don't need the following values when we iterate over an empty set. + // The commented out code is left here to document that the omission + // is intentional. + // + //_value = 0; + //_words = NULL; + //_blocks = NULL; + //_set = NULL; + } else { + _current = 0; + _value = 0; + _next_block = 0; + _next_word = IndexSet::words_per_block; + + _max_blocks = set->_max_blocks; + _words = NULL; + _blocks = set->_blocks; + _set = set; + } +} + +//---------------------------- IndexSetIterator(const) ----------------------------- +// Iterate over a constant IndexSet. + +IndexSetIterator::IndexSetIterator(const IndexSet *set) { +#ifdef ASSERT + if (CollectIndexSetStatistics) { + set->tally_iteration_statistics(); + } + // We don't call check_watch from here to avoid bad recursion. + // set->check_watch("traversed const", set->count()); +#endif + if (set->is_empty()) { + _current = 0; + _next_word = IndexSet::words_per_block; + _next_block = 1; + _max_blocks = 1; + + // We don't need the following values when we iterate over an empty set. + // The commented out code is left here to document that the omission + // is intentional. + // + //_value = 0; + //_words = NULL; + //_blocks = NULL; + //_set = NULL; + } else { + _current = 0; + _value = 0; + _next_block = 0; + _next_word = IndexSet::words_per_block; + + _max_blocks = set->_max_blocks; + _words = NULL; + _blocks = set->_blocks; + _set = NULL; + } +} + +//---------------------------- List16Iterator::advance_and_next() ----------------------------- +// Advance to the next non-empty word in the set being iterated over. Return the next element +// if there is one. If we are done, return 0. This method is called from the next() method +// when it gets done with a word. + +uint IndexSetIterator::advance_and_next() { + // See if there is another non-empty word in the current block. + for (uint wi = _next_word; wi < (unsigned)IndexSet::words_per_block; wi++) { + if (_words[wi] != 0) { + // Found a non-empty word. + _value = ((_next_block - 1) * IndexSet::bits_per_block) + (wi * IndexSet::bits_per_word); + _current = _words[wi]; + + _next_word = wi+1; + + return next(); + } + } + + // We ran out of words in the current block. Advance to next non-empty block. + for (uint bi = _next_block; bi < _max_blocks; bi++) { + if (_blocks[bi] != &IndexSet::_empty_block) { + // Found a non-empty block. + + _words = _blocks[bi]->words(); + for (uint wi = 0; wi < (unsigned)IndexSet::words_per_block; wi++) { + if (_words[wi] != 0) { + // Found a non-empty word. + _value = (bi * IndexSet::bits_per_block) + (wi * IndexSet::bits_per_word); + _current = _words[wi]; + + _next_block = bi+1; + _next_word = wi+1; + + return next(); + } + } + + // All of the words in the block were empty. Replace + // the block with the empty block. + if (_set) { + _set->free_block(bi); + } + } + } + + // These assignments make redundant calls to next on a finished iterator + // faster. Probably not necessary. + _next_block = _max_blocks; + _next_word = IndexSet::words_per_block; + + // No more words. + return 0; +} diff --git a/hotspot/src/share/vm/opto/indexSet.hpp b/hotspot/src/share/vm/opto/indexSet.hpp new file mode 100644 index 00000000000..de7de22aa84 --- /dev/null +++ b/hotspot/src/share/vm/opto/indexSet.hpp @@ -0,0 +1,461 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file defines the IndexSet class, a set of sparse integer indices. +// This data structure is used by the compiler in its liveness analysis and +// during register allocation. + +//-------------------------------- class IndexSet ---------------------------- +// An IndexSet is a piece-wise bitvector. At the top level, we have an array +// of pointers to bitvector chunks called BitBlocks. Each BitBlock has a fixed +// size and is allocated from a shared free list. The bits which are set in +// each BitBlock correspond to the elements of the set. + +class IndexSet : public ResourceObj { + friend class IndexSetIterator; + + public: + // When we allocate an IndexSet, it starts off with an array of top level block + // pointers of a set length. This size is intended to be large enough for the + // majority of IndexSets. In the cases when this size is not large enough, + // a separately allocated array is used. + + // The length of the preallocated top level block array + enum { preallocated_block_list_size = 16 }; + + // Elements of a IndexSet get decomposed into three fields. The highest order + // bits are the block index, which tell which high level block holds the element. + // Within that block, the word index indicates which word holds the element. + // Finally, the bit index determines which single bit within that word indicates + // membership of the element in the set. + + // The lengths of the index bitfields + enum { bit_index_length = 5, + word_index_length = 3, + block_index_length = 8 // not used + }; + + // Derived constants used for manipulating the index bitfields + enum { + bit_index_offset = 0, // not used + word_index_offset = bit_index_length, + block_index_offset = bit_index_length + word_index_length, + + bits_per_word = 1 << bit_index_length, + words_per_block = 1 << word_index_length, + bits_per_block = bits_per_word * words_per_block, + + bit_index_mask = right_n_bits(bit_index_length), + word_index_mask = right_n_bits(word_index_length) + }; + + // These routines are used for extracting the block, word, and bit index + // from an element. + static uint get_block_index(uint element) { + return element >> block_index_offset; + } + static uint get_word_index(uint element) { + return mask_bits(element >> word_index_offset,word_index_mask); + } + static uint get_bit_index(uint element) { + return mask_bits(element,bit_index_mask); + } + + //------------------------------ class BitBlock ---------------------------- + // The BitBlock class is a segment of a bitvector set. + + class BitBlock : public ResourceObj { + friend class IndexSetIterator; + friend class IndexSet; + + private: + // All of BitBlocks fields and methods are declared private. We limit + // access to IndexSet and IndexSetIterator. + + // A BitBlock is composed of some number of 32 bit words. When a BitBlock + // is not in use by any IndexSet, it is stored on a free list. The next field + // is used by IndexSet to mainting this free list. + + union { + uint32 _words[words_per_block]; + BitBlock *_next; + } _data; + + // accessors + uint32 *words() { return _data._words; } + void set_next(BitBlock *next) { _data._next = next; } + BitBlock *next() { return _data._next; } + + // Operations. A BitBlock supports four simple operations, + // clear(), member(), insert(), and remove(). These methods do + // not assume that the block index has been masked out. + + void clear() { + memset(words(), 0, sizeof(uint32) * words_per_block); + } + + bool member(uint element) { + uint word_index = IndexSet::get_word_index(element); + uint bit_index = IndexSet::get_bit_index(element); + + return ((words()[word_index] & (uint32)(0x1 << bit_index)) != 0); + } + + bool insert(uint element) { + uint word_index = IndexSet::get_word_index(element); + uint bit_index = IndexSet::get_bit_index(element); + + uint32 bit = (0x1 << bit_index); + uint32 before = words()[word_index]; + words()[word_index] = before | bit; + return ((before & bit) != 0); + } + + bool remove(uint element) { + uint word_index = IndexSet::get_word_index(element); + uint bit_index = IndexSet::get_bit_index(element); + + uint32 bit = (0x1 << bit_index); + uint32 before = words()[word_index]; + words()[word_index] = before & ~bit; + return ((before & bit) != 0); + } + }; + + //-------------------------- BitBlock allocation --------------------------- + private: + + // All IndexSets share an arena from which they allocate BitBlocks. Unused + // BitBlocks are placed on a free list. + + // The number of BitBlocks to allocate at a time + enum { bitblock_alloc_chunk_size = 50 }; + + static Arena *arena() { return Compile::current()->indexSet_arena(); } + + static void populate_free_list(); + + public: + + // Invalidate the current free BitBlock list and begin allocation + // from a new arena. It is essential that this method is called whenever + // the Arena being used for BitBlock allocation is reset. + static void reset_memory(Compile* compile, Arena *arena) { + compile->set_indexSet_free_block_list(NULL); + compile->set_indexSet_arena(arena); + + // This should probably be done in a static initializer + _empty_block.clear(); + } + + private: + friend class BitBlock; + // A distinguished BitBlock which always remains empty. When a new IndexSet is + // created, all of its top level BitBlock pointers are initialized to point to + // this. + static BitBlock _empty_block; + + //-------------------------- Members ------------------------------------------ + + // The number of elements in the set + uint _count; + + // Our top level array of bitvector segments + BitBlock **_blocks; + + BitBlock *_preallocated_block_list[preallocated_block_list_size]; + + // The number of top level array entries in use + uint _max_blocks; + + // Our assertions need to know the maximum number allowed in the set +#ifdef ASSERT + uint _max_elements; +#endif + + // The next IndexSet on the free list (not used at same time as count) + IndexSet *_next; + + public: + //-------------------------- Free list operations ------------------------------ + // Individual IndexSets can be placed on a free list. This is done in PhaseLive. + + IndexSet *next() { +#ifdef ASSERT + if( VerifyOpto ) { + check_watch("removed from free list?", ((_next == NULL) ? 0 : _next->_serial_number)); + } +#endif + return _next; + } + + void set_next(IndexSet *next) { +#ifdef ASSERT + if( VerifyOpto ) { + check_watch("put on free list?", ((next == NULL) ? 0 : next->_serial_number)); + } +#endif + _next = next; + } + + private: + //-------------------------- Utility methods ----------------------------------- + + // Get the block which holds element + BitBlock *get_block_containing(uint element) const { + assert(element < _max_elements, "element out of bounds"); + return _blocks[get_block_index(element)]; + } + + // Set a block in the top level array + void set_block(uint index, BitBlock *block) { +#ifdef ASSERT + if( VerifyOpto ) + check_watch("set block", index); +#endif + _blocks[index] = block; + } + + // Get a BitBlock from the free list + BitBlock *alloc_block(); + + // Get a BitBlock from the free list and place it in the top level array + BitBlock *alloc_block_containing(uint element); + + // Free a block from the top level array, placing it on the free BitBlock list + void free_block(uint i); + + public: + //-------------------------- Primitive set operations -------------------------- + + void clear() { +#ifdef ASSERT + if( VerifyOpto ) + check_watch("clear"); +#endif + _count = 0; + for (uint i = 0; i < _max_blocks; i++) { + BitBlock *block = _blocks[i]; + if (block != &_empty_block) { + free_block(i); + } + } + } + + uint count() const { return _count; } + + bool is_empty() const { return _count == 0; } + + bool member(uint element) const { + return get_block_containing(element)->member(element); + } + + bool insert(uint element) { +#ifdef ASSERT + if( VerifyOpto ) + check_watch("insert", element); +#endif + if (element == 0) { + return 0; + } + BitBlock *block = get_block_containing(element); + if (block == &_empty_block) { + block = alloc_block_containing(element); + } + bool present = block->insert(element); + if (!present) { + _count++; + } + return !present; + } + + bool remove(uint element) { +#ifdef ASSERT + if( VerifyOpto ) + check_watch("remove", element); +#endif + + BitBlock *block = get_block_containing(element); + bool present = block->remove(element); + if (present) { + _count--; + } + return present; + } + + //-------------------------- Compound set operations ------------------------ + // Compute the union of all elements of one and two which interfere + // with the RegMask mask. If the degree of the union becomes + // exceeds fail_degree, the union bails out. The underlying set is + // cleared before the union is performed. + uint lrg_union(uint lr1, uint lr2, + const uint fail_degree, + const class PhaseIFG *ifg, + const RegMask &mask); + + + //------------------------- Construction, initialization ----------------------- + + IndexSet() {} + + // This constructor is used for making a deep copy of a IndexSet. + IndexSet(IndexSet *set); + + // Perform initialization on a IndexSet + void initialize(uint max_element); + + // Initialize a IndexSet. If the top level BitBlock array needs to be + // allocated, do it from the proffered arena. BitBlocks are still allocated + // from the static Arena member. + void initialize(uint max_element, Arena *arena); + + // Exchange two sets + void swap(IndexSet *set); + + //-------------------------- Debugging and statistics -------------------------- + +#ifndef PRODUCT + // Output a IndexSet for debugging + void dump() const; +#endif + +#ifdef ASSERT + void tally_iteration_statistics() const; + + // BitBlock allocation statistics + static uint _alloc_new; + static uint _alloc_total; + + // Block density statistics + static long _total_bits; + static long _total_used_blocks; + static long _total_unused_blocks; + + // Sanity tests + void verify() const; + + static int _serial_count; + int _serial_number; + + // Check to see if the serial number of the current set is the one we're tracing. + // If it is, print a message. + void check_watch(const char *operation, uint operand) const { + if (IndexSetWatch != 0) { + if (IndexSetWatch == -1 || _serial_number == IndexSetWatch) { + tty->print_cr("IndexSet %d : %s ( %d )", _serial_number, operation, operand); + } + } + } + void check_watch(const char *operation) const { + if (IndexSetWatch != 0) { + if (IndexSetWatch == -1 || _serial_number == IndexSetWatch) { + tty->print_cr("IndexSet %d : %s", _serial_number, operation); + } + } + } + + public: + static void print_statistics(); + +#endif +}; + + +//-------------------------------- class IndexSetIterator -------------------- +// An iterator for IndexSets. + +class IndexSetIterator VALUE_OBJ_CLASS_SPEC { + friend class IndexSet; + + public: + + // We walk over the bits in a word in chunks of size window_size. + enum { window_size = 5, + window_mask = right_n_bits(window_size), + table_size = (1 << window_size) }; + + // For an integer of length window_size, what is the first set bit? + static const byte _first_bit[table_size]; + + // For an integer of length window_size, what is the second set bit? + static const byte _second_bit[table_size]; + + private: + // The current word we are inspecting + uint32 _current; + + // What element number are we currently on? + uint _value; + + // The index of the next word we will inspect + uint _next_word; + + // A pointer to the contents of the current block + uint32 *_words; + + // The index of the next block we will inspect + uint _next_block; + + // A pointer to the blocks in our set + IndexSet::BitBlock **_blocks; + + // The number of blocks in the set + uint _max_blocks; + + // If the iterator was created from a non-const set, we replace + // non-canonical empty blocks with the _empty_block pointer. If + // _set is NULL, we do no replacement. + IndexSet *_set; + + // Advance to the next non-empty word and return the next + // element in the set. + uint advance_and_next(); + + + public: + + // If an iterator is built from a constant set then empty blocks + // are not canonicalized. + IndexSetIterator(IndexSet *set); + IndexSetIterator(const IndexSet *set); + + // Return the next element of the set. Return 0 when done. + uint next() { + uint current = _current; + if (current != 0) { + uint value = _value; + while (mask_bits(current,window_mask) == 0) { + current >>= window_size; + value += window_size; + } + + uint advance = _second_bit[mask_bits(current,window_mask)]; + _current = current >> advance; + _value = value + advance; + return value + _first_bit[mask_bits(current,window_mask)]; + } else { + return advance_and_next(); + } + } +}; diff --git a/hotspot/src/share/vm/opto/lcm.cpp b/hotspot/src/share/vm/opto/lcm.cpp new file mode 100644 index 00000000000..5b0ddae9955 --- /dev/null +++ b/hotspot/src/share/vm/opto/lcm.cpp @@ -0,0 +1,934 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_lcm.cpp.incl" + +//------------------------------implicit_null_check---------------------------- +// Detect implicit-null-check opportunities. Basically, find NULL checks +// with suitable memory ops nearby. Use the memory op to do the NULL check. +// I can generate a memory op if there is not one nearby. +// The proj is the control projection for the not-null case. +// The val is the pointer being checked for nullness. +void Block::implicit_null_check(PhaseCFG *cfg, Node *proj, Node *val, int allowed_reasons) { + // Assume if null check need for 0 offset then always needed + // Intel solaris doesn't support any null checks yet and no + // mechanism exists (yet) to set the switches at an os_cpu level + if( !ImplicitNullChecks || MacroAssembler::needs_explicit_null_check(0)) return; + + // Make sure the ptr-is-null path appears to be uncommon! + float f = end()->as_MachIf()->_prob; + if( proj->Opcode() == Op_IfTrue ) f = 1.0f - f; + if( f > PROB_UNLIKELY_MAG(4) ) return; + + uint bidx = 0; // Capture index of value into memop + bool was_store; // Memory op is a store op + + // Get the successor block for if the test ptr is non-null + Block* not_null_block; // this one goes with the proj + Block* null_block; + if (_nodes[_nodes.size()-1] == proj) { + null_block = _succs[0]; + not_null_block = _succs[1]; + } else { + assert(_nodes[_nodes.size()-2] == proj, "proj is one or the other"); + not_null_block = _succs[0]; + null_block = _succs[1]; + } + + // Search the exception block for an uncommon trap. + // (See Parse::do_if and Parse::do_ifnull for the reason + // we need an uncommon trap. Briefly, we need a way to + // detect failure of this optimization, as in 6366351.) + { + bool found_trap = false; + for (uint i1 = 0; i1 < null_block->_nodes.size(); i1++) { + Node* nn = null_block->_nodes[i1]; + if (nn->is_MachCall() && + nn->as_MachCall()->entry_point() == + SharedRuntime::uncommon_trap_blob()->instructions_begin()) { + const Type* trtype = nn->in(TypeFunc::Parms)->bottom_type(); + if (trtype->isa_int() && trtype->is_int()->is_con()) { + jint tr_con = trtype->is_int()->get_con(); + Deoptimization::DeoptReason reason = Deoptimization::trap_request_reason(tr_con); + Deoptimization::DeoptAction action = Deoptimization::trap_request_action(tr_con); + assert((int)reason < (int)BitsPerInt, "recode bit map"); + if (is_set_nth_bit(allowed_reasons, (int) reason) + && action != Deoptimization::Action_none) { + // This uncommon trap is sure to recompile, eventually. + // When that happens, C->too_many_traps will prevent + // this transformation from happening again. + found_trap = true; + } + } + break; + } + } + if (!found_trap) { + // We did not find an uncommon trap. + return; + } + } + + // Search the successor block for a load or store who's base value is also + // the tested value. There may be several. + Node_List *out = new Node_List(Thread::current()->resource_area()); + MachNode *best = NULL; // Best found so far + for (DUIterator i = val->outs(); val->has_out(i); i++) { + Node *m = val->out(i); + if( !m->is_Mach() ) continue; + MachNode *mach = m->as_Mach(); + was_store = false; + switch( mach->ideal_Opcode() ) { + case Op_LoadB: + case Op_LoadC: + case Op_LoadD: + case Op_LoadF: + case Op_LoadI: + case Op_LoadL: + case Op_LoadP: + case Op_LoadS: + case Op_LoadKlass: + case Op_LoadRange: + case Op_LoadD_unaligned: + case Op_LoadL_unaligned: + break; + case Op_StoreB: + case Op_StoreC: + case Op_StoreCM: + case Op_StoreD: + case Op_StoreF: + case Op_StoreI: + case Op_StoreL: + case Op_StoreP: + was_store = true; // Memory op is a store op + // Stores will have their address in slot 2 (memory in slot 1). + // If the value being nul-checked is in another slot, it means we + // are storing the checked value, which does NOT check the value! + if( mach->in(2) != val ) continue; + break; // Found a memory op? + case Op_StrComp: + // Not a legit memory op for implicit null check regardless of + // embedded loads + continue; + default: // Also check for embedded loads + if( !mach->needs_anti_dependence_check() ) + continue; // Not an memory op; skip it + break; + } + // check if the offset is not too high for implicit exception + { + intptr_t offset = 0; + const TypePtr *adr_type = NULL; // Do not need this return value here + const Node* base = mach->get_base_and_disp(offset, adr_type); + if (base == NULL || base == NodeSentinel) { + // cannot reason about it; is probably not implicit null exception + } else { + const TypePtr* tptr = base->bottom_type()->is_ptr(); + // Give up if offset is not a compile-time constant + if( offset == Type::OffsetBot || tptr->_offset == Type::OffsetBot ) + continue; + offset += tptr->_offset; // correct if base is offseted + if( MacroAssembler::needs_explicit_null_check(offset) ) + continue; // Give up is reference is beyond 4K page size + } + } + + // Check ctrl input to see if the null-check dominates the memory op + Block *cb = cfg->_bbs[mach->_idx]; + cb = cb->_idom; // Always hoist at least 1 block + if( !was_store ) { // Stores can be hoisted only one block + while( cb->_dom_depth > (_dom_depth + 1)) + cb = cb->_idom; // Hoist loads as far as we want + // The non-null-block should dominate the memory op, too. Live + // range spilling will insert a spill in the non-null-block if it is + // needs to spill the memory op for an implicit null check. + if (cb->_dom_depth == (_dom_depth + 1)) { + if (cb != not_null_block) continue; + cb = cb->_idom; + } + } + if( cb != this ) continue; + + // Found a memory user; see if it can be hoisted to check-block + uint vidx = 0; // Capture index of value into memop + uint j; + for( j = mach->req()-1; j > 0; j-- ) { + if( mach->in(j) == val ) vidx = j; + // Block of memory-op input + Block *inb = cfg->_bbs[mach->in(j)->_idx]; + Block *b = this; // Start from nul check + while( b != inb && b->_dom_depth > inb->_dom_depth ) + b = b->_idom; // search upwards for input + // See if input dominates null check + if( b != inb ) + break; + } + if( j > 0 ) + continue; + Block *mb = cfg->_bbs[mach->_idx]; + // Hoisting stores requires more checks for the anti-dependence case. + // Give up hoisting if we have to move the store past any load. + if( was_store ) { + Block *b = mb; // Start searching here for a local load + // mach use (faulting) trying to hoist + // n might be blocker to hoisting + while( b != this ) { + uint k; + for( k = 1; k < b->_nodes.size(); k++ ) { + Node *n = b->_nodes[k]; + if( n->needs_anti_dependence_check() && + n->in(LoadNode::Memory) == mach->in(StoreNode::Memory) ) + break; // Found anti-dependent load + } + if( k < b->_nodes.size() ) + break; // Found anti-dependent load + // Make sure control does not do a merge (would have to check allpaths) + if( b->num_preds() != 2 ) break; + b = cfg->_bbs[b->pred(1)->_idx]; // Move up to predecessor block + } + if( b != this ) continue; + } + + // Make sure this memory op is not already being used for a NullCheck + Node *e = mb->end(); + if( e->is_MachNullCheck() && e->in(1) == mach ) + continue; // Already being used as a NULL check + + // Found a candidate! Pick one with least dom depth - the highest + // in the dom tree should be closest to the null check. + if( !best || + cfg->_bbs[mach->_idx]->_dom_depth < cfg->_bbs[best->_idx]->_dom_depth ) { + best = mach; + bidx = vidx; + + } + } + // No candidate! + if( !best ) return; + + // ---- Found an implicit null check + extern int implicit_null_checks; + implicit_null_checks++; + + // Hoist the memory candidate up to the end of the test block. + Block *old_block = cfg->_bbs[best->_idx]; + old_block->find_remove(best); + add_inst(best); + cfg->_bbs.map(best->_idx,this); + + // Move the control dependence + if (best->in(0) && best->in(0) == old_block->_nodes[0]) + best->set_req(0, _nodes[0]); + + // Check for flag-killing projections that also need to be hoisted + // Should be DU safe because no edge updates. + for (DUIterator_Fast jmax, j = best->fast_outs(jmax); j < jmax; j++) { + Node* n = best->fast_out(j); + if( n->Opcode() == Op_MachProj ) { + cfg->_bbs[n->_idx]->find_remove(n); + add_inst(n); + cfg->_bbs.map(n->_idx,this); + } + } + + Compile *C = cfg->C; + // proj==Op_True --> ne test; proj==Op_False --> eq test. + // One of two graph shapes got matched: + // (IfTrue (If (Bool NE (CmpP ptr NULL)))) + // (IfFalse (If (Bool EQ (CmpP ptr NULL)))) + // NULL checks are always branch-if-eq. If we see a IfTrue projection + // then we are replacing a 'ne' test with a 'eq' NULL check test. + // We need to flip the projections to keep the same semantics. + if( proj->Opcode() == Op_IfTrue ) { + // Swap order of projections in basic block to swap branch targets + Node *tmp1 = _nodes[end_idx()+1]; + Node *tmp2 = _nodes[end_idx()+2]; + _nodes.map(end_idx()+1, tmp2); + _nodes.map(end_idx()+2, tmp1); + Node *tmp = new (C, 1) Node(C->top()); // Use not NULL input + tmp1->replace_by(tmp); + tmp2->replace_by(tmp1); + tmp->replace_by(tmp2); + tmp->destruct(); + } + + // Remove the existing null check; use a new implicit null check instead. + // Since schedule-local needs precise def-use info, we need to correct + // it as well. + Node *old_tst = proj->in(0); + MachNode *nul_chk = new (C) MachNullCheckNode(old_tst->in(0),best,bidx); + _nodes.map(end_idx(),nul_chk); + cfg->_bbs.map(nul_chk->_idx,this); + // Redirect users of old_test to nul_chk + for (DUIterator_Last i2min, i2 = old_tst->last_outs(i2min); i2 >= i2min; --i2) + old_tst->last_out(i2)->set_req(0, nul_chk); + // Clean-up any dead code + for (uint i3 = 0; i3 < old_tst->req(); i3++) + old_tst->set_req(i3, NULL); + + cfg->latency_from_uses(nul_chk); + cfg->latency_from_uses(best); +} + + +//------------------------------select----------------------------------------- +// Select a nice fellow from the worklist to schedule next. If there is only +// one choice, then use it. Projections take top priority for correctness +// reasons - if I see a projection, then it is next. There are a number of +// other special cases, for instructions that consume condition codes, et al. +// These are chosen immediately. Some instructions are required to immediately +// precede the last instruction in the block, and these are taken last. Of the +// remaining cases (most), choose the instruction with the greatest latency +// (that is, the most number of pseudo-cycles required to the end of the +// routine). If there is a tie, choose the instruction with the most inputs. +Node *Block::select(PhaseCFG *cfg, Node_List &worklist, int *ready_cnt, VectorSet &next_call, uint sched_slot) { + + // If only a single entry on the stack, use it + uint cnt = worklist.size(); + if (cnt == 1) { + Node *n = worklist[0]; + worklist.map(0,worklist.pop()); + return n; + } + + uint choice = 0; // Bigger is most important + uint latency = 0; // Bigger is scheduled first + uint score = 0; // Bigger is better + uint idx; // Index in worklist + + for( uint i=0; iis_Mach() ? n->as_Mach()->ideal_Opcode() : 0; + if( n->is_Proj() || // Projections always win + n->Opcode()== Op_Con || // So does constant 'Top' + iop == Op_CreateEx || // Create-exception must start block + iop == Op_CheckCastPP + ) { + worklist.map(i,worklist.pop()); + return n; + } + + // Final call in a block must be adjacent to 'catch' + Node *e = end(); + if( e->is_Catch() && e->in(0)->in(0) == n ) + continue; + + // Memory op for an implicit null check has to be at the end of the block + if( e->is_MachNullCheck() && e->in(1) == n ) + continue; + + uint n_choice = 2; + + // See if this instruction is consumed by a branch. If so, then (as the + // branch is the last instruction in the basic block) force it to the + // end of the basic block + if ( must_clone[iop] ) { + // See if any use is a branch + bool found_machif = false; + + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { + Node* use = n->fast_out(j); + + // The use is a conditional branch, make them adjacent + if (use->is_MachIf() && cfg->_bbs[use->_idx]==this ) { + found_machif = true; + break; + } + + // More than this instruction pending for successor to be ready, + // don't choose this if other opportunities are ready + if (ready_cnt[use->_idx] > 1) + n_choice = 1; + } + + // loop terminated, prefer not to use this instruction + if (found_machif) + continue; + } + + // See if this has a predecessor that is "must_clone", i.e. sets the + // condition code. If so, choose this first + for (uint j = 0; j < n->req() ; j++) { + Node *inn = n->in(j); + if (inn) { + if (inn->is_Mach() && must_clone[inn->as_Mach()->ideal_Opcode()] ) { + n_choice = 3; + break; + } + } + } + + // MachTemps should be scheduled last so they are near their uses + if (n->is_MachTemp()) { + n_choice = 1; + } + + uint n_latency = cfg->_node_latency.at_grow(n->_idx); + uint n_score = n->req(); // Many inputs get high score to break ties + + // Keep best latency found + if( choice < n_choice || + ( choice == n_choice && + ( latency < n_latency || + ( latency == n_latency && + ( score < n_score ))))) { + choice = n_choice; + latency = n_latency; + score = n_score; + idx = i; // Also keep index in worklist + } + } // End of for all ready nodes in worklist + + Node *n = worklist[idx]; // Get the winner + + worklist.map(idx,worklist.pop()); // Compress worklist + return n; +} + + +//------------------------------set_next_call---------------------------------- +void Block::set_next_call( Node *n, VectorSet &next_call, Block_Array &bbs ) { + if( next_call.test_set(n->_idx) ) return; + for( uint i=0; ilen(); i++ ) { + Node *m = n->in(i); + if( !m ) continue; // must see all nodes in block that precede call + if( bbs[m->_idx] == this ) + set_next_call( m, next_call, bbs ); + } +} + +//------------------------------needed_for_next_call--------------------------- +// Set the flag 'next_call' for each Node that is needed for the next call to +// be scheduled. This flag lets me bias scheduling so Nodes needed for the +// next subroutine call get priority - basically it moves things NOT needed +// for the next call till after the call. This prevents me from trying to +// carry lots of stuff live across a call. +void Block::needed_for_next_call(Node *this_call, VectorSet &next_call, Block_Array &bbs) { + // Find the next control-defining Node in this block + Node* call = NULL; + for (DUIterator_Fast imax, i = this_call->fast_outs(imax); i < imax; i++) { + Node* m = this_call->fast_out(i); + if( bbs[m->_idx] == this && // Local-block user + m != this_call && // Not self-start node + m->is_Call() ) + call = m; + break; + } + if (call == NULL) return; // No next call (e.g., block end is near) + // Set next-call for all inputs to this call + set_next_call(call, next_call, bbs); +} + +//------------------------------sched_call------------------------------------- +uint Block::sched_call( Matcher &matcher, Block_Array &bbs, uint node_cnt, Node_List &worklist, int *ready_cnt, MachCallNode *mcall, VectorSet &next_call ) { + RegMask regs; + + // Schedule all the users of the call right now. All the users are + // projection Nodes, so they must be scheduled next to the call. + // Collect all the defined registers. + for (DUIterator_Fast imax, i = mcall->fast_outs(imax); i < imax; i++) { + Node* n = mcall->fast_out(i); + assert( n->Opcode()==Op_MachProj, "" ); + --ready_cnt[n->_idx]; + assert( !ready_cnt[n->_idx], "" ); + // Schedule next to call + _nodes.map(node_cnt++, n); + // Collect defined registers + regs.OR(n->out_RegMask()); + // Check for scheduling the next control-definer + if( n->bottom_type() == Type::CONTROL ) + // Warm up next pile of heuristic bits + needed_for_next_call(n, next_call, bbs); + + // Children of projections are now all ready + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { + Node* m = n->fast_out(j); // Get user + if( bbs[m->_idx] != this ) continue; + if( m->is_Phi() ) continue; + if( !--ready_cnt[m->_idx] ) + worklist.push(m); + } + + } + + // Act as if the call defines the Frame Pointer. + // Certainly the FP is alive and well after the call. + regs.Insert(matcher.c_frame_pointer()); + + // Set all registers killed and not already defined by the call. + uint r_cnt = mcall->tf()->range()->cnt(); + int op = mcall->ideal_Opcode(); + MachProjNode *proj = new (matcher.C, 1) MachProjNode( mcall, r_cnt+1, RegMask::Empty, MachProjNode::fat_proj ); + bbs.map(proj->_idx,this); + _nodes.insert(node_cnt++, proj); + + // Select the right register save policy. + const char * save_policy; + switch (op) { + case Op_CallRuntime: + case Op_CallLeaf: + case Op_CallLeafNoFP: + // Calling C code so use C calling convention + save_policy = matcher._c_reg_save_policy; + break; + + case Op_CallStaticJava: + case Op_CallDynamicJava: + // Calling Java code so use Java calling convention + save_policy = matcher._register_save_policy; + break; + + default: + ShouldNotReachHere(); + } + + // When using CallRuntime mark SOE registers as killed by the call + // so values that could show up in the RegisterMap aren't live in a + // callee saved register since the register wouldn't know where to + // find them. CallLeaf and CallLeafNoFP are ok because they can't + // have debug info on them. Strictly speaking this only needs to be + // done for oops since idealreg2debugmask takes care of debug info + // references but there no way to handle oops differently than other + // pointers as far as the kill mask goes. + bool exclude_soe = op == Op_CallRuntime; + + // Fill in the kill mask for the call + for( OptoReg::Name r = OptoReg::Name(0); r < _last_Mach_Reg; r=OptoReg::add(r,1) ) { + if( !regs.Member(r) ) { // Not already defined by the call + // Save-on-call register? + if ((save_policy[r] == 'C') || + (save_policy[r] == 'A') || + ((save_policy[r] == 'E') && exclude_soe)) { + proj->_rout.Insert(r); + } + } + } + + return node_cnt; +} + + +//------------------------------schedule_local--------------------------------- +// Topological sort within a block. Someday become a real scheduler. +bool Block::schedule_local(PhaseCFG *cfg, Matcher &matcher, int *ready_cnt, VectorSet &next_call) { + // Already "sorted" are the block start Node (as the first entry), and + // the block-ending Node and any trailing control projections. We leave + // these alone. PhiNodes and ParmNodes are made to follow the block start + // Node. Everything else gets topo-sorted. + +#ifndef PRODUCT + if (cfg->trace_opto_pipelining()) { + tty->print_cr("# --- schedule_local B%d, before: ---", _pre_order); + for (uint i = 0;i < _nodes.size();i++) { + tty->print("# "); + _nodes[i]->fast_dump(); + } + tty->print_cr("#"); + } +#endif + + // RootNode is already sorted + if( _nodes.size() == 1 ) return true; + + // Move PhiNodes and ParmNodes from 1 to cnt up to the start + uint node_cnt = end_idx(); + uint phi_cnt = 1; + uint i; + for( i = 1; iis_Phi() || // Found a PhiNode or ParmNode + (n->is_Proj() && n->in(0) == head()) ) { + // Move guy at 'phi_cnt' to the end; makes a hole at phi_cnt + _nodes.map(i,_nodes[phi_cnt]); + _nodes.map(phi_cnt++,n); // swap Phi/Parm up front + } else { // All others + // Count block-local inputs to 'n' + uint cnt = n->len(); // Input count + uint local = 0; + for( uint j=0; jin(j); + if( m && cfg->_bbs[m->_idx] == this && !m->is_top() ) + local++; // One more block-local input + } + ready_cnt[n->_idx] = local; // Count em up + + // A few node types require changing a required edge to a precedence edge + // before allocation. + if( UseConcMarkSweepGC ) { + if( n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_StoreCM ) { + // Note: Required edges with an index greater than oper_input_base + // are not supported by the allocator. + // Note2: Can only depend on unmatched edge being last, + // can not depend on its absolute position. + Node *oop_store = n->in(n->req() - 1); + n->del_req(n->req() - 1); + n->add_prec(oop_store); + assert(cfg->_bbs[oop_store->_idx]->_dom_depth <= this->_dom_depth, "oop_store must dominate card-mark"); + } + } + if( n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_MemBarAcquire ) { + Node *x = n->in(TypeFunc::Parms); + n->del_req(TypeFunc::Parms); + n->add_prec(x); + } + } + } + for(uint i2=i; i2<_nodes.size(); i2++ ) // Trailing guys get zapped count + ready_cnt[_nodes[i2]->_idx] = 0; + + // All the prescheduled guys do not hold back internal nodes + uint i3; + for(i3 = 0; i3fast_outs(jmax); j < jmax; j++) { + Node* m = n->fast_out(j); + if( cfg->_bbs[m->_idx] ==this ) // Local-block user + ready_cnt[m->_idx]--; // Fix ready count + } + } + + Node_List delay; + // Make a worklist + Node_List worklist; + for(uint i4=i3; i4_idx] ) { // Zero ready count? + if (m->is_iteratively_computed()) { + // Push induction variable increments last to allow other uses + // of the phi to be scheduled first. The select() method breaks + // ties in scheduling by worklist order. + delay.push(m); + } else { + worklist.push(m); // Then on to worklist! + } + } + } + while (delay.size()) { + Node* d = delay.pop(); + worklist.push(d); + } + + // Warm up the 'next_call' heuristic bits + needed_for_next_call(_nodes[0], next_call, cfg->_bbs); + +#ifndef PRODUCT + if (cfg->trace_opto_pipelining()) { + for (uint j=0; j<_nodes.size(); j++) { + Node *n = _nodes[j]; + int idx = n->_idx; + tty->print("# ready cnt:%3d ", ready_cnt[idx]); + tty->print("latency:%3d ", cfg->_node_latency.at_grow(idx)); + tty->print("%4d: %s\n", idx, n->Name()); + } + } +#endif + + // Pull from worklist and schedule + while( worklist.size() ) { // Worklist is not ready + +#ifndef PRODUCT + if (cfg->trace_opto_pipelining()) { + tty->print("# ready list:"); + for( uint i=0; iprint(" %d", n->_idx); + } + tty->cr(); + } +#endif + + // Select and pop a ready guy from worklist + Node* n = select(cfg, worklist, ready_cnt, next_call, phi_cnt); + _nodes.map(phi_cnt++,n); // Schedule him next + +#ifndef PRODUCT + if (cfg->trace_opto_pipelining()) { + tty->print("# select %d: %s", n->_idx, n->Name()); + tty->print(", latency:%d", cfg->_node_latency.at_grow(n->_idx)); + n->dump(); + if (Verbose) { + tty->print("# ready list:"); + for( uint i=0; iprint(" %d", n->_idx); + } + tty->cr(); + } + } + +#endif + if( n->is_MachCall() ) { + MachCallNode *mcall = n->as_MachCall(); + phi_cnt = sched_call(matcher, cfg->_bbs, phi_cnt, worklist, ready_cnt, mcall, next_call); + continue; + } + // Children are now all ready + for (DUIterator_Fast i5max, i5 = n->fast_outs(i5max); i5 < i5max; i5++) { + Node* m = n->fast_out(i5); // Get user + if( cfg->_bbs[m->_idx] != this ) continue; + if( m->is_Phi() ) continue; + if( !--ready_cnt[m->_idx] ) + worklist.push(m); + } + } + + if( phi_cnt != end_idx() ) { + // did not schedule all. Retry, Bailout, or Die + Compile* C = matcher.C; + if (C->subsume_loads() == true && !C->failing()) { + // Retry with subsume_loads == false + // If this is the first failure, the sentinel string will "stick" + // to the Compile object, and the C2Compiler will see it and retry. + C->record_failure(C2Compiler::retry_no_subsuming_loads()); + } + // assert( phi_cnt == end_idx(), "did not schedule all" ); + return false; + } + +#ifndef PRODUCT + if (cfg->trace_opto_pipelining()) { + tty->print_cr("#"); + tty->print_cr("# after schedule_local"); + for (uint i = 0;i < _nodes.size();i++) { + tty->print("# "); + _nodes[i]->fast_dump(); + } + tty->cr(); + } +#endif + + + return true; +} + +//--------------------------catch_cleanup_fix_all_inputs----------------------- +static void catch_cleanup_fix_all_inputs(Node *use, Node *old_def, Node *new_def) { + for (uint l = 0; l < use->len(); l++) { + if (use->in(l) == old_def) { + if (l < use->req()) { + use->set_req(l, new_def); + } else { + use->rm_prec(l); + use->add_prec(new_def); + l--; + } + } + } +} + +//------------------------------catch_cleanup_find_cloned_def------------------ +static Node *catch_cleanup_find_cloned_def(Block *use_blk, Node *def, Block *def_blk, Block_Array &bbs, int n_clone_idx) { + assert( use_blk != def_blk, "Inter-block cleanup only"); + + // The use is some block below the Catch. Find and return the clone of the def + // that dominates the use. If there is no clone in a dominating block, then + // create a phi for the def in a dominating block. + + // Find which successor block dominates this use. The successor + // blocks must all be single-entry (from the Catch only; I will have + // split blocks to make this so), hence they all dominate. + while( use_blk->_dom_depth > def_blk->_dom_depth+1 ) + use_blk = use_blk->_idom; + + // Find the successor + Node *fixup = NULL; + + uint j; + for( j = 0; j < def_blk->_num_succs; j++ ) + if( use_blk == def_blk->_succs[j] ) + break; + + if( j == def_blk->_num_succs ) { + // Block at same level in dom-tree is not a successor. It needs a + // PhiNode, the PhiNode uses from the def and IT's uses need fixup. + Node_Array inputs = new Node_List(Thread::current()->resource_area()); + for(uint k = 1; k < use_blk->num_preds(); k++) { + inputs.map(k, catch_cleanup_find_cloned_def(bbs[use_blk->pred(k)->_idx], def, def_blk, bbs, n_clone_idx)); + } + + // Check to see if the use_blk already has an identical phi inserted. + // If it exists, it will be at the first position since all uses of a + // def are processed together. + Node *phi = use_blk->_nodes[1]; + if( phi->is_Phi() ) { + fixup = phi; + for (uint k = 1; k < use_blk->num_preds(); k++) { + if (phi->in(k) != inputs[k]) { + // Not a match + fixup = NULL; + break; + } + } + } + + // If an existing PhiNode was not found, make a new one. + if (fixup == NULL) { + Node *new_phi = PhiNode::make(use_blk->head(), def); + use_blk->_nodes.insert(1, new_phi); + bbs.map(new_phi->_idx, use_blk); + for (uint k = 1; k < use_blk->num_preds(); k++) { + new_phi->set_req(k, inputs[k]); + } + fixup = new_phi; + } + + } else { + // Found the use just below the Catch. Make it use the clone. + fixup = use_blk->_nodes[n_clone_idx]; + } + + return fixup; +} + +//--------------------------catch_cleanup_intra_block-------------------------- +// Fix all input edges in use that reference "def". The use is in the same +// block as the def and both have been cloned in each successor block. +static void catch_cleanup_intra_block(Node *use, Node *def, Block *blk, int beg, int n_clone_idx) { + + // Both the use and def have been cloned. For each successor block, + // get the clone of the use, and make its input the clone of the def + // found in that block. + + uint use_idx = blk->find_node(use); + uint offset_idx = use_idx - beg; + for( uint k = 0; k < blk->_num_succs; k++ ) { + // Get clone in each successor block + Block *sb = blk->_succs[k]; + Node *clone = sb->_nodes[offset_idx+1]; + assert( clone->Opcode() == use->Opcode(), "" ); + + // Make use-clone reference the def-clone + catch_cleanup_fix_all_inputs(clone, def, sb->_nodes[n_clone_idx]); + } +} + +//------------------------------catch_cleanup_inter_block--------------------- +// Fix all input edges in use that reference "def". The use is in a different +// block than the def. +static void catch_cleanup_inter_block(Node *use, Block *use_blk, Node *def, Block *def_blk, Block_Array &bbs, int n_clone_idx) { + if( !use_blk ) return; // Can happen if the use is a precedence edge + + Node *new_def = catch_cleanup_find_cloned_def(use_blk, def, def_blk, bbs, n_clone_idx); + catch_cleanup_fix_all_inputs(use, def, new_def); +} + +//------------------------------call_catch_cleanup----------------------------- +// If we inserted any instructions between a Call and his CatchNode, +// clone the instructions on all paths below the Catch. +void Block::call_catch_cleanup(Block_Array &bbs) { + + // End of region to clone + uint end = end_idx(); + if( !_nodes[end]->is_Catch() ) return; + // Start of region to clone + uint beg = end; + while( _nodes[beg-1]->Opcode() != Op_MachProj || + !_nodes[beg-1]->in(0)->is_Call() ) { + beg--; + assert(beg > 0,"Catch cleanup walking beyond block boundary"); + } + // Range of inserted instructions is [beg, end) + if( beg == end ) return; + + // Clone along all Catch output paths. Clone area between the 'beg' and + // 'end' indices. + for( uint i = 0; i < _num_succs; i++ ) { + Block *sb = _succs[i]; + // Clone the entire area; ignoring the edge fixup for now. + for( uint j = end; j > beg; j-- ) { + Node *clone = _nodes[j-1]->clone(); + sb->_nodes.insert( 1, clone ); + bbs.map(clone->_idx,sb); + } + } + + + // Fixup edges. Check the def-use info per cloned Node + for(uint i2 = beg; i2 < end; i2++ ) { + uint n_clone_idx = i2-beg+1; // Index of clone of n in each successor block + Node *n = _nodes[i2]; // Node that got cloned + // Need DU safe iterator because of edge manipulation in calls. + Unique_Node_List *out = new Unique_Node_List(Thread::current()->resource_area()); + for (DUIterator_Fast j1max, j1 = n->fast_outs(j1max); j1 < j1max; j1++) { + out->push(n->fast_out(j1)); + } + uint max = out->size(); + for (uint j = 0; j < max; j++) {// For all users + Node *use = out->pop(); + Block *buse = bbs[use->_idx]; + if( use->is_Phi() ) { + for( uint k = 1; k < use->req(); k++ ) + if( use->in(k) == n ) { + Node *fixup = catch_cleanup_find_cloned_def(bbs[buse->pred(k)->_idx], n, this, bbs, n_clone_idx); + use->set_req(k, fixup); + } + } else { + if (this == buse) { + catch_cleanup_intra_block(use, n, this, beg, n_clone_idx); + } else { + catch_cleanup_inter_block(use, buse, n, this, bbs, n_clone_idx); + } + } + } // End for all users + + } // End of for all Nodes in cloned area + + // Remove the now-dead cloned ops + for(uint i3 = beg; i3 < end; i3++ ) { + _nodes[beg]->disconnect_inputs(NULL); + _nodes.remove(beg); + } + + // If the successor blocks have a CreateEx node, move it back to the top + for(uint i4 = 0; i4 < _num_succs; i4++ ) { + Block *sb = _succs[i4]; + uint new_cnt = end - beg; + // Remove any newly created, but dead, nodes. + for( uint j = new_cnt; j > 0; j-- ) { + Node *n = sb->_nodes[j]; + if (n->outcnt() == 0 && + (!n->is_Proj() || n->as_Proj()->in(0)->outcnt() == 1) ){ + n->disconnect_inputs(NULL); + sb->_nodes.remove(j); + new_cnt--; + } + } + // If any newly created nodes remain, move the CreateEx node to the top + if (new_cnt > 0) { + Node *cex = sb->_nodes[1+new_cnt]; + if( cex->is_Mach() && cex->as_Mach()->ideal_Opcode() == Op_CreateEx ) { + sb->_nodes.remove(1+new_cnt); + sb->_nodes.insert(1,cex); + } + } + } +} diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp new file mode 100644 index 00000000000..d78f62d4038 --- /dev/null +++ b/hotspot/src/share/vm/opto/library_call.cpp @@ -0,0 +1,4921 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_library_call.cpp.incl" + +class LibraryIntrinsic : public InlineCallGenerator { + // Extend the set of intrinsics known to the runtime: + public: + private: + bool _is_virtual; + vmIntrinsics::ID _intrinsic_id; + + public: + LibraryIntrinsic(ciMethod* m, bool is_virtual, vmIntrinsics::ID id) + : InlineCallGenerator(m), + _is_virtual(is_virtual), + _intrinsic_id(id) + { + } + virtual bool is_intrinsic() const { return true; } + virtual bool is_virtual() const { return _is_virtual; } + virtual JVMState* generate(JVMState* jvms); + vmIntrinsics::ID intrinsic_id() const { return _intrinsic_id; } +}; + + +// Local helper class for LibraryIntrinsic: +class LibraryCallKit : public GraphKit { + private: + LibraryIntrinsic* _intrinsic; // the library intrinsic being called + + public: + LibraryCallKit(JVMState* caller, LibraryIntrinsic* intrinsic) + : GraphKit(caller), + _intrinsic(intrinsic) + { + } + + ciMethod* caller() const { return jvms()->method(); } + int bci() const { return jvms()->bci(); } + LibraryIntrinsic* intrinsic() const { return _intrinsic; } + vmIntrinsics::ID intrinsic_id() const { return _intrinsic->intrinsic_id(); } + ciMethod* callee() const { return _intrinsic->method(); } + ciSignature* signature() const { return callee()->signature(); } + int arg_size() const { return callee()->arg_size(); } + + bool try_to_inline(); + + // Helper functions to inline natives + void push_result(RegionNode* region, PhiNode* value); + Node* generate_guard(Node* test, RegionNode* region, float true_prob); + Node* generate_slow_guard(Node* test, RegionNode* region); + Node* generate_fair_guard(Node* test, RegionNode* region); + Node* generate_negative_guard(Node* index, RegionNode* region, + // resulting CastII of index: + Node* *pos_index = NULL); + Node* generate_nonpositive_guard(Node* index, bool never_negative, + // resulting CastII of index: + Node* *pos_index = NULL); + Node* generate_limit_guard(Node* offset, Node* subseq_length, + Node* array_length, + RegionNode* region); + Node* generate_current_thread(Node* &tls_output); + address basictype2arraycopy(BasicType t, Node *src_offset, Node *dest_offset, + bool disjoint_bases, const char* &name); + Node* load_mirror_from_klass(Node* klass); + Node* load_klass_from_mirror_common(Node* mirror, bool never_see_null, + int nargs, + RegionNode* region, int null_path, + int offset); + Node* load_klass_from_mirror(Node* mirror, bool never_see_null, int nargs, + RegionNode* region, int null_path) { + int offset = java_lang_Class::klass_offset_in_bytes(); + return load_klass_from_mirror_common(mirror, never_see_null, nargs, + region, null_path, + offset); + } + Node* load_array_klass_from_mirror(Node* mirror, bool never_see_null, + int nargs, + RegionNode* region, int null_path) { + int offset = java_lang_Class::array_klass_offset_in_bytes(); + return load_klass_from_mirror_common(mirror, never_see_null, nargs, + region, null_path, + offset); + } + Node* generate_access_flags_guard(Node* kls, + int modifier_mask, int modifier_bits, + RegionNode* region); + Node* generate_interface_guard(Node* kls, RegionNode* region); + Node* generate_array_guard(Node* kls, RegionNode* region) { + return generate_array_guard_common(kls, region, false, false); + } + Node* generate_non_array_guard(Node* kls, RegionNode* region) { + return generate_array_guard_common(kls, region, false, true); + } + Node* generate_objArray_guard(Node* kls, RegionNode* region) { + return generate_array_guard_common(kls, region, true, false); + } + Node* generate_non_objArray_guard(Node* kls, RegionNode* region) { + return generate_array_guard_common(kls, region, true, true); + } + Node* generate_array_guard_common(Node* kls, RegionNode* region, + bool obj_array, bool not_array); + Node* generate_virtual_guard(Node* obj_klass, RegionNode* slow_region); + CallJavaNode* generate_method_call(vmIntrinsics::ID method_id, + bool is_virtual = false, bool is_static = false); + CallJavaNode* generate_method_call_static(vmIntrinsics::ID method_id) { + return generate_method_call(method_id, false, true); + } + CallJavaNode* generate_method_call_virtual(vmIntrinsics::ID method_id) { + return generate_method_call(method_id, true, false); + } + + bool inline_string_compareTo(); + bool inline_string_indexOf(); + Node* string_indexOf(Node* string_object, ciTypeArray* target_array, jint offset, jint cache_i, jint md2_i); + Node* pop_math_arg(); + bool runtime_math(const TypeFunc* call_type, address funcAddr, const char* funcName); + bool inline_math_native(vmIntrinsics::ID id); + bool inline_trig(vmIntrinsics::ID id); + bool inline_trans(vmIntrinsics::ID id); + bool inline_abs(vmIntrinsics::ID id); + bool inline_sqrt(vmIntrinsics::ID id); + bool inline_pow(vmIntrinsics::ID id); + bool inline_exp(vmIntrinsics::ID id); + bool inline_min_max(vmIntrinsics::ID id); + Node* generate_min_max(vmIntrinsics::ID id, Node* x, Node* y); + // This returns Type::AnyPtr, RawPtr, or OopPtr. + int classify_unsafe_addr(Node* &base, Node* &offset); + Node* make_unsafe_address(Node* base, Node* offset); + bool inline_unsafe_access(bool is_native_ptr, bool is_store, BasicType type, bool is_volatile); + bool inline_unsafe_prefetch(bool is_native_ptr, bool is_store, bool is_static); + bool inline_unsafe_allocate(); + bool inline_unsafe_copyMemory(); + bool inline_native_currentThread(); + bool inline_native_time_funcs(bool isNano); + bool inline_native_isInterrupted(); + bool inline_native_Class_query(vmIntrinsics::ID id); + bool inline_native_subtype_check(); + + bool inline_native_newArray(); + bool inline_native_getLength(); + bool inline_array_copyOf(bool is_copyOfRange); + bool inline_native_clone(bool is_virtual); + bool inline_native_Reflection_getCallerClass(); + bool inline_native_AtomicLong_get(); + bool inline_native_AtomicLong_attemptUpdate(); + bool is_method_invoke_or_aux_frame(JVMState* jvms); + // Helper function for inlining native object hash method + bool inline_native_hashcode(bool is_virtual, bool is_static); + bool inline_native_getClass(); + + // Helper functions for inlining arraycopy + bool inline_arraycopy(); + void generate_arraycopy(const TypePtr* adr_type, + BasicType basic_elem_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, + int nargs, // arguments on stack for debug info + bool disjoint_bases = false, + bool length_never_negative = false, + RegionNode* slow_region = NULL); + AllocateArrayNode* tightly_coupled_allocation(Node* ptr, + RegionNode* slow_region); + void generate_clear_array(const TypePtr* adr_type, + Node* dest, + BasicType basic_elem_type, + Node* slice_off, + Node* slice_len, + Node* slice_end); + bool generate_block_arraycopy(const TypePtr* adr_type, + BasicType basic_elem_type, + AllocateNode* alloc, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* dest_size); + void generate_slow_arraycopy(const TypePtr* adr_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, + int nargs); + Node* generate_checkcast_arraycopy(const TypePtr* adr_type, + Node* dest_elem_klass, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, int nargs); + Node* generate_generic_arraycopy(const TypePtr* adr_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, int nargs); + void generate_unchecked_arraycopy(const TypePtr* adr_type, + BasicType basic_elem_type, + bool disjoint_bases, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length); + bool inline_unsafe_CAS(BasicType type); + bool inline_unsafe_ordered_store(BasicType type); + bool inline_fp_conversions(vmIntrinsics::ID id); + bool inline_reverseBytes(vmIntrinsics::ID id); +}; + + +//---------------------------make_vm_intrinsic---------------------------- +CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) { + vmIntrinsics::ID id = m->intrinsic_id(); + assert(id != vmIntrinsics::_none, "must be a VM intrinsic"); + + if (DisableIntrinsic[0] != '\0' + && strstr(DisableIntrinsic, vmIntrinsics::name_at(id)) != NULL) { + // disabled by a user request on the command line: + // example: -XX:DisableIntrinsic=_hashCode,_getClass + return NULL; + } + + if (!m->is_loaded()) { + // do not attempt to inline unloaded methods + return NULL; + } + + // Only a few intrinsics implement a virtual dispatch. + // They are expensive calls which are also frequently overridden. + if (is_virtual) { + switch (id) { + case vmIntrinsics::_hashCode: + case vmIntrinsics::_clone: + // OK, Object.hashCode and Object.clone intrinsics come in both flavors + break; + default: + return NULL; + } + } + + // -XX:-InlineNatives disables nearly all intrinsics: + if (!InlineNatives) { + switch (id) { + case vmIntrinsics::_indexOf: + case vmIntrinsics::_compareTo: + break; // InlineNatives does not control String.compareTo + default: + return NULL; + } + } + + switch (id) { + case vmIntrinsics::_compareTo: + if (!SpecialStringCompareTo) return NULL; + break; + case vmIntrinsics::_indexOf: + if (!SpecialStringIndexOf) return NULL; + break; + case vmIntrinsics::_arraycopy: + if (!InlineArrayCopy) return NULL; + break; + case vmIntrinsics::_copyMemory: + if (StubRoutines::unsafe_arraycopy() == NULL) return NULL; + if (!InlineArrayCopy) return NULL; + break; + case vmIntrinsics::_hashCode: + if (!InlineObjectHash) return NULL; + break; + case vmIntrinsics::_clone: + case vmIntrinsics::_copyOf: + case vmIntrinsics::_copyOfRange: + if (!InlineObjectCopy) return NULL; + // These also use the arraycopy intrinsic mechanism: + if (!InlineArrayCopy) return NULL; + break; + case vmIntrinsics::_checkIndex: + // We do not intrinsify this. The optimizer does fine with it. + return NULL; + + case vmIntrinsics::_get_AtomicLong: + case vmIntrinsics::_attemptUpdate: + if (!InlineAtomicLong) return NULL; + break; + + case vmIntrinsics::_Object_init: + case vmIntrinsics::_invoke: + // We do not intrinsify these; they are marked for other purposes. + return NULL; + + case vmIntrinsics::_getCallerClass: + if (!UseNewReflection) return NULL; + if (!InlineReflectionGetCallerClass) return NULL; + if (!JDK_Version::is_gte_jdk14x_version()) return NULL; + break; + + default: + break; + } + + // -XX:-InlineClassNatives disables natives from the Class class. + // The flag applies to all reflective calls, notably Array.newArray + // (visible to Java programmers as Array.newInstance). + if (m->holder()->name() == ciSymbol::java_lang_Class() || + m->holder()->name() == ciSymbol::java_lang_reflect_Array()) { + if (!InlineClassNatives) return NULL; + } + + // -XX:-InlineThreadNatives disables natives from the Thread class. + if (m->holder()->name() == ciSymbol::java_lang_Thread()) { + if (!InlineThreadNatives) return NULL; + } + + // -XX:-InlineMathNatives disables natives from the Math,Float and Double classes. + if (m->holder()->name() == ciSymbol::java_lang_Math() || + m->holder()->name() == ciSymbol::java_lang_Float() || + m->holder()->name() == ciSymbol::java_lang_Double()) { + if (!InlineMathNatives) return NULL; + } + + // -XX:-InlineUnsafeOps disables natives from the Unsafe class. + if (m->holder()->name() == ciSymbol::sun_misc_Unsafe()) { + if (!InlineUnsafeOps) return NULL; + } + + return new LibraryIntrinsic(m, is_virtual, (vmIntrinsics::ID) id); +} + +//----------------------register_library_intrinsics----------------------- +// Initialize this file's data structures, for each Compile instance. +void Compile::register_library_intrinsics() { + // Nothing to do here. +} + +JVMState* LibraryIntrinsic::generate(JVMState* jvms) { + LibraryCallKit kit(jvms, this); + Compile* C = kit.C; + int nodes = C->unique(); +#ifndef PRODUCT + if ((PrintIntrinsics || PrintInlining NOT_PRODUCT( || PrintOptoInlining) ) && Verbose) { + char buf[1000]; + const char* str = vmIntrinsics::short_name_as_C_string(intrinsic_id(), buf, sizeof(buf)); + tty->print_cr("Intrinsic %s", str); + } +#endif + if (kit.try_to_inline()) { + if (PrintIntrinsics || PrintInlining NOT_PRODUCT( || PrintOptoInlining) ) { + tty->print("Inlining intrinsic %s%s at bci:%d in", + vmIntrinsics::name_at(intrinsic_id()), + (is_virtual() ? " (virtual)" : ""), kit.bci()); + kit.caller()->print_short_name(tty); + tty->print_cr(" (%d bytes)", kit.caller()->code_size()); + } + C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_worked); + if (C->log()) { + C->log()->elem("intrinsic id='%s'%s nodes='%d'", + vmIntrinsics::name_at(intrinsic_id()), + (is_virtual() ? " virtual='1'" : ""), + C->unique() - nodes); + } + return kit.transfer_exceptions_into_jvms(); + } + + if (PrintIntrinsics) { + switch (intrinsic_id()) { + case vmIntrinsics::_invoke: + case vmIntrinsics::_Object_init: + // We do not expect to inline these, so do not produce any noise about them. + break; + default: + tty->print("Did not inline intrinsic %s%s at bci:%d in", + vmIntrinsics::name_at(intrinsic_id()), + (is_virtual() ? " (virtual)" : ""), kit.bci()); + kit.caller()->print_short_name(tty); + tty->print_cr(" (%d bytes)", kit.caller()->code_size()); + } + } + C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_failed); + return NULL; +} + +bool LibraryCallKit::try_to_inline() { + // Handle symbolic names for otherwise undistinguished boolean switches: + const bool is_store = true; + const bool is_native_ptr = true; + const bool is_static = true; + + switch (intrinsic_id()) { + case vmIntrinsics::_hashCode: + return inline_native_hashcode(intrinsic()->is_virtual(), !is_static); + case vmIntrinsics::_identityHashCode: + return inline_native_hashcode(/*!virtual*/ false, is_static); + case vmIntrinsics::_getClass: + return inline_native_getClass(); + + case vmIntrinsics::_dsin: + case vmIntrinsics::_dcos: + case vmIntrinsics::_dtan: + case vmIntrinsics::_dabs: + case vmIntrinsics::_datan2: + case vmIntrinsics::_dsqrt: + case vmIntrinsics::_dexp: + case vmIntrinsics::_dlog: + case vmIntrinsics::_dlog10: + case vmIntrinsics::_dpow: + return inline_math_native(intrinsic_id()); + + case vmIntrinsics::_min: + case vmIntrinsics::_max: + return inline_min_max(intrinsic_id()); + + case vmIntrinsics::_arraycopy: + return inline_arraycopy(); + + case vmIntrinsics::_compareTo: + return inline_string_compareTo(); + case vmIntrinsics::_indexOf: + return inline_string_indexOf(); + + case vmIntrinsics::_getObject: + return inline_unsafe_access(!is_native_ptr, !is_store, T_OBJECT, false); + case vmIntrinsics::_getBoolean: + return inline_unsafe_access(!is_native_ptr, !is_store, T_BOOLEAN, false); + case vmIntrinsics::_getByte: + return inline_unsafe_access(!is_native_ptr, !is_store, T_BYTE, false); + case vmIntrinsics::_getShort: + return inline_unsafe_access(!is_native_ptr, !is_store, T_SHORT, false); + case vmIntrinsics::_getChar: + return inline_unsafe_access(!is_native_ptr, !is_store, T_CHAR, false); + case vmIntrinsics::_getInt: + return inline_unsafe_access(!is_native_ptr, !is_store, T_INT, false); + case vmIntrinsics::_getLong: + return inline_unsafe_access(!is_native_ptr, !is_store, T_LONG, false); + case vmIntrinsics::_getFloat: + return inline_unsafe_access(!is_native_ptr, !is_store, T_FLOAT, false); + case vmIntrinsics::_getDouble: + return inline_unsafe_access(!is_native_ptr, !is_store, T_DOUBLE, false); + + case vmIntrinsics::_putObject: + return inline_unsafe_access(!is_native_ptr, is_store, T_OBJECT, false); + case vmIntrinsics::_putBoolean: + return inline_unsafe_access(!is_native_ptr, is_store, T_BOOLEAN, false); + case vmIntrinsics::_putByte: + return inline_unsafe_access(!is_native_ptr, is_store, T_BYTE, false); + case vmIntrinsics::_putShort: + return inline_unsafe_access(!is_native_ptr, is_store, T_SHORT, false); + case vmIntrinsics::_putChar: + return inline_unsafe_access(!is_native_ptr, is_store, T_CHAR, false); + case vmIntrinsics::_putInt: + return inline_unsafe_access(!is_native_ptr, is_store, T_INT, false); + case vmIntrinsics::_putLong: + return inline_unsafe_access(!is_native_ptr, is_store, T_LONG, false); + case vmIntrinsics::_putFloat: + return inline_unsafe_access(!is_native_ptr, is_store, T_FLOAT, false); + case vmIntrinsics::_putDouble: + return inline_unsafe_access(!is_native_ptr, is_store, T_DOUBLE, false); + + case vmIntrinsics::_getByte_raw: + return inline_unsafe_access(is_native_ptr, !is_store, T_BYTE, false); + case vmIntrinsics::_getShort_raw: + return inline_unsafe_access(is_native_ptr, !is_store, T_SHORT, false); + case vmIntrinsics::_getChar_raw: + return inline_unsafe_access(is_native_ptr, !is_store, T_CHAR, false); + case vmIntrinsics::_getInt_raw: + return inline_unsafe_access(is_native_ptr, !is_store, T_INT, false); + case vmIntrinsics::_getLong_raw: + return inline_unsafe_access(is_native_ptr, !is_store, T_LONG, false); + case vmIntrinsics::_getFloat_raw: + return inline_unsafe_access(is_native_ptr, !is_store, T_FLOAT, false); + case vmIntrinsics::_getDouble_raw: + return inline_unsafe_access(is_native_ptr, !is_store, T_DOUBLE, false); + case vmIntrinsics::_getAddress_raw: + return inline_unsafe_access(is_native_ptr, !is_store, T_ADDRESS, false); + + case vmIntrinsics::_putByte_raw: + return inline_unsafe_access(is_native_ptr, is_store, T_BYTE, false); + case vmIntrinsics::_putShort_raw: + return inline_unsafe_access(is_native_ptr, is_store, T_SHORT, false); + case vmIntrinsics::_putChar_raw: + return inline_unsafe_access(is_native_ptr, is_store, T_CHAR, false); + case vmIntrinsics::_putInt_raw: + return inline_unsafe_access(is_native_ptr, is_store, T_INT, false); + case vmIntrinsics::_putLong_raw: + return inline_unsafe_access(is_native_ptr, is_store, T_LONG, false); + case vmIntrinsics::_putFloat_raw: + return inline_unsafe_access(is_native_ptr, is_store, T_FLOAT, false); + case vmIntrinsics::_putDouble_raw: + return inline_unsafe_access(is_native_ptr, is_store, T_DOUBLE, false); + case vmIntrinsics::_putAddress_raw: + return inline_unsafe_access(is_native_ptr, is_store, T_ADDRESS, false); + + case vmIntrinsics::_getObjectVolatile: + return inline_unsafe_access(!is_native_ptr, !is_store, T_OBJECT, true); + case vmIntrinsics::_getBooleanVolatile: + return inline_unsafe_access(!is_native_ptr, !is_store, T_BOOLEAN, true); + case vmIntrinsics::_getByteVolatile: + return inline_unsafe_access(!is_native_ptr, !is_store, T_BYTE, true); + case vmIntrinsics::_getShortVolatile: + return inline_unsafe_access(!is_native_ptr, !is_store, T_SHORT, true); + case vmIntrinsics::_getCharVolatile: + return inline_unsafe_access(!is_native_ptr, !is_store, T_CHAR, true); + case vmIntrinsics::_getIntVolatile: + return inline_unsafe_access(!is_native_ptr, !is_store, T_INT, true); + case vmIntrinsics::_getLongVolatile: + return inline_unsafe_access(!is_native_ptr, !is_store, T_LONG, true); + case vmIntrinsics::_getFloatVolatile: + return inline_unsafe_access(!is_native_ptr, !is_store, T_FLOAT, true); + case vmIntrinsics::_getDoubleVolatile: + return inline_unsafe_access(!is_native_ptr, !is_store, T_DOUBLE, true); + + case vmIntrinsics::_putObjectVolatile: + return inline_unsafe_access(!is_native_ptr, is_store, T_OBJECT, true); + case vmIntrinsics::_putBooleanVolatile: + return inline_unsafe_access(!is_native_ptr, is_store, T_BOOLEAN, true); + case vmIntrinsics::_putByteVolatile: + return inline_unsafe_access(!is_native_ptr, is_store, T_BYTE, true); + case vmIntrinsics::_putShortVolatile: + return inline_unsafe_access(!is_native_ptr, is_store, T_SHORT, true); + case vmIntrinsics::_putCharVolatile: + return inline_unsafe_access(!is_native_ptr, is_store, T_CHAR, true); + case vmIntrinsics::_putIntVolatile: + return inline_unsafe_access(!is_native_ptr, is_store, T_INT, true); + case vmIntrinsics::_putLongVolatile: + return inline_unsafe_access(!is_native_ptr, is_store, T_LONG, true); + case vmIntrinsics::_putFloatVolatile: + return inline_unsafe_access(!is_native_ptr, is_store, T_FLOAT, true); + case vmIntrinsics::_putDoubleVolatile: + return inline_unsafe_access(!is_native_ptr, is_store, T_DOUBLE, true); + + case vmIntrinsics::_prefetchRead: + return inline_unsafe_prefetch(!is_native_ptr, !is_store, !is_static); + case vmIntrinsics::_prefetchWrite: + return inline_unsafe_prefetch(!is_native_ptr, is_store, !is_static); + case vmIntrinsics::_prefetchReadStatic: + return inline_unsafe_prefetch(!is_native_ptr, !is_store, is_static); + case vmIntrinsics::_prefetchWriteStatic: + return inline_unsafe_prefetch(!is_native_ptr, is_store, is_static); + + case vmIntrinsics::_compareAndSwapObject: + return inline_unsafe_CAS(T_OBJECT); + case vmIntrinsics::_compareAndSwapInt: + return inline_unsafe_CAS(T_INT); + case vmIntrinsics::_compareAndSwapLong: + return inline_unsafe_CAS(T_LONG); + + case vmIntrinsics::_putOrderedObject: + return inline_unsafe_ordered_store(T_OBJECT); + case vmIntrinsics::_putOrderedInt: + return inline_unsafe_ordered_store(T_INT); + case vmIntrinsics::_putOrderedLong: + return inline_unsafe_ordered_store(T_LONG); + + case vmIntrinsics::_currentThread: + return inline_native_currentThread(); + case vmIntrinsics::_isInterrupted: + return inline_native_isInterrupted(); + + case vmIntrinsics::_currentTimeMillis: + return inline_native_time_funcs(false); + case vmIntrinsics::_nanoTime: + return inline_native_time_funcs(true); + case vmIntrinsics::_allocateInstance: + return inline_unsafe_allocate(); + case vmIntrinsics::_copyMemory: + return inline_unsafe_copyMemory(); + case vmIntrinsics::_newArray: + return inline_native_newArray(); + case vmIntrinsics::_getLength: + return inline_native_getLength(); + case vmIntrinsics::_copyOf: + return inline_array_copyOf(false); + case vmIntrinsics::_copyOfRange: + return inline_array_copyOf(true); + case vmIntrinsics::_clone: + return inline_native_clone(intrinsic()->is_virtual()); + + case vmIntrinsics::_isAssignableFrom: + return inline_native_subtype_check(); + + case vmIntrinsics::_isInstance: + case vmIntrinsics::_getModifiers: + case vmIntrinsics::_isInterface: + case vmIntrinsics::_isArray: + case vmIntrinsics::_isPrimitive: + case vmIntrinsics::_getSuperclass: + case vmIntrinsics::_getComponentType: + case vmIntrinsics::_getClassAccessFlags: + return inline_native_Class_query(intrinsic_id()); + + case vmIntrinsics::_floatToRawIntBits: + case vmIntrinsics::_floatToIntBits: + case vmIntrinsics::_intBitsToFloat: + case vmIntrinsics::_doubleToRawLongBits: + case vmIntrinsics::_doubleToLongBits: + case vmIntrinsics::_longBitsToDouble: + return inline_fp_conversions(intrinsic_id()); + + case vmIntrinsics::_reverseBytes_i: + case vmIntrinsics::_reverseBytes_l: + return inline_reverseBytes((vmIntrinsics::ID) intrinsic_id()); + + case vmIntrinsics::_get_AtomicLong: + return inline_native_AtomicLong_get(); + case vmIntrinsics::_attemptUpdate: + return inline_native_AtomicLong_attemptUpdate(); + + case vmIntrinsics::_getCallerClass: + return inline_native_Reflection_getCallerClass(); + + default: + // If you get here, it may be that someone has added a new intrinsic + // to the list in vmSymbols.hpp without implementing it here. +#ifndef PRODUCT + if ((PrintMiscellaneous && (Verbose || WizardMode)) || PrintOpto) { + tty->print_cr("*** Warning: Unimplemented intrinsic %s(%d)", + vmIntrinsics::name_at(intrinsic_id()), intrinsic_id()); + } +#endif + return false; + } +} + +//------------------------------push_result------------------------------ +// Helper function for finishing intrinsics. +void LibraryCallKit::push_result(RegionNode* region, PhiNode* value) { + record_for_igvn(region); + set_control(_gvn.transform(region)); + BasicType value_type = value->type()->basic_type(); + push_node(value_type, _gvn.transform(value)); +} + +//------------------------------generate_guard--------------------------- +// Helper function for generating guarded fast-slow graph structures. +// The given 'test', if true, guards a slow path. If the test fails +// then a fast path can be taken. (We generally hope it fails.) +// In all cases, GraphKit::control() is updated to the fast path. +// The returned value represents the control for the slow path. +// The return value is never 'top'; it is either a valid control +// or NULL if it is obvious that the slow path can never be taken. +// Also, if region and the slow control are not NULL, the slow edge +// is appended to the region. +Node* LibraryCallKit::generate_guard(Node* test, RegionNode* region, float true_prob) { + if (stopped()) { + // Already short circuited. + return NULL; + } + + // Build an if node and its projections. + // If test is true we take the slow path, which we assume is uncommon. + if (_gvn.type(test) == TypeInt::ZERO) { + // The slow branch is never taken. No need to build this guard. + return NULL; + } + + IfNode* iff = create_and_map_if(control(), test, true_prob, COUNT_UNKNOWN); + + Node* if_slow = _gvn.transform( new (C, 1) IfTrueNode(iff) ); + if (if_slow == top()) { + // The slow branch is never taken. No need to build this guard. + return NULL; + } + + if (region != NULL) + region->add_req(if_slow); + + Node* if_fast = _gvn.transform( new (C, 1) IfFalseNode(iff) ); + set_control(if_fast); + + return if_slow; +} + +inline Node* LibraryCallKit::generate_slow_guard(Node* test, RegionNode* region) { + return generate_guard(test, region, PROB_UNLIKELY_MAG(3)); +} +inline Node* LibraryCallKit::generate_fair_guard(Node* test, RegionNode* region) { + return generate_guard(test, region, PROB_FAIR); +} + +inline Node* LibraryCallKit::generate_negative_guard(Node* index, RegionNode* region, + Node* *pos_index) { + if (stopped()) + return NULL; // already stopped + if (_gvn.type(index)->higher_equal(TypeInt::POS)) // [0,maxint] + return NULL; // index is already adequately typed + Node* cmp_lt = _gvn.transform( new (C, 3) CmpINode(index, intcon(0)) ); + Node* bol_lt = _gvn.transform( new (C, 2) BoolNode(cmp_lt, BoolTest::lt) ); + Node* is_neg = generate_guard(bol_lt, region, PROB_MIN); + if (is_neg != NULL && pos_index != NULL) { + // Emulate effect of Parse::adjust_map_after_if. + Node* ccast = new (C, 2) CastIINode(index, TypeInt::POS); + ccast->set_req(0, control()); + (*pos_index) = _gvn.transform(ccast); + } + return is_neg; +} + +inline Node* LibraryCallKit::generate_nonpositive_guard(Node* index, bool never_negative, + Node* *pos_index) { + if (stopped()) + return NULL; // already stopped + if (_gvn.type(index)->higher_equal(TypeInt::POS1)) // [1,maxint] + return NULL; // index is already adequately typed + Node* cmp_le = _gvn.transform( new (C, 3) CmpINode(index, intcon(0)) ); + BoolTest::mask le_or_eq = (never_negative ? BoolTest::eq : BoolTest::le); + Node* bol_le = _gvn.transform( new (C, 2) BoolNode(cmp_le, le_or_eq) ); + Node* is_notp = generate_guard(bol_le, NULL, PROB_MIN); + if (is_notp != NULL && pos_index != NULL) { + // Emulate effect of Parse::adjust_map_after_if. + Node* ccast = new (C, 2) CastIINode(index, TypeInt::POS1); + ccast->set_req(0, control()); + (*pos_index) = _gvn.transform(ccast); + } + return is_notp; +} + +// Make sure that 'position' is a valid limit index, in [0..length]. +// There are two equivalent plans for checking this: +// A. (offset + copyLength) unsigned<= arrayLength +// B. offset <= (arrayLength - copyLength) +// We require that all of the values above, except for the sum and +// difference, are already known to be non-negative. +// Plan A is robust in the face of overflow, if offset and copyLength +// are both hugely positive. +// +// Plan B is less direct and intuitive, but it does not overflow at +// all, since the difference of two non-negatives is always +// representable. Whenever Java methods must perform the equivalent +// check they generally use Plan B instead of Plan A. +// For the moment we use Plan A. +inline Node* LibraryCallKit::generate_limit_guard(Node* offset, + Node* subseq_length, + Node* array_length, + RegionNode* region) { + if (stopped()) + return NULL; // already stopped + bool zero_offset = _gvn.type(offset) == TypeInt::ZERO; + if (zero_offset && _gvn.eqv_uncast(subseq_length, array_length)) + return NULL; // common case of whole-array copy + Node* last = subseq_length; + if (!zero_offset) // last += offset + last = _gvn.transform( new (C, 3) AddINode(last, offset)); + Node* cmp_lt = _gvn.transform( new (C, 3) CmpUNode(array_length, last) ); + Node* bol_lt = _gvn.transform( new (C, 2) BoolNode(cmp_lt, BoolTest::lt) ); + Node* is_over = generate_guard(bol_lt, region, PROB_MIN); + return is_over; +} + + +//--------------------------generate_current_thread-------------------- +Node* LibraryCallKit::generate_current_thread(Node* &tls_output) { + ciKlass* thread_klass = env()->Thread_klass(); + const Type* thread_type = TypeOopPtr::make_from_klass(thread_klass)->cast_to_ptr_type(TypePtr::NotNull); + Node* thread = _gvn.transform(new (C, 1) ThreadLocalNode()); + Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(JavaThread::threadObj_offset())); + Node* threadObj = make_load(NULL, p, thread_type, T_OBJECT); + tls_output = thread; + return threadObj; +} + + +//------------------------------inline_string_compareTo------------------------ +bool LibraryCallKit::inline_string_compareTo() { + + const int value_offset = java_lang_String::value_offset_in_bytes(); + const int count_offset = java_lang_String::count_offset_in_bytes(); + const int offset_offset = java_lang_String::offset_offset_in_bytes(); + + _sp += 2; + Node *argument = pop(); // pop non-receiver first: it was pushed second + Node *receiver = pop(); + + // Null check on self without removing any arguments. The argument + // null check technically happens in the wrong place, which can lead to + // invalid stack traces when string compare is inlined into a method + // which handles NullPointerExceptions. + _sp += 2; + receiver = do_null_check(receiver, T_OBJECT); + argument = do_null_check(argument, T_OBJECT); + _sp -= 2; + if (stopped()) { + return true; + } + + ciInstanceKlass* klass = env()->String_klass(); + const TypeInstPtr* string_type = + TypeInstPtr::make(TypePtr::BotPTR, klass, false, NULL, 0); + + Node* compare = + _gvn.transform(new (C, 7) StrCompNode( + control(), + memory(TypeAryPtr::CHARS), + memory(string_type->add_offset(value_offset)), + memory(string_type->add_offset(count_offset)), + memory(string_type->add_offset(offset_offset)), + receiver, + argument)); + push(compare); + return true; +} + +// Java version of String.indexOf(constant string) +// class StringDecl { +// StringDecl(char[] ca) { +// offset = 0; +// count = ca.length; +// value = ca; +// } +// int offset; +// int count; +// char[] value; +// } +// +// static int string_indexOf_J(StringDecl string_object, char[] target_object, +// int targetOffset, int cache_i, int md2) { +// int cache = cache_i; +// int sourceOffset = string_object.offset; +// int sourceCount = string_object.count; +// int targetCount = target_object.length; +// +// int targetCountLess1 = targetCount - 1; +// int sourceEnd = sourceOffset + sourceCount - targetCountLess1; +// +// char[] source = string_object.value; +// char[] target = target_object; +// int lastChar = target[targetCountLess1]; +// +// outer_loop: +// for (int i = sourceOffset; i < sourceEnd; ) { +// int src = source[i + targetCountLess1]; +// if (src == lastChar) { +// // With random strings and a 4-character alphabet, +// // reverse matching at this point sets up 0.8% fewer +// // frames, but (paradoxically) makes 0.3% more probes. +// // Since those probes are nearer the lastChar probe, +// // there is may be a net D$ win with reverse matching. +// // But, reversing loop inhibits unroll of inner loop +// // for unknown reason. So, does running outer loop from +// // (sourceOffset - targetCountLess1) to (sourceOffset + sourceCount) +// for (int j = 0; j < targetCountLess1; j++) { +// if (target[targetOffset + j] != source[i+j]) { +// if ((cache & (1 << source[i+j])) == 0) { +// if (md2 < j+1) { +// i += j+1; +// continue outer_loop; +// } +// } +// i += md2; +// continue outer_loop; +// } +// } +// return i - sourceOffset; +// } +// if ((cache & (1 << src)) == 0) { +// i += targetCountLess1; +// } // using "i += targetCount;" and an "else i++;" causes a jump to jump. +// i++; +// } +// return -1; +// } + +//------------------------------string_indexOf------------------------ +Node* LibraryCallKit::string_indexOf(Node* string_object, ciTypeArray* target_array, jint targetOffset_i, + jint cache_i, jint md2_i) { + + Node* no_ctrl = NULL; + float likely = PROB_LIKELY(0.9); + float unlikely = PROB_UNLIKELY(0.9); + + const int value_offset = java_lang_String::value_offset_in_bytes(); + const int count_offset = java_lang_String::count_offset_in_bytes(); + const int offset_offset = java_lang_String::offset_offset_in_bytes(); + + ciInstanceKlass* klass = env()->String_klass(); + const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::BotPTR, klass, false, NULL, 0); + const TypeAryPtr* source_type = TypeAryPtr::make(TypePtr::NotNull, TypeAry::make(TypeInt::CHAR,TypeInt::POS), ciTypeArrayKlass::make(T_CHAR), true, 0); + + Node* sourceOffseta = basic_plus_adr(string_object, string_object, offset_offset); + Node* sourceOffset = make_load(no_ctrl, sourceOffseta, TypeInt::INT, T_INT, string_type->add_offset(offset_offset)); + Node* sourceCounta = basic_plus_adr(string_object, string_object, count_offset); + Node* sourceCount = make_load(no_ctrl, sourceCounta, TypeInt::INT, T_INT, string_type->add_offset(count_offset)); + Node* sourcea = basic_plus_adr(string_object, string_object, value_offset); + Node* source = make_load(no_ctrl, sourcea, source_type, T_OBJECT, string_type->add_offset(value_offset)); + + Node* target = _gvn.transform(ConPNode::make(C, target_array)); + jint target_length = target_array->length(); + const TypeAry* target_array_type = TypeAry::make(TypeInt::CHAR, TypeInt::make(0, target_length, Type::WidenMin)); + const TypeAryPtr* target_type = TypeAryPtr::make(TypePtr::BotPTR, target_array_type, target_array->klass(), true, Type::OffsetBot); + + IdealKit kit(gvn(), control(), merged_memory()); +#define __ kit. + Node* zero = __ ConI(0); + Node* one = __ ConI(1); + Node* cache = __ ConI(cache_i); + Node* md2 = __ ConI(md2_i); + Node* lastChar = __ ConI(target_array->char_at(target_length - 1)); + Node* targetCount = __ ConI(target_length); + Node* targetCountLess1 = __ ConI(target_length - 1); + Node* targetOffset = __ ConI(targetOffset_i); + Node* sourceEnd = __ SubI(__ AddI(sourceOffset, sourceCount), targetCountLess1); + + IdealVariable rtn(kit), i(kit), j(kit); __ declares_done(); + Node* outer_loop = __ make_label(2 /* goto */); + Node* return_ = __ make_label(1); + + __ set(rtn,__ ConI(-1)); + __ loop(i, sourceOffset, BoolTest::lt, sourceEnd); { + Node* i2 = __ AddI(__ value(i), targetCountLess1); + // pin to prohibit loading of "next iteration" value which may SEGV (rare) + Node* src = load_array_element(__ ctrl(), source, i2, TypeAryPtr::CHARS); + __ if_then(src, BoolTest::eq, lastChar, unlikely); { + __ loop(j, zero, BoolTest::lt, targetCountLess1); { + Node* tpj = __ AddI(targetOffset, __ value(j)); + Node* targ = load_array_element(no_ctrl, target, tpj, target_type); + Node* ipj = __ AddI(__ value(i), __ value(j)); + Node* src2 = load_array_element(no_ctrl, source, ipj, TypeAryPtr::CHARS); + __ if_then(targ, BoolTest::ne, src2); { + __ if_then(__ AndI(cache, __ LShiftI(one, src2)), BoolTest::eq, zero); { + __ if_then(md2, BoolTest::lt, __ AddI(__ value(j), one)); { + __ increment(i, __ AddI(__ value(j), one)); + __ goto_(outer_loop); + } __ end_if(); __ dead(j); + }__ end_if(); __ dead(j); + __ increment(i, md2); + __ goto_(outer_loop); + }__ end_if(); + __ increment(j, one); + }__ end_loop(); __ dead(j); + __ set(rtn, __ SubI(__ value(i), sourceOffset)); __ dead(i); + __ goto_(return_); + }__ end_if(); + __ if_then(__ AndI(cache, __ LShiftI(one, src)), BoolTest::eq, zero, likely); { + __ increment(i, targetCountLess1); + }__ end_if(); + __ increment(i, one); + __ bind(outer_loop); + }__ end_loop(); __ dead(i); + __ bind(return_); + __ drain_delay_transform(); + + set_control(__ ctrl()); + Node* result = __ value(rtn); +#undef __ + C->set_has_loops(true); + return result; +} + + +//------------------------------inline_string_indexOf------------------------ +bool LibraryCallKit::inline_string_indexOf() { + + _sp += 2; + Node *argument = pop(); // pop non-receiver first: it was pushed second + Node *receiver = pop(); + + // don't intrinsify is argument isn't a constant string. + if (!argument->is_Con()) { + return false; + } + const TypeOopPtr* str_type = _gvn.type(argument)->isa_oopptr(); + if (str_type == NULL) { + return false; + } + ciInstanceKlass* klass = env()->String_klass(); + ciObject* str_const = str_type->const_oop(); + if (str_const == NULL || str_const->klass() != klass) { + return false; + } + ciInstance* str = str_const->as_instance(); + assert(str != NULL, "must be instance"); + + const int value_offset = java_lang_String::value_offset_in_bytes(); + const int count_offset = java_lang_String::count_offset_in_bytes(); + const int offset_offset = java_lang_String::offset_offset_in_bytes(); + + ciObject* v = str->field_value_by_offset(value_offset).as_object(); + int o = str->field_value_by_offset(offset_offset).as_int(); + int c = str->field_value_by_offset(count_offset).as_int(); + ciTypeArray* pat = v->as_type_array(); // pattern (argument) character array + + // constant strings have no offset and count == length which + // simplifies the resulting code somewhat so lets optimize for that. + if (o != 0 || c != pat->length()) { + return false; + } + + // Null check on self without removing any arguments. The argument + // null check technically happens in the wrong place, which can lead to + // invalid stack traces when string compare is inlined into a method + // which handles NullPointerExceptions. + _sp += 2; + receiver = do_null_check(receiver, T_OBJECT); + // No null check on the argument is needed since it's a constant String oop. + _sp -= 2; + if (stopped()) { + return true; + } + + // The null string as a pattern always returns 0 (match at beginning of string) + if (c == 0) { + push(intcon(0)); + return true; + } + + jchar lastChar = pat->char_at(o + (c - 1)); + int cache = 0; + int i; + for (i = 0; i < c - 1; i++) { + assert(i < pat->length(), "out of range"); + cache |= (1 << (pat->char_at(o + i) & (sizeof(cache) * BitsPerByte - 1))); + } + + int md2 = c; + for (i = 0; i < c - 1; i++) { + assert(i < pat->length(), "out of range"); + if (pat->char_at(o + i) == lastChar) { + md2 = (c - 1) - i; + } + } + + Node* result = string_indexOf(receiver, pat, o, cache, md2); + push(result); + return true; +} + +//--------------------------pop_math_arg-------------------------------- +// Pop a double argument to a math function from the stack +// rounding it if necessary. +Node * LibraryCallKit::pop_math_arg() { + Node *arg = pop_pair(); + if( Matcher::strict_fp_requires_explicit_rounding && UseSSE<=1 ) + arg = _gvn.transform( new (C, 2) RoundDoubleNode(0, arg) ); + return arg; +} + +//------------------------------inline_trig---------------------------------- +// Inline sin/cos/tan instructions, if possible. If rounding is required, do +// argument reduction which will turn into a fast/slow diamond. +bool LibraryCallKit::inline_trig(vmIntrinsics::ID id) { + _sp += arg_size(); // restore stack pointer + Node* arg = pop_math_arg(); + Node* trig = NULL; + + switch (id) { + case vmIntrinsics::_dsin: + trig = _gvn.transform((Node*)new (C, 2) SinDNode(arg)); + break; + case vmIntrinsics::_dcos: + trig = _gvn.transform((Node*)new (C, 2) CosDNode(arg)); + break; + case vmIntrinsics::_dtan: + trig = _gvn.transform((Node*)new (C, 2) TanDNode(arg)); + break; + default: + assert(false, "bad intrinsic was passed in"); + return false; + } + + // Rounding required? Check for argument reduction! + if( Matcher::strict_fp_requires_explicit_rounding ) { + + static const double pi_4 = 0.7853981633974483; + static const double neg_pi_4 = -0.7853981633974483; + // pi/2 in 80-bit extended precision + // static const unsigned char pi_2_bits_x[] = {0x35,0xc2,0x68,0x21,0xa2,0xda,0x0f,0xc9,0xff,0x3f,0x00,0x00,0x00,0x00,0x00,0x00}; + // -pi/2 in 80-bit extended precision + // static const unsigned char neg_pi_2_bits_x[] = {0x35,0xc2,0x68,0x21,0xa2,0xda,0x0f,0xc9,0xff,0xbf,0x00,0x00,0x00,0x00,0x00,0x00}; + // Cutoff value for using this argument reduction technique + //static const double pi_2_minus_epsilon = 1.564660403643354; + //static const double neg_pi_2_plus_epsilon = -1.564660403643354; + + // Pseudocode for sin: + // if (x <= Math.PI / 4.0) { + // if (x >= -Math.PI / 4.0) return fsin(x); + // if (x >= -Math.PI / 2.0) return -fcos(x + Math.PI / 2.0); + // } else { + // if (x <= Math.PI / 2.0) return fcos(x - Math.PI / 2.0); + // } + // return StrictMath.sin(x); + + // Pseudocode for cos: + // if (x <= Math.PI / 4.0) { + // if (x >= -Math.PI / 4.0) return fcos(x); + // if (x >= -Math.PI / 2.0) return fsin(x + Math.PI / 2.0); + // } else { + // if (x <= Math.PI / 2.0) return -fsin(x - Math.PI / 2.0); + // } + // return StrictMath.cos(x); + + // Actually, sticking in an 80-bit Intel value into C2 will be tough; it + // requires a special machine instruction to load it. Instead we'll try + // the 'easy' case. If we really need the extra range +/- PI/2 we'll + // probably do the math inside the SIN encoding. + + // Make the merge point + RegionNode *r = new (C, 3) RegionNode(3); + Node *phi = new (C, 3) PhiNode(r,Type::DOUBLE); + + // Flatten arg so we need only 1 test + Node *abs = _gvn.transform(new (C, 2) AbsDNode(arg)); + // Node for PI/4 constant + Node *pi4 = makecon(TypeD::make(pi_4)); + // Check PI/4 : abs(arg) + Node *cmp = _gvn.transform(new (C, 3) CmpDNode(pi4,abs)); + // Check: If PI/4 < abs(arg) then go slow + Node *bol = _gvn.transform( new (C, 2) BoolNode( cmp, BoolTest::lt ) ); + // Branch either way + IfNode *iff = create_and_xform_if(control(),bol, PROB_STATIC_FREQUENT, COUNT_UNKNOWN); + set_control(opt_iff(r,iff)); + + // Set fast path result + phi->init_req(2,trig); + + // Slow path - non-blocking leaf call + Node* call = NULL; + switch (id) { + case vmIntrinsics::_dsin: + call = make_runtime_call(RC_LEAF, OptoRuntime::Math_D_D_Type(), + CAST_FROM_FN_PTR(address, SharedRuntime::dsin), + "Sin", NULL, arg, top()); + break; + case vmIntrinsics::_dcos: + call = make_runtime_call(RC_LEAF, OptoRuntime::Math_D_D_Type(), + CAST_FROM_FN_PTR(address, SharedRuntime::dcos), + "Cos", NULL, arg, top()); + break; + case vmIntrinsics::_dtan: + call = make_runtime_call(RC_LEAF, OptoRuntime::Math_D_D_Type(), + CAST_FROM_FN_PTR(address, SharedRuntime::dtan), + "Tan", NULL, arg, top()); + break; + } + assert(control()->in(0) == call, ""); + Node* slow_result = _gvn.transform(new (C, 1) ProjNode(call,TypeFunc::Parms)); + r->init_req(1,control()); + phi->init_req(1,slow_result); + + // Post-merge + set_control(_gvn.transform(r)); + record_for_igvn(r); + trig = _gvn.transform(phi); + + C->set_has_split_ifs(true); // Has chance for split-if optimization + } + // Push result back on JVM stack + push_pair(trig); + return true; +} + +//------------------------------inline_sqrt------------------------------------- +// Inline square root instruction, if possible. +bool LibraryCallKit::inline_sqrt(vmIntrinsics::ID id) { + assert(id == vmIntrinsics::_dsqrt, "Not square root"); + _sp += arg_size(); // restore stack pointer + push_pair(_gvn.transform(new (C, 2) SqrtDNode(0, pop_math_arg()))); + return true; +} + +//------------------------------inline_abs------------------------------------- +// Inline absolute value instruction, if possible. +bool LibraryCallKit::inline_abs(vmIntrinsics::ID id) { + assert(id == vmIntrinsics::_dabs, "Not absolute value"); + _sp += arg_size(); // restore stack pointer + push_pair(_gvn.transform(new (C, 2) AbsDNode(pop_math_arg()))); + return true; +} + +//------------------------------inline_exp------------------------------------- +// Inline exp instructions, if possible. The Intel hardware only misses +// really odd corner cases (+/- Infinity). Just uncommon-trap them. +bool LibraryCallKit::inline_exp(vmIntrinsics::ID id) { + assert(id == vmIntrinsics::_dexp, "Not exp"); + + // If this inlining ever returned NaN in the past, we do not intrinsify it + // every again. NaN results requires StrictMath.exp handling. + if (too_many_traps(Deoptimization::Reason_intrinsic)) return false; + + // Do not intrinsify on older platforms which lack cmove. + if (ConditionalMoveLimit == 0) return false; + + _sp += arg_size(); // restore stack pointer + Node *x = pop_math_arg(); + Node *result = _gvn.transform(new (C, 2) ExpDNode(0,x)); + + //------------------- + //result=(result.isNaN())? StrictMath::exp():result; + // Check: If isNaN() by checking result!=result? then go to Strict Math + Node* cmpisnan = _gvn.transform(new (C, 3) CmpDNode(result,result)); + // Build the boolean node + Node* bolisnum = _gvn.transform( new (C, 2) BoolNode(cmpisnan, BoolTest::eq) ); + + { BuildCutout unless(this, bolisnum, PROB_STATIC_FREQUENT); + // End the current control-flow path + push_pair(x); + // Math.exp intrinsic returned a NaN, which requires StrictMath.exp + // to handle. Recompile without intrinsifying Math.exp + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_make_not_entrant); + } + + C->set_has_split_ifs(true); // Has chance for split-if optimization + + push_pair(result); + + return true; +} + +//------------------------------inline_pow------------------------------------- +// Inline power instructions, if possible. +bool LibraryCallKit::inline_pow(vmIntrinsics::ID id) { + assert(id == vmIntrinsics::_dpow, "Not pow"); + + // If this inlining ever returned NaN in the past, we do not intrinsify it + // every again. NaN results requires StrictMath.pow handling. + if (too_many_traps(Deoptimization::Reason_intrinsic)) return false; + + // Do not intrinsify on older platforms which lack cmove. + if (ConditionalMoveLimit == 0) return false; + + // Pseudocode for pow + // if (x <= 0.0) { + // if ((double)((int)y)==y) { // if y is int + // result = ((1&(int)y)==0)?-DPow(abs(x), y):DPow(abs(x), y) + // } else { + // result = NaN; + // } + // } else { + // result = DPow(x,y); + // } + // if (result != result)? { + // ucommon_trap(); + // } + // return result; + + _sp += arg_size(); // restore stack pointer + Node* y = pop_math_arg(); + Node* x = pop_math_arg(); + + Node *fast_result = _gvn.transform( new (C, 3) PowDNode(0, x, y) ); + + // Short form: if not top-level (i.e., Math.pow but inlining Math.pow + // inside of something) then skip the fancy tests and just check for + // NaN result. + Node *result = NULL; + if( jvms()->depth() >= 1 ) { + result = fast_result; + } else { + + // Set the merge point for If node with condition of (x <= 0.0) + // There are four possible paths to region node and phi node + RegionNode *r = new (C, 4) RegionNode(4); + Node *phi = new (C, 4) PhiNode(r, Type::DOUBLE); + + // Build the first if node: if (x <= 0.0) + // Node for 0 constant + Node *zeronode = makecon(TypeD::ZERO); + // Check x:0 + Node *cmp = _gvn.transform(new (C, 3) CmpDNode(x, zeronode)); + // Check: If (x<=0) then go complex path + Node *bol1 = _gvn.transform( new (C, 2) BoolNode( cmp, BoolTest::le ) ); + // Branch either way + IfNode *if1 = create_and_xform_if(control(),bol1, PROB_STATIC_INFREQUENT, COUNT_UNKNOWN); + Node *opt_test = _gvn.transform(if1); + //assert( opt_test->is_If(), "Expect an IfNode"); + IfNode *opt_if1 = (IfNode*)opt_test; + // Fast path taken; set region slot 3 + Node *fast_taken = _gvn.transform( new (C, 1) IfFalseNode(opt_if1) ); + r->init_req(3,fast_taken); // Capture fast-control + + // Fast path not-taken, i.e. slow path + Node *complex_path = _gvn.transform( new (C, 1) IfTrueNode(opt_if1) ); + + // Set fast path result + Node *fast_result = _gvn.transform( new (C, 3) PowDNode(0, y, x) ); + phi->init_req(3, fast_result); + + // Complex path + // Build the second if node (if y is int) + // Node for (int)y + Node *inty = _gvn.transform( new (C, 2) ConvD2INode(y)); + // Node for (double)((int) y) + Node *doubleinty= _gvn.transform( new (C, 2) ConvI2DNode(inty)); + // Check (double)((int) y) : y + Node *cmpinty= _gvn.transform(new (C, 3) CmpDNode(doubleinty, y)); + // Check if (y isn't int) then go to slow path + + Node *bol2 = _gvn.transform( new (C, 2) BoolNode( cmpinty, BoolTest::ne ) ); + // Branch eith way + IfNode *if2 = create_and_xform_if(complex_path,bol2, PROB_STATIC_INFREQUENT, COUNT_UNKNOWN); + Node *slow_path = opt_iff(r,if2); // Set region path 2 + + // Calculate DPow(abs(x), y)*(1 & (int)y) + // Node for constant 1 + Node *conone = intcon(1); + // 1& (int)y + Node *signnode= _gvn.transform( new (C, 3) AndINode(conone, inty) ); + // zero node + Node *conzero = intcon(0); + // Check (1&(int)y)==0? + Node *cmpeq1 = _gvn.transform(new (C, 3) CmpINode(signnode, conzero)); + // Check if (1&(int)y)!=0?, if so the result is negative + Node *bol3 = _gvn.transform( new (C, 2) BoolNode( cmpeq1, BoolTest::ne ) ); + // abs(x) + Node *absx=_gvn.transform( new (C, 2) AbsDNode(x)); + // abs(x)^y + Node *absxpowy = _gvn.transform( new (C, 3) PowDNode(0, y, absx) ); + // -abs(x)^y + Node *negabsxpowy = _gvn.transform(new (C, 2) NegDNode (absxpowy)); + // (1&(int)y)==1?-DPow(abs(x), y):DPow(abs(x), y) + Node *signresult = _gvn.transform( CMoveNode::make(C, NULL, bol3, absxpowy, negabsxpowy, Type::DOUBLE)); + // Set complex path fast result + phi->init_req(2, signresult); + + static const jlong nan_bits = CONST64(0x7ff8000000000000); + Node *slow_result = makecon(TypeD::make(*(double*)&nan_bits)); // return NaN + r->init_req(1,slow_path); + phi->init_req(1,slow_result); + + // Post merge + set_control(_gvn.transform(r)); + record_for_igvn(r); + result=_gvn.transform(phi); + } + + //------------------- + //result=(result.isNaN())? uncommon_trap():result; + // Check: If isNaN() by checking result!=result? then go to Strict Math + Node* cmpisnan = _gvn.transform(new (C, 3) CmpDNode(result,result)); + // Build the boolean node + Node* bolisnum = _gvn.transform( new (C, 2) BoolNode(cmpisnan, BoolTest::eq) ); + + { BuildCutout unless(this, bolisnum, PROB_STATIC_FREQUENT); + // End the current control-flow path + push_pair(x); + push_pair(y); + // Math.pow intrinsic returned a NaN, which requires StrictMath.pow + // to handle. Recompile without intrinsifying Math.pow. + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_make_not_entrant); + } + + C->set_has_split_ifs(true); // Has chance for split-if optimization + + push_pair(result); + + return true; +} + +//------------------------------inline_trans------------------------------------- +// Inline transcendental instructions, if possible. The Intel hardware gets +// these right, no funny corner cases missed. +bool LibraryCallKit::inline_trans(vmIntrinsics::ID id) { + _sp += arg_size(); // restore stack pointer + Node* arg = pop_math_arg(); + Node* trans = NULL; + + switch (id) { + case vmIntrinsics::_dlog: + trans = _gvn.transform((Node*)new (C, 2) LogDNode(arg)); + break; + case vmIntrinsics::_dlog10: + trans = _gvn.transform((Node*)new (C, 2) Log10DNode(arg)); + break; + default: + assert(false, "bad intrinsic was passed in"); + return false; + } + + // Push result back on JVM stack + push_pair(trans); + return true; +} + +//------------------------------runtime_math----------------------------- +bool LibraryCallKit::runtime_math(const TypeFunc* call_type, address funcAddr, const char* funcName) { + Node* a = NULL; + Node* b = NULL; + + assert(call_type == OptoRuntime::Math_DD_D_Type() || call_type == OptoRuntime::Math_D_D_Type(), + "must be (DD)D or (D)D type"); + + // Inputs + _sp += arg_size(); // restore stack pointer + if (call_type == OptoRuntime::Math_DD_D_Type()) { + b = pop_math_arg(); + } + a = pop_math_arg(); + + const TypePtr* no_memory_effects = NULL; + Node* trig = make_runtime_call(RC_LEAF, call_type, funcAddr, funcName, + no_memory_effects, + a, top(), b, b ? top() : NULL); + Node* value = _gvn.transform(new (C, 1) ProjNode(trig, TypeFunc::Parms+0)); +#ifdef ASSERT + Node* value_top = _gvn.transform(new (C, 1) ProjNode(trig, TypeFunc::Parms+1)); + assert(value_top == top(), "second value must be top"); +#endif + + push_pair(value); + return true; +} + +//------------------------------inline_math_native----------------------------- +bool LibraryCallKit::inline_math_native(vmIntrinsics::ID id) { + switch (id) { + // These intrinsics are not properly supported on all hardware + case vmIntrinsics::_dcos: return Matcher::has_match_rule(Op_CosD) ? inline_trig(id) : + runtime_math(OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dcos), "COS"); + case vmIntrinsics::_dsin: return Matcher::has_match_rule(Op_SinD) ? inline_trig(id) : + runtime_math(OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dsin), "SIN"); + case vmIntrinsics::_dtan: return Matcher::has_match_rule(Op_TanD) ? inline_trig(id) : + runtime_math(OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dtan), "TAN"); + + case vmIntrinsics::_dlog: return Matcher::has_match_rule(Op_LogD) ? inline_trans(id) : + runtime_math(OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dlog), "LOG"); + case vmIntrinsics::_dlog10: return Matcher::has_match_rule(Op_Log10D) ? inline_trans(id) : + runtime_math(OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dlog10), "LOG10"); + + // These intrinsics are supported on all hardware + case vmIntrinsics::_dsqrt: return Matcher::has_match_rule(Op_SqrtD) ? inline_sqrt(id) : false; + case vmIntrinsics::_dabs: return Matcher::has_match_rule(Op_AbsD) ? inline_abs(id) : false; + + // These intrinsics don't work on X86. The ad implementation doesn't + // handle NaN's properly. Instead of returning infinity, the ad + // implementation returns a NaN on overflow. See bug: 6304089 + // Once the ad implementations are fixed, change the code below + // to match the intrinsics above + + case vmIntrinsics::_dexp: return + runtime_math(OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dexp), "EXP"); + case vmIntrinsics::_dpow: return + runtime_math(OptoRuntime::Math_DD_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dpow), "POW"); + + // These intrinsics are not yet correctly implemented + case vmIntrinsics::_datan2: + return false; + + default: + ShouldNotReachHere(); + return false; + } +} + +static bool is_simple_name(Node* n) { + return (n->req() == 1 // constant + || (n->is_Type() && n->as_Type()->type()->singleton()) + || n->is_Proj() // parameter or return value + || n->is_Phi() // local of some sort + ); +} + +//----------------------------inline_min_max----------------------------------- +bool LibraryCallKit::inline_min_max(vmIntrinsics::ID id) { + push(generate_min_max(id, argument(0), argument(1))); + + return true; +} + +Node* +LibraryCallKit::generate_min_max(vmIntrinsics::ID id, Node* x0, Node* y0) { + // These are the candidate return value: + Node* xvalue = x0; + Node* yvalue = y0; + + if (xvalue == yvalue) { + return xvalue; + } + + bool want_max = (id == vmIntrinsics::_max); + + const TypeInt* txvalue = _gvn.type(xvalue)->isa_int(); + const TypeInt* tyvalue = _gvn.type(yvalue)->isa_int(); + if (txvalue == NULL || tyvalue == NULL) return top(); + // This is not really necessary, but it is consistent with a + // hypothetical MaxINode::Value method: + int widen = MAX2(txvalue->_widen, tyvalue->_widen); + + // %%% This folding logic should (ideally) be in a different place. + // Some should be inside IfNode, and there to be a more reliable + // transformation of ?: style patterns into cmoves. We also want + // more powerful optimizations around cmove and min/max. + + // Try to find a dominating comparison of these guys. + // It can simplify the index computation for Arrays.copyOf + // and similar uses of System.arraycopy. + // First, compute the normalized version of CmpI(x, y). + int cmp_op = Op_CmpI; + Node* xkey = xvalue; + Node* ykey = yvalue; + Node* ideal_cmpxy = _gvn.transform( new(C, 3) CmpINode(xkey, ykey) ); + if (ideal_cmpxy->is_Cmp()) { + // E.g., if we have CmpI(length - offset, count), + // it might idealize to CmpI(length, count + offset) + cmp_op = ideal_cmpxy->Opcode(); + xkey = ideal_cmpxy->in(1); + ykey = ideal_cmpxy->in(2); + } + + // Start by locating any relevant comparisons. + Node* start_from = (xkey->outcnt() < ykey->outcnt()) ? xkey : ykey; + Node* cmpxy = NULL; + Node* cmpyx = NULL; + for (DUIterator_Fast kmax, k = start_from->fast_outs(kmax); k < kmax; k++) { + Node* cmp = start_from->fast_out(k); + if (cmp->outcnt() > 0 && // must have prior uses + cmp->in(0) == NULL && // must be context-independent + cmp->Opcode() == cmp_op) { // right kind of compare + if (cmp->in(1) == xkey && cmp->in(2) == ykey) cmpxy = cmp; + if (cmp->in(1) == ykey && cmp->in(2) == xkey) cmpyx = cmp; + } + } + + const int NCMPS = 2; + Node* cmps[NCMPS] = { cmpxy, cmpyx }; + int cmpn; + for (cmpn = 0; cmpn < NCMPS; cmpn++) { + if (cmps[cmpn] != NULL) break; // find a result + } + if (cmpn < NCMPS) { + // Look for a dominating test that tells us the min and max. + int depth = 0; // Limit search depth for speed + Node* dom = control(); + for (; dom != NULL; dom = IfNode::up_one_dom(dom, true)) { + if (++depth >= 100) break; + Node* ifproj = dom; + if (!ifproj->is_Proj()) continue; + Node* iff = ifproj->in(0); + if (!iff->is_If()) continue; + Node* bol = iff->in(1); + if (!bol->is_Bool()) continue; + Node* cmp = bol->in(1); + if (cmp == NULL) continue; + for (cmpn = 0; cmpn < NCMPS; cmpn++) + if (cmps[cmpn] == cmp) break; + if (cmpn == NCMPS) continue; + BoolTest::mask btest = bol->as_Bool()->_test._test; + if (ifproj->is_IfFalse()) btest = BoolTest(btest).negate(); + if (cmp->in(1) == ykey) btest = BoolTest(btest).commute(); + // At this point, we know that 'x btest y' is true. + switch (btest) { + case BoolTest::eq: + // They are proven equal, so we can collapse the min/max. + // Either value is the answer. Choose the simpler. + if (is_simple_name(yvalue) && !is_simple_name(xvalue)) + return yvalue; + return xvalue; + case BoolTest::lt: // x < y + case BoolTest::le: // x <= y + return (want_max ? yvalue : xvalue); + case BoolTest::gt: // x > y + case BoolTest::ge: // x >= y + return (want_max ? xvalue : yvalue); + } + } + } + + // We failed to find a dominating test. + // Let's pick a test that might GVN with prior tests. + Node* best_bol = NULL; + BoolTest::mask best_btest = BoolTest::illegal; + for (cmpn = 0; cmpn < NCMPS; cmpn++) { + Node* cmp = cmps[cmpn]; + if (cmp == NULL) continue; + for (DUIterator_Fast jmax, j = cmp->fast_outs(jmax); j < jmax; j++) { + Node* bol = cmp->fast_out(j); + if (!bol->is_Bool()) continue; + BoolTest::mask btest = bol->as_Bool()->_test._test; + if (btest == BoolTest::eq || btest == BoolTest::ne) continue; + if (cmp->in(1) == ykey) btest = BoolTest(btest).commute(); + if (bol->outcnt() > (best_bol == NULL ? 0 : best_bol->outcnt())) { + best_bol = bol->as_Bool(); + best_btest = btest; + } + } + } + + Node* answer_if_true = NULL; + Node* answer_if_false = NULL; + switch (best_btest) { + default: + if (cmpxy == NULL) + cmpxy = ideal_cmpxy; + best_bol = _gvn.transform( new(C, 2) BoolNode(cmpxy, BoolTest::lt) ); + // and fall through: + case BoolTest::lt: // x < y + case BoolTest::le: // x <= y + answer_if_true = (want_max ? yvalue : xvalue); + answer_if_false = (want_max ? xvalue : yvalue); + break; + case BoolTest::gt: // x > y + case BoolTest::ge: // x >= y + answer_if_true = (want_max ? xvalue : yvalue); + answer_if_false = (want_max ? yvalue : xvalue); + break; + } + + jint hi, lo; + if (want_max) { + // We can sharpen the minimum. + hi = MAX2(txvalue->_hi, tyvalue->_hi); + lo = MAX2(txvalue->_lo, tyvalue->_lo); + } else { + // We can sharpen the maximum. + hi = MIN2(txvalue->_hi, tyvalue->_hi); + lo = MIN2(txvalue->_lo, tyvalue->_lo); + } + + // Use a flow-free graph structure, to avoid creating excess control edges + // which could hinder other optimizations. + // Since Math.min/max is often used with arraycopy, we want + // tightly_coupled_allocation to be able to see beyond min/max expressions. + Node* cmov = CMoveNode::make(C, NULL, best_bol, + answer_if_false, answer_if_true, + TypeInt::make(lo, hi, widen)); + + return _gvn.transform(cmov); + + /* + // This is not as desirable as it may seem, since Min and Max + // nodes do not have a full set of optimizations. + // And they would interfere, anyway, with 'if' optimizations + // and with CMoveI canonical forms. + switch (id) { + case vmIntrinsics::_min: + result_val = _gvn.transform(new (C, 3) MinINode(x,y)); break; + case vmIntrinsics::_max: + result_val = _gvn.transform(new (C, 3) MaxINode(x,y)); break; + default: + ShouldNotReachHere(); + } + */ +} + +inline int +LibraryCallKit::classify_unsafe_addr(Node* &base, Node* &offset) { + const TypePtr* base_type = TypePtr::NULL_PTR; + if (base != NULL) base_type = _gvn.type(base)->isa_ptr(); + if (base_type == NULL) { + // Unknown type. + return Type::AnyPtr; + } else if (base_type == TypePtr::NULL_PTR) { + // Since this is a NULL+long form, we have to switch to a rawptr. + base = _gvn.transform( new (C, 2) CastX2PNode(offset) ); + offset = MakeConX(0); + return Type::RawPtr; + } else if (base_type->base() == Type::RawPtr) { + return Type::RawPtr; + } else if (base_type->isa_oopptr()) { + // Base is never null => always a heap address. + if (base_type->ptr() == TypePtr::NotNull) { + return Type::OopPtr; + } + // Offset is small => always a heap address. + const TypeX* offset_type = _gvn.type(offset)->isa_intptr_t(); + if (offset_type != NULL && + base_type->offset() == 0 && // (should always be?) + offset_type->_lo >= 0 && + !MacroAssembler::needs_explicit_null_check(offset_type->_hi)) { + return Type::OopPtr; + } + // Otherwise, it might either be oop+off or NULL+addr. + return Type::AnyPtr; + } else { + // No information: + return Type::AnyPtr; + } +} + +inline Node* LibraryCallKit::make_unsafe_address(Node* base, Node* offset) { + int kind = classify_unsafe_addr(base, offset); + if (kind == Type::RawPtr) { + return basic_plus_adr(top(), base, offset); + } else { + return basic_plus_adr(base, offset); + } +} + +//----------------------------inline_reverseBytes_int/long------------------- +// inline Int.reverseBytes(int) +// inline Long.reverseByes(long) +bool LibraryCallKit::inline_reverseBytes(vmIntrinsics::ID id) { + assert(id == vmIntrinsics::_reverseBytes_i || id == vmIntrinsics::_reverseBytes_l, "not reverse Bytes"); + if (id == vmIntrinsics::_reverseBytes_i && !Matcher::has_match_rule(Op_ReverseBytesI)) return false; + if (id == vmIntrinsics::_reverseBytes_l && !Matcher::has_match_rule(Op_ReverseBytesL)) return false; + _sp += arg_size(); // restore stack pointer + switch (id) { + case vmIntrinsics::_reverseBytes_i: + push(_gvn.transform(new (C, 2) ReverseBytesINode(0, pop()))); + break; + case vmIntrinsics::_reverseBytes_l: + push_pair(_gvn.transform(new (C, 2) ReverseBytesLNode(0, pop_pair()))); + break; + default: + ; + } + return true; +} + +//----------------------------inline_unsafe_access---------------------------- + +const static BasicType T_ADDRESS_HOLDER = T_LONG; + +// Interpret Unsafe.fieldOffset cookies correctly: +extern jlong Unsafe_field_offset_to_byte_offset(jlong field_offset); + +bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, BasicType type, bool is_volatile) { + if (callee()->is_static()) return false; // caller must have the capability! + +#ifndef PRODUCT + { + ResourceMark rm; + // Check the signatures. + ciSignature* sig = signature(); +#ifdef ASSERT + if (!is_store) { + // Object getObject(Object base, int/long offset), etc. + BasicType rtype = sig->return_type()->basic_type(); + if (rtype == T_ADDRESS_HOLDER && callee()->name() == ciSymbol::getAddress_name()) + rtype = T_ADDRESS; // it is really a C void* + assert(rtype == type, "getter must return the expected value"); + if (!is_native_ptr) { + assert(sig->count() == 2, "oop getter has 2 arguments"); + assert(sig->type_at(0)->basic_type() == T_OBJECT, "getter base is object"); + assert(sig->type_at(1)->basic_type() == T_LONG, "getter offset is correct"); + } else { + assert(sig->count() == 1, "native getter has 1 argument"); + assert(sig->type_at(0)->basic_type() == T_LONG, "getter base is long"); + } + } else { + // void putObject(Object base, int/long offset, Object x), etc. + assert(sig->return_type()->basic_type() == T_VOID, "putter must not return a value"); + if (!is_native_ptr) { + assert(sig->count() == 3, "oop putter has 3 arguments"); + assert(sig->type_at(0)->basic_type() == T_OBJECT, "putter base is object"); + assert(sig->type_at(1)->basic_type() == T_LONG, "putter offset is correct"); + } else { + assert(sig->count() == 2, "native putter has 2 arguments"); + assert(sig->type_at(0)->basic_type() == T_LONG, "putter base is long"); + } + BasicType vtype = sig->type_at(sig->count()-1)->basic_type(); + if (vtype == T_ADDRESS_HOLDER && callee()->name() == ciSymbol::putAddress_name()) + vtype = T_ADDRESS; // it is really a C void* + assert(vtype == type, "putter must accept the expected value"); + } +#endif // ASSERT + } +#endif //PRODUCT + + C->set_has_unsafe_access(true); // Mark eventual nmethod as "unsafe". + + int type_words = type2size[ (type == T_ADDRESS) ? T_LONG : type ]; + + // Argument words: "this" plus (oop/offset) or (lo/hi) args plus maybe 1 or 2 value words + int nargs = 1 + (is_native_ptr ? 2 : 3) + (is_store ? type_words : 0); + + debug_only(int saved_sp = _sp); + _sp += nargs; + + Node* val; + debug_only(val = (Node*)(uintptr_t)-1); + + + if (is_store) { + // Get the value being stored. (Pop it first; it was pushed last.) + switch (type) { + case T_DOUBLE: + case T_LONG: + case T_ADDRESS: + val = pop_pair(); + break; + default: + val = pop(); + } + } + + // Build address expression. See the code in inline_unsafe_prefetch. + Node *adr; + Node *heap_base_oop = top(); + if (!is_native_ptr) { + // The offset is a value produced by Unsafe.staticFieldOffset or Unsafe.objectFieldOffset + Node* offset = pop_pair(); + // The base is either a Java object or a value produced by Unsafe.staticFieldBase + Node* base = pop(); + // We currently rely on the cookies produced by Unsafe.xxxFieldOffset + // to be plain byte offsets, which are also the same as those accepted + // by oopDesc::field_base. + assert(Unsafe_field_offset_to_byte_offset(11) == 11, + "fieldOffset must be byte-scaled"); + // 32-bit machines ignore the high half! + offset = ConvL2X(offset); + adr = make_unsafe_address(base, offset); + heap_base_oop = base; + } else { + Node* ptr = pop_pair(); + // Adjust Java long to machine word: + ptr = ConvL2X(ptr); + adr = make_unsafe_address(NULL, ptr); + } + + // Pop receiver last: it was pushed first. + Node *receiver = pop(); + + assert(saved_sp == _sp, "must have correct argument count"); + + const TypePtr *adr_type = _gvn.type(adr)->isa_ptr(); + + // First guess at the value type. + const Type *value_type = Type::get_const_basic_type(type); + + // Try to categorize the address. If it comes up as TypeJavaPtr::BOTTOM, + // there was not enough information to nail it down. + Compile::AliasType* alias_type = C->alias_type(adr_type); + assert(alias_type->index() != Compile::AliasIdxBot, "no bare pointers here"); + + // We will need memory barriers unless we can determine a unique + // alias category for this reference. (Note: If for some reason + // the barriers get omitted and the unsafe reference begins to "pollute" + // the alias analysis of the rest of the graph, either Compile::can_alias + // or Compile::must_alias will throw a diagnostic assert.) + bool need_mem_bar = (alias_type->adr_type() == TypeOopPtr::BOTTOM); + + if (!is_store && type == T_OBJECT) { + // Attempt to infer a sharper value type from the offset and base type. + ciKlass* sharpened_klass = NULL; + + // See if it is an instance field, with an object type. + if (alias_type->field() != NULL) { + assert(!is_native_ptr, "native pointer op cannot use a java address"); + if (alias_type->field()->type()->is_klass()) { + sharpened_klass = alias_type->field()->type()->as_klass(); + } + } + + // See if it is a narrow oop array. + if (adr_type->isa_aryptr()) { + if (adr_type->offset() >= objArrayOopDesc::header_size() * wordSize) { + const TypeOopPtr *elem_type = adr_type->is_aryptr()->elem()->isa_oopptr(); + if (elem_type != NULL) { + sharpened_klass = elem_type->klass(); + } + } + } + + if (sharpened_klass != NULL) { + const TypeOopPtr* tjp = TypeOopPtr::make_from_klass(sharpened_klass); + + // Sharpen the value type. + value_type = tjp; + +#ifndef PRODUCT + if (PrintIntrinsics || PrintInlining || PrintOptoInlining) { + tty->print(" from base type: "); adr_type->dump(); + tty->print(" sharpened value: "); value_type->dump(); + } +#endif + } + } + + // Null check on self without removing any arguments. The argument + // null check technically happens in the wrong place, which can lead to + // invalid stack traces when the primitive is inlined into a method + // which handles NullPointerExceptions. + _sp += nargs; + do_null_check(receiver, T_OBJECT); + _sp -= nargs; + if (stopped()) { + return true; + } + // Heap pointers get a null-check from the interpreter, + // as a courtesy. However, this is not guaranteed by Unsafe, + // and it is not possible to fully distinguish unintended nulls + // from intended ones in this API. + + if (is_volatile) { + // We need to emit leading and trailing CPU membars (see below) in + // addition to memory membars when is_volatile. This is a little + // too strong, but avoids the need to insert per-alias-type + // volatile membars (for stores; compare Parse::do_put_xxx), which + // we cannot do effctively here because we probably only have a + // rough approximation of type. + need_mem_bar = true; + // For Stores, place a memory ordering barrier now. + if (is_store) + insert_mem_bar(Op_MemBarRelease); + } + + // Memory barrier to prevent normal and 'unsafe' accesses from + // bypassing each other. Happens after null checks, so the + // exception paths do not take memory state from the memory barrier, + // so there's no problems making a strong assert about mixing users + // of safe & unsafe memory. Otherwise fails in a CTW of rt.jar + // around 5701, class sun/reflect/UnsafeBooleanFieldAccessorImpl. + if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder); + + if (!is_store) { + Node* p = make_load(control(), adr, value_type, type, adr_type, is_volatile); + // load value and push onto stack + switch (type) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + case T_FLOAT: + case T_OBJECT: + push( p ); + break; + case T_ADDRESS: + // Cast to an int type. + p = _gvn.transform( new (C, 2) CastP2XNode(NULL,p) ); + p = ConvX2L(p); + push_pair(p); + break; + case T_DOUBLE: + case T_LONG: + push_pair( p ); + break; + default: ShouldNotReachHere(); + } + } else { + // place effect of store into memory + switch (type) { + case T_DOUBLE: + val = dstore_rounding(val); + break; + case T_ADDRESS: + // Repackage the long as a pointer. + val = ConvL2X(val); + val = _gvn.transform( new (C, 2) CastX2PNode(val) ); + break; + } + + if (type != T_OBJECT ) { + (void) store_to_memory(control(), adr, val, type, adr_type, is_volatile); + } else { + // Possibly an oop being stored to Java heap or native memory + if (!TypePtr::NULL_PTR->higher_equal(_gvn.type(heap_base_oop))) { + // oop to Java heap. + (void) store_oop_to_unknown(control(), heap_base_oop, adr, adr_type, val, val->bottom_type(), type); + } else { + + // We can't tell at compile time if we are storing in the Java heap or outside + // of it. So we need to emit code to conditionally do the proper type of + // store. + + IdealKit kit(gvn(), control(), merged_memory()); + kit.declares_done(); + // QQQ who knows what probability is here?? + kit.if_then(heap_base_oop, BoolTest::ne, null(), PROB_UNLIKELY(0.999)); { + (void) store_oop_to_unknown(control(), heap_base_oop, adr, adr_type, val, val->bottom_type(), type); + } kit.else_(); { + (void) store_to_memory(control(), adr, val, type, adr_type, is_volatile); + } kit.end_if(); + } + } + } + + if (is_volatile) { + if (!is_store) + insert_mem_bar(Op_MemBarAcquire); + else + insert_mem_bar(Op_MemBarVolatile); + } + + if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder); + + return true; +} + +//----------------------------inline_unsafe_prefetch---------------------------- + +bool LibraryCallKit::inline_unsafe_prefetch(bool is_native_ptr, bool is_store, bool is_static) { +#ifndef PRODUCT + { + ResourceMark rm; + // Check the signatures. + ciSignature* sig = signature(); +#ifdef ASSERT + // Object getObject(Object base, int/long offset), etc. + BasicType rtype = sig->return_type()->basic_type(); + if (!is_native_ptr) { + assert(sig->count() == 2, "oop prefetch has 2 arguments"); + assert(sig->type_at(0)->basic_type() == T_OBJECT, "prefetch base is object"); + assert(sig->type_at(1)->basic_type() == T_LONG, "prefetcha offset is correct"); + } else { + assert(sig->count() == 1, "native prefetch has 1 argument"); + assert(sig->type_at(0)->basic_type() == T_LONG, "prefetch base is long"); + } +#endif // ASSERT + } +#endif // !PRODUCT + + C->set_has_unsafe_access(true); // Mark eventual nmethod as "unsafe". + + // Argument words: "this" if not static, plus (oop/offset) or (lo/hi) args + int nargs = (is_static ? 0 : 1) + (is_native_ptr ? 2 : 3); + + debug_only(int saved_sp = _sp); + _sp += nargs; + + // Build address expression. See the code in inline_unsafe_access. + Node *adr; + if (!is_native_ptr) { + // The offset is a value produced by Unsafe.staticFieldOffset or Unsafe.objectFieldOffset + Node* offset = pop_pair(); + // The base is either a Java object or a value produced by Unsafe.staticFieldBase + Node* base = pop(); + // We currently rely on the cookies produced by Unsafe.xxxFieldOffset + // to be plain byte offsets, which are also the same as those accepted + // by oopDesc::field_base. + assert(Unsafe_field_offset_to_byte_offset(11) == 11, + "fieldOffset must be byte-scaled"); + // 32-bit machines ignore the high half! + offset = ConvL2X(offset); + adr = make_unsafe_address(base, offset); + } else { + Node* ptr = pop_pair(); + // Adjust Java long to machine word: + ptr = ConvL2X(ptr); + adr = make_unsafe_address(NULL, ptr); + } + + if (is_static) { + assert(saved_sp == _sp, "must have correct argument count"); + } else { + // Pop receiver last: it was pushed first. + Node *receiver = pop(); + assert(saved_sp == _sp, "must have correct argument count"); + + // Null check on self without removing any arguments. The argument + // null check technically happens in the wrong place, which can lead to + // invalid stack traces when the primitive is inlined into a method + // which handles NullPointerExceptions. + _sp += nargs; + do_null_check(receiver, T_OBJECT); + _sp -= nargs; + if (stopped()) { + return true; + } + } + + // Generate the read or write prefetch + Node *prefetch; + if (is_store) { + prefetch = new (C, 3) PrefetchWriteNode(i_o(), adr); + } else { + prefetch = new (C, 3) PrefetchReadNode(i_o(), adr); + } + prefetch->init_req(0, control()); + set_i_o(_gvn.transform(prefetch)); + + return true; +} + +//----------------------------inline_unsafe_CAS---------------------------- + +bool LibraryCallKit::inline_unsafe_CAS(BasicType type) { + // This basic scheme here is the same as inline_unsafe_access, but + // differs in enough details that combining them would make the code + // overly confusing. (This is a true fact! I originally combined + // them, but even I was confused by it!) As much code/comments as + // possible are retained from inline_unsafe_access though to make + // the correspondances clearer. - dl + + if (callee()->is_static()) return false; // caller must have the capability! + +#ifndef PRODUCT + { + ResourceMark rm; + // Check the signatures. + ciSignature* sig = signature(); +#ifdef ASSERT + BasicType rtype = sig->return_type()->basic_type(); + assert(rtype == T_BOOLEAN, "CAS must return boolean"); + assert(sig->count() == 4, "CAS has 4 arguments"); + assert(sig->type_at(0)->basic_type() == T_OBJECT, "CAS base is object"); + assert(sig->type_at(1)->basic_type() == T_LONG, "CAS offset is long"); +#endif // ASSERT + } +#endif //PRODUCT + + // number of stack slots per value argument (1 or 2) + int type_words = type2size[type]; + + // Cannot inline wide CAS on machines that don't support it natively + if (type2aelembytes[type] > BytesPerInt && !VM_Version::supports_cx8()) + return false; + + C->set_has_unsafe_access(true); // Mark eventual nmethod as "unsafe". + + // Argument words: "this" plus oop plus offset plus oldvalue plus newvalue; + int nargs = 1 + 1 + 2 + type_words + type_words; + + // pop arguments: newval, oldval, offset, base, and receiver + debug_only(int saved_sp = _sp); + _sp += nargs; + Node* newval = (type_words == 1) ? pop() : pop_pair(); + Node* oldval = (type_words == 1) ? pop() : pop_pair(); + Node *offset = pop_pair(); + Node *base = pop(); + Node *receiver = pop(); + assert(saved_sp == _sp, "must have correct argument count"); + + // Null check receiver. + _sp += nargs; + do_null_check(receiver, T_OBJECT); + _sp -= nargs; + if (stopped()) { + return true; + } + + // Build field offset expression. + // We currently rely on the cookies produced by Unsafe.xxxFieldOffset + // to be plain byte offsets, which are also the same as those accepted + // by oopDesc::field_base. + assert(Unsafe_field_offset_to_byte_offset(11) == 11, "fieldOffset must be byte-scaled"); + // 32-bit machines ignore the high half of long offsets + offset = ConvL2X(offset); + Node* adr = make_unsafe_address(base, offset); + const TypePtr *adr_type = _gvn.type(adr)->isa_ptr(); + + // (Unlike inline_unsafe_access, there seems no point in trying + // to refine types. Just use the coarse types here. + const Type *value_type = Type::get_const_basic_type(type); + Compile::AliasType* alias_type = C->alias_type(adr_type); + assert(alias_type->index() != Compile::AliasIdxBot, "no bare pointers here"); + int alias_idx = C->get_alias_index(adr_type); + + // Memory-model-wise, a CAS acts like a little synchronized block, + // so needs barriers on each side. These don't't translate into + // actual barriers on most machines, but we still need rest of + // compiler to respect ordering. + + insert_mem_bar(Op_MemBarRelease); + insert_mem_bar(Op_MemBarCPUOrder); + + // 4984716: MemBars must be inserted before this + // memory node in order to avoid a false + // dependency which will confuse the scheduler. + Node *mem = memory(alias_idx); + + // For now, we handle only those cases that actually exist: ints, + // longs, and Object. Adding others should be straightforward. + Node* cas; + switch(type) { + case T_INT: + cas = _gvn.transform(new (C, 5) CompareAndSwapINode(control(), mem, adr, newval, oldval)); + break; + case T_LONG: + cas = _gvn.transform(new (C, 5) CompareAndSwapLNode(control(), mem, adr, newval, oldval)); + break; + case T_OBJECT: + // reference stores need a store barrier. + // (They don't if CAS fails, but it isn't worth checking.) + pre_barrier(control(), base, adr, alias_idx, newval, value_type, T_OBJECT); + cas = _gvn.transform(new (C, 5) CompareAndSwapPNode(control(), mem, adr, newval, oldval)); + post_barrier(control(), cas, base, adr, alias_idx, newval, T_OBJECT, true); + break; + default: + ShouldNotReachHere(); + break; + } + + // SCMemProjNodes represent the memory state of CAS. Their main + // role is to prevent CAS nodes from being optimized away when their + // results aren't used. + Node* proj = _gvn.transform( new (C, 1) SCMemProjNode(cas)); + set_memory(proj, alias_idx); + + // Add the trailing membar surrounding the access + insert_mem_bar(Op_MemBarCPUOrder); + insert_mem_bar(Op_MemBarAcquire); + + push(cas); + return true; +} + +bool LibraryCallKit::inline_unsafe_ordered_store(BasicType type) { + // This is another variant of inline_unsafe_access, differing in + // that it always issues store-store ("release") barrier and ensures + // store-atomicity (which only matters for "long"). + + if (callee()->is_static()) return false; // caller must have the capability! + +#ifndef PRODUCT + { + ResourceMark rm; + // Check the signatures. + ciSignature* sig = signature(); +#ifdef ASSERT + BasicType rtype = sig->return_type()->basic_type(); + assert(rtype == T_VOID, "must return void"); + assert(sig->count() == 3, "has 3 arguments"); + assert(sig->type_at(0)->basic_type() == T_OBJECT, "base is object"); + assert(sig->type_at(1)->basic_type() == T_LONG, "offset is long"); +#endif // ASSERT + } +#endif //PRODUCT + + // number of stack slots per value argument (1 or 2) + int type_words = type2size[type]; + + C->set_has_unsafe_access(true); // Mark eventual nmethod as "unsafe". + + // Argument words: "this" plus oop plus offset plus value; + int nargs = 1 + 1 + 2 + type_words; + + // pop arguments: val, offset, base, and receiver + debug_only(int saved_sp = _sp); + _sp += nargs; + Node* val = (type_words == 1) ? pop() : pop_pair(); + Node *offset = pop_pair(); + Node *base = pop(); + Node *receiver = pop(); + assert(saved_sp == _sp, "must have correct argument count"); + + // Null check receiver. + _sp += nargs; + do_null_check(receiver, T_OBJECT); + _sp -= nargs; + if (stopped()) { + return true; + } + + // Build field offset expression. + assert(Unsafe_field_offset_to_byte_offset(11) == 11, "fieldOffset must be byte-scaled"); + // 32-bit machines ignore the high half of long offsets + offset = ConvL2X(offset); + Node* adr = make_unsafe_address(base, offset); + const TypePtr *adr_type = _gvn.type(adr)->isa_ptr(); + const Type *value_type = Type::get_const_basic_type(type); + Compile::AliasType* alias_type = C->alias_type(adr_type); + + insert_mem_bar(Op_MemBarRelease); + insert_mem_bar(Op_MemBarCPUOrder); + // Ensure that the store is atomic for longs: + bool require_atomic_access = true; + Node* store; + if (type == T_OBJECT) // reference stores need a store barrier. + store = store_oop_to_unknown(control(), base, adr, adr_type, val, value_type, type); + else { + store = store_to_memory(control(), adr, val, type, adr_type, require_atomic_access); + } + insert_mem_bar(Op_MemBarCPUOrder); + return true; +} + +bool LibraryCallKit::inline_unsafe_allocate() { + if (callee()->is_static()) return false; // caller must have the capability! + int nargs = 1 + 1; + assert(signature()->size() == nargs-1, "alloc has 1 argument"); + null_check_receiver(callee()); // check then ignore argument(0) + _sp += nargs; // set original stack for use by uncommon_trap + Node* cls = do_null_check(argument(1), T_OBJECT); + _sp -= nargs; + if (stopped()) return true; + + Node* kls = load_klass_from_mirror(cls, false, nargs, NULL, 0); + _sp += nargs; // set original stack for use by uncommon_trap + kls = do_null_check(kls, T_OBJECT); + _sp -= nargs; + if (stopped()) return true; // argument was like int.class + + // Note: The argument might still be an illegal value like + // Serializable.class or Object[].class. The runtime will handle it. + // But we must make an explicit check for initialization. + Node* insp = basic_plus_adr(kls, instanceKlass::init_state_offset_in_bytes() + sizeof(oopDesc)); + Node* inst = make_load(NULL, insp, TypeInt::INT, T_INT); + Node* bits = intcon(instanceKlass::fully_initialized); + Node* test = _gvn.transform( new (C, 3) SubINode(inst, bits) ); + // The 'test' is non-zero if we need to take a slow path. + + Node* obj = new_instance(kls, test); + push(obj); + + return true; +} + +//------------------------inline_native_time_funcs-------------- +// inline code for System.currentTimeMillis() and System.nanoTime() +// these have the same type and signature +bool LibraryCallKit::inline_native_time_funcs(bool isNano) { + address funcAddr = isNano ? CAST_FROM_FN_PTR(address, os::javaTimeNanos) : + CAST_FROM_FN_PTR(address, os::javaTimeMillis); + const char * funcName = isNano ? "nanoTime" : "currentTimeMillis"; + const TypeFunc *tf = OptoRuntime::current_time_millis_Type(); + const TypePtr* no_memory_effects = NULL; + Node* time = make_runtime_call(RC_LEAF, tf, funcAddr, funcName, no_memory_effects); + Node* value = _gvn.transform(new (C, 1) ProjNode(time, TypeFunc::Parms+0)); +#ifdef ASSERT + Node* value_top = _gvn.transform(new (C, 1) ProjNode(time, TypeFunc::Parms + 1)); + assert(value_top == top(), "second value must be top"); +#endif + push_pair(value); + return true; +} + +//------------------------inline_native_currentThread------------------ +bool LibraryCallKit::inline_native_currentThread() { + Node* junk = NULL; + push(generate_current_thread(junk)); + return true; +} + +//------------------------inline_native_isInterrupted------------------ +bool LibraryCallKit::inline_native_isInterrupted() { + const int nargs = 1+1; // receiver + boolean + assert(nargs == arg_size(), "sanity"); + // Add a fast path to t.isInterrupted(clear_int): + // (t == Thread.current() && (!TLS._osthread._interrupted || !clear_int)) + // ? TLS._osthread._interrupted : /*slow path:*/ t.isInterrupted(clear_int) + // So, in the common case that the interrupt bit is false, + // we avoid making a call into the VM. Even if the interrupt bit + // is true, if the clear_int argument is false, we avoid the VM call. + // However, if the receiver is not currentThread, we must call the VM, + // because there must be some locking done around the operation. + + // We only go to the fast case code if we pass two guards. + // Paths which do not pass are accumulated in the slow_region. + RegionNode* slow_region = new (C, 1) RegionNode(1); + record_for_igvn(slow_region); + RegionNode* result_rgn = new (C, 4) RegionNode(1+3); // fast1, fast2, slow + PhiNode* result_val = new (C, 4) PhiNode(result_rgn, TypeInt::BOOL); + enum { no_int_result_path = 1, + no_clear_result_path = 2, + slow_result_path = 3 + }; + + // (a) Receiving thread must be the current thread. + Node* rec_thr = argument(0); + Node* tls_ptr = NULL; + Node* cur_thr = generate_current_thread(tls_ptr); + Node* cmp_thr = _gvn.transform( new (C, 3) CmpPNode(cur_thr, rec_thr) ); + Node* bol_thr = _gvn.transform( new (C, 2) BoolNode(cmp_thr, BoolTest::ne) ); + + bool known_current_thread = (_gvn.type(bol_thr) == TypeInt::ZERO); + if (!known_current_thread) + generate_slow_guard(bol_thr, slow_region); + + // (b) Interrupt bit on TLS must be false. + Node* p = basic_plus_adr(top()/*!oop*/, tls_ptr, in_bytes(JavaThread::osthread_offset())); + Node* osthread = make_load(NULL, p, TypeRawPtr::NOTNULL, T_ADDRESS); + p = basic_plus_adr(top()/*!oop*/, osthread, in_bytes(OSThread::interrupted_offset())); + Node* int_bit = make_load(NULL, p, TypeInt::BOOL, T_INT); + Node* cmp_bit = _gvn.transform( new (C, 3) CmpINode(int_bit, intcon(0)) ); + Node* bol_bit = _gvn.transform( new (C, 2) BoolNode(cmp_bit, BoolTest::ne) ); + + IfNode* iff_bit = create_and_map_if(control(), bol_bit, PROB_UNLIKELY_MAG(3), COUNT_UNKNOWN); + + // First fast path: if (!TLS._interrupted) return false; + Node* false_bit = _gvn.transform( new (C, 1) IfFalseNode(iff_bit) ); + result_rgn->init_req(no_int_result_path, false_bit); + result_val->init_req(no_int_result_path, intcon(0)); + + // drop through to next case + set_control( _gvn.transform(new (C, 1) IfTrueNode(iff_bit)) ); + + // (c) Or, if interrupt bit is set and clear_int is false, use 2nd fast path. + Node* clr_arg = argument(1); + Node* cmp_arg = _gvn.transform( new (C, 3) CmpINode(clr_arg, intcon(0)) ); + Node* bol_arg = _gvn.transform( new (C, 2) BoolNode(cmp_arg, BoolTest::ne) ); + IfNode* iff_arg = create_and_map_if(control(), bol_arg, PROB_FAIR, COUNT_UNKNOWN); + + // Second fast path: ... else if (!clear_int) return true; + Node* false_arg = _gvn.transform( new (C, 1) IfFalseNode(iff_arg) ); + result_rgn->init_req(no_clear_result_path, false_arg); + result_val->init_req(no_clear_result_path, intcon(1)); + + // drop through to next case + set_control( _gvn.transform(new (C, 1) IfTrueNode(iff_arg)) ); + + // (d) Otherwise, go to the slow path. + slow_region->add_req(control()); + set_control( _gvn.transform(slow_region) ); + + if (stopped()) { + // There is no slow path. + result_rgn->init_req(slow_result_path, top()); + result_val->init_req(slow_result_path, top()); + } else { + // non-virtual because it is a private non-static + CallJavaNode* slow_call = generate_method_call(vmIntrinsics::_isInterrupted); + + Node* slow_val = set_results_for_java_call(slow_call); + // this->control() comes from set_results_for_java_call + + // If we know that the result of the slow call will be true, tell the optimizer! + if (known_current_thread) slow_val = intcon(1); + + Node* fast_io = slow_call->in(TypeFunc::I_O); + Node* fast_mem = slow_call->in(TypeFunc::Memory); + // These two phis are pre-filled with copies of of the fast IO and Memory + Node* io_phi = PhiNode::make(result_rgn, fast_io, Type::ABIO); + Node* mem_phi = PhiNode::make(result_rgn, fast_mem, Type::MEMORY, TypePtr::BOTTOM); + + result_rgn->init_req(slow_result_path, control()); + io_phi ->init_req(slow_result_path, i_o()); + mem_phi ->init_req(slow_result_path, reset_memory()); + result_val->init_req(slow_result_path, slow_val); + + set_all_memory( _gvn.transform(mem_phi) ); + set_i_o( _gvn.transform(io_phi) ); + } + + push_result(result_rgn, result_val); + C->set_has_split_ifs(true); // Has chance for split-if optimization + + return true; +} + +//---------------------------load_mirror_from_klass---------------------------- +// Given a klass oop, load its java mirror (a java.lang.Class oop). +Node* LibraryCallKit::load_mirror_from_klass(Node* klass) { + Node* p = basic_plus_adr(klass, Klass::java_mirror_offset_in_bytes() + sizeof(oopDesc)); + return make_load(NULL, p, TypeInstPtr::MIRROR, T_OBJECT); +} + +//-----------------------load_klass_from_mirror_common------------------------- +// Given a java mirror (a java.lang.Class oop), load its corresponding klass oop. +// Test the klass oop for null (signifying a primitive Class like Integer.TYPE), +// and branch to the given path on the region. +// If never_see_null, take an uncommon trap on null, so we can optimistically +// compile for the non-null case. +// If the region is NULL, force never_see_null = true. +Node* LibraryCallKit::load_klass_from_mirror_common(Node* mirror, + bool never_see_null, + int nargs, + RegionNode* region, + int null_path, + int offset) { + if (region == NULL) never_see_null = true; + Node* p = basic_plus_adr(mirror, offset); + const TypeKlassPtr* kls_type = TypeKlassPtr::OBJECT_OR_NULL; + Node* kls = _gvn.transform(new (C, 3) LoadKlassNode(0, immutable_memory(), p, TypeRawPtr::BOTTOM, kls_type)); + _sp += nargs; // any deopt will start just before call to enclosing method + Node* null_ctl = top(); + kls = null_check_oop(kls, &null_ctl, never_see_null); + if (region != NULL) { + // Set region->in(null_path) if the mirror is a primitive (e.g, int.class). + region->init_req(null_path, null_ctl); + } else { + assert(null_ctl == top(), "no loose ends"); + } + _sp -= nargs; + return kls; +} + +//--------------------(inline_native_Class_query helpers)--------------------- +// Use this for JVM_ACC_INTERFACE, JVM_ACC_IS_CLONEABLE, JVM_ACC_HAS_FINALIZER. +// Fall through if (mods & mask) == bits, take the guard otherwise. +Node* LibraryCallKit::generate_access_flags_guard(Node* kls, int modifier_mask, int modifier_bits, RegionNode* region) { + // Branch around if the given klass has the given modifier bit set. + // Like generate_guard, adds a new path onto the region. + Node* modp = basic_plus_adr(kls, Klass::access_flags_offset_in_bytes() + sizeof(oopDesc)); + Node* mods = make_load(NULL, modp, TypeInt::INT, T_INT); + Node* mask = intcon(modifier_mask); + Node* bits = intcon(modifier_bits); + Node* mbit = _gvn.transform( new (C, 3) AndINode(mods, mask) ); + Node* cmp = _gvn.transform( new (C, 3) CmpINode(mbit, bits) ); + Node* bol = _gvn.transform( new (C, 2) BoolNode(cmp, BoolTest::ne) ); + return generate_fair_guard(bol, region); +} +Node* LibraryCallKit::generate_interface_guard(Node* kls, RegionNode* region) { + return generate_access_flags_guard(kls, JVM_ACC_INTERFACE, 0, region); +} + +//-------------------------inline_native_Class_query------------------- +bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) { + int nargs = 1+0; // just the Class mirror, in most cases + const Type* return_type = TypeInt::BOOL; + Node* prim_return_value = top(); // what happens if it's a primitive class? + bool never_see_null = !too_many_traps(Deoptimization::Reason_null_check); + bool expect_prim = false; // most of these guys expect to work on refs + + enum { _normal_path = 1, _prim_path = 2, PATH_LIMIT }; + + switch (id) { + case vmIntrinsics::_isInstance: + nargs = 1+1; // the Class mirror, plus the object getting queried about + // nothing is an instance of a primitive type + prim_return_value = intcon(0); + break; + case vmIntrinsics::_getModifiers: + prim_return_value = intcon(JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC); + assert(is_power_of_2((int)JVM_ACC_WRITTEN_FLAGS+1), "change next line"); + return_type = TypeInt::make(0, JVM_ACC_WRITTEN_FLAGS, Type::WidenMin); + break; + case vmIntrinsics::_isInterface: + prim_return_value = intcon(0); + break; + case vmIntrinsics::_isArray: + prim_return_value = intcon(0); + expect_prim = true; // cf. ObjectStreamClass.getClassSignature + break; + case vmIntrinsics::_isPrimitive: + prim_return_value = intcon(1); + expect_prim = true; // obviously + break; + case vmIntrinsics::_getSuperclass: + prim_return_value = null(); + return_type = TypeInstPtr::MIRROR->cast_to_ptr_type(TypePtr::BotPTR); + break; + case vmIntrinsics::_getComponentType: + prim_return_value = null(); + return_type = TypeInstPtr::MIRROR->cast_to_ptr_type(TypePtr::BotPTR); + break; + case vmIntrinsics::_getClassAccessFlags: + prim_return_value = intcon(JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC); + return_type = TypeInt::INT; // not bool! 6297094 + break; + default: + ShouldNotReachHere(); + } + + Node* mirror = argument(0); + Node* obj = (nargs <= 1)? top(): argument(1); + + const TypeInstPtr* mirror_con = _gvn.type(mirror)->isa_instptr(); + if (mirror_con == NULL) return false; // cannot happen? + +#ifndef PRODUCT + if (PrintIntrinsics || PrintInlining || PrintOptoInlining) { + ciType* k = mirror_con->java_mirror_type(); + if (k) { + tty->print("Inlining %s on constant Class ", vmIntrinsics::name_at(intrinsic_id())); + k->print_name(); + tty->cr(); + } + } +#endif + + // Null-check the mirror, and the mirror's klass ptr (in case it is a primitive). + RegionNode* region = new (C, PATH_LIMIT) RegionNode(PATH_LIMIT); + record_for_igvn(region); + PhiNode* phi = new (C, PATH_LIMIT) PhiNode(region, return_type); + + // The mirror will never be null of Reflection.getClassAccessFlags, however + // it may be null for Class.isInstance or Class.getModifiers. Throw a NPE + // if it is. See bug 4774291. + + // For Reflection.getClassAccessFlags(), the null check occurs in + // the wrong place; see inline_unsafe_access(), above, for a similar + // situation. + _sp += nargs; // set original stack for use by uncommon_trap + mirror = do_null_check(mirror, T_OBJECT); + _sp -= nargs; + // If mirror or obj is dead, only null-path is taken. + if (stopped()) return true; + + if (expect_prim) never_see_null = false; // expect nulls (meaning prims) + + // Now load the mirror's klass metaobject, and null-check it. + // Side-effects region with the control path if the klass is null. + Node* kls = load_klass_from_mirror(mirror, never_see_null, nargs, + region, _prim_path); + // If kls is null, we have a primitive mirror. + phi->init_req(_prim_path, prim_return_value); + if (stopped()) { push_result(region, phi); return true; } + + Node* p; // handy temp + Node* null_ctl; + + // Now that we have the non-null klass, we can perform the real query. + // For constant classes, the query will constant-fold in LoadNode::Value. + Node* query_value = top(); + switch (id) { + case vmIntrinsics::_isInstance: + // nothing is an instance of a primitive type + query_value = gen_instanceof(obj, kls); + break; + + case vmIntrinsics::_getModifiers: + p = basic_plus_adr(kls, Klass::modifier_flags_offset_in_bytes() + sizeof(oopDesc)); + query_value = make_load(NULL, p, TypeInt::INT, T_INT); + break; + + case vmIntrinsics::_isInterface: + // (To verify this code sequence, check the asserts in JVM_IsInterface.) + if (generate_interface_guard(kls, region) != NULL) + // A guard was added. If the guard is taken, it was an interface. + phi->add_req(intcon(1)); + // If we fall through, it's a plain class. + query_value = intcon(0); + break; + + case vmIntrinsics::_isArray: + // (To verify this code sequence, check the asserts in JVM_IsArrayClass.) + if (generate_array_guard(kls, region) != NULL) + // A guard was added. If the guard is taken, it was an array. + phi->add_req(intcon(1)); + // If we fall through, it's a plain class. + query_value = intcon(0); + break; + + case vmIntrinsics::_isPrimitive: + query_value = intcon(0); // "normal" path produces false + break; + + case vmIntrinsics::_getSuperclass: + // The rules here are somewhat unfortunate, but we can still do better + // with random logic than with a JNI call. + // Interfaces store null or Object as _super, but must report null. + // Arrays store an intermediate super as _super, but must report Object. + // Other types can report the actual _super. + // (To verify this code sequence, check the asserts in JVM_IsInterface.) + if (generate_interface_guard(kls, region) != NULL) + // A guard was added. If the guard is taken, it was an interface. + phi->add_req(null()); + if (generate_array_guard(kls, region) != NULL) + // A guard was added. If the guard is taken, it was an array. + phi->add_req(makecon(TypeInstPtr::make(env()->Object_klass()->java_mirror()))); + // If we fall through, it's a plain class. Get its _super. + p = basic_plus_adr(kls, Klass::super_offset_in_bytes() + sizeof(oopDesc)); + kls = _gvn.transform(new (C, 3) LoadKlassNode(0, immutable_memory(), p, TypeRawPtr::BOTTOM, TypeKlassPtr::OBJECT_OR_NULL)); + null_ctl = top(); + kls = null_check_oop(kls, &null_ctl); + if (null_ctl != top()) { + // If the guard is taken, Object.superClass is null (both klass and mirror). + region->add_req(null_ctl); + phi ->add_req(null()); + } + if (!stopped()) { + query_value = load_mirror_from_klass(kls); + } + break; + + case vmIntrinsics::_getComponentType: + if (generate_array_guard(kls, region) != NULL) { + // Be sure to pin the oop load to the guard edge just created: + Node* is_array_ctrl = region->in(region->req()-1); + Node* cma = basic_plus_adr(kls, in_bytes(arrayKlass::component_mirror_offset()) + sizeof(oopDesc)); + Node* cmo = make_load(is_array_ctrl, cma, TypeInstPtr::MIRROR, T_OBJECT); + phi->add_req(cmo); + } + query_value = null(); // non-array case is null + break; + + case vmIntrinsics::_getClassAccessFlags: + p = basic_plus_adr(kls, Klass::access_flags_offset_in_bytes() + sizeof(oopDesc)); + query_value = make_load(NULL, p, TypeInt::INT, T_INT); + break; + + default: + ShouldNotReachHere(); + } + + // Fall-through is the normal case of a query to a real class. + phi->init_req(1, query_value); + region->init_req(1, control()); + + push_result(region, phi); + C->set_has_split_ifs(true); // Has chance for split-if optimization + + return true; +} + +//--------------------------inline_native_subtype_check------------------------ +// This intrinsic takes the JNI calls out of the heart of +// UnsafeFieldAccessorImpl.set, which improves Field.set, readObject, etc. +bool LibraryCallKit::inline_native_subtype_check() { + int nargs = 1+1; // the Class mirror, plus the other class getting examined + + // Pull both arguments off the stack. + Node* args[2]; // two java.lang.Class mirrors: superc, subc + args[0] = argument(0); + args[1] = argument(1); + Node* klasses[2]; // corresponding Klasses: superk, subk + klasses[0] = klasses[1] = top(); + + enum { + // A full decision tree on {superc is prim, subc is prim}: + _prim_0_path = 1, // {P,N} => false + // {P,P} & superc!=subc => false + _prim_same_path, // {P,P} & superc==subc => true + _prim_1_path, // {N,P} => false + _ref_subtype_path, // {N,N} & subtype check wins => true + _both_ref_path, // {N,N} & subtype check loses => false + PATH_LIMIT + }; + + RegionNode* region = new (C, PATH_LIMIT) RegionNode(PATH_LIMIT); + Node* phi = new (C, PATH_LIMIT) PhiNode(region, TypeInt::BOOL); + record_for_igvn(region); + + const TypePtr* adr_type = TypeRawPtr::BOTTOM; // memory type of loads + const TypeKlassPtr* kls_type = TypeKlassPtr::OBJECT_OR_NULL; + int class_klass_offset = java_lang_Class::klass_offset_in_bytes(); + + // First null-check both mirrors and load each mirror's klass metaobject. + int which_arg; + for (which_arg = 0; which_arg <= 1; which_arg++) { + Node* arg = args[which_arg]; + _sp += nargs; // set original stack for use by uncommon_trap + arg = do_null_check(arg, T_OBJECT); + _sp -= nargs; + if (stopped()) break; + args[which_arg] = _gvn.transform(arg); + + Node* p = basic_plus_adr(arg, class_klass_offset); + Node* kls = new (C, 3) LoadKlassNode(0, immutable_memory(), p, adr_type, kls_type); + klasses[which_arg] = _gvn.transform(kls); + } + + // Having loaded both klasses, test each for null. + bool never_see_null = !too_many_traps(Deoptimization::Reason_null_check); + for (which_arg = 0; which_arg <= 1; which_arg++) { + Node* kls = klasses[which_arg]; + Node* null_ctl = top(); + _sp += nargs; // set original stack for use by uncommon_trap + kls = null_check_oop(kls, &null_ctl, never_see_null); + _sp -= nargs; + int prim_path = (which_arg == 0 ? _prim_0_path : _prim_1_path); + region->init_req(prim_path, null_ctl); + if (stopped()) break; + klasses[which_arg] = kls; + } + + if (!stopped()) { + // now we have two reference types, in klasses[0..1] + Node* subk = klasses[1]; // the argument to isAssignableFrom + Node* superk = klasses[0]; // the receiver + region->set_req(_both_ref_path, gen_subtype_check(subk, superk)); + // now we have a successful reference subtype check + region->set_req(_ref_subtype_path, control()); + } + + // If both operands are primitive (both klasses null), then + // we must return true when they are identical primitives. + // It is convenient to test this after the first null klass check. + set_control(region->in(_prim_0_path)); // go back to first null check + if (!stopped()) { + // Since superc is primitive, make a guard for the superc==subc case. + Node* cmp_eq = _gvn.transform( new (C, 3) CmpPNode(args[0], args[1]) ); + Node* bol_eq = _gvn.transform( new (C, 2) BoolNode(cmp_eq, BoolTest::eq) ); + generate_guard(bol_eq, region, PROB_FAIR); + if (region->req() == PATH_LIMIT+1) { + // A guard was added. If the added guard is taken, superc==subc. + region->swap_edges(PATH_LIMIT, _prim_same_path); + region->del_req(PATH_LIMIT); + } + region->set_req(_prim_0_path, control()); // Not equal after all. + } + + // these are the only paths that produce 'true': + phi->set_req(_prim_same_path, intcon(1)); + phi->set_req(_ref_subtype_path, intcon(1)); + + // pull together the cases: + assert(region->req() == PATH_LIMIT, "sane region"); + for (uint i = 1; i < region->req(); i++) { + Node* ctl = region->in(i); + if (ctl == NULL || ctl == top()) { + region->set_req(i, top()); + phi ->set_req(i, top()); + } else if (phi->in(i) == NULL) { + phi->set_req(i, intcon(0)); // all other paths produce 'false' + } + } + + set_control(_gvn.transform(region)); + push(_gvn.transform(phi)); + + return true; +} + +//---------------------generate_array_guard_common------------------------ +Node* LibraryCallKit::generate_array_guard_common(Node* kls, RegionNode* region, + bool obj_array, bool not_array) { + // If obj_array/non_array==false/false: + // Branch around if the given klass is in fact an array (either obj or prim). + // If obj_array/non_array==false/true: + // Branch around if the given klass is not an array klass of any kind. + // If obj_array/non_array==true/true: + // Branch around if the kls is not an oop array (kls is int[], String, etc.) + // If obj_array/non_array==true/false: + // Branch around if the kls is an oop array (Object[] or subtype) + // + // Like generate_guard, adds a new path onto the region. + jint layout_con = 0; + Node* layout_val = get_layout_helper(kls, layout_con); + if (layout_val == NULL) { + bool query = (obj_array + ? Klass::layout_helper_is_objArray(layout_con) + : Klass::layout_helper_is_javaArray(layout_con)); + if (query == not_array) { + return NULL; // never a branch + } else { // always a branch + Node* always_branch = control(); + if (region != NULL) + region->add_req(always_branch); + set_control(top()); + return always_branch; + } + } + // Now test the correct condition. + jint nval = (obj_array + ? ((jint)Klass::_lh_array_tag_type_value + << Klass::_lh_array_tag_shift) + : Klass::_lh_neutral_value); + Node* cmp = _gvn.transform( new(C, 3) CmpINode(layout_val, intcon(nval)) ); + BoolTest::mask btest = BoolTest::lt; // correct for testing is_[obj]array + // invert the test if we are looking for a non-array + if (not_array) btest = BoolTest(btest).negate(); + Node* bol = _gvn.transform( new(C, 2) BoolNode(cmp, btest) ); + return generate_fair_guard(bol, region); +} + + +//-----------------------inline_native_newArray-------------------------- +bool LibraryCallKit::inline_native_newArray() { + int nargs = 2; + Node* mirror = argument(0); + Node* count_val = argument(1); + + _sp += nargs; // set original stack for use by uncommon_trap + mirror = do_null_check(mirror, T_OBJECT); + _sp -= nargs; + + enum { _normal_path = 1, _slow_path = 2, PATH_LIMIT }; + RegionNode* result_reg = new(C, PATH_LIMIT) RegionNode(PATH_LIMIT); + PhiNode* result_val = new(C, PATH_LIMIT) PhiNode(result_reg, + TypeInstPtr::NOTNULL); + PhiNode* result_io = new(C, PATH_LIMIT) PhiNode(result_reg, Type::ABIO); + PhiNode* result_mem = new(C, PATH_LIMIT) PhiNode(result_reg, Type::MEMORY, + TypePtr::BOTTOM); + + bool never_see_null = !too_many_traps(Deoptimization::Reason_null_check); + Node* klass_node = load_array_klass_from_mirror(mirror, never_see_null, + nargs, + result_reg, _slow_path); + Node* normal_ctl = control(); + Node* no_array_ctl = result_reg->in(_slow_path); + + // Generate code for the slow case. We make a call to newArray(). + set_control(no_array_ctl); + if (!stopped()) { + // Either the input type is void.class, or else the + // array klass has not yet been cached. Either the + // ensuing call will throw an exception, or else it + // will cache the array klass for next time. + PreserveJVMState pjvms(this); + CallJavaNode* slow_call = generate_method_call_static(vmIntrinsics::_newArray); + Node* slow_result = set_results_for_java_call(slow_call); + // this->control() comes from set_results_for_java_call + result_reg->set_req(_slow_path, control()); + result_val->set_req(_slow_path, slow_result); + result_io ->set_req(_slow_path, i_o()); + result_mem->set_req(_slow_path, reset_memory()); + } + + set_control(normal_ctl); + if (!stopped()) { + // Normal case: The array type has been cached in the java.lang.Class. + // The following call works fine even if the array type is polymorphic. + // It could be a dynamic mix of int[], boolean[], Object[], etc. + _sp += nargs; // set original stack for use by uncommon_trap + Node* obj = new_array(klass_node, count_val); + _sp -= nargs; + result_reg->init_req(_normal_path, control()); + result_val->init_req(_normal_path, obj); + result_io ->init_req(_normal_path, i_o()); + result_mem->init_req(_normal_path, reset_memory()); + } + + // Return the combined state. + set_i_o( _gvn.transform(result_io) ); + set_all_memory( _gvn.transform(result_mem) ); + push_result(result_reg, result_val); + C->set_has_split_ifs(true); // Has chance for split-if optimization + + return true; +} + +//----------------------inline_native_getLength-------------------------- +bool LibraryCallKit::inline_native_getLength() { + if (too_many_traps(Deoptimization::Reason_intrinsic)) return false; + + int nargs = 1; + Node* array = argument(0); + + _sp += nargs; // set original stack for use by uncommon_trap + array = do_null_check(array, T_OBJECT); + _sp -= nargs; + + // If array is dead, only null-path is taken. + if (stopped()) return true; + + // Deoptimize if it is a non-array. + Node* non_array = generate_non_array_guard(load_object_klass(array), NULL); + + if (non_array != NULL) { + PreserveJVMState pjvms(this); + set_control(non_array); + _sp += nargs; // push the arguments back on the stack + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_maybe_recompile); + } + + // If control is dead, only non-array-path is taken. + if (stopped()) return true; + + // The works fine even if the array type is polymorphic. + // It could be a dynamic mix of int[], boolean[], Object[], etc. + push( load_array_length(array) ); + + C->set_has_split_ifs(true); // Has chance for split-if optimization + + return true; +} + +//------------------------inline_array_copyOf---------------------------- +bool LibraryCallKit::inline_array_copyOf(bool is_copyOfRange) { + if (too_many_traps(Deoptimization::Reason_intrinsic)) return false; + + // Restore the stack and pop off the arguments. + int nargs = 3 + (is_copyOfRange? 1: 0); + Node* original = argument(0); + Node* start = is_copyOfRange? argument(1): intcon(0); + Node* end = is_copyOfRange? argument(2): argument(1); + Node* array_type_mirror = is_copyOfRange? argument(3): argument(2); + + _sp += nargs; // set original stack for use by uncommon_trap + array_type_mirror = do_null_check(array_type_mirror, T_OBJECT); + original = do_null_check(original, T_OBJECT); + _sp -= nargs; + + // Check if a null path was taken unconditionally. + if (stopped()) return true; + + Node* orig_length = load_array_length(original); + + Node* klass_node = load_klass_from_mirror(array_type_mirror, false, nargs, + NULL, 0); + _sp += nargs; // set original stack for use by uncommon_trap + klass_node = do_null_check(klass_node, T_OBJECT); + _sp -= nargs; + + RegionNode* bailout = new (C, 1) RegionNode(1); + record_for_igvn(bailout); + + // Despite the generic type of Arrays.copyOf, the mirror might be int, int[], etc. + // Bail out if that is so. + Node* not_objArray = generate_non_objArray_guard(klass_node, bailout); + if (not_objArray != NULL) { + // Improve the klass node's type from the new optimistic assumption: + ciKlass* ak = ciArrayKlass::make(env()->Object_klass()); + const Type* akls = TypeKlassPtr::make(TypePtr::NotNull, ak, 0/*offset*/); + Node* cast = new (C, 2) CastPPNode(klass_node, akls); + cast->init_req(0, control()); + klass_node = _gvn.transform(cast); + } + + // Bail out if either start or end is negative. + generate_negative_guard(start, bailout, &start); + generate_negative_guard(end, bailout, &end); + + Node* length = end; + if (_gvn.type(start) != TypeInt::ZERO) { + length = _gvn.transform( new (C, 3) SubINode(end, start) ); + } + + // Bail out if length is negative. + // ...Not needed, since the new_array will throw the right exception. + //generate_negative_guard(length, bailout, &length); + + if (bailout->req() > 1) { + PreserveJVMState pjvms(this); + set_control( _gvn.transform(bailout) ); + _sp += nargs; // push the arguments back on the stack + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_maybe_recompile); + } + + if (!stopped()) { + // How many elements will we copy from the original? + // The answer is MinI(orig_length - start, length). + Node* orig_tail = _gvn.transform( new(C, 3) SubINode(orig_length, start) ); + Node* moved = generate_min_max(vmIntrinsics::_min, orig_tail, length); + + _sp += nargs; // set original stack for use by uncommon_trap + Node* newcopy = new_array(klass_node, length); + _sp -= nargs; + + // Generate a direct call to the right arraycopy function(s). + // We know the copy is disjoint but we might not know if the + // oop stores need checking. + // Extreme case: Arrays.copyOf((Integer[])x, 10, String[].class). + // This will fail a store-check if x contains any non-nulls. + bool disjoint_bases = true; + bool length_never_negative = true; + generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT, + original, start, newcopy, intcon(0), moved, + nargs, disjoint_bases, length_never_negative); + + push(newcopy); + } + + C->set_has_split_ifs(true); // Has chance for split-if optimization + + return true; +} + + +//----------------------generate_virtual_guard--------------------------- +// Helper for hashCode and clone. Peeks inside the vtable to avoid a call. +Node* LibraryCallKit::generate_virtual_guard(Node* obj_klass, + RegionNode* slow_region) { + ciMethod* method = callee(); + int vtable_index = method->vtable_index(); + // Get the methodOop out of the appropriate vtable entry. + int entry_offset = (instanceKlass::vtable_start_offset() + + vtable_index*vtableEntry::size()) * wordSize + + vtableEntry::method_offset_in_bytes(); + Node* entry_addr = basic_plus_adr(obj_klass, entry_offset); + Node* target_call = make_load(NULL, entry_addr, TypeInstPtr::NOTNULL, T_OBJECT); + + // Compare the target method with the expected method (e.g., Object.hashCode). + const TypeInstPtr* native_call_addr = TypeInstPtr::make(method); + + Node* native_call = makecon(native_call_addr); + Node* chk_native = _gvn.transform( new(C, 3) CmpPNode(target_call, native_call) ); + Node* test_native = _gvn.transform( new(C, 2) BoolNode(chk_native, BoolTest::ne) ); + + return generate_slow_guard(test_native, slow_region); +} + +//-----------------------generate_method_call---------------------------- +// Use generate_method_call to make a slow-call to the real +// method if the fast path fails. An alternative would be to +// use a stub like OptoRuntime::slow_arraycopy_Java. +// This only works for expanding the current library call, +// not another intrinsic. (E.g., don't use this for making an +// arraycopy call inside of the copyOf intrinsic.) +CallJavaNode* +LibraryCallKit::generate_method_call(vmIntrinsics::ID method_id, bool is_virtual, bool is_static) { + // When compiling the intrinsic method itself, do not use this technique. + guarantee(callee() != C->method(), "cannot make slow-call to self"); + + ciMethod* method = callee(); + // ensure the JVMS we have will be correct for this call + guarantee(method_id == method->intrinsic_id(), "must match"); + + const TypeFunc* tf = TypeFunc::make(method); + int tfdc = tf->domain()->cnt(); + CallJavaNode* slow_call; + if (is_static) { + assert(!is_virtual, ""); + slow_call = new(C, tfdc) CallStaticJavaNode(tf, + SharedRuntime::get_resolve_static_call_stub(), + method, bci()); + } else if (is_virtual) { + null_check_receiver(method); + int vtable_index = methodOopDesc::invalid_vtable_index; + if (UseInlineCaches) { + // Suppress the vtable call + } else { + // hashCode and clone are not a miranda methods, + // so the vtable index is fixed. + // No need to use the linkResolver to get it. + vtable_index = method->vtable_index(); + } + slow_call = new(C, tfdc) CallDynamicJavaNode(tf, + SharedRuntime::get_resolve_virtual_call_stub(), + method, vtable_index, bci()); + } else { // neither virtual nor static: opt_virtual + null_check_receiver(method); + slow_call = new(C, tfdc) CallStaticJavaNode(tf, + SharedRuntime::get_resolve_opt_virtual_call_stub(), + method, bci()); + slow_call->set_optimized_virtual(true); + } + set_arguments_for_java_call(slow_call); + set_edges_for_java_call(slow_call); + return slow_call; +} + + +//------------------------------inline_native_hashcode-------------------- +// Build special case code for calls to hashCode on an object. +bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { + assert(is_static == callee()->is_static(), "correct intrinsic selection"); + assert(!(is_virtual && is_static), "either virtual, special, or static"); + + enum { _slow_path = 1, _fast_path, _null_path, PATH_LIMIT }; + + RegionNode* result_reg = new(C, PATH_LIMIT) RegionNode(PATH_LIMIT); + PhiNode* result_val = new(C, PATH_LIMIT) PhiNode(result_reg, + TypeInt::INT); + PhiNode* result_io = new(C, PATH_LIMIT) PhiNode(result_reg, Type::ABIO); + PhiNode* result_mem = new(C, PATH_LIMIT) PhiNode(result_reg, Type::MEMORY, + TypePtr::BOTTOM); + Node* obj = NULL; + if (!is_static) { + // Check for hashing null object + obj = null_check_receiver(callee()); + if (stopped()) return true; // unconditionally null + result_reg->init_req(_null_path, top()); + result_val->init_req(_null_path, top()); + } else { + // Do a null check, and return zero if null. + // System.identityHashCode(null) == 0 + obj = argument(0); + Node* null_ctl = top(); + obj = null_check_oop(obj, &null_ctl); + result_reg->init_req(_null_path, null_ctl); + result_val->init_req(_null_path, _gvn.intcon(0)); + } + + // Unconditionally null? Then return right away. + if (stopped()) { + set_control( result_reg->in(_null_path) ); + if (!stopped()) + push( result_val ->in(_null_path) ); + return true; + } + + // After null check, get the object's klass. + Node* obj_klass = load_object_klass(obj); + + // This call may be virtual (invokevirtual) or bound (invokespecial). + // For each case we generate slightly different code. + + // We only go to the fast case code if we pass a number of guards. The + // paths which do not pass are accumulated in the slow_region. + RegionNode* slow_region = new (C, 1) RegionNode(1); + record_for_igvn(slow_region); + + // If this is a virtual call, we generate a funny guard. We pull out + // the vtable entry corresponding to hashCode() from the target object. + // If the target method which we are calling happens to be the native + // Object hashCode() method, we pass the guard. We do not need this + // guard for non-virtual calls -- the caller is known to be the native + // Object hashCode(). + if (is_virtual) { + generate_virtual_guard(obj_klass, slow_region); + } + + // Get the header out of the object, use LoadMarkNode when available + Node* header_addr = basic_plus_adr(obj, oopDesc::mark_offset_in_bytes()); + Node* header = make_load(NULL, header_addr, TypeRawPtr::BOTTOM, T_ADDRESS); + header = _gvn.transform( new (C, 2) CastP2XNode(NULL, header) ); + + // Test the header to see if it is unlocked. + Node *lock_mask = _gvn.MakeConX(markOopDesc::biased_lock_mask_in_place); + Node *lmasked_header = _gvn.transform( new (C, 3) AndXNode(header, lock_mask) ); + Node *unlocked_val = _gvn.MakeConX(markOopDesc::unlocked_value); + Node *chk_unlocked = _gvn.transform( new (C, 3) CmpXNode( lmasked_header, unlocked_val)); + Node *test_unlocked = _gvn.transform( new (C, 2) BoolNode( chk_unlocked, BoolTest::ne) ); + + generate_slow_guard(test_unlocked, slow_region); + + // Get the hash value and check to see that it has been properly assigned. + // We depend on hash_mask being at most 32 bits and avoid the use of + // hash_mask_in_place because it could be larger than 32 bits in a 64-bit + // vm: see markOop.hpp. + Node *hash_mask = _gvn.intcon(markOopDesc::hash_mask); + Node *hash_shift = _gvn.intcon(markOopDesc::hash_shift); + Node *hshifted_header= _gvn.transform( new (C, 3) URShiftXNode(header, hash_shift) ); + // This hack lets the hash bits live anywhere in the mark object now, as long + // as the shift drops the relevent bits into the low 32 bits. Note that + // Java spec says that HashCode is an int so there's no point in capturing + // an 'X'-sized hashcode (32 in 32-bit build or 64 in 64-bit build). + hshifted_header = ConvX2I(hshifted_header); + Node *hash_val = _gvn.transform( new (C, 3) AndINode(hshifted_header, hash_mask) ); + + Node *no_hash_val = _gvn.intcon(markOopDesc::no_hash); + Node *chk_assigned = _gvn.transform( new (C, 3) CmpINode( hash_val, no_hash_val)); + Node *test_assigned = _gvn.transform( new (C, 2) BoolNode( chk_assigned, BoolTest::eq) ); + + generate_slow_guard(test_assigned, slow_region); + + Node* init_mem = reset_memory(); + // fill in the rest of the null path: + result_io ->init_req(_null_path, i_o()); + result_mem->init_req(_null_path, init_mem); + + result_val->init_req(_fast_path, hash_val); + result_reg->init_req(_fast_path, control()); + result_io ->init_req(_fast_path, i_o()); + result_mem->init_req(_fast_path, init_mem); + + // Generate code for the slow case. We make a call to hashCode(). + set_control(_gvn.transform(slow_region)); + if (!stopped()) { + // No need for PreserveJVMState, because we're using up the present state. + set_all_memory(init_mem); + vmIntrinsics::ID hashCode_id = vmIntrinsics::_hashCode; + if (is_static) hashCode_id = vmIntrinsics::_identityHashCode; + CallJavaNode* slow_call = generate_method_call(hashCode_id, is_virtual, is_static); + Node* slow_result = set_results_for_java_call(slow_call); + // this->control() comes from set_results_for_java_call + result_reg->init_req(_slow_path, control()); + result_val->init_req(_slow_path, slow_result); + result_io ->set_req(_slow_path, i_o()); + result_mem ->set_req(_slow_path, reset_memory()); + } + + // Return the combined state. + set_i_o( _gvn.transform(result_io) ); + set_all_memory( _gvn.transform(result_mem) ); + push_result(result_reg, result_val); + + return true; +} + +//---------------------------inline_native_getClass---------------------------- +// Build special case code for calls to hashCode on an object. +bool LibraryCallKit::inline_native_getClass() { + Node* obj = null_check_receiver(callee()); + if (stopped()) return true; + push( load_mirror_from_klass(load_object_klass(obj)) ); + return true; +} + +//-----------------inline_native_Reflection_getCallerClass--------------------- +// In the presence of deep enough inlining, getCallerClass() becomes a no-op. +// +// NOTE that this code must perform the same logic as +// vframeStream::security_get_caller_frame in that it must skip +// Method.invoke() and auxiliary frames. + + + + +bool LibraryCallKit::inline_native_Reflection_getCallerClass() { + ciMethod* method = callee(); + +#ifndef PRODUCT + if ((PrintIntrinsics || PrintInlining || PrintOptoInlining) && Verbose) { + tty->print_cr("Attempting to inline sun.reflect.Reflection.getCallerClass"); + } +#endif + + debug_only(int saved_sp = _sp); + + // Argument words: (int depth) + int nargs = 1; + + _sp += nargs; + Node* caller_depth_node = pop(); + + assert(saved_sp == _sp, "must have correct argument count"); + + // The depth value must be a constant in order for the runtime call + // to be eliminated. + const TypeInt* caller_depth_type = _gvn.type(caller_depth_node)->isa_int(); + if (caller_depth_type == NULL || !caller_depth_type->is_con()) { +#ifndef PRODUCT + if ((PrintIntrinsics || PrintInlining || PrintOptoInlining) && Verbose) { + tty->print_cr(" Bailing out because caller depth was not a constant"); + } +#endif + return false; + } + // Note that the JVM state at this point does not include the + // getCallerClass() frame which we are trying to inline. The + // semantics of getCallerClass(), however, are that the "first" + // frame is the getCallerClass() frame, so we subtract one from the + // requested depth before continuing. We don't inline requests of + // getCallerClass(0). + int caller_depth = caller_depth_type->get_con() - 1; + if (caller_depth < 0) { +#ifndef PRODUCT + if ((PrintIntrinsics || PrintInlining || PrintOptoInlining) && Verbose) { + tty->print_cr(" Bailing out because caller depth was %d", caller_depth); + } +#endif + return false; + } + + if (!jvms()->has_method()) { +#ifndef PRODUCT + if ((PrintIntrinsics || PrintInlining || PrintOptoInlining) && Verbose) { + tty->print_cr(" Bailing out because intrinsic was inlined at top level"); + } +#endif + return false; + } + int _depth = jvms()->depth(); // cache call chain depth + + // Walk back up the JVM state to find the caller at the required + // depth. NOTE that this code must perform the same logic as + // vframeStream::security_get_caller_frame in that it must skip + // Method.invoke() and auxiliary frames. Note also that depth is + // 1-based (1 is the bottom of the inlining). + int inlining_depth = _depth; + JVMState* caller_jvms = NULL; + + if (inlining_depth > 0) { + caller_jvms = jvms(); + assert(caller_jvms = jvms()->of_depth(inlining_depth), "inlining_depth == our depth"); + do { + // The following if-tests should be performed in this order + if (is_method_invoke_or_aux_frame(caller_jvms)) { + // Skip a Method.invoke() or auxiliary frame + } else if (caller_depth > 0) { + // Skip real frame + --caller_depth; + } else { + // We're done: reached desired caller after skipping. + break; + } + caller_jvms = caller_jvms->caller(); + --inlining_depth; + } while (inlining_depth > 0); + } + + if (inlining_depth == 0) { +#ifndef PRODUCT + if ((PrintIntrinsics || PrintInlining || PrintOptoInlining) && Verbose) { + tty->print_cr(" Bailing out because caller depth (%d) exceeded inlining depth (%d)", caller_depth_type->get_con(), _depth); + tty->print_cr(" JVM state at this point:"); + for (int i = _depth; i >= 1; i--) { + tty->print_cr(" %d) %s", i, jvms()->of_depth(i)->method()->name()->as_utf8()); + } + } +#endif + return false; // Reached end of inlining + } + + // Acquire method holder as java.lang.Class + ciInstanceKlass* caller_klass = caller_jvms->method()->holder(); + ciInstance* caller_mirror = caller_klass->java_mirror(); + // Push this as a constant + push(makecon(TypeInstPtr::make(caller_mirror))); +#ifndef PRODUCT + if ((PrintIntrinsics || PrintInlining || PrintOptoInlining) && Verbose) { + tty->print_cr(" Succeeded: caller = %s.%s, caller depth = %d, depth = %d", caller_klass->name()->as_utf8(), caller_jvms->method()->name()->as_utf8(), caller_depth_type->get_con(), _depth); + tty->print_cr(" JVM state at this point:"); + for (int i = _depth; i >= 1; i--) { + tty->print_cr(" %d) %s", i, jvms()->of_depth(i)->method()->name()->as_utf8()); + } + } +#endif + return true; +} + +// Helper routine for above +bool LibraryCallKit::is_method_invoke_or_aux_frame(JVMState* jvms) { + // Is this the Method.invoke method itself? + if (jvms->method()->intrinsic_id() == vmIntrinsics::_invoke) + return true; + + // Is this a helper, defined somewhere underneath MethodAccessorImpl. + ciKlass* k = jvms->method()->holder(); + if (k->is_instance_klass()) { + ciInstanceKlass* ik = k->as_instance_klass(); + for (; ik != NULL; ik = ik->super()) { + if (ik->name() == ciSymbol::sun_reflect_MethodAccessorImpl() && + ik == env()->find_system_klass(ik->name())) { + return true; + } + } + } + + return false; +} + +static int value_field_offset = -1; // offset of the "value" field of AtomicLongCSImpl. This is needed by + // inline_native_AtomicLong_attemptUpdate() but it has no way of + // computing it since there is no lookup field by name function in the + // CI interface. This is computed and set by inline_native_AtomicLong_get(). + // Using a static variable here is safe even if we have multiple compilation + // threads because the offset is constant. At worst the same offset will be + // computed and stored multiple + +bool LibraryCallKit::inline_native_AtomicLong_get() { + // Restore the stack and pop off the argument + _sp+=1; + Node *obj = pop(); + + // get the offset of the "value" field. Since the CI interfaces + // does not provide a way to look up a field by name, we scan the bytecodes + // to get the field index. We expect the first 2 instructions of the method + // to be: + // 0 aload_0 + // 1 getfield "value" + ciMethod* method = callee(); + if (value_field_offset == -1) + { + ciField* value_field; + ciBytecodeStream iter(method); + Bytecodes::Code bc = iter.next(); + + if ((bc != Bytecodes::_aload_0) && + ((bc != Bytecodes::_aload) || (iter.get_index() != 0))) + return false; + bc = iter.next(); + if (bc != Bytecodes::_getfield) + return false; + bool ignore; + value_field = iter.get_field(ignore); + value_field_offset = value_field->offset_in_bytes(); + } + + // Null check without removing any arguments. + _sp++; + obj = do_null_check(obj, T_OBJECT); + _sp--; + // Check for locking null object + if (stopped()) return true; + + Node *adr = basic_plus_adr(obj, obj, value_field_offset); + const TypePtr *adr_type = _gvn.type(adr)->is_ptr(); + int alias_idx = C->get_alias_index(adr_type); + + Node *result = _gvn.transform(new (C, 3) LoadLLockedNode(control(), memory(alias_idx), adr)); + + push_pair(result); + + return true; +} + +bool LibraryCallKit::inline_native_AtomicLong_attemptUpdate() { + // Restore the stack and pop off the arguments + _sp+=5; + Node *newVal = pop_pair(); + Node *oldVal = pop_pair(); + Node *obj = pop(); + + // we need the offset of the "value" field which was computed when + // inlining the get() method. Give up if we don't have it. + if (value_field_offset == -1) + return false; + + // Null check without removing any arguments. + _sp+=5; + obj = do_null_check(obj, T_OBJECT); + _sp-=5; + // Check for locking null object + if (stopped()) return true; + + Node *adr = basic_plus_adr(obj, obj, value_field_offset); + const TypePtr *adr_type = _gvn.type(adr)->is_ptr(); + int alias_idx = C->get_alias_index(adr_type); + + Node *result = _gvn.transform(new (C, 5) StoreLConditionalNode(control(), memory(alias_idx), adr, newVal, oldVal)); + Node *store_proj = _gvn.transform( new (C, 1) SCMemProjNode(result)); + set_memory(store_proj, alias_idx); + + push(result); + return true; +} + +bool LibraryCallKit::inline_fp_conversions(vmIntrinsics::ID id) { + // restore the arguments + _sp += arg_size(); + + switch (id) { + case vmIntrinsics::_floatToRawIntBits: + push(_gvn.transform( new (C, 2) MoveF2INode(pop()))); + break; + + case vmIntrinsics::_intBitsToFloat: + push(_gvn.transform( new (C, 2) MoveI2FNode(pop()))); + break; + + case vmIntrinsics::_doubleToRawLongBits: + push_pair(_gvn.transform( new (C, 2) MoveD2LNode(pop_pair()))); + break; + + case vmIntrinsics::_longBitsToDouble: + push_pair(_gvn.transform( new (C, 2) MoveL2DNode(pop_pair()))); + break; + + case vmIntrinsics::_doubleToLongBits: { + Node* value = pop_pair(); + + // two paths (plus control) merge in a wood + RegionNode *r = new (C, 3) RegionNode(3); + Node *phi = new (C, 3) PhiNode(r, TypeLong::LONG); + + Node *cmpisnan = _gvn.transform( new (C, 3) CmpDNode(value, value)); + // Build the boolean node + Node *bolisnan = _gvn.transform( new (C, 2) BoolNode( cmpisnan, BoolTest::ne ) ); + + // Branch either way. + // NaN case is less traveled, which makes all the difference. + IfNode *ifisnan = create_and_xform_if(control(), bolisnan, PROB_STATIC_FREQUENT, COUNT_UNKNOWN); + Node *opt_isnan = _gvn.transform(ifisnan); + assert( opt_isnan->is_If(), "Expect an IfNode"); + IfNode *opt_ifisnan = (IfNode*)opt_isnan; + Node *iftrue = _gvn.transform( new (C, 1) IfTrueNode(opt_ifisnan) ); + + set_control(iftrue); + + static const jlong nan_bits = CONST64(0x7ff8000000000000); + Node *slow_result = longcon(nan_bits); // return NaN + phi->init_req(1, _gvn.transform( slow_result )); + r->init_req(1, iftrue); + + // Else fall through + Node *iffalse = _gvn.transform( new (C, 1) IfFalseNode(opt_ifisnan) ); + set_control(iffalse); + + phi->init_req(2, _gvn.transform( new (C, 2) MoveD2LNode(value))); + r->init_req(2, iffalse); + + // Post merge + set_control(_gvn.transform(r)); + record_for_igvn(r); + + Node* result = _gvn.transform(phi); + assert(result->bottom_type()->isa_long(), "must be"); + push_pair(result); + + C->set_has_split_ifs(true); // Has chance for split-if optimization + + break; + } + + case vmIntrinsics::_floatToIntBits: { + Node* value = pop(); + + // two paths (plus control) merge in a wood + RegionNode *r = new (C, 3) RegionNode(3); + Node *phi = new (C, 3) PhiNode(r, TypeInt::INT); + + Node *cmpisnan = _gvn.transform( new (C, 3) CmpFNode(value, value)); + // Build the boolean node + Node *bolisnan = _gvn.transform( new (C, 2) BoolNode( cmpisnan, BoolTest::ne ) ); + + // Branch either way. + // NaN case is less traveled, which makes all the difference. + IfNode *ifisnan = create_and_xform_if(control(), bolisnan, PROB_STATIC_FREQUENT, COUNT_UNKNOWN); + Node *opt_isnan = _gvn.transform(ifisnan); + assert( opt_isnan->is_If(), "Expect an IfNode"); + IfNode *opt_ifisnan = (IfNode*)opt_isnan; + Node *iftrue = _gvn.transform( new (C, 1) IfTrueNode(opt_ifisnan) ); + + set_control(iftrue); + + static const jint nan_bits = 0x7fc00000; + Node *slow_result = makecon(TypeInt::make(nan_bits)); // return NaN + phi->init_req(1, _gvn.transform( slow_result )); + r->init_req(1, iftrue); + + // Else fall through + Node *iffalse = _gvn.transform( new (C, 1) IfFalseNode(opt_ifisnan) ); + set_control(iffalse); + + phi->init_req(2, _gvn.transform( new (C, 2) MoveF2INode(value))); + r->init_req(2, iffalse); + + // Post merge + set_control(_gvn.transform(r)); + record_for_igvn(r); + + Node* result = _gvn.transform(phi); + assert(result->bottom_type()->isa_int(), "must be"); + push(result); + + C->set_has_split_ifs(true); // Has chance for split-if optimization + + break; + } + + default: + ShouldNotReachHere(); + } + + return true; +} + +#ifdef _LP64 +#define XTOP ,top() /*additional argument*/ +#else //_LP64 +#define XTOP /*no additional argument*/ +#endif //_LP64 + +//----------------------inline_unsafe_copyMemory------------------------- +bool LibraryCallKit::inline_unsafe_copyMemory() { + if (callee()->is_static()) return false; // caller must have the capability! + int nargs = 1 + 5 + 3; // 5 args: (src: ptr,off, dst: ptr,off, size) + assert(signature()->size() == nargs-1, "copy has 5 arguments"); + null_check_receiver(callee()); // check then ignore argument(0) + if (stopped()) return true; + + C->set_has_unsafe_access(true); // Mark eventual nmethod as "unsafe". + + Node* src_ptr = argument(1); + Node* src_off = ConvL2X(argument(2)); + assert(argument(3)->is_top(), "2nd half of long"); + Node* dst_ptr = argument(4); + Node* dst_off = ConvL2X(argument(5)); + assert(argument(6)->is_top(), "2nd half of long"); + Node* size = ConvL2X(argument(7)); + assert(argument(8)->is_top(), "2nd half of long"); + + assert(Unsafe_field_offset_to_byte_offset(11) == 11, + "fieldOffset must be byte-scaled"); + + Node* src = make_unsafe_address(src_ptr, src_off); + Node* dst = make_unsafe_address(dst_ptr, dst_off); + + // Conservatively insert a memory barrier on all memory slices. + // Do not let writes of the copy source or destination float below the copy. + insert_mem_bar(Op_MemBarCPUOrder); + + // Call it. Note that the length argument is not scaled. + make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::fast_arraycopy_Type(), + StubRoutines::unsafe_arraycopy(), + "unsafe_arraycopy", + TypeRawPtr::BOTTOM, + src, dst, size XTOP); + + // Do not let reads of the copy destination float above the copy. + insert_mem_bar(Op_MemBarCPUOrder); + + return true; +} + + +//------------------------inline_native_clone---------------------------- +// Here are the simple edge cases: +// null receiver => normal trap +// virtual and clone was overridden => slow path to out-of-line clone +// not cloneable or finalizer => slow path to out-of-line Object.clone +// +// The general case has two steps, allocation and copying. +// Allocation has two cases, and uses GraphKit::new_instance or new_array. +// +// Copying also has two cases, oop arrays and everything else. +// Oop arrays use arrayof_oop_arraycopy (same as System.arraycopy). +// Everything else uses the tight inline loop supplied by CopyArrayNode. +// +// These steps fold up nicely if and when the cloned object's klass +// can be sharply typed as an object array, a type array, or an instance. +// +bool LibraryCallKit::inline_native_clone(bool is_virtual) { + int nargs = 1; + Node* obj = null_check_receiver(callee()); + if (stopped()) return true; + Node* obj_klass = load_object_klass(obj); + const TypeKlassPtr* tklass = _gvn.type(obj_klass)->isa_klassptr(); + const TypeOopPtr* toop = ((tklass != NULL) + ? tklass->as_instance_type() + : TypeInstPtr::NOTNULL); + + // Conservatively insert a memory barrier on all memory slices. + // Do not let writes into the original float below the clone. + insert_mem_bar(Op_MemBarCPUOrder); + + // paths into result_reg: + enum { + _slow_path = 1, // out-of-line call to clone method (virtual or not) + _objArray_path, // plain allocation, plus arrayof_oop_arraycopy + _fast_path, // plain allocation, plus a CopyArray operation + PATH_LIMIT + }; + RegionNode* result_reg = new(C, PATH_LIMIT) RegionNode(PATH_LIMIT); + PhiNode* result_val = new(C, PATH_LIMIT) PhiNode(result_reg, + TypeInstPtr::NOTNULL); + PhiNode* result_i_o = new(C, PATH_LIMIT) PhiNode(result_reg, Type::ABIO); + PhiNode* result_mem = new(C, PATH_LIMIT) PhiNode(result_reg, Type::MEMORY, + TypePtr::BOTTOM); + record_for_igvn(result_reg); + + const TypePtr* raw_adr_type = TypeRawPtr::BOTTOM; + int raw_adr_idx = Compile::AliasIdxRaw; + const bool raw_mem_only = true; + + // paths into alloc_reg (on the fast path, just before the CopyArray): + enum { _typeArray_alloc = 1, _instance_alloc, ALLOC_LIMIT }; + RegionNode* alloc_reg = new(C, ALLOC_LIMIT) RegionNode(ALLOC_LIMIT); + PhiNode* alloc_val = new(C, ALLOC_LIMIT) PhiNode(alloc_reg, raw_adr_type); + PhiNode* alloc_siz = new(C, ALLOC_LIMIT) PhiNode(alloc_reg, TypeX_X); + PhiNode* alloc_i_o = new(C, ALLOC_LIMIT) PhiNode(alloc_reg, Type::ABIO); + PhiNode* alloc_mem = new(C, ALLOC_LIMIT) PhiNode(alloc_reg, Type::MEMORY, + raw_adr_type); + record_for_igvn(alloc_reg); + + bool card_mark = false; // (see below) + + Node* array_ctl = generate_array_guard(obj_klass, (RegionNode*)NULL); + if (array_ctl != NULL) { + // It's an array. + PreserveJVMState pjvms(this); + set_control(array_ctl); + Node* obj_length = load_array_length(obj); + Node* obj_size = NULL; + _sp += nargs; // set original stack for use by uncommon_trap + Node* alloc_obj = new_array(obj_klass, obj_length, + raw_mem_only, &obj_size); + _sp -= nargs; + assert(obj_size != NULL, ""); + Node* raw_obj = alloc_obj->in(1); + assert(raw_obj->is_Proj() && raw_obj->in(0)->is_Allocate(), ""); + if (ReduceBulkZeroing) { + AllocateNode* alloc = AllocateNode::Ideal_allocation(alloc_obj, &_gvn); + if (alloc != NULL) { + // We will be completely responsible for initializing this object. + alloc->maybe_set_complete(&_gvn); + } + } + + if (!use_ReduceInitialCardMarks()) { + // If it is an oop array, it requires very special treatment, + // because card marking is required on each card of the array. + Node* is_obja = generate_objArray_guard(obj_klass, (RegionNode*)NULL); + if (is_obja != NULL) { + PreserveJVMState pjvms2(this); + set_control(is_obja); + // Generate a direct call to the right arraycopy function(s). + bool disjoint_bases = true; + bool length_never_negative = true; + generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT, + obj, intcon(0), alloc_obj, intcon(0), + obj_length, nargs, + disjoint_bases, length_never_negative); + result_reg->init_req(_objArray_path, control()); + result_val->init_req(_objArray_path, alloc_obj); + result_i_o ->set_req(_objArray_path, i_o()); + result_mem ->set_req(_objArray_path, reset_memory()); + } + } + // We can dispense with card marks if we know the allocation + // comes out of eden (TLAB)... In fact, ReduceInitialCardMarks + // causes the non-eden paths to simulate a fresh allocation, + // insofar that no further card marks are required to initialize + // the object. + + // Otherwise, there are no card marks to worry about. + alloc_val->init_req(_typeArray_alloc, raw_obj); + alloc_siz->init_req(_typeArray_alloc, obj_size); + alloc_reg->init_req(_typeArray_alloc, control()); + alloc_i_o->init_req(_typeArray_alloc, i_o()); + alloc_mem->init_req(_typeArray_alloc, memory(raw_adr_type)); + } + + // We only go to the fast case code if we pass a number of guards. + // The paths which do not pass are accumulated in the slow_region. + RegionNode* slow_region = new (C, 1) RegionNode(1); + record_for_igvn(slow_region); + if (!stopped()) { + // It's an instance. Make the slow-path tests. + // If this is a virtual call, we generate a funny guard. We grab + // the vtable entry corresponding to clone() from the target object. + // If the target method which we are calling happens to be the + // Object clone() method, we pass the guard. We do not need this + // guard for non-virtual calls; the caller is known to be the native + // Object clone(). + if (is_virtual) { + generate_virtual_guard(obj_klass, slow_region); + } + + // The object must be cloneable and must not have a finalizer. + // Both of these conditions may be checked in a single test. + // We could optimize the cloneable test further, but we don't care. + generate_access_flags_guard(obj_klass, + // Test both conditions: + JVM_ACC_IS_CLONEABLE | JVM_ACC_HAS_FINALIZER, + // Must be cloneable but not finalizer: + JVM_ACC_IS_CLONEABLE, + slow_region); + } + + if (!stopped()) { + // It's an instance, and it passed the slow-path tests. + PreserveJVMState pjvms(this); + Node* obj_size = NULL; + Node* alloc_obj = new_instance(obj_klass, NULL, raw_mem_only, &obj_size); + assert(obj_size != NULL, ""); + Node* raw_obj = alloc_obj->in(1); + assert(raw_obj->is_Proj() && raw_obj->in(0)->is_Allocate(), ""); + if (ReduceBulkZeroing) { + AllocateNode* alloc = AllocateNode::Ideal_allocation(alloc_obj, &_gvn); + if (alloc != NULL && !alloc->maybe_set_complete(&_gvn)) + alloc = NULL; + } + if (!use_ReduceInitialCardMarks()) { + // Put in store barrier for any and all oops we are sticking + // into this object. (We could avoid this if we could prove + // that the object type contains no oop fields at all.) + card_mark = true; + } + alloc_val->init_req(_instance_alloc, raw_obj); + alloc_siz->init_req(_instance_alloc, obj_size); + alloc_reg->init_req(_instance_alloc, control()); + alloc_i_o->init_req(_instance_alloc, i_o()); + alloc_mem->init_req(_instance_alloc, memory(raw_adr_type)); + } + + // Generate code for the slow case. We make a call to clone(). + set_control(_gvn.transform(slow_region)); + if (!stopped()) { + PreserveJVMState pjvms(this); + CallJavaNode* slow_call = generate_method_call(vmIntrinsics::_clone, is_virtual); + Node* slow_result = set_results_for_java_call(slow_call); + // this->control() comes from set_results_for_java_call + result_reg->init_req(_slow_path, control()); + result_val->init_req(_slow_path, slow_result); + result_i_o ->set_req(_slow_path, i_o()); + result_mem ->set_req(_slow_path, reset_memory()); + } + + // The object is allocated, as an array and/or an instance. Now copy it. + set_control( _gvn.transform(alloc_reg) ); + set_i_o( _gvn.transform(alloc_i_o) ); + set_memory( _gvn.transform(alloc_mem), raw_adr_type ); + Node* raw_obj = _gvn.transform(alloc_val); + + if (!stopped()) { + // Copy the fastest available way. + // (No need for PreserveJVMState, since we're using it all up now.) + Node* src = obj; + Node* dest = raw_obj; + Node* end = dest; + Node* size = _gvn.transform(alloc_siz); + + // Exclude the header. + int base_off = sizeof(oopDesc); + src = basic_plus_adr(src, base_off); + dest = basic_plus_adr(dest, base_off); + end = basic_plus_adr(end, size); + + // Compute the length also, if needed: + Node* countx = size; + countx = _gvn.transform( new (C, 3) SubXNode(countx, MakeConX(base_off)) ); + countx = _gvn.transform( new (C, 3) URShiftXNode(countx, intcon(LogBytesPerLong) )); + + // Select an appropriate instruction to initialize the range. + // The CopyArray instruction (if supported) can be optimized + // into a discrete set of scalar loads and stores. + bool disjoint_bases = true; + generate_unchecked_arraycopy(raw_adr_type, T_LONG, disjoint_bases, + src, NULL, dest, NULL, countx); + + // Now that the object is properly initialized, type it as an oop. + // Use a secondary InitializeNode memory barrier. + InitializeNode* init = insert_mem_bar_volatile(Op_Initialize, raw_adr_idx, + raw_obj)->as_Initialize(); + init->set_complete(&_gvn); // (there is no corresponding AllocateNode) + Node* new_obj = new(C, 2) CheckCastPPNode(control(), raw_obj, + TypeInstPtr::NOTNULL); + new_obj = _gvn.transform(new_obj); + + // If necessary, emit some card marks afterwards. (Non-arrays only.) + if (card_mark) { + Node* no_particular_value = NULL; + Node* no_particular_field = NULL; + post_barrier(control(), + memory(raw_adr_type), + new_obj, + no_particular_field, + raw_adr_idx, + no_particular_value, + T_OBJECT, + false); + } + // Present the results of the slow call. + result_reg->init_req(_fast_path, control()); + result_val->init_req(_fast_path, new_obj); + result_i_o ->set_req(_fast_path, i_o()); + result_mem ->set_req(_fast_path, reset_memory()); + } + + // Return the combined state. + set_control( _gvn.transform(result_reg) ); + set_i_o( _gvn.transform(result_i_o) ); + set_all_memory( _gvn.transform(result_mem) ); + + // Cast the result to a sharper type, since we know what clone does. + Node* new_obj = _gvn.transform(result_val); + Node* cast = new (C, 2) CheckCastPPNode(control(), new_obj, toop); + push(_gvn.transform(cast)); + + return true; +} + + +// constants for computing the copy function +enum { + COPYFUNC_UNALIGNED = 0, + COPYFUNC_ALIGNED = 1, // src, dest aligned to HeapWordSize + COPYFUNC_CONJOINT = 0, + COPYFUNC_DISJOINT = 2 // src != dest, or transfer can descend +}; + +// Note: The condition "disjoint" applies also for overlapping copies +// where an descending copy is permitted (i.e., dest_offset <= src_offset). +static address +select_arraycopy_function(BasicType t, bool aligned, bool disjoint, const char* &name) { + int selector = + (aligned ? COPYFUNC_ALIGNED : COPYFUNC_UNALIGNED) + + (disjoint ? COPYFUNC_DISJOINT : COPYFUNC_CONJOINT); + +#define RETURN_STUB(xxx_arraycopy) { \ + name = #xxx_arraycopy; \ + return StubRoutines::xxx_arraycopy(); } + + switch (t) { + case T_BYTE: + case T_BOOLEAN: + switch (selector) { + case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jbyte_arraycopy); + case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jbyte_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jbyte_disjoint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jbyte_disjoint_arraycopy); + } + case T_CHAR: + case T_SHORT: + switch (selector) { + case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jshort_arraycopy); + case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jshort_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jshort_disjoint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jshort_disjoint_arraycopy); + } + case T_INT: + case T_FLOAT: + switch (selector) { + case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jint_arraycopy); + case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jint_disjoint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jint_disjoint_arraycopy); + } + case T_DOUBLE: + case T_LONG: + switch (selector) { + case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jlong_arraycopy); + case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jlong_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jlong_disjoint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jlong_disjoint_arraycopy); + } + case T_ARRAY: + case T_OBJECT: + switch (selector) { + case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(oop_arraycopy); + case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_oop_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(oop_disjoint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_oop_disjoint_arraycopy); + } + default: + ShouldNotReachHere(); + return NULL; + } + +#undef RETURN_STUB +} + +//------------------------------basictype2arraycopy---------------------------- +address LibraryCallKit::basictype2arraycopy(BasicType t, + Node* src_offset, + Node* dest_offset, + bool disjoint_bases, + const char* &name) { + const TypeInt* src_offset_inttype = gvn().find_int_type(src_offset);; + const TypeInt* dest_offset_inttype = gvn().find_int_type(dest_offset);; + + bool aligned = false; + bool disjoint = disjoint_bases; + + // if the offsets are the same, we can treat the memory regions as + // disjoint, because either the memory regions are in different arrays, + // or they are identical (which we can treat as disjoint.) We can also + // treat a copy with a destination index less that the source index + // as disjoint since a low->high copy will work correctly in this case. + if (src_offset_inttype != NULL && src_offset_inttype->is_con() && + dest_offset_inttype != NULL && dest_offset_inttype->is_con()) { + // both indices are constants + int s_offs = src_offset_inttype->get_con(); + int d_offs = dest_offset_inttype->get_con(); + int element_size = type2aelembytes[t]; + aligned = ((arrayOopDesc::base_offset_in_bytes(t) + s_offs * element_size) % HeapWordSize == 0) && + ((arrayOopDesc::base_offset_in_bytes(t) + d_offs * element_size) % HeapWordSize == 0); + if (s_offs >= d_offs) disjoint = true; + } else if (src_offset == dest_offset && src_offset != NULL) { + // This can occur if the offsets are identical non-constants. + disjoint = true; + } + + return select_arraycopy_function(t, aligned, disjoint, name); +} + + +//------------------------------inline_arraycopy----------------------- +bool LibraryCallKit::inline_arraycopy() { + // Restore the stack and pop off the arguments. + int nargs = 5; // 2 oops, 3 ints, no size_t or long + assert(callee()->signature()->size() == nargs, "copy has 5 arguments"); + + Node *src = argument(0); + Node *src_offset = argument(1); + Node *dest = argument(2); + Node *dest_offset = argument(3); + Node *length = argument(4); + + // Compile time checks. If any of these checks cannot be verified at compile time, + // we do not make a fast path for this call. Instead, we let the call remain as it + // is. The checks we choose to mandate at compile time are: + // + // (1) src and dest are arrays. + const Type* src_type = src->Value(&_gvn); + const Type* dest_type = dest->Value(&_gvn); + const TypeAryPtr* top_src = src_type->isa_aryptr(); + const TypeAryPtr* top_dest = dest_type->isa_aryptr(); + if (top_src == NULL || top_src->klass() == NULL || + top_dest == NULL || top_dest->klass() == NULL) { + // Conservatively insert a memory barrier on all memory slices. + // Do not let writes into the source float below the arraycopy. + insert_mem_bar(Op_MemBarCPUOrder); + + // Call StubRoutines::generic_arraycopy stub. + generate_arraycopy(TypeRawPtr::BOTTOM, T_CONFLICT, + src, src_offset, dest, dest_offset, length, + nargs); + + // Do not let reads from the destination float above the arraycopy. + // Since we cannot type the arrays, we don't know which slices + // might be affected. We could restrict this barrier only to those + // memory slices which pertain to array elements--but don't bother. + if (!InsertMemBarAfterArraycopy) + // (If InsertMemBarAfterArraycopy, there is already one in place.) + insert_mem_bar(Op_MemBarCPUOrder); + return true; + } + + // (2) src and dest arrays must have elements of the same BasicType + // Figure out the size and type of the elements we will be copying. + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); + BasicType dest_elem = top_dest->klass()->as_array_klass()->element_type()->basic_type(); + if (src_elem == T_ARRAY) src_elem = T_OBJECT; + if (dest_elem == T_ARRAY) dest_elem = T_OBJECT; + + if (src_elem != dest_elem || dest_elem == T_VOID) { + // The component types are not the same or are not recognized. Punt. + // (But, avoid the native method wrapper to JVM_ArrayCopy.) + generate_slow_arraycopy(TypePtr::BOTTOM, + src, src_offset, dest, dest_offset, length, + nargs); + return true; + } + + //--------------------------------------------------------------------------- + // We will make a fast path for this call to arraycopy. + + // We have the following tests left to perform: + // + // (3) src and dest must not be null. + // (4) src_offset must not be negative. + // (5) dest_offset must not be negative. + // (6) length must not be negative. + // (7) src_offset + length must not exceed length of src. + // (8) dest_offset + length must not exceed length of dest. + // (9) each element of an oop array must be assignable + + RegionNode* slow_region = new (C, 1) RegionNode(1); + record_for_igvn(slow_region); + + // (3) operands must not be null + // We currently perform our null checks with the do_null_check routine. + // This means that the null exceptions will be reported in the caller + // rather than (correctly) reported inside of the native arraycopy call. + // This should be corrected, given time. We do our null check with the + // stack pointer restored. + _sp += nargs; + src = do_null_check(src, T_ARRAY); + dest = do_null_check(dest, T_ARRAY); + _sp -= nargs; + + // (4) src_offset must not be negative. + generate_negative_guard(src_offset, slow_region); + + // (5) dest_offset must not be negative. + generate_negative_guard(dest_offset, slow_region); + + // (6) length must not be negative (moved to generate_arraycopy()). + // generate_negative_guard(length, slow_region); + + // (7) src_offset + length must not exceed length of src. + generate_limit_guard(src_offset, length, + load_array_length(src), + slow_region); + + // (8) dest_offset + length must not exceed length of dest. + generate_limit_guard(dest_offset, length, + load_array_length(dest), + slow_region); + + // (9) each element of an oop array must be assignable + // The generate_arraycopy subroutine checks this. + + // This is where the memory effects are placed: + const TypePtr* adr_type = TypeAryPtr::get_array_body_type(dest_elem); + generate_arraycopy(adr_type, dest_elem, + src, src_offset, dest, dest_offset, length, + nargs, false, false, slow_region); + + return true; +} + +//-----------------------------generate_arraycopy---------------------- +// Generate an optimized call to arraycopy. +// Caller must guard against non-arrays. +// Caller must determine a common array basic-type for both arrays. +// Caller must validate offsets against array bounds. +// The slow_region has already collected guard failure paths +// (such as out of bounds length or non-conformable array types). +// The generated code has this shape, in general: +// +// if (length == 0) return // via zero_path +// slowval = -1 +// if (types unknown) { +// slowval = call generic copy loop +// if (slowval == 0) return // via checked_path +// } else if (indexes in bounds) { +// if ((is object array) && !(array type check)) { +// slowval = call checked copy loop +// if (slowval == 0) return // via checked_path +// } else { +// call bulk copy loop +// return // via fast_path +// } +// } +// // adjust params for remaining work: +// if (slowval != -1) { +// n = -1^slowval; src_offset += n; dest_offset += n; length -= n +// } +// slow_region: +// call slow arraycopy(src, src_offset, dest, dest_offset, length) +// return // via slow_call_path +// +// This routine is used from several intrinsics: System.arraycopy, +// Object.clone (the array subcase), and Arrays.copyOf[Range]. +// +void +LibraryCallKit::generate_arraycopy(const TypePtr* adr_type, + BasicType basic_elem_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, + int nargs, + bool disjoint_bases, + bool length_never_negative, + RegionNode* slow_region) { + + if (slow_region == NULL) { + slow_region = new(C,1) RegionNode(1); + record_for_igvn(slow_region); + } + + Node* original_dest = dest; + AllocateArrayNode* alloc = NULL; // used for zeroing, if needed + Node* raw_dest = NULL; // used before zeroing, if needed + bool must_clear_dest = false; + + // See if this is the initialization of a newly-allocated array. + // If so, we will take responsibility here for initializing it to zero. + // (Note: Because tightly_coupled_allocation performs checks on the + // out-edges of the dest, we need to avoid making derived pointers + // from it until we have checked its uses.) + if (ReduceBulkZeroing + && !ZeroTLAB // pointless if already zeroed + && basic_elem_type != T_CONFLICT // avoid corner case + && !_gvn.eqv_uncast(src, dest) + && ((alloc = tightly_coupled_allocation(dest, slow_region)) + != NULL) + && alloc->maybe_set_complete(&_gvn)) { + // "You break it, you buy it." + InitializeNode* init = alloc->initialization(); + assert(init->is_complete(), "we just did this"); + assert(dest->Opcode() == Op_CheckCastPP, "sanity"); + assert(dest->in(0)->in(0) == init, "dest pinned"); + raw_dest = dest->in(1); // grab the raw pointer! + original_dest = dest; + dest = raw_dest; + adr_type = TypeRawPtr::BOTTOM; // all initializations are into raw memory + // Decouple the original InitializeNode, turning it into a simple membar. + // We will build a new one at the end of this routine. + init->set_req(InitializeNode::RawAddress, top()); + // From this point on, every exit path is responsible for + // initializing any non-copied parts of the object to zero. + must_clear_dest = true; + } else { + // No zeroing elimination here. + alloc = NULL; + //original_dest = dest; + //must_clear_dest = false; + } + + // Results are placed here: + enum { fast_path = 1, // normal void-returning assembly stub + checked_path = 2, // special assembly stub with cleanup + slow_call_path = 3, // something went wrong; call the VM + zero_path = 4, // bypass when length of copy is zero + bcopy_path = 5, // copy primitive array by 64-bit blocks + PATH_LIMIT = 6 + }; + RegionNode* result_region = new(C, PATH_LIMIT) RegionNode(PATH_LIMIT); + PhiNode* result_i_o = new(C, PATH_LIMIT) PhiNode(result_region, Type::ABIO); + PhiNode* result_memory = new(C, PATH_LIMIT) PhiNode(result_region, Type::MEMORY, adr_type); + record_for_igvn(result_region); + _gvn.set_type_bottom(result_i_o); + _gvn.set_type_bottom(result_memory); + assert(adr_type != TypePtr::BOTTOM, "must be RawMem or a T[] slice"); + + // The slow_control path: + Node* slow_control; + Node* slow_i_o = i_o(); + Node* slow_mem = memory(adr_type); + debug_only(slow_control = (Node*) badAddress); + + // Checked control path: + Node* checked_control = top(); + Node* checked_mem = NULL; + Node* checked_i_o = NULL; + Node* checked_value = NULL; + + if (basic_elem_type == T_CONFLICT) { + assert(!must_clear_dest, ""); + Node* cv = generate_generic_arraycopy(adr_type, + src, src_offset, dest, dest_offset, + copy_length, nargs); + if (cv == NULL) cv = intcon(-1); // failure (no stub available) + checked_control = control(); + checked_i_o = i_o(); + checked_mem = memory(adr_type); + checked_value = cv; + set_control(top()); // no fast path + } + + Node* not_pos = generate_nonpositive_guard(copy_length, length_never_negative); + if (not_pos != NULL) { + PreserveJVMState pjvms(this); + set_control(not_pos); + + // (6) length must not be negative. + if (!length_never_negative) { + generate_negative_guard(copy_length, slow_region); + } + + if (!stopped() && must_clear_dest) { + Node* dest_length = alloc->in(AllocateNode::ALength); + if (_gvn.eqv_uncast(copy_length, dest_length) + || _gvn.find_int_con(dest_length, 1) <= 0) { + // There is no zeroing to do. + } else { + // Clear the whole thing since there are no source elements to copy. + generate_clear_array(adr_type, dest, basic_elem_type, + intcon(0), NULL, + alloc->in(AllocateNode::AllocSize)); + } + } + + // Present the results of the fast call. + result_region->init_req(zero_path, control()); + result_i_o ->init_req(zero_path, i_o()); + result_memory->init_req(zero_path, memory(adr_type)); + } + + if (!stopped() && must_clear_dest) { + // We have to initialize the *uncopied* part of the array to zero. + // The copy destination is the slice dest[off..off+len]. The other slices + // are dest_head = dest[0..off] and dest_tail = dest[off+len..dest.length]. + Node* dest_size = alloc->in(AllocateNode::AllocSize); + Node* dest_length = alloc->in(AllocateNode::ALength); + Node* dest_tail = _gvn.transform( new(C,3) AddINode(dest_offset, + copy_length) ); + + // If there is a head section that needs zeroing, do it now. + if (find_int_con(dest_offset, -1) != 0) { + generate_clear_array(adr_type, dest, basic_elem_type, + intcon(0), dest_offset, + NULL); + } + + // Next, perform a dynamic check on the tail length. + // It is often zero, and we can win big if we prove this. + // There are two wins: Avoid generating the ClearArray + // with its attendant messy index arithmetic, and upgrade + // the copy to a more hardware-friendly word size of 64 bits. + Node* tail_ctl = NULL; + if (!stopped() && !_gvn.eqv_uncast(dest_tail, dest_length)) { + Node* cmp_lt = _gvn.transform( new(C,3) CmpINode(dest_tail, dest_length) ); + Node* bol_lt = _gvn.transform( new(C,2) BoolNode(cmp_lt, BoolTest::lt) ); + tail_ctl = generate_slow_guard(bol_lt, NULL); + assert(tail_ctl != NULL || !stopped(), "must be an outcome"); + } + + // At this point, let's assume there is no tail. + if (!stopped() && alloc != NULL && basic_elem_type != T_OBJECT) { + // There is no tail. Try an upgrade to a 64-bit copy. + bool didit = false; + { PreserveJVMState pjvms(this); + didit = generate_block_arraycopy(adr_type, basic_elem_type, alloc, + src, src_offset, dest, dest_offset, + dest_size); + if (didit) { + // Present the results of the block-copying fast call. + result_region->init_req(bcopy_path, control()); + result_i_o ->init_req(bcopy_path, i_o()); + result_memory->init_req(bcopy_path, memory(adr_type)); + } + } + if (didit) + set_control(top()); // no regular fast path + } + + // Clear the tail, if any. + if (tail_ctl != NULL) { + Node* notail_ctl = stopped() ? NULL : control(); + set_control(tail_ctl); + if (notail_ctl == NULL) { + generate_clear_array(adr_type, dest, basic_elem_type, + dest_tail, NULL, + dest_size); + } else { + // Make a local merge. + Node* done_ctl = new(C,3) RegionNode(3); + Node* done_mem = new(C,3) PhiNode(done_ctl, Type::MEMORY, adr_type); + done_ctl->init_req(1, notail_ctl); + done_mem->init_req(1, memory(adr_type)); + generate_clear_array(adr_type, dest, basic_elem_type, + dest_tail, NULL, + dest_size); + done_ctl->init_req(2, control()); + done_mem->init_req(2, memory(adr_type)); + set_control( _gvn.transform(done_ctl) ); + set_memory( _gvn.transform(done_mem), adr_type ); + } + } + } + + BasicType copy_type = basic_elem_type; + assert(basic_elem_type != T_ARRAY, "caller must fix this"); + if (!stopped() && copy_type == T_OBJECT) { + // If src and dest have compatible element types, we can copy bits. + // Types S[] and D[] are compatible if D is a supertype of S. + // + // If they are not, we will use checked_oop_disjoint_arraycopy, + // which performs a fast optimistic per-oop check, and backs off + // further to JVM_ArrayCopy on the first per-oop check that fails. + // (Actually, we don't move raw bits only; the GC requires card marks.) + + // Get the klassOop for both src and dest + Node* src_klass = load_object_klass(src); + Node* dest_klass = load_object_klass(dest); + + // Generate the subtype check. + // This might fold up statically, or then again it might not. + // + // Non-static example: Copying List.elements to a new String[]. + // The backing store for a List is always an Object[], + // but its elements are always type String, if the generic types + // are correct at the source level. + // + // Test S[] against D[], not S against D, because (probably) + // the secondary supertype cache is less busy for S[] than S. + // This usually only matters when D is an interface. + Node* not_subtype_ctrl = gen_subtype_check(src_klass, dest_klass); + // Plug failing path into checked_oop_disjoint_arraycopy + if (not_subtype_ctrl != top()) { + PreserveJVMState pjvms(this); + set_control(not_subtype_ctrl); + // (At this point we can assume disjoint_bases, since types differ.) + int ek_offset = objArrayKlass::element_klass_offset_in_bytes() + sizeof(oopDesc); + Node* p1 = basic_plus_adr(dest_klass, ek_offset); + Node* n1 = new (C, 3) LoadKlassNode(0, immutable_memory(), p1, TypeRawPtr::BOTTOM); + Node* dest_elem_klass = _gvn.transform(n1); + Node* cv = generate_checkcast_arraycopy(adr_type, + dest_elem_klass, + src, src_offset, dest, dest_offset, + copy_length, + nargs); + if (cv == NULL) cv = intcon(-1); // failure (no stub available) + checked_control = control(); + checked_i_o = i_o(); + checked_mem = memory(adr_type); + checked_value = cv; + } + // At this point we know we do not need type checks on oop stores. + + // Let's see if we need card marks: + if (alloc != NULL && use_ReduceInitialCardMarks()) { + // If we do not need card marks, copy using the jint or jlong stub. + copy_type = LP64_ONLY(T_LONG) NOT_LP64(T_INT); + assert(type2aelembytes[basic_elem_type] == type2aelembytes[copy_type], + "sizes agree"); + } + } + + if (!stopped()) { + // Generate the fast path, if possible. + PreserveJVMState pjvms(this); + generate_unchecked_arraycopy(adr_type, copy_type, disjoint_bases, + src, src_offset, dest, dest_offset, + ConvI2X(copy_length)); + + // Present the results of the fast call. + result_region->init_req(fast_path, control()); + result_i_o ->init_req(fast_path, i_o()); + result_memory->init_req(fast_path, memory(adr_type)); + } + + // Here are all the slow paths up to this point, in one bundle: + slow_control = top(); + if (slow_region != NULL) + slow_control = _gvn.transform(slow_region); + debug_only(slow_region = (RegionNode*)badAddress); + + set_control(checked_control); + if (!stopped()) { + // Clean up after the checked call. + // The returned value is either 0 or -1^K, + // where K = number of partially transferred array elements. + Node* cmp = _gvn.transform( new(C, 3) CmpINode(checked_value, intcon(0)) ); + Node* bol = _gvn.transform( new(C, 2) BoolNode(cmp, BoolTest::eq) ); + IfNode* iff = create_and_map_if(control(), bol, PROB_MAX, COUNT_UNKNOWN); + + // If it is 0, we are done, so transfer to the end. + Node* checks_done = _gvn.transform( new(C, 1) IfTrueNode(iff) ); + result_region->init_req(checked_path, checks_done); + result_i_o ->init_req(checked_path, checked_i_o); + result_memory->init_req(checked_path, checked_mem); + + // If it is not zero, merge into the slow call. + set_control( _gvn.transform( new(C, 1) IfFalseNode(iff) )); + RegionNode* slow_reg2 = new(C, 3) RegionNode(3); + PhiNode* slow_i_o2 = new(C, 3) PhiNode(slow_reg2, Type::ABIO); + PhiNode* slow_mem2 = new(C, 3) PhiNode(slow_reg2, Type::MEMORY, adr_type); + record_for_igvn(slow_reg2); + slow_reg2 ->init_req(1, slow_control); + slow_i_o2 ->init_req(1, slow_i_o); + slow_mem2 ->init_req(1, slow_mem); + slow_reg2 ->init_req(2, control()); + slow_i_o2 ->init_req(2, i_o()); + slow_mem2 ->init_req(2, memory(adr_type)); + + slow_control = _gvn.transform(slow_reg2); + slow_i_o = _gvn.transform(slow_i_o2); + slow_mem = _gvn.transform(slow_mem2); + + if (alloc != NULL) { + // We'll restart from the very beginning, after zeroing the whole thing. + // This can cause double writes, but that's OK since dest is brand new. + // So we ignore the low 31 bits of the value returned from the stub. + } else { + // We must continue the copy exactly where it failed, or else + // another thread might see the wrong number of writes to dest. + Node* checked_offset = _gvn.transform( new(C, 3) XorINode(checked_value, intcon(-1)) ); + Node* slow_offset = new(C, 3) PhiNode(slow_reg2, TypeInt::INT); + slow_offset->init_req(1, intcon(0)); + slow_offset->init_req(2, checked_offset); + slow_offset = _gvn.transform(slow_offset); + + // Adjust the arguments by the conditionally incoming offset. + Node* src_off_plus = _gvn.transform( new(C, 3) AddINode(src_offset, slow_offset) ); + Node* dest_off_plus = _gvn.transform( new(C, 3) AddINode(dest_offset, slow_offset) ); + Node* length_minus = _gvn.transform( new(C, 3) SubINode(copy_length, slow_offset) ); + + // Tweak the node variables to adjust the code produced below: + src_offset = src_off_plus; + dest_offset = dest_off_plus; + copy_length = length_minus; + } + } + + set_control(slow_control); + if (!stopped()) { + // Generate the slow path, if needed. + PreserveJVMState pjvms(this); // replace_in_map may trash the map + + set_memory(slow_mem, adr_type); + set_i_o(slow_i_o); + + if (must_clear_dest) { + generate_clear_array(adr_type, dest, basic_elem_type, + intcon(0), NULL, + alloc->in(AllocateNode::AllocSize)); + } + + if (dest != original_dest) { + // Promote from rawptr to oop, so it looks right in the call's GC map. + dest = _gvn.transform( new(C,2) CheckCastPPNode(control(), dest, + TypeInstPtr::NOTNULL) ); + + // Edit the call's debug-info to avoid referring to original_dest. + // (The problem with original_dest is that it isn't ready until + // after the InitializeNode completes, but this stuff is before.) + // Substitute in the locally valid dest_oop. + replace_in_map(original_dest, dest); + } + + generate_slow_arraycopy(adr_type, + src, src_offset, dest, dest_offset, + copy_length, nargs); + + result_region->init_req(slow_call_path, control()); + result_i_o ->init_req(slow_call_path, i_o()); + result_memory->init_req(slow_call_path, memory(adr_type)); + } + + // Remove unused edges. + for (uint i = 1; i < result_region->req(); i++) { + if (result_region->in(i) == NULL) + result_region->init_req(i, top()); + } + + // Finished; return the combined state. + set_control( _gvn.transform(result_region) ); + set_i_o( _gvn.transform(result_i_o) ); + set_memory( _gvn.transform(result_memory), adr_type ); + + if (dest != original_dest) { + // Pin the "finished" array node after the arraycopy/zeroing operations. + // Use a secondary InitializeNode memory barrier. + InitializeNode* init = insert_mem_bar_volatile(Op_Initialize, + Compile::AliasIdxRaw, + raw_dest)->as_Initialize(); + init->set_complete(&_gvn); // (there is no corresponding AllocateNode) + _gvn.hash_delete(original_dest); + original_dest->set_req(0, control()); + _gvn.hash_find_insert(original_dest); // put back into GVN table + } + + // The memory edges above are precise in order to model effects around + // array copyies accurately to allow value numbering of field loads around + // arraycopy. Such field loads, both before and after, are common in Java + // collections and similar classes involving header/array data structures. + // + // But with low number of register or when some registers are used or killed + // by arraycopy calls it causes registers spilling on stack. See 6544710. + // The next memory barrier is added to avoid it. If the arraycopy can be + // optimized away (which it can, sometimes) then we can manually remove + // the membar also. + if (InsertMemBarAfterArraycopy) + insert_mem_bar(Op_MemBarCPUOrder); +} + + +// Helper function which determines if an arraycopy immediately follows +// an allocation, with no intervening tests or other escapes for the object. +AllocateArrayNode* +LibraryCallKit::tightly_coupled_allocation(Node* ptr, + RegionNode* slow_region) { + if (stopped()) return NULL; // no fast path + if (C->AliasLevel() == 0) return NULL; // no MergeMems around + + AllocateArrayNode* alloc = AllocateArrayNode::Ideal_array_allocation(ptr, &_gvn); + if (alloc == NULL) return NULL; + + Node* rawmem = memory(Compile::AliasIdxRaw); + // Is the allocation's memory state untouched? + if (!(rawmem->is_Proj() && rawmem->in(0)->is_Initialize())) { + // Bail out if there have been raw-memory effects since the allocation. + // (Example: There might have been a call or safepoint.) + return NULL; + } + rawmem = rawmem->in(0)->as_Initialize()->memory(Compile::AliasIdxRaw); + if (!(rawmem->is_Proj() && rawmem->in(0) == alloc)) { + return NULL; + } + + // There must be no unexpected observers of this allocation. + for (DUIterator_Fast imax, i = ptr->fast_outs(imax); i < imax; i++) { + Node* obs = ptr->fast_out(i); + if (obs != this->map()) { + return NULL; + } + } + + // This arraycopy must unconditionally follow the allocation of the ptr. + Node* alloc_ctl = ptr->in(0); + assert(just_allocated_object(alloc_ctl) == ptr, "most recent allo"); + + Node* ctl = control(); + while (ctl != alloc_ctl) { + // There may be guards which feed into the slow_region. + // Any other control flow means that we might not get a chance + // to finish initializing the allocated object. + if ((ctl->is_IfFalse() || ctl->is_IfTrue()) && ctl->in(0)->is_If()) { + IfNode* iff = ctl->in(0)->as_If(); + Node* not_ctl = iff->proj_out(1 - ctl->as_Proj()->_con); + assert(not_ctl != NULL && not_ctl != ctl, "found alternate"); + if (slow_region != NULL && slow_region->find_edge(not_ctl) >= 1) { + ctl = iff->in(0); // This test feeds the known slow_region. + continue; + } + // One more try: Various low-level checks bottom out in + // uncommon traps. If the debug-info of the trap omits + // any reference to the allocation, as we've already + // observed, then there can be no objection to the trap. + bool found_trap = false; + for (DUIterator_Fast jmax, j = not_ctl->fast_outs(jmax); j < jmax; j++) { + Node* obs = not_ctl->fast_out(j); + if (obs->in(0) == not_ctl && obs->is_Call() && + (obs->as_Call()->entry_point() == + SharedRuntime::uncommon_trap_blob()->instructions_begin())) { + found_trap = true; break; + } + } + if (found_trap) { + ctl = iff->in(0); // This test feeds a harmless uncommon trap. + continue; + } + } + return NULL; + } + + // If we get this far, we have an allocation which immediately + // precedes the arraycopy, and we can take over zeroing the new object. + // The arraycopy will finish the initialization, and provide + // a new control state to which we will anchor the destination pointer. + + return alloc; +} + +// Helper for initialization of arrays, creating a ClearArray. +// It writes zero bits in [start..end), within the body of an array object. +// The memory effects are all chained onto the 'adr_type' alias category. +// +// Since the object is otherwise uninitialized, we are free +// to put a little "slop" around the edges of the cleared area, +// as long as it does not go back into the array's header, +// or beyond the array end within the heap. +// +// The lower edge can be rounded down to the nearest jint and the +// upper edge can be rounded up to the nearest MinObjAlignmentInBytes. +// +// Arguments: +// adr_type memory slice where writes are generated +// dest oop of the destination array +// basic_elem_type element type of the destination +// slice_idx array index of first element to store +// slice_len number of elements to store (or NULL) +// dest_size total size in bytes of the array object +// +// Exactly one of slice_len or dest_size must be non-NULL. +// If dest_size is non-NULL, zeroing extends to the end of the object. +// If slice_len is non-NULL, the slice_idx value must be a constant. +void +LibraryCallKit::generate_clear_array(const TypePtr* adr_type, + Node* dest, + BasicType basic_elem_type, + Node* slice_idx, + Node* slice_len, + Node* dest_size) { + // one or the other but not both of slice_len and dest_size: + assert((slice_len != NULL? 1: 0) + (dest_size != NULL? 1: 0) == 1, ""); + if (slice_len == NULL) slice_len = top(); + if (dest_size == NULL) dest_size = top(); + + // operate on this memory slice: + Node* mem = memory(adr_type); // memory slice to operate on + + // scaling and rounding of indexes: + int scale = exact_log2(type2aelembytes[basic_elem_type]); + int abase = arrayOopDesc::base_offset_in_bytes(basic_elem_type); + int clear_low = (-1 << scale) & (BytesPerInt - 1); + int bump_bit = (-1 << scale) & BytesPerInt; + + // determine constant starts and ends + const intptr_t BIG_NEG = -128; + assert(BIG_NEG + 2*abase < 0, "neg enough"); + intptr_t slice_idx_con = (intptr_t) find_int_con(slice_idx, BIG_NEG); + intptr_t slice_len_con = (intptr_t) find_int_con(slice_len, BIG_NEG); + if (slice_len_con == 0) { + return; // nothing to do here + } + intptr_t start_con = (abase + (slice_idx_con << scale)) & ~clear_low; + intptr_t end_con = find_intptr_t_con(dest_size, -1); + if (slice_idx_con >= 0 && slice_len_con >= 0) { + assert(end_con < 0, "not two cons"); + end_con = round_to(abase + ((slice_idx_con + slice_len_con) << scale), + BytesPerLong); + } + + if (start_con >= 0 && end_con >= 0) { + // Constant start and end. Simple. + mem = ClearArrayNode::clear_memory(control(), mem, dest, + start_con, end_con, &_gvn); + } else if (start_con >= 0 && dest_size != top()) { + // Constant start, pre-rounded end after the tail of the array. + Node* end = dest_size; + mem = ClearArrayNode::clear_memory(control(), mem, dest, + start_con, end, &_gvn); + } else if (start_con >= 0 && slice_len != top()) { + // Constant start, non-constant end. End needs rounding up. + // End offset = round_up(abase + ((slice_idx_con + slice_len) << scale), 8) + intptr_t end_base = abase + (slice_idx_con << scale); + int end_round = (-1 << scale) & (BytesPerLong - 1); + Node* end = ConvI2X(slice_len); + if (scale != 0) + end = _gvn.transform( new(C,3) LShiftXNode(end, intcon(scale) )); + end_base += end_round; + end = _gvn.transform( new(C,3) AddXNode(end, MakeConX(end_base)) ); + end = _gvn.transform( new(C,3) AndXNode(end, MakeConX(~end_round)) ); + mem = ClearArrayNode::clear_memory(control(), mem, dest, + start_con, end, &_gvn); + } else if (start_con < 0 && dest_size != top()) { + // Non-constant start, pre-rounded end after the tail of the array. + // This is almost certainly a "round-to-end" operation. + Node* start = slice_idx; + start = ConvI2X(start); + if (scale != 0) + start = _gvn.transform( new(C,3) LShiftXNode( start, intcon(scale) )); + start = _gvn.transform( new(C,3) AddXNode(start, MakeConX(abase)) ); + if ((bump_bit | clear_low) != 0) { + int to_clear = (bump_bit | clear_low); + // Align up mod 8, then store a jint zero unconditionally + // just before the mod-8 boundary. + // This would only fail if the first array element were immediately + // after the length field, and were also at an even offset mod 8. + assert(((abase + bump_bit) & ~to_clear) - BytesPerInt + >= arrayOopDesc::length_offset_in_bytes() + BytesPerInt, + "store must not trash length field"); + + // Bump 'start' up to (or past) the next jint boundary: + start = _gvn.transform( new(C,3) AddXNode(start, MakeConX(bump_bit)) ); + // Round bumped 'start' down to jlong boundary in body of array. + start = _gvn.transform( new(C,3) AndXNode(start, MakeConX(~to_clear)) ); + // Store a zero to the immediately preceding jint: + Node* x1 = _gvn.transform( new(C,3) AddXNode(start, MakeConX(-BytesPerInt)) ); + Node* p1 = basic_plus_adr(dest, x1); + mem = StoreNode::make(C, control(), mem, p1, adr_type, intcon(0), T_INT); + mem = _gvn.transform(mem); + } + + Node* end = dest_size; // pre-rounded + mem = ClearArrayNode::clear_memory(control(), mem, dest, + start, end, &_gvn); + } else { + // Non-constant start, unrounded non-constant end. + // (Nobody zeroes a random midsection of an array using this routine.) + ShouldNotReachHere(); // fix caller + } + + // Done. + set_memory(mem, adr_type); +} + + +bool +LibraryCallKit::generate_block_arraycopy(const TypePtr* adr_type, + BasicType basic_elem_type, + AllocateNode* alloc, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* dest_size) { + // See if there is an advantage from block transfer. + int scale = exact_log2(type2aelembytes[basic_elem_type]); + if (scale >= LogBytesPerLong) + return false; // it is already a block transfer + + // Look at the alignment of the starting offsets. + int abase = arrayOopDesc::base_offset_in_bytes(basic_elem_type); + const intptr_t BIG_NEG = -128; + assert(BIG_NEG + 2*abase < 0, "neg enough"); + + intptr_t src_off = abase + ((intptr_t) find_int_con(src_offset, -1) << scale); + intptr_t dest_off = abase + ((intptr_t) find_int_con(dest_offset, -1) << scale); + if (src_off < 0 || dest_off < 0) + // At present, we can only understand constants. + return false; + + if (((src_off | dest_off) & (BytesPerLong-1)) != 0) { + // Non-aligned; too bad. + // One more chance: Pick off an initial 32-bit word. + // This is a common case, since abase can be odd mod 8. + if (((src_off | dest_off) & (BytesPerLong-1)) == BytesPerInt && + ((src_off ^ dest_off) & (BytesPerLong-1)) == 0) { + Node* sptr = basic_plus_adr(src, src_off); + Node* dptr = basic_plus_adr(dest, dest_off); + Node* sval = make_load(control(), sptr, TypeInt::INT, T_INT, adr_type); + store_to_memory(control(), dptr, sval, T_INT, adr_type); + src_off += BytesPerInt; + dest_off += BytesPerInt; + } else { + return false; + } + } + assert(src_off % BytesPerLong == 0, ""); + assert(dest_off % BytesPerLong == 0, ""); + + // Do this copy by giant steps. + Node* sptr = basic_plus_adr(src, src_off); + Node* dptr = basic_plus_adr(dest, dest_off); + Node* countx = dest_size; + countx = _gvn.transform( new (C, 3) SubXNode(countx, MakeConX(dest_off)) ); + countx = _gvn.transform( new (C, 3) URShiftXNode(countx, intcon(LogBytesPerLong)) ); + + bool disjoint_bases = true; // since alloc != NULL + generate_unchecked_arraycopy(adr_type, T_LONG, disjoint_bases, + sptr, NULL, dptr, NULL, countx); + + return true; +} + + +// Helper function; generates code for the slow case. +// We make a call to a runtime method which emulates the native method, +// but without the native wrapper overhead. +void +LibraryCallKit::generate_slow_arraycopy(const TypePtr* adr_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, + int nargs) { + _sp += nargs; // any deopt will start just before call to enclosing method + Node* call = make_runtime_call(RC_NO_LEAF | RC_UNCOMMON, + OptoRuntime::slow_arraycopy_Type(), + OptoRuntime::slow_arraycopy_Java(), + "slow_arraycopy", adr_type, + src, src_offset, dest, dest_offset, + copy_length); + _sp -= nargs; + + // Handle exceptions thrown by this fellow: + make_slow_call_ex(call, env()->Throwable_klass(), false); +} + +// Helper function; generates code for cases requiring runtime checks. +Node* +LibraryCallKit::generate_checkcast_arraycopy(const TypePtr* adr_type, + Node* dest_elem_klass, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, + int nargs) { + if (stopped()) return NULL; + + address copyfunc_addr = StubRoutines::checkcast_arraycopy(); + if (copyfunc_addr == NULL) { // Stub was not generated, go slow path. + return NULL; + } + + // Pick out the parameters required to perform a store-check + // for the target array. This is an optimistic check. It will + // look in each non-null element's class, at the desired klass's + // super_check_offset, for the desired klass. + int sco_offset = Klass::super_check_offset_offset_in_bytes() + sizeof(oopDesc); + Node* p3 = basic_plus_adr(dest_elem_klass, sco_offset); + Node* n3 = new(C, 3) LoadINode(NULL, immutable_memory(), p3, TypeRawPtr::BOTTOM); + Node* check_offset = _gvn.transform(n3); + Node* check_value = dest_elem_klass; + + Node* src_start = array_element_address(src, src_offset, T_OBJECT); + Node* dest_start = array_element_address(dest, dest_offset, T_OBJECT); + + // (We know the arrays are never conjoint, because their types differ.) + Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::checkcast_arraycopy_Type(), + copyfunc_addr, "checkcast_arraycopy", adr_type, + // five arguments, of which two are + // intptr_t (jlong in LP64) + src_start, dest_start, + copy_length XTOP, + check_offset XTOP, + check_value); + + return _gvn.transform(new (C, 1) ProjNode(call, TypeFunc::Parms)); +} + + +// Helper function; generates code for cases requiring runtime checks. +Node* +LibraryCallKit::generate_generic_arraycopy(const TypePtr* adr_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, + int nargs) { + if (stopped()) return NULL; + + address copyfunc_addr = StubRoutines::generic_arraycopy(); + if (copyfunc_addr == NULL) { // Stub was not generated, go slow path. + return NULL; + } + + Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::generic_arraycopy_Type(), + copyfunc_addr, "generic_arraycopy", adr_type, + src, src_offset, dest, dest_offset, copy_length); + + return _gvn.transform(new (C, 1) ProjNode(call, TypeFunc::Parms)); +} + +// Helper function; generates the fast out-of-line call to an arraycopy stub. +void +LibraryCallKit::generate_unchecked_arraycopy(const TypePtr* adr_type, + BasicType basic_elem_type, + bool disjoint_bases, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length) { + if (stopped()) return; // nothing to do + + Node* src_start = src; + Node* dest_start = dest; + if (src_offset != NULL || dest_offset != NULL) { + assert(src_offset != NULL && dest_offset != NULL, ""); + src_start = array_element_address(src, src_offset, basic_elem_type); + dest_start = array_element_address(dest, dest_offset, basic_elem_type); + } + + // Figure out which arraycopy runtime method to call. + const char* copyfunc_name = "arraycopy"; + address copyfunc_addr = + basictype2arraycopy(basic_elem_type, src_offset, dest_offset, + disjoint_bases, copyfunc_name); + + // Call it. Note that the count_ix value is not scaled to a byte-size. + make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::fast_arraycopy_Type(), + copyfunc_addr, copyfunc_name, adr_type, + src_start, dest_start, copy_length XTOP); +} diff --git a/hotspot/src/share/vm/opto/live.cpp b/hotspot/src/share/vm/opto/live.cpp new file mode 100644 index 00000000000..4127f67e1dd --- /dev/null +++ b/hotspot/src/share/vm/opto/live.cpp @@ -0,0 +1,314 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_live.cpp.incl" + + + +//============================================================================= +//------------------------------PhaseLive-------------------------------------- +// Compute live-in/live-out. We use a totally incremental algorithm. The LIVE +// problem is monotonic. The steady-state solution looks like this: pull a +// block from the worklist. It has a set of delta's - values which are newly +// live-in from the block. Push these to the live-out sets of all predecessor +// blocks. At each predecessor, the new live-out values are ANDed with what is +// already live-out (extra stuff is added to the live-out sets). Then the +// remaining new live-out values are ANDed with what is locally defined. +// Leftover bits become the new live-in for the predecessor block, and the pred +// block is put on the worklist. +// The locally live-in stuff is computed once and added to predecessor +// live-out sets. This seperate compilation is done in the outer loop below. +PhaseLive::PhaseLive( const PhaseCFG &cfg, LRG_List &names, Arena *arena ) : Phase(LIVE), _cfg(cfg), _names(names), _arena(arena), _live(0) { +} + +void PhaseLive::compute(uint maxlrg) { + _maxlrg = maxlrg; + _worklist = new (_arena) Block_List(); + + // Init the sparse live arrays. This data is live on exit from here! + // The _live info is the live-out info. + _live = (IndexSet*)_arena->Amalloc(sizeof(IndexSet)*_cfg._num_blocks); + uint i; + for( i=0; i<_cfg._num_blocks; i++ ) { + _live[i].initialize(_maxlrg); + } + + // Init the sparse arrays for delta-sets. + ResourceMark rm; // Nuke temp storage on exit + + // Does the memory used by _defs and _deltas get reclaimed? Does it matter? TT + + // Array of values defined locally in blocks + _defs = NEW_RESOURCE_ARRAY(IndexSet,_cfg._num_blocks); + for( i=0; i<_cfg._num_blocks; i++ ) { + _defs[i].initialize(_maxlrg); + } + + // Array of delta-set pointers, indexed by block pre_order-1. + _deltas = NEW_RESOURCE_ARRAY(IndexSet*,_cfg._num_blocks); + memset( _deltas, 0, sizeof(IndexSet*)* _cfg._num_blocks); + + _free_IndexSet = NULL; + + // Blocks having done pass-1 + VectorSet first_pass(Thread::current()->resource_area()); + + // Outer loop: must compute local live-in sets and push into predecessors. + uint iters = _cfg._num_blocks; // stat counters + for( uint j=_cfg._num_blocks; j>0; j-- ) { + Block *b = _cfg._blocks[j-1]; + + // Compute the local live-in set. Start with any new live-out bits. + IndexSet *use = getset( b ); + IndexSet *def = &_defs[b->_pre_order-1]; + DEBUG_ONLY(IndexSet *def_outside = getfreeset();) + uint i; + for( i=b->_nodes.size(); i>1; i-- ) { + Node *n = b->_nodes[i-1]; + if( n->is_Phi() ) break; + + uint r = _names[n->_idx]; + assert(!def_outside->member(r), "Use of external LRG overlaps the same LRG defined in this block"); + def->insert( r ); + use->remove( r ); + uint cnt = n->req(); + for( uint k=1; kin(k); + uint nkidx = nk->_idx; + if( _cfg._bbs[nkidx] != b ) { + uint u = _names[nkidx]; + use->insert( u ); + DEBUG_ONLY(def_outside->insert( u );) + } + } + } +#ifdef ASSERT + def_outside->set_next(_free_IndexSet); + _free_IndexSet = def_outside; // Drop onto free list +#endif + // Remove anything defined by Phis and the block start instruction + for( uint k=i; k>0; k-- ) { + uint r = _names[b->_nodes[k-1]->_idx]; + def->insert( r ); + use->remove( r ); + } + + // Push these live-in things to predecessors + for( uint l=1; lnum_preds(); l++ ) { + Block *p = _cfg._bbs[b->pred(l)->_idx]; + add_liveout( p, use, first_pass ); + + // PhiNode uses go in the live-out set of prior blocks. + for( uint k=i; k>0; k-- ) + add_liveout( p, _names[b->_nodes[k-1]->in(l)->_idx], first_pass ); + } + freeset( b ); + first_pass.set(b->_pre_order); + + // Inner loop: blocks that picked up new live-out values to be propagated + while( _worklist->size() ) { + // !!!!! +// #ifdef ASSERT + iters++; +// #endif + Block *b = _worklist->pop(); + IndexSet *delta = getset(b); + assert( delta->count(), "missing delta set" ); + + // Add new-live-in to predecessors live-out sets + for( uint l=1; lnum_preds(); l++ ) + add_liveout( _cfg._bbs[b->pred(l)->_idx], delta, first_pass ); + + freeset(b); + } // End of while-worklist-not-empty + + } // End of for-all-blocks-outer-loop + + // We explicitly clear all of the IndexSets which we are about to release. + // This allows us to recycle their internal memory into IndexSet's free list. + + for( i=0; i<_cfg._num_blocks; i++ ) { + _defs[i].clear(); + if (_deltas[i]) { + // Is this always true? + _deltas[i]->clear(); + } + } + IndexSet *free = _free_IndexSet; + while (free != NULL) { + IndexSet *temp = free; + free = free->next(); + temp->clear(); + } + +} + +//------------------------------stats------------------------------------------ +#ifndef PRODUCT +void PhaseLive::stats(uint iters) const { +} +#endif + +//------------------------------getset----------------------------------------- +// Get an IndexSet for a block. Return existing one, if any. Make a new +// empty one if a prior one does not exist. +IndexSet *PhaseLive::getset( Block *p ) { + IndexSet *delta = _deltas[p->_pre_order-1]; + if( !delta ) // Not on worklist? + // Get a free set; flag as being on worklist + delta = _deltas[p->_pre_order-1] = getfreeset(); + return delta; // Return set of new live-out items +} + +//------------------------------getfreeset------------------------------------- +// Pull from free list, or allocate. Internal allocation on the returned set +// is always from thread local storage. +IndexSet *PhaseLive::getfreeset( ) { + IndexSet *f = _free_IndexSet; + if( !f ) { + f = new IndexSet; +// f->set_arena(Thread::current()->resource_area()); + f->initialize(_maxlrg, Thread::current()->resource_area()); + } else { + // Pull from free list + _free_IndexSet = f->next(); + //f->_cnt = 0; // Reset to empty +// f->set_arena(Thread::current()->resource_area()); + f->initialize(_maxlrg, Thread::current()->resource_area()); + } + return f; +} + +//------------------------------freeset---------------------------------------- +// Free an IndexSet from a block. +void PhaseLive::freeset( const Block *p ) { + IndexSet *f = _deltas[p->_pre_order-1]; + f->set_next(_free_IndexSet); + _free_IndexSet = f; // Drop onto free list + _deltas[p->_pre_order-1] = NULL; +} + +//------------------------------add_liveout------------------------------------ +// Add a live-out value to a given blocks live-out set. If it is new, then +// also add it to the delta set and stick the block on the worklist. +void PhaseLive::add_liveout( Block *p, uint r, VectorSet &first_pass ) { + IndexSet *live = &_live[p->_pre_order-1]; + if( live->insert(r) ) { // If actually inserted... + // We extended the live-out set. See if the value is generated locally. + // If it is not, then we must extend the live-in set. + if( !_defs[p->_pre_order-1].member( r ) ) { + if( !_deltas[p->_pre_order-1] && // Not on worklist? + first_pass.test(p->_pre_order) ) + _worklist->push(p); // Actually go on worklist if already 1st pass + getset(p)->insert(r); + } + } +} + + +//------------------------------add_liveout------------------------------------ +// Add a vector of live-out values to a given blocks live-out set. +void PhaseLive::add_liveout( Block *p, IndexSet *lo, VectorSet &first_pass ) { + IndexSet *live = &_live[p->_pre_order-1]; + IndexSet *defs = &_defs[p->_pre_order-1]; + IndexSet *on_worklist = _deltas[p->_pre_order-1]; + IndexSet *delta = on_worklist ? on_worklist : getfreeset(); + + IndexSetIterator elements(lo); + uint r; + while ((r = elements.next()) != 0) { + if( live->insert(r) && // If actually inserted... + !defs->member( r ) ) // and not defined locally + delta->insert(r); // Then add to live-in set + } + + if( delta->count() ) { // If actually added things + _deltas[p->_pre_order-1] = delta; // Flag as on worklist now + if( !on_worklist && // Not on worklist? + first_pass.test(p->_pre_order) ) + _worklist->push(p); // Actually go on worklist if already 1st pass + } else { // Nothing there; just free it + delta->set_next(_free_IndexSet); + _free_IndexSet = delta; // Drop onto free list + } +} + +#ifndef PRODUCT +//------------------------------dump------------------------------------------- +// Dump the live-out set for a block +void PhaseLive::dump( const Block *b ) const { + tty->print("Block %d: ",b->_pre_order); + tty->print("LiveOut: "); _live[b->_pre_order-1].dump(); + uint cnt = b->_nodes.size(); + for( uint i=0; iprint("L%d/", _names[b->_nodes[i]->_idx] ); + b->_nodes[i]->dump(); + } + tty->print("\n"); +} + +//------------------------------verify_base_ptrs------------------------------- +// Verify that base pointers and derived pointers are still sane. +// Basically, if a derived pointer is live at a safepoint, then its +// base pointer must be live also. +void PhaseChaitin::verify_base_ptrs( ResourceArea *a ) const { + for( uint i = 0; i < _cfg._num_blocks; i++ ) { + Block *b = _cfg._blocks[i]; + for( uint j = b->end_idx() + 1; j > 1; j-- ) { + Node *n = b->_nodes[j-1]; + if( n->is_Phi() ) break; + // Found a safepoint? + if( n->is_MachSafePoint() ) { + MachSafePointNode *sfpt = n->as_MachSafePoint(); + JVMState* jvms = sfpt->jvms(); + if (jvms != NULL) { + // Now scan for a live derived pointer + if (jvms->oopoff() < sfpt->req()) { + // Check each derived/base pair + for (uint idx = jvms->oopoff(); idx < sfpt->req(); idx += 2) { + Node *check = sfpt->in(idx); + uint j = 0; + // search upwards through spills and spill phis for AddP + while(true) { + if( !check ) break; + int idx = check->is_Copy(); + if( idx ) { + check = check->in(idx); + } else if( check->is_Phi() && check->_idx >= _oldphi ) { + check = check->in(1); + } else + break; + j++; + assert(j < 100000,"Derived pointer checking in infinite loop"); + } // End while + assert(check->is_Mach() && check->as_Mach()->ideal_Opcode() == Op_AddP,"Bad derived pointer") + } + } // End of check for derived pointers + } // End of Kcheck for debug info + } // End of if found a safepoint + } // End of forall instructions in block + } // End of forall blocks +} +#endif diff --git a/hotspot/src/share/vm/opto/live.hpp b/hotspot/src/share/vm/opto/live.hpp new file mode 100644 index 00000000000..886f28f571f --- /dev/null +++ b/hotspot/src/share/vm/opto/live.hpp @@ -0,0 +1,74 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Block; +class LRG_List; +class PhaseCFG; +class VectorSet; +class IndexSet; + +//------------------------------PhaseLive-------------------------------------- +// Compute live-in/live-out +class PhaseLive : public Phase { + // Array of Sets of values live at the start of a block. + // Indexed by block pre-order number. + IndexSet *_live; + + // Array of Sets of values defined locally in the block + // Indexed by block pre-order number. + IndexSet *_defs; + + // Array of delta-set pointers, indexed by block pre-order number + IndexSet **_deltas; + IndexSet *_free_IndexSet; // Free list of same + + Block_List *_worklist; // Worklist for iterative solution + + const PhaseCFG &_cfg; // Basic blocks + LRG_List &_names; // Mapping from Nodes to live ranges + uint _maxlrg; // Largest live-range number + Arena *_arena; + + IndexSet *getset( Block *p ); + IndexSet *getfreeset( ); + void freeset( const Block *p ); + void add_liveout( Block *p, uint r, VectorSet &first_pass ); + void add_liveout( Block *p, IndexSet *lo, VectorSet &first_pass ); + +public: + PhaseLive( const PhaseCFG &cfg, LRG_List &names, Arena *arena ); + ~PhaseLive() {} + // Compute liveness info + void compute(uint maxlrg); + // Reset arena storage + void reset() { _live = NULL; } + + // Return the live-out set for this block + IndexSet *live( const Block * b ) { return &_live[b->_pre_order-1]; } + +#ifndef PRODUCT + void dump( const Block *b ) const; + void stats(uint iters) const; +#endif +}; diff --git a/hotspot/src/share/vm/opto/locknode.cpp b/hotspot/src/share/vm/opto/locknode.cpp new file mode 100644 index 00000000000..90da8efaa36 --- /dev/null +++ b/hotspot/src/share/vm/opto/locknode.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_locknode.cpp.incl" + +//============================================================================= +const RegMask &BoxLockNode::in_RegMask(uint i) const { + return _inmask; +} + +const RegMask &BoxLockNode::out_RegMask() const { + return *Matcher::idealreg2regmask[Op_RegP]; +} + +uint BoxLockNode::size_of() const { return sizeof(*this); } + +BoxLockNode::BoxLockNode( int slot ) : Node( Compile::current()->root() ), _slot(slot) { + init_class_id(Class_BoxLock); + init_flags(Flag_rematerialize); + OptoReg::Name reg = OptoReg::stack2reg(_slot); + _inmask.Insert(reg); +} + +//------------------------------cmp-------------------------------------------- +uint BoxLockNode::cmp( const Node &n ) const { + const BoxLockNode &bn = (const BoxLockNode &)n; + return bn._slot == _slot; +} + +OptoReg::Name BoxLockNode::stack_slot(Node* box_node) { + // Chase down the BoxNode + while (!box_node->is_BoxLock()) { + // if (box_node->is_SpillCopy()) { + // Node *m = box_node->in(1); + // if (m->is_Mach() && m->as_Mach()->ideal_Opcode() == Op_StoreP) { + // box_node = m->in(m->as_Mach()->operand_index(2)); + // continue; + // } + // } + assert(box_node->is_SpillCopy() || box_node->is_Phi(), "Bad spill of Lock."); + box_node = box_node->in(1); + } + return box_node->in_RegMask(0).find_first_elem(); +} + +//============================================================================= +//-----------------------------hash-------------------------------------------- +uint FastLockNode::hash() const { return NO_HASH; } + +//------------------------------cmp-------------------------------------------- +uint FastLockNode::cmp( const Node &n ) const { + return (&n == this); // Always fail except on self +} + +//============================================================================= +//-----------------------------hash-------------------------------------------- +uint FastUnlockNode::hash() const { return NO_HASH; } + +//------------------------------cmp-------------------------------------------- +uint FastUnlockNode::cmp( const Node &n ) const { + return (&n == this); // Always fail except on self +} + +// +// Create a counter which counts the number of times this lock is acquired +// +void FastLockNode::create_lock_counter(JVMState* state) { + BiasedLockingNamedCounter* blnc = (BiasedLockingNamedCounter*) + OptoRuntime::new_named_counter(state, NamedCounter::BiasedLockingCounter); + _counters = blnc->counters(); +} + +//============================================================================= +//------------------------------do_monitor_enter------------------------------- +void Parse::do_monitor_enter() { + kill_dead_locals(); + + // Null check; get casted pointer. + Node *obj = do_null_check(peek(), T_OBJECT); + // Check for locking null object + if (stopped()) return; + + // the monitor object is not part of debug info expression stack + pop(); + + // Insert a FastLockNode which takes as arguments the current thread pointer, + // the obj pointer & the address of the stack slot pair used for the lock. + shared_lock(obj); +} + +//------------------------------do_monitor_exit-------------------------------- +void Parse::do_monitor_exit() { + kill_dead_locals(); + + pop(); // Pop oop to unlock + // Because monitors are guarenteed paired (else we bail out), we know + // the matching Lock for this Unlock. Hence we know there is no need + // for a null check on Unlock. + shared_unlock(map()->peek_monitor_box(), map()->peek_monitor_obj()); +} diff --git a/hotspot/src/share/vm/opto/locknode.hpp b/hotspot/src/share/vm/opto/locknode.hpp new file mode 100644 index 00000000000..6b1a8883ca4 --- /dev/null +++ b/hotspot/src/share/vm/opto/locknode.hpp @@ -0,0 +1,97 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//------------------------------BoxLockNode------------------------------------ +class BoxLockNode : public Node { +public: + const int _slot; + RegMask _inmask; + + BoxLockNode( int lock ); + virtual int Opcode() const; + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + virtual uint size(PhaseRegAlloc *ra_) const; + virtual const RegMask &in_RegMask(uint) const; + virtual const RegMask &out_RegMask() const; + virtual uint size_of() const; + virtual uint hash() const { return Node::hash() + _slot; } + virtual uint cmp( const Node &n ) const; + virtual const class Type *bottom_type() const { return TypeRawPtr::BOTTOM; } + virtual uint ideal_reg() const { return Op_RegP; } + + static OptoReg::Name stack_slot(Node* box_node); + +#ifndef PRODUCT + virtual void format( PhaseRegAlloc *, outputStream *st ) const; + virtual void dump_spec(outputStream *st) const { st->print(" Lock %d",_slot); } +#endif +}; + +//------------------------------FastLockNode----------------------------------- +class FastLockNode: public CmpNode { +private: + BiasedLockingCounters* _counters; + +public: + FastLockNode(Node *ctrl, Node *oop, Node *box) : CmpNode(oop,box) { + init_req(0,ctrl); + init_class_id(Class_FastLock); + _counters = NULL; + } + Node* obj_node() const { return in(1); } + Node* box_node() const { return in(2); } + + // FastLock and FastUnlockNode do not hash, we need one for each correspoding + // LockNode/UnLockNode to avoid creating Phi's. + virtual uint hash() const ; // { return NO_HASH; } + virtual uint cmp( const Node &n ) const ; // Always fail, except on self + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const { return TypeInt::CC; } + const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;} + + void create_lock_counter(JVMState* s); + BiasedLockingCounters* counters() const { return _counters; } +}; + + +//------------------------------FastUnlockNode--------------------------------- +class FastUnlockNode: public CmpNode { +public: + FastUnlockNode(Node *ctrl, Node *oop, Node *box) : CmpNode(oop,box) { + init_req(0,ctrl); + init_class_id(Class_FastUnlock); + } + Node* obj_node() const { return in(1); } + Node* box_node() const { return in(2); } + + + // FastLock and FastUnlockNode do not hash, we need one for each correspoding + // LockNode/UnLockNode to avoid creating Phi's. + virtual uint hash() const ; // { return NO_HASH; } + virtual uint cmp( const Node &n ) const ; // Always fail, except on self + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const { return TypeInt::CC; } + const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;} + +}; diff --git a/hotspot/src/share/vm/opto/loopTransform.cpp b/hotspot/src/share/vm/opto/loopTransform.cpp new file mode 100644 index 00000000000..3de4e0cd752 --- /dev/null +++ b/hotspot/src/share/vm/opto/loopTransform.cpp @@ -0,0 +1,1729 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_loopTransform.cpp.incl" + +//------------------------------is_loop_exit----------------------------------- +// Given an IfNode, return the loop-exiting projection or NULL if both +// arms remain in the loop. +Node *IdealLoopTree::is_loop_exit(Node *iff) const { + if( iff->outcnt() != 2 ) return NULL; // Ignore partially dead tests + PhaseIdealLoop *phase = _phase; + // Test is an IfNode, has 2 projections. If BOTH are in the loop + // we need loop unswitching instead of peeling. + if( !is_member(phase->get_loop( iff->raw_out(0) )) ) + return iff->raw_out(0); + if( !is_member(phase->get_loop( iff->raw_out(1) )) ) + return iff->raw_out(1); + return NULL; +} + + +//============================================================================= + + +//------------------------------record_for_igvn---------------------------- +// Put loop body on igvn work list +void IdealLoopTree::record_for_igvn() { + for( uint i = 0; i < _body.size(); i++ ) { + Node *n = _body.at(i); + _phase->_igvn._worklist.push(n); + } +} + +//------------------------------compute_profile_trip_cnt---------------------------- +// Compute loop trip count from profile data as +// (backedge_count + loop_exit_count) / loop_exit_count +void IdealLoopTree::compute_profile_trip_cnt( PhaseIdealLoop *phase ) { + if (!_head->is_CountedLoop()) { + return; + } + CountedLoopNode* head = _head->as_CountedLoop(); + if (head->profile_trip_cnt() != COUNT_UNKNOWN) { + return; // Already computed + } + float trip_cnt = (float)max_jint; // default is big + + Node* back = head->in(LoopNode::LoopBackControl); + while (back != head) { + if ((back->Opcode() == Op_IfTrue || back->Opcode() == Op_IfFalse) && + back->in(0) && + back->in(0)->is_If() && + back->in(0)->as_If()->_fcnt != COUNT_UNKNOWN && + back->in(0)->as_If()->_prob != PROB_UNKNOWN) { + break; + } + back = phase->idom(back); + } + if (back != head) { + assert((back->Opcode() == Op_IfTrue || back->Opcode() == Op_IfFalse) && + back->in(0), "if-projection exists"); + IfNode* back_if = back->in(0)->as_If(); + float loop_back_cnt = back_if->_fcnt * back_if->_prob; + + // Now compute a loop exit count + float loop_exit_cnt = 0.0f; + for( uint i = 0; i < _body.size(); i++ ) { + Node *n = _body[i]; + if( n->is_If() ) { + IfNode *iff = n->as_If(); + if( iff->_fcnt != COUNT_UNKNOWN && iff->_prob != PROB_UNKNOWN ) { + Node *exit = is_loop_exit(iff); + if( exit ) { + float exit_prob = iff->_prob; + if (exit->Opcode() == Op_IfFalse) exit_prob = 1.0 - exit_prob; + if (exit_prob > PROB_MIN) { + float exit_cnt = iff->_fcnt * exit_prob; + loop_exit_cnt += exit_cnt; + } + } + } + } + } + if (loop_exit_cnt > 0.0f) { + trip_cnt = (loop_back_cnt + loop_exit_cnt) / loop_exit_cnt; + } else { + // No exit count so use + trip_cnt = loop_back_cnt; + } + } +#ifndef PRODUCT + if (TraceProfileTripCount) { + tty->print_cr("compute_profile_trip_cnt lp: %d cnt: %f\n", head->_idx, trip_cnt); + } +#endif + head->set_profile_trip_cnt(trip_cnt); +} + +//---------------------is_invariant_addition----------------------------- +// Return nonzero index of invariant operand for an Add or Sub +// of (nonconstant) invariant and variant values. Helper for reassoicate_invariants. +int IdealLoopTree::is_invariant_addition(Node* n, PhaseIdealLoop *phase) { + int op = n->Opcode(); + if (op == Op_AddI || op == Op_SubI) { + bool in1_invar = this->is_invariant(n->in(1)); + bool in2_invar = this->is_invariant(n->in(2)); + if (in1_invar && !in2_invar) return 1; + if (!in1_invar && in2_invar) return 2; + } + return 0; +} + +//---------------------reassociate_add_sub----------------------------- +// Reassociate invariant add and subtract expressions: +// +// inv1 + (x + inv2) => ( inv1 + inv2) + x +// (x + inv2) + inv1 => ( inv1 + inv2) + x +// inv1 + (x - inv2) => ( inv1 - inv2) + x +// inv1 - (inv2 - x) => ( inv1 - inv2) + x +// (x + inv2) - inv1 => (-inv1 + inv2) + x +// (x - inv2) + inv1 => ( inv1 - inv2) + x +// (x - inv2) - inv1 => (-inv1 - inv2) + x +// inv1 + (inv2 - x) => ( inv1 + inv2) - x +// inv1 - (x - inv2) => ( inv1 + inv2) - x +// (inv2 - x) + inv1 => ( inv1 + inv2) - x +// (inv2 - x) - inv1 => (-inv1 + inv2) - x +// inv1 - (x + inv2) => ( inv1 - inv2) - x +// +Node* IdealLoopTree::reassociate_add_sub(Node* n1, PhaseIdealLoop *phase) { + if (!n1->is_Add() && !n1->is_Sub() || n1->outcnt() == 0) return NULL; + if (is_invariant(n1)) return NULL; + int inv1_idx = is_invariant_addition(n1, phase); + if (!inv1_idx) return NULL; + // Don't mess with add of constant (igvn moves them to expression tree root.) + if (n1->is_Add() && n1->in(2)->is_Con()) return NULL; + Node* inv1 = n1->in(inv1_idx); + Node* n2 = n1->in(3 - inv1_idx); + int inv2_idx = is_invariant_addition(n2, phase); + if (!inv2_idx) return NULL; + Node* x = n2->in(3 - inv2_idx); + Node* inv2 = n2->in(inv2_idx); + + bool neg_x = n2->is_Sub() && inv2_idx == 1; + bool neg_inv2 = n2->is_Sub() && inv2_idx == 2; + bool neg_inv1 = n1->is_Sub() && inv1_idx == 2; + if (n1->is_Sub() && inv1_idx == 1) { + neg_x = !neg_x; + neg_inv2 = !neg_inv2; + } + Node* inv1_c = phase->get_ctrl(inv1); + Node* inv2_c = phase->get_ctrl(inv2); + Node* n_inv1; + if (neg_inv1) { + Node *zero = phase->_igvn.intcon(0); + phase->set_ctrl(zero, phase->C->root()); + n_inv1 = new (phase->C, 3) SubINode(zero, inv1); + phase->register_new_node(n_inv1, inv1_c); + } else { + n_inv1 = inv1; + } + Node* inv; + if (neg_inv2) { + inv = new (phase->C, 3) SubINode(n_inv1, inv2); + } else { + inv = new (phase->C, 3) AddINode(n_inv1, inv2); + } + phase->register_new_node(inv, phase->get_early_ctrl(inv)); + + Node* addx; + if (neg_x) { + addx = new (phase->C, 3) SubINode(inv, x); + } else { + addx = new (phase->C, 3) AddINode(x, inv); + } + phase->register_new_node(addx, phase->get_ctrl(x)); + phase->_igvn.hash_delete(n1); + phase->_igvn.subsume_node(n1, addx); + return addx; +} + +//---------------------reassociate_invariants----------------------------- +// Reassociate invariant expressions: +void IdealLoopTree::reassociate_invariants(PhaseIdealLoop *phase) { + for (int i = _body.size() - 1; i >= 0; i--) { + Node *n = _body.at(i); + for (int j = 0; j < 5; j++) { + Node* nn = reassociate_add_sub(n, phase); + if (nn == NULL) break; + n = nn; // again + }; + } +} + +//------------------------------policy_peeling--------------------------------- +// Return TRUE or FALSE if the loop should be peeled or not. Peel if we can +// make some loop-invariant test (usually a null-check) happen before the loop. +bool IdealLoopTree::policy_peeling( PhaseIdealLoop *phase ) const { + Node *test = ((IdealLoopTree*)this)->tail(); + int body_size = ((IdealLoopTree*)this)->_body.size(); + int uniq = phase->C->unique(); + // Peeling does loop cloning which can result in O(N^2) node construction + if( body_size > 255 /* Prevent overflow for large body_size */ + || (body_size * body_size + uniq > MaxNodeLimit) ) { + return false; // too large to safely clone + } + while( test != _head ) { // Scan till run off top of loop + if( test->is_If() ) { // Test? + Node *ctrl = phase->get_ctrl(test->in(1)); + if (ctrl->is_top()) + return false; // Found dead test on live IF? No peeling! + // Standard IF only has one input value to check for loop invariance + assert( test->Opcode() == Op_If || test->Opcode() == Op_CountedLoopEnd, "Check this code when new subtype is added"); + // Condition is not a member of this loop? + if( !is_member(phase->get_loop(ctrl)) && + is_loop_exit(test) ) + return true; // Found reason to peel! + } + // Walk up dominators to loop _head looking for test which is + // executed on every path thru loop. + test = phase->idom(test); + } + return false; +} + +//------------------------------peeled_dom_test_elim--------------------------- +// If we got the effect of peeling, either by actually peeling or by making +// a pre-loop which must execute at least once, we can remove all +// loop-invariant dominated tests in the main body. +void PhaseIdealLoop::peeled_dom_test_elim( IdealLoopTree *loop, Node_List &old_new ) { + bool progress = true; + while( progress ) { + progress = false; // Reset for next iteration + Node *prev = loop->_head->in(LoopNode::LoopBackControl);//loop->tail(); + Node *test = prev->in(0); + while( test != loop->_head ) { // Scan till run off top of loop + + int p_op = prev->Opcode(); + if( (p_op == Op_IfFalse || p_op == Op_IfTrue) && + test->is_If() && // Test? + !test->in(1)->is_Con() && // And not already obvious? + // Condition is not a member of this loop? + !loop->is_member(get_loop(get_ctrl(test->in(1))))){ + // Walk loop body looking for instances of this test + for( uint i = 0; i < loop->_body.size(); i++ ) { + Node *n = loop->_body.at(i); + if( n->is_If() && n->in(1) == test->in(1) /*&& n != loop->tail()->in(0)*/ ) { + // IfNode was dominated by version in peeled loop body + progress = true; + dominated_by( old_new[prev->_idx], n ); + } + } + } + prev = test; + test = idom(test); + } // End of scan tests in loop + + } // End of while( progress ) +} + +//------------------------------do_peeling------------------------------------- +// Peel the first iteration of the given loop. +// Step 1: Clone the loop body. The clone becomes the peeled iteration. +// The pre-loop illegally has 2 control users (old & new loops). +// Step 2: Make the old-loop fall-in edges point to the peeled iteration. +// Do this by making the old-loop fall-in edges act as if they came +// around the loopback from the prior iteration (follow the old-loop +// backedges) and then map to the new peeled iteration. This leaves +// the pre-loop with only 1 user (the new peeled iteration), but the +// peeled-loop backedge has 2 users. +// Step 3: Cut the backedge on the clone (so its not a loop) and remove the +// extra backedge user. +void PhaseIdealLoop::do_peeling( IdealLoopTree *loop, Node_List &old_new ) { + + C->set_major_progress(); + // Peeling a 'main' loop in a pre/main/post situation obfuscates the + // 'pre' loop from the main and the 'pre' can no longer have it's + // iterations adjusted. Therefore, we need to declare this loop as + // no longer a 'main' loop; it will need new pre and post loops before + // we can do further RCE. + Node *h = loop->_head; + if( h->is_CountedLoop() ) { + CountedLoopNode *cl = h->as_CountedLoop(); + assert(cl->trip_count() > 0, "peeling a fully unrolled loop"); + cl->set_trip_count(cl->trip_count() - 1); + if( cl->is_main_loop() ) { + cl->set_normal_loop(); +#ifndef PRODUCT + if( PrintOpto && VerifyLoopOptimizations ) { + tty->print("Peeling a 'main' loop; resetting to 'normal' "); + loop->dump_head(); + } +#endif + } + } + + // Step 1: Clone the loop body. The clone becomes the peeled iteration. + // The pre-loop illegally has 2 control users (old & new loops). + clone_loop( loop, old_new, dom_depth(loop->_head) ); + + + // Step 2: Make the old-loop fall-in edges point to the peeled iteration. + // Do this by making the old-loop fall-in edges act as if they came + // around the loopback from the prior iteration (follow the old-loop + // backedges) and then map to the new peeled iteration. This leaves + // the pre-loop with only 1 user (the new peeled iteration), but the + // peeled-loop backedge has 2 users. + for (DUIterator_Fast jmax, j = loop->_head->fast_outs(jmax); j < jmax; j++) { + Node* old = loop->_head->fast_out(j); + if( old->in(0) == loop->_head && old->req() == 3 && + (old->is_Loop() || old->is_Phi()) ) { + Node *new_exit_value = old_new[old->in(LoopNode::LoopBackControl)->_idx]; + if( !new_exit_value ) // Backedge value is ALSO loop invariant? + // Then loop body backedge value remains the same. + new_exit_value = old->in(LoopNode::LoopBackControl); + _igvn.hash_delete(old); + old->set_req(LoopNode::EntryControl, new_exit_value); + } + } + + + // Step 3: Cut the backedge on the clone (so its not a loop) and remove the + // extra backedge user. + Node *nnn = old_new[loop->_head->_idx]; + _igvn.hash_delete(nnn); + nnn->set_req(LoopNode::LoopBackControl, C->top()); + for (DUIterator_Fast j2max, j2 = nnn->fast_outs(j2max); j2 < j2max; j2++) { + Node* use = nnn->fast_out(j2); + if( use->in(0) == nnn && use->req() == 3 && use->is_Phi() ) { + _igvn.hash_delete(use); + use->set_req(LoopNode::LoopBackControl, C->top()); + } + } + + + // Step 4: Correct dom-depth info. Set to loop-head depth. + int dd = dom_depth(loop->_head); + set_idom(loop->_head, loop->_head->in(1), dd); + for (uint j3 = 0; j3 < loop->_body.size(); j3++) { + Node *old = loop->_body.at(j3); + Node *nnn = old_new[old->_idx]; + if (!has_ctrl(nnn)) + set_idom(nnn, idom(nnn), dd-1); + // While we're at it, remove any SafePoints from the peeled code + if( old->Opcode() == Op_SafePoint ) { + Node *nnn = old_new[old->_idx]; + lazy_replace(nnn,nnn->in(TypeFunc::Control)); + } + } + + // Now force out all loop-invariant dominating tests. The optimizer + // finds some, but we _know_ they are all useless. + peeled_dom_test_elim(loop,old_new); + + loop->record_for_igvn(); +} + +//------------------------------policy_maximally_unroll------------------------ +// Return exact loop trip count, or 0 if not maximally unrolling +bool IdealLoopTree::policy_maximally_unroll( PhaseIdealLoop *phase ) const { + CountedLoopNode *cl = _head->as_CountedLoop(); + assert( cl->is_normal_loop(), "" ); + + Node *init_n = cl->init_trip(); + Node *limit_n = cl->limit(); + + // Non-constant bounds + if( init_n == NULL || !init_n->is_Con() || + limit_n == NULL || !limit_n->is_Con() || + // protect against stride not being a constant + !cl->stride_is_con() ) { + return false; + } + int init = init_n->get_int(); + int limit = limit_n->get_int(); + int span = limit - init; + int stride = cl->stride_con(); + + if (init >= limit || stride > span) { + // return a false (no maximally unroll) and the regular unroll/peel + // route will make a small mess which CCP will fold away. + return false; + } + uint trip_count = span/stride; // trip_count can be greater than 2 Gig. + assert( (int)trip_count*stride == span, "must divide evenly" ); + + // Real policy: if we maximally unroll, does it get too big? + // Allow the unrolled mess to get larger than standard loop + // size. After all, it will no longer be a loop. + uint body_size = _body.size(); + uint unroll_limit = (uint)LoopUnrollLimit * 4; + assert( (intx)unroll_limit == LoopUnrollLimit * 4, "LoopUnrollLimit must fit in 32bits"); + cl->set_trip_count(trip_count); + if( trip_count <= unroll_limit && body_size <= unroll_limit ) { + uint new_body_size = body_size * trip_count; + if (new_body_size <= unroll_limit && + body_size == new_body_size / trip_count && + // Unrolling can result in a large amount of node construction + new_body_size < MaxNodeLimit - phase->C->unique()) { + return true; // maximally unroll + } + } + + return false; // Do not maximally unroll +} + + +//------------------------------policy_unroll---------------------------------- +// Return TRUE or FALSE if the loop should be unrolled or not. Unroll if +// the loop is a CountedLoop and the body is small enough. +bool IdealLoopTree::policy_unroll( PhaseIdealLoop *phase ) const { + + CountedLoopNode *cl = _head->as_CountedLoop(); + assert( cl->is_normal_loop() || cl->is_main_loop(), "" ); + + // protect against stride not being a constant + if( !cl->stride_is_con() ) return false; + + // protect against over-unrolling + if( cl->trip_count() <= 1 ) return false; + + int future_unroll_ct = cl->unrolled_count() * 2; + + // Don't unroll if the next round of unrolling would push us + // over the expected trip count of the loop. One is subtracted + // from the expected trip count because the pre-loop normally + // executes 1 iteration. + if (UnrollLimitForProfileCheck > 0 && + cl->profile_trip_cnt() != COUNT_UNKNOWN && + future_unroll_ct > UnrollLimitForProfileCheck && + (float)future_unroll_ct > cl->profile_trip_cnt() - 1.0) { + return false; + } + + // When unroll count is greater than LoopUnrollMin, don't unroll if: + // the residual iterations are more than 10% of the trip count + // and rounds of "unroll,optimize" are not making significant progress + // Progress defined as current size less than 20% larger than previous size. + if (UseSuperWord && cl->node_count_before_unroll() > 0 && + future_unroll_ct > LoopUnrollMin && + (future_unroll_ct - 1) * 10.0 > cl->profile_trip_cnt() && + 1.2 * cl->node_count_before_unroll() < (double)_body.size()) { + return false; + } + + Node *init_n = cl->init_trip(); + Node *limit_n = cl->limit(); + // Non-constant bounds. + // Protect against over-unrolling when init or/and limit are not constant + // (so that trip_count's init value is maxint) but iv range is known. + if( init_n == NULL || !init_n->is_Con() || + limit_n == NULL || !limit_n->is_Con() ) { + Node* phi = cl->phi(); + if( phi != NULL ) { + assert(phi->is_Phi() && phi->in(0) == _head, "Counted loop should have iv phi."); + const TypeInt* iv_type = phase->_igvn.type(phi)->is_int(); + int next_stride = cl->stride_con() * 2; // stride after this unroll + if( next_stride > 0 ) { + if( iv_type->_lo + next_stride <= iv_type->_lo || // overflow + iv_type->_lo + next_stride > iv_type->_hi ) { + return false; // over-unrolling + } + } else if( next_stride < 0 ) { + if( iv_type->_hi + next_stride >= iv_type->_hi || // overflow + iv_type->_hi + next_stride < iv_type->_lo ) { + return false; // over-unrolling + } + } + } + } + + // Adjust body_size to determine if we unroll or not + uint body_size = _body.size(); + // Key test to unroll CaffeineMark's Logic test + int xors_in_loop = 0; + // Also count ModL, DivL and MulL which expand mightly + for( uint k = 0; k < _body.size(); k++ ) { + switch( _body.at(k)->Opcode() ) { + case Op_XorI: xors_in_loop++; break; // CaffeineMark's Logic test + case Op_ModL: body_size += 30; break; + case Op_DivL: body_size += 30; break; + case Op_MulL: body_size += 10; break; + } + } + + // Check for being too big + if( body_size > (uint)LoopUnrollLimit ) { + if( xors_in_loop >= 4 && body_size < (uint)LoopUnrollLimit*4) return true; + // Normal case: loop too big + return false; + } + + // Check for stride being a small enough constant + if( abs(cl->stride_con()) > (1<<3) ) return false; + + // Unroll once! (Each trip will soon do double iterations) + return true; +} + +//------------------------------policy_align----------------------------------- +// Return TRUE or FALSE if the loop should be cache-line aligned. Gather the +// expression that does the alignment. Note that only one array base can be +// aligned in a loop (unless the VM guarentees mutual alignment). Note that +// if we vectorize short memory ops into longer memory ops, we may want to +// increase alignment. +bool IdealLoopTree::policy_align( PhaseIdealLoop *phase ) const { + return false; +} + +//------------------------------policy_range_check----------------------------- +// Return TRUE or FALSE if the loop should be range-check-eliminated. +// Actually we do iteration-splitting, a more powerful form of RCE. +bool IdealLoopTree::policy_range_check( PhaseIdealLoop *phase ) const { + if( !RangeCheckElimination ) return false; + + CountedLoopNode *cl = _head->as_CountedLoop(); + // If we unrolled with no intention of doing RCE and we later + // changed our minds, we got no pre-loop. Either we need to + // make a new pre-loop, or we gotta disallow RCE. + if( cl->is_main_no_pre_loop() ) return false; // Disallowed for now. + Node *trip_counter = cl->phi(); + + // Check loop body for tests of trip-counter plus loop-invariant vs + // loop-invariant. + for( uint i = 0; i < _body.size(); i++ ) { + Node *iff = _body[i]; + if( iff->Opcode() == Op_If ) { // Test? + + // Comparing trip+off vs limit + Node *bol = iff->in(1); + if( bol->req() != 2 ) continue; // dead constant test + Node *cmp = bol->in(1); + + Node *rc_exp = cmp->in(1); + Node *limit = cmp->in(2); + + Node *limit_c = phase->get_ctrl(limit); + if( limit_c == phase->C->top() ) + return false; // Found dead test on live IF? No RCE! + if( is_member(phase->get_loop(limit_c) ) ) { + // Compare might have operands swapped; commute them + rc_exp = cmp->in(2); + limit = cmp->in(1); + limit_c = phase->get_ctrl(limit); + if( is_member(phase->get_loop(limit_c) ) ) + continue; // Both inputs are loop varying; cannot RCE + } + + if (!phase->is_scaled_iv_plus_offset(rc_exp, trip_counter, NULL, NULL)) { + continue; + } + // Yeah! Found a test like 'trip+off vs limit' + // Test is an IfNode, has 2 projections. If BOTH are in the loop + // we need loop unswitching instead of iteration splitting. + if( is_loop_exit(iff) ) + return true; // Found reason to split iterations + } // End of is IF + } + + return false; +} + +//------------------------------policy_peel_only------------------------------- +// Return TRUE or FALSE if the loop should NEVER be RCE'd or aligned. Useful +// for unrolling loops with NO array accesses. +bool IdealLoopTree::policy_peel_only( PhaseIdealLoop *phase ) const { + + for( uint i = 0; i < _body.size(); i++ ) + if( _body[i]->is_Mem() ) + return false; + + // No memory accesses at all! + return true; +} + +//------------------------------clone_up_backedge_goo-------------------------- +// If Node n lives in the back_ctrl block and cannot float, we clone a private +// version of n in preheader_ctrl block and return that, otherwise return n. +Node *PhaseIdealLoop::clone_up_backedge_goo( Node *back_ctrl, Node *preheader_ctrl, Node *n ) { + if( get_ctrl(n) != back_ctrl ) return n; + + Node *x = NULL; // If required, a clone of 'n' + // Check for 'n' being pinned in the backedge. + if( n->in(0) && n->in(0) == back_ctrl ) { + x = n->clone(); // Clone a copy of 'n' to preheader + x->set_req( 0, preheader_ctrl ); // Fix x's control input to preheader + } + + // Recursive fixup any other input edges into x. + // If there are no changes we can just return 'n', otherwise + // we need to clone a private copy and change it. + for( uint i = 1; i < n->req(); i++ ) { + Node *g = clone_up_backedge_goo( back_ctrl, preheader_ctrl, n->in(i) ); + if( g != n->in(i) ) { + if( !x ) + x = n->clone(); + x->set_req(i, g); + } + } + if( x ) { // x can legally float to pre-header location + register_new_node( x, preheader_ctrl ); + return x; + } else { // raise n to cover LCA of uses + set_ctrl( n, find_non_split_ctrl(back_ctrl->in(0)) ); + } + return n; +} + +//------------------------------insert_pre_post_loops-------------------------- +// Insert pre and post loops. If peel_only is set, the pre-loop can not have +// more iterations added. It acts as a 'peel' only, no lower-bound RCE, no +// alignment. Useful to unroll loops that do no array accesses. +void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_new, bool peel_only ) { + + C->set_major_progress(); + + // Find common pieces of the loop being guarded with pre & post loops + CountedLoopNode *main_head = loop->_head->as_CountedLoop(); + assert( main_head->is_normal_loop(), "" ); + CountedLoopEndNode *main_end = main_head->loopexit(); + assert( main_end->outcnt() == 2, "1 true, 1 false path only" ); + uint dd_main_head = dom_depth(main_head); + uint max = main_head->outcnt(); + + Node *pre_header= main_head->in(LoopNode::EntryControl); + Node *init = main_head->init_trip(); + Node *incr = main_end ->incr(); + Node *limit = main_end ->limit(); + Node *stride = main_end ->stride(); + Node *cmp = main_end ->cmp_node(); + BoolTest::mask b_test = main_end->test_trip(); + + // Need only 1 user of 'bol' because I will be hacking the loop bounds. + Node *bol = main_end->in(CountedLoopEndNode::TestValue); + if( bol->outcnt() != 1 ) { + bol = bol->clone(); + register_new_node(bol,main_end->in(CountedLoopEndNode::TestControl)); + _igvn.hash_delete(main_end); + main_end->set_req(CountedLoopEndNode::TestValue, bol); + } + // Need only 1 user of 'cmp' because I will be hacking the loop bounds. + if( cmp->outcnt() != 1 ) { + cmp = cmp->clone(); + register_new_node(cmp,main_end->in(CountedLoopEndNode::TestControl)); + _igvn.hash_delete(bol); + bol->set_req(1, cmp); + } + + //------------------------------ + // Step A: Create Post-Loop. + Node* main_exit = main_end->proj_out(false); + assert( main_exit->Opcode() == Op_IfFalse, "" ); + int dd_main_exit = dom_depth(main_exit); + + // Step A1: Clone the loop body. The clone becomes the post-loop. The main + // loop pre-header illegally has 2 control users (old & new loops). + clone_loop( loop, old_new, dd_main_exit ); + assert( old_new[main_end ->_idx]->Opcode() == Op_CountedLoopEnd, "" ); + CountedLoopNode *post_head = old_new[main_head->_idx]->as_CountedLoop(); + post_head->set_post_loop(main_head); + + // Build the main-loop normal exit. + IfFalseNode *new_main_exit = new (C, 1) IfFalseNode(main_end); + _igvn.register_new_node_with_optimizer( new_main_exit ); + set_idom(new_main_exit, main_end, dd_main_exit ); + set_loop(new_main_exit, loop->_parent); + + // Step A2: Build a zero-trip guard for the post-loop. After leaving the + // main-loop, the post-loop may not execute at all. We 'opaque' the incr + // (the main-loop trip-counter exit value) because we will be changing + // the exit value (via unrolling) so we cannot constant-fold away the zero + // trip guard until all unrolling is done. + Node *zer_opaq = new (C, 2) Opaque1Node(incr); + Node *zer_cmp = new (C, 3) CmpINode( zer_opaq, limit ); + Node *zer_bol = new (C, 2) BoolNode( zer_cmp, b_test ); + register_new_node( zer_opaq, new_main_exit ); + register_new_node( zer_cmp , new_main_exit ); + register_new_node( zer_bol , new_main_exit ); + + // Build the IfNode + IfNode *zer_iff = new (C, 2) IfNode( new_main_exit, zer_bol, PROB_FAIR, COUNT_UNKNOWN ); + _igvn.register_new_node_with_optimizer( zer_iff ); + set_idom(zer_iff, new_main_exit, dd_main_exit); + set_loop(zer_iff, loop->_parent); + + // Plug in the false-path, taken if we need to skip post-loop + _igvn.hash_delete( main_exit ); + main_exit->set_req(0, zer_iff); + _igvn._worklist.push(main_exit); + set_idom(main_exit, zer_iff, dd_main_exit); + set_idom(main_exit->unique_out(), zer_iff, dd_main_exit); + // Make the true-path, must enter the post loop + Node *zer_taken = new (C, 1) IfTrueNode( zer_iff ); + _igvn.register_new_node_with_optimizer( zer_taken ); + set_idom(zer_taken, zer_iff, dd_main_exit); + set_loop(zer_taken, loop->_parent); + // Plug in the true path + _igvn.hash_delete( post_head ); + post_head->set_req(LoopNode::EntryControl, zer_taken); + set_idom(post_head, zer_taken, dd_main_exit); + + // Step A3: Make the fall-in values to the post-loop come from the + // fall-out values of the main-loop. + for (DUIterator_Fast imax, i = main_head->fast_outs(imax); i < imax; i++) { + Node* main_phi = main_head->fast_out(i); + if( main_phi->is_Phi() && main_phi->in(0) == main_head && main_phi->outcnt() >0 ) { + Node *post_phi = old_new[main_phi->_idx]; + Node *fallmain = clone_up_backedge_goo(main_head->back_control(), + post_head->init_control(), + main_phi->in(LoopNode::LoopBackControl)); + _igvn.hash_delete(post_phi); + post_phi->set_req( LoopNode::EntryControl, fallmain ); + } + } + + // Update local caches for next stanza + main_exit = new_main_exit; + + + //------------------------------ + // Step B: Create Pre-Loop. + + // Step B1: Clone the loop body. The clone becomes the pre-loop. The main + // loop pre-header illegally has 2 control users (old & new loops). + clone_loop( loop, old_new, dd_main_head ); + CountedLoopNode* pre_head = old_new[main_head->_idx]->as_CountedLoop(); + CountedLoopEndNode* pre_end = old_new[main_end ->_idx]->as_CountedLoopEnd(); + pre_head->set_pre_loop(main_head); + Node *pre_incr = old_new[incr->_idx]; + + // Find the pre-loop normal exit. + Node* pre_exit = pre_end->proj_out(false); + assert( pre_exit->Opcode() == Op_IfFalse, "" ); + IfFalseNode *new_pre_exit = new (C, 1) IfFalseNode(pre_end); + _igvn.register_new_node_with_optimizer( new_pre_exit ); + set_idom(new_pre_exit, pre_end, dd_main_head); + set_loop(new_pre_exit, loop->_parent); + + // Step B2: Build a zero-trip guard for the main-loop. After leaving the + // pre-loop, the main-loop may not execute at all. Later in life this + // zero-trip guard will become the minimum-trip guard when we unroll + // the main-loop. + Node *min_opaq = new (C, 2) Opaque1Node(limit); + Node *min_cmp = new (C, 3) CmpINode( pre_incr, min_opaq ); + Node *min_bol = new (C, 2) BoolNode( min_cmp, b_test ); + register_new_node( min_opaq, new_pre_exit ); + register_new_node( min_cmp , new_pre_exit ); + register_new_node( min_bol , new_pre_exit ); + + // Build the IfNode + IfNode *min_iff = new (C, 2) IfNode( new_pre_exit, min_bol, PROB_FAIR, COUNT_UNKNOWN ); + _igvn.register_new_node_with_optimizer( min_iff ); + set_idom(min_iff, new_pre_exit, dd_main_head); + set_loop(min_iff, loop->_parent); + + // Plug in the false-path, taken if we need to skip main-loop + _igvn.hash_delete( pre_exit ); + pre_exit->set_req(0, min_iff); + set_idom(pre_exit, min_iff, dd_main_head); + set_idom(pre_exit->unique_out(), min_iff, dd_main_head); + // Make the true-path, must enter the main loop + Node *min_taken = new (C, 1) IfTrueNode( min_iff ); + _igvn.register_new_node_with_optimizer( min_taken ); + set_idom(min_taken, min_iff, dd_main_head); + set_loop(min_taken, loop->_parent); + // Plug in the true path + _igvn.hash_delete( main_head ); + main_head->set_req(LoopNode::EntryControl, min_taken); + set_idom(main_head, min_taken, dd_main_head); + + // Step B3: Make the fall-in values to the main-loop come from the + // fall-out values of the pre-loop. + for (DUIterator_Fast i2max, i2 = main_head->fast_outs(i2max); i2 < i2max; i2++) { + Node* main_phi = main_head->fast_out(i2); + if( main_phi->is_Phi() && main_phi->in(0) == main_head && main_phi->outcnt() > 0 ) { + Node *pre_phi = old_new[main_phi->_idx]; + Node *fallpre = clone_up_backedge_goo(pre_head->back_control(), + main_head->init_control(), + pre_phi->in(LoopNode::LoopBackControl)); + _igvn.hash_delete(main_phi); + main_phi->set_req( LoopNode::EntryControl, fallpre ); + } + } + + // Step B4: Shorten the pre-loop to run only 1 iteration (for now). + // RCE and alignment may change this later. + Node *cmp_end = pre_end->cmp_node(); + assert( cmp_end->in(2) == limit, "" ); + Node *pre_limit = new (C, 3) AddINode( init, stride ); + + // Save the original loop limit in this Opaque1 node for + // use by range check elimination. + Node *pre_opaq = new (C, 3) Opaque1Node(pre_limit, limit); + + register_new_node( pre_limit, pre_head->in(0) ); + register_new_node( pre_opaq , pre_head->in(0) ); + + // Since no other users of pre-loop compare, I can hack limit directly + assert( cmp_end->outcnt() == 1, "no other users" ); + _igvn.hash_delete(cmp_end); + cmp_end->set_req(2, peel_only ? pre_limit : pre_opaq); + + // Special case for not-equal loop bounds: + // Change pre loop test, main loop test, and the + // main loop guard test to use lt or gt depending on stride + // direction: + // positive stride use < + // negative stride use > + + if (pre_end->in(CountedLoopEndNode::TestValue)->as_Bool()->_test._test == BoolTest::ne) { + + BoolTest::mask new_test = (main_end->stride_con() > 0) ? BoolTest::lt : BoolTest::gt; + // Modify pre loop end condition + Node* pre_bol = pre_end->in(CountedLoopEndNode::TestValue)->as_Bool(); + BoolNode* new_bol0 = new (C, 2) BoolNode(pre_bol->in(1), new_test); + register_new_node( new_bol0, pre_head->in(0) ); + _igvn.hash_delete(pre_end); + pre_end->set_req(CountedLoopEndNode::TestValue, new_bol0); + // Modify main loop guard condition + assert(min_iff->in(CountedLoopEndNode::TestValue) == min_bol, "guard okay"); + BoolNode* new_bol1 = new (C, 2) BoolNode(min_bol->in(1), new_test); + register_new_node( new_bol1, new_pre_exit ); + _igvn.hash_delete(min_iff); + min_iff->set_req(CountedLoopEndNode::TestValue, new_bol1); + // Modify main loop end condition + BoolNode* main_bol = main_end->in(CountedLoopEndNode::TestValue)->as_Bool(); + BoolNode* new_bol2 = new (C, 2) BoolNode(main_bol->in(1), new_test); + register_new_node( new_bol2, main_end->in(CountedLoopEndNode::TestControl) ); + _igvn.hash_delete(main_end); + main_end->set_req(CountedLoopEndNode::TestValue, new_bol2); + } + + // Flag main loop + main_head->set_main_loop(); + if( peel_only ) main_head->set_main_no_pre_loop(); + + // It's difficult to be precise about the trip-counts + // for the pre/post loops. They are usually very short, + // so guess that 4 trips is a reasonable value. + post_head->set_profile_trip_cnt(4.0); + pre_head->set_profile_trip_cnt(4.0); + + // Now force out all loop-invariant dominating tests. The optimizer + // finds some, but we _know_ they are all useless. + peeled_dom_test_elim(loop,old_new); +} + +//------------------------------is_invariant----------------------------- +// Return true if n is invariant +bool IdealLoopTree::is_invariant(Node* n) const { + Node *n_c = _phase->get_ctrl(n); + if (n_c->is_top()) return false; + return !is_member(_phase->get_loop(n_c)); +} + + +//------------------------------do_unroll-------------------------------------- +// Unroll the loop body one step - make each trip do 2 iterations. +void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool adjust_min_trip ) { + assert( LoopUnrollLimit, "" ); +#ifndef PRODUCT + if( PrintOpto && VerifyLoopOptimizations ) { + tty->print("Unrolling "); + loop->dump_head(); + } +#endif + CountedLoopNode *loop_head = loop->_head->as_CountedLoop(); + CountedLoopEndNode *loop_end = loop_head->loopexit(); + assert( loop_end, "" ); + + // Remember loop node count before unrolling to detect + // if rounds of unroll,optimize are making progress + loop_head->set_node_count_before_unroll(loop->_body.size()); + + Node *ctrl = loop_head->in(LoopNode::EntryControl); + Node *limit = loop_head->limit(); + Node *init = loop_head->init_trip(); + Node *strid = loop_head->stride(); + + Node *opaq = NULL; + if( adjust_min_trip ) { // If not maximally unrolling, need adjustment + assert( loop_head->is_main_loop(), "" ); + assert( ctrl->Opcode() == Op_IfTrue || ctrl->Opcode() == Op_IfFalse, "" ); + Node *iff = ctrl->in(0); + assert( iff->Opcode() == Op_If, "" ); + Node *bol = iff->in(1); + assert( bol->Opcode() == Op_Bool, "" ); + Node *cmp = bol->in(1); + assert( cmp->Opcode() == Op_CmpI, "" ); + opaq = cmp->in(2); + // Occasionally it's possible for a pre-loop Opaque1 node to be + // optimized away and then another round of loop opts attempted. + // We can not optimize this particular loop in that case. + if( opaq->Opcode() != Op_Opaque1 ) + return; // Cannot find pre-loop! Bail out! + } + + C->set_major_progress(); + + // Adjust max trip count. The trip count is intentionally rounded + // down here (e.g. 15-> 7-> 3-> 1) because if we unwittingly over-unroll, + // the main, unrolled, part of the loop will never execute as it is protected + // by the min-trip test. See bug 4834191 for a case where we over-unrolled + // and later determined that part of the unrolled loop was dead. + loop_head->set_trip_count(loop_head->trip_count() / 2); + + // Double the count of original iterations in the unrolled loop body. + loop_head->double_unrolled_count(); + + // ----------- + // Step 2: Cut back the trip counter for an unroll amount of 2. + // Loop will normally trip (limit - init)/stride_con. Since it's a + // CountedLoop this is exact (stride divides limit-init exactly). + // We are going to double the loop body, so we want to knock off any + // odd iteration: (trip_cnt & ~1). Then back compute a new limit. + Node *span = new (C, 3) SubINode( limit, init ); + register_new_node( span, ctrl ); + Node *trip = new (C, 3) DivINode( 0, span, strid ); + register_new_node( trip, ctrl ); + Node *mtwo = _igvn.intcon(-2); + set_ctrl(mtwo, C->root()); + Node *rond = new (C, 3) AndINode( trip, mtwo ); + register_new_node( rond, ctrl ); + Node *spn2 = new (C, 3) MulINode( rond, strid ); + register_new_node( spn2, ctrl ); + Node *lim2 = new (C, 3) AddINode( spn2, init ); + register_new_node( lim2, ctrl ); + + // Hammer in the new limit + Node *ctrl2 = loop_end->in(0); + Node *cmp2 = new (C, 3) CmpINode( loop_head->incr(), lim2 ); + register_new_node( cmp2, ctrl2 ); + Node *bol2 = new (C, 2) BoolNode( cmp2, loop_end->test_trip() ); + register_new_node( bol2, ctrl2 ); + _igvn.hash_delete(loop_end); + loop_end->set_req(CountedLoopEndNode::TestValue, bol2); + + // Step 3: Find the min-trip test guaranteed before a 'main' loop. + // Make it a 1-trip test (means at least 2 trips). + if( adjust_min_trip ) { + // Guard test uses an 'opaque' node which is not shared. Hence I + // can edit it's inputs directly. Hammer in the new limit for the + // minimum-trip guard. + assert( opaq->outcnt() == 1, "" ); + _igvn.hash_delete(opaq); + opaq->set_req(1, lim2); + } + + // --------- + // Step 4: Clone the loop body. Move it inside the loop. This loop body + // represents the odd iterations; since the loop trips an even number of + // times its backedge is never taken. Kill the backedge. + uint dd = dom_depth(loop_head); + clone_loop( loop, old_new, dd ); + + // Make backedges of the clone equal to backedges of the original. + // Make the fall-in from the original come from the fall-out of the clone. + for (DUIterator_Fast jmax, j = loop_head->fast_outs(jmax); j < jmax; j++) { + Node* phi = loop_head->fast_out(j); + if( phi->is_Phi() && phi->in(0) == loop_head && phi->outcnt() > 0 ) { + Node *newphi = old_new[phi->_idx]; + _igvn.hash_delete( phi ); + _igvn.hash_delete( newphi ); + + phi ->set_req(LoopNode:: EntryControl, newphi->in(LoopNode::LoopBackControl)); + newphi->set_req(LoopNode::LoopBackControl, phi ->in(LoopNode::LoopBackControl)); + phi ->set_req(LoopNode::LoopBackControl, C->top()); + } + } + Node *clone_head = old_new[loop_head->_idx]; + _igvn.hash_delete( clone_head ); + loop_head ->set_req(LoopNode:: EntryControl, clone_head->in(LoopNode::LoopBackControl)); + clone_head->set_req(LoopNode::LoopBackControl, loop_head ->in(LoopNode::LoopBackControl)); + loop_head ->set_req(LoopNode::LoopBackControl, C->top()); + loop->_head = clone_head; // New loop header + + set_idom(loop_head, loop_head ->in(LoopNode::EntryControl), dd); + set_idom(clone_head, clone_head->in(LoopNode::EntryControl), dd); + + // Kill the clone's backedge + Node *newcle = old_new[loop_end->_idx]; + _igvn.hash_delete( newcle ); + Node *one = _igvn.intcon(1); + set_ctrl(one, C->root()); + newcle->set_req(1, one); + // Force clone into same loop body + uint max = loop->_body.size(); + for( uint k = 0; k < max; k++ ) { + Node *old = loop->_body.at(k); + Node *nnn = old_new[old->_idx]; + loop->_body.push(nnn); + if (!has_ctrl(old)) + set_loop(nnn, loop); + } +} + +//------------------------------do_maximally_unroll---------------------------- + +void PhaseIdealLoop::do_maximally_unroll( IdealLoopTree *loop, Node_List &old_new ) { + CountedLoopNode *cl = loop->_head->as_CountedLoop(); + assert( cl->trip_count() > 0, ""); + + // If loop is tripping an odd number of times, peel odd iteration + if( (cl->trip_count() & 1) == 1 ) { + do_peeling( loop, old_new ); + } + + // Now its tripping an even number of times remaining. Double loop body. + // Do not adjust pre-guards; they are not needed and do not exist. + if( cl->trip_count() > 0 ) { + do_unroll( loop, old_new, false ); + } +} + +//------------------------------dominates_backedge--------------------------------- +// Returns true if ctrl is executed on every complete iteration +bool IdealLoopTree::dominates_backedge(Node* ctrl) { + assert(ctrl->is_CFG(), "must be control"); + Node* backedge = _head->as_Loop()->in(LoopNode::LoopBackControl); + return _phase->dom_lca_internal(ctrl, backedge) == ctrl; +} + +//------------------------------add_constraint--------------------------------- +// Constrain the main loop iterations so the condition: +// scale_con * I + offset < limit +// always holds true. That is, either increase the number of iterations in +// the pre-loop or the post-loop until the condition holds true in the main +// loop. Stride, scale, offset and limit are all loop invariant. Further, +// stride and scale are constants (offset and limit often are). +void PhaseIdealLoop::add_constraint( int stride_con, int scale_con, Node *offset, Node *limit, Node *pre_ctrl, Node **pre_limit, Node **main_limit ) { + + // Compute "I :: (limit-offset)/scale_con" + Node *con = new (C, 3) SubINode( limit, offset ); + register_new_node( con, pre_ctrl ); + Node *scale = _igvn.intcon(scale_con); + set_ctrl(scale, C->root()); + Node *X = new (C, 3) DivINode( 0, con, scale ); + register_new_node( X, pre_ctrl ); + + // For positive stride, the pre-loop limit always uses a MAX function + // and the main loop a MIN function. For negative stride these are + // reversed. + + // Also for positive stride*scale the affine function is increasing, so the + // pre-loop must check for underflow and the post-loop for overflow. + // Negative stride*scale reverses this; pre-loop checks for overflow and + // post-loop for underflow. + if( stride_con*scale_con > 0 ) { + // Compute I < (limit-offset)/scale_con + // Adjust main-loop last iteration to be MIN/MAX(main_loop,X) + *main_limit = (stride_con > 0) + ? (Node*)(new (C, 3) MinINode( *main_limit, X )) + : (Node*)(new (C, 3) MaxINode( *main_limit, X )); + register_new_node( *main_limit, pre_ctrl ); + + } else { + // Compute (limit-offset)/scale_con + SGN(-scale_con) <= I + // Add the negation of the main-loop constraint to the pre-loop. + // See footnote [++] below for a derivation of the limit expression. + Node *incr = _igvn.intcon(scale_con > 0 ? -1 : 1); + set_ctrl(incr, C->root()); + Node *adj = new (C, 3) AddINode( X, incr ); + register_new_node( adj, pre_ctrl ); + *pre_limit = (scale_con > 0) + ? (Node*)new (C, 3) MinINode( *pre_limit, adj ) + : (Node*)new (C, 3) MaxINode( *pre_limit, adj ); + register_new_node( *pre_limit, pre_ctrl ); + +// [++] Here's the algebra that justifies the pre-loop limit expression: +// +// NOT( scale_con * I + offset < limit ) +// == +// scale_con * I + offset >= limit +// == +// SGN(scale_con) * I >= (limit-offset)/|scale_con| +// == +// (limit-offset)/|scale_con| <= I * SGN(scale_con) +// == +// (limit-offset)/|scale_con|-1 < I * SGN(scale_con) +// == +// ( if (scale_con > 0) /*common case*/ +// (limit-offset)/scale_con - 1 < I +// else +// (limit-offset)/scale_con + 1 > I +// ) +// ( if (scale_con > 0) /*common case*/ +// (limit-offset)/scale_con + SGN(-scale_con) < I +// else +// (limit-offset)/scale_con + SGN(-scale_con) > I + } +} + + +//------------------------------is_scaled_iv--------------------------------- +// Return true if exp is a constant times an induction var +bool PhaseIdealLoop::is_scaled_iv(Node* exp, Node* iv, int* p_scale) { + if (exp == iv) { + if (p_scale != NULL) { + *p_scale = 1; + } + return true; + } + int opc = exp->Opcode(); + if (opc == Op_MulI) { + if (exp->in(1) == iv && exp->in(2)->is_Con()) { + if (p_scale != NULL) { + *p_scale = exp->in(2)->get_int(); + } + return true; + } + if (exp->in(2) == iv && exp->in(1)->is_Con()) { + if (p_scale != NULL) { + *p_scale = exp->in(1)->get_int(); + } + return true; + } + } else if (opc == Op_LShiftI) { + if (exp->in(1) == iv && exp->in(2)->is_Con()) { + if (p_scale != NULL) { + *p_scale = 1 << exp->in(2)->get_int(); + } + return true; + } + } + return false; +} + +//-----------------------------is_scaled_iv_plus_offset------------------------------ +// Return true if exp is a simple induction variable expression: k1*iv + (invar + k2) +bool PhaseIdealLoop::is_scaled_iv_plus_offset(Node* exp, Node* iv, int* p_scale, Node** p_offset, int depth) { + if (is_scaled_iv(exp, iv, p_scale)) { + if (p_offset != NULL) { + Node *zero = _igvn.intcon(0); + set_ctrl(zero, C->root()); + *p_offset = zero; + } + return true; + } + int opc = exp->Opcode(); + if (opc == Op_AddI) { + if (is_scaled_iv(exp->in(1), iv, p_scale)) { + if (p_offset != NULL) { + *p_offset = exp->in(2); + } + return true; + } + if (exp->in(2)->is_Con()) { + Node* offset2 = NULL; + if (depth < 2 && + is_scaled_iv_plus_offset(exp->in(1), iv, p_scale, + p_offset != NULL ? &offset2 : NULL, depth+1)) { + if (p_offset != NULL) { + Node *ctrl_off2 = get_ctrl(offset2); + Node* offset = new (C, 3) AddINode(offset2, exp->in(2)); + register_new_node(offset, ctrl_off2); + *p_offset = offset; + } + return true; + } + } + } else if (opc == Op_SubI) { + if (is_scaled_iv(exp->in(1), iv, p_scale)) { + if (p_offset != NULL) { + Node *zero = _igvn.intcon(0); + set_ctrl(zero, C->root()); + Node *ctrl_off = get_ctrl(exp->in(2)); + Node* offset = new (C, 3) SubINode(zero, exp->in(2)); + register_new_node(offset, ctrl_off); + *p_offset = offset; + } + return true; + } + if (is_scaled_iv(exp->in(2), iv, p_scale)) { + if (p_offset != NULL) { + *p_scale *= -1; + *p_offset = exp->in(1); + } + return true; + } + } + return false; +} + +//------------------------------do_range_check--------------------------------- +// Eliminate range-checks and other trip-counter vs loop-invariant tests. +void PhaseIdealLoop::do_range_check( IdealLoopTree *loop, Node_List &old_new ) { +#ifndef PRODUCT + if( PrintOpto && VerifyLoopOptimizations ) { + tty->print("Range Check Elimination "); + loop->dump_head(); + } +#endif + assert( RangeCheckElimination, "" ); + CountedLoopNode *cl = loop->_head->as_CountedLoop(); + assert( cl->is_main_loop(), "" ); + + // Find the trip counter; we are iteration splitting based on it + Node *trip_counter = cl->phi(); + // Find the main loop limit; we will trim it's iterations + // to not ever trip end tests + Node *main_limit = cl->limit(); + // Find the pre-loop limit; we will expand it's iterations to + // not ever trip low tests. + Node *ctrl = cl->in(LoopNode::EntryControl); + assert( ctrl->Opcode() == Op_IfTrue || ctrl->Opcode() == Op_IfFalse, "" ); + Node *iffm = ctrl->in(0); + assert( iffm->Opcode() == Op_If, "" ); + Node *p_f = iffm->in(0); + assert( p_f->Opcode() == Op_IfFalse, "" ); + CountedLoopEndNode *pre_end = p_f->in(0)->as_CountedLoopEnd(); + assert( pre_end->loopnode()->is_pre_loop(), "" ); + Node *pre_opaq1 = pre_end->limit(); + // Occasionally it's possible for a pre-loop Opaque1 node to be + // optimized away and then another round of loop opts attempted. + // We can not optimize this particular loop in that case. + if( pre_opaq1->Opcode() != Op_Opaque1 ) + return; + Opaque1Node *pre_opaq = (Opaque1Node*)pre_opaq1; + Node *pre_limit = pre_opaq->in(1); + + // Where do we put new limit calculations + Node *pre_ctrl = pre_end->loopnode()->in(LoopNode::EntryControl); + + // Ensure the original loop limit is available from the + // pre-loop Opaque1 node. + Node *orig_limit = pre_opaq->original_loop_limit(); + if( orig_limit == NULL || _igvn.type(orig_limit) == Type::TOP ) + return; + + // Need to find the main-loop zero-trip guard + Node *bolzm = iffm->in(1); + assert( bolzm->Opcode() == Op_Bool, "" ); + Node *cmpzm = bolzm->in(1); + assert( cmpzm->is_Cmp(), "" ); + Node *opqzm = cmpzm->in(2); + if( opqzm->Opcode() != Op_Opaque1 ) + return; + assert( opqzm->in(1) == main_limit, "do not understand situation" ); + + // Must know if its a count-up or count-down loop + + // protect against stride not being a constant + if ( !cl->stride_is_con() ) { + return; + } + int stride_con = cl->stride_con(); + Node *zero = _igvn.intcon(0); + Node *one = _igvn.intcon(1); + set_ctrl(zero, C->root()); + set_ctrl(one, C->root()); + + // Range checks that do not dominate the loop backedge (ie. + // conditionally executed) can lengthen the pre loop limit beyond + // the original loop limit. To prevent this, the pre limit is + // (for stride > 0) MINed with the original loop limit (MAXed + // stride < 0) when some range_check (rc) is conditionally + // executed. + bool conditional_rc = false; + + // Check loop body for tests of trip-counter plus loop-invariant vs + // loop-invariant. + for( uint i = 0; i < loop->_body.size(); i++ ) { + Node *iff = loop->_body[i]; + if( iff->Opcode() == Op_If ) { // Test? + + // Test is an IfNode, has 2 projections. If BOTH are in the loop + // we need loop unswitching instead of iteration splitting. + Node *exit = loop->is_loop_exit(iff); + if( !exit ) continue; + int flip = (exit->Opcode() == Op_IfTrue) ? 1 : 0; + + // Get boolean condition to test + Node *i1 = iff->in(1); + if( !i1->is_Bool() ) continue; + BoolNode *bol = i1->as_Bool(); + BoolTest b_test = bol->_test; + // Flip sense of test if exit condition is flipped + if( flip ) + b_test = b_test.negate(); + + // Get compare + Node *cmp = bol->in(1); + + // Look for trip_counter + offset vs limit + Node *rc_exp = cmp->in(1); + Node *limit = cmp->in(2); + jint scale_con= 1; // Assume trip counter not scaled + + Node *limit_c = get_ctrl(limit); + if( loop->is_member(get_loop(limit_c) ) ) { + // Compare might have operands swapped; commute them + b_test = b_test.commute(); + rc_exp = cmp->in(2); + limit = cmp->in(1); + limit_c = get_ctrl(limit); + if( loop->is_member(get_loop(limit_c) ) ) + continue; // Both inputs are loop varying; cannot RCE + } + // Here we know 'limit' is loop invariant + + // 'limit' maybe pinned below the zero trip test (probably from a + // previous round of rce), in which case, it can't be used in the + // zero trip test expression which must occur before the zero test's if. + if( limit_c == ctrl ) { + continue; // Don't rce this check but continue looking for other candidates. + } + + // Check for scaled induction variable plus an offset + Node *offset = NULL; + + if (!is_scaled_iv_plus_offset(rc_exp, trip_counter, &scale_con, &offset)) { + continue; + } + + Node *offset_c = get_ctrl(offset); + if( loop->is_member( get_loop(offset_c) ) ) + continue; // Offset is not really loop invariant + // Here we know 'offset' is loop invariant. + + // As above for the 'limit', the 'offset' maybe pinned below the + // zero trip test. + if( offset_c == ctrl ) { + continue; // Don't rce this check but continue looking for other candidates. + } + + // At this point we have the expression as: + // scale_con * trip_counter + offset :: limit + // where scale_con, offset and limit are loop invariant. Trip_counter + // monotonically increases by stride_con, a constant. Both (or either) + // stride_con and scale_con can be negative which will flip about the + // sense of the test. + + // Adjust pre and main loop limits to guard the correct iteration set + if( cmp->Opcode() == Op_CmpU ) {// Unsigned compare is really 2 tests + if( b_test._test == BoolTest::lt ) { // Range checks always use lt + // The overflow limit: scale*I+offset < limit + add_constraint( stride_con, scale_con, offset, limit, pre_ctrl, &pre_limit, &main_limit ); + // The underflow limit: 0 <= scale*I+offset. + // Some math yields: -scale*I-(offset+1) < 0 + Node *plus_one = new (C, 3) AddINode( offset, one ); + register_new_node( plus_one, pre_ctrl ); + Node *neg_offset = new (C, 3) SubINode( zero, plus_one ); + register_new_node( neg_offset, pre_ctrl ); + add_constraint( stride_con, -scale_con, neg_offset, zero, pre_ctrl, &pre_limit, &main_limit ); + if (!conditional_rc) { + conditional_rc = !loop->dominates_backedge(iff); + } + } else { +#ifndef PRODUCT + if( PrintOpto ) + tty->print_cr("missed RCE opportunity"); +#endif + continue; // In release mode, ignore it + } + } else { // Otherwise work on normal compares + switch( b_test._test ) { + case BoolTest::ge: // Convert X >= Y to -X <= -Y + scale_con = -scale_con; + offset = new (C, 3) SubINode( zero, offset ); + register_new_node( offset, pre_ctrl ); + limit = new (C, 3) SubINode( zero, limit ); + register_new_node( limit, pre_ctrl ); + // Fall into LE case + case BoolTest::le: // Convert X <= Y to X < Y+1 + limit = new (C, 3) AddINode( limit, one ); + register_new_node( limit, pre_ctrl ); + // Fall into LT case + case BoolTest::lt: + add_constraint( stride_con, scale_con, offset, limit, pre_ctrl, &pre_limit, &main_limit ); + if (!conditional_rc) { + conditional_rc = !loop->dominates_backedge(iff); + } + break; + default: +#ifndef PRODUCT + if( PrintOpto ) + tty->print_cr("missed RCE opportunity"); +#endif + continue; // Unhandled case + } + } + + // Kill the eliminated test + C->set_major_progress(); + Node *kill_con = _igvn.intcon( 1-flip ); + set_ctrl(kill_con, C->root()); + _igvn.hash_delete(iff); + iff->set_req(1, kill_con); + _igvn._worklist.push(iff); + // Find surviving projection + assert(iff->is_If(), ""); + ProjNode* dp = ((IfNode*)iff)->proj_out(1-flip); + // Find loads off the surviving projection; remove their control edge + for (DUIterator_Fast imax, i = dp->fast_outs(imax); i < imax; i++) { + Node* cd = dp->fast_out(i); // Control-dependent node + if( cd->is_Load() ) { // Loads can now float around in the loop + _igvn.hash_delete(cd); + // Allow the load to float around in the loop, or before it + // but NOT before the pre-loop. + cd->set_req(0, ctrl); // ctrl, not NULL + _igvn._worklist.push(cd); + --i; + --imax; + } + } + + } // End of is IF + + } + + // Update loop limits + if (conditional_rc) { + pre_limit = (stride_con > 0) ? (Node*)new (C,3) MinINode(pre_limit, orig_limit) + : (Node*)new (C,3) MaxINode(pre_limit, orig_limit); + register_new_node(pre_limit, pre_ctrl); + } + _igvn.hash_delete(pre_opaq); + pre_opaq->set_req(1, pre_limit); + + // Note:: we are making the main loop limit no longer precise; + // need to round up based on stride. + if( stride_con != 1 && stride_con != -1 ) { // Cutout for common case + // "Standard" round-up logic: ([main_limit-init+(y-1)]/y)*y+init + // Hopefully, compiler will optimize for powers of 2. + Node *ctrl = get_ctrl(main_limit); + Node *stride = cl->stride(); + Node *init = cl->init_trip(); + Node *span = new (C, 3) SubINode(main_limit,init); + register_new_node(span,ctrl); + Node *rndup = _igvn.intcon(stride_con + ((stride_con>0)?-1:1)); + Node *add = new (C, 3) AddINode(span,rndup); + register_new_node(add,ctrl); + Node *div = new (C, 3) DivINode(0,add,stride); + register_new_node(div,ctrl); + Node *mul = new (C, 3) MulINode(div,stride); + register_new_node(mul,ctrl); + Node *newlim = new (C, 3) AddINode(mul,init); + register_new_node(newlim,ctrl); + main_limit = newlim; + } + + Node *main_cle = cl->loopexit(); + Node *main_bol = main_cle->in(1); + // Hacking loop bounds; need private copies of exit test + if( main_bol->outcnt() > 1 ) {// BoolNode shared? + _igvn.hash_delete(main_cle); + main_bol = main_bol->clone();// Clone a private BoolNode + register_new_node( main_bol, main_cle->in(0) ); + main_cle->set_req(1,main_bol); + } + Node *main_cmp = main_bol->in(1); + if( main_cmp->outcnt() > 1 ) { // CmpNode shared? + _igvn.hash_delete(main_bol); + main_cmp = main_cmp->clone();// Clone a private CmpNode + register_new_node( main_cmp, main_cle->in(0) ); + main_bol->set_req(1,main_cmp); + } + // Hack the now-private loop bounds + _igvn.hash_delete(main_cmp); + main_cmp->set_req(2, main_limit); + _igvn._worklist.push(main_cmp); + // The OpaqueNode is unshared by design + _igvn.hash_delete(opqzm); + assert( opqzm->outcnt() == 1, "cannot hack shared node" ); + opqzm->set_req(1,main_limit); + _igvn._worklist.push(opqzm); +} + +//------------------------------DCE_loop_body---------------------------------- +// Remove simplistic dead code from loop body +void IdealLoopTree::DCE_loop_body() { + for( uint i = 0; i < _body.size(); i++ ) + if( _body.at(i)->outcnt() == 0 ) + _body.map( i--, _body.pop() ); +} + + +//------------------------------adjust_loop_exit_prob-------------------------- +// Look for loop-exit tests with the 50/50 (or worse) guesses from the parsing stage. +// Replace with a 1-in-10 exit guess. +void IdealLoopTree::adjust_loop_exit_prob( PhaseIdealLoop *phase ) { + Node *test = tail(); + while( test != _head ) { + uint top = test->Opcode(); + if( top == Op_IfTrue || top == Op_IfFalse ) { + int test_con = ((ProjNode*)test)->_con; + assert(top == (uint)(test_con? Op_IfTrue: Op_IfFalse), "sanity"); + IfNode *iff = test->in(0)->as_If(); + if( iff->outcnt() == 2 ) { // Ignore dead tests + Node *bol = iff->in(1); + if( bol && bol->req() > 1 && bol->in(1) && + ((bol->in(1)->Opcode() == Op_StorePConditional ) || + (bol->in(1)->Opcode() == Op_StoreLConditional ) || + (bol->in(1)->Opcode() == Op_CompareAndSwapI ) || + (bol->in(1)->Opcode() == Op_CompareAndSwapL ) || + (bol->in(1)->Opcode() == Op_CompareAndSwapP ))) + return; // Allocation loops RARELY take backedge + // Find the OTHER exit path from the IF + Node* ex = iff->proj_out(1-test_con); + float p = iff->_prob; + if( !phase->is_member( this, ex ) && iff->_fcnt == COUNT_UNKNOWN ) { + if( top == Op_IfTrue ) { + if( p < (PROB_FAIR + PROB_UNLIKELY_MAG(3))) { + iff->_prob = PROB_STATIC_FREQUENT; + } + } else { + if( p > (PROB_FAIR - PROB_UNLIKELY_MAG(3))) { + iff->_prob = PROB_STATIC_INFREQUENT; + } + } + } + } + } + test = phase->idom(test); + } +} + + +//------------------------------policy_do_remove_empty_loop-------------------- +// Micro-benchmark spamming. Policy is to always remove empty loops. +// The 'DO' part is to replace the trip counter with the value it will +// have on the last iteration. This will break the loop. +bool IdealLoopTree::policy_do_remove_empty_loop( PhaseIdealLoop *phase ) { + // Minimum size must be empty loop + if( _body.size() > 7/*number of nodes in an empty loop*/ ) return false; + + if( !_head->is_CountedLoop() ) return false; // Dead loop + CountedLoopNode *cl = _head->as_CountedLoop(); + if( !cl->loopexit() ) return false; // Malformed loop + if( !phase->is_member(this,phase->get_ctrl(cl->loopexit()->in(CountedLoopEndNode::TestValue)) ) ) + return false; // Infinite loop +#ifndef PRODUCT + if( PrintOpto ) + tty->print_cr("Removing empty loop"); +#endif +#ifdef ASSERT + // Ensure only one phi which is the iv. + Node* iv = NULL; + for (DUIterator_Fast imax, i = cl->fast_outs(imax); i < imax; i++) { + Node* n = cl->fast_out(i); + if (n->Opcode() == Op_Phi) { + assert(iv == NULL, "Too many phis" ); + iv = n; + } + } + assert(iv == cl->phi(), "Wrong phi" ); +#endif + // Replace the phi at loop head with the final value of the last + // iteration. Then the CountedLoopEnd will collapse (backedge never + // taken) and all loop-invariant uses of the exit values will be correct. + Node *phi = cl->phi(); + Node *final = new (phase->C, 3) SubINode( cl->limit(), cl->stride() ); + phase->register_new_node(final,cl->in(LoopNode::EntryControl)); + phase->_igvn.hash_delete(phi); + phase->_igvn.subsume_node(phi,final); + phase->C->set_major_progress(); + return true; +} + + +//============================================================================= +//------------------------------iteration_split_impl--------------------------- +void IdealLoopTree::iteration_split_impl( PhaseIdealLoop *phase, Node_List &old_new ) { + // Check and remove empty loops (spam micro-benchmarks) + if( policy_do_remove_empty_loop(phase) ) + return; // Here we removed an empty loop + + bool should_peel = policy_peeling(phase); // Should we peel? + + bool should_unswitch = policy_unswitching(phase); + + // Non-counted loops may be peeled; exactly 1 iteration is peeled. + // This removes loop-invariant tests (usually null checks). + if( !_head->is_CountedLoop() ) { // Non-counted loop + if (PartialPeelLoop && phase->partial_peel(this, old_new)) { + return; + } + if( should_peel ) { // Should we peel? +#ifndef PRODUCT + if (PrintOpto) tty->print_cr("should_peel"); +#endif + phase->do_peeling(this,old_new); + } else if( should_unswitch ) { + phase->do_unswitching(this, old_new); + } + return; + } + CountedLoopNode *cl = _head->as_CountedLoop(); + + if( !cl->loopexit() ) return; // Ignore various kinds of broken loops + + // Do nothing special to pre- and post- loops + if( cl->is_pre_loop() || cl->is_post_loop() ) return; + + // Compute loop trip count from profile data + compute_profile_trip_cnt(phase); + + // Before attempting fancy unrolling, RCE or alignment, see if we want + // to completely unroll this loop or do loop unswitching. + if( cl->is_normal_loop() ) { + bool should_maximally_unroll = policy_maximally_unroll(phase); + if( should_maximally_unroll ) { + // Here we did some unrolling and peeling. Eventually we will + // completely unroll this loop and it will no longer be a loop. + phase->do_maximally_unroll(this,old_new); + return; + } + if (should_unswitch) { + phase->do_unswitching(this, old_new); + return; + } + } + + + // Counted loops may be peeled, may need some iterations run up + // front for RCE, and may want to align loop refs to a cache + // line. Thus we clone a full loop up front whose trip count is + // at least 1 (if peeling), but may be several more. + + // The main loop will start cache-line aligned with at least 1 + // iteration of the unrolled body (zero-trip test required) and + // will have some range checks removed. + + // A post-loop will finish any odd iterations (leftover after + // unrolling), plus any needed for RCE purposes. + + bool should_unroll = policy_unroll(phase); + + bool should_rce = policy_range_check(phase); + + bool should_align = policy_align(phase); + + // If not RCE'ing (iteration splitting) or Aligning, then we do not + // need a pre-loop. We may still need to peel an initial iteration but + // we will not be needing an unknown number of pre-iterations. + // + // Basically, if may_rce_align reports FALSE first time through, + // we will not be able to later do RCE or Aligning on this loop. + bool may_rce_align = !policy_peel_only(phase) || should_rce || should_align; + + // If we have any of these conditions (RCE, alignment, unrolling) met, then + // we switch to the pre-/main-/post-loop model. This model also covers + // peeling. + if( should_rce || should_align || should_unroll ) { + if( cl->is_normal_loop() ) // Convert to 'pre/main/post' loops + phase->insert_pre_post_loops(this,old_new, !may_rce_align); + + // Adjust the pre- and main-loop limits to let the pre and post loops run + // with full checks, but the main-loop with no checks. Remove said + // checks from the main body. + if( should_rce ) + phase->do_range_check(this,old_new); + + // Double loop body for unrolling. Adjust the minimum-trip test (will do + // twice as many iterations as before) and the main body limit (only do + // an even number of trips). If we are peeling, we might enable some RCE + // and we'd rather unroll the post-RCE'd loop SO... do not unroll if + // peeling. + if( should_unroll && !should_peel ) + phase->do_unroll(this,old_new, true); + + // Adjust the pre-loop limits to align the main body + // iterations. + if( should_align ) + Unimplemented(); + + } else { // Else we have an unchanged counted loop + if( should_peel ) // Might want to peel but do nothing else + phase->do_peeling(this,old_new); + } +} + + +//============================================================================= +//------------------------------iteration_split-------------------------------- +void IdealLoopTree::iteration_split( PhaseIdealLoop *phase, Node_List &old_new ) { + // Recursively iteration split nested loops + if( _child ) _child->iteration_split( phase, old_new ); + + // Clean out prior deadwood + DCE_loop_body(); + + + // Look for loop-exit tests with my 50/50 guesses from the Parsing stage. + // Replace with a 1-in-10 exit guess. + if( _parent /*not the root loop*/ && + !_irreducible && + // Also ignore the occasional dead backedge + !tail()->is_top() ) { + adjust_loop_exit_prob(phase); + } + + + // Gate unrolling, RCE and peeling efforts. + if( !_child && // If not an inner loop, do not split + !_irreducible && + !tail()->is_top() ) { // Also ignore the occasional dead backedge + if (!_has_call) { + iteration_split_impl( phase, old_new ); + } else if (policy_unswitching(phase)) { + phase->do_unswitching(this, old_new); + } + } + + // Minor offset re-organization to remove loop-fallout uses of + // trip counter. + if( _head->is_CountedLoop() ) phase->reorg_offsets( this ); + if( _next ) _next->iteration_split( phase, old_new ); +} diff --git a/hotspot/src/share/vm/opto/loopUnswitch.cpp b/hotspot/src/share/vm/opto/loopUnswitch.cpp new file mode 100644 index 00000000000..fcba517e8b2 --- /dev/null +++ b/hotspot/src/share/vm/opto/loopUnswitch.cpp @@ -0,0 +1,237 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_loopUnswitch.cpp.incl" + +//================= Loop Unswitching ===================== +// +// orig: transformed: +// if (invariant-test) then +// loop loop +// stmt1 stmt1 +// if (invariant-test) then stmt2 +// stmt2 stmt4 +// else endloop +// stmt3 else +// endif loop [clone] +// stmt4 stmt1 [clone] +// endloop stmt3 +// stmt4 [clone] +// endloop +// endif +// +// Note: the "else" clause may be empty + +//------------------------------policy_unswitching----------------------------- +// Return TRUE or FALSE if the loop should be unswitched +// (ie. clone loop with an invariant test that does not exit the loop) +bool IdealLoopTree::policy_unswitching( PhaseIdealLoop *phase ) const { + if( !LoopUnswitching ) { + return false; + } + uint nodes_left = MaxNodeLimit - phase->C->unique(); + if (2 * _body.size() > nodes_left) { + return false; // Too speculative if running low on nodes. + } + LoopNode* head = _head->as_Loop(); + if (head->unswitch_count() + 1 > head->unswitch_max()) { + return false; + } + return phase->find_unswitching_candidate(this) != NULL; +} + +//------------------------------find_unswitching_candidate----------------------------- +// Find candidate "if" for unswitching +IfNode* PhaseIdealLoop::find_unswitching_candidate(const IdealLoopTree *loop) const { + + // Find first invariant test that doesn't exit the loop + LoopNode *head = loop->_head->as_Loop(); + IfNode* unswitch_iff = NULL; + Node* n = head->in(LoopNode::LoopBackControl); + while (n != head) { + Node* n_dom = idom(n); + if (n->is_Region()) { + if (n_dom->is_If()) { + IfNode* iff = n_dom->as_If(); + if (iff->in(1)->is_Bool()) { + BoolNode* bol = iff->in(1)->as_Bool(); + if (bol->in(1)->is_Cmp()) { + // If condition is invariant and not a loop exit, + // then found reason to unswitch. + if (loop->is_invariant(bol) && !loop->is_loop_exit(iff)) { + unswitch_iff = iff; + } + } + } + } + } + n = n_dom; + } + return unswitch_iff; +} + +//------------------------------do_unswitching----------------------------- +// Clone loop with an invariant test (that does not exit) and +// insert a clone of the test that selects which version to +// execute. +void PhaseIdealLoop::do_unswitching (IdealLoopTree *loop, Node_List &old_new) { + + // Find first invariant test that doesn't exit the loop + LoopNode *head = loop->_head->as_Loop(); + + IfNode* unswitch_iff = find_unswitching_candidate((const IdealLoopTree *)loop); + assert(unswitch_iff != NULL, "should be at least one"); + + // Need to revert back to normal loop + if (head->is_CountedLoop() && !head->as_CountedLoop()->is_normal_loop()) { + head->as_CountedLoop()->set_normal_loop(); + } + + ProjNode* proj_true = create_slow_version_of_loop(loop, old_new); + + assert(proj_true->is_IfTrue() && proj_true->unique_ctrl_out() == head, "by construction"); + + // Increment unswitch count + LoopNode* head_clone = old_new[head->_idx]->as_Loop(); + int nct = head->unswitch_count() + 1; + head->set_unswitch_count(nct); + head_clone->set_unswitch_count(nct); + + // Add test to new "if" outside of loop + IfNode* invar_iff = proj_true->in(0)->as_If(); + Node* invar_iff_c = invar_iff->in(0); + BoolNode* bol = unswitch_iff->in(1)->as_Bool(); + invar_iff->set_req(1, bol); + invar_iff->_prob = unswitch_iff->_prob; + + ProjNode* proj_false = invar_iff->proj_out(0)->as_Proj(); + + // Hoist invariant casts out of each loop to the appropiate + // control projection. + + Node_List worklist; + + for (DUIterator_Fast imax, i = unswitch_iff->fast_outs(imax); i < imax; i++) { + ProjNode* proj= unswitch_iff->fast_out(i)->as_Proj(); + // Copy to a worklist for easier manipulation + for (DUIterator_Fast jmax, j = proj->fast_outs(jmax); j < jmax; j++) { + Node* use = proj->fast_out(j); + if (use->Opcode() == Op_CheckCastPP && loop->is_invariant(use->in(1))) { + worklist.push(use); + } + } + ProjNode* invar_proj = invar_iff->proj_out(proj->_con)->as_Proj(); + while (worklist.size() > 0) { + Node* use = worklist.pop(); + Node* nuse = use->clone(); + nuse->set_req(0, invar_proj); + _igvn.hash_delete(use); + use->set_req(1, nuse); + _igvn._worklist.push(use); + register_new_node(nuse, invar_proj); + // Same for the clone + Node* use_clone = old_new[use->_idx]; + _igvn.hash_delete(use_clone); + use_clone->set_req(1, nuse); + _igvn._worklist.push(use_clone); + } + } + + // Hardwire the control paths in the loops into if(true) and if(false) + _igvn.hash_delete(unswitch_iff); + short_circuit_if(unswitch_iff, proj_true); + _igvn._worklist.push(unswitch_iff); + + IfNode* unswitch_iff_clone = old_new[unswitch_iff->_idx]->as_If(); + _igvn.hash_delete(unswitch_iff_clone); + short_circuit_if(unswitch_iff_clone, proj_false); + _igvn._worklist.push(unswitch_iff_clone); + + // Reoptimize loops + loop->record_for_igvn(); + for(int i = loop->_body.size() - 1; i >= 0 ; i--) { + Node *n = loop->_body[i]; + Node *n_clone = old_new[n->_idx]; + _igvn._worklist.push(n_clone); + } + +#ifndef PRODUCT + if (TraceLoopUnswitching) { + tty->print_cr("Loop unswitching orig: %d @ %d new: %d @ %d", + head->_idx, unswitch_iff->_idx, + old_new[head->_idx]->_idx, unswitch_iff_clone->_idx); + } +#endif + + C->set_major_progress(); +} + +//-------------------------create_slow_version_of_loop------------------------ +// Create a slow version of the loop by cloning the loop +// and inserting an if to select fast-slow versions. +// Return control projection of the entry to the fast version. +ProjNode* PhaseIdealLoop::create_slow_version_of_loop(IdealLoopTree *loop, + Node_List &old_new) { + LoopNode* head = loop->_head->as_Loop(); + Node* entry = head->in(LoopNode::EntryControl); + _igvn.hash_delete(entry); + _igvn._worklist.push(entry); + IdealLoopTree* outer_loop = loop->_parent; + + Node *cont = _igvn.intcon(1); + set_ctrl(cont, C->root()); + Node* opq = new (C, 2) Opaque1Node(cont); + register_node(opq, outer_loop, entry, dom_depth(entry)); + Node *bol = new (C, 2) Conv2BNode(opq); + register_node(bol, outer_loop, entry, dom_depth(entry)); + IfNode* iff = new (C, 2) IfNode(entry, bol, PROB_MAX, COUNT_UNKNOWN); + register_node(iff, outer_loop, entry, dom_depth(entry)); + ProjNode* iffast = new (C, 1) IfTrueNode(iff); + register_node(iffast, outer_loop, iff, dom_depth(iff)); + ProjNode* ifslow = new (C, 1) IfFalseNode(iff); + register_node(ifslow, outer_loop, iff, dom_depth(iff)); + + // Clone the loop body. The clone becomes the fast loop. The + // original pre-header will (illegally) have 2 control users (old & new loops). + clone_loop(loop, old_new, dom_depth(head), iff); + assert(old_new[head->_idx]->is_Loop(), "" ); + + // Fast (true) control + _igvn.hash_delete(head); + head->set_req(LoopNode::EntryControl, iffast); + set_idom(head, iffast, dom_depth(head)); + _igvn._worklist.push(head); + + // Slow (false) control + LoopNode* slow_head = old_new[head->_idx]->as_Loop(); + _igvn.hash_delete(slow_head); + slow_head->set_req(LoopNode::EntryControl, ifslow); + set_idom(slow_head, ifslow, dom_depth(slow_head)); + _igvn._worklist.push(slow_head); + + recompute_dom_depth(); + + return iffast; +} diff --git a/hotspot/src/share/vm/opto/loopnode.cpp b/hotspot/src/share/vm/opto/loopnode.cpp new file mode 100644 index 00000000000..938862f0fa4 --- /dev/null +++ b/hotspot/src/share/vm/opto/loopnode.cpp @@ -0,0 +1,2886 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_loopnode.cpp.incl" + +//============================================================================= +//------------------------------is_loop_iv------------------------------------- +// Determine if a node is Counted loop induction variable. +// The method is declared in node.hpp. +const Node* Node::is_loop_iv() const { + if (this->is_Phi() && !this->as_Phi()->is_copy() && + this->as_Phi()->region()->is_CountedLoop() && + this->as_Phi()->region()->as_CountedLoop()->phi() == this) { + return this; + } else { + return NULL; + } +} + +//============================================================================= +//------------------------------dump_spec-------------------------------------- +// Dump special per-node info +#ifndef PRODUCT +void LoopNode::dump_spec(outputStream *st) const { + if( is_inner_loop () ) st->print( "inner " ); + if( is_partial_peel_loop () ) st->print( "partial_peel " ); + if( partial_peel_has_failed () ) st->print( "partial_peel_failed " ); +} +#endif + +//------------------------------get_early_ctrl--------------------------------- +// Compute earliest legal control +Node *PhaseIdealLoop::get_early_ctrl( Node *n ) { + assert( !n->is_Phi() && !n->is_CFG(), "this code only handles data nodes" ); + uint i; + Node *early; + if( n->in(0) ) { + early = n->in(0); + if( !early->is_CFG() ) // Might be a non-CFG multi-def + early = get_ctrl(early); // So treat input as a straight data input + i = 1; + } else { + early = get_ctrl(n->in(1)); + i = 2; + } + uint e_d = dom_depth(early); + assert( early, "" ); + for( ; i < n->req(); i++ ) { + Node *cin = get_ctrl(n->in(i)); + assert( cin, "" ); + // Keep deepest dominator depth + uint c_d = dom_depth(cin); + if( c_d > e_d ) { // Deeper guy? + early = cin; // Keep deepest found so far + e_d = c_d; + } else if( c_d == e_d && // Same depth? + early != cin ) { // If not equal, must use slower algorithm + // If same depth but not equal, one _must_ dominate the other + // and we want the deeper (i.e., dominated) guy. + Node *n1 = early; + Node *n2 = cin; + while( 1 ) { + n1 = idom(n1); // Walk up until break cycle + n2 = idom(n2); + if( n1 == cin || // Walked early up to cin + dom_depth(n2) < c_d ) + break; // early is deeper; keep him + if( n2 == early || // Walked cin up to early + dom_depth(n1) < c_d ) { + early = cin; // cin is deeper; keep him + break; + } + } + e_d = dom_depth(early); // Reset depth register cache + } + } + + // Return earliest legal location + assert(early == find_non_split_ctrl(early), "unexpected early control"); + + return early; +} + +//------------------------------set_early_ctrl--------------------------------- +// Set earliest legal control +void PhaseIdealLoop::set_early_ctrl( Node *n ) { + Node *early = get_early_ctrl(n); + + // Record earliest legal location + set_ctrl(n, early); +} + +//------------------------------set_subtree_ctrl------------------------------- +// set missing _ctrl entries on new nodes +void PhaseIdealLoop::set_subtree_ctrl( Node *n ) { + // Already set? Get out. + if( _nodes[n->_idx] ) return; + // Recursively set _nodes array to indicate where the Node goes + uint i; + for( i = 0; i < n->req(); ++i ) { + Node *m = n->in(i); + if( m && m != C->root() ) + set_subtree_ctrl( m ); + } + + // Fixup self + set_early_ctrl( n ); +} + +//------------------------------is_counted_loop-------------------------------- +Node *PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { + PhaseGVN *gvn = &_igvn; + + // Counted loop head must be a good RegionNode with only 3 not NULL + // control input edges: Self, Entry, LoopBack. + if ( x->in(LoopNode::Self) == NULL || x->req() != 3 ) + return NULL; + + Node *init_control = x->in(LoopNode::EntryControl); + Node *back_control = x->in(LoopNode::LoopBackControl); + if( init_control == NULL || back_control == NULL ) // Partially dead + return NULL; + // Must also check for TOP when looking for a dead loop + if( init_control->is_top() || back_control->is_top() ) + return NULL; + + // Allow funny placement of Safepoint + if( back_control->Opcode() == Op_SafePoint ) + back_control = back_control->in(TypeFunc::Control); + + // Controlling test for loop + Node *iftrue = back_control; + uint iftrue_op = iftrue->Opcode(); + if( iftrue_op != Op_IfTrue && + iftrue_op != Op_IfFalse ) + // I have a weird back-control. Probably the loop-exit test is in + // the middle of the loop and I am looking at some trailing control-flow + // merge point. To fix this I would have to partially peel the loop. + return NULL; // Obscure back-control + + // Get boolean guarding loop-back test + Node *iff = iftrue->in(0); + if( get_loop(iff) != loop || !iff->in(1)->is_Bool() ) return NULL; + BoolNode *test = iff->in(1)->as_Bool(); + BoolTest::mask bt = test->_test._test; + float cl_prob = iff->as_If()->_prob; + if( iftrue_op == Op_IfFalse ) { + bt = BoolTest(bt).negate(); + cl_prob = 1.0 - cl_prob; + } + // Get backedge compare + Node *cmp = test->in(1); + int cmp_op = cmp->Opcode(); + if( cmp_op != Op_CmpI ) + return NULL; // Avoid pointer & float compares + + // Find the trip-counter increment & limit. Limit must be loop invariant. + Node *incr = cmp->in(1); + Node *limit = cmp->in(2); + + // --------- + // need 'loop()' test to tell if limit is loop invariant + // --------- + + if( !is_member( loop, get_ctrl(incr) ) ) { // Swapped trip counter and limit? + Node *tmp = incr; // Then reverse order into the CmpI + incr = limit; + limit = tmp; + bt = BoolTest(bt).commute(); // And commute the exit test + } + if( is_member( loop, get_ctrl(limit) ) ) // Limit must loop-invariant + return NULL; + + // Trip-counter increment must be commutative & associative. + uint incr_op = incr->Opcode(); + if( incr_op == Op_Phi && incr->req() == 3 ) { + incr = incr->in(2); // Assume incr is on backedge of Phi + incr_op = incr->Opcode(); + } + Node* trunc1 = NULL; + Node* trunc2 = NULL; + const TypeInt* iv_trunc_t = NULL; + if (!(incr = CountedLoopNode::match_incr_with_optional_truncation(incr, &trunc1, &trunc2, &iv_trunc_t))) { + return NULL; // Funny increment opcode + } + + // Get merge point + Node *xphi = incr->in(1); + Node *stride = incr->in(2); + if( !stride->is_Con() ) { // Oops, swap these + if( !xphi->is_Con() ) // Is the other guy a constant? + return NULL; // Nope, unknown stride, bail out + Node *tmp = xphi; // 'incr' is commutative, so ok to swap + xphi = stride; + stride = tmp; + } + //if( loop(xphi) != l) return NULL;// Merge point is in inner loop?? + if( !xphi->is_Phi() ) return NULL; // Too much math on the trip counter + PhiNode *phi = xphi->as_Phi(); + + // Stride must be constant + const Type *stride_t = stride->bottom_type(); + int stride_con = stride_t->is_int()->get_con(); + assert( stride_con, "missed some peephole opt" ); + + // Phi must be of loop header; backedge must wrap to increment + if( phi->region() != x ) return NULL; + if( trunc1 == NULL && phi->in(LoopNode::LoopBackControl) != incr || + trunc1 != NULL && phi->in(LoopNode::LoopBackControl) != trunc1 ) { + return NULL; + } + Node *init_trip = phi->in(LoopNode::EntryControl); + //if (!init_trip->is_Con()) return NULL; // avoid rolling over MAXINT/MININT + + // If iv trunc type is smaller than int, check for possible wrap. + if (!TypeInt::INT->higher_equal(iv_trunc_t)) { + assert(trunc1 != NULL, "must have found some truncation"); + + // Get a better type for the phi (filtered thru if's) + const TypeInt* phi_ft = filtered_type(phi); + + // Can iv take on a value that will wrap? + // + // Ensure iv's limit is not within "stride" of the wrap value. + // + // Example for "short" type + // Truncation ensures value is in the range -32768..32767 (iv_trunc_t) + // If the stride is +10, then the last value of the induction + // variable before the increment (phi_ft->_hi) must be + // <= 32767 - 10 and (phi_ft->_lo) must be >= -32768 to + // ensure no truncation occurs after the increment. + + if (stride_con > 0) { + if (iv_trunc_t->_hi - phi_ft->_hi < stride_con || + iv_trunc_t->_lo > phi_ft->_lo) { + return NULL; // truncation may occur + } + } else if (stride_con < 0) { + if (iv_trunc_t->_lo - phi_ft->_lo > stride_con || + iv_trunc_t->_hi < phi_ft->_hi) { + return NULL; // truncation may occur + } + } + // No possibility of wrap so truncation can be discarded + // Promote iv type to Int + } else { + assert(trunc1 == NULL && trunc2 == NULL, "no truncation for int"); + } + + // ================================================= + // ---- SUCCESS! Found A Trip-Counted Loop! ----- + // + // Canonicalize the condition on the test. If we can exactly determine + // the trip-counter exit value, then set limit to that value and use + // a '!=' test. Otherwise use conditon '<' for count-up loops and + // '>' for count-down loops. If the condition is inverted and we will + // be rolling through MININT to MAXINT, then bail out. + + C->print_method("Before CountedLoop", 3); + + // Check for SafePoint on backedge and remove + Node *sfpt = x->in(LoopNode::LoopBackControl); + if( sfpt->Opcode() == Op_SafePoint && is_deleteable_safept(sfpt)) { + lazy_replace( sfpt, iftrue ); + loop->_tail = iftrue; + } + + + // If compare points to incr, we are ok. Otherwise the compare + // can directly point to the phi; in this case adjust the compare so that + // it points to the incr by adusting the limit. + if( cmp->in(1) == phi || cmp->in(2) == phi ) + limit = gvn->transform(new (C, 3) AddINode(limit,stride)); + + // trip-count for +-tive stride should be: (limit - init_trip + stride - 1)/stride. + // Final value for iterator should be: trip_count * stride + init_trip. + const Type *limit_t = limit->bottom_type(); + const Type *init_t = init_trip->bottom_type(); + Node *one_p = gvn->intcon( 1); + Node *one_m = gvn->intcon(-1); + + Node *trip_count = NULL; + Node *hook = new (C, 6) Node(6); + switch( bt ) { + case BoolTest::eq: + return NULL; // Bail out, but this loop trips at most twice! + case BoolTest::ne: // Ahh, the case we desire + if( stride_con == 1 ) + trip_count = gvn->transform(new (C, 3) SubINode(limit,init_trip)); + else if( stride_con == -1 ) + trip_count = gvn->transform(new (C, 3) SubINode(init_trip,limit)); + else + return NULL; // Odd stride; must prove we hit limit exactly + set_subtree_ctrl( trip_count ); + //_loop.map(trip_count->_idx,loop(limit)); + break; + case BoolTest::le: // Maybe convert to '<' case + limit = gvn->transform(new (C, 3) AddINode(limit,one_p)); + set_subtree_ctrl( limit ); + hook->init_req(4, limit); + + bt = BoolTest::lt; + // Make the new limit be in the same loop nest as the old limit + //_loop.map(limit->_idx,limit_loop); + // Fall into next case + case BoolTest::lt: { // Maybe convert to '!=' case + if( stride_con < 0 ) return NULL; // Count down loop rolls through MAXINT + Node *range = gvn->transform(new (C, 3) SubINode(limit,init_trip)); + set_subtree_ctrl( range ); + hook->init_req(0, range); + + Node *bias = gvn->transform(new (C, 3) AddINode(range,stride)); + set_subtree_ctrl( bias ); + hook->init_req(1, bias); + + Node *bias1 = gvn->transform(new (C, 3) AddINode(bias,one_m)); + set_subtree_ctrl( bias1 ); + hook->init_req(2, bias1); + + trip_count = gvn->transform(new (C, 3) DivINode(0,bias1,stride)); + set_subtree_ctrl( trip_count ); + hook->init_req(3, trip_count); + break; + } + + case BoolTest::ge: // Maybe convert to '>' case + limit = gvn->transform(new (C, 3) AddINode(limit,one_m)); + set_subtree_ctrl( limit ); + hook->init_req(4 ,limit); + + bt = BoolTest::gt; + // Make the new limit be in the same loop nest as the old limit + //_loop.map(limit->_idx,limit_loop); + // Fall into next case + case BoolTest::gt: { // Maybe convert to '!=' case + if( stride_con > 0 ) return NULL; // count up loop rolls through MININT + Node *range = gvn->transform(new (C, 3) SubINode(limit,init_trip)); + set_subtree_ctrl( range ); + hook->init_req(0, range); + + Node *bias = gvn->transform(new (C, 3) AddINode(range,stride)); + set_subtree_ctrl( bias ); + hook->init_req(1, bias); + + Node *bias1 = gvn->transform(new (C, 3) AddINode(bias,one_p)); + set_subtree_ctrl( bias1 ); + hook->init_req(2, bias1); + + trip_count = gvn->transform(new (C, 3) DivINode(0,bias1,stride)); + set_subtree_ctrl( trip_count ); + hook->init_req(3, trip_count); + break; + } + } + + Node *span = gvn->transform(new (C, 3) MulINode(trip_count,stride)); + set_subtree_ctrl( span ); + hook->init_req(5, span); + + limit = gvn->transform(new (C, 3) AddINode(span,init_trip)); + set_subtree_ctrl( limit ); + + // Build a canonical trip test. + // Clone code, as old values may be in use. + incr = incr->clone(); + incr->set_req(1,phi); + incr->set_req(2,stride); + incr = _igvn.register_new_node_with_optimizer(incr); + set_early_ctrl( incr ); + _igvn.hash_delete(phi); + phi->set_req_X( LoopNode::LoopBackControl, incr, &_igvn ); + + // If phi type is more restrictive than Int, raise to + // Int to prevent (almost) infinite recursion in igvn + // which can only handle integer types for constants or minint..maxint. + if (!TypeInt::INT->higher_equal(phi->bottom_type())) { + Node* nphi = PhiNode::make(phi->in(0), phi->in(LoopNode::EntryControl), TypeInt::INT); + nphi->set_req(LoopNode::LoopBackControl, phi->in(LoopNode::LoopBackControl)); + nphi = _igvn.register_new_node_with_optimizer(nphi); + set_ctrl(nphi, get_ctrl(phi)); + _igvn.subsume_node(phi, nphi); + phi = nphi->as_Phi(); + } + cmp = cmp->clone(); + cmp->set_req(1,incr); + cmp->set_req(2,limit); + cmp = _igvn.register_new_node_with_optimizer(cmp); + set_ctrl(cmp, iff->in(0)); + + Node *tmp = test->clone(); + assert( tmp->is_Bool(), "" ); + test = (BoolNode*)tmp; + (*(BoolTest*)&test->_test)._test = bt; //BoolTest::ne; + test->set_req(1,cmp); + _igvn.register_new_node_with_optimizer(test); + set_ctrl(test, iff->in(0)); + // If the exit test is dead, STOP! + if( test == NULL ) return NULL; + _igvn.hash_delete(iff); + iff->set_req_X( 1, test, &_igvn ); + + // Replace the old IfNode with a new LoopEndNode + Node *lex = _igvn.register_new_node_with_optimizer(new (C, 2) CountedLoopEndNode( iff->in(0), iff->in(1), cl_prob, iff->as_If()->_fcnt )); + IfNode *le = lex->as_If(); + uint dd = dom_depth(iff); + set_idom(le, le->in(0), dd); // Update dominance for loop exit + set_loop(le, loop); + + // Get the loop-exit control + Node *if_f = iff->as_If()->proj_out(!(iftrue_op == Op_IfTrue)); + + // Need to swap loop-exit and loop-back control? + if( iftrue_op == Op_IfFalse ) { + Node *ift2=_igvn.register_new_node_with_optimizer(new (C, 1) IfTrueNode (le)); + Node *iff2=_igvn.register_new_node_with_optimizer(new (C, 1) IfFalseNode(le)); + + loop->_tail = back_control = ift2; + set_loop(ift2, loop); + set_loop(iff2, get_loop(if_f)); + + // Lazy update of 'get_ctrl' mechanism. + lazy_replace_proj( if_f , iff2 ); + lazy_replace_proj( iftrue, ift2 ); + + // Swap names + if_f = iff2; + iftrue = ift2; + } else { + _igvn.hash_delete(if_f ); + _igvn.hash_delete(iftrue); + if_f ->set_req_X( 0, le, &_igvn ); + iftrue->set_req_X( 0, le, &_igvn ); + } + + set_idom(iftrue, le, dd+1); + set_idom(if_f, le, dd+1); + + // Now setup a new CountedLoopNode to replace the existing LoopNode + CountedLoopNode *l = new (C, 3) CountedLoopNode(init_control, back_control); + // The following assert is approximately true, and defines the intention + // of can_be_counted_loop. It fails, however, because phase->type + // is not yet initialized for this loop and its parts. + //assert(l->can_be_counted_loop(this), "sanity"); + _igvn.register_new_node_with_optimizer(l); + set_loop(l, loop); + loop->_head = l; + // Fix all data nodes placed at the old loop head. + // Uses the lazy-update mechanism of 'get_ctrl'. + lazy_replace( x, l ); + set_idom(l, init_control, dom_depth(x)); + + // Check for immediately preceeding SafePoint and remove + Node *sfpt2 = le->in(0); + if( sfpt2->Opcode() == Op_SafePoint && is_deleteable_safept(sfpt2)) + lazy_replace( sfpt2, sfpt2->in(TypeFunc::Control)); + + // Free up intermediate goo + _igvn.remove_dead_node(hook); + + C->print_method("After CountedLoop", 3); + + // Return trip counter + return trip_count; +} + + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. +// Attempt to convert into a counted-loop. +Node *LoopNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if (!can_be_counted_loop(phase)) { + phase->C->set_major_progress(); + } + return RegionNode::Ideal(phase, can_reshape); +} + + +//============================================================================= +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. +// Attempt to convert into a counted-loop. +Node *CountedLoopNode::Ideal(PhaseGVN *phase, bool can_reshape) { + return RegionNode::Ideal(phase, can_reshape); +} + +//------------------------------dump_spec-------------------------------------- +// Dump special per-node info +#ifndef PRODUCT +void CountedLoopNode::dump_spec(outputStream *st) const { + LoopNode::dump_spec(st); + if( stride_is_con() ) { + st->print("stride: %d ",stride_con()); + } else { + st->print("stride: not constant "); + } + if( is_pre_loop () ) st->print("pre of N%d" , _main_idx ); + if( is_main_loop() ) st->print("main of N%d", _idx ); + if( is_post_loop() ) st->print("post of N%d", _main_idx ); +} +#endif + +//============================================================================= +int CountedLoopEndNode::stride_con() const { + return stride()->bottom_type()->is_int()->get_con(); +} + + +//----------------------match_incr_with_optional_truncation-------------------- +// Match increment with optional truncation: +// CHAR: (i+1)&0x7fff, BYTE: ((i+1)<<8)>>8, or SHORT: ((i+1)<<16)>>16 +// Return NULL for failure. Success returns the increment node. +Node* CountedLoopNode::match_incr_with_optional_truncation( + Node* expr, Node** trunc1, Node** trunc2, const TypeInt** trunc_type) { + // Quick cutouts: + if (expr == NULL || expr->req() != 3) return false; + + Node *t1 = NULL; + Node *t2 = NULL; + const TypeInt* trunc_t = TypeInt::INT; + Node* n1 = expr; + int n1op = n1->Opcode(); + + // Try to strip (n1 & M) or (n1 << N >> N) from n1. + if (n1op == Op_AndI && + n1->in(2)->is_Con() && + n1->in(2)->bottom_type()->is_int()->get_con() == 0x7fff) { + // %%% This check should match any mask of 2**K-1. + t1 = n1; + n1 = t1->in(1); + n1op = n1->Opcode(); + trunc_t = TypeInt::CHAR; + } else if (n1op == Op_RShiftI && + n1->in(1) != NULL && + n1->in(1)->Opcode() == Op_LShiftI && + n1->in(2) == n1->in(1)->in(2) && + n1->in(2)->is_Con()) { + jint shift = n1->in(2)->bottom_type()->is_int()->get_con(); + // %%% This check should match any shift in [1..31]. + if (shift == 16 || shift == 8) { + t1 = n1; + t2 = t1->in(1); + n1 = t2->in(1); + n1op = n1->Opcode(); + if (shift == 16) { + trunc_t = TypeInt::SHORT; + } else if (shift == 8) { + trunc_t = TypeInt::BYTE; + } + } + } + + // If (maybe after stripping) it is an AddI, we won: + if (n1op == Op_AddI) { + *trunc1 = t1; + *trunc2 = t2; + *trunc_type = trunc_t; + return n1; + } + + // failed + return NULL; +} + + +//------------------------------filtered_type-------------------------------- +// Return a type based on condition control flow +// A successful return will be a type that is restricted due +// to a series of dominating if-tests, such as: +// if (i < 10) { +// if (i > 0) { +// here: "i" type is [1..10) +// } +// } +// or a control flow merge +// if (i < 10) { +// do { +// phi( , ) -- at top of loop type is [min_int..10) +// i = ? +// } while ( i < 10) +// +const TypeInt* PhaseIdealLoop::filtered_type( Node *n, Node* n_ctrl) { + assert(n && n->bottom_type()->is_int(), "must be int"); + const TypeInt* filtered_t = NULL; + if (!n->is_Phi()) { + assert(n_ctrl != NULL || n_ctrl == C->top(), "valid control"); + filtered_t = filtered_type_from_dominators(n, n_ctrl); + + } else { + Node* phi = n->as_Phi(); + Node* region = phi->in(0); + assert(n_ctrl == NULL || n_ctrl == region, "ctrl parameter must be region"); + if (region && region != C->top()) { + for (uint i = 1; i < phi->req(); i++) { + Node* val = phi->in(i); + Node* use_c = region->in(i); + const TypeInt* val_t = filtered_type_from_dominators(val, use_c); + if (val_t != NULL) { + if (filtered_t == NULL) { + filtered_t = val_t; + } else { + filtered_t = filtered_t->meet(val_t)->is_int(); + } + } + } + } + } + const TypeInt* n_t = _igvn.type(n)->is_int(); + if (filtered_t != NULL) { + n_t = n_t->join(filtered_t)->is_int(); + } + return n_t; +} + + +//------------------------------filtered_type_from_dominators-------------------------------- +// Return a possibly more restrictive type for val based on condition control flow of dominators +const TypeInt* PhaseIdealLoop::filtered_type_from_dominators( Node* val, Node *use_ctrl) { + if (val->is_Con()) { + return val->bottom_type()->is_int(); + } + uint if_limit = 10; // Max number of dominating if's visited + const TypeInt* rtn_t = NULL; + + if (use_ctrl && use_ctrl != C->top()) { + Node* val_ctrl = get_ctrl(val); + uint val_dom_depth = dom_depth(val_ctrl); + Node* pred = use_ctrl; + uint if_cnt = 0; + while (if_cnt < if_limit) { + if ((pred->Opcode() == Op_IfTrue || pred->Opcode() == Op_IfFalse)) { + if_cnt++; + const TypeInt* if_t = filtered_type_at_if(val, pred); + if (if_t != NULL) { + if (rtn_t == NULL) { + rtn_t = if_t; + } else { + rtn_t = rtn_t->join(if_t)->is_int(); + } + } + } + pred = idom(pred); + if (pred == NULL || pred == C->top()) { + break; + } + // Stop if going beyond definition block of val + if (dom_depth(pred) < val_dom_depth) { + break; + } + } + } + return rtn_t; +} + + +//------------------------------filtered_type_at_if-------------------------------- +// Return a possibly more restrictive type for val based on condition control flow for an if +const TypeInt* PhaseIdealLoop::filtered_type_at_if( Node* val, Node *if_proj) { + assert(if_proj && + (if_proj->Opcode() == Op_IfTrue || if_proj->Opcode() == Op_IfFalse), "expecting an if projection"); + if (if_proj->in(0) && if_proj->in(0)->is_If()) { + IfNode* iff = if_proj->in(0)->as_If(); + if (iff->in(1) && iff->in(1)->is_Bool()) { + BoolNode* bol = iff->in(1)->as_Bool(); + if (bol->in(1) && bol->in(1)->is_Cmp()) { + const CmpNode* cmp = bol->in(1)->as_Cmp(); + if (cmp->in(1) == val) { + const TypeInt* cmp2_t = _igvn.type(cmp->in(2))->isa_int(); + if (cmp2_t != NULL) { + jint lo = cmp2_t->_lo; + jint hi = cmp2_t->_hi; + BoolTest::mask msk = if_proj->Opcode() == Op_IfTrue ? bol->_test._test : bol->_test.negate(); + switch (msk) { + case BoolTest::ne: + // Can't refine type + return NULL; + case BoolTest::eq: + return cmp2_t; + case BoolTest::lt: + lo = TypeInt::INT->_lo; + if (hi - 1 < hi) { + hi = hi - 1; + } + break; + case BoolTest::le: + lo = TypeInt::INT->_lo; + break; + case BoolTest::gt: + if (lo + 1 > lo) { + lo = lo + 1; + } + hi = TypeInt::INT->_hi; + break; + case BoolTest::ge: + // lo unchanged + hi = TypeInt::INT->_hi; + break; + } + const TypeInt* rtn_t = TypeInt::make(lo, hi, cmp2_t->_widen); + return rtn_t; + } + } + } + } + } + return NULL; +} + +//------------------------------dump_spec-------------------------------------- +// Dump special per-node info +#ifndef PRODUCT +void CountedLoopEndNode::dump_spec(outputStream *st) const { + if( in(TestValue)->is_Bool() ) { + BoolTest bt( test_trip()); // Added this for g++. + + st->print("["); + bt.dump_on(st); + st->print("]"); + } + st->print(" "); + IfNode::dump_spec(st); +} +#endif + +//============================================================================= +//------------------------------is_member-------------------------------------- +// Is 'l' a member of 'this'? +int IdealLoopTree::is_member( const IdealLoopTree *l ) const { + while( l->_nest > _nest ) l = l->_parent; + return l == this; +} + +//------------------------------set_nest--------------------------------------- +// Set loop tree nesting depth. Accumulate _has_call bits. +int IdealLoopTree::set_nest( uint depth ) { + _nest = depth; + int bits = _has_call; + if( _child ) bits |= _child->set_nest(depth+1); + if( bits ) _has_call = 1; + if( _next ) bits |= _next ->set_nest(depth ); + return bits; +} + +//------------------------------split_fall_in---------------------------------- +// Split out multiple fall-in edges from the loop header. Move them to a +// private RegionNode before the loop. This becomes the loop landing pad. +void IdealLoopTree::split_fall_in( PhaseIdealLoop *phase, int fall_in_cnt ) { + PhaseIterGVN &igvn = phase->_igvn; + uint i; + + // Make a new RegionNode to be the landing pad. + Node *landing_pad = new (phase->C, fall_in_cnt+1) RegionNode( fall_in_cnt+1 ); + phase->set_loop(landing_pad,_parent); + // Gather all the fall-in control paths into the landing pad + uint icnt = fall_in_cnt; + uint oreq = _head->req(); + for( i = oreq-1; i>0; i-- ) + if( !phase->is_member( this, _head->in(i) ) ) + landing_pad->set_req(icnt--,_head->in(i)); + + // Peel off PhiNode edges as well + for (DUIterator_Fast jmax, j = _head->fast_outs(jmax); j < jmax; j++) { + Node *oj = _head->fast_out(j); + if( oj->is_Phi() ) { + PhiNode* old_phi = oj->as_Phi(); + assert( old_phi->region() == _head, "" ); + igvn.hash_delete(old_phi); // Yank from hash before hacking edges + Node *p = PhiNode::make_blank(landing_pad, old_phi); + uint icnt = fall_in_cnt; + for( i = oreq-1; i>0; i-- ) { + if( !phase->is_member( this, _head->in(i) ) ) { + p->init_req(icnt--, old_phi->in(i)); + // Go ahead and clean out old edges from old phi + old_phi->del_req(i); + } + } + // Search for CSE's here, because ZKM.jar does a lot of + // loop hackery and we need to be a little incremental + // with the CSE to avoid O(N^2) node blow-up. + Node *p2 = igvn.hash_find_insert(p); // Look for a CSE + if( p2 ) { // Found CSE + p->destruct(); // Recover useless new node + p = p2; // Use old node + } else { + igvn.register_new_node_with_optimizer(p, old_phi); + } + // Make old Phi refer to new Phi. + old_phi->add_req(p); + // Check for the special case of making the old phi useless and + // disappear it. In JavaGrande I have a case where this useless + // Phi is the loop limit and prevents recognizing a CountedLoop + // which in turn prevents removing an empty loop. + Node *id_old_phi = old_phi->Identity( &igvn ); + if( id_old_phi != old_phi ) { // Found a simple identity? + // Note that I cannot call 'subsume_node' here, because + // that will yank the edge from old_phi to the Region and + // I'm mid-iteration over the Region's uses. + for (DUIterator_Last imin, i = old_phi->last_outs(imin); i >= imin; ) { + Node* use = old_phi->last_out(i); + igvn.hash_delete(use); + igvn._worklist.push(use); + uint uses_found = 0; + for (uint j = 0; j < use->len(); j++) { + if (use->in(j) == old_phi) { + if (j < use->req()) use->set_req (j, id_old_phi); + else use->set_prec(j, id_old_phi); + uses_found++; + } + } + i -= uses_found; // we deleted 1 or more copies of this edge + } + } + igvn._worklist.push(old_phi); + } + } + // Finally clean out the fall-in edges from the RegionNode + for( i = oreq-1; i>0; i-- ) { + if( !phase->is_member( this, _head->in(i) ) ) { + _head->del_req(i); + } + } + // Transform landing pad + igvn.register_new_node_with_optimizer(landing_pad, _head); + // Insert landing pad into the header + _head->add_req(landing_pad); +} + +//------------------------------split_outer_loop------------------------------- +// Split out the outermost loop from this shared header. +void IdealLoopTree::split_outer_loop( PhaseIdealLoop *phase ) { + PhaseIterGVN &igvn = phase->_igvn; + + // Find index of outermost loop; it should also be my tail. + uint outer_idx = 1; + while( _head->in(outer_idx) != _tail ) outer_idx++; + + // Make a LoopNode for the outermost loop. + Node *ctl = _head->in(LoopNode::EntryControl); + Node *outer = new (phase->C, 3) LoopNode( ctl, _head->in(outer_idx) ); + outer = igvn.register_new_node_with_optimizer(outer, _head); + phase->set_created_loop_node(); + // Outermost loop falls into '_head' loop + _head->set_req(LoopNode::EntryControl, outer); + _head->del_req(outer_idx); + // Split all the Phis up between '_head' loop and 'outer' loop. + for (DUIterator_Fast jmax, j = _head->fast_outs(jmax); j < jmax; j++) { + Node *out = _head->fast_out(j); + if( out->is_Phi() ) { + PhiNode *old_phi = out->as_Phi(); + assert( old_phi->region() == _head, "" ); + Node *phi = PhiNode::make_blank(outer, old_phi); + phi->init_req(LoopNode::EntryControl, old_phi->in(LoopNode::EntryControl)); + phi->init_req(LoopNode::LoopBackControl, old_phi->in(outer_idx)); + phi = igvn.register_new_node_with_optimizer(phi, old_phi); + // Make old Phi point to new Phi on the fall-in path + igvn.hash_delete(old_phi); + old_phi->set_req(LoopNode::EntryControl, phi); + old_phi->del_req(outer_idx); + igvn._worklist.push(old_phi); + } + } + + // Use the new loop head instead of the old shared one + _head = outer; + phase->set_loop(_head, this); +} + +//------------------------------fix_parent------------------------------------- +static void fix_parent( IdealLoopTree *loop, IdealLoopTree *parent ) { + loop->_parent = parent; + if( loop->_child ) fix_parent( loop->_child, loop ); + if( loop->_next ) fix_parent( loop->_next , parent ); +} + +//------------------------------estimate_path_freq----------------------------- +static float estimate_path_freq( Node *n ) { + // Try to extract some path frequency info + IfNode *iff; + for( int i = 0; i < 50; i++ ) { // Skip through a bunch of uncommon tests + uint nop = n->Opcode(); + if( nop == Op_SafePoint ) { // Skip any safepoint + n = n->in(0); + continue; + } + if( nop == Op_CatchProj ) { // Get count from a prior call + // Assume call does not always throw exceptions: means the call-site + // count is also the frequency of the fall-through path. + assert( n->is_CatchProj(), "" ); + if( ((CatchProjNode*)n)->_con != CatchProjNode::fall_through_index ) + return 0.0f; // Assume call exception path is rare + Node *call = n->in(0)->in(0)->in(0); + assert( call->is_Call(), "expect a call here" ); + const JVMState *jvms = ((CallNode*)call)->jvms(); + ciMethodData* methodData = jvms->method()->method_data(); + if (!methodData->is_mature()) return 0.0f; // No call-site data + ciProfileData* data = methodData->bci_to_data(jvms->bci()); + if ((data == NULL) || !data->is_CounterData()) { + // no call profile available, try call's control input + n = n->in(0); + continue; + } + return data->as_CounterData()->count()/FreqCountInvocations; + } + // See if there's a gating IF test + Node *n_c = n->in(0); + if( !n_c->is_If() ) break; // No estimate available + iff = n_c->as_If(); + if( iff->_fcnt != COUNT_UNKNOWN ) // Have a valid count? + // Compute how much count comes on this path + return ((nop == Op_IfTrue) ? iff->_prob : 1.0f - iff->_prob) * iff->_fcnt; + // Have no count info. Skip dull uncommon-trap like branches. + if( (nop == Op_IfTrue && iff->_prob < PROB_LIKELY_MAG(5)) || + (nop == Op_IfFalse && iff->_prob > PROB_UNLIKELY_MAG(5)) ) + break; + // Skip through never-taken branch; look for a real loop exit. + n = iff->in(0); + } + return 0.0f; // No estimate available +} + +//------------------------------merge_many_backedges--------------------------- +// Merge all the backedges from the shared header into a private Region. +// Feed that region as the one backedge to this loop. +void IdealLoopTree::merge_many_backedges( PhaseIdealLoop *phase ) { + uint i; + + // Scan for the top 2 hottest backedges + float hotcnt = 0.0f; + float warmcnt = 0.0f; + uint hot_idx = 0; + // Loop starts at 2 because slot 1 is the fall-in path + for( i = 2; i < _head->req(); i++ ) { + float cnt = estimate_path_freq(_head->in(i)); + if( cnt > hotcnt ) { // Grab hottest path + warmcnt = hotcnt; + hotcnt = cnt; + hot_idx = i; + } else if( cnt > warmcnt ) { // And 2nd hottest path + warmcnt = cnt; + } + } + + // See if the hottest backedge is worthy of being an inner loop + // by being much hotter than the next hottest backedge. + if( hotcnt <= 0.0001 || + hotcnt < 2.0*warmcnt ) hot_idx = 0;// No hot backedge + + // Peel out the backedges into a private merge point; peel + // them all except optionally hot_idx. + PhaseIterGVN &igvn = phase->_igvn; + + Node *hot_tail = NULL; + // Make a Region for the merge point + Node *r = new (phase->C, 1) RegionNode(1); + for( i = 2; i < _head->req(); i++ ) { + if( i != hot_idx ) + r->add_req( _head->in(i) ); + else hot_tail = _head->in(i); + } + igvn.register_new_node_with_optimizer(r, _head); + // Plug region into end of loop _head, followed by hot_tail + while( _head->req() > 3 ) _head->del_req( _head->req()-1 ); + _head->set_req(2, r); + if( hot_idx ) _head->add_req(hot_tail); + + // Split all the Phis up between '_head' loop and the Region 'r' + for (DUIterator_Fast jmax, j = _head->fast_outs(jmax); j < jmax; j++) { + Node *out = _head->fast_out(j); + if( out->is_Phi() ) { + PhiNode* n = out->as_Phi(); + igvn.hash_delete(n); // Delete from hash before hacking edges + Node *hot_phi = NULL; + Node *phi = new (phase->C, r->req()) PhiNode(r, n->type(), n->adr_type()); + // Check all inputs for the ones to peel out + uint j = 1; + for( uint i = 2; i < n->req(); i++ ) { + if( i != hot_idx ) + phi->set_req( j++, n->in(i) ); + else hot_phi = n->in(i); + } + // Register the phi but do not transform until whole place transforms + igvn.register_new_node_with_optimizer(phi, n); + // Add the merge phi to the old Phi + while( n->req() > 3 ) n->del_req( n->req()-1 ); + n->set_req(2, phi); + if( hot_idx ) n->add_req(hot_phi); + } + } + + + // Insert a new IdealLoopTree inserted below me. Turn it into a clone + // of self loop tree. Turn self into a loop headed by _head and with + // tail being the new merge point. + IdealLoopTree *ilt = new IdealLoopTree( phase, _head, _tail ); + phase->set_loop(_tail,ilt); // Adjust tail + _tail = r; // Self's tail is new merge point + phase->set_loop(r,this); + ilt->_child = _child; // New guy has my children + _child = ilt; // Self has new guy as only child + ilt->_parent = this; // new guy has self for parent + ilt->_nest = _nest; // Same nesting depth (for now) + + // Starting with 'ilt', look for child loop trees using the same shared + // header. Flatten these out; they will no longer be loops in the end. + IdealLoopTree **pilt = &_child; + while( ilt ) { + if( ilt->_head == _head ) { + uint i; + for( i = 2; i < _head->req(); i++ ) + if( _head->in(i) == ilt->_tail ) + break; // Still a loop + if( i == _head->req() ) { // No longer a loop + // Flatten ilt. Hang ilt's "_next" list from the end of + // ilt's '_child' list. Move the ilt's _child up to replace ilt. + IdealLoopTree **cp = &ilt->_child; + while( *cp ) cp = &(*cp)->_next; // Find end of child list + *cp = ilt->_next; // Hang next list at end of child list + *pilt = ilt->_child; // Move child up to replace ilt + ilt->_head = NULL; // Flag as a loop UNIONED into parent + ilt = ilt->_child; // Repeat using new ilt + continue; // do not advance over ilt->_child + } + assert( ilt->_tail == hot_tail, "expected to only find the hot inner loop here" ); + phase->set_loop(_head,ilt); + } + pilt = &ilt->_child; // Advance to next + ilt = *pilt; + } + + if( _child ) fix_parent( _child, this ); +} + +//------------------------------beautify_loops--------------------------------- +// Split shared headers and insert loop landing pads. +// Insert a LoopNode to replace the RegionNode. +// Return TRUE if loop tree is structurally changed. +bool IdealLoopTree::beautify_loops( PhaseIdealLoop *phase ) { + bool result = false; + // Cache parts in locals for easy + PhaseIterGVN &igvn = phase->_igvn; + + phase->C->print_method("Before beautify loops", 3); + + igvn.hash_delete(_head); // Yank from hash before hacking edges + + // Check for multiple fall-in paths. Peel off a landing pad if need be. + int fall_in_cnt = 0; + for( uint i = 1; i < _head->req(); i++ ) + if( !phase->is_member( this, _head->in(i) ) ) + fall_in_cnt++; + assert( fall_in_cnt, "at least 1 fall-in path" ); + if( fall_in_cnt > 1 ) // Need a loop landing pad to merge fall-ins + split_fall_in( phase, fall_in_cnt ); + + // Swap inputs to the _head and all Phis to move the fall-in edge to + // the left. + fall_in_cnt = 1; + while( phase->is_member( this, _head->in(fall_in_cnt) ) ) + fall_in_cnt++; + if( fall_in_cnt > 1 ) { + // Since I am just swapping inputs I do not need to update def-use info + Node *tmp = _head->in(1); + _head->set_req( 1, _head->in(fall_in_cnt) ); + _head->set_req( fall_in_cnt, tmp ); + // Swap also all Phis + for (DUIterator_Fast imax, i = _head->fast_outs(imax); i < imax; i++) { + Node* phi = _head->fast_out(i); + if( phi->is_Phi() ) { + igvn.hash_delete(phi); // Yank from hash before hacking edges + tmp = phi->in(1); + phi->set_req( 1, phi->in(fall_in_cnt) ); + phi->set_req( fall_in_cnt, tmp ); + } + } + } + assert( !phase->is_member( this, _head->in(1) ), "left edge is fall-in" ); + assert( phase->is_member( this, _head->in(2) ), "right edge is loop" ); + + // If I am a shared header (multiple backedges), peel off the many + // backedges into a private merge point and use the merge point as + // the one true backedge. + if( _head->req() > 3 ) { + // Merge the many backedges into a single backedge. + merge_many_backedges( phase ); + result = true; + } + + // If I am a shared header (multiple backedges), peel off myself loop. + // I better be the outermost loop. + if( _head->req() > 3 ) { + split_outer_loop( phase ); + result = true; + + } else if( !_head->is_Loop() && !_irreducible ) { + // Make a new LoopNode to replace the old loop head + Node *l = new (phase->C, 3) LoopNode( _head->in(1), _head->in(2) ); + l = igvn.register_new_node_with_optimizer(l, _head); + phase->set_created_loop_node(); + // Go ahead and replace _head + phase->_igvn.subsume_node( _head, l ); + _head = l; + phase->set_loop(_head, this); + for (DUIterator_Fast imax, i = l->fast_outs(imax); i < imax; i++) + phase->_igvn.add_users_to_worklist(l->fast_out(i)); + } + + phase->C->print_method("After beautify loops", 3); + + // Now recursively beautify nested loops + if( _child ) result |= _child->beautify_loops( phase ); + if( _next ) result |= _next ->beautify_loops( phase ); + return result; +} + +//------------------------------allpaths_check_safepts---------------------------- +// Allpaths backwards scan from loop tail, terminating each path at first safepoint +// encountered. Helper for check_safepts. +void IdealLoopTree::allpaths_check_safepts(VectorSet &visited, Node_List &stack) { + assert(stack.size() == 0, "empty stack"); + stack.push(_tail); + visited.Clear(); + visited.set(_tail->_idx); + while (stack.size() > 0) { + Node* n = stack.pop(); + if (n->is_Call() && n->as_Call()->guaranteed_safepoint()) { + // Terminate this path + } else if (n->Opcode() == Op_SafePoint) { + if (_phase->get_loop(n) != this) { + if (_required_safept == NULL) _required_safept = new Node_List(); + _required_safept->push(n); // save the one closest to the tail + } + // Terminate this path + } else { + uint start = n->is_Region() ? 1 : 0; + uint end = n->is_Region() && !n->is_Loop() ? n->req() : start + 1; + for (uint i = start; i < end; i++) { + Node* in = n->in(i); + assert(in->is_CFG(), "must be"); + if (!visited.test_set(in->_idx) && is_member(_phase->get_loop(in))) { + stack.push(in); + } + } + } + } +} + +//------------------------------check_safepts---------------------------- +// Given dominators, try to find loops with calls that must always be +// executed (call dominates loop tail). These loops do not need non-call +// safepoints (ncsfpt). +// +// A complication is that a safepoint in a inner loop may be needed +// by an outer loop. In the following, the inner loop sees it has a +// call (block 3) on every path from the head (block 2) to the +// backedge (arc 3->2). So it deletes the ncsfpt (non-call safepoint) +// in block 2, _but_ this leaves the outer loop without a safepoint. +// +// entry 0 +// | +// v +// outer 1,2 +->1 +// | | +// | v +// | 2<---+ ncsfpt in 2 +// |_/|\ | +// | v | +// inner 2,3 / 3 | call in 3 +// / | | +// v +--+ +// exit 4 +// +// +// This method creates a list (_required_safept) of ncsfpt nodes that must +// be protected is created for each loop. When a ncsfpt maybe deleted, it +// is first looked for in the lists for the outer loops of the current loop. +// +// The insights into the problem: +// A) counted loops are okay +// B) innermost loops are okay (only an inner loop can delete +// a ncsfpt needed by an outer loop) +// C) a loop is immune from an inner loop deleting a safepoint +// if the loop has a call on the idom-path +// D) a loop is also immune if it has a ncsfpt (non-call safepoint) on the +// idom-path that is not in a nested loop +// E) otherwise, an ncsfpt on the idom-path that is nested in an inner +// loop needs to be prevented from deletion by an inner loop +// +// There are two analyses: +// 1) The first, and cheaper one, scans the loop body from +// tail to head following the idom (immediate dominator) +// chain, looking for the cases (C,D,E) above. +// Since inner loops are scanned before outer loops, there is summary +// information about inner loops. Inner loops can be skipped over +// when the tail of an inner loop is encountered. +// +// 2) The second, invoked if the first fails to find a call or ncsfpt on +// the idom path (which is rare), scans all predecessor control paths +// from the tail to the head, terminating a path when a call or sfpt +// is encountered, to find the ncsfpt's that are closest to the tail. +// +void IdealLoopTree::check_safepts(VectorSet &visited, Node_List &stack) { + // Bottom up traversal + IdealLoopTree* ch = _child; + while (ch != NULL) { + ch->check_safepts(visited, stack); + ch = ch->_next; + } + + if (!_head->is_CountedLoop() && !_has_sfpt && _parent != NULL && !_irreducible) { + bool has_call = false; // call on dom-path + bool has_local_ncsfpt = false; // ncsfpt on dom-path at this loop depth + Node* nonlocal_ncsfpt = NULL; // ncsfpt on dom-path at a deeper depth + // Scan the dom-path nodes from tail to head + for (Node* n = tail(); n != _head; n = _phase->idom(n)) { + if (n->is_Call() && n->as_Call()->guaranteed_safepoint()) { + has_call = true; + _has_sfpt = 1; // Then no need for a safept! + break; + } else if (n->Opcode() == Op_SafePoint) { + if (_phase->get_loop(n) == this) { + has_local_ncsfpt = true; + break; + } + if (nonlocal_ncsfpt == NULL) { + nonlocal_ncsfpt = n; // save the one closest to the tail + } + } else { + IdealLoopTree* nlpt = _phase->get_loop(n); + if (this != nlpt) { + // If at an inner loop tail, see if the inner loop has already + // recorded seeing a call on the dom-path (and stop.) If not, + // jump to the head of the inner loop. + assert(is_member(nlpt), "nested loop"); + Node* tail = nlpt->_tail; + if (tail->in(0)->is_If()) tail = tail->in(0); + if (n == tail) { + // If inner loop has call on dom-path, so does outer loop + if (nlpt->_has_sfpt) { + has_call = true; + _has_sfpt = 1; + break; + } + // Skip to head of inner loop + assert(_phase->is_dominator(_head, nlpt->_head), "inner head dominated by outer head"); + n = nlpt->_head; + } + } + } + } + // Record safept's that this loop needs preserved when an + // inner loop attempts to delete it's safepoints. + if (_child != NULL && !has_call && !has_local_ncsfpt) { + if (nonlocal_ncsfpt != NULL) { + if (_required_safept == NULL) _required_safept = new Node_List(); + _required_safept->push(nonlocal_ncsfpt); + } else { + // Failed to find a suitable safept on the dom-path. Now use + // an all paths walk from tail to head, looking for safepoints to preserve. + allpaths_check_safepts(visited, stack); + } + } + } +} + +//---------------------------is_deleteable_safept---------------------------- +// Is safept not required by an outer loop? +bool PhaseIdealLoop::is_deleteable_safept(Node* sfpt) { + assert(sfpt->Opcode() == Op_SafePoint, ""); + IdealLoopTree* lp = get_loop(sfpt)->_parent; + while (lp != NULL) { + Node_List* sfpts = lp->_required_safept; + if (sfpts != NULL) { + for (uint i = 0; i < sfpts->size(); i++) { + if (sfpt == sfpts->at(i)) + return false; + } + } + lp = lp->_parent; + } + return true; +} + +//------------------------------counted_loop----------------------------------- +// Convert to counted loops where possible +void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { + + // For grins, set the inner-loop flag here + if( !_child ) { + if( _head->is_Loop() ) _head->as_Loop()->set_inner_loop(); + } + + if( _head->is_CountedLoop() || + phase->is_counted_loop( _head, this ) ) { + _has_sfpt = 1; // Indicate we do not need a safepoint here + + // Look for a safepoint to remove + for (Node* n = tail(); n != _head; n = phase->idom(n)) + if (n->Opcode() == Op_SafePoint && phase->get_loop(n) == this && + phase->is_deleteable_safept(n)) + phase->lazy_replace(n,n->in(TypeFunc::Control)); + + CountedLoopNode *cl = _head->as_CountedLoop(); + Node *incr = cl->incr(); + if( !incr ) return; // Dead loop? + Node *init = cl->init_trip(); + Node *phi = cl->phi(); + // protect against stride not being a constant + if( !cl->stride_is_con() ) return; + int stride_con = cl->stride_con(); + + // Look for induction variables + + // Visit all children, looking for Phis + for (DUIterator i = cl->outs(); cl->has_out(i); i++) { + Node *out = cl->out(i); + if (!out->is_Phi()) continue; // Looking for phis + PhiNode* phi2 = out->as_Phi(); + Node *incr2 = phi2->in( LoopNode::LoopBackControl ); + // Look for induction variables of the form: X += constant + if( phi2->region() != _head || + incr2->req() != 3 || + incr2->in(1) != phi2 || + incr2 == incr || + incr2->Opcode() != Op_AddI || + !incr2->in(2)->is_Con() ) + continue; + + // Check for parallel induction variable (parallel to trip counter) + // via an affine function. In particular, count-down loops with + // count-up array indices are common. We only RCE references off + // the trip-counter, so we need to convert all these to trip-counter + // expressions. + Node *init2 = phi2->in( LoopNode::EntryControl ); + int stride_con2 = incr2->in(2)->get_int(); + + // The general case here gets a little tricky. We want to find the + // GCD of all possible parallel IV's and make a new IV using this + // GCD for the loop. Then all possible IVs are simple multiples of + // the GCD. In practice, this will cover very few extra loops. + // Instead we require 'stride_con2' to be a multiple of 'stride_con', + // where +/-1 is the common case, but other integer multiples are + // also easy to handle. + int ratio_con = stride_con2/stride_con; + + if( ratio_con * stride_con == stride_con2 ) { // Check for exact + // Convert to using the trip counter. The parallel induction + // variable differs from the trip counter by a loop-invariant + // amount, the difference between their respective initial values. + // It is scaled by the 'ratio_con'. + Compile* C = phase->C; + Node* ratio = phase->_igvn.intcon(ratio_con); + phase->set_ctrl(ratio, C->root()); + Node* ratio_init = new (C, 3) MulINode(init, ratio); + phase->_igvn.register_new_node_with_optimizer(ratio_init, init); + phase->set_early_ctrl(ratio_init); + Node* diff = new (C, 3) SubINode(init2, ratio_init); + phase->_igvn.register_new_node_with_optimizer(diff, init2); + phase->set_early_ctrl(diff); + Node* ratio_idx = new (C, 3) MulINode(phi, ratio); + phase->_igvn.register_new_node_with_optimizer(ratio_idx, phi); + phase->set_ctrl(ratio_idx, cl); + Node* add = new (C, 3) AddINode(ratio_idx, diff); + phase->_igvn.register_new_node_with_optimizer(add); + phase->set_ctrl(add, cl); + phase->_igvn.hash_delete( phi2 ); + phase->_igvn.subsume_node( phi2, add ); + // Sometimes an induction variable is unused + if (add->outcnt() == 0) { + phase->_igvn.remove_dead_node(add); + } + --i; // deleted this phi; rescan starting with next position + continue; + } + } + } else if (_parent != NULL && !_irreducible) { + // Not a counted loop. + // Look for a safepoint on the idom-path to remove, preserving the first one + bool found = false; + Node* n = tail(); + for (; n != _head && !found; n = phase->idom(n)) { + if (n->Opcode() == Op_SafePoint && phase->get_loop(n) == this) + found = true; // Found one + } + // Skip past it and delete the others + for (; n != _head; n = phase->idom(n)) { + if (n->Opcode() == Op_SafePoint && phase->get_loop(n) == this && + phase->is_deleteable_safept(n)) + phase->lazy_replace(n,n->in(TypeFunc::Control)); + } + } + + // Recursively + if( _child ) _child->counted_loop( phase ); + if( _next ) _next ->counted_loop( phase ); +} + +#ifndef PRODUCT +//------------------------------dump_head-------------------------------------- +// Dump 1 liner for loop header info +void IdealLoopTree::dump_head( ) const { + for( uint i=0; i<_nest; i++ ) + tty->print(" "); + tty->print("Loop: N%d/N%d ",_head->_idx,_tail->_idx); + if( _irreducible ) tty->print(" IRREDUCIBLE"); + if( _head->is_CountedLoop() ) { + CountedLoopNode *cl = _head->as_CountedLoop(); + tty->print(" counted"); + if( cl->is_pre_loop () ) tty->print(" pre" ); + if( cl->is_main_loop() ) tty->print(" main"); + if( cl->is_post_loop() ) tty->print(" post"); + } + tty->cr(); +} + +//------------------------------dump------------------------------------------- +// Dump loops by loop tree +void IdealLoopTree::dump( ) const { + dump_head(); + if( _child ) _child->dump(); + if( _next ) _next ->dump(); +} + +#endif + +//============================================================================= +//------------------------------PhaseIdealLoop--------------------------------- +// Create a PhaseLoop. Build the ideal Loop tree. Map each Ideal Node to +// its corresponding LoopNode. If 'optimize' is true, do some loop cleanups. +PhaseIdealLoop::PhaseIdealLoop( PhaseIterGVN &igvn, const PhaseIdealLoop *verify_me, bool do_split_ifs ) + : PhaseTransform(Ideal_Loop), + _igvn(igvn), + _dom_lca_tags(C->comp_arena()) { + // Reset major-progress flag for the driver's heuristics + C->clear_major_progress(); + +#ifndef PRODUCT + // Capture for later assert + uint unique = C->unique(); + _loop_invokes++; + _loop_work += unique; +#endif + + // True if the method has at least 1 irreducible loop + _has_irreducible_loops = false; + + _created_loop_node = false; + + Arena *a = Thread::current()->resource_area(); + VectorSet visited(a); + // Pre-grow the mapping from Nodes to IdealLoopTrees. + _nodes.map(C->unique(), NULL); + memset(_nodes.adr(), 0, wordSize * C->unique()); + + // Pre-build the top-level outermost loop tree entry + _ltree_root = new IdealLoopTree( this, C->root(), C->root() ); + // Do not need a safepoint at the top level + _ltree_root->_has_sfpt = 1; + + // Empty pre-order array + allocate_preorders(); + + // Build a loop tree on the fly. Build a mapping from CFG nodes to + // IdealLoopTree entries. Data nodes are NOT walked. + build_loop_tree(); + // Check for bailout, and return + if (C->failing()) { + return; + } + + // No loops after all + if( !_ltree_root->_child ) C->set_has_loops(false); + + // There should always be an outer loop containing the Root and Return nodes. + // If not, we have a degenerate empty program. Bail out in this case. + if (!has_node(C->root())) { + C->clear_major_progress(); + C->record_method_not_compilable("empty program detected during loop optimization"); + return; + } + + // Nothing to do, so get out + if( !C->has_loops() && !do_split_ifs && !verify_me) { + _igvn.optimize(); // Cleanup NeverBranches + return; + } + + // Set loop nesting depth + _ltree_root->set_nest( 0 ); + + // Split shared headers and insert loop landing pads. + // Do not bother doing this on the Root loop of course. + if( !verify_me && _ltree_root->_child ) { + if( _ltree_root->_child->beautify_loops( this ) ) { + // Re-build loop tree! + _ltree_root->_child = NULL; + _nodes.clear(); + reallocate_preorders(); + build_loop_tree(); + // Check for bailout, and return + if (C->failing()) { + return; + } + // Reset loop nesting depth + _ltree_root->set_nest( 0 ); + } + } + + // Build Dominators for elision of NULL checks & loop finding. + // Since nodes do not have a slot for immediate dominator, make + // a persistant side array for that info indexed on node->_idx. + _idom_size = C->unique(); + _idom = NEW_RESOURCE_ARRAY( Node*, _idom_size ); + _dom_depth = NEW_RESOURCE_ARRAY( uint, _idom_size ); + _dom_stk = NULL; // Allocated on demand in recompute_dom_depth + memset( _dom_depth, 0, _idom_size * sizeof(uint) ); + + Dominators(); + + // As a side effect, Dominators removed any unreachable CFG paths + // into RegionNodes. It doesn't do this test against Root, so + // we do it here. + for( uint i = 1; i < C->root()->req(); i++ ) { + if( !_nodes[C->root()->in(i)->_idx] ) { // Dead path into Root? + _igvn.hash_delete(C->root()); + C->root()->del_req(i); + _igvn._worklist.push(C->root()); + i--; // Rerun same iteration on compressed edges + } + } + + // Given dominators, try to find inner loops with calls that must + // always be executed (call dominates loop tail). These loops do + // not need a seperate safepoint. + Node_List cisstack(a); + _ltree_root->check_safepts(visited, cisstack); + + // Walk the DATA nodes and place into loops. Find earliest control + // node. For CFG nodes, the _nodes array starts out and remains + // holding the associated IdealLoopTree pointer. For DATA nodes, the + // _nodes array holds the earliest legal controlling CFG node. + + // Allocate stack with enough space to avoid frequent realloc + int stack_size = (C->unique() >> 1) + 16; // (unique>>1)+16 from Java2D stats + Node_Stack nstack( a, stack_size ); + + visited.Clear(); + Node_List worklist(a); + // Don't need C->root() on worklist since + // it will be processed among C->top() inputs + worklist.push( C->top() ); + visited.set( C->top()->_idx ); // Set C->top() as visited now + build_loop_early( visited, worklist, nstack, verify_me ); + + // Given early legal placement, try finding counted loops. This placement + // is good enough to discover most loop invariants. + if( !verify_me ) + _ltree_root->counted_loop( this ); + + // Find latest loop placement. Find ideal loop placement. + visited.Clear(); + init_dom_lca_tags(); + // Need C->root() on worklist when processing outs + worklist.push( C->root() ); + NOT_PRODUCT( C->verify_graph_edges(); ) + worklist.push( C->top() ); + build_loop_late( visited, worklist, nstack, verify_me ); + + // clear out the dead code + while(_deadlist.size()) { + igvn.remove_globally_dead_node(_deadlist.pop()); + } + +#ifndef PRODUCT + C->verify_graph_edges(); + if( verify_me ) { // Nested verify pass? + // Check to see if the verify mode is broken + assert(C->unique() == unique, "non-optimize mode made Nodes? ? ?"); + return; + } + if( VerifyLoopOptimizations ) verify(); +#endif + + if (ReassociateInvariants) { + // Reassociate invariants and prep for split_thru_phi + for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { + IdealLoopTree* lpt = iter.current(); + if (!lpt->is_counted() || !lpt->is_inner()) continue; + + lpt->reassociate_invariants(this); + + // Because RCE opportunities can be masked by split_thru_phi, + // look for RCE candidates and inhibit split_thru_phi + // on just their loop-phi's for this pass of loop opts + if( SplitIfBlocks && do_split_ifs ) { + if (lpt->policy_range_check(this)) { + lpt->_rce_candidate = true; + } + } + } + } + + // Check for aggressive application of split-if and other transforms + // that require basic-block info (like cloning through Phi's) + if( SplitIfBlocks && do_split_ifs ) { + visited.Clear(); + split_if_with_blocks( visited, nstack ); + NOT_PRODUCT( if( VerifyLoopOptimizations ) verify(); ); + } + + // Perform iteration-splitting on inner loops. Split iterations to avoid + // range checks or one-shot null checks. + + // If split-if's didn't hack the graph too bad (no CFG changes) + // then do loop opts. + if( C->has_loops() && !C->major_progress() ) { + memset( worklist.adr(), 0, worklist.Size()*sizeof(Node*) ); + _ltree_root->_child->iteration_split( this, worklist ); + // No verify after peeling! GCM has hoisted code out of the loop. + // After peeling, the hoisted code could sink inside the peeled area. + // The peeling code does not try to recompute the best location for + // all the code before the peeled area, so the verify pass will always + // complain about it. + } + // Do verify graph edges in any case + NOT_PRODUCT( C->verify_graph_edges(); ); + + if( !do_split_ifs ) { + // We saw major progress in Split-If to get here. We forced a + // pass with unrolling and not split-if, however more split-if's + // might make progress. If the unrolling didn't make progress + // then the major-progress flag got cleared and we won't try + // another round of Split-If. In particular the ever-common + // instance-of/check-cast pattern requires at least 2 rounds of + // Split-If to clear out. + C->set_major_progress(); + } + + // Repeat loop optimizations if new loops were seen + if (created_loop_node()) { + C->set_major_progress(); + } + + // Convert scalar to superword operations + + if (UseSuperWord && C->has_loops() && !C->major_progress()) { + // SuperWord transform + SuperWord sw(this); + for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { + IdealLoopTree* lpt = iter.current(); + if (lpt->is_counted()) { + sw.transform_loop(lpt); + } + } + } + + // Cleanup any modified bits + _igvn.optimize(); + + // Do not repeat loop optimizations if irreducible loops are present + // by claiming no-progress. + if( _has_irreducible_loops ) + C->clear_major_progress(); +} + +#ifndef PRODUCT +//------------------------------print_statistics------------------------------- +int PhaseIdealLoop::_loop_invokes=0;// Count of PhaseIdealLoop invokes +int PhaseIdealLoop::_loop_work=0; // Sum of PhaseIdealLoop x unique +void PhaseIdealLoop::print_statistics() { + tty->print_cr("PhaseIdealLoop=%d, sum _unique=%d", _loop_invokes, _loop_work); +} + +//------------------------------verify----------------------------------------- +// Build a verify-only PhaseIdealLoop, and see that it agrees with me. +static int fail; // debug only, so its multi-thread dont care +void PhaseIdealLoop::verify() const { + int old_progress = C->major_progress(); + ResourceMark rm; + PhaseIdealLoop loop_verify( _igvn, this, false ); + VectorSet visited(Thread::current()->resource_area()); + + fail = 0; + verify_compare( C->root(), &loop_verify, visited ); + assert( fail == 0, "verify loops failed" ); + // Verify loop structure is the same + _ltree_root->verify_tree(loop_verify._ltree_root, NULL); + // Reset major-progress. It was cleared by creating a verify version of + // PhaseIdealLoop. + for( int i=0; iset_major_progress(); +} + +//------------------------------verify_compare--------------------------------- +// Make sure me and the given PhaseIdealLoop agree on key data structures +void PhaseIdealLoop::verify_compare( Node *n, const PhaseIdealLoop *loop_verify, VectorSet &visited ) const { + if( !n ) return; + if( visited.test_set( n->_idx ) ) return; + if( !_nodes[n->_idx] ) { // Unreachable + assert( !loop_verify->_nodes[n->_idx], "both should be unreachable" ); + return; + } + + uint i; + for( i = 0; i < n->req(); i++ ) + verify_compare( n->in(i), loop_verify, visited ); + + // Check the '_nodes' block/loop structure + i = n->_idx; + if( has_ctrl(n) ) { // We have control; verify has loop or ctrl + if( _nodes[i] != loop_verify->_nodes[i] && + get_ctrl_no_update(n) != loop_verify->get_ctrl_no_update(n) ) { + tty->print("Mismatched control setting for: "); + n->dump(); + if( fail++ > 10 ) return; + Node *c = get_ctrl_no_update(n); + tty->print("We have it as: "); + if( c->in(0) ) c->dump(); + else tty->print_cr("N%d",c->_idx); + tty->print("Verify thinks: "); + if( loop_verify->has_ctrl(n) ) + loop_verify->get_ctrl_no_update(n)->dump(); + else + loop_verify->get_loop_idx(n)->dump(); + tty->cr(); + } + } else { // We have a loop + IdealLoopTree *us = get_loop_idx(n); + if( loop_verify->has_ctrl(n) ) { + tty->print("Mismatched loop setting for: "); + n->dump(); + if( fail++ > 10 ) return; + tty->print("We have it as: "); + us->dump(); + tty->print("Verify thinks: "); + loop_verify->get_ctrl_no_update(n)->dump(); + tty->cr(); + } else if (!C->major_progress()) { + // Loop selection can be messed up if we did a major progress + // operation, like split-if. Do not verify in that case. + IdealLoopTree *them = loop_verify->get_loop_idx(n); + if( us->_head != them->_head || us->_tail != them->_tail ) { + tty->print("Unequals loops for: "); + n->dump(); + if( fail++ > 10 ) return; + tty->print("We have it as: "); + us->dump(); + tty->print("Verify thinks: "); + them->dump(); + tty->cr(); + } + } + } + + // Check for immediate dominators being equal + if( i >= _idom_size ) { + if( !n->is_CFG() ) return; + tty->print("CFG Node with no idom: "); + n->dump(); + return; + } + if( !n->is_CFG() ) return; + if( n == C->root() ) return; // No IDOM here + + assert(n->_idx == i, "sanity"); + Node *id = idom_no_update(n); + if( id != loop_verify->idom_no_update(n) ) { + tty->print("Unequals idoms for: "); + n->dump(); + if( fail++ > 10 ) return; + tty->print("We have it as: "); + id->dump(); + tty->print("Verify thinks: "); + loop_verify->idom_no_update(n)->dump(); + tty->cr(); + } + +} + +//------------------------------verify_tree------------------------------------ +// Verify that tree structures match. Because the CFG can change, siblings +// within the loop tree can be reordered. We attempt to deal with that by +// reordering the verify's loop tree if possible. +void IdealLoopTree::verify_tree(IdealLoopTree *loop, const IdealLoopTree *parent) const { + assert( _parent == parent, "Badly formed loop tree" ); + + // Siblings not in same order? Attempt to re-order. + if( _head != loop->_head ) { + // Find _next pointer to update + IdealLoopTree **pp = &loop->_parent->_child; + while( *pp != loop ) + pp = &((*pp)->_next); + // Find proper sibling to be next + IdealLoopTree **nn = &loop->_next; + while( (*nn) && (*nn)->_head != _head ) + nn = &((*nn)->_next); + + // Check for no match. + if( !(*nn) ) { + // Annoyingly, irreducible loops can pick different headers + // after a major_progress operation, so the rest of the loop + // tree cannot be matched. + if (_irreducible && Compile::current()->major_progress()) return; + assert( 0, "failed to match loop tree" ); + } + + // Move (*nn) to (*pp) + IdealLoopTree *hit = *nn; + *nn = hit->_next; + hit->_next = loop; + *pp = loop; + loop = hit; + // Now try again to verify + } + + assert( _head == loop->_head , "mismatched loop head" ); + Node *tail = _tail; // Inline a non-updating version of + while( !tail->in(0) ) // the 'tail()' call. + tail = tail->in(1); + assert( tail == loop->_tail, "mismatched loop tail" ); + + // Counted loops that are guarded should be able to find their guards + if( _head->is_CountedLoop() && _head->as_CountedLoop()->is_main_loop() ) { + CountedLoopNode *cl = _head->as_CountedLoop(); + Node *init = cl->init_trip(); + Node *ctrl = cl->in(LoopNode::EntryControl); + assert( ctrl->Opcode() == Op_IfTrue || ctrl->Opcode() == Op_IfFalse, "" ); + Node *iff = ctrl->in(0); + assert( iff->Opcode() == Op_If, "" ); + Node *bol = iff->in(1); + assert( bol->Opcode() == Op_Bool, "" ); + Node *cmp = bol->in(1); + assert( cmp->Opcode() == Op_CmpI, "" ); + Node *add = cmp->in(1); + Node *opaq; + if( add->Opcode() == Op_Opaque1 ) { + opaq = add; + } else { + assert( add->Opcode() == Op_AddI || add->Opcode() == Op_ConI , "" ); + assert( add == init, "" ); + opaq = cmp->in(2); + } + assert( opaq->Opcode() == Op_Opaque1, "" ); + + } + + if (_child != NULL) _child->verify_tree(loop->_child, this); + if (_next != NULL) _next ->verify_tree(loop->_next, parent); + // Innermost loops need to verify loop bodies, + // but only if no 'major_progress' + int fail = 0; + if (!Compile::current()->major_progress() && _child == NULL) { + for( uint i = 0; i < _body.size(); i++ ) { + Node *n = _body.at(i); + if (n->outcnt() == 0) continue; // Ignore dead + uint j; + for( j = 0; j < loop->_body.size(); j++ ) + if( loop->_body.at(j) == n ) + break; + if( j == loop->_body.size() ) { // Not found in loop body + // Last ditch effort to avoid assertion: Its possible that we + // have some users (so outcnt not zero) but are still dead. + // Try to find from root. + if (Compile::current()->root()->find(n->_idx)) { + fail++; + tty->print("We have that verify does not: "); + n->dump(); + } + } + } + for( uint i2 = 0; i2 < loop->_body.size(); i2++ ) { + Node *n = loop->_body.at(i2); + if (n->outcnt() == 0) continue; // Ignore dead + uint j; + for( j = 0; j < _body.size(); j++ ) + if( _body.at(j) == n ) + break; + if( j == _body.size() ) { // Not found in loop body + // Last ditch effort to avoid assertion: Its possible that we + // have some users (so outcnt not zero) but are still dead. + // Try to find from root. + if (Compile::current()->root()->find(n->_idx)) { + fail++; + tty->print("Verify has that we do not: "); + n->dump(); + } + } + } + assert( !fail, "loop body mismatch" ); + } +} + +#endif + +//------------------------------set_idom--------------------------------------- +void PhaseIdealLoop::set_idom(Node* d, Node* n, uint dom_depth) { + uint idx = d->_idx; + if (idx >= _idom_size) { + uint newsize = _idom_size<<1; + while( idx >= newsize ) { + newsize <<= 1; + } + _idom = REALLOC_RESOURCE_ARRAY( Node*, _idom,_idom_size,newsize); + _dom_depth = REALLOC_RESOURCE_ARRAY( uint, _dom_depth,_idom_size,newsize); + memset( _dom_depth + _idom_size, 0, (newsize - _idom_size) * sizeof(uint) ); + _idom_size = newsize; + } + _idom[idx] = n; + _dom_depth[idx] = dom_depth; +} + +//------------------------------recompute_dom_depth--------------------------------------- +// The dominator tree is constructed with only parent pointers. +// This recomputes the depth in the tree by first tagging all +// nodes as "no depth yet" marker. The next pass then runs up +// the dom tree from each node marked "no depth yet", and computes +// the depth on the way back down. +void PhaseIdealLoop::recompute_dom_depth() { + uint no_depth_marker = C->unique(); + uint i; + // Initialize depth to "no depth yet" + for (i = 0; i < _idom_size; i++) { + if (_dom_depth[i] > 0 && _idom[i] != NULL) { + _dom_depth[i] = no_depth_marker; + } + } + if (_dom_stk == NULL) { + uint init_size = C->unique() / 100; // Guess that 1/100 is a reasonable initial size. + if (init_size < 10) init_size = 10; + _dom_stk = new (C->node_arena()) GrowableArray(C->node_arena(), init_size, 0, 0); + } + // Compute new depth for each node. + for (i = 0; i < _idom_size; i++) { + uint j = i; + // Run up the dom tree to find a node with a depth + while (_dom_depth[j] == no_depth_marker) { + _dom_stk->push(j); + j = _idom[j]->_idx; + } + // Compute the depth on the way back down this tree branch + uint dd = _dom_depth[j] + 1; + while (_dom_stk->length() > 0) { + uint j = _dom_stk->pop(); + _dom_depth[j] = dd; + dd++; + } + } +} + +//------------------------------sort------------------------------------------- +// Insert 'loop' into the existing loop tree. 'innermost' is a leaf of the +// loop tree, not the root. +IdealLoopTree *PhaseIdealLoop::sort( IdealLoopTree *loop, IdealLoopTree *innermost ) { + if( !innermost ) return loop; // New innermost loop + + int loop_preorder = get_preorder(loop->_head); // Cache pre-order number + assert( loop_preorder, "not yet post-walked loop" ); + IdealLoopTree **pp = &innermost; // Pointer to previous next-pointer + IdealLoopTree *l = *pp; // Do I go before or after 'l'? + + // Insert at start of list + while( l ) { // Insertion sort based on pre-order + if( l == loop ) return innermost; // Already on list! + int l_preorder = get_preorder(l->_head); // Cache pre-order number + assert( l_preorder, "not yet post-walked l" ); + // Check header pre-order number to figure proper nesting + if( loop_preorder > l_preorder ) + break; // End of insertion + // If headers tie (e.g., shared headers) check tail pre-order numbers. + // Since I split shared headers, you'd think this could not happen. + // BUT: I must first do the preorder numbering before I can discover I + // have shared headers, so the split headers all get the same preorder + // number as the RegionNode they split from. + if( loop_preorder == l_preorder && + get_preorder(loop->_tail) < get_preorder(l->_tail) ) + break; // Also check for shared headers (same pre#) + pp = &l->_parent; // Chain up list + l = *pp; + } + // Link into list + // Point predecessor to me + *pp = loop; + // Point me to successor + IdealLoopTree *p = loop->_parent; + loop->_parent = l; // Point me to successor + if( p ) sort( p, innermost ); // Insert my parents into list as well + return innermost; +} + +//------------------------------build_loop_tree-------------------------------- +// I use a modified Vick/Tarjan algorithm. I need pre- and a post- visit +// bits. The _nodes[] array is mapped by Node index and holds a NULL for +// not-yet-pre-walked, pre-order # for pre-but-not-post-walked and holds the +// tightest enclosing IdealLoopTree for post-walked. +// +// During my forward walk I do a short 1-layer lookahead to see if I can find +// a loop backedge with that doesn't have any work on the backedge. This +// helps me construct nested loops with shared headers better. +// +// Once I've done the forward recursion, I do the post-work. For each child +// I check to see if there is a backedge. Backedges define a loop! I +// insert an IdealLoopTree at the target of the backedge. +// +// During the post-work I also check to see if I have several children +// belonging to different loops. If so, then this Node is a decision point +// where control flow can choose to change loop nests. It is at this +// decision point where I can figure out how loops are nested. At this +// time I can properly order the different loop nests from my children. +// Note that there may not be any backedges at the decision point! +// +// Since the decision point can be far removed from the backedges, I can't +// order my loops at the time I discover them. Thus at the decision point +// I need to inspect loop header pre-order numbers to properly nest my +// loops. This means I need to sort my childrens' loops by pre-order. +// The sort is of size number-of-control-children, which generally limits +// it to size 2 (i.e., I just choose between my 2 target loops). +void PhaseIdealLoop::build_loop_tree() { + // Allocate stack of size C->unique()/2 to avoid frequent realloc + GrowableArray bltstack(C->unique() >> 1); + Node *n = C->root(); + bltstack.push(n); + int pre_order = 1; + int stack_size; + + while ( ( stack_size = bltstack.length() ) != 0 ) { + n = bltstack.top(); // Leave node on stack + if ( !is_visited(n) ) { + // ---- Pre-pass Work ---- + // Pre-walked but not post-walked nodes need a pre_order number. + + set_preorder_visited( n, pre_order ); // set as visited + + // ---- Scan over children ---- + // Scan first over control projections that lead to loop headers. + // This helps us find inner-to-outer loops with shared headers better. + + // Scan children's children for loop headers. + for ( int i = n->outcnt() - 1; i >= 0; --i ) { + Node* m = n->raw_out(i); // Child + if( m->is_CFG() && !is_visited(m) ) { // Only for CFG children + // Scan over children's children to find loop + for (DUIterator_Fast jmax, j = m->fast_outs(jmax); j < jmax; j++) { + Node* l = m->fast_out(j); + if( is_visited(l) && // Been visited? + !is_postvisited(l) && // But not post-visited + get_preorder(l) < pre_order ) { // And smaller pre-order + // Found! Scan the DFS down this path before doing other paths + bltstack.push(m); + break; + } + } + } + } + pre_order++; + } + else if ( !is_postvisited(n) ) { + // Note: build_loop_tree_impl() adds out edges on rare occasions, + // such as com.sun.rsasign.am::a. + // For non-recursive version, first, process current children. + // On next iteration, check if additional children were added. + for ( int k = n->outcnt() - 1; k >= 0; --k ) { + Node* u = n->raw_out(k); + if ( u->is_CFG() && !is_visited(u) ) { + bltstack.push(u); + } + } + if ( bltstack.length() == stack_size ) { + // There were no additional children, post visit node now + (void)bltstack.pop(); // Remove node from stack + pre_order = build_loop_tree_impl( n, pre_order ); + // Check for bailout + if (C->failing()) { + return; + } + // Check to grow _preorders[] array for the case when + // build_loop_tree_impl() adds new nodes. + check_grow_preorders(); + } + } + else { + (void)bltstack.pop(); // Remove post-visited node from stack + } + } +} + +//------------------------------build_loop_tree_impl--------------------------- +int PhaseIdealLoop::build_loop_tree_impl( Node *n, int pre_order ) { + // ---- Post-pass Work ---- + // Pre-walked but not post-walked nodes need a pre_order number. + + // Tightest enclosing loop for this Node + IdealLoopTree *innermost = NULL; + + // For all children, see if any edge is a backedge. If so, make a loop + // for it. Then find the tightest enclosing loop for the self Node. + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* m = n->fast_out(i); // Child + if( n == m ) continue; // Ignore control self-cycles + if( !m->is_CFG() ) continue;// Ignore non-CFG edges + + IdealLoopTree *l; // Child's loop + if( !is_postvisited(m) ) { // Child visited but not post-visited? + // Found a backedge + assert( get_preorder(m) < pre_order, "should be backedge" ); + // Check for the RootNode, which is already a LoopNode and is allowed + // to have multiple "backedges". + if( m == C->root()) { // Found the root? + l = _ltree_root; // Root is the outermost LoopNode + } else { // Else found a nested loop + // Insert a LoopNode to mark this loop. + l = new IdealLoopTree(this, m, n); + } // End of Else found a nested loop + if( !has_loop(m) ) // If 'm' does not already have a loop set + set_loop(m, l); // Set loop header to loop now + + } else { // Else not a nested loop + if( !_nodes[m->_idx] ) continue; // Dead code has no loop + l = get_loop(m); // Get previously determined loop + // If successor is header of a loop (nest), move up-loop till it + // is a member of some outer enclosing loop. Since there are no + // shared headers (I've split them already) I only need to go up + // at most 1 level. + while( l && l->_head == m ) // Successor heads loop? + l = l->_parent; // Move up 1 for me + // If this loop is not properly parented, then this loop + // has no exit path out, i.e. its an infinite loop. + if( !l ) { + // Make loop "reachable" from root so the CFG is reachable. Basically + // insert a bogus loop exit that is never taken. 'm', the loop head, + // points to 'n', one (of possibly many) fall-in paths. There may be + // many backedges as well. + + // Here I set the loop to be the root loop. I could have, after + // inserting a bogus loop exit, restarted the recursion and found my + // new loop exit. This would make the infinite loop a first-class + // loop and it would then get properly optimized. What's the use of + // optimizing an infinite loop? + l = _ltree_root; // Oops, found infinite loop + + // Insert the NeverBranch between 'm' and it's control user. + NeverBranchNode *iff = new (C, 1) NeverBranchNode( m ); + _igvn.register_new_node_with_optimizer(iff); + set_loop(iff, l); + Node *if_t = new (C, 1) CProjNode( iff, 0 ); + _igvn.register_new_node_with_optimizer(if_t); + set_loop(if_t, l); + + Node* cfg = NULL; // Find the One True Control User of m + for (DUIterator_Fast jmax, j = m->fast_outs(jmax); j < jmax; j++) { + Node* x = m->fast_out(j); + if (x->is_CFG() && x != m && x != iff) + { cfg = x; break; } + } + assert(cfg != NULL, "must find the control user of m"); + uint k = 0; // Probably cfg->in(0) + while( cfg->in(k) != m ) k++; // But check incase cfg is a Region + cfg->set_req( k, if_t ); // Now point to NeverBranch + + // Now create the never-taken loop exit + Node *if_f = new (C, 1) CProjNode( iff, 1 ); + _igvn.register_new_node_with_optimizer(if_f); + set_loop(if_f, l); + // Find frame ptr for Halt. Relies on the optimizer + // V-N'ing. Easier and quicker than searching through + // the program structure. + Node *frame = new (C, 1) ParmNode( C->start(), TypeFunc::FramePtr ); + _igvn.register_new_node_with_optimizer(frame); + // Halt & Catch Fire + Node *halt = new (C, TypeFunc::Parms) HaltNode( if_f, frame ); + _igvn.register_new_node_with_optimizer(halt); + set_loop(halt, l); + C->root()->add_req(halt); + set_loop(C->root(), _ltree_root); + } + } + // Weeny check for irreducible. This child was already visited (this + // IS the post-work phase). Is this child's loop header post-visited + // as well? If so, then I found another entry into the loop. + while( is_postvisited(l->_head) ) { + // found irreducible + l->_irreducible = true; + l = l->_parent; + _has_irreducible_loops = true; + // Check for bad CFG here to prevent crash, and bailout of compile + if (l == NULL) { + C->record_method_not_compilable("unhandled CFG detected during loop optimization"); + return pre_order; + } + } + + // This Node might be a decision point for loops. It is only if + // it's children belong to several different loops. The sort call + // does a trivial amount of work if there is only 1 child or all + // children belong to the same loop. If however, the children + // belong to different loops, the sort call will properly set the + // _parent pointers to show how the loops nest. + // + // In any case, it returns the tightest enclosing loop. + innermost = sort( l, innermost ); + } + + // Def-use info will have some dead stuff; dead stuff will have no + // loop decided on. + + // Am I a loop header? If so fix up my parent's child and next ptrs. + if( innermost && innermost->_head == n ) { + assert( get_loop(n) == innermost, "" ); + IdealLoopTree *p = innermost->_parent; + IdealLoopTree *l = innermost; + while( p && l->_head == n ) { + l->_next = p->_child; // Put self on parents 'next child' + p->_child = l; // Make self as first child of parent + l = p; // Now walk up the parent chain + p = l->_parent; + } + } else { + // Note that it is possible for a LoopNode to reach here, if the + // backedge has been made unreachable (hence the LoopNode no longer + // denotes a Loop, and will eventually be removed). + + // Record tightest enclosing loop for self. Mark as post-visited. + set_loop(n, innermost); + // Also record has_call flag early on + if( innermost ) { + if( n->is_Call() && !n->is_CallLeaf() && !n->is_macro() ) { + // Do not count uncommon calls + if( !n->is_CallStaticJava() || !n->as_CallStaticJava()->_name ) { + Node *iff = n->in(0)->in(0); + if( !iff->is_If() || + (n->in(0)->Opcode() == Op_IfFalse && + (1.0 - iff->as_If()->_prob) >= 0.01) || + (iff->as_If()->_prob >= 0.01) ) + innermost->_has_call = 1; + } + } + } + } + + // Flag as post-visited now + set_postvisited(n); + return pre_order; +} + + +//------------------------------build_loop_early------------------------------- +// Put Data nodes into some loop nest, by setting the _nodes[]->loop mapping. +// First pass computes the earliest controlling node possible. This is the +// controlling input with the deepest dominating depth. +void PhaseIdealLoop::build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack, const PhaseIdealLoop *verify_me ) { + while (worklist.size() != 0) { + // Use local variables nstack_top_n & nstack_top_i to cache values + // on nstack's top. + Node *nstack_top_n = worklist.pop(); + uint nstack_top_i = 0; +//while_nstack_nonempty: + while (true) { + // Get parent node and next input's index from stack's top. + Node *n = nstack_top_n; + uint i = nstack_top_i; + uint cnt = n->req(); // Count of inputs + if (i == 0) { // Pre-process the node. + if( has_node(n) && // Have either loop or control already? + !has_ctrl(n) ) { // Have loop picked out already? + // During "merge_many_backedges" we fold up several nested loops + // into a single loop. This makes the members of the original + // loop bodies pointing to dead loops; they need to move up + // to the new UNION'd larger loop. I set the _head field of these + // dead loops to NULL and the _parent field points to the owning + // loop. Shades of UNION-FIND algorithm. + IdealLoopTree *ilt; + while( !(ilt = get_loop(n))->_head ) { + // Normally I would use a set_loop here. But in this one special + // case, it is legal (and expected) to change what loop a Node + // belongs to. + _nodes.map(n->_idx, (Node*)(ilt->_parent) ); + } + // Remove safepoints ONLY if I've already seen I don't need one. + // (the old code here would yank a 2nd safepoint after seeing a + // first one, even though the 1st did not dominate in the loop body + // and thus could be avoided indefinitely) + if( !verify_me && ilt->_has_sfpt && n->Opcode() == Op_SafePoint && + is_deleteable_safept(n)) { + Node *in = n->in(TypeFunc::Control); + lazy_replace(n,in); // Pull safepoint now + // Carry on with the recursion "as if" we are walking + // only the control input + if( !visited.test_set( in->_idx ) ) { + worklist.push(in); // Visit this guy later, using worklist + } + // Get next node from nstack: + // - skip n's inputs processing by setting i > cnt; + // - we also will not call set_early_ctrl(n) since + // has_node(n) == true (see the condition above). + i = cnt + 1; + } + } + } // if (i == 0) + + // Visit all inputs + bool done = true; // Assume all n's inputs will be processed + while (i < cnt) { + Node *in = n->in(i); + ++i; + if (in == NULL) continue; + if (in->pinned() && !in->is_CFG()) + set_ctrl(in, in->in(0)); + int is_visited = visited.test_set( in->_idx ); + if (!has_node(in)) { // No controlling input yet? + assert( !in->is_CFG(), "CFG Node with no controlling input?" ); + assert( !is_visited, "visit only once" ); + nstack.push(n, i); // Save parent node and next input's index. + nstack_top_n = in; // Process current input now. + nstack_top_i = 0; + done = false; // Not all n's inputs processed. + break; // continue while_nstack_nonempty; + } else if (!is_visited) { + // This guy has a location picked out for him, but has not yet + // been visited. Happens to all CFG nodes, for instance. + // Visit him using the worklist instead of recursion, to break + // cycles. Since he has a location already we do not need to + // find his location before proceeding with the current Node. + worklist.push(in); // Visit this guy later, using worklist + } + } + if (done) { + // All of n's inputs have been processed, complete post-processing. + + // Compute earilest point this Node can go. + // CFG, Phi, pinned nodes already know their controlling input. + if (!has_node(n)) { + // Record earliest legal location + set_early_ctrl( n ); + } + if (nstack.is_empty()) { + // Finished all nodes on stack. + // Process next node on the worklist. + break; + } + // Get saved parent node and next input's index. + nstack_top_n = nstack.node(); + nstack_top_i = nstack.index(); + nstack.pop(); + } + } // while (true) + } +} + +//------------------------------dom_lca_internal-------------------------------- +// Pair-wise LCA +Node *PhaseIdealLoop::dom_lca_internal( Node *n1, Node *n2 ) const { + if( !n1 ) return n2; // Handle NULL original LCA + assert( n1->is_CFG(), "" ); + assert( n2->is_CFG(), "" ); + // find LCA of all uses + uint d1 = dom_depth(n1); + uint d2 = dom_depth(n2); + while (n1 != n2) { + if (d1 > d2) { + n1 = idom(n1); + d1 = dom_depth(n1); + } else if (d1 < d2) { + n2 = idom(n2); + d2 = dom_depth(n2); + } else { + // Here d1 == d2. Due to edits of the dominator-tree, sections + // of the tree might have the same depth. These sections have + // to be searched more carefully. + + // Scan up all the n1's with equal depth, looking for n2. + Node *t1 = idom(n1); + while (dom_depth(t1) == d1) { + if (t1 == n2) return n2; + t1 = idom(t1); + } + // Scan up all the n2's with equal depth, looking for n1. + Node *t2 = idom(n2); + while (dom_depth(t2) == d2) { + if (t2 == n1) return n1; + t2 = idom(t2); + } + // Move up to a new dominator-depth value as well as up the dom-tree. + n1 = t1; + n2 = t2; + d1 = dom_depth(n1); + d2 = dom_depth(n2); + } + } + return n1; +} + +//------------------------------compute_idom----------------------------------- +// Locally compute IDOM using dom_lca call. Correct only if the incoming +// IDOMs are correct. +Node *PhaseIdealLoop::compute_idom( Node *region ) const { + assert( region->is_Region(), "" ); + Node *LCA = NULL; + for( uint i = 1; i < region->req(); i++ ) { + if( region->in(i) != C->top() ) + LCA = dom_lca( LCA, region->in(i) ); + } + return LCA; +} + +//------------------------------get_late_ctrl---------------------------------- +// Compute latest legal control. +Node *PhaseIdealLoop::get_late_ctrl( Node *n, Node *early ) { + assert(early != NULL, "early control should not be NULL"); + + // Compute LCA over list of uses + Node *LCA = NULL; + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax && LCA != early; i++) { + Node* c = n->fast_out(i); + if (_nodes[c->_idx] == NULL) + continue; // Skip the occasional dead node + if( c->is_Phi() ) { // For Phis, we must land above on the path + for( uint j=1; jreq(); j++ ) {// For all inputs + if( c->in(j) == n ) { // Found matching input? + Node *use = c->in(0)->in(j); + LCA = dom_lca_for_get_late_ctrl( LCA, use, n ); + } + } + } else { + // For CFG data-users, use is in the block just prior + Node *use = has_ctrl(c) ? get_ctrl(c) : c->in(0); + LCA = dom_lca_for_get_late_ctrl( LCA, use, n ); + } + } + + // if this is a load, check for anti-dependent stores + // We use a conservative algorithm to identify potential interfering + // instructions and for rescheduling the load. The users of the memory + // input of this load are examined. Any use which is not a load and is + // dominated by early is considered a potentially interfering store. + // This can produce false positives. + if (n->is_Load() && LCA != early) { + Node_List worklist; + + Node *mem = n->in(MemNode::Memory); + for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) { + Node* s = mem->fast_out(i); + worklist.push(s); + } + while(worklist.size() != 0 && LCA != early) { + Node* s = worklist.pop(); + if (s->is_Load()) { + continue; + } else if (s->is_MergeMem()) { + for (DUIterator_Fast imax, i = s->fast_outs(imax); i < imax; i++) { + Node* s1 = s->fast_out(i); + worklist.push(s1); + } + } else { + Node *sctrl = has_ctrl(s) ? get_ctrl(s) : s->in(0); + assert(sctrl != NULL || s->outcnt() == 0, "must have control"); + if (sctrl != NULL && !sctrl->is_top() && is_dominator(early, sctrl)) { + LCA = dom_lca_for_get_late_ctrl(LCA, sctrl, n); + } + } + } + } + + assert(LCA == find_non_split_ctrl(LCA), "unexpected late control"); + return LCA; +} + +// true if CFG node d dominates CFG node n +bool PhaseIdealLoop::is_dominator(Node *d, Node *n) { + if (d == n) + return true; + assert(d->is_CFG() && n->is_CFG(), "must have CFG nodes"); + uint dd = dom_depth(d); + while (dom_depth(n) >= dd) { + if (n == d) + return true; + n = idom(n); + } + return false; +} + +//------------------------------dom_lca_for_get_late_ctrl_internal------------- +// Pair-wise LCA with tags. +// Tag each index with the node 'tag' currently being processed +// before advancing up the dominator chain using idom(). +// Later calls that find a match to 'tag' know that this path has already +// been considered in the current LCA (which is input 'n1' by convention). +// Since get_late_ctrl() is only called once for each node, the tag array +// does not need to be cleared between calls to get_late_ctrl(). +// Algorithm trades a larger constant factor for better asymptotic behavior +// +Node *PhaseIdealLoop::dom_lca_for_get_late_ctrl_internal( Node *n1, Node *n2, Node *tag ) { + uint d1 = dom_depth(n1); + uint d2 = dom_depth(n2); + + do { + if (d1 > d2) { + // current lca is deeper than n2 + _dom_lca_tags.map(n1->_idx, tag); + n1 = idom(n1); + d1 = dom_depth(n1); + } else if (d1 < d2) { + // n2 is deeper than current lca + Node *memo = _dom_lca_tags[n2->_idx]; + if( memo == tag ) { + return n1; // Return the current LCA + } + _dom_lca_tags.map(n2->_idx, tag); + n2 = idom(n2); + d2 = dom_depth(n2); + } else { + // Here d1 == d2. Due to edits of the dominator-tree, sections + // of the tree might have the same depth. These sections have + // to be searched more carefully. + + // Scan up all the n1's with equal depth, looking for n2. + _dom_lca_tags.map(n1->_idx, tag); + Node *t1 = idom(n1); + while (dom_depth(t1) == d1) { + if (t1 == n2) return n2; + _dom_lca_tags.map(t1->_idx, tag); + t1 = idom(t1); + } + // Scan up all the n2's with equal depth, looking for n1. + _dom_lca_tags.map(n2->_idx, tag); + Node *t2 = idom(n2); + while (dom_depth(t2) == d2) { + if (t2 == n1) return n1; + _dom_lca_tags.map(t2->_idx, tag); + t2 = idom(t2); + } + // Move up to a new dominator-depth value as well as up the dom-tree. + n1 = t1; + n2 = t2; + d1 = dom_depth(n1); + d2 = dom_depth(n2); + } + } while (n1 != n2); + return n1; +} + +//------------------------------init_dom_lca_tags------------------------------ +// Tag could be a node's integer index, 32bits instead of 64bits in some cases +// Intended use does not involve any growth for the array, so it could +// be of fixed size. +void PhaseIdealLoop::init_dom_lca_tags() { + uint limit = C->unique() + 1; + _dom_lca_tags.map( limit, NULL ); +#ifdef ASSERT + for( uint i = 0; i < limit; ++i ) { + assert(_dom_lca_tags[i] == NULL, "Must be distinct from each node pointer"); + } +#endif // ASSERT +} + +//------------------------------clear_dom_lca_tags------------------------------ +// Tag could be a node's integer index, 32bits instead of 64bits in some cases +// Intended use does not involve any growth for the array, so it could +// be of fixed size. +void PhaseIdealLoop::clear_dom_lca_tags() { + uint limit = C->unique() + 1; + _dom_lca_tags.map( limit, NULL ); + _dom_lca_tags.clear(); +#ifdef ASSERT + for( uint i = 0; i < limit; ++i ) { + assert(_dom_lca_tags[i] == NULL, "Must be distinct from each node pointer"); + } +#endif // ASSERT +} + +//------------------------------build_loop_late-------------------------------- +// Put Data nodes into some loop nest, by setting the _nodes[]->loop mapping. +// Second pass finds latest legal placement, and ideal loop placement. +void PhaseIdealLoop::build_loop_late( VectorSet &visited, Node_List &worklist, Node_Stack &nstack, const PhaseIdealLoop *verify_me ) { + while (worklist.size() != 0) { + Node *n = worklist.pop(); + // Only visit once + if (visited.test_set(n->_idx)) continue; + uint cnt = n->outcnt(); + uint i = 0; + while (true) { + assert( _nodes[n->_idx], "no dead nodes" ); + // Visit all children + if (i < cnt) { + Node* use = n->raw_out(i); + ++i; + // Check for dead uses. Aggressively prune such junk. It might be + // dead in the global sense, but still have local uses so I cannot + // easily call 'remove_dead_node'. + if( _nodes[use->_idx] != NULL || use->is_top() ) { // Not dead? + // Due to cycles, we might not hit the same fixed point in the verify + // pass as we do in the regular pass. Instead, visit such phis as + // simple uses of the loop head. + if( use->in(0) && (use->is_CFG() || use->is_Phi()) ) { + if( !visited.test(use->_idx) ) + worklist.push(use); + } else if( !visited.test_set(use->_idx) ) { + nstack.push(n, i); // Save parent and next use's index. + n = use; // Process all children of current use. + cnt = use->outcnt(); + i = 0; + } + } else { + // Do not visit around the backedge of loops via data edges. + // push dead code onto a worklist + _deadlist.push(use); + } + } else { + // All of n's children have been processed, complete post-processing. + build_loop_late_post(n, verify_me); + if (nstack.is_empty()) { + // Finished all nodes on stack. + // Process next node on the worklist. + break; + } + // Get saved parent node and next use's index. Visit the rest of uses. + n = nstack.node(); + cnt = n->outcnt(); + i = nstack.index(); + nstack.pop(); + } + } + } +} + +//------------------------------build_loop_late_post--------------------------- +// Put Data nodes into some loop nest, by setting the _nodes[]->loop mapping. +// Second pass finds latest legal placement, and ideal loop placement. +void PhaseIdealLoop::build_loop_late_post( Node *n, const PhaseIdealLoop *verify_me ) { + + if (n->req() == 2 && n->Opcode() == Op_ConvI2L && !C->major_progress()) { + _igvn._worklist.push(n); // Maybe we'll normalize it, if no more loops. + } + + // CFG and pinned nodes already handled + if( n->in(0) ) { + if( n->in(0)->is_top() ) return; // Dead? + + // We'd like +VerifyLoopOptimizations to not believe that Mod's/Loads + // _must_ be pinned (they have to observe their control edge of course). + // Unlike Stores (which modify an unallocable resource, the memory + // state), Mods/Loads can float around. So free them up. + bool pinned = true; + switch( n->Opcode() ) { + case Op_DivI: + case Op_DivF: + case Op_DivD: + case Op_ModI: + case Op_ModF: + case Op_ModD: + case Op_LoadB: // Same with Loads; they can sink + case Op_LoadC: // during loop optimizations. + case Op_LoadD: + case Op_LoadF: + case Op_LoadI: + case Op_LoadKlass: + case Op_LoadL: + case Op_LoadS: + case Op_LoadP: + case Op_LoadRange: + case Op_LoadD_unaligned: + case Op_LoadL_unaligned: + case Op_StrComp: // Does a bunch of load-like effects + pinned = false; + } + if( pinned ) { + IdealLoopTree *choosen_loop = get_loop(n->is_CFG() ? n : get_ctrl(n)); + if( !choosen_loop->_child ) // Inner loop? + choosen_loop->_body.push(n); // Collect inner loops + return; + } + } else { // No slot zero + if( n->is_CFG() ) { // CFG with no slot 0 is dead + _nodes.map(n->_idx,0); // No block setting, it's globally dead + return; + } + assert(!n->is_CFG() || n->outcnt() == 0, ""); + } + + // Do I have a "safe range" I can select over? + Node *early = get_ctrl(n);// Early location already computed + + // Compute latest point this Node can go + Node *LCA = get_late_ctrl( n, early ); + // LCA is NULL due to uses being dead + if( LCA == NULL ) { +#ifdef ASSERT + for (DUIterator i1 = n->outs(); n->has_out(i1); i1++) { + assert( _nodes[n->out(i1)->_idx] == NULL, "all uses must also be dead"); + } +#endif + _nodes.map(n->_idx, 0); // This node is useless + _deadlist.push(n); + return; + } + assert(LCA != NULL && !LCA->is_top(), "no dead nodes"); + + Node *legal = LCA; // Walk 'legal' up the IDOM chain + Node *least = legal; // Best legal position so far + while( early != legal ) { // While not at earliest legal + // Find least loop nesting depth + legal = idom(legal); // Bump up the IDOM tree + // Check for lower nesting depth + if( get_loop(legal)->_nest < get_loop(least)->_nest ) + least = legal; + } + + // Try not to place code on a loop entry projection + // which can inhibit range check elimination. + if (least != early) { + Node* ctrl_out = least->unique_ctrl_out(); + if (ctrl_out && ctrl_out->is_CountedLoop() && + least == ctrl_out->in(LoopNode::EntryControl)) { + Node* least_dom = idom(least); + if (get_loop(least_dom)->is_member(get_loop(least))) { + least = least_dom; + } + } + } + +#ifdef ASSERT + // If verifying, verify that 'verify_me' has a legal location + // and choose it as our location. + if( verify_me ) { + Node *v_ctrl = verify_me->get_ctrl_no_update(n); + Node *legal = LCA; + while( early != legal ) { // While not at earliest legal + if( legal == v_ctrl ) break; // Check for prior good location + legal = idom(legal) ;// Bump up the IDOM tree + } + // Check for prior good location + if( legal == v_ctrl ) least = legal; // Keep prior if found + } +#endif + + // Assign discovered "here or above" point + least = find_non_split_ctrl(least); + set_ctrl(n, least); + + // Collect inner loop bodies + IdealLoopTree *choosen_loop = get_loop(least); + if( !choosen_loop->_child ) // Inner loop? + choosen_loop->_body.push(n);// Collect inner loops +} + +#ifndef PRODUCT +//------------------------------dump------------------------------------------- +void PhaseIdealLoop::dump( ) const { + ResourceMark rm; + Arena* arena = Thread::current()->resource_area(); + Node_Stack stack(arena, C->unique() >> 2); + Node_List rpo_list; + VectorSet visited(arena); + visited.set(C->top()->_idx); + rpo( C->root(), stack, visited, rpo_list ); + // Dump root loop indexed by last element in PO order + dump( _ltree_root, rpo_list.size(), rpo_list ); +} + +void PhaseIdealLoop::dump( IdealLoopTree *loop, uint idx, Node_List &rpo_list ) const { + + // Indent by loop nesting depth + for( uint x = 0; x < loop->_nest; x++ ) + tty->print(" "); + tty->print_cr("---- Loop N%d-N%d ----", loop->_head->_idx,loop->_tail->_idx); + + // Now scan for CFG nodes in the same loop + for( uint j=idx; j > 0; j-- ) { + Node *n = rpo_list[j-1]; + if( !_nodes[n->_idx] ) // Skip dead nodes + continue; + if( get_loop(n) != loop ) { // Wrong loop nest + if( get_loop(n)->_head == n && // Found nested loop? + get_loop(n)->_parent == loop ) + dump(get_loop(n),rpo_list.size(),rpo_list); // Print it nested-ly + continue; + } + + // Dump controlling node + for( uint x = 0; x < loop->_nest; x++ ) + tty->print(" "); + tty->print("C"); + if( n == C->root() ) { + n->dump(); + } else { + Node* cached_idom = idom_no_update(n); + Node *computed_idom = n->in(0); + if( n->is_Region() ) { + computed_idom = compute_idom(n); + // computed_idom() will return n->in(0) when idom(n) is an IfNode (or + // any MultiBranch ctrl node), so apply a similar transform to + // the cached idom returned from idom_no_update. + cached_idom = find_non_split_ctrl(cached_idom); + } + tty->print(" ID:%d",computed_idom->_idx); + n->dump(); + if( cached_idom != computed_idom ) { + tty->print_cr("*** BROKEN IDOM! Computed as: %d, cached as: %d", + computed_idom->_idx, cached_idom->_idx); + } + } + // Dump nodes it controls + for( uint k = 0; k < _nodes.Size(); k++ ) { + // (k < C->unique() && get_ctrl(find(k)) == n) + if (k < C->unique() && _nodes[k] == (Node*)((intptr_t)n + 1)) { + Node *m = C->root()->find(k); + if( m && m->outcnt() > 0 ) { + if (!(has_ctrl(m) && get_ctrl_no_update(m) == n)) { + tty->print_cr("*** BROKEN CTRL ACCESSOR! _nodes[k] is %p, ctrl is %p", + _nodes[k], has_ctrl(m) ? get_ctrl_no_update(m) : NULL); + } + for( uint j = 0; j < loop->_nest; j++ ) + tty->print(" "); + tty->print(" "); + m->dump(); + } + } + } + } +} + +// Collect a R-P-O for the whole CFG. +// Result list is in post-order (scan backwards for RPO) +void PhaseIdealLoop::rpo( Node *start, Node_Stack &stk, VectorSet &visited, Node_List &rpo_list ) const { + stk.push(start, 0); + visited.set(start->_idx); + + while (stk.is_nonempty()) { + Node* m = stk.node(); + uint idx = stk.index(); + if (idx < m->outcnt()) { + stk.set_index(idx + 1); + Node* n = m->raw_out(idx); + if (n->is_CFG() && !visited.test_set(n->_idx)) { + stk.push(n, 0); + } + } else { + rpo_list.push(m); + stk.pop(); + } + } +} +#endif + + +//============================================================================= +//------------------------------LoopTreeIterator----------------------------------- + +// Advance to next loop tree using a preorder, left-to-right traversal. +void LoopTreeIterator::next() { + assert(!done(), "must not be done."); + if (_curnt->_child != NULL) { + _curnt = _curnt->_child; + } else if (_curnt->_next != NULL) { + _curnt = _curnt->_next; + } else { + while (_curnt != _root && _curnt->_next == NULL) { + _curnt = _curnt->_parent; + } + if (_curnt == _root) { + _curnt = NULL; + assert(done(), "must be done."); + } else { + assert(_curnt->_next != NULL, "must be more to do"); + _curnt = _curnt->_next; + } + } +} diff --git a/hotspot/src/share/vm/opto/loopnode.hpp b/hotspot/src/share/vm/opto/loopnode.hpp new file mode 100644 index 00000000000..21ddf801586 --- /dev/null +++ b/hotspot/src/share/vm/opto/loopnode.hpp @@ -0,0 +1,919 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class CmpNode; +class CountedLoopEndNode; +class CountedLoopNode; +class IdealLoopTree; +class LoopNode; +class Node; +class PhaseIdealLoop; +class VectorSet; +struct small_cache; + +// +// I D E A L I Z E D L O O P S +// +// Idealized loops are the set of loops I perform more interesting +// transformations on, beyond simple hoisting. + +//------------------------------LoopNode--------------------------------------- +// Simple loop header. Fall in path on left, loop-back path on right. +class LoopNode : public RegionNode { + // Size is bigger to hold the flags. However, the flags do not change + // the semantics so it does not appear in the hash & cmp functions. + virtual uint size_of() const { return sizeof(*this); } +protected: + short _loop_flags; + // Names for flag bitfields + enum { pre_post_main=0, inner_loop=8, partial_peel_loop=16, partial_peel_failed=32 }; + char _unswitch_count; + enum { _unswitch_max=3 }; + +public: + // Names for edge indices + enum { Self=0, EntryControl, LoopBackControl }; + + int is_inner_loop() const { return _loop_flags & inner_loop; } + void set_inner_loop() { _loop_flags |= inner_loop; } + + int is_partial_peel_loop() const { return _loop_flags & partial_peel_loop; } + void set_partial_peel_loop() { _loop_flags |= partial_peel_loop; } + int partial_peel_has_failed() const { return _loop_flags & partial_peel_failed; } + void mark_partial_peel_failed() { _loop_flags |= partial_peel_failed; } + + int unswitch_max() { return _unswitch_max; } + int unswitch_count() { return _unswitch_count; } + void set_unswitch_count(int val) { + assert (val <= unswitch_max(), "too many unswitches"); + _unswitch_count = val; + } + + LoopNode( Node *entry, Node *backedge ) : RegionNode(3), _loop_flags(0), _unswitch_count(0) { + init_class_id(Class_Loop); + init_req(EntryControl, entry); + init_req(LoopBackControl, backedge); + } + + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual int Opcode() const; + bool can_be_counted_loop(PhaseTransform* phase) const { + return req() == 3 && in(0) != NULL && + in(1) != NULL && phase->type(in(1)) != Type::TOP && + in(2) != NULL && phase->type(in(2)) != Type::TOP; + } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------Counted Loops---------------------------------- +// Counted loops are all trip-counted loops, with exactly 1 trip-counter exit +// path (and maybe some other exit paths). The trip-counter exit is always +// last in the loop. The trip-counter does not have to stride by a constant, +// but it does have to stride by a loop-invariant amount; the exit value is +// also loop invariant. + +// CountedLoopNodes and CountedLoopEndNodes come in matched pairs. The +// CountedLoopNode has the incoming loop control and the loop-back-control +// which is always the IfTrue before the matching CountedLoopEndNode. The +// CountedLoopEndNode has an incoming control (possibly not the +// CountedLoopNode if there is control flow in the loop), the post-increment +// trip-counter value, and the limit. The trip-counter value is always of +// the form (Op old-trip-counter stride). The old-trip-counter is produced +// by a Phi connected to the CountedLoopNode. The stride is loop invariant. +// The Op is any commutable opcode, including Add, Mul, Xor. The +// CountedLoopEndNode also takes in the loop-invariant limit value. + +// From a CountedLoopNode I can reach the matching CountedLoopEndNode via the +// loop-back control. From CountedLoopEndNodes I can reach CountedLoopNodes +// via the old-trip-counter from the Op node. + +//------------------------------CountedLoopNode-------------------------------- +// CountedLoopNodes head simple counted loops. CountedLoopNodes have as +// inputs the incoming loop-start control and the loop-back control, so they +// act like RegionNodes. They also take in the initial trip counter, the +// loop-invariant stride and the loop-invariant limit value. CountedLoopNodes +// produce a loop-body control and the trip counter value. Since +// CountedLoopNodes behave like RegionNodes I still have a standard CFG model. + +class CountedLoopNode : public LoopNode { + // Size is bigger to hold _main_idx. However, _main_idx does not change + // the semantics so it does not appear in the hash & cmp functions. + virtual uint size_of() const { return sizeof(*this); } + + // For Pre- and Post-loops during debugging ONLY, this holds the index of + // the Main CountedLoop. Used to assert that we understand the graph shape. + node_idx_t _main_idx; + + // Known trip count calculated by policy_maximally_unroll + int _trip_count; + + // Expected trip count from profile data + float _profile_trip_cnt; + + // Log2 of original loop bodies in unrolled loop + int _unrolled_count_log2; + + // Node count prior to last unrolling - used to decide if + // unroll,optimize,unroll,optimize,... is making progress + int _node_count_before_unroll; + +public: + CountedLoopNode( Node *entry, Node *backedge ) + : LoopNode(entry, backedge), _trip_count(max_jint), + _profile_trip_cnt(COUNT_UNKNOWN), _unrolled_count_log2(0), + _node_count_before_unroll(0) { + init_class_id(Class_CountedLoop); + // Initialize _trip_count to the largest possible value. + // Will be reset (lower) if the loop's trip count is known. + } + + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + + Node *init_control() const { return in(EntryControl); } + Node *back_control() const { return in(LoopBackControl); } + CountedLoopEndNode *loopexit() const; + Node *init_trip() const; + Node *stride() const; + int stride_con() const; + bool stride_is_con() const; + Node *limit() const; + Node *incr() const; + Node *phi() const; + + // Match increment with optional truncation + static Node* match_incr_with_optional_truncation(Node* expr, Node** trunc1, Node** trunc2, const TypeInt** trunc_type); + + // A 'main' loop has a pre-loop and a post-loop. The 'main' loop + // can run short a few iterations and may start a few iterations in. + // It will be RCE'd and unrolled and aligned. + + // A following 'post' loop will run any remaining iterations. Used + // during Range Check Elimination, the 'post' loop will do any final + // iterations with full checks. Also used by Loop Unrolling, where + // the 'post' loop will do any epilog iterations needed. Basically, + // a 'post' loop can not profitably be further unrolled or RCE'd. + + // A preceding 'pre' loop will run at least 1 iteration (to do peeling), + // it may do under-flow checks for RCE and may do alignment iterations + // so the following main loop 'knows' that it is striding down cache + // lines. + + // A 'main' loop that is ONLY unrolled or peeled, never RCE'd or + // Aligned, may be missing it's pre-loop. + enum { Normal=0, Pre=1, Main=2, Post=3, PrePostFlagsMask=3, Main_Has_No_Pre_Loop=4 }; + int is_normal_loop() const { return (_loop_flags&PrePostFlagsMask) == Normal; } + int is_pre_loop () const { return (_loop_flags&PrePostFlagsMask) == Pre; } + int is_main_loop () const { return (_loop_flags&PrePostFlagsMask) == Main; } + int is_post_loop () const { return (_loop_flags&PrePostFlagsMask) == Post; } + int is_main_no_pre_loop() const { return _loop_flags & Main_Has_No_Pre_Loop; } + void set_main_no_pre_loop() { _loop_flags |= Main_Has_No_Pre_Loop; } + + + void set_pre_loop (CountedLoopNode *main) { assert(is_normal_loop(),""); _loop_flags |= Pre ; _main_idx = main->_idx; } + void set_main_loop ( ) { assert(is_normal_loop(),""); _loop_flags |= Main; } + void set_post_loop (CountedLoopNode *main) { assert(is_normal_loop(),""); _loop_flags |= Post; _main_idx = main->_idx; } + void set_normal_loop( ) { _loop_flags &= ~PrePostFlagsMask; } + + void set_trip_count(int tc) { _trip_count = tc; } + int trip_count() { return _trip_count; } + + void set_profile_trip_cnt(float ptc) { _profile_trip_cnt = ptc; } + float profile_trip_cnt() { return _profile_trip_cnt; } + + void double_unrolled_count() { _unrolled_count_log2++; } + int unrolled_count() { return 1 << MIN2(_unrolled_count_log2, BitsPerInt-3); } + + void set_node_count_before_unroll(int ct) { _node_count_before_unroll = ct; } + int node_count_before_unroll() { return _node_count_before_unroll; } + +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------CountedLoopEndNode----------------------------- +// CountedLoopEndNodes end simple trip counted loops. They act much like +// IfNodes. +class CountedLoopEndNode : public IfNode { +public: + enum { TestControl, TestValue }; + + CountedLoopEndNode( Node *control, Node *test, float prob, float cnt ) + : IfNode( control, test, prob, cnt) { + init_class_id(Class_CountedLoopEnd); + } + virtual int Opcode() const; + + Node *cmp_node() const { return (in(TestValue)->req() >=2) ? in(TestValue)->in(1) : NULL; } + Node *incr() const { Node *tmp = cmp_node(); return (tmp && tmp->req()==3) ? tmp->in(1) : NULL; } + Node *limit() const { Node *tmp = cmp_node(); return (tmp && tmp->req()==3) ? tmp->in(2) : NULL; } + Node *stride() const { Node *tmp = incr (); return (tmp && tmp->req()==3) ? tmp->in(2) : NULL; } + Node *phi() const { Node *tmp = incr (); return (tmp && tmp->req()==3) ? tmp->in(1) : NULL; } + Node *init_trip() const { Node *tmp = phi (); return (tmp && tmp->req()==3) ? tmp->in(1) : NULL; } + int stride_con() const; + bool stride_is_con() const { Node *tmp = stride (); return (tmp != NULL && tmp->is_Con()); } + BoolTest::mask test_trip() const { return in(TestValue)->as_Bool()->_test._test; } + CountedLoopNode *loopnode() const { + Node *ln = phi()->in(0); + assert( ln->Opcode() == Op_CountedLoop, "malformed loop" ); + return (CountedLoopNode*)ln; } + +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + + +inline CountedLoopEndNode *CountedLoopNode::loopexit() const { + Node *bc = back_control(); + if( bc == NULL ) return NULL; + Node *le = bc->in(0); + if( le->Opcode() != Op_CountedLoopEnd ) + return NULL; + return (CountedLoopEndNode*)le; +} +inline Node *CountedLoopNode::init_trip() const { return loopexit() ? loopexit()->init_trip() : NULL; } +inline Node *CountedLoopNode::stride() const { return loopexit() ? loopexit()->stride() : NULL; } +inline int CountedLoopNode::stride_con() const { return loopexit() ? loopexit()->stride_con() : 0; } +inline bool CountedLoopNode::stride_is_con() const { return loopexit() && loopexit()->stride_is_con(); } +inline Node *CountedLoopNode::limit() const { return loopexit() ? loopexit()->limit() : NULL; } +inline Node *CountedLoopNode::incr() const { return loopexit() ? loopexit()->incr() : NULL; } +inline Node *CountedLoopNode::phi() const { return loopexit() ? loopexit()->phi() : NULL; } + + +// -----------------------------IdealLoopTree---------------------------------- +class IdealLoopTree : public ResourceObj { +public: + IdealLoopTree *_parent; // Parent in loop tree + IdealLoopTree *_next; // Next sibling in loop tree + IdealLoopTree *_child; // First child in loop tree + + // The head-tail backedge defines the loop. + // If tail is NULL then this loop has multiple backedges as part of the + // same loop. During cleanup I'll peel off the multiple backedges; merge + // them at the loop bottom and flow 1 real backedge into the loop. + Node *_head; // Head of loop + Node *_tail; // Tail of loop + inline Node *tail(); // Handle lazy update of _tail field + PhaseIdealLoop* _phase; + + Node_List _body; // Loop body for inner loops + + uint8 _nest; // Nesting depth + uint8 _irreducible:1, // True if irreducible + _has_call:1, // True if has call safepoint + _has_sfpt:1, // True if has non-call safepoint + _rce_candidate:1; // True if candidate for range check elimination + + Node_List* _required_safept; // A inner loop cannot delete these safepts; + + IdealLoopTree( PhaseIdealLoop* phase, Node *head, Node *tail ) + : _parent(0), _next(0), _child(0), + _head(head), _tail(tail), + _phase(phase), + _required_safept(NULL), + _nest(0), _irreducible(0), _has_call(0), _has_sfpt(0), _rce_candidate(0) + { } + + // Is 'l' a member of 'this'? + int is_member( const IdealLoopTree *l ) const; // Test for nested membership + + // Set loop nesting depth. Accumulate has_call bits. + int set_nest( uint depth ); + + // Split out multiple fall-in edges from the loop header. Move them to a + // private RegionNode before the loop. This becomes the loop landing pad. + void split_fall_in( PhaseIdealLoop *phase, int fall_in_cnt ); + + // Split out the outermost loop from this shared header. + void split_outer_loop( PhaseIdealLoop *phase ); + + // Merge all the backedges from the shared header into a private Region. + // Feed that region as the one backedge to this loop. + void merge_many_backedges( PhaseIdealLoop *phase ); + + // Split shared headers and insert loop landing pads. + // Insert a LoopNode to replace the RegionNode. + // Returns TRUE if loop tree is structurally changed. + bool beautify_loops( PhaseIdealLoop *phase ); + + // Perform iteration-splitting on inner loops. Split iterations to avoid + // range checks or one-shot null checks. + void iteration_split( PhaseIdealLoop *phase, Node_List &old_new ); + + // Driver for various flavors of iteration splitting + void iteration_split_impl( PhaseIdealLoop *phase, Node_List &old_new ); + + // Given dominators, try to find loops with calls that must always be + // executed (call dominates loop tail). These loops do not need non-call + // safepoints (ncsfpt). + void check_safepts(VectorSet &visited, Node_List &stack); + + // Allpaths backwards scan from loop tail, terminating each path at first safepoint + // encountered. + void allpaths_check_safepts(VectorSet &visited, Node_List &stack); + + // Convert to counted loops where possible + void counted_loop( PhaseIdealLoop *phase ); + + // Check for Node being a loop-breaking test + Node *is_loop_exit(Node *iff) const; + + // Returns true if ctrl is executed on every complete iteration + bool dominates_backedge(Node* ctrl); + + // Remove simplistic dead code from loop body + void DCE_loop_body(); + + // Look for loop-exit tests with my 50/50 guesses from the Parsing stage. + // Replace with a 1-in-10 exit guess. + void adjust_loop_exit_prob( PhaseIdealLoop *phase ); + + // Return TRUE or FALSE if the loop should never be RCE'd or aligned. + // Useful for unrolling loops with NO array accesses. + bool policy_peel_only( PhaseIdealLoop *phase ) const; + + // Return TRUE or FALSE if the loop should be unswitched -- clone + // loop with an invariant test + bool policy_unswitching( PhaseIdealLoop *phase ) const; + + // Micro-benchmark spamming. Remove empty loops. + bool policy_do_remove_empty_loop( PhaseIdealLoop *phase ); + + // Return TRUE or FALSE if the loop should be peeled or not. Peel if we can + // make some loop-invariant test (usually a null-check) happen before the + // loop. + bool policy_peeling( PhaseIdealLoop *phase ) const; + + // Return TRUE or FALSE if the loop should be maximally unrolled. Stash any + // known trip count in the counted loop node. + bool policy_maximally_unroll( PhaseIdealLoop *phase ) const; + + // Return TRUE or FALSE if the loop should be unrolled or not. Unroll if + // the loop is a CountedLoop and the body is small enough. + bool policy_unroll( PhaseIdealLoop *phase ) const; + + // Return TRUE or FALSE if the loop should be range-check-eliminated. + // Gather a list of IF tests that are dominated by iteration splitting; + // also gather the end of the first split and the start of the 2nd split. + bool policy_range_check( PhaseIdealLoop *phase ) const; + + // Return TRUE or FALSE if the loop should be cache-line aligned. + // Gather the expression that does the alignment. Note that only + // one array base can be aligned in a loop (unless the VM guarentees + // mutual alignment). Note that if we vectorize short memory ops + // into longer memory ops, we may want to increase alignment. + bool policy_align( PhaseIdealLoop *phase ) const; + + // Compute loop trip count from profile data + void compute_profile_trip_cnt( PhaseIdealLoop *phase ); + + // Reassociate invariant expressions. + void reassociate_invariants(PhaseIdealLoop *phase); + // Reassociate invariant add and subtract expressions. + Node* reassociate_add_sub(Node* n1, PhaseIdealLoop *phase); + // Return nonzero index of invariant operand if invariant and variant + // are combined with an Add or Sub. Helper for reassoicate_invariants. + int is_invariant_addition(Node* n, PhaseIdealLoop *phase); + + // Return true if n is invariant + bool is_invariant(Node* n) const; + + // Put loop body on igvn work list + void record_for_igvn(); + + bool is_loop() { return !_irreducible && _tail && !_tail->is_top(); } + bool is_inner() { return is_loop() && _child == NULL; } + bool is_counted() { return is_loop() && _head != NULL && _head->is_CountedLoop(); } + +#ifndef PRODUCT + void dump_head( ) const; // Dump loop head only + void dump() const; // Dump this loop recursively + void verify_tree(IdealLoopTree *loop, const IdealLoopTree *parent) const; +#endif + +}; + +// -----------------------------PhaseIdealLoop--------------------------------- +// Computes the mapping from Nodes to IdealLoopTrees. Organizes IdealLoopTrees into a +// loop tree. Drives the loop-based transformations on the ideal graph. +class PhaseIdealLoop : public PhaseTransform { + friend class IdealLoopTree; + friend class SuperWord; + // Pre-computed def-use info + PhaseIterGVN &_igvn; + + // Head of loop tree + IdealLoopTree *_ltree_root; + + // Array of pre-order numbers, plus post-visited bit. + // ZERO for not pre-visited. EVEN for pre-visited but not post-visited. + // ODD for post-visited. Other bits are the pre-order number. + uint *_preorders; + uint _max_preorder; + + // Allocate _preorders[] array + void allocate_preorders() { + _max_preorder = C->unique()+8; + _preorders = NEW_RESOURCE_ARRAY(uint, _max_preorder); + memset(_preorders, 0, sizeof(uint) * _max_preorder); + } + + // Allocate _preorders[] array + void reallocate_preorders() { + if ( _max_preorder < C->unique() ) { + _preorders = REALLOC_RESOURCE_ARRAY(uint, _preorders, _max_preorder, C->unique()); + _max_preorder = C->unique(); + } + memset(_preorders, 0, sizeof(uint) * _max_preorder); + } + + // Check to grow _preorders[] array for the case when build_loop_tree_impl() + // adds new nodes. + void check_grow_preorders( ) { + if ( _max_preorder < C->unique() ) { + uint newsize = _max_preorder<<1; // double size of array + _preorders = REALLOC_RESOURCE_ARRAY(uint, _preorders, _max_preorder, newsize); + memset(&_preorders[_max_preorder],0,sizeof(uint)*(newsize-_max_preorder)); + _max_preorder = newsize; + } + } + // Check for pre-visited. Zero for NOT visited; non-zero for visited. + int is_visited( Node *n ) const { return _preorders[n->_idx]; } + // Pre-order numbers are written to the Nodes array as low-bit-set values. + void set_preorder_visited( Node *n, int pre_order ) { + assert( !is_visited( n ), "already set" ); + _preorders[n->_idx] = (pre_order<<1); + }; + // Return pre-order number. + int get_preorder( Node *n ) const { assert( is_visited(n), "" ); return _preorders[n->_idx]>>1; } + + // Check for being post-visited. + // Should be previsited already (checked with assert(is_visited(n))). + int is_postvisited( Node *n ) const { assert( is_visited(n), "" ); return _preorders[n->_idx]&1; } + + // Mark as post visited + void set_postvisited( Node *n ) { assert( !is_postvisited( n ), "" ); _preorders[n->_idx] |= 1; } + + // Set/get control node out. Set lower bit to distinguish from IdealLoopTree + // Returns true if "n" is a data node, false if it's a control node. + bool has_ctrl( Node *n ) const { return ((intptr_t)_nodes[n->_idx]) & 1; } + + // clear out dead code after build_loop_late + Node_List _deadlist; + + // Support for faster execution of get_late_ctrl()/dom_lca() + // when a node has many uses and dominator depth is deep. + Node_Array _dom_lca_tags; + void init_dom_lca_tags(); + void clear_dom_lca_tags(); + // Inline wrapper for frequent cases: + // 1) only one use + // 2) a use is the same as the current LCA passed as 'n1' + Node *dom_lca_for_get_late_ctrl( Node *lca, Node *n, Node *tag ) { + assert( n->is_CFG(), "" ); + // Fast-path NULL lca + if( lca != NULL && lca != n ) { + assert( lca->is_CFG(), "" ); + // find LCA of all uses + n = dom_lca_for_get_late_ctrl_internal( lca, n, tag ); + } + return find_non_split_ctrl(n); + } + Node *dom_lca_for_get_late_ctrl_internal( Node *lca, Node *n, Node *tag ); + // true if CFG node d dominates CFG node n + bool is_dominator(Node *d, Node *n); + + // Helper function for directing control inputs away from CFG split + // points. + Node *find_non_split_ctrl( Node *ctrl ) const { + if (ctrl != NULL) { + if (ctrl->is_MultiBranch()) { + ctrl = ctrl->in(0); + } + assert(ctrl->is_CFG(), "CFG"); + } + return ctrl; + } + +public: + bool has_node( Node* n ) const { return _nodes[n->_idx] != NULL; } + // check if transform created new nodes that need _ctrl recorded + Node *get_late_ctrl( Node *n, Node *early ); + Node *get_early_ctrl( Node *n ); + void set_early_ctrl( Node *n ); + void set_subtree_ctrl( Node *root ); + void set_ctrl( Node *n, Node *ctrl ) { + assert( !has_node(n) || has_ctrl(n), "" ); + assert( ctrl->in(0), "cannot set dead control node" ); + assert( ctrl == find_non_split_ctrl(ctrl), "must set legal crtl" ); + _nodes.map( n->_idx, (Node*)((intptr_t)ctrl + 1) ); + } + // Set control and update loop membership + void set_ctrl_and_loop(Node* n, Node* ctrl) { + IdealLoopTree* old_loop = get_loop(get_ctrl(n)); + IdealLoopTree* new_loop = get_loop(ctrl); + if (old_loop != new_loop) { + if (old_loop->_child == NULL) old_loop->_body.yank(n); + if (new_loop->_child == NULL) new_loop->_body.push(n); + } + set_ctrl(n, ctrl); + } + // Control nodes can be replaced or subsumed. During this pass they + // get their replacement Node in slot 1. Instead of updating the block + // location of all Nodes in the subsumed block, we lazily do it. As we + // pull such a subsumed block out of the array, we write back the final + // correct block. + Node *get_ctrl( Node *i ) { + assert(has_node(i), ""); + Node *n = get_ctrl_no_update(i); + _nodes.map( i->_idx, (Node*)((intptr_t)n + 1) ); + assert(has_node(i) && has_ctrl(i), ""); + assert(n == find_non_split_ctrl(n), "must return legal ctrl" ); + return n; + } + +private: + Node *get_ctrl_no_update( Node *i ) const { + assert( has_ctrl(i), "" ); + Node *n = (Node*)(((intptr_t)_nodes[i->_idx]) & ~1); + if (!n->in(0)) { + // Skip dead CFG nodes + do { + n = (Node*)(((intptr_t)_nodes[n->_idx]) & ~1); + } while (!n->in(0)); + n = find_non_split_ctrl(n); + } + return n; + } + + // Check for loop being set + // "n" must be a control node. Returns true if "n" is known to be in a loop. + bool has_loop( Node *n ) const { + assert(!has_node(n) || !has_ctrl(n), ""); + return has_node(n); + } + // Set loop + void set_loop( Node *n, IdealLoopTree *loop ) { + _nodes.map(n->_idx, (Node*)loop); + } + // Lazy-dazy update of 'get_ctrl' and 'idom_at' mechanisms. Replace + // the 'old_node' with 'new_node'. Kill old-node. Add a reference + // from old_node to new_node to support the lazy update. Reference + // replaces loop reference, since that is not neede for dead node. +public: + void lazy_update( Node *old_node, Node *new_node ) { + assert( old_node != new_node, "no cycles please" ); + //old_node->set_req( 1, new_node /*NO DU INFO*/ ); + // Nodes always have DU info now, so re-use the side array slot + // for this node to provide the forwarding pointer. + _nodes.map( old_node->_idx, (Node*)((intptr_t)new_node + 1) ); + } + void lazy_replace( Node *old_node, Node *new_node ) { + _igvn.hash_delete(old_node); + _igvn.subsume_node( old_node, new_node ); + lazy_update( old_node, new_node ); + } + void lazy_replace_proj( Node *old_node, Node *new_node ) { + assert( old_node->req() == 1, "use this for Projs" ); + _igvn.hash_delete(old_node); // Must hash-delete before hacking edges + old_node->add_req( NULL ); + lazy_replace( old_node, new_node ); + } + +private: + + // Place 'n' in some loop nest, where 'n' is a CFG node + void build_loop_tree(); + int build_loop_tree_impl( Node *n, int pre_order ); + // Insert loop into the existing loop tree. 'innermost' is a leaf of the + // loop tree, not the root. + IdealLoopTree *sort( IdealLoopTree *loop, IdealLoopTree *innermost ); + + // Place Data nodes in some loop nest + void build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack, const PhaseIdealLoop *verify_me ); + void build_loop_late ( VectorSet &visited, Node_List &worklist, Node_Stack &nstack, const PhaseIdealLoop *verify_me ); + void build_loop_late_post ( Node* n, const PhaseIdealLoop *verify_me ); + + // Array of immediate dominance info for each CFG node indexed by node idx +private: + uint _idom_size; + Node **_idom; // Array of immediate dominators + uint *_dom_depth; // Used for fast LCA test + GrowableArray* _dom_stk; // For recomputation of dom depth + + Node* idom_no_update(Node* d) const { + assert(d->_idx < _idom_size, "oob"); + Node* n = _idom[d->_idx]; + assert(n != NULL,"Bad immediate dominator info."); + while (n->in(0) == NULL) { // Skip dead CFG nodes + //n = n->in(1); + n = (Node*)(((intptr_t)_nodes[n->_idx]) & ~1); + assert(n != NULL,"Bad immediate dominator info."); + } + return n; + } + Node *idom(Node* d) const { + uint didx = d->_idx; + Node *n = idom_no_update(d); + _idom[didx] = n; // Lazily remove dead CFG nodes from table. + return n; + } + uint dom_depth(Node* d) const { + assert(d->_idx < _idom_size, ""); + return _dom_depth[d->_idx]; + } + void set_idom(Node* d, Node* n, uint dom_depth); + // Locally compute IDOM using dom_lca call + Node *compute_idom( Node *region ) const; + // Recompute dom_depth + void recompute_dom_depth(); + + // Is safept not required by an outer loop? + bool is_deleteable_safept(Node* sfpt); + +public: + // Dominators for the sea of nodes + void Dominators(); + Node *dom_lca( Node *n1, Node *n2 ) const { + return find_non_split_ctrl(dom_lca_internal(n1, n2)); + } + Node *dom_lca_internal( Node *n1, Node *n2 ) const; + + // Compute the Ideal Node to Loop mapping + PhaseIdealLoop( PhaseIterGVN &igvn, const PhaseIdealLoop *verify_me, bool do_split_ifs ); + + // True if the method has at least 1 irreducible loop + bool _has_irreducible_loops; + + // Per-Node transform + virtual Node *transform( Node *a_node ) { return 0; } + + Node *is_counted_loop( Node *x, IdealLoopTree *loop ); + + // Return a post-walked LoopNode + IdealLoopTree *get_loop( Node *n ) const { + // Dead nodes have no loop, so return the top level loop instead + if (!has_node(n)) return _ltree_root; + assert(!has_ctrl(n), ""); + return (IdealLoopTree*)_nodes[n->_idx]; + } + + // Is 'n' a (nested) member of 'loop'? + int is_member( const IdealLoopTree *loop, Node *n ) const { + return loop->is_member(get_loop(n)); } + + // This is the basic building block of the loop optimizations. It clones an + // entire loop body. It makes an old_new loop body mapping; with this + // mapping you can find the new-loop equivalent to an old-loop node. All + // new-loop nodes are exactly equal to their old-loop counterparts, all + // edges are the same. All exits from the old-loop now have a RegionNode + // that merges the equivalent new-loop path. This is true even for the + // normal "loop-exit" condition. All uses of loop-invariant old-loop values + // now come from (one or more) Phis that merge their new-loop equivalents. + // Parameter side_by_side_idom: + // When side_by_size_idom is NULL, the dominator tree is constructed for + // the clone loop to dominate the original. Used in construction of + // pre-main-post loop sequence. + // When nonnull, the clone and original are side-by-side, both are + // dominated by the passed in side_by_side_idom node. Used in + // construction of unswitched loops. + void clone_loop( IdealLoopTree *loop, Node_List &old_new, int dom_depth, + Node* side_by_side_idom = NULL); + + // If we got the effect of peeling, either by actually peeling or by + // making a pre-loop which must execute at least once, we can remove + // all loop-invariant dominated tests in the main body. + void peeled_dom_test_elim( IdealLoopTree *loop, Node_List &old_new ); + + // Generate code to do a loop peel for the given loop (and body). + // old_new is a temp array. + void do_peeling( IdealLoopTree *loop, Node_List &old_new ); + + // Add pre and post loops around the given loop. These loops are used + // during RCE, unrolling and aligning loops. + void insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_new, bool peel_only ); + // If Node n lives in the back_ctrl block, we clone a private version of n + // in preheader_ctrl block and return that, otherwise return n. + Node *clone_up_backedge_goo( Node *back_ctrl, Node *preheader_ctrl, Node *n ); + + // Take steps to maximally unroll the loop. Peel any odd iterations, then + // unroll to do double iterations. The next round of major loop transforms + // will repeat till the doubled loop body does all remaining iterations in 1 + // pass. + void do_maximally_unroll( IdealLoopTree *loop, Node_List &old_new ); + + // Unroll the loop body one step - make each trip do 2 iterations. + void do_unroll( IdealLoopTree *loop, Node_List &old_new, bool adjust_min_trip ); + + // Return true if exp is a constant times an induction var + bool is_scaled_iv(Node* exp, Node* iv, int* p_scale); + + // Return true if exp is a scaled induction var plus (or minus) constant + bool is_scaled_iv_plus_offset(Node* exp, Node* iv, int* p_scale, Node** p_offset, int depth = 0); + + // Eliminate range-checks and other trip-counter vs loop-invariant tests. + void do_range_check( IdealLoopTree *loop, Node_List &old_new ); + + // Create a slow version of the loop by cloning the loop + // and inserting an if to select fast-slow versions. + ProjNode* create_slow_version_of_loop(IdealLoopTree *loop, + Node_List &old_new); + + // Clone loop with an invariant test (that does not exit) and + // insert a clone of the test that selects which version to + // execute. + void do_unswitching (IdealLoopTree *loop, Node_List &old_new); + + // Find candidate "if" for unswitching + IfNode* find_unswitching_candidate(const IdealLoopTree *loop) const; + + // Range Check Elimination uses this function! + // Constrain the main loop iterations so the affine function: + // scale_con * I + offset < limit + // always holds true. That is, either increase the number of iterations in + // the pre-loop or the post-loop until the condition holds true in the main + // loop. Scale_con, offset and limit are all loop invariant. + void add_constraint( int stride_con, int scale_con, Node *offset, Node *limit, Node *pre_ctrl, Node **pre_limit, Node **main_limit ); + + // Partially peel loop up through last_peel node. + bool partial_peel( IdealLoopTree *loop, Node_List &old_new ); + + // Create a scheduled list of nodes control dependent on ctrl set. + void scheduled_nodelist( IdealLoopTree *loop, VectorSet& ctrl, Node_List &sched ); + // Has a use in the vector set + bool has_use_in_set( Node* n, VectorSet& vset ); + // Has use internal to the vector set (ie. not in a phi at the loop head) + bool has_use_internal_to_set( Node* n, VectorSet& vset, IdealLoopTree *loop ); + // clone "n" for uses that are outside of loop + void clone_for_use_outside_loop( IdealLoopTree *loop, Node* n, Node_List& worklist ); + // clone "n" for special uses that are in the not_peeled region + void clone_for_special_use_inside_loop( IdealLoopTree *loop, Node* n, + VectorSet& not_peel, Node_List& sink_list, Node_List& worklist ); + // Insert phi(lp_entry_val, back_edge_val) at use->in(idx) for loop lp if phi does not already exist + void insert_phi_for_loop( Node* use, uint idx, Node* lp_entry_val, Node* back_edge_val, LoopNode* lp ); +#ifdef ASSERT + // Validate the loop partition sets: peel and not_peel + bool is_valid_loop_partition( IdealLoopTree *loop, VectorSet& peel, Node_List& peel_list, VectorSet& not_peel ); + // Ensure that uses outside of loop are of the right form + bool is_valid_clone_loop_form( IdealLoopTree *loop, Node_List& peel_list, + uint orig_exit_idx, uint clone_exit_idx); + bool is_valid_clone_loop_exit_use( IdealLoopTree *loop, Node* use, uint exit_idx); +#endif + + // Returns nonzero constant stride if-node is a possible iv test (otherwise returns zero.) + int stride_of_possible_iv( Node* iff ); + bool is_possible_iv_test( Node* iff ) { return stride_of_possible_iv(iff) != 0; } + // Return the (unique) control output node that's in the loop (if it exists.) + Node* stay_in_loop( Node* n, IdealLoopTree *loop); + // Insert a signed compare loop exit cloned from an unsigned compare. + IfNode* insert_cmpi_loop_exit(IfNode* if_cmpu, IdealLoopTree *loop); + void remove_cmpi_loop_exit(IfNode* if_cmp, IdealLoopTree *loop); + // Utility to register node "n" with PhaseIdealLoop + void register_node(Node* n, IdealLoopTree *loop, Node* pred, int ddepth); + // Utility to create an if-projection + ProjNode* proj_clone(ProjNode* p, IfNode* iff); + // Force the iff control output to be the live_proj + Node* short_circuit_if(IfNode* iff, ProjNode* live_proj); + // Insert a region before an if projection + RegionNode* insert_region_before_proj(ProjNode* proj); + // Insert a new if before an if projection + ProjNode* insert_if_before_proj(Node* left, bool Signed, BoolTest::mask relop, Node* right, ProjNode* proj); + + // Passed in a Phi merging (recursively) some nearly equivalent Bool/Cmps. + // "Nearly" because all Nodes have been cloned from the original in the loop, + // but the fall-in edges to the Cmp are different. Clone bool/Cmp pairs + // through the Phi recursively, and return a Bool. + BoolNode *clone_iff( PhiNode *phi, IdealLoopTree *loop ); + CmpNode *clone_bool( PhiNode *phi, IdealLoopTree *loop ); + + + // Rework addressing expressions to get the most loop-invariant stuff + // moved out. We'd like to do all associative operators, but it's especially + // important (common) to do address expressions. + Node *remix_address_expressions( Node *n ); + + // Attempt to use a conditional move instead of a phi/branch + Node *conditional_move( Node *n ); + + // Reorganize offset computations to lower register pressure. + // Mostly prevent loop-fallout uses of the pre-incremented trip counter + // (which are then alive with the post-incremented trip counter + // forcing an extra register move) + void reorg_offsets( IdealLoopTree *loop ); + + // Check for aggressive application of 'split-if' optimization, + // using basic block level info. + void split_if_with_blocks ( VectorSet &visited, Node_Stack &nstack ); + Node *split_if_with_blocks_pre ( Node *n ); + void split_if_with_blocks_post( Node *n ); + Node *has_local_phi_input( Node *n ); + // Mark an IfNode as being dominated by a prior test, + // without actually altering the CFG (and hence IDOM info). + void dominated_by( Node *prevdom, Node *iff ); + + // Split Node 'n' through merge point + Node *split_thru_region( Node *n, Node *region ); + // Split Node 'n' through merge point if there is enough win. + Node *split_thru_phi( Node *n, Node *region, int policy ); + // Found an If getting its condition-code input from a Phi in the + // same block. Split thru the Region. + void do_split_if( Node *iff ); + +private: + // Return a type based on condition control flow + const TypeInt* filtered_type( Node *n, Node* n_ctrl); + const TypeInt* filtered_type( Node *n ) { return filtered_type(n, NULL); } + // Helpers for filtered type + const TypeInt* filtered_type_from_dominators( Node* val, Node *val_ctrl); + const TypeInt* filtered_type_at_if( Node* val, Node *if_proj); + + // Helper functions + void register_new_node( Node *n, Node *blk ); + Node *spinup( Node *iff, Node *new_false, Node *new_true, Node *region, Node *phi, small_cache *cache ); + Node *find_use_block( Node *use, Node *def, Node *old_false, Node *new_false, Node *old_true, Node *new_true ); + void handle_use( Node *use, Node *def, small_cache *cache, Node *region_dom, Node *new_false, Node *new_true, Node *old_false, Node *old_true ); + bool split_up( Node *n, Node *blk1, Node *blk2 ); + void sink_use( Node *use, Node *post_loop ); + Node *place_near_use( Node *useblock ) const; + + bool _created_loop_node; +public: + void set_created_loop_node() { _created_loop_node = true; } + bool created_loop_node() { return _created_loop_node; } + +#ifndef PRODUCT + void dump( ) const; + void dump( IdealLoopTree *loop, uint rpo_idx, Node_List &rpo_list ) const; + void rpo( Node *start, Node_Stack &stk, VectorSet &visited, Node_List &rpo_list ) const; + void verify() const; // Major slow :-) + void verify_compare( Node *n, const PhaseIdealLoop *loop_verify, VectorSet &visited ) const; + IdealLoopTree *get_loop_idx(Node* n) const { + // Dead nodes have no loop, so return the top level loop instead + return _nodes[n->_idx] ? (IdealLoopTree*)_nodes[n->_idx] : _ltree_root; + } + // Print some stats + static void print_statistics(); + static int _loop_invokes; // Count of PhaseIdealLoop invokes + static int _loop_work; // Sum of PhaseIdealLoop x _unique +#endif +}; + +inline Node* IdealLoopTree::tail() { +// Handle lazy update of _tail field + Node *n = _tail; + //while( !n->in(0) ) // Skip dead CFG nodes + //n = n->in(1); + if (n->in(0) == NULL) + n = _phase->get_ctrl(n); + _tail = n; + return n; +} + + +// Iterate over the loop tree using a preorder, left-to-right traversal. +// +// Example that visits all counted loops from within PhaseIdealLoop +// +// for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { +// IdealLoopTree* lpt = iter.current(); +// if (!lpt->is_counted()) continue; +// ... +class LoopTreeIterator : public StackObj { +private: + IdealLoopTree* _root; + IdealLoopTree* _curnt; + +public: + LoopTreeIterator(IdealLoopTree* root) : _root(root), _curnt(root) {} + + bool done() { return _curnt == NULL; } // Finished iterating? + + void next(); // Advance to next loop tree + + IdealLoopTree* current() { return _curnt; } // Return current value of iterator. +}; diff --git a/hotspot/src/share/vm/opto/loopopts.cpp b/hotspot/src/share/vm/opto/loopopts.cpp new file mode 100644 index 00000000000..0da6b1eeebb --- /dev/null +++ b/hotspot/src/share/vm/opto/loopopts.cpp @@ -0,0 +1,2677 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_loopopts.cpp.incl" + +//============================================================================= +//------------------------------split_thru_phi--------------------------------- +// Split Node 'n' through merge point if there is enough win. +Node *PhaseIdealLoop::split_thru_phi( Node *n, Node *region, int policy ) { + int wins = 0; + assert( !n->is_CFG(), "" ); + assert( region->is_Region(), "" ); + Node *phi = new (C, region->req()) PhiNode( region, n->bottom_type() ); + uint old_unique = C->unique(); + for( uint i = 1; i < region->req(); i++ ) { + Node *x; + Node* the_clone = NULL; + if( region->in(i) == C->top() ) { + x = C->top(); // Dead path? Use a dead data op + } else { + x = n->clone(); // Else clone up the data op + the_clone = x; // Remember for possible deletion. + // Alter data node to use pre-phi inputs + if( n->in(0) == region ) + x->set_req( 0, region->in(i) ); + for( uint j = 1; j < n->req(); j++ ) { + Node *in = n->in(j); + if( in->is_Phi() && in->in(0) == region ) + x->set_req( j, in->in(i) ); // Use pre-Phi input for the clone + } + } + // Check for a 'win' on some paths + const Type *t = x->Value(&_igvn); + + bool singleton = t->singleton(); + + // A TOP singleton indicates that there are no possible values incoming + // along a particular edge. In most cases, this is OK, and the Phi will + // be eliminated later in an Ideal call. However, we can't allow this to + // happen if the singleton occurs on loop entry, as the elimination of + // the PhiNode may cause the resulting node to migrate back to a previous + // loop iteration. + if( singleton && t == Type::TOP ) { + // Is_Loop() == false does not confirm the absence of a loop (e.g., an + // irreducible loop may not be indicated by an affirmative is_Loop()); + // therefore, the only top we can split thru a phi is on a backedge of + // a loop. + singleton &= region->is_Loop() && (i != LoopNode::EntryControl); + } + + if( singleton ) { + wins++; + x = ((PhaseGVN&)_igvn).makecon(t); + } else { + // We now call Identity to try to simplify the cloned node. + // Note that some Identity methods call phase->type(this). + // Make sure that the type array is big enough for + // our new node, even though we may throw the node away. + // (Note: This tweaking with igvn only works because x is a new node.) + _igvn.set_type(x, t); + Node *y = x->Identity(&_igvn); + if( y != x ) { + wins++; + x = y; + } else { + y = _igvn.hash_find(x); + if( y ) { + wins++; + x = y; + } else { + // Else x is a new node we are keeping + // We do not need register_new_node_with_optimizer + // because set_type has already been called. + _igvn._worklist.push(x); + } + } + } + if (x != the_clone && the_clone != NULL) + _igvn.remove_dead_node(the_clone); + phi->set_req( i, x ); + } + // Too few wins? + if( wins <= policy ) { + _igvn.remove_dead_node(phi); + return NULL; + } + + // Record Phi + register_new_node( phi, region ); + + for( uint i2 = 1; i2 < phi->req(); i2++ ) { + Node *x = phi->in(i2); + // If we commoned up the cloned 'x' with another existing Node, + // the existing Node picks up a new use. We need to make the + // existing Node occur higher up so it dominates its uses. + Node *old_ctrl; + IdealLoopTree *old_loop; + + // The occasional new node + if( x->_idx >= old_unique ) { // Found a new, unplaced node? + old_ctrl = x->is_Con() ? C->root() : NULL; + old_loop = NULL; // Not in any prior loop + } else { + old_ctrl = x->is_Con() ? C->root() : get_ctrl(x); + old_loop = get_loop(old_ctrl); // Get prior loop + } + // New late point must dominate new use + Node *new_ctrl = dom_lca( old_ctrl, region->in(i2) ); + // Set new location + set_ctrl(x, new_ctrl); + IdealLoopTree *new_loop = get_loop( new_ctrl ); + // If changing loop bodies, see if we need to collect into new body + if( old_loop != new_loop ) { + if( old_loop && !old_loop->_child ) + old_loop->_body.yank(x); + if( !new_loop->_child ) + new_loop->_body.push(x); // Collect body info + } + } + + return phi; +} + +//------------------------------dominated_by------------------------------------ +// Replace the dominated test with an obvious true or false. Place it on the +// IGVN worklist for later cleanup. Move control-dependent data Nodes on the +// live path up to the dominating control. +void PhaseIdealLoop::dominated_by( Node *prevdom, Node *iff ) { +#ifndef PRODUCT + if( VerifyLoopOptimizations && PrintOpto ) tty->print_cr("dominating test"); +#endif + + + // prevdom is the dominating projection of the dominating test. + assert( iff->is_If(), "" ); + assert( iff->Opcode() == Op_If || iff->Opcode() == Op_CountedLoopEnd, "Check this code when new subtype is added"); + int pop = prevdom->Opcode(); + assert( pop == Op_IfFalse || pop == Op_IfTrue, "" ); + // 'con' is set to true or false to kill the dominated test. + Node *con = _igvn.makecon(pop == Op_IfTrue ? TypeInt::ONE : TypeInt::ZERO); + set_ctrl(con, C->root()); // Constant gets a new use + // Hack the dominated test + _igvn.hash_delete(iff); + iff->set_req(1, con); + _igvn._worklist.push(iff); + + // If I dont have a reachable TRUE and FALSE path following the IfNode then + // I can assume this path reaches an infinite loop. In this case it's not + // important to optimize the data Nodes - either the whole compilation will + // be tossed or this path (and all data Nodes) will go dead. + if( iff->outcnt() != 2 ) return; + + // Make control-dependent data Nodes on the live path (path that will remain + // once the dominated IF is removed) become control-dependent on the + // dominating projection. + Node* dp = ((IfNode*)iff)->proj_out(pop == Op_IfTrue); + IdealLoopTree *old_loop = get_loop(dp); + + for (DUIterator_Fast imax, i = dp->fast_outs(imax); i < imax; i++) { + Node* cd = dp->fast_out(i); // Control-dependent node + if( cd->depends_only_on_test() ) { + assert( cd->in(0) == dp, "" ); + _igvn.hash_delete( cd ); + cd->set_req(0, prevdom); + set_early_ctrl( cd ); + _igvn._worklist.push(cd); + IdealLoopTree *new_loop = get_loop(get_ctrl(cd)); + if( old_loop != new_loop ) { + if( !old_loop->_child ) old_loop->_body.yank(cd); + if( !new_loop->_child ) new_loop->_body.push(cd); + } + --i; + --imax; + } + } +} + +//------------------------------has_local_phi_input---------------------------- +// Return TRUE if 'n' has Phi inputs from its local block and no other +// block-local inputs (all non-local-phi inputs come from earlier blocks) +Node *PhaseIdealLoop::has_local_phi_input( Node *n ) { + Node *n_ctrl = get_ctrl(n); + // See if some inputs come from a Phi in this block, or from before + // this block. + uint i; + for( i = 1; i < n->req(); i++ ) { + Node *phi = n->in(i); + if( phi->is_Phi() && phi->in(0) == n_ctrl ) + break; + } + if( i >= n->req() ) + return NULL; // No Phi inputs; nowhere to clone thru + + // Check for inputs created between 'n' and the Phi input. These + // must split as well; they have already been given the chance + // (courtesy of a post-order visit) and since they did not we must + // recover the 'cost' of splitting them by being very profitable + // when splitting 'n'. Since this is unlikely we simply give up. + for( i = 1; i < n->req(); i++ ) { + Node *m = n->in(i); + if( get_ctrl(m) == n_ctrl && !m->is_Phi() ) { + // We allow the special case of AddP's with no local inputs. + // This allows us to split-up address expressions. + if (m->is_AddP() && + get_ctrl(m->in(2)) != n_ctrl && + get_ctrl(m->in(3)) != n_ctrl) { + // Move the AddP up to dominating point + set_ctrl_and_loop(m, find_non_split_ctrl(idom(n_ctrl))); + continue; + } + return NULL; + } + } + + return n_ctrl; +} + +//------------------------------remix_address_expressions---------------------- +// Rework addressing expressions to get the most loop-invariant stuff +// moved out. We'd like to do all associative operators, but it's especially +// important (common) to do address expressions. +Node *PhaseIdealLoop::remix_address_expressions( Node *n ) { + if (!has_ctrl(n)) return NULL; + Node *n_ctrl = get_ctrl(n); + IdealLoopTree *n_loop = get_loop(n_ctrl); + + // See if 'n' mixes loop-varying and loop-invariant inputs and + // itself is loop-varying. + + // Only interested in binary ops (and AddP) + if( n->req() < 3 || n->req() > 4 ) return NULL; + + Node *n1_ctrl = get_ctrl(n->in( 1)); + Node *n2_ctrl = get_ctrl(n->in( 2)); + Node *n3_ctrl = get_ctrl(n->in(n->req() == 3 ? 2 : 3)); + IdealLoopTree *n1_loop = get_loop( n1_ctrl ); + IdealLoopTree *n2_loop = get_loop( n2_ctrl ); + IdealLoopTree *n3_loop = get_loop( n3_ctrl ); + + // Does one of my inputs spin in a tighter loop than self? + if( (n_loop->is_member( n1_loop ) && n_loop != n1_loop) || + (n_loop->is_member( n2_loop ) && n_loop != n2_loop) || + (n_loop->is_member( n3_loop ) && n_loop != n3_loop) ) + return NULL; // Leave well enough alone + + // Is at least one of my inputs loop-invariant? + if( n1_loop == n_loop && + n2_loop == n_loop && + n3_loop == n_loop ) + return NULL; // No loop-invariant inputs + + + int n_op = n->Opcode(); + + // Replace expressions like ((V+I) << 2) with (V<<2 + I<<2). + if( n_op == Op_LShiftI ) { + // Scale is loop invariant + Node *scale = n->in(2); + Node *scale_ctrl = get_ctrl(scale); + IdealLoopTree *scale_loop = get_loop(scale_ctrl ); + if( n_loop == scale_loop || !scale_loop->is_member( n_loop ) ) + return NULL; + const TypeInt *scale_t = scale->bottom_type()->isa_int(); + if( scale_t && scale_t->is_con() && scale_t->get_con() >= 16 ) + return NULL; // Dont bother with byte/short masking + // Add must vary with loop (else shift would be loop-invariant) + Node *add = n->in(1); + Node *add_ctrl = get_ctrl(add); + IdealLoopTree *add_loop = get_loop(add_ctrl); + //assert( n_loop == add_loop, "" ); + if( n_loop != add_loop ) return NULL; // happens w/ evil ZKM loops + + // Convert I-V into I+ (0-V); same for V-I + if( add->Opcode() == Op_SubI && + _igvn.type( add->in(1) ) != TypeInt::ZERO ) { + Node *zero = _igvn.intcon(0); + set_ctrl(zero, C->root()); + Node *neg = new (C, 3) SubINode( _igvn.intcon(0), add->in(2) ); + register_new_node( neg, get_ctrl(add->in(2) ) ); + add = new (C, 3) AddINode( add->in(1), neg ); + register_new_node( add, add_ctrl ); + } + if( add->Opcode() != Op_AddI ) return NULL; + // See if one add input is loop invariant + Node *add_var = add->in(1); + Node *add_var_ctrl = get_ctrl(add_var); + IdealLoopTree *add_var_loop = get_loop(add_var_ctrl ); + Node *add_invar = add->in(2); + Node *add_invar_ctrl = get_ctrl(add_invar); + IdealLoopTree *add_invar_loop = get_loop(add_invar_ctrl ); + if( add_var_loop == n_loop ) { + } else if( add_invar_loop == n_loop ) { + // Swap to find the invariant part + add_invar = add_var; + add_invar_ctrl = add_var_ctrl; + add_invar_loop = add_var_loop; + add_var = add->in(2); + Node *add_var_ctrl = get_ctrl(add_var); + IdealLoopTree *add_var_loop = get_loop(add_var_ctrl ); + } else // Else neither input is loop invariant + return NULL; + if( n_loop == add_invar_loop || !add_invar_loop->is_member( n_loop ) ) + return NULL; // No invariant part of the add? + + // Yes! Reshape address expression! + Node *inv_scale = new (C, 3) LShiftINode( add_invar, scale ); + register_new_node( inv_scale, add_invar_ctrl ); + Node *var_scale = new (C, 3) LShiftINode( add_var, scale ); + register_new_node( var_scale, n_ctrl ); + Node *var_add = new (C, 3) AddINode( var_scale, inv_scale ); + register_new_node( var_add, n_ctrl ); + _igvn.hash_delete( n ); + _igvn.subsume_node( n, var_add ); + return var_add; + } + + // Replace (I+V) with (V+I) + if( n_op == Op_AddI || + n_op == Op_AddL || + n_op == Op_AddF || + n_op == Op_AddD || + n_op == Op_MulI || + n_op == Op_MulL || + n_op == Op_MulF || + n_op == Op_MulD ) { + if( n2_loop == n_loop ) { + assert( n1_loop != n_loop, "" ); + n->swap_edges(1, 2); + } + } + + // Replace ((I1 +p V) +p I2) with ((I1 +p I2) +p V), + // but not if I2 is a constant. + if( n_op == Op_AddP ) { + if( n2_loop == n_loop && n3_loop != n_loop ) { + if( n->in(2)->Opcode() == Op_AddP && !n->in(3)->is_Con() ) { + Node *n22_ctrl = get_ctrl(n->in(2)->in(2)); + Node *n23_ctrl = get_ctrl(n->in(2)->in(3)); + IdealLoopTree *n22loop = get_loop( n22_ctrl ); + IdealLoopTree *n23_loop = get_loop( n23_ctrl ); + if( n22loop != n_loop && n22loop->is_member(n_loop) && + n23_loop == n_loop ) { + Node *add1 = new (C, 4) AddPNode( n->in(1), n->in(2)->in(2), n->in(3) ); + // Stuff new AddP in the loop preheader + register_new_node( add1, n_loop->_head->in(LoopNode::EntryControl) ); + Node *add2 = new (C, 4) AddPNode( n->in(1), add1, n->in(2)->in(3) ); + register_new_node( add2, n_ctrl ); + _igvn.hash_delete( n ); + _igvn.subsume_node( n, add2 ); + return add2; + } + } + } + + // Replace (I1 +p (I2 + V)) with ((I1 +p I2) +p V) + if( n2_loop != n_loop && n3_loop == n_loop ) { + if( n->in(3)->Opcode() == Op_AddI ) { + Node *V = n->in(3)->in(1); + Node *I = n->in(3)->in(2); + if( is_member(n_loop,get_ctrl(V)) ) { + } else { + Node *tmp = V; V = I; I = tmp; + } + if( !is_member(n_loop,get_ctrl(I)) ) { + Node *add1 = new (C, 4) AddPNode( n->in(1), n->in(2), I ); + // Stuff new AddP in the loop preheader + register_new_node( add1, n_loop->_head->in(LoopNode::EntryControl) ); + Node *add2 = new (C, 4) AddPNode( n->in(1), add1, V ); + register_new_node( add2, n_ctrl ); + _igvn.hash_delete( n ); + _igvn.subsume_node( n, add2 ); + return add2; + } + } + } + } + + return NULL; +} + +//------------------------------conditional_move------------------------------- +// Attempt to replace a Phi with a conditional move. We have some pretty +// strict profitability requirements. All Phis at the merge point must +// be converted, so we can remove the control flow. We need to limit the +// number of c-moves to a small handful. All code that was in the side-arms +// of the CFG diamond is now speculatively executed. This code has to be +// "cheap enough". We are pretty much limited to CFG diamonds that merge +// 1 or 2 items with a total of 1 or 2 ops executed speculatively. +Node *PhaseIdealLoop::conditional_move( Node *region ) { + + assert( region->is_Region(), "sanity check" ); + if( region->req() != 3 ) return NULL; + + // Check for CFG diamond + Node *lp = region->in(1); + Node *rp = region->in(2); + if( !lp || !rp ) return NULL; + Node *lp_c = lp->in(0); + if( lp_c == NULL || lp_c != rp->in(0) || !lp_c->is_If() ) return NULL; + IfNode *iff = lp_c->as_If(); + + // Check for highly predictable branch. No point in CMOV'ing if + // we are going to predict accurately all the time. + // %%% This hides patterns produced by utility methods like Math.min. + if( iff->_prob < PROB_UNLIKELY_MAG(3) || + iff->_prob > PROB_LIKELY_MAG(3) ) + return NULL; + + // Check for ops pinned in an arm of the diamond. + // Can't remove the control flow in this case + if( lp->outcnt() > 1 ) return NULL; + if( rp->outcnt() > 1 ) return NULL; + + // Check profitability + int cost = 0; + for (DUIterator_Fast imax, i = region->fast_outs(imax); i < imax; i++) { + Node *out = region->fast_out(i); + if( !out->is_Phi() ) continue; // Ignore other control edges, etc + PhiNode* phi = out->as_Phi(); + switch (phi->type()->basic_type()) { + case T_LONG: + cost++; // Probably encodes as 2 CMOV's + case T_INT: // These all CMOV fine + case T_FLOAT: + case T_DOUBLE: + case T_ADDRESS: // (RawPtr) + cost++; + break; + case T_OBJECT: { // Base oops are OK, but not derived oops + const TypeOopPtr *tp = phi->type()->isa_oopptr(); + // Derived pointers are Bad (tm): what's the Base (for GC purposes) of a + // CMOVE'd derived pointer? It's a CMOVE'd derived base. Thus + // CMOVE'ing a derived pointer requires we also CMOVE the base. If we + // have a Phi for the base here that we convert to a CMOVE all is well + // and good. But if the base is dead, we'll not make a CMOVE. Later + // the allocator will have to produce a base by creating a CMOVE of the + // relevant bases. This puts the allocator in the business of + // manufacturing expensive instructions, generally a bad plan. + // Just Say No to Conditionally-Moved Derived Pointers. + if( tp && tp->offset() != 0 ) + return NULL; + cost++; + break; + } + default: + return NULL; // In particular, can't do memory or I/O + } + // Add in cost any speculative ops + for( uint j = 1; j < region->req(); j++ ) { + Node *proj = region->in(j); + Node *inp = phi->in(j); + if (get_ctrl(inp) == proj) { // Found local op + cost++; + // Check for a chain of dependent ops; these will all become + // speculative in a CMOV. + for( uint k = 1; k < inp->req(); k++ ) + if (get_ctrl(inp->in(k)) == proj) + return NULL; // Too much speculative goo + } + } + // See if the Phi is used by a Cmp. This will likely Split-If, a + // higher-payoff operation. + for (DUIterator_Fast kmax, k = phi->fast_outs(kmax); k < kmax; k++) { + Node* use = phi->fast_out(k); + if( use->is_Cmp() ) + return NULL; + } + } + if( cost >= ConditionalMoveLimit ) return NULL; // Too much goo + + // -------------- + // Now replace all Phis with CMOV's + Node *cmov_ctrl = iff->in(0); + uint flip = (lp->Opcode() == Op_IfTrue); + while( 1 ) { + PhiNode* phi = NULL; + for (DUIterator_Fast imax, i = region->fast_outs(imax); i < imax; i++) { + Node *out = region->fast_out(i); + if (out->is_Phi()) { + phi = out->as_Phi(); + break; + } + } + if (phi == NULL) break; +#ifndef PRODUCT + if( PrintOpto && VerifyLoopOptimizations ) tty->print_cr("CMOV"); +#endif + // Move speculative ops + for( uint j = 1; j < region->req(); j++ ) { + Node *proj = region->in(j); + Node *inp = phi->in(j); + if (get_ctrl(inp) == proj) { // Found local op +#ifndef PRODUCT + if( PrintOpto && VerifyLoopOptimizations ) { + tty->print(" speculate: "); + inp->dump(); + } +#endif + set_ctrl(inp, cmov_ctrl); + } + } + Node *cmov = CMoveNode::make( C, cmov_ctrl, iff->in(1), phi->in(1+flip), phi->in(2-flip), _igvn.type(phi) ); + register_new_node( cmov, cmov_ctrl ); + _igvn.hash_delete(phi); + _igvn.subsume_node( phi, cmov ); +#ifndef PRODUCT + if( VerifyLoopOptimizations ) verify(); +#endif + } + + // The useless CFG diamond will fold up later; see the optimization in + // RegionNode::Ideal. + _igvn._worklist.push(region); + + return iff->in(1); +} + +//------------------------------split_if_with_blocks_pre----------------------- +// Do the real work in a non-recursive function. Data nodes want to be +// cloned in the pre-order so they can feed each other nicely. +Node *PhaseIdealLoop::split_if_with_blocks_pre( Node *n ) { + // Cloning these guys is unlikely to win + int n_op = n->Opcode(); + if( n_op == Op_MergeMem ) return n; + if( n->is_Proj() ) return n; + // Do not clone-up CmpFXXX variations, as these are always + // followed by a CmpI + if( n->is_Cmp() ) return n; + // Attempt to use a conditional move instead of a phi/branch + if( ConditionalMoveLimit > 0 && n_op == Op_Region ) { + Node *cmov = conditional_move( n ); + if( cmov ) return cmov; + } + if( n->is_CFG() || n_op == Op_StorePConditional || n_op == Op_StoreLConditional || n_op == Op_CompareAndSwapI || n_op == Op_CompareAndSwapL ||n_op == Op_CompareAndSwapP) return n; + if( n_op == Op_Opaque1 || // Opaque nodes cannot be mod'd + n_op == Op_Opaque2 ) { + if( !C->major_progress() ) // If chance of no more loop opts... + _igvn._worklist.push(n); // maybe we'll remove them + return n; + } + + if( n->is_Con() ) return n; // No cloning for Con nodes + + Node *n_ctrl = get_ctrl(n); + if( !n_ctrl ) return n; // Dead node + + // Attempt to remix address expressions for loop invariants + Node *m = remix_address_expressions( n ); + if( m ) return m; + + // Determine if the Node has inputs from some local Phi. + // Returns the block to clone thru. + Node *n_blk = has_local_phi_input( n ); + if( !n_blk ) return n; + // Do not clone the trip counter through on a CountedLoop + // (messes up the canonical shape). + if( n_blk->is_CountedLoop() && n->Opcode() == Op_AddI ) return n; + + // Check for having no control input; not pinned. Allow + // dominating control. + if( n->in(0) ) { + Node *dom = idom(n_blk); + if( dom_lca( n->in(0), dom ) != n->in(0) ) + return n; + } + // Policy: when is it profitable. You must get more wins than + // policy before it is considered profitable. Policy is usually 0, + // so 1 win is considered profitable. Big merges will require big + // cloning, so get a larger policy. + int policy = n_blk->req() >> 2; + + // If the loop is a candidate for range check elimination, + // delay splitting through it's phi until a later loop optimization + if (n_blk->is_CountedLoop()) { + IdealLoopTree *lp = get_loop(n_blk); + if (lp && lp->_rce_candidate) { + return n; + } + } + + // Use same limit as split_if_with_blocks_post + if( C->unique() > 35000 ) return n; // Method too big + + // Split 'n' through the merge point if it is profitable + Node *phi = split_thru_phi( n, n_blk, policy ); + if( !phi ) return n; + + // Found a Phi to split thru! + // Replace 'n' with the new phi + _igvn.hash_delete(n); + _igvn.subsume_node( n, phi ); + // Moved a load around the loop, 'en-registering' something. + if( n_blk->Opcode() == Op_Loop && n->is_Load() && + !phi->in(LoopNode::LoopBackControl)->is_Load() ) + C->set_major_progress(); + + return phi; +} + +static bool merge_point_too_heavy(Compile* C, Node* region) { + // Bail out if the region and its phis have too many users. + int weight = 0; + for (DUIterator_Fast imax, i = region->fast_outs(imax); i < imax; i++) { + weight += region->fast_out(i)->outcnt(); + } + int nodes_left = MaxNodeLimit - C->unique(); + if (weight * 8 > nodes_left) { +#ifndef PRODUCT + if (PrintOpto) + tty->print_cr("*** Split-if bails out: %d nodes, region weight %d", C->unique(), weight); +#endif + return true; + } else { + return false; + } +} + +#ifdef _LP64 +static bool merge_point_safe(Node* region) { + // 4799512: Stop split_if_with_blocks from splitting a block with a ConvI2LNode + // having a PhiNode input. This sidesteps the dangerous case where the split + // ConvI2LNode may become TOP if the input Value() does not + // overlap the ConvI2L range, leaving a node which may not dominate its + // uses. + // A better fix for this problem can be found in the BugTraq entry, but + // expediency for Mantis demands this hack. + for (DUIterator_Fast imax, i = region->fast_outs(imax); i < imax; i++) { + Node* n = region->fast_out(i); + if (n->is_Phi()) { + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { + Node* m = n->fast_out(j); + if (m->Opcode() == Op_ConvI2L) { + return false; + } + } + } + } + return true; +} +#endif + + +//------------------------------place_near_use--------------------------------- +// Place some computation next to use but not inside inner loops. +// For inner loop uses move it to the preheader area. +Node *PhaseIdealLoop::place_near_use( Node *useblock ) const { + IdealLoopTree *u_loop = get_loop( useblock ); + return (u_loop->_irreducible || u_loop->_child) + ? useblock + : u_loop->_head->in(LoopNode::EntryControl); +} + + +//------------------------------split_if_with_blocks_post---------------------- +// Do the real work in a non-recursive function. CFG hackery wants to be +// in the post-order, so it can dirty the I-DOM info and not use the dirtied +// info. +void PhaseIdealLoop::split_if_with_blocks_post( Node *n ) { + + // Cloning Cmp through Phi's involves the split-if transform. + // FastLock is not used by an If + if( n->is_Cmp() && !n->is_FastLock() ) { + if( C->unique() > 35000 ) return; // Method too big + + // Do not do 'split-if' if irreducible loops are present. + if( _has_irreducible_loops ) + return; + + Node *n_ctrl = get_ctrl(n); + // Determine if the Node has inputs from some local Phi. + // Returns the block to clone thru. + Node *n_blk = has_local_phi_input( n ); + if( n_blk != n_ctrl ) return; + + if( merge_point_too_heavy(C, n_ctrl) ) + return; + + if( n->outcnt() != 1 ) return; // Multiple bool's from 1 compare? + Node *bol = n->unique_out(); + assert( bol->is_Bool(), "expect a bool here" ); + if( bol->outcnt() != 1 ) return;// Multiple branches from 1 compare? + Node *iff = bol->unique_out(); + + // Check some safety conditions + if( iff->is_If() ) { // Classic split-if? + if( iff->in(0) != n_ctrl ) return; // Compare must be in same blk as if + } else if (iff->is_CMove()) { // Trying to split-up a CMOVE + if( get_ctrl(iff->in(2)) == n_ctrl || + get_ctrl(iff->in(3)) == n_ctrl ) + return; // Inputs not yet split-up + if ( get_loop(n_ctrl) != get_loop(get_ctrl(iff)) ) { + return; // Loop-invar test gates loop-varying CMOVE + } + } else { + return; // some other kind of node, such as an Allocate + } + + // Do not do 'split-if' if some paths are dead. First do dead code + // elimination and then see if its still profitable. + for( uint i = 1; i < n_ctrl->req(); i++ ) + if( n_ctrl->in(i) == C->top() ) + return; + + // When is split-if profitable? Every 'win' on means some control flow + // goes dead, so it's almost always a win. + int policy = 0; + // If trying to do a 'Split-If' at the loop head, it is only + // profitable if the cmp folds up on BOTH paths. Otherwise we + // risk peeling a loop forever. + + // CNC - Disabled for now. Requires careful handling of loop + // body selection for the cloned code. Also, make sure we check + // for any input path not being in the same loop as n_ctrl. For + // irreducible loops we cannot check for 'n_ctrl->is_Loop()' + // because the alternative loop entry points won't be converted + // into LoopNodes. + IdealLoopTree *n_loop = get_loop(n_ctrl); + for( uint j = 1; j < n_ctrl->req(); j++ ) + if( get_loop(n_ctrl->in(j)) != n_loop ) + return; + +#ifdef _LP64 + // Check for safety of the merge point. + if( !merge_point_safe(n_ctrl) ) { + return; + } +#endif + + // Split compare 'n' through the merge point if it is profitable + Node *phi = split_thru_phi( n, n_ctrl, policy ); + if( !phi ) return; + + // Found a Phi to split thru! + // Replace 'n' with the new phi + _igvn.hash_delete(n); + _igvn.subsume_node( n, phi ); + + // Now split the bool up thru the phi + Node *bolphi = split_thru_phi( bol, n_ctrl, -1 ); + _igvn.hash_delete(bol); + _igvn.subsume_node( bol, bolphi ); + assert( iff->in(1) == bolphi, "" ); + if( bolphi->Value(&_igvn)->singleton() ) + return; + + // Conditional-move? Must split up now + if( !iff->is_If() ) { + Node *cmovphi = split_thru_phi( iff, n_ctrl, -1 ); + _igvn.hash_delete(iff); + _igvn.subsume_node( iff, cmovphi ); + return; + } + + // Now split the IF + do_split_if( iff ); + return; + } + + // Check for an IF ready to split; one that has its + // condition codes input coming from a Phi at the block start. + int n_op = n->Opcode(); + + // Check for an IF being dominated by another IF same test + if( n_op == Op_If ) { + Node *bol = n->in(1); + uint max = bol->outcnt(); + // Check for same test used more than once? + if( n_op == Op_If && max > 1 && bol->is_Bool() ) { + // Search up IDOMs to see if this IF is dominated. + Node *cutoff = get_ctrl(bol); + + // Now search up IDOMs till cutoff, looking for a dominating test + Node *prevdom = n; + Node *dom = idom(prevdom); + while( dom != cutoff ) { + if( dom->req() > 1 && dom->in(1) == bol && prevdom->in(0) == dom ) { + // Replace the dominated test with an obvious true or false. + // Place it on the IGVN worklist for later cleanup. + C->set_major_progress(); + dominated_by( prevdom, n ); +#ifndef PRODUCT + if( VerifyLoopOptimizations ) verify(); +#endif + return; + } + prevdom = dom; + dom = idom(prevdom); + } + } + } + + // See if a shared loop-varying computation has no loop-varying uses. + // Happens if something is only used for JVM state in uncommon trap exits, + // like various versions of induction variable+offset. Clone the + // computation per usage to allow it to sink out of the loop. + if (has_ctrl(n) && !n->in(0)) {// n not dead and has no control edge (can float about) + Node *n_ctrl = get_ctrl(n); + IdealLoopTree *n_loop = get_loop(n_ctrl); + if( n_loop != _ltree_root ) { + DUIterator_Fast imax, i = n->fast_outs(imax); + for (; i < imax; i++) { + Node* u = n->fast_out(i); + if( !has_ctrl(u) ) break; // Found control user + IdealLoopTree *u_loop = get_loop(get_ctrl(u)); + if( u_loop == n_loop ) break; // Found loop-varying use + if( n_loop->is_member( u_loop ) ) break; // Found use in inner loop + if( u->Opcode() == Op_Opaque1 ) break; // Found loop limit, bugfix for 4677003 + } + bool did_break = (i < imax); // Did we break out of the previous loop? + if (!did_break && n->outcnt() > 1) { // All uses in outer loops! + Node *late_load_ctrl; + if (n->is_Load()) { + // If n is a load, get and save the result from get_late_ctrl(), + // to be later used in calculating the control for n's clones. + clear_dom_lca_tags(); + late_load_ctrl = get_late_ctrl(n, n_ctrl); + } + // If n is a load, and the late control is the same as the current + // control, then the cloning of n is a pointless exercise, because + // GVN will ensure that we end up where we started. + if (!n->is_Load() || late_load_ctrl != n_ctrl) { + for (DUIterator_Last jmin, j = n->last_outs(jmin); j >= jmin; ) { + Node *u = n->last_out(j); // Clone private computation per use + _igvn.hash_delete(u); + _igvn._worklist.push(u); + Node *x = n->clone(); // Clone computation + Node *x_ctrl = NULL; + if( u->is_Phi() ) { + // Replace all uses of normal nodes. Replace Phi uses + // individually, so the seperate Nodes can sink down + // different paths. + uint k = 1; + while( u->in(k) != n ) k++; + u->set_req( k, x ); + // x goes next to Phi input path + x_ctrl = u->in(0)->in(k); + --j; + } else { // Normal use + // Replace all uses + for( uint k = 0; k < u->req(); k++ ) { + if( u->in(k) == n ) { + u->set_req( k, x ); + --j; + } + } + x_ctrl = get_ctrl(u); + } + + // Find control for 'x' next to use but not inside inner loops. + // For inner loop uses get the preheader area. + x_ctrl = place_near_use(x_ctrl); + + if (n->is_Load()) { + // For loads, add a control edge to a CFG node outside of the loop + // to force them to not combine and return back inside the loop + // during GVN optimization (4641526). + // + // Because we are setting the actual control input, factor in + // the result from get_late_ctrl() so we respect any + // anti-dependences. (6233005). + x_ctrl = dom_lca(late_load_ctrl, x_ctrl); + + // Don't allow the control input to be a CFG splitting node. + // Such nodes should only have ProjNodes as outs, e.g. IfNode + // should only have IfTrueNode and IfFalseNode (4985384). + x_ctrl = find_non_split_ctrl(x_ctrl); + assert(dom_depth(n_ctrl) <= dom_depth(x_ctrl), "n is later than its clone"); + + x->set_req(0, x_ctrl); + } + register_new_node(x, x_ctrl); + + // Some institutional knowledge is needed here: 'x' is + // yanked because if the optimizer runs GVN on it all the + // cloned x's will common up and undo this optimization and + // be forced back in the loop. This is annoying because it + // makes +VerifyOpto report false-positives on progress. I + // tried setting control edges on the x's to force them to + // not combine, but the matching gets worried when it tries + // to fold a StoreP and an AddP together (as part of an + // address expression) and the AddP and StoreP have + // different controls. + if( !x->is_Load() ) _igvn._worklist.yank(x); + } + _igvn.remove_dead_node(n); + } + } + } + } + + // Check for Opaque2's who's loop has disappeared - who's input is in the + // same loop nest as their output. Remove 'em, they are no longer useful. + if( n_op == Op_Opaque2 && + n->in(1) != NULL && + get_loop(get_ctrl(n)) == get_loop(get_ctrl(n->in(1))) ) { + _igvn.add_users_to_worklist(n); + _igvn.hash_delete(n); + _igvn.subsume_node( n, n->in(1) ); + } +} + +//------------------------------split_if_with_blocks--------------------------- +// Check for aggressive application of 'split-if' optimization, +// using basic block level info. +void PhaseIdealLoop::split_if_with_blocks( VectorSet &visited, Node_Stack &nstack ) { + Node *n = C->root(); + visited.set(n->_idx); // first, mark node as visited + // Do pre-visit work for root + n = split_if_with_blocks_pre( n ); + uint cnt = n->outcnt(); + uint i = 0; + while (true) { + // Visit all children + if (i < cnt) { + Node* use = n->raw_out(i); + ++i; + if (use->outcnt() != 0 && !visited.test_set(use->_idx)) { + // Now do pre-visit work for this use + use = split_if_with_blocks_pre( use ); + nstack.push(n, i); // Save parent and next use's index. + n = use; // Process all children of current use. + cnt = use->outcnt(); + i = 0; + } + } + else { + // All of n's children have been processed, complete post-processing. + if (cnt != 0 && !n->is_Con()) { + assert(has_node(n), "no dead nodes"); + split_if_with_blocks_post( n ); + } + if (nstack.is_empty()) { + // Finished all nodes on stack. + break; + } + // Get saved parent node and next use's index. Visit the rest of uses. + n = nstack.node(); + cnt = n->outcnt(); + i = nstack.index(); + nstack.pop(); + } + } +} + + +//============================================================================= +// +// C L O N E A L O O P B O D Y +// + +//------------------------------clone_iff-------------------------------------- +// Passed in a Phi merging (recursively) some nearly equivalent Bool/Cmps. +// "Nearly" because all Nodes have been cloned from the original in the loop, +// but the fall-in edges to the Cmp are different. Clone bool/Cmp pairs +// through the Phi recursively, and return a Bool. +BoolNode *PhaseIdealLoop::clone_iff( PhiNode *phi, IdealLoopTree *loop ) { + + // Convert this Phi into a Phi merging Bools + uint i; + for( i = 1; i < phi->req(); i++ ) { + Node *b = phi->in(i); + if( b->is_Phi() ) { + _igvn.hash_delete(phi); + _igvn._worklist.push(phi); + phi->set_req(i, clone_iff( b->as_Phi(), loop )); + } else { + assert( b->is_Bool(), "" ); + } + } + + Node *sample_bool = phi->in(1); + Node *sample_cmp = sample_bool->in(1); + + // Make Phis to merge the Cmp's inputs. + int size = phi->in(0)->req(); + PhiNode *phi1 = new (C, size) PhiNode( phi->in(0), Type::TOP ); + PhiNode *phi2 = new (C, size) PhiNode( phi->in(0), Type::TOP ); + for( i = 1; i < phi->req(); i++ ) { + Node *n1 = phi->in(i)->in(1)->in(1); + Node *n2 = phi->in(i)->in(1)->in(2); + phi1->set_req( i, n1 ); + phi2->set_req( i, n2 ); + phi1->set_type( phi1->type()->meet(n1->bottom_type()) ); + phi2->set_type( phi2->type()->meet(n2->bottom_type()) ); + } + // See if these Phis have been made before. + // Register with optimizer + Node *hit1 = _igvn.hash_find_insert(phi1); + if( hit1 ) { // Hit, toss just made Phi + _igvn.remove_dead_node(phi1); // Remove new phi + assert( hit1->is_Phi(), "" ); + phi1 = (PhiNode*)hit1; // Use existing phi + } else { // Miss + _igvn.register_new_node_with_optimizer(phi1); + } + Node *hit2 = _igvn.hash_find_insert(phi2); + if( hit2 ) { // Hit, toss just made Phi + _igvn.remove_dead_node(phi2); // Remove new phi + assert( hit2->is_Phi(), "" ); + phi2 = (PhiNode*)hit2; // Use existing phi + } else { // Miss + _igvn.register_new_node_with_optimizer(phi2); + } + // Register Phis with loop/block info + set_ctrl(phi1, phi->in(0)); + set_ctrl(phi2, phi->in(0)); + // Make a new Cmp + Node *cmp = sample_cmp->clone(); + cmp->set_req( 1, phi1 ); + cmp->set_req( 2, phi2 ); + _igvn.register_new_node_with_optimizer(cmp); + set_ctrl(cmp, phi->in(0)); + + // Make a new Bool + Node *b = sample_bool->clone(); + b->set_req(1,cmp); + _igvn.register_new_node_with_optimizer(b); + set_ctrl(b, phi->in(0)); + + assert( b->is_Bool(), "" ); + return (BoolNode*)b; +} + +//------------------------------clone_bool------------------------------------- +// Passed in a Phi merging (recursively) some nearly equivalent Bool/Cmps. +// "Nearly" because all Nodes have been cloned from the original in the loop, +// but the fall-in edges to the Cmp are different. Clone bool/Cmp pairs +// through the Phi recursively, and return a Bool. +CmpNode *PhaseIdealLoop::clone_bool( PhiNode *phi, IdealLoopTree *loop ) { + uint i; + // Convert this Phi into a Phi merging Bools + for( i = 1; i < phi->req(); i++ ) { + Node *b = phi->in(i); + if( b->is_Phi() ) { + _igvn.hash_delete(phi); + _igvn._worklist.push(phi); + phi->set_req(i, clone_bool( b->as_Phi(), loop )); + } else { + assert( b->is_Cmp() || b->is_top(), "inputs are all Cmp or TOP" ); + } + } + + Node *sample_cmp = phi->in(1); + + // Make Phis to merge the Cmp's inputs. + int size = phi->in(0)->req(); + PhiNode *phi1 = new (C, size) PhiNode( phi->in(0), Type::TOP ); + PhiNode *phi2 = new (C, size) PhiNode( phi->in(0), Type::TOP ); + for( uint j = 1; j < phi->req(); j++ ) { + Node *cmp_top = phi->in(j); // Inputs are all Cmp or TOP + Node *n1, *n2; + if( cmp_top->is_Cmp() ) { + n1 = cmp_top->in(1); + n2 = cmp_top->in(2); + } else { + n1 = n2 = cmp_top; + } + phi1->set_req( j, n1 ); + phi2->set_req( j, n2 ); + phi1->set_type( phi1->type()->meet(n1->bottom_type()) ); + phi2->set_type( phi2->type()->meet(n2->bottom_type()) ); + } + + // See if these Phis have been made before. + // Register with optimizer + Node *hit1 = _igvn.hash_find_insert(phi1); + if( hit1 ) { // Hit, toss just made Phi + _igvn.remove_dead_node(phi1); // Remove new phi + assert( hit1->is_Phi(), "" ); + phi1 = (PhiNode*)hit1; // Use existing phi + } else { // Miss + _igvn.register_new_node_with_optimizer(phi1); + } + Node *hit2 = _igvn.hash_find_insert(phi2); + if( hit2 ) { // Hit, toss just made Phi + _igvn.remove_dead_node(phi2); // Remove new phi + assert( hit2->is_Phi(), "" ); + phi2 = (PhiNode*)hit2; // Use existing phi + } else { // Miss + _igvn.register_new_node_with_optimizer(phi2); + } + // Register Phis with loop/block info + set_ctrl(phi1, phi->in(0)); + set_ctrl(phi2, phi->in(0)); + // Make a new Cmp + Node *cmp = sample_cmp->clone(); + cmp->set_req( 1, phi1 ); + cmp->set_req( 2, phi2 ); + _igvn.register_new_node_with_optimizer(cmp); + set_ctrl(cmp, phi->in(0)); + + assert( cmp->is_Cmp(), "" ); + return (CmpNode*)cmp; +} + +//------------------------------sink_use--------------------------------------- +// If 'use' was in the loop-exit block, it now needs to be sunk +// below the post-loop merge point. +void PhaseIdealLoop::sink_use( Node *use, Node *post_loop ) { + if (!use->is_CFG() && get_ctrl(use) == post_loop->in(2)) { + set_ctrl(use, post_loop); + for (DUIterator j = use->outs(); use->has_out(j); j++) + sink_use(use->out(j), post_loop); + } +} + +//------------------------------clone_loop------------------------------------- +// +// C L O N E A L O O P B O D Y +// +// This is the basic building block of the loop optimizations. It clones an +// entire loop body. It makes an old_new loop body mapping; with this mapping +// you can find the new-loop equivalent to an old-loop node. All new-loop +// nodes are exactly equal to their old-loop counterparts, all edges are the +// same. All exits from the old-loop now have a RegionNode that merges the +// equivalent new-loop path. This is true even for the normal "loop-exit" +// condition. All uses of loop-invariant old-loop values now come from (one +// or more) Phis that merge their new-loop equivalents. +// +// This operation leaves the graph in an illegal state: there are two valid +// control edges coming from the loop pre-header to both loop bodies. I'll +// definitely have to hack the graph after running this transform. +// +// From this building block I will further edit edges to perform loop peeling +// or loop unrolling or iteration splitting (Range-Check-Elimination), etc. +// +// Parameter side_by_size_idom: +// When side_by_size_idom is NULL, the dominator tree is constructed for +// the clone loop to dominate the original. Used in construction of +// pre-main-post loop sequence. +// When nonnull, the clone and original are side-by-side, both are +// dominated by the side_by_side_idom node. Used in construction of +// unswitched loops. +void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd, + Node* side_by_side_idom) { + + // Step 1: Clone the loop body. Make the old->new mapping. + uint i; + for( i = 0; i < loop->_body.size(); i++ ) { + Node *old = loop->_body.at(i); + Node *nnn = old->clone(); + old_new.map( old->_idx, nnn ); + _igvn.register_new_node_with_optimizer(nnn); + } + + + // Step 2: Fix the edges in the new body. If the old input is outside the + // loop use it. If the old input is INside the loop, use the corresponding + // new node instead. + for( i = 0; i < loop->_body.size(); i++ ) { + Node *old = loop->_body.at(i); + Node *nnn = old_new[old->_idx]; + // Fix CFG/Loop controlling the new node + if (has_ctrl(old)) { + set_ctrl(nnn, old_new[get_ctrl(old)->_idx]); + } else { + set_loop(nnn, loop->_parent); + if (old->outcnt() > 0) { + set_idom( nnn, old_new[idom(old)->_idx], dd ); + } + } + // Correct edges to the new node + for( uint j = 0; j < nnn->req(); j++ ) { + Node *n = nnn->in(j); + if( n ) { + IdealLoopTree *old_in_loop = get_loop( has_ctrl(n) ? get_ctrl(n) : n ); + if( loop->is_member( old_in_loop ) ) + nnn->set_req(j, old_new[n->_idx]); + } + } + _igvn.hash_find_insert(nnn); + } + Node *newhead = old_new[loop->_head->_idx]; + set_idom(newhead, newhead->in(LoopNode::EntryControl), dd); + + + // Step 3: Now fix control uses. Loop varying control uses have already + // been fixed up (as part of all input edges in Step 2). Loop invariant + // control uses must be either an IfFalse or an IfTrue. Make a merge + // point to merge the old and new IfFalse/IfTrue nodes; make the use + // refer to this. + ResourceArea *area = Thread::current()->resource_area(); + Node_List worklist(area); + uint new_counter = C->unique(); + for( i = 0; i < loop->_body.size(); i++ ) { + Node* old = loop->_body.at(i); + if( !old->is_CFG() ) continue; + Node* nnn = old_new[old->_idx]; + + // Copy uses to a worklist, so I can munge the def-use info + // with impunity. + for (DUIterator_Fast jmax, j = old->fast_outs(jmax); j < jmax; j++) + worklist.push(old->fast_out(j)); + + while( worklist.size() ) { // Visit all uses + Node *use = worklist.pop(); + if (!has_node(use)) continue; // Ignore dead nodes + IdealLoopTree *use_loop = get_loop( has_ctrl(use) ? get_ctrl(use) : use ); + if( !loop->is_member( use_loop ) && use->is_CFG() ) { + // Both OLD and USE are CFG nodes here. + assert( use->is_Proj(), "" ); + + // Clone the loop exit control projection + Node *newuse = use->clone(); + newuse->set_req(0,nnn); + _igvn.register_new_node_with_optimizer(newuse); + set_loop(newuse, use_loop); + set_idom(newuse, nnn, dom_depth(nnn) + 1 ); + + // We need a Region to merge the exit from the peeled body and the + // exit from the old loop body. + RegionNode *r = new (C, 3) RegionNode(3); + // Map the old use to the new merge point + old_new.map( use->_idx, r ); + uint dd_r = MIN2(dom_depth(newuse),dom_depth(use)); + assert( dd_r >= dom_depth(dom_lca(newuse,use)), "" ); + + // The original user of 'use' uses 'r' instead. + for (DUIterator_Last lmin, l = use->last_outs(lmin); l >= lmin;) { + Node* useuse = use->last_out(l); + _igvn.hash_delete(useuse); + _igvn._worklist.push(useuse); + uint uses_found = 0; + if( useuse->in(0) == use ) { + useuse->set_req(0, r); + uses_found++; + if( useuse->is_CFG() ) { + assert( dom_depth(useuse) > dd_r, "" ); + set_idom(useuse, r, dom_depth(useuse)); + } + } + for( uint k = 1; k < useuse->req(); k++ ) { + if( useuse->in(k) == use ) { + useuse->set_req(k, r); + uses_found++; + } + } + l -= uses_found; // we deleted 1 or more copies of this edge + } + + // Now finish up 'r' + r->set_req( 1, newuse ); + r->set_req( 2, use ); + _igvn.register_new_node_with_optimizer(r); + set_loop(r, use_loop); + set_idom(r, !side_by_side_idom ? newuse->in(0) : side_by_side_idom, dd_r); + } // End of if a loop-exit test + } + } + + // Step 4: If loop-invariant use is not control, it must be dominated by a + // loop exit IfFalse/IfTrue. Find "proper" loop exit. Make a Region + // there if needed. Make a Phi there merging old and new used values. + Node_List *split_if_set = NULL; + Node_List *split_bool_set = NULL; + Node_List *split_cex_set = NULL; + for( i = 0; i < loop->_body.size(); i++ ) { + Node* old = loop->_body.at(i); + Node* nnn = old_new[old->_idx]; + // Copy uses to a worklist, so I can munge the def-use info + // with impunity. + for (DUIterator_Fast jmax, j = old->fast_outs(jmax); j < jmax; j++) + worklist.push(old->fast_out(j)); + + while( worklist.size() ) { + Node *use = worklist.pop(); + if (!has_node(use)) continue; // Ignore dead nodes + if (use->in(0) == C->top()) continue; + IdealLoopTree *use_loop = get_loop( has_ctrl(use) ? get_ctrl(use) : use ); + // Check for data-use outside of loop - at least one of OLD or USE + // must not be a CFG node. + if( !loop->is_member( use_loop ) && (!old->is_CFG() || !use->is_CFG())) { + + // If the Data use is an IF, that means we have an IF outside of the + // loop that is switching on a condition that is set inside of the + // loop. Happens if people set a loop-exit flag; then test the flag + // in the loop to break the loop, then test is again outside of the + // loop to determine which way the loop exited. + if( use->is_If() || use->is_CMove() ) { + // Since this code is highly unlikely, we lazily build the worklist + // of such Nodes to go split. + if( !split_if_set ) + split_if_set = new Node_List(area); + split_if_set->push(use); + } + if( use->is_Bool() ) { + if( !split_bool_set ) + split_bool_set = new Node_List(area); + split_bool_set->push(use); + } + if( use->Opcode() == Op_CreateEx ) { + if( !split_cex_set ) + split_cex_set = new Node_List(area); + split_cex_set->push(use); + } + + + // Get "block" use is in + uint idx = 0; + while( use->in(idx) != old ) idx++; + Node *prev = use->is_CFG() ? use : get_ctrl(use); + assert( !loop->is_member( get_loop( prev ) ), "" ); + Node *cfg = prev->_idx >= new_counter + ? prev->in(2) + : idom(prev); + if( use->is_Phi() ) // Phi use is in prior block + cfg = prev->in(idx); // NOT in block of Phi itself + if (cfg->is_top()) { // Use is dead? + _igvn.hash_delete(use); + _igvn._worklist.push(use); + use->set_req(idx, C->top()); + continue; + } + + while( !loop->is_member( get_loop( cfg ) ) ) { + prev = cfg; + cfg = cfg->_idx >= new_counter ? cfg->in(2) : idom(cfg); + } + // If the use occurs after merging several exits from the loop, then + // old value must have dominated all those exits. Since the same old + // value was used on all those exits we did not need a Phi at this + // merge point. NOW we do need a Phi here. Each loop exit value + // is now merged with the peeled body exit; each exit gets its own + // private Phi and those Phis need to be merged here. + Node *phi; + if( prev->is_Region() ) { + if( idx == 0 ) { // Updating control edge? + phi = prev; // Just use existing control + } else { // Else need a new Phi + phi = PhiNode::make( prev, old ); + // Now recursively fix up the new uses of old! + for( uint i = 1; i < prev->req(); i++ ) { + worklist.push(phi); // Onto worklist once for each 'old' input + } + } + } else { + // Get new RegionNode merging old and new loop exits + prev = old_new[prev->_idx]; + assert( prev, "just made this in step 7" ); + if( idx == 0 ) { // Updating control edge? + phi = prev; // Just use existing control + } else { // Else need a new Phi + // Make a new Phi merging data values properly + phi = PhiNode::make( prev, old ); + phi->set_req( 1, nnn ); + } + } + // If inserting a new Phi, check for prior hits + if( idx != 0 ) { + Node *hit = _igvn.hash_find_insert(phi); + if( hit == NULL ) { + _igvn.register_new_node_with_optimizer(phi); // Register new phi + } else { // or + // Remove the new phi from the graph and use the hit + _igvn.remove_dead_node(phi); + phi = hit; // Use existing phi + } + set_ctrl(phi, prev); + } + // Make 'use' use the Phi instead of the old loop body exit value + _igvn.hash_delete(use); + _igvn._worklist.push(use); + use->set_req(idx, phi); + if( use->_idx >= new_counter ) { // If updating new phis + // Not needed for correctness, but prevents a weak assert + // in AddPNode from tripping (when we end up with different + // base & derived Phis that will become the same after + // IGVN does CSE). + Node *hit = _igvn.hash_find_insert(use); + if( hit ) // Go ahead and re-hash for hits. + _igvn.subsume_node( use, hit ); + } + + // If 'use' was in the loop-exit block, it now needs to be sunk + // below the post-loop merge point. + sink_use( use, prev ); + } + } + } + + // Check for IFs that need splitting/cloning. Happens if an IF outside of + // the loop uses a condition set in the loop. The original IF probably + // takes control from one or more OLD Regions (which in turn get from NEW + // Regions). In any case, there will be a set of Phis for each merge point + // from the IF up to where the original BOOL def exists the loop. + if( split_if_set ) { + while( split_if_set->size() ) { + Node *iff = split_if_set->pop(); + if( iff->in(1)->is_Phi() ) { + BoolNode *b = clone_iff( iff->in(1)->as_Phi(), loop ); + _igvn.hash_delete(iff); + _igvn._worklist.push(iff); + iff->set_req(1, b); + } + } + } + if( split_bool_set ) { + while( split_bool_set->size() ) { + Node *b = split_bool_set->pop(); + Node *phi = b->in(1); + assert( phi->is_Phi(), "" ); + CmpNode *cmp = clone_bool( (PhiNode*)phi, loop ); + _igvn.hash_delete(b); + _igvn._worklist.push(b); + b->set_req(1, cmp); + } + } + if( split_cex_set ) { + while( split_cex_set->size() ) { + Node *b = split_cex_set->pop(); + assert( b->in(0)->is_Region(), "" ); + assert( b->in(1)->is_Phi(), "" ); + assert( b->in(0)->in(0) == b->in(1)->in(0), "" ); + split_up( b, b->in(0), NULL ); + } + } + +} + + +//---------------------- stride_of_possible_iv ------------------------------------- +// Looks for an iff/bool/comp with one operand of the compare +// being a cycle involving an add and a phi, +// with an optional truncation (left-shift followed by a right-shift) +// of the add. Returns zero if not an iv. +int PhaseIdealLoop::stride_of_possible_iv(Node* iff) { + Node* trunc1 = NULL; + Node* trunc2 = NULL; + const TypeInt* ttype = NULL; + if (!iff->is_If() || iff->in(1) == NULL || !iff->in(1)->is_Bool()) { + return 0; + } + BoolNode* bl = iff->in(1)->as_Bool(); + Node* cmp = bl->in(1); + if (!cmp || cmp->Opcode() != Op_CmpI && cmp->Opcode() != Op_CmpU) { + return 0; + } + // Must have an invariant operand + if (is_member(get_loop(iff), get_ctrl(cmp->in(2)))) { + return 0; + } + Node* add2 = NULL; + Node* cmp1 = cmp->in(1); + if (cmp1->is_Phi()) { + // (If (Bool (CmpX phi:(Phi ...(Optional-trunc(AddI phi add2))) ))) + Node* phi = cmp1; + for (uint i = 1; i < phi->req(); i++) { + Node* in = phi->in(i); + Node* add = CountedLoopNode::match_incr_with_optional_truncation(in, + &trunc1, &trunc2, &ttype); + if (add && add->in(1) == phi) { + add2 = add->in(2); + break; + } + } + } else { + // (If (Bool (CmpX addtrunc:(Optional-trunc((AddI (Phi ...addtrunc...) add2)) ))) + Node* addtrunc = cmp1; + Node* add = CountedLoopNode::match_incr_with_optional_truncation(addtrunc, + &trunc1, &trunc2, &ttype); + if (add && add->in(1)->is_Phi()) { + Node* phi = add->in(1); + for (uint i = 1; i < phi->req(); i++) { + if (phi->in(i) == addtrunc) { + add2 = add->in(2); + break; + } + } + } + } + if (add2 != NULL) { + const TypeInt* add2t = _igvn.type(add2)->is_int(); + if (add2t->is_con()) { + return add2t->get_con(); + } + } + return 0; +} + + +//---------------------- stay_in_loop ------------------------------------- +// Return the (unique) control output node that's in the loop (if it exists.) +Node* PhaseIdealLoop::stay_in_loop( Node* n, IdealLoopTree *loop) { + Node* unique = NULL; + if (!n) return NULL; + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* use = n->fast_out(i); + if (!has_ctrl(use) && loop->is_member(get_loop(use))) { + if (unique != NULL) { + return NULL; + } + unique = use; + } + } + return unique; +} + +//------------------------------ register_node ------------------------------------- +// Utility to register node "n" with PhaseIdealLoop +void PhaseIdealLoop::register_node(Node* n, IdealLoopTree *loop, Node* pred, int ddepth) { + _igvn.register_new_node_with_optimizer(n); + loop->_body.push(n); + if (n->is_CFG()) { + set_loop(n, loop); + set_idom(n, pred, ddepth); + } else { + set_ctrl(n, pred); + } +} + +//------------------------------ proj_clone ------------------------------------- +// Utility to create an if-projection +ProjNode* PhaseIdealLoop::proj_clone(ProjNode* p, IfNode* iff) { + ProjNode* c = p->clone()->as_Proj(); + c->set_req(0, iff); + return c; +} + +//------------------------------ short_circuit_if ------------------------------------- +// Force the iff control output to be the live_proj +Node* PhaseIdealLoop::short_circuit_if(IfNode* iff, ProjNode* live_proj) { + int proj_con = live_proj->_con; + assert(proj_con == 0 || proj_con == 1, "false or true projection"); + Node *con = _igvn.intcon(proj_con); + set_ctrl(con, C->root()); + if (iff) { + iff->set_req(1, con); + } + return con; +} + +//------------------------------ insert_if_before_proj ------------------------------------- +// Insert a new if before an if projection (* - new node) +// +// before +// if(test) +// / \ +// v v +// other-proj proj (arg) +// +// after +// if(test) +// / \ +// / v +// | * proj-clone +// v | +// other-proj v +// * new_if(relop(cmp[IU](left,right))) +// / \ +// v v +// * new-proj proj +// (returned) +// +ProjNode* PhaseIdealLoop::insert_if_before_proj(Node* left, bool Signed, BoolTest::mask relop, Node* right, ProjNode* proj) { + IfNode* iff = proj->in(0)->as_If(); + IdealLoopTree *loop = get_loop(proj); + ProjNode *other_proj = iff->proj_out(!proj->is_IfTrue())->as_Proj(); + int ddepth = dom_depth(proj); + + _igvn.hash_delete(iff); + _igvn._worklist.push(iff); + _igvn.hash_delete(proj); + _igvn._worklist.push(proj); + + proj->set_req(0, NULL); // temporary disconnect + ProjNode* proj2 = proj_clone(proj, iff); + register_node(proj2, loop, iff, ddepth); + + Node* cmp = Signed ? (Node*) new (C,3)CmpINode(left, right) : (Node*) new (C,3)CmpUNode(left, right); + register_node(cmp, loop, proj2, ddepth); + + BoolNode* bol = new (C,2)BoolNode(cmp, relop); + register_node(bol, loop, proj2, ddepth); + + IfNode* new_if = new (C,2)IfNode(proj2, bol, iff->_prob, iff->_fcnt); + register_node(new_if, loop, proj2, ddepth); + + proj->set_req(0, new_if); // reattach + set_idom(proj, new_if, ddepth); + + ProjNode* new_exit = proj_clone(other_proj, new_if)->as_Proj(); + register_node(new_exit, get_loop(other_proj), new_if, ddepth); + + return new_exit; +} + +//------------------------------ insert_region_before_proj ------------------------------------- +// Insert a region before an if projection (* - new node) +// +// before +// if(test) +// / | +// v | +// proj v +// other-proj +// +// after +// if(test) +// / | +// v | +// * proj-clone v +// | other-proj +// v +// * new-region +// | +// v +// * dum_if +// / \ +// v \ +// * dum-proj v +// proj +// +RegionNode* PhaseIdealLoop::insert_region_before_proj(ProjNode* proj) { + IfNode* iff = proj->in(0)->as_If(); + IdealLoopTree *loop = get_loop(proj); + ProjNode *other_proj = iff->proj_out(!proj->is_IfTrue())->as_Proj(); + int ddepth = dom_depth(proj); + + _igvn.hash_delete(iff); + _igvn._worklist.push(iff); + _igvn.hash_delete(proj); + _igvn._worklist.push(proj); + + proj->set_req(0, NULL); // temporary disconnect + ProjNode* proj2 = proj_clone(proj, iff); + register_node(proj2, loop, iff, ddepth); + + RegionNode* reg = new (C,2)RegionNode(2); + reg->set_req(1, proj2); + register_node(reg, loop, iff, ddepth); + + IfNode* dum_if = new (C,2)IfNode(reg, short_circuit_if(NULL, proj), iff->_prob, iff->_fcnt); + register_node(dum_if, loop, reg, ddepth); + + proj->set_req(0, dum_if); // reattach + set_idom(proj, dum_if, ddepth); + + ProjNode* dum_proj = proj_clone(other_proj, dum_if); + register_node(dum_proj, loop, dum_if, ddepth); + + return reg; +} + +//------------------------------ insert_cmpi_loop_exit ------------------------------------- +// Clone a signed compare loop exit from an unsigned compare and +// insert it before the unsigned cmp on the stay-in-loop path. +// All new nodes inserted in the dominator tree between the original +// if and it's projections. The original if test is replaced with +// a constant to force the stay-in-loop path. +// +// This is done to make sure that the original if and it's projections +// still dominate the same set of control nodes, that the ctrl() relation +// from data nodes to them is preserved, and that their loop nesting is +// preserved. +// +// before +// if(i in(1)->as_Bool(); + if (bol->_test._test != BoolTest::lt) return NULL; + CmpNode* cmpu = bol->in(1)->as_Cmp(); + if (cmpu->Opcode() != Op_CmpU) return NULL; + int stride = stride_of_possible_iv(if_cmpu); + if (stride == 0) return NULL; + + ProjNode* lp_continue = stay_in_loop(if_cmpu, loop)->as_Proj(); + ProjNode* lp_exit = if_cmpu->proj_out(!lp_continue->is_IfTrue())->as_Proj(); + + Node* limit = NULL; + if (stride > 0) { + limit = cmpu->in(2); + } else { + limit = _igvn.makecon(TypeInt::ZERO); + set_ctrl(limit, C->root()); + } + // Create a new region on the exit path + RegionNode* reg = insert_region_before_proj(lp_exit); + + // Clone the if-cmpu-true-false using a signed compare + BoolTest::mask rel_i = stride > 0 ? bol->_test._test : BoolTest::ge; + ProjNode* cmpi_exit = insert_if_before_proj(cmpu->in(1), Signed, rel_i, limit, lp_continue); + reg->add_req(cmpi_exit); + + // Clone the if-cmpu-true-false + BoolTest::mask rel_u = bol->_test._test; + ProjNode* cmpu_exit = insert_if_before_proj(cmpu->in(1), Unsigned, rel_u, cmpu->in(2), lp_continue); + reg->add_req(cmpu_exit); + + // Force original if to stay in loop. + short_circuit_if(if_cmpu, lp_continue); + + return cmpi_exit->in(0)->as_If(); +} + +//------------------------------ remove_cmpi_loop_exit ------------------------------------- +// Remove a previously inserted signed compare loop exit. +void PhaseIdealLoop::remove_cmpi_loop_exit(IfNode* if_cmp, IdealLoopTree *loop) { + Node* lp_proj = stay_in_loop(if_cmp, loop); + assert(if_cmp->in(1)->in(1)->Opcode() == Op_CmpI && + stay_in_loop(lp_proj, loop)->is_If() && + stay_in_loop(lp_proj, loop)->in(1)->in(1)->Opcode() == Op_CmpU, "inserted cmpi before cmpu"); + Node *con = _igvn.makecon(lp_proj->is_IfTrue() ? TypeInt::ONE : TypeInt::ZERO); + set_ctrl(con, C->root()); + if_cmp->set_req(1, con); +} + +//------------------------------ scheduled_nodelist ------------------------------------- +// Create a post order schedule of nodes that are in the +// "member" set. The list is returned in "sched". +// The first node in "sched" is the loop head, followed by +// nodes which have no inputs in the "member" set, and then +// followed by the nodes that have an immediate input dependence +// on a node in "sched". +void PhaseIdealLoop::scheduled_nodelist( IdealLoopTree *loop, VectorSet& member, Node_List &sched ) { + + assert(member.test(loop->_head->_idx), "loop head must be in member set"); + Arena *a = Thread::current()->resource_area(); + VectorSet visited(a); + Node_Stack nstack(a, loop->_body.size()); + + Node* n = loop->_head; // top of stack is cached in "n" + uint idx = 0; + visited.set(n->_idx); + + // Initially push all with no inputs from within member set + for(uint i = 0; i < loop->_body.size(); i++ ) { + Node *elt = loop->_body.at(i); + if (member.test(elt->_idx)) { + bool found = false; + for (uint j = 0; j < elt->req(); j++) { + Node* def = elt->in(j); + if (def && member.test(def->_idx) && def != elt) { + found = true; + break; + } + } + if (!found && elt != loop->_head) { + nstack.push(n, idx); + n = elt; + assert(!visited.test(n->_idx), "not seen yet"); + visited.set(n->_idx); + } + } + } + + // traverse out's that are in the member set + while (true) { + if (idx < n->outcnt()) { + Node* use = n->raw_out(idx); + idx++; + if (!visited.test_set(use->_idx)) { + if (member.test(use->_idx)) { + nstack.push(n, idx); + n = use; + idx = 0; + } + } + } else { + // All outputs processed + sched.push(n); + if (nstack.is_empty()) break; + n = nstack.node(); + idx = nstack.index(); + nstack.pop(); + } + } +} + + +//------------------------------ has_use_in_set ------------------------------------- +// Has a use in the vector set +bool PhaseIdealLoop::has_use_in_set( Node* n, VectorSet& vset ) { + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { + Node* use = n->fast_out(j); + if (vset.test(use->_idx)) { + return true; + } + } + return false; +} + + +//------------------------------ has_use_internal_to_set ------------------------------------- +// Has use internal to the vector set (ie. not in a phi at the loop head) +bool PhaseIdealLoop::has_use_internal_to_set( Node* n, VectorSet& vset, IdealLoopTree *loop ) { + Node* head = loop->_head; + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { + Node* use = n->fast_out(j); + if (vset.test(use->_idx) && !(use->is_Phi() && use->in(0) == head)) { + return true; + } + } + return false; +} + + +//------------------------------ clone_for_use_outside_loop ------------------------------------- +// clone "n" for uses that are outside of loop +void PhaseIdealLoop::clone_for_use_outside_loop( IdealLoopTree *loop, Node* n, Node_List& worklist ) { + + assert(worklist.size() == 0, "should be empty"); + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { + Node* use = n->fast_out(j); + if( !loop->is_member(get_loop(has_ctrl(use) ? get_ctrl(use) : use)) ) { + worklist.push(use); + } + } + while( worklist.size() ) { + Node *use = worklist.pop(); + if (!has_node(use) || use->in(0) == C->top()) continue; + uint j; + for (j = 0; j < use->req(); j++) { + if (use->in(j) == n) break; + } + assert(j < use->req(), "must be there"); + + // clone "n" and insert it between the inputs of "n" and the use outside the loop + Node* n_clone = n->clone(); + _igvn.hash_delete(use); + use->set_req(j, n_clone); + _igvn._worklist.push(use); + if (!use->is_Phi()) { + Node* use_c = has_ctrl(use) ? get_ctrl(use) : use->in(0); + set_ctrl(n_clone, use_c); + assert(!loop->is_member(get_loop(use_c)), "should be outside loop"); + get_loop(use_c)->_body.push(n_clone); + } else { + // Use in a phi is considered a use in the associated predecessor block + Node *prevbb = use->in(0)->in(j); + set_ctrl(n_clone, prevbb); + assert(!loop->is_member(get_loop(prevbb)), "should be outside loop"); + get_loop(prevbb)->_body.push(n_clone); + } + _igvn.register_new_node_with_optimizer(n_clone); +#if !defined(PRODUCT) + if (TracePartialPeeling) { + tty->print_cr("loop exit cloning old: %d new: %d newbb: %d", n->_idx, n_clone->_idx, get_ctrl(n_clone)->_idx); + } +#endif + } +} + + +//------------------------------ clone_for_special_use_inside_loop ------------------------------------- +// clone "n" for special uses that are in the not_peeled region. +// If these def-uses occur in separate blocks, the code generator +// marks the method as not compilable. For example, if a "BoolNode" +// is in a different basic block than the "IfNode" that uses it, then +// the compilation is aborted in the code generator. +void PhaseIdealLoop::clone_for_special_use_inside_loop( IdealLoopTree *loop, Node* n, + VectorSet& not_peel, Node_List& sink_list, Node_List& worklist ) { + if (n->is_Phi() || n->is_Load()) { + return; + } + assert(worklist.size() == 0, "should be empty"); + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { + Node* use = n->fast_out(j); + if ( not_peel.test(use->_idx) && + (use->is_If() || use->is_CMove() || use->is_Bool()) && + use->in(1) == n) { + worklist.push(use); + } + } + if (worklist.size() > 0) { + // clone "n" and insert it between inputs of "n" and the use + Node* n_clone = n->clone(); + loop->_body.push(n_clone); + _igvn.register_new_node_with_optimizer(n_clone); + set_ctrl(n_clone, get_ctrl(n)); + sink_list.push(n_clone); + not_peel <<= n_clone->_idx; // add n_clone to not_peel set. +#if !defined(PRODUCT) + if (TracePartialPeeling) { + tty->print_cr("special not_peeled cloning old: %d new: %d", n->_idx, n_clone->_idx); + } +#endif + while( worklist.size() ) { + Node *use = worklist.pop(); + _igvn.hash_delete(use); + _igvn._worklist.push(use); + for (uint j = 1; j < use->req(); j++) { + if (use->in(j) == n) { + use->set_req(j, n_clone); + } + } + } + } +} + + +//------------------------------ insert_phi_for_loop ------------------------------------- +// Insert phi(lp_entry_val, back_edge_val) at use->in(idx) for loop lp if phi does not already exist +void PhaseIdealLoop::insert_phi_for_loop( Node* use, uint idx, Node* lp_entry_val, Node* back_edge_val, LoopNode* lp ) { + Node *phi = PhiNode::make(lp, back_edge_val); + phi->set_req(LoopNode::EntryControl, lp_entry_val); + // Use existing phi if it already exists + Node *hit = _igvn.hash_find_insert(phi); + if( hit == NULL ) { + _igvn.register_new_node_with_optimizer(phi); + set_ctrl(phi, lp); + } else { + // Remove the new phi from the graph and use the hit + _igvn.remove_dead_node(phi); + phi = hit; + } + _igvn.hash_delete(use); + _igvn._worklist.push(use); + use->set_req(idx, phi); +} + +#ifdef ASSERT +//------------------------------ is_valid_loop_partition ------------------------------------- +// Validate the loop partition sets: peel and not_peel +bool PhaseIdealLoop::is_valid_loop_partition( IdealLoopTree *loop, VectorSet& peel, Node_List& peel_list, + VectorSet& not_peel ) { + uint i; + // Check that peel_list entries are in the peel set + for (i = 0; i < peel_list.size(); i++) { + if (!peel.test(peel_list.at(i)->_idx)) { + return false; + } + } + // Check at loop members are in one of peel set or not_peel set + for (i = 0; i < loop->_body.size(); i++ ) { + Node *def = loop->_body.at(i); + uint di = def->_idx; + // Check that peel set elements are in peel_list + if (peel.test(di)) { + if (not_peel.test(di)) { + return false; + } + // Must be in peel_list also + bool found = false; + for (uint j = 0; j < peel_list.size(); j++) { + if (peel_list.at(j)->_idx == di) { + found = true; + break; + } + } + if (!found) { + return false; + } + } else if (not_peel.test(di)) { + if (peel.test(di)) { + return false; + } + } else { + return false; + } + } + return true; +} + +//------------------------------ is_valid_clone_loop_exit_use ------------------------------------- +// Ensure a use outside of loop is of the right form +bool PhaseIdealLoop::is_valid_clone_loop_exit_use( IdealLoopTree *loop, Node* use, uint exit_idx) { + Node *use_c = has_ctrl(use) ? get_ctrl(use) : use; + return (use->is_Phi() && + use_c->is_Region() && use_c->req() == 3 && + (use_c->in(exit_idx)->Opcode() == Op_IfTrue || + use_c->in(exit_idx)->Opcode() == Op_IfFalse || + use_c->in(exit_idx)->Opcode() == Op_JumpProj) && + loop->is_member( get_loop( use_c->in(exit_idx)->in(0) ) ) ); +} + +//------------------------------ is_valid_clone_loop_form ------------------------------------- +// Ensure that all uses outside of loop are of the right form +bool PhaseIdealLoop::is_valid_clone_loop_form( IdealLoopTree *loop, Node_List& peel_list, + uint orig_exit_idx, uint clone_exit_idx) { + uint len = peel_list.size(); + for (uint i = 0; i < len; i++) { + Node *def = peel_list.at(i); + + for (DUIterator_Fast jmax, j = def->fast_outs(jmax); j < jmax; j++) { + Node *use = def->fast_out(j); + Node *use_c = has_ctrl(use) ? get_ctrl(use) : use; + if (!loop->is_member(get_loop(use_c))) { + // use is not in the loop, check for correct structure + if (use->in(0) == def) { + // Okay + } else if (!is_valid_clone_loop_exit_use(loop, use, orig_exit_idx)) { + return false; + } + } + } + } + return true; +} +#endif + +//------------------------------ partial_peel ------------------------------------- +// Partially peel (aka loop rotation) the top portion of a loop (called +// the peel section below) by cloning it and placing one copy just before +// the new loop head and the other copy at the bottom of the new loop. +// +// before after where it came from +// +// stmt1 stmt1 +// loop: stmt2 clone +// stmt2 if condA goto exitA clone +// if condA goto exitA new_loop: new +// stmt3 stmt3 clone +// if !condB goto loop if condB goto exitB clone +// exitB: stmt2 orig +// stmt4 if !condA goto new_loop orig +// exitA: goto exitA +// exitB: +// stmt4 +// exitA: +// +// Step 1: find the cut point: an exit test on probable +// induction variable. +// Step 2: schedule (with cloning) operations in the peel +// section that can be executed after the cut into +// the section that is not peeled. This may need +// to clone operations into exit blocks. For +// instance, a reference to A[i] in the not-peel +// section and a reference to B[i] in an exit block +// may cause a left-shift of i by 2 to be placed +// in the peel block. This step will clone the left +// shift into the exit block and sink the left shift +// from the peel to the not-peel section. +// Step 3: clone the loop, retarget the control, and insert +// phis for values that are live across the new loop +// head. This is very dependent on the graph structure +// from clone_loop. It creates region nodes for +// exit control and associated phi nodes for values +// flow out of the loop through that exit. The region +// node is dominated by the clone's control projection. +// So the clone's peel section is placed before the +// new loop head, and the clone's not-peel section is +// forms the top part of the new loop. The original +// peel section forms the tail of the new loop. +// Step 4: update the dominator tree and recompute the +// dominator depth. +// +// orig +// +// stmt1 +// | +// v +// loop<----+ +// | | +// stmt2 | +// | | +// v | +// ifA | +// / | | +// v v | +// false true ^ <-- last_peel +// / | | +// / ===|==cut | +// / stmt3 | <-- first_not_peel +// / | | +// | v | +// v ifB | +// exitA: / \ | +// / \ | +// v v | +// false true | +// / \ | +// / ----+ +// | +// v +// exitB: +// stmt4 +// +// +// after clone loop +// +// stmt1 +// / \ +// clone / \ orig +// / \ +// / \ +// v v +// +---->loop loop<----+ +// | | | | +// | stmt2 stmt2 | +// | | | | +// | v v | +// | ifA ifA | +// | | \ / | | +// | v v v v | +// ^ true false false true ^ <-- last_peel +// | | ^ \ / | | +// | cut==|== \ \ / ===|==cut | +// | stmt3 \ \ / stmt3 | <-- first_not_peel +// | | dom | | | | +// | v \ 1v v2 v | +// | ifB regionA ifB | +// | / \ | / \ | +// | / \ v / \ | +// | v v exitA: v v | +// | true false false true | +// | / ^ \ / \ | +// +---- \ \ / ----+ +// dom \ / +// \ 1v v2 +// regionB +// | +// v +// exitB: +// stmt4 +// +// +// after partial peel +// +// stmt1 +// / +// clone / orig +// / TOP +// / \ +// v v +// TOP->region region----+ +// | | | +// stmt2 stmt2 | +// | | | +// v v | +// ifA ifA | +// | \ / | | +// v v v v | +// true false false true | <-- last_peel +// | ^ \ / +------|---+ +// +->newloop \ \ / === ==cut | | +// | stmt3 \ \ / TOP | | +// | | dom | | stmt3 | | <-- first_not_peel +// | v \ 1v v2 v | | +// | ifB regionA ifB ^ v +// | / \ | / \ | | +// | / \ v / \ | | +// | v v exitA: v v | | +// | true false false true | | +// | / ^ \ / \ | | +// | | \ \ / v | | +// | | dom \ / TOP | | +// | | \ 1v v2 | | +// ^ v regionB | | +// | | | | | +// | | v ^ v +// | | exitB: | | +// | | stmt4 | | +// | +------------>-----------------+ | +// | | +// +-----------------<---------------------+ +// +// +// final graph +// +// stmt1 +// | +// v +// ........> ifA clone +// : / | +// dom / | +// : v v +// : false true +// : | | +// : | stmt2 clone +// : | | +// : | v +// : | newloop<-----+ +// : | | | +// : | stmt3 clone | +// : | | | +// : | v | +// : | ifB | +// : | / \ | +// : | v v | +// : | false true | +// : | | | | +// : | v stmt2 | +// : | exitB: | | +// : | stmt4 v | +// : | ifA orig | +// : | / \ | +// : | / \ | +// : | v v | +// : | false true | +// : | / \ | +// : v v -----+ +// RegionA +// | +// v +// exitA +// +bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { + + LoopNode *head = loop->_head->as_Loop(); + + if (head->is_partial_peel_loop() || head->partial_peel_has_failed()) { + return false; + } + + // Check for complex exit control + for(uint ii = 0; ii < loop->_body.size(); ii++ ) { + Node *n = loop->_body.at(ii); + int opc = n->Opcode(); + if (n->is_Call() || + opc == Op_Catch || + opc == Op_CatchProj || + opc == Op_Jump || + opc == Op_JumpProj) { +#if !defined(PRODUCT) + if (TracePartialPeeling) { + tty->print_cr("\nExit control too complex: lp: %d", head->_idx); + } +#endif + return false; + } + } + + int dd = dom_depth(head); + + // Step 1: find cut point + + // Walk up dominators to loop head looking for first loop exit + // which is executed on every path thru loop. + IfNode *peel_if = NULL; + IfNode *peel_if_cmpu = NULL; + + Node *iff = loop->tail(); + while( iff != head ) { + if( iff->is_If() ) { + Node *ctrl = get_ctrl(iff->in(1)); + if (ctrl->is_top()) return false; // Dead test on live IF. + // If loop-varying exit-test, check for induction variable + if( loop->is_member(get_loop(ctrl)) && + loop->is_loop_exit(iff) && + is_possible_iv_test(iff)) { + Node* cmp = iff->in(1)->in(1); + if (cmp->Opcode() == Op_CmpI) { + peel_if = iff->as_If(); + } else { + assert(cmp->Opcode() == Op_CmpU, "must be CmpI or CmpU"); + peel_if_cmpu = iff->as_If(); + } + } + } + iff = idom(iff); + } + // Prefer signed compare over unsigned compare. + IfNode* new_peel_if = NULL; + if (peel_if == NULL) { + if (!PartialPeelAtUnsignedTests || peel_if_cmpu == NULL) { + return false; // No peel point found + } + new_peel_if = insert_cmpi_loop_exit(peel_if_cmpu, loop); + if (new_peel_if == NULL) { + return false; // No peel point found + } + peel_if = new_peel_if; + } + Node* last_peel = stay_in_loop(peel_if, loop); + Node* first_not_peeled = stay_in_loop(last_peel, loop); + if (first_not_peeled == NULL || first_not_peeled == head) { + return false; + } + +#if !defined(PRODUCT) + if (TracePartialPeeling) { + tty->print_cr("before partial peel one iteration"); + Node_List wl; + Node* t = head->in(2); + while (true) { + wl.push(t); + if (t == head) break; + t = idom(t); + } + while (wl.size() > 0) { + Node* tt = wl.pop(); + tt->dump(); + if (tt == last_peel) tty->print_cr("-- cut --"); + } + } +#endif + ResourceArea *area = Thread::current()->resource_area(); + VectorSet peel(area); + VectorSet not_peel(area); + Node_List peel_list(area); + Node_List worklist(area); + Node_List sink_list(area); + + // Set of cfg nodes to peel are those that are executable from + // the head through last_peel. + assert(worklist.size() == 0, "should be empty"); + worklist.push(head); + peel.set(head->_idx); + while (worklist.size() > 0) { + Node *n = worklist.pop(); + if (n != last_peel) { + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { + Node* use = n->fast_out(j); + if (use->is_CFG() && + loop->is_member(get_loop(use)) && + !peel.test_set(use->_idx)) { + worklist.push(use); + } + } + } + } + + // Set of non-cfg nodes to peel are those that are control + // dependent on the cfg nodes. + uint i; + for(i = 0; i < loop->_body.size(); i++ ) { + Node *n = loop->_body.at(i); + Node *n_c = has_ctrl(n) ? get_ctrl(n) : n; + if (peel.test(n_c->_idx)) { + peel.set(n->_idx); + } else { + not_peel.set(n->_idx); + } + } + + // Step 2: move operations from the peeled section down into the + // not-peeled section + + // Get a post order schedule of nodes in the peel region + // Result in right-most operand. + scheduled_nodelist(loop, peel, peel_list ); + + assert(is_valid_loop_partition(loop, peel, peel_list, not_peel), "bad partition"); + + // For future check for too many new phis + uint old_phi_cnt = 0; + for (DUIterator_Fast jmax, j = head->fast_outs(jmax); j < jmax; j++) { + Node* use = head->fast_out(j); + if (use->is_Phi()) old_phi_cnt++; + } + +#if !defined(PRODUCT) + if (TracePartialPeeling) { + tty->print_cr("\npeeled list"); + } +#endif + + // Evacuate nodes in peel region into the not_peeled region if possible + uint new_phi_cnt = 0; + for (i = 0; i < peel_list.size();) { + Node* n = peel_list.at(i); +#if !defined(PRODUCT) + if (TracePartialPeeling) n->dump(); +#endif + bool incr = true; + if ( !n->is_CFG() ) { + + if ( has_use_in_set(n, not_peel) ) { + + // If not used internal to the peeled region, + // move "n" from peeled to not_peeled region. + + if ( !has_use_internal_to_set(n, peel, loop) ) { + + // if not pinned and not a load (which maybe anti-dependent on a store) + // and not a CMove (Matcher expects only bool->cmove). + if ( n->in(0) == NULL && !n->is_Load() && !n->is_CMove() ) { + clone_for_use_outside_loop( loop, n, worklist ); + + sink_list.push(n); + peel >>= n->_idx; // delete n from peel set. + not_peel <<= n->_idx; // add n to not_peel set. + peel_list.remove(i); + incr = false; +#if !defined(PRODUCT) + if (TracePartialPeeling) { + tty->print_cr("sink to not_peeled region: %d newbb: %d", + n->_idx, get_ctrl(n)->_idx); + } +#endif + } + } else { + // Otherwise check for special def-use cases that span + // the peel/not_peel boundary such as bool->if + clone_for_special_use_inside_loop( loop, n, not_peel, sink_list, worklist ); + new_phi_cnt++; + } + } + } + if (incr) i++; + } + + if (new_phi_cnt > old_phi_cnt + PartialPeelNewPhiDelta) { +#if !defined(PRODUCT) + if (TracePartialPeeling) { + tty->print_cr("\nToo many new phis: %d old %d new cmpi: %c", + new_phi_cnt, old_phi_cnt, new_peel_if != NULL?'T':'F'); + } +#endif + if (new_peel_if != NULL) { + remove_cmpi_loop_exit(new_peel_if, loop); + } + // Inhibit more partial peeling on this loop + assert(!head->is_partial_peel_loop(), "not partial peeled"); + head->mark_partial_peel_failed(); + return false; + } + + // Step 3: clone loop, retarget control, and insert new phis + + // Create new loop head for new phis and to hang + // the nodes being moved (sinked) from the peel region. + LoopNode* new_head = new (C, 3) LoopNode(last_peel, last_peel); + _igvn.register_new_node_with_optimizer(new_head); + assert(first_not_peeled->in(0) == last_peel, "last_peel <- first_not_peeled"); + first_not_peeled->set_req(0, new_head); + set_loop(new_head, loop); + loop->_body.push(new_head); + not_peel.set(new_head->_idx); + set_idom(new_head, last_peel, dom_depth(first_not_peeled)); + set_idom(first_not_peeled, new_head, dom_depth(first_not_peeled)); + + while (sink_list.size() > 0) { + Node* n = sink_list.pop(); + set_ctrl(n, new_head); + } + + assert(is_valid_loop_partition(loop, peel, peel_list, not_peel), "bad partition"); + + clone_loop( loop, old_new, dd ); + + const uint clone_exit_idx = 1; + const uint orig_exit_idx = 2; + assert(is_valid_clone_loop_form( loop, peel_list, orig_exit_idx, clone_exit_idx ), "bad clone loop"); + + Node* head_clone = old_new[head->_idx]; + LoopNode* new_head_clone = old_new[new_head->_idx]->as_Loop(); + Node* orig_tail_clone = head_clone->in(2); + + // Add phi if "def" node is in peel set and "use" is not + + for(i = 0; i < peel_list.size(); i++ ) { + Node *def = peel_list.at(i); + if (!def->is_CFG()) { + for (DUIterator_Fast jmax, j = def->fast_outs(jmax); j < jmax; j++) { + Node *use = def->fast_out(j); + if (has_node(use) && use->in(0) != C->top() && + (!peel.test(use->_idx) || + (use->is_Phi() && use->in(0) == head)) ) { + worklist.push(use); + } + } + while( worklist.size() ) { + Node *use = worklist.pop(); + for (uint j = 1; j < use->req(); j++) { + Node* n = use->in(j); + if (n == def) { + + // "def" is in peel set, "use" is not in peel set + // or "use" is in the entry boundary (a phi) of the peel set + + Node* use_c = has_ctrl(use) ? get_ctrl(use) : use; + + if ( loop->is_member(get_loop( use_c )) ) { + // use is in loop + if (old_new[use->_idx] != NULL) { // null for dead code + Node* use_clone = old_new[use->_idx]; + _igvn.hash_delete(use); + use->set_req(j, C->top()); + _igvn._worklist.push(use); + insert_phi_for_loop( use_clone, j, old_new[def->_idx], def, new_head_clone ); + } + } else { + assert(is_valid_clone_loop_exit_use(loop, use, orig_exit_idx), "clone loop format"); + // use is not in the loop, check if the live range includes the cut + Node* lp_if = use_c->in(orig_exit_idx)->in(0); + if (not_peel.test(lp_if->_idx)) { + assert(j == orig_exit_idx, "use from original loop"); + insert_phi_for_loop( use, clone_exit_idx, old_new[def->_idx], def, new_head_clone ); + } + } + } + } + } + } + } + + // Step 3b: retarget control + + // Redirect control to the new loop head if a cloned node in + // the not_peeled region has control that points into the peeled region. + // This necessary because the cloned peeled region will be outside + // the loop. + // from to + // cloned-peeled <---+ + // new_head_clone: | <--+ + // cloned-not_peeled in(0) in(0) + // orig-peeled + + for(i = 0; i < loop->_body.size(); i++ ) { + Node *n = loop->_body.at(i); + if (!n->is_CFG() && n->in(0) != NULL && + not_peel.test(n->_idx) && peel.test(n->in(0)->_idx)) { + Node* n_clone = old_new[n->_idx]; + _igvn.hash_delete(n_clone); + n_clone->set_req(0, new_head_clone); + _igvn._worklist.push(n_clone); + } + } + + // Backedge of the surviving new_head (the clone) is original last_peel + _igvn.hash_delete(new_head_clone); + new_head_clone->set_req(LoopNode::LoopBackControl, last_peel); + _igvn._worklist.push(new_head_clone); + + // Cut first node in original not_peel set + _igvn.hash_delete(new_head); + new_head->set_req(LoopNode::EntryControl, C->top()); + new_head->set_req(LoopNode::LoopBackControl, C->top()); + _igvn._worklist.push(new_head); + + // Copy head_clone back-branch info to original head + // and remove original head's loop entry and + // clone head's back-branch + _igvn.hash_delete(head); + _igvn.hash_delete(head_clone); + head->set_req(LoopNode::EntryControl, head_clone->in(LoopNode::LoopBackControl)); + head->set_req(LoopNode::LoopBackControl, C->top()); + head_clone->set_req(LoopNode::LoopBackControl, C->top()); + _igvn._worklist.push(head); + _igvn._worklist.push(head_clone); + + // Similarly modify the phis + for (DUIterator_Fast kmax, k = head->fast_outs(kmax); k < kmax; k++) { + Node* use = head->fast_out(k); + if (use->is_Phi() && use->outcnt() > 0) { + Node* use_clone = old_new[use->_idx]; + _igvn.hash_delete(use); + _igvn.hash_delete(use_clone); + use->set_req(LoopNode::EntryControl, use_clone->in(LoopNode::LoopBackControl)); + use->set_req(LoopNode::LoopBackControl, C->top()); + use_clone->set_req(LoopNode::LoopBackControl, C->top()); + _igvn._worklist.push(use); + _igvn._worklist.push(use_clone); + } + } + + // Step 4: update dominator tree and dominator depth + + set_idom(head, orig_tail_clone, dd); + recompute_dom_depth(); + + // Inhibit more partial peeling on this loop + new_head_clone->set_partial_peel_loop(); + C->set_major_progress(); + +#if !defined(PRODUCT) + if (TracePartialPeeling) { + tty->print_cr("\nafter partial peel one iteration"); + Node_List wl(area); + Node* t = last_peel; + while (true) { + wl.push(t); + if (t == head_clone) break; + t = idom(t); + } + while (wl.size() > 0) { + Node* tt = wl.pop(); + if (tt == head) tty->print_cr("orig head"); + else if (tt == new_head_clone) tty->print_cr("new head"); + else if (tt == head_clone) tty->print_cr("clone head"); + tt->dump(); + } + } +#endif + return true; +} + +//------------------------------reorg_offsets---------------------------------- +// Reorganize offset computations to lower register pressure. Mostly +// prevent loop-fallout uses of the pre-incremented trip counter (which are +// then alive with the post-incremented trip counter forcing an extra +// register move) +void PhaseIdealLoop::reorg_offsets( IdealLoopTree *loop ) { + + CountedLoopNode *cl = loop->_head->as_CountedLoop(); + CountedLoopEndNode *cle = cl->loopexit(); + if( !cle ) return; // The occasional dead loop + // Find loop exit control + Node *exit = cle->proj_out(false); + assert( exit->Opcode() == Op_IfFalse, "" ); + + // Check for the special case of folks using the pre-incremented + // trip-counter on the fall-out path (forces the pre-incremented + // and post-incremented trip counter to be live at the same time). + // Fix this by adjusting to use the post-increment trip counter. + Node *phi = cl->phi(); + if( !phi ) return; // Dead infinite loop + bool progress = true; + while (progress) { + progress = false; + for (DUIterator_Fast imax, i = phi->fast_outs(imax); i < imax; i++) { + Node* use = phi->fast_out(i); // User of trip-counter + if (!has_ctrl(use)) continue; + Node *u_ctrl = get_ctrl(use); + if( use->is_Phi() ) { + u_ctrl = NULL; + for( uint j = 1; j < use->req(); j++ ) + if( use->in(j) == phi ) + u_ctrl = dom_lca( u_ctrl, use->in(0)->in(j) ); + } + IdealLoopTree *u_loop = get_loop(u_ctrl); + // Look for loop-invariant use + if( u_loop == loop ) continue; + if( loop->is_member( u_loop ) ) continue; + // Check that use is live out the bottom. Assuming the trip-counter + // update is right at the bottom, uses of of the loop middle are ok. + if( dom_lca( exit, u_ctrl ) != exit ) continue; + // protect against stride not being a constant + if( !cle->stride_is_con() ) continue; + // Hit! Refactor use to use the post-incremented tripcounter. + // Compute a post-increment tripcounter. + Node *opaq = new (C, 2) Opaque2Node( cle->incr() ); + register_new_node( opaq, u_ctrl ); + Node *neg_stride = _igvn.intcon(-cle->stride_con()); + set_ctrl(neg_stride, C->root()); + Node *post = new (C, 3) AddINode( opaq, neg_stride); + register_new_node( post, u_ctrl ); + _igvn.hash_delete(use); + _igvn._worklist.push(use); + for( uint j = 1; j < use->req(); j++ ) + if( use->in(j) == phi ) + use->set_req(j, post); + // Since DU info changed, rerun loop + progress = true; + break; + } + } + +} diff --git a/hotspot/src/share/vm/opto/machnode.cpp b/hotspot/src/share/vm/opto/machnode.cpp new file mode 100644 index 00000000000..8b88f00b5fe --- /dev/null +++ b/hotspot/src/share/vm/opto/machnode.cpp @@ -0,0 +1,707 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_machnode.cpp.incl" + +//============================================================================= +// Return the value requested +// result register lookup, corresponding to int_format +int MachOper::reg(PhaseRegAlloc *ra_, const Node *node) const { + return (int)ra_->get_encode(node); +} +// input register lookup, corresponding to ext_format +int MachOper::reg(PhaseRegAlloc *ra_, const Node *node, int idx) const { + return (int)(ra_->get_encode(node->in(idx))); +} +intptr_t MachOper::constant() const { return 0x00; } +bool MachOper::constant_is_oop() const { return false; } +jdouble MachOper::constantD() const { ShouldNotReachHere(); return 0.0; } +jfloat MachOper::constantF() const { ShouldNotReachHere(); return 0.0; } +jlong MachOper::constantL() const { ShouldNotReachHere(); return CONST64(0) ; } +TypeOopPtr *MachOper::oop() const { return NULL; } +int MachOper::ccode() const { return 0x00; } +// A zero, default, indicates this value is not needed. +// May need to lookup the base register, as done in int_ and ext_format +int MachOper::base (PhaseRegAlloc *ra_, const Node *node, int idx) const { return 0x00; } +int MachOper::index(PhaseRegAlloc *ra_, const Node *node, int idx) const { return 0x00; } +int MachOper::scale() const { return 0x00; } +int MachOper::disp (PhaseRegAlloc *ra_, const Node *node, int idx) const { return 0x00; } +int MachOper::constant_disp() const { return 0; } +int MachOper::base_position() const { return -1; } // no base input +int MachOper::index_position() const { return -1; } // no index input +// Check for PC-Relative displacement +bool MachOper::disp_is_oop() const { return false; } +// Return the label +Label* MachOper::label() const { ShouldNotReachHere(); return 0; } +intptr_t MachOper::method() const { ShouldNotReachHere(); return 0; } + + +//------------------------------negate----------------------------------------- +// Negate conditional branches. Error for non-branch operands +void MachOper::negate() { + ShouldNotCallThis(); +} + +//-----------------------------type-------------------------------------------- +const Type *MachOper::type() const { + return Type::BOTTOM; +} + +//------------------------------in_RegMask------------------------------------- +const RegMask *MachOper::in_RegMask(int index) const { + ShouldNotReachHere(); + return NULL; +} + +//------------------------------dump_spec-------------------------------------- +// Print any per-operand special info +#ifndef PRODUCT +void MachOper::dump_spec(outputStream *st) const { } +#endif + +//------------------------------hash------------------------------------------- +// Print any per-operand special info +uint MachOper::hash() const { + ShouldNotCallThis(); + return 5; +} + +//------------------------------cmp-------------------------------------------- +// Print any per-operand special info +uint MachOper::cmp( const MachOper &oper ) const { + ShouldNotCallThis(); + return opcode() == oper.opcode(); +} + +//------------------------------hash------------------------------------------- +// Print any per-operand special info +uint labelOper::hash() const { + return _block_num; +} + +//------------------------------cmp-------------------------------------------- +// Print any per-operand special info +uint labelOper::cmp( const MachOper &oper ) const { + return (opcode() == oper.opcode()) && (_label == oper.label()); +} + +//------------------------------hash------------------------------------------- +// Print any per-operand special info +uint methodOper::hash() const { + return (uint)_method; +} + +//------------------------------cmp-------------------------------------------- +// Print any per-operand special info +uint methodOper::cmp( const MachOper &oper ) const { + return (opcode() == oper.opcode()) && (_method == oper.method()); +} + + +//============================================================================= +//------------------------------MachNode--------------------------------------- + +//------------------------------emit------------------------------------------- +void MachNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + #ifdef ASSERT + tty->print("missing MachNode emit function: "); + dump(); + #endif + ShouldNotCallThis(); +} + +//------------------------------size------------------------------------------- +// Size of instruction in bytes +uint MachNode::size(PhaseRegAlloc *ra_) const { + // If a virtual was not defined for this specific instruction, + // Call the helper which finds the size by emiting the bits. + return MachNode::emit_size(ra_); +} + +//------------------------------size------------------------------------------- +// Helper function that computes size by emitting code +uint MachNode::emit_size(PhaseRegAlloc *ra_) const { + // Emit into a trash buffer and count bytes emitted. + assert(ra_ == ra_->C->regalloc(), "sanity"); + return ra_->C->scratch_emit_size(this); +} + + + +//------------------------------hash------------------------------------------- +uint MachNode::hash() const { + uint no = num_opnds(); + uint sum = rule(); + for( uint i=0; ihash(); + return sum+Node::hash(); +} + +//-----------------------------cmp--------------------------------------------- +uint MachNode::cmp( const Node &node ) const { + MachNode& n = *((Node&)node).as_Mach(); + uint no = num_opnds(); + if( no != n.num_opnds() ) return 0; + if( rule() != n.rule() ) return 0; + for( uint i=0; icmp( *n._opnds[i] ) ) + return 0; // mis-matched operands + return 1; // match +} + +// Return an equivalent instruction using memory for cisc_operand position +MachNode *MachNode::cisc_version(int offset, Compile* C) { + ShouldNotCallThis(); + return NULL; +} + +void MachNode::use_cisc_RegMask() { + ShouldNotReachHere(); +} + + +//-----------------------------in_RegMask-------------------------------------- +const RegMask &MachNode::in_RegMask( uint idx ) const { + uint numopnds = num_opnds(); // Virtual call for number of operands + uint skipped = oper_input_base(); // Sum of leaves skipped so far + if( idx < skipped ) { + assert( ideal_Opcode() == Op_AddP, "expected base ptr here" ); + assert( idx == 1, "expected base ptr here" ); + // debug info can be anywhere + return *Compile::current()->matcher()->idealreg2spillmask[Op_RegP]; + } + uint opcnt = 1; // First operand + uint num_edges = _opnds[1]->num_edges(); // leaves for first operand + while( idx >= skipped+num_edges ) { + skipped += num_edges; + opcnt++; // Bump operand count + assert( opcnt < numopnds, "Accessing non-existent operand" ); + num_edges = _opnds[opcnt]->num_edges(); // leaves for next operand + } + + const RegMask *rm = cisc_RegMask(); + if( rm == NULL || (int)opcnt != cisc_operand() ) { + rm = _opnds[opcnt]->in_RegMask(idx-skipped); + } + return *rm; +} + +//-----------------------------memory_inputs-------------------------------- +const MachOper* MachNode::memory_inputs(Node* &base, Node* &index) const { + const MachOper* oper = memory_operand(); + + if (oper == (MachOper*)-1) { + base = NodeSentinel; + index = NodeSentinel; + } else { + base = NULL; + index = NULL; + if (oper != NULL) { + // It has a unique memory operand. Find its index. + int oper_idx = num_opnds(); + while (--oper_idx >= 0) { + if (_opnds[oper_idx] == oper) break; + } + int oper_pos = operand_index(oper_idx); + int base_pos = oper->base_position(); + if (base_pos >= 0) { + base = _in[oper_pos+base_pos]; + } + int index_pos = oper->index_position(); + if (index_pos >= 0) { + index = _in[oper_pos+index_pos]; + } + } + } + + return oper; +} + +//-----------------------------get_base_and_disp---------------------------- +const Node* MachNode::get_base_and_disp(intptr_t &offset, const TypePtr* &adr_type) const { + + // Find the memory inputs using our helper function + Node* base; + Node* index; + const MachOper* oper = memory_inputs(base, index); + + if (oper == NULL) { + // Base has been set to NULL + offset = 0; + } else if (oper == (MachOper*)-1) { + // Base has been set to NodeSentinel + // There is not a unique memory use here. We will fall to AliasIdxBot. + offset = Type::OffsetBot; + } else { + // Base may be NULL, even if offset turns out to be != 0 + + intptr_t disp = oper->constant_disp(); + int scale = oper->scale(); + // Now we have collected every part of the ADLC MEMORY_INTER. + // See if it adds up to a base + offset. + if (index != NULL) { + if (!index->is_Con()) { + disp = Type::OffsetBot; + } else if (disp != Type::OffsetBot) { + const TypeX* ti = index->bottom_type()->isa_intptr_t(); + if (ti == NULL) { + disp = Type::OffsetBot; // a random constant?? + } else { + disp += ti->get_con() << scale; + } + } + } + offset = disp; + + // In i486.ad, indOffset32X uses base==RegI and disp==RegP, + // this will prevent alias analysis without the following support: + // Lookup the TypePtr used by indOffset32X, a compile-time constant oop, + // Add the offset determined by the "base", or use Type::OffsetBot. + if( adr_type == TYPE_PTR_SENTINAL ) { + const TypePtr *t_disp = oper->disp_as_type(); // only !NULL for indOffset32X + if (t_disp != NULL) { + offset = Type::OffsetBot; + const Type* t_base = base->bottom_type(); + if (t_base->isa_intptr_t()) { + const TypeX *t_offset = t_base->is_intptr_t(); + if( t_offset->is_con() ) { + offset = t_offset->get_con(); + } + } + adr_type = t_disp->add_offset(offset); + } + } + + } + return base; +} + + +//---------------------------------adr_type--------------------------------- +const class TypePtr *MachNode::adr_type() const { + intptr_t offset = 0; + const TypePtr *adr_type = TYPE_PTR_SENTINAL; // attempt computing adr_type + const Node *base = get_base_and_disp(offset, adr_type); + if( adr_type != TYPE_PTR_SENTINAL ) { + return adr_type; // get_base_and_disp has the answer + } + + // Direct addressing modes have no base node, simply an indirect + // offset, which is always to raw memory. + // %%%%% Someday we'd like to allow constant oop offsets which + // would let Intel load from static globals in 1 instruction. + // Currently Intel requires 2 instructions and a register temp. + if (base == NULL) { + // NULL base, zero offset means no memory at all (a null pointer!) + if (offset == 0) { + return NULL; + } + // NULL base, any offset means any pointer whatever + if (offset == Type::OffsetBot) { + return TypePtr::BOTTOM; + } + // %%% make offset be intptr_t + assert(!Universe::heap()->is_in_reserved((oop)offset), "must be a raw ptr"); + return TypeRawPtr::BOTTOM; + } + + // base of -1 with no particular offset means all of memory + if (base == NodeSentinel) return TypePtr::BOTTOM; + + const Type* t = base->bottom_type(); + if (t->isa_intptr_t() && offset != 0 && offset != Type::OffsetBot) { + // We cannot assert that the offset does not look oop-ish here. + // Depending on the heap layout the cardmark base could land + // inside some oopish region. It definitely does for Win2K. + // The sum of cardmark-base plus shift-by-9-oop lands outside + // the oop-ish area but we can't assert for that statically. + return TypeRawPtr::BOTTOM; + } + + const TypePtr *tp = t->isa_ptr(); + + // be conservative if we do not recognize the type + if (tp == NULL) { + return TypePtr::BOTTOM; + } + assert(tp->base() != Type::AnyPtr, "not a bare pointer"); + + return tp->add_offset(offset); +} + + +//-----------------------------operand_index--------------------------------- +int MachNode::operand_index( uint operand ) const { + if( operand < 1 ) return -1; + assert(operand < num_opnds(), "oob"); + if( _opnds[operand]->num_edges() == 0 ) return -1; + + uint skipped = oper_input_base(); // Sum of leaves skipped so far + for (uint opcnt = 1; opcnt < operand; opcnt++) { + uint num_edges = _opnds[opcnt]->num_edges(); // leaves for operand + skipped += num_edges; + } + return skipped; +} + + +//------------------------------negate----------------------------------------- +// Negate conditional branches. Error for non-branch Nodes +void MachNode::negate() { + ShouldNotCallThis(); +} + +//------------------------------peephole--------------------------------------- +// Apply peephole rule(s) to this instruction +MachNode *MachNode::peephole( Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted, Compile* C ) { + return NULL; +} + +//------------------------------add_case_label--------------------------------- +// Adds the label for the case +void MachNode::add_case_label( int index_num, Label* blockLabel) { + ShouldNotCallThis(); +} + +//------------------------------label_set-------------------------------------- +// Set the Label for a LabelOper, if an operand for this instruction +void MachNode::label_set( Label& label, uint block_num ) { + ShouldNotCallThis(); +} + +//------------------------------method_set------------------------------------- +// Set the absolute address of a method +void MachNode::method_set( intptr_t addr ) { + ShouldNotCallThis(); +} + +//------------------------------rematerialize---------------------------------- +bool MachNode::rematerialize() const { + // Temps are always rematerializable + if (is_MachTemp()) return true; + + uint r = rule(); // Match rule + if( r < Matcher::_begin_rematerialize || + r >= Matcher::_end_rematerialize ) + return false; + + // For 2-address instructions, the input live range is also the output + // live range. Remateralizing does not make progress on the that live range. + if( two_adr() ) return false; + + // Check for rematerializing float constants, or not + if( !Matcher::rematerialize_float_constants ) { + int op = ideal_Opcode(); + if( op == Op_ConF || op == Op_ConD ) + return false; + } + + // Defining flags - can't spill these! Must remateralize. + if( ideal_reg() == Op_RegFlags ) + return true; + + // Stretching lots of inputs - don't do it. + if( req() > 2 ) + return false; + + // Don't remateralize somebody with bound inputs - it stretches a + // fixed register lifetime. + uint idx = oper_input_base(); + if( req() > idx ) { + const RegMask &rm = in_RegMask(idx); + if( rm.is_bound1() || rm.is_bound2() ) + return false; + } + + return true; +} + +#ifndef PRODUCT +//------------------------------dump_spec-------------------------------------- +// Print any per-operand special info +void MachNode::dump_spec(outputStream *st) const { + uint cnt = num_opnds(); + for( uint i=0; idump_spec(st); + const TypePtr *t = adr_type(); + if( t ) { + Compile* C = Compile::current(); + if( C->alias_type(t)->is_volatile() ) + st->print(" Volatile!"); + } +} + +//------------------------------dump_format------------------------------------ +// access to virtual +void MachNode::dump_format(PhaseRegAlloc *ra, outputStream *st) const { + format(ra, st); // access to virtual +} +#endif + +//============================================================================= +#ifndef PRODUCT +void MachTypeNode::dump_spec(outputStream *st) const { + _bottom_type->dump_on(st); +} +#endif + +//============================================================================= +#ifndef PRODUCT +void MachNullCheckNode::format( PhaseRegAlloc *ra_, outputStream *st ) const { + int reg = ra_->get_reg_first(in(1)->in(_vidx)); + tty->print("%s %s", Name(), Matcher::regName[reg]); +} +#endif + +void MachNullCheckNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + // only emits entries in the null-pointer exception handler table +} + +const RegMask &MachNullCheckNode::in_RegMask( uint idx ) const { + if( idx == 0 ) return RegMask::Empty; + else return in(1)->as_Mach()->out_RegMask(); +} + +//============================================================================= +const Type *MachProjNode::bottom_type() const { + if( _ideal_reg == fat_proj ) return Type::BOTTOM; + // Try the normal mechanism first + const Type *t = in(0)->bottom_type(); + if( t->base() == Type::Tuple ) { + const TypeTuple *tt = t->is_tuple(); + if (_con < tt->cnt()) + return tt->field_at(_con); + } + // Else use generic type from ideal register set + assert((uint)_ideal_reg < (uint)_last_machine_leaf && Type::mreg2type[_ideal_reg], "in bounds"); + return Type::mreg2type[_ideal_reg]; +} + +const TypePtr *MachProjNode::adr_type() const { + if (bottom_type() == Type::MEMORY) { + // in(0) might be a narrow MemBar; otherwise we will report TypePtr::BOTTOM + const TypePtr* adr_type = in(0)->adr_type(); + #ifdef ASSERT + if (!is_error_reported() && !Node::in_dump()) + assert(adr_type != NULL, "source must have adr_type"); + #endif + return adr_type; + } + assert(bottom_type()->base() != Type::Memory, "no other memories?"); + return NULL; +} + +#ifndef PRODUCT +void MachProjNode::dump_spec(outputStream *st) const { + ProjNode::dump_spec(st); + switch (_ideal_reg) { + case unmatched_proj: st->print("/unmatched"); break; + case fat_proj: st->print("/fat"); if (WizardMode) _rout.dump(); break; + } +} +#endif + +//============================================================================= +#ifndef PRODUCT +void MachIfNode::dump_spec(outputStream *st) const { + st->print("P=%f, C=%f",_prob, _fcnt); +} +#endif + +//============================================================================= +uint MachReturnNode::size_of() const { return sizeof(*this); } + +//------------------------------Registers-------------------------------------- +const RegMask &MachReturnNode::in_RegMask( uint idx ) const { + return _in_rms[idx]; +} + +const TypePtr *MachReturnNode::adr_type() const { + // most returns and calls are assumed to consume & modify all of memory + // the matcher will copy non-wide adr_types from ideal originals + return _adr_type; +} + +//============================================================================= +const Type *MachSafePointNode::bottom_type() const { return TypeTuple::MEMBAR; } + +//------------------------------Registers-------------------------------------- +const RegMask &MachSafePointNode::in_RegMask( uint idx ) const { + // Values in the domain use the users calling convention, embodied in the + // _in_rms array of RegMasks. + if( idx < TypeFunc::Parms ) return _in_rms[idx]; + + if (SafePointNode::needs_polling_address_input() && + idx == TypeFunc::Parms && + ideal_Opcode() == Op_SafePoint) { + return MachNode::in_RegMask(idx); + } + + // Values outside the domain represent debug info + return *Compile::current()->matcher()->idealreg2spillmask[in(idx)->ideal_reg()]; +} + + +//============================================================================= + +uint MachCallNode::cmp( const Node &n ) const +{ return _tf == ((MachCallNode&)n)._tf; } +const Type *MachCallNode::bottom_type() const { return tf()->range(); } +const Type *MachCallNode::Value(PhaseTransform *phase) const { return tf()->range(); } + +#ifndef PRODUCT +void MachCallNode::dump_spec(outputStream *st) const { + st->print("# "); + tf()->dump_on(st); + if (_cnt != COUNT_UNKNOWN) st->print(" C=%f",_cnt); + if (jvms() != NULL) jvms()->dump_spec(st); +} +#endif + + +bool MachCallNode::return_value_is_used() const { + if (tf()->range()->cnt() == TypeFunc::Parms) { + // void return + return false; + } + + // find the projection corresponding to the return value + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { + Node *use = fast_out(i); + if (!use->is_Proj()) continue; + if (use->as_Proj()->_con == TypeFunc::Parms) { + return true; + } + } + return false; +} + + +//------------------------------Registers-------------------------------------- +const RegMask &MachCallNode::in_RegMask( uint idx ) const { + // Values in the domain use the users calling convention, embodied in the + // _in_rms array of RegMasks. + if (idx < tf()->domain()->cnt()) return _in_rms[idx]; + // Values outside the domain represent debug info + return *Compile::current()->matcher()->idealreg2debugmask[in(idx)->ideal_reg()]; +} + +//============================================================================= +uint MachCallJavaNode::size_of() const { return sizeof(*this); } +uint MachCallJavaNode::cmp( const Node &n ) const { + MachCallJavaNode &call = (MachCallJavaNode&)n; + return MachCallNode::cmp(call) && _method->equals(call._method); +} +#ifndef PRODUCT +void MachCallJavaNode::dump_spec(outputStream *st) const { + if( _method ) { + _method->print_short_name(st); + st->print(" "); + } + MachCallNode::dump_spec(st); +} +#endif + +//============================================================================= +uint MachCallStaticJavaNode::size_of() const { return sizeof(*this); } +uint MachCallStaticJavaNode::cmp( const Node &n ) const { + MachCallStaticJavaNode &call = (MachCallStaticJavaNode&)n; + return MachCallJavaNode::cmp(call) && _name == call._name; +} + +//----------------------------uncommon_trap_request---------------------------- +// If this is an uncommon trap, return the request code, else zero. +int MachCallStaticJavaNode::uncommon_trap_request() const { + if (_name != NULL && !strcmp(_name, "uncommon_trap")) { + return CallStaticJavaNode::extract_uncommon_trap_request(this); + } + return 0; +} + +#ifndef PRODUCT +// Helper for summarizing uncommon_trap arguments. +void MachCallStaticJavaNode::dump_trap_args(outputStream *st) const { + int trap_req = uncommon_trap_request(); + if (trap_req != 0) { + char buf[100]; + st->print("(%s)", + Deoptimization::format_trap_request(buf, sizeof(buf), + trap_req)); + } +} + +void MachCallStaticJavaNode::dump_spec(outputStream *st) const { + st->print("Static "); + if (_name != NULL) { + st->print("wrapper for: %s", _name ); + dump_trap_args(st); + st->print(" "); + } + MachCallJavaNode::dump_spec(st); +} +#endif + +//============================================================================= +#ifndef PRODUCT +void MachCallDynamicJavaNode::dump_spec(outputStream *st) const { + st->print("Dynamic "); + MachCallJavaNode::dump_spec(st); +} +#endif +//============================================================================= +uint MachCallRuntimeNode::size_of() const { return sizeof(*this); } +uint MachCallRuntimeNode::cmp( const Node &n ) const { + MachCallRuntimeNode &call = (MachCallRuntimeNode&)n; + return MachCallNode::cmp(call) && !strcmp(_name,call._name); +} +#ifndef PRODUCT +void MachCallRuntimeNode::dump_spec(outputStream *st) const { + st->print("%s ",_name); + MachCallNode::dump_spec(st); +} +#endif +//============================================================================= +// A shared JVMState for all HaltNodes. Indicates the start of debug info +// is at TypeFunc::Parms. Only required for SOE register spill handling - +// to indicate where the stack-slot-only debug info inputs begin. +// There is no other JVM state needed here. +JVMState jvms_for_throw(0); +JVMState *MachHaltNode::jvms() const { + return &jvms_for_throw; +} + +//============================================================================= +#ifndef PRODUCT +void labelOper::int_format(PhaseRegAlloc *ra, const MachNode *node, outputStream *st) const { + st->print("B%d", _block_num); +} +#endif // PRODUCT + +//============================================================================= +#ifndef PRODUCT +void methodOper::int_format(PhaseRegAlloc *ra, const MachNode *node, outputStream *st) const { + st->print(INTPTR_FORMAT, _method); +} +#endif // PRODUCT diff --git a/hotspot/src/share/vm/opto/machnode.hpp b/hotspot/src/share/vm/opto/machnode.hpp new file mode 100644 index 00000000000..3c24a3e5c65 --- /dev/null +++ b/hotspot/src/share/vm/opto/machnode.hpp @@ -0,0 +1,826 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class BufferBlob; +class CodeBuffer; +class JVMState; +class MachCallDynamicJavaNode; +class MachCallJavaNode; +class MachCallLeafNode; +class MachCallNode; +class MachCallRuntimeNode; +class MachCallStaticJavaNode; +class MachEpilogNode; +class MachIfNode; +class MachNullCheckNode; +class MachOper; +class MachProjNode; +class MachPrologNode; +class MachReturnNode; +class MachSafePointNode; +class MachSpillCopyNode; +class Matcher; +class PhaseRegAlloc; +class RegMask; +class State; + +//---------------------------MachOper------------------------------------------ +class MachOper : public ResourceObj { +public: + // Allocate right next to the MachNodes in the same arena + void *operator new( size_t x, Compile* C ) { return C->node_arena()->Amalloc_D(x); } + + // Opcode + virtual uint opcode() const = 0; + + // Number of input edges. + // Generally at least 1 + virtual uint num_edges() const { return 1; } + // Array of Register masks + virtual const RegMask *in_RegMask(int index) const; + + // Methods to output the encoding of the operand + + // Negate conditional branches. Error for non-branch Nodes + virtual void negate(); + + // Return the value requested + // result register lookup, corresponding to int_format + virtual int reg(PhaseRegAlloc *ra_, const Node *node) const; + // input register lookup, corresponding to ext_format + virtual int reg(PhaseRegAlloc *ra_, const Node *node, int idx) const; + + // helpers for MacroAssembler generation from ADLC + Register as_Register(PhaseRegAlloc *ra_, const Node *node) const { + return ::as_Register(reg(ra_, node)); + } + Register as_Register(PhaseRegAlloc *ra_, const Node *node, int idx) const { + return ::as_Register(reg(ra_, node, idx)); + } + FloatRegister as_FloatRegister(PhaseRegAlloc *ra_, const Node *node) const { + return ::as_FloatRegister(reg(ra_, node)); + } + FloatRegister as_FloatRegister(PhaseRegAlloc *ra_, const Node *node, int idx) const { + return ::as_FloatRegister(reg(ra_, node, idx)); + } + +#if defined(IA32) || defined(AMD64) + XMMRegister as_XMMRegister(PhaseRegAlloc *ra_, const Node *node) const { + return ::as_XMMRegister(reg(ra_, node)); + } + XMMRegister as_XMMRegister(PhaseRegAlloc *ra_, const Node *node, int idx) const { + return ::as_XMMRegister(reg(ra_, node, idx)); + } +#endif + + virtual intptr_t constant() const; + virtual bool constant_is_oop() const; + virtual jdouble constantD() const; + virtual jfloat constantF() const; + virtual jlong constantL() const; + virtual TypeOopPtr *oop() const; + virtual int ccode() const; + // A zero, default, indicates this value is not needed. + // May need to lookup the base register, as done in int_ and ext_format + virtual int base (PhaseRegAlloc *ra_, const Node *node, int idx) const; + virtual int index(PhaseRegAlloc *ra_, const Node *node, int idx) const; + virtual int scale() const; + // Parameters needed to support MEMORY_INTERFACE access to stackSlot + virtual int disp (PhaseRegAlloc *ra_, const Node *node, int idx) const; + // Check for PC-Relative displacement + virtual bool disp_is_oop() const; + virtual int constant_disp() const; // usu. 0, may return Type::OffsetBot + virtual int base_position() const; // base edge position, or -1 + virtual int index_position() const; // index edge position, or -1 + + // Access the TypeKlassPtr of operands with a base==RegI and disp==RegP + // Only returns non-null value for i486.ad's indOffset32X + virtual const TypePtr *disp_as_type() const { return NULL; } + + // Return the label + virtual Label *label() const; + + // Return the method's address + virtual intptr_t method() const; + + // Hash and compare over operands are currently identical + virtual uint hash() const; + virtual uint cmp( const MachOper &oper ) const; + + // Virtual clone, since I do not know how big the MachOper is. + virtual MachOper *clone(Compile* C) const = 0; + + // Return ideal Type from simple operands. Fail for complex operands. + virtual const Type *type() const; + + // Set an integer offset if we have one, or error otherwise + virtual void set_con( jint c0 ) { ShouldNotReachHere(); } + +#ifndef PRODUCT + // Return name of operand + virtual const char *Name() const { return "???";} + + // Methods to output the text version of the operand + virtual void int_format(PhaseRegAlloc *,const MachNode *node, outputStream *st) const = 0; + virtual void ext_format(PhaseRegAlloc *,const MachNode *node,int idx, outputStream *st) const=0; + + virtual void dump_spec(outputStream *st) const; // Print per-operand info +#endif +}; + +//------------------------------MachNode--------------------------------------- +// Base type for all machine specific nodes. All node classes generated by the +// ADLC inherit from this class. +class MachNode : public Node { +public: + MachNode() : Node((uint)0), _num_opnds(0), _opnds(NULL) { + init_class_id(Class_Mach); + } + // Required boilerplate + virtual uint size_of() const { return sizeof(MachNode); } + virtual int Opcode() const; // Always equal to MachNode + virtual uint rule() const = 0; // Machine-specific opcode + // Number of inputs which come before the first operand. + // Generally at least 1, to skip the Control input + virtual uint oper_input_base() const { return 1; } + + // Copy inputs and operands to new node of instruction. + // Called from cisc_version() and short_branch_version(). + // !!!! The method's body is defined in ad_.cpp file. + void fill_new_machnode(MachNode *n, Compile* C) const; + + // Return an equivalent instruction using memory for cisc_operand position + virtual MachNode *cisc_version(int offset, Compile* C); + // Modify this instruction's register mask to use stack version for cisc_operand + virtual void use_cisc_RegMask(); + + // Support for short branches + virtual MachNode *short_branch_version(Compile* C) { return NULL; } + bool may_be_short_branch() const { return (flags() & Flag_may_be_short_branch) != 0; } + + // First index in _in[] corresponding to operand, or -1 if there is none + int operand_index(uint operand) const; + + // Register class input is expected in + virtual const RegMask &in_RegMask(uint) const; + + // cisc-spillable instructions redefine for use by in_RegMask + virtual const RegMask *cisc_RegMask() const { return NULL; } + + // If this instruction is a 2-address instruction, then return the + // index of the input which must match the output. Not nessecary + // for instructions which bind the input and output register to the + // same singleton regiser (e.g., Intel IDIV which binds AX to be + // both an input and an output). It is nessecary when the input and + // output have choices - but they must use the same choice. + virtual uint two_adr( ) const { return 0; } + + // Array of complex operand pointers. Each corresponds to zero or + // more leafs. Must be set by MachNode constructor to point to an + // internal array of MachOpers. The MachOper array is sized by + // specific MachNodes described in the ADL. + uint _num_opnds; + MachOper **_opnds; + uint num_opnds() const { return _num_opnds; } + + // Emit bytes into cbuf + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + // Size of instruction in bytes + virtual uint size(PhaseRegAlloc *ra_) const; + // Helper function that computes size by emitting code + virtual uint emit_size(PhaseRegAlloc *ra_) const; + + // Return the alignment required (in units of relocInfo::addr_unit()) + // for this instruction (must be a power of 2) + virtual int alignment_required() const { return 1; } + + // Return the padding (in bytes) to be emitted before this + // instruction to properly align it. + virtual int compute_padding(int current_offset) const { return 0; } + + // Return number of relocatable values contained in this instruction + virtual int reloc() const { return 0; } + + // Return number of words used for double constants in this instruction + virtual int const_size() const { return 0; } + + // Hash and compare over operands. Used to do GVN on machine Nodes. + virtual uint hash() const; + virtual uint cmp( const Node &n ) const; + + // Expand method for MachNode, replaces nodes representing pseudo + // instructions with a set of nodes which represent real machine + // instructions and compute the same value. + virtual MachNode *Expand( State *, Node_List &proj_list ) { return this; } + + // Bottom_type call; value comes from operand0 + virtual const class Type *bottom_type() const { return _opnds[0]->type(); } + virtual uint ideal_reg() const { const Type *t = _opnds[0]->type(); return t == TypeInt::CC ? Op_RegFlags : Matcher::base2reg[t->base()]; } + + // If this is a memory op, return the base pointer and fixed offset. + // If there are no such, return NULL. If there are multiple addresses + // or the address is indeterminate (rare cases) then return (Node*)-1, + // which serves as node bottom. + // If the offset is not statically determined, set it to Type::OffsetBot. + // This method is free to ignore stack slots if that helps. + #define TYPE_PTR_SENTINAL ((const TypePtr*)-1) + // Passing TYPE_PTR_SENTINAL as adr_type asks for computation of the adr_type if possible + const Node* get_base_and_disp(intptr_t &offset, const TypePtr* &adr_type) const; + + // Helper for get_base_and_disp: find the base and index input nodes. + // Returns the MachOper as determined by memory_operand(), for use, if + // needed by the caller. If (MachOper *)-1 is returned, base and index + // are set to NodeSentinel. If (MachOper *) NULL is returned, base and + // index are set to NULL. + const MachOper* memory_inputs(Node* &base, Node* &index) const; + + // Helper for memory_inputs: Which operand carries the necessary info? + // By default, returns NULL, which means there is no such operand. + // If it returns (MachOper*)-1, this means there are multiple memories. + virtual const MachOper* memory_operand() const { return NULL; } + + // Call "get_base_and_disp" to decide which category of memory is used here. + virtual const class TypePtr *adr_type() const; + + // Negate conditional branches. Error for non-branch Nodes + virtual void negate(); + + // Apply peephole rule(s) to this instruction + virtual MachNode *peephole( Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted, Compile* C ); + + // Check for PC-Relative addressing + bool is_pc_relative() const { return (flags() & Flag_is_pc_relative) != 0; } + + // Top-level ideal Opcode matched + virtual int ideal_Opcode() const { return Op_Node; } + + // Set the branch inside jump MachNodes. Error for non-branch Nodes. + virtual void label_set( Label& label, uint block_num ); + + // Adds the label for the case + virtual void add_case_label( int switch_val, Label* blockLabel); + + // Set the absolute address for methods + virtual void method_set( intptr_t addr ); + + // Should we clone rather than spill this instruction? + bool rematerialize() const; + + // Get the pipeline info + static const Pipeline *pipeline_class(); + virtual const Pipeline *pipeline() const; + +#ifndef PRODUCT + virtual const char *Name() const = 0; // Machine-specific name + virtual void dump_spec(outputStream *st) const; // Print per-node info + void dump_format(PhaseRegAlloc *ra, outputStream *st) const; // access to virtual +#endif +}; + +//------------------------------MachIdealNode---------------------------- +// Machine specific versions of nodes that must be defined by user. +// These are not converted by matcher from ideal nodes to machine nodes +// but are inserted into the code by the compiler. +class MachIdealNode : public MachNode { +public: + MachIdealNode( ) {} + + // Define the following defaults for non-matched machine nodes + virtual uint oper_input_base() const { return 0; } + virtual uint rule() const { return 9999999; } + virtual const class Type *bottom_type() const { return _opnds == NULL ? Type::CONTROL : MachNode::bottom_type(); } +}; + +//------------------------------MachTypeNode---------------------------- +// Machine Nodes that need to retain a known Type. +class MachTypeNode : public MachNode { + virtual uint size_of() const { return sizeof(*this); } // Size is bigger +public: + const Type *_bottom_type; + + virtual const class Type *bottom_type() const { return _bottom_type; } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------MachBreakpointNode---------------------------- +// Machine breakpoint or interrupt Node +class MachBreakpointNode : public MachIdealNode { +public: + MachBreakpointNode( ) {} + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + virtual uint size(PhaseRegAlloc *ra_) const; + +#ifndef PRODUCT + virtual const char *Name() const { return "Breakpoint"; } + virtual void format( PhaseRegAlloc *, outputStream *st ) const; +#endif +}; + +//------------------------------MachUEPNode----------------------------------- +// Machine Unvalidated Entry Point Node +class MachUEPNode : public MachIdealNode { +public: + MachUEPNode( ) {} + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + virtual uint size(PhaseRegAlloc *ra_) const; + +#ifndef PRODUCT + virtual const char *Name() const { return "Unvalidated-Entry-Point"; } + virtual void format( PhaseRegAlloc *, outputStream *st ) const; +#endif +}; + +//------------------------------MachPrologNode-------------------------------- +// Machine function Prolog Node +class MachPrologNode : public MachIdealNode { +public: + MachPrologNode( ) {} + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + virtual uint size(PhaseRegAlloc *ra_) const; + virtual int reloc() const; + +#ifndef PRODUCT + virtual const char *Name() const { return "Prolog"; } + virtual void format( PhaseRegAlloc *, outputStream *st ) const; +#endif +}; + +//------------------------------MachEpilogNode-------------------------------- +// Machine function Epilog Node +class MachEpilogNode : public MachIdealNode { +public: + MachEpilogNode(bool do_poll = false) : _do_polling(do_poll) {} + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + virtual uint size(PhaseRegAlloc *ra_) const; + virtual int reloc() const; + virtual const Pipeline *pipeline() const; + +private: + bool _do_polling; + +public: + bool do_polling() const { return _do_polling; } + + // Offset of safepoint from the beginning of the node + int safepoint_offset() const; + +#ifndef PRODUCT + virtual const char *Name() const { return "Epilog"; } + virtual void format( PhaseRegAlloc *, outputStream *st ) const; +#endif +}; + +//------------------------------MachNopNode----------------------------------- +// Machine function Nop Node +class MachNopNode : public MachIdealNode { +private: + int _count; +public: + MachNopNode( ) : _count(1) {} + MachNopNode( int count ) : _count(count) {} + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + virtual uint size(PhaseRegAlloc *ra_) const; + + virtual const class Type *bottom_type() const { return Type::CONTROL; } + + virtual int ideal_Opcode() const { return Op_Con; } // bogus; see output.cpp + virtual const Pipeline *pipeline() const; +#ifndef PRODUCT + virtual const char *Name() const { return "Nop"; } + virtual void format( PhaseRegAlloc *, outputStream *st ) const; + virtual void dump_spec(outputStream *st) const { } // No per-operand info +#endif +}; + +//------------------------------MachSpillCopyNode------------------------------ +// Machine SpillCopy Node. Copies 1 or 2 words from any location to any +// location (stack or register). +class MachSpillCopyNode : public MachIdealNode { + const RegMask *_in; // RegMask for input + const RegMask *_out; // RegMask for output + const Type *_type; +public: + MachSpillCopyNode( Node *n, const RegMask &in, const RegMask &out ) : + MachIdealNode(), _in(&in), _out(&out), _type(n->bottom_type()) { + init_class_id(Class_MachSpillCopy); + init_flags(Flag_is_Copy); + add_req(NULL); + add_req(n); + } + virtual uint size_of() const { return sizeof(*this); } + void set_out_RegMask(const RegMask &out) { _out = &out; } + void set_in_RegMask(const RegMask &in) { _in = ∈ } + virtual const RegMask &out_RegMask() const { return *_out; } + virtual const RegMask &in_RegMask(uint) const { return *_in; } + virtual const class Type *bottom_type() const { return _type; } + virtual uint ideal_reg() const { return Matcher::base2reg[_type->base()]; } + virtual uint oper_input_base() const { return 1; } + uint implementation( CodeBuffer *cbuf, PhaseRegAlloc *ra_, bool do_size, outputStream* st ) const; + + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + virtual uint size(PhaseRegAlloc *ra_) const; + +#ifndef PRODUCT + virtual const char *Name() const { return "MachSpillCopy"; } + virtual void format( PhaseRegAlloc *, outputStream *st ) const; +#endif +}; + +//------------------------------MachNullChkNode-------------------------------- +// Machine-dependent null-pointer-check Node. Points a real MachNode that is +// also some kind of memory op. Turns the indicated MachNode into a +// conditional branch with good latency on the ptr-not-null path and awful +// latency on the pointer-is-null path. + +class MachNullCheckNode : public MachIdealNode { +public: + const uint _vidx; // Index of memop being tested + MachNullCheckNode( Node *ctrl, Node *memop, uint vidx ) : MachIdealNode(), _vidx(vidx) { + init_class_id(Class_MachNullCheck); + init_flags(Flag_is_Branch | Flag_is_pc_relative); + add_req(ctrl); + add_req(memop); + } + + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + virtual bool pinned() const { return true; }; + virtual void negate() { } + virtual const class Type *bottom_type() const { return TypeTuple::IFBOTH; } + virtual uint ideal_reg() const { return NotAMachineReg; } + virtual const RegMask &in_RegMask(uint) const; + virtual const RegMask &out_RegMask() const { return RegMask::Empty; } +#ifndef PRODUCT + virtual const char *Name() const { return "NullCheck"; } + virtual void format( PhaseRegAlloc *, outputStream *st ) const; +#endif +}; + +//------------------------------MachProjNode---------------------------------- +// Machine-dependent Ideal projections (how is that for an oxymoron). Really +// just MachNodes made by the Ideal world that replicate simple projections +// but with machine-dependent input & output register masks. Generally +// produced as part of calling conventions. Normally I make MachNodes as part +// of the Matcher process, but the Matcher is ill suited to issues involving +// frame handling, so frame handling is all done in the Ideal world with +// occasional callbacks to the machine model for important info. +class MachProjNode : public ProjNode { +public: + MachProjNode( Node *multi, uint con, const RegMask &out, uint ideal_reg ) : ProjNode(multi,con), _rout(out), _ideal_reg(ideal_reg) {} + RegMask _rout; + const uint _ideal_reg; + enum projType { + unmatched_proj = 0, // Projs for Control, I/O, memory not matched + fat_proj = 999 // Projs killing many regs, defined by _rout + }; + virtual int Opcode() const; + virtual const Type *bottom_type() const; + virtual const TypePtr *adr_type() const; + virtual const RegMask &in_RegMask(uint) const { return RegMask::Empty; } + virtual const RegMask &out_RegMask() const { return _rout; } + virtual uint ideal_reg() const { return _ideal_reg; } + // Need size_of() for virtual ProjNode::clone() + virtual uint size_of() const { return sizeof(MachProjNode); } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------MachIfNode------------------------------------- +// Machine-specific versions of IfNodes +class MachIfNode : public MachNode { + virtual uint size_of() const { return sizeof(*this); } // Size is bigger +public: + float _prob; // Probability branch goes either way + float _fcnt; // Frequency counter + MachIfNode() : MachNode() { + init_class_id(Class_MachIf); + } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------MachFastLockNode------------------------------------- +// Machine-specific versions of FastLockNodes +class MachFastLockNode : public MachNode { + virtual uint size_of() const { return sizeof(*this); } // Size is bigger +public: + BiasedLockingCounters* _counters; + + MachFastLockNode() : MachNode() {} +}; + +//------------------------------MachReturnNode-------------------------------- +// Machine-specific versions of subroutine returns +class MachReturnNode : public MachNode { + virtual uint size_of() const; // Size is bigger +public: + RegMask *_in_rms; // Input register masks, set during allocation + ReallocMark _nesting; // assertion check for reallocations + const TypePtr* _adr_type; // memory effects of call or return + MachReturnNode() : MachNode() { + init_class_id(Class_MachReturn); + _adr_type = TypePtr::BOTTOM; // the default: all of memory + } + + void set_adr_type(const TypePtr* atp) { _adr_type = atp; } + + virtual const RegMask &in_RegMask(uint) const; + virtual bool pinned() const { return true; }; + virtual const TypePtr *adr_type() const; +}; + +//------------------------------MachSafePointNode----------------------------- +// Machine-specific versions of safepoints +class MachSafePointNode : public MachReturnNode { +public: + OopMap* _oop_map; // Array of OopMap info (8-bit char) for GC + JVMState* _jvms; // Pointer to list of JVM State Objects + uint _jvmadj; // Extra delta to jvms indexes (mach. args) + OopMap* oop_map() const { return _oop_map; } + void set_oop_map(OopMap* om) { _oop_map = om; } + + MachSafePointNode() : MachReturnNode(), _oop_map(NULL), _jvms(NULL), _jvmadj(0) { + init_class_id(Class_MachSafePoint); + init_flags(Flag_is_safepoint_node); + } + + virtual JVMState* jvms() const { return _jvms; } + void set_jvms(JVMState* s) { + _jvms = s; + } + bool is_safepoint_node() const { return (flags() & Flag_is_safepoint_node) != 0; } + virtual const Type *bottom_type() const; + + virtual const RegMask &in_RegMask(uint) const; + + // Functionality from old debug nodes + Node *returnadr() const { return in(TypeFunc::ReturnAdr); } + Node *frameptr () const { return in(TypeFunc::FramePtr); } + + Node *local(const JVMState* jvms, uint idx) const { + assert(verify_jvms(jvms), "jvms must match"); + return in(_jvmadj + jvms->locoff() + idx); + } + Node *stack(const JVMState* jvms, uint idx) const { + assert(verify_jvms(jvms), "jvms must match"); + return in(_jvmadj + jvms->stkoff() + idx); + } + Node *monitor_obj(const JVMState* jvms, uint idx) const { + assert(verify_jvms(jvms), "jvms must match"); + return in(_jvmadj + jvms->monitor_obj_offset(idx)); + } + Node *monitor_box(const JVMState* jvms, uint idx) const { + assert(verify_jvms(jvms), "jvms must match"); + return in(_jvmadj + jvms->monitor_box_offset(idx)); + } + void set_local(const JVMState* jvms, uint idx, Node *c) { + assert(verify_jvms(jvms), "jvms must match"); + set_req(_jvmadj + jvms->locoff() + idx, c); + } + void set_stack(const JVMState* jvms, uint idx, Node *c) { + assert(verify_jvms(jvms), "jvms must match"); + set_req(_jvmadj + jvms->stkoff() + idx, c); + } + void set_monitor(const JVMState* jvms, uint idx, Node *c) { + assert(verify_jvms(jvms), "jvms must match"); + set_req(_jvmadj + jvms->monoff() + idx, c); + } +}; + +//------------------------------MachCallNode---------------------------------- +// Machine-specific versions of subroutine calls +class MachCallNode : public MachSafePointNode { +protected: + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const = 0; // Size is bigger +public: + const TypeFunc *_tf; // Function type + address _entry_point; // Address of the method being called + float _cnt; // Estimate of number of times called + uint _argsize; // Size of argument block on stack + + const TypeFunc* tf() const { return _tf; } + const address entry_point() const { return _entry_point; } + const float cnt() const { return _cnt; } + uint argsize() const { return _argsize; } + + void set_tf(const TypeFunc* tf) { _tf = tf; } + void set_entry_point(address p) { _entry_point = p; } + void set_cnt(float c) { _cnt = c; } + void set_argsize(int s) { _argsize = s; } + + MachCallNode() : MachSafePointNode() { + init_class_id(Class_MachCall); + init_flags(Flag_is_Call); + } + + virtual const Type *bottom_type() const; + virtual bool pinned() const { return false; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const RegMask &in_RegMask(uint) const; + virtual int ret_addr_offset() { return 0; } + + bool returns_long() const { return tf()->return_type() == T_LONG; } + bool return_value_is_used() const; +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------MachCallJavaNode------------------------------ +// "Base" class for machine-specific versions of subroutine calls +class MachCallJavaNode : public MachCallNode { +protected: + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger +public: + ciMethod* _method; // Method being direct called + int _bci; // Byte Code index of call byte code + bool _optimized_virtual; // Tells if node is a static call or an optimized virtual + MachCallJavaNode() : MachCallNode() { + init_class_id(Class_MachCallJava); + } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------MachCallStaticJavaNode------------------------ +// Machine-specific versions of monomorphic subroutine calls +class MachCallStaticJavaNode : public MachCallJavaNode { + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger +public: + const char *_name; // Runtime wrapper name + MachCallStaticJavaNode() : MachCallJavaNode() { + init_class_id(Class_MachCallStaticJava); + } + + // If this is an uncommon trap, return the request code, else zero. + int uncommon_trap_request() const; + + virtual int ret_addr_offset(); +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; + void dump_trap_args(outputStream *st) const; +#endif +}; + +//------------------------------MachCallDynamicJavaNode------------------------ +// Machine-specific versions of possibly megamorphic subroutine calls +class MachCallDynamicJavaNode : public MachCallJavaNode { +public: + int _vtable_index; + MachCallDynamicJavaNode() : MachCallJavaNode() { + init_class_id(Class_MachCallDynamicJava); + DEBUG_ONLY(_vtable_index = -99); // throw an assert if uninitialized + } + virtual int ret_addr_offset(); +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------MachCallRuntimeNode---------------------------- +// Machine-specific versions of subroutine calls +class MachCallRuntimeNode : public MachCallNode { + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger +public: + const char *_name; // Printable name, if _method is NULL + MachCallRuntimeNode() : MachCallNode() { + init_class_id(Class_MachCallRuntime); + } + virtual int ret_addr_offset(); +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +class MachCallLeafNode: public MachCallRuntimeNode { +public: + MachCallLeafNode() : MachCallRuntimeNode() { + init_class_id(Class_MachCallLeaf); + } +}; + +//------------------------------MachHaltNode----------------------------------- +// Machine-specific versions of halt nodes +class MachHaltNode : public MachReturnNode { +public: + virtual JVMState* jvms() const; +}; + + +//------------------------------MachTempNode----------------------------------- +// Node used by the adlc to construct inputs to represent temporary registers +class MachTempNode : public MachNode { +private: + MachOper *_opnd_array[1]; + +public: + virtual const RegMask &out_RegMask() const { return *_opnds[0]->in_RegMask(0); } + virtual uint rule() const { return 9999999; } + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {} + + MachTempNode(MachOper* oper) { + init_class_id(Class_MachTemp); + _num_opnds = 1; + _opnds = _opnd_array; + add_req(NULL); + _opnds[0] = oper; + } + virtual uint size_of() const { return sizeof(MachTempNode); } + +#ifndef PRODUCT + virtual void format(PhaseRegAlloc *, outputStream *st ) const {} + virtual const char *Name() const { return "MachTemp";} +#endif +}; + + + +//------------------------------labelOper-------------------------------------- +// Machine-independent version of label operand +class labelOper : public MachOper { +private: + virtual uint num_edges() const { return 0; } +public: + // Supported for fixed size branches + Label* _label; // Label for branch(es) + + uint _block_num; + + labelOper() : _block_num(0), _label(0) {} + + labelOper(Label* label, uint block_num) : _label(label), _block_num(block_num) {} + + labelOper(labelOper* l) : _label(l->_label) , _block_num(l->_block_num) {} + + virtual MachOper *clone(Compile* C) const; + + virtual Label *label() const { return _label; } + + virtual uint opcode() const; + + virtual uint hash() const; + virtual uint cmp( const MachOper &oper ) const; +#ifndef PRODUCT + virtual const char *Name() const { return "Label";} + + virtual void int_format(PhaseRegAlloc *ra, const MachNode *node, outputStream *st) const; + virtual void ext_format(PhaseRegAlloc *ra, const MachNode *node, int idx, outputStream *st) const { int_format( ra, node, st ); } +#endif +}; + + +//------------------------------methodOper-------------------------------------- +// Machine-independent version of method operand +class methodOper : public MachOper { +private: + virtual uint num_edges() const { return 0; } +public: + intptr_t _method; // Address of method + methodOper() : _method(0) {} + methodOper(intptr_t method) : _method(method) {} + + virtual MachOper *clone(Compile* C) const; + + virtual intptr_t method() const { return _method; } + + virtual uint opcode() const; + + virtual uint hash() const; + virtual uint cmp( const MachOper &oper ) const; +#ifndef PRODUCT + virtual const char *Name() const { return "Method";} + + virtual void int_format(PhaseRegAlloc *ra, const MachNode *node, outputStream *st) const; + virtual void ext_format(PhaseRegAlloc *ra, const MachNode *node, int idx, outputStream *st) const { int_format( ra, node, st ); } +#endif +}; diff --git a/hotspot/src/share/vm/opto/macro.cpp b/hotspot/src/share/vm/opto/macro.cpp new file mode 100644 index 00000000000..9ba4bc3d4ff --- /dev/null +++ b/hotspot/src/share/vm/opto/macro.cpp @@ -0,0 +1,995 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_macro.cpp.incl" + + +// +// Replace any references to "oldref" in inputs to "use" with "newref". +// Returns the number of replacements made. +// +int PhaseMacroExpand::replace_input(Node *use, Node *oldref, Node *newref) { + int nreplacements = 0; + uint req = use->req(); + for (uint j = 0; j < use->len(); j++) { + Node *uin = use->in(j); + if (uin == oldref) { + if (j < req) + use->set_req(j, newref); + else + use->set_prec(j, newref); + nreplacements++; + } else if (j >= req && uin == NULL) { + break; + } + } + return nreplacements; +} + +void PhaseMacroExpand::copy_call_debug_info(CallNode *oldcall, CallNode * newcall) { + // Copy debug information and adjust JVMState information + uint old_dbg_start = oldcall->tf()->domain()->cnt(); + uint new_dbg_start = newcall->tf()->domain()->cnt(); + int jvms_adj = new_dbg_start - old_dbg_start; + assert (new_dbg_start == newcall->req(), "argument count mismatch"); + for (uint i = old_dbg_start; i < oldcall->req(); i++) { + newcall->add_req(oldcall->in(i)); + } + newcall->set_jvms(oldcall->jvms()); + for (JVMState *jvms = newcall->jvms(); jvms != NULL; jvms = jvms->caller()) { + jvms->set_map(newcall); + jvms->set_locoff(jvms->locoff()+jvms_adj); + jvms->set_stkoff(jvms->stkoff()+jvms_adj); + jvms->set_monoff(jvms->monoff()+jvms_adj); + jvms->set_endoff(jvms->endoff()+jvms_adj); + } +} + +Node* PhaseMacroExpand::opt_iff(Node* region, Node* iff) { + IfNode *opt_iff = transform_later(iff)->as_If(); + + // Fast path taken; set region slot 2 + Node *fast_taken = transform_later( new (C, 1) IfFalseNode(opt_iff) ); + region->init_req(2,fast_taken); // Capture fast-control + + // Fast path not-taken, i.e. slow path + Node *slow_taken = transform_later( new (C, 1) IfTrueNode(opt_iff) ); + return slow_taken; +} + +//--------------------copy_predefined_input_for_runtime_call-------------------- +void PhaseMacroExpand::copy_predefined_input_for_runtime_call(Node * ctrl, CallNode* oldcall, CallNode* call) { + // Set fixed predefined input arguments + call->init_req( TypeFunc::Control, ctrl ); + call->init_req( TypeFunc::I_O , oldcall->in( TypeFunc::I_O) ); + call->init_req( TypeFunc::Memory , oldcall->in( TypeFunc::Memory ) ); // ????? + call->init_req( TypeFunc::ReturnAdr, oldcall->in( TypeFunc::ReturnAdr ) ); + call->init_req( TypeFunc::FramePtr, oldcall->in( TypeFunc::FramePtr ) ); +} + +//------------------------------make_slow_call--------------------------------- +CallNode* PhaseMacroExpand::make_slow_call(CallNode *oldcall, const TypeFunc* slow_call_type, address slow_call, const char* leaf_name, Node* slow_path, Node* parm0, Node* parm1) { + + // Slow-path call + int size = slow_call_type->domain()->cnt(); + CallNode *call = leaf_name + ? (CallNode*)new (C, size) CallLeafNode ( slow_call_type, slow_call, leaf_name, TypeRawPtr::BOTTOM ) + : (CallNode*)new (C, size) CallStaticJavaNode( slow_call_type, slow_call, OptoRuntime::stub_name(slow_call), oldcall->jvms()->bci(), TypeRawPtr::BOTTOM ); + + // Slow path call has no side-effects, uses few values + copy_predefined_input_for_runtime_call(slow_path, oldcall, call ); + if (parm0 != NULL) call->init_req(TypeFunc::Parms+0, parm0); + if (parm1 != NULL) call->init_req(TypeFunc::Parms+1, parm1); + copy_call_debug_info(oldcall, call); + call->set_cnt(PROB_UNLIKELY_MAG(4)); // Same effect as RC_UNCOMMON. + _igvn.hash_delete(oldcall); + _igvn.subsume_node(oldcall, call); + transform_later(call); + + return call; +} + +void PhaseMacroExpand::extract_call_projections(CallNode *call) { + _fallthroughproj = NULL; + _fallthroughcatchproj = NULL; + _ioproj_fallthrough = NULL; + _ioproj_catchall = NULL; + _catchallcatchproj = NULL; + _memproj_fallthrough = NULL; + _memproj_catchall = NULL; + _resproj = NULL; + for (DUIterator_Fast imax, i = call->fast_outs(imax); i < imax; i++) { + ProjNode *pn = call->fast_out(i)->as_Proj(); + switch (pn->_con) { + case TypeFunc::Control: + { + // For Control (fallthrough) and I_O (catch_all_index) we have CatchProj -> Catch -> Proj + _fallthroughproj = pn; + DUIterator_Fast jmax, j = pn->fast_outs(jmax); + const Node *cn = pn->fast_out(j); + if (cn->is_Catch()) { + ProjNode *cpn = NULL; + for (DUIterator_Fast kmax, k = cn->fast_outs(kmax); k < kmax; k++) { + cpn = cn->fast_out(k)->as_Proj(); + assert(cpn->is_CatchProj(), "must be a CatchProjNode"); + if (cpn->_con == CatchProjNode::fall_through_index) + _fallthroughcatchproj = cpn; + else { + assert(cpn->_con == CatchProjNode::catch_all_index, "must be correct index."); + _catchallcatchproj = cpn; + } + } + } + break; + } + case TypeFunc::I_O: + if (pn->_is_io_use) + _ioproj_catchall = pn; + else + _ioproj_fallthrough = pn; + break; + case TypeFunc::Memory: + if (pn->_is_io_use) + _memproj_catchall = pn; + else + _memproj_fallthrough = pn; + break; + case TypeFunc::Parms: + _resproj = pn; + break; + default: + assert(false, "unexpected projection from allocation node."); + } + } + +} + + +//---------------------------set_eden_pointers------------------------- +void PhaseMacroExpand::set_eden_pointers(Node* &eden_top_adr, Node* &eden_end_adr) { + if (UseTLAB) { // Private allocation: load from TLS + Node* thread = transform_later(new (C, 1) ThreadLocalNode()); + int tlab_top_offset = in_bytes(JavaThread::tlab_top_offset()); + int tlab_end_offset = in_bytes(JavaThread::tlab_end_offset()); + eden_top_adr = basic_plus_adr(top()/*not oop*/, thread, tlab_top_offset); + eden_end_adr = basic_plus_adr(top()/*not oop*/, thread, tlab_end_offset); + } else { // Shared allocation: load from globals + CollectedHeap* ch = Universe::heap(); + address top_adr = (address)ch->top_addr(); + address end_adr = (address)ch->end_addr(); + eden_top_adr = makecon(TypeRawPtr::make(top_adr)); + eden_end_adr = basic_plus_adr(eden_top_adr, end_adr - top_adr); + } +} + + +Node* PhaseMacroExpand::make_load(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) { + Node* adr = basic_plus_adr(base, offset); + const TypePtr* adr_type = TypeRawPtr::BOTTOM; + Node* value = LoadNode::make(C, ctl, mem, adr, adr_type, value_type, bt); + transform_later(value); + return value; +} + + +Node* PhaseMacroExpand::make_store(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt) { + Node* adr = basic_plus_adr(base, offset); + mem = StoreNode::make(C, ctl, mem, adr, NULL, value, bt); + transform_later(mem); + return mem; +} + +//============================================================================= +// +// A L L O C A T I O N +// +// Allocation attempts to be fast in the case of frequent small objects. +// It breaks down like this: +// +// 1) Size in doublewords is computed. This is a constant for objects and +// variable for most arrays. Doubleword units are used to avoid size +// overflow of huge doubleword arrays. We need doublewords in the end for +// rounding. +// +// 2) Size is checked for being 'too large'. Too-large allocations will go +// the slow path into the VM. The slow path can throw any required +// exceptions, and does all the special checks for very large arrays. The +// size test can constant-fold away for objects. For objects with +// finalizers it constant-folds the otherway: you always go slow with +// finalizers. +// +// 3) If NOT using TLABs, this is the contended loop-back point. +// Load-Locked the heap top. If using TLABs normal-load the heap top. +// +// 4) Check that heap top + size*8 < max. If we fail go the slow ` route. +// NOTE: "top+size*8" cannot wrap the 4Gig line! Here's why: for largish +// "size*8" we always enter the VM, where "largish" is a constant picked small +// enough that there's always space between the eden max and 4Gig (old space is +// there so it's quite large) and large enough that the cost of entering the VM +// is dwarfed by the cost to initialize the space. +// +// 5) If NOT using TLABs, Store-Conditional the adjusted heap top back +// down. If contended, repeat at step 3. If using TLABs normal-store +// adjusted heap top back down; there is no contention. +// +// 6) If !ZeroTLAB then Bulk-clear the object/array. Fill in klass & mark +// fields. +// +// 7) Merge with the slow-path; cast the raw memory pointer to the correct +// oop flavor. +// +//============================================================================= +// FastAllocateSizeLimit value is in DOUBLEWORDS. +// Allocations bigger than this always go the slow route. +// This value must be small enough that allocation attempts that need to +// trigger exceptions go the slow route. Also, it must be small enough so +// that heap_top + size_in_bytes does not wrap around the 4Gig limit. +//=============================================================================j// +// %%% Here is an old comment from parseHelper.cpp; is it outdated? +// The allocator will coalesce int->oop copies away. See comment in +// coalesce.cpp about how this works. It depends critically on the exact +// code shape produced here, so if you are changing this code shape +// make sure the GC info for the heap-top is correct in and around the +// slow-path call. +// + +void PhaseMacroExpand::expand_allocate_common( + AllocateNode* alloc, // allocation node to be expanded + Node* length, // array length for an array allocation + const TypeFunc* slow_call_type, // Type of slow call + address slow_call_address // Address of slow call + ) +{ + + Node* ctrl = alloc->in(TypeFunc::Control); + Node* mem = alloc->in(TypeFunc::Memory); + Node* i_o = alloc->in(TypeFunc::I_O); + Node* size_in_bytes = alloc->in(AllocateNode::AllocSize); + Node* klass_node = alloc->in(AllocateNode::KlassNode); + Node* initial_slow_test = alloc->in(AllocateNode::InitialTest); + + Node* eden_top_adr; + Node* eden_end_adr; + set_eden_pointers(eden_top_adr, eden_end_adr); + + uint raw_idx = C->get_alias_index(TypeRawPtr::BOTTOM); + assert(ctrl != NULL, "must have control"); + + // Load Eden::end. Loop invariant and hoisted. + // + // Note: We set the control input on "eden_end" and "old_eden_top" when using + // a TLAB to work around a bug where these values were being moved across + // a safepoint. These are not oops, so they cannot be include in the oop + // map, but the can be changed by a GC. The proper way to fix this would + // be to set the raw memory state when generating a SafepointNode. However + // this will require extensive changes to the loop optimization in order to + // prevent a degradation of the optimization. + // See comment in memnode.hpp, around line 227 in class LoadPNode. + Node* eden_end = make_load(ctrl, mem, eden_end_adr, 0, TypeRawPtr::BOTTOM, T_ADDRESS); + + // We need a Region and corresponding Phi's to merge the slow-path and fast-path results. + // they will not be used if "always_slow" is set + enum { slow_result_path = 1, fast_result_path = 2 }; + Node *result_region; + Node *result_phi_rawmem; + Node *result_phi_rawoop; + Node *result_phi_i_o; + + // The initial slow comparison is a size check, the comparison + // we want to do is a BoolTest::gt + bool always_slow = false; + int tv = _igvn.find_int_con(initial_slow_test, -1); + if (tv >= 0) { + always_slow = (tv == 1); + initial_slow_test = NULL; + } else { + initial_slow_test = BoolNode::make_predicate(initial_slow_test, &_igvn); + } + + if (DTraceAllocProbes) { + // Force slow-path allocation + always_slow = true; + initial_slow_test = NULL; + } + + enum { too_big_or_final_path = 1, need_gc_path = 2 }; + Node *slow_region = NULL; + Node *toobig_false = ctrl; + + assert (initial_slow_test == NULL || !always_slow, "arguments must be consistent"); + // generate the initial test if necessary + if (initial_slow_test != NULL ) { + slow_region = new (C, 3) RegionNode(3); + + // Now make the initial failure test. Usually a too-big test but + // might be a TRUE for finalizers or a fancy class check for + // newInstance0. + IfNode *toobig_iff = new (C, 2) IfNode(ctrl, initial_slow_test, PROB_MIN, COUNT_UNKNOWN); + transform_later(toobig_iff); + // Plug the failing-too-big test into the slow-path region + Node *toobig_true = new (C, 1) IfTrueNode( toobig_iff ); + transform_later(toobig_true); + slow_region ->init_req( too_big_or_final_path, toobig_true ); + toobig_false = new (C, 1) IfFalseNode( toobig_iff ); + transform_later(toobig_false); + } else { // No initial test, just fall into next case + toobig_false = ctrl; + debug_only(slow_region = NodeSentinel); + } + + Node *slow_mem = mem; // save the current memory state for slow path + // generate the fast allocation code unless we know that the initial test will always go slow + if (!always_slow) { + // allocate the Region and Phi nodes for the result + result_region = new (C, 3) RegionNode(3); + result_phi_rawmem = new (C, 3) PhiNode( result_region, Type::MEMORY, TypeRawPtr::BOTTOM ); + result_phi_rawoop = new (C, 3) PhiNode( result_region, TypeRawPtr::BOTTOM ); + result_phi_i_o = new (C, 3) PhiNode( result_region, Type::ABIO ); // I/O is used for Prefetch + + // We need a Region for the loop-back contended case. + enum { fall_in_path = 1, contended_loopback_path = 2 }; + Node *contended_region; + Node *contended_phi_rawmem; + if( UseTLAB ) { + contended_region = toobig_false; + contended_phi_rawmem = mem; + } else { + contended_region = new (C, 3) RegionNode(3); + contended_phi_rawmem = new (C, 3) PhiNode( contended_region, Type::MEMORY, TypeRawPtr::BOTTOM); + // Now handle the passing-too-big test. We fall into the contended + // loop-back merge point. + contended_region ->init_req( fall_in_path, toobig_false ); + contended_phi_rawmem->init_req( fall_in_path, mem ); + transform_later(contended_region); + transform_later(contended_phi_rawmem); + } + + // Load(-locked) the heap top. + // See note above concerning the control input when using a TLAB + Node *old_eden_top = UseTLAB + ? new (C, 3) LoadPNode ( ctrl, contended_phi_rawmem, eden_top_adr, TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM ) + : new (C, 3) LoadPLockedNode( contended_region, contended_phi_rawmem, eden_top_adr ); + + transform_later(old_eden_top); + // Add to heap top to get a new heap top + Node *new_eden_top = new (C, 4) AddPNode( top(), old_eden_top, size_in_bytes ); + transform_later(new_eden_top); + // Check for needing a GC; compare against heap end + Node *needgc_cmp = new (C, 3) CmpPNode( new_eden_top, eden_end ); + transform_later(needgc_cmp); + Node *needgc_bol = new (C, 2) BoolNode( needgc_cmp, BoolTest::ge ); + transform_later(needgc_bol); + IfNode *needgc_iff = new (C, 2) IfNode(contended_region, needgc_bol, PROB_UNLIKELY_MAG(4), COUNT_UNKNOWN ); + transform_later(needgc_iff); + + // Plug the failing-heap-space-need-gc test into the slow-path region + Node *needgc_true = new (C, 1) IfTrueNode( needgc_iff ); + transform_later(needgc_true); + if( initial_slow_test ) { + slow_region ->init_req( need_gc_path, needgc_true ); + // This completes all paths into the slow merge point + transform_later(slow_region); + } else { // No initial slow path needed! + // Just fall from the need-GC path straight into the VM call. + slow_region = needgc_true; + } + // No need for a GC. Setup for the Store-Conditional + Node *needgc_false = new (C, 1) IfFalseNode( needgc_iff ); + transform_later(needgc_false); + + // Grab regular I/O before optional prefetch may change it. + // Slow-path does no I/O so just set it to the original I/O. + result_phi_i_o->init_req( slow_result_path, i_o ); + + i_o = prefetch_allocation(i_o, needgc_false, contended_phi_rawmem, + old_eden_top, new_eden_top, length); + + // Store (-conditional) the modified eden top back down. + // StorePConditional produces flags for a test PLUS a modified raw + // memory state. + Node *store_eden_top; + Node *fast_oop_ctrl; + if( UseTLAB ) { + store_eden_top = new (C, 4) StorePNode( needgc_false, contended_phi_rawmem, eden_top_adr, TypeRawPtr::BOTTOM, new_eden_top ); + transform_later(store_eden_top); + fast_oop_ctrl = needgc_false; // No contention, so this is the fast path + } else { + store_eden_top = new (C, 5) StorePConditionalNode( needgc_false, contended_phi_rawmem, eden_top_adr, new_eden_top, old_eden_top ); + transform_later(store_eden_top); + Node *contention_check = new (C, 2) BoolNode( store_eden_top, BoolTest::ne ); + transform_later(contention_check); + store_eden_top = new (C, 1) SCMemProjNode(store_eden_top); + transform_later(store_eden_top); + + // If not using TLABs, check to see if there was contention. + IfNode *contention_iff = new (C, 2) IfNode ( needgc_false, contention_check, PROB_MIN, COUNT_UNKNOWN ); + transform_later(contention_iff); + Node *contention_true = new (C, 1) IfTrueNode( contention_iff ); + transform_later(contention_true); + // If contention, loopback and try again. + contended_region->init_req( contended_loopback_path, contention_true ); + contended_phi_rawmem->init_req( contended_loopback_path, store_eden_top ); + + // Fast-path succeeded with no contention! + Node *contention_false = new (C, 1) IfFalseNode( contention_iff ); + transform_later(contention_false); + fast_oop_ctrl = contention_false; + } + + // Rename successful fast-path variables to make meaning more obvious + Node* fast_oop = old_eden_top; + Node* fast_oop_rawmem = store_eden_top; + fast_oop_rawmem = initialize_object(alloc, + fast_oop_ctrl, fast_oop_rawmem, fast_oop, + klass_node, length, size_in_bytes); + + if (ExtendedDTraceProbes) { + // Slow-path call + int size = TypeFunc::Parms + 2; + CallLeafNode *call = new (C, size) CallLeafNode(OptoRuntime::dtrace_object_alloc_Type(), + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc_base), + "dtrace_object_alloc", + TypeRawPtr::BOTTOM); + + // Get base of thread-local storage area + Node* thread = new (C, 1) ThreadLocalNode(); + transform_later(thread); + + call->init_req(TypeFunc::Parms+0, thread); + call->init_req(TypeFunc::Parms+1, fast_oop); + call->init_req( TypeFunc::Control, fast_oop_ctrl ); + call->init_req( TypeFunc::I_O , top() ) ; // does no i/o + call->init_req( TypeFunc::Memory , fast_oop_rawmem ); + call->init_req( TypeFunc::ReturnAdr, alloc->in(TypeFunc::ReturnAdr) ); + call->init_req( TypeFunc::FramePtr, alloc->in(TypeFunc::FramePtr) ); + transform_later(call); + fast_oop_ctrl = new (C, 1) ProjNode(call,TypeFunc::Control); + transform_later(fast_oop_ctrl); + fast_oop_rawmem = new (C, 1) ProjNode(call,TypeFunc::Memory); + transform_later(fast_oop_rawmem); + } + + // Plug in the successful fast-path into the result merge point + result_region ->init_req( fast_result_path, fast_oop_ctrl ); + result_phi_rawoop->init_req( fast_result_path, fast_oop ); + result_phi_i_o ->init_req( fast_result_path, i_o ); + result_phi_rawmem->init_req( fast_result_path, fast_oop_rawmem ); + } else { + slow_region = ctrl; + } + + // Generate slow-path call + CallNode *call = new (C, slow_call_type->domain()->cnt()) + CallStaticJavaNode(slow_call_type, slow_call_address, + OptoRuntime::stub_name(slow_call_address), + alloc->jvms()->bci(), + TypePtr::BOTTOM); + call->init_req( TypeFunc::Control, slow_region ); + call->init_req( TypeFunc::I_O , top() ) ; // does no i/o + call->init_req( TypeFunc::Memory , slow_mem ); // may gc ptrs + call->init_req( TypeFunc::ReturnAdr, alloc->in(TypeFunc::ReturnAdr) ); + call->init_req( TypeFunc::FramePtr, alloc->in(TypeFunc::FramePtr) ); + + call->init_req(TypeFunc::Parms+0, klass_node); + if (length != NULL) { + call->init_req(TypeFunc::Parms+1, length); + } + + // Copy debug information and adjust JVMState information, then replace + // allocate node with the call + copy_call_debug_info((CallNode *) alloc, call); + if (!always_slow) { + call->set_cnt(PROB_UNLIKELY_MAG(4)); // Same effect as RC_UNCOMMON. + } + _igvn.hash_delete(alloc); + _igvn.subsume_node(alloc, call); + transform_later(call); + + // Identify the output projections from the allocate node and + // adjust any references to them. + // The control and io projections look like: + // + // v---Proj(ctrl) <-----+ v---CatchProj(ctrl) + // Allocate Catch + // ^---Proj(io) <-------+ ^---CatchProj(io) + // + // We are interested in the CatchProj nodes. + // + extract_call_projections(call); + + // An allocate node has separate memory projections for the uses on the control and i_o paths + // Replace uses of the control memory projection with result_phi_rawmem (unless we are only generating a slow call) + if (!always_slow && _memproj_fallthrough != NULL) { + for (DUIterator_Fast imax, i = _memproj_fallthrough->fast_outs(imax); i < imax; i++) { + Node *use = _memproj_fallthrough->fast_out(i); + _igvn.hash_delete(use); + imax -= replace_input(use, _memproj_fallthrough, result_phi_rawmem); + _igvn._worklist.push(use); + // back up iterator + --i; + } + } + // Now change uses of _memproj_catchall to use _memproj_fallthrough and delete _memproj_catchall so + // we end up with a call that has only 1 memory projection + if (_memproj_catchall != NULL ) { + if (_memproj_fallthrough == NULL) { + _memproj_fallthrough = new (C, 1) ProjNode(call, TypeFunc::Memory); + transform_later(_memproj_fallthrough); + } + for (DUIterator_Fast imax, i = _memproj_catchall->fast_outs(imax); i < imax; i++) { + Node *use = _memproj_catchall->fast_out(i); + _igvn.hash_delete(use); + imax -= replace_input(use, _memproj_catchall, _memproj_fallthrough); + _igvn._worklist.push(use); + // back up iterator + --i; + } + } + + mem = result_phi_rawmem; + + // An allocate node has separate i_o projections for the uses on the control and i_o paths + // Replace uses of the control i_o projection with result_phi_i_o (unless we are only generating a slow call) + if (_ioproj_fallthrough == NULL) { + _ioproj_fallthrough = new (C, 1) ProjNode(call, TypeFunc::I_O); + transform_later(_ioproj_fallthrough); + } else if (!always_slow) { + for (DUIterator_Fast imax, i = _ioproj_fallthrough->fast_outs(imax); i < imax; i++) { + Node *use = _ioproj_fallthrough->fast_out(i); + + _igvn.hash_delete(use); + imax -= replace_input(use, _ioproj_fallthrough, result_phi_i_o); + _igvn._worklist.push(use); + // back up iterator + --i; + } + } + // Now change uses of _ioproj_catchall to use _ioproj_fallthrough and delete _ioproj_catchall so + // we end up with a call that has only 1 control projection + if (_ioproj_catchall != NULL ) { + for (DUIterator_Fast imax, i = _ioproj_catchall->fast_outs(imax); i < imax; i++) { + Node *use = _ioproj_catchall->fast_out(i); + _igvn.hash_delete(use); + imax -= replace_input(use, _ioproj_catchall, _ioproj_fallthrough); + _igvn._worklist.push(use); + // back up iterator + --i; + } + } + + // if we generated only a slow call, we are done + if (always_slow) + return; + + + if (_fallthroughcatchproj != NULL) { + ctrl = _fallthroughcatchproj->clone(); + transform_later(ctrl); + _igvn.hash_delete(_fallthroughcatchproj); + _igvn.subsume_node(_fallthroughcatchproj, result_region); + } else { + ctrl = top(); + } + Node *slow_result; + if (_resproj == NULL) { + // no uses of the allocation result + slow_result = top(); + } else { + slow_result = _resproj->clone(); + transform_later(slow_result); + _igvn.hash_delete(_resproj); + _igvn.subsume_node(_resproj, result_phi_rawoop); + } + + // Plug slow-path into result merge point + result_region ->init_req( slow_result_path, ctrl ); + result_phi_rawoop->init_req( slow_result_path, slow_result); + result_phi_rawmem->init_req( slow_result_path, _memproj_fallthrough ); + transform_later(result_region); + transform_later(result_phi_rawoop); + transform_later(result_phi_rawmem); + transform_later(result_phi_i_o); + // This completes all paths into the result merge point +} + + +// Helper for PhaseMacroExpand::expand_allocate_common. +// Initializes the newly-allocated storage. +Node* +PhaseMacroExpand::initialize_object(AllocateNode* alloc, + Node* control, Node* rawmem, Node* object, + Node* klass_node, Node* length, + Node* size_in_bytes) { + InitializeNode* init = alloc->initialization(); + // Store the klass & mark bits + Node* mark_node = NULL; + // For now only enable fast locking for non-array types + if (UseBiasedLocking && (length == NULL)) { + mark_node = make_load(NULL, rawmem, klass_node, Klass::prototype_header_offset_in_bytes() + sizeof(oopDesc), TypeRawPtr::BOTTOM, T_ADDRESS); + } else { + mark_node = makecon(TypeRawPtr::make((address)markOopDesc::prototype())); + } + rawmem = make_store(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, T_ADDRESS); + rawmem = make_store(control, rawmem, object, oopDesc::klass_offset_in_bytes(), klass_node, T_OBJECT); + int header_size = alloc->minimum_header_size(); // conservatively small + + // Array length + if (length != NULL) { // Arrays need length field + rawmem = make_store(control, rawmem, object, arrayOopDesc::length_offset_in_bytes(), length, T_INT); + // conservatively small header size: + header_size = sizeof(arrayOopDesc); + ciKlass* k = _igvn.type(klass_node)->is_klassptr()->klass(); + if (k->is_array_klass()) // we know the exact header size in most cases: + header_size = Klass::layout_helper_header_size(k->layout_helper()); + } + + // Clear the object body, if necessary. + if (init == NULL) { + // The init has somehow disappeared; be cautious and clear everything. + // + // This can happen if a node is allocated but an uncommon trap occurs + // immediately. In this case, the Initialize gets associated with the + // trap, and may be placed in a different (outer) loop, if the Allocate + // is in a loop. If (this is rare) the inner loop gets unrolled, then + // there can be two Allocates to one Initialize. The answer in all these + // edge cases is safety first. It is always safe to clear immediately + // within an Allocate, and then (maybe or maybe not) clear some more later. + if (!ZeroTLAB) + rawmem = ClearArrayNode::clear_memory(control, rawmem, object, + header_size, size_in_bytes, + &_igvn); + } else { + if (!init->is_complete()) { + // Try to win by zeroing only what the init does not store. + // We can also try to do some peephole optimizations, + // such as combining some adjacent subword stores. + rawmem = init->complete_stores(control, rawmem, object, + header_size, size_in_bytes, &_igvn); + } + + // We have no more use for this link, since the AllocateNode goes away: + init->set_req(InitializeNode::RawAddress, top()); + // (If we keep the link, it just confuses the register allocator, + // who thinks he sees a real use of the address by the membar.) + } + + return rawmem; +} + +// Generate prefetch instructions for next allocations. +Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, + Node*& contended_phi_rawmem, + Node* old_eden_top, Node* new_eden_top, + Node* length) { + if( UseTLAB && AllocatePrefetchStyle == 2 ) { + // Generate prefetch allocation with watermark check. + // As an allocation hits the watermark, we will prefetch starting + // at a "distance" away from watermark. + enum { fall_in_path = 1, pf_path = 2 }; + + Node *pf_region = new (C, 3) RegionNode(3); + Node *pf_phi_rawmem = new (C, 3) PhiNode( pf_region, Type::MEMORY, + TypeRawPtr::BOTTOM ); + // I/O is used for Prefetch + Node *pf_phi_abio = new (C, 3) PhiNode( pf_region, Type::ABIO ); + + Node *thread = new (C, 1) ThreadLocalNode(); + transform_later(thread); + + Node *eden_pf_adr = new (C, 4) AddPNode( top()/*not oop*/, thread, + _igvn.MakeConX(in_bytes(JavaThread::tlab_pf_top_offset())) ); + transform_later(eden_pf_adr); + + Node *old_pf_wm = new (C, 3) LoadPNode( needgc_false, + contended_phi_rawmem, eden_pf_adr, + TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM ); + transform_later(old_pf_wm); + + // check against new_eden_top + Node *need_pf_cmp = new (C, 3) CmpPNode( new_eden_top, old_pf_wm ); + transform_later(need_pf_cmp); + Node *need_pf_bol = new (C, 2) BoolNode( need_pf_cmp, BoolTest::ge ); + transform_later(need_pf_bol); + IfNode *need_pf_iff = new (C, 2) IfNode( needgc_false, need_pf_bol, + PROB_UNLIKELY_MAG(4), COUNT_UNKNOWN ); + transform_later(need_pf_iff); + + // true node, add prefetchdistance + Node *need_pf_true = new (C, 1) IfTrueNode( need_pf_iff ); + transform_later(need_pf_true); + + Node *need_pf_false = new (C, 1) IfFalseNode( need_pf_iff ); + transform_later(need_pf_false); + + Node *new_pf_wmt = new (C, 4) AddPNode( top(), old_pf_wm, + _igvn.MakeConX(AllocatePrefetchDistance) ); + transform_later(new_pf_wmt ); + new_pf_wmt->set_req(0, need_pf_true); + + Node *store_new_wmt = new (C, 4) StorePNode( need_pf_true, + contended_phi_rawmem, eden_pf_adr, + TypeRawPtr::BOTTOM, new_pf_wmt ); + transform_later(store_new_wmt); + + // adding prefetches + pf_phi_abio->init_req( fall_in_path, i_o ); + + Node *prefetch_adr; + Node *prefetch; + uint lines = AllocatePrefetchDistance / AllocatePrefetchStepSize; + uint step_size = AllocatePrefetchStepSize; + uint distance = 0; + + for ( uint i = 0; i < lines; i++ ) { + prefetch_adr = new (C, 4) AddPNode( old_pf_wm, new_pf_wmt, + _igvn.MakeConX(distance) ); + transform_later(prefetch_adr); + prefetch = new (C, 3) PrefetchWriteNode( i_o, prefetch_adr ); + transform_later(prefetch); + distance += step_size; + i_o = prefetch; + } + pf_phi_abio->set_req( pf_path, i_o ); + + pf_region->init_req( fall_in_path, need_pf_false ); + pf_region->init_req( pf_path, need_pf_true ); + + pf_phi_rawmem->init_req( fall_in_path, contended_phi_rawmem ); + pf_phi_rawmem->init_req( pf_path, store_new_wmt ); + + transform_later(pf_region); + transform_later(pf_phi_rawmem); + transform_later(pf_phi_abio); + + needgc_false = pf_region; + contended_phi_rawmem = pf_phi_rawmem; + i_o = pf_phi_abio; + } else if( AllocatePrefetchStyle > 0 ) { + // Insert a prefetch for each allocation only on the fast-path + Node *prefetch_adr; + Node *prefetch; + // Generate several prefetch instructions only for arrays. + uint lines = (length != NULL) ? AllocatePrefetchLines : 1; + uint step_size = AllocatePrefetchStepSize; + uint distance = AllocatePrefetchDistance; + for ( uint i = 0; i < lines; i++ ) { + prefetch_adr = new (C, 4) AddPNode( old_eden_top, new_eden_top, + _igvn.MakeConX(distance) ); + transform_later(prefetch_adr); + prefetch = new (C, 3) PrefetchWriteNode( i_o, prefetch_adr ); + // Do not let it float too high, since if eden_top == eden_end, + // both might be null. + if( i == 0 ) { // Set control for first prefetch, next follows it + prefetch->init_req(0, needgc_false); + } + transform_later(prefetch); + distance += step_size; + i_o = prefetch; + } + } + return i_o; +} + + +void PhaseMacroExpand::expand_allocate(AllocateNode *alloc) { + expand_allocate_common(alloc, NULL, + OptoRuntime::new_instance_Type(), + OptoRuntime::new_instance_Java()); +} + +void PhaseMacroExpand::expand_allocate_array(AllocateArrayNode *alloc) { + Node* length = alloc->in(AllocateNode::ALength); + expand_allocate_common(alloc, length, + OptoRuntime::new_array_Type(), + OptoRuntime::new_array_Java()); +} + + +// we have determined that this lock/unlock can be eliminated, we simply +// eliminate the node without expanding it. +// +// Note: The membar's associated with the lock/unlock are currently not +// eliminated. This should be investigated as a future enhancement. +// +void PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) { + Node* mem = alock->in(TypeFunc::Memory); + + // The memory projection from a lock/unlock is RawMem + // The input to a Lock is merged memory, so extract its RawMem input + // (unless the MergeMem has been optimized away.) + if (alock->is_Lock()) { + if (mem->is_MergeMem()) + mem = mem->as_MergeMem()->in(Compile::AliasIdxRaw); + } + + extract_call_projections(alock); + // There are 2 projections from the lock. The lock node will + // be deleted when its last use is subsumed below. + assert(alock->outcnt() == 2 && _fallthroughproj != NULL && + _memproj_fallthrough != NULL, "Unexpected projections from Lock/Unlock"); + _igvn.hash_delete(_fallthroughproj); + _igvn.subsume_node(_fallthroughproj, alock->in(TypeFunc::Control)); + _igvn.hash_delete(_memproj_fallthrough); + _igvn.subsume_node(_memproj_fallthrough, mem); + return; +} + + +//------------------------------expand_lock_node---------------------- +void PhaseMacroExpand::expand_lock_node(LockNode *lock) { + + Node* ctrl = lock->in(TypeFunc::Control); + Node* mem = lock->in(TypeFunc::Memory); + Node* obj = lock->obj_node(); + Node* box = lock->box_node(); + Node *flock = lock->fastlock_node(); + + if (lock->is_eliminated()) { + eliminate_locking_node(lock); + return; + } + + // Make the merge point + Node *region = new (C, 3) RegionNode(3); + + Node *bol = transform_later(new (C, 2) BoolNode(flock,BoolTest::ne)); + Node *iff = new (C, 2) IfNode( ctrl, bol, PROB_MIN, COUNT_UNKNOWN ); + // Optimize test; set region slot 2 + Node *slow_path = opt_iff(region,iff); + + // Make slow path call + CallNode *call = make_slow_call( (CallNode *) lock, OptoRuntime::complete_monitor_enter_Type(), OptoRuntime::complete_monitor_locking_Java(), NULL, slow_path, obj, box ); + + extract_call_projections(call); + + // Slow path can only throw asynchronous exceptions, which are always + // de-opted. So the compiler thinks the slow-call can never throw an + // exception. If it DOES throw an exception we would need the debug + // info removed first (since if it throws there is no monitor). + assert ( _ioproj_fallthrough == NULL && _ioproj_catchall == NULL && + _memproj_catchall == NULL && _catchallcatchproj == NULL, "Unexpected projection from Lock"); + + // Capture slow path + // disconnect fall-through projection from call and create a new one + // hook up users of fall-through projection to region + Node *slow_ctrl = _fallthroughproj->clone(); + transform_later(slow_ctrl); + _igvn.hash_delete(_fallthroughproj); + _fallthroughproj->disconnect_inputs(NULL); + region->init_req(1, slow_ctrl); + // region inputs are now complete + transform_later(region); + _igvn.subsume_node(_fallthroughproj, region); + + // create a Phi for the memory state + Node *mem_phi = new (C, 3) PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); + Node *memproj = transform_later( new (C, 1) ProjNode(call, TypeFunc::Memory) ); + mem_phi->init_req(1, memproj ); + mem_phi->init_req(2, mem); + transform_later(mem_phi); + _igvn.hash_delete(_memproj_fallthrough); + _igvn.subsume_node(_memproj_fallthrough, mem_phi); + + +} + +//------------------------------expand_unlock_node---------------------- +void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) { + + Node *ctrl = unlock->in(TypeFunc::Control); + Node* mem = unlock->in(TypeFunc::Memory); + Node* obj = unlock->obj_node(); + Node* box = unlock->box_node(); + + + if (unlock->is_eliminated()) { + eliminate_locking_node(unlock); + return; + } + + // No need for a null check on unlock + + // Make the merge point + RegionNode *region = new (C, 3) RegionNode(3); + + FastUnlockNode *funlock = new (C, 3) FastUnlockNode( ctrl, obj, box ); + funlock = transform_later( funlock )->as_FastUnlock(); + Node *bol = transform_later(new (C, 2) BoolNode(funlock,BoolTest::ne)); + Node *iff = new (C, 2) IfNode( ctrl, bol, PROB_MIN, COUNT_UNKNOWN ); + // Optimize test; set region slot 2 + Node *slow_path = opt_iff(region,iff); + + CallNode *call = make_slow_call( (CallNode *) unlock, OptoRuntime::complete_monitor_exit_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_unlocking_C), "complete_monitor_unlocking_C", slow_path, obj, box ); + + extract_call_projections(call); + + assert ( _ioproj_fallthrough == NULL && _ioproj_catchall == NULL && + _memproj_catchall == NULL && _catchallcatchproj == NULL, "Unexpected projection from Lock"); + + // No exceptions for unlocking + // Capture slow path + // disconnect fall-through projection from call and create a new one + // hook up users of fall-through projection to region + Node *slow_ctrl = _fallthroughproj->clone(); + transform_later(slow_ctrl); + _igvn.hash_delete(_fallthroughproj); + _fallthroughproj->disconnect_inputs(NULL); + region->init_req(1, slow_ctrl); + // region inputs are now complete + transform_later(region); + _igvn.subsume_node(_fallthroughproj, region); + + // create a Phi for the memory state + Node *mem_phi = new (C, 3) PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); + Node *memproj = transform_later( new(C, 1) ProjNode(call, TypeFunc::Memory) ); + mem_phi->init_req(1, memproj ); + mem_phi->init_req(2, mem); + transform_later(mem_phi); + _igvn.hash_delete(_memproj_fallthrough); + _igvn.subsume_node(_memproj_fallthrough, mem_phi); + + +} + +//------------------------------expand_macro_nodes---------------------- +// Returns true if a failure occurred. +bool PhaseMacroExpand::expand_macro_nodes() { + if (C->macro_count() == 0) + return false; + // Make sure expansion will not cause node limit to be exceeded. Worst case is a + // macro node gets expanded into about 50 nodes. Allow 50% more for optimization + if (C->check_node_count(C->macro_count() * 75, "out of nodes before macro expansion" ) ) + return true; + // expand "macro" nodes + // nodes are removed from the macro list as they are processed + while (C->macro_count() > 0) { + Node * n = C->macro_node(0); + assert(n->is_macro(), "only macro nodes expected here"); + if (_igvn.type(n) == Type::TOP || n->in(0)->is_top() ) { + // node is unreachable, so don't try to expand it + C->remove_macro_node(n); + continue; + } + switch (n->class_id()) { + case Node::Class_Allocate: + expand_allocate(n->as_Allocate()); + break; + case Node::Class_AllocateArray: + expand_allocate_array(n->as_AllocateArray()); + break; + case Node::Class_Lock: + expand_lock_node(n->as_Lock()); + break; + case Node::Class_Unlock: + expand_unlock_node(n->as_Unlock()); + break; + default: + assert(false, "unknown node type in macro list"); + } + if (C->failing()) return true; + } + _igvn.optimize(); + return false; +} diff --git a/hotspot/src/share/vm/opto/macro.hpp b/hotspot/src/share/vm/opto/macro.hpp new file mode 100644 index 00000000000..20dd65c40b7 --- /dev/null +++ b/hotspot/src/share/vm/opto/macro.hpp @@ -0,0 +1,107 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class AllocateNode; +class AllocateArrayNode; +class CallNode; +class Node; +class PhaseIterGVN; + +class PhaseMacroExpand : public Phase { +private: + PhaseIterGVN &_igvn; + + // Helper methods roughly modelled after GraphKit: + Node* top() const { return C->top(); } + Node* intcon(jint con) const { return _igvn.intcon(con); } + Node* longcon(jlong con) const { return _igvn.longcon(con); } + Node* makecon(const Type *t) const { return _igvn.makecon(t); } + Node* basic_plus_adr(Node* base, int offset) { + return (offset == 0)? base: basic_plus_adr(base, MakeConX(offset)); + } + Node* basic_plus_adr(Node* base, Node* ptr, int offset) { + return (offset == 0)? ptr: basic_plus_adr(base, ptr, MakeConX(offset)); + } + Node* basic_plus_adr(Node* base, Node* offset) { + return basic_plus_adr(base, base, offset); + } + Node* basic_plus_adr(Node* base, Node* ptr, Node* offset) { + Node* adr = new (C, 4) AddPNode(base, ptr, offset); + return transform_later(adr); + } + Node* transform_later(Node* n) { + // equivalent to _gvn.transform in GraphKit, Ideal, etc. + _igvn.register_new_node_with_optimizer(n); + return n; + } + void set_eden_pointers(Node* &eden_top_adr, Node* &eden_end_adr); + Node* make_load( Node* ctl, Node* mem, Node* base, int offset, + const Type* value_type, BasicType bt); + Node* make_store(Node* ctl, Node* mem, Node* base, int offset, + Node* value, BasicType bt); + + // projections extracted from a call node + ProjNode *_fallthroughproj; + ProjNode *_fallthroughcatchproj; + ProjNode *_ioproj_fallthrough; + ProjNode *_ioproj_catchall; + ProjNode *_catchallcatchproj; + ProjNode *_memproj_fallthrough; + ProjNode *_memproj_catchall; + ProjNode *_resproj; + + + void expand_allocate(AllocateNode *alloc); + void expand_allocate_array(AllocateArrayNode *alloc); + void expand_allocate_common(AllocateNode* alloc, + Node* length, + const TypeFunc* slow_call_type, + address slow_call_address); + void eliminate_locking_node(AbstractLockNode *alock); + void expand_lock_node(LockNode *lock); + void expand_unlock_node(UnlockNode *unlock); + + int replace_input(Node *use, Node *oldref, Node *newref); + void copy_call_debug_info(CallNode *oldcall, CallNode * newcall); + Node* opt_iff(Node* region, Node* iff); + void copy_predefined_input_for_runtime_call(Node * ctrl, CallNode* oldcall, CallNode* call); + CallNode* make_slow_call(CallNode *oldcall, const TypeFunc* slow_call_type, address slow_call, + const char* leaf_name, Node* slow_path, Node* parm0, Node* parm1); + void extract_call_projections(CallNode *call); + + Node* initialize_object(AllocateNode* alloc, + Node* control, Node* rawmem, Node* object, + Node* klass_node, Node* length, + Node* size_in_bytes); + + Node* prefetch_allocation(Node* i_o, + Node*& needgc_false, Node*& contended_phi_rawmem, + Node* old_eden_top, Node* new_eden_top, + Node* length); + +public: + PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn) {} + bool expand_macro_nodes(); + +}; diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp new file mode 100644 index 00000000000..a8d673a7f26 --- /dev/null +++ b/hotspot/src/share/vm/opto/matcher.cpp @@ -0,0 +1,2123 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_matcher.cpp.incl" + +OptoReg::Name OptoReg::c_frame_pointer; + + + +const int Matcher::base2reg[Type::lastype] = { + Node::NotAMachineReg,0,0, Op_RegI, Op_RegL, 0, + Node::NotAMachineReg, Node::NotAMachineReg, /* tuple, array */ + Op_RegP, Op_RegP, Op_RegP, Op_RegP, Op_RegP, Op_RegP, /* the pointers */ + 0, 0/*abio*/, + Op_RegP /* Return address */, 0, /* the memories */ + Op_RegF, Op_RegF, Op_RegF, Op_RegD, Op_RegD, Op_RegD, + 0 /*bottom*/ +}; + +const RegMask *Matcher::idealreg2regmask[_last_machine_leaf]; +RegMask Matcher::mreg2regmask[_last_Mach_Reg]; +RegMask Matcher::STACK_ONLY_mask; +RegMask Matcher::c_frame_ptr_mask; +const uint Matcher::_begin_rematerialize = _BEGIN_REMATERIALIZE; +const uint Matcher::_end_rematerialize = _END_REMATERIALIZE; + +//---------------------------Matcher------------------------------------------- +Matcher::Matcher( Node_List &proj_list ) : + PhaseTransform( Phase::Ins_Select ), +#ifdef ASSERT + _old2new_map(C->comp_arena()), +#endif + _shared_constants(C->comp_arena()), + _reduceOp(reduceOp), _leftOp(leftOp), _rightOp(rightOp), + _swallowed(swallowed), + _begin_inst_chain_rule(_BEGIN_INST_CHAIN_RULE), + _end_inst_chain_rule(_END_INST_CHAIN_RULE), + _must_clone(must_clone), _proj_list(proj_list), + _register_save_policy(register_save_policy), + _c_reg_save_policy(c_reg_save_policy), + _register_save_type(register_save_type), + _ruleName(ruleName), + _allocation_started(false), + _states_arena(Chunk::medium_size), + _visited(&_states_arena), + _shared(&_states_arena), + _dontcare(&_states_arena) { + C->set_matcher(this); + + idealreg2spillmask[Op_RegI] = NULL; + idealreg2spillmask[Op_RegL] = NULL; + idealreg2spillmask[Op_RegF] = NULL; + idealreg2spillmask[Op_RegD] = NULL; + idealreg2spillmask[Op_RegP] = NULL; + + idealreg2debugmask[Op_RegI] = NULL; + idealreg2debugmask[Op_RegL] = NULL; + idealreg2debugmask[Op_RegF] = NULL; + idealreg2debugmask[Op_RegD] = NULL; + idealreg2debugmask[Op_RegP] = NULL; +} + +//------------------------------warp_incoming_stk_arg------------------------ +// This warps a VMReg into an OptoReg::Name +OptoReg::Name Matcher::warp_incoming_stk_arg( VMReg reg ) { + OptoReg::Name warped; + if( reg->is_stack() ) { // Stack slot argument? + warped = OptoReg::add(_old_SP, reg->reg2stack() ); + warped = OptoReg::add(warped, C->out_preserve_stack_slots()); + if( warped >= _in_arg_limit ) + _in_arg_limit = OptoReg::add(warped, 1); // Bump max stack slot seen + if (!RegMask::can_represent(warped)) { + // the compiler cannot represent this method's calling sequence + C->record_method_not_compilable_all_tiers("unsupported incoming calling sequence"); + return OptoReg::Bad; + } + return warped; + } + return OptoReg::as_OptoReg(reg); +} + +//---------------------------compute_old_SP------------------------------------ +OptoReg::Name Compile::compute_old_SP() { + int fixed = fixed_slots(); + int preserve = in_preserve_stack_slots(); + return OptoReg::stack2reg(round_to(fixed + preserve, Matcher::stack_alignment_in_slots())); +} + + + +#ifdef ASSERT +void Matcher::verify_new_nodes_only(Node* xroot) { + // Make sure that the new graph only references new nodes + ResourceMark rm; + Unique_Node_List worklist; + VectorSet visited(Thread::current()->resource_area()); + worklist.push(xroot); + while (worklist.size() > 0) { + Node* n = worklist.pop(); + visited <<= n->_idx; + assert(C->node_arena()->contains(n), "dead node"); + for (uint j = 0; j < n->req(); j++) { + Node* in = n->in(j); + if (in != NULL) { + assert(C->node_arena()->contains(in), "dead node"); + if (!visited.test(in->_idx)) { + worklist.push(in); + } + } + } + } +} +#endif + + +//---------------------------match--------------------------------------------- +void Matcher::match( ) { + // One-time initialization of some register masks. + init_spill_mask( C->root()->in(1) ); + _return_addr_mask = return_addr(); +#ifdef _LP64 + // Pointers take 2 slots in 64-bit land + _return_addr_mask.Insert(OptoReg::add(return_addr(),1)); +#endif + + // Map a Java-signature return type into return register-value + // machine registers for 0, 1 and 2 returned values. + const TypeTuple *range = C->tf()->range(); + if( range->cnt() > TypeFunc::Parms ) { // If not a void function + // Get ideal-register return type + int ireg = base2reg[range->field_at(TypeFunc::Parms)->base()]; + // Get machine return register + uint sop = C->start()->Opcode(); + OptoRegPair regs = return_value(ireg, false); + + // And mask for same + _return_value_mask = RegMask(regs.first()); + if( OptoReg::is_valid(regs.second()) ) + _return_value_mask.Insert(regs.second()); + } + + // --------------- + // Frame Layout + + // Need the method signature to determine the incoming argument types, + // because the types determine which registers the incoming arguments are + // in, and this affects the matched code. + const TypeTuple *domain = C->tf()->domain(); + uint argcnt = domain->cnt() - TypeFunc::Parms; + BasicType *sig_bt = NEW_RESOURCE_ARRAY( BasicType, argcnt ); + VMRegPair *vm_parm_regs = NEW_RESOURCE_ARRAY( VMRegPair, argcnt ); + _parm_regs = NEW_RESOURCE_ARRAY( OptoRegPair, argcnt ); + _calling_convention_mask = NEW_RESOURCE_ARRAY( RegMask, argcnt ); + uint i; + for( i = 0; ifield_at(i+TypeFunc::Parms)->basic_type(); + } + + // Pass array of ideal registers and length to USER code (from the AD file) + // that will convert this to an array of register numbers. + const StartNode *start = C->start(); + start->calling_convention( sig_bt, vm_parm_regs, argcnt ); +#ifdef ASSERT + // Sanity check users' calling convention. Real handy while trying to + // get the initial port correct. + { for (uint i = 0; iis_valid() && !vm_parm_regs[i].second()->is_valid() ) { + assert(domain->field_at(i+TypeFunc::Parms)==Type::HALF, "only allowed on halve" ); + _parm_regs[i].set_bad(); + continue; + } + VMReg parm_reg = vm_parm_regs[i].first(); + assert(parm_reg->is_valid(), "invalid arg?"); + if (parm_reg->is_reg()) { + OptoReg::Name opto_parm_reg = OptoReg::as_OptoReg(parm_reg); + assert(can_be_java_arg(opto_parm_reg) || + C->stub_function() == CAST_FROM_FN_PTR(address, OptoRuntime::rethrow_C) || + opto_parm_reg == inline_cache_reg(), + "parameters in register must be preserved by runtime stubs"); + } + for (uint j = 0; j < i; j++) { + assert(parm_reg != vm_parm_regs[j].first(), + "calling conv. must produce distinct regs"); + } + } + } +#endif + + // Do some initial frame layout. + + // Compute the old incoming SP (may be called FP) as + // OptoReg::stack0() + locks + in_preserve_stack_slots + pad2. + _old_SP = C->compute_old_SP(); + assert( is_even(_old_SP), "must be even" ); + + // Compute highest incoming stack argument as + // _old_SP + out_preserve_stack_slots + incoming argument size. + _in_arg_limit = OptoReg::add(_old_SP, C->out_preserve_stack_slots()); + assert( is_even(_in_arg_limit), "out_preserve must be even" ); + for( i = 0; i < argcnt; i++ ) { + // Permit args to have no register + _calling_convention_mask[i].Clear(); + if( !vm_parm_regs[i].first()->is_valid() && !vm_parm_regs[i].second()->is_valid() ) { + continue; + } + // calling_convention returns stack arguments as a count of + // slots beyond OptoReg::stack0()/VMRegImpl::stack0. We need to convert this to + // the allocators point of view, taking into account all the + // preserve area, locks & pad2. + + OptoReg::Name reg1 = warp_incoming_stk_arg(vm_parm_regs[i].first()); + if( OptoReg::is_valid(reg1)) + _calling_convention_mask[i].Insert(reg1); + + OptoReg::Name reg2 = warp_incoming_stk_arg(vm_parm_regs[i].second()); + if( OptoReg::is_valid(reg2)) + _calling_convention_mask[i].Insert(reg2); + + // Saved biased stack-slot register number + _parm_regs[i].set_pair(reg2, reg1); + } + + // Finally, make sure the incoming arguments take up an even number of + // words, in case the arguments or locals need to contain doubleword stack + // slots. The rest of the system assumes that stack slot pairs (in + // particular, in the spill area) which look aligned will in fact be + // aligned relative to the stack pointer in the target machine. Double + // stack slots will always be allocated aligned. + _new_SP = OptoReg::Name(round_to(_in_arg_limit, RegMask::SlotsPerLong)); + + // Compute highest outgoing stack argument as + // _new_SP + out_preserve_stack_slots + max(outgoing argument size). + _out_arg_limit = OptoReg::add(_new_SP, C->out_preserve_stack_slots()); + assert( is_even(_out_arg_limit), "out_preserve must be even" ); + + if (!RegMask::can_represent(OptoReg::add(_out_arg_limit,-1))) { + // the compiler cannot represent this method's calling sequence + C->record_method_not_compilable("must be able to represent all call arguments in reg mask"); + } + + if (C->failing()) return; // bailed out on incoming arg failure + + // --------------- + // Collect roots of matcher trees. Every node for which + // _shared[_idx] is cleared is guaranteed to not be shared, and thus + // can be a valid interior of some tree. + find_shared( C->root() ); + find_shared( C->top() ); + + C->print_method("Before Matching", 2); + + // Swap out to old-space; emptying new-space + Arena *old = C->node_arena()->move_contents(C->old_arena()); + + // Save debug and profile information for nodes in old space: + _old_node_note_array = C->node_note_array(); + if (_old_node_note_array != NULL) { + C->set_node_note_array(new(C->comp_arena()) GrowableArray + (C->comp_arena(), _old_node_note_array->length(), + 0, NULL)); + } + + // Pre-size the new_node table to avoid the need for range checks. + grow_new_node_array(C->unique()); + + // Reset node counter so MachNodes start with _idx at 0 + int nodes = C->unique(); // save value + C->set_unique(0); + + // Recursively match trees from old space into new space. + // Correct leaves of new-space Nodes; they point to old-space. + _visited.Clear(); // Clear visit bits for xform call + C->set_cached_top_node(xform( C->top(), nodes )); + if (!C->failing()) { + Node* xroot = xform( C->root(), 1 ); + if (xroot == NULL) { + Matcher::soft_match_failure(); // recursive matching process failed + C->record_method_not_compilable("instruction match failed"); + } else { + // During matching shared constants were attached to C->root() + // because xroot wasn't available yet, so transfer the uses to + // the xroot. + for( DUIterator_Fast jmax, j = C->root()->fast_outs(jmax); j < jmax; j++ ) { + Node* n = C->root()->fast_out(j); + if (C->node_arena()->contains(n)) { + assert(n->in(0) == C->root(), "should be control user"); + n->set_req(0, xroot); + --j; + --jmax; + } + } + + C->set_root(xroot->is_Root() ? xroot->as_Root() : NULL); +#ifdef ASSERT + verify_new_nodes_only(xroot); +#endif + } + } + if (C->top() == NULL || C->root() == NULL) { + C->record_method_not_compilable("graph lost"); // %%% cannot happen? + } + if (C->failing()) { + // delete old; + old->destruct_contents(); + return; + } + assert( C->top(), "" ); + assert( C->root(), "" ); + validate_null_checks(); + + // Now smoke old-space + NOT_DEBUG( old->destruct_contents() ); + + // ------------------------ + // Set up save-on-entry registers + Fixup_Save_On_Entry( ); +} + + +//------------------------------Fixup_Save_On_Entry---------------------------- +// The stated purpose of this routine is to take care of save-on-entry +// registers. However, the overall goal of the Match phase is to convert into +// machine-specific instructions which have RegMasks to guide allocation. +// So what this procedure really does is put a valid RegMask on each input +// to the machine-specific variations of all Return, TailCall and Halt +// instructions. It also adds edgs to define the save-on-entry values (and of +// course gives them a mask). + +static RegMask *init_input_masks( uint size, RegMask &ret_adr, RegMask &fp ) { + RegMask *rms = NEW_RESOURCE_ARRAY( RegMask, size ); + // Do all the pre-defined register masks + rms[TypeFunc::Control ] = RegMask::Empty; + rms[TypeFunc::I_O ] = RegMask::Empty; + rms[TypeFunc::Memory ] = RegMask::Empty; + rms[TypeFunc::ReturnAdr] = ret_adr; + rms[TypeFunc::FramePtr ] = fp; + return rms; +} + +//---------------------------init_first_stack_mask----------------------------- +// Create the initial stack mask used by values spilling to the stack. +// Disallow any debug info in outgoing argument areas by setting the +// initial mask accordingly. +void Matcher::init_first_stack_mask() { + + // Allocate storage for spill masks as masks for the appropriate load type. + RegMask *rms = (RegMask*)C->comp_arena()->Amalloc_D(sizeof(RegMask)*10); + idealreg2spillmask[Op_RegI] = &rms[0]; + idealreg2spillmask[Op_RegL] = &rms[1]; + idealreg2spillmask[Op_RegF] = &rms[2]; + idealreg2spillmask[Op_RegD] = &rms[3]; + idealreg2spillmask[Op_RegP] = &rms[4]; + idealreg2debugmask[Op_RegI] = &rms[5]; + idealreg2debugmask[Op_RegL] = &rms[6]; + idealreg2debugmask[Op_RegF] = &rms[7]; + idealreg2debugmask[Op_RegD] = &rms[8]; + idealreg2debugmask[Op_RegP] = &rms[9]; + + OptoReg::Name i; + + // At first, start with the empty mask + C->FIRST_STACK_mask().Clear(); + + // Add in the incoming argument area + OptoReg::Name init = OptoReg::add(_old_SP, C->out_preserve_stack_slots()); + for (i = init; i < _in_arg_limit; i = OptoReg::add(i,1)) + C->FIRST_STACK_mask().Insert(i); + + // Add in all bits past the outgoing argument area + guarantee(RegMask::can_represent(OptoReg::add(_out_arg_limit,-1)), + "must be able to represent all call arguments in reg mask"); + init = _out_arg_limit; + for (i = init; RegMask::can_represent(i); i = OptoReg::add(i,1)) + C->FIRST_STACK_mask().Insert(i); + + // Finally, set the "infinite stack" bit. + C->FIRST_STACK_mask().set_AllStack(); + + // Make spill masks. Registers for their class, plus FIRST_STACK_mask. + *idealreg2spillmask[Op_RegI] = *idealreg2regmask[Op_RegI]; + idealreg2spillmask[Op_RegI]->OR(C->FIRST_STACK_mask()); + *idealreg2spillmask[Op_RegL] = *idealreg2regmask[Op_RegL]; + idealreg2spillmask[Op_RegL]->OR(C->FIRST_STACK_mask()); + *idealreg2spillmask[Op_RegF] = *idealreg2regmask[Op_RegF]; + idealreg2spillmask[Op_RegF]->OR(C->FIRST_STACK_mask()); + *idealreg2spillmask[Op_RegD] = *idealreg2regmask[Op_RegD]; + idealreg2spillmask[Op_RegD]->OR(C->FIRST_STACK_mask()); + *idealreg2spillmask[Op_RegP] = *idealreg2regmask[Op_RegP]; + idealreg2spillmask[Op_RegP]->OR(C->FIRST_STACK_mask()); + + // Make up debug masks. Any spill slot plus callee-save registers. + // Caller-save registers are assumed to be trashable by the various + // inline-cache fixup routines. + *idealreg2debugmask[Op_RegI]= *idealreg2spillmask[Op_RegI]; + *idealreg2debugmask[Op_RegL]= *idealreg2spillmask[Op_RegL]; + *idealreg2debugmask[Op_RegF]= *idealreg2spillmask[Op_RegF]; + *idealreg2debugmask[Op_RegD]= *idealreg2spillmask[Op_RegD]; + *idealreg2debugmask[Op_RegP]= *idealreg2spillmask[Op_RegP]; + + // Prevent stub compilations from attempting to reference + // callee-saved registers from debug info + bool exclude_soe = !Compile::current()->is_method_compilation(); + + for( i=OptoReg::Name(0); iRemove(i); // Exclude save-on-call + idealreg2debugmask[Op_RegL]->Remove(i); // registers from debug + idealreg2debugmask[Op_RegF]->Remove(i); // masks + idealreg2debugmask[Op_RegD]->Remove(i); + idealreg2debugmask[Op_RegP]->Remove(i); + } + } +} + +//---------------------------is_save_on_entry---------------------------------- +bool Matcher::is_save_on_entry( int reg ) { + return + _register_save_policy[reg] == 'E' || + _register_save_policy[reg] == 'A' || // Save-on-entry register? + // Also save argument registers in the trampolining stubs + (C->save_argument_registers() && is_spillable_arg(reg)); +} + +//---------------------------Fixup_Save_On_Entry------------------------------- +void Matcher::Fixup_Save_On_Entry( ) { + init_first_stack_mask(); + + Node *root = C->root(); // Short name for root + // Count number of save-on-entry registers. + uint soe_cnt = number_of_saved_registers(); + uint i; + + // Find the procedure Start Node + StartNode *start = C->start(); + assert( start, "Expect a start node" ); + + // Save argument registers in the trampolining stubs + if( C->save_argument_registers() ) + for( i = 0; i < _last_Mach_Reg; i++ ) + if( is_spillable_arg(i) ) + soe_cnt++; + + // Input RegMask array shared by all Returns. + // The type for doubles and longs has a count of 2, but + // there is only 1 returned value + uint ret_edge_cnt = TypeFunc::Parms + ((C->tf()->range()->cnt() == TypeFunc::Parms) ? 0 : 1); + RegMask *ret_rms = init_input_masks( ret_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask ); + // Returns have 0 or 1 returned values depending on call signature. + // Return register is specified by return_value in the AD file. + if (ret_edge_cnt > TypeFunc::Parms) + ret_rms[TypeFunc::Parms+0] = _return_value_mask; + + // Input RegMask array shared by all Rethrows. + uint reth_edge_cnt = TypeFunc::Parms+1; + RegMask *reth_rms = init_input_masks( reth_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask ); + // Rethrow takes exception oop only, but in the argument 0 slot. + reth_rms[TypeFunc::Parms] = mreg2regmask[find_receiver(false)]; +#ifdef _LP64 + // Need two slots for ptrs in 64-bit land + reth_rms[TypeFunc::Parms].Insert(OptoReg::add(OptoReg::Name(find_receiver(false)),1)); +#endif + + // Input RegMask array shared by all TailCalls + uint tail_call_edge_cnt = TypeFunc::Parms+2; + RegMask *tail_call_rms = init_input_masks( tail_call_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask ); + + // Input RegMask array shared by all TailJumps + uint tail_jump_edge_cnt = TypeFunc::Parms+2; + RegMask *tail_jump_rms = init_input_masks( tail_jump_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask ); + + // TailCalls have 2 returned values (target & moop), whose masks come + // from the usual MachNode/MachOper mechanism. Find a sample + // TailCall to extract these masks and put the correct masks into + // the tail_call_rms array. + for( i=1; i < root->req(); i++ ) { + MachReturnNode *m = root->in(i)->as_MachReturn(); + if( m->ideal_Opcode() == Op_TailCall ) { + tail_call_rms[TypeFunc::Parms+0] = m->MachNode::in_RegMask(TypeFunc::Parms+0); + tail_call_rms[TypeFunc::Parms+1] = m->MachNode::in_RegMask(TypeFunc::Parms+1); + break; + } + } + + // TailJumps have 2 returned values (target & ex_oop), whose masks come + // from the usual MachNode/MachOper mechanism. Find a sample + // TailJump to extract these masks and put the correct masks into + // the tail_jump_rms array. + for( i=1; i < root->req(); i++ ) { + MachReturnNode *m = root->in(i)->as_MachReturn(); + if( m->ideal_Opcode() == Op_TailJump ) { + tail_jump_rms[TypeFunc::Parms+0] = m->MachNode::in_RegMask(TypeFunc::Parms+0); + tail_jump_rms[TypeFunc::Parms+1] = m->MachNode::in_RegMask(TypeFunc::Parms+1); + break; + } + } + + // Input RegMask array shared by all Halts + uint halt_edge_cnt = TypeFunc::Parms; + RegMask *halt_rms = init_input_masks( halt_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask ); + + // Capture the return input masks into each exit flavor + for( i=1; i < root->req(); i++ ) { + MachReturnNode *exit = root->in(i)->as_MachReturn(); + switch( exit->ideal_Opcode() ) { + case Op_Return : exit->_in_rms = ret_rms; break; + case Op_Rethrow : exit->_in_rms = reth_rms; break; + case Op_TailCall : exit->_in_rms = tail_call_rms; break; + case Op_TailJump : exit->_in_rms = tail_jump_rms; break; + case Op_Halt : exit->_in_rms = halt_rms; break; + default : ShouldNotReachHere(); + } + } + + // Next unused projection number from Start. + int proj_cnt = C->tf()->domain()->cnt(); + + // Do all the save-on-entry registers. Make projections from Start for + // them, and give them a use at the exit points. To the allocator, they + // look like incoming register arguments. + for( i = 0; i < _last_Mach_Reg; i++ ) { + if( is_save_on_entry(i) ) { + + // Add the save-on-entry to the mask array + ret_rms [ ret_edge_cnt] = mreg2regmask[i]; + reth_rms [ reth_edge_cnt] = mreg2regmask[i]; + tail_call_rms[tail_call_edge_cnt] = mreg2regmask[i]; + tail_jump_rms[tail_jump_edge_cnt] = mreg2regmask[i]; + // Halts need the SOE registers, but only in the stack as debug info. + // A just-prior uncommon-trap or deoptimization will use the SOE regs. + halt_rms [ halt_edge_cnt] = *idealreg2spillmask[_register_save_type[i]]; + + Node *mproj; + + // Is this a RegF low half of a RegD? Double up 2 adjacent RegF's + // into a single RegD. + if( (i&1) == 0 && + _register_save_type[i ] == Op_RegF && + _register_save_type[i+1] == Op_RegF && + is_save_on_entry(i+1) ) { + // Add other bit for double + ret_rms [ ret_edge_cnt].Insert(OptoReg::Name(i+1)); + reth_rms [ reth_edge_cnt].Insert(OptoReg::Name(i+1)); + tail_call_rms[tail_call_edge_cnt].Insert(OptoReg::Name(i+1)); + tail_jump_rms[tail_jump_edge_cnt].Insert(OptoReg::Name(i+1)); + halt_rms [ halt_edge_cnt].Insert(OptoReg::Name(i+1)); + mproj = new (C, 1) MachProjNode( start, proj_cnt, ret_rms[ret_edge_cnt], Op_RegD ); + proj_cnt += 2; // Skip 2 for doubles + } + else if( (i&1) == 1 && // Else check for high half of double + _register_save_type[i-1] == Op_RegF && + _register_save_type[i ] == Op_RegF && + is_save_on_entry(i-1) ) { + ret_rms [ ret_edge_cnt] = RegMask::Empty; + reth_rms [ reth_edge_cnt] = RegMask::Empty; + tail_call_rms[tail_call_edge_cnt] = RegMask::Empty; + tail_jump_rms[tail_jump_edge_cnt] = RegMask::Empty; + halt_rms [ halt_edge_cnt] = RegMask::Empty; + mproj = C->top(); + } + // Is this a RegI low half of a RegL? Double up 2 adjacent RegI's + // into a single RegL. + else if( (i&1) == 0 && + _register_save_type[i ] == Op_RegI && + _register_save_type[i+1] == Op_RegI && + is_save_on_entry(i+1) ) { + // Add other bit for long + ret_rms [ ret_edge_cnt].Insert(OptoReg::Name(i+1)); + reth_rms [ reth_edge_cnt].Insert(OptoReg::Name(i+1)); + tail_call_rms[tail_call_edge_cnt].Insert(OptoReg::Name(i+1)); + tail_jump_rms[tail_jump_edge_cnt].Insert(OptoReg::Name(i+1)); + halt_rms [ halt_edge_cnt].Insert(OptoReg::Name(i+1)); + mproj = new (C, 1) MachProjNode( start, proj_cnt, ret_rms[ret_edge_cnt], Op_RegL ); + proj_cnt += 2; // Skip 2 for longs + } + else if( (i&1) == 1 && // Else check for high half of long + _register_save_type[i-1] == Op_RegI && + _register_save_type[i ] == Op_RegI && + is_save_on_entry(i-1) ) { + ret_rms [ ret_edge_cnt] = RegMask::Empty; + reth_rms [ reth_edge_cnt] = RegMask::Empty; + tail_call_rms[tail_call_edge_cnt] = RegMask::Empty; + tail_jump_rms[tail_jump_edge_cnt] = RegMask::Empty; + halt_rms [ halt_edge_cnt] = RegMask::Empty; + mproj = C->top(); + } else { + // Make a projection for it off the Start + mproj = new (C, 1) MachProjNode( start, proj_cnt++, ret_rms[ret_edge_cnt], _register_save_type[i] ); + } + + ret_edge_cnt ++; + reth_edge_cnt ++; + tail_call_edge_cnt ++; + tail_jump_edge_cnt ++; + halt_edge_cnt ++; + + // Add a use of the SOE register to all exit paths + for( uint j=1; j < root->req(); j++ ) + root->in(j)->add_req(mproj); + } // End of if a save-on-entry register + } // End of for all machine registers +} + +//------------------------------init_spill_mask-------------------------------- +void Matcher::init_spill_mask( Node *ret ) { + if( idealreg2regmask[Op_RegI] ) return; // One time only init + + OptoReg::c_frame_pointer = c_frame_pointer(); + c_frame_ptr_mask = c_frame_pointer(); +#ifdef _LP64 + // pointers are twice as big + c_frame_ptr_mask.Insert(OptoReg::add(c_frame_pointer(),1)); +#endif + + // Start at OptoReg::stack0() + STACK_ONLY_mask.Clear(); + OptoReg::Name init = OptoReg::stack2reg(0); + // STACK_ONLY_mask is all stack bits + OptoReg::Name i; + for (i = init; RegMask::can_represent(i); i = OptoReg::add(i,1)) + STACK_ONLY_mask.Insert(i); + // Also set the "infinite stack" bit. + STACK_ONLY_mask.set_AllStack(); + + // Copy the register names over into the shared world + for( i=OptoReg::Name(0); iin(TypeFunc::FramePtr); + Node *mem = ret->in(TypeFunc::Memory); + const TypePtr* atp = TypePtr::BOTTOM; + // Share frame pointer while making spill ops + set_shared(fp); + + // Compute generic short-offset Loads + MachNode *spillI = match_tree(new (C, 3) LoadINode(NULL,mem,fp,atp)); + MachNode *spillL = match_tree(new (C, 3) LoadLNode(NULL,mem,fp,atp)); + MachNode *spillF = match_tree(new (C, 3) LoadFNode(NULL,mem,fp,atp)); + MachNode *spillD = match_tree(new (C, 3) LoadDNode(NULL,mem,fp,atp)); + MachNode *spillP = match_tree(new (C, 3) LoadPNode(NULL,mem,fp,atp,TypeInstPtr::BOTTOM)); + assert(spillI != NULL && spillL != NULL && spillF != NULL && + spillD != NULL && spillP != NULL, ""); + + // Get the ADLC notion of the right regmask, for each basic type. + idealreg2regmask[Op_RegI] = &spillI->out_RegMask(); + idealreg2regmask[Op_RegL] = &spillL->out_RegMask(); + idealreg2regmask[Op_RegF] = &spillF->out_RegMask(); + idealreg2regmask[Op_RegD] = &spillD->out_RegMask(); + idealreg2regmask[Op_RegP] = &spillP->out_RegMask(); +} + +#ifdef ASSERT +static void match_alias_type(Compile* C, Node* n, Node* m) { + if (!VerifyAliases) return; // do not go looking for trouble by default + const TypePtr* nat = n->adr_type(); + const TypePtr* mat = m->adr_type(); + int nidx = C->get_alias_index(nat); + int midx = C->get_alias_index(mat); + // Detune the assert for cases like (AndI 0xFF (LoadB p)). + if (nidx == Compile::AliasIdxTop && midx >= Compile::AliasIdxRaw) { + for (uint i = 1; i < n->req(); i++) { + Node* n1 = n->in(i); + const TypePtr* n1at = n1->adr_type(); + if (n1at != NULL) { + nat = n1at; + nidx = C->get_alias_index(n1at); + } + } + } + // %%% Kludgery. Instead, fix ideal adr_type methods for all these cases: + if (nidx == Compile::AliasIdxTop && midx == Compile::AliasIdxRaw) { + switch (n->Opcode()) { + case Op_PrefetchRead: + case Op_PrefetchWrite: + nidx = Compile::AliasIdxRaw; + nat = TypeRawPtr::BOTTOM; + break; + } + } + if (nidx == Compile::AliasIdxRaw && midx == Compile::AliasIdxTop) { + switch (n->Opcode()) { + case Op_ClearArray: + midx = Compile::AliasIdxRaw; + mat = TypeRawPtr::BOTTOM; + break; + } + } + if (nidx == Compile::AliasIdxTop && midx == Compile::AliasIdxBot) { + switch (n->Opcode()) { + case Op_Return: + case Op_Rethrow: + case Op_Halt: + case Op_TailCall: + case Op_TailJump: + nidx = Compile::AliasIdxBot; + nat = TypePtr::BOTTOM; + break; + } + } + if (nidx == Compile::AliasIdxBot && midx == Compile::AliasIdxTop) { + switch (n->Opcode()) { + case Op_StrComp: + case Op_MemBarVolatile: + case Op_MemBarCPUOrder: // %%% these ideals should have narrower adr_type? + nidx = Compile::AliasIdxTop; + nat = NULL; + break; + } + } + if (nidx != midx) { + if (PrintOpto || (PrintMiscellaneous && (WizardMode || Verbose))) { + tty->print_cr("==== Matcher alias shift %d => %d", nidx, midx); + n->dump(); + m->dump(); + } + assert(C->subsume_loads() && C->must_alias(nat, midx), + "must not lose alias info when matching"); + } +} +#endif + + +//------------------------------MStack----------------------------------------- +// State and MStack class used in xform() and find_shared() iterative methods. +enum Node_State { Pre_Visit, // node has to be pre-visited + Visit, // visit node + Post_Visit, // post-visit node + Alt_Post_Visit // alternative post-visit path + }; + +class MStack: public Node_Stack { + public: + MStack(int size) : Node_Stack(size) { } + + void push(Node *n, Node_State ns) { + Node_Stack::push(n, (uint)ns); + } + void push(Node *n, Node_State ns, Node *parent, int indx) { + ++_inode_top; + if ((_inode_top + 1) >= _inode_max) grow(); + _inode_top->node = parent; + _inode_top->indx = (uint)indx; + ++_inode_top; + _inode_top->node = n; + _inode_top->indx = (uint)ns; + } + Node *parent() { + pop(); + return node(); + } + Node_State state() const { + return (Node_State)index(); + } + void set_state(Node_State ns) { + set_index((uint)ns); + } +}; + + +//------------------------------xform------------------------------------------ +// Given a Node in old-space, Match him (Label/Reduce) to produce a machine +// Node in new-space. Given a new-space Node, recursively walk his children. +Node *Matcher::transform( Node *n ) { ShouldNotCallThis(); return n; } +Node *Matcher::xform( Node *n, int max_stack ) { + // Use one stack to keep both: child's node/state and parent's node/index + MStack mstack(max_stack * 2 * 2); // C->unique() * 2 * 2 + mstack.push(n, Visit, NULL, -1); // set NULL as parent to indicate root + + while (mstack.is_nonempty()) { + n = mstack.node(); // Leave node on stack + Node_State nstate = mstack.state(); + if (nstate == Visit) { + mstack.set_state(Post_Visit); + Node *oldn = n; + // Old-space or new-space check + if (!C->node_arena()->contains(n)) { + // Old space! + Node* m; + if (has_new_node(n)) { // Not yet Label/Reduced + m = new_node(n); + } else { + if (!is_dontcare(n)) { // Matcher can match this guy + // Calls match special. They match alone with no children. + // Their children, the incoming arguments, match normally. + m = n->is_SafePoint() ? match_sfpt(n->as_SafePoint()):match_tree(n); + if (C->failing()) return NULL; + if (m == NULL) { Matcher::soft_match_failure(); return NULL; } + } else { // Nothing the matcher cares about + if( n->is_Proj() && n->in(0)->is_Multi()) { // Projections? + // Convert to machine-dependent projection + m = n->in(0)->as_Multi()->match( n->as_Proj(), this ); + if (m->in(0) != NULL) // m might be top + collect_null_checks(m); + } else { // Else just a regular 'ol guy + m = n->clone(); // So just clone into new-space + // Def-Use edges will be added incrementally as Uses + // of this node are matched. + assert(m->outcnt() == 0, "no Uses of this clone yet"); + } + } + + set_new_node(n, m); // Map old to new + if (_old_node_note_array != NULL) { + Node_Notes* nn = C->locate_node_notes(_old_node_note_array, + n->_idx); + C->set_node_notes_at(m->_idx, nn); + } + debug_only(match_alias_type(C, n, m)); + } + n = m; // n is now a new-space node + mstack.set_node(n); + } + + // New space! + if (_visited.test_set(n->_idx)) continue; // while(mstack.is_nonempty()) + + int i; + // Put precedence edges on stack first (match them last). + for (i = oldn->req(); (uint)i < oldn->len(); i++) { + Node *m = oldn->in(i); + if (m == NULL) break; + // set -1 to call add_prec() instead of set_req() during Step1 + mstack.push(m, Visit, n, -1); + } + + // For constant debug info, I'd rather have unmatched constants. + int cnt = n->req(); + JVMState* jvms = n->jvms(); + int debug_cnt = jvms ? jvms->debug_start() : cnt; + + // Now do only debug info. Clone constants rather than matching. + // Constants are represented directly in the debug info without + // the need for executable machine instructions. + // Monitor boxes are also represented directly. + for (i = cnt - 1; i >= debug_cnt; --i) { // For all debug inputs do + Node *m = n->in(i); // Get input + int op = m->Opcode(); + assert((op == Op_BoxLock) == jvms->is_monitor_use(i), "boxes only at monitor sites"); + if( op == Op_ConI || op == Op_ConP || + op == Op_ConF || op == Op_ConD || op == Op_ConL + // || op == Op_BoxLock // %%%% enable this and remove (+++) in chaitin.cpp + ) { + m = m->clone(); + mstack.push(m, Post_Visit, n, i); // Don't neet to visit + mstack.push(m->in(0), Visit, m, 0); + } else { + mstack.push(m, Visit, n, i); + } + } + + // And now walk his children, and convert his inputs to new-space. + for( ; i >= 0; --i ) { // For all normal inputs do + Node *m = n->in(i); // Get input + if(m != NULL) + mstack.push(m, Visit, n, i); + } + + } + else if (nstate == Post_Visit) { + // Set xformed input + Node *p = mstack.parent(); + if (p != NULL) { // root doesn't have parent + int i = (int)mstack.index(); + if (i >= 0) + p->set_req(i, n); // required input + else if (i == -1) + p->add_prec(n); // precedence input + else + ShouldNotReachHere(); + } + mstack.pop(); // remove processed node from stack + } + else { + ShouldNotReachHere(); + } + } // while (mstack.is_nonempty()) + return n; // Return new-space Node +} + +//------------------------------warp_outgoing_stk_arg------------------------ +OptoReg::Name Matcher::warp_outgoing_stk_arg( VMReg reg, OptoReg::Name begin_out_arg_area, OptoReg::Name &out_arg_limit_per_call ) { + // Convert outgoing argument location to a pre-biased stack offset + if (reg->is_stack()) { + OptoReg::Name warped = reg->reg2stack(); + // Adjust the stack slot offset to be the register number used + // by the allocator. + warped = OptoReg::add(begin_out_arg_area, warped); + // Keep track of the largest numbered stack slot used for an arg. + // Largest used slot per call-site indicates the amount of stack + // that is killed by the call. + if( warped >= out_arg_limit_per_call ) + out_arg_limit_per_call = OptoReg::add(warped,1); + if (!RegMask::can_represent(warped)) { + C->record_method_not_compilable_all_tiers("unsupported calling sequence"); + return OptoReg::Bad; + } + return warped; + } + return OptoReg::as_OptoReg(reg); +} + + +//------------------------------match_sfpt------------------------------------- +// Helper function to match call instructions. Calls match special. +// They match alone with no children. Their children, the incoming +// arguments, match normally. +MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { + MachSafePointNode *msfpt = NULL; + MachCallNode *mcall = NULL; + uint cnt; + // Split out case for SafePoint vs Call + CallNode *call; + const TypeTuple *domain; + ciMethod* method = NULL; + if( sfpt->is_Call() ) { + call = sfpt->as_Call(); + domain = call->tf()->domain(); + cnt = domain->cnt(); + + // Match just the call, nothing else + MachNode *m = match_tree(call); + if (C->failing()) return NULL; + if( m == NULL ) { Matcher::soft_match_failure(); return NULL; } + + // Copy data from the Ideal SafePoint to the machine version + mcall = m->as_MachCall(); + + mcall->set_tf( call->tf()); + mcall->set_entry_point(call->entry_point()); + mcall->set_cnt( call->cnt()); + + if( mcall->is_MachCallJava() ) { + MachCallJavaNode *mcall_java = mcall->as_MachCallJava(); + const CallJavaNode *call_java = call->as_CallJava(); + method = call_java->method(); + mcall_java->_method = method; + mcall_java->_bci = call_java->_bci; + mcall_java->_optimized_virtual = call_java->is_optimized_virtual(); + if( mcall_java->is_MachCallStaticJava() ) + mcall_java->as_MachCallStaticJava()->_name = + call_java->as_CallStaticJava()->_name; + if( mcall_java->is_MachCallDynamicJava() ) + mcall_java->as_MachCallDynamicJava()->_vtable_index = + call_java->as_CallDynamicJava()->_vtable_index; + } + else if( mcall->is_MachCallRuntime() ) { + mcall->as_MachCallRuntime()->_name = call->as_CallRuntime()->_name; + } + msfpt = mcall; + } + // This is a non-call safepoint + else { + call = NULL; + domain = NULL; + MachNode *mn = match_tree(sfpt); + if (C->failing()) return NULL; + msfpt = mn->as_MachSafePoint(); + cnt = TypeFunc::Parms; + } + + // Advertise the correct memory effects (for anti-dependence computation). + msfpt->set_adr_type(sfpt->adr_type()); + + // Allocate a private array of RegMasks. These RegMasks are not shared. + msfpt->_in_rms = NEW_RESOURCE_ARRAY( RegMask, cnt ); + // Empty them all. + memset( msfpt->_in_rms, 0, sizeof(RegMask)*cnt ); + + // Do all the pre-defined non-Empty register masks + msfpt->_in_rms[TypeFunc::ReturnAdr] = _return_addr_mask; + msfpt->_in_rms[TypeFunc::FramePtr ] = c_frame_ptr_mask; + + // Place first outgoing argument can possibly be put. + OptoReg::Name begin_out_arg_area = OptoReg::add(_new_SP, C->out_preserve_stack_slots()); + assert( is_even(begin_out_arg_area), "" ); + // Compute max outgoing register number per call site. + OptoReg::Name out_arg_limit_per_call = begin_out_arg_area; + // Calls to C may hammer extra stack slots above and beyond any arguments. + // These are usually backing store for register arguments for varargs. + if( call != NULL && call->is_CallRuntime() ) + out_arg_limit_per_call = OptoReg::add(out_arg_limit_per_call,C->varargs_C_out_slots_killed()); + + + // Do the normal argument list (parameters) register masks + int argcnt = cnt - TypeFunc::Parms; + if( argcnt > 0 ) { // Skip it all if we have no args + BasicType *sig_bt = NEW_RESOURCE_ARRAY( BasicType, argcnt ); + VMRegPair *parm_regs = NEW_RESOURCE_ARRAY( VMRegPair, argcnt ); + int i; + for( i = 0; i < argcnt; i++ ) { + sig_bt[i] = domain->field_at(i+TypeFunc::Parms)->basic_type(); + } + // V-call to pick proper calling convention + call->calling_convention( sig_bt, parm_regs, argcnt ); + +#ifdef ASSERT + // Sanity check users' calling convention. Really handy during + // the initial porting effort. Fairly expensive otherwise. + { for (int i = 0; iis_valid() && + !parm_regs[i].second()->is_valid() ) continue; + VMReg reg1 = parm_regs[i].first(); + VMReg reg2 = parm_regs[i].second(); + for (int j = 0; j < i; j++) { + if( !parm_regs[j].first()->is_valid() && + !parm_regs[j].second()->is_valid() ) continue; + VMReg reg3 = parm_regs[j].first(); + VMReg reg4 = parm_regs[j].second(); + if( !reg1->is_valid() ) { + assert( !reg2->is_valid(), "valid halvsies" ); + } else if( !reg3->is_valid() ) { + assert( !reg4->is_valid(), "valid halvsies" ); + } else { + assert( reg1 != reg2, "calling conv. must produce distinct regs"); + assert( reg1 != reg3, "calling conv. must produce distinct regs"); + assert( reg1 != reg4, "calling conv. must produce distinct regs"); + assert( reg2 != reg3, "calling conv. must produce distinct regs"); + assert( reg2 != reg4 || !reg2->is_valid(), "calling conv. must produce distinct regs"); + assert( reg3 != reg4, "calling conv. must produce distinct regs"); + } + } + } + } +#endif + + // Visit each argument. Compute its outgoing register mask. + // Return results now can have 2 bits returned. + // Compute max over all outgoing arguments both per call-site + // and over the entire method. + for( i = 0; i < argcnt; i++ ) { + // Address of incoming argument mask to fill in + RegMask *rm = &mcall->_in_rms[i+TypeFunc::Parms]; + if( !parm_regs[i].first()->is_valid() && + !parm_regs[i].second()->is_valid() ) { + continue; // Avoid Halves + } + // Grab first register, adjust stack slots and insert in mask. + OptoReg::Name reg1 = warp_outgoing_stk_arg(parm_regs[i].first(), begin_out_arg_area, out_arg_limit_per_call ); + if (OptoReg::is_valid(reg1)) + rm->Insert( reg1 ); + // Grab second register (if any), adjust stack slots and insert in mask. + OptoReg::Name reg2 = warp_outgoing_stk_arg(parm_regs[i].second(), begin_out_arg_area, out_arg_limit_per_call ); + if (OptoReg::is_valid(reg2)) + rm->Insert( reg2 ); + } // End of for all arguments + + // Compute number of stack slots needed to restore stack in case of + // Pascal-style argument popping. + mcall->_argsize = out_arg_limit_per_call - begin_out_arg_area; + } + + // Compute the max stack slot killed by any call. These will not be + // available for debug info, and will be used to adjust FIRST_STACK_mask + // after all call sites have been visited. + if( _out_arg_limit < out_arg_limit_per_call) + _out_arg_limit = out_arg_limit_per_call; + + if (mcall) { + // Kill the outgoing argument area, including any non-argument holes and + // any legacy C-killed slots. Use Fat-Projections to do the killing. + // Since the max-per-method covers the max-per-call-site and debug info + // is excluded on the max-per-method basis, debug info cannot land in + // this killed area. + uint r_cnt = mcall->tf()->range()->cnt(); + MachProjNode *proj = new (C, 1) MachProjNode( mcall, r_cnt+10000, RegMask::Empty, MachProjNode::fat_proj ); + if (!RegMask::can_represent(OptoReg::Name(out_arg_limit_per_call-1))) { + C->record_method_not_compilable_all_tiers("unsupported outgoing calling sequence"); + } else { + for (int i = begin_out_arg_area; i < out_arg_limit_per_call; i++) + proj->_rout.Insert(OptoReg::Name(i)); + } + if( proj->_rout.is_NotEmpty() ) + _proj_list.push(proj); + } + // Transfer the safepoint information from the call to the mcall + // Move the JVMState list + msfpt->set_jvms(sfpt->jvms()); + for (JVMState* jvms = msfpt->jvms(); jvms; jvms = jvms->caller()) { + jvms->set_map(sfpt); + } + + // Debug inputs begin just after the last incoming parameter + assert( (mcall == NULL) || (mcall->jvms() == NULL) || + (mcall->jvms()->debug_start() + mcall->_jvmadj == mcall->tf()->domain()->cnt()), "" ); + + // Move the OopMap + msfpt->_oop_map = sfpt->_oop_map; + + // Registers killed by the call are set in the local scheduling pass + // of Global Code Motion. + return msfpt; +} + +//---------------------------match_tree---------------------------------------- +// Match a Ideal Node DAG - turn it into a tree; Label & Reduce. Used as part +// of the whole-sale conversion from Ideal to Mach Nodes. Also used for +// making GotoNodes while building the CFG and in init_spill_mask() to identify +// a Load's result RegMask for memoization in idealreg2regmask[] +MachNode *Matcher::match_tree( const Node *n ) { + assert( n->Opcode() != Op_Phi, "cannot match" ); + assert( !n->is_block_start(), "cannot match" ); + // Set the mark for all locally allocated State objects. + // When this call returns, the _states_arena arena will be reset + // freeing all State objects. + ResourceMark rm( &_states_arena ); + + LabelRootDepth = 0; + + // StoreNodes require their Memory input to match any LoadNodes + Node *mem = n->is_Store() ? n->in(MemNode::Memory) : (Node*)1 ; + + // State object for root node of match tree + // Allocate it on _states_arena - stack allocation can cause stack overflow. + State *s = new (&_states_arena) State; + s->_kids[0] = NULL; + s->_kids[1] = NULL; + s->_leaf = (Node*)n; + // Label the input tree, allocating labels from top-level arena + Label_Root( n, s, n->in(0), mem ); + if (C->failing()) return NULL; + + // The minimum cost match for the whole tree is found at the root State + uint mincost = max_juint; + uint cost = max_juint; + uint i; + for( i = 0; i < NUM_OPERANDS; i++ ) { + if( s->valid(i) && // valid entry and + s->_cost[i] < cost && // low cost and + s->_rule[i] >= NUM_OPERANDS ) // not an operand + cost = s->_cost[mincost=i]; + } + if (mincost == max_juint) { +#ifndef PRODUCT + tty->print("No matching rule for:"); + s->dump(); +#endif + Matcher::soft_match_failure(); + return NULL; + } + // Reduce input tree based upon the state labels to machine Nodes + MachNode *m = ReduceInst( s, s->_rule[mincost], mem ); +#ifdef ASSERT + _old2new_map.map(n->_idx, m); +#endif + + // Add any Matcher-ignored edges + uint cnt = n->req(); + uint start = 1; + if( mem != (Node*)1 ) start = MemNode::Memory+1; + if( n->Opcode() == Op_AddP ) { + assert( mem == (Node*)1, "" ); + start = AddPNode::Base+1; + } + for( i = start; i < cnt; i++ ) { + if( !n->match_edge(i) ) { + if( i < m->req() ) + m->ins_req( i, n->in(i) ); + else + m->add_req( n->in(i) ); + } + } + + return m; +} + + +//------------------------------match_into_reg--------------------------------- +// Choose to either match this Node in a register or part of the current +// match tree. Return true for requiring a register and false for matching +// as part of the current match tree. +static bool match_into_reg( const Node *n, Node *m, Node *control, int i, bool shared ) { + + const Type *t = m->bottom_type(); + + if( t->singleton() ) { + // Never force constants into registers. Allow them to match as + // constants or registers. Copies of the same value will share + // the same register. See find_shared_constant. + return false; + } else { // Not a constant + // Stop recursion if they have different Controls. + // Slot 0 of constants is not really a Control. + if( control && m->in(0) && control != m->in(0) ) { + + // Actually, we can live with the most conservative control we + // find, if it post-dominates the others. This allows us to + // pick up load/op/store trees where the load can float a little + // above the store. + Node *x = control; + const uint max_scan = 6; // Arbitrary scan cutoff + uint j; + for( j=0; jis_Region() ) // Bail out at merge points + return true; + x = x->in(0); + if( x == m->in(0) ) // Does 'control' post-dominate + break; // m->in(0)? If so, we can use it + } + if( j == max_scan ) // No post-domination before scan end? + return true; // Then break the match tree up + } + } + + // Not forceably cloning. If shared, put it into a register. + return shared; +} + + +//------------------------------Instruction Selection-------------------------- +// Label method walks a "tree" of nodes, using the ADLC generated DFA to match +// ideal nodes to machine instructions. Trees are delimited by shared Nodes, +// things the Matcher does not match (e.g., Memory), and things with different +// Controls (hence forced into different blocks). We pass in the Control +// selected for this entire State tree. + +// The Matcher works on Trees, but an Intel add-to-memory requires a DAG: the +// Store and the Load must have identical Memories (as well as identical +// pointers). Since the Matcher does not have anything for Memory (and +// does not handle DAGs), I have to match the Memory input myself. If the +// Tree root is a Store, I require all Loads to have the identical memory. +Node *Matcher::Label_Root( const Node *n, State *svec, Node *control, const Node *mem){ + // Since Label_Root is a recursive function, its possible that we might run + // out of stack space. See bugs 6272980 & 6227033 for more info. + LabelRootDepth++; + if (LabelRootDepth > MaxLabelRootDepth) { + C->record_method_not_compilable_all_tiers("Out of stack space, increase MaxLabelRootDepth"); + return NULL; + } + uint care = 0; // Edges matcher cares about + uint cnt = n->req(); + uint i = 0; + + // Examine children for memory state + // Can only subsume a child into your match-tree if that child's memory state + // is not modified along the path to another input. + // It is unsafe even if the other inputs are separate roots. + Node *input_mem = NULL; + for( i = 1; i < cnt; i++ ) { + if( !n->match_edge(i) ) continue; + Node *m = n->in(i); // Get ith input + assert( m, "expect non-null children" ); + if( m->is_Load() ) { + if( input_mem == NULL ) { + input_mem = m->in(MemNode::Memory); + } else if( input_mem != m->in(MemNode::Memory) ) { + input_mem = NodeSentinel; + } + } + } + + for( i = 1; i < cnt; i++ ){// For my children + if( !n->match_edge(i) ) continue; + Node *m = n->in(i); // Get ith input + // Allocate states out of a private arena + State *s = new (&_states_arena) State; + svec->_kids[care++] = s; + assert( care <= 2, "binary only for now" ); + + // Recursively label the State tree. + s->_kids[0] = NULL; + s->_kids[1] = NULL; + s->_leaf = m; + + // Check for leaves of the State Tree; things that cannot be a part of + // the current tree. If it finds any, that value is matched as a + // register operand. If not, then the normal matching is used. + if( match_into_reg(n, m, control, i, is_shared(m)) || + // + // Stop recursion if this is LoadNode and the root of this tree is a + // StoreNode and the load & store have different memories. + ((mem!=(Node*)1) && m->is_Load() && m->in(MemNode::Memory) != mem) || + // Can NOT include the match of a subtree when its memory state + // is used by any of the other subtrees + (input_mem == NodeSentinel) ) { +#ifndef PRODUCT + // Print when we exclude matching due to different memory states at input-loads + if( PrintOpto && (Verbose && WizardMode) && (input_mem == NodeSentinel) + && !((mem!=(Node*)1) && m->is_Load() && m->in(MemNode::Memory) != mem) ) { + tty->print_cr("invalid input_mem"); + } +#endif + // Switch to a register-only opcode; this value must be in a register + // and cannot be subsumed as part of a larger instruction. + s->DFA( m->ideal_reg(), m ); + + } else { + // If match tree has no control and we do, adopt it for entire tree + if( control == NULL && m->in(0) != NULL && m->req() > 1 ) + control = m->in(0); // Pick up control + // Else match as a normal part of the match tree. + control = Label_Root(m,s,control,mem); + if (C->failing()) return NULL; + } + } + + + // Call DFA to match this node, and return + svec->DFA( n->Opcode(), n ); + +#ifdef ASSERT + uint x; + for( x = 0; x < _LAST_MACH_OPER; x++ ) + if( svec->valid(x) ) + break; + + if (x >= _LAST_MACH_OPER) { + n->dump(); + svec->dump(); + assert( false, "bad AD file" ); + } +#endif + return control; +} + + +// Con nodes reduced using the same rule can share their MachNode +// which reduces the number of copies of a constant in the final +// program. The register allocator is free to split uses later to +// split live ranges. +MachNode* Matcher::find_shared_constant(Node* leaf, uint rule) { + if (!leaf->is_Con()) return NULL; + + // See if this Con has already been reduced using this rule. + if (_shared_constants.Size() <= leaf->_idx) return NULL; + MachNode* last = (MachNode*)_shared_constants.at(leaf->_idx); + if (last != NULL && rule == last->rule()) { + // Get the new space root. + Node* xroot = new_node(C->root()); + if (xroot == NULL) { + // This shouldn't happen give the order of matching. + return NULL; + } + + // Shared constants need to have their control be root so they + // can be scheduled properly. + Node* control = last->in(0); + if (control != xroot) { + if (control == NULL || control == C->root()) { + last->set_req(0, xroot); + } else { + assert(false, "unexpected control"); + return NULL; + } + } + return last; + } + return NULL; +} + + +//------------------------------ReduceInst------------------------------------- +// Reduce a State tree (with given Control) into a tree of MachNodes. +// This routine (and it's cohort ReduceOper) convert Ideal Nodes into +// complicated machine Nodes. Each MachNode covers some tree of Ideal Nodes. +// Each MachNode has a number of complicated MachOper operands; each +// MachOper also covers a further tree of Ideal Nodes. + +// The root of the Ideal match tree is always an instruction, so we enter +// the recursion here. After building the MachNode, we need to recurse +// the tree checking for these cases: +// (1) Child is an instruction - +// Build the instruction (recursively), add it as an edge. +// Build a simple operand (register) to hold the result of the instruction. +// (2) Child is an interior part of an instruction - +// Skip over it (do nothing) +// (3) Child is the start of a operand - +// Build the operand, place it inside the instruction +// Call ReduceOper. +MachNode *Matcher::ReduceInst( State *s, int rule, Node *&mem ) { + assert( rule >= NUM_OPERANDS, "called with operand rule" ); + + MachNode* shared_con = find_shared_constant(s->_leaf, rule); + if (shared_con != NULL) { + return shared_con; + } + + // Build the object to represent this state & prepare for recursive calls + MachNode *mach = s->MachNodeGenerator( rule, C ); + mach->_opnds[0] = s->MachOperGenerator( _reduceOp[rule], C ); + assert( mach->_opnds[0] != NULL, "Missing result operand" ); + Node *leaf = s->_leaf; + // Check for instruction or instruction chain rule + if( rule >= _END_INST_CHAIN_RULE || rule < _BEGIN_INST_CHAIN_RULE ) { + // Instruction + mach->add_req( leaf->in(0) ); // Set initial control + // Reduce interior of complex instruction + ReduceInst_Interior( s, rule, mem, mach, 1 ); + } else { + // Instruction chain rules are data-dependent on their inputs + mach->add_req(0); // Set initial control to none + ReduceInst_Chain_Rule( s, rule, mem, mach ); + } + + // If a Memory was used, insert a Memory edge + if( mem != (Node*)1 ) + mach->ins_req(MemNode::Memory,mem); + + // If the _leaf is an AddP, insert the base edge + if( leaf->Opcode() == Op_AddP ) + mach->ins_req(AddPNode::Base,leaf->in(AddPNode::Base)); + + uint num_proj = _proj_list.size(); + + // Perform any 1-to-many expansions required + MachNode *ex = mach->Expand(s,_proj_list); + if( ex != mach ) { + assert(ex->ideal_reg() == mach->ideal_reg(), "ideal types should match"); + if( ex->in(1)->is_Con() ) + ex->in(1)->set_req(0, C->root()); + // Remove old node from the graph + for( uint i=0; ireq(); i++ ) { + mach->set_req(i,NULL); + } + } + + // PhaseChaitin::fixup_spills will sometimes generate spill code + // via the matcher. By the time, nodes have been wired into the CFG, + // and any further nodes generated by expand rules will be left hanging + // in space, and will not get emitted as output code. Catch this. + // Also, catch any new register allocation constraints ("projections") + // generated belatedly during spill code generation. + if (_allocation_started) { + guarantee(ex == mach, "no expand rules during spill generation"); + guarantee(_proj_list.size() == num_proj, "no allocation during spill generation"); + } + + if (leaf->is_Con()) { + // Record the con for sharing + _shared_constants.map(leaf->_idx, ex); + } + + return ex; +} + +void Matcher::ReduceInst_Chain_Rule( State *s, int rule, Node *&mem, MachNode *mach ) { + // 'op' is what I am expecting to receive + int op = _leftOp[rule]; + // Operand type to catch childs result + // This is what my child will give me. + int opnd_class_instance = s->_rule[op]; + // Choose between operand class or not. + // This is what I will recieve. + int catch_op = (FIRST_OPERAND_CLASS <= op && op < NUM_OPERANDS) ? opnd_class_instance : op; + // New rule for child. Chase operand classes to get the actual rule. + int newrule = s->_rule[catch_op]; + + if( newrule < NUM_OPERANDS ) { + // Chain from operand or operand class, may be output of shared node + assert( 0 <= opnd_class_instance && opnd_class_instance < NUM_OPERANDS, + "Bad AD file: Instruction chain rule must chain from operand"); + // Insert operand into array of operands for this instruction + mach->_opnds[1] = s->MachOperGenerator( opnd_class_instance, C ); + + ReduceOper( s, newrule, mem, mach ); + } else { + // Chain from the result of an instruction + assert( newrule >= _LAST_MACH_OPER, "Do NOT chain from internal operand"); + mach->_opnds[1] = s->MachOperGenerator( _reduceOp[catch_op], C ); + Node *mem1 = (Node*)1; + mach->add_req( ReduceInst(s, newrule, mem1) ); + } + return; +} + + +uint Matcher::ReduceInst_Interior( State *s, int rule, Node *&mem, MachNode *mach, uint num_opnds ) { + if( s->_leaf->is_Load() ) { + Node *mem2 = s->_leaf->in(MemNode::Memory); + assert( mem == (Node*)1 || mem == mem2, "multiple Memories being matched at once?" ); + mem = mem2; + } + if( s->_leaf->in(0) != NULL && s->_leaf->req() > 1) { + if( mach->in(0) == NULL ) + mach->set_req(0, s->_leaf->in(0)); + } + + // Now recursively walk the state tree & add operand list. + for( uint i=0; i<2; i++ ) { // binary tree + State *newstate = s->_kids[i]; + if( newstate == NULL ) break; // Might only have 1 child + // 'op' is what I am expecting to receive + int op; + if( i == 0 ) { + op = _leftOp[rule]; + } else { + op = _rightOp[rule]; + } + // Operand type to catch childs result + // This is what my child will give me. + int opnd_class_instance = newstate->_rule[op]; + // Choose between operand class or not. + // This is what I will receive. + int catch_op = (op >= FIRST_OPERAND_CLASS && op < NUM_OPERANDS) ? opnd_class_instance : op; + // New rule for child. Chase operand classes to get the actual rule. + int newrule = newstate->_rule[catch_op]; + + if( newrule < NUM_OPERANDS ) { // Operand/operandClass or internalOp/instruction? + // Operand/operandClass + // Insert operand into array of operands for this instruction + mach->_opnds[num_opnds++] = newstate->MachOperGenerator( opnd_class_instance, C ); + ReduceOper( newstate, newrule, mem, mach ); + + } else { // Child is internal operand or new instruction + if( newrule < _LAST_MACH_OPER ) { // internal operand or instruction? + // internal operand --> call ReduceInst_Interior + // Interior of complex instruction. Do nothing but recurse. + num_opnds = ReduceInst_Interior( newstate, newrule, mem, mach, num_opnds ); + } else { + // instruction --> call build operand( ) to catch result + // --> ReduceInst( newrule ) + mach->_opnds[num_opnds++] = s->MachOperGenerator( _reduceOp[catch_op], C ); + Node *mem1 = (Node*)1; + mach->add_req( ReduceInst( newstate, newrule, mem1 ) ); + } + } + assert( mach->_opnds[num_opnds-1], "" ); + } + return num_opnds; +} + +// This routine walks the interior of possible complex operands. +// At each point we check our children in the match tree: +// (1) No children - +// We are a leaf; add _leaf field as an input to the MachNode +// (2) Child is an internal operand - +// Skip over it ( do nothing ) +// (3) Child is an instruction - +// Call ReduceInst recursively and +// and instruction as an input to the MachNode +void Matcher::ReduceOper( State *s, int rule, Node *&mem, MachNode *mach ) { + assert( rule < _LAST_MACH_OPER, "called with operand rule" ); + State *kid = s->_kids[0]; + assert( kid == NULL || s->_leaf->in(0) == NULL, "internal operands have no control" ); + + // Leaf? And not subsumed? + if( kid == NULL && !_swallowed[rule] ) { + mach->add_req( s->_leaf ); // Add leaf pointer + return; // Bail out + } + + if( s->_leaf->is_Load() ) { + assert( mem == (Node*)1, "multiple Memories being matched at once?" ); + mem = s->_leaf->in(MemNode::Memory); + } + if( s->_leaf->in(0) && s->_leaf->req() > 1) { + if( !mach->in(0) ) + mach->set_req(0,s->_leaf->in(0)); + else { + assert( s->_leaf->in(0) == mach->in(0), "same instruction, differing controls?" ); + } + } + + for( uint i=0; kid != NULL && i<2; kid = s->_kids[1], i++ ) { // binary tree + int newrule; + if( i == 0 ) + newrule = kid->_rule[_leftOp[rule]]; + else + newrule = kid->_rule[_rightOp[rule]]; + + if( newrule < _LAST_MACH_OPER ) { // Operand or instruction? + // Internal operand; recurse but do nothing else + ReduceOper( kid, newrule, mem, mach ); + + } else { // Child is a new instruction + // Reduce the instruction, and add a direct pointer from this + // machine instruction to the newly reduced one. + Node *mem1 = (Node*)1; + mach->add_req( ReduceInst( kid, newrule, mem1 ) ); + } + } +} + + +// ------------------------------------------------------------------------- +// Java-Java calling convention +// (what you use when Java calls Java) + +//------------------------------find_receiver---------------------------------- +// For a given signature, return the OptoReg for parameter 0. +OptoReg::Name Matcher::find_receiver( bool is_outgoing ) { + VMRegPair regs; + BasicType sig_bt = T_OBJECT; + calling_convention(&sig_bt, ®s, 1, is_outgoing); + // Return argument 0 register. In the LP64 build pointers + // take 2 registers, but the VM wants only the 'main' name. + return OptoReg::as_OptoReg(regs.first()); +} + +// A method-klass-holder may be passed in the inline_cache_reg +// and then expanded into the inline_cache_reg and a method_oop register +// defined in ad_.cpp + + +//------------------------------find_shared------------------------------------ +// Set bits if Node is shared or otherwise a root +void Matcher::find_shared( Node *n ) { + // Allocate stack of size C->unique() * 2 to avoid frequent realloc + MStack mstack(C->unique() * 2); + mstack.push(n, Visit); // Don't need to pre-visit root node + while (mstack.is_nonempty()) { + n = mstack.node(); // Leave node on stack + Node_State nstate = mstack.state(); + if (nstate == Pre_Visit) { + if (is_visited(n)) { // Visited already? + // Node is shared and has no reason to clone. Flag it as shared. + // This causes it to match into a register for the sharing. + set_shared(n); // Flag as shared and + mstack.pop(); // remove node from stack + continue; + } + nstate = Visit; // Not already visited; so visit now + } + if (nstate == Visit) { + mstack.set_state(Post_Visit); + set_visited(n); // Flag as visited now + bool mem_op = false; + + switch( n->Opcode() ) { // Handle some opcodes special + case Op_Phi: // Treat Phis as shared roots + case Op_Parm: + case Op_Proj: // All handled specially during matching + set_shared(n); + set_dontcare(n); + break; + case Op_If: + case Op_CountedLoopEnd: + mstack.set_state(Alt_Post_Visit); // Alternative way + // Convert (If (Bool (CmpX A B))) into (If (Bool) (CmpX A B)). Helps + // with matching cmp/branch in 1 instruction. The Matcher needs the + // Bool and CmpX side-by-side, because it can only get at constants + // that are at the leaves of Match trees, and the Bool's condition acts + // as a constant here. + mstack.push(n->in(1), Visit); // Clone the Bool + mstack.push(n->in(0), Pre_Visit); // Visit control input + continue; // while (mstack.is_nonempty()) + case Op_ConvI2D: // These forms efficiently match with a prior + case Op_ConvI2F: // Load but not a following Store + if( n->in(1)->is_Load() && // Prior load + n->outcnt() == 1 && // Not already shared + n->unique_out()->is_Store() ) // Following store + set_shared(n); // Force it to be a root + break; + case Op_ReverseBytesI: + case Op_ReverseBytesL: + if( n->in(1)->is_Load() && // Prior load + n->outcnt() == 1 ) // Not already shared + set_shared(n); // Force it to be a root + break; + case Op_BoxLock: // Cant match until we get stack-regs in ADLC + case Op_IfFalse: + case Op_IfTrue: + case Op_MachProj: + case Op_MergeMem: + case Op_Catch: + case Op_CatchProj: + case Op_CProj: + case Op_JumpProj: + case Op_JProj: + case Op_NeverBranch: + set_dontcare(n); + break; + case Op_Jump: + mstack.push(n->in(1), Visit); // Switch Value + mstack.push(n->in(0), Pre_Visit); // Visit Control input + continue; // while (mstack.is_nonempty()) + case Op_StrComp: + set_shared(n); // Force result into register (it will be anyways) + break; + case Op_ConP: { // Convert pointers above the centerline to NUL + TypeNode *tn = n->as_Type(); // Constants derive from type nodes + const TypePtr* tp = tn->type()->is_ptr(); + if (tp->_ptr == TypePtr::AnyNull) { + tn->set_type(TypePtr::NULL_PTR); + } + break; + } + case Op_Binary: // These are introduced in the Post_Visit state. + ShouldNotReachHere(); + break; + case Op_StoreB: // Do match these, despite no ideal reg + case Op_StoreC: + case Op_StoreCM: + case Op_StoreD: + case Op_StoreF: + case Op_StoreI: + case Op_StoreL: + case Op_StoreP: + case Op_Store16B: + case Op_Store8B: + case Op_Store4B: + case Op_Store8C: + case Op_Store4C: + case Op_Store2C: + case Op_Store4I: + case Op_Store2I: + case Op_Store2L: + case Op_Store4F: + case Op_Store2F: + case Op_Store2D: + case Op_ClearArray: + case Op_SafePoint: + mem_op = true; + break; + case Op_LoadB: + case Op_LoadC: + case Op_LoadD: + case Op_LoadF: + case Op_LoadI: + case Op_LoadKlass: + case Op_LoadL: + case Op_LoadS: + case Op_LoadP: + case Op_LoadRange: + case Op_LoadD_unaligned: + case Op_LoadL_unaligned: + case Op_Load16B: + case Op_Load8B: + case Op_Load4B: + case Op_Load4C: + case Op_Load2C: + case Op_Load8C: + case Op_Load8S: + case Op_Load4S: + case Op_Load2S: + case Op_Load4I: + case Op_Load2I: + case Op_Load2L: + case Op_Load4F: + case Op_Load2F: + case Op_Load2D: + mem_op = true; + // Must be root of match tree due to prior load conflict + if( C->subsume_loads() == false ) { + set_shared(n); + } + // Fall into default case + default: + if( !n->ideal_reg() ) + set_dontcare(n); // Unmatchable Nodes + } // end_switch + + for(int i = n->req() - 1; i >= 0; --i) { // For my children + Node *m = n->in(i); // Get ith input + if (m == NULL) continue; // Ignore NULLs + uint mop = m->Opcode(); + + // Must clone all producers of flags, or we will not match correctly. + // Suppose a compare setting int-flags is shared (e.g., a switch-tree) + // then it will match into an ideal Op_RegFlags. Alas, the fp-flags + // are also there, so we may match a float-branch to int-flags and + // expect the allocator to haul the flags from the int-side to the + // fp-side. No can do. + if( _must_clone[mop] ) { + mstack.push(m, Visit); + continue; // for(int i = ...) + } + + // Clone addressing expressions as they are "free" in most instructions + if( mem_op && i == MemNode::Address && mop == Op_AddP ) { + Node *off = m->in(AddPNode::Offset); + if( off->is_Con() ) { + set_visited(m); // Flag as visited now + Node *adr = m->in(AddPNode::Address); + + // Intel, ARM and friends can handle 2 adds in addressing mode + if( clone_shift_expressions && adr->Opcode() == Op_AddP && + // AtomicAdd is not an addressing expression. + // Cheap to find it by looking for screwy base. + !adr->in(AddPNode::Base)->is_top() ) { + set_visited(adr); // Flag as visited now + Node *shift = adr->in(AddPNode::Offset); + // Check for shift by small constant as well + if( shift->Opcode() == Op_LShiftX && shift->in(2)->is_Con() && + shift->in(2)->get_int() <= 3 ) { + set_visited(shift); // Flag as visited now + mstack.push(shift->in(2), Visit); +#ifdef _LP64 + // Allow Matcher to match the rule which bypass + // ConvI2L operation for an array index on LP64 + // if the index value is positive. + if( shift->in(1)->Opcode() == Op_ConvI2L && + shift->in(1)->as_Type()->type()->is_long()->_lo >= 0 ) { + set_visited(shift->in(1)); // Flag as visited now + mstack.push(shift->in(1)->in(1), Pre_Visit); + } else +#endif + mstack.push(shift->in(1), Pre_Visit); + } else { + mstack.push(shift, Pre_Visit); + } + mstack.push(adr->in(AddPNode::Address), Pre_Visit); + mstack.push(adr->in(AddPNode::Base), Pre_Visit); + } else { // Sparc, Alpha, PPC and friends + mstack.push(adr, Pre_Visit); + } + + // Clone X+offset as it also folds into most addressing expressions + mstack.push(off, Visit); + mstack.push(m->in(AddPNode::Base), Pre_Visit); + continue; // for(int i = ...) + } // if( off->is_Con() ) + } // if( mem_op && + mstack.push(m, Pre_Visit); + } // for(int i = ...) + } + else if (nstate == Alt_Post_Visit) { + mstack.pop(); // Remove node from stack + // We cannot remove the Cmp input from the Bool here, as the Bool may be + // shared and all users of the Bool need to move the Cmp in parallel. + // This leaves both the Bool and the If pointing at the Cmp. To + // prevent the Matcher from trying to Match the Cmp along both paths + // BoolNode::match_edge always returns a zero. + + // We reorder the Op_If in a pre-order manner, so we can visit without + // accidently sharing the Cmp (the Bool and the If make 2 users). + n->add_req( n->in(1)->in(1) ); // Add the Cmp next to the Bool + } + else if (nstate == Post_Visit) { + mstack.pop(); // Remove node from stack + + // Now hack a few special opcodes + switch( n->Opcode() ) { // Handle some opcodes special + case Op_StorePConditional: + case Op_StoreLConditional: + case Op_CompareAndSwapI: + case Op_CompareAndSwapL: + case Op_CompareAndSwapP: { // Convert trinary to binary-tree + Node *newval = n->in(MemNode::ValueIn ); + Node *oldval = n->in(LoadStoreNode::ExpectedIn); + Node *pair = new (C, 3) BinaryNode( oldval, newval ); + n->set_req(MemNode::ValueIn,pair); + n->del_req(LoadStoreNode::ExpectedIn); + break; + } + case Op_CMoveD: // Convert trinary to binary-tree + case Op_CMoveF: + case Op_CMoveI: + case Op_CMoveL: + case Op_CMoveP: { + // Restructure into a binary tree for Matching. It's possible that + // we could move this code up next to the graph reshaping for IfNodes + // or vice-versa, but I do not want to debug this for Ladybird. + // 10/2/2000 CNC. + Node *pair1 = new (C, 3) BinaryNode(n->in(1),n->in(1)->in(1)); + n->set_req(1,pair1); + Node *pair2 = new (C, 3) BinaryNode(n->in(2),n->in(3)); + n->set_req(2,pair2); + n->del_req(3); + break; + } + default: + break; + } + } + else { + ShouldNotReachHere(); + } + } // end of while (mstack.is_nonempty()) +} + +#ifdef ASSERT +// machine-independent root to machine-dependent root +void Matcher::dump_old2new_map() { + _old2new_map.dump(); +} +#endif + +//---------------------------collect_null_checks------------------------------- +// Find null checks in the ideal graph; write a machine-specific node for +// it. Used by later implicit-null-check handling. Actually collects +// either an IfTrue or IfFalse for the common NOT-null path, AND the ideal +// value being tested. +void Matcher::collect_null_checks( Node *proj ) { + Node *iff = proj->in(0); + if( iff->Opcode() == Op_If ) { + // During matching If's have Bool & Cmp side-by-side + BoolNode *b = iff->in(1)->as_Bool(); + Node *cmp = iff->in(2); + if( cmp->Opcode() == Op_CmpP ) { + if( cmp->in(2)->bottom_type() == TypePtr::NULL_PTR ) { + + if( proj->Opcode() == Op_IfTrue ) { + extern int all_null_checks_found; + all_null_checks_found++; + if( b->_test._test == BoolTest::ne ) { + _null_check_tests.push(proj); + _null_check_tests.push(cmp->in(1)); + } + } else { + assert( proj->Opcode() == Op_IfFalse, "" ); + if( b->_test._test == BoolTest::eq ) { + _null_check_tests.push(proj); + _null_check_tests.push(cmp->in(1)); + } + } + } + } + } +} + +//---------------------------validate_null_checks------------------------------ +// Its possible that the value being NULL checked is not the root of a match +// tree. If so, I cannot use the value in an implicit null check. +void Matcher::validate_null_checks( ) { + uint cnt = _null_check_tests.size(); + for( uint i=0; i < cnt; i+=2 ) { + Node *test = _null_check_tests[i]; + Node *val = _null_check_tests[i+1]; + if (has_new_node(val)) { + // Is a match-tree root, so replace with the matched value + _null_check_tests.map(i+1, new_node(val)); + } else { + // Yank from candidate list + _null_check_tests.map(i+1,_null_check_tests[--cnt]); + _null_check_tests.map(i,_null_check_tests[--cnt]); + _null_check_tests.pop(); + _null_check_tests.pop(); + i-=2; + } + } +} + + +// Used by the DFA in dfa_sparc.cpp. Check for a prior FastLock +// acting as an Acquire and thus we don't need an Acquire here. We +// retain the Node to act as a compiler ordering barrier. +bool Matcher::prior_fast_lock( const Node *acq ) { + Node *r = acq->in(0); + if( !r->is_Region() || r->req() <= 1 ) return false; + Node *proj = r->in(1); + if( !proj->is_Proj() ) return false; + Node *call = proj->in(0); + if( !call->is_Call() || call->as_Call()->entry_point() != OptoRuntime::complete_monitor_locking_Java() ) + return false; + + return true; +} + +// Used by the DFA in dfa_sparc.cpp. Check for a following FastUnLock +// acting as a Release and thus we don't need a Release here. We +// retain the Node to act as a compiler ordering barrier. +bool Matcher::post_fast_unlock( const Node *rel ) { + Compile *C = Compile::current(); + assert( rel->Opcode() == Op_MemBarRelease, "" ); + const MemBarReleaseNode *mem = (const MemBarReleaseNode*)rel; + DUIterator_Fast imax, i = mem->fast_outs(imax); + Node *ctrl = NULL; + while( true ) { + ctrl = mem->fast_out(i); // Throw out-of-bounds if proj not found + assert( ctrl->is_Proj(), "only projections here" ); + ProjNode *proj = (ProjNode*)ctrl; + if( proj->_con == TypeFunc::Control && + !C->node_arena()->contains(ctrl) ) // Unmatched old-space only + break; + i++; + } + Node *iff = NULL; + for( DUIterator_Fast jmax, j = ctrl->fast_outs(jmax); j < jmax; j++ ) { + Node *x = ctrl->fast_out(j); + if( x->is_If() && x->req() > 1 && + !C->node_arena()->contains(x) ) { // Unmatched old-space only + iff = x; + break; + } + } + if( !iff ) return false; + Node *bol = iff->in(1); + // The iff might be some random subclass of If or bol might be Con-Top + if (!bol->is_Bool()) return false; + assert( bol->req() > 1, "" ); + return (bol->in(1)->Opcode() == Op_FastUnlock); +} + +// Used by the DFA in dfa_xxx.cpp. Check for a following barrier or +// atomic instruction acting as a store_load barrier without any +// intervening volatile load, and thus we don't need a barrier here. +// We retain the Node to act as a compiler ordering barrier. +bool Matcher::post_store_load_barrier(const Node *vmb) { + Compile *C = Compile::current(); + assert( vmb->is_MemBar(), "" ); + assert( vmb->Opcode() != Op_MemBarAcquire, "" ); + const MemBarNode *mem = (const MemBarNode*)vmb; + + // Get the Proj node, ctrl, that can be used to iterate forward + Node *ctrl = NULL; + DUIterator_Fast imax, i = mem->fast_outs(imax); + while( true ) { + ctrl = mem->fast_out(i); // Throw out-of-bounds if proj not found + assert( ctrl->is_Proj(), "only projections here" ); + ProjNode *proj = (ProjNode*)ctrl; + if( proj->_con == TypeFunc::Control && + !C->node_arena()->contains(ctrl) ) // Unmatched old-space only + break; + i++; + } + + for( DUIterator_Fast jmax, j = ctrl->fast_outs(jmax); j < jmax; j++ ) { + Node *x = ctrl->fast_out(j); + int xop = x->Opcode(); + + // We don't need current barrier if we see another or a lock + // before seeing volatile load. + // + // Op_Fastunlock previously appeared in the Op_* list below. + // With the advent of 1-0 lock operations we're no longer guaranteed + // that a monitor exit operation contains a serializing instruction. + + if (xop == Op_MemBarVolatile || + xop == Op_FastLock || + xop == Op_CompareAndSwapL || + xop == Op_CompareAndSwapP || + xop == Op_CompareAndSwapI) + return true; + + if (x->is_MemBar()) { + // We must retain this membar if there is an upcoming volatile + // load, which will be preceded by acquire membar. + if (xop == Op_MemBarAcquire) + return false; + // For other kinds of barriers, check by pretending we + // are them, and seeing if we can be removed. + else + return post_store_load_barrier((const MemBarNode*)x); + } + + // Delicate code to detect case of an upcoming fastlock block + if( x->is_If() && x->req() > 1 && + !C->node_arena()->contains(x) ) { // Unmatched old-space only + Node *iff = x; + Node *bol = iff->in(1); + // The iff might be some random subclass of If or bol might be Con-Top + if (!bol->is_Bool()) return false; + assert( bol->req() > 1, "" ); + return (bol->in(1)->Opcode() == Op_FastUnlock); + } + // probably not necessary to check for these + if (x->is_Call() || x->is_SafePoint() || x->is_block_proj()) + return false; + } + return false; +} + +//============================================================================= +//---------------------------State--------------------------------------------- +State::State(void) { +#ifdef ASSERT + _id = 0; + _kids[0] = _kids[1] = (State*)(intptr_t) CONST64(0xcafebabecafebabe); + _leaf = (Node*)(intptr_t) CONST64(0xbaadf00dbaadf00d); + //memset(_cost, -1, sizeof(_cost)); + //memset(_rule, -1, sizeof(_rule)); +#endif + memset(_valid, 0, sizeof(_valid)); +} + +#ifdef ASSERT +State::~State() { + _id = 99; + _kids[0] = _kids[1] = (State*)(intptr_t) CONST64(0xcafebabecafebabe); + _leaf = (Node*)(intptr_t) CONST64(0xbaadf00dbaadf00d); + memset(_cost, -3, sizeof(_cost)); + memset(_rule, -3, sizeof(_rule)); +} +#endif + +#ifndef PRODUCT +//---------------------------dump---------------------------------------------- +void State::dump() { + tty->print("\n"); + dump(0); +} + +void State::dump(int depth) { + for( int j = 0; j < depth; j++ ) + tty->print(" "); + tty->print("--N: "); + _leaf->dump(); + uint i; + for( i = 0; i < _LAST_MACH_OPER; i++ ) + // Check for valid entry + if( valid(i) ) { + for( int j = 0; j < depth; j++ ) + tty->print(" "); + assert(_cost[i] != max_juint, "cost must be a valid value"); + assert(_rule[i] < _last_Mach_Node, "rule[i] must be valid rule"); + tty->print_cr("%s %d %s", + ruleName[i], _cost[i], ruleName[_rule[i]] ); + } + tty->print_cr(""); + + for( i=0; i<2; i++ ) + if( _kids[i] ) + _kids[i]->dump(depth+1); +} +#endif diff --git a/hotspot/src/share/vm/opto/matcher.hpp b/hotspot/src/share/vm/opto/matcher.hpp new file mode 100644 index 00000000000..a33c4e92da2 --- /dev/null +++ b/hotspot/src/share/vm/opto/matcher.hpp @@ -0,0 +1,392 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Compile; +class Node; +class MachNode; +class MachTypeNode; +class MachOper; + +//---------------------------Matcher------------------------------------------- +class Matcher : public PhaseTransform { + friend class VMStructs; + // Private arena of State objects + ResourceArea _states_arena; + + VectorSet _visited; // Visit bits + + // Used to control the Label pass + VectorSet _shared; // Shared Ideal Node + VectorSet _dontcare; // Nothing the matcher cares about + + // Private methods which perform the actual matching and reduction + // Walks the label tree, generating machine nodes + MachNode *ReduceInst( State *s, int rule, Node *&mem); + void ReduceInst_Chain_Rule( State *s, int rule, Node *&mem, MachNode *mach); + uint ReduceInst_Interior(State *s, int rule, Node *&mem, MachNode *mach, uint num_opnds); + void ReduceOper( State *s, int newrule, Node *&mem, MachNode *mach ); + + // If this node already matched using "rule", return the MachNode for it. + MachNode* find_shared_constant(Node* con, uint rule); + + // Convert a dense opcode number to an expanded rule number + const int *_reduceOp; + const int *_leftOp; + const int *_rightOp; + + // Map dense opcode number to info on when rule is swallowed constant. + const bool *_swallowed; + + // Map dense rule number to determine if this is an instruction chain rule + const uint _begin_inst_chain_rule; + const uint _end_inst_chain_rule; + + // We want to clone constants and possible CmpI-variants. + // If we do not clone CmpI, then we can have many instances of + // condition codes alive at once. This is OK on some chips and + // bad on others. Hence the machine-dependent table lookup. + const char *_must_clone; + + // Find shared Nodes, or Nodes that otherwise are Matcher roots + void find_shared( Node *n ); + + // Debug and profile information for nodes in old space: + GrowableArray* _old_node_note_array; + + // Node labeling iterator for instruction selection + Node *Label_Root( const Node *n, State *svec, Node *control, const Node *mem ); + + Node *transform( Node *dummy ); + + Node_List &_proj_list; // For Machine nodes killing many values + + Node_Array _shared_constants; + + debug_only(Node_Array _old2new_map;) // Map roots of ideal-trees to machine-roots + + // Accessors for the inherited field PhaseTransform::_nodes: + void grow_new_node_array(uint idx_limit) { + _nodes.map(idx_limit-1, NULL); + } + bool has_new_node(const Node* n) const { + return _nodes.at(n->_idx) != NULL; + } + Node* new_node(const Node* n) const { + assert(has_new_node(n), "set before get"); + return _nodes.at(n->_idx); + } + void set_new_node(const Node* n, Node *nn) { + assert(!has_new_node(n), "set only once"); + _nodes.map(n->_idx, nn); + } + +#ifdef ASSERT + // Make sure only new nodes are reachable from this node + void verify_new_nodes_only(Node* root); +#endif + +public: + int LabelRootDepth; + static const int base2reg[]; // Map Types to machine register types + // Convert ideal machine register to a register mask for spill-loads + static const RegMask *idealreg2regmask[]; + RegMask *idealreg2spillmask[_last_machine_leaf]; + RegMask *idealreg2debugmask[_last_machine_leaf]; + void init_spill_mask( Node *ret ); + // Convert machine register number to register mask + static uint mreg2regmask_max; + static RegMask mreg2regmask[]; + static RegMask STACK_ONLY_mask; + + bool is_shared( Node *n ) { return _shared.test(n->_idx) != 0; } + void set_shared( Node *n ) { _shared.set(n->_idx); } + bool is_visited( Node *n ) { return _visited.test(n->_idx) != 0; } + void set_visited( Node *n ) { _visited.set(n->_idx); } + bool is_dontcare( Node *n ) { return _dontcare.test(n->_idx) != 0; } + void set_dontcare( Node *n ) { _dontcare.set(n->_idx); } + + // Mode bit to tell DFA and expand rules whether we are running after + // (or during) register selection. Usually, the matcher runs before, + // but it will also get called to generate post-allocation spill code. + // In this situation, it is a deadly error to attempt to allocate more + // temporary registers. + bool _allocation_started; + + // Machine register names + static const char *regName[]; + // Machine register encodings + static const unsigned char _regEncode[]; + // Machine Node names + const char **_ruleName; + // Rules that are cheaper to rematerialize than to spill + static const uint _begin_rematerialize; + static const uint _end_rematerialize; + + // An array of chars, from 0 to _last_Mach_Reg. + // No Save = 'N' (for register windows) + // Save on Entry = 'E' + // Save on Call = 'C' + // Always Save = 'A' (same as SOE + SOC) + const char *_register_save_policy; + const char *_c_reg_save_policy; + // Convert a machine register to a machine register type, so-as to + // properly match spill code. + const int *_register_save_type; + // Maps from machine register to boolean; true if machine register can + // be holding a call argument in some signature. + static bool can_be_java_arg( int reg ); + // Maps from machine register to boolean; true if machine register holds + // a spillable argument. + static bool is_spillable_arg( int reg ); + + // List of IfFalse or IfTrue Nodes that indicate a taken null test. + // List is valid in the post-matching space. + Node_List _null_check_tests; + void collect_null_checks( Node *proj ); + void validate_null_checks( ); + + Matcher( Node_List &proj_list ); + + // Select instructions for entire method + void match( ); + // Helper for match + OptoReg::Name warp_incoming_stk_arg( VMReg reg ); + + // Transform, then walk. Does implicit DCE while walking. + // Name changed from "transform" to avoid it being virtual. + Node *xform( Node *old_space_node, int Nodes ); + + // Match a single Ideal Node - turn it into a 1-Node tree; Label & Reduce. + MachNode *match_tree( const Node *n ); + MachNode *match_sfpt( SafePointNode *sfpt ); + // Helper for match_sfpt + OptoReg::Name warp_outgoing_stk_arg( VMReg reg, OptoReg::Name begin_out_arg_area, OptoReg::Name &out_arg_limit_per_call ); + + // Initialize first stack mask and related masks. + void init_first_stack_mask(); + + // If we should save-on-entry this register + bool is_save_on_entry( int reg ); + + // Fixup the save-on-entry registers + void Fixup_Save_On_Entry( ); + + // --- Frame handling --- + + // Register number of the stack slot corresponding to the incoming SP. + // Per the Big Picture in the AD file, it is: + // SharedInfo::stack0 + locks + in_preserve_stack_slots + pad2. + OptoReg::Name _old_SP; + + // Register number of the stack slot corresponding to the highest incoming + // argument on the stack. Per the Big Picture in the AD file, it is: + // _old_SP + out_preserve_stack_slots + incoming argument size. + OptoReg::Name _in_arg_limit; + + // Register number of the stack slot corresponding to the new SP. + // Per the Big Picture in the AD file, it is: + // _in_arg_limit + pad0 + OptoReg::Name _new_SP; + + // Register number of the stack slot corresponding to the highest outgoing + // argument on the stack. Per the Big Picture in the AD file, it is: + // _new_SP + max outgoing arguments of all calls + OptoReg::Name _out_arg_limit; + + OptoRegPair *_parm_regs; // Array of machine registers per argument + RegMask *_calling_convention_mask; // Array of RegMasks per argument + + // Does matcher support this ideal node? + static const bool has_match_rule(int opcode); + static const bool _hasMatchRule[_last_opcode]; + + // Used to determine if we have fast l2f conversion + // USII has it, USIII doesn't + static const bool convL2FSupported(void); + + // Vector width in bytes + static const uint vector_width_in_bytes(void); + + // Vector ideal reg + static const uint vector_ideal_reg(void); + + // Used to determine a "low complexity" 64-bit constant. (Zero is simple.) + // The standard of comparison is one (StoreL ConL) vs. two (StoreI ConI). + // Depends on the details of 64-bit constant generation on the CPU. + static const bool isSimpleConstant64(jlong con); + + // These calls are all generated by the ADLC + + // TRUE - grows up, FALSE - grows down (Intel) + virtual bool stack_direction() const; + + // Java-Java calling convention + // (what you use when Java calls Java) + + // Alignment of stack in bytes, standard Intel word alignment is 4. + // Sparc probably wants at least double-word (8). + static uint stack_alignment_in_bytes(); + // Alignment of stack, measured in stack slots. + // The size of stack slots is defined by VMRegImpl::stack_slot_size. + static uint stack_alignment_in_slots() { + return stack_alignment_in_bytes() / (VMRegImpl::stack_slot_size); + } + + // Array mapping arguments to registers. Argument 0 is usually the 'this' + // pointer. Registers can include stack-slots and regular registers. + static void calling_convention( BasicType *, VMRegPair *, uint len, bool is_outgoing ); + + // Convert a sig into a calling convention register layout + // and find interesting things about it. + static OptoReg::Name find_receiver( bool is_outgoing ); + // Return address register. On Intel it is a stack-slot. On PowerPC + // it is the Link register. On Sparc it is r31? + virtual OptoReg::Name return_addr() const; + RegMask _return_addr_mask; + // Return value register. On Intel it is EAX. On Sparc i0/o0. + static OptoRegPair return_value(int ideal_reg, bool is_outgoing); + static OptoRegPair c_return_value(int ideal_reg, bool is_outgoing); + RegMask _return_value_mask; + // Inline Cache Register + static OptoReg::Name inline_cache_reg(); + static const RegMask &inline_cache_reg_mask(); + static int inline_cache_reg_encode(); + + // Register for DIVI projection of divmodI + static RegMask divI_proj_mask(); + // Register for MODI projection of divmodI + static RegMask modI_proj_mask(); + + // Register for DIVL projection of divmodL + static RegMask divL_proj_mask(); + // Register for MODL projection of divmodL + static RegMask modL_proj_mask(); + + // Java-Interpreter calling convention + // (what you use when calling between compiled-Java and Interpreted-Java + + // Number of callee-save + always-save registers + // Ignores frame pointer and "special" registers + static int number_of_saved_registers(); + + // The Method-klass-holder may be passed in the inline_cache_reg + // and then expanded into the inline_cache_reg and a method_oop register + + static OptoReg::Name interpreter_method_oop_reg(); + static const RegMask &interpreter_method_oop_reg_mask(); + static int interpreter_method_oop_reg_encode(); + + static OptoReg::Name compiler_method_oop_reg(); + static const RegMask &compiler_method_oop_reg_mask(); + static int compiler_method_oop_reg_encode(); + + // Interpreter's Frame Pointer Register + static OptoReg::Name interpreter_frame_pointer_reg(); + static const RegMask &interpreter_frame_pointer_reg_mask(); + + // Java-Native calling convention + // (what you use when intercalling between Java and C++ code) + + // Array mapping arguments to registers. Argument 0 is usually the 'this' + // pointer. Registers can include stack-slots and regular registers. + static void c_calling_convention( BasicType*, VMRegPair *, uint ); + // Frame pointer. The frame pointer is kept at the base of the stack + // and so is probably the stack pointer for most machines. On Intel + // it is ESP. On the PowerPC it is R1. On Sparc it is SP. + OptoReg::Name c_frame_pointer() const; + static RegMask c_frame_ptr_mask; + + // !!!!! Special stuff for building ScopeDescs + virtual int regnum_to_fpu_offset(int regnum); + + // Is this branch offset small enough to be addressed by a short branch? + bool is_short_branch_offset(int offset); + + // Optional scaling for the parameter to the ClearArray/CopyArray node. + static const bool init_array_count_is_in_bytes; + + // Threshold small size (in bytes) for a ClearArray/CopyArray node. + // Anything this size or smaller may get converted to discrete scalar stores. + static const int init_array_short_size; + + // Should the Matcher clone shifts on addressing modes, expecting them to + // be subsumed into complex addressing expressions or compute them into + // registers? True for Intel but false for most RISCs + static const bool clone_shift_expressions; + + // Is it better to copy float constants, or load them directly from memory? + // Intel can load a float constant from a direct address, requiring no + // extra registers. Most RISCs will have to materialize an address into a + // register first, so they may as well materialize the constant immediately. + static const bool rematerialize_float_constants; + + // If CPU can load and store mis-aligned doubles directly then no fixup is + // needed. Else we split the double into 2 integer pieces and move it + // piece-by-piece. Only happens when passing doubles into C code or when + // calling i2c adapters as the Java calling convention forces doubles to be + // aligned. + static const bool misaligned_doubles_ok; + + // Perform a platform dependent implicit null fixup. This is needed + // on windows95 to take care of some unusual register constraints. + void pd_implicit_null_fixup(MachNode *load, uint idx); + + // Advertise here if the CPU requires explicit rounding operations + // to implement the UseStrictFP mode. + static const bool strict_fp_requires_explicit_rounding; + + // Do floats take an entire double register or just half? + static const bool float_in_double; + // Do ints take an entire long register or just half? + static const bool int_in_long; + + // This routine is run whenever a graph fails to match. + // If it returns, the compiler should bailout to interpreter without error. + // In non-product mode, SoftMatchFailure is false to detect non-canonical + // graphs. Print a message and exit. + static void soft_match_failure() { + if( SoftMatchFailure ) return; + else { fatal("SoftMatchFailure is not allowed except in product"); } + } + + // Used by the DFA in dfa_sparc.cpp. Check for a prior FastLock + // acting as an Acquire and thus we don't need an Acquire here. We + // retain the Node to act as a compiler ordering barrier. + static bool prior_fast_lock( const Node *acq ); + + // Used by the DFA in dfa_sparc.cpp. Check for a following + // FastUnLock acting as a Release and thus we don't need a Release + // here. We retain the Node to act as a compiler ordering barrier. + static bool post_fast_unlock( const Node *rel ); + + // Check for a following volatile memory barrier without an + // intervening load and thus we don't need a barrier here. We + // retain the Node to act as a compiler ordering barrier. + static bool post_store_load_barrier(const Node* mb); + + +#ifdef ASSERT + void dump_old2new_map(); // machine-independent to machine-dependent +#endif +}; diff --git a/hotspot/src/share/vm/opto/memnode.cpp b/hotspot/src/share/vm/opto/memnode.cpp new file mode 100644 index 00000000000..26904be5853 --- /dev/null +++ b/hotspot/src/share/vm/opto/memnode.cpp @@ -0,0 +1,3222 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_memnode.cpp.incl" + +//============================================================================= +uint MemNode::size_of() const { return sizeof(*this); } + +const TypePtr *MemNode::adr_type() const { + Node* adr = in(Address); + const TypePtr* cross_check = NULL; + DEBUG_ONLY(cross_check = _adr_type); + return calculate_adr_type(adr->bottom_type(), cross_check); +} + +#ifndef PRODUCT +void MemNode::dump_spec(outputStream *st) const { + if (in(Address) == NULL) return; // node is dead +#ifndef ASSERT + // fake the missing field + const TypePtr* _adr_type = NULL; + if (in(Address) != NULL) + _adr_type = in(Address)->bottom_type()->isa_ptr(); +#endif + dump_adr_type(this, _adr_type, st); + + Compile* C = Compile::current(); + if( C->alias_type(_adr_type)->is_volatile() ) + st->print(" Volatile!"); +} + +void MemNode::dump_adr_type(const Node* mem, const TypePtr* adr_type, outputStream *st) { + st->print(" @"); + if (adr_type == NULL) { + st->print("NULL"); + } else { + adr_type->dump_on(st); + Compile* C = Compile::current(); + Compile::AliasType* atp = NULL; + if (C->have_alias_type(adr_type)) atp = C->alias_type(adr_type); + if (atp == NULL) + st->print(", idx=?\?;"); + else if (atp->index() == Compile::AliasIdxBot) + st->print(", idx=Bot;"); + else if (atp->index() == Compile::AliasIdxTop) + st->print(", idx=Top;"); + else if (atp->index() == Compile::AliasIdxRaw) + st->print(", idx=Raw;"); + else { + ciField* field = atp->field(); + if (field) { + st->print(", name="); + field->print_name_on(st); + } + st->print(", idx=%d;", atp->index()); + } + } +} + +extern void print_alias_types(); + +#endif + +//--------------------------Ideal_common--------------------------------------- +// Look for degenerate control and memory inputs. Bypass MergeMem inputs. +// Unhook non-raw memories from complete (macro-expanded) initializations. +Node *MemNode::Ideal_common(PhaseGVN *phase, bool can_reshape) { + // If our control input is a dead region, kill all below the region + Node *ctl = in(MemNode::Control); + if (ctl && remove_dead_region(phase, can_reshape)) + return this; + + // Ignore if memory is dead, or self-loop + Node *mem = in(MemNode::Memory); + if( phase->type( mem ) == Type::TOP ) return NodeSentinel; // caller will return NULL + assert( mem != this, "dead loop in MemNode::Ideal" ); + + Node *address = in(MemNode::Address); + const Type *t_adr = phase->type( address ); + if( t_adr == Type::TOP ) return NodeSentinel; // caller will return NULL + + // Avoid independent memory operations + Node* old_mem = mem; + + if (mem->is_Proj() && mem->in(0)->is_Initialize()) { + InitializeNode* init = mem->in(0)->as_Initialize(); + if (init->is_complete()) { // i.e., after macro expansion + const TypePtr* tp = t_adr->is_ptr(); + uint alias_idx = phase->C->get_alias_index(tp); + // Free this slice from the init. It was hooked, temporarily, + // by GraphKit::set_output_for_allocation. + if (alias_idx > Compile::AliasIdxRaw) { + mem = init->memory(alias_idx); + // ...but not with the raw-pointer slice. + } + } + } + + if (mem->is_MergeMem()) { + MergeMemNode* mmem = mem->as_MergeMem(); + const TypePtr *tp = t_adr->is_ptr(); + uint alias_idx = phase->C->get_alias_index(tp); +#ifdef ASSERT + { + // Check that current type is consistent with the alias index used during graph construction + assert(alias_idx >= Compile::AliasIdxRaw, "must not be a bad alias_idx"); + const TypePtr *adr_t = adr_type(); + bool consistent = adr_t == NULL || adr_t->empty() || phase->C->must_alias(adr_t, alias_idx ); + // Sometimes dead array references collapse to a[-1], a[-2], or a[-3] + if( !consistent && adr_t != NULL && !adr_t->empty() && + tp->isa_aryptr() && tp->offset() == Type::OffsetBot && + adr_t->isa_aryptr() && adr_t->offset() != Type::OffsetBot && + ( adr_t->offset() == arrayOopDesc::length_offset_in_bytes() || + adr_t->offset() == oopDesc::klass_offset_in_bytes() || + adr_t->offset() == oopDesc::mark_offset_in_bytes() ) ) { + // don't assert if it is dead code. + consistent = true; + } + if( !consistent ) { + tty->print("alias_idx==%d, adr_type()==", alias_idx); if( adr_t == NULL ) { tty->print("NULL"); } else { adr_t->dump(); } + tty->cr(); + print_alias_types(); + assert(consistent, "adr_type must match alias idx"); + } + } +#endif + // TypeInstPtr::NOTNULL+any is an OOP with unknown offset - generally + // means an array I have not precisely typed yet. Do not do any + // alias stuff with it any time soon. + const TypeInstPtr *tinst = tp->isa_instptr(); + if( tp->base() != Type::AnyPtr && + !(tinst && + tinst->klass()->is_java_lang_Object() && + tinst->offset() == Type::OffsetBot) ) { + // compress paths and change unreachable cycles to TOP + // If not, we can update the input infinitely along a MergeMem cycle + // Equivalent code in PhiNode::Ideal + Node* m = phase->transform(mmem); + // If tranformed to a MergeMem, get the desired slice + // Otherwise the returned node represents memory for every slice + mem = (m->is_MergeMem())? m->as_MergeMem()->memory_at(alias_idx) : m; + // Update input if it is progress over what we have now + } + } + + if (mem != old_mem) { + set_req(MemNode::Memory, mem); + return this; + } + + // let the subclass continue analyzing... + return NULL; +} + +// Helper function for proving some simple control dominations. +// Attempt to prove that control input 'dom' dominates (or equals) 'sub'. +// Already assumes that 'dom' is available at 'sub', and that 'sub' +// is not a constant (dominated by the method's StartNode). +// Used by MemNode::find_previous_store to prove that the +// control input of a memory operation predates (dominates) +// an allocation it wants to look past. +bool MemNode::detect_dominating_control(Node* dom, Node* sub) { + if (dom == NULL) return false; + if (dom->is_Proj()) dom = dom->in(0); + if (dom->is_Start()) return true; // anything inside the method + if (dom->is_Root()) return true; // dom 'controls' a constant + int cnt = 20; // detect cycle or too much effort + while (sub != NULL) { // walk 'sub' up the chain to 'dom' + if (--cnt < 0) return false; // in a cycle or too complex + if (sub == dom) return true; + if (sub->is_Start()) return false; + if (sub->is_Root()) return false; + Node* up = sub->in(0); + if (sub == up && sub->is_Region()) { + for (uint i = 1; i < sub->req(); i++) { + Node* in = sub->in(i); + if (in != NULL && !in->is_top() && in != sub) { + up = in; break; // take any path on the way up to 'dom' + } + } + } + if (sub == up) return false; // some kind of tight cycle + sub = up; + } + return false; +} + +//---------------------detect_ptr_independence--------------------------------- +// Used by MemNode::find_previous_store to prove that two base +// pointers are never equal. +// The pointers are accompanied by their associated allocations, +// if any, which have been previously discovered by the caller. +bool MemNode::detect_ptr_independence(Node* p1, AllocateNode* a1, + Node* p2, AllocateNode* a2, + PhaseTransform* phase) { + // Attempt to prove that these two pointers cannot be aliased. + // They may both manifestly be allocations, and they should differ. + // Or, if they are not both allocations, they can be distinct constants. + // Otherwise, one is an allocation and the other a pre-existing value. + if (a1 == NULL && a2 == NULL) { // neither an allocation + return (p1 != p2) && p1->is_Con() && p2->is_Con(); + } else if (a1 != NULL && a2 != NULL) { // both allocations + return (a1 != a2); + } else if (a1 != NULL) { // one allocation a1 + // (Note: p2->is_Con implies p2->in(0)->is_Root, which dominates.) + return detect_dominating_control(p2->in(0), a1->in(0)); + } else { //(a2 != NULL) // one allocation a2 + return detect_dominating_control(p1->in(0), a2->in(0)); + } + return false; +} + + +// The logic for reordering loads and stores uses four steps: +// (a) Walk carefully past stores and initializations which we +// can prove are independent of this load. +// (b) Observe that the next memory state makes an exact match +// with self (load or store), and locate the relevant store. +// (c) Ensure that, if we were to wire self directly to the store, +// the optimizer would fold it up somehow. +// (d) Do the rewiring, and return, depending on some other part of +// the optimizer to fold up the load. +// This routine handles steps (a) and (b). Steps (c) and (d) are +// specific to loads and stores, so they are handled by the callers. +// (Currently, only LoadNode::Ideal has steps (c), (d). More later.) +// +Node* MemNode::find_previous_store(PhaseTransform* phase) { + Node* ctrl = in(MemNode::Control); + Node* adr = in(MemNode::Address); + intptr_t offset = 0; + Node* base = AddPNode::Ideal_base_and_offset(adr, phase, offset); + AllocateNode* alloc = AllocateNode::Ideal_allocation(base, phase); + + if (offset == Type::OffsetBot) + return NULL; // cannot unalias unless there are precise offsets + + intptr_t size_in_bytes = memory_size(); + + Node* mem = in(MemNode::Memory); // start searching here... + + int cnt = 50; // Cycle limiter + for (;;) { // While we can dance past unrelated stores... + if (--cnt < 0) break; // Caught in cycle or a complicated dance? + + if (mem->is_Store()) { + Node* st_adr = mem->in(MemNode::Address); + intptr_t st_offset = 0; + Node* st_base = AddPNode::Ideal_base_and_offset(st_adr, phase, st_offset); + if (st_base == NULL) + break; // inscrutable pointer + if (st_offset != offset && st_offset != Type::OffsetBot) { + const int MAX_STORE = BytesPerLong; + if (st_offset >= offset + size_in_bytes || + st_offset <= offset - MAX_STORE || + st_offset <= offset - mem->as_Store()->memory_size()) { + // Success: The offsets are provably independent. + // (You may ask, why not just test st_offset != offset and be done? + // The answer is that stores of different sizes can co-exist + // in the same sequence of RawMem effects. We sometimes initialize + // a whole 'tile' of array elements with a single jint or jlong.) + mem = mem->in(MemNode::Memory); + continue; // (a) advance through independent store memory + } + } + if (st_base != base && + detect_ptr_independence(base, alloc, + st_base, + AllocateNode::Ideal_allocation(st_base, phase), + phase)) { + // Success: The bases are provably independent. + mem = mem->in(MemNode::Memory); + continue; // (a) advance through independent store memory + } + + // (b) At this point, if the bases or offsets do not agree, we lose, + // since we have not managed to prove 'this' and 'mem' independent. + if (st_base == base && st_offset == offset) { + return mem; // let caller handle steps (c), (d) + } + + } else if (mem->is_Proj() && mem->in(0)->is_Initialize()) { + InitializeNode* st_init = mem->in(0)->as_Initialize(); + AllocateNode* st_alloc = st_init->allocation(); + if (st_alloc == NULL) + break; // something degenerated + bool known_identical = false; + bool known_independent = false; + if (alloc == st_alloc) + known_identical = true; + else if (alloc != NULL) + known_independent = true; + else if (ctrl != NULL && + detect_dominating_control(ctrl, st_alloc->in(0))) + known_independent = true; + + if (known_independent) { + // The bases are provably independent: Either they are + // manifestly distinct allocations, or else the control + // of this load dominates the store's allocation. + int alias_idx = phase->C->get_alias_index(adr_type()); + if (alias_idx == Compile::AliasIdxRaw) { + mem = st_alloc->in(TypeFunc::Memory); + } else { + mem = st_init->memory(alias_idx); + } + continue; // (a) advance through independent store memory + } + + // (b) at this point, if we are not looking at a store initializing + // the same allocation we are loading from, we lose. + if (known_identical) { + // From caller, can_see_stored_value will consult find_captured_store. + return mem; // let caller handle steps (c), (d) + } + + } + + // Unless there is an explicit 'continue', we must bail out here, + // because 'mem' is an inscrutable memory state (e.g., a call). + break; + } + + return NULL; // bail out +} + +//----------------------calculate_adr_type------------------------------------- +// Helper function. Notices when the given type of address hits top or bottom. +// Also, asserts a cross-check of the type against the expected address type. +const TypePtr* MemNode::calculate_adr_type(const Type* t, const TypePtr* cross_check) { + if (t == Type::TOP) return NULL; // does not touch memory any more? + #ifdef PRODUCT + cross_check = NULL; + #else + if (!VerifyAliases || is_error_reported() || Node::in_dump()) cross_check = NULL; + #endif + const TypePtr* tp = t->isa_ptr(); + if (tp == NULL) { + assert(cross_check == NULL || cross_check == TypePtr::BOTTOM, "expected memory type must be wide"); + return TypePtr::BOTTOM; // touches lots of memory + } else { + #ifdef ASSERT + // %%%% [phh] We don't check the alias index if cross_check is + // TypeRawPtr::BOTTOM. Needs to be investigated. + if (cross_check != NULL && + cross_check != TypePtr::BOTTOM && + cross_check != TypeRawPtr::BOTTOM) { + // Recheck the alias index, to see if it has changed (due to a bug). + Compile* C = Compile::current(); + assert(C->get_alias_index(cross_check) == C->get_alias_index(tp), + "must stay in the original alias category"); + // The type of the address must be contained in the adr_type, + // disregarding "null"-ness. + // (We make an exception for TypeRawPtr::BOTTOM, which is a bit bucket.) + const TypePtr* tp_notnull = tp->join(TypePtr::NOTNULL)->is_ptr(); + assert(cross_check->meet(tp_notnull) == cross_check, + "real address must not escape from expected memory type"); + } + #endif + return tp; + } +} + +//------------------------adr_phi_is_loop_invariant---------------------------- +// A helper function for Ideal_DU_postCCP to check if a Phi in a counted +// loop is loop invariant. Make a quick traversal of Phi and associated +// CastPP nodes, looking to see if they are a closed group within the loop. +bool MemNode::adr_phi_is_loop_invariant(Node* adr_phi, Node* cast) { + // The idea is that the phi-nest must boil down to only CastPP nodes + // with the same data. This implies that any path into the loop already + // includes such a CastPP, and so the original cast, whatever its input, + // must be covered by an equivalent cast, with an earlier control input. + ResourceMark rm; + + // The loop entry input of the phi should be the unique dominating + // node for every Phi/CastPP in the loop. + Unique_Node_List closure; + closure.push(adr_phi->in(LoopNode::EntryControl)); + + // Add the phi node and the cast to the worklist. + Unique_Node_List worklist; + worklist.push(adr_phi); + if( cast != NULL ){ + if( !cast->is_ConstraintCast() ) return false; + worklist.push(cast); + } + + // Begin recursive walk of phi nodes. + while( worklist.size() ){ + // Take a node off the worklist + Node *n = worklist.pop(); + if( !closure.member(n) ){ + // Add it to the closure. + closure.push(n); + // Make a sanity check to ensure we don't waste too much time here. + if( closure.size() > 20) return false; + // This node is OK if: + // - it is a cast of an identical value + // - or it is a phi node (then we add its inputs to the worklist) + // Otherwise, the node is not OK, and we presume the cast is not invariant + if( n->is_ConstraintCast() ){ + worklist.push(n->in(1)); + } else if( n->is_Phi() ) { + for( uint i = 1; i < n->req(); i++ ) { + worklist.push(n->in(i)); + } + } else { + return false; + } + } + } + + // Quit when the worklist is empty, and we've found no offending nodes. + return true; +} + +//------------------------------Ideal_DU_postCCP------------------------------- +// Find any cast-away of null-ness and keep its control. Null cast-aways are +// going away in this pass and we need to make this memory op depend on the +// gating null check. + +// I tried to leave the CastPP's in. This makes the graph more accurate in +// some sense; we get to keep around the knowledge that an oop is not-null +// after some test. Alas, the CastPP's interfere with GVN (some values are +// the regular oop, some are the CastPP of the oop, all merge at Phi's which +// cannot collapse, etc). This cost us 10% on SpecJVM, even when I removed +// some of the more trivial cases in the optimizer. Removing more useless +// Phi's started allowing Loads to illegally float above null checks. I gave +// up on this approach. CNC 10/20/2000 +Node *MemNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { + Node *ctr = in(MemNode::Control); + Node *mem = in(MemNode::Memory); + Node *adr = in(MemNode::Address); + Node *skipped_cast = NULL; + // Need a null check? Regular static accesses do not because they are + // from constant addresses. Array ops are gated by the range check (which + // always includes a NULL check). Just check field ops. + if( !ctr ) { + // Scan upwards for the highest location we can place this memory op. + while( true ) { + switch( adr->Opcode() ) { + + case Op_AddP: // No change to NULL-ness, so peek thru AddP's + adr = adr->in(AddPNode::Base); + continue; + + case Op_CastPP: + // If the CastPP is useless, just peek on through it. + if( ccp->type(adr) == ccp->type(adr->in(1)) ) { + // Remember the cast that we've peeked though. If we peek + // through more than one, then we end up remembering the highest + // one, that is, if in a loop, the one closest to the top. + skipped_cast = adr; + adr = adr->in(1); + continue; + } + // CastPP is going away in this pass! We need this memory op to be + // control-dependent on the test that is guarding the CastPP. + ccp->hash_delete(this); + set_req(MemNode::Control, adr->in(0)); + ccp->hash_insert(this); + return this; + + case Op_Phi: + // Attempt to float above a Phi to some dominating point. + if (adr->in(0) != NULL && adr->in(0)->is_CountedLoop()) { + // If we've already peeked through a Cast (which could have set the + // control), we can't float above a Phi, because the skipped Cast + // may not be loop invariant. + if (adr_phi_is_loop_invariant(adr, skipped_cast)) { + adr = adr->in(1); + continue; + } + } + + // Intentional fallthrough! + + // No obvious dominating point. The mem op is pinned below the Phi + // by the Phi itself. If the Phi goes away (no true value is merged) + // then the mem op can float, but not indefinitely. It must be pinned + // behind the controls leading to the Phi. + case Op_CheckCastPP: + // These usually stick around to change address type, however a + // useless one can be elided and we still need to pick up a control edge + if (adr->in(0) == NULL) { + // This CheckCastPP node has NO control and is likely useless. But we + // need check further up the ancestor chain for a control input to keep + // the node in place. 4959717. + skipped_cast = adr; + adr = adr->in(1); + continue; + } + ccp->hash_delete(this); + set_req(MemNode::Control, adr->in(0)); + ccp->hash_insert(this); + return this; + + // List of "safe" opcodes; those that implicitly block the memory + // op below any null check. + case Op_CastX2P: // no null checks on native pointers + case Op_Parm: // 'this' pointer is not null + case Op_LoadP: // Loading from within a klass + case Op_LoadKlass: // Loading from within a klass + case Op_ConP: // Loading from a klass + case Op_CreateEx: // Sucking up the guts of an exception oop + case Op_Con: // Reading from TLS + case Op_CMoveP: // CMoveP is pinned + break; // No progress + + case Op_Proj: // Direct call to an allocation routine + case Op_SCMemProj: // Memory state from store conditional ops +#ifdef ASSERT + { + assert(adr->as_Proj()->_con == TypeFunc::Parms, "must be return value"); + const Node* call = adr->in(0); + if (call->is_CallStaticJava()) { + const CallStaticJavaNode* call_java = call->as_CallStaticJava(); + assert(call_java && call_java->method() == NULL, "must be runtime call"); + // We further presume that this is one of + // new_instance_Java, new_array_Java, or + // the like, but do not assert for this. + } else if (call->is_Allocate()) { + // similar case to new_instance_Java, etc. + } else if (!call->is_CallLeaf()) { + // Projections from fetch_oop (OSR) are allowed as well. + ShouldNotReachHere(); + } + } +#endif + break; + default: + ShouldNotReachHere(); + } + break; + } + } + + return NULL; // No progress +} + + +//============================================================================= +uint LoadNode::size_of() const { return sizeof(*this); } +uint LoadNode::cmp( const Node &n ) const +{ return !Type::cmp( _type, ((LoadNode&)n)._type ); } +const Type *LoadNode::bottom_type() const { return _type; } +uint LoadNode::ideal_reg() const { + return Matcher::base2reg[_type->base()]; +} + +#ifndef PRODUCT +void LoadNode::dump_spec(outputStream *st) const { + MemNode::dump_spec(st); + if( !Verbose && !WizardMode ) { + // standard dump does this in Verbose and WizardMode + st->print(" #"); _type->dump_on(st); + } +} +#endif + + +//----------------------------LoadNode::make----------------------------------- +// Polymorphic factory method: +LoadNode *LoadNode::make( Compile *C, Node *ctl, Node *mem, Node *adr, const TypePtr* adr_type, const Type *rt, BasicType bt ) { + // sanity check the alias category against the created node type + assert(!(adr_type->isa_oopptr() && + adr_type->offset() == oopDesc::klass_offset_in_bytes()), + "use LoadKlassNode instead"); + assert(!(adr_type->isa_aryptr() && + adr_type->offset() == arrayOopDesc::length_offset_in_bytes()), + "use LoadRangeNode instead"); + switch (bt) { + case T_BOOLEAN: + case T_BYTE: return new (C, 3) LoadBNode(ctl, mem, adr, adr_type, rt->is_int() ); + case T_INT: return new (C, 3) LoadINode(ctl, mem, adr, adr_type, rt->is_int() ); + case T_CHAR: return new (C, 3) LoadCNode(ctl, mem, adr, adr_type, rt->is_int() ); + case T_SHORT: return new (C, 3) LoadSNode(ctl, mem, adr, adr_type, rt->is_int() ); + case T_LONG: return new (C, 3) LoadLNode(ctl, mem, adr, adr_type, rt->is_long() ); + case T_FLOAT: return new (C, 3) LoadFNode(ctl, mem, adr, adr_type, rt ); + case T_DOUBLE: return new (C, 3) LoadDNode(ctl, mem, adr, adr_type, rt ); + case T_ADDRESS: return new (C, 3) LoadPNode(ctl, mem, adr, adr_type, rt->is_ptr() ); + case T_OBJECT: return new (C, 3) LoadPNode(ctl, mem, adr, adr_type, rt->is_oopptr()); + } + ShouldNotReachHere(); + return (LoadNode*)NULL; +} + +LoadLNode* LoadLNode::make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt) { + bool require_atomic = true; + return new (C, 3) LoadLNode(ctl, mem, adr, adr_type, rt->is_long(), require_atomic); +} + + + + +//------------------------------hash------------------------------------------- +uint LoadNode::hash() const { + // unroll addition of interesting fields + return (uintptr_t)in(Control) + (uintptr_t)in(Memory) + (uintptr_t)in(Address); +} + +//---------------------------can_see_stored_value------------------------------ +// This routine exists to make sure this set of tests is done the same +// everywhere. We need to make a coordinated change: first LoadNode::Ideal +// will change the graph shape in a way which makes memory alive twice at the +// same time (uses the Oracle model of aliasing), then some +// LoadXNode::Identity will fold things back to the equivalence-class model +// of aliasing. +Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const { + Node* ld_adr = in(MemNode::Address); + + // Loop around twice in the case Load -> Initialize -> Store. + // (See PhaseIterGVN::add_users_to_worklist, which knows about this case.) + for (int trip = 0; trip <= 1; trip++) { + + if (st->is_Store()) { + Node* st_adr = st->in(MemNode::Address); + if (!phase->eqv(st_adr, ld_adr)) { + // Try harder before giving up... Match raw and non-raw pointers. + intptr_t st_off = 0; + AllocateNode* alloc = AllocateNode::Ideal_allocation(st_adr, phase, st_off); + if (alloc == NULL) return NULL; + intptr_t ld_off = 0; + AllocateNode* allo2 = AllocateNode::Ideal_allocation(ld_adr, phase, ld_off); + if (alloc != allo2) return NULL; + if (ld_off != st_off) return NULL; + // At this point we have proven something like this setup: + // A = Allocate(...) + // L = LoadQ(, AddP(CastPP(, A.Parm),, #Off)) + // S = StoreQ(, AddP(, A.Parm , #Off), V) + // (Actually, we haven't yet proven the Q's are the same.) + // In other words, we are loading from a casted version of + // the same pointer-and-offset that we stored to. + // Thus, we are able to replace L by V. + } + // Now prove that we have a LoadQ matched to a StoreQ, for some Q. + if (store_Opcode() != st->Opcode()) + return NULL; + return st->in(MemNode::ValueIn); + } + + intptr_t offset = 0; // scratch + + // A load from a freshly-created object always returns zero. + // (This can happen after LoadNode::Ideal resets the load's memory input + // to find_captured_store, which returned InitializeNode::zero_memory.) + if (st->is_Proj() && st->in(0)->is_Allocate() && + st->in(0) == AllocateNode::Ideal_allocation(ld_adr, phase, offset) && + offset >= st->in(0)->as_Allocate()->minimum_header_size()) { + // return a zero value for the load's basic type + // (This is one of the few places where a generic PhaseTransform + // can create new nodes. Think of it as lazily manifesting + // virtually pre-existing constants.) + return phase->zerocon(memory_type()); + } + + // A load from an initialization barrier can match a captured store. + if (st->is_Proj() && st->in(0)->is_Initialize()) { + InitializeNode* init = st->in(0)->as_Initialize(); + AllocateNode* alloc = init->allocation(); + if (alloc != NULL && + alloc == AllocateNode::Ideal_allocation(ld_adr, phase, offset)) { + // examine a captured store value + st = init->find_captured_store(offset, memory_size(), phase); + if (st != NULL) + continue; // take one more trip around + } + } + + break; + } + + return NULL; +} + +//------------------------------Identity--------------------------------------- +// Loads are identity if previous store is to same address +Node *LoadNode::Identity( PhaseTransform *phase ) { + // If the previous store-maker is the right kind of Store, and the store is + // to the same address, then we are equal to the value stored. + Node* mem = in(MemNode::Memory); + Node* value = can_see_stored_value(mem, phase); + if( value ) { + // byte, short & char stores truncate naturally. + // A load has to load the truncated value which requires + // some sort of masking operation and that requires an + // Ideal call instead of an Identity call. + if (memory_size() < BytesPerInt) { + // If the input to the store does not fit with the load's result type, + // it must be truncated via an Ideal call. + if (!phase->type(value)->higher_equal(phase->type(this))) + return this; + } + // (This works even when value is a Con, but LoadNode::Value + // usually runs first, producing the singleton type of the Con.) + return value; + } + return this; +} + +//------------------------------Ideal------------------------------------------ +// If the load is from Field memory and the pointer is non-null, we can +// zero out the control input. +// If the offset is constant and the base is an object allocation, +// try to hook me up to the exact initializing store. +Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node* p = MemNode::Ideal_common(phase, can_reshape); + if (p) return (p == NodeSentinel) ? NULL : p; + + Node* ctrl = in(MemNode::Control); + Node* address = in(MemNode::Address); + + // Skip up past a SafePoint control. Cannot do this for Stores because + // pointer stores & cardmarks must stay on the same side of a SafePoint. + if( ctrl != NULL && ctrl->Opcode() == Op_SafePoint && + phase->C->get_alias_index(phase->type(address)->is_ptr()) != Compile::AliasIdxRaw ) { + ctrl = ctrl->in(0); + set_req(MemNode::Control,ctrl); + } + + // Check for useless control edge in some common special cases + if (in(MemNode::Control) != NULL) { + intptr_t ignore = 0; + Node* base = AddPNode::Ideal_base_and_offset(address, phase, ignore); + if (base != NULL + && phase->type(base)->higher_equal(TypePtr::NOTNULL) + && detect_dominating_control(base->in(0), phase->C->start())) { + // A method-invariant, non-null address (constant or 'this' argument). + set_req(MemNode::Control, NULL); + } + } + + // Check for prior store with a different base or offset; make Load + // independent. Skip through any number of them. Bail out if the stores + // are in an endless dead cycle and report no progress. This is a key + // transform for Reflection. However, if after skipping through the Stores + // we can't then fold up against a prior store do NOT do the transform as + // this amounts to using the 'Oracle' model of aliasing. It leaves the same + // array memory alive twice: once for the hoisted Load and again after the + // bypassed Store. This situation only works if EVERYBODY who does + // anti-dependence work knows how to bypass. I.e. we need all + // anti-dependence checks to ask the same Oracle. Right now, that Oracle is + // the alias index stuff. So instead, peek through Stores and IFF we can + // fold up, do so. + Node* prev_mem = find_previous_store(phase); + // Steps (a), (b): Walk past independent stores to find an exact match. + if (prev_mem != NULL && prev_mem != in(MemNode::Memory)) { + // (c) See if we can fold up on the spot, but don't fold up here. + // Fold-up might require truncation (for LoadB/LoadS/LoadC) or + // just return a prior value, which is done by Identity calls. + if (can_see_stored_value(prev_mem, phase)) { + // Make ready for step (d): + set_req(MemNode::Memory, prev_mem); + return this; + } + } + + return NULL; // No further progress +} + +// Helper to recognize certain Klass fields which are invariant across +// some group of array types (e.g., int[] or all T[] where T < Object). +const Type* +LoadNode::load_array_final_field(const TypeKlassPtr *tkls, + ciKlass* klass) const { + if (tkls->offset() == Klass::modifier_flags_offset_in_bytes() + (int)sizeof(oopDesc)) { + // The field is Klass::_modifier_flags. Return its (constant) value. + // (Folds up the 2nd indirection in aClassConstant.getModifiers().) + assert(this->Opcode() == Op_LoadI, "must load an int from _modifier_flags"); + return TypeInt::make(klass->modifier_flags()); + } + if (tkls->offset() == Klass::access_flags_offset_in_bytes() + (int)sizeof(oopDesc)) { + // The field is Klass::_access_flags. Return its (constant) value. + // (Folds up the 2nd indirection in Reflection.getClassAccessFlags(aClassConstant).) + assert(this->Opcode() == Op_LoadI, "must load an int from _access_flags"); + return TypeInt::make(klass->access_flags()); + } + if (tkls->offset() == Klass::layout_helper_offset_in_bytes() + (int)sizeof(oopDesc)) { + // The field is Klass::_layout_helper. Return its constant value if known. + assert(this->Opcode() == Op_LoadI, "must load an int from _layout_helper"); + return TypeInt::make(klass->layout_helper()); + } + + // No match. + return NULL; +} + +//------------------------------Value----------------------------------------- +const Type *LoadNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + Node* mem = in(MemNode::Memory); + const Type *t1 = phase->type(mem); + if (t1 == Type::TOP) return Type::TOP; + Node* adr = in(MemNode::Address); + const TypePtr* tp = phase->type(adr)->isa_ptr(); + if (tp == NULL || tp->empty()) return Type::TOP; + int off = tp->offset(); + assert(off != Type::OffsetTop, "case covered by TypePtr::empty"); + + // Try to guess loaded type from pointer type + if (tp->base() == Type::AryPtr) { + const Type *t = tp->is_aryptr()->elem(); + // Don't do this for integer types. There is only potential profit if + // the element type t is lower than _type; that is, for int types, if _type is + // more restrictive than t. This only happens here if one is short and the other + // char (both 16 bits), and in those cases we've made an intentional decision + // to use one kind of load over the other. See AndINode::Ideal and 4965907. + // Also, do not try to narrow the type for a LoadKlass, regardless of offset. + // + // Yes, it is possible to encounter an expression like (LoadKlass p1:(AddP x x 8)) + // where the _gvn.type of the AddP is wider than 8. This occurs when an earlier + // copy p0 of (AddP x x 8) has been proven equal to p1, and the p0 has been + // subsumed by p1. If p1 is on the worklist but has not yet been re-transformed, + // it is possible that p1 will have a type like Foo*[int+]:NotNull*+any. + // In fact, that could have been the original type of p1, and p1 could have + // had an original form like p1:(AddP x x (LShiftL quux 3)), where the + // expression (LShiftL quux 3) independently optimized to the constant 8. + if ((t->isa_int() == NULL) && (t->isa_long() == NULL) + && Opcode() != Op_LoadKlass) { + // t might actually be lower than _type, if _type is a unique + // concrete subclass of abstract class t. + // Make sure the reference is not into the header, by comparing + // the offset against the offset of the start of the array's data. + // Different array types begin at slightly different offsets (12 vs. 16). + // We choose T_BYTE as an example base type that is least restrictive + // as to alignment, which will therefore produce the smallest + // possible base offset. + const int min_base_off = arrayOopDesc::base_offset_in_bytes(T_BYTE); + if ((uint)off >= (uint)min_base_off) { // is the offset beyond the header? + const Type* jt = t->join(_type); + // In any case, do not allow the join, per se, to empty out the type. + if (jt->empty() && !t->empty()) { + // This can happen if a interface-typed array narrows to a class type. + jt = _type; + } + return jt; + } + } + } else if (tp->base() == Type::InstPtr) { + assert( off != Type::OffsetBot || + // arrays can be cast to Objects + tp->is_oopptr()->klass()->is_java_lang_Object() || + // unsafe field access may not have a constant offset + phase->C->has_unsafe_access(), + "Field accesses must be precise" ); + // For oop loads, we expect the _type to be precise + } else if (tp->base() == Type::KlassPtr) { + assert( off != Type::OffsetBot || + // arrays can be cast to Objects + tp->is_klassptr()->klass()->is_java_lang_Object() || + // also allow array-loading from the primary supertype + // array during subtype checks + Opcode() == Op_LoadKlass, + "Field accesses must be precise" ); + // For klass/static loads, we expect the _type to be precise + } + + const TypeKlassPtr *tkls = tp->isa_klassptr(); + if (tkls != NULL && !StressReflectiveCode) { + ciKlass* klass = tkls->klass(); + if (klass->is_loaded() && tkls->klass_is_exact()) { + // We are loading a field from a Klass metaobject whose identity + // is known at compile time (the type is "exact" or "precise"). + // Check for fields we know are maintained as constants by the VM. + if (tkls->offset() == Klass::super_check_offset_offset_in_bytes() + (int)sizeof(oopDesc)) { + // The field is Klass::_super_check_offset. Return its (constant) value. + // (Folds up type checking code.) + assert(Opcode() == Op_LoadI, "must load an int from _super_check_offset"); + return TypeInt::make(klass->super_check_offset()); + } + // Compute index into primary_supers array + juint depth = (tkls->offset() - (Klass::primary_supers_offset_in_bytes() + (int)sizeof(oopDesc))) / sizeof(klassOop); + // Check for overflowing; use unsigned compare to handle the negative case. + if( depth < ciKlass::primary_super_limit() ) { + // The field is an element of Klass::_primary_supers. Return its (constant) value. + // (Folds up type checking code.) + assert(Opcode() == Op_LoadKlass, "must load a klass from _primary_supers"); + ciKlass *ss = klass->super_of_depth(depth); + return ss ? TypeKlassPtr::make(ss) : TypePtr::NULL_PTR; + } + const Type* aift = load_array_final_field(tkls, klass); + if (aift != NULL) return aift; + if (tkls->offset() == in_bytes(arrayKlass::component_mirror_offset()) + (int)sizeof(oopDesc) + && klass->is_array_klass()) { + // The field is arrayKlass::_component_mirror. Return its (constant) value. + // (Folds up aClassConstant.getComponentType, common in Arrays.copyOf.) + assert(Opcode() == Op_LoadP, "must load an oop from _component_mirror"); + return TypeInstPtr::make(klass->as_array_klass()->component_mirror()); + } + if (tkls->offset() == Klass::java_mirror_offset_in_bytes() + (int)sizeof(oopDesc)) { + // The field is Klass::_java_mirror. Return its (constant) value. + // (Folds up the 2nd indirection in anObjConstant.getClass().) + assert(Opcode() == Op_LoadP, "must load an oop from _java_mirror"); + return TypeInstPtr::make(klass->java_mirror()); + } + } + + // We can still check if we are loading from the primary_supers array at a + // shallow enough depth. Even though the klass is not exact, entries less + // than or equal to its super depth are correct. + if (klass->is_loaded() ) { + ciType *inner = klass->klass(); + while( inner->is_obj_array_klass() ) + inner = inner->as_obj_array_klass()->base_element_type(); + if( inner->is_instance_klass() && + !inner->as_instance_klass()->flags().is_interface() ) { + // Compute index into primary_supers array + juint depth = (tkls->offset() - (Klass::primary_supers_offset_in_bytes() + (int)sizeof(oopDesc))) / sizeof(klassOop); + // Check for overflowing; use unsigned compare to handle the negative case. + if( depth < ciKlass::primary_super_limit() && + depth <= klass->super_depth() ) { // allow self-depth checks to handle self-check case + // The field is an element of Klass::_primary_supers. Return its (constant) value. + // (Folds up type checking code.) + assert(Opcode() == Op_LoadKlass, "must load a klass from _primary_supers"); + ciKlass *ss = klass->super_of_depth(depth); + return ss ? TypeKlassPtr::make(ss) : TypePtr::NULL_PTR; + } + } + } + + // If the type is enough to determine that the thing is not an array, + // we can give the layout_helper a positive interval type. + // This will help short-circuit some reflective code. + if (tkls->offset() == Klass::layout_helper_offset_in_bytes() + (int)sizeof(oopDesc) + && !klass->is_array_klass() // not directly typed as an array + && !klass->is_interface() // specifically not Serializable & Cloneable + && !klass->is_java_lang_Object() // not the supertype of all T[] + ) { + // Note: When interfaces are reliable, we can narrow the interface + // test to (klass != Serializable && klass != Cloneable). + assert(Opcode() == Op_LoadI, "must load an int from _layout_helper"); + jint min_size = Klass::instance_layout_helper(oopDesc::header_size(), false); + // The key property of this type is that it folds up tests + // for array-ness, since it proves that the layout_helper is positive. + // Thus, a generic value like the basic object layout helper works fine. + return TypeInt::make(min_size, max_jint, Type::WidenMin); + } + } + + // If we are loading from a freshly-allocated object, produce a zero, + // if the load is provably beyond the header of the object. + // (Also allow a variable load from a fresh array to produce zero.) + if (ReduceFieldZeroing) { + Node* value = can_see_stored_value(mem,phase); + if (value != NULL && value->is_Con()) + return value->bottom_type(); + } + + return _type; +} + +//------------------------------match_edge------------------------------------- +// Do we Match on this edge index or not? Match only the address. +uint LoadNode::match_edge(uint idx) const { + return idx == MemNode::Address; +} + +//--------------------------LoadBNode::Ideal-------------------------------------- +// +// If the previous store is to the same address as this load, +// and the value stored was larger than a byte, replace this load +// with the value stored truncated to a byte. If no truncation is +// needed, the replacement is done in LoadNode::Identity(). +// +Node *LoadBNode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node* mem = in(MemNode::Memory); + Node* value = can_see_stored_value(mem,phase); + if( value && !phase->type(value)->higher_equal( _type ) ) { + Node *result = phase->transform( new (phase->C, 3) LShiftINode(value, phase->intcon(24)) ); + return new (phase->C, 3) RShiftINode(result, phase->intcon(24)); + } + // Identity call will handle the case where truncation is not needed. + return LoadNode::Ideal(phase, can_reshape); +} + +//--------------------------LoadCNode::Ideal-------------------------------------- +// +// If the previous store is to the same address as this load, +// and the value stored was larger than a char, replace this load +// with the value stored truncated to a char. If no truncation is +// needed, the replacement is done in LoadNode::Identity(). +// +Node *LoadCNode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node* mem = in(MemNode::Memory); + Node* value = can_see_stored_value(mem,phase); + if( value && !phase->type(value)->higher_equal( _type ) ) + return new (phase->C, 3) AndINode(value,phase->intcon(0xFFFF)); + // Identity call will handle the case where truncation is not needed. + return LoadNode::Ideal(phase, can_reshape); +} + +//--------------------------LoadSNode::Ideal-------------------------------------- +// +// If the previous store is to the same address as this load, +// and the value stored was larger than a short, replace this load +// with the value stored truncated to a short. If no truncation is +// needed, the replacement is done in LoadNode::Identity(). +// +Node *LoadSNode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node* mem = in(MemNode::Memory); + Node* value = can_see_stored_value(mem,phase); + if( value && !phase->type(value)->higher_equal( _type ) ) { + Node *result = phase->transform( new (phase->C, 3) LShiftINode(value, phase->intcon(16)) ); + return new (phase->C, 3) RShiftINode(result, phase->intcon(16)); + } + // Identity call will handle the case where truncation is not needed. + return LoadNode::Ideal(phase, can_reshape); +} + +//============================================================================= +//------------------------------Value------------------------------------------ +const Type *LoadKlassNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(MemNode::Memory) ); + if (t1 == Type::TOP) return Type::TOP; + Node *adr = in(MemNode::Address); + const Type *t2 = phase->type( adr ); + if (t2 == Type::TOP) return Type::TOP; + const TypePtr *tp = t2->is_ptr(); + if (TypePtr::above_centerline(tp->ptr()) || + tp->ptr() == TypePtr::Null) return Type::TOP; + + // Return a more precise klass, if possible + const TypeInstPtr *tinst = tp->isa_instptr(); + if (tinst != NULL) { + ciInstanceKlass* ik = tinst->klass()->as_instance_klass(); + int offset = tinst->offset(); + if (ik == phase->C->env()->Class_klass() + && (offset == java_lang_Class::klass_offset_in_bytes() || + offset == java_lang_Class::array_klass_offset_in_bytes())) { + // We are loading a special hidden field from a Class mirror object, + // the field which points to the VM's Klass metaobject. + ciType* t = tinst->java_mirror_type(); + // java_mirror_type returns non-null for compile-time Class constants. + if (t != NULL) { + // constant oop => constant klass + if (offset == java_lang_Class::array_klass_offset_in_bytes()) { + return TypeKlassPtr::make(ciArrayKlass::make(t)); + } + if (!t->is_klass()) { + // a primitive Class (e.g., int.class) has NULL for a klass field + return TypePtr::NULL_PTR; + } + // (Folds up the 1st indirection in aClassConstant.getModifiers().) + return TypeKlassPtr::make(t->as_klass()); + } + // non-constant mirror, so we can't tell what's going on + } + if( !ik->is_loaded() ) + return _type; // Bail out if not loaded + if (offset == oopDesc::klass_offset_in_bytes()) { + if (tinst->klass_is_exact()) { + return TypeKlassPtr::make(ik); + } + // See if we can become precise: no subklasses and no interface + // (Note: We need to support verified interfaces.) + if (!ik->is_interface() && !ik->has_subklass()) { + //assert(!UseExactTypes, "this code should be useless with exact types"); + // Add a dependence; if any subclass added we need to recompile + if (!ik->is_final()) { + // %%% should use stronger assert_unique_concrete_subtype instead + phase->C->dependencies()->assert_leaf_type(ik); + } + // Return precise klass + return TypeKlassPtr::make(ik); + } + + // Return root of possible klass + return TypeKlassPtr::make(TypePtr::NotNull, ik, 0/*offset*/); + } + } + + // Check for loading klass from an array + const TypeAryPtr *tary = tp->isa_aryptr(); + if( tary != NULL ) { + ciKlass *tary_klass = tary->klass(); + if (tary_klass != NULL // can be NULL when at BOTTOM or TOP + && tary->offset() == oopDesc::klass_offset_in_bytes()) { + if (tary->klass_is_exact()) { + return TypeKlassPtr::make(tary_klass); + } + ciArrayKlass *ak = tary->klass()->as_array_klass(); + // If the klass is an object array, we defer the question to the + // array component klass. + if( ak->is_obj_array_klass() ) { + assert( ak->is_loaded(), "" ); + ciKlass *base_k = ak->as_obj_array_klass()->base_element_klass(); + if( base_k->is_loaded() && base_k->is_instance_klass() ) { + ciInstanceKlass* ik = base_k->as_instance_klass(); + // See if we can become precise: no subklasses and no interface + if (!ik->is_interface() && !ik->has_subklass()) { + //assert(!UseExactTypes, "this code should be useless with exact types"); + // Add a dependence; if any subclass added we need to recompile + if (!ik->is_final()) { + phase->C->dependencies()->assert_leaf_type(ik); + } + // Return precise array klass + return TypeKlassPtr::make(ak); + } + } + return TypeKlassPtr::make(TypePtr::NotNull, ak, 0/*offset*/); + } else { // Found a type-array? + //assert(!UseExactTypes, "this code should be useless with exact types"); + assert( ak->is_type_array_klass(), "" ); + return TypeKlassPtr::make(ak); // These are always precise + } + } + } + + // Check for loading klass from an array klass + const TypeKlassPtr *tkls = tp->isa_klassptr(); + if (tkls != NULL && !StressReflectiveCode) { + ciKlass* klass = tkls->klass(); + if( !klass->is_loaded() ) + return _type; // Bail out if not loaded + if( klass->is_obj_array_klass() && + (uint)tkls->offset() == objArrayKlass::element_klass_offset_in_bytes() + sizeof(oopDesc)) { + ciKlass* elem = klass->as_obj_array_klass()->element_klass(); + // // Always returning precise element type is incorrect, + // // e.g., element type could be object and array may contain strings + // return TypeKlassPtr::make(TypePtr::Constant, elem, 0); + + // The array's TypeKlassPtr was declared 'precise' or 'not precise' + // according to the element type's subclassing. + return TypeKlassPtr::make(tkls->ptr(), elem, 0/*offset*/); + } + if( klass->is_instance_klass() && tkls->klass_is_exact() && + (uint)tkls->offset() == Klass::super_offset_in_bytes() + sizeof(oopDesc)) { + ciKlass* sup = klass->as_instance_klass()->super(); + // The field is Klass::_super. Return its (constant) value. + // (Folds up the 2nd indirection in aClassConstant.getSuperClass().) + return sup ? TypeKlassPtr::make(sup) : TypePtr::NULL_PTR; + } + } + + // Bailout case + return LoadNode::Value(phase); +} + +//------------------------------Identity--------------------------------------- +// To clean up reflective code, simplify k.java_mirror.as_klass to plain k. +// Also feed through the klass in Allocate(...klass...)._klass. +Node* LoadKlassNode::Identity( PhaseTransform *phase ) { + Node* x = LoadNode::Identity(phase); + if (x != this) return x; + + // Take apart the address into an oop and and offset. + // Return 'this' if we cannot. + Node* adr = in(MemNode::Address); + intptr_t offset = 0; + Node* base = AddPNode::Ideal_base_and_offset(adr, phase, offset); + if (base == NULL) return this; + const TypeOopPtr* toop = phase->type(adr)->isa_oopptr(); + if (toop == NULL) return this; + + // We can fetch the klass directly through an AllocateNode. + // This works even if the klass is not constant (clone or newArray). + if (offset == oopDesc::klass_offset_in_bytes()) { + Node* allocated_klass = AllocateNode::Ideal_klass(base, phase); + if (allocated_klass != NULL) { + return allocated_klass; + } + } + + // Simplify k.java_mirror.as_klass to plain k, where k is a klassOop. + // Simplify ak.component_mirror.array_klass to plain ak, ak an arrayKlass. + // See inline_native_Class_query for occurrences of these patterns. + // Java Example: x.getClass().isAssignableFrom(y) + // Java Example: Array.newInstance(x.getClass().getComponentType(), n) + // + // This improves reflective code, often making the Class + // mirror go completely dead. (Current exception: Class + // mirrors may appear in debug info, but we could clean them out by + // introducing a new debug info operator for klassOop.java_mirror). + if (toop->isa_instptr() && toop->klass() == phase->C->env()->Class_klass() + && (offset == java_lang_Class::klass_offset_in_bytes() || + offset == java_lang_Class::array_klass_offset_in_bytes())) { + // We are loading a special hidden field from a Class mirror, + // the field which points to its Klass or arrayKlass metaobject. + if (base->is_Load()) { + Node* adr2 = base->in(MemNode::Address); + const TypeKlassPtr* tkls = phase->type(adr2)->isa_klassptr(); + if (tkls != NULL && !tkls->empty() + && (tkls->klass()->is_instance_klass() || + tkls->klass()->is_array_klass()) + && adr2->is_AddP() + ) { + int mirror_field = Klass::java_mirror_offset_in_bytes(); + if (offset == java_lang_Class::array_klass_offset_in_bytes()) { + mirror_field = in_bytes(arrayKlass::component_mirror_offset()); + } + if (tkls->offset() == mirror_field + (int)sizeof(oopDesc)) { + return adr2->in(AddPNode::Base); + } + } + } + } + + return this; +} + +//------------------------------Value----------------------------------------- +const Type *LoadRangeNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(MemNode::Memory) ); + if( t1 == Type::TOP ) return Type::TOP; + Node *adr = in(MemNode::Address); + const Type *t2 = phase->type( adr ); + if( t2 == Type::TOP ) return Type::TOP; + const TypePtr *tp = t2->is_ptr(); + if (TypePtr::above_centerline(tp->ptr())) return Type::TOP; + const TypeAryPtr *tap = tp->isa_aryptr(); + if( !tap ) return _type; + return tap->size(); +} + +//------------------------------Identity--------------------------------------- +// Feed through the length in AllocateArray(...length...)._length. +Node* LoadRangeNode::Identity( PhaseTransform *phase ) { + Node* x = LoadINode::Identity(phase); + if (x != this) return x; + + // Take apart the address into an oop and and offset. + // Return 'this' if we cannot. + Node* adr = in(MemNode::Address); + intptr_t offset = 0; + Node* base = AddPNode::Ideal_base_and_offset(adr, phase, offset); + if (base == NULL) return this; + const TypeAryPtr* tary = phase->type(adr)->isa_aryptr(); + if (tary == NULL) return this; + + // We can fetch the length directly through an AllocateArrayNode. + // This works even if the length is not constant (clone or newArray). + if (offset == arrayOopDesc::length_offset_in_bytes()) { + Node* allocated_length = AllocateArrayNode::Ideal_length(base, phase); + if (allocated_length != NULL) { + return allocated_length; + } + } + + return this; + +} +//============================================================================= +//---------------------------StoreNode::make----------------------------------- +// Polymorphic factory method: +StoreNode* StoreNode::make( Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, BasicType bt ) { + switch (bt) { + case T_BOOLEAN: + case T_BYTE: return new (C, 4) StoreBNode(ctl, mem, adr, adr_type, val); + case T_INT: return new (C, 4) StoreINode(ctl, mem, adr, adr_type, val); + case T_CHAR: + case T_SHORT: return new (C, 4) StoreCNode(ctl, mem, adr, adr_type, val); + case T_LONG: return new (C, 4) StoreLNode(ctl, mem, adr, adr_type, val); + case T_FLOAT: return new (C, 4) StoreFNode(ctl, mem, adr, adr_type, val); + case T_DOUBLE: return new (C, 4) StoreDNode(ctl, mem, adr, adr_type, val); + case T_ADDRESS: + case T_OBJECT: return new (C, 4) StorePNode(ctl, mem, adr, adr_type, val); + } + ShouldNotReachHere(); + return (StoreNode*)NULL; +} + +StoreLNode* StoreLNode::make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val) { + bool require_atomic = true; + return new (C, 4) StoreLNode(ctl, mem, adr, adr_type, val, require_atomic); +} + + +//--------------------------bottom_type---------------------------------------- +const Type *StoreNode::bottom_type() const { + return Type::MEMORY; +} + +//------------------------------hash------------------------------------------- +uint StoreNode::hash() const { + // unroll addition of interesting fields + //return (uintptr_t)in(Control) + (uintptr_t)in(Memory) + (uintptr_t)in(Address) + (uintptr_t)in(ValueIn); + + // Since they are not commoned, do not hash them: + return NO_HASH; +} + +//------------------------------Ideal------------------------------------------ +// Change back-to-back Store(, p, x) -> Store(m, p, y) to Store(m, p, x). +// When a store immediately follows a relevant allocation/initialization, +// try to capture it into the initialization, or hoist it above. +Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node* p = MemNode::Ideal_common(phase, can_reshape); + if (p) return (p == NodeSentinel) ? NULL : p; + + Node* mem = in(MemNode::Memory); + Node* address = in(MemNode::Address); + + // Back-to-back stores to same address? Fold em up. + // Generally unsafe if I have intervening uses... + if (mem->is_Store() && phase->eqv_uncast(mem->in(MemNode::Address), address)) { + // Looking at a dead closed cycle of memory? + assert(mem != mem->in(MemNode::Memory), "dead loop in StoreNode::Ideal"); + + assert(Opcode() == mem->Opcode() || + phase->C->get_alias_index(adr_type()) == Compile::AliasIdxRaw, + "no mismatched stores, except on raw memory"); + + if (mem->outcnt() == 1 && // check for intervening uses + mem->as_Store()->memory_size() <= this->memory_size()) { + // If anybody other than 'this' uses 'mem', we cannot fold 'mem' away. + // For example, 'mem' might be the final state at a conditional return. + // Or, 'mem' might be used by some node which is live at the same time + // 'this' is live, which might be unschedulable. So, require exactly + // ONE user, the 'this' store, until such time as we clone 'mem' for + // each of 'mem's uses (thus making the exactly-1-user-rule hold true). + if (can_reshape) { // (%%% is this an anachronism?) + set_req_X(MemNode::Memory, mem->in(MemNode::Memory), + phase->is_IterGVN()); + } else { + // It's OK to do this in the parser, since DU info is always accurate, + // and the parser always refers to nodes via SafePointNode maps. + set_req(MemNode::Memory, mem->in(MemNode::Memory)); + } + return this; + } + } + + // Capture an unaliased, unconditional, simple store into an initializer. + // Or, if it is independent of the allocation, hoist it above the allocation. + if (ReduceFieldZeroing && /*can_reshape &&*/ + mem->is_Proj() && mem->in(0)->is_Initialize()) { + InitializeNode* init = mem->in(0)->as_Initialize(); + intptr_t offset = init->can_capture_store(this, phase); + if (offset > 0) { + Node* moved = init->capture_store(this, offset, phase); + // If the InitializeNode captured me, it made a raw copy of me, + // and I need to disappear. + if (moved != NULL) { + // %%% hack to ensure that Ideal returns a new node: + mem = MergeMemNode::make(phase->C, mem); + return mem; // fold me away + } + } + } + + return NULL; // No further progress +} + +//------------------------------Value----------------------------------------- +const Type *StoreNode::Value( PhaseTransform *phase ) const { + // Either input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(MemNode::Memory) ); + if( t1 == Type::TOP ) return Type::TOP; + const Type *t2 = phase->type( in(MemNode::Address) ); + if( t2 == Type::TOP ) return Type::TOP; + const Type *t3 = phase->type( in(MemNode::ValueIn) ); + if( t3 == Type::TOP ) return Type::TOP; + return Type::MEMORY; +} + +//------------------------------Identity--------------------------------------- +// Remove redundant stores: +// Store(m, p, Load(m, p)) changes to m. +// Store(, p, x) -> Store(m, p, x) changes to Store(m, p, x). +Node *StoreNode::Identity( PhaseTransform *phase ) { + Node* mem = in(MemNode::Memory); + Node* adr = in(MemNode::Address); + Node* val = in(MemNode::ValueIn); + + // Load then Store? Then the Store is useless + if (val->is_Load() && + phase->eqv_uncast( val->in(MemNode::Address), adr ) && + phase->eqv_uncast( val->in(MemNode::Memory ), mem ) && + val->as_Load()->store_Opcode() == Opcode()) { + return mem; + } + + // Two stores in a row of the same value? + if (mem->is_Store() && + phase->eqv_uncast( mem->in(MemNode::Address), adr ) && + phase->eqv_uncast( mem->in(MemNode::ValueIn), val ) && + mem->Opcode() == Opcode()) { + return mem; + } + + // Store of zero anywhere into a freshly-allocated object? + // Then the store is useless. + // (It must already have been captured by the InitializeNode.) + if (ReduceFieldZeroing && phase->type(val)->is_zero_type()) { + // a newly allocated object is already all-zeroes everywhere + if (mem->is_Proj() && mem->in(0)->is_Allocate()) { + return mem; + } + + // the store may also apply to zero-bits in an earlier object + Node* prev_mem = find_previous_store(phase); + // Steps (a), (b): Walk past independent stores to find an exact match. + if (prev_mem != NULL) { + Node* prev_val = can_see_stored_value(prev_mem, phase); + if (prev_val != NULL && phase->eqv(prev_val, val)) { + // prev_val and val might differ by a cast; it would be good + // to keep the more informative of the two. + return mem; + } + } + } + + return this; +} + +//------------------------------match_edge------------------------------------- +// Do we Match on this edge index or not? Match only memory & value +uint StoreNode::match_edge(uint idx) const { + return idx == MemNode::Address || idx == MemNode::ValueIn; +} + +//------------------------------cmp-------------------------------------------- +// Do not common stores up together. They generally have to be split +// back up anyways, so do not bother. +uint StoreNode::cmp( const Node &n ) const { + return (&n == this); // Always fail except on self +} + +//------------------------------Ideal_masked_input----------------------------- +// Check for a useless mask before a partial-word store +// (StoreB ... (AndI valIn conIa) ) +// If (conIa & mask == mask) this simplifies to +// (StoreB ... (valIn) ) +Node *StoreNode::Ideal_masked_input(PhaseGVN *phase, uint mask) { + Node *val = in(MemNode::ValueIn); + if( val->Opcode() == Op_AndI ) { + const TypeInt *t = phase->type( val->in(2) )->isa_int(); + if( t && t->is_con() && (t->get_con() & mask) == mask ) { + set_req(MemNode::ValueIn, val->in(1)); + return this; + } + } + return NULL; +} + + +//------------------------------Ideal_sign_extended_input---------------------- +// Check for useless sign-extension before a partial-word store +// (StoreB ... (RShiftI _ (LShiftI _ valIn conIL ) conIR) ) +// If (conIL == conIR && conIR <= num_bits) this simplifies to +// (StoreB ... (valIn) ) +Node *StoreNode::Ideal_sign_extended_input(PhaseGVN *phase, int num_bits) { + Node *val = in(MemNode::ValueIn); + if( val->Opcode() == Op_RShiftI ) { + const TypeInt *t = phase->type( val->in(2) )->isa_int(); + if( t && t->is_con() && (t->get_con() <= num_bits) ) { + Node *shl = val->in(1); + if( shl->Opcode() == Op_LShiftI ) { + const TypeInt *t2 = phase->type( shl->in(2) )->isa_int(); + if( t2 && t2->is_con() && (t2->get_con() == t->get_con()) ) { + set_req(MemNode::ValueIn, shl->in(1)); + return this; + } + } + } + } + return NULL; +} + +//------------------------------value_never_loaded----------------------------------- +// Determine whether there are any possible loads of the value stored. +// For simplicity, we actually check if there are any loads from the +// address stored to, not just for loads of the value stored by this node. +// +bool StoreNode::value_never_loaded( PhaseTransform *phase) const { + Node *adr = in(Address); + const TypeOopPtr *adr_oop = phase->type(adr)->isa_oopptr(); + if (adr_oop == NULL) + return false; + if (!adr_oop->is_instance()) + return false; // if not a distinct instance, there may be aliases of the address + for (DUIterator_Fast imax, i = adr->fast_outs(imax); i < imax; i++) { + Node *use = adr->fast_out(i); + int opc = use->Opcode(); + if (use->is_Load() || use->is_LoadStore()) { + return false; + } + } + return true; +} + +//============================================================================= +//------------------------------Ideal------------------------------------------ +// If the store is from an AND mask that leaves the low bits untouched, then +// we can skip the AND operation. If the store is from a sign-extension +// (a left shift, then right shift) we can skip both. +Node *StoreBNode::Ideal(PhaseGVN *phase, bool can_reshape){ + Node *progress = StoreNode::Ideal_masked_input(phase, 0xFF); + if( progress != NULL ) return progress; + + progress = StoreNode::Ideal_sign_extended_input(phase, 24); + if( progress != NULL ) return progress; + + // Finally check the default case + return StoreNode::Ideal(phase, can_reshape); +} + +//============================================================================= +//------------------------------Ideal------------------------------------------ +// If the store is from an AND mask that leaves the low bits untouched, then +// we can skip the AND operation +Node *StoreCNode::Ideal(PhaseGVN *phase, bool can_reshape){ + Node *progress = StoreNode::Ideal_masked_input(phase, 0xFFFF); + if( progress != NULL ) return progress; + + progress = StoreNode::Ideal_sign_extended_input(phase, 16); + if( progress != NULL ) return progress; + + // Finally check the default case + return StoreNode::Ideal(phase, can_reshape); +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +Node *StoreCMNode::Identity( PhaseTransform *phase ) { + // No need to card mark when storing a null ptr + Node* my_store = in(MemNode::OopStore); + if (my_store->is_Store()) { + const Type *t1 = phase->type( my_store->in(MemNode::ValueIn) ); + if( t1 == TypePtr::NULL_PTR ) { + return in(MemNode::Memory); + } + } + return this; +} + +//------------------------------Value----------------------------------------- +const Type *StoreCMNode::Value( PhaseTransform *phase ) const { + // If extra input is TOP ==> the result is TOP + const Type *t1 = phase->type( in(MemNode::OopStore) ); + if( t1 == Type::TOP ) return Type::TOP; + + return StoreNode::Value( phase ); +} + + +//============================================================================= +//----------------------------------SCMemProjNode------------------------------ +const Type * SCMemProjNode::Value( PhaseTransform *phase ) const +{ + return bottom_type(); +} + +//============================================================================= +LoadStoreNode::LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, Node *ex ) : Node(5) { + init_req(MemNode::Control, c ); + init_req(MemNode::Memory , mem); + init_req(MemNode::Address, adr); + init_req(MemNode::ValueIn, val); + init_req( ExpectedIn, ex ); + init_class_id(Class_LoadStore); + +} + +//============================================================================= +//-------------------------------adr_type-------------------------------------- +// Do we Match on this edge index or not? Do not match memory +const TypePtr* ClearArrayNode::adr_type() const { + Node *adr = in(3); + return MemNode::calculate_adr_type(adr->bottom_type()); +} + +//------------------------------match_edge------------------------------------- +// Do we Match on this edge index or not? Do not match memory +uint ClearArrayNode::match_edge(uint idx) const { + return idx > 1; +} + +//------------------------------Identity--------------------------------------- +// Clearing a zero length array does nothing +Node *ClearArrayNode::Identity( PhaseTransform *phase ) { + return phase->type(in(2))->higher_equal(TypeInt::ZERO) ? in(1) : this; +} + +//------------------------------Idealize--------------------------------------- +// Clearing a short array is faster with stores +Node *ClearArrayNode::Ideal(PhaseGVN *phase, bool can_reshape){ + const int unit = BytesPerLong; + const TypeX* t = phase->type(in(2))->isa_intptr_t(); + if (!t) return NULL; + if (!t->is_con()) return NULL; + intptr_t raw_count = t->get_con(); + intptr_t size = raw_count; + if (!Matcher::init_array_count_is_in_bytes) size *= unit; + // Clearing nothing uses the Identity call. + // Negative clears are possible on dead ClearArrays + // (see jck test stmt114.stmt11402.val). + if (size <= 0 || size % unit != 0) return NULL; + intptr_t count = size / unit; + // Length too long; use fast hardware clear + if (size > Matcher::init_array_short_size) return NULL; + Node *mem = in(1); + if( phase->type(mem)==Type::TOP ) return NULL; + Node *adr = in(3); + const Type* at = phase->type(adr); + if( at==Type::TOP ) return NULL; + const TypePtr* atp = at->isa_ptr(); + // adjust atp to be the correct array element address type + if (atp == NULL) atp = TypePtr::BOTTOM; + else atp = atp->add_offset(Type::OffsetBot); + // Get base for derived pointer purposes + if( adr->Opcode() != Op_AddP ) Unimplemented(); + Node *base = adr->in(1); + + Node *zero = phase->makecon(TypeLong::ZERO); + Node *off = phase->MakeConX(BytesPerLong); + mem = new (phase->C, 4) StoreLNode(in(0),mem,adr,atp,zero); + count--; + while( count-- ) { + mem = phase->transform(mem); + adr = phase->transform(new (phase->C, 4) AddPNode(base,adr,off)); + mem = new (phase->C, 4) StoreLNode(in(0),mem,adr,atp,zero); + } + return mem; +} + +//----------------------------clear_memory------------------------------------- +// Generate code to initialize object storage to zero. +Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest, + intptr_t start_offset, + Node* end_offset, + PhaseGVN* phase) { + Compile* C = phase->C; + intptr_t offset = start_offset; + + int unit = BytesPerLong; + if ((offset % unit) != 0) { + Node* adr = new (C, 4) AddPNode(dest, dest, phase->MakeConX(offset)); + adr = phase->transform(adr); + const TypePtr* atp = TypeRawPtr::BOTTOM; + mem = StoreNode::make(C, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT); + mem = phase->transform(mem); + offset += BytesPerInt; + } + assert((offset % unit) == 0, ""); + + // Initialize the remaining stuff, if any, with a ClearArray. + return clear_memory(ctl, mem, dest, phase->MakeConX(offset), end_offset, phase); +} + +Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest, + Node* start_offset, + Node* end_offset, + PhaseGVN* phase) { + Compile* C = phase->C; + int unit = BytesPerLong; + Node* zbase = start_offset; + Node* zend = end_offset; + + // Scale to the unit required by the CPU: + if (!Matcher::init_array_count_is_in_bytes) { + Node* shift = phase->intcon(exact_log2(unit)); + zbase = phase->transform( new(C,3) URShiftXNode(zbase, shift) ); + zend = phase->transform( new(C,3) URShiftXNode(zend, shift) ); + } + + Node* zsize = phase->transform( new(C,3) SubXNode(zend, zbase) ); + Node* zinit = phase->zerocon((unit == BytesPerLong) ? T_LONG : T_INT); + + // Bulk clear double-words + Node* adr = phase->transform( new(C,4) AddPNode(dest, dest, start_offset) ); + mem = new (C, 4) ClearArrayNode(ctl, mem, zsize, adr); + return phase->transform(mem); +} + +Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest, + intptr_t start_offset, + intptr_t end_offset, + PhaseGVN* phase) { + Compile* C = phase->C; + assert((end_offset % BytesPerInt) == 0, "odd end offset"); + intptr_t done_offset = end_offset; + if ((done_offset % BytesPerLong) != 0) { + done_offset -= BytesPerInt; + } + if (done_offset > start_offset) { + mem = clear_memory(ctl, mem, dest, + start_offset, phase->MakeConX(done_offset), phase); + } + if (done_offset < end_offset) { // emit the final 32-bit store + Node* adr = new (C, 4) AddPNode(dest, dest, phase->MakeConX(done_offset)); + adr = phase->transform(adr); + const TypePtr* atp = TypeRawPtr::BOTTOM; + mem = StoreNode::make(C, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT); + mem = phase->transform(mem); + done_offset += BytesPerInt; + } + assert(done_offset == end_offset, ""); + return mem; +} + +//============================================================================= +// Do we match on this edge? No memory edges +uint StrCompNode::match_edge(uint idx) const { + return idx == 5 || idx == 6; +} + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node *StrCompNode::Ideal(PhaseGVN *phase, bool can_reshape){ + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + + +//============================================================================= +MemBarNode::MemBarNode(Compile* C, int alias_idx, Node* precedent) + : MultiNode(TypeFunc::Parms + (precedent == NULL? 0: 1)), + _adr_type(C->get_adr_type(alias_idx)) +{ + init_class_id(Class_MemBar); + Node* top = C->top(); + init_req(TypeFunc::I_O,top); + init_req(TypeFunc::FramePtr,top); + init_req(TypeFunc::ReturnAdr,top); + if (precedent != NULL) + init_req(TypeFunc::Parms, precedent); +} + +//------------------------------cmp-------------------------------------------- +uint MemBarNode::hash() const { return NO_HASH; } +uint MemBarNode::cmp( const Node &n ) const { + return (&n == this); // Always fail except on self +} + +//------------------------------make------------------------------------------- +MemBarNode* MemBarNode::make(Compile* C, int opcode, int atp, Node* pn) { + int len = Precedent + (pn == NULL? 0: 1); + switch (opcode) { + case Op_MemBarAcquire: return new(C, len) MemBarAcquireNode(C, atp, pn); + case Op_MemBarRelease: return new(C, len) MemBarReleaseNode(C, atp, pn); + case Op_MemBarVolatile: return new(C, len) MemBarVolatileNode(C, atp, pn); + case Op_MemBarCPUOrder: return new(C, len) MemBarCPUOrderNode(C, atp, pn); + case Op_Initialize: return new(C, len) InitializeNode(C, atp, pn); + default: ShouldNotReachHere(); return NULL; + } +} + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node *MemBarNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if (remove_dead_region(phase, can_reshape)) return this; + return NULL; +} + +//------------------------------Value------------------------------------------ +const Type *MemBarNode::Value( PhaseTransform *phase ) const { + if( !in(0) ) return Type::TOP; + if( phase->type(in(0)) == Type::TOP ) + return Type::TOP; + return TypeTuple::MEMBAR; +} + +//------------------------------match------------------------------------------ +// Construct projections for memory. +Node *MemBarNode::match( const ProjNode *proj, const Matcher *m ) { + switch (proj->_con) { + case TypeFunc::Control: + case TypeFunc::Memory: + return new (m->C, 1) MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj); + } + ShouldNotReachHere(); + return NULL; +} + +//===========================InitializeNode==================================== +// SUMMARY: +// This node acts as a memory barrier on raw memory, after some raw stores. +// The 'cooked' oop value feeds from the Initialize, not the Allocation. +// The Initialize can 'capture' suitably constrained stores as raw inits. +// It can coalesce related raw stores into larger units (called 'tiles'). +// It can avoid zeroing new storage for memory units which have raw inits. +// At macro-expansion, it is marked 'complete', and does not optimize further. +// +// EXAMPLE: +// The object 'new short[2]' occupies 16 bytes in a 32-bit machine. +// ctl = incoming control; mem* = incoming memory +// (Note: A star * on a memory edge denotes I/O and other standard edges.) +// First allocate uninitialized memory and fill in the header: +// alloc = (Allocate ctl mem* 16 #short[].klass ...) +// ctl := alloc.Control; mem* := alloc.Memory* +// rawmem = alloc.Memory; rawoop = alloc.RawAddress +// Then initialize to zero the non-header parts of the raw memory block: +// init = (Initialize alloc.Control alloc.Memory* alloc.RawAddress) +// ctl := init.Control; mem.SLICE(#short[*]) := init.Memory +// After the initialize node executes, the object is ready for service: +// oop := (CheckCastPP init.Control alloc.RawAddress #short[]) +// Suppose its body is immediately initialized as {1,2}: +// store1 = (StoreC init.Control init.Memory (+ oop 12) 1) +// store2 = (StoreC init.Control store1 (+ oop 14) 2) +// mem.SLICE(#short[*]) := store2 +// +// DETAILS: +// An InitializeNode collects and isolates object initialization after +// an AllocateNode and before the next possible safepoint. As a +// memory barrier (MemBarNode), it keeps critical stores from drifting +// down past any safepoint or any publication of the allocation. +// Before this barrier, a newly-allocated object may have uninitialized bits. +// After this barrier, it may be treated as a real oop, and GC is allowed. +// +// The semantics of the InitializeNode include an implicit zeroing of +// the new object from object header to the end of the object. +// (The object header and end are determined by the AllocateNode.) +// +// Certain stores may be added as direct inputs to the InitializeNode. +// These stores must update raw memory, and they must be to addresses +// derived from the raw address produced by AllocateNode, and with +// a constant offset. They must be ordered by increasing offset. +// The first one is at in(RawStores), the last at in(req()-1). +// Unlike most memory operations, they are not linked in a chain, +// but are displayed in parallel as users of the rawmem output of +// the allocation. +// +// (See comments in InitializeNode::capture_store, which continue +// the example given above.) +// +// When the associated Allocate is macro-expanded, the InitializeNode +// may be rewritten to optimize collected stores. A ClearArrayNode +// may also be created at that point to represent any required zeroing. +// The InitializeNode is then marked 'complete', prohibiting further +// capturing of nearby memory operations. +// +// During macro-expansion, all captured initializations which store +// constant values of 32 bits or smaller are coalesced (if advantagous) +// into larger 'tiles' 32 or 64 bits. This allows an object to be +// initialized in fewer memory operations. Memory words which are +// covered by neither tiles nor non-constant stores are pre-zeroed +// by explicit stores of zero. (The code shape happens to do all +// zeroing first, then all other stores, with both sequences occurring +// in order of ascending offsets.) +// +// Alternatively, code may be inserted between an AllocateNode and its +// InitializeNode, to perform arbitrary initialization of the new object. +// E.g., the object copying intrinsics insert complex data transfers here. +// The initialization must then be marked as 'complete' disable the +// built-in zeroing semantics and the collection of initializing stores. +// +// While an InitializeNode is incomplete, reads from the memory state +// produced by it are optimizable if they match the control edge and +// new oop address associated with the allocation/initialization. +// They return a stored value (if the offset matches) or else zero. +// A write to the memory state, if it matches control and address, +// and if it is to a constant offset, may be 'captured' by the +// InitializeNode. It is cloned as a raw memory operation and rewired +// inside the initialization, to the raw oop produced by the allocation. +// Operations on addresses which are provably distinct (e.g., to +// other AllocateNodes) are allowed to bypass the initialization. +// +// The effect of all this is to consolidate object initialization +// (both arrays and non-arrays, both piecewise and bulk) into a +// single location, where it can be optimized as a unit. +// +// Only stores with an offset less than TrackedInitializationLimit words +// will be considered for capture by an InitializeNode. This puts a +// reasonable limit on the complexity of optimized initializations. + +//---------------------------InitializeNode------------------------------------ +InitializeNode::InitializeNode(Compile* C, int adr_type, Node* rawoop) + : _is_complete(false), + MemBarNode(C, adr_type, rawoop) +{ + init_class_id(Class_Initialize); + + assert(adr_type == Compile::AliasIdxRaw, "only valid atp"); + assert(in(RawAddress) == rawoop, "proper init"); + // Note: allocation() can be NULL, for secondary initialization barriers +} + +// Since this node is not matched, it will be processed by the +// register allocator. Declare that there are no constraints +// on the allocation of the RawAddress edge. +const RegMask &InitializeNode::in_RegMask(uint idx) const { + // This edge should be set to top, by the set_complete. But be conservative. + if (idx == InitializeNode::RawAddress) + return *(Compile::current()->matcher()->idealreg2spillmask[in(idx)->ideal_reg()]); + return RegMask::Empty; +} + +Node* InitializeNode::memory(uint alias_idx) { + Node* mem = in(Memory); + if (mem->is_MergeMem()) { + return mem->as_MergeMem()->memory_at(alias_idx); + } else { + // incoming raw memory is not split + return mem; + } +} + +bool InitializeNode::is_non_zero() { + if (is_complete()) return false; + remove_extra_zeroes(); + return (req() > RawStores); +} + +void InitializeNode::set_complete(PhaseGVN* phase) { + assert(!is_complete(), "caller responsibility"); + _is_complete = true; + + // After this node is complete, it contains a bunch of + // raw-memory initializations. There is no need for + // it to have anything to do with non-raw memory effects. + // Therefore, tell all non-raw users to re-optimize themselves, + // after skipping the memory effects of this initialization. + PhaseIterGVN* igvn = phase->is_IterGVN(); + if (igvn) igvn->add_users_to_worklist(this); +} + +// convenience function +// return false if the init contains any stores already +bool AllocateNode::maybe_set_complete(PhaseGVN* phase) { + InitializeNode* init = initialization(); + if (init == NULL || init->is_complete()) return false; + init->remove_extra_zeroes(); + // for now, if this allocation has already collected any inits, bail: + if (init->is_non_zero()) return false; + init->set_complete(phase); + return true; +} + +void InitializeNode::remove_extra_zeroes() { + if (req() == RawStores) return; + Node* zmem = zero_memory(); + uint fill = RawStores; + for (uint i = fill; i < req(); i++) { + Node* n = in(i); + if (n->is_top() || n == zmem) continue; // skip + if (fill < i) set_req(fill, n); // compact + ++fill; + } + // delete any empty spaces created: + while (fill < req()) { + del_req(fill); + } +} + +// Helper for remembering which stores go with which offsets. +intptr_t InitializeNode::get_store_offset(Node* st, PhaseTransform* phase) { + if (!st->is_Store()) return -1; // can happen to dead code via subsume_node + intptr_t offset = -1; + Node* base = AddPNode::Ideal_base_and_offset(st->in(MemNode::Address), + phase, offset); + if (base == NULL) return -1; // something is dead, + if (offset < 0) return -1; // dead, dead + return offset; +} + +// Helper for proving that an initialization expression is +// "simple enough" to be folded into an object initialization. +// Attempts to prove that a store's initial value 'n' can be captured +// within the initialization without creating a vicious cycle, such as: +// { Foo p = new Foo(); p.next = p; } +// True for constants and parameters and small combinations thereof. +bool InitializeNode::detect_init_independence(Node* n, + bool st_is_pinned, + int& count) { + if (n == NULL) return true; // (can this really happen?) + if (n->is_Proj()) n = n->in(0); + if (n == this) return false; // found a cycle + if (n->is_Con()) return true; + if (n->is_Start()) return true; // params, etc., are OK + if (n->is_Root()) return true; // even better + + Node* ctl = n->in(0); + if (ctl != NULL && !ctl->is_top()) { + if (ctl->is_Proj()) ctl = ctl->in(0); + if (ctl == this) return false; + + // If we already know that the enclosing memory op is pinned right after + // the init, then any control flow that the store has picked up + // must have preceded the init, or else be equal to the init. + // Even after loop optimizations (which might change control edges) + // a store is never pinned *before* the availability of its inputs. + if (!MemNode::detect_dominating_control(ctl, this->in(0))) + return false; // failed to prove a good control + + } + + // Check data edges for possible dependencies on 'this'. + if ((count += 1) > 20) return false; // complexity limit + for (uint i = 1; i < n->req(); i++) { + Node* m = n->in(i); + if (m == NULL || m == n || m->is_top()) continue; + uint first_i = n->find_edge(m); + if (i != first_i) continue; // process duplicate edge just once + if (!detect_init_independence(m, st_is_pinned, count)) { + return false; + } + } + + return true; +} + +// Here are all the checks a Store must pass before it can be moved into +// an initialization. Returns zero if a check fails. +// On success, returns the (constant) offset to which the store applies, +// within the initialized memory. +intptr_t InitializeNode::can_capture_store(StoreNode* st, PhaseTransform* phase) { + const int FAIL = 0; + if (st->req() != MemNode::ValueIn + 1) + return FAIL; // an inscrutable StoreNode (card mark?) + Node* ctl = st->in(MemNode::Control); + if (!(ctl != NULL && ctl->is_Proj() && ctl->in(0) == this)) + return FAIL; // must be unconditional after the initialization + Node* mem = st->in(MemNode::Memory); + if (!(mem->is_Proj() && mem->in(0) == this)) + return FAIL; // must not be preceded by other stores + Node* adr = st->in(MemNode::Address); + intptr_t offset; + AllocateNode* alloc = AllocateNode::Ideal_allocation(adr, phase, offset); + if (alloc == NULL) + return FAIL; // inscrutable address + if (alloc != allocation()) + return FAIL; // wrong allocation! (store needs to float up) + Node* val = st->in(MemNode::ValueIn); + int complexity_count = 0; + if (!detect_init_independence(val, true, complexity_count)) + return FAIL; // stored value must be 'simple enough' + + return offset; // success +} + +// Find the captured store in(i) which corresponds to the range +// [start..start+size) in the initialized object. +// If there is one, return its index i. If there isn't, return the +// negative of the index where it should be inserted. +// Return 0 if the queried range overlaps an initialization boundary +// or if dead code is encountered. +// If size_in_bytes is zero, do not bother with overlap checks. +int InitializeNode::captured_store_insertion_point(intptr_t start, + int size_in_bytes, + PhaseTransform* phase) { + const int FAIL = 0, MAX_STORE = BytesPerLong; + + if (is_complete()) + return FAIL; // arraycopy got here first; punt + + assert(allocation() != NULL, "must be present"); + + // no negatives, no header fields: + if (start < (intptr_t) sizeof(oopDesc)) return FAIL; + if (start < (intptr_t) sizeof(arrayOopDesc) && + start < (intptr_t) allocation()->minimum_header_size()) return FAIL; + + // after a certain size, we bail out on tracking all the stores: + intptr_t ti_limit = (TrackedInitializationLimit * HeapWordSize); + if (start >= ti_limit) return FAIL; + + for (uint i = InitializeNode::RawStores, limit = req(); ; ) { + if (i >= limit) return -(int)i; // not found; here is where to put it + + Node* st = in(i); + intptr_t st_off = get_store_offset(st, phase); + if (st_off < 0) { + if (st != zero_memory()) { + return FAIL; // bail out if there is dead garbage + } + } else if (st_off > start) { + // ...we are done, since stores are ordered + if (st_off < start + size_in_bytes) { + return FAIL; // the next store overlaps + } + return -(int)i; // not found; here is where to put it + } else if (st_off < start) { + if (size_in_bytes != 0 && + start < st_off + MAX_STORE && + start < st_off + st->as_Store()->memory_size()) { + return FAIL; // the previous store overlaps + } + } else { + if (size_in_bytes != 0 && + st->as_Store()->memory_size() != size_in_bytes) { + return FAIL; // mismatched store size + } + return i; + } + + ++i; + } +} + +// Look for a captured store which initializes at the offset 'start' +// with the given size. If there is no such store, and no other +// initialization interferes, then return zero_memory (the memory +// projection of the AllocateNode). +Node* InitializeNode::find_captured_store(intptr_t start, int size_in_bytes, + PhaseTransform* phase) { + assert(stores_are_sane(phase), ""); + int i = captured_store_insertion_point(start, size_in_bytes, phase); + if (i == 0) { + return NULL; // something is dead + } else if (i < 0) { + return zero_memory(); // just primordial zero bits here + } else { + Node* st = in(i); // here is the store at this position + assert(get_store_offset(st->as_Store(), phase) == start, "sanity"); + return st; + } +} + +// Create, as a raw pointer, an address within my new object at 'offset'. +Node* InitializeNode::make_raw_address(intptr_t offset, + PhaseTransform* phase) { + Node* addr = in(RawAddress); + if (offset != 0) { + Compile* C = phase->C; + addr = phase->transform( new (C, 4) AddPNode(C->top(), addr, + phase->MakeConX(offset)) ); + } + return addr; +} + +// Clone the given store, converting it into a raw store +// initializing a field or element of my new object. +// Caller is responsible for retiring the original store, +// with subsume_node or the like. +// +// From the example above InitializeNode::InitializeNode, +// here are the old stores to be captured: +// store1 = (StoreC init.Control init.Memory (+ oop 12) 1) +// store2 = (StoreC init.Control store1 (+ oop 14) 2) +// +// Here is the changed code; note the extra edges on init: +// alloc = (Allocate ...) +// rawoop = alloc.RawAddress +// rawstore1 = (StoreC alloc.Control alloc.Memory (+ rawoop 12) 1) +// rawstore2 = (StoreC alloc.Control alloc.Memory (+ rawoop 14) 2) +// init = (Initialize alloc.Control alloc.Memory rawoop +// rawstore1 rawstore2) +// +Node* InitializeNode::capture_store(StoreNode* st, intptr_t start, + PhaseTransform* phase) { + assert(stores_are_sane(phase), ""); + + if (start < 0) return NULL; + assert(can_capture_store(st, phase) == start, "sanity"); + + Compile* C = phase->C; + int size_in_bytes = st->memory_size(); + int i = captured_store_insertion_point(start, size_in_bytes, phase); + if (i == 0) return NULL; // bail out + Node* prev_mem = NULL; // raw memory for the captured store + if (i > 0) { + prev_mem = in(i); // there is a pre-existing store under this one + set_req(i, C->top()); // temporarily disconnect it + // See StoreNode::Ideal 'st->outcnt() == 1' for the reason to disconnect. + } else { + i = -i; // no pre-existing store + prev_mem = zero_memory(); // a slice of the newly allocated object + if (i > InitializeNode::RawStores && in(i-1) == prev_mem) + set_req(--i, C->top()); // reuse this edge; it has been folded away + else + ins_req(i, C->top()); // build a new edge + } + Node* new_st = st->clone(); + new_st->set_req(MemNode::Control, in(Control)); + new_st->set_req(MemNode::Memory, prev_mem); + new_st->set_req(MemNode::Address, make_raw_address(start, phase)); + new_st = phase->transform(new_st); + + // At this point, new_st might have swallowed a pre-existing store + // at the same offset, or perhaps new_st might have disappeared, + // if it redundantly stored the same value (or zero to fresh memory). + + // In any case, wire it in: + set_req(i, new_st); + + // The caller may now kill the old guy. + DEBUG_ONLY(Node* check_st = find_captured_store(start, size_in_bytes, phase)); + assert(check_st == new_st || check_st == NULL, "must be findable"); + assert(!is_complete(), ""); + return new_st; +} + +static bool store_constant(jlong* tiles, int num_tiles, + intptr_t st_off, int st_size, + jlong con) { + if ((st_off & (st_size-1)) != 0) + return false; // strange store offset (assume size==2**N) + address addr = (address)tiles + st_off; + assert(st_off >= 0 && addr+st_size <= (address)&tiles[num_tiles], "oob"); + switch (st_size) { + case sizeof(jbyte): *(jbyte*) addr = (jbyte) con; break; + case sizeof(jchar): *(jchar*) addr = (jchar) con; break; + case sizeof(jint): *(jint*) addr = (jint) con; break; + case sizeof(jlong): *(jlong*) addr = (jlong) con; break; + default: return false; // strange store size (detect size!=2**N here) + } + return true; // return success to caller +} + +// Coalesce subword constants into int constants and possibly +// into long constants. The goal, if the CPU permits, +// is to initialize the object with a small number of 64-bit tiles. +// Also, convert floating-point constants to bit patterns. +// Non-constants are not relevant to this pass. +// +// In terms of the running example on InitializeNode::InitializeNode +// and InitializeNode::capture_store, here is the transformation +// of rawstore1 and rawstore2 into rawstore12: +// alloc = (Allocate ...) +// rawoop = alloc.RawAddress +// tile12 = 0x00010002 +// rawstore12 = (StoreI alloc.Control alloc.Memory (+ rawoop 12) tile12) +// init = (Initialize alloc.Control alloc.Memory rawoop rawstore12) +// +void +InitializeNode::coalesce_subword_stores(intptr_t header_size, + Node* size_in_bytes, + PhaseGVN* phase) { + Compile* C = phase->C; + + assert(stores_are_sane(phase), ""); + // Note: After this pass, they are not completely sane, + // since there may be some overlaps. + + int old_subword = 0, old_long = 0, new_int = 0, new_long = 0; + + intptr_t ti_limit = (TrackedInitializationLimit * HeapWordSize); + intptr_t size_limit = phase->find_intptr_t_con(size_in_bytes, ti_limit); + size_limit = MIN2(size_limit, ti_limit); + size_limit = align_size_up(size_limit, BytesPerLong); + int num_tiles = size_limit / BytesPerLong; + + // allocate space for the tile map: + const int small_len = DEBUG_ONLY(true ? 3 :) 30; // keep stack frames small + jlong tiles_buf[small_len]; + Node* nodes_buf[small_len]; + jlong inits_buf[small_len]; + jlong* tiles = ((num_tiles <= small_len) ? &tiles_buf[0] + : NEW_RESOURCE_ARRAY(jlong, num_tiles)); + Node** nodes = ((num_tiles <= small_len) ? &nodes_buf[0] + : NEW_RESOURCE_ARRAY(Node*, num_tiles)); + jlong* inits = ((num_tiles <= small_len) ? &inits_buf[0] + : NEW_RESOURCE_ARRAY(jlong, num_tiles)); + // tiles: exact bitwise model of all primitive constants + // nodes: last constant-storing node subsumed into the tiles model + // inits: which bytes (in each tile) are touched by any initializations + + //// Pass A: Fill in the tile model with any relevant stores. + + Copy::zero_to_bytes(tiles, sizeof(tiles[0]) * num_tiles); + Copy::zero_to_bytes(nodes, sizeof(nodes[0]) * num_tiles); + Copy::zero_to_bytes(inits, sizeof(inits[0]) * num_tiles); + Node* zmem = zero_memory(); // initially zero memory state + for (uint i = InitializeNode::RawStores, limit = req(); i < limit; i++) { + Node* st = in(i); + intptr_t st_off = get_store_offset(st, phase); + + // Figure out the store's offset and constant value: + if (st_off < header_size) continue; //skip (ignore header) + if (st->in(MemNode::Memory) != zmem) continue; //skip (odd store chain) + int st_size = st->as_Store()->memory_size(); + if (st_off + st_size > size_limit) break; + + // Record which bytes are touched, whether by constant or not. + if (!store_constant(inits, num_tiles, st_off, st_size, (jlong) -1)) + continue; // skip (strange store size) + + const Type* val = phase->type(st->in(MemNode::ValueIn)); + if (!val->singleton()) continue; //skip (non-con store) + BasicType type = val->basic_type(); + + jlong con = 0; + switch (type) { + case T_INT: con = val->is_int()->get_con(); break; + case T_LONG: con = val->is_long()->get_con(); break; + case T_FLOAT: con = jint_cast(val->getf()); break; + case T_DOUBLE: con = jlong_cast(val->getd()); break; + default: continue; //skip (odd store type) + } + + if (type == T_LONG && Matcher::isSimpleConstant64(con) && + st->Opcode() == Op_StoreL) { + continue; // This StoreL is already optimal. + } + + // Store down the constant. + store_constant(tiles, num_tiles, st_off, st_size, con); + + intptr_t j = st_off >> LogBytesPerLong; + + if (type == T_INT && st_size == BytesPerInt + && (st_off & BytesPerInt) == BytesPerInt) { + jlong lcon = tiles[j]; + if (!Matcher::isSimpleConstant64(lcon) && + st->Opcode() == Op_StoreI) { + // This StoreI is already optimal by itself. + jint* intcon = (jint*) &tiles[j]; + intcon[1] = 0; // undo the store_constant() + + // If the previous store is also optimal by itself, back up and + // undo the action of the previous loop iteration... if we can. + // But if we can't, just let the previous half take care of itself. + st = nodes[j]; + st_off -= BytesPerInt; + con = intcon[0]; + if (con != 0 && st != NULL && st->Opcode() == Op_StoreI) { + assert(st_off >= header_size, "still ignoring header"); + assert(get_store_offset(st, phase) == st_off, "must be"); + assert(in(i-1) == zmem, "must be"); + DEBUG_ONLY(const Type* tcon = phase->type(st->in(MemNode::ValueIn))); + assert(con == tcon->is_int()->get_con(), "must be"); + // Undo the effects of the previous loop trip, which swallowed st: + intcon[0] = 0; // undo store_constant() + set_req(i-1, st); // undo set_req(i, zmem) + nodes[j] = NULL; // undo nodes[j] = st + --old_subword; // undo ++old_subword + } + continue; // This StoreI is already optimal. + } + } + + // This store is not needed. + set_req(i, zmem); + nodes[j] = st; // record for the moment + if (st_size < BytesPerLong) // something has changed + ++old_subword; // includes int/float, but who's counting... + else ++old_long; + } + + if ((old_subword + old_long) == 0) + return; // nothing more to do + + //// Pass B: Convert any non-zero tiles into optimal constant stores. + // Be sure to insert them before overlapping non-constant stores. + // (E.g., byte[] x = { 1,2,y,4 } => x[int 0] = 0x01020004, x[2]=y.) + for (int j = 0; j < num_tiles; j++) { + jlong con = tiles[j]; + jlong init = inits[j]; + if (con == 0) continue; + jint con0, con1; // split the constant, address-wise + jint init0, init1; // split the init map, address-wise + { union { jlong con; jint intcon[2]; } u; + u.con = con; + con0 = u.intcon[0]; + con1 = u.intcon[1]; + u.con = init; + init0 = u.intcon[0]; + init1 = u.intcon[1]; + } + + Node* old = nodes[j]; + assert(old != NULL, "need the prior store"); + intptr_t offset = (j * BytesPerLong); + + bool split = !Matcher::isSimpleConstant64(con); + + if (offset < header_size) { + assert(offset + BytesPerInt >= header_size, "second int counts"); + assert(*(jint*)&tiles[j] == 0, "junk in header"); + split = true; // only the second word counts + // Example: int a[] = { 42 ... } + } else if (con0 == 0 && init0 == -1) { + split = true; // first word is covered by full inits + // Example: int a[] = { ... foo(), 42 ... } + } else if (con1 == 0 && init1 == -1) { + split = true; // second word is covered by full inits + // Example: int a[] = { ... 42, foo() ... } + } + + // Here's a case where init0 is neither 0 nor -1: + // byte a[] = { ... 0,0,foo(),0, 0,0,0,42 ... } + // Assuming big-endian memory, init0, init1 are 0x0000FF00, 0x000000FF. + // In this case the tile is not split; it is (jlong)42. + // The big tile is stored down, and then the foo() value is inserted. + // (If there were foo(),foo() instead of foo(),0, init0 would be -1.) + + Node* ctl = old->in(MemNode::Control); + Node* adr = make_raw_address(offset, phase); + const TypePtr* atp = TypeRawPtr::BOTTOM; + + // One or two coalesced stores to plop down. + Node* st[2]; + intptr_t off[2]; + int nst = 0; + if (!split) { + ++new_long; + off[nst] = offset; + st[nst++] = StoreNode::make(C, ctl, zmem, adr, atp, + phase->longcon(con), T_LONG); + } else { + // Omit either if it is a zero. + if (con0 != 0) { + ++new_int; + off[nst] = offset; + st[nst++] = StoreNode::make(C, ctl, zmem, adr, atp, + phase->intcon(con0), T_INT); + } + if (con1 != 0) { + ++new_int; + offset += BytesPerInt; + adr = make_raw_address(offset, phase); + off[nst] = offset; + st[nst++] = StoreNode::make(C, ctl, zmem, adr, atp, + phase->intcon(con1), T_INT); + } + } + + // Insert second store first, then the first before the second. + // Insert each one just before any overlapping non-constant stores. + while (nst > 0) { + Node* st1 = st[--nst]; + C->copy_node_notes_to(st1, old); + st1 = phase->transform(st1); + offset = off[nst]; + assert(offset >= header_size, "do not smash header"); + int ins_idx = captured_store_insertion_point(offset, /*size:*/0, phase); + guarantee(ins_idx != 0, "must re-insert constant store"); + if (ins_idx < 0) ins_idx = -ins_idx; // never overlap + if (ins_idx > InitializeNode::RawStores && in(ins_idx-1) == zmem) + set_req(--ins_idx, st1); + else + ins_req(ins_idx, st1); + } + } + + if (PrintCompilation && WizardMode) + tty->print_cr("Changed %d/%d subword/long constants into %d/%d int/long", + old_subword, old_long, new_int, new_long); + if (C->log() != NULL) + C->log()->elem("comment that='%d/%d subword/long to %d/%d int/long'", + old_subword, old_long, new_int, new_long); + + // Clean up any remaining occurrences of zmem: + remove_extra_zeroes(); +} + +// Explore forward from in(start) to find the first fully initialized +// word, and return its offset. Skip groups of subword stores which +// together initialize full words. If in(start) is itself part of a +// fully initialized word, return the offset of in(start). If there +// are no following full-word stores, or if something is fishy, return +// a negative value. +intptr_t InitializeNode::find_next_fullword_store(uint start, PhaseGVN* phase) { + int int_map = 0; + intptr_t int_map_off = 0; + const int FULL_MAP = right_n_bits(BytesPerInt); // the int_map we hope for + + for (uint i = start, limit = req(); i < limit; i++) { + Node* st = in(i); + + intptr_t st_off = get_store_offset(st, phase); + if (st_off < 0) break; // return conservative answer + + int st_size = st->as_Store()->memory_size(); + if (st_size >= BytesPerInt && (st_off % BytesPerInt) == 0) { + return st_off; // we found a complete word init + } + + // update the map: + + intptr_t this_int_off = align_size_down(st_off, BytesPerInt); + if (this_int_off != int_map_off) { + // reset the map: + int_map = 0; + int_map_off = this_int_off; + } + + int subword_off = st_off - this_int_off; + int_map |= right_n_bits(st_size) << subword_off; + if ((int_map & FULL_MAP) == FULL_MAP) { + return this_int_off; // we found a complete word init + } + + // Did this store hit or cross the word boundary? + intptr_t next_int_off = align_size_down(st_off + st_size, BytesPerInt); + if (next_int_off == this_int_off + BytesPerInt) { + // We passed the current int, without fully initializing it. + int_map_off = next_int_off; + int_map >>= BytesPerInt; + } else if (next_int_off > this_int_off + BytesPerInt) { + // We passed the current and next int. + return this_int_off + BytesPerInt; + } + } + + return -1; +} + + +// Called when the associated AllocateNode is expanded into CFG. +// At this point, we may perform additional optimizations. +// Linearize the stores by ascending offset, to make memory +// activity as coherent as possible. +Node* InitializeNode::complete_stores(Node* rawctl, Node* rawmem, Node* rawptr, + intptr_t header_size, + Node* size_in_bytes, + PhaseGVN* phase) { + assert(!is_complete(), "not already complete"); + assert(stores_are_sane(phase), ""); + assert(allocation() != NULL, "must be present"); + + remove_extra_zeroes(); + + if (ReduceFieldZeroing || ReduceBulkZeroing) + // reduce instruction count for common initialization patterns + coalesce_subword_stores(header_size, size_in_bytes, phase); + + Node* zmem = zero_memory(); // initially zero memory state + Node* inits = zmem; // accumulating a linearized chain of inits + #ifdef ASSERT + intptr_t last_init_off = sizeof(oopDesc); // previous init offset + intptr_t last_init_end = sizeof(oopDesc); // previous init offset+size + intptr_t last_tile_end = sizeof(oopDesc); // previous tile offset+size + #endif + intptr_t zeroes_done = header_size; + + bool do_zeroing = true; // we might give up if inits are very sparse + int big_init_gaps = 0; // how many large gaps have we seen? + + if (ZeroTLAB) do_zeroing = false; + if (!ReduceFieldZeroing && !ReduceBulkZeroing) do_zeroing = false; + + for (uint i = InitializeNode::RawStores, limit = req(); i < limit; i++) { + Node* st = in(i); + intptr_t st_off = get_store_offset(st, phase); + if (st_off < 0) + break; // unknown junk in the inits + if (st->in(MemNode::Memory) != zmem) + break; // complicated store chains somehow in list + + int st_size = st->as_Store()->memory_size(); + intptr_t next_init_off = st_off + st_size; + + if (do_zeroing && zeroes_done < next_init_off) { + // See if this store needs a zero before it or under it. + intptr_t zeroes_needed = st_off; + + if (st_size < BytesPerInt) { + // Look for subword stores which only partially initialize words. + // If we find some, we must lay down some word-level zeroes first, + // underneath the subword stores. + // + // Examples: + // byte[] a = { p,q,r,s } => a[0]=p,a[1]=q,a[2]=r,a[3]=s + // byte[] a = { x,y,0,0 } => a[0..3] = 0, a[0]=x,a[1]=y + // byte[] a = { 0,0,z,0 } => a[0..3] = 0, a[2]=z + // + // Note: coalesce_subword_stores may have already done this, + // if it was prompted by constant non-zero subword initializers. + // But this case can still arise with non-constant stores. + + intptr_t next_full_store = find_next_fullword_store(i, phase); + + // In the examples above: + // in(i) p q r s x y z + // st_off 12 13 14 15 12 13 14 + // st_size 1 1 1 1 1 1 1 + // next_full_s. 12 16 16 16 16 16 16 + // z's_done 12 16 16 16 12 16 12 + // z's_needed 12 16 16 16 16 16 16 + // zsize 0 0 0 0 4 0 4 + if (next_full_store < 0) { + // Conservative tack: Zero to end of current word. + zeroes_needed = align_size_up(zeroes_needed, BytesPerInt); + } else { + // Zero to beginning of next fully initialized word. + // Or, don't zero at all, if we are already in that word. + assert(next_full_store >= zeroes_needed, "must go forward"); + assert((next_full_store & (BytesPerInt-1)) == 0, "even boundary"); + zeroes_needed = next_full_store; + } + } + + if (zeroes_needed > zeroes_done) { + intptr_t zsize = zeroes_needed - zeroes_done; + // Do some incremental zeroing on rawmem, in parallel with inits. + zeroes_done = align_size_down(zeroes_done, BytesPerInt); + rawmem = ClearArrayNode::clear_memory(rawctl, rawmem, rawptr, + zeroes_done, zeroes_needed, + phase); + zeroes_done = zeroes_needed; + if (zsize > Matcher::init_array_short_size && ++big_init_gaps > 2) + do_zeroing = false; // leave the hole, next time + } + } + + // Collect the store and move on: + st->set_req(MemNode::Memory, inits); + inits = st; // put it on the linearized chain + set_req(i, zmem); // unhook from previous position + + if (zeroes_done == st_off) + zeroes_done = next_init_off; + + assert(!do_zeroing || zeroes_done >= next_init_off, "don't miss any"); + + #ifdef ASSERT + // Various order invariants. Weaker than stores_are_sane because + // a large constant tile can be filled in by smaller non-constant stores. + assert(st_off >= last_init_off, "inits do not reverse"); + last_init_off = st_off; + const Type* val = NULL; + if (st_size >= BytesPerInt && + (val = phase->type(st->in(MemNode::ValueIn)))->singleton() && + (int)val->basic_type() < (int)T_OBJECT) { + assert(st_off >= last_tile_end, "tiles do not overlap"); + assert(st_off >= last_init_end, "tiles do not overwrite inits"); + last_tile_end = MAX2(last_tile_end, next_init_off); + } else { + intptr_t st_tile_end = align_size_up(next_init_off, BytesPerLong); + assert(st_tile_end >= last_tile_end, "inits stay with tiles"); + assert(st_off >= last_init_end, "inits do not overlap"); + last_init_end = next_init_off; // it's a non-tile + } + #endif //ASSERT + } + + remove_extra_zeroes(); // clear out all the zmems left over + add_req(inits); + + if (!ZeroTLAB) { + // If anything remains to be zeroed, zero it all now. + zeroes_done = align_size_down(zeroes_done, BytesPerInt); + // if it is the last unused 4 bytes of an instance, forget about it + intptr_t size_limit = phase->find_intptr_t_con(size_in_bytes, max_jint); + if (zeroes_done + BytesPerLong >= size_limit) { + assert(allocation() != NULL, ""); + Node* klass_node = allocation()->in(AllocateNode::KlassNode); + ciKlass* k = phase->type(klass_node)->is_klassptr()->klass(); + if (zeroes_done == k->layout_helper()) + zeroes_done = size_limit; + } + if (zeroes_done < size_limit) { + rawmem = ClearArrayNode::clear_memory(rawctl, rawmem, rawptr, + zeroes_done, size_in_bytes, phase); + } + } + + set_complete(phase); + return rawmem; +} + + +#ifdef ASSERT +bool InitializeNode::stores_are_sane(PhaseTransform* phase) { + if (is_complete()) + return true; // stores could be anything at this point + intptr_t last_off = sizeof(oopDesc); + for (uint i = InitializeNode::RawStores; i < req(); i++) { + Node* st = in(i); + intptr_t st_off = get_store_offset(st, phase); + if (st_off < 0) continue; // ignore dead garbage + if (last_off > st_off) { + tty->print_cr("*** bad store offset at %d: %d > %d", i, last_off, st_off); + this->dump(2); + assert(false, "ascending store offsets"); + return false; + } + last_off = st_off + st->as_Store()->memory_size(); + } + return true; +} +#endif //ASSERT + + + + +//============================MergeMemNode===================================== +// +// SEMANTICS OF MEMORY MERGES: A MergeMem is a memory state assembled from several +// contributing store or call operations. Each contributor provides the memory +// state for a particular "alias type" (see Compile::alias_type). For example, +// if a MergeMem has an input X for alias category #6, then any memory reference +// to alias category #6 may use X as its memory state input, as an exact equivalent +// to using the MergeMem as a whole. +// Load<6>( MergeMem(<6>: X, ...), p ) <==> Load<6>(X,p) +// +// (Here, the notation gives the index of the relevant adr_type.) +// +// In one special case (and more cases in the future), alias categories overlap. +// The special alias category "Bot" (Compile::AliasIdxBot) includes all memory +// states. Therefore, if a MergeMem has only one contributing input W for Bot, +// it is exactly equivalent to that state W: +// MergeMem(: W) <==> W +// +// Usually, the merge has more than one input. In that case, where inputs +// overlap (i.e., one is Bot), the narrower alias type determines the memory +// state for that type, and the wider alias type (Bot) fills in everywhere else: +// Load<5>( MergeMem(: W, <6>: X), p ) <==> Load<5>(W,p) +// Load<6>( MergeMem(: W, <6>: X), p ) <==> Load<6>(X,p) +// +// A merge can take a "wide" memory state as one of its narrow inputs. +// This simply means that the merge observes out only the relevant parts of +// the wide input. That is, wide memory states arriving at narrow merge inputs +// are implicitly "filtered" or "sliced" as necessary. (This is rare.) +// +// These rules imply that MergeMem nodes may cascade (via their links), +// and that memory slices "leak through": +// MergeMem(: MergeMem(: W, <7>: Y)) <==> MergeMem(: W, <7>: Y) +// +// But, in such a cascade, repeated memory slices can "block the leak": +// MergeMem(: MergeMem(: W, <7>: Y), <7>: Y') <==> MergeMem(: W, <7>: Y') +// +// In the last example, Y is not part of the combined memory state of the +// outermost MergeMem. The system must, of course, prevent unschedulable +// memory states from arising, so you can be sure that the state Y is somehow +// a precursor to state Y'. +// +// +// REPRESENTATION OF MEMORY MERGES: The indexes used to address the Node::in array +// of each MergeMemNode array are exactly the numerical alias indexes, including +// but not limited to AliasIdxTop, AliasIdxBot, and AliasIdxRaw. The functions +// Compile::alias_type (and kin) produce and manage these indexes. +// +// By convention, the value of in(AliasIdxTop) (i.e., in(1)) is always the top node. +// (Note that this provides quick access to the top node inside MergeMem methods, +// without the need to reach out via TLS to Compile::current.) +// +// As a consequence of what was just described, a MergeMem that represents a full +// memory state has an edge in(AliasIdxBot) which is a "wide" memory state, +// containing all alias categories. +// +// MergeMem nodes never (?) have control inputs, so in(0) is NULL. +// +// All other edges in(N) (including in(AliasIdxRaw), which is in(3)) are either +// a memory state for the alias type , or else the top node, meaning that +// there is no particular input for that alias type. Note that the length of +// a MergeMem is variable, and may be extended at any time to accommodate new +// memory states at larger alias indexes. When merges grow, they are of course +// filled with "top" in the unused in() positions. +// +// This use of top is named "empty_memory()", or "empty_mem" (no-memory) as a variable. +// (Top was chosen because it works smoothly with passes like GCM.) +// +// For convenience, we hardwire the alias index for TypeRawPtr::BOTTOM. (It is +// the type of random VM bits like TLS references.) Since it is always the +// first non-Bot memory slice, some low-level loops use it to initialize an +// index variable: for (i = AliasIdxRaw; i < req(); i++). +// +// +// ACCESSORS: There is a special accessor MergeMemNode::base_memory which returns +// the distinguished "wide" state. The accessor MergeMemNode::memory_at(N) returns +// the memory state for alias type , or (if there is no particular slice at , +// it returns the base memory. To prevent bugs, memory_at does not accept +// or indexes. The iterator MergeMemStream provides robust iteration over +// MergeMem nodes or pairs of such nodes, ensuring that the non-top edges are visited. +// +// %%%% We may get rid of base_memory as a separate accessor at some point; it isn't +// really that different from the other memory inputs. An abbreviation called +// "bot_memory()" for "memory_at(AliasIdxBot)" would keep code tidy. +// +// +// PARTIAL MEMORY STATES: During optimization, MergeMem nodes may arise that represent +// partial memory states. When a Phi splits through a MergeMem, the copy of the Phi +// that "emerges though" the base memory will be marked as excluding the alias types +// of the other (narrow-memory) copies which "emerged through" the narrow edges: +// +// Phi(U, MergeMem(: W, <8>: Y)) +// ==Ideal=> MergeMem(: Phi(U, W), Phi<8>(U, Y)) +// +// This strange "subtraction" effect is necessary to ensure IGVN convergence. +// (It is currently unimplemented.) As you can see, the resulting merge is +// actually a disjoint union of memory states, rather than an overlay. +// + +//------------------------------MergeMemNode----------------------------------- +Node* MergeMemNode::make_empty_memory() { + Node* empty_memory = (Node*) Compile::current()->top(); + assert(empty_memory->is_top(), "correct sentinel identity"); + return empty_memory; +} + +MergeMemNode::MergeMemNode(Node *new_base) : Node(1+Compile::AliasIdxRaw) { + init_class_id(Class_MergeMem); + // all inputs are nullified in Node::Node(int) + // set_input(0, NULL); // no control input + + // Initialize the edges uniformly to top, for starters. + Node* empty_mem = make_empty_memory(); + for (uint i = Compile::AliasIdxTop; i < req(); i++) { + init_req(i,empty_mem); + } + assert(empty_memory() == empty_mem, ""); + + if( new_base != NULL && new_base->is_MergeMem() ) { + MergeMemNode* mdef = new_base->as_MergeMem(); + assert(mdef->empty_memory() == empty_mem, "consistent sentinels"); + for (MergeMemStream mms(this, mdef); mms.next_non_empty2(); ) { + mms.set_memory(mms.memory2()); + } + assert(base_memory() == mdef->base_memory(), ""); + } else { + set_base_memory(new_base); + } +} + +// Make a new, untransformed MergeMem with the same base as 'mem'. +// If mem is itself a MergeMem, populate the result with the same edges. +MergeMemNode* MergeMemNode::make(Compile* C, Node* mem) { + return new(C, 1+Compile::AliasIdxRaw) MergeMemNode(mem); +} + +//------------------------------cmp-------------------------------------------- +uint MergeMemNode::hash() const { return NO_HASH; } +uint MergeMemNode::cmp( const Node &n ) const { + return (&n == this); // Always fail except on self +} + +//------------------------------Identity--------------------------------------- +Node* MergeMemNode::Identity(PhaseTransform *phase) { + // Identity if this merge point does not record any interesting memory + // disambiguations. + Node* base_mem = base_memory(); + Node* empty_mem = empty_memory(); + if (base_mem != empty_mem) { // Memory path is not dead? + for (uint i = Compile::AliasIdxRaw; i < req(); i++) { + Node* mem = in(i); + if (mem != empty_mem && mem != base_mem) { + return this; // Many memory splits; no change + } + } + } + return base_mem; // No memory splits; ID on the one true input +} + +//------------------------------Ideal------------------------------------------ +// This method is invoked recursively on chains of MergeMem nodes +Node *MergeMemNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Remove chain'd MergeMems + // + // This is delicate, because the each "in(i)" (i >= Raw) is interpreted + // relative to the "in(Bot)". Since we are patching both at the same time, + // we have to be careful to read each "in(i)" relative to the old "in(Bot)", + // but rewrite each "in(i)" relative to the new "in(Bot)". + Node *progress = NULL; + + + Node* old_base = base_memory(); + Node* empty_mem = empty_memory(); + if (old_base == empty_mem) + return NULL; // Dead memory path. + + MergeMemNode* old_mbase; + if (old_base != NULL && old_base->is_MergeMem()) + old_mbase = old_base->as_MergeMem(); + else + old_mbase = NULL; + Node* new_base = old_base; + + // simplify stacked MergeMems in base memory + if (old_mbase) new_base = old_mbase->base_memory(); + + // the base memory might contribute new slices beyond my req() + if (old_mbase) grow_to_match(old_mbase); + + // Look carefully at the base node if it is a phi. + PhiNode* phi_base; + if (new_base != NULL && new_base->is_Phi()) + phi_base = new_base->as_Phi(); + else + phi_base = NULL; + + Node* phi_reg = NULL; + uint phi_len = (uint)-1; + if (phi_base != NULL && !phi_base->is_copy()) { + // do not examine phi if degraded to a copy + phi_reg = phi_base->region(); + phi_len = phi_base->req(); + // see if the phi is unfinished + for (uint i = 1; i < phi_len; i++) { + if (phi_base->in(i) == NULL) { + // incomplete phi; do not look at it yet! + phi_reg = NULL; + phi_len = (uint)-1; + break; + } + } + } + + // Note: We do not call verify_sparse on entry, because inputs + // can normalize to the base_memory via subsume_node or similar + // mechanisms. This method repairs that damage. + + assert(!old_mbase || old_mbase->is_empty_memory(empty_mem), "consistent sentinels"); + + // Look at each slice. + for (uint i = Compile::AliasIdxRaw; i < req(); i++) { + Node* old_in = in(i); + // calculate the old memory value + Node* old_mem = old_in; + if (old_mem == empty_mem) old_mem = old_base; + assert(old_mem == memory_at(i), ""); + + // maybe update (reslice) the old memory value + + // simplify stacked MergeMems + Node* new_mem = old_mem; + MergeMemNode* old_mmem; + if (old_mem != NULL && old_mem->is_MergeMem()) + old_mmem = old_mem->as_MergeMem(); + else + old_mmem = NULL; + if (old_mmem == this) { + // This can happen if loops break up and safepoints disappear. + // A merge of BotPtr (default) with a RawPtr memory derived from a + // safepoint can be rewritten to a merge of the same BotPtr with + // the BotPtr phi coming into the loop. If that phi disappears + // also, we can end up with a self-loop of the mergemem. + // In general, if loops degenerate and memory effects disappear, + // a mergemem can be left looking at itself. This simply means + // that the mergemem's default should be used, since there is + // no longer any apparent effect on this slice. + // Note: If a memory slice is a MergeMem cycle, it is unreachable + // from start. Update the input to TOP. + new_mem = (new_base == this || new_base == empty_mem)? empty_mem : new_base; + } + else if (old_mmem != NULL) { + new_mem = old_mmem->memory_at(i); + } + // else preceeding memory was not a MergeMem + + // replace equivalent phis (unfortunately, they do not GVN together) + if (new_mem != NULL && new_mem != new_base && + new_mem->req() == phi_len && new_mem->in(0) == phi_reg) { + if (new_mem->is_Phi()) { + PhiNode* phi_mem = new_mem->as_Phi(); + for (uint i = 1; i < phi_len; i++) { + if (phi_base->in(i) != phi_mem->in(i)) { + phi_mem = NULL; + break; + } + } + if (phi_mem != NULL) { + // equivalent phi nodes; revert to the def + new_mem = new_base; + } + } + } + + // maybe store down a new value + Node* new_in = new_mem; + if (new_in == new_base) new_in = empty_mem; + + if (new_in != old_in) { + // Warning: Do not combine this "if" with the previous "if" + // A memory slice might have be be rewritten even if it is semantically + // unchanged, if the base_memory value has changed. + set_req(i, new_in); + progress = this; // Report progress + } + } + + if (new_base != old_base) { + set_req(Compile::AliasIdxBot, new_base); + // Don't use set_base_memory(new_base), because we need to update du. + assert(base_memory() == new_base, ""); + progress = this; + } + + if( base_memory() == this ) { + // a self cycle indicates this memory path is dead + set_req(Compile::AliasIdxBot, empty_mem); + } + + // Resolve external cycles by calling Ideal on a MergeMem base_memory + // Recursion must occur after the self cycle check above + if( base_memory()->is_MergeMem() ) { + MergeMemNode *new_mbase = base_memory()->as_MergeMem(); + Node *m = phase->transform(new_mbase); // Rollup any cycles + if( m != NULL && (m->is_top() || + m->is_MergeMem() && m->as_MergeMem()->base_memory() == empty_mem) ) { + // propagate rollup of dead cycle to self + set_req(Compile::AliasIdxBot, empty_mem); + } + } + + if( base_memory() == empty_mem ) { + progress = this; + // Cut inputs during Parse phase only. + // During Optimize phase a dead MergeMem node will be subsumed by Top. + if( !can_reshape ) { + for (uint i = Compile::AliasIdxRaw; i < req(); i++) { + if( in(i) != empty_mem ) { set_req(i, empty_mem); } + } + } + } + + if( !progress && base_memory()->is_Phi() && can_reshape ) { + // Check if PhiNode::Ideal's "Split phis through memory merges" + // transform should be attempted. Look for this->phi->this cycle. + uint merge_width = req(); + if (merge_width > Compile::AliasIdxRaw) { + PhiNode* phi = base_memory()->as_Phi(); + for( uint i = 1; i < phi->req(); ++i ) {// For all paths in + if (phi->in(i) == this) { + phase->is_IterGVN()->_worklist.push(phi); + break; + } + } + } + } + + assert(verify_sparse(), "please, no dups of base"); + return progress; +} + +//-------------------------set_base_memory------------------------------------- +void MergeMemNode::set_base_memory(Node *new_base) { + Node* empty_mem = empty_memory(); + set_req(Compile::AliasIdxBot, new_base); + assert(memory_at(req()) == new_base, "must set default memory"); + // Clear out other occurrences of new_base: + if (new_base != empty_mem) { + for (uint i = Compile::AliasIdxRaw; i < req(); i++) { + if (in(i) == new_base) set_req(i, empty_mem); + } + } +} + +//------------------------------out_RegMask------------------------------------ +const RegMask &MergeMemNode::out_RegMask() const { + return RegMask::Empty; +} + +//------------------------------dump_spec-------------------------------------- +#ifndef PRODUCT +void MergeMemNode::dump_spec(outputStream *st) const { + st->print(" {"); + Node* base_mem = base_memory(); + for( uint i = Compile::AliasIdxRaw; i < req(); i++ ) { + Node* mem = memory_at(i); + if (mem == base_mem) { st->print(" -"); continue; } + st->print( " N%d:", mem->_idx ); + Compile::current()->get_adr_type(i)->dump_on(st); + } + st->print(" }"); +} +#endif // !PRODUCT + + +#ifdef ASSERT +static bool might_be_same(Node* a, Node* b) { + if (a == b) return true; + if (!(a->is_Phi() || b->is_Phi())) return false; + // phis shift around during optimization + return true; // pretty stupid... +} + +// verify a narrow slice (either incoming or outgoing) +static void verify_memory_slice(const MergeMemNode* m, int alias_idx, Node* n) { + if (!VerifyAliases) return; // don't bother to verify unless requested + if (is_error_reported()) return; // muzzle asserts when debugging an error + if (Node::in_dump()) return; // muzzle asserts when printing + assert(alias_idx >= Compile::AliasIdxRaw, "must not disturb base_memory or sentinel"); + assert(n != NULL, ""); + // Elide intervening MergeMem's + while (n->is_MergeMem()) { + n = n->as_MergeMem()->memory_at(alias_idx); + } + Compile* C = Compile::current(); + const TypePtr* n_adr_type = n->adr_type(); + if (n == m->empty_memory()) { + // Implicit copy of base_memory() + } else if (n_adr_type != TypePtr::BOTTOM) { + assert(n_adr_type != NULL, "new memory must have a well-defined adr_type"); + assert(C->must_alias(n_adr_type, alias_idx), "new memory must match selected slice"); + } else { + // A few places like make_runtime_call "know" that VM calls are narrow, + // and can be used to update only the VM bits stored as TypeRawPtr::BOTTOM. + bool expected_wide_mem = false; + if (n == m->base_memory()) { + expected_wide_mem = true; + } else if (alias_idx == Compile::AliasIdxRaw || + n == m->memory_at(Compile::AliasIdxRaw)) { + expected_wide_mem = true; + } else if (!C->alias_type(alias_idx)->is_rewritable()) { + // memory can "leak through" calls on channels that + // are write-once. Allow this also. + expected_wide_mem = true; + } + assert(expected_wide_mem, "expected narrow slice replacement"); + } +} +#else // !ASSERT +#define verify_memory_slice(m,i,n) (0) // PRODUCT version is no-op +#endif + + +//-----------------------------memory_at--------------------------------------- +Node* MergeMemNode::memory_at(uint alias_idx) const { + assert(alias_idx >= Compile::AliasIdxRaw || + alias_idx == Compile::AliasIdxBot && Compile::current()->AliasLevel() == 0, + "must avoid base_memory and AliasIdxTop"); + + // Otherwise, it is a narrow slice. + Node* n = alias_idx < req() ? in(alias_idx) : empty_memory(); + Compile *C = Compile::current(); + if (is_empty_memory(n)) { + // the array is sparse; empty slots are the "top" node + n = base_memory(); + assert(Node::in_dump() + || n == NULL || n->bottom_type() == Type::TOP + || n->adr_type() == TypePtr::BOTTOM + || n->adr_type() == TypeRawPtr::BOTTOM + || Compile::current()->AliasLevel() == 0, + "must be a wide memory"); + // AliasLevel == 0 if we are organizing the memory states manually. + // See verify_memory_slice for comments on TypeRawPtr::BOTTOM. + } else { + // make sure the stored slice is sane + #ifdef ASSERT + if (is_error_reported() || Node::in_dump()) { + } else if (might_be_same(n, base_memory())) { + // Give it a pass: It is a mostly harmless repetition of the base. + // This can arise normally from node subsumption during optimization. + } else { + verify_memory_slice(this, alias_idx, n); + } + #endif + } + return n; +} + +//---------------------------set_memory_at------------------------------------- +void MergeMemNode::set_memory_at(uint alias_idx, Node *n) { + verify_memory_slice(this, alias_idx, n); + Node* empty_mem = empty_memory(); + if (n == base_memory()) n = empty_mem; // collapse default + uint need_req = alias_idx+1; + if (req() < need_req) { + if (n == empty_mem) return; // already the default, so do not grow me + // grow the sparse array + do { + add_req(empty_mem); + } while (req() < need_req); + } + set_req( alias_idx, n ); +} + + + +//--------------------------iteration_setup------------------------------------ +void MergeMemNode::iteration_setup(const MergeMemNode* other) { + if (other != NULL) { + grow_to_match(other); + // invariant: the finite support of mm2 is within mm->req() + #ifdef ASSERT + for (uint i = req(); i < other->req(); i++) { + assert(other->is_empty_memory(other->in(i)), "slice left uncovered"); + } + #endif + } + // Replace spurious copies of base_memory by top. + Node* base_mem = base_memory(); + if (base_mem != NULL && !base_mem->is_top()) { + for (uint i = Compile::AliasIdxBot+1, imax = req(); i < imax; i++) { + if (in(i) == base_mem) + set_req(i, empty_memory()); + } + } +} + +//---------------------------grow_to_match------------------------------------- +void MergeMemNode::grow_to_match(const MergeMemNode* other) { + Node* empty_mem = empty_memory(); + assert(other->is_empty_memory(empty_mem), "consistent sentinels"); + // look for the finite support of the other memory + for (uint i = other->req(); --i >= req(); ) { + if (other->in(i) != empty_mem) { + uint new_len = i+1; + while (req() < new_len) add_req(empty_mem); + break; + } + } +} + +//---------------------------verify_sparse------------------------------------- +#ifndef PRODUCT +bool MergeMemNode::verify_sparse() const { + assert(is_empty_memory(make_empty_memory()), "sane sentinel"); + Node* base_mem = base_memory(); + // The following can happen in degenerate cases, since empty==top. + if (is_empty_memory(base_mem)) return true; + for (uint i = Compile::AliasIdxRaw; i < req(); i++) { + assert(in(i) != NULL, "sane slice"); + if (in(i) == base_mem) return false; // should have been the sentinel value! + } + return true; +} + +bool MergeMemStream::match_memory(Node* mem, const MergeMemNode* mm, int idx) { + Node* n; + n = mm->in(idx); + if (mem == n) return true; // might be empty_memory() + n = (idx == Compile::AliasIdxBot)? mm->base_memory(): mm->memory_at(idx); + if (mem == n) return true; + while (n->is_Phi() && (n = n->as_Phi()->is_copy()) != NULL) { + if (mem == n) return true; + if (n == NULL) break; + } + return false; +} +#endif // !PRODUCT diff --git a/hotspot/src/share/vm/opto/memnode.hpp b/hotspot/src/share/vm/opto/memnode.hpp new file mode 100644 index 00000000000..989e255a9b2 --- /dev/null +++ b/hotspot/src/share/vm/opto/memnode.hpp @@ -0,0 +1,1062 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +class MultiNode; +class PhaseCCP; +class PhaseTransform; + +//------------------------------MemNode---------------------------------------- +// Load or Store, possibly throwing a NULL pointer exception +class MemNode : public Node { +protected: +#ifdef ASSERT + const TypePtr* _adr_type; // What kind of memory is being addressed? +#endif + virtual uint size_of() const; // Size is bigger (ASSERT only) +public: + enum { Control, // When is it safe to do this load? + Memory, // Chunk of memory is being loaded from + Address, // Actually address, derived from base + ValueIn, // Value to store + OopStore // Preceeding oop store, only in StoreCM + }; +protected: + MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at ) + : Node(c0,c1,c2 ) { + init_class_id(Class_Mem); + debug_only(_adr_type=at; adr_type();) + } + MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at, Node *c3 ) + : Node(c0,c1,c2,c3) { + init_class_id(Class_Mem); + debug_only(_adr_type=at; adr_type();) + } + MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at, Node *c3, Node *c4) + : Node(c0,c1,c2,c3,c4) { + init_class_id(Class_Mem); + debug_only(_adr_type=at; adr_type();) + } + + // Helpers for the optimizer. Documented in memnode.cpp. + static bool detect_ptr_independence(Node* p1, AllocateNode* a1, + Node* p2, AllocateNode* a2, + PhaseTransform* phase); + static bool adr_phi_is_loop_invariant(Node* adr_phi, Node* cast); + +public: + // This one should probably be a phase-specific function: + static bool detect_dominating_control(Node* dom, Node* sub); + + // Is this Node a MemNode or some descendent? Default is YES. + virtual Node *Ideal_DU_postCCP( PhaseCCP *ccp ); + + virtual const class TypePtr *adr_type() const; // returns bottom_type of address + + // Shared code for Ideal methods: + Node *Ideal_common(PhaseGVN *phase, bool can_reshape); // Return -1 for short-circuit NULL. + + // Helper function for adr_type() implementations. + static const TypePtr* calculate_adr_type(const Type* t, const TypePtr* cross_check = NULL); + + // Raw access function, to allow copying of adr_type efficiently in + // product builds and retain the debug info for debug builds. + const TypePtr *raw_adr_type() const { +#ifdef ASSERT + return _adr_type; +#else + return 0; +#endif + } + + // Map a load or store opcode to its corresponding store opcode. + // (Return -1 if unknown.) + virtual int store_Opcode() const { return -1; } + + // What is the type of the value in memory? (T_VOID mean "unspecified".) + virtual BasicType memory_type() const = 0; + virtual int memory_size() const { return type2aelembytes[memory_type()]; } + + // Search through memory states which precede this node (load or store). + // Look for an exact match for the address, with no intervening + // aliased stores. + Node* find_previous_store(PhaseTransform* phase); + + // Can this node (load or store) accurately see a stored value in + // the given memory state? (The state may or may not be in(Memory).) + Node* can_see_stored_value(Node* st, PhaseTransform* phase) const; + +#ifndef PRODUCT + static void dump_adr_type(const Node* mem, const TypePtr* adr_type, outputStream *st); + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------LoadNode--------------------------------------- +// Load value; requires Memory and Address +class LoadNode : public MemNode { +protected: + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger + const Type* const _type; // What kind of value is loaded? +public: + + LoadNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *rt ) + : MemNode(c,mem,adr,at), _type(rt) { + init_class_id(Class_Load); + } + + // Polymorphic factory method: + static LoadNode* make( Compile *C, Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *rt, BasicType bt ); + + virtual uint hash() const; // Check the type + + // Handle algebraic identities here. If we have an identity, return the Node + // we are equivalent to. We look for Load of a Store. + virtual Node *Identity( PhaseTransform *phase ); + + // If the load is from Field memory and the pointer is non-null, we can + // zero out the control input. + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + + // Compute a new Type for this node. Basically we just do the pre-check, + // then call the virtual add() to set the type. + virtual const Type *Value( PhaseTransform *phase ) const; + + virtual uint ideal_reg() const; + virtual const Type *bottom_type() const; + // Following method is copied from TypeNode: + void set_type(const Type* t) { + assert(t != NULL, "sanity"); + debug_only(uint check_hash = (VerifyHashTableKeys && _hash_lock) ? hash() : NO_HASH); + *(const Type**)&_type = t; // cast away const-ness + // If this node is in the hash table, make sure it doesn't need a rehash. + assert(check_hash == NO_HASH || check_hash == hash(), "type change must preserve hash code"); + } + const Type* type() const { assert(_type != NULL, "sanity"); return _type; }; + + // Do not match memory edge + virtual uint match_edge(uint idx) const; + + // Map a load opcode to its corresponding store opcode. + virtual int store_Opcode() const = 0; + +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +protected: + const Type* load_array_final_field(const TypeKlassPtr *tkls, + ciKlass* klass) const; +}; + +//------------------------------LoadBNode-------------------------------------- +// Load a byte (8bits signed) from memory +class LoadBNode : public LoadNode { +public: + LoadBNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::BYTE ) + : LoadNode(c,mem,adr,at,ti) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual int store_Opcode() const { return Op_StoreB; } + virtual BasicType memory_type() const { return T_BYTE; } +}; + +//------------------------------LoadCNode-------------------------------------- +// Load a char (16bits unsigned) from memory +class LoadCNode : public LoadNode { +public: + LoadCNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::CHAR ) + : LoadNode(c,mem,adr,at,ti) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual int store_Opcode() const { return Op_StoreC; } + virtual BasicType memory_type() const { return T_CHAR; } +}; + +//------------------------------LoadINode-------------------------------------- +// Load an integer from memory +class LoadINode : public LoadNode { +public: + LoadINode( Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::INT ) + : LoadNode(c,mem,adr,at,ti) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual int store_Opcode() const { return Op_StoreI; } + virtual BasicType memory_type() const { return T_INT; } +}; + +//------------------------------LoadRangeNode---------------------------------- +// Load an array length from the array +class LoadRangeNode : public LoadINode { +public: + LoadRangeNode( Node *c, Node *mem, Node *adr, const TypeInt *ti = TypeInt::POS ) + : LoadINode(c,mem,adr,TypeAryPtr::RANGE,ti) {} + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); +}; + +//------------------------------LoadLNode-------------------------------------- +// Load a long from memory +class LoadLNode : public LoadNode { + virtual uint hash() const { return LoadNode::hash() + _require_atomic_access; } + virtual uint cmp( const Node &n ) const { + return _require_atomic_access == ((LoadLNode&)n)._require_atomic_access + && LoadNode::cmp(n); + } + virtual uint size_of() const { return sizeof(*this); } + const bool _require_atomic_access; // is piecewise load forbidden? + +public: + LoadLNode( Node *c, Node *mem, Node *adr, const TypePtr* at, + const TypeLong *tl = TypeLong::LONG, + bool require_atomic_access = false ) + : LoadNode(c,mem,adr,at,tl) + , _require_atomic_access(require_atomic_access) + {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegL; } + virtual int store_Opcode() const { return Op_StoreL; } + virtual BasicType memory_type() const { return T_LONG; } + bool require_atomic_access() { return _require_atomic_access; } + static LoadLNode* make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt); +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const { + LoadNode::dump_spec(st); + if (_require_atomic_access) st->print(" Atomic!"); + } +#endif +}; + +//------------------------------LoadL_unalignedNode---------------------------- +// Load a long from unaligned memory +class LoadL_unalignedNode : public LoadLNode { +public: + LoadL_unalignedNode( Node *c, Node *mem, Node *adr, const TypePtr* at ) + : LoadLNode(c,mem,adr,at) {} + virtual int Opcode() const; +}; + +//------------------------------LoadFNode-------------------------------------- +// Load a float (64 bits) from memory +class LoadFNode : public LoadNode { +public: + LoadFNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *t = Type::FLOAT ) + : LoadNode(c,mem,adr,at,t) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegF; } + virtual int store_Opcode() const { return Op_StoreF; } + virtual BasicType memory_type() const { return T_FLOAT; } +}; + +//------------------------------LoadDNode-------------------------------------- +// Load a double (64 bits) from memory +class LoadDNode : public LoadNode { +public: + LoadDNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *t = Type::DOUBLE ) + : LoadNode(c,mem,adr,at,t) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegD; } + virtual int store_Opcode() const { return Op_StoreD; } + virtual BasicType memory_type() const { return T_DOUBLE; } +}; + +//------------------------------LoadD_unalignedNode---------------------------- +// Load a double from unaligned memory +class LoadD_unalignedNode : public LoadDNode { +public: + LoadD_unalignedNode( Node *c, Node *mem, Node *adr, const TypePtr* at ) + : LoadDNode(c,mem,adr,at) {} + virtual int Opcode() const; +}; + +//------------------------------LoadPNode-------------------------------------- +// Load a pointer from memory (either object or array) +class LoadPNode : public LoadNode { +public: + LoadPNode( Node *c, Node *mem, Node *adr, const TypePtr *at, const TypePtr* t ) + : LoadNode(c,mem,adr,at,t) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegP; } + virtual int store_Opcode() const { return Op_StoreP; } + virtual BasicType memory_type() const { return T_ADDRESS; } + // depends_only_on_test is almost always true, and needs to be almost always + // true to enable key hoisting & commoning optimizations. However, for the + // special case of RawPtr loads from TLS top & end, the control edge carries + // the dependence preventing hoisting past a Safepoint instead of the memory + // edge. (An unfortunate consequence of having Safepoints not set Raw + // Memory; itself an unfortunate consequence of having Nodes which produce + // results (new raw memory state) inside of loops preventing all manner of + // other optimizations). Basically, it's ugly but so is the alternative. + // See comment in macro.cpp, around line 125 expand_allocate_common(). + virtual bool depends_only_on_test() const { return adr_type() != TypeRawPtr::BOTTOM; } +}; + +//------------------------------LoadKlassNode---------------------------------- +// Load a Klass from an object +class LoadKlassNode : public LoadPNode { +public: + LoadKlassNode( Node *c, Node *mem, Node *adr, const TypePtr *at, const TypeKlassPtr *tk = TypeKlassPtr::OBJECT ) + : LoadPNode(c,mem,adr,at,tk) {} + virtual int Opcode() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Identity( PhaseTransform *phase ); + virtual bool depends_only_on_test() const { return true; } +}; + +//------------------------------LoadSNode-------------------------------------- +// Load a short (16bits signed) from memory +class LoadSNode : public LoadNode { +public: + LoadSNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::SHORT ) + : LoadNode(c,mem,adr,at,ti) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual int store_Opcode() const { return Op_StoreC; } + virtual BasicType memory_type() const { return T_SHORT; } +}; + +//------------------------------StoreNode-------------------------------------- +// Store value; requires Store, Address and Value +class StoreNode : public MemNode { +protected: + virtual uint cmp( const Node &n ) const; + virtual bool depends_only_on_test() const { return false; } + + Node *Ideal_masked_input (PhaseGVN *phase, uint mask); + Node *Ideal_sign_extended_input(PhaseGVN *phase, int num_bits); + +public: + StoreNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) + : MemNode(c,mem,adr,at,val) { + init_class_id(Class_Store); + } + StoreNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Node *oop_store ) + : MemNode(c,mem,adr,at,val,oop_store) { + init_class_id(Class_Store); + } + + // Polymorphic factory method: + static StoreNode* make( Compile *C, Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, BasicType bt ); + + virtual uint hash() const; // Check the type + + // If the store is to Field memory and the pointer is non-null, we can + // zero out the control input. + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + + // Compute a new Type for this node. Basically we just do the pre-check, + // then call the virtual add() to set the type. + virtual const Type *Value( PhaseTransform *phase ) const; + + // Check for identity function on memory (Load then Store at same address) + virtual Node *Identity( PhaseTransform *phase ); + + // Do not match memory edge + virtual uint match_edge(uint idx) const; + + virtual const Type *bottom_type() const; // returns Type::MEMORY + + // Map a store opcode to its corresponding own opcode, trivially. + virtual int store_Opcode() const { return Opcode(); } + + // have all possible loads of the value stored been optimized away? + bool value_never_loaded(PhaseTransform *phase) const; +}; + +//------------------------------StoreBNode------------------------------------- +// Store byte to memory +class StoreBNode : public StoreNode { +public: + StoreBNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual BasicType memory_type() const { return T_BYTE; } +}; + +//------------------------------StoreCNode------------------------------------- +// Store char/short to memory +class StoreCNode : public StoreNode { +public: + StoreCNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual BasicType memory_type() const { return T_CHAR; } +}; + +//------------------------------StoreINode------------------------------------- +// Store int to memory +class StoreINode : public StoreNode { +public: + StoreINode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual BasicType memory_type() const { return T_INT; } +}; + +//------------------------------StoreLNode------------------------------------- +// Store long to memory +class StoreLNode : public StoreNode { + virtual uint hash() const { return StoreNode::hash() + _require_atomic_access; } + virtual uint cmp( const Node &n ) const { + return _require_atomic_access == ((StoreLNode&)n)._require_atomic_access + && StoreNode::cmp(n); + } + virtual uint size_of() const { return sizeof(*this); } + const bool _require_atomic_access; // is piecewise store forbidden? + +public: + StoreLNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, + bool require_atomic_access = false ) + : StoreNode(c,mem,adr,at,val) + , _require_atomic_access(require_atomic_access) + {} + virtual int Opcode() const; + virtual BasicType memory_type() const { return T_LONG; } + bool require_atomic_access() { return _require_atomic_access; } + static StoreLNode* make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val); +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const { + StoreNode::dump_spec(st); + if (_require_atomic_access) st->print(" Atomic!"); + } +#endif +}; + +//------------------------------StoreFNode------------------------------------- +// Store float to memory +class StoreFNode : public StoreNode { +public: + StoreFNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual BasicType memory_type() const { return T_FLOAT; } +}; + +//------------------------------StoreDNode------------------------------------- +// Store double to memory +class StoreDNode : public StoreNode { +public: + StoreDNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual BasicType memory_type() const { return T_DOUBLE; } +}; + +//------------------------------StorePNode------------------------------------- +// Store pointer to memory +class StorePNode : public StoreNode { +public: + StorePNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual BasicType memory_type() const { return T_ADDRESS; } +}; + +//------------------------------StoreCMNode----------------------------------- +// Store card-mark byte to memory for CM +// The last StoreCM before a SafePoint must be preserved and occur after its "oop" store +// Preceeding equivalent StoreCMs may be eliminated. +class StoreCMNode : public StoreNode { +public: + StoreCMNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Node *oop_store ) : StoreNode(c,mem,adr,at,val,oop_store) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual BasicType memory_type() const { return T_VOID; } // unspecific +}; + +//------------------------------LoadPLockedNode--------------------------------- +// Load-locked a pointer from memory (either object or array). +// On Sparc & Intel this is implemented as a normal pointer load. +// On PowerPC and friends it's a real load-locked. +class LoadPLockedNode : public LoadPNode { +public: + LoadPLockedNode( Node *c, Node *mem, Node *adr ) + : LoadPNode(c,mem,adr,TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_StorePConditional; } + virtual bool depends_only_on_test() const { return true; } +}; + +//------------------------------LoadLLockedNode--------------------------------- +// Load-locked a pointer from memory (either object or array). +// On Sparc & Intel this is implemented as a normal long load. +class LoadLLockedNode : public LoadLNode { +public: + LoadLLockedNode( Node *c, Node *mem, Node *adr ) + : LoadLNode(c,mem,adr,TypeRawPtr::BOTTOM, TypeLong::LONG) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_StoreLConditional; } +}; + +//------------------------------SCMemProjNode--------------------------------------- +// This class defines a projection of the memory state of a store conditional node. +// These nodes return a value, but also update memory. +class SCMemProjNode : public ProjNode { +public: + enum {SCMEMPROJCON = (uint)-2}; + SCMemProjNode( Node *src) : ProjNode( src, SCMEMPROJCON) { } + virtual int Opcode() const; + virtual bool is_CFG() const { return false; } + virtual const Type *bottom_type() const {return Type::MEMORY;} + virtual const TypePtr *adr_type() const { return in(0)->in(MemNode::Memory)->adr_type();} + virtual uint ideal_reg() const { return 0;} // memory projections don't have a register + virtual const Type *Value( PhaseTransform *phase ) const; +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const {}; +#endif +}; + +//------------------------------LoadStoreNode--------------------------- +class LoadStoreNode : public Node { +public: + enum { + ExpectedIn = MemNode::ValueIn+1 // One more input than MemNode + }; + LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, Node *ex); + virtual bool depends_only_on_test() const { return false; } + virtual const Type *bottom_type() const { return TypeInt::BOOL; } + virtual uint ideal_reg() const { return Op_RegI; } + virtual uint match_edge(uint idx) const { return idx == MemNode::Address || idx == MemNode::ValueIn; } +}; + +//------------------------------StorePConditionalNode--------------------------- +// Conditionally store pointer to memory, if no change since prior +// load-locked. Sets flags for success or failure of the store. +class StorePConditionalNode : public LoadStoreNode { +public: + StorePConditionalNode( Node *c, Node *mem, Node *adr, Node *val, Node *ll ) : LoadStoreNode(c, mem, adr, val, ll) { } + virtual int Opcode() const; + // Produces flags + virtual uint ideal_reg() const { return Op_RegFlags; } +}; + +//------------------------------StoreLConditionalNode--------------------------- +// Conditionally store long to memory, if no change since prior +// load-locked. Sets flags for success or failure of the store. +class StoreLConditionalNode : public LoadStoreNode { +public: + StoreLConditionalNode( Node *c, Node *mem, Node *adr, Node *val, Node *ll ) : LoadStoreNode(c, mem, adr, val, ll) { } + virtual int Opcode() const; +}; + + +//------------------------------CompareAndSwapLNode--------------------------- +class CompareAndSwapLNode : public LoadStoreNode { +public: + CompareAndSwapLNode( Node *c, Node *mem, Node *adr, Node *val, Node *ex) : LoadStoreNode(c, mem, adr, val, ex) { } + virtual int Opcode() const; +}; + + +//------------------------------CompareAndSwapINode--------------------------- +class CompareAndSwapINode : public LoadStoreNode { +public: + CompareAndSwapINode( Node *c, Node *mem, Node *adr, Node *val, Node *ex) : LoadStoreNode(c, mem, adr, val, ex) { } + virtual int Opcode() const; +}; + + +//------------------------------CompareAndSwapPNode--------------------------- +class CompareAndSwapPNode : public LoadStoreNode { +public: + CompareAndSwapPNode( Node *c, Node *mem, Node *adr, Node *val, Node *ex) : LoadStoreNode(c, mem, adr, val, ex) { } + virtual int Opcode() const; +}; + +//------------------------------ClearArray------------------------------------- +class ClearArrayNode: public Node { +public: + ClearArrayNode( Node *ctrl, Node *arymem, Node *word_cnt, Node *base ) : Node(ctrl,arymem,word_cnt,base) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::MEMORY; } + // ClearArray modifies array elements, and so affects only the + // array memory addressed by the bottom_type of its base address. + virtual const class TypePtr *adr_type() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual uint match_edge(uint idx) const; + + // Clear the given area of an object or array. + // The start offset must always be aligned mod BytesPerInt. + // The end offset must always be aligned mod BytesPerLong. + // Return the new memory. + static Node* clear_memory(Node* control, Node* mem, Node* dest, + intptr_t start_offset, + intptr_t end_offset, + PhaseGVN* phase); + static Node* clear_memory(Node* control, Node* mem, Node* dest, + intptr_t start_offset, + Node* end_offset, + PhaseGVN* phase); + static Node* clear_memory(Node* control, Node* mem, Node* dest, + Node* start_offset, + Node* end_offset, + PhaseGVN* phase); +}; + +//------------------------------StrComp------------------------------------- +class StrCompNode: public Node { +public: + StrCompNode(Node *control, + Node* char_array_mem, + Node* value_mem, + Node* count_mem, + Node* offset_mem, + Node* s1, Node* s2): Node(control, + char_array_mem, + value_mem, + count_mem, + offset_mem, + s1, s2) {}; + virtual int Opcode() const; + virtual bool depends_only_on_test() const { return false; } + virtual const Type* bottom_type() const { return TypeInt::INT; } + // a StrCompNode (conservatively) aliases with everything: + virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual uint match_edge(uint idx) const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); +}; + +//------------------------------MemBar----------------------------------------- +// There are different flavors of Memory Barriers to match the Java Memory +// Model. Monitor-enter and volatile-load act as Aquires: no following ref +// can be moved to before them. We insert a MemBar-Acquire after a FastLock or +// volatile-load. Monitor-exit and volatile-store act as Release: no +// preceeding ref can be moved to after them. We insert a MemBar-Release +// before a FastUnlock or volatile-store. All volatiles need to be +// serialized, so we follow all volatile-stores with a MemBar-Volatile to +// seperate it from any following volatile-load. +class MemBarNode: public MultiNode { + virtual uint hash() const ; // { return NO_HASH; } + virtual uint cmp( const Node &n ) const ; // Always fail, except on self + + virtual uint size_of() const { return sizeof(*this); } + // Memory type this node is serializing. Usually either rawptr or bottom. + const TypePtr* _adr_type; + +public: + enum { + Precedent = TypeFunc::Parms // optional edge to force precedence + }; + MemBarNode(Compile* C, int alias_idx, Node* precedent); + virtual int Opcode() const = 0; + virtual const class TypePtr *adr_type() const { return _adr_type; } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual uint match_edge(uint idx) const { return 0; } + virtual const Type *bottom_type() const { return TypeTuple::MEMBAR; } + virtual Node *match( const ProjNode *proj, const Matcher *m ); + // Factory method. Builds a wide or narrow membar. + // Optional 'precedent' becomes an extra edge if not null. + static MemBarNode* make(Compile* C, int opcode, + int alias_idx = Compile::AliasIdxBot, + Node* precedent = NULL); +}; + +// "Acquire" - no following ref can move before (but earlier refs can +// follow, like an early Load stalled in cache). Requires multi-cpu +// visibility. Inserted after a volatile load or FastLock. +class MemBarAcquireNode: public MemBarNode { +public: + MemBarAcquireNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; +}; + +// "Release" - no earlier ref can move after (but later refs can move +// up, like a speculative pipelined cache-hitting Load). Requires +// multi-cpu visibility. Inserted before a volatile store or FastUnLock. +class MemBarReleaseNode: public MemBarNode { +public: + MemBarReleaseNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; +}; + +// Ordering between a volatile store and a following volatile load. +// Requires multi-CPU visibility? +class MemBarVolatileNode: public MemBarNode { +public: + MemBarVolatileNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; +}; + +// Ordering within the same CPU. Used to order unsafe memory references +// inside the compiler when we lack alias info. Not needed "outside" the +// compiler because the CPU does all the ordering for us. +class MemBarCPUOrderNode: public MemBarNode { +public: + MemBarCPUOrderNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return 0; } // not matched in the AD file +}; + +// Isolation of object setup after an AllocateNode and before next safepoint. +// (See comment in memnode.cpp near InitializeNode::InitializeNode for semantics.) +class InitializeNode: public MemBarNode { + friend class AllocateNode; + + bool _is_complete; + +public: + enum { + Control = TypeFunc::Control, + Memory = TypeFunc::Memory, // MergeMem for states affected by this op + RawAddress = TypeFunc::Parms+0, // the newly-allocated raw address + RawStores = TypeFunc::Parms+1 // zero or more stores (or TOP) + }; + + InitializeNode(Compile* C, int adr_type, Node* rawoop); + virtual int Opcode() const; + virtual uint size_of() const { return sizeof(*this); } + virtual uint ideal_reg() const { return 0; } // not matched in the AD file + virtual const RegMask &in_RegMask(uint) const; // mask for RawAddress + + // Manage incoming memory edges via a MergeMem on in(Memory): + Node* memory(uint alias_idx); + + // The raw memory edge coming directly from the Allocation. + // The contents of this memory are *always* all-zero-bits. + Node* zero_memory() { return memory(Compile::AliasIdxRaw); } + + // Return the corresponding allocation for this initialization (or null if none). + // (Note: Both InitializeNode::allocation and AllocateNode::initialization + // are defined in graphKit.cpp, which sets up the bidirectional relation.) + AllocateNode* allocation(); + + // Anything other than zeroing in this init? + bool is_non_zero(); + + // An InitializeNode must completed before macro expansion is done. + // Completion requires that the AllocateNode must be followed by + // initialization of the new memory to zero, then to any initializers. + bool is_complete() { return _is_complete; } + + // Mark complete. (Must not yet be complete.) + void set_complete(PhaseGVN* phase); + +#ifdef ASSERT + // ensure all non-degenerate stores are ordered and non-overlapping + bool stores_are_sane(PhaseTransform* phase); +#endif //ASSERT + + // See if this store can be captured; return offset where it initializes. + // Return 0 if the store cannot be moved (any sort of problem). + intptr_t can_capture_store(StoreNode* st, PhaseTransform* phase); + + // Capture another store; reformat it to write my internal raw memory. + // Return the captured copy, else NULL if there is some sort of problem. + Node* capture_store(StoreNode* st, intptr_t start, PhaseTransform* phase); + + // Find captured store which corresponds to the range [start..start+size). + // Return my own memory projection (meaning the initial zero bits) + // if there is no such store. Return NULL if there is a problem. + Node* find_captured_store(intptr_t start, int size_in_bytes, PhaseTransform* phase); + + // Called when the associated AllocateNode is expanded into CFG. + Node* complete_stores(Node* rawctl, Node* rawmem, Node* rawptr, + intptr_t header_size, Node* size_in_bytes, + PhaseGVN* phase); + + private: + void remove_extra_zeroes(); + + // Find out where a captured store should be placed (or already is placed). + int captured_store_insertion_point(intptr_t start, int size_in_bytes, + PhaseTransform* phase); + + static intptr_t get_store_offset(Node* st, PhaseTransform* phase); + + Node* make_raw_address(intptr_t offset, PhaseTransform* phase); + + bool detect_init_independence(Node* n, bool st_is_pinned, int& count); + + void coalesce_subword_stores(intptr_t header_size, Node* size_in_bytes, + PhaseGVN* phase); + + intptr_t find_next_fullword_store(uint i, PhaseGVN* phase); +}; + +//------------------------------MergeMem--------------------------------------- +// (See comment in memnode.cpp near MergeMemNode::MergeMemNode for semantics.) +class MergeMemNode: public Node { + virtual uint hash() const ; // { return NO_HASH; } + virtual uint cmp( const Node &n ) const ; // Always fail, except on self + friend class MergeMemStream; + MergeMemNode(Node* def); // clients use MergeMemNode::make + +public: + // If the input is a whole memory state, clone it with all its slices intact. + // Otherwise, make a new memory state with just that base memory input. + // In either case, the result is a newly created MergeMem. + static MergeMemNode* make(Compile* C, Node* base_memory); + + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual uint ideal_reg() const { return NotAMachineReg; } + virtual uint match_edge(uint idx) const { return 0; } + virtual const RegMask &out_RegMask() const; + virtual const Type *bottom_type() const { return Type::MEMORY; } + virtual const TypePtr *adr_type() const { return TypePtr::BOTTOM; } + // sparse accessors + // Fetch the previously stored "set_memory_at", or else the base memory. + // (Caller should clone it if it is a phi-nest.) + Node* memory_at(uint alias_idx) const; + // set the memory, regardless of its previous value + void set_memory_at(uint alias_idx, Node* n); + // the "base" is the memory that provides the non-finite support + Node* base_memory() const { return in(Compile::AliasIdxBot); } + // warning: setting the base can implicitly set any of the other slices too + void set_base_memory(Node* def); + // sentinel value which denotes a copy of the base memory: + Node* empty_memory() const { return in(Compile::AliasIdxTop); } + static Node* make_empty_memory(); // where the sentinel comes from + bool is_empty_memory(Node* n) const { assert((n == empty_memory()) == n->is_top(), "sanity"); return n->is_top(); } + // hook for the iterator, to perform any necessary setup + void iteration_setup(const MergeMemNode* other = NULL); + // push sentinels until I am at least as long as the other (semantic no-op) + void grow_to_match(const MergeMemNode* other); + bool verify_sparse() const PRODUCT_RETURN0; +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +class MergeMemStream : public StackObj { + private: + MergeMemNode* _mm; + const MergeMemNode* _mm2; // optional second guy, contributes non-empty iterations + Node* _mm_base; // loop-invariant base memory of _mm + int _idx; + int _cnt; + Node* _mem; + Node* _mem2; + int _cnt2; + + void init(MergeMemNode* mm, const MergeMemNode* mm2 = NULL) { + // subsume_node will break sparseness at times, whenever a memory slice + // folds down to a copy of the base ("fat") memory. In such a case, + // the raw edge will update to base, although it should be top. + // This iterator will recognize either top or base_memory as an + // "empty" slice. See is_empty, is_empty2, and next below. + // + // The sparseness property is repaired in MergeMemNode::Ideal. + // As long as access to a MergeMem goes through this iterator + // or the memory_at accessor, flaws in the sparseness will + // never be observed. + // + // Also, iteration_setup repairs sparseness. + assert(mm->verify_sparse(), "please, no dups of base"); + assert(mm2==NULL || mm2->verify_sparse(), "please, no dups of base"); + + _mm = mm; + _mm_base = mm->base_memory(); + _mm2 = mm2; + _cnt = mm->req(); + _idx = Compile::AliasIdxBot-1; // start at the base memory + _mem = NULL; + _mem2 = NULL; + } + +#ifdef ASSERT + Node* check_memory() const { + if (at_base_memory()) + return _mm->base_memory(); + else if ((uint)_idx < _mm->req() && !_mm->in(_idx)->is_top()) + return _mm->memory_at(_idx); + else + return _mm_base; + } + Node* check_memory2() const { + return at_base_memory()? _mm2->base_memory(): _mm2->memory_at(_idx); + } +#endif + + static bool match_memory(Node* mem, const MergeMemNode* mm, int idx) PRODUCT_RETURN0; + void assert_synch() const { + assert(!_mem || _idx >= _cnt || match_memory(_mem, _mm, _idx), + "no side-effects except through the stream"); + } + + public: + + // expected usages: + // for (MergeMemStream mms(mem->is_MergeMem()); next_non_empty(); ) { ... } + // for (MergeMemStream mms(mem1, mem2); next_non_empty2(); ) { ... } + + // iterate over one merge + MergeMemStream(MergeMemNode* mm) { + mm->iteration_setup(); + init(mm); + debug_only(_cnt2 = 999); + } + // iterate in parallel over two merges + // only iterates through non-empty elements of mm2 + MergeMemStream(MergeMemNode* mm, const MergeMemNode* mm2) { + assert(mm2, "second argument must be a MergeMem also"); + ((MergeMemNode*)mm2)->iteration_setup(); // update hidden state + mm->iteration_setup(mm2); + init(mm, mm2); + _cnt2 = mm2->req(); + } +#ifdef ASSERT + ~MergeMemStream() { + assert_synch(); + } +#endif + + MergeMemNode* all_memory() const { + return _mm; + } + Node* base_memory() const { + assert(_mm_base == _mm->base_memory(), "no update to base memory, please"); + return _mm_base; + } + const MergeMemNode* all_memory2() const { + assert(_mm2 != NULL, ""); + return _mm2; + } + bool at_base_memory() const { + return _idx == Compile::AliasIdxBot; + } + int alias_idx() const { + assert(_mem, "must call next 1st"); + return _idx; + } + + const TypePtr* adr_type() const { + return Compile::current()->get_adr_type(alias_idx()); + } + + const TypePtr* adr_type(Compile* C) const { + return C->get_adr_type(alias_idx()); + } + bool is_empty() const { + assert(_mem, "must call next 1st"); + assert(_mem->is_top() == (_mem==_mm->empty_memory()), "correct sentinel"); + return _mem->is_top(); + } + bool is_empty2() const { + assert(_mem2, "must call next 1st"); + assert(_mem2->is_top() == (_mem2==_mm2->empty_memory()), "correct sentinel"); + return _mem2->is_top(); + } + Node* memory() const { + assert(!is_empty(), "must not be empty"); + assert_synch(); + return _mem; + } + // get the current memory, regardless of empty or non-empty status + Node* force_memory() const { + assert(!is_empty() || !at_base_memory(), ""); + // Use _mm_base to defend against updates to _mem->base_memory(). + Node *mem = _mem->is_top() ? _mm_base : _mem; + assert(mem == check_memory(), ""); + return mem; + } + Node* memory2() const { + assert(_mem2 == check_memory2(), ""); + return _mem2; + } + void set_memory(Node* mem) { + if (at_base_memory()) { + // Note that this does not change the invariant _mm_base. + _mm->set_base_memory(mem); + } else { + _mm->set_memory_at(_idx, mem); + } + _mem = mem; + assert_synch(); + } + + // Recover from a side effect to the MergeMemNode. + void set_memory() { + _mem = _mm->in(_idx); + } + + bool next() { return next(false); } + bool next2() { return next(true); } + + bool next_non_empty() { return next_non_empty(false); } + bool next_non_empty2() { return next_non_empty(true); } + // next_non_empty2 can yield states where is_empty() is true + + private: + // find the next item, which might be empty + bool next(bool have_mm2) { + assert((_mm2 != NULL) == have_mm2, "use other next"); + assert_synch(); + if (++_idx < _cnt) { + // Note: This iterator allows _mm to be non-sparse. + // It behaves the same whether _mem is top or base_memory. + _mem = _mm->in(_idx); + if (have_mm2) + _mem2 = _mm2->in((_idx < _cnt2) ? _idx : Compile::AliasIdxTop); + return true; + } + return false; + } + + // find the next non-empty item + bool next_non_empty(bool have_mm2) { + while (next(have_mm2)) { + if (!is_empty()) { + // make sure _mem2 is filled in sensibly + if (have_mm2 && _mem2->is_top()) _mem2 = _mm2->base_memory(); + return true; + } else if (have_mm2 && !is_empty2()) { + return true; // is_empty() == true + } + } + return false; + } +}; + +//------------------------------Prefetch--------------------------------------- + +// Non-faulting prefetch load. Prefetch for many reads. +class PrefetchReadNode : public Node { +public: + PrefetchReadNode(Node *abio, Node *adr) : Node(0,abio,adr) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return NotAMachineReg; } + virtual uint match_edge(uint idx) const { return idx==2; } + virtual const Type *bottom_type() const { return Type::ABIO; } +}; + +// Non-faulting prefetch load. Prefetch for many reads & many writes. +class PrefetchWriteNode : public Node { +public: + PrefetchWriteNode(Node *abio, Node *adr) : Node(0,abio,adr) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return NotAMachineReg; } + virtual uint match_edge(uint idx) const { return idx==2; } + virtual const Type *bottom_type() const { return Type::ABIO; } +}; diff --git a/hotspot/src/share/vm/opto/mulnode.cpp b/hotspot/src/share/vm/opto/mulnode.cpp new file mode 100644 index 00000000000..146c432feae --- /dev/null +++ b/hotspot/src/share/vm/opto/mulnode.cpp @@ -0,0 +1,1310 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +#include "incls/_precompiled.incl" +#include "incls/_mulnode.cpp.incl" + + +//============================================================================= +//------------------------------hash------------------------------------------- +// Hash function over MulNodes. Needs to be commutative; i.e., I swap +// (commute) inputs to MulNodes willy-nilly so the hash function must return +// the same value in the presence of edge swapping. +uint MulNode::hash() const { + return (uintptr_t)in(1) + (uintptr_t)in(2) + Opcode(); +} + +//------------------------------Identity--------------------------------------- +// Multiplying a one preserves the other argument +Node *MulNode::Identity( PhaseTransform *phase ) { + register const Type *one = mul_id(); // The multiplicative identity + if( phase->type( in(1) )->higher_equal( one ) ) return in(2); + if( phase->type( in(2) )->higher_equal( one ) ) return in(1); + + return this; +} + +//------------------------------Ideal------------------------------------------ +// We also canonicalize the Node, moving constants to the right input, +// and flatten expressions (so that 1+x+2 becomes x+3). +Node *MulNode::Ideal(PhaseGVN *phase, bool can_reshape) { + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + Node *progress = NULL; // Progress flag + // We are OK if right is a constant, or right is a load and + // left is a non-constant. + if( !(t2->singleton() || + (in(2)->is_Load() && !(t1->singleton() || in(1)->is_Load())) ) ) { + if( t1->singleton() || // Left input is a constant? + // Otherwise, sort inputs (commutativity) to help value numbering. + (in(1)->_idx > in(2)->_idx) ) { + swap_edges(1, 2); + const Type *t = t1; + t1 = t2; + t2 = t; + progress = this; // Made progress + } + } + + // If the right input is a constant, and the left input is a product of a + // constant, flatten the expression tree. + uint op = Opcode(); + if( t2->singleton() && // Right input is a constant? + op != Op_MulF && // Float & double cannot reassociate + op != Op_MulD ) { + if( t2 == Type::TOP ) return NULL; + Node *mul1 = in(1); +#ifdef ASSERT + // Check for dead loop + int op1 = mul1->Opcode(); + if( phase->eqv( mul1, this ) || phase->eqv( in(2), this ) || + ( op1 == mul_opcode() || op1 == add_opcode() ) && + ( phase->eqv( mul1->in(1), this ) || phase->eqv( mul1->in(2), this ) || + phase->eqv( mul1->in(1), mul1 ) || phase->eqv( mul1->in(2), mul1 ) ) ) + assert(false, "dead loop in MulNode::Ideal"); +#endif + + if( mul1->Opcode() == mul_opcode() ) { // Left input is a multiply? + // Mul of a constant? + const Type *t12 = phase->type( mul1->in(2) ); + if( t12->singleton() && t12 != Type::TOP) { // Left input is an add of a constant? + // Compute new constant; check for overflow + const Type *tcon01 = mul1->as_Mul()->mul_ring(t2,t12); + if( tcon01->singleton() ) { + // The Mul of the flattened expression + set_req(1, mul1->in(1)); + set_req(2, phase->makecon( tcon01 )); + t2 = tcon01; + progress = this; // Made progress + } + } + } + // If the right input is a constant, and the left input is an add of a + // constant, flatten the tree: (X+con1)*con0 ==> X*con0 + con1*con0 + const Node *add1 = in(1); + if( add1->Opcode() == add_opcode() ) { // Left input is an add? + // Add of a constant? + const Type *t12 = phase->type( add1->in(2) ); + if( t12->singleton() && t12 != Type::TOP ) { // Left input is an add of a constant? + assert( add1->in(1) != add1, "dead loop in MulNode::Ideal" ); + // Compute new constant; check for overflow + const Type *tcon01 = mul_ring(t2,t12); + if( tcon01->singleton() ) { + + // Convert (X+con1)*con0 into X*con0 + Node *mul = clone(); // mul = ()*con0 + mul->set_req(1,add1->in(1)); // mul = X*con0 + mul = phase->transform(mul); + + Node *add2 = add1->clone(); + add2->set_req(1, mul); // X*con0 + con0*con1 + add2->set_req(2, phase->makecon(tcon01) ); + progress = add2; + } + } + } // End of is left input an add + } // End of is right input a Mul + + return progress; +} + +//------------------------------Value----------------------------------------- +const Type *MulNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + // Either input is TOP ==> the result is TOP + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Either input is ZERO ==> the result is ZERO. + // Not valid for floats or doubles since +0.0 * -0.0 --> +0.0 + int op = Opcode(); + if( op == Op_MulI || op == Op_AndI || op == Op_MulL || op == Op_AndL ) { + const Type *zero = add_id(); // The multiplicative zero + if( t1->higher_equal( zero ) ) return zero; + if( t2->higher_equal( zero ) ) return zero; + } + + // Either input is BOTTOM ==> the result is the local BOTTOM + if( t1 == Type::BOTTOM || t2 == Type::BOTTOM ) + return bottom_type(); + + return mul_ring(t1,t2); // Local flavor of type multiplication +} + + +//============================================================================= +//------------------------------Ideal------------------------------------------ +// Check for power-of-2 multiply, then try the regular MulNode::Ideal +Node *MulINode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Swap constant to right + jint con; + if ((con = in(1)->find_int_con(0)) != 0) { + swap_edges(1, 2); + // Finish rest of method to use info in 'con' + } else if ((con = in(2)->find_int_con(0)) == 0) { + return MulNode::Ideal(phase, can_reshape); + } + + // Now we have a constant Node on the right and the constant in con + if( con == 0 ) return NULL; // By zero is handled by Value call + if( con == 1 ) return NULL; // By one is handled by Identity call + + // Check for negative constant; if so negate the final result + bool sign_flip = false; + if( con < 0 ) { + con = -con; + sign_flip = true; + } + + // Get low bit; check for being the only bit + Node *res = NULL; + jint bit1 = con & -con; // Extract low bit + if( bit1 == con ) { // Found a power of 2? + res = new (phase->C, 3) LShiftINode( in(1), phase->intcon(log2_intptr(bit1)) ); + } else { + + // Check for constant with 2 bits set + jint bit2 = con-bit1; + bit2 = bit2 & -bit2; // Extract 2nd bit + if( bit2 + bit1 == con ) { // Found all bits in con? + Node *n1 = phase->transform( new (phase->C, 3) LShiftINode( in(1), phase->intcon(log2_intptr(bit1)) ) ); + Node *n2 = phase->transform( new (phase->C, 3) LShiftINode( in(1), phase->intcon(log2_intptr(bit2)) ) ); + res = new (phase->C, 3) AddINode( n2, n1 ); + + } else if (is_power_of_2(con+1)) { + // Sleezy: power-of-2 -1. Next time be generic. + jint temp = (jint) (con + 1); + Node *n1 = phase->transform( new (phase->C, 3) LShiftINode( in(1), phase->intcon(log2_intptr(temp)) ) ); + res = new (phase->C, 3) SubINode( n1, in(1) ); + } else { + return MulNode::Ideal(phase, can_reshape); + } + } + + if( sign_flip ) { // Need to negate result? + res = phase->transform(res);// Transform, before making the zero con + res = new (phase->C, 3) SubINode(phase->intcon(0),res); + } + + return res; // Return final result +} + +//------------------------------mul_ring--------------------------------------- +// Compute the product type of two integer ranges into this node. +const Type *MulINode::mul_ring(const Type *t0, const Type *t1) const { + const TypeInt *r0 = t0->is_int(); // Handy access + const TypeInt *r1 = t1->is_int(); + + // Fetch endpoints of all ranges + int32 lo0 = r0->_lo; + double a = (double)lo0; + int32 hi0 = r0->_hi; + double b = (double)hi0; + int32 lo1 = r1->_lo; + double c = (double)lo1; + int32 hi1 = r1->_hi; + double d = (double)hi1; + + // Compute all endpoints & check for overflow + int32 A = lo0*lo1; + if( (double)A != a*c ) return TypeInt::INT; // Overflow? + int32 B = lo0*hi1; + if( (double)B != a*d ) return TypeInt::INT; // Overflow? + int32 C = hi0*lo1; + if( (double)C != b*c ) return TypeInt::INT; // Overflow? + int32 D = hi0*hi1; + if( (double)D != b*d ) return TypeInt::INT; // Overflow? + + if( A < B ) { lo0 = A; hi0 = B; } // Sort range endpoints + else { lo0 = B; hi0 = A; } + if( C < D ) { + if( C < lo0 ) lo0 = C; + if( D > hi0 ) hi0 = D; + } else { + if( D < lo0 ) lo0 = D; + if( C > hi0 ) hi0 = C; + } + return TypeInt::make(lo0, hi0, MAX2(r0->_widen,r1->_widen)); +} + + +//============================================================================= +//------------------------------Ideal------------------------------------------ +// Check for power-of-2 multiply, then try the regular MulNode::Ideal +Node *MulLNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Swap constant to right + jlong con; + if ((con = in(1)->find_long_con(0)) != 0) { + swap_edges(1, 2); + // Finish rest of method to use info in 'con' + } else if ((con = in(2)->find_long_con(0)) == 0) { + return MulNode::Ideal(phase, can_reshape); + } + + // Now we have a constant Node on the right and the constant in con + if( con == CONST64(0) ) return NULL; // By zero is handled by Value call + if( con == CONST64(1) ) return NULL; // By one is handled by Identity call + + // Check for negative constant; if so negate the final result + bool sign_flip = false; + if( con < 0 ) { + con = -con; + sign_flip = true; + } + + // Get low bit; check for being the only bit + Node *res = NULL; + jlong bit1 = con & -con; // Extract low bit + if( bit1 == con ) { // Found a power of 2? + res = new (phase->C, 3) LShiftLNode( in(1), phase->intcon(log2_long(bit1)) ); + } else { + + // Check for constant with 2 bits set + jlong bit2 = con-bit1; + bit2 = bit2 & -bit2; // Extract 2nd bit + if( bit2 + bit1 == con ) { // Found all bits in con? + Node *n1 = phase->transform( new (phase->C, 3) LShiftLNode( in(1), phase->intcon(log2_long(bit1)) ) ); + Node *n2 = phase->transform( new (phase->C, 3) LShiftLNode( in(1), phase->intcon(log2_long(bit2)) ) ); + res = new (phase->C, 3) AddLNode( n2, n1 ); + + } else if (is_power_of_2_long(con+1)) { + // Sleezy: power-of-2 -1. Next time be generic. + jlong temp = (jlong) (con + 1); + Node *n1 = phase->transform( new (phase->C, 3) LShiftLNode( in(1), phase->intcon(log2_long(temp)) ) ); + res = new (phase->C, 3) SubLNode( n1, in(1) ); + } else { + return MulNode::Ideal(phase, can_reshape); + } + } + + if( sign_flip ) { // Need to negate result? + res = phase->transform(res);// Transform, before making the zero con + res = new (phase->C, 3) SubLNode(phase->longcon(0),res); + } + + return res; // Return final result +} + +//------------------------------mul_ring--------------------------------------- +// Compute the product type of two integer ranges into this node. +const Type *MulLNode::mul_ring(const Type *t0, const Type *t1) const { + const TypeLong *r0 = t0->is_long(); // Handy access + const TypeLong *r1 = t1->is_long(); + + // Fetch endpoints of all ranges + jlong lo0 = r0->_lo; + double a = (double)lo0; + jlong hi0 = r0->_hi; + double b = (double)hi0; + jlong lo1 = r1->_lo; + double c = (double)lo1; + jlong hi1 = r1->_hi; + double d = (double)hi1; + + // Compute all endpoints & check for overflow + jlong A = lo0*lo1; + if( (double)A != a*c ) return TypeLong::LONG; // Overflow? + jlong B = lo0*hi1; + if( (double)B != a*d ) return TypeLong::LONG; // Overflow? + jlong C = hi0*lo1; + if( (double)C != b*c ) return TypeLong::LONG; // Overflow? + jlong D = hi0*hi1; + if( (double)D != b*d ) return TypeLong::LONG; // Overflow? + + if( A < B ) { lo0 = A; hi0 = B; } // Sort range endpoints + else { lo0 = B; hi0 = A; } + if( C < D ) { + if( C < lo0 ) lo0 = C; + if( D > hi0 ) hi0 = D; + } else { + if( D < lo0 ) lo0 = D; + if( C > hi0 ) hi0 = C; + } + return TypeLong::make(lo0, hi0, MAX2(r0->_widen,r1->_widen)); +} + +//============================================================================= +//------------------------------mul_ring--------------------------------------- +// Compute the product type of two double ranges into this node. +const Type *MulFNode::mul_ring(const Type *t0, const Type *t1) const { + if( t0 == Type::FLOAT || t1 == Type::FLOAT ) return Type::FLOAT; + return TypeF::make( t0->getf() * t1->getf() ); +} + +//============================================================================= +//------------------------------mul_ring--------------------------------------- +// Compute the product type of two double ranges into this node. +const Type *MulDNode::mul_ring(const Type *t0, const Type *t1) const { + if( t0 == Type::DOUBLE || t1 == Type::DOUBLE ) return Type::DOUBLE; + // We must be adding 2 double constants. + return TypeD::make( t0->getd() * t1->getd() ); +} + +//============================================================================= +//------------------------------mul_ring--------------------------------------- +// Supplied function returns the product of the inputs IN THE CURRENT RING. +// For the logical operations the ring's MUL is really a logical AND function. +// This also type-checks the inputs for sanity. Guaranteed never to +// be passed a TOP or BOTTOM type, these are filtered out by pre-check. +const Type *AndINode::mul_ring( const Type *t0, const Type *t1 ) const { + const TypeInt *r0 = t0->is_int(); // Handy access + const TypeInt *r1 = t1->is_int(); + int widen = MAX2(r0->_widen,r1->_widen); + + // If either input is a constant, might be able to trim cases + if( !r0->is_con() && !r1->is_con() ) + return TypeInt::INT; // No constants to be had + + // Both constants? Return bits + if( r0->is_con() && r1->is_con() ) + return TypeInt::make( r0->get_con() & r1->get_con() ); + + if( r0->is_con() && r0->get_con() > 0 ) + return TypeInt::make(0, r0->get_con(), widen); + + if( r1->is_con() && r1->get_con() > 0 ) + return TypeInt::make(0, r1->get_con(), widen); + + if( r0 == TypeInt::BOOL || r1 == TypeInt::BOOL ) { + return TypeInt::BOOL; + } + + return TypeInt::INT; // No constants to be had +} + +//------------------------------Identity--------------------------------------- +// Masking off the high bits of an unsigned load is not required +Node *AndINode::Identity( PhaseTransform *phase ) { + + // x & x => x + if (phase->eqv(in(1), in(2))) return in(1); + + Node *load = in(1); + const TypeInt *t2 = phase->type( in(2) )->isa_int(); + if( t2 && t2->is_con() ) { + int con = t2->get_con(); + // Masking off high bits which are always zero is useless. + const TypeInt* t1 = phase->type( in(1) )->isa_int(); + if (t1 != NULL && t1->_lo >= 0) { + jint t1_support = ((jint)1 << (1 + log2_intptr(t1->_hi))) - 1; + if ((t1_support & con) == t1_support) + return load; + } + uint lop = load->Opcode(); + if( lop == Op_LoadC && + con == 0x0000FFFF ) // Already zero-extended + return load; + // Masking off the high bits of a unsigned-shift-right is not + // needed either. + if( lop == Op_URShiftI ) { + const TypeInt *t12 = phase->type( load->in(2) )->isa_int(); + if( t12 && t12->is_con() ) { + int shift_con = t12->get_con(); + int mask = max_juint >> shift_con; + if( (mask&con) == mask ) // If AND is useless, skip it + return load; + } + } + } + return MulNode::Identity(phase); +} + +//------------------------------Ideal------------------------------------------ +Node *AndINode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Special case constant AND mask + const TypeInt *t2 = phase->type( in(2) )->isa_int(); + if( !t2 || !t2->is_con() ) return MulNode::Ideal(phase, can_reshape); + const int mask = t2->get_con(); + Node *load = in(1); + uint lop = load->Opcode(); + + // Masking bits off of a Character? Hi bits are already zero. + if( lop == Op_LoadC && + (mask & 0xFFFF0000) ) // Can we make a smaller mask? + return new (phase->C, 3) AndINode(load,phase->intcon(mask&0xFFFF)); + + // Masking bits off of a Short? Loading a Character does some masking + if( lop == Op_LoadS && + (mask & 0xFFFF0000) == 0 ) { + Node *ldc = new (phase->C, 3) LoadCNode(load->in(MemNode::Control), + load->in(MemNode::Memory), + load->in(MemNode::Address), + load->adr_type()); + ldc = phase->transform(ldc); + return new (phase->C, 3) AndINode(ldc,phase->intcon(mask&0xFFFF)); + } + + // Masking sign bits off of a Byte? Let the matcher use an unsigned load + if( lop == Op_LoadB && + (!in(0) && load->in(0)) && + (mask == 0x000000FF) ) { + // Associate this node with the LoadB, so the matcher can see them together. + // If we don't do this, it is common for the LoadB to have one control + // edge, and the store or call containing this AndI to have a different + // control edge. This will cause Label_Root to group the AndI with + // the encoding store or call, so the matcher has no chance to match + // this AndI together with the LoadB. Setting the control edge here + // prevents Label_Root from grouping the AndI with the store or call, + // if it has a control edge that is inconsistent with the LoadB. + set_req(0, load->in(0)); + return this; + } + + // Masking off sign bits? Dont make them! + if( lop == Op_RShiftI ) { + const TypeInt *t12 = phase->type(load->in(2))->isa_int(); + if( t12 && t12->is_con() ) { // Shift is by a constant + int shift = t12->get_con(); + shift &= BitsPerJavaInteger-1; // semantics of Java shifts + const int sign_bits_mask = ~right_n_bits(BitsPerJavaInteger - shift); + // If the AND'ing of the 2 masks has no bits, then only original shifted + // bits survive. NO sign-extension bits survive the maskings. + if( (sign_bits_mask & mask) == 0 ) { + // Use zero-fill shift instead + Node *zshift = phase->transform(new (phase->C, 3) URShiftINode(load->in(1),load->in(2))); + return new (phase->C, 3) AndINode( zshift, in(2) ); + } + } + } + + // Check for 'negate/and-1', a pattern emitted when someone asks for + // 'mod 2'. Negate leaves the low order bit unchanged (think: complement + // plus 1) and the mask is of the low order bit. Skip the negate. + if( lop == Op_SubI && mask == 1 && load->in(1) && + phase->type(load->in(1)) == TypeInt::ZERO ) + return new (phase->C, 3) AndINode( load->in(2), in(2) ); + + return MulNode::Ideal(phase, can_reshape); +} + +//============================================================================= +//------------------------------mul_ring--------------------------------------- +// Supplied function returns the product of the inputs IN THE CURRENT RING. +// For the logical operations the ring's MUL is really a logical AND function. +// This also type-checks the inputs for sanity. Guaranteed never to +// be passed a TOP or BOTTOM type, these are filtered out by pre-check. +const Type *AndLNode::mul_ring( const Type *t0, const Type *t1 ) const { + const TypeLong *r0 = t0->is_long(); // Handy access + const TypeLong *r1 = t1->is_long(); + int widen = MAX2(r0->_widen,r1->_widen); + + // If either input is a constant, might be able to trim cases + if( !r0->is_con() && !r1->is_con() ) + return TypeLong::LONG; // No constants to be had + + // Both constants? Return bits + if( r0->is_con() && r1->is_con() ) + return TypeLong::make( r0->get_con() & r1->get_con() ); + + if( r0->is_con() && r0->get_con() > 0 ) + return TypeLong::make(CONST64(0), r0->get_con(), widen); + + if( r1->is_con() && r1->get_con() > 0 ) + return TypeLong::make(CONST64(0), r1->get_con(), widen); + + return TypeLong::LONG; // No constants to be had +} + +//------------------------------Identity--------------------------------------- +// Masking off the high bits of an unsigned load is not required +Node *AndLNode::Identity( PhaseTransform *phase ) { + + // x & x => x + if (phase->eqv(in(1), in(2))) return in(1); + + Node *usr = in(1); + const TypeLong *t2 = phase->type( in(2) )->isa_long(); + if( t2 && t2->is_con() ) { + jlong con = t2->get_con(); + // Masking off high bits which are always zero is useless. + const TypeLong* t1 = phase->type( in(1) )->isa_long(); + if (t1 != NULL && t1->_lo >= 0) { + jlong t1_support = ((jlong)1 << (1 + log2_long(t1->_hi))) - 1; + if ((t1_support & con) == t1_support) + return usr; + } + uint lop = usr->Opcode(); + // Masking off the high bits of a unsigned-shift-right is not + // needed either. + if( lop == Op_URShiftL ) { + const TypeInt *t12 = phase->type( usr->in(2) )->isa_int(); + if( t12 && t12->is_con() ) { + int shift_con = t12->get_con(); + jlong mask = max_julong >> shift_con; + if( (mask&con) == mask ) // If AND is useless, skip it + return usr; + } + } + } + return MulNode::Identity(phase); +} + +//------------------------------Ideal------------------------------------------ +Node *AndLNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Special case constant AND mask + const TypeLong *t2 = phase->type( in(2) )->isa_long(); + if( !t2 || !t2->is_con() ) return MulNode::Ideal(phase, can_reshape); + const jlong mask = t2->get_con(); + + Node *rsh = in(1); + uint rop = rsh->Opcode(); + + // Masking off sign bits? Dont make them! + if( rop == Op_RShiftL ) { + const TypeInt *t12 = phase->type(rsh->in(2))->isa_int(); + if( t12 && t12->is_con() ) { // Shift is by a constant + int shift = t12->get_con(); + shift &= (BitsPerJavaInteger*2)-1; // semantics of Java shifts + const jlong sign_bits_mask = ~(((jlong)CONST64(1) << (jlong)(BitsPerJavaInteger*2 - shift)) -1); + // If the AND'ing of the 2 masks has no bits, then only original shifted + // bits survive. NO sign-extension bits survive the maskings. + if( (sign_bits_mask & mask) == 0 ) { + // Use zero-fill shift instead + Node *zshift = phase->transform(new (phase->C, 3) URShiftLNode(rsh->in(1),rsh->in(2))); + return new (phase->C, 3) AndLNode( zshift, in(2) ); + } + } + } + + return MulNode::Ideal(phase, can_reshape); +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +Node *LShiftINode::Identity( PhaseTransform *phase ) { + const TypeInt *ti = phase->type( in(2) )->isa_int(); // shift count is an int + return ( ti && ti->is_con() && ( ti->get_con() & ( BitsPerInt - 1 ) ) == 0 ) ? in(1) : this; +} + +//------------------------------Ideal------------------------------------------ +// If the right input is a constant, and the left input is an add of a +// constant, flatten the tree: (X+con1)< X<type( in(2) ); + if( t == Type::TOP ) return NULL; // Right input is dead + const TypeInt *t2 = t->isa_int(); + if( !t2 || !t2->is_con() ) return NULL; // Right input is a constant + const int con = t2->get_con() & ( BitsPerInt - 1 ); // masked shift count + + if ( con == 0 ) return NULL; // let Identity() handle 0 shift count + + // Left input is an add of a constant? + Node *add1 = in(1); + int add1_op = add1->Opcode(); + if( add1_op == Op_AddI ) { // Left input is an add? + assert( add1 != add1->in(1), "dead loop in LShiftINode::Ideal" ); + const TypeInt *t12 = phase->type(add1->in(2))->isa_int(); + if( t12 && t12->is_con() ){ // Left input is an add of a con? + // Transform is legal, but check for profit. Avoid breaking 'i2s' + // and 'i2b' patterns which typically fold into 'StoreC/StoreB'. + if( con < 16 ) { + // Compute X << con0 + Node *lsh = phase->transform( new (phase->C, 3) LShiftINode( add1->in(1), in(2) ) ); + // Compute X<C, 3) AddINode( lsh, phase->intcon(t12->get_con() << con)); + } + } + } + + // Check for "(x>>c0)<in(2) == in(2) ) + // Convert to "(x & -(1<C, 3) AndINode(add1->in(1),phase->intcon( -(1<>c0) & Y)<in(1); + int add2_op = add2->Opcode(); + if( (add2_op == Op_RShiftI || add2_op == Op_URShiftI ) && + add2->in(2) == in(2) ) { + // Convert to "(x & (Y<transform( new (phase->C, 3) LShiftINode( add1->in(2), in(2) ) ); + return new (phase->C, 3) AndINode( add2->in(1), y_sh ); + } + } + + // Check for ((x & ((1<<(32-c0))-1)) << c0) which ANDs off high bits + // before shifting them away. + const jint bits_mask = right_n_bits(BitsPerJavaInteger-con); + if( add1_op == Op_AndI && + phase->type(add1->in(2)) == TypeInt::make( bits_mask ) ) + return new (phase->C, 3) LShiftINode( add1->in(1), in(2) ); + + return NULL; +} + +//------------------------------Value------------------------------------------ +// A LShiftINode shifts its input2 left by input1 amount. +const Type *LShiftINode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + // Either input is TOP ==> the result is TOP + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Left input is ZERO ==> the result is ZERO. + if( t1 == TypeInt::ZERO ) return TypeInt::ZERO; + // Shift by zero does nothing + if( t2 == TypeInt::ZERO ) return t1; + + // Either input is BOTTOM ==> the result is BOTTOM + if( (t1 == TypeInt::INT) || (t2 == TypeInt::INT) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return TypeInt::INT; + + const TypeInt *r1 = t1->is_int(); // Handy access + const TypeInt *r2 = t2->is_int(); // Handy access + + if (!r2->is_con()) + return TypeInt::INT; + + uint shift = r2->get_con(); + shift &= BitsPerJavaInteger-1; // semantics of Java shifts + // Shift by a multiple of 32 does nothing: + if (shift == 0) return t1; + + // If the shift is a constant, shift the bounds of the type, + // unless this could lead to an overflow. + if (!r1->is_con()) { + jint lo = r1->_lo, hi = r1->_hi; + if (((lo << shift) >> shift) == lo && + ((hi << shift) >> shift) == hi) { + // No overflow. The range shifts up cleanly. + return TypeInt::make((jint)lo << (jint)shift, + (jint)hi << (jint)shift, + MAX2(r1->_widen,r2->_widen)); + } + return TypeInt::INT; + } + + return TypeInt::make( (jint)r1->get_con() << (jint)shift ); +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +Node *LShiftLNode::Identity( PhaseTransform *phase ) { + const TypeInt *ti = phase->type( in(2) )->isa_int(); // shift count is an int + return ( ti && ti->is_con() && ( ti->get_con() & ( BitsPerLong - 1 ) ) == 0 ) ? in(1) : this; +} + +//------------------------------Ideal------------------------------------------ +// If the right input is a constant, and the left input is an add of a +// constant, flatten the tree: (X+con1)< X<type( in(2) ); + if( t == Type::TOP ) return NULL; // Right input is dead + const TypeInt *t2 = t->isa_int(); + if( !t2 || !t2->is_con() ) return NULL; // Right input is a constant + const int con = t2->get_con() & ( BitsPerLong - 1 ); // masked shift count + + if ( con == 0 ) return NULL; // let Identity() handle 0 shift count + + // Left input is an add of a constant? + Node *add1 = in(1); + int add1_op = add1->Opcode(); + if( add1_op == Op_AddL ) { // Left input is an add? + // Avoid dead data cycles from dead loops + assert( add1 != add1->in(1), "dead loop in LShiftLNode::Ideal" ); + const TypeLong *t12 = phase->type(add1->in(2))->isa_long(); + if( t12 && t12->is_con() ){ // Left input is an add of a con? + // Compute X << con0 + Node *lsh = phase->transform( new (phase->C, 3) LShiftLNode( add1->in(1), in(2) ) ); + // Compute X<C, 3) AddLNode( lsh, phase->longcon(t12->get_con() << con)); + } + } + + // Check for "(x>>c0)<in(2) == in(2) ) + // Convert to "(x & -(1<C, 3) AndLNode(add1->in(1),phase->longcon( -(CONST64(1)<>c0) & Y)<in(1); + int add2_op = add2->Opcode(); + if( (add2_op == Op_RShiftL || add2_op == Op_URShiftL ) && + add2->in(2) == in(2) ) { + // Convert to "(x & (Y<transform( new (phase->C, 3) LShiftLNode( add1->in(2), in(2) ) ); + return new (phase->C, 3) AndLNode( add2->in(1), y_sh ); + } + } + + // Check for ((x & ((CONST64(1)<<(64-c0))-1)) << c0) which ANDs off high bits + // before shifting them away. + const jlong bits_mask = ((jlong)CONST64(1) << (jlong)(BitsPerJavaInteger*2 - con)) - CONST64(1); + if( add1_op == Op_AndL && + phase->type(add1->in(2)) == TypeLong::make( bits_mask ) ) + return new (phase->C, 3) LShiftLNode( add1->in(1), in(2) ); + + return NULL; +} + +//------------------------------Value------------------------------------------ +// A LShiftLNode shifts its input2 left by input1 amount. +const Type *LShiftLNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + // Either input is TOP ==> the result is TOP + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Left input is ZERO ==> the result is ZERO. + if( t1 == TypeLong::ZERO ) return TypeLong::ZERO; + // Shift by zero does nothing + if( t2 == TypeInt::ZERO ) return t1; + + // Either input is BOTTOM ==> the result is BOTTOM + if( (t1 == TypeLong::LONG) || (t2 == TypeInt::INT) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return TypeLong::LONG; + + const TypeLong *r1 = t1->is_long(); // Handy access + const TypeInt *r2 = t2->is_int(); // Handy access + + if (!r2->is_con()) + return TypeLong::LONG; + + uint shift = r2->get_con(); + shift &= (BitsPerJavaInteger*2)-1; // semantics of Java shifts + // Shift by a multiple of 64 does nothing: + if (shift == 0) return t1; + + // If the shift is a constant, shift the bounds of the type, + // unless this could lead to an overflow. + if (!r1->is_con()) { + jlong lo = r1->_lo, hi = r1->_hi; + if (((lo << shift) >> shift) == lo && + ((hi << shift) >> shift) == hi) { + // No overflow. The range shifts up cleanly. + return TypeLong::make((jlong)lo << (jint)shift, + (jlong)hi << (jint)shift, + MAX2(r1->_widen,r2->_widen)); + } + return TypeLong::LONG; + } + + return TypeLong::make( (jlong)r1->get_con() << (jint)shift ); +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +Node *RShiftINode::Identity( PhaseTransform *phase ) { + const TypeInt *t2 = phase->type(in(2))->isa_int(); + if( !t2 ) return this; + if ( t2->is_con() && ( t2->get_con() & ( BitsPerInt - 1 ) ) == 0 ) + return in(1); + + // Check for useless sign-masking + if( in(1)->Opcode() == Op_LShiftI && + in(1)->req() == 3 && + in(1)->in(2) == in(2) && + t2->is_con() ) { + uint shift = t2->get_con(); + shift &= BitsPerJavaInteger-1; // semantics of Java shifts + // Compute masks for which this shifting doesn't change + int lo = (-1 << (BitsPerJavaInteger - shift-1)); // FFFF8000 + int hi = ~lo; // 00007FFF + const TypeInt *t11 = phase->type(in(1)->in(1))->isa_int(); + if( !t11 ) return this; + // Does actual value fit inside of mask? + if( lo <= t11->_lo && t11->_hi <= hi ) + return in(1)->in(1); // Then shifting is a nop + } + + return this; +} + +//------------------------------Ideal------------------------------------------ +Node *RShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Inputs may be TOP if they are dead. + const TypeInt *t1 = phase->type( in(1) )->isa_int(); + if( !t1 ) return NULL; // Left input is an integer + const TypeInt *t2 = phase->type( in(2) )->isa_int(); + if( !t2 || !t2->is_con() ) return NULL; // Right input is a constant + const TypeInt *t3; // type of in(1).in(2) + int shift = t2->get_con(); + shift &= BitsPerJavaInteger-1; // semantics of Java shifts + + if ( shift == 0 ) return NULL; // let Identity() handle 0 shift count + + // Check for (x & 0xFF000000) >> 24, whose mask can be made smaller. + // Such expressions arise normally from shift chains like (byte)(x >> 24). + const Node *mask = in(1); + if( mask->Opcode() == Op_AndI && + (t3 = phase->type(mask->in(2))->isa_int()) && + t3->is_con() ) { + Node *x = mask->in(1); + jint maskbits = t3->get_con(); + // Convert to "(x >> shift) & (mask >> shift)" + Node *shr_nomask = phase->transform( new (phase->C, 3) RShiftINode(mask->in(1), in(2)) ); + return new (phase->C, 3) AndINode(shr_nomask, phase->intcon( maskbits >> shift)); + } + + // Check for "(short[i] <<16)>>16" which simply sign-extends + const Node *shl = in(1); + if( shl->Opcode() != Op_LShiftI ) return NULL; + + if( shift == 16 && + (t3 = phase->type(shl->in(2))->isa_int()) && + t3->is_con(16) ) { + Node *ld = shl->in(1); + if( ld->Opcode() == Op_LoadS ) { + // Sign extension is just useless here. Return a RShiftI of zero instead + // returning 'ld' directly. We cannot return an old Node directly as + // that is the job of 'Identity' calls and Identity calls only work on + // direct inputs ('ld' is an extra Node removed from 'this'). The + // combined optimization requires Identity only return direct inputs. + set_req(1, ld); + set_req(2, phase->intcon(0)); + return this; + } + else if( ld->Opcode() == Op_LoadC ) + // Replace zero-extension-load with sign-extension-load + return new (phase->C, 3) LoadSNode( ld->in(MemNode::Control), + ld->in(MemNode::Memory), + ld->in(MemNode::Address), + ld->adr_type()); + } + + // Check for "(byte[i] <<24)>>24" which simply sign-extends + if( shift == 24 && + (t3 = phase->type(shl->in(2))->isa_int()) && + t3->is_con(24) ) { + Node *ld = shl->in(1); + if( ld->Opcode() == Op_LoadB ) { + // Sign extension is just useless here + set_req(1, ld); + set_req(2, phase->intcon(0)); + return this; + } + } + + return NULL; +} + +//------------------------------Value------------------------------------------ +// A RShiftINode shifts its input2 right by input1 amount. +const Type *RShiftINode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + // Either input is TOP ==> the result is TOP + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Left input is ZERO ==> the result is ZERO. + if( t1 == TypeInt::ZERO ) return TypeInt::ZERO; + // Shift by zero does nothing + if( t2 == TypeInt::ZERO ) return t1; + + // Either input is BOTTOM ==> the result is BOTTOM + if (t1 == Type::BOTTOM || t2 == Type::BOTTOM) + return TypeInt::INT; + + if (t2 == TypeInt::INT) + return TypeInt::INT; + + const TypeInt *r1 = t1->is_int(); // Handy access + const TypeInt *r2 = t2->is_int(); // Handy access + + // If the shift is a constant, just shift the bounds of the type. + // For example, if the shift is 31, we just propagate sign bits. + if (r2->is_con()) { + uint shift = r2->get_con(); + shift &= BitsPerJavaInteger-1; // semantics of Java shifts + // Shift by a multiple of 32 does nothing: + if (shift == 0) return t1; + // Calculate reasonably aggressive bounds for the result. + // This is necessary if we are to correctly type things + // like (x<<24>>24) == ((byte)x). + jint lo = (jint)r1->_lo >> (jint)shift; + jint hi = (jint)r1->_hi >> (jint)shift; + assert(lo <= hi, "must have valid bounds"); + const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen)); +#ifdef ASSERT + // Make sure we get the sign-capture idiom correct. + if (shift == BitsPerJavaInteger-1) { + if (r1->_lo >= 0) assert(ti == TypeInt::ZERO, ">>31 of + is 0"); + if (r1->_hi < 0) assert(ti == TypeInt::MINUS_1, ">>31 of - is -1"); + } +#endif + return ti; + } + + if( !r1->is_con() || !r2->is_con() ) + return TypeInt::INT; + + // Signed shift right + return TypeInt::make( r1->get_con() >> (r2->get_con()&31) ); +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +Node *RShiftLNode::Identity( PhaseTransform *phase ) { + const TypeInt *ti = phase->type( in(2) )->isa_int(); // shift count is an int + return ( ti && ti->is_con() && ( ti->get_con() & ( BitsPerLong - 1 ) ) == 0 ) ? in(1) : this; +} + +//------------------------------Value------------------------------------------ +// A RShiftLNode shifts its input2 right by input1 amount. +const Type *RShiftLNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + // Either input is TOP ==> the result is TOP + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Left input is ZERO ==> the result is ZERO. + if( t1 == TypeLong::ZERO ) return TypeLong::ZERO; + // Shift by zero does nothing + if( t2 == TypeInt::ZERO ) return t1; + + // Either input is BOTTOM ==> the result is BOTTOM + if (t1 == Type::BOTTOM || t2 == Type::BOTTOM) + return TypeLong::LONG; + + if (t2 == TypeInt::INT) + return TypeLong::LONG; + + const TypeLong *r1 = t1->is_long(); // Handy access + const TypeInt *r2 = t2->is_int (); // Handy access + + // If the shift is a constant, just shift the bounds of the type. + // For example, if the shift is 63, we just propagate sign bits. + if (r2->is_con()) { + uint shift = r2->get_con(); + shift &= (2*BitsPerJavaInteger)-1; // semantics of Java shifts + // Shift by a multiple of 64 does nothing: + if (shift == 0) return t1; + // Calculate reasonably aggressive bounds for the result. + // This is necessary if we are to correctly type things + // like (x<<24>>24) == ((byte)x). + jlong lo = (jlong)r1->_lo >> (jlong)shift; + jlong hi = (jlong)r1->_hi >> (jlong)shift; + assert(lo <= hi, "must have valid bounds"); + const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen)); + #ifdef ASSERT + // Make sure we get the sign-capture idiom correct. + if (shift == (2*BitsPerJavaInteger)-1) { + if (r1->_lo >= 0) assert(tl == TypeLong::ZERO, ">>63 of + is 0"); + if (r1->_hi < 0) assert(tl == TypeLong::MINUS_1, ">>63 of - is -1"); + } + #endif + return tl; + } + + return TypeLong::LONG; // Give up +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +Node *URShiftINode::Identity( PhaseTransform *phase ) { + const TypeInt *ti = phase->type( in(2) )->isa_int(); + if ( ti && ti->is_con() && ( ti->get_con() & ( BitsPerInt - 1 ) ) == 0 ) return in(1); + + // Check for "((x << LogBytesPerWord) + (wordSize-1)) >> LogBytesPerWord" which is just "x". + // Happens during new-array length computation. + // Safe if 'x' is in the range [0..(max_int>>LogBytesPerWord)] + Node *add = in(1); + if( add->Opcode() == Op_AddI ) { + const TypeInt *t2 = phase->type(add->in(2))->isa_int(); + if( t2 && t2->is_con(wordSize - 1) && + add->in(1)->Opcode() == Op_LShiftI ) { + // Check that shift_counts are LogBytesPerWord + Node *lshift_count = add->in(1)->in(2); + const TypeInt *t_lshift_count = phase->type(lshift_count)->isa_int(); + if( t_lshift_count && t_lshift_count->is_con(LogBytesPerWord) && + t_lshift_count == phase->type(in(2)) ) { + Node *x = add->in(1)->in(1); + const TypeInt *t_x = phase->type(x)->isa_int(); + if( t_x != NULL && 0 <= t_x->_lo && t_x->_hi <= (max_jint>>LogBytesPerWord) ) { + return x; + } + } + } + } + + return (phase->type(in(2))->higher_equal(TypeInt::ZERO)) ? in(1) : this; +} + +//------------------------------Ideal------------------------------------------ +Node *URShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) { + const TypeInt *t2 = phase->type( in(2) )->isa_int(); + if( !t2 || !t2->is_con() ) return NULL; // Right input is a constant + const int con = t2->get_con() & 31; // Shift count is always masked + if ( con == 0 ) return NULL; // let Identity() handle a 0 shift count + // We'll be wanting the right-shift amount as a mask of that many bits + const int mask = right_n_bits(BitsPerJavaInteger - con); + + int in1_op = in(1)->Opcode(); + + // Check for ((x>>>a)>>>b) and replace with (x>>>(a+b)) when a+b < 32 + if( in1_op == Op_URShiftI ) { + const TypeInt *t12 = phase->type( in(1)->in(2) )->isa_int(); + if( t12 && t12->is_con() ) { // Right input is a constant + assert( in(1) != in(1)->in(1), "dead loop in URShiftINode::Ideal" ); + const int con2 = t12->get_con() & 31; // Shift count is always masked + const int con3 = con+con2; + if( con3 < 32 ) // Only merge shifts if total is < 32 + return new (phase->C, 3) URShiftINode( in(1)->in(1), phase->intcon(con3) ); + } + } + + // Check for ((x << z) + Y) >>> z. Replace with x + con>>>z + // The idiom for rounding to a power of 2 is "(Q+(2^z-1)) >>> z". + // If Q is "X << z" the rounding is useless. Look for patterns like + // ((X<>> Z and replace with (X + Y>>>Z) & Z-mask. + Node *add = in(1); + if( in1_op == Op_AddI ) { + Node *lshl = add->in(1); + if( lshl->Opcode() == Op_LShiftI && + phase->type(lshl->in(2)) == t2 ) { + Node *y_z = phase->transform( new (phase->C, 3) URShiftINode(add->in(2),in(2)) ); + Node *sum = phase->transform( new (phase->C, 3) AddINode( lshl->in(1), y_z ) ); + return new (phase->C, 3) AndINode( sum, phase->intcon(mask) ); + } + } + + // Check for (x & mask) >>> z. Replace with (x >>> z) & (mask >>> z) + // This shortens the mask. Also, if we are extracting a high byte and + // storing it to a buffer, the mask will be removed completely. + Node *andi = in(1); + if( in1_op == Op_AndI ) { + const TypeInt *t3 = phase->type( andi->in(2) )->isa_int(); + if( t3 && t3->is_con() ) { // Right input is a constant + jint mask2 = t3->get_con(); + mask2 >>= con; // *signed* shift downward (high-order zeroes do not help) + Node *newshr = phase->transform( new (phase->C, 3) URShiftINode(andi->in(1), in(2)) ); + return new (phase->C, 3) AndINode(newshr, phase->intcon(mask2)); + // The negative values are easier to materialize than positive ones. + // A typical case from address arithmetic is ((x & ~15) >> 4). + // It's better to change that to ((x >> 4) & ~0) versus + // ((x >> 4) & 0x0FFFFFFF). The difference is greatest in LP64. + } + } + + // Check for "(X << z ) >>> z" which simply zero-extends + Node *shl = in(1); + if( in1_op == Op_LShiftI && + phase->type(shl->in(2)) == t2 ) + return new (phase->C, 3) AndINode( shl->in(1), phase->intcon(mask) ); + + return NULL; +} + +//------------------------------Value------------------------------------------ +// A URShiftINode shifts its input2 right by input1 amount. +const Type *URShiftINode::Value( PhaseTransform *phase ) const { + // (This is a near clone of RShiftINode::Value.) + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + // Either input is TOP ==> the result is TOP + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Left input is ZERO ==> the result is ZERO. + if( t1 == TypeInt::ZERO ) return TypeInt::ZERO; + // Shift by zero does nothing + if( t2 == TypeInt::ZERO ) return t1; + + // Either input is BOTTOM ==> the result is BOTTOM + if (t1 == Type::BOTTOM || t2 == Type::BOTTOM) + return TypeInt::INT; + + if (t2 == TypeInt::INT) + return TypeInt::INT; + + const TypeInt *r1 = t1->is_int(); // Handy access + const TypeInt *r2 = t2->is_int(); // Handy access + + if (r2->is_con()) { + uint shift = r2->get_con(); + shift &= BitsPerJavaInteger-1; // semantics of Java shifts + // Shift by a multiple of 32 does nothing: + if (shift == 0) return t1; + // Calculate reasonably aggressive bounds for the result. + jint lo = (juint)r1->_lo >> (juint)shift; + jint hi = (juint)r1->_hi >> (juint)shift; + if (r1->_hi >= 0 && r1->_lo < 0) { + // If the type has both negative and positive values, + // there are two separate sub-domains to worry about: + // The positive half and the negative half. + jint neg_lo = lo; + jint neg_hi = (juint)-1 >> (juint)shift; + jint pos_lo = (juint) 0 >> (juint)shift; + jint pos_hi = hi; + lo = MIN2(neg_lo, pos_lo); // == 0 + hi = MAX2(neg_hi, pos_hi); // == -1 >>> shift; + } + assert(lo <= hi, "must have valid bounds"); + const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen)); + #ifdef ASSERT + // Make sure we get the sign-capture idiom correct. + if (shift == BitsPerJavaInteger-1) { + if (r1->_lo >= 0) assert(ti == TypeInt::ZERO, ">>>31 of + is 0"); + if (r1->_hi < 0) assert(ti == TypeInt::ONE, ">>>31 of - is +1"); + } + #endif + return ti; + } + + // + // Do not support shifted oops in info for GC + // + // else if( t1->base() == Type::InstPtr ) { + // + // const TypeInstPtr *o = t1->is_instptr(); + // if( t1->singleton() ) + // return TypeInt::make( ((uint32)o->const_oop() + o->_offset) >> shift ); + // } + // else if( t1->base() == Type::KlassPtr ) { + // const TypeKlassPtr *o = t1->is_klassptr(); + // if( t1->singleton() ) + // return TypeInt::make( ((uint32)o->const_oop() + o->_offset) >> shift ); + // } + + return TypeInt::INT; +} + +//============================================================================= +//------------------------------Identity--------------------------------------- +Node *URShiftLNode::Identity( PhaseTransform *phase ) { + const TypeInt *ti = phase->type( in(2) )->isa_int(); // shift count is an int + return ( ti && ti->is_con() && ( ti->get_con() & ( BitsPerLong - 1 ) ) == 0 ) ? in(1) : this; +} + +//------------------------------Ideal------------------------------------------ +Node *URShiftLNode::Ideal(PhaseGVN *phase, bool can_reshape) { + const TypeInt *t2 = phase->type( in(2) )->isa_int(); + if( !t2 || !t2->is_con() ) return NULL; // Right input is a constant + const int con = t2->get_con() & ( BitsPerLong - 1 ); // Shift count is always masked + if ( con == 0 ) return NULL; // let Identity() handle a 0 shift count + // note: mask computation below does not work for 0 shift count + // We'll be wanting the right-shift amount as a mask of that many bits + const jlong mask = (((jlong)CONST64(1) << (jlong)(BitsPerJavaInteger*2 - con)) -1); + + // Check for ((x << z) + Y) >>> z. Replace with x + con>>>z + // The idiom for rounding to a power of 2 is "(Q+(2^z-1)) >>> z". + // If Q is "X << z" the rounding is useless. Look for patterns like + // ((X<>> Z and replace with (X + Y>>>Z) & Z-mask. + Node *add = in(1); + if( add->Opcode() == Op_AddL ) { + Node *lshl = add->in(1); + if( lshl->Opcode() == Op_LShiftL && + phase->type(lshl->in(2)) == t2 ) { + Node *y_z = phase->transform( new (phase->C, 3) URShiftLNode(add->in(2),in(2)) ); + Node *sum = phase->transform( new (phase->C, 3) AddLNode( lshl->in(1), y_z ) ); + return new (phase->C, 3) AndLNode( sum, phase->longcon(mask) ); + } + } + + // Check for (x & mask) >>> z. Replace with (x >>> z) & (mask >>> z) + // This shortens the mask. Also, if we are extracting a high byte and + // storing it to a buffer, the mask will be removed completely. + Node *andi = in(1); + if( andi->Opcode() == Op_AndL ) { + const TypeLong *t3 = phase->type( andi->in(2) )->isa_long(); + if( t3 && t3->is_con() ) { // Right input is a constant + jlong mask2 = t3->get_con(); + mask2 >>= con; // *signed* shift downward (high-order zeroes do not help) + Node *newshr = phase->transform( new (phase->C, 3) URShiftLNode(andi->in(1), in(2)) ); + return new (phase->C, 3) AndLNode(newshr, phase->longcon(mask2)); + } + } + + // Check for "(X << z ) >>> z" which simply zero-extends + Node *shl = in(1); + if( shl->Opcode() == Op_LShiftL && + phase->type(shl->in(2)) == t2 ) + return new (phase->C, 3) AndLNode( shl->in(1), phase->longcon(mask) ); + + return NULL; +} + +//------------------------------Value------------------------------------------ +// A URShiftINode shifts its input2 right by input1 amount. +const Type *URShiftLNode::Value( PhaseTransform *phase ) const { + // (This is a near clone of RShiftLNode::Value.) + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + // Either input is TOP ==> the result is TOP + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + // Left input is ZERO ==> the result is ZERO. + if( t1 == TypeLong::ZERO ) return TypeLong::ZERO; + // Shift by zero does nothing + if( t2 == TypeInt::ZERO ) return t1; + + // Either input is BOTTOM ==> the result is BOTTOM + if (t1 == Type::BOTTOM || t2 == Type::BOTTOM) + return TypeLong::LONG; + + if (t2 == TypeInt::INT) + return TypeLong::LONG; + + const TypeLong *r1 = t1->is_long(); // Handy access + const TypeInt *r2 = t2->is_int (); // Handy access + + if (r2->is_con()) { + uint shift = r2->get_con(); + shift &= (2*BitsPerJavaInteger)-1; // semantics of Java shifts + // Shift by a multiple of 64 does nothing: + if (shift == 0) return t1; + // Calculate reasonably aggressive bounds for the result. + jlong lo = (julong)r1->_lo >> (juint)shift; + jlong hi = (julong)r1->_hi >> (juint)shift; + if (r1->_hi >= 0 && r1->_lo < 0) { + // If the type has both negative and positive values, + // there are two separate sub-domains to worry about: + // The positive half and the negative half. + jlong neg_lo = lo; + jlong neg_hi = (julong)-1 >> (juint)shift; + jlong pos_lo = (julong) 0 >> (juint)shift; + jlong pos_hi = hi; + //lo = MIN2(neg_lo, pos_lo); // == 0 + lo = neg_lo < pos_lo ? neg_lo : pos_lo; + //hi = MAX2(neg_hi, pos_hi); // == -1 >>> shift; + hi = neg_hi > pos_hi ? neg_hi : pos_hi; + } + assert(lo <= hi, "must have valid bounds"); + const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen)); + #ifdef ASSERT + // Make sure we get the sign-capture idiom correct. + if (shift == (2*BitsPerJavaInteger)-1) { + if (r1->_lo >= 0) assert(tl == TypeLong::ZERO, ">>>63 of + is 0"); + if (r1->_hi < 0) assert(tl == TypeLong::ONE, ">>>63 of - is +1"); + } + #endif + return tl; + } + + return TypeLong::LONG; // Give up +} diff --git a/hotspot/src/share/vm/opto/mulnode.hpp b/hotspot/src/share/vm/opto/mulnode.hpp new file mode 100644 index 00000000000..380e35a89ec --- /dev/null +++ b/hotspot/src/share/vm/opto/mulnode.hpp @@ -0,0 +1,247 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +class PhaseTransform; + +//------------------------------MulNode---------------------------------------- +// Classic MULTIPLY functionality. This covers all the usual 'multiply' +// behaviors for an algebraic ring. Multiply-integer, multiply-float, +// multiply-double, and binary-and are all inherited from this class. The +// various identity values are supplied by virtual functions. +class MulNode : public Node { + virtual uint hash() const; +public: + MulNode( Node *in1, Node *in2 ): Node(0,in1,in2) { + init_class_id(Class_Mul); + } + + // Handle algebraic identities here. If we have an identity, return the Node + // we are equivalent to. We look for "add of zero" as an identity. + virtual Node *Identity( PhaseTransform *phase ); + + // We also canonicalize the Node, moving constants to the right input, + // and flatten expressions (so that 1+x+2 becomes x+3). + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + + // Compute a new Type for this node. Basically we just do the pre-check, + // then call the virtual add() to set the type. + virtual const Type *Value( PhaseTransform *phase ) const; + + // Supplied function returns the product of the inputs. + // This also type-checks the inputs for sanity. Guaranteed never to + // be passed a TOP or BOTTOM type, these are filtered out by a pre-check. + // This call recognizes the multiplicative zero type. + virtual const Type *mul_ring( const Type *, const Type * ) const = 0; + + // Supplied function to return the multiplicative identity type + virtual const Type *mul_id() const = 0; + + // Supplied function to return the additive identity type + virtual const Type *add_id() const = 0; + + // Supplied function to return the additive opcode + virtual int add_opcode() const = 0; + + // Supplied function to return the multiplicative opcode + virtual int mul_opcode() const = 0; + +}; + +//------------------------------MulINode--------------------------------------- +// Multiply 2 integers +class MulINode : public MulNode { +public: + MulINode( Node *in1, Node *in2 ) : MulNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *mul_ring( const Type *, const Type * ) const; + const Type *mul_id() const { return TypeInt::ONE; } + const Type *add_id() const { return TypeInt::ZERO; } + int add_opcode() const { return Op_AddI; } + int mul_opcode() const { return Op_MulI; } + const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------MulLNode--------------------------------------- +// Multiply 2 longs +class MulLNode : public MulNode { +public: + MulLNode( Node *in1, Node *in2 ) : MulNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *mul_ring( const Type *, const Type * ) const; + const Type *mul_id() const { return TypeLong::ONE; } + const Type *add_id() const { return TypeLong::ZERO; } + int add_opcode() const { return Op_AddL; } + int mul_opcode() const { return Op_MulL; } + const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + + +//------------------------------MulFNode--------------------------------------- +// Multiply 2 floats +class MulFNode : public MulNode { +public: + MulFNode( Node *in1, Node *in2 ) : MulNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *mul_ring( const Type *, const Type * ) const; + const Type *mul_id() const { return TypeF::ONE; } + const Type *add_id() const { return TypeF::ZERO; } + int add_opcode() const { return Op_AddF; } + int mul_opcode() const { return Op_MulF; } + const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------MulDNode--------------------------------------- +// Multiply 2 doubles +class MulDNode : public MulNode { +public: + MulDNode( Node *in1, Node *in2 ) : MulNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *mul_ring( const Type *, const Type * ) const; + const Type *mul_id() const { return TypeD::ONE; } + const Type *add_id() const { return TypeD::ZERO; } + int add_opcode() const { return Op_AddD; } + int mul_opcode() const { return Op_MulD; } + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + + +//------------------------------AndINode--------------------------------------- +// Logically AND 2 integers. Included with the MUL nodes because it inherits +// all the behavior of multiplication on a ring. +class AndINode : public MulINode { +public: + AndINode( Node *in1, Node *in2 ) : MulINode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *mul_ring( const Type *, const Type * ) const; + const Type *mul_id() const { return TypeInt::MINUS_1; } + const Type *add_id() const { return TypeInt::ZERO; } + int add_opcode() const { return Op_OrI; } + int mul_opcode() const { return Op_AndI; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------AndINode--------------------------------------- +// Logically AND 2 longs. Included with the MUL nodes because it inherits +// all the behavior of multiplication on a ring. +class AndLNode : public MulLNode { +public: + AndLNode( Node *in1, Node *in2 ) : MulLNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *mul_ring( const Type *, const Type * ) const; + const Type *mul_id() const { return TypeLong::MINUS_1; } + const Type *add_id() const { return TypeLong::ZERO; } + int add_opcode() const { return Op_OrL; } + int mul_opcode() const { return Op_AndL; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------LShiftINode------------------------------------ +// Logical shift left +class LShiftINode : public Node { +public: + LShiftINode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------LShiftLNode------------------------------------ +// Logical shift left +class LShiftLNode : public Node { +public: + LShiftLNode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------RShiftINode------------------------------------ +// Signed shift right +class RShiftINode : public Node { +public: + RShiftINode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------RShiftLNode------------------------------------ +// Signed shift right +class RShiftLNode : public Node { +public: + RShiftLNode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual const Type *Value( PhaseTransform *phase ) const; + const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + + +//------------------------------URShiftINode----------------------------------- +// Logical shift right +class URShiftINode : public Node { +public: + URShiftINode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------URShiftLNode----------------------------------- +// Logical shift right +class URShiftLNode : public Node { +public: + URShiftLNode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} + virtual int Opcode() const; + virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; diff --git a/hotspot/src/share/vm/opto/multnode.cpp b/hotspot/src/share/vm/opto/multnode.cpp new file mode 100644 index 00000000000..5caa3dd8fec --- /dev/null +++ b/hotspot/src/share/vm/opto/multnode.cpp @@ -0,0 +1,129 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_multnode.cpp.incl" + +//============================================================================= +//------------------------------MultiNode-------------------------------------- +const RegMask &MultiNode::out_RegMask() const { + return RegMask::Empty; +} + +Node *MultiNode::match( const ProjNode *proj, const Matcher *m ) { return proj->clone(); } + +//------------------------------proj_out--------------------------------------- +// Get a named projection +ProjNode* MultiNode::proj_out(uint which_proj) const { + assert(Opcode() != Op_If || which_proj == (uint)true || which_proj == (uint)false, "must be 1 or 0"); + assert(Opcode() != Op_If || outcnt() == 2, "bad if #1"); + for( DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++ ) { + Node *p = fast_out(i); + if( !p->is_Proj() ) { + assert(p == this && this->is_Start(), "else must be proj"); + continue; + } + ProjNode *proj = p->as_Proj(); + if( proj->_con == which_proj ) { + assert(Opcode() != Op_If || proj->Opcode() == (which_proj?Op_IfTrue:Op_IfFalse), "bad if #2"); + return proj; + } + } + return NULL; +} + +//============================================================================= +//------------------------------ProjNode--------------------------------------- +uint ProjNode::hash() const { + // only one input + return (uintptr_t)in(TypeFunc::Control) + (_con << 1) + (_is_io_use ? 1 : 0); +} +uint ProjNode::cmp( const Node &n ) const { return _con == ((ProjNode&)n)._con && ((ProjNode&)n)._is_io_use == _is_io_use; } +uint ProjNode::size_of() const { return sizeof(ProjNode); } + +// Test if we propagate interesting control along this projection +bool ProjNode::is_CFG() const { + Node *def = in(0); + return (_con == TypeFunc::Control && def->is_CFG()); +} + +const Type *ProjNode::bottom_type() const { + if (in(0) == NULL) return Type::TOP; + const Type *tb = in(0)->bottom_type(); + if( tb == Type::TOP ) return Type::TOP; + if( tb == Type::BOTTOM ) return Type::BOTTOM; + const TypeTuple *t = tb->is_tuple(); + return t->field_at(_con); +} + +const TypePtr *ProjNode::adr_type() const { + if (bottom_type() == Type::MEMORY) { + // in(0) might be a narrow MemBar; otherwise we will report TypePtr::BOTTOM + const TypePtr* adr_type = in(0)->adr_type(); + #ifdef ASSERT + if (!is_error_reported() && !Node::in_dump()) + assert(adr_type != NULL, "source must have adr_type"); + #endif + return adr_type; + } + assert(bottom_type()->base() != Type::Memory, "no other memories?"); + return NULL; +} + +bool ProjNode::pinned() const { return in(0)->pinned(); } +#ifndef PRODUCT +void ProjNode::dump_spec(outputStream *st) const { st->print("#%d",_con); if(_is_io_use) st->print(" (i_o_use)");} +#endif + +//----------------------------check_con---------------------------------------- +void ProjNode::check_con() const { + Node* n = in(0); + if (n == NULL) return; // should be assert, but NodeHash makes bogons + if (n->is_Mach()) return; // mach. projs. are not type-safe + if (n->is_Start()) return; // alas, starts can have mach. projs. also + if (_con == SCMemProjNode::SCMEMPROJCON ) return; + const Type* t = n->bottom_type(); + if (t == Type::TOP) return; // multi is dead + assert(_con < t->is_tuple()->cnt(), "ProjNode::_con must be in range"); +} + +//------------------------------Value------------------------------------------ +const Type *ProjNode::Value( PhaseTransform *phase ) const { + if( !in(0) ) return Type::TOP; + const Type *t = phase->type(in(0)); + if( t == Type::TOP ) return t; + if( t == Type::BOTTOM ) return t; + return t->is_tuple()->field_at(_con); +} + +//------------------------------out_RegMask------------------------------------ +// Pass the buck uphill +const RegMask &ProjNode::out_RegMask() const { + return RegMask::Empty; +} + +//------------------------------ideal_reg-------------------------------------- +uint ProjNode::ideal_reg() const { + return Matcher::base2reg[bottom_type()->base()]; +} diff --git a/hotspot/src/share/vm/opto/multnode.hpp b/hotspot/src/share/vm/opto/multnode.hpp new file mode 100644 index 00000000000..34a573ffcd3 --- /dev/null +++ b/hotspot/src/share/vm/opto/multnode.hpp @@ -0,0 +1,81 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Matcher; +class ProjNode; + +//------------------------------MultiNode-------------------------------------- +// This class defines a MultiNode, a Node which produces many values. The +// values are wrapped up in a tuple Type, i.e. a TypeTuple. +class MultiNode : public Node { +public: + MultiNode( uint required ) : Node(required) { + init_class_id(Class_Multi); + } + virtual int Opcode() const; + virtual const Type *bottom_type() const = 0; + virtual bool is_CFG() const { return true; } + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual bool depends_only_on_test() const { return false; } + virtual const RegMask &out_RegMask() const; + virtual Node *match( const ProjNode *proj, const Matcher *m ); + virtual uint ideal_reg() const { return NotAMachineReg; } + ProjNode* proj_out(uint which_proj) const; // Get a named projection + +}; + +//------------------------------ProjNode--------------------------------------- +// This class defines a Projection node. Projections project a single element +// out of a tuple (or Signature) type. Only MultiNodes produce TypeTuple +// results. +class ProjNode : public Node { +protected: + virtual uint hash() const; + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; + void check_con() const; // Called from constructor. + +public: + ProjNode( Node *src, uint con, bool io_use = false ) + : Node( src ), _con(con), _is_io_use(io_use) + { + init_class_id(Class_Proj); + debug_only(check_con()); + } + const uint _con; // The field in the tuple we are projecting + const bool _is_io_use; // Used to distinguish between the projections + // used on the control and io paths from a macro node + virtual int Opcode() const; + virtual bool is_CFG() const; + virtual bool depends_only_on_test() const { return false; } + virtual const Type *bottom_type() const; + virtual const TypePtr *adr_type() const; + virtual bool pinned() const; + virtual const Type *Value( PhaseTransform *phase ) const; + virtual uint ideal_reg() const; + virtual const RegMask &out_RegMask() const; +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; diff --git a/hotspot/src/share/vm/opto/node.cpp b/hotspot/src/share/vm/opto/node.cpp new file mode 100644 index 00000000000..d7563d611ea --- /dev/null +++ b/hotspot/src/share/vm/opto/node.cpp @@ -0,0 +1,1919 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_node.cpp.incl" + +class RegMask; +// #include "phase.hpp" +class PhaseTransform; +class PhaseGVN; + +// Arena we are currently building Nodes in +const uint Node::NotAMachineReg = 0xffff0000; + +#ifndef PRODUCT +extern int nodes_created; +#endif + +#ifdef ASSERT + +//-------------------------- construct_node------------------------------------ +// Set a breakpoint here to identify where a particular node index is built. +void Node::verify_construction() { + _debug_orig = NULL; + int old_debug_idx = Compile::debug_idx(); + int new_debug_idx = old_debug_idx+1; + if (new_debug_idx > 0) { + // Arrange that the lowest five decimal digits of _debug_idx + // will repeat thos of _idx. In case this is somehow pathological, + // we continue to assign negative numbers (!) consecutively. + const int mod = 100000; + int bump = (int)(_idx - new_debug_idx) % mod; + if (bump < 0) bump += mod; + assert(bump >= 0 && bump < mod, ""); + new_debug_idx += bump; + } + Compile::set_debug_idx(new_debug_idx); + set_debug_idx( new_debug_idx ); + assert(Compile::current()->unique() < (uint)MaxNodeLimit, "Node limit exceeded"); + if (BreakAtNode != 0 && (_debug_idx == BreakAtNode || (int)_idx == BreakAtNode)) { + tty->print_cr("BreakAtNode: _idx=%d _debug_idx=%d", _idx, _debug_idx); + BREAKPOINT; + } +#if OPTO_DU_ITERATOR_ASSERT + _last_del = NULL; + _del_tick = 0; +#endif + _hash_lock = 0; +} + + +// #ifdef ASSERT ... + +#if OPTO_DU_ITERATOR_ASSERT +void DUIterator_Common::sample(const Node* node) { + _vdui = VerifyDUIterators; + _node = node; + _outcnt = node->_outcnt; + _del_tick = node->_del_tick; + _last = NULL; +} + +void DUIterator_Common::verify(const Node* node, bool at_end_ok) { + assert(_node == node, "consistent iterator source"); + assert(_del_tick == node->_del_tick, "no unexpected deletions allowed"); +} + +void DUIterator_Common::verify_resync() { + // Ensure that the loop body has just deleted the last guy produced. + const Node* node = _node; + // Ensure that at least one copy of the last-seen edge was deleted. + // Note: It is OK to delete multiple copies of the last-seen edge. + // Unfortunately, we have no way to verify that all the deletions delete + // that same edge. On this point we must use the Honor System. + assert(node->_del_tick >= _del_tick+1, "must have deleted an edge"); + assert(node->_last_del == _last, "must have deleted the edge just produced"); + // We liked this deletion, so accept the resulting outcnt and tick. + _outcnt = node->_outcnt; + _del_tick = node->_del_tick; +} + +void DUIterator_Common::reset(const DUIterator_Common& that) { + if (this == &that) return; // ignore assignment to self + if (!_vdui) { + // We need to initialize everything, overwriting garbage values. + _last = that._last; + _vdui = that._vdui; + } + // Note: It is legal (though odd) for an iterator over some node x + // to be reassigned to iterate over another node y. Some doubly-nested + // progress loops depend on being able to do this. + const Node* node = that._node; + // Re-initialize everything, except _last. + _node = node; + _outcnt = node->_outcnt; + _del_tick = node->_del_tick; +} + +void DUIterator::sample(const Node* node) { + DUIterator_Common::sample(node); // Initialize the assertion data. + _refresh_tick = 0; // No refreshes have happened, as yet. +} + +void DUIterator::verify(const Node* node, bool at_end_ok) { + DUIterator_Common::verify(node, at_end_ok); + assert(_idx < node->_outcnt + (uint)at_end_ok, "idx in range"); +} + +void DUIterator::verify_increment() { + if (_refresh_tick & 1) { + // We have refreshed the index during this loop. + // Fix up _idx to meet asserts. + if (_idx > _outcnt) _idx = _outcnt; + } + verify(_node, true); +} + +void DUIterator::verify_resync() { + // Note: We do not assert on _outcnt, because insertions are OK here. + DUIterator_Common::verify_resync(); + // Make sure we are still in sync, possibly with no more out-edges: + verify(_node, true); +} + +void DUIterator::reset(const DUIterator& that) { + if (this == &that) return; // self assignment is always a no-op + assert(that._refresh_tick == 0, "assign only the result of Node::outs()"); + assert(that._idx == 0, "assign only the result of Node::outs()"); + assert(_idx == that._idx, "already assigned _idx"); + if (!_vdui) { + // We need to initialize everything, overwriting garbage values. + sample(that._node); + } else { + DUIterator_Common::reset(that); + if (_refresh_tick & 1) { + _refresh_tick++; // Clear the "was refreshed" flag. + } + assert(_refresh_tick < 2*100000, "DU iteration must converge quickly"); + } +} + +void DUIterator::refresh() { + DUIterator_Common::sample(_node); // Re-fetch assertion data. + _refresh_tick |= 1; // Set the "was refreshed" flag. +} + +void DUIterator::verify_finish() { + // If the loop has killed the node, do not require it to re-run. + if (_node->_outcnt == 0) _refresh_tick &= ~1; + // If this assert triggers, it means that a loop used refresh_out_pos + // to re-synch an iteration index, but the loop did not correctly + // re-run itself, using a "while (progress)" construct. + // This iterator enforces the rule that you must keep trying the loop + // until it "runs clean" without any need for refreshing. + assert(!(_refresh_tick & 1), "the loop must run once with no refreshing"); +} + + +void DUIterator_Fast::verify(const Node* node, bool at_end_ok) { + DUIterator_Common::verify(node, at_end_ok); + Node** out = node->_out; + uint cnt = node->_outcnt; + assert(cnt == _outcnt, "no insertions allowed"); + assert(_outp >= out && _outp <= out + cnt - !at_end_ok, "outp in range"); + // This last check is carefully designed to work for NO_OUT_ARRAY. +} + +void DUIterator_Fast::verify_limit() { + const Node* node = _node; + verify(node, true); + assert(_outp == node->_out + node->_outcnt, "limit still correct"); +} + +void DUIterator_Fast::verify_resync() { + const Node* node = _node; + if (_outp == node->_out + _outcnt) { + // Note that the limit imax, not the pointer i, gets updated with the + // exact count of deletions. (For the pointer it's always "--i".) + assert(node->_outcnt+node->_del_tick == _outcnt+_del_tick, "no insertions allowed with deletion(s)"); + // This is a limit pointer, with a name like "imax". + // Fudge the _last field so that the common assert will be happy. + _last = (Node*) node->_last_del; + DUIterator_Common::verify_resync(); + } else { + assert(node->_outcnt < _outcnt, "no insertions allowed with deletion(s)"); + // A normal internal pointer. + DUIterator_Common::verify_resync(); + // Make sure we are still in sync, possibly with no more out-edges: + verify(node, true); + } +} + +void DUIterator_Fast::verify_relimit(uint n) { + const Node* node = _node; + assert((int)n > 0, "use imax -= n only with a positive count"); + // This must be a limit pointer, with a name like "imax". + assert(_outp == node->_out + node->_outcnt, "apply -= only to a limit (imax)"); + // The reported number of deletions must match what the node saw. + assert(node->_del_tick == _del_tick + n, "must have deleted n edges"); + // Fudge the _last field so that the common assert will be happy. + _last = (Node*) node->_last_del; + DUIterator_Common::verify_resync(); +} + +void DUIterator_Fast::reset(const DUIterator_Fast& that) { + assert(_outp == that._outp, "already assigned _outp"); + DUIterator_Common::reset(that); +} + +void DUIterator_Last::verify(const Node* node, bool at_end_ok) { + // at_end_ok means the _outp is allowed to underflow by 1 + _outp += at_end_ok; + DUIterator_Fast::verify(node, at_end_ok); // check _del_tick, etc. + _outp -= at_end_ok; + assert(_outp == (node->_out + node->_outcnt) - 1, "pointer must point to end of nodes"); +} + +void DUIterator_Last::verify_limit() { + // Do not require the limit address to be resynched. + //verify(node, true); + assert(_outp == _node->_out, "limit still correct"); +} + +void DUIterator_Last::verify_step(uint num_edges) { + assert((int)num_edges > 0, "need non-zero edge count for loop progress"); + _outcnt -= num_edges; + _del_tick += num_edges; + // Make sure we are still in sync, possibly with no more out-edges: + const Node* node = _node; + verify(node, true); + assert(node->_last_del == _last, "must have deleted the edge just produced"); +} + +#endif //OPTO_DU_ITERATOR_ASSERT + + +#endif //ASSERT + + +// This constant used to initialize _out may be any non-null value. +// The value NULL is reserved for the top node only. +#define NO_OUT_ARRAY ((Node**)-1) + +// This funny expression handshakes with Node::operator new +// to pull Compile::current out of the new node's _out field, +// and then calls a subroutine which manages most field +// initializations. The only one which is tricky is the +// _idx field, which is const, and so must be initialized +// by a return value, not an assignment. +// +// (Aren't you thankful that Java finals don't require so many tricks?) +#define IDX_INIT(req) this->Init((req), (Compile*) this->_out) +#ifdef _MSC_VER // the IDX_INIT hack falls foul of warning C4355 +#pragma warning( disable:4355 ) // 'this' : used in base member initializer list +#endif + +// Out-of-line code from node constructors. +// Executed only when extra debug info. is being passed around. +static void init_node_notes(Compile* C, int idx, Node_Notes* nn) { + C->set_node_notes_at(idx, nn); +} + +// Shared initialization code. +inline int Node::Init(int req, Compile* C) { + assert(Compile::current() == C, "must use operator new(Compile*)"); + int idx = C->next_unique(); + + // If there are default notes floating around, capture them: + Node_Notes* nn = C->default_node_notes(); + if (nn != NULL) init_node_notes(C, idx, nn); + + // Note: At this point, C is dead, + // and we begin to initialize the new Node. + + _cnt = _max = req; + _outcnt = _outmax = 0; + _class_id = Class_Node; + _flags = 0; + _out = NO_OUT_ARRAY; + return idx; +} + +//------------------------------Node------------------------------------------- +// Create a Node, with a given number of required edges. +Node::Node(uint req) + : _idx(IDX_INIT(req)) +{ + assert( req < (uint)(MaxNodeLimit - NodeLimitFudgeFactor), "Input limit exceeded" ); + debug_only( verify_construction() ); + NOT_PRODUCT(nodes_created++); + if (req == 0) { + assert( _in == (Node**)this, "Must not pass arg count to 'new'" ); + _in = NULL; + } else { + assert( _in[req-1] == this, "Must pass arg count to 'new'" ); + Node** to = _in; + for(uint i = 0; i < req; i++) { + to[i] = NULL; + } + } +} + +//------------------------------Node------------------------------------------- +Node::Node(Node *n0) + : _idx(IDX_INIT(1)) +{ + debug_only( verify_construction() ); + NOT_PRODUCT(nodes_created++); + // Assert we allocated space for input array already + assert( _in[0] == this, "Must pass arg count to 'new'" ); + assert( is_not_dead(n0), "can not use dead node"); + _in[0] = n0; if (n0 != NULL) n0->add_out((Node *)this); +} + +//------------------------------Node------------------------------------------- +Node::Node(Node *n0, Node *n1) + : _idx(IDX_INIT(2)) +{ + debug_only( verify_construction() ); + NOT_PRODUCT(nodes_created++); + // Assert we allocated space for input array already + assert( _in[1] == this, "Must pass arg count to 'new'" ); + assert( is_not_dead(n0), "can not use dead node"); + assert( is_not_dead(n1), "can not use dead node"); + _in[0] = n0; if (n0 != NULL) n0->add_out((Node *)this); + _in[1] = n1; if (n1 != NULL) n1->add_out((Node *)this); +} + +//------------------------------Node------------------------------------------- +Node::Node(Node *n0, Node *n1, Node *n2) + : _idx(IDX_INIT(3)) +{ + debug_only( verify_construction() ); + NOT_PRODUCT(nodes_created++); + // Assert we allocated space for input array already + assert( _in[2] == this, "Must pass arg count to 'new'" ); + assert( is_not_dead(n0), "can not use dead node"); + assert( is_not_dead(n1), "can not use dead node"); + assert( is_not_dead(n2), "can not use dead node"); + _in[0] = n0; if (n0 != NULL) n0->add_out((Node *)this); + _in[1] = n1; if (n1 != NULL) n1->add_out((Node *)this); + _in[2] = n2; if (n2 != NULL) n2->add_out((Node *)this); +} + +//------------------------------Node------------------------------------------- +Node::Node(Node *n0, Node *n1, Node *n2, Node *n3) + : _idx(IDX_INIT(4)) +{ + debug_only( verify_construction() ); + NOT_PRODUCT(nodes_created++); + // Assert we allocated space for input array already + assert( _in[3] == this, "Must pass arg count to 'new'" ); + assert( is_not_dead(n0), "can not use dead node"); + assert( is_not_dead(n1), "can not use dead node"); + assert( is_not_dead(n2), "can not use dead node"); + assert( is_not_dead(n3), "can not use dead node"); + _in[0] = n0; if (n0 != NULL) n0->add_out((Node *)this); + _in[1] = n1; if (n1 != NULL) n1->add_out((Node *)this); + _in[2] = n2; if (n2 != NULL) n2->add_out((Node *)this); + _in[3] = n3; if (n3 != NULL) n3->add_out((Node *)this); +} + +//------------------------------Node------------------------------------------- +Node::Node(Node *n0, Node *n1, Node *n2, Node *n3, Node *n4) + : _idx(IDX_INIT(5)) +{ + debug_only( verify_construction() ); + NOT_PRODUCT(nodes_created++); + // Assert we allocated space for input array already + assert( _in[4] == this, "Must pass arg count to 'new'" ); + assert( is_not_dead(n0), "can not use dead node"); + assert( is_not_dead(n1), "can not use dead node"); + assert( is_not_dead(n2), "can not use dead node"); + assert( is_not_dead(n3), "can not use dead node"); + assert( is_not_dead(n4), "can not use dead node"); + _in[0] = n0; if (n0 != NULL) n0->add_out((Node *)this); + _in[1] = n1; if (n1 != NULL) n1->add_out((Node *)this); + _in[2] = n2; if (n2 != NULL) n2->add_out((Node *)this); + _in[3] = n3; if (n3 != NULL) n3->add_out((Node *)this); + _in[4] = n4; if (n4 != NULL) n4->add_out((Node *)this); +} + +//------------------------------Node------------------------------------------- +Node::Node(Node *n0, Node *n1, Node *n2, Node *n3, + Node *n4, Node *n5) + : _idx(IDX_INIT(6)) +{ + debug_only( verify_construction() ); + NOT_PRODUCT(nodes_created++); + // Assert we allocated space for input array already + assert( _in[5] == this, "Must pass arg count to 'new'" ); + assert( is_not_dead(n0), "can not use dead node"); + assert( is_not_dead(n1), "can not use dead node"); + assert( is_not_dead(n2), "can not use dead node"); + assert( is_not_dead(n3), "can not use dead node"); + assert( is_not_dead(n4), "can not use dead node"); + assert( is_not_dead(n5), "can not use dead node"); + _in[0] = n0; if (n0 != NULL) n0->add_out((Node *)this); + _in[1] = n1; if (n1 != NULL) n1->add_out((Node *)this); + _in[2] = n2; if (n2 != NULL) n2->add_out((Node *)this); + _in[3] = n3; if (n3 != NULL) n3->add_out((Node *)this); + _in[4] = n4; if (n4 != NULL) n4->add_out((Node *)this); + _in[5] = n5; if (n5 != NULL) n5->add_out((Node *)this); +} + +//------------------------------Node------------------------------------------- +Node::Node(Node *n0, Node *n1, Node *n2, Node *n3, + Node *n4, Node *n5, Node *n6) + : _idx(IDX_INIT(7)) +{ + debug_only( verify_construction() ); + NOT_PRODUCT(nodes_created++); + // Assert we allocated space for input array already + assert( _in[6] == this, "Must pass arg count to 'new'" ); + assert( is_not_dead(n0), "can not use dead node"); + assert( is_not_dead(n1), "can not use dead node"); + assert( is_not_dead(n2), "can not use dead node"); + assert( is_not_dead(n3), "can not use dead node"); + assert( is_not_dead(n4), "can not use dead node"); + assert( is_not_dead(n5), "can not use dead node"); + assert( is_not_dead(n6), "can not use dead node"); + _in[0] = n0; if (n0 != NULL) n0->add_out((Node *)this); + _in[1] = n1; if (n1 != NULL) n1->add_out((Node *)this); + _in[2] = n2; if (n2 != NULL) n2->add_out((Node *)this); + _in[3] = n3; if (n3 != NULL) n3->add_out((Node *)this); + _in[4] = n4; if (n4 != NULL) n4->add_out((Node *)this); + _in[5] = n5; if (n5 != NULL) n5->add_out((Node *)this); + _in[6] = n6; if (n6 != NULL) n6->add_out((Node *)this); +} + + +//------------------------------clone------------------------------------------ +// Clone a Node. +Node *Node::clone() const { + Compile *compile = Compile::current(); + uint s = size_of(); // Size of inherited Node + Node *n = (Node*)compile->node_arena()->Amalloc_D(size_of() + _max*sizeof(Node*)); + Copy::conjoint_words_to_lower((HeapWord*)this, (HeapWord*)n, s); + // Set the new input pointer array + n->_in = (Node**)(((char*)n)+s); + // Cannot share the old output pointer array, so kill it + n->_out = NO_OUT_ARRAY; + // And reset the counters to 0 + n->_outcnt = 0; + n->_outmax = 0; + // Unlock this guy, since he is not in any hash table. + debug_only(n->_hash_lock = 0); + // Walk the old node's input list to duplicate its edges + uint i; + for( i = 0; i < len(); i++ ) { + Node *x = in(i); + n->_in[i] = x; + if (x != NULL) x->add_out(n); + } + if (is_macro()) + compile->add_macro_node(n); + + n->set_idx(compile->next_unique()); // Get new unique index as well + debug_only( n->verify_construction() ); + NOT_PRODUCT(nodes_created++); + // Do not patch over the debug_idx of a clone, because it makes it + // impossible to break on the clone's moment of creation. + //debug_only( n->set_debug_idx( debug_idx() ) ); + + compile->copy_node_notes_to(n, (Node*) this); + + // MachNode clone + uint nopnds; + if (this->is_Mach() && (nopnds = this->as_Mach()->num_opnds()) > 0) { + MachNode *mach = n->as_Mach(); + MachNode *mthis = this->as_Mach(); + // Get address of _opnd_array. + // It should be the same offset since it is the clone of this node. + MachOper **from = mthis->_opnds; + MachOper **to = (MachOper **)((size_t)(&mach->_opnds) + + pointer_delta((const void*)from, + (const void*)(&mthis->_opnds), 1)); + mach->_opnds = to; + for ( uint i = 0; i < nopnds; ++i ) { + to[i] = from[i]->clone(compile); + } + } + // cloning CallNode may need to clone JVMState + if (n->is_Call()) { + CallNode *call = n->as_Call(); + call->clone_jvms(); + } + return n; // Return the clone +} + +//---------------------------setup_is_top-------------------------------------- +// Call this when changing the top node, to reassert the invariants +// required by Node::is_top. See Compile::set_cached_top_node. +void Node::setup_is_top() { + if (this == (Node*)Compile::current()->top()) { + // This node has just become top. Kill its out array. + _outcnt = _outmax = 0; + _out = NULL; // marker value for top + assert(is_top(), "must be top"); + } else { + if (_out == NULL) _out = NO_OUT_ARRAY; + assert(!is_top(), "must not be top"); + } +} + + +//------------------------------~Node------------------------------------------ +// Fancy destructor; eagerly attempt to reclaim Node numberings and storage +extern int reclaim_idx ; +extern int reclaim_in ; +extern int reclaim_node; +void Node::destruct() { + // Eagerly reclaim unique Node numberings + Compile* compile = Compile::current(); + if ((uint)_idx+1 == compile->unique()) { + compile->set_unique(compile->unique()-1); +#ifdef ASSERT + reclaim_idx++; +#endif + } + // Clear debug info: + Node_Notes* nn = compile->node_notes_at(_idx); + if (nn != NULL) nn->clear(); + // Walk the input array, freeing the corresponding output edges + _cnt = _max; // forget req/prec distinction + uint i; + for( i = 0; i < _max; i++ ) { + set_req(i, NULL); + //assert(def->out(def->outcnt()-1) == (Node *)this,"bad def-use hacking in reclaim"); + } + assert(outcnt() == 0, "deleting a node must not leave a dangling use"); + // See if the input array was allocated just prior to the object + int edge_size = _max*sizeof(void*); + int out_edge_size = _outmax*sizeof(void*); + char *edge_end = ((char*)_in) + edge_size; + char *out_array = (char*)(_out == NO_OUT_ARRAY? NULL: _out); + char *out_edge_end = out_array + out_edge_size; + int node_size = size_of(); + + // Free the output edge array + if (out_edge_size > 0) { +#ifdef ASSERT + if( out_edge_end == compile->node_arena()->hwm() ) + reclaim_in += out_edge_size; // count reclaimed out edges with in edges +#endif + compile->node_arena()->Afree(out_array, out_edge_size); + } + + // Free the input edge array and the node itself + if( edge_end == (char*)this ) { +#ifdef ASSERT + if( edge_end+node_size == compile->node_arena()->hwm() ) { + reclaim_in += edge_size; + reclaim_node+= node_size; + } +#else + // It was; free the input array and object all in one hit + compile->node_arena()->Afree(_in,edge_size+node_size); +#endif + } else { + + // Free just the input array +#ifdef ASSERT + if( edge_end == compile->node_arena()->hwm() ) + reclaim_in += edge_size; +#endif + compile->node_arena()->Afree(_in,edge_size); + + // Free just the object +#ifdef ASSERT + if( ((char*)this) + node_size == compile->node_arena()->hwm() ) + reclaim_node+= node_size; +#else + compile->node_arena()->Afree(this,node_size); +#endif + } + if (is_macro()) { + compile->remove_macro_node(this); + } +#ifdef ASSERT + // We will not actually delete the storage, but we'll make the node unusable. + *(address*)this = badAddress; // smash the C++ vtbl, probably + _in = _out = (Node**) badAddress; + _max = _cnt = _outmax = _outcnt = 0; +#endif +} + +//------------------------------grow------------------------------------------- +// Grow the input array, making space for more edges +void Node::grow( uint len ) { + Arena* arena = Compile::current()->node_arena(); + uint new_max = _max; + if( new_max == 0 ) { + _max = 4; + _in = (Node**)arena->Amalloc(4*sizeof(Node*)); + Node** to = _in; + to[0] = NULL; + to[1] = NULL; + to[2] = NULL; + to[3] = NULL; + return; + } + while( new_max <= len ) new_max <<= 1; // Find next power-of-2 + // Trimming to limit allows a uint8 to handle up to 255 edges. + // Previously I was using only powers-of-2 which peaked at 128 edges. + //if( new_max >= limit ) new_max = limit-1; + _in = (Node**)arena->Arealloc(_in, _max*sizeof(Node*), new_max*sizeof(Node*)); + Copy::zero_to_bytes(&_in[_max], (new_max-_max)*sizeof(Node*)); // NULL all new space + _max = new_max; // Record new max length + // This assertion makes sure that Node::_max is wide enough to + // represent the numerical value of new_max. + assert(_max == new_max && _max > len, "int width of _max is too small"); +} + +//-----------------------------out_grow---------------------------------------- +// Grow the input array, making space for more edges +void Node::out_grow( uint len ) { + assert(!is_top(), "cannot grow a top node's out array"); + Arena* arena = Compile::current()->node_arena(); + uint new_max = _outmax; + if( new_max == 0 ) { + _outmax = 4; + _out = (Node **)arena->Amalloc(4*sizeof(Node*)); + return; + } + while( new_max <= len ) new_max <<= 1; // Find next power-of-2 + // Trimming to limit allows a uint8 to handle up to 255 edges. + // Previously I was using only powers-of-2 which peaked at 128 edges. + //if( new_max >= limit ) new_max = limit-1; + assert(_out != NULL && _out != NO_OUT_ARRAY, "out must have sensible value"); + _out = (Node**)arena->Arealloc(_out,_outmax*sizeof(Node*),new_max*sizeof(Node*)); + //Copy::zero_to_bytes(&_out[_outmax], (new_max-_outmax)*sizeof(Node*)); // NULL all new space + _outmax = new_max; // Record new max length + // This assertion makes sure that Node::_max is wide enough to + // represent the numerical value of new_max. + assert(_outmax == new_max && _outmax > len, "int width of _outmax is too small"); +} + +#ifdef ASSERT +//------------------------------is_dead---------------------------------------- +bool Node::is_dead() const { + // Mach and pinch point nodes may look like dead. + if( is_top() || is_Mach() || (Opcode() == Op_Node && _outcnt > 0) ) + return false; + for( uint i = 0; i < _max; i++ ) + if( _in[i] != NULL ) + return false; + dump(); + return true; +} +#endif + +//------------------------------add_req---------------------------------------- +// Add a new required input at the end +void Node::add_req( Node *n ) { + assert( is_not_dead(n), "can not use dead node"); + + // Look to see if I can move precedence down one without reallocating + if( (_cnt >= _max) || (in(_max-1) != NULL) ) + grow( _max+1 ); + + // Find a precedence edge to move + if( in(_cnt) != NULL ) { // Next precedence edge is busy? + uint i; + for( i=_cnt; i<_max; i++ ) + if( in(i) == NULL ) // Find the NULL at end of prec edge list + break; // There must be one, since we grew the array + _in[i] = in(_cnt); // Move prec over, making space for req edge + } + _in[_cnt++] = n; // Stuff over old prec edge + if (n != NULL) n->add_out((Node *)this); +} + +//---------------------------add_req_batch------------------------------------- +// Add a new required input at the end +void Node::add_req_batch( Node *n, uint m ) { + assert( is_not_dead(n), "can not use dead node"); + // check various edge cases + if ((int)m <= 1) { + assert((int)m >= 0, "oob"); + if (m != 0) add_req(n); + return; + } + + // Look to see if I can move precedence down one without reallocating + if( (_cnt+m) > _max || _in[_max-m] ) + grow( _max+m ); + + // Find a precedence edge to move + if( _in[_cnt] != NULL ) { // Next precedence edge is busy? + uint i; + for( i=_cnt; i<_max; i++ ) + if( _in[i] == NULL ) // Find the NULL at end of prec edge list + break; // There must be one, since we grew the array + // Slide all the precs over by m positions (assume #prec << m). + Copy::conjoint_words_to_higher((HeapWord*)&_in[_cnt], (HeapWord*)&_in[_cnt+m], ((i-_cnt)*sizeof(Node*))); + } + + // Stuff over the old prec edges + for(uint i=0; iis_top()) { + for(uint i=0; iadd_out((Node *)this); + } + } +} + +//------------------------------del_req---------------------------------------- +// Delete the required edge and compact the edge array +void Node::del_req( uint idx ) { + // First remove corresponding def-use edge + Node *n = in(idx); + if (n != NULL) n->del_out((Node *)this); + _in[idx] = in(--_cnt); // Compact the array + _in[_cnt] = NULL; // NULL out emptied slot +} + +//------------------------------ins_req---------------------------------------- +// Insert a new required input at the end +void Node::ins_req( uint idx, Node *n ) { + assert( is_not_dead(n), "can not use dead node"); + add_req(NULL); // Make space + assert( idx < _max, "Must have allocated enough space"); + // Slide over + if(_cnt-idx-1 > 0) { + Copy::conjoint_words_to_higher((HeapWord*)&_in[idx], (HeapWord*)&_in[idx+1], ((_cnt-idx-1)*sizeof(Node*))); + } + _in[idx] = n; // Stuff over old required edge + if (n != NULL) n->add_out((Node *)this); // Add reciprocal def-use edge +} + +//-----------------------------find_edge--------------------------------------- +int Node::find_edge(Node* n) { + for (uint i = 0; i < len(); i++) { + if (_in[i] == n) return i; + } + return -1; +} + +//----------------------------replace_edge------------------------------------- +int Node::replace_edge(Node* old, Node* neww) { + if (old == neww) return 0; // nothing to do + uint nrep = 0; + for (uint i = 0; i < len(); i++) { + if (in(i) == old) { + if (i < req()) + set_req(i, neww); + else + set_prec(i, neww); + nrep++; + } + } + return nrep; +} + +//-------------------------disconnect_inputs----------------------------------- +// NULL out all inputs to eliminate incoming Def-Use edges. +// Return the number of edges between 'n' and 'this' +int Node::disconnect_inputs(Node *n) { + int edges_to_n = 0; + + uint cnt = req(); + for( uint i = 0; i < cnt; ++i ) { + if( in(i) == 0 ) continue; + if( in(i) == n ) ++edges_to_n; + set_req(i, NULL); + } + // Remove precedence edges if any exist + // Note: Safepoints may have precedence edges, even during parsing + if( (req() != len()) && (in(req()) != NULL) ) { + uint max = len(); + for( uint i = 0; i < max; ++i ) { + if( in(i) == 0 ) continue; + if( in(i) == n ) ++edges_to_n; + set_prec(i, NULL); + } + } + + // Node::destruct requires all out edges be deleted first + // debug_only(destruct();) // no reuse benefit expected + return edges_to_n; +} + +//-----------------------------uncast--------------------------------------- +// %%% Temporary, until we sort out CheckCastPP vs. CastPP. +// Strip away casting. (It is depth-limited.) +Node* Node::uncast() const { + // Should be inline: + //return is_ConstraintCast() ? uncast_helper(this) : (Node*) this; + if (is_ConstraintCast() || + (is_Type() && req() == 2 && Opcode() == Op_CheckCastPP)) + return uncast_helper(this); + else + return (Node*) this; +} + +//---------------------------uncast_helper------------------------------------- +Node* Node::uncast_helper(const Node* p) { + uint max_depth = 3; + for (uint i = 0; i < max_depth; i++) { + if (p == NULL || p->req() != 2) { + break; + } else if (p->is_ConstraintCast()) { + p = p->in(1); + } else if (p->is_Type() && p->Opcode() == Op_CheckCastPP) { + p = p->in(1); + } else { + break; + } + } + return (Node*) p; +} + +//------------------------------add_prec--------------------------------------- +// Add a new precedence input. Precedence inputs are unordered, with +// duplicates removed and NULLs packed down at the end. +void Node::add_prec( Node *n ) { + assert( is_not_dead(n), "can not use dead node"); + + // Check for NULL at end + if( _cnt >= _max || in(_max-1) ) + grow( _max+1 ); + + // Find a precedence edge to move + uint i = _cnt; + while( in(i) != NULL ) i++; + _in[i] = n; // Stuff prec edge over NULL + if ( n != NULL) n->add_out((Node *)this); // Add mirror edge +} + +//------------------------------rm_prec---------------------------------------- +// Remove a precedence input. Precedence inputs are unordered, with +// duplicates removed and NULLs packed down at the end. +void Node::rm_prec( uint j ) { + + // Find end of precedence list to pack NULLs + uint i; + for( i=j; i<_max; i++ ) + if( !_in[i] ) // Find the NULL at end of prec edge list + break; + if (_in[j] != NULL) _in[j]->del_out((Node *)this); + _in[j] = _in[--i]; // Move last element over removed guy + _in[i] = NULL; // NULL out last element +} + +//------------------------------size_of---------------------------------------- +uint Node::size_of() const { return sizeof(*this); } + +//------------------------------ideal_reg-------------------------------------- +uint Node::ideal_reg() const { return 0; } + +//------------------------------jvms------------------------------------------- +JVMState* Node::jvms() const { return NULL; } + +#ifdef ASSERT +//------------------------------jvms------------------------------------------- +bool Node::verify_jvms(const JVMState* using_jvms) const { + for (JVMState* jvms = this->jvms(); jvms != NULL; jvms = jvms->caller()) { + if (jvms == using_jvms) return true; + } + return false; +} + +//------------------------------init_NodeProperty------------------------------ +void Node::init_NodeProperty() { + assert(_max_classes <= max_jushort, "too many NodeProperty classes"); + assert(_max_flags <= max_jushort, "too many NodeProperty flags"); +} +#endif + +//------------------------------format----------------------------------------- +// Print as assembly +void Node::format( PhaseRegAlloc *, outputStream *st ) const {} +//------------------------------emit------------------------------------------- +// Emit bytes starting at parameter 'ptr'. +void Node::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {} +//------------------------------size------------------------------------------- +// Size of instruction in bytes +uint Node::size(PhaseRegAlloc *ra_) const { return 0; } + +//------------------------------CFG Construction------------------------------- +// Nodes that end basic blocks, e.g. IfTrue/IfFalse, JumpProjNode, Root, +// Goto and Return. +const Node *Node::is_block_proj() const { return 0; } + +// Minimum guaranteed type +const Type *Node::bottom_type() const { return Type::BOTTOM; } + + +//------------------------------raise_bottom_type------------------------------ +// Get the worst-case Type output for this Node. +void Node::raise_bottom_type(const Type* new_type) { + if (is_Type()) { + TypeNode *n = this->as_Type(); + if (VerifyAliases) { + assert(new_type->higher_equal(n->type()), "new type must refine old type"); + } + n->set_type(new_type); + } else if (is_Load()) { + LoadNode *n = this->as_Load(); + if (VerifyAliases) { + assert(new_type->higher_equal(n->type()), "new type must refine old type"); + } + n->set_type(new_type); + } +} + +//------------------------------Identity--------------------------------------- +// Return a node that the given node is equivalent to. +Node *Node::Identity( PhaseTransform * ) { + return this; // Default to no identities +} + +//------------------------------Value------------------------------------------ +// Compute a new Type for a node using the Type of the inputs. +const Type *Node::Value( PhaseTransform * ) const { + return bottom_type(); // Default to worst-case Type +} + +//------------------------------Ideal------------------------------------------ +// +// 'Idealize' the graph rooted at this Node. +// +// In order to be efficient and flexible there are some subtle invariants +// these Ideal calls need to hold. Running with '+VerifyIterativeGVN' checks +// these invariants, although its too slow to have on by default. If you are +// hacking an Ideal call, be sure to test with +VerifyIterativeGVN! +// +// The Ideal call almost arbitrarily reshape the graph rooted at the 'this' +// pointer. If ANY change is made, it must return the root of the reshaped +// graph - even if the root is the same Node. Example: swapping the inputs +// to an AddINode gives the same answer and same root, but you still have to +// return the 'this' pointer instead of NULL. +// +// You cannot return an OLD Node, except for the 'this' pointer. Use the +// Identity call to return an old Node; basically if Identity can find +// another Node have the Ideal call make no change and return NULL. +// Example: AddINode::Ideal must check for add of zero; in this case it +// returns NULL instead of doing any graph reshaping. +// +// You cannot modify any old Nodes except for the 'this' pointer. Due to +// sharing there may be other users of the old Nodes relying on their current +// semantics. Modifying them will break the other users. +// Example: when reshape "(X+3)+4" into "X+7" you must leave the Node for +// "X+3" unchanged in case it is shared. +// +// If you modify the 'this' pointer's inputs, you must use 'set_req' with +// def-use info. If you are making a new Node (either as the new root or +// some new internal piece) you must NOT use set_req with def-use info. +// You can make a new Node with either 'new' or 'clone'. In either case, +// def-use info is (correctly) not generated. +// Example: reshape "(X+3)+4" into "X+7": +// set_req(1,in(1)->in(1) /* grab X */, du /* must use DU on 'this' */); +// set_req(2,phase->intcon(7),du); +// return this; +// Example: reshape "X*4" into "X<<1" +// return new (C,3) LShiftINode( in(1), phase->intcon(1) ); +// +// You must call 'phase->transform(X)' on any new Nodes X you make, except +// for the returned root node. Example: reshape "X*31" with "(X<<5)-1". +// Node *shift=phase->transform(new(C,3)LShiftINode(in(1),phase->intcon(5))); +// return new (C,3) AddINode(shift, phase->intcon(-1)); +// +// When making a Node for a constant use 'phase->makecon' or 'phase->intcon'. +// These forms are faster than 'phase->transform(new (C,1) ConNode())' and Do +// The Right Thing with def-use info. +// +// You cannot bury the 'this' Node inside of a graph reshape. If the reshaped +// graph uses the 'this' Node it must be the root. If you want a Node with +// the same Opcode as the 'this' pointer use 'clone'. +// +Node *Node::Ideal(PhaseGVN *phase, bool can_reshape) { + return NULL; // Default to being Ideal already +} + +// Some nodes have specific Ideal subgraph transformations only if they are +// unique users of specific nodes. Such nodes should be put on IGVN worklist +// for the transformations to happen. +bool Node::has_special_unique_user() const { + assert(outcnt() == 1, "match only for unique out"); + Node* n = unique_out(); + int op = Opcode(); + if( this->is_Store() ) { + // Condition for back-to-back stores folding. + return n->Opcode() == op && n->in(MemNode::Memory) == this; + } else if( op == Op_AddL ) { + // Condition for convL2I(addL(x,y)) ==> addI(convL2I(x),convL2I(y)) + return n->Opcode() == Op_ConvL2I && n->in(1) == this; + } else if( op == Op_SubI || op == Op_SubL ) { + // Condition for subI(x,subI(y,z)) ==> subI(addI(x,z),y) + return n->Opcode() == op && n->in(2) == this; + } + return false; +}; + +//------------------------------remove_dead_region----------------------------- +// This control node is dead. Follow the subgraph below it making everything +// using it dead as well. This will happen normally via the usual IterGVN +// worklist but this call is more efficient. Do not update use-def info +// inside the dead region, just at the borders. +static bool kill_dead_code( Node *dead, PhaseIterGVN *igvn ) { + // Con's are a popular node to re-hit in the hash table again. + if( dead->is_Con() ) return false; + + // Can't put ResourceMark here since igvn->_worklist uses the same arena + // for verify pass with +VerifyOpto and we add/remove elements in it here. + Node_List nstack(Thread::current()->resource_area()); + + Node *top = igvn->C->top(); + bool progress = false; + nstack.push(dead); + + while (nstack.size() > 0) { + dead = nstack.pop(); + if (dead->outcnt() > 0) { + // Keep dead node on stack until all uses are processed. + nstack.push(dead); + // For all Users of the Dead... ;-) + for (DUIterator_Last kmin, k = dead->last_outs(kmin); k >= kmin; ) { + Node* use = dead->last_out(k); + igvn->hash_delete(use); // Yank from hash table prior to mod + if (use->in(0) == dead) { // Found another dead node + assert (!use->is_Con(), "Control for Con node should be Root node.") + use->set_req(0, top); // Cut dead edge to prevent processing + nstack.push(use); // the dead node again. + } else { // Else found a not-dead user + for (uint j = 1; j < use->req(); j++) { + if (use->in(j) == dead) { // Turn all dead inputs into TOP + use->set_req(j, top); + } + } + igvn->_worklist.push(use); + } + // Refresh the iterator, since any number of kills might have happened. + k = dead->last_outs(kmin); + } + } else { // (dead->outcnt() == 0) + // Done with outputs. + igvn->hash_delete(dead); + igvn->_worklist.remove(dead); + igvn->set_type(dead, Type::TOP); + if (dead->is_macro()) { + igvn->C->remove_macro_node(dead); + } + // Kill all inputs to the dead guy + for (uint i=0; i < dead->req(); i++) { + Node *n = dead->in(i); // Get input to dead guy + if (n != NULL && !n->is_top()) { // Input is valid? + progress = true; + dead->set_req(i, top); // Smash input away + if (n->outcnt() == 0) { // Input also goes dead? + if (!n->is_Con()) + nstack.push(n); // Clear it out as well + } else if (n->outcnt() == 1 && + n->has_special_unique_user()) { + igvn->add_users_to_worklist( n ); + } else if (n->outcnt() <= 2 && n->is_Store()) { + // Push store's uses on worklist to enable folding optimization for + // store/store and store/load to the same address. + // The restriction (outcnt() <= 2) is the same as in set_req_X() + // and remove_globally_dead_node(). + igvn->add_users_to_worklist( n ); + } + } + } + } // (dead->outcnt() == 0) + } // while (nstack.size() > 0) for outputs + return progress; +} + +//------------------------------remove_dead_region----------------------------- +bool Node::remove_dead_region(PhaseGVN *phase, bool can_reshape) { + Node *n = in(0); + if( !n ) return false; + // Lost control into this guy? I.e., it became unreachable? + // Aggressively kill all unreachable code. + if (can_reshape && n->is_top()) { + return kill_dead_code(this, phase->is_IterGVN()); + } + + if( n->is_Region() && n->as_Region()->is_copy() ) { + Node *m = n->nonnull_req(); + set_req(0, m); + return true; + } + return false; +} + +//------------------------------Ideal_DU_postCCP------------------------------- +// Idealize graph, using DU info. Must clone result into new-space +Node *Node::Ideal_DU_postCCP( PhaseCCP * ) { + return NULL; // Default to no change +} + +//------------------------------hash------------------------------------------- +// Hash function over Nodes. +uint Node::hash() const { + uint sum = 0; + for( uint i=0; i<_cnt; i++ ) // Add in all inputs + sum = (sum<<1)-(uintptr_t)in(i); // Ignore embedded NULLs + return (sum>>2) + _cnt + Opcode(); +} + +//------------------------------cmp-------------------------------------------- +// Compare special parts of simple Nodes +uint Node::cmp( const Node &n ) const { + return 1; // Must be same +} + +//------------------------------rematerialize----------------------------------- +// Should we clone rather than spill this instruction? +bool Node::rematerialize() const { + if ( is_Mach() ) + return this->as_Mach()->rematerialize(); + else + return (_flags & Flag_rematerialize) != 0; +} + +//------------------------------needs_anti_dependence_check--------------------- +// Nodes which use memory without consuming it, hence need antidependences. +bool Node::needs_anti_dependence_check() const { + if( req() < 2 || (_flags & Flag_needs_anti_dependence_check) == 0 ) + return false; + else + return in(1)->bottom_type()->has_memory(); +} + + +// Get an integer constant from a ConNode (or CastIINode). +// Return a default value if there is no apparent constant here. +const TypeInt* Node::find_int_type() const { + if (this->is_Type()) { + return this->as_Type()->type()->isa_int(); + } else if (this->is_Con()) { + assert(is_Mach(), "should be ConNode(TypeNode) or else a MachNode"); + return this->bottom_type()->isa_int(); + } + return NULL; +} + +// Get a pointer constant from a ConstNode. +// Returns the constant if it is a pointer ConstNode +intptr_t Node::get_ptr() const { + assert( Opcode() == Op_ConP, "" ); + return ((ConPNode*)this)->type()->is_ptr()->get_con(); +} + +// Get a long constant from a ConNode. +// Return a default value if there is no apparent constant here. +const TypeLong* Node::find_long_type() const { + if (this->is_Type()) { + return this->as_Type()->type()->isa_long(); + } else if (this->is_Con()) { + assert(is_Mach(), "should be ConNode(TypeNode) or else a MachNode"); + return this->bottom_type()->isa_long(); + } + return NULL; +} + +// Get a double constant from a ConstNode. +// Returns the constant if it is a double ConstNode +jdouble Node::getd() const { + assert( Opcode() == Op_ConD, "" ); + return ((ConDNode*)this)->type()->is_double_constant()->getd(); +} + +// Get a float constant from a ConstNode. +// Returns the constant if it is a float ConstNode +jfloat Node::getf() const { + assert( Opcode() == Op_ConF, "" ); + return ((ConFNode*)this)->type()->is_float_constant()->getf(); +} + +#ifndef PRODUCT + +//----------------------------NotANode---------------------------------------- +// Used in debugging code to avoid walking across dead or uninitialized edges. +static inline bool NotANode(const Node* n) { + if (n == NULL) return true; + if (((intptr_t)n & 1) != 0) return true; // uninitialized, etc. + if (*(address*)n == badAddress) return true; // kill by Node::destruct + return false; +} + + +//------------------------------find------------------------------------------ +// Find a neighbor of this Node with the given _idx +// If idx is negative, find its absolute value, following both _in and _out. +static void find_recur( Node* &result, Node *n, int idx, bool only_ctrl, + VectorSet &old_space, VectorSet &new_space ) { + int node_idx = (idx >= 0) ? idx : -idx; + if (NotANode(n)) return; // Gracefully handle NULL, -1, 0xabababab, etc. + // Contained in new_space or old_space? + VectorSet *v = Compile::current()->node_arena()->contains(n) ? &new_space : &old_space; + if( v->test(n->_idx) ) return; + if( (int)n->_idx == node_idx + debug_only(|| n->debug_idx() == node_idx) ) { + if (result != NULL) + tty->print("find: " INTPTR_FORMAT " and " INTPTR_FORMAT " both have idx==%d\n", + (uintptr_t)result, (uintptr_t)n, node_idx); + result = n; + } + v->set(n->_idx); + for( uint i=0; ilen(); i++ ) { + if( only_ctrl && !(n->is_Region()) && (n->Opcode() != Op_Root) && (i != TypeFunc::Control) ) continue; + find_recur( result, n->in(i), idx, only_ctrl, old_space, new_space ); + } + // Search along forward edges also: + if (idx < 0 && !only_ctrl) { + for( uint j=0; joutcnt(); j++ ) { + find_recur( result, n->raw_out(j), idx, only_ctrl, old_space, new_space ); + } + } +#ifdef ASSERT + // Search along debug_orig edges last: + for (Node* orig = n->debug_orig(); orig != NULL; orig = orig->debug_orig()) { + if (NotANode(orig)) break; + find_recur( result, orig, idx, only_ctrl, old_space, new_space ); + } +#endif //ASSERT +} + +// call this from debugger: +Node* find_node(Node* n, int idx) { + return n->find(idx); +} + +//------------------------------find------------------------------------------- +Node* Node::find(int idx) const { + ResourceArea *area = Thread::current()->resource_area(); + VectorSet old_space(area), new_space(area); + Node* result = NULL; + find_recur( result, (Node*) this, idx, false, old_space, new_space ); + return result; +} + +//------------------------------find_ctrl-------------------------------------- +// Find an ancestor to this node in the control history with given _idx +Node* Node::find_ctrl(int idx) const { + ResourceArea *area = Thread::current()->resource_area(); + VectorSet old_space(area), new_space(area); + Node* result = NULL; + find_recur( result, (Node*) this, idx, true, old_space, new_space ); + return result; +} +#endif + + + +#ifndef PRODUCT +int Node::_in_dump_cnt = 0; + +// -----------------------------Name------------------------------------------- +extern const char *NodeClassNames[]; +const char *Node::Name() const { return NodeClassNames[Opcode()]; } + +static bool is_disconnected(const Node* n) { + for (uint i = 0; i < n->req(); i++) { + if (n->in(i) != NULL) return false; + } + return true; +} + +#ifdef ASSERT +static void dump_orig(Node* orig) { + Compile* C = Compile::current(); + if (NotANode(orig)) orig = NULL; + if (orig != NULL && !C->node_arena()->contains(orig)) orig = NULL; + if (orig == NULL) return; + tty->print(" !orig="); + Node* fast = orig->debug_orig(); // tortoise & hare algorithm to detect loops + if (NotANode(fast)) fast = NULL; + while (orig != NULL) { + bool discon = is_disconnected(orig); // if discon, print [123] else 123 + if (discon) tty->print("["); + if (!Compile::current()->node_arena()->contains(orig)) + tty->print("o"); + tty->print("%d", orig->_idx); + if (discon) tty->print("]"); + orig = orig->debug_orig(); + if (NotANode(orig)) orig = NULL; + if (orig != NULL && !C->node_arena()->contains(orig)) orig = NULL; + if (orig != NULL) tty->print(","); + if (fast != NULL) { + // Step fast twice for each single step of orig: + fast = fast->debug_orig(); + if (NotANode(fast)) fast = NULL; + if (fast != NULL && fast != orig) { + fast = fast->debug_orig(); + if (NotANode(fast)) fast = NULL; + } + if (fast == orig) { + tty->print("..."); + break; + } + } + } +} + +void Node::set_debug_orig(Node* orig) { + _debug_orig = orig; + if (BreakAtNode == 0) return; + if (NotANode(orig)) orig = NULL; + int trip = 10; + while (orig != NULL) { + if (orig->debug_idx() == BreakAtNode || (int)orig->_idx == BreakAtNode) { + tty->print_cr("BreakAtNode: _idx=%d _debug_idx=%d orig._idx=%d orig._debug_idx=%d", + this->_idx, this->debug_idx(), orig->_idx, orig->debug_idx()); + BREAKPOINT; + } + orig = orig->debug_orig(); + if (NotANode(orig)) orig = NULL; + if (trip-- <= 0) break; + } +} +#endif //ASSERT + +//------------------------------dump------------------------------------------ +// Dump a Node +void Node::dump() const { + Compile* C = Compile::current(); + bool is_new = C->node_arena()->contains(this); + _in_dump_cnt++; + tty->print("%c%d\t%s\t=== ", + is_new ? ' ' : 'o', _idx, Name()); + + // Dump the required and precedence inputs + dump_req(); + dump_prec(); + // Dump the outputs + dump_out(); + + if (is_disconnected(this)) { +#ifdef ASSERT + tty->print(" [%d]",debug_idx()); + dump_orig(debug_orig()); +#endif + tty->cr(); + _in_dump_cnt--; + return; // don't process dead nodes + } + + // Dump node-specific info + dump_spec(tty); +#ifdef ASSERT + // Dump the non-reset _debug_idx + if( Verbose && WizardMode ) { + tty->print(" [%d]",debug_idx()); + } +#endif + + const Type *t = bottom_type(); + + if (t != NULL && (t->isa_instptr() || t->isa_klassptr())) { + const TypeInstPtr *toop = t->isa_instptr(); + const TypeKlassPtr *tkls = t->isa_klassptr(); + ciKlass* klass = toop ? toop->klass() : (tkls ? tkls->klass() : NULL ); + if( klass && klass->is_loaded() && klass->is_interface() ) { + tty->print(" Interface:"); + } else if( toop ) { + tty->print(" Oop:"); + } else if( tkls ) { + tty->print(" Klass:"); + } + t->dump(); + } else if( t == Type::MEMORY ) { + tty->print(" Memory:"); + MemNode::dump_adr_type(this, adr_type(), tty); + } else if( Verbose || WizardMode ) { + tty->print(" Type:"); + if( t ) { + t->dump(); + } else { + tty->print("no type"); + } + } + if (is_new) { + debug_only(dump_orig(debug_orig())); + Node_Notes* nn = C->node_notes_at(_idx); + if (nn != NULL && !nn->is_clear()) { + if (nn->jvms() != NULL) { + tty->print(" !jvms:"); + nn->jvms()->dump_spec(tty); + } + } + } + tty->cr(); + _in_dump_cnt--; +} + +//------------------------------dump_req-------------------------------------- +void Node::dump_req() const { + // Dump the required input edges + for (uint i = 0; i < req(); i++) { // For all required inputs + Node* d = in(i); + if (d == NULL) { + tty->print("_ "); + } else if (NotANode(d)) { + tty->print("NotANode "); // uninitialized, sentinel, garbage, etc. + } else { + tty->print("%c%d ", Compile::current()->node_arena()->contains(d) ? ' ' : 'o', d->_idx); + } + } +} + + +//------------------------------dump_prec------------------------------------- +void Node::dump_prec() const { + // Dump the precedence edges + int any_prec = 0; + for (uint i = req(); i < len(); i++) { // For all precedence inputs + Node* p = in(i); + if (p != NULL) { + if( !any_prec++ ) tty->print(" |"); + if (NotANode(p)) { tty->print("NotANode "); continue; } + tty->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); + } + } +} + +//------------------------------dump_out-------------------------------------- +void Node::dump_out() const { + // Delimit the output edges + tty->print(" [["); + // Dump the output edges + for (uint i = 0; i < _outcnt; i++) { // For all outputs + Node* u = _out[i]; + if (u == NULL) { + tty->print("_ "); + } else if (NotANode(u)) { + tty->print("NotANode "); + } else { + tty->print("%c%d ", Compile::current()->node_arena()->contains(u) ? ' ' : 'o', u->_idx); + } + } + tty->print("]] "); +} + +//------------------------------dump_nodes------------------------------------- + +// Helper class for dump_nodes. Wraps an old and new VectorSet. +class OldNewVectorSet : public StackObj { + Arena* _node_arena; + VectorSet _old_vset, _new_vset; + VectorSet* select(Node* n) { + return _node_arena->contains(n) ? &_new_vset : &_old_vset; + } + public: + OldNewVectorSet(Arena* node_arena, ResourceArea* area) : + _node_arena(node_arena), + _old_vset(area), _new_vset(area) {} + + void set(Node* n) { select(n)->set(n->_idx); } + bool test_set(Node* n) { return select(n)->test_set(n->_idx) != 0; } + bool test(Node* n) { return select(n)->test(n->_idx) != 0; } + void del(Node* n) { (*select(n)) >>= n->_idx; } +}; + + +static void dump_nodes(const Node* start, int d, bool only_ctrl) { + Node* s = (Node*)start; // remove const + if (NotANode(s)) return; + + Compile* C = Compile::current(); + ResourceArea *area = Thread::current()->resource_area(); + Node_Stack stack(area, MIN2((uint)ABS(d), C->unique() >> 1)); + OldNewVectorSet visited(C->node_arena(), area); + OldNewVectorSet on_stack(C->node_arena(), area); + + visited.set(s); + on_stack.set(s); + stack.push(s, 0); + if (d < 0) s->dump(); + + // Do a depth first walk over edges + while (stack.is_nonempty()) { + Node* tp = stack.node(); + uint idx = stack.index(); + uint limit = d > 0 ? tp->len() : tp->outcnt(); + if (idx >= limit) { + // no more arcs to visit + if (d > 0) tp->dump(); + on_stack.del(tp); + stack.pop(); + } else { + // process the "idx"th arc + stack.set_index(idx + 1); + Node* n = d > 0 ? tp->in(idx) : tp->raw_out(idx); + + if (NotANode(n)) continue; + // do not recurse through top or the root (would reach unrelated stuff) + if (n->is_Root() || n->is_top()) continue; + if (only_ctrl && !n->is_CFG()) continue; + + if (!visited.test_set(n)) { // forward arc + // Limit depth + if (stack.size() < (uint)ABS(d)) { + if (d < 0) n->dump(); + stack.push(n, 0); + on_stack.set(n); + } + } else { // back or cross arc + if (on_stack.test(n)) { // back arc + // print loop if there are no phis or regions in the mix + bool found_loop_breaker = false; + int k; + for (k = stack.size() - 1; k >= 0; k--) { + Node* m = stack.node_at(k); + if (m->is_Phi() || m->is_Region() || m->is_Root() || m->is_Start()) { + found_loop_breaker = true; + break; + } + if (m == n) // Found loop head + break; + } + assert(k >= 0, "n must be on stack"); + + if (!found_loop_breaker) { + tty->print("# %s LOOP FOUND:", only_ctrl ? "CONTROL" : "DATA"); + for (int i = stack.size() - 1; i >= k; i--) { + Node* m = stack.node_at(i); + bool mnew = C->node_arena()->contains(m); + tty->print(" %s%d:%s", (mnew? "": "o"), m->_idx, m->Name()); + if (i != 0) tty->print(d > 0? " <-": " ->"); + } + tty->cr(); + } + } + } + } + } +} + +//------------------------------dump------------------------------------------- +void Node::dump(int d) const { + dump_nodes(this, d, false); +} + +//------------------------------dump_ctrl-------------------------------------- +// Dump a Node's control history to depth +void Node::dump_ctrl(int d) const { + dump_nodes(this, d, true); +} + +// VERIFICATION CODE +// For each input edge to a node (ie - for each Use-Def edge), verify that +// there is a corresponding Def-Use edge. +//------------------------------verify_edges----------------------------------- +void Node::verify_edges(Unique_Node_List &visited) { + uint i, j, idx; + int cnt; + Node *n; + + // Recursive termination test + if (visited.member(this)) return; + visited.push(this); + + // Walk over all input edges, checking for correspondance + for( i = 0; i < len(); i++ ) { + n = in(i); + if (n != NULL && !n->is_top()) { + // Count instances of (Node *)this + cnt = 0; + for (idx = 0; idx < n->_outcnt; idx++ ) { + if (n->_out[idx] == (Node *)this) cnt++; + } + assert( cnt > 0,"Failed to find Def-Use edge." ); + // Check for duplicate edges + // walk the input array downcounting the input edges to n + for( j = 0; j < len(); j++ ) { + if( in(j) == n ) cnt--; + } + assert( cnt == 0,"Mismatched edge count."); + } else if (n == NULL) { + assert(i >= req() || i == 0 || is_Region() || is_Phi(), "only regions or phis have null data edges"); + } else { + assert(n->is_top(), "sanity"); + // Nothing to check. + } + } + // Recursive walk over all input edges + for( i = 0; i < len(); i++ ) { + n = in(i); + if( n != NULL ) + in(i)->verify_edges(visited); + } +} + +//------------------------------verify_recur----------------------------------- +static const Node *unique_top = NULL; + +void Node::verify_recur(const Node *n, int verify_depth, + VectorSet &old_space, VectorSet &new_space) { + if ( verify_depth == 0 ) return; + if (verify_depth > 0) --verify_depth; + + Compile* C = Compile::current(); + + // Contained in new_space or old_space? + VectorSet *v = C->node_arena()->contains(n) ? &new_space : &old_space; + // Check for visited in the proper space. Numberings are not unique + // across spaces so we need a seperate VectorSet for each space. + if( v->test_set(n->_idx) ) return; + + if (n->is_Con() && n->bottom_type() == Type::TOP) { + if (C->cached_top_node() == NULL) + C->set_cached_top_node((Node*)n); + assert(C->cached_top_node() == n, "TOP node must be unique"); + } + + for( uint i = 0; i < n->len(); i++ ) { + Node *x = n->in(i); + if (!x || x->is_top()) continue; + + // Verify my input has a def-use edge to me + if (true /*VerifyDefUse*/) { + // Count use-def edges from n to x + int cnt = 0; + for( uint j = 0; j < n->len(); j++ ) + if( n->in(j) == x ) + cnt++; + // Count def-use edges from x to n + uint max = x->_outcnt; + for( uint k = 0; k < max; k++ ) + if (x->_out[k] == n) + cnt--; + assert( cnt == 0, "mismatched def-use edge counts" ); + } + + verify_recur(x, verify_depth, old_space, new_space); + } + +} + +//------------------------------verify----------------------------------------- +// Check Def-Use info for my subgraph +void Node::verify() const { + Compile* C = Compile::current(); + Node* old_top = C->cached_top_node(); + ResourceMark rm; + ResourceArea *area = Thread::current()->resource_area(); + VectorSet old_space(area), new_space(area); + verify_recur(this, -1, old_space, new_space); + C->set_cached_top_node(old_top); +} +#endif + + +//------------------------------walk------------------------------------------- +// Graph walk, with both pre-order and post-order functions +void Node::walk(NFunc pre, NFunc post, void *env) { + VectorSet visited(Thread::current()->resource_area()); // Setup for local walk + walk_(pre, post, env, visited); +} + +void Node::walk_(NFunc pre, NFunc post, void *env, VectorSet &visited) { + if( visited.test_set(_idx) ) return; + pre(*this,env); // Call the pre-order walk function + for( uint i=0; i<_max; i++ ) + if( in(i) ) // Input exists and is not walked? + in(i)->walk_(pre,post,env,visited); // Walk it with pre & post functions + post(*this,env); // Call the post-order walk function +} + +void Node::nop(Node &, void*) {} + +//------------------------------Registers-------------------------------------- +// Do we Match on this edge index or not? Generally false for Control +// and true for everything else. Weird for calls & returns. +uint Node::match_edge(uint idx) const { + return idx; // True for other than index 0 (control) +} + +// Register classes are defined for specific machines +const RegMask &Node::out_RegMask() const { + ShouldNotCallThis(); + return *(new RegMask()); +} + +const RegMask &Node::in_RegMask(uint) const { + ShouldNotCallThis(); + return *(new RegMask()); +} + +//============================================================================= +//----------------------------------------------------------------------------- +void Node_Array::reset( Arena *new_arena ) { + _a->Afree(_nodes,_max*sizeof(Node*)); + _max = 0; + _nodes = NULL; + _a = new_arena; +} + +//------------------------------clear------------------------------------------ +// Clear all entries in _nodes to NULL but keep storage +void Node_Array::clear() { + Copy::zero_to_bytes( _nodes, _max*sizeof(Node*) ); +} + +//----------------------------------------------------------------------------- +void Node_Array::grow( uint i ) { + if( !_max ) { + _max = 1; + _nodes = (Node**)_a->Amalloc( _max * sizeof(Node*) ); + _nodes[0] = NULL; + } + uint old = _max; + while( i >= _max ) _max <<= 1; // Double to fit + _nodes = (Node**)_a->Arealloc( _nodes, old*sizeof(Node*),_max*sizeof(Node*)); + Copy::zero_to_bytes( &_nodes[old], (_max-old)*sizeof(Node*) ); +} + +//----------------------------------------------------------------------------- +void Node_Array::insert( uint i, Node *n ) { + if( _nodes[_max-1] ) grow(_max); // Get more space if full + Copy::conjoint_words_to_higher((HeapWord*)&_nodes[i], (HeapWord*)&_nodes[i+1], ((_max-i-1)*sizeof(Node*))); + _nodes[i] = n; +} + +//----------------------------------------------------------------------------- +void Node_Array::remove( uint i ) { + Copy::conjoint_words_to_lower((HeapWord*)&_nodes[i+1], (HeapWord*)&_nodes[i], ((_max-i-1)*sizeof(Node*))); + _nodes[_max-1] = NULL; +} + +//----------------------------------------------------------------------------- +void Node_Array::sort( C_sort_func_t func) { + qsort( _nodes, _max, sizeof( Node* ), func ); +} + +//----------------------------------------------------------------------------- +void Node_Array::dump() const { +#ifndef PRODUCT + for( uint i = 0; i < _max; i++ ) { + Node *nn = _nodes[i]; + if( nn != NULL ) { + tty->print("%5d--> ",i); nn->dump(); + } + } +#endif +} + +//--------------------------is_iteratively_computed------------------------------ +// Operation appears to be iteratively computed (such as an induction variable) +// It is possible for this operation to return false for a loop-varying +// value, if it appears (by local graph inspection) to be computed by a simple conditional. +bool Node::is_iteratively_computed() { + if (ideal_reg()) { // does operation have a result register? + for (uint i = 1; i < req(); i++) { + Node* n = in(i); + if (n != NULL && n->is_Phi()) { + for (uint j = 1; j < n->req(); j++) { + if (n->in(j) == this) { + return true; + } + } + } + } + } + return false; +} + +//--------------------------find_similar------------------------------ +// Return a node with opcode "opc" and same inputs as "this" if one can +// be found; Otherwise return NULL; +Node* Node::find_similar(int opc) { + if (req() >= 2) { + Node* def = in(1); + if (def && def->outcnt() >= 2) { + for (DUIterator_Fast dmax, i = def->fast_outs(dmax); i < dmax; i++) { + Node* use = def->fast_out(i); + if (use->Opcode() == opc && + use->req() == req()) { + uint j; + for (j = 0; j < use->req(); j++) { + if (use->in(j) != in(j)) { + break; + } + } + if (j == use->req()) { + return use; + } + } + } + } + } + return NULL; +} + + +//--------------------------unique_ctrl_out------------------------------ +// Return the unique control out if only one. Null if none or more than one. +Node* Node::unique_ctrl_out() { + Node* found = NULL; + for (uint i = 0; i < outcnt(); i++) { + Node* use = raw_out(i); + if (use->is_CFG() && use != this) { + if (found != NULL) return NULL; + found = use; + } + } + return found; +} + +//============================================================================= +//------------------------------yank------------------------------------------- +// Find and remove +void Node_List::yank( Node *n ) { + uint i; + for( i = 0; i < _cnt; i++ ) + if( _nodes[i] == n ) + break; + + if( i < _cnt ) + _nodes[i] = _nodes[--_cnt]; +} + +//------------------------------dump------------------------------------------- +void Node_List::dump() const { +#ifndef PRODUCT + for( uint i = 0; i < _cnt; i++ ) + if( _nodes[i] ) { + tty->print("%5d--> ",i); + _nodes[i]->dump(); + } +#endif +} + +//============================================================================= +//------------------------------remove----------------------------------------- +void Unique_Node_List::remove( Node *n ) { + if( _in_worklist[n->_idx] ) { + for( uint i = 0; i < size(); i++ ) + if( _nodes[i] == n ) { + map(i,Node_List::pop()); + _in_worklist >>= n->_idx; + return; + } + ShouldNotReachHere(); + } +} + +//-----------------------remove_useless_nodes---------------------------------- +// Remove useless nodes from worklist +void Unique_Node_List::remove_useless_nodes(VectorSet &useful) { + + for( uint i = 0; i < size(); ++i ) { + Node *n = at(i); + assert( n != NULL, "Did not expect null entries in worklist"); + if( ! useful.test(n->_idx) ) { + _in_worklist >>= n->_idx; + map(i,Node_List::pop()); + // Node *replacement = Node_List::pop(); + // if( i != size() ) { // Check if removing last entry + // _nodes[i] = replacement; + // } + --i; // Visit popped node + // If it was last entry, loop terminates since size() was also reduced + } + } +} + +//============================================================================= +void Node_Stack::grow() { + size_t old_top = pointer_delta(_inode_top,_inodes,sizeof(INode)); // save _top + size_t old_max = pointer_delta(_inode_max,_inodes,sizeof(INode)); + size_t max = old_max << 1; // max * 2 + _inodes = REALLOC_ARENA_ARRAY(_a, INode, _inodes, old_max, max); + _inode_max = _inodes + max; + _inode_top = _inodes + old_top; // restore _top +} + +//============================================================================= +uint TypeNode::size_of() const { return sizeof(*this); } +#ifndef PRODUCT +void TypeNode::dump_spec(outputStream *st) const { + if( !Verbose && !WizardMode ) { + // standard dump does this in Verbose and WizardMode + st->print(" #"); _type->dump_on(st); + } +} +#endif +uint TypeNode::hash() const { + return Node::hash() + _type->hash(); +} +uint TypeNode::cmp( const Node &n ) const +{ return !Type::cmp( _type, ((TypeNode&)n)._type ); } +const Type *TypeNode::bottom_type() const { return _type; } +const Type *TypeNode::Value( PhaseTransform * ) const { return _type; } + +//------------------------------ideal_reg-------------------------------------- +uint TypeNode::ideal_reg() const { + return Matcher::base2reg[_type->base()]; +} diff --git a/hotspot/src/share/vm/opto/node.hpp b/hotspot/src/share/vm/opto/node.hpp new file mode 100644 index 00000000000..f93562c09e7 --- /dev/null +++ b/hotspot/src/share/vm/opto/node.hpp @@ -0,0 +1,1492 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + + +class AbstractLockNode; +class AddNode; +class AddPNode; +class AliasInfo; +class AllocateArrayNode; +class AllocateNode; +class Block; +class Block_Array; +class BoolNode; +class BoxLockNode; +class CMoveNode; +class CallDynamicJavaNode; +class CallJavaNode; +class CallLeafNode; +class CallNode; +class CallRuntimeNode; +class CallStaticJavaNode; +class CatchNode; +class CatchProjNode; +class CheckCastPPNode; +class CmpNode; +class CodeBuffer; +class ConstraintCastNode; +class ConNode; +class CountedLoopNode; +class CountedLoopEndNode; +class FastLockNode; +class FastUnlockNode; +class IfNode; +class InitializeNode; +class JVMState; +class JumpNode; +class JumpProjNode; +class LoadNode; +class LoadStoreNode; +class LockNode; +class LoopNode; +class MachCallDynamicJavaNode; +class MachCallJavaNode; +class MachCallLeafNode; +class MachCallNode; +class MachCallRuntimeNode; +class MachCallStaticJavaNode; +class MachIfNode; +class MachNode; +class MachNullCheckNode; +class MachReturnNode; +class MachSafePointNode; +class MachSpillCopyNode; +class MachTempNode; +class Matcher; +class MemBarNode; +class MemNode; +class MergeMemNode; +class MulNode; +class MultiNode; +class MultiBranchNode; +class NeverBranchNode; +class Node; +class Node_Array; +class Node_List; +class Node_Stack; +class NullCheckNode; +class OopMap; +class PCTableNode; +class PhaseCCP; +class PhaseGVN; +class PhaseIterGVN; +class PhaseRegAlloc; +class PhaseTransform; +class PhaseValues; +class PhiNode; +class Pipeline; +class ProjNode; +class RegMask; +class RegionNode; +class RootNode; +class SafePointNode; +class StartNode; +class State; +class StoreNode; +class SubNode; +class Type; +class TypeNode; +class UnlockNode; +class VectorSet; +class IfTrueNode; +class IfFalseNode; +typedef void (*NFunc)(Node&,void*); +extern "C" { + typedef int (*C_sort_func_t)(const void *, const void *); +} + +// The type of all node counts and indexes. +// It must hold at least 16 bits, but must also be fast to load and store. +// This type, if less than 32 bits, could limit the number of possible nodes. +// (To make this type platform-specific, move to globalDefinitions_xxx.hpp.) +typedef unsigned int node_idx_t; + + +#ifndef OPTO_DU_ITERATOR_ASSERT +#ifdef ASSERT +#define OPTO_DU_ITERATOR_ASSERT 1 +#else +#define OPTO_DU_ITERATOR_ASSERT 0 +#endif +#endif //OPTO_DU_ITERATOR_ASSERT + +#if OPTO_DU_ITERATOR_ASSERT +class DUIterator; +class DUIterator_Fast; +class DUIterator_Last; +#else +typedef uint DUIterator; +typedef Node** DUIterator_Fast; +typedef Node** DUIterator_Last; +#endif + +// Node Sentinel +#define NodeSentinel (Node*)-1 + +// Unknown count frequency +#define COUNT_UNKNOWN (-1.0f) + +//------------------------------Node------------------------------------------- +// Nodes define actions in the program. They create values, which have types. +// They are both vertices in a directed graph and program primitives. Nodes +// are labeled; the label is the "opcode", the primitive function in the lambda +// calculus sense that gives meaning to the Node. Node inputs are ordered (so +// that "a-b" is different from "b-a"). The inputs to a Node are the inputs to +// the Node's function. These inputs also define a Type equation for the Node. +// Solving these Type equations amounts to doing dataflow analysis. +// Control and data are uniformly represented in the graph. Finally, Nodes +// have a unique dense integer index which is used to index into side arrays +// whenever I have phase-specific information. + +class Node { + // Lots of restrictions on cloning Nodes + Node(const Node&); // not defined; linker error to use these + Node &operator=(const Node &rhs); + +public: + friend class Compile; + #if OPTO_DU_ITERATOR_ASSERT + friend class DUIterator_Common; + friend class DUIterator; + friend class DUIterator_Fast; + friend class DUIterator_Last; + #endif + + // Because Nodes come and go, I define an Arena of Node structures to pull + // from. This should allow fast access to node creation & deletion. This + // field is a local cache of a value defined in some "program fragment" for + // which these Nodes are just a part of. + + // New Operator that takes a Compile pointer, this will eventually + // be the "new" New operator. + inline void* operator new( size_t x, Compile* C) { + Node* n = (Node*)C->node_arena()->Amalloc_D(x); +#ifdef ASSERT + n->_in = (Node**)n; // magic cookie for assertion check +#endif + n->_out = (Node**)C; + return (void*)n; + } + + // New Operator that takes a Compile pointer, this will eventually + // be the "new" New operator. + inline void* operator new( size_t x, Compile* C, int y) { + Node* n = (Node*)C->node_arena()->Amalloc_D(x + y*sizeof(void*)); + n->_in = (Node**)(((char*)n) + x); +#ifdef ASSERT + n->_in[y-1] = n; // magic cookie for assertion check +#endif + n->_out = (Node**)C; + return (void*)n; + } + + // Delete is a NOP + void operator delete( void *ptr ) {} + // Fancy destructor; eagerly attempt to reclaim Node numberings and storage + void destruct(); + + // Create a new Node. Required is the number is of inputs required for + // semantic correctness. + Node( uint required ); + + // Create a new Node with given input edges. + // This version requires use of the "edge-count" new. + // E.g. new (C,3) FooNode( C, NULL, left, right ); + Node( Node *n0 ); + Node( Node *n0, Node *n1 ); + Node( Node *n0, Node *n1, Node *n2 ); + Node( Node *n0, Node *n1, Node *n2, Node *n3 ); + Node( Node *n0, Node *n1, Node *n2, Node *n3, Node *n4 ); + Node( Node *n0, Node *n1, Node *n2, Node *n3, Node *n4, Node *n5 ); + Node( Node *n0, Node *n1, Node *n2, Node *n3, + Node *n4, Node *n5, Node *n6 ); + + // Clone an inherited Node given only the base Node type. + Node* clone() const; + + // Clone a Node, immediately supplying one or two new edges. + // The first and second arguments, if non-null, replace in(1) and in(2), + // respectively. + Node* clone_with_data_edge(Node* in1, Node* in2 = NULL) const { + Node* nn = clone(); + if (in1 != NULL) nn->set_req(1, in1); + if (in2 != NULL) nn->set_req(2, in2); + return nn; + } + +private: + // Shared setup for the above constructors. + // Handles all interactions with Compile::current. + // Puts initial values in all Node fields except _idx. + // Returns the initial value for _idx, which cannot + // be initialized by assignment. + inline int Init(int req, Compile* C); + +//----------------- input edge handling +protected: + friend class PhaseCFG; // Access to address of _in array elements + Node **_in; // Array of use-def references to Nodes + Node **_out; // Array of def-use references to Nodes + + // Input edges are split into two catagories. Required edges are required + // for semantic correctness; order is important and NULLs are allowed. + // Precedence edges are used to help determine execution order and are + // added, e.g., for scheduling purposes. They are unordered and not + // duplicated; they have no embedded NULLs. Edges from 0 to _cnt-1 + // are required, from _cnt to _max-1 are precedence edges. + node_idx_t _cnt; // Total number of required Node inputs. + + node_idx_t _max; // Actual length of input array. + + // Output edges are an unordered list of def-use edges which exactly + // correspond to required input edges which point from other nodes + // to this one. Thus the count of the output edges is the number of + // users of this node. + node_idx_t _outcnt; // Total number of Node outputs. + + node_idx_t _outmax; // Actual length of output array. + + // Grow the actual input array to the next larger power-of-2 bigger than len. + void grow( uint len ); + // Grow the output array to the next larger power-of-2 bigger than len. + void out_grow( uint len ); + + public: + // Each Node is assigned a unique small/dense number. This number is used + // to index into auxiliary arrays of data and bitvectors. + // It is declared const to defend against inadvertant assignment, + // since it is used by clients as a naked field. + const node_idx_t _idx; + + // Get the (read-only) number of input edges + uint req() const { return _cnt; } + uint len() const { return _max; } + // Get the (read-only) number of output edges + uint outcnt() const { return _outcnt; } + +#if OPTO_DU_ITERATOR_ASSERT + // Iterate over the out-edges of this node. Deletions are illegal. + inline DUIterator outs() const; + // Use this when the out array might have changed to suppress asserts. + inline DUIterator& refresh_out_pos(DUIterator& i) const; + // Does the node have an out at this position? (Used for iteration.) + inline bool has_out(DUIterator& i) const; + inline Node* out(DUIterator& i) const; + // Iterate over the out-edges of this node. All changes are illegal. + inline DUIterator_Fast fast_outs(DUIterator_Fast& max) const; + inline Node* fast_out(DUIterator_Fast& i) const; + // Iterate over the out-edges of this node, deleting one at a time. + inline DUIterator_Last last_outs(DUIterator_Last& min) const; + inline Node* last_out(DUIterator_Last& i) const; + // The inline bodies of all these methods are after the iterator definitions. +#else + // Iterate over the out-edges of this node. Deletions are illegal. + // This iteration uses integral indexes, to decouple from array reallocations. + DUIterator outs() const { return 0; } + // Use this when the out array might have changed to suppress asserts. + DUIterator refresh_out_pos(DUIterator i) const { return i; } + + // Reference to the i'th output Node. Error if out of bounds. + Node* out(DUIterator i) const { assert(i < _outcnt, "oob"); return _out[i]; } + // Does the node have an out at this position? (Used for iteration.) + bool has_out(DUIterator i) const { return i < _outcnt; } + + // Iterate over the out-edges of this node. All changes are illegal. + // This iteration uses a pointer internal to the out array. + DUIterator_Fast fast_outs(DUIterator_Fast& max) const { + Node** out = _out; + // Assign a limit pointer to the reference argument: + max = out + (ptrdiff_t)_outcnt; + // Return the base pointer: + return out; + } + Node* fast_out(DUIterator_Fast i) const { return *i; } + // Iterate over the out-edges of this node, deleting one at a time. + // This iteration uses a pointer internal to the out array. + DUIterator_Last last_outs(DUIterator_Last& min) const { + Node** out = _out; + // Assign a limit pointer to the reference argument: + min = out; + // Return the pointer to the start of the iteration: + return out + (ptrdiff_t)_outcnt - 1; + } + Node* last_out(DUIterator_Last i) const { return *i; } +#endif + + // Reference to the i'th input Node. Error if out of bounds. + Node* in(uint i) const { assert(i < _max,"oob"); return _in[i]; } + // Reference to the i'th output Node. Error if out of bounds. + // Use this accessor sparingly. We are going trying to use iterators instead. + Node* raw_out(uint i) const { assert(i < _outcnt,"oob"); return _out[i]; } + // Return the unique out edge. + Node* unique_out() const { assert(_outcnt==1,"not unique"); return _out[0]; } + // Delete out edge at position 'i' by moving last out edge to position 'i' + void raw_del_out(uint i) { + assert(i < _outcnt,"oob"); + assert(_outcnt > 0,"oob"); + #if OPTO_DU_ITERATOR_ASSERT + // Record that a change happened here. + debug_only(_last_del = _out[i]; ++_del_tick); + #endif + _out[i] = _out[--_outcnt]; + // Smash the old edge so it can't be used accidentally. + debug_only(_out[_outcnt] = (Node *)(uintptr_t)0xdeadbeef); + } + +#ifdef ASSERT + bool is_dead() const; +#define is_not_dead(n) ((n) == NULL || !VerifyIterativeGVN || !((n)->is_dead())) +#endif + + // Set a required input edge, also updates corresponding output edge + void add_req( Node *n ); // Append a NEW required input + void add_req_batch( Node* n, uint m ); // Append m NEW required inputs (all n). + void del_req( uint idx ); // Delete required edge & compact + void ins_req( uint i, Node *n ); // Insert a NEW required input + void set_req( uint i, Node *n ) { + assert( is_not_dead(n), "can not use dead node"); + assert( i < _cnt, "oob"); + assert( !VerifyHashTableKeys || _hash_lock == 0, + "remove node from hash table before modifying it"); + Node** p = &_in[i]; // cache this._in, across the del_out call + if (*p != NULL) (*p)->del_out((Node *)this); + (*p) = n; + if (n != NULL) n->add_out((Node *)this); + } + // Light version of set_req() to init inputs after node creation. + void init_req( uint i, Node *n ) { + assert( i == 0 && this == n || + is_not_dead(n), "can not use dead node"); + assert( i < _cnt, "oob"); + assert( !VerifyHashTableKeys || _hash_lock == 0, + "remove node from hash table before modifying it"); + assert( _in[i] == NULL, "sanity"); + _in[i] = n; + if (n != NULL) n->add_out((Node *)this); + } + // Find first occurrence of n among my edges: + int find_edge(Node* n); + int replace_edge(Node* old, Node* neww); + // NULL out all inputs to eliminate incoming Def-Use edges. + // Return the number of edges between 'n' and 'this' + int disconnect_inputs(Node *n); + + // Quickly, return true if and only if I am Compile::current()->top(). + bool is_top() const { + assert((this == (Node*) Compile::current()->top()) == (_out == NULL), ""); + return (_out == NULL); + } + // Reaffirm invariants for is_top. (Only from Compile::set_cached_top_node.) + void setup_is_top(); + + // Strip away casting. (It is depth-limited.) + Node* uncast() const; + +private: + static Node* uncast_helper(const Node* n); + + // Add an output edge to the end of the list + void add_out( Node *n ) { + if (is_top()) return; + if( _outcnt == _outmax ) out_grow(_outcnt); + _out[_outcnt++] = n; + } + // Delete an output edge + void del_out( Node *n ) { + if (is_top()) return; + Node** outp = &_out[_outcnt]; + // Find and remove n + do { + assert(outp > _out, "Missing Def-Use edge"); + } while (*--outp != n); + *outp = _out[--_outcnt]; + // Smash the old edge so it can't be used accidentally. + debug_only(_out[_outcnt] = (Node *)(uintptr_t)0xdeadbeef); + // Record that a change happened here. + #if OPTO_DU_ITERATOR_ASSERT + debug_only(_last_del = n; ++_del_tick); + #endif + } + +public: + // Globally replace this node by a given new node, updating all uses. + void replace_by(Node* new_node); + void set_req_X( uint i, Node *n, PhaseIterGVN *igvn ); + // Find the one non-null required input. RegionNode only + Node *nonnull_req() const; + // Add or remove precedence edges + void add_prec( Node *n ); + void rm_prec( uint i ); + void set_prec( uint i, Node *n ) { + assert( is_not_dead(n), "can not use dead node"); + assert( i >= _cnt, "not a precedence edge"); + if (_in[i] != NULL) _in[i]->del_out((Node *)this); + _in[i] = n; + if (n != NULL) n->add_out((Node *)this); + } + // Set this node's index, used by cisc_version to replace current node + void set_idx(uint new_idx) { + const node_idx_t* ref = &_idx; + *(node_idx_t*)ref = new_idx; + } + // Swap input edge order. (Edge indexes i1 and i2 are usually 1 and 2.) + void swap_edges(uint i1, uint i2) { + debug_only(uint check_hash = (VerifyHashTableKeys && _hash_lock) ? hash() : NO_HASH); + // Def-Use info is unchanged + Node* n1 = in(i1); + Node* n2 = in(i2); + _in[i1] = n2; + _in[i2] = n1; + // If this node is in the hash table, make sure it doesn't need a rehash. + assert(check_hash == NO_HASH || check_hash == hash(), "edge swap must preserve hash code"); + } + + // Iterators over input Nodes for a Node X are written as: + // for( i = 0; i < X.req(); i++ ) ... X[i] ... + // NOTE: Required edges can contain embedded NULL pointers. + +//----------------- Other Node Properties + + // Generate class id for some ideal nodes to avoid virtual query + // methods is_(). + // Class id is the set of bits corresponded to the node class and all its + // super classes so that queries for super classes are also valid. + // Subclasses of the same super class have different assigned bit + // (the third parameter in the macro DEFINE_CLASS_ID). + // Classes with deeper hierarchy are declared first. + // Classes with the same hierarchy depth are sorted by usage frequency. + // + // The query method masks the bits to cut off bits of subclasses + // and then compare the result with the class id + // (see the macro DEFINE_CLASS_QUERY below). + // + // Class_MachCall=30, ClassMask_MachCall=31 + // 12 8 4 0 + // 0 0 0 0 0 0 0 0 1 1 1 1 0 + // | | | | + // | | | Bit_Mach=2 + // | | Bit_MachReturn=4 + // | Bit_MachSafePoint=8 + // Bit_MachCall=16 + // + // Class_CountedLoop=56, ClassMask_CountedLoop=63 + // 12 8 4 0 + // 0 0 0 0 0 0 0 1 1 1 0 0 0 + // | | | + // | | Bit_Region=8 + // | Bit_Loop=16 + // Bit_CountedLoop=32 + + #define DEFINE_CLASS_ID(cl, supcl, subn) \ + Bit_##cl = (Class_##supcl == 0) ? 1 << subn : (Bit_##supcl) << (1 + subn) , \ + Class_##cl = Class_##supcl + Bit_##cl , \ + ClassMask_##cl = ((Bit_##cl << 1) - 1) , + + // This enum is used only for C2 ideal and mach nodes with is_() methods + // so that it's values fits into 16 bits. + enum NodeClasses { + Bit_Node = 0x0000, + Class_Node = 0x0000, + ClassMask_Node = 0xFFFF, + + DEFINE_CLASS_ID(Multi, Node, 0) + DEFINE_CLASS_ID(SafePoint, Multi, 0) + DEFINE_CLASS_ID(Call, SafePoint, 0) + DEFINE_CLASS_ID(CallJava, Call, 0) + DEFINE_CLASS_ID(CallStaticJava, CallJava, 0) + DEFINE_CLASS_ID(CallDynamicJava, CallJava, 1) + DEFINE_CLASS_ID(CallRuntime, Call, 1) + DEFINE_CLASS_ID(CallLeaf, CallRuntime, 0) + DEFINE_CLASS_ID(Allocate, Call, 2) + DEFINE_CLASS_ID(AllocateArray, Allocate, 0) + DEFINE_CLASS_ID(AbstractLock, Call, 3) + DEFINE_CLASS_ID(Lock, AbstractLock, 0) + DEFINE_CLASS_ID(Unlock, AbstractLock, 1) + DEFINE_CLASS_ID(MultiBranch, Multi, 1) + DEFINE_CLASS_ID(PCTable, MultiBranch, 0) + DEFINE_CLASS_ID(Catch, PCTable, 0) + DEFINE_CLASS_ID(Jump, PCTable, 1) + DEFINE_CLASS_ID(If, MultiBranch, 1) + DEFINE_CLASS_ID(CountedLoopEnd, If, 0) + DEFINE_CLASS_ID(NeverBranch, MultiBranch, 2) + DEFINE_CLASS_ID(Start, Multi, 2) + DEFINE_CLASS_ID(MemBar, Multi, 3) + DEFINE_CLASS_ID(Initialize, MemBar, 0) + + DEFINE_CLASS_ID(Mach, Node, 1) + DEFINE_CLASS_ID(MachReturn, Mach, 0) + DEFINE_CLASS_ID(MachSafePoint, MachReturn, 0) + DEFINE_CLASS_ID(MachCall, MachSafePoint, 0) + DEFINE_CLASS_ID(MachCallJava, MachCall, 0) + DEFINE_CLASS_ID(MachCallStaticJava, MachCallJava, 0) + DEFINE_CLASS_ID(MachCallDynamicJava, MachCallJava, 1) + DEFINE_CLASS_ID(MachCallRuntime, MachCall, 1) + DEFINE_CLASS_ID(MachCallLeaf, MachCallRuntime, 0) + DEFINE_CLASS_ID(MachSpillCopy, Mach, 1) + DEFINE_CLASS_ID(MachNullCheck, Mach, 2) + DEFINE_CLASS_ID(MachIf, Mach, 3) + DEFINE_CLASS_ID(MachTemp, Mach, 4) + + DEFINE_CLASS_ID(Proj, Node, 2) + DEFINE_CLASS_ID(CatchProj, Proj, 0) + DEFINE_CLASS_ID(JumpProj, Proj, 1) + DEFINE_CLASS_ID(IfTrue, Proj, 2) + DEFINE_CLASS_ID(IfFalse, Proj, 3) + + DEFINE_CLASS_ID(Region, Node, 3) + DEFINE_CLASS_ID(Loop, Region, 0) + DEFINE_CLASS_ID(Root, Loop, 0) + DEFINE_CLASS_ID(CountedLoop, Loop, 1) + + DEFINE_CLASS_ID(Sub, Node, 4) + DEFINE_CLASS_ID(Cmp, Sub, 0) + DEFINE_CLASS_ID(FastLock, Cmp, 0) + DEFINE_CLASS_ID(FastUnlock, Cmp, 1) + + DEFINE_CLASS_ID(Type, Node, 5) + DEFINE_CLASS_ID(Phi, Type, 0) + DEFINE_CLASS_ID(ConstraintCast, Type, 1) + DEFINE_CLASS_ID(CheckCastPP, Type, 2) + DEFINE_CLASS_ID(CMove, Type, 3) + + DEFINE_CLASS_ID(Mem, Node, 6) + DEFINE_CLASS_ID(Load, Mem, 0) + DEFINE_CLASS_ID(Store, Mem, 1) + DEFINE_CLASS_ID(LoadStore, Mem, 2) + + DEFINE_CLASS_ID(MergeMem, Node, 7) + DEFINE_CLASS_ID(Bool, Node, 8) + DEFINE_CLASS_ID(AddP, Node, 9) + DEFINE_CLASS_ID(BoxLock, Node, 10) + DEFINE_CLASS_ID(Add, Node, 11) + DEFINE_CLASS_ID(Mul, Node, 12) + + _max_classes = ClassMask_Mul + }; + #undef DEFINE_CLASS_ID + + // Flags are sorted by usage frequency. + enum NodeFlags { + Flag_is_Copy = 0x01, // should be first bit to avoid shift + Flag_is_Call = Flag_is_Copy << 1, + Flag_rematerialize = Flag_is_Call << 1, + Flag_needs_anti_dependence_check = Flag_rematerialize << 1, + Flag_is_macro = Flag_needs_anti_dependence_check << 1, + Flag_is_Con = Flag_is_macro << 1, + Flag_is_cisc_alternate = Flag_is_Con << 1, + Flag_is_Branch = Flag_is_cisc_alternate << 1, + Flag_is_block_start = Flag_is_Branch << 1, + Flag_is_Goto = Flag_is_block_start << 1, + Flag_is_dead_loop_safe = Flag_is_Goto << 1, + Flag_may_be_short_branch = Flag_is_dead_loop_safe << 1, + Flag_is_safepoint_node = Flag_may_be_short_branch << 1, + Flag_is_pc_relative = Flag_is_safepoint_node << 1, + Flag_is_Vector = Flag_is_pc_relative << 1, + _max_flags = (Flag_is_Vector << 1) - 1 // allow flags combination + }; + +private: + jushort _class_id; + jushort _flags; + +protected: + // These methods should be called from constructors only. + void init_class_id(jushort c) { + assert(c <= _max_classes, "invalid node class"); + _class_id = c; // cast out const + } + void init_flags(jushort fl) { + assert(fl <= _max_flags, "invalid node flag"); + _flags |= fl; + } + void clear_flag(jushort fl) { + assert(fl <= _max_flags, "invalid node flag"); + _flags &= ~fl; + } + +public: + const jushort class_id() const { return _class_id; } + + const jushort flags() const { return _flags; } + + // Return a dense integer opcode number + virtual int Opcode() const; + + // Virtual inherited Node size + virtual uint size_of() const; + + // Other interesting Node properties + + // Special case: is_Call() returns true for both CallNode and MachCallNode. + bool is_Call() const { + return (_flags & Flag_is_Call) != 0; + } + + CallNode *as_Call() const { // Only for CallNode (not for MachCallNode) + assert((_class_id & ClassMask_Call) == Class_Call, "invalid node class"); + return (CallNode*)this; + } + + #define DEFINE_CLASS_QUERY(type) \ + bool is_##type() const { \ + return ((_class_id & ClassMask_##type) == Class_##type); \ + } \ + type##Node *as_##type() const { \ + assert(is_##type(), "invalid node class"); \ + return (type##Node*)this; \ + } + + DEFINE_CLASS_QUERY(AbstractLock) + DEFINE_CLASS_QUERY(Add) + DEFINE_CLASS_QUERY(AddP) + DEFINE_CLASS_QUERY(Allocate) + DEFINE_CLASS_QUERY(AllocateArray) + DEFINE_CLASS_QUERY(Bool) + DEFINE_CLASS_QUERY(BoxLock) + DEFINE_CLASS_QUERY(CallDynamicJava) + DEFINE_CLASS_QUERY(CallJava) + DEFINE_CLASS_QUERY(CallLeaf) + DEFINE_CLASS_QUERY(CallRuntime) + DEFINE_CLASS_QUERY(CallStaticJava) + DEFINE_CLASS_QUERY(Catch) + DEFINE_CLASS_QUERY(CatchProj) + DEFINE_CLASS_QUERY(CheckCastPP) + DEFINE_CLASS_QUERY(ConstraintCast) + DEFINE_CLASS_QUERY(CMove) + DEFINE_CLASS_QUERY(Cmp) + DEFINE_CLASS_QUERY(CountedLoop) + DEFINE_CLASS_QUERY(CountedLoopEnd) + DEFINE_CLASS_QUERY(FastLock) + DEFINE_CLASS_QUERY(FastUnlock) + DEFINE_CLASS_QUERY(If) + DEFINE_CLASS_QUERY(IfFalse) + DEFINE_CLASS_QUERY(IfTrue) + DEFINE_CLASS_QUERY(Initialize) + DEFINE_CLASS_QUERY(Jump) + DEFINE_CLASS_QUERY(JumpProj) + DEFINE_CLASS_QUERY(Load) + DEFINE_CLASS_QUERY(LoadStore) + DEFINE_CLASS_QUERY(Lock) + DEFINE_CLASS_QUERY(Loop) + DEFINE_CLASS_QUERY(Mach) + DEFINE_CLASS_QUERY(MachCall) + DEFINE_CLASS_QUERY(MachCallDynamicJava) + DEFINE_CLASS_QUERY(MachCallJava) + DEFINE_CLASS_QUERY(MachCallLeaf) + DEFINE_CLASS_QUERY(MachCallRuntime) + DEFINE_CLASS_QUERY(MachCallStaticJava) + DEFINE_CLASS_QUERY(MachIf) + DEFINE_CLASS_QUERY(MachNullCheck) + DEFINE_CLASS_QUERY(MachReturn) + DEFINE_CLASS_QUERY(MachSafePoint) + DEFINE_CLASS_QUERY(MachSpillCopy) + DEFINE_CLASS_QUERY(MachTemp) + DEFINE_CLASS_QUERY(Mem) + DEFINE_CLASS_QUERY(MemBar) + DEFINE_CLASS_QUERY(MergeMem) + DEFINE_CLASS_QUERY(Mul) + DEFINE_CLASS_QUERY(Multi) + DEFINE_CLASS_QUERY(MultiBranch) + DEFINE_CLASS_QUERY(PCTable) + DEFINE_CLASS_QUERY(Phi) + DEFINE_CLASS_QUERY(Proj) + DEFINE_CLASS_QUERY(Region) + DEFINE_CLASS_QUERY(Root) + DEFINE_CLASS_QUERY(SafePoint) + DEFINE_CLASS_QUERY(Start) + DEFINE_CLASS_QUERY(Store) + DEFINE_CLASS_QUERY(Sub) + DEFINE_CLASS_QUERY(Type) + DEFINE_CLASS_QUERY(Unlock) + + #undef DEFINE_CLASS_QUERY + + // duplicate of is_MachSpillCopy() + bool is_SpillCopy () const { + return ((_class_id & ClassMask_MachSpillCopy) == Class_MachSpillCopy); + } + + bool is_Con () const { return (_flags & Flag_is_Con) != 0; } + bool is_Goto() const { return (_flags & Flag_is_Goto) != 0; } + // The data node which is safe to leave in dead loop during IGVN optimization. + bool is_dead_loop_safe() const { + return is_Phi() || is_Proj() || + (_flags & (Flag_is_dead_loop_safe | Flag_is_Con)) != 0; + } + + // is_Copy() returns copied edge index (0 or 1) + uint is_Copy() const { return (_flags & Flag_is_Copy); } + + virtual bool is_CFG() const { return false; } + + // If this node is control-dependent on a test, can it be + // rerouted to a dominating equivalent test? This is usually + // true of non-CFG nodes, but can be false for operations which + // depend for their correct sequencing on more than one test. + // (In that case, hoisting to a dominating test may silently + // skip some other important test.) + virtual bool depends_only_on_test() const { assert(!is_CFG(), ""); return true; }; + + // defined for MachNodes that match 'If' | 'Goto' | 'CountedLoopEnd' + bool is_Branch() const { return (_flags & Flag_is_Branch) != 0; } + + // When building basic blocks, I need to have a notion of block beginning + // Nodes, next block selector Nodes (block enders), and next block + // projections. These calls need to work on their machine equivalents. The + // Ideal beginning Nodes are RootNode, RegionNode and StartNode. + bool is_block_start() const { + if ( is_Region() ) + return this == (const Node*)in(0); + else + return (_flags & Flag_is_block_start) != 0; + } + + // The Ideal control projection Nodes are IfTrue/IfFalse, JumpProjNode, Root, + // Goto and Return. This call also returns the block ending Node. + virtual const Node *is_block_proj() const; + + // The node is a "macro" node which needs to be expanded before matching + bool is_macro() const { return (_flags & Flag_is_macro) != 0; } + + // Value is a vector of primitive values + bool is_Vector() const { return (_flags & Flag_is_Vector) != 0; } + +//----------------- Optimization + + // Get the worst-case Type output for this Node. + virtual const class Type *bottom_type() const; + + // If we find a better type for a node, try to record it permanently. + // Return true if this node actually changed. + // Be sure to do the hash_delete game in the "rehash" variant. + void raise_bottom_type(const Type* new_type); + + // Get the address type with which this node uses and/or defs memory, + // or NULL if none. The address type is conservatively wide. + // Returns non-null for calls, membars, loads, stores, etc. + // Returns TypePtr::BOTTOM if the node touches memory "broadly". + virtual const class TypePtr *adr_type() const { return NULL; } + + // Return an existing node which computes the same function as this node. + // The optimistic combined algorithm requires this to return a Node which + // is a small number of steps away (e.g., one of my inputs). + virtual Node *Identity( PhaseTransform *phase ); + + // Return the set of values this Node can take on at runtime. + virtual const Type *Value( PhaseTransform *phase ) const; + + // Return a node which is more "ideal" than the current node. + // The invariants on this call are subtle. If in doubt, read the + // treatise in node.cpp above the default implemention AND TEST WITH + // +VerifyIterativeGVN! + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + + // Some nodes have specific Ideal subgraph transformations only if they are + // unique users of specific nodes. Such nodes should be put on IGVN worklist + // for the transformations to happen. + bool has_special_unique_user() const; + +protected: + bool remove_dead_region(PhaseGVN *phase, bool can_reshape); +public: + + // Idealize graph, using DU info. Done after constant propagation + virtual Node *Ideal_DU_postCCP( PhaseCCP *ccp ); + + // See if there is valid pipeline info + static const Pipeline *pipeline_class(); + virtual const Pipeline *pipeline() const; + + // Compute the latency from the def to this instruction of the ith input node + uint latency(uint i); + + // Hash & compare functions, for pessimistic value numbering + + // If the hash function returns the special sentinel value NO_HASH, + // the node is guaranteed never to compare equal to any other node. + // If we accidently generate a hash with value NO_HASH the node + // won't go into the table and we'll lose a little optimization. + enum { NO_HASH = 0 }; + virtual uint hash() const; + virtual uint cmp( const Node &n ) const; + + // Operation appears to be iteratively computed (such as an induction variable) + // It is possible for this operation to return false for a loop-varying + // value, if it appears (by local graph inspection) to be computed by a simple conditional. + bool is_iteratively_computed(); + + // Determine if a node is Counted loop induction variable. + // The method is defined in loopnode.cpp. + const Node* is_loop_iv() const; + + // Return a node with opcode "opc" and same inputs as "this" if one can + // be found; Otherwise return NULL; + Node* find_similar(int opc); + + // Return the unique control out if only one. Null if none or more than one. + Node* unique_ctrl_out(); + +//----------------- Code Generation + + // Ideal register class for Matching. Zero means unmatched instruction + // (these are cloned instead of converted to machine nodes). + virtual uint ideal_reg() const; + + static const uint NotAMachineReg; // must be > max. machine register + + // Do we Match on this edge index or not? Generally false for Control + // and true for everything else. Weird for calls & returns. + virtual uint match_edge(uint idx) const; + + // Register class output is returned in + virtual const RegMask &out_RegMask() const; + // Register class input is expected in + virtual const RegMask &in_RegMask(uint) const; + // Should we clone rather than spill this instruction? + bool rematerialize() const; + + // Return JVM State Object if this Node carries debug info, or NULL otherwise + virtual JVMState* jvms() const; + + // Print as assembly + virtual void format( PhaseRegAlloc *, outputStream* st = tty ) const; + // Emit bytes starting at parameter 'ptr' + // Bump 'ptr' by the number of output bytes + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + // Size of instruction in bytes + virtual uint size(PhaseRegAlloc *ra_) const; + + // Convenience function to extract an integer constant from a node. + // If it is not an integer constant (either Con, CastII, or Mach), + // return value_if_unknown. + jint find_int_con(jint value_if_unknown) const { + const TypeInt* t = find_int_type(); + return (t != NULL && t->is_con()) ? t->get_con() : value_if_unknown; + } + // Return the constant, knowing it is an integer constant already + jint get_int() const { + const TypeInt* t = find_int_type(); + guarantee(t != NULL, "must be con"); + return t->get_con(); + } + // Here's where the work is done. Can produce non-constant int types too. + const TypeInt* find_int_type() const; + + // Same thing for long (and intptr_t, via type.hpp): + jlong get_long() const { + const TypeLong* t = find_long_type(); + guarantee(t != NULL, "must be con"); + return t->get_con(); + } + jlong find_long_con(jint value_if_unknown) const { + const TypeLong* t = find_long_type(); + return (t != NULL && t->is_con()) ? t->get_con() : value_if_unknown; + } + const TypeLong* find_long_type() const; + + // These guys are called by code generated by ADLC: + intptr_t get_ptr() const; + jdouble getd() const; + jfloat getf() const; + + // Nodes which are pinned into basic blocks + virtual bool pinned() const { return false; } + + // Nodes which use memory without consuming it, hence need antidependences + // More specifically, needs_anti_dependence_check returns true iff the node + // (a) does a load, and (b) does not perform a store (except perhaps to a + // stack slot or some other unaliased location). + bool needs_anti_dependence_check() const; + + // Return which operand this instruction may cisc-spill. In other words, + // return operand position that can convert from reg to memory access + virtual int cisc_operand() const { return AdlcVMDeps::Not_cisc_spillable; } + bool is_cisc_alternate() const { return (_flags & Flag_is_cisc_alternate) != 0; } + +//----------------- Graph walking +public: + // Walk and apply member functions recursively. + // Supplied (this) pointer is root. + void walk(NFunc pre, NFunc post, void *env); + static void nop(Node &, void*); // Dummy empty function + static void packregion( Node &n, void* ); +private: + void walk_(NFunc pre, NFunc post, void *env, VectorSet &visited); + +//----------------- Printing, etc +public: +#ifndef PRODUCT + Node* find(int idx) const; // Search the graph for the given idx. + Node* find_ctrl(int idx) const; // Search control ancestors for the given idx. + void dump() const; // Print this node, + void dump(int depth) const; // Print this node, recursively to depth d + void dump_ctrl(int depth) const; // Print control nodes, to depth d + virtual void dump_req() const; // Print required-edge info + virtual void dump_prec() const; // Print precedence-edge info + virtual void dump_out() const; // Print the output edge info + virtual void dump_spec(outputStream *st) const {}; // Print per-node info + void verify_edges(Unique_Node_List &visited); // Verify bi-directional edges + void verify() const; // Check Def-Use info for my subgraph + static void verify_recur(const Node *n, int verify_depth, VectorSet &old_space, VectorSet &new_space); + + // This call defines a class-unique string used to identify class instances + virtual const char *Name() const; + + void dump_format(PhaseRegAlloc *ra) const; // debug access to MachNode::format(...) + // RegMask Print Functions + void dump_in_regmask(int idx) { in_RegMask(idx).dump(); } + void dump_out_regmask() { out_RegMask().dump(); } + static int _in_dump_cnt; + static bool in_dump() { return _in_dump_cnt > 0; } + void fast_dump() const { + tty->print("%4d: %-17s", _idx, Name()); + for (uint i = 0; i < len(); i++) + if (in(i)) + tty->print(" %4d", in(i)->_idx); + else + tty->print(" NULL"); + tty->print("\n"); + } +#endif +#ifdef ASSERT + void verify_construction(); + bool verify_jvms(const JVMState* jvms) const; + int _debug_idx; // Unique value assigned to every node. + int debug_idx() const { return _debug_idx; } + void set_debug_idx( int debug_idx ) { _debug_idx = debug_idx; } + + Node* _debug_orig; // Original version of this, if any. + Node* debug_orig() const { return _debug_orig; } + void set_debug_orig(Node* orig); // _debug_orig = orig + + int _hash_lock; // Barrier to modifications of nodes in the hash table + void enter_hash_lock() { ++_hash_lock; assert(_hash_lock < 99, "in too many hash tables?"); } + void exit_hash_lock() { --_hash_lock; assert(_hash_lock >= 0, "mispaired hash locks"); } + + static void init_NodeProperty(); + + #if OPTO_DU_ITERATOR_ASSERT + const Node* _last_del; // The last deleted node. + uint _del_tick; // Bumped when a deletion happens.. + #endif +#endif +}; + +//----------------------------------------------------------------------------- +// Iterators over DU info, and associated Node functions. + +#if OPTO_DU_ITERATOR_ASSERT + +// Common code for assertion checking on DU iterators. +class DUIterator_Common VALUE_OBJ_CLASS_SPEC { +#ifdef ASSERT + protected: + bool _vdui; // cached value of VerifyDUIterators + const Node* _node; // the node containing the _out array + uint _outcnt; // cached node->_outcnt + uint _del_tick; // cached node->_del_tick + Node* _last; // last value produced by the iterator + + void sample(const Node* node); // used by c'tor to set up for verifies + void verify(const Node* node, bool at_end_ok = false); + void verify_resync(); + void reset(const DUIterator_Common& that); + +// The VDUI_ONLY macro protects code conditionalized on VerifyDUIterators + #define I_VDUI_ONLY(i,x) { if ((i)._vdui) { x; } } +#else + #define I_VDUI_ONLY(i,x) { } +#endif //ASSERT +}; + +#define VDUI_ONLY(x) I_VDUI_ONLY(*this, x) + +// Default DU iterator. Allows appends onto the out array. +// Allows deletion from the out array only at the current point. +// Usage: +// for (DUIterator i = x->outs(); x->has_out(i); i++) { +// Node* y = x->out(i); +// ... +// } +// Compiles in product mode to a unsigned integer index, which indexes +// onto a repeatedly reloaded base pointer of x->_out. The loop predicate +// also reloads x->_outcnt. If you delete, you must perform "--i" just +// before continuing the loop. You must delete only the last-produced +// edge. You must delete only a single copy of the last-produced edge, +// or else you must delete all copies at once (the first time the edge +// is produced by the iterator). +class DUIterator : public DUIterator_Common { + friend class Node; + + // This is the index which provides the product-mode behavior. + // Whatever the product-mode version of the system does to the + // DUI index is done to this index. All other fields in + // this class are used only for assertion checking. + uint _idx; + + #ifdef ASSERT + uint _refresh_tick; // Records the refresh activity. + + void sample(const Node* node); // Initialize _refresh_tick etc. + void verify(const Node* node, bool at_end_ok = false); + void verify_increment(); // Verify an increment operation. + void verify_resync(); // Verify that we can back up over a deletion. + void verify_finish(); // Verify that the loop terminated properly. + void refresh(); // Resample verification info. + void reset(const DUIterator& that); // Resample after assignment. + #endif + + DUIterator(const Node* node, int dummy_to_avoid_conversion) + { _idx = 0; debug_only(sample(node)); } + + public: + // initialize to garbage; clear _vdui to disable asserts + DUIterator() + { /*initialize to garbage*/ debug_only(_vdui = false); } + + void operator++(int dummy_to_specify_postfix_op) + { _idx++; VDUI_ONLY(verify_increment()); } + + void operator--() + { VDUI_ONLY(verify_resync()); --_idx; } + + ~DUIterator() + { VDUI_ONLY(verify_finish()); } + + void operator=(const DUIterator& that) + { _idx = that._idx; debug_only(reset(that)); } +}; + +DUIterator Node::outs() const + { return DUIterator(this, 0); } +DUIterator& Node::refresh_out_pos(DUIterator& i) const + { I_VDUI_ONLY(i, i.refresh()); return i; } +bool Node::has_out(DUIterator& i) const + { I_VDUI_ONLY(i, i.verify(this,true));return i._idx < _outcnt; } +Node* Node::out(DUIterator& i) const + { I_VDUI_ONLY(i, i.verify(this)); return debug_only(i._last=) _out[i._idx]; } + + +// Faster DU iterator. Disallows insertions into the out array. +// Allows deletion from the out array only at the current point. +// Usage: +// for (DUIterator_Fast imax, i = x->fast_outs(imax); i < imax; i++) { +// Node* y = x->fast_out(i); +// ... +// } +// Compiles in product mode to raw Node** pointer arithmetic, with +// no reloading of pointers from the original node x. If you delete, +// you must perform "--i; --imax" just before continuing the loop. +// If you delete multiple copies of the same edge, you must decrement +// imax, but not i, multiple times: "--i, imax -= num_edges". +class DUIterator_Fast : public DUIterator_Common { + friend class Node; + friend class DUIterator_Last; + + // This is the pointer which provides the product-mode behavior. + // Whatever the product-mode version of the system does to the + // DUI pointer is done to this pointer. All other fields in + // this class are used only for assertion checking. + Node** _outp; + + #ifdef ASSERT + void verify(const Node* node, bool at_end_ok = false); + void verify_limit(); + void verify_resync(); + void verify_relimit(uint n); + void reset(const DUIterator_Fast& that); + #endif + + // Note: offset must be signed, since -1 is sometimes passed + DUIterator_Fast(const Node* node, ptrdiff_t offset) + { _outp = node->_out + offset; debug_only(sample(node)); } + + public: + // initialize to garbage; clear _vdui to disable asserts + DUIterator_Fast() + { /*initialize to garbage*/ debug_only(_vdui = false); } + + void operator++(int dummy_to_specify_postfix_op) + { _outp++; VDUI_ONLY(verify(_node, true)); } + + void operator--() + { VDUI_ONLY(verify_resync()); --_outp; } + + void operator-=(uint n) // applied to the limit only + { _outp -= n; VDUI_ONLY(verify_relimit(n)); } + + bool operator<(DUIterator_Fast& limit) { + I_VDUI_ONLY(*this, this->verify(_node, true)); + I_VDUI_ONLY(limit, limit.verify_limit()); + return _outp < limit._outp; + } + + void operator=(const DUIterator_Fast& that) + { _outp = that._outp; debug_only(reset(that)); } +}; + +DUIterator_Fast Node::fast_outs(DUIterator_Fast& imax) const { + // Assign a limit pointer to the reference argument: + imax = DUIterator_Fast(this, (ptrdiff_t)_outcnt); + // Return the base pointer: + return DUIterator_Fast(this, 0); +} +Node* Node::fast_out(DUIterator_Fast& i) const { + I_VDUI_ONLY(i, i.verify(this)); + return debug_only(i._last=) *i._outp; +} + + +// Faster DU iterator. Requires each successive edge to be removed. +// Does not allow insertion of any edges. +// Usage: +// for (DUIterator_Last imin, i = x->last_outs(imin); i >= imin; i -= num_edges) { +// Node* y = x->last_out(i); +// ... +// } +// Compiles in product mode to raw Node** pointer arithmetic, with +// no reloading of pointers from the original node x. +class DUIterator_Last : private DUIterator_Fast { + friend class Node; + + #ifdef ASSERT + void verify(const Node* node, bool at_end_ok = false); + void verify_limit(); + void verify_step(uint num_edges); + #endif + + // Note: offset must be signed, since -1 is sometimes passed + DUIterator_Last(const Node* node, ptrdiff_t offset) + : DUIterator_Fast(node, offset) { } + + void operator++(int dummy_to_specify_postfix_op) {} // do not use + void operator<(int) {} // do not use + + public: + DUIterator_Last() { } + // initialize to garbage + + void operator--() + { _outp--; VDUI_ONLY(verify_step(1)); } + + void operator-=(uint n) + { _outp -= n; VDUI_ONLY(verify_step(n)); } + + bool operator>=(DUIterator_Last& limit) { + I_VDUI_ONLY(*this, this->verify(_node, true)); + I_VDUI_ONLY(limit, limit.verify_limit()); + return _outp >= limit._outp; + } + + void operator=(const DUIterator_Last& that) + { DUIterator_Fast::operator=(that); } +}; + +DUIterator_Last Node::last_outs(DUIterator_Last& imin) const { + // Assign a limit pointer to the reference argument: + imin = DUIterator_Last(this, 0); + // Return the initial pointer: + return DUIterator_Last(this, (ptrdiff_t)_outcnt - 1); +} +Node* Node::last_out(DUIterator_Last& i) const { + I_VDUI_ONLY(i, i.verify(this)); + return debug_only(i._last=) *i._outp; +} + +#endif //OPTO_DU_ITERATOR_ASSERT + +#undef I_VDUI_ONLY +#undef VDUI_ONLY + + +//----------------------------------------------------------------------------- +// Map dense integer indices to Nodes. Uses classic doubling-array trick. +// Abstractly provides an infinite array of Node*'s, initialized to NULL. +// Note that the constructor just zeros things, and since I use Arena +// allocation I do not need a destructor to reclaim storage. +class Node_Array : public ResourceObj { +protected: + Arena *_a; // Arena to allocate in + uint _max; + Node **_nodes; + void grow( uint i ); // Grow array node to fit +public: + Node_Array(Arena *a) : _a(a), _max(OptoNodeListSize) { + _nodes = NEW_ARENA_ARRAY( a, Node *, OptoNodeListSize ); + for( int i = 0; i < OptoNodeListSize; i++ ) { + _nodes[i] = NULL; + } + } + + Node_Array(Node_Array *na) : _a(na->_a), _max(na->_max), _nodes(na->_nodes) {} + Node *operator[] ( uint i ) const // Lookup, or NULL for not mapped + { return (i<_max) ? _nodes[i] : (Node*)NULL; } + Node *at( uint i ) const { assert(i<_max,"oob"); return _nodes[i]; } + Node **adr() { return _nodes; } + // Extend the mapping: index i maps to Node *n. + void map( uint i, Node *n ) { if( i>=_max ) grow(i); _nodes[i] = n; } + void insert( uint i, Node *n ); + void remove( uint i ); // Remove, preserving order + void sort( C_sort_func_t func); + void reset( Arena *new_a ); // Zap mapping to empty; reclaim storage + void clear(); // Set all entries to NULL, keep storage + uint Size() const { return _max; } + void dump() const; +}; + +class Node_List : public Node_Array { + uint _cnt; +public: + Node_List() : Node_Array(Thread::current()->resource_area()), _cnt(0) {} + Node_List(Arena *a) : Node_Array(a), _cnt(0) {} + void insert( uint i, Node *n ) { Node_Array::insert(i,n); _cnt++; } + void remove( uint i ) { Node_Array::remove(i); _cnt--; } + void push( Node *b ) { map(_cnt++,b); } + void yank( Node *n ); // Find and remove + Node *pop() { return _nodes[--_cnt]; } + Node *rpop() { Node *b = _nodes[0]; _nodes[0]=_nodes[--_cnt]; return b;} + void clear() { _cnt = 0; Node_Array::clear(); } // retain storage + uint size() const { return _cnt; } + void dump() const; +}; + +//------------------------------Unique_Node_List------------------------------- +class Unique_Node_List : public Node_List { + VectorSet _in_worklist; + uint _clock_index; // Index in list where to pop from next +public: + Unique_Node_List() : Node_List(), _in_worklist(Thread::current()->resource_area()), _clock_index(0) {} + Unique_Node_List(Arena *a) : Node_List(a), _in_worklist(a), _clock_index(0) {} + + void remove( Node *n ); + bool member( Node *n ) { return _in_worklist.test(n->_idx) != 0; } + VectorSet &member_set(){ return _in_worklist; } + + void push( Node *b ) { + if( !_in_worklist.test_set(b->_idx) ) + Node_List::push(b); + } + Node *pop() { + if( _clock_index >= size() ) _clock_index = 0; + Node *b = at(_clock_index); + map( _clock_index++, Node_List::pop()); + _in_worklist >>= b->_idx; + return b; + } + Node *remove( uint i ) { + Node *b = Node_List::at(i); + _in_worklist >>= b->_idx; + map(i,Node_List::pop()); + return b; + } + void yank( Node *n ) { _in_worklist >>= n->_idx; Node_List::yank(n); } + void clear() { + _in_worklist.Clear(); // Discards storage but grows automatically + Node_List::clear(); + _clock_index = 0; + } + + // Used after parsing to remove useless nodes before Iterative GVN + void remove_useless_nodes(VectorSet &useful); + +#ifndef PRODUCT + void print_set() const { _in_worklist.print(); } +#endif +}; + +// Inline definition of Compile::record_for_igvn must be deferred to this point. +inline void Compile::record_for_igvn(Node* n) { + _for_igvn->push(n); + record_for_escape_analysis(n); +} + +//------------------------------Node_Stack------------------------------------- +class Node_Stack { +protected: + struct INode { + Node *node; // Processed node + uint indx; // Index of next node's child + }; + INode *_inode_top; // tos, stack grows up + INode *_inode_max; // End of _inodes == _inodes + _max + INode *_inodes; // Array storage for the stack + Arena *_a; // Arena to allocate in + void grow(); +public: + Node_Stack(int size) { + size_t max = (size > OptoNodeListSize) ? size : OptoNodeListSize; + _a = Thread::current()->resource_area(); + _inodes = NEW_ARENA_ARRAY( _a, INode, max ); + _inode_max = _inodes + max; + _inode_top = _inodes - 1; // stack is empty + } + + Node_Stack(Arena *a, int size) : _a(a) { + size_t max = (size > OptoNodeListSize) ? size : OptoNodeListSize; + _inodes = NEW_ARENA_ARRAY( _a, INode, max ); + _inode_max = _inodes + max; + _inode_top = _inodes - 1; // stack is empty + } + + void pop() { + assert(_inode_top >= _inodes, "node stack underflow"); + --_inode_top; + } + void push(Node *n, uint i) { + ++_inode_top; + if (_inode_top >= _inode_max) grow(); + INode *top = _inode_top; // optimization + top->node = n; + top->indx = i; + } + Node *node() const { + return _inode_top->node; + } + Node* node_at(uint i) const { + assert(_inodes + i <= _inode_top, "in range"); + return _inodes[i].node; + } + uint index() const { + return _inode_top->indx; + } + void set_node(Node *n) { + _inode_top->node = n; + } + void set_index(uint i) { + _inode_top->indx = i; + } + uint size_max() const { return (uint)pointer_delta(_inode_max, _inodes, sizeof(INode)); } // Max size + uint size() const { return (uint)pointer_delta(_inode_top, _inodes, sizeof(INode)) + 1; } // Current size + bool is_nonempty() const { return (_inode_top >= _inodes); } + bool is_empty() const { return (_inode_top < _inodes); } + void clear() { _inode_top = _inodes - 1; } // retain storage +}; + + +//-----------------------------Node_Notes-------------------------------------- +// Debugging or profiling annotations loosely and sparsely associated +// with some nodes. See Compile::node_notes_at for the accessor. +class Node_Notes VALUE_OBJ_CLASS_SPEC { + JVMState* _jvms; + +public: + Node_Notes(JVMState* jvms = NULL) { + _jvms = jvms; + } + + JVMState* jvms() { return _jvms; } + void set_jvms(JVMState* x) { _jvms = x; } + + // True if there is nothing here. + bool is_clear() { + return (_jvms == NULL); + } + + // Make there be nothing here. + void clear() { + _jvms = NULL; + } + + // Make a new, clean node notes. + static Node_Notes* make(Compile* C) { + Node_Notes* nn = NEW_ARENA_ARRAY(C->comp_arena(), Node_Notes, 1); + nn->clear(); + return nn; + } + + Node_Notes* clone(Compile* C) { + Node_Notes* nn = NEW_ARENA_ARRAY(C->comp_arena(), Node_Notes, 1); + (*nn) = (*this); + return nn; + } + + // Absorb any information from source. + bool update_from(Node_Notes* source) { + bool changed = false; + if (source != NULL) { + if (source->jvms() != NULL) { + set_jvms(source->jvms()); + changed = true; + } + } + return changed; + } +}; + +// Inlined accessors for Compile::node_nodes that require the preceding class: +inline Node_Notes* +Compile::locate_node_notes(GrowableArray* arr, + int idx, bool can_grow) { + assert(idx >= 0, "oob"); + int block_idx = (idx >> _log2_node_notes_block_size); + int grow_by = (block_idx - (arr == NULL? 0: arr->length())); + if (grow_by >= 0) { + if (!can_grow) return NULL; + grow_node_notes(arr, grow_by + 1); + } + // (Every element of arr is a sub-array of length _node_notes_block_size.) + return arr->at(block_idx) + (idx & (_node_notes_block_size-1)); +} + +inline bool +Compile::set_node_notes_at(int idx, Node_Notes* value) { + if (value == NULL || value->is_clear()) + return false; // nothing to write => write nothing + Node_Notes* loc = locate_node_notes(_node_note_array, idx, true); + assert(loc != NULL, ""); + return loc->update_from(value); +} + + +//------------------------------TypeNode--------------------------------------- +// Node with a Type constant. +class TypeNode : public Node { +protected: + virtual uint hash() const; // Check the type + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; // Size is bigger + const Type* const _type; +public: + void set_type(const Type* t) { + assert(t != NULL, "sanity"); + debug_only(uint check_hash = (VerifyHashTableKeys && _hash_lock) ? hash() : NO_HASH); + *(const Type**)&_type = t; // cast away const-ness + // If this node is in the hash table, make sure it doesn't need a rehash. + assert(check_hash == NO_HASH || check_hash == hash(), "type change must preserve hash code"); + } + const Type* type() const { assert(_type != NULL, "sanity"); return _type; }; + TypeNode( const Type *t, uint required ) : Node(required), _type(t) { + init_class_id(Class_Type); + } + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const; + virtual uint ideal_reg() const; +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; diff --git a/hotspot/src/share/vm/opto/opcodes.cpp b/hotspot/src/share/vm/opto/opcodes.cpp new file mode 100644 index 00000000000..533cff06c79 --- /dev/null +++ b/hotspot/src/share/vm/opto/opcodes.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ---------------------------------------------------------------------------- +// Build a table of class names as strings. Used both for debugging printouts +// and in the ADL machine descriptions. +#define macro(x) #x, +const char *NodeClassNames[] = { + "Node", + "Set", + "RegI", + "RegP", + "RegF", + "RegD", + "RegL", + "RegFlags", + "_last_machine_leaf", +#include "classes.hpp" + "_last_class_name", +}; +#undef macro diff --git a/hotspot/src/share/vm/opto/opcodes.hpp b/hotspot/src/share/vm/opto/opcodes.hpp new file mode 100644 index 00000000000..7c3e38a15d6 --- /dev/null +++ b/hotspot/src/share/vm/opto/opcodes.hpp @@ -0,0 +1,43 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Build a big enum of class names to give them dense integer indices +#define macro(x) Op_##x, +enum Opcodes { + Op_Node = 0, + macro(Set) // Instruction selection match rule + macro(RegI) // Machine integer register + macro(RegP) // Machine pointer register + macro(RegF) // Machine float register + macro(RegD) // Machine double register + macro(RegL) // Machine long register + macro(RegFlags) // Machine flags register + _last_machine_leaf, // Split between regular opcodes and machine +#include "classes.hpp" + _last_opcode +}; +#undef macro + +// Table of names, indexed by Opcode +extern const char *NodeClassNames[]; diff --git a/hotspot/src/share/vm/opto/optoreg.hpp b/hotspot/src/share/vm/opto/optoreg.hpp new file mode 100644 index 00000000000..68a2df2cd46 --- /dev/null +++ b/hotspot/src/share/vm/opto/optoreg.hpp @@ -0,0 +1,194 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//------------------------------OptoReg---------------------------------------- +// We eventually need Registers for the Real World. Registers are essentially +// non-SSA names. A Register is represented as a number. Non-regular values +// (e.g., Control, Memory, I/O) use the Special register. The actual machine +// registers (as described in the ADL file for a machine) start at zero. +// Stack-slots (spill locations) start at the nest Chunk past the last machine +// register. +// +// Note that stack spill-slots are treated as a very large register set. +// They have all the correct properties for a Register: not aliased (unique +// named). There is some simple mapping from a stack-slot register number +// to the actual location on the stack; this mapping depends on the calling +// conventions and is described in the ADL. +// +// Note that Name is not enum. C++ standard defines that the range of enum +// is the range of smallest bit-field that can represent all enumerators +// declared in the enum. The result of assigning a value to enum is undefined +// if the value is outside the enumeration's valid range. OptoReg::Name is +// typedef'ed as int, because it needs to be able to represent spill-slots. +// +class OptoReg VALUE_OBJ_CLASS_SPEC { + + friend class C2Compiler; + public: + typedef int Name; + enum { + // Chunk 0 + Physical = AdlcVMDeps::Physical, // Start of physical regs + // A few oddballs at the edge of the world + Special = -2, // All special (not allocated) values + Bad = -1 // Not a register + }; + + private: + + static const VMReg opto2vm[REG_COUNT]; + static Name vm2opto[ConcreteRegisterImpl::number_of_registers]; + + public: + + // Stack pointer register + static OptoReg::Name c_frame_pointer; + + + + // Increment a register number. As in: + // "for ( OptoReg::Name i; i=Control; i = add(i,1) ) ..." + static Name add( Name x, int y ) { return Name(x+y); } + + // (We would like to have an operator+ for RegName, but it is not + // a class, so this would be illegal in C++.) + + static void dump( int ); + + // Get the stack slot number of an OptoReg::Name + static unsigned int reg2stack( OptoReg::Name r) { + assert( r >= stack0(), " must be"); + return r - stack0(); + } + + // convert a stack slot number into an OptoReg::Name + static OptoReg::Name stack2reg( int idx) { + return Name(stack0() + idx); + } + + static bool is_stack(Name n) { + return n >= stack0(); + } + + static bool is_valid(Name n) { + return (n != Bad); + } + + static bool is_reg(Name n) { + return is_valid(n) && !is_stack(n); + } + + static VMReg as_VMReg(OptoReg::Name n) { + if (is_reg(n)) { + // Must use table, it'd be nice if Bad was indexable... + return opto2vm[n]; + } else { + assert(!is_stack(n), "must un warp"); + return VMRegImpl::Bad(); + } + } + + // Can un-warp a stack slot or convert a register or Bad + static VMReg as_VMReg(OptoReg::Name n, int frame_size, int arg_count) { + if (is_reg(n)) { + // Must use table, it'd be nice if Bad was indexable... + return opto2vm[n]; + } else if (is_stack(n)) { + int stack_slot = reg2stack(n); + if (stack_slot < arg_count) { + return VMRegImpl::stack2reg(stack_slot + frame_size); + } + return VMRegImpl::stack2reg(stack_slot - arg_count); + // return return VMRegImpl::stack2reg(reg2stack(OptoReg::add(n, -arg_count))); + } else { + return VMRegImpl::Bad(); + } + } + + static OptoReg::Name as_OptoReg(VMReg r) { + if (r->is_stack()) { + assert(false, "must warp"); + return stack2reg(r->reg2stack()); + } else if (r->is_valid()) { + // Must use table, it'd be nice if Bad was indexable... + return vm2opto[r->value()]; + } else { + return Bad; + } + } + + static OptoReg::Name stack0() { + return VMRegImpl::stack0->value(); + } + + static const char* regname(OptoReg::Name n) { + return as_VMReg(n)->name(); + } + +}; + +//---------------------------OptoRegPair------------------------------------------- +// Pairs of 32-bit registers for the allocator. +// This is a very similar class to VMRegPair. C2 only interfaces with VMRegPair +// via the calling convention code which is shared between the compilers. +// Since C2 uses OptoRegs for register allocation it is more efficient to use +// VMRegPair internally for nodes that can contain a pair of OptoRegs rather +// than use VMRegPair and continually be converting back and forth. So normally +// C2 will take in a VMRegPair from the calling convention code and immediately +// convert them to an OptoRegPair and stay in the OptoReg world. The only over +// conversion between OptoRegs and VMRegs is for debug info and oopMaps. This +// is not a high bandwidth spot and so it is not an issue. +// Note that onde other consequence of staying in the OptoReg world with OptoRegPairs +// is that there are "physical" OptoRegs that are not representable in the VMReg +// world, notably flags. [ But by design there is "space" in the VMReg world +// for such registers they just may not be concrete ]. So if we were to use VMRegPair +// then the VMReg world would have to have a representation for these registers +// so that a OptoReg->VMReg->OptoReg would reproduce ther original OptoReg. As it +// stands if you convert a flag (condition code) to a VMReg you will get VMRegImpl::Bad +// and converting that will return OptoReg::Bad losing the identity of the OptoReg. + +class OptoRegPair { +private: + short _second; + short _first; +public: + void set_bad ( ) { _second = OptoReg::Bad; _first = OptoReg::Bad; } + void set1 ( OptoReg::Name n ) { _second = OptoReg::Bad; _first = n; } + void set2 ( OptoReg::Name n ) { _second = n + 1; _first = n; } + void set_pair( OptoReg::Name second, OptoReg::Name first ) { _second= second; _first= first; } + void set_ptr ( OptoReg::Name ptr ) { +#ifdef _LP64 + _second = ptr+1; +#else + _second = OptoReg::Bad; +#endif + _first = ptr; + } + + OptoReg::Name second() const { return _second; } + OptoReg::Name first() const { return _first; } + OptoRegPair(OptoReg::Name second, OptoReg::Name first) { _second = second; _first = first; } + OptoRegPair(OptoReg::Name f) { _second = OptoReg::Bad; _first = f; } + OptoRegPair() { _second = OptoReg::Bad; _first = OptoReg::Bad; } +}; diff --git a/hotspot/src/share/vm/opto/output.cpp b/hotspot/src/share/vm/opto/output.cpp new file mode 100644 index 00000000000..7868ec2378f --- /dev/null +++ b/hotspot/src/share/vm/opto/output.cpp @@ -0,0 +1,2680 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_output.cpp.incl" + +extern uint size_java_to_interp(); +extern uint reloc_java_to_interp(); +extern uint size_exception_handler(); +extern uint size_deopt_handler(); + +#ifndef PRODUCT +#define DEBUG_ARG(x) , x +#else +#define DEBUG_ARG(x) +#endif + +extern int emit_exception_handler(CodeBuffer &cbuf); +extern int emit_deopt_handler(CodeBuffer &cbuf); + +//------------------------------Output----------------------------------------- +// Convert Nodes to instruction bits and pass off to the VM +void Compile::Output() { + // RootNode goes + assert( _cfg->_broot->_nodes.size() == 0, "" ); + + // Initialize the space for the BufferBlob used to find and verify + // instruction size in MachNode::emit_size() + init_scratch_buffer_blob(); + + // Make sure I can find the Start Node + Block_Array& bbs = _cfg->_bbs; + Block *entry = _cfg->_blocks[1]; + Block *broot = _cfg->_broot; + + const StartNode *start = entry->_nodes[0]->as_Start(); + + // Replace StartNode with prolog + MachPrologNode *prolog = new (this) MachPrologNode(); + entry->_nodes.map( 0, prolog ); + bbs.map( prolog->_idx, entry ); + bbs.map( start->_idx, NULL ); // start is no longer in any block + + // Virtual methods need an unverified entry point + + if( is_osr_compilation() ) { + if( PoisonOSREntry ) { + // TODO: Should use a ShouldNotReachHereNode... + _cfg->insert( broot, 0, new (this) MachBreakpointNode() ); + } + } else { + if( _method && !_method->flags().is_static() ) { + // Insert unvalidated entry point + _cfg->insert( broot, 0, new (this) MachUEPNode() ); + } + + } + + + // Break before main entry point + if( (_method && _method->break_at_execute()) +#ifndef PRODUCT + ||(OptoBreakpoint && is_method_compilation()) + ||(OptoBreakpointOSR && is_osr_compilation()) + ||(OptoBreakpointC2R && !_method) +#endif + ) { + // checking for _method means that OptoBreakpoint does not apply to + // runtime stubs or frame converters + _cfg->insert( entry, 1, new (this) MachBreakpointNode() ); + } + + // Insert epilogs before every return + for( uint i=0; i<_cfg->_num_blocks; i++ ) { + Block *b = _cfg->_blocks[i]; + if( !b->is_connector() && b->non_connector_successor(0) == _cfg->_broot ) { // Found a program exit point? + Node *m = b->end(); + if( m->is_Mach() && m->as_Mach()->ideal_Opcode() != Op_Halt ) { + MachEpilogNode *epilog = new (this) MachEpilogNode(m->as_Mach()->ideal_Opcode() == Op_Return); + b->add_inst( epilog ); + bbs.map(epilog->_idx, b); + //_regalloc->set_bad(epilog->_idx); // Already initialized this way. + } + } + } + +# ifdef ENABLE_ZAP_DEAD_LOCALS + if ( ZapDeadCompiledLocals ) Insert_zap_nodes(); +# endif + + ScheduleAndBundle(); + +#ifndef PRODUCT + if (trace_opto_output()) { + tty->print("\n---- After ScheduleAndBundle ----\n"); + for (uint i = 0; i < _cfg->_num_blocks; i++) { + tty->print("\nBB#%03d:\n", i); + Block *bb = _cfg->_blocks[i]; + for (uint j = 0; j < bb->_nodes.size(); j++) { + Node *n = bb->_nodes[j]; + OptoReg::Name reg = _regalloc->get_reg_first(n); + tty->print(" %-6s ", reg >= 0 && reg < REG_COUNT ? Matcher::regName[reg] : ""); + n->dump(); + } + } + } +#endif + + if (failing()) return; + + BuildOopMaps(); + + if (failing()) return; + + Fill_buffer(); +} + +bool Compile::need_stack_bang(int frame_size_in_bytes) const { + // Determine if we need to generate a stack overflow check. + // Do it if the method is not a stub function and + // has java calls or has frame size > vm_page_size/8. + return (stub_function() == NULL && + (has_java_calls() || frame_size_in_bytes > os::vm_page_size()>>3)); +} + +bool Compile::need_register_stack_bang() const { + // Determine if we need to generate a register stack overflow check. + // This is only used on architectures which have split register + // and memory stacks (ie. IA64). + // Bang if the method is not a stub function and has java calls + return (stub_function() == NULL && has_java_calls()); +} + +# ifdef ENABLE_ZAP_DEAD_LOCALS + + +// In order to catch compiler oop-map bugs, we have implemented +// a debugging mode called ZapDeadCompilerLocals. +// This mode causes the compiler to insert a call to a runtime routine, +// "zap_dead_locals", right before each place in compiled code +// that could potentially be a gc-point (i.e., a safepoint or oop map point). +// The runtime routine checks that locations mapped as oops are really +// oops, that locations mapped as values do not look like oops, +// and that locations mapped as dead are not used later +// (by zapping them to an invalid address). + +int Compile::_CompiledZap_count = 0; + +void Compile::Insert_zap_nodes() { + bool skip = false; + + + // Dink with static counts because code code without the extra + // runtime calls is MUCH faster for debugging purposes + + if ( CompileZapFirst == 0 ) ; // nothing special + else if ( CompileZapFirst > CompiledZap_count() ) skip = true; + else if ( CompileZapFirst == CompiledZap_count() ) + warning("starting zap compilation after skipping"); + + if ( CompileZapLast == -1 ) ; // nothing special + else if ( CompileZapLast < CompiledZap_count() ) skip = true; + else if ( CompileZapLast == CompiledZap_count() ) + warning("about to compile last zap"); + + ++_CompiledZap_count; // counts skipped zaps, too + + if ( skip ) return; + + + if ( _method == NULL ) + return; // no safepoints/oopmaps emitted for calls in stubs,so we don't care + + // Insert call to zap runtime stub before every node with an oop map + for( uint i=0; i<_cfg->_num_blocks; i++ ) { + Block *b = _cfg->_blocks[i]; + for ( uint j = 0; j < b->_nodes.size(); ++j ) { + Node *n = b->_nodes[j]; + + // Determining if we should insert a zap-a-lot node in output. + // We do that for all nodes that has oopmap info, except for calls + // to allocation. Calls to allocation passes in the old top-of-eden pointer + // and expect the C code to reset it. Hence, there can be no safepoints between + // the inlined-allocation and the call to new_Java, etc. + // We also cannot zap monitor calls, as they must hold the microlock + // during the call to Zap, which also wants to grab the microlock. + bool insert = n->is_MachSafePoint() && (n->as_MachSafePoint()->oop_map() != NULL); + if ( insert ) { // it is MachSafePoint + if ( !n->is_MachCall() ) { + insert = false; + } else if ( n->is_MachCall() ) { + MachCallNode* call = n->as_MachCall(); + if (call->entry_point() == OptoRuntime::new_instance_Java() || + call->entry_point() == OptoRuntime::new_array_Java() || + call->entry_point() == OptoRuntime::multianewarray2_Java() || + call->entry_point() == OptoRuntime::multianewarray3_Java() || + call->entry_point() == OptoRuntime::multianewarray4_Java() || + call->entry_point() == OptoRuntime::multianewarray5_Java() || + call->entry_point() == OptoRuntime::slow_arraycopy_Java() || + call->entry_point() == OptoRuntime::complete_monitor_locking_Java() + ) { + insert = false; + } + } + if (insert) { + Node *zap = call_zap_node(n->as_MachSafePoint(), i); + b->_nodes.insert( j, zap ); + _cfg->_bbs.map( zap->_idx, b ); + ++j; + } + } + } + } +} + + +Node* Compile::call_zap_node(MachSafePointNode* node_to_check, int block_no) { + const TypeFunc *tf = OptoRuntime::zap_dead_locals_Type(); + CallStaticJavaNode* ideal_node = + new (this, tf->domain()->cnt()) CallStaticJavaNode( tf, + OptoRuntime::zap_dead_locals_stub(_method->flags().is_native()), + "call zap dead locals stub", 0, TypePtr::BOTTOM); + // We need to copy the OopMap from the site we're zapping at. + // We have to make a copy, because the zap site might not be + // a call site, and zap_dead is a call site. + OopMap* clone = node_to_check->oop_map()->deep_copy(); + + // Add the cloned OopMap to the zap node + ideal_node->set_oop_map(clone); + return _matcher->match_sfpt(ideal_node); +} + +//------------------------------is_node_getting_a_safepoint-------------------- +bool Compile::is_node_getting_a_safepoint( Node* n) { + // This code duplicates the logic prior to the call of add_safepoint + // below in this file. + if( n->is_MachSafePoint() ) return true; + return false; +} + +# endif // ENABLE_ZAP_DEAD_LOCALS + +//------------------------------compute_loop_first_inst_sizes------------------ +// Compute the size of first NumberOfLoopInstrToAlign instructions at head +// of a loop. When aligning a loop we need to provide enough instructions +// in cpu's fetch buffer to feed decoders. The loop alignment could be +// avoided if we have enough instructions in fetch buffer at the head of a loop. +// By default, the size is set to 999999 by Block's constructor so that +// a loop will be aligned if the size is not reset here. +// +// Note: Mach instructions could contain several HW instructions +// so the size is estimated only. +// +void Compile::compute_loop_first_inst_sizes() { + // The next condition is used to gate the loop alignment optimization. + // Don't aligned a loop if there are enough instructions at the head of a loop + // or alignment padding is larger then MaxLoopPad. By default, MaxLoopPad + // is equal to OptoLoopAlignment-1 except on new Intel cpus, where it is + // equal to 11 bytes which is the largest address NOP instruction. + if( MaxLoopPad < OptoLoopAlignment-1 ) { + uint last_block = _cfg->_num_blocks-1; + for( uint i=1; i <= last_block; i++ ) { + Block *b = _cfg->_blocks[i]; + // Check the first loop's block which requires an alignment. + if( b->head()->is_Loop() && + b->code_alignment() > (uint)relocInfo::addr_unit() ) { + uint sum_size = 0; + uint inst_cnt = NumberOfLoopInstrToAlign; + inst_cnt = b->compute_first_inst_size(sum_size, inst_cnt, + _regalloc); + // Check the next fallthrough block if first loop's block does not have + // enough instructions. + if( inst_cnt > 0 && i < last_block ) { + // First, check if the first loop's block contains whole loop. + // LoopNode::LoopBackControl == 2. + Block *bx = _cfg->_bbs[b->pred(2)->_idx]; + // Skip connector blocks (with limit in case of irreducible loops). + int search_limit = 16; + while( bx->is_connector() && search_limit-- > 0) { + bx = _cfg->_bbs[bx->pred(1)->_idx]; + } + if( bx != b ) { // loop body is in several blocks. + Block *nb = NULL; + while( inst_cnt > 0 && i < last_block && nb != bx && + !_cfg->_blocks[i+1]->head()->is_Loop() ) { + i++; + nb = _cfg->_blocks[i]; + inst_cnt = nb->compute_first_inst_size(sum_size, inst_cnt, + _regalloc); + } // while( inst_cnt > 0 && i < last_block ) + } // if( bx != b ) + } // if( inst_cnt > 0 && i < last_block ) + b->set_first_inst_size(sum_size); + } // f( b->head()->is_Loop() ) + } // for( i <= last_block ) + } // if( MaxLoopPad < OptoLoopAlignment-1 ) +} + +//----------------------Shorten_branches--------------------------------------- +// The architecture description provides short branch variants for some long +// branch instructions. Replace eligible long branches with short branches. +void Compile::Shorten_branches(Label *labels, int& code_size, int& reloc_size, int& stub_size, int& const_size) { + + // fill in the nop array for bundling computations + MachNode *_nop_list[Bundle::_nop_count]; + Bundle::initialize_nops(_nop_list, this); + + // ------------------ + // Compute size of each block, method size, and relocation information size + uint *jmp_end = NEW_RESOURCE_ARRAY(uint,_cfg->_num_blocks); + uint *blk_starts = NEW_RESOURCE_ARRAY(uint,_cfg->_num_blocks+1); + DEBUG_ONLY( uint *jmp_target = NEW_RESOURCE_ARRAY(uint,_cfg->_num_blocks); ) + blk_starts[0] = 0; + + // Initialize the sizes to 0 + code_size = 0; // Size in bytes of generated code + stub_size = 0; // Size in bytes of all stub entries + // Size in bytes of all relocation entries, including those in local stubs. + // Start with 2-bytes of reloc info for the unvalidated entry point + reloc_size = 1; // Number of relocation entries + const_size = 0; // size of fp constants in words + + // Make three passes. The first computes pessimistic blk_starts, + // relative jmp_end, reloc_size and const_size information. + // The second performs short branch substitution using the pessimistic + // sizing. The third inserts nops where needed. + + Node *nj; // tmp + + // Step one, perform a pessimistic sizing pass. + uint i; + uint min_offset_from_last_call = 1; // init to a positive value + uint nop_size = (new (this) MachNopNode())->size(_regalloc); + for( i=0; i<_cfg->_num_blocks; i++ ) { // For all blocks + Block *b = _cfg->_blocks[i]; + + // Sum all instruction sizes to compute block size + uint last_inst = b->_nodes.size(); + uint blk_size = 0; + for( uint j = 0; j_nodes[j]; + uint inst_size = nj->size(_regalloc); + blk_size += inst_size; + // Handle machine instruction nodes + if( nj->is_Mach() ) { + MachNode *mach = nj->as_Mach(); + blk_size += (mach->alignment_required() - 1) * relocInfo::addr_unit(); // assume worst case padding + reloc_size += mach->reloc(); + const_size += mach->const_size(); + if( mach->is_MachCall() ) { + MachCallNode *mcall = mach->as_MachCall(); + // This destination address is NOT PC-relative + + mcall->method_set((intptr_t)mcall->entry_point()); + + if( mcall->is_MachCallJava() && mcall->as_MachCallJava()->_method ) { + stub_size += size_java_to_interp(); + reloc_size += reloc_java_to_interp(); + } + } else if (mach->is_MachSafePoint()) { + // If call/safepoint are adjacent, account for possible + // nop to disambiguate the two safepoints. + if (min_offset_from_last_call == 0) { + blk_size += nop_size; + } + } + } + min_offset_from_last_call += inst_size; + // Remember end of call offset + if (nj->is_MachCall() && nj->as_MachCall()->is_safepoint_node()) { + min_offset_from_last_call = 0; + } + } + + // During short branch replacement, we store the relative (to blk_starts) + // end of jump in jmp_end, rather than the absolute end of jump. This + // is so that we do not need to recompute sizes of all nodes when we compute + // correct blk_starts in our next sizing pass. + jmp_end[i] = blk_size; + DEBUG_ONLY( jmp_target[i] = 0; ) + + // When the next block starts a loop, we may insert pad NOP + // instructions. Since we cannot know our future alignment, + // assume the worst. + if( i<_cfg->_num_blocks-1 ) { + Block *nb = _cfg->_blocks[i+1]; + int max_loop_pad = nb->code_alignment()-relocInfo::addr_unit(); + if( max_loop_pad > 0 ) { + assert(is_power_of_2(max_loop_pad+relocInfo::addr_unit()), ""); + blk_size += max_loop_pad; + } + } + + // Save block size; update total method size + blk_starts[i+1] = blk_starts[i]+blk_size; + } + + // Step two, replace eligible long jumps. + + // Note: this will only get the long branches within short branch + // range. Another pass might detect more branches that became + // candidates because the shortening in the first pass exposed + // more opportunities. Unfortunately, this would require + // recomputing the starting and ending positions for the blocks + for( i=0; i<_cfg->_num_blocks; i++ ) { + Block *b = _cfg->_blocks[i]; + + int j; + // Find the branch; ignore trailing NOPs. + for( j = b->_nodes.size()-1; j>=0; j-- ) { + nj = b->_nodes[j]; + if( !nj->is_Mach() || nj->as_Mach()->ideal_Opcode() != Op_Con ) + break; + } + + if (j >= 0) { + if( nj->is_Mach() && nj->as_Mach()->may_be_short_branch() ) { + MachNode *mach = nj->as_Mach(); + // This requires the TRUE branch target be in succs[0] + uint bnum = b->non_connector_successor(0)->_pre_order; + uintptr_t target = blk_starts[bnum]; + if( mach->is_pc_relative() ) { + int offset = target-(blk_starts[i] + jmp_end[i]); + if (_matcher->is_short_branch_offset(offset)) { + // We've got a winner. Replace this branch. + MachNode *replacement = mach->short_branch_version(this); + b->_nodes.map(j, replacement); + + // Update the jmp_end size to save time in our + // next pass. + jmp_end[i] -= (mach->size(_regalloc) - replacement->size(_regalloc)); + DEBUG_ONLY( jmp_target[i] = bnum; ); + } + } else { +#ifndef PRODUCT + mach->dump(3); +#endif + Unimplemented(); + } + } + } + } + + // Compute the size of first NumberOfLoopInstrToAlign instructions at head + // of a loop. It is used to determine the padding for loop alignment. + compute_loop_first_inst_sizes(); + + // Step 3, compute the offsets of all the labels + uint last_call_adr = max_uint; + for( i=0; i<_cfg->_num_blocks; i++ ) { // For all blocks + // copy the offset of the beginning to the corresponding label + assert(labels[i].is_unused(), "cannot patch at this point"); + labels[i].bind_loc(blk_starts[i], CodeBuffer::SECT_INSTS); + + // insert padding for any instructions that need it + Block *b = _cfg->_blocks[i]; + uint last_inst = b->_nodes.size(); + uint adr = blk_starts[i]; + for( uint j = 0; j_nodes[j]; + if( nj->is_Mach() ) { + int padding = nj->as_Mach()->compute_padding(adr); + // If call/safepoint are adjacent insert a nop (5010568) + if (padding == 0 && nj->is_MachSafePoint() && !nj->is_MachCall() && + adr == last_call_adr ) { + padding = nop_size; + } + if(padding > 0) { + assert((padding % nop_size) == 0, "padding is not a multiple of NOP size"); + int nops_cnt = padding / nop_size; + MachNode *nop = new (this) MachNopNode(nops_cnt); + b->_nodes.insert(j++, nop); + _cfg->_bbs.map( nop->_idx, b ); + adr += padding; + last_inst++; + } + } + adr += nj->size(_regalloc); + + // Remember end of call offset + if (nj->is_MachCall() && nj->as_MachCall()->is_safepoint_node()) { + last_call_adr = adr; + } + } + + if ( i != _cfg->_num_blocks-1) { + // Get the size of the block + uint blk_size = adr - blk_starts[i]; + + // When the next block starts a loop, we may insert pad NOP + // instructions. + Block *nb = _cfg->_blocks[i+1]; + int current_offset = blk_starts[i] + blk_size; + current_offset += nb->alignment_padding(current_offset); + // Save block size; update total method size + blk_starts[i+1] = current_offset; + } + } + +#ifdef ASSERT + for( i=0; i<_cfg->_num_blocks; i++ ) { // For all blocks + if( jmp_target[i] != 0 ) { + int offset = blk_starts[jmp_target[i]]-(blk_starts[i] + jmp_end[i]); + if (!_matcher->is_short_branch_offset(offset)) { + tty->print_cr("target (%d) - jmp_end(%d) = offset (%d), jmp_block B%d, target_block B%d", blk_starts[jmp_target[i]], blk_starts[i] + jmp_end[i], offset, i, jmp_target[i]); + } + assert(_matcher->is_short_branch_offset(offset), "Displacement too large for short jmp"); + } + } +#endif + + // ------------------ + // Compute size for code buffer + code_size = blk_starts[i-1] + jmp_end[i-1]; + + // Relocation records + reloc_size += 1; // Relo entry for exception handler + + // Adjust reloc_size to number of record of relocation info + // Min is 2 bytes, max is probably 6 or 8, with a tax up to 25% for + // a relocation index. + // The CodeBuffer will expand the locs array if this estimate is too low. + reloc_size *= 10 / sizeof(relocInfo); + + // Adjust const_size to number of bytes + const_size *= 2*jintSize; // both float and double take two words per entry + +} + +//------------------------------FillLocArray----------------------------------- +// Create a bit of debug info and append it to the array. The mapping is from +// Java local or expression stack to constant, register or stack-slot. For +// doubles, insert 2 mappings and return 1 (to tell the caller that the next +// entry has been taken care of and caller should skip it). +static LocationValue *new_loc_value( PhaseRegAlloc *ra, OptoReg::Name regnum, Location::Type l_type ) { + // This should never have accepted Bad before + assert(OptoReg::is_valid(regnum), "location must be valid"); + return (OptoReg::is_reg(regnum)) + ? new LocationValue(Location::new_reg_loc(l_type, OptoReg::as_VMReg(regnum)) ) + : new LocationValue(Location::new_stk_loc(l_type, ra->reg2offset(regnum))); +} + +void Compile::FillLocArray( int idx, Node *local, GrowableArray *array ) { + assert( local, "use _top instead of null" ); + if (array->length() != idx) { + assert(array->length() == idx + 1, "Unexpected array count"); + // Old functionality: + // return + // New functionality: + // Assert if the local is not top. In product mode let the new node + // override the old entry. + assert(local == top(), "LocArray collision"); + if (local == top()) { + return; + } + array->pop(); + } + const Type *t = local->bottom_type(); + + // Grab the register number for the local + OptoReg::Name regnum = _regalloc->get_reg_first(local); + if( OptoReg::is_valid(regnum) ) {// Got a register/stack? + // Record the double as two float registers. + // The register mask for such a value always specifies two adjacent + // float registers, with the lower register number even. + // Normally, the allocation of high and low words to these registers + // is irrelevant, because nearly all operations on register pairs + // (e.g., StoreD) treat them as a single unit. + // Here, we assume in addition that the words in these two registers + // stored "naturally" (by operations like StoreD and double stores + // within the interpreter) such that the lower-numbered register + // is written to the lower memory address. This may seem like + // a machine dependency, but it is not--it is a requirement on + // the author of the .ad file to ensure that, for every + // even/odd double-register pair to which a double may be allocated, + // the word in the even single-register is stored to the first + // memory word. (Note that register numbers are completely + // arbitrary, and are not tied to any machine-level encodings.) +#ifdef _LP64 + if( t->base() == Type::DoubleBot || t->base() == Type::DoubleCon ) { + array->append(new ConstantIntValue(0)); + array->append(new_loc_value( _regalloc, regnum, Location::dbl )); + } else if ( t->base() == Type::Long ) { + array->append(new ConstantIntValue(0)); + array->append(new_loc_value( _regalloc, regnum, Location::lng )); + } else if ( t->base() == Type::RawPtr ) { + // jsr/ret return address which must be restored into a the full + // width 64-bit stack slot. + array->append(new_loc_value( _regalloc, regnum, Location::lng )); + } +#else //_LP64 +#ifdef SPARC + if (t->base() == Type::Long && OptoReg::is_reg(regnum)) { + // For SPARC we have to swap high and low words for + // long values stored in a single-register (g0-g7). + array->append(new_loc_value( _regalloc, regnum , Location::normal )); + array->append(new_loc_value( _regalloc, OptoReg::add(regnum,1), Location::normal )); + } else +#endif //SPARC + if( t->base() == Type::DoubleBot || t->base() == Type::DoubleCon || t->base() == Type::Long ) { + // Repack the double/long as two jints. + // The convention the interpreter uses is that the second local + // holds the first raw word of the native double representation. + // This is actually reasonable, since locals and stack arrays + // grow downwards in all implementations. + // (If, on some machine, the interpreter's Java locals or stack + // were to grow upwards, the embedded doubles would be word-swapped.) + array->append(new_loc_value( _regalloc, OptoReg::add(regnum,1), Location::normal )); + array->append(new_loc_value( _regalloc, regnum , Location::normal )); + } +#endif //_LP64 + else if( (t->base() == Type::FloatBot || t->base() == Type::FloatCon) && + OptoReg::is_reg(regnum) ) { + array->append(new_loc_value( _regalloc, regnum, Matcher::float_in_double + ? Location::float_in_dbl : Location::normal )); + } else if( t->base() == Type::Int && OptoReg::is_reg(regnum) ) { + array->append(new_loc_value( _regalloc, regnum, Matcher::int_in_long + ? Location::int_in_long : Location::normal )); + } else { + array->append(new_loc_value( _regalloc, regnum, _regalloc->is_oop(local) ? Location::oop : Location::normal )); + } + return; + } + + // No register. It must be constant data. + switch (t->base()) { + case Type::Half: // Second half of a double + ShouldNotReachHere(); // Caller should skip 2nd halves + break; + case Type::AnyPtr: + array->append(new ConstantOopWriteValue(NULL)); + break; + case Type::AryPtr: + case Type::InstPtr: + case Type::KlassPtr: // fall through + array->append(new ConstantOopWriteValue(t->isa_oopptr()->const_oop()->encoding())); + break; + case Type::Int: + array->append(new ConstantIntValue(t->is_int()->get_con())); + break; + case Type::RawPtr: + // A return address (T_ADDRESS). + assert((intptr_t)t->is_ptr()->get_con() < (intptr_t)0x10000, "must be a valid BCI"); +#ifdef _LP64 + // Must be restored to the full-width 64-bit stack slot. + array->append(new ConstantLongValue(t->is_ptr()->get_con())); +#else + array->append(new ConstantIntValue(t->is_ptr()->get_con())); +#endif + break; + case Type::FloatCon: { + float f = t->is_float_constant()->getf(); + array->append(new ConstantIntValue(jint_cast(f))); + break; + } + case Type::DoubleCon: { + jdouble d = t->is_double_constant()->getd(); +#ifdef _LP64 + array->append(new ConstantIntValue(0)); + array->append(new ConstantDoubleValue(d)); +#else + // Repack the double as two jints. + // The convention the interpreter uses is that the second local + // holds the first raw word of the native double representation. + // This is actually reasonable, since locals and stack arrays + // grow downwards in all implementations. + // (If, on some machine, the interpreter's Java locals or stack + // were to grow upwards, the embedded doubles would be word-swapped.) + jint *dp = (jint*)&d; + array->append(new ConstantIntValue(dp[1])); + array->append(new ConstantIntValue(dp[0])); +#endif + break; + } + case Type::Long: { + jlong d = t->is_long()->get_con(); +#ifdef _LP64 + array->append(new ConstantIntValue(0)); + array->append(new ConstantLongValue(d)); +#else + // Repack the long as two jints. + // The convention the interpreter uses is that the second local + // holds the first raw word of the native double representation. + // This is actually reasonable, since locals and stack arrays + // grow downwards in all implementations. + // (If, on some machine, the interpreter's Java locals or stack + // were to grow upwards, the embedded doubles would be word-swapped.) + jint *dp = (jint*)&d; + array->append(new ConstantIntValue(dp[1])); + array->append(new ConstantIntValue(dp[0])); +#endif + break; + } + case Type::Top: // Add an illegal value here + array->append(new LocationValue(Location())); + break; + default: + ShouldNotReachHere(); + break; + } +} + +// Determine if this node starts a bundle +bool Compile::starts_bundle(const Node *n) const { + return (_node_bundling_limit > n->_idx && + _node_bundling_base[n->_idx].starts_bundle()); +} + +//--------------------------Process_OopMap_Node-------------------------------- +void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) { + + // Handle special safepoint nodes for synchronization + MachSafePointNode *sfn = mach->as_MachSafePoint(); + MachCallNode *mcall; + +#ifdef ENABLE_ZAP_DEAD_LOCALS + assert( is_node_getting_a_safepoint(mach), "logic does not match; false negative"); +#endif + + int safepoint_pc_offset = current_offset; + + // Add the safepoint in the DebugInfoRecorder + if( !mach->is_MachCall() ) { + mcall = NULL; + debug_info()->add_safepoint(safepoint_pc_offset, sfn->_oop_map); + } else { + mcall = mach->as_MachCall(); + safepoint_pc_offset += mcall->ret_addr_offset(); + debug_info()->add_safepoint(safepoint_pc_offset, mcall->_oop_map); + } + + // Loop over the JVMState list to add scope information + // Do not skip safepoints with a NULL method, they need monitor info + JVMState* youngest_jvms = sfn->jvms(); + int max_depth = youngest_jvms->depth(); + + // Visit scopes from oldest to youngest. + for (int depth = 1; depth <= max_depth; depth++) { + JVMState* jvms = youngest_jvms->of_depth(depth); + int idx; + ciMethod* method = jvms->has_method() ? jvms->method() : NULL; + // Safepoints that do not have method() set only provide oop-map and monitor info + // to support GC; these do not support deoptimization. + int num_locs = (method == NULL) ? 0 : jvms->loc_size(); + int num_exps = (method == NULL) ? 0 : jvms->stk_size(); + int num_mon = jvms->nof_monitors(); + assert(method == NULL || jvms->bci() < 0 || num_locs == method->max_locals(), + "JVMS local count must match that of the method"); + + // Add Local and Expression Stack Information + + // Insert locals into the locarray + GrowableArray *locarray = new GrowableArray(num_locs); + for( idx = 0; idx < num_locs; idx++ ) { + FillLocArray( idx, sfn->local(jvms, idx), locarray ); + } + + // Insert expression stack entries into the exparray + GrowableArray *exparray = new GrowableArray(num_exps); + for( idx = 0; idx < num_exps; idx++ ) { + FillLocArray( idx, sfn->stack(jvms, idx), exparray ); + } + + // Add in mappings of the monitors + assert( !method || + !method->is_synchronized() || + method->is_native() || + num_mon > 0 || + !GenerateSynchronizationCode, + "monitors must always exist for synchronized methods"); + + // Build the growable array of ScopeValues for exp stack + GrowableArray *monarray = new GrowableArray(num_mon); + + // Loop over monitors and insert into array + for(idx = 0; idx < num_mon; idx++) { + // Grab the node that defines this monitor + Node* box_node; + Node* obj_node; + box_node = sfn->monitor_box(jvms, idx); + obj_node = sfn->monitor_obj(jvms, idx); + + // Create ScopeValue for object + ScopeValue *scval = NULL; + if( !obj_node->is_Con() ) { + OptoReg::Name obj_reg = _regalloc->get_reg_first(obj_node); + scval = new_loc_value( _regalloc, obj_reg, Location::oop ); + } else { + scval = new ConstantOopWriteValue(obj_node->bottom_type()->is_instptr()->const_oop()->encoding()); + } + + OptoReg::Name box_reg = BoxLockNode::stack_slot(box_node); + monarray->append(new MonitorValue(scval, Location::new_stk_loc(Location::normal,_regalloc->reg2offset(box_reg)))); + } + + // Build first class objects to pass to scope + DebugToken *locvals = debug_info()->create_scope_values(locarray); + DebugToken *expvals = debug_info()->create_scope_values(exparray); + DebugToken *monvals = debug_info()->create_monitor_values(monarray); + + // Make method available for all Safepoints + ciMethod* scope_method = method ? method : _method; + // Describe the scope here + assert(jvms->bci() >= InvocationEntryBci && jvms->bci() <= 0x10000, "must be a valid or entry BCI"); + debug_info()->describe_scope(safepoint_pc_offset,scope_method,jvms->bci(),locvals,expvals,monvals); + } // End jvms loop + + // Mark the end of the scope set. + debug_info()->end_safepoint(safepoint_pc_offset); +} + + + +// A simplified version of Process_OopMap_Node, to handle non-safepoints. +class NonSafepointEmitter { + Compile* C; + JVMState* _pending_jvms; + int _pending_offset; + + void emit_non_safepoint(); + + public: + NonSafepointEmitter(Compile* compile) { + this->C = compile; + _pending_jvms = NULL; + _pending_offset = 0; + } + + void observe_instruction(Node* n, int pc_offset) { + if (!C->debug_info()->recording_non_safepoints()) return; + + Node_Notes* nn = C->node_notes_at(n->_idx); + if (nn == NULL || nn->jvms() == NULL) return; + if (_pending_jvms != NULL && + _pending_jvms->same_calls_as(nn->jvms())) { + // Repeated JVMS? Stretch it up here. + _pending_offset = pc_offset; + } else { + if (_pending_jvms != NULL && + _pending_offset < pc_offset) { + emit_non_safepoint(); + } + _pending_jvms = NULL; + if (pc_offset > C->debug_info()->last_pc_offset()) { + // This is the only way _pending_jvms can become non-NULL: + _pending_jvms = nn->jvms(); + _pending_offset = pc_offset; + } + } + } + + // Stay out of the way of real safepoints: + void observe_safepoint(JVMState* jvms, int pc_offset) { + if (_pending_jvms != NULL && + !_pending_jvms->same_calls_as(jvms) && + _pending_offset < pc_offset) { + emit_non_safepoint(); + } + _pending_jvms = NULL; + } + + void flush_at_end() { + if (_pending_jvms != NULL) { + emit_non_safepoint(); + } + _pending_jvms = NULL; + } +}; + +void NonSafepointEmitter::emit_non_safepoint() { + JVMState* youngest_jvms = _pending_jvms; + int pc_offset = _pending_offset; + + // Clear it now: + _pending_jvms = NULL; + + DebugInformationRecorder* debug_info = C->debug_info(); + assert(debug_info->recording_non_safepoints(), "sanity"); + + debug_info->add_non_safepoint(pc_offset); + int max_depth = youngest_jvms->depth(); + + // Visit scopes from oldest to youngest. + for (int depth = 1; depth <= max_depth; depth++) { + JVMState* jvms = youngest_jvms->of_depth(depth); + ciMethod* method = jvms->has_method() ? jvms->method() : NULL; + debug_info->describe_scope(pc_offset, method, jvms->bci()); + } + + // Mark the end of the scope set. + debug_info->end_non_safepoint(pc_offset); +} + + + +// helper for Fill_buffer bailout logic +static void turn_off_compiler(Compile* C) { + if (CodeCache::unallocated_capacity() >= CodeCacheMinimumFreeSpace*10) { + // Do not turn off compilation if a single giant method has + // blown the code cache size. + C->record_failure("excessive request to CodeCache"); + } else { + UseInterpreter = true; + UseCompiler = false; + AlwaysCompileLoopMethods = false; + C->record_failure("CodeCache is full"); + warning("CodeCache is full. Compiling has been disabled"); + } +} + + +//------------------------------Fill_buffer------------------------------------ +void Compile::Fill_buffer() { + + // Set the initially allocated size + int code_req = initial_code_capacity; + int locs_req = initial_locs_capacity; + int stub_req = TraceJumps ? initial_stub_capacity * 10 : initial_stub_capacity; + int const_req = initial_const_capacity; + bool labels_not_set = true; + + int pad_req = NativeCall::instruction_size; + // The extra spacing after the code is necessary on some platforms. + // Sometimes we need to patch in a jump after the last instruction, + // if the nmethod has been deoptimized. (See 4932387, 4894843.) + + uint i; + // Compute the byte offset where we can store the deopt pc. + if (fixed_slots() != 0) { + _orig_pc_slot_offset_in_bytes = _regalloc->reg2offset(OptoReg::stack2reg(_orig_pc_slot)); + } + + // Compute prolog code size + _method_size = 0; + _frame_slots = OptoReg::reg2stack(_matcher->_old_SP)+_regalloc->_framesize; +#ifdef IA64 + if (save_argument_registers()) { + // 4815101: this is a stub with implicit and unknown precision fp args. + // The usual spill mechanism can only generate stfd's in this case, which + // doesn't work if the fp reg to spill contains a single-precision denorm. + // Instead, we hack around the normal spill mechanism using stfspill's and + // ldffill's in the MachProlog and MachEpilog emit methods. We allocate + // space here for the fp arg regs (f8-f15) we're going to thusly spill. + // + // If we ever implement 16-byte 'registers' == stack slots, we can + // get rid of this hack and have SpillCopy generate stfspill/ldffill + // instead of stfd/stfs/ldfd/ldfs. + _frame_slots += 8*(16/BytesPerInt); + } +#endif + assert( _frame_slots >= 0 && _frame_slots < 1000000, "sanity check" ); + + // Create an array of unused labels, one for each basic block + Label *blk_labels = NEW_RESOURCE_ARRAY(Label, _cfg->_num_blocks+1); + + for( i=0; i <= _cfg->_num_blocks; i++ ) { + blk_labels[i].init(); + } + + // If this machine supports different size branch offsets, then pre-compute + // the length of the blocks + if( _matcher->is_short_branch_offset(0) ) { + Shorten_branches(blk_labels, code_req, locs_req, stub_req, const_req); + labels_not_set = false; + } + + // nmethod and CodeBuffer count stubs & constants as part of method's code. + int exception_handler_req = size_exception_handler(); + int deopt_handler_req = size_deopt_handler(); + exception_handler_req += MAX_stubs_size; // add marginal slop for handler + deopt_handler_req += MAX_stubs_size; // add marginal slop for handler + stub_req += MAX_stubs_size; // ensure per-stub margin + code_req += MAX_inst_size; // ensure per-instruction margin + if (StressCodeBuffers) + code_req = const_req = stub_req = exception_handler_req = deopt_handler_req = 0x10; // force expansion + int total_req = code_req + pad_req + stub_req + exception_handler_req + deopt_handler_req + const_req; + CodeBuffer* cb = code_buffer(); + cb->initialize(total_req, locs_req); + + // Have we run out of code space? + if (cb->blob() == NULL) { + turn_off_compiler(this); + return; + } + // Configure the code buffer. + cb->initialize_consts_size(const_req); + cb->initialize_stubs_size(stub_req); + cb->initialize_oop_recorder(env()->oop_recorder()); + + // fill in the nop array for bundling computations + MachNode *_nop_list[Bundle::_nop_count]; + Bundle::initialize_nops(_nop_list, this); + + // Create oopmap set. + _oop_map_set = new OopMapSet(); + + // !!!!! This preserves old handling of oopmaps for now + debug_info()->set_oopmaps(_oop_map_set); + + // Count and start of implicit null check instructions + uint inct_cnt = 0; + uint *inct_starts = NEW_RESOURCE_ARRAY(uint, _cfg->_num_blocks+1); + + // Count and start of calls + uint *call_returns = NEW_RESOURCE_ARRAY(uint, _cfg->_num_blocks+1); + + uint return_offset = 0; + MachNode *nop = new (this) MachNopNode(); + + int previous_offset = 0; + int current_offset = 0; + int last_call_offset = -1; + + // Create an array of unused labels, one for each basic block, if printing is enabled +#ifndef PRODUCT + int *node_offsets = NULL; + uint node_offset_limit = unique(); + + + if ( print_assembly() ) + node_offsets = NEW_RESOURCE_ARRAY(int, node_offset_limit); +#endif + + NonSafepointEmitter non_safepoints(this); // emit non-safepoints lazily + + // ------------------ + // Now fill in the code buffer + Node *delay_slot = NULL; + + for( i=0; i < _cfg->_num_blocks; i++ ) { + Block *b = _cfg->_blocks[i]; + + Node *head = b->head(); + + // If this block needs to start aligned (i.e, can be reached other + // than by falling-thru from the previous block), then force the + // start of a new bundle. + if( Pipeline::requires_bundling() && starts_bundle(head) ) + cb->flush_bundle(true); + + // Define the label at the beginning of the basic block + if( labels_not_set ) + MacroAssembler(cb).bind( blk_labels[b->_pre_order] ); + + else + assert( blk_labels[b->_pre_order].loc_pos() == cb->code_size(), + "label position does not match code offset" ); + + uint last_inst = b->_nodes.size(); + + // Emit block normally, except for last instruction. + // Emit means "dump code bits into code buffer". + for( uint j = 0; j_nodes[j]; + + // See if delay slots are supported + if (valid_bundle_info(n) && + node_bundling(n)->used_in_unconditional_delay()) { + assert(delay_slot == NULL, "no use of delay slot node"); + assert(n->size(_regalloc) == Pipeline::instr_unit_size(), "delay slot instruction wrong size"); + + delay_slot = n; + continue; + } + + // If this starts a new instruction group, then flush the current one + // (but allow split bundles) + if( Pipeline::requires_bundling() && starts_bundle(n) ) + cb->flush_bundle(false); + + // The following logic is duplicated in the code ifdeffed for + // ENABLE_ZAP_DEAD_LOCALS which apppears above in this file. It + // should be factored out. Or maybe dispersed to the nodes? + + // Special handling for SafePoint/Call Nodes + bool is_mcall = false; + if( n->is_Mach() ) { + MachNode *mach = n->as_Mach(); + is_mcall = n->is_MachCall(); + bool is_sfn = n->is_MachSafePoint(); + + // If this requires all previous instructions be flushed, then do so + if( is_sfn || is_mcall || mach->alignment_required() != 1) { + cb->flush_bundle(true); + current_offset = cb->code_size(); + } + + // align the instruction if necessary + int nop_size = nop->size(_regalloc); + int padding = mach->compute_padding(current_offset); + // Make sure safepoint node for polling is distinct from a call's + // return by adding a nop if needed. + if (is_sfn && !is_mcall && padding == 0 && current_offset == last_call_offset ) { + padding = nop_size; + } + assert( labels_not_set || padding == 0, "instruction should already be aligned") + + if(padding > 0) { + assert((padding % nop_size) == 0, "padding is not a multiple of NOP size"); + int nops_cnt = padding / nop_size; + MachNode *nop = new (this) MachNopNode(nops_cnt); + b->_nodes.insert(j++, nop); + last_inst++; + _cfg->_bbs.map( nop->_idx, b ); + nop->emit(*cb, _regalloc); + cb->flush_bundle(true); + current_offset = cb->code_size(); + } + + // Remember the start of the last call in a basic block + if (is_mcall) { + MachCallNode *mcall = mach->as_MachCall(); + + // This destination address is NOT PC-relative + mcall->method_set((intptr_t)mcall->entry_point()); + + // Save the return address + call_returns[b->_pre_order] = current_offset + mcall->ret_addr_offset(); + + if (!mcall->is_safepoint_node()) { + is_mcall = false; + is_sfn = false; + } + } + + // sfn will be valid whenever mcall is valid now because of inheritance + if( is_sfn || is_mcall ) { + + // Handle special safepoint nodes for synchronization + if( !is_mcall ) { + MachSafePointNode *sfn = mach->as_MachSafePoint(); + // !!!!! Stubs only need an oopmap right now, so bail out + if( sfn->jvms()->method() == NULL) { + // Write the oopmap directly to the code blob??!! +# ifdef ENABLE_ZAP_DEAD_LOCALS + assert( !is_node_getting_a_safepoint(sfn), "logic does not match; false positive"); +# endif + continue; + } + } // End synchronization + + non_safepoints.observe_safepoint(mach->as_MachSafePoint()->jvms(), + current_offset); + Process_OopMap_Node(mach, current_offset); + } // End if safepoint + + // If this is a null check, then add the start of the previous instruction to the list + else if( mach->is_MachNullCheck() ) { + inct_starts[inct_cnt++] = previous_offset; + } + + // If this is a branch, then fill in the label with the target BB's label + else if ( mach->is_Branch() ) { + + if ( mach->ideal_Opcode() == Op_Jump ) { + for (uint h = 0; h < b->_num_succs; h++ ) { + Block* succs_block = b->_succs[h]; + for (uint j = 1; j < succs_block->num_preds(); j++) { + Node* jpn = succs_block->pred(j); + if ( jpn->is_JumpProj() && jpn->in(0) == mach ) { + uint block_num = succs_block->non_connector()->_pre_order; + Label *blkLabel = &blk_labels[block_num]; + mach->add_case_label(jpn->as_JumpProj()->proj_no(), blkLabel); + } + } + } + } else { + // For Branchs + // This requires the TRUE branch target be in succs[0] + uint block_num = b->non_connector_successor(0)->_pre_order; + mach->label_set( blk_labels[block_num], block_num ); + } + } + +#ifdef ASSERT + // Check that oop-store preceeds the card-mark + else if( mach->ideal_Opcode() == Op_StoreCM ) { + uint storeCM_idx = j; + Node *oop_store = mach->in(mach->_cnt); // First precedence edge + assert( oop_store != NULL, "storeCM expects a precedence edge"); + uint i4; + for( i4 = 0; i4 < last_inst; ++i4 ) { + if( b->_nodes[i4] == oop_store ) break; + } + // Note: This test can provide a false failure if other precedence + // edges have been added to the storeCMNode. + assert( i4 == last_inst || i4 < storeCM_idx, "CM card-mark executes before oop-store"); + } +#endif + + else if( !n->is_Proj() ) { + // Remember the begining of the previous instruction, in case + // it's followed by a flag-kill and a null-check. Happens on + // Intel all the time, with add-to-memory kind of opcodes. + previous_offset = current_offset; + } + } + + // Verify that there is sufficient space remaining + cb->insts()->maybe_expand_to_ensure_remaining(MAX_inst_size); + if (cb->blob() == NULL) { + turn_off_compiler(this); + return; + } + + // Save the offset for the listing +#ifndef PRODUCT + if( node_offsets && n->_idx < node_offset_limit ) + node_offsets[n->_idx] = cb->code_size(); +#endif + + // "Normal" instruction case + n->emit(*cb, _regalloc); + current_offset = cb->code_size(); + non_safepoints.observe_instruction(n, current_offset); + + // mcall is last "call" that can be a safepoint + // record it so we can see if a poll will directly follow it + // in which case we'll need a pad to make the PcDesc sites unique + // see 5010568. This can be slightly inaccurate but conservative + // in the case that return address is not actually at current_offset. + // This is a small price to pay. + + if (is_mcall) { + last_call_offset = current_offset; + } + + // See if this instruction has a delay slot + if ( valid_bundle_info(n) && node_bundling(n)->use_unconditional_delay()) { + assert(delay_slot != NULL, "expecting delay slot node"); + + // Back up 1 instruction + cb->set_code_end( + cb->code_end()-Pipeline::instr_unit_size()); + + // Save the offset for the listing +#ifndef PRODUCT + if( node_offsets && delay_slot->_idx < node_offset_limit ) + node_offsets[delay_slot->_idx] = cb->code_size(); +#endif + + // Support a SafePoint in the delay slot + if( delay_slot->is_MachSafePoint() ) { + MachNode *mach = delay_slot->as_Mach(); + // !!!!! Stubs only need an oopmap right now, so bail out + if( !mach->is_MachCall() && mach->as_MachSafePoint()->jvms()->method() == NULL ) { + // Write the oopmap directly to the code blob??!! +# ifdef ENABLE_ZAP_DEAD_LOCALS + assert( !is_node_getting_a_safepoint(mach), "logic does not match; false positive"); +# endif + delay_slot = NULL; + continue; + } + + int adjusted_offset = current_offset - Pipeline::instr_unit_size(); + non_safepoints.observe_safepoint(mach->as_MachSafePoint()->jvms(), + adjusted_offset); + // Generate an OopMap entry + Process_OopMap_Node(mach, adjusted_offset); + } + + // Insert the delay slot instruction + delay_slot->emit(*cb, _regalloc); + + // Don't reuse it + delay_slot = NULL; + } + + } // End for all instructions in block + + // If the next block _starts_ a loop, pad this block out to align + // the loop start a little. Helps prevent pipe stalls at loop starts + int nop_size = (new (this) MachNopNode())->size(_regalloc); + if( i<_cfg->_num_blocks-1 ) { + Block *nb = _cfg->_blocks[i+1]; + uint padding = nb->alignment_padding(current_offset); + if( padding > 0 ) { + MachNode *nop = new (this) MachNopNode(padding / nop_size); + b->_nodes.insert( b->_nodes.size(), nop ); + _cfg->_bbs.map( nop->_idx, b ); + nop->emit(*cb, _regalloc); + current_offset = cb->code_size(); + } + } + + } // End of for all blocks + + non_safepoints.flush_at_end(); + + // Offset too large? + if (failing()) return; + + // Define a pseudo-label at the end of the code + MacroAssembler(cb).bind( blk_labels[_cfg->_num_blocks] ); + + // Compute the size of the first block + _first_block_size = blk_labels[1].loc_pos() - blk_labels[0].loc_pos(); + + assert(cb->code_size() < 500000, "method is unreasonably large"); + + // ------------------ + +#ifndef PRODUCT + // Information on the size of the method, without the extraneous code + Scheduling::increment_method_size(cb->code_size()); +#endif + + // ------------------ + // Fill in exception table entries. + FillExceptionTables(inct_cnt, call_returns, inct_starts, blk_labels); + + // Only java methods have exception handlers and deopt handlers + if (_method) { + // Emit the exception handler code. + _code_offsets.set_value(CodeOffsets::Exceptions, emit_exception_handler(*cb)); + // Emit the deopt handler code. + _code_offsets.set_value(CodeOffsets::Deopt, emit_deopt_handler(*cb)); + } + + // One last check for failed CodeBuffer::expand: + if (cb->blob() == NULL) { + turn_off_compiler(this); + return; + } + +#ifndef PRODUCT + // Dump the assembly code, including basic-block numbers + if (print_assembly()) { + ttyLocker ttyl; // keep the following output all in one block + if (!VMThread::should_terminate()) { // test this under the tty lock + // This output goes directly to the tty, not the compiler log. + // To enable tools to match it up with the compilation activity, + // be sure to tag this tty output with the compile ID. + if (xtty != NULL) { + xtty->head("opto_assembly compile_id='%d'%s", compile_id(), + is_osr_compilation() ? " compile_kind='osr'" : + ""); + } + if (method() != NULL) { + method()->print_oop(); + print_codes(); + } + dump_asm(node_offsets, node_offset_limit); + if (xtty != NULL) { + xtty->tail("opto_assembly"); + } + } + } +#endif + +} + +void Compile::FillExceptionTables(uint cnt, uint *call_returns, uint *inct_starts, Label *blk_labels) { + _inc_table.set_size(cnt); + + uint inct_cnt = 0; + for( uint i=0; i<_cfg->_num_blocks; i++ ) { + Block *b = _cfg->_blocks[i]; + Node *n = NULL; + int j; + + // Find the branch; ignore trailing NOPs. + for( j = b->_nodes.size()-1; j>=0; j-- ) { + n = b->_nodes[j]; + if( !n->is_Mach() || n->as_Mach()->ideal_Opcode() != Op_Con ) + break; + } + + // If we didn't find anything, continue + if( j < 0 ) continue; + + // Compute ExceptionHandlerTable subtable entry and add it + // (skip empty blocks) + if( n->is_Catch() ) { + + // Get the offset of the return from the call + uint call_return = call_returns[b->_pre_order]; +#ifdef ASSERT + assert( call_return > 0, "no call seen for this basic block" ); + while( b->_nodes[--j]->Opcode() == Op_MachProj ) ; + assert( b->_nodes[j]->is_Call(), "CatchProj must follow call" ); +#endif + // last instruction is a CatchNode, find it's CatchProjNodes + int nof_succs = b->_num_succs; + // allocate space + GrowableArray handler_bcis(nof_succs); + GrowableArray handler_pcos(nof_succs); + // iterate through all successors + for (int j = 0; j < nof_succs; j++) { + Block* s = b->_succs[j]; + bool found_p = false; + for( uint k = 1; k < s->num_preds(); k++ ) { + Node *pk = s->pred(k); + if( pk->is_CatchProj() && pk->in(0) == n ) { + const CatchProjNode* p = pk->as_CatchProj(); + found_p = true; + // add the corresponding handler bci & pco information + if( p->_con != CatchProjNode::fall_through_index ) { + // p leads to an exception handler (and is not fall through) + assert(s == _cfg->_blocks[s->_pre_order],"bad numbering"); + // no duplicates, please + if( !handler_bcis.contains(p->handler_bci()) ) { + uint block_num = s->non_connector()->_pre_order; + handler_bcis.append(p->handler_bci()); + handler_pcos.append(blk_labels[block_num].loc_pos()); + } + } + } + } + assert(found_p, "no matching predecessor found"); + // Note: Due to empty block removal, one block may have + // several CatchProj inputs, from the same Catch. + } + + // Set the offset of the return from the call + _handler_table.add_subtable(call_return, &handler_bcis, NULL, &handler_pcos); + continue; + } + + // Handle implicit null exception table updates + if( n->is_MachNullCheck() ) { + uint block_num = b->non_connector_successor(0)->_pre_order; + _inc_table.append( inct_starts[inct_cnt++], blk_labels[block_num].loc_pos() ); + continue; + } + } // End of for all blocks fill in exception table entries +} + +// Static Variables +#ifndef PRODUCT +uint Scheduling::_total_nop_size = 0; +uint Scheduling::_total_method_size = 0; +uint Scheduling::_total_branches = 0; +uint Scheduling::_total_unconditional_delays = 0; +uint Scheduling::_total_instructions_per_bundle[Pipeline::_max_instrs_per_cycle+1]; +#endif + +// Initializer for class Scheduling + +Scheduling::Scheduling(Arena *arena, Compile &compile) + : _arena(arena), + _cfg(compile.cfg()), + _bbs(compile.cfg()->_bbs), + _regalloc(compile.regalloc()), + _reg_node(arena), + _bundle_instr_count(0), + _bundle_cycle_number(0), + _scheduled(arena), + _available(arena), + _next_node(NULL), + _bundle_use(0, 0, resource_count, &_bundle_use_elements[0]), + _pinch_free_list(arena) +#ifndef PRODUCT + , _branches(0) + , _unconditional_delays(0) +#endif +{ + // Create a MachNopNode + _nop = new (&compile) MachNopNode(); + + // Now that the nops are in the array, save the count + // (but allow entries for the nops) + _node_bundling_limit = compile.unique(); + uint node_max = _regalloc->node_regs_max_index(); + + compile.set_node_bundling_limit(_node_bundling_limit); + + // This one is persistant within the Compile class + _node_bundling_base = NEW_ARENA_ARRAY(compile.comp_arena(), Bundle, node_max); + + // Allocate space for fixed-size arrays + _node_latency = NEW_ARENA_ARRAY(arena, unsigned short, node_max); + _uses = NEW_ARENA_ARRAY(arena, short, node_max); + _current_latency = NEW_ARENA_ARRAY(arena, unsigned short, node_max); + + // Clear the arrays + memset(_node_bundling_base, 0, node_max * sizeof(Bundle)); + memset(_node_latency, 0, node_max * sizeof(unsigned short)); + memset(_uses, 0, node_max * sizeof(short)); + memset(_current_latency, 0, node_max * sizeof(unsigned short)); + + // Clear the bundling information + memcpy(_bundle_use_elements, + Pipeline_Use::elaborated_elements, + sizeof(Pipeline_Use::elaborated_elements)); + + // Get the last node + Block *bb = _cfg->_blocks[_cfg->_blocks.size()-1]; + + _next_node = bb->_nodes[bb->_nodes.size()-1]; +} + +#ifndef PRODUCT +// Scheduling destructor +Scheduling::~Scheduling() { + _total_branches += _branches; + _total_unconditional_delays += _unconditional_delays; +} +#endif + +// Step ahead "i" cycles +void Scheduling::step(uint i) { + + Bundle *bundle = node_bundling(_next_node); + bundle->set_starts_bundle(); + + // Update the bundle record, but leave the flags information alone + if (_bundle_instr_count > 0) { + bundle->set_instr_count(_bundle_instr_count); + bundle->set_resources_used(_bundle_use.resourcesUsed()); + } + + // Update the state information + _bundle_instr_count = 0; + _bundle_cycle_number += i; + _bundle_use.step(i); +} + +void Scheduling::step_and_clear() { + Bundle *bundle = node_bundling(_next_node); + bundle->set_starts_bundle(); + + // Update the bundle record + if (_bundle_instr_count > 0) { + bundle->set_instr_count(_bundle_instr_count); + bundle->set_resources_used(_bundle_use.resourcesUsed()); + + _bundle_cycle_number += 1; + } + + // Clear the bundling information + _bundle_instr_count = 0; + _bundle_use.reset(); + + memcpy(_bundle_use_elements, + Pipeline_Use::elaborated_elements, + sizeof(Pipeline_Use::elaborated_elements)); +} + +//------------------------------ScheduleAndBundle------------------------------ +// Perform instruction scheduling and bundling over the sequence of +// instructions in backwards order. +void Compile::ScheduleAndBundle() { + + // Don't optimize this if it isn't a method + if (!_method) + return; + + // Don't optimize this if scheduling is disabled + if (!do_scheduling()) + return; + + NOT_PRODUCT( TracePhase t2("isched", &_t_instrSched, TimeCompiler); ) + + // Create a data structure for all the scheduling information + Scheduling scheduling(Thread::current()->resource_area(), *this); + + // Walk backwards over each basic block, computing the needed alignment + // Walk over all the basic blocks + scheduling.DoScheduling(); +} + +//------------------------------ComputeLocalLatenciesForward------------------- +// Compute the latency of all the instructions. This is fairly simple, +// because we already have a legal ordering. Walk over the instructions +// from first to last, and compute the latency of the instruction based +// on the latency of the preceeding instruction(s). +void Scheduling::ComputeLocalLatenciesForward(const Block *bb) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# -> ComputeLocalLatenciesForward\n"); +#endif + + // Walk over all the schedulable instructions + for( uint j=_bb_start; j < _bb_end; j++ ) { + + // This is a kludge, forcing all latency calculations to start at 1. + // Used to allow latency 0 to force an instruction to the beginning + // of the bb + uint latency = 1; + Node *use = bb->_nodes[j]; + uint nlen = use->len(); + + // Walk over all the inputs + for ( uint k=0; k < nlen; k++ ) { + Node *def = use->in(k); + if (!def) + continue; + + uint l = _node_latency[def->_idx] + use->latency(k); + if (latency < l) + latency = l; + } + + _node_latency[use->_idx] = latency; + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + tty->print("# latency %4d: ", latency); + use->dump(); + } +#endif + } + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# <- ComputeLocalLatenciesForward\n"); +#endif + +} // end ComputeLocalLatenciesForward + +// See if this node fits into the present instruction bundle +bool Scheduling::NodeFitsInBundle(Node *n) { + uint n_idx = n->_idx; + + // If this is the unconditional delay instruction, then it fits + if (n == _unconditional_delay_slot) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# NodeFitsInBundle [%4d]: TRUE; is in unconditional delay slot\n", n->_idx); +#endif + return (true); + } + + // If the node cannot be scheduled this cycle, skip it + if (_current_latency[n_idx] > _bundle_cycle_number) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# NodeFitsInBundle [%4d]: FALSE; latency %4d > %d\n", + n->_idx, _current_latency[n_idx], _bundle_cycle_number); +#endif + return (false); + } + + const Pipeline *node_pipeline = n->pipeline(); + + uint instruction_count = node_pipeline->instructionCount(); + if (node_pipeline->mayHaveNoCode() && n->size(_regalloc) == 0) + instruction_count = 0; + else if (node_pipeline->hasBranchDelay() && !_unconditional_delay_slot) + instruction_count++; + + if (_bundle_instr_count + instruction_count > Pipeline::_max_instrs_per_cycle) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# NodeFitsInBundle [%4d]: FALSE; too many instructions: %d > %d\n", + n->_idx, _bundle_instr_count + instruction_count, Pipeline::_max_instrs_per_cycle); +#endif + return (false); + } + + // Don't allow non-machine nodes to be handled this way + if (!n->is_Mach() && instruction_count == 0) + return (false); + + // See if there is any overlap + uint delay = _bundle_use.full_latency(0, node_pipeline->resourceUse()); + + if (delay > 0) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# NodeFitsInBundle [%4d]: FALSE; functional units overlap\n", n_idx); +#endif + return false; + } + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# NodeFitsInBundle [%4d]: TRUE\n", n_idx); +#endif + + return true; +} + +Node * Scheduling::ChooseNodeToBundle() { + uint siz = _available.size(); + + if (siz == 0) { + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# ChooseNodeToBundle: NULL\n"); +#endif + return (NULL); + } + + // Fast path, if only 1 instruction in the bundle + if (siz == 1) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + tty->print("# ChooseNodeToBundle (only 1): "); + _available[0]->dump(); + } +#endif + return (_available[0]); + } + + // Don't bother, if the bundle is already full + if (_bundle_instr_count < Pipeline::_max_instrs_per_cycle) { + for ( uint i = 0; i < siz; i++ ) { + Node *n = _available[i]; + + // Skip projections, we'll handle them another way + if (n->is_Proj()) + continue; + + // This presupposed that instructions are inserted into the + // available list in a legality order; i.e. instructions that + // must be inserted first are at the head of the list + if (NodeFitsInBundle(n)) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + tty->print("# ChooseNodeToBundle: "); + n->dump(); + } +#endif + return (n); + } + } + } + + // Nothing fits in this bundle, choose the highest priority +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + tty->print("# ChooseNodeToBundle: "); + _available[0]->dump(); + } +#endif + + return _available[0]; +} + +//------------------------------AddNodeToAvailableList------------------------- +void Scheduling::AddNodeToAvailableList(Node *n) { + assert( !n->is_Proj(), "projections never directly made available" ); +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + tty->print("# AddNodeToAvailableList: "); + n->dump(); + } +#endif + + int latency = _current_latency[n->_idx]; + + // Insert in latency order (insertion sort) + uint i; + for ( i=0; i < _available.size(); i++ ) + if (_current_latency[_available[i]->_idx] > latency) + break; + + // Special Check for compares following branches + if( n->is_Mach() && _scheduled.size() > 0 ) { + int op = n->as_Mach()->ideal_Opcode(); + Node *last = _scheduled[0]; + if( last->is_MachIf() && last->in(1) == n && + ( op == Op_CmpI || + op == Op_CmpU || + op == Op_CmpP || + op == Op_CmpF || + op == Op_CmpD || + op == Op_CmpL ) ) { + + // Recalculate position, moving to front of same latency + for ( i=0 ; i < _available.size(); i++ ) + if (_current_latency[_available[i]->_idx] >= latency) + break; + } + } + + // Insert the node in the available list + _available.insert(i, n); + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + dump_available(); +#endif +} + +//------------------------------DecrementUseCounts----------------------------- +void Scheduling::DecrementUseCounts(Node *n, const Block *bb) { + for ( uint i=0; i < n->len(); i++ ) { + Node *def = n->in(i); + if (!def) continue; + if( def->is_Proj() ) // If this is a machine projection, then + def = def->in(0); // propagate usage thru to the base instruction + + if( _bbs[def->_idx] != bb ) // Ignore if not block-local + continue; + + // Compute the latency + uint l = _bundle_cycle_number + n->latency(i); + if (_current_latency[def->_idx] < l) + _current_latency[def->_idx] = l; + + // If this does not have uses then schedule it + if ((--_uses[def->_idx]) == 0) + AddNodeToAvailableList(def); + } +} + +//------------------------------AddNodeToBundle-------------------------------- +void Scheduling::AddNodeToBundle(Node *n, const Block *bb) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + tty->print("# AddNodeToBundle: "); + n->dump(); + } +#endif + + // Remove this from the available list + uint i; + for (i = 0; i < _available.size(); i++) + if (_available[i] == n) + break; + assert(i < _available.size(), "entry in _available list not found"); + _available.remove(i); + + // See if this fits in the current bundle + const Pipeline *node_pipeline = n->pipeline(); + const Pipeline_Use& node_usage = node_pipeline->resourceUse(); + + // Check for instructions to be placed in the delay slot. We + // do this before we actually schedule the current instruction, + // because the delay slot follows the current instruction. + if (Pipeline::_branch_has_delay_slot && + node_pipeline->hasBranchDelay() && + !_unconditional_delay_slot) { + + uint siz = _available.size(); + + // Conditional branches can support an instruction that + // is unconditionally executed and not dependant by the + // branch, OR a conditionally executed instruction if + // the branch is taken. In practice, this means that + // the first instruction at the branch target is + // copied to the delay slot, and the branch goes to + // the instruction after that at the branch target + if ( n->is_Mach() && n->is_Branch() ) { + + assert( !n->is_MachNullCheck(), "should not look for delay slot for Null Check" ); + assert( !n->is_Catch(), "should not look for delay slot for Catch" ); + +#ifndef PRODUCT + _branches++; +#endif + + // At least 1 instruction is on the available list + // that is not dependant on the branch + for (uint i = 0; i < siz; i++) { + Node *d = _available[i]; + const Pipeline *avail_pipeline = d->pipeline(); + + // Don't allow safepoints in the branch shadow, that will + // cause a number of difficulties + if ( avail_pipeline->instructionCount() == 1 && + !avail_pipeline->hasMultipleBundles() && + !avail_pipeline->hasBranchDelay() && + Pipeline::instr_has_unit_size() && + d->size(_regalloc) == Pipeline::instr_unit_size() && + NodeFitsInBundle(d) && + !node_bundling(d)->used_in_delay()) { + + if (d->is_Mach() && !d->is_MachSafePoint()) { + // A node that fits in the delay slot was found, so we need to + // set the appropriate bits in the bundle pipeline information so + // that it correctly indicates resource usage. Later, when we + // attempt to add this instruction to the bundle, we will skip + // setting the resource usage. + _unconditional_delay_slot = d; + node_bundling(n)->set_use_unconditional_delay(); + node_bundling(d)->set_used_in_unconditional_delay(); + _bundle_use.add_usage(avail_pipeline->resourceUse()); + _current_latency[d->_idx] = _bundle_cycle_number; + _next_node = d; + ++_bundle_instr_count; +#ifndef PRODUCT + _unconditional_delays++; +#endif + break; + } + } + } + } + + // No delay slot, add a nop to the usage + if (!_unconditional_delay_slot) { + // See if adding an instruction in the delay slot will overflow + // the bundle. + if (!NodeFitsInBundle(_nop)) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# *** STEP(1 instruction for delay slot) ***\n"); +#endif + step(1); + } + + _bundle_use.add_usage(_nop->pipeline()->resourceUse()); + _next_node = _nop; + ++_bundle_instr_count; + } + + // See if the instruction in the delay slot requires a + // step of the bundles + if (!NodeFitsInBundle(n)) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# *** STEP(branch won't fit) ***\n"); +#endif + // Update the state information + _bundle_instr_count = 0; + _bundle_cycle_number += 1; + _bundle_use.step(1); + } + } + + // Get the number of instructions + uint instruction_count = node_pipeline->instructionCount(); + if (node_pipeline->mayHaveNoCode() && n->size(_regalloc) == 0) + instruction_count = 0; + + // Compute the latency information + uint delay = 0; + + if (instruction_count > 0 || !node_pipeline->mayHaveNoCode()) { + int relative_latency = _current_latency[n->_idx] - _bundle_cycle_number; + if (relative_latency < 0) + relative_latency = 0; + + delay = _bundle_use.full_latency(relative_latency, node_usage); + + // Does not fit in this bundle, start a new one + if (delay > 0) { + step(delay); + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# *** STEP(%d) ***\n", delay); +#endif + } + } + + // If this was placed in the delay slot, ignore it + if (n != _unconditional_delay_slot) { + + if (delay == 0) { + if (node_pipeline->hasMultipleBundles()) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# *** STEP(multiple instructions) ***\n"); +#endif + step(1); + } + + else if (instruction_count + _bundle_instr_count > Pipeline::_max_instrs_per_cycle) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# *** STEP(%d >= %d instructions) ***\n", + instruction_count + _bundle_instr_count, + Pipeline::_max_instrs_per_cycle); +#endif + step(1); + } + } + + if (node_pipeline->hasBranchDelay() && !_unconditional_delay_slot) + _bundle_instr_count++; + + // Set the node's latency + _current_latency[n->_idx] = _bundle_cycle_number; + + // Now merge the functional unit information + if (instruction_count > 0 || !node_pipeline->mayHaveNoCode()) + _bundle_use.add_usage(node_usage); + + // Increment the number of instructions in this bundle + _bundle_instr_count += instruction_count; + + // Remember this node for later + if (n->is_Mach()) + _next_node = n; + } + + // It's possible to have a BoxLock in the graph and in the _bbs mapping but + // not in the bb->_nodes array. This happens for debug-info-only BoxLocks. + // 'Schedule' them (basically ignore in the schedule) but do not insert them + // into the block. All other scheduled nodes get put in the schedule here. + int op = n->Opcode(); + if( (op == Op_Node && n->req() == 0) || // anti-dependence node OR + (op != Op_Node && // Not an unused antidepedence node and + // not an unallocated boxlock + (OptoReg::is_valid(_regalloc->get_reg_first(n)) || op != Op_BoxLock)) ) { + + // Push any trailing projections + if( bb->_nodes[bb->_nodes.size()-1] != n ) { + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node *foi = n->fast_out(i); + if( foi->is_Proj() ) + _scheduled.push(foi); + } + } + + // Put the instruction in the schedule list + _scheduled.push(n); + } + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + dump_available(); +#endif + + // Walk all the definitions, decrementing use counts, and + // if a definition has a 0 use count, place it in the available list. + DecrementUseCounts(n,bb); +} + +//------------------------------ComputeUseCount-------------------------------- +// This method sets the use count within a basic block. We will ignore all +// uses outside the current basic block. As we are doing a backwards walk, +// any node we reach that has a use count of 0 may be scheduled. This also +// avoids the problem of cyclic references from phi nodes, as long as phi +// nodes are at the front of the basic block. This method also initializes +// the available list to the set of instructions that have no uses within this +// basic block. +void Scheduling::ComputeUseCount(const Block *bb) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# -> ComputeUseCount\n"); +#endif + + // Clear the list of available and scheduled instructions, just in case + _available.clear(); + _scheduled.clear(); + + // No delay slot specified + _unconditional_delay_slot = NULL; + +#ifdef ASSERT + for( uint i=0; i < bb->_nodes.size(); i++ ) + assert( _uses[bb->_nodes[i]->_idx] == 0, "_use array not clean" ); +#endif + + // Force the _uses count to never go to zero for unscheduable pieces + // of the block + for( uint k = 0; k < _bb_start; k++ ) + _uses[bb->_nodes[k]->_idx] = 1; + for( uint l = _bb_end; l < bb->_nodes.size(); l++ ) + _uses[bb->_nodes[l]->_idx] = 1; + + // Iterate backwards over the instructions in the block. Don't count the + // branch projections at end or the block header instructions. + for( uint j = _bb_end-1; j >= _bb_start; j-- ) { + Node *n = bb->_nodes[j]; + if( n->is_Proj() ) continue; // Projections handled another way + + // Account for all uses + for ( uint k = 0; k < n->len(); k++ ) { + Node *inp = n->in(k); + if (!inp) continue; + assert(inp != n, "no cycles allowed" ); + if( _bbs[inp->_idx] == bb ) { // Block-local use? + if( inp->is_Proj() ) // Skip through Proj's + inp = inp->in(0); + ++_uses[inp->_idx]; // Count 1 block-local use + } + } + + // If this instruction has a 0 use count, then it is available + if (!_uses[n->_idx]) { + _current_latency[n->_idx] = _bundle_cycle_number; + AddNodeToAvailableList(n); + } + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + tty->print("# uses: %3d: ", _uses[n->_idx]); + n->dump(); + } +#endif + } + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# <- ComputeUseCount\n"); +#endif +} + +// This routine performs scheduling on each basic block in reverse order, +// using instruction latencies and taking into account function unit +// availability. +void Scheduling::DoScheduling() { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# -> DoScheduling\n"); +#endif + + Block *succ_bb = NULL; + Block *bb; + + // Walk over all the basic blocks in reverse order + for( int i=_cfg->_num_blocks-1; i >= 0; succ_bb = bb, i-- ) { + bb = _cfg->_blocks[i]; + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + tty->print("# Schedule BB#%03d (initial)\n", i); + for (uint j = 0; j < bb->_nodes.size(); j++) + bb->_nodes[j]->dump(); + } +#endif + + // On the head node, skip processing + if( bb == _cfg->_broot ) + continue; + + // Skip empty, connector blocks + if (bb->is_connector()) + continue; + + // If the following block is not the sole successor of + // this one, then reset the pipeline information + if (bb->_num_succs != 1 || bb->non_connector_successor(0) != succ_bb) { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + tty->print("*** bundle start of next BB, node %d, for %d instructions\n", + _next_node->_idx, _bundle_instr_count); + } +#endif + step_and_clear(); + } + + // Leave untouched the starting instruction, any Phis, a CreateEx node + // or Top. bb->_nodes[_bb_start] is the first schedulable instruction. + _bb_end = bb->_nodes.size()-1; + for( _bb_start=1; _bb_start <= _bb_end; _bb_start++ ) { + Node *n = bb->_nodes[_bb_start]; + // Things not matched, like Phinodes and ProjNodes don't get scheduled. + // Also, MachIdealNodes do not get scheduled + if( !n->is_Mach() ) continue; // Skip non-machine nodes + MachNode *mach = n->as_Mach(); + int iop = mach->ideal_Opcode(); + if( iop == Op_CreateEx ) continue; // CreateEx is pinned + if( iop == Op_Con ) continue; // Do not schedule Top + if( iop == Op_Node && // Do not schedule PhiNodes, ProjNodes + mach->pipeline() == MachNode::pipeline_class() && + !n->is_SpillCopy() ) // Breakpoints, Prolog, etc + continue; + break; // Funny loop structure to be sure... + } + // Compute last "interesting" instruction in block - last instruction we + // might schedule. _bb_end points just after last schedulable inst. We + // normally schedule conditional branches (despite them being forced last + // in the block), because they have delay slots we can fill. Calls all + // have their delay slots filled in the template expansions, so we don't + // bother scheduling them. + Node *last = bb->_nodes[_bb_end]; + if( last->is_Catch() || + (last->is_Mach() && last->as_Mach()->ideal_Opcode() == Op_Halt) ) { + // There must be a prior call. Skip it. + while( !bb->_nodes[--_bb_end]->is_Call() ) { + assert( bb->_nodes[_bb_end]->is_Proj(), "skipping projections after expected call" ); + } + } else if( last->is_MachNullCheck() ) { + // Backup so the last null-checked memory instruction is + // outside the schedulable range. Skip over the nullcheck, + // projection, and the memory nodes. + Node *mem = last->in(1); + do { + _bb_end--; + } while (mem != bb->_nodes[_bb_end]); + } else { + // Set _bb_end to point after last schedulable inst. + _bb_end++; + } + + assert( _bb_start <= _bb_end, "inverted block ends" ); + + // Compute the register antidependencies for the basic block + ComputeRegisterAntidependencies(bb); + if (_cfg->C->failing()) return; // too many D-U pinch points + + // Compute intra-bb latencies for the nodes + ComputeLocalLatenciesForward(bb); + + // Compute the usage within the block, and set the list of all nodes + // in the block that have no uses within the block. + ComputeUseCount(bb); + + // Schedule the remaining instructions in the block + while ( _available.size() > 0 ) { + Node *n = ChooseNodeToBundle(); + AddNodeToBundle(n,bb); + } + + assert( _scheduled.size() == _bb_end - _bb_start, "wrong number of instructions" ); +#ifdef ASSERT + for( uint l = _bb_start; l < _bb_end; l++ ) { + Node *n = bb->_nodes[l]; + uint m; + for( m = 0; m < _bb_end-_bb_start; m++ ) + if( _scheduled[m] == n ) + break; + assert( m < _bb_end-_bb_start, "instruction missing in schedule" ); + } +#endif + + // Now copy the instructions (in reverse order) back to the block + for ( uint k = _bb_start; k < _bb_end; k++ ) + bb->_nodes.map(k, _scheduled[_bb_end-k-1]); + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + tty->print("# Schedule BB#%03d (final)\n", i); + uint current = 0; + for (uint j = 0; j < bb->_nodes.size(); j++) { + Node *n = bb->_nodes[j]; + if( valid_bundle_info(n) ) { + Bundle *bundle = node_bundling(n); + if (bundle->instr_count() > 0 || bundle->flags() > 0) { + tty->print("*** Bundle: "); + bundle->dump(); + } + n->dump(); + } + } + } +#endif +#ifdef ASSERT + verify_good_schedule(bb,"after block local scheduling"); +#endif + } + +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) + tty->print("# <- DoScheduling\n"); +#endif + + // Record final node-bundling array location + _regalloc->C->set_node_bundling_base(_node_bundling_base); + +} // end DoScheduling + +//------------------------------verify_good_schedule--------------------------- +// Verify that no live-range used in the block is killed in the block by a +// wrong DEF. This doesn't verify live-ranges that span blocks. + +// Check for edge existence. Used to avoid adding redundant precedence edges. +static bool edge_from_to( Node *from, Node *to ) { + for( uint i=0; ilen(); i++ ) + if( from->in(i) == to ) + return true; + return false; +} + +#ifdef ASSERT +//------------------------------verify_do_def---------------------------------- +void Scheduling::verify_do_def( Node *n, OptoReg::Name def, const char *msg ) { + // Check for bad kills + if( OptoReg::is_valid(def) ) { // Ignore stores & control flow + Node *prior_use = _reg_node[def]; + if( prior_use && !edge_from_to(prior_use,n) ) { + tty->print("%s = ",OptoReg::as_VMReg(def)->name()); + n->dump(); + tty->print_cr("..."); + prior_use->dump(); + assert_msg(edge_from_to(prior_use,n),msg); + } + _reg_node.map(def,NULL); // Kill live USEs + } +} + +//------------------------------verify_good_schedule--------------------------- +void Scheduling::verify_good_schedule( Block *b, const char *msg ) { + + // Zap to something reasonable for the verify code + _reg_node.clear(); + + // Walk over the block backwards. Check to make sure each DEF doesn't + // kill a live value (other than the one it's supposed to). Add each + // USE to the live set. + for( uint i = b->_nodes.size()-1; i >= _bb_start; i-- ) { + Node *n = b->_nodes[i]; + int n_op = n->Opcode(); + if( n_op == Op_MachProj && n->ideal_reg() == MachProjNode::fat_proj ) { + // Fat-proj kills a slew of registers + RegMask rm = n->out_RegMask();// Make local copy + while( rm.is_NotEmpty() ) { + OptoReg::Name kill = rm.find_first_elem(); + rm.Remove(kill); + verify_do_def( n, kill, msg ); + } + } else if( n_op != Op_Node ) { // Avoid brand new antidependence nodes + // Get DEF'd registers the normal way + verify_do_def( n, _regalloc->get_reg_first(n), msg ); + verify_do_def( n, _regalloc->get_reg_second(n), msg ); + } + + // Now make all USEs live + for( uint i=1; ireq(); i++ ) { + Node *def = n->in(i); + assert(def != 0, "input edge required"); + OptoReg::Name reg_lo = _regalloc->get_reg_first(def); + OptoReg::Name reg_hi = _regalloc->get_reg_second(def); + if( OptoReg::is_valid(reg_lo) ) { + assert_msg(!_reg_node[reg_lo] || edge_from_to(_reg_node[reg_lo],def), msg ); + _reg_node.map(reg_lo,n); + } + if( OptoReg::is_valid(reg_hi) ) { + assert_msg(!_reg_node[reg_hi] || edge_from_to(_reg_node[reg_hi],def), msg ); + _reg_node.map(reg_hi,n); + } + } + + } + + // Zap to something reasonable for the Antidependence code + _reg_node.clear(); +} +#endif + +// Conditionally add precedence edges. Avoid putting edges on Projs. +static void add_prec_edge_from_to( Node *from, Node *to ) { + if( from->is_Proj() ) { // Put precedence edge on Proj's input + assert( from->req() == 1 && (from->len() == 1 || from->in(1)==0), "no precedence edges on projections" ); + from = from->in(0); + } + if( from != to && // No cycles (for things like LD L0,[L0+4] ) + !edge_from_to( from, to ) ) // Avoid duplicate edge + from->add_prec(to); +} + +//------------------------------anti_do_def------------------------------------ +void Scheduling::anti_do_def( Block *b, Node *def, OptoReg::Name def_reg, int is_def ) { + if( !OptoReg::is_valid(def_reg) ) // Ignore stores & control flow + return; + + Node *pinch = _reg_node[def_reg]; // Get pinch point + if( !pinch || _bbs[pinch->_idx] != b || // No pinch-point yet? + is_def ) { // Check for a true def (not a kill) + _reg_node.map(def_reg,def); // Record def/kill as the optimistic pinch-point + return; + } + + Node *kill = def; // Rename 'def' to more descriptive 'kill' + debug_only( def = (Node*)0xdeadbeef; ) + + // After some number of kills there _may_ be a later def + Node *later_def = NULL; + + // Finding a kill requires a real pinch-point. + // Check for not already having a pinch-point. + // Pinch points are Op_Node's. + if( pinch->Opcode() != Op_Node ) { // Or later-def/kill as pinch-point? + later_def = pinch; // Must be def/kill as optimistic pinch-point + if ( _pinch_free_list.size() > 0) { + pinch = _pinch_free_list.pop(); + } else { + pinch = new (_cfg->C, 1) Node(1); // Pinch point to-be + } + if (pinch->_idx >= _regalloc->node_regs_max_index()) { + _cfg->C->record_method_not_compilable("too many D-U pinch points"); + return; + } + _bbs.map(pinch->_idx,b); // Pretend it's valid in this block (lazy init) + _reg_node.map(def_reg,pinch); // Record pinch-point + //_regalloc->set_bad(pinch->_idx); // Already initialized this way. + if( later_def->outcnt() == 0 || later_def->ideal_reg() == MachProjNode::fat_proj ) { // Distinguish def from kill + pinch->init_req(0, _cfg->C->top()); // set not NULL for the next call + add_prec_edge_from_to(later_def,pinch); // Add edge from kill to pinch + later_def = NULL; // and no later def + } + pinch->set_req(0,later_def); // Hook later def so we can find it + } else { // Else have valid pinch point + if( pinch->in(0) ) // If there is a later-def + later_def = pinch->in(0); // Get it + } + + // Add output-dependence edge from later def to kill + if( later_def ) // If there is some original def + add_prec_edge_from_to(later_def,kill); // Add edge from def to kill + + // See if current kill is also a use, and so is forced to be the pinch-point. + if( pinch->Opcode() == Op_Node ) { + Node *uses = kill->is_Proj() ? kill->in(0) : kill; + for( uint i=1; ireq(); i++ ) { + if( _regalloc->get_reg_first(uses->in(i)) == def_reg || + _regalloc->get_reg_second(uses->in(i)) == def_reg ) { + // Yes, found a use/kill pinch-point + pinch->set_req(0,NULL); // + pinch->replace_by(kill); // Move anti-dep edges up + pinch = kill; + _reg_node.map(def_reg,pinch); + return; + } + } + } + + // Add edge from kill to pinch-point + add_prec_edge_from_to(kill,pinch); +} + +//------------------------------anti_do_use------------------------------------ +void Scheduling::anti_do_use( Block *b, Node *use, OptoReg::Name use_reg ) { + if( !OptoReg::is_valid(use_reg) ) // Ignore stores & control flow + return; + Node *pinch = _reg_node[use_reg]; // Get pinch point + // Check for no later def_reg/kill in block + if( pinch && _bbs[pinch->_idx] == b && + // Use has to be block-local as well + _bbs[use->_idx] == b ) { + if( pinch->Opcode() == Op_Node && // Real pinch-point (not optimistic?) + pinch->req() == 1 ) { // pinch not yet in block? + pinch->del_req(0); // yank pointer to later-def, also set flag + // Insert the pinch-point in the block just after the last use + b->_nodes.insert(b->find_node(use)+1,pinch); + _bb_end++; // Increase size scheduled region in block + } + + add_prec_edge_from_to(pinch,use); + } +} + +//------------------------------ComputeRegisterAntidependences----------------- +// We insert antidependences between the reads and following write of +// allocated registers to prevent illegal code motion. Hopefully, the +// number of added references should be fairly small, especially as we +// are only adding references within the current basic block. +void Scheduling::ComputeRegisterAntidependencies(Block *b) { + +#ifdef ASSERT + verify_good_schedule(b,"before block local scheduling"); +#endif + + // A valid schedule, for each register independently, is an endless cycle + // of: a def, then some uses (connected to the def by true dependencies), + // then some kills (defs with no uses), finally the cycle repeats with a new + // def. The uses are allowed to float relative to each other, as are the + // kills. No use is allowed to slide past a kill (or def). This requires + // antidependencies between all uses of a single def and all kills that + // follow, up to the next def. More edges are redundant, because later defs + // & kills are already serialized with true or antidependencies. To keep + // the edge count down, we add a 'pinch point' node if there's more than + // one use or more than one kill/def. + + // We add dependencies in one bottom-up pass. + + // For each instruction we handle it's DEFs/KILLs, then it's USEs. + + // For each DEF/KILL, we check to see if there's a prior DEF/KILL for this + // register. If not, we record the DEF/KILL in _reg_node, the + // register-to-def mapping. If there is a prior DEF/KILL, we insert a + // "pinch point", a new Node that's in the graph but not in the block. + // We put edges from the prior and current DEF/KILLs to the pinch point. + // We put the pinch point in _reg_node. If there's already a pinch point + // we merely add an edge from the current DEF/KILL to the pinch point. + + // After doing the DEF/KILLs, we handle USEs. For each used register, we + // put an edge from the pinch point to the USE. + + // To be expedient, the _reg_node array is pre-allocated for the whole + // compilation. _reg_node is lazily initialized; it either contains a NULL, + // or a valid def/kill/pinch-point, or a leftover node from some prior + // block. Leftover node from some prior block is treated like a NULL (no + // prior def, so no anti-dependence needed). Valid def is distinguished by + // it being in the current block. + bool fat_proj_seen = false; + uint last_safept = _bb_end-1; + Node* end_node = (_bb_end-1 >= _bb_start) ? b->_nodes[last_safept] : NULL; + Node* last_safept_node = end_node; + for( uint i = _bb_end-1; i >= _bb_start; i-- ) { + Node *n = b->_nodes[i]; + int is_def = n->outcnt(); // def if some uses prior to adding precedence edges + if( n->Opcode() == Op_MachProj && n->ideal_reg() == MachProjNode::fat_proj ) { + // Fat-proj kills a slew of registers + // This can add edges to 'n' and obscure whether or not it was a def, + // hence the is_def flag. + fat_proj_seen = true; + RegMask rm = n->out_RegMask();// Make local copy + while( rm.is_NotEmpty() ) { + OptoReg::Name kill = rm.find_first_elem(); + rm.Remove(kill); + anti_do_def( b, n, kill, is_def ); + } + } else { + // Get DEF'd registers the normal way + anti_do_def( b, n, _regalloc->get_reg_first(n), is_def ); + anti_do_def( b, n, _regalloc->get_reg_second(n), is_def ); + } + + // Check each register used by this instruction for a following DEF/KILL + // that must occur afterward and requires an anti-dependence edge. + for( uint j=0; jreq(); j++ ) { + Node *def = n->in(j); + if( def ) { + assert( def->Opcode() != Op_MachProj || def->ideal_reg() != MachProjNode::fat_proj, "" ); + anti_do_use( b, n, _regalloc->get_reg_first(def) ); + anti_do_use( b, n, _regalloc->get_reg_second(def) ); + } + } + // Do not allow defs of new derived values to float above GC + // points unless the base is definitely available at the GC point. + + Node *m = b->_nodes[i]; + + // Add precedence edge from following safepoint to use of derived pointer + if( last_safept_node != end_node && + m != last_safept_node) { + for (uint k = 1; k < m->req(); k++) { + const Type *t = m->in(k)->bottom_type(); + if( t->isa_oop_ptr() && + t->is_ptr()->offset() != 0 ) { + last_safept_node->add_prec( m ); + break; + } + } + } + + if( n->jvms() ) { // Precedence edge from derived to safept + // Check if last_safept_node was moved by pinch-point insertion in anti_do_use() + if( b->_nodes[last_safept] != last_safept_node ) { + last_safept = b->find_node(last_safept_node); + } + for( uint j=last_safept; j > i; j-- ) { + Node *mach = b->_nodes[j]; + if( mach->is_Mach() && mach->as_Mach()->ideal_Opcode() == Op_AddP ) + mach->add_prec( n ); + } + last_safept = i; + last_safept_node = m; + } + } + + if (fat_proj_seen) { + // Garbage collect pinch nodes that were not consumed. + // They are usually created by a fat kill MachProj for a call. + garbage_collect_pinch_nodes(); + } +} + +//------------------------------garbage_collect_pinch_nodes------------------------------- + +// Garbage collect pinch nodes for reuse by other blocks. +// +// The block scheduler's insertion of anti-dependence +// edges creates many pinch nodes when the block contains +// 2 or more Calls. A pinch node is used to prevent a +// combinatorial explosion of edges. If a set of kills for a +// register is anti-dependent on a set of uses (or defs), rather +// than adding an edge in the graph between each pair of kill +// and use (or def), a pinch is inserted between them: +// +// use1 use2 use3 +// \ | / +// \ | / +// pinch +// / | \ +// / | \ +// kill1 kill2 kill3 +// +// One pinch node is created per register killed when +// the second call is encountered during a backwards pass +// over the block. Most of these pinch nodes are never +// wired into the graph because the register is never +// used or def'ed in the block. +// +void Scheduling::garbage_collect_pinch_nodes() { +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) tty->print("Reclaimed pinch nodes:"); +#endif + int trace_cnt = 0; + for (uint k = 0; k < _reg_node.Size(); k++) { + Node* pinch = _reg_node[k]; + if (pinch != NULL && pinch->Opcode() == Op_Node && + // no predecence input edges + (pinch->req() == pinch->len() || pinch->in(pinch->req()) == NULL) ) { + cleanup_pinch(pinch); + _pinch_free_list.push(pinch); + _reg_node.map(k, NULL); +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) { + trace_cnt++; + if (trace_cnt > 40) { + tty->print("\n"); + trace_cnt = 0; + } + tty->print(" %d", pinch->_idx); + } +#endif + } + } +#ifndef PRODUCT + if (_cfg->C->trace_opto_output()) tty->print("\n"); +#endif +} + +// Clean up a pinch node for reuse. +void Scheduling::cleanup_pinch( Node *pinch ) { + assert (pinch && pinch->Opcode() == Op_Node && pinch->req() == 1, "just checking"); + + for (DUIterator_Last imin, i = pinch->last_outs(imin); i >= imin; ) { + Node* use = pinch->last_out(i); + uint uses_found = 0; + for (uint j = use->req(); j < use->len(); j++) { + if (use->in(j) == pinch) { + use->rm_prec(j); + uses_found++; + } + } + assert(uses_found > 0, "must be a precedence edge"); + i -= uses_found; // we deleted 1 or more copies of this edge + } + // May have a later_def entry + pinch->set_req(0, NULL); +} + +//------------------------------print_statistics------------------------------- +#ifndef PRODUCT + +void Scheduling::dump_available() const { + tty->print("#Availist "); + for (uint i = 0; i < _available.size(); i++) + tty->print(" N%d/l%d", _available[i]->_idx,_current_latency[_available[i]->_idx]); + tty->cr(); +} + +// Print Scheduling Statistics +void Scheduling::print_statistics() { + // Print the size added by nops for bundling + tty->print("Nops added %d bytes to total of %d bytes", + _total_nop_size, _total_method_size); + if (_total_method_size > 0) + tty->print(", for %.2f%%", + ((double)_total_nop_size) / ((double) _total_method_size) * 100.0); + tty->print("\n"); + + // Print the number of branch shadows filled + if (Pipeline::_branch_has_delay_slot) { + tty->print("Of %d branches, %d had unconditional delay slots filled", + _total_branches, _total_unconditional_delays); + if (_total_branches > 0) + tty->print(", for %.2f%%", + ((double)_total_unconditional_delays) / ((double)_total_branches) * 100.0); + tty->print("\n"); + } + + uint total_instructions = 0, total_bundles = 0; + + for (uint i = 1; i <= Pipeline::_max_instrs_per_cycle; i++) { + uint bundle_count = _total_instructions_per_bundle[i]; + total_instructions += bundle_count * i; + total_bundles += bundle_count; + } + + if (total_bundles > 0) + tty->print("Average ILP (excluding nops) is %.2f\n", + ((double)total_instructions) / ((double)total_bundles)); +} +#endif diff --git a/hotspot/src/share/vm/opto/output.hpp b/hotspot/src/share/vm/opto/output.hpp new file mode 100644 index 00000000000..386e2be165b --- /dev/null +++ b/hotspot/src/share/vm/opto/output.hpp @@ -0,0 +1,215 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Arena; +class Bundle; +class Block; +class Block_Array; +class Node; +class Node_Array; +class Node_List; +class PhaseCFG; +class PhaseChaitin; +class Pipeline_Use_Element; +class Pipeline_Use; + +#ifndef PRODUCT +#define DEBUG_ARG(x) , x +#else +#define DEBUG_ARG(x) +#endif + +// Define the initial sizes for allocation of the resizable code buffer +enum { + initial_code_capacity = 16 * 1024, + initial_stub_capacity = 4 * 1024, + initial_const_capacity = 4 * 1024, + initial_locs_capacity = 3 * 1024 +}; + +//------------------------------Scheduling---------------------------------- +// This class contains all the information necessary to implement instruction +// scheduling and bundling. +class Scheduling { + +private: + // Arena to use + Arena *_arena; + + // Control-Flow Graph info + PhaseCFG *_cfg; + + // Register Allocation info + PhaseRegAlloc *_regalloc; + + // Number of nodes in the method + uint _node_bundling_limit; + + // List of scheduled nodes. Generated in reverse order + Node_List _scheduled; + + // List of nodes currently available for choosing for scheduling + Node_List _available; + + // Mapping from node (index) to basic block + Block_Array& _bbs; + + // For each instruction beginning a bundle, the number of following + // nodes to be bundled with it. + Bundle *_node_bundling_base; + + // Mapping from register to Node + Node_List _reg_node; + + // Free list for pinch nodes. + Node_List _pinch_free_list; + + // Latency from the beginning of the containing basic block (base 1) + // for each node. + unsigned short *_node_latency; + + // Number of uses of this node within the containing basic block. + short *_uses; + + // Schedulable portion of current block. Skips Region/Phi/CreateEx up + // front, branch+proj at end. Also skips Catch/CProj (same as + // branch-at-end), plus just-prior exception-throwing call. + uint _bb_start, _bb_end; + + // Latency from the end of the basic block as scheduled + unsigned short *_current_latency; + + // Remember the next node + Node *_next_node; + + // Use this for an unconditional branch delay slot + Node *_unconditional_delay_slot; + + // Pointer to a Nop + MachNopNode *_nop; + + // Length of the current bundle, in instructions + uint _bundle_instr_count; + + // Current Cycle number, for computing latencies and bundling + uint _bundle_cycle_number; + + // Bundle information + Pipeline_Use_Element _bundle_use_elements[resource_count]; + Pipeline_Use _bundle_use; + + // Dump the available list + void dump_available() const; + +public: + Scheduling(Arena *arena, Compile &compile); + + // Destructor + NOT_PRODUCT( ~Scheduling(); ) + + // Step ahead "i" cycles + void step(uint i); + + // Step ahead 1 cycle, and clear the bundle state (for example, + // at a branch target) + void step_and_clear(); + + Bundle* node_bundling(const Node *n) { + assert(valid_bundle_info(n), "oob"); + return (&_node_bundling_base[n->_idx]); + } + + bool valid_bundle_info(const Node *n) const { + return (_node_bundling_limit > n->_idx); + } + + bool starts_bundle(const Node *n) const { + return (_node_bundling_limit > n->_idx && _node_bundling_base[n->_idx].starts_bundle()); + } + + // Do the scheduling + void DoScheduling(); + + // Compute the local latencies walking forward over the list of + // nodes for a basic block + void ComputeLocalLatenciesForward(const Block *bb); + + // Compute the register antidependencies within a basic block + void ComputeRegisterAntidependencies(Block *bb); + void verify_do_def( Node *n, OptoReg::Name def, const char *msg ); + void verify_good_schedule( Block *b, const char *msg ); + void anti_do_def( Block *b, Node *def, OptoReg::Name def_reg, int is_def ); + void anti_do_use( Block *b, Node *use, OptoReg::Name use_reg ); + + // Add a node to the current bundle + void AddNodeToBundle(Node *n, const Block *bb); + + // Add a node to the list of available nodes + void AddNodeToAvailableList(Node *n); + + // Compute the local use count for the nodes in a block, and compute + // the list of instructions with no uses in the block as available + void ComputeUseCount(const Block *bb); + + // Choose an instruction from the available list to add to the bundle + Node * ChooseNodeToBundle(); + + // See if this Node fits into the currently accumulating bundle + bool NodeFitsInBundle(Node *n); + + // Decrement the use count for a node + void DecrementUseCounts(Node *n, const Block *bb); + + // Garbage collect pinch nodes for reuse by other blocks. + void garbage_collect_pinch_nodes(); + // Clean up a pinch node for reuse (helper for above). + void cleanup_pinch( Node *pinch ); + + // Information for statistics gathering +#ifndef PRODUCT +private: + // Gather information on size of nops relative to total + uint _branches, _unconditional_delays; + + static uint _total_nop_size, _total_method_size; + static uint _total_branches, _total_unconditional_delays; + static uint _total_instructions_per_bundle[Pipeline::_max_instrs_per_cycle+1]; + +public: + static void print_statistics(); + + static void increment_instructions_per_bundle(uint i) { + _total_instructions_per_bundle[i]++; + } + + static void increment_nop_size(uint s) { + _total_nop_size += s; + } + + static void increment_method_size(uint s) { + _total_method_size += s; + } +#endif + +}; diff --git a/hotspot/src/share/vm/opto/parse.hpp b/hotspot/src/share/vm/opto/parse.hpp new file mode 100644 index 00000000000..60ffdf17dd7 --- /dev/null +++ b/hotspot/src/share/vm/opto/parse.hpp @@ -0,0 +1,555 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class BytecodeParseHistogram; +class InlineTree; +class Parse; +class SwitchRange; + + +//------------------------------InlineTree------------------------------------- +class InlineTree : public ResourceObj { + Compile* C; // cache + JVMState* _caller_jvms; // state of caller + ciMethod* _method; // method being called by the caller_jvms + InlineTree* _caller_tree; + uint _count_inline_bcs; // Accumulated count of inlined bytecodes + // Call-site count / interpreter invocation count, scaled recursively. + // Always between 0.0 and 1.0. Represents the percentage of the method's + // total execution time used at this call site. + const float _site_invoke_ratio; + float compute_callee_frequency( int caller_bci ) const; + + GrowableArray _subtrees; + friend class Compile; + +protected: + InlineTree(Compile* C, + const InlineTree* caller_tree, + ciMethod* callee_method, + JVMState* caller_jvms, + int caller_bci, + float site_invoke_ratio); + InlineTree *build_inline_tree_for_callee(ciMethod* callee_method, + JVMState* caller_jvms, + int caller_bci); + const char* try_to_inline(ciMethod* callee_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result); + const char* shouldInline(ciMethod* callee_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const; + const char* shouldNotInline(ciMethod* callee_method, WarmCallInfo* wci_result) const; + void print_inlining(ciMethod *callee_method, int caller_bci, const char *failure_msg) const PRODUCT_RETURN; + + InlineTree *caller_tree() const { return _caller_tree; } + InlineTree* callee_at(int bci, ciMethod* m) const; + int inline_depth() const { return _caller_jvms ? _caller_jvms->depth() : 0; } + +public: + static InlineTree* build_inline_tree_root(); + static InlineTree* find_subtree_from_root(InlineTree* root, JVMState* jvms, ciMethod* callee, bool create_if_not_found = false); + + // For temporary (stack-allocated, stateless) ilts: + InlineTree(Compile* c, ciMethod* callee_method, JVMState* caller_jvms, float site_invoke_ratio); + + // InlineTree enum + enum InlineStyle { + Inline_do_not_inline = 0, // + Inline_cha_is_monomorphic = 1, // + Inline_type_profile_monomorphic = 2 // + }; + + // See if it is OK to inline. + // The reciever is the inline tree for the caller. + // + // The result is a temperature indication. If it is hot or cold, + // inlining is immediate or undesirable. Otherwise, the info block + // returned is newly allocated and may be enqueued. + // + // If the method is inlinable, a new inline subtree is created on the fly, + // and may be accessed by find_subtree_from_root. + // The call_method is the dest_method for a special or static invocation. + // The call_method is an optimized virtual method candidate otherwise. + WarmCallInfo* ok_to_inline(ciMethod *call_method, JVMState* caller_jvms, ciCallProfile& profile, WarmCallInfo* wci); + + // Information about inlined method + JVMState* caller_jvms() const { return _caller_jvms; } + ciMethod *method() const { return _method; } + int caller_bci() const { return _caller_jvms ? _caller_jvms->bci() : InvocationEntryBci; } + uint count_inline_bcs() const { return _count_inline_bcs; } + float site_invoke_ratio() const { return _site_invoke_ratio; }; + +#ifndef PRODUCT +private: + uint _count_inlines; // Count of inlined methods +public: + // Debug information collected during parse + uint count_inlines() const { return _count_inlines; }; +#endif + GrowableArray subtrees() { return _subtrees; } +}; + + +//----------------------------------------------------------------------------- +//------------------------------Parse------------------------------------------ +// Parse bytecodes, build a Graph +class Parse : public GraphKit { + public: + // Per-block information needed by the parser: + class Block { + private: + ciTypeFlow::Block* _flow; + int _pred_count; // how many predecessors in CFG? + int _preds_parsed; // how many of these have been parsed? + uint _count; // how many times executed? Currently only set by _goto's + bool _is_parsed; // has this block been parsed yet? + bool _is_handler; // is this block an exception handler? + SafePointNode* _start_map; // all values flowing into this block + MethodLivenessResult _live_locals; // lazily initialized liveness bitmap + + int _num_successors; // Includes only normal control flow. + int _all_successors; // Include exception paths also. + Block** _successors; + + // Use init_node/init_graph to initialize Blocks. + // Block() : _live_locals((uintptr_t*)NULL,0) { ShouldNotReachHere(); } + Block() : _live_locals(NULL,0) { ShouldNotReachHere(); } + + public: + + // Set up the block data structure itself. + void init_node(Parse* outer, int po); + // Set up the block's relations to other blocks. + void init_graph(Parse* outer); + + ciTypeFlow::Block* flow() const { return _flow; } + int pred_count() const { return _pred_count; } + int preds_parsed() const { return _preds_parsed; } + bool is_parsed() const { return _is_parsed; } + bool is_handler() const { return _is_handler; } + void set_count( uint x ) { _count = x; } + uint count() const { return _count; } + + SafePointNode* start_map() const { assert(is_merged(),""); return _start_map; } + void set_start_map(SafePointNode* m) { assert(!is_merged(), ""); _start_map = m; } + + // True after any predecessor flows control into this block + bool is_merged() const { return _start_map != NULL; } + + // True when all non-exception predecessors have been parsed. + bool is_ready() const { return preds_parsed() == pred_count(); } + + int num_successors() const { return _num_successors; } + int all_successors() const { return _all_successors; } + Block* successor_at(int i) const { + assert((uint)i < (uint)all_successors(), ""); + return _successors[i]; + } + Block* successor_for_bci(int bci); + + int start() const { return flow()->start(); } + int limit() const { return flow()->limit(); } + int pre_order() const { return flow()->pre_order(); } + int start_sp() const { return flow()->stack_size(); } + + const Type* peek(int off=0) const { return stack_type_at(start_sp() - (off+1)); } + + const Type* stack_type_at(int i) const; + const Type* local_type_at(int i) const; + static const Type* get_type(ciType* t) { return Type::get_typeflow_type(t); } + + bool has_trap_at(int bci) const { return flow()->has_trap() && flow()->trap_bci() == bci; } + + // Call this just before parsing a block. + void mark_parsed() { + assert(!_is_parsed, "must parse each block exactly once"); + _is_parsed = true; + } + + // Return the phi/region input index for the "current" pred, + // and bump the pred number. For historical reasons these index + // numbers are handed out in descending order. The last index is + // always PhiNode::Input (i.e., 1). The value returned is known + // as a "path number" because it distinguishes by which path we are + // entering the block. + int next_path_num() { + assert(preds_parsed() < pred_count(), "too many preds?"); + return pred_count() - _preds_parsed++; + } + + // Add a previously unaccounted predecessor to this block. + // This operates by increasing the size of the block's region + // and all its phi nodes (if any). The value returned is a + // path number ("pnum"). + int add_new_path(); + + // Initialize me by recording the parser's map. My own map must be NULL. + void record_state(Parse* outer); + }; + +#ifndef PRODUCT + // BytecodeParseHistogram collects number of bytecodes parsed, nodes constructed, and transformations. + class BytecodeParseHistogram : public ResourceObj { + private: + enum BPHType { + BPH_transforms, + BPH_values + }; + static bool _initialized; + static uint _bytecodes_parsed [Bytecodes::number_of_codes]; + static uint _nodes_constructed[Bytecodes::number_of_codes]; + static uint _nodes_transformed[Bytecodes::number_of_codes]; + static uint _new_values [Bytecodes::number_of_codes]; + + Bytecodes::Code _initial_bytecode; + int _initial_node_count; + int _initial_transforms; + int _initial_values; + + Parse *_parser; + Compile *_compiler; + + // Initialization + static void reset(); + + // Return info being collected, select with global flag 'BytecodeParseInfo' + int current_count(BPHType info_selector); + + public: + BytecodeParseHistogram(Parse *p, Compile *c); + static bool initialized(); + + // Record info when starting to parse one bytecode + void set_initial_state( Bytecodes::Code bc ); + // Record results of parsing one bytecode + void record_change(); + + // Profile printing + static void print(float cutoff = 0.01F); // cutoff in percent + }; + + public: + // Record work done during parsing + BytecodeParseHistogram* _parse_histogram; + void set_parse_histogram(BytecodeParseHistogram *bph) { _parse_histogram = bph; } + BytecodeParseHistogram* parse_histogram() { return _parse_histogram; } +#endif + + private: + friend class Block; + + // Variables which characterize this compilation as a whole: + + JVMState* _caller; // JVMS which carries incoming args & state. + float _expected_uses; // expected number of calls to this code + float _prof_factor; // discount applied to my profile counts + int _depth; // Inline tree depth, for debug printouts + const TypeFunc*_tf; // My kind of function type + int _entry_bci; // the osr bci or InvocationEntryBci + + ciTypeFlow* _flow; // Results of previous flow pass. + Block* _blocks; // Array of basic-block structs. + int _block_count; // Number of elements in _blocks. + + GraphKit _exits; // Record all normal returns and throws here. + bool _wrote_final; // Did we write a final field? + bool _count_invocations; // update and test invocation counter + bool _method_data_update; // update method data oop + + // Variables which track Java semantics during bytecode parsing: + + Block* _block; // block currently getting parsed + ciBytecodeStream _iter; // stream of this method's bytecodes + + int _blocks_merged; // Progress meter: state merges from BB preds + int _blocks_parsed; // Progress meter: BBs actually parsed + + const FastLockNode* _synch_lock; // FastLockNode for synchronized method + +#ifndef PRODUCT + int _max_switch_depth; // Debugging SwitchRanges. + int _est_switch_depth; // Debugging SwitchRanges. +#endif + + public: + // Constructor + Parse(JVMState* caller, ciMethod* parse_method, float expected_uses); + + virtual Parse* is_Parse() const { return (Parse*)this; } + + public: + // Accessors. + JVMState* caller() const { return _caller; } + float expected_uses() const { return _expected_uses; } + float prof_factor() const { return _prof_factor; } + int depth() const { return _depth; } + const TypeFunc* tf() const { return _tf; } + // entry_bci() -- see osr_bci, etc. + + ciTypeFlow* flow() const { return _flow; } + // blocks() -- see pre_order_at, start_block, etc. + int block_count() const { return _block_count; } + + GraphKit& exits() { return _exits; } + bool wrote_final() const { return _wrote_final; } + void set_wrote_final(bool z) { _wrote_final = z; } + bool count_invocations() const { return _count_invocations; } + bool method_data_update() const { return _method_data_update; } + + Block* block() const { return _block; } + ciBytecodeStream& iter() { return _iter; } + Bytecodes::Code bc() const { return _iter.cur_bc(); } + + void set_block(Block* b) { _block = b; } + + // Derived accessors: + bool is_normal_parse() const { return _entry_bci == InvocationEntryBci; } + bool is_osr_parse() const { return _entry_bci != InvocationEntryBci; } + int osr_bci() const { assert(is_osr_parse(),""); return _entry_bci; } + + void set_parse_bci(int bci); + + // Must this parse be aborted? + bool failing() { return C->failing(); } + + Block* pre_order_at(int po) { + assert(0 <= po && po < _block_count, "oob"); + return &_blocks[po]; + } + Block* start_block() { + return pre_order_at(flow()->start_block()->pre_order()); + } + // Can return NULL if the flow pass did not complete a block. + Block* successor_for_bci(int bci) { + return block()->successor_for_bci(bci); + } + + private: + // Create a JVMS & map for the initial state of this method. + SafePointNode* create_entry_map(); + + // OSR helpers + Node *fetch_interpreter_state(int index, BasicType bt, Node *local_addrs, Node *local_addrs_base); + Node* check_interpreter_type(Node* l, const Type* type, SafePointNode* &bad_type_exit); + void load_interpreter_state(Node* osr_buf); + + // Functions for managing basic blocks: + void init_blocks(); + void load_state_from(Block* b); + void store_state_to(Block* b) { b->record_state(this); } + + // Parse all the basic blocks. + void do_all_blocks(); + + // Helper for do_all_blocks; makes one pass in pre-order. + void visit_blocks(); + + // Parse the current basic block + void do_one_block(); + + // Raise an error if we get a bad ciTypeFlow CFG. + void handle_missing_successor(int bci); + + // first actions (before BCI 0) + void do_method_entry(); + + // implementation of monitorenter/monitorexit + void do_monitor_enter(); + void do_monitor_exit(); + + // Eagerly create phie throughout the state, to cope with back edges. + void ensure_phis_everywhere(); + + // Merge the current mapping into the basic block starting at bci + void merge( int target_bci); + // Same as plain merge, except that it allocates a new path number. + void merge_new_path( int target_bci); + // Merge the current mapping into an exception handler. + void merge_exception(int target_bci); + // Helper: Merge the current mapping into the given basic block + void merge_common(Block* target, int pnum); + // Helper functions for merging individual cells. + PhiNode *ensure_phi( int idx, bool nocreate = false); + PhiNode *ensure_memory_phi(int idx, bool nocreate = false); + // Helper to merge the current memory state into the given basic block + void merge_memory_edges(MergeMemNode* n, int pnum, bool nophi); + + // Parse this bytecode, and alter the Parsers JVM->Node mapping + void do_one_bytecode(); + + // helper function to generate array store check + void array_store_check(); + // Helper function to generate array load + void array_load(BasicType etype); + // Helper function to generate array store + void array_store(BasicType etype); + // Helper function to compute array addressing + Node* array_addressing(BasicType type, int vals, const Type* *result2=NULL); + + // Pass current map to exits + void return_current(Node* value); + + // Register finalizers on return from Object. + void call_register_finalizer(); + + // Insert a compiler safepoint into the graph + void add_safepoint(); + + // Insert a compiler safepoint into the graph, if there is a back-branch. + void maybe_add_safepoint(int target_bci) { + if (UseLoopSafepoints && target_bci <= bci()) { + add_safepoint(); + } + } + + // Note: Intrinsic generation routines may be found in library_call.cpp. + + // Helper function to setup Ideal Call nodes + void do_call(); + + // Helper function to uncommon-trap or bailout for non-compilable call-sites + bool can_not_compile_call_site(ciMethod *dest_method, ciInstanceKlass *klass); + + // Helper function to identify inlining potential at call-site + ciMethod* optimize_inlining(ciMethod* caller, int bci, ciInstanceKlass* klass, + ciMethod *dest_method, const TypeOopPtr* receiver_type); + + // Helper function to setup for type-profile based inlining + bool prepare_type_profile_inline(ciInstanceKlass* prof_klass, ciMethod* prof_method); + + // Helper functions for type checking bytecodes: + void do_checkcast(); + void do_instanceof(); + + // Helper functions for shifting & arithmetic + void modf(); + void modd(); + void l2f(); + + void do_irem(); + + // implementation of _get* and _put* bytecodes + void do_getstatic() { do_field_access(true, false); } + void do_getfield () { do_field_access(true, true); } + void do_putstatic() { do_field_access(false, false); } + void do_putfield () { do_field_access(false, true); } + + // common code for making initial checks and forming addresses + void do_field_access(bool is_get, bool is_field); + bool static_field_ok_in_clinit(ciField *field, ciMethod *method); + + // common code for actually performing the load or store + void do_get_xxx(const TypePtr* obj_type, Node* obj, ciField* field, bool is_field); + void do_put_xxx(const TypePtr* obj_type, Node* obj, ciField* field, bool is_field); + + // loading from a constant field or the constant pool + // returns false if push failed (non-perm field constants only, not ldcs) + bool push_constant(ciConstant con); + + // implementation of object creation bytecodes + void do_new(); + void do_newarray(BasicType elemtype); + void do_anewarray(); + void do_multianewarray(); + Node* expand_multianewarray(ciArrayKlass* array_klass, Node* *lengths, int ndimensions); + + // implementation of jsr/ret + void do_jsr(); + void do_ret(); + + float dynamic_branch_prediction(float &cnt); + float branch_prediction(float &cnt, BoolTest::mask btest, int target_bci); + bool seems_never_taken(float prob); + + void do_ifnull(BoolTest::mask btest); + void do_if(BoolTest::mask btest, Node* c); + void repush_if_args(); + void adjust_map_after_if(BoolTest::mask btest, Node* c, float prob, + Block* path, Block* other_path); + IfNode* jump_if_fork_int(Node* a, Node* b, BoolTest::mask mask); + Node* jump_if_join(Node* iffalse, Node* iftrue); + void jump_if_true_fork(IfNode *ifNode, int dest_bci_if_true, int prof_table_index); + void jump_if_false_fork(IfNode *ifNode, int dest_bci_if_false, int prof_table_index); + void jump_if_always_fork(int dest_bci_if_true, int prof_table_index); + + friend class SwitchRange; + void do_tableswitch(); + void do_lookupswitch(); + void jump_switch_ranges(Node* a, SwitchRange* lo, SwitchRange* hi, int depth = 0); + bool create_jump_tables(Node* a, SwitchRange* lo, SwitchRange* hi); + + // helper functions for methodData style profiling + void test_counter_against_threshold(Node* cnt, int limit); + void increment_and_test_invocation_counter(int limit); + void test_for_osr_md_counter_at(ciMethodData* md, ciProfileData* data, ByteSize offset, int limit); + Node* method_data_addressing(ciMethodData* md, ciProfileData* data, ByteSize offset, Node* idx = NULL, uint stride = 0); + void increment_md_counter_at(ciMethodData* md, ciProfileData* data, ByteSize offset, Node* idx = NULL, uint stride = 0); + void set_md_flag_at(ciMethodData* md, ciProfileData* data, int flag_constant); + + void profile_method_entry(); + void profile_taken_branch(int target_bci, bool force_update = false); + void profile_not_taken_branch(bool force_update = false); + void profile_call(Node* receiver); + void profile_generic_call(); + void profile_receiver_type(Node* receiver); + void profile_ret(int target_bci); + void profile_null_checkcast(); + void profile_switch_case(int table_index); + + // helper function for call statistics + void count_compiled_calls(bool at_method_entry, bool is_inline) PRODUCT_RETURN; + + Node_Notes* make_node_notes(Node_Notes* caller_nn); + + // Helper functions for handling normal and abnormal exits. + void build_exits(); + + // Fix up all exceptional control flow exiting a single bytecode. + void do_exceptions(); + + // Fix up all exiting control flow at the end of the parse. + void do_exits(); + + // Add Catch/CatchProjs + // The call is either a Java call or the VM's rethrow stub + void catch_call_exceptions(ciExceptionHandlerStream&); + + // Handle all exceptions thrown by the inlined method. + // Also handles exceptions for individual bytecodes. + void catch_inline_exceptions(SafePointNode* ex_map); + + // Bytecode classifier, helps decide to use uncommon_trap vs. rethrow_C. + bool can_rerun_bytecode(); + + // Merge the given map into correct exceptional exit state. + // Assumes that there is no applicable local handler. + void throw_to_exit(SafePointNode* ex_map); + + public: +#ifndef PRODUCT + // Handle PrintOpto, etc. + void show_parse_info(); + void dump_map_adr_mem() const; + static void print_statistics(); // Print some performance counters + void dump(); + void dump_bci(int bci); +#endif +}; diff --git a/hotspot/src/share/vm/opto/parse1.cpp b/hotspot/src/share/vm/opto/parse1.cpp new file mode 100644 index 00000000000..4f8e93162cc --- /dev/null +++ b/hotspot/src/share/vm/opto/parse1.cpp @@ -0,0 +1,2166 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_parse1.cpp.incl" + +// Static array so we can figure out which bytecodes stop us from compiling +// the most. Some of the non-static variables are needed in bytecodeInfo.cpp +// and eventually should be encapsulated in a proper class (gri 8/18/98). + +int nodes_created = 0; int nodes_created_old = 0; +int methods_parsed = 0; int methods_parsed_old = 0; +int methods_seen = 0; int methods_seen_old = 0; + +int explicit_null_checks_inserted = 0, explicit_null_checks_inserted_old = 0; +int explicit_null_checks_elided = 0, explicit_null_checks_elided_old = 0; +int all_null_checks_found = 0, implicit_null_checks = 0; +int implicit_null_throws = 0; + +int parse_idx = 0; +size_t parse_arena = 0; +int reclaim_idx = 0; +int reclaim_in = 0; +int reclaim_node = 0; + +#ifndef PRODUCT +bool Parse::BytecodeParseHistogram::_initialized = false; +uint Parse::BytecodeParseHistogram::_bytecodes_parsed [Bytecodes::number_of_codes]; +uint Parse::BytecodeParseHistogram::_nodes_constructed[Bytecodes::number_of_codes]; +uint Parse::BytecodeParseHistogram::_nodes_transformed[Bytecodes::number_of_codes]; +uint Parse::BytecodeParseHistogram::_new_values [Bytecodes::number_of_codes]; +#endif + +//------------------------------print_statistics------------------------------- +#ifndef PRODUCT +void Parse::print_statistics() { + tty->print_cr("--- Compiler Statistics ---"); + tty->print("Methods seen: %d Methods parsed: %d", methods_seen, methods_parsed); + tty->print(" Nodes created: %d", nodes_created); + tty->cr(); + if (methods_seen != methods_parsed) + tty->print_cr("Reasons for parse failures (NOT cumulative):"); + + if( explicit_null_checks_inserted ) + tty->print_cr("%d original NULL checks - %d elided (%2d%%); optimizer leaves %d,", explicit_null_checks_inserted, explicit_null_checks_elided, (100*explicit_null_checks_elided)/explicit_null_checks_inserted, all_null_checks_found); + if( all_null_checks_found ) + tty->print_cr("%d made implicit (%2d%%)", implicit_null_checks, + (100*implicit_null_checks)/all_null_checks_found); + if( implicit_null_throws ) + tty->print_cr("%d implicit null exceptions at runtime", + implicit_null_throws); + + if( PrintParseStatistics && BytecodeParseHistogram::initialized() ) { + BytecodeParseHistogram::print(); + } +} +#endif + +//------------------------------ON STACK REPLACEMENT--------------------------- + +// Construct a node which can be used to get incoming state for +// on stack replacement. +Node *Parse::fetch_interpreter_state(int index, + BasicType bt, + Node *local_addrs, + Node *local_addrs_base) { + Node *mem = memory(Compile::AliasIdxRaw); + Node *adr = basic_plus_adr( local_addrs_base, local_addrs, -index*wordSize ); + + // Very similar to LoadNode::make, except we handle un-aligned longs and + // doubles on Sparc. Intel can handle them just fine directly. + Node *l; + switch( bt ) { // Signature is flattened + case T_INT: l = new (C, 3) LoadINode( 0, mem, adr, TypeRawPtr::BOTTOM ); break; + case T_FLOAT: l = new (C, 3) LoadFNode( 0, mem, adr, TypeRawPtr::BOTTOM ); break; + case T_ADDRESS: + case T_OBJECT: l = new (C, 3) LoadPNode( 0, mem, adr, TypeRawPtr::BOTTOM, TypeInstPtr::BOTTOM ); break; + case T_LONG: + case T_DOUBLE: { + // Since arguments are in reverse order, the argument address 'adr' + // refers to the back half of the long/double. Recompute adr. + adr = basic_plus_adr( local_addrs_base, local_addrs, -(index+1)*wordSize ); + if( Matcher::misaligned_doubles_ok ) { + l = (bt == T_DOUBLE) + ? (Node*)new (C, 3) LoadDNode( 0, mem, adr, TypeRawPtr::BOTTOM ) + : (Node*)new (C, 3) LoadLNode( 0, mem, adr, TypeRawPtr::BOTTOM ); + } else { + l = (bt == T_DOUBLE) + ? (Node*)new (C, 3) LoadD_unalignedNode( 0, mem, adr, TypeRawPtr::BOTTOM ) + : (Node*)new (C, 3) LoadL_unalignedNode( 0, mem, adr, TypeRawPtr::BOTTOM ); + } + break; + } + default: ShouldNotReachHere(); + } + return _gvn.transform(l); +} + +// Helper routine to prevent the interpreter from handing +// unexpected typestate to an OSR method. +// The Node l is a value newly dug out of the interpreter frame. +// The type is the type predicted by ciTypeFlow. Note that it is +// not a general type, but can only come from Type::get_typeflow_type. +// The safepoint is a map which will feed an uncommon trap. +Node* Parse::check_interpreter_type(Node* l, const Type* type, + SafePointNode* &bad_type_exit) { + + const TypeOopPtr* tp = type->isa_oopptr(); + + // TypeFlow may assert null-ness if a type appears unloaded. + if (type == TypePtr::NULL_PTR || + (tp != NULL && !tp->klass()->is_loaded())) { + // Value must be null, not a real oop. + Node* chk = _gvn.transform( new (C, 3) CmpPNode(l, null()) ); + Node* tst = _gvn.transform( new (C, 2) BoolNode(chk, BoolTest::eq) ); + IfNode* iff = create_and_map_if(control(), tst, PROB_MAX, COUNT_UNKNOWN); + set_control(_gvn.transform( new (C, 1) IfTrueNode(iff) )); + Node* bad_type = _gvn.transform( new (C, 1) IfFalseNode(iff) ); + bad_type_exit->control()->add_req(bad_type); + l = null(); + } + + // Typeflow can also cut off paths from the CFG, based on + // types which appear unloaded, or call sites which appear unlinked. + // When paths are cut off, values at later merge points can rise + // toward more specific classes. Make sure these specific classes + // are still in effect. + if (tp != NULL && tp->klass() != C->env()->Object_klass()) { + // TypeFlow asserted a specific object type. Value must have that type. + Node* bad_type_ctrl = NULL; + l = gen_checkcast(l, makecon(TypeKlassPtr::make(tp->klass())), &bad_type_ctrl); + bad_type_exit->control()->add_req(bad_type_ctrl); + } + + BasicType bt_l = _gvn.type(l)->basic_type(); + BasicType bt_t = type->basic_type(); + assert(_gvn.type(l)->higher_equal(type), "must constrain OSR typestate"); + return l; +} + +// Helper routine which sets up elements of the initial parser map when +// performing a parse for on stack replacement. Add values into map. +// The only parameter contains the address of a interpreter arguments. +void Parse::load_interpreter_state(Node* osr_buf) { + int index; + int max_locals = jvms()->loc_size(); + int max_stack = jvms()->stk_size(); + + + // Mismatch between method and jvms can occur since map briefly held + // an OSR entry state (which takes up one RawPtr word). + assert(max_locals == method()->max_locals(), "sanity"); + assert(max_stack >= method()->max_stack(), "sanity"); + assert((int)jvms()->endoff() == TypeFunc::Parms + max_locals + max_stack, "sanity"); + assert((int)jvms()->endoff() == (int)map()->req(), "sanity"); + + // Find the start block. + Block* osr_block = start_block(); + assert(osr_block->start() == osr_bci(), "sanity"); + + // Set initial BCI. + set_parse_bci(osr_block->start()); + + // Set initial stack depth. + set_sp(osr_block->start_sp()); + + // Check bailouts. We currently do not perform on stack replacement + // of loops in catch blocks or loops which branch with a non-empty stack. + if (sp() != 0) { + C->record_method_not_compilable("OSR starts with non-empty stack"); + return; + } + // Do not OSR inside finally clauses: + if (osr_block->has_trap_at(osr_block->start())) { + C->record_method_not_compilable("OSR starts with an immediate trap"); + return; + } + + // Commute monitors from interpreter frame to compiler frame. + assert(jvms()->monitor_depth() == 0, "should be no active locks at beginning of osr"); + int mcnt = osr_block->flow()->monitor_count(); + Node *monitors_addr = basic_plus_adr(osr_buf, osr_buf, (max_locals+mcnt*2-1)*wordSize); + for (index = 0; index < mcnt; index++) { + // Make a BoxLockNode for the monitor. + Node *box = _gvn.transform(new (C, 1) BoxLockNode(next_monitor())); + + + // Displaced headers and locked objects are interleaved in the + // temp OSR buffer. We only copy the locked objects out here. + // Fetch the locked object from the OSR temp buffer and copy to our fastlock node. + Node *lock_object = fetch_interpreter_state(index*2, T_OBJECT, monitors_addr, osr_buf); + // Try and copy the displaced header to the BoxNode + Node *displaced_hdr = fetch_interpreter_state((index*2) + 1, T_ADDRESS, monitors_addr, osr_buf); + + + store_to_memory(control(), box, displaced_hdr, T_ADDRESS, Compile::AliasIdxRaw); + + // Build a bogus FastLockNode (no code will be generated) and push the + // monitor into our debug info. + const FastLockNode *flock = _gvn.transform(new (C, 3) FastLockNode( 0, lock_object, box ))->as_FastLock(); + map()->push_monitor(flock); + + // If the lock is our method synchronization lock, tuck it away in + // _sync_lock for return and rethrow exit paths. + if (index == 0 && method()->is_synchronized()) { + _synch_lock = flock; + } + } + + MethodLivenessResult live_locals = method()->liveness_at_bci(osr_bci()); + if (!live_locals.is_valid()) { + // Degenerate or breakpointed method. + C->record_method_not_compilable("OSR in empty or breakpointed method"); + return; + } + + // Extract the needed locals from the interpreter frame. + Node *locals_addr = basic_plus_adr(osr_buf, osr_buf, (max_locals-1)*wordSize); + + // find all the locals that the interpreter thinks contain live oops + const BitMap live_oops = method()->live_local_oops_at_bci(osr_bci()); + for (index = 0; index < max_locals; index++) { + + if (!live_locals.at(index)) { + continue; + } + + const Type *type = osr_block->local_type_at(index); + + if (type->isa_oopptr() != NULL) { + + // 6403625: Verify that the interpreter oopMap thinks that the oop is live + // else we might load a stale oop if the MethodLiveness disagrees with the + // result of the interpreter. If the interpreter says it is dead we agree + // by making the value go to top. + // + + if (!live_oops.at(index)) { + if (C->log() != NULL) { + C->log()->elem("OSR_mismatch local_index='%d'",index); + } + set_local(index, null()); + // and ignore it for the loads + continue; + } + } + + // Filter out TOP, HALF, and BOTTOM. (Cf. ensure_phi.) + if (type == Type::TOP || type == Type::HALF) { + continue; + } + // If the type falls to bottom, then this must be a local that + // is mixing ints and oops or some such. Forcing it to top + // makes it go dead. + if (type == Type::BOTTOM) { + continue; + } + // Construct code to access the appropriate local. + Node *value = fetch_interpreter_state(index, type->basic_type(), locals_addr, osr_buf); + set_local(index, value); + } + + // Extract the needed stack entries from the interpreter frame. + for (index = 0; index < sp(); index++) { + const Type *type = osr_block->stack_type_at(index); + if (type != Type::TOP) { + // Currently the compiler bails out when attempting to on stack replace + // at a bci with a non-empty stack. We should not reach here. + ShouldNotReachHere(); + } + } + + // End the OSR migration + make_runtime_call(RC_LEAF, OptoRuntime::osr_end_Type(), + CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_end), + "OSR_migration_end", TypeRawPtr::BOTTOM, + osr_buf); + + // Now that the interpreter state is loaded, make sure it will match + // at execution time what the compiler is expecting now: + SafePointNode* bad_type_exit = clone_map(); + bad_type_exit->set_control(new (C, 1) RegionNode(1)); + + for (index = 0; index < max_locals; index++) { + if (stopped()) break; + Node* l = local(index); + if (l->is_top()) continue; // nothing here + const Type *type = osr_block->local_type_at(index); + if (type->isa_oopptr() != NULL) { + if (!live_oops.at(index)) { + // skip type check for dead oops + continue; + } + } + set_local(index, check_interpreter_type(l, type, bad_type_exit)); + } + + for (index = 0; index < sp(); index++) { + if (stopped()) break; + Node* l = stack(index); + if (l->is_top()) continue; // nothing here + const Type *type = osr_block->stack_type_at(index); + set_stack(index, check_interpreter_type(l, type, bad_type_exit)); + } + + if (bad_type_exit->control()->req() > 1) { + // Build an uncommon trap here, if any inputs can be unexpected. + bad_type_exit->set_control(_gvn.transform( bad_type_exit->control() )); + record_for_igvn(bad_type_exit->control()); + SafePointNode* types_are_good = map(); + set_map(bad_type_exit); + // The unexpected type happens because a new edge is active + // in the CFG, which typeflow had previously ignored. + // E.g., Object x = coldAtFirst() && notReached()? "str": new Integer(123). + // This x will be typed as Integer if notReached is not yet linked. + uncommon_trap(Deoptimization::Reason_unreached, + Deoptimization::Action_reinterpret); + set_map(types_are_good); + } +} + +//------------------------------Parse------------------------------------------ +// Main parser constructor. +Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) + : _exits(caller) +{ + // Init some variables + _caller = caller; + _method = parse_method; + _expected_uses = expected_uses; + _depth = 1 + (caller->has_method() ? caller->depth() : 0); + _wrote_final = false; + _entry_bci = InvocationEntryBci; + _tf = NULL; + _block = NULL; + debug_only(_block_count = -1); + debug_only(_blocks = (Block*)-1); +#ifndef PRODUCT + if (PrintCompilation || PrintOpto) { + // Make sure I have an inline tree, so I can print messages about it. + JVMState* ilt_caller = is_osr_parse() ? caller->caller() : caller; + InlineTree::find_subtree_from_root(C->ilt(), ilt_caller, parse_method, true); + } + _max_switch_depth = 0; + _est_switch_depth = 0; +#endif + + _tf = TypeFunc::make(method()); + _iter.reset_to_method(method()); + _flow = method()->get_flow_analysis(); + if (_flow->failing()) { + C->record_method_not_compilable_all_tiers(_flow->failure_reason()); + } + + if (_expected_uses <= 0) { + _prof_factor = 1; + } else { + float prof_total = parse_method->interpreter_invocation_count(); + if (prof_total <= _expected_uses) { + _prof_factor = 1; + } else { + _prof_factor = _expected_uses / prof_total; + } + } + + CompileLog* log = C->log(); + if (log != NULL) { + log->begin_head("parse method='%d' uses='%g'", + log->identify(parse_method), expected_uses); + if (depth() == 1 && C->is_osr_compilation()) { + log->print(" osr_bci='%d'", C->entry_bci()); + } + log->stamp(); + log->end_head(); + } + + // Accumulate deoptimization counts. + // (The range_check and store_check counts are checked elsewhere.) + ciMethodData* md = method()->method_data(); + for (uint reason = 0; reason < md->trap_reason_limit(); reason++) { + uint md_count = md->trap_count(reason); + if (md_count != 0) { + if (md_count == md->trap_count_limit()) + md_count += md->overflow_trap_count(); + uint total_count = C->trap_count(reason); + uint old_count = total_count; + total_count += md_count; + // Saturate the add if it overflows. + if (total_count < old_count || total_count < md_count) + total_count = (uint)-1; + C->set_trap_count(reason, total_count); + if (log != NULL) + log->elem("observe trap='%s' count='%d' total='%d'", + Deoptimization::trap_reason_name(reason), + md_count, total_count); + } + } + // Accumulate total sum of decompilations, also. + C->set_decompile_count(C->decompile_count() + md->decompile_count()); + + _count_invocations = C->do_count_invocations(); + _method_data_update = C->do_method_data_update(); + + if (log != NULL && method()->has_exception_handlers()) { + log->elem("observe that='has_exception_handlers'"); + } + + assert(method()->can_be_compiled(), "Can not parse this method, cutout earlier"); + assert(method()->has_balanced_monitors(), "Can not parse unbalanced monitors, cutout earlier"); + + // Always register dependence if JVMTI is enabled, because + // either breakpoint setting or hotswapping of methods may + // cause deoptimization. + if (JvmtiExport::can_hotswap_or_post_breakpoint()) { + C->dependencies()->assert_evol_method(method()); + } + + methods_seen++; + + // Do some special top-level things. + if (depth() == 1 && C->is_osr_compilation()) { + _entry_bci = C->entry_bci(); + _flow = method()->get_osr_flow_analysis(osr_bci()); + if (_flow->failing()) { + C->record_method_not_compilable(_flow->failure_reason()); +#ifndef PRODUCT + if (PrintOpto && (Verbose || WizardMode)) { + tty->print_cr("OSR @%d type flow bailout: %s", _entry_bci, _flow->failure_reason()); + if (Verbose) { + method()->print_oop(); + method()->print_codes(); + _flow->print(); + } + } +#endif + } + _tf = C->tf(); // the OSR entry type is different + } + +#ifdef ASSERT + if (depth() == 1) { + assert(C->is_osr_compilation() == this->is_osr_parse(), "OSR in sync"); + if (C->tf() != tf()) { + MutexLockerEx ml(Compile_lock, Mutex::_no_safepoint_check_flag); + assert(C->env()->system_dictionary_modification_counter_changed(), + "Must invalidate if TypeFuncs differ"); + } + } else { + assert(!this->is_osr_parse(), "no recursive OSR"); + } +#endif + + methods_parsed++; +#ifndef PRODUCT + // add method size here to guarantee that inlined methods are added too + if (TimeCompiler) + _total_bytes_compiled += method()->code_size(); + + show_parse_info(); +#endif + + if (failing()) { + if (log) log->done("parse"); + return; + } + + gvn().set_type(root(), root()->bottom_type()); + gvn().transform(top()); + + // Import the results of the ciTypeFlow. + init_blocks(); + + // Merge point for all normal exits + build_exits(); + + // Setup the initial JVM state map. + SafePointNode* entry_map = create_entry_map(); + + // Check for bailouts during map initialization + if (failing() || entry_map == NULL) { + if (log) log->done("parse"); + return; + } + + Node_Notes* caller_nn = C->default_node_notes(); + // Collect debug info for inlined calls unless -XX:-DebugInlinedCalls. + if (DebugInlinedCalls || depth() == 1) { + C->set_default_node_notes(make_node_notes(caller_nn)); + } + + if (is_osr_parse()) { + Node* osr_buf = entry_map->in(TypeFunc::Parms+0); + entry_map->set_req(TypeFunc::Parms+0, top()); + set_map(entry_map); + load_interpreter_state(osr_buf); + } else { + set_map(entry_map); + do_method_entry(); + } + + // Check for bailouts during method entry. + if (failing()) { + if (log) log->done("parse"); + C->set_default_node_notes(caller_nn); + return; + } + + entry_map = map(); // capture any changes performed by method setup code + assert(jvms()->endoff() == map()->req(), "map matches JVMS layout"); + + // We begin parsing as if we have just encountered a jump to the + // method entry. + Block* entry_block = start_block(); + assert(entry_block->start() == (is_osr_parse() ? osr_bci() : 0), ""); + set_map_clone(entry_map); + merge_common(entry_block, entry_block->next_path_num()); + +#ifndef PRODUCT + BytecodeParseHistogram *parse_histogram_obj = new (C->env()->arena()) BytecodeParseHistogram(this, C); + set_parse_histogram( parse_histogram_obj ); +#endif + + // Parse all the basic blocks. + do_all_blocks(); + + C->set_default_node_notes(caller_nn); + + // Check for bailouts during conversion to graph + if (failing()) { + if (log) log->done("parse"); + return; + } + + // Fix up all exiting control flow. + set_map(entry_map); + do_exits(); + + // Collect a few more statistics. + parse_idx += C->unique(); + parse_arena += C->node_arena()->used(); + + if (log) log->done("parse nodes='%d' memory='%d'", + C->unique(), C->node_arena()->used()); +} + +//---------------------------do_all_blocks------------------------------------- +void Parse::do_all_blocks() { + _blocks_merged = 0; + _blocks_parsed = 0; + + int old_blocks_merged = -1; + int old_blocks_parsed = -1; + + for (int tries = 0; ; tries++) { + visit_blocks(); + if (failing()) return; // Check for bailout + + // No need for a work list. The outer loop is hardly ever repeated. + // The following loop traverses the blocks in a reasonable pre-order, + // as produced by the ciTypeFlow pass. + + // This loop can be taken more than once if there are two entries to + // a loop (irreduceable CFG), and the edge which ciTypeFlow chose + // as the first predecessor to the loop goes dead in the parser, + // due to parse-time optimization. (Could happen with obfuscated code.) + + // Look for progress, or the lack of it: + if (_blocks_parsed == block_count()) { + // That's all, folks. + if (TraceOptoParse) { + tty->print_cr("All blocks parsed."); + } + break; + } + + // How much work was done this time around? + int new_blocks_merged = _blocks_merged - old_blocks_merged; + int new_blocks_parsed = _blocks_parsed - old_blocks_parsed; + if (new_blocks_merged == 0) { + if (TraceOptoParse) { + tty->print_cr("All live blocks parsed; %d dead blocks.", block_count() - _blocks_parsed); + } + // No new blocks have become parseable. Some blocks are just dead. + break; + } + assert(new_blocks_parsed > 0, "must make progress"); + assert(tries < block_count(), "the pre-order cannot be this bad!"); + + old_blocks_merged = _blocks_merged; + old_blocks_parsed = _blocks_parsed; + } + +#ifndef PRODUCT + // Make sure there are no half-processed blocks remaining. + // Every remaining unprocessed block is dead and may be ignored now. + for (int po = 0; po < block_count(); po++) { + Block* block = pre_order_at(po); + if (!block->is_parsed()) { + if (TraceOptoParse) { + tty->print("Skipped dead block %d at bci:%d", po, block->start()); + assert(!block->is_merged(), "no half-processed blocks"); + } + } + } +#endif +} + +//---------------------------visit_blocks-------------------------------------- +void Parse::visit_blocks() { + // Walk over all blocks, parsing every one that has been reached (merged). + for (int po = 0; po < block_count(); po++) { + Block* block = pre_order_at(po); + + if (block->is_parsed()) { + // Do not parse twice. + continue; + } + + if (!block->is_merged()) { + // No state on this block. It had not yet been reached. + // Delay reaching it until later. + continue; + } + + // Prepare to parse this block. + load_state_from(block); + + if (stopped()) { + // Block is dead. + continue; + } + + if (!block->is_ready() || block->is_handler()) { + // Not all preds have been parsed. We must build phis everywhere. + // (Note that dead locals do not get phis built, ever.) + ensure_phis_everywhere(); + + // Leave behind an undisturbed copy of the map, for future merges. + set_map(clone_map()); + } + + // Ready or not, parse the block. + do_one_block(); + + // Check for bailouts. + if (failing()) return; + } +} + +//-------------------------------build_exits---------------------------------- +// Build normal and exceptional exit merge points. +void Parse::build_exits() { + // make a clone of caller to prevent sharing of side-effects + _exits.set_map(_exits.clone_map()); + _exits.clean_stack(_exits.sp()); + _exits.sync_jvms(); + + RegionNode* region = new (C, 1) RegionNode(1); + record_for_igvn(region); + gvn().set_type_bottom(region); + _exits.set_control(region); + + // Note: iophi and memphi are not transformed until do_exits. + Node* iophi = new (C, region->req()) PhiNode(region, Type::ABIO); + Node* memphi = new (C, region->req()) PhiNode(region, Type::MEMORY, TypePtr::BOTTOM); + _exits.set_i_o(iophi); + _exits.set_all_memory(memphi); + + // Add a return value to the exit state. (Do not push it yet.) + if (tf()->range()->cnt() > TypeFunc::Parms) { + const Type* ret_type = tf()->range()->field_at(TypeFunc::Parms); + // Don't "bind" an unloaded return klass to the ret_phi. If the klass + // becomes loaded during the subsequent parsing, the loaded and unloaded + // types will not join when we transform and push in do_exits(). + const TypeOopPtr* ret_oop_type = ret_type->isa_oopptr(); + if (ret_oop_type && !ret_oop_type->klass()->is_loaded()) { + ret_type = TypeOopPtr::BOTTOM; + } + int ret_size = type2size[ret_type->basic_type()]; + Node* ret_phi = new (C, region->req()) PhiNode(region, ret_type); + _exits.ensure_stack(ret_size); + assert((int)(tf()->range()->cnt() - TypeFunc::Parms) == ret_size, "good tf range"); + assert(method()->return_type()->size() == ret_size, "tf agrees w/ method"); + _exits.set_argument(0, ret_phi); // here is where the parser finds it + // Note: ret_phi is not yet pushed, until do_exits. + } +} + + +//----------------------------build_start_state------------------------------- +// Construct a state which contains only the incoming arguments from an +// unknown caller. The method & bci will be NULL & InvocationEntryBci. +JVMState* Compile::build_start_state(StartNode* start, const TypeFunc* tf) { + int arg_size = tf->domain()->cnt(); + int max_size = MAX2(arg_size, (int)tf->range()->cnt()); + JVMState* jvms = new (this) JVMState(max_size - TypeFunc::Parms); + SafePointNode* map = new (this, max_size) SafePointNode(max_size, NULL); + record_for_igvn(map); + assert(arg_size == TypeFunc::Parms + (is_osr_compilation() ? 1 : method()->arg_size()), "correct arg_size"); + Node_Notes* old_nn = default_node_notes(); + if (old_nn != NULL && has_method()) { + Node_Notes* entry_nn = old_nn->clone(this); + JVMState* entry_jvms = new(this) JVMState(method(), old_nn->jvms()); + entry_jvms->set_offsets(0); + entry_jvms->set_bci(entry_bci()); + entry_nn->set_jvms(entry_jvms); + set_default_node_notes(entry_nn); + } + uint i; + for (i = 0; i < (uint)arg_size; i++) { + Node* parm = initial_gvn()->transform(new (this, 1) ParmNode(start, i)); + map->init_req(i, parm); + // Record all these guys for later GVN. + record_for_igvn(parm); + } + for (; i < map->req(); i++) { + map->init_req(i, top()); + } + assert(jvms->argoff() == TypeFunc::Parms, "parser gets arguments here"); + set_default_node_notes(old_nn); + map->set_jvms(jvms); + jvms->set_map(map); + return jvms; +} + +//-----------------------------make_node_notes--------------------------------- +Node_Notes* Parse::make_node_notes(Node_Notes* caller_nn) { + if (caller_nn == NULL) return NULL; + Node_Notes* nn = caller_nn->clone(C); + JVMState* caller_jvms = nn->jvms(); + JVMState* jvms = new (C) JVMState(method(), caller_jvms); + jvms->set_offsets(0); + jvms->set_bci(_entry_bci); + nn->set_jvms(jvms); + return nn; +} + + +//--------------------------return_values-------------------------------------- +void Compile::return_values(JVMState* jvms) { + GraphKit kit(jvms); + Node* ret = new (this, TypeFunc::Parms) ReturnNode(TypeFunc::Parms, + kit.control(), + kit.i_o(), + kit.reset_memory(), + kit.frameptr(), + kit.returnadr()); + // Add zero or 1 return values + int ret_size = tf()->range()->cnt() - TypeFunc::Parms; + if (ret_size > 0) { + kit.inc_sp(-ret_size); // pop the return value(s) + kit.sync_jvms(); + ret->add_req(kit.argument(0)); + // Note: The second dummy edge is not needed by a ReturnNode. + } + // bind it to root + root()->add_req(ret); + record_for_igvn(ret); + initial_gvn()->transform_no_reclaim(ret); +} + +//------------------------rethrow_exceptions----------------------------------- +// Bind all exception states in the list into a single RethrowNode. +void Compile::rethrow_exceptions(JVMState* jvms) { + GraphKit kit(jvms); + if (!kit.has_exceptions()) return; // nothing to generate + // Load my combined exception state into the kit, with all phis transformed: + SafePointNode* ex_map = kit.combine_and_pop_all_exception_states(); + Node* ex_oop = kit.use_exception_state(ex_map); + RethrowNode* exit = new (this, TypeFunc::Parms + 1) RethrowNode(kit.control(), + kit.i_o(), kit.reset_memory(), + kit.frameptr(), kit.returnadr(), + // like a return but with exception input + ex_oop); + // bind to root + root()->add_req(exit); + record_for_igvn(exit); + initial_gvn()->transform_no_reclaim(exit); +} + +bool Parse::can_rerun_bytecode() { + switch (bc()) { + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + case Bytecodes::_getfield: + case Bytecodes::_putfield: + case Bytecodes::_getstatic: + case Bytecodes::_putstatic: + case Bytecodes::_arraylength: + case Bytecodes::_baload: + case Bytecodes::_caload: + case Bytecodes::_iaload: + case Bytecodes::_saload: + case Bytecodes::_faload: + case Bytecodes::_aaload: + case Bytecodes::_laload: + case Bytecodes::_daload: + case Bytecodes::_bastore: + case Bytecodes::_castore: + case Bytecodes::_iastore: + case Bytecodes::_sastore: + case Bytecodes::_fastore: + case Bytecodes::_aastore: + case Bytecodes::_lastore: + case Bytecodes::_dastore: + case Bytecodes::_irem: + case Bytecodes::_idiv: + case Bytecodes::_lrem: + case Bytecodes::_ldiv: + case Bytecodes::_frem: + case Bytecodes::_fdiv: + case Bytecodes::_drem: + case Bytecodes::_ddiv: + case Bytecodes::_checkcast: + case Bytecodes::_instanceof: + case Bytecodes::_athrow: + case Bytecodes::_anewarray: + case Bytecodes::_newarray: + case Bytecodes::_multianewarray: + case Bytecodes::_new: + case Bytecodes::_monitorenter: // can re-run initial null check, only + case Bytecodes::_return: + return true; + break; + + case Bytecodes::_invokestatic: + case Bytecodes::_invokespecial: + case Bytecodes::_invokevirtual: + case Bytecodes::_invokeinterface: + return false; + break; + + default: + assert(false, "unexpected bytecode produced an exception"); + return true; + } +} + +//---------------------------do_exceptions------------------------------------- +// Process exceptions arising from the current bytecode. +// Send caught exceptions to the proper handler within this method. +// Unhandled exceptions feed into _exit. +void Parse::do_exceptions() { + if (!has_exceptions()) return; + + if (failing()) { + // Pop them all off and throw them away. + while (pop_exception_state() != NULL) ; + return; + } + + // Make sure we can classify this bytecode if we need to. + debug_only(can_rerun_bytecode()); + + PreserveJVMState pjvms(this, false); + + SafePointNode* ex_map; + while ((ex_map = pop_exception_state()) != NULL) { + if (!method()->has_exception_handlers()) { + // Common case: Transfer control outward. + // Doing it this early allows the exceptions to common up + // even between adjacent method calls. + throw_to_exit(ex_map); + } else { + // Have to look at the exception first. + assert(stopped(), "catch_inline_exceptions trashes the map"); + catch_inline_exceptions(ex_map); + stop_and_kill_map(); // we used up this exception state; kill it + } + } + + // We now return to our regularly scheduled program: +} + +//---------------------------throw_to_exit------------------------------------- +// Merge the given map into an exception exit from this method. +// The exception exit will handle any unlocking of receiver. +// The ex_oop must be saved within the ex_map, unlike merge_exception. +void Parse::throw_to_exit(SafePointNode* ex_map) { + // Pop the JVMS to (a copy of) the caller. + GraphKit caller; + caller.set_map_clone(_caller->map()); + caller.set_bci(_caller->bci()); + caller.set_sp(_caller->sp()); + // Copy out the standard machine state: + for (uint i = 0; i < TypeFunc::Parms; i++) { + caller.map()->set_req(i, ex_map->in(i)); + } + // ...and the exception: + Node* ex_oop = saved_ex_oop(ex_map); + SafePointNode* caller_ex_map = caller.make_exception_state(ex_oop); + // Finally, collect the new exception state in my exits: + _exits.add_exception_state(caller_ex_map); +} + +//------------------------------do_exits--------------------------------------- +void Parse::do_exits() { + set_parse_bci(InvocationEntryBci); + + // Now peephole on the return bits + Node* region = _exits.control(); + _exits.set_control(gvn().transform(region)); + + Node* iophi = _exits.i_o(); + _exits.set_i_o(gvn().transform(iophi)); + + if (wrote_final()) { + // This method (which must be a constructor by the rules of Java) + // wrote a final. The effects of all initializations must be + // committed to memory before any code after the constructor + // publishes the reference to the newly constructor object. + // Rather than wait for the publication, we simply block the + // writes here. Rather than put a barrier on only those writes + // which are required to complete, we force all writes to complete. + // + // "All bets are off" unless the first publication occurs after a + // normal return from the constructor. We do not attempt to detect + // such unusual early publications. But no barrier is needed on + // exceptional returns, since they cannot publish normally. + // + _exits.insert_mem_bar(Op_MemBarRelease); +#ifndef PRODUCT + if (PrintOpto && (Verbose || WizardMode)) { + method()->print_name(); + tty->print_cr(" writes finals and needs a memory barrier"); + } +#endif + } + + for (MergeMemStream mms(_exits.merged_memory()); mms.next_non_empty(); ) { + // transform each slice of the original memphi: + mms.set_memory(_gvn.transform(mms.memory())); + } + + if (tf()->range()->cnt() > TypeFunc::Parms) { + const Type* ret_type = tf()->range()->field_at(TypeFunc::Parms); + Node* ret_phi = _gvn.transform( _exits.argument(0) ); + assert(_exits.control()->is_top() || !_gvn.type(ret_phi)->empty(), "return value must be well defined"); + _exits.push_node(ret_type->basic_type(), ret_phi); + } + + // Note: Logic for creating and optimizing the ReturnNode is in Compile. + + // Unlock along the exceptional paths. + // This is done late so that we can common up equivalent exceptions + // (e.g., null checks) arising from multiple points within this method. + // See GraphKit::add_exception_state, which performs the commoning. + bool do_synch = method()->is_synchronized() && GenerateSynchronizationCode; + + // record exit from a method if compiled while Dtrace is turned on. + if (do_synch || DTraceMethodProbes) { + // First move the exception list out of _exits: + GraphKit kit(_exits.transfer_exceptions_into_jvms()); + SafePointNode* normal_map = kit.map(); // keep this guy safe + // Now re-collect the exceptions into _exits: + SafePointNode* ex_map; + while ((ex_map = kit.pop_exception_state()) != NULL) { + Node* ex_oop = kit.use_exception_state(ex_map); + // Force the exiting JVM state to have this method at InvocationEntryBci. + // The exiting JVM state is otherwise a copy of the calling JVMS. + JVMState* caller = kit.jvms(); + JVMState* ex_jvms = caller->clone_shallow(C); + ex_jvms->set_map(kit.clone_map()); + ex_jvms->map()->set_jvms(ex_jvms); + ex_jvms->set_bci( InvocationEntryBci); + kit.set_jvms(ex_jvms); + if (do_synch) { + // Add on the synchronized-method box/object combo + kit.map()->push_monitor(_synch_lock); + // Unlock! + kit.shared_unlock(_synch_lock->box_node(), _synch_lock->obj_node()); + } + if (DTraceMethodProbes) { + kit.make_dtrace_method_exit(method()); + } + // Done with exception-path processing. + ex_map = kit.make_exception_state(ex_oop); + assert(ex_jvms->same_calls_as(ex_map->jvms()), "sanity"); + // Pop the last vestige of this method: + ex_map->set_jvms(caller->clone_shallow(C)); + ex_map->jvms()->set_map(ex_map); + _exits.push_exception_state(ex_map); + } + assert(_exits.map() == normal_map, "keep the same return state"); + } + + { + // Capture very early exceptions (receiver null checks) from caller JVMS + GraphKit caller(_caller); + SafePointNode* ex_map; + while ((ex_map = caller.pop_exception_state()) != NULL) { + _exits.add_exception_state(ex_map); + } + } +} + +//-----------------------------create_entry_map------------------------------- +// Initialize our parser map to contain the types at method entry. +// For OSR, the map contains a single RawPtr parameter. +// Initial monitor locking for sync. methods is performed by do_method_entry. +SafePointNode* Parse::create_entry_map() { + // Check for really stupid bail-out cases. + uint len = TypeFunc::Parms + method()->max_locals() + method()->max_stack(); + if (len >= 32760) { + C->record_method_not_compilable_all_tiers("too many local variables"); + return NULL; + } + + // If this is an inlined method, we may have to do a receiver null check. + if (_caller->has_method() && is_normal_parse() && !method()->is_static()) { + GraphKit kit(_caller); + kit.null_check_receiver(method()); + _caller = kit.transfer_exceptions_into_jvms(); + if (kit.stopped()) { + _exits.add_exception_states_from(_caller); + _exits.set_jvms(_caller); + return NULL; + } + } + + assert(method() != NULL, "parser must have a method"); + + // Create an initial safepoint to hold JVM state during parsing + JVMState* jvms = new (C) JVMState(method(), _caller->has_method() ? _caller : NULL); + set_map(new (C, len) SafePointNode(len, jvms)); + jvms->set_map(map()); + record_for_igvn(map()); + assert(jvms->endoff() == len, "correct jvms sizing"); + + SafePointNode* inmap = _caller->map(); + assert(inmap != NULL, "must have inmap"); + + uint i; + + // Pass thru the predefined input parameters. + for (i = 0; i < TypeFunc::Parms; i++) { + map()->init_req(i, inmap->in(i)); + } + + if (depth() == 1) { + assert(map()->memory()->Opcode() == Op_Parm, ""); + // Insert the memory aliasing node + set_all_memory(reset_memory()); + } + assert(merged_memory(), ""); + + // Now add the locals which are initially bound to arguments: + uint arg_size = tf()->domain()->cnt(); + ensure_stack(arg_size - TypeFunc::Parms); // OSR methods have funny args + for (i = TypeFunc::Parms; i < arg_size; i++) { + map()->init_req(i, inmap->argument(_caller, i - TypeFunc::Parms)); + } + + // Clear out the rest of the map (locals and stack) + for (i = arg_size; i < len; i++) { + map()->init_req(i, top()); + } + + SafePointNode* entry_map = stop(); + return entry_map; +} + +//-----------------------------do_method_entry-------------------------------- +// Emit any code needed in the pseudo-block before BCI zero. +// The main thing to do is lock the receiver of a synchronized method. +void Parse::do_method_entry() { + set_parse_bci(InvocationEntryBci); // Pseudo-BCP + set_sp(0); // Java Stack Pointer + + NOT_PRODUCT( count_compiled_calls(true/*at_method_entry*/, false/*is_inline*/); ) + + if (DTraceMethodProbes) { + make_dtrace_method_entry(method()); + } + + // If the method is synchronized, we need to construct a lock node, attach + // it to the Start node, and pin it there. + if (method()->is_synchronized()) { + // Insert a FastLockNode right after the Start which takes as arguments + // the current thread pointer, the "this" pointer & the address of the + // stack slot pair used for the lock. The "this" pointer is a projection + // off the start node, but the locking spot has to be constructed by + // creating a ConLNode of 0, and boxing it with a BoxLockNode. The BoxLockNode + // becomes the second argument to the FastLockNode call. The + // FastLockNode becomes the new control parent to pin it to the start. + + // Setup Object Pointer + Node *lock_obj = NULL; + if(method()->is_static()) { + ciInstance* mirror = _method->holder()->java_mirror(); + const TypeInstPtr *t_lock = TypeInstPtr::make(mirror); + lock_obj = makecon(t_lock); + } else { // Else pass the "this" pointer, + lock_obj = local(0); // which is Parm0 from StartNode + } + // Clear out dead values from the debug info. + kill_dead_locals(); + // Build the FastLockNode + _synch_lock = shared_lock(lock_obj); + } + + if (depth() == 1) { + increment_and_test_invocation_counter(Tier2CompileThreshold); + } +} + +//------------------------------init_blocks------------------------------------ +// Initialize our parser map to contain the types/monitors at method entry. +void Parse::init_blocks() { + // Create the blocks. + _block_count = flow()->block_count(); + _blocks = NEW_RESOURCE_ARRAY(Block, _block_count); + Copy::zero_to_bytes(_blocks, sizeof(Block)*_block_count); + + int po; + + // Initialize the structs. + for (po = 0; po < block_count(); po++) { + Block* block = pre_order_at(po); + block->init_node(this, po); + } + + // Collect predecessor and successor information. + for (po = 0; po < block_count(); po++) { + Block* block = pre_order_at(po); + block->init_graph(this); + } +} + +//-------------------------------init_node------------------------------------- +void Parse::Block::init_node(Parse* outer, int po) { + _flow = outer->flow()->pre_order_at(po); + _pred_count = 0; + _preds_parsed = 0; + _count = 0; + assert(pred_count() == 0 && preds_parsed() == 0, "sanity"); + assert(!(is_merged() || is_parsed() || is_handler()), "sanity"); + assert(_live_locals.size() == 0, "sanity"); + + // entry point has additional predecessor + if (flow()->is_start()) _pred_count++; + assert(flow()->is_start() == (this == outer->start_block()), ""); +} + +//-------------------------------init_graph------------------------------------ +void Parse::Block::init_graph(Parse* outer) { + // Create the successor list for this parser block. + GrowableArray* tfs = flow()->successors(); + GrowableArray* tfe = flow()->exceptions(); + int ns = tfs->length(); + int ne = tfe->length(); + _num_successors = ns; + _all_successors = ns+ne; + _successors = (ns+ne == 0) ? NULL : NEW_RESOURCE_ARRAY(Block*, ns+ne); + int p = 0; + for (int i = 0; i < ns+ne; i++) { + ciTypeFlow::Block* tf2 = (i < ns) ? tfs->at(i) : tfe->at(i-ns); + Block* block2 = outer->pre_order_at(tf2->pre_order()); + _successors[i] = block2; + + // Accumulate pred info for the other block, too. + if (i < ns) { + block2->_pred_count++; + } else { + block2->_is_handler = true; + } + + #ifdef ASSERT + // A block's successors must be distinguishable by BCI. + // That is, no bytecode is allowed to branch to two different + // clones of the same code location. + for (int j = 0; j < i; j++) { + Block* block1 = _successors[j]; + if (block1 == block2) continue; // duplicates are OK + assert(block1->start() != block2->start(), "successors have unique bcis"); + } + #endif + } + + // Note: We never call next_path_num along exception paths, so they + // never get processed as "ready". Also, the input phis of exception + // handlers get specially processed, so that +} + +//---------------------------successor_for_bci--------------------------------- +Parse::Block* Parse::Block::successor_for_bci(int bci) { + for (int i = 0; i < all_successors(); i++) { + Block* block2 = successor_at(i); + if (block2->start() == bci) return block2; + } + // We can actually reach here if ciTypeFlow traps out a block + // due to an unloaded class, and concurrently with compilation the + // class is then loaded, so that a later phase of the parser is + // able to see more of the bytecode CFG. Or, the flow pass and + // the parser can have a minor difference of opinion about executability + // of bytecodes. For example, "obj.field = null" is executable even + // if the field's type is an unloaded class; the flow pass used to + // make a trap for such code. + return NULL; +} + + +//-----------------------------stack_type_at----------------------------------- +const Type* Parse::Block::stack_type_at(int i) const { + return get_type(flow()->stack_type_at(i)); +} + + +//-----------------------------local_type_at----------------------------------- +const Type* Parse::Block::local_type_at(int i) const { + // Make dead locals fall to bottom. + if (_live_locals.size() == 0) { + MethodLivenessResult live_locals = flow()->outer()->method()->liveness_at_bci(start()); + // This bitmap can be zero length if we saw a breakpoint. + // In such cases, pretend they are all live. + ((Block*)this)->_live_locals = live_locals; + } + if (_live_locals.size() > 0 && !_live_locals.at(i)) + return Type::BOTTOM; + + return get_type(flow()->local_type_at(i)); +} + + +#ifndef PRODUCT + +//----------------------------name_for_bc-------------------------------------- +// helper method for BytecodeParseHistogram +static const char* name_for_bc(int i) { + return Bytecodes::is_defined(i) ? Bytecodes::name(Bytecodes::cast(i)) : "xxxunusedxxx"; +} + +//----------------------------BytecodeParseHistogram------------------------------------ +Parse::BytecodeParseHistogram::BytecodeParseHistogram(Parse *p, Compile *c) { + _parser = p; + _compiler = c; + if( ! _initialized ) { _initialized = true; reset(); } +} + +//----------------------------current_count------------------------------------ +int Parse::BytecodeParseHistogram::current_count(BPHType bph_type) { + switch( bph_type ) { + case BPH_transforms: { return _parser->gvn().made_progress(); } + case BPH_values: { return _parser->gvn().made_new_values(); } + default: { ShouldNotReachHere(); return 0; } + } +} + +//----------------------------initialized-------------------------------------- +bool Parse::BytecodeParseHistogram::initialized() { return _initialized; } + +//----------------------------reset-------------------------------------------- +void Parse::BytecodeParseHistogram::reset() { + int i = Bytecodes::number_of_codes; + while (i-- > 0) { _bytecodes_parsed[i] = 0; _nodes_constructed[i] = 0; _nodes_transformed[i] = 0; _new_values[i] = 0; } +} + +//----------------------------set_initial_state-------------------------------- +// Record info when starting to parse one bytecode +void Parse::BytecodeParseHistogram::set_initial_state( Bytecodes::Code bc ) { + if( PrintParseStatistics && !_parser->is_osr_parse() ) { + _initial_bytecode = bc; + _initial_node_count = _compiler->unique(); + _initial_transforms = current_count(BPH_transforms); + _initial_values = current_count(BPH_values); + } +} + +//----------------------------record_change-------------------------------- +// Record results of parsing one bytecode +void Parse::BytecodeParseHistogram::record_change() { + if( PrintParseStatistics && !_parser->is_osr_parse() ) { + ++_bytecodes_parsed[_initial_bytecode]; + _nodes_constructed [_initial_bytecode] += (_compiler->unique() - _initial_node_count); + _nodes_transformed [_initial_bytecode] += (current_count(BPH_transforms) - _initial_transforms); + _new_values [_initial_bytecode] += (current_count(BPH_values) - _initial_values); + } +} + + +//----------------------------print-------------------------------------------- +void Parse::BytecodeParseHistogram::print(float cutoff) { + ResourceMark rm; + // print profile + int total = 0; + int i = 0; + for( i = 0; i < Bytecodes::number_of_codes; ++i ) { total += _bytecodes_parsed[i]; } + int abs_sum = 0; + tty->cr(); //0123456789012345678901234567890123456789012345678901234567890123456789 + tty->print_cr("Histogram of %d parsed bytecodes:", total); + if( total == 0 ) { return; } + tty->cr(); + tty->print_cr("absolute: count of compiled bytecodes of this type"); + tty->print_cr("relative: percentage contribution to compiled nodes"); + tty->print_cr("nodes : Average number of nodes constructed per bytecode"); + tty->print_cr("rnodes : Significance towards total nodes constructed, (nodes*relative)"); + tty->print_cr("transforms: Average amount of tranform progress per bytecode compiled"); + tty->print_cr("values : Average number of node values improved per bytecode"); + tty->print_cr("name : Bytecode name"); + tty->cr(); + tty->print_cr(" absolute relative nodes rnodes transforms values name"); + tty->print_cr("----------------------------------------------------------------------"); + while (--i > 0) { + int abs = _bytecodes_parsed[i]; + float rel = abs * 100.0F / total; + float nodes = _bytecodes_parsed[i] == 0 ? 0 : (1.0F * _nodes_constructed[i])/_bytecodes_parsed[i]; + float rnodes = _bytecodes_parsed[i] == 0 ? 0 : rel * nodes; + float xforms = _bytecodes_parsed[i] == 0 ? 0 : (1.0F * _nodes_transformed[i])/_bytecodes_parsed[i]; + float values = _bytecodes_parsed[i] == 0 ? 0 : (1.0F * _new_values [i])/_bytecodes_parsed[i]; + if (cutoff <= rel) { + tty->print_cr("%10d %7.2f%% %6.1f %6.2f %6.1f %6.1f %s", abs, rel, nodes, rnodes, xforms, values, name_for_bc(i)); + abs_sum += abs; + } + } + tty->print_cr("----------------------------------------------------------------------"); + float rel_sum = abs_sum * 100.0F / total; + tty->print_cr("%10d %7.2f%% (cutoff = %.2f%%)", abs_sum, rel_sum, cutoff); + tty->print_cr("----------------------------------------------------------------------"); + tty->cr(); +} +#endif + +//----------------------------load_state_from---------------------------------- +// Load block/map/sp. But not do not touch iter/bci. +void Parse::load_state_from(Block* block) { + set_block(block); + // load the block's JVM state: + set_map(block->start_map()); + set_sp( block->start_sp()); +} + + +//-----------------------------record_state------------------------------------ +void Parse::Block::record_state(Parse* p) { + assert(!is_merged(), "can only record state once, on 1st inflow"); + assert(start_sp() == p->sp(), "stack pointer must agree with ciTypeFlow"); + set_start_map(p->stop()); +} + + +//------------------------------do_one_block----------------------------------- +void Parse::do_one_block() { + if (TraceOptoParse) { + Block *b = block(); + int ns = b->num_successors(); + int nt = b->all_successors(); + + tty->print("Parsing block #%d at bci [%d,%d), successors: ", + block()->pre_order(), block()->start(), block()->limit()); + for (int i = 0; i < nt; i++) { + tty->print((( i < ns) ? " %d" : " %d(e)"), b->successor_at(i)->pre_order()); + } + tty->print_cr(""); + } + + assert(block()->is_merged(), "must be merged before being parsed"); + block()->mark_parsed(); + ++_blocks_parsed; + + // Set iterator to start of block. + iter().reset_to_bci(block()->start()); + + CompileLog* log = C->log(); + + // Parse bytecodes + while (!stopped() && !failing()) { + iter().next(); + + // Learn the current bci from the iterator: + set_parse_bci(iter().cur_bci()); + + if (bci() == block()->limit()) { + // Do not walk into the next block until directed by do_all_blocks. + merge(bci()); + break; + } + assert(bci() < block()->limit(), "bci still in block"); + + if (log != NULL) { + // Output an optional context marker, to help place actions + // that occur during parsing of this BC. If there is no log + // output until the next context string, this context string + // will be silently ignored. + log->context()->reset(); + log->context()->print_cr("", (int)bc(), bci()); + } + + if (block()->has_trap_at(bci())) { + // We must respect the flow pass's traps, because it will refuse + // to produce successors for trapping blocks. + int trap_index = block()->flow()->trap_index(); + assert(trap_index != 0, "trap index must be valid"); + uncommon_trap(trap_index); + break; + } + + NOT_PRODUCT( parse_histogram()->set_initial_state(bc()); ); + +#ifdef ASSERT + int pre_bc_sp = sp(); + int inputs, depth; + bool have_se = !stopped() && compute_stack_effects(inputs, depth); + assert(!have_se || pre_bc_sp >= inputs, "have enough stack to execute this BC"); +#endif //ASSERT + + do_one_bytecode(); + + assert(!have_se || stopped() || failing() || (sp() - pre_bc_sp) == depth, "correct depth prediction"); + + do_exceptions(); + + NOT_PRODUCT( parse_histogram()->record_change(); ); + + if (log != NULL) log->context()->reset(); // done w/ this one + + // Fall into next bytecode. Each bytecode normally has 1 sequential + // successor which is typically made ready by visiting this bytecode. + // If the successor has several predecessors, then it is a merge + // point, starts a new basic block, and is handled like other basic blocks. + } +} + + +//------------------------------merge------------------------------------------ +void Parse::set_parse_bci(int bci) { + set_bci(bci); + Node_Notes* nn = C->default_node_notes(); + if (nn == NULL) return; + + // Collect debug info for inlined calls unless -XX:-DebugInlinedCalls. + if (!DebugInlinedCalls && depth() > 1) { + return; + } + + // Update the JVMS annotation, if present. + JVMState* jvms = nn->jvms(); + if (jvms != NULL && jvms->bci() != bci) { + // Update the JVMS. + jvms = jvms->clone_shallow(C); + jvms->set_bci(bci); + nn->set_jvms(jvms); + } +} + +//------------------------------merge------------------------------------------ +// Merge the current mapping into the basic block starting at bci +void Parse::merge(int target_bci) { + Block* target = successor_for_bci(target_bci); + if (target == NULL) { handle_missing_successor(target_bci); return; } + assert(!target->is_ready(), "our arrival must be expected"); + int pnum = target->next_path_num(); + merge_common(target, pnum); +} + +//-------------------------merge_new_path-------------------------------------- +// Merge the current mapping into the basic block, using a new path +void Parse::merge_new_path(int target_bci) { + Block* target = successor_for_bci(target_bci); + if (target == NULL) { handle_missing_successor(target_bci); return; } + assert(!target->is_ready(), "new path into frozen graph"); + int pnum = target->add_new_path(); + merge_common(target, pnum); +} + +//-------------------------merge_exception------------------------------------- +// Merge the current mapping into the basic block starting at bci +// The ex_oop must be pushed on the stack, unlike throw_to_exit. +void Parse::merge_exception(int target_bci) { + assert(sp() == 1, "must have only the throw exception on the stack"); + Block* target = successor_for_bci(target_bci); + if (target == NULL) { handle_missing_successor(target_bci); return; } + assert(target->is_handler(), "exceptions are handled by special blocks"); + int pnum = target->add_new_path(); + merge_common(target, pnum); +} + +//--------------------handle_missing_successor--------------------------------- +void Parse::handle_missing_successor(int target_bci) { +#ifndef PRODUCT + Block* b = block(); + int trap_bci = b->flow()->has_trap()? b->flow()->trap_bci(): -1; + tty->print_cr("### Missing successor at bci:%d for block #%d (trap_bci:%d)", target_bci, b->pre_order(), trap_bci); +#endif + ShouldNotReachHere(); +} + +//--------------------------merge_common--------------------------------------- +void Parse::merge_common(Parse::Block* target, int pnum) { + if (TraceOptoParse) { + tty->print("Merging state at block #%d bci:%d", target->pre_order(), target->start()); + } + + // Zap extra stack slots to top + assert(sp() == target->start_sp(), ""); + clean_stack(sp()); + + if (!target->is_merged()) { // No prior mapping at this bci + if (TraceOptoParse) { tty->print(" with empty state"); } + + // If this path is dead, do not bother capturing it as a merge. + // It is "as if" we had 1 fewer predecessors from the beginning. + if (stopped()) { + if (TraceOptoParse) tty->print_cr(", but path is dead and doesn't count"); + return; + } + + // Record that a new block has been merged. + ++_blocks_merged; + + // Make a region if we know there are multiple or unpredictable inputs. + // (Also, if this is a plain fall-through, we might see another region, + // which must not be allowed into this block's map.) + if (pnum > PhiNode::Input // Known multiple inputs. + || target->is_handler() // These have unpredictable inputs. + || control()->is_Region()) { // We must hide this guy. + // Add a Region to start the new basic block. Phis will be added + // later lazily. + int edges = target->pred_count(); + if (edges < pnum) edges = pnum; // might be a new path! + Node *r = new (C, edges+1) RegionNode(edges+1); + gvn().set_type(r, Type::CONTROL); + record_for_igvn(r); + // zap all inputs to NULL for debugging (done in Node(uint) constructor) + // for (int j = 1; j < edges+1; j++) { r->init_req(j, NULL); } + r->init_req(pnum, control()); + set_control(r); + } + + // Convert the existing Parser mapping into a mapping at this bci. + store_state_to(target); + assert(target->is_merged(), "do not come here twice"); + + } else { // Prior mapping at this bci + if (TraceOptoParse) { tty->print(" with previous state"); } + + // We must not manufacture more phis if the target is already parsed. + bool nophi = target->is_parsed(); + + SafePointNode* newin = map();// Hang on to incoming mapping + Block* save_block = block(); // Hang on to incoming block; + load_state_from(target); // Get prior mapping + + assert(newin->jvms()->locoff() == jvms()->locoff(), "JVMS layouts agree"); + assert(newin->jvms()->stkoff() == jvms()->stkoff(), "JVMS layouts agree"); + assert(newin->jvms()->monoff() == jvms()->monoff(), "JVMS layouts agree"); + assert(newin->jvms()->endoff() == jvms()->endoff(), "JVMS layouts agree"); + + // Iterate over my current mapping and the old mapping. + // Where different, insert Phi functions. + // Use any existing Phi functions. + assert(control()->is_Region(), "must be merging to a region"); + RegionNode* r = control()->as_Region(); + + // Compute where to merge into + // Merge incoming control path + r->set_req(pnum, newin->control()); + + if (pnum == 1) { // Last merge for this Region? + _gvn.transform_no_reclaim(r); + record_for_igvn(r); + } + + // Update all the non-control inputs to map: + assert(TypeFunc::Parms == newin->jvms()->locoff(), "parser map should contain only youngest jvms"); + for (uint j = 1; j < newin->req(); j++) { + Node* m = map()->in(j); // Current state of target. + Node* n = newin->in(j); // Incoming change to target state. + PhiNode* phi; + if (m->is_Phi() && m->as_Phi()->region() == r) + phi = m->as_Phi(); + else + phi = NULL; + if (m != n) { // Different; must merge + switch (j) { + // Frame pointer and Return Address never changes + case TypeFunc::FramePtr:// Drop m, use the original value + case TypeFunc::ReturnAdr: + break; + case TypeFunc::Memory: // Merge inputs to the MergeMem node + assert(phi == NULL, "the merge contains phis, not vice versa"); + merge_memory_edges(n->as_MergeMem(), pnum, nophi); + continue; + default: // All normal stuff + if (phi == NULL) phi = ensure_phi(j, nophi); + break; + } + } + // At this point, n might be top if: + // - there is no phi (because TypeFlow detected a conflict), or + // - the corresponding control edges is top (a dead incoming path) + // It is a bug if we create a phi which sees a garbage value on a live path. + + if (phi != NULL) { + assert(n != top() || r->in(pnum) == top(), "live value must not be garbage"); + assert(phi->region() == r, ""); + phi->set_req(pnum, n); // Then add 'n' to the merge + if (pnum == PhiNode::Input) { + // Last merge for this Phi. + // So far, Phis have had a reasonable type from ciTypeFlow. + // Now _gvn will join that with the meet of current inputs. + // BOTTOM is never permissible here, 'cause pessimistically + // Phis of pointers cannot lose the basic pointer type. + debug_only(const Type* bt1 = phi->bottom_type()); + assert(bt1 != Type::BOTTOM, "should not be building conflict phis"); + map()->set_req(j, _gvn.transform_no_reclaim(phi)); + debug_only(const Type* bt2 = phi->bottom_type()); + assert(bt2->higher_equal(bt1), "must be consistent with type-flow"); + record_for_igvn(phi); + } + } + } // End of for all values to be merged + + if (pnum == PhiNode::Input && + !r->in(0)) { // The occasional useless Region + assert(control() == r, ""); + set_control(r->nonnull_req()); + } + + // newin has been subsumed into the lazy merge, and is now dead. + set_block(save_block); + + stop(); // done with this guy, for now + } + + if (TraceOptoParse) { + tty->print_cr(" on path %d", pnum); + } + + // Done with this parser state. + assert(stopped(), ""); +} + + +//--------------------------merge_memory_edges--------------------------------- +void Parse::merge_memory_edges(MergeMemNode* n, int pnum, bool nophi) { + // (nophi means we must not create phis, because we already parsed here) + assert(n != NULL, ""); + // Merge the inputs to the MergeMems + MergeMemNode* m = merged_memory(); + + assert(control()->is_Region(), "must be merging to a region"); + RegionNode* r = control()->as_Region(); + + PhiNode* base = NULL; + MergeMemNode* remerge = NULL; + for (MergeMemStream mms(m, n); mms.next_non_empty2(); ) { + Node *p = mms.force_memory(); + Node *q = mms.memory2(); + if (mms.is_empty() && nophi) { + // Trouble: No new splits allowed after a loop body is parsed. + // Instead, wire the new split into a MergeMem on the backedge. + // The optimizer will sort it out, slicing the phi. + if (remerge == NULL) { + assert(base != NULL, ""); + assert(base->in(0) != NULL, "should not be xformed away"); + remerge = MergeMemNode::make(C, base->in(pnum)); + gvn().set_type(remerge, Type::MEMORY); + base->set_req(pnum, remerge); + } + remerge->set_memory_at(mms.alias_idx(), q); + continue; + } + assert(!q->is_MergeMem(), ""); + PhiNode* phi; + if (p != q) { + phi = ensure_memory_phi(mms.alias_idx(), nophi); + } else { + if (p->is_Phi() && p->as_Phi()->region() == r) + phi = p->as_Phi(); + else + phi = NULL; + } + // Insert q into local phi + if (phi != NULL) { + assert(phi->region() == r, ""); + p = phi; + phi->set_req(pnum, q); + if (mms.at_base_memory()) { + base = phi; // delay transforming it + } else if (pnum == 1) { + record_for_igvn(phi); + p = _gvn.transform_no_reclaim(phi); + } + mms.set_memory(p);// store back through the iterator + } + } + // Transform base last, in case we must fiddle with remerging. + if (base != NULL && pnum == 1) { + record_for_igvn(base); + m->set_base_memory( _gvn.transform_no_reclaim(base) ); + } +} + + +//------------------------ensure_phis_everywhere------------------------------- +void Parse::ensure_phis_everywhere() { + ensure_phi(TypeFunc::I_O); + + // Ensure a phi on all currently known memories. + for (MergeMemStream mms(merged_memory()); mms.next_non_empty(); ) { + ensure_memory_phi(mms.alias_idx()); + debug_only(mms.set_memory()); // keep the iterator happy + } + + // Note: This is our only chance to create phis for memory slices. + // If we miss a slice that crops up later, it will have to be + // merged into the base-memory phi that we are building here. + // Later, the optimizer will comb out the knot, and build separate + // phi-loops for each memory slice that matters. + + // Monitors must nest nicely and not get confused amongst themselves. + // Phi-ify everything up to the monitors, though. + uint monoff = map()->jvms()->monoff(); + uint nof_monitors = map()->jvms()->nof_monitors(); + + assert(TypeFunc::Parms == map()->jvms()->locoff(), "parser map should contain only youngest jvms"); + for (uint i = TypeFunc::Parms; i < monoff; i++) { + ensure_phi(i); + } + // Even monitors need Phis, though they are well-structured. + // This is true for OSR methods, and also for the rare cases where + // a monitor object is the subject of a replace_in_map operation. + // See bugs 4426707 and 5043395. + for (uint m = 0; m < nof_monitors; m++) { + ensure_phi(map()->jvms()->monitor_obj_offset(m)); + } +} + + +//-----------------------------add_new_path------------------------------------ +// Add a previously unaccounted predecessor to this block. +int Parse::Block::add_new_path() { + // If there is no map, return the lowest unused path number. + if (!is_merged()) return pred_count()+1; // there will be a map shortly + + SafePointNode* map = start_map(); + if (!map->control()->is_Region()) + return pred_count()+1; // there may be a region some day + RegionNode* r = map->control()->as_Region(); + + // Add new path to the region. + uint pnum = r->req(); + r->add_req(NULL); + + for (uint i = 1; i < map->req(); i++) { + Node* n = map->in(i); + if (i == TypeFunc::Memory) { + // Ensure a phi on all currently known memories. + for (MergeMemStream mms(n->as_MergeMem()); mms.next_non_empty(); ) { + Node* phi = mms.memory(); + if (phi->is_Phi() && phi->as_Phi()->region() == r) { + assert(phi->req() == pnum, "must be same size as region"); + phi->add_req(NULL); + } + } + } else { + if (n->is_Phi() && n->as_Phi()->region() == r) { + assert(n->req() == pnum, "must be same size as region"); + n->add_req(NULL); + } + } + } + + return pnum; +} + +//------------------------------ensure_phi------------------------------------- +// Turn the idx'th entry of the current map into a Phi +PhiNode *Parse::ensure_phi(int idx, bool nocreate) { + SafePointNode* map = this->map(); + Node* region = map->control(); + assert(region->is_Region(), ""); + + Node* o = map->in(idx); + assert(o != NULL, ""); + + if (o == top()) return NULL; // TOP always merges into TOP + + if (o->is_Phi() && o->as_Phi()->region() == region) { + return o->as_Phi(); + } + + // Now use a Phi here for merging + assert(!nocreate, "Cannot build a phi for a block already parsed."); + const JVMState* jvms = map->jvms(); + const Type* t; + if (jvms->is_loc(idx)) { + t = block()->local_type_at(idx - jvms->locoff()); + } else if (jvms->is_stk(idx)) { + t = block()->stack_type_at(idx - jvms->stkoff()); + } else if (jvms->is_mon(idx)) { + assert(!jvms->is_monitor_box(idx), "no phis for boxes"); + t = TypeInstPtr::BOTTOM; // this is sufficient for a lock object + } else if ((uint)idx < TypeFunc::Parms) { + t = o->bottom_type(); // Type::RETURN_ADDRESS or such-like. + } else { + assert(false, "no type information for this phi"); + } + + // If the type falls to bottom, then this must be a local that + // is mixing ints and oops or some such. Forcing it to top + // makes it go dead. + if (t == Type::BOTTOM) { + map->set_req(idx, top()); + return NULL; + } + + // Do not create phis for top either. + // A top on a non-null control flow must be an unused even after the.phi. + if (t == Type::TOP || t == Type::HALF) { + map->set_req(idx, top()); + return NULL; + } + + PhiNode* phi = PhiNode::make(region, o, t); + gvn().set_type(phi, t); + if (DoEscapeAnalysis) record_for_igvn(phi); + map->set_req(idx, phi); + return phi; +} + +//--------------------------ensure_memory_phi---------------------------------- +// Turn the idx'th slice of the current memory into a Phi +PhiNode *Parse::ensure_memory_phi(int idx, bool nocreate) { + MergeMemNode* mem = merged_memory(); + Node* region = control(); + assert(region->is_Region(), ""); + + Node *o = (idx == Compile::AliasIdxBot)? mem->base_memory(): mem->memory_at(idx); + assert(o != NULL && o != top(), ""); + + PhiNode* phi; + if (o->is_Phi() && o->as_Phi()->region() == region) { + phi = o->as_Phi(); + if (phi == mem->base_memory() && idx >= Compile::AliasIdxRaw) { + // clone the shared base memory phi to make a new memory split + assert(!nocreate, "Cannot build a phi for a block already parsed."); + const Type* t = phi->bottom_type(); + const TypePtr* adr_type = C->get_adr_type(idx); + phi = phi->slice_memory(adr_type); + gvn().set_type(phi, t); + } + return phi; + } + + // Now use a Phi here for merging + assert(!nocreate, "Cannot build a phi for a block already parsed."); + const Type* t = o->bottom_type(); + const TypePtr* adr_type = C->get_adr_type(idx); + phi = PhiNode::make(region, o, t, adr_type); + gvn().set_type(phi, t); + if (idx == Compile::AliasIdxBot) + mem->set_base_memory(phi); + else + mem->set_memory_at(idx, phi); + return phi; +} + +//------------------------------call_register_finalizer----------------------- +// Check the klass of the receiver and call register_finalizer if the +// class need finalization. +void Parse::call_register_finalizer() { + Node* receiver = local(0); + assert(receiver != NULL && receiver->bottom_type()->isa_instptr() != NULL, + "must have non-null instance type"); + + const TypeInstPtr *tinst = receiver->bottom_type()->isa_instptr(); + if (tinst != NULL && tinst->klass()->is_loaded() && !tinst->klass_is_exact()) { + // The type isn't known exactly so see if CHA tells us anything. + ciInstanceKlass* ik = tinst->klass()->as_instance_klass(); + if (!Dependencies::has_finalizable_subclass(ik)) { + // No finalizable subclasses so skip the dynamic check. + C->dependencies()->assert_has_no_finalizable_subclasses(ik); + return; + } + } + + // Insert a dynamic test for whether the instance needs + // finalization. In general this will fold up since the concrete + // class is often visible so the access flags are constant. + Node* klass_addr = basic_plus_adr( receiver, receiver, oopDesc::klass_offset_in_bytes() ); + Node* klass = _gvn.transform(new (C, 3) LoadKlassNode(NULL, immutable_memory(), klass_addr, TypeInstPtr::KLASS)); + + Node* access_flags_addr = basic_plus_adr(klass, klass, Klass::access_flags_offset_in_bytes() + sizeof(oopDesc)); + Node* access_flags = make_load(NULL, access_flags_addr, TypeInt::INT, T_INT); + + Node* mask = _gvn.transform(new (C, 3) AndINode(access_flags, intcon(JVM_ACC_HAS_FINALIZER))); + Node* check = _gvn.transform(new (C, 3) CmpINode(mask, intcon(0))); + Node* test = _gvn.transform(new (C, 2) BoolNode(check, BoolTest::ne)); + + IfNode* iff = create_and_map_if(control(), test, PROB_MAX, COUNT_UNKNOWN); + + RegionNode* result_rgn = new (C, 3) RegionNode(3); + record_for_igvn(result_rgn); + + Node *skip_register = _gvn.transform(new (C, 1) IfFalseNode(iff)); + result_rgn->init_req(1, skip_register); + + Node *needs_register = _gvn.transform(new (C, 1) IfTrueNode(iff)); + set_control(needs_register); + if (stopped()) { + // There is no slow path. + result_rgn->init_req(2, top()); + } else { + Node *call = make_runtime_call(RC_NO_LEAF, + OptoRuntime::register_finalizer_Type(), + OptoRuntime::register_finalizer_Java(), + NULL, TypePtr::BOTTOM, + receiver); + make_slow_call_ex(call, env()->Throwable_klass(), true); + + Node* fast_io = call->in(TypeFunc::I_O); + Node* fast_mem = call->in(TypeFunc::Memory); + // These two phis are pre-filled with copies of of the fast IO and Memory + Node* io_phi = PhiNode::make(result_rgn, fast_io, Type::ABIO); + Node* mem_phi = PhiNode::make(result_rgn, fast_mem, Type::MEMORY, TypePtr::BOTTOM); + + result_rgn->init_req(2, control()); + io_phi ->init_req(2, i_o()); + mem_phi ->init_req(2, reset_memory()); + + set_all_memory( _gvn.transform(mem_phi) ); + set_i_o( _gvn.transform(io_phi) ); + } + + set_control( _gvn.transform(result_rgn) ); +} + +//------------------------------return_current--------------------------------- +// Append current _map to _exit_return +void Parse::return_current(Node* value) { + if (RegisterFinalizersAtInit && + method()->intrinsic_id() == vmIntrinsics::_Object_init) { + call_register_finalizer(); + } + + // Do not set_parse_bci, so that return goo is credited to the return insn. + set_bci(InvocationEntryBci); + if (method()->is_synchronized() && GenerateSynchronizationCode) { + shared_unlock(_synch_lock->box_node(), _synch_lock->obj_node()); + } + if (DTraceMethodProbes) { + make_dtrace_method_exit(method()); + } + SafePointNode* exit_return = _exits.map(); + exit_return->in( TypeFunc::Control )->add_req( control() ); + exit_return->in( TypeFunc::I_O )->add_req( i_o () ); + Node *mem = exit_return->in( TypeFunc::Memory ); + for (MergeMemStream mms(mem->as_MergeMem(), merged_memory()); mms.next_non_empty2(); ) { + if (mms.is_empty()) { + // get a copy of the base memory, and patch just this one input + const TypePtr* adr_type = mms.adr_type(C); + Node* phi = mms.force_memory()->as_Phi()->slice_memory(adr_type); + assert(phi->as_Phi()->region() == mms.base_memory()->in(0), ""); + gvn().set_type_bottom(phi); + phi->del_req(phi->req()-1); // prepare to re-patch + mms.set_memory(phi); + } + mms.memory()->add_req(mms.memory2()); + } + + // frame pointer is always same, already captured + if (value != NULL) { + // If returning oops to an interface-return, there is a silent free + // cast from oop to interface allowed by the Verifier. Make it explicit + // here. + Node* phi = _exits.argument(0); + const TypeInstPtr *tr = phi->bottom_type()->isa_instptr(); + if( tr && tr->klass()->is_loaded() && + tr->klass()->is_interface() ) { + const TypeInstPtr *tp = value->bottom_type()->isa_instptr(); + if (tp && tp->klass()->is_loaded() && + !tp->klass()->is_interface()) { + // sharpen the type eagerly; this eases certain assert checking + if (tp->higher_equal(TypeInstPtr::NOTNULL)) + tr = tr->join(TypeInstPtr::NOTNULL)->is_instptr(); + value = _gvn.transform(new (C, 2) CheckCastPPNode(0,value,tr)); + } + } + phi->add_req(value); + } + + stop_and_kill_map(); // This CFG path dies here +} + + +//------------------------------add_safepoint---------------------------------- +void Parse::add_safepoint() { + // See if we can avoid this safepoint. No need for a SafePoint immediately + // after a Call (except Leaf Call) or another SafePoint. + Node *proj = control(); + bool add_poll_param = SafePointNode::needs_polling_address_input(); + uint parms = add_poll_param ? TypeFunc::Parms+1 : TypeFunc::Parms; + if( proj->is_Proj() ) { + Node *n0 = proj->in(0); + if( n0->is_Catch() ) { + n0 = n0->in(0)->in(0); + assert( n0->is_Call(), "expect a call here" ); + } + if( n0->is_Call() ) { + if( n0->as_Call()->guaranteed_safepoint() ) + return; + } else if( n0->is_SafePoint() && n0->req() >= parms ) { + return; + } + } + + // Clear out dead values from the debug info. + kill_dead_locals(); + + // Clone the JVM State + SafePointNode *sfpnt = new (C, parms) SafePointNode(parms, NULL); + + // Capture memory state BEFORE a SafePoint. Since we can block at a + // SafePoint we need our GC state to be safe; i.e. we need all our current + // write barriers (card marks) to not float down after the SafePoint so we + // must read raw memory. Likewise we need all oop stores to match the card + // marks. If deopt can happen, we need ALL stores (we need the correct JVM + // state on a deopt). + + // We do not need to WRITE the memory state after a SafePoint. The control + // edge will keep card-marks and oop-stores from floating up from below a + // SafePoint and our true dependency added here will keep them from floating + // down below a SafePoint. + + // Clone the current memory state + Node* mem = MergeMemNode::make(C, map()->memory()); + + mem = _gvn.transform(mem); + + // Pass control through the safepoint + sfpnt->init_req(TypeFunc::Control , control()); + // Fix edges normally used by a call + sfpnt->init_req(TypeFunc::I_O , top() ); + sfpnt->init_req(TypeFunc::Memory , mem ); + sfpnt->init_req(TypeFunc::ReturnAdr, top() ); + sfpnt->init_req(TypeFunc::FramePtr , top() ); + + // Create a node for the polling address + if( add_poll_param ) { + Node *polladr = ConPNode::make(C, (address)os::get_polling_page()); + sfpnt->init_req(TypeFunc::Parms+0, _gvn.transform(polladr)); + } + + // Fix up the JVM State edges + add_safepoint_edges(sfpnt); + Node *transformed_sfpnt = _gvn.transform(sfpnt); + set_control(transformed_sfpnt); + + // Provide an edge from root to safepoint. This makes the safepoint + // appear useful until the parse has completed. + if( OptoRemoveUseless && transformed_sfpnt->is_SafePoint() ) { + assert(C->root() != NULL, "Expect parse is still valid"); + C->root()->add_prec(transformed_sfpnt); + } +} + +#ifndef PRODUCT +//------------------------show_parse_info-------------------------------------- +void Parse::show_parse_info() { + InlineTree* ilt = NULL; + if (C->ilt() != NULL) { + JVMState* caller_jvms = is_osr_parse() ? caller()->caller() : caller(); + ilt = InlineTree::find_subtree_from_root(C->ilt(), caller_jvms, method()); + } + if (PrintCompilation && Verbose) { + if (depth() == 1) { + if( ilt->count_inlines() ) { + tty->print(" __inlined %d (%d bytes)", ilt->count_inlines(), + ilt->count_inline_bcs()); + tty->cr(); + } + } else { + if (method()->is_synchronized()) tty->print("s"); + if (method()->has_exception_handlers()) tty->print("!"); + // Check this is not the final compiled version + if (C->trap_can_recompile()) { + tty->print("-"); + } else { + tty->print(" "); + } + method()->print_short_name(); + if (is_osr_parse()) { + tty->print(" @ %d", osr_bci()); + } + tty->print(" (%d bytes)",method()->code_size()); + if (ilt->count_inlines()) { + tty->print(" __inlined %d (%d bytes)", ilt->count_inlines(), + ilt->count_inline_bcs()); + } + tty->cr(); + } + } + if (PrintOpto && (depth() == 1 || PrintOptoInlining)) { + // Print that we succeeded; suppress this message on the first osr parse. + + if (method()->is_synchronized()) tty->print("s"); + if (method()->has_exception_handlers()) tty->print("!"); + // Check this is not the final compiled version + if (C->trap_can_recompile() && depth() == 1) { + tty->print("-"); + } else { + tty->print(" "); + } + if( depth() != 1 ) { tty->print(" "); } // missing compile count + for (int i = 1; i < depth(); ++i) { tty->print(" "); } + method()->print_short_name(); + if (is_osr_parse()) { + tty->print(" @ %d", osr_bci()); + } + if (ilt->caller_bci() != -1) { + tty->print(" @ %d", ilt->caller_bci()); + } + tty->print(" (%d bytes)",method()->code_size()); + if (ilt->count_inlines()) { + tty->print(" __inlined %d (%d bytes)", ilt->count_inlines(), + ilt->count_inline_bcs()); + } + tty->cr(); + } +} + + +//------------------------------dump------------------------------------------- +// Dump information associated with the bytecodes of current _method +void Parse::dump() { + if( method() != NULL ) { + // Iterate over bytecodes + ciBytecodeStream iter(method()); + for( Bytecodes::Code bc = iter.next(); bc != ciBytecodeStream::EOBC() ; bc = iter.next() ) { + dump_bci( iter.cur_bci() ); + tty->cr(); + } + } +} + +// Dump information associated with a byte code index, 'bci' +void Parse::dump_bci(int bci) { + // Output info on merge-points, cloning, and within _jsr..._ret + // NYI + tty->print(" bci:%d", bci); +} + +#endif diff --git a/hotspot/src/share/vm/opto/parse2.cpp b/hotspot/src/share/vm/opto/parse2.cpp new file mode 100644 index 00000000000..2a5b0da07e8 --- /dev/null +++ b/hotspot/src/share/vm/opto/parse2.cpp @@ -0,0 +1,2171 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_parse2.cpp.incl" + +extern int explicit_null_checks_inserted, + explicit_null_checks_elided; + +//---------------------------------array_load---------------------------------- +void Parse::array_load(BasicType elem_type) { + const Type* elem = Type::TOP; + Node* adr = array_addressing(elem_type, 0, &elem); + if (stopped()) return; // guarenteed null or range check + _sp -= 2; // Pop array and index + const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(elem_type); + Node* ld = make_load(control(), adr, elem, elem_type, adr_type); + push(ld); +} + + +//--------------------------------array_store---------------------------------- +void Parse::array_store(BasicType elem_type) { + Node* adr = array_addressing(elem_type, 1); + if (stopped()) return; // guarenteed null or range check + Node* val = pop(); + _sp -= 2; // Pop array and index + const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(elem_type); + store_to_memory(control(), adr, val, elem_type, adr_type); +} + + +//------------------------------array_addressing------------------------------- +// Pull array and index from the stack. Compute pointer-to-element. +Node* Parse::array_addressing(BasicType type, int vals, const Type* *result2) { + Node *idx = peek(0+vals); // Get from stack without popping + Node *ary = peek(1+vals); // in case of exception + + // Null check the array base, with correct stack contents + ary = do_null_check(ary, T_ARRAY); + // Compile-time detect of null-exception? + if (stopped()) return top(); + + const TypeAryPtr* arytype = _gvn.type(ary)->is_aryptr(); + const TypeInt* sizetype = arytype->size(); + const Type* elemtype = arytype->elem(); + + if (UseUniqueSubclasses && result2 != NULL) { + const TypeInstPtr* toop = elemtype->isa_instptr(); + if (toop) { + if (toop->klass()->as_instance_klass()->unique_concrete_subklass()) { + // If we load from "AbstractClass[]" we must see "ConcreteSubClass". + const Type* subklass = Type::get_const_type(toop->klass()); + elemtype = subklass->join(elemtype); + } + } + } + + // Check for big class initializers with all constant offsets + // feeding into a known-size array. + const TypeInt* idxtype = _gvn.type(idx)->is_int(); + // See if the highest idx value is less than the lowest array bound, + // and if the idx value cannot be negative: + bool need_range_check = true; + if (idxtype->_hi < sizetype->_lo && idxtype->_lo >= 0) { + need_range_check = false; + if (C->log() != NULL) C->log()->elem("observe that='!need_range_check'"); + } + + if (!arytype->klass()->is_loaded()) { + // Only fails for some -Xcomp runs + // The class is unloaded. We have to run this bytecode in the interpreter. + uncommon_trap(Deoptimization::Reason_unloaded, + Deoptimization::Action_reinterpret, + arytype->klass(), "!loaded array"); + return top(); + } + + // Do the range check + if (GenerateRangeChecks && need_range_check) { + // Range is constant in array-oop, so we can use the original state of mem + Node* len = load_array_length(ary); + // Test length vs index (standard trick using unsigned compare) + Node* chk = _gvn.transform( new (C, 3) CmpUNode(idx, len) ); + BoolTest::mask btest = BoolTest::lt; + Node* tst = _gvn.transform( new (C, 2) BoolNode(chk, btest) ); + // Branch to failure if out of bounds + { BuildCutout unless(this, tst, PROB_MAX); + if (C->allow_range_check_smearing()) { + // Do not use builtin_throw, since range checks are sometimes + // made more stringent by an optimistic transformation. + // This creates "tentative" range checks at this point, + // which are not guaranteed to throw exceptions. + // See IfNode::Ideal, is_range_check, adjust_check. + uncommon_trap(Deoptimization::Reason_range_check, + Deoptimization::Action_make_not_entrant, + NULL, "range_check"); + } else { + // If we have already recompiled with the range-check-widening + // heroic optimization turned off, then we must really be throwing + // range check exceptions. + builtin_throw(Deoptimization::Reason_range_check, idx); + } + } + } + // Check for always knowing you are throwing a range-check exception + if (stopped()) return top(); + + Node* ptr = array_element_address( ary, idx, type, sizetype); + + if (result2 != NULL) *result2 = elemtype; + return ptr; +} + + +// returns IfNode +IfNode* Parse::jump_if_fork_int(Node* a, Node* b, BoolTest::mask mask) { + Node *cmp = _gvn.transform( new (C, 3) CmpINode( a, b)); // two cases: shiftcount > 32 and shiftcount <= 32 + Node *tst = _gvn.transform( new (C, 2) BoolNode( cmp, mask)); + IfNode *iff = create_and_map_if( control(), tst, ((mask == BoolTest::eq) ? PROB_STATIC_INFREQUENT : PROB_FAIR), COUNT_UNKNOWN ); + return iff; +} + +// return Region node +Node* Parse::jump_if_join(Node* iffalse, Node* iftrue) { + Node *region = new (C, 3) RegionNode(3); // 2 results + record_for_igvn(region); + region->init_req(1, iffalse); + region->init_req(2, iftrue ); + _gvn.set_type(region, Type::CONTROL); + region = _gvn.transform(region); + set_control (region); + return region; +} + + +//------------------------------helper for tableswitch------------------------- +void Parse::jump_if_true_fork(IfNode *iff, int dest_bci_if_true, int prof_table_index) { + // True branch, use existing map info + { PreserveJVMState pjvms(this); + Node *iftrue = _gvn.transform( new (C, 1) IfTrueNode (iff) ); + set_control( iftrue ); + profile_switch_case(prof_table_index); + merge_new_path(dest_bci_if_true); + } + + // False branch + Node *iffalse = _gvn.transform( new (C, 1) IfFalseNode(iff) ); + set_control( iffalse ); +} + +void Parse::jump_if_false_fork(IfNode *iff, int dest_bci_if_true, int prof_table_index) { + // True branch, use existing map info + { PreserveJVMState pjvms(this); + Node *iffalse = _gvn.transform( new (C, 1) IfFalseNode (iff) ); + set_control( iffalse ); + profile_switch_case(prof_table_index); + merge_new_path(dest_bci_if_true); + } + + // False branch + Node *iftrue = _gvn.transform( new (C, 1) IfTrueNode(iff) ); + set_control( iftrue ); +} + +void Parse::jump_if_always_fork(int dest_bci, int prof_table_index) { + // False branch, use existing map and control() + profile_switch_case(prof_table_index); + merge_new_path(dest_bci); +} + + +extern "C" { + static int jint_cmp(const void *i, const void *j) { + int a = *(jint *)i; + int b = *(jint *)j; + return a > b ? 1 : a < b ? -1 : 0; + } +} + + +// Default value for methodData switch indexing. Must be a negative value to avoid +// conflict with any legal switch index. +#define NullTableIndex -1 + +class SwitchRange : public StackObj { + // a range of integers coupled with a bci destination + jint _lo; // inclusive lower limit + jint _hi; // inclusive upper limit + int _dest; + int _table_index; // index into method data table + +public: + jint lo() const { return _lo; } + jint hi() const { return _hi; } + int dest() const { return _dest; } + int table_index() const { return _table_index; } + bool is_singleton() const { return _lo == _hi; } + + void setRange(jint lo, jint hi, int dest, int table_index) { + assert(lo <= hi, "must be a non-empty range"); + _lo = lo, _hi = hi; _dest = dest; _table_index = table_index; + } + bool adjoinRange(jint lo, jint hi, int dest, int table_index) { + assert(lo <= hi, "must be a non-empty range"); + if (lo == _hi+1 && dest == _dest && table_index == _table_index) { + _hi = hi; + return true; + } + return false; + } + + void set (jint value, int dest, int table_index) { + setRange(value, value, dest, table_index); + } + bool adjoin(jint value, int dest, int table_index) { + return adjoinRange(value, value, dest, table_index); + } + + void print(ciEnv* env) { + if (is_singleton()) + tty->print(" {%d}=>%d", lo(), dest()); + else if (lo() == min_jint) + tty->print(" {..%d}=>%d", hi(), dest()); + else if (hi() == max_jint) + tty->print(" {%d..}=>%d", lo(), dest()); + else + tty->print(" {%d..%d}=>%d", lo(), hi(), dest()); + } +}; + + +//-------------------------------do_tableswitch-------------------------------- +void Parse::do_tableswitch() { + Node* lookup = pop(); + + // Get information about tableswitch + int default_dest = iter().get_dest_table(0); + int lo_index = iter().get_int_table(1); + int hi_index = iter().get_int_table(2); + int len = hi_index - lo_index + 1; + + if (len < 1) { + // If this is a backward branch, add safepoint + maybe_add_safepoint(default_dest); + merge(default_dest); + return; + } + + // generate decision tree, using trichotomy when possible + int rnum = len+2; + bool makes_backward_branch = false; + SwitchRange* ranges = NEW_RESOURCE_ARRAY(SwitchRange, rnum); + int rp = -1; + if (lo_index != min_jint) { + ranges[++rp].setRange(min_jint, lo_index-1, default_dest, NullTableIndex); + } + for (int j = 0; j < len; j++) { + jint match_int = lo_index+j; + int dest = iter().get_dest_table(j+3); + makes_backward_branch |= (dest <= bci()); + int table_index = method_data_update() ? j : NullTableIndex; + if (rp < 0 || !ranges[rp].adjoin(match_int, dest, table_index)) { + ranges[++rp].set(match_int, dest, table_index); + } + } + jint highest = lo_index+(len-1); + assert(ranges[rp].hi() == highest, ""); + if (highest != max_jint + && !ranges[rp].adjoinRange(highest+1, max_jint, default_dest, NullTableIndex)) { + ranges[++rp].setRange(highest+1, max_jint, default_dest, NullTableIndex); + } + assert(rp < len+2, "not too many ranges"); + + // Safepoint in case if backward branch observed + if( makes_backward_branch && UseLoopSafepoints ) + add_safepoint(); + + jump_switch_ranges(lookup, &ranges[0], &ranges[rp]); +} + + +//------------------------------do_lookupswitch-------------------------------- +void Parse::do_lookupswitch() { + Node *lookup = pop(); // lookup value + // Get information about lookupswitch + int default_dest = iter().get_dest_table(0); + int len = iter().get_int_table(1); + + if (len < 1) { // If this is a backward branch, add safepoint + maybe_add_safepoint(default_dest); + merge(default_dest); + return; + } + + // generate decision tree, using trichotomy when possible + jint* table = NEW_RESOURCE_ARRAY(jint, len*2); + { + for( int j = 0; j < len; j++ ) { + table[j+j+0] = iter().get_int_table(2+j+j); + table[j+j+1] = iter().get_dest_table(2+j+j+1); + } + qsort( table, len, 2*sizeof(table[0]), jint_cmp ); + } + + int rnum = len*2+1; + bool makes_backward_branch = false; + SwitchRange* ranges = NEW_RESOURCE_ARRAY(SwitchRange, rnum); + int rp = -1; + for( int j = 0; j < len; j++ ) { + jint match_int = table[j+j+0]; + int dest = table[j+j+1]; + int next_lo = rp < 0 ? min_jint : ranges[rp].hi()+1; + int table_index = method_data_update() ? j : NullTableIndex; + makes_backward_branch |= (dest <= bci()); + if( match_int != next_lo ) { + ranges[++rp].setRange(next_lo, match_int-1, default_dest, NullTableIndex); + } + if( rp < 0 || !ranges[rp].adjoin(match_int, dest, table_index) ) { + ranges[++rp].set(match_int, dest, table_index); + } + } + jint highest = table[2*(len-1)]; + assert(ranges[rp].hi() == highest, ""); + if( highest != max_jint + && !ranges[rp].adjoinRange(highest+1, max_jint, default_dest, NullTableIndex) ) { + ranges[++rp].setRange(highest+1, max_jint, default_dest, NullTableIndex); + } + assert(rp < rnum, "not too many ranges"); + + // Safepoint in case backward branch observed + if( makes_backward_branch && UseLoopSafepoints ) + add_safepoint(); + + jump_switch_ranges(lookup, &ranges[0], &ranges[rp]); +} + +//----------------------------create_jump_tables------------------------------- +bool Parse::create_jump_tables(Node* key_val, SwitchRange* lo, SwitchRange* hi) { + // Are jumptables enabled + if (!UseJumpTables) return false; + + // Are jumptables supported + if (!Matcher::has_match_rule(Op_Jump)) return false; + + // Don't make jump table if profiling + if (method_data_update()) return false; + + // Decide if a guard is needed to lop off big ranges at either (or + // both) end(s) of the input set. We'll call this the default target + // even though we can't be sure that it is the true "default". + + bool needs_guard = false; + int default_dest; + int64 total_outlier_size = 0; + int64 hi_size = ((int64)hi->hi()) - ((int64)hi->lo()) + 1; + int64 lo_size = ((int64)lo->hi()) - ((int64)lo->lo()) + 1; + + if (lo->dest() == hi->dest()) { + total_outlier_size = hi_size + lo_size; + default_dest = lo->dest(); + } else if (lo_size > hi_size) { + total_outlier_size = lo_size; + default_dest = lo->dest(); + } else { + total_outlier_size = hi_size; + default_dest = hi->dest(); + } + + // If a guard test will eliminate very sparse end ranges, then + // it is worth the cost of an extra jump. + if (total_outlier_size > (MaxJumpTableSparseness * 4)) { + needs_guard = true; + if (default_dest == lo->dest()) lo++; + if (default_dest == hi->dest()) hi--; + } + + // Find the total number of cases and ranges + int64 num_cases = ((int64)hi->hi()) - ((int64)lo->lo()) + 1; + int num_range = hi - lo + 1; + + // Don't create table if: too large, too small, or too sparse. + if (num_cases < MinJumpTableSize || num_cases > MaxJumpTableSize) + return false; + if (num_cases > (MaxJumpTableSparseness * num_range)) + return false; + + // Normalize table lookups to zero + int lowval = lo->lo(); + key_val = _gvn.transform( new (C, 3) SubINode(key_val, _gvn.intcon(lowval)) ); + + // Generate a guard to protect against input keyvals that aren't + // in the switch domain. + if (needs_guard) { + Node* size = _gvn.intcon(num_cases); + Node* cmp = _gvn.transform( new (C, 3) CmpUNode(key_val, size) ); + Node* tst = _gvn.transform( new (C, 2) BoolNode(cmp, BoolTest::ge) ); + IfNode* iff = create_and_map_if( control(), tst, PROB_FAIR, COUNT_UNKNOWN); + jump_if_true_fork(iff, default_dest, NullTableIndex); + } + + // Create an ideal node JumpTable that has projections + // of all possible ranges for a switch statement + // The key_val input must be converted to a pointer offset and scaled. + // Compare Parse::array_addressing above. +#ifdef _LP64 + // Clean the 32-bit int into a real 64-bit offset. + // Otherwise, the jint value 0 might turn into an offset of 0x0800000000. + const TypeLong* lkeytype = TypeLong::make(CONST64(0), num_cases-1, Type::WidenMin); + key_val = _gvn.transform( new (C, 2) ConvI2LNode(key_val, lkeytype) ); +#endif + // Shift the value by wordsize so we have an index into the table, rather + // than a switch value + Node *shiftWord = _gvn.MakeConX(wordSize); + key_val = _gvn.transform( new (C, 3) MulXNode( key_val, shiftWord)); + + // Create the JumpNode + Node* jtn = _gvn.transform( new (C, 2) JumpNode(control(), key_val, num_cases) ); + + // These are the switch destinations hanging off the jumpnode + int i = 0; + for (SwitchRange* r = lo; r <= hi; r++) { + for (int j = r->lo(); j <= r->hi(); j++, i++) { + Node* input = _gvn.transform(new (C, 1) JumpProjNode(jtn, i, r->dest(), j - lowval)); + { + PreserveJVMState pjvms(this); + set_control(input); + jump_if_always_fork(r->dest(), r->table_index()); + } + } + } + assert(i == num_cases, "miscount of cases"); + stop_and_kill_map(); // no more uses for this JVMS + return true; +} + +//----------------------------jump_switch_ranges------------------------------- +void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, int switch_depth) { + Block* switch_block = block(); + + if (switch_depth == 0) { + // Do special processing for the top-level call. + assert(lo->lo() == min_jint, "initial range must exhaust Type::INT"); + assert(hi->hi() == max_jint, "initial range must exhaust Type::INT"); + + // Decrement pred-numbers for the unique set of nodes. +#ifdef ASSERT + // Ensure that the block's successors are a (duplicate-free) set. + int successors_counted = 0; // block occurrences in [hi..lo] + int unique_successors = switch_block->num_successors(); + for (int i = 0; i < unique_successors; i++) { + Block* target = switch_block->successor_at(i); + + // Check that the set of successors is the same in both places. + int successors_found = 0; + for (SwitchRange* p = lo; p <= hi; p++) { + if (p->dest() == target->start()) successors_found++; + } + assert(successors_found > 0, "successor must be known"); + successors_counted += successors_found; + } + assert(successors_counted == (hi-lo)+1, "no unexpected successors"); +#endif + + // Maybe prune the inputs, based on the type of key_val. + jint min_val = min_jint; + jint max_val = max_jint; + const TypeInt* ti = key_val->bottom_type()->isa_int(); + if (ti != NULL) { + min_val = ti->_lo; + max_val = ti->_hi; + assert(min_val <= max_val, "invalid int type"); + } + while (lo->hi() < min_val) lo++; + if (lo->lo() < min_val) lo->setRange(min_val, lo->hi(), lo->dest(), lo->table_index()); + while (hi->lo() > max_val) hi--; + if (hi->hi() > max_val) hi->setRange(hi->lo(), max_val, hi->dest(), hi->table_index()); + } + +#ifndef PRODUCT + if (switch_depth == 0) { + _max_switch_depth = 0; + _est_switch_depth = log2_intptr((hi-lo+1)-1)+1; + } +#endif + + assert(lo <= hi, "must be a non-empty set of ranges"); + if (lo == hi) { + jump_if_always_fork(lo->dest(), lo->table_index()); + } else { + assert(lo->hi() == (lo+1)->lo()-1, "contiguous ranges"); + assert(hi->lo() == (hi-1)->hi()+1, "contiguous ranges"); + + if (create_jump_tables(key_val, lo, hi)) return; + + int nr = hi - lo + 1; + + SwitchRange* mid = lo + nr/2; + // if there is an easy choice, pivot at a singleton: + if (nr > 3 && !mid->is_singleton() && (mid-1)->is_singleton()) mid--; + + assert(lo < mid && mid <= hi, "good pivot choice"); + assert(nr != 2 || mid == hi, "should pick higher of 2"); + assert(nr != 3 || mid == hi-1, "should pick middle of 3"); + + Node *test_val = _gvn.intcon(mid->lo()); + + if (mid->is_singleton()) { + IfNode *iff_ne = jump_if_fork_int(key_val, test_val, BoolTest::ne); + jump_if_false_fork(iff_ne, mid->dest(), mid->table_index()); + + // Special Case: If there are exactly three ranges, and the high + // and low range each go to the same place, omit the "gt" test, + // since it will not discriminate anything. + bool eq_test_only = (hi == lo+2 && hi->dest() == lo->dest()); + if (eq_test_only) { + assert(mid == hi-1, ""); + } + + // if there is a higher range, test for it and process it: + if (mid < hi && !eq_test_only) { + // two comparisons of same values--should enable 1 test for 2 branches + // Use BoolTest::le instead of BoolTest::gt + IfNode *iff_le = jump_if_fork_int(key_val, test_val, BoolTest::le); + Node *iftrue = _gvn.transform( new (C, 1) IfTrueNode(iff_le) ); + Node *iffalse = _gvn.transform( new (C, 1) IfFalseNode(iff_le) ); + { PreserveJVMState pjvms(this); + set_control(iffalse); + jump_switch_ranges(key_val, mid+1, hi, switch_depth+1); + } + set_control(iftrue); + } + + } else { + // mid is a range, not a singleton, so treat mid..hi as a unit + IfNode *iff_ge = jump_if_fork_int(key_val, test_val, BoolTest::ge); + + // if there is a higher range, test for it and process it: + if (mid == hi) { + jump_if_true_fork(iff_ge, mid->dest(), mid->table_index()); + } else { + Node *iftrue = _gvn.transform( new (C, 1) IfTrueNode(iff_ge) ); + Node *iffalse = _gvn.transform( new (C, 1) IfFalseNode(iff_ge) ); + { PreserveJVMState pjvms(this); + set_control(iftrue); + jump_switch_ranges(key_val, mid, hi, switch_depth+1); + } + set_control(iffalse); + } + } + + // in any case, process the lower range + jump_switch_ranges(key_val, lo, mid-1, switch_depth+1); + } + + // Decrease pred_count for each successor after all is done. + if (switch_depth == 0) { + int unique_successors = switch_block->num_successors(); + for (int i = 0; i < unique_successors; i++) { + Block* target = switch_block->successor_at(i); + // Throw away the pre-allocated path for each unique successor. + target->next_path_num(); + } + } + +#ifndef PRODUCT + _max_switch_depth = MAX2(switch_depth, _max_switch_depth); + if (TraceOptoParse && Verbose && WizardMode && switch_depth == 0) { + SwitchRange* r; + int nsing = 0; + for( r = lo; r <= hi; r++ ) { + if( r->is_singleton() ) nsing++; + } + tty->print(">>> "); + _method->print_short_name(); + tty->print_cr(" switch decision tree"); + tty->print_cr(" %d ranges (%d singletons), max_depth=%d, est_depth=%d", + hi-lo+1, nsing, _max_switch_depth, _est_switch_depth); + if (_max_switch_depth > _est_switch_depth) { + tty->print_cr("******** BAD SWITCH DEPTH ********"); + } + tty->print(" "); + for( r = lo; r <= hi; r++ ) { + r->print(env()); + } + tty->print_cr(""); + } +#endif +} + +void Parse::modf() { + Node *f2 = pop(); + Node *f1 = pop(); + Node* c = make_runtime_call(RC_LEAF, OptoRuntime::modf_Type(), + CAST_FROM_FN_PTR(address, SharedRuntime::frem), + "frem", NULL, //no memory effects + f1, f2); + Node* res = _gvn.transform(new (C, 1) ProjNode(c, TypeFunc::Parms + 0)); + + push(res); +} + +void Parse::modd() { + Node *d2 = pop_pair(); + Node *d1 = pop_pair(); + Node* c = make_runtime_call(RC_LEAF, OptoRuntime::Math_DD_D_Type(), + CAST_FROM_FN_PTR(address, SharedRuntime::drem), + "drem", NULL, //no memory effects + d1, top(), d2, top()); + Node* res_d = _gvn.transform(new (C, 1) ProjNode(c, TypeFunc::Parms + 0)); + +#ifdef ASSERT + Node* res_top = _gvn.transform(new (C, 1) ProjNode(c, TypeFunc::Parms + 1)); + assert(res_top == top(), "second value must be top"); +#endif + + push_pair(res_d); +} + +void Parse::l2f() { + Node* f2 = pop(); + Node* f1 = pop(); + Node* c = make_runtime_call(RC_LEAF, OptoRuntime::l2f_Type(), + CAST_FROM_FN_PTR(address, SharedRuntime::l2f), + "l2f", NULL, //no memory effects + f1, f2); + Node* res = _gvn.transform(new (C, 1) ProjNode(c, TypeFunc::Parms + 0)); + + push(res); +} + +void Parse::do_irem() { + // Must keep both values on the expression-stack during null-check + do_null_check(peek(), T_INT); + // Compile-time detect of null-exception? + if (stopped()) return; + + Node* b = pop(); + Node* a = pop(); + + const Type *t = _gvn.type(b); + if (t != Type::TOP) { + const TypeInt *ti = t->is_int(); + if (ti->is_con()) { + int divisor = ti->get_con(); + // check for positive power of 2 + if (divisor > 0 && + (divisor & ~(divisor-1)) == divisor) { + // yes ! + Node *mask = _gvn.intcon((divisor - 1)); + // Sigh, must handle negative dividends + Node *zero = _gvn.intcon(0); + IfNode *ifff = jump_if_fork_int(a, zero, BoolTest::lt); + Node *iff = _gvn.transform( new (C, 1) IfFalseNode(ifff) ); + Node *ift = _gvn.transform( new (C, 1) IfTrueNode (ifff) ); + Node *reg = jump_if_join(ift, iff); + Node *phi = PhiNode::make(reg, NULL, TypeInt::INT); + // Negative path; negate/and/negate + Node *neg = _gvn.transform( new (C, 3) SubINode(zero, a) ); + Node *andn= _gvn.transform( new (C, 3) AndINode(neg, mask) ); + Node *negn= _gvn.transform( new (C, 3) SubINode(zero, andn) ); + phi->init_req(1, negn); + // Fast positive case + Node *andx = _gvn.transform( new (C, 3) AndINode(a, mask) ); + phi->init_req(2, andx); + // Push the merge + push( _gvn.transform(phi) ); + return; + } + } + } + // Default case + push( _gvn.transform( new (C, 3) ModINode(control(),a,b) ) ); +} + +// Handle jsr and jsr_w bytecode +void Parse::do_jsr() { + assert(bc() == Bytecodes::_jsr || bc() == Bytecodes::_jsr_w, "wrong bytecode"); + + // Store information about current state, tagged with new _jsr_bci + int return_bci = iter().next_bci(); + int jsr_bci = (bc() == Bytecodes::_jsr) ? iter().get_dest() : iter().get_far_dest(); + + // Update method data + profile_taken_branch(jsr_bci); + + // The way we do things now, there is only one successor block + // for the jsr, because the target code is cloned by ciTypeFlow. + Block* target = successor_for_bci(jsr_bci); + + // What got pushed? + const Type* ret_addr = target->peek(); + assert(ret_addr->singleton(), "must be a constant (cloned jsr body)"); + + // Effect on jsr on stack + push(_gvn.makecon(ret_addr)); + + // Flow to the jsr. + merge(jsr_bci); +} + +// Handle ret bytecode +void Parse::do_ret() { + // Find to whom we return. +#if 0 // %%%% MAKE THIS WORK + Node* con = local(); + const TypePtr* tp = con->bottom_type()->isa_ptr(); + assert(tp && tp->singleton(), ""); + int return_bci = (int) tp->get_con(); + merge(return_bci); +#else + assert(block()->num_successors() == 1, "a ret can only go one place now"); + Block* target = block()->successor_at(0); + assert(!target->is_ready(), "our arrival must be expected"); + profile_ret(target->flow()->start()); + int pnum = target->next_path_num(); + merge_common(target, pnum); +#endif +} + +//--------------------------dynamic_branch_prediction-------------------------- +// Try to gather dynamic branch prediction behavior. Return a probability +// of the branch being taken and set the "cnt" field. Returns a -1.0 +// if we need to use static prediction for some reason. +float Parse::dynamic_branch_prediction(float &cnt) { + ResourceMark rm; + + cnt = COUNT_UNKNOWN; + + // Use MethodData information if it is available + // FIXME: free the ProfileData structure + ciMethodData* methodData = method()->method_data(); + if (!methodData->is_mature()) return PROB_UNKNOWN; + ciProfileData* data = methodData->bci_to_data(bci()); + if (!data->is_JumpData()) return PROB_UNKNOWN; + + // get taken and not taken values + int taken = data->as_JumpData()->taken(); + int not_taken = 0; + if (data->is_BranchData()) { + not_taken = data->as_BranchData()->not_taken(); + } + + // scale the counts to be commensurate with invocation counts: + taken = method()->scale_count(taken); + not_taken = method()->scale_count(not_taken); + + // Give up if too few counts to be meaningful + if (taken + not_taken < 40) { + if (C->log() != NULL) { + C->log()->elem("branch target_bci='%d' taken='%d' not_taken='%d'", iter().get_dest(), taken, not_taken); + } + return PROB_UNKNOWN; + } + + // Compute frequency that we arrive here + int sum = taken + not_taken; + // Adjust, if this block is a cloned private block but the + // Jump counts are shared. Taken the private counts for + // just this path instead of the shared counts. + if( block()->count() > 0 ) + sum = block()->count(); + cnt = (float)sum / (float)FreqCountInvocations; + + // Pin probability to sane limits + float prob; + if( !taken ) + prob = (0+PROB_MIN) / 2; + else if( !not_taken ) + prob = (1+PROB_MAX) / 2; + else { // Compute probability of true path + prob = (float)taken / (float)(taken + not_taken); + if (prob > PROB_MAX) prob = PROB_MAX; + if (prob < PROB_MIN) prob = PROB_MIN; + } + + assert((cnt > 0.0f) && (prob > 0.0f), + "Bad frequency assignment in if"); + + if (C->log() != NULL) { + const char* prob_str = NULL; + if (prob >= PROB_MAX) prob_str = (prob == PROB_MAX) ? "max" : "always"; + if (prob <= PROB_MIN) prob_str = (prob == PROB_MIN) ? "min" : "never"; + char prob_str_buf[30]; + if (prob_str == NULL) { + sprintf(prob_str_buf, "%g", prob); + prob_str = prob_str_buf; + } + C->log()->elem("branch target_bci='%d' taken='%d' not_taken='%d' cnt='%g' prob='%s'", + iter().get_dest(), taken, not_taken, cnt, prob_str); + } + return prob; +} + +//-----------------------------branch_prediction------------------------------- +float Parse::branch_prediction(float& cnt, + BoolTest::mask btest, + int target_bci) { + float prob = dynamic_branch_prediction(cnt); + // If prob is unknown, switch to static prediction + if (prob != PROB_UNKNOWN) return prob; + + prob = PROB_FAIR; // Set default value + if (btest == BoolTest::eq) // Exactly equal test? + prob = PROB_STATIC_INFREQUENT; // Assume its relatively infrequent + else if (btest == BoolTest::ne) + prob = PROB_STATIC_FREQUENT; // Assume its relatively frequent + + // If this is a conditional test guarding a backwards branch, + // assume its a loop-back edge. Make it a likely taken branch. + if (target_bci < bci()) { + if (is_osr_parse()) { // Could be a hot OSR'd loop; force deopt + // Since it's an OSR, we probably have profile data, but since + // branch_prediction returned PROB_UNKNOWN, the counts are too small. + // Let's make a special check here for completely zero counts. + ciMethodData* methodData = method()->method_data(); + if (!methodData->is_empty()) { + ciProfileData* data = methodData->bci_to_data(bci()); + // Only stop for truly zero counts, which mean an unknown part + // of the OSR-ed method, and we want to deopt to gather more stats. + // If you have ANY counts, then this loop is simply 'cold' relative + // to the OSR loop. + if (data->as_BranchData()->taken() + + data->as_BranchData()->not_taken() == 0 ) { + // This is the only way to return PROB_UNKNOWN: + return PROB_UNKNOWN; + } + } + } + prob = PROB_STATIC_FREQUENT; // Likely to take backwards branch + } + + assert(prob != PROB_UNKNOWN, "must have some guess at this point"); + return prob; +} + +// The magic constants are chosen so as to match the output of +// branch_prediction() when the profile reports a zero taken count. +// It is important to distinguish zero counts unambiguously, because +// some branches (e.g., _213_javac.Assembler.eliminate) validly produce +// very small but nonzero probabilities, which if confused with zero +// counts would keep the program recompiling indefinitely. +bool Parse::seems_never_taken(float prob) { + return prob < PROB_MIN; +} + +inline void Parse::repush_if_args() { +#ifndef PRODUCT + if (PrintOpto && WizardMode) { + tty->print("defending against excessive implicit null exceptions on %s @%d in ", + Bytecodes::name(iter().cur_bc()), iter().cur_bci()); + method()->print_name(); tty->cr(); + } +#endif + int bc_depth = - Bytecodes::depth(iter().cur_bc()); + assert(bc_depth == 1 || bc_depth == 2, "only two kinds of branches"); + DEBUG_ONLY(sync_jvms()); // argument(n) requires a synced jvms + assert(argument(0) != NULL, "must exist"); + assert(bc_depth == 1 || argument(1) != NULL, "two must exist"); + _sp += bc_depth; +} + +//----------------------------------do_ifnull---------------------------------- +void Parse::do_ifnull(BoolTest::mask btest) { + int target_bci = iter().get_dest(); + + float cnt; + float prob = branch_prediction(cnt, btest, target_bci); + if (prob == PROB_UNKNOWN) { + // (An earlier version of do_ifnull omitted this trap for OSR methods.) +#ifndef PRODUCT + if (PrintOpto && Verbose) + tty->print_cr("Never-taken backedge stops compilation at bci %d",bci()); +#endif + repush_if_args(); // to gather stats on loop + // We need to mark this branch as taken so that if we recompile we will + // see that it is possible. In the tiered system the interpreter doesn't + // do profiling and by the time we get to the lower tier from the interpreter + // the path may be cold again. Make sure it doesn't look untaken + profile_taken_branch(target_bci, !ProfileInterpreter); + uncommon_trap(Deoptimization::Reason_unreached, + Deoptimization::Action_reinterpret, + NULL, "cold"); + return; + } + + // If this is a backwards branch in the bytecodes, add Safepoint + maybe_add_safepoint(target_bci); + Block* branch_block = successor_for_bci(target_bci); + Block* next_block = successor_for_bci(iter().next_bci()); + + explicit_null_checks_inserted++; + Node* a = null(); + Node* b = pop(); + Node* c = _gvn.transform( new (C, 3) CmpPNode(b, a) ); + + // Make a cast-away-nullness that is control dependent on the test + const Type *t = _gvn.type(b); + const Type *t_not_null = t->join(TypePtr::NOTNULL); + Node *cast = new (C, 2) CastPPNode(b,t_not_null); + + // Generate real control flow + Node *tst = _gvn.transform( new (C, 2) BoolNode( c, btest ) ); + + // Sanity check the probability value + assert(prob > 0.0f,"Bad probability in Parser"); + // Need xform to put node in hash table + IfNode *iff = create_and_xform_if( control(), tst, prob, cnt ); + assert(iff->_prob > 0.0f,"Optimizer made bad probability in parser"); + // True branch + { PreserveJVMState pjvms(this); + Node* iftrue = _gvn.transform( new (C, 1) IfTrueNode (iff) ); + set_control(iftrue); + + if (stopped()) { // Path is dead? + explicit_null_checks_elided++; + } else { // Path is live. + // Update method data + profile_taken_branch(target_bci); + adjust_map_after_if(btest, c, prob, branch_block, next_block); + if (!stopped()) + merge(target_bci); + } + } + + // False branch + Node* iffalse = _gvn.transform( new (C, 1) IfFalseNode(iff) ); + set_control(iffalse); + + if (stopped()) { // Path is dead? + explicit_null_checks_elided++; + } else { // Path is live. + // Update method data + profile_not_taken_branch(); + adjust_map_after_if(BoolTest(btest).negate(), c, 1.0-prob, + next_block, branch_block); + } +} + +//------------------------------------do_if------------------------------------ +void Parse::do_if(BoolTest::mask btest, Node* c) { + int target_bci = iter().get_dest(); + + float cnt; + float prob = branch_prediction(cnt, btest, target_bci); + float untaken_prob = 1.0 - prob; + + if (prob == PROB_UNKNOWN) { +#ifndef PRODUCT + if (PrintOpto && Verbose) + tty->print_cr("Never-taken backedge stops compilation at bci %d",bci()); +#endif + repush_if_args(); // to gather stats on loop + // We need to mark this branch as taken so that if we recompile we will + // see that it is possible. In the tiered system the interpreter doesn't + // do profiling and by the time we get to the lower tier from the interpreter + // the path may be cold again. Make sure it doesn't look untaken + profile_taken_branch(target_bci, !ProfileInterpreter); + uncommon_trap(Deoptimization::Reason_unreached, + Deoptimization::Action_reinterpret, + NULL, "cold"); + return; + } + + // Sanity check the probability value + assert(0.0f < prob && prob < 1.0f,"Bad probability in Parser"); + + bool taken_if_true = true; + // Convert BoolTest to canonical form: + if (!BoolTest(btest).is_canonical()) { + btest = BoolTest(btest).negate(); + taken_if_true = false; + // prob is NOT updated here; it remains the probability of the taken + // path (as opposed to the prob of the path guarded by an 'IfTrueNode'). + } + assert(btest != BoolTest::eq, "!= is the only canonical exact test"); + + Node* tst0 = new (C, 2) BoolNode(c, btest); + Node* tst = _gvn.transform(tst0); + BoolTest::mask taken_btest = BoolTest::illegal; + BoolTest::mask untaken_btest = BoolTest::illegal; + if (btest == BoolTest::ne) { + // For now, these are the only cases of btest that matter. (More later.) + taken_btest = taken_if_true ? btest : BoolTest::eq; + untaken_btest = taken_if_true ? BoolTest::eq : btest; + } + + // Generate real control flow + float true_prob = (taken_if_true ? prob : untaken_prob); + IfNode* iff = create_and_map_if(control(), tst, true_prob, cnt); + assert(iff->_prob > 0.0f,"Optimizer made bad probability in parser"); + Node* taken_branch = new (C, 1) IfTrueNode(iff); + Node* untaken_branch = new (C, 1) IfFalseNode(iff); + if (!taken_if_true) { // Finish conversion to canonical form + Node* tmp = taken_branch; + taken_branch = untaken_branch; + untaken_branch = tmp; + } + + Block* branch_block = successor_for_bci(target_bci); + Block* next_block = successor_for_bci(iter().next_bci()); + + // Branch is taken: + { PreserveJVMState pjvms(this); + taken_branch = _gvn.transform(taken_branch); + set_control(taken_branch); + + if (!stopped()) { + // Update method data + profile_taken_branch(target_bci); + adjust_map_after_if(taken_btest, c, prob, branch_block, next_block); + if (!stopped()) + merge(target_bci); + } + } + + untaken_branch = _gvn.transform(untaken_branch); + set_control(untaken_branch); + + // Branch not taken. + if (!stopped()) { + // Update method data + profile_not_taken_branch(); + adjust_map_after_if(untaken_btest, c, untaken_prob, + next_block, branch_block); + } +} + +//----------------------------adjust_map_after_if------------------------------ +// Adjust the JVM state to reflect the result of taking this path. +// Basically, it means inspecting the CmpNode controlling this +// branch, seeing how it constrains a tested value, and then +// deciding if it's worth our while to encode this constraint +// as graph nodes in the current abstract interpretation map. +void Parse::adjust_map_after_if(BoolTest::mask btest, Node* c, float prob, + Block* path, Block* other_path) { + if (stopped() || !c->is_Cmp() || btest == BoolTest::illegal) + return; // nothing to do + + bool is_fallthrough = (path == successor_for_bci(iter().next_bci())); + + int cop = c->Opcode(); + if (seems_never_taken(prob) && cop == Op_CmpP && btest == BoolTest::eq) { + // (An earlier version of do_if omitted '&& btest == BoolTest::eq'.) + // + // If this might possibly turn into an implicit null check, + // and the null has never yet been seen, we need to generate + // an uncommon trap, so as to recompile instead of suffering + // with very slow branches. (We'll get the slow branches if + // the program ever changes phase and starts seeing nulls here.) + // + // The tests we worry about are of the form (p == null). + // We do not simply inspect for a null constant, since a node may + // optimize to 'null' later on. + repush_if_args(); + // We need to mark this branch as taken so that if we recompile we will + // see that it is possible. In the tiered system the interpreter doesn't + // do profiling and by the time we get to the lower tier from the interpreter + // the path may be cold again. Make sure it doesn't look untaken + if (is_fallthrough) { + profile_not_taken_branch(!ProfileInterpreter); + } else { + profile_taken_branch(iter().get_dest(), !ProfileInterpreter); + } + uncommon_trap(Deoptimization::Reason_unreached, + Deoptimization::Action_reinterpret, + NULL, + (is_fallthrough ? "taken always" : "taken never")); + return; + } + + Node* val = c->in(1); + Node* con = c->in(2); + const Type* tcon = _gvn.type(con); + const Type* tval = _gvn.type(val); + bool have_con = tcon->singleton(); + if (tval->singleton()) { + if (!have_con) { + // Swap, so constant is in con. + con = val; + tcon = tval; + val = c->in(2); + tval = _gvn.type(val); + btest = BoolTest(btest).commute(); + have_con = true; + } else { + // Do we have two constants? Then leave well enough alone. + have_con = false; + } + } + if (!have_con) // remaining adjustments need a con + return; + + + int val_in_map = map()->find_edge(val); + if (val_in_map < 0) return; // replace_in_map would be useless + { + JVMState* jvms = this->jvms(); + if (!(jvms->is_loc(val_in_map) || + jvms->is_stk(val_in_map))) + return; // again, it would be useless + } + + // Check for a comparison to a constant, and "know" that the compared + // value is constrained on this path. + assert(tcon->singleton(), ""); + ConstraintCastNode* ccast = NULL; + Node* cast = NULL; + + switch (btest) { + case BoolTest::eq: // Constant test? + { + const Type* tboth = tcon->join(tval); + if (tboth == tval) break; // Nothing to gain. + if (tcon->isa_int()) { + ccast = new (C, 2) CastIINode(val, tboth); + } else if (tcon == TypePtr::NULL_PTR) { + // Cast to null, but keep the pointer identity temporarily live. + ccast = new (C, 2) CastPPNode(val, tboth); + } else { + const TypeF* tf = tcon->isa_float_constant(); + const TypeD* td = tcon->isa_double_constant(); + // Exclude tests vs float/double 0 as these could be + // either +0 or -0. Just because you are equal to +0 + // doesn't mean you ARE +0! + if ((!tf || tf->_f != 0.0) && + (!td || td->_d != 0.0)) + cast = con; // Replace non-constant val by con. + } + } + break; + + case BoolTest::ne: + if (tcon == TypePtr::NULL_PTR) { + cast = cast_not_null(val, false); + } + break; + + default: + // (At this point we could record int range types with CastII.) + break; + } + + if (ccast != NULL) { + const Type* tcc = ccast->as_Type()->type(); + assert(tcc != tval && tcc->higher_equal(tval), "must improve"); + // Delay transform() call to allow recovery of pre-cast value + // at the control merge. + ccast->set_req(0, control()); + _gvn.set_type_bottom(ccast); + record_for_igvn(ccast); + cast = ccast; + } + + if (cast != NULL) { // Here's the payoff. + replace_in_map(val, cast); + } +} + + +//------------------------------do_one_bytecode-------------------------------- +// Parse this bytecode, and alter the Parsers JVM->Node mapping +void Parse::do_one_bytecode() { + Node *a, *b, *c, *d; // Handy temps + BoolTest::mask btest; + int i; + + assert(!has_exceptions(), "bytecode entry state must be clear of throws"); + + if (C->check_node_count(NodeLimitFudgeFactor * 5, + "out of nodes parsing method")) { + return; + } + +#ifdef ASSERT + // for setting breakpoints + if (TraceOptoParse) { + tty->print(" @"); + dump_bci(bci()); + } +#endif + + switch (bc()) { + case Bytecodes::_nop: + // do nothing + break; + case Bytecodes::_lconst_0: + push_pair(longcon(0)); + break; + + case Bytecodes::_lconst_1: + push_pair(longcon(1)); + break; + + case Bytecodes::_fconst_0: + push(zerocon(T_FLOAT)); + break; + + case Bytecodes::_fconst_1: + push(makecon(TypeF::ONE)); + break; + + case Bytecodes::_fconst_2: + push(makecon(TypeF::make(2.0f))); + break; + + case Bytecodes::_dconst_0: + push_pair(zerocon(T_DOUBLE)); + break; + + case Bytecodes::_dconst_1: + push_pair(makecon(TypeD::ONE)); + break; + + case Bytecodes::_iconst_m1:push(intcon(-1)); break; + case Bytecodes::_iconst_0: push(intcon( 0)); break; + case Bytecodes::_iconst_1: push(intcon( 1)); break; + case Bytecodes::_iconst_2: push(intcon( 2)); break; + case Bytecodes::_iconst_3: push(intcon( 3)); break; + case Bytecodes::_iconst_4: push(intcon( 4)); break; + case Bytecodes::_iconst_5: push(intcon( 5)); break; + case Bytecodes::_bipush: push(intcon( iter().get_byte())); break; + case Bytecodes::_sipush: push(intcon( iter().get_short())); break; + case Bytecodes::_aconst_null: push(null()); break; + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + // If the constant is unresolved, run this BC once in the interpreter. + if (iter().is_unresolved_string()) { + uncommon_trap(Deoptimization::make_trap_request + (Deoptimization::Reason_unloaded, + Deoptimization::Action_reinterpret, + iter().get_constant_index()), + NULL, "unresolved_string"); + break; + } else { + ciConstant constant = iter().get_constant(); + if (constant.basic_type() == T_OBJECT) { + ciObject* c = constant.as_object(); + if (c->is_klass()) { + // The constant returned for a klass is the ciKlass for the + // entry. We want the java_mirror so get it. + ciKlass* klass = c->as_klass(); + if (klass->is_loaded()) { + constant = ciConstant(T_OBJECT, klass->java_mirror()); + } else { + uncommon_trap(Deoptimization::make_trap_request + (Deoptimization::Reason_unloaded, + Deoptimization::Action_reinterpret, + iter().get_constant_index()), + NULL, "unresolved_klass"); + break; + } + } + } + push_constant(constant); + } + + break; + + case Bytecodes::_aload_0: + push( local(0) ); + break; + case Bytecodes::_aload_1: + push( local(1) ); + break; + case Bytecodes::_aload_2: + push( local(2) ); + break; + case Bytecodes::_aload_3: + push( local(3) ); + break; + case Bytecodes::_aload: + push( local(iter().get_index()) ); + break; + + case Bytecodes::_fload_0: + case Bytecodes::_iload_0: + push( local(0) ); + break; + case Bytecodes::_fload_1: + case Bytecodes::_iload_1: + push( local(1) ); + break; + case Bytecodes::_fload_2: + case Bytecodes::_iload_2: + push( local(2) ); + break; + case Bytecodes::_fload_3: + case Bytecodes::_iload_3: + push( local(3) ); + break; + case Bytecodes::_fload: + case Bytecodes::_iload: + push( local(iter().get_index()) ); + break; + case Bytecodes::_lload_0: + push_pair_local( 0 ); + break; + case Bytecodes::_lload_1: + push_pair_local( 1 ); + break; + case Bytecodes::_lload_2: + push_pair_local( 2 ); + break; + case Bytecodes::_lload_3: + push_pair_local( 3 ); + break; + case Bytecodes::_lload: + push_pair_local( iter().get_index() ); + break; + + case Bytecodes::_dload_0: + push_pair_local(0); + break; + case Bytecodes::_dload_1: + push_pair_local(1); + break; + case Bytecodes::_dload_2: + push_pair_local(2); + break; + case Bytecodes::_dload_3: + push_pair_local(3); + break; + case Bytecodes::_dload: + push_pair_local(iter().get_index()); + break; + case Bytecodes::_fstore_0: + case Bytecodes::_istore_0: + case Bytecodes::_astore_0: + set_local( 0, pop() ); + break; + case Bytecodes::_fstore_1: + case Bytecodes::_istore_1: + case Bytecodes::_astore_1: + set_local( 1, pop() ); + break; + case Bytecodes::_fstore_2: + case Bytecodes::_istore_2: + case Bytecodes::_astore_2: + set_local( 2, pop() ); + break; + case Bytecodes::_fstore_3: + case Bytecodes::_istore_3: + case Bytecodes::_astore_3: + set_local( 3, pop() ); + break; + case Bytecodes::_fstore: + case Bytecodes::_istore: + case Bytecodes::_astore: + set_local( iter().get_index(), pop() ); + break; + // long stores + case Bytecodes::_lstore_0: + set_pair_local( 0, pop_pair() ); + break; + case Bytecodes::_lstore_1: + set_pair_local( 1, pop_pair() ); + break; + case Bytecodes::_lstore_2: + set_pair_local( 2, pop_pair() ); + break; + case Bytecodes::_lstore_3: + set_pair_local( 3, pop_pair() ); + break; + case Bytecodes::_lstore: + set_pair_local( iter().get_index(), pop_pair() ); + break; + + // double stores + case Bytecodes::_dstore_0: + set_pair_local( 0, dstore_rounding(pop_pair()) ); + break; + case Bytecodes::_dstore_1: + set_pair_local( 1, dstore_rounding(pop_pair()) ); + break; + case Bytecodes::_dstore_2: + set_pair_local( 2, dstore_rounding(pop_pair()) ); + break; + case Bytecodes::_dstore_3: + set_pair_local( 3, dstore_rounding(pop_pair()) ); + break; + case Bytecodes::_dstore: + set_pair_local( iter().get_index(), dstore_rounding(pop_pair()) ); + break; + + case Bytecodes::_pop: _sp -= 1; break; + case Bytecodes::_pop2: _sp -= 2; break; + case Bytecodes::_swap: + a = pop(); + b = pop(); + push(a); + push(b); + break; + case Bytecodes::_dup: + a = pop(); + push(a); + push(a); + break; + case Bytecodes::_dup_x1: + a = pop(); + b = pop(); + push( a ); + push( b ); + push( a ); + break; + case Bytecodes::_dup_x2: + a = pop(); + b = pop(); + c = pop(); + push( a ); + push( c ); + push( b ); + push( a ); + break; + case Bytecodes::_dup2: + a = pop(); + b = pop(); + push( b ); + push( a ); + push( b ); + push( a ); + break; + + case Bytecodes::_dup2_x1: + // before: .. c, b, a + // after: .. b, a, c, b, a + // not tested + a = pop(); + b = pop(); + c = pop(); + push( b ); + push( a ); + push( c ); + push( b ); + push( a ); + break; + case Bytecodes::_dup2_x2: + // before: .. d, c, b, a + // after: .. b, a, d, c, b, a + // not tested + a = pop(); + b = pop(); + c = pop(); + d = pop(); + push( b ); + push( a ); + push( d ); + push( c ); + push( b ); + push( a ); + break; + + case Bytecodes::_arraylength: { + // Must do null-check with value on expression stack + Node *ary = do_null_check(peek(), T_ARRAY); + // Compile-time detect of null-exception? + if (stopped()) return; + a = pop(); + push(load_array_length(a)); + break; + } + + case Bytecodes::_baload: array_load(T_BYTE); break; + case Bytecodes::_caload: array_load(T_CHAR); break; + case Bytecodes::_iaload: array_load(T_INT); break; + case Bytecodes::_saload: array_load(T_SHORT); break; + case Bytecodes::_faload: array_load(T_FLOAT); break; + case Bytecodes::_aaload: array_load(T_OBJECT); break; + case Bytecodes::_laload: { + a = array_addressing(T_LONG, 0); + if (stopped()) return; // guarenteed null or range check + _sp -= 2; // Pop array and index + push_pair( make_load(control(), a, TypeLong::LONG, T_LONG, TypeAryPtr::LONGS)); + break; + } + case Bytecodes::_daload: { + a = array_addressing(T_DOUBLE, 0); + if (stopped()) return; // guarenteed null or range check + _sp -= 2; // Pop array and index + push_pair( make_load(control(), a, Type::DOUBLE, T_DOUBLE, TypeAryPtr::DOUBLES)); + break; + } + case Bytecodes::_bastore: array_store(T_BYTE); break; + case Bytecodes::_castore: array_store(T_CHAR); break; + case Bytecodes::_iastore: array_store(T_INT); break; + case Bytecodes::_sastore: array_store(T_SHORT); break; + case Bytecodes::_fastore: array_store(T_FLOAT); break; + case Bytecodes::_aastore: { + d = array_addressing(T_OBJECT, 1); + if (stopped()) return; // guarenteed null or range check + array_store_check(); + c = pop(); // Oop to store + b = pop(); // index (already used) + a = pop(); // the array itself + const Type* elemtype = _gvn.type(a)->is_aryptr()->elem(); + const TypeAryPtr* adr_type = TypeAryPtr::OOPS; + Node* store = store_oop_to_array(control(), a, d, adr_type, c, elemtype, T_OBJECT); + break; + } + case Bytecodes::_lastore: { + a = array_addressing(T_LONG, 2); + if (stopped()) return; // guarenteed null or range check + c = pop_pair(); + _sp -= 2; // Pop array and index + store_to_memory(control(), a, c, T_LONG, TypeAryPtr::LONGS); + break; + } + case Bytecodes::_dastore: { + a = array_addressing(T_DOUBLE, 2); + if (stopped()) return; // guarenteed null or range check + c = pop_pair(); + _sp -= 2; // Pop array and index + c = dstore_rounding(c); + store_to_memory(control(), a, c, T_DOUBLE, TypeAryPtr::DOUBLES); + break; + } + case Bytecodes::_getfield: + do_getfield(); + break; + + case Bytecodes::_getstatic: + do_getstatic(); + break; + + case Bytecodes::_putfield: + do_putfield(); + break; + + case Bytecodes::_putstatic: + do_putstatic(); + break; + + case Bytecodes::_irem: + do_irem(); + break; + case Bytecodes::_idiv: + // Must keep both values on the expression-stack during null-check + do_null_check(peek(), T_INT); + // Compile-time detect of null-exception? + if (stopped()) return; + b = pop(); + a = pop(); + push( _gvn.transform( new (C, 3) DivINode(control(),a,b) ) ); + break; + case Bytecodes::_imul: + b = pop(); a = pop(); + push( _gvn.transform( new (C, 3) MulINode(a,b) ) ); + break; + case Bytecodes::_iadd: + b = pop(); a = pop(); + push( _gvn.transform( new (C, 3) AddINode(a,b) ) ); + break; + case Bytecodes::_ineg: + a = pop(); + push( _gvn.transform( new (C, 3) SubINode(_gvn.intcon(0),a)) ); + break; + case Bytecodes::_isub: + b = pop(); a = pop(); + push( _gvn.transform( new (C, 3) SubINode(a,b) ) ); + break; + case Bytecodes::_iand: + b = pop(); a = pop(); + push( _gvn.transform( new (C, 3) AndINode(a,b) ) ); + break; + case Bytecodes::_ior: + b = pop(); a = pop(); + push( _gvn.transform( new (C, 3) OrINode(a,b) ) ); + break; + case Bytecodes::_ixor: + b = pop(); a = pop(); + push( _gvn.transform( new (C, 3) XorINode(a,b) ) ); + break; + case Bytecodes::_ishl: + b = pop(); a = pop(); + push( _gvn.transform( new (C, 3) LShiftINode(a,b) ) ); + break; + case Bytecodes::_ishr: + b = pop(); a = pop(); + push( _gvn.transform( new (C, 3) RShiftINode(a,b) ) ); + break; + case Bytecodes::_iushr: + b = pop(); a = pop(); + push( _gvn.transform( new (C, 3) URShiftINode(a,b) ) ); + break; + + case Bytecodes::_fneg: + a = pop(); + b = _gvn.transform(new (C, 2) NegFNode (a)); + push(b); + break; + + case Bytecodes::_fsub: + b = pop(); + a = pop(); + c = _gvn.transform( new (C, 3) SubFNode(a,b) ); + d = precision_rounding(c); + push( d ); + break; + + case Bytecodes::_fadd: + b = pop(); + a = pop(); + c = _gvn.transform( new (C, 3) AddFNode(a,b) ); + d = precision_rounding(c); + push( d ); + break; + + case Bytecodes::_fmul: + b = pop(); + a = pop(); + c = _gvn.transform( new (C, 3) MulFNode(a,b) ); + d = precision_rounding(c); + push( d ); + break; + + case Bytecodes::_fdiv: + b = pop(); + a = pop(); + c = _gvn.transform( new (C, 3) DivFNode(0,a,b) ); + d = precision_rounding(c); + push( d ); + break; + + case Bytecodes::_frem: + if (Matcher::has_match_rule(Op_ModF)) { + // Generate a ModF node. + b = pop(); + a = pop(); + c = _gvn.transform( new (C, 3) ModFNode(0,a,b) ); + d = precision_rounding(c); + push( d ); + } + else { + // Generate a call. + modf(); + } + break; + + case Bytecodes::_fcmpl: + b = pop(); + a = pop(); + c = _gvn.transform( new (C, 3) CmpF3Node( a, b)); + push(c); + break; + case Bytecodes::_fcmpg: + b = pop(); + a = pop(); + + // Same as fcmpl but need to flip the unordered case. Swap the inputs, + // which negates the result sign except for unordered. Flip the unordered + // as well by using CmpF3 which implements unordered-lesser instead of + // unordered-greater semantics. Finally, commute the result bits. Result + // is same as using a CmpF3Greater except we did it with CmpF3 alone. + c = _gvn.transform( new (C, 3) CmpF3Node( b, a)); + c = _gvn.transform( new (C, 3) SubINode(_gvn.intcon(0),c) ); + push(c); + break; + + case Bytecodes::_f2i: + a = pop(); + push(_gvn.transform(new (C, 2) ConvF2INode(a))); + break; + + case Bytecodes::_d2i: + a = pop_pair(); + b = _gvn.transform(new (C, 2) ConvD2INode(a)); + push( b ); + break; + + case Bytecodes::_f2d: + a = pop(); + b = _gvn.transform( new (C, 2) ConvF2DNode(a)); + push_pair( b ); + break; + + case Bytecodes::_d2f: + a = pop_pair(); + b = _gvn.transform( new (C, 2) ConvD2FNode(a)); + // This breaks _227_mtrt (speed & correctness) and _222_mpegaudio (speed) + //b = _gvn.transform(new (C, 2) RoundFloatNode(0, b) ); + push( b ); + break; + + case Bytecodes::_l2f: + if (Matcher::convL2FSupported()) { + a = pop_pair(); + b = _gvn.transform( new (C, 2) ConvL2FNode(a)); + // For i486.ad, FILD doesn't restrict precision to 24 or 53 bits. + // Rather than storing the result into an FP register then pushing + // out to memory to round, the machine instruction that implements + // ConvL2D is responsible for rounding. + // c = precision_rounding(b); + c = _gvn.transform(b); + push(c); + } else { + l2f(); + } + break; + + case Bytecodes::_l2d: + a = pop_pair(); + b = _gvn.transform( new (C, 2) ConvL2DNode(a)); + // For i486.ad, rounding is always necessary (see _l2f above). + // c = dprecision_rounding(b); + c = _gvn.transform(b); + push_pair(c); + break; + + case Bytecodes::_f2l: + a = pop(); + b = _gvn.transform( new (C, 2) ConvF2LNode(a)); + push_pair(b); + break; + + case Bytecodes::_d2l: + a = pop_pair(); + b = _gvn.transform( new (C, 2) ConvD2LNode(a)); + push_pair(b); + break; + + case Bytecodes::_dsub: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) SubDNode(a,b) ); + d = dprecision_rounding(c); + push_pair( d ); + break; + + case Bytecodes::_dadd: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) AddDNode(a,b) ); + d = dprecision_rounding(c); + push_pair( d ); + break; + + case Bytecodes::_dmul: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) MulDNode(a,b) ); + d = dprecision_rounding(c); + push_pair( d ); + break; + + case Bytecodes::_ddiv: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) DivDNode(0,a,b) ); + d = dprecision_rounding(c); + push_pair( d ); + break; + + case Bytecodes::_dneg: + a = pop_pair(); + b = _gvn.transform(new (C, 2) NegDNode (a)); + push_pair(b); + break; + + case Bytecodes::_drem: + if (Matcher::has_match_rule(Op_ModD)) { + // Generate a ModD node. + b = pop_pair(); + a = pop_pair(); + // a % b + + c = _gvn.transform( new (C, 3) ModDNode(0,a,b) ); + d = dprecision_rounding(c); + push_pair( d ); + } + else { + // Generate a call. + modd(); + } + break; + + case Bytecodes::_dcmpl: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) CmpD3Node( a, b)); + push(c); + break; + + case Bytecodes::_dcmpg: + b = pop_pair(); + a = pop_pair(); + // Same as dcmpl but need to flip the unordered case. + // Commute the inputs, which negates the result sign except for unordered. + // Flip the unordered as well by using CmpD3 which implements + // unordered-lesser instead of unordered-greater semantics. + // Finally, negate the result bits. Result is same as using a + // CmpD3Greater except we did it with CmpD3 alone. + c = _gvn.transform( new (C, 3) CmpD3Node( b, a)); + c = _gvn.transform( new (C, 3) SubINode(_gvn.intcon(0),c) ); + push(c); + break; + + + // Note for longs -> lo word is on TOS, hi word is on TOS - 1 + case Bytecodes::_land: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) AndLNode(a,b) ); + push_pair(c); + break; + case Bytecodes::_lor: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) OrLNode(a,b) ); + push_pair(c); + break; + case Bytecodes::_lxor: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) XorLNode(a,b) ); + push_pair(c); + break; + + case Bytecodes::_lshl: + b = pop(); // the shift count + a = pop_pair(); // value to be shifted + c = _gvn.transform( new (C, 3) LShiftLNode(a,b) ); + push_pair(c); + break; + case Bytecodes::_lshr: + b = pop(); // the shift count + a = pop_pair(); // value to be shifted + c = _gvn.transform( new (C, 3) RShiftLNode(a,b) ); + push_pair(c); + break; + case Bytecodes::_lushr: + b = pop(); // the shift count + a = pop_pair(); // value to be shifted + c = _gvn.transform( new (C, 3) URShiftLNode(a,b) ); + push_pair(c); + break; + case Bytecodes::_lmul: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) MulLNode(a,b) ); + push_pair(c); + break; + + case Bytecodes::_lrem: + // Must keep both values on the expression-stack during null-check + assert(peek(0) == top(), "long word order"); + do_null_check(peek(1), T_LONG); + // Compile-time detect of null-exception? + if (stopped()) return; + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) ModLNode(control(),a,b) ); + push_pair(c); + break; + + case Bytecodes::_ldiv: + // Must keep both values on the expression-stack during null-check + assert(peek(0) == top(), "long word order"); + do_null_check(peek(1), T_LONG); + // Compile-time detect of null-exception? + if (stopped()) return; + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) DivLNode(control(),a,b) ); + push_pair(c); + break; + + case Bytecodes::_ladd: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) AddLNode(a,b) ); + push_pair(c); + break; + case Bytecodes::_lsub: + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) SubLNode(a,b) ); + push_pair(c); + break; + case Bytecodes::_lcmp: + // Safepoints are now inserted _before_ branches. The long-compare + // bytecode painfully produces a 3-way value (-1,0,+1) which requires a + // slew of control flow. These are usually followed by a CmpI vs zero and + // a branch; this pattern then optimizes to the obvious long-compare and + // branch. However, if the branch is backwards there's a Safepoint + // inserted. The inserted Safepoint captures the JVM state at the + // pre-branch point, i.e. it captures the 3-way value. Thus if a + // long-compare is used to control a loop the debug info will force + // computation of the 3-way value, even though the generated code uses a + // long-compare and branch. We try to rectify the situation by inserting + // a SafePoint here and have it dominate and kill the safepoint added at a + // following backwards branch. At this point the JVM state merely holds 2 + // longs but not the 3-way value. + if( UseLoopSafepoints ) { + switch( iter().next_bc() ) { + case Bytecodes::_ifgt: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifle: + case Bytecodes::_ifne: + case Bytecodes::_ifeq: + // If this is a backwards branch in the bytecodes, add Safepoint + maybe_add_safepoint(iter().next_get_dest()); + } + } + b = pop_pair(); + a = pop_pair(); + c = _gvn.transform( new (C, 3) CmpL3Node( a, b )); + push(c); + break; + + case Bytecodes::_lneg: + a = pop_pair(); + b = _gvn.transform( new (C, 3) SubLNode(longcon(0),a)); + push_pair(b); + break; + case Bytecodes::_l2i: + a = pop_pair(); + push( _gvn.transform( new (C, 2) ConvL2INode(a))); + break; + case Bytecodes::_i2l: + a = pop(); + b = _gvn.transform( new (C, 2) ConvI2LNode(a)); + push_pair(b); + break; + case Bytecodes::_i2b: + // Sign extend + a = pop(); + a = _gvn.transform( new (C, 3) LShiftINode(a,_gvn.intcon(24)) ); + a = _gvn.transform( new (C, 3) RShiftINode(a,_gvn.intcon(24)) ); + push( a ); + break; + case Bytecodes::_i2s: + a = pop(); + a = _gvn.transform( new (C, 3) LShiftINode(a,_gvn.intcon(16)) ); + a = _gvn.transform( new (C, 3) RShiftINode(a,_gvn.intcon(16)) ); + push( a ); + break; + case Bytecodes::_i2c: + a = pop(); + push( _gvn.transform( new (C, 3) AndINode(a,_gvn.intcon(0xFFFF)) ) ); + break; + + case Bytecodes::_i2f: + a = pop(); + b = _gvn.transform( new (C, 2) ConvI2FNode(a) ) ; + c = precision_rounding(b); + push (b); + break; + + case Bytecodes::_i2d: + a = pop(); + b = _gvn.transform( new (C, 2) ConvI2DNode(a)); + push_pair(b); + break; + + case Bytecodes::_iinc: // Increment local + i = iter().get_index(); // Get local index + set_local( i, _gvn.transform( new (C, 3) AddINode( _gvn.intcon(iter().get_iinc_con()), local(i) ) ) ); + break; + + // Exit points of synchronized methods must have an unlock node + case Bytecodes::_return: + return_current(NULL); + break; + + case Bytecodes::_ireturn: + case Bytecodes::_areturn: + case Bytecodes::_freturn: + return_current(pop()); + break; + case Bytecodes::_lreturn: + return_current(pop_pair()); + break; + case Bytecodes::_dreturn: + return_current(pop_pair()); + break; + + case Bytecodes::_athrow: + // null exception oop throws NULL pointer exception + do_null_check(peek(), T_OBJECT); + if (stopped()) return; + if (JvmtiExport::can_post_exceptions()) { + // "Full-speed throwing" is not necessary here, + // since we're notifying the VM on every throw. + uncommon_trap(Deoptimization::Reason_unhandled, + Deoptimization::Action_none); + return; + } + // Hook the thrown exception directly to subsequent handlers. + if (BailoutToInterpreterForThrows) { + // Keep method interpreted from now on. + uncommon_trap(Deoptimization::Reason_unhandled, + Deoptimization::Action_make_not_compilable); + return; + } + add_exception_state(make_exception_state(peek())); + break; + + case Bytecodes::_goto: // fall through + case Bytecodes::_goto_w: { + int target_bci = (bc() == Bytecodes::_goto) ? iter().get_dest() : iter().get_far_dest(); + + // If this is a backwards branch in the bytecodes, add Safepoint + maybe_add_safepoint(target_bci); + + // Update method data + profile_taken_branch(target_bci); + + // Merge the current control into the target basic block + merge(target_bci); + + // See if we can get some profile data and hand it off to the next block + Block *target_block = block()->successor_for_bci(target_bci); + if (target_block->pred_count() != 1) break; + ciMethodData* methodData = method()->method_data(); + if (!methodData->is_mature()) break; + ciProfileData* data = methodData->bci_to_data(bci()); + assert( data->is_JumpData(), "" ); + int taken = ((ciJumpData*)data)->taken(); + taken = method()->scale_count(taken); + target_block->set_count(taken); + break; + } + + case Bytecodes::_ifnull: + do_ifnull(BoolTest::eq); + break; + case Bytecodes::_ifnonnull: + do_ifnull(BoolTest::ne); + break; + + case Bytecodes::_if_acmpeq: btest = BoolTest::eq; goto handle_if_acmp; + case Bytecodes::_if_acmpne: btest = BoolTest::ne; goto handle_if_acmp; + handle_if_acmp: + // If this is a backwards branch in the bytecodes, add Safepoint + maybe_add_safepoint(iter().get_dest()); + a = pop(); + b = pop(); + c = _gvn.transform( new (C, 3) CmpPNode(b, a) ); + do_if(btest, c); + break; + + case Bytecodes::_ifeq: btest = BoolTest::eq; goto handle_ifxx; + case Bytecodes::_ifne: btest = BoolTest::ne; goto handle_ifxx; + case Bytecodes::_iflt: btest = BoolTest::lt; goto handle_ifxx; + case Bytecodes::_ifle: btest = BoolTest::le; goto handle_ifxx; + case Bytecodes::_ifgt: btest = BoolTest::gt; goto handle_ifxx; + case Bytecodes::_ifge: btest = BoolTest::ge; goto handle_ifxx; + handle_ifxx: + // If this is a backwards branch in the bytecodes, add Safepoint + maybe_add_safepoint(iter().get_dest()); + a = _gvn.intcon(0); + b = pop(); + c = _gvn.transform( new (C, 3) CmpINode(b, a) ); + do_if(btest, c); + break; + + case Bytecodes::_if_icmpeq: btest = BoolTest::eq; goto handle_if_icmp; + case Bytecodes::_if_icmpne: btest = BoolTest::ne; goto handle_if_icmp; + case Bytecodes::_if_icmplt: btest = BoolTest::lt; goto handle_if_icmp; + case Bytecodes::_if_icmple: btest = BoolTest::le; goto handle_if_icmp; + case Bytecodes::_if_icmpgt: btest = BoolTest::gt; goto handle_if_icmp; + case Bytecodes::_if_icmpge: btest = BoolTest::ge; goto handle_if_icmp; + handle_if_icmp: + // If this is a backwards branch in the bytecodes, add Safepoint + maybe_add_safepoint(iter().get_dest()); + a = pop(); + b = pop(); + c = _gvn.transform( new (C, 3) CmpINode( b, a ) ); + do_if(btest, c); + break; + + case Bytecodes::_tableswitch: + do_tableswitch(); + break; + + case Bytecodes::_lookupswitch: + do_lookupswitch(); + break; + + case Bytecodes::_invokestatic: + case Bytecodes::_invokespecial: + case Bytecodes::_invokevirtual: + case Bytecodes::_invokeinterface: + do_call(); + break; + case Bytecodes::_checkcast: + do_checkcast(); + break; + case Bytecodes::_instanceof: + do_instanceof(); + break; + case Bytecodes::_anewarray: + do_anewarray(); + break; + case Bytecodes::_newarray: + do_newarray((BasicType)iter().get_index()); + break; + case Bytecodes::_multianewarray: + do_multianewarray(); + break; + case Bytecodes::_new: + do_new(); + break; + + case Bytecodes::_jsr: + case Bytecodes::_jsr_w: + do_jsr(); + break; + + case Bytecodes::_ret: + do_ret(); + break; + + + case Bytecodes::_monitorenter: + do_monitor_enter(); + break; + + case Bytecodes::_monitorexit: + do_monitor_exit(); + break; + + case Bytecodes::_breakpoint: + // Breakpoint set concurrently to compile + // %%% use an uncommon trap? + C->record_failure("breakpoint in method"); + return; + + default: +#ifndef PRODUCT + map()->dump(99); +#endif + tty->print("\nUnhandled bytecode %s\n", Bytecodes::name(bc()) ); + ShouldNotReachHere(); + } + +#ifndef PRODUCT + IdealGraphPrinter *printer = IdealGraphPrinter::printer(); + if(printer) { + char buffer[256]; + sprintf(buffer, "Bytecode %d: %s", bci(), Bytecodes::name(bc())); + bool old = printer->traverse_outs(); + printer->set_traverse_outs(true); + printer->print_method(C, buffer, 3); + printer->set_traverse_outs(old); + } +#endif +} diff --git a/hotspot/src/share/vm/opto/parse3.cpp b/hotspot/src/share/vm/opto/parse3.cpp new file mode 100644 index 00000000000..d32053ce299 --- /dev/null +++ b/hotspot/src/share/vm/opto/parse3.cpp @@ -0,0 +1,463 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_parse3.cpp.incl" + +//============================================================================= +// Helper methods for _get* and _put* bytecodes +//============================================================================= +bool Parse::static_field_ok_in_clinit(ciField *field, ciMethod *method) { + // Could be the field_holder's method, or for a subklass. + // Better to check now than to Deoptimize as soon as we execute + assert( field->is_static(), "Only check if field is static"); + // is_being_initialized() is too generous. It allows access to statics + // by threads that are not running the before the finishes. + // return field->holder()->is_being_initialized(); + + // The following restriction is correct but conservative. + // It is also desirable to allow compilation of methods called from + // but this generated code will need to be made safe for execution by + // other threads, or the transition from interpreted to compiled code would + // need to be guarded. + ciInstanceKlass *field_holder = field->holder(); + + bool access_OK = false; + if (method->holder()->is_subclass_of(field_holder)) { + if (method->is_static()) { + if (method->name() == ciSymbol::class_initializer_name()) { + // OK to access static fields inside initializer + access_OK = true; + } + } else { + if (method->name() == ciSymbol::object_initializer_name()) { + // It's also OK to access static fields inside a constructor, + // because any thread calling the constructor must first have + // synchronized on the class by executing a '_new' bytecode. + access_OK = true; + } + } + } + + return access_OK; + +} + + +void Parse::do_field_access(bool is_get, bool is_field) { + bool will_link; + ciField* field = iter().get_field(will_link); + assert(will_link, "getfield: typeflow responsibility"); + + ciInstanceKlass* field_holder = field->holder(); + + if (is_field == field->is_static()) { + // Interpreter will throw java_lang_IncompatibleClassChangeError + // Check this before allowing methods to access static fields + uncommon_trap(Deoptimization::Reason_unhandled, + Deoptimization::Action_none); + return; + } + + if (!is_field && !field_holder->is_initialized()) { + if (!static_field_ok_in_clinit(field, method())) { + uncommon_trap(Deoptimization::Reason_uninitialized, + Deoptimization::Action_reinterpret, + NULL, "!static_field_ok_in_clinit"); + return; + } + } + + assert(field->will_link(method()->holder(), bc()), "getfield: typeflow responsibility"); + + // Note: We do not check for an unloaded field type here any more. + + // Generate code for the object pointer. + Node* obj; + if (is_field) { + int obj_depth = is_get ? 0 : field->type()->size(); + obj = do_null_check(peek(obj_depth), T_OBJECT); + // Compile-time detect of null-exception? + if (stopped()) return; + + const TypeInstPtr *tjp = TypeInstPtr::make(TypePtr::NotNull, iter().get_declared_field_holder()); + assert(_gvn.type(obj)->higher_equal(tjp), "cast_up is no longer needed"); + + if (is_get) { + --_sp; // pop receiver before getting + do_get_xxx(tjp, obj, field, is_field); + } else { + do_put_xxx(tjp, obj, field, is_field); + --_sp; // pop receiver after putting + } + } else { + const TypeKlassPtr* tkp = TypeKlassPtr::make(field_holder); + obj = _gvn.makecon(tkp); + if (is_get) { + do_get_xxx(tkp, obj, field, is_field); + } else { + do_put_xxx(tkp, obj, field, is_field); + } + } +} + + +void Parse::do_get_xxx(const TypePtr* obj_type, Node* obj, ciField* field, bool is_field) { + // Does this field have a constant value? If so, just push the value. + if (field->is_constant() && push_constant(field->constant_value())) return; + + ciType* field_klass = field->type(); + bool is_vol = field->is_volatile(); + + // Compute address and memory type. + int offset = field->offset_in_bytes(); + const TypePtr* adr_type = C->alias_type(field)->adr_type(); + Node *adr = basic_plus_adr(obj, obj, offset); + BasicType bt = field->layout_type(); + + // Build the resultant type of the load + const Type *type; + + bool must_assert_null = false; + + if( bt == T_OBJECT ) { + if (!field->type()->is_loaded()) { + type = TypeInstPtr::BOTTOM; + must_assert_null = true; + } else if (field->is_constant()) { + // This can happen if the constant oop is non-perm. + ciObject* con = field->constant_value().as_object(); + // Do not "join" in the previous type; it doesn't add value, + // and may yield a vacuous result if the field is of interface type. + type = TypeOopPtr::make_from_constant(con)->isa_oopptr(); + assert(type != NULL, "field singleton type must be consistent"); + } else { + type = TypeOopPtr::make_from_klass(field_klass->as_klass()); + } + } else { + type = Type::get_const_basic_type(bt); + } + // Build the load. + Node* ld = make_load(NULL, adr, type, bt, adr_type, is_vol); + + // Adjust Java stack + if (type2size[bt] == 1) + push(ld); + else + push_pair(ld); + + if (must_assert_null) { + // Do not take a trap here. It's possible that the program + // will never load the field's class, and will happily see + // null values in this field forever. Don't stumble into a + // trap for such a program, or we might get a long series + // of useless recompilations. (Or, we might load a class + // which should not be loaded.) If we ever see a non-null + // value, we will then trap and recompile. (The trap will + // not need to mention the class index, since the class will + // already have been loaded if we ever see a non-null value.) + // uncommon_trap(iter().get_field_signature_index()); +#ifndef PRODUCT + if (PrintOpto && (Verbose || WizardMode)) { + method()->print_name(); tty->print_cr(" asserting nullness of field at bci: %d", bci()); + } +#endif + if (C->log() != NULL) { + C->log()->elem("assert_null reason='field' klass='%d'", + C->log()->identify(field->type())); + } + // If there is going to be a trap, put it at the next bytecode: + set_bci(iter().next_bci()); + do_null_assert(peek(), T_OBJECT); + set_bci(iter().cur_bci()); // put it back + } + + // If reference is volatile, prevent following memory ops from + // floating up past the volatile read. Also prevents commoning + // another volatile read. + if (field->is_volatile()) { + // Memory barrier includes bogus read of value to force load BEFORE membar + insert_mem_bar(Op_MemBarAcquire, ld); + } +} + +void Parse::do_put_xxx(const TypePtr* obj_type, Node* obj, ciField* field, bool is_field) { + bool is_vol = field->is_volatile(); + // If reference is volatile, prevent following memory ops from + // floating down past the volatile write. Also prevents commoning + // another volatile read. + if (is_vol) insert_mem_bar(Op_MemBarRelease); + + // Compute address and memory type. + int offset = field->offset_in_bytes(); + const TypePtr* adr_type = C->alias_type(field)->adr_type(); + Node* adr = basic_plus_adr(obj, obj, offset); + BasicType bt = field->layout_type(); + // Value to be stored + Node* val = type2size[bt] == 1 ? pop() : pop_pair(); + // Round doubles before storing + if (bt == T_DOUBLE) val = dstore_rounding(val); + + // Store the value. + Node* store; + if (bt == T_OBJECT) { + const TypePtr* field_type; + if (!field->type()->is_loaded()) { + field_type = TypeInstPtr::BOTTOM; + } else { + field_type = TypeOopPtr::make_from_klass(field->type()->as_klass()); + } + store = store_oop_to_object( control(), obj, adr, adr_type, val, field_type, bt); + } else { + store = store_to_memory( control(), adr, val, bt, adr_type, is_vol ); + } + + // If reference is volatile, prevent following volatiles ops from + // floating up before the volatile write. + if (is_vol) { + // First place the specific membar for THIS volatile index. This first + // membar is dependent on the store, keeping any other membars generated + // below from floating up past the store. + int adr_idx = C->get_alias_index(adr_type); + insert_mem_bar_volatile(Op_MemBarVolatile, adr_idx); + + // Now place a membar for AliasIdxBot for the unknown yet-to-be-parsed + // volatile alias indices. Skip this if the membar is redundant. + if (adr_idx != Compile::AliasIdxBot) { + insert_mem_bar_volatile(Op_MemBarVolatile, Compile::AliasIdxBot); + } + + // Finally, place alias-index-specific membars for each volatile index + // that isn't the adr_idx membar. Typically there's only 1 or 2. + for( int i = Compile::AliasIdxRaw; i < C->num_alias_types(); i++ ) { + if (i != adr_idx && C->alias_type(i)->is_volatile()) { + insert_mem_bar_volatile(Op_MemBarVolatile, i); + } + } + } + + // If the field is final, the rules of Java say we are in or . + // Note the presence of writes to final non-static fields, so that we + // can insert a memory barrier later on to keep the writes from floating + // out of the constructor. + if (is_field && field->is_final()) { + set_wrote_final(true); + } +} + + +bool Parse::push_constant(ciConstant constant) { + switch (constant.basic_type()) { + case T_BOOLEAN: push( intcon(constant.as_boolean()) ); break; + case T_INT: push( intcon(constant.as_int()) ); break; + case T_CHAR: push( intcon(constant.as_char()) ); break; + case T_BYTE: push( intcon(constant.as_byte()) ); break; + case T_SHORT: push( intcon(constant.as_short()) ); break; + case T_FLOAT: push( makecon(TypeF::make(constant.as_float())) ); break; + case T_DOUBLE: push_pair( makecon(TypeD::make(constant.as_double())) ); break; + case T_LONG: push_pair( longcon(constant.as_long()) ); break; + case T_ARRAY: + case T_OBJECT: { + // the oop is in perm space if the ciObject "has_encoding" + ciObject* oop_constant = constant.as_object(); + if (oop_constant->is_null_object()) { + push( zerocon(T_OBJECT) ); + break; + } else if (oop_constant->has_encoding()) { + push( makecon(TypeOopPtr::make_from_constant(oop_constant)) ); + break; + } else { + // we cannot inline the oop, but we can use it later to narrow a type + return false; + } + } + case T_ILLEGAL: { + // Invalid ciConstant returned due to OutOfMemoryError in the CI + assert(C->env()->failing(), "otherwise should not see this"); + // These always occur because of object types; we are going to + // bail out anyway, so make the stack depths match up + push( zerocon(T_OBJECT) ); + return false; + } + default: + ShouldNotReachHere(); + return false; + } + + // success + return true; +} + + + +//============================================================================= +void Parse::do_anewarray() { + bool will_link; + ciKlass* klass = iter().get_klass(will_link); + + // Uncommon Trap when class that array contains is not loaded + // we need the loaded class for the rest of graph; do not + // initialize the container class (see Java spec)!!! + assert(will_link, "anewarray: typeflow responsibility"); + + ciObjArrayKlass* array_klass = ciObjArrayKlass::make(klass); + // Check that array_klass object is loaded + if (!array_klass->is_loaded()) { + // Generate uncommon_trap for unloaded array_class + uncommon_trap(Deoptimization::Reason_unloaded, + Deoptimization::Action_reinterpret, + array_klass); + return; + } + + kill_dead_locals(); + + const TypeKlassPtr* array_klass_type = TypeKlassPtr::make(array_klass); + Node* count_val = pop(); + Node* obj = new_array(makecon(array_klass_type), count_val); + push(obj); +} + + +void Parse::do_newarray(BasicType elem_type) { + kill_dead_locals(); + + Node* count_val = pop(); + const TypeKlassPtr* array_klass = TypeKlassPtr::make(ciTypeArrayKlass::make(elem_type)); + Node* obj = new_array(makecon(array_klass), count_val); + // Push resultant oop onto stack + push(obj); +} + +// Expand simple expressions like new int[3][5] and new Object[2][nonConLen]. +// Also handle the degenerate 1-dimensional case of anewarray. +Node* Parse::expand_multianewarray(ciArrayKlass* array_klass, Node* *lengths, int ndimensions) { + Node* length = lengths[0]; + assert(length != NULL, ""); + Node* array = new_array(makecon(TypeKlassPtr::make(array_klass)), length); + if (ndimensions > 1) { + jint length_con = find_int_con(length, -1); + guarantee(length_con >= 0, "non-constant multianewarray"); + ciArrayKlass* array_klass_1 = array_klass->as_obj_array_klass()->element_klass()->as_array_klass(); + const TypePtr* adr_type = TypeAryPtr::OOPS; + const Type* elemtype = _gvn.type(array)->is_aryptr()->elem(); + const intptr_t header = arrayOopDesc::base_offset_in_bytes(T_OBJECT); + for (jint i = 0; i < length_con; i++) { + Node* elem = expand_multianewarray(array_klass_1, &lengths[1], ndimensions-1); + intptr_t offset = header + ((intptr_t)i << LogBytesPerWord); + Node* eaddr = basic_plus_adr(array, offset); + store_oop_to_array(control(), array, eaddr, adr_type, elem, elemtype, T_OBJECT); + } + } + return array; +} + +void Parse::do_multianewarray() { + int ndimensions = iter().get_dimensions(); + + // the m-dimensional array + bool will_link; + ciArrayKlass* array_klass = iter().get_klass(will_link)->as_array_klass(); + assert(will_link, "multianewarray: typeflow responsibility"); + + // Note: Array classes are always initialized; no is_initialized check. + + enum { MAX_DIMENSION = 5 }; + if (ndimensions > MAX_DIMENSION || ndimensions <= 0) { + uncommon_trap(Deoptimization::Reason_unhandled, + Deoptimization::Action_none); + return; + } + + kill_dead_locals(); + + // get the lengths from the stack (first dimension is on top) + Node* length[MAX_DIMENSION+1]; + length[ndimensions] = NULL; // terminating null for make_runtime_call + int j; + for (j = ndimensions-1; j >= 0 ; j--) length[j] = pop(); + + // The original expression was of this form: new T[length0][length1]... + // It is often the case that the lengths are small (except the last). + // If that happens, use the fast 1-d creator a constant number of times. + const jint expand_limit = MIN2((juint)MultiArrayExpandLimit, (juint)100); + jint expand_count = 1; // count of allocations in the expansion + jint expand_fanout = 1; // running total fanout + for (j = 0; j < ndimensions-1; j++) { + jint dim_con = find_int_con(length[j], -1); + expand_fanout *= dim_con; + expand_count += expand_fanout; // count the level-J sub-arrays + if (dim_con < 0 + || dim_con > expand_limit + || expand_count > expand_limit) { + expand_count = 0; + break; + } + } + + // Can use multianewarray instead of [a]newarray if only one dimension, + // or if all non-final dimensions are small constants. + if (expand_count == 1 || (1 <= expand_count && expand_count <= expand_limit)) { + Node* obj = expand_multianewarray(array_klass, &length[0], ndimensions); + push(obj); + return; + } + + address fun = NULL; + switch (ndimensions) { + //case 1: Actually, there is no case 1. It's handled by new_array. + case 2: fun = OptoRuntime::multianewarray2_Java(); break; + case 3: fun = OptoRuntime::multianewarray3_Java(); break; + case 4: fun = OptoRuntime::multianewarray4_Java(); break; + case 5: fun = OptoRuntime::multianewarray5_Java(); break; + default: ShouldNotReachHere(); + }; + + Node* c = make_runtime_call(RC_NO_LEAF | RC_NO_IO, + OptoRuntime::multianewarray_Type(ndimensions), + fun, NULL, TypeRawPtr::BOTTOM, + makecon(TypeKlassPtr::make(array_klass)), + length[0], length[1], length[2], + length[3], length[4]); + Node* res = _gvn.transform(new (C, 1) ProjNode(c, TypeFunc::Parms)); + + const Type* type = TypeOopPtr::make_from_klass_raw(array_klass); + + // Improve the type: We know it's not null, exact, and of a given length. + type = type->is_ptr()->cast_to_ptr_type(TypePtr::NotNull); + type = type->is_aryptr()->cast_to_exactness(true); + + const TypeInt* ltype = _gvn.find_int_type(length[0]); + if (ltype != NULL) + type = type->is_aryptr()->cast_to_size(ltype); + + // We cannot sharpen the nested sub-arrays, since the top level is mutable. + + Node* cast = _gvn.transform( new (C, 2) CheckCastPPNode(control(), res, type) ); + push(cast); + + // Possible improvements: + // - Make a fast path for small multi-arrays. (W/ implicit init. loops.) + // - Issue CastII against length[*] values, to TypeInt::POS. +} diff --git a/hotspot/src/share/vm/opto/parseHelper.cpp b/hotspot/src/share/vm/opto/parseHelper.cpp new file mode 100644 index 00000000000..d34ca998c70 --- /dev/null +++ b/hotspot/src/share/vm/opto/parseHelper.cpp @@ -0,0 +1,520 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_parseHelper.cpp.incl" + +//------------------------------make_dtrace_method_entry_exit ---------------- +// Dtrace -- record entry or exit of a method if compiled with dtrace support +void GraphKit::make_dtrace_method_entry_exit(ciMethod* method, bool is_entry) { + const TypeFunc *call_type = OptoRuntime::dtrace_method_entry_exit_Type(); + address call_address = is_entry ? CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry) : + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit); + const char *call_name = is_entry ? "dtrace_method_entry" : "dtrace_method_exit"; + + // Get base of thread-local storage area + Node* thread = _gvn.transform( new (C, 1) ThreadLocalNode() ); + + // Get method + const TypeInstPtr* method_type = TypeInstPtr::make(TypePtr::Constant, method->klass(), true, method, 0); + Node *method_node = _gvn.transform( new (C, 1) ConPNode(method_type) ); + + kill_dead_locals(); + + // For some reason, this call reads only raw memory. + const TypePtr* raw_adr_type = TypeRawPtr::BOTTOM; + make_runtime_call(RC_LEAF | RC_NARROW_MEM, + call_type, call_address, + call_name, raw_adr_type, + thread, method_node); +} + + +//============================================================================= +//------------------------------do_checkcast----------------------------------- +void Parse::do_checkcast() { + bool will_link; + ciKlass* klass = iter().get_klass(will_link); + + Node *obj = peek(); + + // Throw uncommon trap if class is not loaded or the value we are casting + // _from_ is not loaded, and value is not null. If the value _is_ NULL, + // then the checkcast does nothing. + const TypeInstPtr *tp = _gvn.type(obj)->isa_instptr(); + if (!will_link || (tp && !tp->is_loaded())) { + if (C->log() != NULL) { + if (!will_link) { + C->log()->elem("assert_null reason='checkcast' klass='%d'", + C->log()->identify(klass)); + } + if (tp && !tp->is_loaded()) { + // %%% Cannot happen? + C->log()->elem("assert_null reason='checkcast source' klass='%d'", + C->log()->identify(tp->klass())); + } + } + do_null_assert(obj, T_OBJECT); + assert( stopped() || _gvn.type(peek())->higher_equal(TypePtr::NULL_PTR), "what's left behind is null" ); + if (!stopped()) { + profile_null_checkcast(); + } + return; + } + + Node *res = gen_checkcast(obj, makecon(TypeKlassPtr::make(klass)) ); + + // Pop from stack AFTER gen_checkcast because it can uncommon trap and + // the debug info has to be correct. + pop(); + push(res); +} + + +//------------------------------do_instanceof---------------------------------- +void Parse::do_instanceof() { + if (stopped()) return; + // We would like to return false if class is not loaded, emitting a + // dependency, but Java requires instanceof to load its operand. + + // Throw uncommon trap if class is not loaded + bool will_link; + ciKlass* klass = iter().get_klass(will_link); + + if (!will_link) { + if (C->log() != NULL) { + C->log()->elem("assert_null reason='instanceof' klass='%d'", + C->log()->identify(klass)); + } + do_null_assert(peek(), T_OBJECT); + assert( stopped() || _gvn.type(peek())->higher_equal(TypePtr::NULL_PTR), "what's left behind is null" ); + if (!stopped()) { + // The object is now known to be null. + // Shortcut the effect of gen_instanceof and return "false" directly. + pop(); // pop the null + push(_gvn.intcon(0)); // push false answer + } + return; + } + + // Push the bool result back on stack + push( gen_instanceof( pop(), makecon(TypeKlassPtr::make(klass)) ) ); +} + +//------------------------------array_store_check------------------------------ +// pull array from stack and check that the store is valid +void Parse::array_store_check() { + + // Shorthand access to array store elements + Node *obj = stack(_sp-1); + Node *idx = stack(_sp-2); + Node *ary = stack(_sp-3); + + if (_gvn.type(obj) == TypePtr::NULL_PTR) { + // There's never a type check on null values. + // This cutout lets us avoid the uncommon_trap(Reason_array_check) + // below, which turns into a performance liability if the + // gen_checkcast folds up completely. + return; + } + + // Extract the array klass type + int klass_offset = oopDesc::klass_offset_in_bytes(); + Node* p = basic_plus_adr( ary, ary, klass_offset ); + // p's type is array-of-OOPS plus klass_offset + Node* array_klass = _gvn.transform(new (C, 3) LoadKlassNode(0, immutable_memory(), p, TypeInstPtr::KLASS)); + // Get the array klass + const TypeKlassPtr *tak = _gvn.type(array_klass)->is_klassptr(); + + // array_klass's type is generally INexact array-of-oop. Heroically + // cast the array klass to EXACT array and uncommon-trap if the cast + // fails. + bool always_see_exact_class = false; + if (MonomorphicArrayCheck + && !too_many_traps(Deoptimization::Reason_array_check)) { + always_see_exact_class = true; + // (If no MDO at all, hope for the best, until a trap actually occurs.) + } + + // Is the array klass is exactly its defined type? + if (always_see_exact_class && !tak->klass_is_exact()) { + // Make a constant out of the inexact array klass + const TypeKlassPtr *extak = tak->cast_to_exactness(true)->is_klassptr(); + Node* con = makecon(extak); + Node* cmp = _gvn.transform(new (C, 3) CmpPNode( array_klass, con )); + Node* bol = _gvn.transform(new (C, 2) BoolNode( cmp, BoolTest::eq )); + Node* ctrl= control(); + { BuildCutout unless(this, bol, PROB_MAX); + uncommon_trap(Deoptimization::Reason_array_check, + Deoptimization::Action_maybe_recompile, + tak->klass()); + } + if (stopped()) { // MUST uncommon-trap? + set_control(ctrl); // Then Don't Do It, just fall into the normal checking + } else { // Cast array klass to exactness: + // Use the exact constant value we know it is. + replace_in_map(array_klass,con); + CompileLog* log = C->log(); + if (log != NULL) { + log->elem("cast_up reason='monomorphic_array' from='%d' to='(exact)'", + log->identify(tak->klass())); + } + array_klass = con; // Use cast value moving forward + } + } + + // Come here for polymorphic array klasses + + // Extract the array element class + int element_klass_offset = objArrayKlass::element_klass_offset_in_bytes() + sizeof(oopDesc); + Node *p2 = basic_plus_adr(array_klass, array_klass, element_klass_offset); + Node *a_e_klass = _gvn.transform(new (C, 3) LoadKlassNode(0, immutable_memory(), p2, tak)); + + // Check (the hard way) and throw if not a subklass. + // Result is ignored, we just need the CFG effects. + gen_checkcast( obj, a_e_klass ); +} + + +//------------------------------do_new----------------------------------------- +void Parse::do_new() { + kill_dead_locals(); + + bool will_link; + ciInstanceKlass* klass = iter().get_klass(will_link)->as_instance_klass(); + assert(will_link, "_new: typeflow responsibility"); + + // Should initialize, or throw an InstantiationError? + if (!klass->is_initialized() || + klass->is_abstract() || klass->is_interface() || + klass->name() == ciSymbol::java_lang_Class() || + iter().is_unresolved_klass()) { + uncommon_trap(Deoptimization::Reason_uninitialized, + Deoptimization::Action_reinterpret, + klass); + return; + } + + Node* kls = makecon(TypeKlassPtr::make(klass)); + Node* obj = new_instance(kls); + + // Push resultant oop onto stack + push(obj); +} + +#ifndef PRODUCT +//------------------------------dump_map_adr_mem------------------------------- +// Debug dump of the mapping from address types to MergeMemNode indices. +void Parse::dump_map_adr_mem() const { + tty->print_cr("--- Mapping from address types to memory Nodes ---"); + MergeMemNode *mem = map() == NULL ? NULL : (map()->memory()->is_MergeMem() ? + map()->memory()->as_MergeMem() : NULL); + for (uint i = 0; i < (uint)C->num_alias_types(); i++) { + C->alias_type(i)->print_on(tty); + tty->print("\t"); + // Node mapping, if any + if (mem && i < mem->req() && mem->in(i) && mem->in(i) != mem->empty_memory()) { + mem->in(i)->dump(); + } else { + tty->cr(); + } + } +} + +#endif + + +//============================================================================= +// +// parser methods for profiling + + +//----------------------test_counter_against_threshold ------------------------ +void Parse::test_counter_against_threshold(Node* cnt, int limit) { + // Test the counter against the limit and uncommon trap if greater. + + // This code is largely copied from the range check code in + // array_addressing() + + // Test invocation count vs threshold + Node *threshold = makecon(TypeInt::make(limit)); + Node *chk = _gvn.transform( new (C, 3) CmpUNode( cnt, threshold) ); + BoolTest::mask btest = BoolTest::lt; + Node *tst = _gvn.transform( new (C, 2) BoolNode( chk, btest) ); + // Branch to failure if threshold exceeded + { BuildCutout unless(this, tst, PROB_ALWAYS); + uncommon_trap(Deoptimization::Reason_age, + Deoptimization::Action_maybe_recompile); + } +} + +//----------------------increment_and_test_invocation_counter------------------- +void Parse::increment_and_test_invocation_counter(int limit) { + if (!count_invocations()) return; + + // Get the methodOop node. + const TypePtr* adr_type = TypeOopPtr::make_from_constant(method()); + Node *methodOop_node = makecon(adr_type); + + // Load the interpreter_invocation_counter from the methodOop. + int offset = methodOopDesc::interpreter_invocation_counter_offset_in_bytes(); + Node* adr_node = basic_plus_adr(methodOop_node, methodOop_node, offset); + Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type); + + test_counter_against_threshold(cnt, limit); + + // Add one to the counter and store + Node* incr = _gvn.transform(new (C, 3) AddINode(cnt, _gvn.intcon(1))); + store_to_memory( NULL, adr_node, incr, T_INT, adr_type ); +} + +//----------------------------method_data_addressing--------------------------- +Node* Parse::method_data_addressing(ciMethodData* md, ciProfileData* data, ByteSize counter_offset, Node* idx, uint stride) { + // Get offset within methodDataOop of the data array + ByteSize data_offset = methodDataOopDesc::data_offset(); + + // Get cell offset of the ProfileData within data array + int cell_offset = md->dp_to_di(data->dp()); + + // Add in counter_offset, the # of bytes into the ProfileData of counter or flag + int offset = in_bytes(data_offset) + cell_offset + in_bytes(counter_offset); + + const TypePtr* adr_type = TypeOopPtr::make_from_constant(md); + Node* mdo = makecon(adr_type); + Node* ptr = basic_plus_adr(mdo, mdo, offset); + + if (stride != 0) { + Node* str = _gvn.MakeConX(stride); + Node* scale = _gvn.transform( new (C, 3) MulXNode( idx, str ) ); + ptr = _gvn.transform( new (C, 4) AddPNode( mdo, ptr, scale ) ); + } + + return ptr; +} + +//--------------------------increment_md_counter_at---------------------------- +void Parse::increment_md_counter_at(ciMethodData* md, ciProfileData* data, ByteSize counter_offset, Node* idx, uint stride) { + Node* adr_node = method_data_addressing(md, data, counter_offset, idx, stride); + + const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr(); + Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type); + Node* incr = _gvn.transform(new (C, 3) AddINode(cnt, _gvn.intcon(DataLayout::counter_increment))); + store_to_memory(NULL, adr_node, incr, T_INT, adr_type ); +} + +//--------------------------test_for_osr_md_counter_at------------------------- +void Parse::test_for_osr_md_counter_at(ciMethodData* md, ciProfileData* data, ByteSize counter_offset, int limit) { + Node* adr_node = method_data_addressing(md, data, counter_offset); + + const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr(); + Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type); + + test_counter_against_threshold(cnt, limit); +} + +//-------------------------------set_md_flag_at-------------------------------- +void Parse::set_md_flag_at(ciMethodData* md, ciProfileData* data, int flag_constant) { + Node* adr_node = method_data_addressing(md, data, DataLayout::flags_offset()); + + const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr(); + Node* flags = make_load(NULL, adr_node, TypeInt::BYTE, T_BYTE, adr_type); + Node* incr = _gvn.transform(new (C, 3) OrINode(flags, _gvn.intcon(flag_constant))); + store_to_memory(NULL, adr_node, incr, T_BYTE, adr_type); +} + +//----------------------------profile_taken_branch----------------------------- +void Parse::profile_taken_branch(int target_bci, bool force_update) { + // This is a potential osr_site if we have a backedge. + int cur_bci = bci(); + bool osr_site = + (target_bci <= cur_bci) && count_invocations() && UseOnStackReplacement; + + // If we are going to OSR, restart at the target bytecode. + set_bci(target_bci); + + // To do: factor out the the limit calculations below. These duplicate + // the similar limit calculations in the interpreter. + + if (method_data_update() || force_update) { + ciMethodData* md = method()->method_data(); + assert(md != NULL, "expected valid ciMethodData"); + ciProfileData* data = md->bci_to_data(cur_bci); + assert(data->is_JumpData(), "need JumpData for taken branch"); + increment_md_counter_at(md, data, JumpData::taken_offset()); + } + + // In the new tiered system this is all we need to do. In the old + // (c2 based) tiered sytem we must do the code below. +#ifndef TIERED + if (method_data_update()) { + ciMethodData* md = method()->method_data(); + if (osr_site) { + ciProfileData* data = md->bci_to_data(cur_bci); + int limit = (CompileThreshold + * (OnStackReplacePercentage - InterpreterProfilePercentage)) / 100; + test_for_osr_md_counter_at(md, data, JumpData::taken_offset(), limit); + } + } else { + // With method data update off, use the invocation counter to trigger an + // OSR compilation, as done in the interpreter. + if (osr_site) { + int limit = (CompileThreshold * OnStackReplacePercentage) / 100; + increment_and_test_invocation_counter(limit); + } + } +#endif // TIERED + + // Restore the original bytecode. + set_bci(cur_bci); +} + +//--------------------------profile_not_taken_branch--------------------------- +void Parse::profile_not_taken_branch(bool force_update) { + + if (method_data_update() || force_update) { + ciMethodData* md = method()->method_data(); + assert(md != NULL, "expected valid ciMethodData"); + ciProfileData* data = md->bci_to_data(bci()); + assert(data->is_BranchData(), "need BranchData for not taken branch"); + increment_md_counter_at(md, data, BranchData::not_taken_offset()); + } + +} + +//---------------------------------profile_call-------------------------------- +void Parse::profile_call(Node* receiver) { + if (!method_data_update()) return; + + profile_generic_call(); + + switch (bc()) { + case Bytecodes::_invokevirtual: + case Bytecodes::_invokeinterface: + profile_receiver_type(receiver); + break; + case Bytecodes::_invokestatic: + case Bytecodes::_invokespecial: + break; + default: fatal("unexpected call bytecode"); + } +} + +//------------------------------profile_generic_call--------------------------- +void Parse::profile_generic_call() { + assert(method_data_update(), "must be generating profile code"); + + ciMethodData* md = method()->method_data(); + assert(md != NULL, "expected valid ciMethodData"); + ciProfileData* data = md->bci_to_data(bci()); + assert(data->is_CounterData(), "need CounterData for not taken branch"); + increment_md_counter_at(md, data, CounterData::count_offset()); +} + +//-----------------------------profile_receiver_type--------------------------- +void Parse::profile_receiver_type(Node* receiver) { + assert(method_data_update(), "must be generating profile code"); + + // Skip if we aren't tracking receivers + if (TypeProfileWidth < 1) return; + + ciMethodData* md = method()->method_data(); + assert(md != NULL, "expected valid ciMethodData"); + ciProfileData* data = md->bci_to_data(bci()); + assert(data->is_ReceiverTypeData(), "need ReceiverTypeData here"); + ciReceiverTypeData* rdata = (ciReceiverTypeData*)data->as_ReceiverTypeData(); + + Node* method_data = method_data_addressing(md, rdata, in_ByteSize(0)); + + // Using an adr_type of TypePtr::BOTTOM to work around anti-dep problems. + // A better solution might be to use TypeRawPtr::BOTTOM with RC_NARROW_MEM. + make_runtime_call(RC_LEAF, OptoRuntime::profile_receiver_type_Type(), + CAST_FROM_FN_PTR(address, + OptoRuntime::profile_receiver_type_C), + "profile_receiver_type_C", + TypePtr::BOTTOM, + method_data, receiver); +} + +//---------------------------------profile_ret--------------------------------- +void Parse::profile_ret(int target_bci) { + if (!method_data_update()) return; + + // Skip if we aren't tracking ret targets + if (TypeProfileWidth < 1) return; + + ciMethodData* md = method()->method_data(); + assert(md != NULL, "expected valid ciMethodData"); + ciProfileData* data = md->bci_to_data(bci()); + assert(data->is_RetData(), "need RetData for ret"); + ciRetData* ret_data = (ciRetData*)data->as_RetData(); + + // Look for the target_bci is already in the table + uint row; + bool table_full = true; + for (row = 0; row < ret_data->row_limit(); row++) { + int key = ret_data->bci(row); + table_full &= (key != RetData::no_bci); + if (key == target_bci) break; + } + + if (row >= ret_data->row_limit()) { + // The target_bci was not found in the table. + if (!table_full) { + // XXX: Make slow call to update RetData + } + return; + } + + // the target_bci is already in the table + increment_md_counter_at(md, data, RetData::bci_count_offset(row)); +} + +//--------------------------profile_null_checkcast---------------------------- +void Parse::profile_null_checkcast() { + // Set the null-seen flag, done in conjunction with the usual null check. We + // never unset the flag, so this is a one-way switch. + if (!method_data_update()) return; + + ciMethodData* md = method()->method_data(); + assert(md != NULL, "expected valid ciMethodData"); + ciProfileData* data = md->bci_to_data(bci()); + assert(data->is_BitData(), "need BitData for checkcast"); + set_md_flag_at(md, data, BitData::null_seen_byte_constant()); +} + +//-----------------------------profile_switch_case----------------------------- +void Parse::profile_switch_case(int table_index) { + if (!method_data_update()) return; + + ciMethodData* md = method()->method_data(); + assert(md != NULL, "expected valid ciMethodData"); + + ciProfileData* data = md->bci_to_data(bci()); + assert(data->is_MultiBranchData(), "need MultiBranchData for switch case"); + if (table_index >= 0) { + increment_md_counter_at(md, data, MultiBranchData::case_count_offset(table_index)); + } else { + increment_md_counter_at(md, data, MultiBranchData::default_count_offset()); + } +} diff --git a/hotspot/src/share/vm/opto/phase.cpp b/hotspot/src/share/vm/opto/phase.cpp new file mode 100644 index 00000000000..5e046dab061 --- /dev/null +++ b/hotspot/src/share/vm/opto/phase.cpp @@ -0,0 +1,164 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_phase.cpp.incl" + +#ifndef PRODUCT +int Phase::_total_bytes_compiled = 0; + +elapsedTimer Phase::_t_totalCompilation; +elapsedTimer Phase::_t_methodCompilation; +elapsedTimer Phase::_t_stubCompilation; +#endif + +// The next timers used for LogCompilation +elapsedTimer Phase::_t_parser; +elapsedTimer Phase::_t_escapeAnalysis; +elapsedTimer Phase::_t_optimizer; +elapsedTimer Phase::_t_idealLoop; +elapsedTimer Phase::_t_ccp; +elapsedTimer Phase::_t_matcher; +elapsedTimer Phase::_t_registerAllocation; +elapsedTimer Phase::_t_output; + +#ifndef PRODUCT +elapsedTimer Phase::_t_graphReshaping; +elapsedTimer Phase::_t_scheduler; +elapsedTimer Phase::_t_removeEmptyBlocks; +elapsedTimer Phase::_t_macroExpand; +elapsedTimer Phase::_t_peephole; +elapsedTimer Phase::_t_codeGeneration; +elapsedTimer Phase::_t_registerMethod; +elapsedTimer Phase::_t_temporaryTimer1; +elapsedTimer Phase::_t_temporaryTimer2; + +// Subtimers for _t_optimizer +elapsedTimer Phase::_t_iterGVN; +elapsedTimer Phase::_t_iterGVN2; + +// Subtimers for _t_registerAllocation +elapsedTimer Phase::_t_ctorChaitin; +elapsedTimer Phase::_t_buildIFGphysical; +elapsedTimer Phase::_t_computeLive; +elapsedTimer Phase::_t_regAllocSplit; +elapsedTimer Phase::_t_postAllocCopyRemoval; +elapsedTimer Phase::_t_fixupSpills; + +// Subtimers for _t_output +elapsedTimer Phase::_t_instrSched; +elapsedTimer Phase::_t_buildOopMaps; +#endif + +//------------------------------Phase------------------------------------------ +Phase::Phase( PhaseNumber pnum ) : _pnum(pnum), C( pnum == Compiler ? NULL : Compile::current()) { + // Poll for requests from shutdown mechanism to quiesce comiler (4448539, 4448544). + // This is an effective place to poll, since the compiler is full of phases. + // In particular, every inlining site uses a recursively created Parse phase. + CompileBroker::maybe_block(); +} + +#ifndef PRODUCT +static const double minimum_reported_time = 0.0001; // seconds +static const double expected_method_compile_coverage = 0.97; // % +static const double minimum_meaningful_method_compile = 2.00; // seconds + +void Phase::print_timers() { + tty->print_cr ("Accumulated compiler times:"); + tty->print_cr ("---------------------------"); + tty->print_cr (" Total compilation: %3.3f sec.", Phase::_t_totalCompilation.seconds()); + tty->print (" method compilation : %3.3f sec", Phase::_t_methodCompilation.seconds()); + tty->print ("/%d bytes",_total_bytes_compiled); + tty->print_cr (" (%3.0f bytes per sec) ", Phase::_total_bytes_compiled / Phase::_t_methodCompilation.seconds()); + tty->print_cr (" stub compilation : %3.3f sec.", Phase::_t_stubCompilation.seconds()); + tty->print_cr (" Phases:"); + tty->print_cr (" parse : %3.3f sec", Phase::_t_parser.seconds()); + if (DoEscapeAnalysis) { + tty->print_cr (" escape analysis : %3.3f sec", Phase::_t_escapeAnalysis.seconds()); + } + tty->print_cr (" optimizer : %3.3f sec", Phase::_t_optimizer.seconds()); + if( Verbose || WizardMode ) { + tty->print_cr (" iterGVN : %3.3f sec", Phase::_t_iterGVN.seconds()); + tty->print_cr (" idealLoop : %3.3f sec", Phase::_t_idealLoop.seconds()); + tty->print_cr (" ccp : %3.3f sec", Phase::_t_ccp.seconds()); + tty->print_cr (" iterGVN2 : %3.3f sec", Phase::_t_iterGVN2.seconds()); + tty->print_cr (" graphReshape : %3.3f sec", Phase::_t_graphReshaping.seconds()); + double optimizer_subtotal = Phase::_t_iterGVN.seconds() + + Phase::_t_idealLoop.seconds() + Phase::_t_ccp.seconds() + + Phase::_t_graphReshaping.seconds(); + double percent_of_optimizer = ((optimizer_subtotal == 0.0) ? 0.0 : (optimizer_subtotal / Phase::_t_optimizer.seconds() * 100.0)); + tty->print_cr (" subtotal : %3.3f sec, %3.2f %%", optimizer_subtotal, percent_of_optimizer); + } + tty->print_cr (" matcher : %3.3f sec", Phase::_t_matcher.seconds()); + tty->print_cr (" scheduler : %3.3f sec", Phase::_t_scheduler.seconds()); + tty->print_cr (" regalloc : %3.3f sec", Phase::_t_registerAllocation.seconds()); + if( Verbose || WizardMode ) { + tty->print_cr (" ctorChaitin : %3.3f sec", Phase::_t_ctorChaitin.seconds()); + tty->print_cr (" buildIFG : %3.3f sec", Phase::_t_buildIFGphysical.seconds()); + tty->print_cr (" computeLive : %3.3f sec", Phase::_t_computeLive.seconds()); + tty->print_cr (" regAllocSplit: %3.3f sec", Phase::_t_regAllocSplit.seconds()); + tty->print_cr (" postAllocCopyRemoval: %3.3f sec", Phase::_t_postAllocCopyRemoval.seconds()); + tty->print_cr (" fixupSpills : %3.3f sec", Phase::_t_fixupSpills.seconds()); + double regalloc_subtotal = Phase::_t_ctorChaitin.seconds() + + Phase::_t_buildIFGphysical.seconds() + Phase::_t_computeLive.seconds() + + Phase::_t_regAllocSplit.seconds() + Phase::_t_fixupSpills.seconds() + + Phase::_t_postAllocCopyRemoval.seconds(); + double percent_of_regalloc = ((regalloc_subtotal == 0.0) ? 0.0 : (regalloc_subtotal / Phase::_t_registerAllocation.seconds() * 100.0)); + tty->print_cr (" subtotal : %3.3f sec, %3.2f %%", regalloc_subtotal, percent_of_regalloc); + } + tty->print_cr (" macroExpand : %3.3f sec", Phase::_t_macroExpand.seconds()); + tty->print_cr (" removeEmpty : %3.3f sec", Phase::_t_removeEmptyBlocks.seconds()); + tty->print_cr (" peephole : %3.3f sec", Phase::_t_peephole.seconds()); + tty->print_cr (" codeGen : %3.3f sec", Phase::_t_codeGeneration.seconds()); + tty->print_cr (" install_code : %3.3f sec", Phase::_t_registerMethod.seconds()); + tty->print_cr (" ------------ : ----------"); + double phase_subtotal = Phase::_t_parser.seconds() + + (DoEscapeAnalysis ? Phase::_t_escapeAnalysis.seconds() : 0.0) + + Phase::_t_optimizer.seconds() + Phase::_t_graphReshaping.seconds() + + Phase::_t_matcher.seconds() + Phase::_t_scheduler.seconds() + + Phase::_t_registerAllocation.seconds() + Phase::_t_removeEmptyBlocks.seconds() + + Phase::_t_macroExpand.seconds() + Phase::_t_peephole.seconds() + + Phase::_t_codeGeneration.seconds() + Phase::_t_registerMethod.seconds(); + double percent_of_method_compile = ((phase_subtotal == 0.0) ? 0.0 : phase_subtotal / Phase::_t_methodCompilation.seconds()) * 100.0; + // counters inside Compile::CodeGen include time for adapters and stubs + // so phase-total can be greater than 100% + tty->print_cr (" total : %3.3f sec, %3.2f %%", phase_subtotal, percent_of_method_compile); + + assert( percent_of_method_compile > expected_method_compile_coverage || + phase_subtotal < minimum_meaningful_method_compile, + "Must account for method compilation"); + + if( Phase::_t_temporaryTimer1.seconds() > minimum_reported_time ) { + tty->cr(); + tty->print_cr (" temporaryTimer1: %3.3f sec", Phase::_t_temporaryTimer1.seconds()); + } + if( Phase::_t_temporaryTimer2.seconds() > minimum_reported_time ) { + tty->cr(); + tty->print_cr (" temporaryTimer2: %3.3f sec", Phase::_t_temporaryTimer2.seconds()); + } + tty->print_cr (" output : %3.3f sec", Phase::_t_output.seconds()); + tty->print_cr (" isched : %3.3f sec", Phase::_t_instrSched.seconds()); + tty->print_cr (" bldOopMaps: %3.3f sec", Phase::_t_buildOopMaps.seconds()); +} +#endif diff --git a/hotspot/src/share/vm/opto/phase.hpp b/hotspot/src/share/vm/opto/phase.hpp new file mode 100644 index 00000000000..bee7dfef70d --- /dev/null +++ b/hotspot/src/share/vm/opto/phase.hpp @@ -0,0 +1,113 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Compile; + +//------------------------------Phase------------------------------------------ +// Most optimizations are done in Phases. Creating a phase does any long +// running analysis required, and caches the analysis in internal data +// structures. Later the analysis is queried using transform() calls to +// guide transforming the program. When the Phase is deleted, so is any +// cached analysis info. This basic Phase class mostly contains timing and +// memory management code. +class Phase : public StackObj { +public: + enum PhaseNumber { + Compiler, // Top-level compiler phase + Parser, // Parse bytecodes + Remove_Useless, // Remove useless nodes + Optimistic, // Optimistic analysis phase + GVN, // Pessimistic global value numbering phase + Ins_Select, // Instruction selection phase + Copy_Elimination, // Copy Elimination + Dead_Code_Elimination, // DCE and compress Nodes + Conditional_Constant, // Conditional Constant Propagation + CFG, // Build a CFG + DefUse, // Build Def->Use chains + Register_Allocation, // Register allocation, duh + LIVE, // Dragon-book LIVE range problem + Interference_Graph, // Building the IFG + Coalesce, // Coalescing copies + Conditional_CProp, // Conditional Constant Propagation + Ideal_Loop, // Find idealized trip-counted loops + Macro_Expand, // Expand macro nodes + Peephole, // Apply peephole optimizations + last_phase + }; +protected: + enum PhaseNumber _pnum; // Phase number (for stat gathering) + +#ifndef PRODUCT + static int _total_bytes_compiled; + + // accumulated timers + static elapsedTimer _t_totalCompilation; + static elapsedTimer _t_methodCompilation; + static elapsedTimer _t_stubCompilation; +#endif + +// The next timers used for LogCompilation + static elapsedTimer _t_parser; + static elapsedTimer _t_escapeAnalysis; + static elapsedTimer _t_optimizer; + static elapsedTimer _t_idealLoop; + static elapsedTimer _t_ccp; + static elapsedTimer _t_matcher; + static elapsedTimer _t_registerAllocation; + static elapsedTimer _t_output; + +#ifndef PRODUCT + static elapsedTimer _t_graphReshaping; + static elapsedTimer _t_scheduler; + static elapsedTimer _t_removeEmptyBlocks; + static elapsedTimer _t_macroExpand; + static elapsedTimer _t_peephole; + static elapsedTimer _t_codeGeneration; + static elapsedTimer _t_registerMethod; + static elapsedTimer _t_temporaryTimer1; + static elapsedTimer _t_temporaryTimer2; + +// Subtimers for _t_optimizer + static elapsedTimer _t_iterGVN; + static elapsedTimer _t_iterGVN2; + +// Subtimers for _t_registerAllocation + static elapsedTimer _t_ctorChaitin; + static elapsedTimer _t_buildIFGphysical; + static elapsedTimer _t_computeLive; + static elapsedTimer _t_regAllocSplit; + static elapsedTimer _t_postAllocCopyRemoval; + static elapsedTimer _t_fixupSpills; + +// Subtimers for _t_output + static elapsedTimer _t_instrSched; + static elapsedTimer _t_buildOopMaps; +#endif +public: + Compile * C; + Phase( PhaseNumber pnum ); +#ifndef PRODUCT + static void print_timers(); +#endif +}; diff --git a/hotspot/src/share/vm/opto/phaseX.cpp b/hotspot/src/share/vm/opto/phaseX.cpp new file mode 100644 index 00000000000..3c5cd2cca13 --- /dev/null +++ b/hotspot/src/share/vm/opto/phaseX.cpp @@ -0,0 +1,1758 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_phaseX.cpp.incl" + +//============================================================================= +#define NODE_HASH_MINIMUM_SIZE 255 +//------------------------------NodeHash--------------------------------------- +NodeHash::NodeHash(uint est_max_size) : + _max( round_up(est_max_size < NODE_HASH_MINIMUM_SIZE ? NODE_HASH_MINIMUM_SIZE : est_max_size) ), + _a(Thread::current()->resource_area()), + _table( NEW_ARENA_ARRAY( _a , Node* , _max ) ), // (Node**)_a->Amalloc(_max * sizeof(Node*)) ), + _inserts(0), _insert_limit( insert_limit() ), + _look_probes(0), _lookup_hits(0), _lookup_misses(0), + _total_insert_probes(0), _total_inserts(0), + _insert_probes(0), _grows(0) { + // _sentinel must be in the current node space + _sentinel = new (Compile::current(), 1) ProjNode(NULL, TypeFunc::Control); + memset(_table,0,sizeof(Node*)*_max); +} + +//------------------------------NodeHash--------------------------------------- +NodeHash::NodeHash(Arena *arena, uint est_max_size) : + _max( round_up(est_max_size < NODE_HASH_MINIMUM_SIZE ? NODE_HASH_MINIMUM_SIZE : est_max_size) ), + _a(arena), + _table( NEW_ARENA_ARRAY( _a , Node* , _max ) ), + _inserts(0), _insert_limit( insert_limit() ), + _look_probes(0), _lookup_hits(0), _lookup_misses(0), + _delete_probes(0), _delete_hits(0), _delete_misses(0), + _total_insert_probes(0), _total_inserts(0), + _insert_probes(0), _grows(0) { + // _sentinel must be in the current node space + _sentinel = new (Compile::current(), 1) ProjNode(NULL, TypeFunc::Control); + memset(_table,0,sizeof(Node*)*_max); +} + +//------------------------------NodeHash--------------------------------------- +NodeHash::NodeHash(NodeHash *nh) { + debug_only(_table = (Node**)badAddress); // interact correctly w/ operator= + // just copy in all the fields + *this = *nh; + // nh->_sentinel must be in the current node space +} + +//------------------------------hash_find-------------------------------------- +// Find in hash table +Node *NodeHash::hash_find( const Node *n ) { + // ((Node*)n)->set_hash( n->hash() ); + uint hash = n->hash(); + if (hash == Node::NO_HASH) { + debug_only( _lookup_misses++ ); + return NULL; + } + uint key = hash & (_max-1); + uint stride = key | 0x01; + debug_only( _look_probes++ ); + Node *k = _table[key]; // Get hashed value + if( !k ) { // ?Miss? + debug_only( _lookup_misses++ ); + return NULL; // Miss! + } + + int op = n->Opcode(); + uint req = n->req(); + while( 1 ) { // While probing hash table + if( k->req() == req && // Same count of inputs + k->Opcode() == op ) { // Same Opcode + for( uint i=0; iin(i)!=k->in(i)) // Different inputs? + goto collision; // "goto" is a speed hack... + if( n->cmp(*k) ) { // Check for any special bits + debug_only( _lookup_hits++ ); + return k; // Hit! + } + } + collision: + debug_only( _look_probes++ ); + key = (key + stride/*7*/) & (_max-1); // Stride through table with relative prime + k = _table[key]; // Get hashed value + if( !k ) { // ?Miss? + debug_only( _lookup_misses++ ); + return NULL; // Miss! + } + } + ShouldNotReachHere(); + return NULL; +} + +//------------------------------hash_find_insert------------------------------- +// Find in hash table, insert if not already present +// Used to preserve unique entries in hash table +Node *NodeHash::hash_find_insert( Node *n ) { + // n->set_hash( ); + uint hash = n->hash(); + if (hash == Node::NO_HASH) { + debug_only( _lookup_misses++ ); + return NULL; + } + uint key = hash & (_max-1); + uint stride = key | 0x01; // stride must be relatively prime to table siz + uint first_sentinel = 0; // replace a sentinel if seen. + debug_only( _look_probes++ ); + Node *k = _table[key]; // Get hashed value + if( !k ) { // ?Miss? + debug_only( _lookup_misses++ ); + _table[key] = n; // Insert into table! + debug_only(n->enter_hash_lock()); // Lock down the node while in the table. + check_grow(); // Grow table if insert hit limit + return NULL; // Miss! + } + else if( k == _sentinel ) { + first_sentinel = key; // Can insert here + } + + int op = n->Opcode(); + uint req = n->req(); + while( 1 ) { // While probing hash table + if( k->req() == req && // Same count of inputs + k->Opcode() == op ) { // Same Opcode + for( uint i=0; iin(i)!=k->in(i)) // Different inputs? + goto collision; // "goto" is a speed hack... + if( n->cmp(*k) ) { // Check for any special bits + debug_only( _lookup_hits++ ); + return k; // Hit! + } + } + collision: + debug_only( _look_probes++ ); + key = (key + stride) & (_max-1); // Stride through table w/ relative prime + k = _table[key]; // Get hashed value + if( !k ) { // ?Miss? + debug_only( _lookup_misses++ ); + key = (first_sentinel == 0) ? key : first_sentinel; // ?saw sentinel? + _table[key] = n; // Insert into table! + debug_only(n->enter_hash_lock()); // Lock down the node while in the table. + check_grow(); // Grow table if insert hit limit + return NULL; // Miss! + } + else if( first_sentinel == 0 && k == _sentinel ) { + first_sentinel = key; // Can insert here + } + + } + ShouldNotReachHere(); + return NULL; +} + +//------------------------------hash_insert------------------------------------ +// Insert into hash table +void NodeHash::hash_insert( Node *n ) { + // // "conflict" comments -- print nodes that conflict + // bool conflict = false; + // n->set_hash(); + uint hash = n->hash(); + if (hash == Node::NO_HASH) { + return; + } + check_grow(); + uint key = hash & (_max-1); + uint stride = key | 0x01; + + while( 1 ) { // While probing hash table + debug_only( _insert_probes++ ); + Node *k = _table[key]; // Get hashed value + if( !k || (k == _sentinel) ) break; // Found a slot + assert( k != n, "already inserted" ); + // if( PrintCompilation && PrintOptoStatistics && Verbose ) { tty->print(" conflict: "); k->dump(); conflict = true; } + key = (key + stride) & (_max-1); // Stride through table w/ relative prime + } + _table[key] = n; // Insert into table! + debug_only(n->enter_hash_lock()); // Lock down the node while in the table. + // if( conflict ) { n->dump(); } +} + +//------------------------------hash_delete------------------------------------ +// Replace in hash table with sentinal +bool NodeHash::hash_delete( const Node *n ) { + Node *k; + uint hash = n->hash(); + if (hash == Node::NO_HASH) { + debug_only( _delete_misses++ ); + return false; + } + uint key = hash & (_max-1); + uint stride = key | 0x01; + debug_only( uint counter = 0; ); + for( ; /* (k != NULL) && (k != _sentinal) */; ) { + debug_only( counter++ ); + debug_only( _delete_probes++ ); + k = _table[key]; // Get hashed value + if( !k ) { // Miss? + debug_only( _delete_misses++ ); +#ifdef ASSERT + if( VerifyOpto ) { + for( uint i=0; i < _max; i++ ) + assert( _table[i] != n, "changed edges with rehashing" ); + } +#endif + return false; // Miss! Not in chain + } + else if( n == k ) { + debug_only( _delete_hits++ ); + _table[key] = _sentinel; // Hit! Label as deleted entry + debug_only(((Node*)n)->exit_hash_lock()); // Unlock the node upon removal from table. + return true; + } + else { + // collision: move through table with prime offset + key = (key + stride/*7*/) & (_max-1); + assert( counter <= _insert_limit, "Cycle in hash-table"); + } + } + ShouldNotReachHere(); + return false; +} + +//------------------------------round_up--------------------------------------- +// Round up to nearest power of 2 +uint NodeHash::round_up( uint x ) { + x += (x>>2); // Add 25% slop + if( x <16 ) return 16; // Small stuff + uint i=16; + while( i < x ) i <<= 1; // Double to fit + return i; // Return hash table size +} + +//------------------------------grow------------------------------------------- +// Grow _table to next power of 2 and insert old entries +void NodeHash::grow() { + // Record old state + uint old_max = _max; + Node **old_table = _table; + // Construct new table with twice the space + _grows++; + _total_inserts += _inserts; + _total_insert_probes += _insert_probes; + _inserts = 0; + _insert_probes = 0; + _max = _max << 1; + _table = NEW_ARENA_ARRAY( _a , Node* , _max ); // (Node**)_a->Amalloc( _max * sizeof(Node*) ); + memset(_table,0,sizeof(Node*)*_max); + _insert_limit = insert_limit(); + // Insert old entries into the new table + for( uint i = 0; i < old_max; i++ ) { + Node *m = *old_table++; + if( !m || m == _sentinel ) continue; + debug_only(m->exit_hash_lock()); // Unlock the node upon removal from old table. + hash_insert(m); + } +} + +//------------------------------clear------------------------------------------ +// Clear all entries in _table to NULL but keep storage +void NodeHash::clear() { +#ifdef ASSERT + // Unlock all nodes upon removal from table. + for (uint i = 0; i < _max; i++) { + Node* n = _table[i]; + if (!n || n == _sentinel) continue; + n->exit_hash_lock(); + } +#endif + + memset( _table, 0, _max * sizeof(Node*) ); +} + +//-----------------------remove_useless_nodes---------------------------------- +// Remove useless nodes from value table, +// implementation does not depend on hash function +void NodeHash::remove_useless_nodes(VectorSet &useful) { + + // Dead nodes in the hash table inherited from GVN should not replace + // existing nodes, remove dead nodes. + uint max = size(); + Node *sentinel_node = sentinel(); + for( uint i = 0; i < max; ++i ) { + Node *n = at(i); + if(n != NULL && n != sentinel_node && !useful.test(n->_idx)) { + debug_only(n->exit_hash_lock()); // Unlock the node when removed + _table[i] = sentinel_node; // Replace with placeholder + } + } +} + +#ifndef PRODUCT +//------------------------------dump------------------------------------------- +// Dump statistics for the hash table +void NodeHash::dump() { + _total_inserts += _inserts; + _total_insert_probes += _insert_probes; + if( PrintCompilation && PrintOptoStatistics && Verbose && (_inserts > 0) ) { // PrintOptoGVN + if( PrintCompilation2 ) { + for( uint i=0; i<_max; i++ ) + if( _table[i] ) + tty->print("%d/%d/%d ",i,_table[i]->hash()&(_max-1),_table[i]->_idx); + } + tty->print("\nGVN Hash stats: %d grows to %d max_size\n", _grows, _max); + tty->print(" %d/%d (%8.1f%% full)\n", _inserts, _max, (double)_inserts/_max*100.0); + tty->print(" %dp/(%dh+%dm) (%8.2f probes/lookup)\n", _look_probes, _lookup_hits, _lookup_misses, (double)_look_probes/(_lookup_hits+_lookup_misses)); + tty->print(" %dp/%di (%8.2f probes/insert)\n", _total_insert_probes, _total_inserts, (double)_total_insert_probes/_total_inserts); + // sentinels increase lookup cost, but not insert cost + assert((_lookup_misses+_lookup_hits)*4+100 >= _look_probes, "bad hash function"); + assert( _inserts+(_inserts>>3) < _max, "table too full" ); + assert( _inserts*3+100 >= _insert_probes, "bad hash function" ); + } +} + +Node *NodeHash::find_index(uint idx) { // For debugging + // Find an entry by its index value + for( uint i = 0; i < _max; i++ ) { + Node *m = _table[i]; + if( !m || m == _sentinel ) continue; + if( m->_idx == (uint)idx ) return m; + } + return NULL; +} +#endif + +#ifdef ASSERT +NodeHash::~NodeHash() { + // Unlock all nodes upon destruction of table. + if (_table != (Node**)badAddress) clear(); +} + +void NodeHash::operator=(const NodeHash& nh) { + // Unlock all nodes upon replacement of table. + if (&nh == this) return; + if (_table != (Node**)badAddress) clear(); + memcpy(this, &nh, sizeof(*this)); + // Do not increment hash_lock counts again. + // Instead, be sure we never again use the source table. + ((NodeHash*)&nh)->_table = (Node**)badAddress; +} + + +#endif + + +//============================================================================= +//------------------------------PhaseRemoveUseless----------------------------- +// 1) Use a breadthfirst walk to collect useful nodes reachable from root. +PhaseRemoveUseless::PhaseRemoveUseless( PhaseGVN *gvn, Unique_Node_List *worklist ) : Phase(Remove_Useless), + _useful(Thread::current()->resource_area()) { + + // Implementation requires 'UseLoopSafepoints == true' and an edge from root + // to each SafePointNode at a backward branch. Inserted in add_safepoint(). + if( !UseLoopSafepoints || !OptoRemoveUseless ) return; + + // Identify nodes that are reachable from below, useful. + C->identify_useful_nodes(_useful); + + // Remove all useless nodes from PhaseValues' recorded types + // Must be done before disconnecting nodes to preserve hash-table-invariant + gvn->remove_useless_nodes(_useful.member_set()); + + // Remove all useless nodes from future worklist + worklist->remove_useless_nodes(_useful.member_set()); + + // Disconnect 'useless' nodes that are adjacent to useful nodes + C->remove_useless_nodes(_useful); + + // Remove edges from "root" to each SafePoint at a backward branch. + // They were inserted during parsing (see add_safepoint()) to make infinite + // loops without calls or exceptions visible to root, i.e., useful. + Node *root = C->root(); + if( root != NULL ) { + for( uint i = root->req(); i < root->len(); ++i ) { + Node *n = root->in(i); + if( n != NULL && n->is_SafePoint() ) { + root->rm_prec(i); + --i; + } + } + } +} + + +//============================================================================= +//------------------------------PhaseTransform--------------------------------- +PhaseTransform::PhaseTransform( PhaseNumber pnum ) : Phase(pnum), + _arena(Thread::current()->resource_area()), + _nodes(_arena), + _types(_arena) +{ + init_con_caches(); +#ifndef PRODUCT + clear_progress(); + clear_transforms(); + set_allow_progress(true); +#endif + // Force allocation for currently existing nodes + _types.map(C->unique(), NULL); +} + +//------------------------------PhaseTransform--------------------------------- +PhaseTransform::PhaseTransform( Arena *arena, PhaseNumber pnum ) : Phase(pnum), + _arena(arena), + _nodes(arena), + _types(arena) +{ + init_con_caches(); +#ifndef PRODUCT + clear_progress(); + clear_transforms(); + set_allow_progress(true); +#endif + // Force allocation for currently existing nodes + _types.map(C->unique(), NULL); +} + +//------------------------------PhaseTransform--------------------------------- +// Initialize with previously generated type information +PhaseTransform::PhaseTransform( PhaseTransform *pt, PhaseNumber pnum ) : Phase(pnum), + _arena(pt->_arena), + _nodes(pt->_nodes), + _types(pt->_types) +{ + init_con_caches(); +#ifndef PRODUCT + clear_progress(); + clear_transforms(); + set_allow_progress(true); +#endif +} + +void PhaseTransform::init_con_caches() { + memset(_icons,0,sizeof(_icons)); + memset(_lcons,0,sizeof(_lcons)); + memset(_zcons,0,sizeof(_zcons)); +} + + +//--------------------------------find_int_type-------------------------------- +const TypeInt* PhaseTransform::find_int_type(Node* n) { + if (n == NULL) return NULL; + // Call type_or_null(n) to determine node's type since we might be in + // parse phase and call n->Value() may return wrong type. + // (For example, a phi node at the beginning of loop parsing is not ready.) + const Type* t = type_or_null(n); + if (t == NULL) return NULL; + return t->isa_int(); +} + + +//-------------------------------find_long_type-------------------------------- +const TypeLong* PhaseTransform::find_long_type(Node* n) { + if (n == NULL) return NULL; + // (See comment above on type_or_null.) + const Type* t = type_or_null(n); + if (t == NULL) return NULL; + return t->isa_long(); +} + + +#ifndef PRODUCT +void PhaseTransform::dump_old2new_map() const { + _nodes.dump(); +} + +void PhaseTransform::dump_new( uint nidx ) const { + for( uint i=0; i<_nodes.Size(); i++ ) + if( _nodes[i] && _nodes[i]->_idx == nidx ) { + _nodes[i]->dump(); + tty->cr(); + tty->print_cr("Old index= %d",i); + return; + } + tty->print_cr("Node %d not found in the new indices", nidx); +} + +//------------------------------dump_types------------------------------------- +void PhaseTransform::dump_types( ) const { + _types.dump(); +} + +//------------------------------dump_nodes_and_types--------------------------- +void PhaseTransform::dump_nodes_and_types(const Node *root, uint depth, bool only_ctrl) { + VectorSet visited(Thread::current()->resource_area()); + dump_nodes_and_types_recur( root, depth, only_ctrl, visited ); +} + +//------------------------------dump_nodes_and_types_recur--------------------- +void PhaseTransform::dump_nodes_and_types_recur( const Node *n, uint depth, bool only_ctrl, VectorSet &visited) { + if( !n ) return; + if( depth == 0 ) return; + if( visited.test_set(n->_idx) ) return; + for( uint i=0; ilen(); i++ ) { + if( only_ctrl && !(n->is_Region()) && i != TypeFunc::Control ) continue; + dump_nodes_and_types_recur( n->in(i), depth-1, only_ctrl, visited ); + } + n->dump(); + if (type_or_null(n) != NULL) { + tty->print(" "); type(n)->dump(); tty->cr(); + } +} + +#endif + + +//============================================================================= +//------------------------------PhaseValues------------------------------------ +// Set minimum table size to "255" +PhaseValues::PhaseValues( Arena *arena, uint est_max_size ) : PhaseTransform(arena, GVN), _table(arena, est_max_size) { + NOT_PRODUCT( clear_new_values(); ) +} + +//------------------------------PhaseValues------------------------------------ +// Set minimum table size to "255" +PhaseValues::PhaseValues( PhaseValues *ptv ) : PhaseTransform( ptv, GVN ), + _table(&ptv->_table) { + NOT_PRODUCT( clear_new_values(); ) +} + +//------------------------------PhaseValues------------------------------------ +// Used by +VerifyOpto. Clear out hash table but copy _types array. +PhaseValues::PhaseValues( PhaseValues *ptv, const char *dummy ) : PhaseTransform( ptv, GVN ), + _table(ptv->arena(),ptv->_table.size()) { + NOT_PRODUCT( clear_new_values(); ) +} + +//------------------------------~PhaseValues----------------------------------- +#ifndef PRODUCT +PhaseValues::~PhaseValues() { + _table.dump(); + + // Statistics for value progress and efficiency + if( PrintCompilation && Verbose && WizardMode ) { + tty->print("\n%sValues: %d nodes ---> %d/%d (%d)", + is_IterGVN() ? "Iter" : " ", C->unique(), made_progress(), made_transforms(), made_new_values()); + if( made_transforms() != 0 ) { + tty->print_cr(" ratio %f", made_progress()/(float)made_transforms() ); + } else { + tty->cr(); + } + } +} +#endif + +//------------------------------makecon---------------------------------------- +ConNode* PhaseTransform::makecon(const Type *t) { + assert(t->singleton(), "must be a constant"); + assert(!t->empty() || t == Type::TOP, "must not be vacuous range"); + switch (t->base()) { // fast paths + case Type::Half: + case Type::Top: return (ConNode*) C->top(); + case Type::Int: return intcon( t->is_int()->get_con() ); + case Type::Long: return longcon( t->is_long()->get_con() ); + } + if (t->is_zero_type()) + return zerocon(t->basic_type()); + return uncached_makecon(t); +} + +//--------------------------uncached_makecon----------------------------------- +// Make an idealized constant - one of ConINode, ConPNode, etc. +ConNode* PhaseValues::uncached_makecon(const Type *t) { + assert(t->singleton(), "must be a constant"); + ConNode* x = ConNode::make(C, t); + ConNode* k = (ConNode*)hash_find_insert(x); // Value numbering + if (k == NULL) { + set_type(x, t); // Missed, provide type mapping + GrowableArray* nna = C->node_note_array(); + if (nna != NULL) { + Node_Notes* loc = C->locate_node_notes(nna, x->_idx, true); + loc->clear(); // do not put debug info on constants + } + // Collect points-to information for escape analysys + ConnectionGraph *cgr = C->congraph(); + if (cgr != NULL) { + cgr->record_escape(x, this); + } + } else { + x->destruct(); // Hit, destroy duplicate constant + x = k; // use existing constant + } + return x; +} + +//------------------------------intcon----------------------------------------- +// Fast integer constant. Same as "transform(new ConINode(TypeInt::make(i)))" +ConINode* PhaseTransform::intcon(int i) { + // Small integer? Check cache! Check that cached node is not dead + if (i >= _icon_min && i <= _icon_max) { + ConINode* icon = _icons[i-_icon_min]; + if (icon != NULL && icon->in(TypeFunc::Control) != NULL) + return icon; + } + ConINode* icon = (ConINode*) uncached_makecon(TypeInt::make(i)); + assert(icon->is_Con(), ""); + if (i >= _icon_min && i <= _icon_max) + _icons[i-_icon_min] = icon; // Cache small integers + return icon; +} + +//------------------------------longcon---------------------------------------- +// Fast long constant. +ConLNode* PhaseTransform::longcon(jlong l) { + // Small integer? Check cache! Check that cached node is not dead + if (l >= _lcon_min && l <= _lcon_max) { + ConLNode* lcon = _lcons[l-_lcon_min]; + if (lcon != NULL && lcon->in(TypeFunc::Control) != NULL) + return lcon; + } + ConLNode* lcon = (ConLNode*) uncached_makecon(TypeLong::make(l)); + assert(lcon->is_Con(), ""); + if (l >= _lcon_min && l <= _lcon_max) + _lcons[l-_lcon_min] = lcon; // Cache small integers + return lcon; +} + +//------------------------------zerocon----------------------------------------- +// Fast zero or null constant. Same as "transform(ConNode::make(Type::get_zero_type(bt)))" +ConNode* PhaseTransform::zerocon(BasicType bt) { + assert((uint)bt <= _zcon_max, "domain check"); + ConNode* zcon = _zcons[bt]; + if (zcon != NULL && zcon->in(TypeFunc::Control) != NULL) + return zcon; + zcon = (ConNode*) uncached_makecon(Type::get_zero_type(bt)); + _zcons[bt] = zcon; + return zcon; +} + + + +//============================================================================= +//------------------------------transform-------------------------------------- +// Return a node which computes the same function as this node, but in a +// faster or cheaper fashion. The Node passed in here must have no other +// pointers to it, as its storage will be reclaimed if the Node can be +// optimized away. +Node *PhaseGVN::transform( Node *n ) { + NOT_PRODUCT( set_transforms(); ) + + // Apply the Ideal call in a loop until it no longer applies + Node *k = n; + NOT_PRODUCT( uint loop_count = 0; ) + while( 1 ) { + Node *i = k->Ideal(this, /*can_reshape=*/false); + if( !i ) break; + assert( i->_idx >= k->_idx, "Idealize should return new nodes, use Identity to return old nodes" ); + // Can never reclaim storage for Ideal calls, because the Ideal call + // returns a new Node, bumping the High Water Mark and our old Node + // is caught behind the new one. + //if( k != i ) { + //k->destruct(); // Reclaim storage for recent node + k = i; + //} + assert(loop_count++ < K, "infinite loop in PhaseGVN::transform"); + } + NOT_PRODUCT( if( loop_count != 0 ) { set_progress(); } ) + + // If brand new node, make space in type array. + ensure_type_or_null(k); + + // Cache result of Value call since it can be expensive + // (abstract interpretation of node 'k' using phase->_types[ inputs ]) + const Type *t = k->Value(this); // Get runtime Value set + assert(t != NULL, "value sanity"); + if (type_or_null(k) != t) { +#ifndef PRODUCT + // Do not record transformation or value construction on first visit + if (type_or_null(k) == NULL) { + inc_new_values(); + set_progress(); + } +#endif + set_type(k, t); + // If k is a TypeNode, capture any more-precise type permanently into Node + k->raise_bottom_type(t); + } + + if( t->singleton() && !k->is_Con() ) { + //k->destruct(); // Reclaim storage for recent node + NOT_PRODUCT( set_progress(); ) + return makecon(t); // Turn into a constant + } + + // Now check for Identities + Node *i = k->Identity(this); // Look for a nearby replacement + if( i != k ) { // Found? Return replacement! + //k->destruct(); // Reclaim storage for recent node + NOT_PRODUCT( set_progress(); ) + return i; + } + + // Try Global Value Numbering + i = hash_find_insert(k); // Found older value when i != NULL + if( i && i != k ) { // Hit? Return the old guy + NOT_PRODUCT( set_progress(); ) + return i; + } + + // Collect points-to information for escape analysys + ConnectionGraph *cgr = C->congraph(); + if (cgr != NULL) { + cgr->record_escape(k, this); + } + + // Return Idealized original + return k; +} + +//------------------------------transform-------------------------------------- +// Return a node which computes the same function as this node, but +// in a faster or cheaper fashion. +Node *PhaseGVN::transform_no_reclaim( Node *n ) { + NOT_PRODUCT( set_transforms(); ) + + // Apply the Ideal call in a loop until it no longer applies + Node *k = n; + NOT_PRODUCT( uint loop_count = 0; ) + while( 1 ) { + Node *i = k->Ideal(this, /*can_reshape=*/false); + if( !i ) break; + assert( i->_idx >= k->_idx, "Idealize should return new nodes, use Identity to return old nodes" ); + k = i; + assert(loop_count++ < K, "infinite loop in PhaseGVN::transform"); + } + NOT_PRODUCT( if( loop_count != 0 ) { set_progress(); } ) + + + // If brand new node, make space in type array. + ensure_type_or_null(k); + + // Since I just called 'Value' to compute the set of run-time values + // for this Node, and 'Value' is non-local (and therefore expensive) I'll + // cache Value. Later requests for the local phase->type of this Node can + // use the cached Value instead of suffering with 'bottom_type'. + const Type *t = k->Value(this); // Get runtime Value set + assert(t != NULL, "value sanity"); + if (type_or_null(k) != t) { +#ifndef PRODUCT + // Do not count initial visit to node as a transformation + if (type_or_null(k) == NULL) { + inc_new_values(); + set_progress(); + } +#endif + set_type(k, t); + // If k is a TypeNode, capture any more-precise type permanently into Node + k->raise_bottom_type(t); + } + + if( t->singleton() && !k->is_Con() ) { + NOT_PRODUCT( set_progress(); ) + return makecon(t); // Turn into a constant + } + + // Now check for Identities + Node *i = k->Identity(this); // Look for a nearby replacement + if( i != k ) { // Found? Return replacement! + NOT_PRODUCT( set_progress(); ) + return i; + } + + // Global Value Numbering + i = hash_find_insert(k); // Insert if new + if( i && (i != k) ) { + // Return the pre-existing node + NOT_PRODUCT( set_progress(); ) + return i; + } + + // Return Idealized original + return k; +} + +#ifdef ASSERT +//------------------------------dead_loop_check-------------------------------- +// Check for a simple dead loop when a data node references itself direcly +// or through an other data node excluding cons and phis. +void PhaseGVN::dead_loop_check( Node *n ) { + // Phi may reference itself in a loop + if (n != NULL && !n->is_dead_loop_safe() && !n->is_CFG()) { + // Do 2 levels check and only data inputs. + bool no_dead_loop = true; + uint cnt = n->req(); + for (uint i = 1; i < cnt && no_dead_loop; i++) { + Node *in = n->in(i); + if (in == n) { + no_dead_loop = false; + } else if (in != NULL && !in->is_dead_loop_safe()) { + uint icnt = in->req(); + for (uint j = 1; j < icnt && no_dead_loop; j++) { + if (in->in(j) == n || in->in(j) == in) + no_dead_loop = false; + } + } + } + if (!no_dead_loop) n->dump(3); + assert(no_dead_loop, "dead loop detected"); + } +} +#endif + +//============================================================================= +//------------------------------PhaseIterGVN----------------------------------- +// Initialize hash table to fresh and clean for +VerifyOpto +PhaseIterGVN::PhaseIterGVN( PhaseIterGVN *igvn, const char *dummy ) : PhaseGVN(igvn,dummy), _worklist( ) { +} + +//------------------------------PhaseIterGVN----------------------------------- +// Initialize with previous PhaseIterGVN info; used by PhaseCCP +PhaseIterGVN::PhaseIterGVN( PhaseIterGVN *igvn ) : PhaseGVN(igvn), + _worklist( igvn->_worklist ) +{ +} + +//------------------------------PhaseIterGVN----------------------------------- +// Initialize with previous PhaseGVN info from Parser +PhaseIterGVN::PhaseIterGVN( PhaseGVN *gvn ) : PhaseGVN(gvn), + _worklist(*C->for_igvn()) +{ + uint max; + + // Dead nodes in the hash table inherited from GVN were not treated as + // roots during def-use info creation; hence they represent an invisible + // use. Clear them out. + max = _table.size(); + for( uint i = 0; i < max; ++i ) { + Node *n = _table.at(i); + if(n != NULL && n != _table.sentinel() && n->outcnt() == 0) { + if( n->is_top() ) continue; + assert( false, "Parse::remove_useless_nodes missed this node"); + hash_delete(n); + } + } + + // Any Phis or Regions on the worklist probably had uses that could not + // make more progress because the uses were made while the Phis and Regions + // were in half-built states. Put all uses of Phis and Regions on worklist. + max = _worklist.size(); + for( uint j = 0; j < max; j++ ) { + Node *n = _worklist.at(j); + uint uop = n->Opcode(); + if( uop == Op_Phi || uop == Op_Region || + n->is_Type() || + n->is_Mem() ) + add_users_to_worklist(n); + } +} + + +#ifndef PRODUCT +void PhaseIterGVN::verify_step(Node* n) { + _verify_window[_verify_counter % _verify_window_size] = n; + ++_verify_counter; + ResourceMark rm; + ResourceArea *area = Thread::current()->resource_area(); + VectorSet old_space(area), new_space(area); + if (C->unique() < 1000 || + 0 == _verify_counter % (C->unique() < 10000 ? 10 : 100)) { + ++_verify_full_passes; + Node::verify_recur(C->root(), -1, old_space, new_space); + } + const int verify_depth = 4; + for ( int i = 0; i < _verify_window_size; i++ ) { + Node* n = _verify_window[i]; + if ( n == NULL ) continue; + if( n->in(0) == NodeSentinel ) { // xform_idom + _verify_window[i] = n->in(1); + --i; continue; + } + // Typical fanout is 1-2, so this call visits about 6 nodes. + Node::verify_recur(n, verify_depth, old_space, new_space); + } +} +#endif + + +//------------------------------init_worklist---------------------------------- +// Initialize worklist for each node. +void PhaseIterGVN::init_worklist( Node *n ) { + if( _worklist.member(n) ) return; + _worklist.push(n); + uint cnt = n->req(); + for( uint i =0 ; i < cnt; i++ ) { + Node *m = n->in(i); + if( m ) init_worklist(m); + } +} + +//------------------------------optimize--------------------------------------- +void PhaseIterGVN::optimize() { + debug_only(uint num_processed = 0;); +#ifndef PRODUCT + { + _verify_counter = 0; + _verify_full_passes = 0; + for ( int i = 0; i < _verify_window_size; i++ ) { + _verify_window[i] = NULL; + } + } +#endif + + // Pull from worklist; transform node; + // If node has changed: update edge info and put uses on worklist. + while( _worklist.size() ) { + Node *n = _worklist.pop(); + if (TraceIterativeGVN && Verbose) { + tty->print(" Pop "); + NOT_PRODUCT( n->dump(); ) + debug_only(if( (num_processed++ % 100) == 0 ) _worklist.print_set();) + } + + if (n->outcnt() != 0) { + +#ifndef PRODUCT + uint wlsize = _worklist.size(); + const Type* oldtype = type_or_null(n); +#endif //PRODUCT + + Node *nn = transform_old(n); + +#ifndef PRODUCT + if (TraceIterativeGVN) { + const Type* newtype = type_or_null(n); + if (nn != n) { + // print old node + tty->print("< "); + if (oldtype != newtype && oldtype != NULL) { + oldtype->dump(); + } + do { tty->print("\t"); } while (tty->position() < 16); + tty->print("<"); + n->dump(); + } + if (oldtype != newtype || nn != n) { + // print new node and/or new type + if (oldtype == NULL) { + tty->print("* "); + } else if (nn != n) { + tty->print("> "); + } else { + tty->print("= "); + } + if (newtype == NULL) { + tty->print("null"); + } else { + newtype->dump(); + } + do { tty->print("\t"); } while (tty->position() < 16); + nn->dump(); + } + if (Verbose && wlsize < _worklist.size()) { + tty->print(" Push {"); + while (wlsize != _worklist.size()) { + Node* pushed = _worklist.at(wlsize++); + tty->print(" %d", pushed->_idx); + } + tty->print_cr(" }"); + } + } + if( VerifyIterativeGVN && nn != n ) { + verify_step((Node*) NULL); // ignore n, it might be subsumed + } +#endif + } else if (!n->is_top()) { + remove_dead_node(n); + } + } + +#ifndef PRODUCT + C->verify_graph_edges(); + if( VerifyOpto && allow_progress() ) { + // Must turn off allow_progress to enable assert and break recursion + C->root()->verify(); + { // Check if any progress was missed using IterGVN + // Def-Use info enables transformations not attempted in wash-pass + // e.g. Region/Phi cleanup, ... + // Null-check elision -- may not have reached fixpoint + // do not propagate to dominated nodes + ResourceMark rm; + PhaseIterGVN igvn2(this,"Verify"); // Fresh and clean! + // Fill worklist completely + igvn2.init_worklist(C->root()); + + igvn2.set_allow_progress(false); + igvn2.optimize(); + igvn2.set_allow_progress(true); + } + } + if ( VerifyIterativeGVN && PrintOpto ) { + if ( _verify_counter == _verify_full_passes ) + tty->print_cr("VerifyIterativeGVN: %d transforms and verify passes", + _verify_full_passes); + else + tty->print_cr("VerifyIterativeGVN: %d transforms, %d full verify passes", + _verify_counter, _verify_full_passes); + } +#endif +} + + +//------------------register_new_node_with_optimizer--------------------------- +// Register a new node with the optimizer. Update the types array, the def-use +// info. Put on worklist. +Node* PhaseIterGVN::register_new_node_with_optimizer(Node* n, Node* orig) { + set_type_bottom(n); + _worklist.push(n); + if (orig != NULL) C->copy_node_notes_to(n, orig); + return n; +} + +//------------------------------transform-------------------------------------- +// Non-recursive: idealize Node 'n' with respect to its inputs and its value +Node *PhaseIterGVN::transform( Node *n ) { + // If brand new node, make space in type array, and give it a type. + ensure_type_or_null(n); + if (type_or_null(n) == NULL) { + set_type_bottom(n); + } + + return transform_old(n); +} + +//------------------------------transform_old---------------------------------- +Node *PhaseIterGVN::transform_old( Node *n ) { +#ifndef PRODUCT + debug_only(uint loop_count = 0;); + set_transforms(); +#endif + // Remove 'n' from hash table in case it gets modified + _table.hash_delete(n); + if( VerifyIterativeGVN ) { + assert( !_table.find_index(n->_idx), "found duplicate entry in table"); + } + + // Apply the Ideal call in a loop until it no longer applies + Node *k = n; + DEBUG_ONLY(dead_loop_check(k);) + Node *i = k->Ideal(this, /*can_reshape=*/true); +#ifndef PRODUCT + if( VerifyIterativeGVN ) + verify_step(k); + if( i && VerifyOpto ) { + if( !allow_progress() ) { + if (i->is_Add() && i->outcnt() == 1) { + // Switched input to left side because this is the only use + } else if( i->is_If() && (i->in(0) == NULL) ) { + // This IF is dead because it is dominated by an equivalent IF When + // dominating if changed, info is not propagated sparsely to 'this' + // Propagating this info further will spuriously identify other + // progress. + return i; + } else + set_progress(); + } else + set_progress(); + } +#endif + + while( i ) { +#ifndef PRODUCT + debug_only( if( loop_count >= K ) i->dump(4); ) + assert(loop_count < K, "infinite loop in PhaseIterGVN::transform"); + debug_only( loop_count++; ) +#endif + assert((i->_idx >= k->_idx) || i->is_top(), "Idealize should return new nodes, use Identity to return old nodes"); + // Made a change; put users of original Node on worklist + add_users_to_worklist( k ); + // Replacing root of transform tree? + if( k != i ) { + // Make users of old Node now use new. + subsume_node( k, i ); + k = i; + } + DEBUG_ONLY(dead_loop_check(k);) + // Try idealizing again + i = k->Ideal(this, /*can_reshape=*/true); +#ifndef PRODUCT + if( VerifyIterativeGVN ) + verify_step(k); + if( i && VerifyOpto ) set_progress(); +#endif + } + + // If brand new node, make space in type array. + ensure_type_or_null(k); + + // See what kind of values 'k' takes on at runtime + const Type *t = k->Value(this); + assert(t != NULL, "value sanity"); + + // Since I just called 'Value' to compute the set of run-time values + // for this Node, and 'Value' is non-local (and therefore expensive) I'll + // cache Value. Later requests for the local phase->type of this Node can + // use the cached Value instead of suffering with 'bottom_type'. + if (t != type_or_null(k)) { + NOT_PRODUCT( set_progress(); ) + NOT_PRODUCT( inc_new_values();) + set_type(k, t); + // If k is a TypeNode, capture any more-precise type permanently into Node + k->raise_bottom_type(t); + // Move users of node to worklist + add_users_to_worklist( k ); + } + + // If 'k' computes a constant, replace it with a constant + if( t->singleton() && !k->is_Con() ) { + NOT_PRODUCT( set_progress(); ) + Node *con = makecon(t); // Make a constant + add_users_to_worklist( k ); + subsume_node( k, con ); // Everybody using k now uses con + return con; + } + + // Now check for Identities + i = k->Identity(this); // Look for a nearby replacement + if( i != k ) { // Found? Return replacement! + NOT_PRODUCT( set_progress(); ) + add_users_to_worklist( k ); + subsume_node( k, i ); // Everybody using k now uses i + return i; + } + + // Global Value Numbering + i = hash_find_insert(k); // Check for pre-existing node + if( i && (i != k) ) { + // Return the pre-existing node if it isn't dead + NOT_PRODUCT( set_progress(); ) + add_users_to_worklist( k ); + subsume_node( k, i ); // Everybody using k now uses i + return i; + } + + // Return Idealized original + return k; +} + +//---------------------------------saturate------------------------------------ +const Type* PhaseIterGVN::saturate(const Type* new_type, const Type* old_type, + const Type* limit_type) const { + return new_type->narrow(old_type); +} + +//------------------------------remove_globally_dead_node---------------------- +// Kill a globally dead Node. All uses are also globally dead and are +// aggressively trimmed. +void PhaseIterGVN::remove_globally_dead_node( Node *dead ) { + assert(dead != C->root(), "killing root, eh?"); + if (dead->is_top()) return; + NOT_PRODUCT( set_progress(); ) + // Remove from iterative worklist + _worklist.remove(dead); + if (!dead->is_Con()) { // Don't kill cons but uses + // Remove from hash table + _table.hash_delete( dead ); + // Smash all inputs to 'dead', isolating him completely + for( uint i = 0; i < dead->req(); i++ ) { + Node *in = dead->in(i); + if( in ) { // Points to something? + dead->set_req(i,NULL); // Kill the edge + if (in->outcnt() == 0 && in != C->top()) {// Made input go dead? + remove_dead_node(in); // Recursively remove + } else if (in->outcnt() == 1 && + in->has_special_unique_user()) { + _worklist.push(in->unique_out()); + } else if (in->outcnt() <= 2 && dead->is_Phi()) { + if( in->Opcode() == Op_Region ) + _worklist.push(in); + else if( in->is_Store() ) { + DUIterator_Fast imax, i = in->fast_outs(imax); + _worklist.push(in->fast_out(i)); + i++; + if(in->outcnt() == 2) { + _worklist.push(in->fast_out(i)); + i++; + } + assert(!(i < imax), "sanity"); + } + } + } + } + + if (dead->is_macro()) { + C->remove_macro_node(dead); + } + } + // Aggressively kill globally dead uses + // (Cannot use DUIterator_Last because of the indefinite number + // of edge deletions per loop trip.) + while (dead->outcnt() > 0) { + remove_globally_dead_node(dead->raw_out(0)); + } +} + +//------------------------------subsume_node----------------------------------- +// Remove users from node 'old' and add them to node 'nn'. +void PhaseIterGVN::subsume_node( Node *old, Node *nn ) { + assert( old != hash_find(old), "should already been removed" ); + assert( old != C->top(), "cannot subsume top node"); + // Copy debug or profile information to the new version: + C->copy_node_notes_to(nn, old); + // Move users of node 'old' to node 'nn' + for (DUIterator_Last imin, i = old->last_outs(imin); i >= imin; ) { + Node* use = old->last_out(i); // for each use... + // use might need re-hashing (but it won't if it's a new node) + bool is_in_table = _table.hash_delete( use ); + // Update use-def info as well + // We remove all occurrences of old within use->in, + // so as to avoid rehashing any node more than once. + // The hash table probe swamps any outer loop overhead. + uint num_edges = 0; + for (uint jmax = use->len(), j = 0; j < jmax; j++) { + if (use->in(j) == old) { + use->set_req(j, nn); + ++num_edges; + } + } + // Insert into GVN hash table if unique + // If a duplicate, 'use' will be cleaned up when pulled off worklist + if( is_in_table ) { + hash_find_insert(use); + } + i -= num_edges; // we deleted 1 or more copies of this edge + } + + // Smash all inputs to 'old', isolating him completely + Node *temp = new (C, 1) Node(1); + temp->init_req(0,nn); // Add a use to nn to prevent him from dying + remove_dead_node( old ); + temp->del_req(0); // Yank bogus edge +#ifndef PRODUCT + if( VerifyIterativeGVN ) { + for ( int i = 0; i < _verify_window_size; i++ ) { + if ( _verify_window[i] == old ) + _verify_window[i] = nn; + } + } +#endif + _worklist.remove(temp); // this can be necessary + temp->destruct(); // reuse the _idx of this little guy +} + +//------------------------------add_users_to_worklist-------------------------- +void PhaseIterGVN::add_users_to_worklist0( Node *n ) { + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + _worklist.push(n->fast_out(i)); // Push on worklist + } +} + +void PhaseIterGVN::add_users_to_worklist( Node *n ) { + add_users_to_worklist0(n); + + // Move users of node to worklist + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* use = n->fast_out(i); // Get use + + if( use->is_Multi() || // Multi-definer? Push projs on worklist + use->is_Store() ) // Enable store/load same address + add_users_to_worklist0(use); + + // If we changed the receiver type to a call, we need to revisit + // the Catch following the call. It's looking for a non-NULL + // receiver to know when to enable the regular fall-through path + // in addition to the NullPtrException path. + if (use->is_CallDynamicJava() && n == use->in(TypeFunc::Parms)) { + Node* p = use->as_CallDynamicJava()->proj_out(TypeFunc::Control); + if (p != NULL) { + add_users_to_worklist0(p); + } + } + + if( use->is_Cmp() ) { // Enable CMP/BOOL optimization + add_users_to_worklist(use); // Put Bool on worklist + // Look for the 'is_x2logic' pattern: "x ? : 0 : 1" and put the + // phi merging either 0 or 1 onto the worklist + if (use->outcnt() > 0) { + Node* bol = use->raw_out(0); + if (bol->outcnt() > 0) { + Node* iff = bol->raw_out(0); + if (iff->outcnt() == 2) { + Node* ifproj0 = iff->raw_out(0); + Node* ifproj1 = iff->raw_out(1); + if (ifproj0->outcnt() > 0 && ifproj1->outcnt() > 0) { + Node* region0 = ifproj0->raw_out(0); + Node* region1 = ifproj1->raw_out(0); + if( region0 == region1 ) + add_users_to_worklist0(region0); + } + } + } + } + } + + uint use_op = use->Opcode(); + // If changed Cast input, check Phi users for simple cycles + if( use->is_ConstraintCast() || use->Opcode() == Op_CheckCastPP ) { + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* u = use->fast_out(i2); + if (u->is_Phi()) + _worklist.push(u); + } + } + // If changed LShift inputs, check RShift users for useless sign-ext + if( use_op == Op_LShiftI ) { + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* u = use->fast_out(i2); + if (u->Opcode() == Op_RShiftI) + _worklist.push(u); + } + } + // If changed AddP inputs, check Stores for loop invariant + if( use_op == Op_AddP ) { + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* u = use->fast_out(i2); + if (u->is_Mem()) + _worklist.push(u); + } + } + // If changed initialization activity, check dependent Stores + if (use_op == Op_Allocate || use_op == Op_AllocateArray) { + InitializeNode* init = use->as_Allocate()->initialization(); + if (init != NULL) { + Node* imem = init->proj_out(TypeFunc::Memory); + if (imem != NULL) add_users_to_worklist0(imem); + } + } + if (use_op == Op_Initialize) { + Node* imem = use->as_Initialize()->proj_out(TypeFunc::Memory); + if (imem != NULL) add_users_to_worklist0(imem); + } + } +} + +//============================================================================= +#ifndef PRODUCT +uint PhaseCCP::_total_invokes = 0; +uint PhaseCCP::_total_constants = 0; +#endif +//------------------------------PhaseCCP--------------------------------------- +// Conditional Constant Propagation, ala Wegman & Zadeck +PhaseCCP::PhaseCCP( PhaseIterGVN *igvn ) : PhaseIterGVN(igvn) { + NOT_PRODUCT( clear_constants(); ) + assert( _worklist.size() == 0, "" ); + // Clear out _nodes from IterGVN. Must be clear to transform call. + _nodes.clear(); // Clear out from IterGVN + analyze(); +} + +#ifndef PRODUCT +//------------------------------~PhaseCCP-------------------------------------- +PhaseCCP::~PhaseCCP() { + inc_invokes(); + _total_constants += count_constants(); +} +#endif + + +#ifdef ASSERT +static bool ccp_type_widens(const Type* t, const Type* t0) { + assert(t->meet(t0) == t, "Not monotonic"); + switch (t->base() == t0->base() ? t->base() : Type::Top) { + case Type::Int: + assert(t0->isa_int()->_widen <= t->isa_int()->_widen, "widen increases"); + break; + case Type::Long: + assert(t0->isa_long()->_widen <= t->isa_long()->_widen, "widen increases"); + break; + } + return true; +} +#endif //ASSERT + +//------------------------------analyze---------------------------------------- +void PhaseCCP::analyze() { + // Initialize all types to TOP, optimistic analysis + for (int i = C->unique() - 1; i >= 0; i--) { + _types.map(i,Type::TOP); + } + + // Push root onto worklist + Unique_Node_List worklist; + worklist.push(C->root()); + + // Pull from worklist; compute new value; push changes out. + // This loop is the meat of CCP. + while( worklist.size() ) { + Node *n = worklist.pop(); + const Type *t = n->Value(this); + if (t != type(n)) { + assert(ccp_type_widens(t, type(n)), "ccp type must widen"); +#ifndef PRODUCT + if( TracePhaseCCP ) { + t->dump(); + do { tty->print("\t"); } while (tty->position() < 16); + n->dump(); + } +#endif + set_type(n, t); + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* m = n->fast_out(i); // Get user + if( m->is_Region() ) { // New path to Region? Must recheck Phis too + for (DUIterator_Fast i2max, i2 = m->fast_outs(i2max); i2 < i2max; i2++) { + Node* p = m->fast_out(i2); // Propagate changes to uses + if( p->bottom_type() != type(p) ) // If not already bottomed out + worklist.push(p); // Propagate change to user + } + } + // If we changed the reciever type to a call, we need to revisit + // the Catch following the call. It's looking for a non-NULL + // receiver to know when to enable the regular fall-through path + // in addition to the NullPtrException path + if (m->is_Call()) { + for (DUIterator_Fast i2max, i2 = m->fast_outs(i2max); i2 < i2max; i2++) { + Node* p = m->fast_out(i2); // Propagate changes to uses + if (p->is_Proj() && p->as_Proj()->_con == TypeFunc::Control && p->outcnt() == 1) + worklist.push(p->unique_out()); + } + } + if( m->bottom_type() != type(m) ) // If not already bottomed out + worklist.push(m); // Propagate change to user + } + } + } +} + +//------------------------------do_transform----------------------------------- +// Top level driver for the recursive transformer +void PhaseCCP::do_transform() { + // Correct leaves of new-space Nodes; they point to old-space. + C->set_root( transform(C->root())->as_Root() ); + assert( C->top(), "missing TOP node" ); + assert( C->root(), "missing root" ); +} + +//------------------------------transform-------------------------------------- +// Given a Node in old-space, clone him into new-space. +// Convert any of his old-space children into new-space children. +Node *PhaseCCP::transform( Node *n ) { + Node *new_node = _nodes[n->_idx]; // Check for transformed node + if( new_node != NULL ) + return new_node; // Been there, done that, return old answer + new_node = transform_once(n); // Check for constant + _nodes.map( n->_idx, new_node ); // Flag as having been cloned + + // Allocate stack of size _nodes.Size()/2 to avoid frequent realloc + GrowableArray trstack(C->unique() >> 1); + + trstack.push(new_node); // Process children of cloned node + while ( trstack.is_nonempty() ) { + Node *clone = trstack.pop(); + uint cnt = clone->req(); + for( uint i = 0; i < cnt; i++ ) { // For all inputs do + Node *input = clone->in(i); + if( input != NULL ) { // Ignore NULLs + Node *new_input = _nodes[input->_idx]; // Check for cloned input node + if( new_input == NULL ) { + new_input = transform_once(input); // Check for constant + _nodes.map( input->_idx, new_input );// Flag as having been cloned + trstack.push(new_input); + } + assert( new_input == clone->in(i), "insanity check"); + } + } + } + return new_node; +} + + +//------------------------------transform_once--------------------------------- +// For PhaseCCP, transformation is IDENTITY unless Node computed a constant. +Node *PhaseCCP::transform_once( Node *n ) { + const Type *t = type(n); + // Constant? Use constant Node instead + if( t->singleton() ) { + Node *nn = n; // Default is to return the original constant + if( t == Type::TOP ) { + // cache my top node on the Compile instance + if( C->cached_top_node() == NULL || C->cached_top_node()->in(0) == NULL ) { + C->set_cached_top_node( ConNode::make(C, Type::TOP) ); + set_type(C->top(), Type::TOP); + } + nn = C->top(); + } + if( !n->is_Con() ) { + if( t != Type::TOP ) { + nn = makecon(t); // ConNode::make(t); + NOT_PRODUCT( inc_constants(); ) + } else if( n->is_Region() ) { // Unreachable region + // Note: nn == C->top() + n->set_req(0, NULL); // Cut selfreference + // Eagerly remove dead phis to avoid phis copies creation. + for (DUIterator i = n->outs(); n->has_out(i); i++) { + Node* m = n->out(i); + if( m->is_Phi() ) { + assert(type(m) == Type::TOP, "Unreachable region should not have live phis."); + add_users_to_worklist(m); + hash_delete(m); // Yank from hash before hacking edges + subsume_node(m, nn); + --i; // deleted this phi; rescan starting with next position + } + } + } + add_users_to_worklist(n); // Users of about-to-be-constant 'n' + hash_delete(n); // Removed 'n' from table before subsuming it + subsume_node(n,nn); // Update DefUse edges for new constant + } + return nn; + } + + // If x is a TypeNode, capture any more-precise type permanently into Node + if (t != n->bottom_type()) { + hash_delete(n); // changing bottom type may force a rehash + n->raise_bottom_type(t); + _worklist.push(n); // n re-enters the hash table via the worklist + } + + // Idealize graph using DU info. Must clone() into new-space. + // DU info is generally used to show profitability, progress or safety + // (but generally not needed for correctness). + Node *nn = n->Ideal_DU_postCCP(this); + + // TEMPORARY fix to ensure that 2nd GVN pass eliminates NULL checks + switch( n->Opcode() ) { + case Op_FastLock: // Revisit FastLocks for lock coarsening + case Op_If: + case Op_CountedLoopEnd: + case Op_Region: + case Op_Loop: + case Op_CountedLoop: + case Op_Conv2B: + case Op_Opaque1: + case Op_Opaque2: + _worklist.push(n); + break; + default: + break; + } + if( nn ) { + _worklist.push(n); + // Put users of 'n' onto worklist for second igvn transform + add_users_to_worklist(n); + return nn; + } + + return n; +} + +//---------------------------------saturate------------------------------------ +const Type* PhaseCCP::saturate(const Type* new_type, const Type* old_type, + const Type* limit_type) const { + const Type* wide_type = new_type->widen(old_type); + if (wide_type != new_type) { // did we widen? + // If so, we may have widened beyond the limit type. Clip it back down. + new_type = wide_type->filter(limit_type); + } + return new_type; +} + +//------------------------------print_statistics------------------------------- +#ifndef PRODUCT +void PhaseCCP::print_statistics() { + tty->print_cr("CCP: %d constants found: %d", _total_invokes, _total_constants); +} +#endif + + +//============================================================================= +#ifndef PRODUCT +uint PhasePeephole::_total_peepholes = 0; +#endif +//------------------------------PhasePeephole---------------------------------- +// Conditional Constant Propagation, ala Wegman & Zadeck +PhasePeephole::PhasePeephole( PhaseRegAlloc *regalloc, PhaseCFG &cfg ) + : PhaseTransform(Peephole), _regalloc(regalloc), _cfg(cfg) { + NOT_PRODUCT( clear_peepholes(); ) +} + +#ifndef PRODUCT +//------------------------------~PhasePeephole--------------------------------- +PhasePeephole::~PhasePeephole() { + _total_peepholes += count_peepholes(); +} +#endif + +//------------------------------transform-------------------------------------- +Node *PhasePeephole::transform( Node *n ) { + ShouldNotCallThis(); + return NULL; +} + +//------------------------------do_transform----------------------------------- +void PhasePeephole::do_transform() { + bool method_name_not_printed = true; + + // Examine each basic block + for( uint block_number = 1; block_number < _cfg._num_blocks; ++block_number ) { + Block *block = _cfg._blocks[block_number]; + bool block_not_printed = true; + + // and each instruction within a block + uint end_index = block->_nodes.size(); + // block->end_idx() not valid after PhaseRegAlloc + for( uint instruction_index = 1; instruction_index < end_index; ++instruction_index ) { + Node *n = block->_nodes.at(instruction_index); + if( n->is_Mach() ) { + MachNode *m = n->as_Mach(); + int deleted_count = 0; + // check for peephole opportunities + MachNode *m2 = m->peephole( block, instruction_index, _regalloc, deleted_count, C ); + if( m2 != NULL ) { +#ifndef PRODUCT + if( PrintOptoPeephole ) { + // Print method, first time only + if( C->method() && method_name_not_printed ) { + C->method()->print_short_name(); tty->cr(); + method_name_not_printed = false; + } + // Print this block + if( Verbose && block_not_printed) { + tty->print_cr("in block"); + block->dump(); + block_not_printed = false; + } + // Print instructions being deleted + for( int i = (deleted_count - 1); i >= 0; --i ) { + block->_nodes.at(instruction_index-i)->as_Mach()->format(_regalloc); tty->cr(); + } + tty->print_cr("replaced with"); + // Print new instruction + m2->format(_regalloc); + tty->print("\n\n"); + } +#endif + // Remove old nodes from basic block and update instruction_index + // (old nodes still exist and may have edges pointing to them + // as register allocation info is stored in the allocator using + // the node index to live range mappings.) + uint safe_instruction_index = (instruction_index - deleted_count); + for( ; (instruction_index > safe_instruction_index); --instruction_index ) { + block->_nodes.remove( instruction_index ); + } + // install new node after safe_instruction_index + block->_nodes.insert( safe_instruction_index + 1, m2 ); + end_index = block->_nodes.size() - 1; // Recompute new block size + NOT_PRODUCT( inc_peepholes(); ) + } + } + } + } +} + +//------------------------------print_statistics------------------------------- +#ifndef PRODUCT +void PhasePeephole::print_statistics() { + tty->print_cr("Peephole: peephole rules applied: %d", _total_peepholes); +} +#endif + + +//============================================================================= +//------------------------------set_req_X-------------------------------------- +void Node::set_req_X( uint i, Node *n, PhaseIterGVN *igvn ) { + assert( is_not_dead(n), "can not use dead node"); + assert( igvn->hash_find(this) != this, "Need to remove from hash before changing edges" ); + Node *old = in(i); + set_req(i, n); + + // old goes dead? + if( old ) { + switch (old->outcnt()) { + case 0: // Kill all his inputs, and recursively kill other dead nodes. + if (!old->is_top()) + igvn->remove_dead_node( old ); + break; + case 1: + if( old->is_Store() || old->has_special_unique_user() ) + igvn->add_users_to_worklist( old ); + break; + case 2: + if( old->is_Store() ) + igvn->add_users_to_worklist( old ); + if( old->Opcode() == Op_Region ) + igvn->_worklist.push(old); + break; + case 3: + if( old->Opcode() == Op_Region ) { + igvn->_worklist.push(old); + igvn->add_users_to_worklist( old ); + } + break; + default: + break; + } + } + +} + +//-------------------------------replace_by----------------------------------- +// Using def-use info, replace one node for another. Follow the def-use info +// to all users of the OLD node. Then make all uses point to the NEW node. +void Node::replace_by(Node *new_node) { + assert(!is_top(), "top node has no DU info"); + for (DUIterator_Last imin, i = last_outs(imin); i >= imin; ) { + Node* use = last_out(i); + uint uses_found = 0; + for (uint j = 0; j < use->len(); j++) { + if (use->in(j) == this) { + if (j < use->req()) + use->set_req(j, new_node); + else use->set_prec(j, new_node); + uses_found++; + } + } + i -= uses_found; // we deleted 1 or more copies of this edge + } +} + +//============================================================================= +//----------------------------------------------------------------------------- +void Type_Array::grow( uint i ) { + if( !_max ) { + _max = 1; + _types = (const Type**)_a->Amalloc( _max * sizeof(Type*) ); + _types[0] = NULL; + } + uint old = _max; + while( i >= _max ) _max <<= 1; // Double to fit + _types = (const Type**)_a->Arealloc( _types, old*sizeof(Type*),_max*sizeof(Type*)); + memset( &_types[old], 0, (_max-old)*sizeof(Type*) ); +} + +//------------------------------dump------------------------------------------- +#ifndef PRODUCT +void Type_Array::dump() const { + uint max = Size(); + for( uint i = 0; i < max; i++ ) { + if( _types[i] != NULL ) { + tty->print(" %d\t== ", i); _types[i]->dump(); tty->cr(); + } + } +} +#endif diff --git a/hotspot/src/share/vm/opto/phaseX.hpp b/hotspot/src/share/vm/opto/phaseX.hpp new file mode 100644 index 00000000000..46439c91e57 --- /dev/null +++ b/hotspot/src/share/vm/opto/phaseX.hpp @@ -0,0 +1,516 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Compile; +class ConINode; +class ConLNode; +class Node; +class Type; +class PhaseTransform; +class PhaseGVN; +class PhaseIterGVN; +class PhaseCCP; +class PhasePeephole; +class PhaseRegAlloc; + + +//----------------------------------------------------------------------------- +// Expandable closed hash-table of nodes, initialized to NULL. +// Note that the constructor just zeros things +// Storage is reclaimed when the Arena's lifetime is over. +class NodeHash : public StackObj { +protected: + Arena *_a; // Arena to allocate in + uint _max; // Size of table (power of 2) + uint _inserts; // For grow and debug, count of hash_inserts + uint _insert_limit; // 'grow' when _inserts reaches _insert_limit + Node **_table; // Hash table of Node pointers + Node *_sentinel; // Replaces deleted entries in hash table + +public: + NodeHash(uint est_max_size); + NodeHash(Arena *arena, uint est_max_size); + NodeHash(NodeHash *use_this_state); +#ifdef ASSERT + ~NodeHash(); // Unlock all nodes upon destruction of table. + void operator=(const NodeHash&); // Unlock all nodes upon replacement of table. +#endif + Node *hash_find(const Node*);// Find an equivalent version in hash table + Node *hash_find_insert(Node*);// If not in table insert else return found node + void hash_insert(Node*); // Insert into hash table + bool hash_delete(const Node*);// Replace with _sentinel in hash table + void check_grow() { + _inserts++; + if( _inserts == _insert_limit ) { grow(); } + assert( _inserts <= _insert_limit, "hash table overflow"); + assert( _inserts < _max, "hash table overflow" ); + } + static uint round_up(uint); // Round up to nearest power of 2 + void grow(); // Grow _table to next power of 2 and rehash + // Return 75% of _max, rounded up. + uint insert_limit() const { return _max - (_max>>2); } + + void clear(); // Set all entries to NULL, keep storage. + // Size of hash table + uint size() const { return _max; } + // Return Node* at index in table + Node *at(uint table_index) { + assert(table_index < _max, "Must be within table"); + return _table[table_index]; + } + + void remove_useless_nodes(VectorSet &useful); // replace with sentinel + + Node *sentinel() { return _sentinel; } + +#ifndef PRODUCT + Node *find_index(uint idx); // For debugging + void dump(); // For debugging, dump statistics +#endif + uint _grows; // For debugging, count of table grow()s + uint _look_probes; // For debugging, count of hash probes + uint _lookup_hits; // For debugging, count of hash_finds + uint _lookup_misses; // For debugging, count of hash_finds + uint _insert_probes; // For debugging, count of hash probes + uint _delete_probes; // For debugging, count of hash probes for deletes + uint _delete_hits; // For debugging, count of hash probes for deletes + uint _delete_misses; // For debugging, count of hash probes for deletes + uint _total_inserts; // For debugging, total inserts into hash table + uint _total_insert_probes; // For debugging, total probes while inserting +}; + + +//----------------------------------------------------------------------------- +// Map dense integer indices to Types. Uses classic doubling-array trick. +// Abstractly provides an infinite array of Type*'s, initialized to NULL. +// Note that the constructor just zeros things, and since I use Arena +// allocation I do not need a destructor to reclaim storage. +// Despite the general name, this class is customized for use by PhaseTransform. +class Type_Array : public StackObj { + Arena *_a; // Arena to allocate in + uint _max; + const Type **_types; + void grow( uint i ); // Grow array node to fit + const Type *operator[] ( uint i ) const // Lookup, or NULL for not mapped + { return (i<_max) ? _types[i] : (Type*)NULL; } + friend class PhaseTransform; +public: + Type_Array(Arena *a) : _a(a), _max(0), _types(0) {} + Type_Array(Type_Array *ta) : _a(ta->_a), _max(ta->_max), _types(ta->_types) { } + const Type *fast_lookup(uint i) const{assert(i<_max,"oob");return _types[i];} + // Extend the mapping: index i maps to Type *n. + void map( uint i, const Type *n ) { if( i>=_max ) grow(i); _types[i] = n; } + uint Size() const { return _max; } +#ifndef PRODUCT + void dump() const; +#endif +}; + + +//------------------------------PhaseRemoveUseless----------------------------- +// Remove useless nodes from GVN hash-table, worklist, and graph +class PhaseRemoveUseless : public Phase { +protected: + Unique_Node_List _useful; // Nodes reachable from root + // list is allocated from current resource area +public: + PhaseRemoveUseless( PhaseGVN *gvn, Unique_Node_List *worklist ); + + Unique_Node_List *get_useful() { return &_useful; } +}; + + +//------------------------------PhaseTransform--------------------------------- +// Phases that analyze, then transform. Constructing the Phase object does any +// global or slow analysis. The results are cached later for a fast +// transformation pass. When the Phase object is deleted the cached analysis +// results are deleted. +class PhaseTransform : public Phase { +protected: + Arena* _arena; + Node_Array _nodes; // Map old node indices to new nodes. + Type_Array _types; // Map old node indices to Types. + + // ConNode caches: + enum { _icon_min = -1 * HeapWordSize, + _icon_max = 16 * HeapWordSize, + _lcon_min = _icon_min, + _lcon_max = _icon_max, + _zcon_max = (uint)T_CONFLICT + }; + ConINode* _icons[_icon_max - _icon_min + 1]; // cached jint constant nodes + ConLNode* _lcons[_lcon_max - _lcon_min + 1]; // cached jlong constant nodes + ConNode* _zcons[_zcon_max + 1]; // cached is_zero_type nodes + void init_con_caches(); + + // Support both int and long caches because either might be an intptr_t, + // so they show up frequently in address computations. + +public: + PhaseTransform( PhaseNumber pnum ); + PhaseTransform( Arena *arena, PhaseNumber pnum ); + PhaseTransform( PhaseTransform *phase, PhaseNumber pnum ); + + Arena* arena() { return _arena; } + Type_Array& types() { return _types; } + // _nodes is used in varying ways by subclasses, which define local accessors + +public: + // Get a previously recorded type for the node n. + // This type must already have been recorded. + // If you want the type of a very new (untransformed) node, + // you must use type_or_null, and test the result for NULL. + const Type* type(const Node* n) const { + const Type* t = _types.fast_lookup(n->_idx); + assert(t != NULL, "must set before get"); + return t; + } + // Get a previously recorded type for the node n, + // or else return NULL if there is none. + const Type* type_or_null(const Node* n) const { + return _types.fast_lookup(n->_idx); + } + // Record a type for a node. + void set_type(const Node* n, const Type *t) { + assert(t != NULL, "type must not be null"); + _types.map(n->_idx, t); + } + // Record an initial type for a node, the node's bottom type. + void set_type_bottom(const Node* n) { + // Use this for initialization when bottom_type() (or better) is not handy. + // Usually the initialization shoudl be to n->Value(this) instead, + // or a hand-optimized value like Type::MEMORY or Type::CONTROL. + assert(_types[n->_idx] == NULL, "must set the initial type just once"); + _types.map(n->_idx, n->bottom_type()); + } + // Make sure the types array is big enough to record a size for the node n. + // (In product builds, we never want to do range checks on the types array!) + void ensure_type_or_null(const Node* n) { + if (n->_idx >= _types.Size()) + _types.map(n->_idx, NULL); // Grow the types array as needed. + } + + // Utility functions: + const TypeInt* find_int_type( Node* n); + const TypeLong* find_long_type(Node* n); + jint find_int_con( Node* n, jint value_if_unknown) { + const TypeInt* t = find_int_type(n); + return (t != NULL && t->is_con()) ? t->get_con() : value_if_unknown; + } + jlong find_long_con(Node* n, jlong value_if_unknown) { + const TypeLong* t = find_long_type(n); + return (t != NULL && t->is_con()) ? t->get_con() : value_if_unknown; + } + + // Make an idealized constant, i.e., one of ConINode, ConPNode, ConFNode, etc. + // Same as transform(ConNode::make(t)). + ConNode* makecon(const Type* t); + virtual ConNode* uncached_makecon(const Type* t) // override in PhaseValues + { ShouldNotCallThis(); return NULL; } + + // Fast int or long constant. Same as TypeInt::make(i) or TypeLong::make(l). + ConINode* intcon(jint i); + ConLNode* longcon(jlong l); + + // Fast zero or null constant. Same as makecon(Type::get_zero_type(bt)). + ConNode* zerocon(BasicType bt); + + // Return a node which computes the same function as this node, but + // in a faster or cheaper fashion. + virtual Node *transform( Node *n ) = 0; + + // Return whether two Nodes are equivalent. + // Must not be recursive, since the recursive version is built from this. + // For pessimistic optimizations this is simply pointer equivalence. + bool eqv(const Node* n1, const Node* n2) const { return n1 == n2; } + + // Return whether two Nodes are equivalent, after stripping casting. + bool eqv_uncast(const Node* n1, const Node* n2) const { + return eqv(n1->uncast(), n2->uncast()); + } + + // For pessimistic passes, the return type must monotonically narrow. + // For optimistic passes, the return type must monotonically widen. + // It is possible to get into a "death march" in either type of pass, + // where the types are continually moving but it will take 2**31 or + // more steps to converge. This doesn't happen on most normal loops. + // + // Here is an example of a deadly loop for an optimistic pass, along + // with a partial trace of inferred types: + // x = phi(0,x'); L: x' = x+1; if (x' >= 0) goto L; + // 0 1 join([0..max], 1) + // [0..1] [1..2] join([0..max], [1..2]) + // [0..2] [1..3] join([0..max], [1..3]) + // ... ... ... + // [0..max] [min]u[1..max] join([0..max], [min..max]) + // [0..max] ==> fixpoint + // We would have proven, the hard way, that the iteration space is all + // non-negative ints, with the loop terminating due to 32-bit overflow. + // + // Here is the corresponding example for a pessimistic pass: + // x = phi(0,x'); L: x' = x-1; if (x' >= 0) goto L; + // int int join([0..max], int) + // [0..max] [-1..max-1] join([0..max], [-1..max-1]) + // [0..max-1] [-1..max-2] join([0..max], [-1..max-2]) + // ... ... ... + // [0..1] [-1..0] join([0..max], [-1..0]) + // 0 -1 join([0..max], -1) + // 0 == fixpoint + // We would have proven, the hard way, that the iteration space is {0}. + // (Usually, other optimizations will make the "if (x >= 0)" fold up + // before we get into trouble. But not always.) + // + // It's a pleasant thing to observe that the pessimistic pass + // will make short work of the optimistic pass's deadly loop, + // and vice versa. That is a good example of the complementary + // purposes of the CCP (optimistic) vs. GVN (pessimistic) phases. + // + // In any case, only widen or narrow a few times before going to the + // correct flavor of top or bottom. + // + // This call only needs to be made once as the data flows around any + // given cycle. We do it at Phis, and nowhere else. + // The types presented are the new type of a phi (computed by PhiNode::Value) + // and the previously computed type, last time the phi was visited. + // + // The third argument is upper limit for the saturated value, + // if the phase wishes to widen the new_type. + // If the phase is narrowing, the old type provides a lower limit. + // Caller guarantees that old_type and new_type are no higher than limit_type. + virtual const Type* saturate(const Type* new_type, const Type* old_type, + const Type* limit_type) const + { ShouldNotCallThis(); return NULL; } + +#ifndef PRODUCT + void dump_old2new_map() const; + void dump_new( uint new_lidx ) const; + void dump_types() const; + void dump_nodes_and_types(const Node *root, uint depth, bool only_ctrl = true); + void dump_nodes_and_types_recur( const Node *n, uint depth, bool only_ctrl, VectorSet &visited); + + uint _count_progress; // For profiling, count transforms that make progress + void set_progress() { ++_count_progress; assert( allow_progress(),"No progress allowed during verification") } + void clear_progress() { _count_progress = 0; } + uint made_progress() const { return _count_progress; } + + uint _count_transforms; // For profiling, count transforms performed + void set_transforms() { ++_count_transforms; } + void clear_transforms() { _count_transforms = 0; } + uint made_transforms() const{ return _count_transforms; } + + bool _allow_progress; // progress not allowed during verification pass + void set_allow_progress(bool allow) { _allow_progress = allow; } + bool allow_progress() { return _allow_progress; } +#endif +}; + +//------------------------------PhaseValues------------------------------------ +// Phase infrastructure to support values +class PhaseValues : public PhaseTransform { +protected: + NodeHash _table; // Hash table for value-numbering + +public: + PhaseValues( Arena *arena, uint est_max_size ); + PhaseValues( PhaseValues *pt ); + PhaseValues( PhaseValues *ptv, const char *dummy ); + NOT_PRODUCT( ~PhaseValues(); ) + virtual PhaseIterGVN *is_IterGVN() { return 0; } + + // Some Ideal and other transforms delete --> modify --> insert values + bool hash_delete(Node *n) { return _table.hash_delete(n); } + void hash_insert(Node *n) { _table.hash_insert(n); } + Node *hash_find_insert(Node *n){ return _table.hash_find_insert(n); } + Node *hash_find(const Node *n) { return _table.hash_find(n); } + + // Used after parsing to eliminate values that are no longer in program + void remove_useless_nodes(VectorSet &useful) { _table.remove_useless_nodes(useful); } + + virtual ConNode* uncached_makecon(const Type* t); // override from PhaseTransform + + virtual const Type* saturate(const Type* new_type, const Type* old_type, + const Type* limit_type) const + { return new_type; } + +#ifndef PRODUCT + uint _count_new_values; // For profiling, count new values produced + void inc_new_values() { ++_count_new_values; } + void clear_new_values() { _count_new_values = 0; } + uint made_new_values() const { return _count_new_values; } +#endif +}; + + +//------------------------------PhaseGVN--------------------------------------- +// Phase for performing local, pessimistic GVN-style optimizations. +class PhaseGVN : public PhaseValues { +public: + PhaseGVN( Arena *arena, uint est_max_size ) : PhaseValues( arena, est_max_size ) {} + PhaseGVN( PhaseGVN *gvn ) : PhaseValues( gvn ) {} + PhaseGVN( PhaseGVN *gvn, const char *dummy ) : PhaseValues( gvn, dummy ) {} + + // Return a node which computes the same function as this node, but + // in a faster or cheaper fashion. + Node *transform( Node *n ); + Node *transform_no_reclaim( Node *n ); + + // Check for a simple dead loop when a data node references itself. + DEBUG_ONLY(void dead_loop_check(Node *n);) +}; + +//------------------------------PhaseIterGVN----------------------------------- +// Phase for iteratively performing local, pessimistic GVN-style optimizations. +// and ideal transformations on the graph. +class PhaseIterGVN : public PhaseGVN { + // Idealize old Node 'n' with respect to its inputs and its value + virtual Node *transform_old( Node *a_node ); +protected: + + // Idealize new Node 'n' with respect to its inputs and its value + virtual Node *transform( Node *a_node ); + + // Warm up hash table, type table and initial worklist + void init_worklist( Node *a_root ); + + virtual const Type* saturate(const Type* new_type, const Type* old_type, + const Type* limit_type) const; + // Usually returns new_type. Returns old_type if new_type is only a slight + // improvement, such that it would take many (>>10) steps to reach 2**32. + +public: + PhaseIterGVN( PhaseIterGVN *igvn ); // Used by CCP constructor + PhaseIterGVN( PhaseGVN *gvn ); // Used after Parser + PhaseIterGVN( PhaseIterGVN *igvn, const char *dummy ); // Used after +VerifyOpto + + virtual PhaseIterGVN *is_IterGVN() { return this; } + + Unique_Node_List _worklist; // Iterative worklist + + // Given def-use info and an initial worklist, apply Node::Ideal, + // Node::Value, Node::Identity, hash-based value numbering, Node::Ideal_DU + // and dominator info to a fixed point. + void optimize(); + + // Register a new node with the iter GVN pass without transforming it. + // Used when we need to restructure a Region/Phi area and all the Regions + // and Phis need to complete this one big transform before any other + // transforms can be triggered on the region. + // Optional 'orig' is an earlier version of this node. + // It is significant only for debugging and profiling. + Node* register_new_node_with_optimizer(Node* n, Node* orig = NULL); + + // Kill a globally dead Node. It is allowed to have uses which are + // assumed dead and left 'in limbo'. + void remove_globally_dead_node( Node *dead ); + + // Kill all inputs to a dead node, recursively making more dead nodes. + // The Node must be dead locally, i.e., have no uses. + void remove_dead_node( Node *dead ) { + assert(dead->outcnt() == 0 && !dead->is_top(), "node must be dead"); + remove_globally_dead_node(dead); + } + + // Subsume users of node 'old' into node 'nn' + // If no Def-Use info existed for 'nn' it will after call. + void subsume_node( Node *old, Node *nn ); + + // Add users of 'n' to worklist + void add_users_to_worklist0( Node *n ); + void add_users_to_worklist ( Node *n ); + +#ifndef PRODUCT +protected: + // Sub-quadratic implementation of VerifyIterativeGVN. + unsigned long _verify_counter; + unsigned long _verify_full_passes; + enum { _verify_window_size = 30 }; + Node* _verify_window[_verify_window_size]; + void verify_step(Node* n); +#endif +}; + +//------------------------------PhaseCCP--------------------------------------- +// Phase for performing global Conditional Constant Propagation. +// Should be replaced with combined CCP & GVN someday. +class PhaseCCP : public PhaseIterGVN { + // Non-recursive. Use analysis to transform single Node. + virtual Node *transform_once( Node *n ); + +public: + PhaseCCP( PhaseIterGVN *igvn ); // Compute conditional constants + NOT_PRODUCT( ~PhaseCCP(); ) + + // Worklist algorithm identifies constants + void analyze(); + // Recursive traversal of program. Used analysis to modify program. + virtual Node *transform( Node *n ); + // Do any transformation after analysis + void do_transform(); + + virtual const Type* saturate(const Type* new_type, const Type* old_type, + const Type* limit_type) const; + // Returns new_type->widen(old_type), which increments the widen bits until + // giving up with TypeInt::INT or TypeLong::LONG. + // Result is clipped to limit_type if necessary. + +#ifndef PRODUCT + static uint _total_invokes; // For profiling, count invocations + void inc_invokes() { ++PhaseCCP::_total_invokes; } + + static uint _total_constants; // For profiling, count constants found + uint _count_constants; + void clear_constants() { _count_constants = 0; } + void inc_constants() { ++_count_constants; } + uint count_constants() const { return _count_constants; } + + static void print_statistics(); +#endif +}; + + +//------------------------------PhasePeephole---------------------------------- +// Phase for performing peephole optimizations on register allocated basic blocks. +class PhasePeephole : public PhaseTransform { + PhaseRegAlloc *_regalloc; + PhaseCFG &_cfg; + // Recursive traversal of program. Pure function is unused in this phase + virtual Node *transform( Node *n ); + +public: + PhasePeephole( PhaseRegAlloc *regalloc, PhaseCFG &cfg ); + NOT_PRODUCT( ~PhasePeephole(); ) + + // Do any transformation after analysis + void do_transform(); + +#ifndef PRODUCT + static uint _total_peepholes; // For profiling, count peephole rules applied + uint _count_peepholes; + void clear_peepholes() { _count_peepholes = 0; } + void inc_peepholes() { ++_count_peepholes; } + uint count_peepholes() const { return _count_peepholes; } + + static void print_statistics(); +#endif +}; diff --git a/hotspot/src/share/vm/opto/postaloc.cpp b/hotspot/src/share/vm/opto/postaloc.cpp new file mode 100644 index 00000000000..35b469baeaa --- /dev/null +++ b/hotspot/src/share/vm/opto/postaloc.cpp @@ -0,0 +1,584 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_postaloc.cpp.incl" + +// see if this register kind does not requires two registers +static bool is_single_register(uint x) { +#ifdef _LP64 + return (x != Op_RegD && x != Op_RegL && x != Op_RegP); +#else + return (x != Op_RegD && x != Op_RegL); +#endif +} + +//------------------------------may_be_copy_of_callee----------------------------- +// Check to see if we can possibly be a copy of a callee-save value. +bool PhaseChaitin::may_be_copy_of_callee( Node *def ) const { + // Short circuit if there are no callee save registers + if (_matcher.number_of_saved_registers() == 0) return false; + + // Expect only a spill-down and reload on exit for callee-save spills. + // Chains of copies cannot be deep. + // 5008997 - This is wishful thinking. Register allocator seems to + // be splitting live ranges for callee save registers to such + // an extent that in large methods the chains can be very long + // (50+). The conservative answer is to return true if we don't + // know as this prevents optimizations from occuring. + + const int limit = 60; + int i; + for( i=0; i < limit; i++ ) { + if( def->is_Proj() && def->in(0)->is_Start() && + _matcher.is_save_on_entry(lrgs(n2lidx(def)).reg()) ) + return true; // Direct use of callee-save proj + if( def->is_Copy() ) // Copies carry value through + def = def->in(def->is_Copy()); + else if( def->is_Phi() ) // Phis can merge it from any direction + def = def->in(1); + else + break; + guarantee(def != NULL, "must not resurrect dead copy"); + } + // If we reached the end and didn't find a callee save proj + // then this may be a callee save proj so we return true + // as the conservative answer. If we didn't reach then end + // we must have discovered that it was not a callee save + // else we would have returned. + return i == limit; +} + + + +//------------------------------yank_if_dead----------------------------------- +// Removed an edge from 'old'. Yank if dead. Return adjustment counts to +// iterators in the current block. +int PhaseChaitin::yank_if_dead( Node *old, Block *current_block, Node_List *value, Node_List *regnd ) { + int blk_adjust=0; + while (old->outcnt() == 0 && old != C->top()) { + Block *oldb = _cfg._bbs[old->_idx]; + oldb->find_remove(old); + // Count 1 if deleting an instruction from the current block + if( oldb == current_block ) blk_adjust++; + _cfg._bbs.map(old->_idx,NULL); + OptoReg::Name old_reg = lrgs(n2lidx(old)).reg(); + if( regnd && (*regnd)[old_reg]==old ) { // Instruction is currently available? + value->map(old_reg,NULL); // Yank from value/regnd maps + regnd->map(old_reg,NULL); // This register's value is now unknown + } + Node *tmp = old->req() > 1 ? old->in(1) : NULL; + old->disconnect_inputs(NULL); + if( !tmp ) break; + old = tmp; + } + return blk_adjust; +} + +//------------------------------use_prior_register----------------------------- +// Use the prior value instead of the current value, in an effort to make +// the current value go dead. Return block iterator adjustment, in case +// we yank some instructions from this block. +int PhaseChaitin::use_prior_register( Node *n, uint idx, Node *def, Block *current_block, Node_List &value, Node_List ®nd ) { + // No effect? + if( def == n->in(idx) ) return 0; + // Def is currently dead and can be removed? Do not resurrect + if( def->outcnt() == 0 ) return 0; + + // Not every pair of physical registers are assignment compatible, + // e.g. on sparc floating point registers are not assignable to integer + // registers. + const LRG &def_lrg = lrgs(n2lidx(def)); + OptoReg::Name def_reg = def_lrg.reg(); + const RegMask &use_mask = n->in_RegMask(idx); + bool can_use = ( RegMask::can_represent(def_reg) ? (use_mask.Member(def_reg) != 0) + : (use_mask.is_AllStack() != 0)); + // Check for a copy to or from a misaligned pair. + can_use = can_use && !use_mask.is_misaligned_Pair() && !def_lrg.mask().is_misaligned_Pair(); + + if (!can_use) + return 0; + + // Capture the old def in case it goes dead... + Node *old = n->in(idx); + + // Save-on-call copies can only be elided if the entire copy chain can go + // away, lest we get the same callee-save value alive in 2 locations at + // once. We check for the obvious trivial case here. Although it can + // sometimes be elided with cooperation outside our scope, here we will just + // miss the opportunity. :-( + if( may_be_copy_of_callee(def) ) { + if( old->outcnt() > 1 ) return 0; // We're the not last user + int idx = old->is_Copy(); + assert( idx, "chain of copies being removed" ); + Node *old2 = old->in(idx); // Chain of copies + if( old2->outcnt() > 1 ) return 0; // old is not the last user + int idx2 = old2->is_Copy(); + if( !idx2 ) return 0; // Not a chain of 2 copies + if( def != old2->in(idx2) ) return 0; // Chain of exactly 2 copies + } + + // Use the new def + n->set_req(idx,def); + _post_alloc++; + + // Is old def now dead? We successfully yanked a copy? + return yank_if_dead(old,current_block,&value,®nd); +} + + +//------------------------------skip_copies------------------------------------ +// Skip through any number of copies (that don't mod oop-i-ness) +Node *PhaseChaitin::skip_copies( Node *c ) { + int idx = c->is_Copy(); + uint is_oop = lrgs(n2lidx(c))._is_oop; + while (idx != 0) { + guarantee(c->in(idx) != NULL, "must not resurrect dead copy"); + if (lrgs(n2lidx(c->in(idx)))._is_oop != is_oop) + break; // casting copy, not the same value + c = c->in(idx); + idx = c->is_Copy(); + } + return c; +} + +//------------------------------elide_copy------------------------------------- +// Remove (bypass) copies along Node n, edge k. +int PhaseChaitin::elide_copy( Node *n, int k, Block *current_block, Node_List &value, Node_List ®nd, bool can_change_regs ) { + int blk_adjust = 0; + + uint nk_idx = n2lidx(n->in(k)); + OptoReg::Name nk_reg = lrgs(nk_idx ).reg(); + + // Remove obvious same-register copies + Node *x = n->in(k); + int idx; + while( (idx=x->is_Copy()) != 0 ) { + Node *copy = x->in(idx); + guarantee(copy != NULL, "must not resurrect dead copy"); + if( lrgs(n2lidx(copy)).reg() != nk_reg ) break; + blk_adjust += use_prior_register(n,k,copy,current_block,value,regnd); + if( n->in(k) != copy ) break; // Failed for some cutout? + x = copy; // Progress, try again + } + + // Phis and 2-address instructions cannot change registers so easily - their + // outputs must match their input. + if( !can_change_regs ) + return blk_adjust; // Only check stupid copies! + + // Loop backedges won't have a value-mapping yet + if( &value == NULL ) return blk_adjust; + + // Skip through all copies to the _value_ being used. Do not change from + // int to pointer. This attempts to jump through a chain of copies, where + // intermediate copies might be illegal, i.e., value is stored down to stack + // then reloaded BUT survives in a register the whole way. + Node *val = skip_copies(n->in(k)); + + if( val == x ) return blk_adjust; // No progress? + + bool single = is_single_register(val->ideal_reg()); + uint val_idx = n2lidx(val); + OptoReg::Name val_reg = lrgs(val_idx).reg(); + + // See if it happens to already be in the correct register! + // (either Phi's direct register, or the common case of the name + // never-clobbered original-def register) + if( value[val_reg] == val && + // Doubles check both halves + ( single || value[val_reg-1] == val ) ) { + blk_adjust += use_prior_register(n,k,regnd[val_reg],current_block,value,regnd); + if( n->in(k) == regnd[val_reg] ) // Success! Quit trying + return blk_adjust; + } + + // See if we can skip the copy by changing registers. Don't change from + // using a register to using the stack unless we know we can remove a + // copy-load. Otherwise we might end up making a pile of Intel cisc-spill + // ops reading from memory instead of just loading once and using the + // register. + + // Also handle duplicate copies here. + const Type *t = val->is_Con() ? val->bottom_type() : NULL; + + // Scan all registers to see if this value is around already + for( uint reg = 0; reg < (uint)_max_reg; reg++ ) { + Node *vv = value[reg]; + if( !single ) { // Doubles check for aligned-adjacent pair + if( (reg&1)==0 ) continue; // Wrong half of a pair + if( vv != value[reg-1] ) continue; // Not a complete pair + } + if( vv == val || // Got a direct hit? + (t && vv && vv->bottom_type() == t && vv->is_Mach() && + vv->as_Mach()->rule() == val->as_Mach()->rule()) ) { // Or same constant? + assert( !n->is_Phi(), "cannot change registers at a Phi so easily" ); + if( OptoReg::is_stack(nk_reg) || // CISC-loading from stack OR + OptoReg::is_reg(reg) || // turning into a register use OR + regnd[reg]->outcnt()==1 ) { // last use of a spill-load turns into a CISC use + blk_adjust += use_prior_register(n,k,regnd[reg],current_block,value,regnd); + if( n->in(k) == regnd[reg] ) // Success! Quit trying + return blk_adjust; + } // End of if not degrading to a stack + } // End of if found value in another register + } // End of scan all machine registers + return blk_adjust; +} + + +// +// Check if nreg already contains the constant value val. Normal copy +// elimination doesn't doesn't work on constants because multiple +// nodes can represent the same constant so the type and rule of the +// MachNode must be checked to ensure equivalence. +// +bool PhaseChaitin::eliminate_copy_of_constant(Node* val, Block *current_block, + Node_List& value, Node_List& regnd, + OptoReg::Name nreg, OptoReg::Name nreg2) { + if (value[nreg] != val && val->is_Con() && + value[nreg] != NULL && value[nreg]->is_Con() && + (nreg2 == OptoReg::Bad || value[nreg] == value[nreg2]) && + value[nreg]->bottom_type() == val->bottom_type() && + value[nreg]->as_Mach()->rule() == val->as_Mach()->rule()) { + // This code assumes that two MachNodes representing constants + // which have the same rule and the same bottom type will produce + // identical effects into a register. This seems like it must be + // objectively true unless there are hidden inputs to the nodes + // but if that were to change this code would need to updated. + // Since they are equivalent the second one if redundant and can + // be removed. + // + // val will be replaced with the old value but val might have + // kills projections associated with it so remove them now so that + // yank_if_dead will be able to elminate the copy once the uses + // have been transferred to the old[value]. + for (DUIterator_Fast imax, i = val->fast_outs(imax); i < imax; i++) { + Node* use = val->fast_out(i); + if (use->is_Proj() && use->outcnt() == 0) { + // Kill projections have no users and one input + use->set_req(0, C->top()); + yank_if_dead(use, current_block, &value, ®nd); + --i; --imax; + } + } + _post_alloc++; + return true; + } + return false; +} + + +//------------------------------post_allocate_copy_removal--------------------- +// Post-Allocation peephole copy removal. We do this in 1 pass over the +// basic blocks. We maintain a mapping of registers to Nodes (an array of +// Nodes indexed by machine register or stack slot number). NULL means that a +// register is not mapped to any Node. We can (want to have!) have several +// registers map to the same Node. We walk forward over the instructions +// updating the mapping as we go. At merge points we force a NULL if we have +// to merge 2 different Nodes into the same register. Phi functions will give +// us a new Node if there is a proper value merging. Since the blocks are +// arranged in some RPO, we will visit all parent blocks before visiting any +// successor blocks (except at loops). +// +// If we find a Copy we look to see if the Copy's source register is a stack +// slot and that value has already been loaded into some machine register; if +// so we use machine register directly. This turns a Load into a reg-reg +// Move. We also look for reloads of identical constants. +// +// When we see a use from a reg-reg Copy, we will attempt to use the copy's +// source directly and make the copy go dead. +void PhaseChaitin::post_allocate_copy_removal() { + NOT_PRODUCT( Compile::TracePhase t3("postAllocCopyRemoval", &_t_postAllocCopyRemoval, TimeCompiler); ) + ResourceMark rm; + + // Need a mapping from basic block Node_Lists. We need a Node_List to + // map from register number to value-producing Node. + Node_List **blk2value = NEW_RESOURCE_ARRAY( Node_List *, _cfg._num_blocks+1); + memset( blk2value, 0, sizeof(Node_List*)*(_cfg._num_blocks+1) ); + // Need a mapping from basic block Node_Lists. We need a Node_List to + // map from register number to register-defining Node. + Node_List **blk2regnd = NEW_RESOURCE_ARRAY( Node_List *, _cfg._num_blocks+1); + memset( blk2regnd, 0, sizeof(Node_List*)*(_cfg._num_blocks+1) ); + + // We keep unused Node_Lists on a free_list to avoid wasting + // memory. + GrowableArray free_list = GrowableArray(16); + + // For all blocks + for( uint i = 0; i < _cfg._num_blocks; i++ ) { + uint j; + Block *b = _cfg._blocks[i]; + + // Count of Phis in block + uint phi_dex; + for( phi_dex = 1; phi_dex < b->_nodes.size(); phi_dex++ ) { + Node *phi = b->_nodes[phi_dex]; + if( !phi->is_Phi() ) + break; + } + + // If any predecessor has not been visited, we do not know the state + // of registers at the start. Check for this, while updating copies + // along Phi input edges + bool missing_some_inputs = false; + Block *freed = NULL; + for( j = 1; j < b->num_preds(); j++ ) { + Block *pb = _cfg._bbs[b->pred(j)->_idx]; + // Remove copies along phi edges + for( uint k=1; k_nodes[k], j, b, *blk2value[pb->_pre_order], *blk2regnd[pb->_pre_order], false ); + if( blk2value[pb->_pre_order] ) { // Have a mapping on this edge? + // See if this predecessor's mappings have been used by everybody + // who wants them. If so, free 'em. + uint k; + for( k=0; k_num_succs; k++ ) { + Block *pbsucc = pb->_succs[k]; + if( !blk2value[pbsucc->_pre_order] && pbsucc != b ) + break; // Found a future user + } + if( k >= pb->_num_succs ) { // No more uses, free! + freed = pb; // Record last block freed + free_list.push(blk2value[pb->_pre_order]); + free_list.push(blk2regnd[pb->_pre_order]); + } + } else { // This block has unvisited (loopback) inputs + missing_some_inputs = true; + } + } + + + // Extract Node_List mappings. If 'freed' is non-zero, we just popped + // 'freed's blocks off the list + Node_List ®nd = *(free_list.is_empty() ? new Node_List() : free_list.pop()); + Node_List &value = *(free_list.is_empty() ? new Node_List() : free_list.pop()); + assert( !freed || blk2value[freed->_pre_order] == &value, "" ); + value.map(_max_reg,NULL); + regnd.map(_max_reg,NULL); + // Set mappings as OUR mappings + blk2value[b->_pre_order] = &value; + blk2regnd[b->_pre_order] = ®nd; + + // Initialize value & regnd for this block + if( missing_some_inputs ) { + // Some predecessor has not yet been visited; zap map to empty + for( uint k = 0; k < (uint)_max_reg; k++ ) { + value.map(k,NULL); + regnd.map(k,NULL); + } + } else { + if( !freed ) { // Didn't get a freebie prior block + // Must clone some data + freed = _cfg._bbs[b->pred(1)->_idx]; + Node_List &f_value = *blk2value[freed->_pre_order]; + Node_List &f_regnd = *blk2regnd[freed->_pre_order]; + for( uint k = 0; k < (uint)_max_reg; k++ ) { + value.map(k,f_value[k]); + regnd.map(k,f_regnd[k]); + } + } + // Merge all inputs together, setting to NULL any conflicts. + for( j = 1; j < b->num_preds(); j++ ) { + Block *pb = _cfg._bbs[b->pred(j)->_idx]; + if( pb == freed ) continue; // Did self already via freelist + Node_List &p_regnd = *blk2regnd[pb->_pre_order]; + for( uint k = 0; k < (uint)_max_reg; k++ ) { + if( regnd[k] != p_regnd[k] ) { // Conflict on reaching defs? + value.map(k,NULL); // Then no value handy + regnd.map(k,NULL); + } + } + } + } + + // For all Phi's + for( j = 1; j < phi_dex; j++ ) { + uint k; + Node *phi = b->_nodes[j]; + uint pidx = n2lidx(phi); + OptoReg::Name preg = lrgs(n2lidx(phi)).reg(); + + // Remove copies remaining on edges. Check for junk phi. + Node *u = NULL; + for( k=1; kreq(); k++ ) { + Node *x = phi->in(k); + if( phi != x && u != x ) // Found a different input + u = u ? NodeSentinel : x; // Capture unique input, or NodeSentinel for 2nd input + } + if( u != NodeSentinel ) { // Junk Phi. Remove + b->_nodes.remove(j--); phi_dex--; + _cfg._bbs.map(phi->_idx,NULL); + phi->replace_by(u); + phi->disconnect_inputs(NULL); + continue; + } + // Note that if value[pidx] exists, then we merged no new values here + // and the phi is useless. This can happen even with the above phi + // removal for complex flows. I cannot keep the better known value here + // because locally the phi appears to define a new merged value. If I + // keep the better value then a copy of the phi, being unable to use the + // global flow analysis, can't "peek through" the phi to the original + // reaching value and so will act like it's defining a new value. This + // can lead to situations where some uses are from the old and some from + // the new values. Not illegal by itself but throws the over-strong + // assert in scheduling. + if( pidx ) { + value.map(preg,phi); + regnd.map(preg,phi); + OptoReg::Name preg_lo = OptoReg::add(preg,-1); + if( !is_single_register(phi->ideal_reg()) ) { + value.map(preg_lo,phi); + regnd.map(preg_lo,phi); + } + } + } + + // For all remaining instructions + for( j = phi_dex; j < b->_nodes.size(); j++ ) { + Node *n = b->_nodes[j]; + + if( n->outcnt() == 0 && // Dead? + n != C->top() && // (ignore TOP, it has no du info) + !n->is_Proj() ) { // fat-proj kills + j -= yank_if_dead(n,b,&value,®nd); + continue; + } + + // Improve reaching-def info. Occasionally post-alloc's liveness gives + // up (at loop backedges, because we aren't doing a full flow pass). + // The presence of a live use essentially asserts that the use's def is + // alive and well at the use (or else the allocator fubar'd). Take + // advantage of this info to set a reaching def for the use-reg. + uint k; + for( k = 1; k < n->req(); k++ ) { + Node *def = n->in(k); // n->in(k) is a USE; def is the DEF for this USE + guarantee(def != NULL, "no disconnected nodes at this point"); + uint useidx = n2lidx(def); // useidx is the live range index for this USE + + if( useidx ) { + OptoReg::Name ureg = lrgs(useidx).reg(); + if( !value[ureg] ) { + int idx; // Skip occasional useless copy + while( (idx=def->is_Copy()) != 0 && + def->in(idx) != NULL && // NULL should not happen + ureg == lrgs(n2lidx(def->in(idx))).reg() ) + def = def->in(idx); + Node *valdef = skip_copies(def); // tighten up val through non-useless copies + value.map(ureg,valdef); // record improved reaching-def info + regnd.map(ureg, def); + // Record other half of doubles + OptoReg::Name ureg_lo = OptoReg::add(ureg,-1); + if( !is_single_register(def->ideal_reg()) && + ( !RegMask::can_represent(ureg_lo) || + lrgs(useidx).mask().Member(ureg_lo) ) && // Nearly always adjacent + !value[ureg_lo] ) { + value.map(ureg_lo,valdef); // record improved reaching-def info + regnd.map(ureg_lo, def); + } + } + } + } + + const uint two_adr = n->is_Mach() ? n->as_Mach()->two_adr() : 0; + + // Remove copies along input edges + for( k = 1; k < n->req(); k++ ) + j -= elide_copy( n, k, b, value, regnd, two_adr!=k ); + + // Unallocated Nodes define no registers + uint lidx = n2lidx(n); + if( !lidx ) continue; + + // Update the register defined by this instruction + OptoReg::Name nreg = lrgs(lidx).reg(); + // Skip through all copies to the _value_ being defined. + // Do not change from int to pointer + Node *val = skip_copies(n); + + uint n_ideal_reg = n->ideal_reg(); + if( is_single_register(n_ideal_reg) ) { + // If Node 'n' does not change the value mapped by the register, + // then 'n' is a useless copy. Do not update the register->node + // mapping so 'n' will go dead. + if( value[nreg] != val ) { + if (eliminate_copy_of_constant(val, b, value, regnd, nreg, OptoReg::Bad)) { + n->replace_by(regnd[nreg]); + j -= yank_if_dead(n,b,&value,®nd); + } else { + // Update the mapping: record new Node defined by the register + regnd.map(nreg,n); + // Update mapping for defined *value*, which is the defined + // Node after skipping all copies. + value.map(nreg,val); + } + } else if( !may_be_copy_of_callee(n) && regnd[nreg]->outcnt() != 0 ) { + assert( n->is_Copy(), "" ); + n->replace_by(regnd[nreg]); + j -= yank_if_dead(n,b,&value,®nd); + } + } else { + // If the value occupies a register pair, record same info + // in both registers. + OptoReg::Name nreg_lo = OptoReg::add(nreg,-1); + if( RegMask::can_represent(nreg_lo) && // Either a spill slot, or + !lrgs(lidx).mask().Member(nreg_lo) ) { // Nearly always adjacent + // Sparc occasionally has non-adjacent pairs. + // Find the actual other value + RegMask tmp = lrgs(lidx).mask(); + tmp.Remove(nreg); + nreg_lo = tmp.find_first_elem(); + } + if( value[nreg] != val || value[nreg_lo] != val ) { + if (eliminate_copy_of_constant(n, b, value, regnd, nreg, nreg_lo)) { + n->replace_by(regnd[nreg]); + j -= yank_if_dead(n,b,&value,®nd); + } else { + regnd.map(nreg , n ); + regnd.map(nreg_lo, n ); + value.map(nreg ,val); + value.map(nreg_lo,val); + } + } else if( !may_be_copy_of_callee(n) && regnd[nreg]->outcnt() != 0 ) { + assert( n->is_Copy(), "" ); + n->replace_by(regnd[nreg]); + j -= yank_if_dead(n,b,&value,®nd); + } + } + + // Fat projections kill many registers + if( n_ideal_reg == MachProjNode::fat_proj ) { + RegMask rm = n->out_RegMask(); + // wow, what an expensive iterator... + nreg = rm.find_first_elem(); + while( OptoReg::is_valid(nreg)) { + rm.Remove(nreg); + value.map(nreg,n); + regnd.map(nreg,n); + nreg = rm.find_first_elem(); + } + } + + } // End of for all instructions in the block + + } // End for all blocks +} diff --git a/hotspot/src/share/vm/opto/reg_split.cpp b/hotspot/src/share/vm/opto/reg_split.cpp new file mode 100644 index 00000000000..5101eb2e75a --- /dev/null +++ b/hotspot/src/share/vm/opto/reg_split.cpp @@ -0,0 +1,1300 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_reg_split.cpp.incl" + +//------------------------------Split-------------------------------------- +// Walk the graph in RPO and for each lrg which spills, propogate reaching +// definitions. During propogation, split the live range around regions of +// High Register Pressure (HRP). If a Def is in a region of Low Register +// Pressure (LRP), it will not get spilled until we encounter a region of +// HRP between it and one of its uses. We will spill at the transition +// point between LRP and HRP. Uses in the HRP region will use the spilled +// Def. The first Use outside the HRP region will generate a SpillCopy to +// hoist the live range back up into a register, and all subsequent uses +// will use that new Def until another HRP region is encountered. Defs in +// HRP regions will get trailing SpillCopies to push the LRG down into the +// stack immediately. +// +// As a side effect, unlink from (hence make dead) coalesced copies. +// + +static const char out_of_nodes[] = "out of nodes during split"; + +//------------------------------get_spillcopy_wide----------------------------- +// Get a SpillCopy node with wide-enough masks. Use the 'wide-mask', the +// wide ideal-register spill-mask if possible. If the 'wide-mask' does +// not cover the input (or output), use the input (or output) mask instead. +Node *PhaseChaitin::get_spillcopy_wide( Node *def, Node *use, uint uidx ) { + // If ideal reg doesn't exist we've got a bad schedule happening + // that is forcing us to spill something that isn't spillable. + // Bail rather than abort + int ireg = def->ideal_reg(); + if( ireg == 0 || ireg == Op_RegFlags ) { + C->record_method_not_compilable("attempted to spill a non-spillable item"); + return NULL; + } + if (C->check_node_count(NodeLimitFudgeFactor, out_of_nodes)) { + return NULL; + } + const RegMask *i_mask = &def->out_RegMask(); + const RegMask *w_mask = C->matcher()->idealreg2spillmask[ireg]; + const RegMask *o_mask = use ? &use->in_RegMask(uidx) : w_mask; + const RegMask *w_i_mask = w_mask->overlap( *i_mask ) ? w_mask : i_mask; + const RegMask *w_o_mask; + + if( w_mask->overlap( *o_mask ) && // Overlap AND + ((ireg != Op_RegL && ireg != Op_RegD // Single use or aligned +#ifdef _LP64 + && ireg != Op_RegP +#endif + ) || o_mask->is_aligned_Pairs()) ) { + // Don't come here for mis-aligned doubles + w_o_mask = w_mask; + } else { // wide ideal mask does not overlap with o_mask + // Mis-aligned doubles come here and XMM->FPR moves on x86. + w_o_mask = o_mask; // Must target desired registers + // Does the ideal-reg-mask overlap with o_mask? I.e., can I use + // a reg-reg move or do I need a trip across register classes + // (and thus through memory)? + if( !C->matcher()->idealreg2regmask[ireg]->overlap( *o_mask) && o_mask->is_UP() ) + // Here we assume a trip through memory is required. + w_i_mask = &C->FIRST_STACK_mask(); + } + return new (C) MachSpillCopyNode( def, *w_i_mask, *w_o_mask ); +} + +//------------------------------insert_proj------------------------------------ +// Insert the spill at chosen location. Skip over any interveneing Proj's or +// Phis. Skip over a CatchNode and projs, inserting in the fall-through block +// instead. Update high-pressure indices. Create a new live range. +void PhaseChaitin::insert_proj( Block *b, uint i, Node *spill, uint maxlrg ) { + // Skip intervening ProjNodes. Do not insert between a ProjNode and + // its definer. + while( i < b->_nodes.size() && + (b->_nodes[i]->is_Proj() || + b->_nodes[i]->is_Phi() ) ) + i++; + + // Do not insert between a call and his Catch + if( b->_nodes[i]->is_Catch() ) { + // Put the instruction at the top of the fall-thru block. + // Find the fall-thru projection + while( 1 ) { + const CatchProjNode *cp = b->_nodes[++i]->as_CatchProj(); + if( cp->_con == CatchProjNode::fall_through_index ) + break; + } + int sidx = i - b->end_idx()-1; + b = b->_succs[sidx]; // Switch to successor block + i = 1; // Right at start of block + } + + b->_nodes.insert(i,spill); // Insert node in block + _cfg._bbs.map(spill->_idx,b); // Update node->block mapping to reflect + // Adjust the point where we go hi-pressure + if( i <= b->_ihrp_index ) b->_ihrp_index++; + if( i <= b->_fhrp_index ) b->_fhrp_index++; + + // Assign a new Live Range Number to the SpillCopy and grow + // the node->live range mapping. + new_lrg(spill,maxlrg); +} + +//------------------------------split_DEF-------------------------------------- +// There are four catagories of Split; UP/DOWN x DEF/USE +// Only three of these really occur as DOWN/USE will always color +// Any Split with a DEF cannot CISC-Spill now. Thus we need +// two helper routines, one for Split DEFS (insert after instruction), +// one for Split USES (insert before instruction). DEF insertion +// happens inside Split, where the Leaveblock array is updated. +uint PhaseChaitin::split_DEF( Node *def, Block *b, int loc, uint maxlrg, Node **Reachblock, Node **debug_defs, GrowableArray splits, int slidx ) { +#ifdef ASSERT + // Increment the counter for this lrg + splits.at_put(slidx, splits.at(slidx)+1); +#endif + // If we are spilling the memory op for an implicit null check, at the + // null check location (ie - null check is in HRP block) we need to do + // the null-check first, then spill-down in the following block. + // (The implicit_null_check function ensures the use is also dominated + // by the branch-not-taken block.) + Node *be = b->end(); + if( be->is_MachNullCheck() && be->in(1) == def && def == b->_nodes[loc] ) { + // Spill goes in the branch-not-taken block + b = b->_succs[b->_nodes[b->end_idx()+1]->Opcode() == Op_IfTrue]; + loc = 0; // Just past the Region + } + assert( loc >= 0, "must insert past block head" ); + + // Get a def-side SpillCopy + Node *spill = get_spillcopy_wide(def,NULL,0); + // Did we fail to split?, then bail + if (!spill) { + return 0; + } + + // Insert the spill at chosen location + insert_proj( b, loc+1, spill, maxlrg++); + + // Insert new node into Reaches array + Reachblock[slidx] = spill; + // Update debug list of reaching down definitions by adding this one + debug_defs[slidx] = spill; + + // return updated count of live ranges + return maxlrg; +} + +//------------------------------split_USE-------------------------------------- +// Splits at uses can involve redeffing the LRG, so no CISC Spilling there. +// Debug uses want to know if def is already stack enabled. +uint PhaseChaitin::split_USE( Node *def, Block *b, Node *use, uint useidx, uint maxlrg, bool def_down, bool cisc_sp, GrowableArray splits, int slidx ) { +#ifdef ASSERT + // Increment the counter for this lrg + splits.at_put(slidx, splits.at(slidx)+1); +#endif + + // Some setup stuff for handling debug node uses + JVMState* jvms = use->jvms(); + uint debug_start = jvms ? jvms->debug_start() : 999999; + uint debug_end = jvms ? jvms->debug_end() : 999999; + + //------------------------------------------- + // Check for use of debug info + if (useidx >= debug_start && useidx < debug_end) { + // Actually it's perfectly legal for constant debug info to appear + // just unlikely. In this case the optimizer left a ConI of a 4 + // as both inputs to a Phi with only a debug use. It's a single-def + // live range of a rematerializable value. The live range spills, + // rematerializes and now the ConI directly feeds into the debug info. + // assert(!def->is_Con(), "constant debug info already constructed directly"); + + // Special split handling for Debug Info + // If DEF is DOWN, just hook the edge and return + // If DEF is UP, Split it DOWN for this USE. + if( def->is_Mach() ) { + if( def_down ) { + // DEF is DOWN, so connect USE directly to the DEF + use->set_req(useidx, def); + } else { + // Block and index where the use occurs. + Block *b = _cfg._bbs[use->_idx]; + // Put the clone just prior to use + int bindex = b->find_node(use); + // DEF is UP, so must copy it DOWN and hook in USE + // Insert SpillCopy before the USE, which uses DEF as its input, + // and defs a new live range, which is used by this node. + Node *spill = get_spillcopy_wide(def,use,useidx); + // did we fail to split? + if (!spill) { + // Bail + return 0; + } + // insert into basic block + insert_proj( b, bindex, spill, maxlrg++ ); + // Use the new split + use->set_req(useidx,spill); + } + // No further split handling needed for this use + return maxlrg; + } // End special splitting for debug info live range + } // If debug info + + // CISC-SPILLING + // Finally, check to see if USE is CISC-Spillable, and if so, + // gather_lrg_masks will add the flags bit to its mask, and + // no use side copy is needed. This frees up the live range + // register choices without causing copy coalescing, etc. + if( UseCISCSpill && cisc_sp ) { + int inp = use->cisc_operand(); + if( inp != AdlcVMDeps::Not_cisc_spillable ) + // Convert operand number to edge index number + inp = use->as_Mach()->operand_index(inp); + if( inp == (int)useidx ) { + use->set_req(useidx, def); +#ifndef PRODUCT + if( TraceCISCSpill ) { + tty->print(" set_split: "); + use->dump(); + } +#endif + return maxlrg; + } + } + + //------------------------------------------- + // Insert a Copy before the use + + // Block and index where the use occurs. + int bindex; + // Phi input spill-copys belong at the end of the prior block + if( use->is_Phi() ) { + b = _cfg._bbs[b->pred(useidx)->_idx]; + bindex = b->end_idx(); + } else { + // Put the clone just prior to use + bindex = b->find_node(use); + } + + Node *spill = get_spillcopy_wide( def, use, useidx ); + if( !spill ) return 0; // Bailed out + // Insert SpillCopy before the USE, which uses the reaching DEF as + // its input, and defs a new live range, which is used by this node. + insert_proj( b, bindex, spill, maxlrg++ ); + // Use the spill/clone + use->set_req(useidx,spill); + + // return updated live range count + return maxlrg; +} + +//------------------------------split_Rematerialize---------------------------- +// Clone a local copy of the def. +Node *PhaseChaitin::split_Rematerialize( Node *def, Block *b, uint insidx, uint &maxlrg, GrowableArray splits, int slidx, uint *lrg2reach, Node **Reachblock, bool walkThru ) { + // The input live ranges will be stretched to the site of the new + // instruction. They might be stretched past a def and will thus + // have the old and new values of the same live range alive at the + // same time - a definite no-no. Split out private copies of + // the inputs. + if( def->req() > 1 ) { + for( uint i = 1; i < def->req(); i++ ) { + Node *in = def->in(i); + // Check for single-def (LRG cannot redefined) + uint lidx = n2lidx(in); + if( lidx >= _maxlrg ) continue; // Value is a recent spill-copy + if( lrgs(lidx)._def != NodeSentinel ) continue; + + Block *b_def = _cfg._bbs[def->_idx]; + int idx_def = b_def->find_node(def); + Node *in_spill = get_spillcopy_wide( in, def, i ); + if( !in_spill ) return 0; // Bailed out + insert_proj(b_def,idx_def,in_spill,maxlrg++); + if( b_def == b ) + insidx++; + def->set_req(i,in_spill); + } + } + + Node *spill = def->clone(); + if (C->check_node_count(NodeLimitFudgeFactor, out_of_nodes)) { + // Check when generating nodes + return 0; + } + + // See if any inputs are currently being spilled, and take the + // latest copy of spilled inputs. + if( spill->req() > 1 ) { + for( uint i = 1; i < spill->req(); i++ ) { + Node *in = spill->in(i); + uint lidx = Find_id(in); + + // Walk backwards thru spill copy node intermediates + if( walkThru ) + while ( in->is_SpillCopy() && lidx >= _maxlrg ) { + in = in->in(1); + lidx = Find_id(in); + } + + if( lidx < _maxlrg && lrgs(lidx).reg() >= LRG::SPILL_REG ) { + Node *rdef = Reachblock[lrg2reach[lidx]]; + if( rdef ) spill->set_req(i,rdef); + } + } + } + + + assert( spill->out_RegMask().is_UP(), "rematerialize to a reg" ); + // Rematerialized op is def->spilled+1 + set_was_spilled(spill); + if( _spilled_once.test(def->_idx) ) + set_was_spilled(spill); + + insert_proj( b, insidx, spill, maxlrg++ ); +#ifdef ASSERT + // Increment the counter for this lrg + splits.at_put(slidx, splits.at(slidx)+1); +#endif + // See if the cloned def kills any flags, and copy those kills as well + uint i = insidx+1; + if( clone_projs( b, i, def, spill, maxlrg ) ) { + // Adjust the point where we go hi-pressure + if( i <= b->_ihrp_index ) b->_ihrp_index++; + if( i <= b->_fhrp_index ) b->_fhrp_index++; + } + + return spill; +} + +//------------------------------is_high_pressure------------------------------- +// Function to compute whether or not this live range is "high pressure" +// in this block - whether it spills eagerly or not. +bool PhaseChaitin::is_high_pressure( Block *b, LRG *lrg, uint insidx ) { + if( lrg->_was_spilled1 ) return true; + // Forced spilling due to conflict? Then split only at binding uses + // or defs, not for supposed capacity problems. + // CNC - Turned off 7/8/99, causes too much spilling + // if( lrg->_is_bound ) return false; + + // Not yet reached the high-pressure cutoff point, so low pressure + uint hrp_idx = lrg->_is_float ? b->_fhrp_index : b->_ihrp_index; + if( insidx < hrp_idx ) return false; + // Register pressure for the block as a whole depends on reg class + int block_pres = lrg->_is_float ? b->_freg_pressure : b->_reg_pressure; + // Bound live ranges will split at the binding points first; + // Intermediate splits should assume the live range's register set + // got "freed up" and that num_regs will become INT_PRESSURE. + int bound_pres = lrg->_is_float ? FLOATPRESSURE : INTPRESSURE; + // Effective register pressure limit. + int lrg_pres = (lrg->get_invalid_mask_size() > lrg->num_regs()) + ? (lrg->get_invalid_mask_size() >> (lrg->num_regs()-1)) : bound_pres; + // High pressure if block pressure requires more register freedom + // than live range has. + return block_pres >= lrg_pres; +} + + +//------------------------------prompt_use--------------------------------- +// True if lidx is used before any real register is def'd in the block +bool PhaseChaitin::prompt_use( Block *b, uint lidx ) { + if( lrgs(lidx)._was_spilled2 ) return false; + + // Scan block for 1st use. + for( uint i = 1; i <= b->end_idx(); i++ ) { + Node *n = b->_nodes[i]; + // Ignore PHI use, these can be up or down + if( n->is_Phi() ) continue; + for( uint j = 1; j < n->req(); j++ ) + if( Find_id(n->in(j)) == lidx ) + return true; // Found 1st use! + if( n->out_RegMask().is_NotEmpty() ) return false; + } + return false; +} + +//------------------------------Split-------------------------------------- +//----------Split Routine---------- +// ***** NEW SPLITTING HEURISTIC ***** +// DEFS: If the DEF is in a High Register Pressure(HRP) Block, split there. +// Else, no split unless there is a HRP block between a DEF and +// one of its uses, and then split at the HRP block. +// +// USES: If USE is in HRP, split at use to leave main LRG on stack. +// Else, hoist LRG back up to register only (ie - split is also DEF) +// We will compute a new maxlrg as we go +uint PhaseChaitin::Split( uint maxlrg ) { + NOT_PRODUCT( Compile::TracePhase t3("regAllocSplit", &_t_regAllocSplit, TimeCompiler); ) + + uint bidx, pidx, slidx, insidx, inpidx, twoidx; + uint non_phi = 1, spill_cnt = 0; + Node **Reachblock; + Node *n1, *n2, *n3; + Node_List *defs,*phis; + bool *UPblock; + bool u1, u2, u3; + Block *b, *pred; + PhiNode *phi; + GrowableArray lidxs; + + // Array of counters to count splits per live range + GrowableArray splits; + + //----------Setup Code---------- + // Create a convenient mapping from lrg numbers to reaches/leaves indices + uint *lrg2reach = NEW_RESOURCE_ARRAY( uint, _maxlrg ); + // Keep track of DEFS & Phis for later passes + defs = new Node_List(); + phis = new Node_List(); + // Gather info on which LRG's are spilling, and build maps + for( bidx = 1; bidx < _maxlrg; bidx++ ) { + if( lrgs(bidx).alive() && lrgs(bidx).reg() >= LRG::SPILL_REG ) { + assert(!lrgs(bidx).mask().is_AllStack(),"AllStack should color"); + lrg2reach[bidx] = spill_cnt; + spill_cnt++; + lidxs.append(bidx); +#ifdef ASSERT + // Initialize the split counts to zero + splits.append(0); +#endif +#ifndef PRODUCT + if( PrintOpto && WizardMode && lrgs(bidx)._was_spilled1 ) + tty->print_cr("Warning, 2nd spill of L%d",bidx); +#endif + } + } + + // Create side arrays for propagating reaching defs info. + // Each block needs a node pointer for each spilling live range for the + // Def which is live into the block. Phi nodes handle multiple input + // Defs by querying the output of their predecessor blocks and resolving + // them to a single Def at the phi. The pointer is updated for each + // Def in the block, and then becomes the output for the block when + // processing of the block is complete. We also need to track whether + // a Def is UP or DOWN. UP means that it should get a register (ie - + // it is always in LRP regions), and DOWN means that it is probably + // on the stack (ie - it crosses HRP regions). + Node ***Reaches = NEW_RESOURCE_ARRAY( Node**, _cfg._num_blocks+1 ); + bool **UP = NEW_RESOURCE_ARRAY( bool*, _cfg._num_blocks+1 ); + Node **debug_defs = NEW_RESOURCE_ARRAY( Node*, spill_cnt ); + VectorSet **UP_entry= NEW_RESOURCE_ARRAY( VectorSet*, spill_cnt ); + + // Initialize Reaches & UP + for( bidx = 0; bidx < _cfg._num_blocks+1; bidx++ ) { + Reaches[bidx] = NEW_RESOURCE_ARRAY( Node*, spill_cnt ); + UP[bidx] = NEW_RESOURCE_ARRAY( bool, spill_cnt ); + Node **Reachblock = Reaches[bidx]; + bool *UPblock = UP[bidx]; + for( slidx = 0; slidx < spill_cnt; slidx++ ) { + UPblock[slidx] = true; // Assume they start in registers + Reachblock[slidx] = NULL; // Assume that no def is present + } + } + + // Initialize to array of empty vectorsets + for( slidx = 0; slidx < spill_cnt; slidx++ ) + UP_entry[slidx] = new VectorSet(Thread::current()->resource_area()); + + //----------PASS 1---------- + //----------Propagation & Node Insertion Code---------- + // Walk the Blocks in RPO for DEF & USE info + for( bidx = 0; bidx < _cfg._num_blocks; bidx++ ) { + + if (C->check_node_count(spill_cnt, out_of_nodes)) { + return 0; + } + + b = _cfg._blocks[bidx]; + // Reaches & UP arrays for this block + Reachblock = Reaches[b->_pre_order]; + UPblock = UP[b->_pre_order]; + // Reset counter of start of non-Phi nodes in block + non_phi = 1; + //----------Block Entry Handling---------- + // Check for need to insert a new phi + // Cycle through this block's predecessors, collecting Reaches + // info for each spilled LRG. If they are identical, no phi is + // needed. If they differ, check for a phi, and insert if missing, + // or update edges if present. Set current block's Reaches set to + // be either the phi's or the reaching def, as appropriate. + // If no Phi is needed, check if the LRG needs to spill on entry + // to the block due to HRP. + for( slidx = 0; slidx < spill_cnt; slidx++ ) { + // Grab the live range number + uint lidx = lidxs.at(slidx); + // Do not bother splitting or putting in Phis for single-def + // rematerialized live ranges. This happens alot to constants + // with long live ranges. + if( lrgs(lidx)._def != NodeSentinel && + lrgs(lidx)._def->rematerialize() ) { + // reset the Reaches & UP entries + Reachblock[slidx] = lrgs(lidx)._def; + UPblock[slidx] = true; + // Record following instruction in case 'n' rematerializes and + // kills flags + Block *pred1 = _cfg._bbs[b->pred(1)->_idx]; + continue; + } + + // Initialize needs_phi and needs_split + bool needs_phi = false; + bool needs_split = false; + // Walk the predecessor blocks to check inputs for that live range + // Grab predecessor block header + n1 = b->pred(1); + // Grab the appropriate reaching def info for inpidx + pred = _cfg._bbs[n1->_idx]; + pidx = pred->_pre_order; + Node **Ltmp = Reaches[pidx]; + bool *Utmp = UP[pidx]; + n1 = Ltmp[slidx]; + u1 = Utmp[slidx]; + // Initialize node for saving type info + n3 = n1; + u3 = u1; + + // Compare inputs to see if a Phi is needed + for( inpidx = 2; inpidx < b->num_preds(); inpidx++ ) { + // Grab predecessor block headers + n2 = b->pred(inpidx); + // Grab the appropriate reaching def info for inpidx + pred = _cfg._bbs[n2->_idx]; + pidx = pred->_pre_order; + Ltmp = Reaches[pidx]; + Utmp = UP[pidx]; + n2 = Ltmp[slidx]; + u2 = Utmp[slidx]; + // For each LRG, decide if a phi is necessary + if( n1 != n2 ) { + needs_phi = true; + } + // See if the phi has mismatched inputs, UP vs. DOWN + if( n1 && n2 && (u1 != u2) ) { + needs_split = true; + } + // Move n2/u2 to n1/u1 for next iteration + n1 = n2; + u1 = u2; + // Preserve a non-NULL predecessor for later type referencing + if( (n3 == NULL) && (n2 != NULL) ){ + n3 = n2; + u3 = u2; + } + } // End for all potential Phi inputs + + // If a phi is needed, check for it + if( needs_phi ) { + // check block for appropriate phinode & update edges + for( insidx = 1; insidx <= b->end_idx(); insidx++ ) { + n1 = b->_nodes[insidx]; + // bail if this is not a phi + phi = n1->is_Phi() ? n1->as_Phi() : NULL; + if( phi == NULL ) { + // Keep track of index of first non-PhiNode instruction in block + non_phi = insidx; + // break out of the for loop as we have handled all phi nodes + break; + } + // must be looking at a phi + if( Find_id(n1) == lidxs.at(slidx) ) { + // found the necessary phi + needs_phi = false; + // initialize the Reaches entry for this LRG + Reachblock[slidx] = phi; + break; + } // end if found correct phi + } // end for all phi's + // add new phinode if one not already found + if( needs_phi ) { + // create a new phi node and insert it into the block + // type is taken from left over pointer to a predecessor + assert(n3,"No non-NULL reaching DEF for a Phi"); + phi = new (C, b->num_preds()) PhiNode(b->head(), n3->bottom_type()); + // initialize the Reaches entry for this LRG + Reachblock[slidx] = phi; + + // add node to block & node_to_block mapping + insert_proj( b, insidx++, phi, maxlrg++ ); + non_phi++; + // Reset new phi's mapping to be the spilling live range + _names.map(phi->_idx, lidx); + assert(Find_id(phi) == lidx,"Bad update on Union-Find mapping"); + } // end if not found correct phi + // Here you have either found or created the Phi, so record it + assert(phi != NULL,"Must have a Phi Node here"); + phis->push(phi); + // PhiNodes should either force the LRG UP or DOWN depending + // on its inputs and the register pressure in the Phi's block. + UPblock[slidx] = true; // Assume new DEF is UP + // If entering a high-pressure area with no immediate use, + // assume Phi is DOWN + if( is_high_pressure( b, &lrgs(lidx), b->end_idx()) && !prompt_use(b,lidx) ) + UPblock[slidx] = false; + // If we are not split up/down and all inputs are down, then we + // are down + if( !needs_split && !u3 ) + UPblock[slidx] = false; + } // end if phi is needed + + // Do not need a phi, so grab the reaching DEF + else { + // Grab predecessor block header + n1 = b->pred(1); + // Grab the appropriate reaching def info for k + pred = _cfg._bbs[n1->_idx]; + pidx = pred->_pre_order; + Node **Ltmp = Reaches[pidx]; + bool *Utmp = UP[pidx]; + // reset the Reaches & UP entries + Reachblock[slidx] = Ltmp[slidx]; + UPblock[slidx] = Utmp[slidx]; + } // end else no Phi is needed + } // end for all spilling live ranges + // DEBUG +#ifndef PRODUCT + if(trace_spilling()) { + tty->print("/`\nBlock %d: ", b->_pre_order); + tty->print("Reaching Definitions after Phi handling\n"); + for( uint x = 0; x < spill_cnt; x++ ) { + tty->print("Spill Idx %d: UP %d: Node\n",x,UPblock[x]); + if( Reachblock[x] ) + Reachblock[x]->dump(); + else + tty->print("Undefined\n"); + } + } +#endif + + //----------Non-Phi Node Splitting---------- + // Since phi-nodes have now been handled, the Reachblock array for this + // block is initialized with the correct starting value for the defs which + // reach non-phi instructions in this block. Thus, process non-phi + // instructions normally, inserting SpillCopy nodes for all spill + // locations. + + // Memoize any DOWN reaching definitions for use as DEBUG info + for( insidx = 0; insidx < spill_cnt; insidx++ ) { + debug_defs[insidx] = (UPblock[insidx]) ? NULL : Reachblock[insidx]; + if( UPblock[insidx] ) // Memoize UP decision at block start + UP_entry[insidx]->set( b->_pre_order ); + } + + //----------Walk Instructions in the Block and Split---------- + // For all non-phi instructions in the block + for( insidx = 1; insidx <= b->end_idx(); insidx++ ) { + Node *n = b->_nodes[insidx]; + // Find the defining Node's live range index + uint defidx = Find_id(n); + uint cnt = n->req(); + + if( n->is_Phi() ) { + // Skip phi nodes after removing dead copies. + if( defidx < _maxlrg ) { + // Check for useless Phis. These appear if we spill, then + // coalesce away copies. Dont touch Phis in spilling live + // ranges; they are busy getting modifed in this pass. + if( lrgs(defidx).reg() < LRG::SPILL_REG ) { + uint i; + Node *u = NULL; + // Look for the Phi merging 2 unique inputs + for( i = 1; i < cnt; i++ ) { + // Ignore repeats and self + if( n->in(i) != u && n->in(i) != n ) { + // Found a unique input + if( u != NULL ) // If it's the 2nd, bail out + break; + u = n->in(i); // Else record it + } + } + assert( u, "at least 1 valid input expected" ); + if( i >= cnt ) { // Didn't find 2+ unique inputs? + n->replace_by(u); // Then replace with unique input + n->disconnect_inputs(NULL); + b->_nodes.remove(insidx); + insidx--; + b->_ihrp_index--; + b->_fhrp_index--; + } + } + } + continue; + } + assert( insidx > b->_ihrp_index || + (b->_reg_pressure < (uint)INTPRESSURE) || + b->_ihrp_index > 4000000 || + b->_ihrp_index >= b->end_idx() || + !b->_nodes[b->_ihrp_index]->is_Proj(), "" ); + assert( insidx > b->_fhrp_index || + (b->_freg_pressure < (uint)FLOATPRESSURE) || + b->_fhrp_index > 4000000 || + b->_fhrp_index >= b->end_idx() || + !b->_nodes[b->_fhrp_index]->is_Proj(), "" ); + + // ********** Handle Crossing HRP Boundry ********** + if( (insidx == b->_ihrp_index) || (insidx == b->_fhrp_index) ) { + for( slidx = 0; slidx < spill_cnt; slidx++ ) { + // Check for need to split at HRP boundry - split if UP + n1 = Reachblock[slidx]; + // bail out if no reaching DEF + if( n1 == NULL ) continue; + // bail out if live range is 'isolated' around inner loop + uint lidx = lidxs.at(slidx); + // If live range is currently UP + if( UPblock[slidx] ) { + // set location to insert spills at + // SPLIT DOWN HERE - NO CISC SPILL + if( is_high_pressure( b, &lrgs(lidx), insidx ) && + !n1->rematerialize() ) { + // If there is already a valid stack definition available, use it + if( debug_defs[slidx] != NULL ) { + Reachblock[slidx] = debug_defs[slidx]; + } + else { + // Insert point is just past last use or def in the block + int insert_point = insidx-1; + while( insert_point > 0 ) { + Node *n = b->_nodes[insert_point]; + // Hit top of block? Quit going backwards + if( n->is_Phi() ) break; + // Found a def? Better split after it. + if( n2lidx(n) == lidx ) break; + // Look for a use + uint i; + for( i = 1; i < n->req(); i++ ) + if( n2lidx(n->in(i)) == lidx ) + break; + // Found a use? Better split after it. + if( i < n->req() ) break; + insert_point--; + } + maxlrg = split_DEF( n1, b, insert_point, maxlrg, Reachblock, debug_defs, splits, slidx); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + insidx++; + } + // This is a new DEF, so update UP + UPblock[slidx] = false; +#ifndef PRODUCT + // DEBUG + if( trace_spilling() ) { + tty->print("\nNew Split DOWN DEF of Spill Idx "); + tty->print("%d, UP %d:\n",slidx,false); + n1->dump(); + } +#endif + } + } // end if LRG is UP + } // end for all spilling live ranges + assert( b->_nodes[insidx] == n, "got insidx set incorrectly" ); + } // end if crossing HRP Boundry + + // If the LRG index is oob, then this is a new spillcopy, skip it. + if( defidx >= _maxlrg ) { + continue; + } + LRG &deflrg = lrgs(defidx); + uint copyidx = n->is_Copy(); + // Remove coalesced copy from CFG + if( copyidx && defidx == n2lidx(n->in(copyidx)) ) { + n->replace_by( n->in(copyidx) ); + n->set_req( copyidx, NULL ); + b->_nodes.remove(insidx--); + b->_ihrp_index--; // Adjust the point where we go hi-pressure + b->_fhrp_index--; + continue; + } + +#define DERIVED 0 + + // ********** Handle USES ********** + bool nullcheck = false; + // Implicit null checks never use the spilled value + if( n->is_MachNullCheck() ) + nullcheck = true; + if( !nullcheck ) { + // Search all inputs for a Spill-USE + JVMState* jvms = n->jvms(); + uint oopoff = jvms ? jvms->oopoff() : cnt; + uint old_last = cnt - 1; + for( inpidx = 1; inpidx < cnt; inpidx++ ) { + // Derived/base pairs may be added to our inputs during this loop. + // If inpidx > old_last, then one of these new inputs is being + // handled. Skip the derived part of the pair, but process + // the base like any other input. + if( inpidx > old_last && ((inpidx - oopoff) & 1) == DERIVED ) { + continue; // skip derived_debug added below + } + // Get lidx of input + uint useidx = Find_id(n->in(inpidx)); + // Not a brand-new split, and it is a spill use + if( useidx < _maxlrg && lrgs(useidx).reg() >= LRG::SPILL_REG ) { + // Check for valid reaching DEF + slidx = lrg2reach[useidx]; + Node *def = Reachblock[slidx]; + assert( def != NULL, "Using Undefined Value in Split()\n"); + + // (+++) %%%% remove this in favor of pre-pass in matcher.cpp + // monitor references do not care where they live, so just hook + if ( jvms && jvms->is_monitor_use(inpidx) ) { + // The effect of this clone is to drop the node out of the block, + // so that the allocator does not see it anymore, and therefore + // does not attempt to assign it a register. + def = def->clone(); + _names.extend(def->_idx,0); + _cfg._bbs.map(def->_idx,b); + n->set_req(inpidx, def); + if (C->check_node_count(NodeLimitFudgeFactor, out_of_nodes)) { + return 0; + } + continue; + } + + // Rematerializable? Then clone def at use site instead + // of store/load + if( def->rematerialize() ) { + int old_size = b->_nodes.size(); + def = split_Rematerialize( def, b, insidx, maxlrg, splits, slidx, lrg2reach, Reachblock, true ); + if( !def ) return 0; // Bail out + insidx += b->_nodes.size()-old_size; + } + + MachNode *mach = n->is_Mach() ? n->as_Mach() : NULL; + // Base pointers and oopmap references do not care where they live. + if ((inpidx >= oopoff) || + (mach && mach->ideal_Opcode() == Op_AddP && inpidx == AddPNode::Base)) { + if (def->rematerialize() && lrgs(useidx)._was_spilled2) { + // This def has been rematerialized a couple of times without + // progress. It doesn't care if it lives UP or DOWN, so + // spill it down now. + maxlrg = split_USE(def,b,n,inpidx,maxlrg,false,false,splits,slidx); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + insidx++; // Reset iterator to skip USE side split + } else { + // Just hook the def edge + n->set_req(inpidx, def); + } + + if (inpidx >= oopoff) { + // After oopoff, we have derived/base pairs. We must mention all + // derived pointers here as derived/base pairs for GC. If the + // derived value is spilling and we have a copy both in Reachblock + // (called here 'def') and debug_defs[slidx] we need to mention + // both in derived/base pairs or kill one. + Node *derived_debug = debug_defs[slidx]; + if( ((inpidx - oopoff) & 1) == DERIVED && // derived vs base? + mach && mach->ideal_Opcode() != Op_Halt && + derived_debug != NULL && + derived_debug != def ) { // Actual 2nd value appears + // We have already set 'def' as a derived value. + // Also set debug_defs[slidx] as a derived value. + uint k; + for( k = oopoff; k < cnt; k += 2 ) + if( n->in(k) == derived_debug ) + break; // Found an instance of debug derived + if( k == cnt ) {// No instance of debug_defs[slidx] + // Add a derived/base pair to cover the debug info. + // We have to process the added base later since it is not + // handled yet at this point but skip derived part. + assert(((n->req() - oopoff) & 1) == DERIVED, + "must match skip condition above"); + n->add_req( derived_debug ); // this will be skipped above + n->add_req( n->in(inpidx+1) ); // this will be processed + // Increment cnt to handle added input edges on + // subsequent iterations. + cnt += 2; + } + } + } + continue; + } + // Special logic for DEBUG info + if( jvms && b->_freq > BLOCK_FREQUENCY(0.5) ) { + uint debug_start = jvms->debug_start(); + // If this is debug info use & there is a reaching DOWN def + if ((debug_start <= inpidx) && (debug_defs[slidx] != NULL)) { + assert(inpidx < oopoff, "handle only debug info here"); + // Just hook it in & move on + n->set_req(inpidx, debug_defs[slidx]); + // (Note that this can make two sides of a split live at the + // same time: The debug def on stack, and another def in a + // register. The GC needs to know about both of them, but any + // derived pointers after oopoff will refer to only one of the + // two defs and the GC would therefore miss the other. Thus + // this hack is only allowed for debug info which is Java state + // and therefore never a derived pointer.) + continue; + } + } + // Grab register mask info + const RegMask &dmask = def->out_RegMask(); + const RegMask &umask = n->in_RegMask(inpidx); + + assert(inpidx < oopoff, "cannot use-split oop map info"); + + bool dup = UPblock[slidx]; + bool uup = umask.is_UP(); + + // Need special logic to handle bound USES. Insert a split at this + // bound use if we can't rematerialize the def, or if we need the + // split to form a misaligned pair. + if( !umask.is_AllStack() && + (int)umask.Size() <= lrgs(useidx).num_regs() && + (!def->rematerialize() || + umask.is_misaligned_Pair())) { + // These need a Split regardless of overlap or pressure + // SPLIT - NO DEF - NO CISC SPILL + maxlrg = split_USE(def,b,n,inpidx,maxlrg,dup,false, splits,slidx); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + insidx++; // Reset iterator to skip USE side split + continue; + } + // Here is the logic chart which describes USE Splitting: + // 0 = false or DOWN, 1 = true or UP + // + // Overlap | DEF | USE | Action + //------------------------------------------------------- + // 0 | 0 | 0 | Copy - mem -> mem + // 0 | 0 | 1 | Split-UP - Check HRP + // 0 | 1 | 0 | Split-DOWN - Debug Info? + // 0 | 1 | 1 | Copy - reg -> reg + // 1 | 0 | 0 | Reset Input Edge (no Split) + // 1 | 0 | 1 | Split-UP - Check HRP + // 1 | 1 | 0 | Split-DOWN - Debug Info? + // 1 | 1 | 1 | Reset Input Edge (no Split) + // + // So, if (dup == uup), then overlap test determines action, + // with true being no split, and false being copy. Else, + // if DEF is DOWN, Split-UP, and check HRP to decide on + // resetting DEF. Finally if DEF is UP, Split-DOWN, with + // special handling for Debug Info. + if( dup == uup ) { + if( dmask.overlap(umask) ) { + // Both are either up or down, and there is overlap, No Split + n->set_req(inpidx, def); + } + else { // Both are either up or down, and there is no overlap + if( dup ) { // If UP, reg->reg copy + // COPY ACROSS HERE - NO DEF - NO CISC SPILL + maxlrg = split_USE(def,b,n,inpidx,maxlrg,false,false, splits,slidx); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + insidx++; // Reset iterator to skip USE side split + } + else { // DOWN, mem->mem copy + // COPY UP & DOWN HERE - NO DEF - NO CISC SPILL + // First Split-UP to move value into Register + uint def_ideal = def->ideal_reg(); + const RegMask* tmp_rm = Matcher::idealreg2regmask[def_ideal]; + Node *spill = new (C) MachSpillCopyNode(def, dmask, *tmp_rm); + insert_proj( b, insidx, spill, maxlrg ); + // Then Split-DOWN as if previous Split was DEF + maxlrg = split_USE(spill,b,n,inpidx,maxlrg,false,false, splits,slidx); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + insidx += 2; // Reset iterator to skip USE side splits + } + } // End else no overlap + } // End if dup == uup + // dup != uup, so check dup for direction of Split + else { + if( dup ) { // If UP, Split-DOWN and check Debug Info + // If this node is already a SpillCopy, just patch the edge + // except the case of spilling to stack. + if( n->is_SpillCopy() ) { + RegMask tmp_rm(umask); + tmp_rm.SUBTRACT(Matcher::STACK_ONLY_mask); + if( dmask.overlap(tmp_rm) ) { + if( def != n->in(inpidx) ) { + n->set_req(inpidx, def); + } + continue; + } + } + // COPY DOWN HERE - NO DEF - NO CISC SPILL + maxlrg = split_USE(def,b,n,inpidx,maxlrg,false,false, splits,slidx); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + insidx++; // Reset iterator to skip USE side split + // Check for debug-info split. Capture it for later + // debug splits of the same value + if (jvms && jvms->debug_start() <= inpidx && inpidx < oopoff) + debug_defs[slidx] = n->in(inpidx); + + } + else { // DOWN, Split-UP and check register pressure + if( is_high_pressure( b, &lrgs(useidx), insidx ) ) { + // COPY UP HERE - NO DEF - CISC SPILL + maxlrg = split_USE(def,b,n,inpidx,maxlrg,true,true, splits,slidx); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + insidx++; // Reset iterator to skip USE side split + } else { // LRP + // COPY UP HERE - WITH DEF - NO CISC SPILL + maxlrg = split_USE(def,b,n,inpidx,maxlrg,true,false, splits,slidx); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + // Flag this lift-up in a low-pressure block as + // already-spilled, so if it spills again it will + // spill hard (instead of not spilling hard and + // coalescing away). + set_was_spilled(n->in(inpidx)); + // Since this is a new DEF, update Reachblock & UP + Reachblock[slidx] = n->in(inpidx); + UPblock[slidx] = true; + insidx++; // Reset iterator to skip USE side split + } + } // End else DOWN + } // End dup != uup + } // End if Spill USE + } // End For All Inputs + } // End If not nullcheck + + // ********** Handle DEFS ********** + // DEFS either Split DOWN in HRP regions or when the LRG is bound, or + // just reset the Reaches info in LRP regions. DEFS must always update + // UP info. + if( deflrg.reg() >= LRG::SPILL_REG ) { // Spilled? + uint slidx = lrg2reach[defidx]; + // Add to defs list for later assignment of new live range number + defs->push(n); + // Set a flag on the Node indicating it has already spilled. + // Only do it for capacity spills not conflict spills. + if( !deflrg._direct_conflict ) + set_was_spilled(n); + assert(!n->is_Phi(),"Cannot insert Phi into DEFS list"); + // Grab UP info for DEF + const RegMask &dmask = n->out_RegMask(); + bool defup = dmask.is_UP(); + // Only split at Def if this is a HRP block or bound (and spilled once) + if( !n->rematerialize() && + (((dmask.is_bound1() || dmask.is_bound2() || dmask.is_misaligned_Pair()) && + (deflrg._direct_conflict || deflrg._must_spill)) || + // Check for LRG being up in a register and we are inside a high + // pressure area. Spill it down immediately. + (defup && is_high_pressure(b,&deflrg,insidx))) ) { + assert( !n->rematerialize(), "" ); + assert( !n->is_SpillCopy(), "" ); + // Do a split at the def site. + maxlrg = split_DEF( n, b, insidx, maxlrg, Reachblock, debug_defs, splits, slidx ); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + // Split DEF's Down + UPblock[slidx] = 0; +#ifndef PRODUCT + // DEBUG + if( trace_spilling() ) { + tty->print("\nNew Split DOWN DEF of Spill Idx "); + tty->print("%d, UP %d:\n",slidx,false); + n->dump(); + } +#endif + } + else { // Neither bound nor HRP, must be LRP + // otherwise, just record the def + Reachblock[slidx] = n; + // UP should come from the outRegmask() of the DEF + UPblock[slidx] = defup; + // Update debug list of reaching down definitions, kill if DEF is UP + debug_defs[slidx] = defup ? NULL : n; +#ifndef PRODUCT + // DEBUG + if( trace_spilling() ) { + tty->print("\nNew DEF of Spill Idx "); + tty->print("%d, UP %d:\n",slidx,defup); + n->dump(); + } +#endif + } // End else LRP + } // End if spill def + + // ********** Split Left Over Mem-Mem Moves ********** + // Check for mem-mem copies and split them now. Do not do this + // to copies about to be spilled; they will be Split shortly. + if( copyidx ) { + Node *use = n->in(copyidx); + uint useidx = Find_id(use); + if( useidx < _maxlrg && // This is not a new split + OptoReg::is_stack(deflrg.reg()) && + deflrg.reg() < LRG::SPILL_REG ) { // And DEF is from stack + LRG &uselrg = lrgs(useidx); + if( OptoReg::is_stack(uselrg.reg()) && + uselrg.reg() < LRG::SPILL_REG && // USE is from stack + deflrg.reg() != uselrg.reg() ) { // Not trivially removed + uint def_ideal_reg = Matcher::base2reg[n->bottom_type()->base()]; + const RegMask &def_rm = *Matcher::idealreg2regmask[def_ideal_reg]; + const RegMask &use_rm = n->in_RegMask(copyidx); + if( def_rm.overlap(use_rm) && n->is_SpillCopy() ) { // Bug 4707800, 'n' may be a storeSSL + if (C->check_node_count(NodeLimitFudgeFactor, out_of_nodes)) { // Check when generating nodes + return 0; + } + Node *spill = new (C) MachSpillCopyNode(use,use_rm,def_rm); + n->set_req(copyidx,spill); + n->as_MachSpillCopy()->set_in_RegMask(def_rm); + // Put the spill just before the copy + insert_proj( b, insidx++, spill, maxlrg++ ); + } + } + } + } + } // End For All Instructions in Block - Non-PHI Pass + + // Check if each LRG is live out of this block so as not to propagate + // beyond the last use of a LRG. + for( slidx = 0; slidx < spill_cnt; slidx++ ) { + uint defidx = lidxs.at(slidx); + IndexSet *liveout = _live->live(b); + if( !liveout->member(defidx) ) { +#ifdef ASSERT + // The index defidx is not live. Check the liveout array to ensure that + // it contains no members which compress to defidx. Finding such an + // instance may be a case to add liveout adjustment in compress_uf_map(). + // See 5063219. + uint member; + IndexSetIterator isi(liveout); + while ((member = isi.next()) != 0) { + assert(defidx != Find_const(member), "Live out member has not been compressed"); + } +#endif + Reachblock[slidx] = NULL; + } else { + assert(Reachblock[slidx] != NULL,"No reaching definition for liveout value"); + } + } +#ifndef PRODUCT + if( trace_spilling() ) + b->dump(); +#endif + } // End For All Blocks + + //----------PASS 2---------- + // Reset all DEF live range numbers here + for( insidx = 0; insidx < defs->size(); insidx++ ) { + // Grab the def + n1 = defs->at(insidx); + // Set new lidx for DEF + new_lrg(n1, maxlrg++); + } + //----------Phi Node Splitting---------- + // Clean up a phi here, and assign a new live range number + // Cycle through this block's predecessors, collecting Reaches + // info for each spilled LRG and update edges. + // Walk the phis list to patch inputs, split phis, and name phis + for( insidx = 0; insidx < phis->size(); insidx++ ) { + Node *phi = phis->at(insidx); + assert(phi->is_Phi(),"This list must only contain Phi Nodes"); + Block *b = _cfg._bbs[phi->_idx]; + // Grab the live range number + uint lidx = Find_id(phi); + uint slidx = lrg2reach[lidx]; + // Update node to lidx map + new_lrg(phi, maxlrg++); + // Get PASS1's up/down decision for the block. + int phi_up = !!UP_entry[slidx]->test(b->_pre_order); + + // Force down if double-spilling live range + if( lrgs(lidx)._was_spilled1 ) + phi_up = false; + + // When splitting a Phi we an split it normal or "inverted". + // An inverted split makes the splits target the Phi's UP/DOWN + // sense inverted; then the Phi is followed by a final def-side + // split to invert back. It changes which blocks the spill code + // goes in. + + // Walk the predecessor blocks and assign the reaching def to the Phi. + // Split Phi nodes by placing USE side splits wherever the reaching + // DEF has the wrong UP/DOWN value. + for( uint i = 1; i < b->num_preds(); i++ ) { + // Get predecessor block pre-order number + Block *pred = _cfg._bbs[b->pred(i)->_idx]; + pidx = pred->_pre_order; + // Grab reaching def + Node *def = Reaches[pidx][slidx]; + assert( def, "must have reaching def" ); + // If input up/down sense and reg-pressure DISagree + if( def->rematerialize() ) { + def = split_Rematerialize( def, pred, pred->end_idx(), maxlrg, splits, slidx, lrg2reach, Reachblock, false ); + if( !def ) return 0; // Bail out + } + // Update the Phi's input edge array + phi->set_req(i,def); + // Grab the UP/DOWN sense for the input + u1 = UP[pidx][slidx]; + if( u1 != (phi_up != 0)) { + maxlrg = split_USE(def, b, phi, i, maxlrg, !u1, false, splits,slidx); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + } + } // End for all inputs to the Phi + } // End for all Phi Nodes + // Update _maxlrg to save Union asserts + _maxlrg = maxlrg; + + + //----------PASS 3---------- + // Pass over all Phi's to union the live ranges + for( insidx = 0; insidx < phis->size(); insidx++ ) { + Node *phi = phis->at(insidx); + assert(phi->is_Phi(),"This list must only contain Phi Nodes"); + // Walk all inputs to Phi and Union input live range with Phi live range + for( uint i = 1; i < phi->req(); i++ ) { + // Grab the input node + Node *n = phi->in(i); + assert( n, "" ); + uint lidx = Find(n); + uint pidx = Find(phi); + if( lidx < pidx ) + Union(n, phi); + else if( lidx > pidx ) + Union(phi, n); + } // End for all inputs to the Phi Node + } // End for all Phi Nodes + // Now union all two address instructions + for( insidx = 0; insidx < defs->size(); insidx++ ) { + // Grab the def + n1 = defs->at(insidx); + // Set new lidx for DEF & handle 2-addr instructions + if( n1->is_Mach() && ((twoidx = n1->as_Mach()->two_adr()) != 0) ) { + assert( Find(n1->in(twoidx)) < maxlrg,"Assigning bad live range index"); + // Union the input and output live ranges + uint lr1 = Find(n1); + uint lr2 = Find(n1->in(twoidx)); + if( lr1 < lr2 ) + Union(n1, n1->in(twoidx)); + else if( lr1 > lr2 ) + Union(n1->in(twoidx), n1); + } // End if two address + } // End for all defs + // DEBUG +#ifdef ASSERT + // Validate all live range index assignments + for( bidx = 0; bidx < _cfg._num_blocks; bidx++ ) { + b = _cfg._blocks[bidx]; + for( insidx = 0; insidx <= b->end_idx(); insidx++ ) { + Node *n = b->_nodes[insidx]; + uint defidx = Find(n); + assert(defidx < _maxlrg,"Bad live range index in Split"); + assert(defidx < maxlrg,"Bad live range index in Split"); + } + } + // Issue a warning if splitting made no progress + int noprogress = 0; + for( slidx = 0; slidx < spill_cnt; slidx++ ) { + if( PrintOpto && WizardMode && splits.at(slidx) == 0 ) { + tty->print_cr("Failed to split live range %d", lidxs.at(slidx)); + //BREAKPOINT; + } + else { + noprogress++; + } + } + if(!noprogress) { + tty->print_cr("Failed to make progress in Split"); + //BREAKPOINT; + } +#endif + // Return updated count of live ranges + return maxlrg; +} diff --git a/hotspot/src/share/vm/opto/regalloc.cpp b/hotspot/src/share/vm/opto/regalloc.cpp new file mode 100644 index 00000000000..7c4d02797c0 --- /dev/null +++ b/hotspot/src/share/vm/opto/regalloc.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_regalloc.cpp.incl" + +static const int NodeRegsOverflowSize = 200; + +void (*PhaseRegAlloc::_alloc_statistics[MAX_REG_ALLOCATORS])(); +int PhaseRegAlloc::_num_allocators = 0; +#ifndef PRODUCT +int PhaseRegAlloc::_total_framesize = 0; +int PhaseRegAlloc::_max_framesize = 0; +#endif + +PhaseRegAlloc::PhaseRegAlloc( uint unique, PhaseCFG &cfg, + Matcher &matcher, + void (*pr_stats)() ): + Phase(Register_Allocation), _cfg(cfg), _matcher(matcher), + _node_oops(Thread::current()->resource_area()), + _node_regs(0), + _framesize(0xdeadbeef) +{ + int i; + + for (i=0; i < _num_allocators; i++) { + if (_alloc_statistics[i] == pr_stats) + return; + } + assert((_num_allocators + 1) < MAX_REG_ALLOCATORS, "too many register allocators"); + _alloc_statistics[_num_allocators++] = pr_stats; +} + + +//------------------------------reg2offset------------------------------------- +int PhaseRegAlloc::reg2offset_unchecked( OptoReg::Name reg ) const { + // Slots below _max_in_arg_stack_reg are offset by the entire frame. + // Slots above _max_in_arg_stack_reg are frame_slots and are not offset. + int slot = (reg < _matcher._new_SP) + ? reg - OptoReg::stack0() + _framesize + : reg - _matcher._new_SP; + // Note: We use the direct formula (reg - SharedInfo::stack0) instead of + // OptoReg::reg2stack(reg), in order to avoid asserts in the latter + // function. This routine must remain unchecked, so that dump_frame() + // can do its work undisturbed. + // %%% not really clear why reg2stack would assert here + + return slot*VMRegImpl::stack_slot_size; +} + +int PhaseRegAlloc::reg2offset( OptoReg::Name reg ) const { + + // Not allowed in the out-preserve area. + // In-preserve area is allowed so Intel can fetch the return pc out. + assert( reg < _matcher._old_SP || + (reg >= OptoReg::add(_matcher._old_SP,C->out_preserve_stack_slots()) && + reg < _matcher._in_arg_limit) || + reg >= OptoReg::add(_matcher._new_SP,C->out_preserve_stack_slots()), + "register allocated in a preserve area" ); + return reg2offset_unchecked( reg ); +} + +//------------------------------offset2reg------------------------------------- +OptoReg::Name PhaseRegAlloc::offset2reg(int stk_offset) const { + int slot = stk_offset / jintSize; + int reg = (slot < (int) _framesize) + ? slot + _matcher._new_SP + : OptoReg::stack2reg(slot) - _framesize; + assert(stk_offset == reg2offset((OptoReg::Name) reg), + "offset2reg does not invert properly"); + return (OptoReg::Name) reg; +} + +//------------------------------set_oop---------------------------------------- +void PhaseRegAlloc::set_oop( const Node *n, bool is_an_oop ) { + if( is_an_oop ) { + _node_oops.set(n->_idx); + } +} + +//------------------------------is_oop----------------------------------------- +bool PhaseRegAlloc::is_oop( const Node *n ) const { + return _node_oops.test(n->_idx) != 0; +} + +// Allocate _node_regs table with at least "size" elements +void PhaseRegAlloc::alloc_node_regs(int size) { + _node_regs_max_index = size + (size >> 1) + NodeRegsOverflowSize; + _node_regs = NEW_RESOURCE_ARRAY( OptoRegPair, _node_regs_max_index ); + // We assume our caller will fill in all elements up to size-1, so + // only the extra space we allocate is initialized here. + for( uint i = size; i < _node_regs_max_index; ++i ) + _node_regs[i].set_bad(); +} + +#ifndef PRODUCT +void +PhaseRegAlloc::print_statistics() { + tty->print_cr("Total frameslots = %d, Max frameslots = %d", _total_framesize, _max_framesize); + int i; + + for (i=0; i < _num_allocators; i++) { + _alloc_statistics[i](); + } +} +#endif diff --git a/hotspot/src/share/vm/opto/regalloc.hpp b/hotspot/src/share/vm/opto/regalloc.hpp new file mode 100644 index 00000000000..37f7ba518bf --- /dev/null +++ b/hotspot/src/share/vm/opto/regalloc.hpp @@ -0,0 +1,133 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Node; +class Matcher; +class PhaseCFG; + +#define MAX_REG_ALLOCATORS 10 + +//------------------------------PhaseRegAlloc------------------------------------ +// Abstract register allocator +class PhaseRegAlloc : public Phase { + static void (*_alloc_statistics[MAX_REG_ALLOCATORS])(); + static int _num_allocators; + +protected: + OptoRegPair *_node_regs; + uint _node_regs_max_index; + VectorSet _node_oops; // Mapping from node indices to oopiness + + void alloc_node_regs(int size); // allocate _node_regs table with at least "size" elements + + PhaseRegAlloc( uint unique, PhaseCFG &cfg, Matcher &matcher, + void (*pr_stats)()); +public: + PhaseCFG &_cfg; // Control flow graph + uint _framesize; // Size of frame in stack-slots. not counting preserve area + OptoReg::Name _max_reg; // Past largest register seen + Matcher &_matcher; // Convert Ideal to MachNodes + uint node_regs_max_index() const { return _node_regs_max_index; } + + // Get the register associated with the Node + OptoReg::Name get_reg_first( const Node *n ) const { + debug_only( if( n->_idx >= _node_regs_max_index ) n->dump(); ); + assert( n->_idx < _node_regs_max_index, "Exceeded _node_regs array"); + return _node_regs[n->_idx].first(); + } + OptoReg::Name get_reg_second( const Node *n ) const { + debug_only( if( n->_idx >= _node_regs_max_index ) n->dump(); ); + assert( n->_idx < _node_regs_max_index, "Exceeded _node_regs array"); + return _node_regs[n->_idx].second(); + } + + // Do all the real work of allocate + virtual void Register_Allocate() = 0; + + + // notify the register allocator that "node" is a new reference + // to the value produced by "old_node" + virtual void add_reference( const Node *node, const Node *old_node) = 0; + + + // Set the register associated with a new Node + void set_bad( uint idx ) { + assert( idx < _node_regs_max_index, "Exceeded _node_regs array"); + _node_regs[idx].set_bad(); + } + void set1( uint idx, OptoReg::Name reg ) { + assert( idx < _node_regs_max_index, "Exceeded _node_regs array"); + _node_regs[idx].set1(reg); + } + void set2( uint idx, OptoReg::Name reg ) { + assert( idx < _node_regs_max_index, "Exceeded _node_regs array"); + _node_regs[idx].set2(reg); + } + void set_pair( uint idx, OptoReg::Name hi, OptoReg::Name lo ) { + assert( idx < _node_regs_max_index, "Exceeded _node_regs array"); + _node_regs[idx].set_pair(hi, lo); + } + void set_ptr( uint idx, OptoReg::Name reg ) { + assert( idx < _node_regs_max_index, "Exceeded _node_regs array"); + _node_regs[idx].set_ptr(reg); + } + // Set and query if a node produces an oop + void set_oop( const Node *n, bool ); + bool is_oop( const Node *n ) const; + + // Convert a register number to a stack offset + int reg2offset ( OptoReg::Name reg ) const; + int reg2offset_unchecked( OptoReg::Name reg ) const; + + // Convert a stack offset to a register number + OptoReg::Name offset2reg( int stk_offset ) const; + + // Get the register encoding associated with the Node + int get_encode( const Node *n ) const { + assert( n->_idx < _node_regs_max_index, "Exceeded _node_regs array"); + OptoReg::Name first = _node_regs[n->_idx].first(); + OptoReg::Name second = _node_regs[n->_idx].second(); + assert( !OptoReg::is_valid(second) || second == first+1, "" ); + assert(OptoReg::is_reg(first), "out of range"); + return Matcher::_regEncode[first]; + } + + // Platform dependent hook for actions prior to allocation + void pd_preallocate_hook(); + +#ifdef ASSERT + // Platform dependent hook for verification after allocation. Will + // only get called when compiling with asserts. + void pd_postallocate_verify_hook(); +#endif + +#ifndef PRODUCT + static int _total_framesize; + static int _max_framesize; + + virtual void dump_frame() const = 0; + virtual char *dump_register( const Node *n, char *buf ) const = 0; + static void print_statistics(); +#endif +}; diff --git a/hotspot/src/share/vm/opto/regmask.cpp b/hotspot/src/share/vm/opto/regmask.cpp new file mode 100644 index 00000000000..782d1fa99e3 --- /dev/null +++ b/hotspot/src/share/vm/opto/regmask.cpp @@ -0,0 +1,288 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_regmask.cpp.incl" + +#define RM_SIZE _RM_SIZE /* a constant private to the class RegMask */ + +//-------------Non-zero bit search methods used by RegMask--------------------- +// Find lowest 1, or return 32 if empty +int find_lowest_bit( uint32 mask ) { + int n = 0; + if( (mask & 0xffff) == 0 ) { + mask >>= 16; + n += 16; + } + if( (mask & 0xff) == 0 ) { + mask >>= 8; + n += 8; + } + if( (mask & 0xf) == 0 ) { + mask >>= 4; + n += 4; + } + if( (mask & 0x3) == 0 ) { + mask >>= 2; + n += 2; + } + if( (mask & 0x1) == 0 ) { + mask >>= 1; + n += 1; + } + if( mask == 0 ) { + n = 32; + } + return n; +} + +// Find highest 1, or return 32 if empty +int find_hihghest_bit( uint32 mask ) { + int n = 0; + if( mask > 0xffff ) { + mask >>= 16; + n += 16; + } + if( mask > 0xff ) { + mask >>= 8; + n += 8; + } + if( mask > 0xf ) { + mask >>= 4; + n += 4; + } + if( mask > 0x3 ) { + mask >>= 2; + n += 2; + } + if( mask > 0x1 ) { + mask >>= 1; + n += 1; + } + if( mask == 0 ) { + n = 32; + } + return n; +} + +//------------------------------dump------------------------------------------- + +#ifndef PRODUCT +void OptoReg::dump( int r ) { + switch( r ) { + case Special: tty->print("r---"); break; + case Bad: tty->print("rBAD"); break; + default: + if( r < _last_Mach_Reg ) tty->print(Matcher::regName[r]); + else tty->print("rS%d",r); + break; + } +} +#endif + + +//============================================================================= +const RegMask RegMask::Empty( +# define BODY(I) 0, + FORALL_BODY +# undef BODY + 0 +); + +//------------------------------find_first_pair-------------------------------- +// Find the lowest-numbered register pair in the mask. Return the +// HIGHEST register number in the pair, or BAD if no pairs. +OptoReg::Name RegMask::find_first_pair() const { + VerifyPairs(); + for( int i = 0; i < RM_SIZE; i++ ) { + if( _A[i] ) { // Found some bits + int bit = _A[i] & -_A[i]; // Extract low bit + // Convert to bit number, return hi bit in pair + return OptoReg::Name((i<<_LogWordBits)+find_lowest_bit(bit)+1); + } + } + return OptoReg::Bad; +} + +//------------------------------ClearToPairs----------------------------------- +// Clear out partial bits; leave only bit pairs +void RegMask::ClearToPairs() { + for( int i = 0; i < RM_SIZE; i++ ) { + int bits = _A[i]; + bits &= ((bits & 0x55555555)<<1); // 1 hi-bit set for each pair + bits |= (bits>>1); // Smear 1 hi-bit into a pair + _A[i] = bits; + } + VerifyPairs(); +} + +//------------------------------SmearToPairs----------------------------------- +// Smear out partial bits; leave only bit pairs +void RegMask::SmearToPairs() { + for( int i = 0; i < RM_SIZE; i++ ) { + int bits = _A[i]; + bits |= ((bits & 0x55555555)<<1); // Smear lo bit hi per pair + bits |= ((bits & 0xAAAAAAAA)>>1); // Smear hi bit lo per pair + _A[i] = bits; + } + VerifyPairs(); +} + +//------------------------------is_aligned_pairs------------------------------- +bool RegMask::is_aligned_Pairs() const { + // Assert that the register mask contains only bit pairs. + for( int i = 0; i < RM_SIZE; i++ ) { + int bits = _A[i]; + while( bits ) { // Check bits for pairing + int bit = bits & -bits; // Extract low bit + // Low bit is not odd means its mis-aligned. + if( (bit & 0x55555555) == 0 ) return false; + bits -= bit; // Remove bit from mask + // Check for aligned adjacent bit + if( (bits & (bit<<1)) == 0 ) return false; + bits -= (bit<<1); // Remove other halve of pair + } + } + return true; +} + +//------------------------------is_bound1-------------------------------------- +// Return TRUE if the mask contains a single bit +int RegMask::is_bound1() const { + if( is_AllStack() ) return false; + int bit = -1; // Set to hold the one bit allowed + for( int i = 0; i < RM_SIZE; i++ ) { + if( _A[i] ) { // Found some bits + if( bit != -1 ) return false; // Already had bits, so fail + bit = _A[i] & -_A[i]; // Extract 1 bit from mask + if( bit != _A[i] ) return false; // Found many bits, so fail + } + } + // True for both the empty mask and for a single bit + return true; +} + +//------------------------------is_bound2-------------------------------------- +// Return TRUE if the mask contains an adjacent pair of bits and no other bits. +int RegMask::is_bound2() const { + if( is_AllStack() ) return false; + + int bit = -1; // Set to hold the one bit allowed + for( int i = 0; i < RM_SIZE; i++ ) { + if( _A[i] ) { // Found some bits + if( bit != -1 ) return false; // Already had bits, so fail + bit = _A[i] & -(_A[i]); // Extract 1 bit from mask + if( (bit << 1) != 0 ) { // Bit pair stays in same word? + if( (bit | (bit<<1)) != _A[i] ) + return false; // Require adjacent bit pair and no more bits + } else { // Else its a split-pair case + if( bit != _A[i] ) return false; // Found many bits, so fail + i++; // Skip iteration forward + if( _A[i] != 1 ) return false; // Require 1 lo bit in next word + } + } + } + // True for both the empty mask and for a bit pair + return true; +} + +//------------------------------is_UP------------------------------------------ +// UP means register only, Register plus stack, or stack only is DOWN +bool RegMask::is_UP() const { + // Quick common case check for DOWN (any stack slot is legal) + if( is_AllStack() ) + return false; + // Slower check for any stack bits set (also DOWN) + if( overlap(Matcher::STACK_ONLY_mask) ) + return false; + // Not DOWN, so must be UP + return true; +} + +//------------------------------Size------------------------------------------- +// Compute size of register mask in bits +uint RegMask::Size() const { + extern uint8 bitsInByte[256]; + uint sum = 0; + for( int i = 0; i < RM_SIZE; i++ ) + sum += + bitsInByte[(_A[i]>>24) & 0xff] + + bitsInByte[(_A[i]>>16) & 0xff] + + bitsInByte[(_A[i]>> 8) & 0xff] + + bitsInByte[ _A[i] & 0xff]; + return sum; +} + +#ifndef PRODUCT +//------------------------------print------------------------------------------ +void RegMask::dump( ) const { + tty->print("["); + RegMask rm = *this; // Structure copy into local temp + + OptoReg::Name start = rm.find_first_elem(); // Get a register + if( OptoReg::is_valid(start) ) { // Check for empty mask + rm.Remove(start); // Yank from mask + OptoReg::dump(start); // Print register + OptoReg::Name last = start; + + // Now I have printed an initial register. + // Print adjacent registers as "rX-rZ" instead of "rX,rY,rZ". + // Begin looping over the remaining registers. + while( 1 ) { // + OptoReg::Name reg = rm.find_first_elem(); // Get a register + if( !OptoReg::is_valid(reg) ) + break; // Empty mask, end loop + rm.Remove(reg); // Yank from mask + + if( last+1 == reg ) { // See if they are adjacent + // Adjacent registers just collect into long runs, no printing. + last = reg; + } else { // Ending some kind of run + if( start == last ) { // 1-register run; no special printing + } else if( start+1 == last ) { + tty->print(","); // 2-register run; print as "rX,rY" + OptoReg::dump(last); + } else { // Multi-register run; print as "rX-rZ" + tty->print("-"); + OptoReg::dump(last); + } + tty->print(","); // Seperate start of new run + start = last = reg; // Start a new register run + OptoReg::dump(start); // Print register + } // End of if ending a register run or not + } // End of while regmask not empty + + if( start == last ) { // 1-register run; no special printing + } else if( start+1 == last ) { + tty->print(","); // 2-register run; print as "rX,rY" + OptoReg::dump(last); + } else { // Multi-register run; print as "rX-rZ" + tty->print("-"); + OptoReg::dump(last); + } + if( rm.is_AllStack() ) tty->print("..."); + } + tty->print("]"); +} +#endif diff --git a/hotspot/src/share/vm/opto/regmask.hpp b/hotspot/src/share/vm/opto/regmask.hpp new file mode 100644 index 00000000000..e34c8354f34 --- /dev/null +++ b/hotspot/src/share/vm/opto/regmask.hpp @@ -0,0 +1,264 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Some fun naming (textual) substitutions: +// +// RegMask::get_low_elem() ==> RegMask::find_first_elem() +// RegMask::Special ==> RegMask::Empty +// RegMask::_flags ==> RegMask::is_AllStack() +// RegMask::operator<<=() ==> RegMask::Insert() +// RegMask::operator>>=() ==> RegMask::Remove() +// RegMask::Union() ==> RegMask::OR +// RegMask::Inter() ==> RegMask::AND +// +// OptoRegister::RegName ==> OptoReg::Name +// +// OptoReg::stack0() ==> _last_Mach_Reg or ZERO in core version +// +// numregs in chaitin ==> proper degree in chaitin + +//-------------Non-zero bit search methods used by RegMask--------------------- +// Find lowest 1, or return 32 if empty +int find_lowest_bit( uint32 mask ); +// Find highest 1, or return 32 if empty +int find_hihghest_bit( uint32 mask ); + +//------------------------------RegMask---------------------------------------- +// The ADL file describes how to print the machine-specific registers, as well +// as any notion of register classes. We provide a register mask, which is +// just a collection of Register numbers. + +// The ADLC defines 2 macros, RM_SIZE and FORALL_BODY. +// RM_SIZE is the size of a register mask in words. +// FORALL_BODY replicates a BODY macro once per word in the register mask. +// The usage is somewhat clumsy and limited to the regmask.[h,c]pp files. +// However, it means the ADLC can redefine the unroll macro and all loops +// over register masks will be unrolled by the correct amount. + +class RegMask VALUE_OBJ_CLASS_SPEC { + union { + double _dummy_force_double_alignment[RM_SIZE>>1]; + // Array of Register Mask bits. This array is large enough to cover + // all the machine registers and all parameters that need to be passed + // on the stack (stack registers) up to some interesting limit. Methods + // that need more parameters will NOT be compiled. On Intel, the limit + // is something like 90+ parameters. + int _A[RM_SIZE]; + }; + + enum { + _WordBits = BitsPerInt, + _LogWordBits = LogBitsPerInt, + _RM_SIZE = RM_SIZE // local constant, imported, then hidden by #undef + }; + +public: + enum { CHUNK_SIZE = RM_SIZE*_WordBits }; + + // SlotsPerLong is 2, since slots are 32 bits and longs are 64 bits. + // Also, consider the maximum alignment size for a normally allocated + // value. Since we allocate register pairs but not register quads (at + // present), this alignment is SlotsPerLong (== 2). A normally + // aligned allocated register is either a single register, or a pair + // of adjacent registers, the lower-numbered being even. + // See also is_aligned_Pairs() below, and the padding added before + // Matcher::_new_SP to keep allocated pairs aligned properly. + // If we ever go to quad-word allocations, SlotsPerQuad will become + // the controlling alignment constraint. Note that this alignment + // requirement is internal to the allocator, and independent of any + // particular platform. + enum { SlotsPerLong = 2 }; + + // A constructor only used by the ADLC output. All mask fields are filled + // in directly. Calls to this look something like RM(1,2,3,4); + RegMask( +# define BODY(I) int a##I, + FORALL_BODY +# undef BODY + int dummy = 0 ) { +# define BODY(I) _A[I] = a##I; + FORALL_BODY +# undef BODY + } + + // Handy copying constructor + RegMask( RegMask *rm ) { +# define BODY(I) _A[I] = rm->_A[I]; + FORALL_BODY +# undef BODY + } + + // Construct an empty mask + RegMask( ) { Clear(); } + + // Construct a mask with a single bit + RegMask( OptoReg::Name reg ) { Clear(); Insert(reg); } + + // Check for register being in mask + int Member( OptoReg::Name reg ) const { + assert( reg < CHUNK_SIZE, "" ); + return _A[reg>>_LogWordBits] & (1<<(reg&(_WordBits-1))); + } + + // The last bit in the register mask indicates that the mask should repeat + // indefinitely with ONE bits. Returns TRUE if mask is infinite or + // unbounded in size. Returns FALSE if mask is finite size. + int is_AllStack() const { return _A[RM_SIZE-1] >> (_WordBits-1); } + + // Work around an -xO3 optimization problme in WS6U1. The old way: + // void set_AllStack() { _A[RM_SIZE-1] |= (1<<(_WordBits-1)); } + // will cause _A[RM_SIZE-1] to be clobbered, not updated when set_AllStack() + // follows an Insert() loop, like the one found in init_spill_mask(). Using + // Insert() instead works because the index into _A in computed instead of + // constant. See bug 4665841. + void set_AllStack() { Insert(OptoReg::Name(CHUNK_SIZE-1)); } + + // Test for being a not-empty mask. + int is_NotEmpty( ) const { + int tmp = 0; +# define BODY(I) tmp |= _A[I]; + FORALL_BODY +# undef BODY + return tmp; + } + + // Find lowest-numbered register from mask, or BAD if mask is empty. + OptoReg::Name find_first_elem() const { + int base, bits; +# define BODY(I) if( (bits = _A[I]) != 0 ) base = I<<_LogWordBits; else + FORALL_BODY +# undef BODY + { base = OptoReg::Bad; bits = 1<<0; } + return OptoReg::Name(base + find_lowest_bit(bits)); + } + // Get highest-numbered register from mask, or BAD if mask is empty. + OptoReg::Name find_last_elem() const { + int base, bits; +# define BODY(I) if( (bits = _A[RM_SIZE-1-I]) != 0 ) base = (RM_SIZE-1-I)<<_LogWordBits; else + FORALL_BODY +# undef BODY + { base = OptoReg::Bad; bits = 1<<0; } + return OptoReg::Name(base + find_hihghest_bit(bits)); + } + + // Find the lowest-numbered register pair in the mask. Return the + // HIGHEST register number in the pair, or BAD if no pairs. + // Assert that the mask contains only bit pairs. + OptoReg::Name find_first_pair() const; + + // Clear out partial bits; leave only aligned adjacent bit pairs. + void ClearToPairs(); + // Smear out partial bits; leave only aligned adjacent bit pairs. + void SmearToPairs(); + // Verify that the mask contains only aligned adjacent bit pairs + void VerifyPairs() const { assert( is_aligned_Pairs(), "mask is not aligned, adjacent pairs" ); } + // Test that the mask contains only aligned adjacent bit pairs + bool is_aligned_Pairs() const; + + // mask is a pair of misaligned registers + bool is_misaligned_Pair() const { return Size()==2 && !is_aligned_Pairs();} + // Test for single register + int is_bound1() const; + // Test for a single adjacent pair + int is_bound2() const; + + // Fast overlap test. Non-zero if any registers in common. + int overlap( const RegMask &rm ) const { + return +# define BODY(I) (_A[I] & rm._A[I]) | + FORALL_BODY +# undef BODY + 0 ; + } + + // Special test for register pressure based splitting + // UP means register only, Register plus stack, or stack only is DOWN + bool is_UP() const; + + // Clear a register mask + void Clear( ) { +# define BODY(I) _A[I] = 0; + FORALL_BODY +# undef BODY + } + + // Fill a register mask with 1's + void Set_All( ) { +# define BODY(I) _A[I] = -1; + FORALL_BODY +# undef BODY + } + + // Insert register into mask + void Insert( OptoReg::Name reg ) { + assert( reg < CHUNK_SIZE, "" ); + _A[reg>>_LogWordBits] |= (1<<(reg&(_WordBits-1))); + } + + // Remove register from mask + void Remove( OptoReg::Name reg ) { + assert( reg < CHUNK_SIZE, "" ); + _A[reg>>_LogWordBits] &= ~(1<<(reg&(_WordBits-1))); + } + + // OR 'rm' into 'this' + void OR( const RegMask &rm ) { +# define BODY(I) this->_A[I] |= rm._A[I]; + FORALL_BODY +# undef BODY + } + + // AND 'rm' into 'this' + void AND( const RegMask &rm ) { +# define BODY(I) this->_A[I] &= rm._A[I]; + FORALL_BODY +# undef BODY + } + + // Subtract 'rm' from 'this' + void SUBTRACT( const RegMask &rm ) { +# define BODY(I) _A[I] &= ~rm._A[I]; + FORALL_BODY +# undef BODY + } + + // Compute size of register mask: number of bits + uint Size() const; + +#ifndef PRODUCT + void print() const { dump(); } + void dump() const; // Print a mask +#endif + + static const RegMask Empty; // Common empty mask + + static bool can_represent(OptoReg::Name reg) { + // NOTE: -1 in computation reflects the usage of the last + // bit of the regmask as an infinite stack flag. + return (int)reg < (int)(CHUNK_SIZE-1); + } +}; + +// Do not use this constant directly in client code! +#undef RM_SIZE diff --git a/hotspot/src/share/vm/opto/rootnode.cpp b/hotspot/src/share/vm/opto/rootnode.cpp new file mode 100644 index 00000000000..44e0118ba87 --- /dev/null +++ b/hotspot/src/share/vm/opto/rootnode.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_rootnode.cpp.incl" + +//------------------------------Ideal------------------------------------------ +// Remove dead inputs +Node *RootNode::Ideal(PhaseGVN *phase, bool can_reshape) { + for( uint i = 1; i < req(); i++ ) { // For all inputs + // Check for and remove dead inputs + if( phase->type(in(i)) == Type::TOP ) { + del_req(i--); // Delete TOP inputs + } + } + + // I used to do tail-splitting in the Ideal graph here, but it does not + // work. The tail-splitting forces values live into the Return to be + // ready at a point which dominates the split returns. This forces Stores + // to be hoisted high. The "proper" fix would be to split Stores down + // each path, but this makes the split unprofitable. If we want to do this + // optimization, it needs to be done after allocation so we can count all + // the instructions needing to be cloned in the cost metric. + + // There used to be a spoof here for caffeine marks which completely + // eliminated very simple self-recursion recursions, but it's not worth it. + // Deep inlining of self-calls gets nearly all of the same benefits. + // If we want to get the rest of the win later, we should pattern match + // simple recursive call trees to closed-form solutions. + + return NULL; // No further opportunities exposed +} + +//============================================================================= +HaltNode::HaltNode( Node *ctrl, Node *frameptr ) : Node(TypeFunc::Parms) { + Node* top = Compile::current()->top(); + init_req(TypeFunc::Control, ctrl ); + init_req(TypeFunc::I_O, top); + init_req(TypeFunc::Memory, top); + init_req(TypeFunc::FramePtr, frameptr ); + init_req(TypeFunc::ReturnAdr,top); +} + +const Type *HaltNode::bottom_type() const { return Type::BOTTOM; } + +//------------------------------Ideal------------------------------------------ +Node *HaltNode::Ideal(PhaseGVN *phase, bool can_reshape) { + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + +//------------------------------Value------------------------------------------ +const Type *HaltNode::Value( PhaseTransform *phase ) const { + return ( phase->type(in(TypeFunc::Control)) == Type::TOP) + ? Type::TOP + : Type::BOTTOM; +} + +const RegMask &HaltNode::out_RegMask() const { + return RegMask::Empty; +} diff --git a/hotspot/src/share/vm/opto/rootnode.hpp b/hotspot/src/share/vm/opto/rootnode.hpp new file mode 100644 index 00000000000..369bd9d6d30 --- /dev/null +++ b/hotspot/src/share/vm/opto/rootnode.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//------------------------------RootNode--------------------------------------- +// The one-and-only before-all-else and after-all-else RootNode. The RootNode +// represents what happens if the user runs the whole program repeatedly. The +// RootNode produces the initial values of I/O and memory for the program or +// procedure start. +class RootNode : public LoopNode { +public: + RootNode( ) : LoopNode(0,0) { + init_class_id(Class_Root); + del_req(2); + del_req(1); + } + virtual int Opcode() const; + virtual const Node *is_block_proj() const { return this; } + virtual const Type *bottom_type() const { return Type::BOTTOM; } + virtual Node *Identity( PhaseTransform *phase ) { return this; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const { return Type::BOTTOM; } +}; + +//------------------------------HaltNode--------------------------------------- +// Throw an exception & die +class HaltNode : public Node { +public: + HaltNode( Node *ctrl, Node *frameptr ); + virtual int Opcode() const; + virtual bool pinned() const { return true; }; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const; + virtual bool is_CFG() const { return true; } + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual bool depends_only_on_test() const { return false; } + virtual const Node *is_block_proj() const { return this; } + virtual const RegMask &out_RegMask() const; + virtual uint ideal_reg() const { return NotAMachineReg; } + virtual uint match_edge(uint idx) const { return 0; } +}; diff --git a/hotspot/src/share/vm/opto/runtime.cpp b/hotspot/src/share/vm/opto/runtime.cpp new file mode 100644 index 00000000000..aedfceb51af --- /dev/null +++ b/hotspot/src/share/vm/opto/runtime.cpp @@ -0,0 +1,1177 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_runtime.cpp.incl" + + +// For debugging purposes: +// To force FullGCALot inside a runtime function, add the following two lines +// +// Universe::release_fullgc_alot_dummy(); +// MarkSweep::invoke(0, "Debugging"); +// +// At command line specify the parameters: -XX:+FullGCALot -XX:FullGCALotStart=100000000 + + + + +// Compiled code entry points +address OptoRuntime::_new_instance_Java = NULL; +address OptoRuntime::_new_array_Java = NULL; +address OptoRuntime::_multianewarray2_Java = NULL; +address OptoRuntime::_multianewarray3_Java = NULL; +address OptoRuntime::_multianewarray4_Java = NULL; +address OptoRuntime::_multianewarray5_Java = NULL; +address OptoRuntime::_vtable_must_compile_Java = NULL; +address OptoRuntime::_complete_monitor_locking_Java = NULL; +address OptoRuntime::_rethrow_Java = NULL; + +address OptoRuntime::_slow_arraycopy_Java = NULL; +address OptoRuntime::_register_finalizer_Java = NULL; + +# ifdef ENABLE_ZAP_DEAD_LOCALS +address OptoRuntime::_zap_dead_Java_locals_Java = NULL; +address OptoRuntime::_zap_dead_native_locals_Java = NULL; +# endif + + +// This should be called in an assertion at the start of OptoRuntime routines +// which are entered from compiled code (all of them) +#ifndef PRODUCT +static bool check_compiled_frame(JavaThread* thread) { + assert(thread->last_frame().is_runtime_frame(), "cannot call runtime directly from compiled code"); +#ifdef ASSERT + RegisterMap map(thread, false); + frame caller = thread->last_frame().sender(&map); + assert(caller.is_compiled_frame(), "not being called from compiled like code"); +#endif /* ASSERT */ + return true; +} +#endif + + +#define gen(env, var, type_func_gen, c_func, fancy_jump, pass_tls, save_arg_regs, return_pc) \ + var = generate_stub(env, type_func_gen, CAST_FROM_FN_PTR(address, c_func), #var, fancy_jump, pass_tls, save_arg_regs, return_pc) + +void OptoRuntime::generate(ciEnv* env) { + + generate_exception_blob(); + + // Note: tls: Means fetching the return oop out of the thread-local storage + // + // variable/name type-function-gen , runtime method ,fncy_jp, tls,save_args,retpc + // ------------------------------------------------------------------------------------------------------------------------------- + gen(env, _new_instance_Java , new_instance_Type , new_instance_C , 0 , true , false, false); + gen(env, _new_array_Java , new_array_Type , new_array_C , 0 , true , false, false); + gen(env, _multianewarray2_Java , multianewarray2_Type , multianewarray2_C , 0 , true , false, false); + gen(env, _multianewarray3_Java , multianewarray3_Type , multianewarray3_C , 0 , true , false, false); + gen(env, _multianewarray4_Java , multianewarray4_Type , multianewarray4_C , 0 , true , false, false); + gen(env, _multianewarray5_Java , multianewarray5_Type , multianewarray5_C , 0 , true , false, false); + gen(env, _complete_monitor_locking_Java , complete_monitor_enter_Type , SharedRuntime::complete_monitor_locking_C , 0 , false, false, false); + gen(env, _rethrow_Java , rethrow_Type , rethrow_C , 2 , true , false, true ); + + gen(env, _slow_arraycopy_Java , slow_arraycopy_Type , SharedRuntime::slow_arraycopy_C , 0 , false, false, false); + gen(env, _register_finalizer_Java , register_finalizer_Type , register_finalizer , 0 , false, false, false); + +# ifdef ENABLE_ZAP_DEAD_LOCALS + gen(env, _zap_dead_Java_locals_Java , zap_dead_locals_Type , zap_dead_Java_locals_C , 0 , false, true , false ); + gen(env, _zap_dead_native_locals_Java , zap_dead_locals_Type , zap_dead_native_locals_C , 0 , false, true , false ); +# endif + +} + +#undef gen + + +// Helper method to do generation of RunTimeStub's +address OptoRuntime::generate_stub( ciEnv* env, + TypeFunc_generator gen, address C_function, + const char *name, int is_fancy_jump, + bool pass_tls, + bool save_argument_registers, + bool return_pc ) { + ResourceMark rm; + Compile C( env, gen, C_function, name, is_fancy_jump, pass_tls, save_argument_registers, return_pc ); + return C.stub_entry_point(); +} + +const char* OptoRuntime::stub_name(address entry) { +#ifndef PRODUCT + CodeBlob* cb = CodeCache::find_blob(entry); + RuntimeStub* rs =(RuntimeStub *)cb; + assert(rs != NULL && rs->is_runtime_stub(), "not a runtime stub"); + return rs->name(); +#else + // Fast implementation for product mode (maybe it should be inlined too) + return "runtime stub"; +#endif +} + + +//============================================================================= +// Opto compiler runtime routines +//============================================================================= + + +//=============================allocation====================================== +// We failed the fast-path allocation. Now we need to do a scavenge or GC +// and try allocation again. + +void OptoRuntime::do_eager_card_mark(JavaThread* thread) { + // After any safepoint, just before going back to compiled code, + // we perform a card mark. This lets the compiled code omit + // card marks for initialization of new objects. + // Keep this code consistent with GraphKit::store_barrier. + + oop new_obj = thread->vm_result(); + if (new_obj == NULL) return; + + assert(Universe::heap()->can_elide_tlab_store_barriers(), + "compiler must check this first"); + new_obj = Universe::heap()->new_store_barrier(new_obj); + thread->set_vm_result(new_obj); +} + +// object allocation +JRT_BLOCK_ENTRY(void, OptoRuntime::new_instance_C(klassOopDesc* klass, JavaThread* thread)) + JRT_BLOCK; +#ifndef PRODUCT + SharedRuntime::_new_instance_ctr++; // new instance requires GC +#endif + assert(check_compiled_frame(thread), "incorrect caller"); + + // These checks are cheap to make and support reflective allocation. + int lh = Klass::cast(klass)->layout_helper(); + if (Klass::layout_helper_needs_slow_path(lh) + || !instanceKlass::cast(klass)->is_initialized()) { + KlassHandle kh(THREAD, klass); + kh->check_valid_for_instantiation(false, THREAD); + if (!HAS_PENDING_EXCEPTION) { + instanceKlass::cast(kh())->initialize(THREAD); + } + if (!HAS_PENDING_EXCEPTION) { + klass = kh(); + } else { + klass = NULL; + } + } + + if (klass != NULL) { + // Scavenge and allocate an instance. + oop result = instanceKlass::cast(klass)->allocate_instance(THREAD); + thread->set_vm_result(result); + + // Pass oops back through thread local storage. Our apparent type to Java + // is that we return an oop, but we can block on exit from this routine and + // a GC can trash the oop in C's return register. The generated stub will + // fetch the oop from TLS after any possible GC. + } + + deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION); + JRT_BLOCK_END; + + if (GraphKit::use_ReduceInitialCardMarks()) { + // do them now so we don't have to do them on the fast path + do_eager_card_mark(thread); + } +JRT_END + + +// array allocation +JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_C(klassOopDesc* array_type, int len, JavaThread *thread)) + JRT_BLOCK; +#ifndef PRODUCT + SharedRuntime::_new_array_ctr++; // new array requires GC +#endif + assert(check_compiled_frame(thread), "incorrect caller"); + + // Scavenge and allocate an instance. + oop result; + + if (Klass::cast(array_type)->oop_is_typeArray()) { + // The oopFactory likes to work with the element type. + // (We could bypass the oopFactory, since it doesn't add much value.) + BasicType elem_type = typeArrayKlass::cast(array_type)->element_type(); + result = oopFactory::new_typeArray(elem_type, len, THREAD); + } else { + // Although the oopFactory likes to work with the elem_type, + // the compiler prefers the array_type, since it must already have + // that latter value in hand for the fast path. + klassOopDesc* elem_type = objArrayKlass::cast(array_type)->element_klass(); + result = oopFactory::new_objArray(elem_type, len, THREAD); + } + + // Pass oops back through thread local storage. Our apparent type to Java + // is that we return an oop, but we can block on exit from this routine and + // a GC can trash the oop in C's return register. The generated stub will + // fetch the oop from TLS after any possible GC. + deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION); + thread->set_vm_result(result); + JRT_BLOCK_END; + + if (GraphKit::use_ReduceInitialCardMarks()) { + // do them now so we don't have to do them on the fast path + do_eager_card_mark(thread); + } +JRT_END + +// Note: multianewarray for one dimension is handled inline by GraphKit::new_array. + +// multianewarray for 2 dimensions +JRT_ENTRY(void, OptoRuntime::multianewarray2_C(klassOopDesc* elem_type, int len1, int len2, JavaThread *thread)) +#ifndef PRODUCT + SharedRuntime::_multi2_ctr++; // multianewarray for 1 dimension +#endif + assert(check_compiled_frame(thread), "incorrect caller"); + assert(oop(elem_type)->is_klass(), "not a class"); + jint dims[2]; + dims[0] = len1; + dims[1] = len2; + oop obj = arrayKlass::cast(elem_type)->multi_allocate(2, dims, THREAD); + deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION); + thread->set_vm_result(obj); +JRT_END + +// multianewarray for 3 dimensions +JRT_ENTRY(void, OptoRuntime::multianewarray3_C(klassOopDesc* elem_type, int len1, int len2, int len3, JavaThread *thread)) +#ifndef PRODUCT + SharedRuntime::_multi3_ctr++; // multianewarray for 1 dimension +#endif + assert(check_compiled_frame(thread), "incorrect caller"); + assert(oop(elem_type)->is_klass(), "not a class"); + jint dims[3]; + dims[0] = len1; + dims[1] = len2; + dims[2] = len3; + oop obj = arrayKlass::cast(elem_type)->multi_allocate(3, dims, THREAD); + deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION); + thread->set_vm_result(obj); +JRT_END + +// multianewarray for 4 dimensions +JRT_ENTRY(void, OptoRuntime::multianewarray4_C(klassOopDesc* elem_type, int len1, int len2, int len3, int len4, JavaThread *thread)) +#ifndef PRODUCT + SharedRuntime::_multi4_ctr++; // multianewarray for 1 dimension +#endif + assert(check_compiled_frame(thread), "incorrect caller"); + assert(oop(elem_type)->is_klass(), "not a class"); + jint dims[4]; + dims[0] = len1; + dims[1] = len2; + dims[2] = len3; + dims[3] = len4; + oop obj = arrayKlass::cast(elem_type)->multi_allocate(4, dims, THREAD); + deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION); + thread->set_vm_result(obj); +JRT_END + +// multianewarray for 5 dimensions +JRT_ENTRY(void, OptoRuntime::multianewarray5_C(klassOopDesc* elem_type, int len1, int len2, int len3, int len4, int len5, JavaThread *thread)) +#ifndef PRODUCT + SharedRuntime::_multi5_ctr++; // multianewarray for 1 dimension +#endif + assert(check_compiled_frame(thread), "incorrect caller"); + assert(oop(elem_type)->is_klass(), "not a class"); + jint dims[5]; + dims[0] = len1; + dims[1] = len2; + dims[2] = len3; + dims[3] = len4; + dims[4] = len5; + oop obj = arrayKlass::cast(elem_type)->multi_allocate(5, dims, THREAD); + deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION); + thread->set_vm_result(obj); +JRT_END + +const TypeFunc *OptoRuntime::new_instance_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // Klass to be allocated + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields); + + // create result type (range) + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeRawPtr::NOTNULL; // Returned oop + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); + + return TypeFunc::make(domain, range); +} + + +const TypeFunc *OptoRuntime::athrow_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // Klass to be allocated + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields); + + // create result type (range) + fields = TypeTuple::fields(0); + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0, fields); + + return TypeFunc::make(domain, range); +} + + +const TypeFunc *OptoRuntime::new_array_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // element klass + fields[TypeFunc::Parms+1] = TypeInt::INT; // array size + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2, fields); + + // create result type (range) + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeRawPtr::NOTNULL; // Returned oop + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); + + return TypeFunc::make(domain, range); +} + +const TypeFunc *OptoRuntime::multianewarray_Type(int ndim) { + // create input type (domain) + const int nargs = ndim + 1; + const Type **fields = TypeTuple::fields(nargs); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // element klass + for( int i = 1; i < nargs; i++ ) + fields[TypeFunc::Parms + i] = TypeInt::INT; // array size + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+nargs, fields); + + // create result type (range) + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeRawPtr::NOTNULL; // Returned oop + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); + + return TypeFunc::make(domain, range); +} + +const TypeFunc *OptoRuntime::multianewarray2_Type() { + return multianewarray_Type(2); +} + +const TypeFunc *OptoRuntime::multianewarray3_Type() { + return multianewarray_Type(3); +} + +const TypeFunc *OptoRuntime::multianewarray4_Type() { + return multianewarray_Type(4); +} + +const TypeFunc *OptoRuntime::multianewarray5_Type() { + return multianewarray_Type(5); +} + +const TypeFunc *OptoRuntime::uncommon_trap_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(1); + // symbolOop name of class to be loaded + fields[TypeFunc::Parms+0] = TypeInt::INT; + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields); + + // create result type (range) + fields = TypeTuple::fields(0); + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0, fields); + + return TypeFunc::make(domain, range); +} + +# ifdef ENABLE_ZAP_DEAD_LOCALS +// Type used for stub generation for zap_dead_locals. +// No inputs or outputs +const TypeFunc *OptoRuntime::zap_dead_locals_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(0); + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms,fields); + + // create result type (range) + fields = TypeTuple::fields(0); + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms,fields); + + return TypeFunc::make(domain,range); +} +# endif + + +//----------------------------------------------------------------------------- +// Monitor Handling +const TypeFunc *OptoRuntime::complete_monitor_enter_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // Object to be Locked + fields[TypeFunc::Parms+1] = TypeRawPtr::BOTTOM; // Address of stack location for lock + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2,fields); + + // create result type (range) + fields = TypeTuple::fields(0); + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields); + + return TypeFunc::make(domain,range); +} + + +//----------------------------------------------------------------------------- +const TypeFunc *OptoRuntime::complete_monitor_exit_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // Object to be Locked + fields[TypeFunc::Parms+1] = TypeRawPtr::BOTTOM; // Address of stack location for lock + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2,fields); + + // create result type (range) + fields = TypeTuple::fields(0); + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields); + + return TypeFunc::make(domain,range); +} + +const TypeFunc* OptoRuntime::flush_windows_Type() { + // create input type (domain) + const Type** fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = NULL; // void + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms, fields); + + // create result type + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = NULL; // void + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms, fields); + + return TypeFunc::make(domain, range); +} + +const TypeFunc* OptoRuntime::l2f_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = TypeLong::LONG; + fields[TypeFunc::Parms+1] = Type::HALF; + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2, fields); + + // create result type (range) + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = Type::FLOAT; + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); + + return TypeFunc::make(domain, range); +} + +const TypeFunc* OptoRuntime::modf_Type() { + const Type **fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = Type::FLOAT; + fields[TypeFunc::Parms+1] = Type::FLOAT; + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2, fields); + + // create result type (range) + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = Type::FLOAT; + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); + + return TypeFunc::make(domain, range); +} + +const TypeFunc *OptoRuntime::Math_D_D_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(2); + // symbolOop name of class to be loaded + fields[TypeFunc::Parms+0] = Type::DOUBLE; + fields[TypeFunc::Parms+1] = Type::HALF; + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2, fields); + + // create result type (range) + fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = Type::DOUBLE; + fields[TypeFunc::Parms+1] = Type::HALF; + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+2, fields); + + return TypeFunc::make(domain, range); +} + +const TypeFunc* OptoRuntime::Math_DD_D_Type() { + const Type **fields = TypeTuple::fields(4); + fields[TypeFunc::Parms+0] = Type::DOUBLE; + fields[TypeFunc::Parms+1] = Type::HALF; + fields[TypeFunc::Parms+2] = Type::DOUBLE; + fields[TypeFunc::Parms+3] = Type::HALF; + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+4, fields); + + // create result type (range) + fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = Type::DOUBLE; + fields[TypeFunc::Parms+1] = Type::HALF; + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+2, fields); + + return TypeFunc::make(domain, range); +} + +//-------------- currentTimeMillis + +const TypeFunc* OptoRuntime::current_time_millis_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(0); + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+0, fields); + + // create result type (range) + fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = TypeLong::LONG; + fields[TypeFunc::Parms+1] = Type::HALF; + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+2, fields); + + return TypeFunc::make(domain, range); +} + +// arraycopy stub variations: +enum ArrayCopyType { + ac_fast, // void(ptr, ptr, size_t) + ac_checkcast, // int(ptr, ptr, size_t, size_t, ptr) + ac_slow, // void(ptr, int, ptr, int, int) + ac_generic // int(ptr, int, ptr, int, int) +}; + +static const TypeFunc* make_arraycopy_Type(ArrayCopyType act) { + // create input type (domain) + int num_args = (act == ac_fast ? 3 : 5); + int num_size_args = (act == ac_fast ? 1 : act == ac_checkcast ? 2 : 0); + int argcnt = num_args; + LP64_ONLY(argcnt += num_size_args); // halfwords for lengths + const Type** fields = TypeTuple::fields(argcnt); + int argp = TypeFunc::Parms; + fields[argp++] = TypePtr::NOTNULL; // src + if (num_size_args == 0) { + fields[argp++] = TypeInt::INT; // src_pos + } + fields[argp++] = TypePtr::NOTNULL; // dest + if (num_size_args == 0) { + fields[argp++] = TypeInt::INT; // dest_pos + fields[argp++] = TypeInt::INT; // length + } + while (num_size_args-- > 0) { + fields[argp++] = TypeX_X; // size in whatevers (size_t) + LP64_ONLY(fields[argp++] = Type::HALF); // other half of long length + } + if (act == ac_checkcast) { + fields[argp++] = TypePtr::NOTNULL; // super_klass + } + assert(argp == TypeFunc::Parms+argcnt, "correct decoding of act"); + const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); + + // create result type if needed + int retcnt = (act == ac_checkcast || act == ac_generic ? 1 : 0); + fields = TypeTuple::fields(1); + if (retcnt == 0) + fields[TypeFunc::Parms+0] = NULL; // void + else + fields[TypeFunc::Parms+0] = TypeInt::INT; // status result, if needed + const TypeTuple* range = TypeTuple::make(TypeFunc::Parms+retcnt, fields); + return TypeFunc::make(domain, range); +} + +const TypeFunc* OptoRuntime::fast_arraycopy_Type() { + // This signature is simple: Two base pointers and a size_t. + return make_arraycopy_Type(ac_fast); +} + +const TypeFunc* OptoRuntime::checkcast_arraycopy_Type() { + // An extension of fast_arraycopy_Type which adds type checking. + return make_arraycopy_Type(ac_checkcast); +} + +const TypeFunc* OptoRuntime::slow_arraycopy_Type() { + // This signature is exactly the same as System.arraycopy. + // There are no intptr_t (int/long) arguments. + return make_arraycopy_Type(ac_slow); +} + +const TypeFunc* OptoRuntime::generic_arraycopy_Type() { + // This signature is like System.arraycopy, except that it returns status. + return make_arraycopy_Type(ac_generic); +} + + +//------------- Interpreter state access for on stack replacement +const TypeFunc* OptoRuntime::osr_end_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeRawPtr::BOTTOM; // OSR temp buf + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields); + + // create result type + fields = TypeTuple::fields(1); + // fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // locked oop + fields[TypeFunc::Parms+0] = NULL; // void + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms, fields); + return TypeFunc::make(domain, range); +} + +//-------------- methodData update helpers + +const TypeFunc* OptoRuntime::profile_receiver_type_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = TypeAryPtr::NOTNULL; // methodData pointer + fields[TypeFunc::Parms+1] = TypeInstPtr::BOTTOM; // receiver oop + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2, fields); + + // create result type + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = NULL; // void + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms, fields); + return TypeFunc::make(domain,range); +} + +JRT_LEAF(void, OptoRuntime::profile_receiver_type_C(DataLayout* data, oopDesc* receiver)) + if (receiver == NULL) return; + klassOop receiver_klass = receiver->klass(); + + intptr_t* mdp = ((intptr_t*)(data)) + DataLayout::header_size_in_cells(); + int empty_row = -1; // free row, if any is encountered + + // ReceiverTypeData* vc = new ReceiverTypeData(mdp); + for (uint row = 0; row < ReceiverTypeData::row_limit(); row++) { + // if (vc->receiver(row) == receiver_klass) + int receiver_off = ReceiverTypeData::receiver_cell_index(row); + intptr_t row_recv = *(mdp + receiver_off); + if (row_recv == (intptr_t) receiver_klass) { + // vc->set_receiver_count(row, vc->receiver_count(row) + DataLayout::counter_increment); + int count_off = ReceiverTypeData::receiver_count_cell_index(row); + *(mdp + count_off) += DataLayout::counter_increment; + return; + } else if (row_recv == 0) { + // else if (vc->receiver(row) == NULL) + empty_row = (int) row; + } + } + + if (empty_row != -1) { + int receiver_off = ReceiverTypeData::receiver_cell_index(empty_row); + // vc->set_receiver(empty_row, receiver_klass); + *(mdp + receiver_off) = (intptr_t) receiver_klass; + // vc->set_receiver_count(empty_row, DataLayout::counter_increment); + int count_off = ReceiverTypeData::receiver_count_cell_index(empty_row); + *(mdp + count_off) = DataLayout::counter_increment; + } +JRT_END + +//----------------------------------------------------------------------------- +// implicit exception support. + +static void report_null_exception_in_code_cache(address exception_pc) { + ResourceMark rm; + CodeBlob* n = CodeCache::find_blob(exception_pc); + if (n != NULL) { + tty->print_cr("#"); + tty->print_cr("# HotSpot Runtime Error, null exception in generated code"); + tty->print_cr("#"); + tty->print_cr("# pc where exception happened = " INTPTR_FORMAT, exception_pc); + + if (n->is_nmethod()) { + methodOop method = ((nmethod*)n)->method(); + tty->print_cr("# Method where it happened %s.%s ", Klass::cast(method->method_holder())->name()->as_C_string(), method->name()->as_C_string()); + tty->print_cr("#"); + if (ShowMessageBoxOnError && UpdateHotSpotCompilerFileOnError) { + const char* title = "HotSpot Runtime Error"; + const char* question = "Do you want to exclude compilation of this method in future runs?"; + if (os::message_box(title, question)) { + CompilerOracle::append_comment_to_file(""); + CompilerOracle::append_comment_to_file("Null exception in compiled code resulted in the following exclude"); + CompilerOracle::append_comment_to_file(""); + CompilerOracle::append_exclude_to_file(method); + tty->print_cr("#"); + tty->print_cr("# %s has been updated to exclude the specified method", CompileCommandFile); + tty->print_cr("#"); + } + } + fatal("Implicit null exception happened in compiled method"); + } else { + n->print(); + fatal("Implicit null exception happened in generated stub"); + } + } + fatal("Implicit null exception at wrong place"); +} + + +//------------------------------------------------------------------------------------- +// register policy + +bool OptoRuntime::is_callee_saved_register(MachRegisterNumbers reg) { + assert(reg >= 0 && reg < _last_Mach_Reg, "must be a machine register"); + switch (register_save_policy[reg]) { + case 'C': return false; //SOC + case 'E': return true ; //SOE + case 'N': return false; //NS + case 'A': return false; //AS + } + ShouldNotReachHere(); + return false; +} + +//----------------------------------------------------------------------- +// Exceptions +// + +static void trace_exception(oop exception_oop, address exception_pc, const char* msg) PRODUCT_RETURN; + +// The method is an entry that is always called by a C++ method not +// directly from compiled code. Compiled code will call the C++ method following. +// We can't allow async exception to be installed during exception processing. +JRT_ENTRY_NO_ASYNC(address, OptoRuntime::handle_exception_C_helper(JavaThread* thread, nmethod* &nm)) + + // Do not confuse exception_oop with pending_exception. The exception_oop + // is only used to pass arguments into the method. Not for general + // exception handling. DO NOT CHANGE IT to use pending_exception, since + // the runtime stubs checks this on exit. + assert(thread->exception_oop() != NULL, "exception oop is found"); + address handler_address = NULL; + + Handle exception(thread, thread->exception_oop()); + + if (TraceExceptions) { + trace_exception(exception(), thread->exception_pc(), ""); + } + // for AbortVMOnException flag + NOT_PRODUCT(Exceptions::debug_check_abort(exception)); + + #ifdef ASSERT + if (!(exception->is_a(SystemDictionary::throwable_klass()))) { + // should throw an exception here + ShouldNotReachHere(); + } + #endif + + + // new exception handling: this method is entered only from adapters + // exceptions from compiled java methods are handled in compiled code + // using rethrow node + + address pc = thread->exception_pc(); + nm = CodeCache::find_nmethod(pc); + assert(nm != NULL, "No NMethod found"); + if (nm->is_native_method()) { + fatal("Native mathod should not have path to exception handling"); + } else { + // we are switching to old paradigm: search for exception handler in caller_frame + // instead in exception handler of caller_frame.sender() + + if (JvmtiExport::can_post_exceptions()) { + // "Full-speed catching" is not necessary here, + // since we're notifying the VM on every catch. + // Force deoptimization and the rest of the lookup + // will be fine. + deoptimize_caller_frame(thread, true); + } + + // Check the stack guard pages. If enabled, look for handler in this frame; + // otherwise, forcibly unwind the frame. + // + // 4826555: use default current sp for reguard_stack instead of &nm: it's more accurate. + bool force_unwind = !thread->reguard_stack(); + bool deopting = false; + if (nm->is_deopt_pc(pc)) { + deopting = true; + RegisterMap map(thread, false); + frame deoptee = thread->last_frame().sender(&map); + assert(deoptee.is_deoptimized_frame(), "must be deopted"); + // Adjust the pc back to the original throwing pc + pc = deoptee.pc(); + } + + // If we are forcing an unwind because of stack overflow then deopt is + // irrelevant sice we are throwing the frame away anyway. + + if (deopting && !force_unwind) { + handler_address = SharedRuntime::deopt_blob()->unpack_with_exception(); + } else { + + handler_address = + force_unwind ? NULL : nm->handler_for_exception_and_pc(exception, pc); + + if (handler_address == NULL) { + handler_address = SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, force_unwind, true); + assert (handler_address != NULL, "must have compiled handler"); + // Update the exception cache only when the unwind was not forced. + if (!force_unwind) { + nm->add_handler_for_exception_and_pc(exception,pc,handler_address); + } + } else { + assert(handler_address == SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, force_unwind, true), "Must be the same"); + } + } + + thread->set_exception_pc(pc); + thread->set_exception_handler_pc(handler_address); + thread->set_exception_stack_size(0); + } + + // Restore correct return pc. Was saved above. + thread->set_exception_oop(exception()); + return handler_address; + +JRT_END + +// We are entering here from exception_blob +// If there is a compiled exception handler in this method, we will continue there; +// otherwise we will unwind the stack and continue at the caller of top frame method +// Note we enter without the usual JRT wrapper. We will call a helper routine that +// will do the normal VM entry. We do it this way so that we can see if the nmethod +// we looked up the handler for has been deoptimized in the meantime. If it has been +// we must not use the handler and instread return the deopt blob. +address OptoRuntime::handle_exception_C(JavaThread* thread) { +// +// We are in Java not VM and in debug mode we have a NoHandleMark +// +#ifndef PRODUCT + SharedRuntime::_find_handler_ctr++; // find exception handler +#endif + debug_only(NoHandleMark __hm;) + nmethod* nm = NULL; + address handler_address = NULL; + { + // Enter the VM + + ResetNoHandleMark rnhm; + handler_address = handle_exception_C_helper(thread, nm); + } + + // Back in java: Use no oops, DON'T safepoint + + // Now check to see if the handler we are returning is in a now + // deoptimized frame + + if (nm != NULL) { + RegisterMap map(thread, false); + frame caller = thread->last_frame().sender(&map); +#ifdef ASSERT + assert(caller.is_compiled_frame(), "must be"); +#endif // ASSERT + if (caller.is_deoptimized_frame()) { + handler_address = SharedRuntime::deopt_blob()->unpack_with_exception(); + } + } + return handler_address; +} + +//------------------------------rethrow---------------------------------------- +// We get here after compiled code has executed a 'RethrowNode'. The callee +// is either throwing or rethrowing an exception. The callee-save registers +// have been restored, synchronized objects have been unlocked and the callee +// stack frame has been removed. The return address was passed in. +// Exception oop is passed as the 1st argument. This routine is then called +// from the stub. On exit, we know where to jump in the caller's code. +// After this C code exits, the stub will pop his frame and end in a jump +// (instead of a return). We enter the caller's default handler. +// +// This must be JRT_LEAF: +// - caller will not change its state as we cannot block on exit, +// therefore raw_exception_handler_for_return_address is all it takes +// to handle deoptimized blobs +// +// However, there needs to be a safepoint check in the middle! So compiled +// safepoints are completely watertight. +// +// Thus, it cannot be a leaf since it contains the No_GC_Verifier. +// +// *THIS IS NOT RECOMMENDED PROGRAMMING STYLE* +// +address OptoRuntime::rethrow_C(oopDesc* exception, JavaThread* thread, address ret_pc) { +#ifndef PRODUCT + SharedRuntime::_rethrow_ctr++; // count rethrows +#endif + assert (exception != NULL, "should have thrown a NULLPointerException"); +#ifdef ASSERT + if (!(exception->is_a(SystemDictionary::throwable_klass()))) { + // should throw an exception here + ShouldNotReachHere(); + } +#endif + + thread->set_vm_result(exception); + // Frame not compiled (handles deoptimization blob) + return SharedRuntime::raw_exception_handler_for_return_address(ret_pc); +} + + +const TypeFunc *OptoRuntime::rethrow_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // Exception oop + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1,fields); + + // create result type (range) + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // Exception oop + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); + + return TypeFunc::make(domain, range); +} + + +void OptoRuntime::deoptimize_caller_frame(JavaThread *thread, bool doit) { + // Deoptimize frame + if (doit) { + // Called from within the owner thread, so no need for safepoint + RegisterMap reg_map(thread); + frame stub_frame = thread->last_frame(); + assert(stub_frame.is_runtime_frame() || exception_blob()->contains(stub_frame.pc()), "sanity check"); + frame caller_frame = stub_frame.sender(®_map); + + VM_DeoptimizeFrame deopt(thread, caller_frame.id()); + VMThread::execute(&deopt); + } +} + + +const TypeFunc *OptoRuntime::register_finalizer_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // oop; Receiver + // // The JavaThread* is passed to each routine as the last argument + // fields[TypeFunc::Parms+1] = TypeRawPtr::NOTNULL; // JavaThread *; Executing thread + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1,fields); + + // create result type (range) + fields = TypeTuple::fields(0); + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields); + + return TypeFunc::make(domain,range); +} + + +//----------------------------------------------------------------------------- +// Dtrace support. entry and exit probes have the same signature +const TypeFunc *OptoRuntime::dtrace_method_entry_exit_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = TypeRawPtr::BOTTOM; // Thread-local storage + fields[TypeFunc::Parms+1] = TypeInstPtr::NOTNULL; // methodOop; Method we are entering + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2,fields); + + // create result type (range) + fields = TypeTuple::fields(0); + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields); + + return TypeFunc::make(domain,range); +} + +const TypeFunc *OptoRuntime::dtrace_object_alloc_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = TypeRawPtr::BOTTOM; // Thread-local storage + fields[TypeFunc::Parms+1] = TypeInstPtr::NOTNULL; // oop; newly allocated object + + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2,fields); + + // create result type (range) + fields = TypeTuple::fields(0); + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields); + + return TypeFunc::make(domain,range); +} + + +JRT_ENTRY_NO_ASYNC(void, OptoRuntime::register_finalizer(oopDesc* obj, JavaThread* thread)) + assert(obj->is_oop(), "must be a valid oop"); + assert(obj->klass()->klass_part()->has_finalizer(), "shouldn't be here otherwise"); + instanceKlass::register_finalizer(instanceOop(obj), CHECK); +JRT_END + +//----------------------------------------------------------------------------- + +NamedCounter * volatile OptoRuntime::_named_counters = NULL; + +// +// dump the collected NamedCounters. +// +void OptoRuntime::print_named_counters() { + int total_lock_count = 0; + int eliminated_lock_count = 0; + + NamedCounter* c = _named_counters; + while (c) { + if (c->tag() == NamedCounter::LockCounter || c->tag() == NamedCounter::EliminatedLockCounter) { + int count = c->count(); + if (count > 0) { + bool eliminated = c->tag() == NamedCounter::EliminatedLockCounter; + if (Verbose) { + tty->print_cr("%d %s%s", count, c->name(), eliminated ? " (eliminated)" : ""); + } + total_lock_count += count; + if (eliminated) { + eliminated_lock_count += count; + } + } + } else if (c->tag() == NamedCounter::BiasedLockingCounter) { + BiasedLockingCounters* blc = ((BiasedLockingNamedCounter*)c)->counters(); + if (blc->nonzero()) { + tty->print_cr("%s", c->name()); + blc->print_on(tty); + } + } + c = c->next(); + } + if (total_lock_count > 0) { + tty->print_cr("dynamic locks: %d", total_lock_count); + if (eliminated_lock_count) { + tty->print_cr("eliminated locks: %d (%d%%)", eliminated_lock_count, + (int)(eliminated_lock_count * 100.0 / total_lock_count)); + } + } +} + +// +// Allocate a new NamedCounter. The JVMState is used to generate the +// name which consists of method@line for the inlining tree. +// + +NamedCounter* OptoRuntime::new_named_counter(JVMState* youngest_jvms, NamedCounter::CounterTag tag) { + int max_depth = youngest_jvms->depth(); + + // Visit scopes from youngest to oldest. + bool first = true; + stringStream st; + for (int depth = max_depth; depth >= 1; depth--) { + JVMState* jvms = youngest_jvms->of_depth(depth); + ciMethod* m = jvms->has_method() ? jvms->method() : NULL; + if (!first) { + st.print(" "); + } else { + first = false; + } + int bci = jvms->bci(); + if (bci < 0) bci = 0; + st.print("%s.%s@%d", m->holder()->name()->as_utf8(), m->name()->as_utf8(), bci); + // To print linenumbers instead of bci use: m->line_number_from_bci(bci) + } + NamedCounter* c; + if (tag == NamedCounter::BiasedLockingCounter) { + c = new BiasedLockingNamedCounter(strdup(st.as_string())); + } else { + c = new NamedCounter(strdup(st.as_string()), tag); + } + + // atomically add the new counter to the head of the list. We only + // add counters so this is safe. + NamedCounter* head; + do { + head = _named_counters; + c->set_next(head); + } while (Atomic::cmpxchg_ptr(c, &_named_counters, head) != head); + return c; +} + +//----------------------------------------------------------------------------- +// Non-product code +#ifndef PRODUCT + +int trace_exception_counter = 0; +static void trace_exception(oop exception_oop, address exception_pc, const char* msg) { + ttyLocker ttyl; + trace_exception_counter++; + tty->print("%d [Exception (%s): ", trace_exception_counter, msg); + exception_oop->print_value(); + tty->print(" in "); + CodeBlob* blob = CodeCache::find_blob(exception_pc); + if (blob->is_nmethod()) { + ((nmethod*)blob)->method()->print_value(); + } else if (blob->is_runtime_stub()) { + tty->print(""); + } else { + tty->print(""); + } + tty->print(" at " INTPTR_FORMAT, exception_pc); + tty->print_cr("]"); +} + +#endif // PRODUCT + + +# ifdef ENABLE_ZAP_DEAD_LOCALS +// Called from call sites in compiled code with oop maps (actually safepoints) +// Zaps dead locals in first java frame. +// Is entry because may need to lock to generate oop maps +// Currently, only used for compiler frames, but someday may be used +// for interpreter frames, too. + +int OptoRuntime::ZapDeadCompiledLocals_count = 0; + +// avoid pointers to member funcs with these helpers +static bool is_java_frame( frame* f) { return f->is_java_frame(); } +static bool is_native_frame(frame* f) { return f->is_native_frame(); } + + +void OptoRuntime::zap_dead_java_or_native_locals(JavaThread* thread, + bool (*is_this_the_right_frame_to_zap)(frame*)) { + assert(JavaThread::current() == thread, "is this needed?"); + + if ( !ZapDeadCompiledLocals ) return; + + bool skip = false; + + if ( ZapDeadCompiledLocalsFirst == 0 ) ; // nothing special + else if ( ZapDeadCompiledLocalsFirst > ZapDeadCompiledLocals_count ) skip = true; + else if ( ZapDeadCompiledLocalsFirst == ZapDeadCompiledLocals_count ) + warning("starting zapping after skipping"); + + if ( ZapDeadCompiledLocalsLast == -1 ) ; // nothing special + else if ( ZapDeadCompiledLocalsLast < ZapDeadCompiledLocals_count ) skip = true; + else if ( ZapDeadCompiledLocalsLast == ZapDeadCompiledLocals_count ) + warning("about to zap last zap"); + + ++ZapDeadCompiledLocals_count; // counts skipped zaps, too + + if ( skip ) return; + + // find java frame and zap it + + for (StackFrameStream sfs(thread); !sfs.is_done(); sfs.next()) { + if (is_this_the_right_frame_to_zap(sfs.current()) ) { + sfs.current()->zap_dead_locals(thread, sfs.register_map()); + return; + } + } + warning("no frame found to zap in zap_dead_Java_locals_C"); +} + +JRT_LEAF(void, OptoRuntime::zap_dead_Java_locals_C(JavaThread* thread)) + zap_dead_java_or_native_locals(thread, is_java_frame); +JRT_END + +// The following does not work because for one thing, the +// thread state is wrong; it expects java, but it is native. +// Also, the invarients in a native stub are different and +// I'm not sure it is safe to have a MachCalRuntimeDirectNode +// in there. +// So for now, we do not zap in native stubs. + +JRT_LEAF(void, OptoRuntime::zap_dead_native_locals_C(JavaThread* thread)) + zap_dead_java_or_native_locals(thread, is_native_frame); +JRT_END + +# endif diff --git a/hotspot/src/share/vm/opto/runtime.hpp b/hotspot/src/share/vm/opto/runtime.hpp new file mode 100644 index 00000000000..50f11712046 --- /dev/null +++ b/hotspot/src/share/vm/opto/runtime.hpp @@ -0,0 +1,289 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//------------------------------OptoRuntime------------------------------------ +// Opto compiler runtime routines +// +// These are all generated from Ideal graphs. They are called with the +// Java calling convention. Internally they call C++. They are made once at +// startup time and Opto compiles calls to them later. +// Things are broken up into quads: the signature they will be called with, +// the address of the generated code, the corresponding C++ code and an +// nmethod. + +// The signature (returned by "xxx_Type()") is used at startup time by the +// Generator to make the generated code "xxx_Java". Opto compiles calls +// to the generated code "xxx_Java". When the compiled code gets executed, +// it calls the C++ code "xxx_C". The generated nmethod is saved in the +// CodeCache. Exception handlers use the nmethod to get the callee-save +// register OopMaps. +class CallInfo; + +// +// NamedCounters are tagged counters which can be used for profiling +// code in various ways. Currently they are used by the lock coarsening code +// + +class NamedCounter : public CHeapObj { +public: + enum CounterTag { + NoTag, + LockCounter, + EliminatedLockCounter, + BiasedLockingCounter + }; + +private: + const char * _name; + int _count; + CounterTag _tag; + NamedCounter* _next; + + public: + NamedCounter(const char *n, CounterTag tag = NoTag): + _name(n), + _count(0), + _next(NULL), + _tag(tag) {} + + const char * name() const { return _name; } + int count() const { return _count; } + address addr() { return (address)&_count; } + CounterTag tag() const { return _tag; } + void set_tag(CounterTag tag) { _tag = tag; } + + NamedCounter* next() const { return _next; } + void set_next(NamedCounter* next) { + assert(_next == NULL, "already set"); + _next = next; + } + +}; + +class BiasedLockingNamedCounter : public NamedCounter { + private: + BiasedLockingCounters _counters; + + public: + BiasedLockingNamedCounter(const char *n) : + NamedCounter(n, BiasedLockingCounter), _counters() {} + + BiasedLockingCounters* counters() { return &_counters; } +}; + +typedef const TypeFunc*(*TypeFunc_generator)(); + +class OptoRuntime : public AllStatic { + friend class Matcher; // allow access to stub names + + private: + // define stubs + static address generate_stub(ciEnv* ci_env, TypeFunc_generator gen, address C_function, const char *name, int is_fancy_jump, bool pass_tls, bool save_arguments, bool return_pc); + + // References to generated stubs + static address _new_instance_Java; + static address _new_array_Java; + static address _multianewarray2_Java; + static address _multianewarray3_Java; + static address _multianewarray4_Java; + static address _multianewarray5_Java; + static address _vtable_must_compile_Java; + static address _complete_monitor_locking_Java; + static address _rethrow_Java; + + static address _slow_arraycopy_Java; + static address _register_finalizer_Java; + +# ifdef ENABLE_ZAP_DEAD_LOCALS + static address _zap_dead_Java_locals_Java; + static address _zap_dead_native_locals_Java; +# endif + + + // + // Implementation of runtime methods + // ================================= + + // Allocate storage for a Java instance. + static void new_instance_C(klassOopDesc* instance_klass, JavaThread *thread); + + // Allocate storage for a objArray or typeArray + static void new_array_C(klassOopDesc* array_klass, int len, JavaThread *thread); + + // Post-allocation step for implementing ReduceInitialCardMarks: + static void do_eager_card_mark(JavaThread* thread); + + // Allocate storage for a multi-dimensional arrays + // Note: needs to be fixed for arbitrary number of dimensions + static void multianewarray2_C(klassOopDesc* klass, int len1, int len2, JavaThread *thread); + static void multianewarray3_C(klassOopDesc* klass, int len1, int len2, int len3, JavaThread *thread); + static void multianewarray4_C(klassOopDesc* klass, int len1, int len2, int len3, int len4, JavaThread *thread); + static void multianewarray5_C(klassOopDesc* klass, int len1, int len2, int len3, int len4, int len5, JavaThread *thread); + +public: + // Slow-path Locking and Unlocking + static void complete_monitor_locking_C(oopDesc* obj, BasicLock* lock, JavaThread* thread); + static void complete_monitor_unlocking_C(oopDesc* obj, BasicLock* lock); + +private: + + // Implicit exception support + static void throw_null_exception_C(JavaThread* thread); + + // Exception handling + static address handle_exception_C (JavaThread* thread); + static address handle_exception_C_helper(JavaThread* thread, nmethod*& nm); + static address rethrow_C (oopDesc* exception, JavaThread *thread, address return_pc ); + static void deoptimize_caller_frame (JavaThread *thread, bool doit); + + // CodeBlob support + // =================================================================== + + static ExceptionBlob* _exception_blob; + static void generate_exception_blob(); + + static void register_finalizer(oopDesc* obj, JavaThread* thread); + + // zaping dead locals, either from Java frames or from native frames +# ifdef ENABLE_ZAP_DEAD_LOCALS + static void zap_dead_Java_locals_C( JavaThread* thread); + static void zap_dead_native_locals_C( JavaThread* thread); + + static void zap_dead_java_or_native_locals( JavaThread*, bool (*)(frame*)); + + public: + static int ZapDeadCompiledLocals_count; + +# endif + + + public: + + static bool is_callee_saved_register(MachRegisterNumbers reg); + + // One time only generate runtime code stubs + static void generate(ciEnv* env); + + // Returns the name of a stub + static const char* stub_name(address entry); + + // access to runtime stubs entry points for java code + static address new_instance_Java() { return _new_instance_Java; } + static address new_array_Java() { return _new_array_Java; } + static address multianewarray2_Java() { return _multianewarray2_Java; } + static address multianewarray3_Java() { return _multianewarray3_Java; } + static address multianewarray4_Java() { return _multianewarray4_Java; } + static address multianewarray5_Java() { return _multianewarray5_Java; } + static address vtable_must_compile_stub() { return _vtable_must_compile_Java; } + static address complete_monitor_locking_Java() { return _complete_monitor_locking_Java; } + + static address slow_arraycopy_Java() { return _slow_arraycopy_Java; } + static address register_finalizer_Java() { return _register_finalizer_Java; } + + +# ifdef ENABLE_ZAP_DEAD_LOCALS + static address zap_dead_locals_stub(bool is_native) { return is_native + ? _zap_dead_native_locals_Java + : _zap_dead_Java_locals_Java; } + static MachNode* node_to_call_zap_dead_locals(Node* n, int block_num, bool is_native); +# endif + + static ExceptionBlob* exception_blob() { return _exception_blob; } + + // Leaf routines helping with method data update + static void profile_receiver_type_C(DataLayout* data, oopDesc* receiver); + + // Implicit exception support + static void throw_div0_exception_C (JavaThread* thread); + static void throw_stack_overflow_error_C(JavaThread* thread); + + // Exception handling + static address rethrow_stub() { return _rethrow_Java; } + + + // Type functions + // ====================================================== + + static const TypeFunc* new_instance_Type(); // object allocation (slow case) + static const TypeFunc* new_array_Type (); // [a]newarray (slow case) + static const TypeFunc* multianewarray_Type(int ndim); // multianewarray + static const TypeFunc* multianewarray2_Type(); // multianewarray + static const TypeFunc* multianewarray3_Type(); // multianewarray + static const TypeFunc* multianewarray4_Type(); // multianewarray + static const TypeFunc* multianewarray5_Type(); // multianewarray + static const TypeFunc* complete_monitor_enter_Type(); + static const TypeFunc* complete_monitor_exit_Type(); + static const TypeFunc* uncommon_trap_Type(); + static const TypeFunc* athrow_Type(); + static const TypeFunc* rethrow_Type(); + static const TypeFunc* Math_D_D_Type(); // sin,cos & friends + static const TypeFunc* Math_DD_D_Type(); // mod,pow & friends + static const TypeFunc* modf_Type(); + static const TypeFunc* l2f_Type(); + static const TypeFunc* current_time_millis_Type(); + + static const TypeFunc* flush_windows_Type(); + + // arraycopy routine types + static const TypeFunc* fast_arraycopy_Type(); // bit-blasters + static const TypeFunc* checkcast_arraycopy_Type(); + static const TypeFunc* generic_arraycopy_Type(); + static const TypeFunc* slow_arraycopy_Type(); // the full routine + + // leaf on stack replacement interpreter accessor types + static const TypeFunc* osr_end_Type(); + + // leaf methodData routine types + static const TypeFunc* profile_receiver_type_Type(); + + // leaf on stack replacement interpreter accessor types + static const TypeFunc* fetch_int_Type(); + static const TypeFunc* fetch_long_Type(); + static const TypeFunc* fetch_float_Type(); + static const TypeFunc* fetch_double_Type(); + static const TypeFunc* fetch_oop_Type(); + static const TypeFunc* fetch_monitor_Type(); + + static const TypeFunc* register_finalizer_Type(); + + // Dtrace support + static const TypeFunc* dtrace_method_entry_exit_Type(); + static const TypeFunc* dtrace_object_alloc_Type(); + +# ifdef ENABLE_ZAP_DEAD_LOCALS + static const TypeFunc* zap_dead_locals_Type(); +# endif + + private: + static NamedCounter * volatile _named_counters; + + public: + // helper function which creates a named counter labeled with the + // if they are available + static NamedCounter* new_named_counter(JVMState* jvms, NamedCounter::CounterTag tag); + + // dumps all the named counters + static void print_named_counters(); + +}; diff --git a/hotspot/src/share/vm/opto/split_if.cpp b/hotspot/src/share/vm/opto/split_if.cpp new file mode 100644 index 00000000000..130b2667513 --- /dev/null +++ b/hotspot/src/share/vm/opto/split_if.cpp @@ -0,0 +1,536 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_split_if.cpp.incl" + + +//------------------------------split_thru_region------------------------------ +// Split Node 'n' through merge point. +Node *PhaseIdealLoop::split_thru_region( Node *n, Node *region ) { + uint wins = 0; + assert( n->is_CFG(), "" ); + assert( region->is_Region(), "" ); + Node *r = new (C, region->req()) RegionNode( region->req() ); + IdealLoopTree *loop = get_loop( n ); + for( uint i = 1; i < region->req(); i++ ) { + Node *x = n->clone(); + Node *in0 = n->in(0); + if( in0->in(0) == region ) x->set_req( 0, in0->in(i) ); + for( uint j = 1; j < n->req(); j++ ) { + Node *in = n->in(j); + if( get_ctrl(in) == region ) + x->set_req( j, in->in(i) ); + } + _igvn.register_new_node_with_optimizer(x); + set_loop(x, loop); + set_idom(x, x->in(0), dom_depth(x->in(0))+1); + r->init_req(i, x); + } + + // Record region + r->set_req(0,region); // Not a TRUE RegionNode + _igvn.register_new_node_with_optimizer(r); + set_loop(r, loop); + if( !loop->_child ) + loop->_body.push(r); + return r; +} + +//------------------------------split_up--------------------------------------- +// Split block-local op up through the phis to empty the current block +bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { + if( n->is_CFG() ) { + assert( n->in(0) != blk1, "Lousy candidate for split-if" ); + return false; + } + if( get_ctrl(n) != blk1 && get_ctrl(n) != blk2 ) + return false; // Not block local + if( n->is_Phi() ) return false; // Local PHIs are expected + + // Recursively split-up inputs + for (uint i = 1; i < n->req(); i++) { + if( split_up( n->in(i), blk1, blk2 ) ) { + // Got split recursively and self went dead? + if (n->outcnt() == 0) + _igvn.remove_dead_node(n); + return true; + } + } + + // Check for needing to clone-up a compare. Can't do that, it forces + // another (nested) split-if transform. Instead, clone it "down". + if( n->is_Cmp() ) { + assert(get_ctrl(n) == blk2 || get_ctrl(n) == blk1, "must be in block with IF"); + // Check for simple Cmp/Bool/CMove which we can clone-up. Cmp/Bool/CMove + // sequence can have no other users and it must all reside in the split-if + // block. Non-simple Cmp/Bool/CMove sequences are 'cloned-down' below - + // private, per-use versions of the Cmp and Bool are made. These sink to + // the CMove block. If the CMove is in the split-if block, then in the + // next iteration this will become a simple Cmp/Bool/CMove set to clone-up. + Node *bol, *cmov; + if( !(n->outcnt() == 1 && n->unique_out()->is_Bool() && + (bol = n->unique_out()->as_Bool()) && + (get_ctrl(bol) == blk1 || + get_ctrl(bol) == blk2) && + bol->outcnt() == 1 && + bol->unique_out()->is_CMove() && + (cmov = bol->unique_out()->as_CMove()) && + (get_ctrl(cmov) == blk1 || + get_ctrl(cmov) == blk2) ) ) { + + // Must clone down +#ifndef PRODUCT + if( PrintOpto && VerifyLoopOptimizations ) { + tty->print("Cloning down: "); + n->dump(); + } +#endif + // Clone down any block-local BoolNode uses of this CmpNode + for (DUIterator i = n->outs(); n->has_out(i); i++) { + Node* bol = n->out(i); + assert( bol->is_Bool(), "" ); + if (bol->outcnt() == 1) { + Node* use = bol->unique_out(); + Node *use_c = use->is_If() ? use->in(0) : get_ctrl(use); + if (use_c == blk1 || use_c == blk2) { + continue; + } + } + if (get_ctrl(bol) == blk1 || get_ctrl(bol) == blk2) { + // Recursively sink any BoolNode +#ifndef PRODUCT + if( PrintOpto && VerifyLoopOptimizations ) { + tty->print("Cloning down: "); + bol->dump(); + } +#endif + for (DUIterator_Last jmin, j = bol->last_outs(jmin); j >= jmin; --j) { + // Uses are either IfNodes or CMoves + Node* iff = bol->last_out(j); + assert( iff->in(1) == bol, "" ); + // Get control block of either the CMove or the If input + Node *iff_ctrl = iff->is_If() ? iff->in(0) : get_ctrl(iff); + Node *x = bol->clone(); + register_new_node(x, iff_ctrl); + _igvn.hash_delete(iff); + iff->set_req(1, x); + _igvn._worklist.push(iff); + } + _igvn.remove_dead_node( bol ); + --i; + } + } + // Clone down this CmpNode + for (DUIterator_Last jmin, j = n->last_outs(jmin); j >= jmin; --j) { + Node* bol = n->last_out(j); + assert( bol->in(1) == n, "" ); + Node *x = n->clone(); + register_new_node(x, get_ctrl(bol)); + _igvn.hash_delete(bol); + bol->set_req(1, x); + _igvn._worklist.push(bol); + } + _igvn.remove_dead_node( n ); + + return true; + } + } + + // See if splitting-up a Store. Any anti-dep loads must go up as + // well. An anti-dep load might be in the wrong block, because in + // this particular layout/schedule we ignored anti-deps and allow + // memory to be alive twice. This only works if we do the same + // operations on anti-dep loads as we do their killing stores. + if( n->is_Store() && n->in(MemNode::Memory)->in(0) == n->in(0) ) { + // Get store's memory slice + int alias_idx = C->get_alias_index(_igvn.type(n->in(MemNode::Address))->is_ptr()); + + // Get memory-phi anti-dep loads will be using + Node *memphi = n->in(MemNode::Memory); + assert( memphi->is_Phi(), "" ); + // Hoist any anti-dep load to the splitting block; + // it will then "split-up". + for (DUIterator_Fast imax,i = memphi->fast_outs(imax); i < imax; i++) { + Node *load = memphi->fast_out(i); + if( load->is_Load() && alias_idx == C->get_alias_index(_igvn.type(load->in(MemNode::Address))->is_ptr()) ) + set_ctrl(load,blk1); + } + } + + // Found some other Node; must clone it up +#ifndef PRODUCT + if( PrintOpto && VerifyLoopOptimizations ) { + tty->print("Cloning up: "); + n->dump(); + } +#endif + + // Now actually split-up this guy. One copy per control path merging. + Node *phi = PhiNode::make_blank(blk1, n); + for( uint j = 1; j < blk1->req(); j++ ) { + Node *x = n->clone(); + if( n->in(0) && n->in(0) == blk1 ) + x->set_req( 0, blk1->in(j) ); + for( uint i = 1; i < n->req(); i++ ) { + Node *m = n->in(i); + if( get_ctrl(m) == blk1 ) { + assert( m->in(0) == blk1, "" ); + x->set_req( i, m->in(j) ); + } + } + register_new_node( x, blk1->in(j) ); + phi->init_req( j, x ); + } + // Announce phi to optimizer + register_new_node(phi, blk1); + + // Remove cloned-up value from optimizer; use phi instead + _igvn.hash_delete(n); + _igvn.subsume_node( n, phi ); + + // (There used to be a self-recursive call to split_up() here, + // but it is not needed. All necessary forward walking is done + // by do_split_if() below.) + + return true; +} + +//------------------------------register_new_node------------------------------ +void PhaseIdealLoop::register_new_node( Node *n, Node *blk ) { + _igvn.register_new_node_with_optimizer(n); + set_ctrl(n, blk); + IdealLoopTree *loop = get_loop(blk); + if( !loop->_child ) + loop->_body.push(n); +} + +//------------------------------small_cache------------------------------------ +struct small_cache : public Dict { + + small_cache() : Dict( cmpkey, hashptr ) {} + Node *probe( Node *use_blk ) { return (Node*)((*this)[use_blk]); } + void lru_insert( Node *use_blk, Node *new_def ) { Insert(use_blk,new_def); } +}; + +//------------------------------spinup----------------------------------------- +// "Spin up" the dominator tree, starting at the use site and stopping when we +// find the post-dominating point. + +// We must be at the merge point which post-dominates 'new_false' and +// 'new_true'. Figure out which edges into the RegionNode eventually lead up +// to false and which to true. Put in a PhiNode to merge values; plug in +// the appropriate false-arm or true-arm values. If some path leads to the +// original IF, then insert a Phi recursively. +Node *PhaseIdealLoop::spinup( Node *iff_dom, Node *new_false, Node *new_true, Node *use_blk, Node *def, small_cache *cache ) { + if (use_blk->is_top()) // Handle dead uses + return use_blk; + Node *prior_n = (Node*)0xdeadbeef; + Node *n = use_blk; // Get path input + assert( use_blk != iff_dom, "" ); + // Here's the "spinup" the dominator tree loop. Do a cache-check + // along the way, in case we've come this way before. + while( n != iff_dom ) { // Found post-dominating point? + prior_n = n; + n = idom(n); // Search higher + Node *s = cache->probe( prior_n ); // Check cache + if( s ) return s; // Cache hit! + } + + Node *phi_post; + if( prior_n == new_false || prior_n == new_true ) { + phi_post = def->clone(); + phi_post->set_req(0, prior_n ); + register_new_node(phi_post, prior_n); + } else { + // This method handles both control uses (looking for Regions) or data + // uses (looking for Phis). If looking for a control use, then we need + // to insert a Region instead of a Phi; however Regions always exist + // previously (the hash_find_insert below would always hit) so we can + // return the existing Region. + if( def->is_CFG() ) { + phi_post = prior_n; // If looking for CFG, return prior + } else { + assert( def->is_Phi(), "" ); + assert( prior_n->is_Region(), "must be a post-dominating merge point" ); + + // Need a Phi here + phi_post = PhiNode::make_blank(prior_n, def); + // Search for both true and false on all paths till find one. + for( uint i = 1; i < phi_post->req(); i++ ) // For all paths + phi_post->init_req( i, spinup( iff_dom, new_false, new_true, prior_n->in(i), def, cache ) ); + Node *t = _igvn.hash_find_insert(phi_post); + if( t ) { // See if we already have this one + // phi_post will not be used, so kill it + _igvn.remove_dead_node(phi_post); + phi_post->destruct(); + phi_post = t; + } else { + register_new_node( phi_post, prior_n ); + } + } + } + + // Update cache everywhere + prior_n = (Node*)0xdeadbeef; // Reset IDOM walk + n = use_blk; // Get path input + // Spin-up the idom tree again, basically doing path-compression. + // Insert cache entries along the way, so that if we ever hit this + // point in the IDOM tree again we'll stop immediately on a cache hit. + while( n != iff_dom ) { // Found post-dominating point? + prior_n = n; + n = idom(n); // Search higher + cache->lru_insert( prior_n, phi_post ); // Fill cache + } // End of while not gone high enough + + return phi_post; +} + +//------------------------------find_use_block--------------------------------- +// Find the block a USE is in. Normally USE's are in the same block as the +// using instruction. For Phi-USE's, the USE is in the predecessor block +// along the corresponding path. +Node *PhaseIdealLoop::find_use_block( Node *use, Node *def, Node *old_false, Node *new_false, Node *old_true, Node *new_true ) { + // CFG uses are their own block + if( use->is_CFG() ) + return use; + + if( use->is_Phi() ) { // Phi uses in prior block + // Grab the first Phi use; there may be many. + // Each will be handled as a seperate iteration of + // the "while( phi->outcnt() )" loop. + uint j; + for( j = 1; j < use->req(); j++ ) + if( use->in(j) == def ) + break; + assert( j < use->req(), "def should be among use's inputs" ); + return use->in(0)->in(j); + } + // Normal (non-phi) use + Node *use_blk = get_ctrl(use); + // Some uses are directly attached to the old (and going away) + // false and true branches. + if( use_blk == old_false ) { + use_blk = new_false; + set_ctrl(use, new_false); + } + if( use_blk == old_true ) { + use_blk = new_true; + set_ctrl(use, new_true); + } + + if (use_blk == NULL) { // He's dead, Jim + _igvn.hash_delete(use); + _igvn.subsume_node(use, C->top()); + } + + return use_blk; +} + +//------------------------------handle_use------------------------------------- +// Handle uses of the merge point. Basically, split-if makes the merge point +// go away so all uses of the merge point must go away as well. Most block +// local uses have already been split-up, through the merge point. Uses from +// far below the merge point can't always be split up (e.g., phi-uses are +// pinned) and it makes too much stuff live. Instead we use a path-based +// solution to move uses down. +// +// If the use is along the pre-split-CFG true branch, then the new use will +// be from the post-split-CFG true merge point. Vice-versa for the false +// path. Some uses will be along both paths; then we sink the use to the +// post-dominating location; we may need to insert a Phi there. +void PhaseIdealLoop::handle_use( Node *use, Node *def, small_cache *cache, Node *region_dom, Node *new_false, Node *new_true, Node *old_false, Node *old_true ) { + + Node *use_blk = find_use_block(use,def,old_false,new_false,old_true,new_true); + if( !use_blk ) return; // He's dead, Jim + + // Walk up the dominator tree until I hit either the old IfFalse, the old + // IfTrue or the old If. Insert Phis where needed. + Node *new_def = spinup( region_dom, new_false, new_true, use_blk, def, cache ); + + // Found where this USE goes. Re-point him. + uint i; + for( i = 0; i < use->req(); i++ ) + if( use->in(i) == def ) + break; + assert( i < use->req(), "def should be among use's inputs" ); + _igvn.hash_delete(use); + use->set_req(i, new_def); + _igvn._worklist.push(use); +} + +//------------------------------do_split_if------------------------------------ +// Found an If getting its condition-code input from a Phi in the same block. +// Split thru the Region. +void PhaseIdealLoop::do_split_if( Node *iff ) { +#ifndef PRODUCT + if( PrintOpto && VerifyLoopOptimizations ) + tty->print_cr("Split-if"); +#endif + C->set_major_progress(); + Node *region = iff->in(0); + Node *region_dom = idom(region); + + // We are going to clone this test (and the control flow with it) up through + // the incoming merge point. We need to empty the current basic block. + // Clone any instructions which must be in this block up through the merge + // point. + DUIterator i, j; + bool progress = true; + while (progress) { + progress = false; + for (i = region->outs(); region->has_out(i); i++) { + Node* n = region->out(i); + if( n == region ) continue; + // The IF to be split is OK. + if( n == iff ) continue; + if( !n->is_Phi() ) { // Found pinned memory op or such + if (split_up(n, region, iff)) { + i = region->refresh_out_pos(i); + progress = true; + } + continue; + } + assert( n->in(0) == region, "" ); + + // Recursively split up all users of a Phi + for (j = n->outs(); n->has_out(j); j++) { + Node* m = n->out(j); + // If m is dead, throw it away, and declare progress + if (_nodes[m->_idx] == NULL) { + _igvn.remove_dead_node(m); + // fall through + } + else if (m != iff && split_up(m, region, iff)) { + // fall through + } else { + continue; + } + // Something unpredictable changed. + // Tell the iterators to refresh themselves, and rerun the loop. + i = region->refresh_out_pos(i); + j = region->refresh_out_pos(j); + progress = true; + } + } + } + + // Now we have no instructions in the block containing the IF. + // Split the IF. + Node *new_iff = split_thru_region( iff, region ); + + // Replace both uses of 'new_iff' with Regions merging True/False + // paths. This makes 'new_iff' go dead. + Node *old_false, *old_true; + Node *new_false, *new_true; + for (DUIterator_Last j2min, j2 = iff->last_outs(j2min); j2 >= j2min; --j2) { + Node *ifp = iff->last_out(j2); + assert( ifp->Opcode() == Op_IfFalse || ifp->Opcode() == Op_IfTrue, "" ); + ifp->set_req(0, new_iff); + Node *ifpx = split_thru_region( ifp, region ); + + // Replace 'If' projection of a Region with a Region of + // 'If' projections. + ifpx->set_req(0, ifpx); // A TRUE RegionNode + + // Setup dominator info + set_idom(ifpx, region_dom, dom_depth(region_dom) + 1); + + // Check for splitting loop tails + if( get_loop(iff)->tail() == ifp ) + get_loop(iff)->_tail = ifpx; + + // Replace in the graph with lazy-update mechanism + new_iff->set_req(0, new_iff); // hook self so it does not go dead + lazy_replace_proj( ifp, ifpx ); + new_iff->set_req(0, region); + + // Record bits for later xforms + if( ifp->Opcode() == Op_IfFalse ) { + old_false = ifp; + new_false = ifpx; + } else { + old_true = ifp; + new_true = ifpx; + } + } + _igvn.remove_dead_node(new_iff); + // Lazy replace IDOM info with the region's dominator + lazy_replace( iff, region_dom ); + + // Now make the original merge point go dead, by handling all its uses. + small_cache region_cache; + // Preload some control flow in region-cache + region_cache.lru_insert( new_false, new_false ); + region_cache.lru_insert( new_true , new_true ); + // Now handle all uses of the splitting block + for (DUIterator_Last kmin, k = region->last_outs(kmin); k >= kmin; --k) { + Node* phi = region->last_out(k); + if( !phi->in(0) ) { // Dead phi? Remove it + _igvn.remove_dead_node(phi); + continue; + } + assert( phi->in(0) == region, "" ); + if( phi == region ) { // Found the self-reference + phi->set_req(0, NULL); + continue; // Break the self-cycle + } + // Expected common case: Phi hanging off of Region + if( phi->is_Phi() ) { + // Need a per-def cache. Phi represents a def, so make a cache + small_cache phi_cache; + + // Inspect all Phi uses to make the Phi go dead + for (DUIterator_Last lmin, l = phi->last_outs(lmin); l >= lmin; --l) { + Node* use = phi->last_out(l); + // Compute the new DEF for this USE. New DEF depends on the path + // taken from the original DEF to the USE. The new DEF may be some + // collection of PHI's merging values from different paths. The Phis + // inserted depend only on the location of the USE. We use a + // 2-element cache to handle multiple uses from the same block. + handle_use( use, phi, &phi_cache, region_dom, new_false, new_true, old_false, old_true ); + } // End of while phi has uses + + // Because handle_use might relocate region->_out, + // we must refresh the iterator. + k = region->last_outs(kmin); + + // Remove the dead Phi + _igvn.remove_dead_node( phi ); + + } else { + // Random memory op guarded by Region. Compute new DEF for USE. + handle_use( phi, region, ®ion_cache, region_dom, new_false, new_true, old_false, old_true ); + } + + } // End of while merge point has phis + + // Any leftover bits in the splitting block must not have depended on local + // Phi inputs (these have already been split-up). Hence it's safe to hoist + // these guys to the dominating point. + lazy_replace( region, region_dom ); +#ifndef PRODUCT + if( VerifyLoopOptimizations ) verify(); +#endif +} diff --git a/hotspot/src/share/vm/opto/subnode.cpp b/hotspot/src/share/vm/opto/subnode.cpp new file mode 100644 index 00000000000..1344197ca22 --- /dev/null +++ b/hotspot/src/share/vm/opto/subnode.cpp @@ -0,0 +1,1206 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_subnode.cpp.incl" +#include "math.h" + +//============================================================================= +//------------------------------Identity--------------------------------------- +// If right input is a constant 0, return the left input. +Node *SubNode::Identity( PhaseTransform *phase ) { + assert(in(1) != this, "Must already have called Value"); + assert(in(2) != this, "Must already have called Value"); + + // Remove double negation + const Type *zero = add_id(); + if( phase->type( in(1) )->higher_equal( zero ) && + in(2)->Opcode() == Opcode() && + phase->type( in(2)->in(1) )->higher_equal( zero ) ) { + return in(2)->in(2); + } + + // Convert "(X+Y) - Y" into X + if( in(1)->Opcode() == Op_AddI ) { + if( phase->eqv(in(1)->in(2),in(2)) ) + return in(1)->in(1); + // Also catch: "(X + Opaque2(Y)) - Y". In this case, 'Y' is a loop-varying + // trip counter and X is likely to be loop-invariant (that's how O2 Nodes + // are originally used, although the optimizer sometimes jiggers things). + // This folding through an O2 removes a loop-exit use of a loop-varying + // value and generally lowers register pressure in and around the loop. + if( in(1)->in(2)->Opcode() == Op_Opaque2 && + phase->eqv(in(1)->in(2)->in(1),in(2)) ) + return in(1)->in(1); + } + + return ( phase->type( in(2) )->higher_equal( zero ) ) ? in(1) : this; +} + +//------------------------------Value------------------------------------------ +// A subtract node differences it's two inputs. +const Type *SubNode::Value( PhaseTransform *phase ) const { + const Node* in1 = in(1); + const Node* in2 = in(2); + // Either input is TOP ==> the result is TOP + const Type* t1 = (in1 == this) ? Type::TOP : phase->type(in1); + if( t1 == Type::TOP ) return Type::TOP; + const Type* t2 = (in2 == this) ? Type::TOP : phase->type(in2); + if( t2 == Type::TOP ) return Type::TOP; + + // Not correct for SubFnode and AddFNode (must check for infinity) + // Equal? Subtract is zero + if (phase->eqv_uncast(in1, in2)) return add_id(); + + // Either input is BOTTOM ==> the result is the local BOTTOM + if( t1 == Type::BOTTOM || t2 == Type::BOTTOM ) + return bottom_type(); + + return sub(t1,t2); // Local flavor of type subtraction + +} + +//============================================================================= + +//------------------------------Helper function-------------------------------- +static bool ok_to_convert(Node* inc, Node* iv) { + // Do not collapse (x+c0)-y if "+" is a loop increment, because the + // "-" is loop invariant and collapsing extends the live-range of "x" + // to overlap with the "+", forcing another register to be used in + // the loop. + // This test will be clearer with '&&' (apply DeMorgan's rule) + // but I like the early cutouts that happen here. + const PhiNode *phi; + if( ( !inc->in(1)->is_Phi() || + !(phi=inc->in(1)->as_Phi()) || + phi->is_copy() || + !phi->region()->is_CountedLoop() || + inc != phi->region()->as_CountedLoop()->incr() ) + && + // Do not collapse (x+c0)-iv if "iv" is a loop induction variable, + // because "x" maybe invariant. + ( !iv->is_loop_iv() ) + ) { + return true; + } else { + return false; + } +} +//------------------------------Ideal------------------------------------------ +Node *SubINode::Ideal(PhaseGVN *phase, bool can_reshape){ + Node *in1 = in(1); + Node *in2 = in(2); + uint op1 = in1->Opcode(); + uint op2 = in2->Opcode(); + +#ifdef ASSERT + // Check for dead loop + if( phase->eqv( in1, this ) || phase->eqv( in2, this ) || + ( op1 == Op_AddI || op1 == Op_SubI ) && + ( phase->eqv( in1->in(1), this ) || phase->eqv( in1->in(2), this ) || + phase->eqv( in1->in(1), in1 ) || phase->eqv( in1->in(2), in1 ) ) ) + assert(false, "dead loop in SubINode::Ideal"); +#endif + + const Type *t2 = phase->type( in2 ); + if( t2 == Type::TOP ) return NULL; + // Convert "x-c0" into "x+ -c0". + if( t2->base() == Type::Int ){ // Might be bottom or top... + const TypeInt *i = t2->is_int(); + if( i->is_con() ) + return new (phase->C, 3) AddINode(in1, phase->intcon(-i->get_con())); + } + + // Convert "(x+c0) - y" into (x-y) + c0" + // Do not collapse (x+c0)-y if "+" is a loop increment or + // if "y" is a loop induction variable. + if( op1 == Op_AddI && ok_to_convert(in1, in2) ) { + const Type *tadd = phase->type( in1->in(2) ); + if( tadd->singleton() && tadd != Type::TOP ) { + Node *sub2 = phase->transform( new (phase->C, 3) SubINode( in1->in(1), in2 )); + return new (phase->C, 3) AddINode( sub2, in1->in(2) ); + } + } + + + // Convert "x - (y+c0)" into "(x-y) - c0" + // Need the same check as in above optimization but reversed. + if (op2 == Op_AddI && ok_to_convert(in2, in1)) { + Node* in21 = in2->in(1); + Node* in22 = in2->in(2); + const TypeInt* tcon = phase->type(in22)->isa_int(); + if (tcon != NULL && tcon->is_con()) { + Node* sub2 = phase->transform( new (phase->C, 3) SubINode(in1, in21) ); + Node* neg_c0 = phase->intcon(- tcon->get_con()); + return new (phase->C, 3) AddINode(sub2, neg_c0); + } + } + + const Type *t1 = phase->type( in1 ); + if( t1 == Type::TOP ) return NULL; + +#ifdef ASSERT + // Check for dead loop + if( ( op2 == Op_AddI || op2 == Op_SubI ) && + ( phase->eqv( in2->in(1), this ) || phase->eqv( in2->in(2), this ) || + phase->eqv( in2->in(1), in2 ) || phase->eqv( in2->in(2), in2 ) ) ) + assert(false, "dead loop in SubINode::Ideal"); +#endif + + // Convert "x - (x+y)" into "-y" + if( op2 == Op_AddI && + phase->eqv( in1, in2->in(1) ) ) + return new (phase->C, 3) SubINode( phase->intcon(0),in2->in(2)); + // Convert "(x-y) - x" into "-y" + if( op1 == Op_SubI && + phase->eqv( in1->in(1), in2 ) ) + return new (phase->C, 3) SubINode( phase->intcon(0),in1->in(2)); + // Convert "x - (y+x)" into "-y" + if( op2 == Op_AddI && + phase->eqv( in1, in2->in(2) ) ) + return new (phase->C, 3) SubINode( phase->intcon(0),in2->in(1)); + + // Convert "0 - (x-y)" into "y-x" + if( t1 == TypeInt::ZERO && op2 == Op_SubI ) + return new (phase->C, 3) SubINode( in2->in(2), in2->in(1) ); + + // Convert "0 - (x+con)" into "-con-x" + jint con; + if( t1 == TypeInt::ZERO && op2 == Op_AddI && + (con = in2->in(2)->find_int_con(0)) != 0 ) + return new (phase->C, 3) SubINode( phase->intcon(-con), in2->in(1) ); + + // Convert "(X+A) - (X+B)" into "A - B" + if( op1 == Op_AddI && op2 == Op_AddI && in1->in(1) == in2->in(1) ) + return new (phase->C, 3) SubINode( in1->in(2), in2->in(2) ); + + // Convert "(A+X) - (B+X)" into "A - B" + if( op1 == Op_AddI && op2 == Op_AddI && in1->in(2) == in2->in(2) ) + return new (phase->C, 3) SubINode( in1->in(1), in2->in(1) ); + + // Convert "A-(B-C)" into (A+C)-B", since add is commutative and generally + // nicer to optimize than subtract. + if( op2 == Op_SubI && in2->outcnt() == 1) { + Node *add1 = phase->transform( new (phase->C, 3) AddINode( in1, in2->in(2) ) ); + return new (phase->C, 3) SubINode( add1, in2->in(1) ); + } + + return NULL; +} + +//------------------------------sub-------------------------------------------- +// A subtract node differences it's two inputs. +const Type *SubINode::sub( const Type *t1, const Type *t2 ) const { + const TypeInt *r0 = t1->is_int(); // Handy access + const TypeInt *r1 = t2->is_int(); + int32 lo = r0->_lo - r1->_hi; + int32 hi = r0->_hi - r1->_lo; + + // We next check for 32-bit overflow. + // If that happens, we just assume all integers are possible. + if( (((r0->_lo ^ r1->_hi) >= 0) || // lo ends have same signs OR + ((r0->_lo ^ lo) >= 0)) && // lo results have same signs AND + (((r0->_hi ^ r1->_lo) >= 0) || // hi ends have same signs OR + ((r0->_hi ^ hi) >= 0)) ) // hi results have same signs + return TypeInt::make(lo,hi,MAX2(r0->_widen,r1->_widen)); + else // Overflow; assume all integers + return TypeInt::INT; +} + +//============================================================================= +//------------------------------Ideal------------------------------------------ +Node *SubLNode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node *in1 = in(1); + Node *in2 = in(2); + uint op1 = in1->Opcode(); + uint op2 = in2->Opcode(); + +#ifdef ASSERT + // Check for dead loop + if( phase->eqv( in1, this ) || phase->eqv( in2, this ) || + ( op1 == Op_AddL || op1 == Op_SubL ) && + ( phase->eqv( in1->in(1), this ) || phase->eqv( in1->in(2), this ) || + phase->eqv( in1->in(1), in1 ) || phase->eqv( in1->in(2), in1 ) ) ) + assert(false, "dead loop in SubLNode::Ideal"); +#endif + + if( phase->type( in2 ) == Type::TOP ) return NULL; + const TypeLong *i = phase->type( in2 )->isa_long(); + // Convert "x-c0" into "x+ -c0". + if( i && // Might be bottom or top... + i->is_con() ) + return new (phase->C, 3) AddLNode(in1, phase->longcon(-i->get_con())); + + // Convert "(x+c0) - y" into (x-y) + c0" + // Do not collapse (x+c0)-y if "+" is a loop increment or + // if "y" is a loop induction variable. + if( op1 == Op_AddL && ok_to_convert(in1, in2) ) { + Node *in11 = in1->in(1); + const Type *tadd = phase->type( in1->in(2) ); + if( tadd->singleton() && tadd != Type::TOP ) { + Node *sub2 = phase->transform( new (phase->C, 3) SubLNode( in11, in2 )); + return new (phase->C, 3) AddLNode( sub2, in1->in(2) ); + } + } + + // Convert "x - (y+c0)" into "(x-y) - c0" + // Need the same check as in above optimization but reversed. + if (op2 == Op_AddL && ok_to_convert(in2, in1)) { + Node* in21 = in2->in(1); + Node* in22 = in2->in(2); + const TypeLong* tcon = phase->type(in22)->isa_long(); + if (tcon != NULL && tcon->is_con()) { + Node* sub2 = phase->transform( new (phase->C, 3) SubLNode(in1, in21) ); + Node* neg_c0 = phase->longcon(- tcon->get_con()); + return new (phase->C, 3) AddLNode(sub2, neg_c0); + } + } + + const Type *t1 = phase->type( in1 ); + if( t1 == Type::TOP ) return NULL; + +#ifdef ASSERT + // Check for dead loop + if( ( op2 == Op_AddL || op2 == Op_SubL ) && + ( phase->eqv( in2->in(1), this ) || phase->eqv( in2->in(2), this ) || + phase->eqv( in2->in(1), in2 ) || phase->eqv( in2->in(2), in2 ) ) ) + assert(false, "dead loop in SubLNode::Ideal"); +#endif + + // Convert "x - (x+y)" into "-y" + if( op2 == Op_AddL && + phase->eqv( in1, in2->in(1) ) ) + return new (phase->C, 3) SubLNode( phase->makecon(TypeLong::ZERO), in2->in(2)); + // Convert "x - (y+x)" into "-y" + if( op2 == Op_AddL && + phase->eqv( in1, in2->in(2) ) ) + return new (phase->C, 3) SubLNode( phase->makecon(TypeLong::ZERO),in2->in(1)); + + // Convert "0 - (x-y)" into "y-x" + if( phase->type( in1 ) == TypeLong::ZERO && op2 == Op_SubL ) + return new (phase->C, 3) SubLNode( in2->in(2), in2->in(1) ); + + // Convert "(X+A) - (X+B)" into "A - B" + if( op1 == Op_AddL && op2 == Op_AddL && in1->in(1) == in2->in(1) ) + return new (phase->C, 3) SubLNode( in1->in(2), in2->in(2) ); + + // Convert "(A+X) - (B+X)" into "A - B" + if( op1 == Op_AddL && op2 == Op_AddL && in1->in(2) == in2->in(2) ) + return new (phase->C, 3) SubLNode( in1->in(1), in2->in(1) ); + + // Convert "A-(B-C)" into (A+C)-B" + if( op2 == Op_SubL && in2->outcnt() == 1) { + Node *add1 = phase->transform( new (phase->C, 3) AddLNode( in1, in2->in(2) ) ); + return new (phase->C, 3) SubLNode( add1, in2->in(1) ); + } + + return NULL; +} + +//------------------------------sub-------------------------------------------- +// A subtract node differences it's two inputs. +const Type *SubLNode::sub( const Type *t1, const Type *t2 ) const { + const TypeLong *r0 = t1->is_long(); // Handy access + const TypeLong *r1 = t2->is_long(); + jlong lo = r0->_lo - r1->_hi; + jlong hi = r0->_hi - r1->_lo; + + // We next check for 32-bit overflow. + // If that happens, we just assume all integers are possible. + if( (((r0->_lo ^ r1->_hi) >= 0) || // lo ends have same signs OR + ((r0->_lo ^ lo) >= 0)) && // lo results have same signs AND + (((r0->_hi ^ r1->_lo) >= 0) || // hi ends have same signs OR + ((r0->_hi ^ hi) >= 0)) ) // hi results have same signs + return TypeLong::make(lo,hi,MAX2(r0->_widen,r1->_widen)); + else // Overflow; assume all integers + return TypeLong::LONG; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +// A subtract node differences its two inputs. +const Type *SubFPNode::Value( PhaseTransform *phase ) const { + const Node* in1 = in(1); + const Node* in2 = in(2); + // Either input is TOP ==> the result is TOP + const Type* t1 = (in1 == this) ? Type::TOP : phase->type(in1); + if( t1 == Type::TOP ) return Type::TOP; + const Type* t2 = (in2 == this) ? Type::TOP : phase->type(in2); + if( t2 == Type::TOP ) return Type::TOP; + + // if both operands are infinity of same sign, the result is NaN; do + // not replace with zero + if( (t1->is_finite() && t2->is_finite()) ) { + if( phase->eqv(in1, in2) ) return add_id(); + } + + // Either input is BOTTOM ==> the result is the local BOTTOM + const Type *bot = bottom_type(); + if( (t1 == bot) || (t2 == bot) || + (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) + return bot; + + return sub(t1,t2); // Local flavor of type subtraction +} + + +//============================================================================= +//------------------------------Ideal------------------------------------------ +Node *SubFNode::Ideal(PhaseGVN *phase, bool can_reshape) { + const Type *t2 = phase->type( in(2) ); + // Convert "x-c0" into "x+ -c0". + if( t2->base() == Type::FloatCon ) { // Might be bottom or top... + // return new (phase->C, 3) AddFNode(in(1), phase->makecon( TypeF::make(-t2->getf()) ) ); + } + + // Not associative because of boundary conditions (infinity) + if( IdealizedNumerics && !phase->C->method()->is_strict() ) { + // Convert "x - (x+y)" into "-y" + if( in(2)->is_Add() && + phase->eqv(in(1),in(2)->in(1) ) ) + return new (phase->C, 3) SubFNode( phase->makecon(TypeF::ZERO),in(2)->in(2)); + } + + // Cannot replace 0.0-X with -X because a 'fsub' bytecode computes + // 0.0-0.0 as +0.0, while a 'fneg' bytecode computes -0.0. + //if( phase->type(in(1)) == TypeF::ZERO ) + //return new (phase->C, 2) NegFNode(in(2)); + + return NULL; +} + +//------------------------------sub-------------------------------------------- +// A subtract node differences its two inputs. +const Type *SubFNode::sub( const Type *t1, const Type *t2 ) const { + // no folding if one of operands is infinity or NaN, do not do constant folding + if( g_isfinite(t1->getf()) && g_isfinite(t2->getf()) ) { + return TypeF::make( t1->getf() - t2->getf() ); + } + else if( g_isnan(t1->getf()) ) { + return t1; + } + else if( g_isnan(t2->getf()) ) { + return t2; + } + else { + return Type::FLOAT; + } +} + +//============================================================================= +//------------------------------Ideal------------------------------------------ +Node *SubDNode::Ideal(PhaseGVN *phase, bool can_reshape){ + const Type *t2 = phase->type( in(2) ); + // Convert "x-c0" into "x+ -c0". + if( t2->base() == Type::DoubleCon ) { // Might be bottom or top... + // return new (phase->C, 3) AddDNode(in(1), phase->makecon( TypeD::make(-t2->getd()) ) ); + } + + // Not associative because of boundary conditions (infinity) + if( IdealizedNumerics && !phase->C->method()->is_strict() ) { + // Convert "x - (x+y)" into "-y" + if( in(2)->is_Add() && + phase->eqv(in(1),in(2)->in(1) ) ) + return new (phase->C, 3) SubDNode( phase->makecon(TypeD::ZERO),in(2)->in(2)); + } + + // Cannot replace 0.0-X with -X because a 'dsub' bytecode computes + // 0.0-0.0 as +0.0, while a 'dneg' bytecode computes -0.0. + //if( phase->type(in(1)) == TypeD::ZERO ) + //return new (phase->C, 2) NegDNode(in(2)); + + return NULL; +} + +//------------------------------sub-------------------------------------------- +// A subtract node differences its two inputs. +const Type *SubDNode::sub( const Type *t1, const Type *t2 ) const { + // no folding if one of operands is infinity or NaN, do not do constant folding + if( g_isfinite(t1->getd()) && g_isfinite(t2->getd()) ) { + return TypeD::make( t1->getd() - t2->getd() ); + } + else if( g_isnan(t1->getd()) ) { + return t1; + } + else if( g_isnan(t2->getd()) ) { + return t2; + } + else { + return Type::DOUBLE; + } +} + +//============================================================================= +//------------------------------Idealize--------------------------------------- +// Unlike SubNodes, compare must still flatten return value to the +// range -1, 0, 1. +// And optimizations like those for (X + Y) - X fail if overflow happens. +Node *CmpNode::Identity( PhaseTransform *phase ) { + return this; +} + +//============================================================================= +//------------------------------cmp-------------------------------------------- +// Simplify a CmpI (compare 2 integers) node, based on local information. +// If both inputs are constants, compare them. +const Type *CmpINode::sub( const Type *t1, const Type *t2 ) const { + const TypeInt *r0 = t1->is_int(); // Handy access + const TypeInt *r1 = t2->is_int(); + + if( r0->_hi < r1->_lo ) // Range is always low? + return TypeInt::CC_LT; + else if( r0->_lo > r1->_hi ) // Range is always high? + return TypeInt::CC_GT; + + else if( r0->is_con() && r1->is_con() ) { // comparing constants? + assert(r0->get_con() == r1->get_con(), "must be equal"); + return TypeInt::CC_EQ; // Equal results. + } else if( r0->_hi == r1->_lo ) // Range is never high? + return TypeInt::CC_LE; + else if( r0->_lo == r1->_hi ) // Range is never low? + return TypeInt::CC_GE; + return TypeInt::CC; // else use worst case results +} + +// Simplify a CmpU (compare 2 integers) node, based on local information. +// If both inputs are constants, compare them. +const Type *CmpUNode::sub( const Type *t1, const Type *t2 ) const { + assert(!t1->isa_ptr(), "obsolete usage of CmpU"); + + // comparing two unsigned ints + const TypeInt *r0 = t1->is_int(); // Handy access + const TypeInt *r1 = t2->is_int(); + + // Current installed version + // Compare ranges for non-overlap + juint lo0 = r0->_lo; + juint hi0 = r0->_hi; + juint lo1 = r1->_lo; + juint hi1 = r1->_hi; + + // If either one has both negative and positive values, + // it therefore contains both 0 and -1, and since [0..-1] is the + // full unsigned range, the type must act as an unsigned bottom. + bool bot0 = ((jint)(lo0 ^ hi0) < 0); + bool bot1 = ((jint)(lo1 ^ hi1) < 0); + + if (bot0 || bot1) { + // All unsigned values are LE -1 and GE 0. + if (lo0 == 0 && hi0 == 0) { + return TypeInt::CC_LE; // 0 <= bot + } else if (lo1 == 0 && hi1 == 0) { + return TypeInt::CC_GE; // bot >= 0 + } + } else { + // We can use ranges of the form [lo..hi] if signs are the same. + assert(lo0 <= hi0 && lo1 <= hi1, "unsigned ranges are valid"); + // results are reversed, '-' > '+' for unsigned compare + if (hi0 < lo1) { + return TypeInt::CC_LT; // smaller + } else if (lo0 > hi1) { + return TypeInt::CC_GT; // greater + } else if (hi0 == lo1 && lo0 == hi1) { + return TypeInt::CC_EQ; // Equal results + } else if (lo0 >= hi1) { + return TypeInt::CC_GE; + } else if (hi0 <= lo1) { + // Check for special case in Hashtable::get. (See below.) + if ((jint)lo0 >= 0 && (jint)lo1 >= 0 && + in(1)->Opcode() == Op_ModI && + in(1)->in(2) == in(2) ) + return TypeInt::CC_LT; + return TypeInt::CC_LE; + } + } + // Check for special case in Hashtable::get - the hash index is + // mod'ed to the table size so the following range check is useless. + // Check for: (X Mod Y) CmpU Y, where the mod result and Y both have + // to be positive. + // (This is a gross hack, since the sub method never + // looks at the structure of the node in any other case.) + if ((jint)lo0 >= 0 && (jint)lo1 >= 0 && + in(1)->Opcode() == Op_ModI && + in(1)->in(2)->uncast() == in(2)->uncast()) + return TypeInt::CC_LT; + return TypeInt::CC; // else use worst case results +} + +//------------------------------Idealize--------------------------------------- +Node *CmpINode::Ideal( PhaseGVN *phase, bool can_reshape ) { + if (phase->type(in(2))->higher_equal(TypeInt::ZERO)) { + switch (in(1)->Opcode()) { + case Op_CmpL3: // Collapse a CmpL3/CmpI into a CmpL + return new (phase->C, 3) CmpLNode(in(1)->in(1),in(1)->in(2)); + case Op_CmpF3: // Collapse a CmpF3/CmpI into a CmpF + return new (phase->C, 3) CmpFNode(in(1)->in(1),in(1)->in(2)); + case Op_CmpD3: // Collapse a CmpD3/CmpI into a CmpD + return new (phase->C, 3) CmpDNode(in(1)->in(1),in(1)->in(2)); + //case Op_SubI: + // If (x - y) cannot overflow, then ((x - y) 0) + // can be turned into (x y). + // This is handled (with more general cases) by Ideal_sub_algebra. + } + } + return NULL; // No change +} + + +//============================================================================= +// Simplify a CmpL (compare 2 longs ) node, based on local information. +// If both inputs are constants, compare them. +const Type *CmpLNode::sub( const Type *t1, const Type *t2 ) const { + const TypeLong *r0 = t1->is_long(); // Handy access + const TypeLong *r1 = t2->is_long(); + + if( r0->_hi < r1->_lo ) // Range is always low? + return TypeInt::CC_LT; + else if( r0->_lo > r1->_hi ) // Range is always high? + return TypeInt::CC_GT; + + else if( r0->is_con() && r1->is_con() ) { // comparing constants? + assert(r0->get_con() == r1->get_con(), "must be equal"); + return TypeInt::CC_EQ; // Equal results. + } else if( r0->_hi == r1->_lo ) // Range is never high? + return TypeInt::CC_LE; + else if( r0->_lo == r1->_hi ) // Range is never low? + return TypeInt::CC_GE; + return TypeInt::CC; // else use worst case results +} + +//============================================================================= +//------------------------------sub-------------------------------------------- +// Simplify an CmpP (compare 2 pointers) node, based on local information. +// If both inputs are constants, compare them. +const Type *CmpPNode::sub( const Type *t1, const Type *t2 ) const { + const TypePtr *r0 = t1->is_ptr(); // Handy access + const TypePtr *r1 = t2->is_ptr(); + + // Undefined inputs makes for an undefined result + if( TypePtr::above_centerline(r0->_ptr) || + TypePtr::above_centerline(r1->_ptr) ) + return Type::TOP; + + if (r0 == r1 && r0->singleton()) { + // Equal pointer constants (klasses, nulls, etc.) + return TypeInt::CC_EQ; + } + + // See if it is 2 unrelated classes. + const TypeOopPtr* p0 = r0->isa_oopptr(); + const TypeOopPtr* p1 = r1->isa_oopptr(); + if (p0 && p1) { + ciKlass* klass0 = p0->klass(); + bool xklass0 = p0->klass_is_exact(); + ciKlass* klass1 = p1->klass(); + bool xklass1 = p1->klass_is_exact(); + int kps = (p0->isa_klassptr()?1:0) + (p1->isa_klassptr()?1:0); + if (klass0 && klass1 && + kps != 1 && // both or neither are klass pointers + !klass0->is_interface() && // do not trust interfaces + !klass1->is_interface()) { + // See if neither subclasses the other, or if the class on top + // is precise. In either of these cases, the compare must fail. + if (klass0->equals(klass1) || // if types are unequal but klasses are + !klass0->is_java_klass() || // types not part of Java language? + !klass1->is_java_klass()) { // types not part of Java language? + // Do nothing; we know nothing for imprecise types + } else if (klass0->is_subtype_of(klass1)) { + // If klass1's type is PRECISE, then we can fail. + if (xklass1) return TypeInt::CC_GT; + } else if (klass1->is_subtype_of(klass0)) { + // If klass0's type is PRECISE, then we can fail. + if (xklass0) return TypeInt::CC_GT; + } else { // Neither subtypes the other + return TypeInt::CC_GT; // ...so always fail + } + } + } + + // Known constants can be compared exactly + // Null can be distinguished from any NotNull pointers + // Unknown inputs makes an unknown result + if( r0->singleton() ) { + intptr_t bits0 = r0->get_con(); + if( r1->singleton() ) + return bits0 == r1->get_con() ? TypeInt::CC_EQ : TypeInt::CC_GT; + return ( r1->_ptr == TypePtr::NotNull && bits0==0 ) ? TypeInt::CC_GT : TypeInt::CC; + } else if( r1->singleton() ) { + intptr_t bits1 = r1->get_con(); + return ( r0->_ptr == TypePtr::NotNull && bits1==0 ) ? TypeInt::CC_GT : TypeInt::CC; + } else + return TypeInt::CC; +} + +//------------------------------Ideal------------------------------------------ +// Check for the case of comparing an unknown klass loaded from the primary +// super-type array vs a known klass with no subtypes. This amounts to +// checking to see an unknown klass subtypes a known klass with no subtypes; +// this only happens on an exact match. We can shorten this test by 1 load. +Node *CmpPNode::Ideal( PhaseGVN *phase, bool can_reshape ) { + // Constant pointer on right? + const TypeKlassPtr* t2 = phase->type(in(2))->isa_klassptr(); + if (t2 == NULL || !t2->klass_is_exact()) + return NULL; + // Get the constant klass we are comparing to. + ciKlass* superklass = t2->klass(); + + // Now check for LoadKlass on left. + Node* ldk1 = in(1); + if (ldk1->Opcode() != Op_LoadKlass) + return NULL; + // Take apart the address of the LoadKlass: + Node* adr1 = ldk1->in(MemNode::Address); + intptr_t con2 = 0; + Node* ldk2 = AddPNode::Ideal_base_and_offset(adr1, phase, con2); + if (ldk2 == NULL) + return NULL; + if (con2 == oopDesc::klass_offset_in_bytes()) { + // We are inspecting an object's concrete class. + // Short-circuit the check if the query is abstract. + if (superklass->is_interface() || + superklass->is_abstract()) { + // Make it come out always false: + this->set_req(2, phase->makecon(TypePtr::NULL_PTR)); + return this; + } + } + + // Check for a LoadKlass from primary supertype array. + // Any nested loadklass from loadklass+con must be from the p.s. array. + if (ldk2->Opcode() != Op_LoadKlass) + return NULL; + + // Verify that we understand the situation + if (con2 != (intptr_t) superklass->super_check_offset()) + return NULL; // Might be element-klass loading from array klass + + // If 'superklass' has no subklasses and is not an interface, then we are + // assured that the only input which will pass the type check is + // 'superklass' itself. + // + // We could be more liberal here, and allow the optimization on interfaces + // which have a single implementor. This would require us to increase the + // expressiveness of the add_dependency() mechanism. + // %%% Do this after we fix TypeOopPtr: Deps are expressive enough now. + + // Object arrays must have their base element have no subtypes + while (superklass->is_obj_array_klass()) { + ciType* elem = superklass->as_obj_array_klass()->element_type(); + superklass = elem->as_klass(); + } + if (superklass->is_instance_klass()) { + ciInstanceKlass* ik = superklass->as_instance_klass(); + if (ik->has_subklass() || ik->is_interface()) return NULL; + // Add a dependency if there is a chance that a subclass will be added later. + if (!ik->is_final()) { + phase->C->dependencies()->assert_leaf_type(ik); + } + } + + // Bypass the dependent load, and compare directly + this->set_req(1,ldk2); + + return this; +} + +//============================================================================= +//------------------------------Value------------------------------------------ +// Simplify an CmpF (compare 2 floats ) node, based on local information. +// If both inputs are constants, compare them. +const Type *CmpFNode::Value( PhaseTransform *phase ) const { + const Node* in1 = in(1); + const Node* in2 = in(2); + // Either input is TOP ==> the result is TOP + const Type* t1 = (in1 == this) ? Type::TOP : phase->type(in1); + if( t1 == Type::TOP ) return Type::TOP; + const Type* t2 = (in2 == this) ? Type::TOP : phase->type(in2); + if( t2 == Type::TOP ) return Type::TOP; + + // Not constants? Don't know squat - even if they are the same + // value! If they are NaN's they compare to LT instead of EQ. + const TypeF *tf1 = t1->isa_float_constant(); + const TypeF *tf2 = t2->isa_float_constant(); + if( !tf1 || !tf2 ) return TypeInt::CC; + + // This implements the Java bytecode fcmpl, so unordered returns -1. + if( tf1->is_nan() || tf2->is_nan() ) + return TypeInt::CC_LT; + + if( tf1->_f < tf2->_f ) return TypeInt::CC_LT; + if( tf1->_f > tf2->_f ) return TypeInt::CC_GT; + assert( tf1->_f == tf2->_f, "do not understand FP behavior" ); + return TypeInt::CC_EQ; +} + + +//============================================================================= +//------------------------------Value------------------------------------------ +// Simplify an CmpD (compare 2 doubles ) node, based on local information. +// If both inputs are constants, compare them. +const Type *CmpDNode::Value( PhaseTransform *phase ) const { + const Node* in1 = in(1); + const Node* in2 = in(2); + // Either input is TOP ==> the result is TOP + const Type* t1 = (in1 == this) ? Type::TOP : phase->type(in1); + if( t1 == Type::TOP ) return Type::TOP; + const Type* t2 = (in2 == this) ? Type::TOP : phase->type(in2); + if( t2 == Type::TOP ) return Type::TOP; + + // Not constants? Don't know squat - even if they are the same + // value! If they are NaN's they compare to LT instead of EQ. + const TypeD *td1 = t1->isa_double_constant(); + const TypeD *td2 = t2->isa_double_constant(); + if( !td1 || !td2 ) return TypeInt::CC; + + // This implements the Java bytecode dcmpl, so unordered returns -1. + if( td1->is_nan() || td2->is_nan() ) + return TypeInt::CC_LT; + + if( td1->_d < td2->_d ) return TypeInt::CC_LT; + if( td1->_d > td2->_d ) return TypeInt::CC_GT; + assert( td1->_d == td2->_d, "do not understand FP behavior" ); + return TypeInt::CC_EQ; +} + +//------------------------------Ideal------------------------------------------ +Node *CmpDNode::Ideal(PhaseGVN *phase, bool can_reshape){ + // Check if we can change this to a CmpF and remove a ConvD2F operation. + // Change (CMPD (F2D (float)) (ConD value)) + // To (CMPF (float) (ConF value)) + // Valid when 'value' does not lose precision as a float. + // Benefits: eliminates conversion, does not require 24-bit mode + + // NaNs prevent commuting operands. This transform works regardless of the + // order of ConD and ConvF2D inputs by preserving the original order. + int idx_f2d = 1; // ConvF2D on left side? + if( in(idx_f2d)->Opcode() != Op_ConvF2D ) + idx_f2d = 2; // No, swap to check for reversed args + int idx_con = 3-idx_f2d; // Check for the constant on other input + + if( ConvertCmpD2CmpF && + in(idx_f2d)->Opcode() == Op_ConvF2D && + in(idx_con)->Opcode() == Op_ConD ) { + const TypeD *t2 = in(idx_con)->bottom_type()->is_double_constant(); + double t2_value_as_double = t2->_d; + float t2_value_as_float = (float)t2_value_as_double; + if( t2_value_as_double == (double)t2_value_as_float ) { + // Test value can be represented as a float + // Eliminate the conversion to double and create new comparison + Node *new_in1 = in(idx_f2d)->in(1); + Node *new_in2 = phase->makecon( TypeF::make(t2_value_as_float) ); + if( idx_f2d != 1 ) { // Must flip args to match original order + Node *tmp = new_in1; + new_in1 = new_in2; + new_in2 = tmp; + } + CmpFNode *new_cmp = (Opcode() == Op_CmpD3) + ? new (phase->C, 3) CmpF3Node( new_in1, new_in2 ) + : new (phase->C, 3) CmpFNode ( new_in1, new_in2 ) ; + return new_cmp; // Changed to CmpFNode + } + // Testing value required the precision of a double + } + return NULL; // No change +} + + +//============================================================================= +//------------------------------cc2logical------------------------------------- +// Convert a condition code type to a logical type +const Type *BoolTest::cc2logical( const Type *CC ) const { + if( CC == Type::TOP ) return Type::TOP; + if( CC->base() != Type::Int ) return TypeInt::BOOL; // Bottom or worse + const TypeInt *ti = CC->is_int(); + if( ti->is_con() ) { // Only 1 kind of condition codes set? + // Match low order 2 bits + int tmp = ((ti->get_con()&3) == (_test&3)) ? 1 : 0; + if( _test & 4 ) tmp = 1-tmp; // Optionally complement result + return TypeInt::make(tmp); // Boolean result + } + + if( CC == TypeInt::CC_GE ) { + if( _test == ge ) return TypeInt::ONE; + if( _test == lt ) return TypeInt::ZERO; + } + if( CC == TypeInt::CC_LE ) { + if( _test == le ) return TypeInt::ONE; + if( _test == gt ) return TypeInt::ZERO; + } + + return TypeInt::BOOL; +} + +//------------------------------dump_spec------------------------------------- +// Print special per-node info +#ifndef PRODUCT +void BoolTest::dump_on(outputStream *st) const { + const char *msg[] = {"eq","gt","??","lt","ne","le","??","ge"}; + st->print(msg[_test]); +} +#endif + +//============================================================================= +uint BoolNode::hash() const { return (Node::hash() << 3)|(_test._test+1); } +uint BoolNode::size_of() const { return sizeof(BoolNode); } + +//------------------------------operator==------------------------------------- +uint BoolNode::cmp( const Node &n ) const { + const BoolNode *b = (const BoolNode *)&n; // Cast up + return (_test._test == b->_test._test); +} + +//------------------------------clone_cmp-------------------------------------- +// Clone a compare/bool tree +static Node *clone_cmp( Node *cmp, Node *cmp1, Node *cmp2, PhaseGVN *gvn, BoolTest::mask test ) { + Node *ncmp = cmp->clone(); + ncmp->set_req(1,cmp1); + ncmp->set_req(2,cmp2); + ncmp = gvn->transform( ncmp ); + return new (gvn->C, 2) BoolNode( ncmp, test ); +} + +//-------------------------------make_predicate-------------------------------- +Node* BoolNode::make_predicate(Node* test_value, PhaseGVN* phase) { + if (test_value->is_Con()) return test_value; + if (test_value->is_Bool()) return test_value; + Compile* C = phase->C; + if (test_value->is_CMove() && + test_value->in(CMoveNode::Condition)->is_Bool()) { + BoolNode* bol = test_value->in(CMoveNode::Condition)->as_Bool(); + const Type* ftype = phase->type(test_value->in(CMoveNode::IfFalse)); + const Type* ttype = phase->type(test_value->in(CMoveNode::IfTrue)); + if (ftype == TypeInt::ZERO && !TypeInt::ZERO->higher_equal(ttype)) { + return bol; + } else if (ttype == TypeInt::ZERO && !TypeInt::ZERO->higher_equal(ftype)) { + return phase->transform( bol->negate(phase) ); + } + // Else fall through. The CMove gets in the way of the test. + // It should be the case that make_predicate(bol->as_int_value()) == bol. + } + Node* cmp = new (C, 3) CmpINode(test_value, phase->intcon(0)); + cmp = phase->transform(cmp); + Node* bol = new (C, 2) BoolNode(cmp, BoolTest::ne); + return phase->transform(bol); +} + +//--------------------------------as_int_value--------------------------------- +Node* BoolNode::as_int_value(PhaseGVN* phase) { + // Inverse to make_predicate. The CMove probably boils down to a Conv2B. + Node* cmov = CMoveNode::make(phase->C, NULL, this, + phase->intcon(0), phase->intcon(1), + TypeInt::BOOL); + return phase->transform(cmov); +} + +//----------------------------------negate------------------------------------- +BoolNode* BoolNode::negate(PhaseGVN* phase) { + Compile* C = phase->C; + return new (C, 2) BoolNode(in(1), _test.negate()); +} + + +//------------------------------Ideal------------------------------------------ +Node *BoolNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // Change "bool tst (cmp con x)" into "bool ~tst (cmp x con)". + // This moves the constant to the right. Helps value-numbering. + Node *cmp = in(1); + if( !cmp->is_Sub() ) return NULL; + int cop = cmp->Opcode(); + if( cop == Op_FastLock || cop == Op_FastUnlock ) return NULL; + Node *cmp1 = cmp->in(1); + Node *cmp2 = cmp->in(2); + if( !cmp1 ) return NULL; + + // Constant on left? + Node *con = cmp1; + uint op2 = cmp2->Opcode(); + // Move constants to the right of compare's to canonicalize. + // Do not muck with Opaque1 nodes, as this indicates a loop + // guard that cannot change shape. + if( con->is_Con() && !cmp2->is_Con() && op2 != Op_Opaque1 && + // Because of NaN's, CmpD and CmpF are not commutative + cop != Op_CmpD && cop != Op_CmpF && + // Protect against swapping inputs to a compare when it is used by a + // counted loop exit, which requires maintaining the loop-limit as in(2) + !is_counted_loop_exit_test() ) { + // Ok, commute the constant to the right of the cmp node. + // Clone the Node, getting a new Node of the same class + cmp = cmp->clone(); + // Swap inputs to the clone + cmp->swap_edges(1, 2); + cmp = phase->transform( cmp ); + return new (phase->C, 2) BoolNode( cmp, _test.commute() ); + } + + // Change "bool eq/ne (cmp (xor X 1) 0)" into "bool ne/eq (cmp X 0)". + // The XOR-1 is an idiom used to flip the sense of a bool. We flip the + // test instead. + int cmp1_op = cmp1->Opcode(); + const TypeInt* cmp2_type = phase->type(cmp2)->isa_int(); + if (cmp2_type == NULL) return NULL; + Node* j_xor = cmp1; + if( cmp2_type == TypeInt::ZERO && + cmp1_op == Op_XorI && + j_xor->in(1) != j_xor && // An xor of itself is dead + phase->type( j_xor->in(2) ) == TypeInt::ONE && + (_test._test == BoolTest::eq || + _test._test == BoolTest::ne) ) { + Node *ncmp = phase->transform(new (phase->C, 3) CmpINode(j_xor->in(1),cmp2)); + return new (phase->C, 2) BoolNode( ncmp, _test.negate() ); + } + + // Change "bool eq/ne (cmp (Conv2B X) 0)" into "bool eq/ne (cmp X 0)". + // This is a standard idiom for branching on a boolean value. + Node *c2b = cmp1; + if( cmp2_type == TypeInt::ZERO && + cmp1_op == Op_Conv2B && + (_test._test == BoolTest::eq || + _test._test == BoolTest::ne) ) { + Node *ncmp = phase->transform(phase->type(c2b->in(1))->isa_int() + ? (Node*)new (phase->C, 3) CmpINode(c2b->in(1),cmp2) + : (Node*)new (phase->C, 3) CmpPNode(c2b->in(1),phase->makecon(TypePtr::NULL_PTR)) + ); + return new (phase->C, 2) BoolNode( ncmp, _test._test ); + } + + // Comparing a SubI against a zero is equal to comparing the SubI + // arguments directly. This only works for eq and ne comparisons + // due to possible integer overflow. + if ((_test._test == BoolTest::eq || _test._test == BoolTest::ne) && + (cop == Op_CmpI) && + (cmp1->Opcode() == Op_SubI) && + ( cmp2_type == TypeInt::ZERO ) ) { + Node *ncmp = phase->transform( new (phase->C, 3) CmpINode(cmp1->in(1),cmp1->in(2))); + return new (phase->C, 2) BoolNode( ncmp, _test._test ); + } + + // Change (-A vs 0) into (A vs 0) by commuting the test. Disallow in the + // most general case because negating 0x80000000 does nothing. Needed for + // the CmpF3/SubI/CmpI idiom. + if( cop == Op_CmpI && + cmp1->Opcode() == Op_SubI && + cmp2_type == TypeInt::ZERO && + phase->type( cmp1->in(1) ) == TypeInt::ZERO && + phase->type( cmp1->in(2) )->higher_equal(TypeInt::SYMINT) ) { + Node *ncmp = phase->transform( new (phase->C, 3) CmpINode(cmp1->in(2),cmp2)); + return new (phase->C, 2) BoolNode( ncmp, _test.commute() ); + } + + // The transformation below is not valid for either signed or unsigned + // comparisons due to wraparound concerns at MAX_VALUE and MIN_VALUE. + // This transformation can be resurrected when we are able to + // make inferences about the range of values being subtracted from + // (or added to) relative to the wraparound point. + // + // // Remove +/-1's if possible. + // // "X <= Y-1" becomes "X < Y" + // // "X+1 <= Y" becomes "X < Y" + // // "X < Y+1" becomes "X <= Y" + // // "X-1 < Y" becomes "X <= Y" + // // Do not this to compares off of the counted-loop-end. These guys are + // // checking the trip counter and they want to use the post-incremented + // // counter. If they use the PRE-incremented counter, then the counter has + // // to be incremented in a private block on a loop backedge. + // if( du && du->cnt(this) && du->out(this)[0]->Opcode() == Op_CountedLoopEnd ) + // return NULL; + // #ifndef PRODUCT + // // Do not do this in a wash GVN pass during verification. + // // Gets triggered by too many simple optimizations to be bothered with + // // re-trying it again and again. + // if( !phase->allow_progress() ) return NULL; + // #endif + // // Not valid for unsigned compare because of corner cases in involving zero. + // // For example, replacing "X-1 Opcode() == Op_CmpU ) return NULL; + // int cmp2_op = cmp2->Opcode(); + // if( _test._test == BoolTest::le ) { + // if( cmp1_op == Op_AddI && + // phase->type( cmp1->in(2) ) == TypeInt::ONE ) + // return clone_cmp( cmp, cmp1->in(1), cmp2, phase, BoolTest::lt ); + // else if( cmp2_op == Op_AddI && + // phase->type( cmp2->in(2) ) == TypeInt::MINUS_1 ) + // return clone_cmp( cmp, cmp1, cmp2->in(1), phase, BoolTest::lt ); + // } else if( _test._test == BoolTest::lt ) { + // if( cmp1_op == Op_AddI && + // phase->type( cmp1->in(2) ) == TypeInt::MINUS_1 ) + // return clone_cmp( cmp, cmp1->in(1), cmp2, phase, BoolTest::le ); + // else if( cmp2_op == Op_AddI && + // phase->type( cmp2->in(2) ) == TypeInt::ONE ) + // return clone_cmp( cmp, cmp1, cmp2->in(1), phase, BoolTest::le ); + // } + + return NULL; +} + +//------------------------------Value------------------------------------------ +// Simplify a Bool (convert condition codes to boolean (1 or 0)) node, +// based on local information. If the input is constant, do it. +const Type *BoolNode::Value( PhaseTransform *phase ) const { + return _test.cc2logical( phase->type( in(1) ) ); +} + +//------------------------------dump_spec-------------------------------------- +// Dump special per-node info +#ifndef PRODUCT +void BoolNode::dump_spec(outputStream *st) const { + st->print("["); + _test.dump_on(st); + st->print("]"); +} +#endif + +//------------------------------is_counted_loop_exit_test-------------------------------------- +// Returns true if node is used by a counted loop node. +bool BoolNode::is_counted_loop_exit_test() { + for( DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++ ) { + Node* use = fast_out(i); + if (use->is_CountedLoopEnd()) { + return true; + } + } + return false; +} + +//============================================================================= +//------------------------------NegNode---------------------------------------- +Node *NegFNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if( in(1)->Opcode() == Op_SubF ) + return new (phase->C, 3) SubFNode( in(1)->in(2), in(1)->in(1) ); + return NULL; +} + +Node *NegDNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if( in(1)->Opcode() == Op_SubD ) + return new (phase->C, 3) SubDNode( in(1)->in(2), in(1)->in(1) ); + return NULL; +} + + +//============================================================================= +//------------------------------Value------------------------------------------ +// Compute sqrt +const Type *SqrtDNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; + double d = t1->getd(); + if( d < 0.0 ) return Type::DOUBLE; + return TypeD::make( sqrt( d ) ); +} + +//============================================================================= +//------------------------------Value------------------------------------------ +// Compute cos +const Type *CosDNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; + double d = t1->getd(); + if( d < 0.0 ) return Type::DOUBLE; + return TypeD::make( SharedRuntime::dcos( d ) ); +} + +//============================================================================= +//------------------------------Value------------------------------------------ +// Compute sin +const Type *SinDNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; + double d = t1->getd(); + if( d < 0.0 ) return Type::DOUBLE; + return TypeD::make( SharedRuntime::dsin( d ) ); +} + +//============================================================================= +//------------------------------Value------------------------------------------ +// Compute tan +const Type *TanDNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; + double d = t1->getd(); + if( d < 0.0 ) return Type::DOUBLE; + return TypeD::make( SharedRuntime::dtan( d ) ); +} + +//============================================================================= +//------------------------------Value------------------------------------------ +// Compute log +const Type *LogDNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; + double d = t1->getd(); + if( d < 0.0 ) return Type::DOUBLE; + return TypeD::make( SharedRuntime::dlog( d ) ); +} + +//============================================================================= +//------------------------------Value------------------------------------------ +// Compute log10 +const Type *Log10DNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; + double d = t1->getd(); + if( d < 0.0 ) return Type::DOUBLE; + return TypeD::make( SharedRuntime::dlog10( d ) ); +} + +//============================================================================= +//------------------------------Value------------------------------------------ +// Compute exp +const Type *ExpDNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; + double d = t1->getd(); + if( d < 0.0 ) return Type::DOUBLE; + return TypeD::make( SharedRuntime::dexp( d ) ); +} + + +//============================================================================= +//------------------------------Value------------------------------------------ +// Compute pow +const Type *PowDNode::Value( PhaseTransform *phase ) const { + const Type *t1 = phase->type( in(1) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; + const Type *t2 = phase->type( in(2) ); + if( t2 == Type::TOP ) return Type::TOP; + if( t2->base() != Type::DoubleCon ) return Type::DOUBLE; + double d1 = t1->getd(); + double d2 = t2->getd(); + if( d1 < 0.0 ) return Type::DOUBLE; + if( d2 < 0.0 ) return Type::DOUBLE; + return TypeD::make( SharedRuntime::dpow( d1, d2 ) ); +} diff --git a/hotspot/src/share/vm/opto/subnode.hpp b/hotspot/src/share/vm/opto/subnode.hpp new file mode 100644 index 00000000000..4992a59c555 --- /dev/null +++ b/hotspot/src/share/vm/opto/subnode.hpp @@ -0,0 +1,501 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +//------------------------------SUBNode---------------------------------------- +// Class SUBTRACTION functionality. This covers all the usual 'subtract' +// behaviors. Subtract-integer, -float, -double, binary xor, compare-integer, +// -float, and -double are all inherited from this class. The compare +// functions behave like subtract functions, except that all negative answers +// are compressed into -1, and all positive answers compressed to 1. +class SubNode : public Node { +public: + SubNode( Node *in1, Node *in2 ) : Node(0,in1,in2) { + init_class_id(Class_Sub); + } + + // Handle algebraic identities here. If we have an identity, return the Node + // we are equivalent to. We look for "add of zero" as an identity. + virtual Node *Identity( PhaseTransform *phase ); + + // Compute a new Type for this node. Basically we just do the pre-check, + // then call the virtual add() to set the type. + virtual const Type *Value( PhaseTransform *phase ) const; + + // Supplied function returns the subtractend of the inputs. + // This also type-checks the inputs for sanity. Guaranteed never to + // be passed a TOP or BOTTOM type, these are filtered out by a pre-check. + virtual const Type *sub( const Type *, const Type * ) const = 0; + + // Supplied function to return the additive identity type. + // This is returned whenever the subtracts inputs are the same. + virtual const Type *add_id() const = 0; + +}; + + +// NOTE: SubINode should be taken away and replaced by add and negate +//------------------------------SubINode--------------------------------------- +// Subtract 2 integers +class SubINode : public SubNode { +public: + SubINode( Node *in1, Node *in2 ) : SubNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *sub( const Type *, const Type * ) const; + const Type *add_id() const { return TypeInt::ZERO; } + const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------SubLNode--------------------------------------- +// Subtract 2 integers +class SubLNode : public SubNode { +public: + SubLNode( Node *in1, Node *in2 ) : SubNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *sub( const Type *, const Type * ) const; + const Type *add_id() const { return TypeLong::ZERO; } + const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + +// NOTE: SubFPNode should be taken away and replaced by add and negate +//------------------------------SubFPNode-------------------------------------- +// Subtract 2 floats or doubles +class SubFPNode : public SubNode { +protected: + SubFPNode( Node *in1, Node *in2 ) : SubNode(in1,in2) {} +public: + const Type *Value( PhaseTransform *phase ) const; +}; + +// NOTE: SubFNode should be taken away and replaced by add and negate +//------------------------------SubFNode--------------------------------------- +// Subtract 2 doubles +class SubFNode : public SubFPNode { +public: + SubFNode( Node *in1, Node *in2 ) : SubFPNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *sub( const Type *, const Type * ) const; + const Type *add_id() const { return TypeF::ZERO; } + const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +// NOTE: SubDNode should be taken away and replaced by add and negate +//------------------------------SubDNode--------------------------------------- +// Subtract 2 doubles +class SubDNode : public SubFPNode { +public: + SubDNode( Node *in1, Node *in2 ) : SubFPNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *sub( const Type *, const Type * ) const; + const Type *add_id() const { return TypeD::ZERO; } + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + +//------------------------------CmpNode--------------------------------------- +// Compare 2 values, returning condition codes (-1, 0 or 1). +class CmpNode : public SubNode { +public: + CmpNode( Node *in1, Node *in2 ) : SubNode(in1,in2) { + init_class_id(Class_Cmp); + } + virtual Node *Identity( PhaseTransform *phase ); + const Type *add_id() const { return TypeInt::ZERO; } + const Type *bottom_type() const { return TypeInt::CC; } + virtual uint ideal_reg() const { return Op_RegFlags; } +}; + +//------------------------------CmpINode--------------------------------------- +// Compare 2 signed values, returning condition codes (-1, 0 or 1). +class CmpINode : public CmpNode { +public: + CmpINode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *sub( const Type *, const Type * ) const; +}; + +//------------------------------CmpUNode--------------------------------------- +// Compare 2 unsigned values (integer or pointer), returning condition codes (-1, 0 or 1). +class CmpUNode : public CmpNode { +public: + CmpUNode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *sub( const Type *, const Type * ) const; +}; + +//------------------------------CmpPNode--------------------------------------- +// Compare 2 pointer values, returning condition codes (-1, 0 or 1). +class CmpPNode : public CmpNode { +public: + CmpPNode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *sub( const Type *, const Type * ) const; +}; + +//------------------------------CmpLNode--------------------------------------- +// Compare 2 long values, returning condition codes (-1, 0 or 1). +class CmpLNode : public CmpNode { +public: + CmpLNode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *sub( const Type *, const Type * ) const; +}; + +//------------------------------CmpL3Node-------------------------------------- +// Compare 2 long values, returning integer value (-1, 0 or 1). +class CmpL3Node : public CmpLNode { +public: + CmpL3Node( Node *in1, Node *in2 ) : CmpLNode(in1,in2) { + // Since it is not consumed by Bools, it is not really a Cmp. + init_class_id(Class_Sub); + } + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------CmpFNode--------------------------------------- +// Compare 2 float values, returning condition codes (-1, 0 or 1). +// This implements the Java bytecode fcmpl, so unordered returns -1. +// Operands may not commute. +class CmpFNode : public CmpNode { +public: + CmpFNode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *sub( const Type *, const Type * ) const { ShouldNotReachHere(); return NULL; } + const Type *Value( PhaseTransform *phase ) const; +}; + +//------------------------------CmpF3Node-------------------------------------- +// Compare 2 float values, returning integer value (-1, 0 or 1). +// This implements the Java bytecode fcmpl, so unordered returns -1. +// Operands may not commute. +class CmpF3Node : public CmpFNode { +public: + CmpF3Node( Node *in1, Node *in2 ) : CmpFNode(in1,in2) { + // Since it is not consumed by Bools, it is not really a Cmp. + init_class_id(Class_Sub); + } + virtual int Opcode() const; + // Since it is not consumed by Bools, it is not really a Cmp. + virtual uint ideal_reg() const { return Op_RegI; } +}; + + +//------------------------------CmpDNode--------------------------------------- +// Compare 2 double values, returning condition codes (-1, 0 or 1). +// This implements the Java bytecode dcmpl, so unordered returns -1. +// Operands may not commute. +class CmpDNode : public CmpNode { +public: + CmpDNode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {} + virtual int Opcode() const; + virtual const Type *sub( const Type *, const Type * ) const { ShouldNotReachHere(); return NULL; } + const Type *Value( PhaseTransform *phase ) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); +}; + +//------------------------------CmpD3Node-------------------------------------- +// Compare 2 double values, returning integer value (-1, 0 or 1). +// This implements the Java bytecode dcmpl, so unordered returns -1. +// Operands may not commute. +class CmpD3Node : public CmpDNode { +public: + CmpD3Node( Node *in1, Node *in2 ) : CmpDNode(in1,in2) { + // Since it is not consumed by Bools, it is not really a Cmp. + init_class_id(Class_Sub); + } + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegI; } +}; + + +//------------------------------BoolTest--------------------------------------- +// Convert condition codes to a boolean test value (0 or -1). +// We pick the values as 3 bits; the low order 2 bits we compare against the +// condition codes, the high bit flips the sense of the result. +struct BoolTest VALUE_OBJ_CLASS_SPEC { + enum mask { eq = 0, ne = 4, le = 5, ge = 7, lt = 3, gt = 1, illegal = 8 }; + mask _test; + BoolTest( mask btm ) : _test(btm) {} + const Type *cc2logical( const Type *CC ) const; + // Commute the test. I use a small table lookup. The table is created as + // a simple char array where each element is the ASCII version of a 'mask' + // enum from above. + mask commute( ) const { return mask("038147858"[_test]-'0'); } + mask negate( ) const { return mask(_test^4); } + bool is_canonical( ) const { return (_test == BoolTest::ne || _test == BoolTest::lt || _test == BoolTest::le); } +#ifndef PRODUCT + void dump_on(outputStream *st) const; +#endif +}; + +//------------------------------BoolNode--------------------------------------- +// A Node to convert a Condition Codes to a Logical result. +class BoolNode : public Node { + virtual uint hash() const; + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; +public: + const BoolTest _test; + BoolNode( Node *cc, BoolTest::mask t): _test(t), Node(0,cc) { + init_class_id(Class_Bool); + } + // Convert an arbitrary int value to a Bool or other suitable predicate. + static Node* make_predicate(Node* test_value, PhaseGVN* phase); + // Convert self back to an integer value. + Node* as_int_value(PhaseGVN* phase); + // Invert sense of self, returning new Bool. + BoolNode* negate(PhaseGVN* phase); + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type *bottom_type() const { return TypeInt::BOOL; } + uint match_edge(uint idx) const { return 0; } + virtual uint ideal_reg() const { return Op_RegI; } + + bool is_counted_loop_exit_test(); +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; + +//------------------------------AbsNode---------------------------------------- +// Abstract class for absolute value. Mostly used to get a handy wrapper +// for finding this pattern in the graph. +class AbsNode : public Node { +public: + AbsNode( Node *value ) : Node(0,value) {} +}; + +//------------------------------AbsINode--------------------------------------- +// Absolute value an integer. Since a naive graph involves control flow, we +// "match" it in the ideal world (so the control flow can be removed). +class AbsINode : public AbsNode { +public: + AbsINode( Node *in1 ) : AbsNode(in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------AbsFNode--------------------------------------- +// Absolute value a float, a common float-point idiom with a cheap hardware +// implemention on most chips. Since a naive graph involves control flow, we +// "match" it in the ideal world (so the control flow can be removed). +class AbsFNode : public AbsNode { +public: + AbsFNode( Node *in1 ) : AbsNode(in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------AbsDNode--------------------------------------- +// Absolute value a double, a common float-point idiom with a cheap hardware +// implemention on most chips. Since a naive graph involves control flow, we +// "match" it in the ideal world (so the control flow can be removed). +class AbsDNode : public AbsNode { +public: + AbsDNode( Node *in1 ) : AbsNode(in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + + +//------------------------------CmpLTMaskNode---------------------------------- +// If p < q, return -1 else return 0. Nice for flow-free idioms. +class CmpLTMaskNode : public Node { +public: + CmpLTMaskNode( Node *p, Node *q ) : Node(0, p, q) {} + virtual int Opcode() const; + const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + + +//------------------------------NegNode---------------------------------------- +class NegNode : public Node { +public: + NegNode( Node *in1 ) : Node(0,in1) {} +}; + +//------------------------------NegFNode--------------------------------------- +// Negate value a float. Negating 0.0 returns -0.0, but subtracting from +// zero returns +0.0 (per JVM spec on 'fneg' bytecode). As subtraction +// cannot be used to replace negation we have to implement negation as ideal +// node; note that negation and addition can replace subtraction. +class NegFNode : public NegNode { +public: + NegFNode( Node *in1 ) : NegNode(in1) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------NegDNode--------------------------------------- +// Negate value a double. Negating 0.0 returns -0.0, but subtracting from +// zero returns +0.0 (per JVM spec on 'dneg' bytecode). As subtraction +// cannot be used to replace negation we have to implement negation as ideal +// node; note that negation and addition can replace subtraction. +class NegDNode : public NegNode { +public: + NegDNode( Node *in1 ) : NegNode(in1) {} + virtual int Opcode() const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + +//------------------------------CosDNode--------------------------------------- +// Cosinus of a double +class CosDNode : public Node { +public: + CosDNode( Node *in1 ) : Node(0, in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } + virtual const Type *Value( PhaseTransform *phase ) const; +}; + +//------------------------------CosDNode--------------------------------------- +// Sinus of a double +class SinDNode : public Node { +public: + SinDNode( Node *in1 ) : Node(0, in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } + virtual const Type *Value( PhaseTransform *phase ) const; +}; + + +//------------------------------TanDNode--------------------------------------- +// tangens of a double +class TanDNode : public Node { +public: + TanDNode(Node *in1 ) : Node(0, in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } + virtual const Type *Value( PhaseTransform *phase ) const; +}; + + +//------------------------------AtanDNode-------------------------------------- +// arcus tangens of a double +class AtanDNode : public Node { +public: + AtanDNode(Node *c, Node *in1, Node *in2 ) : Node(c, in1, in2) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + + +//------------------------------SqrtDNode-------------------------------------- +// square root a double +class SqrtDNode : public Node { +public: + SqrtDNode(Node *c, Node *in1 ) : Node(c, in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } + virtual const Type *Value( PhaseTransform *phase ) const; +}; + +//------------------------------ExpDNode--------------------------------------- +// Exponentiate a double +class ExpDNode : public Node { +public: + ExpDNode( Node *c, Node *in1 ) : Node(c, in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } + virtual const Type *Value( PhaseTransform *phase ) const; +}; + +//------------------------------LogDNode--------------------------------------- +// Log_e of a double +class LogDNode : public Node { +public: + LogDNode( Node *in1 ) : Node(0, in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } + virtual const Type *Value( PhaseTransform *phase ) const; +}; + +//------------------------------Log10DNode--------------------------------------- +// Log_10 of a double +class Log10DNode : public Node { +public: + Log10DNode( Node *in1 ) : Node(0, in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } + virtual const Type *Value( PhaseTransform *phase ) const; +}; + +//------------------------------PowDNode--------------------------------------- +// Raise a double to a double power +class PowDNode : public Node { +public: + PowDNode(Node *c, Node *in1, Node *in2 ) : Node(c, in1, in2) {} + virtual int Opcode() const; + const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } + virtual const Type *Value( PhaseTransform *phase ) const; +}; + +//-------------------------------ReverseBytesINode-------------------------------- +// reverse bytes of an integer +class ReverseBytesINode : public Node { +public: + ReverseBytesINode(Node *c, Node *in1) : Node(c, in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//-------------------------------ReverseBytesLNode-------------------------------- +// reverse bytes of a long +class ReverseBytesLNode : public Node { +public: + ReverseBytesLNode(Node *c, Node *in1) : Node(c, in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; diff --git a/hotspot/src/share/vm/opto/superword.cpp b/hotspot/src/share/vm/opto/superword.cpp new file mode 100644 index 00000000000..b1467fc9e05 --- /dev/null +++ b/hotspot/src/share/vm/opto/superword.cpp @@ -0,0 +1,2025 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#include "incls/_precompiled.incl" +#include "incls/_superword.cpp.incl" + +// +// S U P E R W O R D T R A N S F O R M +//============================================================================= + +//------------------------------SuperWord--------------------------- +SuperWord::SuperWord(PhaseIdealLoop* phase) : + _phase(phase), + _igvn(phase->_igvn), + _arena(phase->C->comp_arena()), + _packset(arena(), 8, 0, NULL), // packs for the current block + _bb_idx(arena(), (int)(1.10 * phase->C->unique()), 0, 0), // node idx to index in bb + _block(arena(), 8, 0, NULL), // nodes in current block + _data_entry(arena(), 8, 0, NULL), // nodes with all inputs from outside + _mem_slice_head(arena(), 8, 0, NULL), // memory slice heads + _mem_slice_tail(arena(), 8, 0, NULL), // memory slice tails + _node_info(arena(), 8, 0, SWNodeInfo::initial), // info needed per node + _align_to_ref(NULL), // memory reference to align vectors to + _disjoint_ptrs(arena(), 8, 0, OrderedPair::initial), // runtime disambiguated pointer pairs + _dg(_arena), // dependence graph + _visited(arena()), // visited node set + _post_visited(arena()), // post visited node set + _n_idx_list(arena(), 8), // scratch list of (node,index) pairs + _stk(arena(), 8, 0, NULL), // scratch stack of nodes + _nlist(arena(), 8, 0, NULL), // scratch list of nodes + _lpt(NULL), // loop tree node + _lp(NULL), // LoopNode + _bb(NULL), // basic block + _iv(NULL) // induction var +{} + +//------------------------------transform_loop--------------------------- +void SuperWord::transform_loop(IdealLoopTree* lpt) { + assert(lpt->_head->is_CountedLoop(), "must be"); + CountedLoopNode *cl = lpt->_head->as_CountedLoop(); + + if (!cl->is_main_loop() ) return; // skip normal, pre, and post loops + + // Check for no control flow in body (other than exit) + Node *cl_exit = cl->loopexit(); + if (cl_exit->in(0) != lpt->_head) return; + + // Check for pre-loop ending with CountedLoopEnd(Bool(Cmp(x,Opaque1(limit)))) + CountedLoopEndNode* pre_end = get_pre_loop_end(cl); + if (pre_end == NULL) return; + Node *pre_opaq1 = pre_end->limit(); + if (pre_opaq1->Opcode() != Op_Opaque1) return; + + // Do vectors exist on this architecture? + if (vector_width_in_bytes() == 0) return; + + init(); // initialize data structures + + set_lpt(lpt); + set_lp(cl); + + // For now, define one block which is the entire loop body + set_bb(cl); + + assert(_packset.length() == 0, "packset must be empty"); + SLP_extract(); +} + +//------------------------------SLP_extract--------------------------- +// Extract the superword level parallelism +// +// 1) A reverse post-order of nodes in the block is constructed. By scanning +// this list from first to last, all definitions are visited before their uses. +// +// 2) A point-to-point dependence graph is constructed between memory references. +// This simplies the upcoming "independence" checker. +// +// 3) The maximum depth in the node graph from the beginning of the block +// to each node is computed. This is used to prune the graph search +// in the independence checker. +// +// 4) For integer types, the necessary bit width is propagated backwards +// from stores to allow packed operations on byte, char, and short +// integers. This reverses the promotion to type "int" that javac +// did for operations like: char c1,c2,c3; c1 = c2 + c3. +// +// 5) One of the memory references is picked to be an aligned vector reference. +// The pre-loop trip count is adjusted to align this reference in the +// unrolled body. +// +// 6) The initial set of pack pairs is seeded with memory references. +// +// 7) The set of pack pairs is extended by following use->def and def->use links. +// +// 8) The pairs are combined into vector sized packs. +// +// 9) Reorder the memory slices to co-locate members of the memory packs. +// +// 10) Generate ideal vector nodes for the final set of packs and where necessary, +// inserting scalar promotion, vector creation from multiple scalars, and +// extraction of scalar values from vectors. +// +void SuperWord::SLP_extract() { + + // Ready the block + + construct_bb(); + + dependence_graph(); + + compute_max_depth(); + + compute_vector_element_type(); + + // Attempt vectorization + + find_adjacent_refs(); + + extend_packlist(); + + combine_packs(); + + construct_my_pack_map(); + + filter_packs(); + + schedule(); + + output(); +} + +//------------------------------find_adjacent_refs--------------------------- +// Find the adjacent memory references and create pack pairs for them. +// This is the initial set of packs that will then be extended by +// following use->def and def->use links. The align positions are +// assigned relative to the reference "align_to_ref" +void SuperWord::find_adjacent_refs() { + // Get list of memory operations + Node_List memops; + for (int i = 0; i < _block.length(); i++) { + Node* n = _block.at(i); + if (n->is_Mem() && in_bb(n)) { + int align = memory_alignment(n->as_Mem(), 0); + if (align != bottom_align) { + memops.push(n); + } + } + } + if (memops.size() == 0) return; + + // Find a memory reference to align to. The pre-loop trip count + // is modified to align this reference to a vector-aligned address + find_align_to_ref(memops); + if (align_to_ref() == NULL) return; + + SWPointer align_to_ref_p(align_to_ref(), this); + int offset = align_to_ref_p.offset_in_bytes(); + int scale = align_to_ref_p.scale_in_bytes(); + int vw = vector_width_in_bytes(); + int stride_sign = (scale * iv_stride()) > 0 ? 1 : -1; + int iv_adjustment = (stride_sign * vw - (offset % vw)) % vw; + +#ifndef PRODUCT + if (TraceSuperWord) + tty->print_cr("\noffset = %d iv_adjustment = %d elt_align = %d", + offset, iv_adjustment, align_to_ref_p.memory_size()); +#endif + + // Set alignment relative to "align_to_ref" + for (int i = memops.size() - 1; i >= 0; i--) { + MemNode* s = memops.at(i)->as_Mem(); + SWPointer p2(s, this); + if (p2.comparable(align_to_ref_p)) { + int align = memory_alignment(s, iv_adjustment); + set_alignment(s, align); + } else { + memops.remove(i); + } + } + + // Create initial pack pairs of memory operations + for (uint i = 0; i < memops.size(); i++) { + Node* s1 = memops.at(i); + for (uint j = 0; j < memops.size(); j++) { + Node* s2 = memops.at(j); + if (s1 != s2 && are_adjacent_refs(s1, s2)) { + int align = alignment(s1); + if (stmts_can_pack(s1, s2, align)) { + Node_List* pair = new Node_List(); + pair->push(s1); + pair->push(s2); + _packset.append(pair); + } + } + } + } + +#ifndef PRODUCT + if (TraceSuperWord) { + tty->print_cr("\nAfter find_adjacent_refs"); + print_packset(); + } +#endif +} + +//------------------------------find_align_to_ref--------------------------- +// Find a memory reference to align the loop induction variable to. +// Looks first at stores then at loads, looking for a memory reference +// with the largest number of references similar to it. +void SuperWord::find_align_to_ref(Node_List &memops) { + GrowableArray cmp_ct(arena(), memops.size(), memops.size(), 0); + + // Count number of comparable memory ops + for (uint i = 0; i < memops.size(); i++) { + MemNode* s1 = memops.at(i)->as_Mem(); + SWPointer p1(s1, this); + // Discard if pre loop can't align this reference + if (!ref_is_alignable(p1)) { + *cmp_ct.adr_at(i) = 0; + continue; + } + for (uint j = i+1; j < memops.size(); j++) { + MemNode* s2 = memops.at(j)->as_Mem(); + if (isomorphic(s1, s2)) { + SWPointer p2(s2, this); + if (p1.comparable(p2)) { + (*cmp_ct.adr_at(i))++; + (*cmp_ct.adr_at(j))++; + } + } + } + } + + // Find Store (or Load) with the greatest number of "comparable" references + int max_ct = 0; + int max_idx = -1; + int min_size = max_jint; + int min_iv_offset = max_jint; + for (uint j = 0; j < memops.size(); j++) { + MemNode* s = memops.at(j)->as_Mem(); + if (s->is_Store()) { + SWPointer p(s, this); + if (cmp_ct.at(j) > max_ct || + cmp_ct.at(j) == max_ct && (data_size(s) < min_size || + data_size(s) == min_size && + p.offset_in_bytes() < min_iv_offset)) { + max_ct = cmp_ct.at(j); + max_idx = j; + min_size = data_size(s); + min_iv_offset = p.offset_in_bytes(); + } + } + } + // If no stores, look at loads + if (max_ct == 0) { + for (uint j = 0; j < memops.size(); j++) { + MemNode* s = memops.at(j)->as_Mem(); + if (s->is_Load()) { + SWPointer p(s, this); + if (cmp_ct.at(j) > max_ct || + cmp_ct.at(j) == max_ct && (data_size(s) < min_size || + data_size(s) == min_size && + p.offset_in_bytes() < min_iv_offset)) { + max_ct = cmp_ct.at(j); + max_idx = j; + min_size = data_size(s); + min_iv_offset = p.offset_in_bytes(); + } + } + } + } + + if (max_ct > 0) + set_align_to_ref(memops.at(max_idx)->as_Mem()); + +#ifndef PRODUCT + if (TraceSuperWord && Verbose) { + tty->print_cr("\nVector memops after find_align_to_refs"); + for (uint i = 0; i < memops.size(); i++) { + MemNode* s = memops.at(i)->as_Mem(); + s->dump(); + } + } +#endif +} + +//------------------------------ref_is_alignable--------------------------- +// Can the preloop align the reference to position zero in the vector? +bool SuperWord::ref_is_alignable(SWPointer& p) { + if (!p.has_iv()) { + return true; // no induction variable + } + CountedLoopEndNode* pre_end = get_pre_loop_end(lp()->as_CountedLoop()); + assert(pre_end->stride_is_con(), "pre loop stride is constant"); + int preloop_stride = pre_end->stride_con(); + + int span = preloop_stride * p.scale_in_bytes(); + + // Stride one accesses are alignable. + if (ABS(span) == p.memory_size()) + return true; + + // If initial offset from start of object is computable, + // compute alignment within the vector. + int vw = vector_width_in_bytes(); + if (vw % span == 0) { + Node* init_nd = pre_end->init_trip(); + if (init_nd->is_Con() && p.invar() == NULL) { + int init = init_nd->bottom_type()->is_int()->get_con(); + + int init_offset = init * p.scale_in_bytes() + p.offset_in_bytes(); + assert(init_offset >= 0, "positive offset from object start"); + + if (span > 0) { + return (vw - (init_offset % vw)) % span == 0; + } else { + assert(span < 0, "nonzero stride * scale"); + return (init_offset % vw) % -span == 0; + } + } + } + return false; +} + +//---------------------------dependence_graph--------------------------- +// Construct dependency graph. +// Add dependence edges to load/store nodes for memory dependence +// A.out()->DependNode.in(1) and DependNode.out()->B.prec(x) +void SuperWord::dependence_graph() { + // First, assign a dependence node to each memory node + for (int i = 0; i < _block.length(); i++ ) { + Node *n = _block.at(i); + if (n->is_Mem() || n->is_Phi() && n->bottom_type() == Type::MEMORY) { + _dg.make_node(n); + } + } + + // For each memory slice, create the dependences + for (int i = 0; i < _mem_slice_head.length(); i++) { + Node* n = _mem_slice_head.at(i); + Node* n_tail = _mem_slice_tail.at(i); + + // Get slice in predecessor order (last is first) + mem_slice_preds(n_tail, n, _nlist); + + // Make the slice dependent on the root + DepMem* slice = _dg.dep(n); + _dg.make_edge(_dg.root(), slice); + + // Create a sink for the slice + DepMem* slice_sink = _dg.make_node(NULL); + _dg.make_edge(slice_sink, _dg.tail()); + + // Now visit each pair of memory ops, creating the edges + for (int j = _nlist.length() - 1; j >= 0 ; j--) { + Node* s1 = _nlist.at(j); + + // If no dependency yet, use slice + if (_dg.dep(s1)->in_cnt() == 0) { + _dg.make_edge(slice, s1); + } + SWPointer p1(s1->as_Mem(), this); + bool sink_dependent = true; + for (int k = j - 1; k >= 0; k--) { + Node* s2 = _nlist.at(k); + if (s1->is_Load() && s2->is_Load()) + continue; + SWPointer p2(s2->as_Mem(), this); + + int cmp = p1.cmp(p2); + if (SuperWordRTDepCheck && + p1.base() != p2.base() && p1.valid() && p2.valid()) { + // Create a runtime check to disambiguate + OrderedPair pp(p1.base(), p2.base()); + _disjoint_ptrs.append_if_missing(pp); + } else if (!SWPointer::not_equal(cmp)) { + // Possibly same address + _dg.make_edge(s1, s2); + sink_dependent = false; + } + } + if (sink_dependent) { + _dg.make_edge(s1, slice_sink); + } + } +#ifndef PRODUCT + if (TraceSuperWord) { + tty->print_cr("\nDependence graph for slice: %d", n->_idx); + for (int q = 0; q < _nlist.length(); q++) { + _dg.print(_nlist.at(q)); + } + tty->cr(); + } +#endif + _nlist.clear(); + } + +#ifndef PRODUCT + if (TraceSuperWord) { + tty->print_cr("\ndisjoint_ptrs: %s", _disjoint_ptrs.length() > 0 ? "" : "NONE"); + for (int r = 0; r < _disjoint_ptrs.length(); r++) { + _disjoint_ptrs.at(r).print(); + tty->cr(); + } + tty->cr(); + } +#endif +} + +//---------------------------mem_slice_preds--------------------------- +// Return a memory slice (node list) in predecessor order starting at "start" +void SuperWord::mem_slice_preds(Node* start, Node* stop, GrowableArray &preds) { + assert(preds.length() == 0, "start empty"); + Node* n = start; + Node* prev = NULL; + while (true) { + assert(in_bb(n), "must be in block"); + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* out = n->fast_out(i); + if (out->is_Load()) { + if (in_bb(out)) { + preds.push(out); + } + } else { + // FIXME + if (out->is_MergeMem() && !in_bb(out)) { + // Either unrolling is causing a memory edge not to disappear, + // or need to run igvn.optimize() again before SLP + } else if (out->is_Phi() && out->bottom_type() == Type::MEMORY && !in_bb(out)) { + // Ditto. Not sure what else to check further. + } else if (out->Opcode() == Op_StoreCM && out->in(4) == n) { + // StoreCM has an input edge used as a precedence edge. + // Maybe an issue when oop stores are vectorized. + } else { + assert(out == prev || prev == NULL, "no branches off of store slice"); + } + } + } + if (n == stop) break; + preds.push(n); + prev = n; + n = n->in(MemNode::Memory); + } +} + +//------------------------------stmts_can_pack--------------------------- +// Can s1 and s2 be in a pack with s1 immediately preceeding s2 and +// s1 aligned at "align" +bool SuperWord::stmts_can_pack(Node* s1, Node* s2, int align) { + if (isomorphic(s1, s2)) { + if (independent(s1, s2)) { + if (!exists_at(s1, 0) && !exists_at(s2, 1)) { + if (!s1->is_Mem() || are_adjacent_refs(s1, s2)) { + int s1_align = alignment(s1); + int s2_align = alignment(s2); + if (s1_align == top_align || s1_align == align) { + if (s2_align == top_align || s2_align == align + data_size(s1)) { + return true; + } + } + } + } + } + } + return false; +} + +//------------------------------exists_at--------------------------- +// Does s exist in a pack at position pos? +bool SuperWord::exists_at(Node* s, uint pos) { + for (int i = 0; i < _packset.length(); i++) { + Node_List* p = _packset.at(i); + if (p->at(pos) == s) { + return true; + } + } + return false; +} + +//------------------------------are_adjacent_refs--------------------------- +// Is s1 immediately before s2 in memory? +bool SuperWord::are_adjacent_refs(Node* s1, Node* s2) { + if (!s1->is_Mem() || !s2->is_Mem()) return false; + if (!in_bb(s1) || !in_bb(s2)) return false; + // FIXME - co_locate_pack fails on Stores in different mem-slices, so + // only pack memops that are in the same alias set until that's fixed. + if (_phase->C->get_alias_index(s1->as_Mem()->adr_type()) != + _phase->C->get_alias_index(s2->as_Mem()->adr_type())) + return false; + SWPointer p1(s1->as_Mem(), this); + SWPointer p2(s2->as_Mem(), this); + if (p1.base() != p2.base() || !p1.comparable(p2)) return false; + int diff = p2.offset_in_bytes() - p1.offset_in_bytes(); + return diff == data_size(s1); +} + +//------------------------------isomorphic--------------------------- +// Are s1 and s2 similar? +bool SuperWord::isomorphic(Node* s1, Node* s2) { + if (s1->Opcode() != s2->Opcode()) return false; + if (s1->req() != s2->req()) return false; + if (s1->in(0) != s2->in(0)) return false; + if (velt_type(s1) != velt_type(s2)) return false; + return true; +} + +//------------------------------independent--------------------------- +// Is there no data path from s1 to s2 or s2 to s1? +bool SuperWord::independent(Node* s1, Node* s2) { + // assert(s1->Opcode() == s2->Opcode(), "check isomorphic first"); + int d1 = depth(s1); + int d2 = depth(s2); + if (d1 == d2) return s1 != s2; + Node* deep = d1 > d2 ? s1 : s2; + Node* shallow = d1 > d2 ? s2 : s1; + + visited_clear(); + + return independent_path(shallow, deep); +} + +//------------------------------independent_path------------------------------ +// Helper for independent +bool SuperWord::independent_path(Node* shallow, Node* deep, uint dp) { + if (dp >= 1000) return false; // stop deep recursion + visited_set(deep); + int shal_depth = depth(shallow); + assert(shal_depth <= depth(deep), "must be"); + for (DepPreds preds(deep, _dg); !preds.done(); preds.next()) { + Node* pred = preds.current(); + if (in_bb(pred) && !visited_test(pred)) { + if (shallow == pred) { + return false; + } + if (shal_depth < depth(pred) && !independent_path(shallow, pred, dp+1)) { + return false; + } + } + } + return true; +} + +//------------------------------set_alignment--------------------------- +void SuperWord::set_alignment(Node* s1, Node* s2, int align) { + set_alignment(s1, align); + set_alignment(s2, align + data_size(s1)); +} + +//------------------------------data_size--------------------------- +int SuperWord::data_size(Node* s) { + const Type* t = velt_type(s); + BasicType bt = t->array_element_basic_type(); + int bsize = type2aelembytes[bt]; + assert(bsize != 0, "valid size"); + return bsize; +} + +//------------------------------extend_packlist--------------------------- +// Extend packset by following use->def and def->use links from pack members. +void SuperWord::extend_packlist() { + bool changed; + do { + changed = false; + for (int i = 0; i < _packset.length(); i++) { + Node_List* p = _packset.at(i); + changed |= follow_use_defs(p); + changed |= follow_def_uses(p); + } + } while (changed); + +#ifndef PRODUCT + if (TraceSuperWord) { + tty->print_cr("\nAfter extend_packlist"); + print_packset(); + } +#endif +} + +//------------------------------follow_use_defs--------------------------- +// Extend the packset by visiting operand definitions of nodes in pack p +bool SuperWord::follow_use_defs(Node_List* p) { + Node* s1 = p->at(0); + Node* s2 = p->at(1); + assert(p->size() == 2, "just checking"); + assert(s1->req() == s2->req(), "just checking"); + assert(alignment(s1) + data_size(s1) == alignment(s2), "just checking"); + + if (s1->is_Load()) return false; + + int align = alignment(s1); + bool changed = false; + int start = s1->is_Store() ? MemNode::ValueIn : 1; + int end = s1->is_Store() ? MemNode::ValueIn+1 : s1->req(); + for (int j = start; j < end; j++) { + Node* t1 = s1->in(j); + Node* t2 = s2->in(j); + if (!in_bb(t1) || !in_bb(t2)) + continue; + if (stmts_can_pack(t1, t2, align)) { + if (est_savings(t1, t2) >= 0) { + Node_List* pair = new Node_List(); + pair->push(t1); + pair->push(t2); + _packset.append(pair); + set_alignment(t1, t2, align); + changed = true; + } + } + } + return changed; +} + +//------------------------------follow_def_uses--------------------------- +// Extend the packset by visiting uses of nodes in pack p +bool SuperWord::follow_def_uses(Node_List* p) { + bool changed = false; + Node* s1 = p->at(0); + Node* s2 = p->at(1); + assert(p->size() == 2, "just checking"); + assert(s1->req() == s2->req(), "just checking"); + assert(alignment(s1) + data_size(s1) == alignment(s2), "just checking"); + + if (s1->is_Store()) return false; + + int align = alignment(s1); + int savings = -1; + Node* u1 = NULL; + Node* u2 = NULL; + for (DUIterator_Fast imax, i = s1->fast_outs(imax); i < imax; i++) { + Node* t1 = s1->fast_out(i); + if (!in_bb(t1)) continue; + for (DUIterator_Fast jmax, j = s2->fast_outs(jmax); j < jmax; j++) { + Node* t2 = s2->fast_out(j); + if (!in_bb(t2)) continue; + if (!opnd_positions_match(s1, t1, s2, t2)) + continue; + if (stmts_can_pack(t1, t2, align)) { + int my_savings = est_savings(t1, t2); + if (my_savings > savings) { + savings = my_savings; + u1 = t1; + u2 = t2; + } + } + } + } + if (savings >= 0) { + Node_List* pair = new Node_List(); + pair->push(u1); + pair->push(u2); + _packset.append(pair); + set_alignment(u1, u2, align); + changed = true; + } + return changed; +} + +//---------------------------opnd_positions_match------------------------- +// Is the use of d1 in u1 at the same operand position as d2 in u2? +bool SuperWord::opnd_positions_match(Node* d1, Node* u1, Node* d2, Node* u2) { + uint ct = u1->req(); + if (ct != u2->req()) return false; + uint i1 = 0; + uint i2 = 0; + do { + for (i1++; i1 < ct; i1++) if (u1->in(i1) == d1) break; + for (i2++; i2 < ct; i2++) if (u2->in(i2) == d2) break; + if (i1 != i2) { + return false; + } + } while (i1 < ct); + return true; +} + +//------------------------------est_savings--------------------------- +// Estimate the savings from executing s1 and s2 as a pack +int SuperWord::est_savings(Node* s1, Node* s2) { + int save = 2 - 1; // 2 operations per instruction in packed form + + // inputs + for (uint i = 1; i < s1->req(); i++) { + Node* x1 = s1->in(i); + Node* x2 = s2->in(i); + if (x1 != x2) { + if (are_adjacent_refs(x1, x2)) { + save += adjacent_profit(x1, x2); + } else if (!in_packset(x1, x2)) { + save -= pack_cost(2); + } else { + save += unpack_cost(2); + } + } + } + + // uses of result + uint ct = 0; + for (DUIterator_Fast imax, i = s1->fast_outs(imax); i < imax; i++) { + Node* s1_use = s1->fast_out(i); + for (int j = 0; j < _packset.length(); j++) { + Node_List* p = _packset.at(j); + if (p->at(0) == s1_use) { + for (DUIterator_Fast kmax, k = s2->fast_outs(kmax); k < kmax; k++) { + Node* s2_use = s2->fast_out(k); + if (p->at(p->size()-1) == s2_use) { + ct++; + if (are_adjacent_refs(s1_use, s2_use)) { + save += adjacent_profit(s1_use, s2_use); + } + } + } + } + } + } + + if (ct < s1->outcnt()) save += unpack_cost(1); + if (ct < s2->outcnt()) save += unpack_cost(1); + + return save; +} + +//------------------------------costs--------------------------- +int SuperWord::adjacent_profit(Node* s1, Node* s2) { return 2; } +int SuperWord::pack_cost(int ct) { return ct; } +int SuperWord::unpack_cost(int ct) { return ct; } + +//------------------------------combine_packs--------------------------- +// Combine packs A and B with A.last == B.first into A.first..,A.last,B.second,..B.last +void SuperWord::combine_packs() { + bool changed; + do { + changed = false; + for (int i = 0; i < _packset.length(); i++) { + Node_List* p1 = _packset.at(i); + if (p1 == NULL) continue; + for (int j = 0; j < _packset.length(); j++) { + Node_List* p2 = _packset.at(j); + if (p2 == NULL) continue; + if (p1->at(p1->size()-1) == p2->at(0)) { + for (uint k = 1; k < p2->size(); k++) { + p1->push(p2->at(k)); + } + _packset.at_put(j, NULL); + changed = true; + } + } + } + } while (changed); + + for (int i = _packset.length() - 1; i >= 0; i--) { + Node_List* p1 = _packset.at(i); + if (p1 == NULL) { + _packset.remove_at(i); + } + } + +#ifndef PRODUCT + if (TraceSuperWord) { + tty->print_cr("\nAfter combine_packs"); + print_packset(); + } +#endif +} + +//-----------------------------construct_my_pack_map-------------------------- +// Construct the map from nodes to packs. Only valid after the +// point where a node is only in one pack (after combine_packs). +void SuperWord::construct_my_pack_map() { + Node_List* rslt = NULL; + for (int i = 0; i < _packset.length(); i++) { + Node_List* p = _packset.at(i); + for (uint j = 0; j < p->size(); j++) { + Node* s = p->at(j); + assert(my_pack(s) == NULL, "only in one pack"); + set_my_pack(s, p); + } + } +} + +//------------------------------filter_packs--------------------------- +// Remove packs that are not implemented or not profitable. +void SuperWord::filter_packs() { + + // Remove packs that are not implemented + for (int i = _packset.length() - 1; i >= 0; i--) { + Node_List* pk = _packset.at(i); + bool impl = implemented(pk); + if (!impl) { +#ifndef PRODUCT + if (TraceSuperWord && Verbose) { + tty->print_cr("Unimplemented"); + pk->at(0)->dump(); + } +#endif + remove_pack_at(i); + } + } + + // Remove packs that are not profitable + bool changed; + do { + changed = false; + for (int i = _packset.length() - 1; i >= 0; i--) { + Node_List* pk = _packset.at(i); + bool prof = profitable(pk); + if (!prof) { +#ifndef PRODUCT + if (TraceSuperWord && Verbose) { + tty->print_cr("Unprofitable"); + pk->at(0)->dump(); + } +#endif + remove_pack_at(i); + changed = true; + } + } + } while (changed); + +#ifndef PRODUCT + if (TraceSuperWord) { + tty->print_cr("\nAfter filter_packs"); + print_packset(); + tty->cr(); + } +#endif +} + +//------------------------------implemented--------------------------- +// Can code be generated for pack p? +bool SuperWord::implemented(Node_List* p) { + Node* p0 = p->at(0); + int vopc = VectorNode::opcode(p0->Opcode(), p->size(), velt_type(p0)); + return vopc > 0 && Matcher::has_match_rule(vopc); +} + +//------------------------------profitable--------------------------- +// For pack p, are all operands and all uses (with in the block) vector? +bool SuperWord::profitable(Node_List* p) { + Node* p0 = p->at(0); + uint start, end; + vector_opd_range(p0, &start, &end); + + // Return false if some input is not vector and inside block + for (uint i = start; i < end; i++) { + if (!is_vector_use(p0, i)) { + // For now, return false if not scalar promotion case (inputs are the same.) + // Later, implement PackNode and allow differring, non-vector inputs + // (maybe just the ones from outside the block.) + Node* p0_def = p0->in(i); + for (uint j = 1; j < p->size(); j++) { + Node* use = p->at(j); + Node* def = use->in(i); + if (p0_def != def) + return false; + } + } + } + if (!p0->is_Store()) { + // For now, return false if not all uses are vector. + // Later, implement ExtractNode and allow non-vector uses (maybe + // just the ones outside the block.) + for (uint i = 0; i < p->size(); i++) { + Node* def = p->at(i); + for (DUIterator_Fast jmax, j = def->fast_outs(jmax); j < jmax; j++) { + Node* use = def->fast_out(j); + for (uint k = 0; k < use->req(); k++) { + Node* n = use->in(k); + if (def == n) { + if (!is_vector_use(use, k)) { + return false; + } + } + } + } + } + } + return true; +} + +//------------------------------schedule--------------------------- +// Adjust the memory graph for the packed operations +void SuperWord::schedule() { + + // Co-locate in the memory graph the members of each memory pack + for (int i = 0; i < _packset.length(); i++) { + co_locate_pack(_packset.at(i)); + } +} + +//------------------------------co_locate_pack--------------------------- +// Within a pack, move stores down to the last executed store, +// and move loads up to the first executed load. +void SuperWord::co_locate_pack(Node_List* pk) { + if (pk->at(0)->is_Store()) { + // Push Stores down towards last executed pack member + MemNode* first = executed_first(pk)->as_Mem(); + MemNode* last = executed_last(pk)->as_Mem(); + MemNode* insert_pt = last; + MemNode* current = last->in(MemNode::Memory)->as_Mem(); + while (true) { + assert(in_bb(current), "stay in block"); + Node* my_mem = current->in(MemNode::Memory); + if (in_pack(current, pk)) { + // Forward users of my memory state to my input memory state + _igvn.hash_delete(current); + _igvn.hash_delete(my_mem); + for (DUIterator i = current->outs(); current->has_out(i); i++) { + Node* use = current->out(i); + if (use->is_Mem()) { + assert(use->in(MemNode::Memory) == current, "must be"); + _igvn.hash_delete(use); + use->set_req(MemNode::Memory, my_mem); + _igvn._worklist.push(use); + --i; // deleted this edge; rescan position + } + } + // put current immediately before insert_pt + current->set_req(MemNode::Memory, insert_pt->in(MemNode::Memory)); + _igvn.hash_delete(insert_pt); + insert_pt->set_req(MemNode::Memory, current); + _igvn._worklist.push(insert_pt); + _igvn._worklist.push(current); + insert_pt = current; + } + if (current == first) break; + current = my_mem->as_Mem(); + } + } else if (pk->at(0)->is_Load()) { + // Pull Loads up towards first executed pack member + LoadNode* first = executed_first(pk)->as_Load(); + Node* first_mem = first->in(MemNode::Memory); + _igvn.hash_delete(first_mem); + // Give each load same memory state as first + for (uint i = 0; i < pk->size(); i++) { + LoadNode* ld = pk->at(i)->as_Load(); + _igvn.hash_delete(ld); + ld->set_req(MemNode::Memory, first_mem); + _igvn._worklist.push(ld); + } + } +} + +//------------------------------output--------------------------- +// Convert packs into vector node operations +void SuperWord::output() { + if (_packset.length() == 0) return; + + // MUST ENSURE main loop's initial value is properly aligned: + // (iv_initial_value + min_iv_offset) % vector_width_in_bytes() == 0 + + align_initial_loop_index(align_to_ref()); + + // Insert extract (unpack) operations for scalar uses + for (int i = 0; i < _packset.length(); i++) { + insert_extracts(_packset.at(i)); + } + + for (int i = 0; i < _block.length(); i++) { + Node* n = _block.at(i); + Node_List* p = my_pack(n); + if (p && n == executed_last(p)) { + uint vlen = p->size(); + Node* vn = NULL; + Node* low_adr = p->at(0); + Node* first = executed_first(p); + if (n->is_Load()) { + int opc = n->Opcode(); + Node* ctl = n->in(MemNode::Control); + Node* mem = first->in(MemNode::Memory); + Node* adr = low_adr->in(MemNode::Address); + const TypePtr* atyp = n->adr_type(); + vn = VectorLoadNode::make(_phase->C, opc, ctl, mem, adr, atyp, vlen); + + } else if (n->is_Store()) { + // Promote value to be stored to vector + VectorNode* val = vector_opd(p, MemNode::ValueIn); + + int opc = n->Opcode(); + Node* ctl = n->in(MemNode::Control); + Node* mem = first->in(MemNode::Memory); + Node* adr = low_adr->in(MemNode::Address); + const TypePtr* atyp = n->adr_type(); + vn = VectorStoreNode::make(_phase->C, opc, ctl, mem, adr, atyp, val, vlen); + + } else if (n->req() == 3) { + // Promote operands to vector + Node* in1 = vector_opd(p, 1); + Node* in2 = vector_opd(p, 2); + vn = VectorNode::make(_phase->C, n->Opcode(), in1, in2, vlen, velt_type(n)); + + } else { + ShouldNotReachHere(); + } + + _phase->_igvn.register_new_node_with_optimizer(vn); + _phase->set_ctrl(vn, _phase->get_ctrl(p->at(0))); + for (uint j = 0; j < p->size(); j++) { + Node* pm = p->at(j); + _igvn.hash_delete(pm); + _igvn.subsume_node(pm, vn); + } + _igvn._worklist.push(vn); + } + } +} + +//------------------------------vector_opd--------------------------- +// Create a vector operand for the nodes in pack p for operand: in(opd_idx) +VectorNode* SuperWord::vector_opd(Node_List* p, int opd_idx) { + Node* p0 = p->at(0); + uint vlen = p->size(); + Node* opd = p0->in(opd_idx); + + bool same_opd = true; + for (uint i = 1; i < vlen; i++) { + Node* pi = p->at(i); + Node* in = pi->in(opd_idx); + if (opd != in) { + same_opd = false; + break; + } + } + + if (same_opd) { + if (opd->is_Vector()) { + return (VectorNode*)opd; // input is matching vector + } + // Convert scalar input to vector. Use p0's type because it's container + // maybe smaller than the operand's container. + const Type* opd_t = velt_type(!in_bb(opd) ? p0 : opd); + const Type* p0_t = velt_type(p0); + if (p0_t->higher_equal(opd_t)) opd_t = p0_t; + VectorNode* vn = VectorNode::scalar2vector(_phase->C, opd, vlen, opd_t); + + _phase->_igvn.register_new_node_with_optimizer(vn); + _phase->set_ctrl(vn, _phase->get_ctrl(opd)); + return vn; + } + + // Insert pack operation + const Type* opd_t = velt_type(!in_bb(opd) ? p0 : opd); + PackNode* pk = PackNode::make(_phase->C, opd, opd_t); + + for (uint i = 1; i < vlen; i++) { + Node* pi = p->at(i); + Node* in = pi->in(opd_idx); + assert(my_pack(in) == NULL, "Should already have been unpacked"); + assert(opd_t == velt_type(!in_bb(in) ? pi : in), "all same type"); + pk->add_opd(in); + } + _phase->_igvn.register_new_node_with_optimizer(pk); + _phase->set_ctrl(pk, _phase->get_ctrl(opd)); + return pk; +} + +//------------------------------insert_extracts--------------------------- +// If a use of pack p is not a vector use, then replace the +// use with an extract operation. +void SuperWord::insert_extracts(Node_List* p) { + if (p->at(0)->is_Store()) return; + assert(_n_idx_list.is_empty(), "empty (node,index) list"); + + // Inspect each use of each pack member. For each use that is + // not a vector use, replace the use with an extract operation. + + for (uint i = 0; i < p->size(); i++) { + Node* def = p->at(i); + for (DUIterator_Fast jmax, j = def->fast_outs(jmax); j < jmax; j++) { + Node* use = def->fast_out(j); + for (uint k = 0; k < use->req(); k++) { + Node* n = use->in(k); + if (def == n) { + if (!is_vector_use(use, k)) { + _n_idx_list.push(use, k); + } + } + } + } + } + + while (_n_idx_list.is_nonempty()) { + Node* use = _n_idx_list.node(); + int idx = _n_idx_list.index(); + _n_idx_list.pop(); + Node* def = use->in(idx); + + // Insert extract operation + _igvn.hash_delete(def); + _igvn.hash_delete(use); + int def_pos = alignment(def) / data_size(def); + const Type* def_t = velt_type(def); + + Node* ex = ExtractNode::make(_phase->C, def, def_pos, def_t); + _phase->_igvn.register_new_node_with_optimizer(ex); + _phase->set_ctrl(ex, _phase->get_ctrl(def)); + use->set_req(idx, ex); + _igvn._worklist.push(def); + _igvn._worklist.push(use); + + bb_insert_after(ex, bb_idx(def)); + set_velt_type(ex, def_t); + } +} + +//------------------------------is_vector_use--------------------------- +// Is use->in(u_idx) a vector use? +bool SuperWord::is_vector_use(Node* use, int u_idx) { + Node_List* u_pk = my_pack(use); + if (u_pk == NULL) return false; + Node* def = use->in(u_idx); + Node_List* d_pk = my_pack(def); + if (d_pk == NULL) { + // check for scalar promotion + Node* n = u_pk->at(0)->in(u_idx); + for (uint i = 1; i < u_pk->size(); i++) { + if (u_pk->at(i)->in(u_idx) != n) return false; + } + return true; + } + if (u_pk->size() != d_pk->size()) + return false; + for (uint i = 0; i < u_pk->size(); i++) { + Node* ui = u_pk->at(i); + Node* di = d_pk->at(i); + if (ui->in(u_idx) != di || alignment(ui) != alignment(di)) + return false; + } + return true; +} + +//------------------------------construct_bb--------------------------- +// Construct reverse postorder list of block members +void SuperWord::construct_bb() { + Node* entry = bb(); + + assert(_stk.length() == 0, "stk is empty"); + assert(_block.length() == 0, "block is empty"); + assert(_data_entry.length() == 0, "data_entry is empty"); + assert(_mem_slice_head.length() == 0, "mem_slice_head is empty"); + assert(_mem_slice_tail.length() == 0, "mem_slice_tail is empty"); + + // Find non-control nodes with no inputs from within block, + // create a temporary map from node _idx to bb_idx for use + // by the visited and post_visited sets, + // and count number of nodes in block. + int bb_ct = 0; + for (uint i = 0; i < lpt()->_body.size(); i++ ) { + Node *n = lpt()->_body.at(i); + set_bb_idx(n, i); // Create a temporary map + if (in_bb(n)) { + bb_ct++; + if (!n->is_CFG()) { + bool found = false; + for (uint j = 0; j < n->req(); j++) { + Node* def = n->in(j); + if (def && in_bb(def)) { + found = true; + break; + } + } + if (!found) { + assert(n != entry, "can't be entry"); + _data_entry.push(n); + } + } + } + } + + // Find memory slices (head and tail) + for (DUIterator_Fast imax, i = lp()->fast_outs(imax); i < imax; i++) { + Node *n = lp()->fast_out(i); + if (in_bb(n) && (n->is_Phi() && n->bottom_type() == Type::MEMORY)) { + Node* n_tail = n->in(LoopNode::LoopBackControl); + _mem_slice_head.push(n); + _mem_slice_tail.push(n_tail); + } + } + + // Create an RPO list of nodes in block + + visited_clear(); + post_visited_clear(); + + // Push all non-control nodes with no inputs from within block, then control entry + for (int j = 0; j < _data_entry.length(); j++) { + Node* n = _data_entry.at(j); + visited_set(n); + _stk.push(n); + } + visited_set(entry); + _stk.push(entry); + + // Do a depth first walk over out edges + int rpo_idx = bb_ct - 1; + int size; + while ((size = _stk.length()) > 0) { + Node* n = _stk.top(); // Leave node on stack + if (!visited_test_set(n)) { + // forward arc in graph + } else if (!post_visited_test(n)) { + // cross or back arc + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node *use = n->fast_out(i); + if (in_bb(use) && !visited_test(use) && + // Don't go around backedge + (!use->is_Phi() || n == entry)) { + _stk.push(use); + } + } + if (_stk.length() == size) { + // There were no additional uses, post visit node now + _stk.pop(); // Remove node from stack + assert(rpo_idx >= 0, ""); + _block.at_put_grow(rpo_idx, n); + rpo_idx--; + post_visited_set(n); + assert(rpo_idx >= 0 || _stk.is_empty(), ""); + } + } else { + _stk.pop(); // Remove post-visited node from stack + } + } + + // Create real map of block indices for nodes + for (int j = 0; j < _block.length(); j++) { + Node* n = _block.at(j); + set_bb_idx(n, j); + } + + initialize_bb(); // Ensure extra info is allocated. + +#ifndef PRODUCT + if (TraceSuperWord) { + print_bb(); + tty->print_cr("\ndata entry nodes: %s", _data_entry.length() > 0 ? "" : "NONE"); + for (int m = 0; m < _data_entry.length(); m++) { + tty->print("%3d ", m); + _data_entry.at(m)->dump(); + } + tty->print_cr("\nmemory slices: %s", _mem_slice_head.length() > 0 ? "" : "NONE"); + for (int m = 0; m < _mem_slice_head.length(); m++) { + tty->print("%3d ", m); _mem_slice_head.at(m)->dump(); + tty->print(" "); _mem_slice_tail.at(m)->dump(); + } + } +#endif + assert(rpo_idx == -1 && bb_ct == _block.length(), "all block members found"); +} + +//------------------------------initialize_bb--------------------------- +// Initialize per node info +void SuperWord::initialize_bb() { + Node* last = _block.at(_block.length() - 1); + grow_node_info(bb_idx(last)); +} + +//------------------------------bb_insert_after--------------------------- +// Insert n into block after pos +void SuperWord::bb_insert_after(Node* n, int pos) { + int n_pos = pos + 1; + // Make room + for (int i = _block.length() - 1; i >= n_pos; i--) { + _block.at_put_grow(i+1, _block.at(i)); + } + for (int j = _node_info.length() - 1; j >= n_pos; j--) { + _node_info.at_put_grow(j+1, _node_info.at(j)); + } + // Set value + _block.at_put_grow(n_pos, n); + _node_info.at_put_grow(n_pos, SWNodeInfo::initial); + // Adjust map from node->_idx to _block index + for (int i = n_pos; i < _block.length(); i++) { + set_bb_idx(_block.at(i), i); + } +} + +//------------------------------compute_max_depth--------------------------- +// Compute max depth for expressions from beginning of block +// Use to prune search paths during test for independence. +void SuperWord::compute_max_depth() { + int ct = 0; + bool again; + do { + again = false; + for (int i = 0; i < _block.length(); i++) { + Node* n = _block.at(i); + if (!n->is_Phi()) { + int d_orig = depth(n); + int d_in = 0; + for (DepPreds preds(n, _dg); !preds.done(); preds.next()) { + Node* pred = preds.current(); + if (in_bb(pred)) { + d_in = MAX2(d_in, depth(pred)); + } + } + if (d_in + 1 != d_orig) { + set_depth(n, d_in + 1); + again = true; + } + } + } + ct++; + } while (again); +#ifndef PRODUCT + if (TraceSuperWord && Verbose) + tty->print_cr("compute_max_depth iterated: %d times", ct); +#endif +} + +//-------------------------compute_vector_element_type----------------------- +// Compute necessary vector element type for expressions +// This propagates backwards a narrower integer type when the +// upper bits of the value are not needed. +// Example: char a,b,c; a = b + c; +// Normally the type of the add is integer, but for packed character +// operations the type of the add needs to be char. +void SuperWord::compute_vector_element_type() { +#ifndef PRODUCT + if (TraceSuperWord && Verbose) + tty->print_cr("\ncompute_velt_type:"); +#endif + + // Initial type + for (int i = 0; i < _block.length(); i++) { + Node* n = _block.at(i); + const Type* t = n->is_Mem() ? Type::get_const_basic_type(n->as_Mem()->memory_type()) + : _igvn.type(n); + const Type* vt = container_type(t); + set_velt_type(n, vt); + } + + // Propagate narrowed type backwards through operations + // that don't depend on higher order bits + for (int i = _block.length() - 1; i >= 0; i--) { + Node* n = _block.at(i); + // Only integer types need be examined + if (n->bottom_type()->isa_int()) { + uint start, end; + vector_opd_range(n, &start, &end); + const Type* vt = velt_type(n); + + for (uint j = start; j < end; j++) { + Node* in = n->in(j); + // Don't propagate through a type conversion + if (n->bottom_type() != in->bottom_type()) + continue; + switch(in->Opcode()) { + case Op_AddI: case Op_AddL: + case Op_SubI: case Op_SubL: + case Op_MulI: case Op_MulL: + case Op_AndI: case Op_AndL: + case Op_OrI: case Op_OrL: + case Op_XorI: case Op_XorL: + case Op_LShiftI: case Op_LShiftL: + case Op_CMoveI: case Op_CMoveL: + if (in_bb(in)) { + bool same_type = true; + for (DUIterator_Fast kmax, k = in->fast_outs(kmax); k < kmax; k++) { + Node *use = in->fast_out(k); + if (!in_bb(use) || velt_type(use) != vt) { + same_type = false; + break; + } + } + if (same_type) { + set_velt_type(in, vt); + } + } + } + } + } + } +#ifndef PRODUCT + if (TraceSuperWord && Verbose) { + for (int i = 0; i < _block.length(); i++) { + Node* n = _block.at(i); + velt_type(n)->dump(); + tty->print("\t"); + n->dump(); + } + } +#endif +} + +//------------------------------memory_alignment--------------------------- +// Alignment within a vector memory reference +int SuperWord::memory_alignment(MemNode* s, int iv_adjust_in_bytes) { + SWPointer p(s, this); + if (!p.valid()) { + return bottom_align; + } + int offset = p.offset_in_bytes(); + offset += iv_adjust_in_bytes; + int off_rem = offset % vector_width_in_bytes(); + int off_mod = off_rem >= 0 ? off_rem : off_rem + vector_width_in_bytes(); + return off_mod; +} + +//---------------------------container_type--------------------------- +// Smallest type containing range of values +const Type* SuperWord::container_type(const Type* t) { + if (t->isa_aryptr()) { + t = t->is_aryptr()->elem(); + } + if (t->basic_type() == T_INT) { + if (t->higher_equal(TypeInt::BOOL)) return TypeInt::BOOL; + if (t->higher_equal(TypeInt::BYTE)) return TypeInt::BYTE; + if (t->higher_equal(TypeInt::CHAR)) return TypeInt::CHAR; + if (t->higher_equal(TypeInt::SHORT)) return TypeInt::SHORT; + return TypeInt::INT; + } + return t; +} + +//-------------------------vector_opd_range----------------------- +// (Start, end] half-open range defining which operands are vector +void SuperWord::vector_opd_range(Node* n, uint* start, uint* end) { + switch (n->Opcode()) { + case Op_LoadB: case Op_LoadC: + case Op_LoadI: case Op_LoadL: + case Op_LoadF: case Op_LoadD: + case Op_LoadP: + *start = 0; + *end = 0; + return; + case Op_StoreB: case Op_StoreC: + case Op_StoreI: case Op_StoreL: + case Op_StoreF: case Op_StoreD: + case Op_StoreP: + *start = MemNode::ValueIn; + *end = *start + 1; + return; + case Op_LShiftI: case Op_LShiftL: + *start = 1; + *end = 2; + return; + case Op_CMoveI: case Op_CMoveL: case Op_CMoveF: case Op_CMoveD: + *start = 2; + *end = n->req(); + return; + } + *start = 1; + *end = n->req(); // default is all operands +} + +//------------------------------in_packset--------------------------- +// Are s1 and s2 in a pack pair and ordered as s1,s2? +bool SuperWord::in_packset(Node* s1, Node* s2) { + for (int i = 0; i < _packset.length(); i++) { + Node_List* p = _packset.at(i); + assert(p->size() == 2, "must be"); + if (p->at(0) == s1 && p->at(p->size()-1) == s2) { + return true; + } + } + return false; +} + +//------------------------------in_pack--------------------------- +// Is s in pack p? +Node_List* SuperWord::in_pack(Node* s, Node_List* p) { + for (uint i = 0; i < p->size(); i++) { + if (p->at(i) == s) { + return p; + } + } + return NULL; +} + +//------------------------------remove_pack_at--------------------------- +// Remove the pack at position pos in the packset +void SuperWord::remove_pack_at(int pos) { + Node_List* p = _packset.at(pos); + for (uint i = 0; i < p->size(); i++) { + Node* s = p->at(i); + set_my_pack(s, NULL); + } + _packset.remove_at(pos); +} + +//------------------------------executed_first--------------------------- +// Return the node executed first in pack p. Uses the RPO block list +// to determine order. +Node* SuperWord::executed_first(Node_List* p) { + Node* n = p->at(0); + int n_rpo = bb_idx(n); + for (uint i = 1; i < p->size(); i++) { + Node* s = p->at(i); + int s_rpo = bb_idx(s); + if (s_rpo < n_rpo) { + n = s; + n_rpo = s_rpo; + } + } + return n; +} + +//------------------------------executed_last--------------------------- +// Return the node executed last in pack p. +Node* SuperWord::executed_last(Node_List* p) { + Node* n = p->at(0); + int n_rpo = bb_idx(n); + for (uint i = 1; i < p->size(); i++) { + Node* s = p->at(i); + int s_rpo = bb_idx(s); + if (s_rpo > n_rpo) { + n = s; + n_rpo = s_rpo; + } + } + return n; +} + +//----------------------------align_initial_loop_index--------------------------- +// Adjust pre-loop limit so that in main loop, a load/store reference +// to align_to_ref will be a position zero in the vector. +// (iv + k) mod vector_align == 0 +void SuperWord::align_initial_loop_index(MemNode* align_to_ref) { + CountedLoopNode *main_head = lp()->as_CountedLoop(); + assert(main_head->is_main_loop(), ""); + CountedLoopEndNode* pre_end = get_pre_loop_end(main_head); + assert(pre_end != NULL, ""); + Node *pre_opaq1 = pre_end->limit(); + assert(pre_opaq1->Opcode() == Op_Opaque1, ""); + Opaque1Node *pre_opaq = (Opaque1Node*)pre_opaq1; + Node *pre_limit = pre_opaq->in(1); + + // Where we put new limit calculations + Node *pre_ctrl = pre_end->loopnode()->in(LoopNode::EntryControl); + + // Ensure the original loop limit is available from the + // pre-loop Opaque1 node. + Node *orig_limit = pre_opaq->original_loop_limit(); + assert(orig_limit != NULL && _igvn.type(orig_limit) != Type::TOP, ""); + + SWPointer align_to_ref_p(align_to_ref, this); + + // Let l0 == original pre_limit, l == new pre_limit, V == v_align + // + // For stride > 0 + // Need l such that l > l0 && (l+k)%V == 0 + // Find n such that l = (l0 + n) + // (l0 + n + k) % V == 0 + // n = [V - (l0 + k)%V]%V + // new limit = l0 + [V - (l0 + k)%V]%V + // For stride < 0 + // Need l such that l < l0 && (l+k)%V == 0 + // Find n such that l = (l0 - n) + // (l0 - n + k) % V == 0 + // n = (l0 + k)%V + // new limit = l0 - (l0 + k)%V + + int elt_size = align_to_ref_p.memory_size(); + int v_align = vector_width_in_bytes() / elt_size; + int k = align_to_ref_p.offset_in_bytes() / elt_size; + + Node *kn = _igvn.intcon(k); + Node *limk = new (_phase->C, 3) AddINode(pre_limit, kn); + _phase->_igvn.register_new_node_with_optimizer(limk); + _phase->set_ctrl(limk, pre_ctrl); + if (align_to_ref_p.invar() != NULL) { + Node* log2_elt = _igvn.intcon(exact_log2(elt_size)); + Node* aref = new (_phase->C, 3) URShiftINode(align_to_ref_p.invar(), log2_elt); + _phase->_igvn.register_new_node_with_optimizer(aref); + _phase->set_ctrl(aref, pre_ctrl); + if (!align_to_ref_p.negate_invar()) { + limk = new (_phase->C, 3) AddINode(limk, aref); + } else { + limk = new (_phase->C, 3) SubINode(limk, aref); + } + _phase->_igvn.register_new_node_with_optimizer(limk); + _phase->set_ctrl(limk, pre_ctrl); + } + Node* va_msk = _igvn.intcon(v_align - 1); + Node* n = new (_phase->C, 3) AndINode(limk, va_msk); + _phase->_igvn.register_new_node_with_optimizer(n); + _phase->set_ctrl(n, pre_ctrl); + Node* newlim; + if (iv_stride() > 0) { + Node* va = _igvn.intcon(v_align); + Node* adj = new (_phase->C, 3) SubINode(va, n); + _phase->_igvn.register_new_node_with_optimizer(adj); + _phase->set_ctrl(adj, pre_ctrl); + Node* adj2 = new (_phase->C, 3) AndINode(adj, va_msk); + _phase->_igvn.register_new_node_with_optimizer(adj2); + _phase->set_ctrl(adj2, pre_ctrl); + newlim = new (_phase->C, 3) AddINode(pre_limit, adj2); + } else { + newlim = new (_phase->C, 3) SubINode(pre_limit, n); + } + _phase->_igvn.register_new_node_with_optimizer(newlim); + _phase->set_ctrl(newlim, pre_ctrl); + Node* constrained = + (iv_stride() > 0) ? (Node*) new (_phase->C,3) MinINode(newlim, orig_limit) + : (Node*) new (_phase->C,3) MaxINode(newlim, orig_limit); + _phase->_igvn.register_new_node_with_optimizer(constrained); + _phase->set_ctrl(constrained, pre_ctrl); + _igvn.hash_delete(pre_opaq); + pre_opaq->set_req(1, constrained); +} + +//----------------------------get_pre_loop_end--------------------------- +// Find pre loop end from main loop. Returns null if none. +CountedLoopEndNode* SuperWord::get_pre_loop_end(CountedLoopNode *cl) { + Node *ctrl = cl->in(LoopNode::EntryControl); + if (!ctrl->is_IfTrue() && !ctrl->is_IfFalse()) return NULL; + Node *iffm = ctrl->in(0); + if (!iffm->is_If()) return NULL; + Node *p_f = iffm->in(0); + if (!p_f->is_IfFalse()) return NULL; + if (!p_f->in(0)->is_CountedLoopEnd()) return NULL; + CountedLoopEndNode *pre_end = p_f->in(0)->as_CountedLoopEnd(); + if (!pre_end->loopnode()->is_pre_loop()) return NULL; + return pre_end; +} + + +//------------------------------init--------------------------- +void SuperWord::init() { + _dg.init(); + _packset.clear(); + _disjoint_ptrs.clear(); + _block.clear(); + _data_entry.clear(); + _mem_slice_head.clear(); + _mem_slice_tail.clear(); + _node_info.clear(); + _align_to_ref = NULL; + _lpt = NULL; + _lp = NULL; + _bb = NULL; + _iv = NULL; +} + +//------------------------------print_packset--------------------------- +void SuperWord::print_packset() { +#ifndef PRODUCT + tty->print_cr("packset"); + for (int i = 0; i < _packset.length(); i++) { + tty->print_cr("Pack: %d", i); + Node_List* p = _packset.at(i); + print_pack(p); + } +#endif +} + +//------------------------------print_pack--------------------------- +void SuperWord::print_pack(Node_List* p) { + for (uint i = 0; i < p->size(); i++) { + print_stmt(p->at(i)); + } +} + +//------------------------------print_bb--------------------------- +void SuperWord::print_bb() { +#ifndef PRODUCT + tty->print_cr("\nBlock"); + for (int i = 0; i < _block.length(); i++) { + Node* n = _block.at(i); + tty->print("%d ", i); + if (n) { + n->dump(); + } + } +#endif +} + +//------------------------------print_stmt--------------------------- +void SuperWord::print_stmt(Node* s) { +#ifndef PRODUCT + tty->print(" align: %d \t", alignment(s)); + s->dump(); +#endif +} + +//------------------------------blank--------------------------- +char* SuperWord::blank(uint depth) { + static char blanks[101]; + assert(depth < 101, "too deep"); + for (uint i = 0; i < depth; i++) blanks[i] = ' '; + blanks[depth] = '\0'; + return blanks; +} + + +//==============================SWPointer=========================== + +//----------------------------SWPointer------------------------ +SWPointer::SWPointer(MemNode* mem, SuperWord* slp) : + _mem(mem), _slp(slp), _base(NULL), _adr(NULL), + _scale(0), _offset(0), _invar(NULL), _negate_invar(false) { + + Node* adr = mem->in(MemNode::Address); + if (!adr->is_AddP()) { + assert(!valid(), "too complex"); + return; + } + // Match AddP(base, AddP(ptr, k*iv [+ invariant]), constant) + Node* base = adr->in(AddPNode::Base); + for (int i = 0; i < 3; i++) { + if (!scaled_iv_plus_offset(adr->in(AddPNode::Offset))) { + assert(!valid(), "too complex"); + return; + } + adr = adr->in(AddPNode::Address); + if (base == adr || !adr->is_AddP()) { + break; // stop looking at addp's + } + } + _base = base; + _adr = adr; + assert(valid(), "Usable"); +} + +// Following is used to create a temporary object during +// the pattern match of an address expression. +SWPointer::SWPointer(SWPointer* p) : + _mem(p->_mem), _slp(p->_slp), _base(NULL), _adr(NULL), + _scale(0), _offset(0), _invar(NULL), _negate_invar(false) {} + +//------------------------scaled_iv_plus_offset-------------------- +// Match: k*iv + offset +// where: k is a constant that maybe zero, and +// offset is (k2 [+/- invariant]) where k2 maybe zero and invariant is optional +bool SWPointer::scaled_iv_plus_offset(Node* n) { + if (scaled_iv(n)) { + return true; + } + if (offset_plus_k(n)) { + return true; + } + int opc = n->Opcode(); + if (opc == Op_AddI) { + if (scaled_iv(n->in(1)) && offset_plus_k(n->in(2))) { + return true; + } + if (scaled_iv(n->in(2)) && offset_plus_k(n->in(1))) { + return true; + } + } else if (opc == Op_SubI) { + if (scaled_iv(n->in(1)) && offset_plus_k(n->in(2), true)) { + return true; + } + if (scaled_iv(n->in(2)) && offset_plus_k(n->in(1))) { + _scale *= -1; + return true; + } + } + return false; +} + +//----------------------------scaled_iv------------------------ +// Match: k*iv where k is a constant that's not zero +bool SWPointer::scaled_iv(Node* n) { + if (_scale != 0) { + return false; // already found a scale + } + if (n == iv()) { + _scale = 1; + return true; + } + int opc = n->Opcode(); + if (opc == Op_MulI) { + if (n->in(1) == iv() && n->in(2)->is_Con()) { + _scale = n->in(2)->get_int(); + return true; + } else if (n->in(2) == iv() && n->in(1)->is_Con()) { + _scale = n->in(1)->get_int(); + return true; + } + } else if (opc == Op_LShiftI) { + if (n->in(1) == iv() && n->in(2)->is_Con()) { + _scale = 1 << n->in(2)->get_int(); + return true; + } + } else if (opc == Op_ConvI2L) { + if (scaled_iv_plus_offset(n->in(1))) { + return true; + } + } else if (opc == Op_LShiftL) { + if (!has_iv() && _invar == NULL) { + // Need to preserve the current _offset value, so + // create a temporary object for this expression subtree. + // Hacky, so should re-engineer the address pattern match. + SWPointer tmp(this); + if (tmp.scaled_iv_plus_offset(n->in(1))) { + if (tmp._invar == NULL) { + int mult = 1 << n->in(2)->get_int(); + _scale = tmp._scale * mult; + _offset += tmp._offset * mult; + return true; + } + } + } + } + return false; +} + +//----------------------------offset_plus_k------------------------ +// Match: offset is (k [+/- invariant]) +// where k maybe zero and invariant is optional, but not both. +bool SWPointer::offset_plus_k(Node* n, bool negate) { + int opc = n->Opcode(); + if (opc == Op_ConI) { + _offset += negate ? -(n->get_int()) : n->get_int(); + return true; + } else if (opc == Op_ConL) { + // Okay if value fits into an int + const TypeLong* t = n->find_long_type(); + if (t->higher_equal(TypeLong::INT)) { + jlong loff = n->get_long(); + jint off = (jint)loff; + _offset += negate ? -off : loff; + return true; + } + return false; + } + if (_invar != NULL) return false; // already have an invariant + if (opc == Op_AddI) { + if (n->in(2)->is_Con() && invariant(n->in(1))) { + _negate_invar = negate; + _invar = n->in(1); + _offset += negate ? -(n->in(2)->get_int()) : n->in(2)->get_int(); + return true; + } else if (n->in(1)->is_Con() && invariant(n->in(2))) { + _offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int(); + _negate_invar = negate; + _invar = n->in(2); + return true; + } + } + if (opc == Op_SubI) { + if (n->in(2)->is_Con() && invariant(n->in(1))) { + _negate_invar = negate; + _invar = n->in(1); + _offset += !negate ? -(n->in(2)->get_int()) : n->in(2)->get_int(); + return true; + } else if (n->in(1)->is_Con() && invariant(n->in(2))) { + _offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int(); + _negate_invar = !negate; + _invar = n->in(2); + return true; + } + } + if (invariant(n)) { + _negate_invar = negate; + _invar = n; + return true; + } + return false; +} + +//----------------------------print------------------------ +void SWPointer::print() { +#ifndef PRODUCT + tty->print("base: %d adr: %d scale: %d offset: %d invar: %c%d\n", + _base != NULL ? _base->_idx : 0, + _adr != NULL ? _adr->_idx : 0, + _scale, _offset, + _negate_invar?'-':'+', + _invar != NULL ? _invar->_idx : 0); +#endif +} + +// ========================= OrderedPair ===================== + +const OrderedPair OrderedPair::initial; + +// ========================= SWNodeInfo ===================== + +const SWNodeInfo SWNodeInfo::initial; + + +// ============================ DepGraph =========================== + +//------------------------------make_node--------------------------- +// Make a new dependence graph node for an ideal node. +DepMem* DepGraph::make_node(Node* node) { + DepMem* m = new (_arena) DepMem(node); + if (node != NULL) { + assert(_map.at_grow(node->_idx) == NULL, "one init only"); + _map.at_put_grow(node->_idx, m); + } + return m; +} + +//------------------------------make_edge--------------------------- +// Make a new dependence graph edge from dpred -> dsucc +DepEdge* DepGraph::make_edge(DepMem* dpred, DepMem* dsucc) { + DepEdge* e = new (_arena) DepEdge(dpred, dsucc, dsucc->in_head(), dpred->out_head()); + dpred->set_out_head(e); + dsucc->set_in_head(e); + return e; +} + +// ========================== DepMem ======================== + +//------------------------------in_cnt--------------------------- +int DepMem::in_cnt() { + int ct = 0; + for (DepEdge* e = _in_head; e != NULL; e = e->next_in()) ct++; + return ct; +} + +//------------------------------out_cnt--------------------------- +int DepMem::out_cnt() { + int ct = 0; + for (DepEdge* e = _out_head; e != NULL; e = e->next_out()) ct++; + return ct; +} + +//------------------------------print----------------------------- +void DepMem::print() { +#ifndef PRODUCT + tty->print(" DepNode %d (", _node->_idx); + for (DepEdge* p = _in_head; p != NULL; p = p->next_in()) { + Node* pred = p->pred()->node(); + tty->print(" %d", pred != NULL ? pred->_idx : 0); + } + tty->print(") ["); + for (DepEdge* s = _out_head; s != NULL; s = s->next_out()) { + Node* succ = s->succ()->node(); + tty->print(" %d", succ != NULL ? succ->_idx : 0); + } + tty->print_cr(" ]"); +#endif +} + +// =========================== DepEdge ========================= + +//------------------------------DepPreds--------------------------- +void DepEdge::print() { +#ifndef PRODUCT + tty->print_cr("DepEdge: %d [ %d ]", _pred->node()->_idx, _succ->node()->_idx); +#endif +} + +// =========================== DepPreds ========================= +// Iterator over predecessor edges in the dependence graph. + +//------------------------------DepPreds--------------------------- +DepPreds::DepPreds(Node* n, DepGraph& dg) { + _n = n; + _done = false; + if (_n->is_Store() || _n->is_Load()) { + _next_idx = MemNode::Address; + _end_idx = n->req(); + _dep_next = dg.dep(_n)->in_head(); + } else if (_n->is_Mem()) { + _next_idx = 0; + _end_idx = 0; + _dep_next = dg.dep(_n)->in_head(); + } else { + _next_idx = 1; + _end_idx = _n->req(); + _dep_next = NULL; + } + next(); +} + +//------------------------------next--------------------------- +void DepPreds::next() { + if (_dep_next != NULL) { + _current = _dep_next->pred()->node(); + _dep_next = _dep_next->next_in(); + } else if (_next_idx < _end_idx) { + _current = _n->in(_next_idx++); + } else { + _done = true; + } +} + +// =========================== DepSuccs ========================= +// Iterator over successor edges in the dependence graph. + +//------------------------------DepSuccs--------------------------- +DepSuccs::DepSuccs(Node* n, DepGraph& dg) { + _n = n; + _done = false; + if (_n->is_Load()) { + _next_idx = 0; + _end_idx = _n->outcnt(); + _dep_next = dg.dep(_n)->out_head(); + } else if (_n->is_Mem() || _n->is_Phi() && _n->bottom_type() == Type::MEMORY) { + _next_idx = 0; + _end_idx = 0; + _dep_next = dg.dep(_n)->out_head(); + } else { + _next_idx = 0; + _end_idx = _n->outcnt(); + _dep_next = NULL; + } + next(); +} + +//-------------------------------next--------------------------- +void DepSuccs::next() { + if (_dep_next != NULL) { + _current = _dep_next->succ()->node(); + _dep_next = _dep_next->next_out(); + } else if (_next_idx < _end_idx) { + _current = _n->raw_out(_next_idx++); + } else { + _done = true; + } +} diff --git a/hotspot/src/share/vm/opto/superword.hpp b/hotspot/src/share/vm/opto/superword.hpp new file mode 100644 index 00000000000..b60cc83c1f0 --- /dev/null +++ b/hotspot/src/share/vm/opto/superword.hpp @@ -0,0 +1,506 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +// +// S U P E R W O R D T R A N S F O R M +// +// SuperWords are short, fixed length vectors. +// +// Algorithm from: +// +// Exploiting SuperWord Level Parallelism with +// Multimedia Instruction Sets +// by +// Samuel Larsen and Saman Amarasighe +// MIT Laboratory for Computer Science +// date +// May 2000 +// published in +// ACM SIGPLAN Notices +// Proceedings of ACM PLDI '00, Volume 35 Issue 5 +// +// Definition 3.1 A Pack is an n-tuple, , where +// s1,...,sn are independent isomorphic statements in a basic +// block. +// +// Definition 3.2 A PackSet is a set of Packs. +// +// Definition 3.3 A Pair is a Pack of size two, where the +// first statement is considered the left element, and the +// second statement is considered the right element. + +class SWPointer; +class OrderedPair; + +// ========================= Dependence Graph ===================== + +class DepMem; + +//------------------------------DepEdge--------------------------- +// An edge in the dependence graph. The edges incident to a dependence +// node are threaded through _next_in for incoming edges and _next_out +// for outgoing edges. +class DepEdge : public ResourceObj { + protected: + DepMem* _pred; + DepMem* _succ; + DepEdge* _next_in; // list of in edges, null terminated + DepEdge* _next_out; // list of out edges, null terminated + + public: + DepEdge(DepMem* pred, DepMem* succ, DepEdge* next_in, DepEdge* next_out) : + _pred(pred), _succ(succ), _next_in(next_in), _next_out(next_out) {} + + DepEdge* next_in() { return _next_in; } + DepEdge* next_out() { return _next_out; } + DepMem* pred() { return _pred; } + DepMem* succ() { return _succ; } + + void print(); +}; + +//------------------------------DepMem--------------------------- +// A node in the dependence graph. _in_head starts the threaded list of +// incoming edges, and _out_head starts the list of outgoing edges. +class DepMem : public ResourceObj { + protected: + Node* _node; // Corresponding ideal node + DepEdge* _in_head; // Head of list of in edges, null terminated + DepEdge* _out_head; // Head of list of out edges, null terminated + + public: + DepMem(Node* node) : _node(node), _in_head(NULL), _out_head(NULL) {} + + Node* node() { return _node; } + DepEdge* in_head() { return _in_head; } + DepEdge* out_head() { return _out_head; } + void set_in_head(DepEdge* hd) { _in_head = hd; } + void set_out_head(DepEdge* hd) { _out_head = hd; } + + int in_cnt(); // Incoming edge count + int out_cnt(); // Outgoing edge count + + void print(); +}; + +//------------------------------DepGraph--------------------------- +class DepGraph VALUE_OBJ_CLASS_SPEC { + protected: + Arena* _arena; + GrowableArray _map; + DepMem* _root; + DepMem* _tail; + + public: + DepGraph(Arena* a) : _arena(a), _map(a, 8, 0, NULL) { + _root = new (_arena) DepMem(NULL); + _tail = new (_arena) DepMem(NULL); + } + + DepMem* root() { return _root; } + DepMem* tail() { return _tail; } + + // Return dependence node corresponding to an ideal node + DepMem* dep(Node* node) { return _map.at(node->_idx); } + + // Make a new dependence graph node for an ideal node. + DepMem* make_node(Node* node); + + // Make a new dependence graph edge dprec->dsucc + DepEdge* make_edge(DepMem* dpred, DepMem* dsucc); + + DepEdge* make_edge(Node* pred, Node* succ) { return make_edge(dep(pred), dep(succ)); } + DepEdge* make_edge(DepMem* pred, Node* succ) { return make_edge(pred, dep(succ)); } + DepEdge* make_edge(Node* pred, DepMem* succ) { return make_edge(dep(pred), succ); } + + void init() { _map.clear(); } // initialize + + void print(Node* n) { dep(n)->print(); } + void print(DepMem* d) { d->print(); } +}; + +//------------------------------DepPreds--------------------------- +// Iterator over predecessors in the dependence graph and +// non-memory-graph inputs of ideal nodes. +class DepPreds : public StackObj { +private: + Node* _n; + int _next_idx, _end_idx; + DepEdge* _dep_next; + Node* _current; + bool _done; + +public: + DepPreds(Node* n, DepGraph& dg); + Node* current() { return _current; } + bool done() { return _done; } + void next(); +}; + +//------------------------------DepSuccs--------------------------- +// Iterator over successors in the dependence graph and +// non-memory-graph outputs of ideal nodes. +class DepSuccs : public StackObj { +private: + Node* _n; + int _next_idx, _end_idx; + DepEdge* _dep_next; + Node* _current; + bool _done; + +public: + DepSuccs(Node* n, DepGraph& dg); + Node* current() { return _current; } + bool done() { return _done; } + void next(); +}; + + +// ========================= SuperWord ===================== + +// -----------------------------SWNodeInfo--------------------------------- +// Per node info needed by SuperWord +class SWNodeInfo VALUE_OBJ_CLASS_SPEC { + public: + int _alignment; // memory alignment for a node + int _depth; // Max expression (DAG) depth from block start + const Type* _velt_type; // vector element type + Node_List* _my_pack; // pack containing this node + + SWNodeInfo() : _alignment(-1), _depth(0), _velt_type(NULL), _my_pack(NULL) {} + static const SWNodeInfo initial; +}; + +// -----------------------------SuperWord--------------------------------- +// Transforms scalar operations into packed (superword) operations. +class SuperWord : public ResourceObj { + private: + PhaseIdealLoop* _phase; + Arena* _arena; + PhaseIterGVN &_igvn; + + enum consts { top_align = -1, bottom_align = -666 }; + + GrowableArray _packset; // Packs for the current block + + GrowableArray _bb_idx; // Map from Node _idx to index within block + + GrowableArray _block; // Nodes in current block + GrowableArray _data_entry; // Nodes with all inputs from outside + GrowableArray _mem_slice_head; // Memory slice head nodes + GrowableArray _mem_slice_tail; // Memory slice tail nodes + + GrowableArray _node_info; // Info needed per node + + MemNode* _align_to_ref; // Memory reference that pre-loop will align to + + GrowableArray _disjoint_ptrs; // runtime disambiguated pointer pairs + + DepGraph _dg; // Dependence graph + + // Scratch pads + VectorSet _visited; // Visited set + VectorSet _post_visited; // Post-visited set + Node_Stack _n_idx_list; // List of (node,index) pairs + GrowableArray _nlist; // List of nodes + GrowableArray _stk; // Stack of nodes + + public: + SuperWord(PhaseIdealLoop* phase); + + void transform_loop(IdealLoopTree* lpt); + + // Accessors for SWPointer + PhaseIdealLoop* phase() { return _phase; } + IdealLoopTree* lpt() { return _lpt; } + PhiNode* iv() { return _iv; } + + private: + IdealLoopTree* _lpt; // Current loop tree node + LoopNode* _lp; // Current LoopNode + Node* _bb; // Current basic block + PhiNode* _iv; // Induction var + + // Accessors + Arena* arena() { return _arena; } + + Node* bb() { return _bb; } + void set_bb(Node* bb) { _bb = bb; } + + void set_lpt(IdealLoopTree* lpt) { _lpt = lpt; } + + LoopNode* lp() { return _lp; } + void set_lp(LoopNode* lp) { _lp = lp; + _iv = lp->as_CountedLoop()->phi()->as_Phi(); } + int iv_stride() { return lp()->as_CountedLoop()->stride_con(); } + + int vector_width_in_bytes() { return Matcher::vector_width_in_bytes(); } + + MemNode* align_to_ref() { return _align_to_ref; } + void set_align_to_ref(MemNode* m) { _align_to_ref = m; } + + Node* ctrl(Node* n) const { return _phase->has_ctrl(n) ? _phase->get_ctrl(n) : n; } + + // block accessors + bool in_bb(Node* n) { return n != NULL && n->outcnt() > 0 && ctrl(n) == _bb; } + int bb_idx(Node* n) { assert(in_bb(n), "must be"); return _bb_idx.at(n->_idx); } + void set_bb_idx(Node* n, int i) { _bb_idx.at_put_grow(n->_idx, i); } + + // visited set accessors + void visited_clear() { _visited.Clear(); } + void visited_set(Node* n) { return _visited.set(bb_idx(n)); } + int visited_test(Node* n) { return _visited.test(bb_idx(n)); } + int visited_test_set(Node* n) { return _visited.test_set(bb_idx(n)); } + void post_visited_clear() { _post_visited.Clear(); } + void post_visited_set(Node* n) { return _post_visited.set(bb_idx(n)); } + int post_visited_test(Node* n) { return _post_visited.test(bb_idx(n)); } + + // Ensure node_info contains element "i" + void grow_node_info(int i) { if (i >= _node_info.length()) _node_info.at_put_grow(i, SWNodeInfo::initial); } + + // memory alignment for a node + int alignment(Node* n) { return _node_info.adr_at(bb_idx(n))->_alignment; } + void set_alignment(Node* n, int a) { int i = bb_idx(n); grow_node_info(i); _node_info.adr_at(i)->_alignment = a; } + + // Max expression (DAG) depth from beginning of the block for each node + int depth(Node* n) { return _node_info.adr_at(bb_idx(n))->_depth; } + void set_depth(Node* n, int d) { int i = bb_idx(n); grow_node_info(i); _node_info.adr_at(i)->_depth = d; } + + // vector element type + const Type* velt_type(Node* n) { return _node_info.adr_at(bb_idx(n))->_velt_type; } + void set_velt_type(Node* n, const Type* t) { int i = bb_idx(n); grow_node_info(i); _node_info.adr_at(i)->_velt_type = t; } + + // my_pack + Node_List* my_pack(Node* n) { return !in_bb(n) ? NULL : _node_info.adr_at(bb_idx(n))->_my_pack; } + void set_my_pack(Node* n, Node_List* p) { int i = bb_idx(n); grow_node_info(i); _node_info.adr_at(i)->_my_pack = p; } + + // methods + + // Extract the superword level parallelism + void SLP_extract(); + // Find the adjacent memory references and create pack pairs for them. + void find_adjacent_refs(); + // Find a memory reference to align the loop induction variable to. + void find_align_to_ref(Node_List &memops); + // Can the preloop align the reference to position zero in the vector? + bool ref_is_alignable(SWPointer& p); + // Construct dependency graph. + void dependence_graph(); + // Return a memory slice (node list) in predecessor order starting at "start" + void mem_slice_preds(Node* start, Node* stop, GrowableArray &preds); + // Can s1 and s2 be in a pack with s1 immediately preceeding s2 and s1 aligned at "align" + bool stmts_can_pack(Node* s1, Node* s2, int align); + // Does s exist in a pack at position pos? + bool exists_at(Node* s, uint pos); + // Is s1 immediately before s2 in memory? + bool are_adjacent_refs(Node* s1, Node* s2); + // Are s1 and s2 similar? + bool isomorphic(Node* s1, Node* s2); + // Is there no data path from s1 to s2 or s2 to s1? + bool independent(Node* s1, Node* s2); + // Helper for independent + bool independent_path(Node* shallow, Node* deep, uint dp=0); + void set_alignment(Node* s1, Node* s2, int align); + int data_size(Node* s); + // Extend packset by following use->def and def->use links from pack members. + void extend_packlist(); + // Extend the packset by visiting operand definitions of nodes in pack p + bool follow_use_defs(Node_List* p); + // Extend the packset by visiting uses of nodes in pack p + bool follow_def_uses(Node_List* p); + // Estimate the savings from executing s1 and s2 as a pack + int est_savings(Node* s1, Node* s2); + int adjacent_profit(Node* s1, Node* s2); + int pack_cost(int ct); + int unpack_cost(int ct); + // Combine packs A and B with A.last == B.first into A.first..,A.last,B.second,..B.last + void combine_packs(); + // Construct the map from nodes to packs. + void construct_my_pack_map(); + // Remove packs that are not implemented or not profitable. + void filter_packs(); + // Adjust the memory graph for the packed operations + void schedule(); + // Within a pack, move stores down to the last executed store, + // and move loads up to the first executed load. + void co_locate_pack(Node_List* p); + // Convert packs into vector node operations + void output(); + // Create a vector operand for the nodes in pack p for operand: in(opd_idx) + VectorNode* vector_opd(Node_List* p, int opd_idx); + // Can code be generated for pack p? + bool implemented(Node_List* p); + // For pack p, are all operands and all uses (with in the block) vector? + bool profitable(Node_List* p); + // If a use of pack p is not a vector use, then replace the use with an extract operation. + void insert_extracts(Node_List* p); + // Is use->in(u_idx) a vector use? + bool is_vector_use(Node* use, int u_idx); + // Construct reverse postorder list of block members + void construct_bb(); + // Initialize per node info + void initialize_bb(); + // Insert n into block after pos + void bb_insert_after(Node* n, int pos); + // Compute max depth for expressions from beginning of block + void compute_max_depth(); + // Compute necessary vector element type for expressions + void compute_vector_element_type(); + // Are s1 and s2 in a pack pair and ordered as s1,s2? + bool in_packset(Node* s1, Node* s2); + // Is s in pack p? + Node_List* in_pack(Node* s, Node_List* p); + // Remove the pack at position pos in the packset + void remove_pack_at(int pos); + // Return the node executed first in pack p. + Node* executed_first(Node_List* p); + // Return the node executed last in pack p. + Node* executed_last(Node_List* p); + // Alignment within a vector memory reference + int memory_alignment(MemNode* s, int iv_adjust_in_bytes); + // (Start, end] half-open range defining which operands are vector + void vector_opd_range(Node* n, uint* start, uint* end); + // Smallest type containing range of values + static const Type* container_type(const Type* t); + // Adjust pre-loop limit so that in main loop, a load/store reference + // to align_to_ref will be a position zero in the vector. + void align_initial_loop_index(MemNode* align_to_ref); + // Find pre loop end from main loop. Returns null if none. + CountedLoopEndNode* get_pre_loop_end(CountedLoopNode *cl); + // Is the use of d1 in u1 at the same operand position as d2 in u2? + bool opnd_positions_match(Node* d1, Node* u1, Node* d2, Node* u2); + void init(); + + // print methods + void print_packset(); + void print_pack(Node_List* p); + void print_bb(); + void print_stmt(Node* s); + char* blank(uint depth); +}; + + +//------------------------------SWPointer--------------------------- +// Information about an address for dependence checking and vector alignment +class SWPointer VALUE_OBJ_CLASS_SPEC { + protected: + MemNode* _mem; // My memory reference node + SuperWord* _slp; // SuperWord class + + Node* _base; // NULL if unsafe nonheap reference + Node* _adr; // address pointer + jint _scale; // multipler for iv (in bytes), 0 if no loop iv + jint _offset; // constant offset (in bytes) + Node* _invar; // invariant offset (in bytes), NULL if none + bool _negate_invar; // if true then use: (0 - _invar) + + PhaseIdealLoop* phase() { return _slp->phase(); } + IdealLoopTree* lpt() { return _slp->lpt(); } + PhiNode* iv() { return _slp->iv(); } // Induction var + + bool invariant(Node* n) { + Node *n_c = phase()->get_ctrl(n); + return !lpt()->is_member(phase()->get_loop(n_c)); + } + + // Match: k*iv + offset + bool scaled_iv_plus_offset(Node* n); + // Match: k*iv where k is a constant that's not zero + bool scaled_iv(Node* n); + // Match: offset is (k [+/- invariant]) + bool offset_plus_k(Node* n, bool negate = false); + + public: + enum CMP { + Less = 1, + Greater = 2, + Equal = 4, + NotEqual = (Less | Greater), + NotComparable = (Less | Greater | Equal) + }; + + SWPointer(MemNode* mem, SuperWord* slp); + // Following is used to create a temporary object during + // the pattern match of an address expression. + SWPointer(SWPointer* p); + + bool valid() { return _adr != NULL; } + bool has_iv() { return _scale != 0; } + + Node* base() { return _base; } + Node* adr() { return _adr; } + int scale_in_bytes() { return _scale; } + Node* invar() { return _invar; } + bool negate_invar() { return _negate_invar; } + int offset_in_bytes() { return _offset; } + int memory_size() { return _mem->memory_size(); } + + // Comparable? + int cmp(SWPointer& q) { + if (valid() && q.valid() && + (_adr == q._adr || _base == _adr && q._base == q._adr) && + _scale == q._scale && + _invar == q._invar && + _negate_invar == q._negate_invar) { + bool overlap = q._offset < _offset + memory_size() && + _offset < q._offset + q.memory_size(); + return overlap ? Equal : (_offset < q._offset ? Less : Greater); + } else { + return NotComparable; + } + } + + bool not_equal(SWPointer& q) { return not_equal(cmp(q)); } + bool equal(SWPointer& q) { return equal(cmp(q)); } + bool comparable(SWPointer& q) { return comparable(cmp(q)); } + static bool not_equal(int cmp) { return cmp <= NotEqual; } + static bool equal(int cmp) { return cmp == Equal; } + static bool comparable(int cmp) { return cmp < NotComparable; } + + void print(); +}; + + +//------------------------------OrderedPair--------------------------- +// Ordered pair of Node*. +class OrderedPair VALUE_OBJ_CLASS_SPEC { + protected: + Node* _p1; + Node* _p2; + public: + OrderedPair() : _p1(NULL), _p2(NULL) {} + OrderedPair(Node* p1, Node* p2) { + if (p1->_idx < p2->_idx) { + _p1 = p1; _p2 = p2; + } else { + _p1 = p2; _p2 = p1; + } + } + + bool operator==(const OrderedPair &rhs) { + return _p1 == rhs._p1 && _p2 == rhs._p2; + } + void print() { tty->print(" (%d, %d)", _p1->_idx, _p2->_idx); } + + static const OrderedPair initial; +}; diff --git a/hotspot/src/share/vm/opto/type.cpp b/hotspot/src/share/vm/opto/type.cpp new file mode 100644 index 00000000000..e396c9732b0 --- /dev/null +++ b/hotspot/src/share/vm/opto/type.cpp @@ -0,0 +1,3751 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + +#include "incls/_precompiled.incl" +#include "incls/_type.cpp.incl" + +// Dictionary of types shared among compilations. +Dict* Type::_shared_type_dict = NULL; + +// Array which maps compiler types to Basic Types +const BasicType Type::_basic_type[Type::lastype] = { + T_ILLEGAL, // Bad + T_ILLEGAL, // Control + T_VOID, // Top + T_INT, // Int + T_LONG, // Long + T_VOID, // Half + + T_ILLEGAL, // Tuple + T_ARRAY, // Array + + T_ADDRESS, // AnyPtr // shows up in factory methods for NULL_PTR + T_ADDRESS, // RawPtr + T_OBJECT, // OopPtr + T_OBJECT, // InstPtr + T_OBJECT, // AryPtr + T_OBJECT, // KlassPtr + + T_OBJECT, // Function + T_ILLEGAL, // Abio + T_ADDRESS, // Return_Address + T_ILLEGAL, // Memory + T_FLOAT, // FloatTop + T_FLOAT, // FloatCon + T_FLOAT, // FloatBot + T_DOUBLE, // DoubleTop + T_DOUBLE, // DoubleCon + T_DOUBLE, // DoubleBot + T_ILLEGAL, // Bottom +}; + +// Map ideal registers (machine types) to ideal types +const Type *Type::mreg2type[_last_machine_leaf]; + +// Map basic types to canonical Type* pointers. +const Type* Type:: _const_basic_type[T_CONFLICT+1]; + +// Map basic types to constant-zero Types. +const Type* Type:: _zero_type[T_CONFLICT+1]; + +// Map basic types to array-body alias types. +const TypeAryPtr* TypeAryPtr::_array_body_type[T_CONFLICT+1]; + +//============================================================================= +// Convenience common pre-built types. +const Type *Type::ABIO; // State-of-machine only +const Type *Type::BOTTOM; // All values +const Type *Type::CONTROL; // Control only +const Type *Type::DOUBLE; // All doubles +const Type *Type::FLOAT; // All floats +const Type *Type::HALF; // Placeholder half of doublewide type +const Type *Type::MEMORY; // Abstract store only +const Type *Type::RETURN_ADDRESS; +const Type *Type::TOP; // No values in set + +//------------------------------get_const_type--------------------------- +const Type* Type::get_const_type(ciType* type) { + if (type == NULL) { + return NULL; + } else if (type->is_primitive_type()) { + return get_const_basic_type(type->basic_type()); + } else { + return TypeOopPtr::make_from_klass(type->as_klass()); + } +} + +//---------------------------array_element_basic_type--------------------------------- +// Mapping to the array element's basic type. +BasicType Type::array_element_basic_type() const { + BasicType bt = basic_type(); + if (bt == T_INT) { + if (this == TypeInt::INT) return T_INT; + if (this == TypeInt::CHAR) return T_CHAR; + if (this == TypeInt::BYTE) return T_BYTE; + if (this == TypeInt::BOOL) return T_BOOLEAN; + if (this == TypeInt::SHORT) return T_SHORT; + return T_VOID; + } + return bt; +} + +//---------------------------get_typeflow_type--------------------------------- +// Import a type produced by ciTypeFlow. +const Type* Type::get_typeflow_type(ciType* type) { + switch (type->basic_type()) { + + case ciTypeFlow::StateVector::T_BOTTOM: + assert(type == ciTypeFlow::StateVector::bottom_type(), ""); + return Type::BOTTOM; + + case ciTypeFlow::StateVector::T_TOP: + assert(type == ciTypeFlow::StateVector::top_type(), ""); + return Type::TOP; + + case ciTypeFlow::StateVector::T_NULL: + assert(type == ciTypeFlow::StateVector::null_type(), ""); + return TypePtr::NULL_PTR; + + case ciTypeFlow::StateVector::T_LONG2: + // The ciTypeFlow pass pushes a long, then the half. + // We do the same. + assert(type == ciTypeFlow::StateVector::long2_type(), ""); + return TypeInt::TOP; + + case ciTypeFlow::StateVector::T_DOUBLE2: + // The ciTypeFlow pass pushes double, then the half. + // Our convention is the same. + assert(type == ciTypeFlow::StateVector::double2_type(), ""); + return Type::TOP; + + case T_ADDRESS: + assert(type->is_return_address(), ""); + return TypeRawPtr::make((address)(intptr_t)type->as_return_address()->bci()); + + default: + // make sure we did not mix up the cases: + assert(type != ciTypeFlow::StateVector::bottom_type(), ""); + assert(type != ciTypeFlow::StateVector::top_type(), ""); + assert(type != ciTypeFlow::StateVector::null_type(), ""); + assert(type != ciTypeFlow::StateVector::long2_type(), ""); + assert(type != ciTypeFlow::StateVector::double2_type(), ""); + assert(!type->is_return_address(), ""); + + return Type::get_const_type(type); + } +} + + +//------------------------------make------------------------------------------- +// Create a simple Type, with default empty symbol sets. Then hashcons it +// and look for an existing copy in the type dictionary. +const Type *Type::make( enum TYPES t ) { + return (new Type(t))->hashcons(); +} + +//------------------------------cmp-------------------------------------------- +int Type::cmp( const Type *const t1, const Type *const t2 ) { + if( t1->_base != t2->_base ) + return 1; // Missed badly + assert(t1 != t2 || t1->eq(t2), "eq must be reflexive"); + return !t1->eq(t2); // Return ZERO if equal +} + +//------------------------------hash------------------------------------------- +int Type::uhash( const Type *const t ) { + return t->hash(); +} + +//--------------------------Initialize_shared---------------------------------- +void Type::Initialize_shared(Compile* current) { + // This method does not need to be locked because the first system + // compilations (stub compilations) occur serially. If they are + // changed to proceed in parallel, then this section will need + // locking. + + Arena* save = current->type_arena(); + Arena* shared_type_arena = new Arena(); + + current->set_type_arena(shared_type_arena); + _shared_type_dict = + new (shared_type_arena) Dict( (CmpKey)Type::cmp, (Hash)Type::uhash, + shared_type_arena, 128 ); + current->set_type_dict(_shared_type_dict); + + // Make shared pre-built types. + CONTROL = make(Control); // Control only + TOP = make(Top); // No values in set + MEMORY = make(Memory); // Abstract store only + ABIO = make(Abio); // State-of-machine only + RETURN_ADDRESS=make(Return_Address); + FLOAT = make(FloatBot); // All floats + DOUBLE = make(DoubleBot); // All doubles + BOTTOM = make(Bottom); // Everything + HALF = make(Half); // Placeholder half of doublewide type + + TypeF::ZERO = TypeF::make(0.0); // Float 0 (positive zero) + TypeF::ONE = TypeF::make(1.0); // Float 1 + + TypeD::ZERO = TypeD::make(0.0); // Double 0 (positive zero) + TypeD::ONE = TypeD::make(1.0); // Double 1 + + TypeInt::MINUS_1 = TypeInt::make(-1); // -1 + TypeInt::ZERO = TypeInt::make( 0); // 0 + TypeInt::ONE = TypeInt::make( 1); // 1 + TypeInt::BOOL = TypeInt::make(0,1, WidenMin); // 0 or 1, FALSE or TRUE. + TypeInt::CC = TypeInt::make(-1, 1, WidenMin); // -1, 0 or 1, condition codes + TypeInt::CC_LT = TypeInt::make(-1,-1, WidenMin); // == TypeInt::MINUS_1 + TypeInt::CC_GT = TypeInt::make( 1, 1, WidenMin); // == TypeInt::ONE + TypeInt::CC_EQ = TypeInt::make( 0, 0, WidenMin); // == TypeInt::ZERO + TypeInt::CC_LE = TypeInt::make(-1, 0, WidenMin); + TypeInt::CC_GE = TypeInt::make( 0, 1, WidenMin); // == TypeInt::BOOL + TypeInt::BYTE = TypeInt::make(-128,127, WidenMin); // Bytes + TypeInt::CHAR = TypeInt::make(0,65535, WidenMin); // Java chars + TypeInt::SHORT = TypeInt::make(-32768,32767, WidenMin); // Java shorts + TypeInt::POS = TypeInt::make(0,max_jint, WidenMin); // Non-neg values + TypeInt::POS1 = TypeInt::make(1,max_jint, WidenMin); // Positive values + TypeInt::INT = TypeInt::make(min_jint,max_jint, WidenMax); // 32-bit integers + TypeInt::SYMINT = TypeInt::make(-max_jint,max_jint,WidenMin); // symmetric range + // CmpL is overloaded both as the bytecode computation returning + // a trinary (-1,0,+1) integer result AND as an efficient long + // compare returning optimizer ideal-type flags. + assert( TypeInt::CC_LT == TypeInt::MINUS_1, "types must match for CmpL to work" ); + assert( TypeInt::CC_GT == TypeInt::ONE, "types must match for CmpL to work" ); + assert( TypeInt::CC_EQ == TypeInt::ZERO, "types must match for CmpL to work" ); + assert( TypeInt::CC_GE == TypeInt::BOOL, "types must match for CmpL to work" ); + + TypeLong::MINUS_1 = TypeLong::make(-1); // -1 + TypeLong::ZERO = TypeLong::make( 0); // 0 + TypeLong::ONE = TypeLong::make( 1); // 1 + TypeLong::POS = TypeLong::make(0,max_jlong, WidenMin); // Non-neg values + TypeLong::LONG = TypeLong::make(min_jlong,max_jlong,WidenMax); // 64-bit integers + TypeLong::INT = TypeLong::make((jlong)min_jint,(jlong)max_jint,WidenMin); + TypeLong::UINT = TypeLong::make(0,(jlong)max_juint,WidenMin); + + const Type **fboth =(const Type**)shared_type_arena->Amalloc_4(2*sizeof(Type*)); + fboth[0] = Type::CONTROL; + fboth[1] = Type::CONTROL; + TypeTuple::IFBOTH = TypeTuple::make( 2, fboth ); + + const Type **ffalse =(const Type**)shared_type_arena->Amalloc_4(2*sizeof(Type*)); + ffalse[0] = Type::CONTROL; + ffalse[1] = Type::TOP; + TypeTuple::IFFALSE = TypeTuple::make( 2, ffalse ); + + const Type **fneither =(const Type**)shared_type_arena->Amalloc_4(2*sizeof(Type*)); + fneither[0] = Type::TOP; + fneither[1] = Type::TOP; + TypeTuple::IFNEITHER = TypeTuple::make( 2, fneither ); + + const Type **ftrue =(const Type**)shared_type_arena->Amalloc_4(2*sizeof(Type*)); + ftrue[0] = Type::TOP; + ftrue[1] = Type::CONTROL; + TypeTuple::IFTRUE = TypeTuple::make( 2, ftrue ); + + const Type **floop =(const Type**)shared_type_arena->Amalloc_4(2*sizeof(Type*)); + floop[0] = Type::CONTROL; + floop[1] = TypeInt::INT; + TypeTuple::LOOPBODY = TypeTuple::make( 2, floop ); + + TypePtr::NULL_PTR= TypePtr::make( AnyPtr, TypePtr::Null, 0 ); + TypePtr::NOTNULL = TypePtr::make( AnyPtr, TypePtr::NotNull, OffsetBot ); + TypePtr::BOTTOM = TypePtr::make( AnyPtr, TypePtr::BotPTR, OffsetBot ); + + TypeRawPtr::BOTTOM = TypeRawPtr::make( TypePtr::BotPTR ); + TypeRawPtr::NOTNULL= TypeRawPtr::make( TypePtr::NotNull ); + + mreg2type[Op_Node] = Type::BOTTOM; + mreg2type[Op_Set ] = 0; + mreg2type[Op_RegI] = TypeInt::INT; + mreg2type[Op_RegP] = TypePtr::BOTTOM; + mreg2type[Op_RegF] = Type::FLOAT; + mreg2type[Op_RegD] = Type::DOUBLE; + mreg2type[Op_RegL] = TypeLong::LONG; + mreg2type[Op_RegFlags] = TypeInt::CC; + + const Type **fmembar = TypeTuple::fields(0); + TypeTuple::MEMBAR = TypeTuple::make(TypeFunc::Parms+0, fmembar); + + const Type **fsc = (const Type**)shared_type_arena->Amalloc_4(2*sizeof(Type*)); + fsc[0] = TypeInt::CC; + fsc[1] = Type::MEMORY; + TypeTuple::STORECONDITIONAL = TypeTuple::make(2, fsc); + + TypeInstPtr::NOTNULL = TypeInstPtr::make(TypePtr::NotNull, current->env()->Object_klass()); + TypeInstPtr::BOTTOM = TypeInstPtr::make(TypePtr::BotPTR, current->env()->Object_klass()); + TypeInstPtr::MIRROR = TypeInstPtr::make(TypePtr::NotNull, current->env()->Class_klass()); + TypeInstPtr::MARK = TypeInstPtr::make(TypePtr::BotPTR, current->env()->Object_klass(), + false, 0, oopDesc::mark_offset_in_bytes()); + TypeInstPtr::KLASS = TypeInstPtr::make(TypePtr::BotPTR, current->env()->Object_klass(), + false, 0, oopDesc::klass_offset_in_bytes()); + TypeOopPtr::BOTTOM = TypeOopPtr::make(TypePtr::BotPTR, OffsetBot); + + TypeAryPtr::RANGE = TypeAryPtr::make( TypePtr::BotPTR, TypeAry::make(Type::BOTTOM,TypeInt::POS), current->env()->Object_klass(), false, arrayOopDesc::length_offset_in_bytes()); + // There is no shared klass for Object[]. See note in TypeAryPtr::klass(). + TypeAryPtr::OOPS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInstPtr::BOTTOM,TypeInt::POS), NULL /*ciArrayKlass::make(o)*/, false, Type::OffsetBot); + TypeAryPtr::BYTES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::BYTE ,TypeInt::POS), ciTypeArrayKlass::make(T_BYTE), true, Type::OffsetBot); + TypeAryPtr::SHORTS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::SHORT ,TypeInt::POS), ciTypeArrayKlass::make(T_SHORT), true, Type::OffsetBot); + TypeAryPtr::CHARS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::CHAR ,TypeInt::POS), ciTypeArrayKlass::make(T_CHAR), true, Type::OffsetBot); + TypeAryPtr::INTS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::INT ,TypeInt::POS), ciTypeArrayKlass::make(T_INT), true, Type::OffsetBot); + TypeAryPtr::LONGS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeLong::LONG ,TypeInt::POS), ciTypeArrayKlass::make(T_LONG), true, Type::OffsetBot); + TypeAryPtr::FLOATS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::FLOAT ,TypeInt::POS), ciTypeArrayKlass::make(T_FLOAT), true, Type::OffsetBot); + TypeAryPtr::DOUBLES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::DOUBLE ,TypeInt::POS), ciTypeArrayKlass::make(T_DOUBLE), true, Type::OffsetBot); + + TypeAryPtr::_array_body_type[T_OBJECT] = TypeAryPtr::OOPS; + TypeAryPtr::_array_body_type[T_ARRAY] = TypeAryPtr::OOPS; // arrays are stored in oop arrays + TypeAryPtr::_array_body_type[T_BYTE] = TypeAryPtr::BYTES; + TypeAryPtr::_array_body_type[T_BOOLEAN] = TypeAryPtr::BYTES; // boolean[] is a byte array + TypeAryPtr::_array_body_type[T_SHORT] = TypeAryPtr::SHORTS; + TypeAryPtr::_array_body_type[T_CHAR] = TypeAryPtr::CHARS; + TypeAryPtr::_array_body_type[T_INT] = TypeAryPtr::INTS; + TypeAryPtr::_array_body_type[T_LONG] = TypeAryPtr::LONGS; + TypeAryPtr::_array_body_type[T_FLOAT] = TypeAryPtr::FLOATS; + TypeAryPtr::_array_body_type[T_DOUBLE] = TypeAryPtr::DOUBLES; + + TypeKlassPtr::OBJECT = TypeKlassPtr::make( TypePtr::NotNull, current->env()->Object_klass(), 0 ); + TypeKlassPtr::OBJECT_OR_NULL = TypeKlassPtr::make( TypePtr::BotPTR, current->env()->Object_klass(), 0 ); + + const Type **fi2c = TypeTuple::fields(2); + fi2c[TypeFunc::Parms+0] = TypeInstPtr::BOTTOM; // methodOop + fi2c[TypeFunc::Parms+1] = TypeRawPtr::BOTTOM; // argument pointer + TypeTuple::START_I2C = TypeTuple::make(TypeFunc::Parms+2, fi2c); + + const Type **intpair = TypeTuple::fields(2); + intpair[0] = TypeInt::INT; + intpair[1] = TypeInt::INT; + TypeTuple::INT_PAIR = TypeTuple::make(2, intpair); + + const Type **longpair = TypeTuple::fields(2); + longpair[0] = TypeLong::LONG; + longpair[1] = TypeLong::LONG; + TypeTuple::LONG_PAIR = TypeTuple::make(2, longpair); + + _const_basic_type[T_BOOLEAN] = TypeInt::BOOL; + _const_basic_type[T_CHAR] = TypeInt::CHAR; + _const_basic_type[T_BYTE] = TypeInt::BYTE; + _const_basic_type[T_SHORT] = TypeInt::SHORT; + _const_basic_type[T_INT] = TypeInt::INT; + _const_basic_type[T_LONG] = TypeLong::LONG; + _const_basic_type[T_FLOAT] = Type::FLOAT; + _const_basic_type[T_DOUBLE] = Type::DOUBLE; + _const_basic_type[T_OBJECT] = TypeInstPtr::BOTTOM; + _const_basic_type[T_ARRAY] = TypeInstPtr::BOTTOM; // there is no separate bottom for arrays + _const_basic_type[T_VOID] = TypePtr::NULL_PTR; // reflection represents void this way + _const_basic_type[T_ADDRESS] = TypeRawPtr::BOTTOM; // both interpreter return addresses & random raw ptrs + _const_basic_type[T_CONFLICT]= Type::BOTTOM; // why not? + + _zero_type[T_BOOLEAN] = TypeInt::ZERO; // false == 0 + _zero_type[T_CHAR] = TypeInt::ZERO; // '\0' == 0 + _zero_type[T_BYTE] = TypeInt::ZERO; // 0x00 == 0 + _zero_type[T_SHORT] = TypeInt::ZERO; // 0x0000 == 0 + _zero_type[T_INT] = TypeInt::ZERO; + _zero_type[T_LONG] = TypeLong::ZERO; + _zero_type[T_FLOAT] = TypeF::ZERO; + _zero_type[T_DOUBLE] = TypeD::ZERO; + _zero_type[T_OBJECT] = TypePtr::NULL_PTR; + _zero_type[T_ARRAY] = TypePtr::NULL_PTR; // null array is null oop + _zero_type[T_ADDRESS] = TypePtr::NULL_PTR; // raw pointers use the same null + _zero_type[T_VOID] = Type::TOP; // the only void value is no value at all + + // get_zero_type() should not happen for T_CONFLICT + _zero_type[T_CONFLICT]= NULL; + + // Restore working type arena. + current->set_type_arena(save); + current->set_type_dict(NULL); +} + +//------------------------------Initialize------------------------------------- +void Type::Initialize(Compile* current) { + assert(current->type_arena() != NULL, "must have created type arena"); + + if (_shared_type_dict == NULL) { + Initialize_shared(current); + } + + Arena* type_arena = current->type_arena(); + + // Create the hash-cons'ing dictionary with top-level storage allocation + Dict *tdic = new (type_arena) Dict( (CmpKey)Type::cmp,(Hash)Type::uhash, type_arena, 128 ); + current->set_type_dict(tdic); + + // Transfer the shared types. + DictI i(_shared_type_dict); + for( ; i.test(); ++i ) { + Type* t = (Type*)i._value; + tdic->Insert(t,t); // New Type, insert into Type table + } +} + +//------------------------------hashcons--------------------------------------- +// Do the hash-cons trick. If the Type already exists in the type table, +// delete the current Type and return the existing Type. Otherwise stick the +// current Type in the Type table. +const Type *Type::hashcons(void) { + debug_only(base()); // Check the assertion in Type::base(). + // Look up the Type in the Type dictionary + Dict *tdic = type_dict(); + Type* old = (Type*)(tdic->Insert(this, this, false)); + if( old ) { // Pre-existing Type? + if( old != this ) // Yes, this guy is not the pre-existing? + delete this; // Yes, Nuke this guy + assert( old->_dual, "" ); + return old; // Return pre-existing + } + + // Every type has a dual (to make my lattice symmetric). + // Since we just discovered a new Type, compute its dual right now. + assert( !_dual, "" ); // No dual yet + _dual = xdual(); // Compute the dual + if( cmp(this,_dual)==0 ) { // Handle self-symmetric + _dual = this; + return this; + } + assert( !_dual->_dual, "" ); // No reverse dual yet + assert( !(*tdic)[_dual], "" ); // Dual not in type system either + // New Type, insert into Type table + tdic->Insert((void*)_dual,(void*)_dual); + ((Type*)_dual)->_dual = this; // Finish up being symmetric +#ifdef ASSERT + Type *dual_dual = (Type*)_dual->xdual(); + assert( eq(dual_dual), "xdual(xdual()) should be identity" ); + delete dual_dual; +#endif + return this; // Return new Type +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool Type::eq( const Type * ) const { + return true; // Nothing else can go wrong +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int Type::hash(void) const { + return _base; +} + +//------------------------------is_finite-------------------------------------- +// Has a finite value +bool Type::is_finite() const { + return false; +} + +//------------------------------is_nan----------------------------------------- +// Is not a number (NaN) +bool Type::is_nan() const { + return false; +} + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. NOT virtual. It enforces that meet is +// commutative and the lattice is symmetric. +const Type *Type::meet( const Type *t ) const { + const Type *mt = xmeet(t); +#ifdef ASSERT + assert( mt == t->xmeet(this), "meet not commutative" ); + const Type* dual_join = mt->_dual; + const Type *t2t = dual_join->xmeet(t->_dual); + const Type *t2this = dual_join->xmeet( _dual); + + // Interface meet Oop is Not Symmetric: + // Interface:AnyNull meet Oop:AnyNull == Interface:AnyNull + // Interface:NotNull meet Oop:NotNull == java/lang/Object:NotNull + const TypeInstPtr* this_inst = this->isa_instptr(); + const TypeInstPtr* t_inst = t->isa_instptr(); + bool interface_vs_oop = false; + if( this_inst && this_inst->is_loaded() && t_inst && t_inst->is_loaded() ) { + bool this_interface = this_inst->klass()->is_interface(); + bool t_interface = t_inst->klass()->is_interface(); + interface_vs_oop = this_interface ^ t_interface; + } + const Type *tdual = t->_dual; + const Type *thisdual = _dual; + // strip out instances + if (t2t->isa_oopptr() != NULL) { + t2t = t2t->isa_oopptr()->cast_to_instance(TypeOopPtr::UNKNOWN_INSTANCE); + } + if (t2this->isa_oopptr() != NULL) { + t2this = t2this->isa_oopptr()->cast_to_instance(TypeOopPtr::UNKNOWN_INSTANCE); + } + if (tdual->isa_oopptr() != NULL) { + tdual = tdual->isa_oopptr()->cast_to_instance(TypeOopPtr::UNKNOWN_INSTANCE); + } + if (thisdual->isa_oopptr() != NULL) { + thisdual = thisdual->isa_oopptr()->cast_to_instance(TypeOopPtr::UNKNOWN_INSTANCE); + } + + if( !interface_vs_oop && (t2t != tdual || t2this != thisdual) ) { + tty->print_cr("=== Meet Not Symmetric ==="); + tty->print("t = "); t->dump(); tty->cr(); + tty->print("this= "); dump(); tty->cr(); + tty->print("mt=(t meet this)= "); mt->dump(); tty->cr(); + + tty->print("t_dual= "); t->_dual->dump(); tty->cr(); + tty->print("this_dual= "); _dual->dump(); tty->cr(); + tty->print("mt_dual= "); mt->_dual->dump(); tty->cr(); + + tty->print("mt_dual meet t_dual= "); t2t ->dump(); tty->cr(); + tty->print("mt_dual meet this_dual= "); t2this ->dump(); tty->cr(); + + fatal("meet not symmetric" ); + } +#endif + return mt; +} + +//------------------------------xmeet------------------------------------------ +// Compute the MEET of two types. It returns a new Type object. +const Type *Type::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Meeting TOP with anything? + if( _base == Top ) return t; + + // Meeting BOTTOM with anything? + if( _base == Bottom ) return BOTTOM; + + // Current "this->_base" is one of: Bad, Multi, Control, Top, + // Abio, Abstore, Floatxxx, Doublexxx, Bottom, lastype. + switch (t->base()) { // Switch on original type + + // Cut in half the number of cases I must handle. Only need cases for when + // the given enum "t->type" is less than or equal to the local enum "type". + case FloatCon: + case DoubleCon: + case Int: + case Long: + return t->xmeet(this); + + case OopPtr: + return t->xmeet(this); + + case InstPtr: + return t->xmeet(this); + + case KlassPtr: + return t->xmeet(this); + + case AryPtr: + return t->xmeet(this); + + case Bad: // Type check + default: // Bogus type not in lattice + typerr(t); + return Type::BOTTOM; + + case Bottom: // Ye Olde Default + return t; + + case FloatTop: + if( _base == FloatTop ) return this; + case FloatBot: // Float + if( _base == FloatBot || _base == FloatTop ) return FLOAT; + if( _base == DoubleTop || _base == DoubleBot ) return Type::BOTTOM; + typerr(t); + return Type::BOTTOM; + + case DoubleTop: + if( _base == DoubleTop ) return this; + case DoubleBot: // Double + if( _base == DoubleBot || _base == DoubleTop ) return DOUBLE; + if( _base == FloatTop || _base == FloatBot ) return Type::BOTTOM; + typerr(t); + return Type::BOTTOM; + + // These next few cases must match exactly or it is a compile-time error. + case Control: // Control of code + case Abio: // State of world outside of program + case Memory: + if( _base == t->_base ) return this; + typerr(t); + return Type::BOTTOM; + + case Top: // Top of the lattice + return this; + } + + // The type is unchanged + return this; +} + +//-----------------------------filter------------------------------------------ +const Type *Type::filter( const Type *kills ) const { + const Type* ft = join(kills); + if (ft->empty()) + return Type::TOP; // Canonical empty value + return ft; +} + +//------------------------------xdual------------------------------------------ +// Compute dual right now. +const Type::TYPES Type::dual_type[Type::lastype] = { + Bad, // Bad + Control, // Control + Bottom, // Top + Bad, // Int - handled in v-call + Bad, // Long - handled in v-call + Half, // Half + + Bad, // Tuple - handled in v-call + Bad, // Array - handled in v-call + + Bad, // AnyPtr - handled in v-call + Bad, // RawPtr - handled in v-call + Bad, // OopPtr - handled in v-call + Bad, // InstPtr - handled in v-call + Bad, // AryPtr - handled in v-call + Bad, // KlassPtr - handled in v-call + + Bad, // Function - handled in v-call + Abio, // Abio + Return_Address,// Return_Address + Memory, // Memory + FloatBot, // FloatTop + FloatCon, // FloatCon + FloatTop, // FloatBot + DoubleBot, // DoubleTop + DoubleCon, // DoubleCon + DoubleTop, // DoubleBot + Top // Bottom +}; + +const Type *Type::xdual() const { + // Note: the base() accessor asserts the sanity of _base. + assert(dual_type[base()] != Bad, "implement with v-call"); + return new Type(dual_type[_base]); +} + +//------------------------------has_memory------------------------------------- +bool Type::has_memory() const { + Type::TYPES tx = base(); + if (tx == Memory) return true; + if (tx == Tuple) { + const TypeTuple *t = is_tuple(); + for (uint i=0; i < t->cnt(); i++) { + tx = t->field_at(i)->base(); + if (tx == Memory) return true; + } + } + return false; +} + +#ifndef PRODUCT +//------------------------------dump2------------------------------------------ +void Type::dump2( Dict &d, uint depth, outputStream *st ) const { + st->print(msg[_base]); +} + +//------------------------------dump------------------------------------------- +void Type::dump_on(outputStream *st) const { + ResourceMark rm; + Dict d(cmpkey,hashkey); // Stop recursive type dumping + dump2(d,1, st); +} + +//------------------------------data------------------------------------------- +const char * const Type::msg[Type::lastype] = { + "bad","control","top","int:","long:","half", + "tuple:", "aryptr", + "anyptr:", "rawptr:", "java:", "inst:", "ary:", "klass:", + "func", "abIO", "return_address", "memory", + "float_top", "ftcon:", "float", + "double_top", "dblcon:", "double", + "bottom" +}; +#endif + +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants (Ldi nodes). Singletons are integer, float or double constants. +bool Type::singleton(void) const { + return _base == Top || _base == Half; +} + +//------------------------------empty------------------------------------------ +// TRUE if Type is a type with no values, FALSE otherwise. +bool Type::empty(void) const { + switch (_base) { + case DoubleTop: + case FloatTop: + case Top: + return true; + + case Half: + case Abio: + case Return_Address: + case Memory: + case Bottom: + case FloatBot: + case DoubleBot: + return false; // never a singleton, therefore never empty + } + + ShouldNotReachHere(); + return false; +} + +//------------------------------dump_stats------------------------------------- +// Dump collected statistics to stderr +#ifndef PRODUCT +void Type::dump_stats() { + tty->print("Types made: %d\n", type_dict()->Size()); +} +#endif + +//------------------------------typerr----------------------------------------- +void Type::typerr( const Type *t ) const { +#ifndef PRODUCT + tty->print("\nError mixing types: "); + dump(); + tty->print(" and "); + t->dump(); + tty->print("\n"); +#endif + ShouldNotReachHere(); +} + +//------------------------------isa_oop_ptr------------------------------------ +// Return true if type is an oop pointer type. False for raw pointers. +static char isa_oop_ptr_tbl[Type::lastype] = { + 0,0,0,0,0,0,0/*tuple*/, 0/*ary*/, + 0/*anyptr*/,0/*rawptr*/,1/*OopPtr*/,1/*InstPtr*/,1/*AryPtr*/,1/*KlassPtr*/, + 0/*func*/,0,0/*return_address*/,0, + /*floats*/0,0,0, /*doubles*/0,0,0, + 0 +}; +bool Type::isa_oop_ptr() const { + return isa_oop_ptr_tbl[_base] != 0; +} + +//------------------------------dump_stats------------------------------------- +// // Check that arrays match type enum +#ifndef PRODUCT +void Type::verify_lastype() { + // Check that arrays match enumeration + assert( Type::dual_type [Type::lastype - 1] == Type::Top, "did not update array"); + assert( strcmp(Type::msg [Type::lastype - 1],"bottom") == 0, "did not update array"); + // assert( PhiNode::tbl [Type::lastype - 1] == NULL, "did not update array"); + assert( Matcher::base2reg[Type::lastype - 1] == 0, "did not update array"); + assert( isa_oop_ptr_tbl [Type::lastype - 1] == (char)0, "did not update array"); +} +#endif + +//============================================================================= +// Convenience common pre-built types. +const TypeF *TypeF::ZERO; // Floating point zero +const TypeF *TypeF::ONE; // Floating point one + +//------------------------------make------------------------------------------- +// Create a float constant +const TypeF *TypeF::make(float f) { + return (TypeF*)(new TypeF(f))->hashcons(); +} + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type object. +const Type *TypeF::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Current "this->_base" is FloatCon + switch (t->base()) { // Switch on original type + case AnyPtr: // Mixing with oops happens when javac + case RawPtr: // reuses local variables + case OopPtr: + case InstPtr: + case KlassPtr: + case AryPtr: + case Int: + case Long: + case DoubleTop: + case DoubleCon: + case DoubleBot: + case Bottom: // Ye Olde Default + return Type::BOTTOM; + + case FloatBot: + return t; + + default: // All else is a mistake + typerr(t); + + case FloatCon: // Float-constant vs Float-constant? + if( jint_cast(_f) != jint_cast(t->getf()) ) // unequal constants? + // must compare bitwise as positive zero, negative zero and NaN have + // all the same representation in C++ + return FLOAT; // Return generic float + // Equal constants + case Top: + case FloatTop: + break; // Return the float constant + } + return this; // Return the float constant +} + +//------------------------------xdual------------------------------------------ +// Dual: symmetric +const Type *TypeF::xdual() const { + return this; +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeF::eq( const Type *t ) const { + if( g_isnan(_f) || + g_isnan(t->getf()) ) { + // One or both are NANs. If both are NANs return true, else false. + return (g_isnan(_f) && g_isnan(t->getf())); + } + if (_f == t->getf()) { + // (NaN is impossible at this point, since it is not equal even to itself) + if (_f == 0.0) { + // difference between positive and negative zero + if (jint_cast(_f) != jint_cast(t->getf())) return false; + } + return true; + } + return false; +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeF::hash(void) const { + return *(int*)(&_f); +} + +//------------------------------is_finite-------------------------------------- +// Has a finite value +bool TypeF::is_finite() const { + return g_isfinite(getf()) != 0; +} + +//------------------------------is_nan----------------------------------------- +// Is not a number (NaN) +bool TypeF::is_nan() const { + return g_isnan(getf()) != 0; +} + +//------------------------------dump2------------------------------------------ +// Dump float constant Type +#ifndef PRODUCT +void TypeF::dump2( Dict &d, uint depth, outputStream *st ) const { + Type::dump2(d,depth, st); + st->print("%f", _f); +} +#endif + +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants (Ldi nodes). Singletons are integer, float or double constants +// or a single symbol. +bool TypeF::singleton(void) const { + return true; // Always a singleton +} + +bool TypeF::empty(void) const { + return false; // always exactly a singleton +} + +//============================================================================= +// Convenience common pre-built types. +const TypeD *TypeD::ZERO; // Floating point zero +const TypeD *TypeD::ONE; // Floating point one + +//------------------------------make------------------------------------------- +const TypeD *TypeD::make(double d) { + return (TypeD*)(new TypeD(d))->hashcons(); +} + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type object. +const Type *TypeD::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Current "this->_base" is DoubleCon + switch (t->base()) { // Switch on original type + case AnyPtr: // Mixing with oops happens when javac + case RawPtr: // reuses local variables + case OopPtr: + case InstPtr: + case KlassPtr: + case AryPtr: + case Int: + case Long: + case FloatTop: + case FloatCon: + case FloatBot: + case Bottom: // Ye Olde Default + return Type::BOTTOM; + + case DoubleBot: + return t; + + default: // All else is a mistake + typerr(t); + + case DoubleCon: // Double-constant vs Double-constant? + if( jlong_cast(_d) != jlong_cast(t->getd()) ) // unequal constants? (see comment in TypeF::xmeet) + return DOUBLE; // Return generic double + case Top: + case DoubleTop: + break; + } + return this; // Return the double constant +} + +//------------------------------xdual------------------------------------------ +// Dual: symmetric +const Type *TypeD::xdual() const { + return this; +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeD::eq( const Type *t ) const { + if( g_isnan(_d) || + g_isnan(t->getd()) ) { + // One or both are NANs. If both are NANs return true, else false. + return (g_isnan(_d) && g_isnan(t->getd())); + } + if (_d == t->getd()) { + // (NaN is impossible at this point, since it is not equal even to itself) + if (_d == 0.0) { + // difference between positive and negative zero + if (jlong_cast(_d) != jlong_cast(t->getd())) return false; + } + return true; + } + return false; +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeD::hash(void) const { + return *(int*)(&_d); +} + +//------------------------------is_finite-------------------------------------- +// Has a finite value +bool TypeD::is_finite() const { + return g_isfinite(getd()) != 0; +} + +//------------------------------is_nan----------------------------------------- +// Is not a number (NaN) +bool TypeD::is_nan() const { + return g_isnan(getd()) != 0; +} + +//------------------------------dump2------------------------------------------ +// Dump double constant Type +#ifndef PRODUCT +void TypeD::dump2( Dict &d, uint depth, outputStream *st ) const { + Type::dump2(d,depth,st); + st->print("%f", _d); +} +#endif + +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants (Ldi nodes). Singletons are integer, float or double constants +// or a single symbol. +bool TypeD::singleton(void) const { + return true; // Always a singleton +} + +bool TypeD::empty(void) const { + return false; // always exactly a singleton +} + +//============================================================================= +// Convience common pre-built types. +const TypeInt *TypeInt::MINUS_1;// -1 +const TypeInt *TypeInt::ZERO; // 0 +const TypeInt *TypeInt::ONE; // 1 +const TypeInt *TypeInt::BOOL; // 0 or 1, FALSE or TRUE. +const TypeInt *TypeInt::CC; // -1,0 or 1, condition codes +const TypeInt *TypeInt::CC_LT; // [-1] == MINUS_1 +const TypeInt *TypeInt::CC_GT; // [1] == ONE +const TypeInt *TypeInt::CC_EQ; // [0] == ZERO +const TypeInt *TypeInt::CC_LE; // [-1,0] +const TypeInt *TypeInt::CC_GE; // [0,1] == BOOL (!) +const TypeInt *TypeInt::BYTE; // Bytes, -128 to 127 +const TypeInt *TypeInt::CHAR; // Java chars, 0-65535 +const TypeInt *TypeInt::SHORT; // Java shorts, -32768-32767 +const TypeInt *TypeInt::POS; // Positive 32-bit integers or zero +const TypeInt *TypeInt::POS1; // Positive 32-bit integers +const TypeInt *TypeInt::INT; // 32-bit integers +const TypeInt *TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] + +//------------------------------TypeInt---------------------------------------- +TypeInt::TypeInt( jint lo, jint hi, int w ) : Type(Int), _lo(lo), _hi(hi), _widen(w) { +} + +//------------------------------make------------------------------------------- +const TypeInt *TypeInt::make( jint lo ) { + return (TypeInt*)(new TypeInt(lo,lo,WidenMin))->hashcons(); +} + +#define SMALLINT ((juint)3) // a value too insignificant to consider widening + +const TypeInt *TypeInt::make( jint lo, jint hi, int w ) { + // Certain normalizations keep us sane when comparing types. + // The 'SMALLINT' covers constants and also CC and its relatives. + assert(CC == NULL || (juint)(CC->_hi - CC->_lo) <= SMALLINT, "CC is truly small"); + if (lo <= hi) { + if ((juint)(hi - lo) <= SMALLINT) w = Type::WidenMin; + if ((juint)(hi - lo) >= max_juint) w = Type::WidenMax; // plain int + } + return (TypeInt*)(new TypeInt(lo,hi,w))->hashcons(); +} + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type representation object +// with reference count equal to the number of Types pointing at it. +// Caller should wrap a Types around it. +const Type *TypeInt::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type? + + // Currently "this->_base" is a TypeInt + switch (t->base()) { // Switch on original type + case AnyPtr: // Mixing with oops happens when javac + case RawPtr: // reuses local variables + case OopPtr: + case InstPtr: + case KlassPtr: + case AryPtr: + case Long: + case FloatTop: + case FloatCon: + case FloatBot: + case DoubleTop: + case DoubleCon: + case DoubleBot: + case Bottom: // Ye Olde Default + return Type::BOTTOM; + default: // All else is a mistake + typerr(t); + case Top: // No change + return this; + case Int: // Int vs Int? + break; + } + + // Expand covered set + const TypeInt *r = t->is_int(); + // (Avoid TypeInt::make, to avoid the argument normalizations it enforces.) + return (new TypeInt( MIN2(_lo,r->_lo), MAX2(_hi,r->_hi), MAX2(_widen,r->_widen) ))->hashcons(); +} + +//------------------------------xdual------------------------------------------ +// Dual: reverse hi & lo; flip widen +const Type *TypeInt::xdual() const { + return new TypeInt(_hi,_lo,WidenMax-_widen); +} + +//------------------------------widen------------------------------------------ +// Only happens for optimistic top-down optimizations. +const Type *TypeInt::widen( const Type *old ) const { + // Coming from TOP or such; no widening + if( old->base() != Int ) return this; + const TypeInt *ot = old->is_int(); + + // If new guy is equal to old guy, no widening + if( _lo == ot->_lo && _hi == ot->_hi ) + return old; + + // If new guy contains old, then we widened + if( _lo <= ot->_lo && _hi >= ot->_hi ) { + // New contains old + // If new guy is already wider than old, no widening + if( _widen > ot->_widen ) return this; + // If old guy was a constant, do not bother + if (ot->_lo == ot->_hi) return this; + // Now widen new guy. + // Check for widening too far + if (_widen == WidenMax) { + if (min_jint < _lo && _hi < max_jint) { + // If neither endpoint is extremal yet, push out the endpoint + // which is closer to its respective limit. + if (_lo >= 0 || // easy common case + (juint)(_lo - min_jint) >= (juint)(max_jint - _hi)) { + // Try to widen to an unsigned range type of 31 bits: + return make(_lo, max_jint, WidenMax); + } else { + return make(min_jint, _hi, WidenMax); + } + } + return TypeInt::INT; + } + // Returned widened new guy + return make(_lo,_hi,_widen+1); + } + + // If old guy contains new, then we probably widened too far & dropped to + // bottom. Return the wider fellow. + if ( ot->_lo <= _lo && ot->_hi >= _hi ) + return old; + + //fatal("Integer value range is not subset"); + //return this; + return TypeInt::INT; +} + +//------------------------------narrow--------------------------------------- +// Only happens for pessimistic optimizations. +const Type *TypeInt::narrow( const Type *old ) const { + if (_lo >= _hi) return this; // already narrow enough + if (old == NULL) return this; + const TypeInt* ot = old->isa_int(); + if (ot == NULL) return this; + jint olo = ot->_lo; + jint ohi = ot->_hi; + + // If new guy is equal to old guy, no narrowing + if (_lo == olo && _hi == ohi) return old; + + // If old guy was maximum range, allow the narrowing + if (olo == min_jint && ohi == max_jint) return this; + + if (_lo < olo || _hi > ohi) + return this; // doesn't narrow; pretty wierd + + // The new type narrows the old type, so look for a "death march". + // See comments on PhaseTransform::saturate. + juint nrange = _hi - _lo; + juint orange = ohi - olo; + if (nrange < max_juint - 1 && nrange > (orange >> 1) + (SMALLINT*2)) { + // Use the new type only if the range shrinks a lot. + // We do not want the optimizer computing 2^31 point by point. + return old; + } + + return this; +} + +//-----------------------------filter------------------------------------------ +const Type *TypeInt::filter( const Type *kills ) const { + const TypeInt* ft = join(kills)->isa_int(); + if (ft == NULL || ft->_lo > ft->_hi) + return Type::TOP; // Canonical empty value + if (ft->_widen < this->_widen) { + // Do not allow the value of kill->_widen to affect the outcome. + // The widen bits must be allowed to run freely through the graph. + ft = TypeInt::make(ft->_lo, ft->_hi, this->_widen); + } + return ft; +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeInt::eq( const Type *t ) const { + const TypeInt *r = t->is_int(); // Handy access + return r->_lo == _lo && r->_hi == _hi && r->_widen == _widen; +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeInt::hash(void) const { + return _lo+_hi+_widen+(int)Type::Int; +} + +//------------------------------is_finite-------------------------------------- +// Has a finite value +bool TypeInt::is_finite() const { + return true; +} + +//------------------------------dump2------------------------------------------ +// Dump TypeInt +#ifndef PRODUCT +static const char* intname(char* buf, jint n) { + if (n == min_jint) + return "min"; + else if (n < min_jint + 10000) + sprintf(buf, "min+" INT32_FORMAT, n - min_jint); + else if (n == max_jint) + return "max"; + else if (n > max_jint - 10000) + sprintf(buf, "max-" INT32_FORMAT, max_jint - n); + else + sprintf(buf, INT32_FORMAT, n); + return buf; +} + +void TypeInt::dump2( Dict &d, uint depth, outputStream *st ) const { + char buf[40], buf2[40]; + if (_lo == min_jint && _hi == max_jint) + st->print("int"); + else if (is_con()) + st->print("int:%s", intname(buf, get_con())); + else if (_lo == BOOL->_lo && _hi == BOOL->_hi) + st->print("bool"); + else if (_lo == BYTE->_lo && _hi == BYTE->_hi) + st->print("byte"); + else if (_lo == CHAR->_lo && _hi == CHAR->_hi) + st->print("char"); + else if (_lo == SHORT->_lo && _hi == SHORT->_hi) + st->print("short"); + else if (_hi == max_jint) + st->print("int:>=%s", intname(buf, _lo)); + else if (_lo == min_jint) + st->print("int:<=%s", intname(buf, _hi)); + else + st->print("int:%s..%s", intname(buf, _lo), intname(buf2, _hi)); + + if (_widen != 0 && this != TypeInt::INT) + st->print(":%.*s", _widen, "wwww"); +} +#endif + +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants. +bool TypeInt::singleton(void) const { + return _lo >= _hi; +} + +bool TypeInt::empty(void) const { + return _lo > _hi; +} + +//============================================================================= +// Convenience common pre-built types. +const TypeLong *TypeLong::MINUS_1;// -1 +const TypeLong *TypeLong::ZERO; // 0 +const TypeLong *TypeLong::ONE; // 1 +const TypeLong *TypeLong::POS; // >=0 +const TypeLong *TypeLong::LONG; // 64-bit integers +const TypeLong *TypeLong::INT; // 32-bit subrange +const TypeLong *TypeLong::UINT; // 32-bit unsigned subrange + +//------------------------------TypeLong--------------------------------------- +TypeLong::TypeLong( jlong lo, jlong hi, int w ) : Type(Long), _lo(lo), _hi(hi), _widen(w) { +} + +//------------------------------make------------------------------------------- +const TypeLong *TypeLong::make( jlong lo ) { + return (TypeLong*)(new TypeLong(lo,lo,WidenMin))->hashcons(); +} + +const TypeLong *TypeLong::make( jlong lo, jlong hi, int w ) { + // Certain normalizations keep us sane when comparing types. + // The '1' covers constants. + if (lo <= hi) { + if ((julong)(hi - lo) <= SMALLINT) w = Type::WidenMin; + if ((julong)(hi - lo) >= max_julong) w = Type::WidenMax; // plain long + } + return (TypeLong*)(new TypeLong(lo,hi,w))->hashcons(); +} + + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type representation object +// with reference count equal to the number of Types pointing at it. +// Caller should wrap a Types around it. +const Type *TypeLong::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type? + + // Currently "this->_base" is a TypeLong + switch (t->base()) { // Switch on original type + case AnyPtr: // Mixing with oops happens when javac + case RawPtr: // reuses local variables + case OopPtr: + case InstPtr: + case KlassPtr: + case AryPtr: + case Int: + case FloatTop: + case FloatCon: + case FloatBot: + case DoubleTop: + case DoubleCon: + case DoubleBot: + case Bottom: // Ye Olde Default + return Type::BOTTOM; + default: // All else is a mistake + typerr(t); + case Top: // No change + return this; + case Long: // Long vs Long? + break; + } + + // Expand covered set + const TypeLong *r = t->is_long(); // Turn into a TypeLong + // (Avoid TypeLong::make, to avoid the argument normalizations it enforces.) + return (new TypeLong( MIN2(_lo,r->_lo), MAX2(_hi,r->_hi), MAX2(_widen,r->_widen) ))->hashcons(); +} + +//------------------------------xdual------------------------------------------ +// Dual: reverse hi & lo; flip widen +const Type *TypeLong::xdual() const { + return new TypeLong(_hi,_lo,WidenMax-_widen); +} + +//------------------------------widen------------------------------------------ +// Only happens for optimistic top-down optimizations. +const Type *TypeLong::widen( const Type *old ) const { + // Coming from TOP or such; no widening + if( old->base() != Long ) return this; + const TypeLong *ot = old->is_long(); + + // If new guy is equal to old guy, no widening + if( _lo == ot->_lo && _hi == ot->_hi ) + return old; + + // If new guy contains old, then we widened + if( _lo <= ot->_lo && _hi >= ot->_hi ) { + // New contains old + // If new guy is already wider than old, no widening + if( _widen > ot->_widen ) return this; + // If old guy was a constant, do not bother + if (ot->_lo == ot->_hi) return this; + // Now widen new guy. + // Check for widening too far + if (_widen == WidenMax) { + if (min_jlong < _lo && _hi < max_jlong) { + // If neither endpoint is extremal yet, push out the endpoint + // which is closer to its respective limit. + if (_lo >= 0 || // easy common case + (julong)(_lo - min_jlong) >= (julong)(max_jlong - _hi)) { + // Try to widen to an unsigned range type of 32/63 bits: + if (_hi < max_juint) + return make(_lo, max_juint, WidenMax); + else + return make(_lo, max_jlong, WidenMax); + } else { + return make(min_jlong, _hi, WidenMax); + } + } + return TypeLong::LONG; + } + // Returned widened new guy + return make(_lo,_hi,_widen+1); + } + + // If old guy contains new, then we probably widened too far & dropped to + // bottom. Return the wider fellow. + if ( ot->_lo <= _lo && ot->_hi >= _hi ) + return old; + + // fatal("Long value range is not subset"); + // return this; + return TypeLong::LONG; +} + +//------------------------------narrow---------------------------------------- +// Only happens for pessimistic optimizations. +const Type *TypeLong::narrow( const Type *old ) const { + if (_lo >= _hi) return this; // already narrow enough + if (old == NULL) return this; + const TypeLong* ot = old->isa_long(); + if (ot == NULL) return this; + jlong olo = ot->_lo; + jlong ohi = ot->_hi; + + // If new guy is equal to old guy, no narrowing + if (_lo == olo && _hi == ohi) return old; + + // If old guy was maximum range, allow the narrowing + if (olo == min_jlong && ohi == max_jlong) return this; + + if (_lo < olo || _hi > ohi) + return this; // doesn't narrow; pretty wierd + + // The new type narrows the old type, so look for a "death march". + // See comments on PhaseTransform::saturate. + julong nrange = _hi - _lo; + julong orange = ohi - olo; + if (nrange < max_julong - 1 && nrange > (orange >> 1) + (SMALLINT*2)) { + // Use the new type only if the range shrinks a lot. + // We do not want the optimizer computing 2^31 point by point. + return old; + } + + return this; +} + +//-----------------------------filter------------------------------------------ +const Type *TypeLong::filter( const Type *kills ) const { + const TypeLong* ft = join(kills)->isa_long(); + if (ft == NULL || ft->_lo > ft->_hi) + return Type::TOP; // Canonical empty value + if (ft->_widen < this->_widen) { + // Do not allow the value of kill->_widen to affect the outcome. + // The widen bits must be allowed to run freely through the graph. + ft = TypeLong::make(ft->_lo, ft->_hi, this->_widen); + } + return ft; +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeLong::eq( const Type *t ) const { + const TypeLong *r = t->is_long(); // Handy access + return r->_lo == _lo && r->_hi == _hi && r->_widen == _widen; +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeLong::hash(void) const { + return (int)(_lo+_hi+_widen+(int)Type::Long); +} + +//------------------------------is_finite-------------------------------------- +// Has a finite value +bool TypeLong::is_finite() const { + return true; +} + +//------------------------------dump2------------------------------------------ +// Dump TypeLong +#ifndef PRODUCT +static const char* longnamenear(jlong x, const char* xname, char* buf, jlong n) { + if (n > x) { + if (n >= x + 10000) return NULL; + sprintf(buf, "%s+" INT64_FORMAT, xname, n - x); + } else if (n < x) { + if (n <= x - 10000) return NULL; + sprintf(buf, "%s-" INT64_FORMAT, xname, x - n); + } else { + return xname; + } + return buf; +} + +static const char* longname(char* buf, jlong n) { + const char* str; + if (n == min_jlong) + return "min"; + else if (n < min_jlong + 10000) + sprintf(buf, "min+" INT64_FORMAT, n - min_jlong); + else if (n == max_jlong) + return "max"; + else if (n > max_jlong - 10000) + sprintf(buf, "max-" INT64_FORMAT, max_jlong - n); + else if ((str = longnamenear(max_juint, "maxuint", buf, n)) != NULL) + return str; + else if ((str = longnamenear(max_jint, "maxint", buf, n)) != NULL) + return str; + else if ((str = longnamenear(min_jint, "minint", buf, n)) != NULL) + return str; + else + sprintf(buf, INT64_FORMAT, n); + return buf; +} + +void TypeLong::dump2( Dict &d, uint depth, outputStream *st ) const { + char buf[80], buf2[80]; + if (_lo == min_jlong && _hi == max_jlong) + st->print("long"); + else if (is_con()) + st->print("long:%s", longname(buf, get_con())); + else if (_hi == max_jlong) + st->print("long:>=%s", longname(buf, _lo)); + else if (_lo == min_jlong) + st->print("long:<=%s", longname(buf, _hi)); + else + st->print("long:%s..%s", longname(buf, _lo), longname(buf2, _hi)); + + if (_widen != 0 && this != TypeLong::LONG) + st->print(":%.*s", _widen, "wwww"); +} +#endif + +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants +bool TypeLong::singleton(void) const { + return _lo >= _hi; +} + +bool TypeLong::empty(void) const { + return _lo > _hi; +} + +//============================================================================= +// Convenience common pre-built types. +const TypeTuple *TypeTuple::IFBOTH; // Return both arms of IF as reachable +const TypeTuple *TypeTuple::IFFALSE; +const TypeTuple *TypeTuple::IFTRUE; +const TypeTuple *TypeTuple::IFNEITHER; +const TypeTuple *TypeTuple::LOOPBODY; +const TypeTuple *TypeTuple::MEMBAR; +const TypeTuple *TypeTuple::STORECONDITIONAL; +const TypeTuple *TypeTuple::START_I2C; +const TypeTuple *TypeTuple::INT_PAIR; +const TypeTuple *TypeTuple::LONG_PAIR; + + +//------------------------------make------------------------------------------- +// Make a TypeTuple from the range of a method signature +const TypeTuple *TypeTuple::make_range(ciSignature* sig) { + ciType* return_type = sig->return_type(); + uint total_fields = TypeFunc::Parms + return_type->size(); + const Type **field_array = fields(total_fields); + switch (return_type->basic_type()) { + case T_LONG: + field_array[TypeFunc::Parms] = TypeLong::LONG; + field_array[TypeFunc::Parms+1] = Type::HALF; + break; + case T_DOUBLE: + field_array[TypeFunc::Parms] = Type::DOUBLE; + field_array[TypeFunc::Parms+1] = Type::HALF; + break; + case T_OBJECT: + case T_ARRAY: + case T_BOOLEAN: + case T_CHAR: + case T_FLOAT: + case T_BYTE: + case T_SHORT: + case T_INT: + field_array[TypeFunc::Parms] = get_const_type(return_type); + break; + case T_VOID: + break; + default: + ShouldNotReachHere(); + } + return (TypeTuple*)(new TypeTuple(total_fields,field_array))->hashcons(); +} + +// Make a TypeTuple from the domain of a method signature +const TypeTuple *TypeTuple::make_domain(ciInstanceKlass* recv, ciSignature* sig) { + uint total_fields = TypeFunc::Parms + sig->size(); + + uint pos = TypeFunc::Parms; + const Type **field_array; + if (recv != NULL) { + total_fields++; + field_array = fields(total_fields); + // Use get_const_type here because it respects UseUniqueSubclasses: + field_array[pos++] = get_const_type(recv)->join(TypePtr::NOTNULL); + } else { + field_array = fields(total_fields); + } + + int i = 0; + while (pos < total_fields) { + ciType* type = sig->type_at(i); + + switch (type->basic_type()) { + case T_LONG: + field_array[pos++] = TypeLong::LONG; + field_array[pos++] = Type::HALF; + break; + case T_DOUBLE: + field_array[pos++] = Type::DOUBLE; + field_array[pos++] = Type::HALF; + break; + case T_OBJECT: + case T_ARRAY: + case T_BOOLEAN: + case T_CHAR: + case T_FLOAT: + case T_BYTE: + case T_SHORT: + case T_INT: + field_array[pos++] = get_const_type(type); + break; + default: + ShouldNotReachHere(); + } + i++; + } + return (TypeTuple*)(new TypeTuple(total_fields,field_array))->hashcons(); +} + +const TypeTuple *TypeTuple::make( uint cnt, const Type **fields ) { + return (TypeTuple*)(new TypeTuple(cnt,fields))->hashcons(); +} + +//------------------------------fields----------------------------------------- +// Subroutine call type with space allocated for argument types +const Type **TypeTuple::fields( uint arg_cnt ) { + const Type **flds = (const Type **)(Compile::current()->type_arena()->Amalloc_4((TypeFunc::Parms+arg_cnt)*sizeof(Type*) )); + flds[TypeFunc::Control ] = Type::CONTROL; + flds[TypeFunc::I_O ] = Type::ABIO; + flds[TypeFunc::Memory ] = Type::MEMORY; + flds[TypeFunc::FramePtr ] = TypeRawPtr::BOTTOM; + flds[TypeFunc::ReturnAdr] = Type::RETURN_ADDRESS; + + return flds; +} + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type object. +const Type *TypeTuple::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Current "this->_base" is Tuple + switch (t->base()) { // switch on original type + + case Bottom: // Ye Olde Default + return t; + + default: // All else is a mistake + typerr(t); + + case Tuple: { // Meeting 2 signatures? + const TypeTuple *x = t->is_tuple(); + assert( _cnt == x->_cnt, "" ); + const Type **fields = (const Type **)(Compile::current()->type_arena()->Amalloc_4( _cnt*sizeof(Type*) )); + for( uint i=0; i<_cnt; i++ ) + fields[i] = field_at(i)->xmeet( x->field_at(i) ); + return TypeTuple::make(_cnt,fields); + } + case Top: + break; + } + return this; // Return the double constant +} + +//------------------------------xdual------------------------------------------ +// Dual: compute field-by-field dual +const Type *TypeTuple::xdual() const { + const Type **fields = (const Type **)(Compile::current()->type_arena()->Amalloc_4( _cnt*sizeof(Type*) )); + for( uint i=0; i<_cnt; i++ ) + fields[i] = _fields[i]->dual(); + return new TypeTuple(_cnt,fields); +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeTuple::eq( const Type *t ) const { + const TypeTuple *s = (const TypeTuple *)t; + if (_cnt != s->_cnt) return false; // Unequal field counts + for (uint i = 0; i < _cnt; i++) + if (field_at(i) != s->field_at(i)) // POINTER COMPARE! NO RECURSION! + return false; // Missed + return true; +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeTuple::hash(void) const { + intptr_t sum = _cnt; + for( uint i=0; i<_cnt; i++ ) + sum += (intptr_t)_fields[i]; // Hash on pointers directly + return sum; +} + +//------------------------------dump2------------------------------------------ +// Dump signature Type +#ifndef PRODUCT +void TypeTuple::dump2( Dict &d, uint depth, outputStream *st ) const { + st->print("{"); + if( !depth || d[this] ) { // Check for recursive print + st->print("...}"); + return; + } + d.Insert((void*)this, (void*)this); // Stop recursion + if( _cnt ) { + uint i; + for( i=0; i<_cnt-1; i++ ) { + st->print("%d:", i); + _fields[i]->dump2(d, depth-1, st); + st->print(", "); + } + st->print("%d:", i); + _fields[i]->dump2(d, depth-1, st); + } + st->print("}"); +} +#endif + +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants (Ldi nodes). Singletons are integer, float or double constants +// or a single symbol. +bool TypeTuple::singleton(void) const { + return false; // Never a singleton +} + +bool TypeTuple::empty(void) const { + for( uint i=0; i<_cnt; i++ ) { + if (_fields[i]->empty()) return true; + } + return false; +} + +//============================================================================= +// Convenience common pre-built types. + +inline const TypeInt* normalize_array_size(const TypeInt* size) { + // Certain normalizations keep us sane when comparing types. + // We do not want arrayOop variables to differ only by the wideness + // of their index types. Pick minimum wideness, since that is the + // forced wideness of small ranges anyway. + if (size->_widen != Type::WidenMin) + return TypeInt::make(size->_lo, size->_hi, Type::WidenMin); + else + return size; +} + +//------------------------------make------------------------------------------- +const TypeAry *TypeAry::make( const Type *elem, const TypeInt *size) { + size = normalize_array_size(size); + return (TypeAry*)(new TypeAry(elem,size))->hashcons(); +} + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type object. +const Type *TypeAry::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Current "this->_base" is Ary + switch (t->base()) { // switch on original type + + case Bottom: // Ye Olde Default + return t; + + default: // All else is a mistake + typerr(t); + + case Array: { // Meeting 2 arrays? + const TypeAry *a = t->is_ary(); + return TypeAry::make(_elem->meet(a->_elem), + _size->xmeet(a->_size)->is_int()); + } + case Top: + break; + } + return this; // Return the double constant +} + +//------------------------------xdual------------------------------------------ +// Dual: compute field-by-field dual +const Type *TypeAry::xdual() const { + const TypeInt* size_dual = _size->dual()->is_int(); + size_dual = normalize_array_size(size_dual); + return new TypeAry( _elem->dual(), size_dual); +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeAry::eq( const Type *t ) const { + const TypeAry *a = (const TypeAry*)t; + return _elem == a->_elem && + _size == a->_size; +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeAry::hash(void) const { + return (intptr_t)_elem + (intptr_t)_size; +} + +//------------------------------dump2------------------------------------------ +#ifndef PRODUCT +void TypeAry::dump2( Dict &d, uint depth, outputStream *st ) const { + _elem->dump2(d, depth, st); + st->print("["); + _size->dump2(d, depth, st); + st->print("]"); +} +#endif + +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants (Ldi nodes). Singletons are integer, float or double constants +// or a single symbol. +bool TypeAry::singleton(void) const { + return false; // Never a singleton +} + +bool TypeAry::empty(void) const { + return _elem->empty() || _size->empty(); +} + +//--------------------------ary_must_be_exact---------------------------------- +bool TypeAry::ary_must_be_exact() const { + if (!UseExactTypes) return false; + // This logic looks at the element type of an array, and returns true + // if the element type is either a primitive or a final instance class. + // In such cases, an array built on this ary must have no subclasses. + if (_elem == BOTTOM) return false; // general array not exact + if (_elem == TOP ) return false; // inverted general array not exact + const TypeOopPtr* toop = _elem->isa_oopptr(); + if (!toop) return true; // a primitive type, like int + ciKlass* tklass = toop->klass(); + if (tklass == NULL) return false; // unloaded class + if (!tklass->is_loaded()) return false; // unloaded class + const TypeInstPtr* tinst = _elem->isa_instptr(); + if (tinst) return tklass->as_instance_klass()->is_final(); + const TypeAryPtr* tap = _elem->isa_aryptr(); + if (tap) return tap->ary()->ary_must_be_exact(); + return false; +} + +//============================================================================= +// Convenience common pre-built types. +const TypePtr *TypePtr::NULL_PTR; +const TypePtr *TypePtr::NOTNULL; +const TypePtr *TypePtr::BOTTOM; + +//------------------------------meet------------------------------------------- +// Meet over the PTR enum +const TypePtr::PTR TypePtr::ptr_meet[TypePtr::lastPTR][TypePtr::lastPTR] = { + // TopPTR, AnyNull, Constant, Null, NotNull, BotPTR, + { /* Top */ TopPTR, AnyNull, Constant, Null, NotNull, BotPTR,}, + { /* AnyNull */ AnyNull, AnyNull, Constant, BotPTR, NotNull, BotPTR,}, + { /* Constant*/ Constant, Constant, Constant, BotPTR, NotNull, BotPTR,}, + { /* Null */ Null, BotPTR, BotPTR, Null, BotPTR, BotPTR,}, + { /* NotNull */ NotNull, NotNull, NotNull, BotPTR, NotNull, BotPTR,}, + { /* BotPTR */ BotPTR, BotPTR, BotPTR, BotPTR, BotPTR, BotPTR,} +}; + +//------------------------------make------------------------------------------- +const TypePtr *TypePtr::make( TYPES t, enum PTR ptr, int offset ) { + return (TypePtr*)(new TypePtr(t,ptr,offset))->hashcons(); +} + +//------------------------------cast_to_ptr_type------------------------------- +const Type *TypePtr::cast_to_ptr_type(PTR ptr) const { + assert(_base == AnyPtr, "subclass must override cast_to_ptr_type"); + if( ptr == _ptr ) return this; + return make(_base, ptr, _offset); +} + +//------------------------------get_con---------------------------------------- +intptr_t TypePtr::get_con() const { + assert( _ptr == Null, "" ); + return _offset; +} + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type object. +const Type *TypePtr::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Current "this->_base" is AnyPtr + switch (t->base()) { // switch on original type + case Int: // Mixing ints & oops happens when javac + case Long: // reuses local variables + case FloatTop: + case FloatCon: + case FloatBot: + case DoubleTop: + case DoubleCon: + case DoubleBot: + case Bottom: // Ye Olde Default + return Type::BOTTOM; + case Top: + return this; + + case AnyPtr: { // Meeting to AnyPtrs + const TypePtr *tp = t->is_ptr(); + return make( AnyPtr, meet_ptr(tp->ptr()), meet_offset(tp->offset()) ); + } + case RawPtr: // For these, flip the call around to cut down + case OopPtr: + case InstPtr: // on the cases I have to handle. + case KlassPtr: + case AryPtr: + return t->xmeet(this); // Call in reverse direction + default: // All else is a mistake + typerr(t); + + } + return this; +} + +//------------------------------meet_offset------------------------------------ +int TypePtr::meet_offset( int offset ) const { + // Either is 'TOP' offset? Return the other offset! + if( _offset == OffsetTop ) return offset; + if( offset == OffsetTop ) return _offset; + // If either is different, return 'BOTTOM' offset + if( _offset != offset ) return OffsetBot; + return _offset; +} + +//------------------------------dual_offset------------------------------------ +int TypePtr::dual_offset( ) const { + if( _offset == OffsetTop ) return OffsetBot;// Map 'TOP' into 'BOTTOM' + if( _offset == OffsetBot ) return OffsetTop;// Map 'BOTTOM' into 'TOP' + return _offset; // Map everything else into self +} + +//------------------------------xdual------------------------------------------ +// Dual: compute field-by-field dual +const TypePtr::PTR TypePtr::ptr_dual[TypePtr::lastPTR] = { + BotPTR, NotNull, Constant, Null, AnyNull, TopPTR +}; +const Type *TypePtr::xdual() const { + return new TypePtr( AnyPtr, dual_ptr(), dual_offset() ); +} + +//------------------------------add_offset------------------------------------- +const TypePtr *TypePtr::add_offset( int offset ) const { + if( offset == 0 ) return this; // No change + if( _offset == OffsetBot ) return this; + if( offset == OffsetBot ) offset = OffsetBot; + else if( _offset == OffsetTop || offset == OffsetTop ) offset = OffsetTop; + else offset += _offset; + return make( AnyPtr, _ptr, offset ); +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypePtr::eq( const Type *t ) const { + const TypePtr *a = (const TypePtr*)t; + return _ptr == a->ptr() && _offset == a->offset(); +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypePtr::hash(void) const { + return _ptr + _offset; +} + +//------------------------------dump2------------------------------------------ +const char *const TypePtr::ptr_msg[TypePtr::lastPTR] = { + "TopPTR","AnyNull","Constant","NULL","NotNull","BotPTR" +}; + +#ifndef PRODUCT +void TypePtr::dump2( Dict &d, uint depth, outputStream *st ) const { + if( _ptr == Null ) st->print("NULL"); + else st->print("%s *", ptr_msg[_ptr]); + if( _offset == OffsetTop ) st->print("+top"); + else if( _offset == OffsetBot ) st->print("+bot"); + else if( _offset ) st->print("+%d", _offset); +} +#endif + +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants +bool TypePtr::singleton(void) const { + // TopPTR, Null, AnyNull, Constant are all singletons + return (_offset != OffsetBot) && !below_centerline(_ptr); +} + +bool TypePtr::empty(void) const { + return (_offset == OffsetTop) || above_centerline(_ptr); +} + +//============================================================================= +// Convenience common pre-built types. +const TypeRawPtr *TypeRawPtr::BOTTOM; +const TypeRawPtr *TypeRawPtr::NOTNULL; + +//------------------------------make------------------------------------------- +const TypeRawPtr *TypeRawPtr::make( enum PTR ptr ) { + assert( ptr != Constant, "what is the constant?" ); + assert( ptr != Null, "Use TypePtr for NULL" ); + return (TypeRawPtr*)(new TypeRawPtr(ptr,0))->hashcons(); +} + +const TypeRawPtr *TypeRawPtr::make( address bits ) { + assert( bits, "Use TypePtr for NULL" ); + return (TypeRawPtr*)(new TypeRawPtr(Constant,bits))->hashcons(); +} + +//------------------------------cast_to_ptr_type------------------------------- +const Type *TypeRawPtr::cast_to_ptr_type(PTR ptr) const { + assert( ptr != Constant, "what is the constant?" ); + assert( ptr != Null, "Use TypePtr for NULL" ); + assert( _bits==0, "Why cast a constant address?"); + if( ptr == _ptr ) return this; + return make(ptr); +} + +//------------------------------get_con---------------------------------------- +intptr_t TypeRawPtr::get_con() const { + assert( _ptr == Null || _ptr == Constant, "" ); + return (intptr_t)_bits; +} + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type object. +const Type *TypeRawPtr::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Current "this->_base" is RawPtr + switch( t->base() ) { // switch on original type + case Bottom: // Ye Olde Default + return t; + case Top: + return this; + case AnyPtr: // Meeting to AnyPtrs + break; + case RawPtr: { // might be top, bot, any/not or constant + enum PTR tptr = t->is_ptr()->ptr(); + enum PTR ptr = meet_ptr( tptr ); + if( ptr == Constant ) { // Cannot be equal constants, so... + if( tptr == Constant && _ptr != Constant) return t; + if( _ptr == Constant && tptr != Constant) return this; + ptr = NotNull; // Fall down in lattice + } + return make( ptr ); + } + + case OopPtr: + case InstPtr: + case KlassPtr: + case AryPtr: + return TypePtr::BOTTOM; // Oop meet raw is not well defined + default: // All else is a mistake + typerr(t); + } + + // Found an AnyPtr type vs self-RawPtr type + const TypePtr *tp = t->is_ptr(); + switch (tp->ptr()) { + case TypePtr::TopPTR: return this; + case TypePtr::BotPTR: return t; + case TypePtr::Null: + if( _ptr == TypePtr::TopPTR ) return t; + return TypeRawPtr::BOTTOM; + case TypePtr::NotNull: return TypePtr::make( AnyPtr, meet_ptr(TypePtr::NotNull), tp->meet_offset(0) ); + case TypePtr::AnyNull: + if( _ptr == TypePtr::Constant) return this; + return make( meet_ptr(TypePtr::AnyNull) ); + default: ShouldNotReachHere(); + } + return this; +} + +//------------------------------xdual------------------------------------------ +// Dual: compute field-by-field dual +const Type *TypeRawPtr::xdual() const { + return new TypeRawPtr( dual_ptr(), _bits ); +} + +//------------------------------add_offset------------------------------------- +const TypePtr *TypeRawPtr::add_offset( int offset ) const { + if( offset == OffsetTop ) return BOTTOM; // Undefined offset-> undefined pointer + if( offset == OffsetBot ) return BOTTOM; // Unknown offset-> unknown pointer + if( offset == 0 ) return this; // No change + switch (_ptr) { + case TypePtr::TopPTR: + case TypePtr::BotPTR: + case TypePtr::NotNull: + return this; + case TypePtr::Null: + case TypePtr::Constant: + return make( _bits+offset ); + default: ShouldNotReachHere(); + } + return NULL; // Lint noise +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeRawPtr::eq( const Type *t ) const { + const TypeRawPtr *a = (const TypeRawPtr*)t; + return _bits == a->_bits && TypePtr::eq(t); +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeRawPtr::hash(void) const { + return (intptr_t)_bits + TypePtr::hash(); +} + +//------------------------------dump2------------------------------------------ +#ifndef PRODUCT +void TypeRawPtr::dump2( Dict &d, uint depth, outputStream *st ) const { + if( _ptr == Constant ) + st->print(INTPTR_FORMAT, _bits); + else + st->print("rawptr:%s", ptr_msg[_ptr]); +} +#endif + +//============================================================================= +// Convenience common pre-built type. +const TypeOopPtr *TypeOopPtr::BOTTOM; + +//------------------------------make------------------------------------------- +const TypeOopPtr *TypeOopPtr::make(PTR ptr, + int offset) { + assert(ptr != Constant, "no constant generic pointers"); + ciKlass* k = ciKlassKlass::make(); + bool xk = false; + ciObject* o = NULL; + return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, xk, o, offset, UNKNOWN_INSTANCE))->hashcons(); +} + + +//------------------------------cast_to_ptr_type------------------------------- +const Type *TypeOopPtr::cast_to_ptr_type(PTR ptr) const { + assert(_base == OopPtr, "subclass must override cast_to_ptr_type"); + if( ptr == _ptr ) return this; + return make(ptr, _offset); +} + +//-----------------------------cast_to_instance------------------------------- +const TypeOopPtr *TypeOopPtr::cast_to_instance(int instance_id) const { + // There are no instances of a general oop. + // Return self unchanged. + return this; +} + +//-----------------------------cast_to_exactness------------------------------- +const Type *TypeOopPtr::cast_to_exactness(bool klass_is_exact) const { + // There is no such thing as an exact general oop. + // Return self unchanged. + return this; +} + + +//------------------------------as_klass_type---------------------------------- +// Return the klass type corresponding to this instance or array type. +// It is the type that is loaded from an object of this type. +const TypeKlassPtr* TypeOopPtr::as_klass_type() const { + ciKlass* k = klass(); + bool xk = klass_is_exact(); + if (k == NULL || !k->is_java_klass()) + return TypeKlassPtr::OBJECT; + else + return TypeKlassPtr::make(xk? Constant: NotNull, k, 0); +} + + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type object. +const Type *TypeOopPtr::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Current "this->_base" is OopPtr + switch (t->base()) { // switch on original type + + case Int: // Mixing ints & oops happens when javac + case Long: // reuses local variables + case FloatTop: + case FloatCon: + case FloatBot: + case DoubleTop: + case DoubleCon: + case DoubleBot: + case Bottom: // Ye Olde Default + return Type::BOTTOM; + case Top: + return this; + + default: // All else is a mistake + typerr(t); + + case RawPtr: + return TypePtr::BOTTOM; // Oop meet raw is not well defined + + case AnyPtr: { + // Found an AnyPtr type vs self-OopPtr type + const TypePtr *tp = t->is_ptr(); + int offset = meet_offset(tp->offset()); + PTR ptr = meet_ptr(tp->ptr()); + switch (tp->ptr()) { + case Null: + if (ptr == Null) return TypePtr::make(AnyPtr, ptr, offset); + // else fall through: + case TopPTR: + case AnyNull: + return make(ptr, offset); + case BotPTR: + case NotNull: + return TypePtr::make(AnyPtr, ptr, offset); + default: typerr(t); + } + } + + case OopPtr: { // Meeting to other OopPtrs + const TypeOopPtr *tp = t->is_oopptr(); + return make( meet_ptr(tp->ptr()), meet_offset(tp->offset()) ); + } + + case InstPtr: // For these, flip the call around to cut down + case KlassPtr: // on the cases I have to handle. + case AryPtr: + return t->xmeet(this); // Call in reverse direction + + } // End of switch + return this; // Return the double constant +} + + +//------------------------------xdual------------------------------------------ +// Dual of a pure heap pointer. No relevant klass or oop information. +const Type *TypeOopPtr::xdual() const { + assert(klass() == ciKlassKlass::make(), "no klasses here"); + assert(const_oop() == NULL, "no constants here"); + return new TypeOopPtr(_base, dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance() ); +} + +//--------------------------make_from_klass_common----------------------------- +// Computes the element-type given a klass. +const TypeOopPtr* TypeOopPtr::make_from_klass_common(ciKlass *klass, bool klass_change, bool try_for_exact) { + assert(klass->is_java_klass(), "must be java language klass"); + if (klass->is_instance_klass()) { + Compile* C = Compile::current(); + Dependencies* deps = C->dependencies(); + assert((deps != NULL) == (C->method() != NULL && C->method()->code_size() > 0), "sanity"); + // Element is an instance + bool klass_is_exact = false; + if (klass->is_loaded()) { + // Try to set klass_is_exact. + ciInstanceKlass* ik = klass->as_instance_klass(); + klass_is_exact = ik->is_final(); + if (!klass_is_exact && klass_change + && deps != NULL && UseUniqueSubclasses) { + ciInstanceKlass* sub = ik->unique_concrete_subklass(); + if (sub != NULL) { + deps->assert_abstract_with_unique_concrete_subtype(ik, sub); + klass = ik = sub; + klass_is_exact = sub->is_final(); + } + } + if (!klass_is_exact && try_for_exact + && deps != NULL && UseExactTypes) { + if (!ik->is_interface() && !ik->has_subklass()) { + // Add a dependence; if concrete subclass added we need to recompile + deps->assert_leaf_type(ik); + klass_is_exact = true; + } + } + } + return TypeInstPtr::make(TypePtr::BotPTR, klass, klass_is_exact, NULL, 0); + } else if (klass->is_obj_array_klass()) { + // Element is an object array. Recursively call ourself. + const TypeOopPtr *etype = TypeOopPtr::make_from_klass_common(klass->as_obj_array_klass()->element_klass(), false, try_for_exact); + bool xk = etype->klass_is_exact(); + const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS); + // We used to pass NotNull in here, asserting that the sub-arrays + // are all not-null. This is not true in generally, as code can + // slam NULLs down in the subarrays. + const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::BotPTR, arr0, klass, xk, 0); + return arr; + } else if (klass->is_type_array_klass()) { + // Element is an typeArray + const Type* etype = get_const_basic_type(klass->as_type_array_klass()->element_type()); + const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS); + // We used to pass NotNull in here, asserting that the array pointer + // is not-null. That was not true in general. + const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::BotPTR, arr0, klass, true, 0); + return arr; + } else { + ShouldNotReachHere(); + return NULL; + } +} + +//------------------------------make_from_constant----------------------------- +// Make a java pointer from an oop constant +const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o) { + if (o->is_method_data() || o->is_method()) { + // Treat much like a typeArray of bytes, like below, but fake the type... + assert(o->has_encoding(), "must be a perm space object"); + const Type* etype = (Type*)get_const_basic_type(T_BYTE); + const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS); + ciKlass *klass = ciTypeArrayKlass::make((BasicType) T_BYTE); + assert(o->has_encoding(), "method data oops should be tenured"); + const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0); + return arr; + } else { + assert(o->is_java_object(), "must be java language object"); + assert(!o->is_null_object(), "null object not yet handled here."); + ciKlass *klass = o->klass(); + if (klass->is_instance_klass()) { + // Element is an instance + if (!o->has_encoding()) { // not a perm-space constant + // %%% remove this restriction by rewriting non-perm ConPNodes in a later phase + return TypeInstPtr::make(TypePtr::NotNull, klass, true, NULL, 0); + } + return TypeInstPtr::make(o); + } else if (klass->is_obj_array_klass()) { + // Element is an object array. Recursively call ourself. + const Type *etype = + TypeOopPtr::make_from_klass_raw(klass->as_obj_array_klass()->element_klass()); + const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length())); + // We used to pass NotNull in here, asserting that the sub-arrays + // are all not-null. This is not true in generally, as code can + // slam NULLs down in the subarrays. + if (!o->has_encoding()) { // not a perm-space constant + // %%% remove this restriction by rewriting non-perm ConPNodes in a later phase + return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, 0); + } + const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0); + return arr; + } else if (klass->is_type_array_klass()) { + // Element is an typeArray + const Type* etype = + (Type*)get_const_basic_type(klass->as_type_array_klass()->element_type()); + const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length())); + // We used to pass NotNull in here, asserting that the array pointer + // is not-null. That was not true in general. + if (!o->has_encoding()) { // not a perm-space constant + // %%% remove this restriction by rewriting non-perm ConPNodes in a later phase + return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, 0); + } + const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0); + return arr; + } + } + + ShouldNotReachHere(); + return NULL; +} + +//------------------------------get_con---------------------------------------- +intptr_t TypeOopPtr::get_con() const { + assert( _ptr == Null || _ptr == Constant, "" ); + assert( _offset >= 0, "" ); + + if (_offset != 0) { + // After being ported to the compiler interface, the compiler no longer + // directly manipulates the addresses of oops. Rather, it only has a pointer + // to a handle at compile time. This handle is embedded in the generated + // code and dereferenced at the time the nmethod is made. Until that time, + // it is not reasonable to do arithmetic with the addresses of oops (we don't + // have access to the addresses!). This does not seem to currently happen, + // but this assertion here is to help prevent its occurrance. + tty->print_cr("Found oop constant with non-zero offset"); + ShouldNotReachHere(); + } + + return (intptr_t)const_oop()->encoding(); +} + + +//-----------------------------filter------------------------------------------ +// Do not allow interface-vs.-noninterface joins to collapse to top. +const Type *TypeOopPtr::filter( const Type *kills ) const { + + const Type* ft = join(kills); + const TypeInstPtr* ftip = ft->isa_instptr(); + const TypeInstPtr* ktip = kills->isa_instptr(); + + if (ft->empty()) { + // Check for evil case of 'this' being a class and 'kills' expecting an + // interface. This can happen because the bytecodes do not contain + // enough type info to distinguish a Java-level interface variable + // from a Java-level object variable. If we meet 2 classes which + // both implement interface I, but their meet is at 'j/l/O' which + // doesn't implement I, we have no way to tell if the result should + // be 'I' or 'j/l/O'. Thus we'll pick 'j/l/O'. If this then flows + // into a Phi which "knows" it's an Interface type we'll have to + // uplift the type. + if (!empty() && ktip != NULL && ktip->is_loaded() && ktip->klass()->is_interface()) + return kills; // Uplift to interface + + return Type::TOP; // Canonical empty value + } + + // If we have an interface-typed Phi or cast and we narrow to a class type, + // the join should report back the class. However, if we have a J/L/Object + // class-typed Phi and an interface flows in, it's possible that the meet & + // join report an interface back out. This isn't possible but happens + // because the type system doesn't interact well with interfaces. + if (ftip != NULL && ktip != NULL && + ftip->is_loaded() && ftip->klass()->is_interface() && + ktip->is_loaded() && !ktip->klass()->is_interface()) { + // Happens in a CTW of rt.jar, 320-341, no extra flags + return ktip->cast_to_ptr_type(ftip->ptr()); + } + + return ft; +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeOopPtr::eq( const Type *t ) const { + const TypeOopPtr *a = (const TypeOopPtr*)t; + if (_klass_is_exact != a->_klass_is_exact || + _instance_id != a->_instance_id) return false; + ciObject* one = const_oop(); + ciObject* two = a->const_oop(); + if (one == NULL || two == NULL) { + return (one == two) && TypePtr::eq(t); + } else { + return one->equals(two) && TypePtr::eq(t); + } +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeOopPtr::hash(void) const { + return + (const_oop() ? const_oop()->hash() : 0) + + _klass_is_exact + + _instance_id + + TypePtr::hash(); +} + +//------------------------------dump2------------------------------------------ +#ifndef PRODUCT +void TypeOopPtr::dump2( Dict &d, uint depth, outputStream *st ) const { + st->print("oopptr:%s", ptr_msg[_ptr]); + if( _klass_is_exact ) st->print(":exact"); + if( const_oop() ) st->print(INTPTR_FORMAT, const_oop()); + switch( _offset ) { + case OffsetTop: st->print("+top"); break; + case OffsetBot: st->print("+any"); break; + case 0: break; + default: st->print("+%d",_offset); break; + } + if (_instance_id != UNKNOWN_INSTANCE) + st->print(",iid=%d",_instance_id); +} +#endif + +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants +bool TypeOopPtr::singleton(void) const { + // detune optimizer to not generate constant oop + constant offset as a constant! + // TopPTR, Null, AnyNull, Constant are all singletons + return (_offset == 0) && !below_centerline(_ptr); +} + +//------------------------------xadd_offset------------------------------------ +int TypeOopPtr::xadd_offset( int offset ) const { + // Adding to 'TOP' offset? Return 'TOP'! + if( _offset == OffsetTop || offset == OffsetTop ) return OffsetTop; + // Adding to 'BOTTOM' offset? Return 'BOTTOM'! + if( _offset == OffsetBot || offset == OffsetBot ) return OffsetBot; + + // assert( _offset >= 0 && _offset+offset >= 0, "" ); + // It is possible to construct a negative offset during PhaseCCP + + return _offset+offset; // Sum valid offsets +} + +//------------------------------add_offset------------------------------------- +const TypePtr *TypeOopPtr::add_offset( int offset ) const { + return make( _ptr, xadd_offset(offset) ); +} + +int TypeOopPtr::meet_instance(int iid) const { + if (iid == 0) { + return (_instance_id < 0) ? _instance_id : UNKNOWN_INSTANCE; + } else if (_instance_id == UNKNOWN_INSTANCE) { + return (iid < 0) ? iid : UNKNOWN_INSTANCE; + } else { + return (_instance_id == iid) ? iid : UNKNOWN_INSTANCE; + } +} + +//============================================================================= +// Convenience common pre-built types. +const TypeInstPtr *TypeInstPtr::NOTNULL; +const TypeInstPtr *TypeInstPtr::BOTTOM; +const TypeInstPtr *TypeInstPtr::MIRROR; +const TypeInstPtr *TypeInstPtr::MARK; +const TypeInstPtr *TypeInstPtr::KLASS; + +//------------------------------TypeInstPtr------------------------------------- +TypeInstPtr::TypeInstPtr(PTR ptr, ciKlass* k, bool xk, ciObject* o, int off, int instance_id) + : TypeOopPtr(InstPtr, ptr, k, xk, o, off, instance_id), _name(k->name()) { + assert(k != NULL && + (k->is_loaded() || o == NULL), + "cannot have constants with non-loaded klass"); +}; + +//------------------------------make------------------------------------------- +const TypeInstPtr *TypeInstPtr::make(PTR ptr, + ciKlass* k, + bool xk, + ciObject* o, + int offset, + int instance_id) { + assert( !k->is_loaded() || k->is_instance_klass() || + k->is_method_klass(), "Must be for instance or method"); + // Either const_oop() is NULL or else ptr is Constant + assert( (!o && ptr != Constant) || (o && ptr == Constant), + "constant pointers must have a value supplied" ); + // Ptr is never Null + assert( ptr != Null, "NULL pointers are not typed" ); + + if (instance_id != UNKNOWN_INSTANCE) + xk = true; // instances are always exactly typed + if (!UseExactTypes) xk = false; + if (ptr == Constant) { + // Note: This case includes meta-object constants, such as methods. + xk = true; + } else if (k->is_loaded()) { + ciInstanceKlass* ik = k->as_instance_klass(); + if (!xk && ik->is_final()) xk = true; // no inexact final klass + if (xk && ik->is_interface()) xk = false; // no exact interface + } + + // Now hash this baby + TypeInstPtr *result = + (TypeInstPtr*)(new TypeInstPtr(ptr, k, xk, o ,offset, instance_id))->hashcons(); + + return result; +} + + +//------------------------------cast_to_ptr_type------------------------------- +const Type *TypeInstPtr::cast_to_ptr_type(PTR ptr) const { + if( ptr == _ptr ) return this; + // Reconstruct _sig info here since not a problem with later lazy + // construction, _sig will show up on demand. + return make(ptr, klass(), klass_is_exact(), const_oop(), _offset); +} + + +//-----------------------------cast_to_exactness------------------------------- +const Type *TypeInstPtr::cast_to_exactness(bool klass_is_exact) const { + if( klass_is_exact == _klass_is_exact ) return this; + if (!UseExactTypes) return this; + if (!_klass->is_loaded()) return this; + ciInstanceKlass* ik = _klass->as_instance_klass(); + if( (ik->is_final() || _const_oop) ) return this; // cannot clear xk + if( ik->is_interface() ) return this; // cannot set xk + return make(ptr(), klass(), klass_is_exact, const_oop(), _offset, _instance_id); +} + +//-----------------------------cast_to_instance------------------------------- +const TypeOopPtr *TypeInstPtr::cast_to_instance(int instance_id) const { + if( instance_id == _instance_id) return this; + bool exact = (instance_id == UNKNOWN_INSTANCE) ? _klass_is_exact : true; + + return make(ptr(), klass(), exact, const_oop(), _offset, instance_id); +} + +//------------------------------xmeet_unloaded--------------------------------- +// Compute the MEET of two InstPtrs when at least one is unloaded. +// Assume classes are different since called after check for same name/class-loader +const TypeInstPtr *TypeInstPtr::xmeet_unloaded(const TypeInstPtr *tinst) const { + int off = meet_offset(tinst->offset()); + PTR ptr = meet_ptr(tinst->ptr()); + + const TypeInstPtr *loaded = is_loaded() ? this : tinst; + const TypeInstPtr *unloaded = is_loaded() ? tinst : this; + if( loaded->klass()->equals(ciEnv::current()->Object_klass()) ) { + // + // Meet unloaded class with java/lang/Object + // + // Meet + // | Unloaded Class + // Object | TOP | AnyNull | Constant | NotNull | BOTTOM | + // =================================================================== + // TOP | ..........................Unloaded......................| + // AnyNull | U-AN |................Unloaded......................| + // Constant | ... O-NN .................................. | O-BOT | + // NotNull | ... O-NN .................................. | O-BOT | + // BOTTOM | ........................Object-BOTTOM ..................| + // + assert(loaded->ptr() != TypePtr::Null, "insanity check"); + // + if( loaded->ptr() == TypePtr::TopPTR ) { return unloaded; } + else if (loaded->ptr() == TypePtr::AnyNull) { return TypeInstPtr::make( ptr, unloaded->klass() ); } + else if (loaded->ptr() == TypePtr::BotPTR ) { return TypeInstPtr::BOTTOM; } + else if (loaded->ptr() == TypePtr::Constant || loaded->ptr() == TypePtr::NotNull) { + if (unloaded->ptr() == TypePtr::BotPTR ) { return TypeInstPtr::BOTTOM; } + else { return TypeInstPtr::NOTNULL; } + } + else if( unloaded->ptr() == TypePtr::TopPTR ) { return unloaded; } + + return unloaded->cast_to_ptr_type(TypePtr::AnyNull)->is_instptr(); + } + + // Both are unloaded, not the same class, not Object + // Or meet unloaded with a different loaded class, not java/lang/Object + if( ptr != TypePtr::BotPTR ) { + return TypeInstPtr::NOTNULL; + } + return TypeInstPtr::BOTTOM; +} + + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type object. +const Type *TypeInstPtr::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Current "this->_base" is Pointer + switch (t->base()) { // switch on original type + + case Int: // Mixing ints & oops happens when javac + case Long: // reuses local variables + case FloatTop: + case FloatCon: + case FloatBot: + case DoubleTop: + case DoubleCon: + case DoubleBot: + case Bottom: // Ye Olde Default + return Type::BOTTOM; + case Top: + return this; + + default: // All else is a mistake + typerr(t); + + case RawPtr: return TypePtr::BOTTOM; + + case AryPtr: { // All arrays inherit from Object class + const TypeAryPtr *tp = t->is_aryptr(); + int offset = meet_offset(tp->offset()); + PTR ptr = meet_ptr(tp->ptr()); + int iid = meet_instance(tp->instance_id()); + switch (ptr) { + case TopPTR: + case AnyNull: // Fall 'down' to dual of object klass + if (klass()->equals(ciEnv::current()->Object_klass())) { + return TypeAryPtr::make(ptr, tp->ary(), tp->klass(), tp->klass_is_exact(), offset, iid); + } else { + // cannot subclass, so the meet has to fall badly below the centerline + ptr = NotNull; + return TypeInstPtr::make( ptr, ciEnv::current()->Object_klass(), false, NULL, offset, iid); + } + case Constant: + case NotNull: + case BotPTR: // Fall down to object klass + // LCA is object_klass, but if we subclass from the top we can do better + if( above_centerline(_ptr) ) { // if( _ptr == TopPTR || _ptr == AnyNull ) + // If 'this' (InstPtr) is above the centerline and it is Object class + // then we can subclass in the Java class heirarchy. + if (klass()->equals(ciEnv::current()->Object_klass())) { + // that is, tp's array type is a subtype of my klass + return TypeAryPtr::make(ptr, tp->ary(), tp->klass(), tp->klass_is_exact(), offset, iid); + } + } + // The other case cannot happen, since I cannot be a subtype of an array. + // The meet falls down to Object class below centerline. + if( ptr == Constant ) + ptr = NotNull; + return make( ptr, ciEnv::current()->Object_klass(), false, NULL, offset, iid ); + default: typerr(t); + } + } + + case OopPtr: { // Meeting to OopPtrs + // Found a OopPtr type vs self-InstPtr type + const TypePtr *tp = t->is_oopptr(); + int offset = meet_offset(tp->offset()); + PTR ptr = meet_ptr(tp->ptr()); + switch (tp->ptr()) { + case TopPTR: + case AnyNull: + return make(ptr, klass(), klass_is_exact(), + (ptr == Constant ? const_oop() : NULL), offset); + case NotNull: + case BotPTR: + return TypeOopPtr::make(ptr, offset); + default: typerr(t); + } + } + + case AnyPtr: { // Meeting to AnyPtrs + // Found an AnyPtr type vs self-InstPtr type + const TypePtr *tp = t->is_ptr(); + int offset = meet_offset(tp->offset()); + PTR ptr = meet_ptr(tp->ptr()); + switch (tp->ptr()) { + case Null: + if( ptr == Null ) return TypePtr::make( AnyPtr, ptr, offset ); + case TopPTR: + case AnyNull: + return make( ptr, klass(), klass_is_exact(), + (ptr == Constant ? const_oop() : NULL), offset ); + case NotNull: + case BotPTR: + return TypePtr::make( AnyPtr, ptr, offset ); + default: typerr(t); + } + } + + /* + A-top } + / | \ } Tops + B-top A-any C-top } + | / | \ | } Any-nulls + B-any | C-any } + | | | + B-con A-con C-con } constants; not comparable across classes + | | | + B-not | C-not } + | \ | / | } not-nulls + B-bot A-not C-bot } + \ | / } Bottoms + A-bot } + */ + + case InstPtr: { // Meeting 2 Oops? + // Found an InstPtr sub-type vs self-InstPtr type + const TypeInstPtr *tinst = t->is_instptr(); + int off = meet_offset( tinst->offset() ); + PTR ptr = meet_ptr( tinst->ptr() ); + int instance_id = meet_instance(tinst->instance_id()); + + // Check for easy case; klasses are equal (and perhaps not loaded!) + // If we have constants, then we created oops so classes are loaded + // and we can handle the constants further down. This case handles + // both-not-loaded or both-loaded classes + if (ptr != Constant && klass()->equals(tinst->klass()) && klass_is_exact() == tinst->klass_is_exact()) { + return make( ptr, klass(), klass_is_exact(), NULL, off, instance_id ); + } + + // Classes require inspection in the Java klass hierarchy. Must be loaded. + ciKlass* tinst_klass = tinst->klass(); + ciKlass* this_klass = this->klass(); + bool tinst_xk = tinst->klass_is_exact(); + bool this_xk = this->klass_is_exact(); + if (!tinst_klass->is_loaded() || !this_klass->is_loaded() ) { + // One of these classes has not been loaded + const TypeInstPtr *unloaded_meet = xmeet_unloaded(tinst); +#ifndef PRODUCT + if( PrintOpto && Verbose ) { + tty->print("meet of unloaded classes resulted in: "); unloaded_meet->dump(); tty->cr(); + tty->print(" this == "); this->dump(); tty->cr(); + tty->print(" tinst == "); tinst->dump(); tty->cr(); + } +#endif + return unloaded_meet; + } + + // Handle mixing oops and interfaces first. + if( this_klass->is_interface() && !tinst_klass->is_interface() ) { + ciKlass *tmp = tinst_klass; // Swap interface around + tinst_klass = this_klass; + this_klass = tmp; + bool tmp2 = tinst_xk; + tinst_xk = this_xk; + this_xk = tmp2; + } + if (tinst_klass->is_interface() && + !(this_klass->is_interface() || + // Treat java/lang/Object as an honorary interface, + // because we need a bottom for the interface hierarchy. + this_klass == ciEnv::current()->Object_klass())) { + // Oop meets interface! + + // See if the oop subtypes (implements) interface. + ciKlass *k; + bool xk; + if( this_klass->is_subtype_of( tinst_klass ) ) { + // Oop indeed subtypes. Now keep oop or interface depending + // on whether we are both above the centerline or either is + // below the centerline. If we are on the centerline + // (e.g., Constant vs. AnyNull interface), use the constant. + k = below_centerline(ptr) ? tinst_klass : this_klass; + // If we are keeping this_klass, keep its exactness too. + xk = below_centerline(ptr) ? tinst_xk : this_xk; + } else { // Does not implement, fall to Object + // Oop does not implement interface, so mixing falls to Object + // just like the verifier does (if both are above the + // centerline fall to interface) + k = above_centerline(ptr) ? tinst_klass : ciEnv::current()->Object_klass(); + xk = above_centerline(ptr) ? tinst_xk : false; + // Watch out for Constant vs. AnyNull interface. + if (ptr == Constant) ptr = NotNull; // forget it was a constant + } + ciObject* o = NULL; // the Constant value, if any + if (ptr == Constant) { + // Find out which constant. + o = (this_klass == klass()) ? const_oop() : tinst->const_oop(); + } + return make( ptr, k, xk, o, off ); + } + + // Either oop vs oop or interface vs interface or interface vs Object + + // !!! Here's how the symmetry requirement breaks down into invariants: + // If we split one up & one down AND they subtype, take the down man. + // If we split one up & one down AND they do NOT subtype, "fall hard". + // If both are up and they subtype, take the subtype class. + // If both are up and they do NOT subtype, "fall hard". + // If both are down and they subtype, take the supertype class. + // If both are down and they do NOT subtype, "fall hard". + // Constants treated as down. + + // Now, reorder the above list; observe that both-down+subtype is also + // "fall hard"; "fall hard" becomes the default case: + // If we split one up & one down AND they subtype, take the down man. + // If both are up and they subtype, take the subtype class. + + // If both are down and they subtype, "fall hard". + // If both are down and they do NOT subtype, "fall hard". + // If both are up and they do NOT subtype, "fall hard". + // If we split one up & one down AND they do NOT subtype, "fall hard". + + // If a proper subtype is exact, and we return it, we return it exactly. + // If a proper supertype is exact, there can be no subtyping relationship! + // If both types are equal to the subtype, exactness is and-ed below the + // centerline and or-ed above it. (N.B. Constants are always exact.) + + // Check for subtyping: + ciKlass *subtype = NULL; + bool subtype_exact = false; + if( tinst_klass->equals(this_klass) ) { + subtype = this_klass; + subtype_exact = below_centerline(ptr) ? (this_xk & tinst_xk) : (this_xk | tinst_xk); + } else if( !tinst_xk && this_klass->is_subtype_of( tinst_klass ) ) { + subtype = this_klass; // Pick subtyping class + subtype_exact = this_xk; + } else if( !this_xk && tinst_klass->is_subtype_of( this_klass ) ) { + subtype = tinst_klass; // Pick subtyping class + subtype_exact = tinst_xk; + } + + if( subtype ) { + if( above_centerline(ptr) ) { // both are up? + this_klass = tinst_klass = subtype; + this_xk = tinst_xk = subtype_exact; + } else if( above_centerline(this ->_ptr) && !above_centerline(tinst->_ptr) ) { + this_klass = tinst_klass; // tinst is down; keep down man + this_xk = tinst_xk; + } else if( above_centerline(tinst->_ptr) && !above_centerline(this ->_ptr) ) { + tinst_klass = this_klass; // this is down; keep down man + tinst_xk = this_xk; + } else { + this_xk = subtype_exact; // either they are equal, or we'll do an LCA + } + } + + // Check for classes now being equal + if (tinst_klass->equals(this_klass)) { + // If the klasses are equal, the constants may still differ. Fall to + // NotNull if they do (neither constant is NULL; that is a special case + // handled elsewhere). + ciObject* o = NULL; // Assume not constant when done + ciObject* this_oop = const_oop(); + ciObject* tinst_oop = tinst->const_oop(); + if( ptr == Constant ) { + if (this_oop != NULL && tinst_oop != NULL && + this_oop->equals(tinst_oop) ) + o = this_oop; + else if (above_centerline(this ->_ptr)) + o = tinst_oop; + else if (above_centerline(tinst ->_ptr)) + o = this_oop; + else + ptr = NotNull; + } + return make( ptr, this_klass, this_xk, o, off, instance_id ); + } // Else classes are not equal + + // Since klasses are different, we require a LCA in the Java + // class hierarchy - which means we have to fall to at least NotNull. + if( ptr == TopPTR || ptr == AnyNull || ptr == Constant ) + ptr = NotNull; + + // Now we find the LCA of Java classes + ciKlass* k = this_klass->least_common_ancestor(tinst_klass); + return make( ptr, k, false, NULL, off ); + } // End of case InstPtr + + case KlassPtr: + return TypeInstPtr::BOTTOM; + + } // End of switch + return this; // Return the double constant +} + + +//------------------------java_mirror_type-------------------------------------- +ciType* TypeInstPtr::java_mirror_type() const { + // must be a singleton type + if( const_oop() == NULL ) return NULL; + + // must be of type java.lang.Class + if( klass() != ciEnv::current()->Class_klass() ) return NULL; + + return const_oop()->as_instance()->java_mirror_type(); +} + + +//------------------------------xdual------------------------------------------ +// Dual: do NOT dual on klasses. This means I do NOT understand the Java +// inheritence mechanism. +const Type *TypeInstPtr::xdual() const { + return new TypeInstPtr( dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance() ); +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeInstPtr::eq( const Type *t ) const { + const TypeInstPtr *p = t->is_instptr(); + return + klass()->equals(p->klass()) && + TypeOopPtr::eq(p); // Check sub-type stuff +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeInstPtr::hash(void) const { + int hash = klass()->hash() + TypeOopPtr::hash(); + return hash; +} + +//------------------------------dump2------------------------------------------ +// Dump oop Type +#ifndef PRODUCT +void TypeInstPtr::dump2( Dict &d, uint depth, outputStream *st ) const { + // Print the name of the klass. + klass()->print_name_on(st); + + switch( _ptr ) { + case Constant: + // TO DO: Make CI print the hex address of the underlying oop. + if (WizardMode || Verbose) { + const_oop()->print_oop(st); + } + case BotPTR: + if (!WizardMode && !Verbose) { + if( _klass_is_exact ) st->print(":exact"); + break; + } + case TopPTR: + case AnyNull: + case NotNull: + st->print(":%s", ptr_msg[_ptr]); + if( _klass_is_exact ) st->print(":exact"); + break; + } + + if( _offset ) { // Dump offset, if any + if( _offset == OffsetBot ) st->print("+any"); + else if( _offset == OffsetTop ) st->print("+unknown"); + else st->print("+%d", _offset); + } + + st->print(" *"); + if (_instance_id != UNKNOWN_INSTANCE) + st->print(",iid=%d",_instance_id); +} +#endif + +//------------------------------add_offset------------------------------------- +const TypePtr *TypeInstPtr::add_offset( int offset ) const { + return make( _ptr, klass(), klass_is_exact(), const_oop(), xadd_offset(offset), _instance_id ); +} + +//============================================================================= +// Convenience common pre-built types. +const TypeAryPtr *TypeAryPtr::RANGE; +const TypeAryPtr *TypeAryPtr::OOPS; +const TypeAryPtr *TypeAryPtr::BYTES; +const TypeAryPtr *TypeAryPtr::SHORTS; +const TypeAryPtr *TypeAryPtr::CHARS; +const TypeAryPtr *TypeAryPtr::INTS; +const TypeAryPtr *TypeAryPtr::LONGS; +const TypeAryPtr *TypeAryPtr::FLOATS; +const TypeAryPtr *TypeAryPtr::DOUBLES; + +//------------------------------make------------------------------------------- +const TypeAryPtr *TypeAryPtr::make( PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id ) { + assert(!(k == NULL && ary->_elem->isa_int()), + "integral arrays must be pre-equipped with a class"); + if (!xk) xk = ary->ary_must_be_exact(); + if (instance_id != UNKNOWN_INSTANCE) + xk = true; // instances are always exactly typed + if (!UseExactTypes) xk = (ptr == Constant); + return (TypeAryPtr*)(new TypeAryPtr(ptr, NULL, ary, k, xk, offset, instance_id))->hashcons(); +} + +//------------------------------make------------------------------------------- +const TypeAryPtr *TypeAryPtr::make( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id ) { + assert(!(k == NULL && ary->_elem->isa_int()), + "integral arrays must be pre-equipped with a class"); + assert( (ptr==Constant && o) || (ptr!=Constant && !o), "" ); + if (!xk) xk = (o != NULL) || ary->ary_must_be_exact(); + if (instance_id != UNKNOWN_INSTANCE) + xk = true; // instances are always exactly typed + if (!UseExactTypes) xk = (ptr == Constant); + return (TypeAryPtr*)(new TypeAryPtr(ptr, o, ary, k, xk, offset, instance_id))->hashcons(); +} + +//------------------------------cast_to_ptr_type------------------------------- +const Type *TypeAryPtr::cast_to_ptr_type(PTR ptr) const { + if( ptr == _ptr ) return this; + return make(ptr, const_oop(), _ary, klass(), klass_is_exact(), _offset); +} + + +//-----------------------------cast_to_exactness------------------------------- +const Type *TypeAryPtr::cast_to_exactness(bool klass_is_exact) const { + if( klass_is_exact == _klass_is_exact ) return this; + if (!UseExactTypes) return this; + if (_ary->ary_must_be_exact()) return this; // cannot clear xk + return make(ptr(), const_oop(), _ary, klass(), klass_is_exact, _offset, _instance_id); +} + +//-----------------------------cast_to_instance------------------------------- +const TypeOopPtr *TypeAryPtr::cast_to_instance(int instance_id) const { + if( instance_id == _instance_id) return this; + bool exact = (instance_id == UNKNOWN_INSTANCE) ? _klass_is_exact : true; + return make(ptr(), const_oop(), _ary, klass(), exact, _offset, instance_id); +} + +//-----------------------------narrow_size_type------------------------------- +// Local cache for arrayOopDesc::max_array_length(etype), +// which is kind of slow (and cached elsewhere by other users). +static jint max_array_length_cache[T_CONFLICT+1]; +static jint max_array_length(BasicType etype) { + jint& cache = max_array_length_cache[etype]; + jint res = cache; + if (res == 0) { + switch (etype) { + case T_CONFLICT: + case T_ILLEGAL: + case T_VOID: + etype = T_BYTE; // will produce conservatively high value + } + cache = res = arrayOopDesc::max_array_length(etype); + } + return res; +} + +// Narrow the given size type to the index range for the given array base type. +// Return NULL if the resulting int type becomes empty. +const TypeInt* TypeAryPtr::narrow_size_type(const TypeInt* size, BasicType elem) { + jint hi = size->_hi; + jint lo = size->_lo; + jint min_lo = 0; + jint max_hi = max_array_length(elem); + //if (index_not_size) --max_hi; // type of a valid array index, FTR + bool chg = false; + if (lo < min_lo) { lo = min_lo; chg = true; } + if (hi > max_hi) { hi = max_hi; chg = true; } + if (lo > hi) + return NULL; + if (!chg) + return size; + return TypeInt::make(lo, hi, Type::WidenMin); +} + +//-------------------------------cast_to_size---------------------------------- +const TypeAryPtr* TypeAryPtr::cast_to_size(const TypeInt* new_size) const { + assert(new_size != NULL, ""); + new_size = narrow_size_type(new_size, elem()->basic_type()); + if (new_size == NULL) // Negative length arrays will produce weird + new_size = TypeInt::ZERO; // intermediate dead fast-path goo + if (new_size == size()) return this; + const TypeAry* new_ary = TypeAry::make(elem(), new_size); + return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset); +} + + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeAryPtr::eq( const Type *t ) const { + const TypeAryPtr *p = t->is_aryptr(); + return + _ary == p->_ary && // Check array + TypeOopPtr::eq(p); // Check sub-parts +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeAryPtr::hash(void) const { + return (intptr_t)_ary + TypeOopPtr::hash(); +} + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type object. +const Type *TypeAryPtr::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + // Current "this->_base" is Pointer + switch (t->base()) { // switch on original type + + // Mixing ints & oops happens when javac reuses local variables + case Int: + case Long: + case FloatTop: + case FloatCon: + case FloatBot: + case DoubleTop: + case DoubleCon: + case DoubleBot: + case Bottom: // Ye Olde Default + return Type::BOTTOM; + case Top: + return this; + + default: // All else is a mistake + typerr(t); + + case OopPtr: { // Meeting to OopPtrs + // Found a OopPtr type vs self-AryPtr type + const TypePtr *tp = t->is_oopptr(); + int offset = meet_offset(tp->offset()); + PTR ptr = meet_ptr(tp->ptr()); + switch (tp->ptr()) { + case TopPTR: + case AnyNull: + return make(ptr, (ptr == Constant ? const_oop() : NULL), _ary, _klass, _klass_is_exact, offset); + case BotPTR: + case NotNull: + return TypeOopPtr::make(ptr, offset); + default: ShouldNotReachHere(); + } + } + + case AnyPtr: { // Meeting two AnyPtrs + // Found an AnyPtr type vs self-AryPtr type + const TypePtr *tp = t->is_ptr(); + int offset = meet_offset(tp->offset()); + PTR ptr = meet_ptr(tp->ptr()); + switch (tp->ptr()) { + case TopPTR: + return this; + case BotPTR: + case NotNull: + return TypePtr::make(AnyPtr, ptr, offset); + case Null: + if( ptr == Null ) return TypePtr::make(AnyPtr, ptr, offset); + case AnyNull: + return make( ptr, (ptr == Constant ? const_oop() : NULL), _ary, _klass, _klass_is_exact, offset ); + default: ShouldNotReachHere(); + } + } + + case RawPtr: return TypePtr::BOTTOM; + + case AryPtr: { // Meeting 2 references? + const TypeAryPtr *tap = t->is_aryptr(); + int off = meet_offset(tap->offset()); + const TypeAry *tary = _ary->meet(tap->_ary)->is_ary(); + PTR ptr = meet_ptr(tap->ptr()); + int iid = meet_instance(tap->instance_id()); + ciKlass* lazy_klass = NULL; + if (tary->_elem->isa_int()) { + // Integral array element types have irrelevant lattice relations. + // It is the klass that determines array layout, not the element type. + if (_klass == NULL) + lazy_klass = tap->_klass; + else if (tap->_klass == NULL || tap->_klass == _klass) { + lazy_klass = _klass; + } else { + // Something like byte[int+] meets char[int+]. + // This must fall to bottom, not (int[-128..65535])[int+]. + tary = TypeAry::make(Type::BOTTOM, tary->_size); + } + } + bool xk; + switch (tap->ptr()) { + case AnyNull: + case TopPTR: + // Compute new klass on demand, do not use tap->_klass + xk = (tap->_klass_is_exact | this->_klass_is_exact); + return make( ptr, const_oop(), tary, lazy_klass, xk, off ); + case Constant: { + ciObject* o = const_oop(); + if( _ptr == Constant ) { + if( tap->const_oop() != NULL && !o->equals(tap->const_oop()) ) { + ptr = NotNull; + o = NULL; + } + } else if( above_centerline(_ptr) ) { + o = tap->const_oop(); + } + xk = true; + return TypeAryPtr::make( ptr, o, tary, tap->_klass, xk, off ); + } + case NotNull: + case BotPTR: + // Compute new klass on demand, do not use tap->_klass + if (above_centerline(this->_ptr)) + xk = tap->_klass_is_exact; + else if (above_centerline(tap->_ptr)) + xk = this->_klass_is_exact; + else xk = (tap->_klass_is_exact & this->_klass_is_exact) && + (klass() == tap->klass()); // Only precise for identical arrays + return TypeAryPtr::make( ptr, NULL, tary, lazy_klass, xk, off, iid ); + default: ShouldNotReachHere(); + } + } + + // All arrays inherit from Object class + case InstPtr: { + const TypeInstPtr *tp = t->is_instptr(); + int offset = meet_offset(tp->offset()); + PTR ptr = meet_ptr(tp->ptr()); + int iid = meet_instance(tp->instance_id()); + switch (ptr) { + case TopPTR: + case AnyNull: // Fall 'down' to dual of object klass + if( tp->klass()->equals(ciEnv::current()->Object_klass()) ) { + return TypeAryPtr::make( ptr, _ary, _klass, _klass_is_exact, offset, iid ); + } else { + // cannot subclass, so the meet has to fall badly below the centerline + ptr = NotNull; + return TypeInstPtr::make( ptr, ciEnv::current()->Object_klass(), false, NULL,offset, iid); + } + case Constant: + case NotNull: + case BotPTR: // Fall down to object klass + // LCA is object_klass, but if we subclass from the top we can do better + if (above_centerline(tp->ptr())) { + // If 'tp' is above the centerline and it is Object class + // then we can subclass in the Java class heirarchy. + if( tp->klass()->equals(ciEnv::current()->Object_klass()) ) { + // that is, my array type is a subtype of 'tp' klass + return make( ptr, _ary, _klass, _klass_is_exact, offset, iid ); + } + } + // The other case cannot happen, since t cannot be a subtype of an array. + // The meet falls down to Object class below centerline. + if( ptr == Constant ) + ptr = NotNull; + return TypeInstPtr::make( ptr, ciEnv::current()->Object_klass(), false, NULL,offset, iid); + default: typerr(t); + } + } + + case KlassPtr: + return TypeInstPtr::BOTTOM; + + } + return this; // Lint noise +} + +//------------------------------xdual------------------------------------------ +// Dual: compute field-by-field dual +const Type *TypeAryPtr::xdual() const { + return new TypeAryPtr( dual_ptr(), _const_oop, _ary->dual()->is_ary(),_klass, _klass_is_exact, dual_offset(), dual_instance() ); +} + +//------------------------------dump2------------------------------------------ +#ifndef PRODUCT +void TypeAryPtr::dump2( Dict &d, uint depth, outputStream *st ) const { + _ary->dump2(d,depth,st); + switch( _ptr ) { + case Constant: + const_oop()->print(st); + break; + case BotPTR: + if (!WizardMode && !Verbose) { + if( _klass_is_exact ) st->print(":exact"); + break; + } + case TopPTR: + case AnyNull: + case NotNull: + st->print(":%s", ptr_msg[_ptr]); + if( _klass_is_exact ) st->print(":exact"); + break; + } + + st->print("*"); + if (_instance_id != UNKNOWN_INSTANCE) + st->print(",iid=%d",_instance_id); + if( !_offset ) return; + if( _offset == OffsetTop ) st->print("+undefined"); + else if( _offset == OffsetBot ) st->print("+any"); + else if( _offset < 12 ) st->print("+%d",_offset); + else st->print("[%d]", (_offset-12)/4 ); +} +#endif + +bool TypeAryPtr::empty(void) const { + if (_ary->empty()) return true; + return TypeOopPtr::empty(); +} + +//------------------------------add_offset------------------------------------- +const TypePtr *TypeAryPtr::add_offset( int offset ) const { + return make( _ptr, _const_oop, _ary, _klass, _klass_is_exact, xadd_offset(offset), _instance_id ); +} + + +//============================================================================= +// Convenience common pre-built types. + +// Not-null object klass or below +const TypeKlassPtr *TypeKlassPtr::OBJECT; +const TypeKlassPtr *TypeKlassPtr::OBJECT_OR_NULL; + +//------------------------------TypeKlasPtr------------------------------------ +TypeKlassPtr::TypeKlassPtr( PTR ptr, ciKlass* klass, int offset ) + : TypeOopPtr(KlassPtr, ptr, klass, (ptr==Constant), (ptr==Constant ? klass : NULL), offset, 0) { +} + +//------------------------------make------------------------------------------- +// ptr to klass 'k', if Constant, or possibly to a sub-klass if not a Constant +const TypeKlassPtr *TypeKlassPtr::make( PTR ptr, ciKlass* k, int offset ) { + assert( k != NULL, "Expect a non-NULL klass"); + assert(k->is_instance_klass() || k->is_array_klass() || + k->is_method_klass(), "Incorrect type of klass oop"); + TypeKlassPtr *r = + (TypeKlassPtr*)(new TypeKlassPtr(ptr, k, offset))->hashcons(); + + return r; +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeKlassPtr::eq( const Type *t ) const { + const TypeKlassPtr *p = t->is_klassptr(); + return + klass()->equals(p->klass()) && + TypeOopPtr::eq(p); +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeKlassPtr::hash(void) const { + return klass()->hash() + TypeOopPtr::hash(); +} + + +//------------------------------klass------------------------------------------ +// Return the defining klass for this class +ciKlass* TypeAryPtr::klass() const { + if( _klass ) return _klass; // Return cached value, if possible + + // Oops, need to compute _klass and cache it + ciKlass* k_ary = NULL; + const TypeInstPtr *tinst; + const TypeAryPtr *tary; + // Get element klass + if ((tinst = elem()->isa_instptr()) != NULL) { + // Compute array klass from element klass + k_ary = ciObjArrayKlass::make(tinst->klass()); + } else if ((tary = elem()->isa_aryptr()) != NULL) { + // Compute array klass from element klass + ciKlass* k_elem = tary->klass(); + // If element type is something like bottom[], k_elem will be null. + if (k_elem != NULL) + k_ary = ciObjArrayKlass::make(k_elem); + } else if ((elem()->base() == Type::Top) || + (elem()->base() == Type::Bottom)) { + // element type of Bottom occurs from meet of basic type + // and object; Top occurs when doing join on Bottom. + // Leave k_ary at NULL. + } else { + // Cannot compute array klass directly from basic type, + // since subtypes of TypeInt all have basic type T_INT. + assert(!elem()->isa_int(), + "integral arrays must be pre-equipped with a class"); + // Compute array klass directly from basic type + k_ary = ciTypeArrayKlass::make(elem()->basic_type()); + } + + if( this != TypeAryPtr::OOPS ) + // The _klass field acts as a cache of the underlying + // ciKlass for this array type. In order to set the field, + // we need to cast away const-ness. + // + // IMPORTANT NOTE: we *never* set the _klass field for the + // type TypeAryPtr::OOPS. This Type is shared between all + // active compilations. However, the ciKlass which represents + // this Type is *not* shared between compilations, so caching + // this value would result in fetching a dangling pointer. + // + // Recomputing the underlying ciKlass for each request is + // a bit less efficient than caching, but calls to + // TypeAryPtr::OOPS->klass() are not common enough to matter. + ((TypeAryPtr*)this)->_klass = k_ary; + return k_ary; +} + + +//------------------------------add_offset------------------------------------- +// Access internals of klass object +const TypePtr *TypeKlassPtr::add_offset( int offset ) const { + return make( _ptr, klass(), xadd_offset(offset) ); +} + +//------------------------------cast_to_ptr_type------------------------------- +const Type *TypeKlassPtr::cast_to_ptr_type(PTR ptr) const { + assert(_base == OopPtr, "subclass must override cast_to_ptr_type"); + if( ptr == _ptr ) return this; + return make(ptr, _klass, _offset); +} + + +//-----------------------------cast_to_exactness------------------------------- +const Type *TypeKlassPtr::cast_to_exactness(bool klass_is_exact) const { + if( klass_is_exact == _klass_is_exact ) return this; + if (!UseExactTypes) return this; + return make(klass_is_exact ? Constant : NotNull, _klass, _offset); +} + + +//-----------------------------as_instance_type-------------------------------- +// Corresponding type for an instance of the given class. +// It will be NotNull, and exact if and only if the klass type is exact. +const TypeOopPtr* TypeKlassPtr::as_instance_type() const { + ciKlass* k = klass(); + bool xk = klass_is_exact(); + //return TypeInstPtr::make(TypePtr::NotNull, k, xk, NULL, 0); + const TypeOopPtr* toop = TypeOopPtr::make_from_klass_raw(k); + toop = toop->cast_to_ptr_type(TypePtr::NotNull)->is_oopptr(); + return toop->cast_to_exactness(xk)->is_oopptr(); +} + + +//------------------------------xmeet------------------------------------------ +// Compute the MEET of two types, return a new Type object. +const Type *TypeKlassPtr::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Current "this->_base" is Pointer + switch (t->base()) { // switch on original type + + case Int: // Mixing ints & oops happens when javac + case Long: // reuses local variables + case FloatTop: + case FloatCon: + case FloatBot: + case DoubleTop: + case DoubleCon: + case DoubleBot: + case Bottom: // Ye Olde Default + return Type::BOTTOM; + case Top: + return this; + + default: // All else is a mistake + typerr(t); + + case RawPtr: return TypePtr::BOTTOM; + + case OopPtr: { // Meeting to OopPtrs + // Found a OopPtr type vs self-KlassPtr type + const TypePtr *tp = t->is_oopptr(); + int offset = meet_offset(tp->offset()); + PTR ptr = meet_ptr(tp->ptr()); + switch (tp->ptr()) { + case TopPTR: + case AnyNull: + return make(ptr, klass(), offset); + case BotPTR: + case NotNull: + return TypePtr::make(AnyPtr, ptr, offset); + default: typerr(t); + } + } + + case AnyPtr: { // Meeting to AnyPtrs + // Found an AnyPtr type vs self-KlassPtr type + const TypePtr *tp = t->is_ptr(); + int offset = meet_offset(tp->offset()); + PTR ptr = meet_ptr(tp->ptr()); + switch (tp->ptr()) { + case TopPTR: + return this; + case Null: + if( ptr == Null ) return TypePtr::make( AnyPtr, ptr, offset ); + case AnyNull: + return make( ptr, klass(), offset ); + case BotPTR: + case NotNull: + return TypePtr::make(AnyPtr, ptr, offset); + default: typerr(t); + } + } + + case AryPtr: // Meet with AryPtr + case InstPtr: // Meet with InstPtr + return TypeInstPtr::BOTTOM; + + // + // A-top } + // / | \ } Tops + // B-top A-any C-top } + // | / | \ | } Any-nulls + // B-any | C-any } + // | | | + // B-con A-con C-con } constants; not comparable across classes + // | | | + // B-not | C-not } + // | \ | / | } not-nulls + // B-bot A-not C-bot } + // \ | / } Bottoms + // A-bot } + // + + case KlassPtr: { // Meet two KlassPtr types + const TypeKlassPtr *tkls = t->is_klassptr(); + int off = meet_offset(tkls->offset()); + PTR ptr = meet_ptr(tkls->ptr()); + + // Check for easy case; klasses are equal (and perhaps not loaded!) + // If we have constants, then we created oops so classes are loaded + // and we can handle the constants further down. This case handles + // not-loaded classes + if( ptr != Constant && tkls->klass()->equals(klass()) ) { + return make( ptr, klass(), off ); + } + + // Classes require inspection in the Java klass hierarchy. Must be loaded. + ciKlass* tkls_klass = tkls->klass(); + ciKlass* this_klass = this->klass(); + assert( tkls_klass->is_loaded(), "This class should have been loaded."); + assert( this_klass->is_loaded(), "This class should have been loaded."); + + // If 'this' type is above the centerline and is a superclass of the + // other, we can treat 'this' as having the same type as the other. + if ((above_centerline(this->ptr())) && + tkls_klass->is_subtype_of(this_klass)) { + this_klass = tkls_klass; + } + // If 'tinst' type is above the centerline and is a superclass of the + // other, we can treat 'tinst' as having the same type as the other. + if ((above_centerline(tkls->ptr())) && + this_klass->is_subtype_of(tkls_klass)) { + tkls_klass = this_klass; + } + + // Check for classes now being equal + if (tkls_klass->equals(this_klass)) { + // If the klasses are equal, the constants may still differ. Fall to + // NotNull if they do (neither constant is NULL; that is a special case + // handled elsewhere). + ciObject* o = NULL; // Assume not constant when done + ciObject* this_oop = const_oop(); + ciObject* tkls_oop = tkls->const_oop(); + if( ptr == Constant ) { + if (this_oop != NULL && tkls_oop != NULL && + this_oop->equals(tkls_oop) ) + o = this_oop; + else if (above_centerline(this->ptr())) + o = tkls_oop; + else if (above_centerline(tkls->ptr())) + o = this_oop; + else + ptr = NotNull; + } + return make( ptr, this_klass, off ); + } // Else classes are not equal + + // Since klasses are different, we require the LCA in the Java + // class hierarchy - which means we have to fall to at least NotNull. + if( ptr == TopPTR || ptr == AnyNull || ptr == Constant ) + ptr = NotNull; + // Now we find the LCA of Java classes + ciKlass* k = this_klass->least_common_ancestor(tkls_klass); + return make( ptr, k, off ); + } // End of case KlassPtr + + } // End of switch + return this; // Return the double constant +} + +//------------------------------xdual------------------------------------------ +// Dual: compute field-by-field dual +const Type *TypeKlassPtr::xdual() const { + return new TypeKlassPtr( dual_ptr(), klass(), dual_offset() ); +} + +//------------------------------dump2------------------------------------------ +// Dump Klass Type +#ifndef PRODUCT +void TypeKlassPtr::dump2( Dict & d, uint depth, outputStream *st ) const { + switch( _ptr ) { + case Constant: + st->print("precise "); + case NotNull: + { + const char *name = klass()->name()->as_utf8(); + if( name ) { + st->print("klass %s: " INTPTR_FORMAT, name, klass()); + } else { + ShouldNotReachHere(); + } + } + case BotPTR: + if( !WizardMode && !Verbose && !_klass_is_exact ) break; + case TopPTR: + case AnyNull: + st->print(":%s", ptr_msg[_ptr]); + if( _klass_is_exact ) st->print(":exact"); + break; + } + + if( _offset ) { // Dump offset, if any + if( _offset == OffsetBot ) { st->print("+any"); } + else if( _offset == OffsetTop ) { st->print("+unknown"); } + else { st->print("+%d", _offset); } + } + + st->print(" *"); +} +#endif + + + +//============================================================================= +// Convenience common pre-built types. + +//------------------------------make------------------------------------------- +const TypeFunc *TypeFunc::make( const TypeTuple *domain, const TypeTuple *range ) { + return (TypeFunc*)(new TypeFunc(domain,range))->hashcons(); +} + +//------------------------------make------------------------------------------- +const TypeFunc *TypeFunc::make(ciMethod* method) { + Compile* C = Compile::current(); + const TypeFunc* tf = C->last_tf(method); // check cache + if (tf != NULL) return tf; // The hit rate here is almost 50%. + const TypeTuple *domain; + if (method->flags().is_static()) { + domain = TypeTuple::make_domain(NULL, method->signature()); + } else { + domain = TypeTuple::make_domain(method->holder(), method->signature()); + } + const TypeTuple *range = TypeTuple::make_range(method->signature()); + tf = TypeFunc::make(domain, range); + C->set_last_tf(method, tf); // fill cache + return tf; +} + +//------------------------------meet------------------------------------------- +// Compute the MEET of two types. It returns a new Type object. +const Type *TypeFunc::xmeet( const Type *t ) const { + // Perform a fast test for common case; meeting the same types together. + if( this == t ) return this; // Meeting same type-rep? + + // Current "this->_base" is Func + switch (t->base()) { // switch on original type + + case Bottom: // Ye Olde Default + return t; + + default: // All else is a mistake + typerr(t); + + case Top: + break; + } + return this; // Return the double constant +} + +//------------------------------xdual------------------------------------------ +// Dual: compute field-by-field dual +const Type *TypeFunc::xdual() const { + return this; +} + +//------------------------------eq--------------------------------------------- +// Structural equality check for Type representations +bool TypeFunc::eq( const Type *t ) const { + const TypeFunc *a = (const TypeFunc*)t; + return _domain == a->_domain && + _range == a->_range; +} + +//------------------------------hash------------------------------------------- +// Type-specific hashing function. +int TypeFunc::hash(void) const { + return (intptr_t)_domain + (intptr_t)_range; +} + +//------------------------------dump2------------------------------------------ +// Dump Function Type +#ifndef PRODUCT +void TypeFunc::dump2( Dict &d, uint depth, outputStream *st ) const { + if( _range->_cnt <= Parms ) + st->print("void"); + else { + uint i; + for (i = Parms; i < _range->_cnt-1; i++) { + _range->field_at(i)->dump2(d,depth,st); + st->print("/"); + } + _range->field_at(i)->dump2(d,depth,st); + } + st->print(" "); + st->print("( "); + if( !depth || d[this] ) { // Check for recursive dump + st->print("...)"); + return; + } + d.Insert((void*)this,(void*)this); // Stop recursion + if (Parms < _domain->_cnt) + _domain->field_at(Parms)->dump2(d,depth-1,st); + for (uint i = Parms+1; i < _domain->_cnt; i++) { + st->print(", "); + _domain->field_at(i)->dump2(d,depth-1,st); + } + st->print(" )"); +} + +//------------------------------print_flattened-------------------------------- +// Print a 'flattened' signature +static const char * const flat_type_msg[Type::lastype] = { + "bad","control","top","int","long","_", + "tuple:", "array:", + "ptr", "rawptr", "ptr", "ptr", "ptr", "ptr", + "func", "abIO", "return_address", "mem", + "float_top", "ftcon:", "flt", + "double_top", "dblcon:", "dbl", + "bottom" +}; + +void TypeFunc::print_flattened() const { + if( _range->_cnt <= Parms ) + tty->print("void"); + else { + uint i; + for (i = Parms; i < _range->_cnt-1; i++) + tty->print("%s/",flat_type_msg[_range->field_at(i)->base()]); + tty->print("%s",flat_type_msg[_range->field_at(i)->base()]); + } + tty->print(" ( "); + if (Parms < _domain->_cnt) + tty->print("%s",flat_type_msg[_domain->field_at(Parms)->base()]); + for (uint i = Parms+1; i < _domain->_cnt; i++) + tty->print(", %s",flat_type_msg[_domain->field_at(i)->base()]); + tty->print(" )"); +} +#endif + +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants (Ldi nodes). Singletons are integer, float or double constants +// or a single symbol. +bool TypeFunc::singleton(void) const { + return false; // Never a singleton +} + +bool TypeFunc::empty(void) const { + return false; // Never empty +} + + +BasicType TypeFunc::return_type() const{ + if (range()->cnt() == TypeFunc::Parms) { + return T_VOID; + } + return range()->field_at(TypeFunc::Parms)->basic_type(); +} diff --git a/hotspot/src/share/vm/opto/type.hpp b/hotspot/src/share/vm/opto/type.hpp new file mode 100644 index 00000000000..cca1e6404bd --- /dev/null +++ b/hotspot/src/share/vm/opto/type.hpp @@ -0,0 +1,1124 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Portions of code courtesy of Clifford Click + +// Optimization - Graph Style + + +// This class defines a Type lattice. The lattice is used in the constant +// propagation algorithms, and for some type-checking of the iloc code. +// Basic types include RSD's (lower bound, upper bound, stride for integers), +// float & double precision constants, sets of data-labels and code-labels. +// The complete lattice is described below. Subtypes have no relationship to +// up or down in the lattice; that is entirely determined by the behavior of +// the MEET/JOIN functions. + +class Dict; +class Type; +class TypeD; +class TypeF; +class TypeInt; +class TypeLong; +class TypeAry; +class TypeTuple; +class TypePtr; +class TypeRawPtr; +class TypeOopPtr; +class TypeInstPtr; +class TypeAryPtr; +class TypeKlassPtr; + +//------------------------------Type------------------------------------------- +// Basic Type object, represents a set of primitive Values. +// Types are hash-cons'd into a private class dictionary, so only one of each +// different kind of Type exists. Types are never modified after creation, so +// all their interesting fields are constant. +class Type { +public: + enum TYPES { + Bad=0, // Type check + Control, // Control of code (not in lattice) + Top, // Top of the lattice + Int, // Integer range (lo-hi) + Long, // Long integer range (lo-hi) + Half, // Placeholder half of doubleword + + Tuple, // Method signature or object layout + Array, // Array types + + AnyPtr, // Any old raw, klass, inst, or array pointer + RawPtr, // Raw (non-oop) pointers + OopPtr, // Any and all Java heap entities + InstPtr, // Instance pointers (non-array objects) + AryPtr, // Array pointers + KlassPtr, // Klass pointers + // (Ptr order matters: See is_ptr, isa_ptr, is_oopptr, isa_oopptr.) + + Function, // Function signature + Abio, // Abstract I/O + Return_Address, // Subroutine return address + Memory, // Abstract store + FloatTop, // No float value + FloatCon, // Floating point constant + FloatBot, // Any float value + DoubleTop, // No double value + DoubleCon, // Double precision constant + DoubleBot, // Any double value + Bottom, // Bottom of lattice + lastype // Bogus ending type (not in lattice) + }; + + // Signal values for offsets from a base pointer + enum OFFSET_SIGNALS { + OffsetTop = -2000000000, // undefined offset + OffsetBot = -2000000001 // any possible offset + }; + + // Min and max WIDEN values. + enum WIDEN { + WidenMin = 0, + WidenMax = 3 + }; + +private: + // Dictionary of types shared among compilations. + static Dict* _shared_type_dict; + + static int uhash( const Type *const t ); + // Structural equality check. Assumes that cmp() has already compared + // the _base types and thus knows it can cast 't' appropriately. + virtual bool eq( const Type *t ) const; + + // Top-level hash-table of types + static Dict *type_dict() { + return Compile::current()->type_dict(); + } + + // DUAL operation: reflect around lattice centerline. Used instead of + // join to ensure my lattice is symmetric up and down. Dual is computed + // lazily, on demand, and cached in _dual. + const Type *_dual; // Cached dual value + // Table for efficient dualing of base types + static const TYPES dual_type[lastype]; + +protected: + // Each class of type is also identified by its base. + const TYPES _base; // Enum of Types type + + Type( TYPES t ) : _dual(NULL), _base(t) {} // Simple types + // ~Type(); // Use fast deallocation + const Type *hashcons(); // Hash-cons the type + +public: + + inline void* operator new( size_t x ) { + Compile* compile = Compile::current(); + compile->set_type_last_size(x); + void *temp = compile->type_arena()->Amalloc_D(x); + compile->set_type_hwm(temp); + return temp; + } + inline void operator delete( void* ptr ) { + Compile* compile = Compile::current(); + compile->type_arena()->Afree(ptr,compile->type_last_size()); + } + + // Initialize the type system for a particular compilation. + static void Initialize(Compile* compile); + + // Initialize the types shared by all compilations. + static void Initialize_shared(Compile* compile); + + TYPES base() const { + assert(_base > Bad && _base < lastype, "sanity"); + return _base; + } + + // Create a new hash-consd type + static const Type *make(enum TYPES); + // Test for equivalence of types + static int cmp( const Type *const t1, const Type *const t2 ); + // Test for higher or equal in lattice + int higher_equal( const Type *t ) const { return !cmp(meet(t),t); } + + // MEET operation; lower in lattice. + const Type *meet( const Type *t ) const; + // WIDEN: 'widens' for Ints and other range types + virtual const Type *widen( const Type *old ) const { return this; } + // NARROW: complement for widen, used by pessimistic phases + virtual const Type *narrow( const Type *old ) const { return this; } + + // DUAL operation: reflect around lattice centerline. Used instead of + // join to ensure my lattice is symmetric up and down. + const Type *dual() const { return _dual; } + + // Compute meet dependent on base type + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + + // JOIN operation; higher in lattice. Done by finding the dual of the + // meet of the dual of the 2 inputs. + const Type *join( const Type *t ) const { + return dual()->meet(t->dual())->dual(); } + + // Modified version of JOIN adapted to the needs Node::Value. + // Normalizes all empty values to TOP. Does not kill _widen bits. + // Currently, it also works around limitations involving interface types. + virtual const Type *filter( const Type *kills ) const; + + // Convenience access + float getf() const; + double getd() const; + + const TypeInt *is_int() const; + const TypeInt *isa_int() const; // Returns NULL if not an Int + const TypeLong *is_long() const; + const TypeLong *isa_long() const; // Returns NULL if not a Long + const TypeD *is_double_constant() const; // Asserts it is a DoubleCon + const TypeD *isa_double_constant() const; // Returns NULL if not a DoubleCon + const TypeF *is_float_constant() const; // Asserts it is a FloatCon + const TypeF *isa_float_constant() const; // Returns NULL if not a FloatCon + const TypeTuple *is_tuple() const; // Collection of fields, NOT a pointer + const TypeAry *is_ary() const; // Array, NOT array pointer + const TypePtr *is_ptr() const; // Asserts it is a ptr type + const TypePtr *isa_ptr() const; // Returns NULL if not ptr type + const TypeRawPtr *is_rawptr() const; // NOT Java oop + const TypeOopPtr *isa_oopptr() const; // Returns NULL if not ptr type + const TypeKlassPtr *isa_klassptr() const; // Returns NULL if not KlassPtr + const TypeKlassPtr *is_klassptr() const; // assert if not KlassPtr + const TypeOopPtr *is_oopptr() const; // Java-style GC'd pointer + const TypeInstPtr *isa_instptr() const; // Returns NULL if not InstPtr + const TypeInstPtr *is_instptr() const; // Instance + const TypeAryPtr *isa_aryptr() const; // Returns NULL if not AryPtr + const TypeAryPtr *is_aryptr() const; // Array oop + virtual bool is_finite() const; // Has a finite value + virtual bool is_nan() const; // Is not a number (NaN) + + // Special test for register pressure heuristic + bool is_floatingpoint() const; // True if Float or Double base type + + // Do you have memory, directly or through a tuple? + bool has_memory( ) const; + + // Are you a pointer type or not? + bool isa_oop_ptr() const; + + // TRUE if type is a singleton + virtual bool singleton(void) const; + + // TRUE if type is above the lattice centerline, and is therefore vacuous + virtual bool empty(void) const; + + // Return a hash for this type. The hash function is public so ConNode + // (constants) can hash on their constant, which is represented by a Type. + virtual int hash() const; + + // Map ideal registers (machine types) to ideal types + static const Type *mreg2type[]; + + // Printing, statistics + static const char * const msg[lastype]; // Printable strings +#ifndef PRODUCT + void dump_on(outputStream *st) const; + void dump() const { + dump_on(tty); + } + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; + static void dump_stats(); + static void verify_lastype(); // Check that arrays match type enum +#endif + void typerr(const Type *t) const; // Mixing types error + + // Create basic type + static const Type* get_const_basic_type(BasicType type) { + assert((uint)type <= T_CONFLICT && _const_basic_type[type] != NULL, "bad type"); + return _const_basic_type[type]; + } + + // Mapping to the array element's basic type. + BasicType array_element_basic_type() const; + + // Create standard type for a ciType: + static const Type* get_const_type(ciType* type); + + // Create standard zero value: + static const Type* get_zero_type(BasicType type) { + assert((uint)type <= T_CONFLICT && _zero_type[type] != NULL, "bad type"); + return _zero_type[type]; + } + + // Report if this is a zero value (not top). + bool is_zero_type() const { + BasicType type = basic_type(); + if (type == T_VOID || type >= T_CONFLICT) + return false; + else + return (this == _zero_type[type]); + } + + // Convenience common pre-built types. + static const Type *ABIO; + static const Type *BOTTOM; + static const Type *CONTROL; + static const Type *DOUBLE; + static const Type *FLOAT; + static const Type *HALF; + static const Type *MEMORY; + static const Type *MULTI; + static const Type *RETURN_ADDRESS; + static const Type *TOP; + + // Mapping from compiler type to VM BasicType + BasicType basic_type() const { return _basic_type[_base]; } + + // Mapping from CI type system to compiler type: + static const Type* get_typeflow_type(ciType* type); + +private: + // support arrays + static const BasicType _basic_type[]; + static const Type* _zero_type[T_CONFLICT+1]; + static const Type* _const_basic_type[T_CONFLICT+1]; +}; + +//------------------------------TypeF------------------------------------------ +// Class of Float-Constant Types. +class TypeF : public Type { + TypeF( float f ) : Type(FloatCon), _f(f) {}; +public: + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + virtual bool singleton(void) const; // TRUE if type is a singleton + virtual bool empty(void) const; // TRUE if type is vacuous +public: + const float _f; // Float constant + + static const TypeF *make(float f); + + virtual bool is_finite() const; // Has a finite value + virtual bool is_nan() const; // Is not a number (NaN) + + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + // Convenience common pre-built types. + static const TypeF *ZERO; // positive zero only + static const TypeF *ONE; +#ifndef PRODUCT + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; +#endif +}; + +//------------------------------TypeD------------------------------------------ +// Class of Double-Constant Types. +class TypeD : public Type { + TypeD( double d ) : Type(DoubleCon), _d(d) {}; +public: + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + virtual bool singleton(void) const; // TRUE if type is a singleton + virtual bool empty(void) const; // TRUE if type is vacuous +public: + const double _d; // Double constant + + static const TypeD *make(double d); + + virtual bool is_finite() const; // Has a finite value + virtual bool is_nan() const; // Is not a number (NaN) + + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + // Convenience common pre-built types. + static const TypeD *ZERO; // positive zero only + static const TypeD *ONE; +#ifndef PRODUCT + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; +#endif +}; + +//------------------------------TypeInt---------------------------------------- +// Class of integer ranges, the set of integers between a lower bound and an +// upper bound, inclusive. +class TypeInt : public Type { + TypeInt( jint lo, jint hi, int w ); +public: + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + virtual bool singleton(void) const; // TRUE if type is a singleton + virtual bool empty(void) const; // TRUE if type is vacuous +public: + const jint _lo, _hi; // Lower bound, upper bound + const short _widen; // Limit on times we widen this sucker + + static const TypeInt *make(jint lo); + // must always specify w + static const TypeInt *make(jint lo, jint hi, int w); + + // Check for single integer + int is_con() const { return _lo==_hi; } + bool is_con(int i) const { return is_con() && _lo == i; } + jint get_con() const { assert( is_con(), "" ); return _lo; } + + virtual bool is_finite() const; // Has a finite value + + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + virtual const Type *widen( const Type *t ) const; + virtual const Type *narrow( const Type *t ) const; + // Do not kill _widen bits. + virtual const Type *filter( const Type *kills ) const; + // Convenience common pre-built types. + static const TypeInt *MINUS_1; + static const TypeInt *ZERO; + static const TypeInt *ONE; + static const TypeInt *BOOL; + static const TypeInt *CC; + static const TypeInt *CC_LT; // [-1] == MINUS_1 + static const TypeInt *CC_GT; // [1] == ONE + static const TypeInt *CC_EQ; // [0] == ZERO + static const TypeInt *CC_LE; // [-1,0] + static const TypeInt *CC_GE; // [0,1] == BOOL (!) + static const TypeInt *BYTE; + static const TypeInt *CHAR; + static const TypeInt *SHORT; + static const TypeInt *POS; + static const TypeInt *POS1; + static const TypeInt *INT; + static const TypeInt *SYMINT; // symmetric range [-max_jint..max_jint] +#ifndef PRODUCT + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; +#endif +}; + + +//------------------------------TypeLong--------------------------------------- +// Class of long integer ranges, the set of integers between a lower bound and +// an upper bound, inclusive. +class TypeLong : public Type { + TypeLong( jlong lo, jlong hi, int w ); +public: + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + virtual bool singleton(void) const; // TRUE if type is a singleton + virtual bool empty(void) const; // TRUE if type is vacuous +public: + const jlong _lo, _hi; // Lower bound, upper bound + const short _widen; // Limit on times we widen this sucker + + static const TypeLong *make(jlong lo); + // must always specify w + static const TypeLong *make(jlong lo, jlong hi, int w); + + // Check for single integer + int is_con() const { return _lo==_hi; } + jlong get_con() const { assert( is_con(), "" ); return _lo; } + + virtual bool is_finite() const; // Has a finite value + + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + virtual const Type *widen( const Type *t ) const; + virtual const Type *narrow( const Type *t ) const; + // Do not kill _widen bits. + virtual const Type *filter( const Type *kills ) const; + // Convenience common pre-built types. + static const TypeLong *MINUS_1; + static const TypeLong *ZERO; + static const TypeLong *ONE; + static const TypeLong *POS; + static const TypeLong *LONG; + static const TypeLong *INT; // 32-bit subrange [min_jint..max_jint] + static const TypeLong *UINT; // 32-bit unsigned [0..max_juint] +#ifndef PRODUCT + virtual void dump2( Dict &d, uint, outputStream *st ) const;// Specialized per-Type dumping +#endif +}; + +//------------------------------TypeTuple-------------------------------------- +// Class of Tuple Types, essentially type collections for function signatures +// and class layouts. It happens to also be a fast cache for the HotSpot +// signature types. +class TypeTuple : public Type { + TypeTuple( uint cnt, const Type **fields ) : Type(Tuple), _cnt(cnt), _fields(fields) { } +public: + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + virtual bool singleton(void) const; // TRUE if type is a singleton + virtual bool empty(void) const; // TRUE if type is vacuous + +public: + const uint _cnt; // Count of fields + const Type ** const _fields; // Array of field types + + // Accessors: + uint cnt() const { return _cnt; } + const Type* field_at(uint i) const { + assert(i < _cnt, "oob"); + return _fields[i]; + } + void set_field_at(uint i, const Type* t) { + assert(i < _cnt, "oob"); + _fields[i] = t; + } + + static const TypeTuple *make( uint cnt, const Type **fields ); + static const TypeTuple *make_range(ciSignature *sig); + static const TypeTuple *make_domain(ciInstanceKlass* recv, ciSignature *sig); + + // Subroutine call type with space allocated for argument types + static const Type **fields( uint arg_cnt ); + + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + // Convenience common pre-built types. + static const TypeTuple *IFBOTH; + static const TypeTuple *IFFALSE; + static const TypeTuple *IFTRUE; + static const TypeTuple *IFNEITHER; + static const TypeTuple *LOOPBODY; + static const TypeTuple *MEMBAR; + static const TypeTuple *STORECONDITIONAL; + static const TypeTuple *START_I2C; + static const TypeTuple *INT_PAIR; + static const TypeTuple *LONG_PAIR; +#ifndef PRODUCT + virtual void dump2( Dict &d, uint, outputStream *st ) const; // Specialized per-Type dumping +#endif +}; + +//------------------------------TypeAry---------------------------------------- +// Class of Array Types +class TypeAry : public Type { + TypeAry( const Type *elem, const TypeInt *size) : Type(Array), + _elem(elem), _size(size) {} +public: + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + virtual bool singleton(void) const; // TRUE if type is a singleton + virtual bool empty(void) const; // TRUE if type is vacuous + +private: + const Type *_elem; // Element type of array + const TypeInt *_size; // Elements in array + friend class TypeAryPtr; + +public: + static const TypeAry *make( const Type *elem, const TypeInt *size); + + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + bool ary_must_be_exact() const; // true if arrays of such are never generic +#ifndef PRODUCT + virtual void dump2( Dict &d, uint, outputStream *st ) const; // Specialized per-Type dumping +#endif +}; + +//------------------------------TypePtr---------------------------------------- +// Class of machine Pointer Types: raw data, instances or arrays. +// If the _base enum is AnyPtr, then this refers to all of the above. +// Otherwise the _base will indicate which subset of pointers is affected, +// and the class will be inherited from. +class TypePtr : public Type { +public: + enum PTR { TopPTR, AnyNull, Constant, Null, NotNull, BotPTR, lastPTR }; +protected: + TypePtr( TYPES t, PTR ptr, int offset ) : Type(t), _ptr(ptr), _offset(offset) {} + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + static const PTR ptr_meet[lastPTR][lastPTR]; + static const PTR ptr_dual[lastPTR]; + static const char * const ptr_msg[lastPTR]; + +public: + const int _offset; // Offset into oop, with TOP & BOT + const PTR _ptr; // Pointer equivalence class + + const int offset() const { return _offset; } + const PTR ptr() const { return _ptr; } + + static const TypePtr *make( TYPES t, PTR ptr, int offset ); + + // Return a 'ptr' version of this type + virtual const Type *cast_to_ptr_type(PTR ptr) const; + + virtual intptr_t get_con() const; + + virtual const TypePtr *add_offset( int offset ) const; + + virtual bool singleton(void) const; // TRUE if type is a singleton + virtual bool empty(void) const; // TRUE if type is vacuous + virtual const Type *xmeet( const Type *t ) const; + int meet_offset( int offset ) const; + int dual_offset( ) const; + virtual const Type *xdual() const; // Compute dual right now. + + // meet, dual and join over pointer equivalence sets + PTR meet_ptr( const PTR in_ptr ) const { return ptr_meet[in_ptr][ptr()]; } + PTR dual_ptr() const { return ptr_dual[ptr()]; } + + // This is textually confusing unless one recalls that + // join(t) == dual()->meet(t->dual())->dual(). + PTR join_ptr( const PTR in_ptr ) const { + return ptr_dual[ ptr_meet[ ptr_dual[in_ptr] ] [ dual_ptr() ] ]; + } + + // Tests for relation to centerline of type lattice: + static bool above_centerline(PTR ptr) { return (ptr <= AnyNull); } + static bool below_centerline(PTR ptr) { return (ptr >= NotNull); } + // Convenience common pre-built types. + static const TypePtr *NULL_PTR; + static const TypePtr *NOTNULL; + static const TypePtr *BOTTOM; +#ifndef PRODUCT + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; +#endif +}; + +//------------------------------TypeRawPtr------------------------------------- +// Class of raw pointers, pointers to things other than Oops. Examples +// include the stack pointer, top of heap, card-marking area, handles, etc. +class TypeRawPtr : public TypePtr { +protected: + TypeRawPtr( PTR ptr, address bits ) : TypePtr(RawPtr,ptr,0), _bits(bits){} +public: + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + + const address _bits; // Constant value, if applicable + + static const TypeRawPtr *make( PTR ptr ); + static const TypeRawPtr *make( address bits ); + + // Return a 'ptr' version of this type + virtual const Type *cast_to_ptr_type(PTR ptr) const; + + virtual intptr_t get_con() const; + + virtual const TypePtr *add_offset( int offset ) const; + + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + // Convenience common pre-built types. + static const TypeRawPtr *BOTTOM; + static const TypeRawPtr *NOTNULL; +#ifndef PRODUCT + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; +#endif +}; + +//------------------------------TypeOopPtr------------------------------------- +// Some kind of oop (Java pointer), either klass or instance or array. +class TypeOopPtr : public TypePtr { +protected: + TypeOopPtr( TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id ) : TypePtr(t, ptr, offset), _const_oop(o), _klass(k), _klass_is_exact(xk), _instance_id(instance_id) { } +public: + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + virtual bool singleton(void) const; // TRUE if type is a singleton + enum { + UNKNOWN_INSTANCE = 0 + }; +protected: + + int xadd_offset( int offset ) const; + // Oop is NULL, unless this is a constant oop. + ciObject* _const_oop; // Constant oop + // If _klass is NULL, then so is _sig. This is an unloaded klass. + ciKlass* _klass; // Klass object + // Does the type exclude subclasses of the klass? (Inexact == polymorphic.) + bool _klass_is_exact; + + int _instance_id; // if not UNKNOWN_INSTANCE, indicates that this is a particular instance + // of this type which is distinct. This is the the node index of the + // node creating this instance + + static const TypeOopPtr* make_from_klass_common(ciKlass* klass, bool klass_change, bool try_for_exact); + + int dual_instance() const { return -_instance_id; } + int meet_instance(int uid) const; + +public: + // Creates a type given a klass. Correctly handles multi-dimensional arrays + // Respects UseUniqueSubclasses. + // If the klass is final, the resulting type will be exact. + static const TypeOopPtr* make_from_klass(ciKlass* klass) { + return make_from_klass_common(klass, true, false); + } + // Same as before, but will produce an exact type, even if + // the klass is not final, as long as it has exactly one implementation. + static const TypeOopPtr* make_from_klass_unique(ciKlass* klass) { + return make_from_klass_common(klass, true, true); + } + // Same as before, but does not respects UseUniqueSubclasses. + // Use this only for creating array element types. + static const TypeOopPtr* make_from_klass_raw(ciKlass* klass) { + return make_from_klass_common(klass, false, false); + } + // Creates a singleton type given an object. + static const TypeOopPtr* make_from_constant(ciObject* o); + + // Make a generic (unclassed) pointer to an oop. + static const TypeOopPtr* make(PTR ptr, int offset); + + ciObject* const_oop() const { return _const_oop; } + virtual ciKlass* klass() const { return _klass; } + bool klass_is_exact() const { return _klass_is_exact; } + bool is_instance() const { return _instance_id != UNKNOWN_INSTANCE; } + uint instance_id() const { return _instance_id; } + + virtual intptr_t get_con() const; + + virtual const Type *cast_to_ptr_type(PTR ptr) const; + + virtual const Type *cast_to_exactness(bool klass_is_exact) const; + + virtual const TypeOopPtr *cast_to_instance(int instance_id) const; + + // corresponding pointer to klass, for a given instance + const TypeKlassPtr* as_klass_type() const; + + virtual const TypePtr *add_offset( int offset ) const; + + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + + // Do not allow interface-vs.-noninterface joins to collapse to top. + virtual const Type *filter( const Type *kills ) const; + + // Convenience common pre-built type. + static const TypeOopPtr *BOTTOM; +#ifndef PRODUCT + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; +#endif +}; + +//------------------------------TypeInstPtr------------------------------------ +// Class of Java object pointers, pointing either to non-array Java instances +// or to a klassOop (including array klasses). +class TypeInstPtr : public TypeOopPtr { + TypeInstPtr( PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id ); + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + + ciSymbol* _name; // class name + + public: + ciSymbol* name() const { return _name; } + + bool is_loaded() const { return _klass->is_loaded(); } + + // Make a pointer to a constant oop. + static const TypeInstPtr *make(ciObject* o) { + return make(TypePtr::Constant, o->klass(), true, o, 0); + } + + // Make a pointer to a constant oop with offset. + static const TypeInstPtr *make(ciObject* o, int offset) { + return make(TypePtr::Constant, o->klass(), true, o, offset); + } + + // Make a pointer to some value of type klass. + static const TypeInstPtr *make(PTR ptr, ciKlass* klass) { + return make(ptr, klass, false, NULL, 0); + } + + // Make a pointer to some non-polymorphic value of exactly type klass. + static const TypeInstPtr *make_exact(PTR ptr, ciKlass* klass) { + return make(ptr, klass, true, NULL, 0); + } + + // Make a pointer to some value of type klass with offset. + static const TypeInstPtr *make(PTR ptr, ciKlass* klass, int offset) { + return make(ptr, klass, false, NULL, offset); + } + + // Make a pointer to an oop. + static const TypeInstPtr *make(PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id = 0 ); + + // If this is a java.lang.Class constant, return the type for it or NULL. + // Pass to Type::get_const_type to turn it to a type, which will usually + // be a TypeInstPtr, but may also be a TypeInt::INT for int.class, etc. + ciType* java_mirror_type() const; + + virtual const Type *cast_to_ptr_type(PTR ptr) const; + + virtual const Type *cast_to_exactness(bool klass_is_exact) const; + + virtual const TypeOopPtr *cast_to_instance(int instance_id) const; + + virtual const TypePtr *add_offset( int offset ) const; + + virtual const Type *xmeet( const Type *t ) const; + virtual const TypeInstPtr *xmeet_unloaded( const TypeInstPtr *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + + // Convenience common pre-built types. + static const TypeInstPtr *NOTNULL; + static const TypeInstPtr *BOTTOM; + static const TypeInstPtr *MIRROR; + static const TypeInstPtr *MARK; + static const TypeInstPtr *KLASS; +#ifndef PRODUCT + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping +#endif +}; + +//------------------------------TypeAryPtr------------------------------------- +// Class of Java array pointers +class TypeAryPtr : public TypeOopPtr { + TypeAryPtr( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id ) : TypeOopPtr(AryPtr,ptr,k,xk,o,offset, instance_id), _ary(ary) {}; + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + const TypeAry *_ary; // Array we point into + +public: + // Accessors + ciKlass* klass() const; + const TypeAry* ary() const { return _ary; } + const Type* elem() const { return _ary->_elem; } + const TypeInt* size() const { return _ary->_size; } + + static const TypeAryPtr *make( PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = 0); + // Constant pointer to array + static const TypeAryPtr *make( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = 0); + + // Convenience + static const TypeAryPtr *make(ciObject* o); + + // Return a 'ptr' version of this type + virtual const Type *cast_to_ptr_type(PTR ptr) const; + + virtual const Type *cast_to_exactness(bool klass_is_exact) const; + + virtual const TypeOopPtr *cast_to_instance(int instance_id) const; + + virtual const TypeAryPtr* cast_to_size(const TypeInt* size) const; + + virtual bool empty(void) const; // TRUE if type is vacuous + virtual const TypePtr *add_offset( int offset ) const; + + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + + // Convenience common pre-built types. + static const TypeAryPtr *RANGE; + static const TypeAryPtr *OOPS; + static const TypeAryPtr *BYTES; + static const TypeAryPtr *SHORTS; + static const TypeAryPtr *CHARS; + static const TypeAryPtr *INTS; + static const TypeAryPtr *LONGS; + static const TypeAryPtr *FLOATS; + static const TypeAryPtr *DOUBLES; + // selects one of the above: + static const TypeAryPtr *get_array_body_type(BasicType elem) { + assert((uint)elem <= T_CONFLICT && _array_body_type[elem] != NULL, "bad elem type"); + return _array_body_type[elem]; + } + static const TypeAryPtr *_array_body_type[T_CONFLICT+1]; + // sharpen the type of an int which is used as an array size + static const TypeInt* narrow_size_type(const TypeInt* size, BasicType elem); +#ifndef PRODUCT + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping +#endif +}; + +//------------------------------TypeKlassPtr----------------------------------- +// Class of Java Klass pointers +class TypeKlassPtr : public TypeOopPtr { + TypeKlassPtr( PTR ptr, ciKlass* klass, int offset ); + + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + +public: + ciSymbol* name() const { return _klass->name(); } + + // ptr to klass 'k' + static const TypeKlassPtr *make( ciKlass* k ) { return make( TypePtr::Constant, k, 0); } + // ptr to klass 'k' with offset + static const TypeKlassPtr *make( ciKlass* k, int offset ) { return make( TypePtr::Constant, k, offset); } + // ptr to klass 'k' or sub-klass + static const TypeKlassPtr *make( PTR ptr, ciKlass* k, int offset); + + virtual const Type *cast_to_ptr_type(PTR ptr) const; + + virtual const Type *cast_to_exactness(bool klass_is_exact) const; + + // corresponding pointer to instance, for a given class + const TypeOopPtr* as_instance_type() const; + + virtual const TypePtr *add_offset( int offset ) const; + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + + // Convenience common pre-built types. + static const TypeKlassPtr* OBJECT; // Not-null object klass or below + static const TypeKlassPtr* OBJECT_OR_NULL; // Maybe-null version of same +#ifndef PRODUCT + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping +#endif +}; + +//------------------------------TypeFunc--------------------------------------- +// Class of Array Types +class TypeFunc : public Type { + TypeFunc( const TypeTuple *domain, const TypeTuple *range ) : Type(Function), _domain(domain), _range(range) {} + virtual bool eq( const Type *t ) const; + virtual int hash() const; // Type specific hashing + virtual bool singleton(void) const; // TRUE if type is a singleton + virtual bool empty(void) const; // TRUE if type is vacuous +public: + // Constants are shared among ADLC and VM + enum { Control = AdlcVMDeps::Control, + I_O = AdlcVMDeps::I_O, + Memory = AdlcVMDeps::Memory, + FramePtr = AdlcVMDeps::FramePtr, + ReturnAdr = AdlcVMDeps::ReturnAdr, + Parms = AdlcVMDeps::Parms + }; + + const TypeTuple* const _domain; // Domain of inputs + const TypeTuple* const _range; // Range of results + + // Accessors: + const TypeTuple* domain() const { return _domain; } + const TypeTuple* range() const { return _range; } + + static const TypeFunc *make(ciMethod* method); + static const TypeFunc *make(ciSignature signature, const Type* extra); + static const TypeFunc *make(const TypeTuple* domain, const TypeTuple* range); + + virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xdual() const; // Compute dual right now. + + BasicType return_type() const; + +#ifndef PRODUCT + virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping + void print_flattened() const; // Print a 'flattened' signature +#endif + // Convenience common pre-built types. +}; + +//------------------------------accessors-------------------------------------- +inline float Type::getf() const { + assert( _base == FloatCon, "Not a FloatCon" ); + return ((TypeF*)this)->_f; +} + +inline double Type::getd() const { + assert( _base == DoubleCon, "Not a DoubleCon" ); + return ((TypeD*)this)->_d; +} + +inline const TypeF *Type::is_float_constant() const { + assert( _base == FloatCon, "Not a Float" ); + return (TypeF*)this; +} + +inline const TypeF *Type::isa_float_constant() const { + return ( _base == FloatCon ? (TypeF*)this : NULL); +} + +inline const TypeD *Type::is_double_constant() const { + assert( _base == DoubleCon, "Not a Double" ); + return (TypeD*)this; +} + +inline const TypeD *Type::isa_double_constant() const { + return ( _base == DoubleCon ? (TypeD*)this : NULL); +} + +inline const TypeInt *Type::is_int() const { + assert( _base == Int, "Not an Int" ); + return (TypeInt*)this; +} + +inline const TypeInt *Type::isa_int() const { + return ( _base == Int ? (TypeInt*)this : NULL); +} + +inline const TypeLong *Type::is_long() const { + assert( _base == Long, "Not a Long" ); + return (TypeLong*)this; +} + +inline const TypeLong *Type::isa_long() const { + return ( _base == Long ? (TypeLong*)this : NULL); +} + +inline const TypeTuple *Type::is_tuple() const { + assert( _base == Tuple, "Not a Tuple" ); + return (TypeTuple*)this; +} + +inline const TypeAry *Type::is_ary() const { + assert( _base == Array , "Not an Array" ); + return (TypeAry*)this; +} + +inline const TypePtr *Type::is_ptr() const { + // AnyPtr is the first Ptr and KlassPtr the last, with no non-ptrs between. + assert(_base >= AnyPtr && _base <= KlassPtr, "Not a pointer"); + return (TypePtr*)this; +} + +inline const TypePtr *Type::isa_ptr() const { + // AnyPtr is the first Ptr and KlassPtr the last, with no non-ptrs between. + return (_base >= AnyPtr && _base <= KlassPtr) ? (TypePtr*)this : NULL; +} + +inline const TypeOopPtr *Type::is_oopptr() const { + // OopPtr is the first and KlassPtr the last, with no non-oops between. + assert(_base >= OopPtr && _base <= KlassPtr, "Not a Java pointer" ) ; + return (TypeOopPtr*)this; +} + +inline const TypeOopPtr *Type::isa_oopptr() const { + // OopPtr is the first and KlassPtr the last, with no non-oops between. + return (_base >= OopPtr && _base <= KlassPtr) ? (TypeOopPtr*)this : NULL; +} + +inline const TypeRawPtr *Type::is_rawptr() const { + assert( _base == RawPtr, "Not a raw pointer" ); + return (TypeRawPtr*)this; +} + +inline const TypeInstPtr *Type::isa_instptr() const { + return (_base == InstPtr) ? (TypeInstPtr*)this : NULL; +} + +inline const TypeInstPtr *Type::is_instptr() const { + assert( _base == InstPtr, "Not an object pointer" ); + return (TypeInstPtr*)this; +} + +inline const TypeAryPtr *Type::isa_aryptr() const { + return (_base == AryPtr) ? (TypeAryPtr*)this : NULL; +} + +inline const TypeAryPtr *Type::is_aryptr() const { + assert( _base == AryPtr, "Not an array pointer" ); + return (TypeAryPtr*)this; +} + +inline const TypeKlassPtr *Type::isa_klassptr() const { + return (_base == KlassPtr) ? (TypeKlassPtr*)this : NULL; +} + +inline const TypeKlassPtr *Type::is_klassptr() const { + assert( _base == KlassPtr, "Not a klass pointer" ); + return (TypeKlassPtr*)this; +} + +inline bool Type::is_floatingpoint() const { + if( (_base == FloatCon) || (_base == FloatBot) || + (_base == DoubleCon) || (_base == DoubleBot) ) + return true; + return false; +} + + +// =============================================================== +// Things that need to be 64-bits in the 64-bit build but +// 32-bits in the 32-bit build. Done this way to get full +// optimization AND strong typing. +#ifdef _LP64 + +// For type queries and asserts +#define is_intptr_t is_long +#define isa_intptr_t isa_long +#define find_intptr_t_type find_long_type +#define find_intptr_t_con find_long_con +#define TypeX TypeLong +#define Type_X Type::Long +#define TypeX_X TypeLong::LONG +#define TypeX_ZERO TypeLong::ZERO +// For 'ideal_reg' machine registers +#define Op_RegX Op_RegL +// For phase->intcon variants +#define MakeConX longcon +#define ConXNode ConLNode +// For array index arithmetic +#define MulXNode MulLNode +#define AndXNode AndLNode +#define OrXNode OrLNode +#define CmpXNode CmpLNode +#define SubXNode SubLNode +#define LShiftXNode LShiftLNode +// For object size computation: +#define AddXNode AddLNode +// For card marks and hashcodes +#define URShiftXNode URShiftLNode +// Opcodes +#define Op_LShiftX Op_LShiftL +#define Op_AndX Op_AndL +#define Op_AddX Op_AddL +#define Op_SubX Op_SubL +// conversions +#define ConvI2X(x) ConvI2L(x) +#define ConvL2X(x) (x) +#define ConvX2I(x) ConvL2I(x) +#define ConvX2L(x) (x) + +#else + +// For type queries and asserts +#define is_intptr_t is_int +#define isa_intptr_t isa_int +#define find_intptr_t_type find_int_type +#define find_intptr_t_con find_int_con +#define TypeX TypeInt +#define Type_X Type::Int +#define TypeX_X TypeInt::INT +#define TypeX_ZERO TypeInt::ZERO +// For 'ideal_reg' machine registers +#define Op_RegX Op_RegI +// For phase->intcon variants +#define MakeConX intcon +#define ConXNode ConINode +// For array index arithmetic +#define MulXNode MulINode +#define AndXNode AndINode +#define OrXNode OrINode +#define CmpXNode CmpINode +#define SubXNode SubINode +#define LShiftXNode LShiftINode +// For object size computation: +#define AddXNode AddINode +// For card marks and hashcodes +#define URShiftXNode URShiftINode +// Opcodes +#define Op_LShiftX Op_LShiftI +#define Op_AndX Op_AndI +#define Op_AddX Op_AddI +#define Op_SubX Op_SubI +// conversions +#define ConvI2X(x) (x) +#define ConvL2X(x) ConvL2I(x) +#define ConvX2I(x) (x) +#define ConvX2L(x) ConvI2L(x) + +#endif diff --git a/hotspot/src/share/vm/opto/vectornode.cpp b/hotspot/src/share/vm/opto/vectornode.cpp new file mode 100644 index 00000000000..f137516052d --- /dev/null +++ b/hotspot/src/share/vm/opto/vectornode.cpp @@ -0,0 +1,478 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#include "incls/_precompiled.incl" +#include "incls/_vectornode.cpp.incl" + +//------------------------------VectorNode-------------------------------------- + +// Return vector type for an element type and vector length. +const Type* VectorNode::vect_type(BasicType elt_bt, uint len) { + assert(len <= VectorNode::max_vlen(elt_bt), "len in range"); + switch(elt_bt) { + case T_BOOLEAN: + case T_BYTE: + switch(len) { + case 2: return TypeInt::CHAR; + case 4: return TypeInt::INT; + case 8: return TypeLong::LONG; + } + break; + case T_CHAR: + case T_SHORT: + switch(len) { + case 2: return TypeInt::INT; + case 4: return TypeLong::LONG; + } + break; + case T_INT: + switch(len) { + case 2: return TypeLong::LONG; + } + break; + case T_LONG: + break; + case T_FLOAT: + switch(len) { + case 2: return Type::DOUBLE; + } + break; + case T_DOUBLE: + break; + } + ShouldNotReachHere(); + return NULL; +} + +// Scalar promotion +VectorNode* VectorNode::scalar2vector(Compile* C, Node* s, uint vlen, const Type* opd_t) { + BasicType bt = opd_t->array_element_basic_type(); + assert(vlen <= VectorNode::max_vlen(bt), "vlen in range"); + switch (bt) { + case T_BOOLEAN: + case T_BYTE: + if (vlen == 16) return new (C, 2) Replicate16BNode(s); + if (vlen == 8) return new (C, 2) Replicate8BNode(s); + if (vlen == 4) return new (C, 2) Replicate4BNode(s); + break; + case T_CHAR: + if (vlen == 8) return new (C, 2) Replicate8CNode(s); + if (vlen == 4) return new (C, 2) Replicate4CNode(s); + if (vlen == 2) return new (C, 2) Replicate2CNode(s); + break; + case T_SHORT: + if (vlen == 8) return new (C, 2) Replicate8SNode(s); + if (vlen == 4) return new (C, 2) Replicate4SNode(s); + if (vlen == 2) return new (C, 2) Replicate2SNode(s); + break; + case T_INT: + if (vlen == 4) return new (C, 2) Replicate4INode(s); + if (vlen == 2) return new (C, 2) Replicate2INode(s); + break; + case T_LONG: + if (vlen == 2) return new (C, 2) Replicate2LNode(s); + break; + case T_FLOAT: + if (vlen == 4) return new (C, 2) Replicate4FNode(s); + if (vlen == 2) return new (C, 2) Replicate2FNode(s); + break; + case T_DOUBLE: + if (vlen == 2) return new (C, 2) Replicate2DNode(s); + break; + } + ShouldNotReachHere(); + return NULL; +} + +// Return initial Pack node. Additional operands added with add_opd() calls. +PackNode* PackNode::make(Compile* C, Node* s, const Type* opd_t) { + BasicType bt = opd_t->array_element_basic_type(); + switch (bt) { + case T_BOOLEAN: + case T_BYTE: + return new (C, 2) PackBNode(s); + case T_CHAR: + return new (C, 2) PackCNode(s); + case T_SHORT: + return new (C, 2) PackSNode(s); + case T_INT: + return new (C, 2) PackINode(s); + case T_LONG: + return new (C, 2) PackLNode(s); + case T_FLOAT: + return new (C, 2) PackFNode(s); + case T_DOUBLE: + return new (C, 2) PackDNode(s); + } + ShouldNotReachHere(); + return NULL; +} + +// Create a binary tree form for Packs. [lo, hi) (half-open) range +Node* PackNode::binaryTreePack(Compile* C, int lo, int hi) { + int ct = hi - lo; + assert(is_power_of_2(ct), "power of 2"); + int mid = lo + ct/2; + Node* n1 = ct == 2 ? in(lo) : binaryTreePack(C, lo, mid); + Node* n2 = ct == 2 ? in(lo+1) : binaryTreePack(C, mid, hi ); + int rslt_bsize = ct * type2aelembytes[elt_basic_type()]; + if (bottom_type()->is_floatingpoint()) { + switch (rslt_bsize) { + case 8: return new (C, 3) PackFNode(n1, n2); + case 16: return new (C, 3) PackDNode(n1, n2); + } + } else { + assert(bottom_type()->isa_int() || bottom_type()->isa_long(), "int or long"); + switch (rslt_bsize) { + case 2: return new (C, 3) Pack2x1BNode(n1, n2); + case 4: return new (C, 3) Pack2x2BNode(n1, n2); + case 8: return new (C, 3) PackINode(n1, n2); + case 16: return new (C, 3) PackLNode(n1, n2); + } + } + ShouldNotReachHere(); + return NULL; +} + +// Return the vector operator for the specified scalar operation +// and vector length. One use is to check if the code generator +// supports the vector operation. +int VectorNode::opcode(int sopc, uint vlen, const Type* opd_t) { + BasicType bt = opd_t->array_element_basic_type(); + if (!(is_power_of_2(vlen) && vlen <= max_vlen(bt))) + return 0; // unimplemented + switch (sopc) { + case Op_AddI: + switch (bt) { + case T_BOOLEAN: + case T_BYTE: return Op_AddVB; + case T_CHAR: return Op_AddVC; + case T_SHORT: return Op_AddVS; + case T_INT: return Op_AddVI; + } + ShouldNotReachHere(); + case Op_AddL: + assert(bt == T_LONG, "must be"); + return Op_AddVL; + case Op_AddF: + assert(bt == T_FLOAT, "must be"); + return Op_AddVF; + case Op_AddD: + assert(bt == T_DOUBLE, "must be"); + return Op_AddVD; + case Op_SubI: + switch (bt) { + case T_BOOLEAN: + case T_BYTE: return Op_SubVB; + case T_CHAR: return Op_SubVC; + case T_SHORT: return Op_SubVS; + case T_INT: return Op_SubVI; + } + ShouldNotReachHere(); + case Op_SubL: + assert(bt == T_LONG, "must be"); + return Op_SubVL; + case Op_SubF: + assert(bt == T_FLOAT, "must be"); + return Op_SubVF; + case Op_SubD: + assert(bt == T_DOUBLE, "must be"); + return Op_SubVD; + case Op_MulF: + assert(bt == T_FLOAT, "must be"); + return Op_MulVF; + case Op_MulD: + assert(bt == T_DOUBLE, "must be"); + return Op_MulVD; + case Op_DivF: + assert(bt == T_FLOAT, "must be"); + return Op_DivVF; + case Op_DivD: + assert(bt == T_DOUBLE, "must be"); + return Op_DivVD; + case Op_LShiftI: + switch (bt) { + case T_BOOLEAN: + case T_BYTE: return Op_LShiftVB; + case T_CHAR: return Op_LShiftVC; + case T_SHORT: return Op_LShiftVS; + case T_INT: return Op_LShiftVI; + } + ShouldNotReachHere(); + case Op_URShiftI: + switch (bt) { + case T_BOOLEAN: + case T_BYTE: return Op_URShiftVB; + case T_CHAR: return Op_URShiftVC; + case T_SHORT: return Op_URShiftVS; + case T_INT: return Op_URShiftVI; + } + ShouldNotReachHere(); + case Op_AndI: + case Op_AndL: + return Op_AndV; + case Op_OrI: + case Op_OrL: + return Op_OrV; + case Op_XorI: + case Op_XorL: + return Op_XorV; + + case Op_LoadB: + case Op_LoadC: + case Op_LoadS: + case Op_LoadI: + case Op_LoadL: + case Op_LoadF: + case Op_LoadD: + return VectorLoadNode::opcode(sopc, vlen); + + case Op_StoreB: + case Op_StoreC: + case Op_StoreI: + case Op_StoreL: + case Op_StoreF: + case Op_StoreD: + return VectorStoreNode::opcode(sopc, vlen); + } + return 0; // Unimplemented +} + +// Helper for above. +int VectorLoadNode::opcode(int sopc, uint vlen) { + switch (sopc) { + case Op_LoadB: + switch (vlen) { + case 2: return 0; // Unimplemented + case 4: return Op_Load4B; + case 8: return Op_Load8B; + case 16: return Op_Load16B; + } + break; + case Op_LoadC: + switch (vlen) { + case 2: return Op_Load2C; + case 4: return Op_Load4C; + case 8: return Op_Load8C; + } + break; + case Op_LoadS: + switch (vlen) { + case 2: return Op_Load2S; + case 4: return Op_Load4S; + case 8: return Op_Load8S; + } + break; + case Op_LoadI: + switch (vlen) { + case 2: return Op_Load2I; + case 4: return Op_Load4I; + } + break; + case Op_LoadL: + if (vlen == 2) return Op_Load2L; + break; + case Op_LoadF: + switch (vlen) { + case 2: return Op_Load2F; + case 4: return Op_Load4F; + } + break; + case Op_LoadD: + if (vlen == 2) return Op_Load2D; + break; + } + return 0; // Unimplemented +} + +// Helper for above +int VectorStoreNode::opcode(int sopc, uint vlen) { + switch (sopc) { + case Op_StoreB: + switch (vlen) { + case 2: return 0; // Unimplemented + case 4: return Op_Store4B; + case 8: return Op_Store8B; + case 16: return Op_Store16B; + } + break; + case Op_StoreC: + switch (vlen) { + case 2: return Op_Store2C; + case 4: return Op_Store4C; + case 8: return Op_Store8C; + } + break; + case Op_StoreI: + switch (vlen) { + case 2: return Op_Store2I; + case 4: return Op_Store4I; + } + break; + case Op_StoreL: + if (vlen == 2) return Op_Store2L; + break; + case Op_StoreF: + switch (vlen) { + case 2: return Op_Store2F; + case 4: return Op_Store4F; + } + break; + case Op_StoreD: + if (vlen == 2) return Op_Store2D; + break; + } + return 0; // Unimplemented +} + +// Return the vector version of a scalar operation node. +VectorNode* VectorNode::make(Compile* C, int sopc, Node* n1, Node* n2, uint vlen, const Type* opd_t) { + int vopc = opcode(sopc, vlen, opd_t); + + switch (vopc) { + case Op_AddVB: return new (C, 3) AddVBNode(n1, n2, vlen); + case Op_AddVC: return new (C, 3) AddVCNode(n1, n2, vlen); + case Op_AddVS: return new (C, 3) AddVSNode(n1, n2, vlen); + case Op_AddVI: return new (C, 3) AddVINode(n1, n2, vlen); + case Op_AddVL: return new (C, 3) AddVLNode(n1, n2, vlen); + case Op_AddVF: return new (C, 3) AddVFNode(n1, n2, vlen); + case Op_AddVD: return new (C, 3) AddVDNode(n1, n2, vlen); + + case Op_SubVB: return new (C, 3) SubVBNode(n1, n2, vlen); + case Op_SubVC: return new (C, 3) SubVCNode(n1, n2, vlen); + case Op_SubVS: return new (C, 3) SubVSNode(n1, n2, vlen); + case Op_SubVI: return new (C, 3) SubVINode(n1, n2, vlen); + case Op_SubVL: return new (C, 3) SubVLNode(n1, n2, vlen); + case Op_SubVF: return new (C, 3) SubVFNode(n1, n2, vlen); + case Op_SubVD: return new (C, 3) SubVDNode(n1, n2, vlen); + + case Op_MulVF: return new (C, 3) MulVFNode(n1, n2, vlen); + case Op_MulVD: return new (C, 3) MulVDNode(n1, n2, vlen); + + case Op_DivVF: return new (C, 3) DivVFNode(n1, n2, vlen); + case Op_DivVD: return new (C, 3) DivVDNode(n1, n2, vlen); + + case Op_LShiftVB: return new (C, 3) LShiftVBNode(n1, n2, vlen); + case Op_LShiftVC: return new (C, 3) LShiftVCNode(n1, n2, vlen); + case Op_LShiftVS: return new (C, 3) LShiftVSNode(n1, n2, vlen); + case Op_LShiftVI: return new (C, 3) LShiftVINode(n1, n2, vlen); + + case Op_URShiftVB: return new (C, 3) URShiftVBNode(n1, n2, vlen); + case Op_URShiftVC: return new (C, 3) URShiftVCNode(n1, n2, vlen); + case Op_URShiftVS: return new (C, 3) URShiftVSNode(n1, n2, vlen); + case Op_URShiftVI: return new (C, 3) URShiftVINode(n1, n2, vlen); + + case Op_AndV: return new (C, 3) AndVNode(n1, n2, vlen, opd_t->array_element_basic_type()); + case Op_OrV: return new (C, 3) OrVNode (n1, n2, vlen, opd_t->array_element_basic_type()); + case Op_XorV: return new (C, 3) XorVNode(n1, n2, vlen, opd_t->array_element_basic_type()); + } + ShouldNotReachHere(); + return NULL; +} + +// Return the vector version of a scalar load node. +VectorLoadNode* VectorLoadNode::make(Compile* C, int opc, Node* ctl, Node* mem, + Node* adr, const TypePtr* atyp, uint vlen) { + int vopc = opcode(opc, vlen); + + switch(vopc) { + case Op_Load16B: return new (C, 3) Load16BNode(ctl, mem, adr, atyp); + case Op_Load8B: return new (C, 3) Load8BNode(ctl, mem, adr, atyp); + case Op_Load4B: return new (C, 3) Load4BNode(ctl, mem, adr, atyp); + + case Op_Load8C: return new (C, 3) Load8CNode(ctl, mem, adr, atyp); + case Op_Load4C: return new (C, 3) Load4CNode(ctl, mem, adr, atyp); + case Op_Load2C: return new (C, 3) Load2CNode(ctl, mem, adr, atyp); + + case Op_Load8S: return new (C, 3) Load8SNode(ctl, mem, adr, atyp); + case Op_Load4S: return new (C, 3) Load4SNode(ctl, mem, adr, atyp); + case Op_Load2S: return new (C, 3) Load2SNode(ctl, mem, adr, atyp); + + case Op_Load4I: return new (C, 3) Load4INode(ctl, mem, adr, atyp); + case Op_Load2I: return new (C, 3) Load2INode(ctl, mem, adr, atyp); + + case Op_Load2L: return new (C, 3) Load2LNode(ctl, mem, adr, atyp); + + case Op_Load4F: return new (C, 3) Load4FNode(ctl, mem, adr, atyp); + case Op_Load2F: return new (C, 3) Load2FNode(ctl, mem, adr, atyp); + + case Op_Load2D: return new (C, 3) Load2DNode(ctl, mem, adr, atyp); + } + ShouldNotReachHere(); + return NULL; +} + +// Return the vector version of a scalar store node. +VectorStoreNode* VectorStoreNode::make(Compile* C, int opc, Node* ctl, Node* mem, + Node* adr, const TypePtr* atyp, VectorNode* val, + uint vlen) { + int vopc = opcode(opc, vlen); + + switch(vopc) { + case Op_Store16B: return new (C, 4) Store16BNode(ctl, mem, adr, atyp, val); + case Op_Store8B: return new (C, 4) Store8BNode(ctl, mem, adr, atyp, val); + case Op_Store4B: return new (C, 4) Store4BNode(ctl, mem, adr, atyp, val); + + case Op_Store8C: return new (C, 4) Store8CNode(ctl, mem, adr, atyp, val); + case Op_Store4C: return new (C, 4) Store4CNode(ctl, mem, adr, atyp, val); + case Op_Store2C: return new (C, 4) Store2CNode(ctl, mem, adr, atyp, val); + + case Op_Store4I: return new (C, 4) Store4INode(ctl, mem, adr, atyp, val); + case Op_Store2I: return new (C, 4) Store2INode(ctl, mem, adr, atyp, val); + + case Op_Store2L: return new (C, 4) Store2LNode(ctl, mem, adr, atyp, val); + + case Op_Store4F: return new (C, 4) Store4FNode(ctl, mem, adr, atyp, val); + case Op_Store2F: return new (C, 4) Store2FNode(ctl, mem, adr, atyp, val); + + case Op_Store2D: return new (C, 4) Store2DNode(ctl, mem, adr, atyp, val); + } + ShouldNotReachHere(); + return NULL; +} + +// Extract a scalar element of vector. +Node* ExtractNode::make(Compile* C, Node* v, uint position, const Type* opd_t) { + BasicType bt = opd_t->array_element_basic_type(); + assert(position < VectorNode::max_vlen(bt), "pos in range"); + ConINode* pos = ConINode::make(C, (int)position); + switch (bt) { + case T_BOOLEAN: + case T_BYTE: + return new (C, 3) ExtractBNode(v, pos); + case T_CHAR: + return new (C, 3) ExtractCNode(v, pos); + case T_SHORT: + return new (C, 3) ExtractSNode(v, pos); + case T_INT: + return new (C, 3) ExtractINode(v, pos); + case T_LONG: + return new (C, 3) ExtractLNode(v, pos); + case T_FLOAT: + return new (C, 3) ExtractFNode(v, pos); + case T_DOUBLE: + return new (C, 3) ExtractDNode(v, pos); + } + ShouldNotReachHere(); + return NULL; +} diff --git a/hotspot/src/share/vm/opto/vectornode.hpp b/hotspot/src/share/vm/opto/vectornode.hpp new file mode 100644 index 00000000000..c0638677780 --- /dev/null +++ b/hotspot/src/share/vm/opto/vectornode.hpp @@ -0,0 +1,1134 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +//------------------------------VectorNode-------------------------------------- +// Vector Operation +class VectorNode : public Node { + protected: + uint _length; // vector length + virtual BasicType elt_basic_type() const = 0; // Vector element basic type + + static const Type* vect_type(BasicType elt_bt, uint len); + static const Type* vect_type(const Type* elt_type, uint len) { + return vect_type(elt_type->array_element_basic_type(), len); + } + + public: + friend class VectorLoadNode; // For vect_type + friend class VectorStoreNode; // ditto. + + VectorNode(Node* n1, uint vlen) : Node(NULL, n1), _length(vlen) { + init_flags(Flag_is_Vector); + } + VectorNode(Node* n1, Node* n2, uint vlen) : Node(NULL, n1, n2), _length(vlen) { + init_flags(Flag_is_Vector); + } + virtual int Opcode() const; + + uint length() const { return _length; } // Vector length + + static uint max_vlen(BasicType bt) { // max vector length + return (uint)(Matcher::vector_width_in_bytes() / type2aelembytes[bt]); + } + + // Element and vector type + const Type* elt_type() const { return Type::get_const_basic_type(elt_basic_type()); } + const Type* vect_type() const { return vect_type(elt_basic_type(), length()); } + + virtual const Type *bottom_type() const { return vect_type(); } + virtual uint ideal_reg() const { return Matcher::vector_ideal_reg(); } + + // Vector opcode from scalar opcode + static int opcode(int sopc, uint vlen, const Type* opd_t); + + static VectorNode* scalar2vector(Compile* C, Node* s, uint vlen, const Type* opd_t); + + static VectorNode* make(Compile* C, int sopc, Node* n1, Node* n2, uint vlen, const Type* elt_t); + +}; + +//===========================Vector=ALU=Operations==================================== + +//------------------------------AddVBNode--------------------------------------- +// Vector add byte +class AddVBNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + AddVBNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------AddVCNode--------------------------------------- +// Vector add char +class AddVCNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + AddVCNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------AddVSNode--------------------------------------- +// Vector add short +class AddVSNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + AddVSNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------AddVINode--------------------------------------- +// Vector add int +class AddVINode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + AddVINode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------AddVLNode--------------------------------------- +// Vector add long +class AddVLNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_LONG; } + public: + AddVLNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------AddVFNode--------------------------------------- +// Vector add float +class AddVFNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + AddVFNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------AddVDNode--------------------------------------- +// Vector add double +class AddVDNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_DOUBLE; } + public: + AddVDNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------SubVBNode--------------------------------------- +// Vector subtract byte +class SubVBNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + SubVBNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------SubVCNode--------------------------------------- +// Vector subtract char +class SubVCNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + SubVCNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------SubVSNode--------------------------------------- +// Vector subtract short +class SubVSNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + SubVSNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------SubVINode--------------------------------------- +// Vector subtract int +class SubVINode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + SubVINode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------SubVLNode--------------------------------------- +// Vector subtract long +class SubVLNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_LONG; } + public: + SubVLNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------SubVFNode--------------------------------------- +// Vector subtract float +class SubVFNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + SubVFNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------SubVDNode--------------------------------------- +// Vector subtract double +class SubVDNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_DOUBLE; } + public: + SubVDNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------MulVFNode--------------------------------------- +// Vector multiply float +class MulVFNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + MulVFNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------MulVDNode--------------------------------------- +// Vector multiply double +class MulVDNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_DOUBLE; } + public: + MulVDNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------DivVFNode--------------------------------------- +// Vector divide float +class DivVFNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + DivVFNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------DivVDNode--------------------------------------- +// Vector Divide double +class DivVDNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_DOUBLE; } + public: + DivVDNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------LShiftVBNode--------------------------------------- +// Vector lshift byte +class LShiftVBNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + LShiftVBNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------LShiftVCNode--------------------------------------- +// Vector lshift chars +class LShiftVCNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + LShiftVCNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------LShiftVSNode--------------------------------------- +// Vector lshift shorts +class LShiftVSNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + LShiftVSNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------LShiftVINode--------------------------------------- +// Vector lshift ints +class LShiftVINode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + LShiftVINode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------URShiftVBNode--------------------------------------- +// Vector urshift bytes +class URShiftVBNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + URShiftVBNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------URShiftVCNode--------------------------------------- +// Vector urshift char +class URShiftVCNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + URShiftVCNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------URShiftVSNode--------------------------------------- +// Vector urshift shorts +class URShiftVSNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + URShiftVSNode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------URShiftVINode--------------------------------------- +// Vector urshift ints +class URShiftVINode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + URShiftVINode(Node* in1, Node* in2, uint vlen) : VectorNode(in1,in2,vlen) {} + virtual int Opcode() const; +}; + +//------------------------------AndVNode--------------------------------------- +// Vector and +class AndVNode : public VectorNode { + protected: + BasicType _bt; + virtual BasicType elt_basic_type() const { return _bt; } + public: + AndVNode(Node* in1, Node* in2, uint vlen, BasicType bt) : VectorNode(in1,in2,vlen), _bt(bt) {} + virtual int Opcode() const; +}; + +//------------------------------OrVNode--------------------------------------- +// Vector or +class OrVNode : public VectorNode { + protected: + BasicType _bt; + virtual BasicType elt_basic_type() const { return _bt; } + public: + OrVNode(Node* in1, Node* in2, uint vlen, BasicType bt) : VectorNode(in1,in2,vlen), _bt(bt) {} + virtual int Opcode() const; +}; + +//------------------------------XorVNode--------------------------------------- +// Vector xor +class XorVNode : public VectorNode { + protected: + BasicType _bt; + virtual BasicType elt_basic_type() const { return _bt; } + public: + XorVNode(Node* in1, Node* in2, uint vlen, BasicType bt) : VectorNode(in1,in2,vlen), _bt(bt) {} + virtual int Opcode() const; +}; + +//================================= M E M O R Y ================================== + + +//------------------------------VectorLoadNode-------------------------------------- +// Vector Load from memory +class VectorLoadNode : public LoadNode { + virtual uint size_of() const { return sizeof(*this); } + + protected: + virtual BasicType elt_basic_type() const = 0; // Vector element basic type + // For use in constructor + static const Type* vect_type(const Type* elt_type, uint len) { + return VectorNode::vect_type(elt_type, len); + } + + public: + VectorLoadNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const Type *rt) + : LoadNode(c,mem,adr,at,rt) { + init_flags(Flag_is_Vector); + } + virtual int Opcode() const; + + virtual uint length() const = 0; // Vector length + + // Element and vector type + const Type* elt_type() const { return Type::get_const_basic_type(elt_basic_type()); } + const Type* vect_type() const { return VectorNode::vect_type(elt_basic_type(), length()); } + + virtual uint ideal_reg() const { return Matcher::vector_ideal_reg(); } + virtual BasicType memory_type() const { return T_VOID; } + virtual int memory_size() const { return length()*type2aelembytes[elt_basic_type()]; } + + // Vector opcode from scalar opcode + static int opcode(int sopc, uint vlen); + + static VectorLoadNode* make(Compile* C, int opc, Node* ctl, Node* mem, + Node* adr, const TypePtr* atyp, uint vlen); +}; + +//------------------------------Load16BNode-------------------------------------- +// Vector load of 16 bytes (8bits signed) from memory +class Load16BNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + Load16BNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::BYTE) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,16)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store16B; } + virtual uint length() const { return 16; } +}; + +//------------------------------Load8BNode-------------------------------------- +// Vector load of 8 bytes (8bits signed) from memory +class Load8BNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + Load8BNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::BYTE) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,8)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store8B; } + virtual uint length() const { return 8; } +}; + +//------------------------------Load4BNode-------------------------------------- +// Vector load of 4 bytes (8bits signed) from memory +class Load4BNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + Load4BNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::BYTE) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,4)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store4B; } + virtual uint length() const { return 4; } +}; + +//------------------------------Load8CNode-------------------------------------- +// Vector load of 8 chars (16bits unsigned) from memory +class Load8CNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + Load8CNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::CHAR) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,8)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store8C; } + virtual uint length() const { return 8; } +}; + +//------------------------------Load4CNode-------------------------------------- +// Vector load of 4 chars (16bits unsigned) from memory +class Load4CNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + Load4CNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::CHAR) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,4)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store4C; } + virtual uint length() const { return 4; } +}; + +//------------------------------Load2CNode-------------------------------------- +// Vector load of 2 chars (16bits unsigned) from memory +class Load2CNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + Load2CNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::CHAR) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,2)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store2C; } + virtual uint length() const { return 2; } +}; + +//------------------------------Load8SNode-------------------------------------- +// Vector load of 8 shorts (16bits signed) from memory +class Load8SNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + Load8SNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::SHORT) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,8)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store8C; } + virtual uint length() const { return 8; } +}; + +//------------------------------Load4SNode-------------------------------------- +// Vector load of 4 shorts (16bits signed) from memory +class Load4SNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + Load4SNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::SHORT) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,4)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store4C; } + virtual uint length() const { return 4; } +}; + +//------------------------------Load2SNode-------------------------------------- +// Vector load of 2 shorts (16bits signed) from memory +class Load2SNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + Load2SNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::SHORT) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,2)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store2C; } + virtual uint length() const { return 2; } +}; + +//------------------------------Load4INode-------------------------------------- +// Vector load of 4 integers (32bits signed) from memory +class Load4INode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + Load4INode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::INT) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,4)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store4I; } + virtual uint length() const { return 4; } +}; + +//------------------------------Load2INode-------------------------------------- +// Vector load of 2 integers (32bits signed) from memory +class Load2INode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + Load2INode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt *ti = TypeInt::INT) + : VectorLoadNode(c,mem,adr,at,vect_type(ti,2)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store2I; } + virtual uint length() const { return 2; } +}; + +//------------------------------Load2LNode-------------------------------------- +// Vector load of 2 longs (64bits signed) from memory +class Load2LNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_LONG; } + public: + Load2LNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeLong *tl = TypeLong::LONG) + : VectorLoadNode(c,mem,adr,at,vect_type(tl,2)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store2L; } + virtual uint length() const { return 2; } +}; + +//------------------------------Load4FNode-------------------------------------- +// Vector load of 4 floats (32bits) from memory +class Load4FNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + Load4FNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const Type *t = Type::FLOAT) + : VectorLoadNode(c,mem,adr,at,vect_type(t,4)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store4F; } + virtual uint length() const { return 4; } +}; + +//------------------------------Load2FNode-------------------------------------- +// Vector load of 2 floats (32bits) from memory +class Load2FNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + Load2FNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const Type *t = Type::FLOAT) + : VectorLoadNode(c,mem,adr,at,vect_type(t,2)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store2F; } + virtual uint length() const { return 2; } +}; + +//------------------------------Load2DNode-------------------------------------- +// Vector load of 2 doubles (64bits) from memory +class Load2DNode : public VectorLoadNode { + protected: + virtual BasicType elt_basic_type() const { return T_DOUBLE; } + public: + Load2DNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const Type *t = Type::DOUBLE) + : VectorLoadNode(c,mem,adr,at,vect_type(t,2)) {} + virtual int Opcode() const; + virtual int store_Opcode() const { return Op_Store2D; } + virtual uint length() const { return 2; } +}; + + +//------------------------------VectorStoreNode-------------------------------------- +// Vector Store to memory +class VectorStoreNode : public StoreNode { + virtual uint size_of() const { return sizeof(*this); } + + protected: + virtual BasicType elt_basic_type() const = 0; // Vector element basic type + + public: + VectorStoreNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : StoreNode(c,mem,adr,at,val) { + init_flags(Flag_is_Vector); + } + virtual int Opcode() const; + + virtual uint length() const = 0; // Vector length + + // Element and vector type + const Type* elt_type() const { return Type::get_const_basic_type(elt_basic_type()); } + const Type* vect_type() const { return VectorNode::vect_type(elt_basic_type(), length()); } + + virtual uint ideal_reg() const { return Matcher::vector_ideal_reg(); } + virtual BasicType memory_type() const { return T_VOID; } + virtual int memory_size() const { return length()*type2aelembytes[elt_basic_type()]; } + + // Vector opcode from scalar opcode + static int opcode(int sopc, uint vlen); + + static VectorStoreNode* make(Compile* C, int opc, Node* ctl, Node* mem, + Node* adr, const TypePtr* atyp, VectorNode* val, + uint vlen); +}; + +//------------------------------Store16BNode-------------------------------------- +// Vector store of 16 bytes (8bits signed) to memory +class Store16BNode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + Store16BNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 16; } +}; + +//------------------------------Store8BNode-------------------------------------- +// Vector store of 8 bytes (8bits signed) to memory +class Store8BNode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + Store8BNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 8; } +}; + +//------------------------------Store4BNode-------------------------------------- +// Vector store of 4 bytes (8bits signed) to memory +class Store4BNode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + Store4BNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 4; } +}; + +//------------------------------Store8CNode-------------------------------------- +// Vector store of 8 chars (16bits signed/unsigned) to memory +class Store8CNode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + Store8CNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 8; } +}; + +//------------------------------Store4CNode-------------------------------------- +// Vector store of 4 chars (16bits signed/unsigned) to memory +class Store4CNode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + Store4CNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 4; } +}; + +//------------------------------Store2CNode-------------------------------------- +// Vector store of 2 chars (16bits signed/unsigned) to memory +class Store2CNode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + Store2CNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 2; } +}; + +//------------------------------Store4INode-------------------------------------- +// Vector store of 4 integers (32bits signed) to memory +class Store4INode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + Store4INode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 4; } +}; + +//------------------------------Store2INode-------------------------------------- +// Vector store of 2 integers (32bits signed) to memory +class Store2INode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + Store2INode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 2; } +}; + +//------------------------------Store2LNode-------------------------------------- +// Vector store of 2 longs (64bits signed) to memory +class Store2LNode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_LONG; } + public: + Store2LNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 2; } +}; + +//------------------------------Store4FNode-------------------------------------- +// Vector store of 4 floats (32bits) to memory +class Store4FNode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + Store4FNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 4; } +}; + +//------------------------------Store2FNode-------------------------------------- +// Vector store of 2 floats (32bits) to memory +class Store2FNode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + Store2FNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 2; } +}; + +//------------------------------Store2DNode-------------------------------------- +// Vector store of 2 doubles (64bits) to memory +class Store2DNode : public VectorStoreNode { + protected: + virtual BasicType elt_basic_type() const { return T_DOUBLE; } + public: + Store2DNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) + : VectorStoreNode(c,mem,adr,at,val) {} + virtual int Opcode() const; + virtual uint length() const { return 2; } +}; + +//=========================Promote_Scalar_to_Vector==================================== + +//------------------------------Replicate16BNode--------------------------------------- +// Replicate byte scalar to be vector of 16 bytes +class Replicate16BNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + Replicate16BNode(Node* in1) : VectorNode(in1, 16) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate8BNode--------------------------------------- +// Replicate byte scalar to be vector of 8 bytes +class Replicate8BNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + Replicate8BNode(Node* in1) : VectorNode(in1, 8) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate4BNode--------------------------------------- +// Replicate byte scalar to be vector of 4 bytes +class Replicate4BNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + Replicate4BNode(Node* in1) : VectorNode(in1, 4) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate8CNode--------------------------------------- +// Replicate char scalar to be vector of 8 chars +class Replicate8CNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + Replicate8CNode(Node* in1) : VectorNode(in1, 8) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate4CNode--------------------------------------- +// Replicate char scalar to be vector of 4 chars +class Replicate4CNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + Replicate4CNode(Node* in1) : VectorNode(in1, 4) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate2CNode--------------------------------------- +// Replicate char scalar to be vector of 2 chars +class Replicate2CNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + Replicate2CNode(Node* in1) : VectorNode(in1, 2) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate8SNode--------------------------------------- +// Replicate short scalar to be vector of 8 shorts +class Replicate8SNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + Replicate8SNode(Node* in1) : VectorNode(in1, 8) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate4SNode--------------------------------------- +// Replicate short scalar to be vector of 4 shorts +class Replicate4SNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + Replicate4SNode(Node* in1) : VectorNode(in1, 4) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate2SNode--------------------------------------- +// Replicate short scalar to be vector of 2 shorts +class Replicate2SNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + Replicate2SNode(Node* in1) : VectorNode(in1, 2) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate4INode--------------------------------------- +// Replicate int scalar to be vector of 4 ints +class Replicate4INode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + Replicate4INode(Node* in1) : VectorNode(in1, 4) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate2INode--------------------------------------- +// Replicate int scalar to be vector of 2 ints +class Replicate2INode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + Replicate2INode(Node* in1) : VectorNode(in1, 2) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate2LNode--------------------------------------- +// Replicate long scalar to be vector of 2 longs +class Replicate2LNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_LONG; } + public: + Replicate2LNode(Node* in1) : VectorNode(in1, 2) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate4FNode--------------------------------------- +// Replicate float scalar to be vector of 4 floats +class Replicate4FNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + Replicate4FNode(Node* in1) : VectorNode(in1, 4) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate2FNode--------------------------------------- +// Replicate float scalar to be vector of 2 floats +class Replicate2FNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + Replicate2FNode(Node* in1) : VectorNode(in1, 2) {} + virtual int Opcode() const; +}; + +//------------------------------Replicate2DNode--------------------------------------- +// Replicate double scalar to be vector of 2 doubles +class Replicate2DNode : public VectorNode { + protected: + virtual BasicType elt_basic_type() const { return T_DOUBLE; } + public: + Replicate2DNode(Node* in1) : VectorNode(in1, 2) {} + virtual int Opcode() const; +}; + +//========================Pack_Scalars_into_a_Vector============================== + +//------------------------------PackNode--------------------------------------- +// Pack parent class (not for code generation). +class PackNode : public VectorNode { + public: + PackNode(Node* in1) : VectorNode(in1, 1) {} + PackNode(Node* in1, Node* n2) : VectorNode(in1, n2, 2) {} + virtual int Opcode() const; + + void add_opd(Node* n) { + add_req(n); + _length++; + assert(_length == req() - 1, "vector length matches edge count"); + } + + // Create a binary tree form for Packs. [lo, hi) (half-open) range + Node* binaryTreePack(Compile* C, int lo, int hi); + + static PackNode* make(Compile* C, Node* s, const Type* elt_t); +}; + +//------------------------------PackBNode--------------------------------------- +// Pack byte scalars into vector +class PackBNode : public PackNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + PackBNode(Node* in1) : PackNode(in1) {} + virtual int Opcode() const; +}; + +//------------------------------PackCNode--------------------------------------- +// Pack char scalars into vector +class PackCNode : public PackNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + PackCNode(Node* in1) : PackNode(in1) {} + virtual int Opcode() const; +}; + +//------------------------------PackSNode--------------------------------------- +// Pack short scalars into a vector +class PackSNode : public PackNode { + protected: + virtual BasicType elt_basic_type() const { return T_SHORT; } + public: + PackSNode(Node* in1) : PackNode(in1) {} + virtual int Opcode() const; +}; + +//------------------------------PackINode--------------------------------------- +// Pack integer scalars into a vector +class PackINode : public PackNode { + protected: + virtual BasicType elt_basic_type() const { return T_INT; } + public: + PackINode(Node* in1) : PackNode(in1) {} + PackINode(Node* in1, Node* in2) : PackNode(in1, in2) {} + virtual int Opcode() const; +}; + +//------------------------------PackLNode--------------------------------------- +// Pack long scalars into a vector +class PackLNode : public PackNode { + protected: + virtual BasicType elt_basic_type() const { return T_LONG; } + public: + PackLNode(Node* in1) : PackNode(in1) {} + PackLNode(Node* in1, Node* in2) : PackNode(in1, in2) {} + virtual int Opcode() const; +}; + +//------------------------------PackFNode--------------------------------------- +// Pack float scalars into vector +class PackFNode : public PackNode { + protected: + virtual BasicType elt_basic_type() const { return T_FLOAT; } + public: + PackFNode(Node* in1) : PackNode(in1) {} + PackFNode(Node* in1, Node* in2) : PackNode(in1, in2) {} + virtual int Opcode() const; +}; + +//------------------------------PackDNode--------------------------------------- +// Pack double scalars into a vector +class PackDNode : public PackNode { + protected: + virtual BasicType elt_basic_type() const { return T_DOUBLE; } + public: + PackDNode(Node* in1) : PackNode(in1) {} + PackDNode(Node* in1, Node* in2) : PackNode(in1, in2) {} + virtual int Opcode() const; +}; + +// The Pack2xN nodes assist code generation. They are created from +// Pack4C, etc. nodes in final_graph_reshape in the form of a +// balanced, binary tree. + +//------------------------------Pack2x1BNode----------------------------------------- +// Pack 2 1-byte integers into vector of 2 bytes +class Pack2x1BNode : public PackNode { + protected: + virtual BasicType elt_basic_type() const { return T_BYTE; } + public: + Pack2x1BNode(Node *in1, Node* in2) : PackNode(in1, in2) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------Pack2x2BNode--------------------------------------- +// Pack 2 2-byte integers into vector of 4 bytes +class Pack2x2BNode : public PackNode { + protected: + virtual BasicType elt_basic_type() const { return T_CHAR; } + public: + Pack2x2BNode(Node *in1, Node* in2) : PackNode(in1, in2) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//========================Extract_Scalar_from_Vector=============================== + +//------------------------------ExtractNode--------------------------------------- +// Extract a scalar from a vector at position "pos" +class ExtractNode : public Node { + public: + ExtractNode(Node* src, ConINode* pos) : Node(NULL, src, (Node*)pos) { + assert(in(2)->get_int() >= 0, "positive constants"); + } + virtual int Opcode() const; + uint pos() const { return in(2)->get_int(); } + + static Node* make(Compile* C, Node* v, uint position, const Type* opd_t); +}; + +//------------------------------ExtractBNode--------------------------------------- +// Extract a byte from a vector at position "pos" +class ExtractBNode : public ExtractNode { + public: + ExtractBNode(Node* src, ConINode* pos) : ExtractNode(src, pos) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------ExtractCNode--------------------------------------- +// Extract a char from a vector at position "pos" +class ExtractCNode : public ExtractNode { + public: + ExtractCNode(Node* src, ConINode* pos) : ExtractNode(src, pos) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------ExtractSNode--------------------------------------- +// Extract a short from a vector at position "pos" +class ExtractSNode : public ExtractNode { + public: + ExtractSNode(Node* src, ConINode* pos) : ExtractNode(src, pos) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------ExtractINode--------------------------------------- +// Extract an int from a vector at position "pos" +class ExtractINode : public ExtractNode { + public: + ExtractINode(Node* src, ConINode* pos) : ExtractNode(src, pos) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------ExtractLNode--------------------------------------- +// Extract a long from a vector at position "pos" +class ExtractLNode : public ExtractNode { + public: + ExtractLNode(Node* src, ConINode* pos) : ExtractNode(src, pos) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------ExtractFNode--------------------------------------- +// Extract a float from a vector at position "pos" +class ExtractFNode : public ExtractNode { + public: + ExtractFNode(Node* src, ConINode* pos) : ExtractNode(src, pos) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------ExtractDNode--------------------------------------- +// Extract a double from a vector at position "pos" +class ExtractDNode : public ExtractNode { + public: + ExtractDNode(Node* src, ConINode* pos) : ExtractNode(src, pos) {} + virtual int Opcode() const; + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; diff --git a/hotspot/src/share/vm/prims/evmCompat.cpp b/hotspot/src/share/vm/prims/evmCompat.cpp new file mode 100644 index 00000000000..673e6617c2b --- /dev/null +++ b/hotspot/src/share/vm/prims/evmCompat.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file contains definitions for functions that exist +// in the ExactVM, but not in HotSpot. They are stubbed out +// here to prevent linker errors when attempting to use HotSpot +// with the ExactVM jdk. + +# include "incls/_precompiled.incl" +# include "incls/_evmCompat.cpp.incl" + +extern "C" void JVM_Process_DestroyProcess(void); +extern "C" void JVM_Process_ForkAndExec(void); +extern "C" void JVM_Process_WaitForProcessExit(void); +extern "C" void gc(void); + +void JVM_Process_DestroyProcess(void) { + ShouldNotReachHere(); +} + +void JVM_Process_ForkAndExec(void) { + ShouldNotReachHere(); +} + +void JVM_Process_WaitForProcessExit(void) { + ShouldNotReachHere(); +} + +void gc(void) { + ShouldNotReachHere(); +} diff --git a/hotspot/src/share/vm/prims/forte.cpp b/hotspot/src/share/vm/prims/forte.cpp new file mode 100644 index 00000000000..efda11f6a7a --- /dev/null +++ b/hotspot/src/share/vm/prims/forte.cpp @@ -0,0 +1,895 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_forte.cpp.incl" + + +//------------------------------------------------------- + +// Native interfaces for use by Forte tools. + + +#ifndef IA64 + +class vframeStreamForte : public vframeStreamCommon { + public: + // constructor that starts with sender of frame fr (top_frame) + vframeStreamForte(JavaThread *jt, frame fr, bool stop_at_java_call_stub); + void forte_next(); +}; + + +static void forte_is_walkable_compiled_frame(frame* fr, RegisterMap* map, + bool* is_compiled_p, bool* is_walkable_p); +static bool forte_is_walkable_interpreted_frame(frame* fr, + methodOop* method_p, int* bci_p); + + +// A Forte specific version of frame:safe_for_sender(). +static bool forte_safe_for_sender(frame* fr, JavaThread *thread) { + bool ret_value = false; // be pessimistic + +#ifdef COMPILER2 +#if defined(IA32) || defined(AMD64) + { + // This check is the same as the standard safe_for_sender() + // on IA32 or AMD64 except that NULL FP values are tolerated + // for C2. + address sp = (address)fr->sp(); + address fp = (address)fr->fp(); + ret_value = sp != NULL && sp <= thread->stack_base() && + sp >= thread->stack_base() - thread->stack_size() && + (fp == NULL || (fp <= thread->stack_base() && + fp >= thread->stack_base() - thread->stack_size())); + + // We used to use standard safe_for_sender() when we are supposed + // to be executing Java code. However, that prevents us from + // walking some intrinsic stacks so now we have to be more refined. + // If we passed the above check and we have a NULL frame pointer + // and we are supposed to be executing Java code, then we have a + // couple of more checks to make. + if (ret_value && fp == NULL && (thread->thread_state() == _thread_in_Java + || thread->thread_state() == _thread_in_Java_trans)) { + + if (fr->is_interpreted_frame()) { + // interpreted frames don't really have a NULL frame pointer + return false; + } else if (CodeCache::find_blob(fr->pc()) == NULL) { + // the NULL frame pointer should be associated with generated code + return false; + } + } + } + +#else // !(IA32 || AMD64) + ret_value = fr->safe_for_sender(thread); +#endif // IA32 || AMD64 + +#else // !COMPILER2 + ret_value = fr->safe_for_sender(thread); +#endif // COMPILER2 + + if (!ret_value) { + return ret_value; // not safe, nothing more to do + } + + address sp1; + +#ifdef SPARC + // On Solaris SPARC, when a compiler frame has an interpreted callee + // the _interpreter_sp_adjustment field contains the adjustment to + // this frame's SP made by that interpreted callee. + // For AsyncGetCallTrace(), we need to verify that the resulting SP + // is valid for the specified thread's stack. + sp1 = (address)fr->sp(); + address sp2 = (address)fr->unextended_sp(); + + // If the second SP is NULL, then the _interpreter_sp_adjustment + // field simply adjusts this frame's SP to NULL and the frame is + // not safe. This strange value can be set in the frame constructor + // when our peek into the interpreted callee's adjusted value for + // this frame's SP finds a NULL. This can happen when SIGPROF + // catches us while we are creating the interpreter frame. + // + if (sp2 == NULL || + + // If the two SPs are different, then _interpreter_sp_adjustment + // is non-zero and we need to validate the second SP. We invert + // the range check from frame::safe_for_sender() and bail out + // if the second SP is not safe. + (sp1 != sp2 && !(sp2 <= thread->stack_base() + && sp2 >= (thread->stack_base() - thread->stack_size())))) { + return false; + } +#endif // SPARC + + if (fr->is_entry_frame()) { + // This frame thinks it is an entry frame; we need to validate + // the JavaCallWrapper pointer. + // Note: frame::entry_frame_is_first() assumes that the + // JavaCallWrapper has a non-NULL _anchor field. We don't + // check that here (yet) since we've never seen a failure + // due to a NULL _anchor field. + // Update: Originally this check was done only for SPARC. However, + // this failure has now been seen on C2 C86. I have no reason to + // believe that this is not a general issue so I'm enabling the + // check for all compilers on all supported platforms. +#ifdef COMPILER2 +#if defined(IA32) || defined(AMD64) + if (fr->fp() == NULL) { + // C2 X86 allows NULL frame pointers, but if we have one then + // we cannot call entry_frame_call_wrapper(). + return false; + } +#endif // IA32 || AMD64 +#endif // COMPILER2 + + sp1 = (address)fr->entry_frame_call_wrapper(); + // We invert the range check from frame::safe_for_sender() and + // bail out if the JavaCallWrapper * is not safe. + if (!(sp1 <= thread->stack_base() + && sp1 >= (thread->stack_base() - thread->stack_size()))) { + return false; + } + } + + return ret_value; +} + + +// Unknown compiled frames have caused assertion failures on Solaris +// X86. This code also detects unknown compiled frames on Solaris +// SPARC, but no assertion failures have been observed. However, I'm +// paranoid so I'm enabling this code whenever we have a compiler. +// +// Returns true if the specified frame is an unknown compiled frame +// and false otherwise. +static bool is_unknown_compiled_frame(frame* fr, JavaThread *thread) { + bool ret_value = false; // be optimistic + + // This failure mode only occurs when the thread is in state + // _thread_in_Java so we are okay for this check for any other + // thread state. + // + // Note: _thread_in_Java does not always mean that the thread + // is executing Java code. AsyncGetCallTrace() has caught + // threads executing in JRT_LEAF() routines when the state + // will also be _thread_in_Java. + if (thread->thread_state() != _thread_in_Java) { + return ret_value; + } + + // This failure mode only occurs with compiled frames so we are + // okay for this check for both entry and interpreted frames. + if (fr->is_entry_frame() || fr->is_interpreted_frame()) { + return ret_value; + } + + // This failure mode only occurs when the compiled frame's PC + // is in the code cache so we are okay for this check if the + // PC is not in the code cache. + CodeBlob* cb = CodeCache::find_blob(fr->pc()); + if (cb == NULL) { + return ret_value; + } + + // We have compiled code in the code cache so it is time for + // the final check: let's see if any frame type is set + ret_value = !( + // is_entry_frame() is checked above + // testers that are a subset of is_entry_frame(): + // is_first_frame() + fr->is_java_frame() + // testers that are a subset of is_java_frame(): + // is_interpreted_frame() + // is_compiled_frame() + || fr->is_native_frame() + || fr->is_runtime_frame() + || fr->is_safepoint_blob_frame() + ); + + // If there is no frame type set, then we have an unknown compiled + // frame and sender() should not be called on it. + + return ret_value; +} + +#define DebugNonSafepoints_IS_CLEARED \ + (!FLAG_IS_DEFAULT(DebugNonSafepoints) && !DebugNonSafepoints) + +// if -XX:-DebugNonSafepoints, then top-frame will be skipped +vframeStreamForte::vframeStreamForte(JavaThread *jt, frame fr, + bool stop_at_java_call_stub) : vframeStreamCommon(jt) { + _stop_at_java_call_stub = stop_at_java_call_stub; + + if (!DebugNonSafepoints_IS_CLEARED) { + // decode the top frame fully + // (usual case, if JVMTI is enabled) + _frame = fr; + } else { + // skip top frame, as it may not be at safepoint + // For AsyncGetCallTrace(), we extracted as much info from the top + // frame as we could in forte_is_walkable_frame(). We also verified + // forte_safe_for_sender() so this sender() call is safe. + _frame = fr.sender(&_reg_map); + } + + if (jt->thread_state() == _thread_in_Java && !fr.is_first_frame()) { + bool sender_check = false; // assume sender is not safe + + if (forte_safe_for_sender(&_frame, jt)) { + // If the initial sender frame is safe, then continue on with other + // checks. The unsafe sender frame has been seen on Solaris X86 + // with both Compiler1 and Compiler2. It has not been seen on + // Solaris SPARC, but seems like a good sanity check to have + // anyway. + + // SIGPROF caught us in Java code and the current frame is not the + // first frame so we should sanity check the sender frame. It is + // possible for SIGPROF to catch us in the middle of making a call. + // When that happens the current frame is actually a combination of + // the real sender and some of the new call's info. We can't find + // the real sender with such a current frame and things can get + // confused. + // + // This sanity check has caught problems with the sender frame on + // Solaris SPARC. So far Solaris X86 has not had a failure here. + sender_check = _frame.is_entry_frame() + // testers that are a subset of is_entry_frame(): + // is_first_frame() + || _frame.is_java_frame() + // testers that are a subset of is_java_frame(): + // is_interpreted_frame() + // is_compiled_frame() + || _frame.is_native_frame() + || _frame.is_runtime_frame() + || _frame.is_safepoint_blob_frame() + ; + + // We need an additional sanity check on an initial interpreted + // sender frame. This interpreted frame needs to be both walkable + // and have a valid BCI. This is yet another variant of SIGPROF + // catching us in the middle of making a call. + if (sender_check && _frame.is_interpreted_frame()) { + methodOop method = NULL; + int bci = -1; + + if (!forte_is_walkable_interpreted_frame(&_frame, &method, &bci) + || bci == -1) { + sender_check = false; + } + } + + // We need an additional sanity check on an initial compiled + // sender frame. This compiled frame also needs to be walkable. + // This is yet another variant of SIGPROF catching us in the + // middle of making a call. + if (sender_check && !_frame.is_interpreted_frame()) { + bool is_compiled, is_walkable; + + forte_is_walkable_compiled_frame(&_frame, &_reg_map, + &is_compiled, &is_walkable); + if (is_compiled && !is_walkable) { + sender_check = false; + } + } + } + + if (!sender_check) { + // nothing else to try if we can't recognize the sender + _mode = at_end_mode; + return; + } + } + + int loop_count = 0; + int loop_max = MaxJavaStackTraceDepth * 2; + + while (!fill_from_frame()) { + _frame = _frame.sender(&_reg_map); + +#ifdef COMPILER2 +#if defined(IA32) || defined(AMD64) + // Stress testing on C2 X86 has shown a periodic problem with + // the sender() call below. The initial _frame that we have on + // entry to the loop has already passed forte_safe_for_sender() + // so we only check frames after it. + if (!forte_safe_for_sender(&_frame, _thread)) { + _mode = at_end_mode; + return; + } +#endif // IA32 || AMD64 +#endif // COMPILER2 + + if (++loop_count >= loop_max) { + // We have looped more than twice the number of possible + // Java frames. This indicates that we are trying to walk + // a stack that is in the middle of being constructed and + // it is self referential. + _mode = at_end_mode; + return; + } + } +} + + +// Solaris SPARC Compiler1 needs an additional check on the grandparent +// of the top_frame when the parent of the top_frame is interpreted and +// the grandparent is compiled. However, in this method we do not know +// the relationship of the current _frame relative to the top_frame so +// we implement a more broad sanity check. When the previous callee is +// interpreted and the current sender is compiled, we verify that the +// current sender is also walkable. If it is not walkable, then we mark +// the current vframeStream as at the end. +void vframeStreamForte::forte_next() { + // handle frames with inlining + if (_mode == compiled_mode && + vframeStreamCommon::fill_in_compiled_inlined_sender()) { + return; + } + + // handle general case + + int loop_count = 0; + int loop_max = MaxJavaStackTraceDepth * 2; + + + do { + +#if defined(COMPILER1) && defined(SPARC) + bool prevIsInterpreted = _frame.is_interpreted_frame(); +#endif // COMPILER1 && SPARC + + _frame = _frame.sender(&_reg_map); + + if (!forte_safe_for_sender(&_frame, _thread)) { + _mode = at_end_mode; + return; + } + +#if defined(COMPILER1) && defined(SPARC) + if (prevIsInterpreted) { + // previous callee was interpreted and may require a special check + if (_frame.is_compiled_frame() && _frame.cb()->is_compiled_by_c1()) { + // compiled sender called interpreted callee so need one more check + bool is_compiled, is_walkable; + + // sanity check the compiled sender frame + forte_is_walkable_compiled_frame(&_frame, &_reg_map, + &is_compiled, &is_walkable); + assert(is_compiled, "sanity check"); + if (!is_walkable) { + // compiled sender frame is not walkable so bail out + _mode = at_end_mode; + return; + } + } + } +#endif // COMPILER1 && SPARC + + if (++loop_count >= loop_max) { + // We have looped more than twice the number of possible + // Java frames. This indicates that we are trying to walk + // a stack that is in the middle of being constructed and + // it is self referential. + _mode = at_end_mode; + return; + } + } while (!fill_from_frame()); +} + +// Determine if 'fr' is a walkable, compiled frame. +// *is_compiled_p is set to true if the frame is compiled and if it +// is, then *is_walkable_p is set to true if it is also walkable. +static void forte_is_walkable_compiled_frame(frame* fr, RegisterMap* map, + bool* is_compiled_p, bool* is_walkable_p) { + + *is_compiled_p = false; + *is_walkable_p = false; + + CodeBlob* cb = CodeCache::find_blob(fr->pc()); + if (cb != NULL && + cb->is_nmethod() && + ((nmethod*)cb)->is_java_method()) { + // frame is compiled and executing a Java method + *is_compiled_p = true; + + // Increment PC because the PcDesc we want is associated with + // the *end* of the instruction, and pc_desc_near searches + // forward to the first matching PC after the probe PC. + PcDesc* pc_desc = NULL; + if (!DebugNonSafepoints_IS_CLEARED) { + // usual case: look for any safepoint near the sampled PC + address probe_pc = fr->pc() + 1; + pc_desc = ((nmethod*) cb)->pc_desc_near(probe_pc); + } else { + // reduced functionality: only recognize PCs immediately after calls + pc_desc = ((nmethod*) cb)->pc_desc_at(fr->pc()); + } + if (pc_desc != NULL && (pc_desc->scope_decode_offset() + == DebugInformationRecorder::serialized_null)) { + pc_desc = NULL; + } + if (pc_desc != NULL) { + // it has a PcDesc so the frame is also walkable + *is_walkable_p = true; + if (!DebugNonSafepoints_IS_CLEARED) { + // Normalize the PC to the one associated exactly with + // this PcDesc, so that subsequent stack-walking queries + // need not be approximate: + fr->set_pc(pc_desc->real_pc((nmethod*) cb)); + } + } + // Implied else: this compiled frame has no PcDesc, i.e., contains + // a frameless stub such as C1 method exit, so it is not walkable. + } + // Implied else: this isn't a compiled frame so it isn't a + // walkable, compiled frame. +} + +// Determine if 'fr' is a walkable interpreted frame. Returns false +// if it is not. *method_p, and *bci_p are not set when false is +// returned. *method_p is non-NULL if frame was executing a Java +// method. *bci_p is != -1 if a valid BCI in the Java method could +// be found. +// Note: this method returns true when a valid Java method is found +// even if a valid BCI cannot be found. + +static bool forte_is_walkable_interpreted_frame(frame* fr, + methodOop* method_p, int* bci_p) { + assert(fr->is_interpreted_frame(), "just checking"); + + // top frame is an interpreted frame + // check if it is walkable (i.e. valid methodOop and valid bci) + if (fr->is_interpreted_frame_valid()) { + if (fr->fp() != NULL) { + // access address in order not to trigger asserts that + // are built in interpreter_frame_method function + methodOop method = *fr->interpreter_frame_method_addr(); + if (Universe::heap()->is_valid_method(method)) { + intptr_t bcx = fr->interpreter_frame_bcx(); + int bci = method->validate_bci_from_bcx(bcx); + // note: bci is set to -1 if not a valid bci + *method_p = method; + *bci_p = bci; + return true; + } + } + } + return false; +} + + +// Determine if 'fr' can be used to find a walkable frame. Returns +// false if a walkable frame cannot be found. *walkframe_p, *method_p, +// and *bci_p are not set when false is returned. Returns true if a +// walkable frame is returned via *walkframe_p. *method_p is non-NULL +// if the returned frame was executing a Java method. *bci_p is != -1 +// if a valid BCI in the Java method could be found. +// +// *walkframe_p will be used by vframeStreamForte as the initial +// frame for walking the stack. Currently the initial frame is +// skipped by vframeStreamForte because we inherited the logic from +// the vframeStream class. This needs to be revisited in the future. +static bool forte_is_walkable_frame(JavaThread* thread, frame* fr, + frame* walkframe_p, methodOop* method_p, int* bci_p) { + + if (!forte_safe_for_sender(fr, thread) + || is_unknown_compiled_frame(fr, thread) + ) { + // If the initial frame is not safe, then bail out. So far this + // has only been seen on Solaris X86 with Compiler2, but it seems + // like a great initial sanity check. + return false; + } + + if (fr->is_first_frame()) { + // If initial frame is frame from StubGenerator and there is no + // previous anchor, there are no java frames yet + return false; + } + + if (fr->is_interpreted_frame()) { + if (forte_is_walkable_interpreted_frame(fr, method_p, bci_p)) { + *walkframe_p = *fr; + return true; + } + return false; + } + + // At this point we have something other than a first frame or an + // interpreted frame. + + methodOop method = NULL; + frame candidate = *fr; + + // If we loop more than twice the number of possible Java + // frames, then this indicates that we are trying to walk + // a stack that is in the middle of being constructed and + // it is self referential. So far this problem has only + // been seen on Solaris X86 Compiler2, but it seems like + // a good robustness fix for all platforms. + + int loop_count; + int loop_max = MaxJavaStackTraceDepth * 2; + + for (loop_count = 0; loop_count < loop_max; loop_count++) { + // determine if the candidate frame is executing a Java method + if (CodeCache::contains(candidate.pc())) { + // candidate is a compiled frame or stub routine + CodeBlob* cb = CodeCache::find_blob(candidate.pc()); + + if (cb->is_nmethod()) { + method = ((nmethod *)cb)->method(); + } + } // end if CodeCache has our PC + + RegisterMap map(thread, false); + + // we have a Java frame that seems reasonable + if (method != NULL && candidate.is_java_frame() + && candidate.sp() != NULL && candidate.pc() != NULL) { + // we need to sanity check the candidate further + bool is_compiled, is_walkable; + + forte_is_walkable_compiled_frame(&candidate, &map, &is_compiled, + &is_walkable); + if (is_compiled) { + // At this point, we know we have a compiled Java frame with + // method information that we want to return. We don't check + // the is_walkable flag here because that flag pertains to + // vframeStreamForte work that is done after we are done here. + break; + } + } + + // At this point, the candidate doesn't work so try the sender. + + // For AsyncGetCallTrace() we cannot assume there is a sender + // for the initial frame. The initial forte_safe_for_sender() call + // and check for is_first_frame() is done on entry to this method. + candidate = candidate.sender(&map); + if (!forte_safe_for_sender(&candidate, thread)) { + +#ifdef COMPILER2 +#if defined(IA32) || defined(AMD64) + // C2 on X86 can use the ebp register as a general purpose register + // which can cause the candidate to fail theforte_safe_for_sender() + // above. We try one more time using a NULL frame pointer (fp). + + candidate = frame(candidate.sp(), NULL, candidate.pc()); + if (!forte_safe_for_sender(&candidate, thread)) { +#endif // IA32 || AMD64 +#endif // COMPILER2 + + return false; + +#ifdef COMPILER2 +#if defined(IA32) || defined(AMD64) + } // end forte_safe_for_sender retry with NULL fp +#endif // IA32 || AMD64 +#endif // COMPILER2 + + } // end first forte_safe_for_sender check + + if (candidate.is_first_frame() + || is_unknown_compiled_frame(&candidate, thread)) { + return false; + } + } // end for loop_count + + if (method == NULL) { + // If we didn't get any method info from the candidate, then + // we have nothing to return so bail out. + return false; + } + + *walkframe_p = candidate; + *method_p = method; + *bci_p = -1; + return true; +} + + +// call frame copied from old .h file and renamed +typedef struct { + jint lineno; // line number in the source file + jmethodID method_id; // method executed in this frame +} ASGCT_CallFrame; + +// call trace copied from old .h file and renamed +typedef struct { + JNIEnv *env_id; // Env where trace was recorded + jint num_frames; // number of frames in this trace + ASGCT_CallFrame *frames; // frames +} ASGCT_CallTrace; + +static void forte_fill_call_trace_given_top(JavaThread* thd, + ASGCT_CallTrace* trace, int depth, frame top_frame) { + NoHandleMark nhm; + + frame walkframe; + methodOop method; + int bci; + int count; + + count = 0; + assert(trace->frames != NULL, "trace->frames must be non-NULL"); + + if (!forte_is_walkable_frame(thd, &top_frame, &walkframe, &method, &bci)) { + // return if no walkable frame is found + return; + } + + CollectedHeap* ch = Universe::heap(); + + if (method != NULL) { + // The method is not stored GC safe so see if GC became active + // after we entered AsyncGetCallTrace() and before we try to + // use the methodOop. + // Yes, there is still a window after this check and before + // we use methodOop below, but we can't lock out GC so that + // has to be an acceptable risk. + if (!ch->is_valid_method(method)) { + trace->num_frames = -2; + return; + } + + if (DebugNonSafepoints_IS_CLEARED) { + // Take whatever method the top-frame decoder managed to scrape up. + // We look further at the top frame only if non-safepoint + // debugging information is available. + count++; + trace->num_frames = count; + trace->frames[0].method_id = method->find_jmethod_id_or_null(); + if (!method->is_native()) { + trace->frames[0].lineno = bci; + } else { + trace->frames[0].lineno = -3; + } + } + } + + // check has_last_Java_frame() after looking at the top frame + // which may be an interpreted Java frame. + if (!thd->has_last_Java_frame() && method == NULL) { + trace->num_frames = 0; + return; + } + + vframeStreamForte st(thd, walkframe, false); + for (; !st.at_end() && count < depth; st.forte_next(), count++) { + bci = st.bci(); + method = st.method(); + + // The method is not stored GC safe so see if GC became active + // after we entered AsyncGetCallTrace() and before we try to + // use the methodOop. + // Yes, there is still a window after this check and before + // we use methodOop below, but we can't lock out GC so that + // has to be an acceptable risk. + if (!ch->is_valid_method(method)) { + // we throw away everything we've gathered in this sample since + // none of it is safe + trace->num_frames = -2; + return; + } + + trace->frames[count].method_id = method->find_jmethod_id_or_null(); + if (!method->is_native()) { + trace->frames[count].lineno = bci; + } else { + trace->frames[count].lineno = -3; + } + } + trace->num_frames = count; + return; +} + + +// Forte Analyzer AsyncGetCallTrace() entry point. Currently supported +// on Linux X86, Solaris SPARC and Solaris X86. +// +// Async-safe version of GetCallTrace being called from a signal handler +// when a LWP gets interrupted by SIGPROF but the stack traces are filled +// with different content (see below). +// +// This function must only be called when JVM/TI +// CLASS_LOAD events have been enabled since agent startup. The enabled +// event will cause the jmethodIDs to be allocated at class load time. +// The jmethodIDs cannot be allocated in a signal handler because locks +// cannot be grabbed in a signal handler safely. +// +// void (*AsyncGetCallTrace)(ASGCT_CallTrace *trace, jint depth, void* ucontext) +// +// Called by the profiler to obtain the current method call stack trace for +// a given thread. The thread is identified by the env_id field in the +// ASGCT_CallTrace structure. The profiler agent should allocate a ASGCT_CallTrace +// structure with enough memory for the requested stack depth. The VM fills in +// the frames buffer and the num_frames field. +// +// Arguments: +// +// trace - trace data structure to be filled by the VM. +// depth - depth of the call stack trace. +// ucontext - ucontext_t of the LWP +// +// ASGCT_CallTrace: +// typedef struct { +// JNIEnv *env_id; +// jint num_frames; +// ASGCT_CallFrame *frames; +// } ASGCT_CallTrace; +// +// Fields: +// env_id - ID of thread which executed this trace. +// num_frames - number of frames in the trace. +// (< 0 indicates the frame is not walkable). +// frames - the ASGCT_CallFrames that make up this trace. Callee followed by callers. +// +// ASGCT_CallFrame: +// typedef struct { +// jint lineno; +// jmethodID method_id; +// } ASGCT_CallFrame; +// +// Fields: +// 1) For Java frame (interpreted and compiled), +// lineno - bci of the method being executed or -1 if bci is not available +// method_id - jmethodID of the method being executed +// 2) For native method +// lineno - (-3) +// method_id - jmethodID of the method being executed + +extern "C" { +void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) { + if (SafepointSynchronize::is_synchronizing()) { + // The safepoint mechanism is trying to synchronize all the threads. + // Since this can involve thread suspension, it is not safe for us + // to be here. We can reduce the deadlock risk window by quickly + // returning to the SIGPROF handler. However, it is still possible + // for VMThread to catch us here or in the SIGPROF handler. If we + // are suspended while holding a resource and another thread blocks + // on that resource in the SIGPROF handler, then we will have a + // three-thread deadlock (VMThread, this thread, the other thread). + trace->num_frames = -10; + return; + } + + JavaThread* thread; + + if (trace->env_id == NULL || + (thread = JavaThread::thread_from_jni_environment(trace->env_id)) == NULL || + thread->is_exiting()) { + + // bad env_id, thread has exited or thread is exiting + trace->num_frames = -8; + return; + } + + if (thread->in_deopt_handler()) { + // thread is in the deoptimization handler so return no frames + trace->num_frames = -9; + return; + } + + assert(JavaThread::current() == thread, + "AsyncGetCallTrace must be called by the current interrupted thread"); + + if (!JvmtiExport::should_post_class_load()) { + trace->num_frames = -1; + return; + } + + if (Universe::heap()->is_gc_active()) { + trace->num_frames = -2; + return; + } + + switch (thread->thread_state()) { + case _thread_new: + case _thread_uninitialized: + case _thread_new_trans: + // We found the thread on the threads list above, but it is too + // young to be useful so return that there are no Java frames. + trace->num_frames = 0; + break; + case _thread_in_native: + case _thread_in_native_trans: + case _thread_blocked: + case _thread_blocked_trans: + case _thread_in_vm: + case _thread_in_vm_trans: + { + frame fr; + + // param isInJava == false - indicate we aren't in Java code + if (!thread->pd_get_top_frame_for_signal_handler(&fr, ucontext, false)) { + if (!thread->has_last_Java_frame()) { + trace->num_frames = 0; // no Java frames + } else { + trace->num_frames = -3; // unknown frame + } + } else { + trace->num_frames = -4; // non walkable frame by default + forte_fill_call_trace_given_top(thread, trace, depth, fr); + } + } + break; + case _thread_in_Java: + case _thread_in_Java_trans: + { + frame fr; + + // param isInJava == true - indicate we are in Java code + if (!thread->pd_get_top_frame_for_signal_handler(&fr, ucontext, true)) { + trace->num_frames = -5; // unknown frame + } else { + trace->num_frames = -6; // non walkable frame by default + forte_fill_call_trace_given_top(thread, trace, depth, fr); + } + } + break; + default: + // Unknown thread state + trace->num_frames = -7; + break; + } +} + + +#ifndef _WINDOWS +// Support for the Forte(TM) Peformance Tools collector. +// +// The method prototype is derived from libcollector.h. For more +// information, please see the libcollect man page. + +// Method to let libcollector know about a dynamically loaded function. +// Because it is weakly bound, the calls become NOP's when the library +// isn't present. +void collector_func_load(char* name, + void* null_argument_1, + void* null_argument_2, + void *vaddr, + int size, + int zero_argument, + void* null_argument_3); +#pragma weak collector_func_load +#define collector_func_load(x0,x1,x2,x3,x4,x5,x6) \ + ( collector_func_load ? collector_func_load(x0,x1,x2,x3,x4,x5,x6),0 : 0 ) +#endif // !_WINDOWS + +} // end extern "C" +#endif // !IA64 + +void Forte::register_stub(const char* name, address start, address end) { +#if !defined(_WINDOWS) && !defined(IA64) + assert(pointer_delta(end, start, sizeof(jbyte)) < INT_MAX, + "Code size exceeds maximum range") + + collector_func_load((char*)name, NULL, NULL, start, + pointer_delta(end, start, sizeof(jbyte)), 0, NULL); +#endif // !_WINDOWS && !IA64 +} diff --git a/hotspot/src/share/vm/prims/forte.hpp b/hotspot/src/share/vm/prims/forte.hpp new file mode 100644 index 00000000000..c0eba40f2a8 --- /dev/null +++ b/hotspot/src/share/vm/prims/forte.hpp @@ -0,0 +1,32 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Interface to Forte support. + +class Forte : AllStatic { + public: + static void register_stub(const char* name, address start, address end) + KERNEL_RETURN; + // register internal VM stub +}; diff --git a/hotspot/src/share/vm/prims/hpi_imported.h b/hotspot/src/share/vm/prims/hpi_imported.h new file mode 100644 index 00000000000..fad386bdcda --- /dev/null +++ b/hotspot/src/share/vm/prims/hpi_imported.h @@ -0,0 +1,317 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * HotSpot integration note: + * + * This is a consolidation of these two files: + * src/share/hpi/export/hpi.h 1.15 99/06/18 JDK1.3 beta build I + * src/share/hpi/export/dll.h 1.3 98/09/15 JDK1.3 beta build I + * from the classic VM. + * + * bool_t is a type in the classic VM, and we define it here, + * but in the future this should be a jboolean. + * + * The files are included verbatim expect for local includes removed from hpi.h. + */ + +#ifndef _JAVASOFT_HPI_H_ +#define _JAVASOFT_HPI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* A classic VMism that should become a jboolean. Fix in 1.2.1? */ +typedef enum { HPI_FALSE = 0, HPI_TRUE = 1 } bool_t; + +/* + * DLL.H: A common interface for helper DLLs loaded by the VM. + * Each library exports the main entry point "DLL_Initialize". Through + * that function the programmer can obtain a function pointer which has + * type "GetInterfaceFunc." Through the function pointer the programmer + * can obtain other interfaces supported in the DLL. + */ +typedef jint (JNICALL * GetInterfaceFunc) + (void **intfP, const char *name, jint ver); + +jint JNICALL DLL_Initialize(GetInterfaceFunc *, void *args); + + +/* + * Host Porting Interface. This defines the "porting layer" for + * POSIX.1 compliant operating systems. + */ + +/* + * memory allocations + */ +typedef struct { + /* + * Malloc must return a unique pointer if size == 0. + */ + void * (*Malloc)(size_t size); + void * (*Realloc)(void *ptr, size_t new_size); + /* + * Free must allow ptr == NULL to be a no-op. + */ + void (*Free)(void *ptr); + /* + * Calloc must return a unique pointer for if + * n_item == 0 || item_size == 0. + */ + void * (*Calloc)(size_t n_item, size_t item_size); + char * (*Strdup)(const char *str); + + void * (*MapMem)(size_t req_size, size_t *maped_size); + void * (*UnmapMem)(void *req_addr, size_t req_size, size_t *unmap_size); + /* + * CommitMem should round the ptr down to the nearest page and + * round the size up to the nearest page so that the committed + * region is at least as large as the requested region. + */ + void * (*CommitMem)(void *ptr, size_t size, size_t *actual); + /* + * sysDecommitMem should round the ptr up to the nearest page and + * round the size down to the nearest page so that the decommitted + * region is no greater than the requested region. + */ + void * (*DecommitMem)(void *ptr, size_t size, size_t *actual); + +#define HPI_PAGE_ALIGNMENT (64 * 1024) + + void * (*AllocBlock)(size_t size, void **headP); + void (*FreeBlock)(void *head); +} HPI_MemoryInterface; + +/* + * dynamic linking libraries + */ +typedef struct { + void (*BuildLibName)(char *buf, int buf_len, char *path, const char *name); + int (*BuildFunName)(char *name, int name_len, int arg_size, int en_idx); + + void * (*LoadLibrary)(const char *name, char *err_buf, int err_buflen); + void (*UnloadLibrary)(void *lib); + void * (*FindLibraryEntry)(void *lib, const char *name); +} HPI_LibraryInterface; + +typedef void (*signal_handler_t)(int sig, void *siginfo, void *context); + +#define HPI_SIG_DFL (signal_handler_t)0 +#define HPI_SIG_ERR (signal_handler_t)-1 +#define HPI_SIG_IGN (signal_handler_t)1 + +typedef struct { + char *name; /* name such as green/native threads. */ + int isMP; +} HPI_SysInfo; + +typedef struct { + HPI_SysInfo * (*GetSysInfo)(void); + long (*GetMilliTicks)(void); + jlong (*TimeMillis)(void); + + signal_handler_t (*Signal)(int sig, signal_handler_t handler); + void (*Raise)(int sig); + void (*SignalNotify)(int sig); + int (*SignalWait)(void); + + int (*Shutdown)(void); + + int (*SetLoggingLevel)(int level); + bool_t (*SetMonitoringOn)(bool_t on); + int (*GetLastErrorString)(char *buf, int len); +} HPI_SystemInterface; + +/* + * threads and monitors + */ +typedef struct sys_thread sys_thread_t; +typedef struct sys_mon sys_mon_t; + +#define HPI_OK 0 +#define HPI_ERR -1 +#define HPI_INTRPT -2 /* Operation was interrupted */ +#define HPI_TIMEOUT -3 /* A timer ran out */ +#define HPI_NOMEM -5 /* Ran out of memory */ +#define HPI_NORESOURCE -6 /* Ran out of some system resource */ + +/* There are three basic states: RUNNABLE, MONITOR_WAIT, and CONDVAR_WAIT. + * When the thread is suspended in any of these states, the + * HPI_THREAD_SUSPENDED bit will be set + */ +enum { + HPI_THREAD_RUNNABLE = 1, + HPI_THREAD_MONITOR_WAIT, + HPI_THREAD_CONDVAR_WAIT +}; + +#define HPI_MINIMUM_PRIORITY 1 +#define HPI_MAXIMUM_PRIORITY 10 +#define HPI_NORMAL_PRIORITY 5 + +#define HPI_THREAD_SUSPENDED 0x8000 +#define HPI_THREAD_INTERRUPTED 0x4000 + +typedef struct { + sys_thread_t *owner; + int entry_count; + sys_thread_t **monitor_waiters; + sys_thread_t **condvar_waiters; + int sz_monitor_waiters; + int sz_condvar_waiters; + int n_monitor_waiters; + int n_condvar_waiters; +} sys_mon_info; + +typedef struct { + int (*ThreadBootstrap)(sys_thread_t **tidP, + sys_mon_t **qlockP, + int nReservedBytes); + int (*ThreadCreate)(sys_thread_t **tidP, + long stk_size, + void (*func)(void *), + void *arg); + sys_thread_t * (*ThreadSelf)(void); + void (*ThreadYield)(void); + int (*ThreadSuspend)(sys_thread_t *tid); + int (*ThreadResume)(sys_thread_t *tid); + int (*ThreadSetPriority)(sys_thread_t *tid, int prio); + int (*ThreadGetPriority)(sys_thread_t *tid, int *prio); + void * (*ThreadStackPointer)(sys_thread_t *tid); + void * (*ThreadStackTop)(sys_thread_t *tid); + long * (*ThreadRegs)(sys_thread_t *tid, int *regs); + int (*ThreadSingle)(void); + void (*ThreadMulti)(void); + int (*ThreadEnumerateOver)(int (*func)(sys_thread_t *, void *), + void *arg); + int (*ThreadCheckStack)(void); + void (*ThreadPostException)(sys_thread_t *tid, void *arg); + void (*ThreadInterrupt)(sys_thread_t *tid); + int (*ThreadIsInterrupted)(sys_thread_t *tid, int clear); + int (*ThreadAlloc)(sys_thread_t **tidP); + int (*ThreadFree)(void); + jlong (*ThreadCPUTime)(void); + int (*ThreadGetStatus)(sys_thread_t *tid, sys_mon_t **monitor); + void * (*ThreadInterruptEvent)(void); + void * (*ThreadNativeID)(sys_thread_t *tid); + + /* These three functions are used by the CPU time profiler. + * sysThreadIsRunning determines whether the thread is running (not just + * runnable). It is only safe to call this function after calling + * sysThreadProfSuspend. + */ + bool_t (*ThreadIsRunning)(sys_thread_t *tid); + void (*ThreadProfSuspend)(sys_thread_t *tid); + void (*ThreadProfResume)(sys_thread_t *tid); + + int (*AdjustTimeSlice)(int ms); + + size_t (*MonitorSizeof)(void); + int (*MonitorInit)(sys_mon_t *mid); + int (*MonitorDestroy)(sys_mon_t *mid); + int (*MonitorEnter)(sys_thread_t *self, sys_mon_t *mid); + bool_t (*MonitorEntered)(sys_thread_t *self, sys_mon_t *mid); + int (*MonitorExit)(sys_thread_t *self, sys_mon_t *mid); + int (*MonitorNotify)(sys_thread_t *self, sys_mon_t *mid); + int (*MonitorNotifyAll)(sys_thread_t *self, sys_mon_t *mid); + int (*MonitorWait)(sys_thread_t *self, sys_mon_t *mid, jlong ms); + bool_t (*MonitorInUse)(sys_mon_t *mid); + sys_thread_t * (*MonitorOwner)(sys_mon_t *mid); + int (*MonitorGetInfo)(sys_mon_t *mid, sys_mon_info *info); + +} HPI_ThreadInterface; + +/* + * files + */ + +#define HPI_FILETYPE_REGULAR (0) +#define HPI_FILETYPE_DIRECTORY (1) +#define HPI_FILETYPE_OTHER (2) + +typedef struct { + char * (*NativePath)(char *path); + int (*FileType)(const char *path); + int (*Open)(const char *name, int openMode, int filePerm); + int (*Close)(int fd); + jlong (*Seek)(int fd, jlong offset, int whence); + int (*SetLength)(int fd, jlong length); + int (*Sync)(int fd); + int (*Available)(int fd, jlong *bytes); + size_t (*Read)(int fd, void *buf, unsigned int nBytes); + size_t (*Write)(int fd, const void *buf, unsigned int nBytes); + int (*FileSizeFD)(int fd, jlong *size); +} HPI_FileInterface; + +/* + * sockets + */ +struct sockaddr; +struct hostent; + +typedef struct { + int (*Close)(int fd); + long (*Available)(int fd, jint *pbytes); + int (*Connect)(int fd, struct sockaddr *him, int len); + int (*Accept)(int fd, struct sockaddr *him, int *len); + int (*SendTo)(int fd, char *buf, int len, int flags, + struct sockaddr *to, int tolen); + int (*RecvFrom)(int fd, char *buf, int nbytes, int flags, + struct sockaddr *from, int *fromlen); + int (*Listen)(int fd, long count); + int (*Recv)(int fd, char *buf, int nBytes, int flags); + int (*Send)(int fd, char *buf, int nBytes, int flags); + int (*Timeout)(int fd, long timeout); + struct hostent * (*GetHostByName)(char *hostname); + int (*Socket)(int domain, int type, int protocol); + int (*SocketShutdown)(int fd, int howto); + int (*Bind)(int fd, struct sockaddr *him, int len); + int (*GetSocketName)(int fd, struct sockaddr *him, int *len); + int (*GetHostName)(char *hostname, int namelen); + struct hostent * (*GetHostByAddr)(const char *hostname, int len, int type); + int (*SocketGetOption)(int fd, int level, int optname, char *optval, int *optlen); + int (*SocketSetOption)(int fd, int level, int optname, const char *optval, int optlen); + struct protoent * (*GetProtoByName)(char* name); +} HPI_SocketInterface; + +/* + * callbacks. + */ +typedef struct vm_calls { + int (*jio_fprintf)(FILE *fp, const char *fmt, ...); + void (*panic)(const char *fmt, ...); + void (*monitorRegister)(sys_mon_t *mid, char *info_str); + + void (*monitorContendedEnter)(sys_thread_t *self, sys_mon_t *mid); + void (*monitorContendedEntered)(sys_thread_t *self, sys_mon_t *mid); + void (*monitorContendedExit)(sys_thread_t *self, sys_mon_t *mid); +} vm_calls_t; + +#ifdef __cplusplus +} +#endif + +#endif /* !_JAVASOFT_HPI_H_ */ diff --git a/hotspot/src/share/vm/prims/jni.cpp b/hotspot/src/share/vm/prims/jni.cpp new file mode 100644 index 00000000000..6f31afc986e --- /dev/null +++ b/hotspot/src/share/vm/prims/jni.cpp @@ -0,0 +1,3582 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jni.cpp.incl" + +static jint CurrentVersion = JNI_VERSION_1_6; + + +// The DT_RETURN_MARK macros create a scoped object to fire the dtrace +// '-return' probe regardless of the return path is taken out of the function. +// Methods that have multiple return paths use this to avoid having to +// instrument each return path. Methods that use CHECK or THROW must use this +// since those macros can cause an immedate uninstrumented return. +// +// In order to get the return value, a reference to the variable containing +// the return value must be passed to the contructor of the object, and +// the return value must be set before return (since the mark object has +// a reference to it). +// +// Example: +// DT_RETURN_MARK_DECL(SomeFunc, int); +// JNI_ENTRY(int, SomeFunc, ...) +// int return_value = 0; +// DT_RETURN_MARK(SomeFunc, int, (const int&)return_value); +// foo(CHECK_0) +// return_value = 5; +// return return_value; +// JNI_END +#define DT_RETURN_MARK_DECL(name, type) \ + HS_DTRACE_PROBE_DECL1(hotspot_jni, name##__return, type); \ + DTRACE_ONLY( \ + class DTraceReturnProbeMark_##name { \ + public: \ + const type& _ret_ref; \ + DTraceReturnProbeMark_##name(const type& v) : _ret_ref(v) {} \ + ~DTraceReturnProbeMark_##name() { \ + HS_DTRACE_PROBE1(hotspot_jni, name##__return, _ret_ref); \ + } \ + } \ + ) +// Void functions are simpler since there's no return value +#define DT_VOID_RETURN_MARK_DECL(name) \ + HS_DTRACE_PROBE_DECL0(hotspot_jni, name##__return); \ + DTRACE_ONLY( \ + class DTraceReturnProbeMark_##name { \ + public: \ + ~DTraceReturnProbeMark_##name() { \ + HS_DTRACE_PROBE0(hotspot_jni, name##__return); \ + } \ + } \ + ) + + +// Place these macros in the function to mark the return. Non-void +// functions need the type and address of the return value. +#define DT_RETURN_MARK(name, type, ref) \ + DTRACE_ONLY( DTraceReturnProbeMark_##name dtrace_return_mark(ref) ) +#define DT_VOID_RETURN_MARK(name) \ + DTRACE_ONLY( DTraceReturnProbeMark_##name dtrace_return_mark ) + + +// Use these to select distinct code for floating-point vs. non-floating point +// situations. Used from within common macros where we need slightly +// different behavior for Float/Double +#define FP_SELECT_Boolean(intcode, fpcode) intcode +#define FP_SELECT_Byte(intcode, fpcode) intcode +#define FP_SELECT_Char(intcode, fpcode) intcode +#define FP_SELECT_Short(intcode, fpcode) intcode +#define FP_SELECT_Object(intcode, fpcode) intcode +#define FP_SELECT_Int(intcode, fpcode) intcode +#define FP_SELECT_Long(intcode, fpcode) intcode +#define FP_SELECT_Float(intcode, fpcode) fpcode +#define FP_SELECT_Double(intcode, fpcode) fpcode +#define FP_SELECT(TypeName, intcode, fpcode) \ + FP_SELECT_##TypeName(intcode, fpcode) + +#define COMMA , + +// Choose DT_RETURN_MARK macros based on the type: float/double -> void +// (dtrace doesn't do FP yet) +#define DT_RETURN_MARK_DECL_FOR(TypeName, name, type) \ + FP_SELECT(TypeName, \ + DT_RETURN_MARK_DECL(name, type), DT_VOID_RETURN_MARK_DECL(name) ) +#define DT_RETURN_MARK_FOR(TypeName, name, type, ref) \ + FP_SELECT(TypeName, \ + DT_RETURN_MARK(name, type, ref), DT_VOID_RETURN_MARK(name) ) + + +// out-of-line helpers for class jfieldIDWorkaround: + +bool jfieldIDWorkaround::is_valid_jfieldID(klassOop k, jfieldID id) { + if (jfieldIDWorkaround::is_instance_jfieldID(k, id)) { + uintptr_t as_uint = (uintptr_t) id; + intptr_t offset = raw_instance_offset(id); + if (is_checked_jfieldID(id)) { + if (!klass_hash_ok(k, id)) { + return false; + } + } + return instanceKlass::cast(k)->contains_field_offset(offset); + } else { + JNIid* result = (JNIid*) id; +#ifdef ASSERT + return result != NULL && result->is_static_field_id(); +#else + return result != NULL; +#endif + } +} + + +intptr_t jfieldIDWorkaround::encode_klass_hash(klassOop k, intptr_t offset) { + if (offset <= small_offset_mask) { + klassOop field_klass = k; + klassOop super_klass = Klass::cast(field_klass)->super(); + while (instanceKlass::cast(super_klass)->contains_field_offset(offset)) { + field_klass = super_klass; // super contains the field also + super_klass = Klass::cast(field_klass)->super(); + } + debug_only(No_Safepoint_Verifier nosafepoint;) + uintptr_t klass_hash = field_klass->identity_hash(); + return ((klass_hash & klass_mask) << klass_shift) | checked_mask_in_place; + } else { +#if 0 + #ifndef PRODUCT + { + ResourceMark rm; + warning("VerifyJNIFields: long offset %d in %s", offset, Klass::cast(k)->external_name()); + } + #endif +#endif + return 0; + } +} + +bool jfieldIDWorkaround::klass_hash_ok(klassOop k, jfieldID id) { + uintptr_t as_uint = (uintptr_t) id; + intptr_t klass_hash = (as_uint >> klass_shift) & klass_mask; + do { + debug_only(No_Safepoint_Verifier nosafepoint;) + // Could use a non-blocking query for identity_hash here... + if ((k->identity_hash() & klass_mask) == klass_hash) + return true; + k = Klass::cast(k)->super(); + } while (k != NULL); + return false; +} + +void jfieldIDWorkaround::verify_instance_jfieldID(klassOop k, jfieldID id) { + guarantee(jfieldIDWorkaround::is_instance_jfieldID(k, id), "must be an instance field" ); + uintptr_t as_uint = (uintptr_t) id; + intptr_t offset = raw_instance_offset(id); + if (VerifyJNIFields) { + if (is_checked_jfieldID(id)) { + guarantee(klass_hash_ok(k, id), + "Bug in native code: jfieldID class must match object"); + } else { +#if 0 + #ifndef PRODUCT + if (Verbose) { + ResourceMark rm; + warning("VerifyJNIFields: unverified offset %d for %s", offset, Klass::cast(k)->external_name()); + } + #endif +#endif + } + } + guarantee(instanceKlass::cast(k)->contains_field_offset(offset), + "Bug in native code: jfieldID offset must address interior of object"); +} + +// Pick a reasonable higher bound for local capacity requested +// for EnsureLocalCapacity and PushLocalFrame. We don't want it too +// high because a test (or very unusual application) may try to allocate +// that many handles and run out of swap space. An implementation is +// permitted to allocate more handles than the ensured capacity, so this +// value is set high enough to prevent compatibility problems. +const int MAX_REASONABLE_LOCAL_CAPACITY = 4*K; + + +// Wrapper to trace JNI functions + +#ifdef ASSERT + Histogram* JNIHistogram; + static volatile jint JNIHistogram_lock = 0; + + class JNITraceWrapper : public StackObj { + public: + JNITraceWrapper(const char* format, ...) { + if (TraceJNICalls) { + va_list ap; + va_start(ap, format); + tty->print("JNI "); + tty->vprint_cr(format, ap); + va_end(ap); + } + } + }; + + class JNIHistogramElement : public HistogramElement { + public: + JNIHistogramElement(const char* name); + }; + + JNIHistogramElement::JNIHistogramElement(const char* elementName) { + _name = elementName; + uintx count = 0; + + while (Atomic::cmpxchg(1, &JNIHistogram_lock, 0) != 0) { + while (OrderAccess::load_acquire(&JNIHistogram_lock) != 0) { + count +=1; + if ( (WarnOnStalledSpinLock > 0) + && (count % WarnOnStalledSpinLock == 0)) { + warning("JNIHistogram_lock seems to be stalled"); + } + } + } + + + if(JNIHistogram == NULL) + JNIHistogram = new Histogram("JNI Call Counts",100); + + JNIHistogram->add_element(this); + Atomic::dec(&JNIHistogram_lock); + } + + #define JNICountWrapper(arg) \ + static JNIHistogramElement* e = new JNIHistogramElement(arg); \ + /* There is a MT-race condition in VC++. So we need to make sure that that e has been initialized */ \ + if (e != NULL) e->increment_count() + #define JNIWrapper(arg) JNICountWrapper(arg); JNITraceWrapper(arg) +#else + #define JNIWrapper(arg) +#endif + + +// Implementation of JNI entries + +DT_RETURN_MARK_DECL(DefineClass, jclass); + +JNI_ENTRY(jclass, jni_DefineClass(JNIEnv *env, const char *name, jobject loaderRef, + const jbyte *buf, jsize bufLen)) + JNIWrapper("DefineClass"); + + DTRACE_PROBE5(hotspot_jni, DefineClass__entry, + env, name, loaderRef, buf, bufLen); + jclass cls = NULL; + DT_RETURN_MARK(DefineClass, jclass, (const jclass&)cls); + + // Since exceptions can be thrown, class initialization can take place + // if name is NULL no check for class name in .class stream has to be made. + symbolHandle class_name; + if (name != NULL) { + const int str_len = (int)strlen(name); + if (str_len > symbolOopDesc::max_length()) { + // It's impossible to create this class; the name cannot fit + // into the constant pool. + THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name); + } + class_name = oopFactory::new_symbol_handle(name, str_len, CHECK_NULL); + } + + ResourceMark rm(THREAD); + ClassFileStream st((u1*) buf, bufLen, NULL); + Handle class_loader (THREAD, JNIHandles::resolve(loaderRef)); + + if (UsePerfData && !class_loader.is_null()) { + // check whether the current caller thread holds the lock or not. + // If not, increment the corresponding counter + if (ObjectSynchronizer:: + query_lock_ownership((JavaThread*)THREAD, class_loader) != + ObjectSynchronizer::owner_self) { + ClassLoader::sync_JNIDefineClassLockFreeCounter()->inc(); + } + } + klassOop k = SystemDictionary::resolve_from_stream(class_name, class_loader, + Handle(), &st, CHECK_NULL); + + cls = (jclass)JNIHandles::make_local( + env, Klass::cast(k)->java_mirror()); + return cls; +JNI_END + + + +static bool first_time_FindClass = true; + +DT_RETURN_MARK_DECL(FindClass, jclass); + +JNI_ENTRY(jclass, jni_FindClass(JNIEnv *env, const char *name)) + JNIWrapper("FindClass"); + DTRACE_PROBE2(hotspot_jni, FindClass__entry, env, name); + + jclass result = NULL; + DT_RETURN_MARK(FindClass, jclass, (const jclass&)result); + + // Remember if we are the first invocation of jni_FindClass + bool first_time = first_time_FindClass; + first_time_FindClass = false; + + // Sanity check the name: it cannot be null or larger than the maximum size + // name we can fit in the constant pool. + if (name == NULL || (int)strlen(name) > symbolOopDesc::max_length()) { + THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name); + } + + //%note jni_3 + Handle loader; + Handle protection_domain; + // Find calling class + instanceKlassHandle k (THREAD, thread->security_get_caller_class(0)); + if (k.not_null()) { + loader = Handle(THREAD, k->class_loader()); + // Special handling to make sure JNI_OnLoad and JNI_OnUnload are executed + // in the correct class context. + if (loader.is_null() && + k->name() == vmSymbols::java_lang_ClassLoader_NativeLibrary()) { + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, k, + vmSymbolHandles::getFromClass_name(), + vmSymbolHandles::void_class_signature(), + thread); + if (HAS_PENDING_EXCEPTION) { + Handle ex(thread, thread->pending_exception()); + CLEAR_PENDING_EXCEPTION; + THROW_HANDLE_0(ex); + } + oop mirror = (oop) result.get_jobject(); + loader = Handle(THREAD, + instanceKlass::cast(java_lang_Class::as_klassOop(mirror))->class_loader()); + protection_domain = Handle(THREAD, + instanceKlass::cast(java_lang_Class::as_klassOop(mirror))->protection_domain()); + } + } else { + // We call ClassLoader.getSystemClassLoader to obtain the system class loader. + loader = Handle(THREAD, SystemDictionary::java_system_loader()); + } + + symbolHandle sym = oopFactory::new_symbol_handle(name, CHECK_NULL); + result = find_class_from_class_loader(env, sym, true, loader, + protection_domain, true, thread); + + // If we were the first invocation of jni_FindClass, we enable compilation again + // rather than just allowing invocation counter to overflow and decay. + // Controlled by flag DelayCompilationDuringStartup. + if (first_time && !CompileTheWorld) + CompilationPolicy::completed_vm_startup(); + + return result; +JNI_END + +DT_RETURN_MARK_DECL(FromReflectedMethod, jmethodID); + +JNI_ENTRY(jmethodID, jni_FromReflectedMethod(JNIEnv *env, jobject method)) + JNIWrapper("FromReflectedMethod"); + DTRACE_PROBE2(hotspot_jni, FromReflectedMethod__entry, env, method); + jmethodID ret = NULL; + DT_RETURN_MARK(FromReflectedMethod, jmethodID, (const jmethodID&)ret); + + // method is a handle to a java.lang.reflect.Method object + oop reflected = JNIHandles::resolve_non_null(method); + oop mirror = NULL; + int slot = 0; + + if (reflected->klass() == SystemDictionary::reflect_constructor_klass()) { + mirror = java_lang_reflect_Constructor::clazz(reflected); + slot = java_lang_reflect_Constructor::slot(reflected); + } else { + assert(reflected->klass() == SystemDictionary::reflect_method_klass(), "wrong type"); + mirror = java_lang_reflect_Method::clazz(reflected); + slot = java_lang_reflect_Method::slot(reflected); + } + klassOop k = java_lang_Class::as_klassOop(mirror); + + KlassHandle k1(THREAD, k); + // Make sure class is initialized before handing id's out to methods + Klass::cast(k1())->initialize(CHECK_NULL); + methodOop m = instanceKlass::cast(k1())->method_with_idnum(slot); + ret = m==NULL? NULL : m->jmethod_id(); // return NULL if reflected method deleted + return ret; +JNI_END + +DT_RETURN_MARK_DECL(FromReflectedField, jfieldID); + +JNI_ENTRY(jfieldID, jni_FromReflectedField(JNIEnv *env, jobject field)) + JNIWrapper("FromReflectedField"); + DTRACE_PROBE2(hotspot_jni, FromReflectedField__entry, env, field); + jfieldID ret = NULL; + DT_RETURN_MARK(FromReflectedField, jfieldID, (const jfieldID&)ret); + + // field is a handle to a java.lang.reflect.Field object + oop reflected = JNIHandles::resolve_non_null(field); + oop mirror = java_lang_reflect_Field::clazz(reflected); + klassOop k = java_lang_Class::as_klassOop(mirror); + int slot = java_lang_reflect_Field::slot(reflected); + int modifiers = java_lang_reflect_Field::modifiers(reflected); + + KlassHandle k1(THREAD, k); + // Make sure class is initialized before handing id's out to fields + Klass::cast(k1())->initialize(CHECK_NULL); + + // First check if this is a static field + if (modifiers & JVM_ACC_STATIC) { + intptr_t offset = instanceKlass::cast(k1())->offset_from_fields( slot ); + JNIid* id = instanceKlass::cast(k1())->jni_id_for(offset); + assert(id != NULL, "corrupt Field object"); + debug_only(id->set_is_static_field_id();) + // A jfieldID for a static field is a JNIid specifying the field holder and the offset within the klassOop + ret = jfieldIDWorkaround::to_static_jfieldID(id); + return ret; + } + + // The slot is the index of the field description in the field-array + // The jfieldID is the offset of the field within the object + // It may also have hash bits for k, if VerifyJNIFields is turned on. + intptr_t offset = instanceKlass::cast(k1())->offset_from_fields( slot ); + assert(instanceKlass::cast(k1())->contains_field_offset(offset), "stay within object"); + ret = jfieldIDWorkaround::to_instance_jfieldID(k1(), offset); + return ret; +JNI_END + +DT_RETURN_MARK_DECL(ToReflectedMethod, jobject); + +JNI_ENTRY(jobject, jni_ToReflectedMethod(JNIEnv *env, jclass cls, jmethodID method_id, jboolean isStatic)) + JNIWrapper("ToReflectedMethod"); + DTRACE_PROBE4(hotspot_jni, ToReflectedMethod__entry, env, cls, method_id, isStatic); + jobject ret = NULL; + DT_RETURN_MARK(ToReflectedMethod, jobject, (const jobject&)ret); + + methodHandle m (THREAD, JNIHandles::resolve_jmethod_id(method_id)); + assert(m->is_static() == (isStatic != 0), "jni_ToReflectedMethod access flags doesn't match"); + oop reflection_method; + if (m->is_initializer()) { + reflection_method = Reflection::new_constructor(m, CHECK_NULL); + } else { + reflection_method = Reflection::new_method(m, UseNewReflection, false, CHECK_NULL); + } + ret = JNIHandles::make_local(env, reflection_method); + return ret; +JNI_END + +DT_RETURN_MARK_DECL(GetSuperclass, jclass); + +JNI_ENTRY(jclass, jni_GetSuperclass(JNIEnv *env, jclass sub)) + JNIWrapper("GetSuperclass"); + DTRACE_PROBE2(hotspot_jni, GetSuperclass__entry, env, sub); + jclass obj = NULL; + DT_RETURN_MARK(GetSuperclass, jclass, (const jclass&)obj); + + oop mirror = JNIHandles::resolve_non_null(sub); + // primitive classes return NULL + if (java_lang_Class::is_primitive(mirror)) return NULL; + + // Rules of Class.getSuperClass as implemented by KLass::java_super: + // arrays return Object + // interfaces return NULL + // proper classes return Klass::super() + klassOop k = java_lang_Class::as_klassOop(mirror); + if (Klass::cast(k)->is_interface()) return NULL; + + // return mirror for superclass + klassOop super = Klass::cast(k)->java_super(); + // super2 is the value computed by the compiler's getSuperClass intrinsic: + debug_only(klassOop super2 = ( Klass::cast(k)->oop_is_javaArray() + ? SystemDictionary::object_klass() + : Klass::cast(k)->super() ) ); + assert(super == super2, + "java_super computation depends on interface, array, other super"); + obj = (super == NULL) ? NULL : (jclass) JNIHandles::make_local(Klass::cast(super)->java_mirror()); + return obj; +JNI_END + +JNI_QUICK_ENTRY(jboolean, jni_IsAssignableFrom(JNIEnv *env, jclass sub, jclass super)) + JNIWrapper("IsSubclassOf"); + DTRACE_PROBE3(hotspot_jni, IsAssignableFrom__entry, env, sub, super); + oop sub_mirror = JNIHandles::resolve_non_null(sub); + oop super_mirror = JNIHandles::resolve_non_null(super); + if (java_lang_Class::is_primitive(sub_mirror) || + java_lang_Class::is_primitive(super_mirror)) { + jboolean ret = (sub_mirror == super_mirror); + DTRACE_PROBE1(hotspot_jni, IsAssignableFrom__return, ret); + return ret; + } + klassOop sub_klass = java_lang_Class::as_klassOop(sub_mirror); + klassOop super_klass = java_lang_Class::as_klassOop(super_mirror); + assert(sub_klass != NULL && super_klass != NULL, "invalid arguments to jni_IsAssignableFrom"); + jboolean ret = Klass::cast(sub_klass)->is_subtype_of(super_klass) ? + JNI_TRUE : JNI_FALSE; + DTRACE_PROBE1(hotspot_jni, IsAssignableFrom__return, ret); + return ret; +JNI_END + +DT_RETURN_MARK_DECL(Throw, jint); + +JNI_ENTRY(jint, jni_Throw(JNIEnv *env, jthrowable obj)) + JNIWrapper("Throw"); + DTRACE_PROBE2(hotspot_jni, Throw__entry, env, obj); + jint ret = JNI_OK; + DT_RETURN_MARK(Throw, jint, (const jint&)ret); + + THROW_OOP_(JNIHandles::resolve(obj), JNI_OK); + ShouldNotReachHere(); +JNI_END + +DT_RETURN_MARK_DECL(ThrowNew, jint); + +JNI_ENTRY(jint, jni_ThrowNew(JNIEnv *env, jclass clazz, const char *message)) + JNIWrapper("ThrowNew"); + DTRACE_PROBE3(hotspot_jni, ThrowNew__entry, env, clazz, message); + jint ret = JNI_OK; + DT_RETURN_MARK(ThrowNew, jint, (const jint&)ret); + + instanceKlass* k = instanceKlass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz))); + symbolHandle name = symbolHandle(THREAD, k->name()); + Handle class_loader (THREAD, k->class_loader()); + Handle protection_domain (THREAD, k->protection_domain()); + THROW_MSG_LOADER_(name, (char *)message, class_loader, protection_domain, JNI_OK); + ShouldNotReachHere(); +JNI_END + + +// JNI functions only transform a pending async exception to a synchronous +// exception in ExceptionOccurred and ExceptionCheck calls, since +// delivering an async exception in other places won't change the native +// code's control flow and would be harmful when native code further calls +// JNI functions with a pending exception. Async exception is also checked +// during the call, so ExceptionOccurred/ExceptionCheck won't return +// false but deliver the async exception at the very end during +// state transition. + +static void jni_check_async_exceptions(JavaThread *thread) { + assert(thread == Thread::current(), "must be itself"); + thread->check_and_handle_async_exceptions(); +} + +JNI_ENTRY_NO_PRESERVE(jthrowable, jni_ExceptionOccurred(JNIEnv *env)) + JNIWrapper("ExceptionOccurred"); + DTRACE_PROBE1(hotspot_jni, ExceptionOccurred__entry, env); + jni_check_async_exceptions(thread); + oop exception = thread->pending_exception(); + jthrowable ret = (jthrowable) JNIHandles::make_local(env, exception); + DTRACE_PROBE1(hotspot_jni, ExceptionOccurred__return, ret); + return ret; +JNI_END + + +JNI_ENTRY_NO_PRESERVE(void, jni_ExceptionDescribe(JNIEnv *env)) + JNIWrapper("ExceptionDescribe"); + DTRACE_PROBE1(hotspot_jni, ExceptionDescribe__entry, env); + if (thread->has_pending_exception()) { + Handle ex(thread, thread->pending_exception()); + thread->clear_pending_exception(); + if (ex->is_a(SystemDictionary::threaddeath_klass())) { + // Don't print anything if we are being killed. + } else { + jio_fprintf(defaultStream::error_stream(), "Exception "); + if (thread != NULL && thread->threadObj() != NULL) { + ResourceMark rm(THREAD); + jio_fprintf(defaultStream::error_stream(), + "in thread \"%s\" ", thread->get_thread_name()); + } + if (ex->is_a(SystemDictionary::throwable_klass())) { + JavaValue result(T_VOID); + JavaCalls::call_virtual(&result, + ex, + KlassHandle(THREAD, + SystemDictionary::throwable_klass()), + vmSymbolHandles::printStackTrace_name(), + vmSymbolHandles::void_method_signature(), + THREAD); + // If an exception is thrown in the call it gets thrown away. Not much + // we can do with it. The native code that calls this, does not check + // for the exception - hence, it might still be in the thread when DestroyVM gets + // called, potentially causing a few asserts to trigger - since no pending exception + // is expected. + CLEAR_PENDING_EXCEPTION; + } else { + ResourceMark rm(THREAD); + jio_fprintf(defaultStream::error_stream(), + ". Uncaught exception of type %s.", + Klass::cast(ex->klass())->external_name()); + } + } + } + DTRACE_PROBE(hotspot_jni, ExceptionDescribe__return); +JNI_END + + +JNI_QUICK_ENTRY(void, jni_ExceptionClear(JNIEnv *env)) + JNIWrapper("ExceptionClear"); + DTRACE_PROBE1(hotspot_jni, ExceptionClear__entry, env); + + // The jni code might be using this API to clear java thrown exception. + // So just mark jvmti thread exception state as exception caught. + JvmtiThreadState *state = JavaThread::current()->jvmti_thread_state(); + if (state != NULL && state->is_exception_detected()) { + state->set_exception_caught(); + } + thread->clear_pending_exception(); + DTRACE_PROBE(hotspot_jni, ExceptionClear__return); +JNI_END + + +JNI_ENTRY(void, jni_FatalError(JNIEnv *env, const char *msg)) + JNIWrapper("FatalError"); + DTRACE_PROBE2(hotspot_jni, FatalError__entry, env, msg); + tty->print_cr("FATAL ERROR in native method: %s", msg); + thread->print_stack(); + os::abort(false); // Prevent core dump, causes a jck failure. +JNI_END + + +JNI_ENTRY(jint, jni_PushLocalFrame(JNIEnv *env, jint capacity)) + JNIWrapper("PushLocalFrame"); + DTRACE_PROBE2(hotspot_jni, PushLocalFrame__entry, env, capacity); + //%note jni_11 + if (capacity < 0 && capacity > MAX_REASONABLE_LOCAL_CAPACITY) { + DTRACE_PROBE1(hotspot_jni, PushLocalFrame__return, JNI_ERR); + return JNI_ERR; + } + JNIHandleBlock* old_handles = thread->active_handles(); + JNIHandleBlock* new_handles = JNIHandleBlock::allocate_block(thread); + assert(new_handles != NULL, "should not be NULL"); + new_handles->set_pop_frame_link(old_handles); + thread->set_active_handles(new_handles); + jint ret = JNI_OK; + DTRACE_PROBE1(hotspot_jni, PushLocalFrame__return, ret); + return ret; +JNI_END + + +JNI_ENTRY(jobject, jni_PopLocalFrame(JNIEnv *env, jobject result)) + JNIWrapper("PopLocalFrame"); + DTRACE_PROBE2(hotspot_jni, PopLocalFrame__entry, env, result); + //%note jni_11 + Handle result_handle(thread, JNIHandles::resolve(result)); + JNIHandleBlock* old_handles = thread->active_handles(); + JNIHandleBlock* new_handles = old_handles->pop_frame_link(); + if (new_handles != NULL) { + // As a sanity check we only release the handle blocks if the pop_frame_link is not NULL. + // This way code will still work if PopLocalFrame is called without a corresponding + // PushLocalFrame call. Note that we set the pop_frame_link to NULL explicitly, otherwise + // the release_block call will release the blocks. + thread->set_active_handles(new_handles); + old_handles->set_pop_frame_link(NULL); // clear link we won't release new_handles below + JNIHandleBlock::release_block(old_handles, thread); // may block + result = JNIHandles::make_local(thread, result_handle()); + } + DTRACE_PROBE1(hotspot_jni, PopLocalFrame__return, result); + return result; +JNI_END + + +JNI_ENTRY(jobject, jni_NewGlobalRef(JNIEnv *env, jobject ref)) + JNIWrapper("NewGlobalRef"); + DTRACE_PROBE2(hotspot_jni, NewGlobalRef__entry, env, ref); + Handle ref_handle(thread, JNIHandles::resolve(ref)); + jobject ret = JNIHandles::make_global(ref_handle); + DTRACE_PROBE1(hotspot_jni, NewGlobalRef__return, ret); + return ret; +JNI_END + +// Must be JNI_ENTRY (with HandleMark) +JNI_ENTRY_NO_PRESERVE(void, jni_DeleteGlobalRef(JNIEnv *env, jobject ref)) + JNIWrapper("DeleteGlobalRef"); + DTRACE_PROBE2(hotspot_jni, DeleteGlobalRef__entry, env, ref); + JNIHandles::destroy_global(ref); + DTRACE_PROBE(hotspot_jni, DeleteGlobalRef__return); +JNI_END + +JNI_QUICK_ENTRY(void, jni_DeleteLocalRef(JNIEnv *env, jobject obj)) + JNIWrapper("DeleteLocalRef"); + DTRACE_PROBE2(hotspot_jni, DeleteLocalRef__entry, env, obj); + JNIHandles::destroy_local(obj); + DTRACE_PROBE(hotspot_jni, DeleteLocalRef__return); +JNI_END + +JNI_QUICK_ENTRY(jboolean, jni_IsSameObject(JNIEnv *env, jobject r1, jobject r2)) + JNIWrapper("IsSameObject"); + DTRACE_PROBE3(hotspot_jni, IsSameObject__entry, env, r1, r2); + oop a = JNIHandles::resolve(r1); + oop b = JNIHandles::resolve(r2); + jboolean ret = (a == b) ? JNI_TRUE : JNI_FALSE; + DTRACE_PROBE1(hotspot_jni, IsSameObject__return, ret); + return ret; +JNI_END + + +JNI_ENTRY(jobject, jni_NewLocalRef(JNIEnv *env, jobject ref)) + JNIWrapper("NewLocalRef"); + DTRACE_PROBE2(hotspot_jni, NewLocalRef__entry, env, ref); + jobject ret = JNIHandles::make_local(env, JNIHandles::resolve(ref)); + DTRACE_PROBE1(hotspot_jni, NewLocalRef__return, ret); + return ret; +JNI_END + +JNI_LEAF(jint, jni_EnsureLocalCapacity(JNIEnv *env, jint capacity)) + JNIWrapper("EnsureLocalCapacity"); + DTRACE_PROBE2(hotspot_jni, EnsureLocalCapacity__entry, env, capacity); + jint ret; + if (capacity >= 0 && capacity <= MAX_REASONABLE_LOCAL_CAPACITY) { + ret = JNI_OK; + } else { + ret = JNI_ERR; + } + DTRACE_PROBE1(hotspot_jni, EnsureLocalCapacity__return, ret); + return ret; +JNI_END + +// Return the Handle Type +JNI_LEAF(jobjectRefType, jni_GetObjectRefType(JNIEnv *env, jobject obj)) + JNIWrapper("GetObjectRefType"); + DTRACE_PROBE2(hotspot_jni, GetObjectRefType__entry, env, obj); + jobjectRefType ret; + if (JNIHandles::is_local_handle(thread, obj) || + JNIHandles::is_frame_handle(thread, obj)) + ret = JNILocalRefType; + else if (JNIHandles::is_global_handle(obj)) + ret = JNIGlobalRefType; + else if (JNIHandles::is_weak_global_handle(obj)) + ret = JNIWeakGlobalRefType; + else + ret = JNIInvalidRefType; + DTRACE_PROBE1(hotspot_jni, GetObjectRefType__return, ret); + return ret; +JNI_END + + +class JNI_ArgumentPusher : public SignatureIterator { + protected: + JavaCallArguments* _arguments; + + virtual void get_bool () = 0; + virtual void get_char () = 0; + virtual void get_short () = 0; + virtual void get_byte () = 0; + virtual void get_int () = 0; + virtual void get_long () = 0; + virtual void get_float () = 0; + virtual void get_double () = 0; + virtual void get_object () = 0; + + JNI_ArgumentPusher(Thread *thread, symbolOop signature) + : SignatureIterator(thread, signature) { + this->_return_type = T_ILLEGAL; + _arguments = NULL; + } + + public: + virtual void iterate( uint64_t fingerprint ) = 0; + + void set_java_argument_object(JavaCallArguments *arguments) { _arguments = arguments; } + + inline void do_bool() { if (!is_return_type()) get_bool(); } + inline void do_char() { if (!is_return_type()) get_char(); } + inline void do_short() { if (!is_return_type()) get_short(); } + inline void do_byte() { if (!is_return_type()) get_byte(); } + inline void do_int() { if (!is_return_type()) get_int(); } + inline void do_long() { if (!is_return_type()) get_long(); } + inline void do_float() { if (!is_return_type()) get_float(); } + inline void do_double() { if (!is_return_type()) get_double(); } + inline void do_object(int begin, int end) { if (!is_return_type()) get_object(); } + inline void do_array(int begin, int end) { if (!is_return_type()) get_object(); } // do_array uses get_object -- there is no get_array + inline void do_void() { } + + JavaCallArguments* arguments() { return _arguments; } + void push_receiver(Handle h) { _arguments->push_oop(h); } +}; + + +class JNI_ArgumentPusherVaArg : public JNI_ArgumentPusher { + protected: + va_list _ap; + + inline void get_bool() { _arguments->push_int(va_arg(_ap, jint)); } // bool is coerced to int when using va_arg + inline void get_char() { _arguments->push_int(va_arg(_ap, jint)); } // char is coerced to int when using va_arg + inline void get_short() { _arguments->push_int(va_arg(_ap, jint)); } // short is coerced to int when using va_arg + inline void get_byte() { _arguments->push_int(va_arg(_ap, jint)); } // byte is coerced to int when using va_arg + inline void get_int() { _arguments->push_int(va_arg(_ap, jint)); } + + // each of these paths is exercized by the various jck Call[Static,Nonvirtual,][Void,Int,..]Method[A,V,] tests + + inline void get_long() { _arguments->push_long(va_arg(_ap, jlong)); } + inline void get_float() { _arguments->push_float((jfloat)va_arg(_ap, jdouble)); } // float is coerced to double w/ va_arg + inline void get_double() { _arguments->push_double(va_arg(_ap, jdouble)); } + inline void get_object() { jobject l = va_arg(_ap, jobject); + _arguments->push_oop(Handle((oop *)l, false)); } + + inline void set_ap(va_list rap) { +#ifdef va_copy + va_copy(_ap, rap); +#elif defined (__va_copy) + __va_copy(_ap, rap); +#else + _ap = rap; +#endif + } + + public: + JNI_ArgumentPusherVaArg(Thread *thread, symbolOop signature, va_list rap) + : JNI_ArgumentPusher(thread, signature) { + set_ap(rap); + } + JNI_ArgumentPusherVaArg(Thread *thread, jmethodID method_id, va_list rap) + : JNI_ArgumentPusher(thread, JNIHandles::resolve_jmethod_id(method_id)->signature()) { + set_ap(rap); + } + + // Optimized path if we have the bitvector form of signature + void iterate( uint64_t fingerprint ) { + if ( fingerprint == UCONST64(-1) ) SignatureIterator::iterate();// Must be too many arguments + else { + _return_type = (BasicType)((fingerprint >> static_feature_size) & + result_feature_mask); + + assert(fingerprint, "Fingerprint should not be 0"); + fingerprint = fingerprint >> (static_feature_size + result_feature_size); + while ( 1 ) { + switch ( fingerprint & parameter_feature_mask ) { + case bool_parm: + case char_parm: + case short_parm: + case byte_parm: + case int_parm: + get_int(); + break; + case obj_parm: + get_object(); + break; + case long_parm: + get_long(); + break; + case float_parm: + get_float(); + break; + case double_parm: + get_double(); + break; + case done_parm: + return; + break; + default: + ShouldNotReachHere(); + break; + } + fingerprint >>= parameter_feature_size; + } + } + } +}; + + +class JNI_ArgumentPusherArray : public JNI_ArgumentPusher { + protected: + const jvalue *_ap; + + inline void get_bool() { _arguments->push_int((jint)(_ap++)->z); } + inline void get_char() { _arguments->push_int((jint)(_ap++)->c); } + inline void get_short() { _arguments->push_int((jint)(_ap++)->s); } + inline void get_byte() { _arguments->push_int((jint)(_ap++)->b); } + inline void get_int() { _arguments->push_int((jint)(_ap++)->i); } + + inline void get_long() { _arguments->push_long((_ap++)->j); } + inline void get_float() { _arguments->push_float((_ap++)->f); } + inline void get_double() { _arguments->push_double((_ap++)->d);} + inline void get_object() { _arguments->push_oop(Handle((oop *)(_ap++)->l, false)); } + + inline void set_ap(const jvalue *rap) { _ap = rap; } + + public: + JNI_ArgumentPusherArray(Thread *thread, symbolOop signature, const jvalue *rap) + : JNI_ArgumentPusher(thread, signature) { + set_ap(rap); + } + JNI_ArgumentPusherArray(Thread *thread, jmethodID method_id, const jvalue *rap) + : JNI_ArgumentPusher(thread, JNIHandles::resolve_jmethod_id(method_id)->signature()) { + set_ap(rap); + } + + // Optimized path if we have the bitvector form of signature + void iterate( uint64_t fingerprint ) { + if ( fingerprint == UCONST64(-1) ) SignatureIterator::iterate(); // Must be too many arguments + else { + _return_type = (BasicType)((fingerprint >> static_feature_size) & + result_feature_mask); + assert(fingerprint, "Fingerprint should not be 0"); + fingerprint = fingerprint >> (static_feature_size + result_feature_size); + while ( 1 ) { + switch ( fingerprint & parameter_feature_mask ) { + case bool_parm: + get_bool(); + break; + case char_parm: + get_char(); + break; + case short_parm: + get_short(); + break; + case byte_parm: + get_byte(); + break; + case int_parm: + get_int(); + break; + case obj_parm: + get_object(); + break; + case long_parm: + get_long(); + break; + case float_parm: + get_float(); + break; + case double_parm: + get_double(); + break; + case done_parm: + return; + break; + default: + ShouldNotReachHere(); + break; + } + fingerprint >>= parameter_feature_size; + } + } + } +}; + + +enum JNICallType { + JNI_STATIC, + JNI_VIRTUAL, + JNI_NONVIRTUAL +}; + +static methodHandle jni_resolve_interface_call(Handle recv, methodHandle method, TRAPS) { + assert(!method.is_null() , "method should not be null"); + + KlassHandle recv_klass; // Default to NULL (use of ?: can confuse gcc) + if (recv.not_null()) recv_klass = KlassHandle(THREAD, recv->klass()); + KlassHandle spec_klass (THREAD, method->method_holder()); + symbolHandle name (THREAD, method->name()); + symbolHandle signature (THREAD, method->signature()); + CallInfo info; + LinkResolver::resolve_interface_call(info, recv, recv_klass, spec_klass, name, signature, KlassHandle(), false, true, CHECK_(methodHandle())); + return info.selected_method(); +} + +static methodHandle jni_resolve_virtual_call(Handle recv, methodHandle method, TRAPS) { + assert(!method.is_null() , "method should not be null"); + + KlassHandle recv_klass; // Default to NULL (use of ?: can confuse gcc) + if (recv.not_null()) recv_klass = KlassHandle(THREAD, recv->klass()); + KlassHandle spec_klass (THREAD, method->method_holder()); + symbolHandle name (THREAD, method->name()); + symbolHandle signature (THREAD, method->signature()); + CallInfo info; + LinkResolver::resolve_virtual_call(info, recv, recv_klass, spec_klass, name, signature, KlassHandle(), false, true, CHECK_(methodHandle())); + return info.selected_method(); +} + + + +static void jni_invoke_static(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) { + methodHandle method(THREAD, JNIHandles::resolve_jmethod_id(method_id)); + + // Create object to hold arguments for the JavaCall, and associate it with + // the jni parser + ResourceMark rm(THREAD); + int number_of_parameters = method->size_of_parameters(); + JavaCallArguments java_args(number_of_parameters); + args->set_java_argument_object(&java_args); + + assert(method->is_static(), "method should be static"); + + // Fill out JavaCallArguments object + args->iterate( Fingerprinter(THREAD, method).fingerprint() ); + // Initialize result type + result->set_type(args->get_ret_type()); + + // Invoke the method. Result is returned as oop. + JavaCalls::call(result, method, &java_args, CHECK); + + // Convert result + if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) { + result->set_jobject(JNIHandles::make_local(env, (oop) result->get_jobject())); + } +} + + +static void jni_invoke_nonstatic(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) { + oop recv = JNIHandles::resolve(receiver); + if (recv == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + Handle h_recv(THREAD, recv); + + int number_of_parameters; + methodOop selected_method; + { + methodOop m = JNIHandles::resolve_jmethod_id(method_id); + number_of_parameters = m->size_of_parameters(); + klassOop holder = m->method_holder(); + if (!(Klass::cast(holder))->is_interface()) { + // non-interface call -- for that little speed boost, don't handlize + debug_only(No_Safepoint_Verifier nosafepoint;) + if (call_type == JNI_VIRTUAL) { + // jni_GetMethodID makes sure class is linked and initialized + // so m should have a valid vtable index. + int vtbl_index = m->vtable_index(); + if (vtbl_index != methodOopDesc::nonvirtual_vtable_index) { + klassOop k = h_recv->klass(); + // k might be an arrayKlassOop but all vtables start at + // the same place. The cast is to avoid virtual call and assertion. + instanceKlass *ik = (instanceKlass*)k->klass_part(); + selected_method = ik->method_at_vtable(vtbl_index); + } else { + // final method + selected_method = m; + } + } else { + // JNI_NONVIRTUAL call + selected_method = m; + } + } else { + // interface call + KlassHandle h_holder(THREAD, holder); + + int itbl_index = m->cached_itable_index(); + if (itbl_index == -1) { + itbl_index = klassItable::compute_itable_index(m); + m->set_cached_itable_index(itbl_index); + // the above may have grabbed a lock, 'm' and anything non-handlized can't be used again + } + klassOop k = h_recv->klass(); + selected_method = instanceKlass::cast(k)->method_at_itable(h_holder(), itbl_index, CHECK); + } + } + + methodHandle method(THREAD, selected_method); + + // Create object to hold arguments for the JavaCall, and associate it with + // the jni parser + ResourceMark rm(THREAD); + JavaCallArguments java_args(number_of_parameters); + args->set_java_argument_object(&java_args); + + // handle arguments + assert(!method->is_static(), "method should not be static"); + args->push_receiver(h_recv); // Push jobject handle + + // Fill out JavaCallArguments object + args->iterate( Fingerprinter(THREAD, method).fingerprint() ); + // Initialize result type + result->set_type(args->get_ret_type()); + + // Invoke the method. Result is returned as oop. + JavaCalls::call(result, method, &java_args, CHECK); + + // Convert result + if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) { + result->set_jobject(JNIHandles::make_local(env, (oop) result->get_jobject())); + } +} + + +static instanceOop alloc_object(jclass clazz, TRAPS) { + KlassHandle k(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz))); + Klass::cast(k())->check_valid_for_instantiation(false, CHECK_NULL); + instanceKlass::cast(k())->initialize(CHECK_NULL); + instanceOop ih = instanceKlass::cast(k())->allocate_instance(THREAD); + return ih; +} + +DT_RETURN_MARK_DECL(AllocObject, jobject); + +JNI_ENTRY(jobject, jni_AllocObject(JNIEnv *env, jclass clazz)) + JNIWrapper("AllocObject"); + + DTRACE_PROBE2(hotspot_jni, AllocObject__entry, env, clazz); + jobject ret = NULL; + DT_RETURN_MARK(AllocObject, jobject, (const jobject&)ret); + + instanceOop i = alloc_object(clazz, CHECK_NULL); + ret = JNIHandles::make_local(env, i); + return ret; +JNI_END + +DT_RETURN_MARK_DECL(NewObjectA, jobject); + +JNI_ENTRY(jobject, jni_NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args)) + JNIWrapper("NewObjectA"); + DTRACE_PROBE3(hotspot_jni, NewObjectA__entry, env, clazz, methodID); + jobject obj = NULL; + DT_RETURN_MARK(NewObjectA, jobject, (const jobject)obj); + + instanceOop i = alloc_object(clazz, CHECK_NULL); + obj = JNIHandles::make_local(env, i); + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherArray ap(THREAD, methodID, args); + jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK_NULL); + return obj; +JNI_END + +DT_RETURN_MARK_DECL(NewObjectV, jobject); + +JNI_ENTRY(jobject, jni_NewObjectV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args)) + JNIWrapper("NewObjectV"); + DTRACE_PROBE3(hotspot_jni, NewObjectV__entry, env, clazz, methodID); + jobject obj = NULL; + DT_RETURN_MARK(NewObjectV, jobject, (const jobject&)obj); + + instanceOop i = alloc_object(clazz, CHECK_NULL); + obj = JNIHandles::make_local(env, i); + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); + jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK_NULL); + return obj; +JNI_END + +DT_RETURN_MARK_DECL(NewObject, jobject); + +JNI_ENTRY(jobject, jni_NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, ...)) + JNIWrapper("NewObject"); + DTRACE_PROBE3(hotspot_jni, NewObject__entry, env, clazz, methodID); + jobject obj = NULL; + DT_RETURN_MARK(NewObject, jobject, (const jobject&)obj); + + instanceOop i = alloc_object(clazz, CHECK_NULL); + obj = JNIHandles::make_local(env, i); + va_list args; + va_start(args, methodID); + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); + jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK_NULL); + va_end(args); + return obj; +JNI_END + + +JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, jobject obj)) + JNIWrapper("GetObjectClass"); + DTRACE_PROBE2(hotspot_jni, GetObjectClass__entry, env, obj); + klassOop k = JNIHandles::resolve_non_null(obj)->klass(); + jclass ret = + (jclass) JNIHandles::make_local(env, Klass::cast(k)->java_mirror()); + DTRACE_PROBE1(hotspot_jni, GetObjectClass__return, ret); + return ret; +JNI_END + +JNI_QUICK_ENTRY(jboolean, jni_IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz)) + JNIWrapper("IsInstanceOf"); + DTRACE_PROBE3(hotspot_jni, IsInstanceOf__entry, env, obj, clazz); + jboolean ret = JNI_TRUE; + if (obj != NULL) { + ret = JNI_FALSE; + klassOop k = java_lang_Class::as_klassOop( + JNIHandles::resolve_non_null(clazz)); + if (k != NULL) { + ret = JNIHandles::resolve_non_null(obj)->is_a(k) ? JNI_TRUE : JNI_FALSE; + } + } + DTRACE_PROBE1(hotspot_jni, IsInstanceOf__return, ret); + return ret; +JNI_END + + +static jmethodID get_method_id(JNIEnv *env, jclass clazz, const char *name_str, + const char *sig, bool is_static, TRAPS) { + // %%%% This code should probably just call into a method in the LinkResolver + // + // The class should have been loaded (we have an instance of the class + // passed in) so the method and signature should already be in the symbol + // table. If they're not there, the method doesn't exist. + symbolHandle signature = + symbolHandle(THREAD, SymbolTable::probe(sig, (int)strlen(sig))); + symbolHandle name; + if (name_str == NULL) { + name = vmSymbolHandles::object_initializer_name(); + } else { + name = symbolHandle(THREAD, + SymbolTable::probe(name_str, (int)strlen(name_str))); + } + if (name.is_null() || signature.is_null()) { + THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), name_str); + } + + // Throw a NoSuchMethodError exception if we have an instance of a + // primitive java.lang.Class + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(clazz))) { + THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), name_str); + } + + KlassHandle klass(THREAD, + java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz))); + + // Make sure class is linked and initialized before handing id's out to + // methodOops. + Klass::cast(klass())->initialize(CHECK_NULL); + + methodOop m; + if (name() == vmSymbols::object_initializer_name() || + name() == vmSymbols::class_initializer_name()) { + // Never search superclasses for constructors + if (klass->oop_is_instance()) { + m = instanceKlass::cast(klass())->find_method(name(), signature()); + } else { + m = NULL; + } + } else { + m = klass->lookup_method(name(), signature()); + // Look up interfaces + if (m == NULL && klass->oop_is_instance()) { + m = instanceKlass::cast(klass())->lookup_method_in_all_interfaces(name(), + signature()); + } + } + if (m == NULL || (m->is_static() != is_static)) { + THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), name_str); + } + return m->jmethod_id(); +} + + +JNI_ENTRY(jmethodID, jni_GetMethodID(JNIEnv *env, jclass clazz, + const char *name, const char *sig)) + JNIWrapper("GetMethodID"); + DTRACE_PROBE4(hotspot_jni, GetMethodID__entry, env, clazz, name, sig); + jmethodID ret = get_method_id(env, clazz, name, sig, false, thread); + DTRACE_PROBE1(hotspot_jni, GetMethodID__return, ret); + return ret; +JNI_END + + +JNI_ENTRY(jmethodID, jni_GetStaticMethodID(JNIEnv *env, jclass clazz, + const char *name, const char *sig)) + JNIWrapper("GetStaticMethodID"); + DTRACE_PROBE4(hotspot_jni, GetStaticMethodID__entry, env, clazz, name, sig); + jmethodID ret = get_method_id(env, clazz, name, sig, true, thread); + DTRACE_PROBE1(hotspot_jni, GetStaticMethodID__return, ret); + return ret; +JNI_END + + + +// +// Calling Methods +// + + +#define DEFINE_CALLMETHOD(ResultType, Result, Tag) \ +\ + DT_RETURN_MARK_DECL_FOR(Result, Call##Result##Method, ResultType);\ + DT_RETURN_MARK_DECL_FOR(Result, Call##Result##MethodV, ResultType);\ + DT_RETURN_MARK_DECL_FOR(Result, Call##Result##MethodA, ResultType);\ +\ +JNI_ENTRY(ResultType, \ + jni_Call##Result##Method(JNIEnv *env, jobject obj, jmethodID methodID, ...)) \ + JNIWrapper("Call" XSTR(Result) "Method"); \ +\ + DTRACE_PROBE3(hotspot_jni, Call##Result##Method__entry, env, obj, methodID);\ + ResultType ret = 0;\ + DT_RETURN_MARK_FOR(Result, Call##Result##Method, ResultType, \ + (const ResultType&)ret);\ +\ + va_list args; \ + va_start(args, methodID); \ + JavaValue jvalue(Tag); \ + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); \ + jni_invoke_nonstatic(env, &jvalue, obj, JNI_VIRTUAL, methodID, &ap, CHECK_0); \ + va_end(args); \ + ret = jvalue.get_##ResultType(); \ + return ret;\ +JNI_END \ +\ +\ +JNI_ENTRY(ResultType, \ + jni_Call##Result##MethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args)) \ + JNIWrapper("Call" XSTR(Result) "MethodV"); \ +\ + DTRACE_PROBE3(hotspot_jni, Call##Result##MethodV__entry, env, obj, methodID);\ + ResultType ret = 0;\ + DT_RETURN_MARK_FOR(Result, Call##Result##MethodV, ResultType, \ + (const ResultType&)ret);\ +\ + JavaValue jvalue(Tag); \ + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); \ + jni_invoke_nonstatic(env, &jvalue, obj, JNI_VIRTUAL, methodID, &ap, CHECK_0); \ + ret = jvalue.get_##ResultType(); \ + return ret;\ +JNI_END \ +\ +\ +JNI_ENTRY(ResultType, \ + jni_Call##Result##MethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args)) \ + JNIWrapper("Call" XSTR(Result) "MethodA"); \ + DTRACE_PROBE3(hotspot_jni, Call##Result##MethodA__entry, env, obj, methodID);\ + ResultType ret = 0;\ + DT_RETURN_MARK_FOR(Result, Call##Result##MethodA, ResultType, \ + (const ResultType&)ret);\ +\ + JavaValue jvalue(Tag); \ + JNI_ArgumentPusherArray ap(THREAD, methodID, args); \ + jni_invoke_nonstatic(env, &jvalue, obj, JNI_VIRTUAL, methodID, &ap, CHECK_0); \ + ret = jvalue.get_##ResultType(); \ + return ret;\ +JNI_END + +// the runtime type of subword integral basic types is integer +DEFINE_CALLMETHOD(jboolean, Boolean, T_BOOLEAN) +DEFINE_CALLMETHOD(jbyte, Byte, T_BYTE) +DEFINE_CALLMETHOD(jchar, Char, T_CHAR) +DEFINE_CALLMETHOD(jshort, Short, T_SHORT) + +DEFINE_CALLMETHOD(jobject, Object, T_OBJECT) +DEFINE_CALLMETHOD(jint, Int, T_INT) +DEFINE_CALLMETHOD(jlong, Long, T_LONG) +DEFINE_CALLMETHOD(jfloat, Float, T_FLOAT) +DEFINE_CALLMETHOD(jdouble, Double, T_DOUBLE) + +DT_VOID_RETURN_MARK_DECL(CallVoidMethod); +DT_VOID_RETURN_MARK_DECL(CallVoidMethodV); +DT_VOID_RETURN_MARK_DECL(CallVoidMethodA); + +JNI_ENTRY(void, jni_CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...)) + JNIWrapper("CallVoidMethod"); + DTRACE_PROBE3(hotspot_jni, CallVoidMethod__entry, env, obj, methodID); + DT_VOID_RETURN_MARK(CallVoidMethod); + + va_list args; + va_start(args, methodID); + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); + jni_invoke_nonstatic(env, &jvalue, obj, JNI_VIRTUAL, methodID, &ap, CHECK); + va_end(args); +JNI_END + + +JNI_ENTRY(void, jni_CallVoidMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args)) + JNIWrapper("CallVoidMethodV"); + DTRACE_PROBE3(hotspot_jni, CallVoidMethodV__entry, env, obj, methodID); + DT_VOID_RETURN_MARK(CallVoidMethodV); + + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); + jni_invoke_nonstatic(env, &jvalue, obj, JNI_VIRTUAL, methodID, &ap, CHECK); +JNI_END + + +JNI_ENTRY(void, jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args)) + JNIWrapper("CallVoidMethodA"); + DTRACE_PROBE3(hotspot_jni, CallVoidMethodA__entry, env, obj, methodID); + DT_VOID_RETURN_MARK(CallVoidMethodA); + + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherArray ap(THREAD, methodID, args); + jni_invoke_nonstatic(env, &jvalue, obj, JNI_VIRTUAL, methodID, &ap, CHECK); +JNI_END + + +#define DEFINE_CALLNONVIRTUALMETHOD(ResultType, Result, Tag) \ +\ + DT_RETURN_MARK_DECL_FOR(Result, CallNonvirtual##Result##Method, ResultType);\ + DT_RETURN_MARK_DECL_FOR(Result, CallNonvirtual##Result##MethodV, ResultType);\ + DT_RETURN_MARK_DECL_FOR(Result, CallNonvirtual##Result##MethodA, ResultType);\ +\ +JNI_ENTRY(ResultType, \ + jni_CallNonvirtual##Result##Method(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, ...)) \ + JNIWrapper("CallNonvitual" XSTR(Result) "Method"); \ +\ + DTRACE_PROBE4(hotspot_jni, CallNonvirtual##Result##Method__entry, env, obj, cls, methodID);\ + ResultType ret;\ + DT_RETURN_MARK_FOR(Result, CallNonvirtual##Result##Method, ResultType, \ + (const ResultType&)ret);\ +\ + va_list args; \ + va_start(args, methodID); \ + JavaValue jvalue(Tag); \ + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); \ + jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK_0); \ + va_end(args); \ + ret = jvalue.get_##ResultType(); \ + return ret;\ +JNI_END \ +\ +JNI_ENTRY(ResultType, \ + jni_CallNonvirtual##Result##MethodV(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, va_list args)) \ + JNIWrapper("CallNonvitual" XSTR(Result) "#MethodV"); \ + DTRACE_PROBE4(hotspot_jni, CallNonvirtual##Result##MethodV__entry, env, obj, cls, methodID);\ + ResultType ret;\ + DT_RETURN_MARK_FOR(Result, CallNonvirtual##Result##MethodV, ResultType, \ + (const ResultType&)ret);\ +\ + JavaValue jvalue(Tag); \ + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); \ + jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK_0); \ + ret = jvalue.get_##ResultType(); \ + return ret;\ +JNI_END \ +\ +JNI_ENTRY(ResultType, \ + jni_CallNonvirtual##Result##MethodA(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, const jvalue *args)) \ + JNIWrapper("CallNonvitual" XSTR(Result) "MethodA"); \ + DTRACE_PROBE4(hotspot_jni, CallNonvirtual##Result##MethodA__entry, env, obj, cls, methodID);\ + ResultType ret;\ + DT_RETURN_MARK_FOR(Result, CallNonvirtual##Result##MethodA, ResultType, \ + (const ResultType&)ret);\ +\ + JavaValue jvalue(Tag); \ + JNI_ArgumentPusherArray ap(THREAD, methodID, args); \ + jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK_0); \ + ret = jvalue.get_##ResultType(); \ + return ret;\ +JNI_END + +// the runtime type of subword integral basic types is integer +DEFINE_CALLNONVIRTUALMETHOD(jboolean, Boolean, T_BOOLEAN) +DEFINE_CALLNONVIRTUALMETHOD(jbyte, Byte, T_BYTE) +DEFINE_CALLNONVIRTUALMETHOD(jchar, Char, T_CHAR) +DEFINE_CALLNONVIRTUALMETHOD(jshort, Short, T_SHORT) + +DEFINE_CALLNONVIRTUALMETHOD(jobject, Object, T_OBJECT) +DEFINE_CALLNONVIRTUALMETHOD(jint, Int, T_INT) +DEFINE_CALLNONVIRTUALMETHOD(jlong, Long, T_LONG) +DEFINE_CALLNONVIRTUALMETHOD(jfloat, Float, T_FLOAT) +DEFINE_CALLNONVIRTUALMETHOD(jdouble, Double, T_DOUBLE) + + +DT_VOID_RETURN_MARK_DECL(CallNonvirtualVoidMethod); +DT_VOID_RETURN_MARK_DECL(CallNonvirtualVoidMethodV); +DT_VOID_RETURN_MARK_DECL(CallNonvirtualVoidMethodA); + +JNI_ENTRY(void, jni_CallNonvirtualVoidMethod(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, ...)) + JNIWrapper("CallNonvirtualVoidMethod"); + + DTRACE_PROBE4(hotspot_jni, CallNonvirtualVoidMethod__entry, + env, obj, cls, methodID); + DT_VOID_RETURN_MARK(CallNonvirtualVoidMethod); + + va_list args; + va_start(args, methodID); + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); + jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK); + va_end(args); +JNI_END + + +JNI_ENTRY(void, jni_CallNonvirtualVoidMethodV(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, va_list args)) + JNIWrapper("CallNonvirtualVoidMethodV"); + + DTRACE_PROBE4(hotspot_jni, CallNonvirtualVoidMethodV__entry, + env, obj, cls, methodID); + DT_VOID_RETURN_MARK(CallNonvirtualVoidMethodV); + + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); + jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK); +JNI_END + + +JNI_ENTRY(void, jni_CallNonvirtualVoidMethodA(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, const jvalue *args)) + JNIWrapper("CallNonvirtualVoidMethodA"); + DTRACE_PROBE4(hotspot_jni, CallNonvirtualVoidMethodA__entry, + env, obj, cls, methodID); + DT_VOID_RETURN_MARK(CallNonvirtualVoidMethodA); + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherArray ap(THREAD, methodID, args); + jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK); +JNI_END + + +#define DEFINE_CALLSTATICMETHOD(ResultType, Result, Tag) \ +\ + DT_RETURN_MARK_DECL_FOR(Result, CallStatic##Result##Method, ResultType);\ + DT_RETURN_MARK_DECL_FOR(Result, CallStatic##Result##MethodV, ResultType);\ + DT_RETURN_MARK_DECL_FOR(Result, CallStatic##Result##MethodA, ResultType);\ +\ +JNI_ENTRY(ResultType, \ + jni_CallStatic##Result##Method(JNIEnv *env, jclass cls, jmethodID methodID, ...)) \ + JNIWrapper("CallStatic" XSTR(Result) "Method"); \ +\ + DTRACE_PROBE3(hotspot_jni, CallStatic##Result##Method__entry, env, cls, methodID);\ + ResultType ret = 0;\ + DT_RETURN_MARK_FOR(Result, CallStatic##Result##Method, ResultType, \ + (const ResultType&)ret);\ +\ + va_list args; \ + va_start(args, methodID); \ + JavaValue jvalue(Tag); \ + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); \ + jni_invoke_static(env, &jvalue, NULL, JNI_STATIC, methodID, &ap, CHECK_0); \ + va_end(args); \ + ret = jvalue.get_##ResultType(); \ + return ret;\ +JNI_END \ +\ +JNI_ENTRY(ResultType, \ + jni_CallStatic##Result##MethodV(JNIEnv *env, jclass cls, jmethodID methodID, va_list args)) \ + JNIWrapper("CallStatic" XSTR(Result) "MethodV"); \ + DTRACE_PROBE3(hotspot_jni, CallStatic##Result##MethodV__entry, env, cls, methodID);\ + ResultType ret = 0;\ + DT_RETURN_MARK_FOR(Result, CallStatic##Result##MethodV, ResultType, \ + (const ResultType&)ret);\ +\ + JavaValue jvalue(Tag); \ + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); \ + jni_invoke_static(env, &jvalue, NULL, JNI_STATIC, methodID, &ap, CHECK_0); \ + ret = jvalue.get_##ResultType(); \ + return ret;\ +JNI_END \ +\ +JNI_ENTRY(ResultType, \ + jni_CallStatic##Result##MethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args)) \ + JNIWrapper("CallStatic" XSTR(Result) "MethodA"); \ + DTRACE_PROBE3(hotspot_jni, CallStatic##Result##MethodA__entry, env, cls, methodID);\ + ResultType ret = 0;\ + DT_RETURN_MARK_FOR(Result, CallStatic##Result##MethodA, ResultType, \ + (const ResultType&)ret);\ +\ + JavaValue jvalue(Tag); \ + JNI_ArgumentPusherArray ap(THREAD, methodID, args); \ + jni_invoke_static(env, &jvalue, NULL, JNI_STATIC, methodID, &ap, CHECK_0); \ + ret = jvalue.get_##ResultType(); \ + return ret;\ +JNI_END + +// the runtime type of subword integral basic types is integer +DEFINE_CALLSTATICMETHOD(jboolean, Boolean, T_BOOLEAN) +DEFINE_CALLSTATICMETHOD(jbyte, Byte, T_BYTE) +DEFINE_CALLSTATICMETHOD(jchar, Char, T_CHAR) +DEFINE_CALLSTATICMETHOD(jshort, Short, T_SHORT) + +DEFINE_CALLSTATICMETHOD(jobject, Object, T_OBJECT) +DEFINE_CALLSTATICMETHOD(jint, Int, T_INT) +DEFINE_CALLSTATICMETHOD(jlong, Long, T_LONG) +DEFINE_CALLSTATICMETHOD(jfloat, Float, T_FLOAT) +DEFINE_CALLSTATICMETHOD(jdouble, Double, T_DOUBLE) + + +DT_VOID_RETURN_MARK_DECL(CallStaticVoidMethod); +DT_VOID_RETURN_MARK_DECL(CallStaticVoidMethodV); +DT_VOID_RETURN_MARK_DECL(CallStaticVoidMethodA); + +JNI_ENTRY(void, jni_CallStaticVoidMethod(JNIEnv *env, jclass cls, jmethodID methodID, ...)) + JNIWrapper("CallStaticVoidMethod"); + DTRACE_PROBE3(hotspot_jni, CallStaticVoidMethod__entry, env, cls, methodID); + DT_VOID_RETURN_MARK(CallStaticVoidMethod); + + va_list args; + va_start(args, methodID); + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); + jni_invoke_static(env, &jvalue, NULL, JNI_STATIC, methodID, &ap, CHECK); + va_end(args); +JNI_END + + +JNI_ENTRY(void, jni_CallStaticVoidMethodV(JNIEnv *env, jclass cls, jmethodID methodID, va_list args)) + JNIWrapper("CallStaticVoidMethodV"); + DTRACE_PROBE3(hotspot_jni, CallStaticVoidMethodV__entry, env, cls, methodID); + DT_VOID_RETURN_MARK(CallStaticVoidMethodV); + + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherVaArg ap(THREAD, methodID, args); + jni_invoke_static(env, &jvalue, NULL, JNI_STATIC, methodID, &ap, CHECK); +JNI_END + + +JNI_ENTRY(void, jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args)) + JNIWrapper("CallStaticVoidMethodA"); + DTRACE_PROBE3(hotspot_jni, CallStaticVoidMethodA__entry, env, cls, methodID); + DT_VOID_RETURN_MARK(CallStaticVoidMethodA); + + JavaValue jvalue(T_VOID); + JNI_ArgumentPusherArray ap(THREAD, methodID, args); + jni_invoke_static(env, &jvalue, NULL, JNI_STATIC, methodID, &ap, CHECK); +JNI_END + + +// +// Accessing Fields +// + + +DT_RETURN_MARK_DECL(GetFieldID, jfieldID); + +JNI_ENTRY(jfieldID, jni_GetFieldID(JNIEnv *env, jclass clazz, + const char *name, const char *sig)) + JNIWrapper("GetFieldID"); + DTRACE_PROBE4(hotspot_jni, GetFieldID__entry, env, clazz, name, sig); + jfieldID ret = 0; + DT_RETURN_MARK(GetFieldID, jfieldID, (const jfieldID&)ret); + + // The class should have been loaded (we have an instance of the class + // passed in) so the field and signature should already be in the symbol + // table. If they're not there, the field doesn't exist. + symbolHandle fieldname = + symbolHandle(THREAD, SymbolTable::probe(name, (int)strlen(name))); + symbolHandle signame = + symbolHandle(THREAD, SymbolTable::probe(sig, (int)strlen(sig))); + if (fieldname.is_null() || signame.is_null()) { + THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), (char*) name); + } + KlassHandle k(THREAD, + java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz))); + // Make sure class is initialized before handing id's out to fields + Klass::cast(k())->initialize(CHECK_NULL); + + fieldDescriptor fd; + if (!Klass::cast(k())->oop_is_instance() || + !instanceKlass::cast(k())->find_field(fieldname(), signame(), false, &fd)) { + THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), (char*) name); + } + + // A jfieldID for a non-static field is simply the offset of the field within the instanceOop + // It may also have hash bits for k, if VerifyJNIFields is turned on. + ret = jfieldIDWorkaround::to_instance_jfieldID(k(), fd.offset()); + return ret; +JNI_END + + +JNI_ENTRY(jobject, jni_GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID)) + JNIWrapper("GetObjectField"); + DTRACE_PROBE3(hotspot_jni, GetObjectField__entry, env, obj, fieldID); + oop o = JNIHandles::resolve_non_null(obj); + klassOop k = o->klass(); + int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID); + // Keep JVMTI addition small and only check enabled flag here. + // jni_GetField_probe() assumes that is okay to create handles. + if (JvmtiExport::should_post_field_access()) { + o = JvmtiExport::jni_GetField_probe(thread, obj, o, k, fieldID, false); + } + jobject ret = JNIHandles::make_local(env, o->obj_field(offset)); + DTRACE_PROBE1(hotspot_jni, GetObjectField__return, ret); + return ret; +JNI_END + + +#define DEFINE_GETFIELD(Return,Fieldname,Result) \ +\ + DT_RETURN_MARK_DECL_FOR(Result, Get##Result##Field, Return);\ +\ +JNI_QUICK_ENTRY(Return, jni_Get##Result##Field(JNIEnv *env, jobject obj, jfieldID fieldID)) \ + JNIWrapper("Get" XSTR(Result) "Field"); \ +\ + DTRACE_PROBE3(hotspot_jni, Get##Result##Field__entry, env, obj, fieldID);\ + Return ret = 0;\ + DT_RETURN_MARK_FOR(Result, Get##Result##Field, Return, (const Return&)ret);\ +\ + oop o = JNIHandles::resolve_non_null(obj); \ + klassOop k = o->klass(); \ + int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID); \ + /* Keep JVMTI addition small and only check enabled flag here. */ \ + /* jni_GetField_probe_nh() assumes that is not okay to create handles */ \ + /* and creates a ResetNoHandleMark. */ \ + if (JvmtiExport::should_post_field_access()) { \ + o = JvmtiExport::jni_GetField_probe_nh(thread, obj, o, k, fieldID, false); \ + } \ + ret = o->Fieldname##_field(offset); \ + return ret; \ +JNI_END + +DEFINE_GETFIELD(jboolean, bool, Boolean) +DEFINE_GETFIELD(jbyte, byte, Byte) +DEFINE_GETFIELD(jchar, char, Char) +DEFINE_GETFIELD(jshort, short, Short) +DEFINE_GETFIELD(jint, int, Int) +DEFINE_GETFIELD(jlong, long, Long) +DEFINE_GETFIELD(jfloat, float, Float) +DEFINE_GETFIELD(jdouble, double, Double) + +address jni_GetBooleanField_addr() { + return (address)jni_GetBooleanField; +} +address jni_GetByteField_addr() { + return (address)jni_GetByteField; +} +address jni_GetCharField_addr() { + return (address)jni_GetCharField; +} +address jni_GetShortField_addr() { + return (address)jni_GetShortField; +} +address jni_GetIntField_addr() { + return (address)jni_GetIntField; +} +address jni_GetLongField_addr() { + return (address)jni_GetLongField; +} +address jni_GetFloatField_addr() { + return (address)jni_GetFloatField; +} +address jni_GetDoubleField_addr() { + return (address)jni_GetDoubleField; +} + +JNI_QUICK_ENTRY(void, jni_SetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID, jobject value)) + JNIWrapper("SetObjectField"); + DTRACE_PROBE4(hotspot_jni, SetObjectField__entry, env, obj, fieldID, value); + oop o = JNIHandles::resolve_non_null(obj); + klassOop k = o->klass(); + int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID); + // Keep JVMTI addition small and only check enabled flag here. + // jni_SetField_probe_nh() assumes that is not okay to create handles + // and creates a ResetNoHandleMark. + if (JvmtiExport::should_post_field_modification()) { + jvalue field_value; + field_value.l = value; + o = JvmtiExport::jni_SetField_probe_nh(thread, obj, o, k, fieldID, false, 'L', (jvalue *)&field_value); + } + o->obj_field_put(offset, JNIHandles::resolve(value)); + DTRACE_PROBE(hotspot_jni, SetObjectField__return); +JNI_END + +#define DEFINE_SETFIELD(Argument,Fieldname,Result,SigType,unionType) \ +\ +JNI_QUICK_ENTRY(void, jni_Set##Result##Field(JNIEnv *env, jobject obj, jfieldID fieldID, Argument value)) \ + JNIWrapper("Set" XSTR(Result) "Field"); \ +\ + HS_DTRACE_PROBE_CDECL_N(hotspot_jni, Set##Result##Field__entry, \ + ( JNIEnv*, jobject, jfieldID FP_SELECT_##Result(COMMA Argument,/*empty*/) ) ); \ + HS_DTRACE_PROBE_N(hotspot_jni, Set##Result##Field__entry, \ + ( env, obj, fieldID FP_SELECT_##Result(COMMA value,/*empty*/) ) ); \ +\ + oop o = JNIHandles::resolve_non_null(obj); \ + klassOop k = o->klass(); \ + int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID); \ + /* Keep JVMTI addition small and only check enabled flag here. */ \ + /* jni_SetField_probe_nh() assumes that is not okay to create handles */ \ + /* and creates a ResetNoHandleMark. */ \ + if (JvmtiExport::should_post_field_modification()) { \ + jvalue field_value; \ + field_value.unionType = value; \ + o = JvmtiExport::jni_SetField_probe_nh(thread, obj, o, k, fieldID, false, SigType, (jvalue *)&field_value); \ + } \ + o->Fieldname##_field_put(offset, value); \ + DTRACE_PROBE(hotspot_jni, Set##Result##Field__return);\ +JNI_END + +DEFINE_SETFIELD(jboolean, bool, Boolean, 'Z', z) +DEFINE_SETFIELD(jbyte, byte, Byte, 'B', b) +DEFINE_SETFIELD(jchar, char, Char, 'C', c) +DEFINE_SETFIELD(jshort, short, Short, 'S', s) +DEFINE_SETFIELD(jint, int, Int, 'I', i) +DEFINE_SETFIELD(jlong, long, Long, 'J', j) +DEFINE_SETFIELD(jfloat, float, Float, 'F', f) +DEFINE_SETFIELD(jdouble, double, Double, 'D', d) + +DT_RETURN_MARK_DECL(ToReflectedField, jobject); + +JNI_ENTRY(jobject, jni_ToReflectedField(JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic)) + JNIWrapper("ToReflectedField"); + DTRACE_PROBE4(hotspot_jni, ToReflectedField__entry, + env, cls, fieldID, isStatic); + jobject ret = NULL; + DT_RETURN_MARK(ToReflectedField, jobject, (const jobject&)ret); + + fieldDescriptor fd; + bool found = false; + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + + assert(jfieldIDWorkaround::is_static_jfieldID(fieldID) == (isStatic != 0), "invalid fieldID"); + + if (isStatic) { + // Static field. The fieldID a JNIid specifying the field holder and the offset within the klassOop. + JNIid* id = jfieldIDWorkaround::from_static_jfieldID(fieldID); + assert(id->is_static_field_id(), "invalid static field id"); + found = instanceKlass::cast(id->holder())->find_local_field_from_offset(id->offset(), true, &fd); + } else { + // Non-static field. The fieldID is really the offset of the field within the instanceOop. + int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID); + found = instanceKlass::cast(k)->find_field_from_offset(offset, false, &fd); + } + assert(found, "bad fieldID passed into jni_ToReflectedField"); + oop reflected = Reflection::new_field(&fd, UseNewReflection, CHECK_NULL); + ret = JNIHandles::make_local(env, reflected); + return ret; +JNI_END + + +// +// Accessing Static Fields +// +DT_RETURN_MARK_DECL(GetStaticFieldID, jfieldID); + +JNI_ENTRY(jfieldID, jni_GetStaticFieldID(JNIEnv *env, jclass clazz, + const char *name, const char *sig)) + JNIWrapper("GetStaticFieldID"); + DTRACE_PROBE4(hotspot_jni, GetStaticFieldID__entry, env, clazz, name, sig); + jfieldID ret = NULL; + DT_RETURN_MARK(GetStaticFieldID, jfieldID, (const jfieldID&)ret); + + // The class should have been loaded (we have an instance of the class + // passed in) so the field and signature should already be in the symbol + // table. If they're not there, the field doesn't exist. + symbolHandle fieldname = + symbolHandle(THREAD, SymbolTable::probe(name, (int)strlen(name))); + symbolHandle signame = + symbolHandle(THREAD, SymbolTable::probe(sig, (int)strlen(sig))); + if (fieldname.is_null() || signame.is_null()) { + THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), (char*) name); + } + KlassHandle k(THREAD, + java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz))); + // Make sure class is initialized before handing id's out to static fields + Klass::cast(k())->initialize(CHECK_NULL); + + fieldDescriptor fd; + if (!Klass::cast(k())->oop_is_instance() || + !instanceKlass::cast(k())->find_field(fieldname(), signame(), true, &fd)) { + THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), (char*) name); + } + + // A jfieldID for a static field is a JNIid specifying the field holder and the offset within the klassOop + JNIid* id = instanceKlass::cast(fd.field_holder())->jni_id_for(fd.offset()); + debug_only(id->set_is_static_field_id();) + + debug_only(int first_offset = instanceKlass::cast(fd.field_holder())->offset_of_static_fields();) + debug_only(int end_offset = first_offset + (instanceKlass::cast(fd.field_holder())->static_field_size() * wordSize);) + assert(id->offset() >= first_offset && id->offset() < end_offset, "invalid static field offset"); + + ret = jfieldIDWorkaround::to_static_jfieldID(id); + return ret; +JNI_END + + +JNI_ENTRY(jobject, jni_GetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID)) + JNIWrapper("GetStaticObjectField"); + DTRACE_PROBE3(hotspot_jni, GetStaticObjectField__entry, env, clazz, fieldID); +#ifndef JNICHECK_KERNEL + DEBUG_ONLY(klassOop param_k = jniCheck::validate_class(thread, clazz);) +#endif // JNICHECK_KERNEL + JNIid* id = jfieldIDWorkaround::from_static_jfieldID(fieldID); + assert(id->is_static_field_id(), "invalid static field id"); + // Keep JVMTI addition small and only check enabled flag here. + // jni_GetField_probe() assumes that is okay to create handles. + if (JvmtiExport::should_post_field_access()) { + JvmtiExport::jni_GetField_probe(thread, NULL, NULL, id->holder(), fieldID, true); + } + jobject ret = JNIHandles::make_local(id->holder()->obj_field(id->offset())); + DTRACE_PROBE1(hotspot_jni, GetStaticObjectField__return, ret); + return ret; +JNI_END + +#define DEFINE_GETSTATICFIELD(Return,Fieldname,Result) \ +\ + DT_RETURN_MARK_DECL_FOR(Result, GetStatic##Result##Field, Return);\ +\ +JNI_ENTRY(Return, jni_GetStatic##Result##Field(JNIEnv *env, jclass clazz, jfieldID fieldID)) \ + JNIWrapper("GetStatic" XSTR(Result) "Field"); \ + DTRACE_PROBE3(hotspot_jni, GetStatic##Result##Field__entry, env, clazz, fieldID);\ + Return ret = 0;\ + DT_RETURN_MARK_FOR(Result, GetStatic##Result##Field, Return, \ + (const Return&)ret);\ + JNIid* id = jfieldIDWorkaround::from_static_jfieldID(fieldID); \ + assert(id->is_static_field_id(), "invalid static field id"); \ + /* Keep JVMTI addition small and only check enabled flag here. */ \ + /* jni_GetField_probe() assumes that is okay to create handles. */ \ + if (JvmtiExport::should_post_field_access()) { \ + JvmtiExport::jni_GetField_probe(thread, NULL, NULL, id->holder(), fieldID, true); \ + } \ + ret = id->holder()-> Fieldname##_field (id->offset()); \ + return ret;\ +JNI_END + +DEFINE_GETSTATICFIELD(jboolean, bool, Boolean) +DEFINE_GETSTATICFIELD(jbyte, byte, Byte) +DEFINE_GETSTATICFIELD(jchar, char, Char) +DEFINE_GETSTATICFIELD(jshort, short, Short) +DEFINE_GETSTATICFIELD(jint, int, Int) +DEFINE_GETSTATICFIELD(jlong, long, Long) +DEFINE_GETSTATICFIELD(jfloat, float, Float) +DEFINE_GETSTATICFIELD(jdouble, double, Double) + + +JNI_ENTRY(void, jni_SetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value)) + JNIWrapper("SetStaticObjectField"); + DTRACE_PROBE4(hotspot_jni, SetStaticObjectField__entry, env, clazz, fieldID, value); + JNIid* id = jfieldIDWorkaround::from_static_jfieldID(fieldID); + assert(id->is_static_field_id(), "invalid static field id"); + // Keep JVMTI addition small and only check enabled flag here. + // jni_SetField_probe() assumes that is okay to create handles. + if (JvmtiExport::should_post_field_modification()) { + jvalue field_value; + field_value.l = value; + JvmtiExport::jni_SetField_probe(thread, NULL, NULL, id->holder(), fieldID, true, 'L', (jvalue *)&field_value); + } + id->holder()->obj_field_put(id->offset(), JNIHandles::resolve(value)); + DTRACE_PROBE(hotspot_jni, SetStaticObjectField__return); +JNI_END + + +#define DEFINE_SETSTATICFIELD(Argument,Fieldname,Result,SigType,unionType) \ +\ +JNI_ENTRY(void, jni_SetStatic##Result##Field(JNIEnv *env, jclass clazz, jfieldID fieldID, Argument value)) \ + JNIWrapper("SetStatic" XSTR(Result) "Field"); \ + HS_DTRACE_PROBE_CDECL_N(hotspot_jni, SetStatic##Result##Field__entry,\ + ( JNIEnv*, jclass, jfieldID FP_SELECT_##Result(COMMA Argument,/*empty*/) ) ); \ + HS_DTRACE_PROBE_N(hotspot_jni, SetStatic##Result##Field__entry, \ + ( env, clazz, fieldID FP_SELECT_##Result(COMMA value,/*empty*/) ) ); \ +\ + JNIid* id = jfieldIDWorkaround::from_static_jfieldID(fieldID); \ + assert(id->is_static_field_id(), "invalid static field id"); \ + /* Keep JVMTI addition small and only check enabled flag here. */ \ + /* jni_SetField_probe() assumes that is okay to create handles. */ \ + if (JvmtiExport::should_post_field_modification()) { \ + jvalue field_value; \ + field_value.unionType = value; \ + JvmtiExport::jni_SetField_probe(thread, NULL, NULL, id->holder(), fieldID, true, SigType, (jvalue *)&field_value); \ + } \ + id->holder()-> Fieldname##_field_put (id->offset(), value); \ + DTRACE_PROBE(hotspot_jni, SetStatic##Result##Field__return);\ +JNI_END + +DEFINE_SETSTATICFIELD(jboolean, bool, Boolean, 'Z', z) +DEFINE_SETSTATICFIELD(jbyte, byte, Byte, 'B', b) +DEFINE_SETSTATICFIELD(jchar, char, Char, 'C', c) +DEFINE_SETSTATICFIELD(jshort, short, Short, 'S', s) +DEFINE_SETSTATICFIELD(jint, int, Int, 'I', i) +DEFINE_SETSTATICFIELD(jlong, long, Long, 'J', j) +DEFINE_SETSTATICFIELD(jfloat, float, Float, 'F', f) +DEFINE_SETSTATICFIELD(jdouble, double, Double, 'D', d) + + +// +// String Operations +// + +// Unicode Interface + +DT_RETURN_MARK_DECL(NewString, jstring); + +JNI_ENTRY(jstring, jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len)) + JNIWrapper("NewString"); + DTRACE_PROBE3(hotspot_jni, NewString__entry, env, unicodeChars, len); + jstring ret = NULL; + DT_RETURN_MARK(NewString, jstring, (const jstring&)ret); + oop string=java_lang_String::create_oop_from_unicode((jchar*) unicodeChars, len, CHECK_NULL); + ret = (jstring) JNIHandles::make_local(env, string); + return ret; +JNI_END + + +JNI_QUICK_ENTRY(jsize, jni_GetStringLength(JNIEnv *env, jstring string)) + JNIWrapper("GetStringLength"); + DTRACE_PROBE2(hotspot_jni, GetStringLength__entry, env, string); + jsize ret = java_lang_String::length(JNIHandles::resolve_non_null(string)); + DTRACE_PROBE1(hotspot_jni, GetStringLength__return, ret); + return ret; +JNI_END + + +JNI_QUICK_ENTRY(const jchar*, jni_GetStringChars( + JNIEnv *env, jstring string, jboolean *isCopy)) + JNIWrapper("GetStringChars"); + DTRACE_PROBE3(hotspot_jni, GetStringChars__entry, env, string, isCopy); + //%note jni_5 + if (isCopy != NULL) { + *isCopy = JNI_TRUE; + } + oop s = JNIHandles::resolve_non_null(string); + int s_len = java_lang_String::length(s); + typeArrayOop s_value = java_lang_String::value(s); + int s_offset = java_lang_String::offset(s); + jchar* buf = NEW_C_HEAP_ARRAY(jchar, s_len + 1); // add one for zero termination + if (s_len > 0) { + memcpy(buf, s_value->char_at_addr(s_offset), sizeof(jchar)*s_len); + } + buf[s_len] = 0; + DTRACE_PROBE1(hotspot_jni, GetStringChars__return, buf); + return buf; +JNI_END + + +JNI_QUICK_ENTRY(void, jni_ReleaseStringChars(JNIEnv *env, jstring str, const jchar *chars)) + JNIWrapper("ReleaseStringChars"); + DTRACE_PROBE3(hotspot_jni, ReleaseStringChars__entry, env, str, chars); + //%note jni_6 + if (chars != NULL) { + // Since String objects are supposed to be immutable, don't copy any + // new data back. A bad user will have to go after the char array. + FreeHeap((void*) chars); + } + DTRACE_PROBE(hotspot_jni, ReleaseStringChars__return); +JNI_END + + +// UTF Interface + +DT_RETURN_MARK_DECL(NewStringUTF, jstring); + +JNI_ENTRY(jstring, jni_NewStringUTF(JNIEnv *env, const char *bytes)) + JNIWrapper("NewStringUTF"); + DTRACE_PROBE2(hotspot_jni, NewStringUTF__entry, env, bytes); + jstring ret; + DT_RETURN_MARK(NewStringUTF, jstring, (const jstring&)ret); + + oop result = java_lang_String::create_oop_from_str((char*) bytes, CHECK_NULL); + ret = (jstring) JNIHandles::make_local(env, result); + return ret; +JNI_END + + +JNI_ENTRY(jsize, jni_GetStringUTFLength(JNIEnv *env, jstring string)) + JNIWrapper("GetStringUTFLength"); + DTRACE_PROBE2(hotspot_jni, GetStringUTFLength__entry, env, string); + jsize ret = java_lang_String::utf8_length(JNIHandles::resolve_non_null(string)); + DTRACE_PROBE1(hotspot_jni, GetStringUTFLength__return, ret); + return ret; +JNI_END + + +JNI_ENTRY(const char*, jni_GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy)) + JNIWrapper("GetStringUTFChars"); + DTRACE_PROBE3(hotspot_jni, GetStringUTFChars__entry, env, string, isCopy); + ResourceMark rm; + char* str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(string)); + int length = (int)strlen(str); + char* result = AllocateHeap(length+1, "GetStringUTFChars"); + strcpy(result, str); + if (isCopy != NULL) *isCopy = JNI_TRUE; + DTRACE_PROBE1(hotspot_jni, GetStringUTFChars__return, result); + return result; +JNI_END + + +JNI_LEAF(void, jni_ReleaseStringUTFChars(JNIEnv *env, jstring str, const char *chars)) + JNIWrapper("ReleaseStringUTFChars"); + DTRACE_PROBE3(hotspot_jni, ReleaseStringUTFChars__entry, env, str, chars); + if (chars != NULL) { + FreeHeap((char*) chars); + } + DTRACE_PROBE(hotspot_jni, ReleaseStringUTFChars__return); +JNI_END + + +JNI_QUICK_ENTRY(jsize, jni_GetArrayLength(JNIEnv *env, jarray array)) + JNIWrapper("GetArrayLength"); + DTRACE_PROBE2(hotspot_jni, GetArrayLength__entry, env, array); + arrayOop a = arrayOop(JNIHandles::resolve_non_null(array)); + assert(a->is_array(), "must be array"); + jsize ret = a->length(); + DTRACE_PROBE1(hotspot_jni, GetArrayLength__return, ret); + return ret; +JNI_END + + +// +// Object Array Operations +// + +DT_RETURN_MARK_DECL(NewObjectArray, jobjectArray); + +JNI_ENTRY(jobjectArray, jni_NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement)) + JNIWrapper("NewObjectArray"); + DTRACE_PROBE4(hotspot_jni, NewObjectArray__entry, env, length, elementClass, initialElement); + jobjectArray ret = NULL; + DT_RETURN_MARK(NewObjectArray, jobjectArray, (const jobjectArray&)ret); + KlassHandle ek(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(elementClass))); + klassOop ako = Klass::cast(ek())->array_klass(CHECK_NULL); + KlassHandle ak = KlassHandle(THREAD, ako); + objArrayKlass::cast(ak())->initialize(CHECK_NULL); + objArrayOop result = objArrayKlass::cast(ak())->allocate(length, CHECK_NULL); + oop initial_value = JNIHandles::resolve(initialElement); + if (initial_value != NULL) { // array already initialized with NULL + for (int index = 0; index < length; index++) { + result->obj_at_put(index, initial_value); + } + } + ret = (jobjectArray) JNIHandles::make_local(env, result); + return ret; +JNI_END + +DT_RETURN_MARK_DECL(GetObjectArrayElement, jobject); + +JNI_ENTRY(jobject, jni_GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index)) + JNIWrapper("GetObjectArrayElement"); + DTRACE_PROBE3(hotspot_jni, GetObjectArrayElement__entry, env, array, index); + jobject ret = NULL; + DT_RETURN_MARK(GetObjectArrayElement, jobject, (const jobject&)ret); + objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array)); + if (a->is_within_bounds(index)) { + jobject ret = JNIHandles::make_local(env, a->obj_at(index)); + return ret; + } else { + char buf[jintAsStringSize]; + sprintf(buf, "%d", index); + THROW_MSG_0(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), buf); + } +JNI_END + +DT_VOID_RETURN_MARK_DECL(SetObjectArrayElement); + +JNI_ENTRY(void, jni_SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value)) + JNIWrapper("SetObjectArrayElement"); + DTRACE_PROBE4(hotspot_jni, SetObjectArrayElement__entry, env, array, index, value); + DT_VOID_RETURN_MARK(SetObjectArrayElement); + + objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array)); + oop v = JNIHandles::resolve(value); + if (a->is_within_bounds(index)) { + if (v == NULL || v->is_a(objArrayKlass::cast(a->klass())->element_klass())) { + a->obj_at_put(index, v); + } else { + THROW(vmSymbols::java_lang_ArrayStoreException()); + } + } else { + char buf[jintAsStringSize]; + sprintf(buf, "%d", index); + THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), buf); + } +JNI_END + + +#define DEFINE_NEWSCALARARRAY(Return,Allocator,Result) \ +\ + DT_RETURN_MARK_DECL_FOR(Result, New##Result##Array, Return);\ +\ +JNI_ENTRY(Return, \ + jni_New##Result##Array(JNIEnv *env, jsize len)) \ + JNIWrapper("New" XSTR(Result) "Array"); \ + DTRACE_PROBE2(hotspot_jni, New##Result##Array__entry, env, len);\ + Return ret = NULL;\ + DT_RETURN_MARK_FOR(Result, New##Result##Array, Return, (const Return&)ret);\ +\ + oop obj= oopFactory::Allocator(len, CHECK_0); \ + ret = (Return) JNIHandles::make_local(env, obj); \ + return ret;\ +JNI_END + +DEFINE_NEWSCALARARRAY(jbooleanArray, new_boolArray, Boolean) +DEFINE_NEWSCALARARRAY(jbyteArray, new_byteArray, Byte) +DEFINE_NEWSCALARARRAY(jshortArray, new_shortArray, Short) +DEFINE_NEWSCALARARRAY(jcharArray, new_charArray, Char) +DEFINE_NEWSCALARARRAY(jintArray, new_intArray, Int) +DEFINE_NEWSCALARARRAY(jlongArray, new_longArray, Long) +DEFINE_NEWSCALARARRAY(jfloatArray, new_singleArray, Float) +DEFINE_NEWSCALARARRAY(jdoubleArray, new_doubleArray, Double) + + +// Return an address which will fault if the caller writes to it. + +static char* get_bad_address() { + static char* bad_address = NULL; + if (bad_address == NULL) { + size_t size = os::vm_allocation_granularity(); + bad_address = os::reserve_memory(size); + if (bad_address != NULL) { + os::commit_memory(bad_address, size); + os::protect_memory(bad_address, size); + } + } + return bad_address; +} + + +#define DEFINE_GETSCALARARRAYELEMENTS(ElementTag,ElementType,Result, Tag) \ +\ +JNI_QUICK_ENTRY(ElementType*, \ + jni_Get##Result##ArrayElements(JNIEnv *env, ElementType##Array array, jboolean *isCopy)) \ + JNIWrapper("Get" XSTR(Result) "ArrayElements"); \ + DTRACE_PROBE3(hotspot_jni, Get##Result##ArrayElements__entry, env, array, isCopy);\ + /* allocate an chunk of memory in c land */ \ + typeArrayOop a = typeArrayOop(JNIHandles::resolve_non_null(array)); \ + ElementType* result; \ + int len = a->length(); \ + if (len == 0) { \ + /* Empty array: legal but useless, can't return NULL. \ + * Return a pointer to something useless. \ + * Avoid asserts in typeArrayOop. */ \ + result = (ElementType*)get_bad_address(); \ + } else { \ + result = NEW_C_HEAP_ARRAY(ElementType, len); \ + /* copy the array to the c chunk */ \ + memcpy(result, a->Tag##_at_addr(0), sizeof(ElementType)*len); \ + } \ + if (isCopy) *isCopy = JNI_TRUE; \ + DTRACE_PROBE1(hotspot_jni, Get##Result##ArrayElements__return, result);\ + return result; \ +JNI_END + +DEFINE_GETSCALARARRAYELEMENTS(T_BOOLEAN, jboolean, Boolean, bool) +DEFINE_GETSCALARARRAYELEMENTS(T_BYTE, jbyte, Byte, byte) +DEFINE_GETSCALARARRAYELEMENTS(T_SHORT, jshort, Short, short) +DEFINE_GETSCALARARRAYELEMENTS(T_CHAR, jchar, Char, char) +DEFINE_GETSCALARARRAYELEMENTS(T_INT, jint, Int, int) +DEFINE_GETSCALARARRAYELEMENTS(T_LONG, jlong, Long, long) +DEFINE_GETSCALARARRAYELEMENTS(T_FLOAT, jfloat, Float, float) +DEFINE_GETSCALARARRAYELEMENTS(T_DOUBLE, jdouble, Double, double) + + +#define DEFINE_RELEASESCALARARRAYELEMENTS(ElementTag,ElementType,Result,Tag) \ +\ +JNI_QUICK_ENTRY(void, \ + jni_Release##Result##ArrayElements(JNIEnv *env, ElementType##Array array, \ + ElementType *buf, jint mode)) \ + JNIWrapper("Release" XSTR(Result) "ArrayElements"); \ + DTRACE_PROBE4(hotspot_jni, Release##Result##ArrayElements__entry, env, array, buf, mode);\ + typeArrayOop a = typeArrayOop(JNIHandles::resolve_non_null(array)); \ + int len = a->length(); \ + if (len != 0) { /* Empty array: nothing to free or copy. */ \ + if ((mode == 0) || (mode == JNI_COMMIT)) { \ + memcpy(a->Tag##_at_addr(0), buf, sizeof(ElementType)*len); \ + } \ + if ((mode == 0) || (mode == JNI_ABORT)) { \ + FreeHeap(buf); \ + } \ + } \ + DTRACE_PROBE(hotspot_jni, Release##Result##ArrayElements__return);\ +JNI_END + +DEFINE_RELEASESCALARARRAYELEMENTS(T_BOOLEAN, jboolean, Boolean, bool) +DEFINE_RELEASESCALARARRAYELEMENTS(T_BYTE, jbyte, Byte, byte) +DEFINE_RELEASESCALARARRAYELEMENTS(T_SHORT, jshort, Short, short) +DEFINE_RELEASESCALARARRAYELEMENTS(T_CHAR, jchar, Char, char) +DEFINE_RELEASESCALARARRAYELEMENTS(T_INT, jint, Int, int) +DEFINE_RELEASESCALARARRAYELEMENTS(T_LONG, jlong, Long, long) +DEFINE_RELEASESCALARARRAYELEMENTS(T_FLOAT, jfloat, Float, float) +DEFINE_RELEASESCALARARRAYELEMENTS(T_DOUBLE, jdouble, Double, double) + +#define DEFINE_GETSCALARARRAYREGION(ElementTag,ElementType,Result, Tag) \ + DT_VOID_RETURN_MARK_DECL(Get##Result##ArrayRegion);\ +\ +JNI_ENTRY(void, \ +jni_Get##Result##ArrayRegion(JNIEnv *env, ElementType##Array array, jsize start, \ + jsize len, ElementType *buf)) \ + JNIWrapper("Get" XSTR(Result) "ArrayRegion"); \ + DTRACE_PROBE5(hotspot_jni, Get##Result##ArrayRegion__entry, env, array, start, len, buf);\ + DT_VOID_RETURN_MARK(Get##Result##ArrayRegion); \ + typeArrayOop src = typeArrayOop(JNIHandles::resolve_non_null(array)); \ + if (start < 0 || len < 0 || ((unsigned int)start + (unsigned int)len > (unsigned int)src->length())) { \ + THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); \ + } else { \ + if (len > 0) { \ + int sc = typeArrayKlass::cast(src->klass())->log2_element_size(); \ + memcpy((u_char*) buf, \ + (u_char*) src->Tag##_at_addr(start), \ + len << sc); \ + } \ + } \ +JNI_END + +DEFINE_GETSCALARARRAYREGION(T_BOOLEAN, jboolean,Boolean, bool) +DEFINE_GETSCALARARRAYREGION(T_BYTE, jbyte, Byte, byte) +DEFINE_GETSCALARARRAYREGION(T_SHORT, jshort, Short, short) +DEFINE_GETSCALARARRAYREGION(T_CHAR, jchar, Char, char) +DEFINE_GETSCALARARRAYREGION(T_INT, jint, Int, int) +DEFINE_GETSCALARARRAYREGION(T_LONG, jlong, Long, long) +DEFINE_GETSCALARARRAYREGION(T_FLOAT, jfloat, Float, float) +DEFINE_GETSCALARARRAYREGION(T_DOUBLE, jdouble, Double, double) + +#define DEFINE_SETSCALARARRAYREGION(ElementTag,ElementType,Result, Tag) \ + DT_VOID_RETURN_MARK_DECL(Set##Result##ArrayRegion);\ +\ +JNI_ENTRY(void, \ +jni_Set##Result##ArrayRegion(JNIEnv *env, ElementType##Array array, jsize start, \ + jsize len, const ElementType *buf)) \ + JNIWrapper("Set" XSTR(Result) "ArrayRegion"); \ + DTRACE_PROBE5(hotspot_jni, Set##Result##ArrayRegion__entry, env, array, start, len, buf);\ + DT_VOID_RETURN_MARK(Set##Result##ArrayRegion); \ + typeArrayOop dst = typeArrayOop(JNIHandles::resolve_non_null(array)); \ + if (start < 0 || len < 0 || ((unsigned int)start + (unsigned int)len > (unsigned int)dst->length())) { \ + THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); \ + } else { \ + if (len > 0) { \ + int sc = typeArrayKlass::cast(dst->klass())->log2_element_size(); \ + memcpy((u_char*) dst->Tag##_at_addr(start), \ + (u_char*) buf, \ + len << sc); \ + } \ + } \ +JNI_END + +DEFINE_SETSCALARARRAYREGION(T_BOOLEAN, jboolean, Boolean, bool) +DEFINE_SETSCALARARRAYREGION(T_BYTE, jbyte, Byte, byte) +DEFINE_SETSCALARARRAYREGION(T_SHORT, jshort, Short, short) +DEFINE_SETSCALARARRAYREGION(T_CHAR, jchar, Char, char) +DEFINE_SETSCALARARRAYREGION(T_INT, jint, Int, int) +DEFINE_SETSCALARARRAYREGION(T_LONG, jlong, Long, long) +DEFINE_SETSCALARARRAYREGION(T_FLOAT, jfloat, Float, float) +DEFINE_SETSCALARARRAYREGION(T_DOUBLE, jdouble, Double, double) + + +// +// Interception of natives +// + +// The RegisterNatives call being attempted tried to register with a method that +// is not native. Ask JVM TI what prefixes have been specified. Then check +// to see if the native method is now wrapped with the prefixes. See the +// SetNativeMethodPrefix(es) functions in the JVM TI Spec for details. +static methodOop find_prefixed_native(KlassHandle k, + symbolHandle name, symbolHandle signature, TRAPS) { + ResourceMark rm(THREAD); + methodOop method; + int name_len = name->utf8_length(); + char* name_str = name->as_utf8(); + int prefix_count; + char** prefixes = JvmtiExport::get_all_native_method_prefixes(&prefix_count); + for (int i = 0; i < prefix_count; i++) { + char* prefix = prefixes[i]; + int prefix_len = (int)strlen(prefix); + + // try adding this prefix to the method name and see if it matches another method name + int trial_len = name_len + prefix_len; + char* trial_name_str = NEW_RESOURCE_ARRAY(char, trial_len + 1); + strcpy(trial_name_str, prefix); + strcat(trial_name_str, name_str); + symbolHandle trial_name(THREAD, SymbolTable::probe(trial_name_str, trial_len)); + if (trial_name.is_null()) { + continue; // no such symbol, so this prefix wasn't used, try the next prefix + } + method = Klass::cast(k())->lookup_method(trial_name(), signature()); + if (method == NULL) { + continue; // signature doesn't match, try the next prefix + } + if (method->is_native()) { + method->set_is_prefixed_native(); + return method; // wahoo, we found a prefixed version of the method, return it + } + // found as non-native, so prefix is good, add it, probably just need more prefixes + name_len = trial_len; + name_str = trial_name_str; + } + return NULL; // not found +} + +static bool register_native(KlassHandle k, symbolHandle name, symbolHandle signature, address entry, TRAPS) { + methodOop method = Klass::cast(k())->lookup_method(name(), signature()); + if (method == NULL) { + ResourceMark rm; + stringStream st; + st.print("Method %s name or signature does not match", + methodOopDesc::name_and_sig_as_C_string(Klass::cast(k()), name(), signature())); + THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false); + } + if (!method->is_native()) { + // trying to register to a non-native method, see if a JVM TI agent has added prefix(es) + method = find_prefixed_native(k, name, signature, THREAD); + if (method == NULL) { + ResourceMark rm; + stringStream st; + st.print("Method %s is not declared as native", + methodOopDesc::name_and_sig_as_C_string(Klass::cast(k()), name(), signature())); + THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false); + } + } + + if (entry != NULL) { + method->set_native_function(entry, + methodOopDesc::native_bind_event_is_interesting); + } else { + method->clear_native_function(); + } + if (PrintJNIResolving) { + ResourceMark rm(THREAD); + tty->print_cr("[Registering JNI native method %s.%s]", + Klass::cast(method->method_holder())->external_name(), + method->name()->as_C_string()); + } + return true; +} + +DT_RETURN_MARK_DECL(RegisterNatives, jint); + +JNI_ENTRY(jint, jni_RegisterNatives(JNIEnv *env, jclass clazz, + const JNINativeMethod *methods, + jint nMethods)) + JNIWrapper("RegisterNatives"); + DTRACE_PROBE4(hotspot_jni, RegisterNatives__entry, env, clazz, methods, nMethods); + jint ret = 0; + DT_RETURN_MARK(RegisterNatives, jint, (const jint&)ret); + + KlassHandle h_k(thread, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz))); + + for (int index = 0; index < nMethods; index++) { + const char* meth_name = methods[index].name; + const char* meth_sig = methods[index].signature; + int meth_name_len = (int)strlen(meth_name); + + // The class should have been loaded (we have an instance of the class + // passed in) so the method and signature should already be in the symbol + // table. If they're not there, the method doesn't exist. + symbolHandle name(THREAD, SymbolTable::probe(meth_name, meth_name_len)); + symbolHandle signature(THREAD, SymbolTable::probe(meth_sig, (int)strlen(meth_sig))); + + if (name.is_null() || signature.is_null()) { + ResourceMark rm; + stringStream st; + st.print("Method %s.%s%s not found", Klass::cast(h_k())->external_name(), meth_name, meth_sig); + // Must return negative value on failure + THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), -1); + } + + bool res = register_native(h_k, name, signature, + (address) methods[index].fnPtr, THREAD); + if (!res) { + ret = -1; + break; + } + } + return ret; +JNI_END + + +JNI_ENTRY(jint, jni_UnregisterNatives(JNIEnv *env, jclass clazz)) + JNIWrapper("UnregisterNatives"); + DTRACE_PROBE2(hotspot_jni, UnregisterNatives__entry, env, clazz); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz)); + //%note jni_2 + if (Klass::cast(k)->oop_is_instance()) { + for (int index = 0; index < instanceKlass::cast(k)->methods()->length(); index++) { + methodOop m = methodOop(instanceKlass::cast(k)->methods()->obj_at(index)); + if (m->is_native()) { + m->clear_native_function(); + m->set_signature_handler(NULL); + } + } + } + DTRACE_PROBE1(hotspot_jni, UnregisterNatives__return, 0); + return 0; +JNI_END + +// +// Monitor functions +// + +DT_RETURN_MARK_DECL(MonitorEnter, jint); + +JNI_ENTRY(jint, jni_MonitorEnter(JNIEnv *env, jobject jobj)) + DTRACE_PROBE2(hotspot_jni, MonitorEnter__entry, env, jobj); + jint ret = JNI_ERR; + DT_RETURN_MARK(MonitorEnter, jint, (const jint&)ret); + + // If the object is null, we can't do anything with it + if (jobj == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), JNI_ERR); + } + + Handle obj(thread, JNIHandles::resolve_non_null(jobj)); + ObjectSynchronizer::jni_enter(obj, CHECK_(JNI_ERR)); + ret = JNI_OK; + return ret; +JNI_END + +DT_RETURN_MARK_DECL(MonitorExit, jint); + +JNI_ENTRY(jint, jni_MonitorExit(JNIEnv *env, jobject jobj)) + DTRACE_PROBE2(hotspot_jni, MonitorExit__entry, env, jobj); + jint ret = JNI_ERR; + DT_RETURN_MARK(MonitorExit, jint, (const jint&)ret); + + // Don't do anything with a null object + if (jobj == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), JNI_ERR); + } + + Handle obj(THREAD, JNIHandles::resolve_non_null(jobj)); + ObjectSynchronizer::jni_exit(obj(), CHECK_(JNI_ERR)); + + ret = JNI_OK; + return ret; +JNI_END + +// +// Extensions +// + +DT_VOID_RETURN_MARK_DECL(GetStringRegion); + +JNI_ENTRY(void, jni_GetStringRegion(JNIEnv *env, jstring string, jsize start, jsize len, jchar *buf)) + JNIWrapper("GetStringRegion"); + DTRACE_PROBE5(hotspot_jni, GetStringRegion__entry, env, string, start, len, buf); + DT_VOID_RETURN_MARK(GetStringRegion); + oop s = JNIHandles::resolve_non_null(string); + int s_len = java_lang_String::length(s); + if (start < 0 || len < 0 || start + len > s_len) { + THROW(vmSymbols::java_lang_StringIndexOutOfBoundsException()); + } else { + if (len > 0) { + int s_offset = java_lang_String::offset(s); + typeArrayOop s_value = java_lang_String::value(s); + memcpy(buf, s_value->char_at_addr(s_offset+start), sizeof(jchar)*len); + } + } +JNI_END + +DT_VOID_RETURN_MARK_DECL(GetStringUTFRegion); + +JNI_ENTRY(void, jni_GetStringUTFRegion(JNIEnv *env, jstring string, jsize start, jsize len, char *buf)) + JNIWrapper("GetStringUTFRegion"); + DTRACE_PROBE5(hotspot_jni, GetStringUTFRegion__entry, env, string, start, len, buf); + DT_VOID_RETURN_MARK(GetStringUTFRegion); + oop s = JNIHandles::resolve_non_null(string); + int s_len = java_lang_String::length(s); + if (start < 0 || len < 0 || start + len > s_len) { + THROW(vmSymbols::java_lang_StringIndexOutOfBoundsException()); + } else { + //%note jni_7 + if (len > 0) { + ResourceMark rm(THREAD); + char *utf_region = java_lang_String::as_utf8_string(s, start, len); + int utf_len = (int)strlen(utf_region); + memcpy(buf, utf_region, utf_len); + buf[utf_len] = 0; + } else { + // JDK null-terminates the buffer even in len is zero + if (buf != NULL) { + buf[0] = 0; + } + } + } +JNI_END + + +JNI_ENTRY(void*, jni_GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy)) + JNIWrapper("GetPrimitiveArrayCritical"); + DTRACE_PROBE3(hotspot_jni, GetPrimitiveArrayCritical__entry, env, array, isCopy); + GC_locker::lock_critical(thread); + if (isCopy != NULL) { + *isCopy = JNI_FALSE; + } + oop a = JNIHandles::resolve_non_null(array); + assert(a->is_array(), "just checking"); + BasicType type; + if (a->is_objArray()) { + type = T_OBJECT; + } else { + type = typeArrayKlass::cast(a->klass())->element_type(); + } + void* ret = arrayOop(a)->base(type); + DTRACE_PROBE1(hotspot_jni, GetPrimitiveArrayCritical__return, ret); + return ret; +JNI_END + + +JNI_ENTRY(void, jni_ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode)) + JNIWrapper("ReleasePrimitiveArrayCritical"); + DTRACE_PROBE4(hotspot_jni, ReleasePrimitiveArrayCritical__entry, env, array, carray, mode); + // The array, carray and mode arguments are ignored + GC_locker::unlock_critical(thread); + DTRACE_PROBE(hotspot_jni, ReleasePrimitiveArrayCritical__return); +JNI_END + + +JNI_ENTRY(const jchar*, jni_GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy)) + JNIWrapper("GetStringCritical"); + DTRACE_PROBE3(hotspot_jni, GetStringCritical__entry, env, string, isCopy); + GC_locker::lock_critical(thread); + if (isCopy != NULL) { + *isCopy = JNI_FALSE; + } + oop s = JNIHandles::resolve_non_null(string); + int s_len = java_lang_String::length(s); + typeArrayOop s_value = java_lang_String::value(s); + int s_offset = java_lang_String::offset(s); + const jchar* ret; + if (s_len > 0) { + ret = s_value->char_at_addr(s_offset); + } else { + ret = (jchar*) s_value->base(T_CHAR); + } + DTRACE_PROBE1(hotspot_jni, GetStringCritical__return, ret); + return ret; +JNI_END + + +JNI_ENTRY(void, jni_ReleaseStringCritical(JNIEnv *env, jstring str, const jchar *chars)) + JNIWrapper("ReleaseStringCritical"); + DTRACE_PROBE3(hotspot_jni, ReleaseStringCritical__entry, env, str, chars); + // The str and chars arguments are ignored + GC_locker::unlock_critical(thread); + DTRACE_PROBE(hotspot_jni, ReleaseStringCritical__return); +JNI_END + + +JNI_ENTRY(jweak, jni_NewWeakGlobalRef(JNIEnv *env, jobject ref)) + JNIWrapper("jni_NewWeakGlobalRef"); + DTRACE_PROBE2(hotspot_jni, NewWeakGlobalRef__entry, env, ref); + Handle ref_handle(thread, JNIHandles::resolve(ref)); + jweak ret = JNIHandles::make_weak_global(ref_handle); + DTRACE_PROBE1(hotspot_jni, NewWeakGlobalRef__return, ret); + return ret; +JNI_END + +// Must be JNI_ENTRY (with HandleMark) +JNI_ENTRY(void, jni_DeleteWeakGlobalRef(JNIEnv *env, jweak ref)) + JNIWrapper("jni_DeleteWeakGlobalRef"); + DTRACE_PROBE2(hotspot_jni, DeleteWeakGlobalRef__entry, env, ref); + JNIHandles::destroy_weak_global(ref); + DTRACE_PROBE(hotspot_jni, DeleteWeakGlobalRef__return); +JNI_END + + +JNI_QUICK_ENTRY(jboolean, jni_ExceptionCheck(JNIEnv *env)) + JNIWrapper("jni_ExceptionCheck"); + DTRACE_PROBE1(hotspot_jni, ExceptionCheck__entry, env); + jni_check_async_exceptions(thread); + jboolean ret = (thread->has_pending_exception()) ? JNI_TRUE : JNI_FALSE; + DTRACE_PROBE1(hotspot_jni, ExceptionCheck__return, ret); + return ret; +JNI_END + + +// Initialization state for three routines below relating to +// java.nio.DirectBuffers +static jint directBufferSupportInitializeStarted = 0; +static volatile jint directBufferSupportInitializeEnded = 0; +static volatile jint directBufferSupportInitializeFailed = 0; +static jclass bufferClass = NULL; +static jclass directBufferClass = NULL; +static jclass directByteBufferClass = NULL; +static jmethodID directByteBufferConstructor = NULL; +static jfieldID directBufferAddressField = NULL; +static jfieldID bufferCapacityField = NULL; + +static jclass lookupOne(JNIEnv* env, const char* name, TRAPS) { + Handle loader; // null (bootstrap) loader + Handle protection_domain; // null protection domain + + symbolHandle sym = oopFactory::new_symbol_handle(name, CHECK_NULL); + return find_class_from_class_loader(env, sym, true, loader, protection_domain, true, CHECK_NULL); +} + +// These lookups are done with the NULL (bootstrap) ClassLoader to +// circumvent any security checks that would be done by jni_FindClass. +JNI_ENTRY(bool, lookupDirectBufferClasses(JNIEnv* env)) +{ + if ((bufferClass = lookupOne(env, "java/nio/Buffer", thread)) == NULL) { return false; } + if ((directBufferClass = lookupOne(env, "sun/nio/ch/DirectBuffer", thread)) == NULL) { return false; } + if ((directByteBufferClass = lookupOne(env, "java/nio/DirectByteBuffer", thread)) == NULL) { return false; } + return true; +} +JNI_END + + +static bool initializeDirectBufferSupport(JNIEnv* env, JavaThread* thread) { + if (directBufferSupportInitializeFailed) { + return false; + } + + if (Atomic::cmpxchg(1, &directBufferSupportInitializeStarted, 0) == 0) { + if (!lookupDirectBufferClasses(env)) { + directBufferSupportInitializeFailed = 1; + return false; + } + + // Make global references for these + bufferClass = (jclass) env->NewGlobalRef(bufferClass); + directBufferClass = (jclass) env->NewGlobalRef(directBufferClass); + directByteBufferClass = (jclass) env->NewGlobalRef(directByteBufferClass); + + // Get needed field and method IDs + directByteBufferConstructor = env->GetMethodID(directByteBufferClass, "", "(JI)V"); + directBufferAddressField = env->GetFieldID(bufferClass, "address", "J"); + bufferCapacityField = env->GetFieldID(bufferClass, "capacity", "I"); + + if ((directByteBufferConstructor == NULL) || + (directBufferAddressField == NULL) || + (bufferCapacityField == NULL)) { + directBufferSupportInitializeFailed = 1; + return false; + } + + directBufferSupportInitializeEnded = 1; + } else { + ThreadInVMfromNative tivn(thread); // set state as yield_all can call os:sleep + while (!directBufferSupportInitializeEnded && !directBufferSupportInitializeFailed) { + os::yield_all(); + } + } + + return !directBufferSupportInitializeFailed; +} + +extern "C" jobject JNICALL jni_NewDirectByteBuffer(JNIEnv *env, void* address, jlong capacity) +{ + // thread_from_jni_environment() will block if VM is gone. + JavaThread* thread = JavaThread::thread_from_jni_environment(env); + + JNIWrapper("jni_NewDirectByteBuffer"); + DTRACE_PROBE3(hotspot_jni, NewDirectByteBuffer__entry, env, address, capacity); + + if (!directBufferSupportInitializeEnded) { + if (!initializeDirectBufferSupport(env, thread)) { + DTRACE_PROBE1(hotspot_jni, NewDirectByteBuffer__return, NULL); + return NULL; + } + } + + // Being paranoid about accidental sign extension on address + jlong addr = (jlong) ((uintptr_t) address); + // NOTE that package-private DirectByteBuffer constructor currently + // takes int capacity + jint cap = (jint) capacity; + jobject ret = env->NewObject(directByteBufferClass, directByteBufferConstructor, addr, cap); + DTRACE_PROBE1(hotspot_jni, NewDirectByteBuffer__return, ret); + return ret; +} + +DT_RETURN_MARK_DECL(GetDirectBufferAddress, void*); + +extern "C" void* JNICALL jni_GetDirectBufferAddress(JNIEnv *env, jobject buf) +{ + // thread_from_jni_environment() will block if VM is gone. + JavaThread* thread = JavaThread::thread_from_jni_environment(env); + + JNIWrapper("jni_GetDirectBufferAddress"); + DTRACE_PROBE2(hotspot_jni, GetDirectBufferAddress__entry, env, buf); + void* ret = NULL; + DT_RETURN_MARK(GetDirectBufferAddress, void*, (const void*&)ret); + + if (!directBufferSupportInitializeEnded) { + if (!initializeDirectBufferSupport(env, thread)) { + return 0; + } + } + + if ((buf != NULL) && (!env->IsInstanceOf(buf, directBufferClass))) { + return 0; + } + + ret = (void*)(intptr_t)env->GetLongField(buf, directBufferAddressField); + return ret; +} + +DT_RETURN_MARK_DECL(GetDirectBufferCapacity, jlong); + +extern "C" jlong JNICALL jni_GetDirectBufferCapacity(JNIEnv *env, jobject buf) +{ + // thread_from_jni_environment() will block if VM is gone. + JavaThread* thread = JavaThread::thread_from_jni_environment(env); + + JNIWrapper("jni_GetDirectBufferCapacity"); + DTRACE_PROBE2(hotspot_jni, GetDirectBufferCapacity__entry, env, buf); + jlong ret = -1; + DT_RETURN_MARK(GetDirectBufferCapacity, jlong, (const jlong&)ret); + + if (!directBufferSupportInitializeEnded) { + if (!initializeDirectBufferSupport(env, thread)) { + ret = 0; + return ret; + } + } + + if (buf == NULL) { + return -1; + } + + if (!env->IsInstanceOf(buf, directBufferClass)) { + return -1; + } + + // NOTE that capacity is currently an int in the implementation + ret = env->GetIntField(buf, bufferCapacityField); + return ret; +} + + +JNI_LEAF(jint, jni_GetVersion(JNIEnv *env)) + JNIWrapper("GetVersion"); + DTRACE_PROBE1(hotspot_jni, GetVersion__entry, env); + DTRACE_PROBE1(hotspot_jni, GetVersion__return, CurrentVersion); + return CurrentVersion; +JNI_END + +extern struct JavaVM_ main_vm; + +JNI_LEAF(jint, jni_GetJavaVM(JNIEnv *env, JavaVM **vm)) + JNIWrapper("jni_GetJavaVM"); + DTRACE_PROBE2(hotspot_jni, GetJavaVM__entry, env, vm); + *vm = (JavaVM *)(&main_vm); + DTRACE_PROBE1(hotspot_jni, GetJavaVM__return, JNI_OK); + return JNI_OK; +JNI_END + +// Structure containing all jni functions +struct JNINativeInterface_ jni_NativeInterface = { + NULL, + NULL, + NULL, + + NULL, + + jni_GetVersion, + + jni_DefineClass, + jni_FindClass, + + jni_FromReflectedMethod, + jni_FromReflectedField, + + jni_ToReflectedMethod, + + jni_GetSuperclass, + jni_IsAssignableFrom, + + jni_ToReflectedField, + + jni_Throw, + jni_ThrowNew, + jni_ExceptionOccurred, + jni_ExceptionDescribe, + jni_ExceptionClear, + jni_FatalError, + + jni_PushLocalFrame, + jni_PopLocalFrame, + + jni_NewGlobalRef, + jni_DeleteGlobalRef, + jni_DeleteLocalRef, + jni_IsSameObject, + + jni_NewLocalRef, + jni_EnsureLocalCapacity, + + jni_AllocObject, + jni_NewObject, + jni_NewObjectV, + jni_NewObjectA, + + jni_GetObjectClass, + jni_IsInstanceOf, + + jni_GetMethodID, + + jni_CallObjectMethod, + jni_CallObjectMethodV, + jni_CallObjectMethodA, + jni_CallBooleanMethod, + jni_CallBooleanMethodV, + jni_CallBooleanMethodA, + jni_CallByteMethod, + jni_CallByteMethodV, + jni_CallByteMethodA, + jni_CallCharMethod, + jni_CallCharMethodV, + jni_CallCharMethodA, + jni_CallShortMethod, + jni_CallShortMethodV, + jni_CallShortMethodA, + jni_CallIntMethod, + jni_CallIntMethodV, + jni_CallIntMethodA, + jni_CallLongMethod, + jni_CallLongMethodV, + jni_CallLongMethodA, + jni_CallFloatMethod, + jni_CallFloatMethodV, + jni_CallFloatMethodA, + jni_CallDoubleMethod, + jni_CallDoubleMethodV, + jni_CallDoubleMethodA, + jni_CallVoidMethod, + jni_CallVoidMethodV, + jni_CallVoidMethodA, + + jni_CallNonvirtualObjectMethod, + jni_CallNonvirtualObjectMethodV, + jni_CallNonvirtualObjectMethodA, + jni_CallNonvirtualBooleanMethod, + jni_CallNonvirtualBooleanMethodV, + jni_CallNonvirtualBooleanMethodA, + jni_CallNonvirtualByteMethod, + jni_CallNonvirtualByteMethodV, + jni_CallNonvirtualByteMethodA, + jni_CallNonvirtualCharMethod, + jni_CallNonvirtualCharMethodV, + jni_CallNonvirtualCharMethodA, + jni_CallNonvirtualShortMethod, + jni_CallNonvirtualShortMethodV, + jni_CallNonvirtualShortMethodA, + jni_CallNonvirtualIntMethod, + jni_CallNonvirtualIntMethodV, + jni_CallNonvirtualIntMethodA, + jni_CallNonvirtualLongMethod, + jni_CallNonvirtualLongMethodV, + jni_CallNonvirtualLongMethodA, + jni_CallNonvirtualFloatMethod, + jni_CallNonvirtualFloatMethodV, + jni_CallNonvirtualFloatMethodA, + jni_CallNonvirtualDoubleMethod, + jni_CallNonvirtualDoubleMethodV, + jni_CallNonvirtualDoubleMethodA, + jni_CallNonvirtualVoidMethod, + jni_CallNonvirtualVoidMethodV, + jni_CallNonvirtualVoidMethodA, + + jni_GetFieldID, + + jni_GetObjectField, + jni_GetBooleanField, + jni_GetByteField, + jni_GetCharField, + jni_GetShortField, + jni_GetIntField, + jni_GetLongField, + jni_GetFloatField, + jni_GetDoubleField, + + jni_SetObjectField, + jni_SetBooleanField, + jni_SetByteField, + jni_SetCharField, + jni_SetShortField, + jni_SetIntField, + jni_SetLongField, + jni_SetFloatField, + jni_SetDoubleField, + + jni_GetStaticMethodID, + + jni_CallStaticObjectMethod, + jni_CallStaticObjectMethodV, + jni_CallStaticObjectMethodA, + jni_CallStaticBooleanMethod, + jni_CallStaticBooleanMethodV, + jni_CallStaticBooleanMethodA, + jni_CallStaticByteMethod, + jni_CallStaticByteMethodV, + jni_CallStaticByteMethodA, + jni_CallStaticCharMethod, + jni_CallStaticCharMethodV, + jni_CallStaticCharMethodA, + jni_CallStaticShortMethod, + jni_CallStaticShortMethodV, + jni_CallStaticShortMethodA, + jni_CallStaticIntMethod, + jni_CallStaticIntMethodV, + jni_CallStaticIntMethodA, + jni_CallStaticLongMethod, + jni_CallStaticLongMethodV, + jni_CallStaticLongMethodA, + jni_CallStaticFloatMethod, + jni_CallStaticFloatMethodV, + jni_CallStaticFloatMethodA, + jni_CallStaticDoubleMethod, + jni_CallStaticDoubleMethodV, + jni_CallStaticDoubleMethodA, + jni_CallStaticVoidMethod, + jni_CallStaticVoidMethodV, + jni_CallStaticVoidMethodA, + + jni_GetStaticFieldID, + + jni_GetStaticObjectField, + jni_GetStaticBooleanField, + jni_GetStaticByteField, + jni_GetStaticCharField, + jni_GetStaticShortField, + jni_GetStaticIntField, + jni_GetStaticLongField, + jni_GetStaticFloatField, + jni_GetStaticDoubleField, + + jni_SetStaticObjectField, + jni_SetStaticBooleanField, + jni_SetStaticByteField, + jni_SetStaticCharField, + jni_SetStaticShortField, + jni_SetStaticIntField, + jni_SetStaticLongField, + jni_SetStaticFloatField, + jni_SetStaticDoubleField, + + jni_NewString, + jni_GetStringLength, + jni_GetStringChars, + jni_ReleaseStringChars, + + jni_NewStringUTF, + jni_GetStringUTFLength, + jni_GetStringUTFChars, + jni_ReleaseStringUTFChars, + + jni_GetArrayLength, + + jni_NewObjectArray, + jni_GetObjectArrayElement, + jni_SetObjectArrayElement, + + jni_NewBooleanArray, + jni_NewByteArray, + jni_NewCharArray, + jni_NewShortArray, + jni_NewIntArray, + jni_NewLongArray, + jni_NewFloatArray, + jni_NewDoubleArray, + + jni_GetBooleanArrayElements, + jni_GetByteArrayElements, + jni_GetCharArrayElements, + jni_GetShortArrayElements, + jni_GetIntArrayElements, + jni_GetLongArrayElements, + jni_GetFloatArrayElements, + jni_GetDoubleArrayElements, + + jni_ReleaseBooleanArrayElements, + jni_ReleaseByteArrayElements, + jni_ReleaseCharArrayElements, + jni_ReleaseShortArrayElements, + jni_ReleaseIntArrayElements, + jni_ReleaseLongArrayElements, + jni_ReleaseFloatArrayElements, + jni_ReleaseDoubleArrayElements, + + jni_GetBooleanArrayRegion, + jni_GetByteArrayRegion, + jni_GetCharArrayRegion, + jni_GetShortArrayRegion, + jni_GetIntArrayRegion, + jni_GetLongArrayRegion, + jni_GetFloatArrayRegion, + jni_GetDoubleArrayRegion, + + jni_SetBooleanArrayRegion, + jni_SetByteArrayRegion, + jni_SetCharArrayRegion, + jni_SetShortArrayRegion, + jni_SetIntArrayRegion, + jni_SetLongArrayRegion, + jni_SetFloatArrayRegion, + jni_SetDoubleArrayRegion, + + jni_RegisterNatives, + jni_UnregisterNatives, + + jni_MonitorEnter, + jni_MonitorExit, + + jni_GetJavaVM, + + jni_GetStringRegion, + jni_GetStringUTFRegion, + + jni_GetPrimitiveArrayCritical, + jni_ReleasePrimitiveArrayCritical, + + jni_GetStringCritical, + jni_ReleaseStringCritical, + + jni_NewWeakGlobalRef, + jni_DeleteWeakGlobalRef, + + jni_ExceptionCheck, + + jni_NewDirectByteBuffer, + jni_GetDirectBufferAddress, + jni_GetDirectBufferCapacity, + + // New 1_6 features + + jni_GetObjectRefType +}; + + +// For jvmti use to modify jni function table. +// Java threads in native contiues to run until it is transitioned +// to VM at safepoint. Before the transition or before it is blocked +// for safepoint it may access jni function table. VM could crash if +// any java thread access the jni function table in the middle of memcpy. +// To avoid this each function pointers are copied automically. +void copy_jni_function_table(const struct JNINativeInterface_ *new_jni_NativeInterface) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + intptr_t *a = (intptr_t *) jni_functions(); + intptr_t *b = (intptr_t *) new_jni_NativeInterface; + for (uint i=0; i < sizeof(struct JNINativeInterface_)/sizeof(void *); i++) { + Atomic::store_ptr(*b++, a++); + } +} + +void quicken_jni_functions() { + // Replace GetField with fast versions + if (UseFastJNIAccessors && !JvmtiExport::can_post_field_access() + && !VerifyJNIFields && !TraceJNICalls && !CountJNICalls && !CheckJNICalls +#if defined(_WINDOWS) && defined(IA32) && defined(COMPILER2) + // windows x86 currently needs SEH wrapper and the gain of the fast + // versions currently isn't certain for server vm on uniprocessor. + && os::is_MP() +#endif + ) { + address func; + func = JNI_FastGetField::generate_fast_get_boolean_field(); + if (func != (address)-1) { + jni_NativeInterface.GetBooleanField = (GetBooleanField_t)func; + } + func = JNI_FastGetField::generate_fast_get_byte_field(); + if (func != (address)-1) { + jni_NativeInterface.GetByteField = (GetByteField_t)func; + } + func = JNI_FastGetField::generate_fast_get_char_field(); + if (func != (address)-1) { + jni_NativeInterface.GetCharField = (GetCharField_t)func; + } + func = JNI_FastGetField::generate_fast_get_short_field(); + if (func != (address)-1) { + jni_NativeInterface.GetShortField = (GetShortField_t)func; + } + func = JNI_FastGetField::generate_fast_get_int_field(); + if (func != (address)-1) { + jni_NativeInterface.GetIntField = (GetIntField_t)func; + } + func = JNI_FastGetField::generate_fast_get_long_field(); + if (func != (address)-1) { + jni_NativeInterface.GetLongField = (GetLongField_t)func; + } + func = JNI_FastGetField::generate_fast_get_float_field(); + if (func != (address)-1) { + jni_NativeInterface.GetFloatField = (GetFloatField_t)func; + } + func = JNI_FastGetField::generate_fast_get_double_field(); + if (func != (address)-1) { + jni_NativeInterface.GetDoubleField = (GetDoubleField_t)func; + } + } +} + +// Returns the function structure +struct JNINativeInterface_* jni_functions() { +#ifndef JNICHECK_KERNEL + if (CheckJNICalls) return jni_functions_check(); +#else // JNICHECK_KERNEL + if (CheckJNICalls) warning("-Xcheck:jni is not supported in kernel vm."); +#endif // JNICHECK_KERNEL + return &jni_NativeInterface; +} + +// Returns the function structure +struct JNINativeInterface_* jni_functions_nocheck() { + return &jni_NativeInterface; +} + + +// Invocation API + + +// Forward declaration +extern const struct JNIInvokeInterface_ jni_InvokeInterface; + +// Global invocation API vars +volatile jint vm_created = 0; +// Indicate whether it is safe to recreate VM +volatile jint safe_to_recreate_vm = 1; +struct JavaVM_ main_vm = {&jni_InvokeInterface}; + + +#define JAVASTACKSIZE (400 * 1024) /* Default size of a thread java stack */ +#define PROCSTACKSIZE 0 /* 0 means default size in HPI */ +enum { VERIFY_NONE, VERIFY_REMOTE, VERIFY_ALL }; + +HS_DTRACE_PROBE_DECL1(hotspot_jni, GetDefaultJavaVMInitArgs__entry, void*); +DT_RETURN_MARK_DECL(GetDefaultJavaVMInitArgs, jint); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetDefaultJavaVMInitArgs(void *args_) { + HS_DTRACE_PROBE1(hotspot_jni, GetDefaultJavaVMInitArgs__entry, args_); + JDK1_1InitArgs *args = (JDK1_1InitArgs *)args_; + jint ret = JNI_ERR; + DT_RETURN_MARK(GetDefaultJavaVMInitArgs, jint, (const jint&)ret); + + if (Threads::is_supported_jni_version(args->version)) { + ret = JNI_OK; + } + // 1.1 style no longer supported in hotspot. + // According the JNI spec, we should update args->version on return. + // We also use the structure to communicate with launcher about default + // stack size. + if (args->version == JNI_VERSION_1_1) { + args->version = JNI_VERSION_1_2; + // javaStackSize is int in arguments structure + assert(jlong(ThreadStackSize) * K < INT_MAX, "integer overflow"); + args->javaStackSize = (jint)(ThreadStackSize * K); + } + return ret; +} + +HS_DTRACE_PROBE_DECL3(hotspot_jni, CreateJavaVM__entry, vm, penv, args); +DT_RETURN_MARK_DECL(CreateJavaVM, jint); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) { + HS_DTRACE_PROBE3(hotspot_jni, CreateJavaVM__entry, vm, penv, args); + + jint result = JNI_ERR; + DT_RETURN_MARK(CreateJavaVM, jint, (const jint&)result); + + // At the moment it's only possible to have one Java VM, + // since some of the runtime state is in global variables. + + // We cannot use our mutex locks here, since they only work on + // Threads. We do an atomic compare and exchange to ensure only + // one thread can call this method at a time + + // We use Atomic::xchg rather than Atomic::add/dec since on some platforms + // the add/dec implementations are dependent on whether we are running + // on a multiprocessor, and at this stage of initialization the os::is_MP + // function used to determine this will always return false. Atomic::xchg + // does not have this problem. + if (Atomic::xchg(1, &vm_created) == 1) { + return JNI_ERR; // already created, or create attempt in progress + } + if (Atomic::xchg(0, &safe_to_recreate_vm) == 0) { + return JNI_ERR; // someone tried and failed and retry not allowed. + } + + assert(vm_created == 1, "vm_created is true during the creation"); + + /** + * Certain errors during initialization are recoverable and do not + * prevent this method from being called again at a later time + * (perhaps with different arguments). However, at a certain + * point during initialization if an error occurs we cannot allow + * this function to be called again (or it will crash). In those + * situations, the 'canTryAgain' flag is set to false, which atomically + * sets safe_to_recreate_vm to 1, such that any new call to + * JNI_CreateJavaVM will immediately fail using the above logic. + */ + bool can_try_again = true; + + result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again); + if (result == JNI_OK) { + JavaThread *thread = JavaThread::current(); + /* thread is thread_in_vm here */ + *vm = (JavaVM *)(&main_vm); + *(JNIEnv**)penv = thread->jni_environment(); + + // Tracks the time application was running before GC + RuntimeService::record_application_start(); + + // Notify JVMTI + if (JvmtiExport::should_post_thread_life()) { + JvmtiExport::post_thread_start(thread); + } + // Check if we should compile all classes on bootclasspath + NOT_PRODUCT(if (CompileTheWorld) ClassLoader::compile_the_world();) + // Since this is not a JVM_ENTRY we have to set the thread state manually before leaving. + ThreadStateTransition::transition_and_fence(thread, _thread_in_vm, _thread_in_native); + } else { + if (can_try_again) { + // reset safe_to_recreate_vm to 1 so that retrial would be possible + safe_to_recreate_vm = 1; + } + + // Creation failed. We must reset vm_created + *vm = 0; + *(JNIEnv**)penv = 0; + // reset vm_created last to avoid race condition. Use OrderAccess to + // control both compiler and architectural-based reordering. + OrderAccess::release_store(&vm_created, 0); + } + + return result; +} + +HS_DTRACE_PROBE_DECL3(hotspot_jni, GetCreatedJavaVMs__entry, \ + JavaVM**, jsize, jsize*); +HS_DTRACE_PROBE_DECL1(hotspot_jni, GetCreatedJavaVMs__return, jint); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetCreatedJavaVMs(JavaVM **vm_buf, jsize bufLen, jsize *numVMs) { + // See bug 4367188, the wrapper can sometimes cause VM crashes + // JNIWrapper("GetCreatedJavaVMs"); + HS_DTRACE_PROBE3(hotspot_jni, GetCreatedJavaVMs__entry, \ + vm_buf, bufLen, numVMs); + if (vm_created) { + if (numVMs != NULL) *numVMs = 1; + if (bufLen > 0) *vm_buf = (JavaVM *)(&main_vm); + } else { + if (numVMs != NULL) *numVMs = 0; + } + HS_DTRACE_PROBE1(hotspot_jni, GetCreatedJavaVMs__return, JNI_OK); + return JNI_OK; +} + +extern "C" { + +DT_RETURN_MARK_DECL(DestroyJavaVM, jint); + +jint JNICALL jni_DestroyJavaVM(JavaVM *vm) { + DTRACE_PROBE1(hotspot_jni, DestroyJavaVM__entry, vm); + jint res = JNI_ERR; + DT_RETURN_MARK(DestroyJavaVM, jint, (const jint&)res); + + if (!vm_created) { + res = JNI_ERR; + return res; + } + + JNIWrapper("DestroyJavaVM"); + JNIEnv *env; + JavaVMAttachArgs destroyargs; + destroyargs.version = CurrentVersion; + destroyargs.name = (char *)"DestroyJavaVM"; + destroyargs.group = NULL; + res = vm->AttachCurrentThread((void **)&env, (void *)&destroyargs); + if (res != JNI_OK) { + return res; + } + + // Since this is not a JVM_ENTRY we have to set the thread state manually before entering. + JavaThread* thread = JavaThread::current(); + ThreadStateTransition::transition_from_native(thread, _thread_in_vm); + if (Threads::destroy_vm()) { + // Should not change thread state, VM is gone + vm_created = false; + res = JNI_OK; + return res; + } else { + ThreadStateTransition::transition_and_fence(thread, _thread_in_vm, _thread_in_native); + res = JNI_ERR; + return res; + } +} + + +static jint attach_current_thread(JavaVM *vm, void **penv, void *_args, bool daemon) { + JavaVMAttachArgs *args = (JavaVMAttachArgs *) _args; + + // Check below commented out from JDK1.2fcs as well + /* + if (args && (args->version != JNI_VERSION_1_1 || args->version != JNI_VERSION_1_2)) { + return JNI_EVERSION; + } + */ + + Thread* t = ThreadLocalStorage::get_thread_slow(); + if (t != NULL) { + // If the thread has been attached this operation is a no-op + *(JNIEnv**)penv = ((JavaThread*) t)->jni_environment(); + return JNI_OK; + } + + // Create a thread and mark it as attaching so it will be skipped by the + // ThreadsListEnumerator - see CR 6404306 + JavaThread* thread = new JavaThread(true); + + // Set correct safepoint info. The thread is going to call into Java when + // initializing the Java level thread object. Hence, the correct state must + // be set in order for the Safepoint code to deal with it correctly. + thread->set_thread_state(_thread_in_vm); + // Must do this before initialize_thread_local_storage + thread->record_stack_base_and_size(); + thread->initialize_thread_local_storage(); + + if (!os::create_attached_thread(thread)) { + delete thread; + return JNI_ERR; + } + thread->initialize_tlab(); + + // Crucial that we do not have a safepoint check for this thread, since it has + // not been added to the Thread list yet. + { Threads_lock->lock_without_safepoint_check(); + // This must be inside this lock in order to get FullGCALot to work properly, i.e., to + // avoid this thread trying to do a GC before it is added to the thread-list + thread->set_active_handles(JNIHandleBlock::allocate_block()); + Threads::add(thread, daemon); + Threads_lock->unlock(); + } + // Create thread group and name info from attach arguments + oop group = NULL; + char* thread_name = NULL; + if (args != NULL && Threads::is_supported_jni_version(args->version)) { + group = JNIHandles::resolve(args->group); + thread_name = args->name; // may be NULL + } + if (group == NULL) group = Universe::main_thread_group(); + + // Create Java level thread object and attach it to this thread + bool attach_failed = false; + { + EXCEPTION_MARK; + HandleMark hm(THREAD); + Handle thread_group(THREAD, group); + thread->allocate_threadObj(thread_group, thread_name, daemon, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + // cleanup outside the handle mark. + attach_failed = true; + } + } + + if (attach_failed) { + // Added missing cleanup + thread->cleanup_failed_attach_current_thread(); + return JNI_ERR; + } + + // mark the thread as no longer attaching + // this uses a fence to push the change through so we don't have + // to regrab the threads_lock + thread->set_attached(); + + // Enable stack overflow checks + thread->create_stack_guard_pages(); + + // Set java thread status. + java_lang_Thread::set_thread_status(thread->threadObj(), + java_lang_Thread::RUNNABLE); + + // Notify the debugger + if (JvmtiExport::should_post_thread_life()) { + JvmtiExport::post_thread_start(thread); + } + + *(JNIEnv**)penv = thread->jni_environment(); + + // Now leaving the VM, so change thread_state. This is normally automatically taken care + // of in the JVM_ENTRY. But in this situation we have to do it manually. Notice, that by + // using ThreadStateTransition::transition, we do a callback to the safepoint code if + // needed. + + ThreadStateTransition::transition_and_fence(thread, _thread_in_vm, _thread_in_native); + + // Perform any platform dependent FPU setup + os::setup_fpu(); + + return JNI_OK; +} + + +jint JNICALL jni_AttachCurrentThread(JavaVM *vm, void **penv, void *_args) { + DTRACE_PROBE3(hotspot_jni, AttachCurrentThread__entry, vm, penv, _args); + if (!vm_created) { + DTRACE_PROBE1(hotspot_jni, AttachCurrentThread__return, JNI_ERR); + return JNI_ERR; + } + + JNIWrapper("AttachCurrentThread"); + jint ret = attach_current_thread(vm, penv, _args, false); + DTRACE_PROBE1(hotspot_jni, AttachCurrentThread__return, ret); + return ret; +} + + +jint JNICALL jni_DetachCurrentThread(JavaVM *vm) { + DTRACE_PROBE1(hotspot_jni, DetachCurrentThread__entry, vm); + VM_Exit::block_if_vm_exited(); + + JNIWrapper("DetachCurrentThread"); + + // If the thread has been deattacted the operations is a no-op + if (ThreadLocalStorage::thread() == NULL) { + DTRACE_PROBE1(hotspot_jni, DetachCurrentThread__return, JNI_OK); + return JNI_OK; + } + + JavaThread* thread = JavaThread::current(); + if (thread->has_last_Java_frame()) { + DTRACE_PROBE1(hotspot_jni, DetachCurrentThread__return, JNI_ERR); + // Can't detach a thread that's running java, that can't work. + return JNI_ERR; + } + + // Safepoint support. Have to do call-back to safepoint code, if in the + // middel of a safepoint operation + ThreadStateTransition::transition_from_native(thread, _thread_in_vm); + + // XXX: Note that JavaThread::exit() call below removes the guards on the + // stack pages set up via enable_stack_{red,yellow}_zone() calls + // above in jni_AttachCurrentThread. Unfortunately, while the setting + // of the guards is visible in jni_AttachCurrentThread above, + // the removal of the guards is buried below in JavaThread::exit() + // here. The abstraction should be more symmetrically either exposed + // or hidden (e.g. it could probably be hidden in the same + // (platform-dependent) methods where we do alternate stack + // maintenance work?) + thread->exit(false, JavaThread::jni_detach); + delete thread; + + DTRACE_PROBE1(hotspot_jni, DetachCurrentThread__return, JNI_OK); + return JNI_OK; +} + +DT_RETURN_MARK_DECL(GetEnv, jint); + +jint JNICALL jni_GetEnv(JavaVM *vm, void **penv, jint version) { + DTRACE_PROBE3(hotspot_jni, GetEnv__entry, vm, penv, version); + jint ret = JNI_ERR; + DT_RETURN_MARK(GetEnv, jint, (const jint&)ret); + + if (!vm_created) { + *penv = NULL; + ret = JNI_EDETACHED; + return ret; + } + + if (JvmtiExport::is_jvmti_version(version)) { + ret = JvmtiExport::get_jvmti_interface(vm, penv, version); + return ret; + } + +#ifndef JVMPI_VERSION_1 +// need these in order to be polite about older agents +#define JVMPI_VERSION_1 ((jint)0x10000001) +#define JVMPI_VERSION_1_1 ((jint)0x10000002) +#define JVMPI_VERSION_1_2 ((jint)0x10000003) +#endif // !JVMPI_VERSION_1 + + Thread* thread = ThreadLocalStorage::thread(); + if (thread != NULL && thread->is_Java_thread()) { + if (Threads::is_supported_jni_version_including_1_1(version)) { + *(JNIEnv**)penv = ((JavaThread*) thread)->jni_environment(); + ret = JNI_OK; + return ret; + + } else if (version == JVMPI_VERSION_1 || + version == JVMPI_VERSION_1_1 || + version == JVMPI_VERSION_1_2) { + tty->print_cr("ERROR: JVMPI, an experimental interface, is no longer supported."); + tty->print_cr("Please use the supported interface: the JVM Tool Interface (JVM TI)."); + ret = JNI_EVERSION; + return ret; + } else if (JvmtiExport::is_jvmdi_version(version)) { + tty->print_cr("FATAL ERROR: JVMDI is no longer supported."); + tty->print_cr("Please use the supported interface: the JVM Tool Interface (JVM TI)."); + ret = JNI_EVERSION; + return ret; + } else { + *penv = NULL; + ret = JNI_EVERSION; + return ret; + } + } else { + *penv = NULL; + ret = JNI_EDETACHED; + return ret; + } +} + + +jint JNICALL jni_AttachCurrentThreadAsDaemon(JavaVM *vm, void **penv, void *_args) { + DTRACE_PROBE3(hotspot_jni, AttachCurrentThreadAsDaemon__entry, vm, penv, _args); + if (!vm_created) { + DTRACE_PROBE1(hotspot_jni, AttachCurrentThreadAsDaemon__return, JNI_ERR); + return JNI_ERR; + } + + JNIWrapper("AttachCurrentThreadAsDaemon"); + jint ret = attach_current_thread(vm, penv, _args, true); + DTRACE_PROBE1(hotspot_jni, AttachCurrentThreadAsDaemon__return, ret); + return ret; +} + + +} // End extern "C" + +const struct JNIInvokeInterface_ jni_InvokeInterface = { + NULL, + NULL, + NULL, + + jni_DestroyJavaVM, + jni_AttachCurrentThread, + jni_DetachCurrentThread, + jni_GetEnv, + jni_AttachCurrentThreadAsDaemon +}; diff --git a/hotspot/src/share/vm/prims/jni.h b/hotspot/src/share/vm/prims/jni.h new file mode 100644 index 00000000000..f1d405e33ab --- /dev/null +++ b/hotspot/src/share/vm/prims/jni.h @@ -0,0 +1,1959 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * We used part of Netscape's Java Runtime Interface (JRI) as the starting + * point of our design and implementation. + */ + +/****************************************************************************** + * Java Runtime Interface + * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved. + *****************************************************************************/ + +#ifndef _JAVASOFT_JNI_H_ +#define _JAVASOFT_JNI_H_ + +#include +#include + +/* jni_md.h contains the machine-dependent typedefs for jbyte, jint + and jlong */ + +#include "jni_md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * JNI Types + */ + +#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H + +typedef unsigned char jboolean; +typedef unsigned short jchar; +typedef short jshort; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +#ifdef __cplusplus + +class _jobject {}; +class _jclass : public _jobject {}; +class _jthrowable : public _jobject {}; +class _jstring : public _jobject {}; +class _jarray : public _jobject {}; +class _jbooleanArray : public _jarray {}; +class _jbyteArray : public _jarray {}; +class _jcharArray : public _jarray {}; +class _jshortArray : public _jarray {}; +class _jintArray : public _jarray {}; +class _jlongArray : public _jarray {}; +class _jfloatArray : public _jarray {}; +class _jdoubleArray : public _jarray {}; +class _jobjectArray : public _jarray {}; + +typedef _jobject *jobject; +typedef _jclass *jclass; +typedef _jthrowable *jthrowable; +typedef _jstring *jstring; +typedef _jarray *jarray; +typedef _jbooleanArray *jbooleanArray; +typedef _jbyteArray *jbyteArray; +typedef _jcharArray *jcharArray; +typedef _jshortArray *jshortArray; +typedef _jintArray *jintArray; +typedef _jlongArray *jlongArray; +typedef _jfloatArray *jfloatArray; +typedef _jdoubleArray *jdoubleArray; +typedef _jobjectArray *jobjectArray; + +#else + +struct _jobject; + +typedef struct _jobject *jobject; +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +#endif + +typedef jobject jweak; + +typedef union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +} jvalue; + +struct _jfieldID; +typedef struct _jfieldID *jfieldID; + +struct _jmethodID; +typedef struct _jmethodID *jmethodID; + +/* Return values from jobjectRefType */ +typedef enum _jobjectType { + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 +} jobjectRefType; + + +#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */ + +/* + * jboolean constants + */ + +#define JNI_FALSE 0 +#define JNI_TRUE 1 + +/* + * possible return values for JNI functions. + */ + +#define JNI_OK 0 /* success */ +#define JNI_ERR (-1) /* unknown error */ +#define JNI_EDETACHED (-2) /* thread detached from the VM */ +#define JNI_EVERSION (-3) /* JNI version error */ +#define JNI_ENOMEM (-4) /* not enough memory */ +#define JNI_EEXIST (-5) /* VM already created */ +#define JNI_EINVAL (-6) /* invalid arguments */ + +/* + * used in ReleaseScalarArrayElements + */ + +#define JNI_COMMIT 1 +#define JNI_ABORT 2 + +/* + * used in RegisterNatives to describe native method name, signature, + * and function pointer. + */ + +typedef struct { + char *name; + char *signature; + void *fnPtr; +} JNINativeMethod; + +/* + * JNI Native Method Interface. + */ + +struct JNINativeInterface_; + +struct JNIEnv_; + +#ifdef __cplusplus +typedef JNIEnv_ JNIEnv; +#else +typedef const struct JNINativeInterface_ *JNIEnv; +#endif + +/* + * JNI Invocation Interface. + */ + +struct JNIInvokeInterface_; + +struct JavaVM_; + +#ifdef __cplusplus +typedef JavaVM_ JavaVM; +#else +typedef const struct JNIInvokeInterface_ *JavaVM; +#endif + +struct JNINativeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + void *reserved3; + jint (JNICALL *GetVersion)(JNIEnv *env); + + jclass (JNICALL *DefineClass) + (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, + jsize len); + jclass (JNICALL *FindClass) + (JNIEnv *env, const char *name); + + jmethodID (JNICALL *FromReflectedMethod) + (JNIEnv *env, jobject method); + jfieldID (JNICALL *FromReflectedField) + (JNIEnv *env, jobject field); + + jobject (JNICALL *ToReflectedMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); + + jclass (JNICALL *GetSuperclass) + (JNIEnv *env, jclass sub); + jboolean (JNICALL *IsAssignableFrom) + (JNIEnv *env, jclass sub, jclass sup); + + jobject (JNICALL *ToReflectedField) + (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); + + jint (JNICALL *Throw) + (JNIEnv *env, jthrowable obj); + jint (JNICALL *ThrowNew) + (JNIEnv *env, jclass clazz, const char *msg); + jthrowable (JNICALL *ExceptionOccurred) + (JNIEnv *env); + void (JNICALL *ExceptionDescribe) + (JNIEnv *env); + void (JNICALL *ExceptionClear) + (JNIEnv *env); + void (JNICALL *FatalError) + (JNIEnv *env, const char *msg); + + jint (JNICALL *PushLocalFrame) + (JNIEnv *env, jint capacity); + jobject (JNICALL *PopLocalFrame) + (JNIEnv *env, jobject result); + + jobject (JNICALL *NewGlobalRef) + (JNIEnv *env, jobject lobj); + void (JNICALL *DeleteGlobalRef) + (JNIEnv *env, jobject gref); + void (JNICALL *DeleteLocalRef) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsSameObject) + (JNIEnv *env, jobject obj1, jobject obj2); + jobject (JNICALL *NewLocalRef) + (JNIEnv *env, jobject ref); + jint (JNICALL *EnsureLocalCapacity) + (JNIEnv *env, jint capacity); + + jobject (JNICALL *AllocObject) + (JNIEnv *env, jclass clazz); + jobject (JNICALL *NewObject) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *NewObjectV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *NewObjectA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jclass (JNICALL *GetObjectClass) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsInstanceOf) + (JNIEnv *env, jobject obj, jclass clazz); + + jmethodID (JNICALL *GetMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallObjectMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jobject (JNICALL *CallObjectMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jobject (JNICALL *CallObjectMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jboolean (JNICALL *CallBooleanMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jboolean (JNICALL *CallBooleanMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jboolean (JNICALL *CallBooleanMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jbyte (JNICALL *CallByteMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jbyte (JNICALL *CallByteMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jbyte (JNICALL *CallByteMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallCharMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jchar (JNICALL *CallCharMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jchar (JNICALL *CallCharMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallShortMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jshort (JNICALL *CallShortMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jshort (JNICALL *CallShortMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallIntMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jint (JNICALL *CallIntMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jint (JNICALL *CallIntMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallLongMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jlong (JNICALL *CallLongMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jlong (JNICALL *CallLongMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallFloatMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jfloat (JNICALL *CallFloatMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jfloat (JNICALL *CallFloatMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallDoubleMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jdouble (JNICALL *CallDoubleMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jdouble (JNICALL *CallDoubleMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallVoidMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + void (JNICALL *CallVoidMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + void (JNICALL *CallVoidMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jobject (JNICALL *CallNonvirtualObjectMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallNonvirtualObjectMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jobject (JNICALL *CallNonvirtualObjectMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jboolean (JNICALL *CallNonvirtualBooleanMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallNonvirtualBooleanMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jboolean (JNICALL *CallNonvirtualBooleanMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jbyte (JNICALL *CallNonvirtualByteMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallNonvirtualByteMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jbyte (JNICALL *CallNonvirtualByteMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jchar (JNICALL *CallNonvirtualCharMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallNonvirtualCharMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jchar (JNICALL *CallNonvirtualCharMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jshort (JNICALL *CallNonvirtualShortMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallNonvirtualShortMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jshort (JNICALL *CallNonvirtualShortMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jint (JNICALL *CallNonvirtualIntMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallNonvirtualIntMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jint (JNICALL *CallNonvirtualIntMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jlong (JNICALL *CallNonvirtualLongMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallNonvirtualLongMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jlong (JNICALL *CallNonvirtualLongMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jfloat (JNICALL *CallNonvirtualFloatMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallNonvirtualFloatMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jfloat (JNICALL *CallNonvirtualFloatMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jdouble (JNICALL *CallNonvirtualDoubleMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallNonvirtualDoubleMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jdouble (JNICALL *CallNonvirtualDoubleMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + void (JNICALL *CallNonvirtualVoidMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + void (JNICALL *CallNonvirtualVoidMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + void (JNICALL *CallNonvirtualVoidMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jfieldID (JNICALL *GetFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *GetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jboolean (JNICALL *GetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jbyte (JNICALL *GetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jchar (JNICALL *GetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jshort (JNICALL *GetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jint (JNICALL *GetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jlong (JNICALL *GetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jfloat (JNICALL *GetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jdouble (JNICALL *GetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + + void (JNICALL *SetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); + void (JNICALL *SetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); + void (JNICALL *SetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); + void (JNICALL *SetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); + void (JNICALL *SetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); + void (JNICALL *SetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jint val); + void (JNICALL *SetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); + void (JNICALL *SetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); + void (JNICALL *SetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); + + jmethodID (JNICALL *GetStaticMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallStaticObjectMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallStaticObjectMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *CallStaticObjectMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jboolean (JNICALL *CallStaticBooleanMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallStaticBooleanMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jboolean (JNICALL *CallStaticBooleanMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jbyte (JNICALL *CallStaticByteMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallStaticByteMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jbyte (JNICALL *CallStaticByteMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallStaticCharMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallStaticCharMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jchar (JNICALL *CallStaticCharMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallStaticShortMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallStaticShortMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jshort (JNICALL *CallStaticShortMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallStaticIntMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallStaticIntMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jint (JNICALL *CallStaticIntMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallStaticLongMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallStaticLongMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jlong (JNICALL *CallStaticLongMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallStaticFloatMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallStaticFloatMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jfloat (JNICALL *CallStaticFloatMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallStaticDoubleMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallStaticDoubleMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jdouble (JNICALL *CallStaticDoubleMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallStaticVoidMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, ...); + void (JNICALL *CallStaticVoidMethodV) + (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); + void (JNICALL *CallStaticVoidMethodA) + (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); + + jfieldID (JNICALL *GetStaticFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + jobject (JNICALL *GetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jboolean (JNICALL *GetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jbyte (JNICALL *GetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jchar (JNICALL *GetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jshort (JNICALL *GetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jint (JNICALL *GetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jlong (JNICALL *GetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jfloat (JNICALL *GetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jdouble (JNICALL *GetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + + void (JNICALL *SetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); + void (JNICALL *SetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); + void (JNICALL *SetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); + void (JNICALL *SetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); + void (JNICALL *SetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); + void (JNICALL *SetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); + void (JNICALL *SetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); + void (JNICALL *SetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); + void (JNICALL *SetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); + + jstring (JNICALL *NewString) + (JNIEnv *env, const jchar *unicode, jsize len); + jsize (JNICALL *GetStringLength) + (JNIEnv *env, jstring str); + const jchar *(JNICALL *GetStringChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringChars) + (JNIEnv *env, jstring str, const jchar *chars); + + jstring (JNICALL *NewStringUTF) + (JNIEnv *env, const char *utf); + jsize (JNICALL *GetStringUTFLength) + (JNIEnv *env, jstring str); + const char* (JNICALL *GetStringUTFChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringUTFChars) + (JNIEnv *env, jstring str, const char* chars); + + + jsize (JNICALL *GetArrayLength) + (JNIEnv *env, jarray array); + + jobjectArray (JNICALL *NewObjectArray) + (JNIEnv *env, jsize len, jclass clazz, jobject init); + jobject (JNICALL *GetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index); + void (JNICALL *SetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index, jobject val); + + jbooleanArray (JNICALL *NewBooleanArray) + (JNIEnv *env, jsize len); + jbyteArray (JNICALL *NewByteArray) + (JNIEnv *env, jsize len); + jcharArray (JNICALL *NewCharArray) + (JNIEnv *env, jsize len); + jshortArray (JNICALL *NewShortArray) + (JNIEnv *env, jsize len); + jintArray (JNICALL *NewIntArray) + (JNIEnv *env, jsize len); + jlongArray (JNICALL *NewLongArray) + (JNIEnv *env, jsize len); + jfloatArray (JNICALL *NewFloatArray) + (JNIEnv *env, jsize len); + jdoubleArray (JNICALL *NewDoubleArray) + (JNIEnv *env, jsize len); + + jboolean * (JNICALL *GetBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *isCopy); + jbyte * (JNICALL *GetByteArrayElements) + (JNIEnv *env, jbyteArray array, jboolean *isCopy); + jchar * (JNICALL *GetCharArrayElements) + (JNIEnv *env, jcharArray array, jboolean *isCopy); + jshort * (JNICALL *GetShortArrayElements) + (JNIEnv *env, jshortArray array, jboolean *isCopy); + jint * (JNICALL *GetIntArrayElements) + (JNIEnv *env, jintArray array, jboolean *isCopy); + jlong * (JNICALL *GetLongArrayElements) + (JNIEnv *env, jlongArray array, jboolean *isCopy); + jfloat * (JNICALL *GetFloatArrayElements) + (JNIEnv *env, jfloatArray array, jboolean *isCopy); + jdouble * (JNICALL *GetDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jboolean *isCopy); + + void (JNICALL *ReleaseBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); + void (JNICALL *ReleaseByteArrayElements) + (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); + void (JNICALL *ReleaseCharArrayElements) + (JNIEnv *env, jcharArray array, jchar *elems, jint mode); + void (JNICALL *ReleaseShortArrayElements) + (JNIEnv *env, jshortArray array, jshort *elems, jint mode); + void (JNICALL *ReleaseIntArrayElements) + (JNIEnv *env, jintArray array, jint *elems, jint mode); + void (JNICALL *ReleaseLongArrayElements) + (JNIEnv *env, jlongArray array, jlong *elems, jint mode); + void (JNICALL *ReleaseFloatArrayElements) + (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); + void (JNICALL *ReleaseDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); + + void (JNICALL *GetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); + void (JNICALL *GetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); + void (JNICALL *GetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); + void (JNICALL *GetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); + void (JNICALL *GetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); + void (JNICALL *GetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); + void (JNICALL *GetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); + void (JNICALL *GetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); + + void (JNICALL *SetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); + void (JNICALL *SetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); + void (JNICALL *SetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); + void (JNICALL *SetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); + void (JNICALL *SetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); + void (JNICALL *SetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); + void (JNICALL *SetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); + void (JNICALL *SetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); + + jint (JNICALL *RegisterNatives) + (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, + jint nMethods); + jint (JNICALL *UnregisterNatives) + (JNIEnv *env, jclass clazz); + + jint (JNICALL *MonitorEnter) + (JNIEnv *env, jobject obj); + jint (JNICALL *MonitorExit) + (JNIEnv *env, jobject obj); + + jint (JNICALL *GetJavaVM) + (JNIEnv *env, JavaVM **vm); + + void (JNICALL *GetStringRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); + void (JNICALL *GetStringUTFRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, char *buf); + + void * (JNICALL *GetPrimitiveArrayCritical) + (JNIEnv *env, jarray array, jboolean *isCopy); + void (JNICALL *ReleasePrimitiveArrayCritical) + (JNIEnv *env, jarray array, void *carray, jint mode); + + const jchar * (JNICALL *GetStringCritical) + (JNIEnv *env, jstring string, jboolean *isCopy); + void (JNICALL *ReleaseStringCritical) + (JNIEnv *env, jstring string, const jchar *cstring); + + jweak (JNICALL *NewWeakGlobalRef) + (JNIEnv *env, jobject obj); + void (JNICALL *DeleteWeakGlobalRef) + (JNIEnv *env, jweak ref); + + jboolean (JNICALL *ExceptionCheck) + (JNIEnv *env); + + jobject (JNICALL *NewDirectByteBuffer) + (JNIEnv* env, void* address, jlong capacity); + void* (JNICALL *GetDirectBufferAddress) + (JNIEnv* env, jobject buf); + jlong (JNICALL *GetDirectBufferCapacity) + (JNIEnv* env, jobject buf); + + /* New JNI 1.6 Features */ + + jobjectRefType (JNICALL *GetObjectRefType) + (JNIEnv* env, jobject obj); +}; + +/* + * We use inlined functions for C++ so that programmers can write: + * + * env->FindClass("java/lang/String") + * + * in C++ rather than: + * + * (*env)->FindClass(env, "java/lang/String") + * + * in C. + */ + +struct JNIEnv_ { + const struct JNINativeInterface_ *functions; +#ifdef __cplusplus + + jint GetVersion() { + return functions->GetVersion(this); + } + jclass DefineClass(const char *name, jobject loader, const jbyte *buf, + jsize len) { + return functions->DefineClass(this, name, loader, buf, len); + } + jclass FindClass(const char *name) { + return functions->FindClass(this, name); + } + jmethodID FromReflectedMethod(jobject method) { + return functions->FromReflectedMethod(this,method); + } + jfieldID FromReflectedField(jobject field) { + return functions->FromReflectedField(this,field); + } + + jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { + return functions->ToReflectedMethod(this, cls, methodID, isStatic); + } + + jclass GetSuperclass(jclass sub) { + return functions->GetSuperclass(this, sub); + } + jboolean IsAssignableFrom(jclass sub, jclass sup) { + return functions->IsAssignableFrom(this, sub, sup); + } + + jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { + return functions->ToReflectedField(this,cls,fieldID,isStatic); + } + + jint Throw(jthrowable obj) { + return functions->Throw(this, obj); + } + jint ThrowNew(jclass clazz, const char *msg) { + return functions->ThrowNew(this, clazz, msg); + } + jthrowable ExceptionOccurred() { + return functions->ExceptionOccurred(this); + } + void ExceptionDescribe() { + functions->ExceptionDescribe(this); + } + void ExceptionClear() { + functions->ExceptionClear(this); + } + void FatalError(const char *msg) { + functions->FatalError(this, msg); + } + + jint PushLocalFrame(jint capacity) { + return functions->PushLocalFrame(this,capacity); + } + jobject PopLocalFrame(jobject result) { + return functions->PopLocalFrame(this,result); + } + + jobject NewGlobalRef(jobject lobj) { + return functions->NewGlobalRef(this,lobj); + } + void DeleteGlobalRef(jobject gref) { + functions->DeleteGlobalRef(this,gref); + } + void DeleteLocalRef(jobject obj) { + functions->DeleteLocalRef(this, obj); + } + + jboolean IsSameObject(jobject obj1, jobject obj2) { + return functions->IsSameObject(this,obj1,obj2); + } + + jobject NewLocalRef(jobject ref) { + return functions->NewLocalRef(this,ref); + } + jint EnsureLocalCapacity(jint capacity) { + return functions->EnsureLocalCapacity(this,capacity); + } + + jobject AllocObject(jclass clazz) { + return functions->AllocObject(this,clazz); + } + jobject NewObject(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = functions->NewObjectV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject NewObjectV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->NewObjectV(this,clazz,methodID,args); + } + jobject NewObjectA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->NewObjectA(this,clazz,methodID,args); + } + + jclass GetObjectClass(jobject obj) { + return functions->GetObjectClass(this,obj); + } + jboolean IsInstanceOf(jobject obj, jclass clazz) { + return functions->IsInstanceOf(this,obj,clazz); + } + + jmethodID GetMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetMethodID(this,clazz,name,sig); + } + + jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallObjectMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jobject CallObjectMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallObjectMethodV(this,obj,methodID,args); + } + jobject CallObjectMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallObjectMethodA(this,obj,methodID,args); + } + + jboolean CallBooleanMethod(jobject obj, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallBooleanMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallBooleanMethodV(this,obj,methodID,args); + } + jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallBooleanMethodA(this,obj,methodID, args); + } + + jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallByteMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jbyte CallByteMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallByteMethodV(this,obj,methodID,args); + } + jbyte CallByteMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallByteMethodA(this,obj,methodID,args); + } + + jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallCharMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jchar CallCharMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallCharMethodV(this,obj,methodID,args); + } + jchar CallCharMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallCharMethodA(this,obj,methodID,args); + } + + jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallShortMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jshort CallShortMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallShortMethodV(this,obj,methodID,args); + } + jshort CallShortMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallShortMethodA(this,obj,methodID,args); + } + + jint CallIntMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallIntMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jint CallIntMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallIntMethodV(this,obj,methodID,args); + } + jint CallIntMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallIntMethodA(this,obj,methodID,args); + } + + jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallLongMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jlong CallLongMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallLongMethodV(this,obj,methodID,args); + } + jlong CallLongMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallLongMethodA(this,obj,methodID,args); + } + + jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallFloatMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jfloat CallFloatMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallFloatMethodV(this,obj,methodID,args); + } + jfloat CallFloatMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallFloatMethodA(this,obj,methodID,args); + } + + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallDoubleMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallDoubleMethodV(this,obj,methodID,args); + } + jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallDoubleMethodA(this,obj,methodID,args); + } + + void CallVoidMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallVoidMethodV(this,obj,methodID,args); + va_end(args); + } + void CallVoidMethodV(jobject obj, jmethodID methodID, + va_list args) { + functions->CallVoidMethodV(this,obj,methodID,args); + } + void CallVoidMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + functions->CallVoidMethodA(this,obj,methodID,args); + } + + jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + } + jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualObjectMethodA(this,obj,clazz, + methodID,args); + } + + jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + } + jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualBooleanMethodA(this,obj,clazz, + methodID, args); + } + + jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + } + jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualByteMethodA(this,obj,clazz, + methodID,args); + } + + jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + } + jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualCharMethodA(this,obj,clazz, + methodID,args); + } + + jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + } + jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualShortMethodA(this,obj,clazz, + methodID,args); + } + + jint CallNonvirtualIntMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + } + jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualIntMethodA(this,obj,clazz, + methodID,args); + } + + jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + } + jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualLongMethodA(this,obj,clazz, + methodID,args); + } + + jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + } + jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualFloatMethodA(this,obj,clazz, + methodID,args); + } + + jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + } + jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualDoubleMethodA(this,obj,clazz, + methodID,args); + } + + void CallNonvirtualVoidMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + va_end(args); + } + void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + } + void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args); + } + + jfieldID GetFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetFieldID(this,clazz,name,sig); + } + + jobject GetObjectField(jobject obj, jfieldID fieldID) { + return functions->GetObjectField(this,obj,fieldID); + } + jboolean GetBooleanField(jobject obj, jfieldID fieldID) { + return functions->GetBooleanField(this,obj,fieldID); + } + jbyte GetByteField(jobject obj, jfieldID fieldID) { + return functions->GetByteField(this,obj,fieldID); + } + jchar GetCharField(jobject obj, jfieldID fieldID) { + return functions->GetCharField(this,obj,fieldID); + } + jshort GetShortField(jobject obj, jfieldID fieldID) { + return functions->GetShortField(this,obj,fieldID); + } + jint GetIntField(jobject obj, jfieldID fieldID) { + return functions->GetIntField(this,obj,fieldID); + } + jlong GetLongField(jobject obj, jfieldID fieldID) { + return functions->GetLongField(this,obj,fieldID); + } + jfloat GetFloatField(jobject obj, jfieldID fieldID) { + return functions->GetFloatField(this,obj,fieldID); + } + jdouble GetDoubleField(jobject obj, jfieldID fieldID) { + return functions->GetDoubleField(this,obj,fieldID); + } + + void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + functions->SetObjectField(this,obj,fieldID,val); + } + void SetBooleanField(jobject obj, jfieldID fieldID, + jboolean val) { + functions->SetBooleanField(this,obj,fieldID,val); + } + void SetByteField(jobject obj, jfieldID fieldID, + jbyte val) { + functions->SetByteField(this,obj,fieldID,val); + } + void SetCharField(jobject obj, jfieldID fieldID, + jchar val) { + functions->SetCharField(this,obj,fieldID,val); + } + void SetShortField(jobject obj, jfieldID fieldID, + jshort val) { + functions->SetShortField(this,obj,fieldID,val); + } + void SetIntField(jobject obj, jfieldID fieldID, + jint val) { + functions->SetIntField(this,obj,fieldID,val); + } + void SetLongField(jobject obj, jfieldID fieldID, + jlong val) { + functions->SetLongField(this,obj,fieldID,val); + } + void SetFloatField(jobject obj, jfieldID fieldID, + jfloat val) { + functions->SetFloatField(this,obj,fieldID,val); + } + void SetDoubleField(jobject obj, jfieldID fieldID, + jdouble val) { + functions->SetDoubleField(this,obj,fieldID,val); + } + + jmethodID GetStaticMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticMethodID(this,clazz,name,sig); + } + + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, + ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->CallStaticObjectMethodV(this,clazz,methodID,args); + } + jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->CallStaticObjectMethodA(this,clazz,methodID,args); + } + + jboolean CallStaticBooleanMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jboolean CallStaticBooleanMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + } + jboolean CallStaticBooleanMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticBooleanMethodA(this,clazz,methodID,args); + } + + jbyte CallStaticByteMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallStaticByteMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jbyte CallStaticByteMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticByteMethodV(this,clazz,methodID,args); + } + jbyte CallStaticByteMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticByteMethodA(this,clazz,methodID,args); + } + + jchar CallStaticCharMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallStaticCharMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jchar CallStaticCharMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticCharMethodV(this,clazz,methodID,args); + } + jchar CallStaticCharMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticCharMethodA(this,clazz,methodID,args); + } + + jshort CallStaticShortMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallStaticShortMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jshort CallStaticShortMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticShortMethodV(this,clazz,methodID,args); + } + jshort CallStaticShortMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticShortMethodA(this,clazz,methodID,args); + } + + jint CallStaticIntMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallStaticIntMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jint CallStaticIntMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticIntMethodV(this,clazz,methodID,args); + } + jint CallStaticIntMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticIntMethodA(this,clazz,methodID,args); + } + + jlong CallStaticLongMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallStaticLongMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jlong CallStaticLongMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticLongMethodV(this,clazz,methodID,args); + } + jlong CallStaticLongMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticLongMethodA(this,clazz,methodID,args); + } + + jfloat CallStaticFloatMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallStaticFloatMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jfloat CallStaticFloatMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticFloatMethodV(this,clazz,methodID,args); + } + jfloat CallStaticFloatMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticFloatMethodA(this,clazz,methodID,args); + } + + jdouble CallStaticDoubleMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jdouble CallStaticDoubleMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + } + jdouble CallStaticDoubleMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticDoubleMethodA(this,clazz,methodID,args); + } + + void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallStaticVoidMethodV(this,cls,methodID,args); + va_end(args); + } + void CallStaticVoidMethodV(jclass cls, jmethodID methodID, + va_list args) { + functions->CallStaticVoidMethodV(this,cls,methodID,args); + } + void CallStaticVoidMethodA(jclass cls, jmethodID methodID, + const jvalue * args) { + functions->CallStaticVoidMethodA(this,cls,methodID,args); + } + + jfieldID GetStaticFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticFieldID(this,clazz,name,sig); + } + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticObjectField(this,clazz,fieldID); + } + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticBooleanField(this,clazz,fieldID); + } + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticByteField(this,clazz,fieldID); + } + jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticCharField(this,clazz,fieldID); + } + jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticShortField(this,clazz,fieldID); + } + jint GetStaticIntField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticIntField(this,clazz,fieldID); + } + jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticLongField(this,clazz,fieldID); + } + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticFloatField(this,clazz,fieldID); + } + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticDoubleField(this,clazz,fieldID); + } + + void SetStaticObjectField(jclass clazz, jfieldID fieldID, + jobject value) { + functions->SetStaticObjectField(this,clazz,fieldID,value); + } + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, + jboolean value) { + functions->SetStaticBooleanField(this,clazz,fieldID,value); + } + void SetStaticByteField(jclass clazz, jfieldID fieldID, + jbyte value) { + functions->SetStaticByteField(this,clazz,fieldID,value); + } + void SetStaticCharField(jclass clazz, jfieldID fieldID, + jchar value) { + functions->SetStaticCharField(this,clazz,fieldID,value); + } + void SetStaticShortField(jclass clazz, jfieldID fieldID, + jshort value) { + functions->SetStaticShortField(this,clazz,fieldID,value); + } + void SetStaticIntField(jclass clazz, jfieldID fieldID, + jint value) { + functions->SetStaticIntField(this,clazz,fieldID,value); + } + void SetStaticLongField(jclass clazz, jfieldID fieldID, + jlong value) { + functions->SetStaticLongField(this,clazz,fieldID,value); + } + void SetStaticFloatField(jclass clazz, jfieldID fieldID, + jfloat value) { + functions->SetStaticFloatField(this,clazz,fieldID,value); + } + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, + jdouble value) { + functions->SetStaticDoubleField(this,clazz,fieldID,value); + } + + jstring NewString(const jchar *unicode, jsize len) { + return functions->NewString(this,unicode,len); + } + jsize GetStringLength(jstring str) { + return functions->GetStringLength(this,str); + } + const jchar *GetStringChars(jstring str, jboolean *isCopy) { + return functions->GetStringChars(this,str,isCopy); + } + void ReleaseStringChars(jstring str, const jchar *chars) { + functions->ReleaseStringChars(this,str,chars); + } + + jstring NewStringUTF(const char *utf) { + return functions->NewStringUTF(this,utf); + } + jsize GetStringUTFLength(jstring str) { + return functions->GetStringUTFLength(this,str); + } + const char* GetStringUTFChars(jstring str, jboolean *isCopy) { + return functions->GetStringUTFChars(this,str,isCopy); + } + void ReleaseStringUTFChars(jstring str, const char* chars) { + functions->ReleaseStringUTFChars(this,str,chars); + } + + jsize GetArrayLength(jarray array) { + return functions->GetArrayLength(this,array); + } + + jobjectArray NewObjectArray(jsize len, jclass clazz, + jobject init) { + return functions->NewObjectArray(this,len,clazz,init); + } + jobject GetObjectArrayElement(jobjectArray array, jsize index) { + return functions->GetObjectArrayElement(this,array,index); + } + void SetObjectArrayElement(jobjectArray array, jsize index, + jobject val) { + functions->SetObjectArrayElement(this,array,index,val); + } + + jbooleanArray NewBooleanArray(jsize len) { + return functions->NewBooleanArray(this,len); + } + jbyteArray NewByteArray(jsize len) { + return functions->NewByteArray(this,len); + } + jcharArray NewCharArray(jsize len) { + return functions->NewCharArray(this,len); + } + jshortArray NewShortArray(jsize len) { + return functions->NewShortArray(this,len); + } + jintArray NewIntArray(jsize len) { + return functions->NewIntArray(this,len); + } + jlongArray NewLongArray(jsize len) { + return functions->NewLongArray(this,len); + } + jfloatArray NewFloatArray(jsize len) { + return functions->NewFloatArray(this,len); + } + jdoubleArray NewDoubleArray(jsize len) { + return functions->NewDoubleArray(this,len); + } + + jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) { + return functions->GetBooleanArrayElements(this,array,isCopy); + } + jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) { + return functions->GetByteArrayElements(this,array,isCopy); + } + jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) { + return functions->GetCharArrayElements(this,array,isCopy); + } + jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) { + return functions->GetShortArrayElements(this,array,isCopy); + } + jint * GetIntArrayElements(jintArray array, jboolean *isCopy) { + return functions->GetIntArrayElements(this,array,isCopy); + } + jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) { + return functions->GetLongArrayElements(this,array,isCopy); + } + jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) { + return functions->GetFloatArrayElements(this,array,isCopy); + } + jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) { + return functions->GetDoubleArrayElements(this,array,isCopy); + } + + void ReleaseBooleanArrayElements(jbooleanArray array, + jboolean *elems, + jint mode) { + functions->ReleaseBooleanArrayElements(this,array,elems,mode); + } + void ReleaseByteArrayElements(jbyteArray array, + jbyte *elems, + jint mode) { + functions->ReleaseByteArrayElements(this,array,elems,mode); + } + void ReleaseCharArrayElements(jcharArray array, + jchar *elems, + jint mode) { + functions->ReleaseCharArrayElements(this,array,elems,mode); + } + void ReleaseShortArrayElements(jshortArray array, + jshort *elems, + jint mode) { + functions->ReleaseShortArrayElements(this,array,elems,mode); + } + void ReleaseIntArrayElements(jintArray array, + jint *elems, + jint mode) { + functions->ReleaseIntArrayElements(this,array,elems,mode); + } + void ReleaseLongArrayElements(jlongArray array, + jlong *elems, + jint mode) { + functions->ReleaseLongArrayElements(this,array,elems,mode); + } + void ReleaseFloatArrayElements(jfloatArray array, + jfloat *elems, + jint mode) { + functions->ReleaseFloatArrayElements(this,array,elems,mode); + } + void ReleaseDoubleArrayElements(jdoubleArray array, + jdouble *elems, + jint mode) { + functions->ReleaseDoubleArrayElements(this,array,elems,mode); + } + + void GetBooleanArrayRegion(jbooleanArray array, + jsize start, jsize len, jboolean *buf) { + functions->GetBooleanArrayRegion(this,array,start,len,buf); + } + void GetByteArrayRegion(jbyteArray array, + jsize start, jsize len, jbyte *buf) { + functions->GetByteArrayRegion(this,array,start,len,buf); + } + void GetCharArrayRegion(jcharArray array, + jsize start, jsize len, jchar *buf) { + functions->GetCharArrayRegion(this,array,start,len,buf); + } + void GetShortArrayRegion(jshortArray array, + jsize start, jsize len, jshort *buf) { + functions->GetShortArrayRegion(this,array,start,len,buf); + } + void GetIntArrayRegion(jintArray array, + jsize start, jsize len, jint *buf) { + functions->GetIntArrayRegion(this,array,start,len,buf); + } + void GetLongArrayRegion(jlongArray array, + jsize start, jsize len, jlong *buf) { + functions->GetLongArrayRegion(this,array,start,len,buf); + } + void GetFloatArrayRegion(jfloatArray array, + jsize start, jsize len, jfloat *buf) { + functions->GetFloatArrayRegion(this,array,start,len,buf); + } + void GetDoubleArrayRegion(jdoubleArray array, + jsize start, jsize len, jdouble *buf) { + functions->GetDoubleArrayRegion(this,array,start,len,buf); + } + + void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, + const jboolean *buf) { + functions->SetBooleanArrayRegion(this,array,start,len,buf); + } + void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, + const jbyte *buf) { + functions->SetByteArrayRegion(this,array,start,len,buf); + } + void SetCharArrayRegion(jcharArray array, jsize start, jsize len, + const jchar *buf) { + functions->SetCharArrayRegion(this,array,start,len,buf); + } + void SetShortArrayRegion(jshortArray array, jsize start, jsize len, + const jshort *buf) { + functions->SetShortArrayRegion(this,array,start,len,buf); + } + void SetIntArrayRegion(jintArray array, jsize start, jsize len, + const jint *buf) { + functions->SetIntArrayRegion(this,array,start,len,buf); + } + void SetLongArrayRegion(jlongArray array, jsize start, jsize len, + const jlong *buf) { + functions->SetLongArrayRegion(this,array,start,len,buf); + } + void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, + const jfloat *buf) { + functions->SetFloatArrayRegion(this,array,start,len,buf); + } + void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, + const jdouble *buf) { + functions->SetDoubleArrayRegion(this,array,start,len,buf); + } + + jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, + jint nMethods) { + return functions->RegisterNatives(this,clazz,methods,nMethods); + } + jint UnregisterNatives(jclass clazz) { + return functions->UnregisterNatives(this,clazz); + } + + jint MonitorEnter(jobject obj) { + return functions->MonitorEnter(this,obj); + } + jint MonitorExit(jobject obj) { + return functions->MonitorExit(this,obj); + } + + jint GetJavaVM(JavaVM **vm) { + return functions->GetJavaVM(this,vm); + } + + void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) { + functions->GetStringRegion(this,str,start,len,buf); + } + void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { + functions->GetStringUTFRegion(this,str,start,len,buf); + } + + void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) { + return functions->GetPrimitiveArrayCritical(this,array,isCopy); + } + void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) { + functions->ReleasePrimitiveArrayCritical(this,array,carray,mode); + } + + const jchar * GetStringCritical(jstring string, jboolean *isCopy) { + return functions->GetStringCritical(this,string,isCopy); + } + void ReleaseStringCritical(jstring string, const jchar *cstring) { + functions->ReleaseStringCritical(this,string,cstring); + } + + jweak NewWeakGlobalRef(jobject obj) { + return functions->NewWeakGlobalRef(this,obj); + } + void DeleteWeakGlobalRef(jweak ref) { + functions->DeleteWeakGlobalRef(this,ref); + } + + jboolean ExceptionCheck() { + return functions->ExceptionCheck(this); + } + + jobject NewDirectByteBuffer(void* address, jlong capacity) { + return functions->NewDirectByteBuffer(this, address, capacity); + } + void* GetDirectBufferAddress(jobject buf) { + return functions->GetDirectBufferAddress(this, buf); + } + jlong GetDirectBufferCapacity(jobject buf) { + return functions->GetDirectBufferCapacity(this, buf); + } + jobjectRefType GetObjectRefType(jobject obj) { + return functions->GetObjectRefType(this, obj); + } + +#endif /* __cplusplus */ +}; + +typedef struct JavaVMOption { + char *optionString; + void *extraInfo; +} JavaVMOption; + +typedef struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption *options; + jboolean ignoreUnrecognized; +} JavaVMInitArgs; + +typedef struct JavaVMAttachArgs { + jint version; + + char *name; + jobject group; +} JavaVMAttachArgs; + +/* These will be VM-specific. */ + +#define JDK1_2 +#define JDK1_4 + +/* End VM-specific. */ + +struct JNIInvokeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + jint (JNICALL *DestroyJavaVM)(JavaVM *vm); + + jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args); + + jint (JNICALL *DetachCurrentThread)(JavaVM *vm); + + jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version); + + jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args); +}; + +struct JavaVM_ { + const struct JNIInvokeInterface_ *functions; +#ifdef __cplusplus + + jint DestroyJavaVM() { + return functions->DestroyJavaVM(this); + } + jint AttachCurrentThread(void **penv, void *args) { + return functions->AttachCurrentThread(this, penv, args); + } + jint DetachCurrentThread() { + return functions->DetachCurrentThread(this); + } + + jint GetEnv(void **penv, jint version) { + return functions->GetEnv(this, penv, version); + } + jint AttachCurrentThreadAsDaemon(void **penv, void *args) { + return functions->AttachCurrentThreadAsDaemon(this, penv, args); + } +#endif +}; + +#ifdef _JNI_IMPLEMENTATION_ +#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT +#else +#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT +#endif +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetDefaultJavaVMInitArgs(void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +/* Defined by native libraries. */ +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved); + +JNIEXPORT void JNICALL +JNI_OnUnload(JavaVM *vm, void *reserved); + +#define JNI_VERSION_1_1 0x00010001 +#define JNI_VERSION_1_2 0x00010002 +#define JNI_VERSION_1_4 0x00010004 +#define JNI_VERSION_1_6 0x00010006 + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVASOFT_JNI_H_ */ diff --git a/hotspot/src/share/vm/prims/jniCheck.cpp b/hotspot/src/share/vm/prims/jniCheck.cpp new file mode 100644 index 00000000000..ed26eabe642 --- /dev/null +++ b/hotspot/src/share/vm/prims/jniCheck.cpp @@ -0,0 +1,2049 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jniCheck.cpp.incl" + + +// Heap objects are allowed to be directly referenced only in VM code, +// not in native code. + +#define ASSERT_OOPS_ALLOWED \ + assert(JavaThread::current()->thread_state() == _thread_in_vm, \ + "jniCheck examining oops in bad state.") + + +// Execute the given block of source code with the thread in VM state. +// To do this, transition from the NATIVE state to the VM state, execute +// the code, and transtition back. The ThreadInVMfromNative constructor +// performs the transition to VM state, its destructor restores the +// NATIVE state. + +#define IN_VM(source_code) { \ + { \ + ThreadInVMfromNative __tiv(thr); \ + source_code \ + } \ + } + + +/* + * DECLARATIONS + */ + +static struct JNINativeInterface_ * unchecked_jni_NativeInterface; + + +/* + * MACRO DEFINITIONS + */ + +// All JNI checked functions here use JNI_ENTRY_CHECKED() instead of the +// QUICK_ENTRY or LEAF variants found in jni.cpp. This allows handles +// to be created if a fatal error should occur. + +// Check for thread not attached to VM; need to catch this before +// assertions in the wrapper routines might fire + +// Check for env being the one value appropriate for this thread. + +#define JNI_ENTRY_CHECKED(result_type, header) \ +extern "C" { \ + result_type JNICALL header { \ + JavaThread* thr = (JavaThread*)ThreadLocalStorage::get_thread_slow();\ + if (thr == NULL || !thr->is_Java_thread()) { \ + tty->print_cr(fatal_using_jnienv_in_nonjava); \ + os::abort(true); \ + } \ + JNIEnv* xenv = thr->jni_environment(); \ + if (env != xenv) { \ + NativeReportJNIFatalError(thr, warn_wrong_jnienv); \ + } \ + __ENTRY(result_type, header, thr) + + +#define UNCHECKED() (unchecked_jni_NativeInterface) + +static const char * warn_wrong_jnienv = "Using JNIEnv in the wrong thread"; +static const char * warn_bad_class_descriptor = "JNI FindClass received a bad class descriptor \"%s\". A correct class descriptor " \ + "has no leading \"L\" or trailing \";\". Incorrect descriptors will not be accepted in future releases."; +static const char * fatal_using_jnienv_in_nonjava = "FATAL ERROR in native method: Using JNIEnv in non-Java thread"; +static const char * warn_other_function_in_critical = "Warning: Calling other JNI functions in the scope of " \ + "Get/ReleasePrimitiveArrayCritical or Get/ReleaseStringCritical"; +static const char * fatal_bad_ref_to_jni = "Bad global or local ref passed to JNI"; +static const char * fatal_received_null_class = "JNI received a null class"; +static const char * fatal_class_not_a_class = "JNI received a class argument that is not a class"; +static const char * fatal_class_not_a_throwable_class = "JNI Throw or ThrowNew received a class argument that is not a Throwable or Throwable subclass"; +static const char * fatal_wrong_class_or_method = "Wrong object class or methodID passed to JNI call"; +static const char * fatal_unknown_array_object = "Unknown array object passed to JNI array operations"; +static const char * fatal_object_array_expected = "Object array expected but not received for JNI array operation"; +static const char * fatal_non_array = "Non-array passed to JNI array operations"; +static const char * fatal_element_type_mismatch = "Array element type mismatch in JNI"; +static const char * fatal_should_be_static = "Non-static field ID passed to JNI"; +static const char * fatal_wrong_static_field = "Wrong static field ID passed to JNI"; +static const char * fatal_static_field_not_found = "Static field not found in JNI get/set field operations"; +static const char * fatal_static_field_mismatch = "Field type (static) mismatch in JNI get/set field operations"; +static const char * fatal_should_be_nonstatic = "Static field ID passed to JNI"; +static const char * fatal_null_object = "Null object passed to JNI"; +static const char * fatal_wrong_field = "Wrong field ID passed to JNI"; +static const char * fatal_instance_field_not_found = "Instance field not found in JNI get/set field operations"; +static const char * fatal_instance_field_mismatch = "Field type (instance) mismatch in JNI get/set field operations"; +static const char * fatal_non_string = "JNI string operation received a non-string"; + + + +// Report a JNI failure caught by -Xcheck:jni. Perform a core dump. +// Note: two variations -- one to be called when in VM state (e.g. when +// within IN_VM macro), one to be called when in NATIVE state. + +// When in VM state: +static void ReportJNIFatalError(JavaThread* thr, const char *msg) { + tty->print_cr("FATAL ERROR in native method: %s", msg); + thr->print_stack(); + os::abort(true); +} + +// When in VM state: +static void ReportJNIWarning(JavaThread* thr, const char *msg) { + tty->print_cr("WARNING in native method: %s", msg); + thr->print_stack(); +} + +// When in NATIVE state: +static void NativeReportJNIFatalError(JavaThread* thr, const char *msg) { + IN_VM( + ReportJNIFatalError(thr, msg); + ) +} + +static void NativeReportJNIWarning(JavaThread* thr, const char *msg) { + IN_VM( + ReportJNIWarning(thr, msg); + ) +} + + + + +/* + * SUPPORT FUNCTIONS + */ + +static inline void +functionEnterCritical(JavaThread* thr) +{ + if (thr->has_pending_exception()) { + NativeReportJNIWarning(thr, "JNI call made with exception pending"); + } +} + +static inline void +functionEnterCriticalExceptionAllowed(JavaThread* thr) +{ +} + +static inline void +functionEnter(JavaThread* thr) +{ + if (thr->in_critical()) { + tty->print_cr(warn_other_function_in_critical); + } + if (thr->has_pending_exception()) { + NativeReportJNIWarning(thr, "JNI call made with exception pending"); + } +} + +static inline void +functionEnterExceptionAllowed(JavaThread* thr) +{ + if (thr->in_critical()) { + tty->print_cr(warn_other_function_in_critical); + } +} + +static inline void +functionExit(JNIEnv *env) +{ + /* nothing to do at this time */ +} + +static inline void +checkStaticFieldID(JavaThread* thr, jfieldID fid, jclass cls, int ftype) +{ + fieldDescriptor fd; + + /* make sure it is a static field */ + if (!jfieldIDWorkaround::is_static_jfieldID(fid)) + ReportJNIFatalError(thr, fatal_should_be_static); + + /* validate the class being passed */ + ASSERT_OOPS_ALLOWED; + klassOop k_oop = jniCheck::validate_class(thr, cls, false); + + /* check for proper subclass hierarchy */ + JNIid* id = jfieldIDWorkaround::from_static_jfieldID(fid); + klassOop f_oop = id->holder(); + if (!instanceKlass::cast(k_oop)->is_subtype_of(f_oop)) + ReportJNIFatalError(thr, fatal_wrong_static_field); + + /* check for proper field type */ + if (!instanceKlass::cast(f_oop)->find_local_field_from_offset( + id->offset(), true, &fd)) + ReportJNIFatalError(thr, fatal_static_field_not_found); + if ((fd.field_type() != ftype) && + !(fd.field_type() == T_ARRAY && ftype == T_OBJECT)) { + ReportJNIFatalError(thr, fatal_static_field_mismatch); + } +} + +static inline void +checkInstanceFieldID(JavaThread* thr, jfieldID fid, jobject obj, int ftype) +{ + fieldDescriptor fd; + + /* make sure it is an instance field */ + if (jfieldIDWorkaround::is_static_jfieldID(fid)) + ReportJNIFatalError(thr, fatal_should_be_nonstatic); + + /* validate the object being passed and then get its class */ + ASSERT_OOPS_ALLOWED; + oop oopObj = jniCheck::validate_object(thr, obj); + if (!oopObj) { + ReportJNIFatalError(thr, fatal_null_object); + } + klassOop k_oop = oopObj->klass(); + + if (!jfieldIDWorkaround::is_valid_jfieldID(k_oop, fid)) { + ReportJNIFatalError(thr, fatal_wrong_field); + } + + /* make sure the field exists */ + int offset = jfieldIDWorkaround::from_instance_jfieldID(k_oop, fid); + if (!instanceKlass::cast(k_oop)->contains_field_offset(offset)) + ReportJNIFatalError(thr, fatal_wrong_field); + + /* check for proper field type */ + if (!instanceKlass::cast(k_oop)->find_field_from_offset(offset, + false, &fd)) + ReportJNIFatalError(thr, fatal_instance_field_not_found); + + if ((fd.field_type() != ftype) && + !(fd.field_type() == T_ARRAY && ftype == T_OBJECT)) { + ReportJNIFatalError(thr, fatal_instance_field_mismatch); + } +} + +static inline void +checkString(JavaThread* thr, jstring js) +{ + ASSERT_OOPS_ALLOWED; + oop s = jniCheck::validate_object(thr, js); + if (!s || !java_lang_String::is_instance(s)) + ReportJNIFatalError(thr, fatal_non_string); +} + +static inline void +checkArray(JavaThread* thr, jarray jArray, int elementType) +{ + ASSERT_OOPS_ALLOWED; + arrayOop aOop; + + aOop = (arrayOop)jniCheck::validate_object(thr, jArray); + if (aOop == NULL || !aOop->is_array()) + ReportJNIFatalError(thr, fatal_non_array); + + if (elementType != -1) { + if (aOop->is_typeArray()) { + BasicType array_type = typeArrayKlass::cast(aOop->klass())->element_type(); + if (array_type != elementType) + ReportJNIFatalError(thr, fatal_element_type_mismatch); + } else if (aOop->is_objArray()) { + if ( T_OBJECT != elementType) + ReportJNIFatalError(thr, fatal_object_array_expected); + } else { + ReportJNIFatalError(thr, fatal_unknown_array_object); + } + } +} + + +oop jniCheck::validate_handle(JavaThread* thr, jobject obj) { + if (JNIHandles::is_frame_handle(thr, obj) || + JNIHandles::is_local_handle(thr, obj) || + JNIHandles::is_global_handle(obj) || + JNIHandles::is_weak_global_handle(obj)) { + ASSERT_OOPS_ALLOWED; + return JNIHandles::resolve_external_guard(obj); + } + ReportJNIFatalError(thr, fatal_bad_ref_to_jni); + return NULL; +} + + +methodOop jniCheck::validate_jmethod_id(JavaThread* thr, jmethodID method_id) { + ASSERT_OOPS_ALLOWED; + methodOop moop = JNIHandles::checked_resolve_jmethod_id(method_id); + if (moop == NULL) { + ReportJNIFatalError(thr, fatal_wrong_class_or_method); + } + return moop; +} + + +oop jniCheck::validate_object(JavaThread* thr, jobject obj) { + if (!obj) + return NULL; + ASSERT_OOPS_ALLOWED; + oop oopObj = jniCheck::validate_handle(thr, obj); + if (!oopObj) { + ReportJNIFatalError(thr, fatal_bad_ref_to_jni); + } + return oopObj; +} + +// Warn if a class descriptor is in decorated form; class descriptors +// passed to JNI findClass should not be decorated unless they are +// array descriptors. +void jniCheck::validate_class_descriptor(JavaThread* thr, const char* name) { + if (name == NULL) return; // implementation accepts NULL so just return + + size_t len = strlen(name); + + if (len >= 2 && + name[0] == JVM_SIGNATURE_CLASS && // 'L' + name[len-1] == JVM_SIGNATURE_ENDCLASS ) { // ';' + char msg[JVM_MAXPATHLEN]; + jio_snprintf(msg, JVM_MAXPATHLEN, warn_bad_class_descriptor, name); + ReportJNIWarning(thr, msg); + } +} + +klassOop jniCheck::validate_class(JavaThread* thr, jclass clazz, bool allow_primitive) { + ASSERT_OOPS_ALLOWED; + oop mirror = jniCheck::validate_handle(thr, clazz); + if (!mirror) { + ReportJNIFatalError(thr, fatal_received_null_class); + } + + if (mirror->klass() != SystemDictionary::class_klass()) { + ReportJNIFatalError(thr, fatal_class_not_a_class); + } + + klassOop k = java_lang_Class::as_klassOop(mirror); + // Make allowances for primitive classes ... + if (!(k != NULL || allow_primitive && java_lang_Class::is_primitive(mirror))) { + ReportJNIFatalError(thr, fatal_class_not_a_class); + } + return k; +} + +void jniCheck::validate_throwable_klass(JavaThread* thr, klassOop klass) { + ASSERT_OOPS_ALLOWED; + assert(klass != NULL, "klass argument must have a value"); + + if (!Klass::cast(klass)->oop_is_instance() || + !instanceKlass::cast(klass)->is_subclass_of(SystemDictionary::throwable_klass())) { + ReportJNIFatalError(thr, fatal_class_not_a_throwable_class); + } +} + +void jniCheck::validate_call_object(JavaThread* thr, jobject obj, jmethodID method_id) { + /* validate the object being passed */ + ASSERT_OOPS_ALLOWED; + jniCheck::validate_jmethod_id(thr, method_id); + jniCheck::validate_object(thr, obj); +} + +void jniCheck::validate_call_class(JavaThread* thr, jclass clazz, jmethodID method_id) { + /* validate the class being passed */ + ASSERT_OOPS_ALLOWED; + jniCheck::validate_jmethod_id(thr, method_id); + jniCheck::validate_class(thr, clazz, false); +} + + +/* + * IMPLEMENTATION OF FUNCTIONS IN CHECKED TABLE + */ + +JNI_ENTRY_CHECKED(jclass, + checked_jni_DefineClass(JNIEnv *env, + const char *name, + jobject loader, + const jbyte *buf, + jsize len)) + functionEnter(thr); + IN_VM( + jniCheck::validate_object(thr, loader); + ) + jclass result = UNCHECKED()->DefineClass(env, name, loader, buf, len); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jclass, + checked_jni_FindClass(JNIEnv *env, + const char *name)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class_descriptor(thr, name); + ) + jclass result = UNCHECKED()->FindClass(env, name); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jmethodID, + checked_jni_FromReflectedMethod(JNIEnv *env, + jobject method)) + functionEnter(thr); + IN_VM( + jniCheck::validate_object(thr, method); + ) + jmethodID result = UNCHECKED()->FromReflectedMethod(env, method); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jfieldID, + checked_jni_FromReflectedField(JNIEnv *env, + jobject field)) + functionEnter(thr); + IN_VM( + jniCheck::validate_object(thr, field); + ) + jfieldID result = UNCHECKED()->FromReflectedField(env, field); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_ToReflectedMethod(JNIEnv *env, + jclass cls, + jmethodID methodID, + jboolean isStatic)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, cls, false); + jniCheck::validate_jmethod_id(thr, methodID); + ) + jobject result = UNCHECKED()->ToReflectedMethod(env, cls, methodID, + isStatic); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jclass, + checked_jni_GetSuperclass(JNIEnv *env, + jclass sub)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, sub, true); + ) + jclass result = UNCHECKED()->GetSuperclass(env, sub); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jboolean, + checked_jni_IsAssignableFrom(JNIEnv *env, + jclass sub, + jclass sup)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, sub, true); + jniCheck::validate_class(thr, sup, true); + ) + jboolean result = UNCHECKED()->IsAssignableFrom(env, sub, sup); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_ToReflectedField(JNIEnv *env, + jclass cls, + jfieldID fieldID, + jboolean isStatic)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, cls, false); + ) + jobject result = UNCHECKED()->ToReflectedField(env, cls, fieldID, + isStatic); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jint, + checked_jni_Throw(JNIEnv *env, + jthrowable obj)) + functionEnter(thr); + IN_VM( + oop oopObj = jniCheck::validate_object(thr, obj); + if (oopObj == NULL) { + // Unchecked Throw tolerates a NULL obj, so just warn + ReportJNIWarning(thr, "JNI Throw called with NULL throwable"); + } else { + jniCheck::validate_throwable_klass(thr, oopObj->klass()); + } + ) + jint result = UNCHECKED()->Throw(env, obj); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jint, + checked_jni_ThrowNew(JNIEnv *env, + jclass clazz, + const char *msg)) + functionEnter(thr); + IN_VM( + klassOop k = jniCheck::validate_class(thr, clazz, false); + assert(k != NULL, "validate_class shouldn't return NULL klassOop"); + jniCheck::validate_throwable_klass(thr, k); + ) + jint result = UNCHECKED()->ThrowNew(env, clazz, msg); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jthrowable, + checked_jni_ExceptionOccurred(JNIEnv *env)) + functionEnterExceptionAllowed(thr); + jthrowable result = UNCHECKED()->ExceptionOccurred(env); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_ExceptionDescribe(JNIEnv *env)) + functionEnterExceptionAllowed(thr); + UNCHECKED()->ExceptionDescribe(env); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_ExceptionClear(JNIEnv *env)) + functionEnterExceptionAllowed(thr); + UNCHECKED()->ExceptionClear(env); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_FatalError(JNIEnv *env, + const char *msg)) + functionEnter(thr); + UNCHECKED()->FatalError(env, msg); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(jint, + checked_jni_PushLocalFrame(JNIEnv *env, + jint capacity)) + functionEnterExceptionAllowed(thr); + if (capacity < 0) + NativeReportJNIFatalError(thr, "negative capacity"); + jint result = UNCHECKED()->PushLocalFrame(env, capacity); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_PopLocalFrame(JNIEnv *env, + jobject result)) + functionEnterExceptionAllowed(thr); + jobject res = UNCHECKED()->PopLocalFrame(env, result); + functionExit(env); + return res; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_NewGlobalRef(JNIEnv *env, + jobject lobj)) + functionEnter(thr); + IN_VM( + if (lobj != NULL) { + jniCheck::validate_handle(thr, lobj); + } + ) + jobject result = UNCHECKED()->NewGlobalRef(env,lobj); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_DeleteGlobalRef(JNIEnv *env, + jobject gref)) + functionEnterExceptionAllowed(thr); + IN_VM( + jniCheck::validate_object(thr, gref); + if (gref && !JNIHandles::is_global_handle(gref)) { + ReportJNIFatalError(thr, + "Invalid global JNI handle passed to DeleteGlobalRef"); + } + ) + UNCHECKED()->DeleteGlobalRef(env,gref); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_DeleteLocalRef(JNIEnv *env, + jobject obj)) + functionEnterExceptionAllowed(thr); + IN_VM( + jniCheck::validate_object(thr, obj); + if (obj && !(JNIHandles::is_local_handle(thr, obj) || + JNIHandles::is_frame_handle(thr, obj))) + ReportJNIFatalError(thr, + "Invalid local JNI handle passed to DeleteLocalRef"); + ) + UNCHECKED()->DeleteLocalRef(env, obj); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(jboolean, + checked_jni_IsSameObject(JNIEnv *env, + jobject obj1, + jobject obj2)) + functionEnterExceptionAllowed(thr); + IN_VM( + /* This JNI function can be used to compare weak global references + * to NULL objects. If the handles are valid, but contain NULL, + * then don't attempt to validate the object. + */ + if (obj1 != NULL && jniCheck::validate_handle(thr, obj1) != NULL) { + jniCheck::validate_object(thr, obj1); + } + if (obj2 != NULL && jniCheck::validate_handle(thr, obj2) != NULL) { + jniCheck::validate_object(thr, obj2); + } + ) + jboolean result = UNCHECKED()->IsSameObject(env,obj1,obj2); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_NewLocalRef(JNIEnv *env, + jobject ref)) + functionEnter(thr); + IN_VM( + if (ref != NULL) { + jniCheck::validate_handle(thr, ref); + } + ) + jobject result = UNCHECKED()->NewLocalRef(env, ref); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jint, + checked_jni_EnsureLocalCapacity(JNIEnv *env, + jint capacity)) + functionEnter(thr); + if (capacity < 0) { + NativeReportJNIFatalError(thr, "negative capacity"); + } + jint result = UNCHECKED()->EnsureLocalCapacity(env, capacity); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_AllocObject(JNIEnv *env, + jclass clazz)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, clazz, false); + ) + jobject result = UNCHECKED()->AllocObject(env,clazz); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_NewObject(JNIEnv *env, + jclass clazz, + jmethodID methodID, + ...)) + functionEnter(thr); + va_list args; + IN_VM( + jniCheck::validate_class(thr, clazz, false); + jniCheck::validate_jmethod_id(thr, methodID); + ) + va_start(args, methodID); + jobject result = UNCHECKED()->NewObjectV(env,clazz,methodID,args); + va_end(args); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_NewObjectV(JNIEnv *env, + jclass clazz, + jmethodID methodID, + va_list args)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, clazz, false); + jniCheck::validate_jmethod_id(thr, methodID); + ) + jobject result = UNCHECKED()->NewObjectV(env,clazz,methodID,args); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_NewObjectA(JNIEnv *env, + jclass clazz, + jmethodID methodID, + const jvalue *args)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, clazz, false); + jniCheck::validate_jmethod_id(thr, methodID); + ) + jobject result = UNCHECKED()->NewObjectA(env,clazz,methodID,args); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jclass, + checked_jni_GetObjectClass(JNIEnv *env, + jobject obj)) + functionEnter(thr); + IN_VM( + jniCheck::validate_object(thr, obj); + ) + jclass result = UNCHECKED()->GetObjectClass(env,obj); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jboolean, + checked_jni_IsInstanceOf(JNIEnv *env, + jobject obj, + jclass clazz)) + functionEnter(thr); + IN_VM( + jniCheck::validate_object(thr, obj); + jniCheck::validate_class(thr, clazz, true); + ) + jboolean result = UNCHECKED()->IsInstanceOf(env,obj,clazz); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jmethodID, + checked_jni_GetMethodID(JNIEnv *env, + jclass clazz, + const char *name, + const char *sig)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, clazz, false); + ) + jmethodID result = UNCHECKED()->GetMethodID(env,clazz,name,sig); + functionExit(env); + return result; +JNI_END + +#define WRAPPER_CallMethod(ResultType, Result) \ +JNI_ENTRY_CHECKED(ResultType, \ + checked_jni_Call##Result##Method(JNIEnv *env, \ + jobject obj, \ + jmethodID methodID, \ + ...)) \ + functionEnter(thr); \ + va_list args; \ + IN_VM( \ + jniCheck::validate_call_object(thr, obj, methodID); \ + ) \ + va_start(args,methodID); \ + ResultType result =UNCHECKED()->Call##Result##MethodV(env, obj, methodID, \ + args); \ + va_end(args); \ + functionExit(env); \ + return result; \ +JNI_END \ +\ +JNI_ENTRY_CHECKED(ResultType, \ + checked_jni_Call##Result##MethodV(JNIEnv *env, \ + jobject obj, \ + jmethodID methodID, \ + va_list args)) \ + functionEnter(thr); \ + IN_VM(\ + jniCheck::validate_call_object(thr, obj, methodID); \ + ) \ + ResultType result = UNCHECKED()->Call##Result##MethodV(env, obj, methodID,\ + args); \ + functionExit(env); \ + return result; \ +JNI_END \ +\ +JNI_ENTRY_CHECKED(ResultType, \ + checked_jni_Call##Result##MethodA(JNIEnv *env, \ + jobject obj, \ + jmethodID methodID, \ + const jvalue * args)) \ + functionEnter(thr); \ + IN_VM( \ + jniCheck::validate_call_object(thr, obj, methodID); \ + ) \ + ResultType result = UNCHECKED()->Call##Result##MethodA(env, obj, methodID,\ + args); \ + functionExit(env); \ + return result; \ +JNI_END + +WRAPPER_CallMethod(jobject,Object) +WRAPPER_CallMethod(jboolean,Boolean) +WRAPPER_CallMethod(jbyte,Byte) +WRAPPER_CallMethod(jshort,Short) +WRAPPER_CallMethod(jchar,Char) +WRAPPER_CallMethod(jint,Int) +WRAPPER_CallMethod(jlong,Long) +WRAPPER_CallMethod(jfloat,Float) +WRAPPER_CallMethod(jdouble,Double) + +JNI_ENTRY_CHECKED(void, + checked_jni_CallVoidMethod(JNIEnv *env, \ + jobject obj, \ + jmethodID methodID, \ + ...)) + functionEnter(thr); + va_list args; + IN_VM( + jniCheck::validate_call_object(thr, obj, methodID); + ) + va_start(args,methodID); + UNCHECKED()->CallVoidMethodV(env,obj,methodID,args); + va_end(args); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_CallVoidMethodV(JNIEnv *env, + jobject obj, + jmethodID methodID, + va_list args)) + functionEnter(thr); + IN_VM( + jniCheck::validate_call_object(thr, obj, methodID); + ) + UNCHECKED()->CallVoidMethodV(env,obj,methodID,args); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_CallVoidMethodA(JNIEnv *env, + jobject obj, + jmethodID methodID, + const jvalue * args)) + functionEnter(thr); + IN_VM( + jniCheck::validate_call_object(thr, obj, methodID); + ) + UNCHECKED()->CallVoidMethodA(env,obj,methodID,args); + functionExit(env); +JNI_END + +#define WRAPPER_CallNonvirtualMethod(ResultType, Result) \ +JNI_ENTRY_CHECKED(ResultType, \ + checked_jni_CallNonvirtual##Result##Method(JNIEnv *env, \ + jobject obj, \ + jclass clazz, \ + jmethodID methodID, \ + ...)) \ + functionEnter(thr); \ + va_list args; \ + IN_VM( \ + jniCheck::validate_call_object(thr, obj, methodID); \ + jniCheck::validate_call_class(thr, clazz, methodID); \ + ) \ + va_start(args,methodID); \ + ResultType result = UNCHECKED()->CallNonvirtual##Result##MethodV(env, \ + obj, \ + clazz, \ + methodID,\ + args); \ + va_end(args); \ + functionExit(env); \ + return result; \ +JNI_END \ +\ +JNI_ENTRY_CHECKED(ResultType, \ + checked_jni_CallNonvirtual##Result##MethodV(JNIEnv *env, \ + jobject obj, \ + jclass clazz, \ + jmethodID methodID, \ + va_list args)) \ + functionEnter(thr); \ + IN_VM( \ + jniCheck::validate_call_object(thr, obj, methodID); \ + jniCheck::validate_call_class(thr, clazz, methodID); \ + ) \ + ResultType result = UNCHECKED()->CallNonvirtual##Result##MethodV(env, \ + obj, \ + clazz, \ + methodID,\ + args); \ + functionExit(env); \ + return result; \ +JNI_END \ +\ +JNI_ENTRY_CHECKED(ResultType, \ + checked_jni_CallNonvirtual##Result##MethodA(JNIEnv *env, \ + jobject obj, \ + jclass clazz, \ + jmethodID methodID, \ + const jvalue * args)) \ + functionEnter(thr); \ + IN_VM( \ + jniCheck::validate_call_object(thr, obj, methodID); \ + jniCheck::validate_call_class(thr, clazz, methodID); \ + ) \ + ResultType result = UNCHECKED()->CallNonvirtual##Result##MethodA(env, \ + obj, \ + clazz, \ + methodID,\ + args); \ + functionExit(env); \ + return result; \ +JNI_END + +WRAPPER_CallNonvirtualMethod(jobject,Object) +WRAPPER_CallNonvirtualMethod(jboolean,Boolean) +WRAPPER_CallNonvirtualMethod(jbyte,Byte) +WRAPPER_CallNonvirtualMethod(jshort,Short) +WRAPPER_CallNonvirtualMethod(jchar,Char) +WRAPPER_CallNonvirtualMethod(jint,Int) +WRAPPER_CallNonvirtualMethod(jlong,Long) +WRAPPER_CallNonvirtualMethod(jfloat,Float) +WRAPPER_CallNonvirtualMethod(jdouble,Double) + +JNI_ENTRY_CHECKED(void, + checked_jni_CallNonvirtualVoidMethod(JNIEnv *env, + jobject obj, + jclass clazz, + jmethodID methodID, + ...)) + functionEnter(thr); + va_list args; + IN_VM( + jniCheck::validate_call_object(thr, obj, methodID); + jniCheck::validate_call_class(thr, clazz, methodID); + ) + va_start(args,methodID); + UNCHECKED()->CallNonvirtualVoidMethodV(env,obj,clazz,methodID,args); + va_end(args); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_CallNonvirtualVoidMethodV(JNIEnv *env, + jobject obj, + jclass clazz, + jmethodID methodID, + va_list args)) + functionEnter(thr); + IN_VM( + jniCheck::validate_call_object(thr, obj, methodID); + jniCheck::validate_call_class(thr, clazz, methodID); + ) + UNCHECKED()->CallNonvirtualVoidMethodV(env,obj,clazz,methodID,args); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_CallNonvirtualVoidMethodA(JNIEnv *env, + jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue * args)) + functionEnter(thr); + IN_VM( + jniCheck::validate_call_object(thr, obj, methodID); + jniCheck::validate_call_class(thr, clazz, methodID); + ) + UNCHECKED()->CallNonvirtualVoidMethodA(env,obj,clazz,methodID,args); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(jfieldID, + checked_jni_GetFieldID(JNIEnv *env, + jclass clazz, + const char *name, + const char *sig)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, clazz, false); + ) + jfieldID result = UNCHECKED()->GetFieldID(env,clazz,name,sig); + functionExit(env); + return result; +JNI_END + +#define WRAPPER_GetField(ReturnType,Result,FieldType) \ +JNI_ENTRY_CHECKED(ReturnType, \ + checked_jni_Get##Result##Field(JNIEnv *env, \ + jobject obj, \ + jfieldID fieldID)) \ + functionEnter(thr); \ + IN_VM( \ + checkInstanceFieldID(thr, fieldID, obj, FieldType); \ + ) \ + ReturnType result = UNCHECKED()->Get##Result##Field(env,obj,fieldID); \ + functionExit(env); \ + return result; \ +JNI_END + +WRAPPER_GetField(jobject, Object, T_OBJECT) +WRAPPER_GetField(jboolean, Boolean, T_BOOLEAN) +WRAPPER_GetField(jbyte, Byte, T_BYTE) +WRAPPER_GetField(jshort, Short, T_SHORT) +WRAPPER_GetField(jchar, Char, T_CHAR) +WRAPPER_GetField(jint, Int, T_INT) +WRAPPER_GetField(jlong, Long, T_LONG) +WRAPPER_GetField(jfloat, Float, T_FLOAT) +WRAPPER_GetField(jdouble, Double, T_DOUBLE) + +#define WRAPPER_SetField(ValueType,Result,FieldType) \ +JNI_ENTRY_CHECKED(void, \ + checked_jni_Set##Result##Field(JNIEnv *env, \ + jobject obj, \ + jfieldID fieldID, \ + ValueType val)) \ + functionEnter(thr); \ + IN_VM( \ + checkInstanceFieldID(thr, fieldID, obj, FieldType); \ + ) \ + UNCHECKED()->Set##Result##Field(env,obj,fieldID,val); \ + functionExit(env); \ +JNI_END + +WRAPPER_SetField(jobject, Object, T_OBJECT) +WRAPPER_SetField(jboolean, Boolean, T_BOOLEAN) +WRAPPER_SetField(jbyte, Byte, T_BYTE) +WRAPPER_SetField(jshort, Short, T_SHORT) +WRAPPER_SetField(jchar, Char, T_CHAR) +WRAPPER_SetField(jint, Int, T_INT) +WRAPPER_SetField(jlong, Long, T_LONG) +WRAPPER_SetField(jfloat, Float, T_FLOAT) +WRAPPER_SetField(jdouble, Double, T_DOUBLE) + + +JNI_ENTRY_CHECKED(jmethodID, + checked_jni_GetStaticMethodID(JNIEnv *env, + jclass clazz, + const char *name, + const char *sig)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, clazz, false); + ) + jmethodID result = UNCHECKED()->GetStaticMethodID(env,clazz,name,sig); + functionExit(env); + return result; +JNI_END + +#define WRAPPER_CallStaticMethod(ReturnType,Result) \ +JNI_ENTRY_CHECKED(ReturnType, \ + checked_jni_CallStatic##Result##Method(JNIEnv *env, \ + jclass clazz, \ + jmethodID methodID, \ + ...)) \ + functionEnter(thr); \ + va_list args; \ + IN_VM( \ + jniCheck::validate_jmethod_id(thr, methodID); \ + jniCheck::validate_class(thr, clazz, false); \ + ) \ + va_start(args,methodID); \ + ReturnType result = UNCHECKED()->CallStatic##Result##MethodV(env, \ + clazz, \ + methodID, \ + args); \ + va_end(args); \ + functionExit(env); \ + return result; \ +JNI_END \ +\ +JNI_ENTRY_CHECKED(ReturnType, \ + checked_jni_CallStatic##Result##MethodV(JNIEnv *env, \ + jclass clazz, \ + jmethodID methodID,\ + va_list args)) \ + functionEnter(thr); \ + IN_VM( \ + jniCheck::validate_jmethod_id(thr, methodID); \ + jniCheck::validate_class(thr, clazz, false); \ + ) \ + ReturnType result = UNCHECKED()->CallStatic##Result##MethodV(env, \ + clazz, \ + methodID, \ + args); \ + functionExit(env); \ + return result; \ +JNI_END \ +\ +JNI_ENTRY_CHECKED(ReturnType, \ + checked_jni_CallStatic##Result##MethodA(JNIEnv *env, \ + jclass clazz, \ + jmethodID methodID, \ + const jvalue *args)) \ + functionEnter(thr); \ + IN_VM( \ + jniCheck::validate_jmethod_id(thr, methodID); \ + jniCheck::validate_class(thr, clazz, false); \ + ) \ + ReturnType result = UNCHECKED()->CallStatic##Result##MethodA(env, \ + clazz, \ + methodID, \ + args); \ + functionExit(env); \ + return result; \ +JNI_END + +WRAPPER_CallStaticMethod(jobject,Object) +WRAPPER_CallStaticMethod(jboolean,Boolean) +WRAPPER_CallStaticMethod(jbyte,Byte) +WRAPPER_CallStaticMethod(jshort,Short) +WRAPPER_CallStaticMethod(jchar,Char) +WRAPPER_CallStaticMethod(jint,Int) +WRAPPER_CallStaticMethod(jlong,Long) +WRAPPER_CallStaticMethod(jfloat,Float) +WRAPPER_CallStaticMethod(jdouble,Double) + +JNI_ENTRY_CHECKED(void, + checked_jni_CallStaticVoidMethod(JNIEnv *env, + jclass cls, + jmethodID methodID, + ...)) + functionEnter(thr); + va_list args; + IN_VM( + jniCheck::validate_jmethod_id(thr, methodID); + jniCheck::validate_class(thr, cls, false); + ) + va_start(args,methodID); + UNCHECKED()->CallStaticVoidMethodV(env,cls,methodID,args); + va_end(args); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_CallStaticVoidMethodV(JNIEnv *env, + jclass cls, + jmethodID methodID, + va_list args)) + functionEnter(thr); + IN_VM( + jniCheck::validate_jmethod_id(thr, methodID); + jniCheck::validate_class(thr, cls, false); + ) + UNCHECKED()->CallStaticVoidMethodV(env,cls,methodID,args); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_CallStaticVoidMethodA(JNIEnv *env, + jclass cls, + jmethodID methodID, + const jvalue * args)) + functionEnter(thr); + IN_VM( + jniCheck::validate_jmethod_id(thr, methodID); + jniCheck::validate_class(thr, cls, false); + ) + UNCHECKED()->CallStaticVoidMethodA(env,cls,methodID,args); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(jfieldID, + checked_jni_GetStaticFieldID(JNIEnv *env, + jclass clazz, + const char *name, + const char *sig)) + functionEnter(thr); + IN_VM( + jniCheck::validate_class(thr, clazz, false); + ) + jfieldID result = UNCHECKED()->GetStaticFieldID(env,clazz,name,sig); + functionExit(env); + return result; +JNI_END + +#define WRAPPER_GetStaticField(ReturnType,Result,FieldType) \ +JNI_ENTRY_CHECKED(ReturnType, \ + checked_jni_GetStatic##Result##Field(JNIEnv *env, \ + jclass clazz, \ + jfieldID fieldID)) \ + functionEnter(thr); \ + IN_VM( \ + jniCheck::validate_class(thr, clazz, false); \ + checkStaticFieldID(thr, fieldID, clazz, FieldType); \ + ) \ + ReturnType result = UNCHECKED()->GetStatic##Result##Field(env, \ + clazz, \ + fieldID); \ + functionExit(env); \ + return result; \ +JNI_END + +WRAPPER_GetStaticField(jobject, Object, T_OBJECT) +WRAPPER_GetStaticField(jboolean, Boolean, T_BOOLEAN) +WRAPPER_GetStaticField(jbyte, Byte, T_BYTE) +WRAPPER_GetStaticField(jshort, Short, T_SHORT) +WRAPPER_GetStaticField(jchar, Char, T_CHAR) +WRAPPER_GetStaticField(jint, Int, T_INT) +WRAPPER_GetStaticField(jlong, Long, T_LONG) +WRAPPER_GetStaticField(jfloat, Float, T_FLOAT) +WRAPPER_GetStaticField(jdouble, Double, T_DOUBLE) + +#define WRAPPER_SetStaticField(ValueType,Result,FieldType) \ +JNI_ENTRY_CHECKED(void, \ + checked_jni_SetStatic##Result##Field(JNIEnv *env, \ + jclass clazz, \ + jfieldID fieldID, \ + ValueType value)) \ + functionEnter(thr); \ + IN_VM( \ + jniCheck::validate_class(thr, clazz, false); \ + checkStaticFieldID(thr, fieldID, clazz, FieldType); \ + ) \ + UNCHECKED()->SetStatic##Result##Field(env,clazz,fieldID,value); \ + functionExit(env); \ +JNI_END + +WRAPPER_SetStaticField(jobject, Object, T_OBJECT) +WRAPPER_SetStaticField(jboolean, Boolean, T_BOOLEAN) +WRAPPER_SetStaticField(jbyte, Byte, T_BYTE) +WRAPPER_SetStaticField(jshort, Short, T_SHORT) +WRAPPER_SetStaticField(jchar, Char, T_CHAR) +WRAPPER_SetStaticField(jint, Int, T_INT) +WRAPPER_SetStaticField(jlong, Long, T_LONG) +WRAPPER_SetStaticField(jfloat, Float, T_FLOAT) +WRAPPER_SetStaticField(jdouble, Double, T_DOUBLE) + + +JNI_ENTRY_CHECKED(jstring, + checked_jni_NewString(JNIEnv *env, + const jchar *unicode, + jsize len)) + functionEnter(thr); + jstring result = UNCHECKED()->NewString(env,unicode,len); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jsize, + checked_jni_GetStringLength(JNIEnv *env, + jstring str)) + functionEnter(thr); + IN_VM( + checkString(thr, str); + ) + jsize result = UNCHECKED()->GetStringLength(env,str); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(const jchar *, + checked_jni_GetStringChars(JNIEnv *env, + jstring str, + jboolean *isCopy)) + functionEnter(thr); + IN_VM( + checkString(thr, str); + ) + const jchar *result = UNCHECKED()->GetStringChars(env,str,isCopy); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_ReleaseStringChars(JNIEnv *env, + jstring str, + const jchar *chars)) + functionEnterExceptionAllowed(thr); + IN_VM( + checkString(thr, str); + ) + /* cannot check validity of copy, unless every request is logged by + * checking code. Implementation of this check is deferred until a + * subsequent release. + */ + UNCHECKED()->ReleaseStringChars(env,str,chars); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(jstring, + checked_jni_NewStringUTF(JNIEnv *env, + const char *utf)) + functionEnter(thr); + jstring result = UNCHECKED()->NewStringUTF(env,utf); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jsize, + checked_jni_GetStringUTFLength(JNIEnv *env, + jstring str)) + functionEnter(thr); + IN_VM( + checkString(thr, str); + ) + jsize result = UNCHECKED()->GetStringUTFLength(env,str); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(const char *, + checked_jni_GetStringUTFChars(JNIEnv *env, + jstring str, + jboolean *isCopy)) + functionEnter(thr); + IN_VM( + checkString(thr, str); + ) + const char *result = UNCHECKED()->GetStringUTFChars(env,str,isCopy); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_ReleaseStringUTFChars(JNIEnv *env, + jstring str, + const char* chars)) + functionEnterExceptionAllowed(thr); + IN_VM( + checkString(thr, str); + ) + /* cannot check validity of copy, unless every request is logged by + * checking code. Implementation of this check is deferred until a + * subsequent release. + */ + UNCHECKED()->ReleaseStringUTFChars(env,str,chars); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(jsize, + checked_jni_GetArrayLength(JNIEnv *env, + jarray array)) + functionEnter(thr); + IN_VM( + checkArray(thr, array, -1); + ) + jsize result = UNCHECKED()->GetArrayLength(env,array); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobjectArray, + checked_jni_NewObjectArray(JNIEnv *env, + jsize len, + jclass clazz, + jobject init)) + functionEnter(thr); + jobjectArray result = UNCHECKED()->NewObjectArray(env,len,clazz,init); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_GetObjectArrayElement(JNIEnv *env, + jobjectArray array, + jsize index)) + functionEnter(thr); + IN_VM( + checkArray(thr, array, T_OBJECT); + ) + jobject result = UNCHECKED()->GetObjectArrayElement(env,array,index); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_SetObjectArrayElement(JNIEnv *env, + jobjectArray array, + jsize index, + jobject val)) + functionEnter(thr); + IN_VM( + checkArray(thr, array, T_OBJECT); + ) + UNCHECKED()->SetObjectArrayElement(env,array,index,val); + functionExit(env); +JNI_END + +#define WRAPPER_NewScalarArray(Return, Result) \ +JNI_ENTRY_CHECKED(Return, \ + checked_jni_New##Result##Array(JNIEnv *env, \ + jsize len)) \ + functionEnter(thr); \ + Return result = UNCHECKED()->New##Result##Array(env,len); \ + functionExit(env); \ + return (Return) result; \ +JNI_END + +WRAPPER_NewScalarArray(jbooleanArray, Boolean) +WRAPPER_NewScalarArray(jbyteArray, Byte) +WRAPPER_NewScalarArray(jshortArray, Short) +WRAPPER_NewScalarArray(jcharArray, Char) +WRAPPER_NewScalarArray(jintArray, Int) +WRAPPER_NewScalarArray(jlongArray, Long) +WRAPPER_NewScalarArray(jfloatArray, Float) +WRAPPER_NewScalarArray(jdoubleArray, Double) + +#define WRAPPER_GetScalarArrayElements(ElementTag,ElementType,Result) \ +JNI_ENTRY_CHECKED(ElementType *, \ + checked_jni_Get##Result##ArrayElements(JNIEnv *env, \ + ElementType##Array array, \ + jboolean *isCopy)) \ + functionEnter(thr); \ + IN_VM( \ + checkArray(thr, array, ElementTag); \ + ) \ + ElementType *result = UNCHECKED()->Get##Result##ArrayElements(env, \ + array, \ + isCopy); \ + functionExit(env); \ + return result; \ +JNI_END + +WRAPPER_GetScalarArrayElements(T_BOOLEAN, jboolean, Boolean) +WRAPPER_GetScalarArrayElements(T_BYTE, jbyte, Byte) +WRAPPER_GetScalarArrayElements(T_SHORT, jshort, Short) +WRAPPER_GetScalarArrayElements(T_CHAR, jchar, Char) +WRAPPER_GetScalarArrayElements(T_INT, jint, Int) +WRAPPER_GetScalarArrayElements(T_LONG, jlong, Long) +WRAPPER_GetScalarArrayElements(T_FLOAT, jfloat, Float) +WRAPPER_GetScalarArrayElements(T_DOUBLE, jdouble, Double) + +#define WRAPPER_ReleaseScalarArrayElements(ElementTag,ElementType,Result,Tag) \ +JNI_ENTRY_CHECKED(void, \ + checked_jni_Release##Result##ArrayElements(JNIEnv *env, \ + ElementType##Array array, \ + ElementType *elems, \ + jint mode)) \ + functionEnterExceptionAllowed(thr); \ + IN_VM( \ + checkArray(thr, array, ElementTag); \ + ASSERT_OOPS_ALLOWED; \ + typeArrayOop a = typeArrayOop(JNIHandles::resolve_non_null(array)); \ + /* cannot check validity of copy, unless every request is logged by + * checking code. Implementation of this check is deferred until a + * subsequent release. + */ \ + ) \ + UNCHECKED()->Release##Result##ArrayElements(env,array,elems,mode); \ + functionExit(env); \ +JNI_END + +WRAPPER_ReleaseScalarArrayElements(T_BOOLEAN,jboolean, Boolean, bool) +WRAPPER_ReleaseScalarArrayElements(T_BYTE, jbyte, Byte, byte) +WRAPPER_ReleaseScalarArrayElements(T_SHORT, jshort, Short, short) +WRAPPER_ReleaseScalarArrayElements(T_CHAR, jchar, Char, char) +WRAPPER_ReleaseScalarArrayElements(T_INT, jint, Int, int) +WRAPPER_ReleaseScalarArrayElements(T_LONG, jlong, Long, long) +WRAPPER_ReleaseScalarArrayElements(T_FLOAT, jfloat, Float, float) +WRAPPER_ReleaseScalarArrayElements(T_DOUBLE, jdouble, Double, double) + +#define WRAPPER_GetScalarArrayRegion(ElementTag,ElementType,Result) \ +JNI_ENTRY_CHECKED(void, \ + checked_jni_Get##Result##ArrayRegion(JNIEnv *env, \ + ElementType##Array array, \ + jsize start, \ + jsize len, \ + ElementType *buf)) \ + functionEnter(thr); \ + IN_VM( \ + checkArray(thr, array, ElementTag); \ + ) \ + UNCHECKED()->Get##Result##ArrayRegion(env,array,start,len,buf); \ + functionExit(env); \ +JNI_END + +WRAPPER_GetScalarArrayRegion(T_BOOLEAN, jboolean, Boolean) +WRAPPER_GetScalarArrayRegion(T_BYTE, jbyte, Byte) +WRAPPER_GetScalarArrayRegion(T_SHORT, jshort, Short) +WRAPPER_GetScalarArrayRegion(T_CHAR, jchar, Char) +WRAPPER_GetScalarArrayRegion(T_INT, jint, Int) +WRAPPER_GetScalarArrayRegion(T_LONG, jlong, Long) +WRAPPER_GetScalarArrayRegion(T_FLOAT, jfloat, Float) +WRAPPER_GetScalarArrayRegion(T_DOUBLE, jdouble, Double) + +#define WRAPPER_SetScalarArrayRegion(ElementTag,ElementType,Result) \ +JNI_ENTRY_CHECKED(void, \ + checked_jni_Set##Result##ArrayRegion(JNIEnv *env, \ + ElementType##Array array, \ + jsize start, \ + jsize len, \ + const ElementType *buf)) \ + functionEnter(thr); \ + IN_VM( \ + checkArray(thr, array, ElementTag); \ + ) \ + UNCHECKED()->Set##Result##ArrayRegion(env,array,start,len,buf); \ + functionExit(env); \ +JNI_END + +WRAPPER_SetScalarArrayRegion(T_BOOLEAN, jboolean, Boolean) +WRAPPER_SetScalarArrayRegion(T_BYTE, jbyte, Byte) +WRAPPER_SetScalarArrayRegion(T_SHORT, jshort, Short) +WRAPPER_SetScalarArrayRegion(T_CHAR, jchar, Char) +WRAPPER_SetScalarArrayRegion(T_INT, jint, Int) +WRAPPER_SetScalarArrayRegion(T_LONG, jlong, Long) +WRAPPER_SetScalarArrayRegion(T_FLOAT, jfloat, Float) +WRAPPER_SetScalarArrayRegion(T_DOUBLE, jdouble, Double) + +JNI_ENTRY_CHECKED(jint, + checked_jni_RegisterNatives(JNIEnv *env, + jclass clazz, + const JNINativeMethod *methods, + jint nMethods)) + functionEnter(thr); + jint result = UNCHECKED()->RegisterNatives(env,clazz,methods,nMethods); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jint, + checked_jni_UnregisterNatives(JNIEnv *env, + jclass clazz)) + functionEnter(thr); + jint result = UNCHECKED()->UnregisterNatives(env,clazz); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jint, + checked_jni_MonitorEnter(JNIEnv *env, + jobject obj)) + functionEnter(thr); + IN_VM( + jniCheck::validate_object(thr, obj); + ) + jint result = UNCHECKED()->MonitorEnter(env,obj); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jint, + checked_jni_MonitorExit(JNIEnv *env, + jobject obj)) + functionEnterExceptionAllowed(thr); + IN_VM( + jniCheck::validate_object(thr, obj); + ) + jint result = UNCHECKED()->MonitorExit(env,obj); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jint, + checked_jni_GetJavaVM(JNIEnv *env, + JavaVM **vm)) + functionEnter(thr); + jint result = UNCHECKED()->GetJavaVM(env,vm); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_GetStringRegion(JNIEnv *env, + jstring str, + jsize start, + jsize len, + jchar *buf)) + functionEnter(thr); + IN_VM( + checkString(thr, str); + ) + UNCHECKED()->GetStringRegion(env, str, start, len, buf); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_GetStringUTFRegion(JNIEnv *env, + jstring str, + jsize start, + jsize len, + char *buf)) + functionEnter(thr); + IN_VM( + checkString(thr, str); + ) + UNCHECKED()->GetStringUTFRegion(env, str, start, len, buf); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(void *, + checked_jni_GetPrimitiveArrayCritical(JNIEnv *env, + jarray array, + jboolean *isCopy)) + functionEnterCritical(thr); + IN_VM( + checkArray(thr, array, -1); + ) + void *result = UNCHECKED()->GetPrimitiveArrayCritical(env, array, isCopy); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_ReleasePrimitiveArrayCritical(JNIEnv *env, + jarray array, + void *carray, + jint mode)) + functionEnterCriticalExceptionAllowed(thr); + IN_VM( + checkArray(thr, array, -1); + ) + /* The Hotspot JNI code does not use the parameters, so just check the + * array parameter as a minor sanity check + */ + UNCHECKED()->ReleasePrimitiveArrayCritical(env, array, carray, mode); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(const jchar*, + checked_jni_GetStringCritical(JNIEnv *env, + jstring string, + jboolean *isCopy)) + functionEnterCritical(thr); + IN_VM( + checkString(thr, string); + ) + const jchar *result = UNCHECKED()->GetStringCritical(env, string, isCopy); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_ReleaseStringCritical(JNIEnv *env, + jstring str, + const jchar *chars)) + functionEnterCriticalExceptionAllowed(thr); + IN_VM( + checkString(thr, str); + ) + /* The Hotspot JNI code does not use the parameters, so just check the + * string parameter as a minor sanity check + */ + UNCHECKED()->ReleaseStringCritical(env, str, chars); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(jweak, + checked_jni_NewWeakGlobalRef(JNIEnv *env, + jobject obj)) + functionEnter(thr); + IN_VM( + if (obj != NULL) { + jniCheck::validate_handle(thr, obj); + } + ) + jweak result = UNCHECKED()->NewWeakGlobalRef(env, obj); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_DeleteWeakGlobalRef(JNIEnv *env, + jweak ref)) + functionEnterExceptionAllowed(thr); + UNCHECKED()->DeleteWeakGlobalRef(env, ref); + functionExit(env); +JNI_END + +JNI_ENTRY_CHECKED(jboolean, + checked_jni_ExceptionCheck(JNIEnv *env)) + functionEnterExceptionAllowed(thr); + jboolean result = UNCHECKED()->ExceptionCheck(env); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_NewDirectByteBuffer(JNIEnv *env, + void *address, + jlong capacity)) + functionEnter(thr); + jobject result = UNCHECKED()->NewDirectByteBuffer(env, address, capacity); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(void *, + checked_jni_GetDirectBufferAddress(JNIEnv *env, + jobject buf)) + functionEnter(thr); + void* result = UNCHECKED()->GetDirectBufferAddress(env, buf); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jlong, + checked_jni_GetDirectBufferCapacity(JNIEnv *env, + jobject buf)) + functionEnter(thr); + jlong result = UNCHECKED()->GetDirectBufferCapacity(env, buf); + functionExit(env); + return result; +JNI_END + +JNI_ENTRY_CHECKED(jobjectRefType, + checked_jni_GetObjectRefType(JNIEnv *env, + jobject obj)) + functionEnter(thr); + /* validate the object being passed */ + IN_VM( + jniCheck::validate_object(thr, obj); + ) + jobjectRefType result = UNCHECKED()->GetObjectRefType(env, obj); + functionExit(env); + return result; +JNI_END + + +JNI_ENTRY_CHECKED(jint, + checked_jni_GetVersion(JNIEnv *env)) + functionEnter(thr); + jint result = UNCHECKED()->GetVersion(env); + functionExit(env); + return result; +JNI_END + + + +/* + * Structure containing all checked jni functions + */ +struct JNINativeInterface_ checked_jni_NativeInterface = { + NULL, + NULL, + NULL, + + NULL, + + checked_jni_GetVersion, + + checked_jni_DefineClass, + checked_jni_FindClass, + + checked_jni_FromReflectedMethod, + checked_jni_FromReflectedField, + + checked_jni_ToReflectedMethod, + + checked_jni_GetSuperclass, + checked_jni_IsAssignableFrom, + + checked_jni_ToReflectedField, + + checked_jni_Throw, + checked_jni_ThrowNew, + checked_jni_ExceptionOccurred, + checked_jni_ExceptionDescribe, + checked_jni_ExceptionClear, + checked_jni_FatalError, + + checked_jni_PushLocalFrame, + checked_jni_PopLocalFrame, + + checked_jni_NewGlobalRef, + checked_jni_DeleteGlobalRef, + checked_jni_DeleteLocalRef, + checked_jni_IsSameObject, + + checked_jni_NewLocalRef, + checked_jni_EnsureLocalCapacity, + + checked_jni_AllocObject, + checked_jni_NewObject, + checked_jni_NewObjectV, + checked_jni_NewObjectA, + + checked_jni_GetObjectClass, + checked_jni_IsInstanceOf, + + checked_jni_GetMethodID, + + checked_jni_CallObjectMethod, + checked_jni_CallObjectMethodV, + checked_jni_CallObjectMethodA, + checked_jni_CallBooleanMethod, + checked_jni_CallBooleanMethodV, + checked_jni_CallBooleanMethodA, + checked_jni_CallByteMethod, + checked_jni_CallByteMethodV, + checked_jni_CallByteMethodA, + checked_jni_CallCharMethod, + checked_jni_CallCharMethodV, + checked_jni_CallCharMethodA, + checked_jni_CallShortMethod, + checked_jni_CallShortMethodV, + checked_jni_CallShortMethodA, + checked_jni_CallIntMethod, + checked_jni_CallIntMethodV, + checked_jni_CallIntMethodA, + checked_jni_CallLongMethod, + checked_jni_CallLongMethodV, + checked_jni_CallLongMethodA, + checked_jni_CallFloatMethod, + checked_jni_CallFloatMethodV, + checked_jni_CallFloatMethodA, + checked_jni_CallDoubleMethod, + checked_jni_CallDoubleMethodV, + checked_jni_CallDoubleMethodA, + checked_jni_CallVoidMethod, + checked_jni_CallVoidMethodV, + checked_jni_CallVoidMethodA, + + checked_jni_CallNonvirtualObjectMethod, + checked_jni_CallNonvirtualObjectMethodV, + checked_jni_CallNonvirtualObjectMethodA, + checked_jni_CallNonvirtualBooleanMethod, + checked_jni_CallNonvirtualBooleanMethodV, + checked_jni_CallNonvirtualBooleanMethodA, + checked_jni_CallNonvirtualByteMethod, + checked_jni_CallNonvirtualByteMethodV, + checked_jni_CallNonvirtualByteMethodA, + checked_jni_CallNonvirtualCharMethod, + checked_jni_CallNonvirtualCharMethodV, + checked_jni_CallNonvirtualCharMethodA, + checked_jni_CallNonvirtualShortMethod, + checked_jni_CallNonvirtualShortMethodV, + checked_jni_CallNonvirtualShortMethodA, + checked_jni_CallNonvirtualIntMethod, + checked_jni_CallNonvirtualIntMethodV, + checked_jni_CallNonvirtualIntMethodA, + checked_jni_CallNonvirtualLongMethod, + checked_jni_CallNonvirtualLongMethodV, + checked_jni_CallNonvirtualLongMethodA, + checked_jni_CallNonvirtualFloatMethod, + checked_jni_CallNonvirtualFloatMethodV, + checked_jni_CallNonvirtualFloatMethodA, + checked_jni_CallNonvirtualDoubleMethod, + checked_jni_CallNonvirtualDoubleMethodV, + checked_jni_CallNonvirtualDoubleMethodA, + checked_jni_CallNonvirtualVoidMethod, + checked_jni_CallNonvirtualVoidMethodV, + checked_jni_CallNonvirtualVoidMethodA, + + checked_jni_GetFieldID, + + checked_jni_GetObjectField, + checked_jni_GetBooleanField, + checked_jni_GetByteField, + checked_jni_GetCharField, + checked_jni_GetShortField, + checked_jni_GetIntField, + checked_jni_GetLongField, + checked_jni_GetFloatField, + checked_jni_GetDoubleField, + + checked_jni_SetObjectField, + checked_jni_SetBooleanField, + checked_jni_SetByteField, + checked_jni_SetCharField, + checked_jni_SetShortField, + checked_jni_SetIntField, + checked_jni_SetLongField, + checked_jni_SetFloatField, + checked_jni_SetDoubleField, + + checked_jni_GetStaticMethodID, + + checked_jni_CallStaticObjectMethod, + checked_jni_CallStaticObjectMethodV, + checked_jni_CallStaticObjectMethodA, + checked_jni_CallStaticBooleanMethod, + checked_jni_CallStaticBooleanMethodV, + checked_jni_CallStaticBooleanMethodA, + checked_jni_CallStaticByteMethod, + checked_jni_CallStaticByteMethodV, + checked_jni_CallStaticByteMethodA, + checked_jni_CallStaticCharMethod, + checked_jni_CallStaticCharMethodV, + checked_jni_CallStaticCharMethodA, + checked_jni_CallStaticShortMethod, + checked_jni_CallStaticShortMethodV, + checked_jni_CallStaticShortMethodA, + checked_jni_CallStaticIntMethod, + checked_jni_CallStaticIntMethodV, + checked_jni_CallStaticIntMethodA, + checked_jni_CallStaticLongMethod, + checked_jni_CallStaticLongMethodV, + checked_jni_CallStaticLongMethodA, + checked_jni_CallStaticFloatMethod, + checked_jni_CallStaticFloatMethodV, + checked_jni_CallStaticFloatMethodA, + checked_jni_CallStaticDoubleMethod, + checked_jni_CallStaticDoubleMethodV, + checked_jni_CallStaticDoubleMethodA, + checked_jni_CallStaticVoidMethod, + checked_jni_CallStaticVoidMethodV, + checked_jni_CallStaticVoidMethodA, + + checked_jni_GetStaticFieldID, + + checked_jni_GetStaticObjectField, + checked_jni_GetStaticBooleanField, + checked_jni_GetStaticByteField, + checked_jni_GetStaticCharField, + checked_jni_GetStaticShortField, + checked_jni_GetStaticIntField, + checked_jni_GetStaticLongField, + checked_jni_GetStaticFloatField, + checked_jni_GetStaticDoubleField, + + checked_jni_SetStaticObjectField, + checked_jni_SetStaticBooleanField, + checked_jni_SetStaticByteField, + checked_jni_SetStaticCharField, + checked_jni_SetStaticShortField, + checked_jni_SetStaticIntField, + checked_jni_SetStaticLongField, + checked_jni_SetStaticFloatField, + checked_jni_SetStaticDoubleField, + + checked_jni_NewString, + checked_jni_GetStringLength, + checked_jni_GetStringChars, + checked_jni_ReleaseStringChars, + + checked_jni_NewStringUTF, + checked_jni_GetStringUTFLength, + checked_jni_GetStringUTFChars, + checked_jni_ReleaseStringUTFChars, + + checked_jni_GetArrayLength, + + checked_jni_NewObjectArray, + checked_jni_GetObjectArrayElement, + checked_jni_SetObjectArrayElement, + + checked_jni_NewBooleanArray, + checked_jni_NewByteArray, + checked_jni_NewCharArray, + checked_jni_NewShortArray, + checked_jni_NewIntArray, + checked_jni_NewLongArray, + checked_jni_NewFloatArray, + checked_jni_NewDoubleArray, + + checked_jni_GetBooleanArrayElements, + checked_jni_GetByteArrayElements, + checked_jni_GetCharArrayElements, + checked_jni_GetShortArrayElements, + checked_jni_GetIntArrayElements, + checked_jni_GetLongArrayElements, + checked_jni_GetFloatArrayElements, + checked_jni_GetDoubleArrayElements, + + checked_jni_ReleaseBooleanArrayElements, + checked_jni_ReleaseByteArrayElements, + checked_jni_ReleaseCharArrayElements, + checked_jni_ReleaseShortArrayElements, + checked_jni_ReleaseIntArrayElements, + checked_jni_ReleaseLongArrayElements, + checked_jni_ReleaseFloatArrayElements, + checked_jni_ReleaseDoubleArrayElements, + + checked_jni_GetBooleanArrayRegion, + checked_jni_GetByteArrayRegion, + checked_jni_GetCharArrayRegion, + checked_jni_GetShortArrayRegion, + checked_jni_GetIntArrayRegion, + checked_jni_GetLongArrayRegion, + checked_jni_GetFloatArrayRegion, + checked_jni_GetDoubleArrayRegion, + + checked_jni_SetBooleanArrayRegion, + checked_jni_SetByteArrayRegion, + checked_jni_SetCharArrayRegion, + checked_jni_SetShortArrayRegion, + checked_jni_SetIntArrayRegion, + checked_jni_SetLongArrayRegion, + checked_jni_SetFloatArrayRegion, + checked_jni_SetDoubleArrayRegion, + + checked_jni_RegisterNatives, + checked_jni_UnregisterNatives, + + checked_jni_MonitorEnter, + checked_jni_MonitorExit, + + checked_jni_GetJavaVM, + + checked_jni_GetStringRegion, + checked_jni_GetStringUTFRegion, + + checked_jni_GetPrimitiveArrayCritical, + checked_jni_ReleasePrimitiveArrayCritical, + + checked_jni_GetStringCritical, + checked_jni_ReleaseStringCritical, + + checked_jni_NewWeakGlobalRef, + checked_jni_DeleteWeakGlobalRef, + + checked_jni_ExceptionCheck, + + checked_jni_NewDirectByteBuffer, + checked_jni_GetDirectBufferAddress, + checked_jni_GetDirectBufferCapacity, + + // New 1.6 Features + + checked_jni_GetObjectRefType +}; + + +// Returns the function structure +struct JNINativeInterface_* jni_functions_check() { + + unchecked_jni_NativeInterface = jni_functions_nocheck(); + + // make sure the last pointer in the checked table is not null, indicating + // an addition to the JNINativeInterface_ structure without initializing + // it in the checked table. + debug_only(int *lastPtr = (int *)((char *)&checked_jni_NativeInterface + \ + sizeof(*unchecked_jni_NativeInterface) - sizeof(char *));) + assert(*lastPtr != 0, + "Mismatched JNINativeInterface tables, check for new entries"); + + // with -verbose:jni this message will print + if (PrintJNIResolving) { + tty->print_cr("Checked JNI functions are being used to " \ + "validate JNI usage"); + } + + return &checked_jni_NativeInterface; +} diff --git a/hotspot/src/share/vm/prims/jniCheck.hpp b/hotspot/src/share/vm/prims/jniCheck.hpp new file mode 100644 index 00000000000..c682d0908b8 --- /dev/null +++ b/hotspot/src/share/vm/prims/jniCheck.hpp @@ -0,0 +1,39 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Checked JNI routines that are useful for outside of checked JNI +// + +class jniCheck : public AllStatic { + public: + static oop validate_handle(JavaThread* thr, jobject obj); + static oop validate_object(JavaThread* thr, jobject obj); + static klassOop validate_class(JavaThread* thr, jclass clazz, bool allow_primitive = false); + static void validate_class_descriptor(JavaThread* thr, const char* name); + static void validate_throwable_klass(JavaThread* thr, klassOop klass); + static void validate_call_object(JavaThread* thr, jobject obj, jmethodID method_id); + static void validate_call_class(JavaThread* thr, jclass clazz, jmethodID method_id); + static methodOop validate_jmethod_id(JavaThread* thr, jmethodID method_id); +}; diff --git a/hotspot/src/share/vm/prims/jniFastGetField.cpp b/hotspot/src/share/vm/prims/jniFastGetField.cpp new file mode 100644 index 00000000000..2692a5bc4d5 --- /dev/null +++ b/hotspot/src/share/vm/prims/jniFastGetField.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jniFastGetField.cpp.incl" + +address JNI_FastGetField::speculative_load_pclist [LIST_CAPACITY]; +address JNI_FastGetField::slowcase_entry_pclist [LIST_CAPACITY]; +int JNI_FastGetField::count = 0; + +address JNI_FastGetField::find_slowcase_pc(address pc) { + for (int i=0; iField: +// +// (See safepoint.hpp for a description of _safepoint_counter) +// +// load _safepoint_counter into old_counter +// IF old_counter is odd THEN +// a safepoint is going on, return jni_GetXXXField +// ELSE +// load the primitive field value into result (speculatively) +// load _safepoint_counter into new_counter +// IF (old_counter == new_counter) THEN +// no safepoint happened during the field access, return result +// ELSE +// a safepoint might have happened in-between, return jni_GetXXXField() +// ENDIF +// ENDIF +// +// LoadLoad membars to maintain the load order may be necessary +// for some platforms. +// +// The fast versions don't check for pending suspension request. +// This is fine since it's totally read-only and doesn't create new race. +// +// There is a hypothetical safepoint counter wraparound. But it's not +// a practical concern. + +class JNI_FastGetField : AllStatic { + private: + enum { LIST_CAPACITY = 40 }; // a conservative number for the number of + // speculative loads on all the platforms + static address speculative_load_pclist []; + static address slowcase_entry_pclist []; + static int count; + + static address generate_fast_get_int_field0(BasicType type); + static address generate_fast_get_float_field0(BasicType type); + + public: +#if defined(_WINDOWS) && !defined(_WIN64) + static GetBooleanField_t jni_fast_GetBooleanField_fp; + static GetByteField_t jni_fast_GetByteField_fp; + static GetCharField_t jni_fast_GetCharField_fp; + static GetShortField_t jni_fast_GetShortField_fp; + static GetIntField_t jni_fast_GetIntField_fp; + static GetLongField_t jni_fast_GetLongField_fp; + static GetFloatField_t jni_fast_GetFloatField_fp; + static GetDoubleField_t jni_fast_GetDoubleField_fp; +#endif + + static address generate_fast_get_boolean_field(); + static address generate_fast_get_byte_field(); + static address generate_fast_get_char_field(); + static address generate_fast_get_short_field(); + static address generate_fast_get_int_field(); + static address generate_fast_get_long_field(); + static address generate_fast_get_float_field(); + static address generate_fast_get_double_field(); + + // If pc is in speculative_load_pclist, return the corresponding + // slow case entry pc. Otherwise, return -1. + // This is used by signal/exception handler to handle such case: + // After an even safepoint counter is loaded and a fast field access + // is about to begin, a GC kicks in and shrinks the heap. Then the + // field access may fault. The signal/exception handler needs to + // return to the slow case. + // + // The GC may decide to temporarily stuff some bad values into handles, + // for example, for debugging purpose, in which case we need the mapping also. + static address find_slowcase_pc(address pc); +}; diff --git a/hotspot/src/share/vm/prims/jni_md.h b/hotspot/src/share/vm/prims/jni_md.h new file mode 100644 index 00000000000..f08ca50d8f9 --- /dev/null +++ b/hotspot/src/share/vm/prims/jni_md.h @@ -0,0 +1,37 @@ +/* + * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* Switch to the correct jni_md.h file without reliance on -I options. */ +#include "incls/_jni_pd.h.incl" + +/* + The local copies of JNI header files may be refreshed + from a JDK distribution by means of these commands: + + cp ${JDK_DIST}/solaris/include/solaris/jni_md.h ./jni_sparc.h + cp ${JDK_DIST}/win32/include/win32/jni_md.h ./jni_i486.h + cp ${JDK_DIST}/win32/include/jni.h ./jni.h + +*/ diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp new file mode 100644 index 00000000000..9a2cbf3676c --- /dev/null +++ b/hotspot/src/share/vm/prims/jvm.cpp @@ -0,0 +1,4498 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_jvm.cpp.incl" +#include + +/* + NOTE about use of any ctor or function call that can trigger a safepoint/GC: + such ctors and calls MUST NOT come between an oop declaration/init and its + usage because if objects are move this may cause various memory stomps, bus + errors and segfaults. Here is a cookbook for causing so called "naked oop + failures": + + JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredFields { + JVMWrapper("JVM_GetClassDeclaredFields"); + + // Object address to be held directly in mirror & not visible to GC + oop mirror = JNIHandles::resolve_non_null(ofClass); + + // If this ctor can hit a safepoint, moving objects around, then + ComplexConstructor foo; + + // Boom! mirror may point to JUNK instead of the intended object + (some dereference of mirror) + + // Here's another call that may block for GC, making mirror stale + MutexLocker ml(some_lock); + + // And here's an initializer that can result in a stale oop + // all in one step. + oop o = call_that_can_throw_exception(TRAPS); + + + The solution is to keep the oop declaration BELOW the ctor or function + call that might cause a GC, do another resolve to reassign the oop, or + consider use of a Handle instead of an oop so there is immunity from object + motion. But note that the "QUICK" entries below do not have a handlemark + and thus can only support use of handles passed in. +*/ + +static void trace_class_resolution_impl(klassOop to_class, TRAPS) { + ResourceMark rm; + int line_number = -1; + const char * source_file = NULL; + klassOop caller = NULL; + JavaThread* jthread = JavaThread::current(); + if (jthread->has_last_Java_frame()) { + vframeStream vfst(jthread); + + // scan up the stack skipping ClassLoader, AccessController and PrivilegedAction frames + symbolHandle access_controller = oopFactory::new_symbol_handle("java/security/AccessController", CHECK); + klassOop access_controller_klass = SystemDictionary::resolve_or_fail(access_controller, false, CHECK); + symbolHandle privileged_action = oopFactory::new_symbol_handle("java/security/PrivilegedAction", CHECK); + klassOop privileged_action_klass = SystemDictionary::resolve_or_fail(privileged_action, false, CHECK); + + methodOop last_caller = NULL; + + while (!vfst.at_end()) { + methodOop m = vfst.method(); + if (!vfst.method()->method_holder()->klass_part()->is_subclass_of(SystemDictionary::classloader_klass())&& + !vfst.method()->method_holder()->klass_part()->is_subclass_of(access_controller_klass) && + !vfst.method()->method_holder()->klass_part()->is_subclass_of(privileged_action_klass)) { + break; + } + last_caller = m; + vfst.next(); + } + // if this is called from Class.forName0 and that is called from Class.forName, + // then print the caller of Class.forName. If this is Class.loadClass, then print + // that caller, otherwise keep quiet since this should be picked up elsewhere. + bool found_it = false; + if (!vfst.at_end() && + instanceKlass::cast(vfst.method()->method_holder())->name() == vmSymbols::java_lang_Class() && + vfst.method()->name() == vmSymbols::forName0_name()) { + vfst.next(); + if (!vfst.at_end() && + instanceKlass::cast(vfst.method()->method_holder())->name() == vmSymbols::java_lang_Class() && + vfst.method()->name() == vmSymbols::forName_name()) { + vfst.next(); + found_it = true; + } + } else if (last_caller != NULL && + instanceKlass::cast(last_caller->method_holder())->name() == + vmSymbols::java_lang_ClassLoader() && + (last_caller->name() == vmSymbols::loadClassInternal_name() || + last_caller->name() == vmSymbols::loadClass_name())) { + found_it = true; + } + if (found_it && !vfst.at_end()) { + // found the caller + caller = vfst.method()->method_holder(); + line_number = vfst.method()->line_number_from_bci(vfst.bci()); + symbolOop s = instanceKlass::cast(vfst.method()->method_holder())->source_file_name(); + if (s != NULL) { + source_file = s->as_C_string(); + } + } + } + if (caller != NULL) { + if (to_class != caller) { + const char * from = Klass::cast(caller)->external_name(); + const char * to = Klass::cast(to_class)->external_name(); + // print in a single call to reduce interleaving between threads + if (source_file != NULL) { + tty->print("RESOLVE %s %s %s:%d (explicit)\n", from, to, source_file, line_number); + } else { + tty->print("RESOLVE %s %s (explicit)\n", from, to); + } + } + } +} + +static void trace_class_resolution(klassOop to_class) { + EXCEPTION_MARK; + trace_class_resolution_impl(to_class, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + } +} + +// Wrapper to trace JVM functions + +#ifdef ASSERT + class JVMTraceWrapper : public StackObj { + public: + JVMTraceWrapper(const char* format, ...) { + if (TraceJVMCalls) { + va_list ap; + va_start(ap, format); + tty->print("JVM "); + tty->vprint_cr(format, ap); + va_end(ap); + } + } + }; + + Histogram* JVMHistogram; + volatile jint JVMHistogram_lock = 0; + + class JVMHistogramElement : public HistogramElement { + public: + JVMHistogramElement(const char* name); + }; + + JVMHistogramElement::JVMHistogramElement(const char* elementName) { + _name = elementName; + uintx count = 0; + + while (Atomic::cmpxchg(1, &JVMHistogram_lock, 0) != 0) { + while (OrderAccess::load_acquire(&JVMHistogram_lock) != 0) { + count +=1; + if ( (WarnOnStalledSpinLock > 0) + && (count % WarnOnStalledSpinLock == 0)) { + warning("JVMHistogram_lock seems to be stalled"); + } + } + } + + if(JVMHistogram == NULL) + JVMHistogram = new Histogram("JVM Call Counts",100); + + JVMHistogram->add_element(this); + Atomic::dec(&JVMHistogram_lock); + } + + #define JVMCountWrapper(arg) \ + static JVMHistogramElement* e = new JVMHistogramElement(arg); \ + if (e != NULL) e->increment_count(); // Due to bug in VC++, we need a NULL check here eventhough it should never happen! + + #define JVMWrapper(arg1) JVMCountWrapper(arg1); JVMTraceWrapper(arg1) + #define JVMWrapper2(arg1, arg2) JVMCountWrapper(arg1); JVMTraceWrapper(arg1, arg2) + #define JVMWrapper3(arg1, arg2, arg3) JVMCountWrapper(arg1); JVMTraceWrapper(arg1, arg2, arg3) + #define JVMWrapper4(arg1, arg2, arg3, arg4) JVMCountWrapper(arg1); JVMTraceWrapper(arg1, arg2, arg3, arg4) +#else + #define JVMWrapper(arg1) + #define JVMWrapper2(arg1, arg2) + #define JVMWrapper3(arg1, arg2, arg3) + #define JVMWrapper4(arg1, arg2, arg3, arg4) +#endif + + +// Interface version ///////////////////////////////////////////////////////////////////// + + +JVM_LEAF(jint, JVM_GetInterfaceVersion()) + return JVM_INTERFACE_VERSION; +JVM_END + + +// java.lang.System ////////////////////////////////////////////////////////////////////// + + +JVM_LEAF(jlong, JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored)) + JVMWrapper("JVM_CurrentTimeMillis"); + return os::javaTimeMillis(); +JVM_END + +JVM_LEAF(jlong, JVM_NanoTime(JNIEnv *env, jclass ignored)) + JVMWrapper("JVM_NanoTime"); + return os::javaTimeNanos(); +JVM_END + + +JVM_ENTRY(void, JVM_ArrayCopy(JNIEnv *env, jclass ignored, jobject src, jint src_pos, + jobject dst, jint dst_pos, jint length)) + JVMWrapper("JVM_ArrayCopy"); + // Check if we have null pointers + if (src == NULL || dst == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + arrayOop s = arrayOop(JNIHandles::resolve_non_null(src)); + arrayOop d = arrayOop(JNIHandles::resolve_non_null(dst)); + assert(s->is_oop(), "JVM_ArrayCopy: src not an oop"); + assert(d->is_oop(), "JVM_ArrayCopy: dst not an oop"); + // Do copy + Klass::cast(s->klass())->copy_array(s, src_pos, d, dst_pos, length, thread); +JVM_END + + +static void set_property(Handle props, const char* key, const char* value, TRAPS) { + JavaValue r(T_OBJECT); + // public synchronized Object put(Object key, Object value); + HandleMark hm(THREAD); + Handle key_str = java_lang_String::create_from_platform_dependent_str(key, CHECK); + Handle value_str = java_lang_String::create_from_platform_dependent_str((value != NULL ? value : ""), CHECK); + JavaCalls::call_virtual(&r, + props, + KlassHandle(THREAD, SystemDictionary::properties_klass()), + vmSymbolHandles::put_name(), + vmSymbolHandles::object_object_object_signature(), + key_str, + value_str, + THREAD); +} + + +#define PUTPROP(props, name, value) set_property((props), (name), (value), CHECK_(properties)); + + +JVM_ENTRY(jobject, JVM_InitProperties(JNIEnv *env, jobject properties)) + JVMWrapper("JVM_InitProperties"); + ResourceMark rm; + + Handle props(THREAD, JNIHandles::resolve_non_null(properties)); + + // System property list includes both user set via -D option and + // jvm system specific properties. + for (SystemProperty* p = Arguments::system_properties(); p != NULL; p = p->next()) { + PUTPROP(props, p->key(), p->value()); + } + + // Convert the -XX:MaxDirectMemorySize= command line flag + // to the sun.nio.MaxDirectMemorySize property. + // Do this after setting user properties to prevent people + // from setting the value with a -D option, as requested. + { + char as_chars[256]; + jio_snprintf(as_chars, sizeof(as_chars), INTX_FORMAT, MaxDirectMemorySize); + PUTPROP(props, "sun.nio.MaxDirectMemorySize", as_chars); + } + + // JVM monitoring and management support + // Add the sun.management.compiler property for the compiler's name + { +#undef CSIZE +#if defined(_LP64) || defined(_WIN64) + #define CSIZE "64-Bit " +#else + #define CSIZE +#endif // 64bit + +#ifdef TIERED + const char* compiler_name = "HotSpot " CSIZE "Tiered Compilers"; +#else +#if defined(COMPILER1) + const char* compiler_name = "HotSpot " CSIZE "Client Compiler"; +#elif defined(COMPILER2) + const char* compiler_name = "HotSpot " CSIZE "Server Compiler"; +#else + const char* compiler_name = ""; +#endif // compilers +#endif // TIERED + + if (*compiler_name != '\0' && + (Arguments::mode() != Arguments::_int)) { + PUTPROP(props, "sun.management.compiler", compiler_name); + } + } + + return properties; +JVM_END + + +// java.lang.Runtime ///////////////////////////////////////////////////////////////////////// + +extern volatile jint vm_created; + +JVM_ENTRY_NO_ENV(void, JVM_Exit(jint code)) + if (vm_created != 0 && (code == 0)) { + // The VM is about to exit. We call back into Java to check whether finalizers should be run + Universe::run_finalizers_on_exit(); + } + before_exit(thread); + vm_exit(code); +JVM_END + + +JVM_ENTRY_NO_ENV(void, JVM_Halt(jint code)) + before_exit(thread); + vm_exit(code); +JVM_END + + +JVM_LEAF(void, JVM_OnExit(void (*func)(void))) + register_on_exit_function(func); +JVM_END + + +JVM_ENTRY_NO_ENV(void, JVM_GC(void)) + JVMWrapper("JVM_GC"); + if (!DisableExplicitGC) { + Universe::heap()->collect(GCCause::_java_lang_system_gc); + } +JVM_END + + +JVM_LEAF(jlong, JVM_MaxObjectInspectionAge(void)) + JVMWrapper("JVM_MaxObjectInspectionAge"); + return Universe::heap()->millis_since_last_gc(); +JVM_END + + +JVM_LEAF(void, JVM_TraceInstructions(jboolean on)) + if (PrintJVMWarnings) warning("JVM_TraceInstructions not supported"); +JVM_END + + +JVM_LEAF(void, JVM_TraceMethodCalls(jboolean on)) + if (PrintJVMWarnings) warning("JVM_TraceMethodCalls not supported"); +JVM_END + +static inline jlong convert_size_t_to_jlong(size_t val) { + // In the 64-bit vm, a size_t can overflow a jlong (which is signed). + NOT_LP64 (return (jlong)val;) + LP64_ONLY(return (jlong)MIN2(val, (size_t)max_jlong);) +} + +JVM_ENTRY_NO_ENV(jlong, JVM_TotalMemory(void)) + JVMWrapper("JVM_TotalMemory"); + size_t n = Universe::heap()->capacity(); + return convert_size_t_to_jlong(n); +JVM_END + + +JVM_ENTRY_NO_ENV(jlong, JVM_FreeMemory(void)) + JVMWrapper("JVM_FreeMemory"); + CollectedHeap* ch = Universe::heap(); + size_t n = ch->capacity() - ch->used(); + return convert_size_t_to_jlong(n); +JVM_END + + +JVM_ENTRY_NO_ENV(jlong, JVM_MaxMemory(void)) + JVMWrapper("JVM_MaxMemory"); + size_t n = Universe::heap()->max_capacity(); + return convert_size_t_to_jlong(n); +JVM_END + + +JVM_ENTRY_NO_ENV(jint, JVM_ActiveProcessorCount(void)) + JVMWrapper("JVM_ActiveProcessorCount"); + return os::active_processor_count(); +JVM_END + + + +// java.lang.Throwable ////////////////////////////////////////////////////// + + +JVM_ENTRY(void, JVM_FillInStackTrace(JNIEnv *env, jobject receiver)) + JVMWrapper("JVM_FillInStackTrace"); + Handle exception(thread, JNIHandles::resolve_non_null(receiver)); + java_lang_Throwable::fill_in_stack_trace(exception); +JVM_END + + +JVM_ENTRY(void, JVM_PrintStackTrace(JNIEnv *env, jobject receiver, jobject printable)) + JVMWrapper("JVM_PrintStackTrace"); + // Note: This is no longer used in Merlin, but we still support it for compatibility. + oop exception = JNIHandles::resolve_non_null(receiver); + oop stream = JNIHandles::resolve_non_null(printable); + java_lang_Throwable::print_stack_trace(exception, stream); +JVM_END + + +JVM_ENTRY(jint, JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable)) + JVMWrapper("JVM_GetStackTraceDepth"); + oop exception = JNIHandles::resolve(throwable); + return java_lang_Throwable::get_stack_trace_depth(exception, THREAD); +JVM_END + + +JVM_ENTRY(jobject, JVM_GetStackTraceElement(JNIEnv *env, jobject throwable, jint index)) + JVMWrapper("JVM_GetStackTraceElement"); + JvmtiVMObjectAllocEventCollector oam; // This ctor (throughout this module) may trigger a safepoint/GC + oop exception = JNIHandles::resolve(throwable); + oop element = java_lang_Throwable::get_stack_trace_element(exception, index, CHECK_NULL); + return JNIHandles::make_local(env, element); +JVM_END + + +// java.lang.Object /////////////////////////////////////////////// + + +JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle)) + JVMWrapper("JVM_IHashCode"); + // as implemented in the classic virtual machine; return 0 if object is NULL + return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ; +JVM_END + + +JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms)) + JVMWrapper("JVM_MonitorWait"); + Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); + assert(obj->is_instance() || obj->is_array(), "JVM_MonitorWait must apply to an object"); + JavaThreadInObjectWaitState jtiows(thread, ms != 0); + if (JvmtiExport::should_post_monitor_wait()) { + JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms); + } + ObjectSynchronizer::wait(obj, ms, CHECK); +JVM_END + + +JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle)) + JVMWrapper("JVM_MonitorNotify"); + Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); + assert(obj->is_instance() || obj->is_array(), "JVM_MonitorNotify must apply to an object"); + ObjectSynchronizer::notify(obj, CHECK); +JVM_END + + +JVM_ENTRY(void, JVM_MonitorNotifyAll(JNIEnv* env, jobject handle)) + JVMWrapper("JVM_MonitorNotifyAll"); + Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); + assert(obj->is_instance() || obj->is_array(), "JVM_MonitorNotifyAll must apply to an object"); + ObjectSynchronizer::notifyall(obj, CHECK); +JVM_END + + +JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle)) + JVMWrapper("JVM_Clone"); + Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); + const KlassHandle klass (THREAD, obj->klass()); + JvmtiVMObjectAllocEventCollector oam; + +#ifdef ASSERT + // Just checking that the cloneable flag is set correct + if (obj->is_javaArray()) { + guarantee(klass->is_cloneable(), "all arrays are cloneable"); + } else { + guarantee(obj->is_instance(), "should be instanceOop"); + bool cloneable = klass->is_subtype_of(SystemDictionary::cloneable_klass()); + guarantee(cloneable == klass->is_cloneable(), "incorrect cloneable flag"); + } +#endif + + // Check if class of obj supports the Cloneable interface. + // All arrays are considered to be cloneable (See JLS 20.1.5) + if (!klass->is_cloneable()) { + ResourceMark rm(THREAD); + THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name()); + } + + // Make shallow object copy + const int size = obj->size(); + oop new_obj = NULL; + if (obj->is_javaArray()) { + const int length = ((arrayOop)obj())->length(); + new_obj = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL); + } else { + new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL); + } + // 4839641 (4840070): We must do an oop-atomic copy, because if another thread + // is modifying a reference field in the clonee, a non-oop-atomic copy might + // be suspended in the middle of copying the pointer and end up with parts + // of two different pointers in the field. Subsequent dereferences will crash. + // 4846409: an oop-copy of objects with long or double fields or arrays of same + // won't copy the longs/doubles atomically in 32-bit vm's, so we copy jlongs instead + // of oops. We know objects are aligned on a minimum of an jlong boundary. + // The same is true of StubRoutines::object_copy and the various oop_copy + // variants, and of the code generated by the inline_native_clone intrinsic. + assert(MinObjAlignmentInBytes >= BytesPerLong, "objects misaligned"); + Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj, + (size_t)align_object_size(size) / HeapWordsPerLong); + // Clear the header + new_obj->init_mark(); + + // Store check (mark entire object and let gc sort it out) + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->has_write_region_opt(), "Barrier set does not have write_region"); + bs->write_region(MemRegion((HeapWord*)new_obj, size)); + + // Caution: this involves a java upcall, so the clone should be + // "gc-robust" by this stage. + if (klass->has_finalizer()) { + assert(obj->is_instance(), "should be instanceOop"); + new_obj = instanceKlass::register_finalizer(instanceOop(new_obj), CHECK_NULL); + } + + return JNIHandles::make_local(env, oop(new_obj)); +JVM_END + +// java.lang.Compiler //////////////////////////////////////////////////// + +// The initial cuts of the HotSpot VM will not support JITs, and all existing +// JITs would need extensive changes to work with HotSpot. The JIT-related JVM +// functions are all silently ignored unless JVM warnings are printed. + +JVM_LEAF(void, JVM_InitializeCompiler (JNIEnv *env, jclass compCls)) + if (PrintJVMWarnings) warning("JVM_InitializeCompiler not supported"); +JVM_END + + +JVM_LEAF(jboolean, JVM_IsSilentCompiler(JNIEnv *env, jclass compCls)) + if (PrintJVMWarnings) warning("JVM_IsSilentCompiler not supported"); + return JNI_FALSE; +JVM_END + + +JVM_LEAF(jboolean, JVM_CompileClass(JNIEnv *env, jclass compCls, jclass cls)) + if (PrintJVMWarnings) warning("JVM_CompileClass not supported"); + return JNI_FALSE; +JVM_END + + +JVM_LEAF(jboolean, JVM_CompileClasses(JNIEnv *env, jclass cls, jstring jname)) + if (PrintJVMWarnings) warning("JVM_CompileClasses not supported"); + return JNI_FALSE; +JVM_END + + +JVM_LEAF(jobject, JVM_CompilerCommand(JNIEnv *env, jclass compCls, jobject arg)) + if (PrintJVMWarnings) warning("JVM_CompilerCommand not supported"); + return NULL; +JVM_END + + +JVM_LEAF(void, JVM_EnableCompiler(JNIEnv *env, jclass compCls)) + if (PrintJVMWarnings) warning("JVM_EnableCompiler not supported"); +JVM_END + + +JVM_LEAF(void, JVM_DisableCompiler(JNIEnv *env, jclass compCls)) + if (PrintJVMWarnings) warning("JVM_DisableCompiler not supported"); +JVM_END + + + +// Error message support ////////////////////////////////////////////////////// + +JVM_LEAF(jint, JVM_GetLastErrorString(char *buf, int len)) + JVMWrapper("JVM_GetLastErrorString"); + return hpi::lasterror(buf, len); +JVM_END + + +// java.io.File /////////////////////////////////////////////////////////////// + +JVM_LEAF(char*, JVM_NativePath(char* path)) + JVMWrapper2("JVM_NativePath (%s)", path); + return hpi::native_path(path); +JVM_END + + +// Misc. class handling /////////////////////////////////////////////////////////// + + +JVM_ENTRY(jclass, JVM_GetCallerClass(JNIEnv* env, int depth)) + JVMWrapper("JVM_GetCallerClass"); + klassOop k = thread->security_get_caller_class(depth); + return (k == NULL) ? NULL : (jclass) JNIHandles::make_local(env, Klass::cast(k)->java_mirror()); +JVM_END + + +JVM_ENTRY(jclass, JVM_FindPrimitiveClass(JNIEnv* env, const char* utf)) + JVMWrapper("JVM_FindPrimitiveClass"); + oop mirror = NULL; + BasicType t = name2type(utf); + if (t != T_ILLEGAL && t != T_OBJECT && t != T_ARRAY) { + mirror = Universe::java_mirror(t); + } + if (mirror == NULL) { + THROW_MSG_0(vmSymbols::java_lang_ClassNotFoundException(), (char*) utf); + } else { + return (jclass) JNIHandles::make_local(env, mirror); + } +JVM_END + + +JVM_ENTRY(void, JVM_ResolveClass(JNIEnv* env, jclass cls)) + JVMWrapper("JVM_ResolveClass"); + if (PrintJVMWarnings) warning("JVM_ResolveClass not implemented"); +JVM_END + + +JVM_ENTRY(jclass, JVM_FindClassFromClassLoader(JNIEnv* env, const char* name, + jboolean init, jobject loader, + jboolean throwError)) + JVMWrapper3("JVM_FindClassFromClassLoader %s throw %s", name, + throwError ? "error" : "exception"); + // Java libraries should ensure that name is never null... + if (name == NULL || (int)strlen(name) > symbolOopDesc::max_length()) { + // It's impossible to create this class; the name cannot fit + // into the constant pool. + if (throwError) { + THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name); + } else { + THROW_MSG_0(vmSymbols::java_lang_ClassNotFoundException(), name); + } + } + symbolHandle h_name = oopFactory::new_symbol_handle(name, CHECK_NULL); + Handle h_loader(THREAD, JNIHandles::resolve(loader)); + jclass result = find_class_from_class_loader(env, h_name, init, h_loader, + Handle(), throwError, thread); + + if (TraceClassResolution && result != NULL) { + trace_class_resolution(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(result))); + } + + return result; +JVM_END + + +JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name, + jboolean init, jclass from)) + JVMWrapper2("JVM_FindClassFromClass %s", name); + if (name == NULL || (int)strlen(name) > symbolOopDesc::max_length()) { + // It's impossible to create this class; the name cannot fit + // into the constant pool. + THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name); + } + symbolHandle h_name = oopFactory::new_symbol_handle(name, CHECK_NULL); + oop from_class_oop = JNIHandles::resolve(from); + klassOop from_class = (from_class_oop == NULL) + ? (klassOop)NULL + : java_lang_Class::as_klassOop(from_class_oop); + oop class_loader = NULL; + oop protection_domain = NULL; + if (from_class != NULL) { + class_loader = Klass::cast(from_class)->class_loader(); + protection_domain = Klass::cast(from_class)->protection_domain(); + } + Handle h_loader(THREAD, class_loader); + Handle h_prot (THREAD, protection_domain); + jclass result = find_class_from_class_loader(env, h_name, init, h_loader, + h_prot, true, thread); + + if (TraceClassResolution && result != NULL) { + // this function is generally only used for class loading during verification. + ResourceMark rm; + oop from_mirror = JNIHandles::resolve_non_null(from); + klassOop from_class = java_lang_Class::as_klassOop(from_mirror); + const char * from_name = Klass::cast(from_class)->external_name(); + + oop mirror = JNIHandles::resolve_non_null(result); + klassOop to_class = java_lang_Class::as_klassOop(mirror); + const char * to = Klass::cast(to_class)->external_name(); + tty->print("RESOLVE %s %s (verification)\n", from_name, to); + } + + return result; +JVM_END + +static void is_lock_held_by_thread(Handle loader, PerfCounter* counter, TRAPS) { + if (loader.is_null()) { + return; + } + + // check whether the current caller thread holds the lock or not. + // If not, increment the corresponding counter + if (ObjectSynchronizer::query_lock_ownership((JavaThread*)THREAD, loader) != + ObjectSynchronizer::owner_self) { + counter->inc(); + } +} + +// common code for JVM_DefineClass() and JVM_DefineClassWithSource() +static jclass jvm_define_class_common(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source, TRAPS) { + + // Since exceptions can be thrown, class initialization can take place + // if name is NULL no check for class name in .class stream has to be made. + symbolHandle class_name; + if (name != NULL) { + const int str_len = (int)strlen(name); + if (str_len > symbolOopDesc::max_length()) { + // It's impossible to create this class; the name cannot fit + // into the constant pool. + THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name); + } + class_name = oopFactory::new_symbol_handle(name, str_len, CHECK_NULL); + } + + ResourceMark rm(THREAD); + ClassFileStream st((u1*) buf, len, (char *)source); + Handle class_loader (THREAD, JNIHandles::resolve(loader)); + if (UsePerfData) { + is_lock_held_by_thread(class_loader, + ClassLoader::sync_JVMDefineClassLockFreeCounter(), + THREAD); + } + Handle protection_domain (THREAD, JNIHandles::resolve(pd)); + klassOop k = SystemDictionary::resolve_from_stream(class_name, class_loader, + protection_domain, &st, + CHECK_NULL); + + if (TraceClassResolution && k != NULL) { + trace_class_resolution(k); + } + + return (jclass) JNIHandles::make_local(env, Klass::cast(k)->java_mirror()); +} + + +JVM_ENTRY(jclass, JVM_DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd)) + JVMWrapper2("JVM_DefineClass %s", name); + + return jvm_define_class_common(env, name, loader, buf, len, pd, "__JVM_DefineClass__", THREAD); +JVM_END + + +JVM_ENTRY(jclass, JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source)) + JVMWrapper2("JVM_DefineClassWithSource %s", name); + + return jvm_define_class_common(env, name, loader, buf, len, pd, source, THREAD); +JVM_END + + +JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name)) + JVMWrapper("JVM_FindLoadedClass"); + ResourceMark rm(THREAD); + + Handle h_name (THREAD, JNIHandles::resolve_non_null(name)); + Handle string = java_lang_String::internalize_classname(h_name, CHECK_NULL); + + const char* str = java_lang_String::as_utf8_string(string()); + // Sanity check, don't expect null + if (str == NULL) return NULL; + + const int str_len = (int)strlen(str); + if (str_len > symbolOopDesc::max_length()) { + // It's impossible to create this class; the name cannot fit + // into the constant pool. + return NULL; + } + symbolHandle klass_name = oopFactory::new_symbol_handle(str, str_len,CHECK_NULL); + + // Security Note: + // The Java level wrapper will perform the necessary security check allowing + // us to pass the NULL as the initiating class loader. + Handle h_loader(THREAD, JNIHandles::resolve(loader)); + if (UsePerfData) { + is_lock_held_by_thread(h_loader, + ClassLoader::sync_JVMFindLoadedClassLockFreeCounter(), + THREAD); + } + + klassOop k = SystemDictionary::find_instance_or_array_klass(klass_name, + h_loader, + Handle(), + CHECK_NULL); + + return (k == NULL) ? NULL : + (jclass) JNIHandles::make_local(env, Klass::cast(k)->java_mirror()); +JVM_END + + +// Reflection support ////////////////////////////////////////////////////////////////////////////// + +JVM_ENTRY(jstring, JVM_GetClassName(JNIEnv *env, jclass cls)) + assert (cls != NULL, "illegal class"); + JVMWrapper("JVM_GetClassName"); + JvmtiVMObjectAllocEventCollector oam; + ResourceMark rm(THREAD); + const char* name; + if (java_lang_Class::is_primitive(JNIHandles::resolve(cls))) { + name = type2name(java_lang_Class::primitive_type(JNIHandles::resolve(cls))); + } else { + // Consider caching interned string in Klass + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve(cls)); + assert(k->is_klass(), "just checking"); + name = Klass::cast(k)->external_name(); + } + oop result = StringTable::intern((char*) name, CHECK_NULL); + return (jstring) JNIHandles::make_local(env, result); +JVM_END + + +JVM_ENTRY(jobjectArray, JVM_GetClassInterfaces(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetClassInterfaces"); + JvmtiVMObjectAllocEventCollector oam; + oop mirror = JNIHandles::resolve_non_null(cls); + + // Special handling for primitive objects + if (java_lang_Class::is_primitive(mirror)) { + // Primitive objects does not have any interfaces + objArrayOop r = oopFactory::new_objArray(SystemDictionary::class_klass(), 0, CHECK_NULL); + return (jobjectArray) JNIHandles::make_local(env, r); + } + + KlassHandle klass(thread, java_lang_Class::as_klassOop(mirror)); + // Figure size of result array + int size; + if (klass->oop_is_instance()) { + size = instanceKlass::cast(klass())->local_interfaces()->length(); + } else { + assert(klass->oop_is_objArray() || klass->oop_is_typeArray(), "Illegal mirror klass"); + size = 2; + } + + // Allocate result array + objArrayOop r = oopFactory::new_objArray(SystemDictionary::class_klass(), size, CHECK_NULL); + objArrayHandle result (THREAD, r); + // Fill in result + if (klass->oop_is_instance()) { + // Regular instance klass, fill in all local interfaces + for (int index = 0; index < size; index++) { + klassOop k = klassOop(instanceKlass::cast(klass())->local_interfaces()->obj_at(index)); + result->obj_at_put(index, Klass::cast(k)->java_mirror()); + } + } else { + // All arrays implement java.lang.Cloneable and java.io.Serializable + result->obj_at_put(0, Klass::cast(SystemDictionary::cloneable_klass())->java_mirror()); + result->obj_at_put(1, Klass::cast(SystemDictionary::serializable_klass())->java_mirror()); + } + return (jobjectArray) JNIHandles::make_local(env, result()); +JVM_END + + +JVM_ENTRY(jobject, JVM_GetClassLoader(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetClassLoader"); + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(cls))) { + return NULL; + } + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + oop loader = Klass::cast(k)->class_loader(); + return JNIHandles::make_local(env, loader); +JVM_END + + +JVM_QUICK_ENTRY(jboolean, JVM_IsInterface(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_IsInterface"); + oop mirror = JNIHandles::resolve_non_null(cls); + if (java_lang_Class::is_primitive(mirror)) { + return JNI_FALSE; + } + klassOop k = java_lang_Class::as_klassOop(mirror); + jboolean result = Klass::cast(k)->is_interface(); + assert(!result || Klass::cast(k)->oop_is_instance(), + "all interfaces are instance types"); + // The compiler intrinsic for isInterface tests the + // Klass::_access_flags bits in the same way. + return result; +JVM_END + + +JVM_ENTRY(jobjectArray, JVM_GetClassSigners(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetClassSigners"); + JvmtiVMObjectAllocEventCollector oam; + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(cls))) { + // There are no signers for primitive types + return NULL; + } + + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + objArrayOop signers = NULL; + if (Klass::cast(k)->oop_is_instance()) { + signers = instanceKlass::cast(k)->signers(); + } + + // If there are no signers set in the class, or if the class + // is an array, return NULL. + if (signers == NULL) return NULL; + + // copy of the signers array + klassOop element = objArrayKlass::cast(signers->klass())->element_klass(); + objArrayOop signers_copy = oopFactory::new_objArray(element, signers->length(), CHECK_NULL); + for (int index = 0; index < signers->length(); index++) { + signers_copy->obj_at_put(index, signers->obj_at(index)); + } + + // return the copy + return (jobjectArray) JNIHandles::make_local(env, signers_copy); +JVM_END + + +JVM_ENTRY(void, JVM_SetClassSigners(JNIEnv *env, jclass cls, jobjectArray signers)) + JVMWrapper("JVM_SetClassSigners"); + if (!java_lang_Class::is_primitive(JNIHandles::resolve_non_null(cls))) { + // This call is ignored for primitive types and arrays. + // Signers are only set once, ClassLoader.java, and thus shouldn't + // be called with an array. Only the bootstrap loader creates arrays. + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + if (Klass::cast(k)->oop_is_instance()) { + instanceKlass::cast(k)->set_signers(objArrayOop(JNIHandles::resolve(signers))); + } + } +JVM_END + + +JVM_ENTRY(jobject, JVM_GetProtectionDomain(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetProtectionDomain"); + if (JNIHandles::resolve(cls) == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), NULL); + } + + if (java_lang_Class::is_primitive(JNIHandles::resolve(cls))) { + // Primitive types does not have a protection domain. + return NULL; + } + + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve(cls)); + return (jobject) JNIHandles::make_local(env, Klass::cast(k)->protection_domain()); +JVM_END + + +// Obsolete since 1.2 (Class.setProtectionDomain removed), although +// still defined in core libraries as of 1.5. +JVM_ENTRY(void, JVM_SetProtectionDomain(JNIEnv *env, jclass cls, jobject protection_domain)) + JVMWrapper("JVM_SetProtectionDomain"); + if (JNIHandles::resolve(cls) == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + if (!java_lang_Class::is_primitive(JNIHandles::resolve(cls))) { + // Call is ignored for primitive types + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve(cls)); + + // cls won't be an array, as this called only from ClassLoader.defineClass + if (Klass::cast(k)->oop_is_instance()) { + oop pd = JNIHandles::resolve(protection_domain); + assert(pd == NULL || pd->is_oop(), "just checking"); + instanceKlass::cast(k)->set_protection_domain(pd); + } + } +JVM_END + + +JVM_ENTRY(jobject, JVM_DoPrivileged(JNIEnv *env, jclass cls, jobject action, jobject context, jboolean wrapException)) + JVMWrapper("JVM_DoPrivileged"); + + if (action == NULL) { + THROW_MSG_0(vmSymbols::java_lang_NullPointerException(), "Null action"); + } + + // Stack allocated list of privileged stack elements + PrivilegedElement pi; + + // Check that action object understands "Object run()" + Handle object (THREAD, JNIHandles::resolve(action)); + + // get run() method + methodOop m_oop = Klass::cast(object->klass())->uncached_lookup_method( + vmSymbols::run_method_name(), + vmSymbols::void_object_signature()); + methodHandle m (THREAD, m_oop); + if (m.is_null() || !m->is_method() || !methodOop(m())->is_public() || methodOop(m())->is_static()) { + THROW_MSG_0(vmSymbols::java_lang_InternalError(), "No run method"); + } + + // Compute the frame initiating the do privileged operation and setup the privileged stack + vframeStream vfst(thread); + vfst.security_get_caller_frame(1); + + if (!vfst.at_end()) { + pi.initialize(&vfst, JNIHandles::resolve(context), thread->privileged_stack_top(), CHECK_NULL); + thread->set_privileged_stack_top(&pi); + } + + + // invoke the Object run() in the action object. We cannot use call_interface here, since the static type + // is not really known - it is either java.security.PrivilegedAction or java.security.PrivilegedExceptionAction + Handle pending_exception; + JavaValue result(T_OBJECT); + JavaCallArguments args(object); + JavaCalls::call(&result, m, &args, THREAD); + + // done with action, remove ourselves from the list + if (!vfst.at_end()) { + assert(thread->privileged_stack_top() != NULL && thread->privileged_stack_top() == &pi, "wrong top element"); + thread->set_privileged_stack_top(thread->privileged_stack_top()->next()); + } + + if (HAS_PENDING_EXCEPTION) { + pending_exception = Handle(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + + if ( pending_exception->is_a(SystemDictionary::exception_klass()) && + !pending_exception->is_a(SystemDictionary::runtime_exception_klass())) { + // Throw a java.security.PrivilegedActionException(Exception e) exception + JavaCallArguments args(pending_exception); + THROW_ARG_0(vmSymbolHandles::java_security_PrivilegedActionException(), + vmSymbolHandles::exception_void_signature(), + &args); + } + } + + if (pending_exception.not_null()) THROW_OOP_0(pending_exception()); + return JNIHandles::make_local(env, (oop) result.get_jobject()); +JVM_END + + +// Returns the inherited_access_control_context field of the running thread. +JVM_ENTRY(jobject, JVM_GetInheritedAccessControlContext(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetInheritedAccessControlContext"); + oop result = java_lang_Thread::inherited_access_control_context(thread->threadObj()); + return JNIHandles::make_local(env, result); +JVM_END + +class RegisterArrayForGC { + private: + JavaThread *_thread; + public: + RegisterArrayForGC(JavaThread *thread, GrowableArray* array) { + _thread = thread; + _thread->register_array_for_gc(array); + } + + ~RegisterArrayForGC() { + _thread->register_array_for_gc(NULL); + } +}; + + +JVM_ENTRY(jobject, JVM_GetStackAccessControlContext(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetStackAccessControlContext"); + if (!UsePrivilegedStack) return NULL; + + ResourceMark rm(THREAD); + GrowableArray* local_array = new GrowableArray(12); + JvmtiVMObjectAllocEventCollector oam; + + // count the protection domains on the execution stack. We collapse + // duplicate consecutive protection domains into a single one, as + // well as stopping when we hit a privileged frame. + + // Use vframeStream to iterate through Java frames + vframeStream vfst(thread); + + oop previous_protection_domain = NULL; + Handle privileged_context(thread, NULL); + bool is_privileged = false; + oop protection_domain = NULL; + + for(; !vfst.at_end(); vfst.next()) { + // get method of frame + methodOop method = vfst.method(); + intptr_t* frame_id = vfst.frame_id(); + + // check the privileged frames to see if we have a match + if (thread->privileged_stack_top() && thread->privileged_stack_top()->frame_id() == frame_id) { + // this frame is privileged + is_privileged = true; + privileged_context = Handle(thread, thread->privileged_stack_top()->privileged_context()); + protection_domain = thread->privileged_stack_top()->protection_domain(); + } else { + protection_domain = instanceKlass::cast(method->method_holder())->protection_domain(); + } + + if ((previous_protection_domain != protection_domain) && (protection_domain != NULL)) { + local_array->push(protection_domain); + previous_protection_domain = protection_domain; + } + + if (is_privileged) break; + } + + + // either all the domains on the stack were system domains, or + // we had a privileged system domain + if (local_array->is_empty()) { + if (is_privileged && privileged_context.is_null()) return NULL; + + oop result = java_security_AccessControlContext::create(objArrayHandle(), is_privileged, privileged_context, CHECK_NULL); + return JNIHandles::make_local(env, result); + } + + // the resource area must be registered in case of a gc + RegisterArrayForGC ragc(thread, local_array); + objArrayOop context = oopFactory::new_objArray(SystemDictionary::protectionDomain_klass(), + local_array->length(), CHECK_NULL); + objArrayHandle h_context(thread, context); + for (int index = 0; index < local_array->length(); index++) { + h_context->obj_at_put(index, local_array->at(index)); + } + + oop result = java_security_AccessControlContext::create(h_context, is_privileged, privileged_context, CHECK_NULL); + + return JNIHandles::make_local(env, result); +JVM_END + + +JVM_QUICK_ENTRY(jboolean, JVM_IsArrayClass(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_IsArrayClass"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + return (k != NULL) && Klass::cast(k)->oop_is_javaArray() ? true : false; +JVM_END + + +JVM_QUICK_ENTRY(jboolean, JVM_IsPrimitiveClass(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_IsPrimitiveClass"); + oop mirror = JNIHandles::resolve_non_null(cls); + return (jboolean) java_lang_Class::is_primitive(mirror); +JVM_END + + +JVM_ENTRY(jclass, JVM_GetComponentType(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetComponentType"); + oop mirror = JNIHandles::resolve_non_null(cls); + oop result = Reflection::array_component_type(mirror, CHECK_NULL); + return (jclass) JNIHandles::make_local(env, result); +JVM_END + + +JVM_ENTRY(jint, JVM_GetClassModifiers(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetClassModifiers"); + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(cls))) { + // Primitive type + return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC; + } + + Klass* k = Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls))); + debug_only(int computed_modifiers = k->compute_modifier_flags(CHECK_0)); + assert(k->modifier_flags() == computed_modifiers, "modifiers cache is OK"); + return k->modifier_flags(); +JVM_END + + +// Inner class reflection /////////////////////////////////////////////////////////////////////////////// + +JVM_ENTRY(jobjectArray, JVM_GetDeclaredClasses(JNIEnv *env, jclass ofClass)) + const int inner_class_info_index = 0; + const int outer_class_info_index = 1; + + JvmtiVMObjectAllocEventCollector oam; + // ofClass is a reference to a java_lang_Class object. The mirror object + // of an instanceKlass + + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass)) || + ! Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass)))->oop_is_instance()) { + oop result = oopFactory::new_objArray(SystemDictionary::class_klass(), 0, CHECK_NULL); + return (jobjectArray)JNIHandles::make_local(env, result); + } + + instanceKlassHandle k(thread, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass))); + + if (k->inner_classes()->length() == 0) { + // Neither an inner nor outer class + oop result = oopFactory::new_objArray(SystemDictionary::class_klass(), 0, CHECK_NULL); + return (jobjectArray)JNIHandles::make_local(env, result); + } + + // find inner class info + typeArrayHandle icls(thread, k->inner_classes()); + constantPoolHandle cp(thread, k->constants()); + int length = icls->length(); + + // Allocate temp. result array + objArrayOop r = oopFactory::new_objArray(SystemDictionary::class_klass(), length/4, CHECK_NULL); + objArrayHandle result (THREAD, r); + int members = 0; + + for(int i = 0; i < length; i += 4) { + int ioff = icls->ushort_at(i + inner_class_info_index); + int ooff = icls->ushort_at(i + outer_class_info_index); + + if (ioff != 0 && ooff != 0) { + // Check to see if the name matches the class we're looking for + // before attempting to find the class. + if (cp->klass_name_at_matches(k, ooff)) { + klassOop outer_klass = cp->klass_at(ooff, CHECK_NULL); + if (outer_klass == k()) { + klassOop ik = cp->klass_at(ioff, CHECK_NULL); + instanceKlassHandle inner_klass (THREAD, ik); + + // Throws an exception if outer klass has not declared k as + // an inner klass + Reflection::check_for_inner_class(k, inner_klass, CHECK_NULL); + + result->obj_at_put(members, inner_klass->java_mirror()); + members++; + } + } + } + } + + if (members != length) { + // Return array of right length + objArrayOop res = oopFactory::new_objArray(SystemDictionary::class_klass(), members, CHECK_NULL); + for(int i = 0; i < members; i++) { + res->obj_at_put(i, result->obj_at(i)); + } + return (jobjectArray)JNIHandles::make_local(env, res); + } + + return (jobjectArray)JNIHandles::make_local(env, result()); +JVM_END + + +JVM_ENTRY(jclass, JVM_GetDeclaringClass(JNIEnv *env, jclass ofClass)) + const int inner_class_info_index = 0; + const int outer_class_info_index = 1; + + // ofClass is a reference to a java_lang_Class object. + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass)) || + ! Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass)))->oop_is_instance()) { + return NULL; + } + + instanceKlassHandle k(thread, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass))); + + if (k->inner_classes()->length() == 0) { + // No inner class info => no declaring class + return NULL; + } + + typeArrayHandle i_icls(thread, k->inner_classes()); + constantPoolHandle i_cp(thread, k->constants()); + int i_length = i_icls->length(); + + bool found = false; + klassOop ok; + instanceKlassHandle outer_klass; + + // Find inner_klass attribute + for(int i = 0; i < i_length && !found; i+= 4) { + int ioff = i_icls->ushort_at(i + inner_class_info_index); + int ooff = i_icls->ushort_at(i + outer_class_info_index); + + if (ioff != 0 && ooff != 0) { + // Check to see if the name matches the class we're looking for + // before attempting to find the class. + if (i_cp->klass_name_at_matches(k, ioff)) { + klassOop inner_klass = i_cp->klass_at(ioff, CHECK_NULL); + if (k() == inner_klass) { + found = true; + ok = i_cp->klass_at(ooff, CHECK_NULL); + outer_klass = instanceKlassHandle(thread, ok); + } + } + } + } + + // If no inner class attribute found for this class. + if (!found) return NULL; + + // Throws an exception if outer klass has not declared k as an inner klass + Reflection::check_for_inner_class(outer_klass, k, CHECK_NULL); + + return (jclass)JNIHandles::make_local(env, outer_klass->java_mirror()); +JVM_END + + +JVM_ENTRY(jstring, JVM_GetClassSignature(JNIEnv *env, jclass cls)) + assert (cls != NULL, "illegal class"); + JVMWrapper("JVM_GetClassSignature"); + JvmtiVMObjectAllocEventCollector oam; + ResourceMark rm(THREAD); + // Return null for arrays and primatives + if (!java_lang_Class::is_primitive(JNIHandles::resolve(cls))) { + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve(cls)); + if (Klass::cast(k)->oop_is_instance()) { + symbolHandle sym = symbolHandle(THREAD, instanceKlass::cast(k)->generic_signature()); + if (sym.is_null()) return NULL; + Handle str = java_lang_String::create_from_symbol(sym, CHECK_NULL); + return (jstring) JNIHandles::make_local(env, str()); + } + } + return NULL; +JVM_END + + +JVM_ENTRY(jbyteArray, JVM_GetClassAnnotations(JNIEnv *env, jclass cls)) + assert (cls != NULL, "illegal class"); + JVMWrapper("JVM_GetClassAnnotations"); + ResourceMark rm(THREAD); + // Return null for arrays and primitives + if (!java_lang_Class::is_primitive(JNIHandles::resolve(cls))) { + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve(cls)); + if (Klass::cast(k)->oop_is_instance()) { + return (jbyteArray) JNIHandles::make_local(env, + instanceKlass::cast(k)->class_annotations()); + } + } + return NULL; +JVM_END + + +JVM_ENTRY(jbyteArray, JVM_GetFieldAnnotations(JNIEnv *env, jobject field)) + assert(field != NULL, "illegal field"); + JVMWrapper("JVM_GetFieldAnnotations"); + + // some of this code was adapted from from jni_FromReflectedField + + // field is a handle to a java.lang.reflect.Field object + oop reflected = JNIHandles::resolve_non_null(field); + oop mirror = java_lang_reflect_Field::clazz(reflected); + klassOop k = java_lang_Class::as_klassOop(mirror); + int slot = java_lang_reflect_Field::slot(reflected); + int modifiers = java_lang_reflect_Field::modifiers(reflected); + + fieldDescriptor fd; + KlassHandle kh(THREAD, k); + intptr_t offset = instanceKlass::cast(kh())->offset_from_fields(slot); + + if (modifiers & JVM_ACC_STATIC) { + // for static fields we only look in the current class + if (!instanceKlass::cast(kh())->find_local_field_from_offset(offset, + true, &fd)) { + assert(false, "cannot find static field"); + return NULL; // robustness + } + } else { + // for instance fields we start with the current class and work + // our way up through the superclass chain + if (!instanceKlass::cast(kh())->find_field_from_offset(offset, false, + &fd)) { + assert(false, "cannot find instance field"); + return NULL; // robustness + } + } + + return (jbyteArray) JNIHandles::make_local(env, fd.annotations()); +JVM_END + + +static methodOop jvm_get_method_common(jobject method, TRAPS) { + // some of this code was adapted from from jni_FromReflectedMethod + + oop reflected = JNIHandles::resolve_non_null(method); + oop mirror = NULL; + int slot = 0; + + if (reflected->klass() == SystemDictionary::reflect_constructor_klass()) { + mirror = java_lang_reflect_Constructor::clazz(reflected); + slot = java_lang_reflect_Constructor::slot(reflected); + } else { + assert(reflected->klass() == SystemDictionary::reflect_method_klass(), + "wrong type"); + mirror = java_lang_reflect_Method::clazz(reflected); + slot = java_lang_reflect_Method::slot(reflected); + } + klassOop k = java_lang_Class::as_klassOop(mirror); + + KlassHandle kh(THREAD, k); + methodOop m = instanceKlass::cast(kh())->method_with_idnum(slot); + if (m == NULL) { + assert(false, "cannot find method"); + return NULL; // robustness + } + + return m; +} + + +JVM_ENTRY(jbyteArray, JVM_GetMethodAnnotations(JNIEnv *env, jobject method)) + JVMWrapper("JVM_GetMethodAnnotations"); + + // method is a handle to a java.lang.reflect.Method object + methodOop m = jvm_get_method_common(method, CHECK_NULL); + return (jbyteArray) JNIHandles::make_local(env, m->annotations()); +JVM_END + + +JVM_ENTRY(jbyteArray, JVM_GetMethodDefaultAnnotationValue(JNIEnv *env, jobject method)) + JVMWrapper("JVM_GetMethodDefaultAnnotationValue"); + + // method is a handle to a java.lang.reflect.Method object + methodOop m = jvm_get_method_common(method, CHECK_NULL); + return (jbyteArray) JNIHandles::make_local(env, m->annotation_default()); +JVM_END + + +JVM_ENTRY(jbyteArray, JVM_GetMethodParameterAnnotations(JNIEnv *env, jobject method)) + JVMWrapper("JVM_GetMethodParameterAnnotations"); + + // method is a handle to a java.lang.reflect.Method object + methodOop m = jvm_get_method_common(method, CHECK_NULL); + return (jbyteArray) JNIHandles::make_local(env, m->parameter_annotations()); +JVM_END + + +// New (JDK 1.4) reflection implementation ///////////////////////////////////// + +JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredFields(JNIEnv *env, jclass ofClass, jboolean publicOnly)) +{ + JVMWrapper("JVM_GetClassDeclaredFields"); + JvmtiVMObjectAllocEventCollector oam; + + // Exclude primitive types and array types + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass)) || + Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass)))->oop_is_javaArray()) { + // Return empty array + oop res = oopFactory::new_objArray(SystemDictionary::reflect_field_klass(), 0, CHECK_NULL); + return (jobjectArray) JNIHandles::make_local(env, res); + } + + instanceKlassHandle k(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass))); + constantPoolHandle cp(THREAD, k->constants()); + + // Ensure class is linked + k->link_class(CHECK_NULL); + + typeArrayHandle fields(THREAD, k->fields()); + int fields_len = fields->length(); + + // 4496456 We need to filter out java.lang.Throwable.backtrace + bool skip_backtrace = false; + + // Allocate result + int num_fields; + + if (publicOnly) { + num_fields = 0; + for (int i = 0, j = 0; i < fields_len; i += instanceKlass::next_offset, j++) { + int mods = fields->ushort_at(i + instanceKlass::access_flags_offset) & JVM_RECOGNIZED_FIELD_MODIFIERS; + if (mods & JVM_ACC_PUBLIC) ++num_fields; + } + } else { + num_fields = fields_len / instanceKlass::next_offset; + + if (k() == SystemDictionary::throwable_klass()) { + num_fields--; + skip_backtrace = true; + } + } + + objArrayOop r = oopFactory::new_objArray(SystemDictionary::reflect_field_klass(), num_fields, CHECK_NULL); + objArrayHandle result (THREAD, r); + + int out_idx = 0; + fieldDescriptor fd; + for (int i = 0; i < fields_len; i += instanceKlass::next_offset) { + if (skip_backtrace) { + // 4496456 skip java.lang.Throwable.backtrace + int offset = k->offset_from_fields(i); + if (offset == java_lang_Throwable::get_backtrace_offset()) continue; + } + + int mods = fields->ushort_at(i + instanceKlass::access_flags_offset) & JVM_RECOGNIZED_FIELD_MODIFIERS; + if (!publicOnly || (mods & JVM_ACC_PUBLIC)) { + fd.initialize(k(), i); + oop field = Reflection::new_field(&fd, UseNewReflection, CHECK_NULL); + result->obj_at_put(out_idx, field); + ++out_idx; + } + } + assert(out_idx == num_fields, "just checking"); + return (jobjectArray) JNIHandles::make_local(env, result()); +} +JVM_END + +JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredMethods(JNIEnv *env, jclass ofClass, jboolean publicOnly)) +{ + JVMWrapper("JVM_GetClassDeclaredMethods"); + JvmtiVMObjectAllocEventCollector oam; + + // Exclude primitive types and array types + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass)) + || Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass)))->oop_is_javaArray()) { + // Return empty array + oop res = oopFactory::new_objArray(SystemDictionary::reflect_method_klass(), 0, CHECK_NULL); + return (jobjectArray) JNIHandles::make_local(env, res); + } + + instanceKlassHandle k(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass))); + + // Ensure class is linked + k->link_class(CHECK_NULL); + + objArrayHandle methods (THREAD, k->methods()); + int methods_length = methods->length(); + int num_methods = 0; + + int i; + for (i = 0; i < methods_length; i++) { + methodHandle method(THREAD, (methodOop) methods->obj_at(i)); + if (!method->is_initializer()) { + if (!publicOnly || method->is_public()) { + ++num_methods; + } + } + } + + // Allocate result + objArrayOop r = oopFactory::new_objArray(SystemDictionary::reflect_method_klass(), num_methods, CHECK_NULL); + objArrayHandle result (THREAD, r); + + int out_idx = 0; + for (i = 0; i < methods_length; i++) { + methodHandle method(THREAD, (methodOop) methods->obj_at(i)); + if (!method->is_initializer()) { + if (!publicOnly || method->is_public()) { + oop m = Reflection::new_method(method, UseNewReflection, false, CHECK_NULL); + result->obj_at_put(out_idx, m); + ++out_idx; + } + } + } + assert(out_idx == num_methods, "just checking"); + return (jobjectArray) JNIHandles::make_local(env, result()); +} +JVM_END + +JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofClass, jboolean publicOnly)) +{ + JVMWrapper("JVM_GetClassDeclaredConstructors"); + JvmtiVMObjectAllocEventCollector oam; + + // Exclude primitive types and array types + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass)) + || Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass)))->oop_is_javaArray()) { + // Return empty array + oop res = oopFactory::new_objArray(SystemDictionary::reflect_constructor_klass(), 0 , CHECK_NULL); + return (jobjectArray) JNIHandles::make_local(env, res); + } + + instanceKlassHandle k(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass))); + + // Ensure class is linked + k->link_class(CHECK_NULL); + + objArrayHandle methods (THREAD, k->methods()); + int methods_length = methods->length(); + int num_constructors = 0; + + int i; + for (i = 0; i < methods_length; i++) { + methodHandle method(THREAD, (methodOop) methods->obj_at(i)); + if (method->is_initializer() && !method->is_static()) { + if (!publicOnly || method->is_public()) { + ++num_constructors; + } + } + } + + // Allocate result + objArrayOop r = oopFactory::new_objArray(SystemDictionary::reflect_constructor_klass(), num_constructors, CHECK_NULL); + objArrayHandle result(THREAD, r); + + int out_idx = 0; + for (i = 0; i < methods_length; i++) { + methodHandle method(THREAD, (methodOop) methods->obj_at(i)); + if (method->is_initializer() && !method->is_static()) { + if (!publicOnly || method->is_public()) { + oop m = Reflection::new_constructor(method, CHECK_NULL); + result->obj_at_put(out_idx, m); + ++out_idx; + } + } + } + assert(out_idx == num_constructors, "just checking"); + return (jobjectArray) JNIHandles::make_local(env, result()); +} +JVM_END + +JVM_ENTRY(jint, JVM_GetClassAccessFlags(JNIEnv *env, jclass cls)) +{ + JVMWrapper("JVM_GetClassAccessFlags"); + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(cls))) { + // Primitive type + return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC; + } + + Klass* k = Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls))); + return k->access_flags().as_int() & JVM_ACC_WRITTEN_FLAGS; +} +JVM_END + + +// Constant pool access ////////////////////////////////////////////////////////// + +JVM_ENTRY(jobject, JVM_GetClassConstantPool(JNIEnv *env, jclass cls)) +{ + JVMWrapper("JVM_GetClassConstantPool"); + JvmtiVMObjectAllocEventCollector oam; + + // Return null for primitives and arrays + if (!java_lang_Class::is_primitive(JNIHandles::resolve_non_null(cls))) { + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + if (Klass::cast(k)->oop_is_instance()) { + instanceKlassHandle k_h(THREAD, k); + Handle jcp = sun_reflect_ConstantPool::create(CHECK_NULL); + sun_reflect_ConstantPool::set_cp_oop(jcp(), k_h->constants()); + return JNIHandles::make_local(jcp()); + } + } + return NULL; +} +JVM_END + + +JVM_ENTRY(jint, JVM_ConstantPoolGetSize(JNIEnv *env, jobject unused, jobject jcpool)) +{ + JVMWrapper("JVM_ConstantPoolGetSize"); + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + return cp->length(); +} +JVM_END + + +static void bounds_check(constantPoolHandle cp, jint index, TRAPS) { + if (!cp->is_within_bounds(index)) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Constant pool index out of bounds"); + } +} + + +JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAt(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetClassAt"); + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_NULL); + constantTag tag = cp->tag_at(index); + if (!tag.is_klass() && !tag.is_unresolved_klass()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + klassOop k = cp->klass_at(index, CHECK_NULL); + return (jclass) JNIHandles::make_local(k->klass_part()->java_mirror()); +} +JVM_END + + +JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAtIfLoaded(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetClassAtIfLoaded"); + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_NULL); + constantTag tag = cp->tag_at(index); + if (!tag.is_klass() && !tag.is_unresolved_klass()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + klassOop k = constantPoolOopDesc::klass_at_if_loaded(cp, index); + if (k == NULL) return NULL; + return (jclass) JNIHandles::make_local(k->klass_part()->java_mirror()); +} +JVM_END + +static jobject get_method_at_helper(constantPoolHandle cp, jint index, bool force_resolution, TRAPS) { + constantTag tag = cp->tag_at(index); + if (!tag.is_method() && !tag.is_interface_method()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + int klass_ref = cp->uncached_klass_ref_index_at(index); + klassOop k_o; + if (force_resolution) { + k_o = cp->klass_at(klass_ref, CHECK_NULL); + } else { + k_o = constantPoolOopDesc::klass_at_if_loaded(cp, klass_ref); + if (k_o == NULL) return NULL; + } + instanceKlassHandle k(THREAD, k_o); + symbolOop name = cp->uncached_name_ref_at(index); + symbolOop sig = cp->uncached_signature_ref_at(index); + methodHandle m (THREAD, k->find_method(name, sig)); + if (m.is_null()) { + THROW_MSG_0(vmSymbols::java_lang_RuntimeException(), "Unable to look up method in target class"); + } + oop method; + if (!m->is_initializer() || m->is_static()) { + method = Reflection::new_method(m, true, true, CHECK_NULL); + } else { + method = Reflection::new_constructor(m, CHECK_NULL); + } + return JNIHandles::make_local(method); +} + +JVM_ENTRY(jobject, JVM_ConstantPoolGetMethodAt(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetMethodAt"); + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_NULL); + jobject res = get_method_at_helper(cp, index, true, CHECK_NULL); + return res; +} +JVM_END + +JVM_ENTRY(jobject, JVM_ConstantPoolGetMethodAtIfLoaded(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetMethodAtIfLoaded"); + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_NULL); + jobject res = get_method_at_helper(cp, index, false, CHECK_NULL); + return res; +} +JVM_END + +static jobject get_field_at_helper(constantPoolHandle cp, jint index, bool force_resolution, TRAPS) { + constantTag tag = cp->tag_at(index); + if (!tag.is_field()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + int klass_ref = cp->uncached_klass_ref_index_at(index); + klassOop k_o; + if (force_resolution) { + k_o = cp->klass_at(klass_ref, CHECK_NULL); + } else { + k_o = constantPoolOopDesc::klass_at_if_loaded(cp, klass_ref); + if (k_o == NULL) return NULL; + } + instanceKlassHandle k(THREAD, k_o); + symbolOop name = cp->uncached_name_ref_at(index); + symbolOop sig = cp->uncached_signature_ref_at(index); + fieldDescriptor fd; + klassOop target_klass = k->find_field(name, sig, &fd); + if (target_klass == NULL) { + THROW_MSG_0(vmSymbols::java_lang_RuntimeException(), "Unable to look up field in target class"); + } + oop field = Reflection::new_field(&fd, true, CHECK_NULL); + return JNIHandles::make_local(field); +} + +JVM_ENTRY(jobject, JVM_ConstantPoolGetFieldAt(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetFieldAt"); + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_NULL); + jobject res = get_field_at_helper(cp, index, true, CHECK_NULL); + return res; +} +JVM_END + +JVM_ENTRY(jobject, JVM_ConstantPoolGetFieldAtIfLoaded(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetFieldAtIfLoaded"); + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_NULL); + jobject res = get_field_at_helper(cp, index, false, CHECK_NULL); + return res; +} +JVM_END + +JVM_ENTRY(jobjectArray, JVM_ConstantPoolGetMemberRefInfoAt(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetMemberRefInfoAt"); + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_NULL); + constantTag tag = cp->tag_at(index); + if (!tag.is_field_or_method()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + int klass_ref = cp->uncached_klass_ref_index_at(index); + symbolHandle klass_name (THREAD, cp->klass_name_at(klass_ref)); + symbolHandle member_name(THREAD, cp->uncached_name_ref_at(index)); + symbolHandle member_sig (THREAD, cp->uncached_signature_ref_at(index)); + objArrayOop dest_o = oopFactory::new_objArray(SystemDictionary::string_klass(), 3, CHECK_NULL); + objArrayHandle dest(THREAD, dest_o); + Handle str = java_lang_String::create_from_symbol(klass_name, CHECK_NULL); + dest->obj_at_put(0, str()); + str = java_lang_String::create_from_symbol(member_name, CHECK_NULL); + dest->obj_at_put(1, str()); + str = java_lang_String::create_from_symbol(member_sig, CHECK_NULL); + dest->obj_at_put(2, str()); + return (jobjectArray) JNIHandles::make_local(dest()); +} +JVM_END + +JVM_ENTRY(jint, JVM_ConstantPoolGetIntAt(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetIntAt"); + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_0); + constantTag tag = cp->tag_at(index); + if (!tag.is_int()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + return cp->int_at(index); +} +JVM_END + +JVM_ENTRY(jlong, JVM_ConstantPoolGetLongAt(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetLongAt"); + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_(0L)); + constantTag tag = cp->tag_at(index); + if (!tag.is_long()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + return cp->long_at(index); +} +JVM_END + +JVM_ENTRY(jfloat, JVM_ConstantPoolGetFloatAt(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetFloatAt"); + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_(0.0f)); + constantTag tag = cp->tag_at(index); + if (!tag.is_float()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + return cp->float_at(index); +} +JVM_END + +JVM_ENTRY(jdouble, JVM_ConstantPoolGetDoubleAt(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetDoubleAt"); + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_(0.0)); + constantTag tag = cp->tag_at(index); + if (!tag.is_double()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + return cp->double_at(index); +} +JVM_END + +JVM_ENTRY(jstring, JVM_ConstantPoolGetStringAt(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetStringAt"); + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_NULL); + constantTag tag = cp->tag_at(index); + if (!tag.is_string() && !tag.is_unresolved_string()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + oop str = cp->string_at(index, CHECK_NULL); + return (jstring) JNIHandles::make_local(str); +} +JVM_END + +JVM_ENTRY(jstring, JVM_ConstantPoolGetUTF8At(JNIEnv *env, jobject unused, jobject jcpool, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetUTF8At"); + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp = constantPoolHandle(THREAD, constantPoolOop(JNIHandles::resolve_non_null(jcpool))); + bounds_check(cp, index, CHECK_NULL); + constantTag tag = cp->tag_at(index); + if (!tag.is_symbol()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + symbolOop sym_o = cp->symbol_at(index); + symbolHandle sym(THREAD, sym_o); + Handle str = java_lang_String::create_from_symbol(sym, CHECK_NULL); + return (jstring) JNIHandles::make_local(str()); +} +JVM_END + + +// Assertion support. ////////////////////////////////////////////////////////// + +JVM_ENTRY(jboolean, JVM_DesiredAssertionStatus(JNIEnv *env, jclass unused, jclass cls)) + JVMWrapper("JVM_DesiredAssertionStatus"); + assert(cls != NULL, "bad class"); + + oop r = JNIHandles::resolve(cls); + assert(! java_lang_Class::is_primitive(r), "primitive classes not allowed"); + if (java_lang_Class::is_primitive(r)) return false; + + klassOop k = java_lang_Class::as_klassOop(r); + assert(Klass::cast(k)->oop_is_instance(), "must be an instance klass"); + if (! Klass::cast(k)->oop_is_instance()) return false; + + ResourceMark rm(THREAD); + const char* name = Klass::cast(k)->name()->as_C_string(); + bool system_class = Klass::cast(k)->class_loader() == NULL; + return JavaAssertions::enabled(name, system_class); + +JVM_END + + +// Return a new AssertionStatusDirectives object with the fields filled in with +// command-line assertion arguments (i.e., -ea, -da). +JVM_ENTRY(jobject, JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused)) + JVMWrapper("JVM_AssertionStatusDirectives"); + JvmtiVMObjectAllocEventCollector oam; + oop asd = JavaAssertions::createAssertionStatusDirectives(CHECK_NULL); + return JNIHandles::make_local(env, asd); +JVM_END + +// Verification //////////////////////////////////////////////////////////////////////////////// + +// Reflection for the verifier ///////////////////////////////////////////////////////////////// + +// RedefineClasses support: bug 6214132 caused verification to fail. +// All functions from this section should call the jvmtiThreadSate function: +// klassOop class_to_verify_considering_redefinition(klassOop klass). +// The function returns a klassOop of the _scratch_class if the verifier +// was invoked in the middle of the class redefinition. +// Otherwise it returns its argument value which is the _the_class klassOop. +// Please, refer to the description in the jvmtiThreadSate.hpp. + +JVM_ENTRY(const char*, JVM_GetClassNameUTF(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetClassNameUTF"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + return Klass::cast(k)->name()->as_utf8(); +JVM_END + + +JVM_QUICK_ENTRY(void, JVM_GetClassCPTypes(JNIEnv *env, jclass cls, unsigned char *types)) + JVMWrapper("JVM_GetClassCPTypes"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + // types will have length zero if this is not an instanceKlass + // (length is determined by call to JVM_GetClassCPEntriesCount) + if (Klass::cast(k)->oop_is_instance()) { + constantPoolOop cp = instanceKlass::cast(k)->constants(); + for (int index = cp->length() - 1; index >= 0; index--) { + constantTag tag = cp->tag_at(index); + types[index] = (tag.is_unresolved_klass()) ? JVM_CONSTANT_Class : + (tag.is_unresolved_string()) ? JVM_CONSTANT_String : tag.value(); + } + } +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetClassCPEntriesCount(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetClassCPEntriesCount"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + if (!Klass::cast(k)->oop_is_instance()) + return 0; + return instanceKlass::cast(k)->constants()->length(); +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetClassFieldsCount(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetClassFieldsCount"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + if (!Klass::cast(k)->oop_is_instance()) + return 0; + return instanceKlass::cast(k)->fields()->length() / instanceKlass::next_offset; +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetClassMethodsCount(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_GetClassMethodsCount"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + if (!Klass::cast(k)->oop_is_instance()) + return 0; + return instanceKlass::cast(k)->methods()->length(); +JVM_END + + +// The following methods, used for the verifier, are never called with +// array klasses, so a direct cast to instanceKlass is safe. +// Typically, these methods are called in a loop with bounds determined +// by the results of JVM_GetClass{Fields,Methods}Count, which return +// zero for arrays. +JVM_QUICK_ENTRY(void, JVM_GetMethodIxExceptionIndexes(JNIEnv *env, jclass cls, jint method_index, unsigned short *exceptions)) + JVMWrapper("JVM_GetMethodIxExceptionIndexes"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + int length = methodOop(method)->checked_exceptions_length(); + if (length > 0) { + CheckedExceptionElement* table= methodOop(method)->checked_exceptions_start(); + for (int i = 0; i < length; i++) { + exceptions[i] = table[i].class_cp_index; + } + } +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetMethodIxExceptionsCount(JNIEnv *env, jclass cls, jint method_index)) + JVMWrapper("JVM_GetMethodIxExceptionsCount"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + return methodOop(method)->checked_exceptions_length(); +JVM_END + + +JVM_QUICK_ENTRY(void, JVM_GetMethodIxByteCode(JNIEnv *env, jclass cls, jint method_index, unsigned char *code)) + JVMWrapper("JVM_GetMethodIxByteCode"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + memcpy(code, methodOop(method)->code_base(), methodOop(method)->code_size()); +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetMethodIxByteCodeLength(JNIEnv *env, jclass cls, jint method_index)) + JVMWrapper("JVM_GetMethodIxByteCodeLength"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + return methodOop(method)->code_size(); +JVM_END + + +JVM_QUICK_ENTRY(void, JVM_GetMethodIxExceptionTableEntry(JNIEnv *env, jclass cls, jint method_index, jint entry_index, JVM_ExceptionTableEntryType *entry)) + JVMWrapper("JVM_GetMethodIxExceptionTableEntry"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + typeArrayOop extable = methodOop(method)->exception_table(); + entry->start_pc = extable->int_at(entry_index * 4); + entry->end_pc = extable->int_at(entry_index * 4 + 1); + entry->handler_pc = extable->int_at(entry_index * 4 + 2); + entry->catchType = extable->int_at(entry_index * 4 + 3); +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetMethodIxExceptionTableLength(JNIEnv *env, jclass cls, int method_index)) + JVMWrapper("JVM_GetMethodIxExceptionTableLength"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + return methodOop(method)->exception_table()->length() / 4; +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetMethodIxModifiers(JNIEnv *env, jclass cls, int method_index)) + JVMWrapper("JVM_GetMethodIxModifiers"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + return methodOop(method)->access_flags().as_int() & JVM_RECOGNIZED_METHOD_MODIFIERS; +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetFieldIxModifiers(JNIEnv *env, jclass cls, int field_index)) + JVMWrapper("JVM_GetFieldIxModifiers"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + typeArrayOop fields = instanceKlass::cast(k)->fields(); + return fields->ushort_at(field_index * instanceKlass::next_offset + instanceKlass::access_flags_offset) & JVM_RECOGNIZED_FIELD_MODIFIERS; +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetMethodIxLocalsCount(JNIEnv *env, jclass cls, int method_index)) + JVMWrapper("JVM_GetMethodIxLocalsCount"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + return methodOop(method)->max_locals(); +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetMethodIxArgsSize(JNIEnv *env, jclass cls, int method_index)) + JVMWrapper("JVM_GetMethodIxArgsSize"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + return methodOop(method)->size_of_parameters(); +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetMethodIxMaxStack(JNIEnv *env, jclass cls, int method_index)) + JVMWrapper("JVM_GetMethodIxMaxStack"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + return methodOop(method)->max_stack(); +JVM_END + + +JVM_QUICK_ENTRY(jboolean, JVM_IsConstructorIx(JNIEnv *env, jclass cls, int method_index)) + JVMWrapper("JVM_IsConstructorIx"); + ResourceMark rm(THREAD); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + return methodOop(method)->name() == vmSymbols::object_initializer_name(); +JVM_END + + +JVM_ENTRY(const char*, JVM_GetMethodIxNameUTF(JNIEnv *env, jclass cls, jint method_index)) + JVMWrapper("JVM_GetMethodIxIxUTF"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + return methodOop(method)->name()->as_utf8(); +JVM_END + + +JVM_ENTRY(const char*, JVM_GetMethodIxSignatureUTF(JNIEnv *env, jclass cls, jint method_index)) + JVMWrapper("JVM_GetMethodIxSignatureUTF"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + oop method = instanceKlass::cast(k)->methods()->obj_at(method_index); + return methodOop(method)->signature()->as_utf8(); +JVM_END + +/** + * All of these JVM_GetCP-xxx methods are used by the old verifier to + * read entries in the constant pool. Since the old verifier always + * works on a copy of the code, it will not see any rewriting that + * may possibly occur in the middle of verification. So it is important + * that nothing it calls tries to use the cpCache instead of the raw + * constant pool, so we must use cp->uncached_x methods when appropriate. + */ +JVM_ENTRY(const char*, JVM_GetCPFieldNameUTF(JNIEnv *env, jclass cls, jint cp_index)) + JVMWrapper("JVM_GetCPFieldNameUTF"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + constantPoolOop cp = instanceKlass::cast(k)->constants(); + switch (cp->tag_at(cp_index).value()) { + case JVM_CONSTANT_Fieldref: + return cp->uncached_name_ref_at(cp_index)->as_utf8(); + default: + fatal("JVM_GetCPFieldNameUTF: illegal constant"); + } + ShouldNotReachHere(); + return NULL; +JVM_END + + +JVM_ENTRY(const char*, JVM_GetCPMethodNameUTF(JNIEnv *env, jclass cls, jint cp_index)) + JVMWrapper("JVM_GetCPMethodNameUTF"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + constantPoolOop cp = instanceKlass::cast(k)->constants(); + switch (cp->tag_at(cp_index).value()) { + case JVM_CONSTANT_InterfaceMethodref: + case JVM_CONSTANT_Methodref: + return cp->uncached_name_ref_at(cp_index)->as_utf8(); + default: + fatal("JVM_GetCPMethodNameUTF: illegal constant"); + } + ShouldNotReachHere(); + return NULL; +JVM_END + + +JVM_ENTRY(const char*, JVM_GetCPMethodSignatureUTF(JNIEnv *env, jclass cls, jint cp_index)) + JVMWrapper("JVM_GetCPMethodSignatureUTF"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + constantPoolOop cp = instanceKlass::cast(k)->constants(); + switch (cp->tag_at(cp_index).value()) { + case JVM_CONSTANT_InterfaceMethodref: + case JVM_CONSTANT_Methodref: + return cp->uncached_signature_ref_at(cp_index)->as_utf8(); + default: + fatal("JVM_GetCPMethodSignatureUTF: illegal constant"); + } + ShouldNotReachHere(); + return NULL; +JVM_END + + +JVM_ENTRY(const char*, JVM_GetCPFieldSignatureUTF(JNIEnv *env, jclass cls, jint cp_index)) + JVMWrapper("JVM_GetCPFieldSignatureUTF"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + constantPoolOop cp = instanceKlass::cast(k)->constants(); + switch (cp->tag_at(cp_index).value()) { + case JVM_CONSTANT_Fieldref: + return cp->uncached_signature_ref_at(cp_index)->as_utf8(); + default: + fatal("JVM_GetCPFieldSignatureUTF: illegal constant"); + } + ShouldNotReachHere(); + return NULL; +JVM_END + + +JVM_ENTRY(const char*, JVM_GetCPClassNameUTF(JNIEnv *env, jclass cls, jint cp_index)) + JVMWrapper("JVM_GetCPClassNameUTF"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + constantPoolOop cp = instanceKlass::cast(k)->constants(); + symbolOop classname = cp->klass_name_at(cp_index); + return classname->as_utf8(); +JVM_END + + +JVM_ENTRY(const char*, JVM_GetCPFieldClassNameUTF(JNIEnv *env, jclass cls, jint cp_index)) + JVMWrapper("JVM_GetCPFieldClassNameUTF"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + constantPoolOop cp = instanceKlass::cast(k)->constants(); + switch (cp->tag_at(cp_index).value()) { + case JVM_CONSTANT_Fieldref: { + int class_index = cp->uncached_klass_ref_index_at(cp_index); + symbolOop classname = cp->klass_name_at(class_index); + return classname->as_utf8(); + } + default: + fatal("JVM_GetCPFieldClassNameUTF: illegal constant"); + } + ShouldNotReachHere(); + return NULL; +JVM_END + + +JVM_ENTRY(const char*, JVM_GetCPMethodClassNameUTF(JNIEnv *env, jclass cls, jint cp_index)) + JVMWrapper("JVM_GetCPMethodClassNameUTF"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + constantPoolOop cp = instanceKlass::cast(k)->constants(); + switch (cp->tag_at(cp_index).value()) { + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: { + int class_index = cp->uncached_klass_ref_index_at(cp_index); + symbolOop classname = cp->klass_name_at(class_index); + return classname->as_utf8(); + } + default: + fatal("JVM_GetCPMethodClassNameUTF: illegal constant"); + } + ShouldNotReachHere(); + return NULL; +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetCPFieldModifiers(JNIEnv *env, jclass cls, int cp_index, jclass called_cls)) + JVMWrapper("JVM_GetCPFieldModifiers"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + klassOop k_called = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(called_cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + k_called = JvmtiThreadState::class_to_verify_considering_redefinition(k_called, thread); + constantPoolOop cp = instanceKlass::cast(k)->constants(); + constantPoolOop cp_called = instanceKlass::cast(k_called)->constants(); + switch (cp->tag_at(cp_index).value()) { + case JVM_CONSTANT_Fieldref: { + symbolOop name = cp->uncached_name_ref_at(cp_index); + symbolOop signature = cp->uncached_signature_ref_at(cp_index); + typeArrayOop fields = instanceKlass::cast(k_called)->fields(); + int fields_count = fields->length(); + for (int i = 0; i < fields_count; i += instanceKlass::next_offset) { + if (cp_called->symbol_at(fields->ushort_at(i + instanceKlass::name_index_offset)) == name && + cp_called->symbol_at(fields->ushort_at(i + instanceKlass::signature_index_offset)) == signature) { + return fields->ushort_at(i + instanceKlass::access_flags_offset) & JVM_RECOGNIZED_FIELD_MODIFIERS; + } + } + return -1; + } + default: + fatal("JVM_GetCPFieldModifiers: illegal constant"); + } + ShouldNotReachHere(); + return 0; +JVM_END + + +JVM_QUICK_ENTRY(jint, JVM_GetCPMethodModifiers(JNIEnv *env, jclass cls, int cp_index, jclass called_cls)) + JVMWrapper("JVM_GetCPMethodModifiers"); + klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls)); + klassOop k_called = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(called_cls)); + k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread); + k_called = JvmtiThreadState::class_to_verify_considering_redefinition(k_called, thread); + constantPoolOop cp = instanceKlass::cast(k)->constants(); + switch (cp->tag_at(cp_index).value()) { + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: { + symbolOop name = cp->uncached_name_ref_at(cp_index); + symbolOop signature = cp->uncached_signature_ref_at(cp_index); + objArrayOop methods = instanceKlass::cast(k_called)->methods(); + int methods_count = methods->length(); + for (int i = 0; i < methods_count; i++) { + methodOop method = methodOop(methods->obj_at(i)); + if (method->name() == name && method->signature() == signature) { + return method->access_flags().as_int() & JVM_RECOGNIZED_METHOD_MODIFIERS; + } + } + return -1; + } + default: + fatal("JVM_GetCPMethodModifiers: illegal constant"); + } + ShouldNotReachHere(); + return 0; +JVM_END + + +// Misc ////////////////////////////////////////////////////////////////////////////////////////////// + +JVM_LEAF(void, JVM_ReleaseUTF(const char *utf)) + // So long as UTF8::convert_to_utf8 returns resource strings, we don't have to do anything +JVM_END + + +JVM_ENTRY(jboolean, JVM_IsSameClassPackage(JNIEnv *env, jclass class1, jclass class2)) + JVMWrapper("JVM_IsSameClassPackage"); + oop class1_mirror = JNIHandles::resolve_non_null(class1); + oop class2_mirror = JNIHandles::resolve_non_null(class2); + klassOop klass1 = java_lang_Class::as_klassOop(class1_mirror); + klassOop klass2 = java_lang_Class::as_klassOop(class2_mirror); + return (jboolean) Reflection::is_same_class_package(klass1, klass2); +JVM_END + + +// IO functions //////////////////////////////////////////////////////////////////////////////////////// + +JVM_LEAF(jint, JVM_Open(const char *fname, jint flags, jint mode)) + JVMWrapper2("JVM_Open (%s)", fname); + + //%note jvm_r6 + int result = hpi::open(fname, flags, mode); + if (result >= 0) { + return result; + } else { + switch(errno) { + case EEXIST: + return JVM_EEXIST; + default: + return -1; + } + } +JVM_END + + +JVM_LEAF(jint, JVM_Close(jint fd)) + JVMWrapper2("JVM_Close (0x%x)", fd); + //%note jvm_r6 + return hpi::close(fd); +JVM_END + + +JVM_LEAF(jint, JVM_Read(jint fd, char *buf, jint nbytes)) + JVMWrapper2("JVM_Read (0x%x)", fd); + + //%note jvm_r6 + return (jint)hpi::read(fd, buf, nbytes); +JVM_END + + +JVM_LEAF(jint, JVM_Write(jint fd, char *buf, jint nbytes)) + JVMWrapper2("JVM_Write (0x%x)", fd); + + //%note jvm_r6 + return (jint)hpi::write(fd, buf, nbytes); +JVM_END + + +JVM_LEAF(jint, JVM_Available(jint fd, jlong *pbytes)) + JVMWrapper2("JVM_Available (0x%x)", fd); + //%note jvm_r6 + return hpi::available(fd, pbytes); +JVM_END + + +JVM_LEAF(jlong, JVM_Lseek(jint fd, jlong offset, jint whence)) + JVMWrapper4("JVM_Lseek (0x%x, %Ld, %d)", fd, offset, whence); + //%note jvm_r6 + return hpi::lseek(fd, offset, whence); +JVM_END + + +JVM_LEAF(jint, JVM_SetLength(jint fd, jlong length)) + JVMWrapper3("JVM_SetLength (0x%x, %Ld)", fd, length); + return hpi::ftruncate(fd, length); +JVM_END + + +JVM_LEAF(jint, JVM_Sync(jint fd)) + JVMWrapper2("JVM_Sync (0x%x)", fd); + //%note jvm_r6 + return hpi::fsync(fd); +JVM_END + + +// Printing support ////////////////////////////////////////////////// +extern "C" { + +int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { + // see bug 4399518, 4417214 + if ((intptr_t)count <= 0) return -1; + return vsnprintf(str, count, fmt, args); +} + + +int jio_snprintf(char *str, size_t count, const char *fmt, ...) { + va_list args; + int len; + va_start(args, fmt); + len = jio_vsnprintf(str, count, fmt, args); + va_end(args); + return len; +} + + +int jio_fprintf(FILE* f, const char *fmt, ...) { + int len; + va_list args; + va_start(args, fmt); + len = jio_vfprintf(f, fmt, args); + va_end(args); + return len; +} + + +int jio_vfprintf(FILE* f, const char *fmt, va_list args) { + if (Arguments::vfprintf_hook() != NULL) { + return Arguments::vfprintf_hook()(f, fmt, args); + } else { + return vfprintf(f, fmt, args); + } +} + + +int jio_printf(const char *fmt, ...) { + int len; + va_list args; + va_start(args, fmt); + len = jio_vfprintf(defaultStream::output_stream(), fmt, args); + va_end(args); + return len; +} + + +// HotSpot specific jio method +void jio_print(const char* s) { + // Try to make this function as atomic as possible. + if (Arguments::vfprintf_hook() != NULL) { + jio_fprintf(defaultStream::output_stream(), "%s", s); + } else { + ::write(defaultStream::output_fd(), s, (int)strlen(s)); + } +} + +} // Extern C + +// java.lang.Thread ////////////////////////////////////////////////////////////////////////////// + +// In most of the JVM Thread support functions we need to be sure to lock the Threads_lock +// to prevent the target thread from exiting after we have a pointer to the C++ Thread or +// OSThread objects. The exception to this rule is when the target object is the thread +// doing the operation, in which case we know that the thread won't exit until the +// operation is done (all exits being voluntary). There are a few cases where it is +// rather silly to do operations on yourself, like resuming yourself or asking whether +// you are alive. While these can still happen, they are not subject to deadlocks if +// the lock is held while the operation occurs (this is not the case for suspend, for +// instance), and are very unlikely. Because IsAlive needs to be fast and its +// implementation is local to this file, we always lock Threads_lock for that one. + +static void thread_entry(JavaThread* thread, TRAPS) { + HandleMark hm(THREAD); + Handle obj(THREAD, thread->threadObj()); + JavaValue result(T_VOID); + JavaCalls::call_virtual(&result, + obj, + KlassHandle(THREAD, SystemDictionary::thread_klass()), + vmSymbolHandles::run_method_name(), + vmSymbolHandles::void_method_signature(), + THREAD); +} + + +JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) + JVMWrapper("JVM_StartThread"); + JavaThread *native_thread = NULL; + + // We cannot hold the Threads_lock when we throw an exception, + // due to rank ordering issues. Example: we might need to grab the + // Heap_lock while we construct the exception. + bool throw_illegal_thread_state = false; + + // We must release the Threads_lock before we can post a jvmti event + // in Thread::start. + { + // Ensure that the C++ Thread and OSThread structures aren't freed before + // we operate. + MutexLocker mu(Threads_lock); + + // Check to see if we're running a thread that's already exited or was + // stopped (is_stillborn) or is still active (thread is not NULL). + if (java_lang_Thread::is_stillborn(JNIHandles::resolve_non_null(jthread)) || + java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) { + throw_illegal_thread_state = true; + } else { + jlong size = + java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread)); + // Allocate the C++ Thread structure and create the native thread. The + // stack size retrieved from java is signed, but the constructor takes + // size_t (an unsigned type), so avoid passing negative values which would + // result in really large stacks. + size_t sz = size > 0 ? (size_t) size : 0; + native_thread = new JavaThread(&thread_entry, sz); + + // At this point it may be possible that no osthread was created for the + // JavaThread due to lack of memory. Check for this situation and throw + // an exception if necessary. Eventually we may want to change this so + // that we only grab the lock if the thread was created successfully - + // then we can also do this check and throw the exception in the + // JavaThread constructor. + if (native_thread->osthread() != NULL) { + // Note: the current thread is not being used within "prepare". + native_thread->prepare(jthread); + } + } + } + + if (throw_illegal_thread_state) { + THROW(vmSymbols::java_lang_IllegalThreadStateException()); + } + + assert(native_thread != NULL, "Starting null thread?"); + + if (native_thread->osthread() == NULL) { + // No one should hold a reference to the 'native_thread'. + delete native_thread; + if (JvmtiExport::should_post_resource_exhausted()) { + JvmtiExport::post_resource_exhausted( + JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS, + "unable to create new native thread"); + } + THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), + "unable to create new native thread"); + } + + Thread::start(native_thread); + +JVM_END + +// JVM_Stop is implemented using a VM_Operation, so threads are forced to safepoints +// before the quasi-asynchronous exception is delivered. This is a little obtrusive, +// but is thought to be reliable and simple. In the case, where the receiver is the +// save thread as the sender, no safepoint is needed. +JVM_ENTRY(void, JVM_StopThread(JNIEnv* env, jobject jthread, jobject throwable)) + JVMWrapper("JVM_StopThread"); + + oop java_throwable = JNIHandles::resolve(throwable); + if (java_throwable == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + oop java_thread = JNIHandles::resolve_non_null(jthread); + JavaThread* receiver = java_lang_Thread::thread(java_thread); + Events::log("JVM_StopThread thread JavaThread " INTPTR_FORMAT " as oop " INTPTR_FORMAT " [exception " INTPTR_FORMAT "]", receiver, (address)java_thread, throwable); + // First check if thread already exited + if (receiver != NULL) { + // Check if exception is getting thrown at self (use oop equality, since the + // target object might exit) + if (java_thread == thread->threadObj()) { + // This is a change from JDK 1.1, but JDK 1.2 will also do it: + // NOTE (from JDK 1.2): this is done solely to prevent stopped + // threads from being restarted. + // Fix for 4314342, 4145910, perhaps others: it now doesn't have + // any effect on the "liveness" of a thread; see + // JVM_IsThreadAlive, below. + if (java_throwable->is_a(SystemDictionary::threaddeath_klass())) { + java_lang_Thread::set_stillborn(java_thread); + } + THROW_OOP(java_throwable); + } else { + // Enques a VM_Operation to stop all threads and then deliver the exception... + Thread::send_async_exception(java_thread, JNIHandles::resolve(throwable)); + } + } +JVM_END + + +JVM_ENTRY(jboolean, JVM_IsThreadAlive(JNIEnv* env, jobject jthread)) + JVMWrapper("JVM_IsThreadAlive"); + + oop thread_oop = JNIHandles::resolve_non_null(jthread); + return java_lang_Thread::is_alive(thread_oop); +JVM_END + + +JVM_ENTRY(void, JVM_SuspendThread(JNIEnv* env, jobject jthread)) + JVMWrapper("JVM_SuspendThread"); + oop java_thread = JNIHandles::resolve_non_null(jthread); + JavaThread* receiver = java_lang_Thread::thread(java_thread); + + if (receiver != NULL) { + // thread has run and has not exited (still on threads list) + + { + MutexLockerEx ml(receiver->SR_lock(), Mutex::_no_safepoint_check_flag); + if (receiver->is_external_suspend()) { + // Don't allow nested external suspend requests. We can't return + // an error from this interface so just ignore the problem. + return; + } + if (receiver->is_exiting()) { // thread is in the process of exiting + return; + } + receiver->set_external_suspend(); + } + + // java_suspend() will catch threads in the process of exiting + // and will ignore them. + receiver->java_suspend(); + + // It would be nice to have the following assertion in all the + // time, but it is possible for a racing resume request to have + // resumed this thread right after we suspended it. Temporarily + // enable this assertion if you are chasing a different kind of + // bug. + // + // assert(java_lang_Thread::thread(receiver->threadObj()) == NULL || + // receiver->is_being_ext_suspended(), "thread is not suspended"); + } +JVM_END + + +JVM_ENTRY(void, JVM_ResumeThread(JNIEnv* env, jobject jthread)) + JVMWrapper("JVM_ResumeThread"); + // Ensure that the C++ Thread and OSThread structures aren't freed before we operate. + // We need to *always* get the threads lock here, since this operation cannot be allowed during + // a safepoint. The safepoint code relies on suspending a thread to examine its state. If other + // threads randomly resumes threads, then a thread might not be suspended when the safepoint code + // looks at it. + MutexLocker ml(Threads_lock); + JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)); + if (thr != NULL) { + // the thread has run and is not in the process of exiting + thr->java_resume(); + } +JVM_END + + +JVM_ENTRY(void, JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio)) + JVMWrapper("JVM_SetThreadPriority"); + // Ensure that the C++ Thread and OSThread structures aren't freed before we operate + MutexLocker ml(Threads_lock); + oop java_thread = JNIHandles::resolve_non_null(jthread); + java_lang_Thread::set_priority(java_thread, (ThreadPriority)prio); + JavaThread* thr = java_lang_Thread::thread(java_thread); + if (thr != NULL) { // Thread not yet started; priority pushed down when it is + Thread::set_priority(thr, (ThreadPriority)prio); + } +JVM_END + + +JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass)) + JVMWrapper("JVM_Yield"); + if (os::dont_yield()) return; + // When ConvertYieldToSleep is off (default), this matches the classic VM use of yield. + // Critical for similar threading behaviour + if (ConvertYieldToSleep) { + os::sleep(thread, MinSleepInterval, false); + } else { + os::yield(); + } +JVM_END + + +JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis)) + JVMWrapper("JVM_Sleep"); + + if (millis < 0) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative"); + } + + if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) { + THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted"); + } + + // Save current thread state and restore it at the end of this block. + // And set new thread state to SLEEPING. + JavaThreadSleepState jtss(thread); + + if (millis == 0) { + // When ConvertSleepToYield is on, this matches the classic VM implementation of + // JVM_Sleep. Critical for similar threading behaviour (Win32) + // It appears that in certain GUI contexts, it may be beneficial to do a short sleep + // for SOLARIS + if (ConvertSleepToYield) { + os::yield(); + } else { + ThreadState old_state = thread->osthread()->get_state(); + thread->osthread()->set_state(SLEEPING); + os::sleep(thread, MinSleepInterval, false); + thread->osthread()->set_state(old_state); + } + } else { + ThreadState old_state = thread->osthread()->get_state(); + thread->osthread()->set_state(SLEEPING); + if (os::sleep(thread, millis, true) == OS_INTRPT) { + // An asynchronous exception (e.g., ThreadDeathException) could have been thrown on + // us while we were sleeping. We do not overwrite those. + if (!HAS_PENDING_EXCEPTION) { + // TODO-FIXME: THROW_MSG returns which means we will not call set_state() + // to properly restore the thread state. That's likely wrong. + THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted"); + } + } + thread->osthread()->set_state(old_state); + } +JVM_END + +JVM_ENTRY(jobject, JVM_CurrentThread(JNIEnv* env, jclass threadClass)) + JVMWrapper("JVM_CurrentThread"); + oop jthread = thread->threadObj(); + assert (thread != NULL, "no current thread!"); + return JNIHandles::make_local(env, jthread); +JVM_END + + +JVM_ENTRY(jint, JVM_CountStackFrames(JNIEnv* env, jobject jthread)) + JVMWrapper("JVM_CountStackFrames"); + + // Ensure that the C++ Thread and OSThread structures aren't freed before we operate + oop java_thread = JNIHandles::resolve_non_null(jthread); + bool throw_illegal_thread_state = false; + int count = 0; + + { + MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock); + // We need to re-resolve the java_thread, since a GC might have happened during the + // acquire of the lock + JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)); + + if (thr == NULL) { + // do nothing + } else if(! thr->is_external_suspend() || ! thr->frame_anchor()->walkable()) { + // Check whether this java thread has been suspended already. If not, throws + // IllegalThreadStateException. We defer to throw that exception until + // Threads_lock is released since loading exception class has to leave VM. + // The correct way to test a thread is actually suspended is + // wait_for_ext_suspend_completion(), but we can't call that while holding + // the Threads_lock. The above tests are sufficient for our purposes + // provided the walkability of the stack is stable - which it isn't + // 100% but close enough for most practical purposes. + throw_illegal_thread_state = true; + } else { + // Count all java activation, i.e., number of vframes + for(vframeStream vfst(thr); !vfst.at_end(); vfst.next()) { + // Native frames are not counted + if (!vfst.method()->is_native()) count++; + } + } + } + + if (throw_illegal_thread_state) { + THROW_MSG_0(vmSymbols::java_lang_IllegalThreadStateException(), + "this thread is not suspended"); + } + return count; +JVM_END + +// Consider: A better way to implement JVM_Interrupt() is to acquire +// Threads_lock to resolve the jthread into a Thread pointer, fetch +// Thread->platformevent, Thread->native_thr, Thread->parker, etc., +// drop Threads_lock, and the perform the unpark() and thr_kill() operations +// outside the critical section. Threads_lock is hot so we want to minimize +// the hold-time. A cleaner interface would be to decompose interrupt into +// two steps. The 1st phase, performed under Threads_lock, would return +// a closure that'd be invoked after Threads_lock was dropped. +// This tactic is safe as PlatformEvent and Parkers are type-stable (TSM) and +// admit spurious wakeups. + +JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread)) + JVMWrapper("JVM_Interrupt"); + + // Ensure that the C++ Thread and OSThread structures aren't freed before we operate + oop java_thread = JNIHandles::resolve_non_null(jthread); + MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock); + // We need to re-resolve the java_thread, since a GC might have happened during the + // acquire of the lock + JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)); + if (thr != NULL) { + Thread::interrupt(thr); + } +JVM_END + + +JVM_QUICK_ENTRY(jboolean, JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clear_interrupted)) + JVMWrapper("JVM_IsInterrupted"); + + // Ensure that the C++ Thread and OSThread structures aren't freed before we operate + oop java_thread = JNIHandles::resolve_non_null(jthread); + MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock); + // We need to re-resolve the java_thread, since a GC might have happened during the + // acquire of the lock + JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)); + if (thr == NULL) { + return JNI_FALSE; + } else { + return (jboolean) Thread::is_interrupted(thr, clear_interrupted != 0); + } +JVM_END + + +// Return true iff the current thread has locked the object passed in + +JVM_ENTRY(jboolean, JVM_HoldsLock(JNIEnv* env, jclass threadClass, jobject obj)) + JVMWrapper("JVM_HoldsLock"); + assert(THREAD->is_Java_thread(), "sanity check"); + if (obj == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), JNI_FALSE); + } + Handle h_obj(THREAD, JNIHandles::resolve(obj)); + return ObjectSynchronizer::current_thread_holds_lock((JavaThread*)THREAD, h_obj); +JVM_END + + +JVM_ENTRY(void, JVM_DumpAllStacks(JNIEnv* env, jclass)) + JVMWrapper("JVM_DumpAllStacks"); + VM_PrintThreads op; + VMThread::execute(&op); + if (JvmtiExport::should_post_data_dump()) { + JvmtiExport::post_data_dump(); + } +JVM_END + + +// java.lang.SecurityManager /////////////////////////////////////////////////////////////////////// + +static bool is_trusted_frame(JavaThread* jthread, vframeStream* vfst) { + assert(jthread->is_Java_thread(), "must be a Java thread"); + if (jthread->privileged_stack_top() == NULL) return false; + if (jthread->privileged_stack_top()->frame_id() == vfst->frame_id()) { + oop loader = jthread->privileged_stack_top()->class_loader(); + if (loader == NULL) return true; + bool trusted = java_lang_ClassLoader::is_trusted_loader(loader); + if (trusted) return true; + } + return false; +} + +JVM_ENTRY(jclass, JVM_CurrentLoadedClass(JNIEnv *env)) + JVMWrapper("JVM_CurrentLoadedClass"); + ResourceMark rm(THREAD); + + for (vframeStream vfst(thread); !vfst.at_end(); vfst.next()) { + // if a method in a class in a trusted loader is in a doPrivileged, return NULL + bool trusted = is_trusted_frame(thread, &vfst); + if (trusted) return NULL; + + methodOop m = vfst.method(); + if (!m->is_native()) { + klassOop holder = m->method_holder(); + oop loader = instanceKlass::cast(holder)->class_loader(); + if (loader != NULL && !java_lang_ClassLoader::is_trusted_loader(loader)) { + return (jclass) JNIHandles::make_local(env, Klass::cast(holder)->java_mirror()); + } + } + } + return NULL; +JVM_END + + +JVM_ENTRY(jobject, JVM_CurrentClassLoader(JNIEnv *env)) + JVMWrapper("JVM_CurrentClassLoader"); + ResourceMark rm(THREAD); + + for (vframeStream vfst(thread); !vfst.at_end(); vfst.next()) { + + // if a method in a class in a trusted loader is in a doPrivileged, return NULL + bool trusted = is_trusted_frame(thread, &vfst); + if (trusted) return NULL; + + methodOop m = vfst.method(); + if (!m->is_native()) { + klassOop holder = m->method_holder(); + assert(holder->is_klass(), "just checking"); + oop loader = instanceKlass::cast(holder)->class_loader(); + if (loader != NULL && !java_lang_ClassLoader::is_trusted_loader(loader)) { + return JNIHandles::make_local(env, loader); + } + } + } + return NULL; +JVM_END + + +// Utility object for collecting method holders walking down the stack +class KlassLink: public ResourceObj { + public: + KlassHandle klass; + KlassLink* next; + + KlassLink(KlassHandle k) { klass = k; next = NULL; } +}; + + +JVM_ENTRY(jobjectArray, JVM_GetClassContext(JNIEnv *env)) + JVMWrapper("JVM_GetClassContext"); + ResourceMark rm(THREAD); + JvmtiVMObjectAllocEventCollector oam; + // Collect linked list of (handles to) method holders + KlassLink* first = NULL; + KlassLink* last = NULL; + int depth = 0; + + for(vframeStream vfst(thread); !vfst.at_end(); vfst.security_get_caller_frame(1)) { + // Native frames are not returned + if (!vfst.method()->is_native()) { + klassOop holder = vfst.method()->method_holder(); + assert(holder->is_klass(), "just checking"); + depth++; + KlassLink* l = new KlassLink(KlassHandle(thread, holder)); + if (first == NULL) { + first = last = l; + } else { + last->next = l; + last = l; + } + } + } + + // Create result array of type [Ljava/lang/Class; + objArrayOop result = oopFactory::new_objArray(SystemDictionary::class_klass(), depth, CHECK_NULL); + // Fill in mirrors corresponding to method holders + int index = 0; + while (first != NULL) { + result->obj_at_put(index++, Klass::cast(first->klass())->java_mirror()); + first = first->next; + } + assert(index == depth, "just checking"); + + return (jobjectArray) JNIHandles::make_local(env, result); +JVM_END + + +JVM_ENTRY(jint, JVM_ClassDepth(JNIEnv *env, jstring name)) + JVMWrapper("JVM_ClassDepth"); + ResourceMark rm(THREAD); + Handle h_name (THREAD, JNIHandles::resolve_non_null(name)); + Handle class_name_str = java_lang_String::internalize_classname(h_name, CHECK_0); + + const char* str = java_lang_String::as_utf8_string(class_name_str()); + symbolHandle class_name_sym = + symbolHandle(THREAD, SymbolTable::probe(str, (int)strlen(str))); + if (class_name_sym.is_null()) { + return -1; + } + + int depth = 0; + + for(vframeStream vfst(thread); !vfst.at_end(); vfst.next()) { + if (!vfst.method()->is_native()) { + klassOop holder = vfst.method()->method_holder(); + assert(holder->is_klass(), "just checking"); + if (instanceKlass::cast(holder)->name() == class_name_sym()) { + return depth; + } + depth++; + } + } + return -1; +JVM_END + + +JVM_ENTRY(jint, JVM_ClassLoaderDepth(JNIEnv *env)) + JVMWrapper("JVM_ClassLoaderDepth"); + ResourceMark rm(THREAD); + int depth = 0; + for (vframeStream vfst(thread); !vfst.at_end(); vfst.next()) { + // if a method in a class in a trusted loader is in a doPrivileged, return -1 + bool trusted = is_trusted_frame(thread, &vfst); + if (trusted) return -1; + + methodOop m = vfst.method(); + if (!m->is_native()) { + klassOop holder = m->method_holder(); + assert(holder->is_klass(), "just checking"); + oop loader = instanceKlass::cast(holder)->class_loader(); + if (loader != NULL && !java_lang_ClassLoader::is_trusted_loader(loader)) { + return depth; + } + depth++; + } + } + return -1; +JVM_END + + +// java.lang.Package //////////////////////////////////////////////////////////////// + + +JVM_ENTRY(jstring, JVM_GetSystemPackage(JNIEnv *env, jstring name)) + JVMWrapper("JVM_GetSystemPackage"); + ResourceMark rm(THREAD); + JvmtiVMObjectAllocEventCollector oam; + char* str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name)); + oop result = ClassLoader::get_system_package(str, CHECK_NULL); + return (jstring) JNIHandles::make_local(result); +JVM_END + + +JVM_ENTRY(jobjectArray, JVM_GetSystemPackages(JNIEnv *env)) + JVMWrapper("JVM_GetSystemPackages"); + JvmtiVMObjectAllocEventCollector oam; + objArrayOop result = ClassLoader::get_system_packages(CHECK_NULL); + return (jobjectArray) JNIHandles::make_local(result); +JVM_END + + +// ObjectInputStream /////////////////////////////////////////////////////////////// + +bool force_verify_field_access(klassOop current_class, klassOop field_class, AccessFlags access, bool classloader_only) { + if (current_class == NULL) { + return true; + } + if ((current_class == field_class) || access.is_public()) { + return true; + } + + if (access.is_protected()) { + // See if current_class is a subclass of field_class + if (Klass::cast(current_class)->is_subclass_of(field_class)) { + return true; + } + } + + return (!access.is_private() && instanceKlass::cast(current_class)->is_same_class_package(field_class)); +} + + +// JVM_AllocateNewObject and JVM_AllocateNewArray are unused as of 1.4 +JVM_ENTRY(jobject, JVM_AllocateNewObject(JNIEnv *env, jobject receiver, jclass currClass, jclass initClass)) + JVMWrapper("JVM_AllocateNewObject"); + JvmtiVMObjectAllocEventCollector oam; + // Receiver is not used + oop curr_mirror = JNIHandles::resolve_non_null(currClass); + oop init_mirror = JNIHandles::resolve_non_null(initClass); + + // Cannot instantiate primitive types + if (java_lang_Class::is_primitive(curr_mirror) || java_lang_Class::is_primitive(init_mirror)) { + ResourceMark rm(THREAD); + THROW_0(vmSymbols::java_lang_InvalidClassException()); + } + + // Arrays not allowed here, must use JVM_AllocateNewArray + if (Klass::cast(java_lang_Class::as_klassOop(curr_mirror))->oop_is_javaArray() || + Klass::cast(java_lang_Class::as_klassOop(init_mirror))->oop_is_javaArray()) { + ResourceMark rm(THREAD); + THROW_0(vmSymbols::java_lang_InvalidClassException()); + } + + instanceKlassHandle curr_klass (THREAD, java_lang_Class::as_klassOop(curr_mirror)); + instanceKlassHandle init_klass (THREAD, java_lang_Class::as_klassOop(init_mirror)); + + assert(curr_klass->is_subclass_of(init_klass()), "just checking"); + + // Interfaces, abstract classes, and java.lang.Class classes cannot be instantiated directly. + curr_klass->check_valid_for_instantiation(false, CHECK_NULL); + + // Make sure klass is initialized, since we are about to instantiate one of them. + curr_klass->initialize(CHECK_NULL); + + methodHandle m (THREAD, + init_klass->find_method(vmSymbols::object_initializer_name(), + vmSymbols::void_method_signature())); + if (m.is_null()) { + ResourceMark rm(THREAD); + THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(init_klass()), + vmSymbols::object_initializer_name(), + vmSymbols::void_method_signature())); + } + + if (curr_klass == init_klass && !m->is_public()) { + // Calling the constructor for class 'curr_klass'. + // Only allow calls to a public no-arg constructor. + // This path corresponds to creating an Externalizable object. + THROW_0(vmSymbols::java_lang_IllegalAccessException()); + } + + if (!force_verify_field_access(curr_klass(), init_klass(), m->access_flags(), false)) { + // subclass 'curr_klass' does not have access to no-arg constructor of 'initcb' + THROW_0(vmSymbols::java_lang_IllegalAccessException()); + } + + Handle obj = curr_klass->allocate_instance_handle(CHECK_NULL); + // Call constructor m. This might call a constructor higher up in the hierachy + JavaCalls::call_default_constructor(thread, m, obj, CHECK_NULL); + + return JNIHandles::make_local(obj()); +JVM_END + + +JVM_ENTRY(jobject, JVM_AllocateNewArray(JNIEnv *env, jobject obj, jclass currClass, jint length)) + JVMWrapper("JVM_AllocateNewArray"); + JvmtiVMObjectAllocEventCollector oam; + oop mirror = JNIHandles::resolve_non_null(currClass); + + if (java_lang_Class::is_primitive(mirror)) { + THROW_0(vmSymbols::java_lang_InvalidClassException()); + } + klassOop k = java_lang_Class::as_klassOop(mirror); + oop result; + + if (k->klass_part()->oop_is_typeArray()) { + // typeArray + result = typeArrayKlass::cast(k)->allocate(length, CHECK_NULL); + } else if (k->klass_part()->oop_is_objArray()) { + // objArray + objArrayKlassHandle oak(THREAD, k); + oak->initialize(CHECK_NULL); // make sure class is initialized (matches Classic VM behavior) + result = oak->allocate(length, CHECK_NULL); + } else { + THROW_0(vmSymbols::java_lang_InvalidClassException()); + } + return JNIHandles::make_local(env, result); +JVM_END + + +// Return the first non-null class loader up the execution stack, or null +// if only code from the null class loader is on the stack. + +JVM_ENTRY(jobject, JVM_LatestUserDefinedLoader(JNIEnv *env)) + for (vframeStream vfst(thread); !vfst.at_end(); vfst.next()) { + // UseNewReflection + vfst.skip_reflection_related_frames(); // Only needed for 1.4 reflection + klassOop holder = vfst.method()->method_holder(); + oop loader = instanceKlass::cast(holder)->class_loader(); + if (loader != NULL) { + return JNIHandles::make_local(env, loader); + } + } + return NULL; +JVM_END + + +// Load a class relative to the most recent class on the stack with a non-null +// classloader. +// This function has been deprecated and should not be considered part of the +// specified JVM interface. + +JVM_ENTRY(jclass, JVM_LoadClass0(JNIEnv *env, jobject receiver, + jclass currClass, jstring currClassName)) + JVMWrapper("JVM_LoadClass0"); + // Receiver is not used + ResourceMark rm(THREAD); + + // Class name argument is not guaranteed to be in internal format + Handle classname (THREAD, JNIHandles::resolve_non_null(currClassName)); + Handle string = java_lang_String::internalize_classname(classname, CHECK_NULL); + + const char* str = java_lang_String::as_utf8_string(string()); + + if (str == NULL || (int)strlen(str) > symbolOopDesc::max_length()) { + // It's impossible to create this class; the name cannot fit + // into the constant pool. + THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), str); + } + + symbolHandle name = oopFactory::new_symbol_handle(str, CHECK_NULL); + Handle curr_klass (THREAD, JNIHandles::resolve(currClass)); + // Find the most recent class on the stack with a non-null classloader + oop loader = NULL; + oop protection_domain = NULL; + if (curr_klass.is_null()) { + for (vframeStream vfst(thread); + !vfst.at_end() && loader == NULL; + vfst.next()) { + if (!vfst.method()->is_native()) { + klassOop holder = vfst.method()->method_holder(); + loader = instanceKlass::cast(holder)->class_loader(); + protection_domain = instanceKlass::cast(holder)->protection_domain(); + } + } + } else { + klassOop curr_klass_oop = java_lang_Class::as_klassOop(curr_klass()); + loader = instanceKlass::cast(curr_klass_oop)->class_loader(); + protection_domain = instanceKlass::cast(curr_klass_oop)->protection_domain(); + } + Handle h_loader(THREAD, loader); + Handle h_prot (THREAD, protection_domain); + return find_class_from_class_loader(env, name, true, h_loader, h_prot, + false, thread); +JVM_END + + +// Array /////////////////////////////////////////////////////////////////////////////////////////// + + +// resolve array handle and check arguments +static inline arrayOop check_array(JNIEnv *env, jobject arr, bool type_array_only, TRAPS) { + if (arr == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + oop a = JNIHandles::resolve_non_null(arr); + if (!a->is_javaArray() || (type_array_only && !a->is_typeArray())) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Argument is not an array"); + } + return arrayOop(a); +} + + +JVM_ENTRY(jint, JVM_GetArrayLength(JNIEnv *env, jobject arr)) + JVMWrapper("JVM_GetArrayLength"); + arrayOop a = check_array(env, arr, false, CHECK_0); + return a->length(); +JVM_END + + +JVM_ENTRY(jobject, JVM_GetArrayElement(JNIEnv *env, jobject arr, jint index)) + JVMWrapper("JVM_Array_Get"); + JvmtiVMObjectAllocEventCollector oam; + arrayOop a = check_array(env, arr, false, CHECK_NULL); + jvalue value; + BasicType type = Reflection::array_get(&value, a, index, CHECK_NULL); + oop box = Reflection::box(&value, type, CHECK_NULL); + return JNIHandles::make_local(env, box); +JVM_END + + +JVM_ENTRY(jvalue, JVM_GetPrimitiveArrayElement(JNIEnv *env, jobject arr, jint index, jint wCode)) + JVMWrapper("JVM_GetPrimitiveArrayElement"); + jvalue value; + value.i = 0; // to initialize value before getting used in CHECK + arrayOop a = check_array(env, arr, true, CHECK_(value)); + assert(a->is_typeArray(), "just checking"); + BasicType type = Reflection::array_get(&value, a, index, CHECK_(value)); + BasicType wide_type = (BasicType) wCode; + if (type != wide_type) { + Reflection::widen(&value, type, wide_type, CHECK_(value)); + } + return value; +JVM_END + + +JVM_ENTRY(void, JVM_SetArrayElement(JNIEnv *env, jobject arr, jint index, jobject val)) + JVMWrapper("JVM_SetArrayElement"); + arrayOop a = check_array(env, arr, false, CHECK); + oop box = JNIHandles::resolve(val); + jvalue value; + value.i = 0; // to initialize value before getting used in CHECK + BasicType value_type; + if (a->is_objArray()) { + // Make sure we do no unbox e.g. java/lang/Integer instances when storing into an object array + value_type = Reflection::unbox_for_regular_object(box, &value); + } else { + value_type = Reflection::unbox_for_primitive(box, &value, CHECK); + } + Reflection::array_set(&value, a, index, value_type, CHECK); +JVM_END + + +JVM_ENTRY(void, JVM_SetPrimitiveArrayElement(JNIEnv *env, jobject arr, jint index, jvalue v, unsigned char vCode)) + JVMWrapper("JVM_SetPrimitiveArrayElement"); + arrayOop a = check_array(env, arr, true, CHECK); + assert(a->is_typeArray(), "just checking"); + BasicType value_type = (BasicType) vCode; + Reflection::array_set(&v, a, index, value_type, CHECK); +JVM_END + + +JVM_ENTRY(jobject, JVM_NewArray(JNIEnv *env, jclass eltClass, jint length)) + JVMWrapper("JVM_NewArray"); + JvmtiVMObjectAllocEventCollector oam; + oop element_mirror = JNIHandles::resolve(eltClass); + oop result = Reflection::reflect_new_array(element_mirror, length, CHECK_NULL); + return JNIHandles::make_local(env, result); +JVM_END + + +JVM_ENTRY(jobject, JVM_NewMultiArray(JNIEnv *env, jclass eltClass, jintArray dim)) + JVMWrapper("JVM_NewMultiArray"); + JvmtiVMObjectAllocEventCollector oam; + arrayOop dim_array = check_array(env, dim, true, CHECK_NULL); + oop element_mirror = JNIHandles::resolve(eltClass); + assert(dim_array->is_typeArray(), "just checking"); + oop result = Reflection::reflect_new_multi_array(element_mirror, typeArrayOop(dim_array), CHECK_NULL); + return JNIHandles::make_local(env, result); +JVM_END + + +// Networking library support //////////////////////////////////////////////////////////////////// + +JVM_LEAF(jint, JVM_InitializeSocketLibrary()) + JVMWrapper("JVM_InitializeSocketLibrary"); + return hpi::initialize_socket_library(); +JVM_END + + +JVM_LEAF(jint, JVM_Socket(jint domain, jint type, jint protocol)) + JVMWrapper("JVM_Socket"); + return hpi::socket(domain, type, protocol); +JVM_END + + +JVM_LEAF(jint, JVM_SocketClose(jint fd)) + JVMWrapper2("JVM_SocketClose (0x%x)", fd); + //%note jvm_r6 + return hpi::socket_close(fd); +JVM_END + + +JVM_LEAF(jint, JVM_SocketShutdown(jint fd, jint howto)) + JVMWrapper2("JVM_SocketShutdown (0x%x)", fd); + //%note jvm_r6 + return hpi::socket_shutdown(fd, howto); +JVM_END + + +JVM_LEAF(jint, JVM_Recv(jint fd, char *buf, jint nBytes, jint flags)) + JVMWrapper2("JVM_Recv (0x%x)", fd); + //%note jvm_r6 + return hpi::recv(fd, buf, nBytes, flags); +JVM_END + + +JVM_LEAF(jint, JVM_Send(jint fd, char *buf, jint nBytes, jint flags)) + JVMWrapper2("JVM_Send (0x%x)", fd); + //%note jvm_r6 + return hpi::send(fd, buf, nBytes, flags); +JVM_END + + +JVM_LEAF(jint, JVM_Timeout(int fd, long timeout)) + JVMWrapper2("JVM_Timeout (0x%x)", fd); + //%note jvm_r6 + return hpi::timeout(fd, timeout); +JVM_END + + +JVM_LEAF(jint, JVM_Listen(jint fd, jint count)) + JVMWrapper2("JVM_Listen (0x%x)", fd); + //%note jvm_r6 + return hpi::listen(fd, count); +JVM_END + + +JVM_LEAF(jint, JVM_Connect(jint fd, struct sockaddr *him, jint len)) + JVMWrapper2("JVM_Connect (0x%x)", fd); + //%note jvm_r6 + return hpi::connect(fd, him, len); +JVM_END + + +JVM_LEAF(jint, JVM_Bind(jint fd, struct sockaddr *him, jint len)) + JVMWrapper2("JVM_Bind (0x%x)", fd); + //%note jvm_r6 + return hpi::bind(fd, him, len); +JVM_END + + +JVM_LEAF(jint, JVM_Accept(jint fd, struct sockaddr *him, jint *len)) + JVMWrapper2("JVM_Accept (0x%x)", fd); + //%note jvm_r6 + return hpi::accept(fd, him, (int *)len); +JVM_END + + +JVM_LEAF(jint, JVM_RecvFrom(jint fd, char *buf, int nBytes, int flags, struct sockaddr *from, int *fromlen)) + JVMWrapper2("JVM_RecvFrom (0x%x)", fd); + //%note jvm_r6 + return hpi::recvfrom(fd, buf, nBytes, flags, from, fromlen); +JVM_END + + +JVM_LEAF(jint, JVM_GetSockName(jint fd, struct sockaddr *him, int *len)) + JVMWrapper2("JVM_GetSockName (0x%x)", fd); + //%note jvm_r6 + return hpi::get_sock_name(fd, him, len); +JVM_END + + +JVM_LEAF(jint, JVM_SendTo(jint fd, char *buf, int len, int flags, struct sockaddr *to, int tolen)) + JVMWrapper2("JVM_SendTo (0x%x)", fd); + //%note jvm_r6 + return hpi::sendto(fd, buf, len, flags, to, tolen); +JVM_END + + +JVM_LEAF(jint, JVM_SocketAvailable(jint fd, jint *pbytes)) + JVMWrapper2("JVM_SocketAvailable (0x%x)", fd); + //%note jvm_r6 + return hpi::socket_available(fd, pbytes); +JVM_END + + +JVM_LEAF(jint, JVM_GetSockOpt(jint fd, int level, int optname, char *optval, int *optlen)) + JVMWrapper2("JVM_GetSockOpt (0x%x)", fd); + //%note jvm_r6 + return hpi::get_sock_opt(fd, level, optname, optval, optlen); +JVM_END + + +JVM_LEAF(jint, JVM_SetSockOpt(jint fd, int level, int optname, const char *optval, int optlen)) + JVMWrapper2("JVM_GetSockOpt (0x%x)", fd); + //%note jvm_r6 + return hpi::set_sock_opt(fd, level, optname, optval, optlen); +JVM_END + +JVM_LEAF(int, JVM_GetHostName(char* name, int namelen)) + JVMWrapper("JVM_GetHostName"); + return hpi::get_host_name(name, namelen); +JVM_END + +#ifdef _WINDOWS + +JVM_LEAF(struct hostent*, JVM_GetHostByAddr(const char* name, int len, int type)) + JVMWrapper("JVM_GetHostByAddr"); + return hpi::get_host_by_addr(name, len, type); +JVM_END + + +JVM_LEAF(struct hostent*, JVM_GetHostByName(char* name)) + JVMWrapper("JVM_GetHostByName"); + return hpi::get_host_by_name(name); +JVM_END + + +JVM_LEAF(struct protoent*, JVM_GetProtoByName(char* name)) + JVMWrapper("JVM_GetProtoByName"); + return hpi::get_proto_by_name(name); +JVM_END + +#endif + +// Library support /////////////////////////////////////////////////////////////////////////// + +JVM_ENTRY_NO_ENV(void*, JVM_LoadLibrary(const char* name)) + //%note jvm_ct + JVMWrapper2("JVM_LoadLibrary (%s)", name); + char ebuf[1024]; + void *load_result; + { + ThreadToNativeFromVM ttnfvm(thread); + load_result = hpi::dll_load(name, ebuf, sizeof ebuf); + } + if (load_result == NULL) { + char msg[1024]; + jio_snprintf(msg, sizeof msg, "%s: %s", name, ebuf); + // Since 'ebuf' may contain a string encoded using + // platform encoding scheme, we need to pass + // Exceptions::unsafe_to_utf8 to the new_exception method + // as the last argument. See bug 6367357. + Handle h_exception = + Exceptions::new_exception(thread, + vmSymbols::java_lang_UnsatisfiedLinkError(), + msg, Exceptions::unsafe_to_utf8); + + THROW_HANDLE_0(h_exception); + } + return load_result; +JVM_END + + +JVM_LEAF(void, JVM_UnloadLibrary(void* handle)) + JVMWrapper("JVM_UnloadLibrary"); + hpi::dll_unload(handle); +JVM_END + + +JVM_LEAF(void*, JVM_FindLibraryEntry(void* handle, const char* name)) + JVMWrapper2("JVM_FindLibraryEntry (%s)", name); + return hpi::dll_lookup(handle, name); +JVM_END + +// Floating point support //////////////////////////////////////////////////////////////////// + +JVM_LEAF(jboolean, JVM_IsNaN(jdouble a)) + JVMWrapper("JVM_IsNaN"); + return g_isnan(a); +JVM_END + + + +// JNI version /////////////////////////////////////////////////////////////////////////////// + +JVM_LEAF(jboolean, JVM_IsSupportedJNIVersion(jint version)) + JVMWrapper2("JVM_IsSupportedJNIVersion (%d)", version); + return Threads::is_supported_jni_version_including_1_1(version); +JVM_END + + +// String support /////////////////////////////////////////////////////////////////////////// + +JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) + JVMWrapper("JVM_InternString"); + JvmtiVMObjectAllocEventCollector oam; + if (str == NULL) return NULL; + oop string = JNIHandles::resolve_non_null(str); + oop result = StringTable::intern(string, CHECK_NULL); + return (jstring) JNIHandles::make_local(env, result); +JVM_END + + +// Raw monitor support ////////////////////////////////////////////////////////////////////// + +// The lock routine below calls lock_without_safepoint_check in order to get a raw lock +// without interfering with the safepoint mechanism. The routines are not JVM_LEAF because +// they might be called by non-java threads. The JVM_LEAF installs a NoHandleMark check +// that only works with java threads. + + +JNIEXPORT void* JNICALL JVM_RawMonitorCreate(void) { + VM_Exit::block_if_vm_exited(); + JVMWrapper("JVM_RawMonitorCreate"); + return new Mutex(Mutex::native, "JVM_RawMonitorCreate"); +} + + +JNIEXPORT void JNICALL JVM_RawMonitorDestroy(void *mon) { + VM_Exit::block_if_vm_exited(); + JVMWrapper("JVM_RawMonitorDestroy"); + delete ((Mutex*) mon); +} + + +JNIEXPORT jint JNICALL JVM_RawMonitorEnter(void *mon) { + VM_Exit::block_if_vm_exited(); + JVMWrapper("JVM_RawMonitorEnter"); + ((Mutex*) mon)->jvm_raw_lock(); + return 0; +} + + +JNIEXPORT void JNICALL JVM_RawMonitorExit(void *mon) { + VM_Exit::block_if_vm_exited(); + JVMWrapper("JVM_RawMonitorExit"); + ((Mutex*) mon)->jvm_raw_unlock(); +} + + +// Support for Serialization + +typedef jfloat (JNICALL *IntBitsToFloatFn )(JNIEnv* env, jclass cb, jint value); +typedef jdouble (JNICALL *LongBitsToDoubleFn)(JNIEnv* env, jclass cb, jlong value); +typedef jint (JNICALL *FloatToIntBitsFn )(JNIEnv* env, jclass cb, jfloat value); +typedef jlong (JNICALL *DoubleToLongBitsFn)(JNIEnv* env, jclass cb, jdouble value); + +static IntBitsToFloatFn int_bits_to_float_fn = NULL; +static LongBitsToDoubleFn long_bits_to_double_fn = NULL; +static FloatToIntBitsFn float_to_int_bits_fn = NULL; +static DoubleToLongBitsFn double_to_long_bits_fn = NULL; + + +void initialize_converter_functions() { + if (JDK_Version::is_gte_jdk14x_version()) { + // These functions only exist for compatibility with 1.3.1 and earlier + return; + } + + // called from universe_post_init() + assert( + int_bits_to_float_fn == NULL && + long_bits_to_double_fn == NULL && + float_to_int_bits_fn == NULL && + double_to_long_bits_fn == NULL , + "initialization done twice" + ); + // initialize + int_bits_to_float_fn = CAST_TO_FN_PTR(IntBitsToFloatFn , NativeLookup::base_library_lookup("java/lang/Float" , "intBitsToFloat" , "(I)F")); + long_bits_to_double_fn = CAST_TO_FN_PTR(LongBitsToDoubleFn, NativeLookup::base_library_lookup("java/lang/Double", "longBitsToDouble", "(J)D")); + float_to_int_bits_fn = CAST_TO_FN_PTR(FloatToIntBitsFn , NativeLookup::base_library_lookup("java/lang/Float" , "floatToIntBits" , "(F)I")); + double_to_long_bits_fn = CAST_TO_FN_PTR(DoubleToLongBitsFn, NativeLookup::base_library_lookup("java/lang/Double", "doubleToLongBits", "(D)J")); + // verify + assert( + int_bits_to_float_fn != NULL && + long_bits_to_double_fn != NULL && + float_to_int_bits_fn != NULL && + double_to_long_bits_fn != NULL , + "initialization failed" + ); +} + + +// Serialization +JVM_ENTRY(void, JVM_SetPrimitiveFieldValues(JNIEnv *env, jclass cb, jobject obj, + jlongArray fieldIDs, jcharArray typecodes, jbyteArray data)) + assert(!JDK_Version::is_gte_jdk14x_version(), "should only be used in 1.3.1 and earlier"); + + typeArrayOop tcodes = typeArrayOop(JNIHandles::resolve(typecodes)); + typeArrayOop dbuf = typeArrayOop(JNIHandles::resolve(data)); + typeArrayOop fids = typeArrayOop(JNIHandles::resolve(fieldIDs)); + oop o = JNIHandles::resolve(obj); + + if (o == NULL || fids == NULL || dbuf == NULL || tcodes == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + + jsize nfids = fids->length(); + if (nfids == 0) return; + + if (tcodes->length() < nfids) { + THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + } + + jsize off = 0; + /* loop through fields, setting values */ + for (jsize i = 0; i < nfids; i++) { + jfieldID fid = (jfieldID)(intptr_t) fids->long_at(i); + int field_offset; + if (fid != NULL) { + // NULL is a legal value for fid, but retrieving the field offset + // trigger assertion in that case + field_offset = jfieldIDWorkaround::from_instance_jfieldID(o->klass(), fid); + } + + switch (tcodes->char_at(i)) { + case 'Z': + if (fid != NULL) { + jboolean val = (dbuf->byte_at(off) != 0) ? JNI_TRUE : JNI_FALSE; + o->bool_field_put(field_offset, val); + } + off++; + break; + + case 'B': + if (fid != NULL) { + o->byte_field_put(field_offset, dbuf->byte_at(off)); + } + off++; + break; + + case 'C': + if (fid != NULL) { + jchar val = ((dbuf->byte_at(off + 0) & 0xFF) << 8) + + ((dbuf->byte_at(off + 1) & 0xFF) << 0); + o->char_field_put(field_offset, val); + } + off += 2; + break; + + case 'S': + if (fid != NULL) { + jshort val = ((dbuf->byte_at(off + 0) & 0xFF) << 8) + + ((dbuf->byte_at(off + 1) & 0xFF) << 0); + o->short_field_put(field_offset, val); + } + off += 2; + break; + + case 'I': + if (fid != NULL) { + jint ival = ((dbuf->byte_at(off + 0) & 0xFF) << 24) + + ((dbuf->byte_at(off + 1) & 0xFF) << 16) + + ((dbuf->byte_at(off + 2) & 0xFF) << 8) + + ((dbuf->byte_at(off + 3) & 0xFF) << 0); + o->int_field_put(field_offset, ival); + } + off += 4; + break; + + case 'F': + if (fid != NULL) { + jint ival = ((dbuf->byte_at(off + 0) & 0xFF) << 24) + + ((dbuf->byte_at(off + 1) & 0xFF) << 16) + + ((dbuf->byte_at(off + 2) & 0xFF) << 8) + + ((dbuf->byte_at(off + 3) & 0xFF) << 0); + jfloat fval = (*int_bits_to_float_fn)(env, NULL, ival); + o->float_field_put(field_offset, fval); + } + off += 4; + break; + + case 'J': + if (fid != NULL) { + jlong lval = (((jlong) dbuf->byte_at(off + 0) & 0xFF) << 56) + + (((jlong) dbuf->byte_at(off + 1) & 0xFF) << 48) + + (((jlong) dbuf->byte_at(off + 2) & 0xFF) << 40) + + (((jlong) dbuf->byte_at(off + 3) & 0xFF) << 32) + + (((jlong) dbuf->byte_at(off + 4) & 0xFF) << 24) + + (((jlong) dbuf->byte_at(off + 5) & 0xFF) << 16) + + (((jlong) dbuf->byte_at(off + 6) & 0xFF) << 8) + + (((jlong) dbuf->byte_at(off + 7) & 0xFF) << 0); + o->long_field_put(field_offset, lval); + } + off += 8; + break; + + case 'D': + if (fid != NULL) { + jlong lval = (((jlong) dbuf->byte_at(off + 0) & 0xFF) << 56) + + (((jlong) dbuf->byte_at(off + 1) & 0xFF) << 48) + + (((jlong) dbuf->byte_at(off + 2) & 0xFF) << 40) + + (((jlong) dbuf->byte_at(off + 3) & 0xFF) << 32) + + (((jlong) dbuf->byte_at(off + 4) & 0xFF) << 24) + + (((jlong) dbuf->byte_at(off + 5) & 0xFF) << 16) + + (((jlong) dbuf->byte_at(off + 6) & 0xFF) << 8) + + (((jlong) dbuf->byte_at(off + 7) & 0xFF) << 0); + jdouble dval = (*long_bits_to_double_fn)(env, NULL, lval); + o->double_field_put(field_offset, dval); + } + off += 8; + break; + + default: + // Illegal typecode + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "illegal typecode"); + } + } +JVM_END + + +JVM_ENTRY(void, JVM_GetPrimitiveFieldValues(JNIEnv *env, jclass cb, jobject obj, + jlongArray fieldIDs, jcharArray typecodes, jbyteArray data)) + assert(!JDK_Version::is_gte_jdk14x_version(), "should only be used in 1.3.1 and earlier"); + + typeArrayOop tcodes = typeArrayOop(JNIHandles::resolve(typecodes)); + typeArrayOop dbuf = typeArrayOop(JNIHandles::resolve(data)); + typeArrayOop fids = typeArrayOop(JNIHandles::resolve(fieldIDs)); + oop o = JNIHandles::resolve(obj); + + if (o == NULL || fids == NULL || dbuf == NULL || tcodes == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + + jsize nfids = fids->length(); + if (nfids == 0) return; + + if (tcodes->length() < nfids) { + THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + } + + /* loop through fields, fetching values */ + jsize off = 0; + for (jsize i = 0; i < nfids; i++) { + jfieldID fid = (jfieldID)(intptr_t) fids->long_at(i); + if (fid == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + int field_offset = jfieldIDWorkaround::from_instance_jfieldID(o->klass(), fid); + + switch (tcodes->char_at(i)) { + case 'Z': + { + jboolean val = o->bool_field(field_offset); + dbuf->byte_at_put(off++, (val != 0) ? 1 : 0); + } + break; + + case 'B': + dbuf->byte_at_put(off++, o->byte_field(field_offset)); + break; + + case 'C': + { + jchar val = o->char_field(field_offset); + dbuf->byte_at_put(off++, (val >> 8) & 0xFF); + dbuf->byte_at_put(off++, (val >> 0) & 0xFF); + } + break; + + case 'S': + { + jshort val = o->short_field(field_offset); + dbuf->byte_at_put(off++, (val >> 8) & 0xFF); + dbuf->byte_at_put(off++, (val >> 0) & 0xFF); + } + break; + + case 'I': + { + jint val = o->int_field(field_offset); + dbuf->byte_at_put(off++, (val >> 24) & 0xFF); + dbuf->byte_at_put(off++, (val >> 16) & 0xFF); + dbuf->byte_at_put(off++, (val >> 8) & 0xFF); + dbuf->byte_at_put(off++, (val >> 0) & 0xFF); + } + break; + + case 'F': + { + jfloat fval = o->float_field(field_offset); + jint ival = (*float_to_int_bits_fn)(env, NULL, fval); + dbuf->byte_at_put(off++, (ival >> 24) & 0xFF); + dbuf->byte_at_put(off++, (ival >> 16) & 0xFF); + dbuf->byte_at_put(off++, (ival >> 8) & 0xFF); + dbuf->byte_at_put(off++, (ival >> 0) & 0xFF); + } + break; + + case 'J': + { + jlong val = o->long_field(field_offset); + dbuf->byte_at_put(off++, (val >> 56) & 0xFF); + dbuf->byte_at_put(off++, (val >> 48) & 0xFF); + dbuf->byte_at_put(off++, (val >> 40) & 0xFF); + dbuf->byte_at_put(off++, (val >> 32) & 0xFF); + dbuf->byte_at_put(off++, (val >> 24) & 0xFF); + dbuf->byte_at_put(off++, (val >> 16) & 0xFF); + dbuf->byte_at_put(off++, (val >> 8) & 0xFF); + dbuf->byte_at_put(off++, (val >> 0) & 0xFF); + } + break; + + case 'D': + { + jdouble dval = o->double_field(field_offset); + jlong lval = (*double_to_long_bits_fn)(env, NULL, dval); + dbuf->byte_at_put(off++, (lval >> 56) & 0xFF); + dbuf->byte_at_put(off++, (lval >> 48) & 0xFF); + dbuf->byte_at_put(off++, (lval >> 40) & 0xFF); + dbuf->byte_at_put(off++, (lval >> 32) & 0xFF); + dbuf->byte_at_put(off++, (lval >> 24) & 0xFF); + dbuf->byte_at_put(off++, (lval >> 16) & 0xFF); + dbuf->byte_at_put(off++, (lval >> 8) & 0xFF); + dbuf->byte_at_put(off++, (lval >> 0) & 0xFF); + } + break; + + default: + // Illegal typecode + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "illegal typecode"); + } + } +JVM_END + + +// Shared JNI/JVM entry points ////////////////////////////////////////////////////////////// + +jclass find_class_from_class_loader(JNIEnv* env, symbolHandle name, jboolean init, Handle loader, Handle protection_domain, jboolean throwError, TRAPS) { + // Security Note: + // The Java level wrapper will perform the necessary security check allowing + // us to pass the NULL as the initiating class loader. + klassOop klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain, throwError != 0, CHECK_NULL); + KlassHandle klass_handle(THREAD, klass); + // Check if we should initialize the class + if (init && klass_handle->oop_is_instance()) { + klass_handle->initialize(CHECK_NULL); + } + return (jclass) JNIHandles::make_local(env, klass_handle->java_mirror()); +} + + +// Internal SQE debugging support /////////////////////////////////////////////////////////// + +#ifndef PRODUCT + +extern "C" { + JNIEXPORT jboolean JNICALL JVM_AccessVMBooleanFlag(const char* name, jboolean* value, jboolean is_get); + JNIEXPORT jboolean JNICALL JVM_AccessVMIntFlag(const char* name, jint* value, jboolean is_get); + JNIEXPORT void JNICALL JVM_VMBreakPoint(JNIEnv *env, jobject obj); +} + +JVM_LEAF(jboolean, JVM_AccessVMBooleanFlag(const char* name, jboolean* value, jboolean is_get)) + JVMWrapper("JVM_AccessBoolVMFlag"); + return is_get ? CommandLineFlags::boolAt((char*) name, (bool*) value) : CommandLineFlags::boolAtPut((char*) name, (bool*) value, INTERNAL); +JVM_END + +JVM_LEAF(jboolean, JVM_AccessVMIntFlag(const char* name, jint* value, jboolean is_get)) + JVMWrapper("JVM_AccessVMIntFlag"); + intx v; + jboolean result = is_get ? CommandLineFlags::intxAt((char*) name, &v) : CommandLineFlags::intxAtPut((char*) name, &v, INTERNAL); + *value = (jint)v; + return result; +JVM_END + + +JVM_ENTRY(void, JVM_VMBreakPoint(JNIEnv *env, jobject obj)) + JVMWrapper("JVM_VMBreakPoint"); + oop the_obj = JNIHandles::resolve(obj); + BREAKPOINT; +JVM_END + + +#endif + + +//--------------------------------------------------------------------------- +// +// Support for old native code-based reflection (pre-JDK 1.4) +// Disabled by default in the product build. +// +// See reflection.hpp for information on SUPPORT_OLD_REFLECTION +// +//--------------------------------------------------------------------------- + +#ifdef SUPPORT_OLD_REFLECTION + +JVM_ENTRY(jobjectArray, JVM_GetClassFields(JNIEnv *env, jclass cls, jint which)) + JVMWrapper("JVM_GetClassFields"); + JvmtiVMObjectAllocEventCollector oam; + oop mirror = JNIHandles::resolve_non_null(cls); + objArrayOop result = Reflection::reflect_fields(mirror, which, CHECK_NULL); + return (jobjectArray) JNIHandles::make_local(env, result); +JVM_END + + +JVM_ENTRY(jobjectArray, JVM_GetClassMethods(JNIEnv *env, jclass cls, jint which)) + JVMWrapper("JVM_GetClassMethods"); + JvmtiVMObjectAllocEventCollector oam; + oop mirror = JNIHandles::resolve_non_null(cls); + objArrayOop result = Reflection::reflect_methods(mirror, which, CHECK_NULL); + //%note jvm_r4 + return (jobjectArray) JNIHandles::make_local(env, result); +JVM_END + + +JVM_ENTRY(jobjectArray, JVM_GetClassConstructors(JNIEnv *env, jclass cls, jint which)) + JVMWrapper("JVM_GetClassConstructors"); + JvmtiVMObjectAllocEventCollector oam; + oop mirror = JNIHandles::resolve_non_null(cls); + objArrayOop result = Reflection::reflect_constructors(mirror, which, CHECK_NULL); + //%note jvm_r4 + return (jobjectArray) JNIHandles::make_local(env, result); +JVM_END + + +JVM_ENTRY(jobject, JVM_GetClassField(JNIEnv *env, jclass cls, jstring name, jint which)) + JVMWrapper("JVM_GetClassField"); + JvmtiVMObjectAllocEventCollector oam; + if (name == NULL) return NULL; + Handle str (THREAD, JNIHandles::resolve_non_null(name)); + + const char* cstr = java_lang_String::as_utf8_string(str()); + symbolHandle field_name = + symbolHandle(THREAD, SymbolTable::probe(cstr, (int)strlen(cstr))); + if (field_name.is_null()) { + THROW_0(vmSymbols::java_lang_NoSuchFieldException()); + } + + oop mirror = JNIHandles::resolve_non_null(cls); + oop result = Reflection::reflect_field(mirror, field_name(), which, CHECK_NULL); + if (result == NULL) { + THROW_0(vmSymbols::java_lang_NoSuchFieldException()); + } + return JNIHandles::make_local(env, result); +JVM_END + + +JVM_ENTRY(jobject, JVM_GetClassMethod(JNIEnv *env, jclass cls, jstring name, jobjectArray types, jint which)) + JVMWrapper("JVM_GetClassMethod"); + JvmtiVMObjectAllocEventCollector oam; + if (name == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + Handle str (THREAD, JNIHandles::resolve_non_null(name)); + + const char* cstr = java_lang_String::as_utf8_string(str()); + symbolHandle method_name = + symbolHandle(THREAD, SymbolTable::probe(cstr, (int)strlen(cstr))); + if (method_name.is_null()) { + THROW_0(vmSymbols::java_lang_NoSuchMethodException()); + } + + oop mirror = JNIHandles::resolve_non_null(cls); + objArrayHandle tarray (THREAD, objArrayOop(JNIHandles::resolve(types))); + oop result = Reflection::reflect_method(mirror, method_name, tarray, + which, CHECK_NULL); + if (result == NULL) { + THROW_0(vmSymbols::java_lang_NoSuchMethodException()); + } + return JNIHandles::make_local(env, result); +JVM_END + + +JVM_ENTRY(jobject, JVM_GetClassConstructor(JNIEnv *env, jclass cls, jobjectArray types, jint which)) + JVMWrapper("JVM_GetClassConstructor"); + JvmtiVMObjectAllocEventCollector oam; + oop mirror = JNIHandles::resolve_non_null(cls); + objArrayHandle tarray (THREAD, objArrayOop(JNIHandles::resolve(types))); + oop result = Reflection::reflect_constructor(mirror, tarray, which, CHECK_NULL); + if (result == NULL) { + THROW_0(vmSymbols::java_lang_NoSuchMethodException()); + } + return (jobject) JNIHandles::make_local(env, result); +JVM_END + + +// Instantiation /////////////////////////////////////////////////////////////////////////////// + +JVM_ENTRY(jobject, JVM_NewInstance(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_NewInstance"); + Handle mirror(THREAD, JNIHandles::resolve_non_null(cls)); + + methodOop resolved_constructor = java_lang_Class::resolved_constructor(mirror()); + if (resolved_constructor == NULL) { + klassOop k = java_lang_Class::as_klassOop(mirror()); + // The java.lang.Class object caches a resolved constructor if all the checks + // below were done successfully and a constructor was found. + + // Do class based checks + if (java_lang_Class::is_primitive(mirror())) { + const char* msg = ""; + if (mirror == Universe::bool_mirror()) msg = "java/lang/Boolean"; + else if (mirror == Universe::char_mirror()) msg = "java/lang/Character"; + else if (mirror == Universe::float_mirror()) msg = "java/lang/Float"; + else if (mirror == Universe::double_mirror()) msg = "java/lang/Double"; + else if (mirror == Universe::byte_mirror()) msg = "java/lang/Byte"; + else if (mirror == Universe::short_mirror()) msg = "java/lang/Short"; + else if (mirror == Universe::int_mirror()) msg = "java/lang/Integer"; + else if (mirror == Universe::long_mirror()) msg = "java/lang/Long"; + THROW_MSG_0(vmSymbols::java_lang_NullPointerException(), msg); + } + + // Check whether we are allowed to instantiate this class + Klass::cast(k)->check_valid_for_instantiation(false, CHECK_NULL); // Array classes get caught here + instanceKlassHandle klass(THREAD, k); + // Make sure class is initialized (also so all methods are rewritten) + klass->initialize(CHECK_NULL); + + // Lookup default constructor + resolved_constructor = klass->find_method(vmSymbols::object_initializer_name(), vmSymbols::void_method_signature()); + if (resolved_constructor == NULL) { + ResourceMark rm(THREAD); + THROW_MSG_0(vmSymbols::java_lang_InstantiationException(), klass->external_name()); + } + + // Cache result in java.lang.Class object. Does not have to be MT safe. + java_lang_Class::set_resolved_constructor(mirror(), resolved_constructor); + } + + assert(resolved_constructor != NULL, "sanity check"); + methodHandle constructor = methodHandle(THREAD, resolved_constructor); + + // We have an initialized instanceKlass with a default constructor + instanceKlassHandle klass(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls))); + assert(klass->is_initialized() || klass->is_being_initialized(), "sanity check"); + + // Do security check + klassOop caller_klass = NULL; + if (UsePrivilegedStack) { + caller_klass = thread->security_get_caller_class(2); + + if (!Reflection::verify_class_access(caller_klass, klass(), false) || + !Reflection::verify_field_access(caller_klass, + klass(), + klass(), + constructor->access_flags(), + false, + true)) { + ResourceMark rm(THREAD); + THROW_MSG_0(vmSymbols::java_lang_IllegalAccessException(), klass->external_name()); + } + } + + // Allocate object and call constructor + Handle receiver = klass->allocate_instance_handle(CHECK_NULL); + JavaCalls::call_default_constructor(thread, constructor, receiver, CHECK_NULL); + + jobject res = JNIHandles::make_local(env, receiver()); + if (JvmtiExport::should_post_vm_object_alloc()) { + JvmtiExport::post_vm_object_alloc(JavaThread::current(), receiver()); + } + return res; +JVM_END + + +// Field //////////////////////////////////////////////////////////////////////////////////////////// + +JVM_ENTRY(jobject, JVM_GetField(JNIEnv *env, jobject field, jobject obj)) + JVMWrapper("JVM_GetField"); + JvmtiVMObjectAllocEventCollector oam; + Handle field_mirror(thread, JNIHandles::resolve(field)); + Handle receiver (thread, JNIHandles::resolve(obj)); + fieldDescriptor fd; + Reflection::resolve_field(field_mirror, receiver, &fd, false, CHECK_NULL); + jvalue value; + BasicType type = Reflection::field_get(&value, &fd, receiver); + oop box = Reflection::box(&value, type, CHECK_NULL); + return JNIHandles::make_local(env, box); +JVM_END + + +JVM_ENTRY(jvalue, JVM_GetPrimitiveField(JNIEnv *env, jobject field, jobject obj, unsigned char wCode)) + JVMWrapper("JVM_GetPrimitiveField"); + Handle field_mirror(thread, JNIHandles::resolve(field)); + Handle receiver (thread, JNIHandles::resolve(obj)); + fieldDescriptor fd; + jvalue value; + value.j = 0; + Reflection::resolve_field(field_mirror, receiver, &fd, false, CHECK_(value)); + BasicType type = Reflection::field_get(&value, &fd, receiver); + BasicType wide_type = (BasicType) wCode; + if (type != wide_type) { + Reflection::widen(&value, type, wide_type, CHECK_(value)); + } + return value; +JVM_END // should really be JVM_END, but that doesn't work for union types! + + +JVM_ENTRY(void, JVM_SetField(JNIEnv *env, jobject field, jobject obj, jobject val)) + JVMWrapper("JVM_SetField"); + Handle field_mirror(thread, JNIHandles::resolve(field)); + Handle receiver (thread, JNIHandles::resolve(obj)); + oop box = JNIHandles::resolve(val); + fieldDescriptor fd; + Reflection::resolve_field(field_mirror, receiver, &fd, true, CHECK); + BasicType field_type = fd.field_type(); + jvalue value; + BasicType value_type; + if (field_type == T_OBJECT || field_type == T_ARRAY) { + // Make sure we do no unbox e.g. java/lang/Integer instances when storing into an object array + value_type = Reflection::unbox_for_regular_object(box, &value); + Reflection::field_set(&value, &fd, receiver, field_type, CHECK); + } else { + value_type = Reflection::unbox_for_primitive(box, &value, CHECK); + Reflection::field_set(&value, &fd, receiver, value_type, CHECK); + } +JVM_END + + +JVM_ENTRY(void, JVM_SetPrimitiveField(JNIEnv *env, jobject field, jobject obj, jvalue v, unsigned char vCode)) + JVMWrapper("JVM_SetPrimitiveField"); + Handle field_mirror(thread, JNIHandles::resolve(field)); + Handle receiver (thread, JNIHandles::resolve(obj)); + fieldDescriptor fd; + Reflection::resolve_field(field_mirror, receiver, &fd, true, CHECK); + BasicType value_type = (BasicType) vCode; + Reflection::field_set(&v, &fd, receiver, value_type, CHECK); +JVM_END + + +// Method /////////////////////////////////////////////////////////////////////////////////////////// + +JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0)) + JVMWrapper("JVM_InvokeMethod"); + Handle method_handle; + if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) { + method_handle = Handle(THREAD, JNIHandles::resolve(method)); + Handle receiver(THREAD, JNIHandles::resolve(obj)); + objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0))); + oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL); + jobject res = JNIHandles::make_local(env, result); + if (JvmtiExport::should_post_vm_object_alloc()) { + oop ret_type = java_lang_reflect_Method::return_type(method_handle()); + assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!"); + if (java_lang_Class::is_primitive(ret_type)) { + // Only for primitive type vm allocates memory for java object. + // See box() method. + JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); + } + } + return res; + } else { + THROW_0(vmSymbols::java_lang_StackOverflowError()); + } +JVM_END + + +JVM_ENTRY(jobject, JVM_NewInstanceFromConstructor(JNIEnv *env, jobject c, jobjectArray args0)) + JVMWrapper("JVM_NewInstanceFromConstructor"); + oop constructor_mirror = JNIHandles::resolve(c); + objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0))); + oop result = Reflection::invoke_constructor(constructor_mirror, args, CHECK_NULL); + jobject res = JNIHandles::make_local(env, result); + if (JvmtiExport::should_post_vm_object_alloc()) { + JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); + } + return res; +JVM_END + +#endif /* SUPPORT_OLD_REFLECTION */ + +// Atomic /////////////////////////////////////////////////////////////////////////////////////////// + +JVM_LEAF(jboolean, JVM_SupportsCX8()) + JVMWrapper("JVM_SupportsCX8"); + return VM_Version::supports_cx8(); +JVM_END + + +JVM_ENTRY(jboolean, JVM_CX8Field(JNIEnv *env, jobject obj, jfieldID fid, jlong oldVal, jlong newVal)) + JVMWrapper("JVM_CX8Field"); + jlong res; + oop o = JNIHandles::resolve(obj); + intptr_t fldOffs = jfieldIDWorkaround::from_instance_jfieldID(o->klass(), fid); + volatile jlong* addr = (volatile jlong*)((address)o + fldOffs); + + assert(VM_Version::supports_cx8(), "cx8 not supported"); + res = Atomic::cmpxchg(newVal, addr, oldVal); + + return res == oldVal; +JVM_END + +// Returns an array of all live Thread objects (VM internal JavaThreads, +// jvmti agent threads, and JNI attaching threads are skipped) +// See CR 6404306 regarding JNI attaching threads +JVM_ENTRY(jobjectArray, JVM_GetAllThreads(JNIEnv *env, jclass dummy)) + ResourceMark rm(THREAD); + ThreadsListEnumerator tle(THREAD, false, false); + JvmtiVMObjectAllocEventCollector oam; + + int num_threads = tle.num_threads(); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::thread_klass(), num_threads, CHECK_NULL); + objArrayHandle threads_ah(THREAD, r); + + for (int i = 0; i < num_threads; i++) { + Handle h = tle.get_threadObj(i); + threads_ah->obj_at_put(i, h()); + } + + return (jobjectArray) JNIHandles::make_local(env, threads_ah()); +JVM_END + + +// Support for java.lang.Thread.getStackTrace() and getAllStackTraces() methods +// Return StackTraceElement[][], each element is the stack trace of a thread in +// the corresponding entry in the given threads array +JVM_ENTRY(jobjectArray, JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobjectArray threads)) + JVMWrapper("JVM_DumpThreads"); + JvmtiVMObjectAllocEventCollector oam; + + // Check if threads is null + if (threads == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), 0); + } + + objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(threads)); + objArrayHandle ah(THREAD, a); + int num_threads = ah->length(); + // check if threads is non-empty array + if (num_threads == 0) { + THROW_(vmSymbols::java_lang_IllegalArgumentException(), 0); + } + + // check if threads is not an array of objects of Thread class + klassOop k = objArrayKlass::cast(ah->klass())->element_klass(); + if (k != SystemDictionary::thread_klass()) { + THROW_(vmSymbols::java_lang_IllegalArgumentException(), 0); + } + + ResourceMark rm(THREAD); + + GrowableArray* thread_handle_array = new GrowableArray(num_threads); + for (int i = 0; i < num_threads; i++) { + oop thread_obj = ah->obj_at(i); + instanceHandle h(THREAD, (instanceOop) thread_obj); + thread_handle_array->append(h); + } + + Handle stacktraces = ThreadService::dump_stack_traces(thread_handle_array, num_threads, CHECK_NULL); + return (jobjectArray)JNIHandles::make_local(env, stacktraces()); + +JVM_END + +// JVM monitoring and management support +JVM_ENTRY_NO_ENV(void*, JVM_GetManagement(jint version)) + return Management::get_jmm_interface(version); +JVM_END + +// com.sun.tools.attach.VirtualMachine agent properties support +// +// Initialize the agent properties with the properties maintained in the VM +JVM_ENTRY(jobject, JVM_InitAgentProperties(JNIEnv *env, jobject properties)) + JVMWrapper("JVM_InitAgentProperties"); + ResourceMark rm; + + Handle props(THREAD, JNIHandles::resolve_non_null(properties)); + + PUTPROP(props, "sun.java.command", Arguments::java_command()); + PUTPROP(props, "sun.jvm.flags", Arguments::jvm_flags()); + PUTPROP(props, "sun.jvm.args", Arguments::jvm_args()); + return properties; +JVM_END + +JVM_ENTRY(jobjectArray, JVM_GetEnclosingMethodInfo(JNIEnv *env, jclass ofClass)) +{ + JVMWrapper("JVM_GetEnclosingMethodInfo"); + JvmtiVMObjectAllocEventCollector oam; + + if (ofClass == NULL) { + return NULL; + } + Handle mirror(THREAD, JNIHandles::resolve_non_null(ofClass)); + // Special handling for primitive objects + if (java_lang_Class::is_primitive(mirror())) { + return NULL; + } + klassOop k = java_lang_Class::as_klassOop(mirror()); + if (!Klass::cast(k)->oop_is_instance()) { + return NULL; + } + instanceKlassHandle ik_h(THREAD, k); + int encl_method_class_idx = ik_h->enclosing_method_class_index(); + if (encl_method_class_idx == 0) { + return NULL; + } + objArrayOop dest_o = oopFactory::new_objArray(SystemDictionary::object_klass(), 3, CHECK_NULL); + objArrayHandle dest(THREAD, dest_o); + klassOop enc_k = ik_h->constants()->klass_at(encl_method_class_idx, CHECK_NULL); + dest->obj_at_put(0, Klass::cast(enc_k)->java_mirror()); + int encl_method_method_idx = ik_h->enclosing_method_method_index(); + if (encl_method_method_idx != 0) { + symbolOop sym_o = ik_h->constants()->symbol_at( + extract_low_short_from_int( + ik_h->constants()->name_and_type_at(encl_method_method_idx))); + symbolHandle sym(THREAD, sym_o); + Handle str = java_lang_String::create_from_symbol(sym, CHECK_NULL); + dest->obj_at_put(1, str()); + sym_o = ik_h->constants()->symbol_at( + extract_high_short_from_int( + ik_h->constants()->name_and_type_at(encl_method_method_idx))); + sym = symbolHandle(THREAD, sym_o); + str = java_lang_String::create_from_symbol(sym, CHECK_NULL); + dest->obj_at_put(2, str()); + } + return (jobjectArray) JNIHandles::make_local(dest()); +} +JVM_END + +JVM_ENTRY(jintArray, JVM_GetThreadStateValues(JNIEnv* env, + jint javaThreadState)) +{ + // If new thread states are added in future JDK and VM versions, + // this should check if the JDK version is compatible with thread + // states supported by the VM. Return NULL if not compatible. + // + // This function must map the VM java_lang_Thread::ThreadStatus + // to the Java thread state that the JDK supports. + // + + typeArrayHandle values_h; + switch (javaThreadState) { + case JAVA_THREAD_STATE_NEW : { + typeArrayOop r = oopFactory::new_typeArray(T_INT, 1, CHECK_NULL); + values_h = typeArrayHandle(THREAD, r); + values_h->int_at_put(0, java_lang_Thread::NEW); + break; + } + case JAVA_THREAD_STATE_RUNNABLE : { + typeArrayOop r = oopFactory::new_typeArray(T_INT, 1, CHECK_NULL); + values_h = typeArrayHandle(THREAD, r); + values_h->int_at_put(0, java_lang_Thread::RUNNABLE); + break; + } + case JAVA_THREAD_STATE_BLOCKED : { + typeArrayOop r = oopFactory::new_typeArray(T_INT, 1, CHECK_NULL); + values_h = typeArrayHandle(THREAD, r); + values_h->int_at_put(0, java_lang_Thread::BLOCKED_ON_MONITOR_ENTER); + break; + } + case JAVA_THREAD_STATE_WAITING : { + typeArrayOop r = oopFactory::new_typeArray(T_INT, 2, CHECK_NULL); + values_h = typeArrayHandle(THREAD, r); + values_h->int_at_put(0, java_lang_Thread::IN_OBJECT_WAIT); + values_h->int_at_put(1, java_lang_Thread::PARKED); + break; + } + case JAVA_THREAD_STATE_TIMED_WAITING : { + typeArrayOop r = oopFactory::new_typeArray(T_INT, 3, CHECK_NULL); + values_h = typeArrayHandle(THREAD, r); + values_h->int_at_put(0, java_lang_Thread::SLEEPING); + values_h->int_at_put(1, java_lang_Thread::IN_OBJECT_WAIT_TIMED); + values_h->int_at_put(2, java_lang_Thread::PARKED_TIMED); + break; + } + case JAVA_THREAD_STATE_TERMINATED : { + typeArrayOop r = oopFactory::new_typeArray(T_INT, 1, CHECK_NULL); + values_h = typeArrayHandle(THREAD, r); + values_h->int_at_put(0, java_lang_Thread::TERMINATED); + break; + } + default: + // Unknown state - probably incompatible JDK version + return NULL; + } + + return (jintArray) JNIHandles::make_local(env, values_h()); +} +JVM_END + + +JVM_ENTRY(jobjectArray, JVM_GetThreadStateNames(JNIEnv* env, + jint javaThreadState, + jintArray values)) +{ + // If new thread states are added in future JDK and VM versions, + // this should check if the JDK version is compatible with thread + // states supported by the VM. Return NULL if not compatible. + // + // This function must map the VM java_lang_Thread::ThreadStatus + // to the Java thread state that the JDK supports. + // + + ResourceMark rm; + + // Check if threads is null + if (values == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), 0); + } + + typeArrayOop v = typeArrayOop(JNIHandles::resolve_non_null(values)); + typeArrayHandle values_h(THREAD, v); + + objArrayHandle names_h; + switch (javaThreadState) { + case JAVA_THREAD_STATE_NEW : { + assert(values_h->length() == 1 && + values_h->int_at(0) == java_lang_Thread::NEW, + "Invalid threadStatus value"); + + objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + 1, /* only 1 substate */ + CHECK_NULL); + names_h = objArrayHandle(THREAD, r); + Handle name = java_lang_String::create_from_str("NEW", CHECK_NULL); + names_h->obj_at_put(0, name()); + break; + } + case JAVA_THREAD_STATE_RUNNABLE : { + assert(values_h->length() == 1 && + values_h->int_at(0) == java_lang_Thread::RUNNABLE, + "Invalid threadStatus value"); + + objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + 1, /* only 1 substate */ + CHECK_NULL); + names_h = objArrayHandle(THREAD, r); + Handle name = java_lang_String::create_from_str("RUNNABLE", CHECK_NULL); + names_h->obj_at_put(0, name()); + break; + } + case JAVA_THREAD_STATE_BLOCKED : { + assert(values_h->length() == 1 && + values_h->int_at(0) == java_lang_Thread::BLOCKED_ON_MONITOR_ENTER, + "Invalid threadStatus value"); + + objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + 1, /* only 1 substate */ + CHECK_NULL); + names_h = objArrayHandle(THREAD, r); + Handle name = java_lang_String::create_from_str("BLOCKED", CHECK_NULL); + names_h->obj_at_put(0, name()); + break; + } + case JAVA_THREAD_STATE_WAITING : { + assert(values_h->length() == 2 && + values_h->int_at(0) == java_lang_Thread::IN_OBJECT_WAIT && + values_h->int_at(1) == java_lang_Thread::PARKED, + "Invalid threadStatus value"); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + 2, /* number of substates */ + CHECK_NULL); + names_h = objArrayHandle(THREAD, r); + Handle name0 = java_lang_String::create_from_str("WAITING.OBJECT_WAIT", + CHECK_NULL); + Handle name1 = java_lang_String::create_from_str("WAITING.PARKED", + CHECK_NULL); + names_h->obj_at_put(0, name0()); + names_h->obj_at_put(1, name1()); + break; + } + case JAVA_THREAD_STATE_TIMED_WAITING : { + assert(values_h->length() == 3 && + values_h->int_at(0) == java_lang_Thread::SLEEPING && + values_h->int_at(1) == java_lang_Thread::IN_OBJECT_WAIT_TIMED && + values_h->int_at(2) == java_lang_Thread::PARKED_TIMED, + "Invalid threadStatus value"); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + 3, /* number of substates */ + CHECK_NULL); + names_h = objArrayHandle(THREAD, r); + Handle name0 = java_lang_String::create_from_str("TIMED_WAITING.SLEEPING", + CHECK_NULL); + Handle name1 = java_lang_String::create_from_str("TIMED_WAITING.OBJECT_WAIT", + CHECK_NULL); + Handle name2 = java_lang_String::create_from_str("TIMED_WAITING.PARKED", + CHECK_NULL); + names_h->obj_at_put(0, name0()); + names_h->obj_at_put(1, name1()); + names_h->obj_at_put(2, name2()); + break; + } + case JAVA_THREAD_STATE_TERMINATED : { + assert(values_h->length() == 1 && + values_h->int_at(0) == java_lang_Thread::TERMINATED, + "Invalid threadStatus value"); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + 1, /* only 1 substate */ + CHECK_NULL); + names_h = objArrayHandle(THREAD, r); + Handle name = java_lang_String::create_from_str("TERMINATED", CHECK_NULL); + names_h->obj_at_put(0, name()); + break; + } + default: + // Unknown state - probably incompatible JDK version + return NULL; + } + return (jobjectArray) JNIHandles::make_local(env, names_h()); +} +JVM_END + +JVM_ENTRY(void, JVM_GetVersionInfo(JNIEnv* env, jvm_version_info* info, size_t info_size)) +{ + memset(info, 0, sizeof(info_size)); + + info->jvm_version = Abstract_VM_Version::jvm_version(); + info->update_version = 0; /* 0 in HotSpot Express VM */ + info->special_update_version = 0; /* 0 in HotSpot Express VM */ + + // when we add a new capability in the jvm_version_info struct, we should also + // consider to expose this new capability in the sun.rt.jvmCapabilities jvmstat + // counter defined in runtimeService.cpp. + info->is_attachable = AttachListener::is_attach_supported(); +#ifdef KERNEL + info->is_kernel_jvm = 1; // true; +#else // KERNEL + info->is_kernel_jvm = 0; // false; +#endif // KERNEL +} +JVM_END diff --git a/hotspot/src/share/vm/prims/jvm.h b/hotspot/src/share/vm/prims/jvm.h new file mode 100644 index 00000000000..a8012ce8b54 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvm.h @@ -0,0 +1,1588 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _JAVASOFT_JVM_H_ +#define _JAVASOFT_JVM_H_ + +// HotSpot integration note: +// +// This file and jvm.h used with the JDK are identical, +// except for the three includes removed below and the +// SUPPORT_OLD_REFLECTION sections cut out of the JDK's jvm.h. + +// #include +// #include "jni.h" +// #include "jvm_md.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This file contains additional functions exported from the VM. + * These functions are complementary to the standard JNI support. + * There are three parts to this file: + * + * First, this file contains the VM-related functions needed by native + * libraries in the standard Java API. For example, the java.lang.Object + * class needs VM-level functions that wait for and notify monitors. + * + * Second, this file contains the functions and constant definitions + * needed by the byte code verifier and class file format checker. + * These functions allow the verifier and format checker to be written + * in a VM-independent way. + * + * Third, this file contains various I/O and nerwork operations needed + * by the standard Java I/O and network APIs. + */ + +/* + * Bump the version number when either of the following happens: + * + * 1. There is a change in JVM_* functions. + * + * 2. There is a change in the contract between VM and Java classes. + * For example, if the VM relies on a new private field in Thread + * class. + */ + +#define JVM_INTERFACE_VERSION 4 + + +JNIEXPORT jint JNICALL +JVM_GetInterfaceVersion(void); + +/************************************************************************* + PART 1: Functions for Native Libraries + ************************************************************************/ +/* + * java.lang.Object + */ +JNIEXPORT jint JNICALL +JVM_IHashCode(JNIEnv *env, jobject obj); + +JNIEXPORT void JNICALL +JVM_MonitorWait(JNIEnv *env, jobject obj, jlong ms); + +JNIEXPORT void JNICALL +JVM_MonitorNotify(JNIEnv *env, jobject obj); + +JNIEXPORT void JNICALL +JVM_MonitorNotifyAll(JNIEnv *env, jobject obj); + +JNIEXPORT jobject JNICALL +JVM_Clone(JNIEnv *env, jobject obj); + +/* + * java.lang.String + */ +JNIEXPORT jstring JNICALL +JVM_InternString(JNIEnv *env, jstring str); + +/* + * java.lang.System + */ +JNIEXPORT jlong JNICALL +JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored); + +JNIEXPORT jlong JNICALL +JVM_NanoTime(JNIEnv *env, jclass ignored); + +JNIEXPORT void JNICALL +JVM_ArrayCopy(JNIEnv *env, jclass ignored, jobject src, jint src_pos, + jobject dst, jint dst_pos, jint length); + +JNIEXPORT jobject JNICALL +JVM_InitProperties(JNIEnv *env, jobject p); + +/* + * java.io.File + */ +JNIEXPORT void JNICALL +JVM_OnExit(void (*func)(void)); + +/* + * java.lang.Runtime + */ +JNIEXPORT void JNICALL +JVM_Exit(jint code); + +JNIEXPORT void JNICALL +JVM_Halt(jint code); + +JNIEXPORT void JNICALL +JVM_GC(void); + +/* Returns the number of real-time milliseconds that have elapsed since the + * least-recently-inspected heap object was last inspected by the garbage + * collector. + * + * For simple stop-the-world collectors this value is just the time + * since the most recent collection. For generational collectors it is the + * time since the oldest generation was most recently collected. Other + * collectors are free to return a pessimistic estimate of the elapsed time, or + * simply the time since the last full collection was performed. + * + * Note that in the presence of reference objects, a given object that is no + * longer strongly reachable may have to be inspected multiple times before it + * can be reclaimed. + */ +JNIEXPORT jlong JNICALL +JVM_MaxObjectInspectionAge(void); + +JNIEXPORT void JNICALL +JVM_TraceInstructions(jboolean on); + +JNIEXPORT void JNICALL +JVM_TraceMethodCalls(jboolean on); + +JNIEXPORT jlong JNICALL +JVM_TotalMemory(void); + +JNIEXPORT jlong JNICALL +JVM_FreeMemory(void); + +JNIEXPORT jlong JNICALL +JVM_MaxMemory(void); + +JNIEXPORT jint JNICALL +JVM_ActiveProcessorCount(void); + +JNIEXPORT void * JNICALL +JVM_LoadLibrary(const char *name); + +JNIEXPORT void JNICALL +JVM_UnloadLibrary(void * handle); + +JNIEXPORT void * JNICALL +JVM_FindLibraryEntry(void *handle, const char *name); + +JNIEXPORT jboolean JNICALL +JVM_IsSupportedJNIVersion(jint version); + +/* + * java.lang.Float and java.lang.Double + */ +JNIEXPORT jboolean JNICALL +JVM_IsNaN(jdouble d); + +/* + * java.lang.Throwable + */ +JNIEXPORT void JNICALL +JVM_FillInStackTrace(JNIEnv *env, jobject throwable); + +JNIEXPORT void JNICALL +JVM_PrintStackTrace(JNIEnv *env, jobject throwable, jobject printable); + +JNIEXPORT jint JNICALL +JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable); + +JNIEXPORT jobject JNICALL +JVM_GetStackTraceElement(JNIEnv *env, jobject throwable, jint index); + +/* + * java.lang.Compiler + */ +JNIEXPORT void JNICALL +JVM_InitializeCompiler (JNIEnv *env, jclass compCls); + +JNIEXPORT jboolean JNICALL +JVM_IsSilentCompiler(JNIEnv *env, jclass compCls); + +JNIEXPORT jboolean JNICALL +JVM_CompileClass(JNIEnv *env, jclass compCls, jclass cls); + +JNIEXPORT jboolean JNICALL +JVM_CompileClasses(JNIEnv *env, jclass cls, jstring jname); + +JNIEXPORT jobject JNICALL +JVM_CompilerCommand(JNIEnv *env, jclass compCls, jobject arg); + +JNIEXPORT void JNICALL +JVM_EnableCompiler(JNIEnv *env, jclass compCls); + +JNIEXPORT void JNICALL +JVM_DisableCompiler(JNIEnv *env, jclass compCls); + +/* + * java.lang.Thread + */ +JNIEXPORT void JNICALL +JVM_StartThread(JNIEnv *env, jobject thread); + +JNIEXPORT void JNICALL +JVM_StopThread(JNIEnv *env, jobject thread, jobject exception); + +JNIEXPORT jboolean JNICALL +JVM_IsThreadAlive(JNIEnv *env, jobject thread); + +JNIEXPORT void JNICALL +JVM_SuspendThread(JNIEnv *env, jobject thread); + +JNIEXPORT void JNICALL +JVM_ResumeThread(JNIEnv *env, jobject thread); + +JNIEXPORT void JNICALL +JVM_SetThreadPriority(JNIEnv *env, jobject thread, jint prio); + +JNIEXPORT void JNICALL +JVM_Yield(JNIEnv *env, jclass threadClass); + +JNIEXPORT void JNICALL +JVM_Sleep(JNIEnv *env, jclass threadClass, jlong millis); + +JNIEXPORT jobject JNICALL +JVM_CurrentThread(JNIEnv *env, jclass threadClass); + +JNIEXPORT jint JNICALL +JVM_CountStackFrames(JNIEnv *env, jobject thread); + +JNIEXPORT void JNICALL +JVM_Interrupt(JNIEnv *env, jobject thread); + +JNIEXPORT jboolean JNICALL +JVM_IsInterrupted(JNIEnv *env, jobject thread, jboolean clearInterrupted); + +JNIEXPORT jboolean JNICALL +JVM_HoldsLock(JNIEnv *env, jclass threadClass, jobject obj); + +JNIEXPORT void JNICALL +JVM_DumpAllStacks(JNIEnv *env, jclass unused); + +JNIEXPORT jobjectArray JNICALL +JVM_GetAllThreads(JNIEnv *env, jclass dummy); + +/* getStackTrace() and getAllStackTraces() method */ +JNIEXPORT jobjectArray JNICALL +JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobjectArray threads); + +/* + * java.lang.SecurityManager + */ +JNIEXPORT jclass JNICALL +JVM_CurrentLoadedClass(JNIEnv *env); + +JNIEXPORT jobject JNICALL +JVM_CurrentClassLoader(JNIEnv *env); + +JNIEXPORT jobjectArray JNICALL +JVM_GetClassContext(JNIEnv *env); + +JNIEXPORT jint JNICALL +JVM_ClassDepth(JNIEnv *env, jstring name); + +JNIEXPORT jint JNICALL +JVM_ClassLoaderDepth(JNIEnv *env); + +/* + * java.lang.Package + */ +JNIEXPORT jstring JNICALL +JVM_GetSystemPackage(JNIEnv *env, jstring name); + +JNIEXPORT jobjectArray JNICALL +JVM_GetSystemPackages(JNIEnv *env); + +/* + * java.io.ObjectInputStream + */ +JNIEXPORT jobject JNICALL +JVM_AllocateNewObject(JNIEnv *env, jobject obj, jclass currClass, + jclass initClass); + +JNIEXPORT jobject JNICALL +JVM_AllocateNewArray(JNIEnv *env, jobject obj, jclass currClass, + jint length); + +JNIEXPORT jobject JNICALL +JVM_LatestUserDefinedLoader(JNIEnv *env); + +/* + * This function has been deprecated and should not be considered + * part of the specified JVM interface. + */ +JNIEXPORT jclass JNICALL +JVM_LoadClass0(JNIEnv *env, jobject obj, jclass currClass, + jstring currClassName); + +/* + * java.lang.reflect.Array + */ +JNIEXPORT jint JNICALL +JVM_GetArrayLength(JNIEnv *env, jobject arr); + +JNIEXPORT jobject JNICALL +JVM_GetArrayElement(JNIEnv *env, jobject arr, jint index); + +JNIEXPORT jvalue JNICALL +JVM_GetPrimitiveArrayElement(JNIEnv *env, jobject arr, jint index, jint wCode); + +JNIEXPORT void JNICALL +JVM_SetArrayElement(JNIEnv *env, jobject arr, jint index, jobject val); + +JNIEXPORT void JNICALL +JVM_SetPrimitiveArrayElement(JNIEnv *env, jobject arr, jint index, jvalue v, + unsigned char vCode); + +JNIEXPORT jobject JNICALL +JVM_NewArray(JNIEnv *env, jclass eltClass, jint length); + +JNIEXPORT jobject JNICALL +JVM_NewMultiArray(JNIEnv *env, jclass eltClass, jintArray dim); + +/* + * java.lang.Class and java.lang.ClassLoader + */ +/* + * Returns the class in which the code invoking the native method + * belongs. + * + * Note that in JDK 1.1, native methods did not create a frame. + * In 1.2, they do. Therefore native methods like Class.forName + * can no longer look at the current frame for the caller class. + */ +JNIEXPORT jclass JNICALL +JVM_GetCallerClass(JNIEnv *env, int n); + +/* + * Find primitive classes + * utf: class name + */ +JNIEXPORT jclass JNICALL +JVM_FindPrimitiveClass(JNIEnv *env, const char *utf); + +/* + * Link the class + */ +JNIEXPORT void JNICALL +JVM_ResolveClass(JNIEnv *env, jclass cls); + +/* + * Find a class from a given class loader. Throw ClassNotFoundException + * or NoClassDefFoundError depending on the value of the last + * argument. + */ +JNIEXPORT jclass JNICALL +JVM_FindClassFromClassLoader(JNIEnv *env, const char *name, jboolean init, + jobject loader, jboolean throwError); + +/* + * Find a class from a given class. + */ +JNIEXPORT jclass JNICALL +JVM_FindClassFromClass(JNIEnv *env, const char *name, jboolean init, + jclass from); + +/* Find a loaded class cached by the VM */ +JNIEXPORT jclass JNICALL +JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name); + +/* Define a class */ +JNIEXPORT jclass JNICALL +JVM_DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, + jsize len, jobject pd); + +/* Define a class with a source (added in JDK1.5) */ +JNIEXPORT jclass JNICALL +JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, + const jbyte *buf, jsize len, jobject pd, + const char *source); + +/* + * Reflection support functions + */ + +JNIEXPORT jstring JNICALL +JVM_GetClassName(JNIEnv *env, jclass cls); + +JNIEXPORT jobjectArray JNICALL +JVM_GetClassInterfaces(JNIEnv *env, jclass cls); + +JNIEXPORT jobject JNICALL +JVM_GetClassLoader(JNIEnv *env, jclass cls); + +JNIEXPORT jboolean JNICALL +JVM_IsInterface(JNIEnv *env, jclass cls); + +JNIEXPORT jobjectArray JNICALL +JVM_GetClassSigners(JNIEnv *env, jclass cls); + +JNIEXPORT void JNICALL +JVM_SetClassSigners(JNIEnv *env, jclass cls, jobjectArray signers); + +JNIEXPORT jobject JNICALL +JVM_GetProtectionDomain(JNIEnv *env, jclass cls); + +JNIEXPORT void JNICALL +JVM_SetProtectionDomain(JNIEnv *env, jclass cls, jobject protection_domain); + +JNIEXPORT jboolean JNICALL +JVM_IsArrayClass(JNIEnv *env, jclass cls); + +JNIEXPORT jboolean JNICALL +JVM_IsPrimitiveClass(JNIEnv *env, jclass cls); + +JNIEXPORT jclass JNICALL +JVM_GetComponentType(JNIEnv *env, jclass cls); + +JNIEXPORT jint JNICALL +JVM_GetClassModifiers(JNIEnv *env, jclass cls); + +JNIEXPORT jobjectArray JNICALL +JVM_GetDeclaredClasses(JNIEnv *env, jclass ofClass); + +JNIEXPORT jclass JNICALL +JVM_GetDeclaringClass(JNIEnv *env, jclass ofClass); + +/* Generics support (JDK 1.5) */ +JNIEXPORT jstring JNICALL +JVM_GetClassSignature(JNIEnv *env, jclass cls); + +/* Annotations support (JDK 1.5) */ +JNIEXPORT jbyteArray JNICALL +JVM_GetClassAnnotations(JNIEnv *env, jclass cls); + +/* Annotations support (JDK 1.6) */ + +// field is a handle to a java.lang.reflect.Field object +JNIEXPORT jbyteArray JNICALL +JVM_GetFieldAnnotations(JNIEnv *env, jobject field); + +// method is a handle to a java.lang.reflect.Method object +JNIEXPORT jbyteArray JNICALL +JVM_GetMethodAnnotations(JNIEnv *env, jobject method); + +// method is a handle to a java.lang.reflect.Method object +JNIEXPORT jbyteArray JNICALL +JVM_GetMethodDefaultAnnotationValue(JNIEnv *env, jobject method); + +// method is a handle to a java.lang.reflect.Method object +JNIEXPORT jbyteArray JNICALL +JVM_GetMethodParameterAnnotations(JNIEnv *env, jobject method); + + +/* + * New (JDK 1.4) reflection implementation + */ + +JNIEXPORT jobjectArray JNICALL +JVM_GetClassDeclaredMethods(JNIEnv *env, jclass ofClass, jboolean publicOnly); + +JNIEXPORT jobjectArray JNICALL +JVM_GetClassDeclaredFields(JNIEnv *env, jclass ofClass, jboolean publicOnly); + +JNIEXPORT jobjectArray JNICALL +JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofClass, jboolean publicOnly); + +/* Differs from JVM_GetClassModifiers in treatment of inner classes. + This returns the access flags for the class as specified in the + class file rather than searching the InnerClasses attribute (if + present) to find the source-level access flags. Only the values of + the low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be + valid. */ +JNIEXPORT jint JNICALL +JVM_GetClassAccessFlags(JNIEnv *env, jclass cls); + +/* + * Constant pool access; currently used to implement reflective access to annotations (JDK 1.5) + */ + +JNIEXPORT jobject JNICALL +JVM_GetClassConstantPool(JNIEnv *env, jclass cls); + +JNIEXPORT jint JNICALL JVM_ConstantPoolGetSize +(JNIEnv *env, jobject unused, jobject jcpool); + +JNIEXPORT jclass JNICALL JVM_ConstantPoolGetClassAt +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jclass JNICALL JVM_ConstantPoolGetClassAtIfLoaded +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jobject JNICALL JVM_ConstantPoolGetMethodAt +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jobject JNICALL JVM_ConstantPoolGetMethodAtIfLoaded +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jobject JNICALL JVM_ConstantPoolGetFieldAt +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jobject JNICALL JVM_ConstantPoolGetFieldAtIfLoaded +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jobjectArray JNICALL JVM_ConstantPoolGetMemberRefInfoAt +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jint JNICALL JVM_ConstantPoolGetIntAt +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jlong JNICALL JVM_ConstantPoolGetLongAt +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jfloat JNICALL JVM_ConstantPoolGetFloatAt +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jdouble JNICALL JVM_ConstantPoolGetDoubleAt +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jstring JNICALL JVM_ConstantPoolGetStringAt +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +JNIEXPORT jstring JNICALL JVM_ConstantPoolGetUTF8At +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + +/* + * java.security.* + */ + +JNIEXPORT jobject JNICALL +JVM_DoPrivileged(JNIEnv *env, jclass cls, + jobject action, jobject context, jboolean wrapException); + +JNIEXPORT jobject JNICALL +JVM_GetInheritedAccessControlContext(JNIEnv *env, jclass cls); + +JNIEXPORT jobject JNICALL +JVM_GetStackAccessControlContext(JNIEnv *env, jclass cls); + +/* + * Signal support, used to implement the shutdown sequence. Every VM must + * support JVM_SIGINT and JVM_SIGTERM, raising the former for user interrupts + * (^C) and the latter for external termination (kill, system shutdown, etc.). + * Other platform-dependent signal values may also be supported. + */ + +JNIEXPORT void * JNICALL +JVM_RegisterSignal(jint sig, void *handler); + +JNIEXPORT jboolean JNICALL +JVM_RaiseSignal(jint sig); + +JNIEXPORT jint JNICALL +JVM_FindSignal(const char *name); + +/* + * Retrieve the assertion directives for the specified class. + */ +JNIEXPORT jboolean JNICALL +JVM_DesiredAssertionStatus(JNIEnv *env, jclass unused, jclass cls); + +/* + * Retrieve the assertion directives from the VM. + */ +JNIEXPORT jobject JNICALL +JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused); + +/* + * sun.misc.AtomicLong + */ +JNIEXPORT jboolean JNICALL +JVM_SupportsCX8(void); + +JNIEXPORT jboolean JNICALL +JVM_CX8Field(JNIEnv *env, jobject obj, jfieldID fldID, jlong oldVal, jlong newVal); + +/************************************************************************* + PART 2: Support for the Verifier and Class File Format Checker + ************************************************************************/ +/* + * Return the class name in UTF format. The result is valid + * until JVM_ReleaseUTf is called. + * + * The caller must treat the string as a constant and not modify it + * in any way. + */ +JNIEXPORT const char * JNICALL +JVM_GetClassNameUTF(JNIEnv *env, jclass cb); + +/* + * Returns the constant pool types in the buffer provided by "types." + */ +JNIEXPORT void JNICALL +JVM_GetClassCPTypes(JNIEnv *env, jclass cb, unsigned char *types); + +/* + * Returns the number of Constant Pool entries. + */ +JNIEXPORT jint JNICALL +JVM_GetClassCPEntriesCount(JNIEnv *env, jclass cb); + +/* + * Returns the number of *declared* fields or methods. + */ +JNIEXPORT jint JNICALL +JVM_GetClassFieldsCount(JNIEnv *env, jclass cb); + +JNIEXPORT jint JNICALL +JVM_GetClassMethodsCount(JNIEnv *env, jclass cb); + +/* + * Returns the CP indexes of exceptions raised by a given method. + * Places the result in the given buffer. + * + * The method is identified by method_index. + */ +JNIEXPORT void JNICALL +JVM_GetMethodIxExceptionIndexes(JNIEnv *env, jclass cb, jint method_index, + unsigned short *exceptions); +/* + * Returns the number of exceptions raised by a given method. + * The method is identified by method_index. + */ +JNIEXPORT jint JNICALL +JVM_GetMethodIxExceptionsCount(JNIEnv *env, jclass cb, jint method_index); + +/* + * Returns the byte code sequence of a given method. + * Places the result in the given buffer. + * + * The method is identified by method_index. + */ +JNIEXPORT void JNICALL +JVM_GetMethodIxByteCode(JNIEnv *env, jclass cb, jint method_index, + unsigned char *code); + +/* + * Returns the length of the byte code sequence of a given method. + * The method is identified by method_index. + */ +JNIEXPORT jint JNICALL +JVM_GetMethodIxByteCodeLength(JNIEnv *env, jclass cb, jint method_index); + +/* + * A structure used to a capture exception table entry in a Java method. + */ +typedef struct { + jint start_pc; + jint end_pc; + jint handler_pc; + jint catchType; +} JVM_ExceptionTableEntryType; + +/* + * Returns the exception table entry at entry_index of a given method. + * Places the result in the given buffer. + * + * The method is identified by method_index. + */ +JNIEXPORT void JNICALL +JVM_GetMethodIxExceptionTableEntry(JNIEnv *env, jclass cb, jint method_index, + jint entry_index, + JVM_ExceptionTableEntryType *entry); + +/* + * Returns the length of the exception table of a given method. + * The method is identified by method_index. + */ +JNIEXPORT jint JNICALL +JVM_GetMethodIxExceptionTableLength(JNIEnv *env, jclass cb, int index); + +/* + * Returns the modifiers of a given field. + * The field is identified by field_index. + */ +JNIEXPORT jint JNICALL +JVM_GetFieldIxModifiers(JNIEnv *env, jclass cb, int index); + +/* + * Returns the modifiers of a given method. + * The method is identified by method_index. + */ +JNIEXPORT jint JNICALL +JVM_GetMethodIxModifiers(JNIEnv *env, jclass cb, int index); + +/* + * Returns the number of local variables of a given method. + * The method is identified by method_index. + */ +JNIEXPORT jint JNICALL +JVM_GetMethodIxLocalsCount(JNIEnv *env, jclass cb, int index); + +/* + * Returns the number of arguments (including this pointer) of a given method. + * The method is identified by method_index. + */ +JNIEXPORT jint JNICALL +JVM_GetMethodIxArgsSize(JNIEnv *env, jclass cb, int index); + +/* + * Returns the maximum amount of stack (in words) used by a given method. + * The method is identified by method_index. + */ +JNIEXPORT jint JNICALL +JVM_GetMethodIxMaxStack(JNIEnv *env, jclass cb, int index); + +/* + * Is a given method a constructor. + * The method is identified by method_index. + */ +JNIEXPORT jboolean JNICALL +JVM_IsConstructorIx(JNIEnv *env, jclass cb, int index); + +/* + * Returns the name of a given method in UTF format. + * The result remains valid until JVM_ReleaseUTF is called. + * + * The caller must treat the string as a constant and not modify it + * in any way. + */ +JNIEXPORT const char * JNICALL +JVM_GetMethodIxNameUTF(JNIEnv *env, jclass cb, jint index); + +/* + * Returns the signature of a given method in UTF format. + * The result remains valid until JVM_ReleaseUTF is called. + * + * The caller must treat the string as a constant and not modify it + * in any way. + */ +JNIEXPORT const char * JNICALL +JVM_GetMethodIxSignatureUTF(JNIEnv *env, jclass cb, jint index); + +/* + * Returns the name of the field refered to at a given constant pool + * index. + * + * The result is in UTF format and remains valid until JVM_ReleaseUTF + * is called. + * + * The caller must treat the string as a constant and not modify it + * in any way. + */ +JNIEXPORT const char * JNICALL +JVM_GetCPFieldNameUTF(JNIEnv *env, jclass cb, jint index); + +/* + * Returns the name of the method refered to at a given constant pool + * index. + * + * The result is in UTF format and remains valid until JVM_ReleaseUTF + * is called. + * + * The caller must treat the string as a constant and not modify it + * in any way. + */ +JNIEXPORT const char * JNICALL +JVM_GetCPMethodNameUTF(JNIEnv *env, jclass cb, jint index); + +/* + * Returns the signature of the method refered to at a given constant pool + * index. + * + * The result is in UTF format and remains valid until JVM_ReleaseUTF + * is called. + * + * The caller must treat the string as a constant and not modify it + * in any way. + */ +JNIEXPORT const char * JNICALL +JVM_GetCPMethodSignatureUTF(JNIEnv *env, jclass cb, jint index); + +/* + * Returns the signature of the field refered to at a given constant pool + * index. + * + * The result is in UTF format and remains valid until JVM_ReleaseUTF + * is called. + * + * The caller must treat the string as a constant and not modify it + * in any way. + */ +JNIEXPORT const char * JNICALL +JVM_GetCPFieldSignatureUTF(JNIEnv *env, jclass cb, jint index); + +/* + * Returns the class name refered to at a given constant pool index. + * + * The result is in UTF format and remains valid until JVM_ReleaseUTF + * is called. + * + * The caller must treat the string as a constant and not modify it + * in any way. + */ +JNIEXPORT const char * JNICALL +JVM_GetCPClassNameUTF(JNIEnv *env, jclass cb, jint index); + +/* + * Returns the class name refered to at a given constant pool index. + * + * The constant pool entry must refer to a CONSTANT_Fieldref. + * + * The result is in UTF format and remains valid until JVM_ReleaseUTF + * is called. + * + * The caller must treat the string as a constant and not modify it + * in any way. + */ +JNIEXPORT const char * JNICALL +JVM_GetCPFieldClassNameUTF(JNIEnv *env, jclass cb, jint index); + +/* + * Returns the class name refered to at a given constant pool index. + * + * The constant pool entry must refer to CONSTANT_Methodref or + * CONSTANT_InterfaceMethodref. + * + * The result is in UTF format and remains valid until JVM_ReleaseUTF + * is called. + * + * The caller must treat the string as a constant and not modify it + * in any way. + */ +JNIEXPORT const char * JNICALL +JVM_GetCPMethodClassNameUTF(JNIEnv *env, jclass cb, jint index); + +/* + * Returns the modifiers of a field in calledClass. The field is + * referred to in class cb at constant pool entry index. + * + * The caller must treat the string as a constant and not modify it + * in any way. + * + * Returns -1 if the field does not exist in calledClass. + */ +JNIEXPORT jint JNICALL +JVM_GetCPFieldModifiers(JNIEnv *env, jclass cb, int index, jclass calledClass); + +/* + * Returns the modifiers of a method in calledClass. The method is + * referred to in class cb at constant pool entry index. + * + * Returns -1 if the method does not exist in calledClass. + */ +JNIEXPORT jint JNICALL +JVM_GetCPMethodModifiers(JNIEnv *env, jclass cb, int index, jclass calledClass); + +/* + * Releases the UTF string obtained from the VM. + */ +JNIEXPORT void JNICALL +JVM_ReleaseUTF(const char *utf); + +/* + * Compare if two classes are in the same package. + */ +JNIEXPORT jboolean JNICALL +JVM_IsSameClassPackage(JNIEnv *env, jclass class1, jclass class2); + +/* Constants in class files */ + +#define JVM_ACC_PUBLIC 0x0001 /* visible to everyone */ +#define JVM_ACC_PRIVATE 0x0002 /* visible only to the defining class */ +#define JVM_ACC_PROTECTED 0x0004 /* visible to subclasses */ +#define JVM_ACC_STATIC 0x0008 /* instance variable is static */ +#define JVM_ACC_FINAL 0x0010 /* no further subclassing, overriding */ +#define JVM_ACC_SYNCHRONIZED 0x0020 /* wrap method call in monitor lock */ +#define JVM_ACC_SUPER 0x0020 /* funky handling of invokespecial */ +#define JVM_ACC_VOLATILE 0x0040 /* can not cache in registers */ +#define JVM_ACC_BRIDGE 0x0040 /* bridge method generated by compiler */ +#define JVM_ACC_TRANSIENT 0x0080 /* not persistent */ +#define JVM_ACC_VARARGS 0x0080 /* method declared with variable number of args */ +#define JVM_ACC_NATIVE 0x0100 /* implemented in C */ +#define JVM_ACC_INTERFACE 0x0200 /* class is an interface */ +#define JVM_ACC_ABSTRACT 0x0400 /* no definition provided */ +#define JVM_ACC_STRICT 0x0800 /* strict floating point */ +#define JVM_ACC_SYNTHETIC 0x1000 /* compiler-generated class, method or field */ +#define JVM_ACC_ANNOTATION 0x2000 /* annotation type */ +#define JVM_ACC_ENUM 0x4000 /* field is declared as element of enum */ + +#define JVM_ACC_PUBLIC_BIT 0 +#define JVM_ACC_PRIVATE_BIT 1 +#define JVM_ACC_PROTECTED_BIT 2 +#define JVM_ACC_STATIC_BIT 3 +#define JVM_ACC_FINAL_BIT 4 +#define JVM_ACC_SYNCHRONIZED_BIT 5 +#define JVM_ACC_SUPER_BIT 5 +#define JVM_ACC_VOLATILE_BIT 6 +#define JVM_ACC_BRIDGE_BIT 6 +#define JVM_ACC_TRANSIENT_BIT 7 +#define JVM_ACC_VARARGS_BIT 7 +#define JVM_ACC_NATIVE_BIT 8 +#define JVM_ACC_INTERFACE_BIT 9 +#define JVM_ACC_ABSTRACT_BIT 10 +#define JVM_ACC_STRICT_BIT 11 +#define JVM_ACC_SYNTHETIC_BIT 12 +#define JVM_ACC_ANNOTATION_BIT 13 +#define JVM_ACC_ENUM_BIT 14 + +// NOTE: replicated in SA in vm/agent/sun/jvm/hotspot/utilities/ConstantTag.java +enum { + JVM_CONSTANT_Utf8 = 1, + JVM_CONSTANT_Unicode, /* unused */ + JVM_CONSTANT_Integer, + JVM_CONSTANT_Float, + JVM_CONSTANT_Long, + JVM_CONSTANT_Double, + JVM_CONSTANT_Class, + JVM_CONSTANT_String, + JVM_CONSTANT_Fieldref, + JVM_CONSTANT_Methodref, + JVM_CONSTANT_InterfaceMethodref, + JVM_CONSTANT_NameAndType +}; + +/* Used in the newarray instruction. */ + +#define JVM_T_BOOLEAN 4 +#define JVM_T_CHAR 5 +#define JVM_T_FLOAT 6 +#define JVM_T_DOUBLE 7 +#define JVM_T_BYTE 8 +#define JVM_T_SHORT 9 +#define JVM_T_INT 10 +#define JVM_T_LONG 11 + +/* JVM method signatures */ + +#define JVM_SIGNATURE_ARRAY '[' +#define JVM_SIGNATURE_BYTE 'B' +#define JVM_SIGNATURE_CHAR 'C' +#define JVM_SIGNATURE_CLASS 'L' +#define JVM_SIGNATURE_ENDCLASS ';' +#define JVM_SIGNATURE_ENUM 'E' +#define JVM_SIGNATURE_FLOAT 'F' +#define JVM_SIGNATURE_DOUBLE 'D' +#define JVM_SIGNATURE_FUNC '(' +#define JVM_SIGNATURE_ENDFUNC ')' +#define JVM_SIGNATURE_INT 'I' +#define JVM_SIGNATURE_LONG 'J' +#define JVM_SIGNATURE_SHORT 'S' +#define JVM_SIGNATURE_VOID 'V' +#define JVM_SIGNATURE_BOOLEAN 'Z' + +/* + * A function defined by the byte-code verifier and called by the VM. + * This is not a function implemented in the VM. + * + * Returns JNI_FALSE if verification fails. A detailed error message + * will be places in msg_buf, whose length is specified by buf_len. + */ +typedef jboolean (*verifier_fn_t)(JNIEnv *env, + jclass cb, + char * msg_buf, + jint buf_len); + + +/* + * Support for a VM-independent class format checker. + */ +typedef struct { + unsigned long code; /* byte code */ + unsigned long excs; /* exceptions */ + unsigned long etab; /* catch table */ + unsigned long lnum; /* line number */ + unsigned long lvar; /* local vars */ +} method_size_info; + +typedef struct { + unsigned int constants; /* constant pool */ + unsigned int fields; + unsigned int methods; + unsigned int interfaces; + unsigned int fields2; /* number of static 2-word fields */ + unsigned int innerclasses; /* # of records in InnerClasses attr */ + + method_size_info clinit; /* memory used in clinit */ + method_size_info main; /* used everywhere else */ +} class_size_info; + +/* + * Functions defined in libjava.so to perform string conversions. + * + */ + +typedef jstring (*to_java_string_fn_t)(JNIEnv *env, char *str); + +typedef char *(*to_c_string_fn_t)(JNIEnv *env, jstring s, jboolean *b); + +/* This is the function defined in libjava.so that performs class + * format checks. This functions fills in size information about + * the class file and returns: + * + * 0: good + * -1: out of memory + * -2: bad format + * -3: unsupported version + * -4: bad class name + */ + +typedef jint (*check_format_fn_t)(char *class_name, + unsigned char *data, + unsigned int data_size, + class_size_info *class_size, + char *message_buffer, + jint buffer_length, + jboolean measure_only, + jboolean check_relaxed); + +#define JVM_RECOGNIZED_CLASS_MODIFIERS (JVM_ACC_PUBLIC | \ + JVM_ACC_FINAL | \ + JVM_ACC_SUPER | \ + JVM_ACC_INTERFACE | \ + JVM_ACC_ABSTRACT | \ + JVM_ACC_ANNOTATION | \ + JVM_ACC_ENUM | \ + JVM_ACC_SYNTHETIC) + +#define JVM_RECOGNIZED_FIELD_MODIFIERS (JVM_ACC_PUBLIC | \ + JVM_ACC_PRIVATE | \ + JVM_ACC_PROTECTED | \ + JVM_ACC_STATIC | \ + JVM_ACC_FINAL | \ + JVM_ACC_VOLATILE | \ + JVM_ACC_TRANSIENT | \ + JVM_ACC_ENUM | \ + JVM_ACC_SYNTHETIC) + +#define JVM_RECOGNIZED_METHOD_MODIFIERS (JVM_ACC_PUBLIC | \ + JVM_ACC_PRIVATE | \ + JVM_ACC_PROTECTED | \ + JVM_ACC_STATIC | \ + JVM_ACC_FINAL | \ + JVM_ACC_SYNCHRONIZED | \ + JVM_ACC_BRIDGE | \ + JVM_ACC_VARARGS | \ + JVM_ACC_NATIVE | \ + JVM_ACC_ABSTRACT | \ + JVM_ACC_STRICT | \ + JVM_ACC_SYNTHETIC) + +/* + * This is the function defined in libjava.so to perform path + * canonicalization. VM call this function before opening jar files + * to load system classes. + * + */ + +typedef int (*canonicalize_fn_t)(JNIEnv *env, char *orig, char *out, int len); + +/************************************************************************* + PART 3: I/O and Network Support + ************************************************************************/ + +/* Note that the JVM IO functions are expected to return JVM_IO_ERR + * when there is any kind of error. The caller can then use the + * platform specific support (e.g., errno) to get the detailed + * error info. The JVM_GetLastErrorString procedure may also be used + * to obtain a descriptive error string. + */ +#define JVM_IO_ERR (-1) + +/* For interruptible IO. Returning JVM_IO_INTR indicates that an IO + * operation has been disrupted by Thread.interrupt. There are a + * number of technical difficulties related to interruptible IO that + * need to be solved. For example, most existing programs do not handle + * InterruptedIOExceptions specially, they simply treat those as any + * IOExceptions, which typically indicate fatal errors. + * + * There are also two modes of operation for interruptible IO. In the + * resumption mode, an interrupted IO operation is guaranteed not to + * have any side-effects, and can be restarted. In the termination mode, + * an interrupted IO operation corrupts the underlying IO stream, so + * that the only reasonable operation on an interrupted stream is to + * close that stream. The resumption mode seems to be impossible to + * implement on Win32 and Solaris. Implementing the termination mode is + * easier, but it's not clear that's the right semantics. + * + * Interruptible IO is not supported on Win32.It can be enabled/disabled + * using a compile-time flag on Solaris. Third-party JVM ports do not + * need to implement interruptible IO. + */ +#define JVM_IO_INTR (-2) + +/* Write a string into the given buffer, in the platform's local encoding, + * that describes the most recent system-level error to occur in this thread. + * Return the length of the string or zero if no error occurred. + */ +JNIEXPORT jint JNICALL +JVM_GetLastErrorString(char *buf, int len); + +/* + * Convert a pathname into native format. This function does syntactic + * cleanup, such as removing redundant separator characters. It modifies + * the given pathname string in place. + */ +JNIEXPORT char * JNICALL +JVM_NativePath(char *); + +/* + * JVM I/O error codes + */ +#define JVM_EEXIST -100 + +/* + * Open a file descriptor. This function returns a negative error code + * on error, and a non-negative integer that is the file descriptor on + * success. + */ +JNIEXPORT jint JNICALL +JVM_Open(const char *fname, jint flags, jint mode); + +/* + * Close a file descriptor. This function returns -1 on error, and 0 + * on success. + * + * fd the file descriptor to close. + */ +JNIEXPORT jint JNICALL +JVM_Close(jint fd); + +/* + * Read data from a file decriptor into a char array. + * + * fd the file descriptor to read from. + * buf the buffer where to put the read data. + * nbytes the number of bytes to read. + * + * This function returns -1 on error, and 0 on success. + */ +JNIEXPORT jint JNICALL +JVM_Read(jint fd, char *buf, jint nbytes); + +/* + * Write data from a char array to a file decriptor. + * + * fd the file descriptor to read from. + * buf the buffer from which to fetch the data. + * nbytes the number of bytes to write. + * + * This function returns -1 on error, and 0 on success. + */ +JNIEXPORT jint JNICALL +JVM_Write(jint fd, char *buf, jint nbytes); + +/* + * Returns the number of bytes available for reading from a given file + * descriptor + */ +JNIEXPORT jint JNICALL +JVM_Available(jint fd, jlong *pbytes); + +/* + * Move the file descriptor pointer from whence by offset. + * + * fd the file descriptor to move. + * offset the number of bytes to move it by. + * whence the start from where to move it. + * + * This function returns the resulting pointer location. + */ +JNIEXPORT jlong JNICALL +JVM_Lseek(jint fd, jlong offset, jint whence); + +/* + * Set the length of the file associated with the given descriptor to the given + * length. If the new length is longer than the current length then the file + * is extended; the contents of the extended portion are not defined. The + * value of the file pointer is undefined after this procedure returns. + */ +JNIEXPORT jint JNICALL +JVM_SetLength(jint fd, jlong length); + +/* + * Synchronize the file descriptor's in memory state with that of the + * physical device. Return of -1 is an error, 0 is OK. + */ +JNIEXPORT jint JNICALL +JVM_Sync(jint fd); + +/* + * Networking library support + */ + +JNIEXPORT jint JNICALL +JVM_InitializeSocketLibrary(void); + +struct sockaddr; + +JNIEXPORT jint JNICALL +JVM_Socket(jint domain, jint type, jint protocol); + +JNIEXPORT jint JNICALL +JVM_SocketClose(jint fd); + +JNIEXPORT jint JNICALL +JVM_SocketShutdown(jint fd, jint howto); + +JNIEXPORT jint JNICALL +JVM_Recv(jint fd, char *buf, jint nBytes, jint flags); + +JNIEXPORT jint JNICALL +JVM_Send(jint fd, char *buf, jint nBytes, jint flags); + +JNIEXPORT jint JNICALL +JVM_Timeout(int fd, long timeout); + +JNIEXPORT jint JNICALL +JVM_Listen(jint fd, jint count); + +JNIEXPORT jint JNICALL +JVM_Connect(jint fd, struct sockaddr *him, jint len); + +JNIEXPORT jint JNICALL +JVM_Bind(jint fd, struct sockaddr *him, jint len); + +JNIEXPORT jint JNICALL +JVM_Accept(jint fd, struct sockaddr *him, jint *len); + +JNIEXPORT jint JNICALL +JVM_RecvFrom(jint fd, char *buf, int nBytes, + int flags, struct sockaddr *from, int *fromlen); + +JNIEXPORT jint JNICALL +JVM_SendTo(jint fd, char *buf, int len, + int flags, struct sockaddr *to, int tolen); + +JNIEXPORT jint JNICALL +JVM_SocketAvailable(jint fd, jint *result); + + +JNIEXPORT jint JNICALL +JVM_GetSockName(jint fd, struct sockaddr *him, int *len); + +JNIEXPORT jint JNICALL +JVM_GetSockOpt(jint fd, int level, int optname, char *optval, int *optlen); + +JNIEXPORT jint JNICALL +JVM_SetSockOpt(jint fd, int level, int optname, const char *optval, int optlen); + +/* + * These routines are only reentrant on Windows + */ + +#ifdef _WINDOWS + +JNIEXPORT struct protoent * JNICALL +JVM_GetProtoByName(char* name); + +JNIEXPORT struct hostent* JNICALL +JVM_GetHostByAddr(const char* name, int len, int type); + +JNIEXPORT struct hostent* JNICALL +JVM_GetHostByName(char* name); + +#endif /* _WINDOWS */ + +JNIEXPORT int JNICALL +JVM_GetHostName(char* name, int namelen); + +/* + * The standard printing functions supported by the Java VM. (Should they + * be renamed to JVM_* in the future? + */ + +/* + * BE CAREFUL! The following functions do not implement the + * full feature set of standard C printf formats. + */ +int +jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args); + +int +jio_snprintf(char *str, size_t count, const char *fmt, ...); + +int +jio_fprintf(FILE *, const char *fmt, ...); + +int +jio_vfprintf(FILE *, const char *fmt, va_list args); + + +JNIEXPORT void * JNICALL +JVM_RawMonitorCreate(void); + +JNIEXPORT void JNICALL +JVM_RawMonitorDestroy(void *mon); + +JNIEXPORT jint JNICALL +JVM_RawMonitorEnter(void *mon); + +JNIEXPORT void JNICALL +JVM_RawMonitorExit(void *mon); + + +#ifdef SUPPORT_OLD_REFLECTION + +/* + * Support for old native code-based (pre-JDK 1.4) reflection implementation. + * Disabled by default in the product build. + * + * See reflection.hpp for information on SUPPORT_OLD_REFLECTION + */ + +/* + * reflecting fields and methods. + * which: 0 --- MEMBER_PUBLIC + * 1 --- MEMBER_DECLARED + * NOTE: absent in product build by default + */ + +JNIEXPORT jobjectArray JNICALL +JVM_GetClassFields(JNIEnv *env, jclass cls, jint which); + +JNIEXPORT jobjectArray JNICALL +JVM_GetClassMethods(JNIEnv *env, jclass cls, jint which); + +JNIEXPORT jobjectArray JNICALL +JVM_GetClassConstructors(JNIEnv *env, jclass cls, jint which); + +JNIEXPORT jobject JNICALL +JVM_GetClassField(JNIEnv *env, jclass cls, jstring name, jint which); + +JNIEXPORT jobject JNICALL +JVM_GetClassMethod(JNIEnv *env, jclass cls, jstring name, jobjectArray types, + jint which); +JNIEXPORT jobject JNICALL +JVM_GetClassConstructor(JNIEnv *env, jclass cls, jobjectArray types, + jint which); + +/* + * Implements Class.newInstance + */ +JNIEXPORT jobject JNICALL +JVM_NewInstance(JNIEnv *env, jclass cls); + +/* + * java.lang.reflect.Field + */ +JNIEXPORT jobject JNICALL +JVM_GetField(JNIEnv *env, jobject field, jobject obj); + +JNIEXPORT jvalue JNICALL +JVM_GetPrimitiveField(JNIEnv *env, jobject field, jobject obj, + unsigned char wCode); + +JNIEXPORT void JNICALL +JVM_SetField(JNIEnv *env, jobject field, jobject obj, jobject val); + +JNIEXPORT void JNICALL +JVM_SetPrimitiveField(JNIEnv *env, jobject field, jobject obj, jvalue v, + unsigned char vCode); + +/* + * java.lang.reflect.Method + */ +JNIEXPORT jobject JNICALL +JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0); + +/* + * java.lang.reflect.Constructor + */ +JNIEXPORT jobject JNICALL +JVM_NewInstanceFromConstructor(JNIEnv *env, jobject c, jobjectArray args0); + +#endif /* SUPPORT_OLD_REFLECTION */ + +/* + * java.lang.management support + */ +JNIEXPORT void* JNICALL +JVM_GetManagement(jint version); + +/* + * com.sun.tools.attach.VirtualMachine support + * + * Initialize the agent properties with the properties maintained in the VM. + */ +JNIEXPORT jobject JNICALL +JVM_InitAgentProperties(JNIEnv *env, jobject agent_props); + +/* Generics reflection support. + * + * Returns information about the given class's EnclosingMethod + * attribute, if present, or null if the class had no enclosing + * method. + * + * If non-null, the returned array contains three elements. Element 0 + * is the java.lang.Class of which the enclosing method is a member, + * and elements 1 and 2 are the java.lang.Strings for the enclosing + * method's name and descriptor, respectively. + */ +JNIEXPORT jobjectArray JNICALL +JVM_GetEnclosingMethodInfo(JNIEnv* env, jclass ofClass); + +/* + * Java thread state support + */ +enum { + JAVA_THREAD_STATE_NEW = 0, + JAVA_THREAD_STATE_RUNNABLE = 1, + JAVA_THREAD_STATE_BLOCKED = 2, + JAVA_THREAD_STATE_WAITING = 3, + JAVA_THREAD_STATE_TIMED_WAITING = 4, + JAVA_THREAD_STATE_TERMINATED = 5, + JAVA_THREAD_STATE_COUNT = 6 +}; + +/* + * Returns an array of the threadStatus values representing the + * given Java thread state. Returns NULL if the VM version is + * incompatible with the JDK or doesn't support the given + * Java thread state. + */ +JNIEXPORT jintArray JNICALL +JVM_GetThreadStateValues(JNIEnv* env, jint javaThreadState); + +/* + * Returns an array of the substate names representing the + * given Java thread state. Returns NULL if the VM version is + * incompatible with the JDK or the VM doesn't support + * the given Java thread state. + * values must be the jintArray returned from JVM_GetThreadStateValues + * and javaThreadState. + */ +JNIEXPORT jobjectArray JNICALL +JVM_GetThreadStateNames(JNIEnv* env, jint javaThreadState, jintArray values); + +/* ========================================================================= + * The following defines a private JVM interface that the JDK can query + * for the JVM version and capabilities. sun.misc.Version defines + * the methods for getting the VM version and its capabilities. + * + * When a new bit is added, the following should be updated to provide + * access to the new capability: + * HS: JVM_GetVersionInfo and Abstract_VM_Version class + * SDK: Version class + * + * Similary, a private JDK interface JDK_GetVersionInfo0 is defined for + * JVM to query for the JDK version and capabilities. + * + * When a new bit is added, the following should be updated to provide + * access to the new capability: + * HS: JDK_Version class + * SDK: JDK_GetVersionInfo0 + * + * ========================================================================== + */ +typedef struct { + /* HotSpot Express VM version string: + * .-bxx[-][-] + */ + unsigned int jvm_version; /* Consists of major.minor.0.build */ + unsigned int update_version : 8; /* 0 in HotSpot Express VM */ + unsigned int special_update_version : 8; /* 0 in HotSpot Express VM */ + unsigned int reserved1 : 16; + unsigned int reserved2; + + /* The following bits represents JVM supports that JDK has dependency on. + * JDK can use these bits to determine which JVM version + * and support it has to maintain runtime compatibility. + * + * When a new bit is added in a minor or update release, make sure + * the new bit is also added in the main/baseline. + */ + unsigned int is_attachable : 1; + unsigned int is_kernel_jvm : 1; + unsigned int : 30; + unsigned int : 32; + unsigned int : 32; +} jvm_version_info; + +#define JVM_VERSION_MAJOR(version) ((version & 0xFF000000) >> 24) +#define JVM_VERSION_MINOR(version) ((version & 0x00FF0000) >> 16) +// Micro version is 0 in HotSpot Express VM (set in jvm.cpp). +#define JVM_VERSION_MICRO(version) ((version & 0x0000FF00) >> 8) +/* Build number is available in all HotSpot Express VM builds. + * It is defined in make/hotspot_version file. + */ +#define JVM_VERSION_BUILD(version) ((version & 0x000000FF)) + +JNIEXPORT void JNICALL +JVM_GetVersionInfo(JNIEnv* env, jvm_version_info* info, size_t info_size); + +typedef struct { + // Naming convention of RE build version string: n.n.n[_uu[c]][-]-bxx + unsigned int jdk_version; /* Consists of major, minor, micro (n.n.n) */ + /* and build number (xx) */ + unsigned int update_version : 8; /* Update release version (uu) */ + unsigned int special_update_version : 8; /* Special update release version (c)*/ + unsigned int reserved1 : 16; + unsigned int reserved2; + + /* The following bits represents new JDK supports that VM has dependency on. + * VM implementation can use these bits to determine which JDK version + * and support it has to maintain runtime compatibility. + * + * When a new bit is added in a minor or update release, make sure + * the new bit is also added in the main/baseline. + */ + unsigned int thread_park_blocker : 1; + unsigned int : 31; + unsigned int : 32; + unsigned int : 32; +} jdk_version_info; + +#define JDK_VERSION_MAJOR(version) ((version & 0xFF000000) >> 24) +#define JDK_VERSION_MINOR(version) ((version & 0x00FF0000) >> 16) +#define JDK_VERSION_MICRO(version) ((version & 0x0000FF00) >> 8) + +/* Build number is available only for RE build (i.e. JDK_BUILD_NUMBER is set to bNN) + * It will be zero for internal builds. + */ +#define JDK_VERSION_BUILD(version) ((version & 0x000000FF)) + +/* + * This is the function JDK_GetVersionInfo0 defined in libjava.so + * that is dynamically looked up by JVM. + */ +typedef void (*jdk_version_info_fn_t)(jdk_version_info* info, size_t info_size); + +/* + * This structure is used by the launcher to get the default thread + * stack size from the VM using JNI_GetDefaultJavaVMInitArgs() with a + * version of 1.1. As it is not supported otherwise, it has been removed + * from jni.h + */ +typedef struct JDK1_1InitArgs { + jint version; + + char **properties; + jint checkSource; + jint nativeStackSize; + jint javaStackSize; + jint minHeapSize; + jint maxHeapSize; + jint verifyMode; + char *classpath; + + jint (JNICALL *vfprintf)(FILE *fp, const char *format, va_list args); + void (JNICALL *exit)(jint code); + void (JNICALL *abort)(void); + + jint enableClassGC; + jint enableVerboseGC; + jint disableAsyncGC; + jint verbose; + jboolean debugging; + jint debugPort; +} JDK1_1InitArgs; + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVASOFT_JVM_H_ */ diff --git a/hotspot/src/share/vm/prims/jvm_misc.hpp b/hotspot/src/share/vm/prims/jvm_misc.hpp new file mode 100644 index 00000000000..7af47518c0f --- /dev/null +++ b/hotspot/src/share/vm/prims/jvm_misc.hpp @@ -0,0 +1,88 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Useful entry points shared by JNI and JVM interface. +// We do not allow real JNI or JVM entry point to call each other. + +jclass find_class_from_class_loader(JNIEnv* env, symbolHandle name, jboolean init, Handle loader, Handle protection_domain, jboolean throwError, TRAPS); + + +/* + * Support for Serialization and RMI. Currently used by HotSpot only. + */ + +extern "C" { + +void JNICALL +JVM_SetPrimitiveFieldValues(JNIEnv *env, jclass cb, jobject obj, + jlongArray fieldIDs, jcharArray typecodes, jbyteArray data); + +void JNICALL +JVM_GetPrimitiveFieldValues(JNIEnv *env, jclass cb, jobject obj, + jlongArray fieldIDs, jcharArray typecodes, jbyteArray data); + +} + +/* + * Support for -Xcheck:jni + */ + +extern struct JNINativeInterface_* jni_functions_nocheck(); +extern struct JNINativeInterface_* jni_functions_check(); + +/* + * Support for swappable jni function table. + */ +extern struct JNINativeInterface_* jni_functions(); +extern void copy_jni_function_table(const struct JNINativeInterface_* new_function_table); + +// Support for fast JNI accessors +extern "C" { + typedef jboolean (JNICALL *GetBooleanField_t) + (JNIEnv *env, jobject obj, jfieldID fieldID); + typedef jbyte (JNICALL *GetByteField_t) + (JNIEnv *env, jobject obj, jfieldID fieldID); + typedef jchar (JNICALL *GetCharField_t) + (JNIEnv *env, jobject obj, jfieldID fieldID); + typedef jshort (JNICALL *GetShortField_t) + (JNIEnv *env, jobject obj, jfieldID fieldID); + typedef jint (JNICALL *GetIntField_t) + (JNIEnv *env, jobject obj, jfieldID fieldID); + typedef jlong (JNICALL *GetLongField_t) + (JNIEnv *env, jobject obj, jfieldID fieldID); + typedef jfloat (JNICALL *GetFloatField_t) + (JNIEnv *env, jobject obj, jfieldID fieldID); + typedef jdouble (JNICALL *GetDoubleField_t) + (JNIEnv *env, jobject obj, jfieldID fieldID); +} + +void quicken_jni_functions(); +address jni_GetBooleanField_addr(); +address jni_GetByteField_addr(); +address jni_GetCharField_addr(); +address jni_GetShortField_addr(); +address jni_GetIntField_addr(); +address jni_GetLongField_addr(); +address jni_GetFloatField_addr(); +address jni_GetDoubleField_addr(); diff --git a/hotspot/src/share/vm/prims/jvmti.xml b/hotspot/src/share/vm/prims/jvmti.xml new file mode 100644 index 00000000000..7f6751e15f3 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmti.xml @@ -0,0 +1,14267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]> + + + + <tm>JVM</tm> Tool Interface + + + + The JVM Tool Interface () + is a programming interface used by development and monitoring tools. + It provides both a way to inspect the state and + to control the execution of applications running in the + Java virtual machine (VM). +

+ is intended to provide a VM interface for the full breadth of tools + that need access to VM state, including but not limited to: profiling, + debugging, monitoring, thread analysis, and coverage analysis tools. +

+ may not be available in all implementations of the Java virtual + machine. +

+ is a two-way interface. + A client of , hereafter called an agent, + can be notified of + interesting occurrences through events. + + can query and control the application through many + functions, + either in response to events or + independent of them. +

+ Agents run in the same process with and communicate directly with + the virtual machine executing + the application being examined. This communication is + through a native interface (). The native in-process interface allows + maximal control with minimal intrusion on the part of a tool. + Typically, agents are relatively compact. They can be controlled + by a separate process which implements the bulk of a tool's + function without interfering with the target application's normal execution. + + + + Tools can be written directly to or indirectly + through higher level interfaces. + The Java Platform Debugger Architecture includes , but also + contains higher-level, out-of-process debugger interfaces. The higher-level + interfaces are more appropriate than for many tools. + For more information on the Java Platform Debugger Architecture, + see the + Java + Platform Debugger Architecture website. + + + + Agents can be written in any native language that supports C + language calling conventions and C or C++ + definitions. +

+ The function, event, data type, and constant definitions needed for + using are defined in the include file jvmti.h. + To use these definitions add the J2SE include directory + to your include path and add + +#include <jvmti.h> + + to your source code. + + + + An agent is deployed in a platform specific manner but is typically the + platform equivalent of a dynamic library. On the Windows operating + system, for example, an agent library is a "Dynamic Linked Library" (DLL). + On the Solaris Operating Environment, an agent library is a shared + object (.so file). +

+ An agent may be started at VM startup by specifying the agent library + name using a command line option. + Some implementations may support a mechanism to + start agents in the live phase. + The details of how this is initiated are implementation specific. + + + + The term "command-line option" is used below to + mean options supplied in the JavaVMInitArgs argument + to the JNI_CreateJavaVM function of the JNI + Invocation API. +

+ One of the two following + command-line options is used on VM startup to + properly load and run agents. + These arguments identify the library containing + the agent as well as an options + string to be passed in at startup. +

+
-agentlib:<agent-lib-name>=<options>
+
+ The name following -agentlib: is the name of the + library to load. Lookup of the library, both its full name and location, + proceeds in a platform-specific manner. + Typically, the <agent-lib-name> is expanded to an + operating system specific file name. + The <options> will be passed to the agent on start-up. + For example, if the option + -agentlib:foo=opt1,opt2 is specified, the VM will attempt to + load the shared library foo.dll from the system PATH + under Windows or libfoo.so from the + LD_LIBRARY_PATH under the Solaris operating environment. +
+
-agentpath:<path-to-agent>=<options>
+
+ The path following -agentpath: is the absolute path from which + to load the library. + No library name expansion will occur. + The <options> will be passed to the agent on start-up. + For example, if the option + -agentpath:c:\myLibs\foo.dll=opt1,opt2 is specified, the VM will attempt to + load the shared library c:\myLibs\foo.dll. +
+
+ The start-up routine Agent_OnLoad + in the library will be invoked. +

+ Libraries loaded with -agentlib: or -agentpath: + will be searched for JNI native method implementations to facilitate the + use of Java programming language code in tools, as is needed for + bytecode instrumentation. +

+ The agent libraries will be searched after all other libraries have been + searched (agents wishing to override or intercept the native method + implementations of non-agent methods can use the + NativeMethodBind event). +

+ These switches do the above and nothing more - they do not change the + state of the VM or . No command line options are needed + to enable + or aspects of , this is handled programmatically + by the use of + capabilities. + + + + The VM starts each agent by invoking a start-up function. + If the agent is started in the OnLoad + phase the function + Agent_OnLoad + will be invoked. + If the agent is started in the live + phase the function + Agent_OnAttach + will be invoked. + Exactly one call to a start-up function is made per agent. + + + + If an agent is started during the OnLoad phase then its + agent library must export a start-up function with the following prototype: + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved) + The VM will start the agent by calling this function. + It will be called early enough in VM initialization that: +

    +
  • system properties + may be set before they have been used in the start-up of the VM
  • +
  • the full set of + capabilities + is still available (note that capabilities that configure the VM + may only be available at this time--see the + Capability function section)
  • +
  • no bytecodes have executed
  • +
  • no classes have been loaded
  • +
  • no objects have been created
  • +
+

+ The VM will call the Agent_OnLoad function with + <options> as the second argument - + that is, using the command-line option examples, + "opt1,opt2" will be passed to the char *options + argument of Agent_OnLoad. + The options argument is encoded as a + modified UTF-8 string. + If =<options> is not specified, + a zero length string is passed to options. + The lifespan of the options string is the Agent_OnLoad + call. If needed beyond this time the string or parts of the string must + be copied. + The period between when Agent_OnLoad is called and when it + returns is called the OnLoad phase. + Since the VM is not initialized during the OnLoad + phase, + the set of allowed operations + inside Agent_OnLoad is restricted (see the function descriptions for the + functionality available at this time). + The agent can safely process the options and set + event callbacks with . Once + the VM initialization event is received + (that is, the VMInit + callback is invoked), the agent + can complete its initialization. + + Early startup is required so that agents can set the desired capabilities, + many of which must be set before the VM is initialized. + In JVMDI, the -Xdebug command-line option provided + very coarse-grain control of capabilities. + JVMPI implementations use various tricks to provide a single "JVMPI on" switch. + No reasonable command-line + option could provide the fine-grain of control required to balance needed capabilities vs + performance impact. + Early startup is also needed so that agents can control the execution + environment - modifying the file system and system properties to install + their functionality. + +

+ The return value from Agent_OnLoad is used to indicate an error. + Any value other than zero indicates an error and causes termination of the VM. + + + + A VM may support a mechanism that allows agents to be started in the VM during the live + phase. The details of how this is supported, + are implementation specific. For example, a tool may use some platform specific mechanism, + or implementation specific API, to attach to the running VM, and request it start a given + agent. +

+ If an agent is started during the live phase then its agent library + must export a start-up function + with the following prototype: + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM* vm, char *options, void *reserved) +

+ The VM will start the agent by calling this function. + It will be called in the context of a thread + that is attached to the VM. The first argument <vm> is the Java VM. + The <options> argument is the startup options provided to the agent. + <options> is encoded as a modified UTF-8 + string. + If startup options were not provided, a zero length string is passed to + options. The lifespan of the options string is the + Agent_OnAttach call. If needed beyond this time the string or parts of + the string must be copied. +

+ Note that some capabilities + may not be available in the live phase. +

+ The Agent_OnAttach function initializes the agent and returns a value + to the VM to indicate if an error occurred. Any value other than zero indicates an error. + An error does not cause the VM to terminate. Instead the VM ignores the error, or takes + some implementation specific action -- for example it might print an error to standard error, + or record the error in a system log. + + + + The library may optionally export a + shutdown function with the following prototype: + +JNIEXPORT void JNICALL +Agent_OnUnload(JavaVM *vm) + This function will be called by the VM when the library is about to be unloaded. + The library will be unloaded and this function will be called if some platform specific + mechanism causes the unload (an unload mechanism is not specified in this document) + or the library is (in effect) unloaded by the termination of the VM whether through + normal termination or VM failure, including start-up failure. + Uncontrolled shutdown is, of couse, an exception to this rule. + Note the distinction between this function and the + VM Death event: for the VM Death event + to be sent, the VM must have run at least to the point of initialization and a valid + environment must exist which has set a callback for VMDeath + and enabled the event + None of these are required for Agent_OnUnload and this function + is also called if the library is unloaded for other reasons. + In the case that a VM Death event is sent, it will be sent before this + function is called (assuming this function is called due to VM termination). + This function can be used to clean-up resources allocated by the agent. + + + + Since the command-line cannot always be accessed or modified, for example in embedded VMs + or simply VMs launched deep within scripts, a JAVA_TOOL_OPTIONS variable is + provided so that agents may be launched in these cases. +

+ Platforms which support environment variables or other named strings, may support the + JAVA_TOOL_OPTIONS variable. This variable will be broken into options at white-space + boundaries. White-space characters include space, tab, carriage-return, new-line, + vertical-tab, and form-feed. Sequences of white-space characters are considered + equivalent to a single white-space character. No white-space is included in the options + unless quoted. Quoting is as follows: +

    +
  • All characters enclosed between a pair of single quote marks (''), except a single + quote, are quoted.
  • +
  • Double quote characters have no special meaning inside a pair of single quote marks.
  • +
  • All characters enclosed between a pair of double quote marks (""), except a double + quote, are quoted.
  • +
  • Single quote characters have no special meaning inside a pair of double quote marks.
  • +
  • A quoted part can start or end anywhere in the variable.
  • +
  • White-space characters have no special meaning when quoted -- they are included in + the option like any other character and do not mark white-space boundaries.
  • +
  • The pair of quote marks is not included in the option.
  • +
+ JNI_CreateJavaVM (in the JNI Invocation API) will prepend these options to the options supplied + in its JavaVMInitArgs argument. Platforms may disable this feature in cases where security is + a concern; for example, the Reference Implementation disables this feature on Unix systems when + the effective user or group ID differs from the real ID. + This feature is intended to support the initialization of tools -- specifically including the + launching of native or Java programming language agents. Multiple tools may wish to use this + feature, so the variable should not be overwritten, instead, options should be appended to + the variable. Note that since the variable is processed at the time of the JNI Invocation + API create VM call, options processed by a launcher (e.g., VM selection options) will not be handled. +
+ + + The specification supports the use of multiple simultaneous + agents. + Each agent has its own environment. + That is, the state is + separate for each agent - changes to one environment do not affect the + others. The state of a + environment includes: +
    +
  • the event callbacks
  • +
  • the set of events which are enabled
  • +
  • the capabilities
  • +
  • the memory allocation/deallocation hooks
  • +
+ Although their state + is separate, agents inspect and modify the shared state + of the VM, they also share the native environment in which they execute. + As such, an agent can perturb the results of other agents or cause them + to fail. It is the responsibility of the agent writer to specify the level + of compatibility with other agents. implementations are not capable + of preventing destructive interactions between agents. Techniques to reduce + the likelihood of these occurrences are beyond the scope of this document. +

+ An agent creates a environment + by passing a version + as the interface ID to the JNI Invocation API function + GetEnv. + See Accessing Functions + for more details on the creation and use of + environments. + Typically, environments are created by calling GetEnv from + Agent_OnLoad. + + + + This interface does not include some events that one might expect in an interface with + profiling support. Some examples include object allocation events and full speed + method enter and exit events. The interface instead provides support for + bytecode instrumentation, the ability to alter the Java virtual machine + bytecode instructions which comprise the target program. Typically, these alterations + are to add "events" to the code of a method - for example, to add, at the beginning of a method, + a call to MyProfiler.methodEntered(). + Since the changes are purely additive, they do not modify application + state or behavior. + Because the inserted agent code is standard bytecodes, the VM can run at full speed, + optimizing not only the target program but also the instrumentation. If the + instrumentation does not involve switching from bytecode execution, no expensive + state transitions are needed. The result is high performance events. + This approach also provides complete control to the agent: instrumentation can be + restricted to "interesting" portions of the code (e.g., the end user's code) and + can be conditional. Instrumentation can run entirely in Java programming language + code or can call into the native agent. Instrumentation can simply maintain + counters or can statistically sample events. +

+ Instrumentation can be inserted in one of three ways: +

    +
  • + Static Instrumentation: The class file is instrumented before it + is loaded into the VM - for example, by creating a duplicate directory of + *.class files which have been modified to add the instrumentation. + This method is extremely awkward and, in general, an agent cannot know + the origin of the class files which will be loaded. +
  • +
  • + Load-Time Instrumentation: When a class file is loaded by the VM, the raw + bytes of the class file are sent for instrumentation to the agent. + The + event, triggered by the class load, + provides this functionality. This mechanism provides efficient + and complete access to one-time instrumentation. +
  • +
  • + Dynamic Instrumentation: A class which is already loaded (and possibly + even running) is modified. This optional feature is provided by the + event, triggered by calling the + function. + Classes can be modified multiple times and can be returned to their + original state. + The mechanism allows instrumentation which changes during the + course of execution. +
  • +
+

+ The class modification functionality provided in this interface + is intended to provide a mechanism for instrumentation + (the event + and the function) + and, during development, for fix-and-continue debugging + (the function). +

+ Care must be taken to avoid perturbing dependencies, especially when + instrumenting core classes. For example, an approach to getting notification + of every object allocation is to instrument the constructor on + Object. Assuming that the constructor is initially + empty, the constructor could be changed to: + + public Object() { + MyProfiler.allocationTracker(this); + } + + However, if this change was made using the + + event then this might impact a typical VM as follows: + the first created object will call the constructor causing a class load of + MyProfiler; which will then cause + object creation, and since MyProfiler isn't loaded yet, + infinite recursion; resulting in a stack overflow. A refinement of this + would be to delay invoking the tracking method until a safe time. For + example, trackAllocations could be set in the + handler for the VMInit event. + + static boolean trackAllocations = false; + + public Object() { + if (trackAllocations) { + MyProfiler.allocationTracker(this); + } + } + +

+ The allows native methods + to be instrumented by the use of wrapper methods. + + + + uses modified UTF-8 to encode character strings. + This is the same encoding used by JNI. + Modified UTF-8 differs + from standard UTF-8 in the representation of supplementary characters + and of the null character. See the + + Modified UTF-8 Strings + section of the JNI specification for details. + + + + Since this interface provides access to the state of applications running in the + Java virtual machine; + terminology refers to the Java platform and not the native + platform (unless stated otherwise). For example: +

    +
  • "thread" means Java programming language thread.
  • +
  • "stack frame" means Java virtual machine stack frame.
  • +
  • "class" means Java programming language class.
  • +
  • "heap" means Java virtual machine heap.
  • +
  • "monitor" means Java programming language object monitor.
  • +
+

+ Sun, Sun Microsystems, the Sun logo, Java, and JVM + are trademarks or registered trademarks of Sun + Microsystems, Inc. in the U.S. and other countries. + + + + + + Native code accesses features + by calling functions. + Access to functions is by use of an interface pointer + in the same manner as + Java + Native Interface (JNI) functions are accessed. + The interface pointer is called the + environment pointer. +

+ An environment pointer is a pointer to an environment and has + the type jvmtiEnv*. + An environment has information about its connection. + The first value in the environment is a pointer to the function table. + The function table is an array of pointers to functions. + Every function pointer is at a predefined offset inside the + array. +

+ When used from the C language: + double indirection is used to access the functions; + the environment pointer provides context and is the first + parameter of each function call; for example: + +jvmtiEnv *jvmti; +... +jvmtiError err = (*jvmti)->GetLoadedClasses(jvmti, &class_count, &classes); + +

+ When used from the C++ language: + functions are accessed as member functions of jvmtiEnv; + the environment pointer is not passed to the function call; for example: + +jvmtiEnv *jvmti; +... +jvmtiError err = jvmti->GetLoadedClasses(&class_count, &classes); + + Unless otherwise stated, all examples and declarations in this + specification use the C language. +

+ A environment can be obtained through the JNI Invocation API + GetEnv function: + +jvmtiEnv *jvmti; +... +(*jvm)->GetEnv(jvm, &jvmti, JVMTI_VERSION_1_0); + + Each call to GetEnv + creates a new connection and thus + a new environment. + The version argument of GetEnv must be + a version. + The returned environment may have a different version than the + requested version but the returned environment must be compatible. + GetEnv will return JNI_EVERSION if a + compatible version is not available, if is not supported or + is not supported in the current VM configuration. + Other interfaces may be added for creating environments + in specific contexts. + Each environment has its own state (for example, + desired events, + event handling functions, and + capabilities). + An environment is released with + . + Thus, unlike JNI which has one environment per thread, environments work + across threads and are created dynamically. + + + + functions always return an + error code via the + function return value. + Some functions can return additional + values through pointers provided by the calling function. + In some cases, functions allocate memory that your program must + explicitly deallocate. This is indicated in the individual + function descriptions. Empty lists, arrays, sequences, etc are + returned as NULL. +

+ In the event that the function encounters + an error (any return value other than JVMTI_ERROR_NONE) the values + of memory referenced by argument pointers is undefined, but no memory + will have been allocated and no global references will have been allocated. + If the error occurs because of invalid input, no action will have occurred. + + + + functions identify objects with JNI references + ( and ) + and their derivatives + ( and ). + References passed to + functions can be either global or local, but they must be + strong references. All references returned by functions are + local references--these local references are created + during the call. + Local references are a resource that must be managed (see the + JNI Documentation). + When threads return from native code all local references + are freed. Note that some threads, including typical + agent threads, will never return from native code. + A thread is ensured the ability to create sixteen local + references without the need for any explicit management. + For threads executing a limited number of calls before + returning from native code + (for example, threads processing events), + it may be determined that no explicit management + is needed. + However, long running agent threads will need explicit + local reference management--usually with the JNI functions + PushLocalFrame and PopLocalFrame. + Conversely, to preserve references beyond the + return from native code, they must be converted to global references. + These rules do not apply to and + as they are not s. + + + + Unless the function explicitly states that the agent must bring + a thread or the VM to a particular state (for example, suspended), + the implementation is responsible for bringing the VM to a + safe and consistent state for performing the function. + + + + functions never throw exceptions; error conditions are + communicated via the + function return value. + Any existing exception state is preserved across a call to a + function. + See the + Java Exceptions + section of the JNI specification for information on handling exceptions. + + + + + These functions provide for the allocation and deallocation of + memory used by functionality and can be used to provide + working memory for agents. + Memory managed by is not compatible with other memory + allocation libraries and mechanisms. + + + + Allocate + + Allocate an area of memory through the allocator. + The allocated + memory should be freed with . + + jvmdi + + + + + + + The number of bytes to allocate. + + jlong is used for compatibility with JVMDI. + + + + + + + On return, a pointer to the beginning of the allocated memory. + If size is zero, NULL is returned. + + + + + + Memory request cannot be honored. + + + is less than zero. + + + + + + Deallocate + + Deallocate mem using the allocator. + This function should + be used to deallocate any memory allocated and returned + by a function + (including memory allocated with ). + All allocated memory must be deallocated + or the memory cannot be reclaimed. + + jvmdi + + + + + + + the call is ignored + + + A pointer to the beginning of the allocated memory. + Please ignore "On return, the elements are set." + keep it from generating "On return, the elements are set" + + + + + + + + + + + + + + Get Thread State + + Get the state of a thread. The state of the thread is represented by the + answers to the hierarchical set of questions below: +

    +
  • Alive? +
      +
    • Not alive. +
        +
      • Why not alive? +
          +
        • New.
        • +
        • Terminated (JVMTI_THREAD_STATE_TERMINATED)
        • +
        +
      • +
      +
    • +
    • Alive (JVMTI_THREAD_STATE_ALIVE) +
        +
      • Suspended? +
          +
        • Suspended (JVMTI_THREAD_STATE_SUSPENDED)
        • +
        • Not suspended
        • +
        +
      • +
      • Interrupted? +
          +
        • Interrupted (JVMTI_THREAD_STATE_INTERRUPTED)
        • +
        • Not interrupted.
        • +
        +
      • +
      • In native? +
          +
        • In native code (JVMTI_THREAD_STATE_IN_NATIVE)
        • +
        • In Java programming language code
        • +
        +
      • +
      • What alive state? +
          +
        • Runnable (JVMTI_THREAD_STATE_RUNNABLE)
        • +
        • Blocked (JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER)
        • +
        • Waiting (JVMTI_THREAD_STATE_WAITING) +
            +
          • Timed wait? +
              +
            • Indefinite (JVMTI_THREAD_STATE_WAITING_INDEFINITELY
            • +
            • Timed (JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT)
            • +
            +
          • +
          • Why waiting? +
              +
            • Object.wait (JVMTI_THREAD_STATE_IN_OBJECT_WAIT)
            • +
            • LockSupport.park (JVMTI_THREAD_STATE_PARKED)
            • +
            • Sleeping (JVMTI_THREAD_STATE_SLEEPING)
            • +
            +
          • +
          +
        • +
        +
      • +
      +
    • +
    +
  • +
+

+ The answers are represented by the following bit vector. + + + Thread is alive. Zero if thread is new (not started) or terminated. + + + Thread has completed execution. + + + Thread is runnable. + + + Thread is waiting to enter a synchronization block/method or, + after an Object.wait(), waiting to re-enter a + synchronization block/method. + + + Thread is waiting. + + + Thread is waiting without a timeout. + For example, Object.wait(). + + + Thread is waiting with a maximum time to wait specified. + For example, Object.wait(long). + + + Thread is sleeping -- Thread.sleep(long). + + + Thread is waiting on an object monitor -- Object.wait. + + + Thread is parked, for example: LockSupport.park, + LockSupport.parkUtil and LockSupport.parkNanos. + + + Thread suspended. + java.lang.Thread.suspend() + or a suspend function + (such as ) + has been called on the thread. If this bit + is set, the other bits refer to the thread state before suspension. + + + Thread has been interrupted. + + + Thread is in native code--that is, a native method is running + which has not called back into the VM or Java programming + language code. +

+ This flag is not set when running VM compiled Java programming + language code nor is it set when running VM code or + VM support code. Native VM interface functions, such as JNI and + functions, may be implemented as VM code. + + + Defined by VM vendor. + + + Defined by VM vendor. + + + Defined by VM vendor. + + + The following definitions are used to convert thread state + to java.lang.Thread.State style states. + + + Mask the state with this before comparison + + + java.lang.Thread.State.NEW + + + java.lang.Thread.State.TERMINATED + + + java.lang.Thread.State.RUNNABLE + + + java.lang.Thread.State.BLOCKED + + + java.lang.Thread.State.WAITING + + + java.lang.Thread.State.TIMED_WAITING + + + Rules +

+ There can be no more than one answer to a question, although there can be no + answer (because the answer is unknown, does not apply, or none of the answers is + correct). An answer is set only when the enclosing answers match. + That is, no more than one of +

    +
  • JVMTI_THREAD_STATE_RUNNABLE
  • +
  • JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER
  • +
  • JVMTI_THREAD_STATE_WAITING
  • +
+ can be set (a J2SE compliant implementation will always set + one of these if JVMTI_THREAD_STATE_ALIVE is set). + And if any of these are set, the enclosing answer + JVMTI_THREAD_STATE_ALIVE is set. + No more than one of +
    +
  • JVMTI_THREAD_STATE_WAITING_INDEFINITELY
  • +
  • JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT
  • +
+ can be set (a J2SE compliant implementation will always set + one of these if JVMTI_THREAD_STATE_WAITING is set). + And if either is set, the enclosing answers + JVMTI_THREAD_STATE_ALIVE and + JVMTI_THREAD_STATE_WAITING are set. + No more than one of +
    +
  • JVMTI_THREAD_STATE_IN_OBJECT_WAIT
  • +
  • JVMTI_THREAD_STATE_PARKED
  • +
  • JVMTI_THREAD_STATE_SLEEPING
  • +
+ can be set. And if any of these is set, the enclosing answers + JVMTI_THREAD_STATE_ALIVE and + JVMTI_THREAD_STATE_WAITING are set. + Also, if JVMTI_THREAD_STATE_SLEEPING is set, + then JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT is set. + If a state A is implemented using the mechanism of + state B then it is state A which + is returned by this function. + For example, if Thread.sleep(long) + is implemented using Object.wait(long) + then it is still JVMTI_THREAD_STATE_SLEEPING + which is returned. + More than one of +
    +
  • JVMTI_THREAD_STATE_SUSPENDED
  • +
  • JVMTI_THREAD_STATE_INTERRUPTED
  • +
  • JVMTI_THREAD_STATE_IN_NATIVE
  • +
+ can be set, but if any is set, + JVMTI_THREAD_STATE_ALIVE is set. +

+ And finally, + JVMTI_THREAD_STATE_TERMINATED cannot be set unless + JVMTI_THREAD_STATE_ALIVE is not set. +

+ The thread state representation is designed for extension in future versions + of the specification; thread state values should be used accordingly, that is + they should not be used as ordinals. + Most queries can be made by testing a single bit, if use in a switch statement is desired, + the state bits should be masked with the interesting bits. + All bits not defined above are reserved for future use. + A VM, compliant to the current specification, must set reserved bits to zero. + An agent should ignore reserved bits -- + they should not be assumed to be zero and thus should not be included in comparisons. +

+ Examples +

+ Note that the values below exclude reserved and vendor bits. +

+ The state of a thread blocked at a synchronized-statement would be: + + JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER + + The state of a thread which hasn't started yet would be: + + 0 + + The state of a thread at a Object.wait(3000) would be: + + JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + + JVMTI_THREAD_STATE_MONITOR_WAITING + + The state of a thread suspended while runnable would be: + + JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_RUNNABLE + JVMTI_THREAD_STATE_SUSPENDED + +

+ Testing the State +

+ In most cases, the thread state can be determined by testing the one bit corresponding + to that question. For example, the code to test if a thread is sleeping: + + jint state; + jvmtiError err; + + err = (*jvmti)->GetThreadState(jvmti, thread, &state); + if (err == JVMTI_ERROR_NONE) { + if (state & JVMTI_THREAD_STATE_SLEEPING) { ... + +

+ For waiting (that is, in Object.wait, parked, or sleeping) it would be: + + if (state & JVMTI_THREAD_STATE_WAITING) { ... + + For some states, more than one bit will need to be tested as is the case + when testing if a thread has not yet been started: + + if ((state & (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_TERMINATED)) == 0) { ... + + To distinguish timed from untimed Object.wait: + + if (state & JVMTI_THREAD_STATE_IN_OBJECT_WAIT) { + if (state & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) { + printf("in Object.wait(long timeout)\n"); + } else { + printf("in Object.wait()\n"); + } + } + +

+ Relationship to java.lang.Thread.State +

+ The thread state represented by java.lang.Thread.State + returned from java.lang.Thread.getState() is a subset of the + information returned from this function. + The corresponding java.lang.Thread.State can be determined + by using the provided conversion masks. + For example, this returns the name of the java.lang.Thread.State thread state: + + err = (*jvmti)->GetThreadState(jvmti, thread, &state); + abortOnError(err); + switch (state & JVMTI_JAVA_LANG_THREAD_STATE_MASK) { + case JVMTI_JAVA_LANG_THREAD_STATE_NEW: + return "NEW"; + case JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED: + return "TERMINATED"; + case JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE: + return "RUNNABLE"; + case JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED: + return "BLOCKED"; + case JVMTI_JAVA_LANG_THREAD_STATE_WAITING: + return "WAITING"; + case JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING: + return "TIMED_WAITING"; + } + + + new + + + + + + + The thread to query. + + + + + + On return, points to state flags, + as defined by the Thread State Flags. + + + + + + + + + Get Current Thread + + Get the current thread. + The current thread is the Java programming language thread which has called the function. +

+ Note that most functions that take a thread + as an argument will accept NULL to mean + the current thread. + + new + + + + + + + On return, points to the current thread. + + + + + + + + + Get All Threads + + Get all live threads. + The threads are Java programming language threads; + that is, threads that are attached to the VM. + A thread is live if java.lang.Thread.isAlive() + would return true, that is, the thread has + been started and has not yet died. + The universe of threads is determined by the context of the + environment, which typically is all threads attached to the VM. + Note that this includes agent threads + (see ). + + jvmdi + + + + + + + On return, points to the number of running threads. + + + + + + On return, points to an array of references, one + for each running thread. + + + + + + + + + Suspend Thread + + Suspend the specified thread. If the calling thread is specified, + this function will not return until some other thread calls + . + If the thread is currently suspended, this function + does nothing and returns an error. + + jvmdi + + + + + + + + The thread to suspend. + + + + + + Thread already suspended. + + + + + + + Suspend All Threads + + + There has been no explicit call for this function, and it will + thus be removed if there is no interest. + + Suspend all live threads except: +

    +
  • already suspended threads
  • +
  • those listed in
  • +
  • certain system (non application) threads, as determined + by the VM implementation
  • +
+ The threads are Java programming language threads; + native threads which are not attached to the VM are not + Java programming language threads. + A thread is live if java.lang.Thread.isAlive() + would return true, that is, the thread has + been started and has not yet died. + The universe of threads is determined + by the context of the + environment, which, typically, is all threads attached to the VM, + except critical VM internal threads and agent threads + (see ). +

+ If the calling thread is specified, + all other threads are suspended first then the caller thread is suspended - + this function will not return until some other thread calls + . +

+ The list of actually + suspended threads is returned in + . + Suspension is as defined in . + + can be used to resume the suspended threads. + + new + + + + + + + + The number of threads in the list of threads not to be suspended. + + + + + + not an error if except_count == 0 + + + The list of threads not to be suspended. + + + + + + On return, points to the number of threads suspended by this call. + + + + + + On return, points to an array of references, one + for each thread suspended. + + + + + + A thread in was invalid. + + + Both was NULL + and was non-zero. + + + + + + + Suspend Thread List + + Suspend the + threads specified in the + array. + Threads may be resumed with + or + . + If the calling thread is specified in the + array, this function will + not return until some other thread resumes it. + Errors encountered in the suspension of a thread + are returned in the + array, not in the return value of this function. + Threads that are currently suspended do not change state. + + jvmdi + + + + + + + + The number of threads to suspend. + + + + + + The list of threads to suspend. + + + + jvmtiError + + An agent supplied array of + elements. + On return, filled with the error code for + the suspend of the corresponding thread. + The error code will be + + if the thread was suspended by this call. + Possible error codes are those specified + for . + + + + + + + + + Resume Thread + + Resume a suspended thread. + Any threads currently suspended through + a suspend function (eg. + ) + or java.lang.Thread.suspend() + will resume execution; + all other threads are unaffected. + + jvmdi + + + + + + + + The thread to resume. + + + + + + Thread was not suspended. + + + The state of the thread has been modified, and is now inconsistent. + + + + + + Resume Thread List + + Resume the + threads specified in the + array. + Any thread suspended through + a suspend function (eg. + ) + or java.lang.Thread.suspend() + will resume execution. + + jvmdi + + + + + + + + The number of threads to resume. + + + + + + The threads to resume. + + + + jvmtiError + + An agent supplied array of + elements. + On return, filled with the error code for + the resume of the corresponding thread. + The error code will be + + if the thread was suspended by this call. + Possible error codes are those specified + for . + + + + + + + + + Stop Thread + + Send the specified asynchronous exception to the specified thread + (similar to java.lang.Thread.stop). + Normally, this function is used to kill the specified thread with an + instance of the exception ThreadDeath. + + jvmdi + + + + + + + + The thread to stop. + + + + + + The asynchronous exception object. + + + + + + + + + Interrupt Thread + + Interrupt the specified thread + (similar to java.lang.Thread.interrupt). + + jvmdi + + + + + + + + The thread to interrupt. + + + + + + + + + Get Thread Info + + + + + The thread name, encoded as a + modified UTF-8 string. + + + + + + The thread priority. See the thread priority constants: + . + + + + + + Is this a daemon thread? + + + + + + The thread group to which this thread belongs. + NULL if the thread has died. + + + + + + The context class loader associated with this thread. + + + + + Get thread information. The fields of the structure + are filled in with details of the specified thread. + + jvmdi + + + + + + + The thread to query. + + + + jvmtiThreadInfo + + On return, filled with information describing the specified thread. +

+ For JDK 1.1 implementations that don't + recognize context class loaders, + the context_class_loader field will be NULL. + + + + + + + + + Get Owned Monitor Info + + Get information about the monitors owned by the + specified thread. + + jvmdiClone + + + + + + + + The thread to query. + + + + + + The number of monitors returned. + + + + + + The array of owned monitors. + + + + + + + + + Get Owned Monitor Stack Depth Info + + + + + The owned monitor. + + + + + + The stack depth. Corresponds to the stack depth used in the + Stack Frame functions. + That is, zero is the current frame, one is the frame which + called the current frame. And it is negative one if the + implementation cannot determine the stack depth (e.g., for + monitors acquired by JNI MonitorEnter). + + + + + Get information about the monitors owned by the + specified thread and the depth of the stack frame which locked them. + + new + + + + + + + + The thread to query. + + + + + + The number of monitors returned. + + + + + jvmtiMonitorStackDepthInfo + + + The array of owned monitor depth information. + + + + + + + + + Get Current Contended Monitor + + Get the object, if any, whose monitor the specified thread is waiting to + enter or waiting to regain through java.lang.Object.wait. + + jvmdi + + + + + + + + The thread to query. + + + + + + On return, filled with the current contended monitor, or + NULL if there is none. + + + + + + + + + + Agent Start Function + + Agent supplied callback function. + This function is the entry point for an agent thread + started with + . + + + + + jvmtiEnv + + + The environment. + + + + + JNIEnv + + + The JNI environment. + + + + + + + + The arg parameter passed to + . + + + + + + + Run Agent Thread + + Starts the execution of an agent thread. with the specified native function. + The parameter is forwarded on to the + start function + (specified with ) as its single argument. + This function allows the creation of agent threads + for handling communication with another process or for handling events + without the need to load a special subclass of java.lang.Thread or + implementer of java.lang.Runnable. + Instead, the created thread can run entirely in native code. + However, the created thread does require a newly created instance + of java.lang.Thread (referenced by the argument thread) to + which it will be associated. + The thread object can be created with JNI calls. +

+ The following common thread priorities are provided for your convenience: + + + Minimum possible thread priority + + + Normal thread priority + + + Maximum possible thread priority + + +

+ The new thread is started as a daemon thread with the specified + . + If enabled, a event will be sent. +

+ Since the thread has been started, the thread will be live when this function + returns, unless the thread has died immediately. +

+ The thread group of the thread is ignored -- specifically, the thread is not + added to the thread group and the thread is not seen on queries of the thread + group at either the Java programming language or levels. +

+ The thread is not visible to Java programming language queries but is + included in queries (for example, + and + ). +

+ Upon execution of proc, the new thread will be attached to the + VM--see the JNI documentation on + Attaching to the VM. + + jvmdiClone + + + + + + + The thread to run. + + + + + jvmtiStartFunction + + + The start function. + + + + + + NULL is passed to the start function + + + The argument to the start function. + + + + + + The priority of the started thread. Any thread + priority allowed by java.lang.Thread.setPriority can be used including + those in . + + + + + + is less than + + or greater than + + + + + + + Set Thread Local Storage + + The VM stores a pointer value associated with each environment-thread + pair. This pointer value is called thread-local storage. + This value is NULL unless set with this function. + Agents can allocate memory in which they store thread specific + information. By setting thread-local storage it can then be + accessed with + . +

+ This function is called by the agent to set the value of the + thread-local storage. supplies to the agent a pointer-size + thread-local storage that can be used to record per-thread + information. + + jvmpi + + + + + + + Store to this thread. + + + + + + value is set to NULL + + + The value to be entered into the thread-local storage. + + + + + + + + + Get Thread Local Storage + + Called by the agent to get the value of the thread-local + storage. + + jvmpi + + + + + + + Retrieve from this thread. + + + + + + Pointer through which the value of the thread local + storage is returned. + If thread-local storage has not been set with + the returned + pointer is NULL. + + + + + + + + + + + + + + + Get Top Thread Groups + + Return all top-level (parentless) thread groups in the VM. + + jvmdi + + + + + + + On return, points to the number of top-level thread groups. + + + + + + On return, refers to a pointer to the top-level thread group array. + + + + + + + + + Get Thread Group Info + + + + + The parent thread group. + + + + + + The thread group's name, encoded as a + modified UTF-8 string. + + + + + + The maximum priority for this thread group. + + + + + + Is this a daemon thread group? + + + + + Get information about the thread group. The fields of the + structure + are filled in with details of the specified thread group. + + jvmdi + + + + + + + The thread group to query. + + + + jvmtiThreadGroupInfo + + On return, filled with information describing the specified + thread group. + + + + + + + + + Get Thread Group Children + + Get the live threads and active subgroups in this thread group. + + jvmdi + + + + + + + The group to query. + + + + + + On return, points to the number of live threads in this thread group. + + + + + + On return, points to an array of the live threads in this thread group. + + + + + + On return, points to the number of active child thread groups + + + + + + On return, points to an array of the active child thread groups. + + + + + + + + + + + These functions provide information about the stack of a thread. + Stack frames are referenced by depth. + The frame at depth zero is the current frame. +

+ Stack frames are as described in the + . + That is, they correspond to method + invocations (including native methods) but do not correspond to platform native or + VM internal frames. +

+ A implementation may use method invocations to launch a thread and + the corresponding frames may be included in the stack as presented by these functions -- + that is, there may be frames shown + deeper than main() and run(). + However this presentation must be consistent across all functionality which + uses stack frames or stack depth. + + + + + Information about a stack frame is returned in this structure. + + + + + The method executing in this frame. + + + + + + The index of the instruction executing in this frame. + -1 if the frame is executing a native method. + + + + + + + Information about a set of stack frames is returned in this structure. + + + + + On return, the thread traced. + + + + + + On return, the thread state. See . + + + + + jvmtiFrameInfo + + + On return, this agent allocated buffer is filled + with stack frame information. + + + + + + On return, the number of records filled into + frame_buffer. + This will be + min(max_frame_count, stackDepth). + + + + + + Get Stack Trace + + Get information about the stack of a thread. + If is less than the depth of the stack, + the topmost frames are returned, + otherwise the entire stack is returned. + The topmost frames, those most recently invoked, are at the beginning of the returned buffer. +

+ The following example causes up to five of the topmost frames + to be returned and (if there are any frames) the currently + executing method name to be printed. + +jvmtiFrameInfo frames[5]; +jint count; +jvmtiError err; + +err = (*jvmti)->GetStackTrace(jvmti, aThread, 0, 5, + &frames, &count); +if (err == JVMTI_ERROR_NONE && count >= 1) { + char *methodName; + err = (*jvmti)->GetMethodName(jvmti, frames[0].method, + &methodName, NULL); + if (err == JVMTI_ERROR_NONE) { + printf("Executing method: %s", methodName); + } +} + + + check example code. + +

+ The need not be suspended + to call this function. +

+ The + function can be used to map locations to line numbers. Note that + this mapping can be done lazily. + + jvmpi + + + + + + + Fetch the stack trace of this thread. + + + + + + Begin retrieving frames at this depth. + If non-negative, count from the current frame, + the first frame retrieved is at depth start_depth. + For example, if zero, start from the current frame; if one, start from the + caller of the current frame; if two, start from the caller of the + caller of the current frame; and so on. + If negative, count from below the oldest frame, + the first frame retrieved is at depth stackDepth + start_depth, + where stackDepth is the count of frames on the stack. + For example, if negative one, only the oldest frame is retrieved; + if negative two, start from the frame called by the oldest frame. + + + + + + The maximum number of records to retrieve. + + + + + jvmtiFrameInfo + + + On return, this agent allocated buffer is filled + with stack frame information. + + + + + + On return, points to the number of records filled in. + For non-negative start_depth, this will be + min(max_frame_count, stackDepth - start_depth). + For negative start_depth, this will be + min(max_frame_count, -start_depth). + + + + + + is positive and greater than or equal to stackDepth. + Or is negative and less than -stackDepth. + + + + + + + Get All Stack Traces + + Get information about the stacks of all live threads + (including agent threads). + If is less than the depth of a stack, + the topmost frames are returned for that thread, + otherwise the entire stack is returned. + The topmost frames, those most recently invoked, are at the beginning of the returned buffer. +

+ All stacks are collected simultaneously, that is, no changes will occur to the + thread state or stacks between the sampling of one thread and the next. + The threads need not be suspended. + + +jvmtiStackInfo *stack_info; +jint thread_count; +int ti; +jvmtiError err; + +err = (*jvmti)->GetAllStackTraces(jvmti, MAX_FRAMES, &stack_info, &thread_count); +if (err != JVMTI_ERROR_NONE) { + ... +} +for (ti = 0; ti < thread_count; ++ti) { + jvmtiStackInfo *infop = &stack_info[ti]; + jthread thread = infop->thread; + jint state = infop->state; + jvmtiFrameInfo *frames = infop->frame_buffer; + int fi; + + myThreadAndStatePrinter(thread, state); + for (fi = 0; fi < infop->frame_count; fi++) { + myFramePrinter(frames[fi].method, frames[fi].location); + } +} +/* this one Deallocate call frees all data allocated by GetAllStackTraces */ +err = (*jvmti)->Deallocate(jvmti, stack_info); + + + check example code. + + + + new + + + + + + + The maximum number of records to retrieve per thread. + + + + + jvmtiStackInfo + + + On return, this buffer is filled + with stack information for each thread. + The number of records is determined + by . +

+ Note that this buffer is allocated to include the + buffers pointed to by . + These buffers must not be separately deallocated. + + + + + + The number of threads traced. + + + + + + + + + Get Thread List Stack Traces + + Get information about the stacks of the supplied threads. + If is less than the depth of a stack, + the topmost frames are returned for that thread, + otherwise the entire stack is returned. + The topmost frames, those most recently invoked, are at the beginning of the returned buffer. +

+ All stacks are collected simultaneously, that is, no changes will occur to the + thread state or stacks between the sampling one thread and the next. + The threads need not be suspended. +

+ If a thread has not yet started or terminates before the stack information is collected, + a zero length stack ( will be zero) + will be returned and the thread can be checked. +

+ See the example for the similar function + . + + new + + + + + + + The number of threads to trace. + + + + + + The list of threads to trace. + + + + + + The maximum number of records to retrieve per thread. + + + + + jvmtiStackInfo + + + On return, this buffer is filled + with stack information for each thread. + The number of records is determined + by . +

+ Note that this buffer is allocated to include the + buffers pointed to by . + These buffers must not be separately deallocated. + + + + + + An element in is not a thread object. + + + + + + + Get Stack Trace--Asynchronous + + Get information about the entire stack of a thread (or a sub-section of it). + This is the asynchronous version of + and is reentrant and safe to call + from asynchronous signal handlers. + The stack trace is returned only for the calling thread. +

+ The + function can be used to map locations to line numbers. Note that + this mapping can be done lazily. + + jvmpi + + + + If false, + + must be false. + + + + + + + Return the stack showing the + model of the stack; + otherwise, show the internal representation of the stack with + inlined and optimized methods missing. If the virtual machine + is using the Java Virtual Machine Specification stack model + internally, this flag is ignored. + + + + + + The maximum number of records to retrieve. + Retrieve this many unless the stack depth is less than max_count. + + + + + jvmtiFrameInfo + this information is not returned + + + The agent passes in a buffer + large enough to hold max_count records of + . This buffer must be + pre-allocated by the agent. + + + + + + On return, points to the number of records filled in.. + + + + + + The thread being used to call this function is not attached + to the virtual machine. Calls must be made from attached threads. + + + + + + + Get Frame Count + + Get the number of frames currently in the specified thread's call stack. +

+ If this function is called for a thread actively executing bytecodes (for example, + not the current thread and not suspended), the information returned is transient. + + jvmdi + + + + + + + The thread to query. + + + + + + On return, points to the number of frames in the call stack. + + + + + + + + + Pop Frame + + Pop the current frame of thread's stack. + Popping a frame takes you to the previous frame. + When the thread is resumed, the execution + state of the thread is reset to the state + immediately before the called method was invoked. + That is (using the terminology): +

    +
  • the current frame is discarded as the previous frame becomes the current one
  • +
  • the operand stack is restored--the argument values are added back + and if the invoke was not invokestatic, + objectref is added back as well
  • +
  • the Java virtual machine PC is restored to the opcode + of the invoke instruction
  • +
+ Note however, that any changes to the arguments, which + occurred in the called method, remain; + when execution continues, the first instruction to + execute will be the invoke. +

+ Between calling PopFrame and resuming the + thread the state of the stack is undefined. + To pop frames beyond the first, + these three steps must be repeated: +

    +
  • suspend the thread via an event (step, breakpoint, ...)
  • +
  • call PopFrame
  • +
  • resume the thread
  • +
+

+ A lock acquired by calling the called method + (if it is a synchronized method) + and locks acquired by entering synchronized + blocks within the called method are released. + Note: this does not apply to native locks or + java.util.concurrent.locks locks. +

+ Finally blocks are not executed. +

+ Changes to global state are not addressed and thus remain changed. +

+ The specified thread must be suspended (which implies it cannot be the current thread). +

+ Both the called method and calling method must be non-native Java programming + language methods. +

+ No events are generated by this function. + + jvmdi + + + + + + + + The thread whose current frame is to be popped. + + + + + + Called or calling method is a native method. + The implementation is unable to pop this frame. + + + Thread was not suspended. + + + There are less than two stack frames on the call stack. + + + + + + Get Frame Location + +

+ For a Java programming language frame, return the location of the instruction + currently executing. + + jvmdiClone + + + + + + + The thread of the frame to query. + + + + + + The depth of the frame to query. + + + + + + On return, points to the method for the current location. + + + + + + On return, points to the index of the currently + executing instruction. + Is set to -1 if the frame is executing + a native method. + + + + + + + + + Notify Frame Pop + + When the frame that is currently at + is popped from the stack, generate a + event. See the + event for details. + Only frames corresponding to non-native Java programming language + methods can receive notification. +

+ The specified thread must either be the current thread + or the thread must be suspended. + + jvmdi + + + + + + + + The thread of the frame for which the frame pop event will be generated. + + + + + + The depth of the frame for which the frame pop event will be generated. + + + + + + The frame at depth is executing a + native method. + + + Thread was not suspended and was not the current thread. + + + + + + + + + These functions allow an agent to force a method + to return at any point during its execution. + The method which will return early is referred to as the called method. + The called method is the current method + (as defined by the + ) + for the specified thread at + the time the function is called. +

+ The specified thread must be suspended or must be the current thread. + The return occurs when execution of Java programming + language code is resumed on this thread. + Between calling one of these functions and resumption + of thread execution, the state of the stack is undefined. +

+ No further instructions are executed in the called method. + Specifically, finally blocks are not executed. + Note: this can cause inconsistent states in the application. +

+ A lock acquired by calling the called method + (if it is a synchronized method) + and locks acquired by entering synchronized + blocks within the called method are released. + Note: this does not apply to native locks or + java.util.concurrent.locks locks. +

+ Events, such as , + are generated as they would be in a normal return. +

+ The called method must be a non-native Java programming + language method. + Forcing return on a thread with only one frame on the + stack causes the thread to exit when resumed. + + + + Force Early Return - Object + + This function can be used to return from a method whose + result type is Object + or a subclass of Object. + + new + + + + + + + + The thread whose current frame is to return early. + + + + + + The return value for the called frame. + An object or NULL. + + + + + + Attempted to return early from a frame + corresponding to a native method. + Or the implementation is unable to provide + this functionality on this frame. + + + The result type of the called method is not + Object or a subclass of Object. + + + The supplied is not compatible with the + result type of the called method. + + + Thread was not the current thread and was not suspended. + + + There are no more frames on the call stack. + + + + + + Force Early Return - Int + + This function can be used to return from a method whose + result type is int, short, + char, byte, or + boolean. + + new + + + + + + + + The thread whose current frame is to return early. + + + + + + The return value for the called frame. + + + + + + Attempted to return early from a frame + corresponding to a native method. + Or the implementation is unable to provide + this functionality on this frame. + + + The result type of the called method is not + int, short, + char, byte, or + boolean. + + + Thread was not the current thread and was not suspended. + + + There are no frames on the call stack. + + + + + + Force Early Return - Long + + This function can be used to return from a method whose + result type is long. + + new + + + + + + + + The thread whose current frame is to return early. + + + + + + The return value for the called frame. + + + + + + Attempted to return early from a frame + corresponding to a native method. + Or the implementation is unable to provide + this functionality on this frame. + + + The result type of the called method is not long. + + + Thread was not the current thread and was not suspended. + + + There are no frames on the call stack. + + + + + + Force Early Return - Float + + This function can be used to return from a method whose + result type is float. + + new + + + + + + + + The thread whose current frame is to return early. + + + + + + The return value for the called frame. + + + + + + Attempted to return early from a frame + corresponding to a native method. + Or the implementation is unable to provide + this functionality on this frame. + + + The result type of the called method is not float. + + + Thread was not the current thread and was not suspended. + + + There are no frames on the call stack. + + + + + + Force Early Return - Double + + This function can be used to return from a method whose + result type is double. + + new + + + + + + + + The thread whose current frame is to return early. + + + + + + The return value for the called frame. + + + + + + Attempted to return early from a frame corresponding to a native method. + Or the implementation is unable to provide this functionality on this frame. + + + The result type of the called method is not double. + + + Thread was not the current thread and was not suspended. + + + There are no frames on the call stack. + + + + + + Force Early Return - Void + + This function can be used to return from a method with no result type. + That is, the called method must be declared void. + + new + + + + + + + + The thread whose current frame is to return early. + + + + + + Attempted to return early from a frame + corresponding to a native method. + Or the implementation is unable to provide + this functionality on this frame. + + + The called method has a result type. + + + Thread was not the current thread and was not suspended. + + + There are no frames on the call stack. + + + + + + + + + These functions are used to analyze the heap. + Functionality includes the ability to view the objects in the + heap and to tag these objects. + + + + A tag is a value associated with an object. + Tags are explicitly set by the agent using the + function or by + callback functions such as . +

+ Tags are local to the environment; that is, the tags of one + environment are not visible in another. +

+ Tags are jlong values which can be used + simply to mark an object or to store a pointer to more detailed + information. Objects which have not been tagged have a + tag of zero. + Setting a tag to zero makes the object untagged. + + + + Heap functions which iterate through the heap and recursively + follow object references use agent supplied callback functions + to deliver the information. +

+ These heap callback functions must adhere to the following restrictions -- + These callbacks must not use JNI functions. + These callbacks must not use functions except + callback safe functions which + specifically allow such use (see the raw monitor, memory management, + and environment local storage functions). +

+ An implementation may invoke a callback on an internal thread or + the thread which called the iteration function. + Heap callbacks are single threaded -- no more than one callback will + be invoked at a time. +

+ The Heap Filter Flags can be used to prevent reporting + based on the tag status of an object or its class. + If no flags are set (the jint is zero), objects + will not be filtered out. + + + + Filter out tagged objects. Objects which are tagged are not included. + + + Filter out untagged objects. Objects which are not tagged are not included. + + + Filter out objects with tagged classes. Objects whose class is tagged are not included. + + + Filter out objects with untagged classes. Objects whose class is not tagged are not included. + + + +

+ The Heap Visit Control Flags are returned by the heap callbacks + and can be used to abort the iteration. For the + Heap + Reference Callback, it can also be used + to prune the graph of traversed references + (JVMTI_VISIT_OBJECTS is not set). + + + + If we are visiting an object and if this callback + was initiated by , + traverse the references of this object. + Otherwise ignored. + + + Abort the iteration. Ignore all other bits. + + + +

+ The Heap Reference Enumeration is provided by the + Heap + Reference Callback and + Primitive Field + Callback to + describe the kind of reference + being reported. + + + + Reference from an object to its class. + + + Reference from an object to the value of one of its instance fields. + + + Reference from an array to one of its elements. + + + Reference from a class to its class loader. + + + Reference from a class to its signers array. + + + Reference from a class to its protection domain. + + + Reference from a class to one of its interfaces. + Note: interfaces are defined via a constant pool reference, + so the referenced interfaces may also be reported with a + JVMTI_HEAP_REFERENCE_CONSTANT_POOL reference kind. + + + Reference from a class to the value of one of its static fields. + + + Reference from a class to a resolved entry in the constant pool. + + + Reference from a class to its superclass. + A callback is bot sent if the superclass is java.lang.Object. + Note: loaded classes define superclasses via a constant pool + reference, so the referenced superclass may also be reported with + a JVMTI_HEAP_REFERENCE_CONSTANT_POOL reference kind. + + + Heap root reference: JNI global reference. + + + Heap root reference: System class. + + + Heap root reference: monitor. + + + Heap root reference: local variable on the stack. + + + Heap root reference: JNI local reference. + + + Heap root reference: Thread. + + + Heap root reference: other heap root reference. + + + +

+ Definitions for the single character type descriptors of + primitive types. + + + + 'Z' - Java programming language boolean - JNI jboolean + + + 'B' - Java programming language byte - JNI jbyte + + + 'C' - Java programming language char - JNI jchar + + + 'S' - Java programming language short - JNI jshort + + + 'I' - Java programming language int - JNI jint + + + 'J' - Java programming language long - JNI jlong + + + 'F' - Java programming language float - JNI jfloat + + + 'D' - Java programming language double - JNI jdouble + + + + + + + Reference information returned for + and + references. + + + + + For , the + referrer object is not a class or an inteface. + In this case, index is the index of the field + in the class of the referrer object. + This class is referred to below as C. +

+ For , + the referrer object is a class (referred to below as C) + or an interface (referred to below as I). + In this case, index is the index of the field in + that class or interface. +

+ If the referrer object is not an interface, then the field + indices are determined as follows: +

    +
  • make a list of all the fields in C and its + superclasses, starting with all the fields in + java.lang.Object and ending with all the + fields in C.
  • +
  • Within this list, put + the fields for a given class in the order returned by + .
  • +
  • Assign the fields in this list indices + n, n+1, ..., in order, where n + is the count of the fields in all the interfaces + implemented by C. + Note that C implements all interfaces + directly implemented by its superclasses; as well + as all superinterfaces of these interfaces.
  • +
+ If the referrer object is an interface, then the field + indices are determined as follows: +
    +
  • make a list of the fields directly declared in + I.
  • +
  • Within this list, put + the fields in the order returned by + .
  • +
  • Assign the fields in this list indices + n, n+1, ..., in order, where n + is the count of the fields in all the superinterfaces + of I.
  • +
+ All fields are included in this computation, regardless of + field modifier (static, public, private, etc). +

+ For example, given the following classes and interfaces: + +interface I0 { + int p = 0; +} + +interface I1 extends I0 { + int x = 1; +} + +interface I2 extends I0 { + int y = 2; +} + +class C1 implements I1 { + public static int a = 3; + private int b = 4; +} + +class C2 extends C1 implements I2 { + static int q = 5; + final int r = 6; +} + + Assume that called on + C1 returns the fields of C1 in the + order: a, b; and that the fields of C2 are + returned in the order: q, r. + An instance of class C1 will have the + following field indices: +

+ + + + + + + + + + +
+ a + + 2 + + The count of the fields in the interfaces + implemented by C1 is two (n=2): + p of I0 + and x of I1. +
+ b + + 3 + + the subsequent index. +
+ The class C1 will have the same field indices. +

+ An instance of class C2 will have the + following field indices: +

+ + + + + + + + + + + + + + + + + + + + +
+ a + + 3 + + The count of the fields in the interfaces + implemented by C2 is three (n=3): + p of I0, + x of I1 and y of I2 + (an interface of C2). Note that the field p + of I0 is only included once. +
+ b + + 4 + + the subsequent index to "a". +
+ q + + 5 + + the subsequent index to "b". +
+ r + + 6 + + the subsequent index to "q". +
+ The class C2 will have the same field indices. + Note that a field may have a different index depending on the + object that is viewing it -- for example field "a" above. + Note also: not all field indices may be visible from the + callbacks, but all indices are shown for illustrative purposes. +

+ The interface I1 will have the + following field indices: +

+ + + + + +
+ x + + 1 + + The count of the fields in the superinterfaces + of I1 is one (n=1): + p of I0. +
+ + + + + + + Reference information returned for + references. + + + + + The array index. + + + + + + + Reference information returned for + references. + + + + + The index into the constant pool of the class. See the + + description. + + + + + + + Reference information returned for + references. + + + + + The tag of the thread corresponding to this stack, zero if not tagged. + + + + + + The unique thread ID of the thread corresponding to this stack. + + + + + + The depth of the frame. + + + + + + The method executing in this frame. + + + + + + The currently executing location in this frame. + + + + + + The slot number of the local variable. + + + + + + + Reference information returned for + references. + + + + + The tag of the thread corresponding to this stack, zero if not tagged. + + + + + + The unique thread ID of the thread corresponding to this stack. + + + + + + The depth of the frame. + + + + + + The method executing in this frame. + + + + + + + Reference information returned for other references. + + + + + reserved for future use. + + + + + + reserved for future use. + + + + + + reserved for future use. + + + + + + reserved for future use. + + + + + + reserved for future use. + + + + + + reserved for future use. + + + + + + reserved for future use. + + + + + + reserved for future use. + + + + + + + The information returned about referrers. + Represented as a union of the various kinds of reference information. + + + jvmtiHeapReferenceInfoField + + The referrer information for + + and references. + + + + jvmtiHeapReferenceInfoArray + + The referrer information for + For references. + + + + jvmtiHeapReferenceInfoConstantPool + + The referrer information for + For references. + + + + jvmtiHeapReferenceInfoStackLocal + + The referrer information for + For references. + + + + jvmtiHeapReferenceInfoJniLocal + + The referrer information for + For references. + + + + jvmtiHeapReferenceInfoReserved + + reserved for future use. + + + + + + + + jvmtiHeapIterationCallback + + + The callback to be called to describe an + object in the heap. Used by the + function, ignored by the + function. + + + + + jvmtiHeapReferenceCallback + + + The callback to be called to describe an + object reference. Used by the + function, ignored by the + function. + + + + + jvmtiPrimitiveFieldCallback + + + The callback to be called to describe a + primitive field. + + + + + jvmtiArrayPrimitiveValueCallback + + + The callback to be called to describe an + array of primitive values. + + + + + jvmtiStringPrimitiveValueCallback + + + The callback to be called to describe a String value. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + jvmtiReservedCallback + + + Reserved for future use.. + + + + + + + + The heap dumping functionality (below) uses a callback + for each object. While it would seem that a buffered approach + would provide better throughput, tests do + not show this to be the case--possibly due to locality of + memory reference or array access overhead. + + + + Still under investigation as to if java.lang.ref references + are reported as a different type of reference. + + + + Should or can an indication of the cost or relative cost of + these operations be included? + + + + + + + Heap Iteration Callback + + Agent supplied callback function. + Describes (but does not pass in) an object in the heap. +

+ This function should return a bit vector of the desired + visit control flags. + This will determine if the entire iteration should be aborted + (the JVMTI_VISIT_OBJECTS flag is ignored). +

+ See the heap callback + function restrictions. + + + + + + The tag of the class of object (zero if the class is not tagged). + If the object represents a runtime class, + the class_tag is the tag + associated with java.lang.Class + (zero if java.lang.Class is not tagged). + + + + + + Size of the object (in bytes). See . + + + + + + The object tag value, or zero if the object is not tagged. + To set the tag value to be associated with the object + the agent sets the jlong pointed to by the parameter. + + + + + + If this object is an array, the length of the array. Otherwise negative one (-1). + + + + + + The user supplied data that was passed into the iteration function. + + + + + + + + Heap Reference Callback + + Agent supplied callback function. + Describes a reference from an object or the VM (the referrer) to another object + (the referree) or a heap root to a referree. +

+ This function should return a bit vector of the desired + visit control flags. + This will determine if the objects referenced by the referree + should be visited or if the entire iteration should be aborted. +

+ See the heap callback + function restrictions. + + + + jvmtiHeapReferenceKind + + The kind of reference. + + + + + jvmtiHeapReferenceInfo + + + Details about the reference. + Set when the is + , + , + , + , + , + or . + Otherwise NULL. + + + + + + The tag of the class of referree object (zero if the class is not tagged). + If the referree object represents a runtime class, + the class_tag is the tag + associated with java.lang.Class + (zero if java.lang.Class is not tagged). + + + + + + The tag of the class of the referrer object (zero if the class is not tagged + or the referree is a heap root). If the referrer object represents a runtime + class, the referrer_class_tag is the tag associated with + the java.lang.Class + (zero if java.lang.Class is not tagged). + + + + + + Size of the referree object (in bytes). + See . + + + + + + Points to the referree object tag value, or zero if the object is not + tagged. + To set the tag value to be associated with the object + the agent sets the jlong pointed to by the parameter. + + + + + + Points to the tag of the referrer object, or + points to the zero if the referrer + object is not tagged. + NULL if the referrer in not an object (that is, + this callback is reporting a heap root). + To set the tag value to be associated with the referrer object + the agent sets the jlong pointed to by the parameter. + If this callback is reporting a reference from an object to itself, + referrer_tag_ptr == tag_ptr. + + + + + + If this object is an array, the length of the array. Otherwise negative one (-1). + + + + + + The user supplied data that was passed into the iteration function. + + + + + + + + Primitive Field Callback + + Agent supplied callback function which + describes a primitive field of an object (the object). + A primitive field is a field whose type is a primitive type. + This callback will describe a static field if the object is a class, + and otherwise will describe an instance field. +

+ This function should return a bit vector of the desired + visit control flags. + This will determine if the entire iteration should be aborted + (the JVMTI_VISIT_OBJECTS flag is ignored). +

+ See the heap callback + function restrictions. + + + + jvmtiHeapReferenceKind + + The kind of field -- instance or static ( or + ). + + + + + jvmtiHeapReferenceInfo + + + Which field (the field index). + + + + + + The tag of the class of the object (zero if the class is not tagged). + If the object represents a runtime class, the + object_class_tag is the tag + associated with java.lang.Class + (zero if java.lang.Class is not tagged). + + + + + + Points to the tag of the object, or zero if the object is not + tagged. + To set the tag value to be associated with the object + the agent sets the jlong pointed to by the parameter. + + + + + + The value of the field. + + + + jvmtiPrimitiveType + + The type of the field. + + + + + + The user supplied data that was passed into the iteration function. + + + + + + + + Array Primitive Value Callback + + Agent supplied callback function. + Describes the values in an array of a primitive type. +

+ This function should return a bit vector of the desired + visit control flags. + This will determine if the entire iteration should be aborted + (the JVMTI_VISIT_OBJECTS flag is ignored). +

+ See the heap callback + function restrictions. + + + + + + The tag of the class of the array object (zero if the class is not tagged). + + + + + + Size of the array (in bytes). + See . + + + + + + Points to the tag of the array object, or zero if the object is not + tagged. + To set the tag value to be associated with the object + the agent sets the jlong pointed to by the parameter. + + + + + + The length of the primitive array. + + + + jvmtiPrimitiveType + + The type of the elements of the array. + + + + + + The elements of the array in a packed array of element_count + items of element_type size each. + + + + + + The user supplied data that was passed into the iteration function. + + + + + + + + String Primitive Value Callback + + Agent supplied callback function. + Describes the value of a java.lang.String. +

+ This function should return a bit vector of the desired + visit control flags. + This will determine if the entire iteration should be aborted + (the JVMTI_VISIT_OBJECTS flag is ignored). +

+ See the heap callback + function restrictions. + + + + + + The tag of the class of the String class (zero if the class is not tagged). + Is this needed? + + + + + + Size of the string (in bytes). + See . + + + + + + Points to the tag of the String object, or zero if the object is not + tagged. + To set the tag value to be associated with the object + the agent sets the jlong pointed to by the parameter. + + + + + + The value of the String, encoded as a Unicode string. + + + + + + The length of the string. + The length is equal to the number of 16-bit Unicode + characters in the string. + + + + + + The user supplied data that was passed into the iteration function. + + + + + + + + + reserved for future use Callback + + Placeholder -- reserved for future use. + + + + + + + Follow References + + This function initiates a traversal over the objects that are + directly and indirectly reachable from the specified object or, + if initial_object is not specified, all objects + reachable from the heap roots. + The heap root are the set of system classes, + JNI globals, references from thread stacks, and other objects used as roots + for the purposes of garbage collection. +

+ This function operates by traversing the reference graph. + Let A, B, ... represent objects. + When a reference from A to B is traversed, + when a reference from a heap root to B is traversed, + or when B is specified as the , + then B is said to be visited. + A reference from A to B is not traversed until A + is visited. + References are reported in the same order that the references are traversed. + Object references are reported by invoking the agent supplied + callback function . + In a reference from A to B, A is known + as the referrer and B as the referree. + The callback is invoked exactly once for each reference from a referrer; + this is true even if there are reference cycles or multiple paths to + the referrer. + There may be more than one reference between a referrer and a referree, + each reference is reported. + These references may be distinguished by examining the + reference_kind + and + reference_info + parameters of the callback. +

+ This function reports a Java programming language view of object references, + not a virtual machine implementation view. The following object references + are reported when they are non-null: +

    +
  • Instance objects report references to each non-primitive instance fields + (including inherited fields).
  • +
  • Instance objects report a reference to the object type (class).
  • +
  • Classes report a reference to the superclass and directly + implemented/extended interfaces.
  • +
  • Classes report a reference to the class loader, protection domain, + signers, and resolved entries in the constant pool.
  • +
  • Classes report a reference to each directly declared non-primitive + static field.
  • +
  • Arrays report a reference to the array type (class) and each + array element.
  • +
  • Primitive arrays report a reference to the array type.
  • +
+

+ This function can also be used to examine primitive (non-object) values. + The primitive value of an array or String + is reported after the object has been visited; + it is reported by invoking the agent supplied callback function + or + . + A primitive field + is reported after the object with that field is visited; + it is reported by invoking the agent supplied callback function + . +

+ Whether a callback is provided or is NULL only determines + whether the callback will be invoked, it does not influence + which objects are visited nor does it influence whether other callbacks + will be invoked. + However, the + visit control flags + returned by + do determine if the objects referenced by the + current object as visited. + The heap filter flags + and provided as parameters to this function + do not control which objects are visited but they do control which + objects and primitive values are reported by the callbacks. + For example, if the only callback that was set is + and klass + is set to the array of bytes class, then only arrays of byte will be + reported. + The table below summarizes this: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Controls objects visited + + Controls objects reported + + Controls primitives reported +
+ the + Heap Visit Control Flags + returned by + + Yes + + Yes, since visits are controlled + + Yes, since visits are controlled +
+ + in set + + No + + Yes + + No +
+ + + No + + Yes + + Yes +
+ + + No + + Yes + + Yes +
+

+ During the execution of this function the state of the heap + does not change: no objects are allocated, no objects are + garbage collected, and the state of objects (including + held values) does not change. + As a result, threads executing Java + programming language code, threads attempting to resume the + execution of Java programming language code, and threads + attempting to execute JNI functions are typically stalled. + + new + + + + + + + + This bit vector of + heap filter flags. + restricts the objects for which the callback function is called. + This applies to both the object and primitive callbacks. + + + + + + callbacks are not limited to instances of a particular + class + + + Callbacks are only reported when the object is an instance of + this class. + Objects which are instances of a subclass of klass + are not reported. + If klass is an interface, no objects are reported. + This applies to both the object and primitive callbacks. + + + + + + references are followed from the heap roots + + + The object to follow + + + + + jvmtiHeapCallbacks + + + Structure defining the set of callback functions. + + + + + + NULL is passed as the user supplied data + + + User supplied data to be passed to the callback. + + + + + + is not a valid class. + + + is not a valid object. + + + + + + + Iterate Through Heap + + Initiate an iteration over all objects in the heap. + This includes both reachable and + unreachable objects. Objects are visited in no particular order. +

+ Heap objects are reported by invoking the agent supplied + callback function . + References between objects are not reported. + If only reachable objects are desired, or if object reference information + is needed, use . +

+ This function can also be used to examine primitive (non-object) values. + The primitive value of an array or String + is reported after the object has been visited; + it is reported by invoking the agent supplied callback function + or + . + A primitive field + is reported after the object with that field is visited; + it is reported by invoking the agent supplied + callback function + . +

+ Unless the iteration is aborted by the + Heap Visit Control Flags + returned by a callback, all objects in the heap are visited. + Whether a callback is provided or is NULL only determines + whether the callback will be invoked, it does not influence + which objects are visited nor does it influence whether other callbacks + will be invoked. + The heap filter flags + and provided as parameters to this function + do not control which objects are visited but they do control which + objects and primitive values are reported by the callbacks. + For example, if the only callback that was set is + and klass + is set to the array of bytes class, then only arrays of byte will be + reported. The table below summarizes this (contrast this with + ): +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Controls objects visited + + Controls objects reported + + Controls primitives reported +
+ the + Heap Visit Control Flags + returned by + + No
(unless they abort the iteration) +
+ No
(unless they abort the iteration) +
+ No
(unless they abort the iteration) +
+ + in set + + No + + Yes + + No +
+ + + No + + Yes + + Yes +
+ + + No + + Yes + + Yes +
+

+ During the execution of this function the state of the heap + does not change: no objects are allocated, no objects are + garbage collected, and the state of objects (including + held values) does not change. + As a result, threads executing Java + programming language code, threads attempting to resume the + execution of Java programming language code, and threads + attempting to execute JNI functions are typically stalled. + + new + + + + + + + + This bit vector of + heap filter flags. + restricts the objects for which the callback function is called. + This applies to both the object and primitive callbacks. + + + + + + callbacks are not limited to instances of a particular class + + + Callbacks are only reported when the object is an instance of + this class. + Objects which are instances of a subclass of klass + are not reported. + If klass is an interface, no objects are reported. + This applies to both the object and primitive callbacks. + + + + + jvmtiHeapCallbacks + + + Structure defining the set callback functions. + + + + + + NULL is passed as the user supplied data + + + User supplied data to be passed to the callback. + + + + + + is not a valid class. + + + + + + Get Tag + + Retrieve the tag associated with an object. + The tag is a long value typically used to store a + unique identifier or pointer to object information. + The tag is set with + . + Objects for which no tags have been set return a + tag value of zero. + + new + + + + + + + + The object whose tag is to be retrieved. + + + + + + On return, the referenced long is set to the value + of the tag. + + + + + + + + + Set Tag + + Set the tag associated with an object. + The tag is a long value typically used to store a + unique identifier or pointer to object information. + The tag is visible with + . + + new + + + + + + + + The object whose tag is to be set. + + + + + + The new value of the tag. + + + + + + + + + Get Objects With Tags + + Return objects in the heap with the specified tags. + The format is parallel arrays of objects and tags. + + new + + + + + + + + Number of tags to scan for. + + + + + + + + Scan for objects with these tags. + Zero is not permitted in this array. + + + + + + + + Return the number of objects with any of the tags + in . + + + + + + this information is not returned + + + Returns the array of objects with any of the tags + in . + + + + + + this information is not returned + + + For each object in , + return the tag at the corresponding index. + + + + + + Zero is present in . + + + + + + Force Garbage Collection + + Force the VM to perform a garbage collection. + The garbage collection is as complete as possible. + This function does not cause finalizers to be run. + This function does not return until the garbage collection + is finished. +

+ Although garbage collection is as complete + as possible there is no guarantee that all + + events will have been + sent by the time that this function + returns. In particular, an object may be + prevented from being freed because it + is awaiting finalization. + + new + + + + + + + + + + + + + + + These functions and data types were introduced in the original + version 1.0 and have been superseded by more + + powerful and flexible versions + + which: + +

    +
  • + + Allow access to primitive values (the value of Strings, arrays, + and primitive fields) + +
  • +
  • + + Allow the tag of the referrer to be set, thus enabling more + efficient localized reference graph building + +
  • +
  • + + Provide more extensive filtering abilities + +
  • +
  • + + Are extensible, allowing their abilities to grow in future versions of + +
  • +
+

+ Please use the + current Heap functions. +

+ + + Tagged objects only. + + + Untagged objects only. + + + Either tagged or untagged objects. + + + + + + JNI global reference. + + + System class. + + + Monitor. + + + Stack local. + + + JNI local reference. + + + Thread. + + + Other. + + + + + + Reference from an object to its class. + + + Reference from an object to the value of one of its instance fields. + For references of this kind the referrer_index + parameter to the + jvmtiObjectReferenceCallback is the index of the + the instance field. The index is based on the order of all the + object's fields. This includes all fields of the directly declared + static and instance fields in the class, and includes all fields (both + public and private) fields declared in superclasses and superinterfaces. + The index is thus calculated by summing the index of the field in the directly + declared class (see ), with the total + number of fields (both public and private) declared in all superclasses + and superinterfaces. The index starts at zero. + + + Reference from an array to one of its elements. + For references of this kind the referrer_index + parameter to the + jvmtiObjectReferenceCallback is the array index. + + + Reference from a class to its class loader. + + + Reference from a class to its signers array. + + + Reference from a class to its protection domain. + + + Reference from a class to one of its interfaces. + + + Reference from a class to the value of one of its static fields. + For references of this kind the referrer_index + parameter to the + jvmtiObjectReferenceCallback is the index of the + the static field. The index is based on the order of all the + object's fields. This includes all fields of the directly declared + static and instance fields in the class, and includes all fields (both + public and private) fields declared in superclasses and superinterfaces. + The index is thus calculated by summing the index of the field in the directly + declared class (see ), with the total + number of fields (both public and private) declared in all superclasses + and superinterfaces. The index starts at zero. + Note: this definition differs from that in the 1.0 Specification. + No known implementations used the 1.0 definition. + + + Reference from a class to a resolved entry in the constant pool. + For references of this kind the referrer_index + parameter to the + jvmtiObjectReferenceCallback is the index into + constant pool table of the class, starting at 1. See the + + + + + + + Continue the iteration. + If this is a reference iteration, follow the references of this object. + + + Continue the iteration. + If this is a reference iteration, ignore the references of this object. + + + Abort the iteration. + + + + + + jvmtiIterationControl + Heap Object Callback + + Agent supplied callback function. + Describes (but does not pass in) an object in the heap. +

+ Return value should be JVMTI_ITERATION_CONTINUE to continue iteration, + or JVMTI_ITERATION_ABORT to stop iteration. +

+ See the heap callback + function restrictions. + + + + + + The tag of the class of object (zero if the class is not tagged). + If the object represents a runtime class, + the class_tag is the tag + associated with java.lang.Class + (zero if java.lang.Class is not tagged). + + + + + + Size of the object (in bytes). See . + + + + + + The object tag value, or zero if the object is not tagged. + To set the tag value to be associated with the object + the agent sets the jlong pointed to by the parameter. + + + + + + The user supplied data that was passed into the iteration function. + + + + + + + jvmtiIterationControl + Heap Root Object Callback + + Agent supplied callback function. + Describes (but does not pass in) an object that is a root for the purposes + of garbage collection. +

+ Return value should be JVMTI_ITERATION_CONTINUE to continue iteration, + JVMTI_ITERATION_IGNORE to continue iteration without pursuing + references from referree object or JVMTI_ITERATION_ABORT to stop iteration. +

+ See the heap callback + function restrictions. + + + + jvmtiHeapRootKind + + The kind of heap root. + + + + + + The tag of the class of object (zero if the class is not tagged). + If the object represents a runtime class, the class_tag is the tag + associated with java.lang.Class + (zero if java.lang.Class is not tagged). + + + + + + Size of the object (in bytes). See . + + + + + + The object tag value, or zero if the object is not tagged. + To set the tag value to be associated with the object + the agent sets the jlong pointed to by the parameter. + + + + + + The user supplied data that was passed into the iteration function. + + + + + + + jvmtiIterationControl + Stack Reference Object Callback + + Agent supplied callback function. + Describes (but does not pass in) an object on the stack that is a root for + the purposes of garbage collection. +

+ Return value should be JVMTI_ITERATION_CONTINUE to continue iteration, + JVMTI_ITERATION_IGNORE to continue iteration without pursuing + references from referree object or JVMTI_ITERATION_ABORT to stop iteration. +

+ See the heap callback + function restrictions. + + + + jvmtiHeapRootKind + + The kind of root (either JVMTI_HEAP_ROOT_STACK_LOCAL or + JVMTI_HEAP_ROOT_JNI_LOCAL). + + + + + + The tag of the class of object (zero if the class is not tagged). + If the object represents a runtime class, the class_tag is the tag + associated with java.lang.Class + (zero if java.lang.Class is not tagged). + + + + + + Size of the object (in bytes). See . + + + + + + The object tag value, or zero if the object is not tagged. + To set the tag value to be associated with the object + the agent sets the jlong pointed to by the parameter. + + + + + + The tag of the thread corresponding to this stack, zero if not tagged. + + + + + + The depth of the frame. + + + + + + The method executing in this frame. + + + + + + The slot number. + + + + + + The user supplied data that was passed into the iteration function. + + + + + + + jvmtiIterationControl + Object Reference Callback + + Agent supplied callback function. + Describes a reference from an object (the referrer) to another object + (the referree). +

+ Return value should be JVMTI_ITERATION_CONTINUE to continue iteration, + JVMTI_ITERATION_IGNORE to continue iteration without pursuing + references from referree object or JVMTI_ITERATION_ABORT to stop iteration. +

+ See the heap callback + function restrictions. + + + + jvmtiObjectReferenceKind + + The type of reference. + + + + + + The tag of the class of referree object (zero if the class is not tagged). + If the referree object represents a runtime class, + the class_tag is the tag + associated with java.lang.Class + (zero if java.lang.Class is not tagged). + + + + + + Size of the referree object (in bytes). + See . + + + + + + The referree object tag value, or zero if the object is not + tagged. + To set the tag value to be associated with the object + the agent sets the jlong pointed to by the parameter. + + + + + + The tag of the referrer object, or zero if the referrer + object is not tagged. + + + + + + For references of type JVMTI_REFERENCE_FIELD or + JVMTI_REFERENCE_STATIC_FIELD the index + of the field in the referrer object. The index is based on the + order of all the object's fields - see JVMTI_REFERENCE_FIELD + or JVMTI_REFERENCE_STATIC_FIELD + for further description. +

+ For references of type JVMTI_REFERENCE_ARRAY_ELEMENT + the array index - see + JVMTI_REFERENCE_ARRAY_ELEMENT for further description. +

+ For references of type JVMTI_REFERENCE_CONSTANT_POOL + the index into the constant pool of the class - see + + JVMTI_REFERENCE_CONSTANT_POOL for further + description. +

+ For references of other kinds the referrer_index is + -1. + + + + + + The user supplied data that was passed into the iteration function. + + + + + + + Iterate Over Objects Reachable From Object + + This function iterates over all objects that are directly + and indirectly reachable from the specified object. + For each object A (known + as the referrer) with a reference to object B the specified + callback function is called to describe the object reference. + The callback is called exactly once for each reference from a referrer; + this is true even if there are reference cycles or multiple paths to + the referrer. + There may be more than one reference between a referrer and a referree, + These may be distinguished by the + and + . + The callback for an object will always occur after the callback for + its referrer. +

+ See for the object + references which are reported. +

+ During the execution of this function the state of the heap + does not change: no objects are allocated, no objects are + garbage collected, and the state of objects (including + held values) does not change. + As a result, threads executing Java + programming language code, threads attempting to resume the + execution of Java programming language code, and threads + attempting to execute JNI functions are typically stalled. + + new + + + + + + + + The object + + + + + jvmtiObjectReferenceCallback + + + The callback to be called to describe each + object reference. + + + + + + NULL is passed as the user supplied data + + + User supplied data to be passed to the callback. + + + + + + + + + Iterate Over Reachable Objects + + This function iterates over the root objects and all objects that + are directly and indirectly reachable from the root objects. + The root objects comprise the set of system classes, + JNI globals, references from thread stacks, and other objects used as roots + for the purposes of garbage collection. +

+ For each root the + or callback is called. + An object can be a root object for more than one reason and in that case + the appropriate callback is called for each reason. +

+ For each object reference the + callback function is called to describe the object reference. + The callback is called exactly once for each reference from a referrer; + this is true even if there are reference cycles or multiple paths to + the referrer. + There may be more than one reference between a referrer and a referree, + These may be distinguished by the + and + . + The callback for an object will always occur after the callback for + its referrer. +

+ See for the object + references which are reported. +

+ Roots are always reported to the profiler before any object references + are reported. In other words, the + callback will not be called until the appropriate callback has been called + for all roots. If the callback is + specified as NULL then this function returns after + reporting the root objects to the profiler. +

+ During the execution of this function the state of the heap + does not change: no objects are allocated, no objects are + garbage collected, and the state of objects (including + held values) does not change. + As a result, threads executing Java + programming language code, threads attempting to resume the + execution of Java programming language code, and threads + attempting to execute JNI functions are typically stalled. + + new + + + + + + + jvmtiHeapRootCallback + do not report heap roots + + + The callback function to be called for each heap root of type + JVMTI_HEAP_ROOT_JNI_GLOBAL, + JVMTI_HEAP_ROOT_SYSTEM_CLASS, + JVMTI_HEAP_ROOT_MONITOR, + JVMTI_HEAP_ROOT_THREAD, or + JVMTI_HEAP_ROOT_OTHER. + + + + + jvmtiStackReferenceCallback + do not report stack references + + + The callback function to be called for each heap root of + JVMTI_HEAP_ROOT_STACK_LOCAL or + JVMTI_HEAP_ROOT_JNI_LOCAL. + + + + + jvmtiObjectReferenceCallback + do not follow references from the root objects + + + The callback function to be called for each object reference. + + + + + + NULL is passed as the user supplied data + + + User supplied data to be passed to the callback. + + + + + + + + + Iterate Over Heap + + Iterate over all objects in the heap. This includes both reachable and + unreachable objects. +

+ The parameter indicates the + objects for which the callback function is called. If this parameter + is JVMTI_HEAP_OBJECT_TAGGED then the callback will only be + called for every object that is tagged. If the parameter is + JVMTI_HEAP_OBJECT_UNTAGGED then the callback will only be + for objects that are not tagged. If the parameter + is JVMTI_HEAP_OBJECT_EITHER then the callback will be + called for every object in the heap, irrespective of whether it is + tagged or not. +

+ During the execution of this function the state of the heap + does not change: no objects are allocated, no objects are + garbage collected, and the state of objects (including + held values) does not change. + As a result, threads executing Java + programming language code, threads attempting to resume the + execution of Java programming language code, and threads + attempting to execute JNI functions are typically stalled. + + new + + + + + + jvmtiHeapObjectFilter + + Indicates the objects for which the callback function is called. + + + + + jvmtiHeapObjectCallback + + + The iterator function to be called for each + object matching the . + + + + + + NULL is passed as the user supplied data + + + User supplied data to be passed to the callback. + + + + + + + + + Iterate Over Instances Of Class + + Iterate over all objects in the heap that are instances of the specified class. + This includes direct instances of the specified class and + instances of all subclasses of the specified class. + This includes both reachable and unreachable objects. +

+ The parameter indicates the + objects for which the callback function is called. If this parameter + is JVMTI_HEAP_OBJECT_TAGGED then the callback will only be + called for every object that is tagged. If the parameter is + JVMTI_HEAP_OBJECT_UNTAGGED then the callback will only be + called for objects that are not tagged. If the parameter + is JVMTI_HEAP_OBJECT_EITHER then the callback will be + called for every object in the heap, irrespective of whether it is + tagged or not. +

+ During the execution of this function the state of the heap + does not change: no objects are allocated, no objects are + garbage collected, and the state of objects (including + held values) does not change. + As a result, threads executing Java + programming language code, threads attempting to resume the + execution of Java programming language code, and threads + attempting to execute JNI functions are typically stalled. + + new + + + + + + + + Iterate over objects of this class only. + + + + jvmtiHeapObjectFilter + + Indicates the objects for which the callback function is called. + + + + + jvmtiHeapObjectCallback + + + The iterator function to be called for each + instance matching + the . + + + + + + NULL is passed as the user supplied data + + + User supplied data to be passed to the callback. + + + + + + + + + + + + + These functions are used to retrieve or set the value of a local variable. + The variable is identified by the depth of the frame containing its + value and the variable's slot number within that frame. + The mapping of variables to + slot numbers can be obtained with the function + . + + + + Get Local Variable - Object + + This function can be used to retrieve the value of a local + variable whose type is Object or a subclass of Object. + + jvmdi + + + + + + + + The thread of the frame containing the variable's value. + + + + + + The depth of the frame containing the variable's value. + + + + + + The variable's slot number. + + + + + + On return, points to the variable's value. + + + + + + Invalid slot. + + + The variable type is not + Object or a subclass of Object. + + + Not a visible frame + + + + + + Get Local Variable - Int + + This function can be used to retrieve the value of a local + variable whose type is int, + short, char, byte, or + boolean. + + jvmdi + + + + + + + + The thread of the frame containing the variable's value. + + + + + + The depth of the frame containing the variable's value. + + + + + + The variable's slot number. + + + + + + On return, points to the variable's value. + + + + + + Invalid slot. + + + The variable type is not + int, short, + char, byte, or + boolean. + + + Not a visible frame + + + + + + Get Local Variable - Long + + This function can be used to retrieve the value of a local + variable whose type is long. + + jvmdi + + + + + + + + The thread of the frame containing the variable's value. + + + + + + The depth of the frame containing the variable's value. + + + + + + The variable's slot number. + + + + + + On return, points to the variable's value. + + + + + + Invalid slot. + + + The variable type is not long. + + + Not a visible frame + + + + + + Get Local Variable - Float + + This function can be used to retrieve the value of a local + variable whose type is float. + + jvmdi + + + + + + + + The thread of the frame containing the variable's value. + + + + + + The depth of the frame containing the variable's value. + + + + + + The variable's slot number. + + + + + + On return, points to the variable's value. + + + + + + Invalid slot. + + + The variable type is not float. + + + Not a visible frame + + + + + + Get Local Variable - Double + + This function can be used to retrieve the value of a local + variable whose type is long. + + jvmdi + + + + + + + + The thread of the frame containing the variable's value. + + + + + + The depth of the frame containing the variable's value. + + + + + + The variable's slot number. + + + + + + On return, points to the variable's value. + + + + + + Invalid slot. + + + The variable type is not double. + + + Not a visible frame + + + + + + Set Local Variable - Object + + This function can be used to set the value of a local + variable whose type is Object or a subclass of Object. + + jvmdi + + + + + + + + The thread of the frame containing the variable's value. + + + + + + The depth of the frame containing the variable's value. + + + + + + The variable's slot number. + + + + + + The new value for the variable. + + + + + + Invalid slot. + + + The variable type is not + Object or a subclass of Object. + + + The supplied is not compatible + with the variable type. + + + Not a visible frame + + + + + + Set Local Variable - Int + + This function can be used to set the value of a local + variable whose type is int, + short, char, byte, or + boolean. + + jvmdi + + + + + + + + The thread of the frame containing the variable's value. + + + + + + The depth of the frame containing the variable's value. + + + + + + The variable's slot number. + + + + + + The new value for the variable. + + + + + + Invalid slot. + + + The variable type is not + int, short, + char, byte, or + boolean. + + + Not a visible frame + + + + + + Set Local Variable - Long + + This function can be used to set the value of a local + variable whose type is long. + + jvmdi + + + + + + + + The thread of the frame containing the variable's value. + + + + + + The depth of the frame containing the variable's value. + + + + + + The variable's slot number. + + + + + + The new value for the variable. + + + + + + Invalid slot. + + + The variable type is not long. + + + Not a visible frame + + + + + + Set Local Variable - Float + + This function can be used to set the value of a local + variable whose type is float. + + jvmdi + + + + + + + + The thread of the frame containing the variable's value. + + + + + + The depth of the frame containing the variable's value. + + + + + + The variable's slot number. + + + + + + The new value for the variable. + + + + + + Invalid slot. + + + The variable type is not float. + + + Not a visible frame + + + + + + Set Local Variable - Double + + This function can be used to set the value of a local + variable whose type is double. + + jvmdi + + + + + + + + The thread of the frame containing the variable's value. + + + + + + The depth of the frame containing the variable's value. + + + + + + The variable's slot number. + + + + + + The new value for the variable. + + + + + + Invalid slot. + + + The variable type is not double. + + + Not a visible frame + + + + + + + + + + + + Set Breakpoint + + Set a breakpoint at the instruction indicated by + method and location. + An instruction can only have one breakpoint. +

+ Whenever the designated instruction is about to be executed, a + event is generated. + + jvmdi + + + + + + + + The class in which to set the breakpoint + + + + + + The method in which to set the breakpoint + + + + + + the index of the instruction at which to set the breakpoint + + + + + + + The designated bytecode already has a breakpoint. + + + + + + Clear Breakpoint + + Clear the breakpoint at the bytecode indicated by + method and location. + + jvmdi + + + + + + + + The class in which to clear the breakpoint + + + + + + The method in which to clear the breakpoint + + + + + + the index of the instruction at which to clear the breakpoint + + + + + + There's no breakpoint at the designated bytecode. + + + + + + + + + + + + + Set Field Access Watch + + Generate a event + when the field specified + by klass and + field is about to be accessed. + An event will be generated for each access of the field + until it is canceled with + . + Field accesses from Java programming language code or from JNI code are watched, + fields modified by other means are not watched. + Note that users should be aware that their own field accesses + will trigger the watch. + A field can only have one field access watch set. + Modification of a field is not considered an access--use + + to monitor modifications. + + jvmdi + + + + + + + + The class containing the field to watch + + + + + + The field to watch + + + + + + + The designated field is already being watched for accesses. + + + + + + Clear Field Access Watch + + Cancel a field access watch previously set by + , on the + field specified + by klass and + field. + + jvmdi + + + + + + + + The class containing the field to watch + + + + + + The field to watch + + + + + + + The designated field is not being watched for accesses. + + + + + + Set Field Modification Watch + + Generate a event + when the field specified + by klass and + field is about to be modified. + An event will be generated for each modification of the field + until it is canceled with + . + Field modifications from Java programming language code or from JNI code are watched, + fields modified by other means are not watched. + Note that users should be aware that their own field modifications + will trigger the watch. + A field can only have one field modification watch set. + + jvmdi + + + + + + + + The class containing the field to watch + + + + + + The field to watch + + + + + + + The designated field is already being watched for modifications. + + + + + + Clear Field Modification Watch + + + Cancel a field modification watch previously set by + , on the + field specified + by klass and + field. + + jvmdi + + + + + + + + The class containing the field to watch + + + + + + The field to watch + + + + + + + The designated field is not being watched for modifications. + + + + + + + + + + + + Get Loaded Classes + + Return an array of all classes loaded in the virtual machine. + The number of classes in the array is returned via + class_count_ptr, and the array itself via + classes_ptr. +

+ Array classes of all types (including arrays of primitive types) are + included in the returned list. Primitive classes (for example, + java.lang.Integer.TYPE) are not included in this list. + + jvmdi + + + + + + + On return, points to the number of classes. + + + + + + On return, points to an array of references, one + for each class. + + + + + + + + + Get Classloader Classes + + Returns an array of those classes for which this class loader has + been recorded as an initiating loader. Each + class in the returned array was created by this class loader, + either by defining it directly or by delegation to another class loader. + See the + . +

+ For JDK version 1.1 implementations that don't + recognize the distinction between initiating and defining class loaders, + this function should return all classes loaded in the virtual machine. + The number of classes in the array is returned via + class_count_ptr, and the array itself via + classes_ptr. + + jvmdi + + + + + + + the classes initiated by the bootstrap loader will be returned + + + An initiating class loader. + + + + + + On return, points to the number of classes. + + + + + + On return, points to an array of references, one + for each class. + + + + + + + + + Get Class Signature + + For the class indicated by klass, return the + JNI + type signature + and the generic signature of the class. + For example, java.util.List is "Ljava/util/List;" + and int[] is "[I" + The returned name for primitive classes + is the type signature character of the corresponding primitive type. + For example, java.lang.Integer.TYPE is "I". + + jvmdiClone + + + + + + + The class to query. + + + + + + the signature is not returned + + + On return, points to the JNI type signature of the class, encoded as a + modified UTF-8 string. + + + + + + the generic signature is not returned + + + On return, points to the generic signature of the class, encoded as a + modified UTF-8 string. + If there is no generic signature attribute for the class, then, + on return, points to NULL. + + + + + + + + + Get Class Status + + Get the status of the class. Zero or more of the following bits can be + set. + + + Class bytecodes have been verified + + + Class preparation is complete + + + Class initialization is complete. Static initializer has been run. + + + Error during initialization makes class unusable + + + Class is an array. If set, all other bits are zero. + + + Class is a primitive class (for example, java.lang.Integer.TYPE). + If set, all other bits are zero. + + + + jvmdi + + + + + + + The class to query. + + + + + + On return, points to the current state of this class as one or + more of the class status flags. + + + + + + + + + Get Source File Name + + For the class indicated by klass, return the source file + name via source_name_ptr. The returned string + is a file name only and never contains a directory name. +

+ For primitive classes (for example, java.lang.Integer.TYPE) + and for arrays this function returns + . + + jvmdi + + + + + + + + The class to query. + + + + + + On return, points to the class's source file name, encoded as a + modified UTF-8 string. + + + + + + Class information does not include a source file name. This includes + cases where the class is an array class or primitive class. + + + + + + Get Class Modifiers + + For the class indicated by klass, return the access + flags + via modifiers_ptr. + Access flags are defined in the + . +

+ If the class is an array class, then its public, private, and protected + modifiers are the same as those of its component type. For arrays of + primitives, this component type is represented by one of the primitive + classes (for example, java.lang.Integer.TYPE). +

+ If the class is a primitive class, its public modifier is always true, + and its protected and private modifiers are always false. +

+ If the class is an array class or a primitive class then its final + modifier is always true and its interface modifier is always false. + The values of its other modifiers are not determined by this specification. + + + jvmdi + + + + + + + The class to query. + + + + + + On return, points to the current access flags of this class. + + + + + + + + + + Get Class Methods + + For the class indicated by klass, return a count of + methods via method_count_ptr and a list of + method IDs via methods_ptr. The method list contains + constructors and static initializers as well as true methods. + Only directly declared methods are returned (not inherited methods). + An empty method list is returned for array classes and primitive classes + (for example, java.lang.Integer.TYPE). + + jvmdi + + + + + + + + The class to query. + + + + + + On return, points to the number of methods declared in this class. + + + + + + On return, points to the method ID array. + + + + + + is not prepared. + + + + + + Get Class Fields + + For the class indicated by klass, return a count of fields + via field_count_ptr and a list of field IDs via + fields_ptr. + Only directly declared fields are returned (not inherited fields). + Fields are returned in the order they occur in the class file. + An empty field list is returned for array classes and primitive classes + (for example, java.lang.Integer.TYPE). + Use JNI to determine the length of an array. + + jvmdi + + + + + + + The class to query. + + + + + + On return, points to the number of fields declared in this class. + + + + + + On return, points to the field ID array. + + + + + + is not prepared. + + + + + + Get Implemented Interfaces + + Return the direct super-interfaces of this class. For a class, this + function returns the interfaces declared in its implements + clause. For an interface, this function returns the interfaces declared in + its extends clause. + An empty interface list is returned for array classes and primitive classes + (for example, java.lang.Integer.TYPE). + + jvmdi + + + + + + + The class to query. + + + + + + On return, points to the number of interfaces. + + + + + + On return, points to the interface array. + + + + + + is not prepared. + + + + + + Get Class Version Numbers + + For the class indicated by klass, + return the minor and major version numbers, + as defined in the + . + + new + + + + + + + The class to query. + + + + + + On return, points to the value of the + minor_version item of the + Class File Format. + Note: to be consistent with the Class File Format, + the minor version number is the first parameter. + + + + + + On return, points to the value of the + major_version item of the + Class File Format. + + + + + + The class is a primitive or array class. + + + + + + Get Constant Pool + + For the class indicated by klass, + return the raw bytes of the constant pool in the format of the + constant_pool item of the + . + The format of the constant pool may differ between versions + of the Class File Format, so, the + minor and major + class version numbers should be checked for + compatibility. +

+ The returned constant pool might not have the same layout or + contents as the constant pool in the defining class file. + The constant pool returned by GetConstantPool() may have + more or fewer entries than the defining constant pool. + Entries may be in a different order. + The constant pool returned by GetConstantPool() will match the + constant pool used by + GetBytecodes(). + That is, the bytecodes returned by GetBytecodes() will have + constant pool indices which refer to constant pool entries returned + by GetConstantPool(). + Note that since + and can change + the constant pool, the constant pool returned by this function + can change accordingly. Thus, the correspondence between + GetConstantPool() and GetBytecodes() does not hold if there + is an intervening class retransformation or redefinition. + The value of a constant pool entry used by a given bytecode will + match that of the defining class file (even if the indices don't match). + Constant pool entries which are not used directly or indirectly by + bytecodes (for example, UTF-8 strings associated with annotations) are + not required to exist in the returned constant pool. + + new + + + + + + + + The class to query. + + + + + + On return, points to the number of entries + in the constant pool table plus one. + This corresponds to the constant_pool_count + item of the Class File Format. + + + + + + On return, points to the number of bytes + in the returned raw constant pool. + + + + + + On return, points to the raw constant pool, that is the bytes + defined by the constant_pool item of the + Class File Format + + + + + + The class is a primitive or array class. + + + + + + Is Interface + + Determines whether a class object reference represents an interface. + The jboolean result is + JNI_TRUE if the "class" is actually an interface, + JNI_FALSE otherwise. + + jvmdi + + + + + + + The class to query. + + + + + + On return, points to the boolean result of this function. + + + + + + + + + + Is Array Class + + Determines whether a class object reference represents an array. + The jboolean result is + JNI_TRUE if the class is an array, + JNI_FALSE otherwise. + + jvmdi + + + + + + + The class to query. + + + + + + On return, points to the boolean result of this function. + + + + + + + + + + Is Modifiable Class + + Determines whether a class is modifiable. + If a class is modifiable ( + returns JNI_TRUE) the class can be + redefined with (assuming + the agent possesses the + + capability) or + retransformed with (assuming + the agent possesses the + + capability). + If a class is not modifiable ( + returns JNI_FALSE) the class can be neither + redefined nor retransformed. +

+ Primitive classes (for example, java.lang.Integer.TYPE) + and array classes are never modifiable. +

+ + new + + + If possessed then all classes (except primitive and array classes) + are modifiable. + + + No effect on the result of the function. + But must additionally be possessed to modify the class with + . + + + No effect on the result of the function. + But must additionally be possessed to modify the class with + . + + + + + + + The class to query. + + + + + + On return, points to the boolean result of this function. + + + + + + + + + Get Class Loader + + For the class indicated by klass, return via + classloader_ptr a reference to the class loader for the + class. + + jvmdi + + + + + + + The class to query. + + + + + + On return, points to the class loader that loaded + this class. + If the class was not created by a class loader + or if the class loader is the bootstrap class loader, + points to NULL. + + + + + + + + + + Get Source Debug Extension + + For the class indicated by klass, return the debug + extension via source_debug_extension_ptr. + The returned string + contains exactly the debug extension information present in the + class file of klass. + + jvmdi + + + + + + + + The class to query. + + + + + + On return, points to the class's debug extension, encoded as a + modified UTF-8 string. + + + + + + Class information does not include a debug extension. + + + + + + Retransform Classes + + This function facilitates the + bytecode instrumentation + of already loaded classes. + To replace the class definition without reference to the existing + bytecodes, as one might do when recompiling from source for + fix-and-continue debugging, + function should be used instead. +

+ When classes are initially loaded or when they are + redefined, + the initial class file bytes can be transformed with the + event. + This function reruns the transformation process + (whether or not a transformation has previously occurred). + This retransformation follows these steps: +

    +
  • starting from the initial class file bytes +
  • +
  • for each retransformation + incapable + agent which received a + ClassFileLoadHook event during the previous + load or redefine, the bytes it returned + (via the new_class_data parameter) + are reused as the output of the transformation; + note that this is equivalent to reapplying + the previous transformation, unaltered. except that + the ClassFileLoadHook event + is not sent to these agents +
  • +
  • for each retransformation + capable + agent, the ClassFileLoadHook event is sent, + allowing a new transformation to be applied +
  • +
  • the transformed class file bytes are installed as the new + definition of the class +
  • +
+ See the event for more details. +

+ The initial class file bytes represent the bytes passed to + ClassLoader.defineClass + or RedefineClasses (before any transformations + were applied), however they may not exactly match them. + The constant pool may differ in ways described in + . + Constant pool indices in the bytecodes of methods will correspond. + Some attributes may not be present. + Where order is not meaningful, for example the order of methods, + order may not be preserved. +

+ Retransformation can cause new versions of methods to be installed. + Old method versions may become + obsolete + The new method version will be used on new invokes. + If a method has active stack frames, those active frames continue to + run the bytecodes of the original method version. +

+ This function does not cause any initialization except that which + would occur under the customary JVM semantics. + In other words, retransforming a class does not cause its initializers to be + run. The values of static fields will remain as they were + prior to the call. +

+ Threads need not be suspended. +

+ All breakpoints in the class are cleared. +

+ All attributes are updated. +

+ Instances of the retransformed class are not affected -- fields retain their + previous values. + Tags on the instances are + also unaffected. +

+ In response to this call, no events other than the + event + will be sent. +

+ The retransformation may change method bodies, the constant pool and attributes. + The retransformation must not add, remove or rename fields or methods, change the + signatures of methods, change modifiers, or change inheritance. + These restrictions may be lifted in future versions. + See the error return description below for information on error codes + returned if an unsupported retransformation is attempted. + The class file bytes are not verified or installed until they have passed + through the chain of events, thus the + returned error code reflects the result of the transformations. + If any error code is returned other than JVMTI_ERROR_NONE, + none of the classes to be retransformed will have a new definition installed. + When this function returns (with the error code of JVMTI_ERROR_NONE) + all of the classes to be retransformed will have their new definitions installed. + + new + + + + + + + + + The number of classes to be retransformed. + + + + + + The array of classes to be retransformed. + + + + + + One of the cannot be modified. + See . + + + One of the is not a valid class. + + + A retransformed class file has a version number not supported by this VM. + + + A retransformed class file is malformed (The VM would return a ClassFormatError). + + + The retransformed class file definitions would lead to a circular definition + (the VM would return a ClassCircularityError). + + + The retransformed class file bytes fail verification. + + + The class name defined in a retransformed class file is + different from the name in the old class object. + + + A retransformed class file would require adding a method. + + + A retransformed class file changes a field. + + + A direct superclass is different for a retransformed class file, + or the set of directly implemented + interfaces is different. + + + A retransformed class file does not declare a method + declared in the old class version. + + + A retransformed class file has different class modifiers. + + + A method in the retransformed class file has different modifiers + than its counterpart in the old class version. + + + + + + Redefine Classes + + + + + Class object for this class + + + + + + Number of bytes defining class (below) + + + + + + Bytes defining class (in the + ) + + + + + All classes given are redefined according to the definitions + supplied. + This function is used to replace the definition of a class + with a new definition, as might be needed in fix-and-continue + debugging. + Where the existing class file bytes are to be transformed, for + example in + bytecode instrumentation, + should be used. +

+ Redefinition can cause new versions of methods to be installed. + Old method versions may become + obsolete + The new method version will be used on new invokes. + If a method has active stack frames, those active frames continue to + run the bytecodes of the original method version. + If resetting of stack frames is desired, use + + to pop frames with obsolete method versions. +

+ This function does not cause any initialization except that which + would occur under the customary JVM semantics. + In other words, redefining a class does not cause its initializers to be + run. The values of static fields will remain as they were + prior to the call. +

+ Threads need not be suspended. +

+ All breakpoints in the class are cleared. +

+ All attributes are updated. +

+ Instances of the redefined class are not affected -- fields retain their + previous values. + Tags on the instances are + also unaffected. +

+ In response to this call, the event + Class File Load Hook + will be sent (if enabled), but no other events will be sent. +

+ The redefinition may change method bodies, the constant pool and attributes. + The redefinition must not add, remove or rename fields or methods, change the + signatures of methods, change modifiers, or change inheritance. + These restrictions may be lifted in future versions. + See the error return description below for information on error codes + returned if an unsupported redefinition is attempted. + The class file bytes are not verified or installed until they have passed + through the chain of events, thus the + returned error code reflects the result of the transformations applied + to the bytes passed into . + If any error code is returned other than JVMTI_ERROR_NONE, + none of the classes to be redefined will have a new definition installed. + When this function returns (with the error code of JVMTI_ERROR_NONE) + all of the classes to be redefined will have their new definitions installed. + + jvmdi + + + + + + + + + The number of classes specified in class_definitions + + + + jvmtiClassDefinition + + The array of new class definitions + + + + + + One of class_bytes is NULL. + + + An element of class_definitions cannot be modified. + See . + + + An element of class_definitions is not a valid class. + + + A new class file has a version number not supported by this VM. + + + A new class file is malformed (The VM would return a ClassFormatError). + + + The new class file definitions would lead to a circular definition + (the VM would return a ClassCircularityError). + + + The class bytes fail verification. + + + The class name defined in a new class file is + different from the name in the old class object. + + + A new class file would require adding a method. + + + A new class version changes a field. + + + A direct superclass is different for a new class + version, or the set of directly implemented + interfaces is different. + + + A new class version does not declare a method + declared in the old class version. + + + A new class version has different modifiers. + + + A method in the new class version has different modifiers + than its counterpart in the old class version. + + + + + + + + + + Get Object Size + + For the object indicated by object, + return via size_ptr the size of the object. + This size is an implementation-specific approximation of + the amount of storage consumed by this object. + It may include some or all of the object's overhead, and thus + is useful for comparison within an implementation but not + between implementations. + The estimate may change during a single invocation of the JVM. + + new + + + + + + + The object to query. + + + + + + On return, points to the object's size in bytes. + + + + + + + + + Get Object Hash Code + + For the object indicated by object, + return via hash_code_ptr a hash code. + This hash code could be used to maintain a hash table of object references, + however, on some implementations this can cause significant performance + impacts--in most cases + tags + will be a more efficient means of associating information with objects. + This function guarantees + the same hash code value for a particular object throughout its life + + jvmdi + + + + + + + The object to query. + + + + + + On return, points to the object's hash code. + + + + + + + + + Get Object Monitor Usage + + + + + The thread owning this monitor, or NULL if unused + + + + + + The number of times the owning thread has entered the monitor + + + + + + The number of threads waiting to own this monitor + + + + + + The waiter_count waiting threads + + + + + + The number of threads waiting to be notified by this monitor + + + + + + The notify_waiter_count threads waiting to be notified + + + + + Get information about the object's monitor. + The fields of the structure + are filled in with information about usage of the monitor. + + Decide and then clarify suspend requirements. + + + jvmdi + + + + + + + + The object to query. + + + + jvmtiMonitorUsage + + On return, filled with monitor information for the + specified object. + + + + + + + + + + Get Object Monitors + + Return the list of object monitors. +

+ Note: details about each monitor can be examined with + . + + new + + + + + + + + On return, pointer to the number + of monitors returned in monitors_ptr. + + + + + + On return, pointer to the monitor list. + + + + + + + + + + + + + + + + + Get Field Name (and Signature) + + For the field indicated by and , + return the field name via and field signature via + . +

+ Field signatures are defined in the JNI Specification and + are referred to as + . + + jvmdiClone + + + + + + + The class of the field to query. + + + + + + The field to query. + + + + + + the name is not returned + + + On return, points to the field name, encoded as a + modified UTF-8 string. + + + + + + the signature is not returned + + + On return, points to the field signature, encoded as a + modified UTF-8 string. + + + + + + the generic signature is not returned + + + On return, points to the generic signature of the field, encoded as a + modified UTF-8 string. + If there is no generic signature attribute for the field, then, + on return, points to NULL. + + + + + + + + + Get Field Declaring Class + + For the field indicated by klass and field + return the class that defined it via declaring_class_ptr. + The declaring class will either be klass, a superclass, or + an implemented interface. + + jvmdi + + + + + + + The class to query. + + + + + + The field to query. + + + + + + On return, points to the declaring class + + + + + + + + + Get Field Modifiers + + For the field indicated by klass and field + return the access flags via modifiers_ptr. + Access flags are defined in the + . + + jvmdi + + + + + + + The class to query. + + + + + + The field to query. + + + + + + On return, points to the access flags. + + + + + + + + + Is Field Synthetic + + For the field indicated by klass and field, return a + value indicating whether the field is synthetic via is_synthetic_ptr. + Synthetic fields are generated by the compiler but not present in the + original source code. + + jvmdi + + + + + + + + The class of the field to query. + + + + + + The field to query. + + + + + + On return, points to the boolean result of this function. + + + + + + + + + + + + + These functions provide information about a method (represented as a + ) and set how methods are processed. + + + + The functions and + can cause new versions + of methods to be installed. + An original version of a method is considered equivalent + to the new version if: +

    +
  • their bytecodes are the same except for indices into the + constant pool and
  • +
  • the referenced constants are equal.
  • +
+ An original method version which is not equivalent to the + new method version is called obsolete and is assigned a new method ID; + the original method ID now refers to the new method version. + A method ID can be tested for obsolescence with + . +
+ + + Get Method Name (and Signature) + + For the method indicated by method, + return the method name via name_ptr and method signature via + signature_ptr. +

+ Method signatures are defined in the JNI Specification and are referred to as + . + Note this is different + than method signatures as defined in the Java Language Specification. + + jvmdiClone + + + + + + + The method to query. + + + + + + the name is not returned + + + On return, points to the method name, encoded as a + modified UTF-8 string. + + + + + + the signature is not returned + + + On return, points to the method signature, encoded as a + modified UTF-8 string. + + + + + + the generic signature is not returned + + + On return, points to the generic signature of the method, encoded as a + modified UTF-8 string. + If there is no generic signature attribute for the method, then, + on return, points to NULL. + + + + + + + + + Get Method Declaring Class + + For the method indicated by method, + return the class that defined it via declaring_class_ptr. + + jvmdi + + + + + + + The class to query. + + + + + + The method to query. + + + + + + On return, points to the declaring class + + + + + + + + + Get Method Modifiers + + For the method indicated by method, + return the access flags via modifiers_ptr. + Access flags are defined in the + . + + jvmdi + + + + + + + The class to query. + + + + + + The method to query. + + + + + + On return, points to the access flags. + + + + + + + + + Get Max Locals + + For the method indicated by method, + return the number of local variable slots used by the method, + including the local variables used to pass parameters to the + method on its invocation. +

+ See max_locals in the + . + + jvmdi + + + + + + + The class to query. + + + + + + The method to query. + + + + + + On return, points to the maximum number of local slots + + + + + + + + + Get Arguments Size + + For the method indicated by method, + return via max_ptr the number of local variable slots used + by the method's arguments. + Note that two-word arguments use two slots. + + jvmdi + + + + + + + The class to query. + + + + + + The method to query. + + + + + + On return, points to the number of argument slots + + + + + + + + + Get Line Number Table + + + + + the where the line begins + + + + + + the line number + + + + + For the method indicated by method, + return a table of source line number entries. The size of the table is + returned via entry_count_ptr and the table itself is + returned via table_ptr. + + jvmdi + + + + + + + + The class to query. + + + + + + The method to query. + + + + + + On return, points to the number of entries in the table + + + + jvmtiLineNumberEntry + + On return, points to the line number table pointer. + + + + + + Class information does not include line numbers. + + + + + + Get Method Location + + For the method indicated by method, + return the beginning and ending addresses through + start_location_ptr and end_location_ptr. In a + conventional byte code indexing scheme, + start_location_ptr will always point to zero + and end_location_ptr + will always point to the byte code count minus one. + + jvmdi + + + + + + + The class to query. + + + + + + The method to query. + + + + + + On return, points to the first location, or + -1 if location information is not available. + If the information is available and + + returns + then this will always be zero. + + + + + + On return, points to the last location, + or -1 if location information is not available. + + + + + + Class information does not include method sizes. + + + + + + Get Local Variable Table + + + + + The code array index where the local variable is first valid + (that is, where it must have a value). + + + + + + The length of the valid section for this local variable. + The last code array index where the local variable is valid + is start_location + length. + + + + + + The local variable name, encoded as a + modified UTF-8 string. + + + + + + The local variable's type signature, encoded as a + modified UTF-8 string. + The signature format is the same as that defined in + + + + + + + The local variable's generic signature, encoded as a + modified UTF-8 string. + The value of this field will be NULL for any local + variable which does not have a generic type. + + + + + + The local variable's slot. See Local Variables. + + + + + Return local variable information. + + jvmdiClone + + + + + + + + The method to query. + + + + + + On return, points to the number of entries in the table + + + + jvmtiLocalVariableEntry + + On return, points to an array of local variable table entries. + + + + + + Class information does not include local variable + information. + + + + + + Get Bytecodes + + For the method indicated by method, + return the byte codes that implement the method. The number of + bytecodes is returned via bytecode_count_ptr. The byte codes + themselves are returned via bytecodes_ptr. + + jvmdi + + + + + + + + The class to query. + + + + + + The method to query. + + + + + + On return, points to the length of the byte code array + + + + + + On return, points to the pointer to the byte code array + + + + + + + + + Is Method Native + + For the method indicated by method, return a + value indicating whether the method is native via is_native_ptr + + jvmdi + + + + + + + The class to query. + + + + + + The method to query. + + + + + + On return, points to the boolean result of this function. + + + + + + + + + Is Method Synthetic + + For the method indicated by method, return a + value indicating whether the method is synthetic via is_synthetic_ptr. + Synthetic methods are generated by the compiler but not present in the + original source code. + + jvmdi + + + + + + + + The class to query. + + + + + + The method to query. + + + + + + On return, points to the boolean result of this function. + + + + + + + + + Is Method Obsolete + + Determine if a method ID refers to an + obsolete + method version. + + jvmdi + + + + + + + The class to query. + + + + + + The method ID to query. + + + + + + On return, points to the boolean result of this function. + + + + + + + + + Set Native Method Prefix + + This function modifies the failure handling of + native method resolution by allowing retry + with a prefix applied to the name. + When used with the + ClassFileLoadHook + event, it enables native methods to be + instrumented. +

+ Since native methods cannot be directly instrumented + (they have no bytecodes), they must be wrapped with + a non-native method which can be instrumented. + For example, if we had: + +native boolean foo(int x); +

+ We could transform the class file (with the + ClassFileLoadHook event) so that this becomes: + +boolean foo(int x) { + ... record entry to foo ... + return wrapped_foo(x); +} + +native boolean wrapped_foo(int x); +

+ Where foo becomes a wrapper for the actual native method + with the appended prefix "wrapped_". Note that + "wrapped_" would be a poor choice of prefix since it + might conceivably form the name of an existing method + thus something like "$$$MyAgentWrapped$$$_" would be + better but would make these examples less readable. +

+ The wrapper will allow data to be collected on the native + method call, but now the problem becomes linking up the + wrapped method with the native implementation. + That is, the method wrapped_foo needs to be + resolved to the native implementation of foo, + which might be: + +Java_somePackage_someClass_foo(JNIEnv* env, jint x) +

+ This function allows the prefix to be specified and the + proper resolution to occur. + Specifically, when the standard resolution fails, the + resolution is retried taking the prefix into consideration. + There are two ways that resolution occurs, explicit + resolution with the JNI function RegisterNatives + and the normal automatic resolution. For + RegisterNatives, the VM will attempt this + association: + +method(foo) -> nativeImplementation(foo) +

+ When this fails, the resolution will be retried with + the specified prefix prepended to the method name, + yielding the correct resolution: + +method(wrapped_foo) -> nativeImplementation(foo) +

+ For automatic resolution, the VM will attempt: + +method(wrapped_foo) -> nativeImplementation(wrapped_foo) +

+ When this fails, the resolution will be retried with + the specified prefix deleted from the implementation name, + yielding the correct resolution: + +method(wrapped_foo) -> nativeImplementation(foo) +

+ Note that since the prefix is only used when standard + resolution fails, native methods can be wrapped selectively. +

+ Since each environment is independent and + can do its own transformation of the bytecodes, more + than one layer of wrappers may be applied. Thus each + environment needs its own prefix. Since transformations + are applied in order, the prefixes, if applied, will + be applied in the same order. + The order of transformation application is described in + the event. + Thus if three environments applied + wrappers, foo might become + $env3_$env2_$env1_foo. But if, say, + the second environment did not apply a wrapper to + foo it would be just + $env3_$env1_foo. To be able to + efficiently determine the sequence of prefixes, + an intermediate prefix is only applied if its non-native + wrapper exists. Thus, in the last example, even though + $env1_foo is not a native method, the + $env1_ prefix is applied since + $env1_foo exists. +

+ Since the prefixes are used at resolution time + and since resolution may be arbitrarily delayed, a + native method prefix must remain set as long as there + are corresponding prefixed native methods. + + new + + + + + + + + + any existing prefix in this environment is cancelled + + + + The prefix to apply, encoded as a + modified UTF-8 string. + + + + + + + + + Set Native Method Prefixes + + For a normal agent, + will provide all needed native method prefixing. + For a meta-agent that performs multiple independent class + file transformations (for example as a proxy for another + layer of agents) this function allows each transformation + to have its own prefix. + The prefixes are applied in the order supplied and are + processed in the same manor as described for the + application of prefixes from multiple environments + in . +

+ Any previous prefixes are replaced. Thus, calling this + function with a of 0 + disables prefixing in this environment. +

+ and this function + are the two ways to set the prefixes. + Calling SetNativeMethodPrefix with + a prefix is the same as calling this function with + of 1. + Calling SetNativeMethodPrefix with + NULL is the same as calling this function with + of 0. + + new + + + + + + + + The number of prefixes to apply. + + + + + + + + The prefixes to apply for this environment, each encoded as a + modified UTF-8 string. + + + + + + + + + + + + + Create Raw Monitor + + Create a raw monitor. + + jvmdi + + + + + + + A name to identify the monitor, encoded as a + modified UTF-8 string. + + + + + + On return, points to the created monitor. + + + + + + + + + Destroy Raw Monitor + + Destroy the raw monitor. + If the monitor being destroyed has been entered by this thread, it will be + exited before it is destroyed. + If the monitor being destroyed has been entered by another thread, + an error will be returned and the monitor will not be destroyed. + + jvmdi + + + + + + + The monitor + + + + + + Not monitor owner + + + + + + Raw Monitor Enter + + Gain exclusive ownership of a raw monitor. + The same thread may enter a monitor more then once. + The thread must + exit + the monitor the same number of times as it is entered. + If a monitor is entered during OnLoad (before attached threads exist) + and has not exited when attached threads come into existence, the enter + is considered to have occurred on the main thread. + + jvmdi + + + + + + + The monitor + + + + + + + + + Raw Monitor Exit + + Release exclusive ownership of a raw monitor. + + jvmdi + + + + + + + The monitor + + + + + + Not monitor owner + + + + + + Raw Monitor Wait + + Wait for notification of the raw monitor. +

+ Causes the current thread to wait until either another thread calls + or + + for the specified raw monitor, or the specified + timeout + has elapsed. + + jvmdi + + + + + + + The monitor + + + + + + The timeout, in milliseconds. If the timeout is + zero, then real time is not taken into consideration + and the thread simply waits until notified. + + + + + + Not monitor owner + + + Wait was interrupted, try again + + + + + + Raw Monitor Notify + + Notify a single thread waiting on the raw monitor. + + jvmdi + + + + + + + The monitor + + + + + + Not monitor owner + + + + + + Raw Monitor Notify All + + Notify all threads waiting on the raw monitor. + + jvmdi + + + + + + + The monitor + + + + + + Not monitor owner + + + + + + + Get Raw Monitor Use + + The fields of the structure + are filled in with information about usage of the raw monitor. + + new + + + + + + + + the raw monitor to query. + + + + jvmtiMonitorUsage + + On return, filled with monitor information for the + specified raw monitor. + + + + + + + + + Get Raw Monitors + + Return the list of raw monitors. +

+ Note: details about each monitor can be examined with + . + + new + + + + + + + + On return, pointer to the number + of monitors returned in monitors_ptr. + + + + + + On return, pointer to the monitor list. + + + + + + + + + + + + + Provides the ability to intercept and resend + Java Native Interface (JNI) function calls + by manipulating the JNI function table. + See JNI + Functions in the Java Native Interface Specification. +

+ The following example illustrates intercepting the + NewGlobalRef JNI call in order to count reference + creation. + +JNIEnv original_jni_Functions; +JNIEnv redirected_jni_Functions; +int my_global_ref_count = 0; + +jobject +MyNewGlobalRef(JNIEnv *jni_env, jobject lobj) { + ++my_global_ref_count; + return originalJNIFunctions->NewGlobalRef(env, lobj); +} + +void +myInit() { + jvmtiError err; + + err = (*jvmti_env)->GetJNIFunctionTable(jvmti_env, &original_jni_Functions); + if (err != JVMTI_ERROR_NONE) { + die(); + } + err = (*jvmti_env)->GetJNIFunctionTable(jvmti_env, &redirected_jni_Functions); + if (err != JVMTI_ERROR_NONE) { + die(); + } + redirectedJNIFunctions->NewGlobalRef = MyNewGlobalRef; + err = (*jvmti_env)->SetJNIFunctionTable(jvmti_env, redirected_jni_Functions); + if (err != JVMTI_ERROR_NONE) { + die(); + } +} + + Sometime after myInit is called the user's JNI + code is executed which makes the call to create a new global + reference. Instead of going to the normal JNI implementation + the call goes to myNewGlobalRef. Note that a + copy of the original function table is kept so that the normal + JNI function can be called after the data is collected. + Note also that any JNI functions which are not overwritten + will behave normally. + + check that the example compiles and executes. + + + + + Set JNI Function Table + + Set the JNI function table + in all current and future JNI environments. + As a result, all future JNI calls are directed to the specified functions. + Use to get the + function table to pass to this function. + For this function to take effect the the updated table entries must be + used by the JNI clients. + Since the table is defined const some compilers may optimize + away the access to the table, thus preventing this function from taking + effect. + The table is copied--changes to the local copy of the + table have no effect. + This function affects only the function table, all other aspects of the environment are + unaffected. + See the examples above. + + new + + + + + + jniNativeInterface + + + Points to the new JNI function table. + + + + + + + + + Get JNI Function Table + + Get the JNI function table. + The JNI function table is copied into allocated memory. + If + has been called, the modified (not the original) function + table is returned. + Only the function table is copied, no other aspects of the environment + are copied. + See the examples above. + + new + + + + + + jniNativeInterface + + + On return, *function_table + points a newly allocated copy of the JNI function table. + + + + + + + + + + + + + Set Event Callbacks + + Set the functions to be called for each event. + The callbacks are specified by supplying a replacement function table. + The function table is copied--changes to the local copy of the + table have no effect. + This is an atomic action, all callbacks are set at once. + No events are sent before this function is called. + When an entry is NULL or when the event is beyond + no event is sent. + Details on events are + described later in this document. + An event must be enabled and have a callback in order to be + sent--the order in which this function and + + are called does not affect the result. + + new + + + + + + jvmtiEventCallbacks + remove the existing callbacks + + + The new event callbacks. + + + + + + sizeof(jvmtiEventCallbacks)--for version + compatibility. + + + + + + + + + Set Event Notification Mode + + Control the generation of events. + + + If is JVMTI_ENABLE, + the event will be enabled + + + If is JVMTI_DISABLE, + the event will be disabled + + + If thread is NULL, + the event is enabled or disabled globally; otherwise, it is + enabled or disabled for a particular thread. + An event is generated for + a particular thread if it is enabled either at the thread or global + levels. +

+ See below for information on specific events. +

+ The following events cannot be controlled at the thread + level through this function. +

    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
+

+ Initially, no events are enabled at either the thread level + or the global level. +

+ Any needed capabilities (see Event Enabling Capabilities below) must be possessed + before calling this function. +

+ Details on events are + described below. + + jvmdiClone + + + + jvmtiEventMode + + JVMTI_ENABLE or JVMTI_DISABLE + + + + jvmtiEvent + + the event to control + + + + + + event is controlled at the global level + + + The thread to control + + + + + + for future expansion + + + + + + is non-NULL and is not a valid thread. + + + is non-NULL and is not live (has not been started or is now dead). + + + thread level control was attempted on events which do not + permit thread level control. + + + The Required Event Enabling Capability is not possessed. + + + + + + Generate Events + + Generate events to represent the current state of the VM. + For example, if is + JVMTI_EVENT_COMPILED_METHOD_LOAD, + a event will be + sent for each currently compiled method. + Methods that were loaded and now have been unloaded are not sent. + The history of what events have previously been sent does not + effect what events are sent by this function--for example, + all currently compiled methods + will be sent each time this function is called. +

+ This function is useful when + events may have been missed due to the agent attaching after program + execution begins; this function generates the missed events. +

+ Attempts to execute Java programming language code or + JNI functions may be paused until this function returns - + so neither should be called from the thread sending the event. + This function returns only after the missed events have been + sent, processed and have returned. + The event may be sent on a different thread than the thread + on which the event occurred. + The callback for the event must be set with + + and the event must be enabled with + + or the events will not occur. + If the VM no longer has the information to generate some or + all of the requested events, the events are simply not sent - + no error is returned. +

+ Only the following events are supported: +

    +
  • +
  • +
+
+ new + + + + + + jvmtiEvent + + The type of event to generate. Must be one of these: +
    +
  • JVMTI_EVENT_COMPILED_METHOD_LOAD
  • +
  • JVMTI_EVENT_DYNAMIC_CODE_GENERATED
  • +
+
+ +
+ + + is + JVMTI_EVENT_COMPILED_METHOD_LOAD + and + is false. + + + is other than + JVMTI_EVENT_COMPILED_METHOD_LOAD + or JVMTI_EVENT_DYNAMIC_CODE_GENERATED. + + +
+ + + + + + + These functions + allow a implementation to provide functions and events + beyond those defined in this specification. +

+ Both extension functions and extension events have parameters + each of which has a 'type' and 'kind' chosen from the following tables: + + + + Java programming language primitive type - byte. + JNI type jbyte. + + + Java programming language primitive type - char. + JNI type jchar. + + + Java programming language primitive type - short. + JNI type jshort. + + + Java programming language primitive type - int. + JNI type . + + + Java programming language primitive type - long. + JNI type . + + + Java programming language primitive type - float. + JNI type . + + + Java programming language primitive type - double. + JNI type . + + + Java programming language primitive type - boolean. + JNI type . + + + Java programming language object type - java.lang.Object. + JNI type . + Returned values are JNI local references and must be managed. + + + Java programming language object type - java.lang.Thread. + type . + Returned values are JNI local references and must be managed. + + + Java programming language object type - java.lang.Class. + JNI type . + Returned values are JNI local references and must be managed. + + + Union of all Java programming language primitive and object types - + JNI type . + Returned values which represent object types are JNI local references and must be managed. + + + Java programming language field identifier - + JNI type . + + + Java programming language method identifier - + JNI type . + + + C programming language type - char. + + + C programming language type - void. + + + JNI environment - JNIEnv. + Should be used with the correct to make it a pointer type. + + + + + + Ingoing argument - foo. + + + Ingoing pointer argument - const foo*. + + + Ingoing array argument - const foo*. + + + Outgoing allocated array argument - foo**. + Free with Deallocate. + + + Outgoing allocated array of allocated arrays argument - foo***. + Free with Deallocate. + + + Outgoing argument - foo*. + + + Outgoing array argument (pre-allocated by agent) - foo*. + Do not Deallocate. + + + + + + + + + + The parameter name, encoded as a + modified UTF-8 string + + + + jvmtiParamKind + + The kind of the parameter - type modifiers + + + + jvmtiParamTypes + + The base type of the parameter - modified by kind + + + + + + Is a NULL argument permitted? Applies only to pointer and object types. + + + + + + jvmtiError + Extension Function + + This is the implementation-specific extension function. + + + + + jvmtiEnv + + + The environment is the only fixed parameter for extension functions. + + + + + + The extension function-specific parameters + + + + + + + Get Extension Functions + + + + + jvmtiExtensionFunction + + + The actual function to call + + + + + + The identifier for the extension function, encoded as a + modified UTF-8 string. + Uses package name conventions. + For example, com.sun.hotspot.bar + + + + + + A one sentence description of the function, encoded as a + modified UTF-8 string. + + + + + + The number of parameters excluding jvmtiEnv *jvmti_env + + + + + jvmtiParamInfo + + + Array of + + parameters (jvmtiEnv *jvmti_env excluded) + + + + + + The number of possible error returns (excluding universal errors) + + + + + jvmtiError + + + Array of + possible errors + + + + + + Returns the set of extension functions. + + new + + + + + + + On return, points to the number of extension functions + + + + jvmtiExtensionFunctionInfo + + Returns an array of extension function info, one per function + + + + + + + + + Get Extension Events + + + + + + The identifying index of the event + + + + + + The identifier for the extension event, encoded as a + modified UTF-8 string. + Uses package name conventions. + For example, com.sun.hotspot.bar + + + + + + A one sentence description of the event, encoded as a + modified UTF-8 string. + + + + + + The number of parameters excluding jvmtiEnv *jvmti_env + + + + + jvmtiParamInfo + + + Array of + + parameters (jvmtiEnv *jvmti_env excluded) + + + + + + Returns the set of extension events. + + new + + + + + + + On return, points to the number of extension events + + + + jvmtiExtensionEventInfo + + Returns an array of extension event info, one per event + + + + + + + + + + Extension Event + + This is the implementation-specific event. + The event handler is set with + . +

+ Event handlers for extension events must be declared varargs to match this definition. + Failure to do so could result in calling convention mismatch and undefined behavior + on some platforms. +

+ For example, if the jvmtiParamInfo + returned by indicates that + there is a jint parameter, the event handler should be + declared: + + void JNICALL myHandler(jvmtiEnv* jvmti_env, jint myInt, ...) + + Note the terminal "..." which indicates varargs. + + + + + jvmtiEnv + + + The environment is the only fixed parameter for extension events. + + + + + + The extension event-specific parameters + + + + + + + Set Extension Event Callback + + + Sets the callback function for an extension event and + enables the event. Or, if the callback is NULL, disables + the event. Note that unlike standard events, setting + the callback and enabling the event are a single operation. + + new + + + + + + + Identifies which callback to set. + This index is the + + field of + . + + + + + jvmtiExtensionEvent + disable the event + + + If callback is non-NULL, + set callback to be the event callback function + and enable the event. + + + + + + is not an + + returned by + + + + + + + + + + + The capabilities functions allow you to change the + functionality available to --that is, + which + functions can be called, what events can be generated, + and what functionality these events and functions can + provide. +

+ The "Capabilities" section of each function and event describe which + capabilities, if any, they are associated with. "Required Functionality" + means it is available for use and no capabilities must be added to use it. + "Optional Functionality" means the agent must possess the capability + before it can be used. + To possess a capability, the agent must + add the capability. + "Optional Features" describe capabilities which, + if added, extend the feature set. +

+ The potentially available capabilities of each implementation are different. + Depending on the implementation, a capability: +

    +
  • may never be added
  • +
  • may be added in either the OnLoad or live phase in any environment
  • +
  • may be added only during the OnLoad phase
  • +
  • may be possessed by only one environment at a time
  • +
  • may be possessed by only one environment at a time, + and only during the OnLoad phase
  • +
  • and so on ...
  • +
+ Frequently, the addition of a capability may incur a cost in execution speed, start up + time, and/or memory footprint. Note that the overhead of using a capability + is completely different than the overhead of possessing a capability. + Take single stepping as an example. When single stepping is on (that + is, when the event is enabled and thus actively sending events) + the overhead of sending and processing an event + on each instruction is huge in any implementation. + However, the overhead of possessing the capability may be small or large, + depending on the implementation. Also, when and if a capability is potentially + available depends on the implementation. Some examples: +
    +
  • One VM might perform all execution by compiling bytecodes into + native code and be unable to generate single step instructions. + In this implementation the capability can not be added.
  • +
  • Another VM may be able to switch execution to a single stepping + interpreter at any time. In this implementation, having the capability has no + overhead and could be added at any time.
  • +
  • Yet another VM might be able to choose a bytecode compiling or single stepping capable interpreted + execution engine at start up, but be unable to switch between them. + In this implementation the capability would need to be added + during the OnLoad phase (before bytecode + execution begins) and would have a large impact on execution speed + even if single stepping was never used.
  • +
  • Still another VM might be able to add an "is single stepping on" check + into compiled bytecodes or a generated interpreter. Again in this implementation + the capability would need to be added during the OnLoad phase but the overhead (a test + and branch on each instruction) would be considerably less.
  • +
+

+ Each environment + has its own set of capabilities. + Initially, that set is empty. + Any desired capability must be added. + If possible, capabilities should be added during the OnLoad phase. For most + virtual machines certain capabilities require special set up for + the virtual machine and this set up must happen + during the OnLoad phase, before the virtual machine begins execution. + Once a capability is added, it can + only be removed if explicitly relinquished by the environment. +

+ The agent can, + determine what + capabilities this VM can potentially provide, + add the capabilities + to be used, + release capabilities + which are no longer needed, and + examine the currently available + capabilities. + + + + For example, a freshly started agent (in the OnLoad function) + wants to enable all possible capabilities. + Note that, in general, this is not advisable as the agent may suffer + a performance penalty for functionality it is not using. + The code might look like this in C: + + jvmtiCapabilities capa; + jvmtiError err; + + err = (*jvmti)->GetPotentialCapabilities(jvmti, &capa); + if (err == JVMTI_ERROR_NONE) { + err = (*jvmti)->AddCapabilities(jvmti, &capa); + + For example, if an agent wants to check if it can get + the bytecodes of a method (that is, it wants to check + if it previously added this capability and has not + relinquished it), the code might + look like this in C: + + jvmtiCapabilities capa; + jvmtiError err; + + err = (*jvmti)->GetCapabilities(jvmti, &capa); + if (err == JVMTI_ERROR_NONE) { + if (capa.can_get_bytecodes) { ... } } + + + + + + The functions in this category use this capabilities structure + which contains boolean flags corresponding to each capability: + + + + Can set and get tags, as described in the + Heap category. + + + + + Can set watchpoints on field modification - + + + + + + Can set watchpoints on field access - + + + + + + Can get bytecodes of a method + + + + + Can test if a field or method is synthetic - + and + + + + + + Can get information about ownership of monitors - + + + + + + Can + + + + + Can + + + + + Can pop frames off the stack - + + + + + Can redefine classes with . + + + + + Can send stop or interrupt to threads + + + + + Can get the source file name of a class + + + + + Can get the line number table of a method + + + + + Can get the source debug extension of a class + + + + + Can set and get local variables + + + + + Can return methods in the order they occur in the class file + + + + + Can get single step events + + + + + Can get exception thrown and + exception catch events + + + + + Can set and thus get + events + + + + + Can set and thus get + events + + + + + Can suspend and resume threads + + + + + Can modify (retransform or redefine) any non-primitive non-array class. + See . + + + + + Can get + current thread CPU time + + + + + Can get + thread CPU time + + + + + Can generate method entry events on entering a method + + + + + Can generate method exit events on leaving a method + + + + + Can generate ClassFileLoadHook events for every loaded class. + + + + + Can generate events when a method is compiled or unloaded + + + + + Can generate events on monitor activity + + + + + Can generate events on VM allocation of an object + + + + + Can generate events when a native method is bound to its + implementation + + + + + Can generate events when garbage collection begins or ends + + + + + Can generate events when the garbage collector frees an object + + + + + Can return early from a method, as described in the + Force Early Return category. + + + + + Can get information about owned monitors with stack depth - + + + + + + Can get the constant pool of a class - + + + + + + Can set prefix to be applied when native method cannot be resolved - + and + + + + + + Can retransform classes with . + In addition to the restrictions imposed by the specific + implementation on this capability (see the + Capability section), + this capability must be set before the + event is enabled for the + first time in this environment. + An environment that possesses this capability at the time that + ClassFileLoadHook is enabled for the first time is + said to be retransformation capable. + An environment that does not possess this capability at the time that + ClassFileLoadHook is enabled for the first time is + said to be retransformation incapable. + + + + + can be called on any class + ( + must also be set) + + + + + Can generate events when the VM is unable to allocate memory from + the Java platform heap. + See . + + + + + Can generate events when the VM is unable to create a thread. + See . + + + + + + Get Potential Capabilities + + Returns via the + features that can potentially be possessed by this environment + at this time. + The returned capabilities differ from the complete set of capabilities + implemented by the VM in two cases: another environment possesses + capabilities that can only be possessed by one environment, or the + current phase is live, + and certain capabilities can only be added during the OnLoad phase. + The function + may be used to set any or all or these capabilities. + Currently possessed capabilities are included. +

+ Typically this function is used in the OnLoad function. + Some virtual machines may allow a limited set of capabilities to be + added in the live phase. + In this case, the set of potentially available capabilities + will likely differ from the OnLoad phase set. +

+ See the + Capability Examples. + + new + + + + + jvmtiCapabilities + + On return, points to the capabilities that may be added. + + + + + + + + + + Estimate Cost Of Capabilities + + There is strong opposition to this function. The concern is + that it would be difficult or impossible to provide meaningful + numbers, as the amount of impact is conditional on many factors + that a single number could not represent. There is doubt that + conditional implementations would be used or are even a good idea. + The thought is that release documentation for the implementation + would be the best means of exposing this information. + Unless new arguments are presented, I intend to remove this + function in the next revision. + +

+ Return via the and + an estimate of the impact + of adding the capabilities pointed to by + . + The returned estimates are in percentage of additional overhead, thus + a time impact of 100 mean the application might run + at half the speed. + The estimates are very rough approximations and are not guaranteed. + Note also, that the estimates are of the impact of having the + capability available--when and if it is used the impact may be + much greater. + Estimates can be for a single capability or for a set of + capabilities. Note that the costs are not necessarily additive, + adding support for one capability might make another available + for free or conversely having two capabilities at once may + have multiplicative impact. + Estimates are relative to the current set of capabilities - + that is, how much more impact given the currently possessed capabilities. +

+ Typically this function is used in the OnLoad function, + some virtual machines may allow a limited set of capabilities to be + added in the live phase. + In this case, the set of potentially available capabilities + will likely differ from the OnLoad phase set. +

+ See the + Capability Examples. + + new + + + + + jvmtiCapabilities + + points to the capabilities to evaluate. + + + + + + On return, points to the estimated percentage increase in + run time if this capability was added. + + + + + + On return, points to the estimated percentage increase in + memory space used if this capability was added. + + + + + + The desired capabilities are not even potentially available. + + + + + + + Add Capabilities + + Set new capabilities by adding the capabilities + whose values are set to one (1) in + *. + All previous capabilities are retained. + Typically this function is used in the OnLoad function. + Some virtual machines may allow a limited set of capabilities to be + added in the live phase. +

+ See the + Capability Examples. + + new + + + + + jvmtiCapabilities + + Points to the capabilities to add. + + + + + + The desired capabilities are not even potentially available. + + + + + + + Relinquish Capabilities + + Relinquish the capabilities + whose values are set to one (1) in + *. + Some implementations may allow only one environment to have a capability + (see the capability introduction). + This function releases capabilities + so that they may be used by other agents. + All other capabilities are retained. + The capability will no longer be present in . + Attempting to relinquish a capability that the agent does not possess is not an error. + + It is possible for the agent to be actively using capabilities + which are being relinquished. For example, a thread is currently + suspended and can_suspend is being relinquished or an event is currently + enabled and can_generate_whatever is being relinquished. + There are three possible ways we could spec this: +

    +
  • relinquish automatically releases them
  • +
  • relinquish checks and returns some error code if held
  • +
  • it is the agent's responsibility and it is not checked
  • +
+ One of these should be chosen. + + + new + + + + + jvmtiCapabilities + + Points to the capabilities to relinquish. + + + + + + + + + + + Get Capabilities + + Returns via the optional + features which this environment currently possesses. + Each possessed capability is indicated by a one (1) in the + corresponding field of the capabilities + structure. + An environment does not possess a capability unless it has been successfully added with + . + An environment only loses possession of a capability if it has been relinquished with + . Thus, this function returns the net result + of the AddCapabilities and RelinquishCapabilities calls which + have been made. +

+ See the + Capability Examples. + + jvmdiClone + + + + + jvmtiCapabilities + + On return, points to the capabilities. + + + + + + + + + + + + + + These functions provide timing information. + The resolution at which the time is updated is not specified. + They provides nanosecond precision, but not necessarily nanosecond accuracy. + Details about the timers, such as their maximum values, can be accessed with + the timer information functions. + + + + + The information function for each timer returns this data structure. + + + + + The maximum value the timer can reach. + After this value is reached the timer wraps back to zero. + This is an unsigned value. If tested or printed as a jlong (signed value) + it may appear to be a negative number. + + + + + + If true, the timer can be externally adjusted and as a result skip forward. + If false, the timer value will never increase faster than real time. + + + + + + If true, the timer can be externally adjusted and as a result skip backward. + If false, the timer value will be monotonically increasing. + + + + jvmtiTimerKind + + The kind of timer. + On a platform that does not distinguish between user and system time, JVMTI_TIMER_TOTAL_CPU + is returned. + + + + + + Reserved for future use. + + + + + + Reserved for future use. + + + + + + Where the timer kind is -- + + + + CPU time that a thread is in user mode. + + + CPU time that a thread is in user or system mode. + + + Elapsed time. + + + + + + Get Current Thread CPU Timer Information + + Get information about the + timer. + The fields of the structure + are filled in with details about the timer. + This information is specific to the platform and the implementation of + and thus + does not vary by thread nor does it vary + during a particular invocation of the VM. +

+ Note that the implementations of + and may differ, and thus the values + returned by GetCurrentThreadCpuTimerInfo + and + may differ -- see for more information. + + new + + + Can get current thread CPU time. + + + + + jvmtiTimerInfo + + On return, filled with information describing the time + returned by . + + + + + + + + + Get Current Thread CPU Time + + Return the CPU time utilized by the current thread. +

+ Note that the + function provides CPU time for any thread, including + the current thread. GetCurrentThreadCpuTime + exists to support platforms which cannot + supply CPU time for threads other than the current + thread or which have more accurate information for + the current thread (see + vs + ). + On many platforms this call will be equivalent to: + + GetThreadCpuTime(env, NULL, nanos_ptr) + + + new + + + Can get current thread CPU time. +

+ If this capability is enabled after threads have started, + the implementation may choose any time up + to and including the time that the capability is enabled + as the point where CPU time collection starts. +

+ This capability must be potentially available on any + platform where + can_get_thread_cpu_time + is potentially available. + + + + + + + On return, points to the CPU time used by this thread + in nanoseconds. + This is an unsigned value. If tested or printed as a jlong (signed value) + it may appear to be a negative number. + + + + + + + + + Get Thread CPU Timer Information + + Get information about the + timer. + The fields of the structure + are filled in with details about the timer. + This information is specific to the platform and the implementation of + and thus + does not vary by thread nor does it vary + during a particular invocation of the VM. +

+ Note that the implementations of + and may differ, and thus the values + returned by + and GetThreadCpuTimerInfo + may differ -- see for more information. + + new + + + Can get thread CPU time. + + + + + jvmtiTimerInfo + + On return, filled with information describing the time + returned by . + + + + + + + + + Get Thread CPU Time + + Return the CPU time utilized by the specified thread. +

+ Get information about this timer with + . + + new + + + Can get thread CPU time. +

+ If this capability is enabled after threads have started, + the implementation may choose any time up + to and including the time that the capability is enabled + as the point where CPU time collection starts. + + + + + + + The thread to query. + + + + + + On return, points to the CPU time used by the specified thread + in nanoseconds. + This is an unsigned value. If tested or printed as a jlong (signed value) + it may appear to be a negative number. + + + + + + + + + Get Timer Information + + Get information about the + timer. + The fields of the structure + are filled in with details about the timer. + This information will not change during a particular invocation of the VM. + + new + + + + + jvmtiTimerInfo + + On return, filled with information describing the time + returned by . + + + + + + + + + Get Time + + Return the current value of the system timer, in nanoseconds. +

+ The value returned represents nanoseconds since some fixed but + arbitrary time (perhaps in the future, so values may be + negative). This function provides nanosecond precision, but not + necessarily nanosecond accuracy. No guarantees are made about + how frequently values change. +

+ Get information about this timer with + . + + new + + + + + + + On return, points to the time in nanoseconds. + This is an unsigned value. If tested or printed as a jlong (signed value) + it may appear to be a negative number. + + + + + + + + + Get Available Processors + + Returns the number of processors available to the Java virtual machine. +

+ This value may change during a particular invocation of the virtual machine. + Applications that are sensitive to the number of available processors should + therefore occasionally poll this property. + + new + + + + + + + On return, points to the maximum number of processors available to the + virtual machine; never smaller than one. + + + + + + + + + + + + + + These functions allow the agent to add to the locations that a class loader searches for a class. + This is useful for installing instrumentation under the correct class loader. + + + + Add To Bootstrap Class Loader Search + + This function can be used to cause instrumentation classes to be defined by the + bootstrap class loader. See + . + After the bootstrap + class loader unsuccessfully searches for a class, the specified platform-dependent + search path will be searched as well. Only one segment may be specified in + the . This function may be called multiple times to add multiple segments, + the segments will be searched in the order that this function was called. +

+ In the OnLoad phase the function may be used to specify any platform-dependent + search path segment to be searched after the bootstrap class loader unsuccessfully searches + for a class. The segment is typically a directory or JAR file. +

+ In the live phase the may be used to specify any platform-dependent + path to a + JAR file. The agent should take care that the JAR file does not + contain any classes or resources other than those to be defined by the bootstrap + class loader for the purposes of instrumentation. +

+ The specifies that a subsequent attempt to resolve a symbolic + reference that the Java virtual machine has previously unsuccessfully attempted + to resolve always fails with the same error that was thrown as a result of the + initial resolution attempt. Consequently, if the JAR file contains an entry + that corresponds to a class for which the Java virtual machine has + unsuccessfully attempted to resolve a reference, then subsequent attempts to + resolve that reference will fail with the same error as the initial attempt. + + new + + + + + + + The platform-dependent search path segment, encoded as a + modified UTF-8 string. + + + + + + is an invalid path. In the live phase, anything other than an + existing JAR file is an invalid path. + + + + + + Add To System Class Loader Search + + This function can be used to cause instrumentation classes to be + defined by the system class loader. See + . + After the class loader unsuccessfully searches for a class, the specified platform-dependent search + path will be searched as well. Only one segment may be specified in the + . This function may be called multiple times to add multiple segments, the + segments will be searched in the order that this function was called. +

+ In the OnLoad phase the function may be used to specify any platform-dependent + search path segment to be searched after the system class loader unsuccessfully searches + for a class. The segment is typically a directory or JAR file. +

+ In the live phase the is a platform-dependent path to a JAR file to be + searched after the system class loader unsuccessfully searches for a class. The agent should + take care that the JAR file does not contain any classes or resources other than those to be + defined by the system class loader for the purposes of instrumentation. +

+ In the live phase the system class loader supports adding a JAR file to be searched if + the system class loader implements a method name appendToClassPathForInstrumentation + which takes a single parameter of type java.lang.String. The method is not required + to have public access. +

+ The specifies that a subsequent attempt to resolve a symbolic + reference that the Java virtual machine has previously unsuccessfully attempted + to resolve always fails with the same error that was thrown as a result of the + initial resolution attempt. Consequently, if the JAR file contains an entry + that corresponds to a class for which the Java virtual machine has + unsuccessfully attempted to resolve a reference, then subsequent attempts to + resolve that reference will fail with the same error as the initial attempt. + + new + + + + + + + The platform-dependent search path segment, encoded as a + modified UTF-8 string. + + + + + + is an invalid path. In the live phase, anything other than an + existing JAR file is an invalid path. + + + Operation not supported by the system class loader. + + + + + + + + + + + These functions get and set system properties. + + + + Get System Properties + + The list of VM system property keys which may be used with + is returned. + It is strongly recommended that virtual machines provide the + following property keys: +

    +
  • java.vm.vendor
  • +
  • java.vm.version
  • +
  • java.vm.name
  • +
  • java.vm.info
  • +
  • java.library.path
  • +
  • java.class.path
  • +
+ Provides access to system properties defined by and used + by the VM. + Properties set on the command-line are included. + This allows getting and setting of these properties + before the VM even begins executing bytecodes. + Since this is a VM view of system properties, the set of available + properties will usually be different than that + in java.lang.System.getProperties. + JNI method invocation may be used to access + java.lang.System.getProperties. +

+ The set of properties may grow during execution. + + new + + + + + + + On return, points to the number of property keys returned. + + + + + + On return, points to an array of property keys, encoded as + modified UTF-8 strings. + + + + + + + + + Get System Property + + Return a VM system property value given the property key. +

+ The function + returns the set of property keys which may be used. + The properties which can be retrieved may grow during + execution. +

+ Since this is a VM view of system properties, the values + of properties may differ from that returned by + java.lang.System.getProperty(String). + A typical VM might copy the values of the VM system + properties into the Properties held by + java.lang.System during the initialization + of that class. Thereafter any changes to the VM system + properties (with ) + or the java.lang.System system properties + (with java.lang.System.setProperty(String,String)) + would cause the values to diverge. + JNI method invocation may be used to access + java.lang.System.getProperty(String). + + new + + + + + + + The key of the property to retrieve, encoded as a + modified UTF-8 string. + + + + + + On return, points to the property value, encoded as a + modified UTF-8 string. + + + + + + This property is not available. + Use to find available properties. + + + + + + Set System Property + + Set a VM system property value. +

+ The function + returns the set of property keys, some of these may be settable. + See . + + new + + + + + + + The key of the property, encoded as a + modified UTF-8 string. + + + + + + + do not set the value, but return + if the property is not writeable + + + + The property value to set, encoded as a + modified UTF-8 string. + + + + + + This property is not available or is not writeable. + + + + + + + + + + + + + Get Phase + + Return the current phase of VM execution. + The phases proceed in sequence: + + + OnLoad phase: while in the + Agent_OnLoad function. + + + Primordial phase: between return from Agent_OnLoad and the + VMStart event. + + + Start phase: when the VMStart event + is sent and until the VMInit event is sent. + + + Live phase: when the VMInit event is sent + and until the event returns. + + + Dead phase: after the event returns or after + start-up failure. + + + In the case of start-up failure the VM will proceed directly to the dead + phase skipping intermediate phases and neither a VMInit nor + VMDeath event will be sent. +

+ Most functions operate only in the live phase. + The following functions operate in either the OnLoad or live phases: + + The following functions operate in only the OnLoad phase: + + The following functions operate in the start or live phases: + + The following functions operate in any phase: + + JNI functions (except the Invocation API) must only be used in the start or live phases. +

+ Most events are sent only in the live phase. + The following events operate in others phases: + + + + new + + + + + jvmtiPhase + + On return, points to the phase. + + + + + + + + + Dispose Environment + + Shutdown a connection created with JNI GetEnv + (see Environments). + Dispose of any resources held by the environment. + + What resources are reclaimed? What is undone? + Breakpoints,watchpoints removed? + + Threads suspended by this environment are not resumed by this call, + this must be done explicitly by the agent. + Memory allocated by this environment via calls to functions + is not released, this can be done explicitly by the agent + by calling . + Raw monitors created by this environment are not destroyed, + this can be done explicitly by the agent + by calling . + The state of threads waiting on raw monitors created by this environment + are not affected. +

+ Any native method + prefixes for this environment will be unset; + the agent must remove any prefixed native methods before + dispose is called. +

+ Any capabilities + held by this environment are relinquished. +

+ Events enabled by this environment will no longer be sent, however + event handlers currently running will continue to run. Caution must + be exercised in the design of event handlers whose environment may + be disposed and thus become invalid during their execution. +

+ This environment may not be used after this call. + This call returns to the caller. + + new + + + + + + + + + + Set Environment Local Storage + + The VM stores a pointer value associated with each environment. + This pointer value is called environment-local storage. + This value is NULL unless set with this function. + Agents can allocate memory in which they store environment specific + information. By setting environment-local storage it can then be + accessed with + . +

+ Called by the agent to set the value of the + environment-local storage. supplies to the agent a pointer-size + environment-local storage that can be used to record per-environment + information. + + new + + + + + + + value is set to NULL + + + The value to be entered into the environment-local storage. + + + + + + + + + Get Environment Local Storage + + Called by the agent to get the value of the environment-local + storage. + + new + + + + + + + Pointer through which the value of the environment local + storage is returned. + If environment-local storage has not been set with + returned + pointer is NULL. + + + + + + + + + Get Version Number + + Return the version via version_ptr. + The return value is the version identifier. + The version identifier includes major, minor and micro + version as well as the interface type. + + + Value of JVMTI_VERSION_MASK_INTERFACE_TYPE for JNI. + + + Value of JVMTI_VERSION_MASK_INTERFACE_TYPE for . + + + + + Mask to extract interface type. + The value of the version returned by this function masked with + JVMTI_VERSION_MASK_INTERFACE_TYPE is always + JVMTI_VERSION_INTERFACE_JVMTI + since this is a function. + + + Mask to extract major version number. + + + Mask to extract minor version number. + + + Mask to extract micro version number. + + + + + Shift to extract major version number. + + + Shift to extract minor version number. + + + Shift to extract micro version number. + + + + jvmdi + + + + + + + On return, points to the version. + + + + + + + + + + Get Error Name + + Return the symbolic name for an + error code. +

+ For example + GetErrorName(env, JVMTI_ERROR_NONE, &err_name) + would return in err_name the string + "JVMTI_ERROR_NONE". + + new + + + + + jvmtiError + + The error code. + + + + + + On return, points to the error name. + The name is encoded as a + modified UTF-8 string, + but is restricted to the ASCII subset. + + + + + + + + + Set Verbose Flag + + + + Verbose output other than the below. + + + Verbose garbage collector output, like that specified with -verbose:gc. + + + Verbose class loading output, like that specified with -verbose:class. + + + Verbose JNI output, like that specified with -verbose:jni. + + + Control verbose output. + This is the output which typically is sent to stderr. + + new + + + + + jvmtiVerboseFlag + + Which verbose flag to set. + + + + + + New value of the flag. + + + + + + + + + + Get JLocation Format + + Although the greatest functionality is achieved with location information + referencing the virtual machine bytecode index, the definition of + jlocation has intentionally been left unconstrained to allow VM + implementations that do not have this information. +

+ This function describes the representation of jlocation used in this VM. + If the returned format is , + jlocations can + be used as in indices into the array returned by + . + + + jlocation values represent virtual machine + bytecode indices--that is, offsets into the + virtual machine code for a method. + + + jlocation values represent native machine + program counter values. + + + jlocation values have some other representation. + + + + new + + + + + jvmtiJlocationFormat + + On return, points to the format identifier for jlocation values. + + + + + + + + + + + + + + Every function returns a jvmtiError error code. +

+ It is the responsibility of the agent to call functions with + valid parameters and in the proper context (calling thread is attached, + phase is correct, etc.). + Detecting some error conditions may be difficult, inefficient, or + impossible for an implementation. + The errors listed in + Function Specific Required Errors + must be detected by the implementation. + All other errors represent the recommended response to the error + condition. + + + + + The following errors may be returned by any function + + + + No error has occurred. This is the error code that is returned + on successful completion of the function. + + + Pointer is unexpectedly NULL. + + + The function attempted to allocate memory and no more memory was + available for allocation. + + + The desired functionality has not been enabled in this virtual machine. + + + The thread being used to call this function is not attached + to the virtual machine. Calls must be made from attached threads. + See AttachCurrentThread in the JNI invocation API. + + + The environment provided is no longer connected or is + not an environment. + + + The desired functionality is not available in the current + phase. + Always returned if the virtual machine has completed running. + + + An unexpected internal error has occurred. + + + + + + The following errors are returned by some functions and must + be returned by the implementation when the condition occurs. + + + + Invalid priority. + + + Thread was not suspended. + + + Thread already suspended. + + + This operation requires the thread to be alive--that is, + it must be started and not yet have died. + + + The class has been loaded but not yet prepared. + + + There are no Java programming language or JNI stack frames at the specified depth. + + + Information about the frame is not available (e.g. for native frames). + + + Item already set. + + + Desired element (e.g. field or breakpoint) not found + + + This thread doesn't own the raw monitor. + + + The call has been interrupted before completion. + + + The class cannot be modified. + + + The functionality is not available in this virtual machine. + + + The requested information is not available. + + + The specified event type ID is not recognized. + + + The requested information is not available for native method. + + + The class loader does not support this operation. + + + + + + The following errors are returned by some functions. + They are returned in the event of invalid parameters passed by the + agent or usage in an invalid context. + An implementation is not required to detect these errors. + + + + The passed thread is not a valid thread. + + + Invalid field. + + + Invalid method. + + + Invalid location. + + + Invalid object. + + + Invalid class. + + + The variable is not an appropriate type for the function used. + + + Invalid slot. + + + The capability being used is false in this environment. + + + Thread group invalid. + + + Invalid raw monitor. + + + Illegal argument. + + + The state of the thread has been modified, and is now inconsistent. + + + A new class file has a version number not supported by this VM. + + + A new class file is malformed (the VM would return a ClassFormatError). + + + The new class file definitions would lead to a circular + definition (the VM would return a ClassCircularityError). + + + A new class file would require adding a method. + + + A new class version changes a field. + + + The class bytes fail verification. + + + A direct superclass is different for the new class + version, or the set of directly implemented + interfaces is different. + + + A new class version does not declare a method + declared in the old class version. + + + The class name defined in the new class file is + different from the name in the old class object. + + + A new class version has different modifiers. + + + A method in the new class version has different modifiers + than its counterpart in the old class version. + + + + + + + Agents can be informed of many events that occur in application + programs. +

+ To handle events, designate a set of callback functions with + . + For each event the corresponding callback function will be + called. + Arguments to the callback function provide additional + information about the event. +

+ The callback function is usually called from within an application + thread. The implementation does not + queue events in any way. This means + that event callback functions must be written + carefully. Here are some general guidelines. See + the individual event descriptions for further + suggestions. +

+

    +
  • Any exception thrown during the execution of an event callback can + overwrite any current pending exception in the current application thread. + Care must be taken to preserve a pending exception + when an event callback makes a JNI call that might generate an exception. +
  • +
  • Event callback functions must be re-entrant. The implementation does + not queue events. If an agent needs to process events one at a time, it + can use a raw monitor inside the + event callback functions to serialize event processing. +
  • +
  • Event callback functions that execute JNI's FindClass function to load + classes need to note that FindClass locates the class loader associated + with the current native method. For the purposes of class loading, an + event callback that includes a JNI environment as a parameter to the + callback will treated as if it is a native call, where the native method + is in the class of the event thread's current frame. +
  • +
+

+ Some events identify objects with JNI references. + All references + in events are JNI local references and will become invalid + after the event callback returns. + Unless stated otherwise, memory referenced by pointers sent in event + callbacks may not be referenced after the event callback returns. +

+ Except where stated otherwise, events are delivered on the thread + that caused the event. + Events are sent at the time they occur. + The specification for each event includes the set of + phases in which it can be sent; + if an event triggering activity occurs during another phase, no event + is sent. +

+ A thread that generates an event does not change its execution status + (for example, the event does not cause the thread to be suspended). + If an agent wishes the event to result in suspension, then the agent + is responsible for explicitly suspending the thread with + . +

+ If an event is enabled in multiple environments, the event will be sent + to each agent in the order that the environments were created. + + + + All events are initially disabled. In order to receive any + event: +

    +
  • + If the event requires a capability, that capability must + be added with + . +
  • +
  • + A callback for the event must be set with + . +
  • +
  • + The event must be enabled with + . +
  • +
+
+ + + In many situations it is possible for multiple events to occur + at the same location in one thread. When this happens, all the events + are reported through the event callbacks in the order specified in this section. +

+ If the current location is at the entry point of a method, the + event is reported before + any other event at the current location in the same thread. +

+ If an exception catch has been detected at the current location, + either because it is the beginning of a catch clause or a native method + that cleared a pending exception has returned, the + exceptionCatch event is reported before + any other event at the current location in the same thread. +

+ If a singleStep event or + breakpoint event is triggered at the + current location, the event is defined to occur + immediately before the code at the current location is executed. + These events are reported before any events which are triggered + by the execution of code at the current location in the same + thread (specifically: + exception, + fieldAccess, and + fieldModification). + If both a step and breakpoint event are triggered for the same thread and + location, the step event is reported before the breakpoint event. +

+ If the current location is the exit point of a method (that is, the last + location before returning to the caller), the + event and + the event (if requested) + are reported after all other events at the current location in the same + thread. There is no specified ordering of these two events + with respect to each other. +

+ Co-located events can be triggered during the processing of some other + event by the agent at the same location in the same thread. + If such an event, of type y, is triggered during the processing of + an event of type x, and if x + precedes y in the ordering specified above, the co-located event + y is reported for the current thread and location. If x does not precede + y, y is not reported for the current thread and location. + For example, if a breakpoint is set at the current location + during the processing of , + that breakpoint will be reported before the thread moves off the current + location. +

The following events are never considered to be co-located with + other events. +

    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
+
+ + + The event callback structure below is used to specify the handler function + for events. It is set with the + function. + + + + + Single step events allow the agent to trace thread execution + at the finest granularity allowed by the VM. A single step event is + generated whenever a thread reaches a new location. + Typically, single step events represent the completion of one VM + instruction as defined in the . However, some implementations + may define locations differently. In any case the + method and location + parameters uniquely identify the current location and allow + the mapping to source file and line number when that information is + available. +

+ No single step events are generated from within native methods. + + jvmdi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread about to execution a new instruction + + + + + + Class of the method about to execute a new instruction + + + + + + Method about to execute a new instruction + + + + + + Location of the new instruction + + + + + + + + Breakpoint events are generated whenever a thread reaches a location + designated as a breakpoint with . + The method and location + parameters uniquely identify the current location and allow + the mapping to source file and line number when that information is + available. + + jvmdi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread. + + + + + + Thread that hit the breakpoint + + + + + + Class of the method that hit the breakpoint + + + + + + Method that hit the breakpoint + + + + + + location of the breakpoint + + + + + + + + Field access events are generated whenever a thread accesses + a field that was designated as a watchpoint + with . + The method and location + parameters uniquely identify the current location and allow + the mapping to source file and line number when that information is + available. + + jvmdi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread accessing the field + + + + + + Class of the method where the access is occurring + + + + + + Method where the access is occurring + + + + + + Location where the access is occurring + + + + + + Class of the field being accessed + + + + + + Object with the field being accessed if the field is an + instance field; NULL otherwise + + + + + + Field being accessed + + + + + + + + Field modification events are generated whenever a thread modifies + a field that was designated as a watchpoint + with . + The method and location + parameters uniquely identify the current location and allow + the mapping to source file and line number when that information is + available. + + jvmdi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread modifying the field + + + + + + Class of the method where the modification is occurring + + + + + + Method where the modification is occurring + + + + + + Location where the modification is occurring + + + + + + Class of the field being modified + + + + + + Object with the field being modified if the field is an + instance field; NULL otherwise + + + + + + Field being modified + + + + + + Signature type of the new value + + + + + + The new value + + + + + + + + Frame pop events are generated upon exit from a single method + in a single frame as specified + in a call to . + This is true whether termination is caused by + executing its return instruction + or by throwing an exception to its caller + (see ). + However, frame pops caused by the + function are not reported. +

+ The location reported by + identifies the executable location in the returning method, + immediately prior to the return. + + jvmdi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread that is popping the frame + + + + + + Class of the method being popped + + + + + + Method being popped + + + + + + True if frame was popped by a thrown exception. + False if method exited through its return instruction. + + + + + + + + Method entry events are generated upon entry of Java + programming language methods (including native methods). +

+ The location reported by + identifies the initial executable location in + the method. +

+ Enabling method + entry or exit events will significantly degrade performance on many platforms and is thus + not advised for performance critical usage (such as profiling). + Bytecode instrumentation should be + used in these cases. + + jvmdi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread entering the method + + + + + + Class of the method being entered + + + + + + Method being entered + + + + + + + + Method exit events are generated upon exit from Java + programming language methods (including native methods). + This is true whether termination is caused by + executing its return instruction + or by throwing an exception to its caller + (see ). +

+ The method field uniquely identifies the + method being entered or exited. The frame field provides + access to the stack frame for the method. +

+ The location reported by + identifies the executable location in the returning method + immediately prior to the return. +

+ Enabling method + entry or exit events will significantly degrade performance on many platforms and is thus + not advised for performance critical usage (such as profiling). + Bytecode instrumentation should be + used in these cases. + + jvmdi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread exiting the method + + + + + + Class of the method being exited + + + + + + Method being exited + + + + + + True if frame was popped by a thrown exception. + False if method exited through its return instruction. + + + + + + The return value of the method being exited. + Undefined and should not be used if + + is true. + + + + + + + + A Native Method Bind event is sent when a VM binds a + Java programming language native method + to the address of a function that implements the native method. + This will occur when the native method is called for the first time + and also occurs when the JNI function RegisterNatives is called. + This event allows the bind to be redirected to an agent-specified + proxy function. + This event is not sent when the native method is unbound. + Typically, this proxy function will need to be specific to a + particular method or, to handle the general case, automatically + generated assembly code, since after instrumentation code is + executed the function at the original binding + address will usually be invoked. + The original binding can be restored or the redirection changed + by use of the JNI function RegisterNatives. + Some events may be sent during the primordial phase, JNI and + most of cannot be used at this time but the method and + address can be saved for use later. + + new + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + Will be NULL if sent during the primordial + phase. + + + + + + Thread requesting the bind + + + + + + Class of the method being bound + + + + + + Native method being bound + + + + + + The address the VM is about to bind to--that is, the + address of the implementation of the native method + + + + + + if the referenced address is changed (that is, if + *new_address_ptr is set), the binding + will instead be made to the supplied address. + + + + + + + + Exception events are generated whenever an exception is first detected + in a Java programming language method. + Where "exception" means any java.lang.Throwable. + The exception may have been thrown by a Java programming language or native + method, but in the case of native methods, the event is not generated + until the exception is first seen by a Java programming language method. If an exception is + set and cleared in a native method (and thus is never visible to Java programming language code), + no exception event is generated. +

+ The method and location + parameters uniquely identify the current location + (where the exception was detected) and allow + the mapping to source file and line number when that information is + available. The exception field identifies the thrown + exception object. The catch_method + and catch_location identify the location of the catch clause, + if any, that handles the thrown exception. If there is no such catch clause, + each field is set to 0. There is no guarantee that the thread will ever + reach this catch clause. If there are native methods on the call stack + between the throw location and the catch clause, the exception may + be reset by one of those native methods. + Similarly, exceptions that are reported as uncaught (catch_klass + et al. set to 0) may in fact be caught by native code. + Agents can check for these occurrences by monitoring + events. + Note that finally clauses are implemented as catch and re-throw. Therefore they + will be reported in the catch location. + + jvmdi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread generating the exception + + + + + + Class generating the exception + + + + + + Method generating the exception + + + + + + Location where exception occurred + + + + + + The exception being thrown + + + + + + Class that will catch the exception, or NULL if no known catch + + + + + + Method that will catch the exception, or NULL if no known catch + + + + + + location which will catch the exception or zero if no known catch + + + + + + + + Exception catch events are generated whenever a thrown exception is caught. + Where "exception" means any java.lang.Throwable. + If the exception is caught in a Java programming language method, the event is generated + when the catch clause is reached. If the exception is caught in a native + method, the event is generated as soon as control is returned to a Java programming language + method. Exception catch events are generated for any exception for which + a throw was detected in a Java programming language method. + Note that finally clauses are implemented as catch and re-throw. Therefore they + will generate exception catch events. +

+ The method and location + parameters uniquely identify the current location + and allow the mapping to source file and line number when that information is + available. For exceptions caught in a Java programming language method, the + exception object identifies the exception object. Exceptions + caught in native methods are not necessarily available by the time the + exception catch is reported, so the exception field is set + to NULL. + + jvmdi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread catching the exception + + + + + + Class catching the exception + + + + + + Method catching the exception + + + + + + Location where exception is being caught + + + + + + Exception being caught + + + + + + + + Thread start events are generated by a new thread before its initial + method executes. +

+ A thread may be listed in the array returned by + + before its thread start event is generated. + It is possible for other events to be generated + on a thread before its thread start event. +

+ The event is sent on the newly started . + + jvmdi + + + + + + JNIEnv + + + The JNI environment of the event (current) thread. + + + + + + Thread starting + + + + + + + + Thread end events are generated by a terminating thread + after its initial method has finished execution. +

+ A thread may be listed in the array returned by + + after its thread end event is generated. + No events are generated on a thread + after its thread end event. +

+ The event is sent on the dying . + + jvmdi + + + + + + JNIEnv + + + The JNI environment of the event (current) thread. + + + + + + Thread ending + + + + + + + + A class load event is generated when a class is first loaded. The order + of class load events generated by a particular thread are guaranteed + to match the order of class loading within that thread. + Array class creation does not generate a class load event. + The creation of a primitive class (for example, java.lang.Integer.TYPE) + does not generate a class load event. +

+ This event is sent at an early stage in loading the class. As + a result the class should be used carefully. Note, for example, + that methods and fields are not yet loaded, so queries for methods, + fields, subclasses, and so on will not give correct results. + See "Loading of Classes and Interfaces" in the Java Language + Specification. For most + purposes the event will + be more useful. + + jvmdi + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread loading the class + + + + + + Class being loaded + + + + + + + + + A class unload event is generated when the class is about to be unloaded. + Class unload events take place during garbage collection and must be + handled extremely carefully. The garbage collector holds many locks + and has suspended all other threads, so the event handler cannot depend + on the ability to acquire any locks. The class unload event handler should + do as little as possible, perhaps by queuing information to be processed + later. In particular, the jclass should be used only in + the JNI function isSameObject or in the following functions: +

    +
  • +
  • +
  • +
  • +
+
+ jvmdi + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread generating the class unload + + + + + + Class being unloaded + + + +
+ + + + + A class prepare event is generated when class preparation is complete. + At this point, class fields, methods, and implemented interfaces are + available, and no code from the class has been executed. Since array + classes never have fields or methods, class prepare events are not + generated for them. Class prepare events are not generated for + primitive classes (for example, java.lang.Integer.TYPE). + + jvmdi + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread generating the class prepare + + + + + + Class being prepared + + + + + + + + This event is sent when the VM obtains class file data, + but before it constructs + the in-memory representation for that class. + This event is also sent when the class is being modified by the + function or + the function, + called in any environment. + The agent can instrument + the existing class file data sent by the VM to include profiling/debugging hooks. + See the description of + bytecode instrumentation + for usage information. +

+ This event may be sent before the VM is initialized (the primordial + phase). During this time + no VM resources should be created. Some classes might not be compatible + with the function (eg. ROMized classes) and this event will not be + generated for these classes. +

+ The agent must allocate the space for the modified + class file data buffer + using the memory allocation function + because the + VM is responsible for freeing the new class file data buffer + using . + Note that + is permitted during the primordial phase. +

+ If the agent wishes to modify the class file, it must set + new_class_data to point + to the newly instrumented class file data buffer and set + new_class_data_len to the length of that + buffer before returning + from this call. If no modification is desired, the agent simply + does not set new_class_data. If multiple agents + have enabled this event the results are chained. That is, if + new_class_data has been set, it becomes the + class_data for the next agent. +

+ The order that this event is sent to each environment differs + from other events. + This event is sent to environments in the following order: +

    +
  • retransformation + incapable + environments, in the + order in which they were created +
  • +
  • retransformation + capable + environments, in the + order in which they were created +
  • +
+ When triggered by , + this event is sent only to retransformation + capable + environments. +
+ jvmpi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread. + Will be NULL if sent during the primordial + phase. + + + + + + The class being + redefined or + retransformed. + NULL if sent by class load. + + + + + + The class loader loading the class. + NULL if the bootstrap class loader. + + + + + + Name of class being loaded as a VM internal qualified name + (for example, "java/util/List"), encoded as a + modified UTF-8 string. + Note: if the class is defined with a NULL name or + without a name specified, name will be NULL. + + + + + + The ProtectionDomain of the class. + + + + + + Length of current class file data buffer. + + + + + + Pointer to the current class file data buffer. + + + + + + Pointer to the length of the new class file data buffer. + + + + + + Pointer to the pointer to the instrumented class file data buffer. + + + +
+ + + + The VM initialization event signals the start of the VM. + At this time JNI is live but the VM is not yet fully initialized. + Once this event is generated, the agent is free to call any JNI function. + This event signals the beginning of the start phase, + functions permitted in the start phase may be called. +

+ In the case of VM start-up failure, this event will not be sent. + + jvmdi + + + + + + JNIEnv + + + The JNI environment of the event (current) thread. + + + + + + + + The VM initialization event signals the completion of VM initialization. Once + this event is generated, the agent is free to call any JNI or + function. The VM initialization event can be preceded by or can be concurrent + with other events, but + the preceding events should be handled carefully, if at all, because the + VM has not completed its initialization. The thread start event for the + main application thread is guaranteed not to occur until after the + handler for the VM initialization event returns. +

+ In the case of VM start-up failure, this event will not be sent. + + jvmdi + + + + + + JNIEnv + + + The JNI environment of the event (current) thread. + + + + + + The initial thread + + + + + + + + The VM death event notifies the agent of the termination of the VM. + No events will occur after the VMDeath event. +

+ In the case of VM start-up failure, this event will not be sent. + Note that Agent_OnUnload + will still be called in these cases. + + jvmdi + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + + + Sent when a method is compiled and loaded into memory by the VM. + If it is unloaded, the event is sent. + If it is moved, the event is sent, + followed by a new CompiledMethodLoad event. + Note that a single method may have multiple compiled forms, and that + this event will be sent for each form. + Note also that several methods may be inlined into a single + address range, and that this event will be sent for each method. +

+ These events can be sent after their initial occurrence with + . + + jvmpi + + + + + Starting native address of code corresponding to a location + + + + + + Corresponding location. See + + for the meaning of location. + + + + + + + + + + + Class of the method being compiled and loaded + + + + + + Method being compiled and loaded + + + + + + Size of compiled code + + + + + + Address where compiled method code is loaded + + + + + + Number of + entries in the address map. + Zero if mapping information cannot be supplied. + + + + jvmtiAddrLocationMap + + Map from native addresses to location. + The native address range of each entry is from + + to start_address-1 of the next entry. + NULL if mapping information cannot be supplied. + + + + + + VM-specific compilation information. + The referenced compile information is managed by the VM + and must not depend on the agent for collection. + A VM implementation defines the content and lifetime + of the information. + + + + + + + + Sent when a compiled method is unloaded from memory. + This event might not be sent on the thread which performed the unload. + This event may be sent sometime after the unload occurs, but + will be sent before the memory is reused + by a newly generated compiled method. This event may be sent after + the class is unloaded. + + jvmpi + + + + + + + + Class of the compiled method being unloaded. + + + + + + Compiled method being unloaded. + For identification of the compiled method only -- the class + may be unloaded and therefore the method should not be used + as an argument to further JNI or functions. + + + + + + Address where compiled method code was loaded. + For identification of the compiled method only -- + the space may have been reclaimed. + + + + + + + + Sent when a component of the virtual machine is generated dynamically. + This does not correspond to Java programming language code that is + compiled--see . + This is for native code--for example, an interpreter that is generated + differently depending on command-line options. +

+ Note that this event has no controlling capability. + If a VM cannot generate these events, it simply does not send any. +

+ These events can be sent after their initial occurrence with + . + + jvmpi + + + + + + + Name of the code, encoded as a + modified UTF-8 string. + Intended for display to an end-user. + The name might not be unique. + + + + + + Native address of the code + + + + + + Length in bytes of the code + + + + + + + + Sent by the VM to request the agent to dump its data. This + is just a hint and the agent need not react to this event. + This is useful for processing command-line signals from users. For + example, in the Java 2 SDK a CTRL-Break on Win32 and a CTRL-\ on Solaris + causes the VM to send this event to the agent. + + jvmpi + + + + + + + + + Sent when a thread is attempting to enter a Java programming language + monitor already acquired by another thread. + + jvmpi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + JNI local reference to the thread + attempting to enter the monitor + + + + + + JNI local reference to the monitor + + + + + + + + Sent when a thread enters a Java programming language + monitor after waiting for it to be released by another thread. + + jvmpi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + JNI local reference to the thread entering + the monitor + + + + + + JNI local reference to the monitor + + + + + + + + Sent when a thread is about to wait on an object. + + jvmpi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + JNI local reference to the thread about to wait + + + + + + JNI local reference to the monitor + + + + + + The number of milliseconds the thread will wait + + + + + + + + Sent when a thread finishes waiting on an object. + + jvmpi + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + JNI local reference to the thread that was finished waiting + + + + + + JNI local reference to the monitor. + + + + + + True if the monitor timed out + + + + + + + + Sent when a VM resource needed by a running application has been exhausted. + Except as required by the optional capabilities, the set of resources + which report exhaustion is implementation dependent. +

+ The following bit flags define the properties of the resource exhaustion: + + + After this event returns, the VM will throw a + java.lang.OutOfMemoryError. + + + The VM was unable to allocate memory from the Java + platform heap. + The heap is the runtime + data area from which memory for all class instances and + arrays are allocated. + + + The VM was unable to create a thread. + + + + new + + + Can generate events when the VM is unable to allocate memory from the + heap. + + + Can generate events when the VM is unable to + create + a thread. + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Flags defining the properties of the of resource exhaustion + as specified by the + Resource + Exhaustion Flags. + + + + + + Reserved. + + + + + + Description of the resource exhaustion, encoded as a + modified UTF-8 string. + + + + + + + + Sent when a method causes the virtual machine to allocate an + Object visible to Java programming language code and the + allocation is not detectable by other intrumentation mechanisms. + Generally object allocation should be detected by instrumenting + the bytecodes of allocating methods. + Object allocation generated in native code by JNI function + calls should be detected using + JNI function interception. + Some methods might not have associated bytecodes and are not + native methods, they instead are executed directly by the + VM. These methods should send this event. + Virtual machines which are incapable of bytecode instrumentation + for some or all of their methods can send this event. +

+ Typical examples where this event might be sent: +

    +
  • Reflection -- for example, java.lang.Class.newInstance()
  • +
  • Methods not represented by bytecodes -- for example, VM intrinsics and + J2ME preloaded classes
  • +
+ Cases where this event would not be generated: +
    +
  • Allocation due to bytecodes -- for example, the new + and newarray VM instructions
  • +
  • Allocation due to JNI function calls -- for example, + AllocObject
  • +
  • Allocations during VM initialization
  • +
  • VM internal objects
  • +
+
+ new + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread + + + + + + Thread allocating the object. + + + + + + JNI local reference to the object that was allocated + + + + + + JNI local reference to the class of the object + + + + + + Size of the object (in bytes). See . + + + +
+ + + + An Object Free event is sent when the garbage collector frees an object. + Events are only sent for tagged objects--see + heap functions. +

+ The event handler must not use JNI functions and + must not use functions except those which + specifically allow such use (see the raw monitor, memory management, + and environment local storage functions). + + new + + + + + + + + The freed object's tag + + + + + + + + A Garbage Collection Start event is sent when a full cycle + garbage collection begins. + Only stop-the-world collections are reported--that is, collections during + which all threads cease to modify the state of the Java virtual machine. + This means that some collectors will never generate these events. + This event is sent while the VM is still stopped, thus + the event handler must not use JNI functions and + must not use functions except those which + specifically allow such use (see the raw monitor, memory management, + and environment local storage functions). +

+ This event is always sent as a matched pair with + + (assuming both events are enabled) and no garbage collection + events will occur between them. + + new + + + + + + + + + + A Garbage Collection Finish event is sent when a full + garbage collection cycle ends. + This event is sent while the VM is still stopped, thus + the event handler must not use JNI functions and + must not use functions except those which + specifically allow such use (see the raw monitor, memory management, + and environment local storage functions). +

+ Some agents may need to do post garbage collection operations that + require the use of the disallowed or JNI functions. For these + cases an agent thread can be created which waits on a raw monitor, + and the handler for the Garbage Collection Finish event simply + notifies the raw monitor +

+ This event is always sent as a matched pair with + (assuming both events are enabled). + + The most important use of this event is to provide timing information, + and thus additional information is not required. However, + information about the collection which is "free" should be included - + what that information is needs to be determined. + + + new + + + + + + + + + + + Send verbose messages as strings. + + This format is extremely fragile, as it can change with each + platform, collector and version. Alternatives include: +

    +
  • building off Java programming language M and M APIs
  • +
  • XML
  • +
  • key/value pairs
  • +
  • removing it
  • +
+ + + Though this seemed trivial to implement. + In the RI it appears this will be quite complex. + +
+ new + + + + + jvmtiVerboseFlag + + Which verbose output is being sent. + + + + + + Message text, encoded as a + modified UTF-8 string. + + + +
+ + + + + + + extends the data types defined by JNI. + + + + + Holds a Java programming language boolean. + Unsigned 8 bits. + + + + + Holds a Java programming language int. + Signed 32 bits. + + + + + Holds a Java programming language long. + Signed 64 bits. + + + + + Holds a Java programming language float. + 32 bits. + + + + + Holds a Java programming language double. + 64 bits. + + + + + Holds a Java programming language object. + + + + + Holds a Java programming language class. + + + + + Is a union of all primitive types and jobject. Thus, holds any Java + programming language value. + + + + + Identifies a Java programming language field. + jfieldIDs returned by functions and events may be + safely stored. + + + + + Identifies a Java programming language method, initializer, or constructor. + jmethodIDs returned by functions and events may be + safely stored. However, if the class is unloaded, they become invalid + and must not be used. + + + + + Pointer to the JNI function table. Pointer to this (JNIEnv *) + is a JNI environment. + + + + + + + + The environment pointer. + See the Function Section. + jvmtiEnv points to the + function table pointer. + + + + typedef jobject jthread; + + Subtype of that holds a thread. + + + + typedef jobject jthreadGroup; + + Subtype of that holds a thread group. + + + + typedef jlong jlocation; + + A 64 bit value, representing a monotonically increasing + executable position within a method. + -1 indicates a native method. + See for the format on a + given VM. + + + + struct _jrawMonitorID; +typedef struct _jrawMonitorID *jrawMonitorID; + + A raw monitor. + + + + + Holds an error return code. + See the Error section for possible values. + +typedef enum { + JVMTI_ERROR_NONE = 0, + JVMTI_ERROR_INVALID_THREAD = 10, + ... +} jvmtiError; + + + + + + An identifier for an event type. + See the Event section for possible values. + It is guaranteed that future versions of this specification will + never assign zero as an event type identifier. + +typedef enum { + JVMTI_EVENT_SINGLE_STEP = 1, + JVMTI_EVENT_BREAKPOINT = 2, + ... +} jvmtiEvent; + + + + + + The callbacks used for events. + +typedef struct { + jvmtiEventVMInit VMInit; + jvmtiEventVMDeath VMDeath; + ... +} jvmtiEventCallbacks; + + See event callbacks + for the complete structure. +

+ Where, for example, the VM initialization callback is defined: + +typedef void (JNICALL *jvmtiEventVMInit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + + See the individual events for the callback function definition. + + + + typedef struct JNINativeInterface_ jniNativeInterface; + + Typedef for the JNI function table JNINativeInterface + defined in the + JNI Specification. + The JNI reference implementation defines this with an underscore. + + + + + + + + + JVMDI requires that the agent suspend threads before calling + certain sensitive functions. JVMPI requires garbage collection to be + disabled before calling certain sensitive functions. + It was suggested that rather than have this requirement, that + VM place itself in a suitable state before performing an + operation. This makes considerable sense since each VM + knows its requirements and can most easily arrange a + safe state. +

+ The ability to externally suspend/resume threads will, of + course, remain. The ability to enable/disable garbage collection will not. +

+ This issue is resolved--suspend will not + be required. The spec has been updated to reflect this. + + + + There are a variety of approaches to sampling call stacks. + The biggest bifurcation is between VM controlled and agent + controlled. +

+ This issue is resolved--agent controlled + sampling will be the approach. + + + + JVMDI represents threads as jthread. JVMPI primarily + uses JNIEnv* to represent threads. +

+ The Expert Group has chosen jthread as the representation + for threads in . + JNIEnv* is sent by + events since it is needed to JNI functions. JNIEnv, per the + JNI spec, are not supposed to be used outside their thread. + + + + The JNI spec allows an implementation to depend on jclass/jmethodID + pairs, rather than simply a jmethodID, to reference a method. + JVMDI, for consistency, choose the same representation. + JVMPI, however, specifies that a jmethodID alone maps to a + method. Both of the Sun J2SE virtual machines (Classic and HotSpot) store + pointers in jmethodIDs, and as a result, a jmethodID is sufficient. + In fact, any JVM implementation that supports JVMPI must have + such a representation. + will use jmethodID as a unique representation of a method + (no jclass is used). + There should be efficiency gains, particularly in + functionality like stack dumping, to this representation. +

+ Note that fields were not used in JVMPI and that the access profile + of fields differs from methods--for implementation efficiency + reasons, a jclass/jfieldID pair will still be needed for field + reference. + + + + Functions return local references. + + + + In JVMDI, a frame ID is used to represent a frame. Problem with this + is that a VM must track when a frame becomes invalid, a far better + approach, and the one used in , is to reference frames by depth. + + + + Currently, having a required capabilities means that the functionality + is optional. Capabilities are useful even for required functionality + since they can inform the VM is needed set-up. Thus, there should be + a set of capabilities that a conformant implementation must provide + (if requested during Agent_OnLoad). + + + + A hint of the percentage of objects that will be tagged would + help the VM pick a good implementation. + + + + How difficult or easy would be to extend the monitor_info category to include +

+  - current number of monitors 
+  - enumeration of monitors 
+  - enumeration of threads waiting on a given monitor 
+    
+ The reason for my question is the fact that current get_monitor_info support + requires the agent to specify a given thread to get the info which is probably + OK in the profiling/debugging space, while in the monitoring space the agent + could be watching the monitor list and then decide which thread to ask for + the info. You might ask why is this important for monitoring .... I think it + can aid in the detection/prediction of application contention caused by hot-locks. + + + + + + The specification is an evolving document with major, minor, + and micro version numbers. + A released version of the specification is uniquely identified + by its major and minor version. + The functions, events, and capabilities in this specification + indicate a "Since" value which is the major and minor version in + which it was introduced. + The version of the specification implemented by the VM can + be retrieved at runtime with the + function. + + + Converted to XML document. + + + Elided heap dump functions (for now) since what was there + was wrong. + + + Added detail throughout. + + + Changed JVMTI_THREAD_STATUS_RUNNING to JVMTI_THREAD_STATUS_RUNNABLE. + + + Added AsyncGetStackTrace. + + + Added jframeID return to GetStackTrace. + + + Elided GetCurrentFrame and GetCallingFrame functions (for now) since what was there + since they are redundant with GetStackTrace. + + + Elided ClearAllBreakpoints since it has always been redundant. + + + Added GetSystemProperties. + + + Changed the thread local storage functions to use jthread. + + + Added GetJLocationFormat. + + + Added events and introductory text. + + + Cross reference type and constant definitions. + + + Added DTD. + + + Added capabilities function section. + + + Assign capabilities to each function and event. + + + Add JNI interception functions. + + + Auto generate SetEventNotificationMode capabilities. + + + Add event. + + + Add event. + + + Add const to declarations. + + + Change method exit and frame pop to send on exception. + + + Add ForceGarbageCollection. + + + Redo Xrun section; clarify GetStackTrace and add example; + Fix width problems; use "agent" consistently. + + + Remove previous start-up intro. + Add Environments + section. + + + Add . + + + Numerous minor updates. + + + Add heap profiling functions added: + get/set annotation, iterate live objects/heap. + Add heap profiling functions place holder added: + heap roots. + Heap profiling event added: object free. + Heap profiling event redesigned: vm object allocation. + Heap profiling event placeholders added: garbage collection start/finish. + Native method bind event added. + + + Revamp suspend/resume functions. + Add origin information with jvmdi tag. + Misc fixes. + + + Add semantics to types. + + + Add local reference section. + Autogenerate parameter descriptions from types. + + + Document that RunAgentThread sends threadStart. + + + Remove redundant local ref and dealloc warning. + Convert GetRawMonitorName to allocated buffer. + Add GenerateEvents. + + + Make raw monitors a type and rename to "jrawMonitorID". + + + Include origin information. + Clean-up JVMDI issue references. + Remove Deallocate warnings which are now automatically generated. + + + Fix representation issues for jthread. + + + Make capabilities buffered out to 64 bits - and do it automatically. + + + Make constants which are enumeration into enum types. + Parameters now of enum type. + Clean-up and index type section. + Replace remaining datadef entities with callback. + + + Correct GenerateEvents description. + More internal semantics work. + + + Replace previous GetSystemProperties with two functions + which use allocated information instead fixed. + Add SetSystemProperty. + More internal semantics work. + + + Add varargs to end of SetEventNotificationMode. + + + Finish fixing spec to reflect that alloc sizes are jlong. + + + Allow NULL as RunAgentThread arg. + + + Fixed names to standardized naming convention + Removed AsyncGetStackTrace. + + + Since we are using jthread, removed GetThread. + + + Change GetFieldName to allow NULLs like GetMethodName. + + + Rewrite the introductory text, adding sections on + start-up, environments and bytecode instrumentation. + Change the command line arguments per EG discussions. + Add an introduction to the capabilities section. + Add the extension mechanism category and functions. + Mark for deletion, but clarified anyhow, SuspendAllThreads. + Rename IterateOverLiveObjects to IterateOverReachableObjects and + change the text accordingly. + Clarify IterateOverHeap. + Clarify CompiledMethodLoad. + Discuss prerequisite state for Calling Functions. + Clarify SetAllocationHooks. + Added issues ("To be resolved:") through-out. + And so on... + + + Remove struct from the call to GetOwnedMonitorInfo. + Automatically generate most error documentation, remove + (rather broken) hand written error doc. + Better describe capability use (empty initial set). + Add min value to jint params. + Remove the capability can_access_thread_local_storage. + Rename error JVMTI_ERROR_NOT_IMPLEMENTED to JVMTI_ERROR_MUST_POSSESS_CAPABILITY; + same for *NOT_IMPLEMENTED. + Description fixes. + + + Rename GetClassSignature to GetClassName. + Rename IterateOverClassObjects to IterateOverInstancesOfClass. + Remove GetMaxStack (operand stack isn't used in ). + Description fixes: define launch-time, remove native frame pop + from PopFrame, and assorted clarifications. + + + Fix minor editing problem. + + + Add phase information. + Remap (compact) event numbers. + + + More phase information - allow "any". + Elide raw monitor queries and events. + Minor description fixes. + + + Add GetPhase. + Use "phase" through document. + Elide GetRawMonitorName. + Elide GetObjectMonitors. + + + Fixes from link, XML, and spell checking. + Auto-generate the callback structure. + + + One character XML fix. + + + Change function parameter names to be consistent with + event parameters (fooBarBaz becomes foo_bar_baz). + + + Fix broken link. Fix thread markers. + + + Change constants so they are under 128 to workaround + compiler problems. + + + Overhaul capabilities. Separate GetStackTrace into + GetStackTrace and GetStackFrames. + + + Use depth instead of jframeID to reference frames. + Remove the now irrelevant GetCurrentFrame, GetCallerFrame and GetStackFrames. + Remove frame arg from events. + + + Remove GetObjectWithAnnotation since tests show bufferred approach more efficient. + Add missing annotation_count to GetObjectsWithAnnotations + + + Remove confusing parenthetical statement in GetObjectsWithAnnotations + + + Replace jclass/jmethodID representation of method with simply jmethodID; + Pass JvmtiEnv* as first arg of every event; remove JNIEnv* where inappropriate. + Replace can_access_frames with can_access_local_variables; remove from purely stack access. + Use can_get_synthetic_attribute; fix description. + Clarify that zero length arrays must be deallocated. + Clarify RelinquishCapabilities. + Generalize JVMTI_ERROR_VM_DEAD to JVMTI_ERROR_WRONG_PHASE. + + + Remove lingering indirect references to OBSOLETE_METHOD_ID. + + + Allow DestroyRawMonitor during OnLoad. + + + Added not monitor owner error return to DestroyRawMonitor. + + + Clarify semantics of raw monitors. + Change flags on GetThreadStatus. + GetClassLoader return NULL for the bootstrap class loader. + Add GetClassName issue. + Define local variable signature. + Disallow zero in annotations array of GetObjectsWithAnnotations. + Remove over specification in GetObjectsWithAnnotations. + Elide SetAllocationHooks. + Elide SuspendAllThreads. + + + Define the data type jvmtiEventCallbacks. + Zero length allocations return NULL. + Keep SetAllocationHooks in JVMDI, but remove from . + Add JVMTI_THREAD_STATUS_FLAG_INTERRUPTED. + + + Better wording, per review. + + + First Alpha. + Make jmethodID and jfieldID unique, jclass not used. + + + Fix minor XSLT errors. + + + Undo making jfieldID unique (jmethodID still is). + + + Changes per June 11th Expert Group meeting -- + Overhaul Heap functionality: single callback, + remove GetHeapRoots, add reachable iterators, + and rename "annotation" to "tag". + NULL thread parameter on most functions is current + thread. + Add timers. + Remove ForceExit. + Add GetEnvironmentLocalStorage. + Add verbose flag and event. + Add AddToBootstrapClassLoaderSearch. + Update ClassFileLoadHook. + + + Clean up issues sections. + Rename GetClassName back to GetClassSignature and + fix description. + Add generic signature to GetClassSignature, + GetFieldSignature, GetMethodSignature, and + GetLocalVariableTable. + Elide EstimateCostOfCapabilities. + Clarify that the system property functions operate + on the VM view of system properties. + Clarify Agent_OnLoad. + Remove "const" from JNIEnv* in events. + Add metadata accessors. + + + Add start_depth to GetStackTrace. + Move system properties to a new category. + Add GetObjectSize. + Remove "X" from command line flags. + XML, HTML, and spell check corrections. + + + Fix JVMTI_HEAP_ROOT_THREAD to be 6. + Make each synopsis match the function name. + Fix unclear wording. + + + SetThreadLocalStorage and SetEnvironmentLocalStorage should allow value + to be set to NULL. + NotifyFramePop, GetFrameLocationm and all the local variable operations + needed to have their wording about frames fixed. + Grammar and clarity need to be fixed throughout. + Capitalization and puntuation need to be consistent. + Need micro version number and masks for accessing major, minor, and micro. + The error code lists should indicate which must be returned by + an implementation. + The command line properties should be visible in the properties functions. + Disallow popping from the current thread. + Allow implementations to return opaque frame error when they cannot pop. + The NativeMethodBind event should be sent during any phase. + The DynamicCodeGenerated event should be sent during any phase. + The following functions should be allowed to operate before VMInit: + Set/GetEnvironmentLocalStorage + GetMethodDeclaringClass + GetClassSignature + GetClassModifiers + IsInterface + IsArrayClass + GetMethodName + GetMethodModifiers + GetMaxLocals + GetArgumentsSize + GetLineNumberTable + GetMethodLocation + IsMethodNative + IsMethodSynthetic. + Other changes (to XSL): + Argument description should show asterisk after not before pointers. + NotifyFramePop, GetFrameLocationm and all the local variable operations + should hsve the NO_MORE_FRAMES error added. + Not alive threads should have a different error return than invalid thread. + + + VerboseOutput event was missing message parameter. + Minor fix-ups. + + + Technical Publications Department corrections. + Allow thread and environment local storage to be set to NULL. + + + Use new Agent_OnLoad rather than overloaded JVM_OnLoad. + Add JNICALL to callbacks (XSL). + Document JNICALL requirement for both events and callbacks (XSL). + Restrict RedefineClasses to methods and attributes. + Elide the VerboseOutput event. + VMObjectAlloc: restrict when event is sent and remove method parameter. + Finish loose ends from Tech Pubs edit. + + + Change ClassFileLoadHook event to send the class instead of a boolean of redefine. + + + XML fixes. + Minor text clarifications and corrections. + + + Remove GetExceptionHandlerTable and GetThrownExceptions from . + Clarify that stack frames are JVM Spec frames. + Split can_get_source_info into can_get_source_file_name, can_get_line_numbers, + and can_get_source_debug_extension. + PopFrame cannot have a native calling method. + Removed incorrect statement in GetClassloaderClasses + (see http://java.sun.com/docs/books/vmspec/2nd-edition/html/ConstantPool.doc.html#79383). + + + XML and text fixes. + Move stack frame description into Stack Frame category. + + + Allow NULL (means bootstrap loader) for GetClassloaderClasses. + Add new heap reference kinds for references from classes. + Add timer information struct and query functions. + Add AvailableProcessors. + Rename GetOtherThreadCpuTime to GetThreadCpuTime. + Explicitly add JVMTI_ERROR_INVALID_THREAD and JVMTI_ERROR_THREAD_NOT_ALIVE + to SetEventNotification mode. + Add initial thread to the VM_INIT event. + Remove platform assumptions from AddToBootstrapClassLoaderSearch. + + + Grammar and clarity changes per review. + + + More grammar and clarity changes per review. + Add Agent_OnUnload. + + + Change return type of Agent_OnUnload to void. + + + Rename JVMTI_REFERENCE_ARRAY to JVMTI_REFERENCE_ARRAY_ELEMENT. + + + Steal java.lang.Runtime.availableProcessors() wording for + AvailableProcessors(). + Guarantee that zero will never be an event ID. + Remove some issues which are no longer issues. + Per review, rename and more completely document the timer + information functions. + + + Non-spec visible change to XML controlled implementation: + SetThreadLocalStorage must run in VM mode. + + + Add GetErrorName. + Add varargs warning to jvmtiExtensionEvent. + Remove "const" on the jvmtiEnv* of jvmtiExtensionEvent. + Remove unused can_get_exception_info capability. + Pass jvmtiEnv* and JNIEnv* to the jvmtiStartFunction. + Fix jvmtiExtensionFunctionInfo.func declared type. + Extension function returns error code. + Use new version numbering. + + + Remove the ClassUnload event. + + + Heap reference iterator callbacks return an enum that + allows outgoing object references to be ignored. + Allow JNIEnv as a param type to extension events/functions. + + + Fix a typo. + + + Remove all metadata functions: GetClassMetadata, + GetFieldMetadata, and GetMethodMetadata. + + + Mark the functions Allocate. Deallocate, RawMonitor*, + SetEnvironmentLocalStorage, and GetEnvironmentLocalStorage + as safe for use in heap callbacks and GC events. + + + Add pass through opaque user data pointer to heap iterate + functions and callbacks. + In the CompiledMethodUnload event, send the code address. + Add GarbageCollectionOccurred event. + Add constant pool reference kind. + Mark the functions CreateRawMonitor and DestroyRawMonitor + as safe for use in heap callbacks and GC events. + Clarify: VMDeath, GetCurrentThreadCpuTimerInfo, + GetThreadCpuTimerInfo, IterateOverReachableObjects, + IterateOverObjectsReachableFromObject, GetTime and + JVMTI_ERROR_NULL_POINTER. + Add missing errors to: GenerateEvents and + AddToBootstrapClassLoaderSearch. + Fix description of ClassFileLoadHook name parameter. + In heap callbacks and GC/ObjectFree events, specify + that only explicitly allowed functions can be called. + Allow GetCurrentThreadCpuTimerInfo, GetCurrentThreadCpuTime, + GetTimerInfo, and GetTime during callback. + Allow calling SetTag/GetTag during the onload phase. + SetEventNotificationMode, add: error attempted inappropriate + thread level control. + Remove jvmtiExceptionHandlerEntry. + Fix handling of native methods on the stack -- + location_ptr param of GetFrameLocation, remove + JVMTI_ERROR_OPAQUE_FRAME from GetFrameLocation, + jvmtiFrameInfo.location, and jlocation. + Remove typo (from JVMPI) implying that the MonitorWaited + event is sent on sleep. + + + Clarifications and typos. + + + Allow NULL user_data in heap iterators. + + + Add GetThreadState, deprecate GetThreadStatus. + + + INVALID_SLOT and TYPE_MISMATCH errors should be optional. + + + Remove MonitorContendedExit. + Added JNIEnv parameter to VMObjectAlloc. + Clarified definition of class_tag and referrer_index + parameters to heap callbacks. + + + Document JAVA_TOOL_OPTIONS. + + + Divide start phase into primordial and start. + Add VMStart event + Change phase associations of functions and events. + + + Elide deprecated GetThreadStatus. + Bump minor version, subtract 100 from micro version + + + Document that timer nanosecond values are unsigned. + Clarify text having to do with native methods. + + + Fix typos. + Remove elided deprecated GetThreadStatus. + + + Require NotifyFramePop to act on suspended threads. + + + Add capabilities + (can_redefine_any_class + and + can_generate_all_class_hook_events) + and an error () + which allow some classes to be unmodifiable. + + + Add JVMTI_ERROR_MUST_POSSESS_CAPABILITY to SetEventNotificationMode. + + + Clarified CompiledMethodUnload so that it is clear the event + may be posted after the class has been unloaded. + + + Change the size parameter of VMObjectAlloc to jlong to match GetObjectSize. + + + Added guideline for the use of the JNI FindClass function in event + callback functions. + + + Add GetAllStackTraces and GetThreadListStackTraces. + + + ClassLoad and ClassPrepare events can be posted during start phase. + + + Add JVMTI_ERROR_NATIVE_METHOD to GetLineNumberTable, GetLocalVariableTable, + GetMaxLocals, GetArgumentsSize, GetMethodLocation, GetBytecodes. + + + Return the timer kind in the timer information structure. + + + Spec clarifications: + JVMTI_THREAD_STATE_IN_NATIVE might not include JNI or . + ForceGarbageCollection does not run finalizers. + The context of the specification is the Java platform. + Warn about early instrumentation. + + + Refinements to the above clarifications and + Clarify that an error returned by Agent_OnLoad terminates the VM. + + + Array class creation does not generate a class load event. + + + Align thread state hierarchy more closely with java.lang.Thread.State. + + + Clarify the documentation of thread state. + + + Remove GarbageCollectionOccurred event -- can be done by agent. + + + Define "command-line option". + + + Describe the intended use of bytecode instrumentation. + Fix description of extension event first parameter. + + + Clarification and typos. + + + Remove DataDumpRequest event. + + + Clarify RawMonitorWait with zero timeout. + Clarify thread state after RunAgentThread. + + + Clean-up: fix bad/old links, etc. + + + Clarifications including: + All character strings are modified UTF-8. + Agent thread visibiity. + Meaning of obsolete method version. + Thread invoking heap callbacks, + + + Bump major.minor version numbers to "1.0". + + + Clarify interaction between ForceGarbageCollection + and ObjectFree. + + + Restrict AddToBootstrapClassLoaderSearch and + SetSystemProperty to the OnLoad phase only. + + + Fix typo in SetTag. + + + Fix trademarks. + Add missing parameter in example GetThreadState usage. + + + Copyright updates. + + + Add missing function table layout. + Add missing description of C++ member function format of functions. + Clarify that name in CFLH can be NULL. + Released as part of J2SE 5.0. + + + Bump major.minor version numbers to "1.1". + Add ForceEarlyReturn* functions. + Add GetOwnedMonitorStackDepthInfo function. + Add GetCurrentThread function. + Add "since" version marker. + Add AddToSystemClassLoaderSearch. + Allow AddToBootstrapClassLoaderSearch be used in live phase. + Fix historic rubbish in the descriptions of the heap_object_callback + parameter of IterateOverHeap and IterateOverInstancesOfClass functions; + disallow NULL for this parameter. + Clarify, correct and make consistent: wording about current thread, + opaque frames and insufficient number of frames in PopFrame. + Consistently use "current frame" rather than "topmost". + Clarify the JVMTI_ERROR_TYPE_MISMATCH errors in GetLocal* and SetLocal* + by making them compatible with those in ForceEarlyReturn*. + Many other clarifications and wording clean ups. + + + Add GetConstantPool. + Switch references to the first edition of the VM Spec, to the seconds edition. + + + Clarify minor/major version order in GetConstantPool. + + + Add SetNativeMethodPrefix and SetNativeMethodPrefixes. + Reassign GetOwnedMonitorStackDepthInfo to position 153. + Break out Class Loader Search in its own documentation category. + Deal with overly long lines in XML source. + + + Allow agents be started in the live phase. + Added paragraph about deploying agents. + + + Add specification description to SetNativeMethodPrefix(es). + Better define the conditions on GetConstantPool. + + + Break out the GetClassVersionNumber function from GetConstantPool. + Clean-up the references to the VM Spec. + + + Allow SetNativeMethodPrefix(es) in any phase. + Add clarifications about the impact of redefinition on GetConstantPool. + + + Various clarifications to SetNativeMethodPrefix(es). + + + Add missing performance warning to the method entry event. + + + Remove internal JVMDI support. + + + Add . + Revamp the bytecode instrumentation documentation. + Change to no longer + require the can_redefine_classes capability. + + + Clarifications for retransformation. + + + Clarifications for retransformation, per review. + Lock "retransformation (in)capable" at class load enable time. + + + Add new heap functionity which supports reporting primitive values, + allows setting the referrer tag, and has more powerful filtering: + FollowReferences, IterateThroughHeap, and their associated + callbacks, structs, enums, and constants. + + + Clarification. + + + FollowReferences, IterateThroughHeap: Put callbacks in a struct; + Add missing error codes; reduce bits in the visit control flags. + + + More on new heap functionity: spec clean-up per review. + + + More on new heap functionity: Rename old heap section to Heap (1.0). + + + Fix typos. + + + Make referrer info structure a union. + + + In new heap functions: + Add missing superclass reference kind. + Use a single scheme for computing field indexes. + Remove outdated references to struct based referrer info. + + + Don't callback during FollowReferences on frivolous java.lang.Object superclass. + + + In string primitive callback, length now Unicode length. + In array and string primitive callbacks, value now "const". + Note possible compiler impacts on setting JNI function table. + + + GetClassVersionNumbers() and GetConstantPool() should return + error on array or primitive class. + + + Grammar fixes. + + + Add IsModifiableClass query. + + + Add referrer_class_tag parameter to jvmtiHeapReferenceCallback. + + + Doc fixes: update can_redefine_any_class to include retransform. + Clarify that exception events cover all Throwables. + In GetStackTrace, no test is done for start_depth too big if start_depth is zero, + Clarify fields reported in Primitive Field Callback -- static vs instance. + Repair confusing names of heap types, including callback names. + Require consistent usage of stack depth in the face of thread launch methods. + Note incompatibility of memory management with other systems. + + + Fix typos and missing renames. + + + Clarify that jmethodIDs and jfieldIDs can be saved. + Clarify that Iterate Over Instances Of Class includes subclasses. + + + Better phrasing. + + + Match the referrer_index for static fields in Object Reference Callback + with the Reference Implementation (and all other known implementations); + that is, make it match the definition for instance fields. + In GetThreadListStackTraces, add JVMTI_ERROR_INVALID_THREAD to cover + an invalid thread in the list; and specify that not started threads + return empty stacks. + + + Typo. + + + Typo. + + + Remove restrictions on AddToBootstrapClassLoaderSearch and + AddToSystemClassLoaderSearch. + + + Changed spec to return -1 for monitor stack depth for the + implementation which can not determine stack depth. + + + Corrections for readability and accuracy courtesy of Alan Pratt of IBM. + List the object relationships reported in FollowReferences. + + + Clarify the object relationships reported in FollowReferences. + + + Clarify DisposeEnvironment; add warning. + Fix typos in SetLocalXXX "retrieve" => "set". + Clarify that native method prefixes must remain set while used. + Clarify that exactly one Agent_OnXXX is called per agent. + Clarify that library loading is independent from start-up. + Remove ambiguous reference to Agent_OnLoad in the Agent_OnUnload spec. + + + Clarify the interaction between functions and exceptions. + Clarify and give examples of field indices. + Remove confusing "That is" sentence from MonitorWait and MonitorWaited events. + Update links to point to Java 6. + + + Add ResourceExhaustedEvent. + + + +
+ diff --git a/hotspot/src/share/vm/prims/jvmti.xsl b/hotspot/src/share/vm/prims/jvmti.xsl new file mode 100644 index 00000000000..e6fa57e047a --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmti.xsl @@ -0,0 +1,2103 @@ + + + + + + + + + + + + + + + + <xsl:value-of select="@label"/> + <xsl:text> </xsl:text> + <xsl:call-template name="showversion"/> + + + + + + + + +
+ +
+ + +

+


+

+

+ +

+ +

+ +

+ +

+ +

+

+


+

+ Constant Index +

+
+ + + +
+ +

+

+


+

+ +

+ +
+

+ + + + + + +

+ +

+

+ + + +

+
+ + +

+


+

+ +

+ +

Function Index

+
    + +
+ +
+ + +
  • + + # + + + + +
      + +
    +
  • +
    + + +
  • + + # + + +
  • +
    + + + + + + +

    + + + +

    +
    +

    + functions: +
      + +
    + + + function types: +
      + +
    +
    + + + types: + + + + + flags and constants: + + + +

    + + + + +


    + + + + + + +
    + + +
    +      jvmtiError
    +
    +      (jvmtiEnv* env)
    +
    + + + + + + +
    + + + + + + + + + + + + + + + +
    + Phase + + Callback Safe + + Position + + Since +
    + + + + + + + +
    +
    + + + + + + + + + + + + + + + + + +
    + Phase + + Event Type + + Number + + Enabling + + Since +
    + + + + + + + SetEventNotificationMode(JVMTI_ENABLE, + , NULL) + + +
    +
    + + + may + + + only be called during the live + + + + + only be called during the OnLoad or the live + + + + + be called during any + + + + + only be called during the start or the live + + + + + only be called during the OnLoad + + + + bad phase - + + + + + + + + + + + + phase + + + + + sent + + + only during the live + + + + + during the primordial, start or live + + + + + during the start or live + + + + bad phase - + + + + + + + + phase + + + + + + + This function may be called from the callbacks to the + Heap iteration functions, or from the + event handlers for the + GarbageCollectionStart, + GarbageCollectionFinish, + and ObjectFree events. + + + No + + + + + + +
    + + + + + +
    +
    +
    +      typedef 
    +      
    +       (JNICALL *
    +      
    +      )
    +    (
    +      
    +        
    +        
    +          , 
    +     
    +          
    +        
    +      
    +      );
    +    
    +
    + + +
    +
    + + +

    +
    + + +

    +


    +

    + +

    + +
    +
    +  
    +typedef struct {
    +
    +  
    +    
    +    
    +    
    +    
    +  
    +  } jvmtiEventCallbacks;
    +
    +  
    +
    +

    +


    +

    Event Index

    +
      + + + +
    + +
    + + +
  • + + # + + + + +
  • +
    + + +

    + + + +

    +
    +

    +

    +

    + +
    +void JNICALL
    +
    +      (jvmtiEnv *jvmti_env)
    +
    + + + + + +
    + + +
    +
    +      
    +    
    +
    +
    + + +
    +  
    +    
    +  
    +  
    +
    + + +
    +  
    +    
    +  
    +  
    +
    + + + + + + +

    + + + + +

    + +
    + + +
    +
    + + +
    +  
    +  
    +
    + + +

    + + + +

    + +
    + + + + + + + + + + + + +
    + - +
    + Field + + Type + + Description +
    +
    + + +

    + + + +

    + +
    + + + + + + + + + + + + + + + +
    + - +
    + All types are unsigned int : 1 +
    + Field + + Description + + Since +
    +
    + + + + + + + # + + + + + + + + + + + + + + + + + . + + + + + + + + + + + + + + + + + + + + + + +

    + +
    + + + +
    +
    + + + + + . + + + + + + + + +
    + + + + + + + # + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + + + ( + + + + ) + +
    + Constant + + Value + + Description +
    +
    +
    + + + + # + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + +

    + + + +

    + + + + + + + + + +
    + +
    + Type + + Description +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +            
    +          
    + + +
    +
    +
    + + + +

    + + + + + + + + + #. + + + + + + + + + + + + + + + + #. + + + + + + + + + + + + + + + + + # + + + + + + + + + + + + + + + +

    +
    + + + + + + + + + + + + + + + + + + + + http://java.sun.com/docs/books/vmspec/2nd-edition/html/ + + + + + + + + + + + of + + + the + + + + http://java.sun.com/docs/books/vmspec/ + + + Java Virtual Machine Specification + + + + + + + # + + + + + +

    + + + + + + + + + + +
    + Parameters +
    + Name + + Type + + Description +
    + + + + + + + + . + + + + + + + + + + + + + + + + + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Capabilities +
    + Required Functionality +
    + Optional Functionality: might not be implemented for all + virtual machines. + + + The following capability + + + One of the following capabilities + + + (as returned by + GetCapabilities) + must be true to use this + + + function. + + + event. + + +
    + Capability + + Effect +
    + Optional Features +
    + Capability + + Effect +
    + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + +
    + Capabilities +
    + Required Functionality +
    + Event Enabling Capabilities +
    + Capability + + Events +
    + + #jvmtiCapabilities. + + + + + + + + + # + + + + + +
    +
    +
    + + + + + + + #jvmtiCapabilities. + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + + + + + + + + + + + + + + + + + + + + + + + +
    + Errors +
    + This function returns either a + universal error + or one of the following errors +
    + Error + + Description +
    + This function returns a + universal error +
    + + + + yes + + + + + + + + JVMTI_ERROR_MUST_POSSESS_CAPABILITY + + + + + The environment does not possess the capability + + #jvmtiCapabilities. + + + + . + Use AddCapabilities. + + + + + + + + + + + + + + + + + + + + + + + + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + # + + + + + + + + + + + + + + + + + + + + + + + + + yes + + + + + yes + + + + + + + + + yes + + + + + + yes + + + + + yes + + + + + + + + + + JVMTI_ERROR_INVALID_METHODID + + + + JVMTI_ERROR_NATIVE_METHOD + + + + + + is not a jmethodID. + + + + + + is a native method. + + + + JVMTI_ERROR_INVALID_FIELDID + + + + + + is not a jfieldID. + + + + JVMTI_ERROR_ILLEGAL_ARGUMENT + + + + JVMTI_ERROR_NO_MORE_FRAMES + + + + + + is less than zero. + + + + + There are no stack frames at the specified + + . + + + + JVMTI_ERROR_INVALID_MONITOR + + + + + + is not a jrawMonitorID. + + + + JVMTI_ERROR_INVALID_CLASS + + + + + + is not a class object or the class has been unloaded. + + + + JVMTI_ERROR_INVALID_THREAD + + + + JVMTI_ERROR_THREAD_NOT_ALIVE + + + + + + is not a thread object. + + + + + + is not live (has not been started or is now dead). + + + + JVMTI_ERROR_INVALID_THREAD_GROUP + + + + + + is not a thread group object. + + + + JVMTI_ERROR_INVALID_OBJECT + + + + + + is not an object. + + + + + + JVMTI_ERROR_INVALID_EVENT_TYPE + + + JVMTI_ERROR_ILLEGAL_ARGUMENT + + + + + + + + is not a + + . + + + + JVMTI_ERROR_INVALID_LOCATION + + + + + + is not a valid location. + + + + + yes + + + + + JVMTI_ERROR_ILLEGAL_ARGUMENT + + + + + + is less than + + . + + + + + + + + + attempt to get error description for + + + + + + yes + + + + + JVMTI_ERROR_NULL_POINTER + + + + + + + attempt to get error description in null ok case for + + + + is + NULL + . + + + + + #. + + + + + + + + + yes + + + + + + + # + + + + + + + + + + + + +

    +


    +

    + Errors +

    +

    + +

    + +

    + + + +

    +


    +

    + Data Types +

    +

    + + +

    + + + + + + + + + + + + +
    + Structure Type Definitions +
    + Type + + Description +
    +

    + + + + + + + + + + + + +
    + Function Type Definitions +
    + Type + + Description +
    +

    + + + + + + + + + + + + +
    + Enumeration Definitions +
    + Type + + Description +
    +

    + + + + + + + + + + + + + + +
    + Function Table Layout +
    + Position + + Function + + Declaration +
    +

    + + + + + + + + + + + + + + + + + # + + + + + + +

    +            jvmtiError (JNICALL *
    +            
    +            ) (jvmtiEnv* env
    +            
    +              
    +                , 
                           
    +              
    +            
    +            );
    +        
    + + + + + + More than one function has index number . + + + + reserved + + +
    +          void *reserved        
    +          
    +          ;
    +        
    + +
    + + + + + + + + +
    + + + + +

    + + + + +

    + +

    +

    + +
    +

    + + + +

    + + + + + () + +
    +
    + +

    +

    +
    + + +


    +

    Change History

    + Last update:
    + Version: +

    + +

    + + + + + + +
    + Version
    + Date +
    + Changes +
    + + + + + + + + + +
    +
    + + + + + + +
    + + + + + +

    + + + + +

    + + + + + + + + + + + +
    + + + +

    + To be resolved: + +

    +
    +
    + + +

    + Rationale: + +

    +
    + + + +

    + To do: + +

    +
    +
    + + + + + + + + + + + +
    +
    +      
    +    
    +
    +
    + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + +
    + +
    +
    + + +
    + +
    +
    + + +

    + +

    +
    + + +
    + +
    +
    + + +
      + + +
    +
    + + +
  • + +
  • +
    + + + + + + + + + + + TM + + + + + + + + + + + + + + + + + + + + + + JVM TI + + + +
    diff --git a/hotspot/src/share/vm/prims/jvmtiAgentThread.hpp b/hotspot/src/share/vm/prims/jvmtiAgentThread.hpp new file mode 100644 index 00000000000..68de0415a78 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiAgentThread.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// class JvmtiAgentThread +// +// JavaThread used to wrap a thread started by an agent +// using the JVMTI method RunAgentThread. +// +class JvmtiAgentThread : public JavaThread { + + jvmtiStartFunction _start_fn; + JvmtiEnv* _env; + const void *_start_arg; + +public: + JvmtiAgentThread(JvmtiEnv* env, jvmtiStartFunction start_fn, const void *start_arg); + + bool is_jvmti_agent_thread() const { return true; } + + static void start_function_wrapper(JavaThread *thread, TRAPS); + void call_start_function(); +}; diff --git a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp new file mode 100644 index 00000000000..2ad6d99b7e2 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp @@ -0,0 +1,677 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +# include "incls/_precompiled.incl" +# include "incls/_jvmtiClassFileReconstituter.cpp.incl" + +// FIXME: add Deprecated, LVT, LVTT attributes +// FIXME: fix Synthetic attribute +// FIXME: per Serguei, add error return handling for constantPoolOopDesc::copy_cpool_bytes() + + +// Write the field information portion of ClassFile structure +// JVMSpec| u2 fields_count; +// JVMSpec| field_info fields[fields_count]; +void JvmtiClassFileReconstituter::write_field_infos() { + HandleMark hm(thread()); + typeArrayHandle fields(thread(), ikh()->fields()); + int fields_length = fields->length(); + int num_fields = fields_length / instanceKlass::next_offset; + objArrayHandle fields_anno(thread(), ikh()->fields_annotations()); + + write_u2(num_fields); + for (int index = 0; index < fields_length; index += instanceKlass::next_offset) { + AccessFlags access_flags; + int flags = fields->ushort_at(index + instanceKlass::access_flags_offset); + access_flags.set_flags(flags); + int name_index = fields->ushort_at(index + instanceKlass::name_index_offset); + int signature_index = fields->ushort_at(index + instanceKlass::signature_index_offset); + int initial_value_index = fields->ushort_at(index + instanceKlass::initval_index_offset); + guarantee(name_index != 0 && signature_index != 0, "bad constant pool index for field"); + int offset = ikh()->offset_from_fields( index ); + int generic_signature_index = + fields->ushort_at(index + instanceKlass::generic_signature_offset); + typeArrayHandle anno(thread(), fields_anno.not_null() ? + (typeArrayOop)(fields_anno->obj_at(index / instanceKlass::next_offset)) : + (typeArrayOop)NULL); + + // JVMSpec| field_info { + // JVMSpec| u2 access_flags; + // JVMSpec| u2 name_index; + // JVMSpec| u2 descriptor_index; + // JVMSpec| u2 attributes_count; + // JVMSpec| attribute_info attributes[attributes_count]; + // JVMSpec| } + + write_u2(flags & JVM_RECOGNIZED_FIELD_MODIFIERS); + write_u2(name_index); + write_u2(signature_index); + int attr_count = 0; + if (initial_value_index != 0) { + ++attr_count; + } + if (access_flags.is_synthetic()) { + // ++attr_count; + } + if (generic_signature_index != 0) { + ++attr_count; + } + if (anno.not_null()) { + ++attr_count; // has RuntimeVisibleAnnotations attribute + } + + write_u2(attr_count); + + if (initial_value_index != 0) { + write_attribute_name_index("ConstantValue"); + write_u4(2); //length always 2 + write_u2(initial_value_index); + } + if (access_flags.is_synthetic()) { + // write_synthetic_attribute(); + } + if (generic_signature_index != 0) { + write_signature_attribute(generic_signature_index); + } + if (anno.not_null()) { + write_annotations_attribute("RuntimeVisibleAnnotations", anno); + } + } +} + +// Write Code attribute +// JVMSpec| Code_attribute { +// JVMSpec| u2 attribute_name_index; +// JVMSpec| u4 attribute_length; +// JVMSpec| u2 max_stack; +// JVMSpec| u2 max_locals; +// JVMSpec| u4 code_length; +// JVMSpec| u1 code[code_length]; +// JVMSpec| u2 exception_table_length; +// JVMSpec| { u2 start_pc; +// JVMSpec| u2 end_pc; +// JVMSpec| u2 handler_pc; +// JVMSpec| u2 catch_type; +// JVMSpec| } exception_table[exception_table_length]; +// JVMSpec| u2 attributes_count; +// JVMSpec| attribute_info attributes[attributes_count]; +// JVMSpec| } +void JvmtiClassFileReconstituter::write_code_attribute(methodHandle method) { + constMethodHandle const_method(thread(), method->constMethod()); + u2 line_num_cnt = 0; + int stackmap_len = 0; + + // compute number and length of attributes -- FIXME: for now no LVT + int attr_count = 0; + int attr_size = 0; + if (const_method->has_linenumber_table()) { + line_num_cnt = line_number_table_entries(method); + if (line_num_cnt != 0) { + ++attr_count; + // Compute the complete size of the line number table attribute: + // LineNumberTable_attribute { + // u2 attribute_name_index; + // u4 attribute_length; + // u2 line_number_table_length; + // { u2 start_pc; + // u2 line_number; + // } line_number_table[line_number_table_length]; + // } + attr_size += 2 + 4 + 2 + line_num_cnt * (2 + 2); + } + } + if (method->has_stackmap_table()) { + stackmap_len = method->stackmap_data()->length(); + if (stackmap_len != 0) { + ++attr_count; + // Compute the size of the stack map table attribute (VM stores raw): + // StackMapTable_attribute { + // u2 attribute_name_index; + // u4 attribute_length; + // u2 number_of_entries; + // stack_map_frame_entries[number_of_entries]; + // } + attr_size += 2 + 4 + stackmap_len; + } + } + + typeArrayHandle exception_table(thread(), const_method->exception_table()); + int exception_table_length = exception_table->length(); + int exception_table_entries = exception_table_length / 4; + int code_size = const_method->code_size(); + int size = + 2+2+4 + // max_stack, max_locals, code_length + code_size + // code + 2 + // exception_table_length + (2+2+2+2) * exception_table_entries + // exception_table + 2 + // attributes_count + attr_size; // attributes + + write_attribute_name_index("Code"); + write_u4(size); + write_u2(method->max_stack()); + write_u2(method->max_locals()); + write_u4(code_size); + copy_bytecodes(method, (unsigned char*)writeable_address(code_size)); + write_u2(exception_table_entries); + for (int index = 0; index < exception_table_length; ) { + write_u2(exception_table->int_at(index++)); + write_u2(exception_table->int_at(index++)); + write_u2(exception_table->int_at(index++)); + write_u2(exception_table->int_at(index++)); + } + write_u2(attr_count); + if (line_num_cnt != 0) { + write_line_number_table_attribute(method, line_num_cnt); + } + if (stackmap_len != 0) { + write_stackmap_table_attribute(method, stackmap_len); + } + + // FIXME: write LVT attribute +} + +// Write Exceptions attribute +// JVMSpec| Exceptions_attribute { +// JVMSpec| u2 attribute_name_index; +// JVMSpec| u4 attribute_length; +// JVMSpec| u2 number_of_exceptions; +// JVMSpec| u2 exception_index_table[number_of_exceptions]; +// JVMSpec| } +void JvmtiClassFileReconstituter::write_exceptions_attribute(constMethodHandle const_method) { + CheckedExceptionElement* checked_exceptions = const_method->checked_exceptions_start(); + int checked_exceptions_length = const_method->checked_exceptions_length(); + int size = + 2 + // number_of_exceptions + 2 * checked_exceptions_length; // exception_index_table + + write_attribute_name_index("Exceptions"); + write_u4(size); + write_u2(checked_exceptions_length); + for (int index = 0; index < checked_exceptions_length; index++) { + write_u2(checked_exceptions[index].class_cp_index); + } +} + +// Write SourceFile attribute +// JVMSpec| SourceFile_attribute { +// JVMSpec| u2 attribute_name_index; +// JVMSpec| u4 attribute_length; +// JVMSpec| u2 sourcefile_index; +// JVMSpec| } +void JvmtiClassFileReconstituter::write_source_file_attribute() { + assert(ikh()->source_file_name() != NULL, "caller must check"); + + write_attribute_name_index("SourceFile"); + write_u4(2); // always length 2 + write_u2(symbol_to_cpool_index(ikh()->source_file_name())); +} + +// Write SourceDebugExtension attribute +// JSR45| SourceDebugExtension_attribute { +// JSR45| u2 attribute_name_index; +// JSR45| u4 attribute_length; +// JSR45| u2 sourcefile_index; +// JSR45| } +void JvmtiClassFileReconstituter::write_source_debug_extension_attribute() { + assert(ikh()->source_debug_extension() != NULL, "caller must check"); + + write_attribute_name_index("SourceDebugExtension"); + write_u4(2); // always length 2 + write_u2(symbol_to_cpool_index(ikh()->source_debug_extension())); +} + +// Write (generic) Signature attribute +// JVMSpec| Signature_attribute { +// JVMSpec| u2 attribute_name_index; +// JVMSpec| u4 attribute_length; +// JVMSpec| u2 signature_index; +// JVMSpec| } +void JvmtiClassFileReconstituter::write_signature_attribute(u2 generic_signature_index) { + write_attribute_name_index("Signature"); + write_u4(2); // always length 2 + write_u2(generic_signature_index); +} + +// Compute the number of entries in the InnerClasses attribute +u2 JvmtiClassFileReconstituter::inner_classes_attribute_length() { + typeArrayOop inner_class_list = ikh()->inner_classes(); + return (inner_class_list == NULL) ? 0 : inner_class_list->length(); +} + +// Write an annotation attribute. The VM stores them in raw form, so all we need +// to do is add the attrubute name and fill in the length. +// JSR202| *Annotations_attribute { +// JSR202| u2 attribute_name_index; +// JSR202| u4 attribute_length; +// JSR202| ... +// JSR202| } +void JvmtiClassFileReconstituter::write_annotations_attribute(const char* attr_name, + typeArrayHandle annos) { + u4 length = annos->length(); + write_attribute_name_index(attr_name); + write_u4(length); + memcpy(writeable_address(length), annos->byte_at_addr(0), length); +} + + +// Write InnerClasses attribute +// JVMSpec| InnerClasses_attribute { +// JVMSpec| u2 attribute_name_index; +// JVMSpec| u4 attribute_length; +// JVMSpec| u2 number_of_classes; +// JVMSpec| { u2 inner_class_info_index; +// JVMSpec| u2 outer_class_info_index; +// JVMSpec| u2 inner_name_index; +// JVMSpec| u2 inner_class_access_flags; +// JVMSpec| } classes[number_of_classes]; +// JVMSpec| } +void JvmtiClassFileReconstituter::write_inner_classes_attribute(int length) { + typeArrayOop inner_class_list = ikh()->inner_classes(); + guarantee(inner_class_list != NULL && inner_class_list->length() == length, + "caller must check"); + typeArrayHandle inner_class_list_h(thread(), inner_class_list); + assert (length % instanceKlass::inner_class_next_offset == 0, "just checking"); + u2 entry_count = length / instanceKlass::inner_class_next_offset; + u4 size = 2 + entry_count * (2+2+2+2); + + write_attribute_name_index("InnerClasses"); + write_u4(size); + write_u2(entry_count); + for (int i = 0; i < length; i += instanceKlass::inner_class_next_offset) { + write_u2(inner_class_list_h->ushort_at( + i + instanceKlass::inner_class_inner_class_info_offset)); + write_u2(inner_class_list_h->ushort_at( + i + instanceKlass::inner_class_outer_class_info_offset)); + write_u2(inner_class_list_h->ushort_at( + i + instanceKlass::inner_class_inner_name_offset)); + write_u2(inner_class_list_h->ushort_at( + i + instanceKlass::inner_class_access_flags_offset)); + } +} + +// Write Synthetic attribute +// JVMSpec| Synthetic_attribute { +// JVMSpec| u2 attribute_name_index; +// JVMSpec| u4 attribute_length; +// JVMSpec| } +void JvmtiClassFileReconstituter::write_synthetic_attribute() { + write_attribute_name_index("Synthetic"); + write_u4(0); //length always zero +} + +// Compute size of LineNumberTable +u2 JvmtiClassFileReconstituter::line_number_table_entries(methodHandle method) { + // The line number table is compressed so we don't know how big it is until decompressed. + // Decompression is really fast so we just do it twice. + u2 num_entries = 0; + CompressedLineNumberReadStream stream(method->compressed_linenumber_table()); + while (stream.read_pair()) { + num_entries++; + } + return num_entries; +} + +// Write LineNumberTable attribute +// JVMSpec| LineNumberTable_attribute { +// JVMSpec| u2 attribute_name_index; +// JVMSpec| u4 attribute_length; +// JVMSpec| u2 line_number_table_length; +// JVMSpec| { u2 start_pc; +// JVMSpec| u2 line_number; +// JVMSpec| } line_number_table[line_number_table_length]; +// JVMSpec| } +void JvmtiClassFileReconstituter::write_line_number_table_attribute(methodHandle method, + u2 num_entries) { + + write_attribute_name_index("LineNumberTable"); + write_u4(2 + num_entries * (2 + 2)); + write_u2(num_entries); + + CompressedLineNumberReadStream stream(method->compressed_linenumber_table()); + while (stream.read_pair()) { + write_u2(stream.bci()); + write_u2(stream.line()); + } +} + +// Write stack map table attribute +// JSR-202| StackMapTable_attribute { +// JSR-202| u2 attribute_name_index; +// JSR-202| u4 attribute_length; +// JSR-202| u2 number_of_entries; +// JSR-202| stack_map_frame_entries[number_of_entries]; +// JSR-202| } +void JvmtiClassFileReconstituter::write_stackmap_table_attribute(methodHandle method, + int stackmap_len) { + + write_attribute_name_index("StackMapTable"); + write_u4(stackmap_len); + memcpy( + writeable_address(stackmap_len), + (void*)(method->stackmap_data()->byte_at_addr(0)), + stackmap_len); +} + +// Write one method_info structure +// JVMSpec| method_info { +// JVMSpec| u2 access_flags; +// JVMSpec| u2 name_index; +// JVMSpec| u2 descriptor_index; +// JVMSpec| u2 attributes_count; +// JVMSpec| attribute_info attributes[attributes_count]; +// JVMSpec| } +void JvmtiClassFileReconstituter::write_method_info(methodHandle method) { + AccessFlags access_flags = method->access_flags(); + constMethodHandle const_method(thread(), method->constMethod()); + u2 generic_signature_index = const_method->generic_signature_index(); + typeArrayHandle anno(thread(), method->annotations()); + typeArrayHandle param_anno(thread(), method->parameter_annotations()); + typeArrayHandle default_anno(thread(), method->annotation_default()); + + write_u2(access_flags.get_flags() & JVM_RECOGNIZED_METHOD_MODIFIERS); + write_u2(const_method->name_index()); + write_u2(const_method->signature_index()); + + // write attributes in the same order javac does, so we can test with byte for + // byte comparison + int attr_count = 0; + if (const_method->code_size() != 0) { + ++attr_count; // has Code attribute + } + if (const_method->has_checked_exceptions()) { + ++attr_count; // has Exceptions attribute + } + if (default_anno.not_null()) { + ++attr_count; // has AnnotationDefault attribute + } + // Deprecated attribute would go here + if (access_flags.is_synthetic()) { // FIXME + // ++attr_count; + } + if (generic_signature_index != 0) { + ++attr_count; + } + if (anno.not_null()) { + ++attr_count; // has RuntimeVisibleAnnotations attribute + } + if (param_anno.not_null()) { + ++attr_count; // has RuntimeVisibleParameterAnnotations attribute + } + + write_u2(attr_count); + if (const_method->code_size() > 0) { + write_code_attribute(method); + } + if (const_method->has_checked_exceptions()) { + write_exceptions_attribute(const_method); + } + if (default_anno.not_null()) { + write_annotations_attribute("AnnotationDefault", default_anno); + } + // Deprecated attribute would go here + if (access_flags.is_synthetic()) { + // write_synthetic_attribute(); + } + if (generic_signature_index != 0) { + write_signature_attribute(generic_signature_index); + } + if (anno.not_null()) { + write_annotations_attribute("RuntimeVisibleAnnotations", anno); + } + if (param_anno.not_null()) { + write_annotations_attribute("RuntimeVisibleParameterAnnotations", param_anno); + } +} + +// Write the class attributes portion of ClassFile structure +// JVMSpec| u2 attributes_count; +// JVMSpec| attribute_info attributes[attributes_count]; +void JvmtiClassFileReconstituter::write_class_attributes() { + u2 inner_classes_length = inner_classes_attribute_length(); + symbolHandle generic_signature(thread(), ikh()->generic_signature()); + typeArrayHandle anno(thread(), ikh()->class_annotations()); + + int attr_count = 0; + if (generic_signature() != NULL) { + ++attr_count; + } + if (ikh()->source_file_name() != NULL) { + ++attr_count; + } + if (ikh()->source_debug_extension() != NULL) { + ++attr_count; + } + if (inner_classes_length > 0) { + ++attr_count; + } + if (anno.not_null()) { + ++attr_count; // has RuntimeVisibleAnnotations attribute + } + + write_u2(attr_count); + + if (generic_signature() != NULL) { + write_signature_attribute(symbol_to_cpool_index(generic_signature())); + } + if (ikh()->source_file_name() != NULL) { + write_source_file_attribute(); + } + if (ikh()->source_debug_extension() != NULL) { + write_source_debug_extension_attribute(); + } + if (inner_classes_length > 0) { + write_inner_classes_attribute(inner_classes_length); + } + if (anno.not_null()) { + write_annotations_attribute("RuntimeVisibleAnnotations", anno); + } +} + +// Write the method information portion of ClassFile structure +// JVMSpec| u2 methods_count; +// JVMSpec| method_info methods[methods_count]; +void JvmtiClassFileReconstituter::write_method_infos() { + HandleMark hm(thread()); + objArrayHandle methods(thread(), ikh()->methods()); + int num_methods = methods->length(); + + write_u2(num_methods); + if (JvmtiExport::can_maintain_original_method_order()) { + int index; + int original_index; + int* method_order = NEW_RESOURCE_ARRAY(int, num_methods); + + // invert the method order mapping + for (index = 0; index < num_methods; index++) { + original_index = ikh()->method_ordering()->int_at(index); + assert(original_index >= 0 && original_index < num_methods, + "invalid original method index"); + method_order[original_index] = index; + } + + // write in original order + for (original_index = 0; original_index < num_methods; original_index++) { + index = method_order[original_index]; + methodHandle method(thread(), (methodOop)(ikh()->methods()->obj_at(index))); + write_method_info(method); + } + } else { + // method order not preserved just dump the method infos + for (int index = 0; index < num_methods; index++) { + methodHandle method(thread(), (methodOop)(ikh()->methods()->obj_at(index))); + write_method_info(method); + } + } +} + +void JvmtiClassFileReconstituter::write_class_file_format() { + ReallocMark(); + + // JVMSpec| ClassFile { + // JVMSpec| u4 magic; + write_u4(0xCAFEBABE); + + // JVMSpec| u2 minor_version; + // JVMSpec| u2 major_version; + write_u2(ikh()->minor_version()); + u2 major = ikh()->major_version(); + write_u2(major); + + // JVMSpec| u2 constant_pool_count; + // JVMSpec| cp_info constant_pool[constant_pool_count-1]; + write_u2(cpool()->length()); + copy_cpool_bytes(writeable_address(cpool_size())); + + // JVMSpec| u2 access_flags; + write_u2(ikh()->access_flags().get_flags() & JVM_RECOGNIZED_CLASS_MODIFIERS); + + // JVMSpec| u2 this_class; + // JVMSpec| u2 super_class; + write_u2(class_symbol_to_cpool_index(ikh()->name())); + klassOop super_class = ikh()->super(); + write_u2(super_class == NULL? 0 : // zero for java.lang.Object + class_symbol_to_cpool_index(super_class->klass_part()->name())); + + // JVMSpec| u2 interfaces_count; + // JVMSpec| u2 interfaces[interfaces_count]; + objArrayHandle interfaces(thread(), ikh()->local_interfaces()); + int num_interfaces = interfaces->length(); + write_u2(num_interfaces); + for (int index = 0; index < num_interfaces; index++) { + HandleMark hm(thread()); + instanceKlassHandle iikh(thread(), klassOop(interfaces->obj_at(index))); + write_u2(class_symbol_to_cpool_index(iikh->name())); + } + + // JVMSpec| u2 fields_count; + // JVMSpec| field_info fields[fields_count]; + write_field_infos(); + + // JVMSpec| u2 methods_count; + // JVMSpec| method_info methods[methods_count]; + write_method_infos(); + + // JVMSpec| u2 attributes_count; + // JVMSpec| attribute_info attributes[attributes_count]; + // JVMSpec| } /* end ClassFile 8? + write_class_attributes(); +} + +address JvmtiClassFileReconstituter::writeable_address(size_t size) { + size_t used_size = _buffer_ptr - _buffer; + if (size + used_size >= _buffer_size) { + // compute the new buffer size: must be at least twice as big as before + // plus whatever new is being used; then convert to nice clean block boundary + size_t new_buffer_size = (size + _buffer_size*2 + 1) / initial_buffer_size + * initial_buffer_size; + + // VM goes belly-up if the memory isn't available, so cannot do OOM processing + _buffer = REALLOC_RESOURCE_ARRAY(u1, _buffer, _buffer_size, new_buffer_size); + _buffer_size = new_buffer_size; + _buffer_ptr = _buffer + used_size; + } + u1* ret_ptr = _buffer_ptr; + _buffer_ptr += size; + return ret_ptr; +} + +void JvmtiClassFileReconstituter::write_attribute_name_index(const char* name) { + unsigned int hash_ignored; + symbolOop sym = SymbolTable::lookup_only(name, (int)strlen(name), hash_ignored); + assert(sym != NULL, "attribute name symbol not found"); + u2 attr_name_index = symbol_to_cpool_index(sym); + assert(attr_name_index != 0, "attribute name symbol not in constant pool"); + write_u2(attr_name_index); +} + +void JvmtiClassFileReconstituter::write_u1(u1 x) { + *writeable_address(1) = x; +} + +void JvmtiClassFileReconstituter::write_u2(u2 x) { + Bytes::put_Java_u2(writeable_address(2), x); +} + +void JvmtiClassFileReconstituter::write_u4(u4 x) { + Bytes::put_Java_u4(writeable_address(4), x); +} + +void JvmtiClassFileReconstituter::write_u8(u8 x) { + Bytes::put_Java_u8(writeable_address(8), x); +} + +void JvmtiClassFileReconstituter::copy_bytecodes(methodHandle mh, + unsigned char* bytecodes) { + // use a BytecodeStream to iterate over the bytecodes. JVM/fast bytecodes + // and the breakpoint bytecode are converted to their original bytecodes. + + BytecodeStream bs(mh); + + unsigned char* p = bytecodes; + Bytecodes::Code code; + bool is_rewritten = instanceKlass::cast(mh->method_holder())->is_rewritten(); + + while ((code = bs.next()) >= 0) { + assert(Bytecodes::is_java_code(code), "sanity check"); + assert(code != Bytecodes::_breakpoint, "sanity check"); + + // length of bytecode (mnemonic + operands) + address bcp = bs.bcp(); + int len = bs.next_bcp() - bcp; + assert(len > 0, "length must be > 0"); + + // copy the bytecodes + *p = (unsigned char) (bs.is_wide()? Bytecodes::_wide : code); + if (len > 1) { + memcpy(p+1, bcp+1, len-1); + } + + // During linking the get/put and invoke instructions are rewritten + // with an index into the constant pool cache. The original constant + // pool index must be returned to caller. Rewrite the index. + if (is_rewritten && len >= 3) { + switch (code) { + case Bytecodes::_getstatic : // fall through + case Bytecodes::_putstatic : // fall through + case Bytecodes::_getfield : // fall through + case Bytecodes::_putfield : // fall through + case Bytecodes::_invokevirtual : // fall through + case Bytecodes::_invokespecial : // fall through + case Bytecodes::_invokestatic : // fall through + case Bytecodes::_invokeinterface : + assert(len == 3 || (code == Bytecodes::_invokeinterface && len ==5), + "sanity check"); + // cache cannot be pre-fetched since some classes won't have it yet + ConstantPoolCacheEntry* entry = + mh->constants()->cache()->entry_at(Bytes::get_native_u2(bcp+1)); + int i = entry->constant_pool_index(); + assert(i < mh->constants()->length(), "sanity check"); + Bytes::put_Java_u2((address)(p+1), (u2)i); // java byte ordering + break; + } + } + + p += len; + } +} diff --git a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.hpp b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.hpp new file mode 100644 index 00000000000..dac947ed2c4 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.hpp @@ -0,0 +1,146 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +class JvmtiConstantPoolReconstituter : public StackObj { + private: + int _cpool_size; + SymbolHashMap* _symmap; + SymbolHashMap* _classmap; + constantPoolHandle _cpool; + instanceKlassHandle _ikh; + jvmtiError _err; + + protected: + instanceKlassHandle ikh() { return _ikh; }; + constantPoolHandle cpool() { return _cpool; }; + + u2 symbol_to_cpool_index(symbolOop sym) { + return _symmap->symbol_to_value(sym); + } + + u2 class_symbol_to_cpool_index(symbolOop sym) { + return _classmap->symbol_to_value(sym); + } + + public: + // Calls to this constructor must be proceeded by a ResourceMark + // and a HandleMark + JvmtiConstantPoolReconstituter(instanceKlassHandle ikh){ + set_error(JVMTI_ERROR_NONE); + _ikh = ikh; + _cpool = constantPoolHandle(Thread::current(), ikh->constants()); + _symmap = new SymbolHashMap(); + _classmap = new SymbolHashMap(); + _cpool_size = _cpool->hash_entries_to(_symmap, _classmap); + if (_cpool_size == 0) { + set_error(JVMTI_ERROR_OUT_OF_MEMORY); + } else if (_cpool_size < 0) { + set_error(JVMTI_ERROR_INTERNAL); + } + } + + ~JvmtiConstantPoolReconstituter() { + if (_symmap != NULL) { + os::free(_symmap); + _symmap = NULL; + } + if (_classmap != NULL) { + os::free(_classmap); + _classmap = NULL; + } + } + + + void set_error(jvmtiError err) { _err = err; } + jvmtiError get_error() { return _err; } + + int cpool_size() { return _cpool_size; } + + void copy_cpool_bytes(unsigned char *cpool_bytes) { + if (cpool_bytes == NULL) { + assert(cpool_bytes != NULL, "cpool_bytes pointer must not be NULL"); + return; + } + cpool()->copy_cpool_bytes(cpool_size(), _symmap, cpool_bytes); + } +}; + + +class JvmtiClassFileReconstituter : public JvmtiConstantPoolReconstituter { + private: + size_t _buffer_size; + u1* _buffer; + u1* _buffer_ptr; + Thread* _thread; + + enum { + // initial size should be power of two + initial_buffer_size = 1024 + }; + + inline Thread* thread() { return _thread; } + + void write_class_file_format(); + void write_field_infos(); + void write_method_infos(); + void write_method_info(methodHandle method); + void write_code_attribute(methodHandle method); + void write_exceptions_attribute(constMethodHandle const_method); + void write_synthetic_attribute(); + void write_class_attributes(); + void write_source_file_attribute(); + void write_source_debug_extension_attribute(); + u2 line_number_table_entries(methodHandle method); + void write_line_number_table_attribute(methodHandle method, u2 num_entries); + void write_stackmap_table_attribute(methodHandle method, int stackmap_table_len); + u2 inner_classes_attribute_length(); + void write_inner_classes_attribute(int length); + void write_signature_attribute(u2 generic_signaure_index); + void write_attribute_name_index(const char* name); + void write_annotations_attribute(const char* attr_name, typeArrayHandle annos); + + address writeable_address(size_t size); + void write_u1(u1 x); + void write_u2(u2 x); + void write_u4(u4 x); + void write_u8(u8 x); + + public: + // Calls to this constructor must be proceeded by a ResourceMark + // and a HandleMark + JvmtiClassFileReconstituter(instanceKlassHandle ikh) : + JvmtiConstantPoolReconstituter(ikh) { + _buffer_size = initial_buffer_size; + _buffer = _buffer_ptr = NEW_RESOURCE_ARRAY(u1, _buffer_size); + _thread = Thread::current(); + write_class_file_format(); + }; + + size_t class_file_size() { return _buffer_ptr - _buffer; } + + u1* class_file_bytes() { return _buffer; } + + static void copy_bytecodes(methodHandle method, unsigned char* bytecodes); +}; diff --git a/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp b/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp new file mode 100644 index 00000000000..d083e5762f4 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp @@ -0,0 +1,420 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiCodeBlobEvents.cpp.incl" + +// Support class to collect a list of the non-nmethod CodeBlobs in +// the CodeCache. +// +// This class actually creates a list of JvmtiCodeBlobDesc - each JvmtiCodeBlobDesc +// describes a single CodeBlob in the CodeCache. Note that collection is +// done to a static list - this is because CodeCache::blobs_do is defined +// as void CodeCache::blobs_do(void f(CodeBlob* nm)) and hence requires +// a C or static method. +// +// Usage :- +// +// CodeBlobCollector collector; +// +// collector.collect(); +// JvmtiCodeBlobDesc* blob = collector.first(); +// while (blob != NULL) { +// : +// blob = collector.next(); +// } +// + +class CodeBlobCollector : StackObj { + private: + GrowableArray* _code_blobs; // collected blobs + int _pos; // iterator position + + // used during a collection + static GrowableArray* _global_code_blobs; + static void do_blob(CodeBlob* cb); + public: + CodeBlobCollector() { + _code_blobs = NULL; + _pos = -1; + } + ~CodeBlobCollector() { + if (_code_blobs != NULL) { + for (int i=0; i<_code_blobs->length(); i++) { + FreeHeap(_code_blobs->at(i)); + } + delete _code_blobs; + } + } + + // collect list of code blobs in the cache + void collect(); + + // iteration support - return first code blob + JvmtiCodeBlobDesc* first() { + assert(_code_blobs != NULL, "not collected"); + if (_code_blobs->length() == 0) { + return NULL; + } + _pos = 0; + return _code_blobs->at(0); + } + + // iteration support - return next code blob + JvmtiCodeBlobDesc* next() { + assert(_pos >= 0, "iteration not started"); + if (_pos+1 >= _code_blobs->length()) { + return NULL; + } + return _code_blobs->at(++_pos); + } + +}; + +// used during collection +GrowableArray* CodeBlobCollector::_global_code_blobs; + + +// called for each CodeBlob in the CodeCache +// +// This function filters out nmethods as it is only interested in +// other CodeBlobs. This function also filters out CodeBlobs that have +// a duplicate starting address as previous blobs. This is needed to +// handle the case where multiple stubs are generated into a single +// BufferBlob. + +void CodeBlobCollector::do_blob(CodeBlob* cb) { + + // ignore nmethods + if (cb->is_nmethod()) { + return; + } + + // check if this starting address has been seen already - the + // assumption is that stubs are inserted into the list before the + // enclosing BufferBlobs. + address addr = cb->instructions_begin(); + for (int i=0; i<_global_code_blobs->length(); i++) { + JvmtiCodeBlobDesc* scb = _global_code_blobs->at(i); + if (addr == scb->code_begin()) { + return; + } + } + + // we must name the CodeBlob - some CodeBlobs already have names :- + // - stubs used by compiled code to call a (static) C++ runtime routine + // - non-relocatable machine code such as the interpreter, stubroutines, etc. + // - various singleton blobs + // + // others are unnamed so we create a name :- + // - OSR adapter (interpreter frame that has been on-stack replaced) + // - I2C and C2I adapters + const char* name = NULL; + if (cb->is_runtime_stub()) { + name = ((RuntimeStub*)cb)->name(); + } + if (cb->is_buffer_blob()) { + name = ((BufferBlob*)cb)->name(); + } + if (cb->is_deoptimization_stub() || cb->is_safepoint_stub()) { + name = ((SingletonBlob*)cb)->name(); + } + if (cb->is_uncommon_trap_stub() || cb->is_exception_stub()) { + name = ((SingletonBlob*)cb)->name(); + } + + // record the CodeBlob details as a JvmtiCodeBlobDesc + JvmtiCodeBlobDesc* scb = new JvmtiCodeBlobDesc(name, cb->instructions_begin(), + cb->instructions_end()); + _global_code_blobs->append(scb); +} + + +// collects a list of CodeBlobs in the CodeCache. +// +// The created list is growable array of JvmtiCodeBlobDesc - each one describes +// a CodeBlob. Note that the list is static - this is because CodeBlob::blobs_do +// requires a a C or static function so we can't use an instance function. This +// isn't a problem as the iteration is serial anyway as we need the CodeCache_lock +// to iterate over the code cache. +// +// Note that the CodeBlobs in the CodeCache will include BufferBlobs that may +// contain multiple stubs. As a profiler is interested in the stubs rather than +// the enclosing container we first iterate over the stub code descriptors so +// that the stubs go into the list first. do_blob will then filter out the +// enclosing blobs if the starting address of the enclosing blobs matches the +// starting address of first stub generated in the enclosing blob. + +void CodeBlobCollector::collect() { + assert_locked_or_safepoint(CodeCache_lock); + assert(_global_code_blobs == NULL, "checking"); + + // create the global list + _global_code_blobs = new (ResourceObj::C_HEAP) GrowableArray(50,true); + + // iterate over the stub code descriptors and put them in the list first. + int index = 0; + StubCodeDesc* desc; + while ((desc = StubCodeDesc::desc_for_index(++index)) != NULL) { + _global_code_blobs->append(new JvmtiCodeBlobDesc(desc->name(), desc->begin(), desc->end())); + } + + // next iterate over all the non-nmethod code blobs and add them to + // the list - as noted above this will filter out duplicates and + // enclosing blobs. + CodeCache::blobs_do(do_blob); + + // make the global list the instance list so that it can be used + // for other iterations. + _code_blobs = _global_code_blobs; + _global_code_blobs = NULL; +} + + +// Generate a DYNAMIC_CODE_GENERATED event for each non-nmethod code blob. + +jvmtiError JvmtiCodeBlobEvents::generate_dynamic_code_events(JvmtiEnv* env) { + CodeBlobCollector collector; + + // first collect all the code blobs + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + collector.collect(); + } + + // iterate over the collected list and post an event for each blob + JvmtiCodeBlobDesc* blob = collector.first(); + while (blob != NULL) { + JvmtiExport::post_dynamic_code_generated(env, blob->name(), blob->code_begin(), blob->code_end()); + blob = collector.next(); + } + return JVMTI_ERROR_NONE; +} + + +// Support class to describe a nmethod in the CodeCache + +class nmethodDesc: public CHeapObj { + private: + methodHandle _method; + address _code_begin; + address _code_end; + jvmtiAddrLocationMap* _map; + jint _map_length; + public: + nmethodDesc(methodHandle method, address code_begin, address code_end, + jvmtiAddrLocationMap* map, jint map_length) { + _method = method; + _code_begin = code_begin; + _code_end = code_end; + _map = map; + _map_length = map_length; + } + methodHandle method() const { return _method; } + address code_begin() const { return _code_begin; } + address code_end() const { return _code_end; } + jvmtiAddrLocationMap* map() const { return _map; } + jint map_length() const { return _map_length; } +}; + + +// Support class to collect a list of the nmethod CodeBlobs in +// the CodeCache. +// +// Usage :- +// +// nmethodCollector collector; +// +// collector.collect(); +// JvmtiCodeBlobDesc* blob = collector.first(); +// while (blob != NULL) { +// : +// blob = collector.next(); +// } +// +class nmethodCollector : StackObj { + private: + GrowableArray* _nmethods; // collect nmethods + int _pos; // iteration support + + // used during a collection + static GrowableArray* _global_nmethods; + static void do_nmethod(nmethod* nm); + public: + nmethodCollector() { + _nmethods = NULL; + _pos = -1; + } + ~nmethodCollector() { + if (_nmethods != NULL) { + for (int i=0; i<_nmethods->length(); i++) { + nmethodDesc* blob = _nmethods->at(i); + if (blob->map()!= NULL) { + FREE_C_HEAP_ARRAY(jvmtiAddrLocationMap, blob->map()); + } + } + delete _nmethods; + } + } + + // collect list of nmethods in the cache + void collect(); + + // iteration support - return first code blob + nmethodDesc* first() { + assert(_nmethods != NULL, "not collected"); + if (_nmethods->length() == 0) { + return NULL; + } + _pos = 0; + return _nmethods->at(0); + } + + // iteration support - return next code blob + nmethodDesc* next() { + assert(_pos >= 0, "iteration not started"); + if (_pos+1 >= _nmethods->length()) { + return NULL; + } + return _nmethods->at(++_pos); + } +}; + +// used during collection +GrowableArray* nmethodCollector::_global_nmethods; + + +// called for each nmethod in the CodeCache +// +// This function simply adds a descriptor for each nmethod to the global list. + +void nmethodCollector::do_nmethod(nmethod* nm) { + // ignore zombies + if (!nm->is_alive()) { + return; + } + + assert(nm->method() != NULL, "checking"); + + // create the location map for the nmethod. + jvmtiAddrLocationMap* map; + jint map_length; + JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nm, &map, &map_length); + + // record the nmethod details + methodHandle mh(nm->method()); + nmethodDesc* snm = new nmethodDesc(mh, + nm->code_begin(), + nm->code_end(), + map, + map_length); + _global_nmethods->append(snm); +} + +// collects a list of nmethod in the CodeCache. +// +// The created list is growable array of nmethodDesc - each one describes +// a nmethod and includs its JVMTI address location map. + +void nmethodCollector::collect() { + assert_locked_or_safepoint(CodeCache_lock); + assert(_global_nmethods == NULL, "checking"); + + // create the list + _global_nmethods = new (ResourceObj::C_HEAP) GrowableArray(100,true); + + // any a descriptor for each nmethod to the list. + CodeCache::nmethods_do(do_nmethod); + + // make the list the instance list + _nmethods = _global_nmethods; + _global_nmethods = NULL; +} + +// Generate a COMPILED_METHOD_LOAD event for each nnmethod + +jvmtiError JvmtiCodeBlobEvents::generate_compiled_method_load_events(JvmtiEnv* env) { + HandleMark hm; + nmethodCollector collector; + + // first collect all nmethods + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + collector.collect(); + } + + // iterate over the list and post an event for each nmethod + nmethodDesc* nm_desc = collector.first(); + while (nm_desc != NULL) { + methodOop method = nm_desc->method()(); + jmethodID mid = method->jmethod_id(); + assert(mid != NULL, "checking"); + JvmtiExport::post_compiled_method_load(env, mid, + (jint)(nm_desc->code_end() - nm_desc->code_begin()), + nm_desc->code_begin(), nm_desc->map_length(), + nm_desc->map()); + nm_desc = collector.next(); + } + return JVMTI_ERROR_NONE; +} + + +// create a C-heap allocated address location map for an nmethod +void JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nmethod *nm, + jvmtiAddrLocationMap** map_ptr, + jint *map_length_ptr) +{ + ResourceMark rm; + jvmtiAddrLocationMap* map = NULL; + jint map_length = 0; + + + // Generate line numbers using PcDesc and ScopeDesc info + methodHandle mh(nm->method()); + + if (!mh->is_native()) { + PcDesc *pcd; + int pcds_in_method; + + pcds_in_method = (nm->scopes_pcs_end() - nm->scopes_pcs_begin()); + map = NEW_C_HEAP_ARRAY(jvmtiAddrLocationMap, pcds_in_method); + + address scopes_data = nm->scopes_data_begin(); + for( pcd = nm->scopes_pcs_begin(); pcd < nm->scopes_pcs_end(); ++pcd ) { + ScopeDesc sc0(nm, pcd->scope_decode_offset()); + ScopeDesc *sd = &sc0; + while( !sd->is_top() ) { sd = sd->sender(); } + int bci = sd->bci(); + if (bci != InvocationEntryBci) { + assert(map_length < pcds_in_method, "checking"); + map[map_length].start_address = (const void*)pcd->real_pc(nm); + map[map_length].location = bci; + ++map_length; + } + } + } + + *map_ptr = map; + *map_length_ptr = map_length; +} diff --git a/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.hpp b/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.hpp new file mode 100644 index 00000000000..1f4fe2116ed --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.hpp @@ -0,0 +1,54 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _JVMTI_CODE_BLOB_EVENTS_H_ +#define _JVMTI_CODE_BLOB_EVENTS_H_ + +// forward declaration +class JvmtiEnv; + + +// JVMTI code blob event support +// -- used by GenerateEvents to generate CompiledMethodLoad and +// DynamicCodeGenerated events +// -- also provide utility function build_jvmti_addr_location_map to create +// a jvmtiAddrLocationMap list for a nmethod. + +class JvmtiCodeBlobEvents : public AllStatic { + public: + + // generate a DYNAMIC_CODE_GENERATED_EVENT event for each non-nmethod + // code blob in the code cache. + static jvmtiError generate_dynamic_code_events(JvmtiEnv* env); + + // generate a COMPILED_METHOD_LOAD event for each nmethod + // code blob in the code cache. + static jvmtiError generate_compiled_method_load_events(JvmtiEnv* env); + + // create a C-heap allocated address location map for an nmethod + static void build_jvmti_addr_location_map(nmethod *nm, jvmtiAddrLocationMap** map, + jint *map_length); +}; + +#endif diff --git a/hotspot/src/share/vm/prims/jvmtiEnter.hpp b/hotspot/src/share/vm/prims/jvmtiEnter.hpp new file mode 100644 index 00000000000..e3d26d474df --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEnter.hpp @@ -0,0 +1,23 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ diff --git a/hotspot/src/share/vm/prims/jvmtiEnter.xsl b/hotspot/src/share/vm/prims/jvmtiEnter.xsl new file mode 100644 index 00000000000..6380ca3ee0a --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEnter.xsl @@ -0,0 +1,1279 @@ + + + + + + + + + + + + + + + + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiEnter.cpp.incl" + + + + + +#ifdef JVMTI_TRACE + + + + + + +// Error names +const char* JvmtiUtil::_error_names[] = { + + + + + +}; + + +// Event threaded +const bool JvmtiUtil::_event_threaded[] = { + + + + + +}; + + + + + + + + + + + + + + + + + + jbyte JvmtiTrace::_event_trace_flags[ + + ]; + +jint JvmtiTrace::_max_event_index = + + ; + +// Event names +const char* JvmtiTrace::_event_names[] = { + + + + + +}; + + + + + + + +#endif /*JVMTI_TRACE */ + + + + + + + + +// + + names +const char* + + ConstantNames[] = { + + + NULL +}; + +// + + value +jint + + ConstantValues[] = { + + + 0 +}; + + + + + + " + + ", + + + + + + + , + + + + + + +// Check Event Capabilities +const bool JvmtiUtil::has_event_capability(jvmtiEvent event_type, const jvmtiCapabilities* capabilities_ptr) { + switch (event_type) { + + + + + case + + : + return capabilities_ptr-> + + != 0; + + + + } + // if it does not have a capability it is required + return JNI_TRUE; +} + + + + + + + + + + + + + + + + + + jbyte JvmtiTrace::_trace_flags[ + + ]; + +jint JvmtiTrace::_max_function_index = + + ; + +// Function names +const char* JvmtiTrace::_function_names[] = { + + + + + +}; + +// Exclude list +short JvmtiTrace::_exclude_functions[] = { + + + + + 0 +}; + + + + + +extern "C" { + + + + +} /* end extern "C" */ + +// JVMTI API functions +struct jvmtiInterface_1_ jvmti + + _Interface = { + + + + + + + +}; + + + + + jvmti + + _ + + + + + + + /* + + : + + + + */ + + + + + RESERVED */ + NULL + + + + + + + + + + + + + + , + + + + + + + + + + + + + + " + + " + + + NULL + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + false + + + true + + + + + false + + + + + + + + + + + + + + , + + + + + + + + + + + + , + + + + + + + // + // functions + // + + + + + + + + + + + + + + if (this_thread == NULL || !this_thread->is_Java_thread()) { + + + + + if (this_thread == NULL || (!this_thread->is_Java_thread() && !this_thread->is_VM_thread())) { + + + if (!this_thread->is_Java_thread()) { + + + + + + + + if (trace_flags) { + + tty->print_cr("JVMTI [non-attached thread] %s %s", func_name, + + JvmtiUtil::error_name(JVMTI_ERROR_UNATTACHED_THREAD)); + + } + + + return JVMTI_ERROR_UNATTACHED_THREAD; + + } + + + JavaThread* current_thread = (JavaThread*)this_thread; + + ThreadInVMfromNative __tiv(current_thread); + + __ENTRY(jvmtiError, + + , current_thread) + + debug_only(VMNativeEntryWrapper __vew;) + + + CautiouslyPreserveExceptionMark __em(this_thread); + + + + + + + + if (jvmti_env->get_capabilities()-> + + == 0) { + + + if (trace_flags) { + tty->print_cr("JVMTI [%s] %s %s", curr_thread_name, func_name, + JvmtiUtil::error_name(JVMTI_ERROR_MUST_POSSESS_CAPABILITY)); + } + + + return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; + } + + + + + + +static jvmtiError JNICALL + + + (jvmtiEnv* env + + ) { + + + + #ifdef JVMTI_KERNEL + return JVMTI_ERROR_NOT_AVAILABLE; + #else + + + + + + if(!JvmtiEnv::is_vm_live()) { + + + if (trace_flags) { + tty->print_cr("JVMTI [-] %s %s", func_name, + JvmtiUtil::error_name(JVMTI_ERROR_WRONG_PHASE)); + } + + + return JVMTI_ERROR_WRONG_PHASE; + } + + + Thread* this_thread = (Thread*)ThreadLocalStorage::thread(); + + + + + + if(JvmtiEnv::get_phase()!=JVMTI_PHASE_ONLOAD + + && JvmtiEnv::get_phase()!=JVMTI_PHASE_LIVE + + ) { + + + if (trace_flags) { + tty->print_cr("JVMTI [-] %s %s", func_name, + JvmtiUtil::error_name(JVMTI_ERROR_WRONG_PHASE)); + } + + + return JVMTI_ERROR_WRONG_PHASE; + } + + + if(JvmtiEnv::get_phase()!=JVMTI_PHASE_START && JvmtiEnv::get_phase()!=JVMTI_PHASE_LIVE) { + + + if (trace_flags) { + tty->print_cr("JVMTI [-] %s %s", func_name, + JvmtiUtil::error_name(JVMTI_ERROR_WRONG_PHASE)); + } + + + return JVMTI_ERROR_WRONG_PHASE; + } + Thread* this_thread = (Thread*)ThreadLocalStorage::thread(); + + + + + + + JvmtiEnv* jvmti_env = JvmtiEnv::JvmtiEnv_from_jvmti_env(env); + if (!jvmti_env->is_valid()) { + + + if (trace_flags) { + tty->print_cr("JVMTI [%s] %s %s env=%d", curr_thread_name, func_name, + JvmtiUtil::error_name(JVMTI_ERROR_INVALID_ENVIRONMENT), env); + } + + + return JVMTI_ERROR_INVALID_ENVIRONMENT; + } + + + + + jvmtiError err; + + + + + + if (Threads::number_of_threads() != 0) { + Thread* this_thread = (Thread*)ThreadLocalStorage::thread(); + + + + Thread* this_thread = NULL; + bool transition; + if (Threads::number_of_threads() == 0) { + transition = false; + } else { + this_thread = (Thread*)ThreadLocalStorage::thread(); + transition = ((this_thread != NULL) && !this_thread->is_VM_thread() && !this_thread->is_ConcurrentGC_thread()); + } + if (transition) { + + + + + + + + + + + + + + + } else { + + + + } + + + + + + + return err; + + + + #endif // JVMTI_KERNEL + + + } + + + + + + + + + + + err = jvmti_env-> + + ( + + ); + + + + + + + SafeResourceMark rm; + jint trace_flags = JvmtiTrace::trace_flags( + + ); + const char *func_name; + const char *curr_thread_name; + if (trace_flags) { + func_name = JvmtiTrace::function_name( + + ); + curr_thread_name = JvmtiTrace::safe_get_current_thread_name(); + } + + + + + + + + + if ((trace_flags & JvmtiTrace::SHOW_IN) != 0) { + + + } + + + + + + + + + + + if ((trace_flags & JvmtiTrace::SHOW_ERROR) != 0) { + if ((trace_flags & JvmtiTrace::SHOW_IN) == 0) { + + + + + } + tty->print_cr("JVMTI [%s] %s } %s - erroneous arg is + + + ", curr_thread_name, func_name, + JvmtiUtil::error_name( + + ) + + ); + } + + + return + + ; + + + + + + if ( err != JVMTI_ERROR_NONE && (trace_flags & JvmtiTrace::SHOW_ERROR) != 0) { + if ((trace_flags & JvmtiTrace::SHOW_IN) == 0) { + + + } + tty->print_cr("JVMTI [%s] %s } %s", curr_thread_name, func_name, + JvmtiUtil::error_name(err)); + } else if ((trace_flags & JvmtiTrace::SHOW_OUT) != 0) { + tty->print_cr("JVMTI [%s] %s }", curr_thread_name, func_name); + } + + + + + + + tty->print_cr("JVMTI [%s] %s { + + + + ", curr_thread_name, func_name + + + + ); + + + + + + + + + + + + + + + + + if ( + + == NULL) { + + + JVMTI_ERROR_NULL_POINTER + + + } + + + + + + + JvmtiRawMonitor *rmonitor = (JvmtiRawMonitor *) + + ; + if (rmonitor == NULL) { + + + JVMTI_ERROR_INVALID_MONITOR + - raw monitor is NULL + + + } + if (!rmonitor->is_valid()) { + + + JVMTI_ERROR_INVALID_MONITOR + - not a raw monitor 0x%x + , rmonitor + + + } + + + + + + oop thread_oop = JNIHandles::resolve_external_guard( + + ); + if (thread_oop == NULL) { + + + JVMTI_ERROR_INVALID_THREAD + - jthread resolved to NULL - jthread = %0x%x + , + + + } + if (!thread_oop->is_a(SystemDictionary::thread_klass())) { + + + JVMTI_ERROR_INVALID_THREAD + - oop is not a thread - jthread = %0x%x + , + + + } + java_thread = java_lang_Thread::thread(thread_oop); + if (java_thread == NULL) { + + + + JVMTI_ERROR_THREAD_NOT_ALIVE + + - not a Java thread - jthread = %0x%x + , + + + } + + + + + + + + JavaThread* java_thread; + + + + + + + + + if ( + + == NULL) { + java_thread = current_thread; + } else { + + + + + } + + + + + + + + + + if (depth < 0) { + + + JVMTI_ERROR_ILLEGAL_ARGUMENT + - negative depth - jthread = %0x%x + , + + + } + + + + + + + + oop k_mirror = JNIHandles::resolve_external_guard( + + ); + if (k_mirror == NULL) { + + + JVMTI_ERROR_INVALID_CLASS + - resolved to NULL - jclass = 0x%x + , + + + } + if (!k_mirror->is_a(SystemDictionary::class_klass())) { + + + JVMTI_ERROR_INVALID_CLASS + - not a class - jclass = 0x%x + , + + + } + + + + if (java_lang_Class::is_primitive(k_mirror)) { + + + JVMTI_ERROR_INVALID_CLASS + - is a primitive class - jclass = 0x%x + , + + + } + klassOop k_oop = java_lang_Class::as_klassOop(k_mirror); + if (k_oop == NULL) { + + + JVMTI_ERROR_INVALID_CLASS + - no klassOop - jclass = 0x%x + , + + + } + + + + + + + + + methodOop method_oop = JNIHandles::checked_resolve_jmethod_id( + + ); + if (method_oop == NULL) { + + JVMTI_ERROR_INVALID_METHODID + + + + + } + + if (method_oop->is_native()) { + return JVMTI_ERROR_NATIVE_METHOD; + } + + + + + + + ResourceMark rm_fdesc(current_thread); + fieldDescriptor fdesc; + if (!JvmtiEnv::get_field_descriptor(k_oop, + + , &fdesc)) { + + JVMTI_ERROR_INVALID_FIELDID + + + } + + + + + + + if ( + + < + + ) { + + + JVMTI_ERROR_ILLEGAL_ARGUMENT + + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ='%s' + + + =0x%x + + + + + + + , + + + + + + + + + + + =0x%x + + + + + + + + + + + + + , + + + + + + + + + + + + + =0x%x + + + + + , + + + + + + + + =%s + + + + + + + + + =%s + + + + + + , rmonitor->get_name() + + + + + + + + + =%s + + + + + + + + , + JvmtiTrace::safe_get_thread_name(java_thread) + + + + + + depth=%d + + + + + , + + + + + + + , + JvmtiTrace::get_class_name(k_mirror) + + + + + + + + =%s.%s + + + + + , + method_oop == NULL? "NULL" : method_oop->klass_name()->as_C_string(), + method_oop == NULL? "NULL" : method_oop->name()->as_C_string() + + + + + + , fdesc.name()->as_C_string() + + + + + + + =%d:%s + + + + + , + + , + + + + JvmtiUtil::error_name( + + ) + + + + + + JvmtiTrace::event_name( + + ) + + + + JvmtiTrace::enum_name( + + ConstantNames, + + ConstantValues, + + ) + + + + + + + + + + + =%d + + + + + + + =%ld + + + + + + + =0x%zx + + + + + + + =%f + + + + + + + =%c + + + + + + + =0x%x + + + + + , + + + + + + + + + =%s + + + + + , + + ? "true" : "false" + + + + + + + + + + + diff --git a/hotspot/src/share/vm/prims/jvmtiEnv.cpp b/hotspot/src/share/vm/prims/jvmtiEnv.cpp new file mode 100644 index 00000000000..a19c48972c8 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEnv.cpp @@ -0,0 +1,3358 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiEnv.cpp.incl" + + +#define FIXLATER 0 // REMOVE this when completed. + + // FIXLATER: hook into JvmtiTrace +#define TraceJVMTICalls false + +JvmtiEnv::JvmtiEnv() : JvmtiEnvBase() { +} + +JvmtiEnv::~JvmtiEnv() { +} + +JvmtiEnv* +JvmtiEnv::create_a_jvmti() { + return new JvmtiEnv(); +} + +// VM operation class to copy jni function table at safepoint. +// More than one java threads or jvmti agents may be reading/ +// modifying jni function tables. To reduce the risk of bad +// interaction b/w these threads it is copied at safepoint. +class VM_JNIFunctionTableCopier : public VM_Operation { + private: + const struct JNINativeInterface_ *_function_table; + public: + VM_JNIFunctionTableCopier(const struct JNINativeInterface_ *func_tbl) { + _function_table = func_tbl; + }; + + VMOp_Type type() const { return VMOp_JNIFunctionTableCopier; } + void doit() { + copy_jni_function_table(_function_table); + }; +}; + +// +// Do not change the "prefix" marker below, everything above it is copied +// unchanged into the filled stub, everything below is controlled by the +// stub filler (only method bodies are carried forward, and then only for +// functionality still in the spec). +// +// end file prefix + + // + // Memory Management functions + // + +// mem_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::Allocate(jlong size, unsigned char** mem_ptr) { + return allocate(size, mem_ptr); +} /* end Allocate */ + + +// mem - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::Deallocate(unsigned char* mem) { + return deallocate(mem); +} /* end Deallocate */ + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// data - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::SetThreadLocalStorage(JavaThread* java_thread, const void* data) { + JvmtiThreadState* state = java_thread->jvmti_thread_state(); + if (state == NULL) { + if (data == NULL) { + // leaving state unset same as data set to NULL + return JVMTI_ERROR_NONE; + } + // otherwise, create the state + state = JvmtiThreadState::state_for(java_thread); + } + state->env_thread_state(this)->set_agent_thread_local_storage_data((void*)data); + return JVMTI_ERROR_NONE; +} /* end SetThreadLocalStorage */ + + +// Threads_lock NOT held +// thread - NOT pre-checked +// data_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) { + JavaThread* current_thread = JavaThread::current(); + if (thread == NULL) { + JvmtiThreadState* state = current_thread->jvmti_thread_state(); + *data_ptr = (state == NULL) ? NULL : + state->env_thread_state(this)->get_agent_thread_local_storage_data(); + } else { + + // jvmti_GetThreadLocalStorage is "in native" and doesn't transition + // the thread to _thread_in_vm. However, when the TLS for a thread + // other than the current thread is required we need to transition + // from native so as to resolve the jthread. + + ThreadInVMfromNative __tiv(current_thread); + __ENTRY(jvmtiError, JvmtiEnv::GetThreadLocalStorage , current_thread) + debug_only(VMNativeEntryWrapper __vew;) + + oop thread_oop = JNIHandles::resolve_external_guard(thread); + if (thread_oop == NULL) { + return JVMTI_ERROR_INVALID_THREAD; + } + if (!thread_oop->is_a(SystemDictionary::thread_klass())) { + return JVMTI_ERROR_INVALID_THREAD; + } + JavaThread* java_thread = java_lang_Thread::thread(thread_oop); + if (java_thread == NULL) { + return JVMTI_ERROR_THREAD_NOT_ALIVE; + } + JvmtiThreadState* state = java_thread->jvmti_thread_state(); + *data_ptr = (state == NULL) ? NULL : + state->env_thread_state(this)->get_agent_thread_local_storage_data(); + } + return JVMTI_ERROR_NONE; +} /* end GetThreadLocalStorage */ + + // + // Class functions + // + +// class_count_ptr - pre-checked for NULL +// classes_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetLoadedClasses(jint* class_count_ptr, jclass** classes_ptr) { + return JvmtiGetLoadedClasses::getLoadedClasses(this, class_count_ptr, classes_ptr); +} /* end GetLoadedClasses */ + + +// initiating_loader - NULL is a valid value, must be checked +// class_count_ptr - pre-checked for NULL +// classes_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetClassLoaderClasses(jobject initiating_loader, jint* class_count_ptr, jclass** classes_ptr) { + return JvmtiGetLoadedClasses::getClassLoaderClasses(this, initiating_loader, + class_count_ptr, classes_ptr); +} /* end GetClassLoaderClasses */ + +// k_mirror - may be primitive, this must be checked +// is_modifiable_class_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::IsModifiableClass(oop k_mirror, jboolean* is_modifiable_class_ptr) { + *is_modifiable_class_ptr = VM_RedefineClasses::is_modifiable_class(k_mirror)? + JNI_TRUE : JNI_FALSE; + return JVMTI_ERROR_NONE; +} /* end IsModifiableClass */ + +// class_count - pre-checked to be greater than or equal to 0 +// classes - pre-checked for NULL +jvmtiError +JvmtiEnv::RetransformClasses(jint class_count, const jclass* classes) { +//TODO: add locking + + int index; + JavaThread* current_thread = JavaThread::current(); + ResourceMark rm(current_thread); + + jvmtiClassDefinition* class_definitions = + NEW_RESOURCE_ARRAY(jvmtiClassDefinition, class_count); + NULL_CHECK(class_definitions, JVMTI_ERROR_OUT_OF_MEMORY); + + for (index = 0; index < class_count; index++) { + HandleMark hm(current_thread); + + jclass jcls = classes[index]; + oop k_mirror = JNIHandles::resolve_external_guard(jcls); + if (k_mirror == NULL) { + return JVMTI_ERROR_INVALID_CLASS; + } + if (!k_mirror->is_a(SystemDictionary::class_klass())) { + return JVMTI_ERROR_INVALID_CLASS; + } + + if (java_lang_Class::is_primitive(k_mirror)) { + return JVMTI_ERROR_UNMODIFIABLE_CLASS; + } + + klassOop k_oop = java_lang_Class::as_klassOop(k_mirror); + KlassHandle klass(current_thread, k_oop); + + jint status = klass->jvmti_class_status(); + if (status & (JVMTI_CLASS_STATUS_ERROR)) { + return JVMTI_ERROR_INVALID_CLASS; + } + if (status & (JVMTI_CLASS_STATUS_ARRAY)) { + return JVMTI_ERROR_UNMODIFIABLE_CLASS; + } + + instanceKlassHandle ikh(current_thread, k_oop); + if (ikh->get_cached_class_file_bytes() == NULL) { + // not cached, we need to reconstitute the class file from VM representation + constantPoolHandle constants(current_thread, ikh->constants()); + ObjectLocker ol(constants, current_thread); // lock constant pool while we query it + + JvmtiClassFileReconstituter reconstituter(ikh); + if (reconstituter.get_error() != JVMTI_ERROR_NONE) { + return reconstituter.get_error(); + } + + class_definitions[index].class_byte_count = (jint)reconstituter.class_file_size(); + class_definitions[index].class_bytes = (unsigned char*) + reconstituter.class_file_bytes(); + } else { + // it is cached, get it from the cache + class_definitions[index].class_byte_count = ikh->get_cached_class_file_len(); + class_definitions[index].class_bytes = ikh->get_cached_class_file_bytes(); + } + class_definitions[index].klass = jcls; + } + VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_retransform); + VMThread::execute(&op); + return (op.check_error()); +} /* end RetransformClasses */ + + +// class_count - pre-checked to be greater than or equal to 0 +// class_definitions - pre-checked for NULL +jvmtiError +JvmtiEnv::RedefineClasses(jint class_count, const jvmtiClassDefinition* class_definitions) { +//TODO: add locking + VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_redefine); + VMThread::execute(&op); + return (op.check_error()); +} /* end RedefineClasses */ + + + // + // Object functions + // + +// size_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetObjectSize(jobject object, jlong* size_ptr) { + oop mirror = JNIHandles::resolve_external_guard(object); + NULL_CHECK(mirror, JVMTI_ERROR_INVALID_OBJECT); + + if (mirror->klass() == SystemDictionary::class_klass()) { + if (!java_lang_Class::is_primitive(mirror)) { + mirror = java_lang_Class::as_klassOop(mirror); + assert(mirror != NULL, "class for non-primitive mirror must exist"); + } + } + + *size_ptr = mirror->size() * wordSize; + return JVMTI_ERROR_NONE; +} /* end GetObjectSize */ + + // + // Method functions + // + +// prefix - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::SetNativeMethodPrefix(const char* prefix) { + return prefix == NULL? + SetNativeMethodPrefixes(0, NULL) : + SetNativeMethodPrefixes(1, (char**)&prefix); +} /* end SetNativeMethodPrefix */ + + +// prefix_count - pre-checked to be greater than or equal to 0 +// prefixes - pre-checked for NULL +jvmtiError +JvmtiEnv::SetNativeMethodPrefixes(jint prefix_count, char** prefixes) { + // Have to grab JVMTI thread state lock to be sure that some thread + // isn't accessing the prefixes at the same time we are setting them. + // No locks during VM bring-up. + if (Threads::number_of_threads() == 0) { + return set_native_method_prefixes(prefix_count, prefixes); + } else { + MutexLocker mu(JvmtiThreadState_lock); + return set_native_method_prefixes(prefix_count, prefixes); + } +} /* end SetNativeMethodPrefixes */ + + // + // Event Management functions + // + +// callbacks - NULL is a valid value, must be checked +// size_of_callbacks - pre-checked to be greater than or equal to 0 +jvmtiError +JvmtiEnv::SetEventCallbacks(const jvmtiEventCallbacks* callbacks, jint size_of_callbacks) { + JvmtiEventController::set_event_callbacks(this, callbacks, size_of_callbacks); + return JVMTI_ERROR_NONE; +} /* end SetEventCallbacks */ + + +// event_thread - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::SetEventNotificationMode(jvmtiEventMode mode, jvmtiEvent event_type, jthread event_thread, ...) { + JavaThread* java_thread = NULL; + if (event_thread != NULL) { + oop thread_oop = JNIHandles::resolve_external_guard(event_thread); + if (thread_oop == NULL) { + return JVMTI_ERROR_INVALID_THREAD; + } + if (!thread_oop->is_a(SystemDictionary::thread_klass())) { + return JVMTI_ERROR_INVALID_THREAD; + } + java_thread = java_lang_Thread::thread(thread_oop); + if (java_thread == NULL) { + return JVMTI_ERROR_THREAD_NOT_ALIVE; + } + } + + // event_type must be valid + if (!JvmtiEventController::is_valid_event_type(event_type)) { + return JVMTI_ERROR_INVALID_EVENT_TYPE; + } + + // global events cannot be controlled at thread level. + if (java_thread != NULL && JvmtiEventController::is_global_event(event_type)) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + + bool enabled = (mode == JVMTI_ENABLE); + + // assure that needed capabilities are present + if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) { + return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; + } + + if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { + record_class_file_load_hook_enabled(); + } + JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled); + + return JVMTI_ERROR_NONE; +} /* end SetEventNotificationMode */ + + // + // Capability functions + // + +// capabilities_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetPotentialCapabilities(jvmtiCapabilities* capabilities_ptr) { + JvmtiManageCapabilities::get_potential_capabilities(get_capabilities(), + get_prohibited_capabilities(), + capabilities_ptr); + return JVMTI_ERROR_NONE; +} /* end GetPotentialCapabilities */ + + +// capabilities_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::AddCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return JvmtiManageCapabilities::add_capabilities(get_capabilities(), + get_prohibited_capabilities(), + capabilities_ptr, + get_capabilities()); +} /* end AddCapabilities */ + + +// capabilities_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::RelinquishCapabilities(const jvmtiCapabilities* capabilities_ptr) { + JvmtiManageCapabilities::relinquish_capabilities(get_capabilities(), capabilities_ptr, get_capabilities()); + return JVMTI_ERROR_NONE; +} /* end RelinquishCapabilities */ + + +// capabilities_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetCapabilities(jvmtiCapabilities* capabilities_ptr) { + JvmtiManageCapabilities::copy_capabilities(get_capabilities(), capabilities_ptr); + return JVMTI_ERROR_NONE; +} /* end GetCapabilities */ + + // + // Class Loader Search functions + // + +// segment - pre-checked for NULL +jvmtiError +JvmtiEnv::AddToBootstrapClassLoaderSearch(const char* segment) { + jvmtiPhase phase = get_phase(); + if (phase == JVMTI_PHASE_ONLOAD) { + Arguments::append_sysclasspath(segment); + return JVMTI_ERROR_NONE; + } else { + assert(phase == JVMTI_PHASE_LIVE, "sanity check"); + + // create the zip entry + ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment); + if (zip_entry == NULL) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + + // lock the loader + Thread* thread = Thread::current(); + HandleMark hm; + Handle loader_lock = Handle(thread, SystemDictionary::system_loader_lock()); + + ObjectLocker ol(loader_lock, thread); + + // add the jar file to the bootclasspath + if (TraceClassLoading) { + tty->print_cr("[Opened %s]", zip_entry->name()); + } + ClassLoader::add_to_list(zip_entry); + return JVMTI_ERROR_NONE; + } + +} /* end AddToBootstrapClassLoaderSearch */ + + +// segment - pre-checked for NULL +jvmtiError +JvmtiEnv::AddToSystemClassLoaderSearch(const char* segment) { + jvmtiPhase phase = get_phase(); + + if (phase == JVMTI_PHASE_ONLOAD) { + for (SystemProperty* p = Arguments::system_properties(); p != NULL; p = p->next()) { + if (strcmp("java.class.path", p->key()) == 0) { + p->append_value(segment); + break; + } + } + return JVMTI_ERROR_NONE; + } else { + HandleMark hm; + + assert(phase == JVMTI_PHASE_LIVE, "sanity check"); + + // create the zip entry (which will open the zip file and hence + // check that the segment is indeed a zip file). + ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment); + if (zip_entry == NULL) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + delete zip_entry; // no longer needed + + // lock the loader + Thread* THREAD = Thread::current(); + Handle loader = Handle(THREAD, SystemDictionary::java_system_loader()); + + ObjectLocker ol(loader, THREAD); + + // need the path as java.lang.String + Handle path = java_lang_String::create_from_str(segment, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return JVMTI_ERROR_INTERNAL; + } + + instanceKlassHandle loader_ik(THREAD, loader->klass()); + + // Invoke the appendToClassPathForInstrumentation method - if the method + // is not found it means the loader doesn't support adding to the class path + // in the live phase. + { + JavaValue res(T_VOID); + JavaCalls::call_special(&res, + loader, + loader_ik, + vmSymbolHandles::appendToClassPathForInstrumentation_name(), + vmSymbolHandles::appendToClassPathForInstrumentation_signature(), + path, + THREAD); + if (HAS_PENDING_EXCEPTION) { + symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); + CLEAR_PENDING_EXCEPTION; + + if (ex_name == vmSymbols::java_lang_NoSuchMethodError()) { + return JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED; + } else { + return JVMTI_ERROR_INTERNAL; + } + } + } + + return JVMTI_ERROR_NONE; + } +} /* end AddToSystemClassLoaderSearch */ + + // + // General functions + // + +// phase_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetPhase(jvmtiPhase* phase_ptr) { + *phase_ptr = get_phase(); + return JVMTI_ERROR_NONE; +} /* end GetPhase */ + + +jvmtiError +JvmtiEnv::DisposeEnvironment() { + dispose(); + return JVMTI_ERROR_NONE; +} /* end DisposeEnvironment */ + + +// data - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::SetEnvironmentLocalStorage(const void* data) { + set_env_local_storage(data); + return JVMTI_ERROR_NONE; +} /* end SetEnvironmentLocalStorage */ + + +// data_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetEnvironmentLocalStorage(void** data_ptr) { + *data_ptr = (void*)get_env_local_storage(); + return JVMTI_ERROR_NONE; +} /* end GetEnvironmentLocalStorage */ + +// version_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetVersionNumber(jint* version_ptr) { + *version_ptr = JVMTI_VERSION; + return JVMTI_ERROR_NONE; +} /* end GetVersionNumber */ + + +// name_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetErrorName(jvmtiError error, char** name_ptr) { + if (error < JVMTI_ERROR_NONE || error > JVMTI_ERROR_MAX) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + const char *name = JvmtiUtil::error_name(error); + if (name == NULL) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + size_t len = strlen(name) + 1; + jvmtiError err = allocate(len, (unsigned char**)name_ptr); + if (err == JVMTI_ERROR_NONE) { + memcpy(*name_ptr, name, len); + } + return err; +} /* end GetErrorName */ + + +jvmtiError +JvmtiEnv::SetVerboseFlag(jvmtiVerboseFlag flag, jboolean value) { + switch (flag) { + case JVMTI_VERBOSE_OTHER: + // ignore + break; + case JVMTI_VERBOSE_CLASS: + TraceClassLoading = value != 0; + TraceClassUnloading = value != 0; + break; + case JVMTI_VERBOSE_GC: + PrintGC = value != 0; + TraceClassUnloading = value != 0; + break; + case JVMTI_VERBOSE_JNI: + PrintJNIResolving = value != 0; + break; + default: + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + }; + return JVMTI_ERROR_NONE; +} /* end SetVerboseFlag */ + + +// format_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetJLocationFormat(jvmtiJlocationFormat* format_ptr) { + *format_ptr = JVMTI_JLOCATION_JVMBCI; + return JVMTI_ERROR_NONE; +} /* end GetJLocationFormat */ + +#ifndef JVMTI_KERNEL + + // + // Thread functions + // + +// Threads_lock NOT held +// thread - NOT pre-checked +// thread_state_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetThreadState(jthread thread, jint* thread_state_ptr) { + jint state; + oop thread_oop; + JavaThread* thr; + + if (thread == NULL) { + thread_oop = JavaThread::current()->threadObj(); + } else { + thread_oop = JNIHandles::resolve_external_guard(thread); + } + + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::thread_klass())) { + return JVMTI_ERROR_INVALID_THREAD; + } + + // get most state bits + state = (jint)java_lang_Thread::get_thread_status(thread_oop); + + // add more state bits + thr = java_lang_Thread::thread(thread_oop); + if (thr != NULL) { + JavaThreadState jts = thr->thread_state(); + + if (thr->is_being_ext_suspended()) { + state |= JVMTI_THREAD_STATE_SUSPENDED; + } + if (jts == _thread_in_native) { + state |= JVMTI_THREAD_STATE_IN_NATIVE; + } + OSThread* osThread = thr->osthread(); + if (osThread != NULL && osThread->interrupted()) { + state |= JVMTI_THREAD_STATE_INTERRUPTED; + } + } + + *thread_state_ptr = state; + return JVMTI_ERROR_NONE; +} /* end GetThreadState */ + + +// thread_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetCurrentThread(jthread* thread_ptr) { + JavaThread* current_thread = JavaThread::current(); + *thread_ptr = (jthread)JNIHandles::make_local(current_thread, current_thread->threadObj()); + return JVMTI_ERROR_NONE; +} /* end GetCurrentThread */ + + +// threads_count_ptr - pre-checked for NULL +// threads_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetAllThreads(jint* threads_count_ptr, jthread** threads_ptr) { + int nthreads = 0; + Handle *thread_objs = NULL; + ResourceMark rm; + HandleMark hm; + + // enumerate threads (including agent threads) + ThreadsListEnumerator tle(Thread::current(), true); + nthreads = tle.num_threads(); + *threads_count_ptr = nthreads; + + if (nthreads == 0) { + *threads_ptr = NULL; + return JVMTI_ERROR_NONE; + } + + thread_objs = NEW_RESOURCE_ARRAY(Handle, nthreads); + NULL_CHECK(thread_objs, JVMTI_ERROR_OUT_OF_MEMORY); + + for (int i=0; i < nthreads; i++) { + thread_objs[i] = Handle(tle.get_threadObj(i)); + } + + // have to make global handles outside of Threads_lock + jthread *jthreads = new_jthreadArray(nthreads, thread_objs); + NULL_CHECK(jthreads, JVMTI_ERROR_OUT_OF_MEMORY); + + *threads_ptr = jthreads; + return JVMTI_ERROR_NONE; +} /* end GetAllThreads */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +jvmtiError +JvmtiEnv::SuspendThread(JavaThread* java_thread) { + // don't allow hidden thread suspend request. + if (java_thread->is_hidden_from_external_view()) { + return (JVMTI_ERROR_NONE); + } + + { + MutexLockerEx ml(java_thread->SR_lock(), Mutex::_no_safepoint_check_flag); + if (java_thread->is_external_suspend()) { + // don't allow nested external suspend requests. + return (JVMTI_ERROR_THREAD_SUSPENDED); + } + if (java_thread->is_exiting()) { // thread is in the process of exiting + return (JVMTI_ERROR_THREAD_NOT_ALIVE); + } + java_thread->set_external_suspend(); + } + + if (!JvmtiSuspendControl::suspend(java_thread)) { + // the thread was in the process of exiting + return (JVMTI_ERROR_THREAD_NOT_ALIVE); + } + return JVMTI_ERROR_NONE; +} /* end SuspendThread */ + + +// request_count - pre-checked to be greater than or equal to 0 +// request_list - pre-checked for NULL +// results - pre-checked for NULL +jvmtiError +JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvmtiError* results) { + int needSafepoint = 0; // > 0 if we need a safepoint + for (int i = 0; i < request_count; i++) { + JavaThread *java_thread = get_JavaThread(request_list[i]); + if (java_thread == NULL) { + results[i] = JVMTI_ERROR_INVALID_THREAD; + continue; + } + // the thread has not yet run or has exited (not on threads list) + if (java_thread->threadObj() == NULL) { + results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE; + continue; + } + if (java_lang_Thread::thread(java_thread->threadObj()) == NULL) { + results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE; + continue; + } + // don't allow hidden thread suspend request. + if (java_thread->is_hidden_from_external_view()) { + results[i] = JVMTI_ERROR_NONE; // indicate successful suspend + continue; + } + + { + MutexLockerEx ml(java_thread->SR_lock(), Mutex::_no_safepoint_check_flag); + if (java_thread->is_external_suspend()) { + // don't allow nested external suspend requests. + results[i] = JVMTI_ERROR_THREAD_SUSPENDED; + continue; + } + if (java_thread->is_exiting()) { // thread is in the process of exiting + results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE; + continue; + } + java_thread->set_external_suspend(); + } + if (java_thread->thread_state() == _thread_in_native) { + // We need to try and suspend native threads here. Threads in + // other states will self-suspend on their next transition. + if (!JvmtiSuspendControl::suspend(java_thread)) { + // The thread was in the process of exiting. Force another + // safepoint to make sure that this thread transitions. + needSafepoint++; + results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE; + continue; + } + } else { + needSafepoint++; + } + results[i] = JVMTI_ERROR_NONE; // indicate successful suspend + } + if (needSafepoint > 0) { + VM_ForceSafepoint vfs; + VMThread::execute(&vfs); + } + // per-thread suspend results returned via results parameter + return JVMTI_ERROR_NONE; +} /* end SuspendThreadList */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +jvmtiError +JvmtiEnv::ResumeThread(JavaThread* java_thread) { + // don't allow hidden thread resume request. + if (java_thread->is_hidden_from_external_view()) { + return JVMTI_ERROR_NONE; + } + + if (!java_thread->is_being_ext_suspended()) { + return JVMTI_ERROR_THREAD_NOT_SUSPENDED; + } + + if (!JvmtiSuspendControl::resume(java_thread)) { + return JVMTI_ERROR_INTERNAL; + } + return JVMTI_ERROR_NONE; +} /* end ResumeThread */ + + +// request_count - pre-checked to be greater than or equal to 0 +// request_list - pre-checked for NULL +// results - pre-checked for NULL +jvmtiError +JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmtiError* results) { + for (int i = 0; i < request_count; i++) { + JavaThread *java_thread = get_JavaThread(request_list[i]); + if (java_thread == NULL) { + results[i] = JVMTI_ERROR_INVALID_THREAD; + continue; + } + // don't allow hidden thread resume request. + if (java_thread->is_hidden_from_external_view()) { + results[i] = JVMTI_ERROR_NONE; // indicate successful resume + continue; + } + if (!java_thread->is_being_ext_suspended()) { + results[i] = JVMTI_ERROR_THREAD_NOT_SUSPENDED; + continue; + } + + if (!JvmtiSuspendControl::resume(java_thread)) { + results[i] = JVMTI_ERROR_INTERNAL; + continue; + } + + results[i] = JVMTI_ERROR_NONE; // indicate successful suspend + } + // per-thread resume results returned via results parameter + return JVMTI_ERROR_NONE; +} /* end ResumeThreadList */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +jvmtiError +JvmtiEnv::StopThread(JavaThread* java_thread, jobject exception) { + oop e = JNIHandles::resolve_external_guard(exception); + NULL_CHECK(e, JVMTI_ERROR_NULL_POINTER); + + JavaThread::send_async_exception(java_thread->threadObj(), e); + + return JVMTI_ERROR_NONE; + +} /* end StopThread */ + + +// Threads_lock NOT held +// thread - NOT pre-checked +jvmtiError +JvmtiEnv::InterruptThread(jthread thread) { + oop thread_oop = JNIHandles::resolve_external_guard(thread); + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::thread_klass())) + return JVMTI_ERROR_INVALID_THREAD; + + JavaThread* current_thread = JavaThread::current(); + + // Todo: this is a duplicate of JVM_Interrupt; share code in future + // Ensure that the C++ Thread and OSThread structures aren't freed before we operate + MutexLockerEx ml(current_thread->threadObj() == thread_oop ? NULL : Threads_lock); + // We need to re-resolve the java_thread, since a GC might have happened during the + // acquire of the lock + + JavaThread* java_thread = java_lang_Thread::thread(JNIHandles::resolve_external_guard(thread)); + NULL_CHECK(java_thread, JVMTI_ERROR_THREAD_NOT_ALIVE); + + Thread::interrupt(java_thread); + + return JVMTI_ERROR_NONE; +} /* end InterruptThread */ + + +// Threads_lock NOT held +// thread - NOT pre-checked +// info_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetThreadInfo(jthread thread, jvmtiThreadInfo* info_ptr) { + ResourceMark rm; + HandleMark hm; + + JavaThread* current_thread = JavaThread::current(); + + // if thread is NULL the current thread is used + oop thread_oop; + if (thread == NULL) { + thread_oop = current_thread->threadObj(); + } else { + thread_oop = JNIHandles::resolve_external_guard(thread); + } + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::thread_klass())) + return JVMTI_ERROR_INVALID_THREAD; + + Handle thread_obj(current_thread, thread_oop); + typeArrayHandle name; + ThreadPriority priority; + Handle thread_group; + Handle context_class_loader; + bool is_daemon; + + { MutexLocker mu(Threads_lock); + + name = typeArrayHandle(current_thread, java_lang_Thread::name(thread_obj())); + priority = java_lang_Thread::priority(thread_obj()); + thread_group = Handle(current_thread, java_lang_Thread::threadGroup(thread_obj())); + is_daemon = java_lang_Thread::is_daemon(thread_obj()); + + oop loader = java_lang_Thread::context_class_loader(thread_obj()); + context_class_loader = Handle(current_thread, loader); + } + { const char *n; + + if (name() != NULL) { + n = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); + } else { + n = UNICODE::as_utf8(NULL, 0); + } + + info_ptr->name = (char *) jvmtiMalloc(strlen(n)+1); + if (info_ptr->name == NULL) + return JVMTI_ERROR_OUT_OF_MEMORY; + + strcpy(info_ptr->name, n); + } + info_ptr->is_daemon = is_daemon; + info_ptr->priority = priority; + + info_ptr->context_class_loader = (context_class_loader.is_null()) ? NULL : + jni_reference(context_class_loader); + info_ptr->thread_group = jni_reference(thread_group); + + return JVMTI_ERROR_NONE; +} /* end GetThreadInfo */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// owned_monitor_count_ptr - pre-checked for NULL +// owned_monitors_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetOwnedMonitorInfo(JavaThread* java_thread, jint* owned_monitor_count_ptr, jobject** owned_monitors_ptr) { + jvmtiError err = JVMTI_ERROR_NONE; + JavaThread* calling_thread = JavaThread::current(); + + // growable array of jvmti monitors info on the C-heap + GrowableArray *owned_monitors_list = + new (ResourceObj::C_HEAP) GrowableArray(1, true); + + uint32_t debug_bits = 0; + if (is_thread_fully_suspended(java_thread, true, &debug_bits)) { + err = get_owned_monitors(calling_thread, java_thread, owned_monitors_list); + } else { + // JVMTI get monitors info at safepoint. Do not require target thread to + // be suspended. + VM_GetOwnedMonitorInfo op(this, calling_thread, java_thread, owned_monitors_list); + VMThread::execute(&op); + err = op.result(); + } + jint owned_monitor_count = owned_monitors_list->length(); + if (err == JVMTI_ERROR_NONE) { + if ((err = allocate(owned_monitor_count * sizeof(jobject *), + (unsigned char**)owned_monitors_ptr)) == JVMTI_ERROR_NONE) { + // copy into the returned array + for (int i = 0; i < owned_monitor_count; i++) { + (*owned_monitors_ptr)[i] = + ((jvmtiMonitorStackDepthInfo*)owned_monitors_list->at(i))->monitor; + } + *owned_monitor_count_ptr = owned_monitor_count; + } + } + // clean up. + for (int i = 0; i < owned_monitor_count; i++) { + deallocate((unsigned char*)owned_monitors_list->at(i)); + } + delete owned_monitors_list; + + return err; +} /* end GetOwnedMonitorInfo */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// monitor_info_count_ptr - pre-checked for NULL +// monitor_info_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetOwnedMonitorStackDepthInfo(JavaThread* java_thread, jint* monitor_info_count_ptr, jvmtiMonitorStackDepthInfo** monitor_info_ptr) { + jvmtiError err = JVMTI_ERROR_NONE; + JavaThread* calling_thread = JavaThread::current(); + + // growable array of jvmti monitors info on the C-heap + GrowableArray *owned_monitors_list = + new (ResourceObj::C_HEAP) GrowableArray(1, true); + + uint32_t debug_bits = 0; + if (is_thread_fully_suspended(java_thread, true, &debug_bits)) { + err = get_owned_monitors(calling_thread, java_thread, owned_monitors_list); + } else { + // JVMTI get owned monitors info at safepoint. Do not require target thread to + // be suspended. + VM_GetOwnedMonitorInfo op(this, calling_thread, java_thread, owned_monitors_list); + VMThread::execute(&op); + err = op.result(); + } + + jint owned_monitor_count = owned_monitors_list->length(); + if (err == JVMTI_ERROR_NONE) { + if ((err = allocate(owned_monitor_count * sizeof(jvmtiMonitorStackDepthInfo), + (unsigned char**)monitor_info_ptr)) == JVMTI_ERROR_NONE) { + // copy to output array. + for (int i = 0; i < owned_monitor_count; i++) { + (*monitor_info_ptr)[i].monitor = + ((jvmtiMonitorStackDepthInfo*)owned_monitors_list->at(i))->monitor; + (*monitor_info_ptr)[i].stack_depth = + ((jvmtiMonitorStackDepthInfo*)owned_monitors_list->at(i))->stack_depth; + } + } + *monitor_info_count_ptr = owned_monitor_count; + } + + // clean up. + for (int i = 0; i < owned_monitor_count; i++) { + deallocate((unsigned char*)owned_monitors_list->at(i)); + } + delete owned_monitors_list; + + return err; +} /* end GetOwnedMonitorStackDepthInfo */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// monitor_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetCurrentContendedMonitor(JavaThread* java_thread, jobject* monitor_ptr) { + jvmtiError err = JVMTI_ERROR_NONE; + uint32_t debug_bits = 0; + JavaThread* calling_thread = JavaThread::current(); + if (is_thread_fully_suspended(java_thread, true, &debug_bits)) { + err = get_current_contended_monitor(calling_thread, java_thread, monitor_ptr); + } else { + // get contended monitor information at safepoint. + VM_GetCurrentContendedMonitor op(this, calling_thread, java_thread, monitor_ptr); + VMThread::execute(&op); + err = op.result(); + } + return err; +} /* end GetCurrentContendedMonitor */ + + +// Threads_lock NOT held +// thread - NOT pre-checked +// proc - pre-checked for NULL +// arg - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::RunAgentThread(jthread thread, jvmtiStartFunction proc, const void* arg, jint priority) { + oop thread_oop = JNIHandles::resolve_external_guard(thread); + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::thread_klass())) { + return JVMTI_ERROR_INVALID_THREAD; + } + if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) { + return JVMTI_ERROR_INVALID_PRIORITY; + } + + //Thread-self + JavaThread* current_thread = JavaThread::current(); + + Handle thread_hndl(current_thread, thread_oop); + { + MutexLocker mu(Threads_lock); // grab Threads_lock + + JvmtiAgentThread *new_thread = new JvmtiAgentThread(this, proc, arg); + + // At this point it may be possible that no osthread was created for the + // JavaThread due to lack of memory. + if (new_thread == NULL || new_thread->osthread() == NULL) { + if (new_thread) delete new_thread; + return JVMTI_ERROR_OUT_OF_MEMORY; + } + + java_lang_Thread::set_thread(thread_hndl(), new_thread); + java_lang_Thread::set_priority(thread_hndl(), (ThreadPriority)priority); + java_lang_Thread::set_daemon(thread_hndl()); + + new_thread->set_threadObj(thread_hndl()); + Threads::add(new_thread); + Thread::start(new_thread); + } // unlock Threads_lock + + return JVMTI_ERROR_NONE; +} /* end RunAgentThread */ + + // + // Thread Group functions + // + +// group_count_ptr - pre-checked for NULL +// groups_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetTopThreadGroups(jint* group_count_ptr, jthreadGroup** groups_ptr) { + JavaThread* current_thread = JavaThread::current(); + + // Only one top level thread group now. + *group_count_ptr = 1; + + // Allocate memory to store global-refs to the thread groups. + // Assume this area is freed by caller. + *groups_ptr = (jthreadGroup *) jvmtiMalloc((sizeof(jthreadGroup)) * (*group_count_ptr)); + + NULL_CHECK(*groups_ptr, JVMTI_ERROR_OUT_OF_MEMORY); + + // Convert oop to Handle, then convert Handle to global-ref. + { + HandleMark hm(current_thread); + Handle system_thread_group(current_thread, Universe::system_thread_group()); + *groups_ptr[0] = jni_reference(system_thread_group); + } + + return JVMTI_ERROR_NONE; +} /* end GetTopThreadGroups */ + + +// info_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetThreadGroupInfo(jthreadGroup group, jvmtiThreadGroupInfo* info_ptr) { + ResourceMark rm; + HandleMark hm; + + JavaThread* current_thread = JavaThread::current(); + + Handle group_obj (current_thread, JNIHandles::resolve_external_guard(group)); + NULL_CHECK(group_obj(), JVMTI_ERROR_INVALID_THREAD_GROUP); + + typeArrayHandle name; + Handle parent_group; + bool is_daemon; + ThreadPriority max_priority; + + { MutexLocker mu(Threads_lock); + + name = typeArrayHandle(current_thread, + java_lang_ThreadGroup::name(group_obj())); + parent_group = Handle(current_thread, java_lang_ThreadGroup::parent(group_obj())); + is_daemon = java_lang_ThreadGroup::is_daemon(group_obj()); + max_priority = java_lang_ThreadGroup::maxPriority(group_obj()); + } + + info_ptr->is_daemon = is_daemon; + info_ptr->max_priority = max_priority; + info_ptr->parent = jni_reference(parent_group); + + if (name() != NULL) { + const char* n = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); + info_ptr->name = (char *)jvmtiMalloc(strlen(n)+1); + NULL_CHECK(info_ptr->name, JVMTI_ERROR_OUT_OF_MEMORY); + strcpy(info_ptr->name, n); + } else { + info_ptr->name = NULL; + } + + return JVMTI_ERROR_NONE; +} /* end GetThreadGroupInfo */ + + +// thread_count_ptr - pre-checked for NULL +// threads_ptr - pre-checked for NULL +// group_count_ptr - pre-checked for NULL +// groups_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetThreadGroupChildren(jthreadGroup group, jint* thread_count_ptr, jthread** threads_ptr, jint* group_count_ptr, jthreadGroup** groups_ptr) { + JavaThread* current_thread = JavaThread::current(); + oop group_obj = (oop) JNIHandles::resolve_external_guard(group); + NULL_CHECK(group_obj, JVMTI_ERROR_INVALID_THREAD_GROUP); + + Handle *thread_objs = NULL; + Handle *group_objs = NULL; + int nthreads = 0; + int ngroups = 0; + int hidden_threads = 0; + + ResourceMark rm; + HandleMark hm; + + Handle group_hdl(current_thread, group_obj); + + { MutexLocker mu(Threads_lock); + + nthreads = java_lang_ThreadGroup::nthreads(group_hdl()); + ngroups = java_lang_ThreadGroup::ngroups(group_hdl()); + + if (nthreads > 0) { + objArrayOop threads = java_lang_ThreadGroup::threads(group_hdl()); + assert(nthreads <= threads->length(), "too many threads"); + thread_objs = NEW_RESOURCE_ARRAY(Handle,nthreads); + for (int i=0, j=0; iobj_at(i); + assert(thread_obj != NULL, "thread_obj is NULL"); + JavaThread *javathread = java_lang_Thread::thread(thread_obj); + // Filter out hidden java threads. + if (javathread != NULL && javathread->is_hidden_from_external_view()) { + hidden_threads++; + continue; + } + thread_objs[j++] = Handle(current_thread, thread_obj); + } + nthreads -= hidden_threads; + } + if (ngroups > 0) { + objArrayOop groups = java_lang_ThreadGroup::groups(group_hdl()); + assert(ngroups <= groups->length(), "too many threads"); + group_objs = NEW_RESOURCE_ARRAY(Handle,ngroups); + for (int i=0; iobj_at(i); + assert(group_obj != NULL, "group_obj != NULL"); + group_objs[i] = Handle(current_thread, group_obj); + } + } + } + + // have to make global handles outside of Threads_lock + *group_count_ptr = ngroups; + *thread_count_ptr = nthreads; + *threads_ptr = new_jthreadArray(nthreads, thread_objs); + *groups_ptr = new_jthreadGroupArray(ngroups, group_objs); + if ((nthreads > 0) && (*threads_ptr == NULL)) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } + if ((ngroups > 0) && (*groups_ptr == NULL)) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } + + return JVMTI_ERROR_NONE; +} /* end GetThreadGroupChildren */ + + + // + // Stack Frame functions + // + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// max_frame_count - pre-checked to be greater than or equal to 0 +// frame_buffer - pre-checked for NULL +// count_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetStackTrace(JavaThread* java_thread, jint start_depth, jint max_frame_count, jvmtiFrameInfo* frame_buffer, jint* count_ptr) { + jvmtiError err = JVMTI_ERROR_NONE; + uint32_t debug_bits = 0; + if (is_thread_fully_suspended(java_thread, true, &debug_bits)) { + err = get_stack_trace(java_thread, start_depth, max_frame_count, frame_buffer, count_ptr); + } else { + // JVMTI get stack trace at safepoint. Do not require target thread to + // be suspended. + VM_GetStackTrace op(this, java_thread, start_depth, max_frame_count, frame_buffer, count_ptr); + VMThread::execute(&op); + err = op.result(); + } + + return err; +} /* end GetStackTrace */ + + +// max_frame_count - pre-checked to be greater than or equal to 0 +// stack_info_ptr - pre-checked for NULL +// thread_count_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetAllStackTraces(jint max_frame_count, jvmtiStackInfo** stack_info_ptr, jint* thread_count_ptr) { + jvmtiError err = JVMTI_ERROR_NONE; + JavaThread* calling_thread = JavaThread::current(); + + // JVMTI get stack traces at safepoint. + VM_GetAllStackTraces op(this, calling_thread, max_frame_count); + VMThread::execute(&op); + *thread_count_ptr = op.final_thread_count(); + *stack_info_ptr = op.stack_info(); + err = op.result(); + return err; +} /* end GetAllStackTraces */ + + +// thread_count - pre-checked to be greater than or equal to 0 +// thread_list - pre-checked for NULL +// max_frame_count - pre-checked to be greater than or equal to 0 +// stack_info_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetThreadListStackTraces(jint thread_count, const jthread* thread_list, jint max_frame_count, jvmtiStackInfo** stack_info_ptr) { + jvmtiError err = JVMTI_ERROR_NONE; + // JVMTI get stack traces at safepoint. + VM_GetThreadListStackTraces op(this, thread_count, thread_list, max_frame_count); + VMThread::execute(&op); + err = op.result(); + if (err == JVMTI_ERROR_NONE) { + *stack_info_ptr = op.stack_info(); + } + return err; +} /* end GetThreadListStackTraces */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// count_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetFrameCount(JavaThread* java_thread, jint* count_ptr) { + jvmtiError err = JVMTI_ERROR_NONE; + + // retrieve or create JvmtiThreadState. + JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread); + uint32_t debug_bits = 0; + if (is_thread_fully_suspended(java_thread, true, &debug_bits)) { + err = get_frame_count(state, count_ptr); + } else { + // get java stack frame count at safepoint. + VM_GetFrameCount op(this, state, count_ptr); + VMThread::execute(&op); + err = op.result(); + } + return err; +} /* end GetFrameCount */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +jvmtiError +JvmtiEnv::PopFrame(JavaThread* java_thread) { + JavaThread* current_thread = JavaThread::current(); + HandleMark hm(current_thread); + uint32_t debug_bits = 0; + + // Check if java_thread is fully suspended + if (!is_thread_fully_suspended(java_thread, true /* wait for suspend completion */, &debug_bits)) { + return JVMTI_ERROR_THREAD_NOT_SUSPENDED; + } + // Check to see if a PopFrame was already in progress + if (java_thread->popframe_condition() != JavaThread::popframe_inactive) { + // Probably possible for JVMTI clients to trigger this, but the + // JPDA backend shouldn't allow this to happen + return JVMTI_ERROR_INTERNAL; + } + + { + // Was workaround bug + // 4812902: popFrame hangs if the method is waiting at a synchronize + // Catch this condition and return an error to avoid hanging. + // Now JVMTI spec allows an implementation to bail out with an opaque frame error. + OSThread* osThread = java_thread->osthread(); + if (osThread->get_state() == MONITOR_WAIT) { + return JVMTI_ERROR_OPAQUE_FRAME; + } + } + + { + ResourceMark rm(current_thread); + // Check if there are more than one Java frame in this thread, that the top two frames + // are Java (not native) frames, and that there is no intervening VM frame + int frame_count = 0; + bool is_interpreted[2]; + intptr_t *frame_sp[2]; + // The 2-nd arg of constructor is needed to stop iterating at java entry frame. + for (vframeStream vfs(java_thread, true); !vfs.at_end(); vfs.next()) { + methodHandle mh(current_thread, vfs.method()); + if (mh->is_native()) return(JVMTI_ERROR_OPAQUE_FRAME); + is_interpreted[frame_count] = vfs.is_interpreted_frame(); + frame_sp[frame_count] = vfs.frame_id(); + if (++frame_count > 1) break; + } + if (frame_count < 2) { + // We haven't found two adjacent non-native Java frames on the top. + // There can be two situations here: + // 1. There are no more java frames + // 2. Two top java frames are separated by non-java native frames + if(vframeFor(java_thread, 1) == NULL) { + return JVMTI_ERROR_NO_MORE_FRAMES; + } else { + // Intervening non-java native or VM frames separate java frames. + // Current implementation does not support this. See bug #5031735. + // In theory it is possible to pop frames in such cases. + return JVMTI_ERROR_OPAQUE_FRAME; + } + } + + // If any of the top 2 frames is a compiled one, need to deoptimize it + for (int i = 0; i < 2; i++) { + if (!is_interpreted[i]) { + VM_DeoptimizeFrame op(java_thread, frame_sp[i]); + VMThread::execute(&op); + } + } + + // Update the thread state to reflect that the top frame is popped + // so that cur_stack_depth is maintained properly and all frameIDs + // are invalidated. + // The current frame will be popped later when the suspended thread + // is resumed and right before returning from VM to Java. + // (see call_VM_base() in assembler_.cpp). + + // It's fine to update the thread state here because no JVMTI events + // shall be posted for this PopFrame. + + // retreive or create the state + JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread); + + state->update_for_pop_top_frame(); + java_thread->set_popframe_condition(JavaThread::popframe_pending_bit); + // Set pending step flag for this popframe and it is cleared when next + // step event is posted. + state->set_pending_step_for_popframe(); + } + + return JVMTI_ERROR_NONE; +} /* end PopFrame */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +// method_ptr - pre-checked for NULL +// location_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetFrameLocation(JavaThread* java_thread, jint depth, jmethodID* method_ptr, jlocation* location_ptr) { + jvmtiError err = JVMTI_ERROR_NONE; + uint32_t debug_bits = 0; + + if (is_thread_fully_suspended(java_thread, true, &debug_bits)) { + err = get_frame_location(java_thread, depth, method_ptr, location_ptr); + } else { + // JVMTI get java stack frame location at safepoint. + VM_GetFrameLocation op(this, java_thread, depth, method_ptr, location_ptr); + VMThread::execute(&op); + err = op.result(); + } + return err; +} /* end GetFrameLocation */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +jvmtiError +JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) { + ResourceMark rm; + uint32_t debug_bits = 0; + + if (!JvmtiEnv::is_thread_fully_suspended(java_thread, true, &debug_bits)) { + return JVMTI_ERROR_THREAD_NOT_SUSPENDED; + } + + if (TraceJVMTICalls) { + JvmtiSuspendControl::print(); + } + + vframe *vf = vframeFor(java_thread, depth); + if (vf == NULL) { + return JVMTI_ERROR_NO_MORE_FRAMES; + } + + if (!vf->is_java_frame() || ((javaVFrame*) vf)->method()->is_native()) { + return JVMTI_ERROR_OPAQUE_FRAME; + } + + assert(vf->frame_pointer() != NULL, "frame pointer mustn't be NULL"); + + JvmtiThreadState *state = JvmtiThreadState::state_for(java_thread); + int frame_number = state->count_frames() - depth; + state->env_thread_state(this)->set_frame_pop(frame_number); + + return JVMTI_ERROR_NONE; +} /* end NotifyFramePop */ + + + // + // Force Early Return functions + // + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +jvmtiError +JvmtiEnv::ForceEarlyReturnObject(JavaThread* java_thread, jobject value) { + jvalue val; + val.l = value; + return force_early_return(java_thread, val, atos); +} /* end ForceEarlyReturnObject */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +jvmtiError +JvmtiEnv::ForceEarlyReturnInt(JavaThread* java_thread, jint value) { + jvalue val; + val.i = value; + return force_early_return(java_thread, val, itos); +} /* end ForceEarlyReturnInt */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +jvmtiError +JvmtiEnv::ForceEarlyReturnLong(JavaThread* java_thread, jlong value) { + jvalue val; + val.j = value; + return force_early_return(java_thread, val, ltos); +} /* end ForceEarlyReturnLong */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +jvmtiError +JvmtiEnv::ForceEarlyReturnFloat(JavaThread* java_thread, jfloat value) { + jvalue val; + val.f = value; + return force_early_return(java_thread, val, ftos); +} /* end ForceEarlyReturnFloat */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +jvmtiError +JvmtiEnv::ForceEarlyReturnDouble(JavaThread* java_thread, jdouble value) { + jvalue val; + val.d = value; + return force_early_return(java_thread, val, dtos); +} /* end ForceEarlyReturnDouble */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +jvmtiError +JvmtiEnv::ForceEarlyReturnVoid(JavaThread* java_thread) { + jvalue val; + val.j = 0L; + return force_early_return(java_thread, val, vtos); +} /* end ForceEarlyReturnVoid */ + + + // + // Heap functions + // + +// klass - NULL is a valid value, must be checked +// initial_object - NULL is a valid value, must be checked +// callbacks - pre-checked for NULL +// user_data - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::FollowReferences(jint heap_filter, jclass klass, jobject initial_object, const jvmtiHeapCallbacks* callbacks, const void* user_data) { + // check klass if provided + klassOop k_oop = NULL; + if (klass != NULL) { + oop k_mirror = JNIHandles::resolve_external_guard(klass); + if (k_mirror == NULL) { + return JVMTI_ERROR_INVALID_CLASS; + } + if (java_lang_Class::is_primitive(k_mirror)) { + return JVMTI_ERROR_NONE; + } + k_oop = java_lang_Class::as_klassOop(k_mirror); + if (k_oop == NULL) { + return JVMTI_ERROR_INVALID_CLASS; + } + } + + Thread *thread = Thread::current(); + HandleMark hm(thread); + KlassHandle kh (thread, k_oop); + + TraceTime t("FollowReferences", TraceJVMTIObjectTagging); + JvmtiTagMap::tag_map_for(this)->follow_references(heap_filter, kh, initial_object, callbacks, user_data); + return JVMTI_ERROR_NONE; +} /* end FollowReferences */ + + +// klass - NULL is a valid value, must be checked +// callbacks - pre-checked for NULL +// user_data - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::IterateThroughHeap(jint heap_filter, jclass klass, const jvmtiHeapCallbacks* callbacks, const void* user_data) { + // check klass if provided + klassOop k_oop = NULL; + if (klass != NULL) { + oop k_mirror = JNIHandles::resolve_external_guard(klass); + if (k_mirror == NULL) { + return JVMTI_ERROR_INVALID_CLASS; + } + if (java_lang_Class::is_primitive(k_mirror)) { + return JVMTI_ERROR_NONE; + } + k_oop = java_lang_Class::as_klassOop(k_mirror); + if (k_oop == NULL) { + return JVMTI_ERROR_INVALID_CLASS; + } + } + + Thread *thread = Thread::current(); + HandleMark hm(thread); + KlassHandle kh (thread, k_oop); + + TraceTime t("IterateThroughHeap", TraceJVMTIObjectTagging); + JvmtiTagMap::tag_map_for(this)->iterate_through_heap(heap_filter, kh, callbacks, user_data); + return JVMTI_ERROR_NONE; +} /* end IterateThroughHeap */ + + +// tag_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetTag(jobject object, jlong* tag_ptr) { + oop o = JNIHandles::resolve_external_guard(object); + NULL_CHECK(o, JVMTI_ERROR_INVALID_OBJECT); + *tag_ptr = JvmtiTagMap::tag_map_for(this)->get_tag(object); + return JVMTI_ERROR_NONE; +} /* end GetTag */ + + +jvmtiError +JvmtiEnv::SetTag(jobject object, jlong tag) { + oop o = JNIHandles::resolve_external_guard(object); + NULL_CHECK(o, JVMTI_ERROR_INVALID_OBJECT); + JvmtiTagMap::tag_map_for(this)->set_tag(object, tag); + return JVMTI_ERROR_NONE; +} /* end SetTag */ + + +// tag_count - pre-checked to be greater than or equal to 0 +// tags - pre-checked for NULL +// count_ptr - pre-checked for NULL +// object_result_ptr - NULL is a valid value, must be checked +// tag_result_ptr - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::GetObjectsWithTags(jint tag_count, const jlong* tags, jint* count_ptr, jobject** object_result_ptr, jlong** tag_result_ptr) { + TraceTime t("GetObjectsWithTags", TraceJVMTIObjectTagging); + return JvmtiTagMap::tag_map_for(this)->get_objects_with_tags((jlong*)tags, tag_count, count_ptr, object_result_ptr, tag_result_ptr); +} /* end GetObjectsWithTags */ + + +jvmtiError +JvmtiEnv::ForceGarbageCollection() { + Universe::heap()->collect(GCCause::_jvmti_force_gc); + return JVMTI_ERROR_NONE; +} /* end ForceGarbageCollection */ + + + // + // Heap (1.0) functions + // + +// object_reference_callback - pre-checked for NULL +// user_data - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::IterateOverObjectsReachableFromObject(jobject object, jvmtiObjectReferenceCallback object_reference_callback, const void* user_data) { + oop o = JNIHandles::resolve_external_guard(object); + NULL_CHECK(o, JVMTI_ERROR_INVALID_OBJECT); + JvmtiTagMap::tag_map_for(this)->iterate_over_objects_reachable_from_object(object, object_reference_callback, user_data); + return JVMTI_ERROR_NONE; +} /* end IterateOverObjectsReachableFromObject */ + + +// heap_root_callback - NULL is a valid value, must be checked +// stack_ref_callback - NULL is a valid value, must be checked +// object_ref_callback - NULL is a valid value, must be checked +// user_data - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::IterateOverReachableObjects(jvmtiHeapRootCallback heap_root_callback, jvmtiStackReferenceCallback stack_ref_callback, jvmtiObjectReferenceCallback object_ref_callback, const void* user_data) { + TraceTime t("IterateOverReachableObjects", TraceJVMTIObjectTagging); + JvmtiTagMap::tag_map_for(this)->iterate_over_reachable_objects(heap_root_callback, stack_ref_callback, object_ref_callback, user_data); + return JVMTI_ERROR_NONE; +} /* end IterateOverReachableObjects */ + + +// heap_object_callback - pre-checked for NULL +// user_data - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::IterateOverHeap(jvmtiHeapObjectFilter object_filter, jvmtiHeapObjectCallback heap_object_callback, const void* user_data) { + TraceTime t("IterateOverHeap", TraceJVMTIObjectTagging); + Thread *thread = Thread::current(); + HandleMark hm(thread); + JvmtiTagMap::tag_map_for(this)->iterate_over_heap(object_filter, KlassHandle(), heap_object_callback, user_data); + return JVMTI_ERROR_NONE; +} /* end IterateOverHeap */ + + +// k_mirror - may be primitive, this must be checked +// heap_object_callback - pre-checked for NULL +// user_data - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::IterateOverInstancesOfClass(oop k_mirror, jvmtiHeapObjectFilter object_filter, jvmtiHeapObjectCallback heap_object_callback, const void* user_data) { + if (java_lang_Class::is_primitive(k_mirror)) { + // DO PRIMITIVE CLASS PROCESSING + return JVMTI_ERROR_NONE; + } + klassOop k_oop = java_lang_Class::as_klassOop(k_mirror); + if (k_oop == NULL) { + return JVMTI_ERROR_INVALID_CLASS; + } + Thread *thread = Thread::current(); + HandleMark hm(thread); + KlassHandle klass (thread, k_oop); + TraceTime t("IterateOverInstancesOfClass", TraceJVMTIObjectTagging); + JvmtiTagMap::tag_map_for(this)->iterate_over_heap(object_filter, klass, heap_object_callback, user_data); + return JVMTI_ERROR_NONE; +} /* end IterateOverInstancesOfClass */ + + + // + // Local Variable functions + // + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +// value_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetLocalObject(JavaThread* java_thread, jint depth, jint slot, jobject* value_ptr) { + JavaThread* current_thread = JavaThread::current(); + // rm object is created to clean up the javaVFrame created in + // doit_prologue(), but after doit() is finished with it. + ResourceMark rm(current_thread); + + VM_GetOrSetLocal op(java_thread, current_thread, depth, slot); + VMThread::execute(&op); + jvmtiError err = op.result(); + if (err != JVMTI_ERROR_NONE) { + return err; + } else { + *value_ptr = op.value().l; + return JVMTI_ERROR_NONE; + } +} /* end GetLocalObject */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +// value_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetLocalInt(JavaThread* java_thread, jint depth, jint slot, jint* value_ptr) { + // rm object is created to clean up the javaVFrame created in + // doit_prologue(), but after doit() is finished with it. + ResourceMark rm; + + VM_GetOrSetLocal op(java_thread, depth, slot, T_INT); + VMThread::execute(&op); + *value_ptr = op.value().i; + return op.result(); +} /* end GetLocalInt */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +// value_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetLocalLong(JavaThread* java_thread, jint depth, jint slot, jlong* value_ptr) { + // rm object is created to clean up the javaVFrame created in + // doit_prologue(), but after doit() is finished with it. + ResourceMark rm; + + VM_GetOrSetLocal op(java_thread, depth, slot, T_LONG); + VMThread::execute(&op); + *value_ptr = op.value().j; + return op.result(); +} /* end GetLocalLong */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +// value_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetLocalFloat(JavaThread* java_thread, jint depth, jint slot, jfloat* value_ptr) { + // rm object is created to clean up the javaVFrame created in + // doit_prologue(), but after doit() is finished with it. + ResourceMark rm; + + VM_GetOrSetLocal op(java_thread, depth, slot, T_FLOAT); + VMThread::execute(&op); + *value_ptr = op.value().f; + return op.result(); +} /* end GetLocalFloat */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +// value_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetLocalDouble(JavaThread* java_thread, jint depth, jint slot, jdouble* value_ptr) { + // rm object is created to clean up the javaVFrame created in + // doit_prologue(), but after doit() is finished with it. + ResourceMark rm; + + VM_GetOrSetLocal op(java_thread, depth, slot, T_DOUBLE); + VMThread::execute(&op); + *value_ptr = op.value().d; + return op.result(); +} /* end GetLocalDouble */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +jvmtiError +JvmtiEnv::SetLocalObject(JavaThread* java_thread, jint depth, jint slot, jobject value) { + // rm object is created to clean up the javaVFrame created in + // doit_prologue(), but after doit() is finished with it. + ResourceMark rm; + jvalue val; + val.l = value; + VM_GetOrSetLocal op(java_thread, depth, slot, T_OBJECT, val); + VMThread::execute(&op); + return op.result(); +} /* end SetLocalObject */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +jvmtiError +JvmtiEnv::SetLocalInt(JavaThread* java_thread, jint depth, jint slot, jint value) { + // rm object is created to clean up the javaVFrame created in + // doit_prologue(), but after doit() is finished with it. + ResourceMark rm; + jvalue val; + val.i = value; + VM_GetOrSetLocal op(java_thread, depth, slot, T_INT, val); + VMThread::execute(&op); + return op.result(); +} /* end SetLocalInt */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +jvmtiError +JvmtiEnv::SetLocalLong(JavaThread* java_thread, jint depth, jint slot, jlong value) { + // rm object is created to clean up the javaVFrame created in + // doit_prologue(), but after doit() is finished with it. + ResourceMark rm; + jvalue val; + val.j = value; + VM_GetOrSetLocal op(java_thread, depth, slot, T_LONG, val); + VMThread::execute(&op); + return op.result(); +} /* end SetLocalLong */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +jvmtiError +JvmtiEnv::SetLocalFloat(JavaThread* java_thread, jint depth, jint slot, jfloat value) { + // rm object is created to clean up the javaVFrame created in + // doit_prologue(), but after doit() is finished with it. + ResourceMark rm; + jvalue val; + val.f = value; + VM_GetOrSetLocal op(java_thread, depth, slot, T_FLOAT, val); + VMThread::execute(&op); + return op.result(); +} /* end SetLocalFloat */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// java_thread - unchecked +// depth - pre-checked as non-negative +jvmtiError +JvmtiEnv::SetLocalDouble(JavaThread* java_thread, jint depth, jint slot, jdouble value) { + // rm object is created to clean up the javaVFrame created in + // doit_prologue(), but after doit() is finished with it. + ResourceMark rm; + jvalue val; + val.d = value; + VM_GetOrSetLocal op(java_thread, depth, slot, T_DOUBLE, val); + VMThread::execute(&op); + return op.result(); +} /* end SetLocalDouble */ + + + // + // Breakpoint functions + // + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +jvmtiError +JvmtiEnv::SetBreakpoint(methodOop method_oop, jlocation location) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + if (location < 0) { // simple invalid location check first + return JVMTI_ERROR_INVALID_LOCATION; + } + // verify that the breakpoint is not past the end of the method + if (location >= (jlocation) method_oop->code_size()) { + return JVMTI_ERROR_INVALID_LOCATION; + } + + ResourceMark rm; + JvmtiBreakpoint bp(method_oop, location); + JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints(); + if (jvmti_breakpoints.set(bp) == JVMTI_ERROR_DUPLICATE) + return JVMTI_ERROR_DUPLICATE; + + if (TraceJVMTICalls) { + jvmti_breakpoints.print(); + } + + return JVMTI_ERROR_NONE; +} /* end SetBreakpoint */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +jvmtiError +JvmtiEnv::ClearBreakpoint(methodOop method_oop, jlocation location) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + + if (location < 0) { // simple invalid location check first + return JVMTI_ERROR_INVALID_LOCATION; + } + + // verify that the breakpoint is not past the end of the method + if (location >= (jlocation) method_oop->code_size()) { + return JVMTI_ERROR_INVALID_LOCATION; + } + + JvmtiBreakpoint bp(method_oop, location); + + JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints(); + if (jvmti_breakpoints.clear(bp) == JVMTI_ERROR_NOT_FOUND) + return JVMTI_ERROR_NOT_FOUND; + + if (TraceJVMTICalls) { + jvmti_breakpoints.print(); + } + + return JVMTI_ERROR_NONE; +} /* end ClearBreakpoint */ + + + // + // Watched Field functions + // + +jvmtiError +JvmtiEnv::SetFieldAccessWatch(fieldDescriptor* fdesc_ptr) { + // make sure we haven't set this watch before + if (fdesc_ptr->is_field_access_watched()) return JVMTI_ERROR_DUPLICATE; + fdesc_ptr->set_is_field_access_watched(true); + update_klass_field_access_flag(fdesc_ptr); + + JvmtiEventController::change_field_watch(JVMTI_EVENT_FIELD_ACCESS, true); + + return JVMTI_ERROR_NONE; +} /* end SetFieldAccessWatch */ + + +jvmtiError +JvmtiEnv::ClearFieldAccessWatch(fieldDescriptor* fdesc_ptr) { + // make sure we have a watch to clear + if (!fdesc_ptr->is_field_access_watched()) return JVMTI_ERROR_NOT_FOUND; + fdesc_ptr->set_is_field_access_watched(false); + update_klass_field_access_flag(fdesc_ptr); + + JvmtiEventController::change_field_watch(JVMTI_EVENT_FIELD_ACCESS, false); + + return JVMTI_ERROR_NONE; +} /* end ClearFieldAccessWatch */ + + +jvmtiError +JvmtiEnv::SetFieldModificationWatch(fieldDescriptor* fdesc_ptr) { + // make sure we haven't set this watch before + if (fdesc_ptr->is_field_modification_watched()) return JVMTI_ERROR_DUPLICATE; + fdesc_ptr->set_is_field_modification_watched(true); + update_klass_field_access_flag(fdesc_ptr); + + JvmtiEventController::change_field_watch(JVMTI_EVENT_FIELD_MODIFICATION, true); + + return JVMTI_ERROR_NONE; +} /* end SetFieldModificationWatch */ + + +jvmtiError +JvmtiEnv::ClearFieldModificationWatch(fieldDescriptor* fdesc_ptr) { + // make sure we have a watch to clear + if (!fdesc_ptr->is_field_modification_watched()) return JVMTI_ERROR_NOT_FOUND; + fdesc_ptr->set_is_field_modification_watched(false); + update_klass_field_access_flag(fdesc_ptr); + + JvmtiEventController::change_field_watch(JVMTI_EVENT_FIELD_MODIFICATION, false); + + return JVMTI_ERROR_NONE; +} /* end ClearFieldModificationWatch */ + + // + // Class functions + // + + +// k_mirror - may be primitive, this must be checked +// signature_ptr - NULL is a valid value, must be checked +// generic_ptr - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::GetClassSignature(oop k_mirror, char** signature_ptr, char** generic_ptr) { + ResourceMark rm; + bool isPrimitive = java_lang_Class::is_primitive(k_mirror); + klassOop k = NULL; + if (!isPrimitive) { + k = java_lang_Class::as_klassOop(k_mirror); + NULL_CHECK(k, JVMTI_ERROR_INVALID_CLASS); + } + if (signature_ptr != NULL) { + char* result = NULL; + if (isPrimitive) { + char tchar = type2char(java_lang_Class::primitive_type(k_mirror)); + result = (char*) jvmtiMalloc(2); + result[0] = tchar; + result[1] = '\0'; + } else { + const char* class_sig = Klass::cast(k)->signature_name(); + result = (char *) jvmtiMalloc(strlen(class_sig)+1); + strcpy(result, class_sig); + } + *signature_ptr = result; + } + if (generic_ptr != NULL) { + *generic_ptr = NULL; + if (!isPrimitive && Klass::cast(k)->oop_is_instance()) { + symbolOop soo = instanceKlass::cast(k)->generic_signature(); + if (soo != NULL) { + const char *gen_sig = soo->as_C_string(); + if (gen_sig != NULL) { + char* gen_result; + jvmtiError err = allocate(strlen(gen_sig) + 1, + (unsigned char **)&gen_result); + if (err != JVMTI_ERROR_NONE) { + return err; + } + strcpy(gen_result, gen_sig); + *generic_ptr = gen_result; + } + } + } + } + return JVMTI_ERROR_NONE; +} /* end GetClassSignature */ + + +// k_mirror - may be primitive, this must be checked +// status_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetClassStatus(oop k_mirror, jint* status_ptr) { + jint result = 0; + if (java_lang_Class::is_primitive(k_mirror)) { + result |= JVMTI_CLASS_STATUS_PRIMITIVE; + } else { + klassOop k = java_lang_Class::as_klassOop(k_mirror); + NULL_CHECK(k, JVMTI_ERROR_INVALID_CLASS); + result = Klass::cast(k)->jvmti_class_status(); + } + *status_ptr = result; + + return JVMTI_ERROR_NONE; +} /* end GetClassStatus */ + + +// k_mirror - may be primitive, this must be checked +// source_name_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetSourceFileName(oop k_mirror, char** source_name_ptr) { + if (java_lang_Class::is_primitive(k_mirror)) { + return JVMTI_ERROR_ABSENT_INFORMATION; + } + klassOop k_klass = java_lang_Class::as_klassOop(k_mirror); + NULL_CHECK(k_klass, JVMTI_ERROR_INVALID_CLASS); + + if (!Klass::cast(k_klass)->oop_is_instance()) { + return JVMTI_ERROR_ABSENT_INFORMATION; + } + + symbolOop sfnOop = instanceKlass::cast(k_klass)->source_file_name(); + NULL_CHECK(sfnOop, JVMTI_ERROR_ABSENT_INFORMATION); + { + JavaThread* current_thread = JavaThread::current(); + ResourceMark rm(current_thread); + const char* sfncp = (const char*) sfnOop->as_C_string(); + *source_name_ptr = (char *) jvmtiMalloc(strlen(sfncp)+1); + strcpy(*source_name_ptr, sfncp); + } + + return JVMTI_ERROR_NONE; +} /* end GetSourceFileName */ + + +// k_mirror - may be primitive, this must be checked +// modifiers_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetClassModifiers(oop k_mirror, jint* modifiers_ptr) { + JavaThread* current_thread = JavaThread::current(); + jint result = 0; + if (!java_lang_Class::is_primitive(k_mirror)) { + klassOop k = java_lang_Class::as_klassOop(k_mirror); + NULL_CHECK(k, JVMTI_ERROR_INVALID_CLASS); + assert((Klass::cast(k)->oop_is_instance() || Klass::cast(k)->oop_is_array()), "should be an instance or an array klass"); + result = Klass::cast(k)->compute_modifier_flags(current_thread); + JavaThread* THREAD = current_thread; // pass to macros + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return JVMTI_ERROR_INTERNAL; + }; + + // Reset the deleted ACC_SUPER bit ( deleted in compute_modifier_flags()). + if(Klass::cast(k)->is_super()) { + result |= JVM_ACC_SUPER; + } + } else { + result = (JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC); + } + *modifiers_ptr = result; + + return JVMTI_ERROR_NONE; +} /* end GetClassModifiers */ + + +// k_mirror - may be primitive, this must be checked +// method_count_ptr - pre-checked for NULL +// methods_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetClassMethods(oop k_mirror, jint* method_count_ptr, jmethodID** methods_ptr) { + JavaThread* current_thread = JavaThread::current(); + HandleMark hm(current_thread); + + if (java_lang_Class::is_primitive(k_mirror)) { + *method_count_ptr = 0; + *methods_ptr = (jmethodID*) jvmtiMalloc(0 * sizeof(jmethodID)); + return JVMTI_ERROR_NONE; + } + klassOop k = java_lang_Class::as_klassOop(k_mirror); + NULL_CHECK(k, JVMTI_ERROR_INVALID_CLASS); + + // Return CLASS_NOT_PREPARED error as per JVMTI spec. + if (!(Klass::cast(k)->jvmti_class_status() & (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY) )) { + return JVMTI_ERROR_CLASS_NOT_PREPARED; + } + + if (!Klass::cast(k)->oop_is_instance()) { + *method_count_ptr = 0; + *methods_ptr = (jmethodID*) jvmtiMalloc(0 * sizeof(jmethodID)); + return JVMTI_ERROR_NONE; + } + instanceKlassHandle instanceK_h(current_thread, k); + // Allocate the result and fill it in + int result_length = instanceK_h->methods()->length(); + jmethodID* result_list = (jmethodID*)jvmtiMalloc(result_length * sizeof(jmethodID)); + int index; + if (JvmtiExport::can_maintain_original_method_order()) { + // Use the original method ordering indices stored in the class, so we can emit + // jmethodIDs in the order they appeared in the class file + for (index = 0; index < result_length; index++) { + methodOop m = methodOop(instanceK_h->methods()->obj_at(index)); + int original_index = instanceK_h->method_ordering()->int_at(index); + assert(original_index >= 0 && original_index < result_length, "invalid original method index"); + jmethodID id = m->jmethod_id(); + result_list[original_index] = id; + } + } else { + // otherwise just copy in any order + for (index = 0; index < result_length; index++) { + methodOop m = methodOop(instanceK_h->methods()->obj_at(index)); + jmethodID id = m->jmethod_id(); + result_list[index] = id; + } + } + // Fill in return value. + *method_count_ptr = result_length; + *methods_ptr = result_list; + + return JVMTI_ERROR_NONE; +} /* end GetClassMethods */ + + +// k_mirror - may be primitive, this must be checked +// field_count_ptr - pre-checked for NULL +// fields_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetClassFields(oop k_mirror, jint* field_count_ptr, jfieldID** fields_ptr) { + if (java_lang_Class::is_primitive(k_mirror)) { + *field_count_ptr = 0; + *fields_ptr = (jfieldID*) jvmtiMalloc(0 * sizeof(jfieldID)); + return JVMTI_ERROR_NONE; + } + JavaThread* current_thread = JavaThread::current(); + HandleMark hm(current_thread); + klassOop k = java_lang_Class::as_klassOop(k_mirror); + NULL_CHECK(k, JVMTI_ERROR_INVALID_CLASS); + + // Return CLASS_NOT_PREPARED error as per JVMTI spec. + if (!(Klass::cast(k)->jvmti_class_status() & (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY) )) { + return JVMTI_ERROR_CLASS_NOT_PREPARED; + } + + if (!Klass::cast(k)->oop_is_instance()) { + *field_count_ptr = 0; + *fields_ptr = (jfieldID*) jvmtiMalloc(0 * sizeof(jfieldID)); + return JVMTI_ERROR_NONE; + } + + + instanceKlassHandle instanceK_h(current_thread, k); + + int result_count = 0; + // First, count the fields. + FilteredFieldStream flds(instanceK_h, true, true); + result_count = flds.field_count(); + + // Allocate the result and fill it in + jfieldID* result_list = (jfieldID*) jvmtiMalloc(result_count * sizeof(jfieldID)); + // The JVMTI spec requires fields in the order they occur in the class file, + // this is the reverse order of what FieldStream hands out. + int id_index = (result_count - 1); + + for (FilteredFieldStream src_st(instanceK_h, true, true); !src_st.eos(); src_st.next()) { + result_list[id_index--] = jfieldIDWorkaround::to_jfieldID( + instanceK_h, src_st.offset(), + src_st.access_flags().is_static()); + } + assert(id_index == -1, "just checking"); + // Fill in the results + *field_count_ptr = result_count; + *fields_ptr = result_list; + + return JVMTI_ERROR_NONE; +} /* end GetClassFields */ + + +// k_mirror - may be primitive, this must be checked +// interface_count_ptr - pre-checked for NULL +// interfaces_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetImplementedInterfaces(oop k_mirror, jint* interface_count_ptr, jclass** interfaces_ptr) { + { + if (java_lang_Class::is_primitive(k_mirror)) { + *interface_count_ptr = 0; + *interfaces_ptr = (jclass*) jvmtiMalloc(0 * sizeof(jclass)); + return JVMTI_ERROR_NONE; + } + JavaThread* current_thread = JavaThread::current(); + HandleMark hm(current_thread); + klassOop k = java_lang_Class::as_klassOop(k_mirror); + NULL_CHECK(k, JVMTI_ERROR_INVALID_CLASS); + + // Return CLASS_NOT_PREPARED error as per JVMTI spec. + if (!(Klass::cast(k)->jvmti_class_status() & (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY) )) + return JVMTI_ERROR_CLASS_NOT_PREPARED; + + if (!Klass::cast(k)->oop_is_instance()) { + *interface_count_ptr = 0; + *interfaces_ptr = (jclass*) jvmtiMalloc(0 * sizeof(jclass)); + return JVMTI_ERROR_NONE; + } + + objArrayHandle interface_list(current_thread, instanceKlass::cast(k)->local_interfaces()); + const int result_length = (interface_list.is_null() ? 0 : interface_list->length()); + jclass* result_list = (jclass*) jvmtiMalloc(result_length * sizeof(jclass)); + for (int i_index = 0; i_index < result_length; i_index += 1) { + oop oop_at = interface_list->obj_at(i_index); + assert(oop_at->is_klass(), "interfaces must be klassOops"); + klassOop klassOop_at = klassOop(oop_at); // ???: is there a better way? + assert(Klass::cast(klassOop_at)->is_interface(), "interfaces must be interfaces"); + oop mirror_at = Klass::cast(klassOop_at)->java_mirror(); + Handle handle_at = Handle(current_thread, mirror_at); + result_list[i_index] = (jclass) jni_reference(handle_at); + } + *interface_count_ptr = result_length; + *interfaces_ptr = result_list; + } + + return JVMTI_ERROR_NONE; +} /* end GetImplementedInterfaces */ + + +// k_mirror - may be primitive, this must be checked +// minor_version_ptr - pre-checked for NULL +// major_version_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetClassVersionNumbers(oop k_mirror, jint* minor_version_ptr, jint* major_version_ptr) { + if (java_lang_Class::is_primitive(k_mirror)) { + return JVMTI_ERROR_ABSENT_INFORMATION; + } + klassOop k_oop = java_lang_Class::as_klassOop(k_mirror); + Thread *thread = Thread::current(); + HandleMark hm(thread); + KlassHandle klass(thread, k_oop); + + jint status = klass->jvmti_class_status(); + if (status & (JVMTI_CLASS_STATUS_ERROR)) { + return JVMTI_ERROR_INVALID_CLASS; + } + if (status & (JVMTI_CLASS_STATUS_ARRAY)) { + return JVMTI_ERROR_ABSENT_INFORMATION; + } + + instanceKlassHandle ik(thread, k_oop); + *minor_version_ptr = ik->minor_version(); + *major_version_ptr = ik->major_version(); + + return JVMTI_ERROR_NONE; +} /* end GetClassVersionNumbers */ + + +// k_mirror - may be primitive, this must be checked +// constant_pool_count_ptr - pre-checked for NULL +// constant_pool_byte_count_ptr - pre-checked for NULL +// constant_pool_bytes_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetConstantPool(oop k_mirror, jint* constant_pool_count_ptr, jint* constant_pool_byte_count_ptr, unsigned char** constant_pool_bytes_ptr) { + if (java_lang_Class::is_primitive(k_mirror)) { + return JVMTI_ERROR_ABSENT_INFORMATION; + } + + klassOop k_oop = java_lang_Class::as_klassOop(k_mirror); + Thread *thread = Thread::current(); + HandleMark hm(thread); + ResourceMark rm(thread); + KlassHandle klass(thread, k_oop); + + jint status = klass->jvmti_class_status(); + if (status & (JVMTI_CLASS_STATUS_ERROR)) { + return JVMTI_ERROR_INVALID_CLASS; + } + if (status & (JVMTI_CLASS_STATUS_ARRAY)) { + return JVMTI_ERROR_ABSENT_INFORMATION; + } + + instanceKlassHandle ikh(thread, k_oop); + constantPoolHandle constants(thread, ikh->constants()); + ObjectLocker ol(constants, thread); // lock constant pool while we query it + + JvmtiConstantPoolReconstituter reconstituter(ikh); + if (reconstituter.get_error() != JVMTI_ERROR_NONE) { + return reconstituter.get_error(); + } + + unsigned char *cpool_bytes; + int cpool_size = reconstituter.cpool_size(); + if (reconstituter.get_error() != JVMTI_ERROR_NONE) { + return reconstituter.get_error(); + } + jvmtiError res = allocate(cpool_size, &cpool_bytes); + if (res != JVMTI_ERROR_NONE) { + return res; + } + reconstituter.copy_cpool_bytes(cpool_bytes); + if (reconstituter.get_error() != JVMTI_ERROR_NONE) { + return reconstituter.get_error(); + } + + *constant_pool_count_ptr = constants->length(); + *constant_pool_byte_count_ptr = cpool_size; + *constant_pool_bytes_ptr = cpool_bytes; + + return JVMTI_ERROR_NONE; +} /* end GetConstantPool */ + + +// k_mirror - may be primitive, this must be checked +// is_interface_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::IsInterface(oop k_mirror, jboolean* is_interface_ptr) { + { + bool result = false; + if (!java_lang_Class::is_primitive(k_mirror)) { + klassOop k = java_lang_Class::as_klassOop(k_mirror); + if (k != NULL && Klass::cast(k)->is_interface()) { + result = true; + } + } + *is_interface_ptr = result; + } + + return JVMTI_ERROR_NONE; +} /* end IsInterface */ + + +// k_mirror - may be primitive, this must be checked +// is_array_class_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::IsArrayClass(oop k_mirror, jboolean* is_array_class_ptr) { + { + bool result = false; + if (!java_lang_Class::is_primitive(k_mirror)) { + klassOop k = java_lang_Class::as_klassOop(k_mirror); + if (k != NULL && Klass::cast(k)->oop_is_array()) { + result = true; + } + } + *is_array_class_ptr = result; + } + + return JVMTI_ERROR_NONE; +} /* end IsArrayClass */ + + +// k_mirror - may be primitive, this must be checked +// classloader_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetClassLoader(oop k_mirror, jobject* classloader_ptr) { + { + if (java_lang_Class::is_primitive(k_mirror)) { + *classloader_ptr = (jclass) jni_reference(Handle()); + return JVMTI_ERROR_NONE; + } + JavaThread* current_thread = JavaThread::current(); + HandleMark hm(current_thread); + klassOop k = java_lang_Class::as_klassOop(k_mirror); + NULL_CHECK(k, JVMTI_ERROR_INVALID_CLASS); + + oop result_oop = Klass::cast(k)->class_loader(); + if (result_oop == NULL) { + *classloader_ptr = (jclass) jni_reference(Handle()); + return JVMTI_ERROR_NONE; + } + Handle result_handle = Handle(current_thread, result_oop); + jclass result_jnihandle = (jclass) jni_reference(result_handle); + *classloader_ptr = result_jnihandle; + } + return JVMTI_ERROR_NONE; +} /* end GetClassLoader */ + + +// k_mirror - may be primitive, this must be checked +// source_debug_extension_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetSourceDebugExtension(oop k_mirror, char** source_debug_extension_ptr) { + { + if (java_lang_Class::is_primitive(k_mirror)) { + return JVMTI_ERROR_ABSENT_INFORMATION; + } + klassOop k = java_lang_Class::as_klassOop(k_mirror); + NULL_CHECK(k, JVMTI_ERROR_INVALID_CLASS); + if (!Klass::cast(k)->oop_is_instance()) { + return JVMTI_ERROR_ABSENT_INFORMATION; + } + symbolOop sdeOop = instanceKlass::cast(k)->source_debug_extension(); + NULL_CHECK(sdeOop, JVMTI_ERROR_ABSENT_INFORMATION); + + { + JavaThread* current_thread = JavaThread::current(); + ResourceMark rm(current_thread); + const char* sdecp = (const char*) sdeOop->as_C_string(); + *source_debug_extension_ptr = (char *) jvmtiMalloc(strlen(sdecp)+1); + strcpy(*source_debug_extension_ptr, sdecp); + } + } + + return JVMTI_ERROR_NONE; +} /* end GetSourceDebugExtension */ + + // + // Object functions + // + +// hash_code_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetObjectHashCode(jobject object, jint* hash_code_ptr) { + oop mirror = JNIHandles::resolve_external_guard(object); + NULL_CHECK(mirror, JVMTI_ERROR_INVALID_OBJECT); + NULL_CHECK(hash_code_ptr, JVMTI_ERROR_NULL_POINTER); + + { + jint result = (jint) mirror->identity_hash(); + *hash_code_ptr = result; + } + return JVMTI_ERROR_NONE; +} /* end GetObjectHashCode */ + + +// info_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetObjectMonitorUsage(jobject object, jvmtiMonitorUsage* info_ptr) { + JavaThread* calling_thread = JavaThread::current(); + jvmtiError err = get_object_monitor_usage(calling_thread, object, info_ptr); + if (err == JVMTI_ERROR_THREAD_NOT_SUSPENDED) { + // Some of the critical threads were not suspended. go to a safepoint and try again + VM_GetObjectMonitorUsage op(this, calling_thread, object, info_ptr); + VMThread::execute(&op); + err = op.result(); + } + return err; +} /* end GetObjectMonitorUsage */ + + + // + // Field functions + // + +// name_ptr - NULL is a valid value, must be checked +// signature_ptr - NULL is a valid value, must be checked +// generic_ptr - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::GetFieldName(fieldDescriptor* fdesc_ptr, char** name_ptr, char** signature_ptr, char** generic_ptr) { + JavaThread* current_thread = JavaThread::current(); + ResourceMark rm(current_thread); + if (name_ptr == NULL) { + // just don't return the name + } else { + const char* fieldName = fdesc_ptr->name()->as_C_string(); + *name_ptr = (char*) jvmtiMalloc(strlen(fieldName) + 1); + if (*name_ptr == NULL) + return JVMTI_ERROR_OUT_OF_MEMORY; + strcpy(*name_ptr, fieldName); + } + if (signature_ptr== NULL) { + // just don't return the signature + } else { + const char* fieldSignature = fdesc_ptr->signature()->as_C_string(); + *signature_ptr = (char*) jvmtiMalloc(strlen(fieldSignature) + 1); + if (*signature_ptr == NULL) + return JVMTI_ERROR_OUT_OF_MEMORY; + strcpy(*signature_ptr, fieldSignature); + } + if (generic_ptr != NULL) { + *generic_ptr = NULL; + symbolOop soop = fdesc_ptr->generic_signature(); + if (soop != NULL) { + const char* gen_sig = soop->as_C_string(); + if (gen_sig != NULL) { + jvmtiError err = allocate(strlen(gen_sig) + 1, (unsigned char **)generic_ptr); + if (err != JVMTI_ERROR_NONE) { + return err; + } + strcpy(*generic_ptr, gen_sig); + } + } + } + return JVMTI_ERROR_NONE; +} /* end GetFieldName */ + + +// declaring_class_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetFieldDeclaringClass(fieldDescriptor* fdesc_ptr, jclass* declaring_class_ptr) { + + *declaring_class_ptr = get_jni_class_non_null(fdesc_ptr->field_holder()); + return JVMTI_ERROR_NONE; +} /* end GetFieldDeclaringClass */ + + +// modifiers_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetFieldModifiers(fieldDescriptor* fdesc_ptr, jint* modifiers_ptr) { + + AccessFlags resultFlags = fdesc_ptr->access_flags(); + jint result = resultFlags.as_int(); + *modifiers_ptr = result; + + return JVMTI_ERROR_NONE; +} /* end GetFieldModifiers */ + + +// is_synthetic_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::IsFieldSynthetic(fieldDescriptor* fdesc_ptr, jboolean* is_synthetic_ptr) { + *is_synthetic_ptr = fdesc_ptr->is_synthetic(); + return JVMTI_ERROR_NONE; +} /* end IsFieldSynthetic */ + + + // + // Method functions + // + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// name_ptr - NULL is a valid value, must be checked +// signature_ptr - NULL is a valid value, must be checked +// generic_ptr - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::GetMethodName(methodOop method_oop, char** name_ptr, char** signature_ptr, char** generic_ptr) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + JavaThread* current_thread = JavaThread::current(); + + ResourceMark rm(current_thread); // get the utf8 name and signature + if (name_ptr == NULL) { + // just don't return the name + } else { + const char* utf8_name = (const char *) method_oop->name()->as_utf8(); + *name_ptr = (char *) jvmtiMalloc(strlen(utf8_name)+1); + strcpy(*name_ptr, utf8_name); + } + if (signature_ptr == NULL) { + // just don't return the signature + } else { + const char* utf8_signature = (const char *) method_oop->signature()->as_utf8(); + *signature_ptr = (char *) jvmtiMalloc(strlen(utf8_signature) + 1); + strcpy(*signature_ptr, utf8_signature); + } + + if (generic_ptr != NULL) { + *generic_ptr = NULL; + symbolOop soop = method_oop->generic_signature(); + if (soop != NULL) { + const char* gen_sig = soop->as_C_string(); + if (gen_sig != NULL) { + jvmtiError err = allocate(strlen(gen_sig) + 1, (unsigned char **)generic_ptr); + if (err != JVMTI_ERROR_NONE) { + return err; + } + strcpy(*generic_ptr, gen_sig); + } + } + } + return JVMTI_ERROR_NONE; +} /* end GetMethodName */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// declaring_class_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetMethodDeclaringClass(methodOop method_oop, jclass* declaring_class_ptr) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + (*declaring_class_ptr) = get_jni_class_non_null(method_oop->method_holder()); + return JVMTI_ERROR_NONE; +} /* end GetMethodDeclaringClass */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// modifiers_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetMethodModifiers(methodOop method_oop, jint* modifiers_ptr) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + (*modifiers_ptr) = method_oop->access_flags().as_int() & JVM_RECOGNIZED_METHOD_MODIFIERS; + return JVMTI_ERROR_NONE; +} /* end GetMethodModifiers */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// max_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetMaxLocals(methodOop method_oop, jint* max_ptr) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + // get max stack + (*max_ptr) = method_oop->max_locals(); + return JVMTI_ERROR_NONE; +} /* end GetMaxLocals */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// size_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetArgumentsSize(methodOop method_oop, jint* size_ptr) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + // get size of arguments + + (*size_ptr) = method_oop->size_of_parameters(); + return JVMTI_ERROR_NONE; +} /* end GetArgumentsSize */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// entry_count_ptr - pre-checked for NULL +// table_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetLineNumberTable(methodOop method_oop, jint* entry_count_ptr, jvmtiLineNumberEntry** table_ptr) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + if (!method_oop->has_linenumber_table()) { + return (JVMTI_ERROR_ABSENT_INFORMATION); + } + + // The line number table is compressed so we don't know how big it is until decompressed. + // Decompression is really fast so we just do it twice. + + // Compute size of table + jint num_entries = 0; + CompressedLineNumberReadStream stream(method_oop->compressed_linenumber_table()); + while (stream.read_pair()) { + num_entries++; + } + jvmtiLineNumberEntry *jvmti_table = + (jvmtiLineNumberEntry *)jvmtiMalloc(num_entries * (sizeof(jvmtiLineNumberEntry))); + + // Fill jvmti table + if (num_entries > 0) { + int index = 0; + CompressedLineNumberReadStream stream(method_oop->compressed_linenumber_table()); + while (stream.read_pair()) { + jvmti_table[index].start_location = (jlocation) stream.bci(); + jvmti_table[index].line_number = (jint) stream.line(); + index++; + } + assert(index == num_entries, "sanity check"); + } + + // Set up results + (*entry_count_ptr) = num_entries; + (*table_ptr) = jvmti_table; + + return JVMTI_ERROR_NONE; +} /* end GetLineNumberTable */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// start_location_ptr - pre-checked for NULL +// end_location_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetMethodLocation(methodOop method_oop, jlocation* start_location_ptr, jlocation* end_location_ptr) { + + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + // get start and end location + (*end_location_ptr) = (jlocation) (method_oop->code_size() - 1); + if (method_oop->code_size() == 0) { + // there is no code so there is no start location + (*start_location_ptr) = (jlocation)(-1); + } else { + (*start_location_ptr) = (jlocation)(0); + } + + return JVMTI_ERROR_NONE; +} /* end GetMethodLocation */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// entry_count_ptr - pre-checked for NULL +// table_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetLocalVariableTable(methodOop method_oop, jint* entry_count_ptr, jvmtiLocalVariableEntry** table_ptr) { + + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + JavaThread* current_thread = JavaThread::current(); + + // does the klass have any local variable information? + instanceKlass* ik = instanceKlass::cast(method_oop->method_holder()); + if (!ik->access_flags().has_localvariable_table()) { + return (JVMTI_ERROR_ABSENT_INFORMATION); + } + + constantPoolOop constants = method_oop->constants(); + NULL_CHECK(constants, JVMTI_ERROR_ABSENT_INFORMATION); + + // in the vm localvariable table representation, 6 consecutive elements in the table + // represent a 6-tuple of shorts + // [start_pc, length, name_index, descriptor_index, signature_index, index] + jint num_entries = method_oop->localvariable_table_length(); + jvmtiLocalVariableEntry *jvmti_table = (jvmtiLocalVariableEntry *) + jvmtiMalloc(num_entries * (sizeof(jvmtiLocalVariableEntry))); + + if (num_entries > 0) { + LocalVariableTableElement* table = method_oop->localvariable_table_start(); + for (int i = 0; i < num_entries; i++) { + // get the 5 tuple information from the vm table + jlocation start_location = (jlocation) table[i].start_bci; + jint length = (jint) table[i].length; + int name_index = (int) table[i].name_cp_index; + int signature_index = (int) table[i].descriptor_cp_index; + int generic_signature_index = (int) table[i].signature_cp_index; + jint slot = (jint) table[i].slot; + + // get utf8 name and signature + char *name_buf = NULL; + char *sig_buf = NULL; + char *gen_sig_buf = NULL; + { + ResourceMark rm(current_thread); + + const char *utf8_name = (const char *) constants->symbol_at(name_index)->as_utf8(); + name_buf = (char *) jvmtiMalloc(strlen(utf8_name)+1); + strcpy(name_buf, utf8_name); + + const char *utf8_signature = (const char *) constants->symbol_at(signature_index)->as_utf8(); + sig_buf = (char *) jvmtiMalloc(strlen(utf8_signature)+1); + strcpy(sig_buf, utf8_signature); + + if (generic_signature_index > 0) { + const char *utf8_gen_sign = (const char *) + constants->symbol_at(generic_signature_index)->as_utf8(); + gen_sig_buf = (char *) jvmtiMalloc(strlen(utf8_gen_sign)+1); + strcpy(gen_sig_buf, utf8_gen_sign); + } + } + + // fill in the jvmti local variable table + jvmti_table[i].start_location = start_location; + jvmti_table[i].length = length; + jvmti_table[i].name = name_buf; + jvmti_table[i].signature = sig_buf; + jvmti_table[i].generic_signature = gen_sig_buf; + jvmti_table[i].slot = slot; + } + } + + // set results + (*entry_count_ptr) = num_entries; + (*table_ptr) = jvmti_table; + + return JVMTI_ERROR_NONE; +} /* end GetLocalVariableTable */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// bytecode_count_ptr - pre-checked for NULL +// bytecodes_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetBytecodes(methodOop method_oop, jint* bytecode_count_ptr, unsigned char** bytecodes_ptr) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + + HandleMark hm; + methodHandle method(method_oop); + jint size = (jint)method->code_size(); + jvmtiError err = allocate(size, bytecodes_ptr); + if (err != JVMTI_ERROR_NONE) { + return err; + } + + (*bytecode_count_ptr) = size; + // get byte codes + JvmtiClassFileReconstituter::copy_bytecodes(method, *bytecodes_ptr); + + return JVMTI_ERROR_NONE; +} /* end GetBytecodes */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// is_native_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::IsMethodNative(methodOop method_oop, jboolean* is_native_ptr) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + (*is_native_ptr) = method_oop->is_native(); + return JVMTI_ERROR_NONE; +} /* end IsMethodNative */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// is_synthetic_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::IsMethodSynthetic(methodOop method_oop, jboolean* is_synthetic_ptr) { + NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID); + (*is_synthetic_ptr) = method_oop->is_synthetic(); + return JVMTI_ERROR_NONE; +} /* end IsMethodSynthetic */ + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method +// is_obsolete_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::IsMethodObsolete(methodOop method_oop, jboolean* is_obsolete_ptr) { + if (method_oop == NULL || method_oop->is_obsolete()) { + *is_obsolete_ptr = true; + } else { + *is_obsolete_ptr = false; + } + return JVMTI_ERROR_NONE; +} /* end IsMethodObsolete */ + + // + // Raw Monitor functions + // + +// name - pre-checked for NULL +// monitor_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::CreateRawMonitor(const char* name, jrawMonitorID* monitor_ptr) { + JvmtiRawMonitor* rmonitor = new JvmtiRawMonitor(name); + NULL_CHECK(rmonitor, JVMTI_ERROR_OUT_OF_MEMORY); + + *monitor_ptr = (jrawMonitorID)rmonitor; + + return JVMTI_ERROR_NONE; +} /* end CreateRawMonitor */ + + +// rmonitor - pre-checked for validity +jvmtiError +JvmtiEnv::DestroyRawMonitor(JvmtiRawMonitor * rmonitor) { + if (Threads::number_of_threads() == 0) { + // Remove this monitor from pending raw monitors list + // if it has entered in onload or start phase. + JvmtiPendingMonitors::destroy(rmonitor); + } else { + Thread* thread = Thread::current(); + if (rmonitor->is_entered(thread)) { + // The caller owns this monitor which we are about to destroy. + // We exit the underlying synchronization object so that the + // "delete monitor" call below can work without an assertion + // failure on systems that don't like destroying synchronization + // objects that are locked. + int r; + intptr_t recursion = rmonitor->recursions(); + for (intptr_t i=0; i <= recursion; i++) { + r = rmonitor->raw_exit(thread); + assert(r == ObjectMonitor::OM_OK, "raw_exit should have worked"); + if (r != ObjectMonitor::OM_OK) { // robustness + return JVMTI_ERROR_INTERNAL; + } + } + } + if (rmonitor->owner() != NULL) { + // The caller is trying to destroy a monitor that is locked by + // someone else. While this is not forbidden by the JVMTI + // spec, it will cause an assertion failure on systems that don't + // like destroying synchronization objects that are locked. + // We indicate a problem with the error return (and leak the + // monitor's memory). + return JVMTI_ERROR_NOT_MONITOR_OWNER; + } + } + + delete rmonitor; + + return JVMTI_ERROR_NONE; +} /* end DestroyRawMonitor */ + + +// rmonitor - pre-checked for validity +jvmtiError +JvmtiEnv::RawMonitorEnter(JvmtiRawMonitor * rmonitor) { + if (Threads::number_of_threads() == 0) { + // No JavaThreads exist so ObjectMonitor enter cannot be + // used, add this raw monitor to the pending list. + // The pending monitors will be actually entered when + // the VM is setup. + // See transition_pending_raw_monitors in create_vm() + // in thread.cpp. + JvmtiPendingMonitors::enter(rmonitor); + } else { + int r; + Thread* thread = Thread::current(); + + if (thread->is_Java_thread()) { + JavaThread* current_thread = (JavaThread*)thread; + +#ifdef PROPER_TRANSITIONS + // Not really unknown but ThreadInVMfromNative does more than we want + ThreadInVMfromUnknown __tiv; + { + ThreadBlockInVM __tbivm(current_thread); + r = rmonitor->raw_enter(current_thread); + } +#else + /* Transition to thread_blocked without entering vm state */ + /* This is really evil. Normally you can't undo _thread_blocked */ + /* transitions like this because it would cause us to miss a */ + /* safepoint but since the thread was already in _thread_in_native */ + /* the thread is not leaving a safepoint safe state and it will */ + /* block when it tries to return from native. We can't safepoint */ + /* block in here because we could deadlock the vmthread. Blech. */ + + JavaThreadState state = current_thread->thread_state(); + assert(state == _thread_in_native, "Must be _thread_in_native"); + // frame should already be walkable since we are in native + assert(!current_thread->has_last_Java_frame() || + current_thread->frame_anchor()->walkable(), "Must be walkable"); + current_thread->set_thread_state(_thread_blocked); + + r = rmonitor->raw_enter(current_thread); + // restore state, still at a safepoint safe state + current_thread->set_thread_state(state); + +#endif /* PROPER_TRANSITIONS */ + assert(r == ObjectMonitor::OM_OK, "raw_enter should have worked"); + } else { + if (thread->is_VM_thread() || thread->is_ConcurrentGC_thread()) { + r = rmonitor->raw_enter(thread); + } else { + ShouldNotReachHere(); + } + } + + if (r != ObjectMonitor::OM_OK) { // robustness + return JVMTI_ERROR_INTERNAL; + } + } + return JVMTI_ERROR_NONE; +} /* end RawMonitorEnter */ + + +// rmonitor - pre-checked for validity +jvmtiError +JvmtiEnv::RawMonitorExit(JvmtiRawMonitor * rmonitor) { + jvmtiError err = JVMTI_ERROR_NONE; + + if (Threads::number_of_threads() == 0) { + // No JavaThreads exist so just remove this monitor from the pending list. + // Bool value from exit is false if rmonitor is not in the list. + if (!JvmtiPendingMonitors::exit(rmonitor)) { + err = JVMTI_ERROR_NOT_MONITOR_OWNER; + } + } else { + int r; + Thread* thread = Thread::current(); + + if (thread->is_Java_thread()) { + JavaThread* current_thread = (JavaThread*)thread; +#ifdef PROPER_TRANSITIONS + // Not really unknown but ThreadInVMfromNative does more than we want + ThreadInVMfromUnknown __tiv; +#endif /* PROPER_TRANSITIONS */ + r = rmonitor->raw_exit(current_thread); + } else { + if (thread->is_VM_thread() || thread->is_ConcurrentGC_thread()) { + r = rmonitor->raw_exit(thread); + } else { + ShouldNotReachHere(); + } + } + + if (r == ObjectMonitor::OM_ILLEGAL_MONITOR_STATE) { + err = JVMTI_ERROR_NOT_MONITOR_OWNER; + } else { + assert(r == ObjectMonitor::OM_OK, "raw_exit should have worked"); + if (r != ObjectMonitor::OM_OK) { // robustness + err = JVMTI_ERROR_INTERNAL; + } + } + } + return err; +} /* end RawMonitorExit */ + + +// rmonitor - pre-checked for validity +jvmtiError +JvmtiEnv::RawMonitorWait(JvmtiRawMonitor * rmonitor, jlong millis) { + int r; + Thread* thread = Thread::current(); + + if (thread->is_Java_thread()) { + JavaThread* current_thread = (JavaThread*)thread; +#ifdef PROPER_TRANSITIONS + // Not really unknown but ThreadInVMfromNative does more than we want + ThreadInVMfromUnknown __tiv; + { + ThreadBlockInVM __tbivm(current_thread); + r = rmonitor->raw_wait(millis, true, current_thread); + } +#else + /* Transition to thread_blocked without entering vm state */ + /* This is really evil. Normally you can't undo _thread_blocked */ + /* transitions like this because it would cause us to miss a */ + /* safepoint but since the thread was already in _thread_in_native */ + /* the thread is not leaving a safepoint safe state and it will */ + /* block when it tries to return from native. We can't safepoint */ + /* block in here because we could deadlock the vmthread. Blech. */ + + JavaThreadState state = current_thread->thread_state(); + assert(state == _thread_in_native, "Must be _thread_in_native"); + // frame should already be walkable since we are in native + assert(!current_thread->has_last_Java_frame() || + current_thread->frame_anchor()->walkable(), "Must be walkable"); + current_thread->set_thread_state(_thread_blocked); + + r = rmonitor->raw_wait(millis, true, current_thread); + // restore state, still at a safepoint safe state + current_thread->set_thread_state(state); + +#endif /* PROPER_TRANSITIONS */ + } else { + if (thread->is_VM_thread() || thread->is_ConcurrentGC_thread()) { + r = rmonitor->raw_wait(millis, true, thread); + } else { + ShouldNotReachHere(); + } + } + + switch (r) { + case ObjectMonitor::OM_INTERRUPTED: + return JVMTI_ERROR_INTERRUPT; + case ObjectMonitor::OM_ILLEGAL_MONITOR_STATE: + return JVMTI_ERROR_NOT_MONITOR_OWNER; + } + assert(r == ObjectMonitor::OM_OK, "raw_wait should have worked"); + if (r != ObjectMonitor::OM_OK) { // robustness + return JVMTI_ERROR_INTERNAL; + } + + return JVMTI_ERROR_NONE; +} /* end RawMonitorWait */ + + +// rmonitor - pre-checked for validity +jvmtiError +JvmtiEnv::RawMonitorNotify(JvmtiRawMonitor * rmonitor) { + int r; + Thread* thread = Thread::current(); + + if (thread->is_Java_thread()) { + JavaThread* current_thread = (JavaThread*)thread; + // Not really unknown but ThreadInVMfromNative does more than we want + ThreadInVMfromUnknown __tiv; + r = rmonitor->raw_notify(current_thread); + } else { + if (thread->is_VM_thread() || thread->is_ConcurrentGC_thread()) { + r = rmonitor->raw_notify(thread); + } else { + ShouldNotReachHere(); + } + } + + if (r == ObjectMonitor::OM_ILLEGAL_MONITOR_STATE) { + return JVMTI_ERROR_NOT_MONITOR_OWNER; + } + assert(r == ObjectMonitor::OM_OK, "raw_notify should have worked"); + if (r != ObjectMonitor::OM_OK) { // robustness + return JVMTI_ERROR_INTERNAL; + } + + return JVMTI_ERROR_NONE; +} /* end RawMonitorNotify */ + + +// rmonitor - pre-checked for validity +jvmtiError +JvmtiEnv::RawMonitorNotifyAll(JvmtiRawMonitor * rmonitor) { + int r; + Thread* thread = Thread::current(); + + if (thread->is_Java_thread()) { + JavaThread* current_thread = (JavaThread*)thread; + ThreadInVMfromUnknown __tiv; + r = rmonitor->raw_notifyAll(current_thread); + } else { + if (thread->is_VM_thread() || thread->is_ConcurrentGC_thread()) { + r = rmonitor->raw_notifyAll(thread); + } else { + ShouldNotReachHere(); + } + } + + if (r == ObjectMonitor::OM_ILLEGAL_MONITOR_STATE) { + return JVMTI_ERROR_NOT_MONITOR_OWNER; + } + assert(r == ObjectMonitor::OM_OK, "raw_notifyAll should have worked"); + if (r != ObjectMonitor::OM_OK) { // robustness + return JVMTI_ERROR_INTERNAL; + } + + return JVMTI_ERROR_NONE; +} /* end RawMonitorNotifyAll */ + + + // + // JNI Function Interception functions + // + + +// function_table - pre-checked for NULL +jvmtiError +JvmtiEnv::SetJNIFunctionTable(const jniNativeInterface* function_table) { + // Copy jni function table at safepoint. + VM_JNIFunctionTableCopier copier(function_table); + VMThread::execute(&copier); + + return JVMTI_ERROR_NONE; +} /* end SetJNIFunctionTable */ + + +// function_table - pre-checked for NULL +jvmtiError +JvmtiEnv::GetJNIFunctionTable(jniNativeInterface** function_table) { + *function_table=(jniNativeInterface*)jvmtiMalloc(sizeof(jniNativeInterface)); + if (*function_table == NULL) + return JVMTI_ERROR_OUT_OF_MEMORY; + memcpy(*function_table,(JavaThread::current())->get_jni_functions(),sizeof(jniNativeInterface)); + return JVMTI_ERROR_NONE; +} /* end GetJNIFunctionTable */ + + + // + // Event Management functions + // + +jvmtiError +JvmtiEnv::GenerateEvents(jvmtiEvent event_type) { + // can only generate two event types + if (event_type != JVMTI_EVENT_COMPILED_METHOD_LOAD && + event_type != JVMTI_EVENT_DYNAMIC_CODE_GENERATED) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + + // for compiled_method_load events we must check that the environment + // has the can_generate_compiled_method_load_events capability. + if (event_type == JVMTI_EVENT_COMPILED_METHOD_LOAD) { + if (get_capabilities()->can_generate_compiled_method_load_events == 0) { + return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; + } + return JvmtiCodeBlobEvents::generate_compiled_method_load_events(this); + } else { + return JvmtiCodeBlobEvents::generate_dynamic_code_events(this); + } + +} /* end GenerateEvents */ + + + // + // Extension Mechanism functions + // + +// extension_count_ptr - pre-checked for NULL +// extensions - pre-checked for NULL +jvmtiError +JvmtiEnv::GetExtensionFunctions(jint* extension_count_ptr, jvmtiExtensionFunctionInfo** extensions) { + return JvmtiExtensions::get_functions(this, extension_count_ptr, extensions); +} /* end GetExtensionFunctions */ + + +// extension_count_ptr - pre-checked for NULL +// extensions - pre-checked for NULL +jvmtiError +JvmtiEnv::GetExtensionEvents(jint* extension_count_ptr, jvmtiExtensionEventInfo** extensions) { + return JvmtiExtensions::get_events(this, extension_count_ptr, extensions); +} /* end GetExtensionEvents */ + + +// callback - NULL is a valid value, must be checked +jvmtiError +JvmtiEnv::SetExtensionEventCallback(jint extension_event_index, jvmtiExtensionEvent callback) { + return JvmtiExtensions::set_event_callback(this, extension_event_index, callback); +} /* end SetExtensionEventCallback */ + + // + // Timers functions + // + +// info_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetCurrentThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + os::current_thread_cpu_time_info(info_ptr); + return JVMTI_ERROR_NONE; +} /* end GetCurrentThreadCpuTimerInfo */ + + +// nanos_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetCurrentThreadCpuTime(jlong* nanos_ptr) { + *nanos_ptr = os::current_thread_cpu_time(); + return JVMTI_ERROR_NONE; +} /* end GetCurrentThreadCpuTime */ + + +// info_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + os::thread_cpu_time_info(info_ptr); + return JVMTI_ERROR_NONE; +} /* end GetThreadCpuTimerInfo */ + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked +// nanos_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetThreadCpuTime(JavaThread* java_thread, jlong* nanos_ptr) { + *nanos_ptr = os::thread_cpu_time(java_thread); + return JVMTI_ERROR_NONE; +} /* end GetThreadCpuTime */ + + +// info_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetTimerInfo(jvmtiTimerInfo* info_ptr) { + os::javaTimeNanos_info(info_ptr); + return JVMTI_ERROR_NONE; +} /* end GetTimerInfo */ + + +// nanos_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetTime(jlong* nanos_ptr) { + *nanos_ptr = os::javaTimeNanos(); + return JVMTI_ERROR_NONE; +} /* end GetTime */ + + +// processor_count_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetAvailableProcessors(jint* processor_count_ptr) { + *processor_count_ptr = os::active_processor_count(); + return JVMTI_ERROR_NONE; +} /* end GetAvailableProcessors */ + + // + // System Properties functions + // + +// count_ptr - pre-checked for NULL +// property_ptr - pre-checked for NULL +jvmtiError +JvmtiEnv::GetSystemProperties(jint* count_ptr, char*** property_ptr) { + jvmtiError err = JVMTI_ERROR_NONE; + + *count_ptr = Arguments::PropertyList_count(Arguments::system_properties()); + + err = allocate(*count_ptr * sizeof(char *), (unsigned char **)property_ptr); + if (err != JVMTI_ERROR_NONE) { + return err; + } + int i = 0 ; + for (SystemProperty* p = Arguments::system_properties(); p != NULL && i < *count_ptr; p = p->next(), i++) { + const char *key = p->key(); + char **tmp_value = *property_ptr+i; + err = allocate((strlen(key)+1) * sizeof(char), (unsigned char**)tmp_value); + if (err == JVMTI_ERROR_NONE) { + strcpy(*tmp_value, key); + } else { + // clean up previously allocated memory. + for (int j=0; jnext()) { + if (strcmp(property, p->key()) == 0) { + if (p->set_value((char *)value)) { + err = JVMTI_ERROR_NONE; + } + } + } + return err; +} /* end SetSystemProperty */ + +#endif // !JVMTI_KERNEL diff --git a/hotspot/src/share/vm/prims/jvmtiEnv.xsl b/hotspot/src/share/vm/prims/jvmtiEnv.xsl new file mode 100644 index 00000000000..3469f7f1dcf --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEnv.xsl @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiEnv.cpp.incl" + +// end file prefix - do not modify or remove this line + + + + + + + + + + + // + // functions + // + + + + + + + +jvmtiError +JvmtiEnv:: + + + + + ( + + ) { + + + + if (java_lang_Class::is_primitive(k_mirror)) { + // DO PRIMITIVE CLASS PROCESSING + return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; + } + klassOop k_oop = java_lang_Class::as_klassOop(k_mirror); + if (k_oop == NULL) { + return JVMTI_ERROR_INVALID_CLASS; + } + + + + return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; +} /* end + + + + + */ + + + + + + + + + + + + + + + + + + + + + + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked + + + +// Threads_lock NOT held +// + + - NOT pre-checked + + + + + + + +// rmonitor - pre-checked for validity + + + + + +// java_thread - unchecked +// depth - pre-checked as non-negative + + + + + +// method_oop - pre-checked for validity, but may be NULL meaning obsolete method + + + + + + + + + + + +// k_mirror - may be primitive, this must be checked + + + + + + + + + + + +// + + - pre-checked for NULL + + + +// + + - NULL is a valid value, must be checked + + + + + + + + +// + + - pre-checked to be greater than or equal to + + + + + + + + + diff --git a/hotspot/src/share/vm/prims/jvmtiEnvBase.cpp b/hotspot/src/share/vm/prims/jvmtiEnvBase.cpp new file mode 100644 index 00000000000..62591145feb --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEnvBase.cpp @@ -0,0 +1,1415 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +# include "incls/_precompiled.incl" +# include "incls/_jvmtiEnvBase.cpp.incl" + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEnvBase +// + +JvmtiEnvBase* JvmtiEnvBase::_head_environment = NULL; + +bool JvmtiEnvBase::_globally_initialized = false; +volatile bool JvmtiEnvBase::_needs_clean_up = false; + +jvmtiPhase JvmtiEnvBase::_phase = JVMTI_PHASE_PRIMORDIAL; + +volatile int JvmtiEnvBase::_dying_thread_env_iteration_count = 0; + +extern jvmtiInterface_1_ jvmti_Interface; +extern jvmtiInterface_1_ jvmtiTrace_Interface; + + +// perform initializations that must occur before any JVMTI environments +// are released but which should only be initialized once (no matter +// how many environments are created). +void +JvmtiEnvBase::globally_initialize() { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + assert(_globally_initialized == false, "bad call"); + + JvmtiManageCapabilities::initialize(); + +#ifndef JVMTI_KERNEL + // register extension functions and events + JvmtiExtensions::register_extensions(); +#endif // !JVMTI_KERNEL + +#ifdef JVMTI_TRACE + JvmtiTrace::initialize(); +#endif + + _globally_initialized = true; +} + + +void +JvmtiEnvBase::initialize() { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + + // Add this environment to the end of the environment list (order is important) + { + // This block of code must not contain any safepoints, as list deallocation + // (which occurs at a safepoint) cannot occur simultaneously with this list + // addition. Note: No_Safepoint_Verifier cannot, currently, be used before + // threads exist. + JvmtiEnvIterator it; + JvmtiEnvBase *previous_env = NULL; + for (JvmtiEnvBase* env = it.first(); env != NULL; env = it.next(env)) { + previous_env = env; + } + if (previous_env == NULL) { + _head_environment = this; + } else { + previous_env->set_next_environment(this); + } + } + + if (_globally_initialized == false) { + globally_initialize(); + } +} + + +JvmtiEnvBase::JvmtiEnvBase() : _env_event_enable() { + _env_local_storage = NULL; + _tag_map = NULL; + _native_method_prefix_count = 0; + _native_method_prefixes = NULL; + _next = NULL; + _class_file_load_hook_ever_enabled = false; + + // Moot since ClassFileLoadHook not yet enabled. + // But "true" will give a more predictable ClassFileLoadHook behavior + // for environment creation during ClassFileLoadHook. + _is_retransformable = true; + + // all callbacks initially NULL + memset(&_event_callbacks,0,sizeof(jvmtiEventCallbacks)); + + // all capabilities initially off + memset(&_current_capabilities, 0, sizeof(_current_capabilities)); + + // all prohibited capabilities initially off + memset(&_prohibited_capabilities, 0, sizeof(_prohibited_capabilities)); + + _magic = JVMTI_MAGIC; + + JvmtiEventController::env_initialize((JvmtiEnv*)this); + +#ifdef JVMTI_TRACE + _jvmti_external.functions = strlen(TraceJVMTI)? &jvmtiTrace_Interface : &jvmti_Interface; +#else + _jvmti_external.functions = &jvmti_Interface; +#endif +} + + +void +JvmtiEnvBase::dispose() { + +#ifdef JVMTI_TRACE + JvmtiTrace::shutdown(); +#endif + + // Dispose of event info and let the event controller call us back + // in a locked state (env_dispose, below) + JvmtiEventController::env_dispose(this); +} + +void +JvmtiEnvBase::env_dispose() { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + + // We have been entered with all events disabled on this environment. + // A race to re-enable events (by setting callbacks) is prevented by + // checking for a valid environment when setting callbacks (while + // holding the JvmtiThreadState_lock). + + // Mark as invalid. + _magic = DISPOSED_MAGIC; + + // Relinquish all capabilities. + jvmtiCapabilities *caps = get_capabilities(); + JvmtiManageCapabilities::relinquish_capabilities(caps, caps, caps); + + // Same situation as with events (see above) + set_native_method_prefixes(0, NULL); + +#ifndef JVMTI_KERNEL + JvmtiTagMap* tag_map_to_deallocate = _tag_map; + set_tag_map(NULL); + // A tag map can be big, deallocate it now + if (tag_map_to_deallocate != NULL) { + delete tag_map_to_deallocate; + } +#endif // !JVMTI_KERNEL + + _needs_clean_up = true; +} + + +JvmtiEnvBase::~JvmtiEnvBase() { + assert(SafepointSynchronize::is_at_safepoint(), "sanity check"); + + // There is a small window of time during which the tag map of a + // disposed environment could have been reallocated. + // Make sure it is gone. +#ifndef JVMTI_KERNEL + JvmtiTagMap* tag_map_to_deallocate = _tag_map; + set_tag_map(NULL); + // A tag map can be big, deallocate it now + if (tag_map_to_deallocate != NULL) { + delete tag_map_to_deallocate; + } +#endif // !JVMTI_KERNEL + + _magic = BAD_MAGIC; +} + + +void +JvmtiEnvBase::periodic_clean_up() { + assert(SafepointSynchronize::is_at_safepoint(), "sanity check"); + + // JvmtiEnvBase reference is saved in JvmtiEnvThreadState. So + // clean up JvmtiThreadState before deleting JvmtiEnv pointer. + JvmtiThreadState::periodic_clean_up(); + + // Unlink all invalid environments from the list of environments + // and deallocate them + JvmtiEnvIterator it; + JvmtiEnvBase* previous_env = NULL; + JvmtiEnvBase* env = it.first(); + while (env != NULL) { + if (env->is_valid()) { + previous_env = env; + env = it.next(env); + } else { + // This one isn't valid, remove it from the list and deallocate it + JvmtiEnvBase* defunct_env = env; + env = it.next(env); + if (previous_env == NULL) { + _head_environment = env; + } else { + previous_env->set_next_environment(env); + } + delete defunct_env; + } + } + +} + + +void +JvmtiEnvBase::check_for_periodic_clean_up() { + assert(SafepointSynchronize::is_at_safepoint(), "sanity check"); + + class ThreadInsideIterationClosure: public ThreadClosure { + private: + bool _inside; + public: + ThreadInsideIterationClosure() : _inside(false) {}; + + void do_thread(Thread* thread) { + _inside |= thread->is_inside_jvmti_env_iteration(); + } + + bool is_inside_jvmti_env_iteration() { + return _inside; + } + }; + + if (_needs_clean_up) { + // Check if we are currently iterating environment, + // deallocation should not occur if we are + ThreadInsideIterationClosure tiic; + Threads::threads_do(&tiic); + if (!tiic.is_inside_jvmti_env_iteration() && + !is_inside_dying_thread_env_iteration()) { + _needs_clean_up = false; + JvmtiEnvBase::periodic_clean_up(); + } + } +} + + +void +JvmtiEnvBase::record_first_time_class_file_load_hook_enabled() { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), + "sanity check"); + + if (!_class_file_load_hook_ever_enabled) { + _class_file_load_hook_ever_enabled = true; + + if (get_capabilities()->can_retransform_classes) { + _is_retransformable = true; + } else { + _is_retransformable = false; + + // cannot add retransform capability after ClassFileLoadHook has been enabled + get_prohibited_capabilities()->can_retransform_classes = 1; + } + } +} + + +void +JvmtiEnvBase::record_class_file_load_hook_enabled() { + if (!_class_file_load_hook_ever_enabled) { + if (Threads::number_of_threads() == 0) { + record_first_time_class_file_load_hook_enabled(); + } else { + MutexLocker mu(JvmtiThreadState_lock); + record_first_time_class_file_load_hook_enabled(); + } + } +} + + +jvmtiError +JvmtiEnvBase::set_native_method_prefixes(jint prefix_count, char** prefixes) { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), + "sanity check"); + + int old_prefix_count = get_native_method_prefix_count(); + char **old_prefixes = get_native_method_prefixes(); + + // allocate and install the new prefixex + if (prefix_count == 0 || !is_valid()) { + _native_method_prefix_count = 0; + _native_method_prefixes = NULL; + } else { + // there are prefixes, allocate an array to hold them, and fill it + char** new_prefixes = (char**)os::malloc((prefix_count) * sizeof(char*)); + if (new_prefixes == NULL) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } + for (int i = 0; i < prefix_count; i++) { + char* prefix = prefixes[i]; + if (prefix == NULL) { + for (int j = 0; j < (i-1); j++) { + os::free(new_prefixes[j]); + } + os::free(new_prefixes); + return JVMTI_ERROR_NULL_POINTER; + } + prefix = os::strdup(prefixes[i]); + if (prefix == NULL) { + for (int j = 0; j < (i-1); j++) { + os::free(new_prefixes[j]); + } + os::free(new_prefixes); + return JVMTI_ERROR_OUT_OF_MEMORY; + } + new_prefixes[i] = prefix; + } + _native_method_prefix_count = prefix_count; + _native_method_prefixes = new_prefixes; + } + + // now that we know the new prefixes have been successfully installed we can + // safely remove the old ones + if (old_prefix_count != 0) { + for (int i = 0; i < old_prefix_count; i++) { + os::free(old_prefixes[i]); + } + os::free(old_prefixes); + } + + return JVMTI_ERROR_NONE; +} + + +// Collect all the prefixes which have been set in any JVM TI environments +// by the SetNativeMethodPrefix(es) functions. Be sure to maintain the +// order of environments and the order of prefixes within each environment. +// Return in a resource allocated array. +char** +JvmtiEnvBase::get_all_native_method_prefixes(int* count_ptr) { + assert(Threads::number_of_threads() == 0 || + SafepointSynchronize::is_at_safepoint() || + JvmtiThreadState_lock->is_locked(), + "sanity check"); + + int total_count = 0; + GrowableArray* prefix_array =new GrowableArray(5); + + JvmtiEnvIterator it; + for (JvmtiEnvBase* env = it.first(); env != NULL; env = it.next(env)) { + int prefix_count = env->get_native_method_prefix_count(); + char** prefixes = env->get_native_method_prefixes(); + for (int j = 0; j < prefix_count; j++) { + // retrieve a prefix and so that it is safe against asynchronous changes + // copy it into the resource area + char* prefix = prefixes[j]; + char* prefix_copy = NEW_RESOURCE_ARRAY(char, strlen(prefix)+1); + strcpy(prefix_copy, prefix); + prefix_array->at_put_grow(total_count++, prefix_copy); + } + } + + char** all_prefixes = NEW_RESOURCE_ARRAY(char*, total_count); + char** p = all_prefixes; + for (int i = 0; i < total_count; ++i) { + *p++ = prefix_array->at(i); + } + *count_ptr = total_count; + return all_prefixes; +} + +void +JvmtiEnvBase::set_event_callbacks(const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks) { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + + size_t byte_cnt = sizeof(jvmtiEventCallbacks); + + // clear in either case to be sure we got any gap between sizes + memset(&_event_callbacks, 0, byte_cnt); + + // Now that JvmtiThreadState_lock is held, prevent a possible race condition where events + // are re-enabled by a call to set event callbacks where the DisposeEnvironment + // occurs after the boiler-plate environment check and before the lock is acquired. + if (callbacks != NULL && is_valid()) { + if (size_of_callbacks < (jint)byte_cnt) { + byte_cnt = size_of_callbacks; + } + memcpy(&_event_callbacks, callbacks, byte_cnt); + } +} + +// Called from JVMTI entry points which perform stack walking. If the +// associated JavaThread is the current thread, then wait_for_suspend +// is not used. Otherwise, it determines if we should wait for the +// "other" thread to complete external suspension. (NOTE: in future +// releases the suspension mechanism should be reimplemented so this +// is not necessary.) +// +bool +JvmtiEnvBase::is_thread_fully_suspended(JavaThread* thr, bool wait_for_suspend, uint32_t *bits) { + // "other" threads require special handling + if (thr != JavaThread::current()) { + if (wait_for_suspend) { + // We are allowed to wait for the external suspend to complete + // so give the other thread a chance to get suspended. + if (!thr->wait_for_ext_suspend_completion(SuspendRetryCount, + SuspendRetryDelay, bits)) { + // didn't make it so let the caller know + return false; + } + } + // We aren't allowed to wait for the external suspend to complete + // so if the other thread isn't externally suspended we need to + // let the caller know. + else if (!thr->is_ext_suspend_completed_with_lock(bits)) { + return false; + } + } + + return true; +} + + +// In the fullness of time, all users of the method should instead +// directly use allocate, besides being cleaner and faster, this will +// mean much better out of memory handling +unsigned char * +JvmtiEnvBase::jvmtiMalloc(jlong size) { + unsigned char* mem; + jvmtiError result = allocate(size, &mem); + assert(result == JVMTI_ERROR_NONE, "Allocate failed"); + return mem; +} + + +// +// Threads +// + +jobject * +JvmtiEnvBase::new_jobjectArray(int length, Handle *handles) { + if (length == 0) { + return NULL; + } + + jobject *objArray = (jobject *) jvmtiMalloc(sizeof(jobject) * length); + NULL_CHECK(objArray, NULL); + + for (int i=0; iis_a(SystemDictionary::thread_klass())) { + return NULL; + } + // The following returns NULL if the thread has not yet run or is in + // process of exiting + return java_lang_Thread::thread(t); +} + + +// update the access_flags for the field in the klass +void +JvmtiEnvBase::update_klass_field_access_flag(fieldDescriptor *fd) { + instanceKlass* ik = instanceKlass::cast(fd->field_holder()); + typeArrayOop fields = ik->fields(); + fields->ushort_at_put(fd->index(), (jushort)fd->access_flags().as_short()); +} + + +// return the vframe on the specified thread and depth, NULL if no such frame +vframe* +JvmtiEnvBase::vframeFor(JavaThread* java_thread, jint depth) { + if (!java_thread->has_last_Java_frame()) { + return NULL; + } + RegisterMap reg_map(java_thread); + vframe *vf = java_thread->last_java_vframe(®_map); + int d = 0; + while ((vf != NULL) && (d < depth)) { + vf = vf->java_sender(); + d++; + } + return vf; +} + + +// +// utilities: JNI objects +// + + +jclass +JvmtiEnvBase::get_jni_class_non_null(klassOop k) { + assert(k != NULL, "k != NULL"); + return (jclass)jni_reference(Klass::cast(k)->java_mirror()); +} + +#ifndef JVMTI_KERNEL + +// +// Field Information +// + +bool +JvmtiEnvBase::get_field_descriptor(klassOop k, jfieldID field, fieldDescriptor* fd) { + if (!jfieldIDWorkaround::is_valid_jfieldID(k, field)) { + return false; + } + bool found = false; + if (jfieldIDWorkaround::is_static_jfieldID(field)) { + JNIid* id = jfieldIDWorkaround::from_static_jfieldID(field); + int offset = id->offset(); + klassOop holder = id->holder(); + found = instanceKlass::cast(holder)->find_local_field_from_offset(offset, true, fd); + } else { + // Non-static field. The fieldID is really the offset of the field within the object. + int offset = jfieldIDWorkaround::from_instance_jfieldID(k, field); + found = instanceKlass::cast(k)->find_field_from_offset(offset, false, fd); + } + return found; +} + +// +// Object Monitor Information +// + +// +// Count the number of objects for a lightweight monitor. The hobj +// parameter is object that owns the monitor so this routine will +// count the number of times the same object was locked by frames +// in java_thread. +// +jint +JvmtiEnvBase::count_locked_objects(JavaThread *java_thread, Handle hobj) { + jint ret = 0; + if (!java_thread->has_last_Java_frame()) { + return ret; // no Java frames so no monitors + } + + ResourceMark rm; + HandleMark hm; + RegisterMap reg_map(java_thread); + + for(javaVFrame *jvf=java_thread->last_java_vframe(®_map); jvf != NULL; + jvf = jvf->java_sender()) { + GrowableArray* mons = jvf->monitors(); + if (!mons->is_empty()) { + for (int i = 0; i < mons->length(); i++) { + MonitorInfo *mi = mons->at(i); + + // see if owner of the monitor is our object + if (mi->owner() != NULL && mi->owner() == hobj()) { + ret++; + } + } + } + } + return ret; +} + + + +jvmtiError +JvmtiEnvBase::get_current_contended_monitor(JavaThread *calling_thread, JavaThread *java_thread, jobject *monitor_ptr) { +#ifdef ASSERT + uint32_t debug_bits = 0; +#endif + assert((SafepointSynchronize::is_at_safepoint() || + is_thread_fully_suspended(java_thread, false, &debug_bits)), + "at safepoint or target thread is suspended"); + oop obj = NULL; + ObjectMonitor *mon = java_thread->current_waiting_monitor(); + if (mon == NULL) { + // thread is not doing an Object.wait() call + mon = java_thread->current_pending_monitor(); + if (mon != NULL) { + // The thread is trying to enter() or raw_enter() an ObjectMonitor. + obj = (oop)mon->object(); + // If obj == NULL, then ObjectMonitor is raw which doesn't count + // as contended for this API + } + // implied else: no contended ObjectMonitor + } else { + // thread is doing an Object.wait() call + obj = (oop)mon->object(); + assert(obj != NULL, "Object.wait() should have an object"); + } + + if (obj == NULL) { + *monitor_ptr = NULL; + } else { + HandleMark hm; + Handle hobj(obj); + *monitor_ptr = jni_reference(calling_thread, hobj); + } + return JVMTI_ERROR_NONE; +} + + +jvmtiError +JvmtiEnvBase::get_owned_monitors(JavaThread *calling_thread, JavaThread* java_thread, + GrowableArray *owned_monitors_list) { + jvmtiError err = JVMTI_ERROR_NONE; +#ifdef ASSERT + uint32_t debug_bits = 0; +#endif + assert((SafepointSynchronize::is_at_safepoint() || + is_thread_fully_suspended(java_thread, false, &debug_bits)), + "at safepoint or target thread is suspended"); + + if (java_thread->has_last_Java_frame()) { + ResourceMark rm; + HandleMark hm; + RegisterMap reg_map(java_thread); + + int depth = 0; + for (javaVFrame *jvf = java_thread->last_java_vframe(®_map); jvf != NULL; + jvf = jvf->java_sender()) { + if (depth++ < MaxJavaStackTraceDepth) { // check for stack too deep + // add locked objects for this frame into list + err = get_locked_objects_in_frame(calling_thread, java_thread, jvf, owned_monitors_list, depth-1); + if (err != JVMTI_ERROR_NONE) { + return err; + } + } + } + } + + // Get off stack monitors. (e.g. acquired via jni MonitorEnter). + JvmtiMonitorClosure jmc(java_thread, calling_thread, owned_monitors_list, this); + ObjectSynchronizer::monitors_iterate(&jmc); + err = jmc.error(); + + return err; +} + +// Save JNI local handles for any objects that this frame owns. +jvmtiError +JvmtiEnvBase::get_locked_objects_in_frame(JavaThread* calling_thread, JavaThread* java_thread, + javaVFrame *jvf, GrowableArray* owned_monitors_list, int stack_depth) { + jvmtiError err = JVMTI_ERROR_NONE; + ResourceMark rm; + + GrowableArray* mons = jvf->monitors(); + if (mons->is_empty()) { + return err; // this javaVFrame holds no monitors + } + + HandleMark hm; + oop wait_obj = NULL; + { + // save object of current wait() call (if any) for later comparison + ObjectMonitor *mon = java_thread->current_waiting_monitor(); + if (mon != NULL) { + wait_obj = (oop)mon->object(); + } + } + oop pending_obj = NULL; + { + // save object of current enter() call (if any) for later comparison + ObjectMonitor *mon = java_thread->current_pending_monitor(); + if (mon != NULL) { + pending_obj = (oop)mon->object(); + } + } + + for (int i = 0; i < mons->length(); i++) { + MonitorInfo *mi = mons->at(i); + + oop obj = mi->owner(); + if (obj == NULL) { + // this monitor doesn't have an owning object so skip it + continue; + } + + if (wait_obj == obj) { + // the thread is waiting on this monitor so it isn't really owned + continue; + } + + if (pending_obj == obj) { + // the thread is pending on this monitor so it isn't really owned + continue; + } + + if (owned_monitors_list->length() > 0) { + // Our list has at least one object on it so we have to check + // for recursive object locking + bool found = false; + for (int j = 0; j < owned_monitors_list->length(); j++) { + jobject jobj = ((jvmtiMonitorStackDepthInfo*)owned_monitors_list->at(j))->monitor; + oop check = JNIHandles::resolve(jobj); + if (check == obj) { + found = true; // we found the object + break; + } + } + + if (found) { + // already have this object so don't include it + continue; + } + } + + // add the owning object to our list + jvmtiMonitorStackDepthInfo *jmsdi; + err = allocate(sizeof(jvmtiMonitorStackDepthInfo), (unsigned char **)&jmsdi); + if (err != JVMTI_ERROR_NONE) { + return err; + } + Handle hobj(obj); + jmsdi->monitor = jni_reference(calling_thread, hobj); + jmsdi->stack_depth = stack_depth; + owned_monitors_list->append(jmsdi); + } + + return err; +} + +jvmtiError +JvmtiEnvBase::get_stack_trace(JavaThread *java_thread, + jint start_depth, jint max_count, + jvmtiFrameInfo* frame_buffer, jint* count_ptr) { +#ifdef ASSERT + uint32_t debug_bits = 0; +#endif + assert((SafepointSynchronize::is_at_safepoint() || + is_thread_fully_suspended(java_thread, false, &debug_bits)), + "at safepoint or target thread is suspended"); + int count = 0; + if (java_thread->has_last_Java_frame()) { + RegisterMap reg_map(java_thread); + Thread* current_thread = Thread::current(); + ResourceMark rm(current_thread); + javaVFrame *jvf = java_thread->last_java_vframe(®_map); + HandleMark hm(current_thread); + if (start_depth != 0) { + if (start_depth > 0) { + for (int j = 0; j < start_depth && jvf != NULL; j++) { + jvf = jvf->java_sender(); + } + if (jvf == NULL) { + // start_depth is deeper than the stack depth + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + } else { // start_depth < 0 + // we are referencing the starting depth based on the oldest + // part of the stack. + // optimize to limit the number of times that java_sender() is called + javaVFrame *jvf_cursor = jvf; + javaVFrame *jvf_prev = NULL; + javaVFrame *jvf_prev_prev; + int j = 0; + while (jvf_cursor != NULL) { + jvf_prev_prev = jvf_prev; + jvf_prev = jvf_cursor; + for (j = 0; j > start_depth && jvf_cursor != NULL; j--) { + jvf_cursor = jvf_cursor->java_sender(); + } + } + if (j == start_depth) { + // previous pointer is exactly where we want to start + jvf = jvf_prev; + } else { + // we need to back up further to get to the right place + if (jvf_prev_prev == NULL) { + // the -start_depth is greater than the stack depth + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + // j now is the number of frames on the stack starting with + // jvf_prev, we start from jvf_prev_prev and move older on + // the stack that many, the result is -start_depth frames + // remaining. + jvf = jvf_prev_prev; + for (; j < 0; j++) { + jvf = jvf->java_sender(); + } + } + } + } + for (; count < max_count && jvf != NULL; count++) { + frame_buffer[count].method = jvf->method()->jmethod_id(); + frame_buffer[count].location = (jvf->method()->is_native() ? -1 : jvf->bci()); + jvf = jvf->java_sender(); + } + } else { + if (start_depth != 0) { + // no frames and there is a starting depth + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + } + *count_ptr = count; + return JVMTI_ERROR_NONE; +} + +jvmtiError +JvmtiEnvBase::get_frame_count(JvmtiThreadState *state, jint *count_ptr) { + assert((state != NULL), + "JavaThread should create JvmtiThreadState before calling this method"); + *count_ptr = state->count_frames(); + return JVMTI_ERROR_NONE; +} + +jvmtiError +JvmtiEnvBase::get_frame_location(JavaThread *java_thread, jint depth, + jmethodID* method_ptr, jlocation* location_ptr) { +#ifdef ASSERT + uint32_t debug_bits = 0; +#endif + assert((SafepointSynchronize::is_at_safepoint() || + is_thread_fully_suspended(java_thread, false, &debug_bits)), + "at safepoint or target thread is suspended"); + Thread* current_thread = Thread::current(); + ResourceMark rm(current_thread); + + vframe *vf = vframeFor(java_thread, depth); + if (vf == NULL) { + return JVMTI_ERROR_NO_MORE_FRAMES; + } + + // vframeFor should return a java frame. If it doesn't + // it means we've got an internal error and we return the + // error in product mode. In debug mode we will instead + // attempt to cast the vframe to a javaVFrame and will + // cause an assertion/crash to allow further diagnosis. +#ifdef PRODUCT + if (!vf->is_java_frame()) { + return JVMTI_ERROR_INTERNAL; + } +#endif + + HandleMark hm(current_thread); + javaVFrame *jvf = javaVFrame::cast(vf); + methodOop method = jvf->method(); + if (method->is_native()) { + *location_ptr = -1; + } else { + *location_ptr = jvf->bci(); + } + *method_ptr = method->jmethod_id(); + + return JVMTI_ERROR_NONE; +} + + +jvmtiError +JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject object, jvmtiMonitorUsage* info_ptr) { + HandleMark hm; + Handle hobj; + + bool at_safepoint = SafepointSynchronize::is_at_safepoint(); + + // Check arguments + { + oop mirror = JNIHandles::resolve_external_guard(object); + NULL_CHECK(mirror, JVMTI_ERROR_INVALID_OBJECT); + NULL_CHECK(info_ptr, JVMTI_ERROR_NULL_POINTER); + + hobj = Handle(mirror); + } + + JavaThread *owning_thread = NULL; + ObjectMonitor *mon = NULL; + jvmtiMonitorUsage ret = { + NULL, 0, 0, NULL, 0, NULL + }; + + uint32_t debug_bits = 0; + // first derive the object's owner and entry_count (if any) + { + // Revoke any biases before querying the mark word + if (SafepointSynchronize::is_at_safepoint()) { + BiasedLocking::revoke_at_safepoint(hobj); + } else { + BiasedLocking::revoke_and_rebias(hobj, false, calling_thread); + } + + address owner = NULL; + { + markOop mark = hobj()->mark(); + + if (!mark->has_monitor()) { + // this object has a lightweight monitor + + if (mark->has_locker()) { + owner = (address)mark->locker(); // save the address of the Lock word + } + // implied else: no owner + } else { + // this object has a heavyweight monitor + mon = mark->monitor(); + + // The owner field of a heavyweight monitor may be NULL for no + // owner, a JavaThread * or it may still be the address of the + // Lock word in a JavaThread's stack. A monitor can be inflated + // by a non-owning JavaThread, but only the owning JavaThread + // can change the owner field from the Lock word to the + // JavaThread * and it may not have done that yet. + owner = (address)mon->owner(); + } + } + + if (owner != NULL) { + // This monitor is owned so we have to find the owning JavaThread. + // Since owning_thread_from_monitor_owner() grabs a lock, GC can + // move our object at this point. However, our owner value is safe + // since it is either the Lock word on a stack or a JavaThread *. + owning_thread = Threads::owning_thread_from_monitor_owner(owner, !at_safepoint); + assert(owning_thread != NULL, "sanity check"); + if (owning_thread != NULL) { // robustness + // The monitor's owner either has to be the current thread, at safepoint + // or it has to be suspended. Any of these conditions will prevent both + // contending and waiting threads from modifying the state of + // the monitor. + if (!at_safepoint && !JvmtiEnv::is_thread_fully_suspended(owning_thread, true, &debug_bits)) { + return JVMTI_ERROR_THREAD_NOT_SUSPENDED; + } + HandleMark hm; + Handle th(owning_thread->threadObj()); + ret.owner = (jthread)jni_reference(calling_thread, th); + } + // implied else: no owner + } + + if (owning_thread != NULL) { // monitor is owned + if ((address)owning_thread == owner) { + // the owner field is the JavaThread * + assert(mon != NULL, + "must have heavyweight monitor with JavaThread * owner"); + ret.entry_count = mon->recursions() + 1; + } else { + // The owner field is the Lock word on the JavaThread's stack + // so the recursions field is not valid. We have to count the + // number of recursive monitor entries the hard way. We pass + // a handle to survive any GCs along the way. + ResourceMark rm; + ret.entry_count = count_locked_objects(owning_thread, hobj); + } + } + // implied else: entry_count == 0 + } + + int nWant,nWait; + if (mon != NULL) { + // this object has a heavyweight monitor + nWant = mon->contentions(); // # of threads contending for monitor + nWait = mon->waiters(); // # of threads in Object.wait() + ret.waiter_count = nWant + nWait; + ret.notify_waiter_count = nWait; + } else { + // this object has a lightweight monitor + ret.waiter_count = 0; + ret.notify_waiter_count = 0; + } + + // Allocate memory for heavyweight and lightweight monitor. + jvmtiError err; + err = allocate(ret.waiter_count * sizeof(jthread *), (unsigned char**)&ret.waiters); + if (err != JVMTI_ERROR_NONE) { + return err; + } + err = allocate(ret.notify_waiter_count * sizeof(jthread *), + (unsigned char**)&ret.notify_waiters); + if (err != JVMTI_ERROR_NONE) { + deallocate((unsigned char*)ret.waiters); + return err; + } + + // now derive the rest of the fields + if (mon != NULL) { + // this object has a heavyweight monitor + + // Number of waiters may actually be less than the waiter count. + // So NULL out memory so that unused memory will be NULL. + memset(ret.waiters, 0, ret.waiter_count * sizeof(jthread *)); + memset(ret.notify_waiters, 0, ret.notify_waiter_count * sizeof(jthread *)); + + if (ret.waiter_count > 0) { + // we have contending and/or waiting threads + HandleMark hm; + if (nWant > 0) { + // we have contending threads + ResourceMark rm; + // get_pending_threads returns only java thread so we do not need to + // check for non java threads. + GrowableArray* wantList = Threads::get_pending_threads( + nWant, (address)mon, !at_safepoint); + if (wantList->length() < nWant) { + // robustness: the pending list has gotten smaller + nWant = wantList->length(); + } + for (int i = 0; i < nWant; i++) { + JavaThread *pending_thread = wantList->at(i); + // If the monitor has no owner, then a non-suspended contending + // thread could potentially change the state of the monitor by + // entering it. The JVM/TI spec doesn't allow this. + if (owning_thread == NULL && !at_safepoint & + !JvmtiEnv::is_thread_fully_suspended(pending_thread, true, &debug_bits)) { + if (ret.owner != NULL) { + destroy_jni_reference(calling_thread, ret.owner); + } + for (int j = 0; j < i; j++) { + destroy_jni_reference(calling_thread, ret.waiters[j]); + } + deallocate((unsigned char*)ret.waiters); + deallocate((unsigned char*)ret.notify_waiters); + return JVMTI_ERROR_THREAD_NOT_SUSPENDED; + } + Handle th(pending_thread->threadObj()); + ret.waiters[i] = (jthread)jni_reference(calling_thread, th); + } + } + if (nWait > 0) { + // we have threads in Object.wait() + int offset = nWant; // add after any contending threads + ObjectWaiter *waiter = mon->first_waiter(); + for (int i = 0, j = 0; i < nWait; i++) { + if (waiter == NULL) { + // robustness: the waiting list has gotten smaller + nWait = j; + break; + } + Thread *t = mon->thread_of_waiter(waiter); + if (t != NULL && t->is_Java_thread()) { + JavaThread *wjava_thread = (JavaThread *)t; + // If the thread was found on the ObjectWaiter list, then + // it has not been notified. This thread can't change the + // state of the monitor so it doesn't need to be suspended. + Handle th(wjava_thread->threadObj()); + ret.waiters[offset + j] = (jthread)jni_reference(calling_thread, th); + ret.notify_waiters[j++] = (jthread)jni_reference(calling_thread, th); + } + waiter = mon->next_waiter(waiter); + } + } + } + + // Adjust count. nWant and nWait count values may be less than original. + ret.waiter_count = nWant + nWait; + ret.notify_waiter_count = nWait; + } else { + // this object has a lightweight monitor and we have nothing more + // to do here because the defaults are just fine. + } + + // we don't update return parameter unless everything worked + *info_ptr = ret; + + return JVMTI_ERROR_NONE; +} + +ResourceTracker::ResourceTracker(JvmtiEnv* env) { + _env = env; + _allocations = new (ResourceObj::C_HEAP) GrowableArray(20, true); + _failed = false; +} +ResourceTracker::~ResourceTracker() { + if (_failed) { + for (int i=0; i<_allocations->length(); i++) { + _env->deallocate(_allocations->at(i)); + } + } + delete _allocations; +} + +jvmtiError ResourceTracker::allocate(jlong size, unsigned char** mem_ptr) { + unsigned char *ptr; + jvmtiError err = _env->allocate(size, &ptr); + if (err == JVMTI_ERROR_NONE) { + _allocations->append(ptr); + *mem_ptr = ptr; + } else { + *mem_ptr = NULL; + _failed = true; + } + return err; + } + +unsigned char* ResourceTracker::allocate(jlong size) { + unsigned char* ptr; + allocate(size, &ptr); + return ptr; +} + +char* ResourceTracker::strdup(const char* str) { + char *dup_str = (char*)allocate(strlen(str)+1); + if (dup_str != NULL) { + strcpy(dup_str, str); + } + return dup_str; +} + +struct StackInfoNode { + struct StackInfoNode *next; + jvmtiStackInfo info; +}; + +// Create a jvmtiStackInfo inside a linked list node and create a +// buffer for the frame information, both allocated as resource objects. +// Fill in both the jvmtiStackInfo and the jvmtiFrameInfo. +// Note that either or both of thr and thread_oop +// may be null if the thread is new or has exited. +void +VM_GetMultipleStackTraces::fill_frames(jthread jt, JavaThread *thr, oop thread_oop) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + + jint state = 0; + struct StackInfoNode *node = NEW_RESOURCE_OBJ(struct StackInfoNode); + jvmtiStackInfo *infop = &(node->info); + node->next = head(); + set_head(node); + infop->frame_count = 0; + infop->thread = jt; + + if (thread_oop != NULL) { + // get most state bits + state = (jint)java_lang_Thread::get_thread_status(thread_oop); + } + + if (thr != NULL) { // add more state bits if there is a JavaThead to query + // same as is_being_ext_suspended() but without locking + if (thr->is_ext_suspended() || thr->is_external_suspend()) { + state |= JVMTI_THREAD_STATE_SUSPENDED; + } + JavaThreadState jts = thr->thread_state(); + if (jts == _thread_in_native) { + state |= JVMTI_THREAD_STATE_IN_NATIVE; + } + OSThread* osThread = thr->osthread(); + if (osThread != NULL && osThread->interrupted()) { + state |= JVMTI_THREAD_STATE_INTERRUPTED; + } + } + infop->state = state; + + if (thr != NULL || (state & JVMTI_THREAD_STATE_ALIVE) != 0) { + infop->frame_buffer = NEW_RESOURCE_ARRAY(jvmtiFrameInfo, max_frame_count()); + env()->get_stack_trace(thr, 0, max_frame_count(), + infop->frame_buffer, &(infop->frame_count)); + } else { + infop->frame_buffer = NULL; + infop->frame_count = 0; + } + _frame_count_total += infop->frame_count; +} + +// Based on the stack information in the linked list, allocate memory +// block to return and fill it from the info in the linked list. +void +VM_GetMultipleStackTraces::allocate_and_fill_stacks(jint thread_count) { + // do I need to worry about alignment issues? + jlong alloc_size = thread_count * sizeof(jvmtiStackInfo) + + _frame_count_total * sizeof(jvmtiFrameInfo); + env()->allocate(alloc_size, (unsigned char **)&_stack_info); + + // pointers to move through the newly allocated space as it is filled in + jvmtiStackInfo *si = _stack_info + thread_count; // bottom of stack info + jvmtiFrameInfo *fi = (jvmtiFrameInfo *)si; // is the top of frame info + + // copy information in resource area into allocated buffer + // insert stack info backwards since linked list is backwards + // insert frame info forwards + // walk the StackInfoNodes + for (struct StackInfoNode *sin = head(); sin != NULL; sin = sin->next) { + jint frame_count = sin->info.frame_count; + size_t frames_size = frame_count * sizeof(jvmtiFrameInfo); + --si; + memcpy(si, &(sin->info), sizeof(jvmtiStackInfo)); + if (frames_size == 0) { + si->frame_buffer = NULL; + } else { + memcpy(fi, sin->info.frame_buffer, frames_size); + si->frame_buffer = fi; // point to the new allocated copy of the frames + fi += frame_count; + } + } + assert(si == _stack_info, "the last copied stack info must be the first record"); + assert((unsigned char *)fi == ((unsigned char *)_stack_info) + alloc_size, + "the last copied frame info must be the last record"); +} + + +void +VM_GetThreadListStackTraces::doit() { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + + ResourceMark rm; + for (int i = 0; i < _thread_count; ++i) { + jthread jt = _thread_list[i]; + oop thread_oop = JNIHandles::resolve_external_guard(jt); + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::thread_klass())) { + set_result(JVMTI_ERROR_INVALID_THREAD); + return; + } + fill_frames(jt, java_lang_Thread::thread(thread_oop), thread_oop); + } + allocate_and_fill_stacks(_thread_count); +} + +void +VM_GetAllStackTraces::doit() { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + + ResourceMark rm; + _final_thread_count = 0; + for (JavaThread *jt = Threads::first(); jt != NULL; jt = jt->next()) { + oop thread_oop = jt->threadObj(); + if (thread_oop != NULL && + !jt->is_exiting() && + java_lang_Thread::is_alive(thread_oop) && + !jt->is_hidden_from_external_view()) { + ++_final_thread_count; + // Handle block of the calling thread is used to create local refs. + fill_frames((jthread)JNIHandles::make_local(_calling_thread, thread_oop), + jt, thread_oop); + } + } + allocate_and_fill_stacks(_final_thread_count); +} + +// Verifies that the top frame is a java frame in an expected state. +// Deoptimizes frame if needed. +// Checks that the frame method signature matches the return type (tos). +// HandleMark must be defined in the caller only. +// It is to keep a ret_ob_h handle alive after return to the caller. +jvmtiError +JvmtiEnvBase::check_top_frame(JavaThread* current_thread, JavaThread* java_thread, + jvalue value, TosState tos, Handle* ret_ob_h) { + ResourceMark rm(current_thread); + + vframe *vf = vframeFor(java_thread, 0); + NULL_CHECK(vf, JVMTI_ERROR_NO_MORE_FRAMES); + + javaVFrame *jvf = (javaVFrame*) vf; + if (!vf->is_java_frame() || jvf->method()->is_native()) { + return JVMTI_ERROR_OPAQUE_FRAME; + } + + // If the frame is a compiled one, need to deoptimize it. + if (vf->is_compiled_frame()) { + if (!vf->fr().can_be_deoptimized()) { + return JVMTI_ERROR_OPAQUE_FRAME; + } + VM_DeoptimizeFrame deopt(java_thread, jvf->fr().id()); + VMThread::execute(&deopt); + } + + // Get information about method return type + symbolHandle signature(current_thread, jvf->method()->signature()); + + ResultTypeFinder rtf(signature); + TosState fr_tos = as_TosState(rtf.type()); + if (fr_tos != tos) { + if (tos != itos || (fr_tos != btos && fr_tos != ctos && fr_tos != stos)) { + return JVMTI_ERROR_TYPE_MISMATCH; + } + } + + // Check that the jobject class matches the return type signature. + jobject jobj = value.l; + if (tos == atos && jobj != NULL) { // NULL reference is allowed + Handle ob_h = Handle(current_thread, JNIHandles::resolve_external_guard(jobj)); + NULL_CHECK(ob_h, JVMTI_ERROR_INVALID_OBJECT); + KlassHandle ob_kh = KlassHandle(current_thread, ob_h()->klass()); + NULL_CHECK(ob_kh, JVMTI_ERROR_INVALID_OBJECT); + + // Method return type signature. + char* ty_sign = 1 + strchr(signature->as_C_string(), ')'); + + if (!VM_GetOrSetLocal::is_assignable(ty_sign, Klass::cast(ob_kh()), current_thread)) { + return JVMTI_ERROR_TYPE_MISMATCH; + } + *ret_ob_h = ob_h; + } + return JVMTI_ERROR_NONE; +} /* end check_top_frame */ + + +// ForceEarlyReturn follows the PopFrame approach in many aspects. +// Main difference is on the last stage in the interpreter. +// The PopFrame stops method execution to continue execution +// from the same method call instruction. +// The ForceEarlyReturn forces return from method so the execution +// continues at the bytecode following the method call. + +// Threads_lock NOT held, java_thread not protected by lock +// java_thread - pre-checked + +jvmtiError +JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState tos) { + JavaThread* current_thread = JavaThread::current(); + HandleMark hm(current_thread); + uint32_t debug_bits = 0; + + // Check if java_thread is fully suspended + if (!is_thread_fully_suspended(java_thread, + true /* wait for suspend completion */, + &debug_bits)) { + return JVMTI_ERROR_THREAD_NOT_SUSPENDED; + } + + // retreive or create the state + JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread); + + // Check to see if a ForceEarlyReturn was already in progress + if (state->is_earlyret_pending()) { + // Probably possible for JVMTI clients to trigger this, but the + // JPDA backend shouldn't allow this to happen + return JVMTI_ERROR_INTERNAL; + } + { + // The same as for PopFrame. Workaround bug: + // 4812902: popFrame hangs if the method is waiting at a synchronize + // Catch this condition and return an error to avoid hanging. + // Now JVMTI spec allows an implementation to bail out with an opaque + // frame error. + OSThread* osThread = java_thread->osthread(); + if (osThread->get_state() == MONITOR_WAIT) { + return JVMTI_ERROR_OPAQUE_FRAME; + } + } + Handle ret_ob_h = Handle(); + jvmtiError err = check_top_frame(current_thread, java_thread, value, tos, &ret_ob_h); + if (err != JVMTI_ERROR_NONE) { + return err; + } + assert(tos != atos || value.l == NULL || ret_ob_h() != NULL, + "return object oop must not be NULL if jobject is not NULL"); + + // Update the thread state to reflect that the top frame must be + // forced to return. + // The current frame will be returned later when the suspended + // thread is resumed and right before returning from VM to Java. + // (see call_VM_base() in assembler_.cpp). + + state->set_earlyret_pending(); + state->set_earlyret_oop(ret_ob_h()); + state->set_earlyret_value(value, tos); + + // Set pending step flag for this early return. + // It is cleared when next step event is posted. + state->set_pending_step_for_earlyret(); + + return JVMTI_ERROR_NONE; +} /* end force_early_return */ + +void +JvmtiMonitorClosure::do_monitor(ObjectMonitor* mon) { + if ( _error != JVMTI_ERROR_NONE) { + // Error occurred in previous iteration so no need to add + // to the list. + return; + } + if (mon->owner() == _java_thread ) { + // Filter out on stack monitors collected during stack walk. + oop obj = (oop)mon->object(); + bool found = false; + for (int j = 0; j < _owned_monitors_list->length(); j++) { + jobject jobj = ((jvmtiMonitorStackDepthInfo*)_owned_monitors_list->at(j))->monitor; + oop check = JNIHandles::resolve(jobj); + if (check == obj) { + // On stack monitor already collected during the stack walk. + found = true; + break; + } + } + if (found == false) { + // This is off stack monitor (e.g. acquired via jni MonitorEnter). + jvmtiError err; + jvmtiMonitorStackDepthInfo *jmsdi; + err = _env->allocate(sizeof(jvmtiMonitorStackDepthInfo), (unsigned char **)&jmsdi); + if (err != JVMTI_ERROR_NONE) { + _error = err; + return; + } + Handle hobj(obj); + jmsdi->monitor = _env->jni_reference(_calling_thread, hobj); + // stack depth is unknown for this monitor. + jmsdi->stack_depth = -1; + _owned_monitors_list->append(jmsdi); + } + } +} + +#endif // !JVMTI_KERNEL diff --git a/hotspot/src/share/vm/prims/jvmtiEnvBase.hpp b/hotspot/src/share/vm/prims/jvmtiEnvBase.hpp new file mode 100644 index 00000000000..477725ffec5 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEnvBase.hpp @@ -0,0 +1,596 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _JAVA_JVMTIENVBASE_H_ +#define _JAVA_JVMTIENVBASE_H_ + +// +// Forward Declarations +// + +class JvmtiEnv; +class JvmtiThreadState; +class JvmtiRawMonitor; // for jvmtiEnv.hpp +class JvmtiEventControllerPrivate; +class JvmtiTagMap; + + + +// One JvmtiEnv object is created per jvmti attachment; +// done via JNI GetEnv() call. Multiple attachments are +// allowed in jvmti. + +class JvmtiEnvBase : public CHeapObj { + + private: + + static JvmtiEnvBase* _head_environment; // head of environment list + + static bool _globally_initialized; + static jvmtiPhase _phase; + static volatile int _dying_thread_env_iteration_count; + + public: + + enum { + JDK15_JVMTI_VERSION = JVMTI_VERSION_1_0 + 33, /* version: 1.0.33 */ + JDK16_JVMTI_VERSION = JVMTI_VERSION_1_1 + 102 /* version: 1.1.102 */ + }; + + static jvmtiPhase get_phase() { return _phase; } + static void set_phase(jvmtiPhase phase) { _phase = phase; } + static bool is_vm_live() { return _phase == JVMTI_PHASE_LIVE; } + + static void entering_dying_thread_env_iteration() { ++_dying_thread_env_iteration_count; } + static void leaving_dying_thread_env_iteration() { --_dying_thread_env_iteration_count; } + static bool is_inside_dying_thread_env_iteration(){ return _dying_thread_env_iteration_count > 0; } + + private: + + enum { + JVMTI_MAGIC = 0x71EE, + DISPOSED_MAGIC = 0xDEFC, + BAD_MAGIC = 0xDEAD + }; + + jvmtiEnv _jvmti_external; + jint _magic; + JvmtiEnvBase* _next; + bool _is_retransformable; + const void *_env_local_storage; // per env agent allocated data. + jvmtiEventCallbacks _event_callbacks; + jvmtiExtEventCallbacks _ext_event_callbacks; + JvmtiTagMap* _tag_map; + JvmtiEnvEventEnable _env_event_enable; + jvmtiCapabilities _current_capabilities; + jvmtiCapabilities _prohibited_capabilities; + volatile bool _class_file_load_hook_ever_enabled; + static volatile bool _needs_clean_up; + char** _native_method_prefixes; + int _native_method_prefix_count; + + protected: + JvmtiEnvBase(); + ~JvmtiEnvBase(); + void dispose(); + void env_dispose(); + + void set_env_local_storage(const void* data) { _env_local_storage = data; } + const void* get_env_local_storage() { return _env_local_storage; } + + void record_class_file_load_hook_enabled(); + void record_first_time_class_file_load_hook_enabled(); + + char** get_native_method_prefixes() { return _native_method_prefixes; } + int get_native_method_prefix_count() { return _native_method_prefix_count; } + jvmtiError set_native_method_prefixes(jint prefix_count, char** prefixes); + + private: + friend class JvmtiEventControllerPrivate; + void initialize(); + void set_event_callbacks(const jvmtiEventCallbacks* callbacks, jint size_of_callbacks); + static void globally_initialize(); + static void periodic_clean_up(); + + friend class JvmtiEnvIterator; + JvmtiEnv* next_environment() { return (JvmtiEnv*)_next; } + void set_next_environment(JvmtiEnvBase* env) { _next = env; } + static JvmtiEnv* head_environment() { return (JvmtiEnv*)_head_environment; } + + public: + + bool is_valid() { return _magic == JVMTI_MAGIC; } + + bool is_retransformable() { return _is_retransformable; } + + static ByteSize jvmti_external_offset() { + return byte_offset_of(JvmtiEnvBase, _jvmti_external); + }; + + static JvmtiEnv* JvmtiEnv_from_jvmti_env(jvmtiEnv *env) { + return (JvmtiEnv*)((intptr_t)env - in_bytes(jvmti_external_offset())); + }; + + jvmtiCapabilities *get_capabilities() { return &_current_capabilities; } + + jvmtiCapabilities *get_prohibited_capabilities() { return &_prohibited_capabilities; } + + static char** get_all_native_method_prefixes(int* count_ptr); + + // This test will answer true when all environments have been disposed and some have + // not yet been deallocated. As a result, this test should only be used as an + // optimization for the no environment case. + static bool environments_might_exist() { + return head_environment() != NULL; + } + + static void check_for_periodic_clean_up(); + + JvmtiEnvEventEnable *env_event_enable() { + return &_env_event_enable; + } + + jvmtiError allocate(jlong size, unsigned char** mem_ptr) { + if (size < 0) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + if (size == 0) { + *mem_ptr = NULL; + } else { + *mem_ptr = (unsigned char *)os::malloc((size_t)size); + if (*mem_ptr == NULL) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } + } + return JVMTI_ERROR_NONE; + } + + jvmtiError deallocate(unsigned char* mem) { + if (mem != NULL) { + os::free(mem); + } + return JVMTI_ERROR_NONE; + } + + + // Memory functions + unsigned char* jvmtiMalloc(jlong size); // don't use this - call allocate + + // method to create a local handle + jobject jni_reference(Handle hndl) { + return JNIHandles::make_local(hndl()); + } + + // method to create a local handle. + // This function allows caller to specify which + // threads local handle table to use. + jobject jni_reference(JavaThread *thread, Handle hndl) { + return JNIHandles::make_local(thread, hndl()); + } + + // method to destroy a local handle + void destroy_jni_reference(jobject jobj) { + JNIHandles::destroy_local(jobj); + } + + // method to destroy a local handle. + // This function allows caller to specify which + // threads local handle table to use although currently it is + // not used. + void destroy_jni_reference(JavaThread *thread, jobject jobj) { + destroy_jni_reference(jobj); + } + + jvmtiEnv* jvmti_external() { return &_jvmti_external; }; + +// Event Dispatch + + bool has_callback(jvmtiEvent event_type) { + assert(event_type >= JVMTI_MIN_EVENT_TYPE_VAL && + event_type <= JVMTI_MAX_EVENT_TYPE_VAL, "checking"); + return ((void**)&_event_callbacks)[event_type-JVMTI_MIN_EVENT_TYPE_VAL] != NULL; + } + + jvmtiEventCallbacks* callbacks() { + return &_event_callbacks; + } + + jvmtiExtEventCallbacks* ext_callbacks() { + return &_ext_event_callbacks; + } + + void set_tag_map(JvmtiTagMap* tag_map) { + _tag_map = tag_map; + } + + JvmtiTagMap* tag_map() { + return _tag_map; + } + + + // return true if event is enabled globally or for any thread + // True only if there is a callback for it. + bool is_enabled(jvmtiEvent event_type) { + return _env_event_enable.is_enabled(event_type); + } + +// Random Utilities + + protected: + // helper methods for creating arrays of global JNI Handles from local Handles + // allocated into environment specific storage + jobject * new_jobjectArray(int length, Handle *handles); + jthread * new_jthreadArray(int length, Handle *handles); + jthreadGroup * new_jthreadGroupArray(int length, Handle *handles); + + // convert from JNIHandle to JavaThread * + JavaThread * get_JavaThread(jthread jni_thread); + + // convert to a jni jclass from a non-null klassOop + jclass get_jni_class_non_null(klassOop k); + + void update_klass_field_access_flag(fieldDescriptor *fd); + + jint count_locked_objects(JavaThread *java_thread, Handle hobj); + jvmtiError get_locked_objects_in_frame(JavaThread *calling_thread, + JavaThread* java_thread, + javaVFrame *jvf, + GrowableArray* owned_monitors_list, + jint depth); + vframe* vframeFor(JavaThread* java_thread, jint depth); + + public: + // get a field descriptor for the specified class and field + static bool get_field_descriptor(klassOop k, jfieldID field, fieldDescriptor* fd); + // test for suspend - most (all?) of these should go away + static bool is_thread_fully_suspended(JavaThread *thread, + bool wait_for_suspend, + uint32_t *bits); + + + // JVMTI API helper functions which are called at safepoint or thread is suspended. + jvmtiError get_frame_count(JvmtiThreadState *state, jint *count_ptr); + jvmtiError get_frame_location(JavaThread* java_thread, jint depth, + jmethodID* method_ptr, jlocation* location_ptr); + jvmtiError get_object_monitor_usage(JavaThread *calling_thread, + jobject object, jvmtiMonitorUsage* info_ptr); + jvmtiError get_stack_trace(JavaThread *java_thread, + jint stack_depth, jint max_count, + jvmtiFrameInfo* frame_buffer, jint* count_ptr); + jvmtiError get_current_contended_monitor(JavaThread *calling_thread, + JavaThread *java_thread, + jobject *monitor_ptr); + jvmtiError get_owned_monitors(JavaThread *calling_thread, JavaThread* java_thread, + GrowableArray *owned_monitors_list); + jvmtiError check_top_frame(JavaThread* current_thread, JavaThread* java_thread, + jvalue value, TosState tos, Handle* ret_ob_h); + jvmtiError force_early_return(JavaThread* java_thread, jvalue value, TosState tos); +}; + +// This class is the only safe means of iterating through environments. +// Note that this iteratation includes invalid environments pending +// deallocation -- in fact, some uses depend on this behavior. + +class JvmtiEnvIterator : public StackObj { + private: + bool _entry_was_marked; + public: + JvmtiEnvIterator() { + if (Threads::number_of_threads() == 0) { + _entry_was_marked = false; // we are single-threaded, no need + } else { + Thread::current()->entering_jvmti_env_iteration(); + _entry_was_marked = true; + } + } + ~JvmtiEnvIterator() { + if (_entry_was_marked) { + Thread::current()->leaving_jvmti_env_iteration(); + } + } + JvmtiEnv* first() { return JvmtiEnvBase::head_environment(); } + JvmtiEnv* next(JvmtiEnvBase* env) { return env->next_environment(); } +}; + + +// VM operation to get monitor information with stack depth. +class VM_GetOwnedMonitorInfo : public VM_Operation { +private: + JvmtiEnv *_env; + JavaThread* _calling_thread; + JavaThread *_java_thread; + jvmtiError _result; + GrowableArray *_owned_monitors_list; + +public: + VM_GetOwnedMonitorInfo(JvmtiEnv* env, JavaThread* calling_thread, + JavaThread* java_thread, + GrowableArray* owned_monitor_list) { + _env = env; + _calling_thread = calling_thread; + _java_thread = java_thread; + _owned_monitors_list = owned_monitor_list; + _result = JVMTI_ERROR_NONE; + } + VMOp_Type type() const { return VMOp_GetOwnedMonitorInfo; } + void doit() { + ((JvmtiEnvBase *)_env)->get_owned_monitors(_calling_thread, _java_thread, + _owned_monitors_list); + } + jvmtiError result() { return _result; } +}; + + +// VM operation to get object monitor usage. +class VM_GetObjectMonitorUsage : public VM_Operation { +private: + JvmtiEnv *_env; + jobject _object; + JavaThread* _calling_thread; + jvmtiMonitorUsage* _info_ptr; + jvmtiError _result; + +public: + VM_GetObjectMonitorUsage(JvmtiEnv *env, JavaThread* calling_thread, jobject object, jvmtiMonitorUsage* info_ptr) { + _env = env; + _object = object; + _calling_thread = calling_thread; + _info_ptr = info_ptr; + } + VMOp_Type type() const { return VMOp_GetObjectMonitorUsage; } + jvmtiError result() { return _result; } + void doit() { + _result = ((JvmtiEnvBase*) _env)->get_object_monitor_usage(_calling_thread, _object, _info_ptr); + } + +}; + +// VM operation to get current contended monitor. +class VM_GetCurrentContendedMonitor : public VM_Operation { +private: + JvmtiEnv *_env; + JavaThread *_calling_thread; + JavaThread *_java_thread; + jobject *_owned_monitor_ptr; + jvmtiError _result; + +public: + VM_GetCurrentContendedMonitor(JvmtiEnv *env, JavaThread *calling_thread, JavaThread *java_thread, jobject *mon_ptr) { + _env = env; + _calling_thread = calling_thread; + _java_thread = java_thread; + _owned_monitor_ptr = mon_ptr; + } + VMOp_Type type() const { return VMOp_GetCurrentContendedMonitor; } + jvmtiError result() { return _result; } + void doit() { + _result = ((JvmtiEnvBase *)_env)->get_current_contended_monitor(_calling_thread,_java_thread,_owned_monitor_ptr); + } +}; + +// VM operation to get stack trace at safepoint. +class VM_GetStackTrace : public VM_Operation { +private: + JvmtiEnv *_env; + JavaThread *_java_thread; + jint _start_depth; + jint _max_count; + jvmtiFrameInfo *_frame_buffer; + jint *_count_ptr; + jvmtiError _result; + +public: + VM_GetStackTrace(JvmtiEnv *env, JavaThread *java_thread, + jint start_depth, jint max_count, + jvmtiFrameInfo* frame_buffer, jint* count_ptr) { + _env = env; + _java_thread = java_thread; + _start_depth = start_depth; + _max_count = max_count; + _frame_buffer = frame_buffer; + _count_ptr = count_ptr; + } + jvmtiError result() { return _result; } + VMOp_Type type() const { return VMOp_GetStackTrace; } + void doit() { + _result = ((JvmtiEnvBase *)_env)->get_stack_trace(_java_thread, + _start_depth, _max_count, + _frame_buffer, _count_ptr); + } +}; + +// forward declaration +struct StackInfoNode; + +// VM operation to get stack trace at safepoint. +class VM_GetMultipleStackTraces : public VM_Operation { +private: + JvmtiEnv *_env; + jint _max_frame_count; + jvmtiStackInfo *_stack_info; + jvmtiError _result; + int _frame_count_total; + struct StackInfoNode *_head; + + JvmtiEnvBase *env() { return (JvmtiEnvBase *)_env; } + jint max_frame_count() { return _max_frame_count; } + struct StackInfoNode *head() { return _head; } + void set_head(StackInfoNode *head) { _head = head; } + +protected: + void set_result(jvmtiError result) { _result = result; } + void fill_frames(jthread jt, JavaThread *thr, oop thread_oop); + void allocate_and_fill_stacks(jint thread_count); + +public: + VM_GetMultipleStackTraces(JvmtiEnv *env, jint max_frame_count) { + _env = env; + _max_frame_count = max_frame_count; + _frame_count_total = 0; + _head = NULL; + _result = JVMTI_ERROR_NONE; + } + VMOp_Type type() const { return VMOp_GetMultipleStackTraces; } + jvmtiStackInfo *stack_info() { return _stack_info; } + jvmtiError result() { return _result; } +}; + + +// VM operation to get stack trace at safepoint. +class VM_GetAllStackTraces : public VM_GetMultipleStackTraces { +private: + JavaThread *_calling_thread; + jint _final_thread_count; + +public: + VM_GetAllStackTraces(JvmtiEnv *env, JavaThread *calling_thread, + jint max_frame_count) + : VM_GetMultipleStackTraces(env, max_frame_count) { + _calling_thread = calling_thread; + } + VMOp_Type type() const { return VMOp_GetAllStackTraces; } + void doit(); + jint final_thread_count() { return _final_thread_count; } +}; + +// VM operation to get stack trace at safepoint. +class VM_GetThreadListStackTraces : public VM_GetMultipleStackTraces { +private: + jint _thread_count; + const jthread* _thread_list; + +public: + VM_GetThreadListStackTraces(JvmtiEnv *env, jint thread_count, const jthread* thread_list, jint max_frame_count) + : VM_GetMultipleStackTraces(env, max_frame_count) { + _thread_count = thread_count; + _thread_list = thread_list; + } + VMOp_Type type() const { return VMOp_GetThreadListStackTraces; } + void doit(); +}; + + +// VM operation to count stack frames at safepoint. +class VM_GetFrameCount : public VM_Operation { +private: + JvmtiEnv *_env; + JvmtiThreadState *_state; + jint *_count_ptr; + jvmtiError _result; + +public: + VM_GetFrameCount(JvmtiEnv *env, JvmtiThreadState *state, jint *count_ptr) { + _env = env; + _state = state; + _count_ptr = count_ptr; + } + VMOp_Type type() const { return VMOp_GetFrameCount; } + jvmtiError result() { return _result; } + void doit() { + _result = ((JvmtiEnvBase*)_env)->get_frame_count(_state, _count_ptr); + } +}; + +// VM operation to frame location at safepoint. +class VM_GetFrameLocation : public VM_Operation { +private: + JvmtiEnv *_env; + JavaThread* _java_thread; + jint _depth; + jmethodID* _method_ptr; + jlocation* _location_ptr; + jvmtiError _result; + +public: + VM_GetFrameLocation(JvmtiEnv *env, JavaThread* java_thread, jint depth, + jmethodID* method_ptr, jlocation* location_ptr) { + _env = env; + _java_thread = java_thread; + _depth = depth; + _method_ptr = method_ptr; + _location_ptr = location_ptr; + } + VMOp_Type type() const { return VMOp_GetFrameLocation; } + jvmtiError result() { return _result; } + void doit() { + _result = ((JvmtiEnvBase*)_env)->get_frame_location(_java_thread, _depth, + _method_ptr, _location_ptr); + } +}; + + +// ResourceTracker +// +// ResourceTracker works a little like a ResourceMark. All allocates +// using the resource tracker are recorded. If an allocate using the +// resource tracker fails the destructor will free any resources +// that were allocated using the tracker. +// The motive for this class is to avoid messy error recovery code +// in situations where multiple allocations are done in sequence. If +// the second or subsequent allocation fails it avoids any code to +// release memory allocated in the previous calls. +// +// Usage :- +// ResourceTracker rt(env); +// : +// err = rt.allocate(1024, &ptr); + +class ResourceTracker : public StackObj { + private: + JvmtiEnv* _env; + GrowableArray *_allocations; + bool _failed; + public: + ResourceTracker(JvmtiEnv* env); + ~ResourceTracker(); + jvmtiError allocate(jlong size, unsigned char** mem_ptr); + unsigned char* allocate(jlong size); + char* strdup(const char* str); +}; + + +// Jvmti monitor closure to collect off stack monitors. +class JvmtiMonitorClosure: public MonitorClosure { + private: + JavaThread *_java_thread; + JavaThread *_calling_thread; + GrowableArray *_owned_monitors_list; + jvmtiError _error; + JvmtiEnvBase *_env; + + public: + JvmtiMonitorClosure(JavaThread* thread, JavaThread *calling_thread, + GrowableArray *owned_monitors, + JvmtiEnvBase *env) { + _java_thread = thread; + _calling_thread = calling_thread; + _owned_monitors_list = owned_monitors; + _error = JVMTI_ERROR_NONE; + _env = env; + } + void do_monitor(ObjectMonitor* mon); + jvmtiError error() { return _error;} +}; + +#endif /* _JAVA_JVMTIENVBASE_H_ */ diff --git a/hotspot/src/share/vm/prims/jvmtiEnvFill.java b/hotspot/src/share/vm/prims/jvmtiEnvFill.java new file mode 100644 index 00000000000..dfd28c306bc --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEnvFill.java @@ -0,0 +1,260 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.io.*; +import java.util.*; + +class jvmtiEnvFill { + + public static void main(String[] args) throws IOException { + if (args.length != 3) { + System.err.println("usage: "); + System.exit(1); + } + String filledFN = args[0]; + String stubFN = args[1]; + String resultFN = args[2]; + + SourceFile filledSF = new SourceFile(filledFN); + SourceFile stubSF = new SourceFile(stubFN); + + + stubSF.fill(filledSF); + + PrintWriter out = new PrintWriter(new FileWriter(resultFN)); + stubSF.output(out); + out.close(); + } +} + +class SourceFile { + + static final String endFilePrefix = "// end file prefix"; + static final String functionPrefix = "JvmtiEnv::"; + + final String fn; + LineNumberReader in; + String line; + List top = new ArrayList(); + List before = new ArrayList(); + boolean inFilePrefix = true; + List functions = new ArrayList(); + Map functionMap = new HashMap(); + + class Function { + String name; + String args; + String compareArgs; + List comment; + List body = new ArrayList(); + + Function() throws IOException { + line = in.readLine(); + String trimmed = line.trim(); + if (!trimmed.startsWith(functionPrefix)) { + error("expected '" + functionPrefix + "'"); + } + int index = trimmed.indexOf('(', functionPrefix.length()); + if (index == -1) { + error("missing open paren"); + } + name = trimmed.substring(functionPrefix.length(), index); + int index2 = trimmed.indexOf(')', index); + if (index2 == -1) { + error("missing close paren - must be on same line"); + } + args = trimmed.substring(index+1, index2); + compareArgs = args.replaceAll("\\s", ""); + String tail = trimmed.substring(index2+1).trim(); + if (!tail.equals("{")) { + error("function declaration first line must end with open bracket '{', instead got '" + + tail + "'"); + } + while(true) { + line = in.readLine(); + if (line == null) { + line = ""; // so error does not look wierd + error("unexpected end of file"); + } + if (line.startsWith("}")) { + break; + } + body.add(line); + } + String expected = "} /* end " + name + " */"; + trimmed = line.replaceAll("\\s",""); + if (!trimmed.equals(expected.replaceAll("\\s",""))) { + error("function end is malformed - should be: " + expected); + } + // copy over the comment prefix + comment = before; + before = new ArrayList(); + } + + void remove() { + functionMap.remove(name); + } + + String fileName() { + return fn; + } + + void fill(Function filledFunc) { + if (filledFunc == null) { + System.err.println("Warning: function " + name + " missing from filled file"); + body.add(0, " /*** warning: function added and not filled in ***/"); + } else { + int fbsize = filledFunc.body.size(); + int bsize = body.size(); + if (fbsize > bsize || !body.subList(bsize-fbsize,bsize).equals(filledFunc.body)) { + // it has actually been filled in + body = filledFunc.body; + if (!compareArgs.equals(filledFunc.compareArgs)) { + System.err.println("Warning: function " + name + + ": filled and stub arguments differ"); + System.err.println(" old (filled): " + filledFunc.args); + System.err.println(" new (stub): " + args); + body.add(0, " /*** warning: arguments changed, were: " + + filledFunc.args + " ***/"); + } + } + filledFunc.remove(); // mark used + } + } + + void output(PrintWriter out) { + Iterator it = comment.iterator(); + while (it.hasNext()) { + out.println(it.next()); + } + out.println("jvmtiError"); + out.print(functionPrefix); + out.print(name); + out.print('('); + out.print(args); + out.println(") {"); + it = body.iterator(); + while (it.hasNext()) { + out.println(it.next()); + } + out.print("} /* end "); + out.print(name); + out.println(" */"); + } + } + + SourceFile(String fn) throws IOException { + this.fn = fn; + Reader reader = new FileReader(fn); + in = new LineNumberReader(reader); + + while (readGaps()) { + Function func = new Function(); + functionMap.put(func.name, func); + functions.add(func); + } + + in.close(); + } + + void error(String msg) { + System.err.println("Fatal error parsing file: " + fn); + System.err.println("Line number: " + in.getLineNumber()); + System.err.println("Error message: " + msg); + System.err.println("Source line: " + line); + System.exit(1); + } + + boolean readGaps() throws IOException { + while(true) { + line = in.readLine(); + if (line == null) { + return false; // end of file + } + if (!inFilePrefix && line.startsWith("}")) { + error("unexpected close bracket in first column, outside of function.\n"); + } + String trimmed = line.trim(); + if (line.startsWith("jvmtiError")) { + if (trimmed.equals("jvmtiError")) { + if (inFilePrefix) { + error("unexpected 'jvmtiError' line in file prefix.\n" + + "is '" + endFilePrefix + "'... line missing?"); + } + return true; // beginning of a function + } else { + error("extra characters at end of 'jvmtiError'"); + } + } + if (inFilePrefix) { + top.add(line); + } else { + trimmed = line.trim(); + if (!trimmed.equals("") && !trimmed.startsWith("//") && !trimmed.startsWith("#")) { + error("only comments and blank lines allowed between functions"); + } + before.add(line); + } + if (line.replaceAll("\\s","").toLowerCase().startsWith(endFilePrefix.replaceAll("\\s",""))) { + if (!inFilePrefix) { + error("excess '" + endFilePrefix + "'"); + } + inFilePrefix = false; + } + } + } + + void fill(SourceFile filledSF) { + // copy beginning of file straight from filled file + top = filledSF.top; + + // file in functions + Iterator it = functions.iterator(); + while (it.hasNext()) { + Function stubFunc = (Function)(it.next()); + Function filledFunc = (Function)filledSF.functionMap.get(stubFunc.name); + stubFunc.fill(filledFunc); + } + if (filledSF.functionMap.size() > 0) { + System.err.println("Warning: the following functions were present in the " + + "filled file but missing in the stub file and thus not copied:"); + it = filledSF.functionMap.values().iterator(); + while (it.hasNext()) { + System.err.println(" " + ((Function)(it.next())).name); + } + } + } + + void output(PrintWriter out) { + Iterator it = top.iterator(); + while (it.hasNext()) { + out.println(it.next()); + } + it = functions.iterator(); + while (it.hasNext()) { + Function stubFunc = (Function)(it.next()); + stubFunc.output(out); + } + } +} diff --git a/hotspot/src/share/vm/prims/jvmtiEnvThreadState.cpp b/hotspot/src/share/vm/prims/jvmtiEnvThreadState.cpp new file mode 100644 index 00000000000..b1b0a0d8efa --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEnvThreadState.cpp @@ -0,0 +1,313 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiEnvThreadState.cpp.incl" + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiFramePop +// + +#ifndef PRODUCT +void JvmtiFramePop::print() { + tty->print_cr("_frame_number=%d", _frame_number); +} +#endif + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiFramePops - private methods +// + +void +JvmtiFramePops::set(JvmtiFramePop& fp) { + if (_pops->find(fp.frame_number()) < 0) { + _pops->append(fp.frame_number()); + } +} + + +void +JvmtiFramePops::clear(JvmtiFramePop& fp) { + assert(_pops->length() > 0, "No more frame pops"); + + _pops->remove(fp.frame_number()); +} + + +int +JvmtiFramePops::clear_to(JvmtiFramePop& fp) { + int cleared = 0; + int index = 0; + while (index < _pops->length()) { + JvmtiFramePop pop = JvmtiFramePop(_pops->at(index)); + if (pop.above_on_stack(fp)) { + _pops->remove_at(index); + ++cleared; + } else { + ++index; + } + } + return cleared; +} + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiFramePops - public methods +// + +JvmtiFramePops::JvmtiFramePops() { + _pops = new (ResourceObj::C_HEAP) GrowableArray (2, true); +} + +JvmtiFramePops::~JvmtiFramePops() { + // return memory to c_heap. + delete _pops; +} + + +#ifndef PRODUCT +void JvmtiFramePops::print() { + ResourceMark rm; + + int n = _pops->length(); + for (int i=0; iat(i)); + tty->print("%d: ", i); + fp.print(); + tty->print_cr(""); + } +} +#endif + +/////////////////////////////////////////////////////////////// +// +// class JvmtiEnvThreadState +// +// Instances of JvmtiEnvThreadState hang off of each JvmtiThreadState, +// one per JvmtiEnv. +// + +JvmtiEnvThreadState::JvmtiEnvThreadState(JavaThread *thread, JvmtiEnvBase *env) : + _event_enable() { + _thread = thread; + _env = (JvmtiEnv*)env; + _next = NULL; + _frame_pops = NULL; + _current_bci = 0; + _current_method_id = NULL; + _breakpoint_posted = false; + _single_stepping_posted = false; + _agent_thread_local_storage_data = NULL; +} + +JvmtiEnvThreadState::~JvmtiEnvThreadState() { + delete _frame_pops; + _frame_pops = NULL; +} + +// Given that a new (potential) event has come in, +// maintain the current JVMTI location on a per-thread per-env basis +// and use it to filter out duplicate events: +// - instruction rewrites +// - breakpoint followed by single step +// - single step at a breakpoint +void JvmtiEnvThreadState::compare_and_set_current_location(methodOop new_method, + address new_location, jvmtiEvent event) { + + int new_bci = new_location - new_method->code_base(); + + // The method is identified and stored as a jmethodID which is safe in this + // case because the class cannot be unloaded while a method is executing. + jmethodID new_method_id = new_method->jmethod_id(); + + // the last breakpoint or single step was at this same location + if (_current_bci == new_bci && _current_method_id == new_method_id) { + switch (event) { + case JVMTI_EVENT_BREAKPOINT: + // Repeat breakpoint is complicated. If we previously posted a breakpoint + // event at this location and if we also single stepped at this location + // then we skip the duplicate breakpoint. + _breakpoint_posted = _breakpoint_posted && _single_stepping_posted; + break; + case JVMTI_EVENT_SINGLE_STEP: + // Repeat single step is easy: just don't post it again. + // If step is pending for popframe then it may not be + // a repeat step. The new_bci and method_id is same as current_bci + // and current method_id after pop and step for recursive calls. + // This has been handled by clearing the location + _single_stepping_posted = true; + break; + default: + assert(false, "invalid event value passed"); + break; + } + return; + } + + set_current_location(new_method_id, new_bci); + _breakpoint_posted = false; + _single_stepping_posted = false; +} + + +JvmtiFramePops* JvmtiEnvThreadState::get_frame_pops() { +#ifdef ASSERT + uint32_t debug_bits = 0; +#endif + assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits), + "frame pop data only accessible from same thread or while suspended"); + + if (_frame_pops == NULL) { + _frame_pops = new JvmtiFramePops(); + assert(_frame_pops != NULL, "_frame_pops != NULL"); + } + return _frame_pops; +} + + +bool JvmtiEnvThreadState::has_frame_pops() { + return _frame_pops == NULL? false : (_frame_pops->length() > 0); +} + +void JvmtiEnvThreadState::set_frame_pop(int frame_number) { +#ifdef ASSERT + uint32_t debug_bits = 0; +#endif + assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits), + "frame pop data only accessible from same thread or while suspended"); + JvmtiFramePop fpop(frame_number); + JvmtiEventController::set_frame_pop(this, fpop); +} + + +void JvmtiEnvThreadState::clear_frame_pop(int frame_number) { +#ifdef ASSERT + uint32_t debug_bits = 0; +#endif + assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits), + "frame pop data only accessible from same thread or while suspended"); + JvmtiFramePop fpop(frame_number); + JvmtiEventController::clear_frame_pop(this, fpop); +} + + +void JvmtiEnvThreadState::clear_to_frame_pop(int frame_number) { +#ifdef ASSERT + uint32_t debug_bits = 0; +#endif + assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits), + "frame pop data only accessible from same thread or while suspended"); + JvmtiFramePop fpop(frame_number); + JvmtiEventController::clear_to_frame_pop(this, fpop); +} + + +bool JvmtiEnvThreadState::is_frame_pop(int cur_frame_number) { +#ifdef ASSERT + uint32_t debug_bits = 0; +#endif + assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits), + "frame pop data only accessible from same thread or while suspended"); + if (!get_thread()->is_interp_only_mode() || _frame_pops == NULL) { + return false; + } + JvmtiFramePop fp(cur_frame_number); + return get_frame_pops()->contains(fp); +} + + +class VM_GetCurrentLocation : public VM_Operation { + private: + JavaThread *_thread; + jmethodID _method_id; + int _bci; + + public: + VM_GetCurrentLocation(JavaThread *thread) { + _thread = thread; + } + VMOp_Type type() const { return VMOp_GetCurrentLocation; } + void doit() { + ResourceMark rmark; // _thread != Thread::current() + RegisterMap rm(_thread, false); + javaVFrame* vf = _thread->last_java_vframe(&rm); + assert(vf != NULL, "must have last java frame"); + methodOop method = vf->method(); + _method_id = method->jmethod_id(); + _bci = vf->bci(); + } + void get_current_location(jmethodID *method_id, int *bci) { + *method_id = _method_id; + *bci = _bci; + } +}; + +void JvmtiEnvThreadState::reset_current_location(jvmtiEvent event_type, bool enabled) { + assert(event_type == JVMTI_EVENT_SINGLE_STEP || event_type == JVMTI_EVENT_BREAKPOINT, + "must be single-step or breakpoint event"); + + // Current location is used to detect the following: + // 1) a breakpoint event followed by single-stepping to the same bci + // 2) single-step to a bytecode that will be transformed to a fast version + // We skip to avoid posting the duplicate single-stepping event. + + // If single-stepping is disabled, clear current location so that + // single-stepping to the same method and bcp at a later time will be + // detected if single-stepping is enabled at that time (see 4388912). + + // If single-stepping is enabled, set the current location to the + // current method and bcp. This covers the following type of case, + // e.g., the debugger stepi command: + // - bytecode single stepped + // - SINGLE_STEP event posted and SINGLE_STEP event disabled + // - SINGLE_STEP event reenabled + // - bytecode rewritten to fast version + + // If breakpoint event is disabled, clear current location only if + // single-stepping is not enabled. Otherwise, keep the thread location + // to detect any duplicate events. + + if (enabled) { + // If enabling breakpoint, no need to reset. + // Can't do anything if empty stack. + if (event_type == JVMTI_EVENT_SINGLE_STEP && _thread->has_last_Java_frame()) { + jmethodID method_id; + int bci; + // The java thread stack may not be walkable for a running thread + // so get current location at safepoint. + VM_GetCurrentLocation op(_thread); + VMThread::execute(&op); + op.get_current_location(&method_id, &bci); + set_current_location(method_id, bci); + } + } else if (event_type == JVMTI_EVENT_SINGLE_STEP || !is_enabled(JVMTI_EVENT_SINGLE_STEP)) { + // If this is to disable breakpoint, also check if single-step is not enabled + clear_current_location(); + } +} diff --git a/hotspot/src/share/vm/prims/jvmtiEnvThreadState.hpp b/hotspot/src/share/vm/prims/jvmtiEnvThreadState.hpp new file mode 100644 index 00000000000..e0373f7e889 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEnvThreadState.hpp @@ -0,0 +1,175 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +#ifndef _JAVA_JVMTIENVTHREADSTATE_H_ +#define _JAVA_JVMTIENVTHREADSTATE_H_ + +/////////////////////////////////////////////////////////////// +// +// class JvmtiFramePop +// Used by : JvmtiFramePops +// Used by JVMTI methods: none directly. +// +// Wrapper class for FramePop, used in the JvmtiFramePops class. +// +// Two problems: 1) this isn't being used as a ValueObj class, in +// several places there are constructors for it. 2) It seems like +// overkill as a means to get an assert and name the geater than +// operator. I'm trying to to rewrite everything. + +class JvmtiFramePop VALUE_OBJ_CLASS_SPEC { + private: + // Frame number counting from BOTTOM (oldest) frame; + // bottom frame == #0 + int _frame_number; + public: + JvmtiFramePop() {} + JvmtiFramePop(int frame_number) { + assert(frame_number >= 0, "invalid frame number"); + _frame_number = frame_number; + } + + int frame_number() { return _frame_number; } + int above_on_stack(JvmtiFramePop& other) { return _frame_number > other._frame_number; } + void print() PRODUCT_RETURN; +}; + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiFramePops +// Used by : JvmtiThreadState +// Used by JVMTI methods: none directly. +// +// A collection of JvmtiFramePop. +// It records what frames on a threads stack should post frame_pop events when they're exited. +// + +class JvmtiFramePops : public CHeapObj { + private: + GrowableArray* _pops; + + // should only be used by JvmtiEventControllerPrivate + // to insure they only occur at safepoints. + // Todo: add checks for safepoint + friend class JvmtiEventControllerPrivate; + void set(JvmtiFramePop& fp); + void clear(JvmtiFramePop& fp); + int clear_to(JvmtiFramePop& fp); + + public: + JvmtiFramePops(); + ~JvmtiFramePops(); + + bool contains(JvmtiFramePop& fp) { return _pops->contains(fp.frame_number()); } + int length() { return _pops->length(); } + void print() PRODUCT_RETURN; +}; + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiEnvThreadState +// +// 2. Cache of pending frame_pop_events, created by NotifyFramePop +// and lazily initialized. +// 3: Location of last executed instruction, used to filter out duplicate +// events due to instruction rewriting. + +class JvmtiEnvThreadState : public CHeapObj { +private: + friend class JvmtiEnv; + JavaThread *_thread; + JvmtiEnv *_env; + JvmtiEnvThreadState *_next; + jmethodID _current_method_id; + int _current_bci; + bool _breakpoint_posted; + bool _single_stepping_posted; + JvmtiEnvThreadEventEnable _event_enable; + void *_agent_thread_local_storage_data; // per env and per thread agent allocated data. + + // Class used to store pending framepops. + // lazily initialized by get_frame_pops(); + JvmtiFramePops *_frame_pops; + + inline void set_current_location(jmethodID method_id, int bci) { + _current_method_id = method_id; + _current_bci = bci; + } + + friend class JvmtiEnvThreadStateIterator; + JvmtiEnvThreadState* next() { return _next; } + + friend class JvmtiThreadState; + void set_next(JvmtiEnvThreadState* link) { _next = link; } + +public: + JvmtiEnvThreadState(JavaThread *thread, JvmtiEnvBase *env); + ~JvmtiEnvThreadState(); + + bool is_enabled(jvmtiEvent event_type) { return _event_enable.is_enabled(event_type); } + + JvmtiEnvThreadEventEnable *event_enable() { return &_event_enable; } + void *get_agent_thread_local_storage_data() { return _agent_thread_local_storage_data; } + void set_agent_thread_local_storage_data (void *data) { _agent_thread_local_storage_data = data; } + + + // If the thread is in the given method at the given + // location just return. Otherwise, reset the current location + // and reset _breakpoint_posted and _single_stepping_posted. + // _breakpoint_posted and _single_stepping_posted are only cleared + // here. + void compare_and_set_current_location(methodOop method, address location, jvmtiEvent event); + + void clear_current_location() { set_current_location((jmethodID)NULL, 0); } + + void reset_current_location(jvmtiEvent event, bool enabled); + + inline void set_breakpoint_posted() { _breakpoint_posted = true; } + inline void set_single_stepping_posted() { + _single_stepping_posted = true; + } + inline bool breakpoint_posted() { return _breakpoint_posted; } + inline bool single_stepping_posted() { + return _single_stepping_posted; + } + + inline JavaThread *get_thread() { return _thread; } + inline JvmtiEnv *get_env() { return _env; } + + // lazily initialize _frame_pops + JvmtiFramePops* get_frame_pops(); + + bool has_frame_pops(); + + // quickly test whether we should deliver a frame pop event on return from sp + bool is_frame_pop(int cur_stack_depth); + + void set_frame_pop(int frame_number); + void clear_frame_pop(int frame_number); + void clear_to_frame_pop(int frame_number); + +}; + +#endif /* _JAVA_JVMTIENVTHREADSTATE_H_ */ diff --git a/hotspot/src/share/vm/prims/jvmtiEventController.cpp b/hotspot/src/share/vm/prims/jvmtiEventController.cpp new file mode 100644 index 00000000000..ebadd45e2c2 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEventController.cpp @@ -0,0 +1,1045 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiEventController.cpp.incl" + +#ifdef JVMTI_TRACE +#define EC_TRACE(out) if (JvmtiTrace::trace_event_controller()) { SafeResourceMark rm; tty->print_cr out; } while (0) +#else +#define EC_TRACE(out) +#endif /*JVMTI_TRACE */ + +// bits for standard events + +static const jlong SINGLE_STEP_BIT = (((jlong)1) << (JVMTI_EVENT_SINGLE_STEP - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong FRAME_POP_BIT = (((jlong)1) << (JVMTI_EVENT_FRAME_POP - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong BREAKPOINT_BIT = (((jlong)1) << (JVMTI_EVENT_BREAKPOINT - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong FIELD_ACCESS_BIT = (((jlong)1) << (JVMTI_EVENT_FIELD_ACCESS - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong FIELD_MODIFICATION_BIT = (((jlong)1) << (JVMTI_EVENT_FIELD_MODIFICATION - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong METHOD_ENTRY_BIT = (((jlong)1) << (JVMTI_EVENT_METHOD_ENTRY - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong METHOD_EXIT_BIT = (((jlong)1) << (JVMTI_EVENT_METHOD_EXIT - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong CLASS_FILE_LOAD_HOOK_BIT = (((jlong)1) << (JVMTI_EVENT_CLASS_FILE_LOAD_HOOK - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong NATIVE_METHOD_BIND_BIT = (((jlong)1) << (JVMTI_EVENT_NATIVE_METHOD_BIND - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong VM_START_BIT = (((jlong)1) << (JVMTI_EVENT_VM_START - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong VM_INIT_BIT = (((jlong)1) << (JVMTI_EVENT_VM_INIT - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong VM_DEATH_BIT = (((jlong)1) << (JVMTI_EVENT_VM_DEATH - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong CLASS_LOAD_BIT = (((jlong)1) << (JVMTI_EVENT_CLASS_LOAD - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong CLASS_PREPARE_BIT = (((jlong)1) << (JVMTI_EVENT_CLASS_PREPARE - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong THREAD_START_BIT = (((jlong)1) << (JVMTI_EVENT_THREAD_START - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong THREAD_END_BIT = (((jlong)1) << (JVMTI_EVENT_THREAD_END - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong EXCEPTION_THROW_BIT = (((jlong)1) << (JVMTI_EVENT_EXCEPTION - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong EXCEPTION_CATCH_BIT = (((jlong)1) << (JVMTI_EVENT_EXCEPTION_CATCH - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong MONITOR_CONTENDED_ENTER_BIT = (((jlong)1) << (JVMTI_EVENT_MONITOR_CONTENDED_ENTER - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong MONITOR_CONTENDED_ENTERED_BIT = (((jlong)1) << (JVMTI_EVENT_MONITOR_CONTENDED_ENTERED - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong MONITOR_WAIT_BIT = (((jlong)1) << (JVMTI_EVENT_MONITOR_WAIT - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong MONITOR_WAITED_BIT = (((jlong)1) << (JVMTI_EVENT_MONITOR_WAITED - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong DYNAMIC_CODE_GENERATED_BIT = (((jlong)1) << (JVMTI_EVENT_DYNAMIC_CODE_GENERATED - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong DATA_DUMP_BIT = (((jlong)1) << (JVMTI_EVENT_DATA_DUMP_REQUEST - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong COMPILED_METHOD_LOAD_BIT = (((jlong)1) << (JVMTI_EVENT_COMPILED_METHOD_LOAD - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong COMPILED_METHOD_UNLOAD_BIT = (((jlong)1) << (JVMTI_EVENT_COMPILED_METHOD_UNLOAD - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong GARBAGE_COLLECTION_START_BIT = (((jlong)1) << (JVMTI_EVENT_GARBAGE_COLLECTION_START - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong GARBAGE_COLLECTION_FINISH_BIT = (((jlong)1) << (JVMTI_EVENT_GARBAGE_COLLECTION_FINISH - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong OBJECT_FREE_BIT = (((jlong)1) << (JVMTI_EVENT_OBJECT_FREE - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong RESOURCE_EXHAUSTED_BIT = (((jlong)1) << (JVMTI_EVENT_RESOURCE_EXHAUSTED - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong VM_OBJECT_ALLOC_BIT = (((jlong)1) << (JVMTI_EVENT_VM_OBJECT_ALLOC - TOTAL_MIN_EVENT_TYPE_VAL)); + +// bits for extension events +static const jlong CLASS_UNLOAD_BIT = (((jlong)1) << (EXT_EVENT_CLASS_UNLOAD - TOTAL_MIN_EVENT_TYPE_VAL)); + + +static const jlong MONITOR_BITS = MONITOR_CONTENDED_ENTER_BIT | MONITOR_CONTENDED_ENTERED_BIT | + MONITOR_WAIT_BIT | MONITOR_WAITED_BIT; +static const jlong EXCEPTION_BITS = EXCEPTION_THROW_BIT | EXCEPTION_CATCH_BIT; +static const jlong INTERP_EVENT_BITS = SINGLE_STEP_BIT | METHOD_ENTRY_BIT | METHOD_EXIT_BIT | + FRAME_POP_BIT | FIELD_ACCESS_BIT | FIELD_MODIFICATION_BIT; +static const jlong THREAD_FILTERED_EVENT_BITS = INTERP_EVENT_BITS | EXCEPTION_BITS | MONITOR_BITS | + BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT; +static const jlong NEED_THREAD_LIFE_EVENTS = THREAD_FILTERED_EVENT_BITS | THREAD_START_BIT; +static const jlong EARLY_EVENT_BITS = CLASS_FILE_LOAD_HOOK_BIT | + VM_START_BIT | VM_INIT_BIT | VM_DEATH_BIT | NATIVE_METHOD_BIND_BIT | + THREAD_START_BIT | THREAD_END_BIT | + DYNAMIC_CODE_GENERATED_BIT; +static const jlong GLOBAL_EVENT_BITS = ~THREAD_FILTERED_EVENT_BITS; + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEventEnabled +// + +JvmtiEventEnabled::JvmtiEventEnabled() { + clear(); +} + + +void JvmtiEventEnabled::clear() { + _enabled_bits = 0; +#ifndef PRODUCT + _init_guard = JEE_INIT_GUARD; +#endif +} + +void JvmtiEventEnabled::set_enabled(jvmtiEvent event_type, bool enabled) { + jlong bits = get_bits(); + jlong mask = bit_for(event_type); + if (enabled) { + bits |= mask; + } else { + bits &= ~mask; + } + set_bits(bits); +} + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEnvThreadEventEnable +// + +JvmtiEnvThreadEventEnable::JvmtiEnvThreadEventEnable() { + _event_user_enabled.clear(); + _event_enabled.clear(); +} + + +JvmtiEnvThreadEventEnable::~JvmtiEnvThreadEventEnable() { + _event_user_enabled.clear(); + _event_enabled.clear(); +} + + +/////////////////////////////////////////////////////////////// +// +// JvmtiThreadEventEnable +// + +JvmtiThreadEventEnable::JvmtiThreadEventEnable() { + _event_enabled.clear(); +} + + +JvmtiThreadEventEnable::~JvmtiThreadEventEnable() { + _event_enabled.clear(); +} + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEnvEventEnable +// + +JvmtiEnvEventEnable::JvmtiEnvEventEnable() { + _event_user_enabled.clear(); + _event_callback_enabled.clear(); + _event_enabled.clear(); +} + + +JvmtiEnvEventEnable::~JvmtiEnvEventEnable() { + _event_user_enabled.clear(); + _event_callback_enabled.clear(); + _event_enabled.clear(); +} + + +/////////////////////////////////////////////////////////////// +// +// VM_EnterInterpOnlyMode +// + +class VM_EnterInterpOnlyMode : public VM_Operation { +private: + JvmtiThreadState *_state; + +public: + VM_EnterInterpOnlyMode(JvmtiThreadState *state); + + bool allow_nested_vm_operations() const { return true; } + VMOp_Type type() const { return VMOp_EnterInterpOnlyMode; } + void doit(); + + // to do: this same function is in jvmtiImpl - should be in one place + bool can_be_deoptimized(vframe* vf) { + return (vf->is_compiled_frame() && vf->fr().can_be_deoptimized()); + } +}; + +VM_EnterInterpOnlyMode::VM_EnterInterpOnlyMode(JvmtiThreadState *state) + : _state(state) +{ +} + + +void VM_EnterInterpOnlyMode::doit() { + // Set up the current stack depth for later tracking + _state->invalidate_cur_stack_depth(); + + _state->enter_interp_only_mode(); + + JavaThread *thread = _state->get_thread(); + if (thread->has_last_Java_frame()) { + // If running in fullspeed mode, single stepping is implemented + // as follows: first, the interpreter does not dispatch to + // compiled code for threads that have single stepping enabled; + // second, we deoptimize all methods on the thread's stack when + // interpreted-only mode is enabled the first time for a given + // thread (nothing to do if no Java frames yet). + int num_marked = 0; + ResourceMark resMark; + RegisterMap rm(thread, false); + for (vframe* vf = thread->last_java_vframe(&rm); vf; vf = vf->sender()) { + if (can_be_deoptimized(vf)) { + ((compiledVFrame*) vf)->code()->mark_for_deoptimization(); + ++num_marked; + } + } + if (num_marked > 0) { + VM_Deoptimize op; + VMThread::execute(&op); + } + } +} + + +/////////////////////////////////////////////////////////////// +// +// VM_ChangeSingleStep +// + +class VM_ChangeSingleStep : public VM_Operation { +private: + bool _on; + +public: + VM_ChangeSingleStep(bool on); + VMOp_Type type() const { return VMOp_ChangeSingleStep; } + bool allow_nested_vm_operations() const { return true; } + void doit(); // method definition is after definition of JvmtiEventControllerPrivate because of scoping +}; + + +VM_ChangeSingleStep::VM_ChangeSingleStep(bool on) + : _on(on != 0) +{ +} + + + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEventControllerPrivate +// +// Private internal implementation methods for JvmtiEventController. +// +// These methods are thread safe either because they are called +// in early VM initialization which is single threaded, or they +// hold the JvmtiThreadState_lock. +// + +class JvmtiEventControllerPrivate : public AllStatic { + static bool _initialized; +public: + static void set_should_post_single_step(bool on); + static void enter_interp_only_mode(JvmtiThreadState *state); + static void leave_interp_only_mode(JvmtiThreadState *state); + static void recompute_enabled(); + static jlong recompute_env_enabled(JvmtiEnvBase* env); + static jlong recompute_env_thread_enabled(JvmtiEnvThreadState* ets, JvmtiThreadState* state); + static jlong recompute_thread_enabled(JvmtiThreadState *state); + static void event_init(); + + static void set_user_enabled(JvmtiEnvBase *env, JavaThread *thread, + jvmtiEvent event_type, bool enabled); + static void set_event_callbacks(JvmtiEnvBase *env, + const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks); + + static void set_extension_event_callback(JvmtiEnvBase *env, + jint extension_event_index, + jvmtiExtensionEvent callback); + + static void set_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop); + static void clear_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop); + static void clear_to_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop); + static void change_field_watch(jvmtiEvent event_type, bool added); + + static void thread_started(JavaThread *thread); + static void thread_ended(JavaThread *thread); + + static void env_initialize(JvmtiEnvBase *env); + static void env_dispose(JvmtiEnvBase *env); + + static void vm_start(); + static void vm_init(); + static void vm_death(); + + static void trace_changed(JvmtiThreadState *state, jlong now_enabled, jlong changed); + static void trace_changed(jlong now_enabled, jlong changed); +}; + +bool JvmtiEventControllerPrivate::_initialized = false; + +void JvmtiEventControllerPrivate::set_should_post_single_step(bool on) { + // we have permission to do this, VM op doesn't + JvmtiExport::set_should_post_single_step(on); +} + + +// This change must always be occur when at a safepoint. +// Being at a safepoint causes the interpreter to use the +// safepoint dispatch table which we overload to find single +// step points. Just to be sure that it has been set, we +// call notice_safepoints when turning on single stepping. +// When we leave our current safepoint, should_post_single_step +// will be checked by the interpreter, and the table kept +// or changed accordingly. +void VM_ChangeSingleStep::doit() { + JvmtiEventControllerPrivate::set_should_post_single_step(_on); + if (_on) { + Interpreter::notice_safepoints(); + } +} + + +void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state) { + EC_TRACE(("JVMTI [%s] # Entering interpreter only mode", + JvmtiTrace::safe_get_thread_name(state->get_thread()))); + + VM_EnterInterpOnlyMode op(state); + VMThread::execute(&op); +} + + +void +JvmtiEventControllerPrivate::leave_interp_only_mode(JvmtiThreadState *state) { + EC_TRACE(("JVMTI [%s] # Leaving interpreter only mode", + JvmtiTrace::safe_get_thread_name(state->get_thread()))); + state->leave_interp_only_mode(); +} + + +void +JvmtiEventControllerPrivate::trace_changed(JvmtiThreadState *state, jlong now_enabled, jlong changed) { +#ifdef JVMTI_TRACE + if (JvmtiTrace::trace_event_controller()) { + SafeResourceMark rm; + // traces standard events only + for (int ei = JVMTI_MIN_EVENT_TYPE_VAL; ei <= JVMTI_MAX_EVENT_TYPE_VAL; ++ei) { + jlong bit = JvmtiEventEnabled::bit_for((jvmtiEvent)ei); + if (changed & bit) { + // it changed, print it + tty->print_cr("JVMTI [%s] # %s event %s", + JvmtiTrace::safe_get_thread_name(state->get_thread()), + (now_enabled & bit)? "Enabling" : "Disabling", JvmtiTrace::event_name((jvmtiEvent)ei)); + } + } + } +#endif /*JVMTI_TRACE */ +} + + +void +JvmtiEventControllerPrivate::trace_changed(jlong now_enabled, jlong changed) { +#ifdef JVMTI_TRACE + if (JvmtiTrace::trace_event_controller()) { + SafeResourceMark rm; + // traces standard events only + for (int ei = JVMTI_MIN_EVENT_TYPE_VAL; ei <= JVMTI_MAX_EVENT_TYPE_VAL; ++ei) { + jlong bit = JvmtiEventEnabled::bit_for((jvmtiEvent)ei); + if (changed & bit) { + // it changed, print it + tty->print_cr("JVMTI [-] # %s event %s", + (now_enabled & bit)? "Enabling" : "Disabling", JvmtiTrace::event_name((jvmtiEvent)ei)); + } + } + } +#endif /*JVMTI_TRACE */ +} + + +// For the specified env: compute the currently truly enabled events +// set external state accordingly. +// Return value and set value must include all events. +// But outside this class, only non-thread-filtered events can be queried.. +jlong +JvmtiEventControllerPrivate::recompute_env_enabled(JvmtiEnvBase* env) { + jlong was_enabled = env->env_event_enable()->_event_enabled.get_bits(); + jlong now_enabled = + env->env_event_enable()->_event_callback_enabled.get_bits() & + env->env_event_enable()->_event_user_enabled.get_bits(); + + switch (JvmtiEnv::get_phase()) { + case JVMTI_PHASE_PRIMORDIAL: + case JVMTI_PHASE_ONLOAD: + // only these events allowed in primordial or onload phase + now_enabled &= (EARLY_EVENT_BITS & ~THREAD_FILTERED_EVENT_BITS); + break; + case JVMTI_PHASE_START: + // only these events allowed in start phase + now_enabled &= EARLY_EVENT_BITS; + break; + case JVMTI_PHASE_LIVE: + // all events allowed during live phase + break; + case JVMTI_PHASE_DEAD: + // no events allowed when dead + now_enabled = 0; + break; + default: + assert(false, "no other phases - sanity check"); + break; + } + + // will we really send these events to this env + env->env_event_enable()->_event_enabled.set_bits(now_enabled); + + trace_changed(now_enabled, (now_enabled ^ was_enabled) & ~THREAD_FILTERED_EVENT_BITS); + + return now_enabled; +} + + +// For the specified env and thread: compute the currently truly enabled events +// set external state accordingly. Only thread-filtered events are included. +jlong +JvmtiEventControllerPrivate::recompute_env_thread_enabled(JvmtiEnvThreadState* ets, JvmtiThreadState* state) { + JvmtiEnv *env = ets->get_env(); + + jlong was_enabled = ets->event_enable()->_event_enabled.get_bits(); + jlong now_enabled = THREAD_FILTERED_EVENT_BITS & + env->env_event_enable()->_event_callback_enabled.get_bits() & + (env->env_event_enable()->_event_user_enabled.get_bits() | + ets->event_enable()->_event_user_enabled.get_bits()); + + // for frame pops and field watchs, computed enabled state + // is only true if an event has been requested + if (!ets->has_frame_pops()) { + now_enabled &= ~FRAME_POP_BIT; + } + if (*((int *)JvmtiExport::get_field_access_count_addr()) == 0) { + now_enabled &= ~FIELD_ACCESS_BIT; + } + if (*((int *)JvmtiExport::get_field_modification_count_addr()) == 0) { + now_enabled &= ~FIELD_MODIFICATION_BIT; + } + + switch (JvmtiEnv::get_phase()) { + case JVMTI_PHASE_DEAD: + // no events allowed when dead + now_enabled = 0; + break; + } + + // if anything changed do update + if (now_enabled != was_enabled) { + + // will we really send these events to this thread x env + ets->event_enable()->_event_enabled.set_bits(now_enabled); + + // If the enabled status of the single step or breakpoint events changed, + // the location status may need to change as well. + jlong changed = now_enabled ^ was_enabled; + if (changed & SINGLE_STEP_BIT) { + ets->reset_current_location(JVMTI_EVENT_SINGLE_STEP, (now_enabled & SINGLE_STEP_BIT) != 0); + } + if (changed & BREAKPOINT_BIT) { + ets->reset_current_location(JVMTI_EVENT_BREAKPOINT, (now_enabled & BREAKPOINT_BIT) != 0); + } + trace_changed(state, now_enabled, changed); + } + return now_enabled; +} + + +// For the specified thread: compute the currently truly enabled events +// set external state accordingly. Only thread-filtered events are included. +jlong +JvmtiEventControllerPrivate::recompute_thread_enabled(JvmtiThreadState *state) { + jlong was_any_env_enabled = state->thread_event_enable()->_event_enabled.get_bits(); + jlong any_env_enabled = 0; + + { + // This iteration will include JvmtiEnvThreadStates whoses environments + // have been disposed. These JvmtiEnvThreadStates must not be filtered + // as recompute must be called on them to disable their events, + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + any_env_enabled |= recompute_env_thread_enabled(ets, state); + } + } + + if (any_env_enabled != was_any_env_enabled) { + // mark if event is truly enabled on this thread in any environment + state->thread_event_enable()->_event_enabled.set_bits(any_env_enabled); + + // compute interp_only mode + bool should_be_interp = (any_env_enabled & INTERP_EVENT_BITS) != 0; + bool is_now_interp = state->is_interp_only_mode(); + + if (should_be_interp != is_now_interp) { + if (should_be_interp) { + enter_interp_only_mode(state); + } else { + leave_interp_only_mode(state); + } + } + } + return any_env_enabled; +} + + +// Compute truly enabled events - meaning if the event can and could be +// sent. An event is truly enabled if it is user enabled on the thread +// or globally user enabled, but only if there is a callback or event hook +// for it and, for field watch and frame pop, one has been set. +// Compute if truly enabled, per thread, per environment, per combination +// (thread x environment), and overall. These merges are true if any is true. +// True per thread if some environment has callback set and the event is globally +// enabled or enabled for this thread. +// True per environment if the callback is set and the event is globally +// enabled in this environment or enabled for any thread in this environment. +// True per combination if the environment has the callback set and the +// event is globally enabled in this environment or the event is enabled +// for this thread and environment. +// +// All states transitions dependent on these transitions are also handled here. +void +JvmtiEventControllerPrivate::recompute_enabled() { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + + // event enabled for any thread in any environment + jlong was_any_env_thread_enabled = JvmtiEventController::_universal_global_event_enabled.get_bits(); + jlong any_env_thread_enabled = 0; + + EC_TRACE(("JVMTI [-] # recompute enabled - before %llx", was_any_env_thread_enabled)); + + // compute non-thread-filters events. + // This must be done separately from thread-filtered events, since some + // events can occur before any threads exist. + JvmtiEnvIterator it; + for (JvmtiEnvBase* env = it.first(); env != NULL; env = it.next(env)) { + any_env_thread_enabled |= recompute_env_enabled(env); + } + + // We need to create any missing jvmti_thread_state if there are globally set thread + // filtered events and there weren't last time + if ( (any_env_thread_enabled & THREAD_FILTERED_EVENT_BITS) != 0 && + (was_any_env_thread_enabled & THREAD_FILTERED_EVENT_BITS) == 0) { + assert(JvmtiEnv::is_vm_live() || (JvmtiEnv::get_phase()==JVMTI_PHASE_START), + "thread filtered events should not be enabled when VM not in start or live phase"); + { + MutexLocker mu(Threads_lock); //hold the Threads_lock for the iteration + for (JavaThread *tp = Threads::first(); tp != NULL; tp = tp->next()) { + JvmtiThreadState::state_for_while_locked(tp); // create the thread state if missing + } + }// release Threads_lock + } + + // compute and set thread-filtered events + for (JvmtiThreadState *state = JvmtiThreadState::first(); state != NULL; state = state->next()) { + any_env_thread_enabled |= recompute_thread_enabled(state); + } + + // set universal state (across all envs and threads) + jlong delta = any_env_thread_enabled ^ was_any_env_thread_enabled; + if (delta != 0) { + JvmtiExport::set_should_post_field_access((any_env_thread_enabled & FIELD_ACCESS_BIT) != 0); + JvmtiExport::set_should_post_field_modification((any_env_thread_enabled & FIELD_MODIFICATION_BIT) != 0); + JvmtiExport::set_should_post_class_load((any_env_thread_enabled & CLASS_LOAD_BIT) != 0); + JvmtiExport::set_should_post_class_file_load_hook((any_env_thread_enabled & CLASS_FILE_LOAD_HOOK_BIT) != 0); + JvmtiExport::set_should_post_native_method_bind((any_env_thread_enabled & NATIVE_METHOD_BIND_BIT) != 0); + JvmtiExport::set_should_post_dynamic_code_generated((any_env_thread_enabled & DYNAMIC_CODE_GENERATED_BIT) != 0); + JvmtiExport::set_should_post_data_dump((any_env_thread_enabled & DATA_DUMP_BIT) != 0); + JvmtiExport::set_should_post_class_prepare((any_env_thread_enabled & CLASS_PREPARE_BIT) != 0); + JvmtiExport::set_should_post_class_unload((any_env_thread_enabled & CLASS_UNLOAD_BIT) != 0); + JvmtiExport::set_should_post_monitor_contended_enter((any_env_thread_enabled & MONITOR_CONTENDED_ENTER_BIT) != 0); + JvmtiExport::set_should_post_monitor_contended_entered((any_env_thread_enabled & MONITOR_CONTENDED_ENTERED_BIT) != 0); + JvmtiExport::set_should_post_monitor_wait((any_env_thread_enabled & MONITOR_WAIT_BIT) != 0); + JvmtiExport::set_should_post_monitor_waited((any_env_thread_enabled & MONITOR_WAITED_BIT) != 0); + JvmtiExport::set_should_post_garbage_collection_start((any_env_thread_enabled & GARBAGE_COLLECTION_START_BIT) != 0); + JvmtiExport::set_should_post_garbage_collection_finish((any_env_thread_enabled & GARBAGE_COLLECTION_FINISH_BIT) != 0); + JvmtiExport::set_should_post_object_free((any_env_thread_enabled & OBJECT_FREE_BIT) != 0); + JvmtiExport::set_should_post_resource_exhausted((any_env_thread_enabled & RESOURCE_EXHAUSTED_BIT) != 0); + JvmtiExport::set_should_post_compiled_method_load((any_env_thread_enabled & COMPILED_METHOD_LOAD_BIT) != 0); + JvmtiExport::set_should_post_compiled_method_unload((any_env_thread_enabled & COMPILED_METHOD_UNLOAD_BIT) != 0); + JvmtiExport::set_should_post_vm_object_alloc((any_env_thread_enabled & VM_OBJECT_ALLOC_BIT) != 0); + + // need this if we want thread events or we need them to init data + JvmtiExport::set_should_post_thread_life((any_env_thread_enabled & NEED_THREAD_LIFE_EVENTS) != 0); + + // If single stepping is turned on or off, execute the VM op to change it. + if (delta & SINGLE_STEP_BIT) { + switch (JvmtiEnv::get_phase()) { + case JVMTI_PHASE_DEAD: + // If the VM is dying we can't execute VM ops + break; + case JVMTI_PHASE_LIVE: { + VM_ChangeSingleStep op((any_env_thread_enabled & SINGLE_STEP_BIT) != 0); + VMThread::execute(&op); + break; + } + default: + assert(false, "should never come here before live phase"); + break; + } + } + + // set global truly enabled, that is, any thread in any environment + JvmtiEventController::_universal_global_event_enabled.set_bits(any_env_thread_enabled); + } + + EC_TRACE(("JVMTI [-] # recompute enabled - after %llx", any_env_thread_enabled)); +} + + +void +JvmtiEventControllerPrivate::thread_started(JavaThread *thread) { + assert(thread->is_Java_thread(), "Must be JavaThread"); + assert(thread == Thread::current(), "must be current thread"); + assert(JvmtiEnvBase::environments_might_exist(), "to enter event controller, JVM TI environments must exist"); + + EC_TRACE(("JVMTI [%s] # thread started", JvmtiTrace::safe_get_thread_name(thread))); + + // if we have any thread filtered events globally enabled, create/update the thread state + if ((JvmtiEventController::_universal_global_event_enabled.get_bits() & THREAD_FILTERED_EVENT_BITS) != 0) { + MutexLocker mu(JvmtiThreadState_lock); + // create the thread state if missing + JvmtiThreadState *state = JvmtiThreadState::state_for_while_locked(thread); + if (state != NULL) { // skip threads with no JVMTI thread state + recompute_thread_enabled(state); + } + } +} + + +void +JvmtiEventControllerPrivate::thread_ended(JavaThread *thread) { + // Removes the JvmtiThreadState associated with the specified thread. + // May be called after all environments have been disposed. + + EC_TRACE(("JVMTI [%s] # thread ended", JvmtiTrace::safe_get_thread_name(thread))); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state != NULL) { + MutexLocker mu(JvmtiThreadState_lock); + delete state; + } +} + +void JvmtiEventControllerPrivate::set_event_callbacks(JvmtiEnvBase *env, + const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks) { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + EC_TRACE(("JVMTI [*] # set event callbacks")); + + env->set_event_callbacks(callbacks, size_of_callbacks); + jlong enabled_bits = 0; + for (int ei = JVMTI_MIN_EVENT_TYPE_VAL; ei <= JVMTI_MAX_EVENT_TYPE_VAL; ++ei) { + jvmtiEvent evt_t = (jvmtiEvent)ei; + if (env->has_callback(evt_t)) { + enabled_bits |= JvmtiEventEnabled::bit_for(evt_t); + } + } + env->env_event_enable()->_event_callback_enabled.set_bits(enabled_bits); + recompute_enabled(); +} + +void +JvmtiEventControllerPrivate::set_extension_event_callback(JvmtiEnvBase *env, + jint extension_event_index, + jvmtiExtensionEvent callback) +{ + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + EC_TRACE(("JVMTI [*] # set extension event callback")); + + // extension events are allocated below JVMTI_MIN_EVENT_TYPE_VAL + assert(extension_event_index >= (jint)EXT_MIN_EVENT_TYPE_VAL && + extension_event_index <= (jint)EXT_MAX_EVENT_TYPE_VAL, "sanity check"); + + + // As the bits for both standard (jvmtiEvent) and extension + // (jvmtiExtEvents) are stored in the same word we cast here to + // jvmtiEvent to set/clear the bit for this extension event. + jvmtiEvent event_type = (jvmtiEvent)extension_event_index; + + // Prevent a possible race condition where events are re-enabled by a call to + // set event callbacks, where the DisposeEnvironment occurs after the boiler-plate + // environment check and before the lock is acquired. + // We can safely do the is_valid check now, as JvmtiThreadState_lock is held. + bool enabling = (callback != NULL) && (env->is_valid()); + env->env_event_enable()->set_user_enabled(event_type, enabling); + + // update the callback + jvmtiExtEventCallbacks* ext_callbacks = env->ext_callbacks(); + switch (extension_event_index) { + case EXT_EVENT_CLASS_UNLOAD : + ext_callbacks->ClassUnload = callback; + break; + default: + ShouldNotReachHere(); + } + + // update the callback enable/disable bit + jlong enabled_bits = env->env_event_enable()->_event_callback_enabled.get_bits(); + jlong bit_for = JvmtiEventEnabled::bit_for(event_type); + if (enabling) { + enabled_bits |= bit_for; + } else { + enabled_bits &= ~bit_for; + } + env->env_event_enable()->_event_callback_enabled.set_bits(enabled_bits); + + recompute_enabled(); +} + + +void +JvmtiEventControllerPrivate::env_initialize(JvmtiEnvBase *env) { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + EC_TRACE(("JVMTI [*] # env initialize")); + + if (JvmtiEnvBase::is_vm_live()) { + // if we didn't initialize event info already (this is a late + // launched environment), do it now. + event_init(); + } + + env->initialize(); + + // add the JvmtiEnvThreadState to each JvmtiThreadState + for (JvmtiThreadState *state = JvmtiThreadState::first(); state != NULL; state = state->next()) { + state->add_env(env); + assert((JvmtiEnv*)(state->env_thread_state(env)->get_env()) == env, "sanity check"); + } + JvmtiEventControllerPrivate::recompute_enabled(); +} + + +void +JvmtiEventControllerPrivate::env_dispose(JvmtiEnvBase *env) { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + EC_TRACE(("JVMTI [*] # env dispose")); + + // Before the environment is marked disposed, disable all events on this + // environment (by zapping the callbacks). As a result, the disposed + // environment will not call event handlers. + set_event_callbacks(env, NULL, 0); + for (jint extension_event_index = EXT_MIN_EVENT_TYPE_VAL; + extension_event_index <= EXT_MAX_EVENT_TYPE_VAL; + ++extension_event_index) { + set_extension_event_callback(env, extension_event_index, NULL); + } + + // Let the environment finish disposing itself. + env->env_dispose(); +} + + +void +JvmtiEventControllerPrivate::set_user_enabled(JvmtiEnvBase *env, JavaThread *thread, + jvmtiEvent event_type, bool enabled) { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + + EC_TRACE(("JVMTI [%s] # user %s event %s", + thread==NULL? "ALL": JvmtiTrace::safe_get_thread_name(thread), + enabled? "enabled" : "disabled", JvmtiTrace::event_name(event_type))); + + if (thread == NULL) { + env->env_event_enable()->set_user_enabled(event_type, enabled); + } else { + // create the thread state (if it didn't exist before) + JvmtiThreadState *state = JvmtiThreadState::state_for_while_locked(thread); + if (state != NULL) { + state->env_thread_state(env)->event_enable()->set_user_enabled(event_type, enabled); + } + } + recompute_enabled(); +} + + +void +JvmtiEventControllerPrivate::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { + EC_TRACE(("JVMTI [%s] # set frame pop - frame=%d", + JvmtiTrace::safe_get_thread_name(ets->get_thread()), + fpop.frame_number() )); + + ets->get_frame_pops()->set(fpop); + recompute_thread_enabled(ets->get_thread()->jvmti_thread_state()); +} + + +void +JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { + EC_TRACE(("JVMTI [%s] # clear frame pop - frame=%d", + JvmtiTrace::safe_get_thread_name(ets->get_thread()), + fpop.frame_number() )); + + ets->get_frame_pops()->clear(fpop); + recompute_thread_enabled(ets->get_thread()->jvmti_thread_state()); +} + + +void +JvmtiEventControllerPrivate::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { + int cleared_cnt = ets->get_frame_pops()->clear_to(fpop); + + EC_TRACE(("JVMTI [%s] # clear to frame pop - frame=%d, count=%d", + JvmtiTrace::safe_get_thread_name(ets->get_thread()), + fpop.frame_number(), + cleared_cnt )); + + if (cleared_cnt > 0) { + recompute_thread_enabled(ets->get_thread()->jvmti_thread_state()); + } +} + +void +JvmtiEventControllerPrivate::change_field_watch(jvmtiEvent event_type, bool added) { + int *count_addr; + + switch (event_type) { + case JVMTI_EVENT_FIELD_MODIFICATION: + count_addr = (int *)JvmtiExport::get_field_modification_count_addr(); + break; + case JVMTI_EVENT_FIELD_ACCESS: + count_addr = (int *)JvmtiExport::get_field_access_count_addr(); + break; + default: + assert(false, "incorrect event"); + return; + } + + EC_TRACE(("JVMTI [-] # change field watch - %s %s count=%d", + event_type==JVMTI_EVENT_FIELD_MODIFICATION? "modification" : "access", + added? "add" : "remove", + *count_addr)); + + if (added) { + (*count_addr)++; + if (*count_addr == 1) { + recompute_enabled(); + } + } else { + if (*count_addr > 0) { + (*count_addr)--; + if (*count_addr == 0) { + recompute_enabled(); + } + } else { + assert(false, "field watch out of phase"); + } + } +} + +void +JvmtiEventControllerPrivate::event_init() { + assert(JvmtiThreadState_lock->is_locked(), "sanity check"); + + if (_initialized) { + return; + } + + EC_TRACE(("JVMTI [-] # VM live")); + +#ifdef ASSERT + // check that our idea and the spec's idea of threaded events match + for (int ei = JVMTI_MIN_EVENT_TYPE_VAL; ei <= JVMTI_MAX_EVENT_TYPE_VAL; ++ei) { + jlong bit = JvmtiEventEnabled::bit_for((jvmtiEvent)ei); + assert(((THREAD_FILTERED_EVENT_BITS & bit) != 0) == JvmtiUtil::event_threaded(ei), + "thread filtered event list does not match"); + } +#endif + + _initialized = true; +} + +void +JvmtiEventControllerPrivate::vm_start() { + // some events are now able to be enabled (phase has changed) + JvmtiEventControllerPrivate::recompute_enabled(); +} + + +void +JvmtiEventControllerPrivate::vm_init() { + event_init(); + + // all the events are now able to be enabled (phase has changed) + JvmtiEventControllerPrivate::recompute_enabled(); +} + + +void +JvmtiEventControllerPrivate::vm_death() { + // events are disabled (phase has changed) + JvmtiEventControllerPrivate::recompute_enabled(); +} + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEventController +// + +JvmtiEventEnabled JvmtiEventController::_universal_global_event_enabled; + +bool +JvmtiEventController::is_global_event(jvmtiEvent event_type) { + assert(is_valid_event_type(event_type), "invalid event type"); + jlong bit_for = ((jlong)1) << (event_type - TOTAL_MIN_EVENT_TYPE_VAL); + return((bit_for & GLOBAL_EVENT_BITS)!=0); +} + +void +JvmtiEventController::set_user_enabled(JvmtiEnvBase *env, JavaThread *thread, jvmtiEvent event_type, bool enabled) { + if (Threads::number_of_threads() == 0) { + // during early VM start-up locks don't exist, but we are safely single threaded, + // call the functionality without holding the JvmtiThreadState_lock. + JvmtiEventControllerPrivate::set_user_enabled(env, thread, event_type, enabled); + } else { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::set_user_enabled(env, thread, event_type, enabled); + } +} + + +void +JvmtiEventController::set_event_callbacks(JvmtiEnvBase *env, + const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks) { + if (Threads::number_of_threads() == 0) { + // during early VM start-up locks don't exist, but we are safely single threaded, + // call the functionality without holding the JvmtiThreadState_lock. + JvmtiEventControllerPrivate::set_event_callbacks(env, callbacks, size_of_callbacks); + } else { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::set_event_callbacks(env, callbacks, size_of_callbacks); + } +} + +void +JvmtiEventController::set_extension_event_callback(JvmtiEnvBase *env, + jint extension_event_index, + jvmtiExtensionEvent callback) { + if (Threads::number_of_threads() == 0) { + JvmtiEventControllerPrivate::set_extension_event_callback(env, extension_event_index, callback); + } else { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::set_extension_event_callback(env, extension_event_index, callback); + } +} + + + + +void +JvmtiEventController::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::set_frame_pop(ets, fpop); +} + + +void +JvmtiEventController::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::clear_frame_pop(ets, fpop); +} + + +void +JvmtiEventController::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::clear_to_frame_pop(ets, fpop); +} + +void +JvmtiEventController::change_field_watch(jvmtiEvent event_type, bool added) { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::change_field_watch(event_type, added); +} + +void +JvmtiEventController::thread_started(JavaThread *thread) { + // operates only on the current thread + // JvmtiThreadState_lock grabbed only if needed. + JvmtiEventControllerPrivate::thread_started(thread); +} + +void +JvmtiEventController::thread_ended(JavaThread *thread) { + // operates only on the current thread + // JvmtiThreadState_lock grabbed only if needed. + JvmtiEventControllerPrivate::thread_ended(thread); +} + +void +JvmtiEventController::env_initialize(JvmtiEnvBase *env) { + if (Threads::number_of_threads() == 0) { + // during early VM start-up locks don't exist, but we are safely single threaded, + // call the functionality without holding the JvmtiThreadState_lock. + JvmtiEventControllerPrivate::env_initialize(env); + } else { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::env_initialize(env); + } +} + +void +JvmtiEventController::env_dispose(JvmtiEnvBase *env) { + if (Threads::number_of_threads() == 0) { + // during early VM start-up locks don't exist, but we are safely single threaded, + // call the functionality without holding the JvmtiThreadState_lock. + JvmtiEventControllerPrivate::env_dispose(env); + } else { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::env_dispose(env); + } +} + + +void +JvmtiEventController::vm_start() { + if (JvmtiEnvBase::environments_might_exist()) { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::vm_start(); + } +} + +void +JvmtiEventController::vm_init() { + if (JvmtiEnvBase::environments_might_exist()) { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::vm_init(); + } +} + +void +JvmtiEventController::vm_death() { + if (JvmtiEnvBase::environments_might_exist()) { + MutexLocker mu(JvmtiThreadState_lock); + JvmtiEventControllerPrivate::vm_death(); + } +} diff --git a/hotspot/src/share/vm/prims/jvmtiEventController.hpp b/hotspot/src/share/vm/prims/jvmtiEventController.hpp new file mode 100644 index 00000000000..dbde512b066 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEventController.hpp @@ -0,0 +1,240 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _JAVA_JVMTI_EVENT_CONTROLLER_H_ +#define _JAVA_JVMTI_EVENT_CONTROLLER_H_ + +// forward declaration +class JvmtiEventControllerPrivate; +class JvmtiEventController; +class JvmtiEnvThreadState; +class JvmtiFramePop; +class JvmtiEnvBase; + + +// Extension event support +// +// jvmtiExtEvent is the extensions equivalent of jvmtiEvent +// jvmtiExtCallbacks is the extensions equivalent of jvmtiEventCallbacks + +// Extension events start JVMTI_MIN_EVENT_TYPE_VAL-1 and work towards 0. +typedef enum { + EXT_EVENT_CLASS_UNLOAD = JVMTI_MIN_EVENT_TYPE_VAL-1, + EXT_MIN_EVENT_TYPE_VAL = EXT_EVENT_CLASS_UNLOAD, + EXT_MAX_EVENT_TYPE_VAL = EXT_EVENT_CLASS_UNLOAD +} jvmtiExtEvent; + +typedef struct { + jvmtiExtensionEvent ClassUnload; +} jvmtiExtEventCallbacks; + + +// The complete range of events is EXT_MIN_EVENT_TYPE_VAL to +// JVMTI_MAX_EVENT_TYPE_VAL (inclusive and contiguous). +const int TOTAL_MIN_EVENT_TYPE_VAL = EXT_MIN_EVENT_TYPE_VAL; +const int TOTAL_MAX_EVENT_TYPE_VAL = JVMTI_MAX_EVENT_TYPE_VAL; + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEventEnabled +// +// Utility class +// +// A boolean array indexed by event_type, used as an internal +// data structure to track what JVMTI event types are enabled. +// Used for user set enabling and disabling (globally and on a +// per thread basis), and for computed merges across environments, +// threads and the VM as a whole. +// +// for inlines see jvmtiEventController_inline.hpp +// + +class JvmtiEventEnabled VALUE_OBJ_CLASS_SPEC { +private: + friend class JvmtiEventControllerPrivate; + jlong _enabled_bits; +#ifndef PRODUCT + enum { + JEE_INIT_GUARD = 0xEAD0 + } _init_guard; +#endif + static jlong bit_for(jvmtiEvent event_type); + jlong get_bits(); + void set_bits(jlong bits); +public: + JvmtiEventEnabled(); + void clear(); + bool is_enabled(jvmtiEvent event_type); + void set_enabled(jvmtiEvent event_type, bool enabled); +}; + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEnvThreadEventEnable +// +// JvmtiEventController data specific to a particular environment and thread. +// +// for inlines see jvmtiEventController_inline.hpp +// + +class JvmtiEnvThreadEventEnable VALUE_OBJ_CLASS_SPEC { +private: + friend class JvmtiEventControllerPrivate; + JvmtiEventEnabled _event_user_enabled; + JvmtiEventEnabled _event_enabled; + +public: + JvmtiEnvThreadEventEnable(); + ~JvmtiEnvThreadEventEnable(); + bool is_enabled(jvmtiEvent event_type); + void set_user_enabled(jvmtiEvent event_type, bool enabled); +}; + + +/////////////////////////////////////////////////////////////// +// +// JvmtiThreadEventEnable +// +// JvmtiEventController data specific to a particular thread. +// +// for inlines see jvmtiEventController_inline.hpp +// + +class JvmtiThreadEventEnable VALUE_OBJ_CLASS_SPEC { +private: + friend class JvmtiEventControllerPrivate; + JvmtiEventEnabled _event_enabled; + +public: + JvmtiThreadEventEnable(); + ~JvmtiThreadEventEnable(); + bool is_enabled(jvmtiEvent event_type); +}; + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEnvEventEnable +// +// JvmtiEventController data specific to a particular environment. +// +// for inlines see jvmtiEventController_inline.hpp +// + +class JvmtiEnvEventEnable VALUE_OBJ_CLASS_SPEC { +private: + friend class JvmtiEventControllerPrivate; + + // user set global event enablement indexed by jvmtiEvent + JvmtiEventEnabled _event_user_enabled; + + // this flag indicates the presence (true) or absence (false) of event callbacks + // it is indexed by jvmtiEvent + JvmtiEventEnabled _event_callback_enabled; + + // indexed by jvmtiEvent true if enabled globally or on any thread. + // True only if there is a callback for it. + JvmtiEventEnabled _event_enabled; + +public: + JvmtiEnvEventEnable(); + ~JvmtiEnvEventEnable(); + bool is_enabled(jvmtiEvent event_type); + void set_user_enabled(jvmtiEvent event_type, bool enabled); +}; + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEventController +// +// The class is the access point for all actions that change +// which events are active, this include: +// enabling and disabling events +// changing the callbacks/eventhook (they may be null) +// setting and clearing field watchpoints +// setting frame pops +// encountering frame pops +// +// for inlines see jvmtiEventController_inline.hpp +// + +class JvmtiEventController : AllStatic { +private: + friend class JvmtiEventControllerPrivate; + + // for all environments, global array indexed by jvmtiEvent + static JvmtiEventEnabled _universal_global_event_enabled; + +public: + static bool is_enabled(jvmtiEvent event_type); + + // events that can ONLY be enabled/disabled globally (can't toggle on individual threads). + static bool is_global_event(jvmtiEvent event_type); + + // is the event_type valid? + // to do: check against valid event array + static bool is_valid_event_type(jvmtiEvent event_type) { + return ((int)event_type >= TOTAL_MIN_EVENT_TYPE_VAL) + && ((int)event_type <= TOTAL_MAX_EVENT_TYPE_VAL); + } + + // Use (thread == NULL) to enable/disable an event globally. + // Use (thread != NULL) to enable/disable an event for a particular thread. + // thread is ignored for events that can only be specified globally + static void set_user_enabled(JvmtiEnvBase *env, JavaThread *thread, + jvmtiEvent event_type, bool enabled); + + // Setting callbacks changes computed enablement and must be done + // at a safepoint otherwise a NULL callback could be attempted + static void set_event_callbacks(JvmtiEnvBase *env, + const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks); + + // Sets the callback function for a single extension event and enables + // (or disables it). + static void set_extension_event_callback(JvmtiEnvBase* env, + jint extension_event_index, + jvmtiExtensionEvent callback); + + static void set_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop); + static void clear_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop); + static void clear_to_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop); + + static void change_field_watch(jvmtiEvent event_type, bool added); + + static void thread_started(JavaThread *thread); + static void thread_ended(JavaThread *thread); + + static void env_initialize(JvmtiEnvBase *env); + static void env_dispose(JvmtiEnvBase *env); + + static void vm_start(); + static void vm_init(); + static void vm_death(); +}; + +#endif /* _JAVA_JVMTI_EVENT_CONTROLLER_H_ */ diff --git a/hotspot/src/share/vm/prims/jvmtiEventController.inline.hpp b/hotspot/src/share/vm/prims/jvmtiEventController.inline.hpp new file mode 100644 index 00000000000..f051a0990ca --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiEventController.inline.hpp @@ -0,0 +1,101 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// these inline functions are in a separate file to break include cycles + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEventEnabled +// + +inline jlong JvmtiEventEnabled::bit_for(jvmtiEvent event_type) { + assert(JvmtiEventController::is_valid_event_type(event_type), "invalid event type"); + return ((jlong)1) << (event_type - TOTAL_MIN_EVENT_TYPE_VAL); +} + +inline jlong JvmtiEventEnabled::get_bits() { + assert(_init_guard == JEE_INIT_GUARD, "enable bits uninitialized or corrupted"); + return _enabled_bits; +} + +inline void JvmtiEventEnabled::set_bits(jlong bits) { + assert(_init_guard == JEE_INIT_GUARD, "enable bits uninitialized or corrupted on set"); + _enabled_bits = bits; +} + +inline bool JvmtiEventEnabled::is_enabled(jvmtiEvent event_type) { + return (bit_for(event_type) & get_bits()) != 0; +} + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEnvThreadEventEnable +// + +inline bool JvmtiEnvThreadEventEnable::is_enabled(jvmtiEvent event_type) { + assert(JvmtiUtil::event_threaded(event_type), "Only thread filtered events should be tested here"); + return _event_enabled.is_enabled(event_type); +} + +inline void JvmtiEnvThreadEventEnable::set_user_enabled(jvmtiEvent event_type, bool enabled) { + _event_user_enabled.set_enabled(event_type, enabled); +} + + +/////////////////////////////////////////////////////////////// +// +// JvmtiThreadEventEnable +// + +inline bool JvmtiThreadEventEnable::is_enabled(jvmtiEvent event_type) { + assert(JvmtiUtil::event_threaded(event_type), "Only thread filtered events should be tested here"); + return _event_enabled.is_enabled(event_type); +} + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEnvEventEnable +// + +inline bool JvmtiEnvEventEnable::is_enabled(jvmtiEvent event_type) { + assert(!JvmtiUtil::event_threaded(event_type), "Only non thread filtered events should be tested here"); + return _event_enabled.is_enabled(event_type); +} + +inline void JvmtiEnvEventEnable::set_user_enabled(jvmtiEvent event_type, bool enabled) { + _event_user_enabled.set_enabled(event_type, enabled); +} + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEventController +// + +inline bool JvmtiEventController::is_enabled(jvmtiEvent event_type) { + return _universal_global_event_enabled.is_enabled(event_type); +} diff --git a/hotspot/src/share/vm/prims/jvmtiExport.cpp b/hotspot/src/share/vm/prims/jvmtiExport.cpp new file mode 100644 index 00000000000..7bb33674d87 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiExport.cpp @@ -0,0 +1,2494 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiExport.cpp.incl" + +#ifdef JVMTI_TRACE +#define EVT_TRACE(evt,out) if ((JvmtiTrace::event_trace_flags(evt) & JvmtiTrace::SHOW_EVENT_SENT) != 0) { SafeResourceMark rm; tty->print_cr out; } +#define EVT_TRIG_TRACE(evt,out) if ((JvmtiTrace::event_trace_flags(evt) & JvmtiTrace::SHOW_EVENT_TRIGGER) != 0) { SafeResourceMark rm; tty->print_cr out; } +#else +#define EVT_TRIG_TRACE(evt,out) +#define EVT_TRACE(evt,out) +#endif + +/////////////////////////////////////////////////////////////// +// +// JvmtiEventTransition +// +// TO DO -- +// more handle purging + +// Use this for JavaThreads and state is _thread_in_vm. +class JvmtiJavaThreadEventTransition : StackObj { +private: + ResourceMark _rm; + ThreadToNativeFromVM _transition; + HandleMark _hm; + +public: + JvmtiJavaThreadEventTransition(JavaThread *thread) : + _rm(), + _transition(thread), + _hm(thread) {}; +}; + +// For JavaThreads which are not in _thread_in_vm state +// and other system threads use this. +class JvmtiThreadEventTransition : StackObj { +private: + ResourceMark _rm; + HandleMark _hm; + JavaThreadState _saved_state; + JavaThread *_jthread; + +public: + JvmtiThreadEventTransition(Thread *thread) : _rm(), _hm() { + if (thread->is_Java_thread()) { + _jthread = (JavaThread *)thread; + _saved_state = _jthread->thread_state(); + if (_saved_state == _thread_in_Java) { + ThreadStateTransition::transition_from_java(_jthread, _thread_in_native); + } else { + ThreadStateTransition::transition(_jthread, _saved_state, _thread_in_native); + } + } else { + _jthread = NULL; + } + } + + ~JvmtiThreadEventTransition() { + if (_jthread != NULL) + ThreadStateTransition::transition_from_native(_jthread, _saved_state); + } +}; + + +/////////////////////////////////////////////////////////////// +// +// JvmtiEventMark +// + +class JvmtiEventMark : public StackObj { +private: + JavaThread *_thread; + JNIEnv* _jni_env; + bool _exception_detected; + bool _exception_caught; +#if 0 + JNIHandleBlock* _hblock; +#endif + +public: + JvmtiEventMark(JavaThread *thread) : _thread(thread), + _jni_env(thread->jni_environment()) { +#if 0 + _hblock = thread->active_handles(); + _hblock->clear_thoroughly(); // so we can be safe +#else + // we want to use the code above - but that needs the JNIHandle changes - later... + // for now, steal JNI push local frame code + JvmtiThreadState *state = thread->jvmti_thread_state(); + // we are before an event. + // Save current jvmti thread exception state. + if (state != NULL) { + _exception_detected = state->is_exception_detected(); + _exception_caught = state->is_exception_caught(); + } else { + _exception_detected = false; + _exception_caught = false; + } + + JNIHandleBlock* old_handles = thread->active_handles(); + JNIHandleBlock* new_handles = JNIHandleBlock::allocate_block(thread); + assert(new_handles != NULL, "should not be NULL"); + new_handles->set_pop_frame_link(old_handles); + thread->set_active_handles(new_handles); +#endif + assert(thread == JavaThread::current(), "thread must be current!"); + thread->frame_anchor()->make_walkable(thread); + }; + + ~JvmtiEventMark() { +#if 0 + _hblock->clear(); // for consistency with future correct behavior +#else + // we want to use the code above - but that needs the JNIHandle changes - later... + // for now, steal JNI pop local frame code + JNIHandleBlock* old_handles = _thread->active_handles(); + JNIHandleBlock* new_handles = old_handles->pop_frame_link(); + assert(new_handles != NULL, "should not be NULL"); + _thread->set_active_handles(new_handles); + // Note that we set the pop_frame_link to NULL explicitly, otherwise + // the release_block call will release the blocks. + old_handles->set_pop_frame_link(NULL); + JNIHandleBlock::release_block(old_handles, _thread); // may block +#endif + + JvmtiThreadState* state = _thread->jvmti_thread_state(); + // we are continuing after an event. + if (state != NULL) { + // Restore the jvmti thread exception state. + if (_exception_detected) { + state->set_exception_detected(); + } + if (_exception_caught) { + state->set_exception_caught(); + } + } + } + +#if 0 + jobject to_jobject(oop obj) { return obj == NULL? NULL : _hblock->allocate_handle_fast(obj); } +#else + // we want to use the code above - but that needs the JNIHandle changes - later... + // for now, use regular make_local + jobject to_jobject(oop obj) { return JNIHandles::make_local(_thread,obj); } +#endif + + jclass to_jclass(klassOop klass) { return (klass == NULL ? NULL : (jclass)to_jobject(Klass::cast(klass)->java_mirror())); } + + jmethodID to_jmethodID(methodHandle method) { return method->jmethod_id(); } + + JNIEnv* jni_env() { return _jni_env; } +}; + +class JvmtiThreadEventMark : public JvmtiEventMark { +private: + jthread _jt; + +public: + JvmtiThreadEventMark(JavaThread *thread) : + JvmtiEventMark(thread) { + _jt = (jthread)(to_jobject(thread->threadObj())); + }; + jthread jni_thread() { return _jt; } +}; + +class JvmtiClassEventMark : public JvmtiThreadEventMark { +private: + jclass _jc; + +public: + JvmtiClassEventMark(JavaThread *thread, klassOop klass) : + JvmtiThreadEventMark(thread) { + _jc = to_jclass(klass); + }; + jclass jni_class() { return _jc; } +}; + +class JvmtiMethodEventMark : public JvmtiThreadEventMark { +private: + jmethodID _mid; + +public: + JvmtiMethodEventMark(JavaThread *thread, methodHandle method) : + JvmtiThreadEventMark(thread), + _mid(to_jmethodID(method)) {}; + jmethodID jni_methodID() { return _mid; } +}; + +class JvmtiLocationEventMark : public JvmtiMethodEventMark { +private: + jlocation _loc; + +public: + JvmtiLocationEventMark(JavaThread *thread, methodHandle method, address location) : + JvmtiMethodEventMark(thread, method), + _loc(location - method->code_base()) {}; + jlocation location() { return _loc; } +}; + +class JvmtiExceptionEventMark : public JvmtiLocationEventMark { +private: + jobject _exc; + +public: + JvmtiExceptionEventMark(JavaThread *thread, methodHandle method, address location, Handle exception) : + JvmtiLocationEventMark(thread, method, location), + _exc(to_jobject(exception())) {}; + jobject exception() { return _exc; } +}; + +class JvmtiClassFileLoadEventMark : public JvmtiThreadEventMark { +private: + const char *_class_name; + jobject _jloader; + jobject _protection_domain; + jclass _class_being_redefined; + +public: + JvmtiClassFileLoadEventMark(JavaThread *thread, symbolHandle name, + Handle class_loader, Handle prot_domain, KlassHandle *class_being_redefined) : JvmtiThreadEventMark(thread) { + _class_name = name() != NULL? name->as_utf8() : NULL; + _jloader = (jobject)to_jobject(class_loader()); + _protection_domain = (jobject)to_jobject(prot_domain()); + if (class_being_redefined == NULL) { + _class_being_redefined = NULL; + } else { + _class_being_redefined = (jclass)to_jclass((*class_being_redefined)()); + } + }; + const char *class_name() { + return _class_name; + } + jobject jloader() { + return _jloader; + } + jobject protection_domain() { + return _protection_domain; + } + jclass class_being_redefined() { + return _class_being_redefined; + } +}; + +////////////////////////////////////////////////////////////////////////////// + +int JvmtiExport::_field_access_count = 0; +int JvmtiExport::_field_modification_count = 0; + +bool JvmtiExport::_can_access_local_variables = false; +bool JvmtiExport::_can_examine_or_deopt_anywhere = false; +bool JvmtiExport::_can_hotswap_or_post_breakpoint = false; +bool JvmtiExport::_can_modify_any_class = false; +bool JvmtiExport::_can_walk_any_space = false; + +bool JvmtiExport::_has_redefined_a_class = false; +bool JvmtiExport::_all_dependencies_are_recorded = false; + +// +// field access management +// + +// interpreter generator needs the address of the counter +address JvmtiExport::get_field_access_count_addr() { + // We don't grab a lock because we don't want to + // serialize field access between all threads. This means that a + // thread on another processor can see the wrong count value and + // may either miss making a needed call into post_field_access() + // or will make an unneeded call into post_field_access(). We pay + // this price to avoid slowing down the VM when we aren't watching + // field accesses. + // Other access/mutation safe by virtue of being in VM state. + return (address)(&_field_access_count); +} + +// +// field modification management +// + +// interpreter generator needs the address of the counter +address JvmtiExport::get_field_modification_count_addr() { + // We don't grab a lock because we don't + // want to serialize field modification between all threads. This + // means that a thread on another processor can see the wrong + // count value and may either miss making a needed call into + // post_field_modification() or will make an unneeded call into + // post_field_modification(). We pay this price to avoid slowing + // down the VM when we aren't watching field modifications. + // Other access/mutation safe by virtue of being in VM state. + return (address)(&_field_modification_count); +} + + +/////////////////////////////////////////////////////////////// +// Functions needed by java.lang.instrument for starting up javaagent. +/////////////////////////////////////////////////////////////// + +jint +JvmtiExport::get_jvmti_interface(JavaVM *jvm, void **penv, jint version) { + /* To Do: add version checks */ + + if (JvmtiEnv::get_phase() == JVMTI_PHASE_LIVE) { + JavaThread* current_thread = (JavaThread*) ThreadLocalStorage::thread(); + // transition code: native to VM + ThreadInVMfromNative __tiv(current_thread); + __ENTRY(jvmtiEnv*, JvmtiExport::get_jvmti_interface, current_thread) + debug_only(VMNativeEntryWrapper __vew;) + + JvmtiEnv *jvmti_env = JvmtiEnv::create_a_jvmti(); + *penv = jvmti_env->jvmti_external(); // actual type is jvmtiEnv* -- not to be confused with JvmtiEnv* + return JNI_OK; + + } else if (JvmtiEnv::get_phase() == JVMTI_PHASE_ONLOAD) { + // not live, no thread to transition + JvmtiEnv *jvmti_env = JvmtiEnv::create_a_jvmti(); + *penv = jvmti_env->jvmti_external(); // actual type is jvmtiEnv* -- not to be confused with JvmtiEnv* + return JNI_OK; + + } else { + // Called at the wrong time + *penv = NULL; + return JNI_EDETACHED; + } +} + +void JvmtiExport::enter_primordial_phase() { + JvmtiEnvBase::set_phase(JVMTI_PHASE_PRIMORDIAL); +} + +void JvmtiExport::enter_start_phase() { + JvmtiManageCapabilities::recompute_always_capabilities(); + JvmtiEnvBase::set_phase(JVMTI_PHASE_START); +} + +void JvmtiExport::enter_onload_phase() { + JvmtiEnvBase::set_phase(JVMTI_PHASE_ONLOAD); +} + +void JvmtiExport::enter_live_phase() { + JvmtiEnvBase::set_phase(JVMTI_PHASE_LIVE); +} + +// +// JVMTI events that the VM posts to the debugger and also startup agent +// and call the agent's premain() for java.lang.instrument. +// + +void JvmtiExport::post_vm_start() { + EVT_TRIG_TRACE(JVMTI_EVENT_VM_START, ("JVMTI Trg VM start event triggered" )); + + // can now enable some events + JvmtiEventController::vm_start(); + + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_VM_START)) { + EVT_TRACE(JVMTI_EVENT_VM_START, ("JVMTI Evt VM start event sent" )); + + JavaThread *thread = JavaThread::current(); + JvmtiThreadEventMark jem(thread); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventVMStart callback = env->callbacks()->VMStart; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env()); + } + } + } +} + + +void JvmtiExport::post_vm_initialized() { + EVT_TRIG_TRACE(JVMTI_EVENT_VM_INIT, ("JVMTI Trg VM init event triggered" )); + + // can now enable events + JvmtiEventController::vm_init(); + + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_VM_INIT)) { + EVT_TRACE(JVMTI_EVENT_VM_INIT, ("JVMTI Evt VM init event sent" )); + + JavaThread *thread = JavaThread::current(); + JvmtiThreadEventMark jem(thread); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventVMInit callback = env->callbacks()->VMInit; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread()); + } + } + } +} + + +void JvmtiExport::post_vm_death() { + EVT_TRIG_TRACE(JVMTI_EVENT_VM_DEATH, ("JVMTI Trg VM death event triggered" )); + + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_VM_DEATH)) { + EVT_TRACE(JVMTI_EVENT_VM_DEATH, ("JVMTI Evt VM death event sent" )); + + JavaThread *thread = JavaThread::current(); + JvmtiEventMark jem(thread); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventVMDeath callback = env->callbacks()->VMDeath; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env()); + } + } + } + + JvmtiEnvBase::set_phase(JVMTI_PHASE_DEAD); + JvmtiEventController::vm_death(); +} + +char** +JvmtiExport::get_all_native_method_prefixes(int* count_ptr) { + // Have to grab JVMTI thread state lock to be sure environment doesn't + // go away while we iterate them. No locks during VM bring-up. + if (Threads::number_of_threads() == 0 || SafepointSynchronize::is_at_safepoint()) { + return JvmtiEnvBase::get_all_native_method_prefixes(count_ptr); + } else { + MutexLocker mu(JvmtiThreadState_lock); + return JvmtiEnvBase::get_all_native_method_prefixes(count_ptr); + } +} + +class JvmtiClassFileLoadHookPoster : public StackObj { + private: + symbolHandle _h_name; + Handle _class_loader; + Handle _h_protection_domain; + unsigned char ** _data_ptr; + unsigned char ** _end_ptr; + JavaThread * _thread; + jint _curr_len; + unsigned char * _curr_data; + JvmtiEnv * _curr_env; + jint * _cached_length_ptr; + unsigned char ** _cached_data_ptr; + JvmtiThreadState * _state; + KlassHandle * _h_class_being_redefined; + JvmtiClassLoadKind _load_kind; + + public: + inline JvmtiClassFileLoadHookPoster(symbolHandle h_name, Handle class_loader, + Handle h_protection_domain, + unsigned char **data_ptr, unsigned char **end_ptr, + unsigned char **cached_data_ptr, + jint *cached_length_ptr) { + _h_name = h_name; + _class_loader = class_loader; + _h_protection_domain = h_protection_domain; + _data_ptr = data_ptr; + _end_ptr = end_ptr; + _thread = JavaThread::current(); + _curr_len = *end_ptr - *data_ptr; + _curr_data = *data_ptr; + _curr_env = NULL; + _cached_length_ptr = cached_length_ptr; + _cached_data_ptr = cached_data_ptr; + *_cached_length_ptr = 0; + *_cached_data_ptr = NULL; + + _state = _thread->jvmti_thread_state(); + if (_state != NULL) { + _h_class_being_redefined = _state->get_class_being_redefined(); + _load_kind = _state->get_class_load_kind(); + // Clear class_being_redefined flag here. The action + // from agent handler could generate a new class file load + // hook event and if it is not cleared the new event generated + // from regular class file load could have this stale redefined + // class handle info. + _state->clear_class_being_redefined(); + } else { + // redefine and retransform will always set the thread state + _h_class_being_redefined = (KlassHandle *) NULL; + _load_kind = jvmti_class_load_kind_load; + } + } + + void post() { +// EVT_TRIG_TRACE(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, +// ("JVMTI [%s] class file load hook event triggered", +// JvmtiTrace::safe_get_thread_name(_thread))); + post_all_envs(); + copy_modified_data(); + } + + private: + void post_all_envs() { + if (_load_kind != jvmti_class_load_kind_retransform) { + // for class load and redefine, + // call the non-retransformable agents + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (!env->is_retransformable() && env->is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { + // non-retransformable agents cannot retransform back, + // so no need to cache the original class file bytes + post_to_env(env, false); + } + } + } + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + // retransformable agents get all events + if (env->is_retransformable() && env->is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { + // retransformable agents need to cache the original class file + // bytes if changes are made via the ClassFileLoadHook + post_to_env(env, true); + } + } + } + + void post_to_env(JvmtiEnv* env, bool caching_needed) { + unsigned char *new_data = NULL; + jint new_len = 0; +// EVT_TRACE(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, +// ("JVMTI [%s] class file load hook event sent %s data_ptr = %d, data_len = %d", +// JvmtiTrace::safe_get_thread_name(_thread), +// _h_name.is_null() ? "NULL" : _h_name->as_utf8(), +// _curr_data, _curr_len )); + JvmtiClassFileLoadEventMark jem(_thread, _h_name, _class_loader, + _h_protection_domain, + _h_class_being_redefined); + JvmtiJavaThreadEventTransition jet(_thread); + JNIEnv* jni_env = (JvmtiEnv::get_phase() == JVMTI_PHASE_PRIMORDIAL)? + NULL : jem.jni_env(); + jvmtiEventClassFileLoadHook callback = env->callbacks()->ClassFileLoadHook; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jni_env, + jem.class_being_redefined(), + jem.jloader(), jem.class_name(), + jem.protection_domain(), + _curr_len, _curr_data, + &new_len, &new_data); + } + if (new_data != NULL) { + // this agent has modified class data. + if (caching_needed && *_cached_data_ptr == NULL) { + // data has been changed by the new retransformable agent + // and it hasn't already been cached, cache it + *_cached_data_ptr = (unsigned char *)os::malloc(_curr_len); + memcpy(*_cached_data_ptr, _curr_data, _curr_len); + *_cached_length_ptr = _curr_len; + } + + if (_curr_data != *_data_ptr) { + // curr_data is previous agent modified class data. + // And this has been changed by the new agent so + // we can delete it now. + _curr_env->Deallocate(_curr_data); + } + + // Class file data has changed by the current agent. + _curr_data = new_data; + _curr_len = new_len; + // Save the current agent env we need this to deallocate the + // memory allocated by this agent. + _curr_env = env; + } + } + + void copy_modified_data() { + // if one of the agent has modified class file data. + // Copy modified class data to new resources array. + if (_curr_data != *_data_ptr) { + *_data_ptr = NEW_RESOURCE_ARRAY(u1, _curr_len); + memcpy(*_data_ptr, _curr_data, _curr_len); + *_end_ptr = *_data_ptr + _curr_len; + _curr_env->Deallocate(_curr_data); + } + } +}; + +bool JvmtiExport::_should_post_class_file_load_hook = false; + +// this entry is for class file load hook on class load, redefine and retransform +void JvmtiExport::post_class_file_load_hook(symbolHandle h_name, + Handle class_loader, + Handle h_protection_domain, + unsigned char **data_ptr, + unsigned char **end_ptr, + unsigned char **cached_data_ptr, + jint *cached_length_ptr) { + JvmtiClassFileLoadHookPoster poster(h_name, class_loader, + h_protection_domain, + data_ptr, end_ptr, + cached_data_ptr, + cached_length_ptr); + poster.post(); +} + +void JvmtiExport::report_unsupported(bool on) { + // If any JVMTI service is turned on, we need to exit before native code + // tries to access nonexistant services. + if (on) { + vm_exit_during_initialization("Java Kernel does not support JVMTI."); + } +} + + +#ifndef JVMTI_KERNEL +static inline klassOop oop_to_klassOop(oop obj) { + klassOop k = obj->klass(); + + // if the object is a java.lang.Class then return the java mirror + if (k == SystemDictionary::class_klass()) { + if (!java_lang_Class::is_primitive(obj)) { + k = java_lang_Class::as_klassOop(obj); + assert(k != NULL, "class for non-primitive mirror must exist"); + } + } + return k; +} + +class JvmtiVMObjectAllocEventMark : public JvmtiClassEventMark { + private: + jobject _jobj; + jlong _size; + public: + JvmtiVMObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klassOop(obj)) { + _jobj = (jobject)to_jobject(obj); + _size = obj->size() * wordSize; + }; + jobject jni_jobject() { return _jobj; } + jlong size() { return _size; } +}; + +class JvmtiCompiledMethodLoadEventMark : public JvmtiMethodEventMark { + private: + jint _code_size; + const void *_code_data; + jint _map_length; + jvmtiAddrLocationMap *_map; + const void *_compile_info; + public: + JvmtiCompiledMethodLoadEventMark(JavaThread *thread, nmethod *nm) + : JvmtiMethodEventMark(thread,methodHandle(thread, nm->method())) { + _code_data = nm->code_begin(); + _code_size = nm->code_size(); + _compile_info = NULL; /* no info for our VM. */ + JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nm, &_map, &_map_length); + } + ~JvmtiCompiledMethodLoadEventMark() { + FREE_C_HEAP_ARRAY(jvmtiAddrLocationMap, _map); + } + + jint code_size() { return _code_size; } + const void *code_data() { return _code_data; } + jint map_length() { return _map_length; } + const jvmtiAddrLocationMap* map() { return _map; } + const void *compile_info() { return _compile_info; } +}; + + + +class JvmtiMonitorEventMark : public JvmtiThreadEventMark { +private: + jobject _jobj; +public: + JvmtiMonitorEventMark(JavaThread *thread, oop object) + : JvmtiThreadEventMark(thread){ + _jobj = to_jobject(object); + } + jobject jni_object() { return _jobj; } +}; + +/////////////////////////////////////////////////////////////// +// +// pending CompiledMethodUnload support +// + +bool JvmtiExport::_have_pending_compiled_method_unload_events; +GrowableArray* JvmtiExport::_pending_compiled_method_unload_method_ids; +GrowableArray* JvmtiExport::_pending_compiled_method_unload_code_begins; +JavaThread* JvmtiExport::_current_poster; + +// post any pending CompiledMethodUnload events + +void JvmtiExport::post_pending_compiled_method_unload_events() { + JavaThread* self = JavaThread::current(); + assert(!self->owns_locks(), "can't hold locks"); + + // Indicates if this is the first activiation of this function. + // In theory the profiler's callback could call back into VM and provoke + // another CompiledMethodLoad event to be posted from this thread. As the + // stack rewinds we need to ensure that the original activation does the + // completion and notifies any waiters. + bool first_activation = false; + + // the jmethodID (may not be valid) to be used for a single event + jmethodID method; + const void *code_begin; + + // grab the monitor and check if another thread is already posting + // events. If there is another thread posting events then we wait + // until it completes. (In theory we could check the pending events to + // see if any of the addresses overlap with the event that we want to + // post but as it will happen so rarely we just block any thread waiting + // to post a CompiledMethodLoad or DynamicCodeGenerated event until all + // pending CompiledMethodUnload events have been posted). + // + // If another thread isn't posting we examine the list of pending jmethodIDs. + // If the list is empty then we are done. If it's not empty then this thread + // (self) becomes the pending event poster and we remove the top (last) + // event from the list. Note that this means we remove the newest event first + // but as they are all CompiledMethodUnload events the order doesn't matter. + // Once we have removed a jmethodID then we exit the monitor. Any other thread + // wanting to post a CompiledMethodLoad or DynamicCodeGenerated event will + // be forced to wait on the monitor. + { + MutexLocker mu(JvmtiPendingEvent_lock); + if (_current_poster != self) { + while (_current_poster != NULL) { + JvmtiPendingEvent_lock->wait(); + } + } + if ((_pending_compiled_method_unload_method_ids == NULL) || + (_pending_compiled_method_unload_method_ids->length() == 0)) { + return; + } + if (_current_poster == NULL) { + _current_poster = self; + first_activation = true; + } else { + // re-entrant + guarantee(_current_poster == self, "checking"); + } + method = _pending_compiled_method_unload_method_ids->pop(); + code_begin = _pending_compiled_method_unload_code_begins->pop(); + } + + // This thread is the pending event poster so it first posts the CompiledMethodUnload + // event for the jmethodID that has been removed from the list. Once posted it + // re-grabs the monitor and checks the list again. If the list is empty then and this + // is the first activation of the function then we reset the _have_pending_events + // flag, cleanup _current_poster to indicate that no thread is now servicing the + // pending events list, and finally notify any thread that might be waiting. + for (;;) { + EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD, + ("JVMTI [%s] method compile unload event triggered", + JvmtiTrace::safe_get_thread_name(self))); + + // post the event for each environment that has this event enabled. + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_COMPILED_METHOD_UNLOAD)) { + EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD, + ("JVMTI [%s] class compile method unload event sent jmethodID " PTR_FORMAT, + JvmtiTrace::safe_get_thread_name(self), method)); + + JvmtiEventMark jem(self); + JvmtiJavaThreadEventTransition jet(self); + jvmtiEventCompiledMethodUnload callback = env->callbacks()->CompiledMethodUnload; + if (callback != NULL) { + (*callback)(env->jvmti_external(), method, code_begin); + } + } + } + + // event posted, now re-grab monitor and get the next event + // If there's no next event then we are done. If this is the first + // activiation of this function by this thread notify any waiters + // so that they can post. + { + MutexLocker ml(JvmtiPendingEvent_lock); + if (_pending_compiled_method_unload_method_ids->length() == 0) { + if (first_activation) { + _have_pending_compiled_method_unload_events = false; + _current_poster = NULL; + JvmtiPendingEvent_lock->notify_all(); + } + return; + } + method = _pending_compiled_method_unload_method_ids->pop(); + code_begin = _pending_compiled_method_unload_code_begins->pop(); + } + } +} + +/////////////////////////////////////////////////////////////// +// +// JvmtiExport +// + +void JvmtiExport::post_raw_breakpoint(JavaThread *thread, methodOop method, address location) { + HandleMark hm(thread); + methodHandle mh(thread, method); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + EVT_TRIG_TRACE(JVMTI_EVENT_BREAKPOINT, ("JVMTI [%s] Trg Breakpoint triggered", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + ets->compare_and_set_current_location(mh(), location, JVMTI_EVENT_BREAKPOINT); + if (!ets->breakpoint_posted() && ets->is_enabled(JVMTI_EVENT_BREAKPOINT)) { + ThreadState old_os_state = thread->osthread()->get_state(); + thread->osthread()->set_state(BREAKPOINTED); + EVT_TRACE(JVMTI_EVENT_BREAKPOINT, ("JVMTI [%s] Evt Breakpoint sent %s.%s @ %d", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string(), + location - mh()->code_base() )); + + JvmtiEnv *env = ets->get_env(); + JvmtiLocationEventMark jem(thread, mh, location); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventBreakpoint callback = env->callbacks()->Breakpoint; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_methodID(), jem.location()); + } + + ets->set_breakpoint_posted(); + thread->osthread()->set_state(old_os_state); + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +bool JvmtiExport::_can_get_source_debug_extension = false; +bool JvmtiExport::_can_maintain_original_method_order = false; +bool JvmtiExport::_can_post_interpreter_events = false; +bool JvmtiExport::_can_post_exceptions = false; +bool JvmtiExport::_can_post_breakpoint = false; +bool JvmtiExport::_can_post_field_access = false; +bool JvmtiExport::_can_post_field_modification = false; +bool JvmtiExport::_can_post_method_entry = false; +bool JvmtiExport::_can_post_method_exit = false; +bool JvmtiExport::_can_pop_frame = false; +bool JvmtiExport::_can_force_early_return = false; + +bool JvmtiExport::_should_post_single_step = false; +bool JvmtiExport::_should_post_field_access = false; +bool JvmtiExport::_should_post_field_modification = false; +bool JvmtiExport::_should_post_class_load = false; +bool JvmtiExport::_should_post_class_prepare = false; +bool JvmtiExport::_should_post_class_unload = false; +bool JvmtiExport::_should_post_thread_life = false; +bool JvmtiExport::_should_clean_up_heap_objects = false; +bool JvmtiExport::_should_post_native_method_bind = false; +bool JvmtiExport::_should_post_dynamic_code_generated = false; +bool JvmtiExport::_should_post_data_dump = false; +bool JvmtiExport::_should_post_compiled_method_load = false; +bool JvmtiExport::_should_post_compiled_method_unload = false; +bool JvmtiExport::_should_post_monitor_contended_enter = false; +bool JvmtiExport::_should_post_monitor_contended_entered = false; +bool JvmtiExport::_should_post_monitor_wait = false; +bool JvmtiExport::_should_post_monitor_waited = false; +bool JvmtiExport::_should_post_garbage_collection_start = false; +bool JvmtiExport::_should_post_garbage_collection_finish = false; +bool JvmtiExport::_should_post_object_free = false; +bool JvmtiExport::_should_post_resource_exhausted = false; +bool JvmtiExport::_should_post_vm_object_alloc = false; + +//////////////////////////////////////////////////////////////////////////////////////////////// + + +// +// JVMTI single step management +// +void JvmtiExport::at_single_stepping_point(JavaThread *thread, methodOop method, address location) { + assert(JvmtiExport::should_post_single_step(), "must be single stepping"); + + HandleMark hm(thread); + methodHandle mh(thread, method); + + // update information about current location and post a step event + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + EVT_TRIG_TRACE(JVMTI_EVENT_SINGLE_STEP, ("JVMTI [%s] Trg Single Step triggered", + JvmtiTrace::safe_get_thread_name(thread))); + if (!state->hide_single_stepping()) { + if (state->is_pending_step_for_popframe()) { + state->process_pending_step_for_popframe(); + } + if (state->is_pending_step_for_earlyret()) { + state->process_pending_step_for_earlyret(); + } + JvmtiExport::post_single_step(thread, mh(), location); + } +} + + +void JvmtiExport::expose_single_stepping(JavaThread *thread) { + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state != NULL) { + state->clear_hide_single_stepping(); + } +} + + +bool JvmtiExport::hide_single_stepping(JavaThread *thread) { + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state != NULL && state->is_enabled(JVMTI_EVENT_SINGLE_STEP)) { + state->set_hide_single_stepping(); + return true; + } else { + return false; + } +} + +void JvmtiExport::post_class_load(JavaThread *thread, klassOop klass) { + HandleMark hm(thread); + KlassHandle kh(thread, klass); + + EVT_TRIG_TRACE(JVMTI_EVENT_CLASS_LOAD, ("JVMTI [%s] Trg Class Load triggered", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiThreadState* state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_CLASS_LOAD)) { + EVT_TRACE(JVMTI_EVENT_CLASS_LOAD, ("JVMTI [%s] Evt Class Load sent %s", + JvmtiTrace::safe_get_thread_name(thread), + kh()==NULL? "NULL" : Klass::cast(kh())->external_name() )); + + JvmtiEnv *env = ets->get_env(); + JvmtiClassEventMark jem(thread, kh()); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventClassLoad callback = env->callbacks()->ClassLoad; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_class()); + } + } + } +} + + +void JvmtiExport::post_class_prepare(JavaThread *thread, klassOop klass) { + HandleMark hm(thread); + KlassHandle kh(thread, klass); + + EVT_TRIG_TRACE(JVMTI_EVENT_CLASS_PREPARE, ("JVMTI [%s] Trg Class Prepare triggered", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiThreadState* state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_CLASS_PREPARE)) { + EVT_TRACE(JVMTI_EVENT_CLASS_PREPARE, ("JVMTI [%s] Evt Class Prepare sent %s", + JvmtiTrace::safe_get_thread_name(thread), + kh()==NULL? "NULL" : Klass::cast(kh())->external_name() )); + + JvmtiEnv *env = ets->get_env(); + JvmtiClassEventMark jem(thread, kh()); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventClassPrepare callback = env->callbacks()->ClassPrepare; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_class()); + } + } + } +} + +void JvmtiExport::post_class_unload(klassOop klass) { + Thread *thread = Thread::current(); + HandleMark hm(thread); + KlassHandle kh(thread, klass); + + EVT_TRIG_TRACE(EXT_EVENT_CLASS_UNLOAD, ("JVMTI [?] Trg Class Unload triggered" )); + if (JvmtiEventController::is_enabled((jvmtiEvent)EXT_EVENT_CLASS_UNLOAD)) { + assert(thread->is_VM_thread(), "wrong thread"); + + // get JavaThread for whom we are proxy + JavaThread *real_thread = + (JavaThread *)((VMThread *)thread)->vm_operation()->calling_thread(); + + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled((jvmtiEvent)EXT_EVENT_CLASS_UNLOAD)) { + EVT_TRACE(EXT_EVENT_CLASS_UNLOAD, ("JVMTI [?] Evt Class Unload sent %s", + kh()==NULL? "NULL" : Klass::cast(kh())->external_name() )); + + // do everything manually, since this is a proxy - needs special care + JNIEnv* jni_env = real_thread->jni_environment(); + jthread jt = (jthread)JNIHandles::make_local(real_thread, real_thread->threadObj()); + jclass jk = (jclass)JNIHandles::make_local(real_thread, Klass::cast(kh())->java_mirror()); + + // Before we call the JVMTI agent, we have to set the state in the + // thread for which we are proxying. + JavaThreadState prev_state = real_thread->thread_state(); + assert(prev_state == _thread_blocked, "JavaThread should be at safepoint"); + real_thread->set_thread_state(_thread_in_native); + + jvmtiExtensionEvent callback = env->ext_callbacks()->ClassUnload; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jni_env, jt, jk); + } + + assert(real_thread->thread_state() == _thread_in_native, + "JavaThread should be in native"); + real_thread->set_thread_state(prev_state); + + JNIHandles::destroy_local(jk); + JNIHandles::destroy_local(jt); + } + } + } +} + + +void JvmtiExport::post_thread_start(JavaThread *thread) { + assert(thread->thread_state() == _thread_in_vm, "must be in vm state"); + + EVT_TRIG_TRACE(JVMTI_EVENT_THREAD_START, ("JVMTI [%s] Trg Thread Start event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + + // do JVMTI thread initialization (if needed) + JvmtiEventController::thread_started(thread); + + // Do not post thread start event for hidden java thread. + if (JvmtiEventController::is_enabled(JVMTI_EVENT_THREAD_START) && + !thread->is_hidden_from_external_view()) { + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_THREAD_START)) { + EVT_TRACE(JVMTI_EVENT_THREAD_START, ("JVMTI [%s] Evt Thread Start event sent", + JvmtiTrace::safe_get_thread_name(thread) )); + + JvmtiThreadEventMark jem(thread); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventThreadStart callback = env->callbacks()->ThreadStart; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread()); + } + } + } + } +} + + +void JvmtiExport::post_thread_end(JavaThread *thread) { + EVT_TRIG_TRACE(JVMTI_EVENT_THREAD_END, ("JVMTI [%s] Trg Thread End event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + + // Do not post thread end event for hidden java thread. + if (state->is_enabled(JVMTI_EVENT_THREAD_END) && + !thread->is_hidden_from_external_view()) { + + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_THREAD_END)) { + EVT_TRACE(JVMTI_EVENT_THREAD_END, ("JVMTI [%s] Evt Thread End event sent", + JvmtiTrace::safe_get_thread_name(thread) )); + + JvmtiEnv *env = ets->get_env(); + JvmtiThreadEventMark jem(thread); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventThreadEnd callback = env->callbacks()->ThreadEnd; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread()); + } + } + } + } +} + +void JvmtiExport::post_object_free(JvmtiEnv* env, jlong tag) { + assert(SafepointSynchronize::is_at_safepoint(), "must be executed at safepoint"); + assert(env->is_enabled(JVMTI_EVENT_OBJECT_FREE), "checking"); + + EVT_TRIG_TRACE(JVMTI_EVENT_OBJECT_FREE, ("JVMTI [?] Trg Object Free triggered" )); + EVT_TRACE(JVMTI_EVENT_OBJECT_FREE, ("JVMTI [?] Evt Object Free sent")); + + jvmtiEventObjectFree callback = env->callbacks()->ObjectFree; + if (callback != NULL) { + (*callback)(env->jvmti_external(), tag); + } +} + +void JvmtiExport::post_resource_exhausted(jint resource_exhausted_flags, const char* description) { + EVT_TRIG_TRACE(JVMTI_EVENT_RESOURCE_EXHAUSTED, ("JVMTI Trg resource exhausted event triggered" )); + + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_RESOURCE_EXHAUSTED)) { + EVT_TRACE(JVMTI_EVENT_RESOURCE_EXHAUSTED, ("JVMTI Evt resource exhausted event sent" )); + + JavaThread *thread = JavaThread::current(); + JvmtiThreadEventMark jem(thread); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventResourceExhausted callback = env->callbacks()->ResourceExhausted; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), + resource_exhausted_flags, NULL, description); + } + } + } +} + +void JvmtiExport::post_method_entry(JavaThread *thread, methodOop method, frame current_frame) { + HandleMark hm(thread); + methodHandle mh(thread, method); + + EVT_TRIG_TRACE(JVMTI_EVENT_METHOD_ENTRY, ("JVMTI [%s] Trg Method Entry triggered %s.%s", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string() )); + + JvmtiThreadState* state = thread->jvmti_thread_state(); + if (state == NULL || !state->is_interp_only_mode()) { + // for any thread that actually wants method entry, interp_only_mode is set + return; + } + + state->incr_cur_stack_depth(); + + if (state->is_enabled(JVMTI_EVENT_METHOD_ENTRY)) { + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_METHOD_ENTRY)) { + EVT_TRACE(JVMTI_EVENT_METHOD_ENTRY, ("JVMTI [%s] Evt Method Entry sent %s.%s", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string() )); + + JvmtiEnv *env = ets->get_env(); + JvmtiMethodEventMark jem(thread, mh); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventMethodEntry callback = env->callbacks()->MethodEntry; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_methodID()); + } + } + } + } +} + +void JvmtiExport::post_method_exit(JavaThread *thread, methodOop method, frame current_frame) { + HandleMark hm(thread); + methodHandle mh(thread, method); + + EVT_TRIG_TRACE(JVMTI_EVENT_METHOD_EXIT, ("JVMTI [%s] Trg Method Exit triggered %s.%s", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string() )); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL || !state->is_interp_only_mode()) { + // for any thread that actually wants method exit, interp_only_mode is set + return; + } + + // return a flag when a method terminates by throwing an exception + // i.e. if an exception is thrown and it's not caught by the current method + bool exception_exit = state->is_exception_detected() && !state->is_exception_caught(); + + + if (state->is_enabled(JVMTI_EVENT_METHOD_EXIT)) { + Handle result; + jvalue value; + value.j = 0L; + + // if the method hasn't been popped because of an exception then we populate + // the return_value parameter for the callback. At this point we only have + // the address of a "raw result" and we just call into the interpreter to + // convert this into a jvalue. + if (!exception_exit) { + oop oop_result; + BasicType type = current_frame.interpreter_frame_result(&oop_result, &value); + if (type == T_OBJECT || type == T_ARRAY) { + result = Handle(thread, oop_result); + } + } + + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_METHOD_EXIT)) { + EVT_TRACE(JVMTI_EVENT_METHOD_EXIT, ("JVMTI [%s] Evt Method Exit sent %s.%s", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string() )); + + JvmtiEnv *env = ets->get_env(); + JvmtiMethodEventMark jem(thread, mh); + if (result.not_null()) { + value.l = JNIHandles::make_local(thread, result()); + } + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventMethodExit callback = env->callbacks()->MethodExit; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_methodID(), exception_exit, value); + } + } + } + } + + if (state->is_enabled(JVMTI_EVENT_FRAME_POP)) { + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + int cur_frame_number = state->cur_stack_depth(); + + if (ets->is_frame_pop(cur_frame_number)) { + // we have a NotifyFramePop entry for this frame. + // now check that this env/thread wants this event + if (ets->is_enabled(JVMTI_EVENT_FRAME_POP)) { + EVT_TRACE(JVMTI_EVENT_FRAME_POP, ("JVMTI [%s] Evt Frame Pop sent %s.%s", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string() )); + + // we also need to issue a frame pop event for this frame + JvmtiEnv *env = ets->get_env(); + JvmtiMethodEventMark jem(thread, mh); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventFramePop callback = env->callbacks()->FramePop; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_methodID(), exception_exit); + } + } + // remove the frame's entry + ets->clear_frame_pop(cur_frame_number); + } + } + } + + state->decr_cur_stack_depth(); +} + + +// Todo: inline this for optimization +void JvmtiExport::post_single_step(JavaThread *thread, methodOop method, address location) { + HandleMark hm(thread); + methodHandle mh(thread, method); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + ets->compare_and_set_current_location(mh(), location, JVMTI_EVENT_SINGLE_STEP); + if (!ets->single_stepping_posted() && ets->is_enabled(JVMTI_EVENT_SINGLE_STEP)) { + EVT_TRACE(JVMTI_EVENT_SINGLE_STEP, ("JVMTI [%s] Evt Single Step sent %s.%s @ %d", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string(), + location - mh()->code_base() )); + + JvmtiEnv *env = ets->get_env(); + JvmtiLocationEventMark jem(thread, mh, location); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventSingleStep callback = env->callbacks()->SingleStep; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_methodID(), jem.location()); + } + + ets->set_single_stepping_posted(); + } + } +} + + +void JvmtiExport::post_exception_throw(JavaThread *thread, methodOop method, address location, oop exception) { + HandleMark hm(thread); + methodHandle mh(thread, method); + Handle exception_handle(thread, exception); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + + EVT_TRIG_TRACE(JVMTI_EVENT_EXCEPTION, ("JVMTI [%s] Trg Exception thrown triggered", + JvmtiTrace::safe_get_thread_name(thread))); + if (!state->is_exception_detected()) { + state->set_exception_detected(); + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_EXCEPTION) && (exception != NULL)) { + + EVT_TRACE(JVMTI_EVENT_EXCEPTION, + ("JVMTI [%s] Evt Exception thrown sent %s.%s @ %d", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string(), + location - mh()->code_base() )); + + JvmtiEnv *env = ets->get_env(); + JvmtiExceptionEventMark jem(thread, mh, location, exception_handle); + + // It's okay to clear these exceptions here because we duplicate + // this lookup in InterpreterRuntime::exception_handler_for_exception. + EXCEPTION_MARK; + + bool should_repeat; + vframeStream st(thread); + assert(!st.at_end(), "cannot be at end"); + methodOop current_method = NULL; + int current_bci = -1; + do { + current_method = st.method(); + current_bci = st.bci(); + do { + should_repeat = false; + KlassHandle eh_klass(thread, exception_handle()->klass()); + current_bci = current_method->fast_exception_handler_bci_for( + eh_klass, current_bci, THREAD); + if (HAS_PENDING_EXCEPTION) { + exception_handle = KlassHandle(thread, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + should_repeat = true; + } + } while (should_repeat && (current_bci != -1)); + st.next(); + } while ((current_bci < 0) && (!st.at_end())); + + jmethodID catch_jmethodID; + if (current_bci < 0) { + catch_jmethodID = 0; + current_bci = 0; + } else { + catch_jmethodID = jem.to_jmethodID( + methodHandle(thread, current_method)); + } + + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventException callback = env->callbacks()->Exception; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_methodID(), jem.location(), + jem.exception(), + catch_jmethodID, current_bci); + } + } + } + } + + // frames may get popped because of this throw, be safe - invalidate cached depth + state->invalidate_cur_stack_depth(); +} + + +void JvmtiExport::notice_unwind_due_to_exception(JavaThread *thread, methodOop method, address location, oop exception, bool in_handler_frame) { + HandleMark hm(thread); + methodHandle mh(thread, method); + Handle exception_handle(thread, exception); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + EVT_TRIG_TRACE(JVMTI_EVENT_EXCEPTION_CATCH, + ("JVMTI [%s] Trg unwind_due_to_exception triggered %s.%s @ %s%d - %s", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string(), + location==0? "no location:" : "", + location==0? 0 : location - mh()->code_base(), + in_handler_frame? "in handler frame" : "not handler frame" )); + + if (state->is_exception_detected()) { + + state->invalidate_cur_stack_depth(); + if (!in_handler_frame) { + // Not in exception handler. + if(state->is_interp_only_mode()) { + // method exit and frame pop events are posted only in interp mode. + // When these events are enabled code should be in running in interp mode. + JvmtiExport::post_method_exit(thread, method, thread->last_frame()); + // The cached cur_stack_depth might have changed from the + // operations of frame pop or method exit. We are not 100% sure + // the cached cur_stack_depth is still valid depth so invalidate + // it. + state->invalidate_cur_stack_depth(); + } + } else { + // In exception handler frame. Report exception catch. + assert(location != NULL, "must be a known location"); + // Update cur_stack_depth - the frames above the current frame + // have been unwound due to this exception: + assert(!state->is_exception_caught(), "exception must not be caught yet."); + state->set_exception_caught(); + + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_EXCEPTION_CATCH) && (exception_handle() != NULL)) { + EVT_TRACE(JVMTI_EVENT_EXCEPTION_CATCH, + ("JVMTI [%s] Evt ExceptionCatch sent %s.%s @ %d", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string(), + location - mh()->code_base() )); + + JvmtiEnv *env = ets->get_env(); + JvmtiExceptionEventMark jem(thread, mh, location, exception_handle); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventExceptionCatch callback = env->callbacks()->ExceptionCatch; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_methodID(), jem.location(), + jem.exception()); + } + } + } + } + } +} + +oop JvmtiExport::jni_GetField_probe(JavaThread *thread, jobject jobj, oop obj, + klassOop klass, jfieldID fieldID, bool is_static) { + if (*((int *)get_field_access_count_addr()) > 0 && thread->has_last_Java_frame()) { + // At least one field access watch is set so we have more work + // to do. This wrapper is used by entry points that allow us + // to create handles in post_field_access_by_jni(). + post_field_access_by_jni(thread, obj, klass, fieldID, is_static); + // event posting can block so refetch oop if we were passed a jobj + if (jobj != NULL) return JNIHandles::resolve_non_null(jobj); + } + return obj; +} + +oop JvmtiExport::jni_GetField_probe_nh(JavaThread *thread, jobject jobj, oop obj, + klassOop klass, jfieldID fieldID, bool is_static) { + if (*((int *)get_field_access_count_addr()) > 0 && thread->has_last_Java_frame()) { + // At least one field access watch is set so we have more work + // to do. This wrapper is used by "quick" entry points that don't + // allow us to create handles in post_field_access_by_jni(). We + // override that with a ResetNoHandleMark. + ResetNoHandleMark rnhm; + post_field_access_by_jni(thread, obj, klass, fieldID, is_static); + // event posting can block so refetch oop if we were passed a jobj + if (jobj != NULL) return JNIHandles::resolve_non_null(jobj); + } + return obj; +} + +void JvmtiExport::post_field_access_by_jni(JavaThread *thread, oop obj, + klassOop klass, jfieldID fieldID, bool is_static) { + // We must be called with a Java context in order to provide reasonable + // values for the klazz, method, and location fields. The callers of this + // function don't make the call unless there is a Java context. + assert(thread->has_last_Java_frame(), "must be called with a Java context"); + + ResourceMark rm; + fieldDescriptor fd; + // if get_field_descriptor finds fieldID to be invalid, then we just bail + bool valid_fieldID = JvmtiEnv::get_field_descriptor(klass, fieldID, &fd); + assert(valid_fieldID == true,"post_field_access_by_jni called with invalid fieldID"); + if (!valid_fieldID) return; + // field accesses are not watched so bail + if (!fd.is_field_access_watched()) return; + + HandleMark hm(thread); + KlassHandle h_klass(thread, klass); + Handle h_obj; + if (!is_static) { + // non-static field accessors have an object, but we need a handle + assert(obj != NULL, "non-static needs an object"); + h_obj = Handle(thread, obj); + } + post_field_access(thread, + thread->last_frame().interpreter_frame_method(), + thread->last_frame().interpreter_frame_bcp(), + h_klass, h_obj, fieldID); +} + +void JvmtiExport::post_field_access(JavaThread *thread, methodOop method, + address location, KlassHandle field_klass, Handle object, jfieldID field) { + + HandleMark hm(thread); + methodHandle mh(thread, method); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + EVT_TRIG_TRACE(JVMTI_EVENT_FIELD_ACCESS, ("JVMTI [%s] Trg Field Access event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_FIELD_ACCESS)) { + EVT_TRACE(JVMTI_EVENT_FIELD_ACCESS, ("JVMTI [%s] Evt Field Access event sent %s.%s @ %d", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string(), + location - mh()->code_base() )); + + JvmtiEnv *env = ets->get_env(); + JvmtiLocationEventMark jem(thread, mh, location); + jclass field_jclass = jem.to_jclass(field_klass()); + jobject field_jobject = jem.to_jobject(object()); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventFieldAccess callback = env->callbacks()->FieldAccess; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_methodID(), jem.location(), + field_jclass, field_jobject, field); + } + } + } +} + +oop JvmtiExport::jni_SetField_probe(JavaThread *thread, jobject jobj, oop obj, + klassOop klass, jfieldID fieldID, bool is_static, + char sig_type, jvalue *value) { + if (*((int *)get_field_modification_count_addr()) > 0 && thread->has_last_Java_frame()) { + // At least one field modification watch is set so we have more work + // to do. This wrapper is used by entry points that allow us + // to create handles in post_field_modification_by_jni(). + post_field_modification_by_jni(thread, obj, klass, fieldID, is_static, sig_type, value); + // event posting can block so refetch oop if we were passed a jobj + if (jobj != NULL) return JNIHandles::resolve_non_null(jobj); + } + return obj; +} + +oop JvmtiExport::jni_SetField_probe_nh(JavaThread *thread, jobject jobj, oop obj, + klassOop klass, jfieldID fieldID, bool is_static, + char sig_type, jvalue *value) { + if (*((int *)get_field_modification_count_addr()) > 0 && thread->has_last_Java_frame()) { + // At least one field modification watch is set so we have more work + // to do. This wrapper is used by "quick" entry points that don't + // allow us to create handles in post_field_modification_by_jni(). We + // override that with a ResetNoHandleMark. + ResetNoHandleMark rnhm; + post_field_modification_by_jni(thread, obj, klass, fieldID, is_static, sig_type, value); + // event posting can block so refetch oop if we were passed a jobj + if (jobj != NULL) return JNIHandles::resolve_non_null(jobj); + } + return obj; +} + +void JvmtiExport::post_field_modification_by_jni(JavaThread *thread, oop obj, + klassOop klass, jfieldID fieldID, bool is_static, + char sig_type, jvalue *value) { + // We must be called with a Java context in order to provide reasonable + // values for the klazz, method, and location fields. The callers of this + // function don't make the call unless there is a Java context. + assert(thread->has_last_Java_frame(), "must be called with Java context"); + + ResourceMark rm; + fieldDescriptor fd; + // if get_field_descriptor finds fieldID to be invalid, then we just bail + bool valid_fieldID = JvmtiEnv::get_field_descriptor(klass, fieldID, &fd); + assert(valid_fieldID == true,"post_field_modification_by_jni called with invalid fieldID"); + if (!valid_fieldID) return; + // field modifications are not watched so bail + if (!fd.is_field_modification_watched()) return; + + HandleMark hm(thread); + + Handle h_obj; + if (!is_static) { + // non-static field accessors have an object, but we need a handle + assert(obj != NULL, "non-static needs an object"); + h_obj = Handle(thread, obj); + } + KlassHandle h_klass(thread, klass); + post_field_modification(thread, + thread->last_frame().interpreter_frame_method(), + thread->last_frame().interpreter_frame_bcp(), + h_klass, h_obj, fieldID, sig_type, value); +} + +void JvmtiExport::post_raw_field_modification(JavaThread *thread, methodOop method, + address location, KlassHandle field_klass, Handle object, jfieldID field, + char sig_type, jvalue *value) { + + if (sig_type == 'I' || sig_type == 'Z' || sig_type == 'C' || sig_type == 'S') { + // 'I' instructions are used for byte, char, short and int. + // determine which it really is, and convert + fieldDescriptor fd; + bool found = JvmtiEnv::get_field_descriptor(field_klass(), field, &fd); + // should be found (if not, leave as is) + if (found) { + jint ival = value->i; + // convert value from int to appropriate type + switch (fd.field_type()) { + case T_BOOLEAN: + sig_type = 'Z'; + value->i = 0; // clear it + value->z = (jboolean)ival; + break; + case T_BYTE: + sig_type = 'B'; + value->i = 0; // clear it + value->b = (jbyte)ival; + break; + case T_CHAR: + sig_type = 'C'; + value->i = 0; // clear it + value->c = (jchar)ival; + break; + case T_SHORT: + sig_type = 'S'; + value->i = 0; // clear it + value->s = (jshort)ival; + break; + case T_INT: + // nothing to do + break; + default: + // this is an integer instruction, should be one of above + ShouldNotReachHere(); + break; + } + } + } + + // convert oop to JNI handle. + if (sig_type == 'L' || sig_type == '[') { + value->l = (jobject)JNIHandles::make_local(thread, (oop)value->l); + } + + post_field_modification(thread, method, location, field_klass, object, field, sig_type, value); + + // Destroy the JNI handle allocated above. + if (sig_type == 'L') { + JNIHandles::destroy_local(value->l); + } +} + +void JvmtiExport::post_field_modification(JavaThread *thread, methodOop method, + address location, KlassHandle field_klass, Handle object, jfieldID field, + char sig_type, jvalue *value_ptr) { + + HandleMark hm(thread); + methodHandle mh(thread, method); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + EVT_TRIG_TRACE(JVMTI_EVENT_FIELD_MODIFICATION, + ("JVMTI [%s] Trg Field Modification event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_FIELD_MODIFICATION)) { + EVT_TRACE(JVMTI_EVENT_FIELD_MODIFICATION, + ("JVMTI [%s] Evt Field Modification event sent %s.%s @ %d", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string(), + location - mh()->code_base() )); + + JvmtiEnv *env = ets->get_env(); + JvmtiLocationEventMark jem(thread, mh, location); + jclass field_jclass = jem.to_jclass(field_klass()); + jobject field_jobject = jem.to_jobject(object()); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventFieldModification callback = env->callbacks()->FieldModification; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_methodID(), jem.location(), + field_jclass, field_jobject, field, sig_type, *value_ptr); + } + } + } +} + +void JvmtiExport::post_native_method_bind(methodOop method, address* function_ptr) { + JavaThread* thread = JavaThread::current(); + assert(thread->thread_state() == _thread_in_vm, "must be in vm state"); + + HandleMark hm(thread); + methodHandle mh(thread, method); + + EVT_TRIG_TRACE(JVMTI_EVENT_NATIVE_METHOD_BIND, ("JVMTI [%s] Trg Native Method Bind event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + + if (JvmtiEventController::is_enabled(JVMTI_EVENT_NATIVE_METHOD_BIND)) { + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_NATIVE_METHOD_BIND)) { + EVT_TRACE(JVMTI_EVENT_NATIVE_METHOD_BIND, ("JVMTI [%s] Evt Native Method Bind event sent", + JvmtiTrace::safe_get_thread_name(thread) )); + + JvmtiMethodEventMark jem(thread, mh); + JvmtiJavaThreadEventTransition jet(thread); + JNIEnv* jni_env = JvmtiEnv::get_phase() == JVMTI_PHASE_PRIMORDIAL? NULL : jem.jni_env(); + jvmtiEventNativeMethodBind callback = env->callbacks()->NativeMethodBind; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jni_env, jem.jni_thread(), + jem.jni_methodID(), (void*)(*function_ptr), (void**)function_ptr); + } + } + } + } +} + + +void JvmtiExport::post_compiled_method_load(nmethod *nm) { + // If there are pending CompiledMethodUnload events then these are + // posted before this CompiledMethodLoad event. We "lock" the nmethod and + // maintain a handle to the methodOop to ensure that the nmethod isn't + // flushed or unloaded while posting the events. + JavaThread* thread = JavaThread::current(); + if (have_pending_compiled_method_unload_events()) { + methodHandle mh(thread, nm->method()); + nmethodLocker nml(nm); + post_pending_compiled_method_unload_events(); + } + + EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD, + ("JVMTI [%s] method compile load event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_COMPILED_METHOD_LOAD)) { + + EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD, + ("JVMTI [%s] class compile method load event sent %s.%s ", + JvmtiTrace::safe_get_thread_name(thread), + (nm->method() == NULL) ? "NULL" : nm->method()->klass_name()->as_C_string(), + (nm->method() == NULL) ? "NULL" : nm->method()->name()->as_C_string())); + + ResourceMark rm(thread); + JvmtiCompiledMethodLoadEventMark jem(thread, nm); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventCompiledMethodLoad callback = env->callbacks()->CompiledMethodLoad; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_methodID(), + jem.code_size(), jem.code_data(), jem.map_length(), + jem.map(), jem.compile_info()); + } + } + } +} + + +// post a COMPILED_METHOD_LOAD event for a given environment +void JvmtiExport::post_compiled_method_load(JvmtiEnv* env, const jmethodID method, const jint length, + const void *code_begin, const jint map_length, + const jvmtiAddrLocationMap* map) +{ + JavaThread* thread = JavaThread::current(); + EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD, + ("JVMTI [%s] method compile load event triggered (by GenerateEvents)", + JvmtiTrace::safe_get_thread_name(thread))); + if (env->is_enabled(JVMTI_EVENT_COMPILED_METHOD_LOAD)) { + + EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD, + ("JVMTI [%s] class compile method load event sent (by GenerateEvents), jmethodID=" PTR_FORMAT, + JvmtiTrace::safe_get_thread_name(thread), method)); + + JvmtiEventMark jem(thread); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventCompiledMethodLoad callback = env->callbacks()->CompiledMethodLoad; + if (callback != NULL) { + (*callback)(env->jvmti_external(), method, + length, code_begin, map_length, + map, NULL); + } + } +} + +// used at a safepoint to post a CompiledMethodUnload event +void JvmtiExport::post_compiled_method_unload_at_safepoint(jmethodID mid, const void *code_begin) { + assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint"); + + // create list lazily + if (_pending_compiled_method_unload_method_ids == NULL) { + _pending_compiled_method_unload_method_ids = new (ResourceObj::C_HEAP) GrowableArray(10,true); + _pending_compiled_method_unload_code_begins = new (ResourceObj::C_HEAP) GrowableArray(10,true); + } + _pending_compiled_method_unload_method_ids->append(mid); + _pending_compiled_method_unload_code_begins->append(code_begin); + _have_pending_compiled_method_unload_events = true; +} + +void JvmtiExport::post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) { + JavaThread* thread = JavaThread::current(); + EVT_TRIG_TRACE(JVMTI_EVENT_DYNAMIC_CODE_GENERATED, + ("JVMTI [%s] method dynamic code generated event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_DYNAMIC_CODE_GENERATED)) { + EVT_TRACE(JVMTI_EVENT_DYNAMIC_CODE_GENERATED, + ("JVMTI [%s] dynamic code generated event sent for %s", + JvmtiTrace::safe_get_thread_name(thread), name)); + JvmtiEventMark jem(thread); + JvmtiJavaThreadEventTransition jet(thread); + jint length = (jint)pointer_delta(code_end, code_begin, sizeof(char)); + jvmtiEventDynamicCodeGenerated callback = env->callbacks()->DynamicCodeGenerated; + if (callback != NULL) { + (*callback)(env->jvmti_external(), name, (void*)code_begin, length); + } + } + } +} + +void JvmtiExport::post_dynamic_code_generated(const char *name, const void *code_begin, const void *code_end) { + // In theory everyone coming thru here is in_vm but we need to be certain + // because a callee will do a vm->native transition + ThreadInVMfromUnknown __tiv; + jvmtiPhase phase = JvmtiEnv::get_phase(); + if (phase == JVMTI_PHASE_PRIMORDIAL || phase == JVMTI_PHASE_START) { + post_dynamic_code_generated_internal(name, code_begin, code_end); + return; + } + + if (have_pending_compiled_method_unload_events()) { + post_pending_compiled_method_unload_events(); + } + post_dynamic_code_generated_internal(name, code_begin, code_end); +} + + +// post a DYNAMIC_CODE_GENERATED event for a given environment +// used by GenerateEvents +void JvmtiExport::post_dynamic_code_generated(JvmtiEnv* env, const char *name, + const void *code_begin, const void *code_end) +{ + JavaThread* thread = JavaThread::current(); + EVT_TRIG_TRACE(JVMTI_EVENT_DYNAMIC_CODE_GENERATED, + ("JVMTI [%s] dynamic code generated event triggered (by GenerateEvents)", + JvmtiTrace::safe_get_thread_name(thread))); + if (env->is_enabled(JVMTI_EVENT_DYNAMIC_CODE_GENERATED)) { + EVT_TRACE(JVMTI_EVENT_DYNAMIC_CODE_GENERATED, + ("JVMTI [%s] dynamic code generated event sent for %s", + JvmtiTrace::safe_get_thread_name(thread), name)); + JvmtiEventMark jem(thread); + JvmtiJavaThreadEventTransition jet(thread); + jint length = (jint)pointer_delta(code_end, code_begin, sizeof(char)); + jvmtiEventDynamicCodeGenerated callback = env->callbacks()->DynamicCodeGenerated; + if (callback != NULL) { + (*callback)(env->jvmti_external(), name, (void*)code_begin, length); + } + } +} + +// post a DynamicCodeGenerated event while holding locks in the VM. +void JvmtiExport::post_dynamic_code_generated_while_holding_locks(const char* name, + address code_begin, address code_end) +{ + // register the stub with the current dynamic code event collector + JvmtiThreadState* state = JvmtiThreadState::state_for(JavaThread::current()); + JvmtiDynamicCodeEventCollector* collector = state->get_dynamic_code_event_collector(); + guarantee(collector != NULL, "attempt to register stub without event collector"); + collector->register_stub(name, code_begin, code_end); +} + +// Collect all the vm internally allocated objects which are visible to java world +void JvmtiExport::record_vm_internal_object_allocation(oop obj) { + Thread* thread = ThreadLocalStorage::thread(); + if (thread != NULL && thread->is_Java_thread()) { + // Can not take safepoint here. + No_Safepoint_Verifier no_sfpt; + // Can not take safepoint here so can not use state_for to get + // jvmti thread state. + JvmtiThreadState *state = ((JavaThread*)thread)->jvmti_thread_state(); + if (state != NULL ) { + // state is non NULL when VMObjectAllocEventCollector is enabled. + JvmtiVMObjectAllocEventCollector *collector; + collector = state->get_vm_object_alloc_event_collector(); + if (collector != NULL && collector->is_enabled()) { + // Don't record classes as these will be notified via the ClassLoad + // event. + if (obj->klass() != SystemDictionary::class_klass()) { + collector->record_allocation(obj); + } + } + } + } +} + +void JvmtiExport::post_garbage_collection_finish() { + Thread *thread = Thread::current(); // this event is posted from VM-Thread. + EVT_TRIG_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, + ("JVMTI [%s] garbage collection finish event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH)) { + EVT_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, + ("JVMTI [%s] garbage collection finish event sent ", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiThreadEventTransition jet(thread); + // JNIEnv is NULL here because this event is posted from VM Thread + jvmtiEventGarbageCollectionFinish callback = env->callbacks()->GarbageCollectionFinish; + if (callback != NULL) { + (*callback)(env->jvmti_external()); + } + } + } +} + +void JvmtiExport::post_garbage_collection_start() { + Thread* thread = Thread::current(); // this event is posted from vm-thread. + EVT_TRIG_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_START, + ("JVMTI [%s] garbage collection start event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_GARBAGE_COLLECTION_START)) { + EVT_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_START, + ("JVMTI [%s] garbage collection start event sent ", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiThreadEventTransition jet(thread); + // JNIEnv is NULL here because this event is posted from VM Thread + jvmtiEventGarbageCollectionStart callback = env->callbacks()->GarbageCollectionStart; + if (callback != NULL) { + (*callback)(env->jvmti_external()); + } + } + } +} + +void JvmtiExport::post_data_dump() { + Thread *thread = Thread::current(); + EVT_TRIG_TRACE(JVMTI_EVENT_DATA_DUMP_REQUEST, + ("JVMTI [%s] data dump request event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_DATA_DUMP_REQUEST)) { + EVT_TRACE(JVMTI_EVENT_DATA_DUMP_REQUEST, + ("JVMTI [%s] data dump request event sent ", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiThreadEventTransition jet(thread); + // JNIEnv is NULL here because this event is posted from VM Thread + jvmtiEventDataDumpRequest callback = env->callbacks()->DataDumpRequest; + if (callback != NULL) { + (*callback)(env->jvmti_external()); + } + } + } +} + +void JvmtiExport::post_monitor_contended_enter(JavaThread *thread, ObjectMonitor *obj_mntr) { + oop object = (oop)obj_mntr->object(); + if (!ServiceUtil::visible_oop(object)) { + // Ignore monitor contended enter for vm internal object. + return; + } + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + + HandleMark hm(thread); + Handle h(thread, object); + + EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_CONTENDED_ENTER, + ("JVMTI [%s] montior contended enter event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_MONITOR_CONTENDED_ENTER)) { + EVT_TRACE(JVMTI_EVENT_MONITOR_CONTENDED_ENTER, + ("JVMTI [%s] monitor contended enter event sent", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiMonitorEventMark jem(thread, h()); + JvmtiEnv *env = ets->get_env(); + JvmtiThreadEventTransition jet(thread); + jvmtiEventMonitorContendedEnter callback = env->callbacks()->MonitorContendedEnter; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_object()); + } + } + } +} + +void JvmtiExport::post_monitor_contended_entered(JavaThread *thread, ObjectMonitor *obj_mntr) { + oop object = (oop)obj_mntr->object(); + if (!ServiceUtil::visible_oop(object)) { + // Ignore monitor contended entered for vm internal object. + return; + } + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + + HandleMark hm(thread); + Handle h(thread, object); + + EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, + ("JVMTI [%s] montior contended entered event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_MONITOR_CONTENDED_ENTERED)) { + EVT_TRACE(JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, + ("JVMTI [%s] monitor contended enter event sent", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiMonitorEventMark jem(thread, h()); + JvmtiEnv *env = ets->get_env(); + JvmtiThreadEventTransition jet(thread); + jvmtiEventMonitorContendedEntered callback = env->callbacks()->MonitorContendedEntered; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_object()); + } + } + } +} + +void JvmtiExport::post_monitor_wait(JavaThread *thread, oop object, + jlong timeout) { + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + + HandleMark hm(thread); + Handle h(thread, object); + + EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_WAIT, + ("JVMTI [%s] montior wait event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_MONITOR_WAIT)) { + EVT_TRACE(JVMTI_EVENT_MONITOR_WAIT, + ("JVMTI [%s] monitor wait event sent ", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiMonitorEventMark jem(thread, h()); + JvmtiEnv *env = ets->get_env(); + JvmtiThreadEventTransition jet(thread); + jvmtiEventMonitorWait callback = env->callbacks()->MonitorWait; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_object(), timeout); + } + } + } +} + +void JvmtiExport::post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mntr, jboolean timed_out) { + oop object = (oop)obj_mntr->object(); + if (!ServiceUtil::visible_oop(object)) { + // Ignore monitor waited for vm internal object. + return; + } + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + + HandleMark hm(thread); + Handle h(thread, object); + + EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_WAITED, + ("JVMTI [%s] montior waited event triggered", + JvmtiTrace::safe_get_thread_name(thread))); + + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_MONITOR_WAITED)) { + EVT_TRACE(JVMTI_EVENT_MONITOR_WAITED, + ("JVMTI [%s] monitor waited event sent ", + JvmtiTrace::safe_get_thread_name(thread))); + JvmtiMonitorEventMark jem(thread, h()); + JvmtiEnv *env = ets->get_env(); + JvmtiThreadEventTransition jet(thread); + jvmtiEventMonitorWaited callback = env->callbacks()->MonitorWaited; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_object(), timed_out); + } + } + } +} + + +void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { + EVT_TRIG_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("JVMTI [%s] Trg vm object alloc triggered", + JvmtiTrace::safe_get_thread_name(thread))); + if (object == NULL) { + return; + } + HandleMark hm(thread); + Handle h(thread, object); + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_VM_OBJECT_ALLOC)) { + EVT_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("JVMTI [%s] Evt vmobject alloc sent %s", + JvmtiTrace::safe_get_thread_name(thread), + object==NULL? "NULL" : Klass::cast(java_lang_Class::as_klassOop(object))->external_name())); + + JvmtiVMObjectAllocEventMark jem(thread, h()); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventVMObjectAlloc callback = env->callbacks()->VMObjectAlloc; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_jobject(), jem.jni_class(), jem.size()); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +void JvmtiExport::cleanup_thread(JavaThread* thread) { + assert(JavaThread::current() == thread, "thread is not current"); + + + // This has to happen after the thread state is removed, which is + // why it is not in post_thread_end_event like its complement + // Maybe both these functions should be rolled into the posts? + JvmtiEventController::thread_ended(thread); +} + +void JvmtiExport::oops_do(OopClosure* f) { + JvmtiCurrentBreakpoints::oops_do(f); + JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(f); +} + +// Onload raw monitor transition. +void JvmtiExport::transition_pending_onload_raw_monitors() { + JvmtiPendingMonitors::transition_raw_monitors(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +// type for the Agent_OnAttach entry point +extern "C" { + typedef jint (JNICALL *OnAttachEntry_t)(JavaVM*, char *, void *); +} + +#ifndef SERVICES_KERNEL +jint JvmtiExport::load_agent_library(AttachOperation* op, outputStream* st) { + char ebuf[1024]; + char buffer[JVM_MAXPATHLEN]; + void* library; + jint result = JNI_ERR; + + // get agent name and options + const char* agent = op->arg(0); + const char* absParam = op->arg(1); + const char* options = op->arg(2); + + // The abs paramter should be "true" or "false" + bool is_absolute_path = (absParam != NULL) && (strcmp(absParam,"true")==0); + + + // If the path is absolute we attempt to load the library. Otherwise we try to + // load it from the standard dll directory. + + if (is_absolute_path) { + library = hpi::dll_load(agent, ebuf, sizeof ebuf); + } else { + // Try to load the agent from the standard dll directory + hpi::dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(), agent); + library = hpi::dll_load(buffer, ebuf, sizeof ebuf); + if (library == NULL) { + // not found - try local path + char ns[1] = {0}; + hpi::dll_build_name(buffer, sizeof(buffer), ns, agent); + library = hpi::dll_load(buffer, ebuf, sizeof ebuf); + } + } + + // If the library was loaded then we attempt to invoke the Agent_OnAttach + // function + if (library != NULL) { + + // Lookup the Agent_OnAttach function + OnAttachEntry_t on_attach_entry = NULL; + const char *on_attach_symbols[] = AGENT_ONATTACH_SYMBOLS; + for (uint symbol_index = 0; symbol_index < ARRAY_SIZE(on_attach_symbols); symbol_index++) { + on_attach_entry = + CAST_TO_FN_PTR(OnAttachEntry_t, hpi::dll_lookup(library, on_attach_symbols[symbol_index])); + if (on_attach_entry != NULL) break; + } + + if (on_attach_entry == NULL) { + // Agent_OnAttach missing - unload library + hpi::dll_unload(library); + } else { + // Invoke the Agent_OnAttach function + JavaThread* THREAD = JavaThread::current(); + { + extern struct JavaVM_ main_vm; + JvmtiThreadEventMark jem(THREAD); + JvmtiJavaThreadEventTransition jet(THREAD); + + result = (*on_attach_entry)(&main_vm, (char*)options, NULL); + } + + // Agent_OnAttach may have used JNI + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + } + + // If OnAttach returns JNI_OK then we add it to the list of + // agent libraries so that we can call Agent_OnUnload later. + if (result == JNI_OK) { + Arguments::add_loaded_agent(agent, (char*)options, is_absolute_path, library); + } + + // Agent_OnAttach executed so completion status is JNI_OK + st->print_cr("%d", result); + result = JNI_OK; + } + } + return result; +} +#endif // SERVICES_KERNEL + +// CMS has completed referencing processing so may need to update +// tag maps. +void JvmtiExport::cms_ref_processing_epilogue() { + if (JvmtiEnv::environments_might_exist()) { + JvmtiTagMap::cms_ref_processing_epilogue(); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////// + +// Setup current current thread for event collection. +void JvmtiEventCollector::setup_jvmti_thread_state() { + // set this event collector to be the current one. + JvmtiThreadState* state = JvmtiThreadState::state_for(JavaThread::current()); + if (is_vm_object_alloc_event()) { + _prev = state->get_vm_object_alloc_event_collector(); + state->set_vm_object_alloc_event_collector((JvmtiVMObjectAllocEventCollector *)this); + } else if (is_dynamic_code_event()) { + _prev = state->get_dynamic_code_event_collector(); + state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)this); + } +} + +// Unset current event collection in this thread and reset it with previous +// collector. +void JvmtiEventCollector::unset_jvmti_thread_state() { + JvmtiThreadState* state = JavaThread::current()->jvmti_thread_state(); + if (state != NULL) { + // restore the previous event collector (if any) + if (is_vm_object_alloc_event()) { + if (state->get_vm_object_alloc_event_collector() == this) { + state->set_vm_object_alloc_event_collector((JvmtiVMObjectAllocEventCollector *)_prev); + } else { + // this thread's jvmti state was created during the scope of + // the event collector. + } + } else { + if (is_dynamic_code_event()) { + if (state->get_dynamic_code_event_collector() == this) { + state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)_prev); + } else { + // this thread's jvmti state was created during the scope of + // the event collector. + } + } + } + } +} + +// create the dynamic code event collector +JvmtiDynamicCodeEventCollector::JvmtiDynamicCodeEventCollector() : _code_blobs(NULL) { + if (JvmtiExport::should_post_dynamic_code_generated()) { + setup_jvmti_thread_state(); + } +} + +// iterate over any code blob descriptors collected and post a +// DYNAMIC_CODE_GENERATED event to the profiler. +JvmtiDynamicCodeEventCollector::~JvmtiDynamicCodeEventCollector() { + assert(!JavaThread::current()->owns_locks(), "all locks must be released to post deferred events"); + // iterate over any code blob descriptors that we collected + if (_code_blobs != NULL) { + for (int i=0; i<_code_blobs->length(); i++) { + JvmtiCodeBlobDesc* blob = _code_blobs->at(i); + JvmtiExport::post_dynamic_code_generated(blob->name(), blob->code_begin(), blob->code_end()); + FreeHeap(blob); + } + delete _code_blobs; + } + unset_jvmti_thread_state(); +} + +// register a stub +void JvmtiDynamicCodeEventCollector::register_stub(const char* name, address start, address end) { + if (_code_blobs == NULL) { + _code_blobs = new (ResourceObj::C_HEAP) GrowableArray(1,true); + } + _code_blobs->append(new JvmtiCodeBlobDesc(name, start, end)); +} + +// Setup current thread to record vm allocated objects. +JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() : _allocated(NULL) { + if (JvmtiExport::should_post_vm_object_alloc()) { + _enable = true; + setup_jvmti_thread_state(); + } else { + _enable = false; + } +} + +// Post vm_object_alloc event for vm allocated objects visible to java +// world. +JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() { + if (_allocated != NULL) { + set_enabled(false); + for (int i = 0; i < _allocated->length(); i++) { + oop obj = _allocated->at(i); + if (ServiceUtil::visible_oop(obj)) { + JvmtiExport::post_vm_object_alloc(JavaThread::current(), obj); + } + } + delete _allocated; + } + unset_jvmti_thread_state(); +} + +void JvmtiVMObjectAllocEventCollector::record_allocation(oop obj) { + assert(is_enabled(), "VM object alloc event collector is not enabled"); + if (_allocated == NULL) { + _allocated = new (ResourceObj::C_HEAP) GrowableArray(1, true); + } + _allocated->push(obj); +} + +// GC support. +void JvmtiVMObjectAllocEventCollector::oops_do(OopClosure* f) { + if (_allocated != NULL) { + for(int i=_allocated->length() - 1; i >= 0; i--) { + if (_allocated->at(i) != NULL) { + f->do_oop(_allocated->adr_at(i)); + } + } + } +} + +void JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) { + // no-op if jvmti not enabled + if (!JvmtiEnv::environments_might_exist()) { + return; + } + + // Runs at safepoint. So no need to acquire Threads_lock. + for (JavaThread *jthr = Threads::first(); jthr != NULL; jthr = jthr->next()) { + JvmtiThreadState *state = jthr->jvmti_thread_state(); + if (state != NULL) { + JvmtiVMObjectAllocEventCollector *collector; + collector = state->get_vm_object_alloc_event_collector(); + while (collector != NULL) { + collector->oops_do(f); + collector = (JvmtiVMObjectAllocEventCollector *)collector->get_prev(); + } + } + } +} + + +// Disable collection of VMObjectAlloc events +NoJvmtiVMObjectAllocMark::NoJvmtiVMObjectAllocMark() : _collector(NULL) { + // a no-op if VMObjectAlloc event is not enabled + if (!JvmtiExport::should_post_vm_object_alloc()) { + return; + } + Thread* thread = ThreadLocalStorage::thread(); + if (thread != NULL && thread->is_Java_thread()) { + JavaThread* current_thread = (JavaThread*)thread; + JvmtiThreadState *state = current_thread->jvmti_thread_state(); + if (state != NULL) { + JvmtiVMObjectAllocEventCollector *collector; + collector = state->get_vm_object_alloc_event_collector(); + if (collector != NULL && collector->is_enabled()) { + _collector = collector; + _collector->set_enabled(false); + } + } + } +} + +// Re-Enable collection of VMObjectAlloc events (if previously enabled) +NoJvmtiVMObjectAllocMark::~NoJvmtiVMObjectAllocMark() { + if (was_enabled()) { + _collector->set_enabled(true); + } +}; + +JvmtiGCMarker::JvmtiGCMarker(bool full) : _full(full), _invocation_count(0) { + assert(Thread::current()->is_VM_thread(), "wrong thread"); + + // if there aren't any JVMTI environments then nothing to do + if (!JvmtiEnv::environments_might_exist()) { + return; + } + + // GarbageCollectionStart event posted from VM thread - okay because + // JVMTI is clear that the "world is stopped" and callback shouldn't + // try to call into the VM. + if (JvmtiExport::should_post_garbage_collection_start()) { + JvmtiExport::post_garbage_collection_start(); + } + + // if "full" is false it probably means this is a scavenge of the young + // generation. However it could turn out that a "full" GC is required + // so we record the number of collections so that it can be checked in + // the destructor. + if (!_full) { + if (Universe::heap()->kind() == CollectedHeap::GenCollectedHeap) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->n_gens() == 2, "configuration not recognized"); + _invocation_count = (unsigned int)gch->get_gen(1)->stat_record()->invocations; + } else { +#ifndef SERIALGC + assert(Universe::heap()->kind() == CollectedHeap::ParallelScavengeHeap, "checking"); + _invocation_count = PSMarkSweep::total_invocations(); +#else // SERIALGC + fatal("SerialGC only supported in this configuration."); +#endif // SERIALGC + } + } + + // Do clean up tasks that need to be done at a safepoint + JvmtiEnvBase::check_for_periodic_clean_up(); +} + +JvmtiGCMarker::~JvmtiGCMarker() { + // if there aren't any JVMTI environments then nothing to do + if (!JvmtiEnv::environments_might_exist()) { + return; + } + + // JVMTI notify gc finish + if (JvmtiExport::should_post_garbage_collection_finish()) { + JvmtiExport::post_garbage_collection_finish(); + } + + // we might have initially started out doing a scavenge of the young + // generation but could have ended up doing a "full" GC - check the + // GC count to see. + if (!_full) { + if (Universe::heap()->kind() == CollectedHeap::GenCollectedHeap) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + if (_invocation_count != (unsigned int)gch->get_gen(1)->stat_record()->invocations) { + _full = true; + } + } else { +#ifndef SERIALGC + if (_invocation_count != PSMarkSweep::total_invocations()) { + _full = true; + } +#else // SERIALGC + fatal("SerialGC only supported in this configuration."); +#endif // SERIALGC + } + } + + // Full collection probably means the perm generation has been GC'ed + // so we clear the breakpoint cache. + if (_full) { + JvmtiCurrentBreakpoints::gc_epilogue(); + } + + // Notify heap/object tagging support + JvmtiTagMap::gc_epilogue(_full); +} +#endif // JVMTI_KERNEL diff --git a/hotspot/src/share/vm/prims/jvmtiExport.hpp b/hotspot/src/share/vm/prims/jvmtiExport.hpp new file mode 100644 index 00000000000..54a9416f425 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiExport.hpp @@ -0,0 +1,553 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _JAVA_JVMTIEXPORT_H_ +#define _JAVA_JVMTIEXPORT_H_ + +// Forward declarations + +class JvmtiEventControllerPrivate; +class JvmtiManageCapabilities; +class JvmtiEnv; +class JvmtiThreadState; +class AttachOperation; + +#ifndef JVMTI_KERNEL +#define JVMTI_SUPPORT_FLAG(key) \ + private: \ + static bool _##key; \ + public: \ + inline static void set_##key(bool on) { _##key = (on != 0); } \ + inline static bool key() { return _##key; } +#else // JVMTI_KERNEL +#define JVMTI_SUPPORT_FLAG(key) \ + private: \ + const static bool _##key = false; \ + public: \ + inline static void set_##key(bool on) { report_unsupported(on); } \ + inline static bool key() { return _##key; } +#endif // JVMTI_KERNEL + + +// This class contains the JVMTI interface for the rest of hotspot. +// +class JvmtiExport : public AllStatic { + private: + static int _field_access_count; + static int _field_modification_count; + + static bool _can_access_local_variables; + static bool _can_examine_or_deopt_anywhere; + static bool _can_hotswap_or_post_breakpoint; + static bool _can_modify_any_class; + static bool _can_walk_any_space; + + JVMTI_SUPPORT_FLAG(can_get_source_debug_extension) + JVMTI_SUPPORT_FLAG(can_maintain_original_method_order) + JVMTI_SUPPORT_FLAG(can_post_interpreter_events) + JVMTI_SUPPORT_FLAG(can_post_exceptions) + JVMTI_SUPPORT_FLAG(can_post_breakpoint) + JVMTI_SUPPORT_FLAG(can_post_field_access) + JVMTI_SUPPORT_FLAG(can_post_field_modification) + JVMTI_SUPPORT_FLAG(can_post_method_entry) + JVMTI_SUPPORT_FLAG(can_post_method_exit) + JVMTI_SUPPORT_FLAG(can_pop_frame) + JVMTI_SUPPORT_FLAG(can_force_early_return) + + friend class JvmtiEventControllerPrivate; // should only modify these flags + JVMTI_SUPPORT_FLAG(should_post_single_step) + JVMTI_SUPPORT_FLAG(should_post_field_access) + JVMTI_SUPPORT_FLAG(should_post_field_modification) + JVMTI_SUPPORT_FLAG(should_post_class_load) + JVMTI_SUPPORT_FLAG(should_post_class_prepare) + JVMTI_SUPPORT_FLAG(should_post_class_unload) + JVMTI_SUPPORT_FLAG(should_post_native_method_bind) + JVMTI_SUPPORT_FLAG(should_post_compiled_method_load) + JVMTI_SUPPORT_FLAG(should_post_compiled_method_unload) + JVMTI_SUPPORT_FLAG(should_post_dynamic_code_generated) + JVMTI_SUPPORT_FLAG(should_post_monitor_contended_enter) + JVMTI_SUPPORT_FLAG(should_post_monitor_contended_entered) + JVMTI_SUPPORT_FLAG(should_post_monitor_wait) + JVMTI_SUPPORT_FLAG(should_post_monitor_waited) + JVMTI_SUPPORT_FLAG(should_post_data_dump) + JVMTI_SUPPORT_FLAG(should_post_garbage_collection_start) + JVMTI_SUPPORT_FLAG(should_post_garbage_collection_finish) + + // ------ the below maybe don't have to be (but are for now) + // fixed conditions here ------------ + // any events can be enabled + JVMTI_SUPPORT_FLAG(should_post_thread_life) + JVMTI_SUPPORT_FLAG(should_post_object_free) + JVMTI_SUPPORT_FLAG(should_post_resource_exhausted) + + // we are holding objects on the heap - need to talk to GC - e.g. + // breakpoint info + JVMTI_SUPPORT_FLAG(should_clean_up_heap_objects) + JVMTI_SUPPORT_FLAG(should_post_vm_object_alloc) + + // If flag cannot be implemented, give an error if on=true + static void report_unsupported(bool on); + + // these should only be called by the friend class + friend class JvmtiManageCapabilities; + inline static void set_can_examine_or_deopt_anywhere(bool on) { _can_examine_or_deopt_anywhere = (on != 0); } + inline static void set_can_modify_any_class(bool on) { _can_modify_any_class = (on != 0); } + inline static void set_can_access_local_variables(bool on) { _can_access_local_variables = (on != 0); } + inline static void set_can_hotswap_or_post_breakpoint(bool on) { _can_hotswap_or_post_breakpoint = (on != 0); } + inline static void set_can_walk_any_space(bool on) { _can_walk_any_space = (on != 0); } + + enum { + JVMTI_VERSION_MASK = 0x70000000, + JVMTI_VERSION_VALUE = 0x30000000, + JVMDI_VERSION_VALUE = 0x20000000 + }; + + static void post_field_modification(JavaThread *thread, methodOop method, address location, + KlassHandle field_klass, Handle object, jfieldID field, + char sig_type, jvalue *value); + + + private: + // CompiledMethodUnload events are reported from the VM thread so they + // are collected in lists (of jmethodID/addresses) and the events are posted later + // from threads posting CompieldMethodLoad or DynamicCodeGenerated events. + static bool _have_pending_compiled_method_unload_events; + static GrowableArray* _pending_compiled_method_unload_method_ids; + static GrowableArray* _pending_compiled_method_unload_code_begins; + static JavaThread* _current_poster; + + // tests if there are CompiledMethodUnload events pending + inline static bool have_pending_compiled_method_unload_events() { + return _have_pending_compiled_method_unload_events; + } + + // posts any pending CompiledMethodUnload events. + static void post_pending_compiled_method_unload_events(); + + // posts a DynamicCodeGenerated event (internal/private implementation). + // The public post_dynamic_code_generated* functions make use of the + // internal implementation. + static void post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) KERNEL_RETURN; + + + // GenerateEvents support to allow posting of CompiledMethodLoad and + // DynamicCodeGenerated events for a given environment. + friend class JvmtiCodeBlobEvents; + + static void post_compiled_method_load(JvmtiEnv* env, const jmethodID method, const jint length, + const void *code_begin, const jint map_length, + const jvmtiAddrLocationMap* map) KERNEL_RETURN; + static void post_dynamic_code_generated(JvmtiEnv* env, const char *name, const void *code_begin, + const void *code_end) KERNEL_RETURN; + + // The RedefineClasses() API breaks some invariants in the "regular" + // system. For example, there are sanity checks when GC'ing nmethods + // that require the containing class to be unloading. However, when a + // method is redefined, the old method and nmethod can become GC'able + // without the containing class unloading. The state of becoming + // GC'able can be asynchronous to the RedefineClasses() call since + // the old method may still be running and cannot be GC'ed until + // after all old invocations have finished. Additionally, a method + // that has not been redefined may have an nmethod that depends on + // the redefined method. The dependent nmethod will get deopted in + // this case and may also be GC'able without the containing class + // being unloaded. + // + // This flag indicates whether RedefineClasses() has ever redefined + // one or more classes during the lifetime of the VM. The flag should + // only be set by the friend class and can be queried by other sub + // systems as needed to relax invariant checks. + static bool _has_redefined_a_class; + friend class VM_RedefineClasses; + inline static void set_has_redefined_a_class() { + _has_redefined_a_class = true; + } + + // Flag to indicate if the compiler has recorded all dependencies. When the + // can_redefine_classes capability is enabled in the OnLoad phase then the compiler + // records all dependencies from startup. However if the capability is first + // enabled some time later then the dependencies recorded by the compiler + // are incomplete. This flag is used by RedefineClasses to know if the + // dependency information is complete or not. + static bool _all_dependencies_are_recorded; + + public: + inline static bool has_redefined_a_class() { + return _has_redefined_a_class; + } + + inline static bool all_dependencies_are_recorded() { + return _all_dependencies_are_recorded; + } + + inline static void set_all_dependencies_are_recorded(bool on) { + _all_dependencies_are_recorded = (on != 0); + } + + + // let JVMTI know that the JVM_OnLoad code is running + static void enter_onload_phase(); + + // let JVMTI know that the VM isn't up yet (and JVM_OnLoad code isn't running) + static void enter_primordial_phase(); + + // let JVMTI know that the VM isn't up yet but JNI is live + static void enter_start_phase(); + + // let JVMTI know that the VM is fully up and running now + static void enter_live_phase(); + + // ------ can_* conditions (below) are set at OnLoad and never changed ------------ + inline static bool can_examine_or_deopt_anywhere() { return _can_examine_or_deopt_anywhere; } + inline static bool can_modify_any_class() { return _can_modify_any_class; } + inline static bool can_access_local_variables() { return _can_access_local_variables; } + inline static bool can_hotswap_or_post_breakpoint() { return _can_hotswap_or_post_breakpoint; } + inline static bool can_walk_any_space() { return _can_walk_any_space; } + + // field access management + static address get_field_access_count_addr(); + + // field modification management + static address get_field_modification_count_addr(); + + // ----------------- + + static bool is_jvmti_version(jint version) { return (version & JVMTI_VERSION_MASK) == JVMTI_VERSION_VALUE; } + static bool is_jvmdi_version(jint version) { return (version & JVMTI_VERSION_MASK) == JVMDI_VERSION_VALUE; } + static jint get_jvmti_interface(JavaVM *jvm, void **penv, jint version); + + // single stepping management methods + static void at_single_stepping_point(JavaThread *thread, methodOop method, address location) KERNEL_RETURN; + static void expose_single_stepping(JavaThread *thread) KERNEL_RETURN; + static bool hide_single_stepping(JavaThread *thread) KERNEL_RETURN_(return false;); + + // Methods that notify the debugger that something interesting has happened in the VM. + static void post_vm_start (); + static void post_vm_initialized (); + static void post_vm_death (); + + static void post_single_step (JavaThread *thread, methodOop method, address location) KERNEL_RETURN; + static void post_raw_breakpoint (JavaThread *thread, methodOop method, address location) KERNEL_RETURN; + + static void post_exception_throw (JavaThread *thread, methodOop method, address location, oop exception) KERNEL_RETURN; + static void notice_unwind_due_to_exception (JavaThread *thread, methodOop method, address location, oop exception, bool in_handler_frame) KERNEL_RETURN; + + static oop jni_GetField_probe (JavaThread *thread, jobject jobj, + oop obj, klassOop klass, jfieldID fieldID, bool is_static) + KERNEL_RETURN_(return NULL;); + static oop jni_GetField_probe_nh (JavaThread *thread, jobject jobj, + oop obj, klassOop klass, jfieldID fieldID, bool is_static) + KERNEL_RETURN_(return NULL;); + static void post_field_access_by_jni (JavaThread *thread, oop obj, + klassOop klass, jfieldID fieldID, bool is_static) KERNEL_RETURN; + static void post_field_access (JavaThread *thread, methodOop method, + address location, KlassHandle field_klass, Handle object, jfieldID field) KERNEL_RETURN; + static oop jni_SetField_probe (JavaThread *thread, jobject jobj, + oop obj, klassOop klass, jfieldID fieldID, bool is_static, char sig_type, + jvalue *value) KERNEL_RETURN_(return NULL;); + static oop jni_SetField_probe_nh (JavaThread *thread, jobject jobj, + oop obj, klassOop klass, jfieldID fieldID, bool is_static, char sig_type, + jvalue *value) KERNEL_RETURN_(return NULL;); + static void post_field_modification_by_jni(JavaThread *thread, oop obj, + klassOop klass, jfieldID fieldID, bool is_static, char sig_type, + jvalue *value); + static void post_raw_field_modification(JavaThread *thread, methodOop method, + address location, KlassHandle field_klass, Handle object, jfieldID field, + char sig_type, jvalue *value) KERNEL_RETURN; + + static void post_method_entry (JavaThread *thread, methodOop method, frame current_frame) KERNEL_RETURN; + static void post_method_exit (JavaThread *thread, methodOop method, frame current_frame) KERNEL_RETURN; + + static void post_class_load (JavaThread *thread, klassOop klass) KERNEL_RETURN; + static void post_class_unload (klassOop klass) KERNEL_RETURN; + static void post_class_prepare (JavaThread *thread, klassOop klass) KERNEL_RETURN; + + static void post_thread_start (JavaThread *thread) KERNEL_RETURN; + static void post_thread_end (JavaThread *thread) KERNEL_RETURN; + + // Support for java.lang.instrument agent loading. + static bool _should_post_class_file_load_hook; + inline static void set_should_post_class_file_load_hook(bool on) { _should_post_class_file_load_hook = on; } + inline static bool should_post_class_file_load_hook() { return _should_post_class_file_load_hook; } + static void post_class_file_load_hook(symbolHandle h_name, Handle class_loader, + Handle h_protection_domain, + unsigned char **data_ptr, unsigned char **end_ptr, + unsigned char **cached_data_ptr, + jint *cached_length_ptr); + static void post_native_method_bind(methodOop method, address* function_ptr) KERNEL_RETURN; + static void post_compiled_method_load(nmethod *nm) KERNEL_RETURN; + static void post_dynamic_code_generated(const char *name, const void *code_begin, const void *code_end) KERNEL_RETURN; + + // used at a safepoint to post a CompiledMethodUnload event + static void post_compiled_method_unload_at_safepoint(jmethodID mid, const void *code_begin) KERNEL_RETURN; + + // similiar to post_dynamic_code_generated except that it can be used to + // post a DynamicCodeGenerated event while holding locks in the VM. Any event + // posted using this function is recorded by the enclosing event collector + // -- JvmtiDynamicCodeEventCollector. + static void post_dynamic_code_generated_while_holding_locks(const char* name, address code_begin, address code_end) KERNEL_RETURN; + + static void post_garbage_collection_finish() KERNEL_RETURN; + static void post_garbage_collection_start() KERNEL_RETURN; + static void post_data_dump() KERNEL_RETURN; + static void post_monitor_contended_enter(JavaThread *thread, ObjectMonitor *obj_mntr) KERNEL_RETURN; + static void post_monitor_contended_entered(JavaThread *thread, ObjectMonitor *obj_mntr) KERNEL_RETURN; + static void post_monitor_wait(JavaThread *thread, oop obj, jlong timeout) KERNEL_RETURN; + static void post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mntr, jboolean timed_out) KERNEL_RETURN; + static void post_object_free(JvmtiEnv* env, jlong tag) KERNEL_RETURN; + static void post_resource_exhausted(jint resource_exhausted_flags, const char* detail) KERNEL_RETURN; + static void record_vm_internal_object_allocation(oop object) KERNEL_RETURN; + // Post objects collected by vm_object_alloc_event_collector. + static void post_vm_object_alloc(JavaThread *thread, oop object) KERNEL_RETURN; + // Collects vm internal objects for later event posting. + inline static void vm_object_alloc_event_collector(oop object) { + if (should_post_vm_object_alloc()) { + record_vm_internal_object_allocation(object); + } + } + + static void cleanup_thread (JavaThread* thread) KERNEL_RETURN; + + static void oops_do(OopClosure* f) KERNEL_RETURN; + + static void transition_pending_onload_raw_monitors() KERNEL_RETURN; + +#ifndef SERVICES_KERNEL + // attach support + static jint load_agent_library(AttachOperation* op, outputStream* out); +#endif // SERVICES_KERNEL + + // SetNativeMethodPrefix support + static char** get_all_native_method_prefixes(int* count_ptr); + + // call after CMS has completed referencing processing + static void cms_ref_processing_epilogue() KERNEL_RETURN; +}; + +// Support class used by JvmtiDynamicCodeEventCollector and others. It +// describes a single code blob by name and address range. +class JvmtiCodeBlobDesc : public CHeapObj { + private: + char _name[64]; + address _code_begin; + address _code_end; + + public: + JvmtiCodeBlobDesc(const char *name, address code_begin, address code_end) { + assert(name != NULL, "all code blobs must be named"); + strncpy(_name, name, sizeof(_name)); + _name[sizeof(_name)-1] = '\0'; + _code_begin = code_begin; + _code_end = code_end; + } + char* name() { return _name; } + address code_begin() { return _code_begin; } + address code_end() { return _code_end; } +}; + +// JvmtiEventCollector is a helper class to setup thread for +// event collection. +class JvmtiEventCollector : public StackObj { + private: + JvmtiEventCollector* _prev; // Save previous one to support nested event collector. + + public: + void setup_jvmti_thread_state(); // Set this collector in current thread. + void unset_jvmti_thread_state(); // Reset previous collector in current thread. + virtual bool is_dynamic_code_event() { return false; } + virtual bool is_vm_object_alloc_event(){ return false; } + JvmtiEventCollector *get_prev() { return _prev; } +}; + +// A JvmtiDynamicCodeEventCollector is a helper class for the JvmtiExport +// interface. It collects "dynamic code generated" events that are posted +// while holding locks. When the event collector goes out of scope the +// events will be posted. +// +// Usage :- +// +// { +// JvmtiDynamicCodeEventCollector event_collector; +// : +// { MutexLocker ml(...) +// : +// JvmtiExport::post_dynamic_code_generated_while_holding_locks(...) +// } +// // event collector goes out of scope => post events to profiler. +// } + +class JvmtiDynamicCodeEventCollector : public JvmtiEventCollector { + private: + GrowableArray* _code_blobs; // collected code blob events + + friend class JvmtiExport; + void register_stub(const char* name, address start, address end); + + public: + JvmtiDynamicCodeEventCollector() KERNEL_RETURN; + ~JvmtiDynamicCodeEventCollector() KERNEL_RETURN; + bool is_dynamic_code_event() { return true; } + +}; + +// Used to record vm internally allocated object oops and post +// vm object alloc event for objects visible to java world. +// Constructor enables JvmtiThreadState flag and all vm allocated +// objects are recorded in a growable array. When destructor is +// called the vm object alloc event is posted for each objects +// visible to java world. +// See jvm.cpp file for its usage. +// +class JvmtiVMObjectAllocEventCollector : public JvmtiEventCollector { + private: + GrowableArray* _allocated; // field to record vm internally allocated object oop. + bool _enable; // This flag is enabled in constructor and disabled + // in destructor before posting event. To avoid + // collection of objects allocated while running java code inside + // agent post_vm_object_alloc() event handler. + + //GC support + void oops_do(OopClosure* f); + + friend class JvmtiExport; + // Record vm allocated object oop. + inline void record_allocation(oop obj); + + //GC support + static void oops_do_for_all_threads(OopClosure* f); + + public: + JvmtiVMObjectAllocEventCollector() KERNEL_RETURN; + ~JvmtiVMObjectAllocEventCollector() KERNEL_RETURN; + bool is_vm_object_alloc_event() { return true; } + + bool is_enabled() { return _enable; } + void set_enabled(bool on) { _enable = on; } +}; + + + +// Marker class to disable the posting of VMObjectAlloc events +// within its scope. +// +// Usage :- +// +// { +// NoJvmtiVMObjectAllocMark njm; +// : +// // VMObjAlloc event will not be posted +// JvmtiExport::vm_object_alloc_event_collector(obj); +// : +// } + +class NoJvmtiVMObjectAllocMark : public StackObj { + private: + // enclosing collector if enabled, NULL otherwise + JvmtiVMObjectAllocEventCollector *_collector; + + bool was_enabled() { return _collector != NULL; } + + public: + NoJvmtiVMObjectAllocMark() KERNEL_RETURN; + ~NoJvmtiVMObjectAllocMark() KERNEL_RETURN; +}; + + +// Base class for reporting GC events to JVMTI. +class JvmtiGCMarker : public StackObj { + private: + bool _full; // marks a "full" GC + unsigned int _invocation_count; // GC invocation count + protected: + JvmtiGCMarker(bool full) KERNEL_RETURN; // protected + ~JvmtiGCMarker() KERNEL_RETURN; // protected +}; + + +// Support class used to report GC events to JVMTI. The class is stack +// allocated and should be placed in the doit() implementation of all +// vm operations that do a stop-the-world GC for failed allocation. +// +// Usage :- +// +// void VM_GenCollectForAllocation::doit() { +// JvmtiGCForAllocationMarker jgcm; +// : +// } +// +// If jvmti is not enabled the constructor and destructor is essentially +// a no-op (no overhead). +// +class JvmtiGCForAllocationMarker : public JvmtiGCMarker { + public: + JvmtiGCForAllocationMarker() : JvmtiGCMarker(false) { + } +}; + +// Support class used to report GC events to JVMTI. The class is stack +// allocated and should be placed in the doit() implementation of all +// vm operations that do a "full" stop-the-world GC. This class differs +// from JvmtiGCForAllocationMarker in that this class assumes that a +// "full" GC will happen. +// +// Usage :- +// +// void VM_GenCollectFull::doit() { +// JvmtiGCFullMarker jgcm; +// : +// } +// +class JvmtiGCFullMarker : public JvmtiGCMarker { + public: + JvmtiGCFullMarker() : JvmtiGCMarker(true) { + } +}; + + +// JvmtiHideSingleStepping is a helper class for hiding +// internal single step events. +class JvmtiHideSingleStepping : public StackObj { + private: + bool _single_step_hidden; + JavaThread * _thread; + + public: + JvmtiHideSingleStepping(JavaThread * thread) { + assert(thread != NULL, "sanity check"); + + _single_step_hidden = false; + _thread = thread; + if (JvmtiExport::should_post_single_step()) { + _single_step_hidden = JvmtiExport::hide_single_stepping(_thread); + } + } + + ~JvmtiHideSingleStepping() { + if (_single_step_hidden) { + JvmtiExport::expose_single_stepping(_thread); + } + } +}; + +#endif /* _JAVA_JVMTIEXPORT_H_ */ diff --git a/hotspot/src/share/vm/prims/jvmtiExtensions.cpp b/hotspot/src/share/vm/prims/jvmtiExtensions.cpp new file mode 100644 index 00000000000..7c75d4c15e6 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiExtensions.cpp @@ -0,0 +1,277 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiExtensions.cpp.incl" + +// the list of extension functions +GrowableArray* JvmtiExtensions::_ext_functions; + +// the list of extension events +GrowableArray* JvmtiExtensions::_ext_events; + + +// extension function +static jvmtiError JNICALL IsClassUnloadingEnabled(const jvmtiEnv* env, jboolean* enabled, ...) { + if (enabled == NULL) { + return JVMTI_ERROR_NULL_POINTER; + } + *enabled = (jboolean)ClassUnloading; + return JVMTI_ERROR_NONE; +} + +// register extension functions and events. In this implementation we +// have a single extension function (to prove the API) that tests if class +// unloading is enabled or disabled. We also have a single extension event +// EXT_EVENT_CLASS_UNLOAD which is used to provide the JVMDI_EVENT_CLASS_UNLOAD +// event. The function and the event are registered here. +// +void JvmtiExtensions::register_extensions() { + _ext_functions = new (ResourceObj::C_HEAP) GrowableArray(1,true); + _ext_events = new (ResourceObj::C_HEAP) GrowableArray(1,true); + + // register our extension function + static jvmtiParamInfo func_params[] = { + { (char*)"IsClassUnloadingEnabled", JVMTI_KIND_OUT, JVMTI_TYPE_JBOOLEAN, JNI_FALSE } + }; + static jvmtiExtensionFunctionInfo ext_func = { + (jvmtiExtensionFunction)IsClassUnloadingEnabled, + (char*)"com.sun.hotspot.functions.IsClassUnloadingEnabled", + (char*)"Tell if class unloading is enabled (-noclassgc)", + sizeof(func_params)/sizeof(func_params[0]), + func_params, + 0, // no non-universal errors + NULL + }; + _ext_functions->append(&ext_func); + + // register our extension event + + static jvmtiParamInfo event_params[] = { + { (char*)"JNI Environment", JVMTI_KIND_IN, JVMTI_TYPE_JNIENV, JNI_FALSE }, + { (char*)"Thread", JVMTI_KIND_IN, JVMTI_TYPE_JTHREAD, JNI_FALSE }, + { (char*)"Class", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, JNI_FALSE } + }; + static jvmtiExtensionEventInfo ext_event = { + EXT_EVENT_CLASS_UNLOAD, + (char*)"com.sun.hotspot.events.ClassUnload", + (char*)"CLASS_UNLOAD event", + sizeof(event_params)/sizeof(event_params[0]), + event_params + }; + _ext_events->append(&ext_event); +} + + +// return the list of extension functions + +jvmtiError JvmtiExtensions::get_functions(JvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions) +{ + guarantee(_ext_functions != NULL, "registration not done"); + + ResourceTracker rt(env); + + jvmtiExtensionFunctionInfo* ext_funcs; + jvmtiError err = rt.allocate(_ext_functions->length() * + sizeof(jvmtiExtensionFunctionInfo), + (unsigned char**)&ext_funcs); + if (err != JVMTI_ERROR_NONE) { + return err; + } + + for (int i=0; i<_ext_functions->length(); i++ ) { + ext_funcs[i].func = _ext_functions->at(i)->func; + + char *id = _ext_functions->at(i)->id; + err = rt.allocate(strlen(id)+1, (unsigned char**)&(ext_funcs[i].id)); + if (err != JVMTI_ERROR_NONE) { + return err; + } + strcpy(ext_funcs[i].id, id); + + char *desc = _ext_functions->at(i)->short_description; + err = rt.allocate(strlen(desc)+1, + (unsigned char**)&(ext_funcs[i].short_description)); + if (err != JVMTI_ERROR_NONE) { + return err; + } + strcpy(ext_funcs[i].short_description, desc); + + // params + + jint param_count = _ext_functions->at(i)->param_count; + + ext_funcs[i].param_count = param_count; + if (param_count == 0) { + ext_funcs[i].params = NULL; + } else { + err = rt.allocate(param_count*sizeof(jvmtiParamInfo), + (unsigned char**)&(ext_funcs[i].params)); + if (err != JVMTI_ERROR_NONE) { + return err; + } + jvmtiParamInfo* src_params = _ext_functions->at(i)->params; + jvmtiParamInfo* dst_params = ext_funcs[i].params; + + for (int j=0; jat(i)->error_count; + ext_funcs[i].error_count = error_count; + if (error_count == 0) { + ext_funcs[i].errors = NULL; + } else { + err = rt.allocate(error_count*sizeof(jvmtiError), + (unsigned char**)&(ext_funcs[i].errors)); + if (err != JVMTI_ERROR_NONE) { + return err; + } + memcpy(ext_funcs[i].errors, _ext_functions->at(i)->errors, + error_count*sizeof(jvmtiError)); + } + } + + *extension_count_ptr = _ext_functions->length(); + *extensions = ext_funcs; + return JVMTI_ERROR_NONE; +} + + +// return the list of extension events + +jvmtiError JvmtiExtensions::get_events(JvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions) +{ + guarantee(_ext_events != NULL, "registration not done"); + + ResourceTracker rt(env); + + jvmtiExtensionEventInfo* ext_events; + jvmtiError err = rt.allocate(_ext_events->length() * sizeof(jvmtiExtensionEventInfo), + (unsigned char**)&ext_events); + if (err != JVMTI_ERROR_NONE) { + return err; + } + + for (int i=0; i<_ext_events->length(); i++ ) { + ext_events[i].extension_event_index = _ext_events->at(i)->extension_event_index; + + char *id = _ext_events->at(i)->id; + err = rt.allocate(strlen(id)+1, (unsigned char**)&(ext_events[i].id)); + if (err != JVMTI_ERROR_NONE) { + return err; + } + strcpy(ext_events[i].id, id); + + char *desc = _ext_events->at(i)->short_description; + err = rt.allocate(strlen(desc)+1, + (unsigned char**)&(ext_events[i].short_description)); + if (err != JVMTI_ERROR_NONE) { + return err; + } + strcpy(ext_events[i].short_description, desc); + + // params + + jint param_count = _ext_events->at(i)->param_count; + + ext_events[i].param_count = param_count; + if (param_count == 0) { + ext_events[i].params = NULL; + } else { + err = rt.allocate(param_count*sizeof(jvmtiParamInfo), + (unsigned char**)&(ext_events[i].params)); + if (err != JVMTI_ERROR_NONE) { + return err; + } + jvmtiParamInfo* src_params = _ext_events->at(i)->params; + jvmtiParamInfo* dst_params = ext_events[i].params; + + for (int j=0; jlength(); + *extensions = ext_events; + return JVMTI_ERROR_NONE; +} + +// set callback for an extension event and enable/disable it. + +jvmtiError JvmtiExtensions::set_event_callback(JvmtiEnv* env, + jint extension_event_index, + jvmtiExtensionEvent callback) +{ + guarantee(_ext_events != NULL, "registration not done"); + + jvmtiExtensionEventInfo* event = NULL; + + // if there are extension events registered then validate that the + // extension_event_index matches one of the registered events. + if (_ext_events != NULL) { + for (int i=0; i<_ext_events->length(); i++ ) { + if (_ext_events->at(i)->extension_event_index == extension_event_index) { + event = _ext_events->at(i); + break; + } + } + } + + // invalid event index + if (event == NULL) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + + JvmtiEventController::set_extension_event_callback(env, extension_event_index, + callback); + + return JVMTI_ERROR_NONE; +} diff --git a/hotspot/src/share/vm/prims/jvmtiExtensions.hpp b/hotspot/src/share/vm/prims/jvmtiExtensions.hpp new file mode 100644 index 00000000000..e819633f4f8 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiExtensions.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _JVMTI_EXTENSIONS_H_ +#define _JVMTI_EXTENSIONS_H_ + + +// JvmtiExtensions +// +// Maintains the list of extension functions and events in this JVMTI +// implementation. The list of functions and events can be obtained by +// the profiler using the JVMTI GetExtensionFunctions and +// GetExtensionEvents functions. + +class JvmtiExtensions : public AllStatic { + private: + static GrowableArray* _ext_functions; + static GrowableArray* _ext_events; + + public: + // register extensions function + static void register_extensions(); + + // returns the list of extension functions + static jvmtiError get_functions(JvmtiEnv* env, jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions); + + // returns the list of extension events + static jvmtiError get_events(JvmtiEnv* env, jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions); + + // sets the callback function for an extension event and enables the event + static jvmtiError set_event_callback(JvmtiEnv* env, jint extension_event_index, + jvmtiExtensionEvent callback); +}; + +#endif /* _JVMTI_EXTENSIONS_H_ */ diff --git a/hotspot/src/share/vm/prims/jvmtiGen.java b/hotspot/src/share/vm/prims/jvmtiGen.java new file mode 100644 index 00000000000..d613e19bcec --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiGen.java @@ -0,0 +1,194 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; + +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.w3c.dom.Document; +import org.w3c.dom.DOMException; + +// For write operation +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.stream.StreamResult; + +import java.io.*; + +public class jvmtiGen +{ + /** + * Write out usage and exit. + */ + private static void showUsage() { + System.err.println("usage:"); + System.err.println(" java jvmtiGen " + + "-IN " + + "-XSL " + + "-OUT " + + "[-PARAM ...]"); + System.exit(0); // There is no returning from showUsage() + } + + // Global value so it can be ref'd by the tree-adapter + static Document document; + + public static void main (String argv []) + { + String inFileName=null; + String xslFileName=null; + String outFileName=null; + java.util.Vector params=new java.util.Vector(); + for (int ii = 0; ii < argv.length; ii++) { + if (argv[ii].equals("-IN")) { + inFileName = argv[++ii]; + } else if (argv[ii].equals("-XSL")) { + xslFileName = argv[++ii]; + } else if (argv[ii].equals("-OUT")) { + outFileName = argv[++ii]; + } else if (argv[ii].equals("-PARAM")) { + if (ii + 2 < argv.length) { + String name = argv[++ii]; + params.addElement(name); + String expression = argv[++ii]; + params.addElement(expression); + } else { + showUsage(); + } + } else { + showUsage(); + } + } + if (inFileName==null || xslFileName==null || outFileName==null){ + showUsage(); + } + + /* + * Due to JAXP breakage in some intermediate Tiger builds, the + * com.sun.org.apache..... parser may throw an exception: + * com.sun.org.apache.xml.internal.utils.WrappedRuntimeException: + * org.apache.xalan.serialize.SerializerToText + * + * To work around the problem, this program uses the + * org.apache.xalan.... version if it is available. It is + * available in J2SE 1.4.x and early builds of 1.5 (Tiger). + * It was removed at the same time the thrown exception issue + * above was fixed, so if the class is not found we can proceed + * and use the default parser. + */ + final String parserProperty = + "javax.xml.transform.TransformerFactory"; + final String workaroundParser = + "org.apache.xalan.processor.TransformerFactoryImpl"; + + try { + java.lang.Class cls = java.lang.Class.forName(workaroundParser); + /* + * If we get here, we found the class. Use it. + */ + System.setProperty(parserProperty, workaroundParser); + System.out.println("Info: jvmtiGen using " + parserProperty + + " = " + workaroundParser); + } catch (ClassNotFoundException cnfex) { + /* + * We didn't find workaroundParser. Ignore the + * exception and proceed with default settings. + */ + } + + DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance(); + + factory.setNamespaceAware(true); + factory.setValidating(true); + + try { + File datafile = new File(inFileName); + File stylesheet = new File(xslFileName); + + DocumentBuilder builder = factory.newDocumentBuilder(); + document = builder.parse(datafile); + + // Use a Transformer for output + TransformerFactory tFactory = + TransformerFactory.newInstance(); + StreamSource stylesource = new StreamSource(stylesheet); + Transformer transformer = tFactory.newTransformer(stylesource); + for (int ii = 0; ii < params.size(); ii += 2){ + transformer.setParameter((String) params.elementAt(ii), + (String) params.elementAt(ii + 1)); + } + DOMSource source = new DOMSource(document); + + PrintStream ps = new PrintStream( new FileOutputStream(outFileName)); + StreamResult result = new StreamResult(ps); + transformer.transform(source, result); + + } catch (TransformerConfigurationException tce) { + // Error generated by the parser + System.out.println ("\n** Transformer Factory error"); + System.out.println(" " + tce.getMessage() ); + + // Use the contained exception, if any + Throwable x = tce; + if (tce.getException() != null) + x = tce.getException(); + x.printStackTrace(); + + } catch (TransformerException te) { + // Error generated by the parser + System.out.println ("\n** Transformation error"); + System.out.println(" " + te.getMessage() ); + + // Use the contained exception, if any + Throwable x = te; + if (te.getException() != null) + x = te.getException(); + x.printStackTrace(); + + } catch (SAXException sxe) { + // Error generated by this application + // (or a parser-initialization error) + Exception x = sxe; + if (sxe.getException() != null) + x = sxe.getException(); + x.printStackTrace(); + + } catch (ParserConfigurationException pce) { + // Parser with specified options can't be built + pce.printStackTrace(); + + } catch (IOException ioe) { + // I/O error + ioe.printStackTrace(); + } + } // main +} diff --git a/hotspot/src/share/vm/prims/jvmtiGetLoadedClasses.cpp b/hotspot/src/share/vm/prims/jvmtiGetLoadedClasses.cpp new file mode 100644 index 00000000000..5b193d5ef89 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiGetLoadedClasses.cpp @@ -0,0 +1,325 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiGetLoadedClasses.cpp.incl" + + +// The closure for GetLoadedClasses and GetClassLoaderClasses +class JvmtiGetLoadedClassesClosure : public StackObj { + // Since the SystemDictionary::classes_do callback + // doesn't pass a closureData pointer, + // we use a thread-local slot to hold a pointer to + // a stack allocated instance of this structure. + private: + jobject _initiatingLoader; + int _count; + Handle* _list; + int _index; + + private: + // Getting and setting the thread local pointer + static JvmtiGetLoadedClassesClosure* get_this() { + JvmtiGetLoadedClassesClosure* result = NULL; + JavaThread* thread = JavaThread::current(); + result = thread->get_jvmti_get_loaded_classes_closure(); + return result; + } + static void set_this(JvmtiGetLoadedClassesClosure* that) { + JavaThread* thread = JavaThread::current(); + thread->set_jvmti_get_loaded_classes_closure(that); + } + + public: + // Constructor/Destructor + JvmtiGetLoadedClassesClosure() { + JvmtiGetLoadedClassesClosure* that = get_this(); + assert(that == NULL, "JvmtiGetLoadedClassesClosure in use"); + _initiatingLoader = NULL; + _count = 0; + _list = NULL; + _index = 0; + set_this(this); + } + + JvmtiGetLoadedClassesClosure(jobject initiatingLoader) { + JvmtiGetLoadedClassesClosure* that = get_this(); + assert(that == NULL, "JvmtiGetLoadedClassesClosure in use"); + _initiatingLoader = initiatingLoader; + _count = 0; + _list = NULL; + _index = 0; + set_this(this); + } + + ~JvmtiGetLoadedClassesClosure() { + JvmtiGetLoadedClassesClosure* that = get_this(); + assert(that != NULL, "JvmtiGetLoadedClassesClosure not found"); + set_this(NULL); + _initiatingLoader = NULL; + _count = 0; + if (_list != NULL) { + FreeHeap(_list); + _list = NULL; + } + _index = 0; + } + + // Accessors. + jobject get_initiatingLoader() { + return _initiatingLoader; + } + + int get_count() { + return _count; + } + + void set_count(int value) { + _count = value; + } + + Handle* get_list() { + return _list; + } + + void set_list(Handle* value) { + _list = value; + } + + int get_index() { + return _index; + } + + void set_index(int value) { + _index = value; + } + + Handle get_element(int index) { + if ((_list != NULL) && (index < _count)) { + return _list[index]; + } else { + assert(false, "empty get_element"); + return Handle(); + } + } + + void set_element(int index, Handle value) { + if ((_list != NULL) && (index < _count)) { + _list[index] = value; + } else { + assert(false, "bad set_element"); + } + } + + // Other predicates + bool available() { + return (_list != NULL); + } + +#ifdef ASSERT + // For debugging. + void check(int limit) { + for (int i = 0; i < limit; i += 1) { + assert(Universe::heap()->is_in(get_element(i)()), "check fails"); + } + } +#endif + + // Public methods that get called within the scope of the closure + void allocate() { + _list = NEW_C_HEAP_ARRAY(Handle, _count); + assert(_list != NULL, "Out of memory"); + if (_list == NULL) { + _count = 0; + } + } + + void extract(JvmtiEnv *env, jclass* result) { + for (int index = 0; index < _count; index += 1) { + result[index] = (jclass) env->jni_reference(get_element(index)); + } + } + + // Finally, the static methods that are the callbacks + static void increment(klassOop k) { + JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this(); + if (that->get_initiatingLoader() == NULL) { + for (klassOop l = k; l != NULL; l = Klass::cast(l)->array_klass_or_null()) { + that->set_count(that->get_count() + 1); + } + } else if (k != NULL) { + // if initiating loader not null, just include the instance with 1 dimension + that->set_count(that->get_count() + 1); + } + } + + static void increment_with_loader(klassOop k, oop loader) { + JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this(); + if (loader == JNIHandles::resolve(that->get_initiatingLoader())) { + for (klassOop l = k; l != NULL; l = Klass::cast(l)->array_klass_or_null()) { + that->set_count(that->get_count() + 1); + } + } + } + + static void prim_array_increment_with_loader(klassOop array, oop loader) { + JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this(); + if (loader == JNIHandles::resolve(that->get_initiatingLoader())) { + that->set_count(that->get_count() + 1); + } + } + + static void add(klassOop k) { + JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this(); + if (that->available()) { + if (that->get_initiatingLoader() == NULL) { + for (klassOop l = k; l != NULL; l = Klass::cast(l)->array_klass_or_null()) { + oop mirror = Klass::cast(l)->java_mirror(); + that->set_element(that->get_index(), mirror); + that->set_index(that->get_index() + 1); + } + } else if (k != NULL) { + // if initiating loader not null, just include the instance with 1 dimension + oop mirror = Klass::cast(k)->java_mirror(); + that->set_element(that->get_index(), mirror); + that->set_index(that->get_index() + 1); + } + } + } + + static void add_with_loader(klassOop k, oop loader) { + JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this(); + if (that->available()) { + if (loader == JNIHandles::resolve(that->get_initiatingLoader())) { + for (klassOop l = k; l != NULL; l = Klass::cast(l)->array_klass_or_null()) { + oop mirror = Klass::cast(l)->java_mirror(); + that->set_element(that->get_index(), mirror); + that->set_index(that->get_index() + 1); + } + } + } + } + + // increment the count for the given basic type array class (and any + // multi-dimensional arrays). For example, for [B we check for + // [[B, [[[B, .. and the count is incremented for each one that exists. + static void increment_for_basic_type_arrays(klassOop k) { + JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this(); + assert(that != NULL, "no JvmtiGetLoadedClassesClosure"); + for (klassOop l = k; l != NULL; l = Klass::cast(l)->array_klass_or_null()) { + that->set_count(that->get_count() + 1); + } + } + + // add the basic type array class and its multi-dimensional array classes to the list + static void add_for_basic_type_arrays(klassOop k) { + JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this(); + assert(that != NULL, "no JvmtiGetLoadedClassesClosure"); + assert(that->available(), "no list"); + for (klassOop l = k; l != NULL; l = Klass::cast(l)->array_klass_or_null()) { + oop mirror = Klass::cast(l)->java_mirror(); + that->set_element(that->get_index(), mirror); + that->set_index(that->get_index() + 1); + } + } +}; + + +jvmtiError +JvmtiGetLoadedClasses::getLoadedClasses(JvmtiEnv *env, jint* classCountPtr, jclass** classesPtr) { + // Since SystemDictionary::classes_do only takes a function pointer + // and doesn't call back with a closure data pointer, + // we can only pass static methods. + + JvmtiGetLoadedClassesClosure closure; + { + // To get a consistent list of classes we need MultiArray_lock to ensure + // array classes aren't created, and SystemDictionary_lock to ensure that + // classes aren't added to the system dictionary, + MutexLocker ma(MultiArray_lock); + MutexLocker sd(SystemDictionary_lock); + + // First, count the classes + SystemDictionary::classes_do(&JvmtiGetLoadedClassesClosure::increment); + Universe::basic_type_classes_do(&JvmtiGetLoadedClassesClosure::increment); + // Next, fill in the classes + closure.allocate(); + SystemDictionary::classes_do(&JvmtiGetLoadedClassesClosure::add); + Universe::basic_type_classes_do(&JvmtiGetLoadedClassesClosure::add); + // Drop the SystemDictionary_lock, so the results could be wrong from here, + // but we still have a snapshot. + } + // Post results + jclass* result_list; + jvmtiError err = env->Allocate(closure.get_count() * sizeof(jclass), + (unsigned char**)&result_list); + if (err != JVMTI_ERROR_NONE) { + return err; + } + closure.extract(env, result_list); + *classCountPtr = closure.get_count(); + *classesPtr = result_list; + return JVMTI_ERROR_NONE; +} + +jvmtiError +JvmtiGetLoadedClasses::getClassLoaderClasses(JvmtiEnv *env, jobject initiatingLoader, + jint* classCountPtr, jclass** classesPtr) { + // Since SystemDictionary::classes_do only takes a function pointer + // and doesn't call back with a closure data pointer, + // we can only pass static methods. + JvmtiGetLoadedClassesClosure closure(initiatingLoader); + { + // To get a consistent list of classes we need MultiArray_lock to ensure + // array classes aren't created, and SystemDictionary_lock to ensure that + // classes aren't added to the system dictionary, + MutexLocker ma(MultiArray_lock); + MutexLocker sd(SystemDictionary_lock); + // First, count the classes in the system dictionary which have this loader recorded + // as an initiating loader. For basic type arrays this information is not recorded + // so GetClassLoaderClasses will return all of the basic type arrays. This is okay + // because the defining loader for basic type arrays is always the boot class loader + // and these classes are "visible" to all loaders. + SystemDictionary::classes_do(&JvmtiGetLoadedClassesClosure::increment_with_loader); + Universe::basic_type_classes_do(&JvmtiGetLoadedClassesClosure::increment_for_basic_type_arrays); + // Next, fill in the classes + closure.allocate(); + SystemDictionary::classes_do(&JvmtiGetLoadedClassesClosure::add_with_loader); + Universe::basic_type_classes_do(&JvmtiGetLoadedClassesClosure::add_for_basic_type_arrays); + // Drop the SystemDictionary_lock, so the results could be wrong from here, + // but we still have a snapshot. + } + // Post results + jclass* result_list; + jvmtiError err = env->Allocate(closure.get_count() * sizeof(jclass), + (unsigned char**)&result_list); + if (err != JVMTI_ERROR_NONE) { + return err; + } + closure.extract(env, result_list); + *classCountPtr = closure.get_count(); + *classesPtr = result_list; + return JVMTI_ERROR_NONE; +} diff --git a/hotspot/src/share/vm/prims/jvmtiGetLoadedClasses.hpp b/hotspot/src/share/vm/prims/jvmtiGetLoadedClasses.hpp new file mode 100644 index 00000000000..31a994b115e --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiGetLoadedClasses.hpp @@ -0,0 +1,30 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class JvmtiGetLoadedClasses : AllStatic { +public: + static jvmtiError getLoadedClasses(JvmtiEnv *env, jint* classCountPtr, jclass** classesPtr); + static jvmtiError getClassLoaderClasses(JvmtiEnv *env, jobject initiatingLoader, + jint* classCountPtr, jclass** classesPtr); +}; diff --git a/hotspot/src/share/vm/prims/jvmtiH.xsl b/hotspot/src/share/vm/prims/jvmtiH.xsl new file mode 100644 index 00000000000..8d488ce7516 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiH.xsl @@ -0,0 +1,452 @@ + + + + + + + + + + + + + + + + + + /* Derived Base Types */ + + + + + + /* Constants */ + + + + + + /* Errors */ + +typedef enum { + + + + + , + + + JVMTI_ERROR_MAX = + + + + +} jvmtiError; + + + + + /* Pre-Declarations */ + + + + + /* Function Types */ + + + + + + /* Structure Types */ + + + + + + + + + + + + + + + + /* Include file for the Java(tm) Virtual Machine Tool Interface */ + +#ifndef _JAVA_JVMTI_H_ +#define _JAVA_JVMTI_H_ + +#include "jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + JVMTI_VERSION_1 = 0x30010000, + JVMTI_VERSION_1_0 = 0x30010000, + JVMTI_VERSION_1_1 = 0x30010100, + + JVMTI_VERSION = 0x30000000 + ( + + * 0x10000) + ( + + * 0x100) + + + + + + /* checked out - + + + + + + /* + + + version: + + */ +}; + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved); + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM* vm, char* options, void* reserved); + +JNIEXPORT void JNICALL +Agent_OnUnload(JavaVM *vm); + + /* Forward declaration of the environment */ + +struct _jvmtiEnv; + +struct jvmtiInterface_1_; + +#ifdef __cplusplus +typedef _jvmtiEnv jvmtiEnv; +#else +typedef const struct jvmtiInterface_1_ *jvmtiEnv; +#endif /* __cplusplus */ + + + + + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVA_JVMTI_H_ */ + + + + + + + /* Event IDs */ + +typedef enum { + + + + + JVMTI_MIN_EVENT_TYPE_VAL = + + , + + + + , + + + JVMTI_MAX_EVENT_TYPE_VAL = + + + + +} jvmtiEvent; + + + + + + + + /* Event Definitions */ + +typedef void (JNICALL *jvmtiEventReserved)(void); + + + + + + + + /* Event Callback Structure */ + +typedef struct { + + + + + + + + } jvmtiEventCallbacks; + + + + + + + +typedef void (JNICALL *jvmtiEvent + + ) + (jvmtiEnv *jvmti_env + + + , + + + + ); + + + + + + + /* Function Interface */ + +typedef struct jvmtiInterface_1_ { + + + + + + + + } jvmtiInterface_1; + +struct _jvmtiEnv { + const struct jvmtiInterface_1_ *functions; +#ifdef __cplusplus + + + + +#endif /* __cplusplus */ +}; + + + + + + + + + /* + + : + + + + */ + jvmtiError (JNICALL * + + ) (jvmtiEnv* env + + + , + + + + ) + + + RESERVED */ + void *reserved + + + + ; + + + + + + + + + + + + + jvmtiError (JNICALL * + + ) (jvmtiEnv* env + + ); + + + + + + + + + + + jvmtiError + + ( + + ) { + return functions-> + + (this + + + + , + + + + + ); + } + + + + + + + + + + + + + + + + /* + + */ + + + + + + + + + + + + + +typedef + + (JNICALL * + + ) + ( + + + + , + + + + ); + + + + + + + + + + + + + struct _ + + ; + + typedef struct _ + + + + ; + + + + + struct _ + + { + + + }; + + + + + union _ + + ; + + typedef union _ + + + + ; + + + + + union _ + + { + + + }; + + + + diff --git a/hotspot/src/share/vm/prims/jvmtiHpp.xsl b/hotspot/src/share/vm/prims/jvmtiHpp.xsl new file mode 100644 index 00000000000..3b3b23e90f6 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiHpp.xsl @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + +enum { + JVMTI_INTERNAL_CAPABILITY_COUNT = + + +}; + + +class JvmtiEnv : public JvmtiEnvBase { + +private: + + JvmtiEnv(); + ~JvmtiEnv(); + +public: + + static JvmtiEnv* create_a_jvmti(); + + + + +}; + + + + + + + + + + // functions + + + + + + jvmtiError + + + + + ( + + ); + + + + diff --git a/hotspot/src/share/vm/prims/jvmtiImpl.cpp b/hotspot/src/share/vm/prims/jvmtiImpl.cpp new file mode 100644 index 00000000000..f9a512c4222 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiImpl.cpp @@ -0,0 +1,912 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiImpl.cpp.incl" + +GrowableArray *JvmtiPendingMonitors::_monitors = new (ResourceObj::C_HEAP) GrowableArray(1,true); + +void JvmtiPendingMonitors::transition_raw_monitors() { + assert((Threads::number_of_threads()==1), + "Java thread has not created yet or more than one java thread \ +is running. Raw monitor transition will not work"); + JavaThread *current_java_thread = JavaThread::current(); + assert(current_java_thread->thread_state() == _thread_in_vm, "Must be in vm"); + { + ThreadBlockInVM __tbivm(current_java_thread); + for(int i=0; i< count(); i++) { + JvmtiRawMonitor *rmonitor = monitors()->at(i); + int r = rmonitor->raw_enter(current_java_thread); + assert(r == ObjectMonitor::OM_OK, "raw_enter should have worked"); + } + } + // pending monitors are converted to real monitor so delete them all. + dispose(); +} + +// +// class JvmtiAgentThread +// +// JavaThread used to wrap a thread started by an agent +// using the JVMTI method RunAgentThread. +// + +JvmtiAgentThread::JvmtiAgentThread(JvmtiEnv* env, jvmtiStartFunction start_fn, const void *start_arg) + : JavaThread(start_function_wrapper) { + _env = env; + _start_fn = start_fn; + _start_arg = start_arg; +} + +void +JvmtiAgentThread::start_function_wrapper(JavaThread *thread, TRAPS) { + // It is expected that any Agent threads will be created as + // Java Threads. If this is the case, notification of the creation + // of the thread is given in JavaThread::thread_main(). + assert(thread->is_Java_thread(), "debugger thread should be a Java Thread"); + assert(thread == JavaThread::current(), "sanity check"); + + JvmtiAgentThread *dthread = (JvmtiAgentThread *)thread; + dthread->call_start_function(); +} + +void +JvmtiAgentThread::call_start_function() { + ThreadToNativeFromVM transition(this); + _start_fn(_env->jvmti_external(), jni_environment(), (void*)_start_arg); +} + + +// +// class GrowableCache - private methods +// + +void GrowableCache::recache() { + int len = _elements->length(); + + FREE_C_HEAP_ARRAY(address, _cache); + _cache = NEW_C_HEAP_ARRAY(address,len+1); + + for (int i=0; iat(i)->getCacheValue(); + // + // The cache entry has gone bad. Without a valid frame pointer + // value, the entry is useless so we simply delete it in product + // mode. The call to remove() will rebuild the cache again + // without the bad entry. + // + if (_cache[i] == NULL) { + assert(false, "cannot recache NULL elements"); + remove(i); + return; + } + } + _cache[len] = NULL; + + _listener_fun(_this_obj,_cache); +} + +bool GrowableCache::equals(void* v, GrowableElement *e2) { + GrowableElement *e1 = (GrowableElement *) v; + assert(e1 != NULL, "e1 != NULL"); + assert(e2 != NULL, "e2 != NULL"); + + return e1->equals(e2); +} + +// +// class GrowableCache - public methods +// + +GrowableCache::GrowableCache() { + _this_obj = NULL; + _listener_fun = NULL; + _elements = NULL; + _cache = NULL; +} + +GrowableCache::~GrowableCache() { + clear(); + delete _elements; + FREE_C_HEAP_ARRAY(address, _cache); +} + +void GrowableCache::initialize(void *this_obj, void listener_fun(void *, address*) ) { + _this_obj = this_obj; + _listener_fun = listener_fun; + _elements = new (ResourceObj::C_HEAP) GrowableArray(5,true); + recache(); +} + +// number of elements in the collection +int GrowableCache::length() { + return _elements->length(); +} + +// get the value of the index element in the collection +GrowableElement* GrowableCache::at(int index) { + GrowableElement *e = (GrowableElement *) _elements->at(index); + assert(e != NULL, "e != NULL"); + return e; +} + +int GrowableCache::find(GrowableElement* e) { + return _elements->find(e, GrowableCache::equals); +} + +// append a copy of the element to the end of the collection +void GrowableCache::append(GrowableElement* e) { + GrowableElement *new_e = e->clone(); + _elements->append(new_e); + recache(); +} + +// insert a copy of the element using lessthan() +void GrowableCache::insert(GrowableElement* e) { + GrowableElement *new_e = e->clone(); + _elements->append(new_e); + + int n = length()-2; + for (int i=n; i>=0; i--) { + GrowableElement *e1 = _elements->at(i); + GrowableElement *e2 = _elements->at(i+1); + if (e2->lessThan(e1)) { + _elements->at_put(i+1, e1); + _elements->at_put(i, e2); + } + } + + recache(); +} + +// remove the element at index +void GrowableCache::remove (int index) { + GrowableElement *e = _elements->at(index); + assert(e != NULL, "e != NULL"); + _elements->remove(e); + delete e; + recache(); +} + +// clear out all elements, release all heap space and +// let our listener know that things have changed. +void GrowableCache::clear() { + int len = _elements->length(); + for (int i=0; iat(i); + } + _elements->clear(); + recache(); +} + +void GrowableCache::oops_do(OopClosure* f) { + int len = _elements->length(); + for (int i=0; iat(i); + e->oops_do(f); + } +} + +void GrowableCache::gc_epilogue() { + int len = _elements->length(); + // recompute the new cache value after GC + for (int i=0; iat(i)->getCacheValue(); + } +} + + +// +// class JvmtiRawMonitor +// + +JvmtiRawMonitor::JvmtiRawMonitor(const char *name) { +#ifdef ASSERT + _name = strcpy(NEW_C_HEAP_ARRAY(char, strlen(name) + 1), name); +#else + _name = NULL; +#endif + _magic = JVMTI_RM_MAGIC; +} + +JvmtiRawMonitor::~JvmtiRawMonitor() { +#ifdef ASSERT + FreeHeap(_name); +#endif + _magic = 0; +} + + +// +// class JvmtiBreakpoint +// + +JvmtiBreakpoint::JvmtiBreakpoint() { + _method = NULL; + _bci = 0; +#ifdef CHECK_UNHANDLED_OOPS + // This one is always allocated with new, but check it just in case. + Thread *thread = Thread::current(); + if (thread->is_in_stack((address)&_method)) { + thread->allow_unhandled_oop((oop*)&_method); + } +#endif // CHECK_UNHANDLED_OOPS +} + +JvmtiBreakpoint::JvmtiBreakpoint(methodOop m_method, jlocation location) { + _method = m_method; + assert(_method != NULL, "_method != NULL"); + _bci = (int) location; +#ifdef CHECK_UNHANDLED_OOPS + // Could be allocated with new and wouldn't be on the unhandled oop list. + Thread *thread = Thread::current(); + if (thread->is_in_stack((address)&_method)) { + thread->allow_unhandled_oop(&_method); + } +#endif // CHECK_UNHANDLED_OOPS + + assert(_bci >= 0, "_bci >= 0"); +} + +void JvmtiBreakpoint::copy(JvmtiBreakpoint& bp) { + _method = bp._method; + _bci = bp._bci; +} + +bool JvmtiBreakpoint::lessThan(JvmtiBreakpoint& bp) { + Unimplemented(); + return false; +} + +bool JvmtiBreakpoint::equals(JvmtiBreakpoint& bp) { + return _method == bp._method + && _bci == bp._bci; +} + +bool JvmtiBreakpoint::is_valid() { + return _method != NULL && + _bci >= 0; +} + +address JvmtiBreakpoint::getBcp() { + return _method->bcp_from(_bci); +} + +void JvmtiBreakpoint::each_method_version_do(method_action meth_act) { + ((methodOopDesc*)_method->*meth_act)(_bci); + + // add/remove breakpoint to/from versions of the method that + // are EMCP. Directly or transitively obsolete methods are + // not saved in the PreviousVersionInfo. + Thread *thread = Thread::current(); + instanceKlassHandle ikh = instanceKlassHandle(thread, _method->method_holder()); + symbolOop m_name = _method->name(); + symbolOop m_signature = _method->signature(); + + { + ResourceMark rm(thread); + // PreviousVersionInfo objects returned via PreviousVersionWalker + // contain a GrowableArray of handles. We have to clean up the + // GrowableArray _after_ the PreviousVersionWalker destructor + // has destroyed the handles. + { + // search previous versions if they exist + PreviousVersionWalker pvw((instanceKlass *)ikh()->klass_part()); + for (PreviousVersionInfo * pv_info = pvw.next_previous_version(); + pv_info != NULL; pv_info = pvw.next_previous_version()) { + GrowableArray* methods = + pv_info->prev_EMCP_method_handles(); + + if (methods == NULL) { + // We have run into a PreviousVersion generation where + // all methods were made obsolete during that generation's + // RedefineClasses() operation. At the time of that + // operation, all EMCP methods were flushed so we don't + // have to go back any further. + // + // A NULL methods array is different than an empty methods + // array. We cannot infer any optimizations about older + // generations from an empty methods array for the current + // generation. + break; + } + + for (int i = methods->length() - 1; i >= 0; i--) { + methodHandle method = methods->at(i); + if (method->name() == m_name && method->signature() == m_signature) { + RC_TRACE(0x00000800, ("%sing breakpoint in %s(%s)", + meth_act == &methodOopDesc::set_breakpoint ? "sett" : "clear", + method->name()->as_C_string(), + method->signature()->as_C_string())); + assert(!method->is_obsolete(), "only EMCP methods here"); + + ((methodOopDesc*)method()->*meth_act)(_bci); + break; + } + } + } + } // pvw is cleaned up + } // rm is cleaned up +} + +void JvmtiBreakpoint::set() { + each_method_version_do(&methodOopDesc::set_breakpoint); +} + +void JvmtiBreakpoint::clear() { + each_method_version_do(&methodOopDesc::clear_breakpoint); +} + +void JvmtiBreakpoint::print() { +#ifndef PRODUCT + const char *class_name = (_method == NULL) ? "NULL" : _method->klass_name()->as_C_string(); + const char *method_name = (_method == NULL) ? "NULL" : _method->name()->as_C_string(); + + tty->print("Breakpoint(%s,%s,%d,%p)",class_name, method_name, _bci, getBcp()); +#endif +} + + +// +// class VM_ChangeBreakpoints +// +// Modify the Breakpoints data structure at a safepoint +// + +void VM_ChangeBreakpoints::doit() { + switch (_operation) { + case SET_BREAKPOINT: + _breakpoints->set_at_safepoint(*_bp); + break; + case CLEAR_BREAKPOINT: + _breakpoints->clear_at_safepoint(*_bp); + break; + case CLEAR_ALL_BREAKPOINT: + _breakpoints->clearall_at_safepoint(); + break; + default: + assert(false, "Unknown operation"); + } +} + +void VM_ChangeBreakpoints::oops_do(OopClosure* f) { + // This operation keeps breakpoints alive + if (_breakpoints != NULL) { + _breakpoints->oops_do(f); + } + if (_bp != NULL) { + _bp->oops_do(f); + } +} + +// +// class JvmtiBreakpoints +// +// a JVMTI internal collection of JvmtiBreakpoint +// + +JvmtiBreakpoints::JvmtiBreakpoints(void listener_fun(void *,address *)) { + _bps.initialize(this,listener_fun); +} + +JvmtiBreakpoints:: ~JvmtiBreakpoints() {} + +void JvmtiBreakpoints::oops_do(OopClosure* f) { + _bps.oops_do(f); +} + +void JvmtiBreakpoints::gc_epilogue() { + _bps.gc_epilogue(); +} + +void JvmtiBreakpoints::print() { +#ifndef PRODUCT + ResourceMark rm; + + int n = _bps.length(); + for (int i=0; iprint("%d: ", i); + bp.print(); + tty->print_cr(""); + } +#endif +} + + +void JvmtiBreakpoints::set_at_safepoint(JvmtiBreakpoint& bp) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + + int i = _bps.find(bp); + if (i == -1) { + _bps.append(bp); + bp.set(); + } +} + +void JvmtiBreakpoints::clear_at_safepoint(JvmtiBreakpoint& bp) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + + int i = _bps.find(bp); + if (i != -1) { + _bps.remove(i); + bp.clear(); + } +} + +void JvmtiBreakpoints::clearall_at_safepoint() { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + + int len = _bps.length(); + for (int i=0; imethod_holder() == klass) { + bp.clear(); + _bps.remove(i); + // This changed 'i' so we have to start over. + changed = true; + break; + } + } + } +} + +void JvmtiBreakpoints::clearall() { + VM_ChangeBreakpoints clearall_breakpoint(this,VM_ChangeBreakpoints::CLEAR_ALL_BREAKPOINT); + VMThread::execute(&clearall_breakpoint); +} + +// +// class JvmtiCurrentBreakpoints +// + +JvmtiBreakpoints *JvmtiCurrentBreakpoints::_jvmti_breakpoints = NULL; +address * JvmtiCurrentBreakpoints::_breakpoint_list = NULL; + + +JvmtiBreakpoints& JvmtiCurrentBreakpoints::get_jvmti_breakpoints() { + if (_jvmti_breakpoints != NULL) return (*_jvmti_breakpoints); + _jvmti_breakpoints = new JvmtiBreakpoints(listener_fun); + assert(_jvmti_breakpoints != NULL, "_jvmti_breakpoints != NULL"); + return (*_jvmti_breakpoints); +} + +void JvmtiCurrentBreakpoints::listener_fun(void *this_obj, address *cache) { + JvmtiBreakpoints *this_jvmti = (JvmtiBreakpoints *) this_obj; + assert(this_jvmti != NULL, "this_jvmti != NULL"); + + debug_only(int n = this_jvmti->length();); + assert(cache[n] == NULL, "cache must be NULL terminated"); + + set_breakpoint_list(cache); +} + + +void JvmtiCurrentBreakpoints::oops_do(OopClosure* f) { + if (_jvmti_breakpoints != NULL) { + _jvmti_breakpoints->oops_do(f); + } +} + +void JvmtiCurrentBreakpoints::gc_epilogue() { + if (_jvmti_breakpoints != NULL) { + _jvmti_breakpoints->gc_epilogue(); + } +} + + +/////////////////////////////////////////////////////////////// +// +// class VM_GetOrSetLocal +// + +// Constructor for non-object getter +VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, int index, BasicType type) + : _thread(thread) + , _calling_thread(NULL) + , _depth(depth) + , _index(index) + , _type(type) + , _set(false) + , _jvf(NULL) + , _result(JVMTI_ERROR_NONE) +{ +} + +// Constructor for object or non-object setter +VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, int index, BasicType type, jvalue value) + : _thread(thread) + , _calling_thread(NULL) + , _depth(depth) + , _index(index) + , _type(type) + , _value(value) + , _set(true) + , _jvf(NULL) + , _result(JVMTI_ERROR_NONE) +{ +} + +// Constructor for object getter +VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, JavaThread* calling_thread, jint depth, int index) + : _thread(thread) + , _calling_thread(calling_thread) + , _depth(depth) + , _index(index) + , _type(T_OBJECT) + , _set(false) + , _jvf(NULL) + , _result(JVMTI_ERROR_NONE) +{ +} + + +vframe *VM_GetOrSetLocal::get_vframe() { + if (!_thread->has_last_Java_frame()) { + return NULL; + } + RegisterMap reg_map(_thread); + vframe *vf = _thread->last_java_vframe(®_map); + int d = 0; + while ((vf != NULL) && (d < _depth)) { + vf = vf->java_sender(); + d++; + } + return vf; +} + +javaVFrame *VM_GetOrSetLocal::get_java_vframe() { + vframe* vf = get_vframe(); + if (vf == NULL) { + _result = JVMTI_ERROR_NO_MORE_FRAMES; + return NULL; + } + javaVFrame *jvf = (javaVFrame*)vf; + + if (!vf->is_java_frame() || jvf->method()->is_native()) { + _result = JVMTI_ERROR_OPAQUE_FRAME; + return NULL; + } + return jvf; +} + +// Check that the klass is assignable to a type with the given signature. +// Another solution could be to use the function Klass::is_subtype_of(type). +// But the type class can be forced to load/initialize eagerly in such a case. +// This may cause unexpected consequences like CFLH or class-init JVMTI events. +// It is better to avoid such a behavior. +bool VM_GetOrSetLocal::is_assignable(const char* ty_sign, Klass* klass, Thread* thread) { + assert(ty_sign != NULL, "type signature must not be NULL"); + assert(thread != NULL, "thread must not be NULL"); + assert(klass != NULL, "klass must not be NULL"); + + int len = (int) strlen(ty_sign); + if (ty_sign[0] == 'L' && ty_sign[len-1] == ';') { // Need pure class/interface name + ty_sign++; + len -= 2; + } + symbolHandle ty_sym = oopFactory::new_symbol_handle(ty_sign, len, thread); + if (klass->name() == ty_sym()) { + return true; + } + // Compare primary supers + int super_depth = klass->super_depth(); + int idx; + for (idx = 0; idx < super_depth; idx++) { + if (Klass::cast(klass->primary_super_of_depth(idx))->name() == ty_sym()) { + return true; + } + } + // Compare secondary supers + objArrayOop sec_supers = klass->secondary_supers(); + for (idx = 0; idx < sec_supers->length(); idx++) { + if (Klass::cast((klassOop) sec_supers->obj_at(idx))->name() == ty_sym()) { + return true; + } + } + return false; +} + +// Checks error conditions: +// JVMTI_ERROR_INVALID_SLOT +// JVMTI_ERROR_TYPE_MISMATCH +// Returns: 'true' - everything is Ok, 'false' - error code + +bool VM_GetOrSetLocal::check_slot_type(javaVFrame* jvf) { + methodOop method_oop = jvf->method(); + if (!method_oop->has_localvariable_table()) { + // Just to check index boundaries + jint extra_slot = (_type == T_LONG || _type == T_DOUBLE) ? 1 : 0; + if (_index < 0 || _index + extra_slot >= method_oop->max_locals()) { + _result = JVMTI_ERROR_INVALID_SLOT; + return false; + } + return true; + } + + jint num_entries = method_oop->localvariable_table_length(); + if (num_entries == 0) { + _result = JVMTI_ERROR_INVALID_SLOT; + return false; // There are no slots + } + int signature_idx = -1; + int vf_bci = jvf->bci(); + LocalVariableTableElement* table = method_oop->localvariable_table_start(); + for (int i = 0; i < num_entries; i++) { + int start_bci = table[i].start_bci; + int end_bci = start_bci + table[i].length; + + // Here we assume that locations of LVT entries + // with the same slot number cannot be overlapped + if (_index == (jint) table[i].slot && start_bci <= vf_bci && vf_bci <= end_bci) { + signature_idx = (int) table[i].descriptor_cp_index; + break; + } + } + if (signature_idx == -1) { + _result = JVMTI_ERROR_INVALID_SLOT; + return false; // Incorrect slot index + } + symbolOop sign_sym = method_oop->constants()->symbol_at(signature_idx); + const char* signature = (const char *) sign_sym->as_utf8(); + BasicType slot_type = char2type(signature[0]); + + switch (slot_type) { + case T_BYTE: + case T_SHORT: + case T_CHAR: + case T_BOOLEAN: + slot_type = T_INT; + break; + case T_ARRAY: + slot_type = T_OBJECT; + break; + }; + if (_type != slot_type) { + _result = JVMTI_ERROR_TYPE_MISMATCH; + return false; + } + + jobject jobj = _value.l; + if (_set && slot_type == T_OBJECT && jobj != NULL) { // NULL reference is allowed + // Check that the jobject class matches the return type signature. + JavaThread* cur_thread = JavaThread::current(); + HandleMark hm(cur_thread); + + Handle obj = Handle(cur_thread, JNIHandles::resolve_external_guard(jobj)); + NULL_CHECK(obj, (_result = JVMTI_ERROR_INVALID_OBJECT, false)); + KlassHandle ob_kh = KlassHandle(cur_thread, obj->klass()); + NULL_CHECK(ob_kh, (_result = JVMTI_ERROR_INVALID_OBJECT, false)); + + if (!is_assignable(signature, Klass::cast(ob_kh()), cur_thread)) { + _result = JVMTI_ERROR_TYPE_MISMATCH; + return false; + } + } + return true; +} + +static bool can_be_deoptimized(vframe* vf) { + return (vf->is_compiled_frame() && vf->fr().can_be_deoptimized()); +} + +bool VM_GetOrSetLocal::doit_prologue() { + _jvf = get_java_vframe(); + NULL_CHECK(_jvf, false); + + if (!check_slot_type(_jvf)) { + return false; + } + return true; +} + +void VM_GetOrSetLocal::doit() { + if (_set) { + // Force deoptimization of frame if compiled because it's + // possible the compiler emitted some locals as constant values, + // meaning they are not mutable. + if (can_be_deoptimized(_jvf)) { + + // Schedule deoptimization so that eventually the local + // update will be written to an interpreter frame. + VM_DeoptimizeFrame deopt(_jvf->thread(), _jvf->fr().id()); + VMThread::execute(&deopt); + + // Now store a new value for the local which will be applied + // once deoptimization occurs. Note however that while this + // write is deferred until deoptimization actually happens + // can vframe created after this point will have its locals + // reflecting this update so as far as anyone can see the + // write has already taken place. + + // If we are updating an oop then get the oop from the handle + // since the handle will be long gone by the time the deopt + // happens. The oop stored in the deferred local will be + // gc'd on its own. + if (_type == T_OBJECT) { + _value.l = (jobject) (JNIHandles::resolve_external_guard(_value.l)); + } + // Re-read the vframe so we can see that it is deoptimized + // [ Only need because of assert in update_local() ] + _jvf = get_java_vframe(); + ((compiledVFrame*)_jvf)->update_local(_type, _index, _value); + return; + } + StackValueCollection *locals = _jvf->locals(); + HandleMark hm; + + switch (_type) { + case T_INT: locals->set_int_at (_index, _value.i); break; + case T_LONG: locals->set_long_at (_index, _value.j); break; + case T_FLOAT: locals->set_float_at (_index, _value.f); break; + case T_DOUBLE: locals->set_double_at(_index, _value.d); break; + case T_OBJECT: { + Handle ob_h(JNIHandles::resolve_external_guard(_value.l)); + locals->set_obj_at (_index, ob_h); + break; + } + default: ShouldNotReachHere(); + } + _jvf->set_locals(locals); + } else { + StackValueCollection *locals = _jvf->locals(); + + if (locals->at(_index)->type() == T_CONFLICT) { + memset(&_value, 0, sizeof(_value)); + _value.l = NULL; + return; + } + + switch (_type) { + case T_INT: _value.i = locals->int_at (_index); break; + case T_LONG: _value.j = locals->long_at (_index); break; + case T_FLOAT: _value.f = locals->float_at (_index); break; + case T_DOUBLE: _value.d = locals->double_at(_index); break; + case T_OBJECT: { + // Wrap the oop to be returned in a local JNI handle since + // oops_do() no longer applies after doit() is finished. + oop obj = locals->obj_at(_index)(); + _value.l = JNIHandles::make_local(_calling_thread, obj); + break; + } + default: ShouldNotReachHere(); + } + } +} + + +bool VM_GetOrSetLocal::allow_nested_vm_operations() const { + return true; // May need to deoptimize +} + + +///////////////////////////////////////////////////////////////////////////////////////// + +// +// class JvmtiSuspendControl - see comments in jvmtiImpl.hpp +// + +bool JvmtiSuspendControl::suspend(JavaThread *java_thread) { + // external suspend should have caught suspending a thread twice + + // Immediate suspension required for JPDA back-end so JVMTI agent threads do + // not deadlock due to later suspension on transitions while holding + // raw monitors. Passing true causes the immediate suspension. + // java_suspend() will catch threads in the process of exiting + // and will ignore them. + java_thread->java_suspend(); + + // It would be nice to have the following assertion in all the time, + // but it is possible for a racing resume request to have resumed + // this thread right after we suspended it. Temporarily enable this + // assertion if you are chasing a different kind of bug. + // + // assert(java_lang_Thread::thread(java_thread->threadObj()) == NULL || + // java_thread->is_being_ext_suspended(), "thread is not suspended"); + + if (java_lang_Thread::thread(java_thread->threadObj()) == NULL) { + // check again because we can get delayed in java_suspend(): + // the thread is in process of exiting. + return false; + } + + return true; +} + +bool JvmtiSuspendControl::resume(JavaThread *java_thread) { + // external suspend should have caught resuming a thread twice + assert(java_thread->is_being_ext_suspended(), "thread should be suspended"); + + // resume thread + { + // must always grab Threads_lock, see JVM_SuspendThread + MutexLocker ml(Threads_lock); + java_thread->java_resume(); + } + + return true; +} + + +void JvmtiSuspendControl::print() { +#ifndef PRODUCT + MutexLocker mu(Threads_lock); + ResourceMark rm; + + tty->print("Suspended Threads: ["); + for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { +#if JVMTI_TRACE + const char *name = JvmtiTrace::safe_get_thread_name(thread); +#else + const char *name = ""; +#endif /*JVMTI_TRACE */ + tty->print("%s(%c ", name, thread->is_being_ext_suspended() ? 'S' : '_'); + if (!thread->has_last_Java_frame()) { + tty->print("no stack"); + } + tty->print(") "); + } + tty->print_cr("]"); +#endif +} diff --git a/hotspot/src/share/vm/prims/jvmtiImpl.hpp b/hotspot/src/share/vm/prims/jvmtiImpl.hpp new file mode 100644 index 00000000000..d1b8414e61e --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiImpl.hpp @@ -0,0 +1,477 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Forward Declarations +// + +class JvmtiRawMonitor; +class JvmtiBreakpoint; +class JvmtiBreakpoints; + + +/////////////////////////////////////////////////////////////// +// +// class GrowableCache, GrowableElement +// Used by : JvmtiBreakpointCache +// Used by JVMTI methods: none directly. +// +// GrowableCache is a permanent CHeap growable array of +// +// In addition, the GrowableCache maintains a NULL terminated cache array of type address +// that's created from the element array using the function: +// address GrowableElement::getCacheValue(). +// +// Whenever the GrowableArray changes size, the cache array gets recomputed into a new C_HEAP allocated +// block of memory. Additionally, every time the cache changes its position in memory, the +// void (*_listener_fun)(void *this_obj, address* cache) +// gets called with the cache's new address. This gives the user of the GrowableCache a callback +// to update its pointer to the address cache. +// + +class GrowableElement : public CHeapObj { +public: + virtual address getCacheValue() =0; + virtual bool equals(GrowableElement* e) =0; + virtual bool lessThan(GrowableElement *e)=0; + virtual GrowableElement *clone() =0; + virtual void oops_do(OopClosure* f) =0; +}; + +class GrowableCache VALUE_OBJ_CLASS_SPEC { + +private: + // Object pointer passed into cache & listener functions. + void *_this_obj; + + // Array of elements in the collection + GrowableArray *_elements; + + // Parallel array of cached values + address *_cache; + + // Listener for changes to the _cache field. + // Called whenever the _cache field has it's value changed + // (but NOT when cached elements are recomputed). + void (*_listener_fun)(void *, address*); + + static bool equals(void *, GrowableElement *); + + // recache all elements after size change, notify listener + void recache(); + +public: + GrowableCache(); + ~GrowableCache(); + + void initialize(void *this_obj, void listener_fun(void *, address*) ); + + // number of elements in the collection + int length(); + // get the value of the index element in the collection + GrowableElement* at(int index); + // find the index of the element, -1 if it doesn't exist + int find(GrowableElement* e); + // append a copy of the element to the end of the collection, notify listener + void append(GrowableElement* e); + // insert a copy of the element using lessthan(), notify listener + void insert(GrowableElement* e); + // remove the element at index, notify listener + void remove (int index); + // clear out all elements and release all heap space, notify listener + void clear(); + // apply f to every element and update the cache + void oops_do(OopClosure* f); + void gc_epilogue(); +}; + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiBreakpointCache +// Used by : JvmtiBreakpoints +// Used by JVMTI methods: none directly. +// Note : typesafe wrapper for GrowableCache of JvmtiBreakpoint +// + +class JvmtiBreakpointCache : public CHeapObj { + +private: + GrowableCache _cache; + +public: + JvmtiBreakpointCache() {} + ~JvmtiBreakpointCache() {} + + void initialize(void *this_obj, void listener_fun(void *, address*) ) { + _cache.initialize(this_obj,listener_fun); + } + + int length() { return _cache.length(); } + JvmtiBreakpoint& at(int index) { return (JvmtiBreakpoint&) *(_cache.at(index)); } + int find(JvmtiBreakpoint& e) { return _cache.find((GrowableElement *) &e); } + void append(JvmtiBreakpoint& e) { _cache.append((GrowableElement *) &e); } + void remove (int index) { _cache.remove(index); } + void clear() { _cache.clear(); } + void oops_do(OopClosure* f) { _cache.oops_do(f); } + void gc_epilogue() { _cache.gc_epilogue(); } +}; + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiBreakpoint +// Used by : JvmtiBreakpoints +// Used by JVMTI methods: SetBreakpoint, ClearBreakpoint, ClearAllBreakpoints +// Note: Extends GrowableElement for use in a GrowableCache +// +// A JvmtiBreakpoint describes a location (class, method, bci) to break at. +// + +typedef void (methodOopDesc::*method_action)(int _bci); + +class JvmtiBreakpoint : public GrowableElement { +private: + methodOop _method; + int _bci; + Bytecodes::Code _orig_bytecode; + +public: + JvmtiBreakpoint(); + JvmtiBreakpoint(methodOop m_method, jlocation location); + bool equals(JvmtiBreakpoint& bp); + bool lessThan(JvmtiBreakpoint &bp); + void copy(JvmtiBreakpoint& bp); + bool is_valid(); + address getBcp(); + void each_method_version_do(method_action meth_act); + void set(); + void clear(); + void print(); + + methodOop method() { return _method; } + + // GrowableElement implementation + address getCacheValue() { return getBcp(); } + bool lessThan(GrowableElement* e) { Unimplemented(); return false; } + bool equals(GrowableElement* e) { return equals((JvmtiBreakpoint&) *e); } + void oops_do(OopClosure* f) { f->do_oop((oop *) &_method); } + GrowableElement *clone() { + JvmtiBreakpoint *bp = new JvmtiBreakpoint(); + bp->copy(*this); + return bp; + } +}; + + +/////////////////////////////////////////////////////////////// +// +// class VM_ChangeBreakpoints +// Used by : JvmtiBreakpoints +// Used by JVMTI methods: none directly. +// Note: A Helper class. +// +// VM_ChangeBreakpoints implements a VM_Operation for ALL modifications to the JvmtiBreakpoints class. +// + +class VM_ChangeBreakpoints : public VM_Operation { +private: + JvmtiBreakpoints* _breakpoints; + int _operation; + JvmtiBreakpoint* _bp; + +public: + enum { SET_BREAKPOINT=0, CLEAR_BREAKPOINT=1, CLEAR_ALL_BREAKPOINT=2 }; + + VM_ChangeBreakpoints(JvmtiBreakpoints* breakpoints, int operation) { + _breakpoints = breakpoints; + _bp = NULL; + _operation = operation; + assert(breakpoints != NULL, "breakpoints != NULL"); + assert(operation == CLEAR_ALL_BREAKPOINT, "unknown breakpoint operation"); + } + VM_ChangeBreakpoints(JvmtiBreakpoints* breakpoints, int operation, JvmtiBreakpoint *bp) { + _breakpoints = breakpoints; + _bp = bp; + _operation = operation; + assert(breakpoints != NULL, "breakpoints != NULL"); + assert(bp != NULL, "bp != NULL"); + assert(operation == SET_BREAKPOINT || operation == CLEAR_BREAKPOINT , "unknown breakpoint operation"); + } + + VMOp_Type type() const { return VMOp_ChangeBreakpoints; } + void doit(); + void oops_do(OopClosure* f); +}; + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiBreakpoints +// Used by : JvmtiCurrentBreakpoints +// Used by JVMTI methods: none directly +// Note: A Helper class +// +// JvmtiBreakpoints is a GrowableCache of JvmtiBreakpoint. +// All changes to the GrowableCache occur at a safepoint using VM_ChangeBreakpoints. +// +// Because _bps is only modified at safepoints, its possible to always use the +// cached byte code pointers from _bps without doing any synchronization (see JvmtiCurrentBreakpoints). +// +// It would be possible to make JvmtiBreakpoints a static class, but I've made it +// CHeap allocated to emphasize its similarity to JvmtiFramePops. +// + +class JvmtiBreakpoints : public CHeapObj { +private: + + JvmtiBreakpointCache _bps; + + // These should only be used by VM_ChangeBreakpoints + // to insure they only occur at safepoints. + // Todo: add checks for safepoint + friend class VM_ChangeBreakpoints; + void set_at_safepoint(JvmtiBreakpoint& bp); + void clear_at_safepoint(JvmtiBreakpoint& bp); + void clearall_at_safepoint(); + + static void do_element(GrowableElement *e); + +public: + JvmtiBreakpoints(void listener_fun(void *, address *)); + ~JvmtiBreakpoints(); + + int length(); + void oops_do(OopClosure* f); + void gc_epilogue(); + void print(); + + int set(JvmtiBreakpoint& bp); + int clear(JvmtiBreakpoint& bp); + void clearall_in_class_at_safepoint(klassOop klass); + void clearall(); +}; + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiCurrentBreakpoints +// +// A static wrapper class for the JvmtiBreakpoints that provides: +// 1. a fast inlined function to check if a byte code pointer is a breakpoint (is_breakpoint). +// 2. a function for lazily creating the JvmtiBreakpoints class (this is not strictly necessary, +// but I'm copying the code from JvmtiThreadState which needs to lazily initialize +// JvmtiFramePops). +// 3. An oops_do entry point for GC'ing the breakpoint array. +// + +class JvmtiCurrentBreakpoints : public AllStatic { + +private: + + // Current breakpoints, lazily initialized by get_jvmti_breakpoints(); + static JvmtiBreakpoints *_jvmti_breakpoints; + + // NULL terminated cache of byte-code pointers corresponding to current breakpoints. + // Updated only at safepoints (with listener_fun) when the cache is moved. + // It exists only to make is_breakpoint fast. + static address *_breakpoint_list; + static inline void set_breakpoint_list(address *breakpoint_list) { _breakpoint_list = breakpoint_list; } + static inline address *get_breakpoint_list() { return _breakpoint_list; } + + // Listener for the GrowableCache in _jvmti_breakpoints, updates _breakpoint_list. + static void listener_fun(void *this_obj, address *cache); + +public: + static void initialize(); + static void destroy(); + + // lazily create _jvmti_breakpoints and _breakpoint_list + static JvmtiBreakpoints& get_jvmti_breakpoints(); + + // quickly test whether the bcp matches a cached breakpoint in the list + static inline bool is_breakpoint(address bcp); + + static void oops_do(OopClosure* f); + static void gc_epilogue(); +}; + +// quickly test whether the bcp matches a cached breakpoint in the list +bool JvmtiCurrentBreakpoints::is_breakpoint(address bcp) { + address *bps = get_breakpoint_list(); + if (bps == NULL) return false; + for ( ; (*bps) != NULL; bps++) { + if ((*bps) == bcp) return true; + } + return false; +} + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiRawMonitor +// +// Used by JVMTI methods: All RawMonitor methods (CreateRawMonitor, EnterRawMonitor, etc.) +// +// Wrapper for ObjectMonitor class that saves the Monitor's name +// + +class JvmtiRawMonitor : public ObjectMonitor { +private: + int _magic; + char * _name; + // JVMTI_RM_MAGIC is set in contructor and unset in destructor. + enum { JVMTI_RM_MAGIC = (int)(('T' << 24) | ('I' << 16) | ('R' << 8) | 'M') }; + +public: + JvmtiRawMonitor(const char *name); + ~JvmtiRawMonitor(); + int magic() { return _magic; } + const char *get_name() { return _name; } + bool is_valid() { return _magic == JVMTI_RM_MAGIC; } +}; + +// Onload pending raw monitors +// Class is used to cache onload or onstart monitor enter +// which will transition into real monitor when +// VM is fully initialized. +class JvmtiPendingMonitors : public AllStatic { + +private: + static GrowableArray *_monitors; // Cache raw monitor enter + + inline static GrowableArray* monitors() { return _monitors; } + + static void dispose() { + delete monitors(); + } + +public: + static void enter(JvmtiRawMonitor *monitor) { + monitors()->append(monitor); + } + + static int count() { + return monitors()->length(); + } + + static void destroy(JvmtiRawMonitor *monitor) { + while (monitors()->contains(monitor)) { + monitors()->remove(monitor); + } + } + + // Return false if monitor is not found in the list. + static bool exit(JvmtiRawMonitor *monitor) { + if (monitors()->contains(monitor)) { + monitors()->remove(monitor); + return true; + } else { + return false; + } + } + + static void transition_raw_monitors(); +}; + + + +/////////////////////////////////////////////////////////////// +// The get/set local operations must only be done by the VM thread +// because the interpreter version needs to access oop maps, which can +// only safely be done by the VM thread +// +// I'm told that in 1.5 oop maps are now protected by a lock and +// we could get rid of the VM op +// However if the VM op is removed then the target thread must +// be suspended AND a lock will be needed to prevent concurrent +// setting of locals to the same java thread. This lock is needed +// to prevent compiledVFrames from trying to add deferred updates +// to the thread simultaneously. +// +class VM_GetOrSetLocal : public VM_Operation { +private: + JavaThread* _thread; + JavaThread* _calling_thread; + jint _depth; + jint _index; + BasicType _type; + jvalue _value; + javaVFrame* _jvf; + bool _set; + + jvmtiError _result; + + vframe* get_vframe(); + javaVFrame* get_java_vframe(); + bool check_slot_type(javaVFrame* vf); + +public: + // Constructor for non-object getter + VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type); + + // Constructor for object or non-object setter + VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type, jvalue value); + + // Constructor for object getter + VM_GetOrSetLocal(JavaThread* thread, JavaThread* calling_thread, jint depth, + int index); + + VMOp_Type type() const { return VMOp_GetOrSetLocal; } + jvalue value() { return _value; } + jvmtiError result() { return _result; } + + bool doit_prologue(); + void doit(); + bool allow_nested_vm_operations() const; + const char* name() const { return "get/set locals"; } + + // Check that the klass is assignable to a type with the given signature. + static bool is_assignable(const char* ty_sign, Klass* klass, Thread* thread); +}; + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiSuspendControl +// +// Convenience routines for suspending and resuming threads. +// +// All attempts by JVMTI to suspend and resume threads must go through the +// JvmtiSuspendControl interface. +// +// methods return true if successful +// +class JvmtiSuspendControl : public AllStatic { +public: + // suspend the thread, taking it to a safepoint + static bool suspend(JavaThread *java_thread); + // resume the thread + static bool resume(JavaThread *java_thread); + + static void print(); +}; + +// Utility macro that checks for NULL pointers: +#define NULL_CHECK(X, Y) if ((X) == NULL) { return (Y); } diff --git a/hotspot/src/share/vm/prims/jvmtiLib.xsl b/hotspot/src/share/vm/prims/jvmtiLib.xsl new file mode 100644 index 00000000000..99074de3a4b --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiLib.xsl @@ -0,0 +1,971 @@ + + + + + + + + + + + + . + + + + + + . + + + + + /* + + + */ + + + + + /* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */ + + + + + // AUTOMATICALLY GENERATED FILE - DO NOT EDIT + + + + + + , + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ; + + + + + If + + + + is + NULL, . + + + + + vmbuf|allocfieldbuf|struct as type of function parameter + + + + +

    + + + + +

    + + Agent passes in a pointer + + to + + + + + . + + + + +

    + + Agent passes in + + + a pointer + + + an array of + + + + + elements of + + + + + + + . + + + + +

    + Agent passes a pointer to a + + + + . + On return, the + + + + has been set. + + + + + +

    + Agent passes a pointer to a + + + * + + . + On return, the + + + * + + points to a newly allocated array + + + of size + + * + + + + + + of size + + + + + + + . The array should be freed with + Deallocate + . + + + + + + + +

    + Agent passes a pointer to a + + + ** + + . + On return, the + + + ** + + points to a newly allocated array + + + of size + + * + + + + + + of size + + + + + + + , each element of which is also newly allocated. + The array should be freed with + Deallocate + . + Each of the elements should be freed with + Deallocate + . + + + + + + + +

    + Agent passes an array + + large enough to hold + + + + elements + + of + + + + . The incoming values of the elements of the array are ignored. + On return, + + + * + + + of + + the elements are set. + + + + + + + +

    + + + + + + + + + + + + If + + + + is + NULL, the current thread is used. + + + + + + + + + + + + + + + + + + + + + The object + + s + + returned by + + + + + + are JNI local references and must be + + + is a JNI local reference and must be + + + managed. + + + + + + outptr, allocallocbuf, outbuf, vmbuf, allocbuf, inptr, inbuf or agentbuf as type of returned field: + of + + + + + + + + + + + + + + + + + + + + + + + The pointer + + s + + returned in the field + + + + of + + + + + + are newly allocated arrays. The arrays + + + is a newly allocated array. The array + + + should be freed with + Deallocate + . + + + + + + + + + + + + + The object + + s + + returned in the field + + + + of + + + + + + are JNI local references and must be + + + is a JNI local reference and must be + + + managed. + + + + + + + + + + + jint + + + + unsigned char + + + + + + + + + + + + * + + + + + + + + const + + * + + + + + ** + + + + + *** + + + + + + + + + # + + + + + + + + + #jint + + jint + + + + + + + # + + + + + + + + + + + + unsigned char + + + + ... + + + + + + + + + * + + + + const + + * + + + + + ** + + + + + *** + + + + + + + + unsigned char + + + + + + + + + * + + + + + typedef struct { + + + } + + ; + + + + + typedef union { + + + } + + ; + + + + + + typedef struct { + + + + + unsigned int : + + ; + + + + unsigned int : 16; + + + + unsigned int : 16; + + + + unsigned int : 16; + + + + unsigned int : 16; + + + + unsigned int : 16; + + + + unsigned int : 16; + + + } + + ; + + + + unsigned int + + : 1; + + + + + +typedef + + + + ; + + + + + + + ; + + + + enum { + + + + , + + + + + +} + + + + + + = + + + + + + + = + + + + + + + + + + + + + + /* + + : + + */ + + + jvmtiEvent + + + + ; + + + + + + /* + + */ + + + jvmtiEventReserved reserved + + ; + + + + + + + + + + + + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + JavaThread* + + + + + + + + + JvmtiRawMonitor * + + + + jint + + + + methodOop + + + + fieldDescriptor* + + + + + + oop + + + + + + + + + + + + + + + + + * + + + + + + + + const + + * + + + + + ** + + + + + *** + + + + + + + + + java_thread + + + + + + + + + rmonitor + + + + depth + + + + method_oop + + + + fdesc_ptr + + + + + + k_mirror + + + + + + + + + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &fdesc + + + + + + + + + NULL + + + + diff --git a/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp b/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp new file mode 100644 index 00000000000..7651a67b0fc --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp @@ -0,0 +1,457 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +# include "incls/_precompiled.incl" +# include "incls/_jvmtiManageCapabilities.cpp.incl" + +static const jint CAPA_SIZE = (JVMTI_INTERNAL_CAPABILITY_COUNT + 7) / 8; + + // capabilities which are always potentially available +jvmtiCapabilities JvmtiManageCapabilities::always_capabilities; + + // capabilities which are potentially available during OnLoad +jvmtiCapabilities JvmtiManageCapabilities::onload_capabilities; + + // capabilities which are always potentially available + // but to only one environment +jvmtiCapabilities JvmtiManageCapabilities::always_solo_capabilities; + + // capabilities which are potentially available during OnLoad + // but to only one environment +jvmtiCapabilities JvmtiManageCapabilities::onload_solo_capabilities; + + // remaining capabilities which are always potentially available + // but to only one environment +jvmtiCapabilities JvmtiManageCapabilities::always_solo_remaining_capabilities; + + // remaining capabilities which are potentially available during OnLoad + // but to only one environment +jvmtiCapabilities JvmtiManageCapabilities::onload_solo_remaining_capabilities; + + // all capabilities ever acquired +jvmtiCapabilities JvmtiManageCapabilities::acquired_capabilities; + +void JvmtiManageCapabilities::initialize() { + always_capabilities = init_always_capabilities(); + if (JvmtiEnv::get_phase() != JVMTI_PHASE_ONLOAD) { + recompute_always_capabilities(); + } + onload_capabilities = init_onload_capabilities(); + always_solo_capabilities = init_always_solo_capabilities(); + onload_solo_capabilities = init_onload_solo_capabilities(); + always_solo_remaining_capabilities = init_always_solo_capabilities(); + onload_solo_remaining_capabilities = init_onload_solo_capabilities(); + memset(&acquired_capabilities, 0, sizeof(acquired_capabilities)); +} + +// if the capability sets are initialized in the onload phase then +// it happens before class data sharing (CDS) is initialized. If it +// turns out that CDS gets disabled then we must adjust the always +// capabilities. To ensure a consistent view of the capabililties +// anything we add here should already be in the onload set. +void JvmtiManageCapabilities::recompute_always_capabilities() { + if (!UseSharedSpaces) { + jvmtiCapabilities jc = always_capabilities; + jc.can_generate_all_class_hook_events = 1; + always_capabilities = jc; + } +} + + +// corresponding init functions +jvmtiCapabilities JvmtiManageCapabilities::init_always_capabilities() { + jvmtiCapabilities jc; + + memset(&jc, 0, sizeof(jc)); + jc.can_get_bytecodes = 1; + jc.can_signal_thread = 1; + jc.can_get_source_file_name = 1; + jc.can_get_line_numbers = 1; + jc.can_get_synthetic_attribute = 1; + jc.can_get_monitor_info = 1; + jc.can_get_constant_pool = 1; + jc.can_generate_monitor_events = 1; + jc.can_generate_garbage_collection_events = 1; + jc.can_generate_compiled_method_load_events = 1; + jc.can_generate_native_method_bind_events = 1; + jc.can_generate_vm_object_alloc_events = 1; + if (os::is_thread_cpu_time_supported()) { + jc.can_get_current_thread_cpu_time = 1; + jc.can_get_thread_cpu_time = 1; + } + jc.can_redefine_classes = 1; + jc.can_redefine_any_class = 1; + jc.can_retransform_classes = 1; + jc.can_retransform_any_class = 1; + jc.can_set_native_method_prefix = 1; + jc.can_tag_objects = 1; + jc.can_generate_object_free_events = 1; + jc.can_generate_resource_exhaustion_heap_events = 1; + jc.can_generate_resource_exhaustion_threads_events = 1; + return jc; +} + +jvmtiCapabilities JvmtiManageCapabilities::init_onload_capabilities() { + jvmtiCapabilities jc; + + memset(&jc, 0, sizeof(jc)); + jc.can_pop_frame = 1; + jc.can_force_early_return = 1; + jc.can_get_source_debug_extension = 1; + jc.can_access_local_variables = 1; + jc.can_maintain_original_method_order = 1; + jc.can_generate_all_class_hook_events = 1; + jc.can_generate_single_step_events = 1; + jc.can_generate_exception_events = 1; + jc.can_generate_frame_pop_events = 1; + jc.can_generate_method_entry_events = 1; + jc.can_generate_method_exit_events = 1; + jc.can_get_owned_monitor_info = 1; + jc.can_get_owned_monitor_stack_depth_info = 1; + jc.can_get_current_contended_monitor = 1; + // jc.can_get_monitor_info = 1; + jc.can_tag_objects = 1; // TODO: this should have been removed + jc.can_generate_object_free_events = 1; // TODO: this should have been removed + return jc; +} + + +jvmtiCapabilities JvmtiManageCapabilities::init_always_solo_capabilities() { + jvmtiCapabilities jc; + + memset(&jc, 0, sizeof(jc)); + jc.can_suspend = 1; + return jc; +} + + +jvmtiCapabilities JvmtiManageCapabilities::init_onload_solo_capabilities() { + jvmtiCapabilities jc; + + memset(&jc, 0, sizeof(jc)); + jc.can_generate_field_modification_events = 1; + jc.can_generate_field_access_events = 1; + jc.can_generate_breakpoint_events = 1; + return jc; +} + + +jvmtiCapabilities *JvmtiManageCapabilities::either(const jvmtiCapabilities *a, const jvmtiCapabilities *b, + jvmtiCapabilities *result) { + char *ap = (char *)a; + char *bp = (char *)b; + char *resultp = (char *)result; + + for (int i = 0; i < CAPA_SIZE; ++i) { + *resultp++ = *ap++ | *bp++; + } + + return result; +} + + +jvmtiCapabilities *JvmtiManageCapabilities::both(const jvmtiCapabilities *a, const jvmtiCapabilities *b, + jvmtiCapabilities *result) { + char *ap = (char *)a; + char *bp = (char *)b; + char *resultp = (char *)result; + + for (int i = 0; i < CAPA_SIZE; ++i) { + *resultp++ = *ap++ & *bp++; + } + + return result; +} + + +jvmtiCapabilities *JvmtiManageCapabilities::exclude(const jvmtiCapabilities *a, const jvmtiCapabilities *b, + jvmtiCapabilities *result) { + char *ap = (char *)a; + char *bp = (char *)b; + char *resultp = (char *)result; + + for (int i = 0; i < CAPA_SIZE; ++i) { + *resultp++ = *ap++ & ~*bp++; + } + + return result; +} + + +bool JvmtiManageCapabilities::has_some(const jvmtiCapabilities *a) { + char *ap = (char *)a; + + for (int i = 0; i < CAPA_SIZE; ++i) { + if (*ap++ != 0) { + return true; + } + } + + return false; +} + + +void JvmtiManageCapabilities::copy_capabilities(const jvmtiCapabilities *from, jvmtiCapabilities *to) { + char *ap = (char *)from; + char *resultp = (char *)to; + + for (int i = 0; i < CAPA_SIZE; ++i) { + *resultp++ = *ap++; + } +} + + +void JvmtiManageCapabilities::get_potential_capabilities(const jvmtiCapabilities *current, + const jvmtiCapabilities *prohibited, + jvmtiCapabilities *result) { + // exclude prohibited capabilities, must be before adding current + exclude(&always_capabilities, prohibited, result); + + // must include current since it may possess solo capabilities and now prohibited + either(result, current, result); + + // add other remaining + either(result, &always_solo_remaining_capabilities, result); + + // if this is during OnLoad more capabilities are available + if (JvmtiEnv::get_phase() == JVMTI_PHASE_ONLOAD) { + either(result, &onload_capabilities, result); + either(result, &onload_solo_remaining_capabilities, result); + } +} + +jvmtiError JvmtiManageCapabilities::add_capabilities(const jvmtiCapabilities *current, + const jvmtiCapabilities *prohibited, + const jvmtiCapabilities *desired, + jvmtiCapabilities *result) { + // check that the capabilities being added are potential capabilities + jvmtiCapabilities temp; + get_potential_capabilities(current, prohibited, &temp); + if (has_some(exclude(desired, &temp, &temp))) { + return JVMTI_ERROR_NOT_AVAILABLE; + } + + // add to the set of ever acquired capabilities + either(&acquired_capabilities, desired, &acquired_capabilities); + + // onload capabilities that got added are now permanent - so, also remove from onload + both(&onload_capabilities, desired, &temp); + either(&always_capabilities, &temp, &always_capabilities); + exclude(&onload_capabilities, &temp, &onload_capabilities); + + // same for solo capabilities (transferred capabilities in the remaining sets handled as part of standard grab - below) + both(&onload_solo_capabilities, desired, &temp); + either(&always_solo_capabilities, &temp, &always_solo_capabilities); + exclude(&onload_solo_capabilities, &temp, &onload_solo_capabilities); + + // remove solo capabilities that are now taken + exclude(&always_solo_remaining_capabilities, desired, &always_solo_remaining_capabilities); + exclude(&onload_solo_remaining_capabilities, desired, &onload_solo_remaining_capabilities); + + // return the result + either(current, desired, result); + + update(); + + return JVMTI_ERROR_NONE; +} + + +void JvmtiManageCapabilities::relinquish_capabilities(const jvmtiCapabilities *current, + const jvmtiCapabilities *unwanted, + jvmtiCapabilities *result) { + jvmtiCapabilities to_trash; + jvmtiCapabilities temp; + + // can't give up what you don't have + both(current, unwanted, &to_trash); + + // restore solo capabilities but only those that belong + either(&always_solo_remaining_capabilities, both(&always_solo_capabilities, &to_trash, &temp), + &always_solo_remaining_capabilities); + either(&onload_solo_remaining_capabilities, both(&onload_solo_capabilities, &to_trash, &temp), + &onload_solo_remaining_capabilities); + + update(); + + // return the result + exclude(current, unwanted, result); +} + + +void JvmtiManageCapabilities::update() { + jvmtiCapabilities avail; + + // all capabilities + either(&always_capabilities, &always_solo_capabilities, &avail); + + bool interp_events = + avail.can_generate_field_access_events || + avail.can_generate_field_modification_events || + avail.can_generate_single_step_events || + avail.can_generate_frame_pop_events || + avail.can_generate_method_entry_events || + avail.can_generate_method_exit_events; + bool enter_all_methods = + interp_events || + avail.can_generate_breakpoint_events; + UseFastEmptyMethods = !enter_all_methods; + UseFastAccessorMethods = !enter_all_methods; + + if (avail.can_generate_breakpoint_events) { + RewriteFrequentPairs = false; + } + + // If can_redefine_classes is enabled in the onload phase then we know that the + // dependency information recorded by the compiler is complete. + if ((avail.can_redefine_classes || avail.can_retransform_classes) && + JvmtiEnv::get_phase() == JVMTI_PHASE_ONLOAD) { + JvmtiExport::set_all_dependencies_are_recorded(true); + } + + JvmtiExport::set_can_get_source_debug_extension(avail.can_get_source_debug_extension); + JvmtiExport::set_can_examine_or_deopt_anywhere( + avail.can_generate_breakpoint_events || + interp_events || + avail.can_redefine_classes || + avail.can_retransform_classes || + avail.can_access_local_variables || + avail.can_get_owned_monitor_info || + avail.can_get_current_contended_monitor || + avail.can_get_monitor_info || + avail.can_get_owned_monitor_stack_depth_info); + JvmtiExport::set_can_maintain_original_method_order(avail.can_maintain_original_method_order); + JvmtiExport::set_can_post_interpreter_events(interp_events); + JvmtiExport::set_can_hotswap_or_post_breakpoint( + avail.can_generate_breakpoint_events || + avail.can_redefine_classes || + avail.can_retransform_classes); + JvmtiExport::set_can_modify_any_class( + avail.can_generate_breakpoint_events || + avail.can_generate_all_class_hook_events); + JvmtiExport::set_can_walk_any_space( + avail.can_tag_objects); // disable sharing in onload phase + JvmtiExport::set_can_access_local_variables( + avail.can_access_local_variables || + avail.can_redefine_classes || + avail.can_retransform_classes); + JvmtiExport::set_can_post_exceptions( + avail.can_generate_exception_events || + avail.can_generate_frame_pop_events || + avail.can_generate_method_exit_events); + JvmtiExport::set_can_post_breakpoint(avail.can_generate_breakpoint_events); + JvmtiExport::set_can_post_field_access(avail.can_generate_field_access_events); + JvmtiExport::set_can_post_field_modification(avail.can_generate_field_modification_events); + JvmtiExport::set_can_post_method_entry(avail.can_generate_method_entry_events); + JvmtiExport::set_can_post_method_exit(avail.can_generate_method_exit_events || + avail.can_generate_frame_pop_events); + JvmtiExport::set_can_pop_frame(avail.can_pop_frame); + JvmtiExport::set_can_force_early_return(avail.can_force_early_return); + JvmtiExport::set_should_clean_up_heap_objects(avail.can_generate_breakpoint_events); +} + +#ifndef PRODUCT + +void JvmtiManageCapabilities:: print(const jvmtiCapabilities* cap) { + tty->print_cr("----- capabilities -----"); + if (cap->can_tag_objects) + tty->print_cr("can_tag_objects"); + if (cap->can_generate_field_modification_events) + tty->print_cr("can_generate_field_modification_events"); + if (cap->can_generate_field_access_events) + tty->print_cr("can_generate_field_access_events"); + if (cap->can_get_bytecodes) + tty->print_cr("can_get_bytecodes"); + if (cap->can_get_synthetic_attribute) + tty->print_cr("can_get_synthetic_attribute"); + if (cap->can_get_owned_monitor_info) + tty->print_cr("can_get_owned_monitor_info"); + if (cap->can_get_current_contended_monitor) + tty->print_cr("can_get_current_contended_monitor"); + if (cap->can_get_monitor_info) + tty->print_cr("can_get_monitor_info"); + if (cap->can_get_constant_pool) + tty->print_cr("can_get_constant_pool"); + if (cap->can_pop_frame) + tty->print_cr("can_pop_frame"); + if (cap->can_force_early_return) + tty->print_cr("can_force_early_return"); + if (cap->can_redefine_classes) + tty->print_cr("can_redefine_classes"); + if (cap->can_retransform_classes) + tty->print_cr("can_retransform_classes"); + if (cap->can_signal_thread) + tty->print_cr("can_signal_thread"); + if (cap->can_get_source_file_name) + tty->print_cr("can_get_source_file_name"); + if (cap->can_get_line_numbers) + tty->print_cr("can_get_line_numbers"); + if (cap->can_get_source_debug_extension) + tty->print_cr("can_get_source_debug_extension"); + if (cap->can_access_local_variables) + tty->print_cr("can_access_local_variables"); + if (cap->can_maintain_original_method_order) + tty->print_cr("can_maintain_original_method_order"); + if (cap->can_generate_single_step_events) + tty->print_cr("can_generate_single_step_events"); + if (cap->can_generate_exception_events) + tty->print_cr("can_generate_exception_events"); + if (cap->can_generate_frame_pop_events) + tty->print_cr("can_generate_frame_pop_events"); + if (cap->can_generate_breakpoint_events) + tty->print_cr("can_generate_breakpoint_events"); + if (cap->can_suspend) + tty->print_cr("can_suspend"); + if (cap->can_redefine_any_class ) + tty->print_cr("can_redefine_any_class"); + if (cap->can_retransform_any_class ) + tty->print_cr("can_retransform_any_class"); + if (cap->can_get_current_thread_cpu_time) + tty->print_cr("can_get_current_thread_cpu_time"); + if (cap->can_get_thread_cpu_time) + tty->print_cr("can_get_thread_cpu_time"); + if (cap->can_generate_method_entry_events) + tty->print_cr("can_generate_method_entry_events"); + if (cap->can_generate_method_exit_events) + tty->print_cr("can_generate_method_exit_events"); + if (cap->can_generate_all_class_hook_events) + tty->print_cr("can_generate_all_class_hook_events"); + if (cap->can_generate_compiled_method_load_events) + tty->print_cr("can_generate_compiled_method_load_events"); + if (cap->can_generate_monitor_events) + tty->print_cr("can_generate_monitor_events"); + if (cap->can_generate_vm_object_alloc_events) + tty->print_cr("can_generate_vm_object_alloc_events"); + if (cap->can_generate_native_method_bind_events) + tty->print_cr("can_generate_native_method_bind_events"); + if (cap->can_generate_garbage_collection_events) + tty->print_cr("can_generate_garbage_collection_events"); + if (cap->can_generate_object_free_events) + tty->print_cr("can_generate_object_free_events"); + if (cap->can_generate_resource_exhaustion_heap_events) + tty->print_cr("can_generate_resource_exhaustion_heap_events"); + if (cap->can_generate_resource_exhaustion_threads_events) + tty->print_cr("can_generate_resource_exhaustion_threads_events"); +} + +#endif diff --git a/hotspot/src/share/vm/prims/jvmtiManageCapabilities.hpp b/hotspot/src/share/vm/prims/jvmtiManageCapabilities.hpp new file mode 100644 index 00000000000..b4615223627 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiManageCapabilities.hpp @@ -0,0 +1,87 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _JAVA_JVMTI_MANAGE_CAPABILITIES_H_ +#define _JAVA_JVMTI_MANAGE_CAPABILITIES_H_ + + + +class JvmtiManageCapabilities : public AllStatic { + +private: + + // these four capabilities sets represent all potentially + // available capabilities. They are disjoint, covering + // the four cases: (OnLoad vs OnLoad+live phase) X + // (one environment vs any environment). + static jvmtiCapabilities always_capabilities; + static jvmtiCapabilities onload_capabilities; + static jvmtiCapabilities always_solo_capabilities; + static jvmtiCapabilities onload_solo_capabilities; + + // solo capabilities that have not been grabbed + static jvmtiCapabilities always_solo_remaining_capabilities; + static jvmtiCapabilities onload_solo_remaining_capabilities; + + // all capabilities ever acquired + static jvmtiCapabilities acquired_capabilities; + + // basic intenal operations + static jvmtiCapabilities *either(const jvmtiCapabilities *a, const jvmtiCapabilities *b, jvmtiCapabilities *result); + static jvmtiCapabilities *both(const jvmtiCapabilities *a, const jvmtiCapabilities *b, jvmtiCapabilities *result); + static jvmtiCapabilities *exclude(const jvmtiCapabilities *a, const jvmtiCapabilities *b, jvmtiCapabilities *result); + static bool has_some(const jvmtiCapabilities *a); + static void update(); + + // init functions + static jvmtiCapabilities init_always_capabilities(); + static jvmtiCapabilities init_onload_capabilities(); + static jvmtiCapabilities init_always_solo_capabilities(); + static jvmtiCapabilities init_onload_solo_capabilities(); + +public: + static void initialize(); + + // may have to adjust always capabilities when VM initialization has completed + static void recompute_always_capabilities(); + + // queries and actions + static void get_potential_capabilities(const jvmtiCapabilities *current, + const jvmtiCapabilities *prohibited, + jvmtiCapabilities *result); + static jvmtiError add_capabilities(const jvmtiCapabilities *current, + const jvmtiCapabilities *prohibited, + const jvmtiCapabilities *desired, + jvmtiCapabilities *result); + static void relinquish_capabilities(const jvmtiCapabilities *current, + const jvmtiCapabilities *unwanted, + jvmtiCapabilities *result); + static void copy_capabilities(const jvmtiCapabilities *from, jvmtiCapabilities *to); + +#ifndef PRODUCT + static void print(const jvmtiCapabilities* caps); +#endif +}; + +#endif /* _JAVA_JVMTI_MANAGE_CAPABILITIES_H_ */ diff --git a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp new file mode 100644 index 00000000000..bd8fe2b356b --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp @@ -0,0 +1,3398 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiRedefineClasses.cpp.incl" + + +objArrayOop VM_RedefineClasses::_old_methods = NULL; +objArrayOop VM_RedefineClasses::_new_methods = NULL; +methodOop* VM_RedefineClasses::_matching_old_methods = NULL; +methodOop* VM_RedefineClasses::_matching_new_methods = NULL; +methodOop* VM_RedefineClasses::_deleted_methods = NULL; +methodOop* VM_RedefineClasses::_added_methods = NULL; +int VM_RedefineClasses::_matching_methods_length = 0; +int VM_RedefineClasses::_deleted_methods_length = 0; +int VM_RedefineClasses::_added_methods_length = 0; +klassOop VM_RedefineClasses::_the_class_oop = NULL; + + +VM_RedefineClasses::VM_RedefineClasses(jint class_count, + const jvmtiClassDefinition *class_defs, + JvmtiClassLoadKind class_load_kind) { + _class_count = class_count; + _class_defs = class_defs; + _class_load_kind = class_load_kind; + _res = JVMTI_ERROR_NONE; +} + +bool VM_RedefineClasses::doit_prologue() { + if (_class_count == 0) { + _res = JVMTI_ERROR_NONE; + return false; + } + if (_class_defs == NULL) { + _res = JVMTI_ERROR_NULL_POINTER; + return false; + } + for (int i = 0; i < _class_count; i++) { + if (_class_defs[i].klass == NULL) { + _res = JVMTI_ERROR_INVALID_CLASS; + return false; + } + if (_class_defs[i].class_byte_count == 0) { + _res = JVMTI_ERROR_INVALID_CLASS_FORMAT; + return false; + } + if (_class_defs[i].class_bytes == NULL) { + _res = JVMTI_ERROR_NULL_POINTER; + return false; + } + } + + // Start timer after all the sanity checks; not quite accurate, but + // better than adding a bunch of stop() calls. + RC_TIMER_START(_timer_vm_op_prologue); + + // We first load new class versions in the prologue, because somewhere down the + // call chain it is required that the current thread is a Java thread. + _res = load_new_class_versions(Thread::current()); + if (_res != JVMTI_ERROR_NONE) { + // Free os::malloc allocated memory in load_new_class_version. + os::free(_scratch_classes); + RC_TIMER_STOP(_timer_vm_op_prologue); + return false; + } + + RC_TIMER_STOP(_timer_vm_op_prologue); + return true; +} + +void VM_RedefineClasses::doit() { + Thread *thread = Thread::current(); + + if (UseSharedSpaces) { + // Sharing is enabled so we remap the shared readonly space to + // shared readwrite, private just in case we need to redefine + // a shared class. We do the remap during the doit() phase of + // the safepoint to be safer. + if (!CompactingPermGenGen::remap_shared_readonly_as_readwrite()) { + RC_TRACE_WITH_THREAD(0x00000001, thread, + ("failed to remap shared readonly space to readwrite, private")); + _res = JVMTI_ERROR_INTERNAL; + return; + } + } + + for (int i = 0; i < _class_count; i++) { + redefine_single_class(_class_defs[i].klass, _scratch_classes[i], thread); + } + // Disable any dependent concurrent compilations + SystemDictionary::notice_modification(); + + // Set flag indicating that some invariants are no longer true. + // See jvmtiExport.hpp for detailed explanation. + JvmtiExport::set_has_redefined_a_class(); + +#ifdef ASSERT + SystemDictionary::classes_do(check_class, thread); +#endif +} + +void VM_RedefineClasses::doit_epilogue() { + // Free os::malloc allocated memory. + // The memory allocated in redefine will be free'ed in next VM operation. + os::free(_scratch_classes); + + if (RC_TRACE_ENABLED(0x00000004)) { + // Used to have separate timers for "doit" and "all", but the timer + // overhead skewed the measurements. + jlong doit_time = _timer_rsc_phase1.milliseconds() + + _timer_rsc_phase2.milliseconds(); + jlong all_time = _timer_vm_op_prologue.milliseconds() + doit_time; + + RC_TRACE(0x00000004, ("vm_op: all=" UINT64_FORMAT + " prologue=" UINT64_FORMAT " doit=" UINT64_FORMAT, all_time, + _timer_vm_op_prologue.milliseconds(), doit_time)); + RC_TRACE(0x00000004, + ("redefine_single_class: phase1=" UINT64_FORMAT " phase2=" UINT64_FORMAT, + _timer_rsc_phase1.milliseconds(), _timer_rsc_phase2.milliseconds())); + } +} + +bool VM_RedefineClasses::is_modifiable_class(oop klass_mirror) { + // classes for primitives cannot be redefined + if (java_lang_Class::is_primitive(klass_mirror)) { + return false; + } + klassOop the_class_oop = java_lang_Class::as_klassOop(klass_mirror); + // classes for arrays cannot be redefined + if (the_class_oop == NULL || !Klass::cast(the_class_oop)->oop_is_instance()) { + return false; + } + return true; +} + +// Append the current entry at scratch_i in scratch_cp to *merge_cp_p +// where the end of *merge_cp_p is specified by *merge_cp_length_p. For +// direct CP entries, there is just the current entry to append. For +// indirect and double-indirect CP entries, there are zero or more +// referenced CP entries along with the current entry to append. +// Indirect and double-indirect CP entries are handled by recursive +// calls to append_entry() as needed. The referenced CP entries are +// always appended to *merge_cp_p before the referee CP entry. These +// referenced CP entries may already exist in *merge_cp_p in which case +// there is nothing extra to append and only the current entry is +// appended. +void VM_RedefineClasses::append_entry(constantPoolHandle scratch_cp, + int scratch_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p, + TRAPS) { + + // append is different depending on entry tag type + switch (scratch_cp->tag_at(scratch_i).value()) { + + // The old verifier is implemented outside the VM. It loads classes, + // but does not resolve constant pool entries directly so we never + // see Class entries here with the old verifier. Similarly the old + // verifier does not like Class entries in the input constant pool. + // The split-verifier is implemented in the VM so it can optionally + // and directly resolve constant pool entries to load classes. The + // split-verifier can accept either Class entries or UnresolvedClass + // entries in the input constant pool. We revert the appended copy + // back to UnresolvedClass so that either verifier will be happy + // with the constant pool entry. + case JVM_CONSTANT_Class: + { + // revert the copy to JVM_CONSTANT_UnresolvedClass + (*merge_cp_p)->unresolved_klass_at_put(*merge_cp_length_p, + scratch_cp->klass_name_at(scratch_i)); + + if (scratch_i != *merge_cp_length_p) { + // The new entry in *merge_cp_p is at a different index than + // the new entry in scratch_cp so we need to map the index values. + map_index(scratch_cp, scratch_i, *merge_cp_length_p); + } + (*merge_cp_length_p)++; + } break; + + // these are direct CP entries so they can be directly appended, + // but double and long take two constant pool entries + case JVM_CONSTANT_Double: // fall through + case JVM_CONSTANT_Long: + { + scratch_cp->copy_entry_to(scratch_i, *merge_cp_p, *merge_cp_length_p, + THREAD); + + if (scratch_i != *merge_cp_length_p) { + // The new entry in *merge_cp_p is at a different index than + // the new entry in scratch_cp so we need to map the index values. + map_index(scratch_cp, scratch_i, *merge_cp_length_p); + } + (*merge_cp_length_p) += 2; + } break; + + // these are direct CP entries so they can be directly appended + case JVM_CONSTANT_Float: // fall through + case JVM_CONSTANT_Integer: // fall through + case JVM_CONSTANT_Utf8: // fall through + + // This was an indirect CP entry, but it has been changed into + // an interned string so this entry can be directly appended. + case JVM_CONSTANT_String: // fall through + + // These were indirect CP entries, but they have been changed into + // symbolOops so these entries can be directly appended. + case JVM_CONSTANT_UnresolvedClass: // fall through + case JVM_CONSTANT_UnresolvedString: + { + scratch_cp->copy_entry_to(scratch_i, *merge_cp_p, *merge_cp_length_p, + THREAD); + + if (scratch_i != *merge_cp_length_p) { + // The new entry in *merge_cp_p is at a different index than + // the new entry in scratch_cp so we need to map the index values. + map_index(scratch_cp, scratch_i, *merge_cp_length_p); + } + (*merge_cp_length_p)++; + } break; + + // this is an indirect CP entry so it needs special handling + case JVM_CONSTANT_NameAndType: + { + int name_ref_i = scratch_cp->name_ref_index_at(scratch_i); + int new_name_ref_i = 0; + bool match = (name_ref_i < *merge_cp_length_p) && + scratch_cp->compare_entry_to(name_ref_i, *merge_cp_p, name_ref_i, + THREAD); + if (!match) { + // forward reference in *merge_cp_p or not a direct match + + int found_i = scratch_cp->find_matching_entry(name_ref_i, *merge_cp_p, + THREAD); + if (found_i != 0) { + guarantee(found_i != name_ref_i, + "compare_entry_to() and find_matching_entry() do not agree"); + + // Found a matching entry somewhere else in *merge_cp_p so + // just need a mapping entry. + new_name_ref_i = found_i; + map_index(scratch_cp, name_ref_i, found_i); + } else { + // no match found so we have to append this entry to *merge_cp_p + append_entry(scratch_cp, name_ref_i, merge_cp_p, merge_cp_length_p, + THREAD); + // The above call to append_entry() can only append one entry + // so the post call query of *merge_cp_length_p is only for + // the sake of consistency. + new_name_ref_i = *merge_cp_length_p - 1; + } + } + + int signature_ref_i = scratch_cp->signature_ref_index_at(scratch_i); + int new_signature_ref_i = 0; + match = (signature_ref_i < *merge_cp_length_p) && + scratch_cp->compare_entry_to(signature_ref_i, *merge_cp_p, + signature_ref_i, THREAD); + if (!match) { + // forward reference in *merge_cp_p or not a direct match + + int found_i = scratch_cp->find_matching_entry(signature_ref_i, + *merge_cp_p, THREAD); + if (found_i != 0) { + guarantee(found_i != signature_ref_i, + "compare_entry_to() and find_matching_entry() do not agree"); + + // Found a matching entry somewhere else in *merge_cp_p so + // just need a mapping entry. + new_signature_ref_i = found_i; + map_index(scratch_cp, signature_ref_i, found_i); + } else { + // no match found so we have to append this entry to *merge_cp_p + append_entry(scratch_cp, signature_ref_i, merge_cp_p, + merge_cp_length_p, THREAD); + // The above call to append_entry() can only append one entry + // so the post call query of *merge_cp_length_p is only for + // the sake of consistency. + new_signature_ref_i = *merge_cp_length_p - 1; + } + } + + // If the referenced entries already exist in *merge_cp_p, then + // both new_name_ref_i and new_signature_ref_i will both be 0. + // In that case, all we are appending is the current entry. + if (new_name_ref_i == 0) { + new_name_ref_i = name_ref_i; + } else { + RC_TRACE(0x00080000, + ("NameAndType entry@%d name_ref_index change: %d to %d", + *merge_cp_length_p, name_ref_i, new_name_ref_i)); + } + if (new_signature_ref_i == 0) { + new_signature_ref_i = signature_ref_i; + } else { + RC_TRACE(0x00080000, + ("NameAndType entry@%d signature_ref_index change: %d to %d", + *merge_cp_length_p, signature_ref_i, new_signature_ref_i)); + } + + (*merge_cp_p)->name_and_type_at_put(*merge_cp_length_p, + new_name_ref_i, new_signature_ref_i); + if (scratch_i != *merge_cp_length_p) { + // The new entry in *merge_cp_p is at a different index than + // the new entry in scratch_cp so we need to map the index values. + map_index(scratch_cp, scratch_i, *merge_cp_length_p); + } + (*merge_cp_length_p)++; + } break; + + // this is a double-indirect CP entry so it needs special handling + case JVM_CONSTANT_Fieldref: // fall through + case JVM_CONSTANT_InterfaceMethodref: // fall through + case JVM_CONSTANT_Methodref: + { + int klass_ref_i = scratch_cp->uncached_klass_ref_index_at(scratch_i); + int new_klass_ref_i = 0; + bool match = (klass_ref_i < *merge_cp_length_p) && + scratch_cp->compare_entry_to(klass_ref_i, *merge_cp_p, klass_ref_i, + THREAD); + if (!match) { + // forward reference in *merge_cp_p or not a direct match + + int found_i = scratch_cp->find_matching_entry(klass_ref_i, *merge_cp_p, + THREAD); + if (found_i != 0) { + guarantee(found_i != klass_ref_i, + "compare_entry_to() and find_matching_entry() do not agree"); + + // Found a matching entry somewhere else in *merge_cp_p so + // just need a mapping entry. + new_klass_ref_i = found_i; + map_index(scratch_cp, klass_ref_i, found_i); + } else { + // no match found so we have to append this entry to *merge_cp_p + append_entry(scratch_cp, klass_ref_i, merge_cp_p, merge_cp_length_p, + THREAD); + // The above call to append_entry() can only append one entry + // so the post call query of *merge_cp_length_p is only for + // the sake of consistency. Without the optimization where we + // use JVM_CONSTANT_UnresolvedClass, then up to two entries + // could be appended. + new_klass_ref_i = *merge_cp_length_p - 1; + } + } + + int name_and_type_ref_i = + scratch_cp->uncached_name_and_type_ref_index_at(scratch_i); + int new_name_and_type_ref_i = 0; + match = (name_and_type_ref_i < *merge_cp_length_p) && + scratch_cp->compare_entry_to(name_and_type_ref_i, *merge_cp_p, + name_and_type_ref_i, THREAD); + if (!match) { + // forward reference in *merge_cp_p or not a direct match + + int found_i = scratch_cp->find_matching_entry(name_and_type_ref_i, + *merge_cp_p, THREAD); + if (found_i != 0) { + guarantee(found_i != name_and_type_ref_i, + "compare_entry_to() and find_matching_entry() do not agree"); + + // Found a matching entry somewhere else in *merge_cp_p so + // just need a mapping entry. + new_name_and_type_ref_i = found_i; + map_index(scratch_cp, name_and_type_ref_i, found_i); + } else { + // no match found so we have to append this entry to *merge_cp_p + append_entry(scratch_cp, name_and_type_ref_i, merge_cp_p, + merge_cp_length_p, THREAD); + // The above call to append_entry() can append more than + // one entry so the post call query of *merge_cp_length_p + // is required in order to get the right index for the + // JVM_CONSTANT_NameAndType entry. + new_name_and_type_ref_i = *merge_cp_length_p - 1; + } + } + + // If the referenced entries already exist in *merge_cp_p, then + // both new_klass_ref_i and new_name_and_type_ref_i will both be + // 0. In that case, all we are appending is the current entry. + if (new_klass_ref_i == 0) { + new_klass_ref_i = klass_ref_i; + } + if (new_name_and_type_ref_i == 0) { + new_name_and_type_ref_i = name_and_type_ref_i; + } + + const char *entry_name; + switch (scratch_cp->tag_at(scratch_i).value()) { + case JVM_CONSTANT_Fieldref: + entry_name = "Fieldref"; + (*merge_cp_p)->field_at_put(*merge_cp_length_p, new_klass_ref_i, + new_name_and_type_ref_i); + break; + case JVM_CONSTANT_InterfaceMethodref: + entry_name = "IFMethodref"; + (*merge_cp_p)->interface_method_at_put(*merge_cp_length_p, + new_klass_ref_i, new_name_and_type_ref_i); + break; + case JVM_CONSTANT_Methodref: + entry_name = "Methodref"; + (*merge_cp_p)->method_at_put(*merge_cp_length_p, new_klass_ref_i, + new_name_and_type_ref_i); + break; + default: + guarantee(false, "bad switch"); + break; + } + + if (klass_ref_i != new_klass_ref_i) { + RC_TRACE(0x00080000, ("%s entry@%d class_index changed: %d to %d", + entry_name, *merge_cp_length_p, klass_ref_i, new_klass_ref_i)); + } + if (name_and_type_ref_i != new_name_and_type_ref_i) { + RC_TRACE(0x00080000, + ("%s entry@%d name_and_type_index changed: %d to %d", + entry_name, *merge_cp_length_p, name_and_type_ref_i, + new_name_and_type_ref_i)); + } + + if (scratch_i != *merge_cp_length_p) { + // The new entry in *merge_cp_p is at a different index than + // the new entry in scratch_cp so we need to map the index values. + map_index(scratch_cp, scratch_i, *merge_cp_length_p); + } + (*merge_cp_length_p)++; + } break; + + // At this stage, Class or UnresolvedClass could be here, but not + // ClassIndex + case JVM_CONSTANT_ClassIndex: // fall through + + // Invalid is used as the tag for the second constant pool entry + // occupied by JVM_CONSTANT_Double or JVM_CONSTANT_Long. It should + // not be seen by itself. + case JVM_CONSTANT_Invalid: // fall through + + // At this stage, String or UnresolvedString could be here, but not + // StringIndex + case JVM_CONSTANT_StringIndex: // fall through + + // At this stage JVM_CONSTANT_UnresolvedClassInError should not be + // here + case JVM_CONSTANT_UnresolvedClassInError: // fall through + + default: + { + // leave a breadcrumb + jbyte bad_value = scratch_cp->tag_at(scratch_i).value(); + ShouldNotReachHere(); + } break; + } // end switch tag value +} // end append_entry() + + +void VM_RedefineClasses::swap_all_method_annotations(int i, int j, instanceKlassHandle scratch_class) { + typeArrayOop save; + + save = scratch_class->get_method_annotations_of(i); + scratch_class->set_method_annotations_of(i, scratch_class->get_method_annotations_of(j)); + scratch_class->set_method_annotations_of(j, save); + + save = scratch_class->get_method_parameter_annotations_of(i); + scratch_class->set_method_parameter_annotations_of(i, scratch_class->get_method_parameter_annotations_of(j)); + scratch_class->set_method_parameter_annotations_of(j, save); + + save = scratch_class->get_method_default_annotations_of(i); + scratch_class->set_method_default_annotations_of(i, scratch_class->get_method_default_annotations_of(j)); + scratch_class->set_method_default_annotations_of(j, save); +} + + +jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions( + instanceKlassHandle the_class, + instanceKlassHandle scratch_class) { + int i; + + // Check superclasses, or rather their names, since superclasses themselves can be + // requested to replace. + // Check for NULL superclass first since this might be java.lang.Object + if (the_class->super() != scratch_class->super() && + (the_class->super() == NULL || scratch_class->super() == NULL || + Klass::cast(the_class->super())->name() != + Klass::cast(scratch_class->super())->name())) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED; + } + + // Check if the number, names and order of directly implemented interfaces are the same. + // I think in principle we should just check if the sets of names of directly implemented + // interfaces are the same, i.e. the order of declaration (which, however, if changed in the + // .java file, also changes in .class file) should not matter. However, comparing sets is + // technically a bit more difficult, and, more importantly, I am not sure at present that the + // order of interfaces does not matter on the implementation level, i.e. that the VM does not + // rely on it somewhere. + objArrayOop k_interfaces = the_class->local_interfaces(); + objArrayOop k_new_interfaces = scratch_class->local_interfaces(); + int n_intfs = k_interfaces->length(); + if (n_intfs != k_new_interfaces->length()) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED; + } + for (i = 0; i < n_intfs; i++) { + if (Klass::cast((klassOop) k_interfaces->obj_at(i))->name() != + Klass::cast((klassOop) k_new_interfaces->obj_at(i))->name()) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED; + } + } + + // Check whether class is in the error init state. + if (the_class->is_in_error_state()) { + // TBD #5057930: special error code is needed in 1.6 + return JVMTI_ERROR_INVALID_CLASS; + } + + // Check whether class modifiers are the same. + jushort old_flags = (jushort) the_class->access_flags().get_flags(); + jushort new_flags = (jushort) scratch_class->access_flags().get_flags(); + if (old_flags != new_flags) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED; + } + + // Check if the number, names, types and order of fields declared in these classes + // are the same. + typeArrayOop k_old_fields = the_class->fields(); + typeArrayOop k_new_fields = scratch_class->fields(); + int n_fields = k_old_fields->length(); + if (n_fields != k_new_fields->length()) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED; + } + + for (i = 0; i < n_fields; i += instanceKlass::next_offset) { + // access + old_flags = k_old_fields->ushort_at(i + instanceKlass::access_flags_offset); + new_flags = k_new_fields->ushort_at(i + instanceKlass::access_flags_offset); + if ((old_flags ^ new_flags) & JVM_RECOGNIZED_FIELD_MODIFIERS) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED; + } + // offset + if (k_old_fields->short_at(i + instanceKlass::low_offset) != + k_new_fields->short_at(i + instanceKlass::low_offset) || + k_old_fields->short_at(i + instanceKlass::high_offset) != + k_new_fields->short_at(i + instanceKlass::high_offset)) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED; + } + // name and signature + jshort name_index = k_old_fields->short_at(i + instanceKlass::name_index_offset); + jshort sig_index = k_old_fields->short_at(i +instanceKlass::signature_index_offset); + symbolOop name_sym1 = the_class->constants()->symbol_at(name_index); + symbolOop sig_sym1 = the_class->constants()->symbol_at(sig_index); + name_index = k_new_fields->short_at(i + instanceKlass::name_index_offset); + sig_index = k_new_fields->short_at(i + instanceKlass::signature_index_offset); + symbolOop name_sym2 = scratch_class->constants()->symbol_at(name_index); + symbolOop sig_sym2 = scratch_class->constants()->symbol_at(sig_index); + if (name_sym1 != name_sym2 || sig_sym1 != sig_sym2) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED; + } + } + + // Do a parallel walk through the old and new methods. Detect + // cases where they match (exist in both), have been added in + // the new methods, or have been deleted (exist only in the + // old methods). The class file parser places methods in order + // by method name, but does not order overloaded methods by + // signature. In order to determine what fate befell the methods, + // this code places the overloaded new methods that have matching + // old methods in the same order as the old methods and places + // new overloaded methods at the end of overloaded methods of + // that name. The code for this order normalization is adapted + // from the algorithm used in instanceKlass::find_method(). + // Since we are swapping out of order entries as we find them, + // we only have to search forward through the overloaded methods. + // Methods which are added and have the same name as an existing + // method (but different signature) will be put at the end of + // the methods with that name, and the name mismatch code will + // handle them. + objArrayHandle k_old_methods(the_class->methods()); + objArrayHandle k_new_methods(scratch_class->methods()); + int n_old_methods = k_old_methods->length(); + int n_new_methods = k_new_methods->length(); + + int ni = 0; + int oi = 0; + while (true) { + methodOop k_old_method; + methodOop k_new_method; + enum { matched, added, deleted, undetermined } method_was = undetermined; + + if (oi >= n_old_methods) { + if (ni >= n_new_methods) { + break; // we've looked at everything, done + } + // New method at the end + k_new_method = (methodOop) k_new_methods->obj_at(ni); + method_was = added; + } else if (ni >= n_new_methods) { + // Old method, at the end, is deleted + k_old_method = (methodOop) k_old_methods->obj_at(oi); + method_was = deleted; + } else { + // There are more methods in both the old and new lists + k_old_method = (methodOop) k_old_methods->obj_at(oi); + k_new_method = (methodOop) k_new_methods->obj_at(ni); + if (k_old_method->name() != k_new_method->name()) { + // Methods are sorted by method name, so a mismatch means added + // or deleted + if (k_old_method->name()->fast_compare(k_new_method->name()) > 0) { + method_was = added; + } else { + method_was = deleted; + } + } else if (k_old_method->signature() == k_new_method->signature()) { + // Both the name and signature match + method_was = matched; + } else { + // The name matches, but the signature doesn't, which means we have to + // search forward through the new overloaded methods. + int nj; // outside the loop for post-loop check + for (nj = ni + 1; nj < n_new_methods; nj++) { + methodOop m = (methodOop)k_new_methods->obj_at(nj); + if (k_old_method->name() != m->name()) { + // reached another method name so no more overloaded methods + method_was = deleted; + break; + } + if (k_old_method->signature() == m->signature()) { + // found a match so swap the methods + k_new_methods->obj_at_put(ni, m); + k_new_methods->obj_at_put(nj, k_new_method); + k_new_method = m; + method_was = matched; + break; + } + } + + if (nj >= n_new_methods) { + // reached the end without a match; so method was deleted + method_was = deleted; + } + } + } + + switch (method_was) { + case matched: + // methods match, be sure modifiers do too + old_flags = (jushort) k_old_method->access_flags().get_flags(); + new_flags = (jushort) k_new_method->access_flags().get_flags(); + if ((old_flags ^ new_flags) & ~(JVM_ACC_NATIVE)) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED; + } + { + u2 new_num = k_new_method->method_idnum(); + u2 old_num = k_old_method->method_idnum(); + if (new_num != old_num) { + methodOop idnum_owner = scratch_class->method_with_idnum(old_num); + if (idnum_owner != NULL) { + // There is already a method assigned this idnum -- switch them + idnum_owner->set_method_idnum(new_num); + } + k_new_method->set_method_idnum(old_num); + swap_all_method_annotations(old_num, new_num, scratch_class); + } + } + RC_TRACE(0x00008000, ("Method matched: new: %s [%d] == old: %s [%d]", + k_new_method->name_and_sig_as_C_string(), ni, + k_old_method->name_and_sig_as_C_string(), oi)); + // advance to next pair of methods + ++oi; + ++ni; + break; + case added: + // method added, see if it is OK + new_flags = (jushort) k_new_method->access_flags().get_flags(); + if ((new_flags & JVM_ACC_PRIVATE) == 0 + // hack: private should be treated as final, but alas + || (new_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0 + ) { + // new methods must be private + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED; + } + { + u2 num = the_class->next_method_idnum(); + if (num == constMethodOopDesc::UNSET_IDNUM) { + // cannot add any more methods + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED; + } + u2 new_num = k_new_method->method_idnum(); + methodOop idnum_owner = scratch_class->method_with_idnum(num); + if (idnum_owner != NULL) { + // There is already a method assigned this idnum -- switch them + idnum_owner->set_method_idnum(new_num); + } + k_new_method->set_method_idnum(num); + swap_all_method_annotations(new_num, num, scratch_class); + } + RC_TRACE(0x00008000, ("Method added: new: %s [%d]", + k_new_method->name_and_sig_as_C_string(), ni)); + ++ni; // advance to next new method + break; + case deleted: + // method deleted, see if it is OK + old_flags = (jushort) k_old_method->access_flags().get_flags(); + if ((old_flags & JVM_ACC_PRIVATE) == 0 + // hack: private should be treated as final, but alas + || (old_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0 + ) { + // deleted methods must be private + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED; + } + RC_TRACE(0x00008000, ("Method deleted: old: %s [%d]", + k_old_method->name_and_sig_as_C_string(), oi)); + ++oi; // advance to next old method + break; + default: + ShouldNotReachHere(); + } + } + + return JVMTI_ERROR_NONE; +} + + +// Find new constant pool index value for old constant pool index value +// by seaching the index map. Returns zero (0) if there is no mapped +// value for the old constant pool index. +int VM_RedefineClasses::find_new_index(int old_index) { + if (_index_map_count == 0) { + // map is empty so nothing can be found + return 0; + } + + if (old_index < 1 || old_index >= _index_map_p->length()) { + // The old_index is out of range so it is not mapped. This should + // not happen in regular constant pool merging use, but it can + // happen if a corrupt annotation is processed. + return 0; + } + + int value = _index_map_p->at(old_index); + if (value == -1) { + // the old_index is not mapped + return 0; + } + + return value; +} // end find_new_index() + + +// Returns true if the current mismatch is due to a resolved/unresolved +// class pair. Otherwise, returns false. +bool VM_RedefineClasses::is_unresolved_class_mismatch(constantPoolHandle cp1, + int index1, constantPoolHandle cp2, int index2) { + + jbyte t1 = cp1->tag_at(index1).value(); + if (t1 != JVM_CONSTANT_Class && t1 != JVM_CONSTANT_UnresolvedClass) { + return false; // wrong entry type; not our special case + } + + jbyte t2 = cp2->tag_at(index2).value(); + if (t2 != JVM_CONSTANT_Class && t2 != JVM_CONSTANT_UnresolvedClass) { + return false; // wrong entry type; not our special case + } + + if (t1 == t2) { + return false; // not a mismatch; not our special case + } + + char *s1 = cp1->klass_name_at(index1)->as_C_string(); + char *s2 = cp2->klass_name_at(index2)->as_C_string(); + if (strcmp(s1, s2) != 0) { + return false; // strings don't match; not our special case + } + + return true; // made it through the gauntlet; this is our special case +} // end is_unresolved_class_mismatch() + + +// Returns true if the current mismatch is due to a resolved/unresolved +// string pair. Otherwise, returns false. +bool VM_RedefineClasses::is_unresolved_string_mismatch(constantPoolHandle cp1, + int index1, constantPoolHandle cp2, int index2) { + + jbyte t1 = cp1->tag_at(index1).value(); + if (t1 != JVM_CONSTANT_String && t1 != JVM_CONSTANT_UnresolvedString) { + return false; // wrong entry type; not our special case + } + + jbyte t2 = cp2->tag_at(index2).value(); + if (t2 != JVM_CONSTANT_String && t2 != JVM_CONSTANT_UnresolvedString) { + return false; // wrong entry type; not our special case + } + + if (t1 == t2) { + return false; // not a mismatch; not our special case + } + + char *s1 = cp1->string_at_noresolve(index1); + char *s2 = cp2->string_at_noresolve(index2); + if (strcmp(s1, s2) != 0) { + return false; // strings don't match; not our special case + } + + return true; // made it through the gauntlet; this is our special case +} // end is_unresolved_string_mismatch() + + +jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) { + // For consistency allocate memory using os::malloc wrapper. + _scratch_classes = (instanceKlassHandle *) + os::malloc(sizeof(instanceKlassHandle) * _class_count); + if (_scratch_classes == NULL) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } + + ResourceMark rm(THREAD); + + JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current()); + for (int i = 0; i < _class_count; i++) { + oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass); + // classes for primitives cannot be redefined + if (!is_modifiable_class(mirror)) { + return JVMTI_ERROR_UNMODIFIABLE_CLASS; + } + klassOop the_class_oop = java_lang_Class::as_klassOop(mirror); + instanceKlassHandle the_class = instanceKlassHandle(THREAD, the_class_oop); + symbolHandle the_class_sym = symbolHandle(THREAD, the_class->name()); + + // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark + RC_TRACE_WITH_THREAD(0x00000001, THREAD, + ("loading name=%s (avail_mem=" UINT64_FORMAT "K)", + the_class->external_name(), os::available_memory() >> 10)); + + ClassFileStream st((u1*) _class_defs[i].class_bytes, + _class_defs[i].class_byte_count, (char *)"__VM_RedefineClasses__"); + + // Parse the stream. + Handle the_class_loader(THREAD, the_class->class_loader()); + Handle protection_domain(THREAD, the_class->protection_domain()); + // Set redefined class handle in JvmtiThreadState class. + // This redefined class is sent to agent event handler for class file + // load hook event. + state->set_class_being_redefined(&the_class, _class_load_kind); + + klassOop k = SystemDictionary::parse_stream(the_class_sym, + the_class_loader, + protection_domain, + &st, + THREAD); + // Clear class_being_redefined just to be sure. + state->clear_class_being_redefined(); + + // TODO: if this is retransform, and nothing changed we can skip it + + instanceKlassHandle scratch_class (THREAD, k); + + if (HAS_PENDING_EXCEPTION) { + symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); + // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark + RC_TRACE_WITH_THREAD(0x00000002, THREAD, ("parse_stream exception: '%s'", + ex_name->as_C_string())); + CLEAR_PENDING_EXCEPTION; + + if (ex_name == vmSymbols::java_lang_UnsupportedClassVersionError()) { + return JVMTI_ERROR_UNSUPPORTED_VERSION; + } else if (ex_name == vmSymbols::java_lang_ClassFormatError()) { + return JVMTI_ERROR_INVALID_CLASS_FORMAT; + } else if (ex_name == vmSymbols::java_lang_ClassCircularityError()) { + return JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION; + } else if (ex_name == vmSymbols::java_lang_NoClassDefFoundError()) { + // The message will be "XXX (wrong name: YYY)" + return JVMTI_ERROR_NAMES_DONT_MATCH; + } else if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } else { // Just in case more exceptions can be thrown.. + return JVMTI_ERROR_FAILS_VERIFICATION; + } + } + + // Ensure class is linked before redefine + if (!the_class->is_linked()) { + the_class->link_class(THREAD); + if (HAS_PENDING_EXCEPTION) { + symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); + // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark + RC_TRACE_WITH_THREAD(0x00000002, THREAD, ("link_class exception: '%s'", + ex_name->as_C_string())); + CLEAR_PENDING_EXCEPTION; + if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } else { + return JVMTI_ERROR_INTERNAL; + } + } + } + + // Do the validity checks in compare_and_normalize_class_versions() + // before verifying the byte codes. By doing these checks first, we + // limit the number of functions that require redirection from + // the_class to scratch_class. In particular, we don't have to + // modify JNI GetSuperclass() and thus won't change its performance. + jvmtiError res = compare_and_normalize_class_versions(the_class, + scratch_class); + if (res != JVMTI_ERROR_NONE) { + return res; + } + + // verify what the caller passed us + { + // The bug 6214132 caused the verification to fail. + // Information about the_class and scratch_class is temporarily + // recorded into jvmtiThreadState. This data is used to redirect + // the_class to scratch_class in the JVM_* functions called by the + // verifier. Please, refer to jvmtiThreadState.hpp for the detailed + // description. + RedefineVerifyMark rvm(&the_class, &scratch_class, state); + Verifier::verify( + scratch_class, Verifier::ThrowException, THREAD); + } + + if (HAS_PENDING_EXCEPTION) { + symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); + // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark + RC_TRACE_WITH_THREAD(0x00000002, THREAD, + ("verify_byte_codes exception: '%s'", ex_name->as_C_string())); + CLEAR_PENDING_EXCEPTION; + if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } else { + // tell the caller the bytecodes are bad + return JVMTI_ERROR_FAILS_VERIFICATION; + } + } + + res = merge_cp_and_rewrite(the_class, scratch_class, THREAD); + if (res != JVMTI_ERROR_NONE) { + return res; + } + + if (VerifyMergedCPBytecodes) { + // verify what we have done during constant pool merging + { + RedefineVerifyMark rvm(&the_class, &scratch_class, state); + Verifier::verify(scratch_class, Verifier::ThrowException, THREAD); + } + + if (HAS_PENDING_EXCEPTION) { + symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); + // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark + RC_TRACE_WITH_THREAD(0x00000002, THREAD, + ("verify_byte_codes post merge-CP exception: '%s'", + ex_name->as_C_string())); + CLEAR_PENDING_EXCEPTION; + if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } else { + // tell the caller that constant pool merging screwed up + return JVMTI_ERROR_INTERNAL; + } + } + } + + Rewriter::rewrite(scratch_class, THREAD); + if (HAS_PENDING_EXCEPTION) { + symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); + CLEAR_PENDING_EXCEPTION; + if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } else { + return JVMTI_ERROR_INTERNAL; + } + } + + _scratch_classes[i] = scratch_class; + + // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark + RC_TRACE_WITH_THREAD(0x00000001, THREAD, + ("loaded name=%s (avail_mem=" UINT64_FORMAT "K)", + the_class->external_name(), os::available_memory() >> 10)); + } + + return JVMTI_ERROR_NONE; +} + + +// Map old_index to new_index as needed. scratch_cp is only needed +// for RC_TRACE() calls. +void VM_RedefineClasses::map_index(constantPoolHandle scratch_cp, + int old_index, int new_index) { + if (find_new_index(old_index) != 0) { + // old_index is already mapped + return; + } + + if (old_index == new_index) { + // no mapping is needed + return; + } + + _index_map_p->at_put(old_index, new_index); + _index_map_count++; + + RC_TRACE(0x00040000, ("mapped tag %d at index %d to %d", + scratch_cp->tag_at(old_index).value(), old_index, new_index)); +} // end map_index() + + +// Merge old_cp and scratch_cp and return the results of the merge via +// merge_cp_p. The number of entries in *merge_cp_p is returned via +// merge_cp_length_p. The entries in old_cp occupy the same locations +// in *merge_cp_p. Also creates a map of indices from entries in +// scratch_cp to the corresponding entry in *merge_cp_p. Index map +// entries are only created for entries in scratch_cp that occupy a +// different location in *merged_cp_p. +bool VM_RedefineClasses::merge_constant_pools(constantPoolHandle old_cp, + constantPoolHandle scratch_cp, constantPoolHandle *merge_cp_p, + int *merge_cp_length_p, TRAPS) { + + if (merge_cp_p == NULL) { + assert(false, "caller must provide scatch constantPool"); + return false; // robustness + } + if (merge_cp_length_p == NULL) { + assert(false, "caller must provide scatch CP length"); + return false; // robustness + } + // Worst case we need old_cp->length() + scratch_cp()->length(), + // but the caller might be smart so make sure we have at least + // the minimum. + if ((*merge_cp_p)->length() < old_cp->length()) { + assert(false, "merge area too small"); + return false; // robustness + } + + RC_TRACE_WITH_THREAD(0x00010000, THREAD, + ("old_cp_len=%d, scratch_cp_len=%d", old_cp->length(), + scratch_cp->length())); + + { + // Pass 0: + // The old_cp is copied to *merge_cp_p; this means that any code + // using old_cp does not have to change. This work looks like a + // perfect fit for constantPoolOop::copy_cp_to(), but we need to + // handle one special case: + // - revert JVM_CONSTANT_Class to JVM_CONSTANT_UnresolvedClass + // This will make verification happy. + + int old_i; // index into old_cp + + // index zero (0) is not used in constantPools + for (old_i = 1; old_i < old_cp->length(); old_i++) { + // leave debugging crumb + jbyte old_tag = old_cp->tag_at(old_i).value(); + switch (old_tag) { + case JVM_CONSTANT_Class: + // revert the copy to JVM_CONSTANT_UnresolvedClass + (*merge_cp_p)->unresolved_klass_at_put(old_i, + old_cp->klass_name_at(old_i)); + break; + + case JVM_CONSTANT_Double: + case JVM_CONSTANT_Long: + // just copy the entry to *merge_cp_p, but double and long take + // two constant pool entries + old_cp->copy_entry_to(old_i, *merge_cp_p, old_i, CHECK_0); + old_i++; + break; + + default: + // just copy the entry to *merge_cp_p + old_cp->copy_entry_to(old_i, *merge_cp_p, old_i, CHECK_0); + break; + } + } // end for each old_cp entry + + // We don't need to sanity check that *merge_cp_length_p is within + // *merge_cp_p bounds since we have the minimum on-entry check above. + (*merge_cp_length_p) = old_i; + } + + // merge_cp_len should be the same as old_cp->length() at this point + // so this trace message is really a "warm-and-breathing" message. + RC_TRACE_WITH_THREAD(0x00020000, THREAD, + ("after pass 0: merge_cp_len=%d", *merge_cp_length_p)); + + int scratch_i; // index into scratch_cp + { + // Pass 1a: + // Compare scratch_cp entries to the old_cp entries that we have + // already copied to *merge_cp_p. In this pass, we are eliminating + // exact duplicates (matching entry at same index) so we only + // compare entries in the common indice range. + int increment = 1; + int pass1a_length = MIN2(old_cp->length(), scratch_cp->length()); + for (scratch_i = 1; scratch_i < pass1a_length; scratch_i += increment) { + switch (scratch_cp->tag_at(scratch_i).value()) { + case JVM_CONSTANT_Double: + case JVM_CONSTANT_Long: + // double and long take two constant pool entries + increment = 2; + break; + + default: + increment = 1; + break; + } + + bool match = scratch_cp->compare_entry_to(scratch_i, *merge_cp_p, + scratch_i, CHECK_0); + if (match) { + // found a match at the same index so nothing more to do + continue; + } else if (is_unresolved_class_mismatch(scratch_cp, scratch_i, + *merge_cp_p, scratch_i)) { + // The mismatch in compare_entry_to() above is because of a + // resolved versus unresolved class entry at the same index + // with the same string value. Since Pass 0 reverted any + // class entries to unresolved class entries in *merge_cp_p, + // we go with the unresolved class entry. + continue; + } else if (is_unresolved_string_mismatch(scratch_cp, scratch_i, + *merge_cp_p, scratch_i)) { + // The mismatch in compare_entry_to() above is because of a + // resolved versus unresolved string entry at the same index + // with the same string value. We can live with whichever + // happens to be at scratch_i in *merge_cp_p. + continue; + } + + int found_i = scratch_cp->find_matching_entry(scratch_i, *merge_cp_p, + CHECK_0); + if (found_i != 0) { + guarantee(found_i != scratch_i, + "compare_entry_to() and find_matching_entry() do not agree"); + + // Found a matching entry somewhere else in *merge_cp_p so + // just need a mapping entry. + map_index(scratch_cp, scratch_i, found_i); + continue; + } + + // The find_matching_entry() call above could fail to find a match + // due to a resolved versus unresolved class or string entry situation + // like we solved above with the is_unresolved_*_mismatch() calls. + // However, we would have to call is_unresolved_*_mismatch() over + // all of *merge_cp_p (potentially) and that doesn't seem to be + // worth the time. + + // No match found so we have to append this entry and any unique + // referenced entries to *merge_cp_p. + append_entry(scratch_cp, scratch_i, merge_cp_p, merge_cp_length_p, + CHECK_0); + } + } + + RC_TRACE_WITH_THREAD(0x00020000, THREAD, + ("after pass 1a: merge_cp_len=%d, scratch_i=%d, index_map_len=%d", + *merge_cp_length_p, scratch_i, _index_map_count)); + + if (scratch_i < scratch_cp->length()) { + // Pass 1b: + // old_cp is smaller than scratch_cp so there are entries in + // scratch_cp that we have not yet processed. We take care of + // those now. + int increment = 1; + for (; scratch_i < scratch_cp->length(); scratch_i += increment) { + switch (scratch_cp->tag_at(scratch_i).value()) { + case JVM_CONSTANT_Double: + case JVM_CONSTANT_Long: + // double and long take two constant pool entries + increment = 2; + break; + + default: + increment = 1; + break; + } + + int found_i = + scratch_cp->find_matching_entry(scratch_i, *merge_cp_p, CHECK_0); + if (found_i != 0) { + // Found a matching entry somewhere else in *merge_cp_p so + // just need a mapping entry. + map_index(scratch_cp, scratch_i, found_i); + continue; + } + + // No match found so we have to append this entry and any unique + // referenced entries to *merge_cp_p. + append_entry(scratch_cp, scratch_i, merge_cp_p, merge_cp_length_p, + CHECK_0); + } + + RC_TRACE_WITH_THREAD(0x00020000, THREAD, + ("after pass 1b: merge_cp_len=%d, scratch_i=%d, index_map_len=%d", + *merge_cp_length_p, scratch_i, _index_map_count)); + } + + return true; +} // end merge_constant_pools() + + +// Merge constant pools between the_class and scratch_class and +// potentially rewrite bytecodes in scratch_class to use the merged +// constant pool. +jvmtiError VM_RedefineClasses::merge_cp_and_rewrite( + instanceKlassHandle the_class, instanceKlassHandle scratch_class, + TRAPS) { + // worst case merged constant pool length is old and new combined + int merge_cp_length = the_class->constants()->length() + + scratch_class->constants()->length(); + + constantPoolHandle old_cp(THREAD, the_class->constants()); + constantPoolHandle scratch_cp(THREAD, scratch_class->constants()); + + // Constant pools are not easily reused so we allocate a new one + // each time. + constantPoolHandle merge_cp(THREAD, + oopFactory::new_constantPool(merge_cp_length, THREAD)); + int orig_length = old_cp->orig_length(); + if (orig_length == 0) { + // This old_cp is an actual original constant pool. We save + // the original length in the merged constant pool so that + // merge_constant_pools() can be more efficient. If a constant + // pool has a non-zero orig_length() value, then that constant + // pool was created by a merge operation in RedefineClasses. + merge_cp->set_orig_length(old_cp->length()); + } else { + // This old_cp is a merged constant pool from a previous + // RedefineClasses() calls so just copy the orig_length() + // value. + merge_cp->set_orig_length(old_cp->orig_length()); + } + + ResourceMark rm(THREAD); + _index_map_count = 0; + _index_map_p = new intArray(scratch_cp->length(), -1); + + bool result = merge_constant_pools(old_cp, scratch_cp, &merge_cp, + &merge_cp_length, THREAD); + if (!result) { + // The merge can fail due to memory allocation failure or due + // to robustness checks. + return JVMTI_ERROR_INTERNAL; + } + + RC_TRACE_WITH_THREAD(0x00010000, THREAD, + ("merge_cp_len=%d, index_map_len=%d", merge_cp_length, _index_map_count)); + + if (_index_map_count == 0) { + // there is nothing to map between the new and merged constant pools + + if (old_cp->length() == scratch_cp->length()) { + // The old and new constant pools are the same length and the + // index map is empty. This means that the three constant pools + // are equivalent (but not the same). Unfortunately, the new + // constant pool has not gone through link resolution nor have + // the new class bytecodes gone through constant pool cache + // rewriting so we can't use the old constant pool with the new + // class. + + merge_cp = constantPoolHandle(); // toss the merged constant pool + } else if (old_cp->length() < scratch_cp->length()) { + // The old constant pool has fewer entries than the new constant + // pool and the index map is empty. This means the new constant + // pool is a superset of the old constant pool. However, the old + // class bytecodes have already gone through constant pool cache + // rewriting so we can't use the new constant pool with the old + // class. + + merge_cp = constantPoolHandle(); // toss the merged constant pool + } else { + // The old constant pool has more entries than the new constant + // pool and the index map is empty. This means that both the old + // and merged constant pools are supersets of the new constant + // pool. + + // Replace the new constant pool with a shrunken copy of the + // merged constant pool; the previous new constant pool will + // get GCed. + set_new_constant_pool(scratch_class, merge_cp, merge_cp_length, true, + THREAD); + // drop local ref to the merged constant pool + merge_cp = constantPoolHandle(); + } + } else { + if (RC_TRACE_ENABLED(0x00040000)) { + // don't want to loop unless we are tracing + int count = 0; + for (int i = 1; i < _index_map_p->length(); i++) { + int value = _index_map_p->at(i); + + if (value != -1) { + RC_TRACE_WITH_THREAD(0x00040000, THREAD, + ("index_map[%d]: old=%d new=%d", count, i, value)); + count++; + } + } + } + + // We have entries mapped between the new and merged constant pools + // so we have to rewrite some constant pool references. + if (!rewrite_cp_refs(scratch_class, THREAD)) { + return JVMTI_ERROR_INTERNAL; + } + + // Replace the new constant pool with a shrunken copy of the + // merged constant pool so now the rewritten bytecodes have + // valid references; the previous new constant pool will get + // GCed. + set_new_constant_pool(scratch_class, merge_cp, merge_cp_length, true, + THREAD); + } + + return JVMTI_ERROR_NONE; +} // end merge_cp_and_rewrite() + + +// Rewrite constant pool references in klass scratch_class. +bool VM_RedefineClasses::rewrite_cp_refs(instanceKlassHandle scratch_class, + TRAPS) { + + // rewrite constant pool references in the methods: + if (!rewrite_cp_refs_in_methods(scratch_class, THREAD)) { + // propogate failure back to caller + return false; + } + + // rewrite constant pool references in the class_annotations: + if (!rewrite_cp_refs_in_class_annotations(scratch_class, THREAD)) { + // propogate failure back to caller + return false; + } + + // rewrite constant pool references in the fields_annotations: + if (!rewrite_cp_refs_in_fields_annotations(scratch_class, THREAD)) { + // propogate failure back to caller + return false; + } + + // rewrite constant pool references in the methods_annotations: + if (!rewrite_cp_refs_in_methods_annotations(scratch_class, THREAD)) { + // propogate failure back to caller + return false; + } + + // rewrite constant pool references in the methods_parameter_annotations: + if (!rewrite_cp_refs_in_methods_parameter_annotations(scratch_class, + THREAD)) { + // propogate failure back to caller + return false; + } + + // rewrite constant pool references in the methods_default_annotations: + if (!rewrite_cp_refs_in_methods_default_annotations(scratch_class, + THREAD)) { + // propogate failure back to caller + return false; + } + + return true; +} // end rewrite_cp_refs() + + +// Rewrite constant pool references in the methods. +bool VM_RedefineClasses::rewrite_cp_refs_in_methods( + instanceKlassHandle scratch_class, TRAPS) { + + objArrayHandle methods(THREAD, scratch_class->methods()); + + if (methods.is_null() || methods->length() == 0) { + // no methods so nothing to do + return true; + } + + // rewrite constant pool references in the methods: + for (int i = methods->length() - 1; i >= 0; i--) { + methodHandle method(THREAD, (methodOop)methods->obj_at(i)); + methodHandle new_method; + rewrite_cp_refs_in_method(method, &new_method, CHECK_false); + if (!new_method.is_null()) { + // the method has been replaced so save the new method version + methods->obj_at_put(i, new_method()); + } + } + + return true; +} + + +// Rewrite constant pool references in the specific method. This code +// was adapted from Rewriter::rewrite_method(). +void VM_RedefineClasses::rewrite_cp_refs_in_method(methodHandle method, + methodHandle *new_method_p, TRAPS) { + + *new_method_p = methodHandle(); // default is no new method + + // We cache a pointer to the bytecodes here in code_base. If GC + // moves the methodOop, then the bytecodes will also move which + // will likely cause a crash. We create a No_Safepoint_Verifier + // object to detect whether we pass a possible safepoint in this + // code block. + No_Safepoint_Verifier nsv; + + // Bytecodes and their length + address code_base = method->code_base(); + int code_length = method->code_size(); + + int bc_length; + for (int bci = 0; bci < code_length; bci += bc_length) { + address bcp = code_base + bci; + Bytecodes::Code c = (Bytecodes::Code)(*bcp); + + bc_length = Bytecodes::length_for(c); + if (bc_length == 0) { + // More complicated bytecodes report a length of zero so + // we have to try again a slightly different way. + bc_length = Bytecodes::length_at(bcp); + } + + assert(bc_length != 0, "impossible bytecode length"); + + switch (c) { + case Bytecodes::_ldc: + { + int cp_index = *(bcp + 1); + int new_index = find_new_index(cp_index); + + if (StressLdcRewrite && new_index == 0) { + // If we are stressing ldc -> ldc_w rewriting, then we + // always need a new_index value. + new_index = cp_index; + } + if (new_index != 0) { + // the original index is mapped so we have more work to do + if (!StressLdcRewrite && new_index <= max_jubyte) { + // The new value can still use ldc instead of ldc_w + // unless we are trying to stress ldc -> ldc_w rewriting + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("%s@" INTPTR_FORMAT " old=%d, new=%d", Bytecodes::name(c), + bcp, cp_index, new_index)); + *(bcp + 1) = new_index; + } else { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("%s->ldc_w@" INTPTR_FORMAT " old=%d, new=%d", + Bytecodes::name(c), bcp, cp_index, new_index)); + // the new value needs ldc_w instead of ldc + u_char inst_buffer[4]; // max instruction size is 4 bytes + bcp = (address)inst_buffer; + // construct new instruction sequence + *bcp = Bytecodes::_ldc_w; + bcp++; + // Rewriter::rewrite_method() does not rewrite ldc -> ldc_w. + // See comment below for difference between put_Java_u2() + // and put_native_u2(). + Bytes::put_Java_u2(bcp, new_index); + + Relocator rc(method, NULL /* no RelocatorListener needed */); + methodHandle m; + { + Pause_No_Safepoint_Verifier pnsv(&nsv); + + // ldc is 2 bytes and ldc_w is 3 bytes + m = rc.insert_space_at(bci, 3, inst_buffer, THREAD); + if (m.is_null() || HAS_PENDING_EXCEPTION) { + guarantee(false, "insert_space_at() failed"); + } + } + + // return the new method so that the caller can update + // the containing class + *new_method_p = method = m; + // switch our bytecode processing loop from the old method + // to the new method + code_base = method->code_base(); + code_length = method->code_size(); + bcp = code_base + bci; + c = (Bytecodes::Code)(*bcp); + bc_length = Bytecodes::length_for(c); + assert(bc_length != 0, "sanity check"); + } // end we need ldc_w instead of ldc + } // end if there is a mapped index + } break; + + // these bytecodes have a two-byte constant pool index + case Bytecodes::_anewarray : // fall through + case Bytecodes::_checkcast : // fall through + case Bytecodes::_getfield : // fall through + case Bytecodes::_getstatic : // fall through + case Bytecodes::_instanceof : // fall through + case Bytecodes::_invokeinterface: // fall through + case Bytecodes::_invokespecial : // fall through + case Bytecodes::_invokestatic : // fall through + case Bytecodes::_invokevirtual : // fall through + case Bytecodes::_ldc_w : // fall through + case Bytecodes::_ldc2_w : // fall through + case Bytecodes::_multianewarray : // fall through + case Bytecodes::_new : // fall through + case Bytecodes::_putfield : // fall through + case Bytecodes::_putstatic : + { + address p = bcp + 1; + int cp_index = Bytes::get_Java_u2(p); + int new_index = find_new_index(cp_index); + if (new_index != 0) { + // the original index is mapped so update w/ new value + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("%s@" INTPTR_FORMAT " old=%d, new=%d", Bytecodes::name(c), + bcp, cp_index, new_index)); + // Rewriter::rewrite_method() uses put_native_u2() in this + // situation because it is reusing the constant pool index + // location for a native index into the constantPoolCache. + // Since we are updating the constant pool index prior to + // verification and constantPoolCache initialization, we + // need to keep the new index in Java byte order. + Bytes::put_Java_u2(p, new_index); + } + } break; + } + } // end for each bytecode +} // end rewrite_cp_refs_in_method() + + +// Rewrite constant pool references in the class_annotations field. +bool VM_RedefineClasses::rewrite_cp_refs_in_class_annotations( + instanceKlassHandle scratch_class, TRAPS) { + + typeArrayHandle class_annotations(THREAD, + scratch_class->class_annotations()); + if (class_annotations.is_null() || class_annotations->length() == 0) { + // no class_annotations so nothing to do + return true; + } + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("class_annotations length=%d", class_annotations->length())); + + int byte_i = 0; // byte index into class_annotations + return rewrite_cp_refs_in_annotations_typeArray(class_annotations, byte_i, + THREAD); +} + + +// Rewrite constant pool references in an annotations typeArray. This +// "structure" is adapted from the RuntimeVisibleAnnotations_attribute +// that is described in section 4.8.15 of the 2nd-edition of the VM spec: +// +// annotations_typeArray { +// u2 num_annotations; +// annotation annotations[num_annotations]; +// } +// +bool VM_RedefineClasses::rewrite_cp_refs_in_annotations_typeArray( + typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) { + + if ((byte_i_ref + 2) > annotations_typeArray->length()) { + // not enough room for num_annotations field + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for num_annotations field")); + return false; + } + + u2 num_annotations = Bytes::get_Java_u2((address) + annotations_typeArray->byte_at_addr(byte_i_ref)); + byte_i_ref += 2; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("num_annotations=%d", num_annotations)); + + int calc_num_annotations = 0; + for (; calc_num_annotations < num_annotations; calc_num_annotations++) { + if (!rewrite_cp_refs_in_annotation_struct(annotations_typeArray, + byte_i_ref, THREAD)) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("bad annotation_struct at %d", calc_num_annotations)); + // propogate failure back to caller + return false; + } + } + assert(num_annotations == calc_num_annotations, "sanity check"); + + return true; +} // end rewrite_cp_refs_in_annotations_typeArray() + + +// Rewrite constant pool references in the annotation struct portion of +// an annotations_typeArray. This "structure" is from section 4.8.15 of +// the 2nd-edition of the VM spec: +// +// struct annotation { +// u2 type_index; +// u2 num_element_value_pairs; +// { +// u2 element_name_index; +// element_value value; +// } element_value_pairs[num_element_value_pairs]; +// } +// +bool VM_RedefineClasses::rewrite_cp_refs_in_annotation_struct( + typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) { + if ((byte_i_ref + 2 + 2) > annotations_typeArray->length()) { + // not enough room for smallest annotation_struct + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for annotation_struct")); + return false; + } + + u2 type_index = rewrite_cp_ref_in_annotation_data(annotations_typeArray, + byte_i_ref, "mapped old type_index=%d", THREAD); + + u2 num_element_value_pairs = Bytes::get_Java_u2((address) + annotations_typeArray->byte_at_addr( + byte_i_ref)); + byte_i_ref += 2; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("type_index=%d num_element_value_pairs=%d", type_index, + num_element_value_pairs)); + + int calc_num_element_value_pairs = 0; + for (; calc_num_element_value_pairs < num_element_value_pairs; + calc_num_element_value_pairs++) { + if ((byte_i_ref + 2) > annotations_typeArray->length()) { + // not enough room for another element_name_index, let alone + // the rest of another component + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for element_name_index")); + return false; + } + + u2 element_name_index = rewrite_cp_ref_in_annotation_data( + annotations_typeArray, byte_i_ref, + "mapped old element_name_index=%d", THREAD); + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("element_name_index=%d", element_name_index)); + + if (!rewrite_cp_refs_in_element_value(annotations_typeArray, + byte_i_ref, THREAD)) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("bad element_value at %d", calc_num_element_value_pairs)); + // propogate failure back to caller + return false; + } + } // end for each component + assert(num_element_value_pairs == calc_num_element_value_pairs, + "sanity check"); + + return true; +} // end rewrite_cp_refs_in_annotation_struct() + + +// Rewrite a constant pool reference at the current position in +// annotations_typeArray if needed. Returns the original constant +// pool reference if a rewrite was not needed or the new constant +// pool reference if a rewrite was needed. +u2 VM_RedefineClasses::rewrite_cp_ref_in_annotation_data( + typeArrayHandle annotations_typeArray, int &byte_i_ref, + const char * trace_mesg, TRAPS) { + + address cp_index_addr = (address) + annotations_typeArray->byte_at_addr(byte_i_ref); + u2 old_cp_index = Bytes::get_Java_u2(cp_index_addr); + u2 new_cp_index = find_new_index(old_cp_index); + if (new_cp_index != 0) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, (trace_mesg, old_cp_index)); + Bytes::put_Java_u2(cp_index_addr, new_cp_index); + old_cp_index = new_cp_index; + } + byte_i_ref += 2; + return old_cp_index; +} + + +// Rewrite constant pool references in the element_value portion of an +// annotations_typeArray. This "structure" is from section 4.8.15.1 of +// the 2nd-edition of the VM spec: +// +// struct element_value { +// u1 tag; +// union { +// u2 const_value_index; +// { +// u2 type_name_index; +// u2 const_name_index; +// } enum_const_value; +// u2 class_info_index; +// annotation annotation_value; +// struct { +// u2 num_values; +// element_value values[num_values]; +// } array_value; +// } value; +// } +// +bool VM_RedefineClasses::rewrite_cp_refs_in_element_value( + typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) { + + if ((byte_i_ref + 1) > annotations_typeArray->length()) { + // not enough room for a tag let alone the rest of an element_value + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a tag")); + return false; + } + + u1 tag = annotations_typeArray->byte_at(byte_i_ref); + byte_i_ref++; + RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("tag='%c'", tag)); + + switch (tag) { + // These BaseType tag values are from Table 4.2 in VM spec: + case 'B': // byte + case 'C': // char + case 'D': // double + case 'F': // float + case 'I': // int + case 'J': // long + case 'S': // short + case 'Z': // boolean + + // The remaining tag values are from Table 4.8 in the 2nd-edition of + // the VM spec: + case 's': + { + // For the above tag values (including the BaseType values), + // value.const_value_index is right union field. + + if ((byte_i_ref + 2) > annotations_typeArray->length()) { + // not enough room for a const_value_index + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a const_value_index")); + return false; + } + + u2 const_value_index = rewrite_cp_ref_in_annotation_data( + annotations_typeArray, byte_i_ref, + "mapped old const_value_index=%d", THREAD); + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("const_value_index=%d", const_value_index)); + } break; + + case 'e': + { + // for the above tag value, value.enum_const_value is right union field + + if ((byte_i_ref + 4) > annotations_typeArray->length()) { + // not enough room for a enum_const_value + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a enum_const_value")); + return false; + } + + u2 type_name_index = rewrite_cp_ref_in_annotation_data( + annotations_typeArray, byte_i_ref, + "mapped old type_name_index=%d", THREAD); + + u2 const_name_index = rewrite_cp_ref_in_annotation_data( + annotations_typeArray, byte_i_ref, + "mapped old const_name_index=%d", THREAD); + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("type_name_index=%d const_name_index=%d", type_name_index, + const_name_index)); + } break; + + case 'c': + { + // for the above tag value, value.class_info_index is right union field + + if ((byte_i_ref + 2) > annotations_typeArray->length()) { + // not enough room for a class_info_index + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a class_info_index")); + return false; + } + + u2 class_info_index = rewrite_cp_ref_in_annotation_data( + annotations_typeArray, byte_i_ref, + "mapped old class_info_index=%d", THREAD); + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("class_info_index=%d", class_info_index)); + } break; + + case '@': + // For the above tag value, value.attr_value is the right union + // field. This is a nested annotation. + if (!rewrite_cp_refs_in_annotation_struct(annotations_typeArray, + byte_i_ref, THREAD)) { + // propogate failure back to caller + return false; + } + break; + + case '[': + { + if ((byte_i_ref + 2) > annotations_typeArray->length()) { + // not enough room for a num_values field + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a num_values field")); + return false; + } + + // For the above tag value, value.array_value is the right union + // field. This is an array of nested element_value. + u2 num_values = Bytes::get_Java_u2((address) + annotations_typeArray->byte_at_addr(byte_i_ref)); + byte_i_ref += 2; + RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("num_values=%d", num_values)); + + int calc_num_values = 0; + for (; calc_num_values < num_values; calc_num_values++) { + if (!rewrite_cp_refs_in_element_value( + annotations_typeArray, byte_i_ref, THREAD)) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("bad nested element_value at %d", calc_num_values)); + // propogate failure back to caller + return false; + } + } + assert(num_values == calc_num_values, "sanity check"); + } break; + + default: + RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("bad tag=0x%x", tag)); + return false; + } // end decode tag field + + return true; +} // end rewrite_cp_refs_in_element_value() + + +// Rewrite constant pool references in a fields_annotations field. +bool VM_RedefineClasses::rewrite_cp_refs_in_fields_annotations( + instanceKlassHandle scratch_class, TRAPS) { + + objArrayHandle fields_annotations(THREAD, + scratch_class->fields_annotations()); + + if (fields_annotations.is_null() || fields_annotations->length() == 0) { + // no fields_annotations so nothing to do + return true; + } + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("fields_annotations length=%d", fields_annotations->length())); + + for (int i = 0; i < fields_annotations->length(); i++) { + typeArrayHandle field_annotations(THREAD, + (typeArrayOop)fields_annotations->obj_at(i)); + if (field_annotations.is_null() || field_annotations->length() == 0) { + // this field does not have any annotations so skip it + continue; + } + + int byte_i = 0; // byte index into field_annotations + if (!rewrite_cp_refs_in_annotations_typeArray(field_annotations, byte_i, + THREAD)) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("bad field_annotations at %d", i)); + // propogate failure back to caller + return false; + } + } + + return true; +} // end rewrite_cp_refs_in_fields_annotations() + + +// Rewrite constant pool references in a methods_annotations field. +bool VM_RedefineClasses::rewrite_cp_refs_in_methods_annotations( + instanceKlassHandle scratch_class, TRAPS) { + + objArrayHandle methods_annotations(THREAD, + scratch_class->methods_annotations()); + + if (methods_annotations.is_null() || methods_annotations->length() == 0) { + // no methods_annotations so nothing to do + return true; + } + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("methods_annotations length=%d", methods_annotations->length())); + + for (int i = 0; i < methods_annotations->length(); i++) { + typeArrayHandle method_annotations(THREAD, + (typeArrayOop)methods_annotations->obj_at(i)); + if (method_annotations.is_null() || method_annotations->length() == 0) { + // this method does not have any annotations so skip it + continue; + } + + int byte_i = 0; // byte index into method_annotations + if (!rewrite_cp_refs_in_annotations_typeArray(method_annotations, byte_i, + THREAD)) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("bad method_annotations at %d", i)); + // propogate failure back to caller + return false; + } + } + + return true; +} // end rewrite_cp_refs_in_methods_annotations() + + +// Rewrite constant pool references in a methods_parameter_annotations +// field. This "structure" is adapted from the +// RuntimeVisibleParameterAnnotations_attribute described in section +// 4.8.17 of the 2nd-edition of the VM spec: +// +// methods_parameter_annotations_typeArray { +// u1 num_parameters; +// { +// u2 num_annotations; +// annotation annotations[num_annotations]; +// } parameter_annotations[num_parameters]; +// } +// +bool VM_RedefineClasses::rewrite_cp_refs_in_methods_parameter_annotations( + instanceKlassHandle scratch_class, TRAPS) { + + objArrayHandle methods_parameter_annotations(THREAD, + scratch_class->methods_parameter_annotations()); + + if (methods_parameter_annotations.is_null() + || methods_parameter_annotations->length() == 0) { + // no methods_parameter_annotations so nothing to do + return true; + } + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("methods_parameter_annotations length=%d", + methods_parameter_annotations->length())); + + for (int i = 0; i < methods_parameter_annotations->length(); i++) { + typeArrayHandle method_parameter_annotations(THREAD, + (typeArrayOop)methods_parameter_annotations->obj_at(i)); + if (method_parameter_annotations.is_null() + || method_parameter_annotations->length() == 0) { + // this method does not have any parameter annotations so skip it + continue; + } + + if (method_parameter_annotations->length() < 1) { + // not enough room for a num_parameters field + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a num_parameters field at %d", i)); + return false; + } + + int byte_i = 0; // byte index into method_parameter_annotations + + u1 num_parameters = method_parameter_annotations->byte_at(byte_i); + byte_i++; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("num_parameters=%d", num_parameters)); + + int calc_num_parameters = 0; + for (; calc_num_parameters < num_parameters; calc_num_parameters++) { + if (!rewrite_cp_refs_in_annotations_typeArray( + method_parameter_annotations, byte_i, THREAD)) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("bad method_parameter_annotations at %d", calc_num_parameters)); + // propogate failure back to caller + return false; + } + } + assert(num_parameters == calc_num_parameters, "sanity check"); + } + + return true; +} // end rewrite_cp_refs_in_methods_parameter_annotations() + + +// Rewrite constant pool references in a methods_default_annotations +// field. This "structure" is adapted from the AnnotationDefault_attribute +// that is described in section 4.8.19 of the 2nd-edition of the VM spec: +// +// methods_default_annotations_typeArray { +// element_value default_value; +// } +// +bool VM_RedefineClasses::rewrite_cp_refs_in_methods_default_annotations( + instanceKlassHandle scratch_class, TRAPS) { + + objArrayHandle methods_default_annotations(THREAD, + scratch_class->methods_default_annotations()); + + if (methods_default_annotations.is_null() + || methods_default_annotations->length() == 0) { + // no methods_default_annotations so nothing to do + return true; + } + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("methods_default_annotations length=%d", + methods_default_annotations->length())); + + for (int i = 0; i < methods_default_annotations->length(); i++) { + typeArrayHandle method_default_annotations(THREAD, + (typeArrayOop)methods_default_annotations->obj_at(i)); + if (method_default_annotations.is_null() + || method_default_annotations->length() == 0) { + // this method does not have any default annotations so skip it + continue; + } + + int byte_i = 0; // byte index into method_default_annotations + + if (!rewrite_cp_refs_in_element_value( + method_default_annotations, byte_i, THREAD)) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("bad default element_value at %d", i)); + // propogate failure back to caller + return false; + } + } + + return true; +} // end rewrite_cp_refs_in_methods_default_annotations() + + +// Rewrite constant pool references in the method's stackmap table. +// These "structures" are adapted from the StackMapTable_attribute that +// is described in section 4.8.4 of the 6.0 version of the VM spec +// (dated 2005.10.26): +// file:///net/quincunx.sfbay/export/gbracha/ClassFile-Java6.pdf +// +// stack_map { +// u2 number_of_entries; +// stack_map_frame entries[number_of_entries]; +// } +// +void VM_RedefineClasses::rewrite_cp_refs_in_stack_map_table( + methodHandle method, TRAPS) { + + if (!method->has_stackmap_table()) { + return; + } + + typeArrayOop stackmap_data = method->stackmap_data(); + address stackmap_p = (address)stackmap_data->byte_at_addr(0); + address stackmap_end = stackmap_p + stackmap_data->length(); + + assert(stackmap_p + 2 <= stackmap_end, "no room for number_of_entries"); + u2 number_of_entries = Bytes::get_Java_u2(stackmap_p); + stackmap_p += 2; + + RC_TRACE_WITH_THREAD(0x04000000, THREAD, + ("number_of_entries=%u", number_of_entries)); + + // walk through each stack_map_frame + u2 calc_number_of_entries = 0; + for (; calc_number_of_entries < number_of_entries; calc_number_of_entries++) { + // The stack_map_frame structure is a u1 frame_type followed by + // 0 or more bytes of data: + // + // union stack_map_frame { + // same_frame; + // same_locals_1_stack_item_frame; + // same_locals_1_stack_item_frame_extended; + // chop_frame; + // same_frame_extended; + // append_frame; + // full_frame; + // } + + assert(stackmap_p + 1 <= stackmap_end, "no room for frame_type"); + // The Linux compiler does not like frame_type to be u1 or u2. It + // issues the following warning for the first if-statement below: + // + // "warning: comparison is always true due to limited range of data type" + // + u4 frame_type = *stackmap_p; + stackmap_p++; + + // same_frame { + // u1 frame_type = SAME; /* 0-63 */ + // } + if (frame_type >= 0 && frame_type <= 63) { + // nothing more to do for same_frame + } + + // same_locals_1_stack_item_frame { + // u1 frame_type = SAME_LOCALS_1_STACK_ITEM; /* 64-127 */ + // verification_type_info stack[1]; + // } + else if (frame_type >= 64 && frame_type <= 127) { + rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, + calc_number_of_entries, frame_type, THREAD); + } + + // reserved for future use + else if (frame_type >= 128 && frame_type <= 246) { + // nothing more to do for reserved frame_types + } + + // same_locals_1_stack_item_frame_extended { + // u1 frame_type = SAME_LOCALS_1_STACK_ITEM_EXTENDED; /* 247 */ + // u2 offset_delta; + // verification_type_info stack[1]; + // } + else if (frame_type == 247) { + stackmap_p += 2; + rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, + calc_number_of_entries, frame_type, THREAD); + } + + // chop_frame { + // u1 frame_type = CHOP; /* 248-250 */ + // u2 offset_delta; + // } + else if (frame_type >= 248 && frame_type <= 250) { + stackmap_p += 2; + } + + // same_frame_extended { + // u1 frame_type = SAME_FRAME_EXTENDED; /* 251*/ + // u2 offset_delta; + // } + else if (frame_type == 251) { + stackmap_p += 2; + } + + // append_frame { + // u1 frame_type = APPEND; /* 252-254 */ + // u2 offset_delta; + // verification_type_info locals[frame_type - 251]; + // } + else if (frame_type >= 252 && frame_type <= 254) { + assert(stackmap_p + 2 <= stackmap_end, + "no room for offset_delta"); + stackmap_p += 2; + u1 len = frame_type - 251; + for (u1 i = 0; i < len; i++) { + rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, + calc_number_of_entries, frame_type, THREAD); + } + } + + // full_frame { + // u1 frame_type = FULL_FRAME; /* 255 */ + // u2 offset_delta; + // u2 number_of_locals; + // verification_type_info locals[number_of_locals]; + // u2 number_of_stack_items; + // verification_type_info stack[number_of_stack_items]; + // } + else if (frame_type == 255) { + assert(stackmap_p + 2 + 2 <= stackmap_end, + "no room for smallest full_frame"); + stackmap_p += 2; + + u2 number_of_locals = Bytes::get_Java_u2(stackmap_p); + stackmap_p += 2; + + for (u2 locals_i = 0; locals_i < number_of_locals; locals_i++) { + rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, + calc_number_of_entries, frame_type, THREAD); + } + + // Use the largest size for the number_of_stack_items, but only get + // the right number of bytes. + u2 number_of_stack_items = Bytes::get_Java_u2(stackmap_p); + stackmap_p += 2; + + for (u2 stack_i = 0; stack_i < number_of_stack_items; stack_i++) { + rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, + calc_number_of_entries, frame_type, THREAD); + } + } + } // end while there is a stack_map_frame + assert(number_of_entries == calc_number_of_entries, "sanity check"); +} // end rewrite_cp_refs_in_stack_map_table() + + +// Rewrite constant pool references in the verification type info +// portion of the method's stackmap table. These "structures" are +// adapted from the StackMapTable_attribute that is described in +// section 4.8.4 of the 6.0 version of the VM spec (dated 2005.10.26): +// file:///net/quincunx.sfbay/export/gbracha/ClassFile-Java6.pdf +// +// The verification_type_info structure is a u1 tag followed by 0 or +// more bytes of data: +// +// union verification_type_info { +// Top_variable_info; +// Integer_variable_info; +// Float_variable_info; +// Long_variable_info; +// Double_variable_info; +// Null_variable_info; +// UninitializedThis_variable_info; +// Object_variable_info; +// Uninitialized_variable_info; +// } +// +void VM_RedefineClasses::rewrite_cp_refs_in_verification_type_info( + address& stackmap_p_ref, address stackmap_end, u2 frame_i, + u1 frame_type, TRAPS) { + + assert(stackmap_p_ref + 1 <= stackmap_end, "no room for tag"); + u1 tag = *stackmap_p_ref; + stackmap_p_ref++; + + switch (tag) { + // Top_variable_info { + // u1 tag = ITEM_Top; /* 0 */ + // } + // verificationType.hpp has zero as ITEM_Bogus instead of ITEM_Top + case 0: // fall through + + // Integer_variable_info { + // u1 tag = ITEM_Integer; /* 1 */ + // } + case ITEM_Integer: // fall through + + // Float_variable_info { + // u1 tag = ITEM_Float; /* 2 */ + // } + case ITEM_Float: // fall through + + // Double_variable_info { + // u1 tag = ITEM_Double; /* 3 */ + // } + case ITEM_Double: // fall through + + // Long_variable_info { + // u1 tag = ITEM_Long; /* 4 */ + // } + case ITEM_Long: // fall through + + // Null_variable_info { + // u1 tag = ITEM_Null; /* 5 */ + // } + case ITEM_Null: // fall through + + // UninitializedThis_variable_info { + // u1 tag = ITEM_UninitializedThis; /* 6 */ + // } + case ITEM_UninitializedThis: + // nothing more to do for the above tag types + break; + + // Object_variable_info { + // u1 tag = ITEM_Object; /* 7 */ + // u2 cpool_index; + // } + case ITEM_Object: + { + assert(stackmap_p_ref + 2 <= stackmap_end, "no room for cpool_index"); + u2 cpool_index = Bytes::get_Java_u2(stackmap_p_ref); + u2 new_cp_index = find_new_index(cpool_index); + if (new_cp_index != 0) { + RC_TRACE_WITH_THREAD(0x04000000, THREAD, + ("mapped old cpool_index=%d", cpool_index)); + Bytes::put_Java_u2(stackmap_p_ref, new_cp_index); + cpool_index = new_cp_index; + } + stackmap_p_ref += 2; + + RC_TRACE_WITH_THREAD(0x04000000, THREAD, + ("frame_i=%u, frame_type=%u, cpool_index=%d", frame_i, + frame_type, cpool_index)); + } break; + + // Uninitialized_variable_info { + // u1 tag = ITEM_Uninitialized; /* 8 */ + // u2 offset; + // } + case ITEM_Uninitialized: + assert(stackmap_p_ref + 2 <= stackmap_end, "no room for offset"); + stackmap_p_ref += 2; + break; + + default: + RC_TRACE_WITH_THREAD(0x04000000, THREAD, + ("frame_i=%u, frame_type=%u, bad tag=0x%x", frame_i, frame_type, tag)); + ShouldNotReachHere(); + break; + } // end switch (tag) +} // end rewrite_cp_refs_in_verification_type_info() + + +// Change the constant pool associated with klass scratch_class to +// scratch_cp. If shrink is true, then scratch_cp_length elements +// are copied from scratch_cp to a smaller constant pool and the +// smaller constant pool is associated with scratch_class. +void VM_RedefineClasses::set_new_constant_pool( + instanceKlassHandle scratch_class, constantPoolHandle scratch_cp, + int scratch_cp_length, bool shrink, TRAPS) { + assert(!shrink || scratch_cp->length() >= scratch_cp_length, "sanity check"); + + if (shrink) { + // scratch_cp is a merged constant pool and has enough space for a + // worst case merge situation. We want to associate the minimum + // sized constant pool with the klass to save space. + constantPoolHandle smaller_cp(THREAD, + oopFactory::new_constantPool(scratch_cp_length, THREAD)); + // preserve orig_length() value in the smaller copy + int orig_length = scratch_cp->orig_length(); + assert(orig_length != 0, "sanity check"); + smaller_cp->set_orig_length(orig_length); + scratch_cp->copy_cp_to(1, scratch_cp_length - 1, smaller_cp, 1, THREAD); + scratch_cp = smaller_cp; + } + + // attach new constant pool to klass + scratch_cp->set_pool_holder(scratch_class()); + + // attach klass to new constant pool + scratch_class->set_constants(scratch_cp()); + + int i; // for portability + + // update each field in klass to use new constant pool indices as needed + typeArrayHandle fields(THREAD, scratch_class->fields()); + int n_fields = fields->length(); + for (i = 0; i < n_fields; i += instanceKlass::next_offset) { + jshort cur_index = fields->short_at(i + instanceKlass::name_index_offset); + jshort new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("field-name_index change: %d to %d", cur_index, new_index)); + fields->short_at_put(i + instanceKlass::name_index_offset, new_index); + } + cur_index = fields->short_at(i + instanceKlass::signature_index_offset); + new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("field-signature_index change: %d to %d", cur_index, new_index)); + fields->short_at_put(i + instanceKlass::signature_index_offset, + new_index); + } + cur_index = fields->short_at(i + instanceKlass::initval_index_offset); + new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("field-initval_index change: %d to %d", cur_index, new_index)); + fields->short_at_put(i + instanceKlass::initval_index_offset, new_index); + } + cur_index = fields->short_at(i + instanceKlass::generic_signature_offset); + new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("field-generic_signature change: %d to %d", cur_index, new_index)); + fields->short_at_put(i + instanceKlass::generic_signature_offset, + new_index); + } + } // end for each field + + // Update constant pool indices in the inner classes info to use + // new constant indices as needed. The inner classes info is a + // quadruple: + // (inner_class_info, outer_class_info, inner_name, inner_access_flags) + typeArrayOop inner_class_list = scratch_class->inner_classes(); + int icl_length = (inner_class_list == NULL) ? 0 : inner_class_list->length(); + if (icl_length > 0) { + typeArrayHandle inner_class_list_h(THREAD, inner_class_list); + for (int i = 0; i < icl_length; + i += instanceKlass::inner_class_next_offset) { + int cur_index = inner_class_list_h->ushort_at(i + + instanceKlass::inner_class_inner_class_info_offset); + if (cur_index == 0) { + continue; // JVM spec. allows null inner class refs so skip it + } + int new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("inner_class_info change: %d to %d", cur_index, new_index)); + inner_class_list_h->ushort_at_put(i + + instanceKlass::inner_class_inner_class_info_offset, new_index); + } + cur_index = inner_class_list_h->ushort_at(i + + instanceKlass::inner_class_outer_class_info_offset); + new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("outer_class_info change: %d to %d", cur_index, new_index)); + inner_class_list_h->ushort_at_put(i + + instanceKlass::inner_class_outer_class_info_offset, new_index); + } + cur_index = inner_class_list_h->ushort_at(i + + instanceKlass::inner_class_inner_name_offset); + new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("inner_name change: %d to %d", cur_index, new_index)); + inner_class_list_h->ushort_at_put(i + + instanceKlass::inner_class_inner_name_offset, new_index); + } + } // end for each inner class + } // end if we have inner classes + + // Attach each method in klass to the new constant pool and update + // to use new constant pool indices as needed: + objArrayHandle methods(THREAD, scratch_class->methods()); + for (i = methods->length() - 1; i >= 0; i--) { + methodHandle method(THREAD, (methodOop)methods->obj_at(i)); + method->set_constants(scratch_cp()); + + int new_index = find_new_index(method->name_index()); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("method-name_index change: %d to %d", method->name_index(), + new_index)); + method->set_name_index(new_index); + } + new_index = find_new_index(method->signature_index()); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("method-signature_index change: %d to %d", + method->signature_index(), new_index)); + method->set_signature_index(new_index); + } + new_index = find_new_index(method->generic_signature_index()); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("method-generic_signature_index change: %d to %d", + method->generic_signature_index(), new_index)); + method->set_generic_signature_index(new_index); + } + + // Update constant pool indices in the method's checked exception + // table to use new constant indices as needed. + int cext_length = method->checked_exceptions_length(); + if (cext_length > 0) { + CheckedExceptionElement * cext_table = + method->checked_exceptions_start(); + for (int j = 0; j < cext_length; j++) { + int cur_index = cext_table[j].class_cp_index; + int new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("cext-class_cp_index change: %d to %d", cur_index, new_index)); + cext_table[j].class_cp_index = (u2)new_index; + } + } // end for each checked exception table entry + } // end if there are checked exception table entries + + // Update each catch type index in the method's exception table + // to use new constant pool indices as needed. The exception table + // holds quadruple entries of the form: + // (beg_bci, end_bci, handler_bci, klass_index) + const int beg_bci_offset = 0; + const int end_bci_offset = 1; + const int handler_bci_offset = 2; + const int klass_index_offset = 3; + const int entry_size = 4; + + typeArrayHandle ex_table (THREAD, method->exception_table()); + int ext_length = ex_table->length(); + assert(ext_length % entry_size == 0, "exception table format has changed"); + + for (int j = 0; j < ext_length; j += entry_size) { + int cur_index = ex_table->int_at(j + klass_index_offset); + int new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("ext-klass_index change: %d to %d", cur_index, new_index)); + ex_table->int_at_put(j + klass_index_offset, new_index); + } + } // end for each exception table entry + + // Update constant pool indices in the method's local variable + // table to use new constant indices as needed. The local variable + // table hold sextuple entries of the form: + // (start_pc, length, name_index, descriptor_index, signature_index, slot) + int lvt_length = method->localvariable_table_length(); + if (lvt_length > 0) { + LocalVariableTableElement * lv_table = + method->localvariable_table_start(); + for (int j = 0; j < lvt_length; j++) { + int cur_index = lv_table[j].name_cp_index; + int new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("lvt-name_cp_index change: %d to %d", cur_index, new_index)); + lv_table[j].name_cp_index = (u2)new_index; + } + cur_index = lv_table[j].descriptor_cp_index; + new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("lvt-descriptor_cp_index change: %d to %d", cur_index, + new_index)); + lv_table[j].descriptor_cp_index = (u2)new_index; + } + cur_index = lv_table[j].signature_cp_index; + new_index = find_new_index(cur_index); + if (new_index != 0) { + RC_TRACE_WITH_THREAD(0x00080000, THREAD, + ("lvt-signature_cp_index change: %d to %d", cur_index, new_index)); + lv_table[j].signature_cp_index = (u2)new_index; + } + } // end for each local variable table entry + } // end if there are local variable table entries + + rewrite_cp_refs_in_stack_map_table(method, THREAD); + } // end for each method +} // end set_new_constant_pool() + + +// Unevolving classes may point to methods of the_class directly +// from their constant pool caches, itables, and/or vtables. We +// use the SystemDictionary::classes_do() facility and this helper +// to fix up these pointers. +// +// Note: We currently don't support updating the vtable in +// arrayKlassOops. See Open Issues in jvmtiRedefineClasses.hpp. +void VM_RedefineClasses::adjust_cpool_cache_and_vtable(klassOop k_oop, + oop initiating_loader, TRAPS) { + Klass *k = k_oop->klass_part(); + if (k->oop_is_instance()) { + HandleMark hm(THREAD); + instanceKlass *ik = (instanceKlass *) k; + + // HotSpot specific optimization! HotSpot does not currently + // support delegation from the bootstrap class loader to a + // user-defined class loader. This means that if the bootstrap + // class loader is the initiating class loader, then it will also + // be the defining class loader. This also means that classes + // loaded by the bootstrap class loader cannot refer to classes + // loaded by a user-defined class loader. Note: a user-defined + // class loader can delegate to the bootstrap class loader. + // + // If the current class being redefined has a user-defined class + // loader as its defining class loader, then we can skip all + // classes loaded by the bootstrap class loader. + bool is_user_defined = + instanceKlass::cast(_the_class_oop)->class_loader() != NULL; + if (is_user_defined && ik->class_loader() == NULL) { + return; + } + + // This is a very busy routine. We don't want too much tracing + // printed out. + bool trace_name_printed = false; + + // Very noisy: only enable this call if you are trying to determine + // that a specific class gets found by this routine. + // RC_TRACE macro has an embedded ResourceMark + // RC_TRACE_WITH_THREAD(0x00100000, THREAD, + // ("adjust check: name=%s", ik->external_name())); + // trace_name_printed = true; + + // Fix the vtable embedded in the_class and subclasses of the_class, + // if one exists. We discard scratch_class and we don't keep an + // instanceKlass around to hold obsolete methods so we don't have + // any other instanceKlass embedded vtables to update. The vtable + // holds the methodOops for virtual (but not final) methods. + if (ik->vtable_length() > 0 && ik->is_subtype_of(_the_class_oop)) { + // ik->vtable() creates a wrapper object; rm cleans it up + ResourceMark rm(THREAD); + ik->vtable()->adjust_method_entries(_matching_old_methods, + _matching_new_methods, + _matching_methods_length, + &trace_name_printed); + } + + // If the current class has an itable and we are either redefining an + // interface or if the current class is a subclass of the_class, then + // we potentially have to fix the itable. If we are redefining an + // interface, then we have to call adjust_method_entries() for + // every instanceKlass that has an itable since there isn't a + // subclass relationship between an interface and an instanceKlass. + if (ik->itable_length() > 0 && (Klass::cast(_the_class_oop)->is_interface() + || ik->is_subclass_of(_the_class_oop))) { + // ik->itable() creates a wrapper object; rm cleans it up + ResourceMark rm(THREAD); + ik->itable()->adjust_method_entries(_matching_old_methods, + _matching_new_methods, + _matching_methods_length, + &trace_name_printed); + } + + // The constant pools in other classes (other_cp) can refer to + // methods in the_class. We have to update method information in + // other_cp's cache. If other_cp has a previous version, then we + // have to repeat the process for each previous version. The + // constant pool cache holds the methodOops for non-virtual + // methods and for virtual, final methods. + // + // Special case: if the current class is the_class, then new_cp + // has already been attached to the_class and old_cp has already + // been added as a previous version. The new_cp doesn't have any + // cached references to old methods so it doesn't need to be + // updated. We can simply start with the previous version(s) in + // that case. + constantPoolHandle other_cp; + constantPoolCacheOop cp_cache; + + if (k_oop != _the_class_oop) { + // this klass' constant pool cache may need adjustment + other_cp = constantPoolHandle(ik->constants()); + cp_cache = other_cp->cache(); + if (cp_cache != NULL) { + cp_cache->adjust_method_entries(_matching_old_methods, + _matching_new_methods, + _matching_methods_length, + &trace_name_printed); + } + } + { + ResourceMark rm(THREAD); + // PreviousVersionInfo objects returned via PreviousVersionWalker + // contain a GrowableArray of handles. We have to clean up the + // GrowableArray _after_ the PreviousVersionWalker destructor + // has destroyed the handles. + { + // the previous versions' constant pool caches may need adjustment + PreviousVersionWalker pvw(ik); + for (PreviousVersionInfo * pv_info = pvw.next_previous_version(); + pv_info != NULL; pv_info = pvw.next_previous_version()) { + other_cp = pv_info->prev_constant_pool_handle(); + cp_cache = other_cp->cache(); + if (cp_cache != NULL) { + cp_cache->adjust_method_entries(_matching_old_methods, + _matching_new_methods, + _matching_methods_length, + &trace_name_printed); + } + } + } // pvw is cleaned up + } // rm is cleaned up + } +} + +void VM_RedefineClasses::update_jmethod_ids() { + for (int j = 0; j < _matching_methods_length; ++j) { + methodOop old_method = _matching_old_methods[j]; + jmethodID jmid = old_method->find_jmethod_id_or_null(); + if (jmid != NULL) { + // There is a jmethodID, change it to point to the new method + methodHandle new_method_h(_matching_new_methods[j]); + JNIHandles::change_method_associated_with_jmethod_id(jmid, new_method_h); + assert(JNIHandles::resolve_jmethod_id(jmid) == _matching_new_methods[j], + "should be replaced"); + } + } +} + +void VM_RedefineClasses::check_methods_and_mark_as_obsolete( + BitMap *emcp_methods, int * emcp_method_count_p) { + *emcp_method_count_p = 0; + int obsolete_count = 0; + int old_index = 0; + for (int j = 0; j < _matching_methods_length; ++j, ++old_index) { + methodOop old_method = _matching_old_methods[j]; + methodOop new_method = _matching_new_methods[j]; + methodOop old_array_method; + + // Maintain an old_index into the _old_methods array by skipping + // deleted methods + while ((old_array_method = (methodOop) _old_methods->obj_at(old_index)) + != old_method) { + ++old_index; + } + + if (MethodComparator::methods_EMCP(old_method, new_method)) { + // The EMCP definition from JSR-163 requires the bytecodes to be + // the same with the exception of constant pool indices which may + // differ. However, the constants referred to by those indices + // must be the same. + // + // We use methods_EMCP() for comparison since constant pool + // merging can remove duplicate constant pool entries that were + // present in the old method and removed from the rewritten new + // method. A faster binary comparison function would consider the + // old and new methods to be different when they are actually + // EMCP. + // + // The old and new methods are EMCP and you would think that we + // could get rid of one of them here and now and save some space. + // However, the concept of EMCP only considers the bytecodes and + // the constant pool entries in the comparison. Other things, + // e.g., the line number table (LNT) or the local variable table + // (LVT) don't count in the comparison. So the new (and EMCP) + // method can have a new LNT that we need so we can't just + // overwrite the new method with the old method. + // + // When this routine is called, we have already attached the new + // methods to the_class so the old methods are effectively + // overwritten. However, if an old method is still executing, + // then the old method cannot be collected until sometime after + // the old method call has returned. So the overwriting of old + // methods by new methods will save us space except for those + // (hopefully few) old methods that are still executing. + // + // A method refers to a constMethodOop and this presents another + // possible avenue to space savings. The constMethodOop in the + // new method contains possibly new attributes (LNT, LVT, etc). + // At first glance, it seems possible to save space by replacing + // the constMethodOop in the old method with the constMethodOop + // from the new method. The old and new methods would share the + // same constMethodOop and we would save the space occupied by + // the old constMethodOop. However, the constMethodOop contains + // a back reference to the containing method. Sharing the + // constMethodOop between two methods could lead to confusion in + // the code that uses the back reference. This would lead to + // brittle code that could be broken in non-obvious ways now or + // in the future. + // + // Another possibility is to copy the constMethodOop from the new + // method to the old method and then overwrite the new method with + // the old method. Since the constMethodOop contains the bytecodes + // for the method embedded in the oop, this option would change + // the bytecodes out from under any threads executing the old + // method and make the thread's bcp invalid. Since EMCP requires + // that the bytecodes be the same modulo constant pool indices, it + // is straight forward to compute the correct new bcp in the new + // constMethodOop from the old bcp in the old constMethodOop. The + // time consuming part would be searching all the frames in all + // of the threads to find all of the calls to the old method. + // + // It looks like we will have to live with the limited savings + // that we get from effectively overwriting the old methods + // when the new methods are attached to the_class. + + // track which methods are EMCP for add_previous_version() call + emcp_methods->set_bit(old_index); + (*emcp_method_count_p)++; + + // An EMCP method is _not_ obsolete. An obsolete method has a + // different jmethodID than the current method. An EMCP method + // has the same jmethodID as the current method. Having the + // same jmethodID for all EMCP versions of a method allows for + // a consistent view of the EMCP methods regardless of which + // EMCP method you happen to have in hand. For example, a + // breakpoint set in one EMCP method will work for all EMCP + // versions of the method including the current one. + } else { + // mark obsolete methods as such + old_method->set_is_obsolete(); + obsolete_count++; + + // obsolete methods need a unique idnum + u2 num = instanceKlass::cast(_the_class_oop)->next_method_idnum(); + if (num != constMethodOopDesc::UNSET_IDNUM) { +// u2 old_num = old_method->method_idnum(); + old_method->set_method_idnum(num); +// TO DO: attach obsolete annotations to obsolete method's new idnum + } + // With tracing we try not to "yack" too much. The position of + // this trace assumes there are fewer obsolete methods than + // EMCP methods. + RC_TRACE(0x00000100, ("mark %s(%s) as obsolete", + old_method->name()->as_C_string(), + old_method->signature()->as_C_string())); + } + old_method->set_is_old(); + } + for (int i = 0; i < _deleted_methods_length; ++i) { + methodOop old_method = _deleted_methods[i]; + + assert(old_method->vtable_index() < 0, + "cannot delete methods with vtable entries");; + + // Mark all deleted methods as old and obsolete + old_method->set_is_old(); + old_method->set_is_obsolete(); + ++obsolete_count; + // With tracing we try not to "yack" too much. The position of + // this trace assumes there are fewer obsolete methods than + // EMCP methods. + RC_TRACE(0x00000100, ("mark deleted %s(%s) as obsolete", + old_method->name()->as_C_string(), + old_method->signature()->as_C_string())); + } + assert((*emcp_method_count_p + obsolete_count) == _old_methods->length(), + "sanity check"); + RC_TRACE(0x00000100, ("EMCP_cnt=%d, obsolete_cnt=%d", *emcp_method_count_p, + obsolete_count)); +} + +// This internal class transfers the native function registration from old methods +// to new methods. It is designed to handle both the simple case of unchanged +// native methods and the complex cases of native method prefixes being added and/or +// removed. +// It expects only to be used during the VM_RedefineClasses op (a safepoint). +// +// This class is used after the new methods have been installed in "the_class". +// +// So, for example, the following must be handled. Where 'm' is a method and +// a number followed by an underscore is a prefix. +// +// Old Name New Name +// Simple transfer to new method m -> m +// Add prefix m -> 1_m +// Remove prefix 1_m -> m +// Simultaneous add of prefixes m -> 3_2_1_m +// Simultaneous removal of prefixes 3_2_1_m -> m +// Simultaneous add and remove 1_m -> 2_m +// Same, caused by prefix removal only 3_2_1_m -> 3_2_m +// +class TransferNativeFunctionRegistration { + private: + instanceKlassHandle the_class; + int prefix_count; + char** prefixes; + + // Recursively search the binary tree of possibly prefixed method names. + // Iteration could be used if all agents were well behaved. Full tree walk is + // more resilent to agents not cleaning up intermediate methods. + // Branch at each depth in the binary tree is: + // (1) without the prefix. + // (2) with the prefix. + // where 'prefix' is the prefix at that 'depth' (first prefix, second prefix,...) + methodOop search_prefix_name_space(int depth, char* name_str, size_t name_len, + symbolOop signature) { + symbolOop name_symbol = SymbolTable::probe(name_str, (int)name_len); + if (name_symbol != NULL) { + methodOop method = Klass::cast(the_class())->lookup_method(name_symbol, signature); + if (method != NULL) { + // Even if prefixed, intermediate methods must exist. + if (method->is_native()) { + // Wahoo, we found a (possibly prefixed) version of the method, return it. + return method; + } + if (depth < prefix_count) { + // Try applying further prefixes (other than this one). + method = search_prefix_name_space(depth+1, name_str, name_len, signature); + if (method != NULL) { + return method; // found + } + + // Try adding this prefix to the method name and see if it matches + // another method name. + char* prefix = prefixes[depth]; + size_t prefix_len = strlen(prefix); + size_t trial_len = name_len + prefix_len; + char* trial_name_str = NEW_RESOURCE_ARRAY(char, trial_len + 1); + strcpy(trial_name_str, prefix); + strcat(trial_name_str, name_str); + method = search_prefix_name_space(depth+1, trial_name_str, trial_len, + signature); + if (method != NULL) { + // If found along this branch, it was prefixed, mark as such + method->set_is_prefixed_native(); + return method; // found + } + } + } + } + return NULL; // This whole branch bore nothing + } + + // Return the method name with old prefixes stripped away. + char* method_name_without_prefixes(methodOop method) { + symbolOop name = method->name(); + char* name_str = name->as_utf8(); + + // Old prefixing may be defunct, strip prefixes, if any. + for (int i = prefix_count-1; i >= 0; i--) { + char* prefix = prefixes[i]; + size_t prefix_len = strlen(prefix); + if (strncmp(prefix, name_str, prefix_len) == 0) { + name_str += prefix_len; + } + } + return name_str; + } + + // Strip any prefixes off the old native method, then try to find a + // (possibly prefixed) new native that matches it. + methodOop strip_and_search_for_new_native(methodOop method) { + ResourceMark rm; + char* name_str = method_name_without_prefixes(method); + return search_prefix_name_space(0, name_str, strlen(name_str), + method->signature()); + } + + public: + + // Construct a native method transfer processor for this class. + TransferNativeFunctionRegistration(instanceKlassHandle _the_class) { + assert(SafepointSynchronize::is_at_safepoint(), "sanity check"); + + the_class = _the_class; + prefixes = JvmtiExport::get_all_native_method_prefixes(&prefix_count); + } + + // Attempt to transfer any of the old or deleted methods that are native + void transfer_registrations(methodOop* old_methods, int methods_length) { + for (int j = 0; j < methods_length; j++) { + methodOop old_method = old_methods[j]; + + if (old_method->is_native() && old_method->has_native_function()) { + methodOop new_method = strip_and_search_for_new_native(old_method); + if (new_method != NULL) { + // Actually set the native function in the new method. + // Redefine does not send events (except CFLH), certainly not this + // behind the scenes re-registration. + new_method->set_native_function(old_method->native_function(), + !methodOopDesc::native_bind_event_is_interesting); + } + } + } + } +}; + +// Don't lose the association between a native method and its JNI function. +void VM_RedefineClasses::transfer_old_native_function_registrations(instanceKlassHandle the_class) { + TransferNativeFunctionRegistration transfer(the_class); + transfer.transfer_registrations(_deleted_methods, _deleted_methods_length); + transfer.transfer_registrations(_matching_old_methods, _matching_methods_length); +} + +// Deoptimize all compiled code that depends on this class. +// +// If the can_redefine_classes capability is obtained in the onload +// phase then the compiler has recorded all dependencies from startup. +// In that case we need only deoptimize and throw away all compiled code +// that depends on the class. +// +// If can_redefine_classes is obtained sometime after the onload +// phase then the dependency information may be incomplete. In that case +// the first call to RedefineClasses causes all compiled code to be +// thrown away. As can_redefine_classes has been obtained then +// all future compilations will record dependencies so second and +// subsequent calls to RedefineClasses need only throw away code +// that depends on the class. +// +void VM_RedefineClasses::flush_dependent_code(instanceKlassHandle k_h, TRAPS) { + assert_locked_or_safepoint(Compile_lock); + + // All dependencies have been recorded from startup or this is a second or + // subsequent use of RedefineClasses + if (JvmtiExport::all_dependencies_are_recorded()) { + Universe::flush_evol_dependents_on(k_h); + } else { + CodeCache::mark_all_nmethods_for_deoptimization(); + + ResourceMark rm(THREAD); + DeoptimizationMarker dm; + + // Deoptimize all activations depending on marked nmethods + Deoptimization::deoptimize_dependents(); + + // Make the dependent methods not entrant (in VM_Deoptimize they are made zombies) + CodeCache::make_marked_nmethods_not_entrant(); + + // From now on we know that the dependency information is complete + JvmtiExport::set_all_dependencies_are_recorded(true); + } +} + +void VM_RedefineClasses::compute_added_deleted_matching_methods() { + methodOop old_method; + methodOop new_method; + + _matching_old_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length()); + _matching_new_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length()); + _added_methods = NEW_RESOURCE_ARRAY(methodOop, _new_methods->length()); + _deleted_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length()); + + _matching_methods_length = 0; + _deleted_methods_length = 0; + _added_methods_length = 0; + + int nj = 0; + int oj = 0; + while (true) { + if (oj >= _old_methods->length()) { + if (nj >= _new_methods->length()) { + break; // we've looked at everything, done + } + // New method at the end + new_method = (methodOop) _new_methods->obj_at(nj); + _added_methods[_added_methods_length++] = new_method; + ++nj; + } else if (nj >= _new_methods->length()) { + // Old method, at the end, is deleted + old_method = (methodOop) _old_methods->obj_at(oj); + _deleted_methods[_deleted_methods_length++] = old_method; + ++oj; + } else { + old_method = (methodOop) _old_methods->obj_at(oj); + new_method = (methodOop) _new_methods->obj_at(nj); + if (old_method->name() == new_method->name()) { + if (old_method->signature() == new_method->signature()) { + _matching_old_methods[_matching_methods_length ] = old_method; + _matching_new_methods[_matching_methods_length++] = new_method; + ++nj; + ++oj; + } else { + // added overloaded have already been moved to the end, + // so this is a deleted overloaded method + _deleted_methods[_deleted_methods_length++] = old_method; + ++oj; + } + } else { // names don't match + if (old_method->name()->fast_compare(new_method->name()) > 0) { + // new method + _added_methods[_added_methods_length++] = new_method; + ++nj; + } else { + // deleted method + _deleted_methods[_deleted_methods_length++] = old_method; + ++oj; + } + } + } + } + assert(_matching_methods_length + _deleted_methods_length == _old_methods->length(), "sanity"); + assert(_matching_methods_length + _added_methods_length == _new_methods->length(), "sanity"); +} + + + +// Install the redefinition of a class: +// - house keeping (flushing breakpoints and caches, deoptimizing +// dependent compiled code) +// - replacing parts in the_class with parts from scratch_class +// - adding a weak reference to track the obsolete but interesting +// parts of the_class +// - adjusting constant pool caches and vtables in other classes +// that refer to methods in the_class. These adjustments use the +// SystemDictionary::classes_do() facility which only allows +// a helper method to be specified. The interesting parameters +// that we would like to pass to the helper method are saved in +// static global fields in the VM operation. +void VM_RedefineClasses::redefine_single_class(jclass the_jclass, + instanceKlassHandle scratch_class, TRAPS) { + + RC_TIMER_START(_timer_rsc_phase1); + + oop the_class_mirror = JNIHandles::resolve_non_null(the_jclass); + klassOop the_class_oop = java_lang_Class::as_klassOop(the_class_mirror); + instanceKlassHandle the_class = instanceKlassHandle(THREAD, the_class_oop); + +#ifndef JVMTI_KERNEL + // Remove all breakpoints in methods of this class + JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints(); + jvmti_breakpoints.clearall_in_class_at_safepoint(the_class_oop); +#endif // !JVMTI_KERNEL + + if (the_class_oop == Universe::reflect_invoke_cache()->klass()) { + // We are redefining java.lang.reflect.Method. Method.invoke() is + // cached and users of the cache care about each active version of + // the method so we have to track this previous version. + // Do this before methods get switched + Universe::reflect_invoke_cache()->add_previous_version( + the_class->method_with_idnum(Universe::reflect_invoke_cache()->method_idnum())); + } + + // Deoptimize all compiled code that depends on this class + flush_dependent_code(the_class, THREAD); + + _old_methods = the_class->methods(); + _new_methods = scratch_class->methods(); + _the_class_oop = the_class_oop; + compute_added_deleted_matching_methods(); + update_jmethod_ids(); + + // Attach new constant pool to the original klass. The original + // klass still refers to the old constant pool (for now). + scratch_class->constants()->set_pool_holder(the_class()); + +#if 0 + // In theory, with constant pool merging in place we should be able + // to save space by using the new, merged constant pool in place of + // the old constant pool(s). By "pool(s)" I mean the constant pool in + // the klass version we are replacing now and any constant pool(s) in + // previous versions of klass. Nice theory, doesn't work in practice. + // When this code is enabled, even simple programs throw NullPointer + // exceptions. I'm guessing that this is caused by some constant pool + // cache difference between the new, merged constant pool and the + // constant pool that was just being used by the klass. I'm keeping + // this code around to archive the idea, but the code has to remain + // disabled for now. + + // Attach each old method to the new constant pool. This can be + // done here since we are past the bytecode verification and + // constant pool optimization phases. + for (int i = _old_methods->length() - 1; i >= 0; i--) { + methodOop method = (methodOop)_old_methods->obj_at(i); + method->set_constants(scratch_class->constants()); + } + + { + // walk all previous versions of the klass + instanceKlass *ik = (instanceKlass *)the_class()->klass_part(); + PreviousVersionWalker pvw(ik); + instanceKlassHandle ikh; + do { + ikh = pvw.next_previous_version(); + if (!ikh.is_null()) { + ik = ikh(); + + // attach previous version of klass to the new constant pool + ik->set_constants(scratch_class->constants()); + + // Attach each method in the previous version of klass to the + // new constant pool + objArrayOop prev_methods = ik->methods(); + for (int i = prev_methods->length() - 1; i >= 0; i--) { + methodOop method = (methodOop)prev_methods->obj_at(i); + method->set_constants(scratch_class->constants()); + } + } + } while (!ikh.is_null()); + } +#endif + + // Replace methods and constantpool + the_class->set_methods(_new_methods); + scratch_class->set_methods(_old_methods); // To prevent potential GCing of the old methods, + // and to be able to undo operation easily. + + constantPoolOop old_constants = the_class->constants(); + the_class->set_constants(scratch_class->constants()); + scratch_class->set_constants(old_constants); // See the previous comment. +#if 0 + // We are swapping the guts of "the new class" with the guts of "the + // class". Since the old constant pool has just been attached to "the + // new class", it seems logical to set the pool holder in the old + // constant pool also. However, doing this will change the observable + // class hierarchy for any old methods that are still executing. A + // method can query the identity of its "holder" and this query uses + // the method's constant pool link to find the holder. The change in + // holding class from "the class" to "the new class" can confuse + // things. + // + // Setting the old constant pool's holder will also cause + // verification done during vtable initialization below to fail. + // During vtable initialization, the vtable's class is verified to be + // a subtype of the method's holder. The vtable's class is "the + // class" and the method's holder is gotten from the constant pool + // link in the method itself. For "the class"'s directly implemented + // methods, the method holder is "the class" itself (as gotten from + // the new constant pool). The check works fine in this case. The + // check also works fine for methods inherited from super classes. + // + // Miranda methods are a little more complicated. A miranda method is + // provided by an interface when the class implementing the interface + // does not provide its own method. These interfaces are implemented + // internally as an instanceKlass. These special instanceKlasses + // share the constant pool of the class that "implements" the + // interface. By sharing the constant pool, the method holder of a + // miranda method is the class that "implements" the interface. In a + // non-redefine situation, the subtype check works fine. However, if + // the old constant pool's pool holder is modified, then the check + // fails because there is no class hierarchy relationship between the + // vtable's class and "the new class". + + old_constants->set_pool_holder(scratch_class()); +#endif + + // track which methods are EMCP for add_previous_version() call below + BitMap emcp_methods(_old_methods->length()); + int emcp_method_count = 0; + emcp_methods.clear(); // clears 0..(length() - 1) + check_methods_and_mark_as_obsolete(&emcp_methods, &emcp_method_count); + transfer_old_native_function_registrations(the_class); + + // The class file bytes from before any retransformable agents mucked + // with them was cached on the scratch class, move to the_class. + // Note: we still want to do this if nothing needed caching since it + // should get cleared in the_class too. + the_class->set_cached_class_file(scratch_class->get_cached_class_file_bytes(), + scratch_class->get_cached_class_file_len()); + + // Replace inner_classes + typeArrayOop old_inner_classes = the_class->inner_classes(); + the_class->set_inner_classes(scratch_class->inner_classes()); + scratch_class->set_inner_classes(old_inner_classes); + + // Initialize the vtable and interface table after + // methods have been rewritten + { + ResourceMark rm(THREAD); + // no exception should happen here since we explicitly + // do not check loader constraints. + // compare_and_normalize_class_versions has already checked: + // - classloaders unchanged, signatures unchanged + // - all instanceKlasses for redefined classes reused & contents updated + the_class->vtable()->initialize_vtable(false, THREAD); + the_class->itable()->initialize_itable(false, THREAD); + assert(!HAS_PENDING_EXCEPTION || (THREAD->pending_exception()->is_a(SystemDictionary::threaddeath_klass())), "redefine exception"); + } + + // Leave arrays of jmethodIDs and itable index cache unchanged + + // Copy the "source file name" attribute from new class version + the_class->set_source_file_name(scratch_class->source_file_name()); + + // Copy the "source debug extension" attribute from new class version + the_class->set_source_debug_extension( + scratch_class->source_debug_extension()); + + // Use of javac -g could be different in the old and the new + if (scratch_class->access_flags().has_localvariable_table() != + the_class->access_flags().has_localvariable_table()) { + + AccessFlags flags = the_class->access_flags(); + if (scratch_class->access_flags().has_localvariable_table()) { + flags.set_has_localvariable_table(); + } else { + flags.clear_has_localvariable_table(); + } + the_class->set_access_flags(flags); + } + + // Replace class annotation fields values + typeArrayOop old_class_annotations = the_class->class_annotations(); + the_class->set_class_annotations(scratch_class->class_annotations()); + scratch_class->set_class_annotations(old_class_annotations); + + // Replace fields annotation fields values + objArrayOop old_fields_annotations = the_class->fields_annotations(); + the_class->set_fields_annotations(scratch_class->fields_annotations()); + scratch_class->set_fields_annotations(old_fields_annotations); + + // Replace methods annotation fields values + objArrayOop old_methods_annotations = the_class->methods_annotations(); + the_class->set_methods_annotations(scratch_class->methods_annotations()); + scratch_class->set_methods_annotations(old_methods_annotations); + + // Replace methods parameter annotation fields values + objArrayOop old_methods_parameter_annotations = + the_class->methods_parameter_annotations(); + the_class->set_methods_parameter_annotations( + scratch_class->methods_parameter_annotations()); + scratch_class->set_methods_parameter_annotations(old_methods_parameter_annotations); + + // Replace methods default annotation fields values + objArrayOop old_methods_default_annotations = + the_class->methods_default_annotations(); + the_class->set_methods_default_annotations( + scratch_class->methods_default_annotations()); + scratch_class->set_methods_default_annotations(old_methods_default_annotations); + + // Replace minor version number of class file + u2 old_minor_version = the_class->minor_version(); + the_class->set_minor_version(scratch_class->minor_version()); + scratch_class->set_minor_version(old_minor_version); + + // Replace major version number of class file + u2 old_major_version = the_class->major_version(); + the_class->set_major_version(scratch_class->major_version()); + scratch_class->set_major_version(old_major_version); + + // Replace CP indexes for class and name+type of enclosing method + u2 old_class_idx = the_class->enclosing_method_class_index(); + u2 old_method_idx = the_class->enclosing_method_method_index(); + the_class->set_enclosing_method_indices( + scratch_class->enclosing_method_class_index(), + scratch_class->enclosing_method_method_index()); + scratch_class->set_enclosing_method_indices(old_class_idx, old_method_idx); + + // keep track of previous versions of this class + the_class->add_previous_version(scratch_class, &emcp_methods, + emcp_method_count); + + RC_TIMER_STOP(_timer_rsc_phase1); + RC_TIMER_START(_timer_rsc_phase2); + + // Adjust constantpool caches and vtables for all classes + // that reference methods of the evolved class. + SystemDictionary::classes_do(adjust_cpool_cache_and_vtable, THREAD); + + if (the_class->oop_map_cache() != NULL) { + // Flush references to any obsolete methods from the oop map cache + // so that obsolete methods are not pinned. + the_class->oop_map_cache()->flush_obsolete_entries(); + } + + // increment the classRedefinedCount field in the_class and in any + // direct and indirect subclasses of the_class + increment_class_counter((instanceKlass *)the_class()->klass_part(), THREAD); + + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE_WITH_THREAD(0x00000001, THREAD, + ("redefined name=%s, count=%d (avail_mem=" UINT64_FORMAT "K)", + the_class->external_name(), + java_lang_Class::classRedefinedCount(the_class_mirror), + os::available_memory() >> 10)); + + RC_TIMER_STOP(_timer_rsc_phase2); +} // end redefine_single_class() + + +// Increment the classRedefinedCount field in the specific instanceKlass +// and in all direct and indirect subclasses. +void VM_RedefineClasses::increment_class_counter(instanceKlass *ik, TRAPS) { + oop class_mirror = ik->java_mirror(); + klassOop class_oop = java_lang_Class::as_klassOop(class_mirror); + int new_count = java_lang_Class::classRedefinedCount(class_mirror) + 1; + java_lang_Class::set_classRedefinedCount(class_mirror, new_count); + + if (class_oop != _the_class_oop) { + // _the_class_oop count is printed at end of redefine_single_class() + RC_TRACE_WITH_THREAD(0x00000008, THREAD, + ("updated count in subclass=%s to %d", ik->external_name(), new_count)); + } + + for (Klass *subk = ik->subklass(); subk != NULL; + subk = subk->next_sibling()) { + klassOop sub = subk->as_klassOop(); + instanceKlass *subik = (instanceKlass *)sub->klass_part(); + + // recursively do subclasses of the current subclass + increment_class_counter(subik, THREAD); + } +} + +#ifndef PRODUCT +void VM_RedefineClasses::check_class(klassOop k_oop, + oop initiating_loader, TRAPS) { + Klass *k = k_oop->klass_part(); + if (k->oop_is_instance()) { + HandleMark hm(THREAD); + instanceKlass *ik = (instanceKlass *) k; + + if (ik->vtable_length() > 0) { + ResourceMark rm(THREAD); + if (!ik->vtable()->check_no_old_entries()) { + tty->print_cr("klassVtable::check_no_old_entries failure -- OLD method found -- class: %s", ik->signature_name()); + ik->vtable()->dump_vtable(); + dump_methods(); + assert(false, "OLD method found"); + } + } + } +} + +void VM_RedefineClasses::dump_methods() { + int j; + tty->print_cr("_old_methods --"); + for (j = 0; j < _old_methods->length(); ++j) { + methodOop m = (methodOop) _old_methods->obj_at(j); + tty->print("%4d (%5d) ", j, m->vtable_index()); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } + tty->print_cr("_new_methods --"); + for (j = 0; j < _new_methods->length(); ++j) { + methodOop m = (methodOop) _new_methods->obj_at(j); + tty->print("%4d (%5d) ", j, m->vtable_index()); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } + tty->print_cr("_matching_(old/new)_methods --"); + for (j = 0; j < _matching_methods_length; ++j) { + methodOop m = _matching_old_methods[j]; + tty->print("%4d (%5d) ", j, m->vtable_index()); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + m = _matching_new_methods[j]; + tty->print(" (%5d) ", m->vtable_index()); + m->access_flags().print_on(tty); + tty->cr(); + } + tty->print_cr("_deleted_methods --"); + for (j = 0; j < _deleted_methods_length; ++j) { + methodOop m = _deleted_methods[j]; + tty->print("%4d (%5d) ", j, m->vtable_index()); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } + tty->print_cr("_added_methods --"); + for (j = 0; j < _added_methods_length; ++j) { + methodOop m = _added_methods[j]; + tty->print("%4d (%5d) ", j, m->vtable_index()); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } +} +#endif diff --git a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp new file mode 100644 index 00000000000..34b04db9274 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp @@ -0,0 +1,489 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Introduction: +// +// The RedefineClasses() API is used to change the definition of one or +// more classes. While the API supports redefining more than one class +// in a single call, in general, the API is discussed in the context of +// changing the definition of a single current class to a single new +// class. For clarity, the current class is will always be called +// "the_class" and the new class will always be called "scratch_class". +// +// The name "the_class" is used because there is only one structure +// that represents a specific class; redefinition does not replace the +// structure, but instead replaces parts of the structure. The name +// "scratch_class" is used because the structure that represents the +// new definition of a specific class is simply used to carry around +// the parts of the new definition until they are used to replace the +// appropriate parts in the_class. Once redefinition of a class is +// complete, scratch_class is thrown away. +// +// +// Implementation Overview: +// +// The RedefineClasses() API is mostly a wrapper around the VM op that +// does the real work. The work is split in varying degrees between +// doit_prologue(), doit() and doit_epilogue(). +// +// 1) doit_prologue() is called by the JavaThread on the way to a +// safepoint. It does parameter verification and loads scratch_class +// which involves: +// - parsing the incoming class definition using the_class' class +// loader and security context +// - linking scratch_class +// - merging constant pools and rewriting bytecodes as needed +// for the merged constant pool +// - verifying the bytecodes in scratch_class +// - setting up the constant pool cache and rewriting bytecodes +// as needed to use the cache +// - finally, scratch_class is compared to the_class to verify +// that it is a valid replacement class +// - if everything is good, then scratch_class is saved in an +// instance field in the VM operation for the doit() call +// +// Note: A JavaThread must do the above work. +// +// 2) doit() is called by the VMThread during a safepoint. It installs +// the new class definition(s) which involves: +// - retrieving the scratch_class from the instance field in the +// VM operation +// - house keeping (flushing breakpoints and caches, deoptimizing +// dependent compiled code) +// - replacing parts in the_class with parts from scratch_class +// - adding weak reference(s) to track the obsolete but interesting +// parts of the_class +// - adjusting constant pool caches and vtables in other classes +// that refer to methods in the_class. These adjustments use the +// SystemDictionary::classes_do() facility which only allows +// a helper method to be specified. The interesting parameters +// that we would like to pass to the helper method are saved in +// static global fields in the VM operation. +// - telling the SystemDictionary to notice our changes +// +// Note: the above work must be done by the VMThread to be safe. +// +// 3) doit_epilogue() is called by the JavaThread after the VM op +// is finished and the safepoint is done. It simply cleans up +// memory allocated in doit_prologue() and used in doit(). +// +// +// Constant Pool Details: +// +// When the_class is redefined, we cannot just replace the constant +// pool in the_class with the constant pool from scratch_class because +// that could confuse obsolete methods that may still be running. +// Instead, the constant pool from the_class, old_cp, is merged with +// the constant pool from scratch_class, scratch_cp. The resulting +// constant pool, merge_cp, replaces old_cp in the_class. +// +// The key part of any merging algorithm is the entry comparison +// function so we have to know the types of entries in a constant pool +// in order to merge two of them together. Constant pools can contain +// up to 12 different kinds of entries; the JVM_CONSTANT_Unicode entry +// is not presently used so we only have to worry about the other 11 +// entry types. For the purposes of constant pool merging, it is +// helpful to know that the 11 entry types fall into 3 different +// subtypes: "direct", "indirect" and "double-indirect". +// +// Direct CP entries contain data and do not contain references to +// other CP entries. The following are direct CP entries: +// JVM_CONSTANT_{Double,Float,Integer,Long,Utf8} +// +// Indirect CP entries contain 1 or 2 references to a direct CP entry +// and no other data. The following are indirect CP entries: +// JVM_CONSTANT_{Class,NameAndType,String} +// +// Double-indirect CP entries contain two references to indirect CP +// entries and no other data. The following are double-indirect CP +// entries: +// JVM_CONSTANT_{Fieldref,InterfaceMethodref,Methodref} +// +// When comparing entries between two constant pools, the entry types +// are compared first and if they match, then further comparisons are +// made depending on the entry subtype. Comparing direct CP entries is +// simply a matter of comparing the data associated with each entry. +// Comparing both indirect and double-indirect CP entries requires +// recursion. +// +// Fortunately, the recursive combinations are limited because indirect +// CP entries can only refer to direct CP entries and double-indirect +// CP entries can only refer to indirect CP entries. The following is +// an example illustration of the deepest set of indirections needed to +// access the data associated with a JVM_CONSTANT_Fieldref entry: +// +// JVM_CONSTANT_Fieldref { +// class_index => JVM_CONSTANT_Class { +// name_index => JVM_CONSTANT_Utf8 { +// +// } +// } +// name_and_type_index => JVM_CONSTANT_NameAndType { +// name_index => JVM_CONSTANT_Utf8 { +// +// } +// descriptor_index => JVM_CONSTANT_Utf8 { +// +// } +// } +// } +// +// The above illustration is not a data structure definition for any +// computer language. The curly braces ('{' and '}') are meant to +// delimit the context of the "fields" in the CP entry types shown. +// Each indirection from the JVM_CONSTANT_Fieldref entry is shown via +// "=>", e.g., the class_index is used to indirectly reference a +// JVM_CONSTANT_Class entry where the name_index is used to indirectly +// reference a JVM_CONSTANT_Utf8 entry which contains the interesting +// . In order to understand a JVM_CONSTANT_Fieldref entry, we +// have to do a total of 5 indirections just to get to the CP entries +// that contain the interesting pieces of data and then we have to +// fetch the three pieces of data. This means we have to do a total of +// (5 + 3) * 2 == 16 dereferences to compare two JVM_CONSTANT_Fieldref +// entries. +// +// Here is the indirection, data and dereference count for each entry +// type: +// +// JVM_CONSTANT_Class 1 indir, 1 data, 2 derefs +// JVM_CONSTANT_Double 0 indir, 1 data, 1 deref +// JVM_CONSTANT_Fieldref 2 indir, 3 data, 8 derefs +// JVM_CONSTANT_Float 0 indir, 1 data, 1 deref +// JVM_CONSTANT_Integer 0 indir, 1 data, 1 deref +// JVM_CONSTANT_InterfaceMethodref 2 indir, 3 data, 8 derefs +// JVM_CONSTANT_Long 0 indir, 1 data, 1 deref +// JVM_CONSTANT_Methodref 2 indir, 3 data, 8 derefs +// JVM_CONSTANT_NameAndType 1 indir, 2 data, 4 derefs +// JVM_CONSTANT_String 1 indir, 1 data, 2 derefs +// JVM_CONSTANT_Utf8 0 indir, 1 data, 1 deref +// +// So different subtypes of CP entries require different amounts of +// work for a proper comparison. +// +// Now that we've talked about the different entry types and how to +// compare them we need to get back to merging. This is not a merge in +// the "sort -u" sense or even in the "sort" sense. When we merge two +// constant pools, we copy all the entries from old_cp to merge_cp, +// preserving entry order. Next we append all the unique entries from +// scratch_cp to merge_cp and we track the index changes from the +// location in scratch_cp to the possibly new location in merge_cp. +// When we are done, any obsolete code that is still running that +// uses old_cp should not be able to observe any difference if it +// were to use merge_cp. As for the new code in scratch_class, it is +// modified to use the appropriate index values in merge_cp before it +// is used to replace the code in the_class. +// +// There is one small complication in copying the entries from old_cp +// to merge_cp. Two of the CP entry types are special in that they are +// lazily resolved. Before explaining the copying complication, we need +// to digress into CP entry resolution. +// +// JVM_CONSTANT_Class and JVM_CONSTANT_String entries are present in +// the class file, but are not stored in memory as such until they are +// resolved. The entries are not resolved unless they are used because +// resolution is expensive. During class file parsing the entries are +// initially stored in memory as JVM_CONSTANT_ClassIndex and +// JVM_CONSTANT_StringIndex entries. These special CP entry types +// indicate that the JVM_CONSTANT_Class and JVM_CONSTANT_String entries +// have been parsed, but the index values in the entries have not been +// validated. After the entire constant pool has been parsed, the index +// values can be validated and then the entries are converted into +// JVM_CONSTANT_UnresolvedClass and JVM_CONSTANT_UnresolvedString +// entries. During this conversion process, the UTF8 values that are +// indirectly referenced by the JVM_CONSTANT_ClassIndex and +// JVM_CONSTANT_StringIndex entries are changed into symbolOops and the +// entries are modified to refer to the symbolOops. This optimization +// eliminates one level of indirection for those two CP entry types and +// gets the entries ready for verification. During class file parsing +// it is also possible for JVM_CONSTANT_UnresolvedString entries to be +// resolved into JVM_CONSTANT_String entries. Verification expects to +// find JVM_CONSTANT_UnresolvedClass and either JVM_CONSTANT_String or +// JVM_CONSTANT_UnresolvedString entries and not JVM_CONSTANT_Class +// entries. +// +// Now we can get back to the copying complication. When we copy +// entries from old_cp to merge_cp, we have to revert any +// JVM_CONSTANT_Class entries to JVM_CONSTANT_UnresolvedClass entries +// or verification will fail. +// +// It is important to explicitly state that the merging algorithm +// effectively unresolves JVM_CONSTANT_Class entries that were in the +// old_cp when they are changed into JVM_CONSTANT_UnresolvedClass +// entries in the merge_cp. This is done both to make verification +// happy and to avoid adding more brittleness between RedefineClasses +// and the constant pool cache. By allowing the constant pool cache +// implementation to (re)resolve JVM_CONSTANT_UnresolvedClass entries +// into JVM_CONSTANT_Class entries, we avoid having to embed knowledge +// about those algorithms in RedefineClasses. +// +// Appending unique entries from scratch_cp to merge_cp is straight +// forward for direct CP entries and most indirect CP entries. For the +// indirect CP entry type JVM_CONSTANT_NameAndType and for the double- +// indirect CP entry types, the presence of more than one piece of +// interesting data makes appending the entries more complicated. +// +// For the JVM_CONSTANT_{Double,Float,Integer,Long,Utf8} entry types, +// the entry is simply copied from scratch_cp to the end of merge_cp. +// If the index in scratch_cp is different than the destination index +// in merge_cp, then the change in index value is tracked. +// +// Note: the above discussion for the direct CP entries also applies +// to the JVM_CONSTANT_Unresolved{Class,String} entry types. +// +// For the JVM_CONSTANT_{Class,String} entry types, since there is only +// one data element at the end of the recursion, we know that we have +// either one or two unique entries. If the JVM_CONSTANT_Utf8 entry is +// unique then it is appended to merge_cp before the current entry. +// If the JVM_CONSTANT_Utf8 entry is not unique, then the current entry +// is updated to refer to the duplicate entry in merge_cp before it is +// appended to merge_cp. Again, any changes in index values are tracked +// as needed. +// +// Note: the above discussion for JVM_CONSTANT_{Class,String} entry +// types is theoretical. Since those entry types have already been +// optimized into JVM_CONSTANT_Unresolved{Class,String} entry types, +// they are handled as direct CP entries. +// +// For the JVM_CONSTANT_NameAndType entry type, since there are two +// data elements at the end of the recursions, we know that we have +// between one and three unique entries. Any unique JVM_CONSTANT_Utf8 +// entries are appended to merge_cp before the current entry. For any +// JVM_CONSTANT_Utf8 entries that are not unique, the current entry is +// updated to refer to the duplicate entry in merge_cp before it is +// appended to merge_cp. Again, any changes in index values are tracked +// as needed. +// +// For the JVM_CONSTANT_{Fieldref,InterfaceMethodref,Methodref} entry +// types, since there are two indirect CP entries and three data +// elements at the end of the recursions, we know that we have between +// one and six unique entries. See the JVM_CONSTANT_Fieldref diagram +// above for an example of all six entries. The uniqueness algorithm +// for the JVM_CONSTANT_Class and JVM_CONSTANT_NameAndType entries is +// covered above. Any unique entries are appended to merge_cp before +// the current entry. For any entries that are not unique, the current +// entry is updated to refer to the duplicate entry in merge_cp before +// it is appended to merge_cp. Again, any changes in index values are +// tracked as needed. +// +// +// Other Details: +// +// Details for other parts of RedefineClasses need to be written. +// This is a placeholder section. +// +// +// Open Issues (in no particular order): +// +// - How do we serialize the RedefineClasses() API without deadlocking? +// +// - SystemDictionary::parse_stream() was called with a NULL protection +// domain since the initial version. This has been changed to pass +// the_class->protection_domain(). This change has been tested with +// all NSK tests and nothing broke, but what will adding it now break +// in ways that we don't test? +// +// - GenerateOopMap::rewrite_load_or_store() has a comment in its +// (indirect) use of the Relocator class that the max instruction +// size is 4 bytes. goto_w and jsr_w are 5 bytes and wide/iinc is +// 6 bytes. Perhaps Relocator only needs a 4 byte buffer to do +// what it does to the bytecodes. More investigation is needed. +// +// - java.lang.Object methods can be called on arrays. This is +// implemented via the arrayKlassOop vtable which we don't +// update. For example, if we redefine java.lang.Object.toString(), +// then the new version of the method will not be called for array +// objects. +// +// - How do we know if redefine_single_class() and the guts of +// instanceKlass are out of sync? I don't think this can be +// automated, but we should probably order the work in +// redefine_single_class() to match the order of field +// definitions in instanceKlass. We also need to add some +// comments about keeping things in sync. +// +// - set_new_constant_pool() is huge and we should consider refactoring +// it into smaller chunks of work. +// +// - The exception table update code in set_new_constant_pool() defines +// const values that are also defined in a local context elsewhere. +// The same literal values are also used in elsewhere. We need to +// coordinate a cleanup of these constants with Runtime. +// + +class VM_RedefineClasses: public VM_Operation { + private: + // These static fields are needed by SystemDictionary::classes_do() + // facility and the adjust_cpool_cache_and_vtable() helper: + static objArrayOop _old_methods; + static objArrayOop _new_methods; + static methodOop* _matching_old_methods; + static methodOop* _matching_new_methods; + static methodOop* _deleted_methods; + static methodOop* _added_methods; + static int _matching_methods_length; + static int _deleted_methods_length; + static int _added_methods_length; + static klassOop _the_class_oop; + + // The instance fields are used to pass information from + // doit_prologue() to doit() and doit_epilogue(). + jint _class_count; + const jvmtiClassDefinition *_class_defs; // ptr to _class_count defs + + // This operation is used by both RedefineClasses and + // RetransformClasses. Indicate which. + JvmtiClassLoadKind _class_load_kind; + + // _index_map_count is just an optimization for knowing if + // _index_map_p contains any entries. + int _index_map_count; + intArray * _index_map_p; + // ptr to _class_count scratch_classes + instanceKlassHandle * _scratch_classes; + jvmtiError _res; + + // Performance measurement support. These timers do not cover all + // the work done for JVM/TI RedefineClasses() but they do cover + // the heavy lifting. + elapsedTimer _timer_rsc_phase1; + elapsedTimer _timer_rsc_phase2; + elapsedTimer _timer_vm_op_prologue; + + // These routines are roughly in call order unless otherwise noted. + + // Load the caller's new class definition(s) into _scratch_classes. + // Constant pool merging work is done here as needed. Also calls + // compare_and_normalize_class_versions() to verify the class + // definition(s). + jvmtiError load_new_class_versions(TRAPS); + + // Verify that the caller provided class definition(s) that meet + // the restrictions of RedefineClasses. Normalize the order of + // overloaded methods as needed. + jvmtiError compare_and_normalize_class_versions( + instanceKlassHandle the_class, instanceKlassHandle scratch_class); + + // Swap annotations[i] with annotations[j] + // Used by compare_and_normalize_class_versions() when normalizing + // overloaded methods or changing idnum as when adding or deleting methods. + void swap_all_method_annotations(int i, int j, instanceKlassHandle scratch_class); + + // Figure out which new methods match old methods in name and signature, + // which methods have been added, and which are no longer present + void compute_added_deleted_matching_methods(); + + // Change jmethodIDs to point to the new methods + void update_jmethod_ids(); + + // In addition to marking methods as obsolete, this routine + // records which methods are EMCP (Equivalent Module Constant + // Pool) in the emcp_methods BitMap and returns the number of + // EMCP methods via emcp_method_count_p. This information is + // used when information about the previous version of the_class + // is squirreled away. + void check_methods_and_mark_as_obsolete(BitMap *emcp_methods, + int * emcp_method_count_p); + void transfer_old_native_function_registrations(instanceKlassHandle the_class); + + // Unevolving classes may point to methods of the_class directly + // from their constant pool caches, itables, and/or vtables. We + // use the SystemDictionary::classes_do() facility and this helper + // to fix up these pointers. + static void adjust_cpool_cache_and_vtable(klassOop k_oop, oop loader, TRAPS); + + // Install the redefinition of a class + void redefine_single_class(jclass the_jclass, + instanceKlassHandle scratch_class, TRAPS); + + // Increment the classRedefinedCount field in the specific instanceKlass + // and in all direct and indirect subclasses. + void increment_class_counter(instanceKlass *ik, TRAPS); + + // Support for constant pool merging (these routines are in alpha + // order): + void append_entry(constantPoolHandle scratch_cp, int scratch_i, + constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS); + int find_new_index(int old_index); + bool is_unresolved_class_mismatch(constantPoolHandle cp1, int index1, + constantPoolHandle cp2, int index2); + bool is_unresolved_string_mismatch(constantPoolHandle cp1, int index1, + constantPoolHandle cp2, int index2); + void map_index(constantPoolHandle scratch_cp, int old_index, int new_index); + bool merge_constant_pools(constantPoolHandle old_cp, + constantPoolHandle scratch_cp, constantPoolHandle *merge_cp_p, + int *merge_cp_length_p, TRAPS); + jvmtiError merge_cp_and_rewrite(instanceKlassHandle the_class, + instanceKlassHandle scratch_class, TRAPS); + u2 rewrite_cp_ref_in_annotation_data( + typeArrayHandle annotations_typeArray, int &byte_i_ref, + const char * trace_mesg, TRAPS); + bool rewrite_cp_refs(instanceKlassHandle scratch_class, TRAPS); + bool rewrite_cp_refs_in_annotation_struct( + typeArrayHandle class_annotations, int &byte_i_ref, TRAPS); + bool rewrite_cp_refs_in_annotations_typeArray( + typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS); + bool rewrite_cp_refs_in_class_annotations( + instanceKlassHandle scratch_class, TRAPS); + bool rewrite_cp_refs_in_element_value( + typeArrayHandle class_annotations, int &byte_i_ref, TRAPS); + bool rewrite_cp_refs_in_fields_annotations( + instanceKlassHandle scratch_class, TRAPS); + void rewrite_cp_refs_in_method(methodHandle method, + methodHandle * new_method_p, TRAPS); + bool rewrite_cp_refs_in_methods(instanceKlassHandle scratch_class, TRAPS); + bool rewrite_cp_refs_in_methods_annotations( + instanceKlassHandle scratch_class, TRAPS); + bool rewrite_cp_refs_in_methods_default_annotations( + instanceKlassHandle scratch_class, TRAPS); + bool rewrite_cp_refs_in_methods_parameter_annotations( + instanceKlassHandle scratch_class, TRAPS); + void rewrite_cp_refs_in_stack_map_table(methodHandle method, TRAPS); + void rewrite_cp_refs_in_verification_type_info( + address& stackmap_addr_ref, address stackmap_end, u2 frame_i, + u1 frame_size, TRAPS); + void set_new_constant_pool(instanceKlassHandle scratch_class, + constantPoolHandle scratch_cp, int scratch_cp_length, bool shrink, TRAPS); + + void flush_dependent_code(instanceKlassHandle k_h, TRAPS); + + static void check_class(klassOop k_oop, oop initiating_loader, TRAPS) PRODUCT_RETURN; + + static void dump_methods() PRODUCT_RETURN; + + public: + VM_RedefineClasses(jint class_count, + const jvmtiClassDefinition *class_defs, + JvmtiClassLoadKind class_load_kind); + VMOp_Type type() const { return VMOp_RedefineClasses; } + bool doit_prologue(); + void doit(); + void doit_epilogue(); + + bool allow_nested_vm_operations() const { return true; } + jvmtiError check_error() { return _res; } + + // Modifiable test must be shared between IsModifiableClass query + // and redefine implementation + static bool is_modifiable_class(oop klass_mirror); +}; diff --git a/hotspot/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp b/hotspot/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp new file mode 100644 index 00000000000..b527a8daec1 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp @@ -0,0 +1,123 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// RedefineClasses tracing support via the TraceRedefineClasses +// option. A bit is assigned to each group of trace messages. +// Groups of messages are individually selectable. We have to use +// decimal values on the command line since the command option +// parsing logic doesn't like non-decimal numerics. The HEX values +// are used in the actual RC_TRACE() calls for sanity. To achieve +// the old cumulative behavior, pick the level after the one in +// which you are interested and subtract one, e.g., 33554431 will +// print every tracing message. +// +// 0x00000000 | 0 - default; no tracing messages +// 0x00000001 | 1 - name each target class before loading, after +// loading and after redefinition is completed +// 0x00000002 | 2 - print info if parsing, linking or +// verification throws an exception +// 0x00000004 | 4 - print timer info for the VM operation +// 0x00000008 | 8 - print subclass counter updates +// 0x00000010 | 16 - unused +// 0x00000020 | 32 - unused +// 0x00000040 | 64 - unused +// 0x00000080 | 128 - unused +// 0x00000100 | 256 - previous class weak reference addition +// 0x00000200 | 512 - previous class weak reference mgmt during +// class unloading checks (GC) +// 0x00000400 | 1024 - previous class weak reference mgmt during +// add previous ops (GC) +// 0x00000800 | 2048 - previous class breakpoint mgmt +// 0x00001000 | 4096 - unused +// 0x00002000 | 8192 - unused +// 0x00004000 | 16384 - unused +// 0x00008000 | 32768 - old/new method matching/add/delete +// 0x00010000 | 65536 - impl details: CP size info +// 0x00020000 | 131072 - impl details: CP merge pass info +// 0x00040000 | 262144 - impl details: CP index maps +// 0x00080000 | 524288 - impl details: modified CP index values +// 0x00100000 | 1048576 - impl details: vtable updates +// 0x00200000 | 2097152 - impl details: itable updates +// 0x00400000 | 4194304 - impl details: constant pool cache updates +// 0x00800000 | 8388608 - impl details: methodComparator info +// 0x01000000 | 16777216 - impl details: nmethod evolution info +// 0x02000000 | 33554432 - impl details: annotation updates +// 0x04000000 | 67108864 - impl details: StackMapTable updates +// 0x08000000 | 134217728 - unused +// 0x10000000 | 268435456 - unused +// 0x20000000 | 536870912 - unused +// 0x40000000 | 1073741824 - unused +// 0x80000000 | 2147483648 - unused +// +// Note: The ResourceMark is to cleanup resource allocated args. +// The "while (0)" is so we can use semi-colon at end of RC_TRACE(). +#define RC_TRACE(level, args) \ + if ((TraceRedefineClasses & level) != 0) { \ + ResourceMark rm; \ + tty->print("RedefineClasses-0x%x: ", level); \ + tty->print_cr args; \ + } while (0) + +#define RC_TRACE_WITH_THREAD(level, thread, args) \ + if ((TraceRedefineClasses & level) != 0) { \ + ResourceMark rm(thread); \ + tty->print("RedefineClasses-0x%x: ", level); \ + tty->print_cr args; \ + } while (0) + +#define RC_TRACE_MESG(args) \ + { \ + ResourceMark rm; \ + tty->print("RedefineClasses: "); \ + tty->print_cr args; \ + } while (0) + +// Macro for checking if TraceRedefineClasses has a specific bit +// enabled. Returns true if the bit specified by level is set. +#define RC_TRACE_ENABLED(level) ((TraceRedefineClasses & level) != 0) + +// Macro for checking if TraceRedefineClasses has one or more bits +// set in a range of bit values. Returns true if one or more bits +// is set in the range from low..high inclusive. Assumes that low +// and high are single bit values. +// +// ((high << 1) - 1) +// Yields a mask that removes bits greater than the high bit value. +// This algorithm doesn't work with highest bit. +// ~(low - 1) +// Yields a mask that removes bits lower than the low bit value. +#define RC_TRACE_IN_RANGE(low, high) \ +(((TraceRedefineClasses & ((high << 1) - 1)) & ~(low - 1)) != 0) + +// Timer support macros. Only do timer operations if timer tracing +// is enabled. The "while (0)" is so we can use semi-colon at end of +// the macro. +#define RC_TIMER_START(t) \ + if (RC_TRACE_ENABLED(0x00000004)) { \ + t.start(); \ + } while (0) +#define RC_TIMER_STOP(t) \ + if (RC_TRACE_ENABLED(0x00000004)) { \ + t.stop(); \ + } while (0) diff --git a/hotspot/src/share/vm/prims/jvmtiTagMap.cpp b/hotspot/src/share/vm/prims/jvmtiTagMap.cpp new file mode 100644 index 00000000000..23e46ede884 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiTagMap.cpp @@ -0,0 +1,3535 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiTagMap.cpp.incl" + +// JvmtiTagHashmapEntry +// +// Each entry encapsulates a JNI weak reference to the tagged object +// and the tag value. In addition an entry includes a next pointer which +// is used to chain entries together. + +class JvmtiTagHashmapEntry : public CHeapObj { + private: + friend class JvmtiTagMap; + + jweak _object; // JNI weak ref to tagged object + jlong _tag; // the tag + JvmtiTagHashmapEntry* _next; // next on the list + + inline void init(jweak object, jlong tag) { + _object = object; + _tag = tag; + _next = NULL; + } + + // constructor + JvmtiTagHashmapEntry(jweak object, jlong tag) { init(object, tag); } + + public: + + // accessor methods + inline jweak object() const { return _object; } + inline jlong tag() const { return _tag; } + + inline void set_tag(jlong tag) { + assert(tag != 0, "can't be zero"); + _tag = tag; + } + + inline JvmtiTagHashmapEntry* next() const { return _next; } + inline void set_next(JvmtiTagHashmapEntry* next) { _next = next; } +}; + + +// JvmtiTagHashmap +// +// A hashmap is essentially a table of pointers to entries. Entries +// are hashed to a location, or position in the table, and then +// chained from that location. The "key" for hashing is address of +// the object, or oop. The "value" is the JNI weak reference to the +// object and the tag value. Keys are not stored with the entry. +// Instead the weak reference is resolved to obtain the key. +// +// A hashmap maintains a count of the number entries in the hashmap +// and resizes if the number of entries exceeds a given threshold. +// The threshold is specified as a percentage of the size - for +// example a threshold of 0.75 will trigger the hashmap to resize +// if the number of entries is >75% of table size. +// +// A hashmap provides functions for adding, removing, and finding +// entries. It also provides a function to iterate over all entries +// in the hashmap. + +class JvmtiTagHashmap : public CHeapObj { + private: + friend class JvmtiTagMap; + + enum { + small_trace_threshold = 10000, // threshold for tracing + medium_trace_threshold = 100000, + large_trace_threshold = 1000000, + initial_trace_threshold = small_trace_threshold + }; + + static int _sizes[]; // array of possible hashmap sizes + int _size; // actual size of the table + int _size_index; // index into size table + + int _entry_count; // number of entries in the hashmap + + float _load_factor; // load factor as a % of the size + int _resize_threshold; // computed threshold to trigger resizing. + bool _resizing_enabled; // indicates if hashmap can resize + + int _trace_threshold; // threshold for trace messages + + JvmtiTagHashmapEntry** _table; // the table of entries. + + // private accessors + int resize_threshold() const { return _resize_threshold; } + int trace_threshold() const { return _trace_threshold; } + + // initialize the hashmap + void init(int size_index=0, float load_factor=4.0f) { + int initial_size = _sizes[size_index]; + _size_index = size_index; + _size = initial_size; + _entry_count = 0; + if (TraceJVMTIObjectTagging) { + _trace_threshold = initial_trace_threshold; + } else { + _trace_threshold = -1; + } + _load_factor = load_factor; + _resize_threshold = (int)(_load_factor * _size); + _resizing_enabled = true; + size_t s = initial_size * sizeof(JvmtiTagHashmapEntry*); + _table = (JvmtiTagHashmapEntry**)os::malloc(s); + if (_table == NULL) { + vm_exit_out_of_memory(s, "unable to allocate initial hashtable for jvmti object tags"); + } + for (int i=0; i> 3) % size; +#else + return (addr >> 2) % size; +#endif + } + + // hash a given key (oop) + unsigned int hash(oop key) { + return hash(key, _size); + } + + // resize the hashmap - allocates a large table and re-hashes + // all entries into the new table. + void resize() { + int new_size_index = _size_index+1; + int new_size = _sizes[new_size_index]; + if (new_size < 0) { + // hashmap already at maximum capacity + return; + } + + // allocate new table + size_t s = new_size * sizeof(JvmtiTagHashmapEntry*); + JvmtiTagHashmapEntry** new_table = (JvmtiTagHashmapEntry**)os::malloc(s); + if (new_table == NULL) { + warning("unable to allocate larger hashtable for jvmti object tags"); + set_resizing_enabled(false); + return; + } + + // initialize new table + int i; + for (i=0; inext(); + oop key = JNIHandles::resolve(entry->object()); + assert(key != NULL, "jni weak reference cleared!!"); + unsigned int h = hash(key, new_size); + JvmtiTagHashmapEntry* anchor = new_table[h]; + if (anchor == NULL) { + new_table[h] = entry; + entry->set_next(NULL); + } else { + entry->set_next(anchor); + new_table[h] = entry; + } + entry = next; + } + } + + // free old table and update settings. + os::free((void*)_table); + _table = new_table; + _size_index = new_size_index; + _size = new_size; + + // compute new resize threshold + _resize_threshold = (int)(_load_factor * _size); + } + + + // internal remove function - remove an entry at a given position in the + // table. + inline void remove(JvmtiTagHashmapEntry* prev, int pos, JvmtiTagHashmapEntry* entry) { + assert(pos >= 0 && pos < _size, "out of range"); + if (prev == NULL) { + _table[pos] = entry->next(); + } else { + prev->set_next(entry->next()); + } + assert(_entry_count > 0, "checking"); + _entry_count--; + } + + // resizing switch + bool is_resizing_enabled() const { return _resizing_enabled; } + void set_resizing_enabled(bool enable) { _resizing_enabled = enable; } + + // debugging + void print_memory_usage(); + void compute_next_trace_threshold(); + + public: + + // create a JvmtiTagHashmap of a preferred size and optionally a load factor. + // The preferred size is rounded down to an actual size. + JvmtiTagHashmap(int size, float load_factor=0.0f) { + int i=0; + while (_sizes[i] < size) { + if (_sizes[i] < 0) { + assert(i > 0, "sanity check"); + i--; + break; + } + i++; + } + + // if a load factor is specified then use it, otherwise use default + if (load_factor > 0.01f) { + init(i, load_factor); + } else { + init(i); + } + } + + // create a JvmtiTagHashmap with default settings + JvmtiTagHashmap() { + init(); + } + + // release table when JvmtiTagHashmap destroyed + ~JvmtiTagHashmap() { + if (_table != NULL) { + os::free((void*)_table); + _table = NULL; + } + } + + // accessors + int size() const { return _size; } + JvmtiTagHashmapEntry** table() const { return _table; } + int entry_count() const { return _entry_count; } + + // find an entry in the hashmap, returns NULL if not found. + inline JvmtiTagHashmapEntry* find(oop key) { + unsigned int h = hash(key); + JvmtiTagHashmapEntry* entry = _table[h]; + while (entry != NULL) { + oop orig_key = JNIHandles::resolve(entry->object()); + assert(orig_key != NULL, "jni weak reference cleared!!"); + if (key == orig_key) { + break; + } + entry = entry->next(); + } + return entry; + } + + + // add a new entry to hashmap + inline void add(oop key, JvmtiTagHashmapEntry* entry) { + assert(key != NULL, "checking"); + assert(find(key) == NULL, "duplicate detected"); + unsigned int h = hash(key); + JvmtiTagHashmapEntry* anchor = _table[h]; + if (anchor == NULL) { + _table[h] = entry; + entry->set_next(NULL); + } else { + entry->set_next(anchor); + _table[h] = entry; + } + + _entry_count++; + if (trace_threshold() > 0 && entry_count() >= trace_threshold()) { + assert(TraceJVMTIObjectTagging, "should only get here when tracing"); + print_memory_usage(); + compute_next_trace_threshold(); + } + + // if the number of entries exceed the threshold then resize + if (entry_count() > resize_threshold() && is_resizing_enabled()) { + resize(); + } + } + + // remove an entry with the given key. + inline JvmtiTagHashmapEntry* remove(oop key) { + unsigned int h = hash(key); + JvmtiTagHashmapEntry* entry = _table[h]; + JvmtiTagHashmapEntry* prev = NULL; + while (entry != NULL) { + oop orig_key = JNIHandles::resolve(entry->object()); + assert(orig_key != NULL, "jni weak reference cleared!!"); + if (key == orig_key) { + break; + } + prev = entry; + entry = entry->next(); + } + if (entry != NULL) { + remove(prev, h, entry); + } + return entry; + } + + // iterate over all entries in the hashmap + void entry_iterate(JvmtiTagHashmapEntryClosure* closure); +}; + +// possible hashmap sizes - odd primes that roughly double in size. +// To avoid excessive resizing the odd primes from 4801-76831 and +// 76831-307261 have been removed. The list must be terminated by -1. +int JvmtiTagHashmap::_sizes[] = { 4801, 76831, 307261, 614563, 1228891, + 2457733, 4915219, 9830479, 19660831, 39321619, 78643219, -1 }; + + +// A supporting class for iterating over all entries in Hashmap +class JvmtiTagHashmapEntryClosure { + public: + virtual void do_entry(JvmtiTagHashmapEntry* entry) = 0; +}; + + +// iterate over all entries in the hashmap +void JvmtiTagHashmap::entry_iterate(JvmtiTagHashmapEntryClosure* closure) { + for (int i=0; i<_size; i++) { + JvmtiTagHashmapEntry* entry = _table[i]; + JvmtiTagHashmapEntry* prev = NULL; + while (entry != NULL) { + // obtain the next entry before invoking do_entry - this is + // necessary because do_entry may remove the entry from the + // hashmap. + JvmtiTagHashmapEntry* next = entry->next(); + closure->do_entry(entry); + entry = next; + } + } +} + +// debugging +void JvmtiTagHashmap::print_memory_usage() { + intptr_t p = (intptr_t)this; + tty->print("[JvmtiTagHashmap @ " INTPTR_FORMAT, p); + + // table + entries in KB + int hashmap_usage = (size()*sizeof(JvmtiTagHashmapEntry*) + + entry_count()*sizeof(JvmtiTagHashmapEntry))/K; + + int weak_globals_usage = (int)(JNIHandles::weak_global_handle_memory_usage()/K); + tty->print_cr(", %d entries (%d KB) ]", + entry_count(), hashmap_usage, weak_globals_usage); +} + +// compute threshold for the next trace message +void JvmtiTagHashmap::compute_next_trace_threshold() { + if (trace_threshold() < medium_trace_threshold) { + _trace_threshold += small_trace_threshold; + } else { + if (trace_threshold() < large_trace_threshold) { + _trace_threshold += medium_trace_threshold; + } else { + _trace_threshold += large_trace_threshold; + } + } +} + +// memory region for young generation +MemRegion JvmtiTagMap::_young_gen; + +// get the memory region used for the young generation +void JvmtiTagMap::get_young_generation() { + if (Universe::heap()->kind() == CollectedHeap::GenCollectedHeap) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + _young_gen = gch->get_gen(0)->reserved(); + } else { +#ifndef SERIALGC + ParallelScavengeHeap* psh = ParallelScavengeHeap::heap(); + _young_gen= psh->young_gen()->reserved(); +#else // SERIALGC + fatal("SerialGC only supported in this configuration."); +#endif // SERIALGC + } +} + +// returns true if oop is in the young generation +inline bool JvmtiTagMap::is_in_young(oop o) { + assert(_young_gen.start() != NULL, "checking"); + void* p = (void*)o; + bool in_young = _young_gen.contains(p); + return in_young; +} + +// returns the appropriate hashmap for a given object +inline JvmtiTagHashmap* JvmtiTagMap::hashmap_for(oop o) { + if (is_in_young(o)) { + return _hashmap[0]; + } else { + return _hashmap[1]; + } +} + + +// create a JvmtiTagMap +JvmtiTagMap::JvmtiTagMap(JvmtiEnv* env) : + _env(env), + _lock(Mutex::nonleaf+2, "JvmtiTagMap._lock", false), + _free_entries(NULL), + _free_entries_count(0) +{ + assert(JvmtiThreadState_lock->is_locked(), "sanity check"); + assert(((JvmtiEnvBase *)env)->tag_map() == NULL, "tag map already exists for environment"); + + // create the hashmaps + for (int i=0; iset_tag_map(this); +} + + +// destroy a JvmtiTagMap +JvmtiTagMap::~JvmtiTagMap() { + + // no lock acquired as we assume the enclosing environment is + // also being destroryed. + ((JvmtiEnvBase *)_env)->set_tag_map(NULL); + + // iterate over the hashmaps and destroy each of the entries + for (int i=0; itable(); + for (int j=0; jsize(); j++) { + JvmtiTagHashmapEntry *entry = table[j]; + while (entry != NULL) { + JvmtiTagHashmapEntry* next = entry->next(); + jweak ref = entry->object(); + JNIHandles::destroy_weak_global(ref); + delete entry; + entry = next; + } + } + + // finally destroy the hashmap + delete hashmap; + } + + // remove any entries on the free list + JvmtiTagHashmapEntry* entry = _free_entries; + while (entry != NULL) { + JvmtiTagHashmapEntry* next = entry->next(); + delete entry; + entry = next; + } +} + +// create a hashmap entry +// - if there's an entry on the (per-environment) free list then this +// is returned. Otherwise an new entry is allocated. +JvmtiTagHashmapEntry* JvmtiTagMap::create_entry(jweak ref, jlong tag) { + assert(Thread::current()->is_VM_thread() || is_locked(), "checking"); + JvmtiTagHashmapEntry* entry; + if (_free_entries == NULL) { + entry = new JvmtiTagHashmapEntry(ref, tag); + } else { + assert(_free_entries_count > 0, "mismatched _free_entries_count"); + _free_entries_count--; + entry = _free_entries; + _free_entries = entry->next(); + entry->init(ref, tag); + } + return entry; +} + +// destroy an entry by returning it to the free list +void JvmtiTagMap::destroy_entry(JvmtiTagHashmapEntry* entry) { + assert(SafepointSynchronize::is_at_safepoint() || is_locked(), "checking"); + // limit the size of the free list + if (_free_entries_count >= max_free_entries) { + delete entry; + } else { + entry->set_next(_free_entries); + _free_entries = entry; + _free_entries_count++; + } +} + +// returns the tag map for the given environments. If the tag map +// doesn't exist then it is created. +JvmtiTagMap* JvmtiTagMap::tag_map_for(JvmtiEnv* env) { + JvmtiTagMap* tag_map = ((JvmtiEnvBase *)env)->tag_map(); + if (tag_map == NULL) { + MutexLocker mu(JvmtiThreadState_lock); + tag_map = ((JvmtiEnvBase *)env)->tag_map(); + if (tag_map == NULL) { + tag_map = new JvmtiTagMap(env); + } + } else { + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } + return tag_map; +} + +// iterate over all entries in the tag map. +void JvmtiTagMap::entry_iterate(JvmtiTagHashmapEntryClosure* closure) { + for (int i=0; ientry_iterate(closure); + } +} + +// returns true if the hashmaps are empty +bool JvmtiTagMap::is_empty() { + assert(SafepointSynchronize::is_at_safepoint() || is_locked(), "checking"); + assert(n_hashmaps == 2, "not implemented"); + return ((_hashmap[0]->entry_count() == 0) && (_hashmap[1]->entry_count() == 0)); +} + + +// Return the tag value for an object, or 0 if the object is +// not tagged +// +static inline jlong tag_for(JvmtiTagMap* tag_map, oop o) { + JvmtiTagHashmapEntry* entry = tag_map->hashmap_for(o)->find(o); + if (entry == NULL) { + return 0; + } else { + return entry->tag(); + } +} + +// If the object is a java.lang.Class then return the klassOop, +// otherwise return the original object +static inline oop klassOop_if_java_lang_Class(oop o) { + if (o->klass() == SystemDictionary::class_klass()) { + if (!java_lang_Class::is_primitive(o)) { + o = (oop)java_lang_Class::as_klassOop(o); + assert(o != NULL, "class for non-primitive mirror must exist"); + } + } + return o; +} + +// A CallbackWrapper is a support class for querying and tagging an object +// around a callback to a profiler. The constructor does pre-callback +// work to get the tag value, klass tag value, ... and the destructor +// does the post-callback work of tagging or untagging the object. +// +// { +// CallbackWrapper wrapper(tag_map, o); +// +// (*callback)(wrapper.klass_tag(), wrapper.obj_size(), wrapper.obj_tag_p(), ...) +// +// } // wrapper goes out of scope here which results in the destructor +// checking to see if the object has been tagged, untagged, or the +// tag value has changed. +// +class CallbackWrapper : public StackObj { + private: + JvmtiTagMap* _tag_map; + JvmtiTagHashmap* _hashmap; + JvmtiTagHashmapEntry* _entry; + oop _o; + jlong _obj_size; + jlong _obj_tag; + klassOop _klass; // the object's class + jlong _klass_tag; + + protected: + JvmtiTagMap* tag_map() const { return _tag_map; } + + // invoked post-callback to tag, untag, or update the tag of an object + void inline post_callback_tag_update(oop o, JvmtiTagHashmap* hashmap, + JvmtiTagHashmapEntry* entry, jlong obj_tag); + public: + CallbackWrapper(JvmtiTagMap* tag_map, oop o) { + assert(Thread::current()->is_VM_thread() || tag_map->is_locked(), + "MT unsafe or must be VM thread"); + + // for Classes the klassOop is tagged + _o = klassOop_if_java_lang_Class(o); + + // object size + _obj_size = _o->size() * wordSize; + + // record the context + _tag_map = tag_map; + _hashmap = tag_map->hashmap_for(_o); + _entry = _hashmap->find(_o); + + // get object tag + _obj_tag = (_entry == NULL) ? 0 : _entry->tag(); + + // get the class and the class's tag value + if (_o == o) { + _klass = _o->klass(); + } else { + // if the object represents a runtime class then use the + // tag for java.lang.Class + _klass = SystemDictionary::class_klass(); + } + _klass_tag = tag_for(tag_map, _klass); + } + + ~CallbackWrapper() { + post_callback_tag_update(_o, _hashmap, _entry, _obj_tag); + } + + inline jlong* obj_tag_p() { return &_obj_tag; } + inline jlong obj_size() const { return _obj_size; } + inline jlong obj_tag() const { return _obj_tag; } + inline klassOop klass() const { return _klass; } + inline jlong klass_tag() const { return _klass_tag; } +}; + + + +// callback post-callback to tag, untag, or update the tag of an object +void inline CallbackWrapper::post_callback_tag_update(oop o, + JvmtiTagHashmap* hashmap, + JvmtiTagHashmapEntry* entry, + jlong obj_tag) { + if (entry == NULL) { + if (obj_tag != 0) { + // callback has tagged the object + assert(Thread::current()->is_VM_thread(), "must be VMThread"); + HandleMark hm; + Handle h(o); + jweak ref = JNIHandles::make_weak_global(h); + entry = tag_map()->create_entry(ref, obj_tag); + hashmap->add(o, entry); + } + } else { + // object was previously tagged - the callback may have untagged + // the object or changed the tag value + if (obj_tag == 0) { + jweak ref = entry->object(); + + JvmtiTagHashmapEntry* entry_removed = hashmap->remove(o); + assert(entry_removed == entry, "checking"); + tag_map()->destroy_entry(entry); + + JNIHandles::destroy_weak_global(ref); + } else { + if (obj_tag != entry->tag()) { + entry->set_tag(obj_tag); + } + } + } +} + +// An extended CallbackWrapper used when reporting an object reference +// to the agent. +// +// { +// TwoOopCallbackWrapper wrapper(tag_map, referrer, o); +// +// (*callback)(wrapper.klass_tag(), +// wrapper.obj_size(), +// wrapper.obj_tag_p() +// wrapper.referrer_tag_p(), ...) +// +// } // wrapper goes out of scope here which results in the destructor +// checking to see if the referrer object has been tagged, untagged, +// or the tag value has changed. +// +class TwoOopCallbackWrapper : public CallbackWrapper { + private: + bool _is_reference_to_self; + JvmtiTagHashmap* _referrer_hashmap; + JvmtiTagHashmapEntry* _referrer_entry; + oop _referrer; + jlong _referrer_obj_tag; + jlong _referrer_klass_tag; + jlong* _referrer_tag_p; + + bool is_reference_to_self() const { return _is_reference_to_self; } + + public: + TwoOopCallbackWrapper(JvmtiTagMap* tag_map, oop referrer, oop o) : + CallbackWrapper(tag_map, o) + { + // self reference needs to be handled in a special way + _is_reference_to_self = (referrer == o); + + if (_is_reference_to_self) { + _referrer_klass_tag = klass_tag(); + _referrer_tag_p = obj_tag_p(); + } else { + // for Classes the klassOop is tagged + _referrer = klassOop_if_java_lang_Class(referrer); + // record the context + _referrer_hashmap = tag_map->hashmap_for(_referrer); + _referrer_entry = _referrer_hashmap->find(_referrer); + + // get object tag + _referrer_obj_tag = (_referrer_entry == NULL) ? 0 : _referrer_entry->tag(); + _referrer_tag_p = &_referrer_obj_tag; + + // get referrer class tag. + klassOop k = (_referrer == referrer) ? // Check if referrer is a class... + _referrer->klass() // No, just get its class + : SystemDictionary::class_klass(); // Yes, its class is Class + _referrer_klass_tag = tag_for(tag_map, k); + } + } + + ~TwoOopCallbackWrapper() { + if (!is_reference_to_self()){ + post_callback_tag_update(_referrer, + _referrer_hashmap, + _referrer_entry, + _referrer_obj_tag); + } + } + + // address of referrer tag + // (for a self reference this will return the same thing as obj_tag_p()) + inline jlong* referrer_tag_p() { return _referrer_tag_p; } + + // referrer's class tag + inline jlong referrer_klass_tag() { return _referrer_klass_tag; } +}; + +// tag an object +// +// This function is performance critical. If many threads attempt to tag objects +// around the same time then it's possible that the Mutex associated with the +// tag map will be a hot lock. Eliminating this lock will not eliminate the issue +// because creating a JNI weak reference requires acquiring a global lock also. +void JvmtiTagMap::set_tag(jobject object, jlong tag) { + MutexLocker ml(lock()); + + // resolve the object + oop o = JNIHandles::resolve_non_null(object); + + // for Classes we tag the klassOop + o = klassOop_if_java_lang_Class(o); + + // see if the object is already tagged + JvmtiTagHashmap* hashmap = hashmap_for(o); + JvmtiTagHashmapEntry* entry = hashmap->find(o); + + // if the object is not already tagged then we tag it + if (entry == NULL) { + if (tag != 0) { + HandleMark hm; + Handle h(o); + jweak ref = JNIHandles::make_weak_global(h); + + // the object may have moved because make_weak_global may + // have blocked - thus it is necessary resolve the handle + // and re-hash the object. + o = h(); + entry = create_entry(ref, tag); + hashmap_for(o)->add(o, entry); + } else { + // no-op + } + } else { + // if the object is already tagged then we either update + // the tag (if a new tag value has been provided) + // or remove the object if the new tag value is 0. + // Removing the object requires that we also delete the JNI + // weak ref to the object. + if (tag == 0) { + jweak ref = entry->object(); + hashmap->remove(o); + destroy_entry(entry); + JNIHandles::destroy_weak_global(ref); + } else { + entry->set_tag(tag); + } + } +} + +// get the tag for an object +jlong JvmtiTagMap::get_tag(jobject object) { + MutexLocker ml(lock()); + + // resolve the object + oop o = JNIHandles::resolve_non_null(object); + + // for Classes get the tag from the klassOop + return tag_for(this, klassOop_if_java_lang_Class(o)); +} + + +// Helper class used to describe the static or instance fields of a class. +// For each field it holds the field index (as defined by the JVMTI specification), +// the field type, and the offset. + +class ClassFieldDescriptor: public CHeapObj { + private: + int _field_index; + int _field_offset; + char _field_type; + public: + ClassFieldDescriptor(int index, char type, int offset) : + _field_index(index), _field_type(type), _field_offset(offset) { + } + int field_index() const { return _field_index; } + char field_type() const { return _field_type; } + int field_offset() const { return _field_offset; } +}; + +class ClassFieldMap: public CHeapObj { + private: + enum { + initial_field_count = 5 + }; + + // list of field descriptors + GrowableArray* _fields; + + // constructor + ClassFieldMap(); + + // add a field + void add(int index, char type, int offset); + + // returns the field count for the given class + static int compute_field_count(instanceKlassHandle ikh); + + public: + ~ClassFieldMap(); + + // access + int field_count() { return _fields->length(); } + ClassFieldDescriptor* field_at(int i) { return _fields->at(i); } + + // functions to create maps of static or instance fields + static ClassFieldMap* create_map_of_static_fields(klassOop k); + static ClassFieldMap* create_map_of_instance_fields(oop obj); +}; + +ClassFieldMap::ClassFieldMap() { + _fields = new (ResourceObj::C_HEAP) GrowableArray(initial_field_count, true); +} + +ClassFieldMap::~ClassFieldMap() { + for (int i=0; i<_fields->length(); i++) { + delete _fields->at(i); + } + delete _fields; +} + +void ClassFieldMap::add(int index, char type, int offset) { + ClassFieldDescriptor* field = new ClassFieldDescriptor(index, type, offset); + _fields->append(field); +} + +// Returns a heap allocated ClassFieldMap to describe the static fields +// of the given class. +// +ClassFieldMap* ClassFieldMap::create_map_of_static_fields(klassOop k) { + HandleMark hm; + instanceKlassHandle ikh = instanceKlassHandle(Thread::current(), k); + + // create the field map + ClassFieldMap* field_map = new ClassFieldMap(); + + FilteredFieldStream f(ikh, false, false); + int max_field_index = f.field_count()-1; + + int index = 0; + for (FilteredFieldStream fld(ikh, true, true); !fld.eos(); fld.next(), index++) { + // ignore instance fields + if (!fld.access_flags().is_static()) { + continue; + } + field_map->add(max_field_index - index, fld.signature()->byte_at(0), fld.offset()); + } + return field_map; +} + +// Returns a heap allocated ClassFieldMap to describe the instance fields +// of the given class. All instance fields are included (this means public +// and private fields declared in superclasses and superinterfaces too). +// +ClassFieldMap* ClassFieldMap::create_map_of_instance_fields(oop obj) { + HandleMark hm; + instanceKlassHandle ikh = instanceKlassHandle(Thread::current(), obj->klass()); + + // create the field map + ClassFieldMap* field_map = new ClassFieldMap(); + + FilteredFieldStream f(ikh, false, false); + + int max_field_index = f.field_count()-1; + + int index = 0; + for (FilteredFieldStream fld(ikh, false, false); !fld.eos(); fld.next(), index++) { + // ignore static fields + if (fld.access_flags().is_static()) { + continue; + } + field_map->add(max_field_index - index, fld.signature()->byte_at(0), fld.offset()); + } + + return field_map; +} + +// Helper class used to cache a ClassFileMap for the instance fields of +// a cache. A JvmtiCachedClassFieldMap can be cached by an instanceKlass during +// heap iteration and avoid creating a field map for each object in the heap +// (only need to create the map when the first instance of a class is encountered). +// +class JvmtiCachedClassFieldMap : public CHeapObj { + private: + enum { + initial_class_count = 200 + }; + ClassFieldMap* _field_map; + + ClassFieldMap* field_map() const { return _field_map; } + + JvmtiCachedClassFieldMap(ClassFieldMap* field_map); + ~JvmtiCachedClassFieldMap(); + + static GrowableArray* _class_list; + static void add_to_class_list(instanceKlass* ik); + + public: + // returns the field map for a given object (returning map cached + // by instanceKlass if possible + static ClassFieldMap* get_map_of_instance_fields(oop obj); + + // removes the field map from all instanceKlasses - should be + // called before VM operation completes + static void clear_cache(); + + // returns the number of ClassFieldMap cached by instanceKlasses + static int cached_field_map_count(); +}; + +GrowableArray* JvmtiCachedClassFieldMap::_class_list; + +JvmtiCachedClassFieldMap::JvmtiCachedClassFieldMap(ClassFieldMap* field_map) { + _field_map = field_map; +} + +JvmtiCachedClassFieldMap::~JvmtiCachedClassFieldMap() { + if (_field_map != NULL) { + delete _field_map; + } +} + +// Marker class to ensure that the class file map cache is only used in a defined +// scope. +class ClassFieldMapCacheMark : public StackObj { + private: + static bool _is_active; + public: + ClassFieldMapCacheMark() { + assert(Thread::current()->is_VM_thread(), "must be VMThread"); + assert(JvmtiCachedClassFieldMap::cached_field_map_count() == 0, "cache not empty"); + assert(!_is_active, "ClassFieldMapCacheMark cannot be nested"); + _is_active = true; + } + ~ClassFieldMapCacheMark() { + JvmtiCachedClassFieldMap::clear_cache(); + _is_active = false; + } + static bool is_active() { return _is_active; } +}; + +bool ClassFieldMapCacheMark::_is_active; + + +// record that the given instanceKlass is caching a field map +void JvmtiCachedClassFieldMap::add_to_class_list(instanceKlass* ik) { + if (_class_list == NULL) { + _class_list = new (ResourceObj::C_HEAP) GrowableArray(initial_class_count, true); + } + _class_list->push(ik); +} + +// returns the instance field map for the given object +// (returns field map cached by the instanceKlass if possible) +ClassFieldMap* JvmtiCachedClassFieldMap::get_map_of_instance_fields(oop obj) { + assert(Thread::current()->is_VM_thread(), "must be VMThread"); + assert(ClassFieldMapCacheMark::is_active(), "ClassFieldMapCacheMark not active"); + + klassOop k = obj->klass(); + instanceKlass* ik = instanceKlass::cast(k); + + // return cached map if possible + JvmtiCachedClassFieldMap* cached_map = ik->jvmti_cached_class_field_map(); + if (cached_map != NULL) { + assert(cached_map->field_map() != NULL, "missing field list"); + return cached_map->field_map(); + } else { + ClassFieldMap* field_map = ClassFieldMap::create_map_of_instance_fields(obj); + cached_map = new JvmtiCachedClassFieldMap(field_map); + ik->set_jvmti_cached_class_field_map(cached_map); + add_to_class_list(ik); + return field_map; + } +} + +// remove the fields maps cached from all instanceKlasses +void JvmtiCachedClassFieldMap::clear_cache() { + assert(Thread::current()->is_VM_thread(), "must be VMThread"); + if (_class_list != NULL) { + for (int i = 0; i < _class_list->length(); i++) { + instanceKlass* ik = _class_list->at(i); + JvmtiCachedClassFieldMap* cached_map = ik->jvmti_cached_class_field_map(); + assert(cached_map != NULL, "should not be NULL"); + ik->set_jvmti_cached_class_field_map(NULL); + delete cached_map; // deletes the encapsulated field map + } + delete _class_list; + _class_list = NULL; + } +} + +// returns the number of ClassFieldMap cached by instanceKlasses +int JvmtiCachedClassFieldMap::cached_field_map_count() { + return (_class_list == NULL) ? 0 : _class_list->length(); +} + +// helper function to indicate if an object is filtered by its tag or class tag +static inline bool is_filtered_by_heap_filter(jlong obj_tag, + jlong klass_tag, + int heap_filter) { + // apply the heap filter + if (obj_tag != 0) { + // filter out tagged objects + if (heap_filter & JVMTI_HEAP_FILTER_TAGGED) return true; + } else { + // filter out untagged objects + if (heap_filter & JVMTI_HEAP_FILTER_UNTAGGED) return true; + } + if (klass_tag != 0) { + // filter out objects with tagged classes + if (heap_filter & JVMTI_HEAP_FILTER_CLASS_TAGGED) return true; + } else { + // filter out objects with untagged classes. + if (heap_filter & JVMTI_HEAP_FILTER_CLASS_UNTAGGED) return true; + } + return false; +} + +// helper function to indicate if an object is filtered by a klass filter +static inline bool is_filtered_by_klass_filter(oop obj, KlassHandle klass_filter) { + if (!klass_filter.is_null()) { + if (obj->klass() != klass_filter()) { + return true; + } + } + return false; +} + +// helper function to tell if a field is a primitive field or not +static inline bool is_primitive_field_type(char type) { + return (type != 'L' && type != '['); +} + +// helper function to copy the value from location addr to jvalue. +static inline void copy_to_jvalue(jvalue *v, address addr, jvmtiPrimitiveType value_type) { + switch (value_type) { + case JVMTI_PRIMITIVE_TYPE_BOOLEAN : { v->z = *(jboolean*)addr; break; } + case JVMTI_PRIMITIVE_TYPE_BYTE : { v->b = *(jbyte*)addr; break; } + case JVMTI_PRIMITIVE_TYPE_CHAR : { v->c = *(jchar*)addr; break; } + case JVMTI_PRIMITIVE_TYPE_SHORT : { v->s = *(jshort*)addr; break; } + case JVMTI_PRIMITIVE_TYPE_INT : { v->i = *(jint*)addr; break; } + case JVMTI_PRIMITIVE_TYPE_LONG : { v->j = *(jlong*)addr; break; } + case JVMTI_PRIMITIVE_TYPE_FLOAT : { v->f = *(jfloat*)addr; break; } + case JVMTI_PRIMITIVE_TYPE_DOUBLE : { v->d = *(jdouble*)addr; break; } + default: ShouldNotReachHere(); + } +} + +// helper function to invoke string primitive value callback +// returns visit control flags +static jint invoke_string_value_callback(jvmtiStringPrimitiveValueCallback cb, + CallbackWrapper* wrapper, + oop str, + void* user_data) +{ + assert(str->klass() == SystemDictionary::string_klass(), "not a string"); + + // get the string value and length + // (string value may be offset from the base) + int s_len = java_lang_String::length(str); + typeArrayOop s_value = java_lang_String::value(str); + int s_offset = java_lang_String::offset(str); + jchar* value; + if (s_len > 0) { + value = s_value->char_at_addr(s_offset); + } else { + value = (jchar*) s_value->base(T_CHAR); + } + + // invoke the callback + return (*cb)(wrapper->klass_tag(), + wrapper->obj_size(), + wrapper->obj_tag_p(), + value, + (jint)s_len, + user_data); +} + +// helper function to invoke string primitive value callback +// returns visit control flags +static jint invoke_array_primitive_value_callback(jvmtiArrayPrimitiveValueCallback cb, + CallbackWrapper* wrapper, + oop obj, + void* user_data) +{ + assert(obj->is_typeArray(), "not a primitive array"); + + // get base address of first element + typeArrayOop array = typeArrayOop(obj); + BasicType type = typeArrayKlass::cast(array->klass())->element_type(); + void* elements = array->base(type); + + // jvmtiPrimitiveType is defined so this mapping is always correct + jvmtiPrimitiveType elem_type = (jvmtiPrimitiveType)type2char(type); + + return (*cb)(wrapper->klass_tag(), + wrapper->obj_size(), + wrapper->obj_tag_p(), + (jint)array->length(), + elem_type, + elements, + user_data); +} + +// helper function to invoke the primitive field callback for all static fields +// of a given class +static jint invoke_primitive_field_callback_for_static_fields + (CallbackWrapper* wrapper, + oop obj, + jvmtiPrimitiveFieldCallback cb, + void* user_data) +{ + // for static fields only the index will be set + static jvmtiHeapReferenceInfo reference_info = { 0 }; + + assert(obj->klass() == SystemDictionary::class_klass(), "not a class"); + if (java_lang_Class::is_primitive(obj)) { + return 0; + } + klassOop k = java_lang_Class::as_klassOop(obj); + Klass* klass = k->klass_part(); + + // ignore classes for object and type arrays + if (!klass->oop_is_instance()) { + return 0; + } + + // ignore classes which aren't linked yet + instanceKlass* ik = instanceKlass::cast(k); + if (!ik->is_linked()) { + return 0; + } + + // get the field map + ClassFieldMap* field_map = ClassFieldMap::create_map_of_static_fields(k); + + // invoke the callback for each static primitive field + for (int i=0; ifield_count(); i++) { + ClassFieldDescriptor* field = field_map->field_at(i); + + // ignore non-primitive fields + char type = field->field_type(); + if (!is_primitive_field_type(type)) { + continue; + } + // one-to-one mapping + jvmtiPrimitiveType value_type = (jvmtiPrimitiveType)type; + + // get offset and field value + int offset = field->field_offset(); + address addr = (address)k + offset; + jvalue value; + copy_to_jvalue(&value, addr, value_type); + + // field index + reference_info.field.index = field->field_index(); + + // invoke the callback + jint res = (*cb)(JVMTI_HEAP_REFERENCE_STATIC_FIELD, + &reference_info, + wrapper->klass_tag(), + wrapper->obj_tag_p(), + value, + value_type, + user_data); + if (res & JVMTI_VISIT_ABORT) { + delete field_map; + return res; + } + } + + delete field_map; + return 0; +} + +// helper function to invoke the primitive field callback for all instance fields +// of a given object +static jint invoke_primitive_field_callback_for_instance_fields( + CallbackWrapper* wrapper, + oop obj, + jvmtiPrimitiveFieldCallback cb, + void* user_data) +{ + // for instance fields only the index will be set + static jvmtiHeapReferenceInfo reference_info = { 0 }; + + // get the map of the instance fields + ClassFieldMap* fields = JvmtiCachedClassFieldMap::get_map_of_instance_fields(obj); + + // invoke the callback for each instance primitive field + for (int i=0; ifield_count(); i++) { + ClassFieldDescriptor* field = fields->field_at(i); + + // ignore non-primitive fields + char type = field->field_type(); + if (!is_primitive_field_type(type)) { + continue; + } + // one-to-one mapping + jvmtiPrimitiveType value_type = (jvmtiPrimitiveType)type; + + // get offset and field value + int offset = field->field_offset(); + address addr = (address)obj + offset; + jvalue value; + copy_to_jvalue(&value, addr, value_type); + + // field index + reference_info.field.index = field->field_index(); + + // invoke the callback + jint res = (*cb)(JVMTI_HEAP_REFERENCE_FIELD, + &reference_info, + wrapper->klass_tag(), + wrapper->obj_tag_p(), + value, + value_type, + user_data); + if (res & JVMTI_VISIT_ABORT) { + return res; + } + } + return 0; +} + + +// VM operation to iterate over all objects in the heap (both reachable +// and unreachable) +class VM_HeapIterateOperation: public VM_Operation { + private: + ObjectClosure* _blk; + public: + VM_HeapIterateOperation(ObjectClosure* blk) { _blk = blk; } + + VMOp_Type type() const { return VMOp_HeapIterateOperation; } + void doit() { + // allows class files maps to be cached during iteration + ClassFieldMapCacheMark cm; + + // make sure that heap is parsable (fills TLABs with filler objects) + Universe::heap()->ensure_parsability(false); // no need to retire TLABs + + // Verify heap before iteration - if the heap gets corrupted then + // JVMTI's IterateOverHeap will crash. + if (VerifyBeforeIteration) { + Universe::verify(); + } + + // do the iteration + Universe::heap()->object_iterate(_blk); + + // when sharing is enabled we must iterate over the shared spaces + if (UseSharedSpaces) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + CompactingPermGenGen* gen = (CompactingPermGenGen*)gch->perm_gen(); + gen->ro_space()->object_iterate(_blk); + gen->rw_space()->object_iterate(_blk); + } + } + +}; + + +// An ObjectClosure used to support the deprecated IterateOverHeap and +// IterateOverInstancesOfClass functions +class IterateOverHeapObjectClosure: public ObjectClosure { + private: + JvmtiTagMap* _tag_map; + KlassHandle _klass; + jvmtiHeapObjectFilter _object_filter; + jvmtiHeapObjectCallback _heap_object_callback; + const void* _user_data; + + // accessors + JvmtiTagMap* tag_map() const { return _tag_map; } + jvmtiHeapObjectFilter object_filter() const { return _object_filter; } + jvmtiHeapObjectCallback object_callback() const { return _heap_object_callback; } + KlassHandle klass() const { return _klass; } + const void* user_data() const { return _user_data; } + + // indicates if iteration has been aborted + bool _iteration_aborted; + bool is_iteration_aborted() const { return _iteration_aborted; } + void set_iteration_aborted(bool aborted) { _iteration_aborted = aborted; } + + public: + IterateOverHeapObjectClosure(JvmtiTagMap* tag_map, + KlassHandle klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) : + _tag_map(tag_map), + _klass(klass), + _object_filter(object_filter), + _heap_object_callback(heap_object_callback), + _user_data(user_data), + _iteration_aborted(false) + { + } + + void do_object(oop o); +}; + +// invoked for each object in the heap +void IterateOverHeapObjectClosure::do_object(oop o) { + // check if iteration has been halted + if (is_iteration_aborted()) return; + + // ignore any objects that aren't visible to profiler + if (!ServiceUtil::visible_oop(o)) return; + + // instanceof check when filtering by klass + if (!klass().is_null() && !o->is_a(klass()())) { + return; + } + // prepare for the calllback + CallbackWrapper wrapper(tag_map(), o); + + // if the object is tagged and we're only interested in untagged objects + // then don't invoke the callback. Similiarly, if the object is untagged + // and we're only interested in tagged objects we skip the callback. + if (wrapper.obj_tag() != 0) { + if (object_filter() == JVMTI_HEAP_OBJECT_UNTAGGED) return; + } else { + if (object_filter() == JVMTI_HEAP_OBJECT_TAGGED) return; + } + + // invoke the agent's callback + jvmtiIterationControl control = (*object_callback())(wrapper.klass_tag(), + wrapper.obj_size(), + wrapper.obj_tag_p(), + (void*)user_data()); + if (control == JVMTI_ITERATION_ABORT) { + set_iteration_aborted(true); + } +} + +// An ObjectClosure used to support the IterateThroughHeap function +class IterateThroughHeapObjectClosure: public ObjectClosure { + private: + JvmtiTagMap* _tag_map; + KlassHandle _klass; + int _heap_filter; + const jvmtiHeapCallbacks* _callbacks; + const void* _user_data; + + // accessor functions + JvmtiTagMap* tag_map() const { return _tag_map; } + int heap_filter() const { return _heap_filter; } + const jvmtiHeapCallbacks* callbacks() const { return _callbacks; } + KlassHandle klass() const { return _klass; } + const void* user_data() const { return _user_data; } + + // indicates if the iteration has been aborted + bool _iteration_aborted; + bool is_iteration_aborted() const { return _iteration_aborted; } + + // used to check the visit control flags. If the abort flag is set + // then we set the iteration aborted flag so that the iteration completes + // without processing any further objects + bool check_flags_for_abort(jint flags) { + bool is_abort = (flags & JVMTI_VISIT_ABORT) != 0; + if (is_abort) { + _iteration_aborted = true; + } + return is_abort; + } + + public: + IterateThroughHeapObjectClosure(JvmtiTagMap* tag_map, + KlassHandle klass, + int heap_filter, + const jvmtiHeapCallbacks* heap_callbacks, + const void* user_data) : + _tag_map(tag_map), + _klass(klass), + _heap_filter(heap_filter), + _callbacks(heap_callbacks), + _user_data(user_data), + _iteration_aborted(false) + { + } + + void do_object(oop o); +}; + +// invoked for each object in the heap +void IterateThroughHeapObjectClosure::do_object(oop obj) { + // check if iteration has been halted + if (is_iteration_aborted()) return; + + // ignore any objects that aren't visible to profiler + if (!ServiceUtil::visible_oop(obj)) return; + + // apply class filter + if (is_filtered_by_klass_filter(obj, klass())) return; + + // prepare for callback + CallbackWrapper wrapper(tag_map(), obj); + + // check if filtered by the heap filter + if (is_filtered_by_heap_filter(wrapper.obj_tag(), wrapper.klass_tag(), heap_filter())) { + return; + } + + // for arrays we need the length, otherwise -1 + bool is_array = obj->is_array(); + int len = is_array ? arrayOop(obj)->length() : -1; + + // invoke the object callback (if callback is provided) + if (callbacks()->heap_iteration_callback != NULL) { + jvmtiHeapIterationCallback cb = callbacks()->heap_iteration_callback; + jint res = (*cb)(wrapper.klass_tag(), + wrapper.obj_size(), + wrapper.obj_tag_p(), + (jint)len, + (void*)user_data()); + if (check_flags_for_abort(res)) return; + } + + // for objects and classes we report primitive fields if callback provided + if (callbacks()->primitive_field_callback != NULL && obj->is_instance()) { + jint res; + jvmtiPrimitiveFieldCallback cb = callbacks()->primitive_field_callback; + if (obj->klass() == SystemDictionary::class_klass()) { + res = invoke_primitive_field_callback_for_static_fields(&wrapper, + obj, + cb, + (void*)user_data()); + } else { + res = invoke_primitive_field_callback_for_instance_fields(&wrapper, + obj, + cb, + (void*)user_data()); + } + if (check_flags_for_abort(res)) return; + } + + // string callback + if (!is_array && + callbacks()->string_primitive_value_callback != NULL && + obj->klass() == SystemDictionary::string_klass()) { + jint res = invoke_string_value_callback( + callbacks()->string_primitive_value_callback, + &wrapper, + obj, + (void*)user_data() ); + if (check_flags_for_abort(res)) return; + } + + // array callback + if (is_array && + callbacks()->array_primitive_value_callback != NULL && + obj->is_typeArray()) { + jint res = invoke_array_primitive_value_callback( + callbacks()->array_primitive_value_callback, + &wrapper, + obj, + (void*)user_data() ); + if (check_flags_for_abort(res)) return; + } +}; + + +// Deprecated function to iterate over all objects in the heap +void JvmtiTagMap::iterate_over_heap(jvmtiHeapObjectFilter object_filter, + KlassHandle klass, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) +{ + MutexLocker ml(Heap_lock); + IterateOverHeapObjectClosure blk(this, + klass, + object_filter, + heap_object_callback, + user_data); + VM_HeapIterateOperation op(&blk); + VMThread::execute(&op); +} + + +// Iterates over all objects in the heap +void JvmtiTagMap::iterate_through_heap(jint heap_filter, + KlassHandle klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) +{ + MutexLocker ml(Heap_lock); + IterateThroughHeapObjectClosure blk(this, + klass, + heap_filter, + callbacks, + user_data); + VM_HeapIterateOperation op(&blk); + VMThread::execute(&op); +} + +// support class for get_objects_with_tags + +class TagObjectCollector : public JvmtiTagHashmapEntryClosure { + private: + JvmtiEnv* _env; + jlong* _tags; + jint _tag_count; + + GrowableArray* _object_results; // collected objects (JNI weak refs) + GrowableArray* _tag_results; // collected tags + + public: + TagObjectCollector(JvmtiEnv* env, const jlong* tags, jint tag_count) { + _env = env; + _tags = (jlong*)tags; + _tag_count = tag_count; + _object_results = new (ResourceObj::C_HEAP) GrowableArray(1,true); + _tag_results = new (ResourceObj::C_HEAP) GrowableArray(1,true); + } + + ~TagObjectCollector() { + delete _object_results; + delete _tag_results; + } + + // for each tagged object check if the tag value matches + // - if it matches then we create a JNI local reference to the object + // and record the reference and tag value. + // + void do_entry(JvmtiTagHashmapEntry* entry) { + for (int i=0; i<_tag_count; i++) { + if (_tags[i] == entry->tag()) { + oop o = JNIHandles::resolve(entry->object()); + assert(o != NULL && o != JNIHandles::deleted_handle(), "sanity check"); + + // the mirror is tagged + if (o->is_klass()) { + klassOop k = (klassOop)o; + o = Klass::cast(k)->java_mirror(); + } + + jobject ref = JNIHandles::make_local(JavaThread::current(), o); + _object_results->append(ref); + _tag_results->append((uint64_t)entry->tag()); + } + } + } + + // return the results from the collection + // + jvmtiError result(jint* count_ptr, jobject** object_result_ptr, jlong** tag_result_ptr) { + jvmtiError error; + int count = _object_results->length(); + assert(count >= 0, "sanity check"); + + // if object_result_ptr is not NULL then allocate the result and copy + // in the object references. + if (object_result_ptr != NULL) { + error = _env->Allocate(count * sizeof(jobject), (unsigned char**)object_result_ptr); + if (error != JVMTI_ERROR_NONE) { + return error; + } + for (int i=0; iat(i); + } + } + + // if tag_result_ptr is not NULL then allocate the result and copy + // in the tag values. + if (tag_result_ptr != NULL) { + error = _env->Allocate(count * sizeof(jlong), (unsigned char**)tag_result_ptr); + if (error != JVMTI_ERROR_NONE) { + if (object_result_ptr != NULL) { + _env->Deallocate((unsigned char*)object_result_ptr); + } + return error; + } + for (int i=0; iat(i); + } + } + + *count_ptr = count; + return JVMTI_ERROR_NONE; + } +}; + +// return the list of objects with the specified tags +jvmtiError JvmtiTagMap::get_objects_with_tags(const jlong* tags, + jint count, jint* count_ptr, jobject** object_result_ptr, jlong** tag_result_ptr) { + + TagObjectCollector collector(env(), tags, count); + { + // iterate over all tagged objects + MutexLocker ml(lock()); + entry_iterate(&collector); + } + return collector.result(count_ptr, object_result_ptr, tag_result_ptr); +} + + +// ObjectMarker is used to support the marking objects when walking the +// heap. +// +// This implementation uses the existing mark bits in an object for +// marking. Objects that are marked must later have their headers restored. +// As most objects are unlocked and don't have their identity hash computed +// we don't have to save their headers. Instead we save the headers that +// are "interesting". Later when the headers are restored this implementation +// restores all headers to their initial value and then restores the few +// objects that had interesting headers. +// +// Future work: This implementation currently uses growable arrays to save +// the oop and header of interesting objects. As an optimization we could +// use the same technique as the GC and make use of the unused area +// between top() and end(). +// + +// An ObjectClosure used to restore the mark bits of an object +class RestoreMarksClosure : public ObjectClosure { + public: + void do_object(oop o) { + if (o != NULL) { + markOop mark = o->mark(); + if (mark->is_marked()) { + o->init_mark(); + } + } + } +}; + +// ObjectMarker provides the mark and visited functions +class ObjectMarker : AllStatic { + private: + // saved headers + static GrowableArray* _saved_oop_stack; + static GrowableArray* _saved_mark_stack; + + public: + static void init(); // initialize + static void done(); // clean-up + + static inline void mark(oop o); // mark an object + static inline bool visited(oop o); // check if object has been visited +}; + +GrowableArray* ObjectMarker::_saved_oop_stack = NULL; +GrowableArray* ObjectMarker::_saved_mark_stack = NULL; + +// initialize ObjectMarker - prepares for object marking +void ObjectMarker::init() { + assert(Thread::current()->is_VM_thread(), "must be VMThread"); + + // prepare heap for iteration + Universe::heap()->ensure_parsability(false); // no need to retire TLABs + + // create stacks for interesting headers + _saved_mark_stack = new (ResourceObj::C_HEAP) GrowableArray(4000, true); + _saved_oop_stack = new (ResourceObj::C_HEAP) GrowableArray(4000, true); + + if (UseBiasedLocking) { + BiasedLocking::preserve_marks(); + } +} + +// Object marking is done so restore object headers +void ObjectMarker::done() { + // iterate over all objects and restore the mark bits to + // their initial value + RestoreMarksClosure blk; + Universe::heap()->object_iterate(&blk); + + // When sharing is enabled we need to restore the headers of the objects + // in the readwrite space too. + if (UseSharedSpaces) { + GenCollectedHeap* gch = GenCollectedHeap::heap(); + CompactingPermGenGen* gen = (CompactingPermGenGen*)gch->perm_gen(); + gen->rw_space()->object_iterate(&blk); + } + + // now restore the interesting headers + for (int i = 0; i < _saved_oop_stack->length(); i++) { + oop o = _saved_oop_stack->at(i); + markOop mark = _saved_mark_stack->at(i); + o->set_mark(mark); + } + + if (UseBiasedLocking) { + BiasedLocking::restore_marks(); + } + + // free the stacks + delete _saved_oop_stack; + delete _saved_mark_stack; +} + +// mark an object +inline void ObjectMarker::mark(oop o) { + assert(Universe::heap()->is_in(o), "sanity check"); + assert(!o->mark()->is_marked(), "should only mark an object once"); + + // object's mark word + markOop mark = o->mark(); + + if (mark->must_be_preserved(o)) { + _saved_mark_stack->push(mark); + _saved_oop_stack->push(o); + } + + // mark the object + o->set_mark(markOopDesc::prototype()->set_marked()); +} + +// return true if object is marked +inline bool ObjectMarker::visited(oop o) { + return o->mark()->is_marked(); +} + +// Stack allocated class to help ensure that ObjectMarker is used +// correctly. Constructor initializes ObjectMarker, destructor calls +// ObjectMarker's done() function to restore object headers. +class ObjectMarkerController : public StackObj { + public: + ObjectMarkerController() { + ObjectMarker::init(); + } + ~ObjectMarkerController() { + ObjectMarker::done(); + } +}; + + +// helper to map a jvmtiHeapReferenceKind to an old style jvmtiHeapRootKind +// (not performance critical as only used for roots) +static jvmtiHeapRootKind toJvmtiHeapRootKind(jvmtiHeapReferenceKind kind) { + switch (kind) { + case JVMTI_HEAP_REFERENCE_JNI_GLOBAL: return JVMTI_HEAP_ROOT_JNI_GLOBAL; + case JVMTI_HEAP_REFERENCE_SYSTEM_CLASS: return JVMTI_HEAP_ROOT_SYSTEM_CLASS; + case JVMTI_HEAP_REFERENCE_MONITOR: return JVMTI_HEAP_ROOT_MONITOR; + case JVMTI_HEAP_REFERENCE_STACK_LOCAL: return JVMTI_HEAP_ROOT_STACK_LOCAL; + case JVMTI_HEAP_REFERENCE_JNI_LOCAL: return JVMTI_HEAP_ROOT_JNI_LOCAL; + case JVMTI_HEAP_REFERENCE_THREAD: return JVMTI_HEAP_ROOT_THREAD; + case JVMTI_HEAP_REFERENCE_OTHER: return JVMTI_HEAP_ROOT_OTHER; + default: ShouldNotReachHere(); return JVMTI_HEAP_ROOT_OTHER; + } +} + +// Base class for all heap walk contexts. The base class maintains a flag +// to indicate if the context is valid or not. +class HeapWalkContext VALUE_OBJ_CLASS_SPEC { + private: + bool _valid; + public: + HeapWalkContext(bool valid) { _valid = valid; } + void invalidate() { _valid = false; } + bool is_valid() const { return _valid; } +}; + +// A basic heap walk context for the deprecated heap walking functions. +// The context for a basic heap walk are the callbacks and fields used by +// the referrer caching scheme. +class BasicHeapWalkContext: public HeapWalkContext { + private: + jvmtiHeapRootCallback _heap_root_callback; + jvmtiStackReferenceCallback _stack_ref_callback; + jvmtiObjectReferenceCallback _object_ref_callback; + + // used for caching + oop _last_referrer; + jlong _last_referrer_tag; + + public: + BasicHeapWalkContext() : HeapWalkContext(false) { } + + BasicHeapWalkContext(jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback) : + HeapWalkContext(true), + _heap_root_callback(heap_root_callback), + _stack_ref_callback(stack_ref_callback), + _object_ref_callback(object_ref_callback), + _last_referrer(NULL), + _last_referrer_tag(0) { + } + + // accessors + jvmtiHeapRootCallback heap_root_callback() const { return _heap_root_callback; } + jvmtiStackReferenceCallback stack_ref_callback() const { return _stack_ref_callback; } + jvmtiObjectReferenceCallback object_ref_callback() const { return _object_ref_callback; } + + oop last_referrer() const { return _last_referrer; } + void set_last_referrer(oop referrer) { _last_referrer = referrer; } + jlong last_referrer_tag() const { return _last_referrer_tag; } + void set_last_referrer_tag(jlong value) { _last_referrer_tag = value; } +}; + +// The advanced heap walk context for the FollowReferences functions. +// The context is the callbacks, and the fields used for filtering. +class AdvancedHeapWalkContext: public HeapWalkContext { + private: + jint _heap_filter; + KlassHandle _klass_filter; + const jvmtiHeapCallbacks* _heap_callbacks; + + public: + AdvancedHeapWalkContext() : HeapWalkContext(false) { } + + AdvancedHeapWalkContext(jint heap_filter, + KlassHandle klass_filter, + const jvmtiHeapCallbacks* heap_callbacks) : + HeapWalkContext(true), + _heap_filter(heap_filter), + _klass_filter(klass_filter), + _heap_callbacks(heap_callbacks) { + } + + // accessors + jint heap_filter() const { return _heap_filter; } + KlassHandle klass_filter() const { return _klass_filter; } + + const jvmtiHeapReferenceCallback heap_reference_callback() const { + return _heap_callbacks->heap_reference_callback; + }; + const jvmtiPrimitiveFieldCallback primitive_field_callback() const { + return _heap_callbacks->primitive_field_callback; + } + const jvmtiArrayPrimitiveValueCallback array_primitive_value_callback() const { + return _heap_callbacks->array_primitive_value_callback; + } + const jvmtiStringPrimitiveValueCallback string_primitive_value_callback() const { + return _heap_callbacks->string_primitive_value_callback; + } +}; + +// The CallbackInvoker is a class with static functions that the heap walk can call +// into to invoke callbacks. It works in one of two modes. The "basic" mode is +// used for the deprecated IterateOverReachableObjects functions. The "advanced" +// mode is for the newer FollowReferences function which supports a lot of +// additional callbacks. +class CallbackInvoker : AllStatic { + private: + // heap walk styles + enum { basic, advanced }; + static int _heap_walk_type; + static bool is_basic_heap_walk() { return _heap_walk_type == basic; } + static bool is_advanced_heap_walk() { return _heap_walk_type == advanced; } + + // context for basic style heap walk + static BasicHeapWalkContext _basic_context; + static BasicHeapWalkContext* basic_context() { + assert(_basic_context.is_valid(), "invalid"); + return &_basic_context; + } + + // context for advanced style heap walk + static AdvancedHeapWalkContext _advanced_context; + static AdvancedHeapWalkContext* advanced_context() { + assert(_advanced_context.is_valid(), "invalid"); + return &_advanced_context; + } + + // context needed for all heap walks + static JvmtiTagMap* _tag_map; + static const void* _user_data; + static GrowableArray* _visit_stack; + + // accessors + static JvmtiTagMap* tag_map() { return _tag_map; } + static const void* user_data() { return _user_data; } + static GrowableArray* visit_stack() { return _visit_stack; } + + // if the object hasn't been visited then push it onto the visit stack + // so that it will be visited later + static inline bool check_for_visit(oop obj) { + if (!ObjectMarker::visited(obj)) visit_stack()->push(obj); + return true; + } + + // invoke basic style callbacks + static inline bool invoke_basic_heap_root_callback + (jvmtiHeapRootKind root_kind, oop obj); + static inline bool invoke_basic_stack_ref_callback + (jvmtiHeapRootKind root_kind, jlong thread_tag, jint depth, jmethodID method, + int slot, oop obj); + static inline bool invoke_basic_object_reference_callback + (jvmtiObjectReferenceKind ref_kind, oop referrer, oop referree, jint index); + + // invoke advanced style callbacks + static inline bool invoke_advanced_heap_root_callback + (jvmtiHeapReferenceKind ref_kind, oop obj); + static inline bool invoke_advanced_stack_ref_callback + (jvmtiHeapReferenceKind ref_kind, jlong thread_tag, jlong tid, int depth, + jmethodID method, jlocation bci, jint slot, oop obj); + static inline bool invoke_advanced_object_reference_callback + (jvmtiHeapReferenceKind ref_kind, oop referrer, oop referree, jint index); + + // used to report the value of primitive fields + static inline bool report_primitive_field + (jvmtiHeapReferenceKind ref_kind, oop obj, jint index, address addr, char type); + + public: + // initialize for basic mode + static void initialize_for_basic_heap_walk(JvmtiTagMap* tag_map, + GrowableArray* visit_stack, + const void* user_data, + BasicHeapWalkContext context); + + // initialize for advanced mode + static void initialize_for_advanced_heap_walk(JvmtiTagMap* tag_map, + GrowableArray* visit_stack, + const void* user_data, + AdvancedHeapWalkContext context); + + // functions to report roots + static inline bool report_simple_root(jvmtiHeapReferenceKind kind, oop o); + static inline bool report_jni_local_root(jlong thread_tag, jlong tid, jint depth, + jmethodID m, oop o); + static inline bool report_stack_ref_root(jlong thread_tag, jlong tid, jint depth, + jmethodID method, jlocation bci, jint slot, oop o); + + // functions to report references + static inline bool report_array_element_reference(oop referrer, oop referree, jint index); + static inline bool report_class_reference(oop referrer, oop referree); + static inline bool report_class_loader_reference(oop referrer, oop referree); + static inline bool report_signers_reference(oop referrer, oop referree); + static inline bool report_protection_domain_reference(oop referrer, oop referree); + static inline bool report_superclass_reference(oop referrer, oop referree); + static inline bool report_interface_reference(oop referrer, oop referree); + static inline bool report_static_field_reference(oop referrer, oop referree, jint slot); + static inline bool report_field_reference(oop referrer, oop referree, jint slot); + static inline bool report_constant_pool_reference(oop referrer, oop referree, jint index); + static inline bool report_primitive_array_values(oop array); + static inline bool report_string_value(oop str); + static inline bool report_primitive_instance_field(oop o, jint index, address value, char type); + static inline bool report_primitive_static_field(oop o, jint index, address value, char type); +}; + +// statics +int CallbackInvoker::_heap_walk_type; +BasicHeapWalkContext CallbackInvoker::_basic_context; +AdvancedHeapWalkContext CallbackInvoker::_advanced_context; +JvmtiTagMap* CallbackInvoker::_tag_map; +const void* CallbackInvoker::_user_data; +GrowableArray* CallbackInvoker::_visit_stack; + +// initialize for basic heap walk (IterateOverReachableObjects et al) +void CallbackInvoker::initialize_for_basic_heap_walk(JvmtiTagMap* tag_map, + GrowableArray* visit_stack, + const void* user_data, + BasicHeapWalkContext context) { + _tag_map = tag_map; + _visit_stack = visit_stack; + _user_data = user_data; + _basic_context = context; + _advanced_context.invalidate(); // will trigger assertion if used + _heap_walk_type = basic; +} + +// initialize for advanced heap walk (FollowReferences) +void CallbackInvoker::initialize_for_advanced_heap_walk(JvmtiTagMap* tag_map, + GrowableArray* visit_stack, + const void* user_data, + AdvancedHeapWalkContext context) { + _tag_map = tag_map; + _visit_stack = visit_stack; + _user_data = user_data; + _advanced_context = context; + _basic_context.invalidate(); // will trigger assertion if used + _heap_walk_type = advanced; +} + + +// invoke basic style heap root callback +inline bool CallbackInvoker::invoke_basic_heap_root_callback(jvmtiHeapRootKind root_kind, oop obj) { + assert(ServiceUtil::visible_oop(obj), "checking"); + + // if we heap roots should be reported + jvmtiHeapRootCallback cb = basic_context()->heap_root_callback(); + if (cb == NULL) { + return check_for_visit(obj); + } + + CallbackWrapper wrapper(tag_map(), obj); + jvmtiIterationControl control = (*cb)(root_kind, + wrapper.klass_tag(), + wrapper.obj_size(), + wrapper.obj_tag_p(), + (void*)user_data()); + // push root to visit stack when following references + if (control == JVMTI_ITERATION_CONTINUE && + basic_context()->object_ref_callback() != NULL) { + visit_stack()->push(obj); + } + return control != JVMTI_ITERATION_ABORT; +} + +// invoke basic style stack ref callback +inline bool CallbackInvoker::invoke_basic_stack_ref_callback(jvmtiHeapRootKind root_kind, + jlong thread_tag, + jint depth, + jmethodID method, + jint slot, + oop obj) { + assert(ServiceUtil::visible_oop(obj), "checking"); + + // if we stack refs should be reported + jvmtiStackReferenceCallback cb = basic_context()->stack_ref_callback(); + if (cb == NULL) { + return check_for_visit(obj); + } + + CallbackWrapper wrapper(tag_map(), obj); + jvmtiIterationControl control = (*cb)(root_kind, + wrapper.klass_tag(), + wrapper.obj_size(), + wrapper.obj_tag_p(), + thread_tag, + depth, + method, + slot, + (void*)user_data()); + // push root to visit stack when following references + if (control == JVMTI_ITERATION_CONTINUE && + basic_context()->object_ref_callback() != NULL) { + visit_stack()->push(obj); + } + return control != JVMTI_ITERATION_ABORT; +} + +// invoke basic style object reference callback +inline bool CallbackInvoker::invoke_basic_object_reference_callback(jvmtiObjectReferenceKind ref_kind, + oop referrer, + oop referree, + jint index) { + + assert(ServiceUtil::visible_oop(referrer), "checking"); + assert(ServiceUtil::visible_oop(referree), "checking"); + + BasicHeapWalkContext* context = basic_context(); + + // callback requires the referrer's tag. If it's the same referrer + // as the last call then we use the cached value. + jlong referrer_tag; + if (referrer == context->last_referrer()) { + referrer_tag = context->last_referrer_tag(); + } else { + referrer_tag = tag_for(tag_map(), klassOop_if_java_lang_Class(referrer)); + } + + // do the callback + CallbackWrapper wrapper(tag_map(), referree); + jvmtiObjectReferenceCallback cb = context->object_ref_callback(); + jvmtiIterationControl control = (*cb)(ref_kind, + wrapper.klass_tag(), + wrapper.obj_size(), + wrapper.obj_tag_p(), + referrer_tag, + index, + (void*)user_data()); + + // record referrer and referrer tag. For self-references record the + // tag value from the callback as this might differ from referrer_tag. + context->set_last_referrer(referrer); + if (referrer == referree) { + context->set_last_referrer_tag(*wrapper.obj_tag_p()); + } else { + context->set_last_referrer_tag(referrer_tag); + } + + if (control == JVMTI_ITERATION_CONTINUE) { + return check_for_visit(referree); + } else { + return control != JVMTI_ITERATION_ABORT; + } +} + +// invoke advanced style heap root callback +inline bool CallbackInvoker::invoke_advanced_heap_root_callback(jvmtiHeapReferenceKind ref_kind, + oop obj) { + assert(ServiceUtil::visible_oop(obj), "checking"); + + AdvancedHeapWalkContext* context = advanced_context(); + + // check that callback is provided + jvmtiHeapReferenceCallback cb = context->heap_reference_callback(); + if (cb == NULL) { + return check_for_visit(obj); + } + + // apply class filter + if (is_filtered_by_klass_filter(obj, context->klass_filter())) { + return check_for_visit(obj); + } + + // setup the callback wrapper + CallbackWrapper wrapper(tag_map(), obj); + + // apply tag filter + if (is_filtered_by_heap_filter(wrapper.obj_tag(), + wrapper.klass_tag(), + context->heap_filter())) { + return check_for_visit(obj); + } + + // for arrays we need the length, otherwise -1 + jint len = (jint)(obj->is_array() ? arrayOop(obj)->length() : -1); + + // invoke the callback + jint res = (*cb)(ref_kind, + NULL, // referrer info + wrapper.klass_tag(), + 0, // referrer_class_tag is 0 for heap root + wrapper.obj_size(), + wrapper.obj_tag_p(), + NULL, // referrer_tag_p + len, + (void*)user_data()); + if (res & JVMTI_VISIT_ABORT) { + return false;// referrer class tag + } + if (res & JVMTI_VISIT_OBJECTS) { + check_for_visit(obj); + } + return true; +} + +// report a reference from a thread stack to an object +inline bool CallbackInvoker::invoke_advanced_stack_ref_callback(jvmtiHeapReferenceKind ref_kind, + jlong thread_tag, + jlong tid, + int depth, + jmethodID method, + jlocation bci, + jint slot, + oop obj) { + assert(ServiceUtil::visible_oop(obj), "checking"); + + AdvancedHeapWalkContext* context = advanced_context(); + + // check that callback is provider + jvmtiHeapReferenceCallback cb = context->heap_reference_callback(); + if (cb == NULL) { + return check_for_visit(obj); + } + + // apply class filter + if (is_filtered_by_klass_filter(obj, context->klass_filter())) { + return check_for_visit(obj); + } + + // setup the callback wrapper + CallbackWrapper wrapper(tag_map(), obj); + + // apply tag filter + if (is_filtered_by_heap_filter(wrapper.obj_tag(), + wrapper.klass_tag(), + context->heap_filter())) { + return check_for_visit(obj); + } + + // setup the referrer info + jvmtiHeapReferenceInfo reference_info; + reference_info.stack_local.thread_tag = thread_tag; + reference_info.stack_local.thread_id = tid; + reference_info.stack_local.depth = depth; + reference_info.stack_local.method = method; + reference_info.stack_local.location = bci; + reference_info.stack_local.slot = slot; + + // for arrays we need the length, otherwise -1 + jint len = (jint)(obj->is_array() ? arrayOop(obj)->length() : -1); + + // call into the agent + int res = (*cb)(ref_kind, + &reference_info, + wrapper.klass_tag(), + 0, // referrer_class_tag is 0 for heap root (stack) + wrapper.obj_size(), + wrapper.obj_tag_p(), + NULL, // referrer_tag is 0 for root + len, + (void*)user_data()); + + if (res & JVMTI_VISIT_ABORT) { + return false; + } + if (res & JVMTI_VISIT_OBJECTS) { + check_for_visit(obj); + } + return true; +} + +// This mask is used to pass reference_info to a jvmtiHeapReferenceCallback +// only for ref_kinds defined by the JVM TI spec. Otherwise, NULL is passed. +#define REF_INFO_MASK ((1 << JVMTI_HEAP_REFERENCE_FIELD) \ + | (1 << JVMTI_HEAP_REFERENCE_STATIC_FIELD) \ + | (1 << JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT) \ + | (1 << JVMTI_HEAP_REFERENCE_CONSTANT_POOL) \ + | (1 << JVMTI_HEAP_REFERENCE_STACK_LOCAL) \ + | (1 << JVMTI_HEAP_REFERENCE_JNI_LOCAL)) + +// invoke the object reference callback to report a reference +inline bool CallbackInvoker::invoke_advanced_object_reference_callback(jvmtiHeapReferenceKind ref_kind, + oop referrer, + oop obj, + jint index) +{ + // field index is only valid field in reference_info + static jvmtiHeapReferenceInfo reference_info = { 0 }; + + assert(ServiceUtil::visible_oop(referrer), "checking"); + assert(ServiceUtil::visible_oop(obj), "checking"); + + AdvancedHeapWalkContext* context = advanced_context(); + + // check that callback is provider + jvmtiHeapReferenceCallback cb = context->heap_reference_callback(); + if (cb == NULL) { + return check_for_visit(obj); + } + + // apply class filter + if (is_filtered_by_klass_filter(obj, context->klass_filter())) { + return check_for_visit(obj); + } + + // setup the callback wrapper + TwoOopCallbackWrapper wrapper(tag_map(), referrer, obj); + + // apply tag filter + if (is_filtered_by_heap_filter(wrapper.obj_tag(), + wrapper.klass_tag(), + context->heap_filter())) { + return check_for_visit(obj); + } + + // field index is only valid field in reference_info + reference_info.field.index = index; + + // for arrays we need the length, otherwise -1 + jint len = (jint)(obj->is_array() ? arrayOop(obj)->length() : -1); + + // invoke the callback + int res = (*cb)(ref_kind, + (REF_INFO_MASK & (1 << ref_kind)) ? &reference_info : NULL, + wrapper.klass_tag(), + wrapper.referrer_klass_tag(), + wrapper.obj_size(), + wrapper.obj_tag_p(), + wrapper.referrer_tag_p(), + len, + (void*)user_data()); + + if (res & JVMTI_VISIT_ABORT) { + return false; + } + if (res & JVMTI_VISIT_OBJECTS) { + check_for_visit(obj); + } + return true; +} + +// report a "simple root" +inline bool CallbackInvoker::report_simple_root(jvmtiHeapReferenceKind kind, oop obj) { + assert(kind != JVMTI_HEAP_REFERENCE_STACK_LOCAL && + kind != JVMTI_HEAP_REFERENCE_JNI_LOCAL, "not a simple root"); + assert(ServiceUtil::visible_oop(obj), "checking"); + + if (is_basic_heap_walk()) { + // map to old style root kind + jvmtiHeapRootKind root_kind = toJvmtiHeapRootKind(kind); + return invoke_basic_heap_root_callback(root_kind, obj); + } else { + assert(is_advanced_heap_walk(), "wrong heap walk type"); + return invoke_advanced_heap_root_callback(kind, obj); + } +} + + +// invoke the primitive array values +inline bool CallbackInvoker::report_primitive_array_values(oop obj) { + assert(obj->is_typeArray(), "not a primitive array"); + + AdvancedHeapWalkContext* context = advanced_context(); + assert(context->array_primitive_value_callback() != NULL, "no callback"); + + // apply class filter + if (is_filtered_by_klass_filter(obj, context->klass_filter())) { + return true; + } + + CallbackWrapper wrapper(tag_map(), obj); + + // apply tag filter + if (is_filtered_by_heap_filter(wrapper.obj_tag(), + wrapper.klass_tag(), + context->heap_filter())) { + return true; + } + + // invoke the callback + int res = invoke_array_primitive_value_callback(context->array_primitive_value_callback(), + &wrapper, + obj, + (void*)user_data()); + return (!(res & JVMTI_VISIT_ABORT)); +} + +// invoke the string value callback +inline bool CallbackInvoker::report_string_value(oop str) { + assert(str->klass() == SystemDictionary::string_klass(), "not a string"); + + AdvancedHeapWalkContext* context = advanced_context(); + assert(context->string_primitive_value_callback() != NULL, "no callback"); + + // apply class filter + if (is_filtered_by_klass_filter(str, context->klass_filter())) { + return true; + } + + CallbackWrapper wrapper(tag_map(), str); + + // apply tag filter + if (is_filtered_by_heap_filter(wrapper.obj_tag(), + wrapper.klass_tag(), + context->heap_filter())) { + return true; + } + + // invoke the callback + int res = invoke_string_value_callback(context->string_primitive_value_callback(), + &wrapper, + str, + (void*)user_data()); + return (!(res & JVMTI_VISIT_ABORT)); +} + +// invoke the primitive field callback +inline bool CallbackInvoker::report_primitive_field(jvmtiHeapReferenceKind ref_kind, + oop obj, + jint index, + address addr, + char type) +{ + // for primitive fields only the index will be set + static jvmtiHeapReferenceInfo reference_info = { 0 }; + + AdvancedHeapWalkContext* context = advanced_context(); + assert(context->primitive_field_callback() != NULL, "no callback"); + + // apply class filter + if (is_filtered_by_klass_filter(obj, context->klass_filter())) { + return true; + } + + CallbackWrapper wrapper(tag_map(), obj); + + // apply tag filter + if (is_filtered_by_heap_filter(wrapper.obj_tag(), + wrapper.klass_tag(), + context->heap_filter())) { + return true; + } + + // the field index in the referrer + reference_info.field.index = index; + + // map the type + jvmtiPrimitiveType value_type = (jvmtiPrimitiveType)type; + + // setup the jvalue + jvalue value; + copy_to_jvalue(&value, addr, value_type); + + jvmtiPrimitiveFieldCallback cb = context->primitive_field_callback(); + int res = (*cb)(ref_kind, + &reference_info, + wrapper.klass_tag(), + wrapper.obj_tag_p(), + value, + value_type, + (void*)user_data()); + return (!(res & JVMTI_VISIT_ABORT)); +} + + +// instance field +inline bool CallbackInvoker::report_primitive_instance_field(oop obj, + jint index, + address value, + char type) { + return report_primitive_field(JVMTI_HEAP_REFERENCE_FIELD, + obj, + index, + value, + type); +} + +// static field +inline bool CallbackInvoker::report_primitive_static_field(oop obj, + jint index, + address value, + char type) { + return report_primitive_field(JVMTI_HEAP_REFERENCE_STATIC_FIELD, + obj, + index, + value, + type); +} + +// report a JNI local (root object) to the profiler +inline bool CallbackInvoker::report_jni_local_root(jlong thread_tag, jlong tid, jint depth, jmethodID m, oop obj) { + if (is_basic_heap_walk()) { + return invoke_basic_stack_ref_callback(JVMTI_HEAP_ROOT_JNI_LOCAL, + thread_tag, + depth, + m, + -1, + obj); + } else { + return invoke_advanced_stack_ref_callback(JVMTI_HEAP_REFERENCE_JNI_LOCAL, + thread_tag, tid, + depth, + m, + (jlocation)-1, + -1, + obj); + } +} + + +// report a local (stack reference, root object) +inline bool CallbackInvoker::report_stack_ref_root(jlong thread_tag, + jlong tid, + jint depth, + jmethodID method, + jlocation bci, + jint slot, + oop obj) { + if (is_basic_heap_walk()) { + return invoke_basic_stack_ref_callback(JVMTI_HEAP_ROOT_STACK_LOCAL, + thread_tag, + depth, + method, + slot, + obj); + } else { + return invoke_advanced_stack_ref_callback(JVMTI_HEAP_REFERENCE_STACK_LOCAL, + thread_tag, + tid, + depth, + method, + bci, + slot, + obj); + } +} + +// report an object referencing a class. +inline bool CallbackInvoker::report_class_reference(oop referrer, oop referree) { + if (is_basic_heap_walk()) { + return invoke_basic_object_reference_callback(JVMTI_REFERENCE_CLASS, referrer, referree, -1); + } else { + return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_CLASS, referrer, referree, -1); + } +} + +// report a class referencing its class loader. +inline bool CallbackInvoker::report_class_loader_reference(oop referrer, oop referree) { + if (is_basic_heap_walk()) { + return invoke_basic_object_reference_callback(JVMTI_REFERENCE_CLASS_LOADER, referrer, referree, -1); + } else { + return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_CLASS_LOADER, referrer, referree, -1); + } +} + +// report a class referencing its signers. +inline bool CallbackInvoker::report_signers_reference(oop referrer, oop referree) { + if (is_basic_heap_walk()) { + return invoke_basic_object_reference_callback(JVMTI_REFERENCE_SIGNERS, referrer, referree, -1); + } else { + return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_SIGNERS, referrer, referree, -1); + } +} + +// report a class referencing its protection domain.. +inline bool CallbackInvoker::report_protection_domain_reference(oop referrer, oop referree) { + if (is_basic_heap_walk()) { + return invoke_basic_object_reference_callback(JVMTI_REFERENCE_PROTECTION_DOMAIN, referrer, referree, -1); + } else { + return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN, referrer, referree, -1); + } +} + +// report a class referencing its superclass. +inline bool CallbackInvoker::report_superclass_reference(oop referrer, oop referree) { + if (is_basic_heap_walk()) { + // Send this to be consistent with past implementation + return invoke_basic_object_reference_callback(JVMTI_REFERENCE_CLASS, referrer, referree, -1); + } else { + return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_SUPERCLASS, referrer, referree, -1); + } +} + +// report a class referencing one of its interfaces. +inline bool CallbackInvoker::report_interface_reference(oop referrer, oop referree) { + if (is_basic_heap_walk()) { + return invoke_basic_object_reference_callback(JVMTI_REFERENCE_INTERFACE, referrer, referree, -1); + } else { + return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_INTERFACE, referrer, referree, -1); + } +} + +// report a class referencing one of its static fields. +inline bool CallbackInvoker::report_static_field_reference(oop referrer, oop referree, jint slot) { + if (is_basic_heap_walk()) { + return invoke_basic_object_reference_callback(JVMTI_REFERENCE_STATIC_FIELD, referrer, referree, slot); + } else { + return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_STATIC_FIELD, referrer, referree, slot); + } +} + +// report an array referencing an element object +inline bool CallbackInvoker::report_array_element_reference(oop referrer, oop referree, jint index) { + if (is_basic_heap_walk()) { + return invoke_basic_object_reference_callback(JVMTI_REFERENCE_ARRAY_ELEMENT, referrer, referree, index); + } else { + return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT, referrer, referree, index); + } +} + +// report an object referencing an instance field object +inline bool CallbackInvoker::report_field_reference(oop referrer, oop referree, jint slot) { + if (is_basic_heap_walk()) { + return invoke_basic_object_reference_callback(JVMTI_REFERENCE_FIELD, referrer, referree, slot); + } else { + return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_FIELD, referrer, referree, slot); + } +} + +// report an array referencing an element object +inline bool CallbackInvoker::report_constant_pool_reference(oop referrer, oop referree, jint index) { + if (is_basic_heap_walk()) { + return invoke_basic_object_reference_callback(JVMTI_REFERENCE_CONSTANT_POOL, referrer, referree, index); + } else { + return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_CONSTANT_POOL, referrer, referree, index); + } +} + +// A supporting closure used to process simple roots +class SimpleRootsClosure : public OopClosure { + private: + jvmtiHeapReferenceKind _kind; + bool _continue; + + jvmtiHeapReferenceKind root_kind() { return _kind; } + + public: + void set_kind(jvmtiHeapReferenceKind kind) { + _kind = kind; + _continue = true; + } + + inline bool stopped() { + return !_continue; + } + + void do_oop(oop* obj_p) { + // iteration has terminated + if (stopped()) { + return; + } + + // ignore null or deleted handles + oop o = *obj_p; + if (o == NULL || o == JNIHandles::deleted_handle()) { + return; + } + + jvmtiHeapReferenceKind kind = root_kind(); + + // many roots are Klasses so we use the java mirror + if (o->is_klass()) { + klassOop k = (klassOop)o; + o = Klass::cast(k)->java_mirror(); + } else { + + // SystemDictionary::always_strong_oops_do reports the application + // class loader as a root. We want this root to be reported as + // a root kind of "OTHER" rather than "SYSTEM_CLASS". + if (o->is_instance() && root_kind() == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) { + kind = JVMTI_HEAP_REFERENCE_OTHER; + } + } + + // some objects are ignored - in the case of simple + // roots it's mostly symbolOops that we are skipping + // here. + if (!ServiceUtil::visible_oop(o)) { + return; + } + + // invoke the callback + _continue = CallbackInvoker::report_simple_root(kind, o); + + } +}; + +// A supporting closure used to process JNI locals +class JNILocalRootsClosure : public OopClosure { + private: + jlong _thread_tag; + jlong _tid; + jint _depth; + jmethodID _method; + bool _continue; + public: + void set_context(jlong thread_tag, jlong tid, jint depth, jmethodID method) { + _thread_tag = thread_tag; + _tid = tid; + _depth = depth; + _method = method; + _continue = true; + } + + inline bool stopped() { + return !_continue; + } + + void do_oop(oop* obj_p) { + // iteration has terminated + if (stopped()) { + return; + } + + // ignore null or deleted handles + oop o = *obj_p; + if (o == NULL || o == JNIHandles::deleted_handle()) { + return; + } + + if (!ServiceUtil::visible_oop(o)) { + return; + } + + // invoke the callback + _continue = CallbackInvoker::report_jni_local_root(_thread_tag, _tid, _depth, _method, o); + } +}; + + +// A VM operation to iterate over objects that are reachable from +// a set of roots or an initial object. +// +// For VM_HeapWalkOperation the set of roots used is :- +// +// - All JNI global references +// - All inflated monitors +// - All classes loaded by the boot class loader (or all classes +// in the event that class unloading is disabled) +// - All java threads +// - For each java thread then all locals and JNI local references +// on the thread's execution stack +// - All visible/explainable objects from Universes::oops_do +// +class VM_HeapWalkOperation: public VM_Operation { + private: + enum { + initial_visit_stack_size = 4000 + }; + + bool _is_advanced_heap_walk; // indicates FollowReferences + JvmtiTagMap* _tag_map; + Handle _initial_object; + GrowableArray* _visit_stack; // the visit stack + + bool _collecting_heap_roots; // are we collecting roots + bool _following_object_refs; // are we following object references + + bool _reporting_primitive_fields; // optional reporting + bool _reporting_primitive_array_values; + bool _reporting_string_values; + + GrowableArray* create_visit_stack() { + return new (ResourceObj::C_HEAP) GrowableArray(initial_visit_stack_size, true); + } + + // accessors + bool is_advanced_heap_walk() const { return _is_advanced_heap_walk; } + JvmtiTagMap* tag_map() const { return _tag_map; } + Handle initial_object() const { return _initial_object; } + + bool is_following_references() const { return _following_object_refs; } + + bool is_reporting_primitive_fields() const { return _reporting_primitive_fields; } + bool is_reporting_primitive_array_values() const { return _reporting_primitive_array_values; } + bool is_reporting_string_values() const { return _reporting_string_values; } + + GrowableArray* visit_stack() const { return _visit_stack; } + + // iterate over the various object types + inline bool iterate_over_array(oop o); + inline bool iterate_over_type_array(oop o); + inline bool iterate_over_class(klassOop o); + inline bool iterate_over_object(oop o); + + // root collection + inline bool collect_simple_roots(); + inline bool collect_stack_roots(); + inline bool collect_stack_roots(JavaThread* java_thread, JNILocalRootsClosure* blk); + + // visit an object + inline bool visit(oop o); + + public: + VM_HeapWalkOperation(JvmtiTagMap* tag_map, + Handle initial_object, + BasicHeapWalkContext callbacks, + const void* user_data); + + VM_HeapWalkOperation(JvmtiTagMap* tag_map, + Handle initial_object, + AdvancedHeapWalkContext callbacks, + const void* user_data); + + ~VM_HeapWalkOperation(); + + VMOp_Type type() const { return VMOp_HeapWalkOperation; } + void doit(); +}; + + +VM_HeapWalkOperation::VM_HeapWalkOperation(JvmtiTagMap* tag_map, + Handle initial_object, + BasicHeapWalkContext callbacks, + const void* user_data) { + _is_advanced_heap_walk = false; + _tag_map = tag_map; + _initial_object = initial_object; + _following_object_refs = (callbacks.object_ref_callback() != NULL); + _reporting_primitive_fields = false; + _reporting_primitive_array_values = false; + _reporting_string_values = false; + _visit_stack = create_visit_stack(); + + + CallbackInvoker::initialize_for_basic_heap_walk(tag_map, _visit_stack, user_data, callbacks); +} + +VM_HeapWalkOperation::VM_HeapWalkOperation(JvmtiTagMap* tag_map, + Handle initial_object, + AdvancedHeapWalkContext callbacks, + const void* user_data) { + _is_advanced_heap_walk = true; + _tag_map = tag_map; + _initial_object = initial_object; + _following_object_refs = true; + _reporting_primitive_fields = (callbacks.primitive_field_callback() != NULL);; + _reporting_primitive_array_values = (callbacks.array_primitive_value_callback() != NULL);; + _reporting_string_values = (callbacks.string_primitive_value_callback() != NULL);; + _visit_stack = create_visit_stack(); + + CallbackInvoker::initialize_for_advanced_heap_walk(tag_map, _visit_stack, user_data, callbacks); +} + +VM_HeapWalkOperation::~VM_HeapWalkOperation() { + if (_following_object_refs) { + assert(_visit_stack != NULL, "checking"); + delete _visit_stack; + _visit_stack = NULL; + } +} + +// an array references its class and has a reference to +// each element in the array +inline bool VM_HeapWalkOperation::iterate_over_array(oop o) { + objArrayOop array = objArrayOop(o); + if (array->klass() == Universe::systemObjArrayKlassObj()) { + // filtered out + return true; + } + + // array reference to its class + oop mirror = objArrayKlass::cast(array->klass())->java_mirror(); + if (!CallbackInvoker::report_class_reference(o, mirror)) { + return false; + } + + // iterate over the array and report each reference to a + // non-null element + for (int index=0; indexlength(); index++) { + oop elem = array->obj_at(index); + if (elem == NULL) { + continue; + } + + // report the array reference o[index] = elem + if (!CallbackInvoker::report_array_element_reference(o, elem, index)) { + return false; + } + } + return true; +} + +// a type array references its class +inline bool VM_HeapWalkOperation::iterate_over_type_array(oop o) { + klassOop k = o->klass(); + oop mirror = Klass::cast(k)->java_mirror(); + if (!CallbackInvoker::report_class_reference(o, mirror)) { + return false; + } + + // report the array contents if required + if (is_reporting_primitive_array_values()) { + if (!CallbackInvoker::report_primitive_array_values(o)) { + return false; + } + } + return true; +} + +// verify that a static oop field is in range +static inline bool verify_static_oop(instanceKlass* ik, oop* obj_p) { + oop* start = ik->start_of_static_fields(); + oop* end = start + ik->static_oop_field_size(); + assert(end >= start, "sanity check"); + + if (obj_p >= start && obj_p < end) { + return true; + } else { + return false; + } +} + +// a class references its super class, interfaces, class loader, ... +// and finally its static fields +inline bool VM_HeapWalkOperation::iterate_over_class(klassOop k) { + int i; + Klass* klass = klassOop(k)->klass_part(); + + if (klass->oop_is_instance()) { + instanceKlass* ik = instanceKlass::cast(k); + + // ignore the class if it's has been initialized yet + if (!ik->is_linked()) { + return true; + } + + // get the java mirror + oop mirror = klass->java_mirror(); + + // super (only if something more interesting than java.lang.Object) + klassOop java_super = ik->java_super(); + if (java_super != NULL && java_super != SystemDictionary::object_klass()) { + oop super = Klass::cast(java_super)->java_mirror(); + if (!CallbackInvoker::report_superclass_reference(mirror, super)) { + return false; + } + } + + // class loader + oop cl = ik->class_loader(); + if (cl != NULL) { + if (!CallbackInvoker::report_class_loader_reference(mirror, cl)) { + return false; + } + } + + // protection domain + oop pd = ik->protection_domain(); + if (pd != NULL) { + if (!CallbackInvoker::report_protection_domain_reference(mirror, pd)) { + return false; + } + } + + // signers + oop signers = ik->signers(); + if (signers != NULL) { + if (!CallbackInvoker::report_signers_reference(mirror, signers)) { + return false; + } + } + + // references from the constant pool + { + const constantPoolOop pool = ik->constants(); + for (int i = 1; i < pool->length(); i++) { + constantTag tag = pool->tag_at(i).value(); + if (tag.is_string() || tag.is_klass()) { + oop entry; + if (tag.is_string()) { + entry = pool->resolved_string_at(i); + assert(java_lang_String::is_instance(entry), "must be string"); + } else { + entry = Klass::cast(pool->resolved_klass_at(i))->java_mirror(); + } + if (!CallbackInvoker::report_constant_pool_reference(mirror, entry, (jint)i)) { + return false; + } + } + } + } + + // interfaces + // (These will already have been reported as references from the constant pool + // but are specified by IterateOverReachableObjects and must be reported). + objArrayOop interfaces = ik->local_interfaces(); + for (i = 0; i < interfaces->length(); i++) { + oop interf = Klass::cast((klassOop)interfaces->obj_at(i))->java_mirror(); + if (interf == NULL) { + continue; + } + if (!CallbackInvoker::report_interface_reference(mirror, interf)) { + return false; + } + } + + // iterate over the static fields + + ClassFieldMap* field_map = ClassFieldMap::create_map_of_static_fields(k); + for (i=0; ifield_count(); i++) { + ClassFieldDescriptor* field = field_map->field_at(i); + char type = field->field_type(); + if (!is_primitive_field_type(type)) { + address addr = (address)k + field->field_offset(); + oop* f = (oop*)addr; + assert(verify_static_oop(ik, f), "sanity check"); + oop fld_o = *f; + if (fld_o != NULL) { + int slot = field->field_index(); + if (!CallbackInvoker::report_static_field_reference(mirror, fld_o, slot)) { + delete field_map; + return false; + } + } + } else { + if (is_reporting_primitive_fields()) { + address addr = (address)k + field->field_offset(); + int slot = field->field_index(); + if (!CallbackInvoker::report_primitive_static_field(mirror, slot, addr, type)) { + delete field_map; + return false; + } + } + } + } + delete field_map; + + return true; + } + + return true; +} + +// an object references a class and its instance fields +// (static fields are ignored here as we report these as +// references from the class). +inline bool VM_HeapWalkOperation::iterate_over_object(oop o) { + // reference to the class + if (!CallbackInvoker::report_class_reference(o, Klass::cast(o->klass())->java_mirror())) { + return false; + } + + // iterate over instance fields + ClassFieldMap* field_map = JvmtiCachedClassFieldMap::get_map_of_instance_fields(o); + for (int i=0; ifield_count(); i++) { + ClassFieldDescriptor* field = field_map->field_at(i); + char type = field->field_type(); + if (!is_primitive_field_type(type)) { + address addr = (address)o + field->field_offset(); + oop* f = (oop*)addr; + oop fld_o = *f; + if (fld_o != NULL) { + // reflection code may have a reference to a klassOop. + // - see sun.reflect.UnsafeStaticFieldAccessorImpl and sun.misc.Unsafe + if (fld_o->is_klass()) { + klassOop k = (klassOop)fld_o; + fld_o = Klass::cast(k)->java_mirror(); + } + int slot = field->field_index(); + if (!CallbackInvoker::report_field_reference(o, fld_o, slot)) { + return false; + } + } + } else { + if (is_reporting_primitive_fields()) { + // primitive instance field + address addr = (address)o + field->field_offset(); + int slot = field->field_index(); + if (!CallbackInvoker::report_primitive_instance_field(o, slot, addr, type)) { + return false; + } + } + } + } + + // if the object is a java.lang.String + if (is_reporting_string_values() && + o->klass() == SystemDictionary::string_klass()) { + if (!CallbackInvoker::report_string_value(o)) { + return false; + } + } + return true; +} + + +// collects all simple (non-stack) roots. +// if there's a heap root callback provided then the callback is +// invoked for each simple root. +// if an object reference callback is provided then all simple +// roots are pushed onto the marking stack so that they can be +// processed later +// +inline bool VM_HeapWalkOperation::collect_simple_roots() { + SimpleRootsClosure blk; + + // JNI globals + blk.set_kind(JVMTI_HEAP_REFERENCE_JNI_GLOBAL); + JNIHandles::oops_do(&blk); + if (blk.stopped()) { + return false; + } + + // Preloaded classes and loader from the system dictionary + blk.set_kind(JVMTI_HEAP_REFERENCE_SYSTEM_CLASS); + SystemDictionary::always_strong_oops_do(&blk); + if (blk.stopped()) { + return false; + } + + // Inflated monitors + blk.set_kind(JVMTI_HEAP_REFERENCE_MONITOR); + ObjectSynchronizer::oops_do(&blk); + if (blk.stopped()) { + return false; + } + + // Threads + for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) { + oop threadObj = thread->threadObj(); + if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) { + bool cont = CallbackInvoker::report_simple_root(JVMTI_HEAP_REFERENCE_THREAD, threadObj); + if (!cont) { + return false; + } + } + } + + // Other kinds of roots maintained by HotSpot + // Many of these won't be visible but others (such as instances of important + // exceptions) will be visible. + blk.set_kind(JVMTI_HEAP_REFERENCE_OTHER); + Universe::oops_do(&blk); + return true; +} + +// Walk the stack of a given thread and find all references (locals +// and JNI calls) and report these as stack references +inline bool VM_HeapWalkOperation::collect_stack_roots(JavaThread* java_thread, + JNILocalRootsClosure* blk) +{ + oop threadObj = java_thread->threadObj(); + assert(threadObj != NULL, "sanity check"); + + // only need to get the thread's tag once per thread + jlong thread_tag = tag_for(_tag_map, threadObj); + + // also need the thread id + jlong tid = java_lang_Thread::thread_id(threadObj); + + + if (java_thread->has_last_Java_frame()) { + + // vframes are resource allocated + Thread* current_thread = Thread::current(); + ResourceMark rm(current_thread); + HandleMark hm(current_thread); + + RegisterMap reg_map(java_thread); + frame f = java_thread->last_frame(); + vframe* vf = vframe::new_vframe(&f, ®_map, java_thread); + + bool is_top_frame = true; + int depth = 0; + frame* last_entry_frame = NULL; + + while (vf != NULL) { + if (vf->is_java_frame()) { + + // java frame (interpreted, compiled, ...) + javaVFrame *jvf = javaVFrame::cast(vf); + + // the jmethodID + jmethodID method = jvf->method()->jmethod_id(); + + if (!(jvf->method()->is_native())) { + jlocation bci = (jlocation)jvf->bci(); + StackValueCollection* locals = jvf->locals(); + for (int slot=0; slotsize(); slot++) { + if (locals->at(slot)->type() == T_OBJECT) { + oop o = locals->obj_at(slot)(); + if (o == NULL) { + continue; + } + + // stack reference + if (!CallbackInvoker::report_stack_ref_root(thread_tag, tid, depth, method, + bci, slot, o)) { + return false; + } + } + } + } else { + blk->set_context(thread_tag, tid, depth, method); + if (is_top_frame) { + // JNI locals for the top frame. + java_thread->active_handles()->oops_do(blk); + } else { + if (last_entry_frame != NULL) { + // JNI locals for the entry frame + assert(last_entry_frame->is_entry_frame(), "checking"); + last_entry_frame->entry_frame_call_wrapper()->handles()->oops_do(blk); + } + } + } + last_entry_frame = NULL; + depth++; + } else { + // externalVFrame - for an entry frame then we report the JNI locals + // when we find the corresponding javaVFrame + frame* fr = vf->frame_pointer(); + assert(fr != NULL, "sanity check"); + if (fr->is_entry_frame()) { + last_entry_frame = fr; + } + } + + vf = vf->sender(); + is_top_frame = false; + } + } else { + // no last java frame but there may be JNI locals + blk->set_context(thread_tag, tid, 0, (jmethodID)NULL); + java_thread->active_handles()->oops_do(blk); + } + return true; +} + + +// collects all stack roots - for each thread it walks the execution +// stack to find all references and local JNI refs. +inline bool VM_HeapWalkOperation::collect_stack_roots() { + JNILocalRootsClosure blk; + for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) { + oop threadObj = thread->threadObj(); + if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) { + if (!collect_stack_roots(thread, &blk)) { + return false; + } + } + } + return true; +} + +// visit an object +// first mark the object as visited +// second get all the outbound references from this object (in other words, all +// the objects referenced by this object). +// +bool VM_HeapWalkOperation::visit(oop o) { + // mark object as visited + assert(!ObjectMarker::visited(o), "can't visit same object more than once"); + ObjectMarker::mark(o); + + // instance + if (o->is_instance()) { + if (o->klass() == SystemDictionary::class_klass()) { + o = klassOop_if_java_lang_Class(o); + if (o->is_klass()) { + // a java.lang.Class + return iterate_over_class(klassOop(o)); + } + } else { + return iterate_over_object(o); + } + } + + // object array + if (o->is_objArray()) { + return iterate_over_array(o); + } + + // type array + if (o->is_typeArray()) { + return iterate_over_type_array(o); + } + + return true; +} + +void VM_HeapWalkOperation::doit() { + ResourceMark rm; + ObjectMarkerController marker; + ClassFieldMapCacheMark cm; + + assert(visit_stack()->is_empty(), "visit stack must be empty"); + + // the heap walk starts with an initial object or the heap roots + if (initial_object().is_null()) { + if (!collect_simple_roots()) return; + if (!collect_stack_roots()) return; + } else { + visit_stack()->push(initial_object()()); + } + + // object references required + if (is_following_references()) { + + // visit each object until all reachable objects have been + // visited or the callback asked to terminate the iteration. + while (!visit_stack()->is_empty()) { + oop o = visit_stack()->pop(); + if (!ObjectMarker::visited(o)) { + if (!visit(o)) { + break; + } + } + } + } +} + +// iterate over all objects that are reachable from a set of roots +void JvmtiTagMap::iterate_over_reachable_objects(jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data) { + MutexLocker ml(Heap_lock); + BasicHeapWalkContext context(heap_root_callback, stack_ref_callback, object_ref_callback); + VM_HeapWalkOperation op(this, Handle(), context, user_data); + VMThread::execute(&op); +} + +// iterate over all objects that are reachable from a given object +void JvmtiTagMap::iterate_over_objects_reachable_from_object(jobject object, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data) { + oop obj = JNIHandles::resolve(object); + Handle initial_object(Thread::current(), obj); + + MutexLocker ml(Heap_lock); + BasicHeapWalkContext context(NULL, NULL, object_ref_callback); + VM_HeapWalkOperation op(this, initial_object, context, user_data); + VMThread::execute(&op); +} + +// follow references from an initial object or the GC roots +void JvmtiTagMap::follow_references(jint heap_filter, + KlassHandle klass, + jobject object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) +{ + oop obj = JNIHandles::resolve(object); + Handle initial_object(Thread::current(), obj); + + MutexLocker ml(Heap_lock); + AdvancedHeapWalkContext context(heap_filter, klass, callbacks); + VM_HeapWalkOperation op(this, initial_object, context, user_data); + VMThread::execute(&op); +} + + +// called post-GC +// - for each JVMTI environment with an object tag map, call its rehash +// function to re-sync with the new object locations. +void JvmtiTagMap::gc_epilogue(bool full) { + assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint"); + if (JvmtiEnv::environments_might_exist()) { + // re-obtain the memory region for the young generation (might + // changed due to adaptive resizing policy) + get_young_generation(); + + JvmtiEnvIterator it; + for (JvmtiEnvBase* env = it.first(); env != NULL; env = it.next(env)) { + JvmtiTagMap* tag_map = env->tag_map(); + if (tag_map != NULL && !tag_map->is_empty()) { + TraceTime t(full ? "JVMTI Full Rehash " : "JVMTI Rehash ", TraceJVMTIObjectTagging); + if (full) { + tag_map->rehash(0, n_hashmaps); + } else { + tag_map->rehash(0, 0); // tag map for young gen only + } + } + } + } +} + +// CMS has completed referencing processing so we may have JNI weak refs +// to objects in the CMS generation that have been GC'ed. +void JvmtiTagMap::cms_ref_processing_epilogue() { + assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint"); + assert(UseConcMarkSweepGC, "should only be used with CMS"); + if (JvmtiEnv::environments_might_exist()) { + JvmtiEnvIterator it; + for (JvmtiEnvBase* env = it.first(); env != NULL; env = it.next(env)) { + JvmtiTagMap* tag_map = ((JvmtiEnvBase *)env)->tag_map(); + if (tag_map != NULL && !tag_map->is_empty()) { + TraceTime t("JVMTI Rehash (CMS) ", TraceJVMTIObjectTagging); + tag_map->rehash(1, n_hashmaps); // assume CMS not used in young gen + } + } + } +} + + +// For each entry in the hashmaps 'start' to 'end' : +// +// 1. resolve the JNI weak reference +// +// 2. If it resolves to NULL it means the object has been freed so the entry +// is removed, the weak reference destroyed, and the object free event is +// posted (if enabled). +// +// 3. If the weak reference resolves to an object then we re-hash the object +// to see if it has moved or has been promoted (from the young to the old +// generation for example). +// +void JvmtiTagMap::rehash(int start, int end) { + + // does this environment have the OBJECT_FREE event enabled + bool post_object_free = env()->is_enabled(JVMTI_EVENT_OBJECT_FREE); + + // counters used for trace message + int freed = 0; + int moved = 0; + int promoted = 0; + + // we assume there are two hashmaps - one for the young generation + // and the other for all other spaces. + assert(n_hashmaps == 2, "not implemented"); + JvmtiTagHashmap* young_hashmap = _hashmap[0]; + JvmtiTagHashmap* other_hashmap = _hashmap[1]; + + // reenable sizing (if disabled) + young_hashmap->set_resizing_enabled(true); + other_hashmap->set_resizing_enabled(true); + + // when re-hashing the hashmap corresponding to the young generation we + // collect the entries corresponding to objects that have been promoted. + JvmtiTagHashmapEntry* promoted_entries = NULL; + + if (end >= n_hashmaps) { + end = n_hashmaps - 1; + } + + for (int i=start; i <= end; i++) { + JvmtiTagHashmap* hashmap = _hashmap[i]; + + // if the hashmap is empty then we can skip it + if (hashmap->_entry_count == 0) { + continue; + } + + // now iterate through each entry in the table + + JvmtiTagHashmapEntry** table = hashmap->table(); + int size = hashmap->size(); + + for (int pos=0; posnext(); + + jweak ref = entry->object(); + oop oop = JNIHandles::resolve(ref); + + // has object been GC'ed + if (oop == NULL) { + // grab the tag + jlong tag = entry->tag(); + guarantee(tag != 0, "checking"); + + // remove GC'ed entry from hashmap and return the + // entry to the free list + hashmap->remove(prev, pos, entry); + destroy_entry(entry); + + // destroy the weak ref + JNIHandles::destroy_weak_global(ref); + + // post the event to the profiler + if (post_object_free) { + JvmtiExport::post_object_free(env(), tag); + } + + freed++; + entry = next; + continue; + } + + // if this is the young hashmap then the object is either promoted + // or moved. + // if this is the other hashmap then the object is moved. + + bool same_gen; + if (i == 0) { + assert(hashmap == young_hashmap, "checking"); + same_gen = is_in_young(oop); + } else { + same_gen = true; + } + + + if (same_gen) { + // if the object has moved then re-hash it and move its + // entry to its new location. + unsigned int new_pos = JvmtiTagHashmap::hash(oop, size); + if (new_pos != (unsigned int)pos) { + if (prev == NULL) { + table[pos] = next; + } else { + prev->set_next(next); + } + entry->set_next(table[new_pos]); + table[new_pos] = entry; + moved++; + } else { + // object didn't move + prev = entry; + } + } else { + // object has been promoted so remove the entry from the + // young hashmap + assert(hashmap == young_hashmap, "checking"); + hashmap->remove(prev, pos, entry); + + // move the entry to the promoted list + entry->set_next(promoted_entries); + promoted_entries = entry; + } + + entry = next; + } + } + } + + + // add the entries, corresponding to the promoted objects, to the + // other hashmap. + JvmtiTagHashmapEntry* entry = promoted_entries; + while (entry != NULL) { + oop o = JNIHandles::resolve(entry->object()); + assert(hashmap_for(o) == other_hashmap, "checking"); + JvmtiTagHashmapEntry* next = entry->next(); + other_hashmap->add(o, entry); + entry = next; + promoted++; + } + + // stats + if (TraceJVMTIObjectTagging) { + int total_moves = promoted + moved; + + int post_total = 0; + for (int i=0; i_entry_count; + } + int pre_total = post_total + freed; + + tty->print("(%d->%d, %d freed, %d promoted, %d total moves)", + pre_total, post_total, freed, promoted, total_moves); + } +} diff --git a/hotspot/src/share/vm/prims/jvmtiTagMap.hpp b/hotspot/src/share/vm/prims/jvmtiTagMap.hpp new file mode 100644 index 00000000000..84667c53176 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiTagMap.hpp @@ -0,0 +1,135 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// JvmtiTagMap + +#ifndef _JAVA_JVMTI_TAG_MAP_H_ +#define _JAVA_JVMTI_TAG_MAP_H_ + +// forward references +class JvmtiTagHashmap; +class JvmtiTagHashmapEntry; +class JvmtiTagHashmapEntryClosure; + +class JvmtiTagMap : public CHeapObj { + private: + + enum{ + n_hashmaps = 2, // encapsulates 2 hashmaps + max_free_entries = 4096 // maximum number of free entries per env + }; + + // memory region for young generation + static MemRegion _young_gen; + static void get_young_generation(); + + JvmtiEnv* _env; // the jvmti environment + Mutex _lock; // lock for this tag map + JvmtiTagHashmap* _hashmap[n_hashmaps]; // the hashmaps + + JvmtiTagHashmapEntry* _free_entries; // free list for this environment + int _free_entries_count; // number of entries on the free list + + // create a tag map + JvmtiTagMap(JvmtiEnv* env); + + // accessors + inline Mutex* lock() { return &_lock; } + inline JvmtiEnv* env() const { return _env; } + + // rehash tags maps for generation start to end + void rehash(int start, int end); + + // indicates if the object is in the young generation + static bool is_in_young(oop o); + + // iterate over all entries in this tag map + void entry_iterate(JvmtiTagHashmapEntryClosure* closure); + + public: + + // indicates if this tag map is locked + bool is_locked() { return lock()->is_locked(); } + + // return the appropriate hashmap for a given object + JvmtiTagHashmap* hashmap_for(oop o); + + // create/destroy entries + JvmtiTagHashmapEntry* create_entry(jweak ref, jlong tag); + void destroy_entry(JvmtiTagHashmapEntry* entry); + + // returns true if the hashmaps are empty + bool is_empty(); + + // return tag for the given environment + static JvmtiTagMap* tag_map_for(JvmtiEnv* env); + + // destroy tag map + ~JvmtiTagMap(); + + // set/get tag + void set_tag(jobject obj, jlong tag); + jlong get_tag(jobject obj); + + // deprecated heap iteration functions + void iterate_over_heap(jvmtiHeapObjectFilter object_filter, + KlassHandle klass, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + void iterate_over_reachable_objects(jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data); + + void iterate_over_objects_reachable_from_object(jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data); + + + // advanced (JVMTI 1.1) heap iteration functions + void iterate_through_heap(jint heap_filter, + KlassHandle klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + void follow_references(jint heap_filter, + KlassHandle klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + // get tagged objects + jvmtiError get_objects_with_tags(const jlong* tags, jint count, + jint* count_ptr, jobject** object_result_ptr, + jlong** tag_result_ptr); + + // call post-GC to rehash the tag maps. + static void gc_epilogue(bool full); + + // call after referencing processing has completed (CMS) + static void cms_ref_processing_epilogue(); +}; + +#endif /* _JAVA_JVMTI_TAG_MAP_H_ */ diff --git a/hotspot/src/share/vm/prims/jvmtiThreadState.cpp b/hotspot/src/share/vm/prims/jvmtiThreadState.cpp new file mode 100644 index 00000000000..72af98b55af --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiThreadState.cpp @@ -0,0 +1,394 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiThreadState.cpp.incl" + +// marker for when the stack depth has been reset and is now unknown. +// any negative number would work but small ones might obscure an +// underrun error. +static const int UNKNOWN_STACK_DEPTH = -99; + +/////////////////////////////////////////////////////////////// +// +// class JvmtiThreadState +// +// Instances of JvmtiThreadState hang off of each thread. +// Thread local storage for JVMTI. +// + +JvmtiThreadState *JvmtiThreadState::_head = NULL; + +JvmtiThreadState::JvmtiThreadState(JavaThread* thread) + : _thread_event_enable() { + assert(JvmtiThreadState_lock->is_locked(), "sanity check"); + _thread = thread; + _exception_detected = false; + _exception_caught = false; + _debuggable = true; + _hide_single_stepping = false; + _hide_level = 0; + _pending_step_for_popframe = false; + _class_being_redefined = NULL; + _class_load_kind = jvmti_class_load_kind_load; + _head_env_thread_state = NULL; + _dynamic_code_event_collector = NULL; + _vm_object_alloc_event_collector = NULL; + _the_class_for_redefinition_verification = NULL; + _scratch_class_for_redefinition_verification = NULL; + + // JVMTI ForceEarlyReturn support + _pending_step_for_earlyret = false; + _earlyret_state = earlyret_inactive; + _earlyret_tos = ilgl; + _earlyret_value.j = 0L; + _earlyret_oop = NULL; + + // add all the JvmtiEnvThreadState to the new JvmtiThreadState + { + JvmtiEnvIterator it; + for (JvmtiEnvBase* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_valid()) { + add_env(env); + } + } + } + + // link us into the list + { + // The thread state list manipulation code must not have safepoints. + // See periodic_clean_up(). + debug_only(No_Safepoint_Verifier nosafepoint;) + + _prev = NULL; + _next = _head; + if (_head != NULL) { + _head->_prev = this; + } + _head = this; + } + + // set this as the state for the thread + thread->set_jvmti_thread_state(this); +} + + +JvmtiThreadState::~JvmtiThreadState() { + assert(JvmtiThreadState_lock->is_locked(), "sanity check"); + + // clear this as the state for the thread + get_thread()->set_jvmti_thread_state(NULL); + + // zap our env thread states + { + JvmtiEnvBase::entering_dying_thread_env_iteration(); + JvmtiEnvThreadStateIterator it(this); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ) { + JvmtiEnvThreadState* zap = ets; + ets = it.next(ets); + delete zap; + } + JvmtiEnvBase::leaving_dying_thread_env_iteration(); + } + + // remove us from the list + { + // The thread state list manipulation code must not have safepoints. + // See periodic_clean_up(). + debug_only(No_Safepoint_Verifier nosafepoint;) + + if (_prev == NULL) { + assert(_head == this, "sanity check"); + _head = _next; + } else { + assert(_head != this, "sanity check"); + _prev->_next = _next; + } + if (_next != NULL) { + _next->_prev = _prev; + } + _next = NULL; + _prev = NULL; + } +} + + +void +JvmtiThreadState::periodic_clean_up() { + assert(SafepointSynchronize::is_at_safepoint(), "at safepoint"); + + // This iteration is initialized with "_head" instead of "JvmtiThreadState::first()" + // because the latter requires the JvmtiThreadState_lock. + // This iteration is safe at a safepoint as well, see the No_Safepoint_Verifier + // asserts at all list manipulation sites. + for (JvmtiThreadState *state = _head; state != NULL; state = state->next()) { + // For each environment thread state corresponding to an invalid environment + // unlink it from the list and deallocate it. + JvmtiEnvThreadStateIterator it(state); + JvmtiEnvThreadState* previous_ets = NULL; + JvmtiEnvThreadState* ets = it.first(); + while (ets != NULL) { + if (ets->get_env()->is_valid()) { + previous_ets = ets; + ets = it.next(ets); + } else { + // This one isn't valid, remove it from the list and deallocate it + JvmtiEnvThreadState* defunct_ets = ets; + ets = ets->next(); + if (previous_ets == NULL) { + assert(state->head_env_thread_state() == defunct_ets, "sanity check"); + state->set_head_env_thread_state(ets); + } else { + previous_ets->set_next(ets); + } + delete defunct_ets; + } + } + } +} + +void JvmtiThreadState::add_env(JvmtiEnvBase *env) { + assert(JvmtiThreadState_lock->is_locked(), "sanity check"); + + JvmtiEnvThreadState *new_ets = new JvmtiEnvThreadState(_thread, env); + // add this environment thread state to the end of the list (order is important) + { + // list deallocation (which occurs at a safepoint) cannot occur simultaneously + debug_only(No_Safepoint_Verifier nosafepoint;) + + JvmtiEnvThreadStateIterator it(this); + JvmtiEnvThreadState* previous_ets = NULL; + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + previous_ets = ets; + } + if (previous_ets == NULL) { + set_head_env_thread_state(new_ets); + } else { + previous_ets->set_next(new_ets); + } + } +} + + + + +void JvmtiThreadState::enter_interp_only_mode() { + assert(_thread->get_interp_only_mode() == 0, "entering interp only when mode not zero"); + _thread->increment_interp_only_mode(); +} + + +void JvmtiThreadState::leave_interp_only_mode() { + assert(_thread->get_interp_only_mode() == 1, "leaving interp only when mode not one"); + _thread->decrement_interp_only_mode(); +} + + +// Helper routine used in several places +int JvmtiThreadState::count_frames() { +#ifdef ASSERT + uint32_t debug_bits = 0; +#endif + assert(SafepointSynchronize::is_at_safepoint() || + JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits), + "at safepoint or must be suspended"); + + if (!get_thread()->has_last_Java_frame()) return 0; // no Java frames + + ResourceMark rm; + RegisterMap reg_map(get_thread()); + javaVFrame *jvf = get_thread()->last_java_vframe(®_map); + int n = 0; + // tty->print_cr("CSD: counting frames on %s ...", + // JvmtiTrace::safe_get_thread_name(get_thread())); + while (jvf != NULL) { + methodOop method = jvf->method(); + // tty->print_cr("CSD: frame - method %s.%s - loc %d", + // method->klass_name()->as_C_string(), + // method->name()->as_C_string(), + // jvf->bci() ); + jvf = jvf->java_sender(); + n++; + } + // tty->print_cr("CSD: frame count: %d", n); + return n; +} + + +void JvmtiThreadState::invalidate_cur_stack_depth() { + Thread *cur = Thread::current(); + uint32_t debug_bits = 0; + + // The caller can be the VMThread at a safepoint, the current thread + // or the target thread must be suspended. + guarantee((cur->is_VM_thread() && SafepointSynchronize::is_at_safepoint()) || + (JavaThread *)cur == get_thread() || + JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits), + "sanity check"); + + _cur_stack_depth = UNKNOWN_STACK_DEPTH; +} + +void JvmtiThreadState::incr_cur_stack_depth() { + guarantee(JavaThread::current() == get_thread(), "must be current thread"); + + if (!is_interp_only_mode()) { + _cur_stack_depth = UNKNOWN_STACK_DEPTH; + } + if (_cur_stack_depth != UNKNOWN_STACK_DEPTH) { + ++_cur_stack_depth; + } +} + +void JvmtiThreadState::decr_cur_stack_depth() { + guarantee(JavaThread::current() == get_thread(), "must be current thread"); + + if (!is_interp_only_mode()) { + _cur_stack_depth = UNKNOWN_STACK_DEPTH; + } + if (_cur_stack_depth != UNKNOWN_STACK_DEPTH) { + --_cur_stack_depth; + assert(_cur_stack_depth >= 0, "incr/decr_cur_stack_depth mismatch"); + } +} + +int JvmtiThreadState::cur_stack_depth() { + uint32_t debug_bits = 0; + guarantee(JavaThread::current() == get_thread() || + JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits), + "must be current thread or suspended"); + + if (!is_interp_only_mode() || _cur_stack_depth == UNKNOWN_STACK_DEPTH) { + _cur_stack_depth = count_frames(); + } else { + // heavy weight assert + assert(_cur_stack_depth == count_frames(), + "cur_stack_depth out of sync"); + } + return _cur_stack_depth; +} + +bool JvmtiThreadState::may_be_walked() { + return (get_thread()->is_being_ext_suspended() || (JavaThread::current() == get_thread())); +} + + +void JvmtiThreadState::process_pending_step_for_popframe() { + // We are single stepping as the last part of the PopFrame() dance + // so we have some house keeping to do. + + JavaThread *thr = get_thread(); + if (thr->popframe_condition() != JavaThread::popframe_inactive) { + // If the popframe_condition field is not popframe_inactive, then + // we missed all of the popframe_field cleanup points: + // + // - unpack_frames() was not called (nothing to deopt) + // - remove_activation_preserving_args_entry() was not called + // (did not get suspended in a call_vm() family call and did + // not complete a call_vm() family call on the way here) + thr->clear_popframe_condition(); + } + + // clearing the flag indicates we are done with the PopFrame() dance + clr_pending_step_for_popframe(); + + // If step is pending for popframe then it may not be + // a repeat step. The new_bci and method_id is same as current_bci + // and current method_id after pop and step for recursive calls. + // Force the step by clearing the last location. + JvmtiEnvThreadStateIterator it(this); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + ets->clear_current_location(); + } +} + + +// Class: JvmtiThreadState +// Function: update_for_pop_top_frame +// Description: +// This function removes any frame pop notification request for +// the top frame and invalidates both the current stack depth and +// all cached frameIDs. +// +// Called by: PopFrame +// +void JvmtiThreadState::update_for_pop_top_frame() { + if (is_interp_only_mode()) { + // remove any frame pop notification request for the top frame + // in any environment + int popframe_number = cur_stack_depth(); + { + JvmtiEnvThreadStateIterator it(this); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_frame_pop(popframe_number)) { + ets->clear_frame_pop(popframe_number); + } + } + } + // force stack depth to be recalculated + invalidate_cur_stack_depth(); + } else { + assert(!is_enabled(JVMTI_EVENT_FRAME_POP), "Must have no framepops set"); + } +} + + +void JvmtiThreadState::process_pending_step_for_earlyret() { + // We are single stepping as the last part of the ForceEarlyReturn + // dance so we have some house keeping to do. + + if (is_earlyret_pending()) { + // If the earlyret_state field is not earlyret_inactive, then + // we missed all of the earlyret_field cleanup points: + // + // - remove_activation() was not called + // (did not get suspended in a call_vm() family call and did + // not complete a call_vm() family call on the way here) + // + // One legitimate way for us to miss all the cleanup points is + // if we got here right after handling a compiled return. If that + // is the case, then we consider our return from compiled code to + // complete the ForceEarlyReturn request and we clear the condition. + clr_earlyret_pending(); + set_earlyret_oop(NULL); + clr_earlyret_value(); + } + + // clearing the flag indicates we are done with + // the ForceEarlyReturn() dance + clr_pending_step_for_earlyret(); + + // If step is pending for earlyret then it may not be a repeat step. + // The new_bci and method_id is same as current_bci and current + // method_id after earlyret and step for recursive calls. + // Force the step by clearing the last location. + JvmtiEnvThreadStateIterator it(this); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + ets->clear_current_location(); + } +} + +void JvmtiThreadState::oops_do(OopClosure* f) { + f->do_oop((oop*) &_earlyret_oop); +} diff --git a/hotspot/src/share/vm/prims/jvmtiThreadState.hpp b/hotspot/src/share/vm/prims/jvmtiThreadState.hpp new file mode 100644 index 00000000000..703d873397f --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiThreadState.hpp @@ -0,0 +1,396 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifndef _JAVA_JVMTITHREADSTATE_H_ +#define _JAVA_JVMTITHREADSTATE_H_ + +// +// Forward Declarations +// + +class JvmtiEnvBase; +class JvmtiEnvThreadState; +class JvmtiDynamicCodeEventCollector; + +enum JvmtiClassLoadKind { + jvmti_class_load_kind_load = 100, + jvmti_class_load_kind_retransform, + jvmti_class_load_kind_redefine +}; + +/////////////////////////////////////////////////////////////// +// +// class JvmtiEnvThreadStateIterator +// +// The only safe means of iterating through the JvmtiEnvThreadStates +// in a JvmtiThreadState. +// Note that this iteratation includes invalid environments pending +// deallocation -- in fact, some uses depend on this behavior. +// +class JvmtiEnvThreadStateIterator : public StackObj { + private: + JvmtiThreadState* state; + public: + JvmtiEnvThreadStateIterator(JvmtiThreadState* thread_state); + ~JvmtiEnvThreadStateIterator(); + JvmtiEnvThreadState* first(); + JvmtiEnvThreadState* next(JvmtiEnvThreadState* ets); +}; + + +/////////////////////////////////////////////////////////////// +// +// class JvmtiThreadState +// +// The Jvmti state for each thread (across all JvmtiEnv): +// 1. Local table of enabled events. +class JvmtiThreadState : public CHeapObj { + private: + friend class JvmtiEnv; + JavaThread *_thread; + bool _exception_detected; + bool _exception_caught; + bool _hide_single_stepping; + bool _pending_step_for_popframe; + bool _pending_step_for_earlyret; + int _hide_level; + + // Used to send class being redefined/retransformed and kind of transform + // info to the class file load hook event handler. + KlassHandle *_class_being_redefined; + JvmtiClassLoadKind _class_load_kind; + + // This is only valid when is_interp_only_mode() returns true + int _cur_stack_depth; + + JvmtiThreadEventEnable _thread_event_enable; + + // for support of JvmtiEnvThreadState + JvmtiEnvThreadState* _head_env_thread_state; + + // doubly-linked linear list of active thread state + // needed in order to iterate the list without holding Threads_lock + static JvmtiThreadState *_head; + JvmtiThreadState *_next; + JvmtiThreadState *_prev; + + // holds the current dynamic code event collector, NULL if no event collector in use + JvmtiDynamicCodeEventCollector* _dynamic_code_event_collector; + // holds the current vm object alloc event collector, NULL if no event collector in use + JvmtiVMObjectAllocEventCollector* _vm_object_alloc_event_collector; + + // Should only be created by factory methods + JvmtiThreadState(JavaThread *thread); + + friend class JvmtiEnvThreadStateIterator; + inline JvmtiEnvThreadState* head_env_thread_state(); + inline void set_head_env_thread_state(JvmtiEnvThreadState* ets); + + public: + ~JvmtiThreadState(); + + // is event_type enabled and usable for this thread in any enviroments? + bool is_enabled(jvmtiEvent event_type) { + return _thread_event_enable.is_enabled(event_type); + } + + JvmtiThreadEventEnable *thread_event_enable() { + return &_thread_event_enable; + } + + // Must only be called in situations where the state is for the current thread and + // the environment can not go away. To be safe, the returned JvmtiEnvThreadState + // must be used in such a way as there can be no intervening safepoints. + inline JvmtiEnvThreadState* env_thread_state(JvmtiEnvBase *env); + + static void periodic_clean_up(); + + void add_env(JvmtiEnvBase *env); + + // Used by the interpreter for fullspeed debugging support + bool is_interp_only_mode() { return _thread->is_interp_only_mode(); } + void enter_interp_only_mode(); + void leave_interp_only_mode(); + + // access to the linked list of all JVMTI thread states + static JvmtiThreadState *first() { + assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check"); + return _head; + } + + JvmtiThreadState *next() { + return _next; + } + + // Current stack depth is only valid when is_interp_only_mode() returns true. + // These functions should only be called at a safepoint - usually called from same thread. + // Returns the number of Java activations on the stack. + int cur_stack_depth(); + void invalidate_cur_stack_depth(); + void incr_cur_stack_depth(); + void decr_cur_stack_depth(); + + int count_frames(); + + inline JavaThread *get_thread() { return _thread; } + inline bool is_exception_detected() { return _exception_detected; } + inline bool is_exception_caught() { return _exception_caught; } + inline void set_exception_detected() { _exception_detected = true; + _exception_caught = false; } + inline void set_exception_caught() { _exception_caught = true; + _exception_detected = false; } + + inline void clear_hide_single_stepping() { + if (_hide_level > 0) { + _hide_level--; + } else { + assert(_hide_single_stepping, "hide_single_stepping is out of phase"); + _hide_single_stepping = false; + } + } + inline bool hide_single_stepping() { return _hide_single_stepping; } + inline void set_hide_single_stepping() { + if (_hide_single_stepping) { + _hide_level++; + } else { + assert(_hide_level == 0, "hide_level is out of phase"); + _hide_single_stepping = true; + } + } + + // Step pending flag is set when PopFrame is called and it is cleared + // when step for the Pop Frame is completed. + // This logic is used to distinguish b/w step for pop frame and repeat step. + void set_pending_step_for_popframe() { _pending_step_for_popframe = true; } + void clr_pending_step_for_popframe() { _pending_step_for_popframe = false; } + bool is_pending_step_for_popframe() { return _pending_step_for_popframe; } + void process_pending_step_for_popframe(); + + // Step pending flag is set when ForceEarlyReturn is called and it is cleared + // when step for the ForceEarlyReturn is completed. + // This logic is used to distinguish b/w step for early return and repeat step. + void set_pending_step_for_earlyret() { _pending_step_for_earlyret = true; } + void clr_pending_step_for_earlyret() { _pending_step_for_earlyret = false; } + bool is_pending_step_for_earlyret() { return _pending_step_for_earlyret; } + void process_pending_step_for_earlyret(); + + // Setter and getter method is used to send redefined class info + // when class file load hook event is posted. + // It is set while loading redefined class and cleared before the + // class file load hook event is posted. + inline void set_class_being_redefined(KlassHandle *h_class, JvmtiClassLoadKind kind) { + _class_being_redefined = h_class; + _class_load_kind = kind; + } + + inline void clear_class_being_redefined() { + _class_being_redefined = NULL; + _class_load_kind = jvmti_class_load_kind_load; + } + + inline KlassHandle *get_class_being_redefined() { + return _class_being_redefined; + } + + inline JvmtiClassLoadKind get_class_load_kind() { + return _class_load_kind; + } + + // RedefineClasses support + // The bug 6214132 caused the verification to fail. + // + // Below is the detailed description of the fix approach taken: + // 1. What's done in RedefineClasses() before verification: + // a) A reference to the class being redefined (_the_class) and a + // reference to new version of the class (_scratch_class) are + // saved here for use during the bytecode verification phase of + // RedefineClasses. See RedefineVerifyMark for how these fields + // are managed. + // b) The _java_mirror field from _the_class is copied to the + // _java_mirror field in _scratch_class. This means that a jclass + // returned for _the_class or _scratch_class will refer to the + // same Java mirror. The verifier will see the "one true mirror" + // for the class being verified. + // 2. What is done at verification: + // When the verifier makes calls into the VM to ask questions about + // the class being verified, it will pass the jclass to JVM_* functions. + // The jclass is always pointing to the mirror of _the_class. + // ~28 JVM_* functions called by the verifier for the information + // about CP entries and klass structure should check the jvmtiThreadState + // info about equivalent klass versions and use it to replace a klassOop + // of _the_class with a klassOop of _scratch_class. The function + // class_to_verify_considering_redefinition() must be called for it. + // + // Note again, that this redirection happens only for the verifier thread. + // Other threads have very small overhead by checking the existence + // of the jvmtiThreadSate and the information about klasses equivalence. + // No JNI functions need to be changed, they don't reference the klass guts. + // The JavaThread pointer is already available in all JVM_* functions + // used by the verifier, so there is no extra performance issue with it. + + private: + KlassHandle *_the_class_for_redefinition_verification; + KlassHandle *_scratch_class_for_redefinition_verification; + + public: + inline void set_class_versions_map(KlassHandle *the_class, + KlassHandle *scratch_class) { + _the_class_for_redefinition_verification = the_class; + _scratch_class_for_redefinition_verification = scratch_class; + } + + inline void clear_class_versions_map() { set_class_versions_map(NULL, NULL); } + + static inline + klassOop class_to_verify_considering_redefinition(klassOop klass, + JavaThread *thread) { + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state != NULL && state->_the_class_for_redefinition_verification != NULL) { + if ((*(state->_the_class_for_redefinition_verification))() == klass) { + klass = (*(state->_scratch_class_for_redefinition_verification))(); + } + } + return klass; + } + + // Todo: get rid of this! + private: + bool _debuggable; + public: + // Should the thread be enumerated by jvmtiInternal::GetAllThreads? + bool is_debuggable() { return _debuggable; } + // If a thread cannot be suspended (has no valid last_java_frame) then it gets marked !debuggable + void set_debuggable(bool debuggable) { _debuggable = debuggable; } + + public: + + bool may_be_walked(); + + // Thread local event collector setter and getter methods. + JvmtiDynamicCodeEventCollector* get_dynamic_code_event_collector() { + return _dynamic_code_event_collector; + } + JvmtiVMObjectAllocEventCollector* get_vm_object_alloc_event_collector() { + return _vm_object_alloc_event_collector; + } + void set_dynamic_code_event_collector(JvmtiDynamicCodeEventCollector* collector) { + _dynamic_code_event_collector = collector; + } + void set_vm_object_alloc_event_collector(JvmtiVMObjectAllocEventCollector* collector) { + _vm_object_alloc_event_collector = collector; + } + + + // + // Frame routines + // + + public: + + // true when the thread was suspended with a pointer to the last Java frame. + bool has_last_frame() { return _thread->has_last_Java_frame(); } + + void update_for_pop_top_frame(); + + // already holding JvmtiThreadState_lock - retrieve or create JvmtiThreadState + inline static JvmtiThreadState *state_for_while_locked(JavaThread *thread) { + assert(JvmtiThreadState_lock->is_locked(), "sanity check"); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + state = new JvmtiThreadState(thread); + } + return state; + } + + // retrieve or create JvmtiThreadState + inline static JvmtiThreadState *state_for(JavaThread *thread) { + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + MutexLocker mu(JvmtiThreadState_lock); + // check again with the lock held + state = state_for_while_locked(thread); + } else { + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } + return state; + } + + // JVMTI ForceEarlyReturn support + + // This is set to earlyret_pending to signal that top Java frame + // should be returned immediately + public: + int _earlyret_state; + TosState _earlyret_tos; + jvalue _earlyret_value; + oop _earlyret_oop; // Used to return an oop result into Java code from + // ForceEarlyReturnObject, GC-preserved + + // Setting and clearing earlyret_state + // earlyret_pending indicates that a ForceEarlyReturn() has been + // requested and not yet been completed. + public: + enum EarlyretState { + earlyret_inactive = 0, + earlyret_pending = 1 + }; + + void set_earlyret_pending(void) { _earlyret_state = earlyret_pending; } + void clr_earlyret_pending(void) { _earlyret_state = earlyret_inactive; } + bool is_earlyret_pending(void) { return (_earlyret_state == earlyret_pending); } + + TosState earlyret_tos() { return _earlyret_tos; } + oop earlyret_oop() const { return _earlyret_oop; } + void set_earlyret_oop (oop x) { _earlyret_oop = x; } + jvalue earlyret_value() { return _earlyret_value; } + void set_earlyret_value(jvalue val, TosState tos) { _earlyret_tos = tos; _earlyret_value = val; } + void clr_earlyret_value() { _earlyret_tos = ilgl; _earlyret_value.j = 0L; } + + static ByteSize earlyret_state_offset() { return byte_offset_of(JvmtiThreadState, _earlyret_state); } + static ByteSize earlyret_tos_offset() { return byte_offset_of(JvmtiThreadState, _earlyret_tos); } + static ByteSize earlyret_oop_offset() { return byte_offset_of(JvmtiThreadState, _earlyret_oop); } + static ByteSize earlyret_value_offset() { return byte_offset_of(JvmtiThreadState, _earlyret_value); } + + void oops_do(OopClosure* f); // GC support +}; + +class RedefineVerifyMark : public StackObj { + private: + JvmtiThreadState *_state; + + public: + RedefineVerifyMark(KlassHandle *the_class, KlassHandle *scratch_class, + JvmtiThreadState *state) : _state(state) + { + _state->set_class_versions_map(the_class, scratch_class); + (*scratch_class)->set_java_mirror((*the_class)->java_mirror()); + } + + ~RedefineVerifyMark() { + _state->clear_class_versions_map(); + } +}; + +#endif /* _JAVA_JVMTITHREADSTATE_H_ */ diff --git a/hotspot/src/share/vm/prims/jvmtiThreadState.inline.hpp b/hotspot/src/share/vm/prims/jvmtiThreadState.inline.hpp new file mode 100644 index 00000000000..9cda8466201 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiThreadState.inline.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// JvmtiEnvThreadStateIterator implementation + +inline JvmtiEnvThreadStateIterator::JvmtiEnvThreadStateIterator(JvmtiThreadState* thread_state) { + state = thread_state; + Thread::current()->entering_jvmti_env_iteration(); +} + +inline JvmtiEnvThreadStateIterator::~JvmtiEnvThreadStateIterator() { + Thread::current()->leaving_jvmti_env_iteration(); +} + +inline JvmtiEnvThreadState* JvmtiEnvThreadStateIterator::first() { + return state->head_env_thread_state(); +} + +inline JvmtiEnvThreadState* JvmtiEnvThreadStateIterator::next(JvmtiEnvThreadState* ets) { + return ets->next(); +} + +// JvmtiThreadState implementation + +JvmtiEnvThreadState* JvmtiThreadState::env_thread_state(JvmtiEnvBase *env) { + JvmtiEnvThreadStateIterator it(this); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if ((JvmtiEnvBase*)(ets->get_env()) == env) { + return ets; + } + } + return NULL; +} + +JvmtiEnvThreadState* JvmtiThreadState::head_env_thread_state() { + return _head_env_thread_state; +} + +void JvmtiThreadState::set_head_env_thread_state(JvmtiEnvThreadState* ets) { + _head_env_thread_state = ets; +} diff --git a/hotspot/src/share/vm/prims/jvmtiTrace.cpp b/hotspot/src/share/vm/prims/jvmtiTrace.cpp new file mode 100644 index 00000000000..2fff2cfc2da --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiTrace.cpp @@ -0,0 +1,297 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiTrace.cpp.incl" + +// +// class JvmtiTrace +// +// Support for JVMTI tracing code +// +// ------------ +// Usage: +// -XX:TraceJVMTI=DESC,DESC,DESC +// +// DESC is DOMAIN ACTION KIND +// +// DOMAIN is function name +// event name +// "all" (all functions and events) +// "func" (all functions except boring) +// "allfunc" (all functions) +// "event" (all events) +// "ec" (event controller) +// +// ACTION is "+" (add) +// "-" (remove) +// +// KIND is +// for func +// "i" (input params) +// "e" (error returns) +// "o" (output) +// for event +// "t" (event triggered aka posted) +// "s" (event sent) +// +// Example: +// -XX:TraceJVMTI=ec+,GetCallerFrame+ie,Breakpoint+s + +#ifdef JVMTI_TRACE + +bool JvmtiTrace::_initialized = false; +bool JvmtiTrace::_on = false; +bool JvmtiTrace::_trace_event_controller = false; + +void JvmtiTrace::initialize() { + if (_initialized) { + return; + } + SafeResourceMark rm; + + const char *very_end; + const char *curr; + if (strlen(TraceJVMTI)) { + curr = TraceJVMTI; + } else { + curr = ""; // hack in fixed tracing here + } + very_end = curr + strlen(curr); + while (curr < very_end) { + const char *curr_end = strchr(curr, ','); + if (curr_end == NULL) { + curr_end = very_end; + } + const char *op_pos = strchr(curr, '+'); + const char *minus_pos = strchr(curr, '-'); + if (minus_pos != NULL && (minus_pos < op_pos || op_pos == NULL)) { + op_pos = minus_pos; + } + char op; + const char *flags = op_pos + 1; + const char *flags_end = curr_end; + if (op_pos == NULL || op_pos > curr_end) { + flags = "ies"; + flags_end = flags + strlen(flags); + op_pos = curr_end; + op = '+'; + } else { + op = *op_pos; + } + jbyte bits = 0; + for (; flags < flags_end; ++flags) { + switch (*flags) { + case 'i': + bits |= SHOW_IN; + break; + case 'I': + bits |= SHOW_IN_DETAIL; + break; + case 'e': + bits |= SHOW_ERROR; + break; + case 'o': + bits |= SHOW_OUT; + break; + case 'O': + bits |= SHOW_OUT_DETAIL; + break; + case 't': + bits |= SHOW_EVENT_TRIGGER; + break; + case 's': + bits |= SHOW_EVENT_SENT; + break; + default: + tty->print_cr("Invalid trace flag '%c'", *flags); + break; + } + } + const int FUNC = 1; + const int EXCLUDE = 2; + const int ALL_FUNC = 4; + const int EVENT = 8; + const int ALL_EVENT = 16; + int domain = 0; + size_t len = op_pos - curr; + if (op_pos == curr) { + domain = ALL_FUNC | FUNC | ALL_EVENT | EVENT | EXCLUDE; + } else if (len==3 && strncmp(curr, "all", 3)==0) { + domain = ALL_FUNC | FUNC | ALL_EVENT | EVENT; + } else if (len==7 && strncmp(curr, "allfunc", 7)==0) { + domain = ALL_FUNC | FUNC; + } else if (len==4 && strncmp(curr, "func", 4)==0) { + domain = ALL_FUNC | FUNC | EXCLUDE; + } else if (len==8 && strncmp(curr, "allevent", 8)==0) { + domain = ALL_EVENT | EVENT; + } else if (len==5 && strncmp(curr, "event", 5)==0) { + domain = ALL_EVENT | EVENT; + } else if (len==2 && strncmp(curr, "ec", 2)==0) { + _trace_event_controller = true; + tty->print_cr("JVMTI Tracing the event controller"); + } else { + domain = FUNC | EVENT; // go searching + } + + int exclude_index = 0; + if (domain & FUNC) { + if (domain & ALL_FUNC) { + if (domain & EXCLUDE) { + tty->print("JVMTI Tracing all significant functions"); + } else { + tty->print_cr("JVMTI Tracing all functions"); + } + } + for (int i = 0; i <= _max_function_index; ++i) { + if (domain & EXCLUDE && i == _exclude_functions[exclude_index]) { + ++exclude_index; + } else { + bool do_op = false; + if (domain & ALL_FUNC) { + do_op = true; + } else { + const char *fname = function_name(i); + if (fname != NULL) { + size_t fnlen = strlen(fname); + if (len==fnlen && strncmp(curr, fname, fnlen)==0) { + tty->print_cr("JVMTI Tracing the function: %s", fname); + do_op = true; + } + } + } + if (do_op) { + if (op == '+') { + _trace_flags[i] |= bits; + } else { + _trace_flags[i] &= ~bits; + } + _on = true; + } + } + } + } + if (domain & EVENT) { + if (domain & ALL_EVENT) { + tty->print_cr("JVMTI Tracing all events"); + } + for (int i = 0; i <= _max_event_index; ++i) { + bool do_op = false; + if (domain & ALL_EVENT) { + do_op = true; + } else { + const char *ename = event_name(i); + if (ename != NULL) { + size_t evtlen = strlen(ename); + if (len==evtlen && strncmp(curr, ename, evtlen)==0) { + tty->print_cr("JVMTI Tracing the event: %s", ename); + do_op = true; + } + } + } + if (do_op) { + if (op == '+') { + _event_trace_flags[i] |= bits; + } else { + _event_trace_flags[i] &= ~bits; + } + _on = true; + } + } + } + if (!_on && (domain & (FUNC|EVENT))) { + tty->print_cr("JVMTI Trace domain not found"); + } + curr = curr_end + 1; + } + _initialized = true; +} + + +void JvmtiTrace::shutdown() { + int i; + _on = false; + _trace_event_controller = false; + for (i = 0; i <= _max_function_index; ++i) { + _trace_flags[i] = 0; + } + for (i = 0; i <= _max_event_index; ++i) { + _event_trace_flags[i] = 0; + } +} + + +const char* JvmtiTrace::enum_name(const char** names, const jint* values, jint value) { + for (int index = 0; names[index] != 0; ++index) { + if (values[index] == value) { + return names[index]; + } + } + return "*INVALID-ENUM-VALUE*"; +} + + +// return a valid string no matter what state the thread is in +const char *JvmtiTrace::safe_get_thread_name(Thread *thread) { + if (thread == NULL) { + return "NULL"; + } + if (!thread->is_Java_thread()) { + return thread->name(); + } + JavaThread *java_thread = (JavaThread *)thread; + oop threadObj = java_thread->threadObj(); + if (threadObj == NULL) { + return "NULL"; + } + typeArrayOop name = java_lang_Thread::name(threadObj); + if (name == NULL) { + return ""; + } + return UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); +} + + +// return the name of the current thread +const char *JvmtiTrace::safe_get_current_thread_name() { + if (JvmtiEnv::is_vm_live()) { + return JvmtiTrace::safe_get_thread_name(Thread::current()); + } else { + return "VM not live"; + } +} + +// return a valid string no matter what the state of k_mirror +const char * JvmtiTrace::get_class_name(oop k_mirror) { + if (java_lang_Class::is_primitive(k_mirror)) { + return "primitive"; + } + klassOop k_oop = java_lang_Class::as_klassOop(k_mirror); + if (k_oop == NULL) { + return "INVALID"; + } + return Klass::cast(k_oop)->external_name(); +} + +#endif /*JVMTI_TRACE */ diff --git a/hotspot/src/share/vm/prims/jvmtiTrace.hpp b/hotspot/src/share/vm/prims/jvmtiTrace.hpp new file mode 100644 index 00000000000..22544a63c8a --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiTrace.hpp @@ -0,0 +1,98 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/////////////////////////////////////////////////////////////// +// +// class JvmtiTrace +// +// Support for JVMTI tracing code +// + +// Support tracing except in product build on the client compiler +#ifndef PRODUCT +#define JVMTI_TRACE 1 +#else +#ifdef COMPILER2 +#define JVMTI_TRACE 1 +#endif +#endif + +#ifdef JVMTI_TRACE + +class JvmtiTrace : AllStatic { + + static bool _initialized; + static bool _on; + static bool _trace_event_controller; + static jbyte _trace_flags[]; + static jbyte _event_trace_flags[]; + static const char* _event_names[]; + static jint _max_function_index; + static jint _max_event_index; + static short _exclude_functions[]; + static const char* _function_names[]; + +public: + + enum { + SHOW_IN = 01, + SHOW_OUT = 02, + SHOW_ERROR = 04, + SHOW_IN_DETAIL = 010, + SHOW_OUT_DETAIL = 020, + SHOW_EVENT_TRIGGER = 040, + SHOW_EVENT_SENT = 0100 + }; + + static bool tracing() { return _on; } + static bool trace_event_controller() { return _trace_event_controller; } + static jbyte trace_flags(int num) { return _trace_flags[num]; } + static jbyte event_trace_flags(int num) { return _event_trace_flags[num]; } + static const char* function_name(int num) { return _function_names[num]; } // To Do: add range checking + + static const char* event_name(int num) { + static char* ext_event_name = (char*)"(extension event)"; + if (num >= JVMTI_MIN_EVENT_TYPE_VAL && num <= JVMTI_MAX_EVENT_TYPE_VAL) { + return _event_names[num]; + } else { + return ext_event_name; + } + } + + static const char* enum_name(const char** names, const jint* values, jint value); + + static void initialize(); + static void shutdown(); + + // return a valid string no matter what state the thread is in + static const char *safe_get_thread_name(Thread *thread); + + // return the name of the current thread + static const char *safe_get_current_thread_name(); + + // return a valid string no matter what the state of k_mirror + static const char *get_class_name(oop k_mirror); +}; + +#endif /*JVMTI_TRACE */ diff --git a/hotspot/src/share/vm/prims/jvmtiUtil.cpp b/hotspot/src/share/vm/prims/jvmtiUtil.cpp new file mode 100644 index 00000000000..a472c9eb348 --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiUtil.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jvmtiUtil.cpp.incl" +// +// class JvmtiUtil +// + +ResourceArea* JvmtiUtil::_single_threaded_resource_area = NULL; + +ResourceArea* JvmtiUtil::single_threaded_resource_area() { + if (_single_threaded_resource_area == NULL) { + // lazily create the single threaded resource area + // pick a size which is not a standard since the pools don't exist yet + _single_threaded_resource_area = new ResourceArea(Chunk::non_pool_size); + } + return _single_threaded_resource_area; +} diff --git a/hotspot/src/share/vm/prims/jvmtiUtil.hpp b/hotspot/src/share/vm/prims/jvmtiUtil.hpp new file mode 100644 index 00000000000..046816e5fef --- /dev/null +++ b/hotspot/src/share/vm/prims/jvmtiUtil.hpp @@ -0,0 +1,86 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/////////////////////////////////////////////////////////////// +// +// class JvmtiUtil +// +// class for miscellaneous jvmti utility static methods +// + +class JvmtiUtil : AllStatic { + + static ResourceArea* _single_threaded_resource_area; + + static const char* _error_names[]; + static const bool _event_threaded[]; + +public: + + static ResourceArea* single_threaded_resource_area(); + + static const char* error_name(int num) { return _error_names[num]; } // To Do: add range checking + + static const bool has_event_capability(jvmtiEvent event_type, const jvmtiCapabilities* capabilities_ptr); + + static const bool event_threaded(int num) { + if (num >= JVMTI_MIN_EVENT_TYPE_VAL && num <= JVMTI_MAX_EVENT_TYPE_VAL) { + return _event_threaded[num]; + } + if (num >= EXT_MIN_EVENT_TYPE_VAL && num <= EXT_MAX_EVENT_TYPE_VAL) { + return false; + } + ShouldNotReachHere(); + return false; + } +}; + + +/////////////////////////////////////////////////////////////// +// +// class SafeResourceMark +// +// ResourceMarks that work before threads exist +// + +class SafeResourceMark : public ResourceMark { + + ResourceArea* safe_resource_area() { + Thread* thread; + + if (Threads::number_of_threads() == 0) { + return JvmtiUtil::single_threaded_resource_area(); + } + thread = ThreadLocalStorage::thread(); + if (thread == NULL) { + return JvmtiUtil::single_threaded_resource_area(); + } + return thread->resource_area(); + } + + public: + + SafeResourceMark() : ResourceMark(safe_resource_area()) {} + +}; diff --git a/hotspot/src/share/vm/prims/methodComparator.cpp b/hotspot/src/share/vm/prims/methodComparator.cpp new file mode 100644 index 00000000000..ed7fc2269b4 --- /dev/null +++ b/hotspot/src/share/vm/prims/methodComparator.cpp @@ -0,0 +1,383 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_methodComparator.cpp.incl" + +BytecodeStream *MethodComparator::_s_old; +BytecodeStream *MethodComparator::_s_new; +constantPoolOop MethodComparator::_old_cp; +constantPoolOop MethodComparator::_new_cp; +BciMap *MethodComparator::_bci_map; +bool MethodComparator::_switchable_test; +GrowableArray *MethodComparator::_fwd_jmps; + +bool MethodComparator::methods_EMCP(methodOop old_method, methodOop new_method) { + if (old_method->code_size() != new_method->code_size()) + return false; + if (check_stack_and_locals_size(old_method, new_method) != 0) { + // RC_TRACE macro has an embedded ResourceMark + RC_TRACE(0x00800000, ("Methods %s non-comparable with diagnosis %d", + old_method->name()->as_C_string(), + check_stack_and_locals_size(old_method, new_method))); + return false; + } + + _old_cp = old_method->constants(); + _new_cp = new_method->constants(); + BytecodeStream s_old(old_method); + BytecodeStream s_new(new_method); + _s_old = &s_old; + _s_new = &s_new; + _switchable_test = false; + Bytecodes::Code c_old, c_new; + + while ((c_old = s_old.next()) >= 0) { + if ((c_new = s_new.next()) < 0 || c_old != c_new) + return false; + + if (! args_same(c_old, c_new)) + return false; + } + return true; +} + + +bool MethodComparator::methods_switchable(methodOop old_method, methodOop new_method, + BciMap &bci_map) { + if (old_method->code_size() > new_method->code_size()) + // Something has definitely been deleted in the new method, compared to the old one. + return false; + + if (! check_stack_and_locals_size(old_method, new_method)) + return false; + + _old_cp = old_method->constants(); + _new_cp = new_method->constants(); + BytecodeStream s_old(old_method); + BytecodeStream s_new(new_method); + _s_old = &s_old; + _s_new = &s_new; + _bci_map = &bci_map; + _switchable_test = true; + GrowableArray fwd_jmps(16); + _fwd_jmps = &fwd_jmps; + Bytecodes::Code c_old, c_new; + + while ((c_old = s_old.next()) >= 0) { + if ((c_new = s_new.next()) < 0) + return false; + if (! (c_old == c_new && args_same(c_old, c_new))) { + int old_bci = s_old.bci(); + int new_st_bci = s_new.bci(); + bool found_match = false; + do { + c_new = s_new.next(); + if (c_new == c_old && args_same(c_old, c_new)) { + found_match = true; + break; + } + } while (c_new >= 0); + if (! found_match) + return false; + int new_end_bci = s_new.bci(); + bci_map.store_fragment_location(old_bci, new_st_bci, new_end_bci); + } + } + + // Now we can test all forward jumps + for (int i = 0; i < fwd_jmps.length() / 2; i++) { + if (! bci_map.old_and_new_locations_same(fwd_jmps.at(i*2), fwd_jmps.at(i*2+1))) { + RC_TRACE(0x00800000, + ("Fwd jump miss: old dest = %d, calc new dest = %d, act new dest = %d", + fwd_jmps.at(i*2), bci_map.new_bci_for_old(fwd_jmps.at(i*2)), + fwd_jmps.at(i*2+1))); + return false; + } + } + + return true; +} + + +bool MethodComparator::args_same(Bytecodes::Code c_old, Bytecodes::Code c_new) { + // BytecodeStream returns the correct standard Java bytecodes for various "fast" + // bytecode versions, so we don't have to bother about them here.. + switch (c_old) { + case Bytecodes::_new : // fall through + case Bytecodes::_anewarray : // fall through + case Bytecodes::_multianewarray : // fall through + case Bytecodes::_checkcast : // fall through + case Bytecodes::_instanceof : { + u2 cpi_old = _s_old->get_index_big(); + u2 cpi_new = _s_new->get_index_big(); + if ((_old_cp->klass_at_noresolve(cpi_old) != _new_cp->klass_at_noresolve(cpi_new))) + return false; + if (c_old == Bytecodes::_multianewarray && + *(jbyte*)(_s_old->bcp() + 3) != *(jbyte*)(_s_new->bcp() + 3)) + return false; + break; + } + + case Bytecodes::_getstatic : // fall through + case Bytecodes::_putstatic : // fall through + case Bytecodes::_getfield : // fall through + case Bytecodes::_putfield : // fall through + case Bytecodes::_invokevirtual : // fall through + case Bytecodes::_invokespecial : // fall through + case Bytecodes::_invokestatic : // fall through + case Bytecodes::_invokeinterface : { + u2 cpci_old = _s_old->get_index_big(); + u2 cpci_new = _s_new->get_index_big(); + // Check if the names of classes, field/method names and signatures at these indexes + // are the same. Indices which are really into constantpool cache (rather than constant + // pool itself) are accepted by the constantpool query routines below. + if ((_old_cp->klass_ref_at_noresolve(cpci_old) != _new_cp->klass_ref_at_noresolve(cpci_new)) || + (_old_cp->name_ref_at(cpci_old) != _new_cp->name_ref_at(cpci_new)) || + (_old_cp->signature_ref_at(cpci_old) != _new_cp->signature_ref_at(cpci_new))) + return false; + break; + } + + case Bytecodes::_ldc : // fall through + case Bytecodes::_ldc_w : { + u2 cpi_old, cpi_new; + if (c_old == Bytecodes::_ldc) { + cpi_old = _s_old->bcp()[1]; + cpi_new = _s_new->bcp()[1]; + } else { + cpi_old = _s_old->get_index_big(); + cpi_new = _s_new->get_index_big(); + } + constantTag tag_old = _old_cp->tag_at(cpi_old); + constantTag tag_new = _new_cp->tag_at(cpi_new); + if (tag_old.is_int() || tag_old.is_float()) { + if (tag_old.value() != tag_new.value()) + return false; + if (tag_old.is_int()) { + if (_old_cp->int_at(cpi_old) != _new_cp->int_at(cpi_new)) + return false; + } else { + if (_old_cp->float_at(cpi_old) != _new_cp->float_at(cpi_new)) + return false; + } + } else if (tag_old.is_string() || tag_old.is_unresolved_string()) { + if (! (tag_new.is_unresolved_string() || tag_new.is_string())) + return false; + if (strcmp(_old_cp->string_at_noresolve(cpi_old), + _new_cp->string_at_noresolve(cpi_new)) != 0) + return false; + } else { // tag_old should be klass - 4881222 + if (! (tag_new.is_unresolved_klass() || tag_new.is_klass())) + return false; + if (_old_cp->klass_at_noresolve(cpi_old) != + _new_cp->klass_at_noresolve(cpi_new)) + return false; + } + break; + } + + case Bytecodes::_ldc2_w : { + u2 cpi_old = _s_old->get_index_big(); + u2 cpi_new = _s_new->get_index_big(); + constantTag tag_old = _old_cp->tag_at(cpi_old); + constantTag tag_new = _new_cp->tag_at(cpi_new); + if (tag_old.value() != tag_new.value()) + return false; + if (tag_old.is_long()) { + if (_old_cp->long_at(cpi_old) != _new_cp->long_at(cpi_new)) + return false; + } else { + if (_old_cp->double_at(cpi_old) != _new_cp->double_at(cpi_new)) + return false; + } + break; + } + + case Bytecodes::_bipush : + if (_s_old->bcp()[1] != _s_new->bcp()[1]) + return false; + break; + + case Bytecodes::_sipush : + if (_s_old->get_index_big() != _s_new->get_index_big()) + return false; + break; + + case Bytecodes::_aload : // fall through + case Bytecodes::_astore : // fall through + case Bytecodes::_dload : // fall through + case Bytecodes::_dstore : // fall through + case Bytecodes::_fload : // fall through + case Bytecodes::_fstore : // fall through + case Bytecodes::_iload : // fall through + case Bytecodes::_istore : // fall through + case Bytecodes::_lload : // fall through + case Bytecodes::_lstore : // fall through + case Bytecodes::_ret : + if (_s_old->is_wide() != _s_new->is_wide()) + return false; + if (_s_old->get_index() != _s_new->get_index()) + return false; + break; + + case Bytecodes::_goto : // fall through + case Bytecodes::_if_acmpeq : // fall through + case Bytecodes::_if_acmpne : // fall through + case Bytecodes::_if_icmpeq : // fall through + case Bytecodes::_if_icmpne : // fall through + case Bytecodes::_if_icmplt : // fall through + case Bytecodes::_if_icmpge : // fall through + case Bytecodes::_if_icmpgt : // fall through + case Bytecodes::_if_icmple : // fall through + case Bytecodes::_ifeq : // fall through + case Bytecodes::_ifne : // fall through + case Bytecodes::_iflt : // fall through + case Bytecodes::_ifge : // fall through + case Bytecodes::_ifgt : // fall through + case Bytecodes::_ifle : // fall through + case Bytecodes::_ifnonnull : // fall through + case Bytecodes::_ifnull : // fall through + case Bytecodes::_jsr : { + short old_ofs = (short) _s_old->get_index_big(); + short new_ofs = (short) _s_new->get_index_big(); + if (_switchable_test) { + int old_dest = _s_old->bci() + old_ofs; + int new_dest = _s_new->bci() + new_ofs; + if (old_ofs < 0 && new_ofs < 0) { + if (! _bci_map->old_and_new_locations_same(old_dest, new_dest)) + return false; + } else if (old_ofs > 0 && new_ofs > 0) { + _fwd_jmps->append(old_dest); + _fwd_jmps->append(new_dest); + } else { + return false; + } + } else { + if (old_ofs != new_ofs) + return false; + } + break; + } + + case Bytecodes::_iinc : + if (_s_old->is_wide() != _s_new->is_wide()) + return false; + if (! _s_old->is_wide()) { + if (_s_old->get_index_big() != _s_new->get_index_big()) + return false; + } else { + if (Bytes::get_Java_u4(_s_old->bcp() + 1) != Bytes::get_Java_u4(_s_new->bcp() + 1)) + return false; + } + break; + + case Bytecodes::_goto_w : // fall through + case Bytecodes::_jsr_w : { + int old_ofs = (int) Bytes::get_Java_u4(_s_old->bcp() + 1); + int new_ofs = (int) Bytes::get_Java_u4(_s_new->bcp() + 1); + if (_switchable_test) { + int old_dest = _s_old->bci() + old_ofs; + int new_dest = _s_new->bci() + new_ofs; + if (old_ofs < 0 && new_ofs < 0) { + if (! _bci_map->old_and_new_locations_same(old_dest, new_dest)) + return false; + } else if (old_ofs > 0 && new_ofs > 0) { + _fwd_jmps->append(old_dest); + _fwd_jmps->append(new_dest); + } else { + return false; + } + } else { + if (old_ofs != new_ofs) + return false; + } + break; + } + + case Bytecodes::_lookupswitch : // fall through + case Bytecodes::_tableswitch : { + if (_switchable_test) { + address aligned_bcp_old = (address) round_to((intptr_t)_s_old->bcp() + 1, jintSize); + address aligned_bcp_new = (address) round_to((intptr_t)_s_new->bcp() + 1, jintSize); + int default_old = (int) Bytes::get_Java_u4(aligned_bcp_old); + int default_new = (int) Bytes::get_Java_u4(aligned_bcp_new); + _fwd_jmps->append(_s_old->bci() + default_old); + _fwd_jmps->append(_s_new->bci() + default_new); + if (c_old == Bytecodes::_lookupswitch) { + int npairs_old = (int) Bytes::get_Java_u4(aligned_bcp_old + jintSize); + int npairs_new = (int) Bytes::get_Java_u4(aligned_bcp_new + jintSize); + if (npairs_old != npairs_new) + return false; + for (int i = 0; i < npairs_old; i++) { + int match_old = (int) Bytes::get_Java_u4(aligned_bcp_old + (2+2*i)*jintSize); + int match_new = (int) Bytes::get_Java_u4(aligned_bcp_new + (2+2*i)*jintSize); + if (match_old != match_new) + return false; + int ofs_old = (int) Bytes::get_Java_u4(aligned_bcp_old + (2+2*i+1)*jintSize); + int ofs_new = (int) Bytes::get_Java_u4(aligned_bcp_new + (2+2*i+1)*jintSize); + _fwd_jmps->append(_s_old->bci() + ofs_old); + _fwd_jmps->append(_s_new->bci() + ofs_new); + } + } else if (c_old == Bytecodes::_tableswitch) { + int lo_old = (int) Bytes::get_Java_u4(aligned_bcp_old + jintSize); + int lo_new = (int) Bytes::get_Java_u4(aligned_bcp_new + jintSize); + if (lo_old != lo_new) + return false; + int hi_old = (int) Bytes::get_Java_u4(aligned_bcp_old + 2*jintSize); + int hi_new = (int) Bytes::get_Java_u4(aligned_bcp_new + 2*jintSize); + if (hi_old != hi_new) + return false; + for (int i = 0; i < hi_old - lo_old + 1; i++) { + int ofs_old = (int) Bytes::get_Java_u4(aligned_bcp_old + (3+i)*jintSize); + int ofs_new = (int) Bytes::get_Java_u4(aligned_bcp_new + (3+i)*jintSize); + _fwd_jmps->append(_s_old->bci() + ofs_old); + _fwd_jmps->append(_s_new->bci() + ofs_new); + } + } + } else { // !_switchable_test, can use fast rough compare + int len_old = _s_old->next_bcp() - _s_old->bcp(); + int len_new = _s_new->next_bcp() - _s_new->bcp(); + if (len_old != len_new) + return false; + if (memcmp(_s_old->bcp(), _s_new->bcp(), len_old) != 0) + return false; + } + break; + } + } + + return true; +} + + +int MethodComparator::check_stack_and_locals_size(methodOop old_method, methodOop new_method) { + if (old_method->max_stack() != new_method->max_stack()) { + return 1; + } else if (old_method->max_locals() != new_method->max_locals()) { + return 2; + } else if (old_method->size_of_parameters() != new_method->size_of_parameters()) { + return 3; + } else return 0; +} diff --git a/hotspot/src/share/vm/prims/methodComparator.hpp b/hotspot/src/share/vm/prims/methodComparator.hpp new file mode 100644 index 00000000000..afaf696bea3 --- /dev/null +++ b/hotspot/src/share/vm/prims/methodComparator.hpp @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class BciMap; + +// methodComparator provides an interface for determining if methods of +// different versions of classes are equivalent or switchable + +class MethodComparator { + private: + static BytecodeStream *_s_old, *_s_new; + static constantPoolOop _old_cp, _new_cp; + static BciMap *_bci_map; + static bool _switchable_test; + static GrowableArray *_fwd_jmps; + + static bool args_same(Bytecodes::Code c_old, Bytecodes::Code c_new); + static int check_stack_and_locals_size(methodOop old_method, methodOop new_method); + + public: + // Check if the new method is equivalent to the old one modulo constant pool (EMCP). + // Intuitive definition: two versions of the same method are EMCP, if they don't differ + // on the source code level. Practically, we check whether the only difference between + // method versions is some constantpool indices embedded into the bytecodes, and whether + // these indices eventually point to the same constants for both method versions. + static bool methods_EMCP(methodOop old_method, methodOop new_method); + + static bool methods_switchable(methodOop old_method, methodOop new_method, BciMap &bci_map); +}; + + +// ByteCode Index Map. For two versions of the same method, where the new version may contain +// fragments not found in the old version, provides a mapping from an index of a bytecode in +// the old method to the index of the same bytecode in the new method. + +class BciMap { + private: + int *_old_bci, *_new_st_bci, *_new_end_bci; + int _cur_size, _cur_pos; + int _pos; + + public: + BciMap() { + _cur_size = 50; + _old_bci = (int*) malloc(sizeof(int) * _cur_size); + _new_st_bci = (int*) malloc(sizeof(int) * _cur_size); + _new_end_bci = (int*) malloc(sizeof(int) * _cur_size); + _cur_pos = 0; + } + + ~BciMap() { + free(_old_bci); + free(_new_st_bci); + free(_new_end_bci); + } + + // Store the position of an added fragment, e.g. + // + // |<- old_bci + // ----------------------------------------- + // Old method |invokevirtual 5|aload 1|... + // ----------------------------------------- + // + // |<- new_st_bci |<- new_end_bci + // -------------------------------------------------------------------- + // New method |invokevirual 5|aload 2|invokevirtual 6|aload 1|... + // -------------------------------------------------------------------- + // ^^^^^^^^^^^^^^^^^^^^^^^^ + // Added fragment + + void store_fragment_location(int old_bci, int new_st_bci, int new_end_bci) { + if (_cur_pos == _cur_size) { + _cur_size += 10; + _old_bci = (int*) realloc(_old_bci, sizeof(int) * _cur_size); + _new_st_bci = (int*) realloc(_new_st_bci, sizeof(int) * _cur_size); + _new_end_bci = (int*) realloc(_new_end_bci, sizeof(int) * _cur_size); + } + _old_bci[_cur_pos] = old_bci; + _new_st_bci[_cur_pos] = new_st_bci; + _new_end_bci[_cur_pos] = new_end_bci; + _cur_pos++; + } + + int new_bci_for_old(int old_bci) { + if (_cur_pos == 0 || old_bci < _old_bci[0]) return old_bci; + _pos = 1; + while (_pos < _cur_pos && old_bci >= _old_bci[_pos]) + _pos++; + return _new_end_bci[_pos-1] + (old_bci - _old_bci[_pos-1]); + } + + // Test if two indexes - one in the old method and another in the new one - correspond + // to the same bytecode + bool old_and_new_locations_same(int old_dest_bci, int new_dest_bci) { + if (new_bci_for_old(old_dest_bci) == new_dest_bci) + return true; + else if (_old_bci[_pos-1] == old_dest_bci) + return (new_dest_bci == _new_st_bci[_pos-1]); + else return false; + } +}; diff --git a/hotspot/src/share/vm/prims/nativeLookup.cpp b/hotspot/src/share/vm/prims/nativeLookup.cpp new file mode 100644 index 00000000000..0aac9a7b789 --- /dev/null +++ b/hotspot/src/share/vm/prims/nativeLookup.cpp @@ -0,0 +1,290 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_nativeLookup.cpp.incl" + + +static void mangle_name_on(outputStream* st, symbolOop name, int begin, int end) { + char* bytes = (char*)name->bytes() + begin; + char* end_bytes = (char*)name->bytes() + end; + while (bytes < end_bytes) { + jchar c; + bytes = UTF8::next(bytes, &c); + if (c <= 0x7f && isalnum(c)) { + st->put((char) c); + } else { + if (c == '_') st->print("_1"); + else if (c == '/') st->print("_"); + else if (c == ';') st->print("_2"); + else if (c == '[') st->print("_3"); + else st->print("_%.5x", c); + } + } +} + + +static void mangle_name_on(outputStream* st, symbolOop name) { + mangle_name_on(st, name, 0, name->utf8_length()); +} + + +char* NativeLookup::pure_jni_name(methodHandle method) { + stringStream st; + // Prefix + st.print("Java_"); + // Klass name + mangle_name_on(&st, method->klass_name()); + st.print("_"); + // Method name + mangle_name_on(&st, method->name()); + return st.as_string(); +} + + +char* NativeLookup::long_jni_name(methodHandle method) { + // Signature ignore the wrapping parenteses and the trailing return type + stringStream st; + symbolOop signature = method->signature(); + st.print("__"); + // find ')' + int end; + for (end = 0; end < signature->utf8_length() && signature->byte_at(end) != ')'; end++); + // skip first '(' + mangle_name_on(&st, signature, 1, end); + return st.as_string(); +} + +extern "C" { + void JNICALL JVM_RegisterUnsafeMethods(JNIEnv *env, jclass unsafecls); + void JNICALL JVM_RegisterPerfMethods(JNIEnv *env, jclass perfclass); +} + +static address lookup_special_native(char* jni_name) { + // NB: To ignore the jni prefix and jni postfix strstr is used matching. + if (!JDK_Version::is_gte_jdk14x_version()) { + // These functions only exist for compatibility with 1.3.1 and earlier + // Intercept ObjectOutputStream getPrimitiveFieldValues for faster serialization + if (strstr(jni_name, "Java_java_io_ObjectOutputStream_getPrimitiveFieldValues") != NULL) { + return CAST_FROM_FN_PTR(address, JVM_GetPrimitiveFieldValues); + } + // Intercept ObjectInputStream setPrimitiveFieldValues for faster serialization + if (strstr(jni_name, "Java_java_io_ObjectInputStream_setPrimitiveFieldValues") != NULL) { + return CAST_FROM_FN_PTR(address, JVM_SetPrimitiveFieldValues); + } + } + if (strstr(jni_name, "Java_sun_misc_Unsafe_registerNatives") != NULL) { + return CAST_FROM_FN_PTR(address, JVM_RegisterUnsafeMethods); + } + if (strstr(jni_name, "Java_sun_misc_Perf_registerNatives") != NULL) { + return CAST_FROM_FN_PTR(address, JVM_RegisterPerfMethods); + } + + return NULL; +} + +address NativeLookup::lookup_style(methodHandle method, char* pure_name, const char* long_name, int args_size, bool os_style, bool& in_base_library, TRAPS) { + address entry; + // Compute complete JNI name for style + stringStream st; + if (os_style) os::print_jni_name_prefix_on(&st, args_size); + st.print_raw(pure_name); + st.print_raw(long_name); + if (os_style) os::print_jni_name_suffix_on(&st, args_size); + char* jni_name = st.as_string(); + + // If the loader is null we have a system class, so we attempt a lookup in + // the native Java library. This takes care of any bootstrapping problems. + // Note: It is critical for bootstrapping that Java_java_lang_ClassLoader_00024NativeLibrary_find + // gets found the first time around - otherwise an infinite loop can occure. This is + // another VM/library dependency + Handle loader(THREAD, + instanceKlass::cast(method->method_holder())->class_loader()); + if (loader.is_null()) { + entry = lookup_special_native(jni_name); + if (entry == NULL) { + entry = (address) hpi::dll_lookup(os::native_java_library(), jni_name); + } + if (entry != NULL) { + in_base_library = true; + return entry; + } + } + + // Otherwise call static method findNative in ClassLoader + KlassHandle klass (THREAD, SystemDictionary::classloader_klass()); + Handle name_arg = java_lang_String::create_from_str(jni_name, CHECK_NULL); + + JavaValue result(T_LONG); + JavaCalls::call_static(&result, + klass, + vmSymbolHandles::findNative_name(), + vmSymbolHandles::classloader_string_long_signature(), + // Arguments + loader, + name_arg, + CHECK_NULL); + entry = (address) (intptr_t) result.get_jlong(); + + if (entry == NULL) { + // findNative didn't find it, if there are any agent libraries look in them + AgentLibrary* agent; + for (agent = Arguments::agents(); agent != NULL; agent = agent->next()) { + entry = (address) hpi::dll_lookup(agent->os_lib(), jni_name); + if (entry != NULL) { + return entry; + } + } + } + + return entry; +} + + +// Check all the formats of native implementation name to see if there is one +// for the specified method. +address NativeLookup::lookup_entry(methodHandle method, bool& in_base_library, TRAPS) { + address entry = NULL; + in_base_library = false; + // Compute pure name + char* pure_name = pure_jni_name(method); + + // Compute argument size + int args_size = 1 // JNIEnv + + (method->is_static() ? 1 : 0) // class for static methods + + method->size_of_parameters(); // actual parameters + + + // 1) Try JNI short style + entry = lookup_style(method, pure_name, "", args_size, true, in_base_library, CHECK_NULL); + if (entry != NULL) return entry; + + // Compute long name + char* long_name = long_jni_name(method); + + // 2) Try JNI long style + entry = lookup_style(method, pure_name, long_name, args_size, true, in_base_library, CHECK_NULL); + if (entry != NULL) return entry; + + // 3) Try JNI short style without os prefix/suffix + entry = lookup_style(method, pure_name, "", args_size, false, in_base_library, CHECK_NULL); + if (entry != NULL) return entry; + + // 4) Try JNI long style without os prefix/suffix + entry = lookup_style(method, pure_name, long_name, args_size, false, in_base_library, CHECK_NULL); + + return entry; // NULL indicates not found +} + +// Check if there are any JVM TI prefixes which have been applied to the native method name. +// If any are found, remove them before attemping the look up of the +// native implementation again. +// See SetNativeMethodPrefix in the JVM TI Spec for more details. +address NativeLookup::lookup_entry_prefixed(methodHandle method, bool& in_base_library, TRAPS) { + ResourceMark rm(THREAD); + + int prefix_count; + char** prefixes = JvmtiExport::get_all_native_method_prefixes(&prefix_count); + char* in_name = method->name()->as_C_string(); + char* wrapper_name = in_name; + // last applied prefix will be first -- go backwards + for (int i = prefix_count-1; i >= 0; i--) { + char* prefix = prefixes[i]; + size_t prefix_len = strlen(prefix); + if (strncmp(prefix, wrapper_name, prefix_len) == 0) { + // has this prefix remove it + wrapper_name += prefix_len; + } + } + if (wrapper_name != in_name) { + // we have a name for a wrapping method + int wrapper_name_len = (int)strlen(wrapper_name); + symbolHandle wrapper_symbol(THREAD, SymbolTable::probe(wrapper_name, wrapper_name_len)); + if (!wrapper_symbol.is_null()) { + KlassHandle kh(method->method_holder()); + methodOop wrapper_method = Klass::cast(kh())->lookup_method(wrapper_symbol(), + method->signature()); + if (wrapper_method != NULL && !wrapper_method->is_native()) { + // we found a wrapper method, use its native entry + method->set_is_prefixed_native(); + return lookup_entry(wrapper_method, in_base_library, THREAD); + } + } + } + return NULL; +} + +address NativeLookup::lookup_base(methodHandle method, bool& in_base_library, TRAPS) { + address entry = NULL; + ResourceMark rm(THREAD); + + entry = lookup_entry(method, in_base_library, THREAD); + if (entry != NULL) return entry; + + // standard native method resolution has failed. Check if there are any + // JVM TI prefixes which have been applied to the native method name. + entry = lookup_entry_prefixed(method, in_base_library, THREAD); + if (entry != NULL) return entry; + + // Native function not found, throw UnsatisfiedLinkError + THROW_MSG_0(vmSymbols::java_lang_UnsatisfiedLinkError(), + method->name_and_sig_as_C_string()); +} + + +address NativeLookup::lookup(methodHandle method, bool& in_base_library, TRAPS) { + if (!method->has_native_function()) { + address entry = lookup_base(method, in_base_library, CHECK_NULL); + method->set_native_function(entry, + methodOopDesc::native_bind_event_is_interesting); + // -verbose:jni printing + if (PrintJNIResolving) { + ResourceMark rm(THREAD); + tty->print_cr("[Dynamic-linking native method %s.%s ... JNI]", + Klass::cast(method->method_holder())->external_name(), + method->name()->as_C_string()); + } + } + return method->native_function(); +} + +address NativeLookup::base_library_lookup(const char* class_name, const char* method_name, const char* signature) { + EXCEPTION_MARK; + bool in_base_library = true; // SharedRuntime inits some math methods. + symbolHandle c_name = oopFactory::new_symbol_handle(class_name, CATCH); + symbolHandle m_name = oopFactory::new_symbol_handle(method_name, CATCH); + symbolHandle s_name = oopFactory::new_symbol_handle(signature, CATCH); + + // Find the class + klassOop k = SystemDictionary::resolve_or_fail(c_name, true, CATCH); + instanceKlassHandle klass (THREAD, k); + + // Find method and invoke standard lookup + methodHandle method (THREAD, + klass->uncached_lookup_method(m_name(), s_name())); + address result = lookup(method, in_base_library, CATCH); + assert(in_base_library, "must be in basic library"); + guarantee(result != NULL, "must be non NULL"); + return result; +} diff --git a/hotspot/src/share/vm/prims/nativeLookup.hpp b/hotspot/src/share/vm/prims/nativeLookup.hpp new file mode 100644 index 00000000000..4b308761cb4 --- /dev/null +++ b/hotspot/src/share/vm/prims/nativeLookup.hpp @@ -0,0 +1,45 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// NativeLookup provides an interface for finding DLL entry points for +// Java native functions. + +class NativeLookup : AllStatic { + private: + // JNI name computation + static char* pure_jni_name(methodHandle method); + static char* long_jni_name(methodHandle method); + + // Style specific lookup + static address lookup_style(methodHandle method, char* pure_name, const char* long_name, int args_size, bool os_style, bool& in_base_library, TRAPS); + static address lookup_base (methodHandle method, bool& in_base_library, TRAPS); + static address lookup_entry(methodHandle method, bool& in_base_library, TRAPS); + static address lookup_entry_prefixed(methodHandle method, bool& in_base_library, TRAPS); + public: + // Lookup native function. May throw UnsatisfiedLinkError. + static address lookup(methodHandle method, bool& in_base_library, TRAPS); + + // Lookup native functions in base library. + static address base_library_lookup(const char* class_name, const char* method_name, const char* signature); +}; diff --git a/hotspot/src/share/vm/prims/perf.cpp b/hotspot/src/share/vm/prims/perf.cpp new file mode 100644 index 00000000000..51cba6982db --- /dev/null +++ b/hotspot/src/share/vm/prims/perf.cpp @@ -0,0 +1,313 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Implementation of class sun.misc.Perf + */ + +#include "incls/_precompiled.incl" +#include "incls/_perf.cpp.incl" + + +#define PERF_ENTRY(result_type, header) \ + JVM_ENTRY(result_type, header) + +#define PERF_END JVM_END + +#define PerfWrapper(arg) /* Unimplemented at this time */ + +static char* jstr_to_utf(JNIEnv *env, jstring str, TRAPS) { + + char* utfstr = NULL; + + if (str == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + //throw_new(env,"NullPointerException"); + } + + int len = env->GetStringUTFLength(str); + int unicode_len = env->GetStringLength(str); + + utfstr = NEW_RESOURCE_ARRAY(char, len + 1); + + env->GetStringUTFRegion(str, 0, unicode_len, utfstr); + + return utfstr; +} + +PERF_ENTRY(jobject, Perf_Attach(JNIEnv *env, jobject unused, jstring user, int vmid, int mode)) + + PerfWrapper("Perf_Attach"); + + char* address = 0; + size_t capacity = 0; + const char* user_utf = NULL; + + ResourceMark rm; + + { + ThreadToNativeFromVM ttnfv(thread); + + user_utf = user == NULL ? NULL : jstr_to_utf(env, user, CHECK_NULL); + } + + if (mode != PerfMemory::PERF_MODE_RO && + mode != PerfMemory::PERF_MODE_RW) { + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + + // attach to the PerfData memory region for the specified VM + PerfMemory::attach(user_utf, vmid, (PerfMemory::PerfMemoryMode) mode, + &address, &capacity, CHECK_NULL); + + { + ThreadToNativeFromVM ttnfv(thread); + return env->NewDirectByteBuffer(address, (jlong)capacity); + } + +PERF_END + +PERF_ENTRY(void, Perf_Detach(JNIEnv *env, jobject unused, jobject buffer)) + + PerfWrapper("Perf_Detach"); + + void* address = 0; + jlong capacity = 0; + + // get buffer address and capacity + { + ThreadToNativeFromVM ttnfv(thread); + address = env->GetDirectBufferAddress(buffer); + capacity = env->GetDirectBufferCapacity(buffer); + } + + PerfMemory::detach((char*)address, capacity, CHECK); + +PERF_END + +PERF_ENTRY(jobject, Perf_CreateLong(JNIEnv *env, jobject perf, jstring name, + int variability, int units, jlong value)) + + PerfWrapper("Perf_CreateLong"); + + char* name_utf = NULL; + + if (units <= 0 || units > PerfData::U_Last) { + debug_only(warning("unexpected units argument, units = %d", units)); + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + + ResourceMark rm; + + { + ThreadToNativeFromVM ttnfv(thread); + + name_utf = jstr_to_utf(env, name, CHECK_NULL); + } + + PerfLong* pl = NULL; + + // check that the PerfData name doesn't already exist + if (PerfDataManager::exists(name_utf)) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "PerfLong name already exists"); + } + + switch(variability) { + case 1: /* V_Constant */ + pl = PerfDataManager::create_long_constant(NULL_NS, (char *)name_utf, + (PerfData::Units)units, value, + CHECK_NULL); + break; + + case 2: /* V_Variable */ + pl = PerfDataManager::create_long_variable(NULL_NS, (char *)name_utf, + (PerfData::Units)units, value, + CHECK_NULL); + break; + + case 3: /* V_Monotonic Counter */ + pl = PerfDataManager::create_long_counter(NULL_NS, (char *)name_utf, + (PerfData::Units)units, value, + CHECK_NULL); + break; + + default: /* Illegal Argument */ + debug_only(warning("unexpected variability value: %d", variability)); + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + break; + } + + long* lp = (long*)pl->get_address(); + + { + ThreadToNativeFromVM ttnfv(thread); + return env->NewDirectByteBuffer(lp, sizeof(jlong)); + } + +PERF_END + +PERF_ENTRY(jobject, Perf_CreateByteArray(JNIEnv *env, jobject perf, + jstring name, jint variability, + jint units, jbyteArray value, + jint maxlength)) + + PerfWrapper("Perf_CreateByteArray"); + + // check for valid byte array objects + if (name == NULL || value == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + + // check for valid variability classification + if (variability != PerfData::V_Constant && + variability != PerfData::V_Variable) { + debug_only(warning("unexpected variability value: %d", variability)); + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + + // check for valid units + if (units != PerfData::U_String) { + // only String based ByteArray objects are currently supported + debug_only(warning("unexpected units value: %d", variability)); + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + + int value_length; + char* name_utf = NULL; + jbyte* value_local = NULL; + + ResourceMark rm; + + { + ThreadToNativeFromVM ttnfv(thread); + + name_utf = jstr_to_utf(env, name, CHECK_NULL); + + value_length = env->GetArrayLength(value); + + value_local = NEW_RESOURCE_ARRAY(jbyte, value_length + 1); + + env->GetByteArrayRegion(value, 0, value_length, value_local); + } + + // check that the counter name doesn't already exist + if (PerfDataManager::exists((char*)name_utf)) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "PerfByteArray name already exists"); + } + + PerfByteArray* pbv = NULL; + + if (units == PerfData::U_String) { + + if (variability == PerfData::V_Constant) { + // create the string constant + pbv = PerfDataManager::create_string_constant(NULL_NS, (char*)name_utf, + (char*)value_local, + CHECK_NULL); + + assert(maxlength == value_length, "string constant length should be == maxlength"); + maxlength = value_length; + } + else { + + // create the string variable + pbv = PerfDataManager::create_string_variable(NULL_NS, (char*)name_utf, + maxlength, + (char*)value_local, + CHECK_NULL); + + assert(maxlength >= value_length,"string variable length should be <= maxlength"); + } + } + + char* cp = (char*)pbv->get_address(); + + { + ThreadToNativeFromVM ttnfv(thread); + return env->NewDirectByteBuffer(cp, maxlength+1); + } + +PERF_END + +PERF_ENTRY(jlong, Perf_HighResCounter(JNIEnv *env, jobject perf)) + + PerfWrapper("Perf_HighResCounter"); + + // this should be a method in java.lang.System. This value could + // be acquired through access to a PerfData performance counter, but + // doing so would require that the PerfData monitoring overhead be + // incurred by all Java applications, which is unacceptable. + + return os::elapsed_counter(); + +PERF_END + +PERF_ENTRY(jlong, Perf_HighResFrequency(JNIEnv *env, jobject perf)) + + PerfWrapper("Perf_HighResFrequency"); + + // this should be a method in java.lang.System. This value could + // be acquired through access to a PerfData performance counter, but + // doing so would require that the PerfData monitoring overhead be + // incurred by all Java applications, which is unacceptable. + + return os::elapsed_frequency(); + +PERF_END + +/// JVM_RegisterPerfMethods + +#define CC (char*) /*cast a literal from (const char*)*/ +#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) +#define BB "Ljava/nio/ByteBuffer;" +#define JLS "Ljava/lang/String;" +#define CL_ARGS CC"("JLS"IIJ)"BB +#define CBA_ARGS CC"("JLS"II[BI)"BB + +static JNINativeMethod perfmethods[] = { + + {CC"attach", CC"("JLS"II)"BB, FN_PTR(Perf_Attach)}, + {CC"detach", CC"("BB")V", FN_PTR(Perf_Detach)}, + {CC"createLong", CL_ARGS, FN_PTR(Perf_CreateLong)}, + {CC"createByteArray", CBA_ARGS, FN_PTR(Perf_CreateByteArray)}, + {CC"highResCounter", CC"()J", FN_PTR(Perf_HighResCounter)}, + {CC"highResFrequency", CC"()J", FN_PTR(Perf_HighResFrequency)} +}; + +#undef CBA_ARGS +#undef CL_ARGS +#undef JLS +#undef BB +#undef FN_PTR +#undef CC + +// This one function is exported, used by NativeLookup. +JVM_ENTRY(void, JVM_RegisterPerfMethods(JNIEnv *env, jclass perfclass)) + PerfWrapper("JVM_RegisterPerfMethods"); + { + ThreadToNativeFromVM ttnfv(thread); + int ok = env->RegisterNatives(perfclass, perfmethods, sizeof(perfmethods)/sizeof(JNINativeMethod)); + guarantee(ok == 0, "register perf natives"); + } +JVM_END diff --git a/hotspot/src/share/vm/prims/privilegedStack.cpp b/hotspot/src/share/vm/prims/privilegedStack.cpp new file mode 100644 index 00000000000..4a8b8271fbb --- /dev/null +++ b/hotspot/src/share/vm/prims/privilegedStack.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_privilegedStack.cpp.incl" + + +void PrivilegedElement::initialize(vframeStream* vfst, oop context, PrivilegedElement* next, TRAPS) { + methodOop method = vfst->method(); + _klass = method->method_holder(); + _privileged_context = context; +#ifdef CHECK_UNHANDLED_OOPS + THREAD->allow_unhandled_oop(&_klass); + THREAD->allow_unhandled_oop(&_privileged_context); +#endif // CHECK_UNHANDLED_OOPS + _frame_id = vfst->frame_id(); + _next = next; + assert(_privileged_context == NULL || _privileged_context->is_oop(), "must be an oop"); + assert(protection_domain() == NULL || protection_domain()->is_oop(), "must be an oop"); +} + +void PrivilegedElement::oops_do(OopClosure* f) { + PrivilegedElement *cur = this; + do { + f->do_oop((oop*) &cur->_klass); + f->do_oop((oop*) &cur->_privileged_context); + cur = cur->_next; + } while(cur != NULL); +} + +//------------------------------------------------------------------------------- +#ifndef PRODUCT + +void PrivilegedElement::print_on(outputStream* st) const { + st->print(" 0x%lx ", _frame_id); + _klass->print_value_on(st); + if (protection_domain() != NULL) { + st->print(" "); + protection_domain()->print_value_on(st); + } + st->cr(); +} + +bool PrivilegedElement::contains(address addr) { + PrivilegedElement *e = (PrivilegedElement *)addr; + if (e >= this && e < this+1) return true; + + if (_next != NULL) { + return _next->contains(addr); + } else { + return false; + } +} + +#endif diff --git a/hotspot/src/share/vm/prims/privilegedStack.hpp b/hotspot/src/share/vm/prims/privilegedStack.hpp new file mode 100644 index 00000000000..b93942e905f --- /dev/null +++ b/hotspot/src/share/vm/prims/privilegedStack.hpp @@ -0,0 +1,43 @@ +/* + * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class PrivilegedElement VALUE_OBJ_CLASS_SPEC { + private: + klassOop _klass; // klass for method + oop _privileged_context; // context for operation + intptr_t* _frame_id; // location on stack + PrivilegedElement* _next; // Link to next one on stack + public: + void initialize(vframeStream* vf, oop context, PrivilegedElement* next, TRAPS); + void oops_do(OopClosure* f); + intptr_t* frame_id() const { return _frame_id; } + oop privileged_context() const { return _privileged_context; } + oop class_loader() const { return instanceKlass::cast(_klass)->class_loader(); } + oop protection_domain() const { return instanceKlass::cast(_klass)->protection_domain(); } + PrivilegedElement *next() const { return _next; } + + // debugging (used for find) + void print_on(outputStream* st) const PRODUCT_RETURN; + bool contains(address addr) PRODUCT_RETURN0; +}; diff --git a/hotspot/src/share/vm/prims/unsafe.cpp b/hotspot/src/share/vm/prims/unsafe.cpp new file mode 100644 index 00000000000..adea250987c --- /dev/null +++ b/hotspot/src/share/vm/prims/unsafe.cpp @@ -0,0 +1,1338 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * Implementation of class sun.misc.Unsafe + */ + +#include "incls/_precompiled.incl" +#include "incls/_unsafe.cpp.incl" + +#define MAX_OBJECT_SIZE \ + ( arrayOopDesc::header_size(T_DOUBLE) * HeapWordSize \ + + ((julong)max_jint * sizeof(double)) ) + + +#define UNSAFE_ENTRY(result_type, header) \ + JVM_ENTRY(result_type, header) + +// Can't use UNSAFE_LEAF because it has the signature of a straight +// call into the runtime (just like JVM_LEAF, funny that) but it's +// called like a Java Native and thus the wrapper built for it passes +// arguments like a JNI call. It expects those arguments to be popped +// from the stack on Intel like all good JNI args are, and adjusts the +// stack according. Since the JVM_LEAF call expects no extra +// arguments the stack isn't popped in the C code, is pushed by the +// wrapper and we get sick. +//#define UNSAFE_LEAF(result_type, header) \ +// JVM_LEAF(result_type, header) + +#define UNSAFE_END JVM_END + +#define UnsafeWrapper(arg) /*nothing, for the present*/ + + +inline void* addr_from_java(jlong addr) { + // This assert fails in a variety of ways on 32-bit systems. + // It is impossible to predict whether native code that converts + // pointers to longs will sign-extend or zero-extend the addresses. + //assert(addr == (uintptr_t)addr, "must not be odd high bits"); + return (void*)(uintptr_t)addr; +} + +inline jlong addr_to_java(void* p) { + assert(p == (void*)(uintptr_t)p, "must not be odd high bits"); + return (uintptr_t)p; +} + + +// Note: The VM's obj_field and related accessors use byte-scaled +// ("unscaled") offsets, just as the unsafe methods do. + +// However, the method Unsafe.fieldOffset explicitly declines to +// guarantee this. The field offset values manipulated by the Java user +// through the Unsafe API are opaque cookies that just happen to be byte +// offsets. We represent this state of affairs by passing the cookies +// through conversion functions when going between the VM and the Unsafe API. +// The conversion functions just happen to be no-ops at present. + +inline jlong field_offset_to_byte_offset(jlong field_offset) { + return field_offset; +} + +inline jlong field_offset_from_byte_offset(jlong byte_offset) { + return byte_offset; +} + +inline jint invocation_key_from_method_slot(jint slot) { + return slot; +} + +inline jint invocation_key_to_method_slot(jint key) { + return key; +} + +inline void* index_oop_from_field_offset_long(oop p, jlong field_offset) { + jlong byte_offset = field_offset_to_byte_offset(field_offset); +#ifdef ASSERT + if (p != NULL) { + assert(byte_offset >= 0 && byte_offset <= (jlong)MAX_OBJECT_SIZE, "sane offset"); + if (byte_offset == (jint)byte_offset) { + void* ptr_plus_disp = (address)p + byte_offset; + assert((void*)p->obj_field_addr((jint)byte_offset) == ptr_plus_disp, + "raw [ptr+disp] must be consistent with oop::field_base"); + } + } +#endif + if (sizeof(char*) == sizeof(jint)) // (this constant folds!) + return (address)p + (jint) byte_offset; + else + return (address)p + byte_offset; +} + +// Externally callable versions: +// (Use these in compiler intrinsics which emulate unsafe primitives.) +jlong Unsafe_field_offset_to_byte_offset(jlong field_offset) { + return field_offset; +} +jlong Unsafe_field_offset_from_byte_offset(jlong byte_offset) { + return byte_offset; +} +jint Unsafe_invocation_key_from_method_slot(jint slot) { + return invocation_key_from_method_slot(slot); +} +jint Unsafe_invocation_key_to_method_slot(jint key) { + return invocation_key_to_method_slot(key); +} + + +///// Data in the Java heap. + +#define GET_FIELD(obj, offset, type_name, v) \ + oop p = JNIHandles::resolve(obj); \ + type_name v = *(type_name*)index_oop_from_field_offset_long(p, offset) + +#define SET_FIELD(obj, offset, type_name, x) \ + oop p = JNIHandles::resolve(obj); \ + *(type_name*)index_oop_from_field_offset_long(p, offset) = x + +#define GET_FIELD_VOLATILE(obj, offset, type_name, v) \ + oop p = JNIHandles::resolve(obj); \ + volatile type_name v = *(volatile type_name*)index_oop_from_field_offset_long(p, offset) + +#define SET_FIELD_VOLATILE(obj, offset, type_name, x) \ + oop p = JNIHandles::resolve(obj); \ + *(volatile type_name*)index_oop_from_field_offset_long(p, offset) = x; \ + OrderAccess::fence(); + +// Get/SetObject must be special-cased, since it works with handles. + +// The xxx140 variants for backward compatibility do not allow a full-width offset. +UNSAFE_ENTRY(jobject, Unsafe_GetObject140(JNIEnv *env, jobject unsafe, jobject obj, jint offset)) + UnsafeWrapper("Unsafe_GetObject"); + if (obj == NULL) THROW_0(vmSymbols::java_lang_NullPointerException()); + GET_FIELD(obj, offset, oop, v); + return JNIHandles::make_local(env, v); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_SetObject140(JNIEnv *env, jobject unsafe, jobject obj, jint offset, jobject x_h)) + UnsafeWrapper("Unsafe_SetObject"); + if (obj == NULL) THROW(vmSymbols::java_lang_NullPointerException()); + oop x = JNIHandles::resolve(x_h); + //SET_FIELD(obj, offset, oop, x); + oop p = JNIHandles::resolve(obj); + if (x != NULL) { + // If there is a heap base pointer, we are obliged to emit a store barrier. + oop_store((oop*)index_oop_from_field_offset_long(p, offset), x); + } else { + *(oop*)index_oop_from_field_offset_long(p, offset) = x; + } +UNSAFE_END + +// The normal variants allow a null base pointer with an arbitrary address. +// But if the base pointer is non-null, the offset should make some sense. +// That is, it should be in the range [0, MAX_OBJECT_SIZE]. +UNSAFE_ENTRY(jobject, Unsafe_GetObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) + UnsafeWrapper("Unsafe_GetObject"); + GET_FIELD(obj, offset, oop, v); + return JNIHandles::make_local(env, v); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_SetObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) + UnsafeWrapper("Unsafe_SetObject"); + oop x = JNIHandles::resolve(x_h); + oop p = JNIHandles::resolve(obj); + oop_store((oop*)index_oop_from_field_offset_long(p, offset), x); +UNSAFE_END + +UNSAFE_ENTRY(jobject, Unsafe_GetObjectVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) + UnsafeWrapper("Unsafe_GetObjectVolatile"); + GET_FIELD_VOLATILE(obj, offset, oop, v); + return JNIHandles::make_local(env, v); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_SetObjectVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) + UnsafeWrapper("Unsafe_SetObjectVolatile"); + oop x = JNIHandles::resolve(x_h); + oop p = JNIHandles::resolve(obj); + oop_store((oop*)index_oop_from_field_offset_long(p, offset), x); + OrderAccess::fence(); +UNSAFE_END + +// Volatile long versions must use locks if !VM_Version::supports_cx8(). +// support_cx8 is a surrogate for 'supports atomic long memory ops'. + +UNSAFE_ENTRY(jlong, Unsafe_GetLongVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) + UnsafeWrapper("Unsafe_GetLongVolatile"); + { + if (VM_Version::supports_cx8()) { + GET_FIELD_VOLATILE(obj, offset, jlong, v); + return v; + } + else { + Handle p (THREAD, JNIHandles::resolve(obj)); + jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset)); + ObjectLocker ol(p, THREAD); + jlong value = *addr; + return value; + } + } +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_SetLongVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong x)) + UnsafeWrapper("Unsafe_SetLongVolatile"); + { + if (VM_Version::supports_cx8()) { + SET_FIELD_VOLATILE(obj, offset, jlong, x); + } + else { + Handle p (THREAD, JNIHandles::resolve(obj)); + jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset)); + ObjectLocker ol(p, THREAD); + *addr = x; + } + } +UNSAFE_END + + +#define DEFINE_GETSETOOP(jboolean, Boolean) \ + \ +UNSAFE_ENTRY(jboolean, Unsafe_Get##Boolean##140(JNIEnv *env, jobject unsafe, jobject obj, jint offset)) \ + UnsafeWrapper("Unsafe_Get"#Boolean); \ + if (obj == NULL) THROW_0(vmSymbols::java_lang_NullPointerException()); \ + GET_FIELD(obj, offset, jboolean, v); \ + return v; \ +UNSAFE_END \ + \ +UNSAFE_ENTRY(void, Unsafe_Set##Boolean##140(JNIEnv *env, jobject unsafe, jobject obj, jint offset, jboolean x)) \ + UnsafeWrapper("Unsafe_Set"#Boolean); \ + if (obj == NULL) THROW(vmSymbols::java_lang_NullPointerException()); \ + SET_FIELD(obj, offset, jboolean, x); \ +UNSAFE_END \ + \ +UNSAFE_ENTRY(jboolean, Unsafe_Get##Boolean(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) \ + UnsafeWrapper("Unsafe_Get"#Boolean); \ + GET_FIELD(obj, offset, jboolean, v); \ + return v; \ +UNSAFE_END \ + \ +UNSAFE_ENTRY(void, Unsafe_Set##Boolean(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jboolean x)) \ + UnsafeWrapper("Unsafe_Set"#Boolean); \ + SET_FIELD(obj, offset, jboolean, x); \ +UNSAFE_END \ + \ +// END DEFINE_GETSETOOP. + + +#define DEFINE_GETSETOOP_VOLATILE(jboolean, Boolean) \ + \ +UNSAFE_ENTRY(jboolean, Unsafe_Get##Boolean##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) \ + UnsafeWrapper("Unsafe_Get"#Boolean); \ + GET_FIELD_VOLATILE(obj, offset, jboolean, v); \ + return v; \ +UNSAFE_END \ + \ +UNSAFE_ENTRY(void, Unsafe_Set##Boolean##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jboolean x)) \ + UnsafeWrapper("Unsafe_Set"#Boolean); \ + SET_FIELD_VOLATILE(obj, offset, jboolean, x); \ +UNSAFE_END \ + \ +// END DEFINE_GETSETOOP_VOLATILE. + +DEFINE_GETSETOOP(jboolean, Boolean) +DEFINE_GETSETOOP(jbyte, Byte) +DEFINE_GETSETOOP(jshort, Short); +DEFINE_GETSETOOP(jchar, Char); +DEFINE_GETSETOOP(jint, Int); +DEFINE_GETSETOOP(jlong, Long); +DEFINE_GETSETOOP(jfloat, Float); +DEFINE_GETSETOOP(jdouble, Double); + +DEFINE_GETSETOOP_VOLATILE(jboolean, Boolean) +DEFINE_GETSETOOP_VOLATILE(jbyte, Byte) +DEFINE_GETSETOOP_VOLATILE(jshort, Short); +DEFINE_GETSETOOP_VOLATILE(jchar, Char); +DEFINE_GETSETOOP_VOLATILE(jint, Int); +// no long -- handled specially +DEFINE_GETSETOOP_VOLATILE(jfloat, Float); +DEFINE_GETSETOOP_VOLATILE(jdouble, Double); + +#undef DEFINE_GETSETOOP + +// The non-intrinsified versions of setOrdered just use setVolatile + +UNSAFE_ENTRY(void, Unsafe_SetOrderedInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint x)) \ + UnsafeWrapper("Unsafe_SetOrderedInt"); \ + SET_FIELD_VOLATILE(obj, offset, jint, x); \ +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_SetOrderedObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) + UnsafeWrapper("Unsafe_SetOrderedObject"); + oop x = JNIHandles::resolve(x_h); + oop p = JNIHandles::resolve(obj); + oop_store((oop*)index_oop_from_field_offset_long(p, offset), x); + OrderAccess::fence(); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_SetOrderedLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong x)) + UnsafeWrapper("Unsafe_SetOrderedLong"); + { + if (VM_Version::supports_cx8()) { + SET_FIELD_VOLATILE(obj, offset, jlong, x); + } + else { + Handle p (THREAD, JNIHandles::resolve(obj)); + jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset)); + ObjectLocker ol(p, THREAD); + *addr = x; + } + } +UNSAFE_END + +////// Data in the C heap. + +// Note: These do not throw NullPointerException for bad pointers. +// They just crash. Only a oop base pointer can generate a NullPointerException. +// +#define DEFINE_GETSETNATIVE(java_type, Type, native_type) \ + \ +UNSAFE_ENTRY(java_type, Unsafe_GetNative##Type(JNIEnv *env, jobject unsafe, jlong addr)) \ + UnsafeWrapper("Unsafe_GetNative"#Type); \ + void* p = addr_from_java(addr); \ + JavaThread* t = JavaThread::current(); \ + t->set_doing_unsafe_access(true); \ + java_type x = *(volatile native_type*)p; \ + t->set_doing_unsafe_access(false); \ + return x; \ +UNSAFE_END \ + \ +UNSAFE_ENTRY(void, Unsafe_SetNative##Type(JNIEnv *env, jobject unsafe, jlong addr, java_type x)) \ + UnsafeWrapper("Unsafe_SetNative"#Type); \ + JavaThread* t = JavaThread::current(); \ + t->set_doing_unsafe_access(true); \ + void* p = addr_from_java(addr); \ + *(volatile native_type*)p = x; \ + t->set_doing_unsafe_access(false); \ +UNSAFE_END \ + \ +// END DEFINE_GETSETNATIVE. + +DEFINE_GETSETNATIVE(jbyte, Byte, signed char) +DEFINE_GETSETNATIVE(jshort, Short, signed short); +DEFINE_GETSETNATIVE(jchar, Char, unsigned short); +DEFINE_GETSETNATIVE(jint, Int, jint); +// no long -- handled specially +DEFINE_GETSETNATIVE(jfloat, Float, float); +DEFINE_GETSETNATIVE(jdouble, Double, double); + +#undef DEFINE_GETSETNATIVE + +UNSAFE_ENTRY(jlong, Unsafe_GetNativeLong(JNIEnv *env, jobject unsafe, jlong addr)) + UnsafeWrapper("Unsafe_GetNativeLong"); + JavaThread* t = JavaThread::current(); + // We do it this way to avoid problems with access to heap using 64 + // bit loads, as jlong in heap could be not 64-bit aligned, and on + // some CPUs (SPARC) it leads to SIGBUS. + t->set_doing_unsafe_access(true); + void* p = addr_from_java(addr); + jlong x; + if (((intptr_t)p & 7) == 0) { + // jlong is aligned, do a volatile access + x = *(volatile jlong*)p; + } else { + jlong_accessor acc; + acc.words[0] = ((volatile jint*)p)[0]; + acc.words[1] = ((volatile jint*)p)[1]; + x = acc.long_value; + } + t->set_doing_unsafe_access(false); + return x; +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_SetNativeLong(JNIEnv *env, jobject unsafe, jlong addr, jlong x)) + UnsafeWrapper("Unsafe_SetNativeLong"); + JavaThread* t = JavaThread::current(); + // see comment for Unsafe_GetNativeLong + t->set_doing_unsafe_access(true); + void* p = addr_from_java(addr); + if (((intptr_t)p & 7) == 0) { + // jlong is aligned, do a volatile access + *(volatile jlong*)p = x; + } else { + jlong_accessor acc; + acc.long_value = x; + ((volatile jint*)p)[0] = acc.words[0]; + ((volatile jint*)p)[1] = acc.words[1]; + } + t->set_doing_unsafe_access(false); +UNSAFE_END + + +UNSAFE_ENTRY(jlong, Unsafe_GetNativeAddress(JNIEnv *env, jobject unsafe, jlong addr)) + UnsafeWrapper("Unsafe_GetNativeAddress"); + void* p = addr_from_java(addr); + return addr_to_java(*(void**)p); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_SetNativeAddress(JNIEnv *env, jobject unsafe, jlong addr, jlong x)) + UnsafeWrapper("Unsafe_SetNativeAddress"); + void* p = addr_from_java(addr); + *(void**)p = addr_from_java(x); +UNSAFE_END + + +////// Allocation requests + +UNSAFE_ENTRY(jobject, Unsafe_AllocateInstance(JNIEnv *env, jobject unsafe, jclass cls)) + UnsafeWrapper("Unsafe_AllocateInstance"); + { + ThreadToNativeFromVM ttnfv(thread); + return env->AllocObject(cls); + } +UNSAFE_END + +UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory(JNIEnv *env, jobject unsafe, jlong size)) + UnsafeWrapper("Unsafe_AllocateMemory"); + size_t sz = (size_t)size; + if (sz != (julong)size || size < 0) { + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + if (sz == 0) { + return 0; + } + sz = round_to(sz, HeapWordSize); + void* x = os::malloc(sz); + if (x == NULL) { + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + //Copy::fill_to_words((HeapWord*)x, sz / HeapWordSize); + return addr_to_java(x); +UNSAFE_END + +UNSAFE_ENTRY(jlong, Unsafe_ReallocateMemory(JNIEnv *env, jobject unsafe, jlong addr, jlong size)) + UnsafeWrapper("Unsafe_ReallocateMemory"); + void* p = addr_from_java(addr); + size_t sz = (size_t)size; + if (sz != (julong)size || size < 0) { + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + if (sz == 0) { + os::free(p); + return 0; + } + sz = round_to(sz, HeapWordSize); + void* x = (p == NULL) ? os::malloc(sz) : os::realloc(p, sz); + if (x == NULL) { + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + return addr_to_java(x); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_FreeMemory(JNIEnv *env, jobject unsafe, jlong addr)) + UnsafeWrapper("Unsafe_FreeMemory"); + void* p = addr_from_java(addr); + if (p == NULL) { + return; + } + os::free(p); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_SetMemory(JNIEnv *env, jobject unsafe, jlong addr, jlong size, jbyte value)) + UnsafeWrapper("Unsafe_SetMemory"); + size_t sz = (size_t)size; + if (sz != (julong)size || size < 0) { + THROW(vmSymbols::java_lang_IllegalArgumentException()); + } + char* p = (char*) addr_from_java(addr); + Copy::fill_to_memory_atomic(p, sz, value); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_SetMemory2(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong size, jbyte value)) + UnsafeWrapper("Unsafe_SetMemory"); + size_t sz = (size_t)size; + if (sz != (julong)size || size < 0) { + THROW(vmSymbols::java_lang_IllegalArgumentException()); + } + oop base = JNIHandles::resolve(obj); + void* p = index_oop_from_field_offset_long(base, offset); + Copy::fill_to_memory_atomic(p, sz, value); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_CopyMemory(JNIEnv *env, jobject unsafe, jlong srcAddr, jlong dstAddr, jlong size)) + UnsafeWrapper("Unsafe_CopyMemory"); + if (size == 0) { + return; + } + size_t sz = (size_t)size; + if (sz != (julong)size || size < 0) { + THROW(vmSymbols::java_lang_IllegalArgumentException()); + } + void* src = addr_from_java(srcAddr); + void* dst = addr_from_java(dstAddr); + Copy::conjoint_memory_atomic(src, dst, sz); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_CopyMemory2(JNIEnv *env, jobject unsafe, jobject srcObj, jlong srcOffset, jobject dstObj, jlong dstOffset, jlong size)) + UnsafeWrapper("Unsafe_CopyMemory"); + if (size == 0) { + return; + } + size_t sz = (size_t)size; + if (sz != (julong)size || size < 0) { + THROW(vmSymbols::java_lang_IllegalArgumentException()); + } + oop srcp = JNIHandles::resolve(srcObj); + oop dstp = JNIHandles::resolve(dstObj); + if (dstp != NULL && !dstp->is_typeArray()) { + // NYI: This works only for non-oop arrays at present. + // Generalizing it would be reasonable, but requires card marking. + // Also, autoboxing a Long from 0L in copyMemory(x,y, 0L,z, n) would be bad. + THROW(vmSymbols::java_lang_IllegalArgumentException()); + } + void* src = index_oop_from_field_offset_long(srcp, srcOffset); + void* dst = index_oop_from_field_offset_long(dstp, dstOffset); + Copy::conjoint_memory_atomic(src, dst, sz); +UNSAFE_END + + +////// Random queries + +// See comment at file start about UNSAFE_LEAF +//UNSAFE_LEAF(jint, Unsafe_AddressSize()) +UNSAFE_ENTRY(jint, Unsafe_AddressSize(JNIEnv *env, jobject unsafe)) + UnsafeWrapper("Unsafe_AddressSize"); + return sizeof(void*); +UNSAFE_END + +// See comment at file start about UNSAFE_LEAF +//UNSAFE_LEAF(jint, Unsafe_PageSize()) +UNSAFE_ENTRY(jint, Unsafe_PageSize(JNIEnv *env, jobject unsafe)) + UnsafeWrapper("Unsafe_PageSize"); + return os::vm_page_size(); +UNSAFE_END + +jint find_field_offset(jobject field, int must_be_static, TRAPS) { + if (field == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + + oop reflected = JNIHandles::resolve_non_null(field); + oop mirror = java_lang_reflect_Field::clazz(reflected); + klassOop k = java_lang_Class::as_klassOop(mirror); + int slot = java_lang_reflect_Field::slot(reflected); + int modifiers = java_lang_reflect_Field::modifiers(reflected); + + if (must_be_static >= 0) { + int really_is_static = ((modifiers & JVM_ACC_STATIC) != 0); + if (must_be_static != really_is_static) { + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + } + + int offset = instanceKlass::cast(k)->offset_from_fields(slot); + return field_offset_from_byte_offset(offset); +} + +UNSAFE_ENTRY(jlong, Unsafe_ObjectFieldOffset(JNIEnv *env, jobject unsafe, jobject field)) + UnsafeWrapper("Unsafe_ObjectFieldOffset"); + return find_field_offset(field, 0, THREAD); +UNSAFE_END + +UNSAFE_ENTRY(jlong, Unsafe_StaticFieldOffset(JNIEnv *env, jobject unsafe, jobject field)) + UnsafeWrapper("Unsafe_StaticFieldOffset"); + return find_field_offset(field, 1, THREAD); +UNSAFE_END + +UNSAFE_ENTRY(jobject, Unsafe_StaticFieldBaseFromField(JNIEnv *env, jobject unsafe, jobject field)) + UnsafeWrapper("Unsafe_StaticFieldBase"); + // Note: In this VM implementation, a field address is always a short + // offset from the base of a a klass metaobject. Thus, the full dynamic + // range of the return type is never used. However, some implementations + // might put the static field inside an array shared by many classes, + // or even at a fixed address, in which case the address could be quite + // large. In that last case, this function would return NULL, since + // the address would operate alone, without any base pointer. + + if (field == NULL) THROW_0(vmSymbols::java_lang_NullPointerException()); + + oop reflected = JNIHandles::resolve_non_null(field); + oop mirror = java_lang_reflect_Field::clazz(reflected); + int modifiers = java_lang_reflect_Field::modifiers(reflected); + + if ((modifiers & JVM_ACC_STATIC) == 0) { + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + + return JNIHandles::make_local(env, java_lang_Class::as_klassOop(mirror)); +UNSAFE_END + +//@deprecated +UNSAFE_ENTRY(jint, Unsafe_FieldOffset(JNIEnv *env, jobject unsafe, jobject field)) + UnsafeWrapper("Unsafe_FieldOffset"); + // tries (but fails) to be polymorphic between static and non-static: + jlong offset = find_field_offset(field, -1, THREAD); + guarantee(offset == (jint)offset, "offset fits in 32 bits"); + return (jint)offset; +UNSAFE_END + +//@deprecated +UNSAFE_ENTRY(jobject, Unsafe_StaticFieldBaseFromClass(JNIEnv *env, jobject unsafe, jobject clazz)) + UnsafeWrapper("Unsafe_StaticFieldBase"); + if (clazz == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + return JNIHandles::make_local(env, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz))); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_EnsureClassInitialized(JNIEnv *env, jobject unsafe, jobject clazz)) + UnsafeWrapper("Unsafe_EnsureClassInitialized"); + if (clazz == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + oop mirror = JNIHandles::resolve_non_null(clazz); + instanceKlass* k = instanceKlass::cast(java_lang_Class::as_klassOop(mirror)); + if (k != NULL) { + k->initialize(CHECK); + } +UNSAFE_END + +static void getBaseAndScale(int& base, int& scale, jclass acls, TRAPS) { + if (acls == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + oop mirror = JNIHandles::resolve_non_null(acls); + klassOop k = java_lang_Class::as_klassOop(mirror); + if (k == NULL || !k->klass_part()->oop_is_array()) { + THROW(vmSymbols::java_lang_InvalidClassException()); + } else if (k->klass_part()->oop_is_objArray()) { + base = arrayOopDesc::base_offset_in_bytes(T_OBJECT); + scale = oopSize; + } else if (k->klass_part()->oop_is_typeArray()) { + typeArrayKlass* tak = typeArrayKlass::cast(k); + base = tak->array_header_in_bytes(); + assert(base == arrayOopDesc::base_offset_in_bytes(tak->element_type()), "array_header_size semantics ok"); + scale = (1 << tak->log2_element_size()); + } else { + ShouldNotReachHere(); + } +} + +UNSAFE_ENTRY(jint, Unsafe_ArrayBaseOffset(JNIEnv *env, jobject unsafe, jclass acls)) + UnsafeWrapper("Unsafe_ArrayBaseOffset"); + int base, scale; + getBaseAndScale(base, scale, acls, CHECK_0); + return field_offset_from_byte_offset(base); +UNSAFE_END + + +UNSAFE_ENTRY(jint, Unsafe_ArrayIndexScale(JNIEnv *env, jobject unsafe, jclass acls)) + UnsafeWrapper("Unsafe_ArrayIndexScale"); + int base, scale; + getBaseAndScale(base, scale, acls, CHECK_0); + // This VM packs both fields and array elements down to the byte. + // But watch out: If this changes, so that array references for + // a given primitive type (say, T_BOOLEAN) use different memory units + // than fields, this method MUST return zero for such arrays. + // For example, the VM used to store sub-word sized fields in full + // words in the object layout, so that accessors like getByte(Object,int) + // did not really do what one might expect for arrays. Therefore, + // this function used to report a zero scale factor, so that the user + // would know not to attempt to access sub-word array elements. + // // Code for unpacked fields: + // if (scale < wordSize) return 0; + + // The following allows for a pretty general fieldOffset cookie scheme, + // but requires it to be linear in byte offset. + return field_offset_from_byte_offset(scale) - field_offset_from_byte_offset(0); +UNSAFE_END + + +static inline void throw_new(JNIEnv *env, const char *ename) { + char buf[100]; + strcpy(buf, "java/lang/"); + strcat(buf, ename); + jclass cls = env->FindClass(buf); + char* msg = NULL; + env->ThrowNew(cls, msg); +} + +static jclass Unsafe_DefineClass(JNIEnv *env, jstring name, jbyteArray data, int offset, int length, jobject loader, jobject pd) { + { + // Code lifted from JDK 1.3 ClassLoader.c + + jbyte *body; + char *utfName; + jclass result = 0; + char buf[128]; + + if (UsePerfData) { + ClassLoader::unsafe_defineClassCallCounter()->inc(); + } + + if (data == NULL) { + throw_new(env, "NullPointerException"); + return 0; + } + + /* Work around 4153825. malloc crashes on Solaris when passed a + * negative size. + */ + if (length < 0) { + throw_new(env, "ArrayIndexOutOfBoundsException"); + return 0; + } + + body = NEW_C_HEAP_ARRAY(jbyte, length); + + if (body == 0) { + throw_new(env, "OutOfMemoryError"); + return 0; + } + + env->GetByteArrayRegion(data, offset, length, body); + + if (env->ExceptionOccurred()) + goto free_body; + + if (name != NULL) { + uint len = env->GetStringUTFLength(name); + int unicode_len = env->GetStringLength(name); + if (len >= sizeof(buf)) { + utfName = NEW_C_HEAP_ARRAY(char, len + 1); + if (utfName == NULL) { + throw_new(env, "OutOfMemoryError"); + goto free_body; + } + } else { + utfName = buf; + } + env->GetStringUTFRegion(name, 0, unicode_len, utfName); + //VerifyFixClassname(utfName); + for (uint i = 0; i < len; i++) { + if (utfName[i] == '.') utfName[i] = '/'; + } + } else { + utfName = NULL; + } + + result = JVM_DefineClass(env, utfName, loader, body, length, pd); + + if (utfName && utfName != buf) + FREE_C_HEAP_ARRAY(char, utfName); + + free_body: + FREE_C_HEAP_ARRAY(jbyte, body); + return result; + } +} + + +UNSAFE_ENTRY(jclass, Unsafe_DefineClass0(JNIEnv *env, jobject unsafe, jstring name, jbyteArray data, int offset, int length)) + UnsafeWrapper("Unsafe_DefineClass"); + { + ThreadToNativeFromVM ttnfv(thread); + + int depthFromDefineClass0 = 1; + jclass caller = JVM_GetCallerClass(env, depthFromDefineClass0); + jobject loader = (caller == NULL) ? NULL : JVM_GetClassLoader(env, caller); + jobject pd = (caller == NULL) ? NULL : JVM_GetProtectionDomain(env, caller); + + return Unsafe_DefineClass(env, name, data, offset, length, loader, pd); + } +UNSAFE_END + + +UNSAFE_ENTRY(jclass, Unsafe_DefineClass1(JNIEnv *env, jobject unsafe, jstring name, jbyteArray data, int offset, int length, jobject loader, jobject pd)) + UnsafeWrapper("Unsafe_DefineClass"); + { + ThreadToNativeFromVM ttnfv(thread); + + return Unsafe_DefineClass(env, name, data, offset, length, loader, pd); + } +UNSAFE_END + + +UNSAFE_ENTRY(void, Unsafe_MonitorEnter(JNIEnv *env, jobject unsafe, jobject jobj)) + UnsafeWrapper("Unsafe_MonitorEnter"); + { + if (jobj == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + Handle obj(thread, JNIHandles::resolve_non_null(jobj)); + ObjectSynchronizer::jni_enter(obj, CHECK); + } +UNSAFE_END + + +UNSAFE_ENTRY(jboolean, Unsafe_TryMonitorEnter(JNIEnv *env, jobject unsafe, jobject jobj)) + UnsafeWrapper("Unsafe_TryMonitorEnter"); + { + if (jobj == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), JNI_FALSE); + } + Handle obj(thread, JNIHandles::resolve_non_null(jobj)); + bool res = ObjectSynchronizer::jni_try_enter(obj, CHECK_0); + return (res ? JNI_TRUE : JNI_FALSE); + } +UNSAFE_END + + +UNSAFE_ENTRY(void, Unsafe_MonitorExit(JNIEnv *env, jobject unsafe, jobject jobj)) + UnsafeWrapper("Unsafe_MonitorExit"); + { + if (jobj == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + Handle obj(THREAD, JNIHandles::resolve_non_null(jobj)); + ObjectSynchronizer::jni_exit(obj(), CHECK); + } +UNSAFE_END + + +UNSAFE_ENTRY(void, Unsafe_ThrowException(JNIEnv *env, jobject unsafe, jthrowable thr)) + UnsafeWrapper("Unsafe_ThrowException"); + { + ThreadToNativeFromVM ttnfv(thread); + env->Throw(thr); + } +UNSAFE_END + +// JSR166 ------------------------------------------------------------------ + +UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h)) + UnsafeWrapper("Unsafe_CompareAndSwapObject"); + oop x = JNIHandles::resolve(x_h); + oop e = JNIHandles::resolve(e_h); + oop p = JNIHandles::resolve(obj); + intptr_t* addr = (intptr_t *)index_oop_from_field_offset_long(p, offset); + intptr_t res = Atomic::cmpxchg_ptr((intptr_t)x, addr, (intptr_t)e); + jboolean success = (res == (intptr_t)e); + if (success) + update_barrier_set((oop*)addr, x); + return success; +UNSAFE_END + +UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) + UnsafeWrapper("Unsafe_CompareAndSwapInt"); + oop p = JNIHandles::resolve(obj); + jint* addr = (jint *) index_oop_from_field_offset_long(p, offset); + return (jint)(Atomic::cmpxchg(x, addr, e)) == e; +UNSAFE_END + +UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) + UnsafeWrapper("Unsafe_CompareAndSwapLong"); + Handle p (THREAD, JNIHandles::resolve(obj)); + jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset)); + if (VM_Version::supports_cx8()) + return (jlong)(Atomic::cmpxchg(x, addr, e)) == e; + else { + jboolean success = false; + ObjectLocker ol(p, THREAD); + if (*addr == e) { *addr = x; success = true; } + return success; + } +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time)) + UnsafeWrapper("Unsafe_Park"); + JavaThreadParkedState jtps(thread, time != 0); + thread->parker()->park(isAbsolute != 0, time); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread)) + UnsafeWrapper("Unsafe_Unpark"); + Parker* p = NULL; + if (jthread != NULL) { + oop java_thread = JNIHandles::resolve_non_null(jthread); + if (java_thread != NULL) { + jlong lp = java_lang_Thread::park_event(java_thread); + if (lp != 0) { + // This cast is OK even though the jlong might have been read + // non-atomically on 32bit systems, since there, one word will + // always be zero anyway and the value set is always the same + p = (Parker*)addr_from_java(lp); + } else { + // Grab lock if apparently null or using older version of library + MutexLocker mu(Threads_lock); + java_thread = JNIHandles::resolve_non_null(jthread); + if (java_thread != NULL) { + JavaThread* thr = java_lang_Thread::thread(java_thread); + if (thr != NULL) { + p = thr->parker(); + if (p != NULL) { // Bind to Java thread for next time. + java_lang_Thread::set_park_event(java_thread, addr_to_java(p)); + } + } + } + } + } + } + if (p != NULL) { + p->unpark(); + } +UNSAFE_END + +UNSAFE_ENTRY(jint, Unsafe_Loadavg(JNIEnv *env, jobject unsafe, jdoubleArray loadavg, jint nelem)) + UnsafeWrapper("Unsafe_Loadavg"); + const int max_nelem = 3; + double la[max_nelem]; + jint ret; + + typeArrayOop a = typeArrayOop(JNIHandles::resolve_non_null(loadavg)); + assert(a->is_typeArray(), "must be type array"); + + if (nelem < 0 || nelem > max_nelem || a->length() < nelem) { + ThreadToNativeFromVM ttnfv(thread); + throw_new(env, "ArrayIndexOutOfBoundsException"); + return -1; + } + + ret = os::loadavg(la, nelem); + if (ret == -1) return -1; + + // if successful, ret is the number of samples actually retrieved. + assert(ret >= 0 && ret <= max_nelem, "Unexpected loadavg return value"); + switch(ret) { + case 3: a->double_at_put(2, (jdouble)la[2]); // fall through + case 2: a->double_at_put(1, (jdouble)la[1]); // fall through + case 1: a->double_at_put(0, (jdouble)la[0]); break; + } + return ret; +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_PrefetchRead(JNIEnv* env, jclass ignored, jobject obj, jlong offset)) + UnsafeWrapper("Unsafe_PrefetchRead"); + oop p = JNIHandles::resolve(obj); + void* addr = index_oop_from_field_offset_long(p, 0); + Prefetch::read(addr, (intx)offset); +UNSAFE_END + +UNSAFE_ENTRY(void, Unsafe_PrefetchWrite(JNIEnv* env, jclass ignored, jobject obj, jlong offset)) + UnsafeWrapper("Unsafe_PrefetchWrite"); + oop p = JNIHandles::resolve(obj); + void* addr = index_oop_from_field_offset_long(p, 0); + Prefetch::write(addr, (intx)offset); +UNSAFE_END + + +/// JVM_RegisterUnsafeMethods + +#define ADR "J" + +#define LANG "Ljava/lang/" + +#define OBJ LANG"Object;" +#define CLS LANG"Class;" +#define CTR LANG"reflect/Constructor;" +#define FLD LANG"reflect/Field;" +#define MTH LANG"reflect/Method;" +#define THR LANG"Throwable;" + +#define DC0_Args LANG"String;[BII" +#define DC1_Args DC0_Args LANG"ClassLoader;" "Ljava/security/ProtectionDomain;" + +#define CC (char*) /*cast a literal from (const char*)*/ +#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) + +// define deprecated accessors for compabitility with 1.4.0 +#define DECLARE_GETSETOOP_140(Boolean, Z) \ + {CC"get"#Boolean, CC"("OBJ"I)"#Z, FN_PTR(Unsafe_Get##Boolean##140)}, \ + {CC"put"#Boolean, CC"("OBJ"I"#Z")V", FN_PTR(Unsafe_Set##Boolean##140)} + +// Note: In 1.4.1, getObject and kin take both int and long offsets. +#define DECLARE_GETSETOOP_141(Boolean, Z) \ + {CC"get"#Boolean, CC"("OBJ"J)"#Z, FN_PTR(Unsafe_Get##Boolean)}, \ + {CC"put"#Boolean, CC"("OBJ"J"#Z")V", FN_PTR(Unsafe_Set##Boolean)} + +// Note: In 1.5.0, there are volatile versions too +#define DECLARE_GETSETOOP(Boolean, Z) \ + {CC"get"#Boolean, CC"("OBJ"J)"#Z, FN_PTR(Unsafe_Get##Boolean)}, \ + {CC"put"#Boolean, CC"("OBJ"J"#Z")V", FN_PTR(Unsafe_Set##Boolean)}, \ + {CC"get"#Boolean"Volatile", CC"("OBJ"J)"#Z, FN_PTR(Unsafe_Get##Boolean##Volatile)}, \ + {CC"put"#Boolean"Volatile", CC"("OBJ"J"#Z")V", FN_PTR(Unsafe_Set##Boolean##Volatile)} + + +#define DECLARE_GETSETNATIVE(Byte, B) \ + {CC"get"#Byte, CC"("ADR")"#B, FN_PTR(Unsafe_GetNative##Byte)}, \ + {CC"put"#Byte, CC"("ADR#B")V", FN_PTR(Unsafe_SetNative##Byte)} + + + +// %%% These are temporarily supported until the SDK sources +// contain the necessarily updated Unsafe.java. +static JNINativeMethod methods_140[] = { + + {CC"getObject", CC"("OBJ"I)"OBJ"", FN_PTR(Unsafe_GetObject140)}, + {CC"putObject", CC"("OBJ"I"OBJ")V", FN_PTR(Unsafe_SetObject140)}, + + DECLARE_GETSETOOP_140(Boolean, Z), + DECLARE_GETSETOOP_140(Byte, B), + DECLARE_GETSETOOP_140(Short, S), + DECLARE_GETSETOOP_140(Char, C), + DECLARE_GETSETOOP_140(Int, I), + DECLARE_GETSETOOP_140(Long, J), + DECLARE_GETSETOOP_140(Float, F), + DECLARE_GETSETOOP_140(Double, D), + + DECLARE_GETSETNATIVE(Byte, B), + DECLARE_GETSETNATIVE(Short, S), + DECLARE_GETSETNATIVE(Char, C), + DECLARE_GETSETNATIVE(Int, I), + DECLARE_GETSETNATIVE(Long, J), + DECLARE_GETSETNATIVE(Float, F), + DECLARE_GETSETNATIVE(Double, D), + + {CC"getAddress", CC"("ADR")"ADR, FN_PTR(Unsafe_GetNativeAddress)}, + {CC"putAddress", CC"("ADR""ADR")V", FN_PTR(Unsafe_SetNativeAddress)}, + + {CC"allocateMemory", CC"(J)"ADR, FN_PTR(Unsafe_AllocateMemory)}, + {CC"reallocateMemory", CC"("ADR"J)"ADR, FN_PTR(Unsafe_ReallocateMemory)}, +// {CC"setMemory", CC"("ADR"JB)V", FN_PTR(Unsafe_SetMemory)}, +// {CC"copyMemory", CC"("ADR ADR"J)V", FN_PTR(Unsafe_CopyMemory)}, + {CC"freeMemory", CC"("ADR")V", FN_PTR(Unsafe_FreeMemory)}, + + {CC"fieldOffset", CC"("FLD")I", FN_PTR(Unsafe_FieldOffset)}, //deprecated + {CC"staticFieldBase", CC"("CLS")"OBJ, FN_PTR(Unsafe_StaticFieldBaseFromClass)}, //deprecated + {CC"ensureClassInitialized",CC"("CLS")V", FN_PTR(Unsafe_EnsureClassInitialized)}, + {CC"arrayBaseOffset", CC"("CLS")I", FN_PTR(Unsafe_ArrayBaseOffset)}, + {CC"arrayIndexScale", CC"("CLS")I", FN_PTR(Unsafe_ArrayIndexScale)}, + {CC"addressSize", CC"()I", FN_PTR(Unsafe_AddressSize)}, + {CC"pageSize", CC"()I", FN_PTR(Unsafe_PageSize)}, + + {CC"defineClass", CC"("DC0_Args")"CLS, FN_PTR(Unsafe_DefineClass0)}, + {CC"defineClass", CC"("DC1_Args")"CLS, FN_PTR(Unsafe_DefineClass1)}, + {CC"allocateInstance", CC"("CLS")"OBJ, FN_PTR(Unsafe_AllocateInstance)}, + {CC"monitorEnter", CC"("OBJ")V", FN_PTR(Unsafe_MonitorEnter)}, + {CC"monitorExit", CC"("OBJ")V", FN_PTR(Unsafe_MonitorExit)}, + {CC"throwException", CC"("THR")V", FN_PTR(Unsafe_ThrowException)} +}; + +// These are the old methods prior to the JSR 166 changes in 1.5.0 +static JNINativeMethod methods_141[] = { + + {CC"getObject", CC"("OBJ"J)"OBJ"", FN_PTR(Unsafe_GetObject)}, + {CC"putObject", CC"("OBJ"J"OBJ")V", FN_PTR(Unsafe_SetObject)}, + + DECLARE_GETSETOOP_141(Boolean, Z), + DECLARE_GETSETOOP_141(Byte, B), + DECLARE_GETSETOOP_141(Short, S), + DECLARE_GETSETOOP_141(Char, C), + DECLARE_GETSETOOP_141(Int, I), + DECLARE_GETSETOOP_141(Long, J), + DECLARE_GETSETOOP_141(Float, F), + DECLARE_GETSETOOP_141(Double, D), + + DECLARE_GETSETNATIVE(Byte, B), + DECLARE_GETSETNATIVE(Short, S), + DECLARE_GETSETNATIVE(Char, C), + DECLARE_GETSETNATIVE(Int, I), + DECLARE_GETSETNATIVE(Long, J), + DECLARE_GETSETNATIVE(Float, F), + DECLARE_GETSETNATIVE(Double, D), + + {CC"getAddress", CC"("ADR")"ADR, FN_PTR(Unsafe_GetNativeAddress)}, + {CC"putAddress", CC"("ADR""ADR")V", FN_PTR(Unsafe_SetNativeAddress)}, + + {CC"allocateMemory", CC"(J)"ADR, FN_PTR(Unsafe_AllocateMemory)}, + {CC"reallocateMemory", CC"("ADR"J)"ADR, FN_PTR(Unsafe_ReallocateMemory)}, +// {CC"setMemory", CC"("ADR"JB)V", FN_PTR(Unsafe_SetMemory)}, +// {CC"copyMemory", CC"("ADR ADR"J)V", FN_PTR(Unsafe_CopyMemory)}, + {CC"freeMemory", CC"("ADR")V", FN_PTR(Unsafe_FreeMemory)}, + + {CC"objectFieldOffset", CC"("FLD")J", FN_PTR(Unsafe_ObjectFieldOffset)}, + {CC"staticFieldOffset", CC"("FLD")J", FN_PTR(Unsafe_StaticFieldOffset)}, + {CC"staticFieldBase", CC"("FLD")"OBJ, FN_PTR(Unsafe_StaticFieldBaseFromField)}, + {CC"ensureClassInitialized",CC"("CLS")V", FN_PTR(Unsafe_EnsureClassInitialized)}, + {CC"arrayBaseOffset", CC"("CLS")I", FN_PTR(Unsafe_ArrayBaseOffset)}, + {CC"arrayIndexScale", CC"("CLS")I", FN_PTR(Unsafe_ArrayIndexScale)}, + {CC"addressSize", CC"()I", FN_PTR(Unsafe_AddressSize)}, + {CC"pageSize", CC"()I", FN_PTR(Unsafe_PageSize)}, + + {CC"defineClass", CC"("DC0_Args")"CLS, FN_PTR(Unsafe_DefineClass0)}, + {CC"defineClass", CC"("DC1_Args")"CLS, FN_PTR(Unsafe_DefineClass1)}, + {CC"allocateInstance", CC"("CLS")"OBJ, FN_PTR(Unsafe_AllocateInstance)}, + {CC"monitorEnter", CC"("OBJ")V", FN_PTR(Unsafe_MonitorEnter)}, + {CC"monitorExit", CC"("OBJ")V", FN_PTR(Unsafe_MonitorExit)}, + {CC"throwException", CC"("THR")V", FN_PTR(Unsafe_ThrowException)} + +}; + +// These are the old methods prior to the JSR 166 changes in 1.6.0 +static JNINativeMethod methods_15[] = { + + {CC"getObject", CC"("OBJ"J)"OBJ"", FN_PTR(Unsafe_GetObject)}, + {CC"putObject", CC"("OBJ"J"OBJ")V", FN_PTR(Unsafe_SetObject)}, + {CC"getObjectVolatile",CC"("OBJ"J)"OBJ"", FN_PTR(Unsafe_GetObjectVolatile)}, + {CC"putObjectVolatile",CC"("OBJ"J"OBJ")V", FN_PTR(Unsafe_SetObjectVolatile)}, + + + DECLARE_GETSETOOP(Boolean, Z), + DECLARE_GETSETOOP(Byte, B), + DECLARE_GETSETOOP(Short, S), + DECLARE_GETSETOOP(Char, C), + DECLARE_GETSETOOP(Int, I), + DECLARE_GETSETOOP(Long, J), + DECLARE_GETSETOOP(Float, F), + DECLARE_GETSETOOP(Double, D), + + DECLARE_GETSETNATIVE(Byte, B), + DECLARE_GETSETNATIVE(Short, S), + DECLARE_GETSETNATIVE(Char, C), + DECLARE_GETSETNATIVE(Int, I), + DECLARE_GETSETNATIVE(Long, J), + DECLARE_GETSETNATIVE(Float, F), + DECLARE_GETSETNATIVE(Double, D), + + {CC"getAddress", CC"("ADR")"ADR, FN_PTR(Unsafe_GetNativeAddress)}, + {CC"putAddress", CC"("ADR""ADR")V", FN_PTR(Unsafe_SetNativeAddress)}, + + {CC"allocateMemory", CC"(J)"ADR, FN_PTR(Unsafe_AllocateMemory)}, + {CC"reallocateMemory", CC"("ADR"J)"ADR, FN_PTR(Unsafe_ReallocateMemory)}, +// {CC"setMemory", CC"("ADR"JB)V", FN_PTR(Unsafe_SetMemory)}, +// {CC"copyMemory", CC"("ADR ADR"J)V", FN_PTR(Unsafe_CopyMemory)}, + {CC"freeMemory", CC"("ADR")V", FN_PTR(Unsafe_FreeMemory)}, + + {CC"objectFieldOffset", CC"("FLD")J", FN_PTR(Unsafe_ObjectFieldOffset)}, + {CC"staticFieldOffset", CC"("FLD")J", FN_PTR(Unsafe_StaticFieldOffset)}, + {CC"staticFieldBase", CC"("FLD")"OBJ, FN_PTR(Unsafe_StaticFieldBaseFromField)}, + {CC"ensureClassInitialized",CC"("CLS")V", FN_PTR(Unsafe_EnsureClassInitialized)}, + {CC"arrayBaseOffset", CC"("CLS")I", FN_PTR(Unsafe_ArrayBaseOffset)}, + {CC"arrayIndexScale", CC"("CLS")I", FN_PTR(Unsafe_ArrayIndexScale)}, + {CC"addressSize", CC"()I", FN_PTR(Unsafe_AddressSize)}, + {CC"pageSize", CC"()I", FN_PTR(Unsafe_PageSize)}, + + {CC"defineClass", CC"("DC0_Args")"CLS, FN_PTR(Unsafe_DefineClass0)}, + {CC"defineClass", CC"("DC1_Args")"CLS, FN_PTR(Unsafe_DefineClass1)}, + {CC"allocateInstance", CC"("CLS")"OBJ, FN_PTR(Unsafe_AllocateInstance)}, + {CC"monitorEnter", CC"("OBJ")V", FN_PTR(Unsafe_MonitorEnter)}, + {CC"monitorExit", CC"("OBJ")V", FN_PTR(Unsafe_MonitorExit)}, + {CC"throwException", CC"("THR")V", FN_PTR(Unsafe_ThrowException)}, + {CC"compareAndSwapObject", CC"("OBJ"J"OBJ""OBJ")Z", FN_PTR(Unsafe_CompareAndSwapObject)}, + {CC"compareAndSwapInt", CC"("OBJ"J""I""I"")Z", FN_PTR(Unsafe_CompareAndSwapInt)}, + {CC"compareAndSwapLong", CC"("OBJ"J""J""J"")Z", FN_PTR(Unsafe_CompareAndSwapLong)}, + {CC"park", CC"(ZJ)V", FN_PTR(Unsafe_Park)}, + {CC"unpark", CC"("OBJ")V", FN_PTR(Unsafe_Unpark)} + +}; + +// These are the correct methods, moving forward: +static JNINativeMethod methods[] = { + + {CC"getObject", CC"("OBJ"J)"OBJ"", FN_PTR(Unsafe_GetObject)}, + {CC"putObject", CC"("OBJ"J"OBJ")V", FN_PTR(Unsafe_SetObject)}, + {CC"getObjectVolatile",CC"("OBJ"J)"OBJ"", FN_PTR(Unsafe_GetObjectVolatile)}, + {CC"putObjectVolatile",CC"("OBJ"J"OBJ")V", FN_PTR(Unsafe_SetObjectVolatile)}, + + + DECLARE_GETSETOOP(Boolean, Z), + DECLARE_GETSETOOP(Byte, B), + DECLARE_GETSETOOP(Short, S), + DECLARE_GETSETOOP(Char, C), + DECLARE_GETSETOOP(Int, I), + DECLARE_GETSETOOP(Long, J), + DECLARE_GETSETOOP(Float, F), + DECLARE_GETSETOOP(Double, D), + + DECLARE_GETSETNATIVE(Byte, B), + DECLARE_GETSETNATIVE(Short, S), + DECLARE_GETSETNATIVE(Char, C), + DECLARE_GETSETNATIVE(Int, I), + DECLARE_GETSETNATIVE(Long, J), + DECLARE_GETSETNATIVE(Float, F), + DECLARE_GETSETNATIVE(Double, D), + + {CC"getAddress", CC"("ADR")"ADR, FN_PTR(Unsafe_GetNativeAddress)}, + {CC"putAddress", CC"("ADR""ADR")V", FN_PTR(Unsafe_SetNativeAddress)}, + + {CC"allocateMemory", CC"(J)"ADR, FN_PTR(Unsafe_AllocateMemory)}, + {CC"reallocateMemory", CC"("ADR"J)"ADR, FN_PTR(Unsafe_ReallocateMemory)}, +// {CC"setMemory", CC"("ADR"JB)V", FN_PTR(Unsafe_SetMemory)}, +// {CC"copyMemory", CC"("ADR ADR"J)V", FN_PTR(Unsafe_CopyMemory)}, + {CC"freeMemory", CC"("ADR")V", FN_PTR(Unsafe_FreeMemory)}, + + {CC"objectFieldOffset", CC"("FLD")J", FN_PTR(Unsafe_ObjectFieldOffset)}, + {CC"staticFieldOffset", CC"("FLD")J", FN_PTR(Unsafe_StaticFieldOffset)}, + {CC"staticFieldBase", CC"("FLD")"OBJ, FN_PTR(Unsafe_StaticFieldBaseFromField)}, + {CC"ensureClassInitialized",CC"("CLS")V", FN_PTR(Unsafe_EnsureClassInitialized)}, + {CC"arrayBaseOffset", CC"("CLS")I", FN_PTR(Unsafe_ArrayBaseOffset)}, + {CC"arrayIndexScale", CC"("CLS")I", FN_PTR(Unsafe_ArrayIndexScale)}, + {CC"addressSize", CC"()I", FN_PTR(Unsafe_AddressSize)}, + {CC"pageSize", CC"()I", FN_PTR(Unsafe_PageSize)}, + + {CC"defineClass", CC"("DC0_Args")"CLS, FN_PTR(Unsafe_DefineClass0)}, + {CC"defineClass", CC"("DC1_Args")"CLS, FN_PTR(Unsafe_DefineClass1)}, + {CC"allocateInstance", CC"("CLS")"OBJ, FN_PTR(Unsafe_AllocateInstance)}, + {CC"monitorEnter", CC"("OBJ")V", FN_PTR(Unsafe_MonitorEnter)}, + {CC"monitorExit", CC"("OBJ")V", FN_PTR(Unsafe_MonitorExit)}, + {CC"tryMonitorEnter", CC"("OBJ")Z", FN_PTR(Unsafe_TryMonitorEnter)}, + {CC"throwException", CC"("THR")V", FN_PTR(Unsafe_ThrowException)}, + {CC"compareAndSwapObject", CC"("OBJ"J"OBJ""OBJ")Z", FN_PTR(Unsafe_CompareAndSwapObject)}, + {CC"compareAndSwapInt", CC"("OBJ"J""I""I"")Z", FN_PTR(Unsafe_CompareAndSwapInt)}, + {CC"compareAndSwapLong", CC"("OBJ"J""J""J"")Z", FN_PTR(Unsafe_CompareAndSwapLong)}, + {CC"putOrderedObject", CC"("OBJ"J"OBJ")V", FN_PTR(Unsafe_SetOrderedObject)}, + {CC"putOrderedInt", CC"("OBJ"JI)V", FN_PTR(Unsafe_SetOrderedInt)}, + {CC"putOrderedLong", CC"("OBJ"JJ)V", FN_PTR(Unsafe_SetOrderedLong)}, + {CC"park", CC"(ZJ)V", FN_PTR(Unsafe_Park)}, + {CC"unpark", CC"("OBJ")V", FN_PTR(Unsafe_Unpark)} + +// {CC"getLoadAverage", CC"([DI)I", FN_PTR(Unsafe_Loadavg)}, + +// {CC"prefetchRead", CC"("OBJ"J)V", FN_PTR(Unsafe_PrefetchRead)}, +// {CC"prefetchWrite", CC"("OBJ"J)V", FN_PTR(Unsafe_PrefetchWrite)} +// {CC"prefetchReadStatic", CC"("OBJ"J)V", FN_PTR(Unsafe_PrefetchRead)}, +// {CC"prefetchWriteStatic",CC"("OBJ"J)V", FN_PTR(Unsafe_PrefetchWrite)} + +}; + +JNINativeMethod loadavg_method[] = { + {CC"getLoadAverage", CC"([DI)I", FN_PTR(Unsafe_Loadavg)} +}; + +JNINativeMethod prefetch_methods[] = { + {CC"prefetchRead", CC"("OBJ"J)V", FN_PTR(Unsafe_PrefetchRead)}, + {CC"prefetchWrite", CC"("OBJ"J)V", FN_PTR(Unsafe_PrefetchWrite)}, + {CC"prefetchReadStatic", CC"("OBJ"J)V", FN_PTR(Unsafe_PrefetchRead)}, + {CC"prefetchWriteStatic",CC"("OBJ"J)V", FN_PTR(Unsafe_PrefetchWrite)} +}; + +JNINativeMethod memcopy_methods[] = { + {CC"copyMemory", CC"("OBJ"J"OBJ"JJ)V", FN_PTR(Unsafe_CopyMemory2)}, + {CC"setMemory", CC"("OBJ"JJB)V", FN_PTR(Unsafe_SetMemory2)} +}; + +JNINativeMethod memcopy_methods_15[] = { + {CC"setMemory", CC"("ADR"JB)V", FN_PTR(Unsafe_SetMemory)}, + {CC"copyMemory", CC"("ADR ADR"J)V", FN_PTR(Unsafe_CopyMemory)} +}; + + +#undef CC +#undef FN_PTR + +#undef ADR +#undef LANG +#undef OBJ +#undef CLS +#undef CTR +#undef FLD +#undef MTH +#undef THR +#undef DC0_Args +#undef DC1_Args + +#undef DECLARE_GETSETOOP +#undef DECLARE_GETSETNATIVE + + +// This one function is exported, used by NativeLookup. +// The Unsafe_xxx functions above are called only from the interpreter. +// The optimizer looks at names and signatures to recognize +// individual functions. + +JVM_ENTRY(void, JVM_RegisterUnsafeMethods(JNIEnv *env, jclass unsafecls)) + UnsafeWrapper("JVM_RegisterUnsafeMethods"); + { + ThreadToNativeFromVM ttnfv(thread); + { + env->RegisterNatives(unsafecls, loadavg_method, sizeof(loadavg_method)/sizeof(JNINativeMethod)); + if (env->ExceptionOccurred()) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + tty->print_cr("Warning: SDK 1.6 Unsafe.loadavg not found."); + } + env->ExceptionClear(); + } + } + { + env->RegisterNatives(unsafecls, prefetch_methods, sizeof(prefetch_methods)/sizeof(JNINativeMethod)); + if (env->ExceptionOccurred()) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + tty->print_cr("Warning: SDK 1.6 Unsafe.prefetchRead/Write not found."); + } + env->ExceptionClear(); + } + } + { + env->RegisterNatives(unsafecls, memcopy_methods, sizeof(memcopy_methods)/sizeof(JNINativeMethod)); + if (env->ExceptionOccurred()) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + tty->print_cr("Warning: SDK 1.7 Unsafe.copyMemory not found."); + } + env->ExceptionClear(); + env->RegisterNatives(unsafecls, memcopy_methods_15, sizeof(memcopy_methods_15)/sizeof(JNINativeMethod)); + if (env->ExceptionOccurred()) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + tty->print_cr("Warning: SDK 1.5 Unsafe.copyMemory not found."); + } + env->ExceptionClear(); + } + } + } + int status = env->RegisterNatives(unsafecls, methods, sizeof(methods)/sizeof(JNINativeMethod)); + if (env->ExceptionOccurred()) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + tty->print_cr("Warning: SDK 1.6 version of Unsafe not found."); + } + env->ExceptionClear(); + // %%% For now, be backward compatible with an older class: + status = env->RegisterNatives(unsafecls, methods_15, sizeof(methods_15)/sizeof(JNINativeMethod)); + } + if (env->ExceptionOccurred()) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + tty->print_cr("Warning: SDK 1.5 version of Unsafe not found."); + } + env->ExceptionClear(); + // %%% For now, be backward compatible with an older class: + status = env->RegisterNatives(unsafecls, methods_141, sizeof(methods_141)/sizeof(JNINativeMethod)); + } + if (env->ExceptionOccurred()) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + tty->print_cr("Warning: SDK 1.4.1 version of Unsafe not found."); + } + env->ExceptionClear(); + // %%% For now, be backward compatible with an older class: + status = env->RegisterNatives(unsafecls, methods_140, sizeof(methods_140)/sizeof(JNINativeMethod)); + } + guarantee(status == 0, "register unsafe natives"); + } +JVM_END diff --git a/hotspot/src/share/vm/runtime/aprofiler.cpp b/hotspot/src/share/vm/runtime/aprofiler.cpp new file mode 100644 index 00000000000..4787ba56f1b --- /dev/null +++ b/hotspot/src/share/vm/runtime/aprofiler.cpp @@ -0,0 +1,172 @@ +/* + * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_aprofiler.cpp.incl" + + +bool AllocationProfiler::_active = false; +GrowableArray* AllocationProfiler::_print_array = NULL; + + +class AllocProfClosure : public ObjectClosure { + public: + void do_object(oop obj) { + Klass* k = obj->blueprint(); + k->set_alloc_count(k->alloc_count() + 1); + k->set_alloc_size(k->alloc_size() + obj->size()); + } +}; + + +#ifndef PRODUCT + +class AllocProfResetClosure : public ObjectClosure { + public: + void do_object(oop obj) { + if (obj->is_klass()) { + Klass* k = Klass::cast(klassOop(obj)); + k->set_alloc_count(0); + k->set_alloc_size(0); + } + } +}; + +#endif + + +void AllocationProfiler::iterate_since_last_gc() { + if (is_active()) { + AllocProfClosure blk; + GenCollectedHeap* heap = GenCollectedHeap::heap(); + heap->object_iterate_since_last_GC(&blk); + } +} + + +void AllocationProfiler::engage() { + _active = true; +} + + +void AllocationProfiler::disengage() { + _active = false; +} + + +void AllocationProfiler::add_class_to_array(klassOop k) { + _print_array->append(k); +} + + +void AllocationProfiler::add_classes_to_array(klassOop k) { + // Iterate over klass and all array klasses for klass + k->klass_part()->with_array_klasses_do(&AllocationProfiler::add_class_to_array); +} + + +int AllocationProfiler::compare_classes(klassOop* k1, klassOop* k2) { + // Sort by total allocation size + return (*k2)->klass_part()->alloc_size() - (*k1)->klass_part()->alloc_size(); +} + + +int AllocationProfiler::average(size_t alloc_size, int alloc_count) { + return (int) ((double) (alloc_size * BytesPerWord) / MAX2(alloc_count, 1) + 0.5); +} + + +void AllocationProfiler::sort_and_print_array(size_t cutoff) { + _print_array->sort(&AllocationProfiler::compare_classes); + tty->print_cr("________________Size" + "__Instances" + "__Average" + "__Class________________"); + size_t total_alloc_size = 0; + int total_alloc_count = 0; + for (int index = 0; index < _print_array->length(); index++) { + klassOop k = _print_array->at(index); + size_t alloc_size = k->klass_part()->alloc_size(); + if (alloc_size > cutoff) { + int alloc_count = k->klass_part()->alloc_count(); +#ifdef PRODUCT + const char* name = k->klass_part()->external_name(); +#else + const char* name = k->klass_part()->internal_name(); +#endif + tty->print_cr("%20u %10u %8u %s", + alloc_size * BytesPerWord, + alloc_count, + average(alloc_size, alloc_count), + name); + total_alloc_size += alloc_size; + total_alloc_count += alloc_count; + } + } + tty->print_cr("%20u %10u %8u --total--", + total_alloc_size * BytesPerWord, + total_alloc_count, + average(total_alloc_size, total_alloc_count)); + tty->cr(); +} + + +void AllocationProfiler::print(size_t cutoff) { + ResourceMark rm; + assert(!is_active(), "AllocationProfiler cannot be active while printing profile"); + + tty->cr(); + tty->print_cr("Allocation profile (sizes in bytes, cutoff = %ld bytes):", cutoff * BytesPerWord); + tty->cr(); + + // Print regular instance klasses and basic type array klasses + _print_array = new GrowableArray(SystemDictionary::number_of_classes()*2); + SystemDictionary::classes_do(&add_classes_to_array); + Universe::basic_type_classes_do(&add_classes_to_array); + sort_and_print_array(cutoff); + + #ifndef PRODUCT + tty->print_cr("Allocation profile for system classes (sizes in bytes, cutoff = %d bytes):", cutoff * BytesPerWord); + tty->cr(); + + // Print system klasses (methods, symbols, constant pools, etc.) + _print_array = new GrowableArray(64); + Universe::system_classes_do(&add_classes_to_array); + sort_and_print_array(cutoff); + + tty->print_cr("Permanent generation dump (sizes in bytes, cutoff = %d bytes):", cutoff * BytesPerWord); + tty->cr(); + + AllocProfResetClosure resetblk; + Universe::heap()->permanent_object_iterate(&resetblk); + AllocProfClosure blk; + Universe::heap()->permanent_object_iterate(&blk); + + _print_array = new GrowableArray(SystemDictionary::number_of_classes()*2); + SystemDictionary::classes_do(&add_classes_to_array); + Universe::basic_type_classes_do(&add_classes_to_array); + Universe::system_classes_do(&add_classes_to_array); + sort_and_print_array(cutoff); + #endif +} diff --git a/hotspot/src/share/vm/runtime/aprofiler.hpp b/hotspot/src/share/vm/runtime/aprofiler.hpp new file mode 100644 index 00000000000..3a7b9b01ba2 --- /dev/null +++ b/hotspot/src/share/vm/runtime/aprofiler.hpp @@ -0,0 +1,60 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A simple allocation profiler for Java. The profiler collects and prints +// the number and total size of instances allocated per class, including +// array classes. +// +// The profiler is currently global for all threads. It can be changed to a +// per threads profiler by keeping a more elaborate data structure and calling +// iterate_since_last_scavenge at thread switches. + + +class AllocationProfiler: AllStatic { + friend class GenCollectedHeap; + friend class MarkSweep; + private: + static bool _active; // tells whether profiler is active + static GrowableArray* _print_array; // temporary array for printing + + // Utility printing functions + static void add_class_to_array(klassOop k); + static void add_classes_to_array(klassOop k); + static int compare_classes(klassOop* k1, klassOop* k2); + static int average(size_t alloc_size, int alloc_count); + static void sort_and_print_array(size_t cutoff); + + // Call for collecting allocation information. Called at scavenge, mark-sweep and disengage. + static void iterate_since_last_gc(); + + public: + // Start profiler + static void engage(); + // Stop profiler + static void disengage(); + // Tells whether profiler is active + static bool is_active() { return _active; } + // Print profile + static void print(size_t cutoff); // Cutoff in total allocation size (in words) +}; diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp new file mode 100644 index 00000000000..5d0329ad046 --- /dev/null +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -0,0 +1,2654 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_arguments.cpp.incl" + +#define DEFAULT_VENDOR_URL_BUG "http://java.sun.com/webapps/bugreport/crash.jsp" +#define DEFAULT_JAVA_LAUNCHER "generic" + +char** Arguments::_jvm_flags_array = NULL; +int Arguments::_num_jvm_flags = 0; +char** Arguments::_jvm_args_array = NULL; +int Arguments::_num_jvm_args = 0; +char* Arguments::_java_command = NULL; +SystemProperty* Arguments::_system_properties = NULL; +const char* Arguments::_gc_log_filename = NULL; +bool Arguments::_has_profile = false; +bool Arguments::_has_alloc_profile = false; +uintx Arguments::_initial_heap_size = 0; +uintx Arguments::_min_heap_size = 0; +Arguments::Mode Arguments::_mode = _mixed; +bool Arguments::_java_compiler = false; +bool Arguments::_xdebug_mode = false; +const char* Arguments::_java_vendor_url_bug = DEFAULT_VENDOR_URL_BUG; +const char* Arguments::_sun_java_launcher = DEFAULT_JAVA_LAUNCHER; +int Arguments::_sun_java_launcher_pid = -1; + +// These parameters are reset in method parse_vm_init_args(JavaVMInitArgs*) +bool Arguments::_AlwaysCompileLoopMethods = AlwaysCompileLoopMethods; +bool Arguments::_UseOnStackReplacement = UseOnStackReplacement; +bool Arguments::_BackgroundCompilation = BackgroundCompilation; +bool Arguments::_ClipInlining = ClipInlining; +intx Arguments::_Tier2CompileThreshold = Tier2CompileThreshold; + +char* Arguments::SharedArchivePath = NULL; + +AgentLibraryList Arguments::_libraryList; +AgentLibraryList Arguments::_agentList; + +abort_hook_t Arguments::_abort_hook = NULL; +exit_hook_t Arguments::_exit_hook = NULL; +vfprintf_hook_t Arguments::_vfprintf_hook = NULL; + + +SystemProperty *Arguments::_java_ext_dirs = NULL; +SystemProperty *Arguments::_java_endorsed_dirs = NULL; +SystemProperty *Arguments::_sun_boot_library_path = NULL; +SystemProperty *Arguments::_java_library_path = NULL; +SystemProperty *Arguments::_java_home = NULL; +SystemProperty *Arguments::_java_class_path = NULL; +SystemProperty *Arguments::_sun_boot_class_path = NULL; + +char* Arguments::_meta_index_path = NULL; +char* Arguments::_meta_index_dir = NULL; + +static bool force_client_mode = false; + +// Check if head of 'option' matches 'name', and sets 'tail' remaining part of option string + +static bool match_option(const JavaVMOption *option, const char* name, + const char** tail) { + int len = (int)strlen(name); + if (strncmp(option->optionString, name, len) == 0) { + *tail = option->optionString + len; + return true; + } else { + return false; + } +} + +static void logOption(const char* opt) { + if (PrintVMOptions) { + jio_fprintf(defaultStream::output_stream(), "VM option '%s'\n", opt); + } +} + +// Process java launcher properties. +void Arguments::process_sun_java_launcher_properties(JavaVMInitArgs* args) { + // See if sun.java.launcher or sun.java.launcher.pid is defined. + // Must do this before setting up other system properties, + // as some of them may depend on launcher type. + for (int index = 0; index < args->nOptions; index++) { + const JavaVMOption* option = args->options + index; + const char* tail; + + if (match_option(option, "-Dsun.java.launcher=", &tail)) { + process_java_launcher_argument(tail, option->extraInfo); + continue; + } + if (match_option(option, "-Dsun.java.launcher.pid=", &tail)) { + _sun_java_launcher_pid = atoi(tail); + continue; + } + } +} + +// Initialize system properties key and value. +void Arguments::init_system_properties() { + + PropertyList_add(&_system_properties, new SystemProperty("java.vm.specification.version", "1.0", false)); + PropertyList_add(&_system_properties, new SystemProperty("java.vm.specification.name", + "Java Virtual Machine Specification", false)); + PropertyList_add(&_system_properties, new SystemProperty("java.vm.specification.vendor", + "Sun Microsystems Inc.", false)); + PropertyList_add(&_system_properties, new SystemProperty("java.vm.version", VM_Version::vm_release(), false)); + PropertyList_add(&_system_properties, new SystemProperty("java.vm.name", VM_Version::vm_name(), false)); + PropertyList_add(&_system_properties, new SystemProperty("java.vm.vendor", VM_Version::vm_vendor(), false)); + PropertyList_add(&_system_properties, new SystemProperty("java.vm.info", VM_Version::vm_info_string(), true)); + + // following are JVMTI agent writeable properties. + // Properties values are set to NULL and they are + // os specific they are initialized in os::init_system_properties_values(). + _java_ext_dirs = new SystemProperty("java.ext.dirs", NULL, true); + _java_endorsed_dirs = new SystemProperty("java.endorsed.dirs", NULL, true); + _sun_boot_library_path = new SystemProperty("sun.boot.library.path", NULL, true); + _java_library_path = new SystemProperty("java.library.path", NULL, true); + _java_home = new SystemProperty("java.home", NULL, true); + _sun_boot_class_path = new SystemProperty("sun.boot.class.path", NULL, true); + + _java_class_path = new SystemProperty("java.class.path", "", true); + + // Add to System Property list. + PropertyList_add(&_system_properties, _java_ext_dirs); + PropertyList_add(&_system_properties, _java_endorsed_dirs); + PropertyList_add(&_system_properties, _sun_boot_library_path); + PropertyList_add(&_system_properties, _java_library_path); + PropertyList_add(&_system_properties, _java_home); + PropertyList_add(&_system_properties, _java_class_path); + PropertyList_add(&_system_properties, _sun_boot_class_path); + + // Set OS specific system properties values + os::init_system_properties_values(); +} + +// String containing commands that will be ignored and cause a +// warning to be issued. These commands should be accepted +// for 1.6 but not 1.7. The string should be cleared at the +// beginning of 1.7. +static const char* obsolete_jvm_flags_1_5_0[] = { + "UseTrainGC", + "UseSpecialLargeObjectHandling", + "UseOversizedCarHandling", + "TraceCarAllocation", + "PrintTrainGCProcessingStats", + "LogOfCarSpaceSize", + "OversizedCarThreshold", + "MinTickInterval", + "DefaultTickInterval", + "MaxTickInterval", + "DelayTickAdjustment", + "ProcessingToTenuringRatio", + "MinTrainLength", + 0}; + +bool Arguments::made_obsolete_in_1_5_0(const char *s) { + int i = 0; + while (obsolete_jvm_flags_1_5_0[i] != NULL) { + // =xxx form + // [-|+] form + if ((strncmp(obsolete_jvm_flags_1_5_0[i], s, + strlen(obsolete_jvm_flags_1_5_0[i])) == 0) || + ((s[0] == '+' || s[0] == '-') && + (strncmp(obsolete_jvm_flags_1_5_0[i], &s[1], + strlen(obsolete_jvm_flags_1_5_0[i])) == 0))) { + return true; + } + i++; + } + return false; +} + +// Constructs the system class path (aka boot class path) from the following +// components, in order: +// +// prefix // from -Xbootclasspath/p:... +// endorsed // the expansion of -Djava.endorsed.dirs=... +// base // from os::get_system_properties() or -Xbootclasspath= +// suffix // from -Xbootclasspath/a:... +// +// java.endorsed.dirs is a list of directories; any jar or zip files in the +// directories are added to the sysclasspath just before the base. +// +// This could be AllStatic, but it isn't needed after argument processing is +// complete. +class SysClassPath: public StackObj { +public: + SysClassPath(const char* base); + ~SysClassPath(); + + inline void set_base(const char* base); + inline void add_prefix(const char* prefix); + inline void add_suffix(const char* suffix); + inline void reset_path(const char* base); + + // Expand the jar/zip files in each directory listed by the java.endorsed.dirs + // property. Must be called after all command-line arguments have been + // processed (in particular, -Djava.endorsed.dirs=...) and before calling + // combined_path(). + void expand_endorsed(); + + inline const char* get_base() const { return _items[_scp_base]; } + inline const char* get_prefix() const { return _items[_scp_prefix]; } + inline const char* get_suffix() const { return _items[_scp_suffix]; } + inline const char* get_endorsed() const { return _items[_scp_endorsed]; } + + // Combine all the components into a single c-heap-allocated string; caller + // must free the string if/when no longer needed. + char* combined_path(); + +private: + // Utility routines. + static char* add_to_path(const char* path, const char* str, bool prepend); + static char* add_jars_to_path(char* path, const char* directory); + + inline void reset_item_at(int index); + + // Array indices for the items that make up the sysclasspath. All except the + // base are allocated in the C heap and freed by this class. + enum { + _scp_prefix, // from -Xbootclasspath/p:... + _scp_endorsed, // the expansion of -Djava.endorsed.dirs=... + _scp_base, // the default sysclasspath + _scp_suffix, // from -Xbootclasspath/a:... + _scp_nitems // the number of items, must be last. + }; + + const char* _items[_scp_nitems]; + DEBUG_ONLY(bool _expansion_done;) +}; + +SysClassPath::SysClassPath(const char* base) { + memset(_items, 0, sizeof(_items)); + _items[_scp_base] = base; + DEBUG_ONLY(_expansion_done = false;) +} + +SysClassPath::~SysClassPath() { + // Free everything except the base. + for (int i = 0; i < _scp_nitems; ++i) { + if (i != _scp_base) reset_item_at(i); + } + DEBUG_ONLY(_expansion_done = false;) +} + +inline void SysClassPath::set_base(const char* base) { + _items[_scp_base] = base; +} + +inline void SysClassPath::add_prefix(const char* prefix) { + _items[_scp_prefix] = add_to_path(_items[_scp_prefix], prefix, true); +} + +inline void SysClassPath::add_suffix(const char* suffix) { + _items[_scp_suffix] = add_to_path(_items[_scp_suffix], suffix, false); +} + +inline void SysClassPath::reset_item_at(int index) { + assert(index < _scp_nitems && index != _scp_base, "just checking"); + if (_items[index] != NULL) { + FREE_C_HEAP_ARRAY(char, _items[index]); + _items[index] = NULL; + } +} + +inline void SysClassPath::reset_path(const char* base) { + // Clear the prefix and suffix. + reset_item_at(_scp_prefix); + reset_item_at(_scp_suffix); + set_base(base); +} + +//------------------------------------------------------------------------------ + +void SysClassPath::expand_endorsed() { + assert(_items[_scp_endorsed] == NULL, "can only be called once."); + + const char* path = Arguments::get_property("java.endorsed.dirs"); + if (path == NULL) { + path = Arguments::get_endorsed_dir(); + assert(path != NULL, "no default for java.endorsed.dirs"); + } + + char* expanded_path = NULL; + const char separator = *os::path_separator(); + const char* const end = path + strlen(path); + while (path < end) { + const char* tmp_end = strchr(path, separator); + if (tmp_end == NULL) { + expanded_path = add_jars_to_path(expanded_path, path); + path = end; + } else { + char* dirpath = NEW_C_HEAP_ARRAY(char, tmp_end - path + 1); + memcpy(dirpath, path, tmp_end - path); + dirpath[tmp_end - path] = '\0'; + expanded_path = add_jars_to_path(expanded_path, dirpath); + FREE_C_HEAP_ARRAY(char, dirpath); + path = tmp_end + 1; + } + } + _items[_scp_endorsed] = expanded_path; + DEBUG_ONLY(_expansion_done = true;) +} + +// Combine the bootclasspath elements, some of which may be null, into a single +// c-heap-allocated string. +char* SysClassPath::combined_path() { + assert(_items[_scp_base] != NULL, "empty default sysclasspath"); + assert(_expansion_done, "must call expand_endorsed() first."); + + size_t lengths[_scp_nitems]; + size_t total_len = 0; + + const char separator = *os::path_separator(); + + // Get the lengths. + int i; + for (i = 0; i < _scp_nitems; ++i) { + if (_items[i] != NULL) { + lengths[i] = strlen(_items[i]); + // Include space for the separator char (or a NULL for the last item). + total_len += lengths[i] + 1; + } + } + assert(total_len > 0, "empty sysclasspath not allowed"); + + // Copy the _items to a single string. + char* cp = NEW_C_HEAP_ARRAY(char, total_len); + char* cp_tmp = cp; + for (i = 0; i < _scp_nitems; ++i) { + if (_items[i] != NULL) { + memcpy(cp_tmp, _items[i], lengths[i]); + cp_tmp += lengths[i]; + *cp_tmp++ = separator; + } + } + *--cp_tmp = '\0'; // Replace the extra separator. + return cp; +} + +// Note: path must be c-heap-allocated (or NULL); it is freed if non-null. +char* +SysClassPath::add_to_path(const char* path, const char* str, bool prepend) { + char *cp; + + assert(str != NULL, "just checking"); + if (path == NULL) { + size_t len = strlen(str) + 1; + cp = NEW_C_HEAP_ARRAY(char, len); + memcpy(cp, str, len); // copy the trailing null + } else { + const char separator = *os::path_separator(); + size_t old_len = strlen(path); + size_t str_len = strlen(str); + size_t len = old_len + str_len + 2; + + if (prepend) { + cp = NEW_C_HEAP_ARRAY(char, len); + char* cp_tmp = cp; + memcpy(cp_tmp, str, str_len); + cp_tmp += str_len; + *cp_tmp = separator; + memcpy(++cp_tmp, path, old_len + 1); // copy the trailing null + FREE_C_HEAP_ARRAY(char, path); + } else { + cp = REALLOC_C_HEAP_ARRAY(char, path, len); + char* cp_tmp = cp + old_len; + *cp_tmp = separator; + memcpy(++cp_tmp, str, str_len + 1); // copy the trailing null + } + } + return cp; +} + +// Scan the directory and append any jar or zip files found to path. +// Note: path must be c-heap-allocated (or NULL); it is freed if non-null. +char* SysClassPath::add_jars_to_path(char* path, const char* directory) { + DIR* dir = os::opendir(directory); + if (dir == NULL) return path; + + char dir_sep[2] = { '\0', '\0' }; + size_t directory_len = strlen(directory); + const char fileSep = *os::file_separator(); + if (directory[directory_len - 1] != fileSep) dir_sep[0] = fileSep; + + /* Scan the directory for jars/zips, appending them to path. */ + struct dirent *entry; + char *dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(directory)); + while ((entry = os::readdir(dir, (dirent *) dbuf)) != NULL) { + const char* name = entry->d_name; + const char* ext = name + strlen(name) - 4; + bool isJarOrZip = ext > name && + (os::file_name_strcmp(ext, ".jar") == 0 || + os::file_name_strcmp(ext, ".zip") == 0); + if (isJarOrZip) { + char* jarpath = NEW_C_HEAP_ARRAY(char, directory_len + 2 + strlen(name)); + sprintf(jarpath, "%s%s%s", directory, dir_sep, name); + path = add_to_path(path, jarpath, false); + FREE_C_HEAP_ARRAY(char, jarpath); + } + } + FREE_C_HEAP_ARRAY(char, dbuf); + os::closedir(dir); + return path; +} + +// Parses a memory size specification string. +static bool atomll(const char *s, jlong* result) { + jlong n = 0; + int args_read = sscanf(s, os::jlong_format_specifier(), &n); + if (args_read != 1) { + return false; + } + while (*s != '\0' && isdigit(*s)) { + s++; + } + // 4705540: illegal if more characters are found after the first non-digit + if (strlen(s) > 1) { + return false; + } + switch (*s) { + case 'T': case 't': + *result = n * G * K; + return true; + case 'G': case 'g': + *result = n * G; + return true; + case 'M': case 'm': + *result = n * M; + return true; + case 'K': case 'k': + *result = n * K; + return true; + case '\0': + *result = n; + return true; + default: + return false; + } +} + +Arguments::ArgsRange Arguments::check_memory_size(jlong size, jlong min_size) { + if (size < min_size) return arg_too_small; + // Check that size will fit in a size_t (only relevant on 32-bit) + if ((julong) size > max_uintx) return arg_too_big; + return arg_in_range; +} + +// Describe an argument out of range error +void Arguments::describe_range_error(ArgsRange errcode) { + switch(errcode) { + case arg_too_big: + jio_fprintf(defaultStream::error_stream(), + "The specified size exceeds the maximum " + "representable size.\n"); + break; + case arg_too_small: + case arg_unreadable: + case arg_in_range: + // do nothing for now + break; + default: + ShouldNotReachHere(); + } +} + +static bool set_bool_flag(char* name, bool value, FlagValueOrigin origin) { + return CommandLineFlags::boolAtPut(name, &value, origin); +} + + +static bool set_fp_numeric_flag(char* name, char* value, FlagValueOrigin origin) { + double v; + if (sscanf(value, "%lf", &v) != 1) { + return false; + } + + if (CommandLineFlags::doubleAtPut(name, &v, origin)) { + return true; + } + return false; +} + + +static bool set_numeric_flag(char* name, char* value, FlagValueOrigin origin) { + jlong v; + intx intx_v; + bool is_neg = false; + // Check the sign first since atomll() parses only unsigned values. + if (*value == '-') { + if (!CommandLineFlags::intxAt(name, &intx_v)) { + return false; + } + value++; + is_neg = true; + } + if (!atomll(value, &v)) { + return false; + } + intx_v = (intx) v; + if (is_neg) { + intx_v = -intx_v; + } + if (CommandLineFlags::intxAtPut(name, &intx_v, origin)) { + return true; + } + uintx uintx_v = (uintx) v; + if (!is_neg && CommandLineFlags::uintxAtPut(name, &uintx_v, origin)) { + return true; + } + return false; +} + + +static bool set_string_flag(char* name, const char* value, FlagValueOrigin origin) { + if (!CommandLineFlags::ccstrAtPut(name, &value, origin)) return false; + // Contract: CommandLineFlags always returns a pointer that needs freeing. + FREE_C_HEAP_ARRAY(char, value); + return true; +} + +static bool append_to_string_flag(char* name, const char* new_value, FlagValueOrigin origin) { + const char* old_value = ""; + if (!CommandLineFlags::ccstrAt(name, &old_value)) return false; + size_t old_len = old_value != NULL ? strlen(old_value) : 0; + size_t new_len = strlen(new_value); + const char* value; + char* free_this_too = NULL; + if (old_len == 0) { + value = new_value; + } else if (new_len == 0) { + value = old_value; + } else { + char* buf = NEW_C_HEAP_ARRAY(char, old_len + 1 + new_len + 1); + // each new setting adds another LINE to the switch: + sprintf(buf, "%s\n%s", old_value, new_value); + value = buf; + free_this_too = buf; + } + (void) CommandLineFlags::ccstrAtPut(name, &value, origin); + // CommandLineFlags always returns a pointer that needs freeing. + FREE_C_HEAP_ARRAY(char, value); + if (free_this_too != NULL) { + // CommandLineFlags made its own copy, so I must delete my own temp. buffer. + FREE_C_HEAP_ARRAY(char, free_this_too); + } + return true; +} + + +bool Arguments::parse_argument(const char* arg, FlagValueOrigin origin) { + + // range of acceptable characters spelled out for portability reasons +#define NAME_RANGE "[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]" +#define BUFLEN 255 + char name[BUFLEN+1]; + char dummy; + + if (sscanf(arg, "-%" XSTR(BUFLEN) NAME_RANGE "%c", name, &dummy) == 1) { + return set_bool_flag(name, false, origin); + } + if (sscanf(arg, "+%" XSTR(BUFLEN) NAME_RANGE "%c", name, &dummy) == 1) { + return set_bool_flag(name, true, origin); + } + + char punct; + if (sscanf(arg, "%" XSTR(BUFLEN) NAME_RANGE "%c", name, &punct) == 2 && punct == '=') { + const char* value = strchr(arg, '=') + 1; + Flag* flag = Flag::find_flag(name, strlen(name)); + if (flag != NULL && flag->is_ccstr()) { + if (flag->ccstr_accumulates()) { + return append_to_string_flag(name, value, origin); + } else { + if (value[0] == '\0') { + value = NULL; + } + return set_string_flag(name, value, origin); + } + } + } + + if (sscanf(arg, "%" XSTR(BUFLEN) NAME_RANGE ":%c", name, &punct) == 2 && punct == '=') { + const char* value = strchr(arg, '=') + 1; + // -XX:Foo:=xxx will reset the string flag to the given value. + if (value[0] == '\0') { + value = NULL; + } + return set_string_flag(name, value, origin); + } + +#define SIGNED_FP_NUMBER_RANGE "[-0123456789.]" +#define SIGNED_NUMBER_RANGE "[-0123456789]" +#define NUMBER_RANGE "[0123456789]" + char value[BUFLEN + 1]; + char value2[BUFLEN + 1]; + if (sscanf(arg, "%" XSTR(BUFLEN) NAME_RANGE "=" "%" XSTR(BUFLEN) SIGNED_NUMBER_RANGE "." "%" XSTR(BUFLEN) NUMBER_RANGE "%c", name, value, value2, &dummy) == 3) { + // Looks like a floating-point number -- try again with more lenient format string + if (sscanf(arg, "%" XSTR(BUFLEN) NAME_RANGE "=" "%" XSTR(BUFLEN) SIGNED_FP_NUMBER_RANGE "%c", name, value, &dummy) == 2) { + return set_fp_numeric_flag(name, value, origin); + } + } + +#define VALUE_RANGE "[-kmgtKMGT0123456789]" + if (sscanf(arg, "%" XSTR(BUFLEN) NAME_RANGE "=" "%" XSTR(BUFLEN) VALUE_RANGE "%c", name, value, &dummy) == 2) { + return set_numeric_flag(name, value, origin); + } + + return false; +} + + +void Arguments::add_string(char*** bldarray, int* count, const char* arg) { + assert(bldarray != NULL, "illegal argument"); + + if (arg == NULL) { + return; + } + + int index = *count; + + // expand the array and add arg to the last element + (*count)++; + if (*bldarray == NULL) { + *bldarray = NEW_C_HEAP_ARRAY(char*, *count); + } else { + *bldarray = REALLOC_C_HEAP_ARRAY(char*, *bldarray, *count); + } + (*bldarray)[index] = strdup(arg); +} + +void Arguments::build_jvm_args(const char* arg) { + add_string(&_jvm_args_array, &_num_jvm_args, arg); +} + +void Arguments::build_jvm_flags(const char* arg) { + add_string(&_jvm_flags_array, &_num_jvm_flags, arg); +} + +// utility function to return a string that concatenates all +// strings in a given char** array +const char* Arguments::build_resource_string(char** args, int count) { + if (args == NULL || count == 0) { + return NULL; + } + size_t length = strlen(args[0]) + 1; // add 1 for the null terminator + for (int i = 1; i < count; i++) { + length += strlen(args[i]) + 1; // add 1 for a space + } + char* s = NEW_RESOURCE_ARRAY(char, length); + strcpy(s, args[0]); + for (int j = 1; j < count; j++) { + strcat(s, " "); + strcat(s, args[j]); + } + return (const char*) s; +} + +void Arguments::print_on(outputStream* st) { + st->print_cr("VM Arguments:"); + if (num_jvm_flags() > 0) { + st->print("jvm_flags: "); print_jvm_flags_on(st); + } + if (num_jvm_args() > 0) { + st->print("jvm_args: "); print_jvm_args_on(st); + } + st->print_cr("java_command: %s", java_command() ? java_command() : ""); + st->print_cr("Launcher Type: %s", _sun_java_launcher); +} + +void Arguments::print_jvm_flags_on(outputStream* st) { + if (_num_jvm_flags > 0) { + for (int i=0; i < _num_jvm_flags; i++) { + st->print("%s ", _jvm_flags_array[i]); + } + st->print_cr(""); + } +} + +void Arguments::print_jvm_args_on(outputStream* st) { + if (_num_jvm_args > 0) { + for (int i=0; i < _num_jvm_args; i++) { + st->print("%s ", _jvm_args_array[i]); + } + st->print_cr(""); + } +} + +bool Arguments::process_argument(const char* arg, jboolean ignore_unrecognized, FlagValueOrigin origin) { + + if (parse_argument(arg, origin)) { + // do nothing + } else if (made_obsolete_in_1_5_0(arg)) { + jio_fprintf(defaultStream::error_stream(), + "Warning: The flag %s has been EOL'd as of 1.5.0 and will" + " be ignored\n", arg); + } else { + if (!ignore_unrecognized) { + jio_fprintf(defaultStream::error_stream(), + "Unrecognized VM option '%s'\n", arg); + // allow for commandline "commenting out" options like -XX:#+Verbose + if (strlen(arg) == 0 || arg[0] != '#') { + return false; + } + } + } + return true; +} + + +bool Arguments::process_settings_file(const char* file_name, bool should_exist, jboolean ignore_unrecognized) { + FILE* stream = fopen(file_name, "rb"); + if (stream == NULL) { + if (should_exist) { + jio_fprintf(defaultStream::error_stream(), + "Could not open settings file %s\n", file_name); + return false; + } else { + return true; + } + } + + char token[1024]; + int pos = 0; + + bool in_white_space = true; + bool in_comment = false; + bool in_quote = false; + char quote_c = 0; + bool result = true; + + int c = getc(stream); + while(c != EOF) { + if (in_white_space) { + if (in_comment) { + if (c == '\n') in_comment = false; + } else { + if (c == '#') in_comment = true; + else if (!isspace(c)) { + in_white_space = false; + token[pos++] = c; + } + } + } else { + if (c == '\n' || (!in_quote && isspace(c))) { + // token ends at newline, or at unquoted whitespace + // this allows a way to include spaces in string-valued options + token[pos] = '\0'; + logOption(token); + result &= process_argument(token, ignore_unrecognized, CONFIG_FILE); + build_jvm_flags(token); + pos = 0; + in_white_space = true; + in_quote = false; + } else if (!in_quote && (c == '\'' || c == '"')) { + in_quote = true; + quote_c = c; + } else if (in_quote && (c == quote_c)) { + in_quote = false; + } else { + token[pos++] = c; + } + } + c = getc(stream); + } + if (pos > 0) { + token[pos] = '\0'; + result &= process_argument(token, ignore_unrecognized, CONFIG_FILE); + build_jvm_flags(token); + } + fclose(stream); + return result; +} + +//============================================================================================================= +// Parsing of properties (-D) + +const char* Arguments::get_property(const char* key) { + return PropertyList_get_value(system_properties(), key); +} + +bool Arguments::add_property(const char* prop) { + const char* eq = strchr(prop, '='); + char* key; + // ns must be static--its address may be stored in a SystemProperty object. + const static char ns[1] = {0}; + char* value = (char *)ns; + + size_t key_len = (eq == NULL) ? strlen(prop) : (eq - prop); + key = AllocateHeap(key_len + 1, "add_property"); + strncpy(key, prop, key_len); + key[key_len] = '\0'; + + if (eq != NULL) { + size_t value_len = strlen(prop) - key_len - 1; + value = AllocateHeap(value_len + 1, "add_property"); + strncpy(value, &prop[key_len + 1], value_len + 1); + } + + if (strcmp(key, "java.compiler") == 0) { + process_java_compiler_argument(value); + FreeHeap(key); + if (eq != NULL) { + FreeHeap(value); + } + return true; + } + else if (strcmp(key, "sun.java.command") == 0) { + + _java_command = value; + + // don't add this property to the properties exposed to the java application + FreeHeap(key); + return true; + } + else if (strcmp(key, "sun.java.launcher.pid") == 0) { + // launcher.pid property is private and is processed + // in process_sun_java_launcher_properties(); + // the sun.java.launcher property is passed on to the java application + FreeHeap(key); + if (eq != NULL) { + FreeHeap(value); + } + return true; + } + else if (strcmp(key, "java.vendor.url.bug") == 0) { + // save it in _java_vendor_url_bug, so JVM fatal error handler can access + // its value without going through the property list or making a Java call. + _java_vendor_url_bug = value; + } + + // Create new property and add at the end of the list + PropertyList_unique_add(&_system_properties, key, value); + return true; +} + +//=========================================================================================================== +// Setting int/mixed/comp mode flags + +void Arguments::set_mode_flags(Mode mode) { + // Set up default values for all flags. + // If you add a flag to any of the branches below, + // add a default value for it here. + set_java_compiler(false); + _mode = mode; + + // Ensure Agent_OnLoad has the correct initial values. + // This may not be the final mode; mode may change later in onload phase. + PropertyList_unique_add(&_system_properties, "java.vm.info", + (char*)Abstract_VM_Version::vm_info_string()); + + UseInterpreter = true; + UseCompiler = true; + UseLoopCounter = true; + + // Default values may be platform/compiler dependent - + // use the saved values + ClipInlining = Arguments::_ClipInlining; + AlwaysCompileLoopMethods = Arguments::_AlwaysCompileLoopMethods; + UseOnStackReplacement = Arguments::_UseOnStackReplacement; + BackgroundCompilation = Arguments::_BackgroundCompilation; + Tier2CompileThreshold = Arguments::_Tier2CompileThreshold; + + // Change from defaults based on mode + switch (mode) { + default: + ShouldNotReachHere(); + break; + case _int: + UseCompiler = false; + UseLoopCounter = false; + AlwaysCompileLoopMethods = false; + UseOnStackReplacement = false; + break; + case _mixed: + // same as default + break; + case _comp: + UseInterpreter = false; + BackgroundCompilation = false; + ClipInlining = false; + break; + } +} + + +// Conflict: required to use shared spaces (-Xshare:on), but +// incompatible command line options were chosen. + +static void no_shared_spaces() { + if (RequireSharedSpaces) { + jio_fprintf(defaultStream::error_stream(), + "Class data sharing is inconsistent with other specified options.\n"); + vm_exit_during_initialization("Unable to use shared archive.", NULL); + } else { + FLAG_SET_DEFAULT(UseSharedSpaces, false); + } +} + + +// If the user has chosen ParallelGCThreads > 0, we set UseParNewGC +// if it's not explictly set or unset. If the user has chosen +// UseParNewGC and not explicitly set ParallelGCThreads we +// set it, unless this is a single cpu machine. +void Arguments::set_parnew_gc_flags() { + assert(!UseSerialGC && !UseParallelGC, "control point invariant"); + + if (FLAG_IS_DEFAULT(UseParNewGC) && ParallelGCThreads > 1) { + FLAG_SET_DEFAULT(UseParNewGC, true); + } else if (UseParNewGC && ParallelGCThreads == 0) { + FLAG_SET_DEFAULT(ParallelGCThreads, nof_parallel_gc_threads()); + if (FLAG_IS_DEFAULT(ParallelGCThreads) && ParallelGCThreads == 1) { + FLAG_SET_DEFAULT(UseParNewGC, false); + } + } + if (!UseParNewGC) { + FLAG_SET_DEFAULT(ParallelGCThreads, 0); + } else { + no_shared_spaces(); + + // By default YoungPLABSize and OldPLABSize are set to 4096 and 1024 correspondinly, + // these settings are default for Parallel Scavenger. For ParNew+Tenured configuration + // we set them to 1024 and 1024. + // See CR 6362902. + if (FLAG_IS_DEFAULT(YoungPLABSize)) { + FLAG_SET_DEFAULT(YoungPLABSize, (intx)1024); + } + if (FLAG_IS_DEFAULT(OldPLABSize)) { + FLAG_SET_DEFAULT(OldPLABSize, (intx)1024); + } + + // AlwaysTenure flag should make ParNew to promote all at first collection. + // See CR 6362902. + if (AlwaysTenure) { + FLAG_SET_CMDLINE(intx, MaxTenuringThreshold, 0); + } + } +} + +// CAUTION: this code is currently shared by UseParallelGC, UseParNewGC and +// UseconcMarkSweepGC. Further tuning of individual collectors might +// dictate refinement on a per-collector basis. +int Arguments::nof_parallel_gc_threads() { + if (FLAG_IS_DEFAULT(ParallelGCThreads)) { + // For very large machines, there are diminishing returns + // for large numbers of worker threads. Instead of + // hogging the whole system, use 5/8ths of a worker for every + // processor after the first 8. For example, on a 72 cpu + // machine use 8 + (72 - 8) * (5/8) == 48 worker threads. + // This is just a start and needs further tuning and study in + // Tiger. + int ncpus = os::active_processor_count(); + return (ncpus <= 8) ? ncpus : 3 + ((ncpus * 5) / 8); + } else { + return ParallelGCThreads; + } +} + +// Adjust some sizes to suit CMS and/or ParNew needs; these work well on +// sparc/solaris for certain applications, but would gain from +// further optimization and tuning efforts, and would almost +// certainly gain from analysis of platform and environment. +void Arguments::set_cms_and_parnew_gc_flags() { + if (UseSerialGC || UseParallelGC) { + return; + } + + // If we are using CMS, we prefer to UseParNewGC, + // unless explicitly forbidden. + if (UseConcMarkSweepGC && !UseParNewGC && FLAG_IS_DEFAULT(UseParNewGC)) { + FLAG_SET_DEFAULT(UseParNewGC, true); + } + + // Turn off AdaptiveSizePolicy by default for cms until it is + // complete. Also turn it off in general if the + // parnew collector has been selected. + if ((UseConcMarkSweepGC || UseParNewGC) && + FLAG_IS_DEFAULT(UseAdaptiveSizePolicy)) { + FLAG_SET_DEFAULT(UseAdaptiveSizePolicy, false); + } + + // In either case, adjust ParallelGCThreads and/or UseParNewGC + // as needed. + set_parnew_gc_flags(); + + if (!UseConcMarkSweepGC) { + return; + } + + // Now make adjustments for CMS + size_t young_gen_per_worker; + intx new_ratio; + size_t min_new_default; + intx tenuring_default; + if (CMSUseOldDefaults) { // old defaults: "old" as of 6.0 + if FLAG_IS_DEFAULT(CMSYoungGenPerWorker) { + FLAG_SET_DEFAULT(CMSYoungGenPerWorker, 4*M); + } + young_gen_per_worker = 4*M; + new_ratio = (intx)15; + min_new_default = 4*M; + tenuring_default = (intx)0; + } else { // new defaults: "new" as of 6.0 + young_gen_per_worker = CMSYoungGenPerWorker; + new_ratio = (intx)7; + min_new_default = 16*M; + tenuring_default = (intx)4; + } + + // Preferred young gen size for "short" pauses + const uintx parallel_gc_threads = + (ParallelGCThreads == 0 ? 1 : ParallelGCThreads); + const size_t preferred_max_new_size_unaligned = + ScaleForWordSize(young_gen_per_worker * parallel_gc_threads); + const size_t preferred_max_new_size = + align_size_up(preferred_max_new_size_unaligned, os::vm_page_size()); + + // Unless explicitly requested otherwise, size young gen + // for "short" pauses ~ 4M*ParallelGCThreads + if (FLAG_IS_DEFAULT(MaxNewSize)) { // MaxNewSize not set at command-line + if (!FLAG_IS_DEFAULT(NewSize)) { // NewSize explicitly set at command-line + FLAG_SET_DEFAULT(MaxNewSize, MAX2(NewSize, preferred_max_new_size)); + } else { + FLAG_SET_DEFAULT(MaxNewSize, preferred_max_new_size); + } + } + // Unless explicitly requested otherwise, prefer a large + // Old to Young gen size so as to shift the collection load + // to the old generation concurrent collector + if (FLAG_IS_DEFAULT(NewRatio)) { + FLAG_SET_DEFAULT(NewRatio, MAX2(NewRatio, new_ratio)); + + size_t min_new = align_size_up(ScaleForWordSize(min_new_default), os::vm_page_size()); + size_t prev_initial_size = initial_heap_size(); + if (prev_initial_size != 0 && prev_initial_size < min_new+OldSize) { + set_initial_heap_size(min_new+OldSize); + // Currently minimum size and the initial heap sizes are the same. + set_min_heap_size(initial_heap_size()); + if (PrintGCDetails && Verbose) { + warning("Initial heap size increased to " SIZE_FORMAT " M from " + SIZE_FORMAT " M; use -XX:NewSize=... for finer control.", + initial_heap_size()/M, prev_initial_size/M); + } + } + // MaxHeapSize is aligned down in collectorPolicy + size_t max_heap = align_size_down(MaxHeapSize, + CardTableRS::ct_max_alignment_constraint()); + + if (max_heap > min_new) { + // Unless explicitly requested otherwise, make young gen + // at least min_new, and at most preferred_max_new_size. + if (FLAG_IS_DEFAULT(NewSize)) { + FLAG_SET_DEFAULT(NewSize, MAX2(NewSize, min_new)); + FLAG_SET_DEFAULT(NewSize, MIN2(preferred_max_new_size, NewSize)); + } + // Unless explicitly requested otherwise, size old gen + // so that it's at least 3X of NewSize to begin with; + // later NewRatio will decide how it grows; see above. + if (FLAG_IS_DEFAULT(OldSize)) { + if (max_heap > NewSize) { + FLAG_SET_DEFAULT(OldSize, MIN2(3*NewSize, max_heap - NewSize)); + } + } + } + } + // Unless explicitly requested otherwise, definitely + // promote all objects surviving "tenuring_default" scavenges. + if (FLAG_IS_DEFAULT(MaxTenuringThreshold) && + FLAG_IS_DEFAULT(SurvivorRatio)) { + FLAG_SET_DEFAULT(MaxTenuringThreshold, tenuring_default); + } + // If we decided above (or user explicitly requested) + // `promote all' (via MaxTenuringThreshold := 0), + // prefer minuscule survivor spaces so as not to waste + // space for (non-existent) survivors + if (FLAG_IS_DEFAULT(SurvivorRatio) && MaxTenuringThreshold == 0) { + FLAG_SET_DEFAULT(SurvivorRatio, MAX2((intx)1024, SurvivorRatio)); + } + // If OldPLABSize is set and CMSParPromoteBlocksToClaim is not, + // set CMSParPromoteBlocksToClaim equal to OldPLABSize. + // This is done in order to make ParNew+CMS configuration to work + // with YoungPLABSize and OldPLABSize options. + // See CR 6362902. + if (!FLAG_IS_DEFAULT(OldPLABSize)) { + if (FLAG_IS_DEFAULT(CMSParPromoteBlocksToClaim)) { + FLAG_SET_CMDLINE(uintx, CMSParPromoteBlocksToClaim, OldPLABSize); + } + else { + // OldPLABSize and CMSParPromoteBlocksToClaim are both set. + // CMSParPromoteBlocksToClaim is a collector-specific flag, so + // we'll let it to take precedence. + jio_fprintf(defaultStream::error_stream(), + "Both OldPLABSize and CMSParPromoteBlocksToClaim options are specified " + "for the CMS collector. CMSParPromoteBlocksToClaim will take precedence.\n"); + } + } +} + +bool Arguments::should_auto_select_low_pause_collector() { + if (UseAutoGCSelectPolicy && + !FLAG_IS_DEFAULT(MaxGCPauseMillis) && + (MaxGCPauseMillis <= AutoGCSelectPauseMillis)) { + if (PrintGCDetails) { + // Cannot use gclog_or_tty yet. + tty->print_cr("Automatic selection of the low pause collector" + " based on pause goal of %d (ms)", MaxGCPauseMillis); + } + return true; + } + return false; +} + +void Arguments::set_ergonomics_flags() { + // Parallel GC is not compatible with sharing. If one specifies + // that they want sharing explicitly, do not set ergonmics flags. + if (DumpSharedSpaces || ForceSharedSpaces) { + return; + } + + if (os::is_server_class_machine() && !force_client_mode ) { + // If no other collector is requested explicitly, + // let the VM select the collector based on + // machine class and automatic selection policy. + if (!UseSerialGC && + !UseConcMarkSweepGC && + !UseParNewGC && + !DumpSharedSpaces && + FLAG_IS_DEFAULT(UseParallelGC)) { + if (should_auto_select_low_pause_collector()) { + FLAG_SET_ERGO(bool, UseConcMarkSweepGC, true); + set_cms_and_parnew_gc_flags(); + } else { + FLAG_SET_ERGO(bool, UseParallelGC, true); + } + no_shared_spaces(); + } + + // This is here because the parallel collector could + // have been selected so this initialization should + // still be done. + set_parallel_gc_flags(); + } +} + +void Arguments::set_parallel_gc_flags() { + // If parallel old was requested, automatically enable parallel scavenge. + if (UseParallelOldGC && !UseParallelGC && FLAG_IS_DEFAULT(UseParallelGC)) { + FLAG_SET_DEFAULT(UseParallelGC, true); + } + + // If no heap maximum was requested explicitly, use some reasonable fraction + // of the physical memory, up to a maximum of 1GB. + if (UseParallelGC) { + if (FLAG_IS_DEFAULT(MaxHeapSize)) { + const uint64_t reasonable_fraction = + os::physical_memory() / DefaultMaxRAMFraction; + const uint64_t maximum_size = (uint64_t) DefaultMaxRAM; + size_t reasonable_max = + (size_t) os::allocatable_physical_memory(reasonable_fraction); + if (reasonable_max > maximum_size) { + reasonable_max = maximum_size; + } + if (PrintGCDetails && Verbose) { + // Cannot use gclog_or_tty yet. + tty->print_cr(" Max heap size for server class platform " + SIZE_FORMAT, reasonable_max); + } + // If the initial_heap_size has not been set with -Xms, + // then set it as fraction of size of physical memory + // respecting the maximum and minimum sizes of the heap. + if (initial_heap_size() == 0) { + const uint64_t reasonable_initial_fraction = + os::physical_memory() / DefaultInitialRAMFraction; + const size_t reasonable_initial = + (size_t) os::allocatable_physical_memory(reasonable_initial_fraction); + const size_t minimum_size = NewSize + OldSize; + set_initial_heap_size(MAX2(MIN2(reasonable_initial, reasonable_max), + minimum_size)); + // Currently the minimum size and the initial heap sizes are the same. + set_min_heap_size(initial_heap_size()); + if (PrintGCDetails && Verbose) { + // Cannot use gclog_or_tty yet. + tty->print_cr(" Initial heap size for server class platform " + SIZE_FORMAT, initial_heap_size()); + } + } else { + // An minimum size was specified on the command line. Be sure + // that the maximum size is consistent. + if (initial_heap_size() > reasonable_max) { + reasonable_max = initial_heap_size(); + } + } + FLAG_SET_ERGO(uintx, MaxHeapSize, (uintx) reasonable_max); + } + + // If InitialSurvivorRatio or MinSurvivorRatio were not specified, but the + // SurvivorRatio has been set, reset their default values to SurvivorRatio + + // 2. By doing this we make SurvivorRatio also work for Parallel Scavenger. + // See CR 6362902 for details. + if (!FLAG_IS_DEFAULT(SurvivorRatio)) { + if (FLAG_IS_DEFAULT(InitialSurvivorRatio)) { + FLAG_SET_DEFAULT(InitialSurvivorRatio, SurvivorRatio + 2); + } + if (FLAG_IS_DEFAULT(MinSurvivorRatio)) { + FLAG_SET_DEFAULT(MinSurvivorRatio, SurvivorRatio + 2); + } + } + + if (UseParallelOldGC) { + // Par compact uses lower default values since they are treated as + // minimums. + if (FLAG_IS_DEFAULT(MarkSweepDeadRatio)) { + MarkSweepDeadRatio = 1; + } + if (FLAG_IS_DEFAULT(PermMarkSweepDeadRatio)) { + PermMarkSweepDeadRatio = 5; + } + } + } +} + +// This must be called after ergonomics because we want bytecode rewriting +// if the server compiler is used, or if UseSharedSpaces is disabled. +void Arguments::set_bytecode_flags() { + // Better not attempt to store into a read-only space. + if (UseSharedSpaces) { + FLAG_SET_DEFAULT(RewriteBytecodes, false); + FLAG_SET_DEFAULT(RewriteFrequentPairs, false); + } + + if (!RewriteBytecodes) { + FLAG_SET_DEFAULT(RewriteFrequentPairs, false); + } +} + +// Aggressive optimization flags -XX:+AggressiveOpts +void Arguments::set_aggressive_opts_flags() { + if (AggressiveOpts) { +NOT_WINDOWS( + // No measured benefit on Windows + if (FLAG_IS_DEFAULT(CacheTimeMillis)) { + FLAG_SET_DEFAULT(CacheTimeMillis, true); + } +) + } +} + +//=========================================================================================================== +// Parsing of java.compiler property + +void Arguments::process_java_compiler_argument(char* arg) { + // For backwards compatibility, Djava.compiler=NONE or "" + // causes us to switch to -Xint mode UNLESS -Xdebug + // is also specified. + if (strlen(arg) == 0 || strcasecmp(arg, "NONE") == 0) { + set_java_compiler(true); // "-Djava.compiler[=...]" most recently seen. + } +} + +void Arguments::process_java_launcher_argument(const char* launcher, void* extra_info) { + _sun_java_launcher = strdup(launcher); +} + +bool Arguments::created_by_java_launcher() { + assert(_sun_java_launcher != NULL, "property must have value"); + return strcmp(DEFAULT_JAVA_LAUNCHER, _sun_java_launcher) != 0; +} + +//=========================================================================================================== +// Parsing of main arguments + +bool Arguments::verify_percentage(uintx value, const char* name) { + if (value <= 100) { + return true; + } + jio_fprintf(defaultStream::error_stream(), + "%s of " UINTX_FORMAT " is invalid; must be between 0 and 100\n", + name, value); + return false; +} + +static void set_serial_gc_flags() { + FLAG_SET_DEFAULT(UseSerialGC, true); + FLAG_SET_DEFAULT(UseParNewGC, false); + FLAG_SET_DEFAULT(UseConcMarkSweepGC, false); + FLAG_SET_DEFAULT(UseParallelGC, false); + FLAG_SET_DEFAULT(UseParallelOldGC, false); +} + +static bool verify_serial_gc_flags() { + return (UseSerialGC && + !(UseParNewGC || UseConcMarkSweepGC || UseParallelGC || + UseParallelOldGC)); +} + +// Check the consistency of vm_init_args +bool Arguments::check_vm_args_consistency() { + // Method for adding checks for flag consistency. + // The intent is to warn the user of all possible conflicts, + // before returning an error. + // Note: Needs platform-dependent factoring. + bool status = true; + +#if ( (defined(COMPILER2) && defined(SPARC))) + // NOTE: The call to VM_Version_init depends on the fact that VM_Version_init + // on sparc doesn't require generation of a stub as is the case on, e.g., + // x86. Normally, VM_Version_init must be called from init_globals in + // init.cpp, which is called by the initial java thread *after* arguments + // have been parsed. VM_Version_init gets called twice on sparc. + extern void VM_Version_init(); + VM_Version_init(); + if (!VM_Version::has_v9()) { + jio_fprintf(defaultStream::error_stream(), + "V8 Machine detected, Server requires V9\n"); + status = false; + } +#endif /* COMPILER2 && SPARC */ + + // Allow both -XX:-UseStackBanging and -XX:-UseBoundThreads in non-product + // builds so the cost of stack banging can be measured. +#if (defined(PRODUCT) && defined(SOLARIS)) + if (!UseBoundThreads && !UseStackBanging) { + jio_fprintf(defaultStream::error_stream(), + "-UseStackBanging conflicts with -UseBoundThreads\n"); + + status = false; + } +#endif + + if (TLABRefillWasteFraction == 0) { + jio_fprintf(defaultStream::error_stream(), + "TLABRefillWasteFraction should be a denominator, " + "not " SIZE_FORMAT "\n", + TLABRefillWasteFraction); + status = false; + } + + status &= verify_percentage(MaxLiveObjectEvacuationRatio, + "MaxLiveObjectEvacuationRatio"); + status &= verify_percentage(AdaptiveSizePolicyWeight, + "AdaptiveSizePolicyWeight"); + status &= verify_percentage(AdaptivePermSizeWeight, "AdaptivePermSizeWeight"); + status &= verify_percentage(ThresholdTolerance, "ThresholdTolerance"); + status &= verify_percentage(MinHeapFreeRatio, "MinHeapFreeRatio"); + status &= verify_percentage(MaxHeapFreeRatio, "MaxHeapFreeRatio"); + + if (MinHeapFreeRatio > MaxHeapFreeRatio) { + jio_fprintf(defaultStream::error_stream(), + "MinHeapFreeRatio (" UINTX_FORMAT ") must be less than or " + "equal to MaxHeapFreeRatio (" UINTX_FORMAT ")\n", + MinHeapFreeRatio, MaxHeapFreeRatio); + status = false; + } + // Keeping the heap 100% free is hard ;-) so limit it to 99%. + MinHeapFreeRatio = MIN2(MinHeapFreeRatio, (uintx) 99); + + if (FullGCALot && FLAG_IS_DEFAULT(MarkSweepAlwaysCompactCount)) { + MarkSweepAlwaysCompactCount = 1; // Move objects every gc. + } + + status &= verify_percentage(GCHeapFreeLimit, "GCHeapFreeLimit"); + status &= verify_percentage(GCTimeLimit, "GCTimeLimit"); + if (GCTimeLimit == 100) { + // Turn off gc-overhead-limit-exceeded checks + FLAG_SET_DEFAULT(UseGCOverheadLimit, false); + } + + status &= verify_percentage(GCHeapFreeLimit, "GCHeapFreeLimit"); + + // Check user specified sharing option conflict with Parallel GC + bool cannot_share = (UseConcMarkSweepGC || UseParallelGC || + UseParallelOldGC || UseParNewGC || + SOLARIS_ONLY(UseISM) NOT_SOLARIS(UseLargePages)); + + if (cannot_share) { + // Either force sharing on by forcing the other options off, or + // force sharing off. + if (DumpSharedSpaces || ForceSharedSpaces) { + set_serial_gc_flags(); + FLAG_SET_DEFAULT(SOLARIS_ONLY(UseISM) NOT_SOLARIS(UseLargePages), false); + } else { + no_shared_spaces(); + } + } + + // Ensure that the user has not selected conflicting sets + // of collectors. [Note: this check is merely a user convenience; + // collectors over-ride each other so that only a non-conflicting + // set is selected; however what the user gets is not what they + // may have expected from the combination they asked for. It's + // better to reduce user confusion by not allowing them to + // select conflicting combinations. + uint i = 0; + if (UseSerialGC) i++; + if (UseConcMarkSweepGC || UseParNewGC) i++; + if (UseParallelGC || UseParallelOldGC) i++; + if (i > 1) { + jio_fprintf(defaultStream::error_stream(), + "Conflicting collector combinations in option list; " + "please refer to the release notes for the combinations " + "allowed\n"); + status = false; + } + + if (_has_alloc_profile) { + if (UseParallelGC || UseParallelOldGC) { + jio_fprintf(defaultStream::error_stream(), + "error: invalid argument combination.\n" + "Allocation profiling (-Xaprof) cannot be used together with " + "Parallel GC (-XX:+UseParallelGC or -XX:+UseParallelOldGC).\n"); + status = false; + } + if (UseConcMarkSweepGC) { + jio_fprintf(defaultStream::error_stream(), + "error: invalid argument combination.\n" + "Allocation profiling (-Xaprof) cannot be used together with " + "the CMS collector (-XX:+UseConcMarkSweepGC).\n"); + status = false; + } + } + + if (CMSIncrementalMode) { + if (!UseConcMarkSweepGC) { + jio_fprintf(defaultStream::error_stream(), + "error: invalid argument combination.\n" + "The CMS collector (-XX:+UseConcMarkSweepGC) must be " + "selected in order\nto use CMSIncrementalMode.\n"); + status = false; + } else if (!UseTLAB) { + jio_fprintf(defaultStream::error_stream(), + "error: CMSIncrementalMode requires thread-local " + "allocation buffers\n(-XX:+UseTLAB).\n"); + status = false; + } else { + status &= verify_percentage(CMSIncrementalDutyCycle, + "CMSIncrementalDutyCycle"); + status &= verify_percentage(CMSIncrementalDutyCycleMin, + "CMSIncrementalDutyCycleMin"); + status &= verify_percentage(CMSIncrementalSafetyFactor, + "CMSIncrementalSafetyFactor"); + status &= verify_percentage(CMSIncrementalOffset, + "CMSIncrementalOffset"); + status &= verify_percentage(CMSExpAvgFactor, + "CMSExpAvgFactor"); + // If it was not set on the command line, set + // CMSInitiatingOccupancyFraction to 1 so icms can initiate cycles early. + if (CMSInitiatingOccupancyFraction < 0) { + FLAG_SET_DEFAULT(CMSInitiatingOccupancyFraction, 1); + } + } + } + + if (UseNUMA && !UseTLAB) { + jio_fprintf(defaultStream::error_stream(), + "error: NUMA allocator (-XX:+UseNUMA) requires thread-local " + "allocation\nbuffers (-XX:+UseTLAB).\n"); + status = false; + } + + // CMS space iteration, which FLSVerifyAllHeapreferences entails, + // insists that we hold the requisite locks so that the iteration is + // MT-safe. For the verification at start-up and shut-down, we don't + // yet have a good way of acquiring and releasing these locks, + // which are not visible at the CollectedHeap level. We want to + // be able to acquire these locks and then do the iteration rather + // than just disable the lock verification. This will be fixed under + // bug 4788986. + if (UseConcMarkSweepGC && FLSVerifyAllHeapReferences) { + if (VerifyGCStartAt == 0) { + warning("Heap verification at start-up disabled " + "(due to current incompatibility with FLSVerifyAllHeapReferences)"); + VerifyGCStartAt = 1; // Disable verification at start-up + } + if (VerifyBeforeExit) { + warning("Heap verification at shutdown disabled " + "(due to current incompatibility with FLSVerifyAllHeapReferences)"); + VerifyBeforeExit = false; // Disable verification at shutdown + } + } + + // Note: only executed in non-PRODUCT mode + if (!UseAsyncConcMarkSweepGC && + (ExplicitGCInvokesConcurrent || + ExplicitGCInvokesConcurrentAndUnloadsClasses)) { + jio_fprintf(defaultStream::error_stream(), + "error: +ExplictGCInvokesConcurrent[AndUnloadsClasses] conflicts" + " with -UseAsyncConcMarkSweepGC"); + status = false; + } + + return status; +} + +bool Arguments::is_bad_option(const JavaVMOption* option, jboolean ignore, + const char* option_type) { + if (ignore) return false; + + const char* spacer = " "; + if (option_type == NULL) { + option_type = ++spacer; // Set both to the empty string. + } + + if (os::obsolete_option(option)) { + jio_fprintf(defaultStream::error_stream(), + "Obsolete %s%soption: %s\n", option_type, spacer, + option->optionString); + return false; + } else { + jio_fprintf(defaultStream::error_stream(), + "Unrecognized %s%soption: %s\n", option_type, spacer, + option->optionString); + return true; + } +} + +static const char* user_assertion_options[] = { + "-da", "-ea", "-disableassertions", "-enableassertions", 0 +}; + +static const char* system_assertion_options[] = { + "-dsa", "-esa", "-disablesystemassertions", "-enablesystemassertions", 0 +}; + +// Return true if any of the strings in null-terminated array 'names' matches. +// If tail_allowed is true, then the tail must begin with a colon; otherwise, +// the option must match exactly. +static bool match_option(const JavaVMOption* option, const char** names, const char** tail, + bool tail_allowed) { + for (/* empty */; *names != NULL; ++names) { + if (match_option(option, *names, tail)) { + if (**tail == '\0' || tail_allowed && **tail == ':') { + return true; + } + } + } + return false; +} + +Arguments::ArgsRange Arguments::parse_memory_size(const char* s, + jlong* long_arg, + jlong min_size) { + if (!atomll(s, long_arg)) return arg_unreadable; + return check_memory_size(*long_arg, min_size); +} + +// Parse JavaVMInitArgs structure + +jint Arguments::parse_vm_init_args(const JavaVMInitArgs* args) { + // For components of the system classpath. + SysClassPath scp(Arguments::get_sysclasspath()); + bool scp_assembly_required = false; + + // Save default settings for some mode flags + Arguments::_AlwaysCompileLoopMethods = AlwaysCompileLoopMethods; + Arguments::_UseOnStackReplacement = UseOnStackReplacement; + Arguments::_ClipInlining = ClipInlining; + Arguments::_BackgroundCompilation = BackgroundCompilation; + Arguments::_Tier2CompileThreshold = Tier2CompileThreshold; + + // Parse JAVA_TOOL_OPTIONS environment variable (if present) + jint result = parse_java_tool_options_environment_variable(&scp, &scp_assembly_required); + if (result != JNI_OK) { + return result; + } + + // Parse JavaVMInitArgs structure passed in + result = parse_each_vm_init_arg(args, &scp, &scp_assembly_required, COMMAND_LINE); + if (result != JNI_OK) { + return result; + } + + // Parse _JAVA_OPTIONS environment variable (if present) (mimics classic VM) + result = parse_java_options_environment_variable(&scp, &scp_assembly_required); + if (result != JNI_OK) { + return result; + } + + // Do final processing now that all arguments have been parsed + result = finalize_vm_init_args(&scp, scp_assembly_required); + if (result != JNI_OK) { + return result; + } + + return JNI_OK; +} + + +jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, + SysClassPath* scp_p, + bool* scp_assembly_required_p, + FlagValueOrigin origin) { + // Remaining part of option string + const char* tail; + + // iterate over arguments + for (int index = 0; index < args->nOptions; index++) { + bool is_absolute_path = false; // for -agentpath vs -agentlib + + const JavaVMOption* option = args->options + index; + + if (!match_option(option, "-Djava.class.path", &tail) && + !match_option(option, "-Dsun.java.command", &tail) && + !match_option(option, "-Dsun.java.launcher", &tail)) { + + // add all jvm options to the jvm_args string. This string + // is used later to set the java.vm.args PerfData string constant. + // the -Djava.class.path and the -Dsun.java.command options are + // omitted from jvm_args string as each have their own PerfData + // string constant object. + build_jvm_args(option->optionString); + } + + // -verbose:[class/gc/jni] + if (match_option(option, "-verbose", &tail)) { + if (!strcmp(tail, ":class") || !strcmp(tail, "")) { + FLAG_SET_CMDLINE(bool, TraceClassLoading, true); + FLAG_SET_CMDLINE(bool, TraceClassUnloading, true); + } else if (!strcmp(tail, ":gc")) { + FLAG_SET_CMDLINE(bool, PrintGC, true); + FLAG_SET_CMDLINE(bool, TraceClassUnloading, true); + } else if (!strcmp(tail, ":jni")) { + FLAG_SET_CMDLINE(bool, PrintJNIResolving, true); + } + // -da / -ea / -disableassertions / -enableassertions + // These accept an optional class/package name separated by a colon, e.g., + // -da:java.lang.Thread. + } else if (match_option(option, user_assertion_options, &tail, true)) { + bool enable = option->optionString[1] == 'e'; // char after '-' is 'e' + if (*tail == '\0') { + JavaAssertions::setUserClassDefault(enable); + } else { + assert(*tail == ':', "bogus match by match_option()"); + JavaAssertions::addOption(tail + 1, enable); + } + // -dsa / -esa / -disablesystemassertions / -enablesystemassertions + } else if (match_option(option, system_assertion_options, &tail, false)) { + bool enable = option->optionString[1] == 'e'; // char after '-' is 'e' + JavaAssertions::setSystemClassDefault(enable); + // -bootclasspath: + } else if (match_option(option, "-Xbootclasspath:", &tail)) { + scp_p->reset_path(tail); + *scp_assembly_required_p = true; + // -bootclasspath/a: + } else if (match_option(option, "-Xbootclasspath/a:", &tail)) { + scp_p->add_suffix(tail); + *scp_assembly_required_p = true; + // -bootclasspath/p: + } else if (match_option(option, "-Xbootclasspath/p:", &tail)) { + scp_p->add_prefix(tail); + *scp_assembly_required_p = true; + // -Xrun + } else if (match_option(option, "-Xrun", &tail)) { + if(tail != NULL) { + const char* pos = strchr(tail, ':'); + size_t len = (pos == NULL) ? strlen(tail) : pos - tail; + char* name = (char*)memcpy(NEW_C_HEAP_ARRAY(char, len + 1), tail, len); + name[len] = '\0'; + + char *options = NULL; + if(pos != NULL) { + size_t len2 = strlen(pos+1) + 1; // options start after ':'. Final zero must be copied. + options = (char*)memcpy(NEW_C_HEAP_ARRAY(char, len2), pos+1, len2); + } +#ifdef JVMTI_KERNEL + if ((strcmp(name, "hprof") == 0) || (strcmp(name, "jdwp") == 0)) { + warning("profiling and debugging agents are not supported with Kernel VM"); + } else +#endif // JVMTI_KERNEL + add_init_library(name, options); + } + // -agentlib and -agentpath + } else if (match_option(option, "-agentlib:", &tail) || + (is_absolute_path = match_option(option, "-agentpath:", &tail))) { + if(tail != NULL) { + const char* pos = strchr(tail, '='); + size_t len = (pos == NULL) ? strlen(tail) : pos - tail; + char* name = strncpy(NEW_C_HEAP_ARRAY(char, len + 1), tail, len); + name[len] = '\0'; + + char *options = NULL; + if(pos != NULL) { + options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(pos + 1) + 1), pos + 1); + } +#ifdef JVMTI_KERNEL + if ((strcmp(name, "hprof") == 0) || (strcmp(name, "jdwp") == 0)) { + warning("profiling and debugging agents are not supported with Kernel VM"); + } else +#endif // JVMTI_KERNEL + add_init_agent(name, options, is_absolute_path); + + } + // -javaagent + } else if (match_option(option, "-javaagent:", &tail)) { + if(tail != NULL) { + char *options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(tail) + 1), tail); + add_init_agent("instrument", options, false); + } + // -Xnoclassgc + } else if (match_option(option, "-Xnoclassgc", &tail)) { + FLAG_SET_CMDLINE(bool, ClassUnloading, false); + // -Xincgc: i-CMS + } else if (match_option(option, "-Xincgc", &tail)) { + FLAG_SET_CMDLINE(bool, UseConcMarkSweepGC, true); + FLAG_SET_CMDLINE(bool, CMSIncrementalMode, true); + // -Xnoincgc: no i-CMS + } else if (match_option(option, "-Xnoincgc", &tail)) { + FLAG_SET_CMDLINE(bool, UseConcMarkSweepGC, false); + FLAG_SET_CMDLINE(bool, CMSIncrementalMode, false); + // -Xconcgc + } else if (match_option(option, "-Xconcgc", &tail)) { + FLAG_SET_CMDLINE(bool, UseConcMarkSweepGC, true); + // -Xnoconcgc + } else if (match_option(option, "-Xnoconcgc", &tail)) { + FLAG_SET_CMDLINE(bool, UseConcMarkSweepGC, false); + // -Xbatch + } else if (match_option(option, "-Xbatch", &tail)) { + FLAG_SET_CMDLINE(bool, BackgroundCompilation, false); + // -Xmn for compatibility with other JVM vendors + } else if (match_option(option, "-Xmn", &tail)) { + jlong long_initial_eden_size = 0; + ArgsRange errcode = parse_memory_size(tail, &long_initial_eden_size, 1); + if (errcode != arg_in_range) { + jio_fprintf(defaultStream::error_stream(), + "Invalid initial eden size: %s\n", option->optionString); + describe_range_error(errcode); + return JNI_EINVAL; + } + FLAG_SET_CMDLINE(uintx, MaxNewSize, (size_t) long_initial_eden_size); + FLAG_SET_CMDLINE(uintx, NewSize, (size_t) long_initial_eden_size); + // -Xms + } else if (match_option(option, "-Xms", &tail)) { + jlong long_initial_heap_size = 0; + ArgsRange errcode = parse_memory_size(tail, &long_initial_heap_size, 1); + if (errcode != arg_in_range) { + jio_fprintf(defaultStream::error_stream(), + "Invalid initial heap size: %s\n", option->optionString); + describe_range_error(errcode); + return JNI_EINVAL; + } + set_initial_heap_size((size_t) long_initial_heap_size); + // Currently the minimum size and the initial heap sizes are the same. + set_min_heap_size(initial_heap_size()); + // -Xmx + } else if (match_option(option, "-Xmx", &tail)) { + jlong long_max_heap_size = 0; + ArgsRange errcode = parse_memory_size(tail, &long_max_heap_size, 1); + if (errcode != arg_in_range) { + jio_fprintf(defaultStream::error_stream(), + "Invalid maximum heap size: %s\n", option->optionString); + describe_range_error(errcode); + return JNI_EINVAL; + } + FLAG_SET_CMDLINE(uintx, MaxHeapSize, (size_t) long_max_heap_size); + // Xmaxf + } else if (match_option(option, "-Xmaxf", &tail)) { + int maxf = (int)(atof(tail) * 100); + if (maxf < 0 || maxf > 100) { + jio_fprintf(defaultStream::error_stream(), + "Bad max heap free percentage size: %s\n", + option->optionString); + return JNI_EINVAL; + } else { + FLAG_SET_CMDLINE(uintx, MaxHeapFreeRatio, maxf); + } + // Xminf + } else if (match_option(option, "-Xminf", &tail)) { + int minf = (int)(atof(tail) * 100); + if (minf < 0 || minf > 100) { + jio_fprintf(defaultStream::error_stream(), + "Bad min heap free percentage size: %s\n", + option->optionString); + return JNI_EINVAL; + } else { + FLAG_SET_CMDLINE(uintx, MinHeapFreeRatio, minf); + } + // -Xss + } else if (match_option(option, "-Xss", &tail)) { + jlong long_ThreadStackSize = 0; + ArgsRange errcode = parse_memory_size(tail, &long_ThreadStackSize, 1000); + if (errcode != arg_in_range) { + jio_fprintf(defaultStream::error_stream(), + "Invalid thread stack size: %s\n", option->optionString); + describe_range_error(errcode); + return JNI_EINVAL; + } + // Internally track ThreadStackSize in units of 1024 bytes. + FLAG_SET_CMDLINE(intx, ThreadStackSize, + round_to((int)long_ThreadStackSize, K) / K); + // -Xoss + } else if (match_option(option, "-Xoss", &tail)) { + // HotSpot does not have separate native and Java stacks, ignore silently for compatibility + // -Xmaxjitcodesize + } else if (match_option(option, "-Xmaxjitcodesize", &tail)) { + jlong long_ReservedCodeCacheSize = 0; + ArgsRange errcode = parse_memory_size(tail, &long_ReservedCodeCacheSize, + InitialCodeCacheSize); + if (errcode != arg_in_range) { + jio_fprintf(defaultStream::error_stream(), + "Invalid maximum code cache size: %s\n", + option->optionString); + describe_range_error(errcode); + return JNI_EINVAL; + } + FLAG_SET_CMDLINE(uintx, ReservedCodeCacheSize, (uintx)long_ReservedCodeCacheSize); + // -green + } else if (match_option(option, "-green", &tail)) { + jio_fprintf(defaultStream::error_stream(), + "Green threads support not available\n"); + return JNI_EINVAL; + // -native + } else if (match_option(option, "-native", &tail)) { + // HotSpot always uses native threads, ignore silently for compatibility + // -Xsqnopause + } else if (match_option(option, "-Xsqnopause", &tail)) { + // EVM option, ignore silently for compatibility + // -Xrs + } else if (match_option(option, "-Xrs", &tail)) { + // Classic/EVM option, new functionality + FLAG_SET_CMDLINE(bool, ReduceSignalUsage, true); + } else if (match_option(option, "-Xusealtsigs", &tail)) { + // change default internal VM signals used - lower case for back compat + FLAG_SET_CMDLINE(bool, UseAltSigs, true); + // -Xoptimize + } else if (match_option(option, "-Xoptimize", &tail)) { + // EVM option, ignore silently for compatibility + // -Xprof + } else if (match_option(option, "-Xprof", &tail)) { +#ifndef FPROF_KERNEL + _has_profile = true; +#else // FPROF_KERNEL + // do we have to exit? + warning("Kernel VM does not support flat profiling."); +#endif // FPROF_KERNEL + // -Xaprof + } else if (match_option(option, "-Xaprof", &tail)) { + _has_alloc_profile = true; + // -Xconcurrentio + } else if (match_option(option, "-Xconcurrentio", &tail)) { + FLAG_SET_CMDLINE(bool, UseLWPSynchronization, true); + FLAG_SET_CMDLINE(bool, BackgroundCompilation, false); + FLAG_SET_CMDLINE(intx, DeferThrSuspendLoopCount, 1); + FLAG_SET_CMDLINE(bool, UseTLAB, false); + FLAG_SET_CMDLINE(uintx, NewSizeThreadIncrease, 16 * K); // 20Kb per thread added to new generation + + // -Xinternalversion + } else if (match_option(option, "-Xinternalversion", &tail)) { + jio_fprintf(defaultStream::output_stream(), "%s\n", + VM_Version::internal_vm_info_string()); + vm_exit(0); +#ifndef PRODUCT + // -Xprintflags + } else if (match_option(option, "-Xprintflags", &tail)) { + CommandLineFlags::printFlags(); + vm_exit(0); +#endif + // -D + } else if (match_option(option, "-D", &tail)) { + if (!add_property(tail)) { + return JNI_ENOMEM; + } + // Out of the box management support + if (match_option(option, "-Dcom.sun.management", &tail)) { + FLAG_SET_CMDLINE(bool, ManagementServer, true); + } + // -Xint + } else if (match_option(option, "-Xint", &tail)) { + set_mode_flags(_int); + // -Xmixed + } else if (match_option(option, "-Xmixed", &tail)) { + set_mode_flags(_mixed); + // -Xcomp + } else if (match_option(option, "-Xcomp", &tail)) { + // for testing the compiler; turn off all flags that inhibit compilation + set_mode_flags(_comp); + + // -Xshare:dump + } else if (match_option(option, "-Xshare:dump", &tail)) { +#ifdef TIERED + FLAG_SET_CMDLINE(bool, DumpSharedSpaces, true); + set_mode_flags(_int); // Prevent compilation, which creates objects +#elif defined(COMPILER2) + vm_exit_during_initialization( + "Dumping a shared archive is not supported on the Server JVM.", NULL); +#elif defined(KERNEL) + vm_exit_during_initialization( + "Dumping a shared archive is not supported on the Kernel JVM.", NULL); +#else + FLAG_SET_CMDLINE(bool, DumpSharedSpaces, true); + set_mode_flags(_int); // Prevent compilation, which creates objects +#endif + // -Xshare:on + } else if (match_option(option, "-Xshare:on", &tail)) { + FLAG_SET_CMDLINE(bool, UseSharedSpaces, true); + FLAG_SET_CMDLINE(bool, RequireSharedSpaces, true); +#ifdef TIERED + FLAG_SET_CMDLINE(bool, ForceSharedSpaces, true); +#endif // TIERED + // -Xshare:auto + } else if (match_option(option, "-Xshare:auto", &tail)) { + FLAG_SET_CMDLINE(bool, UseSharedSpaces, true); + FLAG_SET_CMDLINE(bool, RequireSharedSpaces, false); + // -Xshare:off + } else if (match_option(option, "-Xshare:off", &tail)) { + FLAG_SET_CMDLINE(bool, UseSharedSpaces, false); + FLAG_SET_CMDLINE(bool, RequireSharedSpaces, false); + + // -Xverify + } else if (match_option(option, "-Xverify", &tail)) { + if (strcmp(tail, ":all") == 0 || strcmp(tail, "") == 0) { + FLAG_SET_CMDLINE(bool, BytecodeVerificationLocal, true); + FLAG_SET_CMDLINE(bool, BytecodeVerificationRemote, true); + } else if (strcmp(tail, ":remote") == 0) { + FLAG_SET_CMDLINE(bool, BytecodeVerificationLocal, false); + FLAG_SET_CMDLINE(bool, BytecodeVerificationRemote, true); + } else if (strcmp(tail, ":none") == 0) { + FLAG_SET_CMDLINE(bool, BytecodeVerificationLocal, false); + FLAG_SET_CMDLINE(bool, BytecodeVerificationRemote, false); + } else if (is_bad_option(option, args->ignoreUnrecognized, "verification")) { + return JNI_EINVAL; + } + // -Xdebug + } else if (match_option(option, "-Xdebug", &tail)) { + // note this flag has been used, then ignore + set_xdebug_mode(true); + // -Xnoagent + } else if (match_option(option, "-Xnoagent", &tail)) { + // For compatibility with classic. HotSpot refuses to load the old style agent.dll. + } else if (match_option(option, "-Xboundthreads", &tail)) { + // Bind user level threads to kernel threads (Solaris only) + FLAG_SET_CMDLINE(bool, UseBoundThreads, true); + } else if (match_option(option, "-Xloggc:", &tail)) { + // Redirect GC output to the file. -Xloggc: + // ostream_init_log(), when called will use this filename + // to initialize a fileStream. + _gc_log_filename = strdup(tail); + FLAG_SET_CMDLINE(bool, PrintGC, true); + FLAG_SET_CMDLINE(bool, PrintGCTimeStamps, true); + FLAG_SET_CMDLINE(bool, TraceClassUnloading, true); + + // JNI hooks + } else if (match_option(option, "-Xcheck", &tail)) { + if (!strcmp(tail, ":jni")) { + CheckJNICalls = true; + } else if (is_bad_option(option, args->ignoreUnrecognized, + "check")) { + return JNI_EINVAL; + } + } else if (match_option(option, "vfprintf", &tail)) { + _vfprintf_hook = CAST_TO_FN_PTR(vfprintf_hook_t, option->extraInfo); + } else if (match_option(option, "exit", &tail)) { + _exit_hook = CAST_TO_FN_PTR(exit_hook_t, option->extraInfo); + } else if (match_option(option, "abort", &tail)) { + _abort_hook = CAST_TO_FN_PTR(abort_hook_t, option->extraInfo); + // -XX:+AggressiveHeap + } else if (match_option(option, "-XX:+AggressiveHeap", &tail)) { + + // This option inspects the machine and attempts to set various + // parameters to be optimal for long-running, memory allocation + // intensive jobs. It is intended for machines with large + // amounts of cpu and memory. + + // initHeapSize is needed since _initial_heap_size is 4 bytes on a 32 bit + // VM, but we may not be able to represent the total physical memory + // available (like having 8gb of memory on a box but using a 32bit VM). + // Thus, we need to make sure we're using a julong for intermediate + // calculations. + julong initHeapSize; + julong total_memory = os::physical_memory(); + + if (total_memory < (julong)256*M) { + jio_fprintf(defaultStream::error_stream(), + "You need at least 256mb of memory to use -XX:+AggressiveHeap\n"); + vm_exit(1); + } + + // The heap size is half of available memory, or (at most) + // all of possible memory less 160mb (leaving room for the OS + // when using ISM). This is the maximum; because adaptive sizing + // is turned on below, the actual space used may be smaller. + + initHeapSize = MIN2(total_memory / (julong)2, + total_memory - (julong)160*M); + + // Make sure that if we have a lot of memory we cap the 32 bit + // process space. The 64bit VM version of this function is a nop. + initHeapSize = os::allocatable_physical_memory(initHeapSize); + + // The perm gen is separate but contiguous with the + // object heap (and is reserved with it) so subtract it + // from the heap size. + if (initHeapSize > MaxPermSize) { + initHeapSize = initHeapSize - MaxPermSize; + } else { + warning("AggressiveHeap and MaxPermSize values may conflict"); + } + + if (FLAG_IS_DEFAULT(MaxHeapSize)) { + FLAG_SET_CMDLINE(uintx, MaxHeapSize, initHeapSize); + set_initial_heap_size(MaxHeapSize); + // Currently the minimum size and the initial heap sizes are the same. + set_min_heap_size(initial_heap_size()); + } + if (FLAG_IS_DEFAULT(NewSize)) { + // Make the young generation 3/8ths of the total heap. + FLAG_SET_CMDLINE(uintx, NewSize, + ((julong)MaxHeapSize / (julong)8) * (julong)3); + FLAG_SET_CMDLINE(uintx, MaxNewSize, NewSize); + } + + FLAG_SET_DEFAULT(UseLargePages, true); + + // Increase some data structure sizes for efficiency + FLAG_SET_CMDLINE(uintx, BaseFootPrintEstimate, MaxHeapSize); + FLAG_SET_CMDLINE(bool, ResizeTLAB, false); + FLAG_SET_CMDLINE(uintx, TLABSize, 256*K); + + // See the OldPLABSize comment below, but replace 'after promotion' + // with 'after copying'. YoungPLABSize is the size of the survivor + // space per-gc-thread buffers. The default is 4kw. + FLAG_SET_CMDLINE(uintx, YoungPLABSize, 256*K); // Note: this is in words + + // OldPLABSize is the size of the buffers in the old gen that + // UseParallelGC uses to promote live data that doesn't fit in the + // survivor spaces. At any given time, there's one for each gc thread. + // The default size is 1kw. These buffers are rarely used, since the + // survivor spaces are usually big enough. For specjbb, however, there + // are occasions when there's lots of live data in the young gen + // and we end up promoting some of it. We don't have a definite + // explanation for why bumping OldPLABSize helps, but the theory + // is that a bigger PLAB results in retaining something like the + // original allocation order after promotion, which improves mutator + // locality. A minor effect may be that larger PLABs reduce the + // number of PLAB allocation events during gc. The value of 8kw + // was arrived at by experimenting with specjbb. + FLAG_SET_CMDLINE(uintx, OldPLABSize, 8*K); // Note: this is in words + + // CompilationPolicyChoice=0 causes the server compiler to adopt + // a more conservative which-method-do-I-compile policy when one + // of the counters maintained by the interpreter trips. The + // result is reduced startup time and improved specjbb and + // alacrity performance. Zero is the default, but we set it + // explicitly here in case the default changes. + // See runtime/compilationPolicy.*. + FLAG_SET_CMDLINE(intx, CompilationPolicyChoice, 0); + + // Enable parallel GC and adaptive generation sizing + FLAG_SET_CMDLINE(bool, UseParallelGC, true); + FLAG_SET_DEFAULT(ParallelGCThreads, nof_parallel_gc_threads()); + + // Encourage steady state memory management + FLAG_SET_CMDLINE(uintx, ThresholdTolerance, 100); + + // This appears to improve mutator locality + FLAG_SET_CMDLINE(bool, ScavengeBeforeFullGC, false); + + // Get around early Solaris scheduling bug + // (affinity vs other jobs on system) + // but disallow DR and offlining (5008695). + FLAG_SET_CMDLINE(bool, BindGCTaskThreadsToCPUs, true); + + } else if (match_option(option, "-XX:+NeverTenure", &tail)) { + // The last option must always win. + FLAG_SET_CMDLINE(bool, AlwaysTenure, false); + FLAG_SET_CMDLINE(bool, NeverTenure, true); + } else if (match_option(option, "-XX:+AlwaysTenure", &tail)) { + // The last option must always win. + FLAG_SET_CMDLINE(bool, NeverTenure, false); + FLAG_SET_CMDLINE(bool, AlwaysTenure, true); + } else if (match_option(option, "-XX:+CMSPermGenSweepingEnabled", &tail) || + match_option(option, "-XX:-CMSPermGenSweepingEnabled", &tail)) { + jio_fprintf(defaultStream::error_stream(), + "Please use CMSClassUnloadingEnabled in place of " + "CMSPermGenSweepingEnabled in the future\n"); + } else if (match_option(option, "-XX:+UseGCTimeLimit", &tail)) { + FLAG_SET_CMDLINE(bool, UseGCOverheadLimit, true); + jio_fprintf(defaultStream::error_stream(), + "Please use -XX:+UseGCOverheadLimit in place of " + "-XX:+UseGCTimeLimit in the future\n"); + } else if (match_option(option, "-XX:-UseGCTimeLimit", &tail)) { + FLAG_SET_CMDLINE(bool, UseGCOverheadLimit, false); + jio_fprintf(defaultStream::error_stream(), + "Please use -XX:-UseGCOverheadLimit in place of " + "-XX:-UseGCTimeLimit in the future\n"); + // The TLE options are for compatibility with 1.3 and will be + // removed without notice in a future release. These options + // are not to be documented. + } else if (match_option(option, "-XX:MaxTLERatio=", &tail)) { + // No longer used. + } else if (match_option(option, "-XX:+ResizeTLE", &tail)) { + FLAG_SET_CMDLINE(bool, ResizeTLAB, true); + } else if (match_option(option, "-XX:-ResizeTLE", &tail)) { + FLAG_SET_CMDLINE(bool, ResizeTLAB, false); + } else if (match_option(option, "-XX:+PrintTLE", &tail)) { + FLAG_SET_CMDLINE(bool, PrintTLAB, true); + } else if (match_option(option, "-XX:-PrintTLE", &tail)) { + FLAG_SET_CMDLINE(bool, PrintTLAB, false); + } else if (match_option(option, "-XX:TLEFragmentationRatio=", &tail)) { + // No longer used. + } else if (match_option(option, "-XX:TLESize=", &tail)) { + jlong long_tlab_size = 0; + ArgsRange errcode = parse_memory_size(tail, &long_tlab_size, 1); + if (errcode != arg_in_range) { + jio_fprintf(defaultStream::error_stream(), + "Invalid TLAB size: %s\n", option->optionString); + describe_range_error(errcode); + return JNI_EINVAL; + } + FLAG_SET_CMDLINE(uintx, TLABSize, long_tlab_size); + } else if (match_option(option, "-XX:TLEThreadRatio=", &tail)) { + // No longer used. + } else if (match_option(option, "-XX:+UseTLE", &tail)) { + FLAG_SET_CMDLINE(bool, UseTLAB, true); + } else if (match_option(option, "-XX:-UseTLE", &tail)) { + FLAG_SET_CMDLINE(bool, UseTLAB, false); +SOLARIS_ONLY( + } else if (match_option(option, "-XX:+UsePermISM", &tail)) { + warning("-XX:+UsePermISM is obsolete."); + FLAG_SET_CMDLINE(bool, UseISM, true); + } else if (match_option(option, "-XX:-UsePermISM", &tail)) { + FLAG_SET_CMDLINE(bool, UseISM, false); +) + } else if (match_option(option, "-XX:+DisplayVMOutputToStderr", &tail)) { + FLAG_SET_CMDLINE(bool, DisplayVMOutputToStdout, false); + FLAG_SET_CMDLINE(bool, DisplayVMOutputToStderr, true); + } else if (match_option(option, "-XX:+DisplayVMOutputToStdout", &tail)) { + FLAG_SET_CMDLINE(bool, DisplayVMOutputToStderr, false); + FLAG_SET_CMDLINE(bool, DisplayVMOutputToStdout, true); + } else if (match_option(option, "-XX:+ExtendedDTraceProbes", &tail)) { +#ifdef SOLARIS + FLAG_SET_CMDLINE(bool, ExtendedDTraceProbes, true); + FLAG_SET_CMDLINE(bool, DTraceMethodProbes, true); + FLAG_SET_CMDLINE(bool, DTraceAllocProbes, true); + FLAG_SET_CMDLINE(bool, DTraceMonitorProbes, true); +#else // ndef SOLARIS + jio_fprintf(defaultStream::error_stream(), + "ExtendedDTraceProbes flag is only applicable on Solaris\n"); + return JNI_EINVAL; +#endif // ndef SOLARIS + } else +#ifdef ASSERT + if (match_option(option, "-XX:+FullGCALot", &tail)) { + FLAG_SET_CMDLINE(bool, FullGCALot, true); + // disable scavenge before parallel mark-compact + FLAG_SET_CMDLINE(bool, ScavengeBeforeFullGC, false); + } else +#endif + if (match_option(option, "-XX:ParCMSPromoteBlocksToClaim=", &tail)) { + julong cms_blocks_to_claim = (julong)atol(tail); + FLAG_SET_CMDLINE(uintx, CMSParPromoteBlocksToClaim, cms_blocks_to_claim); + jio_fprintf(defaultStream::error_stream(), + "Please use -XX:CMSParPromoteBlocksToClaim in place of " + "-XX:ParCMSPromoteBlocksToClaim in the future\n"); + } else + if (match_option(option, "-XX:ParallelGCOldGenAllocBufferSize=", &tail)) { + jlong old_plab_size = 0; + ArgsRange errcode = parse_memory_size(tail, &old_plab_size, 1); + if (errcode != arg_in_range) { + jio_fprintf(defaultStream::error_stream(), + "Invalid old PLAB size: %s\n", option->optionString); + describe_range_error(errcode); + return JNI_EINVAL; + } + FLAG_SET_CMDLINE(uintx, OldPLABSize, (julong)old_plab_size); + jio_fprintf(defaultStream::error_stream(), + "Please use -XX:OldPLABSize in place of " + "-XX:ParallelGCOldGenAllocBufferSize in the future\n"); + } else + if (match_option(option, "-XX:ParallelGCToSpaceAllocBufferSize=", &tail)) { + jlong young_plab_size = 0; + ArgsRange errcode = parse_memory_size(tail, &young_plab_size, 1); + if (errcode != arg_in_range) { + jio_fprintf(defaultStream::error_stream(), + "Invalid young PLAB size: %s\n", option->optionString); + describe_range_error(errcode); + return JNI_EINVAL; + } + FLAG_SET_CMDLINE(uintx, YoungPLABSize, (julong)young_plab_size); + jio_fprintf(defaultStream::error_stream(), + "Please use -XX:YoungPLABSize in place of " + "-XX:ParallelGCToSpaceAllocBufferSize in the future\n"); + } else + if (match_option(option, "-XX:", &tail)) { // -XX:xxxx + // Skip -XX:Flags= since that case has already been handled + if (strncmp(tail, "Flags=", strlen("Flags=")) != 0) { + if (!process_argument(tail, args->ignoreUnrecognized, origin)) { + return JNI_EINVAL; + } + } + // Unknown option + } else if (is_bad_option(option, args->ignoreUnrecognized)) { + return JNI_ERR; + } + } + + return JNI_OK; +} + +jint Arguments::finalize_vm_init_args(SysClassPath* scp_p, bool scp_assembly_required) { + // This must be done after all -D arguments have been processed. + scp_p->expand_endorsed(); + + if (scp_assembly_required || scp_p->get_endorsed() != NULL) { + // Assemble the bootclasspath elements into the final path. + Arguments::set_sysclasspath(scp_p->combined_path()); + } + + // This must be done after all arguments have been processed. + // java_compiler() true means set to "NONE" or empty. + if (java_compiler() && !xdebug_mode()) { + // For backwards compatibility, we switch to interpreted mode if + // -Djava.compiler="NONE" or "" is specified AND "-Xdebug" was + // not specified. + set_mode_flags(_int); + } + if (CompileThreshold == 0) { + set_mode_flags(_int); + } + +#ifdef TIERED + // If we are using tiered compilation in the tiered vm then c1 will + // do the profiling and we don't want to waste that time in the + // interpreter. + if (TieredCompilation) { + ProfileInterpreter = false; + } else { + // Since we are running vanilla server we must adjust the compile threshold + // unless the user has already adjusted it because the default threshold assumes + // we will run tiered. + + if (FLAG_IS_DEFAULT(CompileThreshold)) { + CompileThreshold = Tier2CompileThreshold; + } + } +#endif // TIERED + +#ifndef COMPILER2 + // Don't degrade server performance for footprint + if (FLAG_IS_DEFAULT(UseLargePages) && + MaxHeapSize < LargePageHeapSizeThreshold) { + // No need for large granularity pages w/small heaps. + // Note that large pages are enabled/disabled for both the + // Java heap and the code cache. + FLAG_SET_DEFAULT(UseLargePages, false); + SOLARIS_ONLY(FLAG_SET_DEFAULT(UseMPSS, false)); + SOLARIS_ONLY(FLAG_SET_DEFAULT(UseISM, false)); + } +#else + if (!FLAG_IS_DEFAULT(OptoLoopAlignment) && FLAG_IS_DEFAULT(MaxLoopPad)) { + FLAG_SET_DEFAULT(MaxLoopPad, OptoLoopAlignment-1); + } +#endif + + if (!check_vm_args_consistency()) { + return JNI_ERR; + } + + return JNI_OK; +} + +jint Arguments::parse_java_options_environment_variable(SysClassPath* scp_p, bool* scp_assembly_required_p) { + return parse_options_environment_variable("_JAVA_OPTIONS", scp_p, + scp_assembly_required_p); +} + +jint Arguments::parse_java_tool_options_environment_variable(SysClassPath* scp_p, bool* scp_assembly_required_p) { + return parse_options_environment_variable("JAVA_TOOL_OPTIONS", scp_p, + scp_assembly_required_p); +} + +jint Arguments::parse_options_environment_variable(const char* name, SysClassPath* scp_p, bool* scp_assembly_required_p) { + const int N_MAX_OPTIONS = 64; + const int OPTION_BUFFER_SIZE = 1024; + char buffer[OPTION_BUFFER_SIZE]; + + // The variable will be ignored if it exceeds the length of the buffer. + // Don't check this variable if user has special privileges + // (e.g. unix su command). + if (os::getenv(name, buffer, sizeof(buffer)) && + !os::have_special_privileges()) { + JavaVMOption options[N_MAX_OPTIONS]; // Construct option array + jio_fprintf(defaultStream::error_stream(), + "Picked up %s: %s\n", name, buffer); + char* rd = buffer; // pointer to the input string (rd) + int i; + for (i = 0; i < N_MAX_OPTIONS;) { // repeat for all options in the input string + while (isspace(*rd)) rd++; // skip whitespace + if (*rd == 0) break; // we re done when the input string is read completely + + // The output, option string, overwrites the input string. + // Because of quoting, the pointer to the option string (wrt) may lag the pointer to + // input string (rd). + char* wrt = rd; + + options[i++].optionString = wrt; // Fill in option + while (*rd != 0 && !isspace(*rd)) { // unquoted strings terminate with a space or NULL + if (*rd == '\'' || *rd == '"') { // handle a quoted string + int quote = *rd; // matching quote to look for + rd++; // don't copy open quote + while (*rd != quote) { // include everything (even spaces) up until quote + if (*rd == 0) { // string termination means unmatched string + jio_fprintf(defaultStream::error_stream(), + "Unmatched quote in %s\n", name); + return JNI_ERR; + } + *wrt++ = *rd++; // copy to option string + } + rd++; // don't copy close quote + } else { + *wrt++ = *rd++; // copy to option string + } + } + // Need to check if we're done before writing a NULL, + // because the write could be to the byte that rd is pointing to. + if (*rd++ == 0) { + *wrt = 0; + break; + } + *wrt = 0; // Zero terminate option + } + // Construct JavaVMInitArgs structure and parse as if it was part of the command line + JavaVMInitArgs vm_args; + vm_args.version = JNI_VERSION_1_2; + vm_args.options = options; + vm_args.nOptions = i; + vm_args.ignoreUnrecognized = false; + + if (PrintVMOptions) { + const char* tail; + for (int i = 0; i < vm_args.nOptions; i++) { + const JavaVMOption *option = vm_args.options + i; + if (match_option(option, "-XX:", &tail)) { + logOption(tail); + } + } + } + + return(parse_each_vm_init_arg(&vm_args, scp_p, scp_assembly_required_p, ENVIRON_VAR)); + } + return JNI_OK; +} + +// Parse entry point called from JNI_CreateJavaVM + +jint Arguments::parse(const JavaVMInitArgs* args) { + + // Sharing support + // Construct the path to the archive + char jvm_path[JVM_MAXPATHLEN]; + os::jvm_path(jvm_path, sizeof(jvm_path)); +#ifdef TIERED + if (strstr(jvm_path, "client") != NULL) { + force_client_mode = true; + } +#endif // TIERED + char *end = strrchr(jvm_path, *os::file_separator()); + if (end != NULL) *end = '\0'; + char *shared_archive_path = NEW_C_HEAP_ARRAY(char, strlen(jvm_path) + + strlen(os::file_separator()) + 20); + if (shared_archive_path == NULL) return JNI_ENOMEM; + strcpy(shared_archive_path, jvm_path); + strcat(shared_archive_path, os::file_separator()); + strcat(shared_archive_path, "classes"); + DEBUG_ONLY(strcat(shared_archive_path, "_g");) + strcat(shared_archive_path, ".jsa"); + SharedArchivePath = shared_archive_path; + + // Remaining part of option string + const char* tail; + + // If flag "-XX:Flags=flags-file" is used it will be the first option to be processed. + bool settings_file_specified = false; + int index; + for (index = 0; index < args->nOptions; index++) { + const JavaVMOption *option = args->options + index; + if (match_option(option, "-XX:Flags=", &tail)) { + if (!process_settings_file(tail, true, args->ignoreUnrecognized)) { + return JNI_EINVAL; + } + settings_file_specified = true; + } + if (match_option(option, "-XX:+PrintVMOptions", &tail)) { + PrintVMOptions = true; + } + } + + // Parse default .hotspotrc settings file + if (!settings_file_specified) { + if (!process_settings_file(".hotspotrc", false, args->ignoreUnrecognized)) { + return JNI_EINVAL; + } + } + + if (PrintVMOptions) { + for (index = 0; index < args->nOptions; index++) { + const JavaVMOption *option = args->options + index; + if (match_option(option, "-XX:", &tail)) { + logOption(tail); + } + } + } + + // Parse JavaVMInitArgs structure passed in, as well as JAVA_TOOL_OPTIONS and _JAVA_OPTIONS + jint result = parse_vm_init_args(args); + if (result != JNI_OK) { + return result; + } + +#ifndef PRODUCT + if (TraceBytecodesAt != 0) { + TraceBytecodes = true; + } + if (CountCompiledCalls) { + if (UseCounterDecay) { + warning("UseCounterDecay disabled because CountCalls is set"); + UseCounterDecay = false; + } + } +#endif // PRODUCT + + if (PrintGCDetails) { + // Turn on -verbose:gc options as well + PrintGC = true; + if (FLAG_IS_DEFAULT(TraceClassUnloading)) { + TraceClassUnloading = true; + } + } + +#ifdef SERIALGC + set_serial_gc_flags(); +#endif // SERIALGC +#ifdef KERNEL + no_shared_spaces(); +#endif // KERNEL + + // Set some flags for ParallelGC if needed. + set_parallel_gc_flags(); + + // Set some flags for CMS and/or ParNew collectors, as needed. + set_cms_and_parnew_gc_flags(); + + // Set flags based on ergonomics. + set_ergonomics_flags(); + +#ifdef SERIALGC + assert(verify_serial_gc_flags(), "SerialGC unset"); +#endif // SERIALGC + + // Set bytecode rewriting flags + set_bytecode_flags(); + + // Set flags if Aggressive optimization flags (-XX:+AggressiveOpts) enabled. + set_aggressive_opts_flags(); + +#ifdef CC_INTERP + // Biased locking is not implemented with c++ interpreter + FLAG_SET_DEFAULT(UseBiasedLocking, false); +#endif /* CC_INTERP */ + + if (PrintCommandLineFlags) { + CommandLineFlags::printSetFlags(); + } + + return JNI_OK; +} + +int Arguments::PropertyList_count(SystemProperty* pl) { + int count = 0; + while(pl != NULL) { + count++; + pl = pl->next(); + } + return count; +} + +const char* Arguments::PropertyList_get_value(SystemProperty *pl, const char* key) { + assert(key != NULL, "just checking"); + SystemProperty* prop; + for (prop = pl; prop != NULL; prop = prop->next()) { + if (strcmp(key, prop->key()) == 0) return prop->value(); + } + return NULL; +} + +const char* Arguments::PropertyList_get_key_at(SystemProperty *pl, int index) { + int count = 0; + const char* ret_val = NULL; + + while(pl != NULL) { + if(count >= index) { + ret_val = pl->key(); + break; + } + count++; + pl = pl->next(); + } + + return ret_val; +} + +char* Arguments::PropertyList_get_value_at(SystemProperty* pl, int index) { + int count = 0; + char* ret_val = NULL; + + while(pl != NULL) { + if(count >= index) { + ret_val = pl->value(); + break; + } + count++; + pl = pl->next(); + } + + return ret_val; +} + +void Arguments::PropertyList_add(SystemProperty** plist, SystemProperty *new_p) { + SystemProperty* p = *plist; + if (p == NULL) { + *plist = new_p; + } else { + while (p->next() != NULL) { + p = p->next(); + } + p->set_next(new_p); + } +} + +void Arguments::PropertyList_add(SystemProperty** plist, const char* k, char* v) { + if (plist == NULL) + return; + + SystemProperty* new_p = new SystemProperty(k, v, true); + PropertyList_add(plist, new_p); +} + +// This add maintains unique property key in the list. +void Arguments::PropertyList_unique_add(SystemProperty** plist, const char* k, char* v) { + if (plist == NULL) + return; + + // If property key exist then update with new value. + SystemProperty* prop; + for (prop = *plist; prop != NULL; prop = prop->next()) { + if (strcmp(k, prop->key()) == 0) { + prop->set_value(v); + return; + } + } + + PropertyList_add(plist, k, v); +} + +#ifdef KERNEL +char *Arguments::get_kernel_properties() { + // Find properties starting with kernel and append them to string + // We need to find out how long they are first because the URL's that they + // might point to could get long. + int length = 0; + SystemProperty* prop; + for (prop = _system_properties; prop != NULL; prop = prop->next()) { + if (strncmp(prop->key(), "kernel.", 7 ) == 0) { + length += (strlen(prop->key()) + strlen(prop->value()) + 5); // "-D =" + } + } + // Add one for null terminator. + char *props = AllocateHeap(length + 1, "get_kernel_properties"); + if (length != 0) { + int pos = 0; + for (prop = _system_properties; prop != NULL; prop = prop->next()) { + if (strncmp(prop->key(), "kernel.", 7 ) == 0) { + jio_snprintf(&props[pos], length-pos, + "-D%s=%s ", prop->key(), prop->value()); + pos = strlen(props); + } + } + } + // null terminate props in case of null + props[length] = '\0'; + return props; +} +#endif // KERNEL + +// Copies src into buf, replacing "%%" with "%" and "%p" with pid +// Returns true if all of the source pointed by src has been copied over to +// the destination buffer pointed by buf. Otherwise, returns false. +// Notes: +// 1. If the length (buflen) of the destination buffer excluding the +// NULL terminator character is not long enough for holding the expanded +// pid characters, it also returns false instead of returning the partially +// expanded one. +// 2. The passed in "buflen" should be large enough to hold the null terminator. +bool Arguments::copy_expand_pid(const char* src, size_t srclen, + char* buf, size_t buflen) { + const char* p = src; + char* b = buf; + const char* src_end = &src[srclen]; + char* buf_end = &buf[buflen - 1]; + + while (p < src_end && b < buf_end) { + if (*p == '%') { + switch (*(++p)) { + case '%': // "%%" ==> "%" + *b++ = *p++; + break; + case 'p': { // "%p" ==> current process id + // buf_end points to the character before the last character so + // that we could write '\0' to the end of the buffer. + size_t buf_sz = buf_end - b + 1; + int ret = jio_snprintf(b, buf_sz, "%d", os::current_process_id()); + + // if jio_snprintf fails or the buffer is not long enough to hold + // the expanded pid, returns false. + if (ret < 0 || ret >= (int)buf_sz) { + return false; + } else { + b += ret; + assert(*b == '\0', "fail in copy_expand_pid"); + if (p == src_end && b == buf_end + 1) { + // reach the end of the buffer. + return true; + } + } + p++; + break; + } + default : + *b++ = '%'; + } + } else { + *b++ = *p++; + } + } + *b = '\0'; + return (p == src_end); // return false if not all of the source was copied +} diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp new file mode 100644 index 00000000000..fc373c41dd0 --- /dev/null +++ b/hotspot/src/share/vm/runtime/arguments.hpp @@ -0,0 +1,511 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Arguments parses the command line and recognizes options + +// Invocation API hook typedefs (these should really be defined in jni.hpp) +extern "C" { + typedef void (JNICALL *abort_hook_t)(void); + typedef void (JNICALL *exit_hook_t)(jint code); + typedef jint (JNICALL *vfprintf_hook_t)(FILE *fp, const char *format, va_list args); +} + +// Forward declarations + +class SysClassPath; + +// Element describing System and User (-Dkey=value flags) defined property. + +class SystemProperty: public CHeapObj { + private: + char* _key; + char* _value; + SystemProperty* _next; + bool _writeable; + bool writeable() { return _writeable; } + + public: + // Accessors + const char* key() const { return _key; } + char* value() const { return _value; } + SystemProperty* next() const { return _next; } + void set_next(SystemProperty* next) { _next = next; } + bool set_value(char *value) { + if (writeable()) { + if (_value != NULL) { + FreeHeap(_value); + } + _value = AllocateHeap(strlen(value)+1); + if (_value != NULL) { + strcpy(_value, value); + } + return true; + } + return false; + } + + void append_value(const char *value) { + char *sp; + size_t len = 0; + if (value != NULL) { + len = strlen(value); + if (_value != NULL) { + len += strlen(_value); + } + sp = AllocateHeap(len+2); + if (sp != NULL) { + if (_value != NULL) { + strcpy(sp, _value); + strcat(sp, os::path_separator()); + strcat(sp, value); + FreeHeap(_value); + } else { + strcpy(sp, value); + } + _value = sp; + } + } + } + + // Constructor + SystemProperty(const char* key, const char* value, bool writeable) { + if (key == NULL) { + _key = NULL; + } else { + _key = AllocateHeap(strlen(key)+1); + strcpy(_key, key); + } + if (value == NULL) { + _value = NULL; + } else { + _value = AllocateHeap(strlen(value)+1); + strcpy(_value, value); + } + _next = NULL; + _writeable = writeable; + } +}; + + +// For use by -agentlib, -agentpath and -Xrun +class AgentLibrary : public CHeapObj { + friend class AgentLibraryList; + private: + char* _name; + char* _options; + void* _os_lib; + bool _is_absolute_path; + AgentLibrary* _next; + + public: + // Accessors + const char* name() const { return _name; } + char* options() const { return _options; } + bool is_absolute_path() const { return _is_absolute_path; } + void* os_lib() const { return _os_lib; } + void set_os_lib(void* os_lib) { _os_lib = os_lib; } + AgentLibrary* next() const { return _next; } + + // Constructor + AgentLibrary(const char* name, const char* options, bool is_absolute_path, void* os_lib) { + _name = AllocateHeap(strlen(name)+1); + strcpy(_name, name); + if (options == NULL) { + _options = NULL; + } else { + _options = AllocateHeap(strlen(options)+1); + strcpy(_options, options); + } + _is_absolute_path = is_absolute_path; + _os_lib = os_lib; + _next = NULL; + } +}; + +// maintain an order of entry list of AgentLibrary +class AgentLibraryList VALUE_OBJ_CLASS_SPEC { + private: + AgentLibrary* _first; + AgentLibrary* _last; + public: + bool is_empty() const { return _first == NULL; } + AgentLibrary* first() const { return _first; } + + // add to the end of the list + void add(AgentLibrary* lib) { + if (is_empty()) { + _first = _last = lib; + } else { + _last->_next = lib; + _last = lib; + } + lib->_next = NULL; + } + + // search for and remove a library known to be in the list + void remove(AgentLibrary* lib) { + AgentLibrary* curr; + AgentLibrary* prev = NULL; + for (curr = first(); curr != NULL; prev = curr, curr = curr->next()) { + if (curr == lib) { + break; + } + } + assert(curr != NULL, "always should be found"); + + if (curr != NULL) { + // it was found, by-pass this library + if (prev == NULL) { + _first = curr->_next; + } else { + prev->_next = curr->_next; + } + if (curr == _last) { + _last = prev; + } + curr->_next = NULL; + } + } + + AgentLibraryList() { + _first = NULL; + _last = NULL; + } +}; + + +class Arguments : AllStatic { + friend class VMStructs; + friend class JvmtiExport; + public: + // Operation modi + enum Mode { + _int, // corresponds to -Xint + _mixed, // corresponds to -Xmixed + _comp // corresponds to -Xcomp + }; + + enum ArgsRange { + arg_unreadable = -3, + arg_too_small = -2, + arg_too_big = -1, + arg_in_range = 0 + }; + + private: + + // an array containing all flags specified in the .hotspotrc file + static char** _jvm_flags_array; + static int _num_jvm_flags; + // an array containing all jvm arguments specified in the command line + static char** _jvm_args_array; + static int _num_jvm_args; + // string containing all java command (class/jarfile name and app args) + static char* _java_command; + + // Property list + static SystemProperty* _system_properties; + + // Quick accessor to System properties in the list: + static SystemProperty *_java_ext_dirs; + static SystemProperty *_java_endorsed_dirs; + static SystemProperty *_sun_boot_library_path; + static SystemProperty *_java_library_path; + static SystemProperty *_java_home; + static SystemProperty *_java_class_path; + static SystemProperty *_sun_boot_class_path; + + // Meta-index for knowing what packages are in the boot class path + static char* _meta_index_path; + static char* _meta_index_dir; + + // java.vendor.url.bug, bug reporting URL for fatal errors. + static const char* _java_vendor_url_bug; + + // sun.java.launcher, private property to provide information about + // java/gamma launcher + static const char* _sun_java_launcher; + + // sun.java.launcher.pid, private property + static int _sun_java_launcher_pid; + + // Option flags + static bool _has_profile; + static bool _has_alloc_profile; + static const char* _gc_log_filename; + static uintx _initial_heap_size; + static uintx _min_heap_size; + + // -Xrun arguments + static AgentLibraryList _libraryList; + static void add_init_library(const char* name, char* options) + { _libraryList.add(new AgentLibrary(name, options, false, NULL)); } + + // -agentlib and -agentpath arguments + static AgentLibraryList _agentList; + static void add_init_agent(const char* name, char* options, bool absolute_path) + { _agentList.add(new AgentLibrary(name, options, absolute_path, NULL)); } + + // Late-binding agents not started via arguments + static void add_loaded_agent(const char* name, char* options, bool absolute_path, void* os_lib) + { _agentList.add(new AgentLibrary(name, options, absolute_path, os_lib)); } + + // Operation modi + static Mode _mode; + static void set_mode_flags(Mode mode); + static bool _java_compiler; + static void set_java_compiler(bool arg) { _java_compiler = arg; } + static bool java_compiler() { return _java_compiler; } + + // -Xdebug flag + static bool _xdebug_mode; + static void set_xdebug_mode(bool arg) { _xdebug_mode = arg; } + static bool xdebug_mode() { return _xdebug_mode; } + + // Used to save default settings + static bool _AlwaysCompileLoopMethods; + static bool _UseOnStackReplacement; + static bool _BackgroundCompilation; + static bool _ClipInlining; + static bool _CIDynamicCompilePriority; + static intx _Tier2CompileThreshold; + + // GC processing + static int nof_parallel_gc_threads(); + // CMS/ParNew garbage collectors + static void set_parnew_gc_flags(); + static void set_cms_and_parnew_gc_flags(); + // UseParallelGC + static void set_parallel_gc_flags(); + // GC ergonomics + static void set_ergonomics_flags(); + // Based on automatic selection criteria, should the + // low pause collector be used. + static bool should_auto_select_low_pause_collector(); + + // Bytecode rewriting + static void set_bytecode_flags(); + + // Invocation API hooks + static abort_hook_t _abort_hook; + static exit_hook_t _exit_hook; + static vfprintf_hook_t _vfprintf_hook; + + // System properties + static bool add_property(const char* prop); + + // Aggressive optimization flags. + static void set_aggressive_opts_flags(); + + // Argument parsing + static void do_pd_flag_adjustments(); + static bool parse_argument(const char* arg, FlagValueOrigin origin); + static bool process_argument(const char* arg, jboolean ignore_unrecognized, FlagValueOrigin origin); + static void process_java_launcher_argument(const char*, void*); + static void process_java_compiler_argument(char* arg); + static jint parse_options_environment_variable(const char* name, SysClassPath* scp_p, bool* scp_assembly_required_p); + static jint parse_java_tool_options_environment_variable(SysClassPath* scp_p, bool* scp_assembly_required_p); + static jint parse_java_options_environment_variable(SysClassPath* scp_p, bool* scp_assembly_required_p); + static jint parse_vm_init_args(const JavaVMInitArgs* args); + static jint parse_each_vm_init_arg(const JavaVMInitArgs* args, SysClassPath* scp_p, bool* scp_assembly_required_p, FlagValueOrigin origin); + static jint finalize_vm_init_args(SysClassPath* scp_p, bool scp_assembly_required); + static bool is_bad_option(const JavaVMOption* option, jboolean ignore, + const char* option_type); + static bool is_bad_option(const JavaVMOption* option, jboolean ignore) { + return is_bad_option(option, ignore, NULL); + } + static bool verify_percentage(uintx value, const char* name); + static void describe_range_error(ArgsRange errcode); + static ArgsRange check_memory_size(jlong size, jlong min_size); + static ArgsRange parse_memory_size(const char* s, jlong* long_arg, + jlong min_size); + + // methods to build strings from individual args + static void build_jvm_args(const char* arg); + static void build_jvm_flags(const char* arg); + static void add_string(char*** bldarray, int* count, const char* arg); + static const char* build_resource_string(char** args, int count); + + static bool methodExists( + char* className, char* methodName, + int classesNum, char** classes, bool* allMethods, + int methodsNum, char** methods, bool* allClasses + ); + + static void parseOnlyLine( + const char* line, + short* classesNum, short* classesMax, char*** classes, bool** allMethods, + short* methodsNum, short* methodsMax, char*** methods, bool** allClasses + ); + + // Returns true if the string s is in the list of + // flags made obsolete in 1.5.0. + static bool made_obsolete_in_1_5_0(const char* s); + + static short CompileOnlyClassesNum; + static short CompileOnlyClassesMax; + static char** CompileOnlyClasses; + static bool* CompileOnlyAllMethods; + + static short CompileOnlyMethodsNum; + static short CompileOnlyMethodsMax; + static char** CompileOnlyMethods; + static bool* CompileOnlyAllClasses; + + static short InterpretOnlyClassesNum; + static short InterpretOnlyClassesMax; + static char** InterpretOnlyClasses; + static bool* InterpretOnlyAllMethods; + + static bool CheckCompileOnly; + + static char* SharedArchivePath; + + public: + // Parses the arguments + static jint parse(const JavaVMInitArgs* args); + // Check consistecy or otherwise of VM argument settings + static bool check_vm_args_consistency(); + // Used by os_solaris + static bool process_settings_file(const char* file_name, bool should_exist, jboolean ignore_unrecognized); + + // return a char* array containing all options + static char** jvm_flags_array() { return _jvm_flags_array; } + static char** jvm_args_array() { return _jvm_args_array; } + static int num_jvm_flags() { return _num_jvm_flags; } + static int num_jvm_args() { return _num_jvm_args; } + // return the arguments passed to the Java application + static const char* java_command() { return _java_command; } + + // print jvm_flags, jvm_args and java_command + static void print_on(outputStream* st); + + // convenient methods to obtain / print jvm_flags and jvm_args + static const char* jvm_flags() { return build_resource_string(_jvm_flags_array, _num_jvm_flags); } + static const char* jvm_args() { return build_resource_string(_jvm_args_array, _num_jvm_args); } + static void print_jvm_flags_on(outputStream* st); + static void print_jvm_args_on(outputStream* st); + + // -Dkey=value flags + static SystemProperty* system_properties() { return _system_properties; } + static const char* get_property(const char* key); + + // -Djava.vendor.url.bug + static const char* java_vendor_url_bug() { return _java_vendor_url_bug; } + + // -Dsun.java.launcher + static const char* sun_java_launcher() { return _sun_java_launcher; } + // Was VM created by a Java launcher? + static bool created_by_java_launcher(); + // -Dsun.java.launcher.pid + static int sun_java_launcher_pid() { return _sun_java_launcher_pid; } + + // -Xloggc:, if not specified will be NULL + static const char* gc_log_filename() { return _gc_log_filename; } + + // -Xprof/-Xaprof + static bool has_profile() { return _has_profile; } + static bool has_alloc_profile() { return _has_alloc_profile; } + + // -Xms , -Xmx + static uintx initial_heap_size() { return _initial_heap_size; } + static void set_initial_heap_size(uintx v) { _initial_heap_size = v; } + static uintx min_heap_size() { return _min_heap_size; } + static void set_min_heap_size(uintx v) { _min_heap_size = v; } + + // -Xrun + static AgentLibrary* libraries() { return _libraryList.first(); } + static bool init_libraries_at_startup() { return !_libraryList.is_empty(); } + static void convert_library_to_agent(AgentLibrary* lib) + { _libraryList.remove(lib); + _agentList.add(lib); } + + // -agentlib -agentpath + static AgentLibrary* agents() { return _agentList.first(); } + static bool init_agents_at_startup() { return !_agentList.is_empty(); } + + // abort, exit, vfprintf hooks + static abort_hook_t abort_hook() { return _abort_hook; } + static exit_hook_t exit_hook() { return _exit_hook; } + static vfprintf_hook_t vfprintf_hook() { return _vfprintf_hook; } + + static bool GetCheckCompileOnly () { return CheckCompileOnly; } + + static const char* GetSharedArchivePath() { return SharedArchivePath; } + + static bool CompileMethod(char* className, char* methodName) { + return + methodExists( + className, methodName, + CompileOnlyClassesNum, CompileOnlyClasses, CompileOnlyAllMethods, + CompileOnlyMethodsNum, CompileOnlyMethods, CompileOnlyAllClasses + ); + } + + // Java launcher properties + static void process_sun_java_launcher_properties(JavaVMInitArgs* args); + + // System properties + static void init_system_properties(); + + // Proptery List manipulation + static void PropertyList_add(SystemProperty** plist, SystemProperty *element); + static void PropertyList_add(SystemProperty** plist, const char* k, char* v); + static void PropertyList_unique_add(SystemProperty** plist, const char* k, char* v); + static const char* PropertyList_get_value(SystemProperty* plist, const char* key); + static int PropertyList_count(SystemProperty* pl); + static const char* PropertyList_get_key_at(SystemProperty* pl,int index); + static char* PropertyList_get_value_at(SystemProperty* pl,int index); + + // Miscellaneous System property value getter and setters. + static void set_dll_dir(char *value) { _sun_boot_library_path->set_value(value); } + static void set_java_home(char *value) { _java_home->set_value(value); } + static void set_library_path(char *value) { _java_library_path->set_value(value); } + static void set_ext_dirs(char *value) { _java_ext_dirs->set_value(value); } + static void set_endorsed_dirs(char *value) { _java_endorsed_dirs->set_value(value); } + static void set_sysclasspath(char *value) { _sun_boot_class_path->set_value(value); } + static void append_sysclasspath(const char *value) { _sun_boot_class_path->append_value(value); } + static void set_meta_index_path(char* meta_index_path, char* meta_index_dir) { + _meta_index_path = meta_index_path; + _meta_index_dir = meta_index_dir; + } + + static char *get_java_home() { return _java_home->value(); } + static char *get_dll_dir() { return _sun_boot_library_path->value(); } + static char *get_endorsed_dir() { return _java_endorsed_dirs->value(); } + static char *get_sysclasspath() { return _sun_boot_class_path->value(); } + static char* get_meta_index_path() { return _meta_index_path; } + static char* get_meta_index_dir() { return _meta_index_dir; } + + // Operation modi + static Mode mode() { return _mode; } + + // Utility: copies src into buf, replacing "%%" with "%" and "%p" with pid. + static bool copy_expand_pid(const char* src, size_t srclen, char* buf, size_t buflen); + +#ifdef KERNEL + // For java kernel vm, return property string for kernel properties. + static char *get_kernel_properties(); +#endif // KERNEL +}; diff --git a/hotspot/src/share/vm/runtime/atomic.cpp b/hotspot/src/share/vm/runtime/atomic.cpp new file mode 100644 index 00000000000..299d2b00af6 --- /dev/null +++ b/hotspot/src/share/vm/runtime/atomic.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_atomic.cpp.incl" + +jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) { + assert(sizeof(jbyte) == 1, "assumption."); + uintptr_t dest_addr = (uintptr_t)dest; + uintptr_t offset = dest_addr % sizeof(jint); + volatile jint* dest_int = (volatile jint*)(dest_addr - offset); + jint cur = *dest_int; + jbyte* cur_as_bytes = (jbyte*)(&cur); + jint new_val = cur; + jbyte* new_val_as_bytes = (jbyte*)(&new_val); + new_val_as_bytes[offset] = exchange_value; + while (cur_as_bytes[offset] == compare_value) { + jint res = cmpxchg(new_val, dest_int, cur); + if (res == cur) break; + cur = res; + new_val = cur; + new_val_as_bytes[offset] = exchange_value; + } + return cur_as_bytes[offset]; +} diff --git a/hotspot/src/share/vm/runtime/atomic.hpp b/hotspot/src/share/vm/runtime/atomic.hpp new file mode 100644 index 00000000000..cfbda4ba7f7 --- /dev/null +++ b/hotspot/src/share/vm/runtime/atomic.hpp @@ -0,0 +1,70 @@ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class Atomic : AllStatic { + public: + // Atomically store to a location + static void store (jbyte store_value, jbyte* dest); + static void store (jshort store_value, jshort* dest); + static void store (jint store_value, jint* dest); + static void store (jlong store_value, jlong* dest); + static void store_ptr(intptr_t store_value, intptr_t* dest); + static void store_ptr(void* store_value, void* dest); + + static void store (jbyte store_value, volatile jbyte* dest); + static void store (jshort store_value, volatile jshort* dest); + static void store (jint store_value, volatile jint* dest); + static void store (jlong store_value, volatile jlong* dest); + static void store_ptr(intptr_t store_value, volatile intptr_t* dest); + static void store_ptr(void* store_value, volatile void* dest); + + // Atomically add to a location, return updated value + static jint add (jint add_value, volatile jint* dest); + static intptr_t add_ptr(intptr_t add_value, volatile intptr_t* dest); + static void* add_ptr(intptr_t add_value, volatile void* dest); + + // Atomically increment location + static void inc (volatile jint* dest); + static void inc_ptr(volatile intptr_t* dest); + static void inc_ptr(volatile void* dest); + + // Atomically decrement a location + static void dec (volatile jint* dest); + static void dec_ptr(volatile intptr_t* dest); + static void dec_ptr(volatile void* dest); + + // Performs atomic exchange of *dest with exchange_value. Returns old prior value of *dest. + static jint xchg (jint exchange_value, volatile jint* dest); + static intptr_t xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest); + static void* xchg_ptr(void* exchange_value, volatile void* dest); + + // Performs atomic compare of *dest and compare_value, and exchanges *dest with exchange_value + // if the comparison succeeded. Returns prior value of *dest. Guarantees a two-way memory + // barrier across the cmpxchg. I.e., it's really a 'fence_cmpxchg_acquire'. + static jbyte cmpxchg (jbyte exchange_value, volatile jbyte* dest, jbyte compare_value); + static jint cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value); + static jlong cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value); + static intptr_t cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value); + static void* cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value); +}; diff --git a/hotspot/src/share/vm/runtime/biasedLocking.cpp b/hotspot/src/share/vm/runtime/biasedLocking.cpp new file mode 100644 index 00000000000..bfb3b790432 --- /dev/null +++ b/hotspot/src/share/vm/runtime/biasedLocking.cpp @@ -0,0 +1,752 @@ + +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_biasedLocking.cpp.incl" + +static bool _biased_locking_enabled = false; +BiasedLockingCounters BiasedLocking::_counters; + +static GrowableArray* _preserved_oop_stack = NULL; +static GrowableArray* _preserved_mark_stack = NULL; + +static void enable_biased_locking(klassOop k) { + Klass::cast(k)->set_prototype_header(markOopDesc::biased_locking_prototype()); +} + +class VM_EnableBiasedLocking: public VM_Operation { + public: + VM_EnableBiasedLocking() {} + VMOp_Type type() const { return VMOp_EnableBiasedLocking; } + void doit() { + // Iterate the system dictionary enabling biased locking for all + // currently loaded classes + SystemDictionary::classes_do(enable_biased_locking); + // Indicate that future instances should enable it as well + _biased_locking_enabled = true; + + if (TraceBiasedLocking) { + tty->print_cr("Biased locking enabled"); + } + } + + bool allow_nested_vm_operations() const { return false; } +}; + + +// One-shot PeriodicTask subclass for enabling biased locking +class EnableBiasedLockingTask : public PeriodicTask { + public: + EnableBiasedLockingTask(size_t interval_time) : PeriodicTask(interval_time) {} + + virtual void task() { + VM_EnableBiasedLocking op; + VMThread::execute(&op); + + // Reclaim our storage and disenroll ourself + delete this; + } +}; + + +void BiasedLocking::init() { + // If biased locking is enabled, schedule a task to fire a few + // seconds into the run which turns on biased locking for all + // currently loaded classes as well as future ones. This is a + // workaround for startup time regressions due to a large number of + // safepoints being taken during VM startup for bias revocation. + // Ideally we would have a lower cost for individual bias revocation + // and not need a mechanism like this. + if (UseBiasedLocking) { + if (BiasedLockingStartupDelay > 0) { + EnableBiasedLockingTask* task = new EnableBiasedLockingTask(BiasedLockingStartupDelay); + task->enroll(); + } else { + VM_EnableBiasedLocking op; + VMThread::execute(&op); + } + } +} + + +bool BiasedLocking::enabled() { + return _biased_locking_enabled; +} + +// Returns MonitorInfos for all objects locked on this thread in youngest to oldest order +static GrowableArray* get_or_compute_monitor_info(JavaThread* thread) { + GrowableArray* info = thread->cached_monitor_info(); + if (info != NULL) { + return info; + } + + info = new GrowableArray(); + + // It's possible for the thread to not have any Java frames on it, + // i.e., if it's the main thread and it's already returned from main() + if (thread->has_last_Java_frame()) { + RegisterMap rm(thread); + for (javaVFrame* vf = thread->last_java_vframe(&rm); vf != NULL; vf = vf->java_sender()) { + GrowableArray *monitors = vf->monitors(); + if (monitors != NULL) { + int len = monitors->length(); + // Walk monitors youngest to oldest + for (int i = len - 1; i >= 0; i--) { + MonitorInfo* mon_info = monitors->at(i); + oop owner = mon_info->owner(); + if (owner != NULL) { + info->append(mon_info); + } + } + } + } + } + + thread->set_cached_monitor_info(info); + return info; +} + + +static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread) { + markOop mark = obj->mark(); + if (!mark->has_bias_pattern()) { + if (TraceBiasedLocking) { + ResourceMark rm; + tty->print_cr(" (Skipping revocation of object of type %s because it's no longer biased)", + Klass::cast(obj->klass())->external_name()); + } + return BiasedLocking::NOT_BIASED; + } + + int age = mark->age(); + markOop biased_prototype = markOopDesc::biased_locking_prototype()->set_age(age); + markOop unbiased_prototype = markOopDesc::prototype()->set_age(age); + + if (TraceBiasedLocking && (Verbose || !is_bulk)) { + ResourceMark rm; + tty->print_cr("Revoking bias of object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s , prototype header " INTPTR_FORMAT " , allow rebias %d , requesting thread " INTPTR_FORMAT, + (intptr_t) obj, (intptr_t) mark, Klass::cast(obj->klass())->external_name(), (intptr_t) Klass::cast(obj->klass())->prototype_header(), (allow_rebias ? 1 : 0), (intptr_t) requesting_thread); + } + + JavaThread* biased_thread = mark->biased_locker(); + if (biased_thread == NULL) { + // Object is anonymously biased. We can get here if, for + // example, we revoke the bias due to an identity hash code + // being computed for an object. + if (!allow_rebias) { + obj->set_mark(unbiased_prototype); + } + if (TraceBiasedLocking && (Verbose || !is_bulk)) { + tty->print_cr(" Revoked bias of anonymously-biased object"); + } + return BiasedLocking::BIAS_REVOKED; + } + + // Handle case where the thread toward which the object was biased has exited + bool thread_is_alive = false; + if (requesting_thread == biased_thread) { + thread_is_alive = true; + } else { + for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) { + if (cur_thread == biased_thread) { + thread_is_alive = true; + break; + } + } + } + if (!thread_is_alive) { + if (allow_rebias) { + obj->set_mark(biased_prototype); + } else { + obj->set_mark(unbiased_prototype); + } + if (TraceBiasedLocking && (Verbose || !is_bulk)) { + tty->print_cr(" Revoked bias of object biased toward dead thread"); + } + return BiasedLocking::BIAS_REVOKED; + } + + // Thread owning bias is alive. + // Check to see whether it currently owns the lock and, if so, + // write down the needed displaced headers to the thread's stack. + // Otherwise, restore the object's header either to the unlocked + // or unbiased state. + GrowableArray* cached_monitor_info = get_or_compute_monitor_info(biased_thread); + BasicLock* highest_lock = NULL; + for (int i = 0; i < cached_monitor_info->length(); i++) { + MonitorInfo* mon_info = cached_monitor_info->at(i); + if (mon_info->owner() == obj) { + if (TraceBiasedLocking && Verbose) { + tty->print_cr(" mon_info->owner (" PTR_FORMAT ") == obj (" PTR_FORMAT ")", + (intptr_t) mon_info->owner(), + (intptr_t) obj); + } + // Assume recursive case and fix up highest lock later + markOop mark = markOopDesc::encode((BasicLock*) NULL); + highest_lock = mon_info->lock(); + highest_lock->set_displaced_header(mark); + } else { + if (TraceBiasedLocking && Verbose) { + tty->print_cr(" mon_info->owner (" PTR_FORMAT ") != obj (" PTR_FORMAT ")", + (intptr_t) mon_info->owner(), + (intptr_t) obj); + } + } + } + if (highest_lock != NULL) { + // Fix up highest lock to contain displaced header and point + // object at it + highest_lock->set_displaced_header(unbiased_prototype); + // Reset object header to point to displaced mark + obj->set_mark(markOopDesc::encode(highest_lock)); + assert(!obj->mark()->has_bias_pattern(), "illegal mark state: stack lock used bias bit"); + if (TraceBiasedLocking && (Verbose || !is_bulk)) { + tty->print_cr(" Revoked bias of currently-locked object"); + } + } else { + if (TraceBiasedLocking && (Verbose || !is_bulk)) { + tty->print_cr(" Revoked bias of currently-unlocked object"); + } + if (allow_rebias) { + obj->set_mark(biased_prototype); + } else { + // Store the unlocked value into the object's header. + obj->set_mark(unbiased_prototype); + } + } + + return BiasedLocking::BIAS_REVOKED; +} + + +enum HeuristicsResult { + HR_NOT_BIASED = 1, + HR_SINGLE_REVOKE = 2, + HR_BULK_REBIAS = 3, + HR_BULK_REVOKE = 4 +}; + + +static HeuristicsResult update_heuristics(oop o, bool allow_rebias) { + markOop mark = o->mark(); + if (!mark->has_bias_pattern()) { + return HR_NOT_BIASED; + } + + // Heuristics to attempt to throttle the number of revocations. + // Stages: + // 1. Revoke the biases of all objects in the heap of this type, + // but allow rebiasing of those objects if unlocked. + // 2. Revoke the biases of all objects in the heap of this type + // and don't allow rebiasing of these objects. Disable + // allocation of objects of that type with the bias bit set. + Klass* k = o->blueprint(); + jlong cur_time = os::javaTimeMillis(); + jlong last_bulk_revocation_time = k->last_biased_lock_bulk_revocation_time(); + int revocation_count = k->biased_lock_revocation_count(); + if ((revocation_count >= BiasedLockingBulkRebiasThreshold) && + (revocation_count < BiasedLockingBulkRevokeThreshold) && + (last_bulk_revocation_time != 0) && + (cur_time - last_bulk_revocation_time >= BiasedLockingDecayTime)) { + // This is the first revocation we've seen in a while of an + // object of this type since the last time we performed a bulk + // rebiasing operation. The application is allocating objects in + // bulk which are biased toward a thread and then handing them + // off to another thread. We can cope with this allocation + // pattern via the bulk rebiasing mechanism so we reset the + // klass's revocation count rather than allow it to increase + // monotonically. If we see the need to perform another bulk + // rebias operation later, we will, and if subsequently we see + // many more revocation operations in a short period of time we + // will completely disable biasing for this type. + k->set_biased_lock_revocation_count(0); + revocation_count = 0; + } + + // Make revocation count saturate just beyond BiasedLockingBulkRevokeThreshold + if (revocation_count <= BiasedLockingBulkRevokeThreshold) { + revocation_count = k->atomic_incr_biased_lock_revocation_count(); + } + + if (revocation_count == BiasedLockingBulkRevokeThreshold) { + return HR_BULK_REVOKE; + } + + if (revocation_count == BiasedLockingBulkRebiasThreshold) { + return HR_BULK_REBIAS; + } + + return HR_SINGLE_REVOKE; +} + + +static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o, + bool bulk_rebias, + bool attempt_rebias_of_object, + JavaThread* requesting_thread) { + assert(SafepointSynchronize::is_at_safepoint(), "must be done at safepoint"); + + if (TraceBiasedLocking) { + tty->print_cr("* Beginning bulk revocation (kind == %s) because of object " + INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s", + (bulk_rebias ? "rebias" : "revoke"), + (intptr_t) o, (intptr_t) o->mark(), Klass::cast(o->klass())->external_name()); + } + + jlong cur_time = os::javaTimeMillis(); + o->blueprint()->set_last_biased_lock_bulk_revocation_time(cur_time); + + + klassOop k_o = o->klass(); + Klass* klass = Klass::cast(k_o); + + if (bulk_rebias) { + // Use the epoch in the klass of the object to implicitly revoke + // all biases of objects of this data type and force them to be + // reacquired. However, we also need to walk the stacks of all + // threads and update the headers of lightweight locked objects + // with biases to have the current epoch. + + // If the prototype header doesn't have the bias pattern, don't + // try to update the epoch -- assume another VM operation came in + // and reset the header to the unbiased state, which will + // implicitly cause all existing biases to be revoked + if (klass->prototype_header()->has_bias_pattern()) { + int prev_epoch = klass->prototype_header()->bias_epoch(); + klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch()); + int cur_epoch = klass->prototype_header()->bias_epoch(); + + // Now walk all threads' stacks and adjust epochs of any biased + // and locked objects of this data type we encounter + for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) { + GrowableArray* cached_monitor_info = get_or_compute_monitor_info(thr); + for (int i = 0; i < cached_monitor_info->length(); i++) { + MonitorInfo* mon_info = cached_monitor_info->at(i); + oop owner = mon_info->owner(); + markOop mark = owner->mark(); + if ((owner->klass() == k_o) && mark->has_bias_pattern()) { + // We might have encountered this object already in the case of recursive locking + assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment"); + owner->set_mark(mark->set_bias_epoch(cur_epoch)); + } + } + } + } + + // At this point we're done. All we have to do is potentially + // adjust the header of the given object to revoke its bias. + revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread); + } else { + if (TraceBiasedLocking) { + ResourceMark rm; + tty->print_cr("* Disabling biased locking for type %s", klass->external_name()); + } + + // Disable biased locking for this data type. Not only will this + // cause future instances to not be biased, but existing biased + // instances will notice that this implicitly caused their biases + // to be revoked. + klass->set_prototype_header(markOopDesc::prototype()); + + // Now walk all threads' stacks and forcibly revoke the biases of + // any locked and biased objects of this data type we encounter. + for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) { + GrowableArray* cached_monitor_info = get_or_compute_monitor_info(thr); + for (int i = 0; i < cached_monitor_info->length(); i++) { + MonitorInfo* mon_info = cached_monitor_info->at(i); + oop owner = mon_info->owner(); + markOop mark = owner->mark(); + if ((owner->klass() == k_o) && mark->has_bias_pattern()) { + revoke_bias(owner, false, true, requesting_thread); + } + } + } + + // Must force the bias of the passed object to be forcibly revoked + // as well to ensure guarantees to callers + revoke_bias(o, false, true, requesting_thread); + } + + if (TraceBiasedLocking) { + tty->print_cr("* Ending bulk revocation"); + } + + BiasedLocking::Condition status_code = BiasedLocking::BIAS_REVOKED; + + if (attempt_rebias_of_object && + o->mark()->has_bias_pattern() && + klass->prototype_header()->has_bias_pattern()) { + markOop new_mark = markOopDesc::encode(requesting_thread, o->mark()->age(), + klass->prototype_header()->bias_epoch()); + o->set_mark(new_mark); + status_code = BiasedLocking::BIAS_REVOKED_AND_REBIASED; + if (TraceBiasedLocking) { + tty->print_cr(" Rebiased object toward thread " INTPTR_FORMAT, (intptr_t) requesting_thread); + } + } + + assert(!o->mark()->has_bias_pattern() || + (attempt_rebias_of_object && (o->mark()->biased_locker() == requesting_thread)), + "bug in bulk bias revocation"); + + return status_code; +} + + +static void clean_up_cached_monitor_info() { + // Walk the thread list clearing out the cached monitors + for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) { + thr->set_cached_monitor_info(NULL); + } +} + + +class VM_RevokeBias : public VM_Operation { +protected: + Handle* _obj; + GrowableArray* _objs; + JavaThread* _requesting_thread; + BiasedLocking::Condition _status_code; + +public: + VM_RevokeBias(Handle* obj, JavaThread* requesting_thread) + : _obj(obj) + , _objs(NULL) + , _requesting_thread(requesting_thread) + , _status_code(BiasedLocking::NOT_BIASED) {} + + VM_RevokeBias(GrowableArray* objs, JavaThread* requesting_thread) + : _obj(NULL) + , _objs(objs) + , _requesting_thread(requesting_thread) + , _status_code(BiasedLocking::NOT_BIASED) {} + + virtual VMOp_Type type() const { return VMOp_RevokeBias; } + + virtual bool doit_prologue() { + // Verify that there is actual work to do since the callers just + // give us locked object(s). If we don't find any biased objects + // there is nothing to do and we avoid a safepoint. + if (_obj != NULL) { + markOop mark = (*_obj)()->mark(); + if (mark->has_bias_pattern()) { + return true; + } + } else { + for ( int i = 0 ; i < _objs->length(); i++ ) { + markOop mark = (_objs->at(i))()->mark(); + if (mark->has_bias_pattern()) { + return true; + } + } + } + return false; + } + + virtual void doit() { + if (_obj != NULL) { + if (TraceBiasedLocking) { + tty->print_cr("Revoking bias with potentially per-thread safepoint:"); + } + _status_code = revoke_bias((*_obj)(), false, false, _requesting_thread); + clean_up_cached_monitor_info(); + return; + } else { + if (TraceBiasedLocking) { + tty->print_cr("Revoking bias with global safepoint:"); + } + BiasedLocking::revoke_at_safepoint(_objs); + } + } + + BiasedLocking::Condition status_code() const { + return _status_code; + } +}; + + +class VM_BulkRevokeBias : public VM_RevokeBias { +private: + bool _bulk_rebias; + bool _attempt_rebias_of_object; + +public: + VM_BulkRevokeBias(Handle* obj, JavaThread* requesting_thread, + bool bulk_rebias, + bool attempt_rebias_of_object) + : VM_RevokeBias(obj, requesting_thread) + , _bulk_rebias(bulk_rebias) + , _attempt_rebias_of_object(attempt_rebias_of_object) {} + + virtual VMOp_Type type() const { return VMOp_BulkRevokeBias; } + virtual bool doit_prologue() { return true; } + + virtual void doit() { + _status_code = bulk_revoke_or_rebias_at_safepoint((*_obj)(), _bulk_rebias, _attempt_rebias_of_object, _requesting_thread); + clean_up_cached_monitor_info(); + } +}; + + +BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) { + assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint"); + + // We can revoke the biases of anonymously-biased objects + // efficiently enough that we should not cause these revocations to + // update the heuristics because doing so may cause unwanted bulk + // revocations (which are expensive) to occur. + markOop mark = obj->mark(); + if (mark->is_biased_anonymously() && !attempt_rebias) { + // We are probably trying to revoke the bias of this object due to + // an identity hash code computation. Try to revoke the bias + // without a safepoint. This is possible if we can successfully + // compare-and-exchange an unbiased header into the mark word of + // the object, meaning that no other thread has raced to acquire + // the bias of the object. + markOop biased_value = mark; + markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); + markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark); + if (res_mark == biased_value) { + return BIAS_REVOKED; + } + } else if (mark->has_bias_pattern()) { + Klass* k = Klass::cast(obj->klass()); + markOop prototype_header = k->prototype_header(); + if (!prototype_header->has_bias_pattern()) { + // This object has a stale bias from before the bulk revocation + // for this data type occurred. It's pointless to update the + // heuristics at this point so simply update the header with a + // CAS. If we fail this race, the object's bias has been revoked + // by another thread so we simply return and let the caller deal + // with it. + markOop biased_value = mark; + markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark); + assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked"); + return BIAS_REVOKED; + } else if (prototype_header->bias_epoch() != mark->bias_epoch()) { + // The epoch of this biasing has expired indicating that the + // object is effectively unbiased. Depending on whether we need + // to rebias or revoke the bias of this object we can do it + // efficiently enough with a CAS that we shouldn't update the + // heuristics. This is normally done in the assembly code but we + // can reach this point due to various points in the runtime + // needing to revoke biases. + if (attempt_rebias) { + assert(THREAD->is_Java_thread(), ""); + markOop biased_value = mark; + markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch()); + markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark); + if (res_mark == biased_value) { + return BIAS_REVOKED_AND_REBIASED; + } + } else { + markOop biased_value = mark; + markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); + markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark); + if (res_mark == biased_value) { + return BIAS_REVOKED; + } + } + } + } + + HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias); + if (heuristics == HR_NOT_BIASED) { + return NOT_BIASED; + } else if (heuristics == HR_SINGLE_REVOKE) { + if (mark->biased_locker() == THREAD) { + // A thread is trying to revoke the bias of an object biased + // toward it, again likely due to an identity hash code + // computation. We can again avoid a safepoint in this case + // since we are only going to walk our own stack. There are no + // races with revocations occurring in other threads because we + // reach no safepoints in the revocation path. + ResourceMark rm; + if (TraceBiasedLocking) { + tty->print_cr("Revoking bias by walking my own stack:"); + } + BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD); + ((JavaThread*) THREAD)->set_cached_monitor_info(NULL); + assert(cond == BIAS_REVOKED, "why not?"); + return cond; + } else { + VM_RevokeBias revoke(&obj, (JavaThread*) THREAD); + VMThread::execute(&revoke); + return revoke.status_code(); + } + } + + assert((heuristics == HR_BULK_REVOKE) || + (heuristics == HR_BULK_REBIAS), "?"); + VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD, + (heuristics == HR_BULK_REBIAS), + attempt_rebias); + VMThread::execute(&bulk_revoke); + return bulk_revoke.status_code(); +} + + +void BiasedLocking::revoke(GrowableArray* objs) { + assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint"); + if (objs->length() == 0) { + return; + } + VM_RevokeBias revoke(objs, JavaThread::current()); + VMThread::execute(&revoke); +} + + +void BiasedLocking::revoke_at_safepoint(Handle h_obj) { + assert(SafepointSynchronize::is_at_safepoint(), "must only be called while at safepoint"); + oop obj = h_obj(); + HeuristicsResult heuristics = update_heuristics(obj, false); + if (heuristics == HR_SINGLE_REVOKE) { + revoke_bias(obj, false, false, NULL); + } else if ((heuristics == HR_BULK_REBIAS) || + (heuristics == HR_BULK_REVOKE)) { + bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL); + } + clean_up_cached_monitor_info(); +} + + +void BiasedLocking::revoke_at_safepoint(GrowableArray* objs) { + assert(SafepointSynchronize::is_at_safepoint(), "must only be called while at safepoint"); + int len = objs->length(); + for (int i = 0; i < len; i++) { + oop obj = (objs->at(i))(); + HeuristicsResult heuristics = update_heuristics(obj, false); + if (heuristics == HR_SINGLE_REVOKE) { + revoke_bias(obj, false, false, NULL); + } else if ((heuristics == HR_BULK_REBIAS) || + (heuristics == HR_BULK_REVOKE)) { + bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL); + } + } + clean_up_cached_monitor_info(); +} + + +void BiasedLocking::preserve_marks() { + if (!UseBiasedLocking) + return; + + assert(SafepointSynchronize::is_at_safepoint(), "must only be called while at safepoint"); + + assert(_preserved_oop_stack == NULL, "double initialization"); + assert(_preserved_mark_stack == NULL, "double initialization"); + + // In order to reduce the number of mark words preserved during GC + // due to the presence of biased locking, we reinitialize most mark + // words to the class's prototype during GC -- even those which have + // a currently valid bias owner. One important situation where we + // must not clobber a bias is when a biased object is currently + // locked. To handle this case we iterate over the currently-locked + // monitors in a prepass and, if they are biased, preserve their + // mark words here. This should be a relatively small set of objects + // especially compared to the number of objects in the heap. + _preserved_mark_stack = new (ResourceObj::C_HEAP) GrowableArray(10, true); + _preserved_oop_stack = new (ResourceObj::C_HEAP) GrowableArray(10, true); + + ResourceMark rm; + Thread* cur = Thread::current(); + for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) { + if (thread->has_last_Java_frame()) { + RegisterMap rm(thread); + for (javaVFrame* vf = thread->last_java_vframe(&rm); vf != NULL; vf = vf->java_sender()) { + GrowableArray *monitors = vf->monitors(); + if (monitors != NULL) { + int len = monitors->length(); + // Walk monitors youngest to oldest + for (int i = len - 1; i >= 0; i--) { + MonitorInfo* mon_info = monitors->at(i); + oop owner = mon_info->owner(); + if (owner != NULL) { + markOop mark = owner->mark(); + if (mark->has_bias_pattern()) { + _preserved_oop_stack->push(Handle(cur, owner)); + _preserved_mark_stack->push(mark); + } + } + } + } + } + } + } +} + + +void BiasedLocking::restore_marks() { + if (!UseBiasedLocking) + return; + + assert(_preserved_oop_stack != NULL, "double free"); + assert(_preserved_mark_stack != NULL, "double free"); + + int len = _preserved_oop_stack->length(); + for (int i = 0; i < len; i++) { + Handle owner = _preserved_oop_stack->at(i); + markOop mark = _preserved_mark_stack->at(i); + owner->set_mark(mark); + } + + delete _preserved_oop_stack; + _preserved_oop_stack = NULL; + delete _preserved_mark_stack; + _preserved_mark_stack = NULL; +} + + +int* BiasedLocking::total_entry_count_addr() { return _counters.total_entry_count_addr(); } +int* BiasedLocking::biased_lock_entry_count_addr() { return _counters.biased_lock_entry_count_addr(); } +int* BiasedLocking::anonymously_biased_lock_entry_count_addr() { return _counters.anonymously_biased_lock_entry_count_addr(); } +int* BiasedLocking::rebiased_lock_entry_count_addr() { return _counters.rebiased_lock_entry_count_addr(); } +int* BiasedLocking::revoked_lock_entry_count_addr() { return _counters.revoked_lock_entry_count_addr(); } +int* BiasedLocking::fast_path_entry_count_addr() { return _counters.fast_path_entry_count_addr(); } +int* BiasedLocking::slow_path_entry_count_addr() { return _counters.slow_path_entry_count_addr(); } + + +// BiasedLockingCounters + +int BiasedLockingCounters::slow_path_entry_count() { + if (_slow_path_entry_count != 0) { + return _slow_path_entry_count; + } + int sum = _biased_lock_entry_count + _anonymously_biased_lock_entry_count + + _rebiased_lock_entry_count + _revoked_lock_entry_count + + _fast_path_entry_count; + + return _total_entry_count - sum; +} + +void BiasedLockingCounters::print_on(outputStream* st) { + tty->print_cr("# total entries: %d", _total_entry_count); + tty->print_cr("# biased lock entries: %d", _biased_lock_entry_count); + tty->print_cr("# anonymously biased lock entries: %d", _anonymously_biased_lock_entry_count); + tty->print_cr("# rebiased lock entries: %d", _rebiased_lock_entry_count); + tty->print_cr("# revoked lock entries: %d", _revoked_lock_entry_count); + tty->print_cr("# fast path lock entries: %d", _fast_path_entry_count); + tty->print_cr("# slow path lock entries: %d", slow_path_entry_count()); +} diff --git a/hotspot/src/share/vm/runtime/biasedLocking.hpp b/hotspot/src/share/vm/runtime/biasedLocking.hpp new file mode 100644 index 00000000000..49fe7730025 --- /dev/null +++ b/hotspot/src/share/vm/runtime/biasedLocking.hpp @@ -0,0 +1,187 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This class describes operations to implement Store-Free Biased +// Locking. The high-level properties of the scheme are similar to +// IBM's lock reservation, Dice-Moir-Scherer QR locks, and other biased +// locking mechanisms. The principal difference is in the handling of +// recursive locking which is how this technique achieves a more +// efficient fast path than these other schemes. +// +// The basic observation is that in HotSpot's current fast locking +// scheme, recursive locking (in the fast path) causes no update to +// the object header. The recursion is described simply by stack +// records containing a specific value (NULL). Only the last unlock by +// a given thread causes an update to the object header. +// +// This observation, coupled with the fact that HotSpot only compiles +// methods for which monitor matching is obeyed (and which therefore +// can not throw IllegalMonitorStateException), implies that we can +// completely eliminate modifications to the object header for +// recursive locking in compiled code, and perform similar recursion +// checks and throwing of IllegalMonitorStateException in the +// interpreter with little or no impact on the performance of the fast +// path. +// +// The basic algorithm is as follows (note, see below for more details +// and information). A pattern in the low three bits is reserved in +// the object header to indicate whether biasing of a given object's +// lock is currently being done or is allowed at all. If the bias +// pattern is present, the contents of the rest of the header are +// either the JavaThread* of the thread to which the lock is biased, +// or NULL, indicating that the lock is "anonymously biased". The +// first thread which locks an anonymously biased object biases the +// lock toward that thread. If another thread subsequently attempts to +// lock the same object, the bias is revoked. +// +// Because there are no updates to the object header at all during +// recursive locking while the lock is biased, the biased lock entry +// code is simply a test of the object header's value. If this test +// succeeds, the lock has been acquired by the thread. If this test +// fails, a bit test is done to see whether the bias bit is still +// set. If not, we fall back to HotSpot's original CAS-based locking +// scheme. If it is set, we attempt to CAS in a bias toward this +// thread. The latter operation is expected to be the rarest operation +// performed on these locks. We optimistically expect the biased lock +// entry to hit most of the time, and want the CAS-based fallthrough +// to occur quickly in the situations where the bias has been revoked. +// +// Revocation of the lock's bias is fairly straightforward. We want to +// restore the object's header and stack-based BasicObjectLocks and +// BasicLocks to the state they would have been in had the object been +// locked by HotSpot's usual fast locking scheme. To do this, we bring +// the system to a safepoint and walk the stack of the thread toward +// which the lock is biased. We find all of the lock records on the +// stack corresponding to this object, in particular the first / +// "highest" record. We fill in the highest lock record with the +// object's displaced header (which is a well-known value given that +// we don't maintain an identity hash nor age bits for the object +// while it's in the biased state) and all other lock records with 0, +// the value for recursive locks. When the safepoint is released, the +// formerly-biased thread and all other threads revert back to +// HotSpot's CAS-based locking. +// +// This scheme can not handle transfers of biases of single objects +// from thread to thread efficiently, but it can handle bulk transfers +// of such biases, which is a usage pattern showing up in some +// applications and benchmarks. We implement "bulk rebias" and "bulk +// revoke" operations using a "bias epoch" on a per-data-type basis. +// If too many bias revocations are occurring for a particular data +// type, the bias epoch for the data type is incremented at a +// safepoint, effectively meaning that all previous biases are +// invalid. The fast path locking case checks for an invalid epoch in +// the object header and attempts to rebias the object with a CAS if +// found, avoiding safepoints or bulk heap sweeps (the latter which +// was used in a prior version of this algorithm and did not scale +// well). If too many bias revocations persist, biasing is completely +// disabled for the data type by resetting the prototype header to the +// unbiased markOop. The fast-path locking code checks to see whether +// the instance's bias pattern differs from the prototype header's and +// causes the bias to be revoked without reaching a safepoint or, +// again, a bulk heap sweep. + +// Biased locking counters +class BiasedLockingCounters VALUE_OBJ_CLASS_SPEC { + private: + int _total_entry_count; + int _biased_lock_entry_count; + int _anonymously_biased_lock_entry_count; + int _rebiased_lock_entry_count; + int _revoked_lock_entry_count; + int _fast_path_entry_count; + int _slow_path_entry_count; + + public: + BiasedLockingCounters() : + _total_entry_count(0), + _biased_lock_entry_count(0), + _anonymously_biased_lock_entry_count(0), + _rebiased_lock_entry_count(0), + _revoked_lock_entry_count(0), + _fast_path_entry_count(0), + _slow_path_entry_count(0) {} + + int slow_path_entry_count(); // Compute this field if necessary + + int* total_entry_count_addr() { return &_total_entry_count; } + int* biased_lock_entry_count_addr() { return &_biased_lock_entry_count; } + int* anonymously_biased_lock_entry_count_addr() { return &_anonymously_biased_lock_entry_count; } + int* rebiased_lock_entry_count_addr() { return &_rebiased_lock_entry_count; } + int* revoked_lock_entry_count_addr() { return &_revoked_lock_entry_count; } + int* fast_path_entry_count_addr() { return &_fast_path_entry_count; } + int* slow_path_entry_count_addr() { return &_slow_path_entry_count; } + + bool nonzero() { return _total_entry_count > 0; } + + void print_on(outputStream* st); + void print() { print_on(tty); } +}; + + +class BiasedLocking : AllStatic { +private: + static BiasedLockingCounters _counters; + +public: + static int* total_entry_count_addr(); + static int* biased_lock_entry_count_addr(); + static int* anonymously_biased_lock_entry_count_addr(); + static int* rebiased_lock_entry_count_addr(); + static int* revoked_lock_entry_count_addr(); + static int* fast_path_entry_count_addr(); + static int* slow_path_entry_count_addr(); + + enum Condition { + NOT_BIASED = 1, + BIAS_REVOKED = 2, + BIAS_REVOKED_AND_REBIASED = 3 + }; + + // This initialization routine should only be called once and + // schedules a PeriodicTask to turn on biased locking a few seconds + // into the VM run to avoid startup time regressions + static void init(); + + // This provides a global switch for leaving biased locking disabled + // for the first part of a run and enabling it later + static bool enabled(); + + // This should be called by JavaThreads to revoke the bias of an object + static Condition revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS); + + // These do not allow rebiasing; they are used by deoptimization to + // ensure that monitors on the stack can be migrated + static void revoke(GrowableArray* objs); + static void revoke_at_safepoint(Handle obj); + static void revoke_at_safepoint(GrowableArray* objs); + + static void print_counters() { _counters.print(); } + static BiasedLockingCounters* counters() { return &_counters; } + + // These routines are GC-related and should not be called by end + // users. GCs which do not do preservation of mark words do not need + // to call these routines. + static void preserve_marks(); + static void restore_marks(); +}; diff --git a/hotspot/src/share/vm/runtime/compilationPolicy.cpp b/hotspot/src/share/vm/runtime/compilationPolicy.cpp new file mode 100644 index 00000000000..ac870e0cc75 --- /dev/null +++ b/hotspot/src/share/vm/runtime/compilationPolicy.cpp @@ -0,0 +1,448 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_compilationPolicy.cpp.incl" + +CompilationPolicy* CompilationPolicy::_policy; +elapsedTimer CompilationPolicy::_accumulated_time; +bool CompilationPolicy::_in_vm_startup; + +// Determine compilation policy based on command line argument +void compilationPolicy_init() { + CompilationPolicy::set_in_vm_startup(DelayCompilationDuringStartup); + + switch(CompilationPolicyChoice) { + case 0: + CompilationPolicy::set_policy(new SimpleCompPolicy()); + break; + + case 1: +#ifdef COMPILER2 + CompilationPolicy::set_policy(new StackWalkCompPolicy()); +#else + Unimplemented(); +#endif + break; + + default: + fatal("CompilationPolicyChoice must be in the range: [0-1]"); + } +} + +void CompilationPolicy::completed_vm_startup() { + if (TraceCompilationPolicy) { + tty->print("CompilationPolicy: completed vm startup.\n"); + } + _in_vm_startup = false; +} + +// Returns true if m must be compiled before executing it +// This is intended to force compiles for methods (usually for +// debugging) that would otherwise be interpreted for some reason. +bool CompilationPolicy::mustBeCompiled(methodHandle m) { + if (m->has_compiled_code()) return false; // already compiled + if (!canBeCompiled(m)) return false; + + return !UseInterpreter || // must compile all methods + (UseCompiler && AlwaysCompileLoopMethods && m->has_loops()); // eagerly compile loop methods +} + +// Returns true if m is allowed to be compiled +bool CompilationPolicy::canBeCompiled(methodHandle m) { + if (m->is_abstract()) return false; + if (DontCompileHugeMethods && m->code_size() > HugeMethodLimit) return false; + + return !m->is_not_compilable(); +} + +#ifndef PRODUCT +void CompilationPolicy::print_time() { + tty->print_cr ("Accumulated compilationPolicy times:"); + tty->print_cr ("---------------------------"); + tty->print_cr (" Total: %3.3f sec.", _accumulated_time.seconds()); +} + +static void trace_osr_completion(nmethod* osr_nm) { + if (TraceOnStackReplacement) { + if (osr_nm == NULL) tty->print_cr("compilation failed"); + else tty->print_cr("nmethod " INTPTR_FORMAT, osr_nm); + } +} +#endif // !PRODUCT + +void CompilationPolicy::reset_counter_for_invocation_event(methodHandle m) { + // Make sure invocation and backedge counter doesn't overflow again right away + // as would be the case for native methods. + + // BUT also make sure the method doesn't look like it was never executed. + // Set carry bit and reduce counter's value to min(count, CompileThreshold/2). + m->invocation_counter()->set_carry(); + m->backedge_counter()->set_carry(); + + assert(!m->was_never_executed(), "don't reset to 0 -- could be mistaken for never-executed"); +} + +void CompilationPolicy::reset_counter_for_back_branch_event(methodHandle m) { + // Delay next back-branch event but pump up invocation counter to triger + // whole method compilation. + InvocationCounter* i = m->invocation_counter(); + InvocationCounter* b = m->backedge_counter(); + + // Don't set invocation_counter's value too low otherwise the method will + // look like immature (ic < ~5300) which prevents the inlining based on + // the type profiling. + i->set(i->state(), CompileThreshold); + // Don't reset counter too low - it is used to check if OSR method is ready. + b->set(b->state(), CompileThreshold / 2); +} + +// SimpleCompPolicy - compile current method + +void SimpleCompPolicy::method_invocation_event( methodHandle m, TRAPS) { + assert(UseCompiler || CompileTheWorld, "UseCompiler should be set by now."); + + int hot_count = m->invocation_count(); + reset_counter_for_invocation_event(m); + const char* comment = "count"; + + if (!delayCompilationDuringStartup() && canBeCompiled(m) && UseCompiler) { + nmethod* nm = m->code(); + if (nm == NULL ) { + const char* comment = "count"; + CompileBroker::compile_method(m, InvocationEntryBci, + m, hot_count, comment, CHECK); + } else { +#ifdef TIERED + + if (nm->is_compiled_by_c1()) { + const char* comment = "tier1 overflow"; + CompileBroker::compile_method(m, InvocationEntryBci, + m, hot_count, comment, CHECK); + } +#endif // TIERED + } + } +} + +void SimpleCompPolicy::method_back_branch_event(methodHandle m, int branch_bci, int loop_top_bci, TRAPS) { + assert(UseCompiler || CompileTheWorld, "UseCompiler should be set by now."); + + int hot_count = m->backedge_count(); + const char* comment = "backedge_count"; + + if (!m->is_not_osr_compilable() && !delayCompilationDuringStartup() && canBeCompiled(m)) { + CompileBroker::compile_method(m, loop_top_bci, m, hot_count, comment, CHECK); + + NOT_PRODUCT(trace_osr_completion(m->lookup_osr_nmethod_for(loop_top_bci));) + } +} + +int SimpleCompPolicy::compilation_level(methodHandle m, int branch_bci) +{ +#ifdef TIERED + if (!TieredCompilation) { + return CompLevel_highest_tier; + } + if (/* m()->tier1_compile_done() && */ + // QQQ HACK FIX ME set tier1_compile_done!! + !m()->is_native()) { + // Grab the nmethod so it doesn't go away while it's being queried + nmethod* code = m()->code(); + if (code != NULL && code->is_compiled_by_c1()) { + return CompLevel_highest_tier; + } + } + return CompLevel_fast_compile; +#else + return CompLevel_highest_tier; +#endif // TIERED +} + +// StackWalkCompPolicy - walk up stack to find a suitable method to compile + +#ifdef COMPILER2 +const char* StackWalkCompPolicy::_msg = NULL; + + +// Consider m for compilation +void StackWalkCompPolicy::method_invocation_event(methodHandle m, TRAPS) { + assert(UseCompiler || CompileTheWorld, "UseCompiler should be set by now."); + + int hot_count = m->invocation_count(); + reset_counter_for_invocation_event(m); + const char* comment = "count"; + + if (m->code() == NULL && !delayCompilationDuringStartup() && canBeCompiled(m) && UseCompiler) { + ResourceMark rm(THREAD); + JavaThread *thread = (JavaThread*)THREAD; + frame fr = thread->last_frame(); + assert(fr.is_interpreted_frame(), "must be interpreted"); + assert(fr.interpreter_frame_method() == m(), "bad method"); + + if (TraceCompilationPolicy) { + tty->print("method invocation trigger: "); + m->print_short_name(tty); + tty->print(" ( interpreted " INTPTR_FORMAT ", size=%d ) ", (address)m(), m->code_size()); + } + RegisterMap reg_map(thread, false); + javaVFrame* triggerVF = thread->last_java_vframe(®_map); + // triggerVF is the frame that triggered its counter + RFrame* first = new InterpretedRFrame(triggerVF->fr(), thread, m); + + if (first->top_method()->code() != NULL) { + // called obsolete method/nmethod -- no need to recompile + if (TraceCompilationPolicy) tty->print_cr(" --> " INTPTR_FORMAT, first->top_method()->code()); + } else if (compilation_level(m, InvocationEntryBci) == CompLevel_fast_compile) { + // Tier1 compilation policy avaoids stack walking. + CompileBroker::compile_method(m, InvocationEntryBci, + m, hot_count, comment, CHECK); + } else { + if (TimeCompilationPolicy) accumulated_time()->start(); + GrowableArray* stack = new GrowableArray(50); + stack->push(first); + RFrame* top = findTopInlinableFrame(stack); + if (TimeCompilationPolicy) accumulated_time()->stop(); + assert(top != NULL, "findTopInlinableFrame returned null"); + if (TraceCompilationPolicy) top->print(); + CompileBroker::compile_method(top->top_method(), InvocationEntryBci, + m, hot_count, comment, CHECK); + } + } +} + +void StackWalkCompPolicy::method_back_branch_event(methodHandle m, int branch_bci, int loop_top_bci, TRAPS) { + assert(UseCompiler || CompileTheWorld, "UseCompiler should be set by now."); + + int hot_count = m->backedge_count(); + const char* comment = "backedge_count"; + + if (!m->is_not_osr_compilable() && !delayCompilationDuringStartup() && canBeCompiled(m)) { + CompileBroker::compile_method(m, loop_top_bci, m, hot_count, comment, CHECK); + + NOT_PRODUCT(trace_osr_completion(m->lookup_osr_nmethod_for(loop_top_bci));) + } +} + +int StackWalkCompPolicy::compilation_level(methodHandle m, int osr_bci) +{ + int comp_level = CompLevel_full_optimization; + if (TieredCompilation && osr_bci == InvocationEntryBci) { + if (CompileTheWorld) { + // Under CTW, the first compile is tier1, the second tier2 + if (m->highest_tier_compile() == CompLevel_none) { + comp_level = CompLevel_fast_compile; + } + } else if (!m->has_osr_nmethod()) { + // Before tier1 is done, use invocation_count + backedge_count to + // compare against the threshold. After that, the counters may/will + // be reset, so rely on the straight interpreter_invocation_count. + if (m->highest_tier_compile() == CompLevel_initial_compile) { + if (m->interpreter_invocation_count() < Tier2CompileThreshold) { + comp_level = CompLevel_fast_compile; + } + } else if (m->invocation_count() + m->backedge_count() < + Tier2CompileThreshold) { + comp_level = CompLevel_fast_compile; + } + } + + } + return comp_level; +} + + +RFrame* StackWalkCompPolicy::findTopInlinableFrame(GrowableArray* stack) { + // go up the stack until finding a frame that (probably) won't be inlined + // into its caller + RFrame* current = stack->at(0); // current choice for stopping + assert( current && !current->is_compiled(), "" ); + const char* msg = NULL; + + while (1) { + + // before going up the stack further, check if doing so would get us into + // compiled code + RFrame* next = senderOf(current, stack); + if( !next ) // No next frame up the stack? + break; // Then compile with current frame + + methodHandle m = current->top_method(); + methodHandle next_m = next->top_method(); + + if (TraceCompilationPolicy && Verbose) { + tty->print("[caller: "); + next_m->print_short_name(tty); + tty->print("] "); + } + + if( !Inline ) { // Inlining turned off + msg = "Inlining turned off"; + break; + } + if (next_m->is_not_compilable()) { // Did fail to compile this before/ + msg = "caller not compilable"; + break; + } + if (next->num() > MaxRecompilationSearchLength) { + // don't go up too high when searching for recompilees + msg = "don't go up any further: > MaxRecompilationSearchLength"; + break; + } + if (next->distance() > MaxInterpretedSearchLength) { + // don't go up too high when searching for recompilees + msg = "don't go up any further: next > MaxInterpretedSearchLength"; + break; + } + // Compiled frame above already decided not to inline; + // do not recompile him. + if (next->is_compiled()) { + msg = "not going up into optimized code"; + break; + } + + // Interpreted frame above us was already compiled. Do not force + // a recompile, although if the frame above us runs long enough an + // OSR might still happen. + if( current->is_interpreted() && next_m->has_compiled_code() ) { + msg = "not going up -- already compiled caller"; + break; + } + + // Compute how frequent this call site is. We have current method 'm'. + // We know next method 'next_m' is interpreted. Find the call site and + // check the various invocation counts. + int invcnt = 0; // Caller counts + if (ProfileInterpreter) { + invcnt = next_m->interpreter_invocation_count(); + } + int cnt = 0; // Call site counts + if (ProfileInterpreter && next_m->method_data() != NULL) { + ResourceMark rm; + int bci = next->top_vframe()->bci(); + ProfileData* data = next_m->method_data()->bci_to_data(bci); + if (data != NULL && data->is_CounterData()) + cnt = data->as_CounterData()->count(); + } + + // Caller counts / call-site counts; i.e. is this call site + // a hot call site for method next_m? + int freq = (invcnt) ? cnt/invcnt : cnt; + + // Check size and frequency limits + if ((msg = shouldInline(m, freq, cnt)) != NULL) { + break; + } + // Check inlining negative tests + if ((msg = shouldNotInline(m)) != NULL) { + break; + } + + + // If the caller method is too big or something then we do not want to + // compile it just to inline a method + if (!canBeCompiled(next_m)) { + msg = "caller cannot be compiled"; + break; + } + + if( next_m->name() == vmSymbols::class_initializer_name() ) { + msg = "do not compile class initializer (OSR ok)"; + break; + } + + if (TraceCompilationPolicy && Verbose) { + tty->print("\n\t check caller: "); + next_m->print_short_name(tty); + tty->print(" ( interpreted " INTPTR_FORMAT ", size=%d ) ", (address)next_m(), next_m->code_size()); + } + + current = next; + } + + assert( !current || !current->is_compiled(), "" ); + + if (TraceCompilationPolicy && msg) tty->print("(%s)\n", msg); + + return current; +} + +RFrame* StackWalkCompPolicy::senderOf(RFrame* rf, GrowableArray* stack) { + RFrame* sender = rf->caller(); + if (sender && sender->num() == stack->length()) stack->push(sender); + return sender; +} + + +const char* StackWalkCompPolicy::shouldInline(methodHandle m, float freq, int cnt) { + // Allows targeted inlining + // positive filter: should send be inlined? returns NULL (--> yes) + // or rejection msg + int max_size = MaxInlineSize; + int cost = m->code_size(); + + // Check for too many throws (and not too huge) + if (m->interpreter_throwout_count() > InlineThrowCount && cost < InlineThrowMaxSize ) { + return NULL; + } + + // bump the max size if the call is frequent + if ((freq >= InlineFrequencyRatio) || (cnt >= InlineFrequencyCount)) { + if (TraceFrequencyInlining) { + tty->print("(Inlined frequent method)\n"); + m->print(); + } + max_size = FreqInlineSize; + } + if (cost > max_size) { + return (_msg = "too big"); + } + return NULL; +} + + +const char* StackWalkCompPolicy::shouldNotInline(methodHandle m) { + // negative filter: should send NOT be inlined? returns NULL (--> inline) or rejection msg + if (m->is_abstract()) return (_msg = "abstract method"); + // note: we allow ik->is_abstract() + if (!instanceKlass::cast(m->method_holder())->is_initialized()) return (_msg = "method holder not initialized"); + if (m->is_native()) return (_msg = "native method"); + nmethod* m_code = m->code(); + if( m_code != NULL && m_code->instructions_size() > InlineSmallCode ) + return (_msg = "already compiled into a big method"); + + // use frequency-based objections only for non-trivial methods + if (m->code_size() <= MaxTrivialSize) return NULL; + if (UseInterpreter) { // don't use counts with -Xcomp + if ((m->code() == NULL) && m->was_never_executed()) return (_msg = "never executed"); + if (!m->was_executed_more_than(MIN2(MinInliningThreshold, CompileThreshold >> 1))) return (_msg = "executed < MinInliningThreshold times"); + } + if (methodOopDesc::has_unloaded_classes_in_signature(m, JavaThread::current())) return (_msg = "unloaded signature classes"); + + return NULL; +} + + + +#endif // COMPILER2 diff --git a/hotspot/src/share/vm/runtime/compilationPolicy.hpp b/hotspot/src/share/vm/runtime/compilationPolicy.hpp new file mode 100644 index 00000000000..937215128c9 --- /dev/null +++ b/hotspot/src/share/vm/runtime/compilationPolicy.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The CompilationPolicy selects which method (if any) should be compiled. +// It also decides which methods must always be compiled (i.e., are never +// interpreted). + +class CompilationPolicy : public CHeapObj { + private: + static CompilationPolicy* _policy; + // Accumulated time + static elapsedTimer _accumulated_time; + + static bool _in_vm_startup; + + public: + virtual void method_invocation_event(methodHandle m, TRAPS) = 0; + virtual void method_back_branch_event(methodHandle m, int branch_bci, int loop_top_bci, TRAPS) = 0; + virtual int compilation_level(methodHandle m, int branch_bci) = 0; + + void reset_counter_for_invocation_event(methodHandle method); + void reset_counter_for_back_branch_event(methodHandle method); + + static void set_in_vm_startup(bool in_vm_startup) { _in_vm_startup = in_vm_startup; } + static void completed_vm_startup(); + static bool delayCompilationDuringStartup() { return _in_vm_startup; } + + static bool mustBeCompiled(methodHandle m); // m must be compiled before executing it + static bool canBeCompiled(methodHandle m); // m is allowed to be compiled + + static void set_policy(CompilationPolicy* policy) { _policy = policy; } + static CompilationPolicy* policy() { return _policy; } + + // Profiling + elapsedTimer* accumulated_time() { return &_accumulated_time; } + void print_time() PRODUCT_RETURN; +}; + +class SimpleCompPolicy : public CompilationPolicy { + public: + void method_invocation_event( methodHandle m, TRAPS); + void method_back_branch_event(methodHandle m, int branch_bci, int loop_top_bci, TRAPS); + int compilation_level(methodHandle m, int branch_bci); +}; + +// StackWalkCompPolicy - existing C2 policy + +#ifdef COMPILER2 +class StackWalkCompPolicy : public CompilationPolicy { + public: + void method_invocation_event(methodHandle m, TRAPS); + void method_back_branch_event(methodHandle m, int branch_bci, int loop_top_bci, TRAPS); + int compilation_level(methodHandle m, int branch_bci); + + private: + RFrame* findTopInlinableFrame(GrowableArray* stack); + RFrame* senderOf(RFrame* rf, GrowableArray* stack); + + // the following variables hold values computed by the last inlining decision + // they are used for performance debugging only (print better messages) + static const char* _msg; // reason for not inlining + + static const char* shouldInline (methodHandle callee, float frequency, int cnt); + // positive filter: should send be inlined? returns NULL (--> yes) or rejection msg + static const char* shouldNotInline(methodHandle callee); + // negative filter: should send NOT be inlined? returns NULL (--> inline) or rejection msg + +}; +#endif diff --git a/hotspot/src/share/vm/runtime/deoptimization.cpp b/hotspot/src/share/vm/runtime/deoptimization.cpp new file mode 100644 index 00000000000..a4c3a181a1f --- /dev/null +++ b/hotspot/src/share/vm/runtime/deoptimization.cpp @@ -0,0 +1,1789 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_deoptimization.cpp.incl" + +bool DeoptimizationMarker::_is_active = false; + +Deoptimization::UnrollBlock::UnrollBlock(int size_of_deoptimized_frame, + int caller_adjustment, + int number_of_frames, + intptr_t* frame_sizes, + address* frame_pcs, + BasicType return_type) { + _size_of_deoptimized_frame = size_of_deoptimized_frame; + _caller_adjustment = caller_adjustment; + _number_of_frames = number_of_frames; + _frame_sizes = frame_sizes; + _frame_pcs = frame_pcs; + _register_block = NEW_C_HEAP_ARRAY(intptr_t, RegisterMap::reg_count * 2); + _return_type = return_type; + // PD (x86 only) + _counter_temp = 0; + _initial_fp = 0; + _unpack_kind = 0; + _sender_sp_temp = 0; + + _total_frame_sizes = size_of_frames(); +} + + +Deoptimization::UnrollBlock::~UnrollBlock() { + FREE_C_HEAP_ARRAY(intptr_t, _frame_sizes); + FREE_C_HEAP_ARRAY(intptr_t, _frame_pcs); + FREE_C_HEAP_ARRAY(intptr_t, _register_block); +} + + +intptr_t* Deoptimization::UnrollBlock::value_addr_at(int register_number) const { + assert(register_number < RegisterMap::reg_count, "checking register number"); + return &_register_block[register_number * 2]; +} + + + +int Deoptimization::UnrollBlock::size_of_frames() const { + // Acount first for the adjustment of the initial frame + int result = _caller_adjustment; + for (int index = 0; index < number_of_frames(); index++) { + result += frame_sizes()[index]; + } + return result; +} + + +void Deoptimization::UnrollBlock::print() { + ttyLocker ttyl; + tty->print_cr("UnrollBlock"); + tty->print_cr(" size_of_deoptimized_frame = %d", _size_of_deoptimized_frame); + tty->print( " frame_sizes: "); + for (int index = 0; index < number_of_frames(); index++) { + tty->print("%d ", frame_sizes()[index]); + } + tty->cr(); +} + + +// In order to make fetch_unroll_info work properly with escape +// analysis, The method was changed from JRT_LEAF to JRT_BLOCK_ENTRY and +// ResetNoHandleMark and HandleMark were removed from it. The actual reallocation +// of previously eliminated objects occurs in realloc_objects, which is +// called from the method fetch_unroll_info_helper below. +JRT_BLOCK_ENTRY(Deoptimization::UnrollBlock*, Deoptimization::fetch_unroll_info(JavaThread* thread)) + // It is actually ok to allocate handles in a leaf method. It causes no safepoints, + // but makes the entry a little slower. There is however a little dance we have to + // do in debug mode to get around the NoHandleMark code in the JRT_LEAF macro + + // fetch_unroll_info() is called at the beginning of the deoptimization + // handler. Note this fact before we start generating temporary frames + // that can confuse an asynchronous stack walker. This counter is + // decremented at the end of unpack_frames(). + thread->inc_in_deopt_handler(); + + return fetch_unroll_info_helper(thread); +JRT_END + + +// This is factored, since it is both called from a JRT_LEAF (deoptimization) and a JRT_ENTRY (uncommon_trap) +Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread* thread) { + + // Note: there is a safepoint safety issue here. No matter whether we enter + // via vanilla deopt or uncommon trap we MUST NOT stop at a safepoint once + // the vframeArray is created. + // + + // Allocate our special deoptimization ResourceMark + DeoptResourceMark* dmark = new DeoptResourceMark(thread); + assert(thread->deopt_mark() == NULL, "Pending deopt!"); + thread->set_deopt_mark(dmark); + + frame stub_frame = thread->last_frame(); // Makes stack walkable as side effect + RegisterMap map(thread, true); + RegisterMap dummy_map(thread, false); + // Now get the deoptee with a valid map + frame deoptee = stub_frame.sender(&map); + + // Create a growable array of VFrames where each VFrame represents an inlined + // Java frame. This storage is allocated with the usual system arena. + assert(deoptee.is_compiled_frame(), "Wrong frame type"); + GrowableArray* chunk = new GrowableArray(10); + vframe* vf = vframe::new_vframe(&deoptee, &map, thread); + while (!vf->is_top()) { + assert(vf->is_compiled_frame(), "Wrong frame type"); + chunk->push(compiledVFrame::cast(vf)); + vf = vf->sender(); + } + assert(vf->is_compiled_frame(), "Wrong frame type"); + chunk->push(compiledVFrame::cast(vf)); + +#ifdef COMPILER2 + // Reallocate the non-escaping objects and restore their fields. Then + // relock objects if synchronization on them was eliminated. + if (DoEscapeAnalysis && EliminateAllocations) { + GrowableArray* objects = chunk->at(0)->scope()->objects(); + bool reallocated = false; + if (objects != NULL) { + JRT_BLOCK + reallocated = realloc_objects(thread, &deoptee, objects, THREAD); + JRT_END + } + if (reallocated) { + reassign_fields(&deoptee, &map, objects); +#ifndef PRODUCT + if (TraceDeoptimization) { + ttyLocker ttyl; + tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread); + print_objects(objects); + } +#endif + } + for (int i = 0; i < chunk->length(); i++) { + GrowableArray* monitors = chunk->at(i)->scope()->monitors(); + if (monitors != NULL) { + relock_objects(&deoptee, &map, monitors); +#ifndef PRODUCT + if (TraceDeoptimization) { + ttyLocker ttyl; + tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, thread); + for (int j = 0; i < monitors->length(); i++) { + MonitorValue* mv = monitors->at(i); + if (mv->eliminated()) { + StackValue* owner = StackValue::create_stack_value(&deoptee, &map, mv->owner()); + tty->print_cr(" object <" INTPTR_FORMAT "> locked", owner->get_obj()()); + } + } + } +#endif + } + } + } +#endif // COMPILER2 + // Ensure that no safepoint is taken after pointers have been stored + // in fields of rematerialized objects. If a safepoint occurs from here on + // out the java state residing in the vframeArray will be missed. + No_Safepoint_Verifier no_safepoint; + + vframeArray* array = create_vframeArray(thread, deoptee, &map, chunk); + + assert(thread->vframe_array_head() == NULL, "Pending deopt!");; + thread->set_vframe_array_head(array); + + // Now that the vframeArray has been created if we have any deferred local writes + // added by jvmti then we can free up that structure as the data is now in the + // vframeArray + + if (thread->deferred_locals() != NULL) { + GrowableArray* list = thread->deferred_locals(); + int i = 0; + do { + // Because of inlining we could have multiple vframes for a single frame + // and several of the vframes could have deferred writes. Find them all. + if (list->at(i)->id() == array->original().id()) { + jvmtiDeferredLocalVariableSet* dlv = list->at(i); + list->remove_at(i); + // individual jvmtiDeferredLocalVariableSet are CHeapObj's + delete dlv; + } else { + i++; + } + } while ( i < list->length() ); + if (list->length() == 0) { + thread->set_deferred_locals(NULL); + // free the list and elements back to C heap. + delete list; + } + + } + + // Compute the caller frame based on the sender sp of stub_frame and stored frame sizes info. + CodeBlob* cb = stub_frame.cb(); + // Verify we have the right vframeArray + assert(cb->frame_size() >= 0, "Unexpected frame size"); + intptr_t* unpack_sp = stub_frame.sp() + cb->frame_size(); + +#ifdef ASSERT + assert(cb->is_deoptimization_stub() || cb->is_uncommon_trap_stub(), "just checking"); + Events::log("fetch unroll sp " INTPTR_FORMAT, unpack_sp); +#endif + // This is a guarantee instead of an assert because if vframe doesn't match + // we will unpack the wrong deoptimized frame and wind up in strange places + // where it will be very difficult to figure out what went wrong. Better + // to die an early death here than some very obscure death later when the + // trail is cold. + // Note: on ia64 this guarantee can be fooled by frames with no memory stack + // in that it will fail to detect a problem when there is one. This needs + // more work in tiger timeframe. + guarantee(array->unextended_sp() == unpack_sp, "vframe_array_head must contain the vframeArray to unpack"); + + int number_of_frames = array->frames(); + + // Compute the vframes' sizes. Note that frame_sizes[] entries are ordered from outermost to innermost + // virtual activation, which is the reverse of the elements in the vframes array. + intptr_t* frame_sizes = NEW_C_HEAP_ARRAY(intptr_t, number_of_frames); + // +1 because we always have an interpreter return address for the final slot. + address* frame_pcs = NEW_C_HEAP_ARRAY(address, number_of_frames + 1); + int callee_parameters = 0; + int callee_locals = 0; + int popframe_extra_args = 0; + // Create an interpreter return address for the stub to use as its return + // address so the skeletal frames are perfectly walkable + frame_pcs[number_of_frames] = Interpreter::deopt_entry(vtos, 0); + + // PopFrame requires that the preserved incoming arguments from the recently-popped topmost + // activation be put back on the expression stack of the caller for reexecution + if (JvmtiExport::can_pop_frame() && thread->popframe_forcing_deopt_reexecution()) { + popframe_extra_args = in_words(thread->popframe_preserved_args_size_in_words()); + } + + // + // frame_sizes/frame_pcs[0] oldest frame (int or c2i) + // frame_sizes/frame_pcs[1] next oldest frame (int) + // frame_sizes/frame_pcs[n] youngest frame (int) + // + // Now a pc in frame_pcs is actually the return address to the frame's caller (a frame + // owns the space for the return address to it's caller). Confusing ain't it. + // + // The vframe array can address vframes with indices running from + // 0.._frames-1. Index 0 is the youngest frame and _frame - 1 is the oldest (root) frame. + // When we create the skeletal frames we need the oldest frame to be in the zero slot + // in the frame_sizes/frame_pcs so the assembly code can do a trivial walk. + // so things look a little strange in this loop. + // + for (int index = 0; index < array->frames(); index++ ) { + // frame[number_of_frames - 1 ] = on_stack_size(youngest) + // frame[number_of_frames - 2 ] = on_stack_size(sender(youngest)) + // frame[number_of_frames - 3 ] = on_stack_size(sender(sender(youngest))) + frame_sizes[number_of_frames - 1 - index] = BytesPerWord * array->element(index)->on_stack_size(callee_parameters, + callee_locals, + index == 0, + popframe_extra_args); + // This pc doesn't have to be perfect just good enough to identify the frame + // as interpreted so the skeleton frame will be walkable + // The correct pc will be set when the skeleton frame is completely filled out + // The final pc we store in the loop is wrong and will be overwritten below + frame_pcs[number_of_frames - 1 - index ] = Interpreter::deopt_entry(vtos, 0) - frame::pc_return_offset; + + callee_parameters = array->element(index)->method()->size_of_parameters(); + callee_locals = array->element(index)->method()->max_locals(); + popframe_extra_args = 0; + } + + // Compute whether the root vframe returns a float or double value. + BasicType return_type; + { + HandleMark hm; + methodHandle method(thread, array->element(0)->method()); + Bytecode_invoke* invoke = Bytecode_invoke_at_check(method, array->element(0)->bci()); + return_type = (invoke != NULL) ? invoke->result_type(thread) : T_ILLEGAL; + } + + // Compute information for handling adapters and adjusting the frame size of the caller. + int caller_adjustment = 0; + + // Find the current pc for sender of the deoptee. Since the sender may have been deoptimized + // itself since the deoptee vframeArray was created we must get a fresh value of the pc rather + // than simply use array->sender.pc(). This requires us to walk the current set of frames + // + frame deopt_sender = stub_frame.sender(&dummy_map); // First is the deoptee frame + deopt_sender = deopt_sender.sender(&dummy_map); // Now deoptee caller + + // Compute the amount the oldest interpreter frame will have to adjust + // its caller's stack by. If the caller is a compiled frame then + // we pretend that the callee has no parameters so that the + // extension counts for the full amount of locals and not just + // locals-parms. This is because without a c2i adapter the parm + // area as created by the compiled frame will not be usable by + // the interpreter. (Depending on the calling convention there + // may not even be enough space). + + // QQQ I'd rather see this pushed down into last_frame_adjust + // and have it take the sender (aka caller). + + if (deopt_sender.is_compiled_frame()) { + caller_adjustment = last_frame_adjust(0, callee_locals); + } else if (callee_locals > callee_parameters) { + // The caller frame may need extending to accommodate + // non-parameter locals of the first unpacked interpreted frame. + // Compute that adjustment. + caller_adjustment = last_frame_adjust(callee_parameters, callee_locals); + } + + + // If the sender is deoptimized the we must retrieve the address of the handler + // since the frame will "magically" show the original pc before the deopt + // and we'd undo the deopt. + + frame_pcs[0] = deopt_sender.raw_pc(); + + assert(CodeCache::find_blob_unsafe(frame_pcs[0]) != NULL, "bad pc"); + + UnrollBlock* info = new UnrollBlock(array->frame_size() * BytesPerWord, + caller_adjustment * BytesPerWord, + number_of_frames, + frame_sizes, + frame_pcs, + return_type); +#if defined(IA32) || defined(AMD64) + // We need a way to pass fp to the unpacking code so the skeletal frames + // come out correct. This is only needed for x86 because of c2 using ebp + // as an allocatable register. So this update is useless (and harmless) + // on the other platforms. It would be nice to do this in a different + // way but even the old style deoptimization had a problem with deriving + // this value. NEEDS_CLEANUP + // Note: now that c1 is using c2's deopt blob we must do this on all + // x86 based platforms + intptr_t** fp_addr = (intptr_t**) (((address)info) + info->initial_fp_offset_in_bytes()); + *fp_addr = array->sender().fp(); // was adapter_caller +#endif /* IA32 || AMD64 */ + + if (array->frames() > 1) { + if (VerifyStack && TraceDeoptimization) { + tty->print_cr("Deoptimizing method containing inlining"); + } + } + + array->set_unroll_block(info); + return info; +} + +// Called to cleanup deoptimization data structures in normal case +// after unpacking to stack and when stack overflow error occurs +void Deoptimization::cleanup_deopt_info(JavaThread *thread, + vframeArray *array) { + + // Get array if coming from exception + if (array == NULL) { + array = thread->vframe_array_head(); + } + thread->set_vframe_array_head(NULL); + + // Free the previous UnrollBlock + vframeArray* old_array = thread->vframe_array_last(); + thread->set_vframe_array_last(array); + + if (old_array != NULL) { + UnrollBlock* old_info = old_array->unroll_block(); + old_array->set_unroll_block(NULL); + delete old_info; + delete old_array; + } + + // Deallocate any resource creating in this routine and any ResourceObjs allocated + // inside the vframeArray (StackValueCollections) + + delete thread->deopt_mark(); + thread->set_deopt_mark(NULL); + + + if (JvmtiExport::can_pop_frame()) { +#ifndef CC_INTERP + // Regardless of whether we entered this routine with the pending + // popframe condition bit set, we should always clear it now + thread->clear_popframe_condition(); +#else + // C++ interpeter will clear has_pending_popframe when it enters + // with method_resume. For deopt_resume2 we clear it now. + if (thread->popframe_forcing_deopt_reexecution()) + thread->clear_popframe_condition(); +#endif /* CC_INTERP */ + } + + // unpack_frames() is called at the end of the deoptimization handler + // and (in C2) at the end of the uncommon trap handler. Note this fact + // so that an asynchronous stack walker can work again. This counter is + // incremented at the beginning of fetch_unroll_info() and (in C2) at + // the beginning of uncommon_trap(). + thread->dec_in_deopt_handler(); +} + + +// Return BasicType of value being returned +JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_mode)) + + // We are already active int he special DeoptResourceMark any ResourceObj's we + // allocate will be freed at the end of the routine. + + // It is actually ok to allocate handles in a leaf method. It causes no safepoints, + // but makes the entry a little slower. There is however a little dance we have to + // do in debug mode to get around the NoHandleMark code in the JRT_LEAF macro + ResetNoHandleMark rnhm; // No-op in release/product versions + HandleMark hm; + + frame stub_frame = thread->last_frame(); + + // Since the frame to unpack is the top frame of this thread, the vframe_array_head + // must point to the vframeArray for the unpack frame. + vframeArray* array = thread->vframe_array_head(); + +#ifndef PRODUCT + if (TraceDeoptimization) { + tty->print_cr("DEOPT UNPACKING thread " INTPTR_FORMAT " vframeArray " INTPTR_FORMAT " mode %d", thread, array, exec_mode); + } +#endif + + UnrollBlock* info = array->unroll_block(); + + // Unpack the interpreter frames and any adapter frame (c2 only) we might create. + array->unpack_to_stack(stub_frame, exec_mode); + + BasicType bt = info->return_type(); + + // If we have an exception pending, claim that the return type is an oop + // so the deopt_blob does not overwrite the exception_oop. + + if (exec_mode == Unpack_exception) + bt = T_OBJECT; + + // Cleanup thread deopt data + cleanup_deopt_info(thread, array); + +#ifndef PRODUCT + if (VerifyStack) { + ResourceMark res_mark; + + // Verify that the just-unpacked frames match the interpreter's + // notions of expression stack and locals + vframeArray* cur_array = thread->vframe_array_last(); + RegisterMap rm(thread, false); + rm.set_include_argument_oops(false); + bool is_top_frame = true; + int callee_size_of_parameters = 0; + int callee_max_locals = 0; + for (int i = 0; i < cur_array->frames(); i++) { + vframeArrayElement* el = cur_array->element(i); + frame* iframe = el->iframe(); + guarantee(iframe->is_interpreted_frame(), "Wrong frame type"); + + // Get the oop map for this bci + InterpreterOopMap mask; + int cur_invoke_parameter_size = 0; + bool try_next_mask = false; + int next_mask_expression_stack_size = -1; + int top_frame_expression_stack_adjustment = 0; + methodHandle mh(thread, iframe->interpreter_frame_method()); + OopMapCache::compute_one_oop_map(mh, iframe->interpreter_frame_bci(), &mask); + BytecodeStream str(mh); + str.set_start(iframe->interpreter_frame_bci()); + int max_bci = mh->code_size(); + // Get to the next bytecode if possible + assert(str.bci() < max_bci, "bci in interpreter frame out of bounds"); + // Check to see if we can grab the number of outgoing arguments + // at an uncommon trap for an invoke (where the compiler + // generates debug info before the invoke has executed) + Bytecodes::Code cur_code = str.next(); + if (cur_code == Bytecodes::_invokevirtual || + cur_code == Bytecodes::_invokespecial || + cur_code == Bytecodes::_invokestatic || + cur_code == Bytecodes::_invokeinterface) { + Bytecode_invoke* invoke = Bytecode_invoke_at(mh, iframe->interpreter_frame_bci()); + symbolHandle signature(thread, invoke->signature()); + ArgumentSizeComputer asc(signature); + cur_invoke_parameter_size = asc.size(); + if (cur_code != Bytecodes::_invokestatic) { + // Add in receiver + ++cur_invoke_parameter_size; + } + } + if (str.bci() < max_bci) { + Bytecodes::Code bc = str.next(); + if (bc >= 0) { + // The interpreter oop map generator reports results before + // the current bytecode has executed except in the case of + // calls. It seems to be hard to tell whether the compiler + // has emitted debug information matching the "state before" + // a given bytecode or the state after, so we try both + switch (cur_code) { + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokeinterface: + case Bytecodes::_athrow: + break; + default: { + InterpreterOopMap next_mask; + OopMapCache::compute_one_oop_map(mh, str.bci(), &next_mask); + next_mask_expression_stack_size = next_mask.expression_stack_size(); + // Need to subtract off the size of the result type of + // the bytecode because this is not described in the + // debug info but returned to the interpreter in the TOS + // caching register + BasicType bytecode_result_type = Bytecodes::result_type(cur_code); + if (bytecode_result_type != T_ILLEGAL) { + top_frame_expression_stack_adjustment = type2size[bytecode_result_type]; + } + assert(top_frame_expression_stack_adjustment >= 0, ""); + try_next_mask = true; + break; + } + } + } + } + + // Verify stack depth and oops in frame + // This assertion may be dependent on the platform we're running on and may need modification (tested on x86 and sparc) + if (!( + /* SPARC */ + (iframe->interpreter_frame_expression_stack_size() == mask.expression_stack_size() + callee_size_of_parameters) || + /* x86 */ + (iframe->interpreter_frame_expression_stack_size() == mask.expression_stack_size() + callee_max_locals) || + (try_next_mask && + (iframe->interpreter_frame_expression_stack_size() == (next_mask_expression_stack_size - + top_frame_expression_stack_adjustment))) || + (is_top_frame && (exec_mode == Unpack_exception) && iframe->interpreter_frame_expression_stack_size() == 0) || + (is_top_frame && (exec_mode == Unpack_uncommon_trap || exec_mode == Unpack_reexecute) && + (iframe->interpreter_frame_expression_stack_size() == mask.expression_stack_size() + cur_invoke_parameter_size)) + )) { + ttyLocker ttyl; + + // Print out some information that will help us debug the problem + tty->print_cr("Wrong number of expression stack elements during deoptimization"); + tty->print_cr(" Error occurred while verifying frame %d (0..%d, 0 is topmost)", i, cur_array->frames() - 1); + tty->print_cr(" Fabricated interpreter frame had %d expression stack elements", + iframe->interpreter_frame_expression_stack_size()); + tty->print_cr(" Interpreter oop map had %d expression stack elements", mask.expression_stack_size()); + tty->print_cr(" try_next_mask = %d", try_next_mask); + tty->print_cr(" next_mask_expression_stack_size = %d", next_mask_expression_stack_size); + tty->print_cr(" callee_size_of_parameters = %d", callee_size_of_parameters); + tty->print_cr(" callee_max_locals = %d", callee_max_locals); + tty->print_cr(" top_frame_expression_stack_adjustment = %d", top_frame_expression_stack_adjustment); + tty->print_cr(" exec_mode = %d", exec_mode); + tty->print_cr(" cur_invoke_parameter_size = %d", cur_invoke_parameter_size); + tty->print_cr(" Thread = " INTPTR_FORMAT ", thread ID = " UINTX_FORMAT, thread, thread->osthread()->thread_id()); + tty->print_cr(" Interpreted frames:"); + for (int k = 0; k < cur_array->frames(); k++) { + vframeArrayElement* el = cur_array->element(k); + tty->print_cr(" %s (bci %d)", el->method()->name_and_sig_as_C_string(), el->bci()); + } + cur_array->print_on_2(tty); + guarantee(false, "wrong number of expression stack elements during deopt"); + } + VerifyOopClosure verify; + iframe->oops_interpreted_do(&verify, &rm, false); + callee_size_of_parameters = mh->size_of_parameters(); + callee_max_locals = mh->max_locals(); + is_top_frame = false; + } + } +#endif /* !PRODUCT */ + + + return bt; +JRT_END + + +int Deoptimization::deoptimize_dependents() { + Threads::deoptimized_wrt_marked_nmethods(); + return 0; +} + + +#ifdef COMPILER2 +bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArray* objects, TRAPS) { + Handle pending_exception(thread->pending_exception()); + const char* exception_file = thread->exception_file(); + int exception_line = thread->exception_line(); + thread->clear_pending_exception(); + + for (int i = 0; i < objects->length(); i++) { + assert(objects->at(i)->is_object(), "invalid debug information"); + ObjectValue* sv = (ObjectValue*) objects->at(i); + + KlassHandle k(((ConstantOopReadValue*) sv->klass())->value()()); + oop obj = NULL; + + if (k->oop_is_instance()) { + instanceKlass* ik = instanceKlass::cast(k()); + obj = ik->allocate_instance(CHECK_(false)); + } else if (k->oop_is_typeArray()) { + typeArrayKlass* ak = typeArrayKlass::cast(k()); + assert(sv->field_size() % type2size[ak->element_type()] == 0, "non-integral array length"); + int len = sv->field_size() / type2size[ak->element_type()]; + obj = ak->allocate(len, CHECK_(false)); + } else if (k->oop_is_objArray()) { + objArrayKlass* ak = objArrayKlass::cast(k()); + obj = ak->allocate(sv->field_size(), CHECK_(false)); + } + + assert(obj != NULL, "allocation failed"); + assert(sv->value().is_null(), "redundant reallocation"); + sv->set_value(obj); + } + + if (pending_exception.not_null()) { + thread->set_pending_exception(pending_exception(), exception_file, exception_line); + } + + return true; +} + +// This assumes that the fields are stored in ObjectValue in the same order +// they are yielded by do_nonstatic_fields. +class FieldReassigner: public FieldClosure { + frame* _fr; + RegisterMap* _reg_map; + ObjectValue* _sv; + instanceKlass* _ik; + oop _obj; + + int _i; +public: + FieldReassigner(frame* fr, RegisterMap* reg_map, ObjectValue* sv, oop obj) : + _fr(fr), _reg_map(reg_map), _sv(sv), _obj(obj), _i(0) {} + + int i() const { return _i; } + + + void do_field(fieldDescriptor* fd) { + StackValue* value = + StackValue::create_stack_value(_fr, _reg_map, _sv->field_at(i())); + int offset = fd->offset(); + switch (fd->field_type()) { + case T_OBJECT: case T_ARRAY: + assert(value->type() == T_OBJECT, "Agreement."); + _obj->obj_field_put(offset, value->get_obj()()); + break; + + case T_LONG: case T_DOUBLE: { + assert(value->type() == T_INT, "Agreement."); + StackValue* low = + StackValue::create_stack_value(_fr, _reg_map, _sv->field_at(++_i)); + jlong res = jlong_from((jint)value->get_int(), (jint)low->get_int()); + _obj->long_field_put(offset, res); + break; + } + + case T_INT: case T_FLOAT: // 4 bytes. + assert(value->type() == T_INT, "Agreement."); + _obj->int_field_put(offset, (jint)value->get_int()); + break; + + case T_SHORT: case T_CHAR: // 2 bytes + assert(value->type() == T_INT, "Agreement."); + _obj->short_field_put(offset, (jshort)value->get_int()); + break; + + case T_BOOLEAN: // 1 byte + assert(value->type() == T_INT, "Agreement."); + _obj->bool_field_put(offset, (jboolean)value->get_int()); + break; + + default: + ShouldNotReachHere(); + } + _i++; + } +}; + +// restore elements of an eliminated type array +void Deoptimization::reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type) { + StackValue* low; + jlong lval; + int index = 0; + + for (int i = 0; i < sv->field_size(); i++) { + StackValue* value = StackValue::create_stack_value(fr, reg_map, sv->field_at(i)); + switch(type) { + case T_BOOLEAN: obj->bool_at_put (index, (jboolean) value->get_int()); break; + case T_BYTE: obj->byte_at_put (index, (jbyte) value->get_int()); break; + case T_CHAR: obj->char_at_put (index, (jchar) value->get_int()); break; + case T_SHORT: obj->short_at_put(index, (jshort) value->get_int()); break; + case T_INT: obj->int_at_put (index, (jint) value->get_int()); break; + case T_FLOAT: obj->float_at_put(index, (jfloat) value->get_int()); break; + case T_LONG: + case T_DOUBLE: + low = StackValue::create_stack_value(fr, reg_map, sv->field_at(++i)); + lval = jlong_from((jint)value->get_int(), (jint)low->get_int()); + sv->value()->long_field_put(index, lval); + break; + default: + ShouldNotReachHere(); + } + index++; + } +} + + +// restore fields of an eliminated object array +void Deoptimization::reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj) { + for (int i = 0; i < sv->field_size(); i++) { + StackValue* value = StackValue::create_stack_value(fr, reg_map, sv->field_at(i)); + assert(value->type() == T_OBJECT, "object element expected"); + obj->obj_at_put(i, value->get_obj()()); + } +} + + +// restore fields of all eliminated objects and arrays +void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects) { + for (int i = 0; i < objects->length(); i++) { + ObjectValue* sv = (ObjectValue*) objects->at(i); + KlassHandle k(((ConstantOopReadValue*) sv->klass())->value()()); + Handle obj = sv->value(); + assert(obj.not_null(), "reallocation was missed"); + + if (k->oop_is_instance()) { + instanceKlass* ik = instanceKlass::cast(k()); + FieldReassigner reassign(fr, reg_map, sv, obj()); + ik->do_nonstatic_fields(&reassign); + } else if (k->oop_is_typeArray()) { + typeArrayKlass* ak = typeArrayKlass::cast(k()); + reassign_type_array_elements(fr, reg_map, sv, (typeArrayOop) obj(), ak->element_type()); + } else if (k->oop_is_objArray()) { + reassign_object_array_elements(fr, reg_map, sv, (objArrayOop) obj()); + } + } +} + + +// relock objects for which synchronization was eliminated +void Deoptimization::relock_objects(frame* fr, RegisterMap* reg_map, GrowableArray* monitors) { + for (int i = 0; i < monitors->length(); i++) { + MonitorValue* mv = monitors->at(i); + StackValue* owner = StackValue::create_stack_value(fr, reg_map, mv->owner()); + if (mv->eliminated()) { + Handle obj = owner->get_obj(); + assert(obj.not_null(), "reallocation was missed"); + BasicLock* lock = StackValue::resolve_monitor_lock(fr, mv->basic_lock()); + lock->set_displaced_header(obj->mark()); + obj->set_mark((markOop) lock); + } + assert(owner->get_obj()->is_locked(), "object must be locked now"); + } +} + + +#ifndef PRODUCT +// print information about reallocated objects +void Deoptimization::print_objects(GrowableArray* objects) { + fieldDescriptor fd; + + for (int i = 0; i < objects->length(); i++) { + ObjectValue* sv = (ObjectValue*) objects->at(i); + KlassHandle k(((ConstantOopReadValue*) sv->klass())->value()()); + Handle obj = sv->value(); + + tty->print(" object <" INTPTR_FORMAT "> of type ", sv->value()()); + k->as_klassOop()->print_value(); + tty->print(" allocated (%d bytes)", obj->size() * HeapWordSize); + tty->cr(); + + if (Verbose) { + k->oop_print_on(obj(), tty); + } + } +} +#endif +#endif // COMPILER2 + +vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray* chunk) { + +#ifndef PRODUCT + if (TraceDeoptimization) { + ttyLocker ttyl; + tty->print("DEOPT PACKING thread " INTPTR_FORMAT " ", thread); + fr.print_on(tty); + tty->print_cr(" Virtual frames (innermost first):"); + for (int index = 0; index < chunk->length(); index++) { + compiledVFrame* vf = chunk->at(index); + tty->print(" %2d - ", index); + vf->print_value(); + int bci = chunk->at(index)->raw_bci(); + const char* code_name; + if (bci == SynchronizationEntryBCI) { + code_name = "sync entry"; + } else { + Bytecodes::Code code = Bytecodes::code_at(vf->method(), bci); + code_name = Bytecodes::name(code); + } + tty->print(" - %s", code_name); + tty->print_cr(" @ bci %d ", bci); + if (Verbose) { + vf->print(); + tty->cr(); + } + } + } +#endif + + // Register map for next frame (used for stack crawl). We capture + // the state of the deopt'ing frame's caller. Thus if we need to + // stuff a C2I adapter we can properly fill in the callee-save + // register locations. + frame caller = fr.sender(reg_map); + int frame_size = caller.sp() - fr.sp(); + + frame sender = caller; + + // Since the Java thread being deoptimized will eventually adjust it's own stack, + // the vframeArray containing the unpacking information is allocated in the C heap. + // For Compiler1, the caller of the deoptimized frame is saved for use by unpack_frames(). + vframeArray* array = vframeArray::allocate(thread, frame_size, chunk, reg_map, sender, caller, fr); + + // Compare the vframeArray to the collected vframes + assert(array->structural_compare(thread, chunk), "just checking"); + Events::log("# vframes = %d", (intptr_t)chunk->length()); + +#ifndef PRODUCT + if (TraceDeoptimization) { + ttyLocker ttyl; + tty->print_cr(" Created vframeArray " INTPTR_FORMAT, array); + if (Verbose) { + int count = 0; + // this used to leak deoptimizedVFrame like it was going out of style!!! + for (int index = 0; index < array->frames(); index++ ) { + vframeArrayElement* e = array->element(index); + e->print(tty); + + /* + No printing yet. + array->vframe_at(index)->print_activation(count++); + // better as... + array->print_activation_for(index, count++); + */ + } + } + } +#endif // PRODUCT + + return array; +} + + +static void collect_monitors(compiledVFrame* cvf, GrowableArray* objects_to_revoke) { + GrowableArray* monitors = cvf->monitors(); + for (int i = 0; i < monitors->length(); i++) { + MonitorInfo* mon_info = monitors->at(i); + if (mon_info->owner() != NULL) { + objects_to_revoke->append(Handle(mon_info->owner())); + } + } +} + + +void Deoptimization::revoke_biases_of_monitors(JavaThread* thread, frame fr, RegisterMap* map) { + if (!UseBiasedLocking) { + return; + } + + GrowableArray* objects_to_revoke = new GrowableArray(); + + // Unfortunately we don't have a RegisterMap available in most of + // the places we want to call this routine so we need to walk the + // stack again to update the register map. + if (map == NULL || !map->update_map()) { + StackFrameStream sfs(thread, true); + bool found = false; + while (!found && !sfs.is_done()) { + frame* cur = sfs.current(); + sfs.next(); + found = cur->id() == fr.id(); + } + assert(found, "frame to be deoptimized not found on target thread's stack"); + map = sfs.register_map(); + } + + vframe* vf = vframe::new_vframe(&fr, map, thread); + compiledVFrame* cvf = compiledVFrame::cast(vf); + // Revoke monitors' biases in all scopes + while (!cvf->is_top()) { + collect_monitors(cvf, objects_to_revoke); + cvf = compiledVFrame::cast(cvf->sender()); + } + collect_monitors(cvf, objects_to_revoke); + + if (SafepointSynchronize::is_at_safepoint()) { + BiasedLocking::revoke_at_safepoint(objects_to_revoke); + } else { + BiasedLocking::revoke(objects_to_revoke); + } +} + + +void Deoptimization::revoke_biases_of_monitors(CodeBlob* cb) { + if (!UseBiasedLocking) { + return; + } + + assert(SafepointSynchronize::is_at_safepoint(), "must only be called from safepoint"); + GrowableArray* objects_to_revoke = new GrowableArray(); + for (JavaThread* jt = Threads::first(); jt != NULL ; jt = jt->next()) { + if (jt->has_last_Java_frame()) { + StackFrameStream sfs(jt, true); + while (!sfs.is_done()) { + frame* cur = sfs.current(); + if (cb->contains(cur->pc())) { + vframe* vf = vframe::new_vframe(cur, sfs.register_map(), jt); + compiledVFrame* cvf = compiledVFrame::cast(vf); + // Revoke monitors' biases in all scopes + while (!cvf->is_top()) { + collect_monitors(cvf, objects_to_revoke); + cvf = compiledVFrame::cast(cvf->sender()); + } + collect_monitors(cvf, objects_to_revoke); + } + sfs.next(); + } + } + } + BiasedLocking::revoke_at_safepoint(objects_to_revoke); +} + + +void Deoptimization::deoptimize_single_frame(JavaThread* thread, frame fr) { + assert(fr.can_be_deoptimized(), "checking frame type"); + + gather_statistics(Reason_constraint, Action_none, Bytecodes::_illegal); + + EventMark m("Deoptimization (pc=" INTPTR_FORMAT ", sp=" INTPTR_FORMAT ")", fr.pc(), fr.id()); + + // Patch the nmethod so that when execution returns to it we will + // deopt the execution state and return to the interpreter. + fr.deoptimize(thread); +} + +void Deoptimization::deoptimize(JavaThread* thread, frame fr, RegisterMap *map) { + // Deoptimize only if the frame comes from compile code. + // Do not deoptimize the frame which is already patched + // during the execution of the loops below. + if (!fr.is_compiled_frame() || fr.is_deoptimized_frame()) { + return; + } + ResourceMark rm; + DeoptimizationMarker dm; + if (UseBiasedLocking) { + revoke_biases_of_monitors(thread, fr, map); + } + deoptimize_single_frame(thread, fr); + +} + + +void Deoptimization::deoptimize_frame(JavaThread* thread, intptr_t* id) { + // Compute frame and register map based on thread and sp. + RegisterMap reg_map(thread, UseBiasedLocking); + frame fr = thread->last_frame(); + while (fr.id() != id) { + fr = fr.sender(®_map); + } + deoptimize(thread, fr, ®_map); +} + + +// JVMTI PopFrame support +JRT_LEAF(void, Deoptimization::popframe_preserve_args(JavaThread* thread, int bytes_to_save, void* start_address)) +{ + thread->popframe_preserve_args(in_ByteSize(bytes_to_save), start_address); +} +JRT_END + + +#ifdef COMPILER2 +void Deoptimization::load_class_by_index(constantPoolHandle constant_pool, int index, TRAPS) { + // in case of an unresolved klass entry, load the class. + if (constant_pool->tag_at(index).is_unresolved_klass()) { + klassOop tk = constant_pool->klass_at(index, CHECK); + return; + } + + if (!constant_pool->tag_at(index).is_symbol()) return; + + Handle class_loader (THREAD, instanceKlass::cast(constant_pool->pool_holder())->class_loader()); + symbolHandle symbol (THREAD, constant_pool->symbol_at(index)); + + // class name? + if (symbol->byte_at(0) != '(') { + Handle protection_domain (THREAD, Klass::cast(constant_pool->pool_holder())->protection_domain()); + SystemDictionary::resolve_or_null(symbol, class_loader, protection_domain, CHECK); + return; + } + + // then it must be a signature! + for (SignatureStream ss(symbol); !ss.is_done(); ss.next()) { + if (ss.is_object()) { + symbolOop s = ss.as_symbol(CHECK); + symbolHandle class_name (THREAD, s); + Handle protection_domain (THREAD, Klass::cast(constant_pool->pool_holder())->protection_domain()); + SystemDictionary::resolve_or_null(class_name, class_loader, protection_domain, CHECK); + } + } +} + + +void Deoptimization::load_class_by_index(constantPoolHandle constant_pool, int index) { + EXCEPTION_MARK; + load_class_by_index(constant_pool, index, THREAD); + if (HAS_PENDING_EXCEPTION) { + // Exception happened during classloading. We ignore the exception here, since it + // is going to be rethrown since the current activation is going to be deoptimzied and + // the interpreter will re-execute the bytecode. + CLEAR_PENDING_EXCEPTION; + } +} + +JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint trap_request)) { + HandleMark hm; + + // uncommon_trap() is called at the beginning of the uncommon trap + // handler. Note this fact before we start generating temporary frames + // that can confuse an asynchronous stack walker. This counter is + // decremented at the end of unpack_frames(). + thread->inc_in_deopt_handler(); + + // We need to update the map if we have biased locking. + RegisterMap reg_map(thread, UseBiasedLocking); + frame stub_frame = thread->last_frame(); + frame fr = stub_frame.sender(®_map); + // Make sure the calling nmethod is not getting deoptimized and removed + // before we are done with it. + nmethodLocker nl(fr.pc()); + + { + ResourceMark rm; + + // Revoke biases of any monitors in the frame to ensure we can migrate them + revoke_biases_of_monitors(thread, fr, ®_map); + + DeoptReason reason = trap_request_reason(trap_request); + DeoptAction action = trap_request_action(trap_request); + jint unloaded_class_index = trap_request_index(trap_request); // CP idx or -1 + + Events::log("Uncommon trap occurred @" INTPTR_FORMAT " unloaded_class_index = %d", fr.pc(), (int) trap_request); + vframe* vf = vframe::new_vframe(&fr, ®_map, thread); + compiledVFrame* cvf = compiledVFrame::cast(vf); + + nmethod* nm = cvf->code(); + + ScopeDesc* trap_scope = cvf->scope(); + methodHandle trap_method = trap_scope->method(); + int trap_bci = trap_scope->bci(); + Bytecodes::Code trap_bc = Bytecode_at(trap_method->bcp_from(trap_bci))->java_code(); + + // Record this event in the histogram. + gather_statistics(reason, action, trap_bc); + + // Ensure that we can record deopt. history: + bool create_if_missing = ProfileTraps; + + methodDataHandle trap_mdo + (THREAD, get_method_data(thread, trap_method, create_if_missing)); + + // Print a bunch of diagnostics, if requested. + if (TraceDeoptimization || LogCompilation) { + ResourceMark rm; + ttyLocker ttyl; + char buf[100]; + if (xtty != NULL) { + xtty->begin_head("uncommon_trap thread='" UINTX_FORMAT"' %s", + os::current_thread_id(), + format_trap_request(buf, sizeof(buf), trap_request)); + nm->log_identity(xtty); + } + symbolHandle class_name; + bool unresolved = false; + if (unloaded_class_index >= 0) { + constantPoolHandle constants (THREAD, trap_method->constants()); + if (constants->tag_at(unloaded_class_index).is_unresolved_klass()) { + class_name = symbolHandle(THREAD, + constants->klass_name_at(unloaded_class_index)); + unresolved = true; + if (xtty != NULL) + xtty->print(" unresolved='1'"); + } else if (constants->tag_at(unloaded_class_index).is_symbol()) { + class_name = symbolHandle(THREAD, + constants->symbol_at(unloaded_class_index)); + } + if (xtty != NULL) + xtty->name(class_name); + } + if (xtty != NULL && trap_mdo.not_null()) { + // Dump the relevant MDO state. + // This is the deopt count for the current reason, any previous + // reasons or recompiles seen at this point. + int dcnt = trap_mdo->trap_count(reason); + if (dcnt != 0) + xtty->print(" count='%d'", dcnt); + ProfileData* pdata = trap_mdo->bci_to_data(trap_bci); + int dos = (pdata == NULL)? 0: pdata->trap_state(); + if (dos != 0) { + xtty->print(" state='%s'", format_trap_state(buf, sizeof(buf), dos)); + if (trap_state_is_recompiled(dos)) { + int recnt2 = trap_mdo->overflow_recompile_count(); + if (recnt2 != 0) + xtty->print(" recompiles2='%d'", recnt2); + } + } + } + if (xtty != NULL) { + xtty->stamp(); + xtty->end_head(); + } + if (TraceDeoptimization) { // make noise on the tty + tty->print("Uncommon trap occurred in"); + nm->method()->print_short_name(tty); + tty->print(" (@" INTPTR_FORMAT ") thread=%d reason=%s action=%s unloaded_class_index=%d", + fr.pc(), + (int) os::current_thread_id(), + trap_reason_name(reason), + trap_action_name(action), + unloaded_class_index); + if (class_name.not_null()) { + tty->print(unresolved ? " unresolved class: " : " symbol: "); + class_name->print_symbol_on(tty); + } + tty->cr(); + } + if (xtty != NULL) { + // Log the precise location of the trap. + for (ScopeDesc* sd = trap_scope; ; sd = sd->sender()) { + xtty->begin_elem("jvms bci='%d'", sd->bci()); + xtty->method(sd->method()); + xtty->end_elem(); + if (sd->is_top()) break; + } + xtty->tail("uncommon_trap"); + } + } + // (End diagnostic printout.) + + // Load class if necessary + if (unloaded_class_index >= 0) { + constantPoolHandle constants(THREAD, trap_method->constants()); + load_class_by_index(constants, unloaded_class_index); + } + + // Flush the nmethod if necessary and desirable. + // + // We need to avoid situations where we are re-flushing the nmethod + // because of a hot deoptimization site. Repeated flushes at the same + // point need to be detected by the compiler and avoided. If the compiler + // cannot avoid them (or has a bug and "refuses" to avoid them), this + // module must take measures to avoid an infinite cycle of recompilation + // and deoptimization. There are several such measures: + // + // 1. If a recompilation is ordered a second time at some site X + // and for the same reason R, the action is adjusted to 'reinterpret', + // to give the interpreter time to exercise the method more thoroughly. + // If this happens, the method's overflow_recompile_count is incremented. + // + // 2. If the compiler fails to reduce the deoptimization rate, then + // the method's overflow_recompile_count will begin to exceed the set + // limit PerBytecodeRecompilationCutoff. If this happens, the action + // is adjusted to 'make_not_compilable', and the method is abandoned + // to the interpreter. This is a performance hit for hot methods, + // but is better than a disastrous infinite cycle of recompilations. + // (Actually, only the method containing the site X is abandoned.) + // + // 3. In parallel with the previous measures, if the total number of + // recompilations of a method exceeds the much larger set limit + // PerMethodRecompilationCutoff, the method is abandoned. + // This should only happen if the method is very large and has + // many "lukewarm" deoptimizations. The code which enforces this + // limit is elsewhere (class nmethod, class methodOopDesc). + // + // Note that the per-BCI 'is_recompiled' bit gives the compiler one chance + // to recompile at each bytecode independently of the per-BCI cutoff. + // + // The decision to update code is up to the compiler, and is encoded + // in the Action_xxx code. If the compiler requests Action_none + // no trap state is changed, no compiled code is changed, and the + // computation suffers along in the interpreter. + // + // The other action codes specify various tactics for decompilation + // and recompilation. Action_maybe_recompile is the loosest, and + // allows the compiled code to stay around until enough traps are seen, + // and until the compiler gets around to recompiling the trapping method. + // + // The other actions cause immediate removal of the present code. + + bool update_trap_state = true; + bool make_not_entrant = false; + bool make_not_compilable = false; + bool reset_counters = false; + switch (action) { + case Action_none: + // Keep the old code. + update_trap_state = false; + break; + case Action_maybe_recompile: + // Do not need to invalidate the present code, but we can + // initiate another + // Start compiler without (necessarily) invalidating the nmethod. + // The system will tolerate the old code, but new code should be + // generated when possible. + break; + case Action_reinterpret: + // Go back into the interpreter for a while, and then consider + // recompiling form scratch. + make_not_entrant = true; + // Reset invocation counter for outer most method. + // This will allow the interpreter to exercise the bytecodes + // for a while before recompiling. + // By contrast, Action_make_not_entrant is immediate. + // + // Note that the compiler will track null_check, null_assert, + // range_check, and class_check events and log them as if they + // had been traps taken from compiled code. This will update + // the MDO trap history so that the next compilation will + // properly detect hot trap sites. + reset_counters = true; + break; + case Action_make_not_entrant: + // Request immediate recompilation, and get rid of the old code. + // Make them not entrant, so next time they are called they get + // recompiled. Unloaded classes are loaded now so recompile before next + // time they are called. Same for uninitialized. The interpreter will + // link the missing class, if any. + make_not_entrant = true; + break; + case Action_make_not_compilable: + // Give up on compiling this method at all. + make_not_entrant = true; + make_not_compilable = true; + break; + default: + ShouldNotReachHere(); + } + + // Setting +ProfileTraps fixes the following, on all platforms: + // 4852688: ProfileInterpreter is off by default for ia64. The result is + // infinite heroic-opt-uncommon-trap/deopt/recompile cycles, since the + // recompile relies on a methodDataOop to record heroic opt failures. + + // Whether the interpreter is producing MDO data or not, we also need + // to use the MDO to detect hot deoptimization points and control + // aggressive optimization. + if (ProfileTraps && update_trap_state && trap_mdo.not_null()) { + assert(trap_mdo() == get_method_data(thread, trap_method, false), "sanity"); + uint this_trap_count = 0; + bool maybe_prior_trap = false; + bool maybe_prior_recompile = false; + ProfileData* pdata + = query_update_method_data(trap_mdo, trap_bci, reason, + //outputs: + this_trap_count, + maybe_prior_trap, + maybe_prior_recompile); + // Because the interpreter also counts null, div0, range, and class + // checks, these traps from compiled code are double-counted. + // This is harmless; it just means that the PerXTrapLimit values + // are in effect a little smaller than they look. + + DeoptReason per_bc_reason = reason_recorded_per_bytecode_if_any(reason); + if (per_bc_reason != Reason_none) { + // Now take action based on the partially known per-BCI history. + if (maybe_prior_trap + && this_trap_count >= (uint)PerBytecodeTrapLimit) { + // If there are too many traps at this BCI, force a recompile. + // This will allow the compiler to see the limit overflow, and + // take corrective action, if possible. The compiler generally + // does not use the exact PerBytecodeTrapLimit value, but instead + // changes its tactics if it sees any traps at all. This provides + // a little hysteresis, delaying a recompile until a trap happens + // several times. + // + // Actually, since there is only one bit of counter per BCI, + // the possible per-BCI counts are {0,1,(per-method count)}. + // This produces accurate results if in fact there is only + // one hot trap site, but begins to get fuzzy if there are + // many sites. For example, if there are ten sites each + // trapping two or more times, they each get the blame for + // all of their traps. + make_not_entrant = true; + } + + // Detect repeated recompilation at the same BCI, and enforce a limit. + if (make_not_entrant && maybe_prior_recompile) { + // More than one recompile at this point. + trap_mdo->inc_overflow_recompile_count(); + if (maybe_prior_trap + && ((uint)trap_mdo->overflow_recompile_count() + > (uint)PerBytecodeRecompilationCutoff)) { + // Give up on the method containing the bad BCI. + if (trap_method() == nm->method()) { + make_not_compilable = true; + } else { + trap_method->set_not_compilable(); + // But give grace to the enclosing nm->method(). + } + } + } + } else { + // For reasons which are not recorded per-bytecode, we simply + // force recompiles unconditionally. + // (Note that PerMethodRecompilationCutoff is enforced elsewhere.) + make_not_entrant = true; + } + + // Go back to the compiler if there are too many traps in this method. + if (this_trap_count >= (uint)PerMethodTrapLimit) { + // If there are too many traps in this method, force a recompile. + // This will allow the compiler to see the limit overflow, and + // take corrective action, if possible. + // (This condition is an unlikely backstop only, because the + // PerBytecodeTrapLimit is more likely to take effect first, + // if it is applicable.) + make_not_entrant = true; + } + + // Here's more hysteresis: If there has been a recompile at + // this trap point already, run the method in the interpreter + // for a while to exercise it more thoroughly. + if (make_not_entrant && maybe_prior_recompile && maybe_prior_trap) { + reset_counters = true; + } + + if (make_not_entrant && pdata != NULL) { + // Record the recompilation event, if any. + int tstate0 = pdata->trap_state(); + int tstate1 = trap_state_set_recompiled(tstate0, true); + if (tstate1 != tstate0) + pdata->set_trap_state(tstate1); + } + } + + // Take requested actions on the method: + + // Reset invocation counters + if (reset_counters) { + if (nm->is_osr_method()) + reset_invocation_counter(trap_scope, CompileThreshold); + else + reset_invocation_counter(trap_scope); + } + + // Recompile + if (make_not_entrant) { + nm->make_not_entrant(); + } + + // Give up compiling + if (make_not_compilable) { + assert(make_not_entrant, "consistent"); + nm->method()->set_not_compilable(); + } + + } // Free marked resources + +} +JRT_END + +methodDataOop +Deoptimization::get_method_data(JavaThread* thread, methodHandle m, + bool create_if_missing) { + Thread* THREAD = thread; + methodDataOop mdo = m()->method_data(); + if (mdo == NULL && create_if_missing && !HAS_PENDING_EXCEPTION) { + // Build an MDO. Ignore errors like OutOfMemory; + // that simply means we won't have an MDO to update. + methodOopDesc::build_interpreter_method_data(m, THREAD); + if (HAS_PENDING_EXCEPTION) { + assert((PENDING_EXCEPTION->is_a(SystemDictionary::OutOfMemoryError_klass())), "we expect only an OOM error here"); + CLEAR_PENDING_EXCEPTION; + } + mdo = m()->method_data(); + } + return mdo; +} + +ProfileData* +Deoptimization::query_update_method_data(methodDataHandle trap_mdo, + int trap_bci, + Deoptimization::DeoptReason reason, + //outputs: + uint& ret_this_trap_count, + bool& ret_maybe_prior_trap, + bool& ret_maybe_prior_recompile) { + uint prior_trap_count = trap_mdo->trap_count(reason); + uint this_trap_count = trap_mdo->inc_trap_count(reason); + + // If the runtime cannot find a place to store trap history, + // it is estimated based on the general condition of the method. + // If the method has ever been recompiled, or has ever incurred + // a trap with the present reason , then this BCI is assumed + // (pessimistically) to be the culprit. + bool maybe_prior_trap = (prior_trap_count != 0); + bool maybe_prior_recompile = (trap_mdo->decompile_count() != 0); + ProfileData* pdata = NULL; + + + // For reasons which are recorded per bytecode, we check per-BCI data. + DeoptReason per_bc_reason = reason_recorded_per_bytecode_if_any(reason); + if (per_bc_reason != Reason_none) { + // Find the profile data for this BCI. If there isn't one, + // try to allocate one from the MDO's set of spares. + // This will let us detect a repeated trap at this point. + pdata = trap_mdo->allocate_bci_to_data(trap_bci); + + if (pdata != NULL) { + // Query the trap state of this profile datum. + int tstate0 = pdata->trap_state(); + if (!trap_state_has_reason(tstate0, per_bc_reason)) + maybe_prior_trap = false; + if (!trap_state_is_recompiled(tstate0)) + maybe_prior_recompile = false; + + // Update the trap state of this profile datum. + int tstate1 = tstate0; + // Record the reason. + tstate1 = trap_state_add_reason(tstate1, per_bc_reason); + // Store the updated state on the MDO, for next time. + if (tstate1 != tstate0) + pdata->set_trap_state(tstate1); + } else { + if (LogCompilation && xtty != NULL) + // Missing MDP? Leave a small complaint in the log. + xtty->elem("missing_mdp bci='%d'", trap_bci); + } + } + + // Return results: + ret_this_trap_count = this_trap_count; + ret_maybe_prior_trap = maybe_prior_trap; + ret_maybe_prior_recompile = maybe_prior_recompile; + return pdata; +} + +void +Deoptimization::update_method_data_from_interpreter(methodDataHandle trap_mdo, int trap_bci, int reason) { + ResourceMark rm; + // Ignored outputs: + uint ignore_this_trap_count; + bool ignore_maybe_prior_trap; + bool ignore_maybe_prior_recompile; + query_update_method_data(trap_mdo, trap_bci, + (DeoptReason)reason, + ignore_this_trap_count, + ignore_maybe_prior_trap, + ignore_maybe_prior_recompile); +} + +void Deoptimization::reset_invocation_counter(ScopeDesc* trap_scope, jint top_count) { + ScopeDesc* sd = trap_scope; + for (; !sd->is_top(); sd = sd->sender()) { + // Reset ICs of inlined methods, since they can trigger compilations also. + sd->method()->invocation_counter()->reset(); + } + InvocationCounter* c = sd->method()->invocation_counter(); + if (top_count != _no_count) { + // It was an OSR method, so bump the count higher. + c->set(c->state(), top_count); + } else { + c->reset(); + } + sd->method()->backedge_counter()->reset(); +} + +Deoptimization::UnrollBlock* Deoptimization::uncommon_trap(JavaThread* thread, jint trap_request) { + + // Still in Java no safepoints + { + // This enters VM and may safepoint + uncommon_trap_inner(thread, trap_request); + } + return fetch_unroll_info_helper(thread); +} + +// Local derived constants. +// Further breakdown of DataLayout::trap_state, as promised by DataLayout. +const int DS_REASON_MASK = DataLayout::trap_mask >> 1; +const int DS_RECOMPILE_BIT = DataLayout::trap_mask - DS_REASON_MASK; + +//---------------------------trap_state_reason--------------------------------- +Deoptimization::DeoptReason +Deoptimization::trap_state_reason(int trap_state) { + // This assert provides the link between the width of DataLayout::trap_bits + // and the encoding of "recorded" reasons. It ensures there are enough + // bits to store all needed reasons in the per-BCI MDO profile. + assert(DS_REASON_MASK >= Reason_RECORDED_LIMIT, "enough bits"); + int recompile_bit = (trap_state & DS_RECOMPILE_BIT); + trap_state -= recompile_bit; + if (trap_state == DS_REASON_MASK) { + return Reason_many; + } else { + assert((int)Reason_none == 0, "state=0 => Reason_none"); + return (DeoptReason)trap_state; + } +} +//-------------------------trap_state_has_reason------------------------------- +int Deoptimization::trap_state_has_reason(int trap_state, int reason) { + assert(reason_is_recorded_per_bytecode((DeoptReason)reason), "valid reason"); + assert(DS_REASON_MASK >= Reason_RECORDED_LIMIT, "enough bits"); + int recompile_bit = (trap_state & DS_RECOMPILE_BIT); + trap_state -= recompile_bit; + if (trap_state == DS_REASON_MASK) { + return -1; // true, unspecifically (bottom of state lattice) + } else if (trap_state == reason) { + return 1; // true, definitely + } else if (trap_state == 0) { + return 0; // false, definitely (top of state lattice) + } else { + return 0; // false, definitely + } +} +//-------------------------trap_state_add_reason------------------------------- +int Deoptimization::trap_state_add_reason(int trap_state, int reason) { + assert(reason_is_recorded_per_bytecode((DeoptReason)reason) || reason == Reason_many, "valid reason"); + int recompile_bit = (trap_state & DS_RECOMPILE_BIT); + trap_state -= recompile_bit; + if (trap_state == DS_REASON_MASK) { + return trap_state + recompile_bit; // already at state lattice bottom + } else if (trap_state == reason) { + return trap_state + recompile_bit; // the condition is already true + } else if (trap_state == 0) { + return reason + recompile_bit; // no condition has yet been true + } else { + return DS_REASON_MASK + recompile_bit; // fall to state lattice bottom + } +} +//-----------------------trap_state_is_recompiled------------------------------ +bool Deoptimization::trap_state_is_recompiled(int trap_state) { + return (trap_state & DS_RECOMPILE_BIT) != 0; +} +//-----------------------trap_state_set_recompiled----------------------------- +int Deoptimization::trap_state_set_recompiled(int trap_state, bool z) { + if (z) return trap_state | DS_RECOMPILE_BIT; + else return trap_state & ~DS_RECOMPILE_BIT; +} +//---------------------------format_trap_state--------------------------------- +// This is used for debugging and diagnostics, including hotspot.log output. +const char* Deoptimization::format_trap_state(char* buf, size_t buflen, + int trap_state) { + DeoptReason reason = trap_state_reason(trap_state); + bool recomp_flag = trap_state_is_recompiled(trap_state); + // Re-encode the state from its decoded components. + int decoded_state = 0; + if (reason_is_recorded_per_bytecode(reason) || reason == Reason_many) + decoded_state = trap_state_add_reason(decoded_state, reason); + if (recomp_flag) + decoded_state = trap_state_set_recompiled(decoded_state, recomp_flag); + // If the state re-encodes properly, format it symbolically. + // Because this routine is used for debugging and diagnostics, + // be robust even if the state is a strange value. + size_t len; + if (decoded_state != trap_state) { + // Random buggy state that doesn't decode?? + len = jio_snprintf(buf, buflen, "#%d", trap_state); + } else { + len = jio_snprintf(buf, buflen, "%s%s", + trap_reason_name(reason), + recomp_flag ? " recompiled" : ""); + } + if (len >= buflen) + buf[buflen-1] = '\0'; + return buf; +} + + +//--------------------------------statics-------------------------------------- +Deoptimization::DeoptAction Deoptimization::_unloaded_action + = Deoptimization::Action_reinterpret; +const char* Deoptimization::_trap_reason_name[Reason_LIMIT] = { + // Note: Keep this in sync. with enum DeoptReason. + "none", + "null_check", + "null_assert", + "range_check", + "class_check", + "array_check", + "intrinsic", + "unloaded", + "uninitialized", + "unreached", + "unhandled", + "constraint", + "div0_check", + "age" +}; +const char* Deoptimization::_trap_action_name[Action_LIMIT] = { + // Note: Keep this in sync. with enum DeoptAction. + "none", + "maybe_recompile", + "reinterpret", + "make_not_entrant", + "make_not_compilable" +}; + +const char* Deoptimization::trap_reason_name(int reason) { + if (reason == Reason_many) return "many"; + if ((uint)reason < Reason_LIMIT) + return _trap_reason_name[reason]; + static char buf[20]; + sprintf(buf, "reason%d", reason); + return buf; +} +const char* Deoptimization::trap_action_name(int action) { + if ((uint)action < Action_LIMIT) + return _trap_action_name[action]; + static char buf[20]; + sprintf(buf, "action%d", action); + return buf; +} + +// This is used for debugging and diagnostics, including hotspot.log output. +const char* Deoptimization::format_trap_request(char* buf, size_t buflen, + int trap_request) { + jint unloaded_class_index = trap_request_index(trap_request); + const char* reason = trap_reason_name(trap_request_reason(trap_request)); + const char* action = trap_action_name(trap_request_action(trap_request)); + size_t len; + if (unloaded_class_index < 0) { + len = jio_snprintf(buf, buflen, "reason='%s' action='%s'", + reason, action); + } else { + len = jio_snprintf(buf, buflen, "reason='%s' action='%s' index='%d'", + reason, action, unloaded_class_index); + } + if (len >= buflen) + buf[buflen-1] = '\0'; + return buf; +} + +juint Deoptimization::_deoptimization_hist + [Deoptimization::Reason_LIMIT] + [1 + Deoptimization::Action_LIMIT] + [Deoptimization::BC_CASE_LIMIT] + = {0}; + +enum { + LSB_BITS = 8, + LSB_MASK = right_n_bits(LSB_BITS) +}; + +void Deoptimization::gather_statistics(DeoptReason reason, DeoptAction action, + Bytecodes::Code bc) { + assert(reason >= 0 && reason < Reason_LIMIT, "oob"); + assert(action >= 0 && action < Action_LIMIT, "oob"); + _deoptimization_hist[Reason_none][0][0] += 1; // total + _deoptimization_hist[reason][0][0] += 1; // per-reason total + juint* cases = _deoptimization_hist[reason][1+action]; + juint* bc_counter_addr = NULL; + juint bc_counter = 0; + // Look for an unused counter, or an exact match to this BC. + if (bc != Bytecodes::_illegal) { + for (int bc_case = 0; bc_case < BC_CASE_LIMIT; bc_case++) { + juint* counter_addr = &cases[bc_case]; + juint counter = *counter_addr; + if ((counter == 0 && bc_counter_addr == NULL) + || (Bytecodes::Code)(counter & LSB_MASK) == bc) { + // this counter is either free or is already devoted to this BC + bc_counter_addr = counter_addr; + bc_counter = counter | bc; + } + } + } + if (bc_counter_addr == NULL) { + // Overflow, or no given bytecode. + bc_counter_addr = &cases[BC_CASE_LIMIT-1]; + bc_counter = (*bc_counter_addr & ~LSB_MASK); // clear LSB + } + *bc_counter_addr = bc_counter + (1 << LSB_BITS); +} + +jint Deoptimization::total_deoptimization_count() { + return _deoptimization_hist[Reason_none][0][0]; +} + +jint Deoptimization::deoptimization_count(DeoptReason reason) { + assert(reason >= 0 && reason < Reason_LIMIT, "oob"); + return _deoptimization_hist[reason][0][0]; +} + +void Deoptimization::print_statistics() { + juint total = total_deoptimization_count(); + juint account = total; + if (total != 0) { + ttyLocker ttyl; + if (xtty != NULL) xtty->head("statistics type='deoptimization'"); + tty->print_cr("Deoptimization traps recorded:"); + #define PRINT_STAT_LINE(name, r) \ + tty->print_cr(" %4d (%4.1f%%) %s", (int)(r), ((r) * 100.0) / total, name); + PRINT_STAT_LINE("total", total); + // For each non-zero entry in the histogram, print the reason, + // the action, and (if specifically known) the type of bytecode. + for (int reason = 0; reason < Reason_LIMIT; reason++) { + for (int action = 0; action < Action_LIMIT; action++) { + juint* cases = _deoptimization_hist[reason][1+action]; + for (int bc_case = 0; bc_case < BC_CASE_LIMIT; bc_case++) { + juint counter = cases[bc_case]; + if (counter != 0) { + char name[1*K]; + Bytecodes::Code bc = (Bytecodes::Code)(counter & LSB_MASK); + if (bc_case == BC_CASE_LIMIT && (int)bc == 0) + bc = Bytecodes::_illegal; + sprintf(name, "%s/%s/%s", + trap_reason_name(reason), + trap_action_name(action), + Bytecodes::is_defined(bc)? Bytecodes::name(bc): "other"); + juint r = counter >> LSB_BITS; + tty->print_cr(" %40s: " UINT32_FORMAT " (%.1f%%)", name, r, (r * 100.0) / total); + account -= r; + } + } + } + } + if (account != 0) { + PRINT_STAT_LINE("unaccounted", account); + } + #undef PRINT_STAT_LINE + if (xtty != NULL) xtty->tail("statistics"); + } +} +#else // COMPILER2 + + +// Stubs for C1 only system. +bool Deoptimization::trap_state_is_recompiled(int trap_state) { + return false; +} + +const char* Deoptimization::trap_reason_name(int reason) { + return "unknown"; +} + +void Deoptimization::print_statistics() { + // no output +} + +void +Deoptimization::update_method_data_from_interpreter(methodDataHandle trap_mdo, int trap_bci, int reason) { + // no udpate +} + +int Deoptimization::trap_state_has_reason(int trap_state, int reason) { + return 0; +} + +void Deoptimization::gather_statistics(DeoptReason reason, DeoptAction action, + Bytecodes::Code bc) { + // no update +} + +const char* Deoptimization::format_trap_state(char* buf, size_t buflen, + int trap_state) { + jio_snprintf(buf, buflen, "#%d", trap_state); + return buf; +} + +#endif // COMPILER2 diff --git a/hotspot/src/share/vm/runtime/deoptimization.hpp b/hotspot/src/share/vm/runtime/deoptimization.hpp new file mode 100644 index 00000000000..63565704a69 --- /dev/null +++ b/hotspot/src/share/vm/runtime/deoptimization.hpp @@ -0,0 +1,348 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ProfileData; +class vframeArray; +class MonitorValue; +class ObjectValue; + +class Deoptimization : AllStatic { + public: + // What condition caused the deoptimization? + enum DeoptReason { + Reason_many = -1, // indicates presence of several reasons + Reason_none = 0, // indicates absence of a relevant deopt. + Reason_null_check, // saw unexpected null or zero divisor (@bci) + Reason_null_assert, // saw unexpected non-null or non-zero (@bci) + Reason_range_check, // saw unexpected array index (@bci) + Reason_class_check, // saw unexpected object class (@bci) + Reason_array_check, // saw unexpected array class (aastore @bci) + Reason_intrinsic, // saw unexpected operand to intrinsic (@bci) + Reason_unloaded, // unloaded class or constant pool entry + Reason_uninitialized, // bad class state (uninitialized) + Reason_unreached, // code is not reached, compiler + Reason_unhandled, // arbitrary compiler limitation + Reason_constraint, // arbitrary runtime constraint violated + Reason_div0_check, // a null_check due to division by zero + Reason_age, // nmethod too old; tier threshold reached + Reason_LIMIT, + // Note: Keep this enum in sync. with _trap_reason_name. + Reason_RECORDED_LIMIT = Reason_unloaded // some are not recorded per bc + // Note: Reason_RECORDED_LIMIT should be < 8 to fit into 3 bits of + // DataLayout::trap_bits. This dependency is enforced indirectly + // via asserts, to avoid excessive direct header-to-header dependencies. + // See Deoptimization::trap_state_reason and class DataLayout. + }; + + // What action must be taken by the runtime? + enum DeoptAction { + Action_none, // just interpret, do not invalidate nmethod + Action_maybe_recompile, // recompile the nmethod; need not invalidate + Action_reinterpret, // invalidate the nmethod, reset IC, maybe recompile + Action_make_not_entrant, // invalidate the nmethod, recompile (probably) + Action_make_not_compilable, // invalidate the nmethod and do not compile + Action_LIMIT + // Note: Keep this enum in sync. with _trap_action_name. + }; + + enum { + _action_bits = 3, + _reason_bits = 4, + _action_shift = 0, + _reason_shift = _action_shift+_action_bits, + BC_CASE_LIMIT = PRODUCT_ONLY(1) NOT_PRODUCT(4) // for _deoptimization_hist + }; + + enum UnpackType { + Unpack_deopt = 0, // normal deoptimization, use pc computed in unpack_vframe_on_stack + Unpack_exception = 1, // exception is pending + Unpack_uncommon_trap = 2, // redo last byte code (C2 only) + Unpack_reexecute = 3 // reexecute bytecode (C1 only) + }; + + // Checks all compiled methods. Invalid methods are deleted and + // corresponding activations are deoptimized. + static int deoptimize_dependents(); + + // Deoptimizes a frame lazily. nmethod gets patched deopt happens on return to the frame + static void deoptimize(JavaThread* thread, frame fr, RegisterMap *reg_map); + + private: + // Does the actual work for deoptimizing a single frame + static void deoptimize_single_frame(JavaThread* thread, frame fr); + + // Helper function to revoke biases of all monitors in frame if UseBiasedLocking + // is enabled + static void revoke_biases_of_monitors(JavaThread* thread, frame fr, RegisterMap* map); + // Helper function to revoke biases of all monitors in frames + // executing in a particular CodeBlob if UseBiasedLocking is enabled + static void revoke_biases_of_monitors(CodeBlob* cb); + +#ifdef COMPILER2 + // Support for restoring non-escaping objects + static bool realloc_objects(JavaThread* thread, frame* fr, GrowableArray* objects, TRAPS); + static void reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type); + static void reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj); + static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects); + static void relock_objects(frame* fr, RegisterMap* reg_map, GrowableArray* monitors); + NOT_PRODUCT(static void print_objects(GrowableArray* objects);) +#endif // COMPILER2 + + public: + static vframeArray* create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray* chunk); + + // Interface used for unpacking deoptimized frames + + // UnrollBlock is returned by fetch_unroll_info() to the deoptimization handler (blob). + // This is only a CheapObj to ease debugging after a deopt failure + class UnrollBlock : public CHeapObj { + private: + int _size_of_deoptimized_frame; // Size, in bytes, of current deoptimized frame + int _caller_adjustment; // Adjustment, in bytes, to caller's SP by initial interpreted frame + int _number_of_frames; // Number frames to unroll + int _total_frame_sizes; // Total of number*sizes frames + intptr_t* _frame_sizes; // Array of frame sizes, in bytes, for unrolling the stack + address* _frame_pcs; // Array of frame pc's, in bytes, for unrolling the stack + intptr_t* _register_block; // Block for storing callee-saved registers. + BasicType _return_type; // Tells if we have to restore double or long return value + // The following fields are used as temps during the unpacking phase + // (which is tight on registers, especially on x86). They really ought + // to be PD variables but that involves moving this class into its own + // file to use the pd include mechanism. Maybe in a later cleanup ... + intptr_t _counter_temp; // SHOULD BE PD VARIABLE (x86 frame count temp) + intptr_t _initial_fp; // SHOULD BE PD VARIABLE (x86/c2 initial ebp) + intptr_t _unpack_kind; // SHOULD BE PD VARIABLE (x86 unpack kind) + intptr_t _sender_sp_temp; // SHOULD BE PD VARIABLE (x86 sender_sp) + public: + // Constructor + UnrollBlock(int size_of_deoptimized_frame, + int caller_adjustment, + int number_of_frames, + intptr_t* frame_sizes, + address* frames_pcs, + BasicType return_type); + ~UnrollBlock(); + + // Returns where a register is located. + intptr_t* value_addr_at(int register_number) const; + + // Accessors + intptr_t* frame_sizes() const { return _frame_sizes; } + int number_of_frames() const { return _number_of_frames; } + address* frame_pcs() const { return _frame_pcs ; } + + // Returns the total size of frames + int size_of_frames() const; + + // Accessors used by the code generator for the unpack stub. + static int size_of_deoptimized_frame_offset_in_bytes() { return offset_of(UnrollBlock, _size_of_deoptimized_frame); } + static int caller_adjustment_offset_in_bytes() { return offset_of(UnrollBlock, _caller_adjustment); } + static int number_of_frames_offset_in_bytes() { return offset_of(UnrollBlock, _number_of_frames); } + static int frame_sizes_offset_in_bytes() { return offset_of(UnrollBlock, _frame_sizes); } + static int total_frame_sizes_offset_in_bytes() { return offset_of(UnrollBlock, _total_frame_sizes); } + static int frame_pcs_offset_in_bytes() { return offset_of(UnrollBlock, _frame_pcs); } + static int register_block_offset_in_bytes() { return offset_of(UnrollBlock, _register_block); } + static int return_type_offset_in_bytes() { return offset_of(UnrollBlock, _return_type); } + static int counter_temp_offset_in_bytes() { return offset_of(UnrollBlock, _counter_temp); } + static int initial_fp_offset_in_bytes() { return offset_of(UnrollBlock, _initial_fp); } + static int unpack_kind_offset_in_bytes() { return offset_of(UnrollBlock, _unpack_kind); } + static int sender_sp_temp_offset_in_bytes() { return offset_of(UnrollBlock, _sender_sp_temp); } + + BasicType return_type() const { return _return_type; } + void print(); + }; + + //** Returns an UnrollBlock continuing information + // how to make room for the resulting interpreter frames. + // Called by assembly stub after execution has returned to + // deoptimized frame. + // @argument thread. Thread where stub_frame resides. + // @see OptoRuntime::deoptimization_fetch_unroll_info_C + static UnrollBlock* fetch_unroll_info(JavaThread* thread); + + //** Unpacks vframeArray onto execution stack + // Called by assembly stub after execution has returned to + // deoptimized frame and after the stack unrolling. + // @argument thread. Thread where stub_frame resides. + // @argument exec_mode. Determines how execution should be continuted in top frame. + // 0 means continue after current byte code + // 1 means exception has happened, handle exception + // 2 means reexecute current bytecode (for uncommon traps). + // @see OptoRuntime::deoptimization_unpack_frames_C + // Return BasicType of call return type, if any + static BasicType unpack_frames(JavaThread* thread, int exec_mode); + + // Cleans up deoptimization bits on thread after unpacking or in the + // case of an exception. + static void cleanup_deopt_info(JavaThread *thread, + vframeArray * array); + + // Restores callee saved values from deoptimized frame into oldest interpreter frame + // so caller of the deoptimized frame will get back the values it expects. + static void unwind_callee_save_values(frame* f, vframeArray* vframe_array); + + //** Performs an uncommon trap for compiled code. + // The top most compiler frame is converted into interpreter frames + static UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); + // Helper routine that enters the VM and may block + static void uncommon_trap_inner(JavaThread* thread, jint unloaded_class_index); + + //** Deoptimizes the frame identified by id. + // Only called from VMDeoptimizeFrame + // @argument thread. Thread where stub_frame resides. + // @argument id. id of frame that should be deoptimized. + static void deoptimize_frame(JavaThread* thread, intptr_t* id); + + // Statistics + static void gather_statistics(DeoptReason reason, DeoptAction action, + Bytecodes::Code bc = Bytecodes::_illegal); + static void print_statistics(); + + // How much room to adjust the last frame's SP by, to make space for + // the callee's interpreter frame (which expects locals to be next to + // incoming arguments) + static int last_frame_adjust(int callee_parameters, int callee_locals); + + // trap_request codes + static DeoptReason trap_request_reason(int trap_request) { + if (trap_request < 0) + return (DeoptReason) + ((~(trap_request) >> _reason_shift) & right_n_bits(_reason_bits)); + else + // standard reason for unloaded CP entry + return Reason_unloaded; + } + static DeoptAction trap_request_action(int trap_request) { + if (trap_request < 0) + return (DeoptAction) + ((~(trap_request) >> _action_shift) & right_n_bits(_action_bits)); + else + // standard action for unloaded CP entry + return _unloaded_action; + } + static int trap_request_index(int trap_request) { + if (trap_request < 0) + return -1; + else + return trap_request; + } + static int make_trap_request(DeoptReason reason, DeoptAction action, + int index = -1) { + assert((1 << _reason_bits) >= Reason_LIMIT, "enough bits"); + assert((1 << _action_bits) >= Action_LIMIT, "enough bits"); + int trap_request; + if (index != -1) + trap_request = index; + else + trap_request = (~(((reason) << _reason_shift) + + ((action) << _action_shift))); + assert(reason == trap_request_reason(trap_request), "valid reason"); + assert(action == trap_request_action(trap_request), "valid action"); + assert(index == trap_request_index(trap_request), "valid index"); + return trap_request; + } + + // The trap_state stored in a MDO is decoded here. + // It records two items of information. + // reason: If a deoptimization happened here, what its reason was, + // or if there were multiple deopts with differing reasons. + // recompiled: If a deoptimization here triggered a recompilation. + // Note that not all reasons are recorded per-bci. + static DeoptReason trap_state_reason(int trap_state); + static int trap_state_has_reason(int trap_state, int reason); + static int trap_state_add_reason(int trap_state, int reason); + static bool trap_state_is_recompiled(int trap_state); + static int trap_state_set_recompiled(int trap_state, bool z); + static const char* format_trap_state(char* buf, size_t buflen, + int trap_state); + + static bool reason_is_recorded_per_bytecode(DeoptReason reason) { + return reason > Reason_none && reason < Reason_RECORDED_LIMIT; + } + + static DeoptReason reason_recorded_per_bytecode_if_any(DeoptReason reason) { + if (reason_is_recorded_per_bytecode(reason)) + return reason; + else if (reason == Reason_div0_check) // null check due to divide-by-zero? + return Reason_null_check; // recorded per BCI as a null check + else + return Reason_none; + } + + static const char* trap_reason_name(int reason); + static const char* trap_action_name(int action); + // Format like reason='foo' action='bar' index='123'. + // This is suitable both for XML and for tty output. + static const char* format_trap_request(char* buf, size_t buflen, + int trap_request); + + static jint total_deoptimization_count(); + static jint deoptimization_count(DeoptReason reason); + + // JVMTI PopFrame support + + // Preserves incoming arguments to the popped frame when it is + // returning to a deoptimized caller + static void popframe_preserve_args(JavaThread* thread, int bytes_to_save, void* start_address); + + private: + enum { + _no_count = -1 + }; + + static void reset_invocation_counter(ScopeDesc* trap_scope, jint count = _no_count); + + static methodDataOop get_method_data(JavaThread* thread, methodHandle m, bool create_if_missing); + // Update the mdo's count and per-BCI reason bits, returning previous state: + static ProfileData* query_update_method_data(methodDataHandle trap_mdo, + int trap_bci, + DeoptReason reason, + //outputs: + uint& ret_this_trap_count, + bool& ret_maybe_prior_trap, + bool& ret_maybe_prior_recompile); + // class loading support for uncommon trap + static void load_class_by_index(constantPoolHandle constant_pool, int index, TRAPS); + static void load_class_by_index(constantPoolHandle constant_pool, int index); + + static UnrollBlock* fetch_unroll_info_helper(JavaThread* thread); + + static DeoptAction _unloaded_action; // == Action_reinterpret; + static const char* _trap_reason_name[Reason_LIMIT]; + static const char* _trap_action_name[Action_LIMIT]; + + static juint _deoptimization_hist[Reason_LIMIT][1+Action_LIMIT][BC_CASE_LIMIT]; + // Note: Histogram array size is 1-2 Kb. + + public: + static void update_method_data_from_interpreter(methodDataHandle trap_mdo, int trap_bci, int reason); +}; + +class DeoptimizationMarker : StackObj { // for profiling + static bool _is_active; +public: + DeoptimizationMarker() { _is_active = true; } + ~DeoptimizationMarker() { _is_active = false; } + static bool is_active() { return _is_active; } +}; diff --git a/hotspot/src/share/vm/runtime/extendedPC.hpp b/hotspot/src/share/vm/runtime/extendedPC.hpp new file mode 100644 index 00000000000..a2680851adc --- /dev/null +++ b/hotspot/src/share/vm/runtime/extendedPC.hpp @@ -0,0 +1,36 @@ +/* + * Copyright 1998-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// An ExtendedPC contains the _pc from a signal handler in a platform +// independant way. + +class ExtendedPC VALUE_OBJ_CLASS_SPEC { + private: + address _pc; + + public: + address pc() const { return _pc; } + ExtendedPC(address pc) { _pc = pc; } + ExtendedPC() { _pc = NULL; } +}; diff --git a/hotspot/src/share/vm/runtime/fieldDescriptor.cpp b/hotspot/src/share/vm/runtime/fieldDescriptor.cpp new file mode 100644 index 00000000000..d750981a4c3 --- /dev/null +++ b/hotspot/src/share/vm/runtime/fieldDescriptor.cpp @@ -0,0 +1,168 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +#include "incls/_fieldDescriptor.cpp.incl" + + +oop fieldDescriptor::loader() const { + return instanceKlass::cast(_cp->pool_holder())->class_loader(); +} + +typeArrayOop fieldDescriptor::annotations() const { + instanceKlass* ik = instanceKlass::cast(field_holder()); + objArrayOop md = ik->fields_annotations(); + if (md == NULL) + return NULL; + assert((index() % instanceKlass::next_offset) == 0, ""); + return typeArrayOop(md->obj_at(index() / instanceKlass::next_offset)); +} + +constantTag fieldDescriptor::initial_value_tag() const { + return constants()->tag_at(_initial_value_index); +} + +jint fieldDescriptor::int_initial_value() const { + return constants()->int_at(_initial_value_index); +} + +jlong fieldDescriptor::long_initial_value() const { + return constants()->long_at(_initial_value_index); +} + +jfloat fieldDescriptor::float_initial_value() const { + return constants()->float_at(_initial_value_index); +} + +jdouble fieldDescriptor::double_initial_value() const { + return constants()->double_at(_initial_value_index); +} + +oop fieldDescriptor::string_initial_value(TRAPS) const { + return constants()->string_at(_initial_value_index, CHECK_0); +} + +void fieldDescriptor::initialize(klassOop k, int index) { + instanceKlass* ik = instanceKlass::cast(k); + _cp = ik->constants(); + typeArrayOop fields = ik->fields(); + + assert(fields->length() % instanceKlass::next_offset == 0, "Illegal size of field array"); + assert(fields->length() >= index + instanceKlass::next_offset, "Illegal size of field array"); + + _access_flags.set_field_flags(fields->ushort_at(index + instanceKlass::access_flags_offset)); + _name_index = fields->ushort_at(index + instanceKlass::name_index_offset); + _signature_index = fields->ushort_at(index + instanceKlass::signature_index_offset); + _initial_value_index = fields->ushort_at(index + instanceKlass::initval_index_offset); + guarantee(_name_index != 0 && _signature_index != 0, "bad constant pool index for fieldDescriptor"); + _offset = ik->offset_from_fields( index ); + _generic_signature_index = fields->ushort_at(index + instanceKlass::generic_signature_offset); + _index = index; +} + +#ifndef PRODUCT + +void fieldDescriptor::print_on(outputStream* st) const { + _access_flags.print_on(st); + constants()->symbol_at(_name_index)->print_value_on(st); + st->print(" "); + constants()->symbol_at(_signature_index)->print_value_on(st); + st->print(" @%d ", offset()); + if (WizardMode && has_initial_value()) { + st->print("(initval "); + constantTag t = initial_value_tag(); + if (t.is_int()) { + st->print("int %d)", int_initial_value()); + } else if (t.is_long()){ + st->print_jlong(long_initial_value()); + } else if (t.is_float()){ + st->print("float %f)", float_initial_value()); + } else if (t.is_double()){ + st->print("double %lf)", double_initial_value()); + } + } +} + +void fieldDescriptor::print_on_for(outputStream* st, oop obj) { + print_on(st); + BasicType ft = field_type(); + jint as_int; + switch (ft) { + case T_BYTE: + as_int = (jint)obj->byte_field(offset()); + st->print(" %d", obj->byte_field(offset())); + break; + case T_CHAR: + { + jchar c = obj->char_field(offset()); + as_int = c; + st->print(" %c %d", isprint(c) ? c : ' ', c); + } + break; + case T_DOUBLE: + st->print(" %lf", obj->double_field(offset())); + break; + case T_FLOAT: + as_int = obj->int_field(offset()); + st->print(" %f", obj->float_field(offset())); + break; + case T_INT: + st->print(" %d", obj->int_field(offset())); + break; + case T_LONG: + st->print(" "); + st->print_jlong(obj->long_field(offset())); + break; + case T_SHORT: + as_int = obj->short_field(offset()); + st->print(" %d", obj->short_field(offset())); + break; + case T_BOOLEAN: + as_int = obj->bool_field(offset()); + st->print(" %s", obj->bool_field(offset()) ? "true" : "false"); + break; + case T_ARRAY: + st->print(" "); + as_int = obj->int_field(offset()); + obj->obj_field(offset())->print_value_on(st); + break; + case T_OBJECT: + st->print(" "); + as_int = obj->int_field(offset()); + obj->obj_field(offset())->print_value_on(st); + break; + default: + ShouldNotReachHere(); + break; + } + // Print a hint as to the underlying integer representation. This can be wrong for + // pointers on an LP64 machine + if (ft == T_LONG || ft == T_DOUBLE) { + st->print(" (%x %x)", obj->int_field(offset()), obj->int_field(offset()+sizeof(jint))); + } else { + st->print(" (%x)", as_int); + } +} + +#endif /* PRODUCT */ diff --git a/hotspot/src/share/vm/runtime/fieldDescriptor.hpp b/hotspot/src/share/vm/runtime/fieldDescriptor.hpp new file mode 100644 index 00000000000..00cd662991d --- /dev/null +++ b/hotspot/src/share/vm/runtime/fieldDescriptor.hpp @@ -0,0 +1,92 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A fieldDescriptor describes the attributes of a single field (instance or class variable). +// It needs the class constant pool to work (because it only holds indices into the pool +// rather than the actual info). + +class fieldDescriptor VALUE_OBJ_CLASS_SPEC { + private: + AccessFlags _access_flags; + int _name_index; + int _signature_index; + int _initial_value_index; + int _offset; + int _generic_signature_index; + int _index; // index into fields() array + constantPoolHandle _cp; + + public: + symbolOop name() const { return _cp->symbol_at(_name_index); } + symbolOop signature() const { return _cp->symbol_at(_signature_index); } + klassOop field_holder() const { return _cp->pool_holder(); } + constantPoolOop constants() const { return _cp(); } + AccessFlags access_flags() const { return _access_flags; } + oop loader() const; + // Offset (in words) of field from start of instanceOop / klassOop + int offset() const { return _offset; } + symbolOop generic_signature() const { return (_generic_signature_index > 0 ? _cp->symbol_at(_generic_signature_index) : (symbolOop)NULL); } + int index() const { return _index; } + typeArrayOop annotations() const; + + // Initial field value + bool has_initial_value() const { return _initial_value_index != 0; } + constantTag initial_value_tag() const; // The tag will return true on one of is_int(), is_long(), is_single(), is_double() + jint int_initial_value() const; + jlong long_initial_value() const; + jfloat float_initial_value() const; + jdouble double_initial_value() const; + oop string_initial_value(TRAPS) const; + + // Field signature type + BasicType field_type() const { return FieldType::basic_type(signature()); } + + // Access flags + bool is_public() const { return _access_flags.is_public(); } + bool is_private() const { return _access_flags.is_private(); } + bool is_protected() const { return _access_flags.is_protected(); } + bool is_package_private() const { return !is_public() && !is_private() && !is_protected(); } + + bool is_static() const { return _access_flags.is_static(); } + bool is_final() const { return _access_flags.is_final(); } + bool is_volatile() const { return _access_flags.is_volatile(); } + bool is_transient() const { return _access_flags.is_transient(); } + + bool is_synthetic() const { return _access_flags.is_synthetic(); } + + bool is_field_access_watched() const { return _access_flags.is_field_access_watched(); } + bool is_field_modification_watched() const + { return _access_flags.is_field_modification_watched(); } + void set_is_field_access_watched(const bool value) + { _access_flags.set_is_field_access_watched(value); } + void set_is_field_modification_watched(const bool value) + { _access_flags.set_is_field_modification_watched(value); } + + // Initialization + void initialize(klassOop k, int index); + + // Print + void print_on(outputStream* st) const PRODUCT_RETURN; + void print_on_for(outputStream* st, oop obj) PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/runtime/fieldType.cpp b/hotspot/src/share/vm/runtime/fieldType.cpp new file mode 100644 index 00000000000..ab5840b8076 --- /dev/null +++ b/hotspot/src/share/vm/runtime/fieldType.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_fieldType.cpp.incl" + +void FieldType::skip_optional_size(symbolOop signature, int* index) { + jchar c = signature->byte_at(*index); + while (c >= '0' && c <= '9') { + *index = *index + 1; + c = signature->byte_at(*index); + } +} + +BasicType FieldType::basic_type(symbolOop signature) { + return char2type(signature->byte_at(0)); +} + +// Check if it is a valid array signature +bool FieldType::is_valid_array_signature(symbolOop sig) { + assert(sig->utf8_length() > 1, "this should already have been checked"); + assert(sig->byte_at(0) == '[', "this should already have been checked"); + // The first character is already checked + int i = 1; + int len = sig->utf8_length(); + // First skip all '['s + while(i < len - 1 && sig->byte_at(i) == '[') i++; + + // Check type + switch(sig->byte_at(i)) { + case 'B': // T_BYTE + case 'C': // T_CHAR + case 'D': // T_DOUBLE + case 'F': // T_FLOAT + case 'I': // T_INT + case 'J': // T_LONG + case 'S': // T_SHORT + case 'Z': // T_BOOLEAN + // If it is an array, the type is the last character + return (i + 1 == len); + case 'L': + // If it is an object, the last character must be a ';' + return sig->byte_at(len - 1) == ';'; + } + + return false; +} + + +BasicType FieldType::get_array_info(symbolOop signature, jint* dimension, symbolOop* object_key, TRAPS) { + assert(basic_type(signature) == T_ARRAY, "must be array"); + int index = 1; + int dim = 1; + skip_optional_size(signature, &index); + while (signature->byte_at(index) == '[') { + index++; + dim++; + skip_optional_size(signature, &index); + } + ResourceMark rm; + symbolOop element = oopFactory::new_symbol(signature->as_C_string() + index, CHECK_(T_BYTE)); + BasicType element_type = FieldType::basic_type(element); + if (element_type == T_OBJECT) { + char* object_type = element->as_C_string(); + object_type[element->utf8_length() - 1] = '\0'; + *object_key = oopFactory::new_symbol(object_type + 1, CHECK_(T_BYTE)); + } + // Pass dimension back to caller + *dimension = dim; + return element_type; +} diff --git a/hotspot/src/share/vm/runtime/fieldType.hpp b/hotspot/src/share/vm/runtime/fieldType.hpp new file mode 100644 index 00000000000..9f271667c20 --- /dev/null +++ b/hotspot/src/share/vm/runtime/fieldType.hpp @@ -0,0 +1,52 @@ +/* + * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Note: FieldType should be based on the SignatureIterator (or vice versa). +// In any case, this structure should be re-thought at some point. + +// A FieldType is used to determine the type of a field from a signature string. + +class FieldType: public AllStatic { + private: + static void skip_optional_size(symbolOop signature, int* index); + static bool is_valid_array_signature(symbolOop signature); + public: + + // Return basic type + static BasicType basic_type(symbolOop signature); + + // Testing + static bool is_array(symbolOop signature) { return signature->utf8_length() > 1 && signature->byte_at(0) == '[' && is_valid_array_signature(signature); } + + static bool is_obj(symbolOop signature) { + int sig_length = signature->utf8_length(); + // Must start with 'L' and end with ';' + return (sig_length >= 2 && + (signature->byte_at(0) == 'L') && + (signature->byte_at(sig_length - 1) == ';')); + } + + // Parse field and extract array information. Works for T_ARRAY only. + static BasicType get_array_info(symbolOop signature, jint* dimension, symbolOop *object_key, TRAPS); +}; diff --git a/hotspot/src/share/vm/runtime/fprofiler.cpp b/hotspot/src/share/vm/runtime/fprofiler.cpp new file mode 100644 index 00000000000..89d9cbaa204 --- /dev/null +++ b/hotspot/src/share/vm/runtime/fprofiler.cpp @@ -0,0 +1,1595 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_fprofiler.cpp.incl" + +// Static fields of FlatProfiler +int FlatProfiler::received_gc_ticks = 0; +int FlatProfiler::vm_operation_ticks = 0; +int FlatProfiler::threads_lock_ticks = 0; +int FlatProfiler::class_loader_ticks = 0; +int FlatProfiler::extra_ticks = 0; +int FlatProfiler::blocked_ticks = 0; +int FlatProfiler::deopt_ticks = 0; +int FlatProfiler::unknown_ticks = 0; +int FlatProfiler::interpreter_ticks = 0; +int FlatProfiler::compiler_ticks = 0; +int FlatProfiler::received_ticks = 0; +int FlatProfiler::delivered_ticks = 0; +int* FlatProfiler::bytecode_ticks = NULL; +int* FlatProfiler::bytecode_ticks_stub = NULL; +int FlatProfiler::all_int_ticks = 0; +int FlatProfiler::all_comp_ticks = 0; +int FlatProfiler::all_ticks = 0; +bool FlatProfiler::full_profile_flag = false; +ThreadProfiler* FlatProfiler::thread_profiler = NULL; +ThreadProfiler* FlatProfiler::vm_thread_profiler = NULL; +FlatProfilerTask* FlatProfiler::task = NULL; +elapsedTimer FlatProfiler::timer; +int FlatProfiler::interval_ticks_previous = 0; +IntervalData* FlatProfiler::interval_data = NULL; + +ThreadProfiler::ThreadProfiler() { + // Space for the ProfilerNodes + const int area_size = 1 * ProfilerNodeSize * 1024; + area_bottom = AllocateHeap(area_size, "fprofiler"); + area_top = area_bottom; + area_limit = area_bottom + area_size; + + // ProfilerNode pointer table + table = NEW_C_HEAP_ARRAY(ProfilerNode*, table_size); + initialize(); + engaged = false; +} + +ThreadProfiler::~ThreadProfiler() { + FreeHeap(area_bottom); + area_bottom = NULL; + area_top = NULL; + area_limit = NULL; + FreeHeap(table); + table = NULL; +} + +// Statics for ThreadProfiler +int ThreadProfiler::table_size = 1024; + +int ThreadProfiler::entry(int value) { + value = (value > 0) ? value : -value; + return value % table_size; +} + +ThreadProfilerMark::ThreadProfilerMark(ThreadProfilerMark::Region r) { + _r = r; + _pp = NULL; + assert(((r > ThreadProfilerMark::noRegion) && (r < ThreadProfilerMark::maxRegion)), "ThreadProfilerMark::Region out of bounds"); + Thread* tp = Thread::current(); + if (tp != NULL && tp->is_Java_thread()) { + JavaThread* jtp = (JavaThread*) tp; + ThreadProfiler* pp = jtp->get_thread_profiler(); + _pp = pp; + if (pp != NULL) { + pp->region_flag[r] = true; + } + } +} + +ThreadProfilerMark::~ThreadProfilerMark() { + if (_pp != NULL) { + _pp->region_flag[_r] = false; + } + _pp = NULL; +} + +// Random other statics +static const int col1 = 2; // position of output column 1 +static const int col2 = 11; // position of output column 2 +static const int col3 = 25; // position of output column 3 +static const int col4 = 55; // position of output column 4 + + +// Used for detailed profiling of nmethods. +class PCRecorder : AllStatic { + private: + static int* counters; + static address base; + enum { + bucket_size = 16 + }; + static int index_for(address pc) { return (pc - base)/bucket_size; } + static address pc_for(int index) { return base + (index * bucket_size); } + static int size() { + return ((int)CodeCache::max_capacity())/bucket_size * BytesPerWord; + } + public: + static address bucket_start_for(address pc) { + if (counters == NULL) return NULL; + return pc_for(index_for(pc)); + } + static int bucket_count_for(address pc) { return counters[index_for(pc)]; } + static void init(); + static void record(address pc); + static void print(); + static void print_blobs(CodeBlob* cb); +}; + +int* PCRecorder::counters = NULL; +address PCRecorder::base = NULL; + +void PCRecorder::init() { + MutexLockerEx lm(CodeCache_lock, Mutex::_no_safepoint_check_flag); + int s = size(); + counters = NEW_C_HEAP_ARRAY(int, s); + for (int index = 0; index < s; index++) { + counters[index] = 0; + } + base = CodeCache::first_address(); +} + +void PCRecorder::record(address pc) { + if (counters == NULL) return; + assert(CodeCache::contains(pc), "must be in CodeCache"); + counters[index_for(pc)]++; +} + + +address FlatProfiler::bucket_start_for(address pc) { + return PCRecorder::bucket_start_for(pc); +} + +int FlatProfiler::bucket_count_for(address pc) { + return PCRecorder::bucket_count_for(pc); +} + +void PCRecorder::print() { + if (counters == NULL) return; + + tty->cr(); + tty->print_cr("Printing compiled methods with PC buckets having more than %d ticks", ProfilerPCTickThreshold); + tty->print_cr("==================================================================="); + tty->cr(); + + GrowableArray* candidates = new GrowableArray(20); + + + int s; + { + MutexLockerEx lm(CodeCache_lock, Mutex::_no_safepoint_check_flag); + s = size(); + } + + for (int index = 0; index < s; index++) { + int count = counters[index]; + if (count > ProfilerPCTickThreshold) { + address pc = pc_for(index); + CodeBlob* cb = CodeCache::find_blob_unsafe(pc); + if (cb != NULL && candidates->find(cb) < 0) { + candidates->push(cb); + } + } + } + for (int i = 0; i < candidates->length(); i++) { + print_blobs(candidates->at(i)); + } +} + +void PCRecorder::print_blobs(CodeBlob* cb) { + if (cb != NULL) { + cb->print(); + if (cb->is_nmethod()) { + ((nmethod*)cb)->print_code(); + } + tty->cr(); + } else { + tty->print_cr("stub code"); + } +} + +class tick_counter { // holds tick info for one node + public: + int ticks_in_code; + int ticks_in_native; + + tick_counter() { ticks_in_code = ticks_in_native = 0; } + tick_counter(int code, int native) { ticks_in_code = code; ticks_in_native = native; } + + int total() const { + return (ticks_in_code + ticks_in_native); + } + + void add(tick_counter* a) { + ticks_in_code += a->ticks_in_code; + ticks_in_native += a->ticks_in_native; + } + + void update(TickPosition where) { + switch(where) { + case tp_code: ticks_in_code++; break; + case tp_native: ticks_in_native++; break; + } + } + + void print_code(outputStream* st, int total_ticks) { + st->print("%5.1f%% %5d ", total() * 100.0 / total_ticks, ticks_in_code); + } + + void print_native(outputStream* st) { + st->print(" + %5d ", ticks_in_native); + } +}; + +class ProfilerNode { + private: + ProfilerNode* _next; + public: + tick_counter ticks; + + public: + + void* operator new(size_t size, ThreadProfiler* tp); + void operator delete(void* p); + + ProfilerNode() { + _next = NULL; + } + + virtual ~ProfilerNode() { + if (_next) + delete _next; + } + + void set_next(ProfilerNode* n) { _next = n; } + ProfilerNode* next() { return _next; } + + void update(TickPosition where) { ticks.update(where);} + int total_ticks() { return ticks.total(); } + + virtual bool is_interpreted() const { return false; } + virtual bool is_compiled() const { return false; } + virtual bool is_stub() const { return false; } + virtual bool is_runtime_stub() const{ return false; } + virtual void oops_do(OopClosure* f) = 0; + + virtual bool interpreted_match(methodOop m) const { return false; } + virtual bool compiled_match(methodOop m ) const { return false; } + virtual bool stub_match(methodOop m, const char* name) const { return false; } + virtual bool adapter_match() const { return false; } + virtual bool runtimeStub_match(const CodeBlob* stub, const char* name) const { return false; } + virtual bool unknown_compiled_match(const CodeBlob* cb) const { return false; } + + static void print_title(outputStream* st) { + st->print(" + native"); + st->fill_to(col3); + st->print("Method"); + st->fill_to(col4); + st->cr(); + } + + static void print_total(outputStream* st, tick_counter* t, int total, const char* msg) { + t->print_code(st, total); + st->fill_to(col2); + t->print_native(st); + st->fill_to(col3); + st->print(msg); + st->cr(); + } + + virtual methodOop method() = 0; + + virtual void print_method_on(outputStream* st) { + int limit; + int i; + methodOop m = method(); + symbolOop k = m->klass_name(); + // Print the class name with dots instead of slashes + limit = k->utf8_length(); + for (i = 0 ; i < limit ; i += 1) { + char c = (char) k->byte_at(i); + if (c == '/') { + c = '.'; + } + st->print("%c", c); + } + if (limit > 0) { + st->print("."); + } + symbolOop n = m->name(); + limit = n->utf8_length(); + for (i = 0 ; i < limit ; i += 1) { + char c = (char) n->byte_at(i); + st->print("%c", c); + } + if( Verbose ) { + // Disambiguate overloaded methods + symbolOop sig = m->signature(); + sig->print_symbol_on(st); + } + } + + virtual void print(outputStream* st, int total_ticks) { + ticks.print_code(st, total_ticks); + st->fill_to(col2); + ticks.print_native(st); + st->fill_to(col3); + print_method_on(st); + st->cr(); + } + + // for hashing into the table + static int hash(methodOop method) { + // The point here is to try to make something fairly unique + // out of the fields we can read without grabbing any locks + // since the method may be locked when we need the hash. + return ( + method->code_size() ^ + method->max_stack() ^ + method->max_locals() ^ + method->size_of_parameters()); + } + + // for sorting + static int compare(ProfilerNode** a, ProfilerNode** b) { + return (*b)->total_ticks() - (*a)->total_ticks(); + } +}; + +void* ProfilerNode::operator new(size_t size, ThreadProfiler* tp){ + void* result = (void*) tp->area_top; + tp->area_top += size; + + if (tp->area_top > tp->area_limit) { + fatal("flat profiler buffer overflow"); + } + return result; +} + +void ProfilerNode::operator delete(void* p){ +} + +class interpretedNode : public ProfilerNode { + private: + methodOop _method; + public: + interpretedNode(methodOop method, TickPosition where) : ProfilerNode() { + _method = method; + update(where); + } + + bool is_interpreted() const { return true; } + + bool interpreted_match(methodOop m) const { + return _method == m; + } + + void oops_do(OopClosure* f) { + f->do_oop((oop*)&_method); + } + + methodOop method() { return _method; } + + static void print_title(outputStream* st) { + st->fill_to(col1); + st->print("%11s", "Interpreted"); + ProfilerNode::print_title(st); + } + + void print(outputStream* st, int total_ticks) { + ProfilerNode::print(st, total_ticks); + } + + void print_method_on(outputStream* st) { + ProfilerNode::print_method_on(st); + if (Verbose) method()->invocation_counter()->print_short(); + } +}; + +class compiledNode : public ProfilerNode { + private: + methodOop _method; + public: + compiledNode(methodOop method, TickPosition where) : ProfilerNode() { + _method = method; + update(where); + } + bool is_compiled() const { return true; } + + bool compiled_match(methodOop m) const { + return _method == m; + } + + methodOop method() { return _method; } + + void oops_do(OopClosure* f) { + f->do_oop((oop*)&_method); + } + + static void print_title(outputStream* st) { + st->fill_to(col1); + st->print("%11s", "Compiled"); + ProfilerNode::print_title(st); + } + + void print(outputStream* st, int total_ticks) { + ProfilerNode::print(st, total_ticks); + } + + void print_method_on(outputStream* st) { + ProfilerNode::print_method_on(st); + } +}; + +class stubNode : public ProfilerNode { + private: + methodOop _method; + const char* _symbol; // The name of the nearest VM symbol (for +ProfileVM). Points to a unique string + public: + stubNode(methodOop method, const char* name, TickPosition where) : ProfilerNode() { + _method = method; + _symbol = name; + update(where); + } + + bool is_stub() const { return true; } + + bool stub_match(methodOop m, const char* name) const { + return (_method == m) && (_symbol == name); + } + + void oops_do(OopClosure* f) { + f->do_oop((oop*)&_method); + } + + methodOop method() { return _method; } + + static void print_title(outputStream* st) { + st->fill_to(col1); + st->print("%11s", "Stub"); + ProfilerNode::print_title(st); + } + + void print(outputStream* st, int total_ticks) { + ProfilerNode::print(st, total_ticks); + } + + void print_method_on(outputStream* st) { + ProfilerNode::print_method_on(st); + print_symbol_on(st); + } + + void print_symbol_on(outputStream* st) { + if(_symbol) { + st->print(" (%s)", _symbol); + } + } +}; + +class adapterNode : public ProfilerNode { + public: + adapterNode(TickPosition where) : ProfilerNode() { + update(where); + } + bool is_compiled() const { return true; } + + bool adapter_match() const { return true; } + + methodOop method() { return NULL; } + + void oops_do(OopClosure* f) { + ; + } + + void print(outputStream* st, int total_ticks) { + ProfilerNode::print(st, total_ticks); + } + + void print_method_on(outputStream* st) { + st->print("%s", "adapters"); + } +}; + +class runtimeStubNode : public ProfilerNode { + private: + const CodeBlob* _stub; + const char* _symbol; // The name of the nearest VM symbol when ProfileVM is on. Points to a unique string. + public: + runtimeStubNode(const CodeBlob* stub, const char* name, TickPosition where) : ProfilerNode(), _stub(stub), _symbol(name) { + assert(stub->is_runtime_stub(), "wrong code blob"); + update(where); + } + + bool is_runtime_stub() const { return true; } + + bool runtimeStub_match(const CodeBlob* stub, const char* name) const { + assert(stub->is_runtime_stub(), "wrong code blob"); + return ((RuntimeStub*)_stub)->entry_point() == ((RuntimeStub*)stub)->entry_point() && + (_symbol == name); + } + + methodOop method() { return NULL; } + + static void print_title(outputStream* st) { + st->fill_to(col1); + st->print("%11s", "Runtime stub"); + ProfilerNode::print_title(st); + } + + void oops_do(OopClosure* f) { + ; + } + + void print(outputStream* st, int total_ticks) { + ProfilerNode::print(st, total_ticks); + } + + void print_method_on(outputStream* st) { + st->print("%s", ((RuntimeStub*)_stub)->name()); + print_symbol_on(st); + } + + void print_symbol_on(outputStream* st) { + if(_symbol) { + st->print(" (%s)", _symbol); + } + } +}; + + +class unknown_compiledNode : public ProfilerNode { + const char *_name; + public: + unknown_compiledNode(const CodeBlob* cb, TickPosition where) : ProfilerNode() { + if ( cb->is_buffer_blob() ) + _name = ((BufferBlob*)cb)->name(); + else + _name = ((SingletonBlob*)cb)->name(); + update(where); + } + bool is_compiled() const { return true; } + + bool unknown_compiled_match(const CodeBlob* cb) const { + if ( cb->is_buffer_blob() ) + return !strcmp(((BufferBlob*)cb)->name(), _name); + else + return !strcmp(((SingletonBlob*)cb)->name(), _name); + } + + methodOop method() { return NULL; } + + void oops_do(OopClosure* f) { + ; + } + + void print(outputStream* st, int total_ticks) { + ProfilerNode::print(st, total_ticks); + } + + void print_method_on(outputStream* st) { + st->print("%s", _name); + } +}; + +class vmNode : public ProfilerNode { + private: + const char* _name; // "optional" name obtained by os means such as dll lookup + public: + vmNode(const TickPosition where) : ProfilerNode() { + _name = NULL; + update(where); + } + + vmNode(const char* name, const TickPosition where) : ProfilerNode() { + _name = name; + update(where); + } + + const char *name() const { return _name; } + bool is_compiled() const { return true; } + + bool vm_match(const char* name) const { return strcmp(name, _name) == 0; } + + methodOop method() { return NULL; } + + static int hash(const char* name){ + // Compute a simple hash + const char* cp = name; + int h = 0; + + if(name != NULL){ + while(*cp != '\0'){ + h = (h << 1) ^ *cp; + cp++; + } + } + return h; + } + + void oops_do(OopClosure* f) { + ; + } + + void print(outputStream* st, int total_ticks) { + ProfilerNode::print(st, total_ticks); + } + + void print_method_on(outputStream* st) { + if(_name==NULL){ + st->print("%s", "unknown code"); + } + else { + st->print("%s", _name); + } + } +}; + +void ThreadProfiler::interpreted_update(methodOop method, TickPosition where) { + int index = entry(ProfilerNode::hash(method)); + if (!table[index]) { + table[index] = new (this) interpretedNode(method, where); + } else { + ProfilerNode* prev = table[index]; + for(ProfilerNode* node = prev; node; node = node->next()) { + if (node->interpreted_match(method)) { + node->update(where); + return; + } + prev = node; + } + prev->set_next(new (this) interpretedNode(method, where)); + } +} + +void ThreadProfiler::compiled_update(methodOop method, TickPosition where) { + int index = entry(ProfilerNode::hash(method)); + if (!table[index]) { + table[index] = new (this) compiledNode(method, where); + } else { + ProfilerNode* prev = table[index]; + for(ProfilerNode* node = prev; node; node = node->next()) { + if (node->compiled_match(method)) { + node->update(where); + return; + } + prev = node; + } + prev->set_next(new (this) compiledNode(method, where)); + } +} + +void ThreadProfiler::stub_update(methodOop method, const char* name, TickPosition where) { + int index = entry(ProfilerNode::hash(method)); + if (!table[index]) { + table[index] = new (this) stubNode(method, name, where); + } else { + ProfilerNode* prev = table[index]; + for(ProfilerNode* node = prev; node; node = node->next()) { + if (node->stub_match(method, name)) { + node->update(where); + return; + } + prev = node; + } + prev->set_next(new (this) stubNode(method, name, where)); + } +} + +void ThreadProfiler::adapter_update(TickPosition where) { + int index = 0; + if (!table[index]) { + table[index] = new (this) adapterNode(where); + } else { + ProfilerNode* prev = table[index]; + for(ProfilerNode* node = prev; node; node = node->next()) { + if (node->adapter_match()) { + node->update(where); + return; + } + prev = node; + } + prev->set_next(new (this) adapterNode(where)); + } +} + +void ThreadProfiler::runtime_stub_update(const CodeBlob* stub, const char* name, TickPosition where) { + int index = 0; + if (!table[index]) { + table[index] = new (this) runtimeStubNode(stub, name, where); + } else { + ProfilerNode* prev = table[index]; + for(ProfilerNode* node = prev; node; node = node->next()) { + if (node->runtimeStub_match(stub, name)) { + node->update(where); + return; + } + prev = node; + } + prev->set_next(new (this) runtimeStubNode(stub, name, where)); + } +} + + +void ThreadProfiler::unknown_compiled_update(const CodeBlob* cb, TickPosition where) { + int index = 0; + if (!table[index]) { + table[index] = new (this) unknown_compiledNode(cb, where); + } else { + ProfilerNode* prev = table[index]; + for(ProfilerNode* node = prev; node; node = node->next()) { + if (node->unknown_compiled_match(cb)) { + node->update(where); + return; + } + prev = node; + } + prev->set_next(new (this) unknown_compiledNode(cb, where)); + } +} + +void ThreadProfiler::vm_update(TickPosition where) { + vm_update(NULL, where); +} + +void ThreadProfiler::vm_update(const char* name, TickPosition where) { + int index = entry(vmNode::hash(name)); + assert(index >= 0, "Must be positive"); + // Note that we call strdup below since the symbol may be resource allocated + if (!table[index]) { + table[index] = new (this) vmNode(os::strdup(name), where); + } else { + ProfilerNode* prev = table[index]; + for(ProfilerNode* node = prev; node; node = node->next()) { + if (((vmNode *)node)->vm_match(name)) { + node->update(where); + return; + } + prev = node; + } + prev->set_next(new (this) vmNode(os::strdup(name), where)); + } +} + + +class FlatProfilerTask : public PeriodicTask { +public: + FlatProfilerTask(int interval_time) : PeriodicTask(interval_time) {} + void task(); +}; + +void FlatProfiler::record_vm_operation() { + if (Universe::heap()->is_gc_active()) { + FlatProfiler::received_gc_ticks += 1; + return; + } + + if (DeoptimizationMarker::is_active()) { + FlatProfiler::deopt_ticks += 1; + return; + } + + FlatProfiler::vm_operation_ticks += 1; +} + +void FlatProfiler::record_vm_tick() { + // Profile the VM Thread itself if needed + // This is done without getting the Threads_lock and we can go deep + // inside Safepoint, etc. + if( ProfileVM ) { + ResourceMark rm; + ExtendedPC epc; + const char *name = NULL; + char buf[256]; + buf[0] = '\0'; + + vm_thread_profiler->inc_thread_ticks(); + + // Get a snapshot of a current VMThread pc (and leave it running!) + // The call may fail if, for instance the VM thread is interrupted while + // holding the Interrupt_lock or for other reasons. + epc = os::get_thread_pc(VMThread::vm_thread()); + if(epc.pc() != NULL) { + if (os::dll_address_to_function_name(epc.pc(), buf, sizeof(buf), NULL)) { + name = buf; + } + } + if (name != NULL) { + vm_thread_profiler->vm_update(name, tp_native); + } + } +} + +void FlatProfiler::record_thread_ticks() { + + int maxthreads, suspendedthreadcount; + JavaThread** threadsList; + bool interval_expired = false; + + if (ProfileIntervals && + (FlatProfiler::received_ticks >= interval_ticks_previous + ProfileIntervalsTicks)) { + interval_expired = true; + interval_ticks_previous = FlatProfiler::received_ticks; + } + + // Try not to wait for the Threads_lock + if (Threads_lock->try_lock()) { + { // Threads_lock scope + maxthreads = Threads::number_of_threads(); + threadsList = NEW_C_HEAP_ARRAY(JavaThread *, maxthreads); + suspendedthreadcount = 0; + for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) { + if (tp->is_Compiler_thread()) { + // Only record ticks for active compiler threads + CompilerThread* cthread = (CompilerThread*)tp; + if (cthread->task() != NULL) { + // The compiler is active. If we need to access any of the fields + // of the compiler task we should suspend the CompilerThread first. + FlatProfiler::compiler_ticks += 1; + continue; + } + } + + // First externally suspend all threads by marking each for + // external suspension - so it will stop at its next transition + // Then do a safepoint + ThreadProfiler* pp = tp->get_thread_profiler(); + if (pp != NULL && pp->engaged) { + MutexLockerEx ml(tp->SR_lock(), Mutex::_no_safepoint_check_flag); + if (!tp->is_external_suspend() && !tp->is_exiting()) { + tp->set_external_suspend(); + threadsList[suspendedthreadcount++] = tp; + } + } + } + Threads_lock->unlock(); + } + // Suspend each thread. This call should just return + // for any threads that have already self-suspended + // Net result should be one safepoint + for (int j = 0; j < suspendedthreadcount; j++) { + JavaThread *tp = threadsList[j]; + if (tp) { + tp->java_suspend(); + } + } + + // We are responsible for resuming any thread on this list + for (int i = 0; i < suspendedthreadcount; i++) { + JavaThread *tp = threadsList[i]; + if (tp) { + ThreadProfiler* pp = tp->get_thread_profiler(); + if (pp != NULL && pp->engaged) { + HandleMark hm; + FlatProfiler::delivered_ticks += 1; + if (interval_expired) { + FlatProfiler::interval_record_thread(pp); + } + // This is the place where we check to see if a user thread is + // blocked waiting for compilation. + if (tp->blocked_on_compilation()) { + pp->compiler_ticks += 1; + pp->interval_data_ref()->inc_compiling(); + } else { + pp->record_tick(tp); + } + } + MutexLocker ml(Threads_lock); + tp->java_resume(); + } + } + if (interval_expired) { + FlatProfiler::interval_print(); + FlatProfiler::interval_reset(); + } + } else { + // Couldn't get the threads lock, just record that rather than blocking + FlatProfiler::threads_lock_ticks += 1; + } + +} + +void FlatProfilerTask::task() { + FlatProfiler::received_ticks += 1; + + if (ProfileVM) { + FlatProfiler::record_vm_tick(); + } + + VM_Operation* op = VMThread::vm_operation(); + if (op != NULL) { + FlatProfiler::record_vm_operation(); + if (SafepointSynchronize::is_at_safepoint()) { + return; + } + } + FlatProfiler::record_thread_ticks(); +} + +void ThreadProfiler::record_interpreted_tick(frame fr, TickPosition where, int* ticks) { + FlatProfiler::all_int_ticks++; + if (!FlatProfiler::full_profile()) { + return; + } + + if (!fr.is_interpreted_frame_valid()) { + // tick came at a bad time + interpreter_ticks += 1; + FlatProfiler::interpreter_ticks += 1; + return; + } + + methodOop method = NULL; + if (fr.fp() != NULL) { + method = *fr.interpreter_frame_method_addr(); + } + if (!Universe::heap()->is_valid_method(method)) { + // tick came at a bad time, stack frame not initialized correctly + interpreter_ticks += 1; + FlatProfiler::interpreter_ticks += 1; + return; + } + interpreted_update(method, where); + + // update byte code table + InterpreterCodelet* desc = Interpreter::codelet_containing(fr.pc()); + if (desc != NULL && desc->bytecode() >= 0) { + ticks[desc->bytecode()]++; + } +} + +void ThreadProfiler::record_compiled_tick(JavaThread* thread, frame fr, TickPosition where) { + const char *name = NULL; + TickPosition localwhere = where; + + FlatProfiler::all_comp_ticks++; + if (!FlatProfiler::full_profile()) return; + + CodeBlob* cb = fr.cb(); + +// For runtime stubs, record as native rather than as compiled + if (cb->is_runtime_stub()) { + RegisterMap map(thread, false); + fr = fr.sender(&map); + cb = fr.cb(); + localwhere = tp_native; + } + methodOop method = (cb->is_nmethod()) ? ((nmethod *)cb)->method() : + (methodOop)NULL; + + if (method == NULL) { + if (cb->is_runtime_stub()) + runtime_stub_update(cb, name, localwhere); + else + unknown_compiled_update(cb, localwhere); + } + else { + if (method->is_native()) { + stub_update(method, name, localwhere); + } else { + compiled_update(method, localwhere); + } + } +} + +extern "C" void find(int x); + + +void ThreadProfiler::record_tick_for_running_frame(JavaThread* thread, frame fr) { + // The tick happend in real code -> non VM code + if (fr.is_interpreted_frame()) { + interval_data_ref()->inc_interpreted(); + record_interpreted_tick(fr, tp_code, FlatProfiler::bytecode_ticks); + return; + } + + if (CodeCache::contains(fr.pc())) { + interval_data_ref()->inc_compiled(); + PCRecorder::record(fr.pc()); + record_compiled_tick(thread, fr, tp_code); + return; + } + + if (VtableStubs::stub_containing(fr.pc()) != NULL) { + unknown_ticks_array[ut_vtable_stubs] += 1; + return; + } + + frame caller = fr.profile_find_Java_sender_frame(thread); + + if (caller.sp() != NULL && caller.pc() != NULL) { + record_tick_for_calling_frame(thread, caller); + return; + } + + unknown_ticks_array[ut_running_frame] += 1; + FlatProfiler::unknown_ticks += 1; +} + +void ThreadProfiler::record_tick_for_calling_frame(JavaThread* thread, frame fr) { + // The tick happend in VM code + interval_data_ref()->inc_native(); + if (fr.is_interpreted_frame()) { + record_interpreted_tick(fr, tp_native, FlatProfiler::bytecode_ticks_stub); + return; + } + if (CodeCache::contains(fr.pc())) { + record_compiled_tick(thread, fr, tp_native); + return; + } + + frame caller = fr.profile_find_Java_sender_frame(thread); + + if (caller.sp() != NULL && caller.pc() != NULL) { + record_tick_for_calling_frame(thread, caller); + return; + } + + unknown_ticks_array[ut_calling_frame] += 1; + FlatProfiler::unknown_ticks += 1; +} + +void ThreadProfiler::record_tick(JavaThread* thread) { + FlatProfiler::all_ticks++; + thread_ticks += 1; + + // Here's another way to track global state changes. + // When the class loader starts it marks the ThreadProfiler to tell it it is in the class loader + // and we check that here. + // This is more direct, and more than one thread can be in the class loader at a time, + // but it does mean the class loader has to know about the profiler. + if (region_flag[ThreadProfilerMark::classLoaderRegion]) { + class_loader_ticks += 1; + FlatProfiler::class_loader_ticks += 1; + return; + } else if (region_flag[ThreadProfilerMark::extraRegion]) { + extra_ticks += 1; + FlatProfiler::extra_ticks += 1; + return; + } + // Note that the WatcherThread can now stop for safepoints + uint32_t debug_bits = 0; + if (!thread->wait_for_ext_suspend_completion(SuspendRetryCount, + SuspendRetryDelay, &debug_bits)) { + unknown_ticks_array[ut_unknown_thread_state] += 1; + FlatProfiler::unknown_ticks += 1; + return; + } + + frame fr; + + switch (thread->thread_state()) { + case _thread_in_native: + case _thread_in_native_trans: + case _thread_in_vm: + case _thread_in_vm_trans: + if (thread->profile_last_Java_frame(&fr)) { + if (fr.is_runtime_frame()) { + RegisterMap map(thread, false); + fr = fr.sender(&map); + } + record_tick_for_calling_frame(thread, fr); + } else { + unknown_ticks_array[ut_no_last_Java_frame] += 1; + FlatProfiler::unknown_ticks += 1; + } + break; + // handle_special_runtime_exit_condition self-suspends threads in Java + case _thread_in_Java: + case _thread_in_Java_trans: + if (thread->profile_last_Java_frame(&fr)) { + if (fr.is_safepoint_blob_frame()) { + RegisterMap map(thread, false); + fr = fr.sender(&map); + } + record_tick_for_running_frame(thread, fr); + } else { + unknown_ticks_array[ut_no_last_Java_frame] += 1; + FlatProfiler::unknown_ticks += 1; + } + break; + case _thread_blocked: + case _thread_blocked_trans: + if (thread->osthread() && thread->osthread()->get_state() == RUNNABLE) { + if (thread->profile_last_Java_frame(&fr)) { + if (fr.is_safepoint_blob_frame()) { + RegisterMap map(thread, false); + fr = fr.sender(&map); + record_tick_for_running_frame(thread, fr); + } else { + record_tick_for_calling_frame(thread, fr); + } + } else { + unknown_ticks_array[ut_no_last_Java_frame] += 1; + FlatProfiler::unknown_ticks += 1; + } + } else { + blocked_ticks += 1; + FlatProfiler::blocked_ticks += 1; + } + break; + case _thread_uninitialized: + case _thread_new: + // not used, included for completeness + case _thread_new_trans: + unknown_ticks_array[ut_no_last_Java_frame] += 1; + FlatProfiler::unknown_ticks += 1; + break; + default: + unknown_ticks_array[ut_unknown_thread_state] += 1; + FlatProfiler::unknown_ticks += 1; + break; + } + return; +} + +void ThreadProfiler::engage() { + engaged = true; + timer.start(); +} + +void ThreadProfiler::disengage() { + engaged = false; + timer.stop(); +} + +void ThreadProfiler::initialize() { + for (int index = 0; index < table_size; index++) { + table[index] = NULL; + } + thread_ticks = 0; + blocked_ticks = 0; + compiler_ticks = 0; + interpreter_ticks = 0; + for (int ut = 0; ut < ut_end; ut += 1) { + unknown_ticks_array[ut] = 0; + } + region_flag[ThreadProfilerMark::classLoaderRegion] = false; + class_loader_ticks = 0; + region_flag[ThreadProfilerMark::extraRegion] = false; + extra_ticks = 0; + timer.start(); + interval_data_ref()->reset(); +} + +void ThreadProfiler::reset() { + timer.stop(); + if (table != NULL) { + for (int index = 0; index < table_size; index++) { + ProfilerNode* n = table[index]; + if (n != NULL) { + delete n; + } + } + } + initialize(); +} + +void FlatProfiler::allocate_table() { + { // Bytecode table + bytecode_ticks = NEW_C_HEAP_ARRAY(int, Bytecodes::number_of_codes); + bytecode_ticks_stub = NEW_C_HEAP_ARRAY(int, Bytecodes::number_of_codes); + for(int index = 0; index < Bytecodes::number_of_codes; index++) { + bytecode_ticks[index] = 0; + bytecode_ticks_stub[index] = 0; + } + } + + if (ProfilerRecordPC) PCRecorder::init(); + + interval_data = NEW_C_HEAP_ARRAY(IntervalData, interval_print_size); + FlatProfiler::interval_reset(); +} + +void FlatProfiler::engage(JavaThread* mainThread, bool fullProfile) { + full_profile_flag = fullProfile; + if (bytecode_ticks == NULL) { + allocate_table(); + } + if(ProfileVM && (vm_thread_profiler == NULL)){ + vm_thread_profiler = new ThreadProfiler(); + } + if (task == NULL) { + task = new FlatProfilerTask(WatcherThread::delay_interval); + task->enroll(); + } + timer.start(); + if (mainThread != NULL) { + // When mainThread was created, it might not have a ThreadProfiler + ThreadProfiler* pp = mainThread->get_thread_profiler(); + if (pp == NULL) { + mainThread->set_thread_profiler(new ThreadProfiler()); + } else { + pp->reset(); + } + mainThread->get_thread_profiler()->engage(); + } + // This is where we would assign thread_profiler + // if we wanted only one thread_profiler for all threads. + thread_profiler = NULL; +} + +void FlatProfiler::disengage() { + if (!task) { + return; + } + timer.stop(); + task->disenroll(); + delete task; + task = NULL; + if (thread_profiler != NULL) { + thread_profiler->disengage(); + } else { + MutexLocker tl(Threads_lock); + for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) { + ThreadProfiler* pp = tp->get_thread_profiler(); + if (pp != NULL) { + pp->disengage(); + } + } + } +} + +void FlatProfiler::reset() { + if (task) { + disengage(); + } + + class_loader_ticks = 0; + extra_ticks = 0; + received_gc_ticks = 0; + vm_operation_ticks = 0; + compiler_ticks = 0; + deopt_ticks = 0; + interpreter_ticks = 0; + blocked_ticks = 0; + unknown_ticks = 0; + received_ticks = 0; + delivered_ticks = 0; + timer.stop(); +} + +bool FlatProfiler::is_active() { + return task != NULL; +} + +void FlatProfiler::print_byte_code_statistics() { + GrowableArray * array = new GrowableArray(200); + + tty->print_cr(" Bytecode ticks:"); + for (int index = 0; index < Bytecodes::number_of_codes; index++) { + if (FlatProfiler::bytecode_ticks[index] > 0 || FlatProfiler::bytecode_ticks_stub[index] > 0) { + tty->print_cr(" %4d %4d = %s", + FlatProfiler::bytecode_ticks[index], + FlatProfiler::bytecode_ticks_stub[index], + Bytecodes::name( (Bytecodes::Code) index)); + } + } + tty->cr(); +} + +void print_ticks(const char* title, int ticks, int total) { + if (ticks > 0) { + tty->print("%5.1f%% %5d", ticks * 100.0 / total, ticks); + tty->fill_to(col3); + tty->print("%s", title); + tty->cr(); + } +} + +void ThreadProfiler::print(const char* thread_name) { + ResourceMark rm; + MutexLocker ppl(ProfilePrint_lock); + int index = 0; // Declared outside for loops for portability + + if (table == NULL) { + return; + } + + if (thread_ticks <= 0) { + return; + } + + const char* title = "too soon to tell"; + double secs = timer.seconds(); + + GrowableArray * array = new GrowableArray(200); + for(index = 0; index < table_size; index++) { + for(ProfilerNode* node = table[index]; node; node = node->next()) + array->append(node); + } + + array->sort(&ProfilerNode::compare); + + // compute total (sanity check) + int active = + class_loader_ticks + + compiler_ticks + + interpreter_ticks + + unknown_ticks(); + for (index = 0; index < array->length(); index++) { + active += array->at(index)->ticks.total(); + } + int total = active + blocked_ticks; + + tty->cr(); + tty->print_cr("Flat profile of %3.2f secs (%d total ticks): %s", secs, total, thread_name); + if (total != thread_ticks) { + print_ticks("Lost ticks", thread_ticks-total, thread_ticks); + } + tty->cr(); + + // print interpreted methods + tick_counter interpreted_ticks; + bool has_interpreted_ticks = false; + int print_count = 0; + for (index = 0; index < array->length(); index++) { + ProfilerNode* n = array->at(index); + if (n->is_interpreted()) { + interpreted_ticks.add(&n->ticks); + if (!has_interpreted_ticks) { + interpretedNode::print_title(tty); + has_interpreted_ticks = true; + } + if (print_count++ < ProfilerNumberOfInterpretedMethods) { + n->print(tty, active); + } + } + } + if (has_interpreted_ticks) { + if (print_count <= ProfilerNumberOfInterpretedMethods) { + title = "Total interpreted"; + } else { + title = "Total interpreted (including elided)"; + } + interpretedNode::print_total(tty, &interpreted_ticks, active, title); + tty->cr(); + } + + // print compiled methods + tick_counter compiled_ticks; + bool has_compiled_ticks = false; + print_count = 0; + for (index = 0; index < array->length(); index++) { + ProfilerNode* n = array->at(index); + if (n->is_compiled()) { + compiled_ticks.add(&n->ticks); + if (!has_compiled_ticks) { + compiledNode::print_title(tty); + has_compiled_ticks = true; + } + if (print_count++ < ProfilerNumberOfCompiledMethods) { + n->print(tty, active); + } + } + } + if (has_compiled_ticks) { + if (print_count <= ProfilerNumberOfCompiledMethods) { + title = "Total compiled"; + } else { + title = "Total compiled (including elided)"; + } + compiledNode::print_total(tty, &compiled_ticks, active, title); + tty->cr(); + } + + // print stub methods + tick_counter stub_ticks; + bool has_stub_ticks = false; + print_count = 0; + for (index = 0; index < array->length(); index++) { + ProfilerNode* n = array->at(index); + if (n->is_stub()) { + stub_ticks.add(&n->ticks); + if (!has_stub_ticks) { + stubNode::print_title(tty); + has_stub_ticks = true; + } + if (print_count++ < ProfilerNumberOfStubMethods) { + n->print(tty, active); + } + } + } + if (has_stub_ticks) { + if (print_count <= ProfilerNumberOfStubMethods) { + title = "Total stub"; + } else { + title = "Total stub (including elided)"; + } + stubNode::print_total(tty, &stub_ticks, active, title); + tty->cr(); + } + + // print runtime stubs + tick_counter runtime_stub_ticks; + bool has_runtime_stub_ticks = false; + print_count = 0; + for (index = 0; index < array->length(); index++) { + ProfilerNode* n = array->at(index); + if (n->is_runtime_stub()) { + runtime_stub_ticks.add(&n->ticks); + if (!has_runtime_stub_ticks) { + runtimeStubNode::print_title(tty); + has_runtime_stub_ticks = true; + } + if (print_count++ < ProfilerNumberOfRuntimeStubNodes) { + n->print(tty, active); + } + } + } + if (has_runtime_stub_ticks) { + if (print_count <= ProfilerNumberOfRuntimeStubNodes) { + title = "Total runtime stubs"; + } else { + title = "Total runtime stubs (including elided)"; + } + runtimeStubNode::print_total(tty, &runtime_stub_ticks, active, title); + tty->cr(); + } + + if (blocked_ticks + class_loader_ticks + interpreter_ticks + compiler_ticks + unknown_ticks() != 0) { + tty->fill_to(col1); + tty->print_cr("Thread-local ticks:"); + print_ticks("Blocked (of total)", blocked_ticks, total); + print_ticks("Class loader", class_loader_ticks, active); + print_ticks("Extra", extra_ticks, active); + print_ticks("Interpreter", interpreter_ticks, active); + print_ticks("Compilation", compiler_ticks, active); + print_ticks("Unknown: vtable stubs", unknown_ticks_array[ut_vtable_stubs], active); + print_ticks("Unknown: null method", unknown_ticks_array[ut_null_method], active); + print_ticks("Unknown: running frame", unknown_ticks_array[ut_running_frame], active); + print_ticks("Unknown: calling frame", unknown_ticks_array[ut_calling_frame], active); + print_ticks("Unknown: no pc", unknown_ticks_array[ut_no_pc], active); + print_ticks("Unknown: no last frame", unknown_ticks_array[ut_no_last_Java_frame], active); + print_ticks("Unknown: thread_state", unknown_ticks_array[ut_unknown_thread_state], active); + tty->cr(); + } + + if (WizardMode) { + tty->print_cr("Node area used: %dKb", (area_top - area_bottom) / 1024); + } + reset(); +} + +/* +ThreadProfiler::print_unknown(){ + if (table == NULL) { + return; + } + + if (thread_ticks <= 0) { + return; + } +} */ + +void FlatProfiler::print(int unused) { + ResourceMark rm; + if (thread_profiler != NULL) { + thread_profiler->print("All threads"); + } else { + MutexLocker tl(Threads_lock); + for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) { + ThreadProfiler* pp = tp->get_thread_profiler(); + if (pp != NULL) { + pp->print(tp->get_thread_name()); + } + } + } + + if (ProfilerPrintByteCodeStatistics) { + print_byte_code_statistics(); + } + + if (non_method_ticks() > 0) { + tty->cr(); + tty->print_cr("Global summary of %3.2f seconds:", timer.seconds()); + print_ticks("Received ticks", received_ticks, received_ticks); + print_ticks("Received GC ticks", received_gc_ticks, received_ticks); + print_ticks("Compilation", compiler_ticks, received_ticks); + print_ticks("Deoptimization", deopt_ticks, received_ticks); + print_ticks("Other VM operations", vm_operation_ticks, received_ticks); +#ifndef PRODUCT + print_ticks("Blocked ticks", blocked_ticks, received_ticks); + print_ticks("Threads_lock blocks", threads_lock_ticks, received_ticks); + print_ticks("Delivered ticks", delivered_ticks, received_ticks); + print_ticks("All ticks", all_ticks, received_ticks); +#endif + print_ticks("Class loader", class_loader_ticks, received_ticks); + print_ticks("Extra ", extra_ticks, received_ticks); + print_ticks("Interpreter", interpreter_ticks, received_ticks); + print_ticks("Unknown code", unknown_ticks, received_ticks); + } + + PCRecorder::print(); + + if(ProfileVM){ + tty->cr(); + vm_thread_profiler->print("VM Thread"); + } +} + +void IntervalData::print_header(outputStream* st) { + st->print("i/c/n/g"); +} + +void IntervalData::print_data(outputStream* st) { + st->print("%d/%d/%d/%d", interpreted(), compiled(), native(), compiling()); +} + +void FlatProfiler::interval_record_thread(ThreadProfiler* tp) { + IntervalData id = tp->interval_data(); + int total = id.total(); + tp->interval_data_ref()->reset(); + + // Insertion sort the data, if it's relevant. + for (int i = 0; i < interval_print_size; i += 1) { + if (total > interval_data[i].total()) { + for (int j = interval_print_size - 1; j > i; j -= 1) { + interval_data[j] = interval_data[j-1]; + } + interval_data[i] = id; + break; + } + } +} + +void FlatProfiler::interval_print() { + if ((interval_data[0].total() > 0)) { + tty->stamp(); + tty->print("\t"); + IntervalData::print_header(tty); + for (int i = 0; i < interval_print_size; i += 1) { + if (interval_data[i].total() > 0) { + tty->print("\t"); + interval_data[i].print_data(tty); + } + } + tty->cr(); + } +} + +void FlatProfiler::interval_reset() { + for (int i = 0; i < interval_print_size; i += 1) { + interval_data[i].reset(); + } +} + +void ThreadProfiler::oops_do(OopClosure* f) { + if (table == NULL) return; + + for(int index = 0; index < table_size; index++) { + for(ProfilerNode* node = table[index]; node; node = node->next()) + node->oops_do(f); + } +} + +void FlatProfiler::oops_do(OopClosure* f) { + if (thread_profiler != NULL) { + thread_profiler->oops_do(f); + } else { + for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) { + ThreadProfiler* pp = tp->get_thread_profiler(); + if (pp != NULL) { + pp->oops_do(f); + } + } + } +} diff --git a/hotspot/src/share/vm/runtime/fprofiler.hpp b/hotspot/src/share/vm/runtime/fprofiler.hpp new file mode 100644 index 00000000000..5c288d2e576 --- /dev/null +++ b/hotspot/src/share/vm/runtime/fprofiler.hpp @@ -0,0 +1,311 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// a simple flat profiler for Java + + +// Forward declaration of classes defined in this header file +class ThreadProfiler; +class ThreadProfilerMark; +class FlatProfiler; +class IntervalData; + +// Declarations of classes defined only in the implementation. +class ProfilerNode; +class FlatProfilerTask; + +enum TickPosition { + tp_code, + tp_native +}; + +// One of these guys is constructed as we enter interesting regions +// and destructed as we exit the region. While we are in the region +// ticks are allotted to the region. +class ThreadProfilerMark: public StackObj { +public: + // For now, the only thread-specific region is the class loader. + enum Region { noRegion, classLoaderRegion, extraRegion, maxRegion }; + + ThreadProfilerMark(Region) KERNEL_RETURN; + ~ThreadProfilerMark() KERNEL_RETURN; + +private: + ThreadProfiler* _pp; + Region _r; +}; + +#ifndef FPROF_KERNEL + +class IntervalData VALUE_OBJ_CLASS_SPEC { + // Just to keep these things all together +private: + int _interpreted; + int _compiled; + int _native; + int _compiling; +public: + int interpreted() { + return _interpreted; + } + int compiled() { + return _compiled; + } + int native() { + return _native; + } + int compiling() { + return _compiling; + } + int total() { + return (interpreted() + compiled() + native() + compiling()); + } + void inc_interpreted() { + _interpreted += 1; + } + void inc_compiled() { + _compiled += 1; + } + void inc_native() { + _native += 1; + } + void inc_compiling() { + _compiling += 1; + } + void reset() { + _interpreted = 0; + _compiled = 0; + _native = 0; + _compiling = 0; + } + static void print_header(outputStream* st); + void print_data(outputStream* st); +}; +#endif // FPROF_KERNEL + +class ThreadProfiler: public CHeapObj { +public: + ThreadProfiler() KERNEL_RETURN; + ~ThreadProfiler() KERNEL_RETURN; + + // Resets the profiler + void reset() KERNEL_RETURN; + + // Activates the profiler for a certain thread + void engage() KERNEL_RETURN; + + // Deactivates the profiler + void disengage() KERNEL_RETURN; + + // Prints the collected profiling information + void print(const char* thread_name) KERNEL_RETURN; + + // Garbage Collection Support + void oops_do(OopClosure* f) KERNEL_RETURN; + +#ifndef FPROF_KERNEL +private: + // for recording ticks. + friend class ProfilerNode; + char* area_bottom; // preallocated area for pnodes + char* area_top; + char* area_limit; + static int table_size; + ProfilerNode** table; + +private: + void record_interpreted_tick(frame fr, TickPosition where, int* ticks); + void record_compiled_tick (JavaThread* thread, frame fr, TickPosition where); + void interpreted_update(methodOop method, TickPosition where); + void compiled_update (methodOop method, TickPosition where); + void stub_update (methodOop method, const char* name, TickPosition where); + void adapter_update (TickPosition where); + + void runtime_stub_update(const CodeBlob* stub, const char* name, TickPosition where); + void unknown_compiled_update (const CodeBlob* cb, TickPosition where); + + void vm_update (TickPosition where); + void vm_update (const char* name, TickPosition where); + + void record_tick_for_running_frame(JavaThread* thread, frame fr); + void record_tick_for_calling_frame(JavaThread* thread, frame fr); + + void initialize(); + + static int entry(int value); + + +private: + friend class FlatProfiler; + void record_tick(JavaThread* thread); + bool engaged; + // so we can do percentages for this thread, and quick checks for activity + int thread_ticks; + int compiler_ticks; + int interpreter_ticks; + +public: + void inc_thread_ticks() { thread_ticks += 1; } + +private: + friend class ThreadProfilerMark; + // counters for thread-specific regions + bool region_flag[ThreadProfilerMark::maxRegion]; + int class_loader_ticks; + int extra_ticks; + +private: + // other thread-specific regions + int blocked_ticks; + enum UnknownTickSites { + ut_null_method, + ut_vtable_stubs, + ut_running_frame, + ut_calling_frame, + ut_no_pc, + ut_no_last_Java_frame, + ut_unknown_thread_state, + ut_end + }; + int unknown_ticks_array[ut_end]; + int unknown_ticks() { + int result = 0; + for (int ut = 0; ut < ut_end; ut += 1) { + result += unknown_ticks_array[ut]; + } + return result; + } + + elapsedTimer timer; + + // For interval timing +private: + IntervalData _interval_data; + IntervalData interval_data() { + return _interval_data; + } + IntervalData* interval_data_ref() { + return &_interval_data; + } +#endif // FPROF_KERNEL +}; + +class FlatProfiler: AllStatic { +public: + static void reset() KERNEL_RETURN ; + static void engage(JavaThread* mainThread, bool fullProfile) KERNEL_RETURN ; + static void disengage() KERNEL_RETURN ; + static void print(int unused) KERNEL_RETURN ; + static bool is_active() KERNEL_RETURN_(return false;) ; + + // This is NULL if each thread has its own thread profiler, + // else this is the single thread profiler used by all threads. + // In particular it makes a difference during garbage collection, + // where you only want to traverse each thread profiler once. + static ThreadProfiler* get_thread_profiler() KERNEL_RETURN_(return NULL;); + + // Garbage Collection Support + static void oops_do(OopClosure* f) KERNEL_RETURN ; + + // Support for disassembler to inspect the PCRecorder + + // Returns the start address for a given pc + // NULL is returned if the PCRecorder is inactive + static address bucket_start_for(address pc) KERNEL_RETURN_(return NULL;); + + enum { MillisecsPerTick = 10 }; // ms per profiling ticks + + // Returns the number of ticks recorded for the bucket + // pc belongs to. + static int bucket_count_for(address pc) KERNEL_RETURN_(return 0;); + +#ifndef FPROF_KERNEL + + private: + static bool full_profile() { + return full_profile_flag; + } + + friend class ThreadProfiler; + // the following group of ticks cover everything that's not attributed to individual Java methods + static int received_gc_ticks; // ticks during which gc was active + static int vm_operation_ticks; // total ticks in vm_operations other than GC + static int threads_lock_ticks; // the number of times we couldn't get the Threads_lock without blocking + static int blocked_ticks; // ticks when the thread was blocked. + static int class_loader_ticks; // total ticks in class loader + static int extra_ticks; // total ticks an extra temporary measuring + static int compiler_ticks; // total ticks in compilation + static int interpreter_ticks; // ticks in unknown interpreted method + static int deopt_ticks; // ticks in deoptimization + static int unknown_ticks; // ticks that cannot be categorized + static int received_ticks; // ticks that were received by task + static int delivered_ticks; // ticks that were delivered by task + static int non_method_ticks() { + return + ( received_gc_ticks + + vm_operation_ticks + + deopt_ticks + + threads_lock_ticks + + blocked_ticks + + compiler_ticks + + interpreter_ticks + + unknown_ticks ); + } + static elapsedTimer timer; + + // Counts of each of the byte codes + static int* bytecode_ticks; + static int* bytecode_ticks_stub; + static void print_byte_code_statistics(); + + // the ticks below are for continuous profiling (to adjust recompilation, etc.) + static int all_ticks; // total count of ticks received so far + static int all_int_ticks; // ticks in interpreter + static int all_comp_ticks; // ticks in compiled code (+ native) + static bool full_profile_flag; // collecting full profile? + + // to accumulate thread-specific data + // if we aren't profiling individual threads. + static ThreadProfiler* thread_profiler; + static ThreadProfiler* vm_thread_profiler; + + static void allocate_table(); + + // The task that periodically interrupts things. + friend class FlatProfilerTask; + static FlatProfilerTask* task; + static void record_vm_operation(); + static void record_vm_tick(); + static void record_thread_ticks(); + + // For interval analysis + private: + static int interval_ticks_previous; // delivered_ticks from the last interval + static void interval_record_thread(ThreadProfiler* tp); // extract ticks from ThreadProfiler. + static void interval_print(); // print interval data. + static void interval_reset(); // reset interval data. + enum {interval_print_size = 10}; + static IntervalData* interval_data; +#endif // FPROF_KERNEL +}; diff --git a/hotspot/src/share/vm/runtime/frame.cpp b/hotspot/src/share/vm/runtime/frame.cpp new file mode 100644 index 00000000000..efc74a36128 --- /dev/null +++ b/hotspot/src/share/vm/runtime/frame.cpp @@ -0,0 +1,1408 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_frame.cpp.incl" + +RegisterMap::RegisterMap(JavaThread *thread, bool update_map) { + _thread = thread; + _update_map = update_map; + clear(); + debug_only(_update_for_id = NULL;) +#ifndef PRODUCT + for (int i = 0; i < reg_count ; i++ ) _location[i] = NULL; +#endif /* PRODUCT */ +} + +RegisterMap::RegisterMap(const RegisterMap* map) { + assert(map != this, "bad initialization parameter"); + assert(map != NULL, "RegisterMap must be present"); + _thread = map->thread(); + _update_map = map->update_map(); + _include_argument_oops = map->include_argument_oops(); + debug_only(_update_for_id = map->_update_for_id;) + pd_initialize_from(map); + if (update_map()) { + for(int i = 0; i < location_valid_size; i++) { + LocationValidType bits = !update_map() ? 0 : map->_location_valid[i]; + _location_valid[i] = bits; + // for whichever bits are set, pull in the corresponding map->_location + int j = i*location_valid_type_size; + while (bits != 0) { + if ((bits & 1) != 0) { + assert(0 <= j && j < reg_count, "range check"); + _location[j] = map->_location[j]; + } + bits >>= 1; + j += 1; + } + } + } +} + +void RegisterMap::clear() { + set_include_argument_oops(true); + if (_update_map) { + for(int i = 0; i < location_valid_size; i++) { + _location_valid[i] = 0; + } + pd_clear(); + } else { + pd_initialize(); + } +} + +#ifndef PRODUCT + +void RegisterMap::print_on(outputStream* st) const { + st->print_cr("Register map"); + for(int i = 0; i < reg_count; i++) { + + VMReg r = VMRegImpl::as_VMReg(i); + intptr_t* src = (intptr_t*) location(r); + if (src != NULL) { + + r->print(); + tty->print(" [" INTPTR_FORMAT "] = ", src); + if (((uintptr_t)src & (sizeof(*src)-1)) != 0) { + tty->print_cr(""); + } else { + tty->print_cr(INTPTR_FORMAT, *src); + } + } + } +} + +void RegisterMap::print() const { + print_on(tty); +} + +#endif +// This returns the pc that if you were in the debugger you'd see. Not +// the idealized value in the frame object. This undoes the magic conversion +// that happens for deoptimized frames. In addition it makes the value the +// hardware would want to see in the native frame. The only user (at this point) +// is deoptimization. It likely no one else should ever use it. + +address frame::raw_pc() const { + if (is_deoptimized_frame()) { + return ((nmethod*) cb())->deopt_handler_begin() - pc_return_offset; + } else { + return (pc() - pc_return_offset); + } +} + +// Change the pc in a frame object. This does not change the actual pc in +// actual frame. To do that use patch_pc. +// +void frame::set_pc(address newpc ) { +#ifdef ASSERT + if (_cb != NULL && _cb->is_nmethod()) { + assert(!((nmethod*)_cb)->is_deopt_pc(_pc), "invariant violation"); + } +#endif // ASSERT + + // Unsafe to use the is_deoptimzed tester after changing pc + _deopt_state = unknown; + _pc = newpc; + _cb = CodeCache::find_blob_unsafe(_pc); + +} + +// type testers +bool frame::is_deoptimized_frame() const { + assert(_deopt_state != unknown, "not answerable"); + return _deopt_state == is_deoptimized; +} + +bool frame::is_native_frame() const { + return (_cb != NULL && + _cb->is_nmethod() && + ((nmethod*)_cb)->is_native_method()); +} + +bool frame::is_java_frame() const { + if (is_interpreted_frame()) return true; + if (is_compiled_frame()) return true; + return false; +} + + +bool frame::is_compiled_frame() const { + if (_cb != NULL && + _cb->is_nmethod() && + ((nmethod*)_cb)->is_java_method()) { + return true; + } + return false; +} + + +bool frame::is_runtime_frame() const { + return (_cb != NULL && _cb->is_runtime_stub()); +} + +bool frame::is_safepoint_blob_frame() const { + return (_cb != NULL && _cb->is_safepoint_stub()); +} + +// testers + +bool frame::is_first_java_frame() const { + RegisterMap map(JavaThread::current(), false); // No update + frame s; + for (s = sender(&map); !(s.is_java_frame() || s.is_first_frame()); s = s.sender(&map)); + return s.is_first_frame(); +} + + +bool frame::entry_frame_is_first() const { + return entry_frame_call_wrapper()->anchor()->last_Java_sp() == NULL; +} + + +bool frame::should_be_deoptimized() const { + if (_deopt_state == is_deoptimized || + !is_compiled_frame() ) return false; + assert(_cb != NULL && _cb->is_nmethod(), "must be an nmethod"); + nmethod* nm = (nmethod *)_cb; + if (TraceDependencies) { + tty->print("checking (%s) ", nm->is_marked_for_deoptimization() ? "true" : "false"); + nm->print_value_on(tty); + tty->cr(); + } + + if( !nm->is_marked_for_deoptimization() ) + return false; + + // If at the return point, then the frame has already been popped, and + // only the return needs to be executed. Don't deoptimize here. + return !nm->is_at_poll_return(pc()); +} + +bool frame::can_be_deoptimized() const { + if (!is_compiled_frame()) return false; + nmethod* nm = (nmethod*)_cb; + + if( !nm->can_be_deoptimized() ) + return false; + + return !nm->is_at_poll_return(pc()); +} + +void frame::deoptimize(JavaThread* thread, bool thread_is_known_safe) { +// Schedule deoptimization of an nmethod activation with this frame. + + // Store the original pc before an patch (or request to self-deopt) + // in the published location of the frame. + + assert(_cb != NULL && _cb->is_nmethod(), "must be"); + nmethod* nm = (nmethod*)_cb; + + // This is a fix for register window patching race + if (NeedsDeoptSuspend && !thread_is_known_safe) { + + // It is possible especially with DeoptimizeALot/DeoptimizeRandom that + // we could see the frame again and ask for it to be deoptimized since + // it might move for a long time. That is harmless and we just ignore it. + if (id() == thread->must_deopt_id()) { + assert(thread->is_deopt_suspend(), "lost suspension"); + return; + } + + // We are at a safepoint so the target thread can only be + // in 4 states: + // blocked - no problem + // blocked_trans - no problem (i.e. could have woken up from blocked + // during a safepoint). + // native - register window pc patching race + // native_trans - momentary state + // + // We could just wait out a thread in native_trans to block. + // Then we'd have all the issues that the safepoint code has as to + // whether to spin or block. It isn't worth it. Just treat it like + // native and be done with it. + // + JavaThreadState state = thread->thread_state(); + if (state == _thread_in_native || state == _thread_in_native_trans) { + // Since we are at a safepoint the target thread will stop itself + // before it can return to java as long as we remain at the safepoint. + // Therefore we can put an additional request for the thread to stop + // no matter what no (like a suspend). This will cause the thread + // to notice it needs to do the deopt on its own once it leaves native. + // + // The only reason we must do this is because on machine with register + // windows we have a race with patching the return address and the + // window coming live as the thread returns to the Java code (but still + // in native mode) and then blocks. It is only this top most frame + // that is at risk. So in truth we could add an additional check to + // see if this frame is one that is at risk. + RegisterMap map(thread, false); + frame at_risk = thread->last_frame().sender(&map); + if (id() == at_risk.id()) { + thread->set_must_deopt_id(id()); + thread->set_deopt_suspend(); + return; + } + } + } // NeedsDeoptSuspend + + + address deopt = nm->deopt_handler_begin(); + // Save the original pc before we patch in the new one + nm->set_original_pc(this, pc()); + patch_pc(thread, deopt); +#ifdef ASSERT + { + RegisterMap map(thread, false); + frame check = thread->last_frame(); + while (id() != check.id()) { + check = check.sender(&map); + } + assert(check.is_deoptimized_frame(), "missed deopt"); + } +#endif // ASSERT +} + +frame frame::java_sender() const { + RegisterMap map(JavaThread::current(), false); + frame s; + for (s = sender(&map); !(s.is_java_frame() || s.is_first_frame()); s = s.sender(&map)) ; + guarantee(s.is_java_frame(), "tried to get caller of first java frame"); + return s; +} + +frame frame::real_sender(RegisterMap* map) const { + frame result = sender(map); + while (result.is_runtime_frame()) { + result = result.sender(map); + } + return result; +} + +// Note: called by profiler - NOT for current thread +frame frame::profile_find_Java_sender_frame(JavaThread *thread) { +// If we don't recognize this frame, walk back up the stack until we do + RegisterMap map(thread, false); + frame first_java_frame = frame(); + + // Find the first Java frame on the stack starting with input frame + if (is_java_frame()) { + // top frame is compiled frame or deoptimized frame + first_java_frame = *this; + } else if (safe_for_sender(thread)) { + for (frame sender_frame = sender(&map); + sender_frame.safe_for_sender(thread) && !sender_frame.is_first_frame(); + sender_frame = sender_frame.sender(&map)) { + if (sender_frame.is_java_frame()) { + first_java_frame = sender_frame; + break; + } + } + } + return first_java_frame; +} + +// Interpreter frames + + +void frame::interpreter_frame_set_locals(intptr_t* locs) { + assert(is_interpreted_frame(), "Not an interpreted frame"); + *interpreter_frame_locals_addr() = locs; +} + +methodOop frame::interpreter_frame_method() const { + assert(is_interpreted_frame(), "interpreted frame expected"); + methodOop m = *interpreter_frame_method_addr(); + assert(m->is_perm(), "bad methodOop in interpreter frame"); + assert(m->is_method(), "not a methodOop"); + return m; +} + +void frame::interpreter_frame_set_method(methodOop method) { + assert(is_interpreted_frame(), "interpreted frame expected"); + *interpreter_frame_method_addr() = method; +} + +void frame::interpreter_frame_set_bcx(intptr_t bcx) { + assert(is_interpreted_frame(), "Not an interpreted frame"); + if (ProfileInterpreter) { + bool formerly_bci = is_bci(interpreter_frame_bcx()); + bool is_now_bci = is_bci(bcx); + *interpreter_frame_bcx_addr() = bcx; + + intptr_t mdx = interpreter_frame_mdx(); + + if (mdx != 0) { + if (formerly_bci) { + if (!is_now_bci) { + // The bcx was just converted from bci to bcp. + // Convert the mdx in parallel. + methodDataOop mdo = interpreter_frame_method()->method_data(); + assert(mdo != NULL, ""); + int mdi = mdx - 1; // We distinguish valid mdi from zero by adding one. + address mdp = mdo->di_to_dp(mdi); + interpreter_frame_set_mdx((intptr_t)mdp); + } + } else { + if (is_now_bci) { + // The bcx was just converted from bcp to bci. + // Convert the mdx in parallel. + methodDataOop mdo = interpreter_frame_method()->method_data(); + assert(mdo != NULL, ""); + int mdi = mdo->dp_to_di((address)mdx); + interpreter_frame_set_mdx((intptr_t)mdi + 1); // distinguish valid from 0. + } + } + } + } else { + *interpreter_frame_bcx_addr() = bcx; + } +} + +jint frame::interpreter_frame_bci() const { + assert(is_interpreted_frame(), "interpreted frame expected"); + intptr_t bcx = interpreter_frame_bcx(); + return is_bci(bcx) ? bcx : interpreter_frame_method()->bci_from((address)bcx); +} + +void frame::interpreter_frame_set_bci(jint bci) { + assert(is_interpreted_frame(), "interpreted frame expected"); + assert(!is_bci(interpreter_frame_bcx()), "should not set bci during GC"); + interpreter_frame_set_bcx((intptr_t)interpreter_frame_method()->bcp_from(bci)); +} + +address frame::interpreter_frame_bcp() const { + assert(is_interpreted_frame(), "interpreted frame expected"); + intptr_t bcx = interpreter_frame_bcx(); + return is_bci(bcx) ? interpreter_frame_method()->bcp_from(bcx) : (address)bcx; +} + +void frame::interpreter_frame_set_bcp(address bcp) { + assert(is_interpreted_frame(), "interpreted frame expected"); + assert(!is_bci(interpreter_frame_bcx()), "should not set bcp during GC"); + interpreter_frame_set_bcx((intptr_t)bcp); +} + +void frame::interpreter_frame_set_mdx(intptr_t mdx) { + assert(is_interpreted_frame(), "Not an interpreted frame"); + assert(ProfileInterpreter, "must be profiling interpreter"); + *interpreter_frame_mdx_addr() = mdx; +} + +address frame::interpreter_frame_mdp() const { + assert(ProfileInterpreter, "must be profiling interpreter"); + assert(is_interpreted_frame(), "interpreted frame expected"); + intptr_t bcx = interpreter_frame_bcx(); + intptr_t mdx = interpreter_frame_mdx(); + + assert(!is_bci(bcx), "should not access mdp during GC"); + return (address)mdx; +} + +void frame::interpreter_frame_set_mdp(address mdp) { + assert(is_interpreted_frame(), "interpreted frame expected"); + if (mdp == NULL) { + // Always allow the mdp to be cleared. + interpreter_frame_set_mdx((intptr_t)mdp); + } + intptr_t bcx = interpreter_frame_bcx(); + assert(!is_bci(bcx), "should not set mdp during GC"); + interpreter_frame_set_mdx((intptr_t)mdp); +} + +BasicObjectLock* frame::next_monitor_in_interpreter_frame(BasicObjectLock* current) const { + assert(is_interpreted_frame(), "Not an interpreted frame"); +#ifdef ASSERT + interpreter_frame_verify_monitor(current); +#endif + BasicObjectLock* next = (BasicObjectLock*) (((intptr_t*) current) + interpreter_frame_monitor_size()); + return next; +} + +BasicObjectLock* frame::previous_monitor_in_interpreter_frame(BasicObjectLock* current) const { + assert(is_interpreted_frame(), "Not an interpreted frame"); +#ifdef ASSERT +// // This verification needs to be checked before being enabled +// interpreter_frame_verify_monitor(current); +#endif + BasicObjectLock* previous = (BasicObjectLock*) (((intptr_t*) current) - interpreter_frame_monitor_size()); + return previous; +} + +// Interpreter locals and expression stack locations. + +intptr_t* frame::interpreter_frame_local_at(int index) const { + const int n = Interpreter::local_offset_in_bytes(index)/wordSize; + return &((*interpreter_frame_locals_addr())[n]); +} + +frame::Tag frame::interpreter_frame_local_tag(int index) const { + const int n = Interpreter::local_tag_offset_in_bytes(index)/wordSize; + return (Tag)(*interpreter_frame_locals_addr()) [n]; +} + +void frame::interpreter_frame_set_local_tag(int index, Tag tag) const { + const int n = Interpreter::local_tag_offset_in_bytes(index)/wordSize; + (*interpreter_frame_locals_addr())[n] = (intptr_t)tag; +} + +intptr_t* frame::interpreter_frame_expression_stack_at(jint offset) const { + const int i = offset * interpreter_frame_expression_stack_direction(); + const int n = ((i * Interpreter::stackElementSize()) + + Interpreter::value_offset_in_bytes())/wordSize; + return &(interpreter_frame_expression_stack()[n]); +} + +frame::Tag frame::interpreter_frame_expression_stack_tag(jint offset) const { + const int i = offset * interpreter_frame_expression_stack_direction(); + const int n = ((i * Interpreter::stackElementSize()) + + Interpreter::tag_offset_in_bytes())/wordSize; + return (Tag)(interpreter_frame_expression_stack()[n]); +} + +void frame::interpreter_frame_set_expression_stack_tag(jint offset, + Tag tag) const { + const int i = offset * interpreter_frame_expression_stack_direction(); + const int n = ((i * Interpreter::stackElementSize()) + + Interpreter::tag_offset_in_bytes())/wordSize; + interpreter_frame_expression_stack()[n] = (intptr_t)tag; +} + +jint frame::interpreter_frame_expression_stack_size() const { + // Number of elements on the interpreter expression stack + // Callers should span by stackElementWords + int element_size = Interpreter::stackElementWords(); + if (frame::interpreter_frame_expression_stack_direction() < 0) { + return (interpreter_frame_expression_stack() - + interpreter_frame_tos_address() + 1)/element_size; + } else { + return (interpreter_frame_tos_address() - + interpreter_frame_expression_stack() + 1)/element_size; + } +} + + +// (frame::interpreter_frame_sender_sp accessor is in frame_.cpp) + +const char* frame::print_name() const { + if (is_native_frame()) return "Native"; + if (is_interpreted_frame()) return "Interpreted"; + if (is_compiled_frame()) { + if (is_deoptimized_frame()) return "Deoptimized"; + return "Compiled"; + } + if (sp() == NULL) return "Empty"; + return "C"; +} + +void frame::print_value_on(outputStream* st, JavaThread *thread) const { + NOT_PRODUCT(address begin = pc()-40;) + NOT_PRODUCT(address end = NULL;) + + st->print("%s frame (sp=" INTPTR_FORMAT " unextended sp=" INTPTR_FORMAT, print_name(), sp(), unextended_sp()); + if (sp() != NULL) + st->print(", fp=" INTPTR_FORMAT ", pc=" INTPTR_FORMAT, fp(), pc()); + + if (StubRoutines::contains(pc())) { + st->print_cr(")"); + st->print("("); + StubCodeDesc* desc = StubCodeDesc::desc_for(pc()); + st->print("~Stub::%s", desc->name()); + NOT_PRODUCT(begin = desc->begin(); end = desc->end();) + } else if (Interpreter::contains(pc())) { + st->print_cr(")"); + st->print("("); + InterpreterCodelet* desc = Interpreter::codelet_containing(pc()); + if (desc != NULL) { + st->print("~"); + desc->print(); + NOT_PRODUCT(begin = desc->code_begin(); end = desc->code_end();) + } else { + st->print("~interpreter"); + } + } + st->print_cr(")"); + + if (_cb != NULL) { + st->print(" "); + _cb->print_value_on(st); + st->cr(); +#ifndef PRODUCT + if (end == NULL) { + begin = _cb->instructions_begin(); + end = _cb->instructions_end(); + } +#endif + } + NOT_PRODUCT(if (WizardMode && Verbose) Disassembler::decode(begin, end);) +} + + +void frame::print_on(outputStream* st) const { + print_value_on(st,NULL); + if (is_interpreted_frame()) { + interpreter_frame_print_on(st); + } +} + + +void frame::interpreter_frame_print_on(outputStream* st) const { +#ifndef PRODUCT + assert(is_interpreted_frame(), "Not an interpreted frame"); + jint i; + for (i = 0; i < interpreter_frame_method()->max_locals(); i++ ) { + intptr_t x = *interpreter_frame_local_at(i); + st->print(" - local [" INTPTR_FORMAT "]", x); + if (TaggedStackInterpreter) { + Tag x = interpreter_frame_local_tag(i); + st->print(" - local tag [" INTPTR_FORMAT "]", x); + } + st->fill_to(23); + st->print_cr("; #%d", i); + } + for (i = interpreter_frame_expression_stack_size() - 1; i >= 0; --i ) { + intptr_t x = *interpreter_frame_expression_stack_at(i); + st->print(" - stack [" INTPTR_FORMAT "]", x); + if (TaggedStackInterpreter) { + Tag x = interpreter_frame_expression_stack_tag(i); + st->print(" - stack tag [" INTPTR_FORMAT "]", x); + } + st->fill_to(23); + st->print_cr("; #%d", i); + } + // locks for synchronization + for (BasicObjectLock* current = interpreter_frame_monitor_end(); + current < interpreter_frame_monitor_begin(); + current = next_monitor_in_interpreter_frame(current)) { + st->print_cr(" [ - obj "); + current->obj()->print_value_on(st); + st->cr(); + st->print_cr(" - lock "); + current->lock()->print_on(st); + st->cr(); + } + // monitor + st->print_cr(" - monitor[" INTPTR_FORMAT "]", interpreter_frame_monitor_begin()); + // bcp + st->print(" - bcp [" INTPTR_FORMAT "]", interpreter_frame_bcp()); + st->fill_to(23); + st->print_cr("; @%d", interpreter_frame_bci()); + // locals + st->print_cr(" - locals [" INTPTR_FORMAT "]", interpreter_frame_local_at(0)); + // method + st->print(" - method [" INTPTR_FORMAT "]", (address)interpreter_frame_method()); + st->fill_to(23); + st->print("; "); + interpreter_frame_method()->print_name(st); + st->cr(); +#endif +} + +// Return whether the frame is in the VM or os indicating a Hotspot problem. +// Otherwise, it's likely a bug in the native library that the Java code calls, +// hopefully indicating where to submit bugs. +static void print_C_frame(outputStream* st, char* buf, int buflen, address pc) { + // C/C++ frame + bool in_vm = os::address_is_in_vm(pc); + st->print(in_vm ? "V" : "C"); + + int offset; + bool found; + + // libname + found = os::dll_address_to_library_name(pc, buf, buflen, &offset); + if (found) { + // skip directory names + const char *p1, *p2; + p1 = buf; + int len = (int)strlen(os::file_separator()); + while ((p2 = strstr(p1, os::file_separator())) != NULL) p1 = p2 + len; + st->print(" [%s+0x%x]", p1, offset); + } else { + st->print(" " PTR_FORMAT, pc); + } + + // function name - os::dll_address_to_function_name() may return confusing + // names if pc is within jvm.dll or libjvm.so, because JVM only has + // JVM_xxxx and a few other symbols in the dynamic symbol table. Do this + // only for native libraries. + if (!in_vm) { + found = os::dll_address_to_function_name(pc, buf, buflen, &offset); + + if (found) { + st->print(" %s+0x%x", buf, offset); + } + } +} + +// frame::print_on_error() is called by fatal error handler. Notice that we may +// crash inside this function if stack frame is corrupted. The fatal error +// handler can catch and handle the crash. Here we assume the frame is valid. +// +// First letter indicates type of the frame: +// J: Java frame (compiled) +// j: Java frame (interpreted) +// V: VM frame (C/C++) +// v: Other frames running VM generated code (e.g. stubs, adapters, etc.) +// C: C/C++ frame +// +// We don't need detailed frame type as that in frame::print_name(). "C" +// suggests the problem is in user lib; everything else is likely a VM bug. + +void frame::print_on_error(outputStream* st, char* buf, int buflen, bool verbose) const { + if (_cb != NULL) { + if (Interpreter::contains(pc())) { + methodOop m = this->interpreter_frame_method(); + if (m != NULL) { + m->name_and_sig_as_C_string(buf, buflen); + st->print("j %s", buf); + st->print("+%d", this->interpreter_frame_bci()); + } else { + st->print("j " PTR_FORMAT, pc()); + } + } else if (StubRoutines::contains(pc())) { + StubCodeDesc* desc = StubCodeDesc::desc_for(pc()); + if (desc != NULL) { + st->print("v ~StubRoutines::%s", desc->name()); + } else { + st->print("v ~StubRoutines::" PTR_FORMAT, pc()); + } + } else if (_cb->is_buffer_blob()) { + st->print("v ~BufferBlob::%s", ((BufferBlob *)_cb)->name()); + } else if (_cb->is_nmethod()) { + methodOop m = ((nmethod *)_cb)->method(); + if (m != NULL) { + m->name_and_sig_as_C_string(buf, buflen); + st->print("J %s", buf); + } else { + st->print("J " PTR_FORMAT, pc()); + } + } else if (_cb->is_runtime_stub()) { + st->print("v ~RuntimeStub::%s", ((RuntimeStub *)_cb)->name()); + } else if (_cb->is_deoptimization_stub()) { + st->print("v ~DeoptimizationBlob"); + } else if (_cb->is_exception_stub()) { + st->print("v ~ExceptionBlob"); + } else if (_cb->is_safepoint_stub()) { + st->print("v ~SafepointBlob"); + } else { + st->print("v blob " PTR_FORMAT, pc()); + } + } else { + print_C_frame(st, buf, buflen, pc()); + } +} + + +/* + The interpreter_frame_expression_stack_at method in the case of SPARC needs the + max_stack value of the method in order to compute the expression stack address. + It uses the methodOop in order to get the max_stack value but during GC this + methodOop value saved on the frame is changed by reverse_and_push and hence cannot + be used. So we save the max_stack value in the FrameClosure object and pass it + down to the interpreter_frame_expression_stack_at method +*/ +class InterpreterFrameClosure : public OffsetClosure { + private: + frame* _fr; + OopClosure* _f; + int _max_locals; + int _max_stack; + + public: + InterpreterFrameClosure(frame* fr, int max_locals, int max_stack, + OopClosure* f) { + _fr = fr; + _max_locals = max_locals; + _max_stack = max_stack; + _f = f; + } + + void offset_do(int offset) { + oop* addr; + if (offset < _max_locals) { + addr = (oop*) _fr->interpreter_frame_local_at(offset); + assert((intptr_t*)addr >= _fr->sp(), "must be inside the frame"); + _f->do_oop(addr); + } else { + addr = (oop*) _fr->interpreter_frame_expression_stack_at((offset - _max_locals)); + // In case of exceptions, the expression stack is invalid and the esp will be reset to express + // this condition. Therefore, we call f only if addr is 'inside' the stack (i.e., addr >= esp for Intel). + bool in_stack; + if (frame::interpreter_frame_expression_stack_direction() > 0) { + in_stack = (intptr_t*)addr <= _fr->interpreter_frame_tos_address(); + } else { + in_stack = (intptr_t*)addr >= _fr->interpreter_frame_tos_address(); + } + if (in_stack) { + _f->do_oop(addr); + } + } + } + + int max_locals() { return _max_locals; } + frame* fr() { return _fr; } +}; + + +class InterpretedArgumentOopFinder: public SignatureInfo { + private: + OopClosure* _f; // Closure to invoke + int _offset; // TOS-relative offset, decremented with each argument + bool _is_static; // true if the callee is a static method + frame* _fr; + + void set(int size, BasicType type) { + _offset -= size; + if (type == T_OBJECT || type == T_ARRAY) oop_offset_do(); + } + + void oop_offset_do() { + oop* addr; + addr = (oop*)_fr->interpreter_frame_tos_at(_offset); + _f->do_oop(addr); + } + + public: + InterpretedArgumentOopFinder(symbolHandle signature, bool is_static, frame* fr, OopClosure* f) : SignatureInfo(signature) { + // compute size of arguments + int args_size = ArgumentSizeComputer(signature).size() + (is_static ? 0 : 1); + assert(!fr->is_interpreted_frame() || + args_size <= fr->interpreter_frame_expression_stack_size(), + "args cannot be on stack anymore"); + // initialize InterpretedArgumentOopFinder + _f = f; + _fr = fr; + _offset = args_size; + _is_static = is_static; + } + + void oops_do() { + if (!_is_static) { + --_offset; + oop_offset_do(); + } + iterate_parameters(); + } +}; + + +// Entry frame has following form (n arguments) +// +-----------+ +// sp -> | last arg | +// +-----------+ +// : ::: : +// +-----------+ +// (sp+n)->| first arg| +// +-----------+ + + + +// visits and GC's all the arguments in entry frame +class EntryFrameOopFinder: public SignatureInfo { + private: + bool _is_static; + int _offset; + frame* _fr; + OopClosure* _f; + + void set(int size, BasicType type) { + assert (_offset >= 0, "illegal offset"); + if (type == T_OBJECT || type == T_ARRAY) oop_at_offset_do(_offset); + _offset -= size; + } + + void oop_at_offset_do(int offset) { + assert (offset >= 0, "illegal offset") + oop* addr = (oop*) _fr->entry_frame_argument_at(offset); + _f->do_oop(addr); + } + + public: + EntryFrameOopFinder(frame* frame, symbolHandle signature, bool is_static) : SignatureInfo(signature) { + _f = NULL; // will be set later + _fr = frame; + _is_static = is_static; + _offset = ArgumentSizeComputer(signature).size() - 1; // last parameter is at index 0 + } + + void arguments_do(OopClosure* f) { + _f = f; + if (!_is_static) oop_at_offset_do(_offset+1); // do the receiver + iterate_parameters(); + } + +}; + +oop* frame::interpreter_callee_receiver_addr(symbolHandle signature) { + ArgumentSizeComputer asc(signature); + int size = asc.size(); + return (oop *)interpreter_frame_tos_at(size); +} + + +void frame::oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache) { + assert(is_interpreted_frame(), "Not an interpreted frame"); + assert(map != NULL, "map must be set"); + Thread *thread = Thread::current(); + methodHandle m (thread, interpreter_frame_method()); + jint bci = interpreter_frame_bci(); + + assert(Universe::heap()->is_in(m()), "must be valid oop"); + assert(m->is_method(), "checking frame value"); + assert((m->is_native() && bci == 0) || (!m->is_native() && bci >= 0 && bci < m->code_size()), "invalid bci value"); + + // Handle the monitor elements in the activation + for ( + BasicObjectLock* current = interpreter_frame_monitor_end(); + current < interpreter_frame_monitor_begin(); + current = next_monitor_in_interpreter_frame(current) + ) { +#ifdef ASSERT + interpreter_frame_verify_monitor(current); +#endif + current->oops_do(f); + } + + // process fixed part + f->do_oop((oop*)interpreter_frame_method_addr()); + f->do_oop((oop*)interpreter_frame_cache_addr()); + + // Hmm what about the mdp? +#ifdef CC_INTERP + // Interpreter frame in the midst of a call have a methodOop within the + // object. + interpreterState istate = get_interpreterState(); + if (istate->msg() == BytecodeInterpreter::call_method) { + f->do_oop((oop*)&istate->_result._to_call._callee); + } + +#endif /* CC_INTERP */ + + if (m->is_native()) { +#ifdef CC_INTERP + f->do_oop((oop*)&istate->_oop_temp); +#else + f->do_oop((oop*)( fp() + interpreter_frame_oop_temp_offset )); +#endif /* CC_INTERP */ + } + + int max_locals = m->is_native() ? m->size_of_parameters() : m->max_locals(); + + symbolHandle signature; + bool is_static = false; + + // Process a callee's arguments if we are at a call site + // (i.e., if we are at an invoke bytecode) + // This is used sometimes for calling into the VM, not for another + // interpreted or compiled frame. + if (!m->is_native()) { + Bytecode_invoke *call = Bytecode_invoke_at_check(m, bci); + if (call != NULL) { + signature = symbolHandle(thread, call->signature()); + is_static = call->is_invokestatic(); + if (map->include_argument_oops() && + interpreter_frame_expression_stack_size() > 0) { + ResourceMark rm(thread); // is this right ??? + // we are at a call site & the expression stack is not empty + // => process callee's arguments + // + // Note: The expression stack can be empty if an exception + // occured during method resolution/execution. In all + // cases we empty the expression stack completely be- + // fore handling the exception (the exception handling + // code in the interpreter calls a blocking runtime + // routine which can cause this code to be executed). + // (was bug gri 7/27/98) + oops_interpreted_arguments_do(signature, is_static, f); + } + } + } + + if (TaggedStackInterpreter) { + // process locals & expression stack + InterpreterOopMap *mask = NULL; +#ifdef ASSERT + InterpreterOopMap oopmap_mask; + OopMapCache::compute_one_oop_map(m, bci, &oopmap_mask); + mask = &oopmap_mask; +#endif // ASSERT + oops_interpreted_locals_do(f, max_locals, mask); + oops_interpreted_expressions_do(f, signature, is_static, + m->max_stack(), + max_locals, mask); + } else { + InterpreterFrameClosure blk(this, max_locals, m->max_stack(), f); + + // process locals & expression stack + InterpreterOopMap mask; + if (query_oop_map_cache) { + m->mask_for(bci, &mask); + } else { + OopMapCache::compute_one_oop_map(m, bci, &mask); + } + mask.iterate_oop(&blk); + } +} + + +void frame::oops_interpreted_locals_do(OopClosure *f, + int max_locals, + InterpreterOopMap *mask) { + // Process locals then interpreter expression stack + for (int i = 0; i < max_locals; i++ ) { + Tag tag = interpreter_frame_local_tag(i); + if (tag == TagReference) { + oop* addr = (oop*) interpreter_frame_local_at(i); + assert((intptr_t*)addr >= sp(), "must be inside the frame"); + f->do_oop(addr); +#ifdef ASSERT + } else { + assert(tag == TagValue, "bad tag value for locals"); + oop* p = (oop*) interpreter_frame_local_at(i); + // Not always true - too bad. May have dead oops without tags in locals. + // assert(*p == NULL || !(*p)->is_oop(), "oop not tagged on interpreter locals"); + assert(*p == NULL || !mask->is_oop(i), "local oop map mismatch"); +#endif // ASSERT + } + } +} + +void frame::oops_interpreted_expressions_do(OopClosure *f, + symbolHandle signature, + bool is_static, + int max_stack, + int max_locals, + InterpreterOopMap *mask) { + // There is no stack no matter what the esp is pointing to (native methods + // might look like expression stack is nonempty). + if (max_stack == 0) return; + + // Point the top of the expression stack above arguments to a call so + // arguments aren't gc'ed as both stack values for callee and callee + // arguments in callee's locals. + int args_size = 0; + if (!signature.is_null()) { + args_size = ArgumentSizeComputer(signature).size() + (is_static ? 0 : 1); + } + + intptr_t *tos_addr = interpreter_frame_tos_at(args_size); + assert(args_size != 0 || tos_addr == interpreter_frame_tos_address(), "these are same"); + intptr_t *frst_expr = interpreter_frame_expression_stack_at(0); + // In case of exceptions, the expression stack is invalid and the esp + // will be reset to express this condition. Therefore, we call f only + // if addr is 'inside' the stack (i.e., addr >= esp for Intel). + bool in_stack; + if (interpreter_frame_expression_stack_direction() > 0) { + in_stack = (intptr_t*)frst_expr <= tos_addr; + } else { + in_stack = (intptr_t*)frst_expr >= tos_addr; + } + if (!in_stack) return; + + jint stack_size = interpreter_frame_expression_stack_size() - args_size; + for (int j = 0; j < stack_size; j++) { + Tag tag = interpreter_frame_expression_stack_tag(j); + if (tag == TagReference) { + oop *addr = (oop*) interpreter_frame_expression_stack_at(j); + f->do_oop(addr); +#ifdef ASSERT + } else { + assert(tag == TagValue, "bad tag value for stack element"); + oop *p = (oop*) interpreter_frame_expression_stack_at((j)); + assert(*p == NULL || !mask->is_oop(j+max_locals), "stack oop map mismatch"); +#endif // ASSERT + } + } +} + +void frame::oops_interpreted_arguments_do(symbolHandle signature, bool is_static, OopClosure* f) { + InterpretedArgumentOopFinder finder(signature, is_static, this, f); + finder.oops_do(); +} + +void frame::oops_code_blob_do(OopClosure* f, const RegisterMap* reg_map) { + assert(_cb != NULL, "sanity check"); + if (_cb->oop_maps() != NULL) { + OopMapSet::oops_do(this, reg_map, f); + + // Preserve potential arguments for a callee. We handle this by dispatching + // on the codeblob. For c2i, we do + if (reg_map->include_argument_oops()) { + _cb->preserve_callee_argument_oops(*this, reg_map, f); + } + } + // In cases where perm gen is collected, GC will want to mark + // oops referenced from nmethods active on thread stacks so as to + // prevent them from being collected. However, this visit should be + // restricted to certain phases of the collection only. The + // closure answers whether it wants nmethods to be traced. + // (All CodeBlob subtypes other than NMethod currently have + // an empty oops_do() method. + if (f->do_nmethods()) { + _cb->oops_do(f); + } +} + +void frame::nmethods_code_blob_do() { + assert(_cb != NULL, "sanity check"); + + // If we see an activation belonging to a non_entrant nmethod, we mark it. + if (_cb->is_nmethod() && ((nmethod *)_cb)->is_not_entrant()) { + ((nmethod*)_cb)->mark_as_seen_on_stack(); + } +} + +class CompiledArgumentOopFinder: public SignatureInfo { + protected: + OopClosure* _f; + int _offset; // the current offset, incremented with each argument + bool _is_static; // true if the callee is a static method + frame _fr; + RegisterMap* _reg_map; + int _arg_size; + VMRegPair* _regs; // VMReg list of arguments + + void set(int size, BasicType type) { + if (type == T_OBJECT || type == T_ARRAY) handle_oop_offset(); + _offset += size; + } + + virtual void handle_oop_offset() { + // Extract low order register number from register array. + // In LP64-land, the high-order bits are valid but unhelpful. + VMReg reg = _regs[_offset].first(); + oop *loc = _fr.oopmapreg_to_location(reg, _reg_map); + _f->do_oop(loc); + } + + public: + CompiledArgumentOopFinder(symbolHandle signature, bool is_static, OopClosure* f, frame fr, const RegisterMap* reg_map) + : SignatureInfo(signature) { + + // initialize CompiledArgumentOopFinder + _f = f; + _offset = 0; + _is_static = is_static; + _fr = fr; + _reg_map = (RegisterMap*)reg_map; + _arg_size = ArgumentSizeComputer(signature).size() + (is_static ? 0 : 1); + + int arg_size; + _regs = SharedRuntime::find_callee_arguments(signature(), is_static, &arg_size); + assert(arg_size == _arg_size, "wrong arg size"); + } + + void oops_do() { + if (!_is_static) { + handle_oop_offset(); + _offset++; + } + iterate_parameters(); + } +}; + +void frame::oops_compiled_arguments_do(symbolHandle signature, bool is_static, const RegisterMap* reg_map, OopClosure* f) { + ResourceMark rm; + CompiledArgumentOopFinder finder(signature, is_static, f, *this, reg_map); + finder.oops_do(); +} + + +// Get receiver out of callers frame, i.e. find parameter 0 in callers +// frame. Consult ADLC for where parameter 0 is to be found. Then +// check local reg_map for it being a callee-save register or argument +// register, both of which are saved in the local frame. If not found +// there, it must be an in-stack argument of the caller. +// Note: caller.sp() points to callee-arguments +oop frame::retrieve_receiver(RegisterMap* reg_map) { + frame caller = *this; + + // First consult the ADLC on where it puts parameter 0 for this signature. + VMReg reg = SharedRuntime::name_for_receiver(); + oop r = *caller.oopmapreg_to_location(reg, reg_map); + assert( Universe::heap()->is_in_or_null(r), "bad receiver" ); + return r; +} + + +oop* frame::oopmapreg_to_location(VMReg reg, const RegisterMap* reg_map) const { + if(reg->is_reg()) { + // If it is passed in a register, it got spilled in the stub frame. + return (oop *)reg_map->location(reg); + } else { + int sp_offset_in_stack_slots = reg->reg2stack(); + int sp_offset = sp_offset_in_stack_slots >> (LogBytesPerWord - LogBytesPerInt); + return (oop *)&unextended_sp()[sp_offset]; + } +} + +BasicLock* frame::compiled_synchronized_native_monitor(nmethod* nm) { + if (nm == NULL) { + assert(_cb != NULL && _cb->is_nmethod() && + nm->method()->is_native() && + nm->method()->is_synchronized(), + "should not call this otherwise"); + nm = (nmethod*) _cb; + } + int byte_offset = in_bytes(nm->compiled_synchronized_native_basic_lock_sp_offset()); + assert(byte_offset >= 0, "should not see invalid offset"); + return (BasicLock*) &sp()[byte_offset / wordSize]; +} + +oop frame::compiled_synchronized_native_monitor_owner(nmethod* nm) { + if (nm == NULL) { + assert(_cb != NULL && _cb->is_nmethod() && + nm->method()->is_native() && + nm->method()->is_synchronized(), + "should not call this otherwise"); + nm = (nmethod*) _cb; + } + int byte_offset = in_bytes(nm->compiled_synchronized_native_basic_lock_owner_sp_offset()); + assert(byte_offset >= 0, "should not see invalid offset"); + oop owner = ((oop*) sp())[byte_offset / wordSize]; + assert( Universe::heap()->is_in(owner), "bad receiver" ); + return owner; +} + +void frame::oops_entry_do(OopClosure* f, const RegisterMap* map) { + assert(map != NULL, "map must be set"); + if (map->include_argument_oops()) { + // must collect argument oops, as nobody else is doing it + Thread *thread = Thread::current(); + methodHandle m (thread, entry_frame_call_wrapper()->callee_method()); + symbolHandle signature (thread, m->signature()); + EntryFrameOopFinder finder(this, signature, m->is_static()); + finder.arguments_do(f); + } + // Traverse the Handle Block saved in the entry frame + entry_frame_call_wrapper()->oops_do(f); +} + + +void frame::oops_do_internal(OopClosure* f, RegisterMap* map, bool use_interpreter_oop_map_cache) { + if (is_interpreted_frame()) { oops_interpreted_do(f, map, use_interpreter_oop_map_cache); + } else if (is_entry_frame()) { oops_entry_do (f, map); + } else if (CodeCache::contains(pc())) { oops_code_blob_do (f, map); + } else { + ShouldNotReachHere(); + } +} + +void frame::nmethods_do() { + if (_cb != NULL && _cb->is_nmethod()) { + nmethods_code_blob_do(); + } +} + + +void frame::gc_prologue() { + if (is_interpreted_frame()) { + // set bcx to bci to become methodOop position independent during GC + interpreter_frame_set_bcx(interpreter_frame_bci()); + } +} + + +void frame::gc_epilogue() { + if (is_interpreted_frame()) { + // set bcx back to bcp for interpreter + interpreter_frame_set_bcx((intptr_t)interpreter_frame_bcp()); + } + // call processor specific epilog function + pd_gc_epilog(); +} + + +# ifdef ENABLE_ZAP_DEAD_LOCALS + +void frame::CheckValueClosure::do_oop(oop* p) { + if (CheckOopishValues && Universe::heap()->is_in_reserved(*p)) { + warning("value @ " INTPTR_FORMAT " looks oopish (" INTPTR_FORMAT ") (thread = " INTPTR_FORMAT ")", p, (address)*p, Thread::current()); + } +} +frame::CheckValueClosure frame::_check_value; + + +void frame::CheckOopClosure::do_oop(oop* p) { + if (*p != NULL && !(*p)->is_oop()) { + warning("value @ " INTPTR_FORMAT " should be an oop (" INTPTR_FORMAT ") (thread = " INTPTR_FORMAT ")", p, (address)*p, Thread::current()); + } +} +frame::CheckOopClosure frame::_check_oop; + +void frame::check_derived_oop(oop* base, oop* derived) { + _check_oop.do_oop(base); +} + + +void frame::ZapDeadClosure::do_oop(oop* p) { + if (TraceZapDeadLocals) tty->print_cr("zapping @ " INTPTR_FORMAT " containing " INTPTR_FORMAT, p, (address)*p); + // Need cast because on _LP64 the conversion to oop is ambiguous. Constant + // can be either long or int. + *p = (oop)(int)0xbabebabe; +} +frame::ZapDeadClosure frame::_zap_dead; + +void frame::zap_dead_locals(JavaThread* thread, const RegisterMap* map) { + assert(thread == Thread::current(), "need to synchronize to do this to another thread"); + // Tracing - part 1 + if (TraceZapDeadLocals) { + ResourceMark rm(thread); + tty->print_cr("--------------------------------------------------------------------------------"); + tty->print("Zapping dead locals in "); + print_on(tty); + tty->cr(); + } + // Zapping + if (is_entry_frame ()) zap_dead_entry_locals (thread, map); + else if (is_interpreted_frame()) zap_dead_interpreted_locals(thread, map); + else if (is_compiled_frame()) zap_dead_compiled_locals (thread, map); + + else + // could be is_runtime_frame + // so remove error: ShouldNotReachHere(); + ; + // Tracing - part 2 + if (TraceZapDeadLocals) { + tty->cr(); + } +} + + +void frame::zap_dead_interpreted_locals(JavaThread *thread, const RegisterMap* map) { + // get current interpreter 'pc' + assert(is_interpreted_frame(), "Not an interpreted frame"); + methodOop m = interpreter_frame_method(); + int bci = interpreter_frame_bci(); + + int max_locals = m->is_native() ? m->size_of_parameters() : m->max_locals(); + + if (TaggedStackInterpreter) { + InterpreterOopMap *mask = NULL; +#ifdef ASSERT + InterpreterOopMap oopmap_mask; + methodHandle method(thread, m); + OopMapCache::compute_one_oop_map(method, bci, &oopmap_mask); + mask = &oopmap_mask; +#endif // ASSERT + oops_interpreted_locals_do(&_check_oop, max_locals, mask); + } else { + // process dynamic part + InterpreterFrameClosure value_blk(this, max_locals, m->max_stack(), + &_check_value); + InterpreterFrameClosure oop_blk(this, max_locals, m->max_stack(), + &_check_oop ); + InterpreterFrameClosure dead_blk(this, max_locals, m->max_stack(), + &_zap_dead ); + + // get frame map + InterpreterOopMap mask; + m->mask_for(bci, &mask); + mask.iterate_all( &oop_blk, &value_blk, &dead_blk); + } +} + + +void frame::zap_dead_compiled_locals(JavaThread* thread, const RegisterMap* reg_map) { + + ResourceMark rm(thread); + assert(_cb != NULL, "sanity check"); + if (_cb->oop_maps() != NULL) { + OopMapSet::all_do(this, reg_map, &_check_oop, check_derived_oop, + &_check_value, &_zap_dead); + } +} + + +void frame::zap_dead_entry_locals(JavaThread*, const RegisterMap*) { + if (TraceZapDeadLocals) warning("frame::zap_dead_entry_locals unimplemented"); +} + + +void frame::zap_dead_deoptimized_locals(JavaThread*, const RegisterMap*) { + if (TraceZapDeadLocals) warning("frame::zap_dead_deoptimized_locals unimplemented"); +} + +# endif // ENABLE_ZAP_DEAD_LOCALS + +void frame::verify(const RegisterMap* map) { + // for now make sure receiver type is correct + if (is_interpreted_frame()) { + methodOop method = interpreter_frame_method(); + guarantee(method->is_method(), "method is wrong in frame::verify"); + if (!method->is_static()) { + // fetch the receiver + oop* p = (oop*) interpreter_frame_local_at(0); + // make sure we have the right receiver type + } + } + COMPILER2_PRESENT(assert(DerivedPointerTable::is_empty(), "must be empty before verify");) + oops_do_internal(&VerifyOopClosure::verify_oop, (RegisterMap*)map, false); +} + + +#ifdef ASSERT +bool frame::verify_return_pc(address x) { + if (StubRoutines::returns_to_call_stub(x)) { + return true; + } + if (CodeCache::contains(x)) { + return true; + } + if (Interpreter::contains(x)) { + return true; + } + return false; +} +#endif + + +#ifdef ASSERT +void frame::interpreter_frame_verify_monitor(BasicObjectLock* value) const { + assert(is_interpreted_frame(), "Not an interpreted frame"); + // verify that the value is in the right part of the frame + address low_mark = (address) interpreter_frame_monitor_end(); + address high_mark = (address) interpreter_frame_monitor_begin(); + address current = (address) value; + + const int monitor_size = frame::interpreter_frame_monitor_size(); + guarantee((high_mark - current) % monitor_size == 0 , "Misaligned top of BasicObjectLock*"); + guarantee( high_mark > current , "Current BasicObjectLock* higher than high_mark"); + + guarantee((current - low_mark) % monitor_size == 0 , "Misaligned bottom of BasicObjectLock*"); + guarantee( current >= low_mark , "Current BasicObjectLock* below than low_mark"); +} +#endif + + +//----------------------------------------------------------------------------------- +// StackFrameStream implementation + +StackFrameStream::StackFrameStream(JavaThread *thread, bool update) : _reg_map(thread, update) { + assert(thread->has_last_Java_frame(), "sanity check"); + _fr = thread->last_frame(); + _is_done = false; +} diff --git a/hotspot/src/share/vm/runtime/frame.hpp b/hotspot/src/share/vm/runtime/frame.hpp new file mode 100644 index 00000000000..5c695457063 --- /dev/null +++ b/hotspot/src/share/vm/runtime/frame.hpp @@ -0,0 +1,469 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +typedef class BytecodeInterpreter* interpreterState; + +class CodeBlob; + + +// A frame represents a physical stack frame (an activation). Frames +// can be C or Java frames, and the Java frames can be interpreted or +// compiled. In contrast, vframes represent source-level activations, +// so that one physical frame can correspond to multiple source level +// frames because of inlining. + +class frame VALUE_OBJ_CLASS_SPEC { + private: + // Instance variables: + intptr_t* _sp; // stack pointer (from Thread::last_Java_sp) + address _pc; // program counter (the next instruction after the call) + + CodeBlob* _cb; // CodeBlob that "owns" pc + enum deopt_state { + not_deoptimized, + is_deoptimized, + unknown + }; + + deopt_state _deopt_state; + + public: + // Constructors + frame(); + + // Accessors + + // pc: Returns the pc at which this frame will continue normally. + // It must point at the beginning of the next instruction to execute. + address pc() const { return _pc; } + + // This returns the pc that if you were in the debugger you'd see. Not + // the idealized value in the frame object. This undoes the magic conversion + // that happens for deoptimized frames. In addition it makes the value the + // hardware would want to see in the native frame. The only user (at this point) + // is deoptimization. It likely no one else should ever use it. + address raw_pc() const; + + void set_pc( address newpc ); + + intptr_t* sp() const { return _sp; } + void set_sp( intptr_t* newsp ) { _sp = newsp; } + + + CodeBlob* cb() const { return _cb; } + + // patching operations + void patch_pc(Thread* thread, address pc); + + // Every frame needs to return a unique id which distinguishes it from all other frames. + // For sparc and ia32 use sp. ia64 can have memory frames that are empty so multiple frames + // will have identical sp values. For ia64 the bsp (fp) value will serve. No real frame + // should have an id() of NULL so it is a distinguishing value for an unmatchable frame. + // We also have relationals which allow comparing a frame to anoth frame's id() allow + // us to distinguish younger (more recent activation) from older (less recent activations) + // A NULL id is only valid when comparing for equality. + + intptr_t* id(void) const; + bool is_younger(intptr_t* id) const; + bool is_older(intptr_t* id) const; + + // testers + + // Compares for strict equality. Rarely used or needed. + // It can return a different result than f1.id() == f2.id() + bool equal(frame other) const; + + // type testers + bool is_interpreted_frame() const; + bool is_java_frame() const; + bool is_entry_frame() const; // Java frame called from C? + bool is_native_frame() const; + bool is_runtime_frame() const; + bool is_compiled_frame() const; + bool is_safepoint_blob_frame() const; + bool is_deoptimized_frame() const; + + // testers + bool is_first_frame() const; // oldest frame? (has no sender) + bool is_first_java_frame() const; // same for Java frame + + bool is_interpreted_frame_valid() const; // performs sanity checks on interpreted frames. + + // tells whether this frame is marked for deoptimization + bool should_be_deoptimized() const; + + // tells whether this frame can be deoptimized + bool can_be_deoptimized() const; + + // returns the frame size in stack slots + int frame_size() const; + + // returns the sending frame + frame sender(RegisterMap* map) const; + + // for Profiling - acting on another frame. walks sender frames + // if valid. + frame profile_find_Java_sender_frame(JavaThread *thread); + bool safe_for_sender(JavaThread *thread); + + // returns the sender, but skips conversion frames + frame real_sender(RegisterMap* map) const; + + // returns the the sending Java frame, skipping any intermediate C frames + // NB: receiver must not be first frame + frame java_sender() const; + + private: + // Helper methods for better factored code in frame::sender + frame sender_for_compiled_frame(RegisterMap* map) const; + frame sender_for_entry_frame(RegisterMap* map) const; + frame sender_for_interpreter_frame(RegisterMap* map) const; + frame sender_for_native_frame(RegisterMap* map) const; + + // All frames: + + // A low-level interface for vframes: + + public: + + intptr_t* addr_at(int index) const { return &fp()[index]; } + intptr_t at(int index) const { return *addr_at(index); } + + // accessors for locals + oop obj_at(int offset) const { return *obj_at_addr(offset); } + void obj_at_put(int offset, oop value) { *obj_at_addr(offset) = value; } + + jint int_at(int offset) const { return *int_at_addr(offset); } + void int_at_put(int offset, jint value) { *int_at_addr(offset) = value; } + + oop* obj_at_addr(int offset) const { return (oop*) addr_at(offset); } + + oop* adjusted_obj_at_addr(methodOop method, int index) { return obj_at_addr(adjust_offset(method, index)); } + + private: + jint* int_at_addr(int offset) const { return (jint*) addr_at(offset); } + + public: + // Link (i.e., the pointer to the previous frame) + intptr_t* link() const; + void set_link(intptr_t* addr); + + // Return address + address sender_pc() const; + + // Support for deoptimization + void deoptimize(JavaThread* thread, bool thread_is_known_safe = false); + + // The frame's original SP, before any extension by an interpreted callee; + // used for packing debug info into vframeArray objects and vframeArray lookup. + intptr_t* unextended_sp() const; + + // returns the stack pointer of the calling frame + intptr_t* sender_sp() const; + + + // Interpreter frames: + + private: + intptr_t** interpreter_frame_locals_addr() const; + intptr_t* interpreter_frame_bcx_addr() const; + intptr_t* interpreter_frame_mdx_addr() const; + + public: + // Tags for TaggedStackInterpreter + enum Tag { + TagValue = 0, // Important: must be zero to use G0 on sparc. + TagReference = 0x555, // Reference type - is an oop that needs gc. + TagCategory2 = 0x666 // Only used internally by interpreter + // and not written to the java stack. + // The values above are chosen so that misuse causes a crash + // with a recognizable value. + }; + + static Tag tag_for_basic_type(BasicType typ) { + return (typ == T_OBJECT ? TagReference : TagValue); + } + + // Locals + + // The _at version returns a pointer because the address is used for GC. + intptr_t* interpreter_frame_local_at(int index) const; + Tag interpreter_frame_local_tag(int index) const; + void interpreter_frame_set_local_tag(int index, Tag tag) const; + + void interpreter_frame_set_locals(intptr_t* locs); + + // byte code index/pointer (use these functions for unchecked frame access only!) + intptr_t interpreter_frame_bcx() const { return *interpreter_frame_bcx_addr(); } + void interpreter_frame_set_bcx(intptr_t bcx); + + // byte code index + jint interpreter_frame_bci() const; + void interpreter_frame_set_bci(jint bci); + + // byte code pointer + address interpreter_frame_bcp() const; + void interpreter_frame_set_bcp(address bcp); + + // Unchecked access to the method data index/pointer. + // Only use this if you know what you are doing. + intptr_t interpreter_frame_mdx() const { return *interpreter_frame_mdx_addr(); } + void interpreter_frame_set_mdx(intptr_t mdx); + + // method data pointer + address interpreter_frame_mdp() const; + void interpreter_frame_set_mdp(address dp); + + // Find receiver out of caller's (compiled) argument list + oop retrieve_receiver(RegisterMap *reg_map); + + // Return the monitor owner and BasicLock for compiled synchronized + // native methods so that biased locking can revoke the receiver's + // bias if necessary. Takes optional nmethod for this frame as + // argument to avoid performing repeated lookups in code cache. + BasicLock* compiled_synchronized_native_monitor (nmethod* nm = NULL); + oop compiled_synchronized_native_monitor_owner(nmethod* nm = NULL); + + // Find receiver for an invoke when arguments are just pushed on stack (i.e., callee stack-frame is + // not setup) + oop interpreter_callee_receiver(symbolHandle signature) { return *interpreter_callee_receiver_addr(signature); } + + + oop *interpreter_callee_receiver_addr(symbolHandle signature); + + + // expression stack (may go up or down, direction == 1 or -1) + public: + intptr_t* interpreter_frame_expression_stack() const; + static jint interpreter_frame_expression_stack_direction(); + + // The _at version returns a pointer because the address is used for GC. + intptr_t* interpreter_frame_expression_stack_at(jint offset) const; + Tag interpreter_frame_expression_stack_tag(jint offset) const; + void interpreter_frame_set_expression_stack_tag(jint offset, Tag tag) const; + + // top of expression stack + intptr_t* interpreter_frame_tos_at(jint offset) const; + intptr_t* interpreter_frame_tos_address() const; + + + jint interpreter_frame_expression_stack_size() const; + + intptr_t* interpreter_frame_sender_sp() const; + +#ifndef CC_INTERP + // template based interpreter deoptimization support + void set_interpreter_frame_sender_sp(intptr_t* sender_sp); + void interpreter_frame_set_monitor_end(BasicObjectLock* value); +#endif // CC_INTERP + + // BasicObjectLocks: + // + // interpreter_frame_monitor_begin is higher in memory than interpreter_frame_monitor_end + // Interpreter_frame_monitor_begin points to one element beyond the oldest one, + // interpreter_frame_monitor_end points to the youngest one, or if there are none, + // it points to one beyond where the first element will be. + // interpreter_frame_monitor_size reports the allocation size of a monitor in the interpreter stack. + // this value is >= BasicObjectLock::size(), and may be rounded up + + BasicObjectLock* interpreter_frame_monitor_begin() const; + BasicObjectLock* interpreter_frame_monitor_end() const; + BasicObjectLock* next_monitor_in_interpreter_frame(BasicObjectLock* current) const; + BasicObjectLock* previous_monitor_in_interpreter_frame(BasicObjectLock* current) const; + static int interpreter_frame_monitor_size(); + + void interpreter_frame_verify_monitor(BasicObjectLock* value) const; + + // Tells whether the current interpreter_frame frame pointer + // corresponds to the old compiled/deoptimized fp + // The receiver used to be a top level frame + bool interpreter_frame_equals_unpacked_fp(intptr_t* fp); + + // Return/result value from this interpreter frame + // If the method return type is T_OBJECT or T_ARRAY populates oop_result + // For other (non-T_VOID) the appropriate field in the jvalue is populated + // with the result value. + // Should only be called when at method exit when the method is not + // exiting due to an exception. + BasicType interpreter_frame_result(oop* oop_result, jvalue* value_result); + + public: + // Method & constant pool cache + methodOop interpreter_frame_method() const; + void interpreter_frame_set_method(methodOop method); + methodOop* interpreter_frame_method_addr() const; + constantPoolCacheOop* interpreter_frame_cache_addr() const; + + public: + // Entry frames + JavaCallWrapper* entry_frame_call_wrapper() const; + intptr_t* entry_frame_argument_at(int offset) const; + + // tells whether there is another chunk of Delta stack above + bool entry_frame_is_first() const; + + // Compiled frames: + + public: + // Given the index of a local, and the number of argument words + // in this stack frame, tell which word of the stack frame to find + // the local in. Arguments are stored above the ofp/rpc pair, + // while other locals are stored below it. + // Since monitors (BasicLock blocks) are also assigned indexes, + // but may have different storage requirements, their presence + // can also affect the calculation of offsets. + static int local_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors); + + // Given the index of a monitor, etc., tell which word of the + // stack frame contains the start of the BasicLock block. + // Note that the local index by convention is the __higher__ + // of the two indexes allocated to the block. + static int monitor_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors); + + // Tell the smallest value that local_offset_for_compiler will attain. + // This is used to help determine how much stack frame to allocate. + static int min_local_offset_for_compiler(int nof_args, int max_nof_locals, int max_nof_monitors); + + // Tells if this register must be spilled during a call. + // On Intel, all registers are smashed by calls. + static bool volatile_across_calls(Register reg); + + + // Safepoints + + public: + oop saved_oop_result(RegisterMap* map) const; + void set_saved_oop_result(RegisterMap* map, oop obj); + + // For debugging + private: + const char* print_name() const; + + public: + void print_value() const { print_value_on(tty,NULL); } + void print_value_on(outputStream* st, JavaThread *thread) const; + void print_on(outputStream* st) const; + void interpreter_frame_print_on(outputStream* st) const; + void print_on_error(outputStream* st, char* buf, int buflen, bool verbose = false) const; + + // Conversion from an VMReg to physical stack location + oop* oopmapreg_to_location(VMReg reg, const RegisterMap* regmap) const; + + // Oops-do's + void oops_compiled_arguments_do(symbolHandle signature, bool is_static, const RegisterMap* reg_map, OopClosure* f); + void oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache = true); + + private: + void oops_interpreted_locals_do(OopClosure *f, + int max_locals, + InterpreterOopMap *mask); + void oops_interpreted_expressions_do(OopClosure *f, symbolHandle signature, + bool is_static, int max_stack, int max_locals, + InterpreterOopMap *mask); + void oops_interpreted_arguments_do(symbolHandle signature, bool is_static, OopClosure* f); + + // Iteration of oops + void oops_do_internal(OopClosure* f, RegisterMap* map, bool use_interpreter_oop_map_cache); + void oops_entry_do(OopClosure* f, const RegisterMap* map); + void oops_code_blob_do(OopClosure* f, const RegisterMap* map); + int adjust_offset(methodOop method, int index); // helper for above fn + // Iteration of nmethods + void nmethods_code_blob_do(); + public: + // Memory management + void oops_do(OopClosure* f, RegisterMap* map) { oops_do_internal(f, map, true); } + void nmethods_do(); + + void gc_prologue(); + void gc_epilogue(); + void pd_gc_epilog(); + +# ifdef ENABLE_ZAP_DEAD_LOCALS + private: + class CheckValueClosure: public OopClosure { + public: void do_oop(oop* p); + }; + static CheckValueClosure _check_value; + + class CheckOopClosure: public OopClosure { + public: void do_oop(oop* p); + }; + static CheckOopClosure _check_oop; + + static void check_derived_oop(oop* base, oop* derived); + + class ZapDeadClosure: public OopClosure { + public: void do_oop(oop* p); + }; + static ZapDeadClosure _zap_dead; + + public: + // Zapping + void zap_dead_locals (JavaThread* thread, const RegisterMap* map); + void zap_dead_interpreted_locals(JavaThread* thread, const RegisterMap* map); + void zap_dead_compiled_locals (JavaThread* thread, const RegisterMap* map); + void zap_dead_entry_locals (JavaThread* thread, const RegisterMap* map); + void zap_dead_deoptimized_locals(JavaThread* thread, const RegisterMap* map); +# endif + // Verification + void verify(const RegisterMap* map); + static bool verify_return_pc(address x); + static bool is_bci(intptr_t bcx); + // Usage: + // assert(frame::verify_return_pc(return_address), "must be a return pc"); + + int pd_oop_map_offset_adjustment() const; + +# include "incls/_frame_pd.hpp.incl" +}; + + +// +// StackFrameStream iterates through the frames of a thread starting from +// top most frame. It automatically takes care of updating the location of +// all (callee-saved) registers. Notice: If a thread is stopped at +// a safepoint, all registers are saved, not only the callee-saved ones. +// +// Use: +// +// for(StackFrameStream fst(thread); !fst.is_done(); fst.next()) { +// ... +// } +// +class StackFrameStream : public StackObj { + private: + frame _fr; + RegisterMap _reg_map; + bool _is_done; + public: + StackFrameStream(JavaThread *thread, bool update = true); + + // Iteration + bool is_done() { return (_is_done) ? true : (_is_done = _fr.is_first_frame(), false); } + void next() { if (!_is_done) _fr = _fr.sender(&_reg_map); } + + // Query + frame *current() { return &_fr; } + RegisterMap* register_map() { return &_reg_map; } +}; diff --git a/hotspot/src/share/vm/runtime/frame.inline.hpp b/hotspot/src/share/vm/runtime/frame.inline.hpp new file mode 100644 index 00000000000..3449ead761a --- /dev/null +++ b/hotspot/src/share/vm/runtime/frame.inline.hpp @@ -0,0 +1,55 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds platform-independant bodies of inline functions for frames. + +// Note: The bcx usually contains the bcp; however during GC it contains the bci +// (changed by gc_prologue() and gc_epilogue()) to be methodOop position +// independent. These accessors make sure the correct value is returned +// by testing the range of the bcx value. bcp's are guaranteed to be above +// max_method_code_size, since methods are always allocated in OldSpace and +// Eden is allocated before OldSpace. +// +// The bcp is accessed sometimes during GC for ArgumentDescriptors; than +// the correct translation has to be performed (was bug). + +inline bool frame::is_bci(intptr_t bcx) { +#ifdef _LP64 + return ((uintptr_t) bcx) <= ((uintptr_t) max_method_code_size) ; +#else + return 0 <= bcx && bcx <= max_method_code_size; +#endif +} + +inline bool frame::is_entry_frame() const { + return StubRoutines::returns_to_call_stub(pc()); +} + +inline bool frame::is_first_frame() const { + return is_entry_frame() && entry_frame_is_first(); +} + +// here are the platform-dependent bodies: + +# include "incls/_frame_pd.inline.hpp.incl" diff --git a/hotspot/src/share/vm/runtime/globals.cpp b/hotspot/src/share/vm/runtime/globals.cpp new file mode 100644 index 00000000000..50bf7658b11 --- /dev/null +++ b/hotspot/src/share/vm/runtime/globals.cpp @@ -0,0 +1,429 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_globals.cpp.incl" + + +RUNTIME_FLAGS(MATERIALIZE_DEVELOPER_FLAG, MATERIALIZE_PD_DEVELOPER_FLAG, \ + MATERIALIZE_PRODUCT_FLAG, MATERIALIZE_PD_PRODUCT_FLAG, \ + MATERIALIZE_DIAGNOSTIC_FLAG, MATERIALIZE_NOTPRODUCT_FLAG, \ + MATERIALIZE_MANAGEABLE_FLAG, MATERIALIZE_PRODUCT_RW_FLAG) + +RUNTIME_OS_FLAGS(MATERIALIZE_DEVELOPER_FLAG, MATERIALIZE_PD_DEVELOPER_FLAG, \ + MATERIALIZE_PRODUCT_FLAG, MATERIALIZE_PD_PRODUCT_FLAG, \ + MATERIALIZE_DIAGNOSTIC_FLAG, MATERIALIZE_NOTPRODUCT_FLAG) + +bool Flag::is_unlocker() const { + return strcmp(name, "UnlockDiagnosticVMOptions") == 0; +} + +bool Flag::is_unlocked() const { + if (strcmp(kind, "{diagnostic}") == 0) { + return UnlockDiagnosticVMOptions; + } else { + return true; + } +} + +bool Flag::is_writeable() const { + return (strcmp(kind, "{manageable}") == 0 || strcmp(kind, "{product rw}") == 0); +} + +// All flags except "manageable" are assumed internal flags. +// Long term, we need to define a mechanism to specify which flags +// are external/stable and change this function accordingly. +bool Flag::is_external() const { + return (strcmp(kind, "{manageable}") == 0); +} + +// Length of format string (e.g. "%.1234s") for printing ccstr below +#define FORMAT_BUFFER_LEN 16 + +void Flag::print_on(outputStream* st) { + st->print("%5s %-35s %c= ", type, name, (origin != DEFAULT ? ':' : ' ')); + if (is_bool()) st->print("%-16s", get_bool() ? "true" : "false"); + if (is_intx()) st->print("%-16ld", get_intx()); + if (is_uintx()) st->print("%-16lu", get_uintx()); + if (is_ccstr()) { + const char* cp = get_ccstr(); + const char* eol; + while ((eol = strchr(cp, '\n')) != NULL) { + char format_buffer[FORMAT_BUFFER_LEN]; + size_t llen = pointer_delta(eol, cp, sizeof(char)); + jio_snprintf(format_buffer, FORMAT_BUFFER_LEN, + "%%." SIZE_FORMAT "s", llen); + st->print(format_buffer, cp); + st->cr(); + cp = eol+1; + st->print("%5s %-35s += ", "", name); + } + st->print("%-16s", cp); + } + st->print(" %s", kind); + st->cr(); +} + +void Flag::print_as_flag(outputStream* st) { + if (is_bool()) { + st->print("-XX:%s%s", get_bool() ? "+" : "-", name); + } else if (is_intx()) { + st->print("-XX:%s=" INTX_FORMAT, name, get_intx()); + } else if (is_uintx()) { + st->print("-XX:%s=" UINTX_FORMAT, name, get_uintx()); + } else if (is_ccstr()) { + st->print("-XX:%s=", name); + // Need to turn embedded '\n's back into separate arguments + // Not so efficient to print one character at a time, + // but the choice is to do the transformation to a buffer + // and print that. And this need not be efficient. + for (const char* cp = get_ccstr(); *cp != '\0'; cp += 1) { + switch (*cp) { + default: + st->print("%c", *cp); + break; + case '\n': + st->print(" -XX:%s=", name); + break; + } + } + } else { + ShouldNotReachHere(); + } +} + +// 4991491 do not "optimize out" the was_set false values: omitting them +// tickles a Microsoft compiler bug causing flagTable to be malformed + +#define RUNTIME_PRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{product}", DEFAULT }, +#define RUNTIME_PD_PRODUCT_FLAG_STRUCT(type, name, doc) { #type, XSTR(name), &name, "{pd product}", DEFAULT }, +#define RUNTIME_DIAGNOSTIC_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{diagnostic}", DEFAULT }, +#define RUNTIME_MANAGEABLE_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{manageable}", DEFAULT }, +#define RUNTIME_PRODUCT_RW_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{product rw}", DEFAULT }, + +#ifdef PRODUCT + #define RUNTIME_DEVELOP_FLAG_STRUCT(type, name, value, doc) /* flag is constant */ + #define RUNTIME_PD_DEVELOP_FLAG_STRUCT(type, name, doc) /* flag is constant */ + #define RUNTIME_NOTPRODUCT_FLAG_STRUCT(type, name, value, doc) +#else + #define RUNTIME_DEVELOP_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "", DEFAULT }, + #define RUNTIME_PD_DEVELOP_FLAG_STRUCT(type, name, doc) { #type, XSTR(name), &name, "{pd}", DEFAULT }, + #define RUNTIME_NOTPRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{notproduct}", DEFAULT }, +#endif + +#define C1_PRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C1 product}", DEFAULT }, +#define C1_PD_PRODUCT_FLAG_STRUCT(type, name, doc) { #type, XSTR(name), &name, "{C1 pd product}", DEFAULT }, +#ifdef PRODUCT + #define C1_DEVELOP_FLAG_STRUCT(type, name, value, doc) /* flag is constant */ + #define C1_PD_DEVELOP_FLAG_STRUCT(type, name, doc) /* flag is constant */ + #define C1_NOTPRODUCT_FLAG_STRUCT(type, name, value, doc) +#else + #define C1_DEVELOP_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C1}", DEFAULT }, + #define C1_PD_DEVELOP_FLAG_STRUCT(type, name, doc) { #type, XSTR(name), &name, "{C1 pd}", DEFAULT }, + #define C1_NOTPRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C1 notproduct}", DEFAULT }, +#endif + + +#define C2_PRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 product}", DEFAULT }, +#define C2_PD_PRODUCT_FLAG_STRUCT(type, name, doc) { #type, XSTR(name), &name, "{C2 pd product}", DEFAULT }, +#define C2_DIAGNOSTIC_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 diagnostic}", DEFAULT }, +#ifdef PRODUCT + #define C2_DEVELOP_FLAG_STRUCT(type, name, value, doc) /* flag is constant */ + #define C2_PD_DEVELOP_FLAG_STRUCT(type, name, doc) /* flag is constant */ + #define C2_NOTPRODUCT_FLAG_STRUCT(type, name, value, doc) +#else + #define C2_DEVELOP_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2}", DEFAULT }, + #define C2_PD_DEVELOP_FLAG_STRUCT(type, name, doc) { #type, XSTR(name), &name, "{C2 pd}", DEFAULT }, + #define C2_NOTPRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 notproduct}", DEFAULT }, +#endif + + +static Flag flagTable[] = { + RUNTIME_FLAGS(RUNTIME_DEVELOP_FLAG_STRUCT, RUNTIME_PD_DEVELOP_FLAG_STRUCT, RUNTIME_PRODUCT_FLAG_STRUCT, RUNTIME_PD_PRODUCT_FLAG_STRUCT, RUNTIME_DIAGNOSTIC_FLAG_STRUCT, RUNTIME_NOTPRODUCT_FLAG_STRUCT, RUNTIME_MANAGEABLE_FLAG_STRUCT, RUNTIME_PRODUCT_RW_FLAG_STRUCT) + RUNTIME_OS_FLAGS(RUNTIME_DEVELOP_FLAG_STRUCT, RUNTIME_PD_DEVELOP_FLAG_STRUCT, RUNTIME_PRODUCT_FLAG_STRUCT, RUNTIME_PD_PRODUCT_FLAG_STRUCT, RUNTIME_DIAGNOSTIC_FLAG_STRUCT, RUNTIME_NOTPRODUCT_FLAG_STRUCT) +#ifdef COMPILER1 + C1_FLAGS(C1_DEVELOP_FLAG_STRUCT, C1_PD_DEVELOP_FLAG_STRUCT, C1_PRODUCT_FLAG_STRUCT, C1_PD_PRODUCT_FLAG_STRUCT, C1_NOTPRODUCT_FLAG_STRUCT) +#endif +#ifdef COMPILER2 + C2_FLAGS(C2_DEVELOP_FLAG_STRUCT, C2_PD_DEVELOP_FLAG_STRUCT, C2_PRODUCT_FLAG_STRUCT, C2_PD_PRODUCT_FLAG_STRUCT, C2_DIAGNOSTIC_FLAG_STRUCT, C2_NOTPRODUCT_FLAG_STRUCT) +#endif + {0, NULL, NULL} +}; + +Flag* Flag::flags = flagTable; +size_t Flag::numFlags = (sizeof(flagTable) / sizeof(Flag)); + +inline bool str_equal(const char* s, char* q, size_t len) { + // s is null terminated, q is not! + if (strlen(s) != (unsigned int) len) return false; + return strncmp(s, q, len) == 0; +} + +Flag* Flag::find_flag(char* name, size_t length) { + for (Flag* current = &flagTable[0]; current->name; current++) { + if (str_equal(current->name, name, length)) { + if (!(current->is_unlocked() || current->is_unlocker())) { + // disable use of diagnostic flags until they are unlocked + return NULL; + } + return current; + } + } + return NULL; +} + +// Returns the address of the index'th element +static Flag* address_of_flag(CommandLineFlagWithType flag) { + assert((size_t)flag < Flag::numFlags, "bad command line flag index"); + return &Flag::flags[flag]; +} + +bool CommandLineFlagsEx::is_default(CommandLineFlag flag) { + assert((size_t)flag < Flag::numFlags, "bad command line flag index"); + Flag* f = &Flag::flags[flag]; + return (f->origin == DEFAULT); +} + +bool CommandLineFlags::wasSetOnCmdline(const char* name, bool* value) { + Flag* result = Flag::find_flag((char*)name, strlen(name)); + if (result == NULL) return false; + *value = (result->origin == COMMAND_LINE); + return true; +} + +bool CommandLineFlags::boolAt(char* name, size_t len, bool* value) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_bool()) return false; + *value = result->get_bool(); + return true; +} + +bool CommandLineFlags::boolAtPut(char* name, size_t len, bool* value, FlagValueOrigin origin) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_bool()) return false; + bool old_value = result->get_bool(); + result->set_bool(*value); + *value = old_value; + result->origin = origin; + return true; +} + +void CommandLineFlagsEx::boolAtPut(CommandLineFlagWithType flag, bool value, FlagValueOrigin origin) { + Flag* faddr = address_of_flag(flag); + guarantee(faddr != NULL && faddr->is_bool(), "wrong flag type"); + faddr->set_bool(value); + faddr->origin = origin; +} + +bool CommandLineFlags::intxAt(char* name, size_t len, intx* value) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_intx()) return false; + *value = result->get_intx(); + return true; +} + +bool CommandLineFlags::intxAtPut(char* name, size_t len, intx* value, FlagValueOrigin origin) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_intx()) return false; + intx old_value = result->get_intx(); + result->set_intx(*value); + *value = old_value; + result->origin = origin; + return true; +} + +void CommandLineFlagsEx::intxAtPut(CommandLineFlagWithType flag, intx value, FlagValueOrigin origin) { + Flag* faddr = address_of_flag(flag); + guarantee(faddr != NULL && faddr->is_intx(), "wrong flag type"); + faddr->set_intx(value); + faddr->origin = origin; +} + +bool CommandLineFlags::uintxAt(char* name, size_t len, uintx* value) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_uintx()) return false; + *value = result->get_uintx(); + return true; +} + +bool CommandLineFlags::uintxAtPut(char* name, size_t len, uintx* value, FlagValueOrigin origin) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_uintx()) return false; + uintx old_value = result->get_uintx(); + result->set_uintx(*value); + *value = old_value; + result->origin = origin; + return true; +} + +void CommandLineFlagsEx::uintxAtPut(CommandLineFlagWithType flag, uintx value, FlagValueOrigin origin) { + Flag* faddr = address_of_flag(flag); + guarantee(faddr != NULL && faddr->is_uintx(), "wrong flag type"); + faddr->set_uintx(value); + faddr->origin = origin; +} + +bool CommandLineFlags::doubleAt(char* name, size_t len, double* value) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_double()) return false; + *value = result->get_double(); + return true; +} + +bool CommandLineFlags::doubleAtPut(char* name, size_t len, double* value, FlagValueOrigin origin) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_double()) return false; + double old_value = result->get_double(); + result->set_double(*value); + *value = old_value; + result->origin = origin; + return true; +} + +void CommandLineFlagsEx::doubleAtPut(CommandLineFlagWithType flag, double value, FlagValueOrigin origin) { + Flag* faddr = address_of_flag(flag); + guarantee(faddr != NULL && faddr->is_double(), "wrong flag type"); + faddr->set_double(value); + faddr->origin = origin; +} + +bool CommandLineFlags::ccstrAt(char* name, size_t len, ccstr* value) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_ccstr()) return false; + *value = result->get_ccstr(); + return true; +} + +// Contract: Flag will make private copy of the incoming value. +// Outgoing value is always malloc-ed, and caller MUST call free. +bool CommandLineFlags::ccstrAtPut(char* name, size_t len, ccstr* value, FlagValueOrigin origin) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_ccstr()) return false; + ccstr old_value = result->get_ccstr(); + char* new_value = NEW_C_HEAP_ARRAY(char, strlen(*value)+1); + strcpy(new_value, *value); + result->set_ccstr(new_value); + if (result->origin == DEFAULT && old_value != NULL) { + // Prior value is NOT heap allocated, but was a literal constant. + char* old_value_to_free = NEW_C_HEAP_ARRAY(char, strlen(old_value)+1); + strcpy(old_value_to_free, old_value); + old_value = old_value_to_free; + } + *value = old_value; + result->origin = origin; + return true; +} + +// Contract: Flag will make private copy of the incoming value. +void CommandLineFlagsEx::ccstrAtPut(CommandLineFlagWithType flag, ccstr value, FlagValueOrigin origin) { + Flag* faddr = address_of_flag(flag); + guarantee(faddr != NULL && faddr->is_ccstr(), "wrong flag type"); + ccstr old_value = faddr->get_ccstr(); + char* new_value = NEW_C_HEAP_ARRAY(char, strlen(value)+1); + strcpy(new_value, value); + faddr->set_ccstr(new_value); + if (faddr->origin != DEFAULT && old_value != NULL) { + // Prior value is heap allocated so free it. + FREE_C_HEAP_ARRAY(char, old_value); + } + faddr->origin = origin; +} + +extern "C" { + static int compare_flags(const void* void_a, const void* void_b) { + return strcmp((*((Flag**) void_a))->name, (*((Flag**) void_b))->name); + } +} + +void CommandLineFlags::printSetFlags() { + // Print which flags were set on the command line + // note: this method is called before the thread structure is in place + // which means resource allocation cannot be used. + + // Compute size + int length= 0; + while (flagTable[length].name != NULL) length++; + + // Sort + Flag** array = NEW_C_HEAP_ARRAY(Flag*, length); + for (int index = 0; index < length; index++) { + array[index] = &flagTable[index]; + } + qsort(array, length, sizeof(Flag*), compare_flags); + + // Print + for (int i = 0; i < length; i++) { + if (array[i]->origin /* naked field! */) { + array[i]->print_as_flag(tty); + tty->print(" "); + } + } + tty->cr(); + FREE_C_HEAP_ARRAY(Flag*, array); +} + +#ifndef PRODUCT + + +void CommandLineFlags::verify() { + assert(Arguments::check_vm_args_consistency(), "Some flag settings conflict"); +} + +void CommandLineFlags::printFlags() { + // Print the flags sorted by name + // note: this method is called before the thread structure is in place + // which means resource allocation cannot be used. + + // Compute size + int length= 0; + while (flagTable[length].name != NULL) length++; + + // Sort + Flag** array = NEW_C_HEAP_ARRAY(Flag*, length); + for (int index = 0; index < length; index++) { + array[index] = &flagTable[index]; + } + qsort(array, length, sizeof(Flag*), compare_flags); + + // Print + tty->print_cr("[Global flags]"); + for (int i = 0; i < length; i++) { + if (array[i]->is_unlocked()) { + array[i]->print_on(tty); + } + } + FREE_C_HEAP_ARRAY(Flag*, array); +} + +#endif diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp new file mode 100644 index 00000000000..c1e8fefeba9 --- /dev/null +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -0,0 +1,3208 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#if !defined(COMPILER1) && !defined(COMPILER2) +define_pd_global(bool, BackgroundCompilation, false); +define_pd_global(bool, UseTLAB, false); +define_pd_global(bool, CICompileOSR, false); +define_pd_global(bool, UseTypeProfile, false); +define_pd_global(bool, UseOnStackReplacement, false); +define_pd_global(bool, InlineIntrinsics, false); +define_pd_global(bool, PreferInterpreterNativeStubs, true); +define_pd_global(bool, ProfileInterpreter, false); +define_pd_global(bool, ProfileTraps, false); +define_pd_global(bool, TieredCompilation, false); + +define_pd_global(intx, CompileThreshold, 0); +define_pd_global(intx, Tier2CompileThreshold, 0); +define_pd_global(intx, Tier3CompileThreshold, 0); +define_pd_global(intx, Tier4CompileThreshold, 0); + +define_pd_global(intx, BackEdgeThreshold, 0); +define_pd_global(intx, Tier2BackEdgeThreshold, 0); +define_pd_global(intx, Tier3BackEdgeThreshold, 0); +define_pd_global(intx, Tier4BackEdgeThreshold, 0); + +define_pd_global(intx, OnStackReplacePercentage, 0); +define_pd_global(bool, ResizeTLAB, false); +define_pd_global(intx, FreqInlineSize, 0); +define_pd_global(intx, NewSizeThreadIncrease, 4*K); +define_pd_global(intx, NewRatio, 4); +define_pd_global(intx, InlineClassNatives, true); +define_pd_global(intx, InlineUnsafeOps, true); +define_pd_global(intx, InitialCodeCacheSize, 160*K); +define_pd_global(intx, ReservedCodeCacheSize, 32*M); +define_pd_global(intx, CodeCacheExpansionSize, 32*K); +define_pd_global(intx, CodeCacheMinBlockLength, 1); +define_pd_global(uintx,PermSize, ScaleForWordSize(4*M)); +define_pd_global(uintx,MaxPermSize, ScaleForWordSize(64*M)); +define_pd_global(bool, NeverActAsServerClassMachine, true); +define_pd_global(uintx, DefaultMaxRAM, 1*G); +#define CI_COMPILER_COUNT 0 +#else + +#ifdef COMPILER2 +#define CI_COMPILER_COUNT 2 +#else +#define CI_COMPILER_COUNT 1 +#endif // COMPILER2 + +#endif // no compilers + + +// string type aliases used only in this file +typedef const char* ccstr; +typedef const char* ccstrlist; // represents string arguments which accumulate + +enum FlagValueOrigin { + DEFAULT = 0, + COMMAND_LINE = 1, + ENVIRON_VAR = 2, + CONFIG_FILE = 3, + MANAGEMENT = 4, + ERGONOMIC = 5, + ATTACH_ON_DEMAND = 6, + INTERNAL = 99 +}; + +struct Flag { + const char *type; + const char *name; + void* addr; + const char *kind; + FlagValueOrigin origin; + + // points to all Flags static array + static Flag *flags; + + // number of flags + static size_t numFlags; + + static Flag* find_flag(char* name, size_t length); + + bool is_bool() const { return strcmp(type, "bool") == 0; } + bool get_bool() const { return *((bool*) addr); } + void set_bool(bool value) { *((bool*) addr) = value; } + + bool is_intx() const { return strcmp(type, "intx") == 0; } + intx get_intx() const { return *((intx*) addr); } + void set_intx(intx value) { *((intx*) addr) = value; } + + bool is_uintx() const { return strcmp(type, "uintx") == 0; } + uintx get_uintx() const { return *((uintx*) addr); } + void set_uintx(uintx value) { *((uintx*) addr) = value; } + + bool is_double() const { return strcmp(type, "double") == 0; } + double get_double() const { return *((double*) addr); } + void set_double(double value) { *((double*) addr) = value; } + + bool is_ccstr() const { return strcmp(type, "ccstr") == 0 || strcmp(type, "ccstrlist") == 0; } + bool ccstr_accumulates() const { return strcmp(type, "ccstrlist") == 0; } + ccstr get_ccstr() const { return *((ccstr*) addr); } + void set_ccstr(ccstr value) { *((ccstr*) addr) = value; } + + bool is_unlocker() const; + bool is_unlocked() const; + bool is_writeable() const; + bool is_external() const; + + void print_on(outputStream* st); + void print_as_flag(outputStream* st); +}; + +// debug flags control various aspects of the VM and are global accessible + +// use FlagSetting to temporarily change some debug flag +// e.g. FlagSetting fs(DebugThisAndThat, true); +// restored to previous value upon leaving scope +class FlagSetting { + bool val; + bool* flag; + public: + FlagSetting(bool& fl, bool newValue) { flag = &fl; val = fl; fl = newValue; } + ~FlagSetting() { *flag = val; } +}; + + +class CounterSetting { + intx* counter; + public: + CounterSetting(intx* cnt) { counter = cnt; (*counter)++; } + ~CounterSetting() { (*counter)--; } +}; + + +class IntFlagSetting { + intx val; + intx* flag; + public: + IntFlagSetting(intx& fl, intx newValue) { flag = &fl; val = fl; fl = newValue; } + ~IntFlagSetting() { *flag = val; } +}; + + +class DoubleFlagSetting { + double val; + double* flag; + public: + DoubleFlagSetting(double& fl, double newValue) { flag = &fl; val = fl; fl = newValue; } + ~DoubleFlagSetting() { *flag = val; } +}; + + +class CommandLineFlags { + public: + static bool boolAt(char* name, size_t len, bool* value); + static bool boolAt(char* name, bool* value) { return boolAt(name, strlen(name), value); } + static bool boolAtPut(char* name, size_t len, bool* value, FlagValueOrigin origin); + static bool boolAtPut(char* name, bool* value, FlagValueOrigin origin) { return boolAtPut(name, strlen(name), value, origin); } + + static bool intxAt(char* name, size_t len, intx* value); + static bool intxAt(char* name, intx* value) { return intxAt(name, strlen(name), value); } + static bool intxAtPut(char* name, size_t len, intx* value, FlagValueOrigin origin); + static bool intxAtPut(char* name, intx* value, FlagValueOrigin origin) { return intxAtPut(name, strlen(name), value, origin); } + + static bool uintxAt(char* name, size_t len, uintx* value); + static bool uintxAt(char* name, uintx* value) { return uintxAt(name, strlen(name), value); } + static bool uintxAtPut(char* name, size_t len, uintx* value, FlagValueOrigin origin); + static bool uintxAtPut(char* name, uintx* value, FlagValueOrigin origin) { return uintxAtPut(name, strlen(name), value, origin); } + + static bool doubleAt(char* name, size_t len, double* value); + static bool doubleAt(char* name, double* value) { return doubleAt(name, strlen(name), value); } + static bool doubleAtPut(char* name, size_t len, double* value, FlagValueOrigin origin); + static bool doubleAtPut(char* name, double* value, FlagValueOrigin origin) { return doubleAtPut(name, strlen(name), value, origin); } + + static bool ccstrAt(char* name, size_t len, ccstr* value); + static bool ccstrAt(char* name, ccstr* value) { return ccstrAt(name, strlen(name), value); } + static bool ccstrAtPut(char* name, size_t len, ccstr* value, FlagValueOrigin origin); + static bool ccstrAtPut(char* name, ccstr* value, FlagValueOrigin origin) { return ccstrAtPut(name, strlen(name), value, origin); } + + // Returns false if name is not a command line flag. + static bool wasSetOnCmdline(const char* name, bool* value); + static void printSetFlags(); + + static void printFlags() PRODUCT_RETURN; + + static void verify() PRODUCT_RETURN; +}; + +// use this for flags that are true by default in the debug version but +// false in the optimized version, and vice versa +#ifdef ASSERT +#define trueInDebug true +#define falseInDebug false +#else +#define trueInDebug false +#define falseInDebug true +#endif + +// use this for flags that are true per default in the product build +// but false in development builds, and vice versa +#ifdef PRODUCT +#define trueInProduct true +#define falseInProduct false +#else +#define trueInProduct false +#define falseInProduct true +#endif + +// use this for flags that are true per default in the tiered build +// but false in non-tiered builds, and vice versa +#ifdef TIERED +#define trueInTiered true +#define falseInTiered false +#else +#define trueInTiered false +#define falseInTiered true +#endif + + +// develop flags are settable / visible only during development and are constant in the PRODUCT version +// product flags are always settable / visible +// notproduct flags are settable / visible only during development and are not declared in the PRODUCT version + +// A flag must be declared with one of the following types: +// bool, intx, uintx, ccstr. +// The type "ccstr" is an alias for "const char*" and is used +// only in this file, because the macrology requires single-token type names. + +// Note: Diagnostic options not meant for VM tuning or for product modes. +// They are to be used for VM quality assurance or field diagnosis +// of VM bugs. They are hidden so that users will not be encouraged to +// try them as if they were VM ordinary execution options. However, they +// are available in the product version of the VM. Under instruction +// from support engineers, VM customers can turn them on to collect +// diagnostic information about VM problems. To use a VM diagnostic +// option, you must first specify +UnlockDiagnosticVMOptions. +// (This master switch also affects the behavior of -Xprintflags.) + +// manageable flags are writeable external product flags. +// They are dynamically writeable through the JDK management interface +// (com.sun.management.HotSpotDiagnosticMXBean API) and also through JConsole. +// These flags are external exported interface (see CCC). The list of +// manageable flags can be queried programmatically through the management +// interface. +// +// A flag can be made as "manageable" only if +// - the flag is defined in a CCC as an external exported interface. +// - the VM implementation supports dynamic setting of the flag. +// This implies that the VM must *always* query the flag variable +// and not reuse state related to the flag state at any given time. +// - you want the flag to be queried programmatically by the customers. +// +// product_rw flags are writeable internal product flags. +// They are like "manageable" flags but for internal/private use. +// The list of product_rw flags are internal/private flags which +// may be changed/removed in a future release. It can be set +// through the management interface to get/set value +// when the name of flag is supplied. +// +// A flag can be made as "product_rw" only if +// - the VM implementation supports dynamic setting of the flag. +// This implies that the VM must *always* query the flag variable +// and not reuse state related to the flag state at any given time. +// +// Note that when there is a need to support develop flags to be writeable, +// it can be done in the same way as product_rw. + +#define RUNTIME_FLAGS(develop, develop_pd, product, product_pd, diagnostic, notproduct, manageable, product_rw) \ + \ + /* UseMembar is theoretically a temp flag used for memory barrier \ + * removal testing. It was supposed to be removed before FCS but has \ + * been re-added (see 6401008) */ \ + product(bool, UseMembar, false, \ + "(Unstable) Issues membars on thread state transitions") \ + \ + product(bool, PrintCommandLineFlags, false, \ + "Prints flags that appeared on the command line") \ + \ + diagnostic(bool, UnlockDiagnosticVMOptions, trueInDebug, \ + "Enable processing of flags relating to field diagnostics") \ + \ + product(bool, JavaMonitorsInStackTrace, true, \ + "Print info. about Java monitor locks when the stacks are dumped")\ + \ + product_pd(bool, UseLargePages, \ + "Use large page memory") \ + \ + develop(bool, TracePageSizes, false, \ + "Trace page size selection and usage.") \ + \ + product(bool, UseNUMA, false, \ + "Use NUMA if available") \ + \ + product(intx, NUMAChunkResizeWeight, 20, \ + "Percentage (0-100) used to weight the current sample when " \ + "computing exponentially decaying average for " \ + "AdaptiveNUMAChunkSizing") \ + \ + product(intx, NUMASpaceResizeRate, 1*G, \ + "Do not reallocate more that this amount per collection") \ + \ + product(bool, UseAdaptiveNUMAChunkSizing, true, \ + "Enable adaptive chunk sizing for NUMA") \ + \ + product(bool, NUMAStats, false, \ + "Print NUMA stats in detailed heap information") \ + \ + product(intx, NUMAPageScanRate, 256, \ + "Maximum number of pages to include in the page scan procedure") \ + \ + product_pd(bool, NeedsDeoptSuspend, \ + "True for register window machines (sparc/ia64)") \ + \ + product(intx, UseSSE, 99, \ + "Highest supported SSE instructions set on x86/x64") \ + \ + product(uintx, LargePageSizeInBytes, 0, \ + "Large page size (0 to let VM choose the page size") \ + \ + product(uintx, LargePageHeapSizeThreshold, 128*M, \ + "Use large pages if max heap is at least this big") \ + \ + product(bool, ForceTimeHighResolution, false, \ + "Using high time resolution(For Win32 only)") \ + \ + product(bool, CacheTimeMillis, false, \ + "Cache os::javaTimeMillis with CacheTimeMillisGranularity") \ + \ + diagnostic(uintx, CacheTimeMillisGranularity, 50, \ + "Granularity for CacheTimeMillis") \ + \ + develop(bool, TraceItables, false, \ + "Trace initialization and use of itables") \ + \ + develop(bool, TracePcPatching, false, \ + "Trace usage of frame::patch_pc") \ + \ + develop(bool, TraceJumps, false, \ + "Trace assembly jumps in thread ring buffer") \ + \ + develop(bool, TraceRelocator, false, \ + "Trace the bytecode relocator") \ + \ + develop(bool, TraceLongCompiles, false, \ + "Print out every time compilation is longer than " \ + "a given threashold") \ + \ + develop(bool, SafepointALot, false, \ + "Generates a lot of safepoints. Works with " \ + "GuaranteedSafepointInterval") \ + \ + product_pd(bool, BackgroundCompilation, \ + "A thread requesting compilation is not blocked during " \ + "compilation") \ + \ + product(bool, PrintVMQWaitTime, false, \ + "Prints out the waiting time in VM operation queue") \ + \ + develop(bool, BailoutToInterpreterForThrows, false, \ + "Compiled methods which throws/catches exceptions will be " \ + "deopt and intp.") \ + \ + develop(bool, NoYieldsInMicrolock, false, \ + "Disable yields in microlock") \ + \ + develop(bool, TraceOopMapGeneration, false, \ + "Shows oopmap generation") \ + \ + product(bool, MethodFlushing, true, \ + "Reclamation of zombie and not-entrant methods") \ + \ + develop(bool, VerifyStack, false, \ + "Verify stack of each thread when it is entering a runtime call") \ + \ + develop(bool, ForceUnreachable, false, \ + "(amd64) Make all non code cache addresses to be unreachable with rip-rel forcing use of 64bit literal fixups") \ + \ + notproduct(bool, StressDerivedPointers, false, \ + "Force scavenge when a derived pointers is detected on stack " \ + "after rtm call") \ + \ + develop(bool, TraceDerivedPointers, false, \ + "Trace traversal of derived pointers on stack") \ + \ + notproduct(bool, TraceCodeBlobStacks, false, \ + "Trace stack-walk of codeblobs") \ + \ + product(bool, PrintJNIResolving, false, \ + "Used to implement -v:jni") \ + \ + notproduct(bool, PrintRewrites, false, \ + "Print methods that are being rewritten") \ + \ + product(bool, UseInlineCaches, true, \ + "Use Inline Caches for virtual calls ") \ + \ + develop(bool, InlineArrayCopy, true, \ + "inline arraycopy native that is known to be part of " \ + "base library DLL") \ + \ + develop(bool, InlineObjectHash, true, \ + "inline Object::hashCode() native that is known to be part " \ + "of base library DLL") \ + \ + develop(bool, InlineObjectCopy, true, \ + "inline Object.clone and Arrays.copyOf[Range] intrinsics") \ + \ + develop(bool, InlineNatives, true, \ + "inline natives that are known to be part of base library DLL") \ + \ + develop(bool, InlineMathNatives, true, \ + "inline SinD, CosD, etc.") \ + \ + develop(bool, InlineClassNatives, true, \ + "inline Class.isInstance, etc") \ + \ + develop(bool, InlineAtomicLong, true, \ + "inline sun.misc.AtomicLong") \ + \ + develop(bool, InlineThreadNatives, true, \ + "inline Thread.currentThread, etc") \ + \ + develop(bool, InlineReflectionGetCallerClass, true, \ + "inline sun.reflect.Reflection.getCallerClass(), known to be part "\ + "of base library DLL") \ + \ + develop(bool, InlineUnsafeOps, true, \ + "inline memory ops (native methods) from sun.misc.Unsafe") \ + \ + develop(bool, ConvertCmpD2CmpF, true, \ + "Convert cmpD to cmpF when one input is constant in float range") \ + \ + develop(bool, ConvertFloat2IntClipping, true, \ + "Convert float2int clipping idiom to integer clipping") \ + \ + develop(bool, SpecialStringCompareTo, true, \ + "special version of string compareTo") \ + \ + develop(bool, SpecialStringIndexOf, true, \ + "special version of string indexOf") \ + \ + develop(bool, TraceCallFixup, false, \ + "traces all call fixups") \ + \ + develop(bool, DeoptimizeALot, false, \ + "deoptimize at every exit from the runtime system") \ + \ + develop(ccstrlist, DeoptimizeOnlyAt, "", \ + "a comma separated list of bcis to deoptimize at") \ + \ + product(bool, DeoptimizeRandom, false, \ + "deoptimize random frames on random exit from the runtime system")\ + \ + notproduct(bool, ZombieALot, false, \ + "creates zombies (non-entrant) at exit from the runt. system") \ + \ + notproduct(bool, WalkStackALot, false, \ + "trace stack (no print) at every exit from the runtime system") \ + \ + develop(bool, Debugging, false, \ + "set when executing debug methods in debug.ccp " \ + "(to prevent triggering assertions)") \ + \ + notproduct(bool, StrictSafepointChecks, trueInDebug, \ + "Enable strict checks that safepoints cannot happen for threads " \ + "that used No_Safepoint_Verifier") \ + \ + notproduct(bool, VerifyLastFrame, false, \ + "Verify oops on last frame on entry to VM") \ + \ + develop(bool, TraceHandleAllocation, false, \ + "Prints out warnings when suspicious many handles are allocated") \ + \ + product(bool, UseCompilerSafepoints, true, \ + "Stop at safepoints in compiled code") \ + \ + product(bool, UseSplitVerifier, true, \ + "use split verifier with StackMapTable attributes") \ + \ + product(bool, FailOverToOldVerifier, true, \ + "fail over to old verifier when split verifier fails") \ + \ + develop(bool, ShowSafepointMsgs, false, \ + "Show msg. about safepoint synch.") \ + \ + product(bool, SafepointTimeout, false, \ + "Time out and warn or fail after SafepointTimeoutDelay " \ + "milliseconds if failed to reach safepoint") \ + \ + develop(bool, DieOnSafepointTimeout, false, \ + "Die upon failure to reach safepoint (see SafepointTimeout)") \ + \ + /* 50 retries * (5 * current_retry_count) millis = ~6.375 seconds */ \ + /* typically, at most a few retries are needed */ \ + product(intx, SuspendRetryCount, 50, \ + "Maximum retry count for an external suspend request") \ + \ + product(intx, SuspendRetryDelay, 5, \ + "Milliseconds to delay per retry (* current_retry_count)") \ + \ + product(bool, AssertOnSuspendWaitFailure, false, \ + "Assert/Guarantee on external suspend wait failure") \ + \ + product(bool, TraceSuspendWaitFailures, false, \ + "Trace external suspend wait failures") \ + \ + product(bool, MaxFDLimit, true, \ + "Bump the number of file descriptors to max in solaris.") \ + \ + notproduct(bool, LogEvents, trueInDebug, \ + "Enable Event log") \ + \ + product(bool, BytecodeVerificationRemote, true, \ + "Enables the Java bytecode verifier for remote classes") \ + \ + product(bool, BytecodeVerificationLocal, false, \ + "Enables the Java bytecode verifier for local classes") \ + \ + develop(bool, ForceFloatExceptions, trueInDebug, \ + "Force exceptions on FP stack under/overflow") \ + \ + develop(bool, SoftMatchFailure, trueInProduct, \ + "If the DFA fails to match a node, print a message and bail out") \ + \ + develop(bool, VerifyStackAtCalls, false, \ + "Verify that the stack pointer is unchanged after calls") \ + \ + develop(bool, TraceJavaAssertions, false, \ + "Trace java language assertions") \ + \ + notproduct(bool, CheckAssertionStatusDirectives, false, \ + "temporary - see javaClasses.cpp") \ + \ + notproduct(bool, PrintMallocFree, false, \ + "Trace calls to C heap malloc/free allocation") \ + \ + notproduct(bool, PrintOopAddress, false, \ + "Always print the location of the oop") \ + \ + notproduct(bool, VerifyCodeCacheOften, false, \ + "Verify compiled-code cache often") \ + \ + develop(bool, ZapDeadCompiledLocals, false, \ + "Zap dead locals in compiler frames") \ + \ + notproduct(bool, ZapDeadLocalsOld, false, \ + "Zap dead locals (old version, zaps all frames when " \ + "entering the VM") \ + \ + notproduct(bool, CheckOopishValues, false, \ + "Warn if value contains oop ( requires ZapDeadLocals)") \ + \ + develop(bool, UseMallocOnly, false, \ + "use only malloc/free for allocation (no resource area/arena)") \ + \ + develop(bool, PrintMalloc, false, \ + "print all malloc/free calls") \ + \ + develop(bool, ZapResourceArea, trueInDebug, \ + "Zap freed resource/arena space with 0xABABABAB") \ + \ + notproduct(bool, ZapVMHandleArea, trueInDebug, \ + "Zap freed VM handle space with 0xBCBCBCBC") \ + \ + develop(bool, ZapJNIHandleArea, trueInDebug, \ + "Zap freed JNI handle space with 0xFEFEFEFE") \ + \ + develop(bool, ZapUnusedHeapArea, trueInDebug, \ + "Zap unused heap space with 0xBAADBABE") \ + \ + develop(bool, PrintVMMessages, true, \ + "Print vm messages on console") \ + \ + product(bool, PrintGCApplicationConcurrentTime, false, \ + "Print the time the application has been running") \ + \ + product(bool, PrintGCApplicationStoppedTime, false, \ + "Print the time the application has been stopped") \ + \ + develop(bool, Verbose, false, \ + "Prints additional debugging information from other modes") \ + \ + develop(bool, PrintMiscellaneous, false, \ + "Prints uncategorized debugging information (requires +Verbose)") \ + \ + develop(bool, WizardMode, false, \ + "Prints much more debugging information") \ + \ + product(bool, ShowMessageBoxOnError, false, \ + "Keep process alive on VM fatal error") \ + \ + product_pd(bool, UseOSErrorReporting, \ + "Let VM fatal error propagate to the OS (ie. WER on Windows)") \ + \ + product(bool, SuppressFatalErrorMessage, false, \ + "Do NO Fatal Error report [Avoid deadlock]") \ + \ + product(ccstrlist, OnError, "", \ + "Run user-defined commands on fatal error; see VMError.cpp " \ + "for examples") \ + \ + product(ccstrlist, OnOutOfMemoryError, "", \ + "Run user-defined commands on first java.lang.OutOfMemoryError") \ + \ + manageable(bool, HeapDumpOnOutOfMemoryError, false, \ + "Dump heap to file when java.lang.OutOfMemoryError is thrown") \ + \ + manageable(ccstr, HeapDumpPath, NULL, \ + "When HeapDumpOnOutOfMemoryError is on, the path (filename or" \ + "directory) of the dump file (defaults to java_pid.hprof" \ + "in the working directory)") \ + \ + develop(uintx, SegmentedHeapDumpThreshold, 2*G, \ + "Generate a segmented heap dump (JAVA PROFILE 1.0.2 format) " \ + "when the heap usage is larger than this") \ + \ + develop(uintx, HeapDumpSegmentSize, 1*G, \ + "Approximate segment size when generating a segmented heap dump") \ + \ + develop(bool, BreakAtWarning, false, \ + "Execute breakpoint upon encountering VM warning") \ + \ + product_pd(bool, UseVectoredExceptions, \ + "Temp Flag - Use Vectored Exceptions rather than SEH (Windows Only)") \ + \ + develop(bool, TraceVMOperation, false, \ + "Trace vm operations") \ + \ + develop(bool, UseFakeTimers, false, \ + "Tells whether the VM should use system time or a fake timer") \ + \ + diagnostic(bool, LogCompilation, false, \ + "Log compilation activity in detail to hotspot.log or LogFile") \ + \ + product(bool, PrintCompilation, false, \ + "Print compilations") \ + \ + diagnostic(bool, TraceNMethodInstalls, false, \ + "Trace nmethod intallation") \ + \ + diagnostic(bool, TraceOSRBreakpoint, false, \ + "Trace OSR Breakpoint ") \ + \ + diagnostic(bool, TraceCompileTriggered, false, \ + "Trace compile triggered") \ + \ + diagnostic(bool, TraceTriggers, false, \ + "Trace triggers") \ + \ + product(bool, AlwaysRestoreFPU, false, \ + "Restore the FPU control word after every JNI call (expensive)") \ + \ + notproduct(bool, PrintCompilation2, false, \ + "Print additional statistics per compilation") \ + \ + notproduct(bool, PrintAdapterHandlers, false, \ + "Print code generated for i2c/c2i adapters") \ + \ + develop(bool, PrintAssembly, false, \ + "Print assembly code") \ + \ + develop(bool, PrintNMethods, false, \ + "Print assembly code for nmethods when generated") \ + \ + develop(bool, PrintNativeNMethods, false, \ + "Print assembly code for native nmethods when generated") \ + \ + develop(bool, PrintDebugInfo, false, \ + "Print debug information for all nmethods when generated") \ + \ + develop(bool, PrintRelocations, false, \ + "Print relocation information for all nmethods when generated") \ + \ + develop(bool, PrintDependencies, false, \ + "Print dependency information for all nmethods when generated") \ + \ + develop(bool, PrintExceptionHandlers, false, \ + "Print exception handler tables for all nmethods when generated") \ + \ + develop(bool, InterceptOSException, false, \ + "Starts debugger when an implicit OS (e.g., NULL) " \ + "exception happens") \ + \ + notproduct(bool, PrintCodeCache, false, \ + "Print the compiled_code cache when exiting") \ + \ + develop(bool, PrintCodeCache2, false, \ + "Print detailed info on the compiled_code cache when exiting") \ + \ + develop(bool, PrintStubCode, false, \ + "Print generated stub code") \ + \ + product(bool, StackTraceInThrowable, true, \ + "Collect backtrace in throwable when exception happens") \ + \ + product(bool, OmitStackTraceInFastThrow, true, \ + "Omit backtraces for some 'hot' exceptions in optimized code") \ + \ + product(bool, ProfilerPrintByteCodeStatistics, false, \ + "Prints byte code statictics when dumping profiler output") \ + \ + product(bool, ProfilerRecordPC, false, \ + "Collects tick for each 16 byte interval of compiled code") \ + \ + product(bool, ProfileVM, false, \ + "Profiles ticks that fall within VM (either in the VM Thread " \ + "or VM code called through stubs)") \ + \ + product(bool, ProfileIntervals, false, \ + "Prints profiles for each interval (see ProfileIntervalsTicks)") \ + \ + notproduct(bool, ProfilerCheckIntervals, false, \ + "Collect and print info on spacing of profiler ticks") \ + \ + develop(bool, PrintJVMWarnings, false, \ + "Prints warnings for unimplemented JVM functions") \ + \ + notproduct(uintx, WarnOnStalledSpinLock, 0, \ + "Prints warnings for stalled SpinLocks") \ + \ + develop(bool, InitializeJavaLangSystem, true, \ + "Initialize java.lang.System - turn off for individual " \ + "method debugging") \ + \ + develop(bool, InitializeJavaLangString, true, \ + "Initialize java.lang.String - turn off for individual " \ + "method debugging") \ + \ + develop(bool, InitializeJavaLangExceptionsErrors, true, \ + "Initialize various error and exception classes - turn off for " \ + "individual method debugging") \ + \ + product(bool, RegisterFinalizersAtInit, true, \ + "Register finalizable objects at end of Object. or " \ + "after allocation.") \ + \ + develop(bool, RegisterReferences, true, \ + "Tells whether the VM should register soft/weak/final/phantom " \ + "references") \ + \ + develop(bool, IgnoreRewrites, false, \ + "Supress rewrites of bytecodes in the oopmap generator. " \ + "This is unsafe!") \ + \ + develop(bool, PrintCodeCacheExtension, false, \ + "Print extension of code cache") \ + \ + develop(bool, UsePrivilegedStack, true, \ + "Enable the security JVM functions") \ + \ + develop(bool, IEEEPrecision, true, \ + "Enables IEEE precision (for INTEL only)") \ + \ + develop(bool, ProtectionDomainVerification, true, \ + "Verifies protection domain before resolution in system " \ + "dictionary") \ + \ + product(bool, ClassUnloading, true, \ + "Do unloading of classes") \ + \ + develop(bool, DisableStartThread, false, \ + "Disable starting of additional Java threads " \ + "(for debugging only)") \ + \ + develop(bool, MemProfiling, false, \ + "Write memory usage profiling to log file") \ + \ + notproduct(bool, PrintSystemDictionaryAtExit, false, \ + "Prints the system dictionary at exit") \ + \ + diagnostic(bool, UnsyncloadClass, false, \ + "Unstable: VM calls loadClass unsynchronized. Custom classloader "\ + "must call VM synchronized for findClass & defineClass") \ + \ + product_pd(bool, DontYieldALot, \ + "Throw away obvious excess yield calls (for SOLARIS only)") \ + \ + product_pd(bool, ConvertSleepToYield, \ + "Converts sleep(0) to thread yield " \ + "(may be off for SOLARIS to improve GUI)") \ + \ + product(bool, ConvertYieldToSleep, false, \ + "Converts yield to a sleep of MinSleepInterval to simulate Win32 "\ + "behavior (SOLARIS only)") \ + \ + product(bool, UseBoundThreads, true, \ + "Bind user level threads to kernel threads (for SOLARIS only)") \ + \ + develop(bool, UseDetachedThreads, true, \ + "Use detached threads that are recycled upon termination " \ + "(for SOLARIS only)") \ + \ + product(bool, UseLWPSynchronization, true, \ + "Use LWP-based instead of libthread-based synchronization " \ + "(SPARC only)") \ + \ + product(ccstr, SyncKnobs, "", \ + "(Unstable) Various monitor synchronization tunables") \ + \ + product(intx, EmitSync, 0, \ + "(Unsafe,Unstable) " \ + " Controls emission of inline sync fast-path code") \ + \ + product(intx, AlwaysInflate, 0, "(Unstable) Force inflation") \ + \ + product(intx, Atomics, 0, \ + "(Unsafe,Unstable) Diagnostic - Controls emission of atomics") \ + \ + product(intx, FenceInstruction, 0, \ + "(Unsafe,Unstable) Experimental") \ + \ + product(intx, SyncFlags, 0, "(Unsafe,Unstable) Experimental Sync flags" ) \ + \ + product(intx, SyncVerbose, 0, "(Unstable)" ) \ + \ + product(intx, ClearFPUAtPark, 0, "(Unsafe,Unstable)" ) \ + \ + product(intx, hashCode, 0, \ + "(Unstable) select hashCode generation algorithm" ) \ + \ + product(intx, WorkAroundNPTLTimedWaitHang, 1, \ + "(Unstable, Linux-specific)" \ + " avoid NPTL-FUTEX hang pthread_cond_timedwait" ) \ + \ + product(bool, FilterSpuriousWakeups , true, \ + "Prevent spurious or premature wakeups from object.wait" \ + "(Solaris only)") \ + \ + product(intx, NativeMonitorTimeout, -1, "(Unstable)" ) \ + product(intx, NativeMonitorFlags, 0, "(Unstable)" ) \ + product(intx, NativeMonitorSpinLimit, 20, "(Unstable)" ) \ + \ + develop(bool, UsePthreads, false, \ + "Use pthread-based instead of libthread-based synchronization " \ + "(SPARC only)") \ + \ + product(bool, AdjustConcurrency, false, \ + "call thr_setconcurrency at thread create time to avoid " \ + "LWP starvation on MP systems (For Solaris Only)") \ + \ + develop(bool, UpdateHotSpotCompilerFileOnError, true, \ + "Should the system attempt to update the compiler file when " \ + "an error occurs?") \ + \ + product(bool, ReduceSignalUsage, false, \ + "Reduce the use of OS signals in Java and/or the VM") \ + \ + notproduct(bool, ValidateMarkSweep, false, \ + "Do extra validation during MarkSweep collection") \ + \ + notproduct(bool, RecordMarkSweepCompaction, false, \ + "Enable GC-to-GC recording and querying of compaction during " \ + "MarkSweep") \ + \ + develop_pd(bool, ShareVtableStubs, \ + "Share vtable stubs (smaller code but worse branch prediction") \ + \ + develop(bool, LoadLineNumberTables, true, \ + "Tells whether the class file parser loads line number tables") \ + \ + develop(bool, LoadLocalVariableTables, true, \ + "Tells whether the class file parser loads local variable tables")\ + \ + develop(bool, LoadLocalVariableTypeTables, true, \ + "Tells whether the class file parser loads local variable type tables")\ + \ + product(bool, AllowUserSignalHandlers, false, \ + "Do not complain if the application installs signal handlers " \ + "(Solaris & Linux only)") \ + \ + product(bool, UseSignalChaining, true, \ + "Use signal-chaining to invoke signal handlers installed " \ + "by the application (Solaris & Linux only)") \ + \ + product(bool, UseAltSigs, false, \ + "Use alternate signals instead of SIGUSR1 & SIGUSR2 for VM " \ + "internal signals. (Solaris only)") \ + \ + product(bool, UseSpinning, false, \ + "Use spinning in monitor inflation and before entry") \ + \ + product(bool, PreSpinYield, false, \ + "Yield before inner spinning loop") \ + \ + product(bool, PostSpinYield, true, \ + "Yield after inner spinning loop") \ + \ + product(bool, AllowJNIEnvProxy, false, \ + "Allow JNIEnv proxies for jdbx") \ + \ + product(bool, JNIDetachReleasesMonitors, true, \ + "JNI DetachCurrentThread releases monitors owned by thread") \ + \ + product(bool, RestoreMXCSROnJNICalls, false, \ + "Restore MXCSR when returning from JNI calls") \ + \ + product(bool, CheckJNICalls, false, \ + "Verify all arguments to JNI calls") \ + \ + product(bool, UseFastJNIAccessors, true, \ + "Use optimized versions of GetField") \ + \ + product(bool, EagerXrunInit, false, \ + "Eagerly initialize -Xrun libraries; allows startup profiling, " \ + " but not all -Xrun libraries may support the state of the VM at this time") \ + \ + product(bool, PreserveAllAnnotations, false, \ + "Preserve RuntimeInvisibleAnnotations as well as RuntimeVisibleAnnotations") \ + \ + develop(uintx, PreallocatedOutOfMemoryErrorCount, 4, \ + "Number of OutOfMemoryErrors preallocated with backtrace") \ + \ + product(bool, LazyBootClassLoader, true, \ + "Enable/disable lazy opening of boot class path entries") \ + \ + diagnostic(bool, UseIncDec, true, \ + "Use INC, DEC instructions on x86") \ + \ + product(bool, UseStoreImmI16, true, \ + "Use store immediate 16-bits value instruction on x86") \ + \ + product(bool, UseAddressNop, false, \ + "Use '0F 1F [addr]' NOP instructions on x86 cpus") \ + \ + product(bool, UseXmmLoadAndClearUpper, true, \ + "Load low part of XMM register and clear upper part") \ + \ + product(bool, UseXmmRegToRegMoveAll, false, \ + "Copy all XMM register bits when moving value between registers") \ + \ + product(intx, FieldsAllocationStyle, 1, \ + "0 - type based with oops first, 1 - with oops last") \ + \ + product(bool, CompactFields, true, \ + "Allocate nonstatic fields in gaps between previous fields") \ + \ + notproduct(bool, PrintCompactFieldsSavings, false, \ + "Print how many words were saved with CompactFields") \ + \ + product(bool, UseBiasedLocking, true, \ + "Enable biased locking in JVM") \ + \ + product(intx, BiasedLockingStartupDelay, 4000, \ + "Number of milliseconds to wait before enabling biased locking") \ + \ + diagnostic(bool, PrintBiasedLockingStatistics, false, \ + "Print statistics of biased locking in JVM") \ + \ + product(intx, BiasedLockingBulkRebiasThreshold, 20, \ + "Threshold of number of revocations per type to try to " \ + "rebias all objects in the heap of that type") \ + \ + product(intx, BiasedLockingBulkRevokeThreshold, 40, \ + "Threshold of number of revocations per type to permanently " \ + "revoke biases of all objects in the heap of that type") \ + \ + product(intx, BiasedLockingDecayTime, 25000, \ + "Decay time (in milliseconds) to re-enable bulk rebiasing of a " \ + "type after previous bulk rebias") \ + \ + /* tracing */ \ + \ + notproduct(bool, TraceRuntimeCalls, false, \ + "Trace run-time calls") \ + \ + develop(bool, TraceJNICalls, false, \ + "Trace JNI calls") \ + \ + notproduct(bool, TraceJVMCalls, false, \ + "Trace JVM calls") \ + \ + product(ccstr, TraceJVMTI, "", \ + "Trace flags for JVMTI functions and events") \ + \ + /* This option can change an EMCP method into an obsolete method. */ \ + /* This can affect tests that except specific methods to be EMCP. */ \ + /* This option should be used with caution. */ \ + product(bool, StressLdcRewrite, false, \ + "Force ldc -> ldc_w rewrite during RedefineClasses") \ + \ + product(intx, TraceRedefineClasses, 0, \ + "Trace level for JVMTI RedefineClasses") \ + \ + /* change to false by default sometime after Mustang */ \ + product(bool, VerifyMergedCPBytecodes, true, \ + "Verify bytecodes after RedefineClasses constant pool merging") \ + \ + develop(bool, TraceJNIHandleAllocation, false, \ + "Trace allocation/deallocation of JNI handle blocks") \ + \ + develop(bool, TraceThreadEvents, false, \ + "Trace all thread events") \ + \ + develop(bool, TraceBytecodes, false, \ + "Trace bytecode execution") \ + \ + develop(bool, TraceClassInitialization, false, \ + "Trace class initialization") \ + \ + develop(bool, TraceExceptions, false, \ + "Trace exceptions") \ + \ + develop(bool, TraceICs, false, \ + "Trace inline cache changes") \ + \ + notproduct(bool, TraceInvocationCounterOverflow, false, \ + "Trace method invocation counter overflow") \ + \ + develop(bool, TraceInlineCacheClearing, false, \ + "Trace clearing of inline caches in nmethods") \ + \ + develop(bool, TraceDependencies, false, \ + "Trace dependencies") \ + \ + develop(bool, VerifyDependencies, trueInDebug, \ + "Exercise and verify the compilation dependency mechanism") \ + \ + develop(bool, TraceNewOopMapGeneration, false, \ + "Trace OopMapGeneration") \ + \ + develop(bool, TraceNewOopMapGenerationDetailed, false, \ + "Trace OopMapGeneration: print detailed cell states") \ + \ + develop(bool, TimeOopMap, false, \ + "Time calls to GenerateOopMap::compute_map() in sum") \ + \ + develop(bool, TimeOopMap2, false, \ + "Time calls to GenerateOopMap::compute_map() individually") \ + \ + develop(bool, TraceMonitorMismatch, false, \ + "Trace monitor matching failures during OopMapGeneration") \ + \ + develop(bool, TraceOopMapRewrites, false, \ + "Trace rewritting of method oops during oop map generation") \ + \ + develop(bool, TraceSafepoint, false, \ + "Trace safepoint operations") \ + \ + develop(bool, TraceICBuffer, false, \ + "Trace usage of IC buffer") \ + \ + develop(bool, TraceCompiledIC, false, \ + "Trace changes of compiled IC") \ + \ + notproduct(bool, TraceZapDeadLocals, false, \ + "Trace zapping dead locals") \ + \ + develop(bool, TraceStartupTime, false, \ + "Trace setup time") \ + \ + develop(bool, TraceHPI, false, \ + "Trace Host Porting Interface (HPI)") \ + \ + product(ccstr, HPILibPath, NULL, \ + "Specify alternate path to HPI library") \ + \ + develop(bool, TraceProtectionDomainVerification, false, \ + "Trace protection domain verifcation") \ + \ + develop(bool, TraceClearedExceptions, false, \ + "Prints when an exception is forcibly cleared") \ + \ + product(bool, TraceClassResolution, false, \ + "Trace all constant pool resolutions (for debugging)") \ + \ + product(bool, TraceBiasedLocking, false, \ + "Trace biased locking in JVM") \ + \ + product(bool, TraceMonitorInflation, false, \ + "Trace monitor inflation in JVM") \ + \ + /* assembler */ \ + product(bool, Use486InstrsOnly, false, \ + "Use 80486 Compliant instruction subset") \ + \ + /* gc */ \ + \ + product(bool, UseSerialGC, false, \ + "Tells whether the VM should use serial garbage collector") \ + \ + product(bool, UseParallelGC, false, \ + "Use the Parallel Scavenge garbage collector") \ + \ + product(bool, UseParallelOldGC, false, \ + "Use the Parallel Old garbage collector") \ + \ + product(bool, UseParallelOldGCCompacting, true, \ + "In the Parallel Old garbage collector use parallel compaction") \ + \ + product(bool, UseParallelDensePrefixUpdate, true, \ + "In the Parallel Old garbage collector use parallel dense" \ + " prefix update") \ + \ + develop(bool, UseParallelOldGCChunkPointerCalc, true, \ + "In the Parallel Old garbage collector use chucks to calculate" \ + " new object locations") \ + \ + product(uintx, HeapMaximumCompactionInterval, 20, \ + "How often should we maximally compact the heap (not allowing " \ + "any dead space)") \ + \ + product(uintx, HeapFirstMaximumCompactionCount, 3, \ + "The collection count for the first maximum compaction") \ + \ + product(bool, UseMaximumCompactionOnSystemGC, true, \ + "In the Parallel Old garbage collector maximum compaction for " \ + "a system GC") \ + \ + product(uintx, ParallelOldDeadWoodLimiterMean, 50, \ + "The mean used by the par compact dead wood" \ + "limiter (a number between 0-100).") \ + \ + product(uintx, ParallelOldDeadWoodLimiterStdDev, 80, \ + "The standard deviation used by the par compact dead wood" \ + "limiter (a number between 0-100).") \ + \ + product(bool, UseParallelOldGCDensePrefix, true, \ + "Use a dense prefix with the Parallel Old garbage collector") \ + \ + product(uintx, ParallelGCThreads, 0, \ + "Number of parallel threads parallel gc will use") \ + \ + product(uintx, ParallelCMSThreads, 0, \ + "Max number of threads CMS will use for concurrent work") \ + \ + develop(bool, VerifyParallelOldWithMarkSweep, false, \ + "Use the MarkSweep code to verify phases of Parallel Old") \ + \ + develop(uintx, VerifyParallelOldWithMarkSweepInterval, 1, \ + "Interval at which the MarkSweep code is used to verify " \ + "phases of Parallel Old") \ + \ + develop(bool, ParallelOldMTUnsafeMarkBitMap, false, \ + "Use the Parallel Old MT unsafe in marking the bitmap") \ + \ + develop(bool, ParallelOldMTUnsafeUpdateLiveData, false, \ + "Use the Parallel Old MT unsafe in update of live size") \ + \ + develop(bool, TraceChunkTasksQueuing, false, \ + "Trace the queuing of the chunk tasks") \ + \ + product(uintx, YoungPLABSize, 4096, \ + "Size of young gen promotion labs (in HeapWords)") \ + \ + product(uintx, OldPLABSize, 1024, \ + "Size of old gen promotion labs (in HeapWords)") \ + \ + product(uintx, GCTaskTimeStampEntries, 200, \ + "Number of time stamp entries per gc worker thread") \ + \ + product(bool, AlwaysTenure, false, \ + "Always tenure objects in eden. (ParallelGC only)") \ + \ + product(bool, NeverTenure, false, \ + "Never tenure objects in eden, May tenure on overflow" \ + " (ParallelGC only)") \ + \ + product(bool, ScavengeBeforeFullGC, true, \ + "Scavenge youngest generation before each full GC," \ + " used with UseParallelGC") \ + \ + develop(bool, ScavengeWithObjectsInToSpace, false, \ + "Allow scavenges to occur when to_space contains objects.") \ + \ + product(bool, UseConcMarkSweepGC, false, \ + "Use Concurrent Mark-Sweep GC in the old generation") \ + \ + product(bool, ExplicitGCInvokesConcurrent, false, \ + "A System.gc() request invokes a concurrent collection;" \ + " (effective only when UseConcMarkSweepGC)") \ + \ + product(bool, ExplicitGCInvokesConcurrentAndUnloadsClasses, false, \ + "A System.gc() request invokes a concurrent collection and" \ + " also unloads classes during such a concurrent gc cycle " \ + " (effective only when UseConcMarkSweepGC)") \ + \ + develop(bool, UseCMSAdaptiveFreeLists, true, \ + "Use Adaptive Free Lists in the CMS generation") \ + \ + develop(bool, UseAsyncConcMarkSweepGC, true, \ + "Use Asynchronous Concurrent Mark-Sweep GC in the old generation")\ + \ + develop(bool, RotateCMSCollectionTypes, false, \ + "Rotate the CMS collections among concurrent and STW") \ + \ + product(bool, UseCMSBestFit, true, \ + "Use CMS best fit allocation strategy") \ + \ + product(bool, UseCMSCollectionPassing, true, \ + "Use passing of collection from background to foreground") \ + \ + product(bool, UseParNewGC, false, \ + "Use parallel threads in the new generation.") \ + \ + product(bool, ParallelGCVerbose, false, \ + "Verbose output for parallel GC.") \ + \ + product(intx, ParallelGCBufferWastePct, 10, \ + "wasted fraction of parallel allocation buffer.") \ + \ + product(bool, ParallelGCRetainPLAB, true, \ + "Retain parallel allocation buffers across scavenges.") \ + \ + product(intx, TargetPLABWastePct, 10, \ + "target wasted space in last buffer as pct of overall allocation")\ + \ + product(uintx, PLABWeight, 75, \ + "Percentage (0-100) used to weight the current sample when" \ + "computing exponentially decaying average for ResizePLAB.") \ + \ + product(bool, ResizePLAB, true, \ + "Dynamically resize (survivor space) promotion labs") \ + \ + product(bool, PrintPLAB, false, \ + "Print (survivor space) promotion labs sizing decisions") \ + \ + product(intx, ParGCArrayScanChunk, 50, \ + "Scan a subset and push remainder, if array is bigger than this") \ + \ + product(intx, ParGCDesiredObjsFromOverflowList, 20, \ + "The desired number of objects to claim from the overflow list") \ + \ + product(uintx, CMSParPromoteBlocksToClaim, 50, \ + "Number of blocks to attempt to claim when refilling CMS LAB for "\ + "parallel GC.") \ + \ + product(bool, AlwaysPreTouch, false, \ + "It forces all freshly committed pages to be pre-touched.") \ + \ + product(bool, CMSUseOldDefaults, false, \ + "A flag temporarily introduced to allow reverting to some older" \ + "default settings; older as of 6.0 ") \ + \ + product(intx, CMSYoungGenPerWorker, 16*M, \ + "The amount of young gen chosen by default per GC worker " \ + "thread available ") \ + \ + product(bool, CMSIncrementalMode, false, \ + "Whether CMS GC should operate in \"incremental\" mode") \ + \ + product(uintx, CMSIncrementalDutyCycle, 10, \ + "CMS incremental mode duty cycle (a percentage, 0-100). If" \ + "CMSIncrementalPacing is enabled, then this is just the initial" \ + "value") \ + \ + product(bool, CMSIncrementalPacing, true, \ + "Whether the CMS incremental mode duty cycle should be " \ + "automatically adjusted") \ + \ + product(uintx, CMSIncrementalDutyCycleMin, 0, \ + "Lower bound on the duty cycle when CMSIncrementalPacing is" \ + "enabled (a percentage, 0-100).") \ + \ + product(uintx, CMSIncrementalSafetyFactor, 10, \ + "Percentage (0-100) used to add conservatism when computing the" \ + "duty cycle.") \ + \ + product(uintx, CMSIncrementalOffset, 0, \ + "Percentage (0-100) by which the CMS incremental mode duty cycle" \ + "is shifted to the right within the period between young GCs") \ + \ + product(uintx, CMSExpAvgFactor, 25, \ + "Percentage (0-100) used to weight the current sample when" \ + "computing exponential averages for CMS statistics.") \ + \ + product(uintx, CMS_FLSWeight, 50, \ + "Percentage (0-100) used to weight the current sample when" \ + "computing exponentially decating averages for CMS FLS statistics.") \ + \ + product(uintx, CMS_FLSPadding, 2, \ + "The multiple of deviation from mean to use for buffering" \ + "against volatility in free list demand.") \ + \ + product(uintx, FLSCoalescePolicy, 2, \ + "CMS: Aggression level for coalescing, increasing from 0 to 4") \ + \ + product(uintx, CMS_SweepWeight, 50, \ + "Percentage (0-100) used to weight the current sample when" \ + "computing exponentially decaying average for inter-sweep duration.") \ + \ + product(uintx, CMS_SweepPadding, 2, \ + "The multiple of deviation from mean to use for buffering" \ + "against volatility in inter-sweep duration.") \ + \ + product(uintx, CMS_SweepTimerThresholdMillis, 10, \ + "Skip block flux-rate sampling for an epoch unless inter-sweep " \ + " duration exceeds this threhold in milliseconds") \ + \ + develop(bool, CMSTraceIncrementalMode, false, \ + "Trace CMS incremental mode") \ + \ + develop(bool, CMSTraceIncrementalPacing, false, \ + "Trace CMS incremental mode pacing computation") \ + \ + develop(bool, CMSTraceThreadState, false, \ + "Trace the CMS thread state (enable the trace_state() method)") \ + \ + product(bool, CMSClassUnloadingEnabled, false, \ + "Whether class unloading enabled when using CMS GC") \ + \ + product(bool, CMSCompactWhenClearAllSoftRefs, true, \ + "Compact when asked to collect CMS gen with clear_all_soft_refs") \ + \ + product(bool, UseCMSCompactAtFullCollection, true, \ + "Use mark sweep compact at full collections") \ + \ + product(uintx, CMSFullGCsBeforeCompaction, 0, \ + "Number of CMS full collection done before compaction if > 0") \ + \ + develop(intx, CMSDictionaryChoice, 0, \ + "Use BinaryTreeDictionary as default in the CMS generation") \ + \ + product(uintx, CMSIndexedFreeListReplenish, 4, \ + "Replenish and indexed free list with this number of chunks") \ + \ + product(bool, CMSLoopWarn, false, \ + "Warn in case of excessive CMS looping") \ + \ + develop(bool, CMSOverflowEarlyRestoration, false, \ + "Whether preserved marks should be restored early") \ + \ + product(uintx, CMSMarkStackSize, 32*K, \ + "Size of CMS marking stack") \ + \ + product(uintx, CMSMarkStackSizeMax, 4*M, \ + "Max size of CMS marking stack") \ + \ + notproduct(bool, CMSMarkStackOverflowALot, false, \ + "Whether we should simulate frequent marking stack / work queue" \ + " overflow") \ + \ + notproduct(intx, CMSMarkStackOverflowInterval, 1000, \ + "A per-thread `interval' counter that determines how frequently" \ + " we simulate overflow; a smaller number increases frequency") \ + \ + product(uintx, CMSMaxAbortablePrecleanLoops, 0, \ + "(Temporary, subject to experimentation)" \ + "Maximum number of abortable preclean iterations, if > 0") \ + \ + product(intx, CMSMaxAbortablePrecleanTime, 5000, \ + "(Temporary, subject to experimentation)" \ + "Maximum time in abortable preclean in ms") \ + \ + product(uintx, CMSAbortablePrecleanMinWorkPerIteration, 100, \ + "(Temporary, subject to experimentation)" \ + "Nominal minimum work per abortable preclean iteration") \ + \ + product(intx, CMSAbortablePrecleanWaitMillis, 100, \ + "(Temporary, subject to experimentation)" \ + " Time that we sleep between iterations when not given" \ + " enough work per iteration") \ + \ + product(uintx, CMSRescanMultiple, 32, \ + "Size (in cards) of CMS parallel rescan task") \ + \ + product(uintx, CMSConcMarkMultiple, 32, \ + "Size (in cards) of CMS concurrent MT marking task") \ + \ + product(uintx, CMSRevisitStackSize, 1*M, \ + "Size of CMS KlassKlass revisit stack") \ + \ + product(bool, CMSAbortSemantics, false, \ + "Whether abort-on-overflow semantics is implemented") \ + \ + product(bool, CMSParallelRemarkEnabled, true, \ + "Whether parallel remark enabled (only if ParNewGC)") \ + \ + product(bool, CMSParallelSurvivorRemarkEnabled, true, \ + "Whether parallel remark of survivor space" \ + " enabled (effective only if CMSParallelRemarkEnabled)") \ + \ + product(bool, CMSPLABRecordAlways, true, \ + "Whether to always record survivor space PLAB bdries" \ + " (effective only if CMSParallelSurvivorRemarkEnabled)") \ + \ + product(bool, CMSConcurrentMTEnabled, true, \ + "Whether multi-threaded concurrent work enabled (if ParNewGC)") \ + \ + product(bool, CMSPermGenPrecleaningEnabled, true, \ + "Whether concurrent precleaning enabled in perm gen" \ + " (effective only when CMSPrecleaningEnabled is true)") \ + \ + product(bool, CMSPrecleaningEnabled, true, \ + "Whether concurrent precleaning enabled") \ + \ + product(uintx, CMSPrecleanIter, 3, \ + "Maximum number of precleaning iteration passes") \ + \ + product(uintx, CMSPrecleanNumerator, 2, \ + "CMSPrecleanNumerator:CMSPrecleanDenominator yields convergence" \ + " ratio") \ + \ + product(uintx, CMSPrecleanDenominator, 3, \ + "CMSPrecleanNumerator:CMSPrecleanDenominator yields convergence" \ + " ratio") \ + \ + product(bool, CMSPrecleanRefLists1, true, \ + "Preclean ref lists during (initial) preclean phase") \ + \ + product(bool, CMSPrecleanRefLists2, false, \ + "Preclean ref lists during abortable preclean phase") \ + \ + product(bool, CMSPrecleanSurvivors1, false, \ + "Preclean survivors during (initial) preclean phase") \ + \ + product(bool, CMSPrecleanSurvivors2, true, \ + "Preclean survivors during abortable preclean phase") \ + \ + product(uintx, CMSPrecleanThreshold, 1000, \ + "Don't re-iterate if #dirty cards less than this") \ + \ + product(bool, CMSCleanOnEnter, true, \ + "Clean-on-enter optimization for reducing number of dirty cards") \ + \ + product(uintx, CMSRemarkVerifyVariant, 1, \ + "Choose variant (1,2) of verification following remark") \ + \ + product(uintx, CMSScheduleRemarkEdenSizeThreshold, 2*M, \ + "If Eden used is below this value, don't try to schedule remark") \ + \ + product(uintx, CMSScheduleRemarkEdenPenetration, 50, \ + "The Eden occupancy % at which to try and schedule remark pause") \ + \ + product(uintx, CMSScheduleRemarkSamplingRatio, 5, \ + "Start sampling Eden top at least before yg occupancy reaches" \ + " 1/ of the size at which we plan to schedule remark") \ + \ + product(uintx, CMSSamplingGrain, 16*K, \ + "The minimum distance between eden samples for CMS (see above)") \ + \ + product(bool, CMSScavengeBeforeRemark, false, \ + "Attempt scavenge before the CMS remark step") \ + \ + develop(bool, CMSTraceSweeper, false, \ + "Trace some actions of the CMS sweeper") \ + \ + product(uintx, CMSWorkQueueDrainThreshold, 10, \ + "Don't drain below this size per parallel worker/thief") \ + \ + product(intx, CMSWaitDuration, 2000, \ + "Time in milliseconds that CMS thread waits for young GC") \ + \ + product(bool, CMSYield, true, \ + "Yield between steps of concurrent mark & sweep") \ + \ + product(uintx, CMSBitMapYieldQuantum, 10*M, \ + "Bitmap operations should process at most this many bits" \ + "between yields") \ + \ + diagnostic(bool, FLSVerifyAllHeapReferences, false, \ + "Verify that all refs across the FLS boundary " \ + " are to valid objects") \ + \ + diagnostic(bool, FLSVerifyLists, false, \ + "Do lots of (expensive) FreeListSpace verification") \ + \ + diagnostic(bool, FLSVerifyIndexTable, false, \ + "Do lots of (expensive) FLS index table verification") \ + \ + develop(bool, FLSVerifyDictionary, false, \ + "Do lots of (expensive) FLS dictionary verification") \ + \ + develop(bool, VerifyBlockOffsetArray, false, \ + "Do (expensive!) block offset array verification") \ + \ + product(bool, BlockOffsetArrayUseUnallocatedBlock, trueInDebug, \ + "Maintain _unallocated_block in BlockOffsetArray" \ + " (currently applicable only to CMS collector)") \ + \ + develop(bool, TraceCMSState, false, \ + "Trace the state of the CMS collection") \ + \ + product(intx, RefDiscoveryPolicy, 0, \ + "Whether reference-based(0) or referent-based(1)") \ + \ + product(bool, ParallelRefProcEnabled, false, \ + "Enable parallel reference processing whenever possible") \ + \ + product(bool, ParallelRefProcBalancingEnabled, true, \ + "Enable balancing of reference processing queues") \ + \ + product(intx, CMSTriggerRatio, 80, \ + "Percentage of MinHeapFreeRatio in CMS generation that is " \ + " allocated before a CMS collection cycle commences") \ + \ + product(intx, CMSBootstrapOccupancy, 50, \ + "Percentage CMS generation occupancy at which to " \ + " initiate CMS collection for bootstrapping collection stats") \ + \ + product(intx, CMSInitiatingOccupancyFraction, -1, \ + "Percentage CMS generation occupancy to start a CMS collection " \ + " cycle (A negative value means that CMSTirggerRatio is used)") \ + \ + product(bool, UseCMSInitiatingOccupancyOnly, false, \ + "Only use occupancy as a crierion for starting a CMS collection") \ + \ + develop(bool, CMSTestInFreeList, false, \ + "Check if the coalesced range is already in the " \ + "free lists as claimed.") \ + \ + notproduct(bool, CMSVerifyReturnedBytes, false, \ + "Check that all the garbage collected was returned to the " \ + "free lists.") \ + \ + notproduct(bool, ScavengeALot, false, \ + "Force scavenge at every Nth exit from the runtime system " \ + "(N=ScavengeALotInterval)") \ + \ + develop(bool, FullGCALot, false, \ + "Force full gc at every Nth exit from the runtime system " \ + "(N=FullGCALotInterval)") \ + \ + notproduct(bool, GCALotAtAllSafepoints, false, \ + "Enforce ScavengeALot/GCALot at all potential safepoints") \ + \ + product(bool, HandlePromotionFailure, true, \ + "The youngest generation collection does not require" \ + " a guarantee of full promotion of all live objects.") \ + \ + notproduct(bool, PromotionFailureALot, false, \ + "Use promotion failure handling on every youngest generation " \ + "collection") \ + \ + develop(uintx, PromotionFailureALotCount, 1000, \ + "Number of promotion failures occurring at ParGCAllocBuffer" \ + "refill attempts (ParNew) or promotion attempts " \ + "(other young collectors) ") \ + \ + develop(uintx, PromotionFailureALotInterval, 5, \ + "Total collections between promotion failures alot") \ + \ + develop(intx, WorkStealingSleepMillis, 1, \ + "Sleep time when sleep is used for yields") \ + \ + develop(uintx, WorkStealingYieldsBeforeSleep, 1000, \ + "Number of yields before a sleep is done during workstealing") \ + \ + product(uintx, PreserveMarkStackSize, 40, \ + "Size for stack used in promotion failure handling") \ + \ + product_pd(bool, UseTLAB, "Use thread-local object allocation") \ + \ + product_pd(bool, ResizeTLAB, \ + "Dynamically resize tlab size for threads") \ + \ + product(bool, ZeroTLAB, false, \ + "Zero out the newly created TLAB") \ + \ + product(bool, PrintTLAB, false, \ + "Print various TLAB related information") \ + \ + product(bool, TLABStats, true, \ + "Print various TLAB related information") \ + \ + product_pd(bool, NeverActAsServerClassMachine, \ + "Never act like a server-class machine") \ + \ + product(bool, AlwaysActAsServerClassMachine, false, \ + "Always act like a server-class machine") \ + \ + product_pd(uintx, DefaultMaxRAM, \ + "Maximum real memory size for setting server class heap size") \ + \ + product(uintx, DefaultMaxRAMFraction, 4, \ + "Fraction (1/n) of real memory used for server class max heap") \ + \ + product(uintx, DefaultInitialRAMFraction, 64, \ + "Fraction (1/n) of real memory used for server class initial heap") \ + \ + product(bool, UseAutoGCSelectPolicy, false, \ + "Use automatic collection selection policy") \ + \ + product(uintx, AutoGCSelectPauseMillis, 5000, \ + "Automatic GC selection pause threshhold in ms") \ + \ + product(bool, UseAdaptiveSizePolicy, true, \ + "Use adaptive generation sizing policies") \ + \ + product(bool, UsePSAdaptiveSurvivorSizePolicy, true, \ + "Use adaptive survivor sizing policies") \ + \ + product(bool, UseAdaptiveGenerationSizePolicyAtMinorCollection, true, \ + "Use adaptive young-old sizing policies at minor collections") \ + \ + product(bool, UseAdaptiveGenerationSizePolicyAtMajorCollection, true, \ + "Use adaptive young-old sizing policies at major collections") \ + \ + product(bool, UseAdaptiveSizePolicyWithSystemGC, false, \ + "Use statistics from System.GC for adaptive size policy") \ + \ + product(bool, UseAdaptiveGCBoundary, false, \ + "Allow young-old boundary to move") \ + \ + develop(bool, TraceAdaptiveGCBoundary, false, \ + "Trace young-old boundary moves") \ + \ + develop(intx, PSAdaptiveSizePolicyResizeVirtualSpaceAlot, -1, \ + "Resize the virtual spaces of the young or old generations") \ + \ + product(uintx, AdaptiveSizeThroughPutPolicy, 0, \ + "Policy for changeing generation size for throughput goals") \ + \ + product(uintx, AdaptiveSizePausePolicy, 0, \ + "Policy for changing generation size for pause goals") \ + \ + develop(bool, PSAdjustTenuredGenForMinorPause, false, \ + "Adjust tenured generation to achive a minor pause goal") \ + \ + develop(bool, PSAdjustYoungGenForMajorPause, false, \ + "Adjust young generation to achive a major pause goal") \ + \ + product(uintx, AdaptiveSizePolicyInitializingSteps, 20, \ + "Number of steps where heuristics is used before data is used") \ + \ + develop(uintx, AdaptiveSizePolicyReadyThreshold, 5, \ + "Number of collections before the adaptive sizing is started") \ + \ + product(uintx, AdaptiveSizePolicyOutputInterval, 0, \ + "Collecton interval for printing information, zero => never") \ + \ + product(bool, UseAdaptiveSizePolicyFootprintGoal, true, \ + "Use adaptive minimum footprint as a goal") \ + \ + product(uintx, AdaptiveSizePolicyWeight, 10, \ + "Weight given to exponential resizing, between 0 and 100") \ + \ + product(uintx, AdaptiveTimeWeight, 25, \ + "Weight given to time in adaptive policy, between 0 and 100") \ + \ + product(uintx, PausePadding, 1, \ + "How much buffer to keep for pause time") \ + \ + product(uintx, PromotedPadding, 3, \ + "How much buffer to keep for promotion failure") \ + \ + product(uintx, SurvivorPadding, 3, \ + "How much buffer to keep for survivor overflow") \ + \ + product(uintx, AdaptivePermSizeWeight, 20, \ + "Weight for perm gen exponential resizing, between 0 and 100") \ + \ + product(uintx, PermGenPadding, 3, \ + "How much buffer to keep for perm gen sizing") \ + \ + product(uintx, ThresholdTolerance, 10, \ + "Allowed collection cost difference between generations") \ + \ + product(uintx, AdaptiveSizePolicyCollectionCostMargin, 50, \ + "If collection costs are within margin, reduce both by full delta") \ + \ + product(uintx, YoungGenerationSizeIncrement, 20, \ + "Adaptive size percentage change in young generation") \ + \ + product(uintx, YoungGenerationSizeSupplement, 80, \ + "Supplement to YoungedGenerationSizeIncrement used at startup") \ + \ + product(uintx, YoungGenerationSizeSupplementDecay, 8, \ + "Decay factor to YoungedGenerationSizeSupplement") \ + \ + product(uintx, TenuredGenerationSizeIncrement, 20, \ + "Adaptive size percentage change in tenured generation") \ + \ + product(uintx, TenuredGenerationSizeSupplement, 80, \ + "Supplement to TenuredGenerationSizeIncrement used at startup") \ + \ + product(uintx, TenuredGenerationSizeSupplementDecay, 2, \ + "Decay factor to TenuredGenerationSizeIncrement") \ + \ + product(uintx, MaxGCPauseMillis, max_uintx, \ + "Adaptive size policy maximum GC pause time goal in msec") \ + \ + product(uintx, MaxGCMinorPauseMillis, max_uintx, \ + "Adaptive size policy maximum GC minor pause time goal in msec") \ + \ + product(uintx, GCTimeRatio, 99, \ + "Adaptive size policy application time to GC time ratio") \ + \ + product(uintx, AdaptiveSizeDecrementScaleFactor, 4, \ + "Adaptive size scale down factor for shrinking") \ + \ + product(bool, UseAdaptiveSizeDecayMajorGCCost, true, \ + "Adaptive size decays the major cost for long major intervals") \ + \ + product(uintx, AdaptiveSizeMajorGCDecayTimeScale, 10, \ + "Time scale over which major costs decay") \ + \ + product(uintx, MinSurvivorRatio, 3, \ + "Minimum ratio of young generation/survivor space size") \ + \ + product(uintx, InitialSurvivorRatio, 8, \ + "Initial ratio of eden/survivor space size") \ + \ + product(uintx, BaseFootPrintEstimate, 256*M, \ + "Estimate of footprint other than Java Heap") \ + \ + product(bool, UseGCOverheadLimit, true, \ + "Use policy to limit of proportion of time spent in GC " \ + "before an OutOfMemory error is thrown") \ + \ + product(uintx, GCTimeLimit, 98, \ + "Limit of proportion of time spent in GC before an OutOfMemory" \ + "error is thrown (used with GCHeapFreeLimit)") \ + \ + product(uintx, GCHeapFreeLimit, 2, \ + "Minimum percentage of free space after a full GC before an " \ + "OutOfMemoryError is thrown (used with GCTimeLimit)") \ + \ + develop(uintx, AdaptiveSizePolicyGCTimeLimitThreshold, 5, \ + "Number of consecutive collections before gc time limit fires") \ + \ + product(bool, PrintAdaptiveSizePolicy, false, \ + "Print information about AdaptiveSizePolicy") \ + \ + product(intx, PrefetchCopyIntervalInBytes, -1, \ + "How far ahead to prefetch destination area (<= 0 means off)") \ + \ + product(intx, PrefetchScanIntervalInBytes, -1, \ + "How far ahead to prefetch scan area (<= 0 means off)") \ + \ + product(intx, PrefetchFieldsAhead, -1, \ + "How many fields ahead to prefetch in oop scan (<= 0 means off)") \ + \ + develop(bool, UsePrefetchQueue, true, \ + "Use the prefetch queue during PS promotion") \ + \ + diagnostic(bool, VerifyBeforeExit, trueInDebug, \ + "Verify system before exiting") \ + \ + diagnostic(bool, VerifyBeforeGC, false, \ + "Verify memory system before GC") \ + \ + diagnostic(bool, VerifyAfterGC, false, \ + "Verify memory system after GC") \ + \ + diagnostic(bool, VerifyDuringGC, false, \ + "Verify memory system during GC (between phases)") \ + \ + diagnostic(bool, VerifyRememberedSets, false, \ + "Verify GC remembered sets") \ + \ + diagnostic(bool, VerifyObjectStartArray, true, \ + "Verify GC object start array if verify before/after") \ + \ + product(bool, DisableExplicitGC, false, \ + "Tells whether calling System.gc() does a full GC") \ + \ + notproduct(bool, CheckMemoryInitialization, false, \ + "Checks memory initialization") \ + \ + product(bool, CollectGen0First, false, \ + "Collect youngest generation before each full GC") \ + \ + diagnostic(bool, BindCMSThreadToCPU, false, \ + "Bind CMS Thread to CPU if possible") \ + \ + diagnostic(uintx, CPUForCMSThread, 0, \ + "When BindCMSThreadToCPU is true, the CPU to bind CMS thread to") \ + \ + product(bool, BindGCTaskThreadsToCPUs, false, \ + "Bind GCTaskThreads to CPUs if possible") \ + \ + product(bool, UseGCTaskAffinity, false, \ + "Use worker affinity when asking for GCTasks") \ + \ + product(uintx, ProcessDistributionStride, 4, \ + "Stride through processors when distributing processes") \ + \ + product(uintx, CMSCoordinatorYieldSleepCount, 10, \ + "number of times the coordinator GC thread will sleep while " \ + "yielding before giving up and resuming GC") \ + \ + product(uintx, CMSYieldSleepCount, 0, \ + "number of times a GC thread (minus the coordinator) " \ + "will sleep while yielding before giving up and resuming GC") \ + \ + /* gc tracing */ \ + manageable(bool, PrintGC, false, \ + "Print message at garbage collect") \ + \ + manageable(bool, PrintGCDetails, false, \ + "Print more details at garbage collect") \ + \ + manageable(bool, PrintGCDateStamps, false, \ + "Print date stamps at garbage collect") \ + \ + manageable(bool, PrintGCTimeStamps, false, \ + "Print timestamps at garbage collect") \ + \ + product(bool, PrintGCTaskTimeStamps, false, \ + "Print timestamps for individual gc worker thread tasks") \ + \ + develop(intx, ConcGCYieldTimeout, 0, \ + "If non-zero, assert that GC threads yield within this # of ms.") \ + \ + notproduct(bool, TraceMarkSweep, false, \ + "Trace mark sweep") \ + \ + product(bool, PrintReferenceGC, false, \ + "Print times spent handling reference objects during GC " \ + " (enabled only when PrintGCDetails)") \ + \ + develop(bool, TraceReferenceGC, false, \ + "Trace handling of soft/weak/final/phantom references") \ + \ + develop(bool, TraceFinalizerRegistration, false, \ + "Trace registration of final references") \ + \ + notproduct(bool, TraceScavenge, false, \ + "Trace scavenge") \ + \ + product_rw(bool, TraceClassLoading, false, \ + "Trace all classes loaded") \ + \ + product(bool, TraceClassLoadingPreorder, false, \ + "Trace all classes loaded in order referenced (not loaded)") \ + \ + product_rw(bool, TraceClassUnloading, false, \ + "Trace unloading of classes") \ + \ + product_rw(bool, TraceLoaderConstraints, false, \ + "Trace loader constraints") \ + \ + product(bool, TraceGen0Time, false, \ + "Trace accumulated time for Gen 0 collection") \ + \ + product(bool, TraceGen1Time, false, \ + "Trace accumulated time for Gen 1 collection") \ + \ + product(bool, PrintTenuringDistribution, false, \ + "Print tenuring age information") \ + \ + product_rw(bool, PrintHeapAtGC, false, \ + "Print heap layout before and after each GC") \ + \ + product(bool, PrintHeapAtSIGBREAK, true, \ + "Print heap layout in response to SIGBREAK") \ + \ + manageable(bool, PrintClassHistogram, false, \ + "Print a histogram of class instances") \ + \ + develop(bool, TraceWorkGang, false, \ + "Trace activities of work gangs") \ + \ + product(bool, TraceParallelOldGCTasks, false, \ + "Trace multithreaded GC activity") \ + \ + develop(bool, TraceBlockOffsetTable, false, \ + "Print BlockOffsetTable maps") \ + \ + develop(bool, TraceCardTableModRefBS, false, \ + "Print CardTableModRefBS maps") \ + \ + develop(bool, TraceGCTaskManager, false, \ + "Trace actions of the GC task manager") \ + \ + develop(bool, TraceGCTaskQueue, false, \ + "Trace actions of the GC task queues") \ + \ + develop(bool, TraceGCTaskThread, false, \ + "Trace actions of the GC task threads") \ + \ + product(bool, PrintParallelOldGCPhaseTimes, false, \ + "Print the time taken by each parallel old gc phase." \ + "PrintGCDetails must also be enabled.") \ + \ + develop(bool, TraceParallelOldGCMarkingPhase, false, \ + "Trace parallel old gc marking phase") \ + \ + develop(bool, TraceParallelOldGCSummaryPhase, false, \ + "Trace parallel old gc summary phase") \ + \ + develop(bool, TraceParallelOldGCCompactionPhase, false, \ + "Trace parallel old gc compaction phase") \ + \ + develop(bool, TraceParallelOldGCDensePrefix, false, \ + "Trace parallel old gc dense prefix computation") \ + \ + develop(bool, IgnoreLibthreadGPFault, false, \ + "Suppress workaround for libthread GP fault") \ + \ + /* JVMTI heap profiling */ \ + \ + diagnostic(bool, TraceJVMTIObjectTagging, false, \ + "Trace JVMTI object tagging calls") \ + \ + diagnostic(bool, VerifyBeforeIteration, false, \ + "Verify memory system before JVMTI iteration") \ + \ + /* compiler interface */ \ + \ + develop(bool, CIPrintCompilerName, false, \ + "when CIPrint is active, print the name of the active compiler") \ + \ + develop(bool, CIPrintCompileQueue, false, \ + "display the contents of the compile queue whenever a " \ + "compilation is enqueued") \ + \ + develop(bool, CIPrintRequests, false, \ + "display every request for compilation") \ + \ + product(bool, CITime, false, \ + "collect timing information for compilation") \ + \ + develop(bool, CITimeEach, false, \ + "display timing information after each successful compilation") \ + \ + develop(bool, CICountOSR, true, \ + "use a separate counter when assigning ids to osr compilations") \ + \ + develop(bool, CICompileNatives, true, \ + "compile native methods if supported by the compiler") \ + \ + develop_pd(bool, CICompileOSR, \ + "compile on stack replacement methods if supported by the " \ + "compiler") \ + \ + develop(bool, CIPrintMethodCodes, false, \ + "print method bytecodes of the compiled code") \ + \ + develop(bool, CIPrintTypeFlow, false, \ + "print the results of ciTypeFlow analysis") \ + \ + develop(bool, CITraceTypeFlow, false, \ + "detailed per-bytecode tracing of ciTypeFlow analysis") \ + \ + develop(intx, CICloneLoopTestLimit, 100, \ + "size limit for blocks heuristically cloned in ciTypeFlow") \ + \ + /* temp diagnostics */ \ + \ + diagnostic(bool, TraceRedundantCompiles, false, \ + "Have compile broker print when a request already in the queue is"\ + " requested again") \ + \ + diagnostic(bool, InitialCompileFast, false, \ + "Initial compile at CompLevel_fast_compile") \ + \ + diagnostic(bool, InitialCompileReallyFast, false, \ + "Initial compile at CompLevel_really_fast_compile (no profile)") \ + \ + diagnostic(bool, FullProfileOnReInterpret, true, \ + "On re-interpret unc-trap compile next at CompLevel_fast_compile")\ + \ + /* compiler */ \ + \ + product(intx, CICompilerCount, CI_COMPILER_COUNT, \ + "Number of compiler threads to run") \ + \ + product(intx, CompilationPolicyChoice, 0, \ + "which compilation policy (0/1)") \ + \ + develop(bool, UseStackBanging, true, \ + "use stack banging for stack overflow checks (required for " \ + "proper StackOverflow handling; disable only to measure cost " \ + "of stackbanging)") \ + \ + develop(bool, Use24BitFPMode, true, \ + "Set 24-bit FPU mode on a per-compile basis ") \ + \ + develop(bool, Use24BitFP, true, \ + "use FP instructions that produce 24-bit precise results") \ + \ + develop(bool, UseStrictFP, true, \ + "use strict fp if modifier strictfp is set") \ + \ + develop(bool, GenerateSynchronizationCode, true, \ + "generate locking/unlocking code for synchronized methods and " \ + "monitors") \ + \ + develop(bool, GenerateCompilerNullChecks, true, \ + "Generate explicit null checks for loads/stores/calls") \ + \ + develop(bool, GenerateRangeChecks, true, \ + "Generate range checks for array accesses") \ + \ + develop_pd(bool, ImplicitNullChecks, \ + "generate code for implicit null checks") \ + \ + product(bool, PrintSafepointStatistics, false, \ + "print statistics about safepoint synchronization") \ + \ + product(intx, PrintSafepointStatisticsCount, 300, \ + "total number of safepoint statistics collected " \ + "before printing them out") \ + \ + product(intx, PrintSafepointStatisticsTimeout, -1, \ + "print safepoint statistics only when safepoint takes" \ + " more than PrintSafepointSatisticsTimeout in millis") \ + \ + develop(bool, InlineAccessors, true, \ + "inline accessor methods (get/set)") \ + \ + product(bool, Inline, true, \ + "enable inlining") \ + \ + product(bool, ClipInlining, true, \ + "clip inlining if aggregate method exceeds DesiredMethodLimit") \ + \ + develop(bool, UseCHA, true, \ + "enable CHA") \ + \ + product(bool, UseTypeProfile, true, \ + "Check interpreter profile for historically monomorphic calls") \ + \ + product(intx, TypeProfileMajorReceiverPercent, 90, \ + "% of major receiver type to all profiled receivers") \ + \ + notproduct(bool, TimeCompiler, false, \ + "time the compiler") \ + \ + notproduct(bool, TimeCompiler2, false, \ + "detailed time the compiler (requires +TimeCompiler)") \ + \ + diagnostic(bool, PrintInlining, false, \ + "prints inlining optimizations") \ + \ + diagnostic(bool, PrintIntrinsics, false, \ + "prints attempted and successful inlining of intrinsics") \ + \ + diagnostic(ccstrlist, DisableIntrinsic, "", \ + "do not expand intrinsics whose (internal) names appear here") \ + \ + develop(bool, StressReflectiveCode, false, \ + "Use inexact types at allocations, etc., to test reflection") \ + \ + develop(bool, EagerInitialization, false, \ + "Eagerly initialize classes if possible") \ + \ + product(bool, Tier1UpdateMethodData, trueInTiered, \ + "Update methodDataOops in Tier1-generated code") \ + \ + develop(bool, TraceMethodReplacement, false, \ + "Print when methods are replaced do to recompilation") \ + \ + develop(bool, PrintMethodFlushing, false, \ + "print the nmethods being flushed") \ + \ + notproduct(bool, LogMultipleMutexLocking, false, \ + "log locking and unlocking of mutexes (only if multiple locks " \ + "are held)") \ + \ + develop(bool, UseRelocIndex, false, \ + "use an index to speed random access to relocations") \ + \ + develop(bool, StressCodeBuffers, false, \ + "Exercise code buffer expansion and other rare state changes") \ + \ + diagnostic(bool, DebugNonSafepoints, trueInDebug, \ + "Generate extra debugging info for non-safepoints in nmethods") \ + \ + diagnostic(bool, DebugInlinedCalls, true, \ + "If false, restricts profiled locations to the root method only") \ + \ + product(bool, PrintVMOptions, trueInDebug, \ + "print VM flag settings") \ + \ + diagnostic(bool, SerializeVMOutput, true, \ + "Use a mutex to serialize output to tty and hotspot.log") \ + \ + diagnostic(bool, DisplayVMOutput, true, \ + "Display all VM output on the tty, independently of LogVMOutput") \ + \ + diagnostic(bool, LogVMOutput, trueInDebug, \ + "Save VM output to hotspot.log, or to LogFile") \ + \ + diagnostic(ccstr, LogFile, NULL, \ + "If LogVMOutput is on, save VM output to this file [hotspot.log]") \ + \ + product(ccstr, ErrorFile, NULL, \ + "If an error occurs, save the error data to this file " \ + "[default: ./hs_err_pid%p.log] (%p replaced with pid)") \ + \ + product(bool, DisplayVMOutputToStderr, false, \ + "If DisplayVMOutput is true, display all VM output to stderr") \ + \ + product(bool, DisplayVMOutputToStdout, false, \ + "If DisplayVMOutput is true, display all VM output to stdout") \ + \ + product(bool, UseHeavyMonitors, false, \ + "use heavyweight instead of lightweight Java monitors") \ + \ + notproduct(bool, PrintSymbolTableSizeHistogram, false, \ + "print histogram of the symbol table") \ + \ + notproduct(bool, ExitVMOnVerifyError, false, \ + "standard exit from VM if bytecode verify error " \ + "(only in debug mode)") \ + \ + notproduct(ccstr, AbortVMOnException, NULL, \ + "Call fatal if this exception is thrown. Example: " \ + "java -XX:AbortVMOnException=java.lang.NullPointerException Foo") \ + \ + develop(bool, DebugVtables, false, \ + "add debugging code to vtable dispatch") \ + \ + develop(bool, PrintVtables, false, \ + "print vtables when printing klass") \ + \ + notproduct(bool, PrintVtableStats, false, \ + "print vtables stats at end of run") \ + \ + develop(bool, TraceCreateZombies, false, \ + "trace creation of zombie nmethods") \ + \ + notproduct(bool, IgnoreLockingAssertions, false, \ + "disable locking assertions (for speed)") \ + \ + notproduct(bool, VerifyLoopOptimizations, false, \ + "verify major loop optimizations") \ + \ + product(bool, RangeCheckElimination, true, \ + "Split loop iterations to eliminate range checks") \ + \ + develop_pd(bool, UncommonNullCast, \ + "track occurrences of null in casts; adjust compiler tactics") \ + \ + develop(bool, TypeProfileCasts, true, \ + "treat casts like calls for purposes of type profiling") \ + \ + develop(bool, MonomorphicArrayCheck, true, \ + "Uncommon-trap array store checks that require full type check") \ + \ + develop(bool, DelayCompilationDuringStartup, true, \ + "Delay invoking the compiler until main application class is " \ + "loaded") \ + \ + develop(bool, CompileTheWorld, false, \ + "Compile all methods in all classes in bootstrap class path " \ + "(stress test)") \ + \ + develop(bool, CompileTheWorldPreloadClasses, true, \ + "Preload all classes used by a class before start loading") \ + \ + notproduct(bool, CompileTheWorldIgnoreInitErrors, false, \ + "Compile all methods although class initializer failed") \ + \ + develop(bool, TraceIterativeGVN, false, \ + "Print progress during Iterative Global Value Numbering") \ + \ + develop(bool, FillDelaySlots, true, \ + "Fill delay slots (on SPARC only)") \ + \ + develop(bool, VerifyIterativeGVN, false, \ + "Verify Def-Use modifications during sparse Iterative Global " \ + "Value Numbering") \ + \ + notproduct(bool, TracePhaseCCP, false, \ + "Print progress during Conditional Constant Propagation") \ + \ + develop(bool, TimeLivenessAnalysis, false, \ + "Time computation of bytecode liveness analysis") \ + \ + develop(bool, TraceLivenessGen, false, \ + "Trace the generation of liveness analysis information") \ + \ + notproduct(bool, TraceLivenessQuery, false, \ + "Trace queries of liveness analysis information") \ + \ + notproduct(bool, CollectIndexSetStatistics, false, \ + "Collect information about IndexSets") \ + \ + develop(bool, PrintDominators, false, \ + "Print out dominator trees for GVN") \ + \ + develop(bool, UseLoopSafepoints, true, \ + "Generate Safepoint nodes in every loop") \ + \ + notproduct(bool, TraceCISCSpill, false, \ + "Trace allocators use of cisc spillable instructions") \ + \ + notproduct(bool, TraceSpilling, false, \ + "Trace spilling") \ + \ + develop(bool, DeutschShiffmanExceptions, true, \ + "Fast check to find exception handler for precisely typed " \ + "exceptions") \ + \ + product(bool, SplitIfBlocks, true, \ + "Clone compares and control flow through merge points to fold " \ + "some branches") \ + \ + develop(intx, FastAllocateSizeLimit, 128*K, \ + /* Note: This value is zero mod 1<<13 for a cheap sparc set. */ \ + "Inline allocations larger than this in doublewords must go slow")\ + \ + product(bool, AggressiveOpts, false, \ + "Enable aggressive optimizations - see arguments.cpp") \ + \ + /* statistics */ \ + develop(bool, UseVTune, false, \ + "enable support for Intel's VTune profiler") \ + \ + develop(bool, CountCompiledCalls, false, \ + "counts method invocations") \ + \ + notproduct(bool, CountRuntimeCalls, false, \ + "counts VM runtime calls") \ + \ + develop(bool, CountJNICalls, false, \ + "counts jni method invocations") \ + \ + notproduct(bool, CountJVMCalls, false, \ + "counts jvm method invocations") \ + \ + notproduct(bool, CountRemovableExceptions, false, \ + "count exceptions that could be replaced by branches due to " \ + "inlining") \ + \ + notproduct(bool, ICMissHistogram, false, \ + "produce histogram of IC misses") \ + \ + notproduct(bool, PrintClassStatistics, false, \ + "prints class statistics at end of run") \ + \ + notproduct(bool, PrintMethodStatistics, false, \ + "prints method statistics at end of run") \ + \ + /* interpreter */ \ + develop(bool, ClearInterpreterLocals, false, \ + "Always clear local variables of interpreter activations upon " \ + "entry") \ + \ + product_pd(bool, RewriteBytecodes, \ + "Allow rewriting of bytecodes (bytecodes are not immutable)") \ + \ + product_pd(bool, RewriteFrequentPairs, \ + "Rewrite frequently used bytecode pairs into a single bytecode") \ + \ + product(bool, PrintInterpreter, false, \ + "Prints the generated interpreter code") \ + \ + product(bool, UseInterpreter, true, \ + "Use interpreter for non-compiled methods") \ + \ + develop(bool, UseFastSignatureHandlers, true, \ + "Use fast signature handlers for native calls") \ + \ + develop(bool, UseV8InstrsOnly, false, \ + "Use SPARC-V8 Compliant instruction subset") \ + \ + product(bool, UseNiagaraInstrs, false, \ + "Use Niagara-efficient instruction subset") \ + \ + develop(bool, UseCASForSwap, false, \ + "Do not use swap instructions, but only CAS (in a loop) on SPARC")\ + \ + product(bool, UseLoopCounter, true, \ + "Increment invocation counter on backward branch") \ + \ + product(bool, UseFastEmptyMethods, true, \ + "Use fast method entry code for empty methods") \ + \ + product(bool, UseFastAccessorMethods, true, \ + "Use fast method entry code for accessor methods") \ + \ + product_pd(bool, UseOnStackReplacement, \ + "Use on stack replacement, calls runtime if invoc. counter " \ + "overflows in loop") \ + \ + notproduct(bool, TraceOnStackReplacement, false, \ + "Trace on stack replacement") \ + \ + develop(bool, PoisonOSREntry, true, \ + "Detect abnormal calls to OSR code") \ + \ + product_pd(bool, PreferInterpreterNativeStubs, \ + "Use always interpreter stubs for native methods invoked via " \ + "interpreter") \ + \ + develop(bool, CountBytecodes, false, \ + "Count number of bytecodes executed") \ + \ + develop(bool, PrintBytecodeHistogram, false, \ + "Print histogram of the executed bytecodes") \ + \ + develop(bool, PrintBytecodePairHistogram, false, \ + "Print histogram of the executed bytecode pairs") \ + \ + develop(bool, PrintSignatureHandlers, false, \ + "Print code generated for native method signature handlers") \ + \ + develop(bool, VerifyOops, false, \ + "Do plausibility checks for oops") \ + \ + develop(bool, CheckUnhandledOops, false, \ + "Check for unhandled oops in VM code") \ + \ + develop(bool, VerifyJNIFields, trueInDebug, \ + "Verify jfieldIDs for instance fields") \ + \ + notproduct(bool, VerifyJNIEnvThread, false, \ + "Verify JNIEnv.thread == Thread::current() when entering VM " \ + "from JNI") \ + \ + develop(bool, VerifyFPU, false, \ + "Verify FPU state (check for NaN's, etc.)") \ + \ + develop(bool, VerifyThread, false, \ + "Watch the thread register for corruption (SPARC only)") \ + \ + develop(bool, VerifyActivationFrameSize, false, \ + "Verify that activation frame didn't become smaller than its " \ + "minimal size") \ + \ + develop(bool, TraceFrequencyInlining, false, \ + "Trace frequency based inlining") \ + \ + notproduct(bool, TraceTypeProfile, false, \ + "Trace type profile") \ + \ + develop_pd(bool, InlineIntrinsics, \ + "Inline intrinsics that can be statically resolved") \ + \ + product_pd(bool, ProfileInterpreter, \ + "Profile at the bytecode level during interpretation") \ + \ + develop_pd(bool, ProfileTraps, \ + "Profile deoptimization traps at the bytecode level") \ + \ + product(intx, ProfileMaturityPercentage, 20, \ + "number of method invocations/branches (expressed as % of " \ + "CompileThreshold) before using the method's profile") \ + \ + develop(bool, PrintMethodData, false, \ + "Print the results of +ProfileInterpreter at end of run") \ + \ + develop(bool, VerifyDataPointer, trueInDebug, \ + "Verify the method data pointer during interpreter profiling") \ + \ + develop(bool, VerifyCompiledCode, false, \ + "Include miscellaneous runtime verifications in nmethod code; " \ + "off by default because it disturbs nmethod size heuristics.") \ + \ + \ + /* compilation */ \ + product(bool, UseCompiler, true, \ + "use compilation") \ + \ + develop(bool, TraceCompilationPolicy, false, \ + "Trace compilation policy") \ + \ + develop(bool, TimeCompilationPolicy, false, \ + "Time the compilation policy") \ + \ + product(bool, UseCounterDecay, true, \ + "adjust recompilation counters") \ + \ + develop(intx, CounterHalfLifeTime, 30, \ + "half-life time of invocation counters (in secs)") \ + \ + develop(intx, CounterDecayMinIntervalLength, 500, \ + "Min. ms. between invocation of CounterDecay") \ + \ + product(bool, AlwaysCompileLoopMethods, false, \ + "when using recompilation, never interpret methods " \ + "containing loops") \ + \ + product(bool, DontCompileHugeMethods, true, \ + "don't compile methods > HugeMethodLimit") \ + \ + /* Bytecode escape analysis estimation. */ \ + product(bool, EstimateArgEscape, true, \ + "Analyze bytecodes to estimate escape state of arguments") \ + \ + product(intx, BCEATraceLevel, 0, \ + "How much tracing to do of bytecode escape analysis estimates") \ + \ + product(intx, MaxBCEAEstimateLevel, 5, \ + "Maximum number of nested calls that are analyzed by BC EA.") \ + \ + product(intx, MaxBCEAEstimateSize, 150, \ + "Maximum bytecode size of a method to be analyzed by BC EA.") \ + \ + product(intx, AllocatePrefetchStyle, 1, \ + "0 = no prefetch, " \ + "1 = prefetch instructions for each allocation, " \ + "2 = use TLAB watermark to gate allocation prefetch") \ + \ + product(intx, AllocatePrefetchDistance, -1, \ + "Distance to prefetch ahead of allocation pointer") \ + \ + product(intx, AllocatePrefetchLines, 1, \ + "Number of lines to prefetch ahead of allocation pointer") \ + \ + product(intx, AllocatePrefetchStepSize, 16, \ + "Step size in bytes of sequential prefetch instructions") \ + \ + product(intx, AllocatePrefetchInstr, 0, \ + "Prefetch instruction to prefetch ahead of allocation pointer") \ + \ + product(intx, ReadPrefetchInstr, 0, \ + "Prefetch instruction to prefetch ahead") \ + \ + /* deoptimization */ \ + develop(bool, TraceDeoptimization, false, \ + "Trace deoptimization") \ + \ + develop(bool, DebugDeoptimization, false, \ + "Tracing various information while debugging deoptimization") \ + \ + product(intx, SelfDestructTimer, 0, \ + "Will cause VM to terminate after a given time (in minutes) " \ + "(0 means off)") \ + \ + product(intx, MaxJavaStackTraceDepth, 1024, \ + "Max. no. of lines in the stack trace for Java exceptions " \ + "(0 means all)") \ + \ + develop(intx, GuaranteedSafepointInterval, 1000, \ + "Guarantee a safepoint (at least) every so many milliseconds " \ + "(0 means none)") \ + \ + product(intx, SafepointTimeoutDelay, 10000, \ + "Delay in milliseconds for option SafepointTimeout") \ + \ + product(intx, NmethodSweepFraction, 4, \ + "Number of invocations of sweeper to cover all nmethods") \ + \ + notproduct(intx, MemProfilingInterval, 500, \ + "Time between each invocation of the MemProfiler") \ + \ + develop(intx, MallocCatchPtr, -1, \ + "Hit breakpoint when mallocing/freeing this pointer") \ + \ + notproduct(intx, AssertRepeat, 1, \ + "number of times to evaluate expression in assert " \ + "(to estimate overhead); only works with -DUSE_REPEATED_ASSERTS") \ + \ + notproduct(ccstrlist, SuppressErrorAt, "", \ + "List of assertions (file:line) to muzzle") \ + \ + notproduct(uintx, HandleAllocationLimit, 1024, \ + "Threshold for HandleMark allocation when +TraceHandleAllocation "\ + "is used") \ + \ + develop(uintx, TotalHandleAllocationLimit, 1024, \ + "Threshold for total handle allocation when " \ + "+TraceHandleAllocation is used") \ + \ + develop(intx, StackPrintLimit, 100, \ + "number of stack frames to print in VM-level stack dump") \ + \ + notproduct(intx, MaxElementPrintSize, 256, \ + "maximum number of elements to print") \ + \ + notproduct(intx, MaxSubklassPrintSize, 4, \ + "maximum number of subklasses to print when printing klass") \ + \ + develop(intx, MaxInlineLevel, 9, \ + "maximum number of nested calls that are inlined") \ + \ + develop(intx, MaxRecursiveInlineLevel, 1, \ + "maximum number of nested recursive calls that are inlined") \ + \ + develop(intx, InlineSmallCode, 1000, \ + "Only inline already compiled methods if their code size is " \ + "less than this") \ + \ + product(intx, MaxInlineSize, 35, \ + "maximum bytecode size of a method to be inlined") \ + \ + product_pd(intx, FreqInlineSize, \ + "maximum bytecode size of a frequent method to be inlined") \ + \ + develop(intx, MaxTrivialSize, 6, \ + "maximum bytecode size of a trivial method to be inlined") \ + \ + develop(intx, MinInliningThreshold, 250, \ + "min. invocation count a method needs to have to be inlined") \ + \ + develop(intx, AlignEntryCode, 4, \ + "aligns entry code to specified value (in bytes)") \ + \ + develop(intx, MethodHistogramCutoff, 100, \ + "cutoff value for method invoc. histogram (+CountCalls)") \ + \ + develop(intx, ProfilerNumberOfInterpretedMethods, 25, \ + "# of interpreted methods to show in profile") \ + \ + develop(intx, ProfilerNumberOfCompiledMethods, 25, \ + "# of compiled methods to show in profile") \ + \ + develop(intx, ProfilerNumberOfStubMethods, 25, \ + "# of stub methods to show in profile") \ + \ + develop(intx, ProfilerNumberOfRuntimeStubNodes, 25, \ + "# of runtime stub nodes to show in profile") \ + \ + product(intx, ProfileIntervalsTicks, 100, \ + "# of ticks between printing of interval profile " \ + "(+ProfileIntervals)") \ + \ + notproduct(intx, ScavengeALotInterval, 1, \ + "Interval between which scavenge will occur with +ScavengeALot") \ + \ + notproduct(intx, FullGCALotInterval, 1, \ + "Interval between which full gc will occur with +FullGCALot") \ + \ + notproduct(intx, FullGCALotStart, 0, \ + "For which invocation to start FullGCAlot") \ + \ + notproduct(intx, FullGCALotDummies, 32*K, \ + "Dummy object allocated with +FullGCALot, forcing all objects " \ + "to move") \ + \ + develop(intx, DontYieldALotInterval, 10, \ + "Interval between which yields will be dropped (milliseconds)") \ + \ + develop(intx, MinSleepInterval, 1, \ + "Minimum sleep() interval (milliseconds) when " \ + "ConvertSleepToYield is off (used for SOLARIS)") \ + \ + product(intx, EventLogLength, 2000, \ + "maximum nof events in event log") \ + \ + develop(intx, ProfilerPCTickThreshold, 15, \ + "Number of ticks in a PC buckets to be a hotspot") \ + \ + notproduct(intx, DeoptimizeALotInterval, 5, \ + "Number of exits until DeoptimizeALot kicks in") \ + \ + notproduct(intx, ZombieALotInterval, 5, \ + "Number of exits until ZombieALot kicks in") \ + \ + develop(bool, StressNonEntrant, false, \ + "Mark nmethods non-entrant at registration") \ + \ + diagnostic(intx, MallocVerifyInterval, 0, \ + "if non-zero, verify C heap after every N calls to " \ + "malloc/realloc/free") \ + \ + diagnostic(intx, MallocVerifyStart, 0, \ + "if non-zero, start verifying C heap after Nth call to " \ + "malloc/realloc/free") \ + \ + product(intx, TypeProfileWidth, 2, \ + "number of receiver types to record in call/cast profile") \ + \ + develop(intx, BciProfileWidth, 2, \ + "number of return bci's to record in ret profile") \ + \ + product(intx, PerMethodRecompilationCutoff, 400, \ + "After recompiling N times, stay in the interpreter (-1=>'Inf')") \ + \ + product(intx, PerBytecodeRecompilationCutoff, 100, \ + "Per-BCI limit on repeated recompilation (-1=>'Inf')") \ + \ + product(intx, PerMethodTrapLimit, 100, \ + "Limit on traps (of one kind) in a method (includes inlines)") \ + \ + product(intx, PerBytecodeTrapLimit, 4, \ + "Limit on traps (of one kind) at a particular BCI") \ + \ + develop(intx, FreqCountInvocations, 1, \ + "Scaling factor for branch frequencies (deprecated)") \ + \ + develop(intx, InlineFrequencyRatio, 20, \ + "Ratio of call site execution to caller method invocation") \ + \ + develop_pd(intx, InlineFrequencyCount, \ + "Count of call site execution necessary to trigger frequent " \ + "inlining") \ + \ + develop(intx, InlineThrowCount, 50, \ + "Force inlining of interpreted methods that throw this often") \ + \ + develop(intx, InlineThrowMaxSize, 200, \ + "Force inlining of throwing methods smaller than this") \ + \ + product(intx, AliasLevel, 3, \ + "0 for no aliasing, 1 for oop/field/static/array split, " \ + "2 for class split, 3 for unique instances") \ + \ + develop(bool, VerifyAliases, false, \ + "perform extra checks on the results of alias analysis") \ + \ + develop(intx, ProfilerNodeSize, 1024, \ + "Size in K to allocate for the Profile Nodes of each thread") \ + \ + develop(intx, V8AtomicOperationUnderLockSpinCount, 50, \ + "Number of times to spin wait on a v8 atomic operation lock") \ + \ + product(intx, ReadSpinIterations, 100, \ + "Number of read attempts before a yield (spin inner loop)") \ + \ + product_pd(intx, PreInflateSpin, \ + "Number of times to spin wait before inflation") \ + \ + product(intx, PreBlockSpin, 10, \ + "Number of times to spin in an inflated lock before going to " \ + "an OS lock") \ + \ + /* gc parameters */ \ + product(uintx, MaxHeapSize, ScaleForWordSize(64*M), \ + "Default maximum size for object heap (in bytes)") \ + \ + product_pd(uintx, NewSize, \ + "Default size of new generation (in bytes)") \ + \ + product(uintx, MaxNewSize, max_uintx, \ + "Maximum size of new generation (in bytes)") \ + \ + product(uintx, PretenureSizeThreshold, 0, \ + "Max size in bytes of objects allocated in DefNew generation") \ + \ + product_pd(uintx, TLABSize, \ + "Default (or starting) size of TLAB (in bytes)") \ + \ + product(uintx, MinTLABSize, 2*K, \ + "Minimum allowed TLAB size (in bytes)") \ + \ + product(uintx, TLABAllocationWeight, 35, \ + "Allocation averaging weight") \ + \ + product(uintx, TLABWasteTargetPercent, 1, \ + "Percentage of Eden that can be wasted") \ + \ + product(uintx, TLABRefillWasteFraction, 64, \ + "Max TLAB waste at a refill (internal fragmentation)") \ + \ + product(uintx, TLABWasteIncrement, 4, \ + "Increment allowed waste at slow allocation") \ + \ + product_pd(intx, SurvivorRatio, \ + "Ratio of eden/survivor space size") \ + \ + product_pd(intx, NewRatio, \ + "Ratio of new/old generation sizes") \ + \ + product(uintx, MaxLiveObjectEvacuationRatio, 100, \ + "Max percent of eden objects that will be live at scavenge") \ + \ + product_pd(uintx, NewSizeThreadIncrease, \ + "Additional size added to desired new generation size per " \ + "non-daemon thread (in bytes)") \ + \ + product(uintx, OldSize, ScaleForWordSize(4096*K), \ + "Default size of tenured generation (in bytes)") \ + \ + product_pd(uintx, PermSize, \ + "Default size of permanent generation (in bytes)") \ + \ + product_pd(uintx, MaxPermSize, \ + "Maximum size of permanent generation (in bytes)") \ + \ + product(uintx, MinHeapFreeRatio, 40, \ + "Min percentage of heap free after GC to avoid expansion") \ + \ + product(uintx, MaxHeapFreeRatio, 70, \ + "Max percentage of heap free after GC to avoid shrinking") \ + \ + product(intx, SoftRefLRUPolicyMSPerMB, 1000, \ + "Number of milliseconds per MB of free space in the heap") \ + \ + product(uintx, MinHeapDeltaBytes, ScaleForWordSize(128*K), \ + "Min change in heap space due to GC (in bytes)") \ + \ + product(uintx, MinPermHeapExpansion, ScaleForWordSize(256*K), \ + "Min expansion of permanent heap (in bytes)") \ + \ + product(uintx, MaxPermHeapExpansion, ScaleForWordSize(4*M), \ + "Max expansion of permanent heap without full GC (in bytes)") \ + \ + product(intx, QueuedAllocationWarningCount, 0, \ + "Number of times an allocation that queues behind a GC " \ + "will retry before printing a warning") \ + \ + diagnostic(uintx, VerifyGCStartAt, 0, \ + "GC invoke count where +VerifyBefore/AfterGC kicks in") \ + \ + diagnostic(intx, VerifyGCLevel, 0, \ + "Generation level at which to start +VerifyBefore/AfterGC") \ + \ + develop(uintx, ExitAfterGCNum, 0, \ + "If non-zero, exit after this GC.") \ + \ + product(intx, MaxTenuringThreshold, 15, \ + "Maximum value for tenuring threshold") \ + \ + product(intx, InitialTenuringThreshold, 7, \ + "Initial value for tenuring threshold") \ + \ + product(intx, TargetSurvivorRatio, 50, \ + "Desired percentage of survivor space used after scavenge") \ + \ + product(intx, MarkSweepDeadRatio, 5, \ + "Percentage (0-100) of the old gen allowed as dead wood." \ + "Serial mark sweep treats this as both the min and max value." \ + "CMS uses this value only if it falls back to mark sweep." \ + "Par compact uses a variable scale based on the density of the" \ + "generation and treats this as the max value when the heap is" \ + "either completely full or completely empty. Par compact also" \ + "has a smaller default value; see arguments.cpp.") \ + \ + product(intx, PermMarkSweepDeadRatio, 20, \ + "Percentage (0-100) of the perm gen allowed as dead wood." \ + "See MarkSweepDeadRatio for collector-specific comments.") \ + \ + product(intx, MarkSweepAlwaysCompactCount, 4, \ + "How often should we fully compact the heap (ignoring the dead " \ + "space parameters)") \ + \ + product(intx, PrintCMSStatistics, 0, \ + "Statistics for CMS") \ + \ + product(bool, PrintCMSInitiationStatistics, false, \ + "Statistics for initiating a CMS collection") \ + \ + product(intx, PrintFLSStatistics, 0, \ + "Statistics for CMS' FreeListSpace") \ + \ + product(intx, PrintFLSCensus, 0, \ + "Census for CMS' FreeListSpace") \ + \ + develop(uintx, GCExpandToAllocateDelayMillis, 0, \ + "Delay in ms between expansion and allocation") \ + \ + product(intx, DeferThrSuspendLoopCount, 4000, \ + "(Unstable) Number of times to iterate in safepoint loop " \ + " before blocking VM threads ") \ + \ + product(intx, DeferPollingPageLoopCount, -1, \ + "(Unsafe,Unstable) Number of iterations in safepoint loop " \ + "before changing safepoint polling page to RO ") \ + \ + product(intx, SafepointSpinBeforeYield, 2000, "(Unstable)") \ + \ + product(bool, UseDepthFirstScavengeOrder, true, \ + "true: the scavenge order will be depth-first, " \ + "false: the scavenge order will be breadth-first") \ + \ + product(bool, PSChunkLargeArrays, true, \ + "true: process large arrays in chunks") \ + \ + product(uintx, GCDrainStackTargetSize, 64, \ + "how many entries we'll try to leave on the stack during " \ + "parallel GC") \ + \ + /* stack parameters */ \ + product_pd(intx, StackYellowPages, \ + "Number of yellow zone (recoverable overflows) pages") \ + \ + product_pd(intx, StackRedPages, \ + "Number of red zone (unrecoverable overflows) pages") \ + \ + product_pd(intx, StackShadowPages, \ + "Number of shadow zone (for overflow checking) pages" \ + " this should exceed the depth of the VM and native call stack") \ + \ + product_pd(intx, ThreadStackSize, \ + "Thread Stack Size (in Kbytes)") \ + \ + product_pd(intx, VMThreadStackSize, \ + "Non-Java Thread Stack Size (in Kbytes)") \ + \ + product_pd(intx, CompilerThreadStackSize, \ + "Compiler Thread Stack Size (in Kbytes)") \ + \ + develop_pd(uintx, JVMInvokeMethodSlack, \ + "Stack space (bytes) required for JVM_InvokeMethod to complete") \ + \ + product(uintx, ThreadSafetyMargin, 50*M, \ + "Thread safety margin is used on fixed-stack LinuxThreads (on " \ + "Linux/x86 only) to prevent heap-stack collision. Set to 0 to " \ + "disable this feature") \ + \ + /* code cache parameters */ \ + develop(uintx, CodeCacheSegmentSize, 64, \ + "Code cache segment size (in bytes) - smallest unit of " \ + "allocation") \ + \ + develop_pd(intx, CodeEntryAlignment, \ + "Code entry alignment for generated code (in bytes)") \ + \ + product_pd(uintx, InitialCodeCacheSize, \ + "Initial code cache size (in bytes)") \ + \ + product_pd(uintx, ReservedCodeCacheSize, \ + "Reserved code cache size (in bytes) - maximum code cache size") \ + \ + product(uintx, CodeCacheMinimumFreeSpace, 500*K, \ + "When less than X space left, we stop compiling.") \ + \ + product_pd(uintx, CodeCacheExpansionSize, \ + "Code cache expansion size (in bytes)") \ + \ + develop_pd(uintx, CodeCacheMinBlockLength, \ + "Minimum number of segments in a code cache block.") \ + \ + notproduct(bool, ExitOnFullCodeCache, false, \ + "Exit the VM if we fill the code cache.") \ + \ + /* interpreter debugging */ \ + develop(intx, BinarySwitchThreshold, 5, \ + "Minimal number of lookupswitch entries for rewriting to binary " \ + "switch") \ + \ + develop(intx, StopInterpreterAt, 0, \ + "Stops interpreter execution at specified bytecode number") \ + \ + develop(intx, TraceBytecodesAt, 0, \ + "Traces bytecodes starting with specified bytecode number") \ + \ + /* compiler interface */ \ + develop(intx, CIStart, 0, \ + "the id of the first compilation to permit") \ + \ + develop(intx, CIStop, -1, \ + "the id of the last compilation to permit") \ + \ + develop(intx, CIStartOSR, 0, \ + "the id of the first osr compilation to permit " \ + "(CICountOSR must be on)") \ + \ + develop(intx, CIStopOSR, -1, \ + "the id of the last osr compilation to permit " \ + "(CICountOSR must be on)") \ + \ + develop(intx, CIBreakAtOSR, -1, \ + "id of osr compilation to break at") \ + \ + develop(intx, CIBreakAt, -1, \ + "id of compilation to break at") \ + \ + product(ccstrlist, CompileOnly, "", \ + "List of methods (pkg/class.name) to restrict compilation to") \ + \ + product(ccstr, CompileCommandFile, NULL, \ + "Read compiler commands from this file [.hotspot_compiler]") \ + \ + product(ccstrlist, CompileCommand, "", \ + "Prepend to .hotspot_compiler; e.g. log,java/lang/String.") \ + \ + product(bool, CICompilerCountPerCPU, false, \ + "1 compiler thread for log(N CPUs)") \ + \ + develop(intx, CIFireOOMAt, -1, \ + "Fire OutOfMemoryErrors throughout CI for testing the compiler " \ + "(non-negative value throws OOM after this many CI accesses " \ + "in each compile)") \ + \ + develop(intx, CIFireOOMAtDelay, -1, \ + "Wait for this many CI accesses to occur in all compiles before " \ + "beginning to throw OutOfMemoryErrors in each compile") \ + \ + /* Priorities */ \ + product_pd(bool, UseThreadPriorities, "Use native thread priorities") \ + \ + product(intx, ThreadPriorityPolicy, 0, \ + "0 : Normal. "\ + " VM chooses priorities that are appropriate for normal "\ + " applications. On Solaris NORM_PRIORITY and above are mapped "\ + " to normal native priority. Java priorities below NORM_PRIORITY"\ + " map to lower native priority values. On Windows applications"\ + " are allowed to use higher native priorities. However, with "\ + " ThreadPriorityPolicy=0, VM will not use the highest possible"\ + " native priority, THREAD_PRIORITY_TIME_CRITICAL, as it may "\ + " interfere with system threads. On Linux thread priorities "\ + " are ignored because the OS does not support static priority "\ + " in SCHED_OTHER scheduling class which is the only choice for"\ + " non-root, non-realtime applications. "\ + "1 : Aggressive. "\ + " Java thread priorities map over to the entire range of "\ + " native thread priorities. Higher Java thread priorities map "\ + " to higher native thread priorities. This policy should be "\ + " used with care, as sometimes it can cause performance "\ + " degradation in the application and/or the entire system. On "\ + " Linux this policy requires root privilege.") \ + \ + product(bool, ThreadPriorityVerbose, false, \ + "print priority changes") \ + \ + product(intx, DefaultThreadPriority, -1, \ + "what native priority threads run at if not specified elsewhere (-1 means no change)") \ + \ + product(intx, CompilerThreadPriority, -1, \ + "what priority should compiler threads run at (-1 means no change)") \ + \ + product(intx, VMThreadPriority, -1, \ + "what priority should VM threads run at (-1 means no change)") \ + \ + product(bool, CompilerThreadHintNoPreempt, true, \ + "(Solaris only) Give compiler threads an extra quanta") \ + \ + product(bool, VMThreadHintNoPreempt, false, \ + "(Solaris only) Give VM thread an extra quanta") \ + \ + product(intx, JavaPriority1_To_OSPriority, -1, "Map Java priorities to OS priorities") \ + product(intx, JavaPriority2_To_OSPriority, -1, "Map Java priorities to OS priorities") \ + product(intx, JavaPriority3_To_OSPriority, -1, "Map Java priorities to OS priorities") \ + product(intx, JavaPriority4_To_OSPriority, -1, "Map Java priorities to OS priorities") \ + product(intx, JavaPriority5_To_OSPriority, -1, "Map Java priorities to OS priorities") \ + product(intx, JavaPriority6_To_OSPriority, -1, "Map Java priorities to OS priorities") \ + product(intx, JavaPriority7_To_OSPriority, -1, "Map Java priorities to OS priorities") \ + product(intx, JavaPriority8_To_OSPriority, -1, "Map Java priorities to OS priorities") \ + product(intx, JavaPriority9_To_OSPriority, -1, "Map Java priorities to OS priorities") \ + product(intx, JavaPriority10_To_OSPriority,-1, "Map Java priorities to OS priorities") \ + \ + /* compiler debugging */ \ + notproduct(intx, CompileTheWorldStartAt, 1, \ + "First class to consider when using +CompileTheWorld") \ + \ + notproduct(intx, CompileTheWorldStopAt, max_jint, \ + "Last class to consider when using +CompileTheWorld") \ + \ + develop(intx, NewCodeParameter, 0, \ + "Testing Only: Create a dedicated integer parameter before " \ + "putback") \ + \ + /* new oopmap storage allocation */ \ + develop(intx, MinOopMapAllocation, 8, \ + "Minimum number of OopMap entries in an OopMapSet") \ + \ + /* Background Compilation */ \ + develop(intx, LongCompileThreshold, 50, \ + "Used with +TraceLongCompiles") \ + \ + product(intx, StarvationMonitorInterval, 200, \ + "Pause between each check in ms") \ + \ + /* recompilation */ \ + product_pd(intx, CompileThreshold, \ + "number of interpreted method invocations before (re-)compiling") \ + \ + product_pd(intx, BackEdgeThreshold, \ + "Interpreter Back edge threshold at which an OSR compilation is invoked")\ + \ + product(intx, Tier1BytecodeLimit, 10, \ + "Must have at least this many bytecodes before tier1" \ + "invocation counters are used") \ + \ + product_pd(intx, Tier2CompileThreshold, \ + "threshold at which a tier 2 compilation is invoked") \ + \ + product_pd(intx, Tier2BackEdgeThreshold, \ + "Back edge threshold at which a tier 2 compilation is invoked") \ + \ + product_pd(intx, Tier3CompileThreshold, \ + "threshold at which a tier 3 compilation is invoked") \ + \ + product_pd(intx, Tier3BackEdgeThreshold, \ + "Back edge threshold at which a tier 3 compilation is invoked") \ + \ + product_pd(intx, Tier4CompileThreshold, \ + "threshold at which a tier 4 compilation is invoked") \ + \ + product_pd(intx, Tier4BackEdgeThreshold, \ + "Back edge threshold at which a tier 4 compilation is invoked") \ + \ + product_pd(bool, TieredCompilation, \ + "Enable two-tier compilation") \ + \ + product(bool, StressTieredRuntime, false, \ + "Alternate client and server compiler on compile requests") \ + \ + product_pd(intx, OnStackReplacePercentage, \ + "NON_TIERED number of method invocations/branches (expressed as %"\ + "of CompileThreshold) before (re-)compiling OSR code") \ + \ + product(intx, InterpreterProfilePercentage, 33, \ + "NON_TIERED number of method invocations/branches (expressed as %"\ + "of CompileThreshold) before profiling in the interpreter") \ + \ + develop(intx, MaxRecompilationSearchLength, 10, \ + "max. # frames to inspect searching for recompilee") \ + \ + develop(intx, MaxInterpretedSearchLength, 3, \ + "max. # interp. frames to skip when searching for recompilee") \ + \ + develop(intx, DesiredMethodLimit, 8000, \ + "desired max. method size (in bytecodes) after inlining") \ + \ + develop(intx, HugeMethodLimit, 8000, \ + "don't compile methods larger than this if " \ + "+DontCompileHugeMethods") \ + \ + /* New JDK 1.4 reflection implementation */ \ + \ + develop(bool, UseNewReflection, true, \ + "Temporary flag for transition to reflection based on dynamic " \ + "bytecode generation in 1.4; can no longer be turned off in 1.4 " \ + "JDK, and is unneeded in 1.3 JDK, but marks most places VM " \ + "changes were needed") \ + \ + develop(bool, VerifyReflectionBytecodes, false, \ + "Force verification of 1.4 reflection bytecodes. Does not work " \ + "in situations like that described in 4486457 or for " \ + "constructors generated for serialization, so can not be enabled "\ + "in product.") \ + \ + product(bool, ReflectionWrapResolutionErrors, true, \ + "Temporary flag for transition to AbstractMethodError wrapped " \ + "in InvocationTargetException. See 6531596") \ + \ + \ + develop(intx, FastSuperclassLimit, 8, \ + "Depth of hardwired instanceof accelerator array") \ + \ + /* Properties for Java libraries */ \ + \ + product(intx, MaxDirectMemorySize, -1, \ + "Maximum total size of NIO direct-buffer allocations") \ + \ + /* temporary developer defined flags */ \ + \ + diagnostic(bool, UseNewCode, false, \ + "Testing Only: Use the new version while testing") \ + \ + diagnostic(bool, UseNewCode2, false, \ + "Testing Only: Use the new version while testing") \ + \ + diagnostic(bool, UseNewCode3, false, \ + "Testing Only: Use the new version while testing") \ + \ + /* flags for performance data collection */ \ + \ + product(bool, UsePerfData, true, \ + "Flag to disable jvmstat instrumentation for performance testing" \ + "and problem isolation purposes.") \ + \ + product(bool, PerfDataSaveToFile, false, \ + "Save PerfData memory to hsperfdata_ file on exit") \ + \ + product(ccstr, PerfDataSaveFile, NULL, \ + "Save PerfData memory to the specified absolute pathname," \ + "%p in the file name if present will be replaced by pid") \ + \ + product(intx, PerfDataSamplingInterval, 50 /*ms*/, \ + "Data sampling interval in milliseconds") \ + \ + develop(bool, PerfTraceDataCreation, false, \ + "Trace creation of Performance Data Entries") \ + \ + develop(bool, PerfTraceMemOps, false, \ + "Trace PerfMemory create/attach/detach calls") \ + \ + product(bool, PerfDisableSharedMem, false, \ + "Store performance data in standard memory") \ + \ + product(intx, PerfDataMemorySize, 32*K, \ + "Size of performance data memory region. Will be rounded " \ + "up to a multiple of the native os page size.") \ + \ + product(intx, PerfMaxStringConstLength, 1024, \ + "Maximum PerfStringConstant string length before truncation") \ + \ + product(bool, PerfAllowAtExitRegistration, false, \ + "Allow registration of atexit() methods") \ + \ + product(bool, PerfBypassFileSystemCheck, false, \ + "Bypass Win32 file system criteria checks (Windows Only)") \ + \ + product(intx, UnguardOnExecutionViolation, 0, \ + "Unguard page and retry on no-execute fault (Win32 only)" \ + "0=off, 1=conservative, 2=aggressive") \ + \ + /* Serviceability Support */ \ + \ + product(bool, ManagementServer, false, \ + "Create JMX Management Server") \ + \ + product(bool, DisableAttachMechanism, false, \ + "Disable mechanism that allows tools to attach to this VM") \ + \ + product(bool, StartAttachListener, false, \ + "Always start Attach Listener at VM startup") \ + \ + manageable(bool, PrintConcurrentLocks, false, \ + "Print java.util.concurrent locks in thread dump") \ + \ + /* Shared spaces */ \ + \ + product(bool, UseSharedSpaces, true, \ + "Use shared spaces in the permanent generation") \ + \ + product(bool, RequireSharedSpaces, false, \ + "Require shared spaces in the permanent generation") \ + \ + product(bool, ForceSharedSpaces, false, \ + "Require shared spaces in the permanent generation") \ + \ + product(bool, DumpSharedSpaces, false, \ + "Special mode: JVM reads a class list, loads classes, builds " \ + "shared spaces, and dumps the shared spaces to a file to be " \ + "used in future JVM runs.") \ + \ + product(bool, PrintSharedSpaces, false, \ + "Print usage of shared spaces") \ + \ + product(uintx, SharedDummyBlockSize, 512*M, \ + "Size of dummy block used to shift heap addresses (in bytes)") \ + \ + product(uintx, SharedReadWriteSize, 12*M, \ + "Size of read-write space in permanent generation (in bytes)") \ + \ + product(uintx, SharedReadOnlySize, 8*M, \ + "Size of read-only space in permanent generation (in bytes)") \ + \ + product(uintx, SharedMiscDataSize, 4*M, \ + "Size of the shared data area adjacent to the heap (in bytes)") \ + \ + product(uintx, SharedMiscCodeSize, 4*M, \ + "Size of the shared code area adjacent to the heap (in bytes)") \ + \ + diagnostic(bool, SharedOptimizeColdStart, true, \ + "At dump time, order shared objects to achieve better " \ + "cold startup time.") \ + \ + develop(intx, SharedOptimizeColdStartPolicy, 2, \ + "Reordering policy for SharedOptimizeColdStart " \ + "0=favor classload-time locality, 1=balanced, " \ + "2=favor runtime locality") \ + \ + diagnostic(bool, SharedSkipVerify, false, \ + "Skip assert() and verify() which page-in unwanted shared " \ + "objects. ") \ + \ + product(bool, TaggedStackInterpreter, false, \ + "Insert tags in interpreter execution stack for oopmap generaion")\ + \ + diagnostic(bool, PauseAtStartup, false, \ + "Causes the VM to pause at startup time and wait for the pause " \ + "file to be removed (default: ./vm.paused.)") \ + \ + diagnostic(ccstr, PauseAtStartupFile, NULL, \ + "The file to create and for whose removal to await when pausing " \ + "at startup. (default: ./vm.paused.)") \ + \ + product(bool, ExtendedDTraceProbes, false, \ + "Enable performance-impacting dtrace probes") \ + \ + product(bool, DTraceMethodProbes, false, \ + "Enable dtrace probes for method-entry and method-exit") \ + \ + product(bool, DTraceAllocProbes, false, \ + "Enable dtrace probes for object allocation") \ + \ + product(bool, DTraceMonitorProbes, false, \ + "Enable dtrace probes for monitor events") \ + \ + product(bool, RelaxAccessControlCheck, false, \ + "Relax the access control checks in the verifier") \ + \ + product(bool, UseVMInterruptibleIO, true, \ + "(Unstable, Solaris-specific) Thread interrupt before or with " \ + "EINTR for I/O operations results in OS_INTRPT") + + +/* + * Macros for factoring of globals + */ + +// Interface macros +#define DECLARE_PRODUCT_FLAG(type, name, value, doc) extern "C" type name; +#define DECLARE_PD_PRODUCT_FLAG(type, name, doc) extern "C" type name; +#define DECLARE_DIAGNOSTIC_FLAG(type, name, value, doc) extern "C" type name; +#define DECLARE_MANAGEABLE_FLAG(type, name, value, doc) extern "C" type name; +#define DECLARE_PRODUCT_RW_FLAG(type, name, value, doc) extern "C" type name; +#ifdef PRODUCT +#define DECLARE_DEVELOPER_FLAG(type, name, value, doc) const type name = value; +#define DECLARE_PD_DEVELOPER_FLAG(type, name, doc) const type name = pd_##name; +#define DECLARE_NOTPRODUCT_FLAG(type, name, value, doc) +#else +#define DECLARE_DEVELOPER_FLAG(type, name, value, doc) extern "C" type name; +#define DECLARE_PD_DEVELOPER_FLAG(type, name, doc) extern "C" type name; +#define DECLARE_NOTPRODUCT_FLAG(type, name, value, doc) extern "C" type name; +#endif + +// Implementation macros +#define MATERIALIZE_PRODUCT_FLAG(type, name, value, doc) type name = value; +#define MATERIALIZE_PD_PRODUCT_FLAG(type, name, doc) type name = pd_##name; +#define MATERIALIZE_DIAGNOSTIC_FLAG(type, name, value, doc) type name = value; +#define MATERIALIZE_MANAGEABLE_FLAG(type, name, value, doc) type name = value; +#define MATERIALIZE_PRODUCT_RW_FLAG(type, name, value, doc) type name = value; +#ifdef PRODUCT +#define MATERIALIZE_DEVELOPER_FLAG(type, name, value, doc) /* flag name is constant */ +#define MATERIALIZE_PD_DEVELOPER_FLAG(type, name, doc) /* flag name is constant */ +#define MATERIALIZE_NOTPRODUCT_FLAG(type, name, value, doc) +#else +#define MATERIALIZE_DEVELOPER_FLAG(type, name, value, doc) type name = value; +#define MATERIALIZE_PD_DEVELOPER_FLAG(type, name, doc) type name = pd_##name; +#define MATERIALIZE_NOTPRODUCT_FLAG(type, name, value, doc) type name = value; +#endif + +RUNTIME_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_NOTPRODUCT_FLAG, DECLARE_MANAGEABLE_FLAG, DECLARE_PRODUCT_RW_FLAG) + +RUNTIME_OS_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_NOTPRODUCT_FLAG) diff --git a/hotspot/src/share/vm/runtime/globals_extension.hpp b/hotspot/src/share/vm/runtime/globals_extension.hpp new file mode 100644 index 00000000000..10d22537077 --- /dev/null +++ b/hotspot/src/share/vm/runtime/globals_extension.hpp @@ -0,0 +1,174 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Construct enum of Flag_ constants. + +// Parens left off in the following for the enum decl below. +#define FLAG_MEMBER(flag) Flag_##flag + +#define RUNTIME_PRODUCT_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#define RUNTIME_PD_PRODUCT_FLAG_MEMBER(type, name, doc) FLAG_MEMBER(name), +#define RUNTIME_DIAGNOSTIC_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#define RUNTIME_MANAGEABLE_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#define RUNTIME_PRODUCT_RW_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#ifdef PRODUCT + #define RUNTIME_DEVELOP_FLAG_MEMBER(type, name, value, doc) /* flag is constant */ + #define RUNTIME_PD_DEVELOP_FLAG_MEMBER(type, name, doc) /* flag is constant */ + #define RUNTIME_NOTPRODUCT_FLAG_MEMBER(type, name, value, doc) +#else + #define RUNTIME_DEVELOP_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), + #define RUNTIME_PD_DEVELOP_FLAG_MEMBER(type, name, doc) FLAG_MEMBER(name), + #define RUNTIME_NOTPRODUCT_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#endif + +#define C1_PRODUCT_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#define C1_PD_PRODUCT_FLAG_MEMBER(type, name, doc) FLAG_MEMBER(name), +#ifdef PRODUCT + #define C1_DEVELOP_FLAG_MEMBER(type, name, value, doc) /* flag is constant */ + #define C1_PD_DEVELOP_FLAG_MEMBER(type, name, doc) /* flag is constant */ + #define C1_NOTPRODUCT_FLAG_MEMBER(type, name, value, doc) +#else + #define C1_DEVELOP_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), + #define C1_PD_DEVELOP_FLAG_MEMBER(type, name, doc) FLAG_MEMBER(name), + #define C1_NOTPRODUCT_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#endif + + +#define C2_PRODUCT_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#define C2_PD_PRODUCT_FLAG_MEMBER(type, name, doc) FLAG_MEMBER(name), +#define C2_DIAGNOSTIC_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#ifdef PRODUCT + #define C2_DEVELOP_FLAG_MEMBER(type, name, value, doc) /* flag is constant */ + #define C2_PD_DEVELOP_FLAG_MEMBER(type, name, doc) /* flag is constant */ + #define C2_NOTPRODUCT_FLAG_MEMBER(type, name, value, doc) +#else + #define C2_DEVELOP_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), + #define C2_PD_DEVELOP_FLAG_MEMBER(type, name, doc) FLAG_MEMBER(name), + #define C2_NOTPRODUCT_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#endif + +typedef enum { + RUNTIME_FLAGS(RUNTIME_DEVELOP_FLAG_MEMBER, RUNTIME_PD_DEVELOP_FLAG_MEMBER, RUNTIME_PRODUCT_FLAG_MEMBER, + RUNTIME_PD_PRODUCT_FLAG_MEMBER, RUNTIME_DIAGNOSTIC_FLAG_MEMBER, + RUNTIME_NOTPRODUCT_FLAG_MEMBER, RUNTIME_MANAGEABLE_FLAG_MEMBER, RUNTIME_PRODUCT_RW_FLAG_MEMBER) + RUNTIME_OS_FLAGS(RUNTIME_DEVELOP_FLAG_MEMBER, RUNTIME_PD_DEVELOP_FLAG_MEMBER, RUNTIME_PRODUCT_FLAG_MEMBER, + RUNTIME_PD_PRODUCT_FLAG_MEMBER, RUNTIME_DIAGNOSTIC_FLAG_MEMBER, + RUNTIME_NOTPRODUCT_FLAG_MEMBER) +#ifdef COMPILER1 + C1_FLAGS(C1_DEVELOP_FLAG_MEMBER, C1_PD_DEVELOP_FLAG_MEMBER, C1_PRODUCT_FLAG_MEMBER, C1_PD_PRODUCT_FLAG_MEMBER, + C1_NOTPRODUCT_FLAG_MEMBER) +#endif +#ifdef COMPILER2 + C2_FLAGS(C2_DEVELOP_FLAG_MEMBER, C2_PD_DEVELOP_FLAG_MEMBER, C2_PRODUCT_FLAG_MEMBER, C2_PD_PRODUCT_FLAG_MEMBER, + C2_DIAGNOSTIC_FLAG_MEMBER, C2_NOTPRODUCT_FLAG_MEMBER) +#endif + NUM_CommandLineFlag +} CommandLineFlag; + +// Construct enum of Flag__ constants. + +#define FLAG_MEMBER_WITH_TYPE(flag,type) Flag_##flag##_##type + +#define RUNTIME_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#define RUNTIME_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#define RUNTIME_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#define RUNTIME_MANAGEABLE_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#define RUNTIME_PRODUCT_RW_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#ifdef PRODUCT + #define RUNTIME_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) /* flag is constant */ + #define RUNTIME_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, doc) /* flag is constant */ + #define RUNTIME_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) +#else + #define RUNTIME_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), + #define RUNTIME_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, doc) FLAG_MEMBER_WITH_TYPE(name,type), + #define RUNTIME_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#endif + +#define C1_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#define C1_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#ifdef PRODUCT + #define C1_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) /* flag is constant */ + #define C1_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, doc) /* flag is constant */ + #define C1_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) +#else + #define C1_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), + #define C1_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, doc) FLAG_MEMBER_WITH_TYPE(name,type), + #define C1_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#endif + + +#define C2_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#define C2_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#define C2_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#ifdef PRODUCT + #define C2_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) /* flag is constant */ + #define C2_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, doc) /* flag is constant */ + #define C2_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) +#else + #define C2_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), + #define C2_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, doc) FLAG_MEMBER_WITH_TYPE(name,type), + #define C2_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#endif + +typedef enum { + RUNTIME_FLAGS(RUNTIME_DEVELOP_FLAG_MEMBER_WITH_TYPE, RUNTIME_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE, + RUNTIME_PRODUCT_FLAG_MEMBER_WITH_TYPE, RUNTIME_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE, + RUNTIME_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE, + RUNTIME_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE, + RUNTIME_MANAGEABLE_FLAG_MEMBER_WITH_TYPE, + RUNTIME_PRODUCT_RW_FLAG_MEMBER_WITH_TYPE) +RUNTIME_OS_FLAGS(RUNTIME_DEVELOP_FLAG_MEMBER_WITH_TYPE, RUNTIME_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE, + RUNTIME_PRODUCT_FLAG_MEMBER_WITH_TYPE, RUNTIME_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE, + RUNTIME_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE, + RUNTIME_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE) +#ifdef COMPILER1 + C1_FLAGS(C1_DEVELOP_FLAG_MEMBER_WITH_TYPE, C1_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE, C1_PRODUCT_FLAG_MEMBER_WITH_TYPE, + C1_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE, C1_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE) +#endif +#ifdef COMPILER2 + C2_FLAGS(C2_DEVELOP_FLAG_MEMBER_WITH_TYPE, C2_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE, C2_PRODUCT_FLAG_MEMBER_WITH_TYPE, + C2_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE, C2_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE, C2_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE) +#endif + NUM_CommandLineFlagWithType +} CommandLineFlagWithType; + +#define FLAG_IS_DEFAULT(name) (CommandLineFlagsEx::is_default(FLAG_MEMBER(name))) + +#define FLAG_SET_DEFAULT(name, value) ((name) = (value)) + +#define FLAG_SET_CMDLINE(type, name, value) (CommandLineFlagsEx::type##AtPut(FLAG_MEMBER_WITH_TYPE(name,type), (type)(value), COMMAND_LINE)) +#define FLAG_SET_ERGO(type, name, value) (CommandLineFlagsEx::type##AtPut(FLAG_MEMBER_WITH_TYPE(name,type), (type)(value), ERGONOMIC)) + +// Can't put the following in CommandLineFlags because +// of a circular dependency on the enum definition. +class CommandLineFlagsEx : CommandLineFlags { + public: + static void boolAtPut(CommandLineFlagWithType flag, bool value, FlagValueOrigin origin); + static void intxAtPut(CommandLineFlagWithType flag, intx value, FlagValueOrigin origin); + static void uintxAtPut(CommandLineFlagWithType flag, uintx value, FlagValueOrigin origin); + static void doubleAtPut(CommandLineFlagWithType flag, double value, FlagValueOrigin origin); + static void ccstrAtPut(CommandLineFlagWithType flag, ccstr value, FlagValueOrigin origin); + + static bool is_default(CommandLineFlag flag); +}; diff --git a/hotspot/src/share/vm/runtime/handles.cpp b/hotspot/src/share/vm/runtime/handles.cpp new file mode 100644 index 00000000000..9282eab14cb --- /dev/null +++ b/hotspot/src/share/vm/runtime/handles.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_handles.cpp.incl" + +#ifdef ASSERT +oop* HandleArea::allocate_handle(oop obj) { + assert(_handle_mark_nesting > 1, "memory leak: allocating handle outside HandleMark"); + assert(_no_handle_mark_nesting == 0, "allocating handle inside NoHandleMark"); + assert(SharedSkipVerify || obj->is_oop(), "sanity check"); + return real_allocate_handle(obj); +} + +Handle::Handle(Thread* thread, oop obj) { + assert(thread == Thread::current(), "sanity check"); + if (obj == NULL) { + _handle = NULL; + } else { + _handle = thread->handle_area()->allocate_handle(obj); + } +} + +#endif + +static uintx chunk_oops_do(OopClosure* f, Chunk* chunk, char* chunk_top) { + oop* bottom = (oop*) chunk->bottom(); + oop* top = (oop*) chunk_top; + uintx handles_visited = top - bottom; + assert(top >= bottom && top <= (oop*) chunk->top(), "just checking"); + // during GC phase 3, a handle may be a forward pointer that + // is not yet valid, so loosen the assertion + while (bottom < top) { +// assert((*bottom)->is_oop(), "handle should point to oop"); + assert(Universe::heap()->is_in(*bottom), "handle should be valid heap address"); + f->do_oop(bottom++); + } + return handles_visited; +} + +// Used for debugging handle allocation. +NOT_PRODUCT(jint _nof_handlemarks = 0;) + +void HandleArea::oops_do(OopClosure* f) { + uintx handles_visited = 0; + // First handle the current chunk. It is filled to the high water mark. + handles_visited += chunk_oops_do(f, _chunk, _hwm); + // Then handle all previous chunks. They are completely filled. + Chunk* k = _first; + while(k != _chunk) { + handles_visited += chunk_oops_do(f, k, k->top()); + k = k->next(); + } + + // The thread local handle areas should not get very large + if (TraceHandleAllocation && handles_visited > TotalHandleAllocationLimit) { +#ifdef ASSERT + warning("%d: Visited in HandleMark : %d", + _nof_handlemarks, handles_visited); +#else + warning("Visited in HandleMark : %d", handles_visited); +#endif + } + if (_prev != NULL) _prev->oops_do(f); +} + +void HandleMark::initialize(Thread* thread) { + _thread = thread; + // Save area + _area = thread->handle_area(); + // Save current top + _chunk = _area->_chunk; + _hwm = _area->_hwm; + _max = _area->_max; + NOT_PRODUCT(_size_in_bytes = _area->_size_in_bytes;) + debug_only(_area->_handle_mark_nesting++); + assert(_area->_handle_mark_nesting > 0, "must stack allocate HandleMarks"); + debug_only(Atomic::inc(&_nof_handlemarks);) + + // Link this in the thread + set_previous_handle_mark(thread->last_handle_mark()); + thread->set_last_handle_mark(this); +} + + +HandleMark::~HandleMark() { + HandleArea* area = _area; // help compilers with poor alias analysis + assert(area == _thread->handle_area(), "sanity check"); + assert(area->_handle_mark_nesting > 0, "must stack allocate HandleMarks" ); + debug_only(area->_handle_mark_nesting--); + + // Debug code to trace the number of handles allocated per mark/ +#ifdef ASSERT + if (TraceHandleAllocation) { + size_t handles = 0; + Chunk *c = _chunk->next(); + if (c == NULL) { + handles = area->_hwm - _hwm; // no new chunk allocated + } else { + handles = _max - _hwm; // add rest in first chunk + while(c != NULL) { + handles += c->length(); + c = c->next(); + } + handles -= area->_max - area->_hwm; // adjust for last trunk not full + } + handles /= sizeof(void *); // Adjust for size of a handle + if (handles > HandleAllocationLimit) { + // Note: _nof_handlemarks is only set in debug mode + warning("%d: Allocated in HandleMark : %d", _nof_handlemarks, handles); + } + } +#endif + + // Delete later chunks + if( _chunk->next() ) { + _chunk->next_chop(); + } + // Roll back arena to saved top markers + area->_chunk = _chunk; + area->_hwm = _hwm; + area->_max = _max; + NOT_PRODUCT(area->set_size_in_bytes(_size_in_bytes);) +#ifdef ASSERT + // clear out first chunk (to detect allocation bugs) + if (ZapVMHandleArea) { + memset(_hwm, badHandleValue, _max - _hwm); + } + Atomic::dec(&_nof_handlemarks); +#endif + + // Unlink this from the thread + _thread->set_last_handle_mark(previous_handle_mark()); +} + +#ifdef ASSERT + +NoHandleMark::NoHandleMark() { + HandleArea* area = Thread::current()->handle_area(); + area->_no_handle_mark_nesting++; + assert(area->_no_handle_mark_nesting > 0, "must stack allocate NoHandleMark" ); +} + + +NoHandleMark::~NoHandleMark() { + HandleArea* area = Thread::current()->handle_area(); + assert(area->_no_handle_mark_nesting > 0, "must stack allocate NoHandleMark" ); + area->_no_handle_mark_nesting--; +} + + +ResetNoHandleMark::ResetNoHandleMark() { + HandleArea* area = Thread::current()->handle_area(); + _no_handle_mark_nesting = area->_no_handle_mark_nesting; + area->_no_handle_mark_nesting = 0; +} + + +ResetNoHandleMark::~ResetNoHandleMark() { + HandleArea* area = Thread::current()->handle_area(); + area->_no_handle_mark_nesting = _no_handle_mark_nesting; +} + +#endif diff --git a/hotspot/src/share/vm/runtime/handles.hpp b/hotspot/src/share/vm/runtime/handles.hpp new file mode 100644 index 00000000000..55e9b41fa20 --- /dev/null +++ b/hotspot/src/share/vm/runtime/handles.hpp @@ -0,0 +1,348 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +//------------------------------------------------------------------------------------------------------------------------ +// In order to preserve oops during garbage collection, they should be +// allocated and passed around via Handles within the VM. A handle is +// simply an extra indirection allocated in a thread local handle area. +// +// A handle is a ValueObj, so it can be passed around as a value, can +// be used as a parameter w/o using &-passing, and can be returned as a +// return value. +// +// oop parameters and return types should be Handles whenever feasible. +// +// Handles are declared in a straight-forward manner, e.g. +// +// oop obj = ...; +// Handle h1(obj); // allocate new handle +// Handle h2(thread, obj); // faster allocation when current thread is known +// Handle h3; // declare handle only, no allocation occurs +// ... +// h3 = h1; // make h3 refer to same indirection as h1 +// oop obj2 = h2(); // get handle value +// h1->print(); // invoking operation on oop +// +// Handles are specialized for different oop types to provide extra type +// information and avoid unnecessary casting. For each oop type xxxOop +// there is a corresponding handle called xxxHandle, e.g. +// +// oop Handle +// methodOop methodHandle +// instanceOop instanceHandle +// +// For klassOops, it is often useful to model the Klass hierarchy in order +// to get access to the klass_part without casting. For each xxxKlass there +// is a corresponding handle called xxxKlassHandle, e.g. +// +// klassOop Klass KlassHandle +// klassOop methodKlass methodKlassHandle +// klassOop instanceKlass instanceKlassHandle +// + +//------------------------------------------------------------------------------------------------------------------------ +// Base class for all handles. Provides overloading of frequently +// used operators for ease of use. + +class Handle VALUE_OBJ_CLASS_SPEC { + private: + oop* _handle; + + protected: + oop obj() const { return _handle == NULL ? (oop)NULL : *_handle; } + oop non_null_obj() const { assert(_handle != NULL, "resolving NULL handle"); return *_handle; } + + public: + // Constructors + Handle() { _handle = NULL; } + Handle(oop obj); +#ifndef ASSERT + Handle(Thread* thread, oop obj); +#else + // Don't inline body with assert for current thread + Handle(Thread* thread, oop obj); +#endif // ASSERT + + // General access + oop operator () () const { return obj(); } + oop operator -> () const { return non_null_obj(); } + bool operator == (oop o) const { return obj() == o; } + bool operator == (const Handle& h) const { return obj() == h.obj(); } + + // Null checks + bool is_null() const { return _handle == NULL; } + bool not_null() const { return _handle != NULL; } + + // Debugging + void print() { obj()->print(); } + + // Direct interface, use very sparingly. + // Used by JavaCalls to quickly convert handles and to create handles static data structures. + // Constructor takes a dummy argument to prevent unintentional type conversion in C++. + Handle(oop *handle, bool dummy) { _handle = handle; } + + // Raw handle access. Allows easy duplication of Handles. This can be very unsafe + // since duplicates is only valid as long as original handle is alive. + oop* raw_value() { return _handle; } + static oop raw_resolve(oop *handle) { return handle == NULL ? (oop)NULL : *handle; } +}; + + +//------------------------------------------------------------------------------------------------------------------------ +// Base class for Handles containing klassOops. Provides overloading of frequently +// used operators for ease of use and typed access to the Klass part. +class KlassHandle: public Handle { + protected: + klassOop obj() const { return (klassOop)Handle::obj(); } + klassOop non_null_obj() const { return (klassOop)Handle::non_null_obj(); } + Klass* as_klass() const { return non_null_obj()->klass_part(); } + + public: + // Constructors + KlassHandle () : Handle() {} + KlassHandle (oop obj) : Handle(obj) { + assert(SharedSkipVerify || is_null() || obj->is_klass(), "not a klassOop"); + } + KlassHandle (Klass* kl) : Handle(kl ? kl->as_klassOop() : (klassOop)NULL) { + assert(SharedSkipVerify || is_null() || obj()->is_klass(), "not a klassOop"); + } + + // Faster versions passing Thread + KlassHandle (Thread* thread, oop obj) : Handle(thread, obj) { + assert(SharedSkipVerify || is_null() || obj->is_klass(), "not a klassOop"); + } + KlassHandle (Thread *thread, Klass* kl) + : Handle(thread, kl ? kl->as_klassOop() : (klassOop)NULL) { + assert(is_null() || obj()->is_klass(), "not a klassOop"); + } + + // General access + klassOop operator () () const { return obj(); } + Klass* operator -> () const { return as_klass(); } +}; + + +//------------------------------------------------------------------------------------------------------------------------ +// Specific Handles for different oop types +#define DEF_HANDLE(type, is_a) \ + class type##Handle; \ + class type##Handle: public Handle { \ + protected: \ + type##Oop obj() const { return (type##Oop)Handle::obj(); } \ + type##Oop non_null_obj() const { return (type##Oop)Handle::non_null_obj(); } \ + \ + public: \ + /* Constructors */ \ + type##Handle () : Handle() {} \ + type##Handle (type##Oop obj) : Handle((oop)obj) { \ + assert(SharedSkipVerify || is_null() || ((oop)obj)->is_a(), \ + "illegal type"); \ + } \ + type##Handle (Thread* thread, type##Oop obj) : Handle(thread, (oop)obj) { \ + assert(SharedSkipVerify || is_null() || ((oop)obj)->is_a(), "illegal type"); \ + } \ + \ + /* Special constructor, use sparingly */ \ + type##Handle (type##Oop *handle, bool dummy) : Handle((oop*)handle, dummy) {} \ + \ + /* Operators for ease of use */ \ + type##Oop operator () () const { return obj(); } \ + type##Oop operator -> () const { return non_null_obj(); } \ + }; + + +DEF_HANDLE(instance , is_instance ) +DEF_HANDLE(method , is_method ) +DEF_HANDLE(constMethod , is_constMethod ) +DEF_HANDLE(methodData , is_methodData ) +DEF_HANDLE(array , is_array ) +DEF_HANDLE(constantPool , is_constantPool ) +DEF_HANDLE(constantPoolCache, is_constantPoolCache) +DEF_HANDLE(objArray , is_objArray ) +DEF_HANDLE(typeArray , is_typeArray ) +DEF_HANDLE(symbol , is_symbol ) + +//------------------------------------------------------------------------------------------------------------------------ +// Specific KlassHandles for different Klass types + +#define DEF_KLASS_HANDLE(type, is_a) \ + class type##Handle : public KlassHandle { \ + public: \ + /* Constructors */ \ + type##Handle () : KlassHandle() {} \ + type##Handle (klassOop obj) : KlassHandle(obj) { \ + assert(SharedSkipVerify || is_null() || obj->klass_part()->is_a(), \ + "illegal type"); \ + } \ + type##Handle (Thread* thread, klassOop obj) : KlassHandle(thread, obj) { \ + assert(SharedSkipVerify || is_null() || obj->klass_part()->is_a(), \ + "illegal type"); \ + } \ + \ + /* Access to klass part */ \ + type* operator -> () const { return (type*)obj()->klass_part(); } \ + \ + static type##Handle cast(KlassHandle h) { return type##Handle(h()); } \ + \ + }; + + +DEF_KLASS_HANDLE(instanceKlass , oop_is_instance_slow ) +DEF_KLASS_HANDLE(methodKlass , oop_is_method ) +DEF_KLASS_HANDLE(constMethodKlass , oop_is_constMethod ) +DEF_KLASS_HANDLE(klassKlass , oop_is_klass ) +DEF_KLASS_HANDLE(arrayKlassKlass , oop_is_arrayKlass ) +DEF_KLASS_HANDLE(objArrayKlassKlass , oop_is_objArrayKlass ) +DEF_KLASS_HANDLE(typeArrayKlassKlass , oop_is_typeArrayKlass) +DEF_KLASS_HANDLE(arrayKlass , oop_is_array ) +DEF_KLASS_HANDLE(typeArrayKlass , oop_is_typeArray_slow) +DEF_KLASS_HANDLE(objArrayKlass , oop_is_objArray_slow ) +DEF_KLASS_HANDLE(symbolKlass , oop_is_symbol ) +DEF_KLASS_HANDLE(constantPoolKlass , oop_is_constantPool ) +DEF_KLASS_HANDLE(constantPoolCacheKlass, oop_is_constantPool ) + + +//------------------------------------------------------------------------------------------------------------------------ +// Thread local handle area + +class HandleArea: public Arena { + friend class HandleMark; + friend class NoHandleMark; + friend class ResetNoHandleMark; +#ifdef ASSERT + int _handle_mark_nesting; + int _no_handle_mark_nesting; +#endif + HandleArea* _prev; // link to outer (older) area + public: + // Constructor + HandleArea(HandleArea* prev) { + debug_only(_handle_mark_nesting = 0); + debug_only(_no_handle_mark_nesting = 0); + _prev = prev; + } + + // Handle allocation + private: + oop* real_allocate_handle(oop obj) { +#ifdef ASSERT + oop* handle = (oop*) (UseMallocOnly ? internal_malloc_4(oopSize) : Amalloc_4(oopSize)); +#else + oop* handle = (oop*) Amalloc_4(oopSize); +#endif + *handle = obj; + return handle; + } + public: +#ifdef ASSERT + oop* allocate_handle(oop obj); +#else + oop* allocate_handle(oop obj) { return real_allocate_handle(obj); } +#endif + + // Garbage collection support + void oops_do(OopClosure* f); + + // Number of handles in use + size_t used() const { return Arena::used() / oopSize; } + + debug_only(bool no_handle_mark_active() { return _no_handle_mark_nesting > 0; }) +}; + + +//------------------------------------------------------------------------------------------------------------------------ +// Handles are allocated in a (growable) thread local handle area. Deallocation +// is managed using a HandleMark. It should normally not be necessary to use +// HandleMarks manually. +// +// A HandleMark constructor will record the current handle area top, and the +// desctructor will reset the top, destroying all handles allocated in between. +// The following code will therefore NOT work: +// +// Handle h; +// { +// HandleMark hm; +// h = Handle(obj); +// } +// h()->print(); // WRONG, h destroyed by HandleMark destructor. +// +// If h has to be preserved, it can be converted to an oop or a local JNI handle +// across the HandleMark boundary. + +// The base class of HandleMark should have been StackObj but we also heap allocate +// a HandleMark when a thread is created. + +class HandleMark { + private: + Thread *_thread; // thread that owns this mark + HandleArea *_area; // saved handle area + Chunk *_chunk; // saved arena chunk + char *_hwm, *_max; // saved arena info + NOT_PRODUCT(size_t _size_in_bytes;) // size of handle area + // Link to previous active HandleMark in thread + HandleMark* _previous_handle_mark; + + void initialize(Thread* thread); // common code for constructors + void set_previous_handle_mark(HandleMark* mark) { _previous_handle_mark = mark; } + HandleMark* previous_handle_mark() const { return _previous_handle_mark; } + + public: + HandleMark(); // see handles_inline.hpp + HandleMark(Thread* thread) { initialize(thread); } + ~HandleMark(); + + // Functions used by HandleMarkCleaner + // called in the constructor of HandleMarkCleaner + void push(); + // called in the destructor of HandleMarkCleaner + void pop_and_restore(); +}; + +//------------------------------------------------------------------------------------------------------------------------ +// A NoHandleMark stack object will verify that no handles are allocated +// in its scope. Enabled in debug mode only. + +class NoHandleMark: public StackObj { + public: +#ifdef ASSERT + NoHandleMark(); + ~NoHandleMark(); +#else + NoHandleMark() {} + ~NoHandleMark() {} +#endif +}; + + +class ResetNoHandleMark: public StackObj { + int _no_handle_mark_nesting; + public: +#ifdef ASSERT + ResetNoHandleMark(); + ~ResetNoHandleMark(); +#else + ResetNoHandleMark() {} + ~ResetNoHandleMark() {} +#endif +}; diff --git a/hotspot/src/share/vm/runtime/handles.inline.hpp b/hotspot/src/share/vm/runtime/handles.inline.hpp new file mode 100644 index 00000000000..b4d4b319731 --- /dev/null +++ b/hotspot/src/share/vm/runtime/handles.inline.hpp @@ -0,0 +1,73 @@ +/* + * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// these inline functions are in a separate file to break an include cycle +// between Thread and Handle + +inline Handle::Handle(oop obj) { + if (obj == NULL) { + _handle = NULL; + } else { + _handle = Thread::current()->handle_area()->allocate_handle(obj); + } +} + + +#ifndef ASSERT +inline Handle::Handle(Thread* thread, oop obj) { + assert(thread == Thread::current(), "sanity check"); + if (obj == NULL) { + _handle = NULL; + } else { + _handle = thread->handle_area()->allocate_handle(obj); + } +} +#endif // ASSERT + + +inline HandleMark::HandleMark() { + initialize(Thread::current()); +} + + +inline void HandleMark::push() { + // This is intentionally a NOP. pop_and_restore will reset + // values to the HandleMark further down the stack, typically + // in JavaCalls::call_helper. + debug_only(_area->_handle_mark_nesting++); +} + +inline void HandleMark::pop_and_restore() { + HandleArea* area = _area; // help compilers with poor alias analysis + // Delete later chunks + if( _chunk->next() ) { + _chunk->next_chop(); + } + // Roll back arena to saved top markers + area->_chunk = _chunk; + area->_hwm = _hwm; + area->_max = _max; + NOT_PRODUCT(area->set_size_in_bytes(_size_in_bytes);) + debug_only(area->_handle_mark_nesting--); +} diff --git a/hotspot/src/share/vm/runtime/hpi.cpp b/hotspot/src/share/vm/runtime/hpi.cpp new file mode 100644 index 00000000000..18e4e797623 --- /dev/null +++ b/hotspot/src/share/vm/runtime/hpi.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_hpi.cpp.incl" + +extern "C" { + static void unimplemented_panic(const char *fmt, ...) { + Unimplemented(); + } + + static void unimplemented_monitorRegister(sys_mon_t *mid, char *info_str) { + Unimplemented(); + } +} + +static vm_calls_t callbacks = { + jio_fprintf, + unimplemented_panic, + unimplemented_monitorRegister, + + NULL, // unused + NULL, // unused + NULL // unused +}; + +GetInterfaceFunc hpi::_get_interface = NULL; +HPI_FileInterface* hpi::_file = NULL; +HPI_SocketInterface* hpi::_socket = NULL; +HPI_LibraryInterface* hpi::_library = NULL; +HPI_SystemInterface* hpi::_system = NULL; + +jint hpi::initialize() +{ + initialize_get_interface(&callbacks); + if (_get_interface == NULL) + return JNI_ERR; + + jint result; + + result = (*_get_interface)((void **)&_file, "File", 1); + if (result != 0) { + if (TraceHPI) tty->print_cr("Can't find HPI_FileInterface"); + return JNI_ERR; + } + + + result = (*_get_interface)((void **)&_library, "Library", 1); + if (result != 0) { + if (TraceHPI) tty->print_cr("Can't find HPI_LibraryInterface"); + return JNI_ERR; + } + + result = (*_get_interface)((void **)&_system, "System", 1); + if (result != 0) { + if (TraceHPI) tty->print_cr("Can't find HPI_SystemInterface"); + return JNI_ERR; + } + + return JNI_OK; +} + +jint hpi::initialize_socket_library() +{ + if (_get_interface == NULL) { + if (TraceHPI) { + tty->print_cr("Fatal HPI error: reached initialize_socket_library with NULL _get_interface"); + } + return JNI_ERR; + } + + jint result; + result = (*_get_interface)((void **)&_socket, "Socket", 1); + if (result != 0) { + if (TraceHPI) tty->print_cr("Can't find HPI_SocketInterface"); + return JNI_ERR; + } + + return JNI_OK; +} diff --git a/hotspot/src/share/vm/runtime/hpi.hpp b/hotspot/src/share/vm/runtime/hpi.hpp new file mode 100644 index 00000000000..00fc21c90a5 --- /dev/null +++ b/hotspot/src/share/vm/runtime/hpi.hpp @@ -0,0 +1,224 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// C++ wrapper to HPI. +// + +class hpi : AllStatic { + +private: + static GetInterfaceFunc _get_interface; + static HPI_FileInterface* _file; + static HPI_SocketInterface* _socket; + static HPI_LibraryInterface* _library; + static HPI_SystemInterface* _system; + +private: + static void initialize_get_interface(vm_calls_t *callbacks); + +public: + // Load and initialize everything except sockets. + static jint initialize(); + + // Socket library needs to be lazy intialized because eagerly + // loading Winsock is known to cause "connect to your ISP" + // dialog to show up. Or so goes the legend. + static jint initialize_socket_library(); + + // HPI_FileInterface + static inline char* native_path(char *path); + static inline int file_type(const char *path); + static inline int open(const char *name, int mode, int perm); + static inline int close(int fd); + static inline jlong lseek(int fd, jlong off, int whence); + static inline int ftruncate(int fd, jlong length); + static inline int fsync(int fd); + static inline int available(int fd, jlong *bytes); + static inline size_t read(int fd, void *buf, unsigned int nBytes); + static inline size_t write(int fd, const void *buf, unsigned int nBytes); + static inline int fsize(int fd, jlong *size); + + // HPI_SocketInterface + static inline int socket(int domain, int type, int protocol); + static inline int socket_close(int fd); + static inline int socket_shutdown(int fd, int howto); + static inline int recv(int fd, char *buf, int nBytes, int flags); + static inline int send(int fd, char *buf, int nBytes, int flags); + static inline int timeout(int fd, long timeout); + static inline int listen(int fd, int count); + static inline int connect(int fd, struct sockaddr *him, int len); + static inline int bind(int fd, struct sockaddr *him, int len); + static inline int accept(int fd, struct sockaddr *him, int *len); + static inline int recvfrom(int fd, char *buf, int nbytes, int flags, + struct sockaddr *from, int *fromlen); + static inline int get_sock_name(int fd, struct sockaddr *him, int *len); + static inline int sendto(int fd, char *buf, int len, int flags, + struct sockaddr *to, int tolen); + static inline int socket_available(int fd, jint *pbytes); + + static inline int get_sock_opt(int fd, int level, int optname, + char *optval, int* optlen); + static inline int set_sock_opt(int fd, int level, int optname, + const char *optval, int optlen); + static inline int get_host_name(char* name, int namelen); + static inline struct hostent* get_host_by_addr(const char* name, int len, int type); + static inline struct hostent* get_host_by_name(char* name); + static inline struct protoent* get_proto_by_name(char* name); + + // HPI_LibraryInterface + static inline void dll_build_name(char *buf, int buf_len, char* path, + const char *name); + static inline void* dll_load(const char *name, char *ebuf, int ebuflen); + static inline void dll_unload(void *lib); + static inline void* dll_lookup(void *lib, const char *name); + + // HPI_SystemInterface + static inline int lasterror(char *buf, int len); +}; + +// +// Macros that provide inline bodies for the functions. +// + +#define HPIDECL(name, names, intf, func, ret_type, ret_fmt, arg_type, arg_print, arg) \ + inline ret_type hpi::name arg_type { \ + if (TraceHPI) { \ + tty->print("hpi::" names "("); \ + tty->print arg_print ; \ + tty->print(") = "); \ + } \ + ret_type result = (*intf->func) arg ; \ + if (TraceHPI) { \ + tty->print_cr(ret_fmt, result); \ + } \ + return result; \ + } + +// Macro to facilitate moving HPI functionality into the vm. +// See bug 6348631. The only difference between this macro and +// HPIDECL is that we call a vm method rather than use the HPI +// transfer vector. Ultimately, we'll replace HPIDECL with +// VM_HPIDECL for all hpi methods. +#define VM_HPIDECL(name, names, func, ret_type, ret_fmt, arg_type,arg_print, arg) \ + inline ret_type hpi::name arg_type { \ + if (TraceHPI) { \ + tty->print("hpi::" names "("); \ + tty->print arg_print ; \ + tty->print(") = "); \ + } \ + ret_type result = func arg ; \ + if (TraceHPI) { \ + tty->print_cr(ret_fmt, result); \ + } \ + return result; \ + } + + + +#define HPIDECL_VOID(name, names, intf, func, arg_type, arg_print, arg) \ + inline void hpi::name arg_type { \ + if (TraceHPI) { \ + tty->print("hpi::" names "("); \ + tty->print arg_print ; \ + tty->print_cr(") = void"); \ + } \ + (*intf->func) arg ; \ + } + + +// The macro calls below realize into +// inline char * hpi::native_path(...) { inlined_body; } +// etc. + +// HPI_FileInterface + +HPIDECL(native_path, "native_path", _file, NativePath, char *, "%s", + (char *path), + ("path = %s", path), + (path)); + +HPIDECL(file_type, "file_type", _file, FileType, int, "%d", + (const char *path), + ("path = %s", path), + (path)); + +HPIDECL(open, "open", _file, Open, int, "%d", + (const char *name, int mode, int perm), + ("name = %s, mode = %d, perm = %d", name, mode, perm), + (name, mode, perm)); + +HPIDECL(lseek, "seek", _file, Seek, jlong, "(a jlong)", + (int fd, jlong off, int whence), + ("fd = %d, off = (a jlong), whence = %d", fd, /* off, */ whence), + (fd, off, whence)); + +HPIDECL(ftruncate, "ftruncate", _file, SetLength, int, "%d", + (int fd, jlong length), + ("fd = %d, length = (a jlong)", fd /*, length */), + (fd, length)); + +HPIDECL(fsync, "fsync", _file, Sync, int, "%d", + (int fd), + ("fd = %d", fd), + (fd)); + +HPIDECL(available, "available", _file, Available, int, "%d", + (int fd, jlong *bytes), + ("fd = %d, bytes = %p", fd, bytes), + (fd, bytes)); + +HPIDECL(fsize, "fsize", _file, FileSizeFD, int, "%d", + (int fd, jlong *size), + ("fd = %d, size = %p", fd, size), + (fd, size)); + +// HPI_LibraryInterface +HPIDECL_VOID(dll_build_name, "dll_build_name", _library, BuildLibName, + (char *buf, int buf_len, char *path, const char *name), + ("buf = %p, buflen = %d, path = %s, name = %s", + buf, buf_len, path, name), + (buf, buf_len, path, name)); + +VM_HPIDECL(dll_load, "dll_load", os::dll_load, + void *, "(void *)%p", + (const char *name, char *ebuf, int ebuflen), + ("name = %s, ebuf = %p, ebuflen = %d", name, ebuf, ebuflen), + (name, ebuf, ebuflen)); + +HPIDECL_VOID(dll_unload, "dll_unload", _library, UnloadLibrary, + (void *lib), + ("lib = %p", lib), + (lib)); + +HPIDECL(dll_lookup, "dll_lookup", _library, FindLibraryEntry, void *, "%p", + (void *lib, const char *name), + ("lib = %p, name = %s", lib, name), + (lib, name)); + +// HPI_SystemInterface +HPIDECL(lasterror, "lasterror", _system, GetLastErrorString, int, "%d", + (char *buf, int len), + ("buf = %p, len = %d", buf, len), + (buf, len)); diff --git a/hotspot/src/share/vm/runtime/icache.cpp b/hotspot/src/share/vm/runtime/icache.cpp new file mode 100644 index 00000000000..bafc17b605b --- /dev/null +++ b/hotspot/src/share/vm/runtime/icache.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_icache.cpp.incl" + +// The flush stub function address +AbstractICache::flush_icache_stub_t AbstractICache::_flush_icache_stub = NULL; + +void AbstractICache::initialize() { + // Making this stub must be FIRST use of assembler + ResourceMark rm; + + BufferBlob* b = BufferBlob::create("flush_icache_stub", ICache::stub_size); + CodeBuffer c(b->instructions_begin(), b->instructions_size()); + + ICacheStubGenerator g(&c); + g.generate_icache_flush(&_flush_icache_stub); + + // The first use of flush_icache_stub must apply it to itself. + // The StubCodeMark destructor in generate_icache_flush will + // call Assembler::flush, which in turn will call invalidate_range, + // which will in turn call the flush stub. Thus we don't need an + // explicit call to invalidate_range here. This assumption is + // checked in invalidate_range. +} + +void AbstractICache::call_flush_stub(address start, int lines) { + // The business with the magic number is just a little security. + // We cannot call the flush stub when generating the flush stub + // because it isn't there yet. So, the stub also returns its third + // parameter. This is a cheap check that the stub was really executed. + static int magic = 0xbaadbabe; + + int auto_magic = magic; // Make a local copy to avoid race condition + int r = (*_flush_icache_stub)(start, lines, auto_magic); + guarantee(r == auto_magic, "flush stub routine did not execute"); + ++magic; +} + +void AbstractICache::invalidate_word(address addr) { + // Because this is called for instruction patching on the fly, long after + // bootstrapping, we execute the stub directly. Account for a 4-byte word + // spanning two cache lines by computing a start line address by rounding + // addr down to a line_size boundary, and an end line address by adding + // the word size - 1 and rounding the result down to a line_size boundary. + // If we just added word size, we'd mistakenly flush the next cache line + // if the word to be flushed started in the last 4 bytes of the line. + // Doing that would segv if the next line weren't mapped. + + const int word_size_in_bytes = 4; // Always, regardless of platform + + intptr_t start_line = ((intptr_t)addr + 0) & ~(ICache::line_size - 1); + intptr_t end_line = ((intptr_t)addr + word_size_in_bytes - 1) + & ~(ICache::line_size - 1); + (*_flush_icache_stub)((address)start_line, start_line == end_line ? 1 : 2, 0); +} + +void AbstractICache::invalidate_range(address start, int nbytes) { + static bool firstTime = true; + if (firstTime) { + guarantee(start == CAST_FROM_FN_PTR(address, _flush_icache_stub), + "first flush should be for flush stub"); + firstTime = false; + return; + } + if (nbytes == 0) { + return; + } + // Align start address to an icache line boundary and transform + // nbytes to an icache line count. + const uint line_offset = mask_address_bits(start, ICache::line_size-1); + if (line_offset != 0) { + start -= line_offset; + nbytes += line_offset; + } + call_flush_stub(start, round_to(nbytes, ICache::line_size) >> + ICache::log2_line_size); +} + +// For init.cpp +void icache_init() { + ICache::initialize(); +} diff --git a/hotspot/src/share/vm/runtime/icache.hpp b/hotspot/src/share/vm/runtime/icache.hpp new file mode 100644 index 00000000000..13e6e73b9d2 --- /dev/null +++ b/hotspot/src/share/vm/runtime/icache.hpp @@ -0,0 +1,115 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Interface for updating the instruction cache. Whenever the VM modifies +// code, part of the processor instruction cache potentially has to be flushed. + +// Default implementation is in icache.cpp, and can be hidden per-platform. +// Most platforms must provide only ICacheStubGenerator::generate_icache_flush(). +// Platforms that don't require icache flushing can just nullify the public +// members of AbstractICache in their ICache class. AbstractICache should never +// be referenced other than by deriving the ICache class from it. +// +// The code for the ICache class and for generate_icache_flush() must be in +// architecture-specific files, i.e., icache_.hpp/.cpp + +class AbstractICache : AllStatic { + public: + // The flush stub signature + typedef int (*flush_icache_stub_t)(address addr, int lines, int magic); + + protected: + // The flush stub function address + static flush_icache_stub_t _flush_icache_stub; + + // Call the flush stub + static void call_flush_stub(address start, int lines); + + public: + enum { + stub_size = 0, // Size of the icache flush stub in bytes + line_size = 0, // Icache line size in bytes + log2_line_size = 0 // log2(line_size) + }; + + static void initialize(); + static void invalidate_word(address addr); + static void invalidate_range(address start, int nbytes); +}; + + +// Must be included before the definition of ICacheStubGenerator +// because ICacheStubGenerator uses ICache definitions. + +#include "incls/_icache_pd.hpp.incl" + + +class ICacheStubGenerator : public StubCodeGenerator { + public: + ICacheStubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} + + // Generate the icache flush stub. + // + // Since we cannot flush the cache when this stub is generated, + // it must be generated first, and just to be sure, we do extra + // work to allow a check that these instructions got executed. + // + // The flush stub has three parameters (see flush_icache_stub_t). + // + // addr - Start address, must be aligned at log2_line_size + // lines - Number of line_size icache lines to flush + // magic - Magic number copied to result register to make sure + // the stub executed properly + // + // A template for generate_icache_flush is + // + // #define __ _masm-> + // + // void ICacheStubGenerator::generate_icache_flush( + // ICache::flush_icache_stub_t* flush_icache_stub + // ) { + // StubCodeMark mark(this, "ICache", "flush_icache_stub"); + // + // address start = __ pc(); + // + // // emit flush stub asm code + // + // // Must be set here so StubCodeMark destructor can call the flush stub. + // *flush_icache_stub = (ICache::flush_icache_stub_t)start; + // }; + // + // #undef __ + // + // The first use of flush_icache_stub must apply it to itself. The + // StubCodeMark destructor in generate_icache_flush will call Assembler::flush, + // which in turn will call invalidate_range (see asm/assembler.cpp), which + // in turn will call the flush stub *before* generate_icache_flush returns. + // The usual method of having generate_icache_flush return the address of the + // stub to its caller, which would then, e.g., store that address in + // flush_icache_stub, won't work. generate_icache_flush must itself set + // flush_icache_stub to the address of the stub it generates before + // the StubCodeMark destructor is invoked. + + void generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub); +}; diff --git a/hotspot/src/share/vm/runtime/init.cpp b/hotspot/src/share/vm/runtime/init.cpp new file mode 100644 index 00000000000..b93099ecb8b --- /dev/null +++ b/hotspot/src/share/vm/runtime/init.cpp @@ -0,0 +1,160 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_init.cpp.incl" + +// Initialization done by VM thread in vm_init_globals() +void check_ThreadShadow(); +void check_basic_types(); +void eventlog_init(); +void mutex_init(); +void chunkpool_init(); +void perfMemory_init(); + +// Initialization done by Java thread in init_globals() +void management_init(); +void vtune_init(); +void bytecodes_init(); +void classLoader_init(); +void codeCache_init(); +void VM_Version_init(); +void JDK_Version_init(); +void stubRoutines_init1(); +jint universe_init(); // dependent on codeCache_init and stubRoutines_init +void interpreter_init(); // before any methods loaded +void invocationCounter_init(); // before any methods loaded +void marksweep_init(); +void accessFlags_init(); +void templateTable_init(); +void InterfaceSupport_init(); +void universe2_init(); // dependent on codeCache_init and stubRoutines_init +void referenceProcessor_init(); +void jni_handles_init(); +void vmStructs_init(); + +void vtableStubs_init(); +void InlineCacheBuffer_init(); +void compilerOracle_init(); +void compilationPolicy_init(); + + +// Initialization after compiler initialization +bool universe_post_init(); // must happen after compiler_init +void javaClasses_init(); // must happen after vtable initialization +void stubRoutines_init2(); // note: StubRoutines need 2-phase init + +// Do not disable thread-local-storage, as it is important for some +// JNI/JVM/JVMTI functions and signal handlers to work properly +// during VM shutdown +void perfMemory_exit(); +void ostream_exit(); + +void vm_init_globals() { + check_ThreadShadow(); + check_basic_types(); + eventlog_init(); + mutex_init(); + chunkpool_init(); + perfMemory_init(); +} + + +jint init_globals() { + HandleMark hm; + management_init(); + vtune_init(); + bytecodes_init(); + classLoader_init(); + codeCache_init(); + VM_Version_init(); + JDK_Version_init(); + stubRoutines_init1(); + jint status = universe_init(); // dependent on codeCache_init and stubRoutines_init + if (status != JNI_OK) + return status; + + interpreter_init(); // before any methods loaded + invocationCounter_init(); // before any methods loaded + marksweep_init(); + accessFlags_init(); + templateTable_init(); + InterfaceSupport_init(); + SharedRuntime::generate_stubs(); + universe2_init(); // dependent on codeCache_init and stubRoutines_init + referenceProcessor_init(); + jni_handles_init(); +#ifndef VM_STRUCTS_KERNEL + vmStructs_init(); +#endif // VM_STRUCTS_KERNEL + + vtableStubs_init(); + InlineCacheBuffer_init(); + compilerOracle_init(); + compilationPolicy_init(); + VMRegImpl::set_regName(); + + if (!universe_post_init()) { + return JNI_ERR; + } + javaClasses_init(); // must happen after vtable initialization + stubRoutines_init2(); // note: StubRoutines need 2-phase init + + // Although we'd like to, we can't easily do a heap verify + // here because the main thread isn't yet a JavaThread, so + // its TLAB may not be made parseable from the usual interfaces. + if (VerifyBeforeGC && !UseTLAB && + Universe::heap()->total_collections() >= VerifyGCStartAt) { + Universe::heap()->prepare_for_verify(); + Universe::verify(); // make sure we're starting with a clean slate + } + + return JNI_OK; +} + + +void exit_globals() { + static bool destructorsCalled = false; + if (!destructorsCalled) { + destructorsCalled = true; + perfMemory_exit(); + if (PrintSafepointStatistics) { + // Print the collected safepoint statistics. + SafepointSynchronize::print_stat_on_exit(); + } + ostream_exit(); + } +} + + +static bool _init_completed = false; + +bool is_init_completed() { + return _init_completed; +} + + +void set_init_completed() { + _init_completed = true; +} diff --git a/hotspot/src/share/vm/runtime/init.hpp b/hotspot/src/share/vm/runtime/init.hpp new file mode 100644 index 00000000000..f76be77651b --- /dev/null +++ b/hotspot/src/share/vm/runtime/init.hpp @@ -0,0 +1,38 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// init_globals replaces C++ global objects so we can use the standard linker +// to link Delta (which is at least twice as fast as using the GNU C++ linker). +// Also, init.c gives explicit control over the sequence of initialization. + +// Programming convention: instead of using a global object (e,g, "Foo foo;"), +// use "Foo* foo;", create a function init_foo() in foo.c, and add a call +// to init_foo in init.cpp. + +jint init_globals(); // call constructors at startup (main Java thread) +void vm_init_globals(); // call constructors at startup (VM thread) +void exit_globals(); // call destructors before exit + +bool is_init_completed(); // returns true when bootstrapping has completed +void set_init_completed(); // set basic init to completed diff --git a/hotspot/src/share/vm/runtime/interfaceSupport.cpp b/hotspot/src/share/vm/runtime/interfaceSupport.cpp new file mode 100644 index 00000000000..9a6267c4efc --- /dev/null +++ b/hotspot/src/share/vm/runtime/interfaceSupport.cpp @@ -0,0 +1,269 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interfaceSupport.cpp.incl" + + +// Implementation of InterfaceSupport + +#ifdef ASSERT + +long InterfaceSupport::_number_of_calls = 0; +long InterfaceSupport::_scavenge_alot_counter = 1; +long InterfaceSupport::_fullgc_alot_counter = 1; +long InterfaceSupport::_fullgc_alot_invocation = 0; + +Histogram* RuntimeHistogram; + +RuntimeHistogramElement::RuntimeHistogramElement(const char* elementName) { + static volatile jint RuntimeHistogram_lock = 0; + _name = elementName; + uintx count = 0; + + while (Atomic::cmpxchg(1, &RuntimeHistogram_lock, 0) != 0) { + while (OrderAccess::load_acquire(&RuntimeHistogram_lock) != 0) { + count +=1; + if ( (WarnOnStalledSpinLock > 0) + && (count % WarnOnStalledSpinLock == 0)) { + warning("RuntimeHistogram_lock seems to be stalled"); + } + } + } + + if (RuntimeHistogram == NULL) { + RuntimeHistogram = new Histogram("VM Runtime Call Counts",200); + } + + RuntimeHistogram->add_element(this); + Atomic::dec(&RuntimeHistogram_lock); +} + +void InterfaceSupport::trace(const char* result_type, const char* header) { + tty->print_cr("%6d %s", _number_of_calls, header); +} + +void InterfaceSupport::gc_alot() { + Thread *thread = Thread::current(); + if (thread->is_VM_thread()) return; // Avoid concurrent calls + // Check for new, not quite initialized thread. A thread in new mode cannot initiate a GC. + JavaThread *current_thread = (JavaThread *)thread; + if (current_thread->active_handles() == NULL) return; + + if (is_init_completed()) { + + if (++_fullgc_alot_invocation < FullGCALotStart) { + return; + } + + // Use this line if you want to block at a specific point, + // e.g. one number_of_calls/scavenge/gc before you got into problems + if (FullGCALot) _fullgc_alot_counter--; + + // Check if we should force a full gc + if (_fullgc_alot_counter == 0) { + // Release dummy so objects are forced to move + if (!Universe::release_fullgc_alot_dummy()) { + warning("FullGCALot: Unable to release more dummies at bottom of heap"); + } + HandleMark hm(thread); + Universe::heap()->collect(GCCause::_full_gc_alot); + unsigned int invocations = Universe::heap()->total_full_collections(); + // Compute new interval + if (FullGCALotInterval > 1) { + _fullgc_alot_counter = 1+(long)((double)FullGCALotInterval*os::random()/(max_jint+1.0)); + if (PrintGCDetails && Verbose) { + tty->print_cr("Full gc no: %u\tInterval: %d", invocations, + _fullgc_alot_counter); + } + } else { + _fullgc_alot_counter = 1; + } + // Print progress message + if (invocations % 100 == 0) { + if (PrintGCDetails && Verbose) tty->print_cr("Full gc no: %u", invocations); + } + } else { + if (ScavengeALot) _scavenge_alot_counter--; + // Check if we should force a scavenge + if (_scavenge_alot_counter == 0) { + HandleMark hm(thread); + Universe::heap()->collect(GCCause::_scavenge_alot); + unsigned int invocations = Universe::heap()->total_collections() - Universe::heap()->total_full_collections(); + // Compute new interval + if (ScavengeALotInterval > 1) { + _scavenge_alot_counter = 1+(long)((double)ScavengeALotInterval*os::random()/(max_jint+1.0)); + if (PrintGCDetails && Verbose) { + tty->print_cr("Scavenge no: %u\tInterval: %d", invocations, + _scavenge_alot_counter); + } + } else { + _scavenge_alot_counter = 1; + } + // Print progress message + if (invocations % 1000 == 0) { + if (PrintGCDetails && Verbose) tty->print_cr("Scavenge no: %u", invocations); + } + } + } + } +} + + +vframe* vframe_array[50]; +int walk_stack_counter = 0; + +void InterfaceSupport::walk_stack_from(vframe* start_vf) { + // walk + int i = 0; + for (vframe* f = start_vf; f; f = f->sender() ) { + if (i < 50) vframe_array[i++] = f; + } +} + + +void InterfaceSupport::walk_stack() { + JavaThread* thread = JavaThread::current(); + walk_stack_counter++; + if (!thread->has_last_Java_frame()) return; + ResourceMark rm(thread); + RegisterMap reg_map(thread); + walk_stack_from(thread->last_java_vframe(®_map)); +} + + +# ifdef ENABLE_ZAP_DEAD_LOCALS + +static int zap_traversals = 0; + +void InterfaceSupport::zap_dead_locals_old() { + JavaThread* thread = JavaThread::current(); + if (zap_traversals == -1) // edit constant for debugging + warning("I am here"); + int zap_frame_count = 0; // count frames to help debugging + for (StackFrameStream sfs(thread); !sfs.is_done(); sfs.next()) { + sfs.current()->zap_dead_locals(thread, sfs.register_map()); + ++zap_frame_count; + } + ++zap_traversals; +} + +# endif + + +int deoptimizeAllCounter = 0; +int zombieAllCounter = 0; + + +void InterfaceSupport::zombieAll() { + if (is_init_completed() && zombieAllCounter > ZombieALotInterval) { + zombieAllCounter = 0; + VM_ZombieAll op; + VMThread::execute(&op); + } else { + zombieAllCounter++; + } +} + +void InterfaceSupport::deoptimizeAll() { + if (is_init_completed() ) { + if (DeoptimizeALot && deoptimizeAllCounter > DeoptimizeALotInterval) { + deoptimizeAllCounter = 0; + VM_DeoptimizeAll op; + VMThread::execute(&op); + } else if (DeoptimizeRandom && (deoptimizeAllCounter & 0x1f) == (os::random() & 0x1f)) { + VM_DeoptimizeAll op; + VMThread::execute(&op); + } + } + deoptimizeAllCounter++; +} + + +void InterfaceSupport::stress_derived_pointers() { +#ifdef COMPILER2 + JavaThread *thread = JavaThread::current(); + if (!is_init_completed()) return; + ResourceMark rm(thread); + bool found = false; + for (StackFrameStream sfs(thread); !sfs.is_done() && !found; sfs.next()) { + CodeBlob* cb = sfs.current()->cb(); + if (cb != NULL && cb->oop_maps() ) { + // Find oopmap for current method + OopMap* map = cb->oop_map_for_return_address(sfs.current()->pc()); + assert(map != NULL, "no oopmap found for pc"); + found = map->has_derived_pointer(); + } + } + if (found) { + // $$$ Not sure what to do here. + /* + Scavenge::invoke(0); + */ + } +#endif +} + + +void InterfaceSupport::verify_stack() { + JavaThread* thread = JavaThread::current(); + ResourceMark rm(thread); + // disabled because it throws warnings that oop maps should only be accessed + // in VM thread or during debugging + + if (!thread->has_pending_exception()) { + // verification does not work if there are pending exceptions + StackFrameStream sfs(thread); + CodeBlob* cb = sfs.current()->cb(); + // In case of exceptions we might not have a runtime_stub on + // top of stack, hence, all callee-saved registers are not going + // to be setup correctly, hence, we cannot do stack verify + if (cb != NULL && !(cb->is_runtime_stub() || cb->is_uncommon_trap_stub())) return; + + for (; !sfs.is_done(); sfs.next()) { + sfs.current()->verify(sfs.register_map()); + } + } +} + + +void InterfaceSupport::verify_last_frame() { + JavaThread* thread = JavaThread::current(); + ResourceMark rm(thread); + RegisterMap reg_map(thread); + frame fr = thread->last_frame(); + fr.verify(®_map); +} + + +#endif // ASSERT + + +void InterfaceSupport_init() { +#ifdef ASSERT + if (ScavengeALot || FullGCALot) { + srand(ScavengeALotInterval * FullGCALotInterval); + } +#endif +} diff --git a/hotspot/src/share/vm/runtime/interfaceSupport.hpp b/hotspot/src/share/vm/runtime/interfaceSupport.hpp new file mode 100644 index 00000000000..0d5db30391e --- /dev/null +++ b/hotspot/src/share/vm/runtime/interfaceSupport.hpp @@ -0,0 +1,568 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Wrapper for all entry points to the virtual machine. +// The HandleMarkCleaner is a faster version of HandleMark. +// It relies on the fact that there is a HandleMark further +// down the stack (in JavaCalls::call_helper), and just resets +// to the saved values in that HandleMark. + +class HandleMarkCleaner: public StackObj { + private: + Thread* _thread; + public: + HandleMarkCleaner(Thread* thread) { + _thread = thread; + _thread->last_handle_mark()->push(); + } + ~HandleMarkCleaner() { + _thread->last_handle_mark()->pop_and_restore(); + } + + private: + inline void* operator new(size_t size, void* ptr) { + return ptr; + } +}; + +// InterfaceSupport provides functionality used by the __LEAF and __ENTRY +// macros. These macros are used to guard entry points into the VM and +// perform checks upon leave of the VM. + + +class InterfaceSupport: AllStatic { +# ifdef ASSERT + public: + static long _scavenge_alot_counter; + static long _fullgc_alot_counter; + static long _number_of_calls; + static long _fullgc_alot_invocation; + + // tracing + static void trace(const char* result_type, const char* header); + + // Helper methods used to implement +ScavengeALot and +FullGCALot + static void check_gc_alot() { if (ScavengeALot || FullGCALot) gc_alot(); } + static void gc_alot(); + + static void walk_stack_from(vframe* start_vf); + static void walk_stack(); + +# ifdef ENABLE_ZAP_DEAD_LOCALS + static void zap_dead_locals_old(); +# endif + + static void zombieAll(); + static void deoptimizeAll(); + static void stress_derived_pointers(); + static void verify_stack(); + static void verify_last_frame(); +# endif + + public: + // OS dependent stuff + #include "incls/_interfaceSupport_pd.hpp.incl" +}; + + +// Basic class for all thread transition classes. + +class ThreadStateTransition : public StackObj { + protected: + JavaThread* _thread; + public: + ThreadStateTransition(JavaThread *thread) { + _thread = thread; + assert(thread != NULL && thread->is_Java_thread(), "must be Java thread"); + } + + // Change threadstate in a manner, so safepoint can detect changes. + // Time-critical: called on exit from every runtime routine + static inline void transition(JavaThread *thread, JavaThreadState from, JavaThreadState to) { + assert(from != _thread_in_Java, "use transition_from_java"); + assert(from != _thread_in_native, "use transition_from_native"); + assert((from & 1) == 0 && (to & 1) == 0, "odd numbers are transitions states"); + assert(thread->thread_state() == from, "coming from wrong thread state"); + // Change to transition state (assumes total store ordering! -Urs) + thread->set_thread_state((JavaThreadState)(from + 1)); + + // Make sure new state is seen by VM thread + if (os::is_MP()) { + if (UseMembar) { + // Force a fence between the write above and read below + OrderAccess::fence(); + } else { + // store to serialize page so VM thread can do pseudo remote membar + os::write_memory_serialize_page(thread); + } + } + + if (SafepointSynchronize::do_call_back()) { + SafepointSynchronize::block(thread); + } + thread->set_thread_state(to); + + CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();) + } + + // transition_and_fence must be used on any thread state transition + // where there might not be a Java call stub on the stack, in + // particular on Windows where the Structured Exception Handler is + // set up in the call stub. os::write_memory_serialize_page() can + // fault and we can't recover from it on Windows without a SEH in + // place. + static inline void transition_and_fence(JavaThread *thread, JavaThreadState from, JavaThreadState to) { + assert(thread->thread_state() == from, "coming from wrong thread state"); + assert((from & 1) == 0 && (to & 1) == 0, "odd numbers are transitions states"); + // Change to transition state (assumes total store ordering! -Urs) + thread->set_thread_state((JavaThreadState)(from + 1)); + + // Make sure new state is seen by VM thread + if (os::is_MP()) { + if (UseMembar) { + // Force a fence between the write above and read below + OrderAccess::fence(); + } else { + // Must use this rather than serialization page in particular on Windows + InterfaceSupport::serialize_memory(thread); + } + } + + if (SafepointSynchronize::do_call_back()) { + SafepointSynchronize::block(thread); + } + thread->set_thread_state(to); + + CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();) + } + + // Same as above, but assumes from = _thread_in_Java. This is simpler, since we + // never block on entry to the VM. This will break the code, since e.g. preserve arguments + // have not been setup. + static inline void transition_from_java(JavaThread *thread, JavaThreadState to) { + assert(thread->thread_state() == _thread_in_Java, "coming from wrong thread state"); + thread->set_thread_state(to); + } + + static inline void transition_from_native(JavaThread *thread, JavaThreadState to) { + assert((to & 1) == 0, "odd numbers are transitions states"); + assert(thread->thread_state() == _thread_in_native, "coming from wrong thread state"); + // Change to transition state (assumes total store ordering! -Urs) + thread->set_thread_state(_thread_in_native_trans); + + // Make sure new state is seen by GC thread + if (os::is_MP()) { + if (UseMembar) { + // Force a fence between the write above and read below + OrderAccess::fence(); + } else { + // Must use this rather than serialization page in particular on Windows + InterfaceSupport::serialize_memory(thread); + } + } + + // We never install asynchronous exceptions when coming (back) in + // to the runtime from native code because the runtime is not set + // up to handle exceptions floating around at arbitrary points. + if (SafepointSynchronize::do_call_back() || thread->is_suspend_after_native()) { + JavaThread::check_safepoint_and_suspend_for_native_trans(thread); + + // Clear unhandled oops anywhere where we could block, even if we don't. + CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();) + } + + thread->set_thread_state(to); + } + protected: + void trans(JavaThreadState from, JavaThreadState to) { transition(_thread, from, to); } + void trans_from_java(JavaThreadState to) { transition_from_java(_thread, to); } + void trans_from_native(JavaThreadState to) { transition_from_native(_thread, to); } + void trans_and_fence(JavaThreadState from, JavaThreadState to) { transition_and_fence(_thread, from, to); } +}; + + +class ThreadInVMfromJava : public ThreadStateTransition { + public: + ThreadInVMfromJava(JavaThread* thread) : ThreadStateTransition(thread) { + trans_from_java(_thread_in_vm); + } + ~ThreadInVMfromJava() { + trans(_thread_in_vm, _thread_in_Java); + // Check for pending. async. exceptions or suspends. + if (_thread->has_special_runtime_exit_condition()) _thread->handle_special_runtime_exit_condition(); + } +}; + + +class ThreadInVMfromUnknown { + private: + JavaThread* _thread; + public: + ThreadInVMfromUnknown() : _thread(NULL) { + Thread* t = Thread::current(); + if (t->is_Java_thread()) { + JavaThread* t2 = (JavaThread*) t; + if (t2->thread_state() == _thread_in_native) { + _thread = t2; + ThreadStateTransition::transition_from_native(t2, _thread_in_vm); + // Used to have a HandleMarkCleaner but that is dangerous as + // it could free a handle in our (indirect, nested) caller. + // We expect any handles will be short lived and figure we + // don't need an actual HandleMark. + } + } + } + ~ThreadInVMfromUnknown() { + if (_thread) { + ThreadStateTransition::transition_and_fence(_thread, _thread_in_vm, _thread_in_native); + } + } +}; + + +class ThreadInVMfromNative : public ThreadStateTransition { + public: + ThreadInVMfromNative(JavaThread* thread) : ThreadStateTransition(thread) { + trans_from_native(_thread_in_vm); + } + ~ThreadInVMfromNative() { + trans_and_fence(_thread_in_vm, _thread_in_native); + } +}; + + +class ThreadToNativeFromVM : public ThreadStateTransition { + public: + ThreadToNativeFromVM(JavaThread *thread) : ThreadStateTransition(thread) { + // We are leaving the VM at this point and going directly to native code. + // Block, if we are in the middle of a safepoint synchronization. + assert(!thread->owns_locks(), "must release all locks when leaving VM"); + thread->frame_anchor()->make_walkable(thread); + trans_and_fence(_thread_in_vm, _thread_in_native); + // Check for pending. async. exceptions or suspends. + if (_thread->has_special_runtime_exit_condition()) _thread->handle_special_runtime_exit_condition(false); + } + + ~ThreadToNativeFromVM() { + trans_from_native(_thread_in_vm); + // We don't need to clear_walkable because it will happen automagically when we return to java + } +}; + + +class ThreadBlockInVM : public ThreadStateTransition { + public: + ThreadBlockInVM(JavaThread *thread) + : ThreadStateTransition(thread) { + // Once we are blocked vm expects stack to be walkable + thread->frame_anchor()->make_walkable(thread); + trans_and_fence(_thread_in_vm, _thread_blocked); + } + ~ThreadBlockInVM() { + trans_and_fence(_thread_blocked, _thread_in_vm); + // We don't need to clear_walkable because it will happen automagically when we return to java + } +}; + + +// This special transition class is only used to prevent asynchronous exceptions +// from being installed on vm exit in situations where we can't tolerate them. +// See bugs: 4324348, 4854693, 4998314, 5040492, 5050705. +class ThreadInVMfromJavaNoAsyncException : public ThreadStateTransition { + public: + ThreadInVMfromJavaNoAsyncException(JavaThread* thread) : ThreadStateTransition(thread) { + trans_from_java(_thread_in_vm); + } + ~ThreadInVMfromJavaNoAsyncException() { + trans(_thread_in_vm, _thread_in_Java); + // NOTE: We do not check for pending. async. exceptions. + // If we did and moved the pending async exception over into the + // pending exception field, we would need to deopt (currently C2 + // only). However, to do so would require that we transition back + // to the _thread_in_vm state. Instead we postpone the handling of + // the async exception. + + // Check for pending. suspends only. + if (_thread->has_special_runtime_exit_condition()) + _thread->handle_special_runtime_exit_condition(false); + } +}; + +// Debug class instantiated in JRT_ENTRY and ITR_ENTRY macro. +// Can be used to verify properties on enter/exit of the VM. + +#ifdef ASSERT +class VMEntryWrapper { + public: + VMEntryWrapper() { + if (VerifyLastFrame) { + InterfaceSupport::verify_last_frame(); + } + } + + ~VMEntryWrapper() { + InterfaceSupport::check_gc_alot(); + if (WalkStackALot) { + InterfaceSupport::walk_stack(); + } +#ifdef ENABLE_ZAP_DEAD_LOCALS + if (ZapDeadLocalsOld) { + InterfaceSupport::zap_dead_locals_old(); + } +#endif +#ifdef COMPILER2 + // This option is not used by Compiler 1 + if (StressDerivedPointers) { + InterfaceSupport::stress_derived_pointers(); + } +#endif + if (DeoptimizeALot || DeoptimizeRandom) { + InterfaceSupport::deoptimizeAll(); + } + if (ZombieALot) { + InterfaceSupport::zombieAll(); + } + // do verification AFTER potential deoptimization + if (VerifyStack) { + InterfaceSupport::verify_stack(); + } + + } +}; + + +class VMNativeEntryWrapper { + public: + VMNativeEntryWrapper() { + if (GCALotAtAllSafepoints) InterfaceSupport::check_gc_alot(); + } + + ~VMNativeEntryWrapper() { + if (GCALotAtAllSafepoints) InterfaceSupport::check_gc_alot(); + } +}; + +#endif + + +// VM-internal runtime interface support + +#ifdef ASSERT + +class RuntimeHistogramElement : public HistogramElement { + public: + RuntimeHistogramElement(const char* name); +}; + +#define TRACE_CALL(result_type, header) \ + InterfaceSupport::_number_of_calls++; \ + if (TraceRuntimeCalls) \ + InterfaceSupport::trace(#result_type, #header); \ + if (CountRuntimeCalls) { \ + static RuntimeHistogramElement* e = new RuntimeHistogramElement(#header); \ + if (e != NULL) e->increment_count(); \ + } +#else +#define TRACE_CALL(result_type, header) \ + /* do nothing */ +#endif + + +// LEAF routines do not lock, GC or throw exceptions + +#define __LEAF(result_type, header) \ + TRACE_CALL(result_type, header) \ + debug_only(NoHandleMark __hm;) \ + /* begin of body */ + + +// ENTRY routines may lock, GC and throw exceptions + +#define __ENTRY(result_type, header, thread) \ + TRACE_CALL(result_type, header) \ + HandleMarkCleaner __hm(thread); \ + Thread* THREAD = thread; \ + /* begin of body */ + + +// QUICK_ENTRY routines behave like ENTRY but without a handle mark + +#define __QUICK_ENTRY(result_type, header, thread) \ + TRACE_CALL(result_type, header) \ + debug_only(NoHandleMark __hm;) \ + Thread* THREAD = thread; \ + /* begin of body */ + + +// Definitions for IRT (Interpreter Runtime) +// (thread is an argument passed in to all these routines) + +#define IRT_ENTRY(result_type, header) \ + result_type header { \ + ThreadInVMfromJava __tiv(thread); \ + __ENTRY(result_type, header, thread) \ + debug_only(VMEntryWrapper __vew;) + + +#define IRT_LEAF(result_type, header) \ + result_type header { \ + __LEAF(result_type, header) \ + debug_only(No_Safepoint_Verifier __nspv(true);) + + +#define IRT_ENTRY_NO_ASYNC(result_type, header) \ + result_type header { \ + ThreadInVMfromJavaNoAsyncException __tiv(thread); \ + __ENTRY(result_type, header, thread) \ + debug_only(VMEntryWrapper __vew;) + +// Another special case for nmethod_entry_point so the nmethod that the +// interpreter is about to branch to doesn't get flushed before as we +// branch to it's interpreter_entry_point. Skip stress testing here too. +// Also we don't allow async exceptions because it is just too painful. +#define IRT_ENTRY_FOR_NMETHOD(result_type, header) \ + result_type header { \ + nmethodLocker _nmlock(nm); \ + ThreadInVMfromJavaNoAsyncException __tiv(thread); \ + __ENTRY(result_type, header, thread) + +#define IRT_END } + + +// Definitions for JRT (Java (Compiler/Shared) Runtime) + +#define JRT_ENTRY(result_type, header) \ + result_type header { \ + ThreadInVMfromJava __tiv(thread); \ + __ENTRY(result_type, header, thread) \ + debug_only(VMEntryWrapper __vew;) + + +#define JRT_LEAF(result_type, header) \ + result_type header { \ + __LEAF(result_type, header) \ + debug_only(JRT_Leaf_Verifier __jlv;) + + +#define JRT_ENTRY_NO_ASYNC(result_type, header) \ + result_type header { \ + ThreadInVMfromJavaNoAsyncException __tiv(thread); \ + __ENTRY(result_type, header, thread) \ + debug_only(VMEntryWrapper __vew;) + +// Same as JRT Entry but allows for return value after the safepoint +// to get back into Java from the VM +#define JRT_BLOCK_ENTRY(result_type, header) \ + result_type header { \ + TRACE_CALL(result_type, header) \ + HandleMarkCleaner __hm(thread); + +#define JRT_BLOCK \ + { \ + ThreadInVMfromJava __tiv(thread); \ + Thread* THREAD = thread; \ + debug_only(VMEntryWrapper __vew;) + +#define JRT_BLOCK_END } + +#define JRT_END } + +// Definitions for JNI + +#define JNI_ENTRY(result_type, header) \ + JNI_ENTRY_NO_PRESERVE(result_type, header) \ + WeakPreserveExceptionMark __wem(thread); + +#define JNI_ENTRY_NO_PRESERVE(result_type, header) \ +extern "C" { \ + result_type JNICALL header { \ + JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ + assert( !VerifyJNIEnvThread || (thread == Thread::current()), "JNIEnv is only valid in same thread"); \ + ThreadInVMfromNative __tiv(thread); \ + debug_only(VMNativeEntryWrapper __vew;) \ + __ENTRY(result_type, header, thread) + + +// Ensure that the VMNativeEntryWrapper constructor, which can cause +// a GC, is called outside the NoHandleMark (set via __QUICK_ENTRY). +#define JNI_QUICK_ENTRY(result_type, header) \ +extern "C" { \ + result_type JNICALL header { \ + JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ + assert( !VerifyJNIEnvThread || (thread == Thread::current()), "JNIEnv is only valid in same thread"); \ + ThreadInVMfromNative __tiv(thread); \ + debug_only(VMNativeEntryWrapper __vew;) \ + __QUICK_ENTRY(result_type, header, thread) + + +#define JNI_LEAF(result_type, header) \ +extern "C" { \ + result_type JNICALL header { \ + JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ + assert( !VerifyJNIEnvThread || (thread == Thread::current()), "JNIEnv is only valid in same thread"); \ + __LEAF(result_type, header) + + +// Close the routine and the extern "C" +#define JNI_END } } + + + +// Definitions for JVM + +#define JVM_ENTRY(result_type, header) \ +extern "C" { \ + result_type JNICALL header { \ + JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ + ThreadInVMfromNative __tiv(thread); \ + debug_only(VMNativeEntryWrapper __vew;) \ + __ENTRY(result_type, header, thread) + + +#define JVM_ENTRY_NO_ENV(result_type, header) \ +extern "C" { \ + result_type JNICALL header { \ + JavaThread* thread = (JavaThread*)ThreadLocalStorage::thread(); \ + ThreadInVMfromNative __tiv(thread); \ + debug_only(VMNativeEntryWrapper __vew;) \ + __ENTRY(result_type, header, thread) + + +#define JVM_QUICK_ENTRY(result_type, header) \ +extern "C" { \ + result_type JNICALL header { \ + JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ + ThreadInVMfromNative __tiv(thread); \ + debug_only(VMNativeEntryWrapper __vew;) \ + __QUICK_ENTRY(result_type, header, thread) + + +#define JVM_LEAF(result_type, header) \ +extern "C" { \ + result_type JNICALL header { \ + VM_Exit::block_if_vm_exited(); \ + __LEAF(result_type, header) + + +#define JVM_END } } diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp new file mode 100644 index 00000000000..f7ed8939e65 --- /dev/null +++ b/hotspot/src/share/vm/runtime/java.cpp @@ -0,0 +1,593 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_java.cpp.incl" + +HS_DTRACE_PROBE_DECL(hotspot, vm__shutdown); + +#ifndef PRODUCT + +// Statistics printing (method invocation histogram) + +GrowableArray* collected_invoked_methods; + +void collect_invoked_methods(methodOop m) { + if (m->invocation_count() + m->compiled_invocation_count() >= 1 ) { + collected_invoked_methods->push(m); + } +} + + +GrowableArray* collected_profiled_methods; + +void collect_profiled_methods(methodOop m) { + methodHandle mh(Thread::current(), m); + if ((m->method_data() != NULL) && + (PrintMethodData || CompilerOracle::should_print(mh))) { + collected_profiled_methods->push(m); + } +} + + +int compare_methods(methodOop* a, methodOop* b) { + // %%% there can be 32-bit overflow here + return ((*b)->invocation_count() + (*b)->compiled_invocation_count()) + - ((*a)->invocation_count() + (*a)->compiled_invocation_count()); +} + + +void print_method_invocation_histogram() { + ResourceMark rm; + HandleMark hm; + collected_invoked_methods = new GrowableArray(1024); + SystemDictionary::methods_do(collect_invoked_methods); + collected_invoked_methods->sort(&compare_methods); + // + tty->cr(); + tty->print_cr("Histogram Over MethodOop Invocation Counters (cutoff = %d):", MethodHistogramCutoff); + tty->cr(); + tty->print_cr("____Count_(I+C)____Method________________________Module_________________"); + unsigned total = 0, int_total = 0, comp_total = 0, static_total = 0, final_total = 0, + synch_total = 0, nativ_total = 0, acces_total = 0; + for (int index = 0; index < collected_invoked_methods->length(); index++) { + methodOop m = collected_invoked_methods->at(index); + int c = m->invocation_count() + m->compiled_invocation_count(); + if (c >= MethodHistogramCutoff) m->print_invocation_count(); + int_total += m->invocation_count(); + comp_total += m->compiled_invocation_count(); + if (m->is_final()) final_total += c; + if (m->is_static()) static_total += c; + if (m->is_synchronized()) synch_total += c; + if (m->is_native()) nativ_total += c; + if (m->is_accessor()) acces_total += c; + } + tty->cr(); + total = int_total + comp_total; + tty->print_cr("Invocations summary:"); + tty->print_cr("\t%9d (%4.1f%%) interpreted", int_total, 100.0 * int_total / total); + tty->print_cr("\t%9d (%4.1f%%) compiled", comp_total, 100.0 * comp_total / total); + tty->print_cr("\t%9d (100%%) total", total); + tty->print_cr("\t%9d (%4.1f%%) synchronized", synch_total, 100.0 * synch_total / total); + tty->print_cr("\t%9d (%4.1f%%) final", final_total, 100.0 * final_total / total); + tty->print_cr("\t%9d (%4.1f%%) static", static_total, 100.0 * static_total / total); + tty->print_cr("\t%9d (%4.1f%%) native", nativ_total, 100.0 * nativ_total / total); + tty->print_cr("\t%9d (%4.1f%%) accessor", acces_total, 100.0 * acces_total / total); + tty->cr(); + SharedRuntime::print_call_statistics(comp_total); +} + +void print_method_profiling_data() { + ResourceMark rm; + HandleMark hm; + collected_profiled_methods = new GrowableArray(1024); + SystemDictionary::methods_do(collect_profiled_methods); + collected_profiled_methods->sort(&compare_methods); + + int count = collected_profiled_methods->length(); + if (count > 0) { + for (int index = 0; index < count; index++) { + methodOop m = collected_profiled_methods->at(index); + ttyLocker ttyl; + tty->print_cr("------------------------------------------------------------------------"); + //m->print_name(tty); + m->print_invocation_count(); + tty->cr(); + m->print_codes(); + } + tty->print_cr("------------------------------------------------------------------------"); + } +} + +void print_bytecode_count() { + if (CountBytecodes || TraceBytecodes || StopInterpreterAt) { + tty->print_cr("[BytecodeCounter::counter_value = %d]", BytecodeCounter::counter_value()); + } +} + +AllocStats alloc_stats; + + + +// General statistics printing (profiling ...) + +void print_statistics() { + +#ifdef ASSERT + + if (CountRuntimeCalls) { + extern Histogram *RuntimeHistogram; + RuntimeHistogram->print(); + } + + if (CountJNICalls) { + extern Histogram *JNIHistogram; + JNIHistogram->print(); + } + + if (CountJVMCalls) { + extern Histogram *JVMHistogram; + JVMHistogram->print(); + } + +#endif + + if (MemProfiling) { + MemProfiler::disengage(); + } + + if (CITime) { + CompileBroker::print_times(); + } + +#ifdef COMPILER1 + if ((PrintC1Statistics || LogVMOutput || LogCompilation) && UseCompiler) { + FlagSetting fs(DisplayVMOutput, DisplayVMOutput && PrintC1Statistics); + Runtime1::print_statistics(); + Deoptimization::print_statistics(); + nmethod::print_statistics(); + } +#endif /* COMPILER1 */ + +#ifdef COMPILER2 + if ((PrintOptoStatistics || LogVMOutput || LogCompilation) && UseCompiler) { + FlagSetting fs(DisplayVMOutput, DisplayVMOutput && PrintOptoStatistics); + Compile::print_statistics(); +#ifndef COMPILER1 + Deoptimization::print_statistics(); + nmethod::print_statistics(); +#endif //COMPILER1 + SharedRuntime::print_statistics(); + os::print_statistics(); + } + + if (PrintLockStatistics || PrintPreciseBiasedLockingStatistics) { + OptoRuntime::print_named_counters(); + } + + if (TimeLivenessAnalysis) { + MethodLiveness::print_times(); + } +#ifdef ASSERT + if (CollectIndexSetStatistics) { + IndexSet::print_statistics(); + } +#endif // ASSERT +#endif // COMPILER2 + if (CountCompiledCalls) { + print_method_invocation_histogram(); + } + if (ProfileInterpreter || Tier1UpdateMethodData) { + print_method_profiling_data(); + } + if (TimeCompiler) { + COMPILER2_PRESENT(Compile::print_timers();) + } + if (TimeCompilationPolicy) { + CompilationPolicy::policy()->print_time(); + } + if (TimeOopMap) { + GenerateOopMap::print_time(); + } + if (ProfilerCheckIntervals) { + PeriodicTask::print_intervals(); + } + if (PrintSymbolTableSizeHistogram) { + SymbolTable::print_histogram(); + } + if (CountBytecodes || TraceBytecodes || StopInterpreterAt) { + BytecodeCounter::print(); + } + if (PrintBytecodePairHistogram) { + BytecodePairHistogram::print(); + } + + if (PrintCodeCache) { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + CodeCache::print(); + } + + if (PrintCodeCache2) { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + CodeCache::print_internals(); + } + + if (PrintClassStatistics) { + SystemDictionary::print_class_statistics(); + } + if (PrintMethodStatistics) { + SystemDictionary::print_method_statistics(); + } + + if (PrintVtableStats) { + klassVtable::print_statistics(); + klassItable::print_statistics(); + } + if (VerifyOops) { + tty->print_cr("+VerifyOops count: %d", StubRoutines::verify_oop_count()); + } + + print_bytecode_count(); + if (WizardMode) { + tty->print("allocation stats: "); + alloc_stats.print(); + tty->cr(); + } + + if (PrintSystemDictionaryAtExit) { + SystemDictionary::print(); + } + + if (PrintBiasedLockingStatistics) { + BiasedLocking::print_counters(); + } + +#ifdef ENABLE_ZAP_DEAD_LOCALS +#ifdef COMPILER2 + if (ZapDeadCompiledLocals) { + tty->print_cr("Compile::CompiledZap_count = %d", Compile::CompiledZap_count); + tty->print_cr("OptoRuntime::ZapDeadCompiledLocals_count = %d", OptoRuntime::ZapDeadCompiledLocals_count); + } +#endif // COMPILER2 +#endif // ENABLE_ZAP_DEAD_LOCALS +} + +#else // PRODUCT MODE STATISTICS + +void print_statistics() { + + if (CITime) { + CompileBroker::print_times(); + } +#ifdef COMPILER2 + if (PrintPreciseBiasedLockingStatistics) { + OptoRuntime::print_named_counters(); + } +#endif + if (PrintBiasedLockingStatistics) { + BiasedLocking::print_counters(); + } +} + +#endif + + +// Helper class for registering on_exit calls through JVM_OnExit + +extern "C" { + typedef void (*__exit_proc)(void); +} + +class ExitProc : public CHeapObj { + private: + __exit_proc _proc; + // void (*_proc)(void); + ExitProc* _next; + public: + // ExitProc(void (*proc)(void)) { + ExitProc(__exit_proc proc) { + _proc = proc; + _next = NULL; + } + void evaluate() { _proc(); } + ExitProc* next() const { return _next; } + void set_next(ExitProc* next) { _next = next; } +}; + + +// Linked list of registered on_exit procedures + +static ExitProc* exit_procs = NULL; + + +extern "C" { + void register_on_exit_function(void (*func)(void)) { + ExitProc *entry = new ExitProc(func); + // Classic vm does not throw an exception in case the allocation failed, + if (entry != NULL) { + entry->set_next(exit_procs); + exit_procs = entry; + } + } +} + +// Note: before_exit() can be executed only once, if more than one threads +// are trying to shutdown the VM at the same time, only one thread +// can run before_exit() and all other threads must wait. +void before_exit(JavaThread * thread) { + #define BEFORE_EXIT_NOT_RUN 0 + #define BEFORE_EXIT_RUNNING 1 + #define BEFORE_EXIT_DONE 2 + static jint volatile _before_exit_status = BEFORE_EXIT_NOT_RUN; + + // Note: don't use a Mutex to guard the entire before_exit(), as + // JVMTI post_thread_end_event and post_vm_death_event will run native code. + // A CAS or OSMutex would work just fine but then we need to manipulate + // thread state for Safepoint. Here we use Monitor wait() and notify_all() + // for synchronization. + { MutexLocker ml(BeforeExit_lock); + switch (_before_exit_status) { + case BEFORE_EXIT_NOT_RUN: + _before_exit_status = BEFORE_EXIT_RUNNING; + break; + case BEFORE_EXIT_RUNNING: + while (_before_exit_status == BEFORE_EXIT_RUNNING) { + BeforeExit_lock->wait(); + } + assert(_before_exit_status == BEFORE_EXIT_DONE, "invalid state"); + return; + case BEFORE_EXIT_DONE: + return; + } + } + + // The only difference between this and Win32's _onexit procs is that + // this version is invoked before any threads get killed. + ExitProc* current = exit_procs; + while (current != NULL) { + ExitProc* next = current->next(); + current->evaluate(); + delete current; + current = next; + } + + // Hang forever on exit if we're reporting an error. + if (ShowMessageBoxOnError && is_error_reported()) { + os::infinite_sleep(); + } + + // Terminate watcher thread - must before disenrolling any periodic task + WatcherThread::stop(); + + // Print statistics gathered (profiling ...) + if (Arguments::has_profile()) { + FlatProfiler::disengage(); + FlatProfiler::print(10); + } + + // shut down the StatSampler task + StatSampler::disengage(); + StatSampler::destroy(); + + // shut down the TimeMillisUpdateTask + if (CacheTimeMillis) { + TimeMillisUpdateTask::disengage(); + } + +#ifndef SERIALGC + // stop CMS threads + if (UseConcMarkSweepGC) { + ConcurrentMarkSweepThread::stop(); + } +#endif // SERIALGC + + // Print GC/heap related information. + if (PrintGCDetails) { + Universe::print(); + AdaptiveSizePolicyOutput(0); + } + + + if (Arguments::has_alloc_profile()) { + HandleMark hm; + // Do one last collection to enumerate all the objects + // allocated since the last one. + Universe::heap()->collect(GCCause::_allocation_profiler); + AllocationProfiler::disengage(); + AllocationProfiler::print(0); + } + + if (PrintBytecodeHistogram) { + BytecodeHistogram::print(); + } + + if (JvmtiExport::should_post_thread_life()) { + JvmtiExport::post_thread_end(thread); + } + // Always call even when there are not JVMTI environments yet, since environments + // may be attached late and JVMTI must track phases of VM execution + JvmtiExport::post_vm_death(); + Threads::shutdown_vm_agents(); + + // Terminate the signal thread + // Note: we don't wait until it actually dies. + os::terminate_signal_thread(); + + print_statistics(); + Universe::heap()->print_tracing_info(); + + VTune::exit(); + + { MutexLocker ml(BeforeExit_lock); + _before_exit_status = BEFORE_EXIT_DONE; + BeforeExit_lock->notify_all(); + } + + #undef BEFORE_EXIT_NOT_RUN + #undef BEFORE_EXIT_RUNNING + #undef BEFORE_EXIT_DONE +} + +void vm_exit(int code) { + Thread* thread = ThreadLocalStorage::thread_index() == -1 ? NULL + : ThreadLocalStorage::get_thread_slow(); + if (thread == NULL) { + // we have serious problems -- just exit + vm_direct_exit(code); + } + + if (VMThread::vm_thread() != NULL) { + // Fire off a VM_Exit operation to bring VM to a safepoint and exit + VM_Exit op(code); + if (thread->is_Java_thread()) + ((JavaThread*)thread)->set_thread_state(_thread_in_vm); + VMThread::execute(&op); + // should never reach here; but in case something wrong with VM Thread. + vm_direct_exit(code); + } else { + // VM thread is gone, just exit + vm_direct_exit(code); + } + ShouldNotReachHere(); +} + +void notify_vm_shutdown() { + // For now, just a dtrace probe. + HS_DTRACE_PROBE(hotspot, vm__shutdown); +} + +void vm_direct_exit(int code) { + notify_vm_shutdown(); + ::exit(code); +} + +void vm_perform_shutdown_actions() { + // Warning: do not call 'exit_globals()' here. All threads are still running. + // Calling 'exit_globals()' will disable thread-local-storage and cause all + // kinds of assertions to trigger in debug mode. + if (is_init_completed()) { + Thread* thread = Thread::current(); + if (thread->is_Java_thread()) { + // We are leaving the VM, set state to native (in case any OS exit + // handlers call back to the VM) + JavaThread* jt = (JavaThread*)thread; + // Must always be walkable or have no last_Java_frame when in + // thread_in_native + jt->frame_anchor()->make_walkable(jt); + jt->set_thread_state(_thread_in_native); + } + } + notify_vm_shutdown(); +} + +void vm_shutdown() +{ + vm_perform_shutdown_actions(); + os::shutdown(); +} + +void vm_abort() { + vm_perform_shutdown_actions(); + os::abort(PRODUCT_ONLY(false)); + ShouldNotReachHere(); +} + +void vm_notify_during_shutdown(const char* error, const char* message) { + if (error != NULL) { + tty->print_cr("Error occurred during initialization of VM"); + tty->print("%s", error); + if (message != NULL) { + tty->print_cr(": %s", message); + } + else { + tty->cr(); + } + } + if (ShowMessageBoxOnError && WizardMode) { + fatal("Error occurred during initialization of VM"); + } +} + +void vm_exit_during_initialization(Handle exception) { + tty->print_cr("Error occurred during initialization of VM"); + // If there are exceptions on this thread it must be cleared + // first and here. Any future calls to EXCEPTION_MARK requires + // that no pending exceptions exist. + Thread *THREAD = Thread::current(); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + } + java_lang_Throwable::print(exception, tty); + tty->cr(); + java_lang_Throwable::print_stack_trace(exception(), tty); + tty->cr(); + vm_notify_during_shutdown(NULL, NULL); + vm_abort(); +} + +void vm_exit_during_initialization(symbolHandle ex, const char* message) { + ResourceMark rm; + vm_notify_during_shutdown(ex->as_C_string(), message); + vm_abort(); +} + +void vm_exit_during_initialization(const char* error, const char* message) { + vm_notify_during_shutdown(error, message); + vm_abort(); +} + +void vm_shutdown_during_initialization(const char* error, const char* message) { + vm_notify_during_shutdown(error, message); + vm_shutdown(); +} + +jdk_version_info JDK_Version::_version_info = {0}; +bool JDK_Version::_pre_jdk16_version = false; +int JDK_Version::_jdk_version = 0; + +void JDK_Version::initialize() { + void *lib_handle = os::native_java_library(); + jdk_version_info_fn_t func = + CAST_TO_FN_PTR(jdk_version_info_fn_t, hpi::dll_lookup(lib_handle, "JDK_GetVersionInfo0")); + + if (func == NULL) { + // JDK older than 1.6 + _pre_jdk16_version = true; + return; + } + + if (func != NULL) { + (*func)(&_version_info, sizeof(_version_info)); + } + if (jdk_major_version() == 1) { + _jdk_version = jdk_minor_version(); + } else { + // If the release version string is changed to n.x.x (e.g. 7.0.0) in a future release + _jdk_version = jdk_major_version(); + } +} + +void JDK_Version_init() { + JDK_Version::initialize(); +} diff --git a/hotspot/src/share/vm/runtime/java.hpp b/hotspot/src/share/vm/runtime/java.hpp new file mode 100644 index 00000000000..e3ce6d41997 --- /dev/null +++ b/hotspot/src/share/vm/runtime/java.hpp @@ -0,0 +1,123 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Register function to be called by before_exit +extern "C" { void register_on_exit_function(void (*func)(void)) ;} + +// Execute code before all handles are released and thread is killed; prologue to vm_exit +extern void before_exit(JavaThread * thread); + +// Forced VM exit (i.e, internal error or JVM_Exit) +extern void vm_exit(int code); + +// Wrapper for ::exit() +extern void vm_direct_exit(int code); + +// Shutdown the VM but do not exit the process +extern void vm_shutdown(); +// Shutdown the VM and abort the process +extern void vm_abort(); + +// Trigger any necessary notification of the VM being shutdown +extern void notify_vm_shutdown(); + +// VM exit if error occurs during initialization of VM +extern void vm_exit_during_initialization(Handle exception); +extern void vm_exit_during_initialization(symbolHandle exception_name, const char* message); +extern void vm_exit_during_initialization(const char* error, const char* message = NULL); +extern void vm_shutdown_during_initialization(const char* error, const char* message = NULL); + +class JDK_Version : AllStatic { + friend class VMStructs; + private: + static jdk_version_info _version_info; + static bool _pre_jdk16_version; + static int _jdk_version; // JDK version number representing the release + // i.e. n in 1.n.x (= jdk_minor_version()) + + public: + static void initialize(); + static int jdk_major_version() { return JDK_VERSION_MAJOR(_version_info.jdk_version); } + static int jdk_minor_version() { return JDK_VERSION_MINOR(_version_info.jdk_version); } + static int jdk_micro_version() { return JDK_VERSION_MICRO(_version_info.jdk_version); } + static int jdk_build_number() { return JDK_VERSION_BUILD(_version_info.jdk_version); } + + static bool is_pre_jdk16_version() { return _pre_jdk16_version; } + static bool is_jdk12x_version() { assert(is_jdk_version_initialized(), "must have been initialized"); return _jdk_version == 2; } + static bool is_jdk13x_version() { assert(is_jdk_version_initialized(), "must have been initialized"); return _jdk_version == 3; } + static bool is_jdk14x_version() { assert(is_jdk_version_initialized(), "must have been initialized"); return _jdk_version == 4; } + static bool is_jdk15x_version() { assert(is_jdk_version_initialized(), "must have been initialized"); return _jdk_version == 5; } + static bool is_jdk16x_version() { assert(is_jdk_version_initialized(), "must have been initialized"); return _jdk_version == 6; } + static bool is_jdk17x_version() { assert(is_jdk_version_initialized(), "must have been initialized"); return _jdk_version == 7; } + + static bool supports_thread_park_blocker() { return _version_info.thread_park_blocker; } + + static bool is_gte_jdk14x_version() { + // Keep the semantics of this that the version number is >= 1.4 + assert(is_jdk_version_initialized(), "Not initialized"); + return _jdk_version >= 4; + } + static bool is_gte_jdk15x_version() { + // Keep the semantics of this that the version number is >= 1.5 + assert(is_jdk_version_initialized(), "Not initialized"); + return _jdk_version >= 5; + } + static bool is_gte_jdk16x_version() { + // Keep the semantics of this that the version number is >= 1.6 + assert(is_jdk_version_initialized(), "Not initialized"); + return _jdk_version >= 6; + } + + static bool is_gte_jdk17x_version() { + // Keep the semantics of this that the version number is >= 1.7 + assert(is_jdk_version_initialized(), "Not initialized"); + return _jdk_version >= 7; + } + + static bool is_jdk_version_initialized() { + return _jdk_version > 0; + } + + // These methods are defined to deal with pre JDK 1.6 versions + static void set_jdk12x_version() { + assert(_pre_jdk16_version && !is_jdk_version_initialized(), "must not initialize"); + _jdk_version = 2; + _version_info.jdk_version = (1 << 24) | (2 << 16); + } + static void set_jdk13x_version() { + assert(_pre_jdk16_version && !is_jdk_version_initialized(), "must not initialize"); + _jdk_version = 3; + _version_info.jdk_version = (1 << 24) | (3 << 16); + } + static void set_jdk14x_version() { + assert(_pre_jdk16_version && !is_jdk_version_initialized(), "must not initialize"); + _jdk_version = 4; + _version_info.jdk_version = (1 << 24) | (4 << 16); + } + static void set_jdk15x_version() { + assert(_pre_jdk16_version && !is_jdk_version_initialized(), "must not initialize"); + _jdk_version = 5; + _version_info.jdk_version = (1 << 24) | (5 << 16); + } +}; diff --git a/hotspot/src/share/vm/runtime/javaCalls.cpp b/hotspot/src/share/vm/runtime/javaCalls.cpp new file mode 100644 index 00000000000..444bc7f77c7 --- /dev/null +++ b/hotspot/src/share/vm/runtime/javaCalls.cpp @@ -0,0 +1,524 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_javaCalls.cpp.incl" + +// ----------------------------------------------------- +// Implementation of JavaCallWrapper + +JavaCallWrapper::JavaCallWrapper(methodHandle callee_method, Handle receiver, JavaValue* result, TRAPS) { + JavaThread* thread = (JavaThread *)THREAD; + bool clear_pending_exception = true; + + guarantee(thread->is_Java_thread(), "crucial check - the VM thread cannot and must not escape to Java code"); + assert(!thread->owns_locks(), "must release all locks when leaving VM"); + guarantee(!thread->is_Compiler_thread(), "cannot make java calls from the compiler"); + _result = result; + + // Make sure that that the value of the higest_lock is at least the same as the current stackpointer, + // since, the Java code is highly likely to use locks. + // Use '(address)this' to guarantee that highest_lock address is conservative and inside our thread + thread->update_highest_lock((address)this); + + // Allocate handle block for Java code. This must be done before we change thread_state to _thread_in_Java_or_stub, + // since it can potentially block. + JNIHandleBlock* new_handles = JNIHandleBlock::allocate_block(thread); + + // After this, we are official in JavaCode. This needs to be done before we change any of the thread local + // info, since we cannot find oops before the new information is set up completely. + ThreadStateTransition::transition(thread, _thread_in_vm, _thread_in_Java); + + // Make sure that we handle asynchronous stops and suspends _before_ we clear all thread state + // in JavaCallWrapper::JavaCallWrapper(). This way, we can decide if we need to do any pd actions + // to prepare for stop/suspend (flush register windows on sparcs, cache sp, or other state). + if (thread->has_special_runtime_exit_condition()) { + thread->handle_special_runtime_exit_condition(); + if (HAS_PENDING_EXCEPTION) { + clear_pending_exception = false; + } + } + + + // Make sure to set the oop's after the thread transition - since we can block there. No one is GC'ing + // the JavaCallWrapper before the entry frame is on the stack. + _callee_method = callee_method(); + _receiver = receiver(); + +#ifdef CHECK_UNHANDLED_OOPS + THREAD->allow_unhandled_oop(&_callee_method); + THREAD->allow_unhandled_oop(&_receiver); +#endif // CHECK_UNHANDLED_OOPS + + _thread = (JavaThread *)thread; + _handles = _thread->active_handles(); // save previous handle block & Java frame linkage + + // For the profiler, the last_Java_frame information in thread must always be in + // legal state. We have no last Java frame if last_Java_sp == NULL so + // the valid transition is to clear _last_Java_sp and then reset the rest of + // the (platform specific) state. + + _anchor.copy(_thread->frame_anchor()); + _thread->frame_anchor()->clear(); + + debug_only(_thread->inc_java_call_counter()); + _thread->set_active_handles(new_handles); // install new handle block and reset Java frame linkage + + assert (_thread->thread_state() != _thread_in_native, "cannot set native pc to NULL"); + + // clear any pending exception in thread (native calls start with no exception pending) + if(clear_pending_exception) { + _thread->clear_pending_exception(); + } + + if (_anchor.last_Java_sp() == NULL) { + _thread->record_base_of_stack_pointer(); + } +} + + +JavaCallWrapper::~JavaCallWrapper() { + assert(_thread == JavaThread::current(), "must still be the same thread"); + + // restore previous handle block & Java frame linkage + JNIHandleBlock *_old_handles = _thread->active_handles(); + _thread->set_active_handles(_handles); + + _thread->frame_anchor()->zap(); + + debug_only(_thread->dec_java_call_counter()); + + if (_anchor.last_Java_sp() == NULL) { + _thread->set_base_of_stack_pointer(NULL); + } + + + // Old thread-local info. has been restored. We are not back in the VM. + ThreadStateTransition::transition_from_java(_thread, _thread_in_vm); + + // State has been restored now make the anchor frame visible for the profiler. + // Do this after the transition because this allows us to put an assert + // the Java->vm transition which checks to see that stack is not walkable + // on sparc/ia64 which will catch violations of the reseting of last_Java_frame + // invariants (i.e. _flags always cleared on return to Java) + + _thread->frame_anchor()->copy(&_anchor); + + // Release handles after we are marked as being inside the VM again, since this + // operation might block + JNIHandleBlock::release_block(_old_handles, _thread); +} + + +void JavaCallWrapper::oops_do(OopClosure* f) { + f->do_oop((oop*)&_callee_method); + f->do_oop((oop*)&_receiver); + handles()->oops_do(f); +} + + +// Helper methods +static BasicType runtime_type_from(JavaValue* result) { + switch (result->get_type()) { + case T_BOOLEAN: // fall through + case T_CHAR : // fall through + case T_SHORT : // fall through + case T_INT : // fall through +#ifndef _LP64 + case T_OBJECT : // fall through + case T_ARRAY : // fall through +#endif + case T_BYTE : // fall through + case T_VOID : return T_INT; + case T_LONG : return T_LONG; + case T_FLOAT : return T_FLOAT; + case T_DOUBLE : return T_DOUBLE; +#ifdef _LP64 + case T_ARRAY : // fall through + case T_OBJECT: return T_OBJECT; +#endif + } + ShouldNotReachHere(); + return T_ILLEGAL; +} + +// ===== object constructor calls ===== + +void JavaCalls::call_default_constructor(JavaThread* thread, methodHandle method, Handle receiver, TRAPS) { + assert(method->name() == vmSymbols::object_initializer_name(), "Should only be called for default constructor"); + assert(method->signature() == vmSymbols::void_method_signature(), "Should only be called for default constructor"); + + instanceKlass* ik = instanceKlass::cast(method->method_holder()); + if (ik->is_initialized() && ik->has_vanilla_constructor()) { + // safe to skip constructor call + } else { + static JavaValue result(T_VOID); + JavaCallArguments args(receiver); + call(&result, method, &args, CHECK); + } +} + +// ============ Virtual calls ============ + +void JavaCalls::call_virtual(JavaValue* result, KlassHandle spec_klass, symbolHandle name, symbolHandle signature, JavaCallArguments* args, TRAPS) { + CallInfo callinfo; + Handle receiver = args->receiver(); + KlassHandle recvrKlass(THREAD, receiver.is_null() ? (klassOop)NULL : receiver->klass()); + LinkResolver::resolve_virtual_call( + callinfo, receiver, recvrKlass, spec_klass, name, signature, + KlassHandle(), false, true, CHECK); + methodHandle method = callinfo.selected_method(); + assert(method.not_null(), "should have thrown exception"); + + // Invoke the method + JavaCalls::call(result, method, args, CHECK); +} + + +void JavaCalls::call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, symbolHandle name, symbolHandle signature, TRAPS) { + JavaCallArguments args(receiver); // One oop argument + call_virtual(result, spec_klass, name, signature, &args, CHECK); +} + + +void JavaCalls::call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, symbolHandle name, symbolHandle signature, Handle arg1, TRAPS) { + JavaCallArguments args(receiver); // One oop argument + args.push_oop(arg1); + call_virtual(result, spec_klass, name, signature, &args, CHECK); +} + + + +void JavaCalls::call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, symbolHandle name, symbolHandle signature, Handle arg1, Handle arg2, TRAPS) { + JavaCallArguments args(receiver); // One oop argument + args.push_oop(arg1); + args.push_oop(arg2); + call_virtual(result, spec_klass, name, signature, &args, CHECK); +} + + +// ============ Special calls ============ + +void JavaCalls::call_special(JavaValue* result, KlassHandle klass, symbolHandle name, symbolHandle signature, JavaCallArguments* args, TRAPS) { + CallInfo callinfo; + LinkResolver::resolve_special_call(callinfo, klass, name, signature, KlassHandle(), false, CHECK); + methodHandle method = callinfo.selected_method(); + assert(method.not_null(), "should have thrown exception"); + + // Invoke the method + JavaCalls::call(result, method, args, CHECK); +} + + +void JavaCalls::call_special(JavaValue* result, Handle receiver, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS) { + JavaCallArguments args(receiver); // One oop argument + call_special(result, klass, name, signature, &args, CHECK); +} + + +void JavaCalls::call_special(JavaValue* result, Handle receiver, KlassHandle klass, symbolHandle name, symbolHandle signature, Handle arg1, TRAPS) { + JavaCallArguments args(receiver); // One oop argument + args.push_oop(arg1); + call_special(result, klass, name, signature, &args, CHECK); +} + + +void JavaCalls::call_special(JavaValue* result, Handle receiver, KlassHandle klass, symbolHandle name, symbolHandle signature, Handle arg1, Handle arg2, TRAPS) { + JavaCallArguments args(receiver); // One oop argument + args.push_oop(arg1); + args.push_oop(arg2); + call_special(result, klass, name, signature, &args, CHECK); +} + + +// ============ Static calls ============ + +void JavaCalls::call_static(JavaValue* result, KlassHandle klass, symbolHandle name, symbolHandle signature, JavaCallArguments* args, TRAPS) { + CallInfo callinfo; + LinkResolver::resolve_static_call(callinfo, klass, name, signature, KlassHandle(), false, true, CHECK); + methodHandle method = callinfo.selected_method(); + assert(method.not_null(), "should have thrown exception"); + + // Invoke the method + JavaCalls::call(result, method, args, CHECK); +} + + +void JavaCalls::call_static(JavaValue* result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS) { + JavaCallArguments args; // No argument + call_static(result, klass, name, signature, &args, CHECK); +} + + +void JavaCalls::call_static(JavaValue* result, KlassHandle klass, symbolHandle name, symbolHandle signature, Handle arg1, TRAPS) { + JavaCallArguments args(arg1); // One oop argument + call_static(result, klass, name, signature, &args, CHECK); +} + + +void JavaCalls::call_static(JavaValue* result, KlassHandle klass, symbolHandle name, symbolHandle signature, Handle arg1, Handle arg2, TRAPS) { + JavaCallArguments args; // One oop argument + args.push_oop(arg1); + args.push_oop(arg2); + call_static(result, klass, name, signature, &args, CHECK); +} + + +// ------------------------------------------------- +// Implementation of JavaCalls (low level) + + +void JavaCalls::call(JavaValue* result, methodHandle method, JavaCallArguments* args, TRAPS) { + // Check if we need to wrap a potential OS exception handler around thread + // This is used for e.g. Win32 structured exception handlers + assert(THREAD->is_Java_thread(), "only JavaThreads can make JavaCalls"); + // Need to wrap each and everytime, since there might be native code down the + // stack that has installed its own exception handlers + os::os_exception_wrapper(call_helper, result, &method, args, THREAD); +} + +void JavaCalls::call_helper(JavaValue* result, methodHandle* m, JavaCallArguments* args, TRAPS) { + methodHandle method = *m; + JavaThread* thread = (JavaThread*)THREAD; + assert(thread->is_Java_thread(), "must be called by a java thread"); + assert(method.not_null(), "must have a method to call"); + assert(!SafepointSynchronize::is_at_safepoint(), "call to Java code during VM operation"); + assert(!thread->handle_area()->no_handle_mark_active(), "cannot call out to Java here"); + + + CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();) + + // Make sure that the arguments have the right type + debug_only(args->verify(method, result->get_type(), thread)); + + // Ignore call if method is empty + if (method->is_empty_method()) { + assert(result->get_type() == T_VOID, "an empty method must return a void value"); + return; + } + + +#ifdef ASSERT + { klassOop holder = method->method_holder(); + // A klass might not be initialized since JavaCall's might be used during the executing of + // the . For example, a Thread.start might start executing on an object that is + // not fully initialized! (bad Java programming style) + assert(instanceKlass::cast(holder)->is_linked(), "rewritting must have taken place"); + } +#endif + + + assert(!thread->is_Compiler_thread(), "cannot compile from the compiler"); + if (CompilationPolicy::mustBeCompiled(method)) { + CompileBroker::compile_method(method, InvocationEntryBci, + methodHandle(), 0, "mustBeCompiled", CHECK); + } + + // Since the call stub sets up like the interpreter we call the from_interpreted_entry + // so we can go compiled via a i2c. Otherwise initial entry method will always + // run interpreted. + address entry_point = method->from_interpreted_entry(); + if (JvmtiExport::can_post_interpreter_events() && thread->is_interp_only_mode()) { + entry_point = method->interpreter_entry(); + } + + // Figure out if the result value is an oop or not (Note: This is a different value + // than result_type. result_type will be T_INT of oops. (it is about size) + BasicType result_type = runtime_type_from(result); + bool oop_result_flag = (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY); + + // NOTE: if we move the computation of the result_val_address inside + // the call to call_stub, the optimizer produces wrong code. + intptr_t* result_val_address = (intptr_t*)(result->get_value_addr()); + + // Find receiver + Handle receiver = (!method->is_static()) ? args->receiver() : Handle(); + + // When we reenter Java, we need to reenable the yellow zone which + // might already be disabled when we are in VM. + if (thread->stack_yellow_zone_disabled()) { + thread->reguard_stack(); + } + + // Check that there are shadow pages available before changing thread state + // to Java + if (!os::stack_shadow_pages_available(THREAD, method)) { + // Throw stack overflow exception with preinitialized exception. + Exceptions::throw_stack_overflow_exception(THREAD, __FILE__, __LINE__); + return; + } else { + // Touch pages checked if the OS needs them to be touched to be mapped. + os::bang_stack_shadow_pages(); + } + + // do call + { JavaCallWrapper link(method, receiver, result, CHECK); + { HandleMark hm(thread); // HandleMark used by HandleMarkCleaner + + StubRoutines::call_stub()( + (address)&link, + // (intptr_t*)&(result->_value), // see NOTE above (compiler problem) + result_val_address, // see NOTE above (compiler problem) + result_type, + method(), + entry_point, + args->parameters(), + args->size_of_parameters(), + CHECK + ); + + result = link.result(); // circumvent MS C++ 5.0 compiler bug (result is clobbered across call) + // Preserve oop return value across possible gc points + if (oop_result_flag) { + thread->set_vm_result((oop) result->get_jobject()); + } + } + } // Exit JavaCallWrapper (can block - potential return oop must be preserved) + + // Check if a thread stop or suspend should be executed + // The following assert was not realistic. Thread.stop can set that bit at any moment. + //assert(!thread->has_special_runtime_exit_condition(), "no async. exceptions should be installed"); + + // Restore possible oop return + if (oop_result_flag) { + result->set_jobject((jobject)thread->vm_result()); + thread->set_vm_result(NULL); + } +} + + +//-------------------------------------------------------------------------------------- +// Implementation of JavaCallArguments + +intptr_t* JavaCallArguments::parameters() { + // First convert all handles to oops + for(int i = 0; i < _size; i++) { + if (_is_oop[i]) { + // Handle conversion + _value[i] = (intptr_t)Handle::raw_resolve((oop *)_value[i]); + } + // The parameters are moved to the parameters array to include the tags. + if (TaggedStackInterpreter) { + // Tags are interspersed with arguments. Tags are first. + int tagged_index = i*2; + _parameters[tagged_index] = _is_oop[i] ? frame::TagReference : + frame::TagValue; + _parameters[tagged_index+1] = _value[i]; + } + } + // Return argument vector + return TaggedStackInterpreter ? _parameters : _value; +} + +//-------------------------------------------------------------------------------------- +// Non-Product code +#ifndef PRODUCT + +class SignatureChekker : public SignatureIterator { + private: + bool *_is_oop; + int _pos; + BasicType _return_type; + + public: + bool _is_return; + + SignatureChekker(symbolHandle signature, BasicType return_type, bool is_static, bool* is_oop) : SignatureIterator(signature) { + _is_oop = is_oop; + _is_return = false; + _return_type = return_type; + _pos = 0; + if (!is_static) { + check_value(true); // Receiver must be an oop + } + } + + void check_value(bool type) { + guarantee(_is_oop[_pos++] == type, "signature does not match pushed arguments"); + } + + void check_doing_return(bool state) { _is_return = state; } + + void check_return_type(BasicType t) { + guarantee(_is_return && t == _return_type, "return type does not match"); + } + + void check_int(BasicType t) { + if (_is_return) { + check_return_type(t); + return; + } + check_value(false); + } + + void check_double(BasicType t) { check_long(t); } + + void check_long(BasicType t) { + if (_is_return) { + check_return_type(t); + return; + } + + check_value(false); + check_value(false); + } + + void check_obj(BasicType t) { + if (_is_return) { + check_return_type(t); + return; + } + check_value(true); + } + + void do_bool() { check_int(T_BOOLEAN); } + void do_char() { check_int(T_CHAR); } + void do_float() { check_int(T_FLOAT); } + void do_double() { check_double(T_DOUBLE); } + void do_byte() { check_int(T_BYTE); } + void do_short() { check_int(T_SHORT); } + void do_int() { check_int(T_INT); } + void do_long() { check_long(T_LONG); } + void do_void() { check_return_type(T_VOID); } + void do_object(int begin, int end) { check_obj(T_OBJECT); } + void do_array(int begin, int end) { check_obj(T_OBJECT); } +}; + +void JavaCallArguments::verify(methodHandle method, BasicType return_type, + Thread *thread) { + guarantee(method->size_of_parameters() == size_of_parameters(), "wrong no. of arguments pushed"); + + // Treat T_OBJECT and T_ARRAY as the same + if (return_type == T_ARRAY) return_type = T_OBJECT; + + // Check that oop information is correct + symbolHandle signature (thread, method->signature()); + + SignatureChekker sc(signature, return_type, method->is_static(),_is_oop); + sc.iterate_parameters(); + sc.check_doing_return(true); + sc.iterate_returntype(); +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/runtime/javaCalls.hpp b/hotspot/src/share/vm/runtime/javaCalls.hpp new file mode 100644 index 00000000000..5923430a533 --- /dev/null +++ b/hotspot/src/share/vm/runtime/javaCalls.hpp @@ -0,0 +1,195 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A JavaCallWrapper is constructed before each JavaCall and destructed after the call. +// Its purpose is to allocate/deallocate a new handle block and to save/restore the last +// Java fp/sp. A pointer to the JavaCallWrapper is stored on the stack. + +class JavaCallWrapper: StackObj { + friend class VMStructs; + private: + JavaThread* _thread; // the thread to which this call belongs + JNIHandleBlock* _handles; // the saved handle block + methodOop _callee_method; // to be able to collect arguments if entry frame is top frame + oop _receiver; // the receiver of the call (if a non-static call) + + JavaFrameAnchor _anchor; // last thread anchor state that we must restore + + JavaValue* _result; // result value + + public: + // Construction/destruction + JavaCallWrapper(methodHandle callee_method, Handle receiver, JavaValue* result, TRAPS); + ~JavaCallWrapper(); + + // Accessors + JavaThread* thread() const { return _thread; } + JNIHandleBlock* handles() const { return _handles; } + + JavaFrameAnchor* anchor(void) { return &_anchor; } + + JavaValue* result() const { return _result; } + // GC support + methodOop callee_method() { return _callee_method; } + oop receiver() { return _receiver; } + void oops_do(OopClosure* f); + +}; + + +// Encapsulates arguments to a JavaCall (faster, safer, and more convenient than using var-args) +class JavaCallArguments : public StackObj { + private: + enum Constants { + _default_size = 8 // Must be at least # of arguments in JavaCalls methods + }; + + intptr_t _value_buffer [_default_size + 1]; + intptr_t _parameter_buffer [_default_size*2 + 1]; + bool _is_oop_buffer[_default_size + 1]; + + intptr_t* _value; + intptr_t* _parameters; + bool* _is_oop; + int _size; + int _max_size; + bool _start_at_zero; // Support late setting of receiver + + void initialize() { + // Starts at first element to support set_receiver. + _value = &_value_buffer[1]; + _is_oop = &_is_oop_buffer[1]; + + _parameters = &_parameter_buffer[0]; + _max_size = _default_size; + _size = 0; + _start_at_zero = false; + } + + public: + JavaCallArguments() { initialize(); } + + JavaCallArguments(Handle receiver) { + initialize(); + push_oop(receiver); + } + + JavaCallArguments(int max_size) { + if (max_size > _default_size) { + _value = NEW_RESOURCE_ARRAY(intptr_t, max_size + 1); + _is_oop = NEW_RESOURCE_ARRAY(bool, max_size + 1); + if (TaggedStackInterpreter) { + _parameters = NEW_RESOURCE_ARRAY(intptr_t, max_size*2 + 1); + } + // Reserve room for potential receiver in value and is_oop + _value++; _is_oop++; + _max_size = max_size; + _size = 0; + _start_at_zero = false; + } else { + initialize(); + } + } + + inline void push_oop(Handle h) { _is_oop[_size] = true; + JNITypes::put_obj((oop)h.raw_value(), _value, _size); } + + inline void push_int(int i) { _is_oop[_size] = false; + JNITypes::put_int(i, _value, _size); } + + inline void push_double(double d) { _is_oop[_size] = false; _is_oop[_size + 1] = false; + JNITypes::put_double(d, _value, _size); } + + inline void push_long(jlong l) { _is_oop[_size] = false; _is_oop[_size + 1] = false; + JNITypes::put_long(l, _value, _size); } + + inline void push_float(float f) { _is_oop[_size] = false; + JNITypes::put_float(f, _value, _size); } + + // receiver + Handle receiver() { + assert(_size > 0, "must at least be one argument"); + assert(_is_oop[0], "first argument must be an oop"); + assert(_value[0] != 0, "receiver must be not-null"); + return Handle((oop*)_value[0], false); + } + + void set_receiver(Handle h) { + assert(_start_at_zero == false, "can only be called once"); + _start_at_zero = true; + _is_oop--; + _value--; + _size++; + _is_oop[0] = true; + _value[0] = (intptr_t)h.raw_value(); + } + + // Converts all Handles to oops, and returns a reference to parameter vector + intptr_t* parameters() ; + int size_of_parameters() const { return _size; } + + // Verify that pushed arguments fits a given method + void verify(methodHandle method, BasicType return_type, Thread *thread) PRODUCT_RETURN; +}; + +// All calls to Java have to go via JavaCalls. Sets up the stack frame +// and makes sure that the last_Java_frame pointers are chained correctly. +// + +class JavaCalls: AllStatic { + static void call_helper(JavaValue* result, methodHandle* method, JavaCallArguments* args, TRAPS); + public: + // Optimized Constuctor call + static void call_default_constructor(JavaThread* thread, methodHandle method, Handle receiver, TRAPS); + + // call_special + // ------------ + // The receiver must be first oop in argument list + static void call_special(JavaValue* result, KlassHandle klass, symbolHandle name, symbolHandle signature, JavaCallArguments* args, TRAPS); + + static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); // No args + static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, symbolHandle name, symbolHandle signature, Handle arg1, TRAPS); + static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, symbolHandle name, symbolHandle signature, Handle arg1, Handle arg2, TRAPS); + + // virtual call + // ------------ + + // The receiver must be first oop in argument list + static void call_virtual(JavaValue* result, KlassHandle spec_klass, symbolHandle name, symbolHandle signature, JavaCallArguments* args, TRAPS); + + static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, symbolHandle name, symbolHandle signature, TRAPS); // No args + static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, symbolHandle name, symbolHandle signature, Handle arg1, TRAPS); + static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, symbolHandle name, symbolHandle signature, Handle arg1, Handle arg2, TRAPS); + + // Static call + // ----------- + static void call_static(JavaValue* result, KlassHandle klass, symbolHandle name, symbolHandle signature, JavaCallArguments* args, TRAPS); + + static void call_static(JavaValue* result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); + static void call_static(JavaValue* result, KlassHandle klass, symbolHandle name, symbolHandle signature, Handle arg1, TRAPS); + static void call_static(JavaValue* result, KlassHandle klass, symbolHandle name, symbolHandle signature, Handle arg1, Handle arg2, TRAPS); + + // Low-level interface + static void call(JavaValue* result, methodHandle method, JavaCallArguments* args, TRAPS); +}; diff --git a/hotspot/src/share/vm/runtime/javaFrameAnchor.hpp b/hotspot/src/share/vm/runtime/javaFrameAnchor.hpp new file mode 100644 index 00000000000..1320eb12681 --- /dev/null +++ b/hotspot/src/share/vm/runtime/javaFrameAnchor.hpp @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +// +// An object for encapsulating the machine/os dependent part of a JavaThread frame state +// +class JavaThread; + +class JavaFrameAnchor VALUE_OBJ_CLASS_SPEC { +// Too many friends... +friend class CallNativeDirectNode; +friend class OptoRuntime; +friend class Runtime1; +friend class StubAssembler; +friend class CallRuntimeDirectNode; +friend class MacroAssembler; +friend class InterpreterGenerator; +friend class LIR_Assembler; +friend class GraphKit; +friend class StubGenerator; +friend class JavaThread; +friend class frame; +friend class VMStructs; +friend class BytecodeInterpreter; +friend class JavaCallWrapper; + + private: + // + // Whenever _last_Java_sp != NULL other anchor fields MUST be valid! + // The stack may not be walkable [check with walkable() ] but the values must be valid. + // The profiler apparently depends on this. + // + intptr_t* volatile _last_Java_sp; + + // Whenever we call from Java to native we can not be assured that the return + // address that composes the last_Java_frame will be in an accessible location + // so calls from Java to native store that pc (or one good enough to locate + // the oopmap) in the frame anchor. Since the frames that call from Java to + // native are never deoptimized we never need to patch the pc and so this + // is acceptable. + volatile address _last_Java_pc; + + // tells whether the last Java frame is set + // It is important that when last_Java_sp != NULL that the rest of the frame + // anchor (including platform specific) all be valid. + + bool has_last_Java_frame() const { return _last_Java_sp != NULL; } + // This is very dangerous unless sp == NULL + // Invalidate the anchor so that has_last_frame is false + // and no one should look at the other fields. + void zap(void) { _last_Java_sp = NULL; } + +#include "incls/_javaFrameAnchor_pd.hpp.incl" + +public: + JavaFrameAnchor() { clear(); } + JavaFrameAnchor(JavaFrameAnchor *src) { copy(src); } + + address last_Java_pc(void) { return _last_Java_pc; } + void set_last_Java_pc(address pc) { _last_Java_pc = pc; } + + // Assembly stub generation helpers + + static ByteSize last_Java_sp_offset() { return byte_offset_of(JavaFrameAnchor, _last_Java_sp); } + static ByteSize last_Java_pc_offset() { return byte_offset_of(JavaFrameAnchor, _last_Java_pc); } + +}; diff --git a/hotspot/src/share/vm/runtime/jfieldIDWorkaround.hpp b/hotspot/src/share/vm/runtime/jfieldIDWorkaround.hpp new file mode 100644 index 00000000000..ce1c34bb358 --- /dev/null +++ b/hotspot/src/share/vm/runtime/jfieldIDWorkaround.hpp @@ -0,0 +1,159 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class jfieldIDWorkaround: AllStatic { + // This workaround is because JVMTI doesn't have distinct entry points + // for methods that use static jfieldIDs and instance jfieldIDs. + // The workaround is to steal a low-order bit: + // a 1 means the jfieldID is an instance jfieldID, + // and the rest of the word is the offset of the field. + // a 0 means the jfieldID is a static jfieldID, + // and the rest of the word is the JNIid*. + // + // Another low-order bit is used to mark if an instance field + // is accompanied by an indication of which class it applies to. + // + // Bit-format of a jfieldID (most significant first): + // address:30 instance=0:1 checked=0:1 + // offset:30 instance=1:1 checked=0:1 + // klass:23 offset:7 instance=1:1 checked=1:1 + // + // If the offset does not fit in 7 bits, or if the fieldID is + // not checked, then the checked bit is zero and the rest of + // the word (30 bits) contains only the offset. + // + private: + enum { + checked_bits = 1, + instance_bits = 1, + address_bits = BitsPerWord - checked_bits - instance_bits, + + large_offset_bits = address_bits, // unioned with address + small_offset_bits = 7, + klass_bits = address_bits - small_offset_bits, + + checked_shift = 0, + instance_shift = checked_shift + checked_bits, + address_shift = instance_shift + instance_bits, + + offset_shift = address_shift, // unioned with address + klass_shift = offset_shift + small_offset_bits, + + checked_mask_in_place = right_n_bits(checked_bits) << checked_shift, + instance_mask_in_place = right_n_bits(instance_bits) << instance_shift, +#ifndef _WIN64 + large_offset_mask = right_n_bits(large_offset_bits), + small_offset_mask = right_n_bits(small_offset_bits), + klass_mask = right_n_bits(klass_bits) +#endif + }; + +#ifdef _WIN64 + // These values are too big for Win64 + const static uintptr_t large_offset_mask = right_n_bits(large_offset_bits); + const static uintptr_t small_offset_mask = right_n_bits(small_offset_bits); + const static uintptr_t klass_mask = right_n_bits(klass_bits); +#endif + + // helper routines: + static bool is_checked_jfieldID(jfieldID id) { + uintptr_t as_uint = (uintptr_t) id; + return ((as_uint & checked_mask_in_place) != 0); + } + static intptr_t raw_instance_offset(jfieldID id) { + uintptr_t result = (uintptr_t) id >> address_shift; + if (VerifyJNIFields && is_checked_jfieldID(id)) { + result &= small_offset_mask; // cut off the hash bits + } + return (intptr_t)result; + } + static intptr_t encode_klass_hash(klassOop k, intptr_t offset); + static bool klass_hash_ok(klassOop k, jfieldID id); + static void verify_instance_jfieldID(klassOop k, jfieldID id); + + public: + static bool is_valid_jfieldID(klassOop k, jfieldID id); + + static bool is_instance_jfieldID(klassOop k, jfieldID id) { + uintptr_t as_uint = (uintptr_t) id; + return ((as_uint & instance_mask_in_place) != 0); + } + static bool is_static_jfieldID(jfieldID id) { + uintptr_t as_uint = (uintptr_t) id; + return ((as_uint & instance_mask_in_place) == 0); + } + + static jfieldID to_instance_jfieldID(klassOop k, int offset) { + intptr_t as_uint = ((offset & large_offset_mask) << offset_shift) | instance_mask_in_place; + if (VerifyJNIFields) { + as_uint |= encode_klass_hash(k, offset); + } + jfieldID result = (jfieldID) as_uint; +#ifndef ASSERT + // always verify in debug mode; switchable in anything else + if (VerifyJNIFields) +#endif // ASSERT + { + verify_instance_jfieldID(k, result); + } + assert(raw_instance_offset(result) == (offset & large_offset_mask), "extract right offset"); + return result; + } + + static intptr_t from_instance_jfieldID(klassOop k, jfieldID id) { +#ifndef ASSERT + // always verify in debug mode; switchable in anything else + if (VerifyJNIFields) +#endif // ASSERT + { + verify_instance_jfieldID(k, id); + } + return raw_instance_offset(id); + } + + static jfieldID to_static_jfieldID(JNIid* id) { + assert(id->is_static_field_id(), "from_JNIid, but not static field id"); + jfieldID result = (jfieldID) id; + assert(from_static_jfieldID(result) == id, "must produce the same static id"); + return result; + } + + static JNIid* from_static_jfieldID(jfieldID id) { + assert(jfieldIDWorkaround::is_static_jfieldID(id), + "to_JNIid, but not static jfieldID"); + JNIid* result = (JNIid*) id; + assert(result->is_static_field_id(), "to_JNIid, but not static field id"); + return result; + } + + static jfieldID to_jfieldID(instanceKlassHandle k, int offset, bool is_static) { + if (is_static) { + JNIid *id = k->jni_id_for(offset); + debug_only(id->set_is_static_field_id()); + return jfieldIDWorkaround::to_static_jfieldID(id); + } else { + return jfieldIDWorkaround::to_instance_jfieldID(k(), offset); + } + } +}; diff --git a/hotspot/src/share/vm/runtime/jniHandles.cpp b/hotspot/src/share/vm/runtime/jniHandles.cpp new file mode 100644 index 00000000000..05078c5591b --- /dev/null +++ b/hotspot/src/share/vm/runtime/jniHandles.cpp @@ -0,0 +1,576 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jniHandles.cpp.incl" + + +JNIHandleBlock* JNIHandles::_global_handles = NULL; +JNIHandleBlock* JNIHandles::_weak_global_handles = NULL; +oop JNIHandles::_deleted_handle = NULL; + + +jobject JNIHandles::make_local(oop obj) { + if (obj == NULL) { + return NULL; // ignore null handles + } else { + Thread* thread = Thread::current(); + assert(Universe::heap()->is_in_reserved(obj), "sanity check"); + return thread->active_handles()->allocate_handle(obj); + } +} + + +// optimized versions + +jobject JNIHandles::make_local(Thread* thread, oop obj) { + if (obj == NULL) { + return NULL; // ignore null handles + } else { + assert(Universe::heap()->is_in_reserved(obj), "sanity check"); + return thread->active_handles()->allocate_handle(obj); + } +} + + +jobject JNIHandles::make_local(JNIEnv* env, oop obj) { + if (obj == NULL) { + return NULL; // ignore null handles + } else { + JavaThread* thread = JavaThread::thread_from_jni_environment(env); + assert(Universe::heap()->is_in_reserved(obj), "sanity check"); + return thread->active_handles()->allocate_handle(obj); + } +} + + +jobject JNIHandles::make_global(Handle obj) { + jobject res = NULL; + if (!obj.is_null()) { + // ignore null handles + MutexLocker ml(JNIGlobalHandle_lock); + assert(Universe::heap()->is_in_reserved(obj()), "sanity check"); + res = _global_handles->allocate_handle(obj()); + } else { + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } + + return res; +} + + +jobject JNIHandles::make_weak_global(Handle obj) { + jobject res = NULL; + if (!obj.is_null()) { + // ignore null handles + MutexLocker ml(JNIGlobalHandle_lock); + assert(Universe::heap()->is_in_reserved(obj()), "sanity check"); + res = _weak_global_handles->allocate_handle(obj()); + } else { + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } + return res; +} + +jmethodID JNIHandles::make_jmethod_id(methodHandle mh) { + return (jmethodID) make_weak_global(mh); +} + + + +void JNIHandles::change_method_associated_with_jmethod_id(jmethodID jmid, methodHandle mh) { + MutexLocker ml(JNIGlobalHandle_lock); // Is this necessary? + Handle obj = (Handle)mh; + oop* jobj = (oop*)jmid; + *jobj = obj(); +} + + +void JNIHandles::destroy_global(jobject handle) { + if (handle != NULL) { + assert(is_global_handle(handle), "Invalid delete of global JNI handle"); + *((oop*)handle) = deleted_handle(); // Mark the handle as deleted, allocate will reuse it + } +} + + +void JNIHandles::destroy_weak_global(jobject handle) { + if (handle != NULL) { + assert(!CheckJNICalls || is_weak_global_handle(handle), "Invalid delete of weak global JNI handle"); + *((oop*)handle) = deleted_handle(); // Mark the handle as deleted, allocate will reuse it + } +} + +void JNIHandles::destroy_jmethod_id(jmethodID mid) { + destroy_weak_global((jobject)mid); +} + + +void JNIHandles::oops_do(OopClosure* f) { + f->do_oop(&_deleted_handle); + _global_handles->oops_do(f); +} + + +void JNIHandles::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) { + _weak_global_handles->weak_oops_do(is_alive, f); +} + + +void JNIHandles::initialize() { + _global_handles = JNIHandleBlock::allocate_block(); + _weak_global_handles = JNIHandleBlock::allocate_block(); + EXCEPTION_MARK; + // We will never reach the CATCH below since Exceptions::_throw will cause + // the VM to exit if an exception is thrown during initialization + klassOop k = SystemDictionary::object_klass(); + _deleted_handle = instanceKlass::cast(k)->allocate_permanent_instance(CATCH); +} + + +bool JNIHandles::is_local_handle(Thread* thread, jobject handle) { + JNIHandleBlock* block = thread->active_handles(); + + // Look back past possible native calls to jni_PushLocalFrame. + while (block != NULL) { + if (block->chain_contains(handle)) { + return true; + } + block = block->pop_frame_link(); + } + return false; +} + + +// Determine if the handle is somewhere in the current thread's stack. +// We easily can't isolate any particular stack frame the handle might +// come from, so we'll check the whole stack. + +bool JNIHandles::is_frame_handle(JavaThread* thr, jobject obj) { + // If there is no java frame, then this must be top level code, such + // as the java command executable, in which case, this type of handle + // is not permitted. + return (thr->has_last_Java_frame() && + (void*)obj < (void*)thr->stack_base() && + (void*)obj >= (void*)thr->last_Java_sp()); +} + + +bool JNIHandles::is_global_handle(jobject handle) { + return _global_handles->chain_contains(handle); +} + + +bool JNIHandles::is_weak_global_handle(jobject handle) { + return _weak_global_handles->chain_contains(handle); +} + +long JNIHandles::global_handle_memory_usage() { + return _global_handles->memory_usage(); +} + +long JNIHandles::weak_global_handle_memory_usage() { + return _weak_global_handles->memory_usage(); +} + + +class AlwaysAliveClosure: public BoolObjectClosure { +public: + bool do_object_b(oop obj) { return true; } + void do_object(oop obj) { assert(false, "Don't call"); } +}; + +class CountHandleClosure: public OopClosure { +private: + int _count; +public: + CountHandleClosure(): _count(0) {} + void do_oop(oop* unused) { + _count++; + } + int count() { return _count; } +}; + +// We assume this is called at a safepoint: no lock is needed. +void JNIHandles::print_on(outputStream* st) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + assert(_global_handles != NULL && _weak_global_handles != NULL, + "JNIHandles not initialized"); + + CountHandleClosure global_handle_count; + AlwaysAliveClosure always_alive; + oops_do(&global_handle_count); + weak_oops_do(&always_alive, &global_handle_count); + + st->print_cr("JNI global references: %d", global_handle_count.count()); + st->cr(); + st->flush(); +} + +class VerifyHandleClosure: public OopClosure { +public: + void do_oop(oop* root) { + (*root)->verify(); + } +}; + +void JNIHandles::verify() { + VerifyHandleClosure verify_handle; + AlwaysAliveClosure always_alive; + + oops_do(&verify_handle); + weak_oops_do(&always_alive, &verify_handle); +} + + + +void jni_handles_init() { + JNIHandles::initialize(); +} + + +int JNIHandleBlock::_blocks_allocated = 0; +JNIHandleBlock* JNIHandleBlock::_block_free_list = NULL; +#ifndef PRODUCT +JNIHandleBlock* JNIHandleBlock::_block_list = NULL; +#endif + + +void JNIHandleBlock::zap() { + // Zap block values + _top = 0; + for (int index = 0; index < block_size_in_oops; index++) { + _handles[index] = badJNIHandle; + } +} + +JNIHandleBlock* JNIHandleBlock::allocate_block(Thread* thread) { + assert(thread == NULL || thread == Thread::current(), "sanity check"); + JNIHandleBlock* block; + // Check the thread-local free list for a block so we don't + // have to acquire a mutex. + if (thread != NULL && thread->free_handle_block() != NULL) { + block = thread->free_handle_block(); + thread->set_free_handle_block(block->_next); + } + else { + // locking with safepoint checking introduces a potential deadlock: + // - we would hold JNIHandleBlockFreeList_lock and then Threads_lock + // - another would hold Threads_lock (jni_AttachCurrentThread) and then + // JNIHandleBlockFreeList_lock (JNIHandleBlock::allocate_block) + MutexLockerEx ml(JNIHandleBlockFreeList_lock, + Mutex::_no_safepoint_check_flag); + if (_block_free_list == NULL) { + // Allocate new block + block = new JNIHandleBlock(); + _blocks_allocated++; + if (TraceJNIHandleAllocation) { + tty->print_cr("JNIHandleBlock " INTPTR_FORMAT " allocated (%d total blocks)", + block, _blocks_allocated); + } + if (ZapJNIHandleArea) block->zap(); + #ifndef PRODUCT + // Link new block to list of all allocated blocks + block->_block_list_link = _block_list; + _block_list = block; + #endif + } else { + // Get block from free list + block = _block_free_list; + _block_free_list = _block_free_list->_next; + } + } + block->_top = 0; + block->_next = NULL; + block->_pop_frame_link = NULL; + // _last, _free_list & _allocate_before_rebuild initialized in allocate_handle + debug_only(block->_last = NULL); + debug_only(block->_free_list = NULL); + debug_only(block->_allocate_before_rebuild = -1); + return block; +} + + +void JNIHandleBlock::release_block(JNIHandleBlock* block, Thread* thread) { + assert(thread == NULL || thread == Thread::current(), "sanity check"); + JNIHandleBlock* pop_frame_link = block->pop_frame_link(); + // Put returned block at the beginning of the thread-local free list. + // Note that if thread == NULL, we use it as an implicit argument that + // we _don't_ want the block to be kept on the free_handle_block. + // See for instance JavaThread::exit(). + if (thread != NULL ) { + if (ZapJNIHandleArea) block->zap(); + JNIHandleBlock* freelist = thread->free_handle_block(); + block->_pop_frame_link = NULL; + thread->set_free_handle_block(block); + + // Add original freelist to end of chain + if ( freelist != NULL ) { + while ( block->_next != NULL ) block = block->_next; + block->_next = freelist; + } + block = NULL; + } + if (block != NULL) { + // Return blocks to free list + // locking with safepoint checking introduces a potential deadlock: + // - we would hold JNIHandleBlockFreeList_lock and then Threads_lock + // - another would hold Threads_lock (jni_AttachCurrentThread) and then + // JNIHandleBlockFreeList_lock (JNIHandleBlock::allocate_block) + MutexLockerEx ml(JNIHandleBlockFreeList_lock, + Mutex::_no_safepoint_check_flag); + while (block != NULL) { + if (ZapJNIHandleArea) block->zap(); + JNIHandleBlock* next = block->_next; + block->_next = _block_free_list; + _block_free_list = block; + block = next; + } + } + if (pop_frame_link != NULL) { + // As a sanity check we release blocks pointed to by the pop_frame_link. + // This should never happen (only if PopLocalFrame is not called the + // correct number of times). + release_block(pop_frame_link, thread); + } +} + + +void JNIHandleBlock::oops_do(OopClosure* f) { + JNIHandleBlock* current_chain = this; + // Iterate over chain of blocks, followed by chains linked through the + // pop frame links. + while (current_chain != NULL) { + for (JNIHandleBlock* current = current_chain; current != NULL; + current = current->_next) { + assert(current == current_chain || current->pop_frame_link() == NULL, + "only blocks first in chain should have pop frame link set"); + for (int index = 0; index < current->_top; index++) { + oop* root = &(current->_handles)[index]; + oop value = *root; + // traverse heap pointers only, not deleted handles or free list + // pointers + if (value != NULL && Universe::heap()->is_in_reserved(value)) { + f->do_oop(root); + } + } + // the next handle block is valid only if current block is full + if (current->_top < block_size_in_oops) { + break; + } + } + current_chain = current_chain->pop_frame_link(); + } +} + + +void JNIHandleBlock::weak_oops_do(BoolObjectClosure* is_alive, + OopClosure* f) { + for (JNIHandleBlock* current = this; current != NULL; current = current->_next) { + assert(current->pop_frame_link() == NULL, + "blocks holding weak global JNI handles should not have pop frame link set"); + for (int index = 0; index < current->_top; index++) { + oop* root = &(current->_handles)[index]; + oop value = *root; + // traverse heap pointers only, not deleted handles or free list pointers + if (value != NULL && Universe::heap()->is_in_reserved(value)) { + if (is_alive->do_object_b(value)) { + // The weakly referenced object is alive, update pointer + f->do_oop(root); + } else { + // The weakly referenced object is not alive, clear the reference by storing NULL + if (TraceReferenceGC) { + tty->print_cr("Clearing JNI weak reference (" INTPTR_FORMAT ")", root); + } + *root = NULL; + } + } + } + // the next handle block is valid only if current block is full + if (current->_top < block_size_in_oops) { + break; + } + } +} + + +jobject JNIHandleBlock::allocate_handle(oop obj) { + assert(Universe::heap()->is_in_reserved(obj), "sanity check"); + if (_top == 0) { + // This is the first allocation or the initial block got zapped when + // entering a native function. If we have any following blocks they are + // not valid anymore. + for (JNIHandleBlock* current = _next; current != NULL; + current = current->_next) { + assert(current->_last == NULL, "only first block should have _last set"); + assert(current->_free_list == NULL, + "only first block should have _free_list set"); + current->_top = 0; + if (ZapJNIHandleArea) current->zap(); + } + // Clear initial block + _free_list = NULL; + _allocate_before_rebuild = 0; + _last = this; + if (ZapJNIHandleArea) zap(); + } + + // Try last block + if (_last->_top < block_size_in_oops) { + oop* handle = &(_last->_handles)[_last->_top++]; + *handle = obj; + return (jobject) handle; + } + + // Try free list + if (_free_list != NULL) { + oop* handle = _free_list; + _free_list = (oop*) *_free_list; + *handle = obj; + return (jobject) handle; + } + // Check if unused block follow last + if (_last->_next != NULL) { + // update last and retry + _last = _last->_next; + return allocate_handle(obj); + } + + // No space available, we have to rebuild free list or expand + if (_allocate_before_rebuild == 0) { + rebuild_free_list(); // updates _allocate_before_rebuild counter + } else { + // Append new block + Thread* thread = Thread::current(); + Handle obj_handle(thread, obj); + // This can block, so we need to preserve obj accross call. + _last->_next = JNIHandleBlock::allocate_block(thread); + _last = _last->_next; + _allocate_before_rebuild--; + obj = obj_handle(); + } + return allocate_handle(obj); // retry +} + + +void JNIHandleBlock::rebuild_free_list() { + assert(_allocate_before_rebuild == 0 && _free_list == NULL, "just checking"); + int free = 0; + int blocks = 0; + for (JNIHandleBlock* current = this; current != NULL; current = current->_next) { + for (int index = 0; index < current->_top; index++) { + oop* handle = &(current->_handles)[index]; + if (*handle == JNIHandles::deleted_handle()) { + // this handle was cleared out by a delete call, reuse it + *handle = (oop) _free_list; + _free_list = handle; + free++; + } + } + // we should not rebuild free list if there are unused handles at the end + assert(current->_top == block_size_in_oops, "just checking"); + blocks++; + } + // Heuristic: if more than half of the handles are free we rebuild next time + // as well, otherwise we append a corresponding number of new blocks before + // attempting a free list rebuild again. + int total = blocks * block_size_in_oops; + int extra = total - 2*free; + if (extra > 0) { + // Not as many free handles as we would like - compute number of new blocks to append + _allocate_before_rebuild = (extra + block_size_in_oops - 1) / block_size_in_oops; + } + if (TraceJNIHandleAllocation) { + tty->print_cr("Rebuild free list JNIHandleBlock " INTPTR_FORMAT " blocks=%d used=%d free=%d add=%d", + this, blocks, total-free, free, _allocate_before_rebuild); + } +} + + +bool JNIHandleBlock::contains(jobject handle) const { + return ((jobject)&_handles[0] <= handle && handle<(jobject)&_handles[_top]); +} + + +bool JNIHandleBlock::chain_contains(jobject handle) const { + for (JNIHandleBlock* current = (JNIHandleBlock*) this; current != NULL; current = current->_next) { + if (current->contains(handle)) { + return true; + } + } + return false; +} + + +int JNIHandleBlock::length() const { + int result = 1; + for (JNIHandleBlock* current = _next; current != NULL; current = current->_next) { + result++; + } + return result; +} + +// This method is not thread-safe, i.e., must be called whule holding a lock on the +// structure. +long JNIHandleBlock::memory_usage() const { + return length() * sizeof(JNIHandleBlock); +} + + +#ifndef PRODUCT + +bool JNIHandleBlock::any_contains(jobject handle) { + for (JNIHandleBlock* current = _block_list; current != NULL; current = current->_block_list_link) { + if (current->contains(handle)) { + return true; + } + } + return false; +} + +void JNIHandleBlock::print_statistics() { + int used_blocks = 0; + int free_blocks = 0; + int used_handles = 0; + int free_handles = 0; + JNIHandleBlock* block = _block_list; + while (block != NULL) { + if (block->_top > 0) { + used_blocks++; + } else { + free_blocks++; + } + used_handles += block->_top; + free_handles += (block_size_in_oops - block->_top); + block = block->_block_list_link; + } + tty->print_cr("JNIHandleBlocks statistics"); + tty->print_cr("- blocks allocated: %d", used_blocks + free_blocks); + tty->print_cr("- blocks in use: %d", used_blocks); + tty->print_cr("- blocks free: %d", free_blocks); + tty->print_cr("- handles in use: %d", used_handles); + tty->print_cr("- handles free: %d", free_handles); +} + +#endif diff --git a/hotspot/src/share/vm/runtime/jniHandles.hpp b/hotspot/src/share/vm/runtime/jniHandles.hpp new file mode 100644 index 00000000000..88cb71559a1 --- /dev/null +++ b/hotspot/src/share/vm/runtime/jniHandles.hpp @@ -0,0 +1,212 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class JNIHandleBlock; + + +// Interface for creating and resolving local/global JNI handles + +class JNIHandles : AllStatic { + friend class VMStructs; + private: + static JNIHandleBlock* _global_handles; // First global handle block + static JNIHandleBlock* _weak_global_handles; // First weak global handle block + static oop _deleted_handle; // Sentinel marking deleted handles + + public: + // Resolve handle into oop + inline static oop resolve(jobject handle); + // Resolve externally provided handle into oop with some guards + inline static oop resolve_external_guard(jobject handle); + // Resolve handle into oop, result guaranteed not to be null + inline static oop resolve_non_null(jobject handle); + + // Local handles + static jobject make_local(oop obj); + static jobject make_local(JNIEnv* env, oop obj); // Fast version when env is known + static jobject make_local(Thread* thread, oop obj); // Even faster version when current thread is known + inline static void destroy_local(jobject handle); + + // Global handles + static jobject make_global(Handle obj); + static void destroy_global(jobject handle); + + // Weak global handles + static jobject make_weak_global(Handle obj); + static void destroy_weak_global(jobject handle); + + // jmethodID handling (as Weak global handles). + // Because the useful life-span of a jmethodID cannot be determined, once created they are + // never reclaimed. The methods to which they refer, however, can be GC'ed away if the class + // is unloaded or if the method is made obsolete or deleted -- in these cases, the jmethodID + // refers to NULL (as is the case for any weak reference). + static jmethodID make_jmethod_id(methodHandle mh); + static void destroy_jmethod_id(jmethodID mid); + inline static methodOop resolve_jmethod_id(jmethodID mid); + inline static methodOop checked_resolve_jmethod_id(jmethodID mid); // NULL on invalid jmethodID + static void change_method_associated_with_jmethod_id(jmethodID jmid, methodHandle mh); + + // Sentinel marking deleted handles in block. Note that we cannot store NULL as + // the sentinel, since clearing weak global JNI refs are done by storing NULL in + // the handle. The handle may not be reused before destroy_weak_global is called. + static oop deleted_handle() { return _deleted_handle; } + + // Initialization + static void initialize(); + + // Debugging + static void print_on(outputStream* st); + static void print() { print_on(tty); } + static void verify(); + static bool is_local_handle(Thread* thread, jobject handle); + static bool is_frame_handle(JavaThread* thr, jobject obj); + static bool is_global_handle(jobject handle); + static bool is_weak_global_handle(jobject handle); + static long global_handle_memory_usage(); + static long weak_global_handle_memory_usage(); + + // Garbage collection support(global handles only, local handles are traversed from thread) + // Traversal of regular global handles + static void oops_do(OopClosure* f); + // Traversal of weak global handles. Unreachable oops are cleared. + static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f); +}; + + + +// JNI handle blocks holding local/global JNI handles + +class JNIHandleBlock : public CHeapObj { + friend class VMStructs; + private: + enum SomeConstants { + block_size_in_oops = 32 // Number of handles per handle block + }; + + oop _handles[block_size_in_oops]; // The handles + int _top; // Index of next unused handle + JNIHandleBlock* _next; // Link to next block + + // The following instance variables are only used by the first block in a chain. + // Having two types of blocks complicates the code and the space overhead in negligble. + JNIHandleBlock* _last; // Last block in use + JNIHandleBlock* _pop_frame_link; // Block to restore on PopLocalFrame call + oop* _free_list; // Handle free list + int _allocate_before_rebuild; // Number of blocks to allocate before rebuilding free list + + #ifndef PRODUCT + JNIHandleBlock* _block_list_link; // Link for list below + static JNIHandleBlock* _block_list; // List of all allocated blocks (for debugging only) + #endif + + static JNIHandleBlock* _block_free_list; // Free list of currently unused blocks + static int _blocks_allocated; // For debugging/printing + + // Fill block with bad_handle values + void zap(); + + // No more handles in the both the current and following blocks + void clear() { _top = 0; } + + // Free list computation + void rebuild_free_list(); + + public: + // Handle allocation + jobject allocate_handle(oop obj); + + // Block allocation and block free list management + static JNIHandleBlock* allocate_block(Thread* thread = NULL); + static void release_block(JNIHandleBlock* block, Thread* thread = NULL); + + // JNI PushLocalFrame/PopLocalFrame support + JNIHandleBlock* pop_frame_link() const { return _pop_frame_link; } + void set_pop_frame_link(JNIHandleBlock* block) { _pop_frame_link = block; } + + // Stub generator support + static int top_offset_in_bytes() { return offset_of(JNIHandleBlock, _top); } + + // Garbage collection support + // Traversal of regular handles + void oops_do(OopClosure* f); + // Traversal of weak handles. Unreachable oops are cleared. + void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f); + + // Debugging + bool chain_contains(jobject handle) const; // Does this block or following blocks contain handle + bool contains(jobject handle) const; // Does this block contain handle + int length() const; // Length of chain starting with this block + long memory_usage() const; + #ifndef PRODUCT + static bool any_contains(jobject handle); // Does any block currently in use contain handle + static void print_statistics(); + #endif +}; + + +inline oop JNIHandles::resolve(jobject handle) { + oop result = (handle == NULL ? (oop)NULL : *(oop*)handle); + assert(result != NULL || (handle == NULL || !CheckJNICalls || is_weak_global_handle(handle)), "Invalid value read from jni handle"); + assert(result != badJNIHandle, "Pointing to zapped jni handle area"); + return result; +}; + + +inline oop JNIHandles::resolve_external_guard(jobject handle) { + if (handle == NULL) return NULL; + oop result = *(oop*)handle; + if (result == NULL || result == badJNIHandle) return NULL; + return result; +}; + + +inline oop JNIHandles::resolve_non_null(jobject handle) { + assert(handle != NULL, "JNI handle should not be null"); + oop result = *(oop*)handle; + assert(result != NULL, "Invalid value read from jni handle"); + assert(result != badJNIHandle, "Pointing to zapped jni handle area"); + // Don't let that private _deleted_handle object escape into the wild. + assert(result != deleted_handle(), "Used a deleted global handle."); + return result; +}; + +inline methodOop JNIHandles::resolve_jmethod_id(jmethodID mid) { + return (methodOop) resolve_non_null((jobject)mid); +}; + +inline methodOop JNIHandles::checked_resolve_jmethod_id(jmethodID mid) { + jobject handle = (jobject)mid; + if (is_weak_global_handle(handle)) { + return (methodOop) resolve_non_null(handle); + } else { + return (methodOop) NULL; + } +}; + + +inline void JNIHandles::destroy_local(jobject handle) { + if (handle != NULL) { + *((oop*)handle) = deleted_handle(); // Mark the handle as deleted, allocate will reuse it + } +} diff --git a/hotspot/src/share/vm/runtime/jniPeriodicChecker.cpp b/hotspot/src/share/vm/runtime/jniPeriodicChecker.cpp new file mode 100644 index 00000000000..7632e28f0c6 --- /dev/null +++ b/hotspot/src/share/vm/runtime/jniPeriodicChecker.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_jniPeriodicChecker.cpp.incl" + + +// -------------------------------------------------------- +// Class to aid in periodic checking under CheckJNICalls +class JniPeriodicCheckerTask : public PeriodicTask { + public: + JniPeriodicCheckerTask(int interval_time) : PeriodicTask(interval_time) {} + void task() { os::run_periodic_checks(); } + static void engage(); + static void disengage(); +}; + + +//---------------------------------------------------------- +// Implementation of JniPeriodicChecker + +JniPeriodicCheckerTask* JniPeriodicChecker::_task = NULL; + +/* + * The engage() method is called at initialization time via + * Thread::create_vm() to initialize the JniPeriodicChecker and + * register it with the WatcherThread as a periodic task. + */ +void JniPeriodicChecker::engage() { + if (CheckJNICalls && !is_active()) { + // start up the periodic task + _task = new JniPeriodicCheckerTask(10); + _task->enroll(); + } +} + + +/* + * the disengage() method is responsible for deactivating the periodic + * task. This method is called from before_exit() in java.cpp and is only called + * after the WatcherThread has been stopped. + */ +void JniPeriodicChecker::disengage() { + if (CheckJNICalls && is_active()) { + // remove JniPeriodicChecker + _task->disenroll(); + delete _task; + _task = NULL; + } +} + +void jniPeriodicChecker_exit() { + if (!CheckJNICalls) return; +} diff --git a/hotspot/src/share/vm/runtime/jniPeriodicChecker.hpp b/hotspot/src/share/vm/runtime/jniPeriodicChecker.hpp new file mode 100644 index 00000000000..9914b188139 --- /dev/null +++ b/hotspot/src/share/vm/runtime/jniPeriodicChecker.hpp @@ -0,0 +1,52 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class JniPeriodicCheckerTask; + +/* + * This gets activated under Xcheck:jni (CheckJNICalls), and is typically + * to detect any errors caused by JNI applications, such as signal handler, + * hijacking, va 0x0 hijacking either by mmap or an OS error. + */ + + +class JniPeriodicChecker : AllStatic { + + friend class JniPeriodicCheckerTask; + + private: + static JniPeriodicCheckerTask* _task; + + public: + // Start/stop task + static void engage(); + static void disengage(); + + static bool is_active() { return _task != NULL; } + + static void initialize(); + static void destroy(); +}; + +void jniPeriodicChecker_exit(); diff --git a/hotspot/src/share/vm/runtime/memprofiler.cpp b/hotspot/src/share/vm/runtime/memprofiler.cpp new file mode 100644 index 00000000000..4d5ad533d83 --- /dev/null +++ b/hotspot/src/share/vm/runtime/memprofiler.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_memprofiler.cpp.incl" + +#ifndef PRODUCT + +// -------------------------------------------------------- +// MemProfilerTask + +class MemProfilerTask : public PeriodicTask { + public: + MemProfilerTask(int interval_time) : PeriodicTask(interval_time) {} + void task(); +}; + + +void MemProfilerTask::task() { + // Get thread lock to provide mutual exclusion, and so we can iterate safely over the thread list. + MutexLocker mu(Threads_lock); + MemProfiler::do_trace(); +} + + +//---------------------------------------------------------- +// Implementation of MemProfiler + +MemProfilerTask* MemProfiler::_task = NULL; +FILE* MemProfiler::_log_fp = NULL; + + +bool MemProfiler::is_active() { + return _task != NULL; +} + + +void MemProfiler::engage() { + const char *log_name = "mprofile.log"; + if (!is_active()) { + // Create log file + _log_fp = fopen(log_name , "w+"); + if (_log_fp == NULL) { + fatal1("MemProfiler: Cannot create log file: %s", log_name); + } + fprintf(_log_fp, "MemProfiler: sizes are in Kb, time is in seconds since startup\n\n"); + fprintf(_log_fp, " time, #thr, #cls, heap, heap, perm, perm, code, hndls, rescs, oopmp\n"); + fprintf(_log_fp, " used, total, used, total, total, total, total, total\n"); + fprintf(_log_fp, "--------------------------------------------------------------------------\n"); + + _task = new MemProfilerTask(MemProfilingInterval); + _task->enroll(); + } +} + + +void MemProfiler::disengage() { + if (!is_active()) return; + // Do one last trace at disengage time + do_trace(); + + // Close logfile + fprintf(_log_fp, "MemProfiler detached\n"); + fclose(_log_fp); + + // remove MemProfilerTask + assert(_task != NULL, "sanity check"); + _task->disenroll(); + delete _task; + _task = NULL; +} + + +void MemProfiler::do_trace() { + // Calculate thread local sizes + size_t handles_memory_usage = VMThread::vm_thread()->handle_area()->size_in_bytes(); + size_t resource_memory_usage = VMThread::vm_thread()->resource_area()->size_in_bytes(); + JavaThread *cur = Threads::first(); + while (cur != NULL) { + handles_memory_usage += cur->handle_area()->size_in_bytes(); + resource_memory_usage += cur->resource_area()->size_in_bytes(); + cur = cur->next(); + } + + // Print trace line in log + fprintf(_log_fp, "%6.1f,%5d,%5d,%6ld,%6ld,%6ld,%6ld,", + os::elapsedTime(), + Threads::number_of_threads(), + SystemDictionary::number_of_classes(), + Universe::heap()->used() / K, + Universe::heap()->capacity() / K, + Universe::heap()->permanent_used() / HWperKB, + Universe::heap()->permanent_capacity() / HWperKB); + + fprintf(_log_fp, "%6ld,", CodeCache::capacity() / K); + + fprintf(_log_fp, "%6ld,%6ld,%6ld\n", + handles_memory_usage / K, + resource_memory_usage / K, + OopMapCache::memory_usage() / K); + fflush(_log_fp); +} + +#endif diff --git a/hotspot/src/share/vm/runtime/memprofiler.hpp b/hotspot/src/share/vm/runtime/memprofiler.hpp new file mode 100644 index 00000000000..e6025f06423 --- /dev/null +++ b/hotspot/src/share/vm/runtime/memprofiler.hpp @@ -0,0 +1,42 @@ +/* + * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Prints periodic memory usage trace of HotSpot VM + +class MemProfilerTask; + +class MemProfiler : AllStatic { + friend class MemProfilerTask; + private: + static MemProfilerTask* _task; + static FILE* _log_fp; + // Do trace (callback from MemProfilerTask and from disengage) + static void do_trace() PRODUCT_RETURN; + public: + // Start/stop the profiler + static void engage() PRODUCT_RETURN; + static void disengage() PRODUCT_RETURN; + // Tester + static bool is_active() PRODUCT_RETURN0; +}; diff --git a/hotspot/src/share/vm/runtime/monitorChunk.cpp b/hotspot/src/share/vm/runtime/monitorChunk.cpp new file mode 100644 index 00000000000..dd9ddf40106 --- /dev/null +++ b/hotspot/src/share/vm/runtime/monitorChunk.cpp @@ -0,0 +1,44 @@ +/* + * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_monitorChunk.cpp.incl" + +MonitorChunk::MonitorChunk(int number_on_monitors) { + _number_of_monitors = number_on_monitors; + _monitors = NEW_C_HEAP_ARRAY(BasicObjectLock, number_on_monitors); + _next = NULL; +} + + +MonitorChunk::~MonitorChunk() { + FreeHeap(monitors()); +} + + +void MonitorChunk::oops_do(OopClosure* f) { + for (int index = 0; index < number_of_monitors(); index++) { + at(index)->oops_do(f); + } +} diff --git a/hotspot/src/share/vm/runtime/monitorChunk.hpp b/hotspot/src/share/vm/runtime/monitorChunk.hpp new file mode 100644 index 00000000000..5a7eef64ea8 --- /dev/null +++ b/hotspot/src/share/vm/runtime/monitorChunk.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Data structure for holding monitors for one activation during +// deoptimization. + +class MonitorChunk: public CHeapObj { + private: + int _number_of_monitors; + BasicObjectLock* _monitors; + BasicObjectLock* monitors() const { return _monitors; } + MonitorChunk* _next; + public: + // Constructor + MonitorChunk(int number_on_monitors); + ~MonitorChunk(); + + // link operations + MonitorChunk* next() const { return _next; } + void set_next(MonitorChunk* next) { _next = next; } + + // Tells whether the monitor chunk is linked into the JavaThread + bool is_linked() const { return next() != NULL; } + + // Returns the number of monitors + int number_of_monitors() const { return _number_of_monitors; } + + // Returns the index'th monitor + BasicObjectLock* at(int index) { assert(index >= 0 && index < number_of_monitors(), "out of bounds check"); return &monitors()[index]; } + + + // Memory management + void oops_do(OopClosure* f); + + // Tells whether the addr point into the monitors. + bool contains(void* addr) const { return (addr >= (void*) monitors()) && (addr < (void*) (monitors() + number_of_monitors())); } +}; diff --git a/hotspot/src/share/vm/runtime/mutex.cpp b/hotspot/src/share/vm/runtime/mutex.cpp new file mode 100644 index 00000000000..a4d341b9637 --- /dev/null +++ b/hotspot/src/share/vm/runtime/mutex.cpp @@ -0,0 +1,1356 @@ + +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_mutex.cpp.incl" + +// o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o +// +// Native Monitor-Mutex locking - theory of operations +// +// * Native Monitors are completely unrelated to Java-level monitors, +// although the "back-end" slow-path implementations share a common lineage. +// See objectMonitor:: in synchronizer.cpp. +// Native Monitors do *not* support nesting or recursion but otherwise +// they're basically Hoare-flavor monitors. +// +// * A thread acquires ownership of a Monitor/Mutex by CASing the LockByte +// in the _LockWord from zero to non-zero. Note that the _Owner field +// is advisory and is used only to verify that the thread calling unlock() +// is indeed the last thread to have acquired the lock. +// +// * Contending threads "push" themselves onto the front of the contention +// queue -- called the cxq -- with CAS and then spin/park. +// The _LockWord contains the LockByte as well as the pointer to the head +// of the cxq. Colocating the LockByte with the cxq precludes certain races. +// +// * Using a separately addressable LockByte allows for CAS:MEMBAR or CAS:0 +// idioms. We currently use MEMBAR in the uncontended unlock() path, as +// MEMBAR often has less latency than CAS. If warranted, we could switch to +// a CAS:0 mode, using timers to close the resultant race, as is done +// with Java Monitors in synchronizer.cpp. +// +// See the following for a discussion of the relative cost of atomics (CAS) +// MEMBAR, and ways to eliminate such instructions from the common-case paths: +// -- http://blogs.sun.com/dave/entry/biased_locking_in_hotspot +// -- http://blogs.sun.com/dave/resource/MustangSync.pdf +// -- http://blogs.sun.com/dave/resource/synchronization-public2.pdf +// -- synchronizer.cpp +// +// * Overall goals - desiderata +// 1. Minimize context switching +// 2. Minimize lock migration +// 3. Minimize CPI -- affinity and locality +// 4. Minimize the execution of high-latency instructions such as CAS or MEMBAR +// 5. Minimize outer lock hold times +// 6. Behave gracefully on a loaded system +// +// * Thread flow and list residency: +// +// Contention queue --> EntryList --> OnDeck --> Owner --> !Owner +// [..resident on monitor list..] +// [...........contending..................] +// +// -- The contention queue (cxq) contains recently-arrived threads (RATs). +// Threads on the cxq eventually drain into the EntryList. +// -- Invariant: a thread appears on at most one list -- cxq, EntryList +// or WaitSet -- at any one time. +// -- For a given monitor there can be at most one "OnDeck" thread at any +// given time but if needbe this particular invariant could be relaxed. +// +// * The WaitSet and EntryList linked lists are composed of ParkEvents. +// I use ParkEvent instead of threads as ParkEvents are immortal and +// type-stable, meaning we can safely unpark() a possibly stale +// list element in the unlock()-path. (That's benign). +// +// * Succession policy - providing for progress: +// +// As necessary, the unlock()ing thread identifies, unlinks, and unparks +// an "heir presumptive" tentative successor thread from the EntryList. +// This becomes the so-called "OnDeck" thread, of which there can be only +// one at any given time for a given monitor. The wakee will recontend +// for ownership of monitor. +// +// Succession is provided for by a policy of competitive handoff. +// The exiting thread does _not_ grant or pass ownership to the +// successor thread. (This is also referred to as "handoff" succession"). +// Instead the exiting thread releases ownership and possibly wakes +// a successor, so the successor can (re)compete for ownership of the lock. +// +// Competitive handoff provides excellent overall throughput at the expense +// of short-term fairness. If fairness is a concern then one remedy might +// be to add an AcquireCounter field to the monitor. After a thread acquires +// the lock it will decrement the AcquireCounter field. When the count +// reaches 0 the thread would reset the AcquireCounter variable, abdicate +// the lock directly to some thread on the EntryList, and then move itself to the +// tail of the EntryList. +// +// But in practice most threads engage or otherwise participate in resource +// bounded producer-consumer relationships, so lock domination is not usually +// a practical concern. Recall too, that in general it's easier to construct +// a fair lock from a fast lock, but not vice-versa. +// +// * The cxq can have multiple concurrent "pushers" but only one concurrent +// detaching thread. This mechanism is immune from the ABA corruption. +// More precisely, the CAS-based "push" onto cxq is ABA-oblivious. +// We use OnDeck as a pseudo-lock to enforce the at-most-one detaching +// thread constraint. +// +// * Taken together, the cxq and the EntryList constitute or form a +// single logical queue of threads stalled trying to acquire the lock. +// We use two distinct lists to reduce heat on the list ends. +// Threads in lock() enqueue onto cxq while threads in unlock() will +// dequeue from the EntryList. (c.f. Michael Scott's "2Q" algorithm). +// A key desideratum is to minimize queue & monitor metadata manipulation +// that occurs while holding the "outer" monitor lock -- that is, we want to +// minimize monitor lock holds times. +// +// The EntryList is ordered by the prevailing queue discipline and +// can be organized in any convenient fashion, such as a doubly-linked list or +// a circular doubly-linked list. If we need a priority queue then something akin +// to Solaris' sleepq would work nicely. Viz., +// -- http://agg.eng/ws/on10_nightly/source/usr/src/uts/common/os/sleepq.c. +// -- http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/os/sleepq.c +// Queue discipline is enforced at ::unlock() time, when the unlocking thread +// drains the cxq into the EntryList, and orders or reorders the threads on the +// EntryList accordingly. +// +// Barring "lock barging", this mechanism provides fair cyclic ordering, +// somewhat similar to an elevator-scan. +// +// * OnDeck +// -- For a given monitor there can be at most one OnDeck thread at any given +// instant. The OnDeck thread is contending for the lock, but has been +// unlinked from the EntryList and cxq by some previous unlock() operations. +// Once a thread has been designated the OnDeck thread it will remain so +// until it manages to acquire the lock -- being OnDeck is a stable property. +// -- Threads on the EntryList or cxq are _not allowed to attempt lock acquisition. +// -- OnDeck also serves as an "inner lock" as follows. Threads in unlock() will, after +// having cleared the LockByte and dropped the outer lock, attempt to "trylock" +// OnDeck by CASing the field from null to non-null. If successful, that thread +// is then responsible for progress and succession and can use CAS to detach and +// drain the cxq into the EntryList. By convention, only this thread, the holder of +// the OnDeck inner lock, can manipulate the EntryList or detach and drain the +// RATs on the cxq into the EntryList. This avoids ABA corruption on the cxq as +// we allow multiple concurrent "push" operations but restrict detach concurrency +// to at most one thread. Having selected and detached a successor, the thread then +// changes the OnDeck to refer to that successor, and then unparks the successor. +// That successor will eventually acquire the lock and clear OnDeck. Beware +// that the OnDeck usage as a lock is asymmetric. A thread in unlock() transiently +// "acquires" OnDeck, performs queue manipulations, passes OnDeck to some successor, +// and then the successor eventually "drops" OnDeck. Note that there's never +// any sense of contention on the inner lock, however. Threads never contend +// or wait for the inner lock. +// -- OnDeck provides for futile wakeup throttling a described in section 3.3 of +// See http://www.usenix.org/events/jvm01/full_papers/dice/dice.pdf +// In a sense, OnDeck subsumes the ObjectMonitor _Succ and ObjectWaiter +// TState fields found in Java-level objectMonitors. (See synchronizer.cpp). +// +// * Waiting threads reside on the WaitSet list -- wait() puts +// the caller onto the WaitSet. Notify() or notifyAll() simply +// transfers threads from the WaitSet to either the EntryList or cxq. +// Subsequent unlock() operations will eventually unpark the notifyee. +// Unparking a notifee in notify() proper is inefficient - if we were to do so +// it's likely the notifyee would simply impale itself on the lock held +// by the notifier. +// +// * The mechanism is obstruction-free in that if the holder of the transient +// OnDeck lock in unlock() is preempted or otherwise stalls, other threads +// can still acquire and release the outer lock and continue to make progress. +// At worst, waking of already blocked contending threads may be delayed, +// but nothing worse. (We only use "trylock" operations on the inner OnDeck +// lock). +// +// * Note that thread-local storage must be initialized before a thread +// uses Native monitors or mutexes. The native monitor-mutex subsystem +// depends on Thread::current(). +// +// * The monitor synchronization subsystem avoids the use of native +// synchronization primitives except for the narrow platform-specific +// park-unpark abstraction. See the comments in os_solaris.cpp regarding +// the semantics of park-unpark. Put another way, this monitor implementation +// depends only on atomic operations and park-unpark. The monitor subsystem +// manages all RUNNING->BLOCKED and BLOCKED->READY transitions while the +// underlying OS manages the READY<->RUN transitions. +// +// * The memory consistency model provide by lock()-unlock() is at least as +// strong or stronger than the Java Memory model defined by JSR-133. +// That is, we guarantee at least entry consistency, if not stronger. +// See http://g.oswego.edu/dl/jmm/cookbook.html. +// +// * Thread:: currently contains a set of purpose-specific ParkEvents: +// _MutexEvent, _ParkEvent, etc. A better approach might be to do away with +// the purpose-specific ParkEvents and instead implement a general per-thread +// stack of available ParkEvents which we could provision on-demand. The +// stack acts as a local cache to avoid excessive calls to ParkEvent::Allocate() +// and ::Release(). A thread would simply pop an element from the local stack before it +// enqueued or park()ed. When the contention was over the thread would +// push the no-longer-needed ParkEvent back onto its stack. +// +// * A slightly reduced form of ILock() and IUnlock() have been partially +// model-checked (Murphi) for safety and progress at T=1,2,3 and 4. +// It'd be interesting to see if TLA/TLC could be useful as well. +// +// * Mutex-Monitor is a low-level "leaf" subsystem. That is, the monitor +// code should never call other code in the JVM that might itself need to +// acquire monitors or mutexes. That's true *except* in the case of the +// ThreadBlockInVM state transition wrappers. The ThreadBlockInVM DTOR handles +// mutator reentry (ingress) by checking for a pending safepoint in which case it will +// call SafepointSynchronize::block(), which in turn may call Safepoint_lock->lock(), etc. +// In that particular case a call to lock() for a given Monitor can end up recursively +// calling lock() on another monitor. While distasteful, this is largely benign +// as the calls come from jacket that wraps lock(), and not from deep within lock() itself. +// +// It's unfortunate that native mutexes and thread state transitions were convolved. +// They're really separate concerns and should have remained that way. Melding +// them together was facile -- a bit too facile. The current implementation badly +// conflates the two concerns. +// +// * TODO-FIXME: +// +// -- Add DTRACE probes for contended acquire, contended acquired, contended unlock +// We should also add DTRACE probes in the ParkEvent subsystem for +// Park-entry, Park-exit, and Unpark. +// +// -- We have an excess of mutex-like constructs in the JVM, namely: +// 1. objectMonitors for Java-level synchronization (synchronizer.cpp) +// 2. low-level muxAcquire and muxRelease +// 3. low-level spinAcquire and spinRelease +// 4. native Mutex:: and Monitor:: +// 5. jvm_raw_lock() and _unlock() +// 6. JVMTI raw monitors -- distinct from (5) despite having a confusingly +// similar name. +// +// o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o + + +// CASPTR() uses the canonical argument order that dominates in the literature. +// Our internal cmpxchg_ptr() uses a bastardized ordering to accommodate Sun .il templates. + +#define CASPTR(a,c,s) intptr_t(Atomic::cmpxchg_ptr ((void *)(s),(void *)(a),(void *)(c))) +#define UNS(x) (uintptr_t(x)) +#define TRACE(m) { static volatile int ctr = 0 ; int x = ++ctr ; if ((x & (x-1))==0) { ::printf ("%d:%s\n", x, #m); ::fflush(stdout); }} + +// Simplistic low-quality Marsaglia SHIFT-XOR RNG. +// Bijective except for the trailing mask operation. +// Useful for spin loops as the compiler can't optimize it away. + +static inline jint MarsagliaXORV (jint x) { + if (x == 0) x = 1|os::random() ; + x ^= x << 6; + x ^= ((unsigned)x) >> 21; + x ^= x << 7 ; + return x & 0x7FFFFFFF ; +} + +static inline jint MarsagliaXOR (jint * const a) { + jint x = *a ; + if (x == 0) x = UNS(a)|1 ; + x ^= x << 6; + x ^= ((unsigned)x) >> 21; + x ^= x << 7 ; + *a = x ; + return x & 0x7FFFFFFF ; +} + +static int Stall (int its) { + static volatile jint rv = 1 ; + volatile int OnFrame = 0 ; + jint v = rv ^ UNS(OnFrame) ; + while (--its >= 0) { + v = MarsagliaXORV (v) ; + } + // Make this impossible for the compiler to optimize away, + // but (mostly) avoid W coherency sharing on MP systems. + if (v == 0x12345) rv = v ; + return v ; +} + +int Monitor::TryLock () { + intptr_t v = _LockWord.FullWord ; + for (;;) { + if ((v & _LBIT) != 0) return 0 ; + const intptr_t u = CASPTR (&_LockWord, v, v|_LBIT) ; + if (v == u) return 1 ; + v = u ; + } +} + +int Monitor::TryFast () { + // Optimistic fast-path form ... + // Fast-path attempt for the common uncontended case. + // Avoid RTS->RTO $ coherence upgrade on typical SMP systems. + intptr_t v = CASPTR (&_LockWord, 0, _LBIT) ; // agro ... + if (v == 0) return 1 ; + + for (;;) { + if ((v & _LBIT) != 0) return 0 ; + const intptr_t u = CASPTR (&_LockWord, v, v|_LBIT) ; + if (v == u) return 1 ; + v = u ; + } +} + +int Monitor::ILocked () { + const intptr_t w = _LockWord.FullWord & 0xFF ; + assert (w == 0 || w == _LBIT, "invariant") ; + return w == _LBIT ; +} + +// Polite TATAS spinlock with exponential backoff - bounded spin. +// Ideally we'd use processor cycles, time or vtime to control +// the loop, but we currently use iterations. +// All the constants within were derived empirically but work over +// over the spectrum of J2SE reference platforms. +// On Niagara-class systems the back-off is unnecessary but +// is relatively harmless. (At worst it'll slightly retard +// acquisition times). The back-off is critical for older SMP systems +// where constant fetching of the LockWord would otherwise impair +// scalability. +// +// Clamp spinning at approximately 1/2 of a context-switch round-trip. +// See synchronizer.cpp for details and rationale. + +int Monitor::TrySpin (Thread * const Self) { + if (TryLock()) return 1 ; + if (!os::is_MP()) return 0 ; + + int Probes = 0 ; + int Delay = 0 ; + int Steps = 0 ; + int SpinMax = NativeMonitorSpinLimit ; + int flgs = NativeMonitorFlags ; + for (;;) { + intptr_t v = _LockWord.FullWord; + if ((v & _LBIT) == 0) { + if (CASPTR (&_LockWord, v, v|_LBIT) == v) { + return 1 ; + } + continue ; + } + + if ((flgs & 8) == 0) { + SpinPause () ; + } + + // Periodically increase Delay -- variable Delay form + // conceptually: delay *= 1 + 1/Exponent + ++ Probes; + if (Probes > SpinMax) return 0 ; + + if ((Probes & 0x7) == 0) { + Delay = ((Delay << 1)|1) & 0x7FF ; + // CONSIDER: Delay += 1 + (Delay/4); Delay &= 0x7FF ; + } + + if (flgs & 2) continue ; + + // Consider checking _owner's schedctl state, if OFFPROC abort spin. + // If the owner is OFFPROC then it's unlike that the lock will be dropped + // in a timely fashion, which suggests that spinning would not be fruitful + // or profitable. + + // Stall for "Delay" time units - iterations in the current implementation. + // Avoid generating coherency traffic while stalled. + // Possible ways to delay: + // PAUSE, SLEEP, MEMBAR #sync, MEMBAR #halt, + // wr %g0,%asi, gethrtime, rdstick, rdtick, rdtsc, etc. ... + // Note that on Niagara-class systems we want to minimize STs in the + // spin loop. N1 and brethren write-around the L1$ over the xbar into the L2$. + // Furthermore, they don't have a W$ like traditional SPARC processors. + // We currently use a Marsaglia Shift-Xor RNG loop. + Steps += Delay ; + if (Self != NULL) { + jint rv = Self->rng[0] ; + for (int k = Delay ; --k >= 0; ) { + rv = MarsagliaXORV (rv) ; + if ((flgs & 4) == 0 && SafepointSynchronize::do_call_back()) return 0 ; + } + Self->rng[0] = rv ; + } else { + Stall (Delay) ; + } + } +} + +static int ParkCommon (ParkEvent * ev, jlong timo) { + // Diagnostic support - periodically unwedge blocked threads + intx nmt = NativeMonitorTimeout ; + if (nmt > 0 && (nmt < timo || timo <= 0)) { + timo = nmt ; + } + int err = OS_OK ; + if (0 == timo) { + ev->park() ; + } else { + err = ev->park(timo) ; + } + return err ; +} + +inline int Monitor::AcquireOrPush (ParkEvent * ESelf) { + intptr_t v = _LockWord.FullWord ; + for (;;) { + if ((v & _LBIT) == 0) { + const intptr_t u = CASPTR (&_LockWord, v, v|_LBIT) ; + if (u == v) return 1 ; // indicate acquired + v = u ; + } else { + // Anticipate success ... + ESelf->ListNext = (ParkEvent *) (v & ~_LBIT) ; + const intptr_t u = CASPTR (&_LockWord, v, intptr_t(ESelf)|_LBIT) ; + if (u == v) return 0 ; // indicate pushed onto cxq + v = u ; + } + // Interference - LockWord change - just retry + } +} + +// ILock and IWait are the lowest level primitive internal blocking +// synchronization functions. The callers of IWait and ILock must have +// performed any needed state transitions beforehand. +// IWait and ILock may directly call park() without any concern for thread state. +// Note that ILock and IWait do *not* access _owner. +// _owner is a higher-level logical concept. + +void Monitor::ILock (Thread * Self) { + assert (_OnDeck != Self->_MutexEvent, "invariant") ; + + if (TryFast()) { + Exeunt: + assert (ILocked(), "invariant") ; + return ; + } + + ParkEvent * const ESelf = Self->_MutexEvent ; + assert (_OnDeck != ESelf, "invariant") ; + + // As an optimization, spinners could conditionally try to set ONDECK to _LBIT + // Synchronizer.cpp uses a similar optimization. + if (TrySpin (Self)) goto Exeunt ; + + // Slow-path - the lock is contended. + // Either Enqueue Self on cxq or acquire the outer lock. + // LockWord encoding = (cxq,LOCKBYTE) + ESelf->reset() ; + OrderAccess::fence() ; + + // Optional optimization ... try barging on the inner lock + if ((NativeMonitorFlags & 32) && CASPTR (&_OnDeck, NULL, UNS(Self)) == 0) { + goto OnDeck_LOOP ; + } + + if (AcquireOrPush (ESelf)) goto Exeunt ; + + // At any given time there is at most one ondeck thread. + // ondeck implies not resident on cxq and not resident on EntryList + // Only the OnDeck thread can try to acquire -- contended for -- the lock. + // CONSIDER: use Self->OnDeck instead of m->OnDeck. + // Deschedule Self so that others may run. + while (_OnDeck != ESelf) { + ParkCommon (ESelf, 0) ; + } + + // Self is now in the ONDECK position and will remain so until it + // manages to acquire the lock. + OnDeck_LOOP: + for (;;) { + assert (_OnDeck == ESelf, "invariant") ; + if (TrySpin (Self)) break ; + // CONSIDER: if ESelf->TryPark() && TryLock() break ... + // It's probably wise to spin only if we *actually* blocked + // CONSIDER: check the lockbyte, if it remains set then + // preemptively drain the cxq into the EntryList. + // The best place and time to perform queue operations -- lock metadata -- + // is _before having acquired the outer lock, while waiting for the lock to drop. + ParkCommon (ESelf, 0) ; + } + + assert (_OnDeck == ESelf, "invariant") ; + _OnDeck = NULL ; + + // Note that we current drop the inner lock (clear OnDeck) in the slow-path + // epilog immediately after having acquired the outer lock. + // But instead we could consider the following optimizations: + // A. Shift or defer dropping the inner lock until the subsequent IUnlock() operation. + // This might avoid potential reacquisition of the inner lock in IUlock(). + // B. While still holding the inner lock, attempt to opportunistically select + // and unlink the next ONDECK thread from the EntryList. + // If successful, set ONDECK to refer to that thread, otherwise clear ONDECK. + // It's critical that the select-and-unlink operation run in constant-time as + // it executes when holding the outer lock and may artificially increase the + // effective length of the critical section. + // Note that (A) and (B) are tantamount to succession by direct handoff for + // the inner lock. + goto Exeunt ; +} + +void Monitor::IUnlock (bool RelaxAssert) { + assert (ILocked(), "invariant") ; + _LockWord.Bytes[_LSBINDEX] = 0 ; // drop outer lock + OrderAccess::storeload (); + ParkEvent * const w = _OnDeck ; + assert (RelaxAssert || w != Thread::current()->_MutexEvent, "invariant") ; + if (w != NULL) { + // Either we have a valid ondeck thread or ondeck is transiently "locked" + // by some exiting thread as it arranges for succession. The LSBit of + // OnDeck allows us to discriminate two cases. If the latter, the + // responsibility for progress and succession lies with that other thread. + // For good performance, we also depend on the fact that redundant unpark() + // operations are cheap. That is, repeated Unpark()ing of the ONDECK thread + // is inexpensive. This approach provides implicit futile wakeup throttling. + // Note that the referent "w" might be stale with respect to the lock. + // In that case the following unpark() is harmless and the worst that'll happen + // is a spurious return from a park() operation. Critically, if "w" _is stale, + // then progress is known to have occurred as that means the thread associated + // with "w" acquired the lock. In that case this thread need take no further + // action to guarantee progress. + if ((UNS(w) & _LBIT) == 0) w->unpark() ; + return ; + } + + intptr_t cxq = _LockWord.FullWord ; + if (((cxq & ~_LBIT)|UNS(_EntryList)) == 0) { + return ; // normal fast-path exit - cxq and EntryList both empty + } + if (cxq & _LBIT) { + // Optional optimization ... + // Some other thread acquired the lock in the window since this + // thread released it. Succession is now that thread's responsibility. + return ; + } + + Succession: + // Slow-path exit - this thread must ensure succession and progress. + // OnDeck serves as lock to protect cxq and EntryList. + // Only the holder of OnDeck can manipulate EntryList or detach the RATs from cxq. + // Avoid ABA - allow multiple concurrent producers (enqueue via push-CAS) + // but only one concurrent consumer (detacher of RATs). + // Consider protecting this critical section with schedctl on Solaris. + // Unlike a normal lock, however, the exiting thread "locks" OnDeck, + // picks a successor and marks that thread as OnDeck. That successor + // thread will then clear OnDeck once it eventually acquires the outer lock. + if (CASPTR (&_OnDeck, NULL, _LBIT) != UNS(NULL)) { + return ; + } + + ParkEvent * List = _EntryList ; + if (List != NULL) { + // Transfer the head of the EntryList to the OnDeck position. + // Once OnDeck, a thread stays OnDeck until it acquires the lock. + // For a given lock there is at most OnDeck thread at any one instant. + WakeOne: + assert (List == _EntryList, "invariant") ; + ParkEvent * const w = List ; + assert (RelaxAssert || w != Thread::current()->_MutexEvent, "invariant") ; + _EntryList = w->ListNext ; + // as a diagnostic measure consider setting w->_ListNext = BAD + assert (UNS(_OnDeck) == _LBIT, "invariant") ; + _OnDeck = w ; // pass OnDeck to w. + // w will clear OnDeck once it acquires the outer lock + + // Another optional optimization ... + // For heavily contended locks it's not uncommon that some other + // thread acquired the lock while this thread was arranging succession. + // Try to defer the unpark() operation - Delegate the responsibility + // for unpark()ing the OnDeck thread to the current or subsequent owners + // That is, the new owner is responsible for unparking the OnDeck thread. + OrderAccess::storeload() ; + cxq = _LockWord.FullWord ; + if (cxq & _LBIT) return ; + + w->unpark() ; + return ; + } + + cxq = _LockWord.FullWord ; + if ((cxq & ~_LBIT) != 0) { + // The EntryList is empty but the cxq is populated. + // drain RATs from cxq into EntryList + // Detach RATs segment with CAS and then merge into EntryList + for (;;) { + // optional optimization - if locked, the owner is responsible for succession + if (cxq & _LBIT) goto Punt ; + const intptr_t vfy = CASPTR (&_LockWord, cxq, cxq & _LBIT) ; + if (vfy == cxq) break ; + cxq = vfy ; + // Interference - LockWord changed - Just retry + // We can see concurrent interference from contending threads + // pushing themselves onto the cxq or from lock-unlock operations. + // From the perspective of this thread, EntryList is stable and + // the cxq is prepend-only -- the head is volatile but the interior + // of the cxq is stable. In theory if we encounter interference from threads + // pushing onto cxq we could simply break off the original cxq suffix and + // move that segment to the EntryList, avoiding a 2nd or multiple CAS attempts + // on the high-traffic LockWord variable. For instance lets say the cxq is "ABCD" + // when we first fetch cxq above. Between the fetch -- where we observed "A" + // -- and CAS -- where we attempt to CAS null over A -- "PQR" arrive, + // yielding cxq = "PQRABCD". In this case we could simply set A.ListNext + // null, leaving cxq = "PQRA" and transfer the "BCD" segment to the EntryList. + // Note too, that it's safe for this thread to traverse the cxq + // without taking any special concurrency precautions. + } + + // We don't currently reorder the cxq segment as we move it onto + // the EntryList, but it might make sense to reverse the order + // or perhaps sort by thread priority. See the comments in + // synchronizer.cpp objectMonitor::exit(). + assert (_EntryList == NULL, "invariant") ; + _EntryList = List = (ParkEvent *)(cxq & ~_LBIT) ; + assert (List != NULL, "invariant") ; + goto WakeOne ; + } + + // cxq|EntryList is empty. + // w == NULL implies that cxq|EntryList == NULL in the past. + // Possible race - rare inopportune interleaving. + // A thread could have added itself to cxq since this thread previously checked. + // Detect and recover by refetching cxq. + Punt: + assert (UNS(_OnDeck) == _LBIT, "invariant") ; + _OnDeck = NULL ; // Release inner lock. + OrderAccess::storeload(); // Dekker duality - pivot point + + // Resample LockWord/cxq to recover from possible race. + // For instance, while this thread T1 held OnDeck, some other thread T2 might + // acquire the outer lock. Another thread T3 might try to acquire the outer + // lock, but encounter contention and enqueue itself on cxq. T2 then drops the + // outer lock, but skips succession as this thread T1 still holds OnDeck. + // T1 is and remains responsible for ensuring succession of T3. + // + // Note that we don't need to recheck EntryList, just cxq. + // If threads moved onto EntryList since we dropped OnDeck + // that implies some other thread forced succession. + cxq = _LockWord.FullWord ; + if ((cxq & ~_LBIT) != 0 && (cxq & _LBIT) == 0) { + goto Succession ; // potential race -- re-run succession + } + return ; +} + +bool Monitor::notify() { + assert (_owner == Thread::current(), "invariant") ; + assert (ILocked(), "invariant") ; + if (_WaitSet == NULL) return true ; + NotifyCount ++ ; + + // Transfer one thread from the WaitSet to the EntryList or cxq. + // Currently we just unlink the head of the WaitSet and prepend to the cxq. + // And of course we could just unlink it and unpark it, too, but + // in that case it'd likely impale itself on the reentry. + Thread::muxAcquire (_WaitLock, "notify:WaitLock") ; + ParkEvent * nfy = _WaitSet ; + if (nfy != NULL) { // DCL idiom + _WaitSet = nfy->ListNext ; + assert (nfy->Notified == 0, "invariant") ; + // push nfy onto the cxq + for (;;) { + const intptr_t v = _LockWord.FullWord ; + assert ((v & 0xFF) == _LBIT, "invariant") ; + nfy->ListNext = (ParkEvent *)(v & ~_LBIT); + if (CASPTR (&_LockWord, v, UNS(nfy)|_LBIT) == v) break; + // interference - _LockWord changed -- just retry + } + // Note that setting Notified before pushing nfy onto the cxq is + // also legal and safe, but the safety properties are much more + // subtle, so for the sake of code stewardship ... + OrderAccess::fence() ; + nfy->Notified = 1; + } + Thread::muxRelease (_WaitLock) ; + if (nfy != NULL && (NativeMonitorFlags & 16)) { + // Experimental code ... light up the wakee in the hope that this thread (the owner) + // will drop the lock just about the time the wakee comes ONPROC. + nfy->unpark() ; + } + assert (ILocked(), "invariant") ; + return true ; +} + +// Currently notifyAll() transfers the waiters one-at-a-time from the waitset +// to the cxq. This could be done more efficiently with a single bulk en-mass transfer, +// but in practice notifyAll() for large #s of threads is rare and not time-critical. +// Beware too, that we invert the order of the waiters. Lets say that the +// waitset is "ABCD" and the cxq is "XYZ". After a notifyAll() the waitset +// will be empty and the cxq will be "DCBAXYZ". This is benign, of course. + +bool Monitor::notify_all() { + assert (_owner == Thread::current(), "invariant") ; + assert (ILocked(), "invariant") ; + while (_WaitSet != NULL) notify() ; + return true ; +} + +int Monitor::IWait (Thread * Self, jlong timo) { + assert (ILocked(), "invariant") ; + + // Phases: + // 1. Enqueue Self on WaitSet - currently prepend + // 2. unlock - drop the outer lock + // 3. wait for either notification or timeout + // 4. lock - reentry - reacquire the outer lock + + ParkEvent * const ESelf = Self->_MutexEvent ; + ESelf->Notified = 0 ; + ESelf->reset() ; + OrderAccess::fence() ; + + // Add Self to WaitSet + // Ideally only the holder of the outer lock would manipulate the WaitSet - + // That is, the outer lock would implicitly protect the WaitSet. + // But if a thread in wait() encounters a timeout it will need to dequeue itself + // from the WaitSet _before it becomes the owner of the lock. We need to dequeue + // as the ParkEvent -- which serves as a proxy for the thread -- can't reside + // on both the WaitSet and the EntryList|cxq at the same time.. That is, a thread + // on the WaitSet can't be allowed to compete for the lock until it has managed to + // unlink its ParkEvent from WaitSet. Thus the need for WaitLock. + // Contention on the WaitLock is minimal. + // + // Another viable approach would be add another ParkEvent, "WaitEvent" to the + // thread class. The WaitSet would be composed of WaitEvents. Only the + // owner of the outer lock would manipulate the WaitSet. A thread in wait() + // could then compete for the outer lock, and then, if necessary, unlink itself + // from the WaitSet only after having acquired the outer lock. More precisely, + // there would be no WaitLock. A thread in in wait() would enqueue its WaitEvent + // on the WaitSet; release the outer lock; wait for either notification or timeout; + // reacquire the inner lock; and then, if needed, unlink itself from the WaitSet. + // + // Alternatively, a 2nd set of list link fields in the ParkEvent might suffice. + // One set would be for the WaitSet and one for the EntryList. + // We could also deconstruct the ParkEvent into a "pure" event and add a + // new immortal/TSM "ListElement" class that referred to ParkEvents. + // In that case we could have one ListElement on the WaitSet and another + // on the EntryList, with both referring to the same pure Event. + + Thread::muxAcquire (_WaitLock, "wait:WaitLock:Add") ; + ESelf->ListNext = _WaitSet ; + _WaitSet = ESelf ; + Thread::muxRelease (_WaitLock) ; + + // Release the outer lock + // We call IUnlock (RelaxAssert=true) as a thread T1 might + // enqueue itself on the WaitSet, call IUnlock(), drop the lock, + // and then stall before it can attempt to wake a successor. + // Some other thread T2 acquires the lock, and calls notify(), moving + // T1 from the WaitSet to the cxq. T2 then drops the lock. T1 resumes, + // and then finds *itself* on the cxq. During the course of a normal + // IUnlock() call a thread should _never find itself on the EntryList + // or cxq, but in the case of wait() it's possible. + // See synchronizer.cpp objectMonitor::wait(). + IUnlock (true) ; + + // Wait for either notification or timeout + // Beware that in some circumstances we might propagate + // spurious wakeups back to the caller. + + for (;;) { + if (ESelf->Notified) break ; + int err = ParkCommon (ESelf, timo) ; + if (err == OS_TIMEOUT || (NativeMonitorFlags & 1)) break ; + } + + // Prepare for reentry - if necessary, remove ESelf from WaitSet + // ESelf can be: + // 1. Still on the WaitSet. This can happen if we exited the loop by timeout. + // 2. On the cxq or EntryList + // 3. Not resident on cxq, EntryList or WaitSet, but in the OnDeck position. + + OrderAccess::fence() ; + int WasOnWaitSet = 0 ; + if (ESelf->Notified == 0) { + Thread::muxAcquire (_WaitLock, "wait:WaitLock:remove") ; + if (ESelf->Notified == 0) { // DCL idiom + assert (_OnDeck != ESelf, "invariant") ; // can't be both OnDeck and on WaitSet + // ESelf is resident on the WaitSet -- unlink it. + // A doubly-linked list would be better here so we can unlink in constant-time. + // We have to unlink before we potentially recontend as ESelf might otherwise + // end up on the cxq|EntryList -- it can't be on two lists at once. + ParkEvent * p = _WaitSet ; + ParkEvent * q = NULL ; // classic q chases p + while (p != NULL && p != ESelf) { + q = p ; + p = p->ListNext ; + } + assert (p == ESelf, "invariant") ; + if (p == _WaitSet) { // found at head + assert (q == NULL, "invariant") ; + _WaitSet = p->ListNext ; + } else { // found in interior + assert (q->ListNext == p, "invariant") ; + q->ListNext = p->ListNext ; + } + WasOnWaitSet = 1 ; // We were *not* notified but instead encountered timeout + } + Thread::muxRelease (_WaitLock) ; + } + + // Reentry phase - reacquire the lock + if (WasOnWaitSet) { + // ESelf was previously on the WaitSet but we just unlinked it above + // because of a timeout. ESelf is not resident on any list and is not OnDeck + assert (_OnDeck != ESelf, "invariant") ; + ILock (Self) ; + } else { + // A prior notify() operation moved ESelf from the WaitSet to the cxq. + // ESelf is now on the cxq, EntryList or at the OnDeck position. + // The following fragment is extracted from Monitor::ILock() + for (;;) { + if (_OnDeck == ESelf && TrySpin(Self)) break ; + ParkCommon (ESelf, 0) ; + } + assert (_OnDeck == ESelf, "invariant") ; + _OnDeck = NULL ; + } + + assert (ILocked(), "invariant") ; + return WasOnWaitSet != 0 ; // return true IFF timeout +} + + +// ON THE VMTHREAD SNEAKING PAST HELD LOCKS: +// In particular, there are certain types of global lock that may be held +// by a Java thread while it is blocked at a safepoint but before it has +// written the _owner field. These locks may be sneakily acquired by the +// VM thread during a safepoint to avoid deadlocks. Alternatively, one should +// identify all such locks, and ensure that Java threads never block at +// safepoints while holding them (_no_safepoint_check_flag). While it +// seems as though this could increase the time to reach a safepoint +// (or at least increase the mean, if not the variance), the latter +// approach might make for a cleaner, more maintainable JVM design. +// +// Sneaking is vile and reprehensible and should be excised at the 1st +// opportunity. It's possible that the need for sneaking could be obviated +// as follows. Currently, a thread might (a) while TBIVM, call pthread_mutex_lock +// or ILock() thus acquiring the "physical" lock underlying Monitor/Mutex. +// (b) stall at the TBIVM exit point as a safepoint is in effect. Critically, +// it'll stall at the TBIVM reentry state transition after having acquired the +// underlying lock, but before having set _owner and having entered the actual +// critical section. The lock-sneaking facility leverages that fact and allowed the +// VM thread to logically acquire locks that had already be physically locked by mutators +// but where mutators were known blocked by the reentry thread state transition. +// +// If we were to modify the Monitor-Mutex so that TBIVM state transitions tightly +// wrapped calls to park(), then we could likely do away with sneaking. We'd +// decouple lock acquisition and parking. The critical invariant to eliminating +// sneaking is to ensure that we never "physically" acquire the lock while TBIVM. +// An easy way to accomplish this is to wrap the park calls in a narrow TBIVM jacket. +// One difficulty with this approach is that the TBIVM wrapper could recurse and +// call lock() deep from within a lock() call, while the MutexEvent was already enqueued. +// Using a stack (N=2 at minimum) of ParkEvents would take care of that problem. +// +// But of course the proper ultimate approach is to avoid schemes that require explicit +// sneaking or dependence on any any clever invariants or subtle implementation properties +// of Mutex-Monitor and instead directly address the underlying design flaw. + +void Monitor::lock (Thread * Self) { +#ifdef CHECK_UNHANDLED_OOPS + // Clear unhandled oops so we get a crash right away. Only clear for non-vm + // or GC threads. + if (Self->is_Java_thread()) { + Self->clear_unhandled_oops(); + } +#endif // CHECK_UNHANDLED_OOPS + + debug_only(check_prelock_state(Self)); + assert (_owner != Self , "invariant") ; + assert (_OnDeck != Self->_MutexEvent, "invariant") ; + + if (TryFast()) { + Exeunt: + assert (ILocked(), "invariant") ; + assert (owner() == NULL, "invariant"); + set_owner (Self); + return ; + } + + // The lock is contended ... + + bool can_sneak = Self->is_VM_thread() && SafepointSynchronize::is_at_safepoint(); + if (can_sneak && _owner == NULL) { + // a java thread has locked the lock but has not entered the + // critical region -- let's just pretend we've locked the lock + // and go on. we note this with _snuck so we can also + // pretend to unlock when the time comes. + _snuck = true; + goto Exeunt ; + } + + // Try a brief spin to avoid passing thru thread state transition ... + if (TrySpin (Self)) goto Exeunt ; + + check_block_state(Self); + if (Self->is_Java_thread()) { + // Horribile dictu - we suffer through a state transition + assert(rank() > Mutex::special, "Potential deadlock with special or lesser rank mutex"); + ThreadBlockInVM tbivm ((JavaThread *) Self) ; + ILock (Self) ; + } else { + // Mirabile dictu + ILock (Self) ; + } + goto Exeunt ; +} + +void Monitor::lock() { + this->lock(Thread::current()); +} + +// Lock without safepoint check - a degenerate variant of lock(). +// Should ONLY be used by safepoint code and other code +// that is guaranteed not to block while running inside the VM. If this is called with +// thread state set to be in VM, the safepoint synchronization code will deadlock! + +void Monitor::lock_without_safepoint_check (Thread * Self) { + assert (_owner != Self, "invariant") ; + ILock (Self) ; + assert (_owner == NULL, "invariant"); + set_owner (Self); +} + +void Monitor::lock_without_safepoint_check () { + lock_without_safepoint_check (Thread::current()) ; +} + + +// Returns true if thread succeceed [sic] in grabbing the lock, otherwise false. + +bool Monitor::try_lock() { + Thread * const Self = Thread::current(); + debug_only(check_prelock_state(Self)); + // assert(!thread->is_inside_signal_handler(), "don't lock inside signal handler"); + + // Special case, where all Java threads are stopped. + // The lock may have been acquired but _owner is not yet set. + // In that case the VM thread can safely grab the lock. + // It strikes me this should appear _after the TryLock() fails, below. + bool can_sneak = Self->is_VM_thread() && SafepointSynchronize::is_at_safepoint(); + if (can_sneak && _owner == NULL) { + set_owner(Self); // Do not need to be atomic, since we are at a safepoint + _snuck = true; + return true; + } + + if (TryLock()) { + // We got the lock + assert (_owner == NULL, "invariant"); + set_owner (Self); + return true; + } + return false; +} + +void Monitor::unlock() { + assert (_owner == Thread::current(), "invariant") ; + assert (_OnDeck != Thread::current()->_MutexEvent , "invariant") ; + set_owner (NULL) ; + if (_snuck) { + assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread(), "sneak"); + _snuck = false; + return ; + } + IUnlock (false) ; +} + +// Yet another degenerate version of Monitor::lock() or lock_without_safepoint_check() +// jvm_raw_lock() and _unlock() can be called by non-Java threads via JVM_RawMonitorEnter. +// +// There's no expectation that JVM_RawMonitors will interoperate properly with the native +// Mutex-Monitor constructs. We happen to implement JVM_RawMonitors in terms of +// native Mutex-Monitors simply as a matter of convenience. A simple abstraction layer +// over a pthread_mutex_t would work equally as well, but require more platform-specific +// code -- a "PlatformMutex". Alternatively, a simply layer over muxAcquire-muxRelease +// would work too. +// +// Since the caller might be a foreign thread, we don't necessarily have a Thread.MutexEvent +// instance available. Instead, we transiently allocate a ParkEvent on-demand if +// we encounter contention. That ParkEvent remains associated with the thread +// until it manages to acquire the lock, at which time we return the ParkEvent +// to the global ParkEvent free list. This is correct and suffices for our purposes. +// +// Beware that the original jvm_raw_unlock() had a "_snuck" test but that +// jvm_raw_lock() didn't have the corresponding test. I suspect that's an +// oversight, but I've replicated the original suspect logic in the new code ... + +void Monitor::jvm_raw_lock() { + assert(rank() == native, "invariant"); + + if (TryLock()) { + Exeunt: + assert (ILocked(), "invariant") ; + assert (_owner == NULL, "invariant"); + // This can potentially be called by non-java Threads. Thus, the ThreadLocalStorage + // might return NULL. Don't call set_owner since it will break on an NULL owner + // Consider installing a non-null "ANON" distinguished value instead of just NULL. + _owner = ThreadLocalStorage::thread(); + return ; + } + + if (TrySpin(NULL)) goto Exeunt ; + + // slow-path - apparent contention + // Allocate a ParkEvent for transient use. + // The ParkEvent remains associated with this thread until + // the time the thread manages to acquire the lock. + ParkEvent * const ESelf = ParkEvent::Allocate(NULL) ; + ESelf->reset() ; + OrderAccess::storeload() ; + + // Either Enqueue Self on cxq or acquire the outer lock. + if (AcquireOrPush (ESelf)) { + ParkEvent::Release (ESelf) ; // surrender the ParkEvent + goto Exeunt ; + } + + // At any given time there is at most one ondeck thread. + // ondeck implies not resident on cxq and not resident on EntryList + // Only the OnDeck thread can try to acquire -- contended for -- the lock. + // CONSIDER: use Self->OnDeck instead of m->OnDeck. + for (;;) { + if (_OnDeck == ESelf && TrySpin(NULL)) break ; + ParkCommon (ESelf, 0) ; + } + + assert (_OnDeck == ESelf, "invariant") ; + _OnDeck = NULL ; + ParkEvent::Release (ESelf) ; // surrender the ParkEvent + goto Exeunt ; +} + +void Monitor::jvm_raw_unlock() { + // Nearly the same as Monitor::unlock() ... + // directly set _owner instead of using set_owner(null) + _owner = NULL ; + if (_snuck) { // ??? + assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread(), "sneak"); + _snuck = false; + return ; + } + IUnlock(false) ; +} + +bool Monitor::wait(bool no_safepoint_check, long timeout, bool as_suspend_equivalent) { + Thread * const Self = Thread::current() ; + assert (_owner == Self, "invariant") ; + assert (ILocked(), "invariant") ; + + // as_suspend_equivalent logically implies !no_safepoint_check + guarantee (!as_suspend_equivalent || !no_safepoint_check, "invariant") ; + // !no_safepoint_check logically implies java_thread + guarantee (no_safepoint_check || Self->is_Java_thread(), "invariant") ; + + #ifdef ASSERT + Monitor * least = get_least_ranked_lock_besides_this(Self->owned_locks()); + assert(least != this, "Specification of get_least_... call above"); + if (least != NULL && least->rank() <= special) { + tty->print("Attempting to wait on monitor %s/%d while holding" + " lock %s/%d -- possible deadlock", + name(), rank(), least->name(), least->rank()); + assert(false, "Shouldn't block(wait) while holding a lock of rank special"); + } + #endif // ASSERT + + int wait_status ; + // conceptually set the owner to NULL in anticipation of + // abdicating the lock in wait + set_owner(NULL); + if (no_safepoint_check) { + wait_status = IWait (Self, timeout) ; + } else { + assert (Self->is_Java_thread(), "invariant") ; + JavaThread *jt = (JavaThread *)Self; + + // Enter safepoint region - ornate and Rococo ... + ThreadBlockInVM tbivm(jt); + OSThreadWaitState osts(Self->osthread(), false /* not Object.wait() */); + + if (as_suspend_equivalent) { + jt->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() + } + + wait_status = IWait (Self, timeout) ; + + // were we externally suspended while we were waiting? + if (as_suspend_equivalent && jt->handle_special_suspend_equivalent_condition()) { + // Our event wait has finished and we own the lock, but + // while we were waiting another thread suspended us. We don't + // want to hold the lock while suspended because that + // would surprise the thread that suspended us. + assert (ILocked(), "invariant") ; + IUnlock (true) ; + jt->java_suspend_self(); + ILock (Self) ; + assert (ILocked(), "invariant") ; + } + } + + // Conceptually reestablish ownership of the lock. + // The "real" lock -- the LockByte -- was reacquired by IWait(). + assert (ILocked(), "invariant") ; + assert (_owner == NULL, "invariant") ; + set_owner (Self) ; + return wait_status != 0 ; // return true IFF timeout +} + +Monitor::~Monitor() { + assert ((UNS(_owner)|UNS(_LockWord.FullWord)|UNS(_EntryList)|UNS(_WaitSet)|UNS(_OnDeck)) == 0, "") ; +} + +void Monitor::ClearMonitor (Monitor * m) { + m->_owner = NULL ; + m->_snuck = false ; + m->_name = "UNKNOWN" ; + m->_LockWord.FullWord = 0 ; + m->_EntryList = NULL ; + m->_OnDeck = NULL ; + m->_WaitSet = NULL ; + m->_WaitLock[0] = 0 ; +} + +Monitor::Monitor() { ClearMonitor(this); } + +Monitor::Monitor (int Rank, const char * name, bool allow_vm_block) { + ClearMonitor (this) ; +#ifdef ASSERT + _allow_vm_block = allow_vm_block; + _rank = Rank ; +#endif +} + +Mutex::~Mutex() { + assert ((UNS(_owner)|UNS(_LockWord.FullWord)|UNS(_EntryList)|UNS(_WaitSet)|UNS(_OnDeck)) == 0, "") ; +} + +Mutex::Mutex (int Rank, const char * name, bool allow_vm_block) { + ClearMonitor ((Monitor *) this) ; +#ifdef ASSERT + _allow_vm_block = allow_vm_block; + _rank = Rank ; +#endif +} + +bool Monitor::owned_by_self() const { + bool ret = _owner == Thread::current(); + assert (!ret || _LockWord.Bytes[_LSBINDEX] != 0, "invariant") ; + return ret; +} + +void Monitor::print_on_error(outputStream* st) const { + st->print("[" PTR_FORMAT, this); + st->print("] %s", _name); + st->print(" - owner thread: " PTR_FORMAT, _owner); +} + + + + +// ---------------------------------------------------------------------------------- +// Non-product code + +#ifndef PRODUCT +void Monitor::print_on(outputStream* st) const { + st->print_cr("Mutex: [0x%lx/0x%lx] %s - owner: 0x%lx", this, _LockWord.FullWord, _name, _owner); +} +#endif + +#ifndef PRODUCT +#ifdef ASSERT +Monitor * Monitor::get_least_ranked_lock(Monitor * locks) { + Monitor *res, *tmp; + for (res = tmp = locks; tmp != NULL; tmp = tmp->next()) { + if (tmp->rank() < res->rank()) { + res = tmp; + } + } + if (!SafepointSynchronize::is_at_safepoint()) { + // In this case, we expect the held locks to be + // in increasing rank order (modulo any native ranks) + for (tmp = locks; tmp != NULL; tmp = tmp->next()) { + if (tmp->next() != NULL) { + assert(tmp->rank() == Mutex::native || + tmp->rank() <= tmp->next()->rank(), "mutex rank anomaly?"); + } + } + } + return res; +} + +Monitor* Monitor::get_least_ranked_lock_besides_this(Monitor* locks) { + Monitor *res, *tmp; + for (res = NULL, tmp = locks; tmp != NULL; tmp = tmp->next()) { + if (tmp != this && (res == NULL || tmp->rank() < res->rank())) { + res = tmp; + } + } + if (!SafepointSynchronize::is_at_safepoint()) { + // In this case, we expect the held locks to be + // in increasing rank order (modulo any native ranks) + for (tmp = locks; tmp != NULL; tmp = tmp->next()) { + if (tmp->next() != NULL) { + assert(tmp->rank() == Mutex::native || + tmp->rank() <= tmp->next()->rank(), "mutex rank anomaly?"); + } + } + } + return res; +} + + +bool Monitor::contains(Monitor* locks, Monitor * lock) { + for (; locks != NULL; locks = locks->next()) { + if (locks == lock) + return true; + } + return false; +} +#endif + +// Called immediately after lock acquisition or release as a diagnostic +// to track the lock-set of the thread and test for rank violations that +// might indicate exposure to deadlock. +// Rather like an EventListener for _owner (:>). + +void Monitor::set_owner_implementation(Thread *new_owner) { + // This function is solely responsible for maintaining + // and checking the invariant that threads and locks + // are in a 1/N relation, with some some locks unowned. + // It uses the Mutex::_owner, Mutex::_next, and + // Thread::_owned_locks fields, and no other function + // changes those fields. + // It is illegal to set the mutex from one non-NULL + // owner to another--it must be owned by NULL as an + // intermediate state. + + if (new_owner != NULL) { + // the thread is acquiring this lock + + assert(new_owner == Thread::current(), "Should I be doing this?"); + assert(_owner == NULL, "setting the owner thread of an already owned mutex"); + _owner = new_owner; // set the owner + + // link "this" into the owned locks list + + #ifdef ASSERT // Thread::_owned_locks is under the same ifdef + Monitor* locks = get_least_ranked_lock(new_owner->owned_locks()); + // Mutex::set_owner_implementation is a friend of Thread + + assert(this->rank() >= 0, "bad lock rank"); + + if (LogMultipleMutexLocking && locks != NULL) { + Events::log("thread " INTPTR_FORMAT " locks %s, already owns %s", new_owner, name(), locks->name()); + } + + // Deadlock avoidance rules require us to acquire Mutexes only in + // a global total order. For example m1 is the lowest ranked mutex + // that the thread holds and m2 is the mutex the thread is trying + // to acquire, then deadlock avoidance rules require that the rank + // of m2 be less than the rank of m1. + // The rank Mutex::native is an exception in that it is not subject + // to the verification rules. + // Here are some further notes relating to mutex acquisition anomalies: + // . under Solaris, the interrupt lock gets acquired when doing + // profiling, so any lock could be held. + // . it is also ok to acquire Safepoint_lock at the very end while we + // already hold Terminator_lock - may happen because of periodic safepoints + if (this->rank() != Mutex::native && + this->rank() != Mutex::suspend_resume && + locks != NULL && locks->rank() <= this->rank() && + !SafepointSynchronize::is_at_safepoint() && + this != Interrupt_lock && this != ProfileVM_lock && + !(this == Safepoint_lock && contains(locks, Terminator_lock) && + SafepointSynchronize::is_synchronizing())) { + new_owner->print_owned_locks(); + fatal4("acquiring lock %s/%d out of order with lock %s/%d -- possible deadlock", + this->name(), this->rank(), locks->name(), locks->rank()); + } + + this->_next = new_owner->_owned_locks; + new_owner->_owned_locks = this; + #endif + + } else { + // the thread is releasing this lock + + Thread* old_owner = _owner; + debug_only(_last_owner = old_owner); + + assert(old_owner != NULL, "removing the owner thread of an unowned mutex"); + assert(old_owner == Thread::current(), "removing the owner thread of an unowned mutex"); + + _owner = NULL; // set the owner + + #ifdef ASSERT + Monitor *locks = old_owner->owned_locks(); + + if (LogMultipleMutexLocking && locks != this) { + Events::log("thread " INTPTR_FORMAT " unlocks %s, still owns %s", old_owner, this->name(), locks->name()); + } + + // remove "this" from the owned locks list + + Monitor *prev = NULL; + bool found = false; + for (; locks != NULL; prev = locks, locks = locks->next()) { + if (locks == this) { + found = true; + break; + } + } + assert(found, "Removing a lock not owned"); + if (prev == NULL) { + old_owner->_owned_locks = _next; + } else { + prev->_next = _next; + } + _next = NULL; + #endif + } +} + + +// Factored out common sanity checks for locking mutex'es. Used by lock() and try_lock() +void Monitor::check_prelock_state(Thread *thread) { + assert((!thread->is_Java_thread() || ((JavaThread *)thread)->thread_state() == _thread_in_vm) + || rank() == Mutex::special, "wrong thread state for using locks"); + if (StrictSafepointChecks) { + if (thread->is_VM_thread() && !allow_vm_block()) { + fatal1("VM thread using lock %s (not allowed to block on)", name()); + } + debug_only(if (rank() != Mutex::special) \ + thread->check_for_valid_safepoint_state(false);) + } +} + +void Monitor::check_block_state(Thread *thread) { + if (!_allow_vm_block && thread->is_VM_thread()) { + warning("VM thread blocked on lock"); + print(); + BREAKPOINT; + } + assert(_owner != thread, "deadlock: blocking on monitor owned by current thread"); +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/runtime/mutex.hpp b/hotspot/src/share/vm/runtime/mutex.hpp new file mode 100644 index 00000000000..761cebb2ba4 --- /dev/null +++ b/hotspot/src/share/vm/runtime/mutex.hpp @@ -0,0 +1,318 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The SplitWord construct allows us to colocate the contention queue +// (cxq) with the lock-byte. The queue elements are ParkEvents, which are +// always aligned on 256-byte addresses - the least significant byte of +// a ParkEvent is always 0. Colocating the lock-byte with the queue +// allows us to easily avoid what would otherwise be a race in lock() +// if we were to use two completely separate fields for the contention queue +// and the lock indicator. Specifically, colocation renders us immune +// from the race where a thread might enqueue itself in the lock() slow-path +// immediately after the lock holder drops the outer lock in the unlock() +// fast-path. +// +// Colocation allows us to use a fast-path unlock() form that uses +// A MEMBAR instead of a CAS. MEMBAR has lower local latency than CAS +// on many platforms. +// +// See: +// + http://blogs.sun.com/dave/entry/biased_locking_in_hotspot +// + http://blogs.sun.com/dave/resource/synchronization-public2.pdf +// +// Note that we're *not* using word-tearing the classic sense. +// The lock() fast-path will CAS the lockword and the unlock() +// fast-path will store into the lock-byte colocated within the lockword. +// We depend on the fact that all our reference platforms have +// coherent and atomic byte accesses. More precisely, byte stores +// interoperate in a safe, sane, and expected manner with respect to +// CAS, ST and LDs to the full-word containing the byte. +// If you're porting HotSpot to a platform where that isn't the case +// then you'll want change the unlock() fast path from: +// STB;MEMBAR #storeload; LDN +// to a full-word CAS of the lockword. + + +union SplitWord { // full-word with separately addressable LSB + volatile intptr_t FullWord ; + volatile void * Address ; + volatile jbyte Bytes [sizeof(intptr_t)] ; +} ; + +// Endian-ness ... index of least-significant byte in SplitWord.Bytes[] +#ifdef AMD64 // little + #define _LSBINDEX 0 +#else +#if IA32 // little + #define _LSBINDEX 0 +#else +#ifdef SPARC // big + #define _LSBINDEX (sizeof(intptr_t)-1) +#else + #error "unknown architecture" +#endif +#endif +#endif + +class ParkEvent ; + +// See orderAccess.hpp. We assume throughout the VM that mutex lock and +// try_lock do fence-lock-acquire, and that unlock does a release-unlock, +// *in that order*. If their implementations change such that these +// assumptions are violated, a whole lot of code will break. + +class Monitor : public CHeapObj { + + public: + // A special lock: Is a lock where you are guaranteed not to block while you are + // holding it, i.e., no vm operation can happen, taking other locks, etc. + // NOTE: It is critical that the rank 'special' be the lowest (earliest) + // (except for "event"?) for the deadlock dection to work correctly. + // The rank native is only for use in Mutex's created by JVM_RawMonitorCreate, + // which being external to the VM are not subject to deadlock detection. + // The rank safepoint is used only for synchronization in reaching a + // safepoint and leaving a safepoint. It is only used for the Safepoint_lock + // currently. While at a safepoint no mutexes of rank safepoint are held + // by any thread. + // The rank named "leaf" is probably historical (and should + // be changed) -- mutexes of this rank aren't really leaf mutexes + // at all. + enum lock_types { + event, + special, + suspend_resume, + leaf = suspend_resume + 2, + safepoint = leaf + 10, + barrier = safepoint + 1, + nonleaf = barrier + 1, + max_nonleaf = nonleaf + 900, + native = max_nonleaf + 1 + }; + + // The WaitSet and EntryList linked lists are composed of ParkEvents. + // I use ParkEvent instead of threads as ParkEvents are immortal and + // type-stable, meaning we can safely unpark() a possibly stale + // list element in the unlock()-path. + + protected: // Monitor-Mutex metadata + SplitWord _LockWord ; // Contention queue (cxq) colocated with Lock-byte + enum LockWordBits { _LBIT=1 } ; + Thread * volatile _owner; // The owner of the lock + // Consider sequestering _owner on its own $line + // to aid future synchronization mechanisms. + ParkEvent * volatile _EntryList ; // List of threads waiting for entry + ParkEvent * volatile _OnDeck ; // heir-presumptive + volatile intptr_t _WaitLock [1] ; // Protects _WaitSet + ParkEvent * volatile _WaitSet ; // LL of ParkEvents + volatile bool _snuck; // Used for sneaky locking (evil). + const char * _name; // Name of mutex + int NotifyCount ; // diagnostic assist + double pad [8] ; // avoid false sharing + + // Debugging fields for naming, deadlock detection, etc. (some only used in debug mode) +#ifndef PRODUCT + bool _allow_vm_block; + debug_only(int _rank;) // rank (to avoid/detect potential deadlocks) + debug_only(Monitor * _next;) // Used by a Thread to link up owned locks + debug_only(Thread* _last_owner;) // the last thread to own the lock + debug_only(static bool contains(Monitor * locks, Monitor * lock);) + debug_only(static Monitor * get_least_ranked_lock(Monitor * locks);) + debug_only(Monitor * get_least_ranked_lock_besides_this(Monitor * locks);) +#endif + + void set_owner_implementation(Thread* owner) PRODUCT_RETURN; + void check_prelock_state (Thread* thread) PRODUCT_RETURN; + void check_block_state (Thread* thread) PRODUCT_RETURN; + + // platform-dependent support code can go here (in os_.cpp) + public: + enum { + _no_safepoint_check_flag = true, + _allow_vm_block_flag = true, + _as_suspend_equivalent_flag = true + }; + + enum WaitResults { + CONDVAR_EVENT, // Wait returned because of condition variable notification + INTERRUPT_EVENT, // Wait returned because waiting thread was interrupted + NUMBER_WAIT_RESULTS + }; + + private: + int TrySpin (Thread * Self) ; + int TryLock () ; + int TryFast () ; + int AcquireOrPush (ParkEvent * ev) ; + void IUnlock (bool RelaxAssert) ; + void ILock (Thread * Self) ; + int IWait (Thread * Self, jlong timo); + int ILocked () ; + + protected: + static void ClearMonitor (Monitor * m) ; + Monitor() ; + + public: + Monitor(int rank, const char *name, bool allow_vm_block=false); + ~Monitor(); + + // Wait until monitor is notified (or times out). + // Defaults are to make safepoint checks, wait time is forever (i.e., + // zero), and not a suspend-equivalent condition. Returns true if wait + // times out; otherwise returns false. + bool wait(bool no_safepoint_check = !_no_safepoint_check_flag, + long timeout = 0, + bool as_suspend_equivalent = !_as_suspend_equivalent_flag); + bool notify(); + bool notify_all(); + + + void lock(); // prints out warning if VM thread blocks + void lock(Thread *thread); // overloaded with current thread + void unlock(); + bool is_locked() const { return _owner != NULL; } + + bool try_lock(); // Like lock(), but unblocking. It returns false instead + + // Lock without safepoint check. Should ONLY be used by safepoint code and other code + // that is guaranteed not to block while running inside the VM. + void lock_without_safepoint_check(); + void lock_without_safepoint_check (Thread * Self) ; + + // Current owner - not not MT-safe. Can only be used to guarantee that + // the current running thread owns the lock + Thread* owner() const { return _owner; } + bool owned_by_self() const; + + // Support for JVM_RawMonitorEnter & JVM_RawMonitorExit. These can be called by + // non-Java thread. (We should really have a RawMonitor abstraction) + void jvm_raw_lock(); + void jvm_raw_unlock(); + const char *name() const { return _name; } + + void print_on_error(outputStream* st) const; + + #ifndef PRODUCT + void print_on(outputStream* st) const; + void print() const { print_on(tty); } + debug_only(int rank() const { return _rank; }) + bool allow_vm_block() { return _allow_vm_block; } + + debug_only(Monitor *next() const { return _next; }) + debug_only(void set_next(Monitor *next) { _next = next; }) + #endif + + void set_owner(Thread* owner) { + #ifndef PRODUCT + set_owner_implementation(owner); + debug_only(void verify_Monitor(Thread* thr)); + #else + _owner = owner; + #endif + } + +}; + +// Normally we'd expect Monitor to extend Mutex in the sense that a monitor +// constructed from pthreads primitives might extend a mutex by adding +// a condvar and some extra metadata. In fact this was the case until J2SE7. +// +// Currently, however, the base object is a monitor. Monitor contains all the +// logic for wait(), notify(), etc. Mutex extends monitor and restricts the +// visiblity of wait(), notify(), and notify_all(). +// +// Another viable alternative would have been to have Monitor extend Mutex and +// implement all the normal mutex and wait()-notify() logic in Mutex base class. +// The wait()-notify() facility would be exposed via special protected member functions +// (e.g., _Wait() and _Notify()) in Mutex. Monitor would extend Mutex and expose wait() +// as a call to _Wait(). That is, the public wait() would be a wrapper for the protected +// _Wait(). +// +// An even better alternative is to simply eliminate Mutex:: and use Monitor:: instead. +// After all, monitors are sufficient for Java-level synchronization. At one point in time +// there may have been some benefit to having distinct mutexes and monitors, but that time +// has past. +// +// The Mutex/Monitor design parallels that of Java-monitors, being based on +// thread-specific park-unpark platform-specific primitives. + + +class Mutex : public Monitor { // degenerate Monitor + public: + Mutex (int rank, const char *name, bool allow_vm_block=false); + ~Mutex () ; + private: + bool notify () { ShouldNotReachHere(); return false; } + bool notify_all() { ShouldNotReachHere(); return false; } + bool wait (bool no_safepoint_check, long timeout, bool as_suspend_equivalent) { + ShouldNotReachHere() ; + return false ; + } +}; + +/* + * Per-thread blocking support for JSR166. See the Java-level + * Documentation for rationale. Basically, park acts like wait, unpark + * like notify. + * + * 6271289 -- + * To avoid errors where an os thread expires but the JavaThread still + * exists, Parkers are immortal (type-stable) and are recycled across + * new threads. This parallels the ParkEvent implementation. + * Because park-unpark allow spurious wakeups it is harmless if an + * unpark call unparks a new thread using the old Parker reference. + * + * In the future we'll want to think about eliminating Parker and using + * ParkEvent instead. There's considerable duplication between the two + * services. + * + */ + +class Parker : public os::PlatformParker { +private: + volatile int _counter ; + Parker * FreeNext ; + JavaThread * AssociatedWith ; // Current association + +public: + Parker() : PlatformParker() { + _counter = 0 ; + FreeNext = NULL ; + AssociatedWith = NULL ; + } +protected: + ~Parker() { ShouldNotReachHere(); } +public: + // For simplicity of interface with Java, all forms of park (indefinite, + // relative, and absolute) are multiplexed into one call. + void park(bool isAbsolute, jlong time); + void unpark(); + + // Lifecycle operators + static Parker * Allocate (JavaThread * t) ; + static void Release (Parker * e) ; +private: + static Parker * volatile FreeList ; + static volatile int ListLock ; +}; diff --git a/hotspot/src/share/vm/runtime/mutexLocker.cpp b/hotspot/src/share/vm/runtime/mutexLocker.cpp new file mode 100644 index 00000000000..317e24ee535 --- /dev/null +++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp @@ -0,0 +1,266 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_mutexLocker.cpp.incl" + +// Mutexes used in the VM (see comment in mutexLocker.hpp): +// +// Note that the following pointers are effectively final -- after having been +// set at JVM startup-time, they should never be subsequently mutated. +// Instead of using pointers to malloc()ed monitors and mutexes we should consider +// eliminating the indirection and using instances instead. +// Consider using GCC's __read_mostly. + +Mutex* Patching_lock = NULL; +Monitor* SystemDictionary_lock = NULL; +Mutex* PackageTable_lock = NULL; +Mutex* CompiledIC_lock = NULL; +Mutex* InlineCacheBuffer_lock = NULL; +Mutex* VMStatistic_lock = NULL; +Mutex* JNIGlobalHandle_lock = NULL; +Mutex* JNIHandleBlockFreeList_lock = NULL; +Mutex* JNICachedItableIndex_lock = NULL; +Mutex* JmethodIdCreation_lock = NULL; +Mutex* JfieldIdCreation_lock = NULL; +Monitor* JNICritical_lock = NULL; +Mutex* JvmtiThreadState_lock = NULL; +Monitor* JvmtiPendingEvent_lock = NULL; +Mutex* Heap_lock = NULL; +Mutex* ExpandHeap_lock = NULL; +Mutex* AdapterHandlerLibrary_lock = NULL; +Mutex* SignatureHandlerLibrary_lock = NULL; +Mutex* VtableStubs_lock = NULL; +Mutex* SymbolTable_lock = NULL; +Mutex* StringTable_lock = NULL; +Mutex* CodeCache_lock = NULL; +Mutex* MethodData_lock = NULL; +Mutex* RetData_lock = NULL; +Monitor* VMOperationQueue_lock = NULL; +Monitor* VMOperationRequest_lock = NULL; +Monitor* Safepoint_lock = NULL; +Monitor* SerializePage_lock = NULL; +Monitor* Threads_lock = NULL; +Monitor* CGC_lock = NULL; +Mutex* STS_init_lock = NULL; +Monitor* SLT_lock = NULL; +Monitor* iCMS_lock = NULL; +Monitor* FullGCCount_lock = NULL; +Mutex* ParGCRareEvent_lock = NULL; +Mutex* DerivedPointerTableGC_lock = NULL; +Mutex* Compile_lock = NULL; +Monitor* MethodCompileQueue_lock = NULL; +#ifdef TIERED +Monitor* C1_lock = NULL; +#endif // TIERED +Monitor* CompileThread_lock = NULL; +Mutex* CompileTaskAlloc_lock = NULL; +Mutex* CompileStatistics_lock = NULL; +Mutex* MultiArray_lock = NULL; +Monitor* Terminator_lock = NULL; +Monitor* BeforeExit_lock = NULL; +Monitor* Notify_lock = NULL; +Monitor* Interrupt_lock = NULL; +Monitor* ProfileVM_lock = NULL; +Mutex* ProfilePrint_lock = NULL; +Mutex* ExceptionCache_lock = NULL; +Monitor* ObjAllocPost_lock = NULL; +Mutex* OsrList_lock = NULL; +#ifndef PRODUCT +Mutex* FullGCALot_lock = NULL; +#endif + +Mutex* Debug1_lock = NULL; +Mutex* Debug2_lock = NULL; +Mutex* Debug3_lock = NULL; + +Mutex* tty_lock = NULL; + +Mutex* RawMonitor_lock = NULL; +Mutex* PerfDataMemAlloc_lock = NULL; +Mutex* PerfDataManager_lock = NULL; +Mutex* OopMapCacheAlloc_lock = NULL; + +Monitor* GCTaskManager_lock = NULL; + +Mutex* Management_lock = NULL; +Monitor* LowMemory_lock = NULL; + +#define MAX_NUM_MUTEX 128 +static Monitor * _mutex_array[MAX_NUM_MUTEX]; +static int _num_mutex; + +#ifdef ASSERT +void assert_locked_or_safepoint(const Monitor * lock) { + // check if this thread owns the lock (common case) + if (IgnoreLockingAssertions) return; + assert(lock != NULL, "Need non-NULL lock"); + if (lock->owned_by_self()) return; + if (SafepointSynchronize::is_at_safepoint()) return; + if (!Universe::is_fully_initialized()) return; + // see if invoker of VM operation owns it + VM_Operation* op = VMThread::vm_operation(); + if (op != NULL && op->calling_thread() == lock->owner()) return; + fatal1("must own lock %s", lock->name()); +} + +// a stronger assertion than the above +void assert_lock_strong(const Monitor * lock) { + if (IgnoreLockingAssertions) return; + assert(lock != NULL, "Need non-NULL lock"); + if (lock->owned_by_self()) return; + fatal1("must own lock %s", lock->name()); +} +#endif + +#define def(var, type, pri, vm_block) { \ + var = new type(Mutex::pri, #var, vm_block); \ + assert(_num_mutex < MAX_NUM_MUTEX, \ + "increase MAX_NUM_MUTEX"); \ + _mutex_array[_num_mutex++] = var; \ +} + +void mutex_init() { + def(tty_lock , Mutex , event, true ); // allow to lock in VM + + def(CGC_lock , Monitor, special, true ); // coordinate between fore- and background GC + def(STS_init_lock , Mutex, leaf, true ); + if (UseConcMarkSweepGC) { + def(iCMS_lock , Monitor, special, true ); // CMS incremental mode start/stop notification + def(FullGCCount_lock , Monitor, leaf, true ); // in support of ExplicitGCInvokesConcurrent + } + def(ParGCRareEvent_lock , Mutex , leaf , true ); + def(DerivedPointerTableGC_lock , Mutex, leaf, true ); + def(CodeCache_lock , Mutex , special, true ); + def(Interrupt_lock , Monitor, special, true ); // used for interrupt processing + def(RawMonitor_lock , Mutex, special, true ); + def(OopMapCacheAlloc_lock , Mutex, leaf, true ); // used for oop_map_cache allocation. + + def(Patching_lock , Mutex , special, true ); // used for safepointing and code patching. + def(ObjAllocPost_lock , Monitor, special, false); + def(LowMemory_lock , Monitor, special, true ); // used for low memory detection + def(JmethodIdCreation_lock , Mutex , leaf, true ); // used for creating jmethodIDs. + + def(SystemDictionary_lock , Monitor, leaf, true ); // lookups done by VM thread + def(PackageTable_lock , Mutex , leaf, false); + def(InlineCacheBuffer_lock , Mutex , leaf, true ); + def(VMStatistic_lock , Mutex , leaf, false); + def(ExpandHeap_lock , Mutex , leaf, true ); // Used during compilation by VM thread + def(JNIHandleBlockFreeList_lock , Mutex , leaf, true ); // handles are used by VM thread + def(SignatureHandlerLibrary_lock , Mutex , leaf, false); + def(SymbolTable_lock , Mutex , leaf, true ); + def(StringTable_lock , Mutex , leaf, true ); + def(ProfilePrint_lock , Mutex , leaf, false); // serial profile printing + def(ExceptionCache_lock , Mutex , leaf, false); // serial profile printing + def(OsrList_lock , Mutex , leaf, true ); + def(Debug1_lock , Mutex , leaf, true ); +#ifndef PRODUCT + def(FullGCALot_lock , Mutex , leaf, false); // a lock to make FullGCALot MT safe +#endif + def(BeforeExit_lock , Monitor, leaf, true ); + def(PerfDataMemAlloc_lock , Mutex , leaf, true ); // used for allocating PerfData memory for performance data + def(PerfDataManager_lock , Mutex , leaf, true ); // used for synchronized access to PerfDataManager resources + + // CMS_modUnionTable_lock leaf + // CMS_bitMap_lock leaf + 1 + // CMS_freeList_lock leaf + 2 + + def(Safepoint_lock , Monitor, safepoint, true ); // locks SnippetCache_lock/Threads_lock + + if (!UseMembar) { + def(SerializePage_lock , Monitor, leaf, true ); + } + + def(Threads_lock , Monitor, barrier, true ); + + def(VMOperationQueue_lock , Monitor, nonleaf, true ); // VM_thread allowed to block on these + def(VMOperationRequest_lock , Monitor, nonleaf, true ); + def(RetData_lock , Mutex , nonleaf, false); + def(Terminator_lock , Monitor, nonleaf, true ); + def(VtableStubs_lock , Mutex , nonleaf, true ); + def(Notify_lock , Monitor, nonleaf, true ); + def(JNIGlobalHandle_lock , Mutex , nonleaf, true ); // locks JNIHandleBlockFreeList_lock + def(JNICritical_lock , Monitor, nonleaf, true ); // used for JNI critical regions + def(AdapterHandlerLibrary_lock , Mutex , nonleaf, true); + if (UseConcMarkSweepGC) { + def(SLT_lock , Monitor, nonleaf, false ); + // used in CMS GC for locking PLL lock + } + def(Heap_lock , Mutex , nonleaf+1, false); + def(JfieldIdCreation_lock , Mutex , nonleaf+1, true ); // jfieldID, Used in VM_Operation + def(JNICachedItableIndex_lock , Mutex , nonleaf+1, false); // Used to cache an itable index during JNI invoke + + def(CompiledIC_lock , Mutex , nonleaf+2, false); // locks VtableStubs_lock, InlineCacheBuffer_lock + def(CompileTaskAlloc_lock , Mutex , nonleaf+2, true ); + def(CompileStatistics_lock , Mutex , nonleaf+2, false); + def(MultiArray_lock , Mutex , nonleaf+2, false); // locks SymbolTable_lock + + def(JvmtiThreadState_lock , Mutex , nonleaf+2, false); // Used by JvmtiThreadState/JvmtiEventController + def(JvmtiPendingEvent_lock , Monitor, nonleaf, false); // Used by JvmtiCodeBlobEvents + def(Management_lock , Mutex , nonleaf+2, false); // used for JVM management + + def(Compile_lock , Mutex , nonleaf+3, true ); + def(MethodData_lock , Mutex , nonleaf+3, false); + + def(MethodCompileQueue_lock , Monitor, nonleaf+4, true ); + def(Debug2_lock , Mutex , nonleaf+4, true ); + def(Debug3_lock , Mutex , nonleaf+4, true ); + def(ProfileVM_lock , Monitor, nonleaf+4, false); // used for profiling of the VMThread + def(CompileThread_lock , Monitor, nonleaf+5, false ); +#ifdef TIERED + def(C1_lock , Monitor, nonleaf+5, false ); +#endif // TIERED + + +} + +GCMutexLocker::GCMutexLocker(Monitor * mutex) { + if (SafepointSynchronize::is_at_safepoint()) { + _locked = false; + } else { + _mutex = mutex; + _locked = true; + _mutex->lock(); + } +} + +// Print all mutexes/monitors that are currently owned by a thread; called +// by fatal error handler. +void print_owned_locks_on_error(outputStream* st) { + st->print("VM Mutex/Monitor currently owned by a thread: "); + bool none = true; + for (int i = 0; i < _num_mutex; i++) { + // see if it has an owner + if (_mutex_array[i]->owner() != NULL) { + if (none) { + // print format used by Mutex::print_on_error() + st->print_cr(" ([mutex/lock_event])"); + none = false; + } + _mutex_array[i]->print_on_error(st); + st->cr(); + } + } + if (none) st->print_cr("None"); +} diff --git a/hotspot/src/share/vm/runtime/mutexLocker.hpp b/hotspot/src/share/vm/runtime/mutexLocker.hpp new file mode 100644 index 00000000000..59f145fcfb6 --- /dev/null +++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp @@ -0,0 +1,325 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Mutexes used in the VM. + +extern Mutex* Patching_lock; // a lock used to guard code patching of compiled code +extern Monitor* SystemDictionary_lock; // a lock on the system dictonary +extern Mutex* PackageTable_lock; // a lock on the class loader package table +extern Mutex* CompiledIC_lock; // a lock used to guard compiled IC patching and access +extern Mutex* InlineCacheBuffer_lock; // a lock used to guard the InlineCacheBuffer +extern Mutex* VMStatistic_lock; // a lock used to guard statistics count increment +extern Mutex* JNIGlobalHandle_lock; // a lock on creating JNI global handles +extern Mutex* JNIHandleBlockFreeList_lock; // a lock on the JNI handle block free list +extern Mutex* JNICachedItableIndex_lock; // a lock on caching an itable index during JNI invoke +extern Mutex* JmethodIdCreation_lock; // a lock on creating JNI method identifiers +extern Mutex* JfieldIdCreation_lock; // a lock on creating JNI static field identifiers +extern Monitor* JNICritical_lock; // a lock used while entering and exiting JNI critical regions, allows GC to sometimes get in +extern Mutex* JvmtiThreadState_lock; // a lock on modification of JVMTI thread data +extern Monitor* JvmtiPendingEvent_lock; // a lock on the JVMTI pending events list +extern Mutex* Heap_lock; // a lock on the heap +extern Mutex* ExpandHeap_lock; // a lock on expanding the heap +extern Mutex* AdapterHandlerLibrary_lock; // a lock on the AdapterHandlerLibrary +extern Mutex* SignatureHandlerLibrary_lock; // a lock on the SignatureHandlerLibrary +extern Mutex* VtableStubs_lock; // a lock on the VtableStubs +extern Mutex* SymbolTable_lock; // a lock on the symbol table +extern Mutex* StringTable_lock; // a lock on the interned string table +extern Mutex* CodeCache_lock; // a lock on the CodeCache, rank is special, use MutexLockerEx +extern Mutex* MethodData_lock; // a lock on installation of method data +extern Mutex* RetData_lock; // a lock on installation of RetData inside method data +extern Mutex* DerivedPointerTableGC_lock; // a lock to protect the derived pointer table +extern Monitor* VMOperationQueue_lock; // a lock on queue of vm_operations waiting to execute +extern Monitor* VMOperationRequest_lock; // a lock on Threads waiting for a vm_operation to terminate +extern Monitor* Safepoint_lock; // a lock used by the safepoint abstraction +extern Monitor* SerializePage_lock; // a lock used when VMThread changing serialize memory page permission during safepoint +extern Monitor* Threads_lock; // a lock on the Threads table of active Java threads + // (also used by Safepoints too to block threads creation/destruction) +extern Monitor* CGC_lock; // used for coordination between + // fore- & background GC threads. +extern Mutex* STS_init_lock; // coordinate initialization of SuspendibleThreadSets. +extern Monitor* SLT_lock; // used in CMS GC for acquiring PLL +extern Monitor* iCMS_lock; // CMS incremental mode start/stop notification +extern Monitor* FullGCCount_lock; // in support of "concurrent" full gc + // (see option ExplicitGCInvokesConcurrent) +extern Mutex* ParGCRareEvent_lock; // Synchronizes various (rare) parallel GC ops. +extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc) +extern Monitor* MethodCompileQueue_lock; // a lock held when method compilations are enqueued, dequeued +#ifdef TIERED +extern Monitor* C1_lock; // a lock to ensure on single c1 compile is ever active +#endif // TIERED +extern Monitor* CompileThread_lock; // a lock held by compile threads during compilation system initialization +extern Mutex* CompileTaskAlloc_lock; // a lock held when CompileTasks are allocated +extern Mutex* CompileStatistics_lock; // a lock held when updating compilation statistics +extern Mutex* MultiArray_lock; // a lock used to guard allocation of multi-dim arrays +extern Monitor* Terminator_lock; // a lock used to guard termination of the vm +extern Monitor* BeforeExit_lock; // a lock used to guard cleanups and shutdown hooks +extern Monitor* Notify_lock; // a lock used to synchronize the start-up of the vm +extern Monitor* Interrupt_lock; // a lock used for condition variable mediated interrupt processing +extern Monitor* ProfileVM_lock; // a lock used for profiling the VMThread +extern Mutex* ProfilePrint_lock; // a lock used to serialize the printing of profiles +extern Mutex* ExceptionCache_lock; // a lock used to synchronize exception cache updates +extern Mutex* OsrList_lock; // a lock used to serialize access to OSR queues + +#ifndef PRODUCT +extern Mutex* FullGCALot_lock; // a lock to make FullGCALot MT safe +#endif +extern Mutex* Debug1_lock; // A bunch of pre-allocated locks that can be used for tracing +extern Mutex* Debug2_lock; // down synchronization related bugs! +extern Mutex* Debug3_lock; + +extern Mutex* RawMonitor_lock; +extern Mutex* PerfDataMemAlloc_lock; // a lock on the allocator for PerfData memory for performance data +extern Mutex* PerfDataManager_lock; // a long on access to PerfDataManager resources +extern Mutex* ParkerFreeList_lock; +extern Mutex* OopMapCacheAlloc_lock; // protects allocation of oop_map caches + +extern Mutex* Management_lock; // a lock used to serialize JVM management +extern Monitor* LowMemory_lock; // a lock used for low memory detection + +// A MutexLocker provides mutual exclusion with respect to a given mutex +// for the scope which contains the locker. The lock is an OS lock, not +// an object lock, and the two do not interoperate. Do not use Mutex-based +// locks to lock on Java objects, because they will not be respected if a +// that object is locked using the Java locking mechanism. +// +// NOTE WELL!! +// +// See orderAccess.hpp. We assume throughout the VM that MutexLocker's +// and friends constructors do a fence, a lock and an acquire *in that +// order*. And that their destructors do a release and unlock, in *that* +// order. If their implementations change such that these assumptions +// are violated, a whole lot of code will break. + +// Print all mutexes/monitors that are currently owned by a thread; called +// by fatal error handler. +void print_owned_locks_on_error(outputStream* st); + +char *lock_name(Mutex *mutex); + +class MutexLocker: StackObj { + private: + Monitor * _mutex; + public: + MutexLocker(Monitor * mutex) { + assert(mutex->rank() != Mutex::special, + "Special ranked mutex should only use MutexLockerEx"); + _mutex = mutex; + _mutex->lock(); + } + + // Overloaded constructor passing current thread + MutexLocker(Monitor * mutex, Thread *thread) { + assert(mutex->rank() != Mutex::special, + "Special ranked mutex should only use MutexLockerEx"); + _mutex = mutex; + _mutex->lock(thread); + } + + ~MutexLocker() { + _mutex->unlock(); + } + +}; + +// for debugging: check that we're already owning this lock (or are at a safepoint) +#ifdef ASSERT +void assert_locked_or_safepoint(const Monitor * lock); +void assert_lock_strong(const Monitor * lock); +#else +#define assert_locked_or_safepoint(lock) +#define assert_lock_strong(lock) +#endif + +// A MutexLockerEx behaves like a MutexLocker when its constructor is +// called with a Mutex. Unlike a MutexLocker, its constructor can also be +// called with NULL, in which case the MutexLockerEx is a no-op. There +// is also a corresponding MutexUnlockerEx. We want to keep the +// basic MutexLocker as fast as possible. MutexLockerEx can also lock +// without safepoint check. + +class MutexLockerEx: public StackObj { + private: + Monitor * _mutex; + public: + MutexLockerEx(Monitor * mutex, bool no_safepoint_check = !Mutex::_no_safepoint_check_flag) { + _mutex = mutex; + if (_mutex != NULL) { + assert(mutex->rank() > Mutex::special || no_safepoint_check, + "Mutexes with rank special or lower should not do safepoint checks"); + if (no_safepoint_check) + _mutex->lock_without_safepoint_check(); + else + _mutex->lock(); + } + } + + ~MutexLockerEx() { + if (_mutex != NULL) { + _mutex->unlock(); + } + } +}; + +// A MonitorLockerEx is like a MutexLockerEx above, except it takes +// a possibly null Monitor, and allows wait/notify as well which are +// delegated to the underlying Monitor. + +class MonitorLockerEx: public MutexLockerEx { + private: + Monitor * _monitor; + public: + MonitorLockerEx(Monitor* monitor, + bool no_safepoint_check = !Mutex::_no_safepoint_check_flag): + MutexLockerEx(monitor, no_safepoint_check), + _monitor(monitor) { + // Superclass constructor did locking + } + + ~MonitorLockerEx() { + #ifdef ASSERT + if (_monitor != NULL) { + assert_lock_strong(_monitor); + } + #endif // ASSERT + // Superclass destructor will do unlocking + } + + bool wait(bool no_safepoint_check = !Mutex::_no_safepoint_check_flag, + long timeout = 0, + bool as_suspend_equivalent = !Mutex::_as_suspend_equivalent_flag) { + if (_monitor != NULL) { + return _monitor->wait(no_safepoint_check, timeout, as_suspend_equivalent); + } + return false; + } + + bool notify_all() { + if (_monitor != NULL) { + return _monitor->notify_all(); + } + return true; + } + + bool notify() { + if (_monitor != NULL) { + return _monitor->notify(); + } + return true; + } +}; + + + +// A GCMutexLocker is usually initialized with a mutex that is +// automatically acquired in order to do GC. The function that +// synchronizes using a GCMutexLocker may be called both during and between +// GC's. Thus, it must acquire the mutex if GC is not in progress, but not +// if GC is in progress (since the mutex is already held on its behalf.) + +class GCMutexLocker: public StackObj { +private: + Monitor * _mutex; + bool _locked; +public: + GCMutexLocker(Monitor * mutex); + ~GCMutexLocker() { if (_locked) _mutex->unlock(); } +}; + + + +// A MutexUnlocker temporarily exits a previously +// entered mutex for the scope which contains the unlocker. + +class MutexUnlocker: StackObj { + private: + Monitor * _mutex; + + public: + MutexUnlocker(Monitor * mutex) { + _mutex = mutex; + _mutex->unlock(); + } + + ~MutexUnlocker() { + _mutex->lock(); + } +}; + +// A MutexUnlockerEx temporarily exits a previously +// entered mutex for the scope which contains the unlocker. + +class MutexUnlockerEx: StackObj { + private: + Monitor * _mutex; + bool _no_safepoint_check; + + public: + MutexUnlockerEx(Monitor * mutex, bool no_safepoint_check = !Mutex::_no_safepoint_check_flag) { + _mutex = mutex; + _no_safepoint_check = no_safepoint_check; + _mutex->unlock(); + } + + ~MutexUnlockerEx() { + if (_no_safepoint_check == Mutex::_no_safepoint_check_flag) { + _mutex->lock_without_safepoint_check(); + } else { + _mutex->lock(); + } + } +}; + +#ifndef PRODUCT +// +// A special MutexLocker that allows: +// - reentrant locking +// - locking out of order +// +// Only too be used for verify code, where we can relaxe out dead-lock +// dection code a bit (unsafe, but probably ok). This code is NEVER to +// be included in a product version. +// +class VerifyMutexLocker: StackObj { + private: + Monitor * _mutex; + bool _reentrant; + public: + VerifyMutexLocker(Monitor * mutex) { + _mutex = mutex; + _reentrant = mutex->owned_by_self(); + if (!_reentrant) { + // We temp. diable strict safepoint checking, while we require the lock + FlagSetting fs(StrictSafepointChecks, false); + _mutex->lock(); + } + } + + ~VerifyMutexLocker() { + if (!_reentrant) { + _mutex->unlock(); + } + } +}; + +#endif diff --git a/hotspot/src/share/vm/runtime/objectMonitor.hpp b/hotspot/src/share/vm/runtime/objectMonitor.hpp new file mode 100644 index 00000000000..2a1384c530f --- /dev/null +++ b/hotspot/src/share/vm/runtime/objectMonitor.hpp @@ -0,0 +1,208 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// WARNING: +// This is a very sensitive and fragile class. DO NOT make any +// change unless you are fully aware of the underlying semantics. + +// This class can not inherit from any other class, because I have +// to let the displaced header be the very first word. Otherwise I +// have to let markOop include this file, which would export the +// monitor data structure to everywhere. +// +// The ObjectMonitor class is used to implement JavaMonitors which have +// transformed from the lightweight structure of the thread stack to a +// heavy weight lock due to contention + +// It is also used as RawMonitor by the JVMTI + + +class ObjectWaiter; + +class ObjectMonitor { + public: + enum { + OM_OK, // no error + OM_SYSTEM_ERROR, // operating system error + OM_ILLEGAL_MONITOR_STATE, // IllegalMonitorStateException + OM_INTERRUPTED, // Thread.interrupt() + OM_TIMED_OUT // Object.wait() timed out + }; + + public: + // TODO-FIXME: the "offset" routines should return a type of off_t instead of int ... + // ByteSize would also be an appropriate type. + static int header_offset_in_bytes() { return offset_of(ObjectMonitor, _header); } + static int object_offset_in_bytes() { return offset_of(ObjectMonitor, _object); } + static int owner_offset_in_bytes() { return offset_of(ObjectMonitor, _owner); } + static int count_offset_in_bytes() { return offset_of(ObjectMonitor, _count); } + static int recursions_offset_in_bytes() { return offset_of(ObjectMonitor, _recursions); } + static int cxq_offset_in_bytes() { return offset_of(ObjectMonitor, _cxq) ; } + static int succ_offset_in_bytes() { return offset_of(ObjectMonitor, _succ) ; } + static int EntryList_offset_in_bytes() { return offset_of(ObjectMonitor, _EntryList); } + static int FreeNext_offset_in_bytes() { return offset_of(ObjectMonitor, FreeNext); } + static int WaitSet_offset_in_bytes() { return offset_of(ObjectMonitor, _WaitSet) ; } + static int Responsible_offset_in_bytes() { return offset_of(ObjectMonitor, _Responsible);} + static int Spinner_offset_in_bytes() { return offset_of(ObjectMonitor, _Spinner); } + + public: + // Eventaully we'll make provisions for multiple callbacks, but + // now one will suffice. + static int (*SpinCallbackFunction)(intptr_t, int) ; + static intptr_t SpinCallbackArgument ; + + + public: + ObjectMonitor(); + ~ObjectMonitor(); + + markOop header() const; + void set_header(markOop hdr); + + intptr_t is_busy() const; + intptr_t is_entered(Thread* current) const; + + void* owner() const; + void set_owner(void* owner); + + intptr_t waiters() const; + + intptr_t count() const; + void set_count(intptr_t count); + intptr_t contentions() const ; + + // JVM/DI GetMonitorInfo() needs this + Thread * thread_of_waiter (ObjectWaiter *) ; + ObjectWaiter * first_waiter () ; + ObjectWaiter * next_waiter(ObjectWaiter* o); + + intptr_t recursions() const { return _recursions; } + + void* object() const; + void* object_addr(); + void set_object(void* obj); + + bool check(TRAPS); // true if the thread owns the monitor. + void check_slow(TRAPS); + void clear(); +#ifndef PRODUCT + void verify(); + void print(); +#endif + + bool try_enter (TRAPS) ; + void enter(TRAPS); + void exit(TRAPS); + void wait(jlong millis, bool interruptable, TRAPS); + void notify(TRAPS); + void notifyAll(TRAPS); + +// Use the following at your own risk + intptr_t complete_exit(TRAPS); + void reenter(intptr_t recursions, TRAPS); + + int raw_enter(TRAPS); + int raw_exit(TRAPS); + int raw_wait(jlong millis, bool interruptable, TRAPS); + int raw_notify(TRAPS); + int raw_notifyAll(TRAPS); + + private: + // JVMTI support -- remove ASAP + int SimpleEnter (Thread * Self) ; + int SimpleExit (Thread * Self) ; + int SimpleWait (Thread * Self, jlong millis) ; + int SimpleNotify (Thread * Self, bool All) ; + + private: + void Recycle () ; + void AddWaiter (ObjectWaiter * waiter) ; + + ObjectWaiter * DequeueWaiter () ; + void DequeueSpecificWaiter (ObjectWaiter * waiter) ; + void EnterI (TRAPS) ; + void ReenterI (Thread * Self, ObjectWaiter * SelfNode) ; + void UnlinkAfterAcquire (Thread * Self, ObjectWaiter * SelfNode) ; + int TryLock (Thread * Self) ; + int NotRunnable (Thread * Self, Thread * Owner) ; + int TrySpin_Fixed (Thread * Self) ; + int TrySpin_VaryFrequency (Thread * Self) ; + int TrySpin_VaryDuration (Thread * Self) ; + void ctAsserts () ; + void ExitEpilog (Thread * Self, ObjectWaiter * Wakee) ; + bool ExitSuspendEquivalent (JavaThread * Self) ; + + private: + friend class ObjectSynchronizer; + friend class ObjectWaiter; + friend class VMStructs; + + // WARNING: this must be the very first word of ObjectMonitor + // This means this class can't use any virtual member functions. + // TODO-FIXME: assert that offsetof(_header) is 0 or get rid of the + // implicit 0 offset in emitted code. + + volatile markOop _header; // displaced object header word - mark + void* volatile _object; // backward object pointer - strong root + + double SharingPad [1] ; // temp to reduce false sharing + + // All the following fields must be machine word aligned + // The VM assumes write ordering wrt these fields, which can be + // read from other threads. + + void * volatile _owner; // pointer to owning thread OR BasicLock + volatile intptr_t _recursions; // recursion count, 0 for first entry + int OwnerIsThread ; // _owner is (Thread *) vs SP/BasicLock + ObjectWaiter * volatile _cxq ; // LL of recently-arrived threads blocked on entry. + // The list is actually composed of WaitNodes, acting + // as proxies for Threads. + ObjectWaiter * volatile _EntryList ; // Threads blocked on entry or reentry. + Thread * volatile _succ ; // Heir presumptive thread - used for futile wakeup throttling + Thread * volatile _Responsible ; + int _PromptDrain ; // rqst to drain cxq into EntryList ASAP + + volatile int _Spinner ; // for exit->spinner handoff optimization + volatile int _SpinFreq ; // Spin 1-out-of-N attempts: success rate + volatile int _SpinClock ; + volatile int _SpinDuration ; + volatile intptr_t _SpinState ; // MCS/CLH list of spinners + + // TODO-FIXME: _count, _waiters and _recursions should be of + // type int, or int32_t but not intptr_t. There's no reason + // to use 64-bit fields for these variables on a 64-bit JVM. + + volatile intptr_t _count; // reference count to prevent reclaimation/deflation + // at stop-the-world time. See deflate_idle_monitors(). + // _count is approximately |_WaitSet| + |_EntryList| + volatile intptr_t _waiters; // number of waiting threads + ObjectWaiter * volatile _WaitSet; // LL of threads wait()ing on the monitor + volatile int _WaitSetLock; // protects Wait Queue - simple spinlock + + public: + int _QMix ; // Mixed prepend queue discipline + ObjectMonitor * FreeNext ; // Free list linkage + intptr_t StatA, StatsB ; + +}; diff --git a/hotspot/src/share/vm/runtime/objectMonitor.inline.hpp b/hotspot/src/share/vm/runtime/objectMonitor.inline.hpp new file mode 100644 index 00000000000..63bb7cb263d --- /dev/null +++ b/hotspot/src/share/vm/runtime/objectMonitor.inline.hpp @@ -0,0 +1,110 @@ +/* + * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline intptr_t ObjectMonitor::is_entered(TRAPS) const { + if (THREAD == _owner || THREAD->is_lock_owned((address) _owner)) { + return 1; + } + return 0; +} + +inline markOop ObjectMonitor::header() const { + return _header; +} + +inline void ObjectMonitor::set_header(markOop hdr) { + _header = hdr; +} + +inline intptr_t ObjectMonitor::count() const { + return _count; +} + +inline void ObjectMonitor::set_count(intptr_t count) { + _count= count; +} + +inline intptr_t ObjectMonitor::waiters() const { + return _waiters; +} + +inline void* ObjectMonitor::owner() const { + return _owner; +} + +inline void ObjectMonitor::clear() { + assert(_header, "Fatal logic error in ObjectMonitor header!"); + assert(_count == 0, "Fatal logic error in ObjectMonitor count!"); + assert(_waiters == 0, "Fatal logic error in ObjectMonitor waiters!"); + assert(_recursions == 0, "Fatal logic error in ObjectMonitor recursions!"); + assert(_object, "Fatal logic error in ObjectMonitor object!"); + assert(_owner == 0, "Fatal logic error in ObjectMonitor owner!"); + + _header = NULL; + _object = NULL; +} + + +inline void* ObjectMonitor::object() const { + return _object; +} + +inline void* ObjectMonitor::object_addr() { + return (void *)(&_object); +} + +inline void ObjectMonitor::set_object(void* obj) { + _object = obj; +} + +inline bool ObjectMonitor::check(TRAPS) { + if (THREAD != _owner) { + if (THREAD->is_lock_owned((address) _owner)) { + _owner = THREAD; // regain ownership of inflated monitor + OwnerIsThread = 1 ; + assert (_recursions == 0, "invariant") ; + } else { + check_slow(THREAD); + return false; + } + } + return true; +} + + +// return number of threads contending for this monitor +inline intptr_t ObjectMonitor::contentions() const { + return _count; +} + +inline void ObjectMonitor::set_owner(void* owner) { + _owner = owner; + _recursions = 0; + _count = 0; +} + + +// here are the platform-dependent bodies: + +# include "incls/_objectMonitor_pd.inline.hpp.incl" diff --git a/hotspot/src/share/vm/runtime/orderAccess.cpp b/hotspot/src/share/vm/runtime/orderAccess.cpp new file mode 100644 index 00000000000..392b5978126 --- /dev/null +++ b/hotspot/src/share/vm/runtime/orderAccess.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_orderAccess.cpp.incl" + +volatile intptr_t OrderAccess::dummy = 0; diff --git a/hotspot/src/share/vm/runtime/orderAccess.hpp b/hotspot/src/share/vm/runtime/orderAccess.hpp new file mode 100644 index 00000000000..a2040ed8488 --- /dev/null +++ b/hotspot/src/share/vm/runtime/orderAccess.hpp @@ -0,0 +1,303 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Memory Access Ordering Model +// +// This interface is based on the JSR-133 Cookbook for Compiler Writers +// and on the IA64 memory model. It is the dynamic equivalent of the +// C/C++ volatile specifier. I.e., volatility restricts compile-time +// memory access reordering in a way similar to what we want to occur +// at runtime. +// +// In the following, the terms 'previous', 'subsequent', 'before', +// 'after', 'preceeding' and 'succeeding' refer to program order. The +// terms 'down' and 'below' refer to forward load or store motion +// relative to program order, while 'up' and 'above' refer to backward +// motion. +// +// +// We define four primitive memory barrier operations. +// +// LoadLoad: Load1(s); LoadLoad; Load2 +// +// Ensures that Load1 completes (obtains the value it loads from memory) +// before Load2 and any subsequent load operations. Loads before Load1 +// may *not* float below Load2 and any subsequent load operations. +// +// StoreStore: Store1(s); StoreStore; Store2 +// +// Ensures that Store1 completes (the effect on memory of Store1 is made +// visible to other processors) before Store2 and any subsequent store +// operations. Stores before Store1 may *not* float below Store2 and any +// subsequent store operations. +// +// LoadStore: Load1(s); LoadStore; Store2 +// +// Ensures that Load1 completes before Store2 and any subsequent store +// operations. Loads before Load1 may *not* float below Store2 and any +// subseqeuent store operations. +// +// StoreLoad: Store1(s); StoreLoad; Load2 +// +// Ensures that Store1 completes before Load2 and any subsequent load +// operations. Stores before Store1 may *not* float below Load2 and any +// subseqeuent load operations. +// +// +// We define two further operations, 'release' and 'acquire'. They are +// mirror images of each other. +// +// Execution by a processor of release makes the effect of all memory +// accesses issued by it previous to the release visible to all +// processors *before* the release completes. The effect of subsequent +// memory accesses issued by it *may* be made visible *before* the +// release. I.e., subsequent memory accesses may float above the +// release, but prior ones may not float below it. +// +// Execution by a processor of acquire makes the effect of all memory +// accesses issued by it subsequent to the acquire visible to all +// processors *after* the acquire completes. The effect of prior memory +// accesses issued by it *may* be made visible *after* the acquire. +// I.e., prior memory accesses may float below the acquire, but +// subsequent ones may not float above it. +// +// Finally, we define a 'fence' operation, which conceptually is a +// release combined with an acquire. In the real world these operations +// require one or more machine instructions which can float above and +// below the release or acquire, so we usually can't just issue the +// release-acquire back-to-back. All machines we know of implement some +// sort of memory fence instruction. +// +// +// The standalone implementations of release and acquire need an associated +// dummy volatile store or load respectively. To avoid redundant operations, +// we can define the composite operators: 'release_store', 'store_fence' and +// 'load_acquire'. Here's a summary of the machine instructions corresponding +// to each operation. +// +// sparc RMO ia64 x86 +// --------------------------------------------------------------------- +// fence membar #LoadStore | mf lock addl 0,(sp) +// #StoreStore | +// #LoadLoad | +// #StoreLoad +// +// release membar #LoadStore | st.rel [sp]=r0 movl $0, +// #StoreStore +// st %g0,[] +// +// acquire ld [%sp],%g0 ld.acq =[sp] movl (sp), +// membar #LoadLoad | +// #LoadStore +// +// release_store membar #LoadStore | st.rel +// #StoreStore +// st +// +// store_fence st st lock xchg +// fence mf +// +// load_acquire ld ld.acq +// membar #LoadLoad | +// #LoadStore +// +// Using only release_store and load_acquire, we can implement the +// following ordered sequences. +// +// 1. load, load == load_acquire, load +// or load_acquire, load_acquire +// 2. load, store == load, release_store +// or load_acquire, store +// or load_acquire, release_store +// 3. store, store == store, release_store +// or release_store, release_store +// +// These require no membar instructions for sparc-TSO and no extra +// instructions for ia64. +// +// Ordering a load relative to preceding stores requires a store_fence, +// which implies a membar #StoreLoad between the store and load under +// sparc-TSO. A fence is required by ia64. On x86, we use locked xchg. +// +// 4. store, load == store_fence, load +// +// Use store_fence to make sure all stores done in an 'interesting' +// region are made visible prior to both subsequent loads and stores. +// +// Conventional usage is to issue a load_acquire for ordered loads. Use +// release_store for ordered stores when you care only that prior stores +// are visible before the release_store, but don't care exactly when the +// store associated with the release_store becomes visible. Use +// release_store_fence to update values like the thread state, where we +// don't want the current thread to continue until all our prior memory +// accesses (including the new thread state) are visible to other threads. +// +// +// C++ Volatility +// +// C++ guarantees ordering at operations termed 'sequence points' (defined +// to be volatile accesses and calls to library I/O functions). 'Side +// effects' (defined as volatile accesses, calls to library I/O functions +// and object modification) previous to a sequence point must be visible +// at that sequence point. See the C++ standard, section 1.9, titled +// "Program Execution". This means that all barrier implementations, +// including standalone loadload, storestore, loadstore, storeload, acquire +// and release must include a sequence point, usually via a volatile memory +// access. Other ways to guarantee a sequence point are, e.g., use of +// indirect calls and linux's __asm__ volatile. +// +// +// os::is_MP Considered Redundant +// +// Callers of this interface do not need to test os::is_MP() before +// issuing an operation. The test is taken care of by the implementation +// of the interface (depending on the vm version and platform, the test +// may or may not be actually done by the implementation). +// +// +// A Note on Memory Ordering and Cache Coherency +// +// Cache coherency and memory ordering are orthogonal concepts, though they +// interact. E.g., all existing itanium machines are cache-coherent, but +// the hardware can freely reorder loads wrt other loads unless it sees a +// load-acquire instruction. All existing sparc machines are cache-coherent +// and, unlike itanium, TSO guarantees that the hardware orders loads wrt +// loads and stores, and stores wrt to each other. +// +// Consider the implementation of loadload. *If* your platform *isn't* +// cache-coherent, then loadload must not only prevent hardware load +// instruction reordering, but it must *also* ensure that subsequent +// loads from addresses that could be written by other processors (i.e., +// that are broadcast by other processors) go all the way to the first +// level of memory shared by those processors and the one issuing +// the loadload. +// +// So if we have a MP that has, say, a per-processor D$ that doesn't see +// writes by other processors, and has a shared E$ that does, the loadload +// barrier would have to make sure that either +// +// 1. cache lines in the issuing processor's D$ that contained data from +// addresses that could be written by other processors are invalidated, so +// subsequent loads from those addresses go to the E$, (it could do this +// by tagging such cache lines as 'shared', though how to tell the hardware +// to do the tagging is an interesting problem), or +// +// 2. there never are such cache lines in the issuing processor's D$, which +// means all references to shared data (however identified: see above) +// bypass the D$ (i.e., are satisfied from the E$). +// +// If your machine doesn't have an E$, substitute 'main memory' for 'E$'. +// +// Either of these alternatives is a pain, so no current machine we know of +// has incoherent caches. +// +// If loadload didn't have these properties, the store-release sequence for +// publishing a shared data structure wouldn't work, because a processor +// trying to read data newly published by another processor might go to +// its own incoherent caches to satisfy the read instead of to the newly +// written shared memory. +// +// +// NOTE WELL!! +// +// A Note on MutexLocker and Friends +// +// See mutexLocker.hpp. We assume throughout the VM that MutexLocker's +// and friends' constructors do a fence, a lock and an acquire *in that +// order*. And that their destructors do a release and unlock, in *that* +// order. If their implementations change such that these assumptions +// are violated, a whole lot of code will break. + +class OrderAccess : AllStatic { + public: + static void loadload(); + static void storestore(); + static void loadstore(); + static void storeload(); + + static void acquire(); + static void release(); + static void fence(); + + static jbyte load_acquire(volatile jbyte* p); + static jshort load_acquire(volatile jshort* p); + static jint load_acquire(volatile jint* p); + static jlong load_acquire(volatile jlong* p); + static jubyte load_acquire(volatile jubyte* p); + static jushort load_acquire(volatile jushort* p); + static juint load_acquire(volatile juint* p); + static julong load_acquire(volatile julong* p); + static jfloat load_acquire(volatile jfloat* p); + static jdouble load_acquire(volatile jdouble* p); + + static intptr_t load_ptr_acquire(volatile intptr_t* p); + static void* load_ptr_acquire(volatile void* p); + static void* load_ptr_acquire(const volatile void* p); + + static void release_store(volatile jbyte* p, jbyte v); + static void release_store(volatile jshort* p, jshort v); + static void release_store(volatile jint* p, jint v); + static void release_store(volatile jlong* p, jlong v); + static void release_store(volatile jubyte* p, jubyte v); + static void release_store(volatile jushort* p, jushort v); + static void release_store(volatile juint* p, juint v); + static void release_store(volatile julong* p, julong v); + static void release_store(volatile jfloat* p, jfloat v); + static void release_store(volatile jdouble* p, jdouble v); + + static void release_store_ptr(volatile intptr_t* p, intptr_t v); + static void release_store_ptr(volatile void* p, void* v); + + static void store_fence(jbyte* p, jbyte v); + static void store_fence(jshort* p, jshort v); + static void store_fence(jint* p, jint v); + static void store_fence(jlong* p, jlong v); + static void store_fence(jubyte* p, jubyte v); + static void store_fence(jushort* p, jushort v); + static void store_fence(juint* p, juint v); + static void store_fence(julong* p, julong v); + static void store_fence(jfloat* p, jfloat v); + static void store_fence(jdouble* p, jdouble v); + + static void store_ptr_fence(intptr_t* p, intptr_t v); + static void store_ptr_fence(void** p, void* v); + + static void release_store_fence(volatile jbyte* p, jbyte v); + static void release_store_fence(volatile jshort* p, jshort v); + static void release_store_fence(volatile jint* p, jint v); + static void release_store_fence(volatile jlong* p, jlong v); + static void release_store_fence(volatile jubyte* p, jubyte v); + static void release_store_fence(volatile jushort* p, jushort v); + static void release_store_fence(volatile juint* p, juint v); + static void release_store_fence(volatile julong* p, julong v); + static void release_store_fence(volatile jfloat* p, jfloat v); + static void release_store_fence(volatile jdouble* p, jdouble v); + + static void release_store_ptr_fence(volatile intptr_t* p, intptr_t v); + static void release_store_ptr_fence(volatile void* p, void* v); + + // In order to force a memory access, implementations may + // need a volatile externally visible dummy variable. + static volatile intptr_t dummy; +}; diff --git a/hotspot/src/share/vm/runtime/os.cpp b/hotspot/src/share/vm/runtime/os.cpp new file mode 100644 index 00000000000..72dfb2265ff --- /dev/null +++ b/hotspot/src/share/vm/runtime/os.cpp @@ -0,0 +1,1108 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_os.cpp.incl" + +# include + +OSThread* os::_starting_thread = NULL; +address os::_polling_page = NULL; +volatile int32_t* os::_mem_serialize_page = NULL; +uintptr_t os::_serialize_page_mask = 0; +long os::_rand_seed = 1; +int os::_processor_count = 0; +volatile jlong os::_global_time = 0; +volatile int os::_global_time_lock = 0; +bool os::_use_global_time = false; +size_t os::_page_sizes[os::page_sizes_max]; + +#ifndef PRODUCT +int os::num_mallocs = 0; // # of calls to malloc/realloc +size_t os::alloc_bytes = 0; // # of bytes allocated +int os::num_frees = 0; // # of calls to free +#endif + +// Atomic read of a jlong is assured by a seqlock; see update_global_time() +jlong os::read_global_time() { +#ifdef _LP64 + return _global_time; +#else + volatile int lock; + volatile jlong current_time; + int ctr = 0; + + for (;;) { + lock = _global_time_lock; + + // spin while locked + while ((lock & 0x1) != 0) { + ++ctr; + if ((ctr & 0xFFF) == 0) { + // Guarantee writer progress. Can't use yield; yield is advisory + // and has almost no effect on some platforms. Don't need a state + // transition - the park call will return promptly. + assert(Thread::current() != NULL, "TLS not initialized"); + assert(Thread::current()->_ParkEvent != NULL, "sync not initialized"); + Thread::current()->_ParkEvent->park(1); + } + lock = _global_time_lock; + } + + OrderAccess::loadload(); + current_time = _global_time; + OrderAccess::loadload(); + + // ratify seqlock value + if (lock == _global_time_lock) { + return current_time; + } + } +#endif +} + +// +// NOTE - Assumes only one writer thread! +// +// We use a seqlock to guarantee that jlong _global_time is updated +// atomically on 32-bit platforms. A locked value is indicated by +// the lock variable LSB == 1. Readers will initially read the lock +// value, spinning until the LSB == 0. They then speculatively read +// the global time value, then re-read the lock value to ensure that +// it hasn't changed. If the lock value has changed, the entire read +// sequence is retried. +// +// Writers simply set the LSB = 1 (i.e. increment the variable), +// update the global time, then release the lock and bump the version +// number (i.e. increment the variable again.) In this case we don't +// even need a CAS since we ensure there's only one writer. +// +void os::update_global_time() { +#ifdef _LP64 + _global_time = timeofday(); +#else + assert((_global_time_lock & 0x1) == 0, "multiple writers?"); + jlong current_time = timeofday(); + _global_time_lock++; // lock + OrderAccess::storestore(); + _global_time = current_time; + OrderAccess::storestore(); + _global_time_lock++; // unlock +#endif +} + +// Fill in buffer with current local time as an ISO-8601 string. +// E.g., yyyy-mm-ddThh:mm:ss-zzzz. +// Returns buffer, or NULL if it failed. +// This would mostly be a call to +// strftime(...., "%Y-%m-%d" "T" "%H:%M:%S" "%z", ....) +// except that on Windows the %z behaves badly, so we do it ourselves. +// Also, people wanted milliseconds on there, +// and strftime doesn't do milliseconds. +char* os::iso8601_time(char* buffer, size_t buffer_length) { + // Output will be of the form "YYYY-MM-DDThh:mm:ss.mmm+zzzz\0" + // 1 2 + // 12345678901234567890123456789 + static const char* iso8601_format = + "%04d-%02d-%02dT%02d:%02d:%02d.%03d%c%02d%02d"; + static const size_t needed_buffer = 29; + + // Sanity check the arguments + if (buffer == NULL) { + assert(false, "NULL buffer"); + return NULL; + } + if (buffer_length < needed_buffer) { + assert(false, "buffer_length too small"); + return NULL; + } + // Get the current time + jlong milliseconds_since_19700101 = timeofday(); + const int milliseconds_per_microsecond = 1000; + const time_t seconds_since_19700101 = + milliseconds_since_19700101 / milliseconds_per_microsecond; + const int milliseconds_after_second = + milliseconds_since_19700101 % milliseconds_per_microsecond; + // Convert the time value to a tm and timezone variable + const struct tm *time_struct_temp = localtime(&seconds_since_19700101); + if (time_struct_temp == NULL) { + assert(false, "Failed localtime"); + return NULL; + } + // Save the results of localtime + const struct tm time_struct = *time_struct_temp; + const time_t zone = timezone; + + // If daylight savings time is in effect, + // we are 1 hour East of our time zone + const time_t seconds_per_minute = 60; + const time_t minutes_per_hour = 60; + const time_t seconds_per_hour = seconds_per_minute * minutes_per_hour; + time_t UTC_to_local = zone; + if (time_struct.tm_isdst > 0) { + UTC_to_local = UTC_to_local - seconds_per_hour; + } + // Compute the time zone offset. + // localtime(3C) sets timezone to the difference (in seconds) + // between UTC and and local time. + // ISO 8601 says we need the difference between local time and UTC, + // we change the sign of the localtime(3C) result. + const time_t local_to_UTC = -(UTC_to_local); + // Then we have to figure out if if we are ahead (+) or behind (-) UTC. + char sign_local_to_UTC = '+'; + time_t abs_local_to_UTC = local_to_UTC; + if (local_to_UTC < 0) { + sign_local_to_UTC = '-'; + abs_local_to_UTC = -(abs_local_to_UTC); + } + // Convert time zone offset seconds to hours and minutes. + const time_t zone_hours = (abs_local_to_UTC / seconds_per_hour); + const time_t zone_min = + ((abs_local_to_UTC % seconds_per_hour) / seconds_per_minute); + + // Print an ISO 8601 date and time stamp into the buffer + const int year = 1900 + time_struct.tm_year; + const int month = 1 + time_struct.tm_mon; + const int printed = jio_snprintf(buffer, buffer_length, iso8601_format, + year, + month, + time_struct.tm_mday, + time_struct.tm_hour, + time_struct.tm_min, + time_struct.tm_sec, + milliseconds_after_second, + sign_local_to_UTC, + zone_hours, + zone_min); + if (printed == 0) { + assert(false, "Failed jio_printf"); + return NULL; + } + return buffer; +} + +OSReturn os::set_priority(Thread* thread, ThreadPriority p) { +#ifdef ASSERT + if (!(!thread->is_Java_thread() || + Thread::current() == thread || + Threads_lock->owned_by_self() + || thread->is_Compiler_thread() + )) { + assert(false, "possibility of dangling Thread pointer"); + } +#endif + + if (p >= MinPriority && p <= MaxPriority) { + int priority = java_to_os_priority[p]; + return set_native_priority(thread, priority); + } else { + assert(false, "Should not happen"); + return OS_ERR; + } +} + + +OSReturn os::get_priority(const Thread* const thread, ThreadPriority& priority) { + int p; + int os_prio; + OSReturn ret = get_native_priority(thread, &os_prio); + if (ret != OS_OK) return ret; + + for (p = MaxPriority; p > MinPriority && java_to_os_priority[p] > os_prio; p--) ; + priority = (ThreadPriority)p; + return OS_OK; +} + + +// --------------------- sun.misc.Signal (optional) --------------------- + + +// SIGBREAK is sent by the keyboard to query the VM state +#ifndef SIGBREAK +#define SIGBREAK SIGQUIT +#endif + +// sigexitnum_pd is a platform-specific special signal used for terminating the Signal thread. + + +static void signal_thread_entry(JavaThread* thread, TRAPS) { + os::set_priority(thread, NearMaxPriority); + while (true) { + int sig; + { + // FIXME : Currently we have not decieded what should be the status + // for this java thread blocked here. Once we decide about + // that we should fix this. + sig = os::signal_wait(); + } + if (sig == os::sigexitnum_pd()) { + // Terminate the signal thread + return; + } + + switch (sig) { + case SIGBREAK: { + // Check if the signal is a trigger to start the Attach Listener - in that + // case don't print stack traces. + if (!DisableAttachMechanism && AttachListener::is_init_trigger()) { + continue; + } + // Print stack traces + // Any SIGBREAK operations added here should make sure to flush + // the output stream (e.g. tty->flush()) after output. See 4803766. + // Each module also prints an extra carriage return after its output. + VM_PrintThreads op; + VMThread::execute(&op); + VM_PrintJNI jni_op; + VMThread::execute(&jni_op); + VM_FindDeadlocks op1(tty); + VMThread::execute(&op1); + Universe::print_heap_at_SIGBREAK(); + if (PrintClassHistogram) { + VM_GC_HeapInspection op1(gclog_or_tty, true /* force full GC before heap inspection */); + VMThread::execute(&op1); + } + if (JvmtiExport::should_post_data_dump()) { + JvmtiExport::post_data_dump(); + } + break; + } + default: { + // Dispatch the signal to java + HandleMark hm(THREAD); + klassOop k = SystemDictionary::resolve_or_null(vmSymbolHandles::sun_misc_Signal(), THREAD); + KlassHandle klass (THREAD, k); + if (klass.not_null()) { + JavaValue result(T_VOID); + JavaCallArguments args; + args.push_int(sig); + JavaCalls::call_static( + &result, + klass, + vmSymbolHandles::dispatch_name(), + vmSymbolHandles::int_void_signature(), + &args, + THREAD + ); + } + if (HAS_PENDING_EXCEPTION) { + // tty is initialized early so we don't expect it to be null, but + // if it is we can't risk doing an initialization that might + // trigger additional out-of-memory conditions + if (tty != NULL) { + char klass_name[256]; + char tmp_sig_name[16]; + const char* sig_name = "UNKNOWN"; + instanceKlass::cast(PENDING_EXCEPTION->klass())-> + name()->as_klass_external_name(klass_name, 256); + if (os::exception_name(sig, tmp_sig_name, 16) != NULL) + sig_name = tmp_sig_name; + warning("Exception %s occurred dispatching signal %s to handler" + "- the VM may need to be forcibly terminated", + klass_name, sig_name ); + } + CLEAR_PENDING_EXCEPTION; + } + } + } + } +} + + +void os::signal_init() { + if (!ReduceSignalUsage) { + // Setup JavaThread for processing signals + EXCEPTION_MARK; + klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_Thread(), true, CHECK); + instanceKlassHandle klass (THREAD, k); + instanceHandle thread_oop = klass->allocate_instance_handle(CHECK); + + const char thread_name[] = "Signal Dispatcher"; + Handle string = java_lang_String::create_from_str(thread_name, CHECK); + + // Initialize thread_oop to put it into the system threadGroup + Handle thread_group (THREAD, Universe::system_thread_group()); + JavaValue result(T_VOID); + JavaCalls::call_special(&result, thread_oop, + klass, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::threadgroup_string_void_signature(), + thread_group, + string, + CHECK); + + KlassHandle group(THREAD, SystemDictionary::threadGroup_klass()); + JavaCalls::call_special(&result, + thread_group, + group, + vmSymbolHandles::add_method_name(), + vmSymbolHandles::thread_void_signature(), + thread_oop, // ARG 1 + CHECK); + + os::signal_init_pd(); + + { MutexLocker mu(Threads_lock); + JavaThread* signal_thread = new JavaThread(&signal_thread_entry); + + // At this point it may be possible that no osthread was created for the + // JavaThread due to lack of memory. We would have to throw an exception + // in that case. However, since this must work and we do not allow + // exceptions anyway, check and abort if this fails. + if (signal_thread == NULL || signal_thread->osthread() == NULL) { + vm_exit_during_initialization("java.lang.OutOfMemoryError", + "unable to create new native thread"); + } + + java_lang_Thread::set_thread(thread_oop(), signal_thread); + java_lang_Thread::set_priority(thread_oop(), NearMaxPriority); + java_lang_Thread::set_daemon(thread_oop()); + + signal_thread->set_threadObj(thread_oop()); + Threads::add(signal_thread); + Thread::start(signal_thread); + } + // Handle ^BREAK + os::signal(SIGBREAK, os::user_handler()); + } +} + + +void os::terminate_signal_thread() { + if (!ReduceSignalUsage) + signal_notify(sigexitnum_pd()); +} + + +// --------------------- loading libraries --------------------- + +typedef jint (JNICALL *JNI_OnLoad_t)(JavaVM *, void *); +extern struct JavaVM_ main_vm; + +static void* _native_java_library = NULL; + +void* os::native_java_library() { + if (_native_java_library == NULL) { + char buffer[JVM_MAXPATHLEN]; + char ebuf[1024]; + + // Try to load verify dll first. In 1.3 java dll depends on it and is not always + // able to find it when the loading executable is outside the JDK. + // In order to keep working with 1.2 we ignore any loading errors. + hpi::dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(), "verify"); + hpi::dll_load(buffer, ebuf, sizeof(ebuf)); + + // Load java dll + hpi::dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(), "java"); + _native_java_library = hpi::dll_load(buffer, ebuf, sizeof(ebuf)); + if (_native_java_library == NULL) { + vm_exit_during_initialization("Unable to load native library", ebuf); + } + // The JNI_OnLoad handling is normally done by method load in java.lang.ClassLoader$NativeLibrary, + // but the VM loads the base library explicitly so we have to check for JNI_OnLoad as well + const char *onLoadSymbols[] = JNI_ONLOAD_SYMBOLS; + JNI_OnLoad_t JNI_OnLoad = CAST_TO_FN_PTR(JNI_OnLoad_t, hpi::dll_lookup(_native_java_library, onLoadSymbols[0])); + if (JNI_OnLoad != NULL) { + JavaThread* thread = JavaThread::current(); + ThreadToNativeFromVM ttn(thread); + HandleMark hm(thread); + jint ver = (*JNI_OnLoad)(&main_vm, NULL); + if (!Threads::is_supported_jni_version_including_1_1(ver)) { + vm_exit_during_initialization("Unsupported JNI version"); + } + } + } + return _native_java_library; +} + +// --------------------- heap allocation utilities --------------------- + +char *os::strdup(const char *str) { + size_t size = strlen(str); + char *dup_str = (char *)malloc(size + 1); + if (dup_str == NULL) return NULL; + strcpy(dup_str, str); + return dup_str; +} + + + +#ifdef ASSERT +#define space_before (MallocCushion + sizeof(double)) +#define space_after MallocCushion +#define size_addr_from_base(p) (size_t*)(p + space_before - sizeof(size_t)) +#define size_addr_from_obj(p) ((size_t*)p - 1) +// MallocCushion: size of extra cushion allocated around objects with +UseMallocOnly +// NB: cannot be debug variable, because these aren't set from the command line until +// *after* the first few allocs already happened +#define MallocCushion 16 +#else +#define space_before 0 +#define space_after 0 +#define size_addr_from_base(p) should not use w/o ASSERT +#define size_addr_from_obj(p) should not use w/o ASSERT +#define MallocCushion 0 +#endif +#define paranoid 0 /* only set to 1 if you suspect checking code has bug */ + +#ifdef ASSERT +inline size_t get_size(void* obj) { + size_t size = *size_addr_from_obj(obj); + if (size < 0 ) + fatal2("free: size field of object #%p was overwritten (%lu)", obj, size); + return size; +} + +u_char* find_cushion_backwards(u_char* start) { + u_char* p = start; + while (p[ 0] != badResourceValue || p[-1] != badResourceValue || + p[-2] != badResourceValue || p[-3] != badResourceValue) p--; + // ok, we have four consecutive marker bytes; find start + u_char* q = p - 4; + while (*q == badResourceValue) q--; + return q + 1; +} + +u_char* find_cushion_forwards(u_char* start) { + u_char* p = start; + while (p[0] != badResourceValue || p[1] != badResourceValue || + p[2] != badResourceValue || p[3] != badResourceValue) p++; + // ok, we have four consecutive marker bytes; find end of cushion + u_char* q = p + 4; + while (*q == badResourceValue) q++; + return q - MallocCushion; +} + +void print_neighbor_blocks(void* ptr) { + // find block allocated before ptr (not entirely crash-proof) + if (MallocCushion < 4) { + tty->print_cr("### cannot find previous block (MallocCushion < 4)"); + return; + } + u_char* start_of_this_block = (u_char*)ptr - space_before; + u_char* end_of_prev_block_data = start_of_this_block - space_after -1; + // look for cushion in front of prev. block + u_char* start_of_prev_block = find_cushion_backwards(end_of_prev_block_data); + ptrdiff_t size = *size_addr_from_base(start_of_prev_block); + u_char* obj = start_of_prev_block + space_before; + if (size <= 0 ) { + // start is bad; mayhave been confused by OS data inbetween objects + // search one more backwards + start_of_prev_block = find_cushion_backwards(start_of_prev_block); + size = *size_addr_from_base(start_of_prev_block); + obj = start_of_prev_block + space_before; + } + + if (start_of_prev_block + space_before + size + space_after == start_of_this_block) { + tty->print_cr("### previous object: %p (%ld bytes)", obj, size); + } else { + tty->print_cr("### previous object (not sure if correct): %p (%ld bytes)", obj, size); + } + + // now find successor block + u_char* start_of_next_block = (u_char*)ptr + *size_addr_from_obj(ptr) + space_after; + start_of_next_block = find_cushion_forwards(start_of_next_block); + u_char* next_obj = start_of_next_block + space_before; + ptrdiff_t next_size = *size_addr_from_base(start_of_next_block); + if (start_of_next_block[0] == badResourceValue && + start_of_next_block[1] == badResourceValue && + start_of_next_block[2] == badResourceValue && + start_of_next_block[3] == badResourceValue) { + tty->print_cr("### next object: %p (%ld bytes)", next_obj, next_size); + } else { + tty->print_cr("### next object (not sure if correct): %p (%ld bytes)", next_obj, next_size); + } +} + + +void report_heap_error(void* memblock, void* bad, const char* where) { + tty->print_cr("## nof_mallocs = %d, nof_frees = %d", os::num_mallocs, os::num_frees); + tty->print_cr("## memory stomp: byte at %p %s object %p", bad, where, memblock); + print_neighbor_blocks(memblock); + fatal("memory stomping error"); +} + +void verify_block(void* memblock) { + size_t size = get_size(memblock); + if (MallocCushion) { + u_char* ptr = (u_char*)memblock - space_before; + for (int i = 0; i < MallocCushion; i++) { + if (ptr[i] != badResourceValue) { + report_heap_error(memblock, ptr+i, "in front of"); + } + } + u_char* end = (u_char*)memblock + size + space_after; + for (int j = -MallocCushion; j < 0; j++) { + if (end[j] != badResourceValue) { + report_heap_error(memblock, end+j, "after"); + } + } + } +} +#endif + +void* os::malloc(size_t size) { + NOT_PRODUCT(num_mallocs++); + NOT_PRODUCT(alloc_bytes += size); + + if (size == 0) { + // return a valid pointer if size is zero + // if NULL is returned the calling functions assume out of memory. + size = 1; + } + + NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap()); + u_char* ptr = (u_char*)::malloc(size + space_before + space_after); +#ifdef ASSERT + if (ptr == NULL) return NULL; + if (MallocCushion) { + for (u_char* p = ptr; p < ptr + MallocCushion; p++) *p = (u_char)badResourceValue; + u_char* end = ptr + space_before + size; + for (u_char* pq = ptr+MallocCushion; pq < end; pq++) *pq = (u_char)uninitBlockPad; + for (u_char* q = end; q < end + MallocCushion; q++) *q = (u_char)badResourceValue; + } + // put size just before data + *size_addr_from_base(ptr) = size; +#endif + u_char* memblock = ptr + space_before; + if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) { + tty->print_cr("os::malloc caught, %lu bytes --> %p", size, memblock); + breakpoint(); + } + debug_only(if (paranoid) verify_block(memblock)); + if (PrintMalloc && tty != NULL) tty->print_cr("os::malloc %lu bytes --> %p", size, memblock); + return memblock; +} + + +void* os::realloc(void *memblock, size_t size) { + NOT_PRODUCT(num_mallocs++); + NOT_PRODUCT(alloc_bytes += size); +#ifndef ASSERT + return ::realloc(memblock, size); +#else + if (memblock == NULL) { + return os::malloc(size); + } + if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) { + tty->print_cr("os::realloc caught %p", memblock); + breakpoint(); + } + verify_block(memblock); + NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap()); + if (size == 0) return NULL; + // always move the block + void* ptr = malloc(size); + if (PrintMalloc) tty->print_cr("os::remalloc %lu bytes, %p --> %p", size, memblock, ptr); + // Copy to new memory if malloc didn't fail + if ( ptr != NULL ) { + memcpy(ptr, memblock, MIN2(size, get_size(memblock))); + if (paranoid) verify_block(ptr); + if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) { + tty->print_cr("os::realloc caught, %lu bytes --> %p", size, ptr); + breakpoint(); + } + free(memblock); + } + return ptr; +#endif +} + + +void os::free(void *memblock) { + NOT_PRODUCT(num_frees++); +#ifdef ASSERT + if (memblock == NULL) return; + if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) { + if (tty != NULL) tty->print_cr("os::free caught %p", memblock); + breakpoint(); + } + verify_block(memblock); + if (PrintMalloc && tty != NULL) + // tty->print_cr("os::free %p", memblock); + fprintf(stderr, "os::free %p\n", memblock); + NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap()); + // Added by detlefs. + if (MallocCushion) { + u_char* ptr = (u_char*)memblock - space_before; + for (u_char* p = ptr; p < ptr + MallocCushion; p++) { + guarantee(*p == badResourceValue, + "Thing freed should be malloc result."); + *p = (u_char)freeBlockPad; + } + size_t size = get_size(memblock); + u_char* end = ptr + space_before + size; + for (u_char* q = end; q < end + MallocCushion; q++) { + guarantee(*q == badResourceValue, + "Thing freed should be malloc result."); + *q = (u_char)freeBlockPad; + } + } +#endif + ::free((char*)memblock - space_before); +} + +void os::init_random(long initval) { + _rand_seed = initval; +} + + +long os::random() { + /* standard, well-known linear congruential random generator with + * next_rand = (16807*seed) mod (2**31-1) + * see + * (1) "Random Number Generators: Good Ones Are Hard to Find", + * S.K. Park and K.W. Miller, Communications of the ACM 31:10 (Oct 1988), + * (2) "Two Fast Implementations of the 'Minimal Standard' Random + * Number Generator", David G. Carta, Comm. ACM 33, 1 (Jan 1990), pp. 87-88. + */ + const long a = 16807; + const unsigned long m = 2147483647; + const long q = m / a; assert(q == 127773, "weird math"); + const long r = m % a; assert(r == 2836, "weird math"); + + // compute az=2^31p+q + unsigned long lo = a * (long)(_rand_seed & 0xFFFF); + unsigned long hi = a * (long)((unsigned long)_rand_seed >> 16); + lo += (hi & 0x7FFF) << 16; + + // if q overflowed, ignore the overflow and increment q + if (lo > m) { + lo &= m; + ++lo; + } + lo += hi >> 15; + + // if (p+q) overflowed, ignore the overflow and increment (p+q) + if (lo > m) { + lo &= m; + ++lo; + } + return (_rand_seed = lo); +} + +// The INITIALIZED state is distinguished from the SUSPENDED state because the +// conditions in which a thread is first started are different from those in which +// a suspension is resumed. These differences make it hard for us to apply the +// tougher checks when starting threads that we want to do when resuming them. +// However, when start_thread is called as a result of Thread.start, on a Java +// thread, the operation is synchronized on the Java Thread object. So there +// cannot be a race to start the thread and hence for the thread to exit while +// we are working on it. Non-Java threads that start Java threads either have +// to do so in a context in which races are impossible, or should do appropriate +// locking. + +void os::start_thread(Thread* thread) { + // guard suspend/resume + MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag); + OSThread* osthread = thread->osthread(); + osthread->set_state(RUNNABLE); + pd_start_thread(thread); +} + +//--------------------------------------------------------------------------- +// Helper functions for fatal error handler + +void os::print_hex_dump(outputStream* st, address start, address end, int unitsize) { + assert(unitsize == 1 || unitsize == 2 || unitsize == 4 || unitsize == 8, "just checking"); + + int cols = 0; + int cols_per_line = 0; + switch (unitsize) { + case 1: cols_per_line = 16; break; + case 2: cols_per_line = 8; break; + case 4: cols_per_line = 4; break; + case 8: cols_per_line = 2; break; + default: return; + } + + address p = start; + st->print(PTR_FORMAT ": ", start); + while (p < end) { + switch (unitsize) { + case 1: st->print("%02x", *(u1*)p); break; + case 2: st->print("%04x", *(u2*)p); break; + case 4: st->print("%08x", *(u4*)p); break; + case 8: st->print("%016" FORMAT64_MODIFIER "x", *(u8*)p); break; + } + p += unitsize; + cols++; + if (cols >= cols_per_line && p < end) { + cols = 0; + st->cr(); + st->print(PTR_FORMAT ": ", p); + } else { + st->print(" "); + } + } + st->cr(); +} + +void os::print_environment_variables(outputStream* st, const char** env_list, + char* buffer, int len) { + if (env_list) { + st->print_cr("Environment Variables:"); + + for (int i = 0; env_list[i] != NULL; i++) { + if (getenv(env_list[i], buffer, len)) { + st->print(env_list[i]); + st->print("="); + st->print_cr(buffer); + } + } + } +} + +void os::print_cpu_info(outputStream* st) { + // cpu + st->print("CPU:"); + st->print("total %d", os::processor_count()); + // It's not safe to query number of active processors after crash + // st->print("(active %d)", os::active_processor_count()); + st->print(" %s", VM_Version::cpu_features()); + st->cr(); +} + +void os::print_date_and_time(outputStream *st) { + time_t tloc; + (void)time(&tloc); + st->print("time: %s", ctime(&tloc)); // ctime adds newline. + + double t = os::elapsedTime(); + // NOTE: It tends to crash after a SEGV if we want to printf("%f",...) in + // Linux. Must be a bug in glibc ? Workaround is to round "t" to int + // before printf. We lost some precision, but who cares? + st->print_cr("elapsed time: %d seconds", (int)t); +} + + +// Looks like all platforms except IA64 can use the same function to check +// if C stack is walkable beyond current frame. The check for fp() is not +// necessary on Sparc, but it's harmless. +bool os::is_first_C_frame(frame* fr) { +#ifdef IA64 + // In order to walk native frames on Itanium, we need to access the unwind + // table, which is inside ELF. We don't want to parse ELF after fatal error, + // so return true for IA64. If we need to support C stack walking on IA64, + // this function needs to be moved to CPU specific files, as fp() on IA64 + // is register stack, which grows towards higher memory address. + return true; +#endif + + // Load up sp, fp, sender sp and sender fp, check for reasonable values. + // Check usp first, because if that's bad the other accessors may fault + // on some architectures. Ditto ufp second, etc. + uintptr_t fp_align_mask = (uintptr_t)(sizeof(address)-1); + // sp on amd can be 32 bit aligned. + uintptr_t sp_align_mask = (uintptr_t)(sizeof(int)-1); + + uintptr_t usp = (uintptr_t)fr->sp(); + if ((usp & sp_align_mask) != 0) return true; + + uintptr_t ufp = (uintptr_t)fr->fp(); + if ((ufp & fp_align_mask) != 0) return true; + + uintptr_t old_sp = (uintptr_t)fr->sender_sp(); + if ((old_sp & sp_align_mask) != 0) return true; + if (old_sp == 0 || old_sp == (uintptr_t)-1) return true; + + uintptr_t old_fp = (uintptr_t)fr->link(); + if ((old_fp & fp_align_mask) != 0) return true; + if (old_fp == 0 || old_fp == (uintptr_t)-1 || old_fp == ufp) return true; + + // stack grows downwards; if old_fp is below current fp or if the stack + // frame is too large, either the stack is corrupted or fp is not saved + // on stack (i.e. on x86, ebp may be used as general register). The stack + // is not walkable beyond current frame. + if (old_fp < ufp) return true; + if (old_fp - ufp > 64 * K) return true; + + return false; +} + +#ifdef ASSERT +extern "C" void test_random() { + const double m = 2147483647; + double mean = 0.0, variance = 0.0, t; + long reps = 10000; + unsigned long seed = 1; + + tty->print_cr("seed %ld for %ld repeats...", seed, reps); + os::init_random(seed); + long num; + for (int k = 0; k < reps; k++) { + num = os::random(); + double u = (double)num / m; + assert(u >= 0.0 && u <= 1.0, "bad random number!"); + + // calculate mean and variance of the random sequence + mean += u; + variance += (u*u); + } + mean /= reps; + variance /= (reps - 1); + + assert(num == 1043618065, "bad seed"); + tty->print_cr("mean of the 1st 10000 numbers: %f", mean); + tty->print_cr("variance of the 1st 10000 numbers: %f", variance); + const double eps = 0.0001; + t = fabsd(mean - 0.5018); + assert(t < eps, "bad mean"); + t = (variance - 0.3355) < 0.0 ? -(variance - 0.3355) : variance - 0.3355; + assert(t < eps, "bad variance"); +} +#endif + + +// Set up the boot classpath. + +char* os::format_boot_path(const char* format_string, + const char* home, + int home_len, + char fileSep, + char pathSep) { + assert((fileSep == '/' && pathSep == ':') || + (fileSep == '\\' && pathSep == ';'), "unexpected seperator chars"); + + // Scan the format string to determine the length of the actual + // boot classpath, and handle platform dependencies as well. + int formatted_path_len = 0; + const char* p; + for (p = format_string; *p != 0; ++p) { + if (*p == '%') formatted_path_len += home_len - 1; + ++formatted_path_len; + } + + char* formatted_path = NEW_C_HEAP_ARRAY(char, formatted_path_len + 1); + if (formatted_path == NULL) { + return NULL; + } + + // Create boot classpath from format, substituting separator chars and + // java home directory. + char* q = formatted_path; + for (p = format_string; *p != 0; ++p) { + switch (*p) { + case '%': + strcpy(q, home); + q += home_len; + break; + case '/': + *q++ = fileSep; + break; + case ':': + *q++ = pathSep; + break; + default: + *q++ = *p; + } + } + *q = '\0'; + + assert((q - formatted_path) == formatted_path_len, "formatted_path size botched"); + return formatted_path; +} + + +bool os::set_boot_path(char fileSep, char pathSep) { + + const char* home = Arguments::get_java_home(); + int home_len = (int)strlen(home); + + static const char* meta_index_dir_format = "%/lib/"; + static const char* meta_index_format = "%/lib/meta-index"; + char* meta_index = format_boot_path(meta_index_format, home, home_len, fileSep, pathSep); + if (meta_index == NULL) return false; + char* meta_index_dir = format_boot_path(meta_index_dir_format, home, home_len, fileSep, pathSep); + if (meta_index_dir == NULL) return false; + Arguments::set_meta_index_path(meta_index, meta_index_dir); + + // Any modification to the JAR-file list, for the boot classpath must be + // aligned with install/install/make/common/Pack.gmk. Note: boot class + // path class JARs, are stripped for StackMapTable to reduce download size. + static const char classpath_format[] = + "%/lib/resources.jar:" + "%/lib/rt.jar:" + "%/lib/sunrsasign.jar:" + "%/lib/jsse.jar:" + "%/lib/jce.jar:" + "%/lib/charsets.jar:" + "%/classes"; + char* sysclasspath = format_boot_path(classpath_format, home, home_len, fileSep, pathSep); + if (sysclasspath == NULL) return false; + Arguments::set_sysclasspath(sysclasspath); + + return true; +} + + +void os::set_memory_serialize_page(address page) { + int count = log2_intptr(sizeof(class JavaThread)) - log2_intptr(64); + _mem_serialize_page = (volatile int32_t *)page; + // We initialize the serialization page shift count here + // We assume a cache line size of 64 bytes + assert(SerializePageShiftCount == count, + "thread size changed, fix SerializePageShiftCount constant"); + set_serialize_page_mask((uintptr_t)(vm_page_size() - sizeof(int32_t))); +} + +// This method is called from signal handler when SIGSEGV occurs while the current +// thread tries to store to the "read-only" memory serialize page during state +// transition. +void os::block_on_serialize_page_trap() { + if (TraceSafepoint) { + tty->print_cr("Block until the serialize page permission restored"); + } + // When VMThread is holding the SerializePage_lock during modifying the + // access permission of the memory serialize page, the following call + // will block until the permission of that page is restored to rw. + // Generally, it is unsafe to manipulate locks in signal handlers, but in + // this case, it's OK as the signal is synchronous and we know precisely when + // it can occur. SerializePage_lock is a transiently-held leaf lock, so + // lock_without_safepoint_check should be safe. + SerializePage_lock->lock_without_safepoint_check(); + SerializePage_lock->unlock(); +} + +// Serialize all thread state variables +void os::serialize_thread_states() { + // On some platforms such as Solaris & Linux, the time duration of the page + // permission restoration is observed to be much longer than expected due to + // scheduler starvation problem etc. To avoid the long synchronization + // time and expensive page trap spinning, 'SerializePage_lock' is used to block + // the mutator thread if such case is encountered. Since this method is always + // called by VMThread during safepoint, lock_without_safepoint_check is used + // instead. See bug 6546278. + SerializePage_lock->lock_without_safepoint_check(); + os::protect_memory( (char *)os::get_memory_serialize_page(), os::vm_page_size() ); + os::unguard_memory( (char *)os::get_memory_serialize_page(), os::vm_page_size() ); + SerializePage_lock->unlock(); +} + +// Returns true if the current stack pointer is above the stack shadow +// pages, false otherwise. + +bool os::stack_shadow_pages_available(Thread *thread, methodHandle method) { + assert(StackRedPages > 0 && StackYellowPages > 0,"Sanity check"); + address sp = current_stack_pointer(); + // Check if we have StackShadowPages above the yellow zone. This parameter + // is dependant on the depth of the maximum VM call stack possible from + // the handler for stack overflow. 'instanceof' in the stack overflow + // handler or a println uses at least 8k stack of VM and native code + // respectively. + const int framesize_in_bytes = + Interpreter::size_top_interpreter_activation(method()) * wordSize; + int reserved_area = ((StackShadowPages + StackRedPages + StackYellowPages) + * vm_page_size()) + framesize_in_bytes; + // The very lower end of the stack + address stack_limit = thread->stack_base() - thread->stack_size(); + return (sp > (stack_limit + reserved_area)); +} + +size_t os::page_size_for_region(size_t region_min_size, size_t region_max_size, + uint min_pages) +{ + assert(min_pages > 0, "sanity"); + if (UseLargePages) { + const size_t max_page_size = region_max_size / min_pages; + + for (unsigned int i = 0; _page_sizes[i] != 0; ++i) { + const size_t sz = _page_sizes[i]; + const size_t mask = sz - 1; + if ((region_min_size & mask) == 0 && (region_max_size & mask) == 0) { + // The largest page size with no fragmentation. + return sz; + } + + if (sz <= max_page_size) { + // The largest page size that satisfies the min_pages requirement. + return sz; + } + } + } + + return vm_page_size(); +} + +#ifndef PRODUCT +void os::trace_page_sizes(const char* str, const size_t region_min_size, + const size_t region_max_size, const size_t page_size, + const char* base, const size_t size) +{ + if (TracePageSizes) { + tty->print_cr("%s: min=" SIZE_FORMAT " max=" SIZE_FORMAT + " pg_sz=" SIZE_FORMAT " base=" PTR_FORMAT + " size=" SIZE_FORMAT, + str, region_min_size, region_max_size, + page_size, base, size); + } +} +#endif // #ifndef PRODUCT + +// This is the working definition of a server class machine: +// >= 2 physical CPU's and >=2GB of memory, with some fuzz +// because the graphics memory (?) sometimes masks physical memory. +// If you want to change the definition of a server class machine +// on some OS or platform, e.g., >=4GB on Windohs platforms, +// then you'll have to parameterize this method based on that state, +// as was done for logical processors here, or replicate and +// specialize this method for each platform. (Or fix os to have +// some inheritance structure and use subclassing. Sigh.) +// If you want some platform to always or never behave as a server +// class machine, change the setting of AlwaysActAsServerClassMachine +// and NeverActAsServerClassMachine in globals*.hpp. +bool os::is_server_class_machine() { + // First check for the early returns + if (NeverActAsServerClassMachine) { + return false; + } + if (AlwaysActAsServerClassMachine) { + return true; + } + // Then actually look at the machine + bool result = false; + const unsigned int server_processors = 2; + const julong server_memory = 2UL * G; + // We seem not to get our full complement of memory. + // We allow some part (1/8?) of the memory to be "missing", + // based on the sizes of DIMMs, and maybe graphics cards. + const julong missing_memory = 256UL * M; + + /* Is this a server class machine? */ + if ((os::active_processor_count() >= (int)server_processors) && + (os::physical_memory() >= (server_memory - missing_memory))) { + const unsigned int logical_processors = + VM_Version::logical_processors_per_package(); + if (logical_processors > 1) { + const unsigned int physical_packages = + os::active_processor_count() / logical_processors; + if (physical_packages > server_processors) { + result = true; + } + } else { + result = true; + } + } + return result; +} diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp new file mode 100644 index 00000000000..4762efc5110 --- /dev/null +++ b/hotspot/src/share/vm/runtime/os.hpp @@ -0,0 +1,596 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// os defines the interface to operating system; this includes traditional +// OS services (time, I/O) as well as other functionality with system- +// dependent code. + +typedef void (*dll_func)(...); + +class Thread; +class JavaThread; +class Event; +class DLL; +class FileHandle; + +// %%%%% Moved ThreadState, START_FN, OSThread to new osThread.hpp. -- Rose + +// Platform-independent error return values from OS functions +enum OSReturn { + OS_OK = 0, // Operation was successful + OS_ERR = -1, // Operation failed + OS_INTRPT = -2, // Operation was interrupted + OS_TIMEOUT = -3, // Operation timed out + OS_NOMEM = -5, // Operation failed for lack of memory + OS_NORESOURCE = -6 // Operation failed for lack of nonmemory resource +}; + +enum ThreadPriority { // JLS 20.20.1-3 + NoPriority = -1, // Initial non-priority value + MinPriority = 1, // Minimum priority + NormPriority = 5, // Normal (non-daemon) priority + NearMaxPriority = 9, // High priority, used for VMThread + MaxPriority = 10 // Highest priority, used for WatcherThread + // ensures that VMThread doesn't starve profiler +}; + +// Typedef for structured exception handling support +typedef void (*java_call_t)(JavaValue* value, methodHandle* method, JavaCallArguments* args, Thread* thread); + +class os: AllStatic { + private: + enum { page_sizes_max = 9 }; // Size of _page_sizes array (8 plus a sentinel) + + static OSThread* _starting_thread; + static address _polling_page; + static volatile int32_t * _mem_serialize_page; + static uintptr_t _serialize_page_mask; + static volatile jlong _global_time; + static volatile int _global_time_lock; + static bool _use_global_time; + static size_t _page_sizes[page_sizes_max]; + + static void init_page_sizes(size_t default_page_size) { + _page_sizes[0] = default_page_size; + _page_sizes[1] = 0; // sentinel + } + + public: + + static void init(void); // Called before command line parsing + static jint init_2(void); // Called after command line parsing + + // File names are case-insensitive on windows only + // Override me as needed + static int file_name_strcmp(const char* s1, const char* s2); + + static bool getenv(const char* name, char* buffer, int len); + static bool have_special_privileges(); + + static jlong timeofday(); + static void enable_global_time() { _use_global_time = true; } + static void disable_global_time() { _use_global_time = false; } + static jlong read_global_time(); + static void update_global_time(); + static jlong javaTimeMillis(); + static jlong javaTimeNanos(); + static void javaTimeNanos_info(jvmtiTimerInfo *info_ptr); + static void run_periodic_checks(); + + + // Returns the elapsed time in seconds since the vm started. + static double elapsedTime(); + + // Returns real time in seconds since an arbitrary point + // in the past. + static bool getTimesSecs(double* process_real_time, + double* process_user_time, + double* process_system_time); + + // Interface to the performance counter + static jlong elapsed_counter(); + static jlong elapsed_frequency(); + + // Return current local time in a string (YYYY-MM-DD HH:MM:SS). + // It is MT safe, but not async-safe, as reading time zone + // information may require a lock on some platforms. + static char* local_time_string(char *buf, size_t buflen); + // Fill in buffer with current local time as an ISO-8601 string. + // E.g., YYYY-MM-DDThh:mm:ss.mmm+zzzz. + // Returns buffer, or NULL if it failed. + static char* iso8601_time(char* buffer, size_t buffer_length); + + // Interface for detecting multiprocessor system + static inline bool is_MP() { + assert(_processor_count > 0, "invalid processor count"); + return _processor_count > 1; + } + static julong available_memory(); + static julong physical_memory(); + static julong allocatable_physical_memory(julong size); + static bool is_server_class_machine(); + + // number of CPUs + static int processor_count() { + return _processor_count; + } + + // Returns the number of CPUs this process is currently allowed to run on. + // Note that on some OSes this can change dynamically. + static int active_processor_count(); + + // Bind processes to processors. + // This is a two step procedure: + // first you generate a distribution of processes to processors, + // then you bind processes according to that distribution. + // Compute a distribution for number of processes to processors. + // Stores the processor id's into the distribution array argument. + // Returns true if it worked, false if it didn't. + static bool distribute_processes(uint length, uint* distribution); + // Binds the current process to a processor. + // Returns true if it worked, false if it didn't. + static bool bind_to_processor(uint processor_id); + + // Interface for stack banging (predetect possible stack overflow for + // exception processing) There are guard pages, and above that shadow + // pages for stack overflow checking. + static bool uses_stack_guard_pages(); + static bool allocate_stack_guard_pages(); + static void bang_stack_shadow_pages(); + static bool stack_shadow_pages_available(Thread *thread, methodHandle method); + + // OS interface to Virtual Memory + + // Return the default page size. + static int vm_page_size(); + + // Return the page size to use for a region of memory. The min_pages argument + // is a hint intended to limit fragmentation; it says the returned page size + // should be <= region_max_size / min_pages. Because min_pages is a hint, + // this routine may return a size larger than region_max_size / min_pages. + // + // The current implementation ignores min_pages if a larger page size is an + // exact multiple of both region_min_size and region_max_size. This allows + // larger pages to be used when doing so would not cause fragmentation; in + // particular, a single page can be used when region_min_size == + // region_max_size == a supported page size. + static size_t page_size_for_region(size_t region_min_size, + size_t region_max_size, + uint min_pages); + + // Method for tracing page sizes returned by the above method; enabled by + // TracePageSizes. The region_{min,max}_size parameters should be the values + // passed to page_size_for_region() and page_size should be the result of that + // call. The (optional) base and size parameters should come from the + // ReservedSpace base() and size() methods. + static void trace_page_sizes(const char* str, const size_t region_min_size, + const size_t region_max_size, + const size_t page_size, + const char* base = NULL, + const size_t size = 0) PRODUCT_RETURN; + + static int vm_allocation_granularity(); + static char* reserve_memory(size_t bytes, char* addr = 0, + size_t alignment_hint = 0); + static char* attempt_reserve_memory_at(size_t bytes, char* addr); + static void split_reserved_memory(char *base, size_t size, + size_t split, bool realloc); + static bool commit_memory(char* addr, size_t bytes); + static bool commit_memory(char* addr, size_t size, size_t alignment_hint); + static bool uncommit_memory(char* addr, size_t bytes); + static bool release_memory(char* addr, size_t bytes); + static bool protect_memory(char* addr, size_t bytes); + static bool guard_memory(char* addr, size_t bytes); + static bool unguard_memory(char* addr, size_t bytes); + static char* map_memory(int fd, const char* file_name, size_t file_offset, + char *addr, size_t bytes, bool read_only = false, + bool allow_exec = false); + static char* remap_memory(int fd, const char* file_name, size_t file_offset, + char *addr, size_t bytes, bool read_only, + bool allow_exec); + static bool unmap_memory(char *addr, size_t bytes); + static void free_memory(char *addr, size_t bytes); + static void realign_memory(char *addr, size_t bytes, size_t alignment_hint); + + // NUMA-specific interface + static void numa_make_local(char *addr, size_t bytes); + static void numa_make_global(char *addr, size_t bytes); + static size_t numa_get_groups_num(); + static size_t numa_get_leaf_groups(int *ids, size_t size); + static bool numa_topology_changed(); + static int numa_get_group_id(); + + // Page manipulation + struct page_info { + size_t size; + int lgrp_id; + }; + static bool get_page_info(char *start, page_info* info); + static char* scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found); + + static char* non_memory_address_word(); + // reserve, commit and pin the entire memory region + static char* reserve_memory_special(size_t size); + static bool release_memory_special(char* addr, size_t bytes); + static bool large_page_init(); + static size_t large_page_size(); + static bool can_commit_large_page_memory(); + + // OS interface to polling page + static address get_polling_page() { return _polling_page; } + static void set_polling_page(address page) { _polling_page = page; } + static bool is_poll_address(address addr) { return addr >= _polling_page && addr < (_polling_page + os::vm_page_size()); } + static void make_polling_page_unreadable(); + static void make_polling_page_readable(); + + // Routines used to serialize the thread state without using membars + static void serialize_thread_states(); + + // Since we write to the serialize page from every thread, we + // want stores to be on unique cache lines whenever possible + // in order to minimize CPU cross talk. We pre-compute the + // amount to shift the thread* to make this offset unique to + // each thread. + static int get_serialize_page_shift_count() { + return SerializePageShiftCount; + } + + static void set_serialize_page_mask(uintptr_t mask) { + _serialize_page_mask = mask; + } + + static unsigned int get_serialize_page_mask() { + return _serialize_page_mask; + } + + static void set_memory_serialize_page(address page); + + static address get_memory_serialize_page() { + return (address)_mem_serialize_page; + } + + static inline void write_memory_serialize_page(JavaThread *thread) { + uintptr_t page_offset = ((uintptr_t)thread >> + get_serialize_page_shift_count()) & + get_serialize_page_mask(); + *(volatile int32_t *)((uintptr_t)_mem_serialize_page+page_offset) = 1; + } + + static bool is_memory_serialize_page(JavaThread *thread, address addr) { + address thr_addr; + if (UseMembar) return false; + // Calculate thread specific address + if (thread == NULL) return false; + // TODO-FIXME: some platforms mask off faulting addresses to the base pagesize. + // Instead of using a test for equality we should probably use something + // of the form: + // return ((_mem_serialize_page ^ addr) & -pagesize) == 0 + // + thr_addr = (address)(((uintptr_t)thread >> + get_serialize_page_shift_count()) & + get_serialize_page_mask()) + (uintptr_t)_mem_serialize_page; + return (thr_addr == addr); + } + + static void block_on_serialize_page_trap(); + + // threads + + enum ThreadType { + vm_thread, + cgc_thread, // Concurrent GC thread + pgc_thread, // Parallel GC thread + java_thread, + compiler_thread, + watcher_thread + }; + + static bool create_thread(Thread* thread, + ThreadType thr_type, + size_t stack_size = 0); + static bool create_main_thread(JavaThread* thread); + static bool create_attached_thread(JavaThread* thread); + static void pd_start_thread(Thread* thread); + static void start_thread(Thread* thread); + + static void initialize_thread(); + static void free_thread(OSThread* osthread); + + // thread id on Linux/64bit is 64bit, on Windows and Solaris, it's 32bit + static intx current_thread_id(); + static int current_process_id(); + // hpi::read for calls from non native state + // For performance, hpi::read is only callable from _thread_in_native + static size_t read(int fd, void *buf, unsigned int nBytes); + static int sleep(Thread* thread, jlong ms, bool interruptable); + static int naked_sleep(); + static void infinite_sleep(); // never returns, use with CAUTION + static void yield(); // Yields to all threads with same priority + enum YieldResult { + YIELD_SWITCHED = 1, // caller descheduled, other ready threads exist & ran + YIELD_NONEREADY = 0, // No other runnable/ready threads. + // platform-specific yield return immediately + YIELD_UNKNOWN = -1 // Unknown: platform doesn't support _SWITCHED or _NONEREADY + // YIELD_SWITCHED and YIELD_NONREADY imply the platform supports a "strong" + // yield that can be used in lieu of blocking. + } ; + static YieldResult NakedYield () ; + static void yield_all(int attempts = 0); // Yields to all other threads including lower priority + static void loop_breaker(int attempts); // called from within tight loops to possibly influence time-sharing + static OSReturn set_priority(Thread* thread, ThreadPriority priority); + static OSReturn get_priority(const Thread* const thread, ThreadPriority& priority); + + static void interrupt(Thread* thread); + static bool is_interrupted(Thread* thread, bool clear_interrupted); + + static int pd_self_suspend_thread(Thread* thread); + + static ExtendedPC fetch_frame_from_context(void* ucVoid, intptr_t** sp, intptr_t** fp); + static frame fetch_frame_from_context(void* ucVoid); + + static ExtendedPC get_thread_pc(Thread *thread); + static void breakpoint(); + + static address current_stack_pointer(); + static address current_stack_base(); + static size_t current_stack_size(); + + static int message_box(const char* title, const char* message); + static char* do_you_want_to_debug(const char* message); + + // run cmd in a separate process and return its exit code; or -1 on failures + static int fork_and_exec(char *cmd); + + // Set file to send error reports. + static void set_error_file(const char *logfile); + + // os::exit() is merged with vm_exit() + // static void exit(int num); + + // Terminate the VM, but don't exit the process + static void shutdown(); + + // Terminate with an error. Default is to generate a core file on platforms + // that support such things. This calls shutdown() and then aborts. + static void abort(bool dump_core = true); + + // Die immediately, no exit hook, no abort hook, no cleanup. + static void die(); + + // Reading directories. + static DIR* opendir(const char* dirname); + static int readdir_buf_size(const char *path); + static struct dirent* readdir(DIR* dirp, dirent* dbuf); + static int closedir(DIR* dirp); + + // Dynamic library extension + static const char* dll_file_extension(); + + static const char* get_temp_directory(); + static const char* get_current_directory(char *buf, int buflen); + + // Symbol lookup, find nearest function name; basically it implements + // dladdr() for all platforms. Name of the nearest function is copied + // to buf. Distance from its base address is returned as offset. + // If function name is not found, buf[0] is set to '\0' and offset is + // set to -1. + static bool dll_address_to_function_name(address addr, char* buf, + int buflen, int* offset); + + // Locate DLL/DSO. On success, full path of the library is copied to + // buf, and offset is set to be the distance between addr and the + // library's base address. On failure, buf[0] is set to '\0' and + // offset is set to -1. + static bool dll_address_to_library_name(address addr, char* buf, + int buflen, int* offset); + + // Find out whether the pc is in the static code for jvm.dll/libjvm.so. + static bool address_is_in_vm(address addr); + + // Loads .dll/.so and + // in case of error it checks if .dll/.so was built for the + // same architecture as Hotspot is running on + static void* dll_load(const char *name, char *ebuf, int ebuflen); + + // Print out system information; they are called by fatal error handler. + // Output format may be different on different platforms. + static void print_os_info(outputStream* st); + static void print_cpu_info(outputStream* st); + static void print_memory_info(outputStream* st); + static void print_dll_info(outputStream* st); + static void print_environment_variables(outputStream* st, const char** env_list, char* buffer, int len); + static void print_context(outputStream* st, void* context); + static void print_siginfo(outputStream* st, void* siginfo); + static void print_signal_handlers(outputStream* st, char* buf, size_t buflen); + static void print_date_and_time(outputStream* st); + + // The following two functions are used by fatal error handler to trace + // native (C) frames. They are not part of frame.hpp/frame.cpp because + // frame.hpp/cpp assume thread is JavaThread, and also because different + // OS/compiler may have different convention or provide different API to + // walk C frames. + // + // We don't attempt to become a debugger, so we only follow frames if that + // does not require a lookup in the unwind table, which is part of the binary + // file but may be unsafe to read after a fatal error. So on x86, we can + // only walk stack if %ebp is used as frame pointer; on ia64, it's not + // possible to walk C stack without having the unwind table. + static bool is_first_C_frame(frame *fr); + static frame get_sender_for_C_frame(frame *fr); + + // return current frame. pc() and sp() are set to NULL on failure. + static frame current_frame(); + + static void print_hex_dump(outputStream* st, address start, address end, int unitsize); + + // returns a string to describe the exception/signal; + // returns NULL if exception_code is not an OS exception/signal. + static const char* exception_name(int exception_code, char* buf, size_t buflen); + + // Returns native Java library, loads if necessary + static void* native_java_library(); + + // Fills in path to jvm.dll/libjvm.so (this info used to find hpi). + static void jvm_path(char *buf, jint buflen); + + // JNI names + static void print_jni_name_prefix_on(outputStream* st, int args_size); + static void print_jni_name_suffix_on(outputStream* st, int args_size); + + // File conventions + static const char* file_separator(); + static const char* line_separator(); + static const char* path_separator(); + + // Init os specific system properties values + static void init_system_properties_values(); + + // IO operations, non-JVM_ version. + static int stat(const char* path, struct stat* sbuf); + static bool dir_is_empty(const char* path); + + // IO operations on binary files + static int create_binary_file(const char* path, bool rewrite_existing); + static jlong current_file_offset(int fd); + static jlong seek_to_file_offset(int fd, jlong offset); + + // Thread Local Storage + static int allocate_thread_local_storage(); + static void thread_local_storage_at_put(int index, void* value); + static void* thread_local_storage_at(int index); + static void free_thread_local_storage(int index); + + // General allocation (must be MT-safe) + static void* malloc (size_t size); + static void* realloc (void *memblock, size_t size); + static void free (void *memblock); + static bool check_heap(bool force = false); // verify C heap integrity + static char* strdup(const char *); // Like strdup + +#ifndef PRODUCT + static int num_mallocs; // # of calls to malloc/realloc + static size_t alloc_bytes; // # of bytes allocated + static int num_frees; // # of calls to free +#endif + + // Printing 64 bit integers + static const char* jlong_format_specifier(); + static const char* julong_format_specifier(); + + // Support for signals (see JVM_RaiseSignal, JVM_RegisterSignal) + static void signal_init(); + static void signal_init_pd(); + static void signal_notify(int signal_number); + static void* signal(int signal_number, void* handler); + static void signal_raise(int signal_number); + static int signal_wait(); + static int signal_lookup(); + static void* user_handler(); + static void terminate_signal_thread(); + static int sigexitnum_pd(); + + // random number generation + static long random(); // return 32bit pseudorandom number + static void init_random(long initval); // initialize random sequence + + // Structured OS Exception support + static void os_exception_wrapper(java_call_t f, JavaValue* value, methodHandle* method, JavaCallArguments* args, Thread* thread); + + // JVMTI & JVM monitoring and management support + // The thread_cpu_time() and current_thread_cpu_time() are only + // supported if is_thread_cpu_time_supported() returns true. + // They are not supported on Solaris T1. + + // Thread CPU Time - return the fast estimate on a platform + // On Solaris - call gethrvtime (fast) - user time only + // On Linux - fast clock_gettime where available - user+sys + // - otherwise: very slow /proc fs - user+sys + // On Windows - GetThreadTimes - user+sys + static jlong current_thread_cpu_time(); + static jlong thread_cpu_time(Thread* t); + + // Thread CPU Time with user_sys_cpu_time parameter. + // + // If user_sys_cpu_time is true, user+sys time is returned. + // Otherwise, only user time is returned + static jlong current_thread_cpu_time(bool user_sys_cpu_time); + static jlong thread_cpu_time(Thread* t, bool user_sys_cpu_time); + + // Return a bunch of info about the timers. + // Note that the returned info for these two functions may be different + // on some platforms + static void current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr); + static void thread_cpu_time_info(jvmtiTimerInfo *info_ptr); + + static bool is_thread_cpu_time_supported(); + + // System loadavg support. Returns -1 if load average cannot be obtained. + static int loadavg(double loadavg[], int nelem); + + // Hook for os specific jvm options that we don't want to abort on seeing + static bool obsolete_option(const JavaVMOption *option); + + // Platform dependent stuff + #include "incls/_os_pd.hpp.incl" + + // debugging support (mostly used by debug.cpp) + static bool find(address pc) PRODUCT_RETURN0; // OS specific function to make sense out of an address + + static bool dont_yield(); // when true, JVM_Yield() is nop + static void print_statistics(); + + // Thread priority helpers (implemented in OS-specific part) + static OSReturn set_native_priority(Thread* thread, int native_prio); + static OSReturn get_native_priority(const Thread* const thread, int* priority_ptr); + static int java_to_os_priority[MaxPriority + 1]; + // Hint to the underlying OS that a task switch would not be good. + // Void return because it's a hint and can fail. + static void hint_no_preempt(); + + // Used at creation if requested by the diagnostic flag PauseAtStartup. + // Causes the VM to wait until an external stimulus has been applied + // (for Unix, that stimulus is a signal, for Windows, an external + // ResumeThread call) + static void pause(); + + protected: + static long _rand_seed; // seed for random number generator + static int _processor_count; // number of processors + + static char* format_boot_path(const char* format_string, + const char* home, + int home_len, + char fileSep, + char pathSep); + static bool set_boot_path(char fileSep, char pathSep); +}; + +// Note that "PAUSE" is almost always used with synchronization +// so arguably we should provide Atomic::SpinPause() instead +// of the global SpinPause() with C linkage. +// It'd also be eligible for inlining on many platforms. + +extern "C" int SpinPause () ; +extern "C" int SafeFetch32 (int * adr, int errValue) ; +extern "C" intptr_t SafeFetchN (intptr_t * adr, intptr_t errValue) ; diff --git a/hotspot/src/share/vm/runtime/osThread.cpp b/hotspot/src/share/vm/runtime/osThread.cpp new file mode 100644 index 00000000000..f8c78628bc8 --- /dev/null +++ b/hotspot/src/share/vm/runtime/osThread.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_osThread.cpp.incl" + + +OSThread::OSThread(OSThreadStartFunc start_proc, void* start_parm) { + pd_initialize(); + set_start_proc(start_proc); + set_start_parm(start_parm); + set_interrupted(false); +} + +OSThread::~OSThread() { + pd_destroy(); +} + +// Printing +void OSThread::print_on(outputStream *st) const { + st->print("nid=0x%lx ", thread_id()); + switch (_state) { + case ALLOCATED: st->print("allocated "); break; + case INITIALIZED: st->print("initialized "); break; + case RUNNABLE: st->print("runnable "); break; + case MONITOR_WAIT: st->print("waiting for monitor entry "); break; + case CONDVAR_WAIT: st->print("waiting on condition "); break; + case OBJECT_WAIT: st->print("in Object.wait() "); break; + case BREAKPOINTED: st->print("at breakpoint"); break; + case SLEEPING: st->print("sleeping"); break; + case ZOMBIE: st->print("zombie"); break; + default: st->print("unknown state %d", _state); break; + } +} diff --git a/hotspot/src/share/vm/runtime/osThread.hpp b/hotspot/src/share/vm/runtime/osThread.hpp new file mode 100644 index 00000000000..1491217ff8c --- /dev/null +++ b/hotspot/src/share/vm/runtime/osThread.hpp @@ -0,0 +1,131 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The OSThread class holds OS-specific thread information. It is equivalent +// to the sys_thread_t structure of the classic JVM implementation. + +// The thread states represented by the ThreadState values are platform-specific +// and are likely to be only approximate, because most OSes don't give you access +// to precise thread state information. + +// Note: the ThreadState is legacy code and is not correctly implemented. +// Uses of ThreadState need to be replaced by the state in the JavaThread. + +enum ThreadState { + ALLOCATED, // Memory has been allocated but not initialized + INITIALIZED, // The thread has been initialized but yet started + RUNNABLE, // Has been started and is runnable, but not necessarily running + MONITOR_WAIT, // Waiting on a contended monitor lock + CONDVAR_WAIT, // Waiting on a condition variable + OBJECT_WAIT, // Waiting on an Object.wait() call + BREAKPOINTED, // Suspended at breakpoint + SLEEPING, // Thread.sleep() + ZOMBIE // All done, but not reclaimed yet +}; + +// I'd make OSThread a ValueObj embedded in Thread to avoid an indirection, but +// the assembler test in java.cpp expects that it can install the OSThread of +// the main thread into its own Thread at will. + + +class OSThread: public CHeapObj { + friend class VMStructs; + private: + //void* _start_proc; // Thread start routine + OSThreadStartFunc _start_proc; // Thread start routine + void* _start_parm; // Thread start routine parameter + volatile ThreadState _state; // Thread state *hint* + jint _interrupted; // Thread.isInterrupted state + + // Note: _interrupted must be jint, so that Java intrinsics can access it. + // The value stored there must be either 0 or 1. It must be possible + // for Java to emulate Thread.currentThread().isInterrupted() by performing + // the double indirection Thread::current()->_osthread->_interrupted. + + // Methods + public: + void set_state(ThreadState state) { _state = state; } + ThreadState get_state() { return _state; } + + // Constructor + OSThread(OSThreadStartFunc start_proc, void* start_parm); + + // Destructor + ~OSThread(); + + // Accessors + OSThreadStartFunc start_proc() const { return _start_proc; } + void set_start_proc(OSThreadStartFunc start_proc) { _start_proc = start_proc; } + void* start_parm() const { return _start_parm; } + void set_start_parm(void* start_parm) { _start_parm = start_parm; } + + bool interrupted() const { return _interrupted != 0; } + void set_interrupted(bool z) { _interrupted = z ? 1 : 0; } + + // Printing + void print_on(outputStream* st) const; + void print() const { print_on(tty); } + + // For java intrinsics: + static ByteSize interrupted_offset() { return byte_offset_of(OSThread, _interrupted); } + + // Platform dependent stuff + #include "incls/_osThread_pd.hpp.incl" +}; + + +// Utility class for use with condition variables: +class OSThreadWaitState : public StackObj { + OSThread* _osthread; + ThreadState _old_state; + public: + OSThreadWaitState(OSThread* osthread, bool is_object_wait) { + _osthread = osthread; + _old_state = osthread->get_state(); + if (is_object_wait) { + osthread->set_state(OBJECT_WAIT); + } else { + osthread->set_state(CONDVAR_WAIT); + } + } + ~OSThreadWaitState() { + _osthread->set_state(_old_state); + } +}; + + +// Utility class for use with contended monitors: +class OSThreadContendState : public StackObj { + OSThread* _osthread; + ThreadState _old_state; + public: + OSThreadContendState(OSThread* osthread) { + _osthread = osthread; + _old_state = osthread->get_state(); + osthread->set_state(MONITOR_WAIT); + } + ~OSThreadContendState() { + _osthread->set_state(_old_state); + } +}; diff --git a/hotspot/src/share/vm/runtime/perfData.cpp b/hotspot/src/share/vm/runtime/perfData.cpp new file mode 100644 index 00000000000..e928e407e22 --- /dev/null +++ b/hotspot/src/share/vm/runtime/perfData.cpp @@ -0,0 +1,594 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_perfData.cpp.incl" + +PerfDataList* PerfDataManager::_all = NULL; +PerfDataList* PerfDataManager::_sampled = NULL; +PerfDataList* PerfDataManager::_constants = NULL; + +/* + * The jvmstat global and subsysem jvmstat counter name spaces. The top + * level name spaces imply the interface stability level of the counter, + * which generally follows the Java package, class, and property naming + * conventions. The CounterNS enumeration values should be used to index + * into this array. + */ +const char* PerfDataManager::_name_spaces[] = { + // top level name spaces + "java", // stable and supported name space + "com.sun", // unstable but supported name space + "sun", // unstable and unsupported name space + // subsystem name spaces + "java.gc", // Garbage Collection name spaces + "com.sun.gc", + "sun.gc", + "java.ci", // Compiler name spaces + "com.sun.ci", + "sun.ci", + "java.cls", // Class Loader name spaces + "com.sun.cls", + "sun.cls", + "java.rt", // Runtime name spaces + "com.sun.rt", + "sun.rt", + "java.os", // Operating System name spaces + "com.sun.os", + "sun.os", + "java.threads", // Threads System name spaces + "com.sun.threads", + "sun.threads", + "java.property", // Java Property name spaces + "com.sun.property", + "sun.property", + "", +}; + +PerfData::PerfData(CounterNS ns, const char* name, Units u, Variability v) + : _name(NULL), _u(u), _v(v), _valuep(NULL), + _on_c_heap(false) { + + const char* prefix = PerfDataManager::ns_to_string(ns); + + _name = NEW_C_HEAP_ARRAY(char, strlen(name) + strlen(prefix) + 2); + assert(_name != NULL && strlen(name) != 0, "invalid name"); + + if (ns == NULL_NS) { + // No prefix is added to counters with the NULL_NS namespace. + strcpy(_name, name); + // set the F_Supported flag based on the counter name prefix. + if (PerfDataManager::is_stable_supported(_name) || + PerfDataManager::is_unstable_supported(_name)) { + _flags = F_Supported; + } + else { + _flags = F_None; + } + } + else { + sprintf(_name, "%s.%s", prefix, name); + // set the F_Supported flag based on the given namespace. + if (PerfDataManager::is_stable_supported(ns) || + PerfDataManager::is_unstable_supported(ns)) { + _flags = F_Supported; + } + else { + _flags = F_None; + } + } +} + +PerfData::~PerfData() { + if (_name != NULL) { + FREE_C_HEAP_ARRAY(char, _name); + } + if (is_on_c_heap()) { + FREE_C_HEAP_ARRAY(PerfDataEntry, _pdep); + } +} + +void PerfData::create_entry(BasicType dtype, size_t dsize, size_t vlen) { + + size_t dlen = vlen==0 ? 1 : vlen; + + size_t namelen = strlen(name()) + 1; // include null terminator + size_t size = sizeof(PerfDataEntry) + namelen; + size_t pad_length = ((size % dsize) == 0) ? 0 : dsize - (size % dsize); + size += pad_length; + size_t data_start = size; + size += (dsize * dlen); + + // align size to assure allocation in units of 8 bytes + int align = sizeof(jlong) - 1; + size = ((size + align) & ~align); + char* psmp = PerfMemory::alloc(size); + + if (psmp == NULL) { + // out of PerfMemory memory resources. allocate on the C heap + // to avoid vm termination. + psmp = NEW_C_HEAP_ARRAY(char, size); + _on_c_heap = true; + } + + // compute the addresses for the name and data + char* cname = psmp + sizeof(PerfDataEntry); + + // data is in the last dsize*dlen bytes of the entry + void* valuep = (void*) (psmp + data_start); + + assert(is_on_c_heap() || PerfMemory::contains(cname), "just checking"); + assert(is_on_c_heap() || PerfMemory::contains((char*)valuep), "just checking"); + + // copy the name, including null terminator, into PerfData memory + strcpy(cname, name()); + + + // set the header values in PerfData memory + PerfDataEntry* pdep = (PerfDataEntry*)psmp; + pdep->entry_length = (jint)size; + pdep->name_offset = (jint) ((uintptr_t) cname - (uintptr_t) psmp); + pdep->vector_length = (jint)vlen; + pdep->data_type = (jbyte) type2char(dtype); + pdep->data_units = units(); + pdep->data_variability = variability(); + pdep->flags = (jbyte)flags(); + pdep->data_offset = (jint) data_start; + + if (PerfTraceDataCreation) { + tty->print("name = %s, dtype = %d, variability = %d," + " units = %d, dsize = %d, vlen = %d," + " pad_length = %d, size = %d, on_c_heap = %s," + " address = " INTPTR_FORMAT "," + " data address = " INTPTR_FORMAT "\n", + cname, dtype, variability(), + units(), dsize, vlen, + pad_length, size, is_on_c_heap() ? "TRUE":"FALSE", + psmp, valuep); + } + + // record the start of the entry and the location of the data field. + _pdep = pdep; + _valuep = valuep; + + // mark the PerfData memory region as having been updated. + PerfMemory::mark_updated(); +} + +PerfLong::PerfLong(CounterNS ns, const char* namep, Units u, Variability v) + : PerfData(ns, namep, u, v) { + + create_entry(T_LONG, sizeof(jlong)); +} + +int PerfLong::format(char* buffer, int length) { + return jio_snprintf(buffer, length,"%lld", *(jlong*)_valuep); +} + +PerfLongVariant::PerfLongVariant(CounterNS ns, const char* namep, Units u, + Variability v, jlong* sampled) + : PerfLong(ns, namep, u, v), + _sampled(sampled), _sample_helper(NULL) { + + sample(); +} + +PerfLongVariant::PerfLongVariant(CounterNS ns, const char* namep, Units u, + Variability v, PerfLongSampleHelper* helper) + : PerfLong(ns, namep, u, v), + _sampled(NULL), _sample_helper(helper) { + + sample(); +} + +void PerfLongVariant::sample() { + + assert(_sample_helper != NULL || _sampled != NULL, "unexpected state"); + + if (_sample_helper != NULL) { + *(jlong*)_valuep = _sample_helper->take_sample(); + } + else if (_sampled != NULL) { + *(jlong*)_valuep = *_sampled; + } +} + +PerfByteArray::PerfByteArray(CounterNS ns, const char* namep, Units u, + Variability v, jint length) + : PerfData(ns, namep, u, v), _length(length) { + + create_entry(T_BYTE, sizeof(jbyte), (size_t)_length); +} + +void PerfString::set_string(const char* s2) { + + // copy n bytes of the string, assuring the null string is + // copied if s2 == NULL. + strncpy((char *)_valuep, s2 == NULL ? "" : s2, _length); + + // assure the string is null terminated when strlen(s2) >= _length + ((char*)_valuep)[_length-1] = '\0'; +} + +int PerfString::format(char* buffer, int length) { + return jio_snprintf(buffer, length, "%s", (char*)_valuep); +} + +PerfStringConstant::PerfStringConstant(CounterNS ns, const char* namep, + const char* initial_value) + : PerfString(ns, namep, V_Constant, + initial_value == NULL ? 1 : + MIN2((jint)(strlen((char*)initial_value)+1), + (jint)(PerfMaxStringConstLength+1)), + initial_value) { + + if (PrintMiscellaneous && Verbose) { + if (is_valid() && initial_value != NULL && + ((jint)strlen(initial_value) > (jint)PerfMaxStringConstLength)) { + + warning("Truncating PerfStringConstant: name = %s," + " length = " INT32_FORMAT "," + " PerfMaxStringConstLength = " INT32_FORMAT "\n", + namep, + (jint)strlen(initial_value), + (jint)PerfMaxStringConstLength); + } + } +} + + + + + + +void PerfDataManager::destroy() { + + if (_all == NULL) + // destroy already called, or initialization never happened + return; + + for (int index = 0; index < _all->length(); index++) { + PerfData* p = _all->at(index); + delete p; + } + + delete(_all); + delete(_sampled); + delete(_constants); + + _all = NULL; + _sampled = NULL; + _constants = NULL; +} + +void PerfDataManager::add_item(PerfData* p, bool sampled) { + + MutexLocker ml(PerfDataManager_lock); + + if (_all == NULL) { + _all = new PerfDataList(100); + } + + assert(!_all->contains(p->name()), "duplicate name added"); + + // add to the list of all perf data items + _all->append(p); + + if (p->variability() == PerfData::V_Constant) { + if (_constants == NULL) { + _constants = new PerfDataList(25); + } + _constants->append(p); + return; + } + + if (sampled) { + if (_sampled == NULL) { + _sampled = new PerfDataList(25); + } + _sampled->append(p); + } +} + +PerfDataList* PerfDataManager::all() { + + MutexLocker ml(PerfDataManager_lock); + + if (_all == NULL) + return NULL; + + PerfDataList* clone = _all->clone(); + return clone; +} + +PerfDataList* PerfDataManager::sampled() { + + MutexLocker ml(PerfDataManager_lock); + + if (_sampled == NULL) + return NULL; + + PerfDataList* clone = _sampled->clone(); + return clone; +} + +PerfDataList* PerfDataManager::constants() { + + MutexLocker ml(PerfDataManager_lock); + + if (_constants == NULL) + return NULL; + + PerfDataList* clone = _constants->clone(); + return clone; +} + +char* PerfDataManager::counter_name(const char* ns, const char* name) { + assert(ns != NULL, "ns string required"); + assert(name != NULL, "name string required"); + + size_t len = strlen(ns) + strlen(name) + 2; + char* result = NEW_RESOURCE_ARRAY(char, len); + sprintf(result, "%s.%s", ns, name); + return result; +} + +char* PerfDataManager::name_space(const char* ns, const char* sub, + int instance) { + char intbuf[40]; + jio_snprintf(intbuf, 40, UINT32_FORMAT, instance); + return name_space(ns, name_space(sub, intbuf)); +} + +char *PerfDataManager::name_space(const char* ns, int instance) { + char intbuf[40]; + jio_snprintf(intbuf, 40, UINT32_FORMAT, instance); + return name_space(ns, intbuf); +} + +PerfStringConstant* PerfDataManager::create_string_constant(CounterNS ns, + const char* name, + const char* s, + TRAPS) { + + PerfStringConstant* p = new PerfStringConstant(ns, name, s); + + if (!p->is_valid()) { + // allocation of native resources failed. + delete p; + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + + add_item(p, false); + + return p; +} + +PerfLongConstant* PerfDataManager::create_long_constant(CounterNS ns, + const char* name, + PerfData::Units u, + jlong val, TRAPS) { + + PerfLongConstant* p = new PerfLongConstant(ns, name, u, val); + + if (!p->is_valid()) { + // allocation of native resources failed. + delete p; + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + + add_item(p, false); + + return p; +} + +PerfStringVariable* PerfDataManager::create_string_variable(CounterNS ns, + const char* name, + jint max_length, + const char* s, + TRAPS) { + + if (max_length == 0 && s != NULL) max_length = (jint)strlen(s); + + assert(max_length != 0, "PerfStringVariable with length 0"); + + PerfStringVariable* p = new PerfStringVariable(ns, name, max_length, s); + + if (!p->is_valid()) { + // allocation of native resources failed. + delete p; + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + + add_item(p, false); + + return p; +} + +PerfLongVariable* PerfDataManager::create_long_variable(CounterNS ns, + const char* name, + PerfData::Units u, + jlong ival, TRAPS) { + + PerfLongVariable* p = new PerfLongVariable(ns, name, u, ival); + + if (!p->is_valid()) { + // allocation of native resources failed. + delete p; + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + + add_item(p, false); + + return p; +} + +PerfLongVariable* PerfDataManager::create_long_variable(CounterNS ns, + const char* name, + PerfData::Units u, + jlong* sp, TRAPS) { + + // Sampled counters not supported if UsePerfData is false + if (!UsePerfData) return NULL; + + PerfLongVariable* p = new PerfLongVariable(ns, name, u, sp); + + if (!p->is_valid()) { + // allocation of native resources failed. + delete p; + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + + add_item(p, true); + + return p; +} + +PerfLongVariable* PerfDataManager::create_long_variable(CounterNS ns, + const char* name, + PerfData::Units u, + PerfSampleHelper* sh, + TRAPS) { + + // Sampled counters not supported if UsePerfData is false + if (!UsePerfData) return NULL; + + PerfLongVariable* p = new PerfLongVariable(ns, name, u, sh); + + if (!p->is_valid()) { + // allocation of native resources failed. + delete p; + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + + add_item(p, true); + + return p; +} + +PerfLongCounter* PerfDataManager::create_long_counter(CounterNS ns, + const char* name, + PerfData::Units u, + jlong ival, TRAPS) { + + PerfLongCounter* p = new PerfLongCounter(ns, name, u, ival); + + if (!p->is_valid()) { + // allocation of native resources failed. + delete p; + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + + add_item(p, false); + + return p; +} + +PerfLongCounter* PerfDataManager::create_long_counter(CounterNS ns, + const char* name, + PerfData::Units u, + jlong* sp, TRAPS) { + + // Sampled counters not supported if UsePerfData is false + if (!UsePerfData) return NULL; + + PerfLongCounter* p = new PerfLongCounter(ns, name, u, sp); + + if (!p->is_valid()) { + // allocation of native resources failed. + delete p; + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + + add_item(p, true); + + return p; +} + +PerfLongCounter* PerfDataManager::create_long_counter(CounterNS ns, + const char* name, + PerfData::Units u, + PerfSampleHelper* sh, + TRAPS) { + + // Sampled counters not supported if UsePerfData is false + if (!UsePerfData) return NULL; + + PerfLongCounter* p = new PerfLongCounter(ns, name, u, sh); + + if (!p->is_valid()) { + // allocation of native resources failed. + delete p; + THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + } + + add_item(p, true); + + return p; +} + +PerfDataList::PerfDataList(int length) { + + _set = new(ResourceObj::C_HEAP) PerfDataArray(length, true); +} + +PerfDataList::PerfDataList(PerfDataList* p) { + + _set = new(ResourceObj::C_HEAP) PerfDataArray(p->length(), true); + + _set->appendAll(p->get_impl()); +} + +PerfDataList::~PerfDataList() { + + delete _set; + +} + +bool PerfDataList::by_name(void* name, PerfData* pd) { + + if (pd == NULL) + return false; + + return strcmp((const char*)name, pd->name()) == 0; +} + +PerfData* PerfDataList::find_by_name(const char* name) { + + int i = _set->find((void*)name, PerfDataList::by_name); + + if (i >= 0 && i <= _set->length()) + return _set->at(i); + else + return NULL; +} + +PerfDataList* PerfDataList::clone() { + + PerfDataList* copy = new PerfDataList(this); + + assert(copy != NULL, "just checking"); + + return copy; +} diff --git a/hotspot/src/share/vm/runtime/perfData.hpp b/hotspot/src/share/vm/runtime/perfData.hpp new file mode 100644 index 00000000000..16c0c742b47 --- /dev/null +++ b/hotspot/src/share/vm/runtime/perfData.hpp @@ -0,0 +1,955 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* jvmstat global and subsystem counter name space - enumeration value + * serve as an index into the PerfDataManager::_name_space[] array + * containing the corresponding name space string. Only the top level + * subsystem name spaces are represented here. + */ +enum CounterNS { + // top level name spaces + JAVA_NS, + COM_NS, + SUN_NS, + // subsystem name spaces + JAVA_GC, // Garbage Collection name spaces + COM_GC, + SUN_GC, + JAVA_CI, // Compiler name spaces + COM_CI, + SUN_CI, + JAVA_CLS, // Class Loader name spaces + COM_CLS, + SUN_CLS, + JAVA_RT, // Runtime name spaces + COM_RT, + SUN_RT, + JAVA_OS, // Operating System name spaces + COM_OS, + SUN_OS, + JAVA_THREADS, // Threads System name spaces + COM_THREADS, + SUN_THREADS, + JAVA_PROPERTY, // Java Property name spaces + COM_PROPERTY, + SUN_PROPERTY, + NULL_NS, + COUNTERNS_LAST = NULL_NS +}; + +/* + * Classes to support access to production performance data + * + * The PerfData class structure is provided for creation, access, and update + * of performance data (a.k.a. instrumentation) in a specific memory region + * which is possibly accessible as shared memory. Although not explicitly + * prevented from doing so, developers should not use the values returned + * by accessor methods to make algorithmic decisions as they are potentially + * extracted from a shared memory region. Although any shared memory region + * created is with appropriate access restrictions, allowing read-write access + * only to the principal that created the JVM, it is believed that a the + * shared memory region facilitates an easier attack path than attacks + * launched through mechanisms such as /proc. For this reason, it is + * recommended that data returned by PerfData accessor methods be used + * cautiously. + * + * There are three variability classifications of performance data + * Constants - value is written to the PerfData memory once, on creation + * Variables - value is modifiable, with no particular restrictions + * Counters - value is monotonically changing (increasing or decreasing) + * + * The performance data items can also have various types. The class + * hierarchy and the structure of the memory region are designed to + * accommodate new types as they are needed. Types are specified in + * terms of Java basic types, which accommodates client applications + * written in the Java programming language. The class hierarchy is: + * + * - PerfData (Abstract) + * - PerfLong (Abstract) + * - PerfLongConstant (alias: PerfConstant) + * - PerfLongVariant (Abstract) + * - PerfLongVariable (alias: PerfVariable) + * - PerfLongCounter (alias: PerfCounter) + * + * - PerfByteArray (Abstract) + * - PerfString (Abstract) + * - PerfStringVariable + * - PerfStringConstant + * + * + * As seen in the class hierarchy, the initially supported types are: + * + * Long - performance data holds a Java long type + * ByteArray - performance data holds an array of Java bytes + * used for holding C++ char arrays. + * + * The String type is derived from the ByteArray type. + * + * A PerfData subtype is not required to provide an implementation for + * each variability classification. For example, the String type provides + * Variable and Constant variablility classifications in the PerfStringVariable + * and PerfStringConstant classes, but does not provide a counter type. + * + * Performance data are also described by a unit of measure. Units allow + * client applications to make reasonable decisions on how to treat + * performance data generically, preventing the need to hard-code the + * specifics of a particular data item in client applications. The current + * set of units are: + * + * None - the data has no units of measure + * Bytes - data is measured in bytes + * Ticks - data is measured in clock ticks + * Events - data is measured in events. For example, + * the number of garbage collection events or the + * number of methods compiled. + * String - data is not numerical. For example, + * the java command line options + * Hertz - data is a frequency + * + * The performance counters also provide a support attribute, indicating + * the stability of the counter as a programmatic interface. The support + * level is also implied by the name space in which the counter is created. + * The counter name space support conventions follow the Java package, class, + * and property support conventions: + * + * java.* - stable, supported interface + * com.sun.* - unstable, supported interface + * sun.* - unstable, unsupported interface + * + * In the above context, unstable is a measure of the interface support + * level, not the implementation stability level. + * + * Currently, instances of PerfData subtypes are considered to have + * a life time equal to that of the VM and are managed by the + * PerfDataManager class. All constructors for the PerfData class and + * its subtypes have protected constructors. Creation of PerfData + * instances is performed by invoking various create methods on the + * PerfDataManager class. Users should not attempt to delete these + * instances as the PerfDataManager class expects to perform deletion + * operations on exit of the VM. + * + * Examples: + * + * Creating performance counter that holds a monotonically increasing + * long data value with units specified in U_Bytes in the "java.gc.*" + * name space. + * + * PerfLongCounter* foo_counter; + * + * foo_counter = PerfDataManager::create_long_counter(JAVA_GC, "foo", + * PerfData::U_Bytes, + * optionalInitialValue, + * CHECK); + * foo_counter->inc(); + * + * Creating a performance counter that holds a variably change long + * data value with untis specified in U_Bytes in the "com.sun.ci + * name space. + * + * PerfLongVariable* bar_varible; + * bar_variable = PerfDataManager::create_long_variable(COM_CI, "bar", +.* PerfData::U_Bytes, + * optionalInitialValue, + * CHECK); + * + * bar_variable->inc(); + * bar_variable->set_value(0); + * + * Creating a performance counter that holds a constant string value in + * the "sun.cls.*" name space. + * + * PerfDataManager::create_string_constant(SUN_CLS, "foo", string, CHECK); + * + * Although the create_string_constant() factory method returns a pointer + * to the PerfStringConstant object, it can safely be ignored. Developers + * are not encouraged to access the string constant's value via this + * pointer at this time due to security concerns. + * + * Creating a performance counter in an arbitrary name space that holds a + * value that is sampled by the StatSampler periodic task. + * + * PerfDataManager::create_counter("foo.sampled", PerfData::U_Events, + * &my_jlong, CHECK); + * + * In this example, the PerfData pointer can be ignored as the caller + * is relying on the StatSampler PeriodicTask to sample the given + * address at a regular interval. The interval is defined by the + * PerfDataSamplingInterval global variable, and is applyied on + * a system wide basis, not on an per-counter basis. + * + * Creating a performance counter in an arbitrary name space that utilizes + * a helper object to return a value to the StatSampler via the take_sample() + * method. + * + * class MyTimeSampler : public PerfLongSampleHelper { + * public: + * jlong take_sample() { return os::elapsed_counter(); } + * }; + * + * PerfDataManager::create_counter(SUN_RT, "helped", + * PerfData::U_Ticks, + * new MyTimeSampler(), CHECK); + * + * In this example, a subtype of PerfLongSampleHelper is instantiated + * and its take_sample() method is overridden to perform whatever + * operation is necessary to generate the data sample. This method + * will be called by the StatSampler at a regular interval, defined + * by the PerfDataSamplingInterval global variable. + * + * As before, PerfSampleHelper is an alias for PerfLongSampleHelper. + * + * For additional uses of PerfData subtypes, see the utility classes + * PerfTraceTime and PerfTraceTimedEvent below. + * + * Always-on non-sampled counters can be created independent of + * the UsePerfData flag. Counters will be created on the c-heap + * if UsePerfData is false. + * + * Until further noice, all PerfData objects should be created and + * manipulated within a guarded block. The guard variable is + * UsePerfData, a product flag set to true by default. This flag may + * be removed from the product in the future. + * + */ +class PerfData : public CHeapObj { + + friend class StatSampler; // for access to protected void sample() + friend class PerfDataManager; // for access to protected destructor + + public: + + // the Variability enum must be kept in synchronization with the + // the com.sun.hotspot.perfdata.Variability class + enum Variability { + V_Constant = 1, + V_Monotonic = 2, + V_Variable = 3, + V_last = V_Variable + }; + + // the Units enum must be kept in synchronization with the + // the com.sun.hotspot.perfdata.Units class + enum Units { + U_None = 1, + U_Bytes = 2, + U_Ticks = 3, + U_Events = 4, + U_String = 5, + U_Hertz = 6, + U_Last = U_Hertz + }; + + // Miscellaneous flags + enum Flags { + F_None = 0x0, + F_Supported = 0x1 // interface is supported - java.* and com.sun.* + }; + + private: + char* _name; + Variability _v; + Units _u; + bool _on_c_heap; + Flags _flags; + + PerfDataEntry* _pdep; + + protected: + + void *_valuep; + + PerfData(CounterNS ns, const char* name, Units u, Variability v); + ~PerfData(); + + // create the entry for the PerfData item in the PerfData memory region. + // this region is maintained separately from the PerfData objects to + // facilitate its use by external processes. + void create_entry(BasicType dtype, size_t dsize, size_t dlen = 0); + + // sample the data item given at creation time and write its value + // into the its corresponding PerfMemory location. + virtual void sample() = 0; + + public: + + // returns a boolean indicating the validity of this object. + // the object is valid if and only if memory in PerfMemory + // region was successfully allocated. + inline bool is_valid() { return _valuep != NULL; } + + // returns a boolean indicating whether the underlying object + // was allocated in the PerfMemory region or on the C heap. + inline bool is_on_c_heap() { return _on_c_heap; } + + // returns a pointer to a char* containing the name of the item. + // The pointer returned is the pointer to a copy of the name + // passed to the constructor, not the pointer to the name in the + // PerfData memory region. This redundancy is maintained for + // security reasons as the PerfMemory region may be in shared + // memory. + const char* name() { return _name; } + + // returns the variability classification associated with this item + Variability variability() { return _v; } + + // returns the units associated with this item. + Units units() { return _u; } + + // returns the flags associated with this item. + Flags flags() { return _flags; } + + // returns the address of the data portion of the item in the + // PerfData memory region. + inline void* get_address() { return _valuep; } + + // returns the value of the data portion of the item in the + // PerfData memory region formatted as a string. + virtual int format(char* cp, int length) = 0; +}; + +/* + * PerfLongSampleHelper, and its alias PerfSamplerHelper, is a base class + * for helper classes that rely upon the StatSampler periodic task to + * invoke the take_sample() method and write the value returned to its + * appropriate location in the PerfData memory region. + */ +class PerfLongSampleHelper : public CHeapObj { + public: + virtual jlong take_sample() = 0; +}; + +typedef PerfLongSampleHelper PerfSampleHelper; + + +/* + * PerfLong is the base class for the various Long PerfData subtypes. + * it contains implementation details that are common among its derived + * types. + */ +class PerfLong : public PerfData { + + protected: + + PerfLong(CounterNS ns, const char* namep, Units u, Variability v); + + public: + int format(char* buffer, int length); + + // returns the value of the data portion of the item in the + // PerfData memory region. + inline jlong get_value() { return *(jlong*)_valuep; } +}; + +/* + * The PerfLongConstant class, and its alias PerfConstant, implement + * a PerfData subtype that holds a jlong data value that is set upon + * creation of an instance of this class. This class provides no + * methods for changing the data value stored in PerfData memory region. + */ +class PerfLongConstant : public PerfLong { + + friend class PerfDataManager; // for access to protected constructor + + private: + // hide sample() - no need to sample constants + void sample() { } + + protected: + + PerfLongConstant(CounterNS ns, const char* namep, Units u, + jlong initial_value=0) + : PerfLong(ns, namep, u, V_Constant) { + + if (is_valid()) *(jlong*)_valuep = initial_value; + } +}; + +typedef PerfLongConstant PerfConstant; + +/* + * The PerfLongVariant class, and its alias PerfVariant, implement + * a PerfData subtype that holds a jlong data value that can be modified + * in an unrestricted manner. This class provides the implementation details + * for common functionality among its derived types. + */ +class PerfLongVariant : public PerfLong { + + protected: + jlong* _sampled; + PerfLongSampleHelper* _sample_helper; + + PerfLongVariant(CounterNS ns, const char* namep, Units u, Variability v, + jlong initial_value=0) + : PerfLong(ns, namep, u, v) { + if (is_valid()) *(jlong*)_valuep = initial_value; + } + + PerfLongVariant(CounterNS ns, const char* namep, Units u, Variability v, + jlong* sampled); + + PerfLongVariant(CounterNS ns, const char* namep, Units u, Variability v, + PerfLongSampleHelper* sample_helper); + + void sample(); + + public: + inline void inc() { (*(jlong*)_valuep)++; } + inline void inc(jlong val) { (*(jlong*)_valuep) += val; } + inline void add(jlong val) { (*(jlong*)_valuep) += val; } +}; + +/* + * The PerfLongCounter class, and its alias PerfCounter, implement + * a PerfData subtype that holds a jlong data value that can (should) + * be modified in a monotonic manner. The inc(jlong) and add(jlong) + * methods can be passed negative values to implement a monotonically + * decreasing value. However, we rely upon the programmer to honor + * the notion that this counter always moves in the same direction - + * either increasing or decreasing. + */ +class PerfLongCounter : public PerfLongVariant { + + friend class PerfDataManager; // for access to protected constructor + + protected: + + PerfLongCounter(CounterNS ns, const char* namep, Units u, + jlong initial_value=0) + : PerfLongVariant(ns, namep, u, V_Monotonic, + initial_value) { } + + PerfLongCounter(CounterNS ns, const char* namep, Units u, jlong* sampled) + : PerfLongVariant(ns, namep, u, V_Monotonic, sampled) { } + + PerfLongCounter(CounterNS ns, const char* namep, Units u, + PerfLongSampleHelper* sample_helper) + : PerfLongVariant(ns, namep, u, V_Monotonic, + sample_helper) { } +}; + +typedef PerfLongCounter PerfCounter; + +/* + * The PerfLongVariable class, and its alias PerfVariable, implement + * a PerfData subtype that holds a jlong data value that can + * be modified in an unrestricted manner. + */ +class PerfLongVariable : public PerfLongVariant { + + friend class PerfDataManager; // for access to protected constructor + + protected: + + PerfLongVariable(CounterNS ns, const char* namep, Units u, + jlong initial_value=0) + : PerfLongVariant(ns, namep, u, V_Variable, + initial_value) { } + + PerfLongVariable(CounterNS ns, const char* namep, Units u, jlong* sampled) + : PerfLongVariant(ns, namep, u, V_Variable, sampled) { } + + PerfLongVariable(CounterNS ns, const char* namep, Units u, + PerfLongSampleHelper* sample_helper) + : PerfLongVariant(ns, namep, u, V_Variable, + sample_helper) { } + + public: + inline void set_value(jlong val) { (*(jlong*)_valuep) = val; } +}; + +typedef PerfLongVariable PerfVariable; + +/* + * The PerfByteArray provides a PerfData subtype that allows the creation + * of a contiguous region of the PerfData memory region for storing a vector + * of bytes. This class is currently intended to be a base class for + * the PerfString class, and cannot be instantiated directly. + */ +class PerfByteArray : public PerfData { + + protected: + jint _length; + + PerfByteArray(CounterNS ns, const char* namep, Units u, Variability v, + jint length); +}; + +class PerfString : public PerfByteArray { + + protected: + + void set_string(const char* s2); + + PerfString(CounterNS ns, const char* namep, Variability v, jint length, + const char* initial_value) + : PerfByteArray(ns, namep, U_String, v, length) { + if (is_valid()) set_string(initial_value); + } + + public: + + int format(char* buffer, int length); +}; + +/* + * The PerfStringConstant class provides a PerfData sub class that + * allows a null terminated string of single byte characters to be + * stored in the PerfData memory region. + */ +class PerfStringConstant : public PerfString { + + friend class PerfDataManager; // for access to protected constructor + + private: + + // hide sample() - no need to sample constants + void sample() { } + + protected: + + // Restrict string constant lengths to be <= PerfMaxStringConstLength. + // This prevents long string constants, as can occur with very + // long classpaths or java command lines, from consuming too much + // PerfData memory. + PerfStringConstant(CounterNS ns, const char* namep, + const char* initial_value); +}; + +/* + * The PerfStringVariable class provides a PerfData sub class that + * allows a null terminated string of single byte character data + * to be stored in PerfData memory region. The string value can be reset + * after initialization. If the string value is >= max_length, then + * it will be truncated to max_length characters. The copied string + * is always null terminated. + */ +class PerfStringVariable : public PerfString { + + friend class PerfDataManager; // for access to protected constructor + + protected: + + // sampling of string variables are not yet supported + void sample() { } + + PerfStringVariable(CounterNS ns, const char* namep, jint max_length, + const char* initial_value) + : PerfString(ns, namep, V_Variable, max_length+1, + initial_value) { } + + public: + inline void set_value(const char* val) { set_string(val); } +}; + + +/* + * The PerfDataList class is a container class for managing lists + * of PerfData items. The intention of this class is to allow for + * alternative implementations for management of list of PerfData + * items without impacting the code that uses the lists. + * + * The initial implementation is based upon GrowableArray. Searches + * on GrowableArray types is linear in nature and this may become + * a performance issue for creation of PerfData items, particularly + * from Java code where a test for existence is implemented as a + * search over all existing PerfData items. + * + * The abstraction is not complete. A more general container class + * would provide an Iterator abstraction that could be used to + * traverse the lists. This implementation still relys upon integer + * iterators and the at(int index) method. However, the GrowableArray + * is not directly visible outside this class and can be replaced by + * some other implementation, as long as that implementation provides + * a mechanism to iterate over the container by index. + */ +class PerfDataList : public CHeapObj { + + private: + + // GrowableArray implementation + typedef GrowableArray PerfDataArray; + + PerfDataArray* _set; + + // method to search for a instrumentation object by name + static bool by_name(void* name, PerfData* pd); + + protected: + // we expose the implementation here to facilitate the clone + // method. + PerfDataArray* get_impl() { return _set; } + + public: + + // create a PerfDataList with the given initial length + PerfDataList(int length); + + // create a PerfDataList as a shallow copy of the given PerfDataList + PerfDataList(PerfDataList* p); + + ~PerfDataList(); + + // return the PerfData item indicated by name, + // or NULL if it doesn't exist. + PerfData* find_by_name(const char* name); + + // return true if a PerfData item with the name specified in the + // argument exists, otherwise return false. + bool contains(const char* name) { return find_by_name(name) != NULL; } + + // return the number of PerfData items in this list + int length() { return _set->length(); } + + // add a PerfData item to this list + void append(PerfData *p) { _set->append(p); } + + // remove the given PerfData item from this list. When called + // while iterating over the list, this method will result in a + // change in the length of the container. The at(int index) + // method is also impacted by this method as elements with an + // index greater than the index of the element removed by this + // method will be shifted down by one. + void remove(PerfData *p) { _set->remove(p); } + + // create a new PerfDataList from this list. The new list is + // a shallow copy of the original list and care should be taken + // with respect to delete operations on the elements of the list + // as the are likely in use by another copy of the list. + PerfDataList* clone(); + + // for backward compatibility with GrowableArray - need to implement + // some form of iterator to provide a cleaner abstraction for + // iteration over the container. + PerfData* at(int index) { return _set->at(index); } +}; + + +/* + * The PerfDataManager class is responsible for creating PerfData + * subtypes via a set a factory methods and for managing lists + * of the various PerfData types. + */ +class PerfDataManager : AllStatic { + + friend class StatSampler; // for access to protected PerfDataList methods + + private: + static PerfDataList* _all; + static PerfDataList* _sampled; + static PerfDataList* _constants; + static const char* _name_spaces[]; + + // add a PerfData item to the list(s) of know PerfData objects + static void add_item(PerfData* p, bool sampled); + + protected: + // return the list of all known PerfData items + static PerfDataList* all(); + static int count() { return _all->length(); } + + // return the list of all known PerfData items that are to be + // sampled by the StatSampler. + static PerfDataList* sampled(); + static int sampled_count() { return _sampled->length(); } + + // return the list of all known PerfData items that have a + // variability classification of type Constant + static PerfDataList* constants(); + static int constants_count() { return _constants->length(); } + + public: + + // method to check for the existence of a PerfData item with + // the given name. + static bool exists(const char* name) { return _all->contains(name); } + + // method to map a CounterNS enumeration to a namespace string + static const char* ns_to_string(CounterNS ns) { + return _name_spaces[ns]; + } + + // methods to test the interface stability of a given counter namespace + // + static bool is_stable_supported(CounterNS ns) { + return (ns != NULL_NS) && ((ns % 3) == JAVA_NS); + } + static bool is_unstable_supported(CounterNS ns) { + return (ns != NULL_NS) && ((ns % 3) == COM_NS); + } + static bool is_unstable_unsupported(CounterNS ns) { + return (ns == NULL_NS) || ((ns % 3) == SUN_NS); + } + + // methods to test the interface stability of a given counter name + // + static bool is_stable_supported(const char* name) { + const char* javadot = "java."; + return strncmp(name, javadot, strlen(javadot)) == 0; + } + static bool is_unstable_supported(const char* name) { + const char* comdot = "com.sun."; + return strncmp(name, comdot, strlen(comdot)) == 0; + } + static bool is_unstable_unsupported(const char* name) { + return !(is_stable_supported(name) && is_unstable_supported(name)); + } + + // method to construct counter name strings in a given name space. + // The string object is allocated from the Resource Area and calls + // to this method must be made within a ResourceMark. + // + static char* counter_name(const char* name_space, const char* name); + + // method to construct name space strings in a given name space. + // The string object is allocated from the Resource Area and calls + // to this method must be made within a ResourceMark. + // + static char* name_space(const char* name_space, const char* sub_space) { + return counter_name(name_space, sub_space); + } + + // same as above, but appends the instance number to the name space + // + static char* name_space(const char* name_space, const char* sub_space, + int instance); + static char* name_space(const char* name_space, int instance); + + + // these methods provide the general interface for creating + // performance data resources. The types of performance data + // resources can be extended by adding additional create + // methods. + + // Constant Types + static PerfStringConstant* create_string_constant(CounterNS ns, + const char* name, + const char *s, TRAPS); + + static PerfLongConstant* create_long_constant(CounterNS ns, + const char* name, + PerfData::Units u, + jlong val, TRAPS); + + + // Variable Types + static PerfStringVariable* create_string_variable(CounterNS ns, + const char* name, + int max_length, + const char *s, TRAPS); + + static PerfStringVariable* create_string_variable(CounterNS ns, + const char* name, + const char *s, TRAPS) { + return create_string_variable(ns, name, 0, s, CHECK_NULL); + }; + + static PerfLongVariable* create_long_variable(CounterNS ns, + const char* name, + PerfData::Units u, + jlong ival, TRAPS); + + static PerfLongVariable* create_long_variable(CounterNS ns, + const char* name, + PerfData::Units u, TRAPS) { + return create_long_variable(ns, name, u, (jlong)0, CHECK_NULL); + }; + + static PerfLongVariable* create_long_variable(CounterNS, const char* name, + PerfData::Units u, + jlong* sp, TRAPS); + + static PerfLongVariable* create_long_variable(CounterNS ns, + const char* name, + PerfData::Units u, + PerfLongSampleHelper* sh, + TRAPS); + + + // Counter Types + static PerfLongCounter* create_long_counter(CounterNS ns, const char* name, + PerfData::Units u, + jlong ival, TRAPS); + + static PerfLongCounter* create_long_counter(CounterNS ns, const char* name, + PerfData::Units u, TRAPS) { + return create_long_counter(ns, name, u, (jlong)0, CHECK_NULL); + }; + + static PerfLongCounter* create_long_counter(CounterNS ns, const char* name, + PerfData::Units u, jlong* sp, + TRAPS); + + static PerfLongCounter* create_long_counter(CounterNS ns, const char* name, + PerfData::Units u, + PerfLongSampleHelper* sh, + TRAPS); + + + // these creation methods are provided for ease of use. These allow + // Long performance data types to be created with a shorthand syntax. + + static PerfConstant* create_constant(CounterNS ns, const char* name, + PerfData::Units u, jlong val, TRAPS) { + return create_long_constant(ns, name, u, val, CHECK_NULL); + } + + static PerfVariable* create_variable(CounterNS ns, const char* name, + PerfData::Units u, jlong ival, TRAPS) { + return create_long_variable(ns, name, u, ival, CHECK_NULL); + } + + static PerfVariable* create_variable(CounterNS ns, const char* name, + PerfData::Units u, TRAPS) { + return create_long_variable(ns, name, u, (jlong)0, CHECK_NULL); + } + + static PerfVariable* create_variable(CounterNS ns, const char* name, + PerfData::Units u, jlong* sp, TRAPS) { + return create_long_variable(ns, name, u, sp, CHECK_NULL); + } + + static PerfVariable* create_variable(CounterNS ns, const char* name, + PerfData::Units u, + PerfSampleHelper* sh, TRAPS) { + return create_long_variable(ns, name, u, sh, CHECK_NULL); + } + + static PerfCounter* create_counter(CounterNS ns, const char* name, + PerfData::Units u, jlong ival, TRAPS) { + return create_long_counter(ns, name, u, ival, CHECK_NULL); + } + + static PerfCounter* create_counter(CounterNS ns, const char* name, + PerfData::Units u, TRAPS) { + return create_long_counter(ns, name, u, (jlong)0, CHECK_NULL); + } + + static PerfCounter* create_counter(CounterNS ns, const char* name, + PerfData::Units u, jlong* sp, TRAPS) { + return create_long_counter(ns, name, u, sp, CHECK_NULL); + } + + static PerfCounter* create_counter(CounterNS ns, const char* name, + PerfData::Units u, + PerfSampleHelper* sh, TRAPS) { + return create_long_counter(ns, name, u, sh, CHECK_NULL); + } + + static void destroy(); +}; + +// Useful macros to create the performance counters +#define NEWPERFTICKCOUNTER(counter, counter_ns, counter_name) \ + {counter = PerfDataManager::create_counter(counter_ns, counter_name, \ + PerfData::U_Ticks,CHECK);} + +#define NEWPERFEVENTCOUNTER(counter, counter_ns, counter_name) \ + {counter = PerfDataManager::create_counter(counter_ns, counter_name, \ + PerfData::U_Events,CHECK);} + +// Utility Classes + +/* + * this class will administer a PerfCounter used as a time accumulator + * for a basic block much like the TraceTime class. + * + * Example: + * + * static PerfCounter* my_time_counter = PerfDataManager::create_counter("my.time.counter", PerfData::U_Ticks, 0LL, CHECK); + * + * { + * PerfTraceTime ptt(my_time_counter); + * // perform the operation you want to measure + * } + * + * Note: use of this class does not need to occur within a guarded + * block. The UsePerfData guard is used with the implementation + * of this class. + */ +class PerfTraceTime : public StackObj { + + protected: + elapsedTimer _t; + PerfLongCounter* _timerp; + // pointer to thread-local or global recursion counter variable + int* _recursion_counter; + + public: + inline PerfTraceTime(PerfLongCounter* timerp) : _timerp(timerp), _recursion_counter(NULL) { + if (!UsePerfData) return; + _t.start(); + } + + inline PerfTraceTime(PerfLongCounter* timerp, int* recursion_counter) : _timerp(timerp), _recursion_counter(recursion_counter) { + if (!UsePerfData || (_recursion_counter != NULL && + (*_recursion_counter)++ > 0)) return; + _t.start(); + } + + inline void suspend() { if (!UsePerfData) return; _t.stop(); } + inline void resume() { if (!UsePerfData) return; _t.start(); } + + inline ~PerfTraceTime() { + if (!UsePerfData || (_recursion_counter != NULL && + --(*_recursion_counter) > 0)) return; + _t.stop(); + _timerp->inc(_t.ticks()); + } +}; + +/* The PerfTraceTimedEvent class is responsible for counting the + * occurrence of some event and measuring the the elapsed time of + * the event in two separate PerfCounter instances. + * + * Example: + * + * static PerfCounter* my_time_counter = PerfDataManager::create_counter("my.time.counter", PerfData::U_Ticks, CHECK); + * static PerfCounter* my_event_counter = PerfDataManager::create_counter("my.event.counter", PerfData::U_Events, CHECK); + * + * { + * PerfTraceTimedEvent ptte(my_time_counter, my_event_counter); + * // perform the operation you want to count and measure + * } + * + * Note: use of this class does not need to occur within a guarded + * block. The UsePerfData guard is used with the implementation + * of this class. + * + */ +class PerfTraceTimedEvent : public PerfTraceTime { + + protected: + PerfLongCounter* _eventp; + + public: + inline PerfTraceTimedEvent(PerfLongCounter* timerp, PerfLongCounter* eventp): PerfTraceTime(timerp), _eventp(eventp) { + if (!UsePerfData) return; + _eventp->inc(); + } + + inline PerfTraceTimedEvent(PerfLongCounter* timerp, PerfLongCounter* eventp, int* recursion_counter): PerfTraceTime(timerp, recursion_counter), _eventp(eventp) { + if (!UsePerfData) return; + _eventp->inc(); + } +}; diff --git a/hotspot/src/share/vm/runtime/perfMemory.cpp b/hotspot/src/share/vm/runtime/perfMemory.cpp new file mode 100644 index 00000000000..0ae679bdd00 --- /dev/null +++ b/hotspot/src/share/vm/runtime/perfMemory.cpp @@ -0,0 +1,248 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_perfMemory.cpp.incl" + +char* PerfMemory::_start = NULL; +char* PerfMemory::_end = NULL; +char* PerfMemory::_top = NULL; +size_t PerfMemory::_capacity = 0; +jint PerfMemory::_initialized = false; +PerfDataPrologue* PerfMemory::_prologue = NULL; + +void perfMemory_init() { + + if (!UsePerfData) return; + + PerfMemory::initialize(); +} + +void perfMemory_exit() { + + if (!UsePerfData) return; + if (!PerfMemory::is_initialized()) return; + + // if the StatSampler is active, then we don't want to remove + // resources it may be dependent on. Typically, the StatSampler + // is disengaged from the watcher thread when this method is called, + // but it is not disengaged if this method is invoked during a + // VM abort. + // + if (!StatSampler::is_active()) + PerfDataManager::destroy(); + + // remove the persistent external resources, if any. this method + // does not unmap or invalidate any virtual memory allocated during + // initialization. + // + PerfMemory::destroy(); +} + +void PerfMemory::initialize() { + + if (_prologue != NULL) + // initialization already performed + return; + + size_t capacity = align_size_up(PerfDataMemorySize, + os::vm_allocation_granularity()); + + if (PerfTraceMemOps) { + tty->print("PerfDataMemorySize = " SIZE_FORMAT "," + " os::vm_allocation_granularity = " SIZE_FORMAT "," + " adjusted size = " SIZE_FORMAT "\n", + PerfDataMemorySize, + os::vm_allocation_granularity(), + capacity); + } + + // allocate PerfData memory region + create_memory_region(capacity); + + if (_start == NULL) { + + // the PerfMemory region could not be created as desired. Rather + // than terminating the JVM, we revert to creating the instrumentation + // on the C heap. When running in this mode, external monitoring + // clients cannot attach to and monitor this JVM. + // + // the warning is issued only in debug mode in order to avoid + // additional output to the stdout or stderr output streams. + // + if (PrintMiscellaneous && Verbose) { + warning("Could not create PerfData Memory region, reverting to malloc"); + } + + _prologue = NEW_C_HEAP_OBJ(PerfDataPrologue); + } + else { + + // the PerfMemory region was created as expected. + + if (PerfTraceMemOps) { + tty->print("PerfMemory created: address = " INTPTR_FORMAT "," + " size = " SIZE_FORMAT "\n", + (void*)_start, + _capacity); + } + + _prologue = (PerfDataPrologue *)_start; + _end = _start + _capacity; + _top = _start + sizeof(PerfDataPrologue); + } + + assert(_prologue != NULL, "prologue pointer must be initialized"); + +#ifdef VM_LITTLE_ENDIAN + _prologue->magic = (jint)0xc0c0feca; + _prologue->byte_order = PERFDATA_LITTLE_ENDIAN; +#else + _prologue->magic = (jint)0xcafec0c0; + _prologue->byte_order = PERFDATA_BIG_ENDIAN; +#endif + + _prologue->major_version = PERFDATA_MAJOR_VERSION; + _prologue->minor_version = PERFDATA_MINOR_VERSION; + _prologue->accessible = 0; + + _prologue->entry_offset = sizeof(PerfDataPrologue); + _prologue->num_entries = 0; + _prologue->used = 0; + _prologue->overflow = 0; + _prologue->mod_time_stamp = 0; + + OrderAccess::release_store(&_initialized, 1); +} + +void PerfMemory::destroy() { + + assert(_prologue != NULL, "prologue pointer must be initialized"); + + if (_start != NULL && _prologue->overflow != 0) { + + // This state indicates that the contiguous memory region exists and + // that it wasn't large enough to hold all the counters. In this case, + // we output a warning message to the user on exit if the -XX:+Verbose + // flag is set (a debug only flag). External monitoring tools can detect + // this condition by monitoring the _prologue->overflow word. + // + // There are two tunables that can help resolve this issue: + // - increase the size of the PerfMemory with -XX:PerfDataMemorySize= + // - decrease the maximum string constant length with + // -XX:PerfMaxStringConstLength= + // + if (PrintMiscellaneous && Verbose) { + warning("PerfMemory Overflow Occurred.\n" + "\tCapacity = " SIZE_FORMAT " bytes" + " Used = " SIZE_FORMAT " bytes" + " Overflow = " INT32_FORMAT " bytes" + "\n\tUse -XX:PerfDataMemorySize= to specify larger size.", + PerfMemory::capacity(), + PerfMemory::used(), + _prologue->overflow); + } + } + + if (_start != NULL) { + + // this state indicates that the contiguous memory region was successfully + // and that persistent resources may need to be cleaned up. This is + // expected to be the typical condition. + // + delete_memory_region(); + } + + _start = NULL; + _end = NULL; + _top = NULL; + _prologue = NULL; + _capacity = 0; +} + +// allocate an aligned block of memory from the PerfData memory +// region. This method assumes that the PerfData memory region +// was aligned on a double word boundary when created. +// +char* PerfMemory::alloc(size_t size) { + + if (!UsePerfData) return NULL; + + MutexLocker ml(PerfDataMemAlloc_lock); + + assert(_prologue != NULL, "called before initialization"); + + // check that there is enough memory for this request + if ((_top + size) >= _end) { + + _prologue->overflow += (jint)size; + + return NULL; + } + + char* result = _top; + + _top += size; + + assert(contains(result), "PerfData memory pointer out of range"); + + _prologue->used = (jint)used(); + _prologue->num_entries = _prologue->num_entries + 1; + + return result; +} + +void PerfMemory::mark_updated() { + if (!UsePerfData) return; + + _prologue->mod_time_stamp = os::elapsed_counter(); +} + +// Returns the complete path including the file name of performance data file. +// Caller is expected to release the allocated memory. +char* PerfMemory::get_perfdata_file_path() { + char* dest_file = NULL; + + if (PerfDataSaveFile != NULL) { + // dest_file_name stores the validated file name if file_name + // contains %p which will be replaced by pid. + dest_file = NEW_C_HEAP_ARRAY(char, JVM_MAXPATHLEN); + if(!Arguments::copy_expand_pid(PerfDataSaveFile, strlen(PerfDataSaveFile), + dest_file, JVM_MAXPATHLEN)) { + FREE_C_HEAP_ARRAY(char, dest_file); + if (PrintMiscellaneous && Verbose) { + warning("Invalid performance data file path name specified, "\ + "fall back to a default name"); + } + } else { + return dest_file; + } + } + // create the name of the file for retaining the instrumentation memory. + dest_file = NEW_C_HEAP_ARRAY(char, PERFDATA_FILENAME_LEN); + jio_snprintf(dest_file, PERFDATA_FILENAME_LEN, + "%s_%d", PERFDATA_NAME, os::current_process_id()); + + return dest_file; +} diff --git a/hotspot/src/share/vm/runtime/perfMemory.hpp b/hotspot/src/share/vm/runtime/perfMemory.hpp new file mode 100644 index 00000000000..812ab3bac23 --- /dev/null +++ b/hotspot/src/share/vm/runtime/perfMemory.hpp @@ -0,0 +1,167 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * PerfData Version Constants + * - Major Version - change whenever the structure of PerfDataEntry changes + * - Minor Version - change whenever the data within the PerfDataEntry + * structure changes. for example, new unit or variability + * values are added or new PerfData subtypes are added. + */ +#define PERFDATA_MAJOR_VERSION 2 +#define PERFDATA_MINOR_VERSION 0 + +/* Byte order of the PerfData memory region. The byte order is exposed in + * the PerfData memory region as the data in the memory region may have + * been generated by a little endian JVM implementation. Tracking the byte + * order in the PerfData memory region allows Java applications to adapt + * to the native byte order for monitoring purposes. This indicator is + * also useful when a snapshot of the PerfData memory region is shipped + * to a machine with a native byte order different from that of the + * originating machine. + */ +#define PERFDATA_BIG_ENDIAN 0 +#define PERFDATA_LITTLE_ENDIAN 1 + +/* + * The PerfDataPrologue structure is known by the PerfDataBuffer Java class + * libraries that read the PerfData memory region. The size and the position + * of the fields must be changed along with their counterparts in the + * PerfDataBuffer Java class. The first four bytes of this structure + * should never change, or compatibility problems between the monitoring + * applications and Hotspot VMs will result. The reserved fields are + * available for future enhancements. + */ +typedef struct { + jint magic; // magic number - 0xcafec0c0 + jbyte byte_order; // byte order of the buffer + jbyte major_version; // major and minor version numbers + jbyte minor_version; + jbyte accessible; // ready to access + jint used; // number of PerfData memory bytes used + jint overflow; // number of bytes of overflow + jlong mod_time_stamp; // time stamp of last structural modification + jint entry_offset; // offset of the first PerfDataEntry + jint num_entries; // number of allocated PerfData entries +} PerfDataPrologue; + +/* The PerfDataEntry structure defines the fixed portion of an entry + * in the PerfData memory region. The PerfDataBuffer Java libraries + * are aware of this structure and need to be changed when this + * structure changes. + */ +typedef struct { + + jint entry_length; // entry length in bytes + jint name_offset; // offset of the data item name + jint vector_length; // length of the vector. If 0, then scalar + jbyte data_type; // type of the data item - + // 'B','Z','J','I','S','C','D','F','V','L','[' + jbyte flags; // flags indicating misc attributes + jbyte data_units; // unit of measure for the data type + jbyte data_variability; // variability classification of data type + jint data_offset; // offset of the data item + +/* + body of PerfData memory entry is variable length + + jbyte[name_length] data_name; // name of the data item + jbyte[pad_length] data_pad; // alignment of data item + j[data_length] data_item; // array of appropriate types. + // data_length is > 1 only when the + // data_type is T_ARRAY. +*/ +} PerfDataEntry; + +// Prefix of performance data file. +static const char PERFDATA_NAME[] = "hsperfdata"; + +// UINT_CHARS contains the number of characters holding a process id +// (i.e. pid). pid is defined as unsigned "int" so the maximum possible pid value +// would be 2^32 - 1 (4294967295) which can be represented as a 10 characters +// string. +static const size_t UINT_CHARS = 10; + +// Add 1 for the '_' character between PERFDATA_NAME and pid. The '\0' terminating +// character will be included in the sizeof(PERFDATA_NAME) operation. +static const size_t PERFDATA_FILENAME_LEN = sizeof(PERFDATA_NAME) + + UINT_CHARS + 1; + +/* the PerfMemory class manages creation, destruction, + * and allocation of the PerfData region. + */ +class PerfMemory : AllStatic { + friend class VMStructs; + private: + static char* _start; + static char* _end; + static char* _top; + static size_t _capacity; + static PerfDataPrologue* _prologue; + static jint _initialized; + + static void create_memory_region(size_t sizep); + static void delete_memory_region(); + + public: + enum PerfMemoryMode { + PERF_MODE_RO = 0, + PERF_MODE_RW = 1 + }; + + static char* alloc(size_t size); + static char* start() { return _start; } + static char* end() { return _end; } + static size_t used() { return (size_t) (_top - _start); } + static size_t capacity() { return _capacity; } + static bool is_initialized() { return _initialized != 0; } + static bool contains(char* addr) { + return ((_start != NULL) && (addr >= _start) && (addr < _end)); + } + static void mark_updated(); + + // methods for attaching to and detaching from the PerfData + // memory segment of another JVM process on the same system. + static void attach(const char* user, int vmid, PerfMemoryMode mode, + char** addrp, size_t* size, TRAPS); + static void detach(char* addr, size_t bytes, TRAPS); + + static void initialize(); + static void destroy(); + static void set_accessible(bool value) { + if (UsePerfData) { + _prologue->accessible = value; + } + } + + // filename of backing store or NULL if none. + static char* backing_store_filename(); + + // returns the complete file path of hsperfdata. + // the caller is expected to free the allocated memory. + static char* get_perfdata_file_path(); +}; + +void perfMemory_init(); +void perfMemory_exit(); diff --git a/hotspot/src/share/vm/runtime/prefetch.hpp b/hotspot/src/share/vm/runtime/prefetch.hpp new file mode 100644 index 00000000000..0c498f8399e --- /dev/null +++ b/hotspot/src/share/vm/runtime/prefetch.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// If calls to prefetch methods are in a loop, the loop should be cloned +// such that if Prefetch{Scan,Copy}Interval and/or PrefetchFieldInterval +// say not to do prefetching, these methods aren't called. At the very +// least, they take up a memory issue slot. They should be implemented +// as inline assembly code: doing an actual call isn't worth the cost. + +class Prefetch : AllStatic { + public: + enum style { + do_none, // Do no prefetching + do_read, // Do read prefetching + do_write // Do write prefetching + }; + + // Prefetch anticipating read; must not fault, semantically a no-op + static void read(void* loc, intx interval); + + // Prefetch anticipating write; must not fault, semantically a no-op + static void write(void* loc, intx interval); +}; diff --git a/hotspot/src/share/vm/runtime/reflection.cpp b/hotspot/src/share/vm/runtime/reflection.cpp new file mode 100644 index 00000000000..55cf925e6e2 --- /dev/null +++ b/hotspot/src/share/vm/runtime/reflection.cpp @@ -0,0 +1,1586 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_reflection.cpp.incl" + +#define JAVA_1_5_VERSION 49 + +static void trace_class_resolution(klassOop to_class) { + ResourceMark rm; + int line_number = -1; + const char * source_file = NULL; + klassOop caller = NULL; + JavaThread* jthread = JavaThread::current(); + if (jthread->has_last_Java_frame()) { + vframeStream vfst(jthread); + // skip over any frames belonging to java.lang.Class + while (!vfst.at_end() && + instanceKlass::cast(vfst.method()->method_holder())->name() == vmSymbols::java_lang_Class()) { + vfst.next(); + } + if (!vfst.at_end()) { + // this frame is a likely suspect + caller = vfst.method()->method_holder(); + line_number = vfst.method()->line_number_from_bci(vfst.bci()); + symbolOop s = instanceKlass::cast(vfst.method()->method_holder())->source_file_name(); + if (s != NULL) { + source_file = s->as_C_string(); + } + } + } + if (caller != NULL) { + const char * from = Klass::cast(caller)->external_name(); + const char * to = Klass::cast(to_class)->external_name(); + // print in a single call to reduce interleaving between threads + if (source_file != NULL) { + tty->print("RESOLVE %s %s %s:%d (reflection)\n", from, to, source_file, line_number); + } else { + tty->print("RESOLVE %s %s (reflection)\n", from, to); + } + } +} + + +oop Reflection::box(jvalue* value, BasicType type, TRAPS) { + if (type == T_VOID) { + return NULL; + } + if (type == T_OBJECT || type == T_ARRAY) { + // regular objects are not boxed + return (oop) value->l; + } + oop result = java_lang_boxing_object::create(type, value, CHECK_NULL); + if (result == NULL) { + THROW_(vmSymbols::java_lang_IllegalArgumentException(), result); + } + return result; +} + + +BasicType Reflection::unbox_for_primitive(oop box, jvalue* value, TRAPS) { + if (box == NULL) { + THROW_(vmSymbols::java_lang_IllegalArgumentException(), T_ILLEGAL); + } + return java_lang_boxing_object::get_value(box, value); +} + +BasicType Reflection::unbox_for_regular_object(oop box, jvalue* value) { + // Note: box is really the unboxed oop. It might even be a Short, etc.! + value->l = (jobject) box; + return T_OBJECT; +} + + +void Reflection::widen(jvalue* value, BasicType current_type, BasicType wide_type, TRAPS) { + assert(wide_type != current_type, "widen should not be called with identical types"); + switch (wide_type) { + case T_BOOLEAN: + case T_BYTE: + case T_CHAR: + break; // fail + case T_SHORT: + switch (current_type) { + case T_BYTE: + value->s = (jshort) value->b; + return; + } + break; // fail + case T_INT: + switch (current_type) { + case T_BYTE: + value->i = (jint) value->b; + return; + case T_CHAR: + value->i = (jint) value->c; + return; + case T_SHORT: + value->i = (jint) value->s; + return; + } + break; // fail + case T_LONG: + switch (current_type) { + case T_BYTE: + value->j = (jlong) value->b; + return; + case T_CHAR: + value->j = (jlong) value->c; + return; + case T_SHORT: + value->j = (jlong) value->s; + return; + case T_INT: + value->j = (jlong) value->i; + return; + } + break; // fail + case T_FLOAT: + switch (current_type) { + case T_BYTE: + value->f = (jfloat) value->b; + return; + case T_CHAR: + value->f = (jfloat) value->c; + return; + case T_SHORT: + value->f = (jfloat) value->s; + return; + case T_INT: + value->f = (jfloat) value->i; + return; + case T_LONG: + value->f = (jfloat) value->j; + return; + } + break; // fail + case T_DOUBLE: + switch (current_type) { + case T_BYTE: + value->d = (jdouble) value->b; + return; + case T_CHAR: + value->d = (jdouble) value->c; + return; + case T_SHORT: + value->d = (jdouble) value->s; + return; + case T_INT: + value->d = (jdouble) value->i; + return; + case T_FLOAT: + value->d = (jdouble) value->f; + return; + case T_LONG: + value->d = (jdouble) value->j; + return; + } + break; // fail + default: + break; // fail + } + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "argument type mismatch"); +} + + +BasicType Reflection::array_get(jvalue* value, arrayOop a, int index, TRAPS) { + if (!a->is_within_bounds(index)) { + THROW_(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), T_ILLEGAL); + } + if (a->is_objArray()) { + value->l = (jobject) objArrayOop(a)->obj_at(index); + return T_OBJECT; + } else { + assert(a->is_typeArray(), "just checking"); + BasicType type = typeArrayKlass::cast(a->klass())->element_type(); + switch (type) { + case T_BOOLEAN: + value->z = typeArrayOop(a)->bool_at(index); + break; + case T_CHAR: + value->c = typeArrayOop(a)->char_at(index); + break; + case T_FLOAT: + value->f = typeArrayOop(a)->float_at(index); + break; + case T_DOUBLE: + value->d = typeArrayOop(a)->double_at(index); + break; + case T_BYTE: + value->b = typeArrayOop(a)->byte_at(index); + break; + case T_SHORT: + value->s = typeArrayOop(a)->short_at(index); + break; + case T_INT: + value->i = typeArrayOop(a)->int_at(index); + break; + case T_LONG: + value->j = typeArrayOop(a)->long_at(index); + break; + default: + return T_ILLEGAL; + } + return type; + } +} + + +void Reflection::array_set(jvalue* value, arrayOop a, int index, BasicType value_type, TRAPS) { + if (!a->is_within_bounds(index)) { + THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + } + if (a->is_objArray()) { + if (value_type == T_OBJECT) { + oop obj = (oop) value->l; + if (obj != NULL) { + klassOop element_klass = objArrayKlass::cast(a->klass())->element_klass(); + if (!obj->is_a(element_klass)) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "array element type mismatch"); + } + } + objArrayOop(a)->obj_at_put(index, obj); + } + } else { + assert(a->is_typeArray(), "just checking"); + BasicType array_type = typeArrayKlass::cast(a->klass())->element_type(); + if (array_type != value_type) { + // The widen operation can potentially throw an exception, but cannot block, + // so typeArrayOop a is safe if the call succeeds. + widen(value, value_type, array_type, CHECK); + } + switch (array_type) { + case T_BOOLEAN: + typeArrayOop(a)->bool_at_put(index, value->z); + break; + case T_CHAR: + typeArrayOop(a)->char_at_put(index, value->c); + break; + case T_FLOAT: + typeArrayOop(a)->float_at_put(index, value->f); + break; + case T_DOUBLE: + typeArrayOop(a)->double_at_put(index, value->d); + break; + case T_BYTE: + typeArrayOop(a)->byte_at_put(index, value->b); + break; + case T_SHORT: + typeArrayOop(a)->short_at_put(index, value->s); + break; + case T_INT: + typeArrayOop(a)->int_at_put(index, value->i); + break; + case T_LONG: + typeArrayOop(a)->long_at_put(index, value->j); + break; + default: + THROW(vmSymbols::java_lang_IllegalArgumentException()); + } + } +} + + +klassOop Reflection::basic_type_mirror_to_arrayklass(oop basic_type_mirror, TRAPS) { + assert(java_lang_Class::is_primitive(basic_type_mirror), "just checking"); + BasicType type = java_lang_Class::primitive_type(basic_type_mirror); + if (type == T_VOID) { + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } else { + return Universe::typeArrayKlassObj(type); + } +} + + +oop Reflection:: basic_type_arrayklass_to_mirror(klassOop basic_type_arrayklass, TRAPS) { + BasicType type = typeArrayKlass::cast(basic_type_arrayklass)->element_type(); + return Universe::java_mirror(type); +} + + +arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) { + if (element_mirror == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + if (length < 0) { + THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); + } + if (java_lang_Class::is_primitive(element_mirror)) { + klassOop tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL); + return typeArrayKlass::cast(tak)->allocate(length, THREAD); + } else { + klassOop k = java_lang_Class::as_klassOop(element_mirror); + if (Klass::cast(k)->oop_is_array() && arrayKlass::cast(k)->dimension() >= MAX_DIM) { + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + return oopFactory::new_objArray(k, length, THREAD); + } +} + + +arrayOop Reflection::reflect_new_multi_array(oop element_mirror, typeArrayOop dim_array, TRAPS) { + assert(dim_array->is_typeArray(), "just checking"); + assert(typeArrayKlass::cast(dim_array->klass())->element_type() == T_INT, "just checking"); + + if (element_mirror == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + + int len = dim_array->length(); + if (len <= 0 || len > MAX_DIM) { + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + + jint dimensions[MAX_DIM]; // C array copy of intArrayOop + for (int i = 0; i < len; i++) { + int d = dim_array->int_at(i); + if (d < 0) { + THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); + } + dimensions[i] = d; + } + + klassOop klass; + int dim = len; + if (java_lang_Class::is_primitive(element_mirror)) { + klass = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL); + } else { + klass = java_lang_Class::as_klassOop(element_mirror); + if (Klass::cast(klass)->oop_is_array()) { + int k_dim = arrayKlass::cast(klass)->dimension(); + if (k_dim + len > MAX_DIM) { + THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + } + dim += k_dim; + } + } + klass = Klass::cast(klass)->array_klass(dim, CHECK_NULL); + oop obj = arrayKlass::cast(klass)->multi_allocate(len, dimensions, THREAD); + assert(obj->is_array(), "just checking"); + return arrayOop(obj); +} + + +oop Reflection::array_component_type(oop mirror, TRAPS) { + if (java_lang_Class::is_primitive(mirror)) { + return NULL; + } + + klassOop klass = java_lang_Class::as_klassOop(mirror); + if (!Klass::cast(klass)->oop_is_array()) { + return NULL; + } + + oop result = arrayKlass::cast(klass)->component_mirror(); +#ifdef ASSERT + oop result2 = NULL; + if (arrayKlass::cast(klass)->dimension() == 1) { + if (Klass::cast(klass)->oop_is_typeArray()) { + result2 = basic_type_arrayklass_to_mirror(klass, CHECK_NULL); + } else { + result2 = Klass::cast(objArrayKlass::cast(klass)->element_klass())->java_mirror(); + } + } else { + klassOop lower_dim = arrayKlass::cast(klass)->lower_dimension(); + assert(Klass::cast(lower_dim)->oop_is_array(), "just checking"); + result2 = Klass::cast(lower_dim)->java_mirror(); + } + assert(result == result2, "results must be consistent"); +#endif //ASSERT + return result; +} + + +bool Reflection::reflect_check_access(klassOop field_class, AccessFlags acc, klassOop target_class, bool is_method_invoke, TRAPS) { + // field_class : declaring class + // acc : declared field access + // target_class : for protected + + // Check if field or method is accessible to client. Throw an + // IllegalAccessException and return false if not. + + // The "client" is the class associated with the nearest real frame + // getCallerClass already skips Method.invoke frames, so pass 0 in + // that case (same as classic). + ResourceMark rm(THREAD); + assert(THREAD->is_Java_thread(), "sanity check"); + klassOop client_class = ((JavaThread *)THREAD)->security_get_caller_class(is_method_invoke ? 0 : 1); + + if (client_class != field_class) { + if (!verify_class_access(client_class, field_class, false) + || !verify_field_access(client_class, + field_class, + field_class, + acc, + false)) { + THROW_(vmSymbols::java_lang_IllegalAccessException(), false); + } + } + + // Additional test for protected members: JLS 6.6.2 + + if (acc.is_protected()) { + if (target_class != client_class) { + if (!is_same_class_package(client_class, field_class)) { + if (!Klass::cast(target_class)->is_subclass_of(client_class)) { + THROW_(vmSymbols::java_lang_IllegalAccessException(), false); + } + } + } + } + + // Passed all tests + return true; +} + + +bool Reflection::verify_class_access(klassOop current_class, klassOop new_class, bool classloader_only) { + // Verify that current_class can access new_class. If the classloader_only + // flag is set, we automatically allow any accesses in which current_class + // doesn't have a classloader. + if ((current_class == NULL) || + (current_class == new_class) || + (instanceKlass::cast(new_class)->is_public()) || + is_same_class_package(current_class, new_class)) { + return true; + } + // New (1.4) reflection implementation. Allow all accesses from + // sun/reflect/MagicAccessorImpl subclasses to succeed trivially. + if ( JDK_Version::is_gte_jdk14x_version() + && UseNewReflection + && Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_magic_klass())) { + return true; + } + + return can_relax_access_check_for(current_class, new_class, classloader_only); +} + +bool Reflection::can_relax_access_check_for( + klassOop accessor, klassOop accessee, bool classloader_only) { + instanceKlass* accessor_ik = instanceKlass::cast(accessor); + instanceKlass* accessee_ik = instanceKlass::cast(accessee); + if (RelaxAccessControlCheck || + (accessor_ik->major_version() < JAVA_1_5_VERSION && + accessee_ik->major_version() < JAVA_1_5_VERSION)) { + return classloader_only && + Verifier::relax_verify_for(accessor_ik->class_loader()) && + accessor_ik->protection_domain() == accessee_ik->protection_domain() && + accessor_ik->class_loader() == accessee_ik->class_loader(); + } else { + return false; + } +} + +bool Reflection::verify_field_access(klassOop current_class, + klassOop resolved_class, + klassOop field_class, + AccessFlags access, + bool classloader_only, + bool protected_restriction) { + // Verify that current_class can access a field of field_class, where that + // field's access bits are "access". We assume that we've already verified + // that current_class can access field_class. + // + // If the classloader_only flag is set, we automatically allow any accesses + // in which current_class doesn't have a classloader. + // + // "resolved_class" is the runtime type of "field_class". Sometimes we don't + // need this distinction (e.g. if all we have is the runtime type, or during + // class file parsing when we only care about the static type); in that case + // callers should ensure that resolved_class == field_class. + // + if ((current_class == NULL) || + (current_class == field_class) || + access.is_public()) { + return true; + } + + if (access.is_protected()) { + if (!protected_restriction) { + // See if current_class is a subclass of field_class + if (Klass::cast(current_class)->is_subclass_of(field_class)) { + if (current_class == resolved_class || + field_class == resolved_class || + Klass::cast(current_class)->is_subclass_of(resolved_class) || + Klass::cast(resolved_class)->is_subclass_of(current_class)) { + return true; + } + } + } + } + + if (!access.is_private() && is_same_class_package(current_class, field_class)) { + return true; + } + + // New (1.4) reflection implementation. Allow all accesses from + // sun/reflect/MagicAccessorImpl subclasses to succeed trivially. + if ( JDK_Version::is_gte_jdk14x_version() + && UseNewReflection + && Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_magic_klass())) { + return true; + } + + return can_relax_access_check_for( + current_class, field_class, classloader_only); +} + + +bool Reflection::is_same_class_package(klassOop class1, klassOop class2) { + return instanceKlass::cast(class1)->is_same_class_package(class2); +} + + +// Checks that the 'outer' klass has declared 'inner' as being an inner klass. If not, +// throw an incompatible class change exception +void Reflection::check_for_inner_class(instanceKlassHandle outer, instanceKlassHandle inner, TRAPS) { + const int inner_class_info_index = 0; + const int outer_class_info_index = 1; + + typeArrayHandle icls (THREAD, outer->inner_classes()); + constantPoolHandle cp (THREAD, outer->constants()); + for(int i = 0; i < icls->length(); i += 4) { + int ioff = icls->ushort_at(i + inner_class_info_index); + int ooff = icls->ushort_at(i + outer_class_info_index); + + if (ioff != 0 && ooff != 0) { + klassOop o = cp->klass_at(ooff, CHECK); + if (o == outer()) { + klassOop i = cp->klass_at(ioff, CHECK); + if (i == inner()) { + return; + } + } + } + } + + // 'inner' not declared as an inner klass in outer + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbolHandles::java_lang_IncompatibleClassChangeError(), + "%s and %s disagree on InnerClasses attribute", + outer->external_name(), + inner->external_name() + ); +} + +// Utility method converting a single SignatureStream element into java.lang.Class instance + +oop get_mirror_from_signature(methodHandle method, SignatureStream* ss, TRAPS) { + switch (ss->type()) { + default: + assert(ss->type() != T_VOID || ss->at_return_type(), "T_VOID should only appear as return type"); + return java_lang_Class::primitive_mirror(ss->type()); + case T_OBJECT: + case T_ARRAY: + symbolOop name = ss->as_symbol(CHECK_NULL); + oop loader = instanceKlass::cast(method->method_holder())->class_loader(); + oop protection_domain = instanceKlass::cast(method->method_holder())->protection_domain(); + klassOop k = SystemDictionary::resolve_or_fail( + symbolHandle(THREAD, name), + Handle(THREAD, loader), + Handle(THREAD, protection_domain), + true, CHECK_NULL); + if (TraceClassResolution) { + trace_class_resolution(k); + } + return k->klass_part()->java_mirror(); + }; +} + + +objArrayHandle Reflection::get_parameter_types(methodHandle method, int parameter_count, oop* return_type, TRAPS) { + // Allocate array holding parameter types (java.lang.Class instances) + objArrayOop m = oopFactory::new_objArray(SystemDictionary::class_klass(), parameter_count, CHECK_(objArrayHandle())); + objArrayHandle mirrors (THREAD, m); + int index = 0; + // Collect parameter types + symbolHandle signature (THREAD, method->signature()); + SignatureStream ss(signature); + while (!ss.at_return_type()) { + oop mirror = get_mirror_from_signature(method, &ss, CHECK_(objArrayHandle())); + mirrors->obj_at_put(index++, mirror); + ss.next(); + } + assert(index == parameter_count, "invalid parameter count"); + if (return_type != NULL) { + // Collect return type as well + assert(ss.at_return_type(), "return type should be present"); + *return_type = get_mirror_from_signature(method, &ss, CHECK_(objArrayHandle())); + } + return mirrors; +} + +objArrayHandle Reflection::get_exception_types(methodHandle method, TRAPS) { + return method->resolved_checked_exceptions(CHECK_(objArrayHandle())); +} + + +Handle Reflection::new_type(symbolHandle signature, KlassHandle k, TRAPS) { + // Basic types + BasicType type = vmSymbols::signature_type(signature()); + if (type != T_OBJECT) { + return Handle(THREAD, Universe::java_mirror(type)); + } + + oop loader = instanceKlass::cast(k())->class_loader(); + oop protection_domain = Klass::cast(k())->protection_domain(); + klassOop result = SystemDictionary::resolve_or_fail(signature, + Handle(THREAD, loader), + Handle(THREAD, protection_domain), + true, CHECK_(Handle())); + + if (TraceClassResolution) { + trace_class_resolution(result); + } + + oop nt = Klass::cast(result)->java_mirror(); + return Handle(THREAD, nt); +} + + +oop Reflection::new_method(methodHandle method, bool intern_name, bool for_constant_pool_access, TRAPS) { + // In jdk1.2.x, getMethods on an interface erroneously includes , thus the complicated assert. + // Also allow sun.reflect.ConstantPool to refer to methods as java.lang.reflect.Methods. + assert(!method()->is_initializer() || + (for_constant_pool_access && method()->is_static()) || + (method()->name() == vmSymbols::class_initializer_name() + && Klass::cast(method()->method_holder())->is_interface() && JDK_Version::is_jdk12x_version()), "should call new_constructor instead"); + instanceKlassHandle holder (THREAD, method->method_holder()); + int slot = method->method_idnum(); + + symbolHandle signature (THREAD, method->signature()); + int parameter_count = ArgumentCount(signature).size(); + oop return_type_oop = NULL; + objArrayHandle parameter_types = get_parameter_types(method, parameter_count, &return_type_oop, CHECK_NULL); + if (parameter_types.is_null() || return_type_oop == NULL) return NULL; + + Handle return_type(THREAD, return_type_oop); + + objArrayHandle exception_types = get_exception_types(method, CHECK_NULL); + + if (exception_types.is_null()) return NULL; + + symbolHandle method_name(THREAD, method->name()); + Handle name; + if (intern_name) { + // intern_name is only true with UseNewReflection + oop name_oop = StringTable::intern(method_name(), CHECK_NULL); + name = Handle(THREAD, name_oop); + } else { + name = java_lang_String::create_from_symbol(method_name, CHECK_NULL); + } + if (name.is_null()) return NULL; + + int modifiers = method->access_flags().as_int() & JVM_RECOGNIZED_METHOD_MODIFIERS; + + Handle mh = java_lang_reflect_Method::create(CHECK_NULL); + + java_lang_reflect_Method::set_clazz(mh(), holder->java_mirror()); + java_lang_reflect_Method::set_slot(mh(), slot); + java_lang_reflect_Method::set_name(mh(), name()); + java_lang_reflect_Method::set_return_type(mh(), return_type()); + java_lang_reflect_Method::set_parameter_types(mh(), parameter_types()); + java_lang_reflect_Method::set_exception_types(mh(), exception_types()); + java_lang_reflect_Method::set_modifiers(mh(), modifiers); + java_lang_reflect_Method::set_override(mh(), false); + if (java_lang_reflect_Method::has_signature_field() && + method->generic_signature() != NULL) { + symbolHandle gs(THREAD, method->generic_signature()); + Handle sig = java_lang_String::create_from_symbol(gs, CHECK_NULL); + java_lang_reflect_Method::set_signature(mh(), sig()); + } + if (java_lang_reflect_Method::has_annotations_field()) { + java_lang_reflect_Method::set_annotations(mh(), method->annotations()); + } + if (java_lang_reflect_Method::has_parameter_annotations_field()) { + java_lang_reflect_Method::set_parameter_annotations(mh(), method->parameter_annotations()); + } + if (java_lang_reflect_Method::has_annotation_default_field()) { + java_lang_reflect_Method::set_annotation_default(mh(), method->annotation_default()); + } + return mh(); +} + + +oop Reflection::new_constructor(methodHandle method, TRAPS) { + assert(method()->is_initializer(), "should call new_method instead"); + + instanceKlassHandle holder (THREAD, method->method_holder()); + int slot = method->method_idnum(); + + symbolHandle signature (THREAD, method->signature()); + int parameter_count = ArgumentCount(signature).size(); + objArrayHandle parameter_types = get_parameter_types(method, parameter_count, NULL, CHECK_NULL); + if (parameter_types.is_null()) return NULL; + + objArrayHandle exception_types = get_exception_types(method, CHECK_NULL); + if (exception_types.is_null()) return NULL; + + int modifiers = method->access_flags().as_int() & JVM_RECOGNIZED_METHOD_MODIFIERS; + + Handle ch = java_lang_reflect_Constructor::create(CHECK_NULL); + + java_lang_reflect_Constructor::set_clazz(ch(), holder->java_mirror()); + java_lang_reflect_Constructor::set_slot(ch(), slot); + java_lang_reflect_Constructor::set_parameter_types(ch(), parameter_types()); + java_lang_reflect_Constructor::set_exception_types(ch(), exception_types()); + java_lang_reflect_Constructor::set_modifiers(ch(), modifiers); + java_lang_reflect_Constructor::set_override(ch(), false); + if (java_lang_reflect_Constructor::has_signature_field() && + method->generic_signature() != NULL) { + symbolHandle gs(THREAD, method->generic_signature()); + Handle sig = java_lang_String::create_from_symbol(gs, CHECK_NULL); + java_lang_reflect_Constructor::set_signature(ch(), sig()); + } + if (java_lang_reflect_Constructor::has_annotations_field()) { + java_lang_reflect_Constructor::set_annotations(ch(), method->annotations()); + } + if (java_lang_reflect_Constructor::has_parameter_annotations_field()) { + java_lang_reflect_Constructor::set_parameter_annotations(ch(), method->parameter_annotations()); + } + return ch(); +} + + +oop Reflection::new_field(fieldDescriptor* fd, bool intern_name, TRAPS) { + symbolHandle field_name(THREAD, fd->name()); + Handle name; + if (intern_name) { + // intern_name is only true with UseNewReflection + oop name_oop = StringTable::intern(field_name(), CHECK_NULL); + name = Handle(THREAD, name_oop); + } else { + name = java_lang_String::create_from_symbol(field_name, CHECK_NULL); + } + symbolHandle signature (THREAD, fd->signature()); + KlassHandle holder (THREAD, fd->field_holder()); + Handle type = new_type(signature, holder, CHECK_NULL); + Handle rh = java_lang_reflect_Field::create(CHECK_NULL); + + java_lang_reflect_Field::set_clazz(rh(), Klass::cast(fd->field_holder())->java_mirror()); + java_lang_reflect_Field::set_slot(rh(), fd->index()); + java_lang_reflect_Field::set_name(rh(), name()); + java_lang_reflect_Field::set_type(rh(), type()); + // Note the ACC_ANNOTATION bit, which is a per-class access flag, is never set here. + java_lang_reflect_Field::set_modifiers(rh(), fd->access_flags().as_int() & JVM_RECOGNIZED_FIELD_MODIFIERS); + java_lang_reflect_Field::set_override(rh(), false); + if (java_lang_reflect_Field::has_signature_field() && + fd->generic_signature() != NULL) { + symbolHandle gs(THREAD, fd->generic_signature()); + Handle sig = java_lang_String::create_from_symbol(gs, CHECK_NULL); + java_lang_reflect_Field::set_signature(rh(), sig()); + } + if (java_lang_reflect_Field::has_annotations_field()) { + java_lang_reflect_Field::set_annotations(rh(), fd->annotations()); + } + return rh(); +} + + +//--------------------------------------------------------------------------- +// +// Supporting routines for old native code-based reflection (pre-JDK 1.4). +// +// See reflection.hpp for details. +// +//--------------------------------------------------------------------------- + +#ifdef SUPPORT_OLD_REFLECTION + +methodHandle Reflection::resolve_interface_call(instanceKlassHandle klass, methodHandle method, + KlassHandle recv_klass, Handle receiver, TRAPS) { + assert(!method.is_null() , "method should not be null"); + + CallInfo info; + symbolHandle signature (THREAD, method->signature()); + symbolHandle name (THREAD, method->name()); + LinkResolver::resolve_interface_call(info, receiver, recv_klass, klass, + name, signature, + KlassHandle(), false, true, + CHECK_(methodHandle())); + return info.selected_method(); +} + + +oop Reflection::invoke(instanceKlassHandle klass, methodHandle reflected_method, + Handle receiver, bool override, objArrayHandle ptypes, + BasicType rtype, objArrayHandle args, bool is_method_invoke, TRAPS) { + ResourceMark rm(THREAD); + + methodHandle method; // actual method to invoke + KlassHandle target_klass; // target klass, receiver's klass for non-static + + // Ensure klass is initialized + klass->initialize(CHECK_NULL); + + bool is_static = reflected_method->is_static(); + if (is_static) { + // ignore receiver argument + method = reflected_method; + target_klass = klass; + } else { + // check for null receiver + if (receiver.is_null()) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + // Check class of receiver against class declaring method + if (!receiver->is_a(klass())) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "object is not an instance of declaring class"); + } + // target klass is receiver's klass + target_klass = KlassHandle(THREAD, receiver->klass()); + // no need to resolve if method is private or + if (reflected_method->is_private() || reflected_method->name() == vmSymbols::object_initializer_name()) { + method = reflected_method; + } else { + // resolve based on the receiver + if (instanceKlass::cast(reflected_method->method_holder())->is_interface()) { + // resolve interface call + if (ReflectionWrapResolutionErrors) { + // new default: 6531596 + // Match resolution errors with those thrown due to reflection inlining + // Linktime resolution & IllegalAccessCheck already done by Class.getMethod() + method = resolve_interface_call(klass, reflected_method, target_klass, receiver, THREAD); + if (HAS_PENDING_EXCEPTION) { + // Method resolution threw an exception; wrap it in an InvocationTargetException + oop resolution_exception = PENDING_EXCEPTION; + CLEAR_PENDING_EXCEPTION; + JavaCallArguments args(Handle(THREAD, resolution_exception)); + THROW_ARG_0(vmSymbolHandles::java_lang_reflect_InvocationTargetException(), + vmSymbolHandles::throwable_void_signature(), + &args); + } + } else { + method = resolve_interface_call(klass, reflected_method, target_klass, receiver, CHECK_(NULL)); + } + } else { + // if the method can be overridden, we resolve using the vtable index. + int index = reflected_method->vtable_index(); + method = reflected_method; + if (index != methodOopDesc::nonvirtual_vtable_index) { + // target_klass might be an arrayKlassOop but all vtables start at + // the same place. The cast is to avoid virtual call and assertion. + instanceKlass* inst = (instanceKlass*)target_klass()->klass_part(); + method = methodHandle(THREAD, inst->method_at_vtable(index)); + } + if (!method.is_null()) { + // Check for abstract methods as well + if (method->is_abstract()) { + // new default: 6531596 + if (ReflectionWrapResolutionErrors) { + ResourceMark rm(THREAD); + Handle h_origexception = Exceptions::new_exception(THREAD, + vmSymbols::java_lang_AbstractMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(target_klass()), + method->name(), + method->signature())); + JavaCallArguments args(h_origexception); + THROW_ARG_0(vmSymbolHandles::java_lang_reflect_InvocationTargetException(), + vmSymbolHandles::throwable_void_signature(), + &args); + } else { + ResourceMark rm(THREAD); + THROW_MSG_0(vmSymbols::java_lang_AbstractMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(target_klass()), + method->name(), + method->signature())); + } + } + } + } + } + } + + // I believe this is a ShouldNotGetHere case which requires + // an internal vtable bug. If you ever get this please let Karen know. + if (method.is_null()) { + ResourceMark rm(THREAD); + THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), + methodOopDesc::name_and_sig_as_C_string(Klass::cast(klass()), + reflected_method->name(), + reflected_method->signature())); + } + + // In the JDK 1.4 reflection implementation, the security check is + // done at the Java level + if (!(JDK_Version::is_gte_jdk14x_version() && UseNewReflection)) { + + // Access checking (unless overridden by Method) + if (!override) { + if (!(klass->is_public() && reflected_method->is_public())) { + bool access = Reflection::reflect_check_access(klass(), reflected_method->access_flags(), target_klass(), is_method_invoke, CHECK_NULL); + if (!access) { + return NULL; // exception + } + } + } + + } // !(Universe::is_gte_jdk14x_version() && UseNewReflection) + + assert(ptypes->is_objArray(), "just checking"); + int args_len = args.is_null() ? 0 : args->length(); + // Check number of arguments + if (ptypes->length() != args_len) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "wrong number of arguments"); + } + + // Create object to contain parameters for the JavaCall + JavaCallArguments java_args(method->size_of_parameters()); + + if (!is_static) { + java_args.push_oop(receiver); + } + + for (int i = 0; i < args_len; i++) { + oop type_mirror = ptypes->obj_at(i); + oop arg = args->obj_at(i); + if (java_lang_Class::is_primitive(type_mirror)) { + jvalue value; + BasicType ptype = basic_type_mirror_to_basic_type(type_mirror, CHECK_NULL); + BasicType atype = unbox_for_primitive(arg, &value, CHECK_NULL); + if (ptype != atype) { + widen(&value, atype, ptype, CHECK_NULL); + } + switch (ptype) { + case T_BOOLEAN: java_args.push_int(value.z); break; + case T_CHAR: java_args.push_int(value.c); break; + case T_BYTE: java_args.push_int(value.b); break; + case T_SHORT: java_args.push_int(value.s); break; + case T_INT: java_args.push_int(value.i); break; + case T_LONG: java_args.push_long(value.j); break; + case T_FLOAT: java_args.push_float(value.f); break; + case T_DOUBLE: java_args.push_double(value.d); break; + default: + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "argument type mismatch"); + } + } else { + if (arg != NULL) { + klassOop k = java_lang_Class::as_klassOop(type_mirror); + if (!arg->is_a(k)) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "argument type mismatch"); + } + } + Handle arg_handle(THREAD, arg); // Create handle for argument + java_args.push_oop(arg_handle); // Push handle + } + } + + assert(java_args.size_of_parameters() == method->size_of_parameters(), "just checking"); + + // All oops (including receiver) is passed in as Handles. An potential oop is returned as an + // oop (i.e., NOT as an handle) + JavaValue result(rtype); + JavaCalls::call(&result, method, &java_args, THREAD); + + if (HAS_PENDING_EXCEPTION) { + // Method threw an exception; wrap it in an InvocationTargetException + oop target_exception = PENDING_EXCEPTION; + CLEAR_PENDING_EXCEPTION; + JavaCallArguments args(Handle(THREAD, target_exception)); + THROW_ARG_0(vmSymbolHandles::java_lang_reflect_InvocationTargetException(), + vmSymbolHandles::throwable_void_signature(), + &args); + } else { + if (rtype == T_BOOLEAN || rtype == T_BYTE || rtype == T_CHAR || rtype == T_SHORT) + narrow((jvalue*) result.get_value_addr(), rtype, CHECK_NULL); + return box((jvalue*) result.get_value_addr(), rtype, CHECK_NULL); + } +} + + +void Reflection::narrow(jvalue* value, BasicType narrow_type, TRAPS) { + switch (narrow_type) { + case T_BOOLEAN: + value->z = (jboolean) value->i; + return; + case T_BYTE: + value->b = (jbyte) value->i; + return; + case T_CHAR: + value->c = (jchar) value->i; + return; + case T_SHORT: + value->s = (jshort) value->i; + return; + default: + break; // fail + } + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "argument type mismatch"); +} + + +BasicType Reflection::basic_type_mirror_to_basic_type(oop basic_type_mirror, TRAPS) { + assert(java_lang_Class::is_primitive(basic_type_mirror), "just checking"); + return java_lang_Class::primitive_type(basic_type_mirror); +} + + +bool Reflection::match_parameter_types(methodHandle method, objArrayHandle types, int parameter_count, TRAPS) { + int types_len = types.is_null() ? 0 : types->length(); + if (types_len != parameter_count) return false; + if (parameter_count > 0) { + objArrayHandle method_types = get_parameter_types(method, parameter_count, NULL, CHECK_false); + for (int index = 0; index < parameter_count; index++) { + if (types->obj_at(index) != method_types->obj_at(index)) { + return false; + } + } + } + return true; +} + + +oop Reflection::new_field(FieldStream* st, TRAPS) { + symbolHandle field_name(THREAD, st->name()); + Handle name = java_lang_String::create_from_symbol(field_name, CHECK_NULL); + symbolHandle signature(THREAD, st->signature()); + Handle type = new_type(signature, st->klass(), CHECK_NULL); + Handle rh = java_lang_reflect_Field::create(CHECK_NULL); + oop result = rh(); + + java_lang_reflect_Field::set_clazz(result, st->klass()->java_mirror()); + java_lang_reflect_Field::set_slot(result, st->index()); + java_lang_reflect_Field::set_name(result, name()); + java_lang_reflect_Field::set_type(result, type()); + // Note the ACC_ANNOTATION bit, which is a per-class access flag, is never set here. + java_lang_reflect_Field::set_modifiers(result, st->access_flags().as_int() & JVM_RECOGNIZED_FIELD_MODIFIERS); + java_lang_reflect_Field::set_override(result, false); + return result; +} + + +bool Reflection::resolve_field(Handle field_mirror, Handle& receiver, fieldDescriptor* fd, bool check_final, TRAPS) { + if (field_mirror.is_null()) { + THROW_(vmSymbols::java_lang_NullPointerException(), false); + } + + instanceKlassHandle klass (THREAD, java_lang_Class::as_klassOop(java_lang_reflect_Field::clazz(field_mirror()))); + int slot = java_lang_reflect_Field::slot(field_mirror()); + + // Ensure klass is initialized + klass->initialize(CHECK_false); + fd->initialize(klass(), slot); + + bool is_static = fd->is_static(); + KlassHandle receiver_klass; + + if (is_static) { + receiver = KlassHandle(THREAD, klass()); + receiver_klass = klass; + } else { + // Check object is a non-null instance of declaring class + if (receiver.is_null()) { + THROW_(vmSymbols::java_lang_NullPointerException(), false); + } + if (!receiver->is_a(klass())) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), "object is not an instance of declaring class", false); + } + receiver_klass = KlassHandle(THREAD, receiver->klass()); + } + + // Access checking (unless overridden by Field) + if (!java_lang_reflect_Field::override(field_mirror())) { + if (!(klass->is_public() && fd->is_public())) { + bool access_check = reflect_check_access(klass(), fd->access_flags(), receiver_klass(), false, CHECK_false); + if (!access_check) { + return false; // exception + } + } + } + + if (check_final && fd->is_final()) { + // In 1.3 we always throw an error when attempting to set a final field. + // In 1.2.x, this was allowed in the override bit was set by calling Field.setAccessible(true). + // We currently maintain backwards compatibility. See bug 4250960. + bool strict_final_check = !JDK_Version::is_jdk12x_version(); + if (strict_final_check || !java_lang_reflect_Field::override(field_mirror())) { + THROW_MSG_(vmSymbols::java_lang_IllegalAccessException(), "field is final", false); + } + } + return true; +} + + +BasicType Reflection::field_get(jvalue* value, fieldDescriptor* fd, Handle receiver) { + BasicType field_type = fd->field_type(); + int offset = fd->offset(); + switch (field_type) { + case T_BOOLEAN: + value->z = receiver->bool_field(offset); + break; + case T_CHAR: + value->c = receiver->char_field(offset); + break; + case T_FLOAT: + value->f = receiver->float_field(offset); + break; + case T_DOUBLE: + value->d = receiver->double_field(offset); + break; + case T_BYTE: + value->b = receiver->byte_field(offset); + break; + case T_SHORT: + value->s = receiver->short_field(offset); + break; + case T_INT: + value->i = receiver->int_field(offset); + break; + case T_LONG: + value->j = receiver->long_field(offset); + break; + case T_OBJECT: + case T_ARRAY: + value->l = (jobject) receiver->obj_field(offset); + break; + default: + return T_ILLEGAL; + } + return field_type; +} + + +void Reflection::field_set(jvalue* value, fieldDescriptor* fd, Handle receiver, BasicType value_type, TRAPS) { + BasicType field_type = fd->field_type(); + if (field_type != value_type) { + widen(value, value_type, field_type, CHECK); + } + + int offset = fd->offset(); + switch (field_type) { + case T_BOOLEAN: + receiver->bool_field_put(offset, value->z); + break; + case T_CHAR: + receiver->char_field_put(offset, value->c); + break; + case T_FLOAT: + receiver->float_field_put(offset, value->f); + break; + case T_DOUBLE: + receiver->double_field_put(offset, value->d); + break; + case T_BYTE: + receiver->byte_field_put(offset, value->b); + break; + case T_SHORT: + receiver->short_field_put(offset, value->s); + break; + case T_INT: + receiver->int_field_put(offset, value->i); + break; + case T_LONG: + receiver->long_field_put(offset, value->j); + break; + case T_OBJECT: + case T_ARRAY: { + Handle obj(THREAD, (oop) value->l); + if (obj.not_null()) { + symbolHandle signature(THREAD, fd->signature()); + Handle loader (THREAD, fd->loader()); + Handle protect (THREAD, Klass::cast(fd->field_holder())->protection_domain()); + klassOop k = SystemDictionary::resolve_or_fail(signature, loader, protect, true, CHECK); // may block + if (!obj->is_a(k)) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "field type mismatch"); + } + } + receiver->obj_field_put(offset, obj()); + break; + } + default: + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "field type mismatch"); + } +} + + +oop Reflection::reflect_field(oop mirror, symbolOop field_name, jint which, TRAPS) { + // Exclude primitive types and array types + if (java_lang_Class::is_primitive(mirror)) return NULL; + if (Klass::cast(java_lang_Class::as_klassOop(mirror))->oop_is_array()) return NULL; + + instanceKlassHandle k(THREAD, java_lang_Class::as_klassOop(mirror)); + bool local_fields_only = (which == DECLARED); + + // Ensure class is linked + k->link_class(CHECK_NULL); + + // Search class and interface fields + for (FieldStream st(k, local_fields_only, false); !st.eos(); st.next()) { + if (st.name() == field_name) { + if (local_fields_only || st.access_flags().is_public()) { + return new_field(&st, THREAD); + } + } + } + + return NULL; +} + + +objArrayOop Reflection::reflect_fields(oop mirror, jint which, TRAPS) { + // Exclude primitive types and array types + if (java_lang_Class::is_primitive(mirror) + || Klass::cast(java_lang_Class::as_klassOop(mirror))->oop_is_array()) { + symbolHandle name = vmSymbolHandles::java_lang_reflect_Field(); + klassOop klass = SystemDictionary::resolve_or_fail(name, true, CHECK_NULL); + return oopFactory::new_objArray(klass, 0, CHECK_NULL); // Return empty array + } + + instanceKlassHandle k(THREAD, java_lang_Class::as_klassOop(mirror)); + + // Ensure class is linked + k->link_class(CHECK_NULL); + + bool local_fields_only = (which == DECLARED); + int count = 0; + { // Compute fields count for class and interface fields + for (FieldStream st(k, local_fields_only, false); !st.eos(); st.next()) { + if (local_fields_only || st.access_flags().is_public()) { + count++; + } + } + } + + // Allocate result + symbolHandle name = vmSymbolHandles::java_lang_reflect_Field(); + klassOop klass = SystemDictionary::resolve_or_fail(name, true, CHECK_NULL); + objArrayOop r = oopFactory::new_objArray(klass, count, CHECK_NULL); + objArrayHandle result (THREAD, r); + + // Fill in results backwards + { + for (FieldStream st(k, local_fields_only, false); !st.eos(); st.next()) { + if (local_fields_only || st.access_flags().is_public()) { + oop field = new_field(&st, CHECK_NULL); + result->obj_at_put(--count, field); + } + } + assert(count == 0, "just checking"); + } + return result(); +} + + +oop Reflection::reflect_method(oop mirror, symbolHandle method_name, objArrayHandle types, jint which, TRAPS) { + if (java_lang_Class::is_primitive(mirror)) return NULL; + klassOop klass = java_lang_Class::as_klassOop(mirror); + if (Klass::cast(klass)->oop_is_array() && which == MEMBER_DECLARED) return NULL; + + if (Klass::cast(java_lang_Class::as_klassOop(mirror))->oop_is_array()) { + klass = SystemDictionary::object_klass(); + } + instanceKlassHandle h_k(THREAD, klass); + + // Ensure klass is linked (need not be initialized) + h_k->link_class(CHECK_NULL); + + // For interfaces include static initializers under jdk1.2.x (since classic does that) + bool include_clinit = JDK_Version::is_jdk12x_version() && h_k->is_interface(); + + switch (which) { + case MEMBER_PUBLIC: + // First the public non-static methods (works if method holder is an interface) + // Note that we can ignore checks for overridden methods, since we go up the hierarchy. + { + for (MethodStream st(h_k, false, false); !st.eos(); st.next()) { + methodHandle m(THREAD, st.method()); + // For interfaces include static initializers since classic does that! + if (method_name() == m->name() && (include_clinit || (m->is_public() && !m->is_static() && !m->is_initializer()))) { + symbolHandle signature(THREAD, m->signature()); + bool parameter_match = match_parameter_types(m, types, ArgumentCount(signature).size(), CHECK_NULL); + if (parameter_match) { + return new_method(m, false, false, THREAD); + } + } + } + } + // Then the public static methods (works if method holder is an interface) + { + for (MethodStream st(h_k, false, false); !st.eos(); st.next()) { + methodHandle m(THREAD, st.method()); + if (method_name() == m->name() && m->is_public() && m->is_static() && !m->is_initializer()) { + symbolHandle signature(THREAD, m->signature()); + bool parameter_match = match_parameter_types(m, types, ArgumentCount(signature).size(), CHECK_NULL); + if (parameter_match) { + return new_method(m, false, false, THREAD); + } + } + } + } + break; + case MEMBER_DECLARED: + // All local methods + { + for (MethodStream st(h_k, true, true); !st.eos(); st.next()) { + methodHandle m(THREAD, st.method()); + if (method_name() == m->name() && !m->is_initializer()) { + symbolHandle signature(THREAD, m->signature()); + bool parameter_match = match_parameter_types(m, types, ArgumentCount(signature).size(), CHECK_NULL); + if (parameter_match) { + return new_method(m, false, false, THREAD); + } + } + } + } + break; + default: + break; + } + return NULL; +} + + +objArrayOop Reflection::reflect_methods(oop mirror, jint which, TRAPS) { + // Exclude primitive types + if (java_lang_Class::is_primitive(mirror) || + (Klass::cast(java_lang_Class::as_klassOop(mirror))->oop_is_array() && (which == MEMBER_DECLARED))) { + klassOop klass = SystemDictionary::reflect_method_klass(); + return oopFactory::new_objArray(klass, 0, CHECK_NULL); // Return empty array + } + + klassOop klass = java_lang_Class::as_klassOop(mirror); + if (Klass::cast(java_lang_Class::as_klassOop(mirror))->oop_is_array()) { + klass = SystemDictionary::object_klass(); + } + instanceKlassHandle h_k(THREAD, klass); + + // Ensure klass is linked (need not be initialized) + h_k->link_class(CHECK_NULL); + + // We search the (super)interfaces only if h_k is an interface itself + bool is_interface = h_k->is_interface(); + + // For interfaces include static initializers under jdk1.2.x (since classic does that) + bool include_clinit = JDK_Version::is_jdk12x_version() && is_interface; + + switch (which) { + case MEMBER_PUBLIC: + { + + // Count public methods (non-static and static) + int count = 0; + { + for (MethodStream st(h_k, false, false); !st.eos(); st.next()) { + methodOop m = st.method(); + // For interfaces include static initializers since classic does that! + if (include_clinit || (!m->is_initializer() && m->is_public() && !m->is_overridden_in(h_k()))) { + count++; + } + } + } + + // Allocate result + klassOop klass = SystemDictionary::reflect_method_klass(); + objArrayOop r = oopFactory::new_objArray(klass, count, CHECK_NULL); + objArrayHandle h_result (THREAD, r); + + // Fill in results backwards + { + // First the non-static public methods + for (MethodStream st(h_k, false, false); !st.eos(); st.next()) { + methodHandle m (THREAD, st.method()); + if (!m->is_static() && !m->is_initializer() && m->is_public() && !m->is_overridden_in(h_k())) { + oop method = new_method(m, false, false, CHECK_NULL); + if (method == NULL) { + return NULL; + } else { + h_result->obj_at_put(--count, method); + } + } + } + } + { + // Then the static public methods + for (MethodStream st(h_k, false, !is_interface); !st.eos(); st.next()) { + methodHandle m (THREAD, st.method()); + if (m->is_static() && (include_clinit || (!m->is_initializer()) && m->is_public() && !m->is_overridden_in(h_k()))) { + oop method = new_method(m, false, false, CHECK_NULL); + if (method == NULL) { + return NULL; + } else { + h_result->obj_at_put(--count, method); + } + } + } + } + + assert(count == 0, "just checking"); + return h_result(); + } + + case MEMBER_DECLARED: + { + // Count all methods + int count = 0; + { + for (MethodStream st(h_k, true, !is_interface); !st.eos(); st.next()) { + methodOop m = st.method(); + if (!m->is_initializer()) { + count++; + } + } + } + // Allocate result + klassOop klass = SystemDictionary::reflect_method_klass(); + objArrayOop r = oopFactory::new_objArray(klass, count, CHECK_NULL); + objArrayHandle h_result (THREAD, r); + + // Fill in results backwards + { + for (MethodStream st(h_k, true, true); !st.eos(); st.next()) { + methodHandle m (THREAD, st.method()); + if (!m->is_initializer()) { + oop method = new_method(m, false, false, CHECK_NULL); + if (method == NULL) { + return NULL; + } else { + h_result->obj_at_put(--count, method); + } + } + } + } + assert(count == 0, "just checking"); + return h_result(); + } + } + ShouldNotReachHere(); + return NULL; +} + + +oop Reflection::reflect_constructor(oop mirror, objArrayHandle types, jint which, TRAPS) { + + // Exclude primitive, interface and array types + bool prim = java_lang_Class::is_primitive(mirror); + Klass* klass = prim ? NULL : Klass::cast(java_lang_Class::as_klassOop(mirror)); + if (prim || klass->is_interface() || klass->oop_is_array()) return NULL; + + // Must be instance klass + instanceKlassHandle h_k(THREAD, java_lang_Class::as_klassOop(mirror)); + + // Ensure klass is linked (need not be initialized) + h_k->link_class(CHECK_NULL); + + bool local_only = (which == MEMBER_DECLARED); + for (MethodStream st(h_k, true, true); !st.eos(); st.next()) { + methodHandle m(THREAD, st.method()); + if (m->name() == vmSymbols::object_initializer_name() && (local_only || m->is_public())) { + symbolHandle signature(THREAD, m->signature()); + bool parameter_match = match_parameter_types(m, types, ArgumentCount(signature).size(), CHECK_NULL); + if (parameter_match) { + return new_constructor(m, THREAD); + } + } + } + + return NULL; +} + + +objArrayOop Reflection::reflect_constructors(oop mirror, jint which, TRAPS) { + // Exclude primitive, interface and array types + bool prim = java_lang_Class::is_primitive(mirror); + Klass* k = prim ? NULL : Klass::cast(java_lang_Class::as_klassOop(mirror)); + if (prim || k->is_interface() || k->oop_is_array()) { + return oopFactory::new_objArray(SystemDictionary::reflect_constructor_klass(), 0, CHECK_NULL); // Return empty array + } + + // Must be instanceKlass at this point + instanceKlassHandle h_k(THREAD, java_lang_Class::as_klassOop(mirror)); + + // Ensure klass is linked (need not be initialized) + h_k->link_class(CHECK_NULL); + + bool local_only = (which == MEMBER_DECLARED); + int count = 0; + { + for (MethodStream st(h_k, true, true); !st.eos(); st.next()) { + methodOop m = st.method(); + if (m->name() == vmSymbols::object_initializer_name() && (local_only || m->is_public())) { + count++; + } + } + } + + // Allocate result + symbolHandle name = vmSymbolHandles::java_lang_reflect_Constructor(); + klassOop klass = SystemDictionary::resolve_or_fail(name, true, CHECK_NULL); + objArrayOop r = oopFactory::new_objArray(klass, count, CHECK_NULL); + objArrayHandle h_result (THREAD, r); + + // Fill in results backwards + { + for (MethodStream st(h_k, true, true); !st.eos(); st.next()) { + methodHandle m (THREAD, st.method()); + if (m->name() == vmSymbols::object_initializer_name() && (local_only || m->is_public())) { + oop constr = new_constructor(m, CHECK_NULL); + if (constr == NULL) { + return NULL; + } else { + h_result->obj_at_put(--count, constr); + } + } + } + assert(count == 0, "just checking"); + } + return h_result(); +} + + +// This would be nicer if, say, java.lang.reflect.Method was a subclass +// of java.lang.reflect.Constructor + +oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) { + oop mirror = java_lang_reflect_Method::clazz(method_mirror); + int slot = java_lang_reflect_Method::slot(method_mirror); + bool override = java_lang_reflect_Method::override(method_mirror) != 0; + objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror))); + + oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror); + BasicType rtype; + if (java_lang_Class::is_primitive(return_type_mirror)) { + rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL); + } else { + rtype = T_OBJECT; + } + + instanceKlassHandle klass(THREAD, java_lang_Class::as_klassOop(mirror)); + if (!klass->methods()->is_within_bounds(slot)) { + THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke"); + } + methodHandle method(THREAD, methodOop(klass->methods()->obj_at(slot))); + + return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD); +} + + +oop Reflection::invoke_constructor(oop constructor_mirror, objArrayHandle args, TRAPS) { + oop mirror = java_lang_reflect_Constructor::clazz(constructor_mirror); + int slot = java_lang_reflect_Constructor::slot(constructor_mirror); + bool override = java_lang_reflect_Constructor::override(constructor_mirror) != 0; + objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Constructor::parameter_types(constructor_mirror))); + + instanceKlassHandle klass(THREAD, java_lang_Class::as_klassOop(mirror)); + if (!klass->methods()->is_within_bounds(slot)) { + THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke"); + } + methodHandle method(THREAD, methodOop(klass->methods()->obj_at(slot))); + assert(method->name() == vmSymbols::object_initializer_name(), "invalid constructor"); + + // Make sure klass gets initialize + klass->initialize(CHECK_NULL); + + // Create new instance (the receiver) + klass->check_valid_for_instantiation(false, CHECK_NULL); + Handle receiver = klass->allocate_instance_handle(CHECK_NULL); + + // Ignore result from call and return receiver + invoke(klass, method, receiver, override, ptypes, T_VOID, args, false, CHECK_NULL); + return receiver(); +} + + +#endif /* SUPPORT_OLD_REFLECTION */ diff --git a/hotspot/src/share/vm/runtime/reflection.hpp b/hotspot/src/share/vm/runtime/reflection.hpp new file mode 100644 index 00000000000..4e8054af5cd --- /dev/null +++ b/hotspot/src/share/vm/runtime/reflection.hpp @@ -0,0 +1,162 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Class Reflection contains utility methods needed for implementing the +// reflection api. +// +// Used by functions in the JVM interface. +// +// NOTE that in JDK 1.4 most of reflection is now implemented in Java +// using dynamic bytecode generation. The Array class has not yet been +// rewritten using bytecodes; if it were, most of the rest of this +// class could go away, as well as a few more entry points in jvm.cpp. + +class FieldStream; + +class Reflection: public AllStatic { + private: + // Access checking + static bool reflect_check_access(klassOop field_class, AccessFlags acc, klassOop target_class, bool is_method_invoke, TRAPS); + + // Conversion + static klassOop basic_type_mirror_to_arrayklass(oop basic_type_mirror, TRAPS); + static oop basic_type_arrayklass_to_mirror(klassOop basic_type_arrayklass, TRAPS); + + static objArrayHandle get_parameter_types(methodHandle method, int parameter_count, oop* return_type, TRAPS); + static objArrayHandle get_exception_types(methodHandle method, TRAPS); + // Creating new java.lang.reflect.xxx wrappers + static Handle new_type(symbolHandle signature, KlassHandle k, TRAPS); + + public: + // Constants defined by java reflection api classes + enum SomeConstants { + PUBLIC = 0, + DECLARED = 1, + MEMBER_PUBLIC = 0, + MEMBER_DECLARED = 1, + MAX_DIM = 255 + }; + + // Boxing. Returns boxed value of appropriate type. Throws IllegalArgumentException. + static oop box(jvalue* v, BasicType type, TRAPS); + // Unboxing. Returns type code and sets value. + static BasicType unbox_for_primitive(oop boxed_value, jvalue* value, TRAPS); + static BasicType unbox_for_regular_object(oop boxed_value, jvalue* value); + + // Widening of basic types. Throws IllegalArgumentException. + static void widen(jvalue* value, BasicType current_type, BasicType wide_type, TRAPS); + + // Reflective array access. Returns type code. Throws ArrayIndexOutOfBoundsException. + static BasicType array_get(jvalue* value, arrayOop a, int index, TRAPS); + static void array_set(jvalue* value, arrayOop a, int index, BasicType value_type, TRAPS); + // Returns mirror on array element type (NULL for basic type arrays and non-arrays). + static oop array_component_type(oop mirror, TRAPS); + + // Object creation + static arrayOop reflect_new_array(oop element_mirror, jint length, TRAPS); + static arrayOop reflect_new_multi_array(oop element_mirror, typeArrayOop dimensions, TRAPS); + + // Verification + static bool verify_class_access(klassOop current_class, klassOop new_class, bool classloader_only); + + static bool verify_field_access(klassOop current_class, + klassOop resolved_class, + klassOop field_class, + AccessFlags access, + bool classloader_only, + bool protected_restriction = false); + static bool is_same_class_package(klassOop class1, klassOop class2); + + static bool can_relax_access_check_for( + klassOop accessor, klassOop accesee, bool classloader_only); + + // inner class reflection + static void check_for_inner_class(instanceKlassHandle outer, instanceKlassHandle inner, TRAPS); + + // + // Support for reflection based on dynamic bytecode generation (JDK 1.4) + // + + // Create a java.lang.reflect.Method object based on a method + static oop new_method(methodHandle method, bool intern_name, bool for_constant_pool_access, TRAPS); + // Create a java.lang.reflect.Constructor object based on a method + static oop new_constructor(methodHandle method, TRAPS); + // Create a java.lang.reflect.Field object based on a field descriptor + static oop new_field(fieldDescriptor* fd, bool intern_name, TRAPS); + + //--------------------------------------------------------------------------- + // + // Support for old native code-based reflection (pre-JDK 1.4) + // + // NOTE: the method and constructor invocation code is still used + // for startup time reasons; see reflectionCompat.hpp. + // + //--------------------------------------------------------------------------- + +#ifdef SUPPORT_OLD_REFLECTION +private: + // method resolution for invoke + static methodHandle resolve_interface_call(instanceKlassHandle klass, methodHandle method, KlassHandle recv_klass, Handle receiver, TRAPS); + // Method call (shared by invoke_method and invoke_constructor) + static oop invoke(instanceKlassHandle klass, methodHandle method, Handle receiver, bool override, objArrayHandle ptypes, BasicType rtype, objArrayHandle args, bool is_method_invoke, TRAPS); + + // Narrowing of basic types. Used to create correct jvalues for + // boolean, byte, char and short return return values from interpreter + // which are returned as ints. Throws IllegalArgumentException. + static void narrow(jvalue* value, BasicType narrow_type, TRAPS); + + // Conversion + static BasicType basic_type_mirror_to_basic_type(oop basic_type_mirror, TRAPS); + + static bool match_parameter_types(methodHandle method, objArrayHandle types, int parameter_count, TRAPS); + // Creating new java.lang.reflect.xxx wrappers + static oop new_field(FieldStream* st, TRAPS); + +public: + // Field lookup and verification. + static bool resolve_field(Handle field_mirror, Handle& receiver, fieldDescriptor* fd, bool check_final, TRAPS); + + // Reflective field access. Returns type code. Throws IllegalArgumentException. + static BasicType field_get(jvalue* value, fieldDescriptor* fd, Handle receiver); + static void field_set(jvalue* value, fieldDescriptor* fd, Handle receiver, BasicType value_type, TRAPS); + + // Reflective lookup of fields. Returns java.lang.reflect.Field instances. + static oop reflect_field(oop mirror, symbolOop field_name, jint which, TRAPS); + static objArrayOop reflect_fields(oop mirror, jint which, TRAPS); + + // Reflective lookup of methods. Returns java.lang.reflect.Method instances. + static oop reflect_method(oop mirror, symbolHandle method_name, objArrayHandle types, jint which, TRAPS); + static objArrayOop reflect_methods(oop mirror, jint which, TRAPS); + + // Reflective lookup of constructors. Returns java.lang.reflect.Constructor instances. + static oop reflect_constructor(oop mirror, objArrayHandle types, jint which, TRAPS); + static objArrayOop reflect_constructors(oop mirror, jint which, TRAPS); + + // Method invokation through java.lang.reflect.Method + static oop invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS); + // Method invokation through java.lang.reflect.Constructor + static oop invoke_constructor(oop method_mirror, objArrayHandle args, TRAPS); +#endif /* SUPPORT_OLD_REFLECTION */ + +}; diff --git a/hotspot/src/share/vm/runtime/reflectionCompat.hpp b/hotspot/src/share/vm/runtime/reflectionCompat.hpp new file mode 100644 index 00000000000..fba01e4876b --- /dev/null +++ b/hotspot/src/share/vm/runtime/reflectionCompat.hpp @@ -0,0 +1,42 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// During the development of the JDK 1.4 reflection implementation +// based on dynamic bytecode generation, it was hoped that the bulk of +// the native code for reflection could be removed. Unfortunately +// there is currently a significant cost associated with loading the +// stub classes which impacts startup time. Until this cost can be +// reduced, the JVM entry points JVM_InvokeMethod and +// JVM_NewInstanceFromConstructor are still needed; these and their +// dependents currently constitute the bulk of the native code for +// reflection. If this cost is reduced in the future, the +// NativeMethodAccessorImpl and NativeConstructorAccessorImpl classes +// can be removed from sun.reflect and all of the code guarded by this +// flag removed from the product build. (Non-product builds, +// specifically the "optimized" target, would retain the code so they +// could be dropped into earlier JDKs for comparative benchmarking.) + +//#ifndef PRODUCT +# define SUPPORT_OLD_REFLECTION +//#endif diff --git a/hotspot/src/share/vm/runtime/reflectionUtils.cpp b/hotspot/src/share/vm/runtime/reflectionUtils.cpp new file mode 100644 index 00000000000..7ea22e1122c --- /dev/null +++ b/hotspot/src/share/vm/runtime/reflectionUtils.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_reflectionUtils.cpp.incl" + +KlassStream::KlassStream(instanceKlassHandle klass, bool local_only, bool classes_only) { + _klass = klass; + if (classes_only) { + _interfaces = Universe::the_empty_system_obj_array(); + } else { + _interfaces = klass->transitive_interfaces(); + } + _interface_index = _interfaces->length(); + _local_only = local_only; + _classes_only = classes_only; +} + +bool KlassStream::eos() { + if (index() >= 0) return false; + if (_local_only) return true; + if (!_klass->is_interface() && _klass->super() != NULL) { + // go up superclass chain (not for interfaces) + _klass = _klass->super(); + } else { + if (_interface_index > 0) { + _klass = klassOop(_interfaces->obj_at(--_interface_index)); + } else { + return true; + } + } + _index = length(); + next(); + return eos(); +} + + +GrowableArray *FilteredFieldsMap::_filtered_fields = + new (ResourceObj::C_HEAP) GrowableArray(3,true); + + +void FilteredFieldsMap::initialize() { + int offset; + offset = java_lang_Throwable::get_backtrace_offset(); + _filtered_fields->append(new FilteredField(SystemDictionary::throwable_klass(), offset)); + // The latest version of vm may be used with old jdk. + if (JDK_Version::is_gte_jdk16x_version()) { + // The following class fields do not exist in + // previous version of jdk. + offset = sun_reflect_ConstantPool::cp_oop_offset(); + _filtered_fields->append(new FilteredField(SystemDictionary::reflect_constant_pool_klass(), offset)); + offset = sun_reflect_UnsafeStaticFieldAccessorImpl::base_offset(); + _filtered_fields->append(new FilteredField(SystemDictionary::reflect_unsafe_static_field_accessor_impl_klass(), offset)); + } +} + +int FilteredFieldStream::field_count() { + int numflds = 0; + for (;!eos(); next()) { + numflds++; + } + return numflds; +} diff --git a/hotspot/src/share/vm/runtime/reflectionUtils.hpp b/hotspot/src/share/vm/runtime/reflectionUtils.hpp new file mode 100644 index 00000000000..b65c21522de --- /dev/null +++ b/hotspot/src/share/vm/runtime/reflectionUtils.hpp @@ -0,0 +1,211 @@ +/* + * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A KlassStream is an abstract stream for streaming over self, superclasses +// and (super)interfaces. Streaming is done in reverse order (subclasses first, +// interfaces last). +// +// for (KlassStream st(k, false, false); !st.eos(); st.next()) { +// klassOop k = st.klass(); +// ... +// } + +class KlassStream VALUE_OBJ_CLASS_SPEC { + protected: + instanceKlassHandle _klass; // current klass/interface iterated over + objArrayHandle _interfaces; // transitive interfaces for initial class + int _interface_index; // current interface being processed + bool _local_only; // process initial class/interface only + bool _classes_only; // process classes only (no interfaces) + int _index; + + virtual int length() const = 0; + + public: + // constructor + KlassStream(instanceKlassHandle klass, bool local_only, bool classes_only); + + // testing + bool eos(); + + // iterating + virtual void next() = 0; + + // accessors + instanceKlassHandle klass() const { return _klass; } + int index() const { return _index; } +}; + + +// A MethodStream streams over all methods in a class, superclasses and (super)interfaces. +// Streaming is done in reverse order (subclasses first, methods in reverse order) +// Usage: +// +// for (MethodStream st(k, false, false); !st.eos(); st.next()) { +// methodOop m = st.method(); +// ... +// } + +class MethodStream : public KlassStream { + private: + int length() const { return methods()->length(); } + objArrayOop methods() const { return _klass->methods(); } + public: + MethodStream(instanceKlassHandle klass, bool local_only, bool classes_only) + : KlassStream(klass, local_only, classes_only) { + _index = length(); + next(); + } + + void next() { _index--; } + methodOop method() const { return methodOop(methods()->obj_at(index())); } +}; + + +// A FieldStream streams over all fields in a class, superclasses and (super)interfaces. +// Streaming is done in reverse order (subclasses first, fields in reverse order) +// Usage: +// +// for (FieldStream st(k, false, false); !st.eos(); st.next()) { +// symbolOop field_name = st.name(); +// ... +// } + + +class FieldStream : public KlassStream { + private: + int length() const { return fields()->length(); } + constantPoolOop constants() const { return _klass->constants(); } + protected: + typeArrayOop fields() const { return _klass->fields(); } + public: + FieldStream(instanceKlassHandle klass, bool local_only, bool classes_only) + : KlassStream(klass, local_only, classes_only) { + _index = length(); + next(); + } + + void next() { _index -= instanceKlass::next_offset; } + + // Accessors for current field + AccessFlags access_flags() const { + AccessFlags flags; + flags.set_flags(fields()->ushort_at(index() + instanceKlass::access_flags_offset)); + return flags; + } + symbolOop name() const { + int name_index = fields()->ushort_at(index() + instanceKlass::name_index_offset); + return constants()->symbol_at(name_index); + } + symbolOop signature() const { + int signature_index = fields()->ushort_at(index() + + instanceKlass::signature_index_offset); + return constants()->symbol_at(signature_index); + } + // missing: initval() + int offset() const { + return _klass->offset_from_fields( index() ); + } +}; + +class FilteredField { + private: + klassOop _klass; + int _field_offset; + + public: + FilteredField(klassOop klass, int field_offset) { + _klass = klass; + _field_offset = field_offset; + } + klassOop klass() { return _klass; } + oop* klass_addr() { return (oop*) &_klass; } + int field_offset() { return _field_offset; } +}; + +class FilteredFieldsMap : AllStatic { + private: + static GrowableArray *_filtered_fields; + public: + static void initialize(); + static bool is_filtered_field(klassOop klass, int field_offset) { + for (int i=0; i < _filtered_fields->length(); i++) { + if (klass == _filtered_fields->at(i)->klass() && + field_offset == _filtered_fields->at(i)->field_offset()) { + return true; + } + } + return false; + } + static int filtered_fields_count(klassOop klass, bool local_only) { + int nflds = 0; + for (int i=0; i < _filtered_fields->length(); i++) { + if (local_only && klass == _filtered_fields->at(i)->klass()) { + nflds++; + } else if (klass->klass_part()->is_subtype_of(_filtered_fields->at(i)->klass())) { + nflds++; + } + } + return nflds; + } + // GC support. + static void klasses_oops_do(OopClosure* f) { + for (int i = 0; i < _filtered_fields->length(); i++) { + f->do_oop((oop*)_filtered_fields->at(i)->klass_addr()); + } + } +}; + + +// A FilteredFieldStream streams over all fields in a class, superclasses and +// (super)interfaces. Streaming is done in reverse order (subclasses first, +// fields in reverse order) +// +// Usage: +// +// for (FilteredFieldStream st(k, false, false); !st.eos(); st.next()) { +// symbolOop field_name = st.name(); +// ... +// } + +class FilteredFieldStream : public FieldStream { + private: + int _filtered_fields_count; + bool has_filtered_field() { return (_filtered_fields_count > 0); } + + public: + FilteredFieldStream(instanceKlassHandle klass, bool local_only, bool classes_only) + : FieldStream(klass, local_only, classes_only) { + _filtered_fields_count = FilteredFieldsMap::filtered_fields_count((klassOop)klass(), local_only); + } + int field_count(); + void next() { + _index -= instanceKlass::next_offset; + if (has_filtered_field()) { + while (_index >=0 && FilteredFieldsMap::is_filtered_field((klassOop)_klass(), offset())) { + _index -= instanceKlass::next_offset; + } + } + } +}; diff --git a/hotspot/src/share/vm/runtime/registerMap.hpp b/hotspot/src/share/vm/runtime/registerMap.hpp new file mode 100644 index 00000000000..84d1c42a8ab --- /dev/null +++ b/hotspot/src/share/vm/runtime/registerMap.hpp @@ -0,0 +1,118 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class JavaThread; + +// +// RegisterMap +// +// A companion structure used for stack traversal. The RegisterMap contains +// misc. information needed in order to do correct stack traversal of stack +// frames. Hence, it must always be passed in as an argument to +// frame::sender(RegisterMap*). +// +// In particular, +// 1) It provides access to the thread for which the stack belongs. The +// thread object is needed in order to get sender of a deoptimized frame. +// +// 2) It is used to pass information from a callee frame to its caller +// frame about how the frame should be traversed. This is used to let +// the caller frame take care of calling oops-do of out-going +// arguments, when the callee frame is not instantiated yet. This +// happens, e.g., when a compiled frame calls into +// resolve_virtual_call. (Hence, it is critical that the same +// RegisterMap object is used for the entire stack walk. Normally, +// this is hidden by using the StackFrameStream.) This is used when +// doing follow_oops and oops_do. +// +// 3) The RegisterMap keeps track of the values of callee-saved registers +// from frame to frame (hence, the name). For some stack traversal the +// values of the callee-saved registers does not matter, e.g., if you +// only need the static properies such as frame type, pc, and such. +// Updating of the RegisterMap can be turned off by instantiating the +// register map as: RegisterMap map(thread, false); + +class RegisterMap : public StackObj { + public: + typedef julong LocationValidType; + enum { + reg_count = ConcreteRegisterImpl::number_of_registers, + location_valid_type_size = sizeof(LocationValidType)*8, + location_valid_size = (reg_count+location_valid_type_size-1)/location_valid_type_size + }; + private: + intptr_t* _location[reg_count]; // Location of registers (intptr_t* looks better than address in the debugger) + LocationValidType _location_valid[location_valid_size]; + bool _include_argument_oops; // Should include argument_oop marked locations for compiler + JavaThread* _thread; // Reference to current thread + bool _update_map; // Tells if the register map need to be + // updated when traversing the stack + +#ifdef ASSERT + void check_location_valid(); +#else + void check_location_valid() {} +#endif + + public: + debug_only(intptr_t* _update_for_id;) // Assert that RegisterMap is not updated twice for same frame + RegisterMap(JavaThread *thread, bool update_map = true); + RegisterMap(const RegisterMap* map); + + address location(VMReg reg) const { + int index = reg->value() / location_valid_type_size; + assert(0 <= reg->value() && reg->value() < reg_count, "range check"); + assert(0 <= index && index < location_valid_size, "range check"); + if (_location_valid[index] & ((LocationValidType)1 << (reg->value() % location_valid_type_size))) { + return (address) _location[reg->value()]; + } else { + return pd_location(reg); + } + } + + void set_location(VMReg reg, address loc) { + int index = reg->value() / location_valid_type_size; + assert(0 <= reg->value() && reg->value() < reg_count, "range check"); + assert(0 <= index && index < location_valid_size, "range check"); + assert(_update_map, "updating map that does not need updating"); + _location[reg->value()] = (intptr_t*) loc; + _location_valid[index] |= ((LocationValidType)1 << (reg->value() % location_valid_type_size)); + check_location_valid(); + } + + // Called by an entry frame. + void clear(); + + bool include_argument_oops() const { return _include_argument_oops; } + void set_include_argument_oops(bool f) { _include_argument_oops = f; } + + JavaThread *thread() const { return _thread; } + bool update_map() const { return _update_map; } + + void print_on(outputStream* st) const; + void print() const; + + // the following contains the definition of pd_xxx methods +# include "incls/_registerMap_pd.hpp.incl" +}; diff --git a/hotspot/src/share/vm/runtime/relocator.cpp b/hotspot/src/share/vm/runtime/relocator.cpp new file mode 100644 index 00000000000..4696ac6afb0 --- /dev/null +++ b/hotspot/src/share/vm/runtime/relocator.cpp @@ -0,0 +1,647 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_relocator.cpp.incl" + +#define MAX_METHOD_LENGTH 65535 + +#define MAX_SHORT ((1 << 15) - 1) +#define MIN_SHORT (- (1 << 15)) + +// Encapsulates a code change request. There are 3 types. +// General instruction, jump instruction, and table/lookup switches +// +class ChangeItem : public ResourceObj { + int _bci; + public: + ChangeItem(int bci) { _bci = bci; } + virtual bool handle_code_change(Relocator *r) = 0; + + // type info + virtual bool is_widen() { return false; } + virtual bool is_jump_widen() { return false; } + virtual bool is_switch_pad() { return false; } + + // accessors + int bci() { return _bci; } + void relocate(int break_bci, int delta) { if (_bci > break_bci) { _bci += delta; } } + + virtual bool adjust(int bci, int delta) { return false; } + + // debug + virtual void print() = 0; +}; + +class ChangeWiden : public ChangeItem { + int _new_ilen; // New length of instruction at bci + u_char* _inst_buffer; // New bytecodes + public: + ChangeWiden(int bci, int new_ilen, u_char* inst_buffer) : ChangeItem(bci) { + _new_ilen = new_ilen; + _inst_buffer = inst_buffer; + } + + // Callback to do instruction + bool handle_code_change(Relocator *r) { return r->handle_widen(bci(), _new_ilen, _inst_buffer); }; + + bool is_widen() { return true; } + + void print() { tty->print_cr("ChangeWiden. bci: %d New_ilen: %d", bci(), _new_ilen); } +}; + +class ChangeJumpWiden : public ChangeItem { + int _delta; // New length of instruction at bci + public: + ChangeJumpWiden(int bci, int delta) : ChangeItem(bci) { _delta = delta; } + + // Callback to do instruction + bool handle_code_change(Relocator *r) { return r->handle_jump_widen(bci(), _delta); }; + + bool is_jump_widen() { return true; } + + // If the bci matches, adjust the delta in the change jump request. + bool adjust(int jump_bci, int delta) { + if (bci() == jump_bci) { + if (_delta > 0) + _delta += delta; + else + _delta -= delta; + return true; + } + return false; + } + + void print() { tty->print_cr("ChangeJumpWiden. bci: %d Delta: %d", bci(), _delta); } +}; + +class ChangeSwitchPad : public ChangeItem { + int _padding; + bool _is_lookup_switch; + public: + ChangeSwitchPad(int bci, int padding, bool is_lookup_switch) : ChangeItem(bci) { + _padding = padding; + _is_lookup_switch = is_lookup_switch; + } + + // Callback to do instruction + bool handle_code_change(Relocator *r) { return r->handle_switch_pad(bci(), _padding, _is_lookup_switch); }; + + bool is_switch_pad() { return true; } + int padding() { return _padding; } + bool is_lookup_switch() { return _is_lookup_switch; } + + void print() { tty->print_cr("ChangeSwitchPad. bci: %d Padding: %d IsLookupSwitch: %d", bci(), _padding, _is_lookup_switch); } +}; + +//----------------------------------------------------------------------------------------------------------- +// Relocator code + +Relocator::Relocator(methodHandle m, RelocatorListener* listener) { + set_method(m); + set_code_length(method()->code_size()); + set_code_array(NULL); + // Allocate code array and copy bytecodes + if (!expand_code_array(0)) { + // Should have at least MAX_METHOD_LENGTH available or the verifier + // would have failed. + ShouldNotReachHere(); + } + set_compressed_line_number_table(NULL); + set_compressed_line_number_table_size(0); + _listener = listener; +} + +// size is the new size of the instruction at bci. Hence, if size is less than the current +// instruction sice, we will shrink the code. +methodHandle Relocator::insert_space_at(int bci, int size, u_char inst_buffer[], TRAPS) { + _changes = new GrowableArray (10); + _changes->push(new ChangeWiden(bci, size, inst_buffer)); + + if (TraceRelocator) { + tty->print_cr("Space at: %d Size: %d", bci, size); + _method->print(); + _method->print_codes(); + tty->print_cr("-------------------------------------------------"); + } + + if (!handle_code_changes()) return methodHandle(); + + // Construct the new method + methodHandle new_method = methodOopDesc::clone_with_new_data(method(), + code_array(), code_length(), + compressed_line_number_table(), + compressed_line_number_table_size(), + CHECK_(methodHandle())); + set_method(new_method); + + if (TraceRelocator) { + tty->print_cr("-------------------------------------------------"); + tty->print_cr("new method"); + _method->print_codes(); + } + + return new_method; +} + + +bool Relocator::handle_code_changes() { + assert(_changes != NULL, "changes vector must be initialized"); + + while (!_changes->is_empty()) { + // Inv: everything is aligned. + ChangeItem* ci = _changes->first(); + + if (TraceRelocator) { + ci->print(); + } + + // Execute operation + if (!ci->handle_code_change(this)) return false; + + // Shuffel items up + for (int index = 1; index < _changes->length(); index++) { + _changes->at_put(index-1, _changes->at(index)); + } + _changes->pop(); + } + return true; +} + + +bool Relocator::is_opcode_lookupswitch(Bytecodes::Code bc) { + switch (bc) { + case Bytecodes::_tableswitch: return false; + case Bytecodes::_lookupswitch: // not rewritten on ia64 + case Bytecodes::_fast_linearswitch: // rewritten _lookupswitch + case Bytecodes::_fast_binaryswitch: return true; // rewritten _lookupswitch + default: ShouldNotReachHere(); + } + return true; // dummy +} + +// We need a special instruction size method, since lookupswitches and tableswitches might not be +// properly alligned during relocation +int Relocator::rc_instr_len(int bci) { + Bytecodes::Code bc= code_at(bci); + switch (bc) { + // In the case of switch instructions, see if we have the original + // padding recorded. + case Bytecodes::_tableswitch: + case Bytecodes::_lookupswitch: + case Bytecodes::_fast_linearswitch: + case Bytecodes::_fast_binaryswitch: + { + int pad = get_orig_switch_pad(bci, is_opcode_lookupswitch(bc)); + if (pad == -1) { + return instruction_length_at(bci); + } + // Otherwise, depends on the switch type. + switch (bc) { + case Bytecodes::_tableswitch: { + int lo = int_at(bci + 1 + pad + 4 * 1); + int hi = int_at(bci + 1 + pad + 4 * 2); + int n = hi - lo + 1; + return 1 + pad + 4*(3 + n); + } + case Bytecodes::_lookupswitch: + case Bytecodes::_fast_linearswitch: + case Bytecodes::_fast_binaryswitch: { + int npairs = int_at(bci + 1 + pad + 4 * 1); + return 1 + pad + 4*(2 + 2*npairs); + } + default: + ShouldNotReachHere(); + } + } + } + return instruction_length_at(bci); +} + +// If a change item is recorded for "pc", with type "ct", returns the +// associated padding, else -1. +int Relocator::get_orig_switch_pad(int bci, bool is_lookup_switch) { + for (int k = 0; k < _changes->length(); k++) { + ChangeItem* ci = _changes->at(k); + if (ci->is_switch_pad()) { + ChangeSwitchPad* csp = (ChangeSwitchPad*)ci; + if (csp->is_lookup_switch() == is_lookup_switch && csp->bci() == bci) { + return csp->padding(); + } + } + } + return -1; +} + + +// Push a ChangeJumpWiden if it doesn't already exist on the work queue, +// otherwise adjust the item already there by delta. The calculation for +// new_delta is wrong for this because it uses the offset stored in the +// code stream itself which wasn't fixed when item was pushed on the work queue. +void Relocator::push_jump_widen(int bci, int delta, int new_delta) { + for (int j = 0; j < _changes->length(); j++) { + ChangeItem* ci = _changes->at(j); + if (ci->adjust(bci, delta)) return; + } + _changes->push(new ChangeJumpWiden(bci, new_delta)); +} + + +// The current instruction of "c" is a jump; one of its offset starts +// at "offset" and is a short if "isShort" is "TRUE", +// and an integer otherwise. If the jump crosses "breakPC", change +// the span of the jump by "delta". +void Relocator::change_jump(int bci, int offset, bool is_short, int break_bci, int delta) { + int bci_delta = (is_short) ? short_at(offset) : int_at(offset); + int targ = bci + bci_delta; + + if ((bci <= break_bci && targ > break_bci) || + (bci > break_bci && targ <= break_bci)) { + int new_delta; + if (bci_delta > 0) + new_delta = bci_delta + delta; + else + new_delta = bci_delta - delta; + + if (is_short && ((new_delta > MAX_SHORT) || new_delta < MIN_SHORT)) { + push_jump_widen(bci, delta, new_delta); + } else if (is_short) { + short_at_put(offset, new_delta); + } else { + int_at_put(offset, new_delta); + } + } +} + + +// Changes all jumps crossing "break_bci" by "delta". May enqueue things +// on "rc->changes" +void Relocator::change_jumps(int break_bci, int delta) { + int bci = 0; + Bytecodes::Code bc; + // Now, adjust any affected instructions. + while (bci < code_length()) { + switch (bc= code_at(bci)) { + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + case Bytecodes::_goto: + case Bytecodes::_jsr: + change_jump(bci, bci+1, true, break_bci, delta); + break; + case Bytecodes::_goto_w: + case Bytecodes::_jsr_w: + change_jump(bci, bci+1, false, break_bci, delta); + break; + case Bytecodes::_tableswitch: + case Bytecodes::_lookupswitch: + case Bytecodes::_fast_linearswitch: + case Bytecodes::_fast_binaryswitch: { + int recPad = get_orig_switch_pad(bci, (bc != Bytecodes::_tableswitch)); + int oldPad = (recPad != -1) ? recPad : align(bci+1) - (bci+1); + if (bci > break_bci) { + int new_bci = bci + delta; + int newPad = align(new_bci+1) - (new_bci+1); + // Do we need to check the padding? + if (newPad != oldPad) { + if (recPad == -1) { + _changes->push(new ChangeSwitchPad(bci, oldPad, (bc != Bytecodes::_tableswitch))); + } + } + } + + // Then the rest, which depend on the kind of switch. + switch (bc) { + case Bytecodes::_tableswitch: { + change_jump(bci, bci +1 + oldPad, false, break_bci, delta); + // We cannot use the Bytecode_tableswitch abstraction, since the padding might not be correct. + int lo = int_at(bci + 1 + oldPad + 4 * 1); + int hi = int_at(bci + 1 + oldPad + 4 * 2); + int n = hi - lo + 1; + for (int k = 0; k < n; k++) { + change_jump(bci, bci +1 + oldPad + 4*(k+3), false, break_bci, delta); + } + // Special next-bci calculation here... + bci += 1 + oldPad + (n+3)*4; + continue; + } + case Bytecodes::_lookupswitch: + case Bytecodes::_fast_linearswitch: + case Bytecodes::_fast_binaryswitch: { + change_jump(bci, bci +1 + oldPad, false, break_bci, delta); + // We cannot use the Bytecode_lookupswitch abstraction, since the padding might not be correct. + int npairs = int_at(bci + 1 + oldPad + 4 * 1); + for (int k = 0; k < npairs; k++) { + change_jump(bci, bci + 1 + oldPad + 4*(2 + 2*k + 1), false, break_bci, delta); + } + /* Special next-bci calculation here... */ + bci += 1 + oldPad + (2 + (npairs*2))*4; + continue; + } + default: + ShouldNotReachHere(); + } + } + default: + break; + } + bci += rc_instr_len(bci); + } +} + +// The width of instruction at "pc" is changing by "delta". Adjust the +// exception table, if any, of "rc->mb". +void Relocator::adjust_exception_table(int bci, int delta) { + typeArrayOop table = method()->exception_table(); + for (int index = 0; index < table->length(); index +=4) { + if (table->int_at(index) > bci) { + table->int_at_put(index+0, table->int_at(index+0) + delta); + table->int_at_put(index+1, table->int_at(index+1) + delta); + } else if (bci < table->int_at(index+1)) { + table->int_at_put(index+1, table->int_at(index+1) + delta); + } + if (table->int_at(index+2) > bci) + table->int_at_put(index+2, table->int_at(index+2) + delta); + } +} + + +// The width of instruction at "bci" is changing by "delta". Adjust the line number table. +void Relocator::adjust_line_no_table(int bci, int delta) { + if (method()->has_linenumber_table()) { + CompressedLineNumberReadStream reader(method()->compressed_linenumber_table()); + CompressedLineNumberWriteStream writer(64); // plenty big for most line number tables + while (reader.read_pair()) { + int adjustment = (reader.bci() > bci) ? delta : 0; + writer.write_pair(reader.bci() + adjustment, reader.line()); + } + writer.write_terminator(); + set_compressed_line_number_table(writer.buffer()); + set_compressed_line_number_table_size(writer.position()); + } +} + + +// The width of instruction at "bci" is changing by "delta". Adjust the local variable table. +void Relocator::adjust_local_var_table(int bci, int delta) { + int localvariable_table_length = method()->localvariable_table_length(); + if (localvariable_table_length > 0) { + LocalVariableTableElement* table = method()->localvariable_table_start(); + for (int i = 0; i < localvariable_table_length; i++) { + u2 current_bci = table[i].start_bci; + if (current_bci > bci) { + table[i].start_bci = current_bci + delta; + } else { + u2 current_length = table[i].length; + if (current_bci + current_length > bci) { + table[i].length = current_length + delta; + } + } + } + } +} + + +bool Relocator::expand_code_array(int delta) { + int length = MAX2(code_length() + delta, code_length() * (100+code_slop_pct()) / 100); + + if (length > MAX_METHOD_LENGTH) { + if (delta == 0 && code_length() <= MAX_METHOD_LENGTH) { + length = MAX_METHOD_LENGTH; + } else { + return false; + } + } + + unsigned char* new_code_array = NEW_RESOURCE_ARRAY(unsigned char, length); + if (!new_code_array) return false; + + // Expanding current array + if (code_array() != NULL) { + memcpy(new_code_array, code_array(), code_length()); + } else { + // Initial copy. Copy directly from methodOop + memcpy(new_code_array, method()->code_base(), code_length()); + } + + set_code_array(new_code_array); + set_code_array_length(length); + + return true; +} + + +// The instruction at "bci", whose size is "ilen", is changing size by +// "delta". Reallocate, move code, recalculate jumps, and enqueue +// change items as necessary. +bool Relocator::relocate_code(int bci, int ilen, int delta) { + int next_bci = bci + ilen; + if (delta > 0 && code_length() + delta > code_array_length()) { + // Expand allocated code space, if necessary. + if (!expand_code_array(delta)) { + return false; + } + } + + // We require 4-byte alignment of code arrays. + assert(((intptr_t)code_array() & 3) == 0, "check code alignment"); + // Change jumps before doing the copying; this routine requires aligned switches. + change_jumps(bci, delta); + + // In case we have shrunken a tableswitch/lookupswitch statement, we store the last + // bytes that get overwritten. We have to copy the bytes after the change_jumps method + // has been called, since it is likly to update last offset in a tableswitch/lookupswitch + if (delta < 0) { + assert(delta>=-3, "we cannot overwrite more than 3 bytes"); + memcpy(_overwrite, addr_at(bci + ilen + delta), -delta); + } + + memmove(addr_at(next_bci + delta), addr_at(next_bci), code_length() - next_bci); + set_code_length(code_length() + delta); + // Also adjust exception tables... + adjust_exception_table(bci, delta); + // Line number tables... + adjust_line_no_table(bci, delta); + // And local variable table... + adjust_local_var_table(bci, delta); + + // Relocate the pending change stack... + for (int j = 0; j < _changes->length(); j++) { + ChangeItem* ci = _changes->at(j); + ci->relocate(bci, delta); + } + + // Notify any listeners about code relocation + notify(bci, delta, code_length()); + + return true; +} + +// relocate a general instruction. Called by ChangeWiden class +bool Relocator::handle_widen(int bci, int new_ilen, u_char inst_buffer[]) { + int ilen = rc_instr_len(bci); + if (!relocate_code(bci, ilen, new_ilen - ilen)) + return false; + + // Insert new bytecode(s) + for(int k = 0; k < new_ilen; k++) { + code_at_put(bci + k, (Bytecodes::Code)inst_buffer[k]); + } + + return true; +} + +// handle jump_widen instruction. Called be ChangeJumpWiden class +bool Relocator::handle_jump_widen(int bci, int delta) { + int ilen = rc_instr_len(bci); + + Bytecodes::Code bc = code_at(bci); + switch (bc) { + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: { + const int goto_length = Bytecodes::length_for(Bytecodes::_goto); + + // If 'if' points to the next bytecode after goto, it's already handled. + // it shouldn't be. + assert (short_at(bci+1) != ilen+goto_length, "if relocation already handled"); + assert(ilen == 3, "check length"); + + // Convert to 0 if goto 6 + // 3 _goto 11 + // 6 _goto_w + // 11 + const int goto_w_length = Bytecodes::length_for(Bytecodes::_goto_w); + const int add_bci = goto_length + goto_w_length; + + if (!relocate_code(bci, 3, /*delta*/add_bci)) return false; + + // if bytecode points to goto_w instruction + short_at_put(bci + 1, ilen + goto_length); + + int cbci = bci + ilen; + // goto around + code_at_put(cbci, Bytecodes::_goto); + short_at_put(cbci + 1, add_bci); + // goto_w + cbci = cbci + goto_length; + code_at_put(cbci, Bytecodes::_goto_w); + if (delta > 0) { + delta += 2; // goto_w is 2 bytes more than "if" code + } else { + delta -= ilen+goto_length; // branch starts at goto_w offset + } + int_at_put(cbci + 1, delta); + break; + + } + case Bytecodes::_goto: + case Bytecodes::_jsr: + assert(ilen == 3, "check length"); + + if (!relocate_code(bci, 3, 2)) return false; + if (bc == Bytecodes::_goto) + code_at_put(bci, Bytecodes::_goto_w); + else + code_at_put(bci, Bytecodes::_jsr_w); + + // If it's a forward jump, add 2 for the widening. + if (delta > 0) delta += 2; + int_at_put(bci + 1, delta); + break; + + default: ShouldNotReachHere(); + } + + return true; +} + +// handle lookup/table switch instructions. Called be ChangeSwitchPad class +bool Relocator::handle_switch_pad(int bci, int old_pad, bool is_lookup_switch) { + int ilen = rc_instr_len(bci); + int new_pad = align(bci+1) - (bci+1); + int pad_delta = new_pad - old_pad; + if (pad_delta != 0) { + int len; + if (!is_lookup_switch) { + int low = int_at(bci+1+old_pad+4); + int high = int_at(bci+1+old_pad+8); + len = high-low+1 + 3; // 3 for default, hi, lo. + } else { + int npairs = int_at(bci+1+old_pad+4); + len = npairs*2 + 2; // 2 for default, npairs. + } + // Because "relocateCode" does a "changeJumps" loop, + // which parses instructions to determine their length, + // we need to call that before messing with the current + // instruction. Since it may also overwrite the current + // instruction when moving down, remember the possibly + // overwritten part. + + // Move the code following the instruction... + if (!relocate_code(bci, ilen, pad_delta)) return false; + + if (pad_delta < 0) { + // Move the shrunken instruction down. + memmove(addr_at(bci + 1 + new_pad), + addr_at(bci + 1 + old_pad), + len * 4 + pad_delta); + memmove(addr_at(bci + 1 + new_pad + len*4 + pad_delta), + _overwrite, -pad_delta); + } else { + assert(pad_delta > 0, "check"); + // Move the expanded instruction up. + memmove(addr_at(bci +1 + new_pad), + addr_at(bci +1 + old_pad), + len * 4); + } + } + return true; +} diff --git a/hotspot/src/share/vm/runtime/relocator.hpp b/hotspot/src/share/vm/runtime/relocator.hpp new file mode 100644 index 00000000000..598f9ec145e --- /dev/null +++ b/hotspot/src/share/vm/runtime/relocator.hpp @@ -0,0 +1,118 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This code has been converted from the 1.1E java virtual machine +// Thanks to the JavaTopics group for using the code + +class ChangeItem; + +// Callback object for code relocations +class RelocatorListener : public StackObj { + public: + RelocatorListener() {}; + virtual void relocated(int bci, int delta, int new_method_size) = 0; +}; + + +class Relocator : public ResourceObj { + public: + Relocator(methodHandle method, RelocatorListener* listener); + methodHandle insert_space_at(int bci, int space, u_char inst_buffer[], TRAPS); + + // Callbacks from ChangeItem's + bool handle_code_changes(); + bool handle_widen (int bci, int new_ilen, u_char inst_buffer[]); // handles general instructions + void push_jump_widen (int bci, int delta, int new_delta); // pushes jumps + bool handle_jump_widen (int bci, int delta); // handles jumps + bool handle_switch_pad (int bci, int old_pad, bool is_lookup_switch); // handles table and lookup switches + + private: + unsigned char* _code_array; + int _code_array_length; + int _code_length; + unsigned char* _compressed_line_number_table; + int _compressed_line_number_table_size; + methodHandle _method; + u_char _overwrite[3]; // stores overwritten bytes for shrunken instructions + + GrowableArray* _changes; + + unsigned char* code_array() const { return _code_array; } + void set_code_array(unsigned char* array) { _code_array = array; } + + int code_length() const { return _code_length; } + void set_code_length(int length) { _code_length = length; } + + int code_array_length() const { return _code_array_length; } + void set_code_array_length(int length) { _code_array_length = length; } + + unsigned char* compressed_line_number_table() const { return _compressed_line_number_table; } + void set_compressed_line_number_table(unsigned char* table) { _compressed_line_number_table = table; } + + int compressed_line_number_table_size() const { return _compressed_line_number_table_size; } + void set_compressed_line_number_table_size(int size) { _compressed_line_number_table_size = size; } + + methodHandle method() const { return _method; } + void set_method(methodHandle method) { _method = method; } + + // This will return a raw bytecode, which is possibly rewritten. + Bytecodes::Code code_at(int bci) const { return (Bytecodes::Code) code_array()[bci]; } + void code_at_put(int bci, Bytecodes::Code code) { code_array()[bci] = (char) code; } + + // get and set signed integers in the code_array + inline int int_at(int bci) const { return Bytes::get_Java_u4(&code_array()[bci]); } + inline void int_at_put(int bci, int value) { Bytes::put_Java_u4(&code_array()[bci], value); } + + // get and set signed shorts in the code_array + inline short short_at(int bci) const { return (short)Bytes::get_Java_u2(&code_array()[bci]); } + inline void short_at_put(int bci, short value) { Bytes::put_Java_u2((address) &code_array()[bci], value); } + + // get the address of in the code_array + inline char* addr_at(int bci) const { return (char*) &code_array()[bci]; } + + int instruction_length_at(int bci) { return Bytecodes::length_at(code_array() + bci); } + + // Helper methods + int align(int n) const { return (n+3) & ~3; } + int code_slop_pct() const { return 25; } + bool is_opcode_lookupswitch(Bytecodes::Code bc); + + // basic relocation methods + bool relocate_code (int bci, int ilen, int delta); + void change_jumps (int break_bci, int delta); + void change_jump (int bci, int offset, bool is_short, int break_bci, int delta); + void adjust_exception_table(int bci, int delta); + void adjust_line_no_table (int bci, int delta); + void adjust_local_var_table(int bci, int delta); + int get_orig_switch_pad (int bci, bool is_lookup_switch); + int rc_instr_len (int bci); + bool expand_code_array (int delta); + + // Callback support + RelocatorListener *_listener; + void notify(int bci, int delta, int new_code_length) { + if (_listener != NULL) + _listener->relocated(bci, delta, new_code_length); + } +}; diff --git a/hotspot/src/share/vm/runtime/rframe.cpp b/hotspot/src/share/vm/runtime/rframe.cpp new file mode 100644 index 00000000000..348ef47571b --- /dev/null +++ b/hotspot/src/share/vm/runtime/rframe.cpp @@ -0,0 +1,170 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" + +#include "incls/_rframe.cpp.incl" + +static RFrame*const noCaller = (RFrame*) 0x1; // no caller (i.e., initial frame) +static RFrame*const noCallerYet = (RFrame*) 0x0; // caller not yet computed + +RFrame::RFrame(frame fr, JavaThread* thread, RFrame*const callee) : + _fr(fr), _thread(thread), _callee(callee), _num(callee ? callee->num() + 1 : 0) { + _caller = (RFrame*)noCallerYet; + _invocations = 0; + _distance = 0; +} + +void RFrame::set_distance(int d) { + assert(is_compiled() || d >= 0, "should be positive"); + _distance = d; +} + +InterpretedRFrame::InterpretedRFrame(frame fr, JavaThread* thread, RFrame*const callee) +: RFrame(fr, thread, callee) { + RegisterMap map(thread, false); + _vf = javaVFrame::cast(vframe::new_vframe(&_fr, &map, thread)); + _method = methodHandle(thread, _vf->method()); + assert( _vf->is_interpreted_frame(), "must be interpreted"); + init(); +} + +InterpretedRFrame::InterpretedRFrame(frame fr, JavaThread* thread, methodHandle m) +: RFrame(fr, thread, NULL) { + RegisterMap map(thread, false); + _vf = javaVFrame::cast(vframe::new_vframe(&_fr, &map, thread)); + _method = m; + + assert( _vf->is_interpreted_frame(), "must be interpreted"); + init(); +} + +CompiledRFrame::CompiledRFrame(frame fr, JavaThread* thread, RFrame*const callee) +: RFrame(fr, thread, callee) { + init(); +} + +CompiledRFrame::CompiledRFrame(frame fr, JavaThread* thread) +: RFrame(fr, thread, NULL) { + init(); +} + +DeoptimizedRFrame::DeoptimizedRFrame(frame fr, JavaThread* thread, RFrame*const callee) +: InterpretedRFrame(fr, thread, callee) {} + +RFrame* RFrame::new_RFrame(frame fr, JavaThread* thread, RFrame*const callee) { + RFrame* rf; + int dist = callee ? callee->distance() : -1; + if (fr.is_interpreted_frame()) { + rf = new InterpretedRFrame(fr, thread, callee); + dist++; + } else if (fr.is_compiled_frame()) { + // Even deopted frames look compiled because the deopt + // is invisible until it happens. + rf = new CompiledRFrame(fr, thread, callee); + } else { + assert(false, "Unhandled frame type"); + } + rf->set_distance(dist); + rf->init(); + return rf; +} + +RFrame* RFrame::caller() { + if (_caller != noCallerYet) return (_caller == noCaller) ? NULL : _caller; // already computed caller + + // caller not yet computed; do it now + if (_fr.is_first_java_frame()) { + _caller = (RFrame*)noCaller; + return NULL; + } + + RegisterMap map(_thread, false); + frame sender = _fr.real_sender(&map); + if (sender.is_java_frame()) { + _caller = new_RFrame(sender, thread(), this); + return _caller; + } + + // Real caller is not java related + _caller = (RFrame*)noCaller; + return NULL; +} + +int InterpretedRFrame::cost() const { + return _method->code_size(); // fix this + //return _method->estimated_inline_cost(_receiverKlass); +} + +int CompiledRFrame::cost() const { + nmethod* nm = top_method()->code(); + if (nm != NULL) { + return nm->code_size(); + } else { + return top_method()->code_size(); + } +} + +void CompiledRFrame::init() { + RegisterMap map(thread(), false); + vframe* vf = vframe::new_vframe(&_fr, &map, thread()); + assert(vf->is_compiled_frame(), "must be compiled"); + _nm = compiledVFrame::cast(vf)->code(); + vf = vf->top(); + _vf = javaVFrame::cast(vf); + _method = methodHandle(thread(), CodeCache::find_nmethod(_fr.pc())->method()); + assert(_method(), "should have found a method"); +#ifndef PRODUCT + _invocations = _method->compiled_invocation_count(); +#endif +} + +void InterpretedRFrame::init() { + _invocations = _method->invocation_count() + _method->backedge_count(); +} + +void RFrame::print(const char* kind) { +#ifndef PRODUCT +#ifdef COMPILER2 + int cnt = top_method()->interpreter_invocation_count(); +#else + int cnt = top_method()->invocation_count(); +#endif + tty->print("%3d %s ", _num, is_interpreted() ? "I" : "C"); + top_method()->print_short_name(tty); + tty->print_cr(": inv=%5d(%d) cst=%4d", _invocations, cnt, cost()); +#endif +} + +void CompiledRFrame::print() { + RFrame::print("comp"); +} + +void InterpretedRFrame::print() { + RFrame::print("int."); +} + +void DeoptimizedRFrame::print() { + RFrame::print("deopt."); +} diff --git a/hotspot/src/share/vm/runtime/rframe.hpp b/hotspot/src/share/vm/runtime/rframe.hpp new file mode 100644 index 00000000000..a20cc419c98 --- /dev/null +++ b/hotspot/src/share/vm/runtime/rframe.hpp @@ -0,0 +1,117 @@ +/* + * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// rframes ("recompiler frames") decorate stack frames with some extra information +// needed by the recompiler. The recompiler views the stack (at the time of recompilation) +// as a list of rframes. + +class RFrame : public ResourceObj { + protected: + const frame _fr; // my frame + JavaThread* const _thread; // thread where frame resides. + RFrame* _caller; // caller / callee rframes (or NULL) + RFrame*const _callee; + const int _num; // stack frame number (0 = most recent) + int _invocations; // current invocation estimate (for this frame) + // (i.e., how often was this frame called) + int _distance; // recompilation search "distance" (measured in # of interpreted frames) + + RFrame(frame fr, JavaThread* thread, RFrame*const callee); + virtual void init() = 0; // compute invocations, loopDepth, etc. + void print(const char* name); + + public: + + static RFrame* new_RFrame(frame fr, JavaThread* thread, RFrame*const callee); + + virtual bool is_interpreted() const { return false; } + virtual bool is_compiled() const { return false; } + int distance() const { return _distance; } + void set_distance(int d); + int invocations() const { return _invocations; } + int num() const { return _num; } + frame fr() const { return _fr; } + JavaThread* thread() const { return _thread; } + virtual int cost() const = 0; // estimated inlining cost (size) + virtual methodHandle top_method() const = 0; + virtual javaVFrame* top_vframe() const = 0; + virtual nmethod* nm() const { ShouldNotCallThis(); return NULL; } + + RFrame* caller(); + RFrame* callee() const { return _callee; } + RFrame* parent() const; // rframe containing lexical scope (if any) + virtual void print() = 0; + + static int computeSends(methodOop m); + static int computeSends(nmethod* nm); + static int computeCumulSends(methodOop m); + static int computeCumulSends(nmethod* nm); +}; + +class CompiledRFrame : public RFrame { // frame containing a compiled method + protected: + nmethod* _nm; + javaVFrame* _vf; // top vframe; may be NULL (for most recent frame) + methodHandle _method; // top method + + CompiledRFrame(frame fr, JavaThread* thread, RFrame*const callee); + void init(); + friend class RFrame; + + public: + CompiledRFrame(frame fr, JavaThread* thread); // for nmethod triggering its counter (callee == NULL) + bool is_compiled() const { return true; } + methodHandle top_method() const { return _method; } + javaVFrame* top_vframe() const { return _vf; } + nmethod* nm() const { return _nm; } + int cost() const; + void print(); +}; + +class InterpretedRFrame : public RFrame { // interpreter frame + protected: + javaVFrame* _vf; // may be NULL (for most recent frame) + methodHandle _method; + + InterpretedRFrame(frame fr, JavaThread* thread, RFrame*const callee); + void init(); + friend class RFrame; + + public: + InterpretedRFrame(frame fr, JavaThread* thread, methodHandle m); // constructor for method triggering its invocation counter + bool is_interpreted() const { return true; } + methodHandle top_method() const { return _method; } + javaVFrame* top_vframe() const { return _vf; } + int cost() const; + void print(); +}; + +// treat deoptimized frames as interpreted +class DeoptimizedRFrame : public InterpretedRFrame { + protected: + DeoptimizedRFrame(frame fr, JavaThread* thread, RFrame*const callee); + friend class RFrame; + public: + void print(); +}; diff --git a/hotspot/src/share/vm/runtime/safepoint.cpp b/hotspot/src/share/vm/runtime/safepoint.cpp new file mode 100644 index 00000000000..2a3b838f6e5 --- /dev/null +++ b/hotspot/src/share/vm/runtime/safepoint.cpp @@ -0,0 +1,1215 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_safepoint.cpp.incl" + +// -------------------------------------------------------------------------------------------------- +// Implementation of Safepoint begin/end + +SafepointSynchronize::SynchronizeState volatile SafepointSynchronize::_state = SafepointSynchronize::_not_synchronized; +volatile int SafepointSynchronize::_waiting_to_block = 0; +jlong SafepointSynchronize::_last_safepoint = 0; +volatile int SafepointSynchronize::_safepoint_counter = 0; +static volatile int PageArmed = 0 ; // safepoint polling page is RO|RW vs PROT_NONE +static volatile int TryingToBlock = 0 ; // proximate value -- for advisory use only +static bool timeout_error_printed = false; + +// Roll all threads forward to a safepoint and suspend them all +void SafepointSynchronize::begin() { + + Thread* myThread = Thread::current(); + assert(myThread->is_VM_thread(), "Only VM thread may execute a safepoint"); + + _last_safepoint = os::javaTimeNanos(); + +#ifndef SERIALGC + if (UseConcMarkSweepGC) { + // In the future we should investigate whether CMS can use the + // more-general mechanism below. DLD (01/05). + ConcurrentMarkSweepThread::synchronize(false); + } else { + ConcurrentGCThread::safepoint_synchronize(); + } +#endif // SERIALGC + + // By getting the Threads_lock, we assure that no threads are about to start or + // exit. It is released again in SafepointSynchronize::end(). + Threads_lock->lock(); + + assert( _state == _not_synchronized, "trying to safepoint synchronize with wrong state"); + + int nof_threads = Threads::number_of_threads(); + + if (TraceSafepoint) { + tty->print_cr("Safepoint synchronization initiated. (%d)", nof_threads); + } + + RuntimeService::record_safepoint_begin(); + + { + MutexLocker mu(Safepoint_lock); + + // Set number of threads to wait for, before we initiate the callbacks + _waiting_to_block = nof_threads; + TryingToBlock = 0 ; + int still_running = nof_threads; + + // Save the starting time, so that it can be compared to see if this has taken + // too long to complete. + jlong safepoint_limit_time; + timeout_error_printed = false; + + // Begin the process of bringing the system to a safepoint. + // Java threads can be in several different states and are + // stopped by different mechanisms: + // + // 1. Running interpreted + // The interpeter dispatch table is changed to force it to + // check for a safepoint condition between bytecodes. + // 2. Running in native code + // When returning from the native code, a Java thread must check + // the safepoint _state to see if we must block. If the + // VM thread sees a Java thread in native, it does + // not wait for this thread to block. The order of the memory + // writes and reads of both the safepoint state and the Java + // threads state is critical. In order to guarantee that the + // memory writes are serialized with respect to each other, + // the VM thread issues a memory barrier instruction + // (on MP systems). In order to avoid the overhead of issuing + // a memory barrier for each Java thread making native calls, each Java + // thread performs a write to a single memory page after changing + // the thread state. The VM thread performs a sequence of + // mprotect OS calls which forces all previous writes from all + // Java threads to be serialized. This is done in the + // os::serialize_thread_states() call. This has proven to be + // much more efficient than executing a membar instruction + // on every call to native code. + // 3. Running compiled Code + // Compiled code reads a global (Safepoint Polling) page that + // is set to fault if we are trying to get to a safepoint. + // 4. Blocked + // A thread which is blocked will not be allowed to return from the + // block condition until the safepoint operation is complete. + // 5. In VM or Transitioning between states + // If a Java thread is currently running in the VM or transitioning + // between states, the safepointing code will wait for the thread to + // block itself when it attempts transitions to a new state. + // + _state = _synchronizing; + OrderAccess::fence(); + + // Flush all thread states to memory + if (!UseMembar) { + os::serialize_thread_states(); + } + + // Make interpreter safepoint aware + Interpreter::notice_safepoints(); + + if (UseCompilerSafepoints && DeferPollingPageLoopCount < 0) { + // Make polling safepoint aware + guarantee (PageArmed == 0, "invariant") ; + PageArmed = 1 ; + os::make_polling_page_unreadable(); + } + + // Consider using active_processor_count() ... but that call is expensive. + int ncpus = os::processor_count() ; + +#ifdef ASSERT + for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) { + assert(cur->safepoint_state()->is_running(), "Illegal initial state"); + } +#endif // ASSERT + + if (SafepointTimeout) + safepoint_limit_time = os::javaTimeNanos() + (jlong)SafepointTimeoutDelay * MICROUNITS; + + // Iterate through all threads until it have been determined how to stop them all at a safepoint + unsigned int iterations = 0; + int steps = 0 ; + while(still_running > 0) { + for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) { + assert(!cur->is_ConcurrentGC_thread(), "A concurrent GC thread is unexpectly being suspended"); + ThreadSafepointState *cur_state = cur->safepoint_state(); + if (cur_state->is_running()) { + cur_state->examine_state_of_thread(); + if (!cur_state->is_running()) { + still_running--; + // consider adjusting steps downward: + // steps = 0 + // steps -= NNN + // steps >>= 1 + // steps = MIN(steps, 2000-100) + // if (iterations != 0) steps -= NNN + } + if (TraceSafepoint && Verbose) cur_state->print(); + } + } + + if ( (PrintSafepointStatistics || (PrintSafepointStatisticsTimeout > 0)) + && iterations == 0) { + begin_statistics(nof_threads, still_running); + } + + if (still_running > 0) { + // Check for if it takes to long + if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) { + print_safepoint_timeout(_spinning_timeout); + } + + // Spin to avoid context switching. + // There's a tension between allowing the mutators to run (and rendezvous) + // vs spinning. As the VM thread spins, wasting cycles, it consumes CPU that + // a mutator might otherwise use profitably to reach a safepoint. Excessive + // spinning by the VM thread on a saturated system can increase rendezvous latency. + // Blocking or yielding incur their own penalties in the form of context switching + // and the resultant loss of $ residency. + // + // Further complicating matters is that yield() does not work as naively expected + // on many platforms -- yield() does not guarantee that any other ready threads + // will run. As such we revert yield_all() after some number of iterations. + // Yield_all() is implemented as a short unconditional sleep on some platforms. + // Typical operating systems round a "short" sleep period up to 10 msecs, so sleeping + // can actually increase the time it takes the VM thread to detect that a system-wide + // stop-the-world safepoint has been reached. In a pathological scenario such as that + // described in CR6415670 the VMthread may sleep just before the mutator(s) become safe. + // In that case the mutators will be stalled waiting for the safepoint to complete and the + // the VMthread will be sleeping, waiting for the mutators to rendezvous. The VMthread + // will eventually wake up and detect that all mutators are safe, at which point + // we'll again make progress. + // + // Beware too that that the VMThread typically runs at elevated priority. + // Its default priority is higher than the default mutator priority. + // Obviously, this complicates spinning. + // + // Note too that on Windows XP SwitchThreadTo() has quite different behavior than Sleep(0). + // Sleep(0) will _not yield to lower priority threads, while SwitchThreadTo() will. + // + // See the comments in synchronizer.cpp for additional remarks on spinning. + // + // In the future we might: + // 1. Modify the safepoint scheme to avoid potentally unbounded spinning. + // This is tricky as the path used by a thread exiting the JVM (say on + // on JNI call-out) simply stores into its state field. The burden + // is placed on the VM thread, which must poll (spin). + // 2. Find something useful to do while spinning. If the safepoint is GC-related + // we might aggressively scan the stacks of threads that are already safe. + // 3. Use Solaris schedctl to examine the state of the still-running mutators. + // If all the mutators are ONPROC there's no reason to sleep or yield. + // 4. YieldTo() any still-running mutators that are ready but OFFPROC. + // 5. Check system saturation. If the system is not fully saturated then + // simply spin and avoid sleep/yield. + // 6. As still-running mutators rendezvous they could unpark the sleeping + // VMthread. This works well for still-running mutators that become + // safe. The VMthread must still poll for mutators that call-out. + // 7. Drive the policy on time-since-begin instead of iterations. + // 8. Consider making the spin duration a function of the # of CPUs: + // Spin = (((ncpus-1) * M) + K) + F(still_running) + // Alternately, instead of counting iterations of the outer loop + // we could count the # of threads visited in the inner loop, above. + // 9. On windows consider using the return value from SwitchThreadTo() + // to drive subsequent spin/SwitchThreadTo()/Sleep(N) decisions. + + if (UseCompilerSafepoints && int(iterations) == DeferPollingPageLoopCount) { + guarantee (PageArmed == 0, "invariant") ; + PageArmed = 1 ; + os::make_polling_page_unreadable(); + } + + // Instead of (ncpus > 1) consider either (still_running < (ncpus + EPSILON)) or + // ((still_running + _waiting_to_block - TryingToBlock)) < ncpus) + ++steps ; + if (ncpus > 1 && steps < SafepointSpinBeforeYield) { + SpinPause() ; // MP-Polite spin + } else + if (steps < DeferThrSuspendLoopCount) { + os::NakedYield() ; + } else { + os::yield_all(steps) ; + // Alternately, the VM thread could transiently depress its scheduling priority or + // transiently increase the priority of the tardy mutator(s). + } + + iterations ++ ; + } + assert(iterations < (uint)max_jint, "We have been iterating in the safepoint loop too long"); + } + assert(still_running == 0, "sanity check"); + + if (PrintSafepointStatistics) { + update_statistics_on_spin_end(); + } + + // wait until all threads are stopped + while (_waiting_to_block > 0) { + if (TraceSafepoint) tty->print_cr("Waiting for %d thread(s) to block", _waiting_to_block); + if (!SafepointTimeout || timeout_error_printed) { + Safepoint_lock->wait(true); // true, means with no safepoint checks + } else { + // Compute remaining time + jlong remaining_time = safepoint_limit_time - os::javaTimeNanos(); + + // If there is no remaining time, then there is an error + if (remaining_time < 0 || Safepoint_lock->wait(true, remaining_time / MICROUNITS)) { + print_safepoint_timeout(_blocking_timeout); + } + } + } + assert(_waiting_to_block == 0, "sanity check"); + +#ifndef PRODUCT + if (SafepointTimeout) { + jlong current_time = os::javaTimeNanos(); + if (safepoint_limit_time < current_time) { + tty->print_cr("# SafepointSynchronize: Finished after " + INT64_FORMAT_W(6) " ms", + ((current_time - safepoint_limit_time) / MICROUNITS + + SafepointTimeoutDelay)); + } + } +#endif + + assert((_safepoint_counter & 0x1) == 0, "must be even"); + assert(Threads_lock->owned_by_self(), "must hold Threads_lock"); + _safepoint_counter ++; + + // Record state + _state = _synchronized; + + OrderAccess::fence(); + + if (TraceSafepoint) { + VM_Operation *op = VMThread::vm_operation(); + tty->print_cr("Entering safepoint region: %s", (op != NULL) ? op->name() : "no vm operation"); + } + + RuntimeService::record_safepoint_synchronized(); + if (PrintSafepointStatistics) { + update_statistics_on_sync_end(os::javaTimeNanos()); + } + + // Call stuff that needs to be run when a safepoint is just about to be completed + do_cleanup_tasks(); + } +} + +// Wake up all threads, so they are ready to resume execution after the safepoint +// operation has been carried out +void SafepointSynchronize::end() { + + assert(Threads_lock->owned_by_self(), "must hold Threads_lock"); + assert((_safepoint_counter & 0x1) == 1, "must be odd"); + _safepoint_counter ++; + // memory fence isn't required here since an odd _safepoint_counter + // value can do no harm and a fence is issued below anyway. + + DEBUG_ONLY(Thread* myThread = Thread::current();) + assert(myThread->is_VM_thread(), "Only VM thread can execute a safepoint"); + + if (PrintSafepointStatistics) { + end_statistics(os::javaTimeNanos()); + } + +#ifdef ASSERT + // A pending_exception cannot be installed during a safepoint. The threads + // may install an async exception after they come back from a safepoint into + // pending_exception after they unblock. But that should happen later. + for(JavaThread *cur = Threads::first(); cur; cur = cur->next()) { + assert (!(cur->has_pending_exception() && + cur->safepoint_state()->is_at_poll_safepoint()), + "safepoint installed a pending exception"); + } +#endif // ASSERT + + if (PageArmed) { + // Make polling safepoint aware + os::make_polling_page_readable(); + PageArmed = 0 ; + } + + // Remove safepoint check from interpreter + Interpreter::ignore_safepoints(); + + { + MutexLocker mu(Safepoint_lock); + + assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization"); + + // Set to not synchronized, so the threads will not go into the signal_thread_blocked method + // when they get restarted. + _state = _not_synchronized; + OrderAccess::fence(); + + if (TraceSafepoint) { + tty->print_cr("Leaving safepoint region"); + } + + // Start suspended threads + for(JavaThread *current = Threads::first(); current; current = current->next()) { + // A problem occuring on Solaris is when attempting to restart threads + // the first #cpus - 1 go well, but then the VMThread is preempted when we get + // to the next one (since it has been running the longest). We then have + // to wait for a cpu to become available before we can continue restarting + // threads. + // FIXME: This causes the performance of the VM to degrade when active and with + // large numbers of threads. Apparently this is due to the synchronous nature + // of suspending threads. + // + // TODO-FIXME: the comments above are vestigial and no longer apply. + // Furthermore, using solaris' schedctl in this particular context confers no benefit + if (VMThreadHintNoPreempt) { + os::hint_no_preempt(); + } + ThreadSafepointState* cur_state = current->safepoint_state(); + assert(cur_state->type() != ThreadSafepointState::_running, "Thread not suspended at safepoint"); + cur_state->restart(); + assert(cur_state->is_running(), "safepoint state has not been reset"); + } + + RuntimeService::record_safepoint_end(); + + // Release threads lock, so threads can be created/destroyed again. It will also starts all threads + // blocked in signal_thread_blocked + Threads_lock->unlock(); + + } +#ifndef SERIALGC + // If there are any concurrent GC threads resume them. + if (UseConcMarkSweepGC) { + ConcurrentMarkSweepThread::desynchronize(false); + } else { + ConcurrentGCThread::safepoint_desynchronize(); + } +#endif // SERIALGC +} + +bool SafepointSynchronize::is_cleanup_needed() { + // Need a safepoint if some inline cache buffers is non-empty + if (!InlineCacheBuffer::is_empty()) return true; + return false; +} + +jlong CounterDecay::_last_timestamp = 0; + +static void do_method(methodOop m) { + m->invocation_counter()->decay(); +} + +void CounterDecay::decay() { + _last_timestamp = os::javaTimeMillis(); + + // This operation is going to be performed only at the end of a safepoint + // and hence GC's will not be going on, all Java mutators are suspended + // at this point and hence SystemDictionary_lock is also not needed. + assert(SafepointSynchronize::is_at_safepoint(), "can only be executed at a safepoint"); + int nclasses = SystemDictionary::number_of_classes(); + double classes_per_tick = nclasses * (CounterDecayMinIntervalLength * 1e-3 / + CounterHalfLifeTime); + for (int i = 0; i < classes_per_tick; i++) { + klassOop k = SystemDictionary::try_get_next_class(); + if (k != NULL && k->klass_part()->oop_is_instance()) { + instanceKlass::cast(k)->methods_do(do_method); + } + } +} + +// Various cleaning tasks that should be done periodically at safepoints +void SafepointSynchronize::do_cleanup_tasks() { + jlong cleanup_time; + + // Update fat-monitor pool, since this is a safepoint. + if (TraceSafepoint) { + cleanup_time = os::javaTimeNanos(); + } + + ObjectSynchronizer::deflate_idle_monitors(); + InlineCacheBuffer::update_inline_caches(); + if(UseCounterDecay && CounterDecay::is_decay_needed()) { + CounterDecay::decay(); + } + NMethodSweeper::sweep(); + + if (TraceSafepoint) { + tty->print_cr("do_cleanup_tasks takes "INT64_FORMAT_W(6) "ms", + (os::javaTimeNanos() - cleanup_time) / MICROUNITS); + } +} + + +bool SafepointSynchronize::safepoint_safe(JavaThread *thread, JavaThreadState state) { + switch(state) { + case _thread_in_native: + // native threads are safe if they have no java stack or have walkable stack + return !thread->has_last_Java_frame() || thread->frame_anchor()->walkable(); + + // blocked threads should have already have walkable stack + case _thread_blocked: + assert(!thread->has_last_Java_frame() || thread->frame_anchor()->walkable(), "blocked and not walkable"); + return true; + + default: + return false; + } +} + + +// ------------------------------------------------------------------------------------------------------- +// Implementation of Safepoint callback point + +void SafepointSynchronize::block(JavaThread *thread) { + assert(thread != NULL, "thread must be set"); + assert(thread->is_Java_thread(), "not a Java thread"); + + // Threads shouldn't block if they are in the middle of printing, but... + ttyLocker::break_tty_lock_for_safepoint(os::current_thread_id()); + + // Only bail from the block() call if the thread is gone from the + // thread list; starting to exit should still block. + if (thread->is_terminated()) { + // block current thread if we come here from native code when VM is gone + thread->block_if_vm_exited(); + + // otherwise do nothing + return; + } + + JavaThreadState state = thread->thread_state(); + thread->frame_anchor()->make_walkable(thread); + + // Check that we have a valid thread_state at this point + switch(state) { + case _thread_in_vm_trans: + case _thread_in_Java: // From compiled code + + // We are highly likely to block on the Safepoint_lock. In order to avoid blocking in this case, + // we pretend we are still in the VM. + thread->set_thread_state(_thread_in_vm); + + if (is_synchronizing()) { + Atomic::inc (&TryingToBlock) ; + } + + // We will always be holding the Safepoint_lock when we are examine the state + // of a thread. Hence, the instructions between the Safepoint_lock->lock() and + // Safepoint_lock->unlock() are happening atomic with regards to the safepoint code + Safepoint_lock->lock_without_safepoint_check(); + if (is_synchronizing()) { + // Decrement the number of threads to wait for and signal vm thread + assert(_waiting_to_block > 0, "sanity check"); + _waiting_to_block--; + thread->safepoint_state()->set_has_called_back(true); + + // Consider (_waiting_to_block < 2) to pipeline the wakeup of the VM thread + if (_waiting_to_block == 0) { + Safepoint_lock->notify_all(); + } + } + + // We transition the thread to state _thread_blocked here, but + // we can't do our usual check for external suspension and then + // self-suspend after the lock_without_safepoint_check() call + // below because we are often called during transitions while + // we hold different locks. That would leave us suspended while + // holding a resource which results in deadlocks. + thread->set_thread_state(_thread_blocked); + Safepoint_lock->unlock(); + + // We now try to acquire the threads lock. Since this lock is hold by the VM thread during + // the entire safepoint, the threads will all line up here during the safepoint. + Threads_lock->lock_without_safepoint_check(); + // restore original state. This is important if the thread comes from compiled code, so it + // will continue to execute with the _thread_in_Java state. + thread->set_thread_state(state); + Threads_lock->unlock(); + break; + + case _thread_in_native_trans: + case _thread_blocked_trans: + case _thread_new_trans: + if (thread->safepoint_state()->type() == ThreadSafepointState::_call_back) { + thread->print_thread_state(); + fatal("Deadlock in safepoint code. " + "Should have called back to the VM before blocking."); + } + + // We transition the thread to state _thread_blocked here, but + // we can't do our usual check for external suspension and then + // self-suspend after the lock_without_safepoint_check() call + // below because we are often called during transitions while + // we hold different locks. That would leave us suspended while + // holding a resource which results in deadlocks. + thread->set_thread_state(_thread_blocked); + + // It is not safe to suspend a thread if we discover it is in _thread_in_native_trans. Hence, + // the safepoint code might still be waiting for it to block. We need to change the state here, + // so it can see that it is at a safepoint. + + // Block until the safepoint operation is completed. + Threads_lock->lock_without_safepoint_check(); + + // Restore state + thread->set_thread_state(state); + + Threads_lock->unlock(); + break; + + default: + fatal1("Illegal threadstate encountered: %d", state); + } + + // Check for pending. async. exceptions or suspends - except if the + // thread was blocked inside the VM. has_special_runtime_exit_condition() + // is called last since it grabs a lock and we only want to do that when + // we must. + // + // Note: we never deliver an async exception at a polling point as the + // compiler may not have an exception handler for it. The polling + // code will notice the async and deoptimize and the exception will + // be delivered. (Polling at a return point is ok though). Sure is + // a lot of bother for a deprecated feature... + // + // We don't deliver an async exception if the thread state is + // _thread_in_native_trans so JNI functions won't be called with + // a surprising pending exception. If the thread state is going back to java, + // async exception is checked in check_special_condition_for_native_trans(). + + if (state != _thread_blocked_trans && + state != _thread_in_vm_trans && + thread->has_special_runtime_exit_condition()) { + thread->handle_special_runtime_exit_condition( + !thread->is_at_poll_safepoint() && (state != _thread_in_native_trans)); + } +} + +// ------------------------------------------------------------------------------------------------------ +// Exception handlers + +#ifndef PRODUCT +#ifdef _LP64 +#define PTR_PAD "" +#else +#define PTR_PAD " " +#endif + +static void print_ptrs(intptr_t oldptr, intptr_t newptr, bool wasoop) { + bool is_oop = newptr ? ((oop)newptr)->is_oop() : false; + tty->print_cr(PTR_FORMAT PTR_PAD " %s %c " PTR_FORMAT PTR_PAD " %s %s", + oldptr, wasoop?"oop":" ", oldptr == newptr ? ' ' : '!', + newptr, is_oop?"oop":" ", (wasoop && !is_oop) ? "STALE" : ((wasoop==false&&is_oop==false&&oldptr !=newptr)?"STOMP":" ")); +} + +static void print_longs(jlong oldptr, jlong newptr, bool wasoop) { + bool is_oop = newptr ? ((oop)(intptr_t)newptr)->is_oop() : false; + tty->print_cr(PTR64_FORMAT " %s %c " PTR64_FORMAT " %s %s", + oldptr, wasoop?"oop":" ", oldptr == newptr ? ' ' : '!', + newptr, is_oop?"oop":" ", (wasoop && !is_oop) ? "STALE" : ((wasoop==false&&is_oop==false&&oldptr !=newptr)?"STOMP":" ")); +} + +#ifdef SPARC +static void print_me(intptr_t *new_sp, intptr_t *old_sp, bool *was_oops) { +#ifdef _LP64 + tty->print_cr("--------+------address-----+------before-----------+-------after----------+"); + const int incr = 1; // Increment to skip a long, in units of intptr_t +#else + tty->print_cr("--------+--address-+------before-----------+-------after----------+"); + const int incr = 2; // Increment to skip a long, in units of intptr_t +#endif + tty->print_cr("---SP---|"); + for( int i=0; i<16; i++ ) { + tty->print("blob %c%d |"PTR_FORMAT" ","LO"[i>>3],i&7,new_sp); print_ptrs(*old_sp++,*new_sp++,*was_oops++); } + tty->print_cr("--------|"); + for( int i1=0; i1print("argv pad|"PTR_FORMAT" ",new_sp); print_ptrs(*old_sp++,*new_sp++,*was_oops++); } + tty->print(" pad|"PTR_FORMAT" ",new_sp); print_ptrs(*old_sp++,*new_sp++,*was_oops++); + tty->print_cr("--------|"); + tty->print(" G1 |"PTR_FORMAT" ",new_sp); print_longs(*(jlong*)old_sp,*(jlong*)new_sp,was_oops[incr-1]); old_sp += incr; new_sp += incr; was_oops += incr; + tty->print(" G3 |"PTR_FORMAT" ",new_sp); print_longs(*(jlong*)old_sp,*(jlong*)new_sp,was_oops[incr-1]); old_sp += incr; new_sp += incr; was_oops += incr; + tty->print(" G4 |"PTR_FORMAT" ",new_sp); print_longs(*(jlong*)old_sp,*(jlong*)new_sp,was_oops[incr-1]); old_sp += incr; new_sp += incr; was_oops += incr; + tty->print(" G5 |"PTR_FORMAT" ",new_sp); print_longs(*(jlong*)old_sp,*(jlong*)new_sp,was_oops[incr-1]); old_sp += incr; new_sp += incr; was_oops += incr; + tty->print_cr(" FSR |"PTR_FORMAT" "PTR64_FORMAT" "PTR64_FORMAT,new_sp,*(jlong*)old_sp,*(jlong*)new_sp); + old_sp += incr; new_sp += incr; was_oops += incr; + // Skip the floats + tty->print_cr("--Float-|"PTR_FORMAT,new_sp); + tty->print_cr("---FP---|"); + old_sp += incr*32; new_sp += incr*32; was_oops += incr*32; + for( int i2=0; i2<16; i2++ ) { + tty->print("call %c%d |"PTR_FORMAT" ","LI"[i2>>3],i2&7,new_sp); print_ptrs(*old_sp++,*new_sp++,*was_oops++); } + tty->print_cr(""); +} +#endif // SPARC +#endif // PRODUCT + + +void SafepointSynchronize::handle_polling_page_exception(JavaThread *thread) { + assert(thread->is_Java_thread(), "polling reference encountered by VM thread"); + assert(thread->thread_state() == _thread_in_Java, "should come from Java code"); + assert(SafepointSynchronize::is_synchronizing(), "polling encountered outside safepoint synchronization"); + + // Uncomment this to get some serious before/after printing of the + // Sparc safepoint-blob frame structure. + /* + intptr_t* sp = thread->last_Java_sp(); + intptr_t stack_copy[150]; + for( int i=0; i<150; i++ ) stack_copy[i] = sp[i]; + bool was_oops[150]; + for( int i=0; i<150; i++ ) + was_oops[i] = stack_copy[i] ? ((oop)stack_copy[i])->is_oop() : false; + */ + + if (ShowSafepointMsgs) { + tty->print("handle_polling_page_exception: "); + } + + if (PrintSafepointStatistics) { + inc_page_trap_count(); + } + + ThreadSafepointState* state = thread->safepoint_state(); + + state->handle_polling_page_exception(); + // print_me(sp,stack_copy,was_oops); +} + + +void SafepointSynchronize::print_safepoint_timeout(SafepointTimeoutReason reason) { + if (!timeout_error_printed) { + timeout_error_printed = true; + // Print out the thread infor which didn't reach the safepoint for debugging + // purposes (useful when there are lots of threads in the debugger). + tty->print_cr(""); + tty->print_cr("# SafepointSynchronize::begin: Timeout detected:"); + if (reason == _spinning_timeout) { + tty->print_cr("# SafepointSynchronize::begin: Timed out while spinning to reach a safepoint."); + } else if (reason == _blocking_timeout) { + tty->print_cr("# SafepointSynchronize::begin: Timed out while waiting for threads to stop."); + } + + tty->print_cr("# SafepointSynchronize::begin: Threads which did not reach the safepoint:"); + ThreadSafepointState *cur_state; + ResourceMark rm; + for(JavaThread *cur_thread = Threads::first(); cur_thread; + cur_thread = cur_thread->next()) { + cur_state = cur_thread->safepoint_state(); + + if (cur_thread->thread_state() != _thread_blocked && + ((reason == _spinning_timeout && cur_state->is_running()) || + (reason == _blocking_timeout && !cur_state->has_called_back()))) { + tty->print("# "); + cur_thread->print(); + tty->print_cr(""); + } + } + tty->print_cr("# SafepointSynchronize::begin: (End of list)"); + } + + // To debug the long safepoint, specify both DieOnSafepointTimeout & + // ShowMessageBoxOnError. + if (DieOnSafepointTimeout) { + char msg[1024]; + VM_Operation *op = VMThread::vm_operation(); + sprintf(msg, "Safepoint sync time longer than %d ms detected when executing %s.", + SafepointTimeoutDelay, + op != NULL ? op->name() : "no vm operation"); + fatal(msg); + } +} + + +// ------------------------------------------------------------------------------------------------------- +// Implementation of ThreadSafepointState + +ThreadSafepointState::ThreadSafepointState(JavaThread *thread) { + _thread = thread; + _type = _running; + _has_called_back = false; + _at_poll_safepoint = false; +} + +void ThreadSafepointState::create(JavaThread *thread) { + ThreadSafepointState *state = new ThreadSafepointState(thread); + thread->set_safepoint_state(state); +} + +void ThreadSafepointState::destroy(JavaThread *thread) { + if (thread->safepoint_state()) { + delete(thread->safepoint_state()); + thread->set_safepoint_state(NULL); + } +} + +void ThreadSafepointState::examine_state_of_thread() { + assert(is_running(), "better be running or just have hit safepoint poll"); + + JavaThreadState state = _thread->thread_state(); + + // Check for a thread that is suspended. Note that thread resume tries + // to grab the Threads_lock which we own here, so a thread cannot be + // resumed during safepoint synchronization. + + // We check with locking because another thread that has not yet + // synchronized may be trying to suspend this one. + bool is_suspended = _thread->is_any_suspended_with_lock(); + if (is_suspended) { + roll_forward(_at_safepoint); + return; + } + + // Some JavaThread states have an initial safepoint state of + // running, but are actually at a safepoint. We will happily + // agree and update the safepoint state here. + if (SafepointSynchronize::safepoint_safe(_thread, state)) { + roll_forward(_at_safepoint); + return; + } + + if (state == _thread_in_vm) { + roll_forward(_call_back); + return; + } + + // All other thread states will continue to run until they + // transition and self-block in state _blocked + // Safepoint polling in compiled code causes the Java threads to do the same. + // Note: new threads may require a malloc so they must be allowed to finish + + assert(is_running(), "examine_state_of_thread on non-running thread"); + return; +} + +// Returns true is thread could not be rolled forward at present position. +void ThreadSafepointState::roll_forward(suspend_type type) { + _type = type; + + switch(_type) { + case _at_safepoint: + SafepointSynchronize::signal_thread_at_safepoint(); + break; + + case _call_back: + set_has_called_back(false); + break; + + case _running: + default: + ShouldNotReachHere(); + } +} + +void ThreadSafepointState::restart() { + switch(type()) { + case _at_safepoint: + case _call_back: + break; + + case _running: + default: + tty->print_cr("restart thread "INTPTR_FORMAT" with state %d", + _thread, _type); + _thread->print(); + ShouldNotReachHere(); + } + _type = _running; + set_has_called_back(false); +} + + +void ThreadSafepointState::print_on(outputStream *st) const { + const char *s; + + switch(_type) { + case _running : s = "_running"; break; + case _at_safepoint : s = "_at_safepoint"; break; + case _call_back : s = "_call_back"; break; + default: + ShouldNotReachHere(); + } + + st->print_cr("Thread: " INTPTR_FORMAT + " [0x%2x] State: %s _has_called_back %d _at_poll_safepoint %d", + _thread, _thread->osthread()->thread_id(), s, _has_called_back, + _at_poll_safepoint); + + _thread->print_thread_state_on(st); +} + + +// --------------------------------------------------------------------------------------------------------------------- + +// Block the thread at the safepoint poll or poll return. +void ThreadSafepointState::handle_polling_page_exception() { + + // Check state. block() will set thread state to thread_in_vm which will + // cause the safepoint state _type to become _call_back. + assert(type() == ThreadSafepointState::_running, + "polling page exception on thread not running state"); + + // Step 1: Find the nmethod from the return address + if (ShowSafepointMsgs && Verbose) { + tty->print_cr("Polling page exception at " INTPTR_FORMAT, thread()->saved_exception_pc()); + } + address real_return_addr = thread()->saved_exception_pc(); + + CodeBlob *cb = CodeCache::find_blob(real_return_addr); + assert(cb != NULL && cb->is_nmethod(), "return address should be in nmethod"); + nmethod* nm = (nmethod*)cb; + + // Find frame of caller + frame stub_fr = thread()->last_frame(); + CodeBlob* stub_cb = stub_fr.cb(); + assert(stub_cb->is_safepoint_stub(), "must be a safepoint stub"); + RegisterMap map(thread(), true); + frame caller_fr = stub_fr.sender(&map); + + // Should only be poll_return or poll + assert( nm->is_at_poll_or_poll_return(real_return_addr), "should not be at call" ); + + // This is a poll immediately before a return. The exception handling code + // has already had the effect of causing the return to occur, so the execution + // will continue immediately after the call. In addition, the oopmap at the + // return point does not mark the return value as an oop (if it is), so + // it needs a handle here to be updated. + if( nm->is_at_poll_return(real_return_addr) ) { + // See if return type is an oop. + bool return_oop = nm->method()->is_returning_oop(); + Handle return_value; + if (return_oop) { + // The oop result has been saved on the stack together with all + // the other registers. In order to preserve it over GCs we need + // to keep it in a handle. + oop result = caller_fr.saved_oop_result(&map); + assert(result == NULL || result->is_oop(), "must be oop"); + return_value = Handle(thread(), result); + assert(Universe::heap()->is_in_or_null(result), "must be heap pointer"); + } + + // Block the thread + SafepointSynchronize::block(thread()); + + // restore oop result, if any + if (return_oop) { + caller_fr.set_saved_oop_result(&map, return_value()); + } + } + + // This is a safepoint poll. Verify the return address and block. + else { + set_at_poll_safepoint(true); + + // verify the blob built the "return address" correctly + assert(real_return_addr == caller_fr.pc(), "must match"); + + // Block the thread + SafepointSynchronize::block(thread()); + set_at_poll_safepoint(false); + + // If we have a pending async exception deoptimize the frame + // as otherwise we may never deliver it. + if (thread()->has_async_condition()) { + ThreadInVMfromJavaNoAsyncException __tiv(thread()); + VM_DeoptimizeFrame deopt(thread(), caller_fr.id()); + VMThread::execute(&deopt); + } + + // If an exception has been installed we must check for a pending deoptimization + // Deoptimize frame if exception has been thrown. + + if (thread()->has_pending_exception() ) { + RegisterMap map(thread(), true); + frame caller_fr = stub_fr.sender(&map); + if (caller_fr.is_deoptimized_frame()) { + // The exception patch will destroy registers that are still + // live and will be needed during deoptimization. Defer the + // Async exception should have defered the exception until the + // next safepoint which will be detected when we get into + // the interpreter so if we have an exception now things + // are messed up. + + fatal("Exception installed and deoptimization is pending"); + } + } + } +} + + +// +// Statistics & Instrumentations +// +SafepointSynchronize::SafepointStats* SafepointSynchronize::_safepoint_stats = NULL; +int SafepointSynchronize::_cur_stat_index = 0; +julong SafepointSynchronize::_safepoint_reasons[VM_Operation::VMOp_Terminating]; +julong SafepointSynchronize::_coalesced_vmop_count = 0; +jlong SafepointSynchronize::_max_sync_time = 0; + +// last_safepoint_start_time records the start time of last safepoint. +static jlong last_safepoint_start_time = 0; +static jlong sync_end_time = 0; +static bool need_to_track_page_armed_status = false; +static bool init_done = false; + +void SafepointSynchronize::deferred_initialize_stat() { + if (init_done) return; + + if (PrintSafepointStatisticsCount <= 0) { + fatal("Wrong PrintSafepointStatisticsCount"); + } + + // If PrintSafepointStatisticsTimeout is specified, the statistics data will + // be printed right away, in which case, _safepoint_stats will regress to + // a single element array. Otherwise, it is a circular ring buffer with default + // size of PrintSafepointStatisticsCount. + int stats_array_size; + if (PrintSafepointStatisticsTimeout > 0) { + stats_array_size = 1; + PrintSafepointStatistics = true; + } else { + stats_array_size = PrintSafepointStatisticsCount; + } + _safepoint_stats = (SafepointStats*)os::malloc(stats_array_size + * sizeof(SafepointStats)); + guarantee(_safepoint_stats != NULL, + "not enough memory for safepoint instrumentation data"); + + if (UseCompilerSafepoints && DeferPollingPageLoopCount >= 0) { + need_to_track_page_armed_status = true; + } + + tty->print(" vmop_name " + "[threads: total initially_running wait_to_block] "); + tty->print("[time: spin block sync] " + "[vmop_time time_elapsed] "); + + // no page armed status printed out if it is always armed. + if (need_to_track_page_armed_status) { + tty->print("page_armed "); + } + + tty->print_cr("page_trap_count"); + + init_done = true; +} + +void SafepointSynchronize::begin_statistics(int nof_threads, int nof_running) { + deferred_initialize_stat(); + + SafepointStats *spstat = &_safepoint_stats[_cur_stat_index]; + + VM_Operation *op = VMThread::vm_operation(); + spstat->_vmop_type = (op != NULL ? op->type() : -1); + if (op != NULL) { + _safepoint_reasons[spstat->_vmop_type]++; + } + + spstat->_nof_total_threads = nof_threads; + spstat->_nof_initial_running_threads = nof_running; + spstat->_nof_threads_hit_page_trap = 0; + + // Records the start time of spinning. The real time spent on spinning + // will be adjusted when spin is done. Same trick is applied for time + // spent on waiting for threads to block. + if (nof_running != 0) { + spstat->_time_to_spin = os::javaTimeNanos(); + } else { + spstat->_time_to_spin = 0; + } + + if (last_safepoint_start_time == 0) { + spstat->_time_elapsed_since_last_safepoint = 0; + } else { + spstat->_time_elapsed_since_last_safepoint = _last_safepoint - + last_safepoint_start_time; + } + last_safepoint_start_time = _last_safepoint; +} + +void SafepointSynchronize::update_statistics_on_spin_end() { + SafepointStats *spstat = &_safepoint_stats[_cur_stat_index]; + + jlong cur_time = os::javaTimeNanos(); + + spstat->_nof_threads_wait_to_block = _waiting_to_block; + if (spstat->_nof_initial_running_threads != 0) { + spstat->_time_to_spin = cur_time - spstat->_time_to_spin; + } + + if (need_to_track_page_armed_status) { + spstat->_page_armed = (PageArmed == 1); + } + + // Records the start time of waiting for to block. Updated when block is done. + if (_waiting_to_block != 0) { + spstat->_time_to_wait_to_block = cur_time; + } else { + spstat->_time_to_wait_to_block = 0; + } +} + +void SafepointSynchronize::update_statistics_on_sync_end(jlong end_time) { + SafepointStats *spstat = &_safepoint_stats[_cur_stat_index]; + + if (spstat->_nof_threads_wait_to_block != 0) { + spstat->_time_to_wait_to_block = end_time - + spstat->_time_to_wait_to_block; + } + + // Records the end time of sync which will be used to calculate the total + // vm operation time. Again, the real time spending in syncing will be deducted + // from the start of the sync time later when end_statistics is called. + spstat->_time_to_sync = end_time - _last_safepoint; + if (spstat->_time_to_sync > _max_sync_time) { + _max_sync_time = spstat->_time_to_sync; + } + sync_end_time = end_time; +} + +void SafepointSynchronize::end_statistics(jlong vmop_end_time) { + SafepointStats *spstat = &_safepoint_stats[_cur_stat_index]; + + // Update the vm operation time. + spstat->_time_to_exec_vmop = vmop_end_time - sync_end_time; + // Only the sync time longer than the specified + // PrintSafepointStatisticsTimeout will be printed out right away. + // By default, it is -1 meaning all samples will be put into the list. + if ( PrintSafepointStatisticsTimeout > 0) { + if (spstat->_time_to_sync > PrintSafepointStatisticsTimeout * MICROUNITS) { + print_statistics(); + } + } else { + // The safepoint statistics will be printed out when the _safepoin_stats + // array fills up. + if (_cur_stat_index != PrintSafepointStatisticsCount - 1) { + _cur_stat_index ++; + } else { + print_statistics(); + _cur_stat_index = 0; + tty->print_cr(""); + } + } +} + +void SafepointSynchronize::print_statistics() { + int index; + SafepointStats* sstats = _safepoint_stats; + + for (index = 0; index <= _cur_stat_index; index++) { + sstats = &_safepoint_stats[index]; + tty->print("%-28s [" + INT32_FORMAT_W(8)INT32_FORMAT_W(11)INT32_FORMAT_W(15) + "] ", + sstats->_vmop_type == -1 ? "no vm operation" : + VM_Operation::name(sstats->_vmop_type), + sstats->_nof_total_threads, + sstats->_nof_initial_running_threads, + sstats->_nof_threads_wait_to_block); + // "/ MICROUNITS " is to convert the unit from nanos to millis. + tty->print(" [" + INT64_FORMAT_W(6)INT64_FORMAT_W(6)INT64_FORMAT_W(6) + "] " + "["INT64_FORMAT_W(6)INT64_FORMAT_W(9) "] ", + sstats->_time_to_spin / MICROUNITS, + sstats->_time_to_wait_to_block / MICROUNITS, + sstats->_time_to_sync / MICROUNITS, + sstats->_time_to_exec_vmop / MICROUNITS, + sstats->_time_elapsed_since_last_safepoint / MICROUNITS); + + if (need_to_track_page_armed_status) { + tty->print(INT32_FORMAT" ", sstats->_page_armed); + } + tty->print_cr(INT32_FORMAT" ", sstats->_nof_threads_hit_page_trap); + } +} + +// This method will be called when VM exits. It will first call +// print_statistics to print out the rest of the sampling. Then +// it tries to summarize the sampling. +void SafepointSynchronize::print_stat_on_exit() { + if (_safepoint_stats == NULL) return; + + SafepointStats *spstat = &_safepoint_stats[_cur_stat_index]; + + // During VM exit, end_statistics may not get called and in that + // case, if the sync time is less than PrintSafepointStatisticsTimeout, + // don't print it out. + // Approximate the vm op time. + _safepoint_stats[_cur_stat_index]._time_to_exec_vmop = + os::javaTimeNanos() - sync_end_time; + + if ( PrintSafepointStatisticsTimeout < 0 || + spstat->_time_to_sync > PrintSafepointStatisticsTimeout * MICROUNITS) { + print_statistics(); + } + tty->print_cr(""); + + // Print out polling page sampling status. + if (!need_to_track_page_armed_status) { + if (UseCompilerSafepoints) { + tty->print_cr("Polling page always armed"); + } + } else { + tty->print_cr("Defer polling page loop count = %d\n", + DeferPollingPageLoopCount); + } + + for (int index = 0; index < VM_Operation::VMOp_Terminating; index++) { + if (_safepoint_reasons[index] != 0) { + tty->print_cr("%-26s"UINT64_FORMAT_W(10), VM_Operation::name(index), + _safepoint_reasons[index]); + } + } + + tty->print_cr(UINT64_FORMAT_W(5)" VM operations coalesced during safepoint", + _coalesced_vmop_count); + tty->print_cr("Maximum sync time "INT64_FORMAT_W(5)" ms", + _max_sync_time / MICROUNITS); +} + +// ------------------------------------------------------------------------------------------------ +// Non-product code + +#ifndef PRODUCT + +void SafepointSynchronize::print_state() { + if (_state == _not_synchronized) { + tty->print_cr("not synchronized"); + } else if (_state == _synchronizing || _state == _synchronized) { + tty->print_cr("State: %s", (_state == _synchronizing) ? "synchronizing" : + "synchronized"); + + for(JavaThread *cur = Threads::first(); cur; cur = cur->next()) { + cur->safepoint_state()->print(); + } + } +} + +void SafepointSynchronize::safepoint_msg(const char* format, ...) { + if (ShowSafepointMsgs) { + va_list ap; + va_start(ap, format); + tty->vprint_cr(format, ap); + va_end(ap); + } +} + +#endif // !PRODUCT diff --git a/hotspot/src/share/vm/runtime/safepoint.hpp b/hotspot/src/share/vm/runtime/safepoint.hpp new file mode 100644 index 00000000000..86b16e7e0d7 --- /dev/null +++ b/hotspot/src/share/vm/runtime/safepoint.hpp @@ -0,0 +1,234 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Safepoint synchronization +//// +// The VMThread or CMS_thread uses the SafepointSynchronize::begin/end +// methods to enter/exit a safepoint region. The begin method will roll +// all JavaThreads forward to a safepoint. +// +// JavaThreads must use the ThreadSafepointState abstraction (defined in +// thread.hpp) to indicate that that they are at a safepoint. +// +// The Mutex/Condition variable and ObjectLocker classes calls the enter/ +// exit safepoint methods, when a thread is blocked/restarted. Hence, all mutex exter/ +// exit points *must* be at a safepoint. + + +class ThreadSafepointState; +class SnippetCache; +class nmethod; + +// +// Implements roll-forward to safepoint (safepoint synchronization) +// +class SafepointSynchronize : AllStatic { + public: + enum SynchronizeState { + _not_synchronized = 0, // Threads not synchronized at a safepoint + // Keep this value 0. See the coment in do_call_back() + _synchronizing = 1, // Synchronizing in progress + _synchronized = 2 // All Java threads are stopped at a safepoint. Only VM thread is running + }; + + enum SafepointingThread { + _null_thread = 0, + _vm_thread = 1, + _other_thread = 2 + }; + + enum SafepointTimeoutReason { + _spinning_timeout = 0, + _blocking_timeout = 1 + }; + + typedef struct { + int _vmop_type; // type of VM operation triggers the safepoint + int _nof_total_threads; // total number of Java threads + int _nof_initial_running_threads; // total number of initially seen running threads + int _nof_threads_wait_to_block; // total number of threads waiting for to block + bool _page_armed; // true if polling page is armed, false otherwise + int _nof_threads_hit_page_trap; // total number of threads hitting the page trap + jlong _time_to_spin; // total time in millis spent in spinning + jlong _time_to_wait_to_block; // total time in millis spent in waiting for to block + jlong _time_to_sync; // total time in millis spent in getting to _synchronized + jlong _time_to_exec_vmop; // total time in millis spent in vm operation itself + jlong _time_elapsed_since_last_safepoint; // time elasped since last safepoint + } SafepointStats; + + private: + static volatile SynchronizeState _state; // Threads might read this flag directly, without acquireing the Threads_lock + static volatile int _waiting_to_block; // No. of threads we are waiting for to block. + + // This counter is used for fast versions of jni_GetField. + // An even value means there is no ongoing safepoint operations. + // The counter is incremented ONLY at the beginning and end of each + // safepoint. The fact that Threads_lock is held throughout each pair of + // increments (at the beginning and end of each safepoint) guarantees + // race freedom. +public: + static volatile int _safepoint_counter; +private: + + static jlong _last_safepoint; // Time of last safepoint + + // statistics + static SafepointStats* _safepoint_stats; // array of SafepointStats struct + static int _cur_stat_index; // current index to the above array + static julong _safepoint_reasons[]; // safepoint count for each VM op + static julong _coalesced_vmop_count;// coalesced vmop count + static jlong _max_sync_time; // maximum sync time in nanos + + static void begin_statistics(int nof_threads, int nof_running); + static void update_statistics_on_spin_end(); + static void update_statistics_on_sync_end(jlong end_time); + static void end_statistics(jlong end_time); + static void print_statistics(); + inline static void inc_page_trap_count() { + Atomic::inc(&_safepoint_stats[_cur_stat_index]._nof_threads_hit_page_trap); + } + + // For debug long safepoint + static void print_safepoint_timeout(SafepointTimeoutReason timeout_reason); + +public: + + // Main entry points + + // Roll all threads forward to safepoint. Must be called by the + // VMThread or CMS_thread. + static void begin(); + static void end(); // Start all suspended threads again... + + static bool safepoint_safe(JavaThread *thread, JavaThreadState state); + + // Query + inline static bool is_at_safepoint() { return _state == _synchronized; } + inline static bool is_synchronizing() { return _state == _synchronizing; } + + inline static bool do_call_back() { + return (_state != _not_synchronized); + } + + // Called when a thread volantary blocks + static void block(JavaThread *thread); + static void signal_thread_at_safepoint() { _waiting_to_block--; } + + // Exception handling for page polling + static void handle_polling_page_exception(JavaThread *thread); + + // VM Thread interface for determining safepoint rate + static long last_non_safepoint_interval() { return os::javaTimeMillis() - _last_safepoint; } + static bool is_cleanup_needed(); + static void do_cleanup_tasks(); + + // debugging + static void print_state() PRODUCT_RETURN; + static void safepoint_msg(const char* format, ...) PRODUCT_RETURN; + + static void deferred_initialize_stat(); + static void print_stat_on_exit(); + inline static void inc_vmop_coalesced_count() { _coalesced_vmop_count++; } + + static void set_is_at_safepoint() { _state = _synchronized; } + static void set_is_not_at_safepoint() { _state = _not_synchronized; } + + // assembly support + static address address_of_state() { return (address)&_state; } + + static address safepoint_counter_addr() { return (address)&_safepoint_counter; } +}; + +// State class for a thread suspended at a safepoint +class ThreadSafepointState: public CHeapObj { + public: + // These states are maintained by VM thread while threads are being brought + // to a safepoint. After SafepointSynchronize::end(), they are reset to + // _running. + enum suspend_type { + _running = 0, // Thread state not yet determined (i.e., not at a safepoint yet) + _at_safepoint = 1, // Thread at a safepoint (f.ex., when blocked on a lock) + _call_back = 2 // Keep executing and wait for callback (if thread is in interpreted or vm) + }; + private: + volatile bool _at_poll_safepoint; // At polling page safepoint (NOT a poll return safepoint) + // Thread has called back the safepoint code (for debugging) + bool _has_called_back; + + JavaThread * _thread; + volatile suspend_type _type; + + + public: + ThreadSafepointState(JavaThread *thread); + + // examine/roll-forward/restart + void examine_state_of_thread(); + void roll_forward(suspend_type type); + void restart(); + + // Query + JavaThread* thread() const { return _thread; } + suspend_type type() const { return _type; } + bool is_running() const { return (_type==_running); } + + // Support for safepoint timeout (debugging) + bool has_called_back() const { return _has_called_back; } + void set_has_called_back(bool val) { _has_called_back = val; } + bool is_at_poll_safepoint() { return _at_poll_safepoint; } + void set_at_poll_safepoint(bool val) { _at_poll_safepoint = val; } + + void handle_polling_page_exception(); + + // debugging + void print_on(outputStream* st) const; + void print() const { print_on(tty); } + + // Initialize + static void create(JavaThread *thread); + static void destroy(JavaThread *thread); + + void safepoint_msg(const char* format, ...) { + if (ShowSafepointMsgs) { + va_list ap; + va_start(ap, format); + tty->vprint_cr(format, ap); + va_end(ap); + } + } +}; + +// +// CounterDecay +// +// Interates through invocation counters and decrements them. This +// is done at each safepoint. +// +class CounterDecay : public AllStatic { + static jlong _last_timestamp; + public: + static void decay(); + static bool is_decay_needed() { return (os::javaTimeMillis() - _last_timestamp) > CounterDecayMinIntervalLength; } +}; diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.cpp b/hotspot/src/share/vm/runtime/sharedRuntime.cpp new file mode 100644 index 00000000000..e9a5685889d --- /dev/null +++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp @@ -0,0 +1,2181 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_sharedRuntime.cpp.incl" +#include + +HS_DTRACE_PROBE_DECL4(hotspot, object__alloc, Thread*, char*, int, size_t); +HS_DTRACE_PROBE_DECL7(hotspot, method__entry, int, + char*, int, char*, int, char*, int); +HS_DTRACE_PROBE_DECL7(hotspot, method__return, int, + char*, int, char*, int, char*, int); + +// Implementation of SharedRuntime + +#ifndef PRODUCT +// For statistics +int SharedRuntime::_ic_miss_ctr = 0; +int SharedRuntime::_wrong_method_ctr = 0; +int SharedRuntime::_resolve_static_ctr = 0; +int SharedRuntime::_resolve_virtual_ctr = 0; +int SharedRuntime::_resolve_opt_virtual_ctr = 0; +int SharedRuntime::_implicit_null_throws = 0; +int SharedRuntime::_implicit_div0_throws = 0; +int SharedRuntime::_throw_null_ctr = 0; + +int SharedRuntime::_nof_normal_calls = 0; +int SharedRuntime::_nof_optimized_calls = 0; +int SharedRuntime::_nof_inlined_calls = 0; +int SharedRuntime::_nof_megamorphic_calls = 0; +int SharedRuntime::_nof_static_calls = 0; +int SharedRuntime::_nof_inlined_static_calls = 0; +int SharedRuntime::_nof_interface_calls = 0; +int SharedRuntime::_nof_optimized_interface_calls = 0; +int SharedRuntime::_nof_inlined_interface_calls = 0; +int SharedRuntime::_nof_megamorphic_interface_calls = 0; +int SharedRuntime::_nof_removable_exceptions = 0; + +int SharedRuntime::_new_instance_ctr=0; +int SharedRuntime::_new_array_ctr=0; +int SharedRuntime::_multi1_ctr=0; +int SharedRuntime::_multi2_ctr=0; +int SharedRuntime::_multi3_ctr=0; +int SharedRuntime::_multi4_ctr=0; +int SharedRuntime::_multi5_ctr=0; +int SharedRuntime::_mon_enter_stub_ctr=0; +int SharedRuntime::_mon_exit_stub_ctr=0; +int SharedRuntime::_mon_enter_ctr=0; +int SharedRuntime::_mon_exit_ctr=0; +int SharedRuntime::_partial_subtype_ctr=0; +int SharedRuntime::_jbyte_array_copy_ctr=0; +int SharedRuntime::_jshort_array_copy_ctr=0; +int SharedRuntime::_jint_array_copy_ctr=0; +int SharedRuntime::_jlong_array_copy_ctr=0; +int SharedRuntime::_oop_array_copy_ctr=0; +int SharedRuntime::_checkcast_array_copy_ctr=0; +int SharedRuntime::_unsafe_array_copy_ctr=0; +int SharedRuntime::_generic_array_copy_ctr=0; +int SharedRuntime::_slow_array_copy_ctr=0; +int SharedRuntime::_find_handler_ctr=0; +int SharedRuntime::_rethrow_ctr=0; + +int SharedRuntime::_ICmiss_index = 0; +int SharedRuntime::_ICmiss_count[SharedRuntime::maxICmiss_count]; +address SharedRuntime::_ICmiss_at[SharedRuntime::maxICmiss_count]; + +void SharedRuntime::trace_ic_miss(address at) { + for (int i = 0; i < _ICmiss_index; i++) { + if (_ICmiss_at[i] == at) { + _ICmiss_count[i]++; + return; + } + } + int index = _ICmiss_index++; + if (_ICmiss_index >= maxICmiss_count) _ICmiss_index = maxICmiss_count - 1; + _ICmiss_at[index] = at; + _ICmiss_count[index] = 1; +} + +void SharedRuntime::print_ic_miss_histogram() { + if (ICMissHistogram) { + tty->print_cr ("IC Miss Histogram:"); + int tot_misses = 0; + for (int i = 0; i < _ICmiss_index; i++) { + tty->print_cr(" at: " INTPTR_FORMAT " nof: %d", _ICmiss_at[i], _ICmiss_count[i]); + tot_misses += _ICmiss_count[i]; + } + tty->print_cr ("Total IC misses: %7d", tot_misses); + } +} +#endif // PRODUCT + + +JRT_LEAF(jlong, SharedRuntime::lmul(jlong y, jlong x)) + return x * y; +JRT_END + + +JRT_LEAF(jlong, SharedRuntime::ldiv(jlong y, jlong x)) + if (x == min_jlong && y == CONST64(-1)) { + return x; + } else { + return x / y; + } +JRT_END + + +JRT_LEAF(jlong, SharedRuntime::lrem(jlong y, jlong x)) + if (x == min_jlong && y == CONST64(-1)) { + return 0; + } else { + return x % y; + } +JRT_END + + +const juint float_sign_mask = 0x7FFFFFFF; +const juint float_infinity = 0x7F800000; +const julong double_sign_mask = CONST64(0x7FFFFFFFFFFFFFFF); +const julong double_infinity = CONST64(0x7FF0000000000000); + +JRT_LEAF(jfloat, SharedRuntime::frem(jfloat x, jfloat y)) +#ifdef _WIN64 + // 64-bit Windows on amd64 returns the wrong values for + // infinity operands. + union { jfloat f; juint i; } xbits, ybits; + xbits.f = x; + ybits.f = y; + // x Mod Infinity == x unless x is infinity + if ( ((xbits.i & float_sign_mask) != float_infinity) && + ((ybits.i & float_sign_mask) == float_infinity) ) { + return x; + } +#endif + return ((jfloat)fmod((double)x,(double)y)); +JRT_END + + +JRT_LEAF(jdouble, SharedRuntime::drem(jdouble x, jdouble y)) +#ifdef _WIN64 + union { jdouble d; julong l; } xbits, ybits; + xbits.d = x; + ybits.d = y; + // x Mod Infinity == x unless x is infinity + if ( ((xbits.l & double_sign_mask) != double_infinity) && + ((ybits.l & double_sign_mask) == double_infinity) ) { + return x; + } +#endif + return ((jdouble)fmod((double)x,(double)y)); +JRT_END + + +JRT_LEAF(jint, SharedRuntime::f2i(jfloat x)) + if (g_isnan(x)) {return 0;} + jlong lltmp = (jlong)x; + jint ltmp = (jint)lltmp; + if (ltmp == lltmp) { + return ltmp; + } else { + if (x < 0) { + return min_jint; + } else { + return max_jint; + } + } +JRT_END + + +JRT_LEAF(jlong, SharedRuntime::f2l(jfloat x)) + if (g_isnan(x)) {return 0;} + jlong lltmp = (jlong)x; + if (lltmp != min_jlong) { + return lltmp; + } else { + if (x < 0) { + return min_jlong; + } else { + return max_jlong; + } + } +JRT_END + + +JRT_LEAF(jint, SharedRuntime::d2i(jdouble x)) + if (g_isnan(x)) {return 0;} + jlong lltmp = (jlong)x; + jint ltmp = (jint)lltmp; + if (ltmp == lltmp) { + return ltmp; + } else { + if (x < 0) { + return min_jint; + } else { + return max_jint; + } + } +JRT_END + + +JRT_LEAF(jlong, SharedRuntime::d2l(jdouble x)) + if (g_isnan(x)) {return 0;} + jlong lltmp = (jlong)x; + if (lltmp != min_jlong) { + return lltmp; + } else { + if (x < 0) { + return min_jlong; + } else { + return max_jlong; + } + } +JRT_END + + +JRT_LEAF(jfloat, SharedRuntime::d2f(jdouble x)) + return (jfloat)x; +JRT_END + + +JRT_LEAF(jfloat, SharedRuntime::l2f(jlong x)) + return (jfloat)x; +JRT_END + + +JRT_LEAF(jdouble, SharedRuntime::l2d(jlong x)) + return (jdouble)x; +JRT_END + +// Exception handling accross interpreter/compiler boundaries +// +// exception_handler_for_return_address(...) returns the continuation address. +// The continuation address is the entry point of the exception handler of the +// previous frame depending on the return address. + +address SharedRuntime::raw_exception_handler_for_return_address(address return_address) { + assert(frame::verify_return_pc(return_address), "must be a return pc"); + + // the fastest case first + CodeBlob* blob = CodeCache::find_blob(return_address); + if (blob != NULL && blob->is_nmethod()) { + nmethod* code = (nmethod*)blob; + assert(code != NULL, "nmethod must be present"); + // native nmethods don't have exception handlers + assert(!code->is_native_method(), "no exception handler"); + assert(code->header_begin() != code->exception_begin(), "no exception handler"); + if (code->is_deopt_pc(return_address)) { + return SharedRuntime::deopt_blob()->unpack_with_exception(); + } else { + return code->exception_begin(); + } + } + + // Entry code + if (StubRoutines::returns_to_call_stub(return_address)) { + return StubRoutines::catch_exception_entry(); + } + // Interpreted code + if (Interpreter::contains(return_address)) { + return Interpreter::rethrow_exception_entry(); + } + + // Compiled code + if (CodeCache::contains(return_address)) { + CodeBlob* blob = CodeCache::find_blob(return_address); + if (blob->is_nmethod()) { + nmethod* code = (nmethod*)blob; + assert(code != NULL, "nmethod must be present"); + assert(code->header_begin() != code->exception_begin(), "no exception handler"); + return code->exception_begin(); + } + if (blob->is_runtime_stub()) { + ShouldNotReachHere(); // callers are responsible for skipping runtime stub frames + } + } + guarantee(!VtableStubs::contains(return_address), "NULL exceptions in vtables should have been handled already!"); +#ifndef PRODUCT + { ResourceMark rm; + tty->print_cr("No exception handler found for exception at " INTPTR_FORMAT " - potential problems:", return_address); + tty->print_cr("a) exception happened in (new?) code stubs/buffers that is not handled here"); + tty->print_cr("b) other problem"); + } +#endif // PRODUCT + ShouldNotReachHere(); + return NULL; +} + + +JRT_LEAF(address, SharedRuntime::exception_handler_for_return_address(address return_address)) + return raw_exception_handler_for_return_address(return_address); +JRT_END + +address SharedRuntime::get_poll_stub(address pc) { + address stub; + // Look up the code blob + CodeBlob *cb = CodeCache::find_blob(pc); + + // Should be an nmethod + assert( cb && cb->is_nmethod(), "safepoint polling: pc must refer to an nmethod" ); + + // Look up the relocation information + assert( ((nmethod*)cb)->is_at_poll_or_poll_return(pc), + "safepoint polling: type must be poll" ); + + assert( ((NativeInstruction*)pc)->is_safepoint_poll(), + "Only polling locations are used for safepoint"); + + bool at_poll_return = ((nmethod*)cb)->is_at_poll_return(pc); + if (at_poll_return) { + assert(SharedRuntime::polling_page_return_handler_blob() != NULL, + "polling page return stub not created yet"); + stub = SharedRuntime::polling_page_return_handler_blob()->instructions_begin(); + } else { + assert(SharedRuntime::polling_page_safepoint_handler_blob() != NULL, + "polling page safepoint stub not created yet"); + stub = SharedRuntime::polling_page_safepoint_handler_blob()->instructions_begin(); + } +#ifndef PRODUCT + if( TraceSafepoint ) { + char buf[256]; + jio_snprintf(buf, sizeof(buf), + "... found polling page %s exception at pc = " + INTPTR_FORMAT ", stub =" INTPTR_FORMAT, + at_poll_return ? "return" : "loop", + (intptr_t)pc, (intptr_t)stub); + tty->print_raw_cr(buf); + } +#endif // PRODUCT + return stub; +} + + +oop SharedRuntime::retrieve_receiver( symbolHandle sig, frame caller ) { + assert(caller.is_interpreted_frame(), ""); + int args_size = ArgumentSizeComputer(sig).size() + 1; + assert(args_size <= caller.interpreter_frame_expression_stack_size(), "receiver must be on interpreter stack"); + oop result = (oop) *caller.interpreter_frame_tos_at(args_size - 1); + assert(Universe::heap()->is_in(result) && result->is_oop(), "receiver must be an oop"); + return result; +} + + +void SharedRuntime::throw_and_post_jvmti_exception(JavaThread *thread, Handle h_exception) { + if (JvmtiExport::can_post_exceptions()) { + vframeStream vfst(thread, true); + methodHandle method = methodHandle(thread, vfst.method()); + address bcp = method()->bcp_from(vfst.bci()); + JvmtiExport::post_exception_throw(thread, method(), bcp, h_exception()); + } + Exceptions::_throw(thread, __FILE__, __LINE__, h_exception); +} + +void SharedRuntime::throw_and_post_jvmti_exception(JavaThread *thread, symbolOop name, const char *message) { + Handle h_exception = Exceptions::new_exception(thread, name, message); + throw_and_post_jvmti_exception(thread, h_exception); +} + +// ret_pc points into caller; we are returning caller's exception handler +// for given exception +address SharedRuntime::compute_compiled_exc_handler(nmethod* nm, address ret_pc, Handle& exception, + bool force_unwind, bool top_frame_only) { + assert(nm != NULL, "must exist"); + ResourceMark rm; + + ScopeDesc* sd = nm->scope_desc_at(ret_pc); + // determine handler bci, if any + EXCEPTION_MARK; + + int handler_bci = -1; + int scope_depth = 0; + if (!force_unwind) { + int bci = sd->bci(); + do { + bool skip_scope_increment = false; + // exception handler lookup + KlassHandle ek (THREAD, exception->klass()); + handler_bci = sd->method()->fast_exception_handler_bci_for(ek, bci, THREAD); + if (HAS_PENDING_EXCEPTION) { + // We threw an exception while trying to find the exception handler. + // Transfer the new exception to the exception handle which will + // be set into thread local storage, and do another lookup for an + // exception handler for this exception, this time starting at the + // BCI of the exception handler which caused the exception to be + // thrown (bugs 4307310 and 4546590). Set "exception" reference + // argument to ensure that the correct exception is thrown (4870175). + exception = Handle(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + if (handler_bci >= 0) { + bci = handler_bci; + handler_bci = -1; + skip_scope_increment = true; + } + } + if (!top_frame_only && handler_bci < 0 && !skip_scope_increment) { + sd = sd->sender(); + if (sd != NULL) { + bci = sd->bci(); + } + ++scope_depth; + } + } while (!top_frame_only && handler_bci < 0 && sd != NULL); + } + + // found handling method => lookup exception handler + int catch_pco = ret_pc - nm->instructions_begin(); + + ExceptionHandlerTable table(nm); + HandlerTableEntry *t = table.entry_for(catch_pco, handler_bci, scope_depth); + if (t == NULL && (nm->is_compiled_by_c1() || handler_bci != -1)) { + // Allow abbreviated catch tables. The idea is to allow a method + // to materialize its exceptions without committing to the exact + // routing of exceptions. In particular this is needed for adding + // a synthethic handler to unlock monitors when inlining + // synchonized methods since the unlock path isn't represented in + // the bytecodes. + t = table.entry_for(catch_pco, -1, 0); + } + +#ifdef COMPILER1 + if (nm->is_compiled_by_c1() && t == NULL && handler_bci == -1) { + // Exception is not handled by this frame so unwind. Note that + // this is not the same as how C2 does this. C2 emits a table + // entry that dispatches to the unwind code in the nmethod. + return NULL; + } +#endif /* COMPILER1 */ + + + if (t == NULL) { + tty->print_cr("MISSING EXCEPTION HANDLER for pc " INTPTR_FORMAT " and handler bci %d", ret_pc, handler_bci); + tty->print_cr(" Exception:"); + exception->print(); + tty->cr(); + tty->print_cr(" Compiled exception table :"); + table.print(); + nm->print_code(); + guarantee(false, "missing exception handler"); + return NULL; + } + + return nm->instructions_begin() + t->pco(); +} + +JRT_ENTRY(void, SharedRuntime::throw_AbstractMethodError(JavaThread* thread)) + // These errors occur only at call sites + throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_AbstractMethodError()); +JRT_END + +JRT_ENTRY(void, SharedRuntime::throw_ArithmeticException(JavaThread* thread)) + throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_ArithmeticException(), "/ by zero"); +JRT_END + +JRT_ENTRY(void, SharedRuntime::throw_NullPointerException(JavaThread* thread)) + throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_NullPointerException()); +JRT_END + +JRT_ENTRY(void, SharedRuntime::throw_NullPointerException_at_call(JavaThread* thread)) + // This entry point is effectively only used for NullPointerExceptions which occur at inline + // cache sites (when the callee activation is not yet set up) so we are at a call site + throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_NullPointerException()); +JRT_END + +JRT_ENTRY(void, SharedRuntime::throw_StackOverflowError(JavaThread* thread)) + // We avoid using the normal exception construction in this case because + // it performs an upcall to Java, and we're already out of stack space. + klassOop k = SystemDictionary::StackOverflowError_klass(); + oop exception_oop = instanceKlass::cast(k)->allocate_instance(CHECK); + Handle exception (thread, exception_oop); + if (StackTraceInThrowable) { + java_lang_Throwable::fill_in_stack_trace(exception); + } + throw_and_post_jvmti_exception(thread, exception); +JRT_END + +address SharedRuntime::continuation_for_implicit_exception(JavaThread* thread, + address pc, + SharedRuntime::ImplicitExceptionKind exception_kind) +{ + address target_pc = NULL; + + if (Interpreter::contains(pc)) { +#ifdef CC_INTERP + // C++ interpreter doesn't throw implicit exceptions + ShouldNotReachHere(); +#else + switch (exception_kind) { + case IMPLICIT_NULL: return Interpreter::throw_NullPointerException_entry(); + case IMPLICIT_DIVIDE_BY_ZERO: return Interpreter::throw_ArithmeticException_entry(); + case STACK_OVERFLOW: return Interpreter::throw_StackOverflowError_entry(); + default: ShouldNotReachHere(); + } +#endif // !CC_INTERP + } else { + switch (exception_kind) { + case STACK_OVERFLOW: { + // Stack overflow only occurs upon frame setup; the callee is + // going to be unwound. Dispatch to a shared runtime stub + // which will cause the StackOverflowError to be fabricated + // and processed. + // For stack overflow in deoptimization blob, cleanup thread. + if (thread->deopt_mark() != NULL) { + Deoptimization::cleanup_deopt_info(thread, NULL); + } + return StubRoutines::throw_StackOverflowError_entry(); + } + + case IMPLICIT_NULL: { + if (VtableStubs::contains(pc)) { + // We haven't yet entered the callee frame. Fabricate an + // exception and begin dispatching it in the caller. Since + // the caller was at a call site, it's safe to destroy all + // caller-saved registers, as these entry points do. + VtableStub* vt_stub = VtableStubs::stub_containing(pc); + guarantee(vt_stub != NULL, "unable to find SEGVing vtable stub"); + if (vt_stub->is_abstract_method_error(pc)) { + assert(!vt_stub->is_vtable_stub(), "should never see AbstractMethodErrors from vtable-type VtableStubs"); + return StubRoutines::throw_AbstractMethodError_entry(); + } else { + return StubRoutines::throw_NullPointerException_at_call_entry(); + } + } else { + CodeBlob* cb = CodeCache::find_blob(pc); + guarantee(cb != NULL, "exception happened outside interpreter, nmethods and vtable stubs (1)"); + + // Exception happened in CodeCache. Must be either: + // 1. Inline-cache check in C2I handler blob, + // 2. Inline-cache check in nmethod, or + // 3. Implict null exception in nmethod + + if (!cb->is_nmethod()) { + guarantee(cb->is_adapter_blob(), + "exception happened outside interpreter, nmethods and vtable stubs (2)"); + // There is no handler here, so we will simply unwind. + return StubRoutines::throw_NullPointerException_at_call_entry(); + } + + // Otherwise, it's an nmethod. Consult its exception handlers. + nmethod* nm = (nmethod*)cb; + if (nm->inlinecache_check_contains(pc)) { + // exception happened inside inline-cache check code + // => the nmethod is not yet active (i.e., the frame + // is not set up yet) => use return address pushed by + // caller => don't push another return address + return StubRoutines::throw_NullPointerException_at_call_entry(); + } + +#ifndef PRODUCT + _implicit_null_throws++; +#endif + target_pc = nm->continuation_for_implicit_exception(pc); + guarantee(target_pc != 0, "must have a continuation point"); + } + + break; // fall through + } + + + case IMPLICIT_DIVIDE_BY_ZERO: { + nmethod* nm = CodeCache::find_nmethod(pc); + guarantee(nm != NULL, "must have containing nmethod for implicit division-by-zero exceptions"); +#ifndef PRODUCT + _implicit_div0_throws++; +#endif + target_pc = nm->continuation_for_implicit_exception(pc); + guarantee(target_pc != 0, "must have a continuation point"); + break; // fall through + } + + default: ShouldNotReachHere(); + } + + guarantee(target_pc != NULL, "must have computed destination PC for implicit exception"); + assert(exception_kind == IMPLICIT_NULL || exception_kind == IMPLICIT_DIVIDE_BY_ZERO, "wrong implicit exception kind"); + + // for AbortVMOnException flag + NOT_PRODUCT(Exceptions::debug_check_abort("java.lang.NullPointerException")); + if (exception_kind == IMPLICIT_NULL) { + Events::log("Implicit null exception at " INTPTR_FORMAT " to " INTPTR_FORMAT, pc, target_pc); + } else { + Events::log("Implicit division by zero exception at " INTPTR_FORMAT " to " INTPTR_FORMAT, pc, target_pc); + } + return target_pc; + } + + ShouldNotReachHere(); + return NULL; +} + + +JNI_ENTRY(void, throw_unsatisfied_link_error(JNIEnv* env, ...)) +{ + THROW(vmSymbols::java_lang_UnsatisfiedLinkError()); +} +JNI_END + + +address SharedRuntime::native_method_throw_unsatisfied_link_error_entry() { + return CAST_FROM_FN_PTR(address, &throw_unsatisfied_link_error); +} + + +#ifndef PRODUCT +JRT_ENTRY(intptr_t, SharedRuntime::trace_bytecode(JavaThread* thread, intptr_t preserve_this_value, intptr_t tos, intptr_t tos2)) + const frame f = thread->last_frame(); + assert(f.is_interpreted_frame(), "must be an interpreted frame"); +#ifndef PRODUCT + methodHandle mh(THREAD, f.interpreter_frame_method()); + BytecodeTracer::trace(mh, f.interpreter_frame_bcp(), tos, tos2); +#endif // !PRODUCT + return preserve_this_value; +JRT_END +#endif // !PRODUCT + + +JRT_ENTRY(void, SharedRuntime::yield_all(JavaThread* thread, int attempts)) + os::yield_all(attempts); +JRT_END + + +// --------------------------------------------------------------------------------------------------------- +// Non-product code +#ifndef PRODUCT + +void SharedRuntime::verify_caller_frame(frame caller_frame, methodHandle callee_method) { + ResourceMark rm; + assert (caller_frame.is_interpreted_frame(), "sanity check"); + assert (callee_method->has_compiled_code(), "callee must be compiled"); + methodHandle caller_method (Thread::current(), caller_frame.interpreter_frame_method()); + jint bci = caller_frame.interpreter_frame_bci(); + methodHandle method = find_callee_method_inside_interpreter(caller_frame, caller_method, bci); + assert (callee_method == method, "incorrect method"); +} + +methodHandle SharedRuntime::find_callee_method_inside_interpreter(frame caller_frame, methodHandle caller_method, int bci) { + EXCEPTION_MARK; + Bytecode_invoke* bytecode = Bytecode_invoke_at(caller_method, bci); + methodHandle staticCallee = bytecode->static_target(CATCH); // Non-product code + + bytecode = Bytecode_invoke_at(caller_method, bci); + int bytecode_index = bytecode->index(); + Bytecodes::Code bc = bytecode->adjusted_invoke_code(); + + Handle receiver; + if (bc == Bytecodes::_invokeinterface || + bc == Bytecodes::_invokevirtual || + bc == Bytecodes::_invokespecial) { + symbolHandle signature (THREAD, staticCallee->signature()); + receiver = Handle(THREAD, retrieve_receiver(signature, caller_frame)); + } else { + receiver = Handle(); + } + CallInfo result; + constantPoolHandle constants (THREAD, caller_method->constants()); + LinkResolver::resolve_invoke(result, receiver, constants, bytecode_index, bc, CATCH); // Non-product code + methodHandle calleeMethod = result.selected_method(); + return calleeMethod; +} + +#endif // PRODUCT + + +JRT_ENTRY_NO_ASYNC(void, SharedRuntime::register_finalizer(JavaThread* thread, oopDesc* obj)) + assert(obj->is_oop(), "must be a valid oop"); + assert(obj->klass()->klass_part()->has_finalizer(), "shouldn't be here otherwise"); + instanceKlass::register_finalizer(instanceOop(obj), CHECK); +JRT_END + + +jlong SharedRuntime::get_java_tid(Thread* thread) { + if (thread != NULL) { + if (thread->is_Java_thread()) { + oop obj = ((JavaThread*)thread)->threadObj(); + return (obj == NULL) ? 0 : java_lang_Thread::thread_id(obj); + } + } + return 0; +} + +/** + * This function ought to be a void function, but cannot be because + * it gets turned into a tail-call on sparc, which runs into dtrace bug + * 6254741. Once that is fixed we can remove the dummy return value. + */ +int SharedRuntime::dtrace_object_alloc(oopDesc* o) { + return dtrace_object_alloc_base(Thread::current(), o); +} + +int SharedRuntime::dtrace_object_alloc_base(Thread* thread, oopDesc* o) { + assert(DTraceAllocProbes, "wrong call"); + Klass* klass = o->blueprint(); + int size = o->size(); + symbolOop name = klass->name(); + HS_DTRACE_PROBE4(hotspot, object__alloc, get_java_tid(thread), + name->bytes(), name->utf8_length(), size * HeapWordSize); + return 0; +} + +JRT_LEAF(int, SharedRuntime::dtrace_method_entry( + JavaThread* thread, methodOopDesc* method)) + assert(DTraceMethodProbes, "wrong call"); + symbolOop kname = method->klass_name(); + symbolOop name = method->name(); + symbolOop sig = method->signature(); + HS_DTRACE_PROBE7(hotspot, method__entry, get_java_tid(thread), + kname->bytes(), kname->utf8_length(), + name->bytes(), name->utf8_length(), + sig->bytes(), sig->utf8_length()); + return 0; +JRT_END + +JRT_LEAF(int, SharedRuntime::dtrace_method_exit( + JavaThread* thread, methodOopDesc* method)) + assert(DTraceMethodProbes, "wrong call"); + symbolOop kname = method->klass_name(); + symbolOop name = method->name(); + symbolOop sig = method->signature(); + HS_DTRACE_PROBE7(hotspot, method__return, get_java_tid(thread), + kname->bytes(), kname->utf8_length(), + name->bytes(), name->utf8_length(), + sig->bytes(), sig->utf8_length()); + return 0; +JRT_END + + +// Finds receiver, CallInfo (i.e. receiver method), and calling bytecode) +// for a call current in progress, i.e., arguments has been pushed on stack +// put callee has not been invoked yet. Used by: resolve virtual/static, +// vtable updates, etc. Caller frame must be compiled. +Handle SharedRuntime::find_callee_info(JavaThread* thread, Bytecodes::Code& bc, CallInfo& callinfo, TRAPS) { + ResourceMark rm(THREAD); + + // last java frame on stack (which includes native call frames) + vframeStream vfst(thread, true); // Do not skip and javaCalls + + return find_callee_info_helper(thread, vfst, bc, callinfo, CHECK_(Handle())); +} + + +// Finds receiver, CallInfo (i.e. receiver method), and calling bytecode +// for a call current in progress, i.e., arguments has been pushed on stack +// but callee has not been invoked yet. Caller frame must be compiled. +Handle SharedRuntime::find_callee_info_helper(JavaThread* thread, + vframeStream& vfst, + Bytecodes::Code& bc, + CallInfo& callinfo, TRAPS) { + Handle receiver; + Handle nullHandle; //create a handy null handle for exception returns + + assert(!vfst.at_end(), "Java frame must exist"); + + // Find caller and bci from vframe + methodHandle caller (THREAD, vfst.method()); + int bci = vfst.bci(); + + // Find bytecode + Bytecode_invoke* bytecode = Bytecode_invoke_at(caller, bci); + bc = bytecode->adjusted_invoke_code(); + int bytecode_index = bytecode->index(); + + // Find receiver for non-static call + if (bc != Bytecodes::_invokestatic) { + // This register map must be update since we need to find the receiver for + // compiled frames. The receiver might be in a register. + RegisterMap reg_map2(thread); + frame stubFrame = thread->last_frame(); + // Caller-frame is a compiled frame + frame callerFrame = stubFrame.sender(®_map2); + + methodHandle callee = bytecode->static_target(CHECK_(nullHandle)); + if (callee.is_null()) { + THROW_(vmSymbols::java_lang_NoSuchMethodException(), nullHandle); + } + // Retrieve from a compiled argument list + receiver = Handle(THREAD, callerFrame.retrieve_receiver(®_map2)); + + if (receiver.is_null()) { + THROW_(vmSymbols::java_lang_NullPointerException(), nullHandle); + } + } + + // Resolve method. This is parameterized by bytecode. + constantPoolHandle constants (THREAD, caller->constants()); + assert (receiver.is_null() || receiver->is_oop(), "wrong receiver"); + LinkResolver::resolve_invoke(callinfo, receiver, constants, bytecode_index, bc, CHECK_(nullHandle)); + +#ifdef ASSERT + // Check that the receiver klass is of the right subtype and that it is initialized for virtual calls + if (bc != Bytecodes::_invokestatic) { + assert(receiver.not_null(), "should have thrown exception"); + KlassHandle receiver_klass (THREAD, receiver->klass()); + klassOop rk = constants->klass_ref_at(bytecode_index, CHECK_(nullHandle)); + // klass is already loaded + KlassHandle static_receiver_klass (THREAD, rk); + assert(receiver_klass->is_subtype_of(static_receiver_klass()), "actual receiver must be subclass of static receiver klass"); + if (receiver_klass->oop_is_instance()) { + if (instanceKlass::cast(receiver_klass())->is_not_initialized()) { + tty->print_cr("ERROR: Klass not yet initialized!!"); + receiver_klass.print(); + } + assert (!instanceKlass::cast(receiver_klass())->is_not_initialized(), "receiver_klass must be initialized"); + } + } +#endif + + return receiver; +} + +methodHandle SharedRuntime::find_callee_method(JavaThread* thread, TRAPS) { + ResourceMark rm(THREAD); + // We need first to check if any Java activations (compiled, interpreted) + // exist on the stack since last JavaCall. If not, we need + // to get the target method from the JavaCall wrapper. + vframeStream vfst(thread, true); // Do not skip any javaCalls + methodHandle callee_method; + if (vfst.at_end()) { + // No Java frames were found on stack since we did the JavaCall. + // Hence the stack can only contain an entry_frame. We need to + // find the target method from the stub frame. + RegisterMap reg_map(thread, false); + frame fr = thread->last_frame(); + assert(fr.is_runtime_frame(), "must be a runtimeStub"); + fr = fr.sender(®_map); + assert(fr.is_entry_frame(), "must be"); + // fr is now pointing to the entry frame. + callee_method = methodHandle(THREAD, fr.entry_frame_call_wrapper()->callee_method()); + assert(fr.entry_frame_call_wrapper()->receiver() == NULL || !callee_method->is_static(), "non-null receiver for static call??"); + } else { + Bytecodes::Code bc; + CallInfo callinfo; + find_callee_info_helper(thread, vfst, bc, callinfo, CHECK_(methodHandle())); + callee_method = callinfo.selected_method(); + } + assert(callee_method()->is_method(), "must be"); + return callee_method; +} + +// Resolves a call. +methodHandle SharedRuntime::resolve_helper(JavaThread *thread, + bool is_virtual, + bool is_optimized, TRAPS) { + methodHandle callee_method; + callee_method = resolve_sub_helper(thread, is_virtual, is_optimized, THREAD); + if (JvmtiExport::can_hotswap_or_post_breakpoint()) { + int retry_count = 0; + while (!HAS_PENDING_EXCEPTION && callee_method->is_old() && + callee_method->method_holder() != SystemDictionary::object_klass()) { + // If has a pending exception then there is no need to re-try to + // resolve this method. + // If the method has been redefined, we need to try again. + // Hack: we have no way to update the vtables of arrays, so don't + // require that java.lang.Object has been updated. + + // It is very unlikely that method is redefined more than 100 times + // in the middle of resolve. If it is looping here more than 100 times + // means then there could be a bug here. + guarantee((retry_count++ < 100), + "Could not resolve to latest version of redefined method"); + // method is redefined in the middle of resolve so re-try. + callee_method = resolve_sub_helper(thread, is_virtual, is_optimized, THREAD); + } + } + return callee_method; +} + +// Resolves a call. The compilers generate code for calls that go here +// and are patched with the real destination of the call. +methodHandle SharedRuntime::resolve_sub_helper(JavaThread *thread, + bool is_virtual, + bool is_optimized, TRAPS) { + + ResourceMark rm(thread); + RegisterMap cbl_map(thread, false); + frame caller_frame = thread->last_frame().sender(&cbl_map); + + CodeBlob* cb = caller_frame.cb(); + guarantee(cb != NULL && cb->is_nmethod(), "must be called from nmethod"); + // make sure caller is not getting deoptimized + // and removed before we are done with it. + // CLEANUP - with lazy deopt shouldn't need this lock + nmethodLocker caller_lock((nmethod*)cb); + + + // determine call info & receiver + // note: a) receiver is NULL for static calls + // b) an exception is thrown if receiver is NULL for non-static calls + CallInfo call_info; + Bytecodes::Code invoke_code = Bytecodes::_illegal; + Handle receiver = find_callee_info(thread, invoke_code, + call_info, CHECK_(methodHandle())); + methodHandle callee_method = call_info.selected_method(); + + assert((!is_virtual && invoke_code == Bytecodes::_invokestatic) || + ( is_virtual && invoke_code != Bytecodes::_invokestatic), "inconsistent bytecode"); + +#ifndef PRODUCT + // tracing/debugging/statistics + int *addr = (is_optimized) ? (&_resolve_opt_virtual_ctr) : + (is_virtual) ? (&_resolve_virtual_ctr) : + (&_resolve_static_ctr); + Atomic::inc(addr); + + if (TraceCallFixup) { + ResourceMark rm(thread); + tty->print("resolving %s%s (%s) call to", + (is_optimized) ? "optimized " : "", (is_virtual) ? "virtual" : "static", + Bytecodes::name(invoke_code)); + callee_method->print_short_name(tty); + tty->print_cr(" code: " INTPTR_FORMAT, callee_method->code()); + } +#endif + + // Compute entry points. This might require generation of C2I converter + // frames, so we cannot be holding any locks here. Furthermore, the + // computation of the entry points is independent of patching the call. We + // always return the entry-point, but we only patch the stub if the call has + // not been deoptimized. Return values: For a virtual call this is an + // (cached_oop, destination address) pair. For a static call/optimized + // virtual this is just a destination address. + + StaticCallInfo static_call_info; + CompiledICInfo virtual_call_info; + + + // Make sure the callee nmethod does not get deoptimized and removed before + // we are done patching the code. + nmethod* nm = callee_method->code(); + nmethodLocker nl_callee(nm); +#ifdef ASSERT + address dest_entry_point = nm == NULL ? 0 : nm->entry_point(); // used below +#endif + + if (is_virtual) { + assert(receiver.not_null(), "sanity check"); + bool static_bound = call_info.resolved_method()->can_be_statically_bound(); + KlassHandle h_klass(THREAD, receiver->klass()); + CompiledIC::compute_monomorphic_entry(callee_method, h_klass, + is_optimized, static_bound, virtual_call_info, + CHECK_(methodHandle())); + } else { + // static call + CompiledStaticCall::compute_entry(callee_method, static_call_info); + } + + // grab lock, check for deoptimization and potentially patch caller + { + MutexLocker ml_patch(CompiledIC_lock); + + // Now that we are ready to patch if the methodOop was redefined then + // don't update call site and let the caller retry. + + if (!callee_method->is_old()) { +#ifdef ASSERT + // We must not try to patch to jump to an already unloaded method. + if (dest_entry_point != 0) { + assert(CodeCache::find_blob(dest_entry_point) != NULL, + "should not unload nmethod while locked"); + } +#endif + if (is_virtual) { + CompiledIC* inline_cache = CompiledIC_before(caller_frame.pc()); + if (inline_cache->is_clean()) { + inline_cache->set_to_monomorphic(virtual_call_info); + } + } else { + CompiledStaticCall* ssc = compiledStaticCall_before(caller_frame.pc()); + if (ssc->is_clean()) ssc->set(static_call_info); + } + } + + } // unlock CompiledIC_lock + + return callee_method; +} + + +// Inline caches exist only in compiled code +JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method_ic_miss(JavaThread* thread)) +#ifdef ASSERT + RegisterMap reg_map(thread, false); + frame stub_frame = thread->last_frame(); + assert(stub_frame.is_runtime_frame(), "sanity check"); + frame caller_frame = stub_frame.sender(®_map); + assert(!caller_frame.is_interpreted_frame() && !caller_frame.is_entry_frame(), "unexpected frame"); +#endif /* ASSERT */ + + methodHandle callee_method; + JRT_BLOCK + callee_method = SharedRuntime::handle_ic_miss_helper(thread, CHECK_NULL); + // Return methodOop through TLS + thread->set_vm_result(callee_method()); + JRT_BLOCK_END + // return compiled code entry point after potential safepoints + assert(callee_method->verified_code_entry() != NULL, " Jump to zero!"); + return callee_method->verified_code_entry(); +JRT_END + + +// Handle call site that has been made non-entrant +JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method(JavaThread* thread)) + // 6243940 We might end up in here if the callee is deoptimized + // as we race to call it. We don't want to take a safepoint if + // the caller was interpreted because the caller frame will look + // interpreted to the stack walkers and arguments are now + // "compiled" so it is much better to make this transition + // invisible to the stack walking code. The i2c path will + // place the callee method in the callee_target. It is stashed + // there because if we try and find the callee by normal means a + // safepoint is possible and have trouble gc'ing the compiled args. + RegisterMap reg_map(thread, false); + frame stub_frame = thread->last_frame(); + assert(stub_frame.is_runtime_frame(), "sanity check"); + frame caller_frame = stub_frame.sender(®_map); + if (caller_frame.is_interpreted_frame() || caller_frame.is_entry_frame() ) { + methodOop callee = thread->callee_target(); + guarantee(callee != NULL && callee->is_method(), "bad handshake"); + thread->set_vm_result(callee); + thread->set_callee_target(NULL); + return callee->get_c2i_entry(); + } + + // Must be compiled to compiled path which is safe to stackwalk + methodHandle callee_method; + JRT_BLOCK + // Force resolving of caller (if we called from compiled frame) + callee_method = SharedRuntime::reresolve_call_site(thread, CHECK_NULL); + thread->set_vm_result(callee_method()); + JRT_BLOCK_END + // return compiled code entry point after potential safepoints + assert(callee_method->verified_code_entry() != NULL, " Jump to zero!"); + return callee_method->verified_code_entry(); +JRT_END + + +// resolve a static call and patch code +JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_static_call_C(JavaThread *thread )) + methodHandle callee_method; + JRT_BLOCK + callee_method = SharedRuntime::resolve_helper(thread, false, false, CHECK_NULL); + thread->set_vm_result(callee_method()); + JRT_BLOCK_END + // return compiled code entry point after potential safepoints + assert(callee_method->verified_code_entry() != NULL, " Jump to zero!"); + return callee_method->verified_code_entry(); +JRT_END + + +// resolve virtual call and update inline cache to monomorphic +JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_virtual_call_C(JavaThread *thread )) + methodHandle callee_method; + JRT_BLOCK + callee_method = SharedRuntime::resolve_helper(thread, true, false, CHECK_NULL); + thread->set_vm_result(callee_method()); + JRT_BLOCK_END + // return compiled code entry point after potential safepoints + assert(callee_method->verified_code_entry() != NULL, " Jump to zero!"); + return callee_method->verified_code_entry(); +JRT_END + + +// Resolve a virtual call that can be statically bound (e.g., always +// monomorphic, so it has no inline cache). Patch code to resolved target. +JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_opt_virtual_call_C(JavaThread *thread)) + methodHandle callee_method; + JRT_BLOCK + callee_method = SharedRuntime::resolve_helper(thread, true, true, CHECK_NULL); + thread->set_vm_result(callee_method()); + JRT_BLOCK_END + // return compiled code entry point after potential safepoints + assert(callee_method->verified_code_entry() != NULL, " Jump to zero!"); + return callee_method->verified_code_entry(); +JRT_END + + + + + +methodHandle SharedRuntime::handle_ic_miss_helper(JavaThread *thread, TRAPS) { + ResourceMark rm(thread); + CallInfo call_info; + Bytecodes::Code bc; + + // receiver is NULL for static calls. An exception is thrown for NULL + // receivers for non-static calls + Handle receiver = find_callee_info(thread, bc, call_info, + CHECK_(methodHandle())); + // Compiler1 can produce virtual call sites that can actually be statically bound + // If we fell thru to below we would think that the site was going megamorphic + // when in fact the site can never miss. Worse because we'd think it was megamorphic + // we'd try and do a vtable dispatch however methods that can be statically bound + // don't have vtable entries (vtable_index < 0) and we'd blow up. So we force a + // reresolution of the call site (as if we did a handle_wrong_method and not an + // plain ic_miss) and the site will be converted to an optimized virtual call site + // never to miss again. I don't believe C2 will produce code like this but if it + // did this would still be the correct thing to do for it too, hence no ifdef. + // + if (call_info.resolved_method()->can_be_statically_bound()) { + methodHandle callee_method = SharedRuntime::reresolve_call_site(thread, CHECK_(methodHandle())); + if (TraceCallFixup) { + RegisterMap reg_map(thread, false); + frame caller_frame = thread->last_frame().sender(®_map); + ResourceMark rm(thread); + tty->print("converting IC miss to reresolve (%s) call to", Bytecodes::name(bc)); + callee_method->print_short_name(tty); + tty->print_cr(" from pc: " INTPTR_FORMAT, caller_frame.pc()); + tty->print_cr(" code: " INTPTR_FORMAT, callee_method->code()); + } + return callee_method; + } + + methodHandle callee_method = call_info.selected_method(); + + bool should_be_mono = false; + +#ifndef PRODUCT + Atomic::inc(&_ic_miss_ctr); + + // Statistics & Tracing + if (TraceCallFixup) { + ResourceMark rm(thread); + tty->print("IC miss (%s) call to", Bytecodes::name(bc)); + callee_method->print_short_name(tty); + tty->print_cr(" code: " INTPTR_FORMAT, callee_method->code()); + } + + if (ICMissHistogram) { + MutexLocker m(VMStatistic_lock); + RegisterMap reg_map(thread, false); + frame f = thread->last_frame().real_sender(®_map);// skip runtime stub + // produce statistics under the lock + trace_ic_miss(f.pc()); + } +#endif + + // install an event collector so that when a vtable stub is created the + // profiler can be notified via a DYNAMIC_CODE_GENERATED event. The + // event can't be posted when the stub is created as locks are held + // - instead the event will be deferred until the event collector goes + // out of scope. + JvmtiDynamicCodeEventCollector event_collector; + + // Update inline cache to megamorphic. Skip update if caller has been + // made non-entrant or we are called from interpreted. + { MutexLocker ml_patch (CompiledIC_lock); + RegisterMap reg_map(thread, false); + frame caller_frame = thread->last_frame().sender(®_map); + CodeBlob* cb = caller_frame.cb(); + if (cb->is_nmethod() && ((nmethod*)cb)->is_in_use()) { + // Not a non-entrant nmethod, so find inline_cache + CompiledIC* inline_cache = CompiledIC_before(caller_frame.pc()); + bool should_be_mono = false; + if (inline_cache->is_optimized()) { + if (TraceCallFixup) { + ResourceMark rm(thread); + tty->print("OPTIMIZED IC miss (%s) call to", Bytecodes::name(bc)); + callee_method->print_short_name(tty); + tty->print_cr(" code: " INTPTR_FORMAT, callee_method->code()); + } + should_be_mono = true; + } else { + compiledICHolderOop ic_oop = (compiledICHolderOop) inline_cache->cached_oop(); + if ( ic_oop != NULL && ic_oop->is_compiledICHolder()) { + + if (receiver()->klass() == ic_oop->holder_klass()) { + // This isn't a real miss. We must have seen that compiled code + // is now available and we want the call site converted to a + // monomorphic compiled call site. + // We can't assert for callee_method->code() != NULL because it + // could have been deoptimized in the meantime + if (TraceCallFixup) { + ResourceMark rm(thread); + tty->print("FALSE IC miss (%s) converting to compiled call to", Bytecodes::name(bc)); + callee_method->print_short_name(tty); + tty->print_cr(" code: " INTPTR_FORMAT, callee_method->code()); + } + should_be_mono = true; + } + } + } + + if (should_be_mono) { + + // We have a path that was monomorphic but was going interpreted + // and now we have (or had) a compiled entry. We correct the IC + // by using a new icBuffer. + CompiledICInfo info; + KlassHandle receiver_klass(THREAD, receiver()->klass()); + inline_cache->compute_monomorphic_entry(callee_method, + receiver_klass, + inline_cache->is_optimized(), + false, + info, CHECK_(methodHandle())); + inline_cache->set_to_monomorphic(info); + } else if (!inline_cache->is_megamorphic() && !inline_cache->is_clean()) { + // Change to megamorphic + inline_cache->set_to_megamorphic(&call_info, bc, CHECK_(methodHandle())); + } else { + // Either clean or megamorphic + } + } + } // Release CompiledIC_lock + + return callee_method; +} + +// +// Resets a call-site in compiled code so it will get resolved again. +// This routines handles both virtual call sites, optimized virtual call +// sites, and static call sites. Typically used to change a call sites +// destination from compiled to interpreted. +// +methodHandle SharedRuntime::reresolve_call_site(JavaThread *thread, TRAPS) { + ResourceMark rm(thread); + RegisterMap reg_map(thread, false); + frame stub_frame = thread->last_frame(); + assert(stub_frame.is_runtime_frame(), "must be a runtimeStub"); + frame caller = stub_frame.sender(®_map); + + // Do nothing if the frame isn't a live compiled frame. + // nmethod could be deoptimized by the time we get here + // so no update to the caller is needed. + + if (caller.is_compiled_frame() && !caller.is_deoptimized_frame()) { + + address pc = caller.pc(); + Events::log("update call-site at pc " INTPTR_FORMAT, pc); + + // Default call_addr is the location of the "basic" call. + // Determine the address of the call we a reresolving. With + // Inline Caches we will always find a recognizable call. + // With Inline Caches disabled we may or may not find a + // recognizable call. We will always find a call for static + // calls and for optimized virtual calls. For vanilla virtual + // calls it depends on the state of the UseInlineCaches switch. + // + // With Inline Caches disabled we can get here for a virtual call + // for two reasons: + // 1 - calling an abstract method. The vtable for abstract methods + // will run us thru handle_wrong_method and we will eventually + // end up in the interpreter to throw the ame. + // 2 - a racing deoptimization. We could be doing a vanilla vtable + // call and between the time we fetch the entry address and + // we jump to it the target gets deoptimized. Similar to 1 + // we will wind up in the interprter (thru a c2i with c2). + // + address call_addr = NULL; + { + // Get call instruction under lock because another thread may be + // busy patching it. + MutexLockerEx ml_patch(Patching_lock, Mutex::_no_safepoint_check_flag); + // Location of call instruction + if (NativeCall::is_call_before(pc)) { + NativeCall *ncall = nativeCall_before(pc); + call_addr = ncall->instruction_address(); + } + } + + // Check for static or virtual call + bool is_static_call = false; + nmethod* caller_nm = CodeCache::find_nmethod(pc); + // Make sure nmethod doesn't get deoptimized and removed until + // this is done with it. + // CLEANUP - with lazy deopt shouldn't need this lock + nmethodLocker nmlock(caller_nm); + + if (call_addr != NULL) { + RelocIterator iter(caller_nm, call_addr, call_addr+1); + int ret = iter.next(); // Get item + if (ret) { + assert(iter.addr() == call_addr, "must find call"); + if (iter.type() == relocInfo::static_call_type) { + is_static_call = true; + } else { + assert(iter.type() == relocInfo::virtual_call_type || + iter.type() == relocInfo::opt_virtual_call_type + , "unexpected relocInfo. type"); + } + } else { + assert(!UseInlineCaches, "relocation info. must exist for this address"); + } + + // Cleaning the inline cache will force a new resolve. This is more robust + // than directly setting it to the new destination, since resolving of calls + // is always done through the same code path. (experience shows that it + // leads to very hard to track down bugs, if an inline cache gets updated + // to a wrong method). It should not be performance critical, since the + // resolve is only done once. + + MutexLocker ml(CompiledIC_lock); + // + // We do not patch the call site if the nmethod has been made non-entrant + // as it is a waste of time + // + if (caller_nm->is_in_use()) { + if (is_static_call) { + CompiledStaticCall* ssc= compiledStaticCall_at(call_addr); + ssc->set_to_clean(); + } else { + // compiled, dispatched call (which used to call an interpreted method) + CompiledIC* inline_cache = CompiledIC_at(call_addr); + inline_cache->set_to_clean(); + } + } + } + + } + + methodHandle callee_method = find_callee_method(thread, CHECK_(methodHandle())); + + +#ifndef PRODUCT + Atomic::inc(&_wrong_method_ctr); + + if (TraceCallFixup) { + ResourceMark rm(thread); + tty->print("handle_wrong_method reresolving call to"); + callee_method->print_short_name(tty); + tty->print_cr(" code: " INTPTR_FORMAT, callee_method->code()); + } +#endif + + return callee_method; +} + +// --------------------------------------------------------------------------- +// We are calling the interpreter via a c2i. Normally this would mean that +// we were called by a compiled method. However we could have lost a race +// where we went int -> i2c -> c2i and so the caller could in fact be +// interpreted. If the caller is compiled we attampt to patch the caller +// so he no longer calls into the interpreter. +IRT_LEAF(void, SharedRuntime::fixup_callers_callsite(methodOopDesc* method, address caller_pc)) + methodOop moop(method); + + address entry_point = moop->from_compiled_entry(); + + // It's possible that deoptimization can occur at a call site which hasn't + // been resolved yet, in which case this function will be called from + // an nmethod that has been patched for deopt and we can ignore the + // request for a fixup. + // Also it is possible that we lost a race in that from_compiled_entry + // is now back to the i2c in that case we don't need to patch and if + // we did we'd leap into space because the callsite needs to use + // "to interpreter" stub in order to load up the methodOop. Don't + // ask me how I know this... + // + + CodeBlob* cb = CodeCache::find_blob(caller_pc); + if ( !cb->is_nmethod() || entry_point == moop->get_c2i_entry()) { + return; + } + + // There is a benign race here. We could be attempting to patch to a compiled + // entry point at the same time the callee is being deoptimized. If that is + // the case then entry_point may in fact point to a c2i and we'd patch the + // call site with the same old data. clear_code will set code() to NULL + // at the end of it. If we happen to see that NULL then we can skip trying + // to patch. If we hit the window where the callee has a c2i in the + // from_compiled_entry and the NULL isn't present yet then we lose the race + // and patch the code with the same old data. Asi es la vida. + + if (moop->code() == NULL) return; + + if (((nmethod*)cb)->is_in_use()) { + + // Expect to find a native call there (unless it was no-inline cache vtable dispatch) + MutexLockerEx ml_patch(Patching_lock, Mutex::_no_safepoint_check_flag); + if (NativeCall::is_call_before(caller_pc + frame::pc_return_offset)) { + NativeCall *call = nativeCall_before(caller_pc + frame::pc_return_offset); + // + // bug 6281185. We might get here after resolving a call site to a vanilla + // virtual call. Because the resolvee uses the verified entry it may then + // see compiled code and attempt to patch the site by calling us. This would + // then incorrectly convert the call site to optimized and its downhill from + // there. If you're lucky you'll get the assert in the bugid, if not you've + // just made a call site that could be megamorphic into a monomorphic site + // for the rest of its life! Just another racing bug in the life of + // fixup_callers_callsite ... + // + RelocIterator iter(cb, call->instruction_address(), call->next_instruction_address()); + iter.next(); + assert(iter.has_current(), "must have a reloc at java call site"); + relocInfo::relocType typ = iter.reloc()->type(); + if ( typ != relocInfo::static_call_type && + typ != relocInfo::opt_virtual_call_type && + typ != relocInfo::static_stub_type) { + return; + } + address destination = call->destination(); + if (destination != entry_point) { + CodeBlob* callee = CodeCache::find_blob(destination); + // callee == cb seems weird. It means calling interpreter thru stub. + if (callee == cb || callee->is_adapter_blob()) { + // static call or optimized virtual + if (TraceCallFixup) { + tty->print("fixup callsite at " INTPTR_FORMAT " to compiled code for", caller_pc); + moop->print_short_name(tty); + tty->print_cr(" to " INTPTR_FORMAT, entry_point); + } + call->set_destination_mt_safe(entry_point); + } else { + if (TraceCallFixup) { + tty->print("failed to fixup callsite at " INTPTR_FORMAT " to compiled code for", caller_pc); + moop->print_short_name(tty); + tty->print_cr(" to " INTPTR_FORMAT, entry_point); + } + // assert is too strong could also be resolve destinations. + // assert(InlineCacheBuffer::contains(destination) || VtableStubs::contains(destination), "must be"); + } + } else { + if (TraceCallFixup) { + tty->print("already patched callsite at " INTPTR_FORMAT " to compiled code for", caller_pc); + moop->print_short_name(tty); + tty->print_cr(" to " INTPTR_FORMAT, entry_point); + } + } + } + } + +IRT_END + + +// same as JVM_Arraycopy, but called directly from compiled code +JRT_ENTRY(void, SharedRuntime::slow_arraycopy_C(oopDesc* src, jint src_pos, + oopDesc* dest, jint dest_pos, + jint length, + JavaThread* thread)) { +#ifndef PRODUCT + _slow_array_copy_ctr++; +#endif + // Check if we have null pointers + if (src == NULL || dest == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + // Do the copy. The casts to arrayOop are necessary to the copy_array API, + // even though the copy_array API also performs dynamic checks to ensure + // that src and dest are truly arrays (and are conformable). + // The copy_array mechanism is awkward and could be removed, but + // the compilers don't call this function except as a last resort, + // so it probably doesn't matter. + Klass::cast(src->klass())->copy_array((arrayOopDesc*)src, src_pos, + (arrayOopDesc*)dest, dest_pos, + length, thread); +} +JRT_END + +char* SharedRuntime::generate_class_cast_message( + JavaThread* thread, const char* objName) { + + // Get target class name from the checkcast instruction + vframeStream vfst(thread, true); + assert(!vfst.at_end(), "Java frame must exist"); + Bytecode_checkcast* cc = Bytecode_checkcast_at( + vfst.method()->bcp_from(vfst.bci())); + Klass* targetKlass = Klass::cast(vfst.method()->constants()->klass_at( + cc->index(), thread)); + return generate_class_cast_message(objName, targetKlass->external_name()); +} + +char* SharedRuntime::generate_class_cast_message( + const char* objName, const char* targetKlassName) { + const char* desc = " cannot be cast to "; + size_t msglen = strlen(objName) + strlen(desc) + strlen(targetKlassName) + 1; + + char* message = NEW_C_HEAP_ARRAY(char, msglen); + if (NULL == message) { + // out of memory - can't use a detailed message. Since caller is + // using a resource mark to free memory, returning this should be + // safe (caller won't explicitly delete it). + message = const_cast(objName); + } else { + jio_snprintf(message, msglen, "%s%s%s", objName, desc, targetKlassName); + } + return message; +} + +JRT_LEAF(void, SharedRuntime::reguard_yellow_pages()) + (void) JavaThread::current()->reguard_stack(); +JRT_END + + +// Handles the uncommon case in locking, i.e., contention or an inflated lock. +#ifndef PRODUCT +int SharedRuntime::_monitor_enter_ctr=0; +#endif +JRT_ENTRY_NO_ASYNC(void, SharedRuntime::complete_monitor_locking_C(oopDesc* _obj, BasicLock* lock, JavaThread* thread)) + oop obj(_obj); +#ifndef PRODUCT + _monitor_enter_ctr++; // monitor enter slow +#endif + if (PrintBiasedLockingStatistics) { + Atomic::inc(BiasedLocking::slow_path_entry_count_addr()); + } + Handle h_obj(THREAD, obj); + if (UseBiasedLocking) { + // Retry fast entry if bias is revoked to avoid unnecessary inflation + ObjectSynchronizer::fast_enter(h_obj, lock, true, CHECK); + } else { + ObjectSynchronizer::slow_enter(h_obj, lock, CHECK); + } + assert(!HAS_PENDING_EXCEPTION, "Should have no exception here"); +JRT_END + +#ifndef PRODUCT +int SharedRuntime::_monitor_exit_ctr=0; +#endif +// Handles the uncommon cases of monitor unlocking in compiled code +JRT_LEAF(void, SharedRuntime::complete_monitor_unlocking_C(oopDesc* _obj, BasicLock* lock)) + oop obj(_obj); +#ifndef PRODUCT + _monitor_exit_ctr++; // monitor exit slow +#endif + Thread* THREAD = JavaThread::current(); + // I'm not convinced we need the code contained by MIGHT_HAVE_PENDING anymore + // testing was unable to ever fire the assert that guarded it so I have removed it. + assert(!HAS_PENDING_EXCEPTION, "Do we need code below anymore?"); +#undef MIGHT_HAVE_PENDING +#ifdef MIGHT_HAVE_PENDING + // Save and restore any pending_exception around the exception mark. + // While the slow_exit must not throw an exception, we could come into + // this routine with one set. + oop pending_excep = NULL; + const char* pending_file; + int pending_line; + if (HAS_PENDING_EXCEPTION) { + pending_excep = PENDING_EXCEPTION; + pending_file = THREAD->exception_file(); + pending_line = THREAD->exception_line(); + CLEAR_PENDING_EXCEPTION; + } +#endif /* MIGHT_HAVE_PENDING */ + + { + // Exit must be non-blocking, and therefore no exceptions can be thrown. + EXCEPTION_MARK; + ObjectSynchronizer::slow_exit(obj, lock, THREAD); + } + +#ifdef MIGHT_HAVE_PENDING + if (pending_excep != NULL) { + THREAD->set_pending_exception(pending_excep, pending_file, pending_line); + } +#endif /* MIGHT_HAVE_PENDING */ +JRT_END + +#ifndef PRODUCT + +void SharedRuntime::print_statistics() { + ttyLocker ttyl; + if (xtty != NULL) xtty->head("statistics type='SharedRuntime'"); + + if (_monitor_enter_ctr ) tty->print_cr("%5d monitor enter slow", _monitor_enter_ctr); + if (_monitor_exit_ctr ) tty->print_cr("%5d monitor exit slow", _monitor_exit_ctr); + if (_throw_null_ctr) tty->print_cr("%5d implicit null throw", _throw_null_ctr); + + SharedRuntime::print_ic_miss_histogram(); + + if (CountRemovableExceptions) { + if (_nof_removable_exceptions > 0) { + Unimplemented(); // this counter is not yet incremented + tty->print_cr("Removable exceptions: %d", _nof_removable_exceptions); + } + } + + // Dump the JRT_ENTRY counters + if( _new_instance_ctr ) tty->print_cr("%5d new instance requires GC", _new_instance_ctr); + if( _new_array_ctr ) tty->print_cr("%5d new array requires GC", _new_array_ctr); + if( _multi1_ctr ) tty->print_cr("%5d multianewarray 1 dim", _multi1_ctr); + if( _multi2_ctr ) tty->print_cr("%5d multianewarray 2 dim", _multi2_ctr); + if( _multi3_ctr ) tty->print_cr("%5d multianewarray 3 dim", _multi3_ctr); + if( _multi4_ctr ) tty->print_cr("%5d multianewarray 4 dim", _multi4_ctr); + if( _multi5_ctr ) tty->print_cr("%5d multianewarray 5 dim", _multi5_ctr); + + tty->print_cr("%5d inline cache miss in compiled", _ic_miss_ctr ); + tty->print_cr("%5d wrong method", _wrong_method_ctr ); + tty->print_cr("%5d unresolved static call site", _resolve_static_ctr ); + tty->print_cr("%5d unresolved virtual call site", _resolve_virtual_ctr ); + tty->print_cr("%5d unresolved opt virtual call site", _resolve_opt_virtual_ctr ); + + if( _mon_enter_stub_ctr ) tty->print_cr("%5d monitor enter stub", _mon_enter_stub_ctr ); + if( _mon_exit_stub_ctr ) tty->print_cr("%5d monitor exit stub", _mon_exit_stub_ctr ); + if( _mon_enter_ctr ) tty->print_cr("%5d monitor enter slow", _mon_enter_ctr ); + if( _mon_exit_ctr ) tty->print_cr("%5d monitor exit slow", _mon_exit_ctr ); + if( _partial_subtype_ctr) tty->print_cr("%5d slow partial subtype", _partial_subtype_ctr ); + if( _jbyte_array_copy_ctr ) tty->print_cr("%5d byte array copies", _jbyte_array_copy_ctr ); + if( _jshort_array_copy_ctr ) tty->print_cr("%5d short array copies", _jshort_array_copy_ctr ); + if( _jint_array_copy_ctr ) tty->print_cr("%5d int array copies", _jint_array_copy_ctr ); + if( _jlong_array_copy_ctr ) tty->print_cr("%5d long array copies", _jlong_array_copy_ctr ); + if( _oop_array_copy_ctr ) tty->print_cr("%5d oop array copies", _oop_array_copy_ctr ); + if( _checkcast_array_copy_ctr ) tty->print_cr("%5d checkcast array copies", _checkcast_array_copy_ctr ); + if( _unsafe_array_copy_ctr ) tty->print_cr("%5d unsafe array copies", _unsafe_array_copy_ctr ); + if( _generic_array_copy_ctr ) tty->print_cr("%5d generic array copies", _generic_array_copy_ctr ); + if( _slow_array_copy_ctr ) tty->print_cr("%5d slow array copies", _slow_array_copy_ctr ); + if( _find_handler_ctr ) tty->print_cr("%5d find exception handler", _find_handler_ctr ); + if( _rethrow_ctr ) tty->print_cr("%5d rethrow handler", _rethrow_ctr ); + + if (xtty != NULL) xtty->tail("statistics"); +} + +inline double percent(int x, int y) { + return 100.0 * x / MAX2(y, 1); +} + +class MethodArityHistogram { + public: + enum { MAX_ARITY = 256 }; + private: + static int _arity_histogram[MAX_ARITY]; // histogram of #args + static int _size_histogram[MAX_ARITY]; // histogram of arg size in words + static int _max_arity; // max. arity seen + static int _max_size; // max. arg size seen + + static void add_method_to_histogram(nmethod* nm) { + methodOop m = nm->method(); + ArgumentCount args(m->signature()); + int arity = args.size() + (m->is_static() ? 0 : 1); + int argsize = m->size_of_parameters(); + arity = MIN2(arity, MAX_ARITY-1); + argsize = MIN2(argsize, MAX_ARITY-1); + int count = nm->method()->compiled_invocation_count(); + _arity_histogram[arity] += count; + _size_histogram[argsize] += count; + _max_arity = MAX2(_max_arity, arity); + _max_size = MAX2(_max_size, argsize); + } + + void print_histogram_helper(int n, int* histo, const char* name) { + const int N = MIN2(5, n); + tty->print_cr("\nHistogram of call arity (incl. rcvr, calls to compiled methods only):"); + double sum = 0; + double weighted_sum = 0; + int i; + for (i = 0; i <= n; i++) { sum += histo[i]; weighted_sum += i*histo[i]; } + double rest = sum; + double percent = sum / 100; + for (i = 0; i <= N; i++) { + rest -= histo[i]; + tty->print_cr("%4d: %7d (%5.1f%%)", i, histo[i], histo[i] / percent); + } + tty->print_cr("rest: %7d (%5.1f%%))", (int)rest, rest / percent); + tty->print_cr("(avg. %s = %3.1f, max = %d)", name, weighted_sum / sum, n); + } + + void print_histogram() { + tty->print_cr("\nHistogram of call arity (incl. rcvr, calls to compiled methods only):"); + print_histogram_helper(_max_arity, _arity_histogram, "arity"); + tty->print_cr("\nSame for parameter size (in words):"); + print_histogram_helper(_max_size, _size_histogram, "size"); + tty->cr(); + } + + public: + MethodArityHistogram() { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + _max_arity = _max_size = 0; + for (int i = 0; i < MAX_ARITY; i++) _arity_histogram[i] = _size_histogram [i] = 0; + CodeCache::nmethods_do(add_method_to_histogram); + print_histogram(); + } +}; + +int MethodArityHistogram::_arity_histogram[MethodArityHistogram::MAX_ARITY]; +int MethodArityHistogram::_size_histogram[MethodArityHistogram::MAX_ARITY]; +int MethodArityHistogram::_max_arity; +int MethodArityHistogram::_max_size; + +void SharedRuntime::print_call_statistics(int comp_total) { + tty->print_cr("Calls from compiled code:"); + int total = _nof_normal_calls + _nof_interface_calls + _nof_static_calls; + int mono_c = _nof_normal_calls - _nof_optimized_calls - _nof_megamorphic_calls; + int mono_i = _nof_interface_calls - _nof_optimized_interface_calls - _nof_megamorphic_interface_calls; + tty->print_cr("\t%9d (%4.1f%%) total non-inlined ", total, percent(total, total)); + tty->print_cr("\t%9d (%4.1f%%) virtual calls ", _nof_normal_calls, percent(_nof_normal_calls, total)); + tty->print_cr("\t %9d (%3.0f%%) inlined ", _nof_inlined_calls, percent(_nof_inlined_calls, _nof_normal_calls)); + tty->print_cr("\t %9d (%3.0f%%) optimized ", _nof_optimized_calls, percent(_nof_optimized_calls, _nof_normal_calls)); + tty->print_cr("\t %9d (%3.0f%%) monomorphic ", mono_c, percent(mono_c, _nof_normal_calls)); + tty->print_cr("\t %9d (%3.0f%%) megamorphic ", _nof_megamorphic_calls, percent(_nof_megamorphic_calls, _nof_normal_calls)); + tty->print_cr("\t%9d (%4.1f%%) interface calls ", _nof_interface_calls, percent(_nof_interface_calls, total)); + tty->print_cr("\t %9d (%3.0f%%) inlined ", _nof_inlined_interface_calls, percent(_nof_inlined_interface_calls, _nof_interface_calls)); + tty->print_cr("\t %9d (%3.0f%%) optimized ", _nof_optimized_interface_calls, percent(_nof_optimized_interface_calls, _nof_interface_calls)); + tty->print_cr("\t %9d (%3.0f%%) monomorphic ", mono_i, percent(mono_i, _nof_interface_calls)); + tty->print_cr("\t %9d (%3.0f%%) megamorphic ", _nof_megamorphic_interface_calls, percent(_nof_megamorphic_interface_calls, _nof_interface_calls)); + tty->print_cr("\t%9d (%4.1f%%) static/special calls", _nof_static_calls, percent(_nof_static_calls, total)); + tty->print_cr("\t %9d (%3.0f%%) inlined ", _nof_inlined_static_calls, percent(_nof_inlined_static_calls, _nof_static_calls)); + tty->cr(); + tty->print_cr("Note 1: counter updates are not MT-safe."); + tty->print_cr("Note 2: %% in major categories are relative to total non-inlined calls;"); + tty->print_cr(" %% in nested categories are relative to their category"); + tty->print_cr(" (and thus add up to more than 100%% with inlining)"); + tty->cr(); + + MethodArityHistogram h; +} +#endif + + +// --------------------------------------------------------------------------- +// Implementation of AdapterHandlerLibrary +const char* AdapterHandlerEntry::name = "I2C/C2I adapters"; +GrowableArray* AdapterHandlerLibrary::_fingerprints = NULL; +GrowableArray* AdapterHandlerLibrary::_handlers = NULL; +const int AdapterHandlerLibrary_size = 16*K; +u_char AdapterHandlerLibrary::_buffer[AdapterHandlerLibrary_size + 32]; + +void AdapterHandlerLibrary::initialize() { + if (_fingerprints != NULL) return; + _fingerprints = new(ResourceObj::C_HEAP)GrowableArray(32, true); + _handlers = new(ResourceObj::C_HEAP)GrowableArray(32, true); + // Index 0 reserved for the slow path handler + _fingerprints->append(0/*the never-allowed 0 fingerprint*/); + _handlers->append(NULL); + + // Create a special handler for abstract methods. Abstract methods + // are never compiled so an i2c entry is somewhat meaningless, but + // fill it in with something appropriate just in case. Pass handle + // wrong method for the c2i transitions. + address wrong_method = SharedRuntime::get_handle_wrong_method_stub(); + _fingerprints->append(0/*the never-allowed 0 fingerprint*/); + assert(_handlers->length() == AbstractMethodHandler, "in wrong slot"); + _handlers->append(new AdapterHandlerEntry(StubRoutines::throw_AbstractMethodError_entry(), + wrong_method, wrong_method)); +} + +int AdapterHandlerLibrary::get_create_adapter_index(methodHandle method) { + // Use customized signature handler. Need to lock around updates to the + // _fingerprints array (it is not safe for concurrent readers and a single + // writer: this can be fixed if it becomes a problem). + + // Shouldn't be here if running -Xint + if (Arguments::mode() == Arguments::_int) { + ShouldNotReachHere(); + } + + // Get the address of the ic_miss handlers before we grab the + // AdapterHandlerLibrary_lock. This fixes bug 6236259 which + // was caused by the initialization of the stubs happening + // while we held the lock and then notifying jvmti while + // holding it. This just forces the initialization to be a little + // earlier. + address ic_miss = SharedRuntime::get_ic_miss_stub(); + assert(ic_miss != NULL, "must have handler"); + + int result; + BufferBlob *B = NULL; + uint64_t fingerprint; + { + MutexLocker mu(AdapterHandlerLibrary_lock); + // make sure data structure is initialized + initialize(); + + if (method->is_abstract()) { + return AbstractMethodHandler; + } + + // Lookup method signature's fingerprint + fingerprint = Fingerprinter(method).fingerprint(); + assert( fingerprint != CONST64( 0), "no zero fingerprints allowed" ); + // Fingerprints are small fixed-size condensed representations of + // signatures. If the signature is too large, it won't fit in a + // fingerprint. Signatures which cannot support a fingerprint get a new i2c + // adapter gen'd each time, instead of searching the cache for one. This -1 + // game can be avoided if I compared signatures instead of using + // fingerprints. However, -1 fingerprints are very rare. + if( fingerprint != UCONST64(-1) ) { // If this is a cache-able fingerprint + // Turns out i2c adapters do not care what the return value is. Mask it + // out so signatures that only differ in return type will share the same + // adapter. + fingerprint &= ~(SignatureIterator::result_feature_mask << SignatureIterator::static_feature_size); + // Search for a prior existing i2c/c2i adapter + int index = _fingerprints->find(fingerprint); + if( index >= 0 ) return index; // Found existing handlers? + } else { + // Annoyingly, I end up adding -1 fingerprints to the array of handlers, + // because I need a unique handler index. It cannot be scanned for + // because all -1's look alike. Instead, the matching index is passed out + // and immediately used to collect the 2 return values (the c2i and i2c + // adapters). + } + + // Create I2C & C2I handlers + ResourceMark rm; + // Improve alignment slightly + u_char *buf = (u_char*)(((intptr_t)_buffer + CodeEntryAlignment-1) & ~(CodeEntryAlignment-1)); + CodeBuffer buffer(buf, AdapterHandlerLibrary_size); + short buffer_locs[20]; + buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs, + sizeof(buffer_locs)/sizeof(relocInfo)); + MacroAssembler _masm(&buffer); + + // Fill in the signature array, for the calling-convention call. + int total_args_passed = method->size_of_parameters(); // All args on stack + + BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType,total_args_passed); + VMRegPair * regs = NEW_RESOURCE_ARRAY(VMRegPair ,total_args_passed); + int i=0; + if( !method->is_static() ) // Pass in receiver first + sig_bt[i++] = T_OBJECT; + for( SignatureStream ss(method->signature()); !ss.at_return_type(); ss.next()) { + sig_bt[i++] = ss.type(); // Collect remaining bits of signature + if( ss.type() == T_LONG || ss.type() == T_DOUBLE ) + sig_bt[i++] = T_VOID; // Longs & doubles take 2 Java slots + } + assert( i==total_args_passed, "" ); + + // Now get the re-packed compiled-Java layout. + int comp_args_on_stack; + + // Get a description of the compiled java calling convention and the largest used (VMReg) stack slot usage + comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, false); + + AdapterHandlerEntry* entry = SharedRuntime::generate_i2c2i_adapters(&_masm, + total_args_passed, + comp_args_on_stack, + sig_bt, + regs); + + B = BufferBlob::create(AdapterHandlerEntry::name, &buffer); + if (B == NULL) return -2; // Out of CodeCache space + entry->relocate(B->instructions_begin()); +#ifndef PRODUCT + // debugging suppport + if (PrintAdapterHandlers) { + tty->cr(); + tty->print_cr("i2c argument handler #%d for: %s %s (fingerprint = 0x%llx, %d bytes generated)", + _handlers->length(), (method->is_static() ? "static" : "receiver"), + method->signature()->as_C_string(), fingerprint, buffer.code_size() ); + tty->print_cr("c2i argument handler starts at %p",entry->get_c2i_entry()); + Disassembler::decode(entry->get_i2c_entry(), entry->get_i2c_entry() + buffer.code_size()); + } +#endif + + // add handlers to library + _fingerprints->append(fingerprint); + _handlers->append(entry); + // set handler index + assert(_fingerprints->length() == _handlers->length(), "sanity check"); + result = _fingerprints->length() - 1; + } + // Outside of the lock + if (B != NULL) { + char blob_id[256]; + jio_snprintf(blob_id, + sizeof(blob_id), + "%s(" PTR64_FORMAT ")@" PTR_FORMAT, + AdapterHandlerEntry::name, + fingerprint, + B->instructions_begin()); + VTune::register_stub(blob_id, B->instructions_begin(), B->instructions_end()); + Forte::register_stub(blob_id, B->instructions_begin(), B->instructions_end()); + + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated(blob_id, + B->instructions_begin(), + B->instructions_end()); + } + } + return result; +} + +void AdapterHandlerEntry::relocate(address new_base) { + ptrdiff_t delta = new_base - _i2c_entry; + _i2c_entry += delta; + _c2i_entry += delta; + _c2i_unverified_entry += delta; +} + +// Create a native wrapper for this native method. The wrapper converts the +// java compiled calling convention to the native convention, handlizes +// arguments, and transitions to native. On return from the native we transition +// back to java blocking if a safepoint is in progress. +nmethod *AdapterHandlerLibrary::create_native_wrapper(methodHandle method) { + ResourceMark rm; + nmethod* nm = NULL; + + if (PrintCompilation) { + ttyLocker ttyl; + tty->print("--- n%s ", (method->is_synchronized() ? "s" : " ")); + method->print_short_name(tty); + if (method->is_static()) { + tty->print(" (static)"); + } + tty->cr(); + } + + assert(method->has_native_function(), "must have something valid to call!"); + + { + // perform the work while holding the lock, but perform any printing outside the lock + MutexLocker mu(AdapterHandlerLibrary_lock); + // See if somebody beat us to it + nm = method->code(); + if (nm) { + return nm; + } + + // Improve alignment slightly + u_char* buf = (u_char*)(((intptr_t)_buffer + CodeEntryAlignment-1) & ~(CodeEntryAlignment-1)); + CodeBuffer buffer(buf, AdapterHandlerLibrary_size); + // Need a few relocation entries + double locs_buf[20]; + buffer.insts()->initialize_shared_locs((relocInfo*)locs_buf, sizeof(locs_buf) / sizeof(relocInfo)); + MacroAssembler _masm(&buffer); + + // Fill in the signature array, for the calling-convention call. + int total_args_passed = method->size_of_parameters(); + + BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType,total_args_passed); + VMRegPair * regs = NEW_RESOURCE_ARRAY(VMRegPair ,total_args_passed); + int i=0; + if( !method->is_static() ) // Pass in receiver first + sig_bt[i++] = T_OBJECT; + SignatureStream ss(method->signature()); + for( ; !ss.at_return_type(); ss.next()) { + sig_bt[i++] = ss.type(); // Collect remaining bits of signature + if( ss.type() == T_LONG || ss.type() == T_DOUBLE ) + sig_bt[i++] = T_VOID; // Longs & doubles take 2 Java slots + } + assert( i==total_args_passed, "" ); + BasicType ret_type = ss.type(); + + // Now get the compiled-Java layout as input arguments + int comp_args_on_stack; + comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, false); + + // Generate the compiled-to-native wrapper code + nm = SharedRuntime::generate_native_wrapper(&_masm, + method, + total_args_passed, + comp_args_on_stack, + sig_bt,regs, + ret_type); + } + + // Must unlock before calling set_code + // Install the generated code. + if (nm != NULL) { + method->set_code(method, nm); + nm->post_compiled_method_load_event(); + } else { + // CodeCache is full, disable compilation + // Ought to log this but compile log is only per compile thread + // and we're some non descript Java thread. + UseInterpreter = true; + if (UseCompiler || AlwaysCompileLoopMethods ) { +#ifndef PRODUCT + warning("CodeCache is full. Compiler has been disabled"); + if (CompileTheWorld || ExitOnFullCodeCache) { + before_exit(JavaThread::current()); + exit_globals(); // will delete tty + vm_direct_exit(CompileTheWorld ? 0 : 1); + } +#endif + UseCompiler = false; + AlwaysCompileLoopMethods = false; + } + } + return nm; +} + +// ------------------------------------------------------------------------- +// Java-Java calling convention +// (what you use when Java calls Java) + +//------------------------------name_for_receiver---------------------------------- +// For a given signature, return the VMReg for parameter 0. +VMReg SharedRuntime::name_for_receiver() { + VMRegPair regs; + BasicType sig_bt = T_OBJECT; + (void) java_calling_convention(&sig_bt, ®s, 1, true); + // Return argument 0 register. In the LP64 build pointers + // take 2 registers, but the VM wants only the 'main' name. + return regs.first(); +} + +VMRegPair *SharedRuntime::find_callee_arguments(symbolOop sig, bool is_static, int* arg_size) { + // This method is returning a data structure allocating as a + // ResourceObject, so do not put any ResourceMarks in here. + char *s = sig->as_C_string(); + int len = (int)strlen(s); + *s++; len--; // Skip opening paren + char *t = s+len; + while( *(--t) != ')' ) ; // Find close paren + + BasicType *sig_bt = NEW_RESOURCE_ARRAY( BasicType, 256 ); + VMRegPair *regs = NEW_RESOURCE_ARRAY( VMRegPair, 256 ); + int cnt = 0; + if (!is_static) { + sig_bt[cnt++] = T_OBJECT; // Receiver is argument 0; not in signature + } + + while( s < t ) { + switch( *s++ ) { // Switch on signature character + case 'B': sig_bt[cnt++] = T_BYTE; break; + case 'C': sig_bt[cnt++] = T_CHAR; break; + case 'D': sig_bt[cnt++] = T_DOUBLE; sig_bt[cnt++] = T_VOID; break; + case 'F': sig_bt[cnt++] = T_FLOAT; break; + case 'I': sig_bt[cnt++] = T_INT; break; + case 'J': sig_bt[cnt++] = T_LONG; sig_bt[cnt++] = T_VOID; break; + case 'S': sig_bt[cnt++] = T_SHORT; break; + case 'Z': sig_bt[cnt++] = T_BOOLEAN; break; + case 'V': sig_bt[cnt++] = T_VOID; break; + case 'L': // Oop + while( *s++ != ';' ) ; // Skip signature + sig_bt[cnt++] = T_OBJECT; + break; + case '[': { // Array + do { // Skip optional size + while( *s >= '0' && *s <= '9' ) s++; + } while( *s++ == '[' ); // Nested arrays? + // Skip element type + if( s[-1] == 'L' ) + while( *s++ != ';' ) ; // Skip signature + sig_bt[cnt++] = T_ARRAY; + break; + } + default : ShouldNotReachHere(); + } + } + assert( cnt < 256, "grow table size" ); + + int comp_args_on_stack; + comp_args_on_stack = java_calling_convention(sig_bt, regs, cnt, true); + + // the calling convention doesn't count out_preserve_stack_slots so + // we must add that in to get "true" stack offsets. + + if (comp_args_on_stack) { + for (int i = 0; i < cnt; i++) { + VMReg reg1 = regs[i].first(); + if( reg1->is_stack()) { + // Yuck + reg1 = reg1->bias(out_preserve_stack_slots()); + } + VMReg reg2 = regs[i].second(); + if( reg2->is_stack()) { + // Yuck + reg2 = reg2->bias(out_preserve_stack_slots()); + } + regs[i].set_pair(reg2, reg1); + } + } + + // results + *arg_size = cnt; + return regs; +} + +// OSR Migration Code +// +// This code is used convert interpreter frames into compiled frames. It is +// called from very start of a compiled OSR nmethod. A temp array is +// allocated to hold the interesting bits of the interpreter frame. All +// active locks are inflated to allow them to move. The displaced headers and +// active interpeter locals are copied into the temp buffer. Then we return +// back to the compiled code. The compiled code then pops the current +// interpreter frame off the stack and pushes a new compiled frame. Then it +// copies the interpreter locals and displaced headers where it wants. +// Finally it calls back to free the temp buffer. +// +// All of this is done NOT at any Safepoint, nor is any safepoint or GC allowed. + +JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *thread) ) + +#ifdef IA64 + ShouldNotReachHere(); // NYI +#endif /* IA64 */ + + // + // This code is dependent on the memory layout of the interpreter local + // array and the monitors. On all of our platforms the layout is identical + // so this code is shared. If some platform lays the their arrays out + // differently then this code could move to platform specific code or + // the code here could be modified to copy items one at a time using + // frame accessor methods and be platform independent. + + frame fr = thread->last_frame(); + assert( fr.is_interpreted_frame(), "" ); + assert( fr.interpreter_frame_expression_stack_size()==0, "only handle empty stacks" ); + + // Figure out how many monitors are active. + int active_monitor_count = 0; + for( BasicObjectLock *kptr = fr.interpreter_frame_monitor_end(); + kptr < fr.interpreter_frame_monitor_begin(); + kptr = fr.next_monitor_in_interpreter_frame(kptr) ) { + if( kptr->obj() != NULL ) active_monitor_count++; + } + + // QQQ we could place number of active monitors in the array so that compiled code + // could double check it. + + methodOop moop = fr.interpreter_frame_method(); + int max_locals = moop->max_locals(); + // Allocate temp buffer, 1 word per local & 2 per active monitor + int buf_size_words = max_locals + active_monitor_count*2; + intptr_t *buf = NEW_C_HEAP_ARRAY(intptr_t,buf_size_words); + + // Copy the locals. Order is preserved so that loading of longs works. + // Since there's no GC I can copy the oops blindly. + assert( sizeof(HeapWord)==sizeof(intptr_t), "fix this code"); + if (TaggedStackInterpreter) { + for (int i = 0; i < max_locals; i++) { + // copy only each local separately to the buffer avoiding the tag + buf[i] = *fr.interpreter_frame_local_at(max_locals-i-1); + } + } else { + Copy::disjoint_words( + (HeapWord*)fr.interpreter_frame_local_at(max_locals-1), + (HeapWord*)&buf[0], + max_locals); + } + + // Inflate locks. Copy the displaced headers. Be careful, there can be holes. + int i = max_locals; + for( BasicObjectLock *kptr2 = fr.interpreter_frame_monitor_end(); + kptr2 < fr.interpreter_frame_monitor_begin(); + kptr2 = fr.next_monitor_in_interpreter_frame(kptr2) ) { + if( kptr2->obj() != NULL) { // Avoid 'holes' in the monitor array + BasicLock *lock = kptr2->lock(); + // Inflate so the displaced header becomes position-independent + if (lock->displaced_header()->is_unlocked()) + ObjectSynchronizer::inflate_helper(kptr2->obj()); + // Now the displaced header is free to move + buf[i++] = (intptr_t)lock->displaced_header(); + buf[i++] = (intptr_t)kptr2->obj(); + } + } + assert( i - max_locals == active_monitor_count*2, "found the expected number of monitors" ); + + return buf; +JRT_END + +JRT_LEAF(void, SharedRuntime::OSR_migration_end( intptr_t* buf) ) + FREE_C_HEAP_ARRAY(intptr_t,buf); +JRT_END + +#ifndef PRODUCT +bool AdapterHandlerLibrary::contains(CodeBlob* b) { + + for (int i = 0 ; i < _handlers->length() ; i++) { + AdapterHandlerEntry* a = get_entry(i); + if ( a != NULL && b == CodeCache::find_blob(a->get_i2c_entry()) ) return true; + } + return false; +} + +void AdapterHandlerLibrary::print_handler(CodeBlob* b) { + + for (int i = 0 ; i < _handlers->length() ; i++) { + AdapterHandlerEntry* a = get_entry(i); + if ( a != NULL && b == CodeCache::find_blob(a->get_i2c_entry()) ) { + tty->print("Adapter for signature: "); + // Fingerprinter::print(_fingerprints->at(i)); + tty->print("0x%" FORMAT64_MODIFIER "x", _fingerprints->at(i)); + tty->print_cr(" i2c: " INTPTR_FORMAT " c2i: " INTPTR_FORMAT " c2iUV: " INTPTR_FORMAT, + a->get_i2c_entry(), a->get_c2i_entry(), a->get_c2i_unverified_entry()); + + return; + } + } + assert(false, "Should have found handler"); +} +#endif /* PRODUCT */ diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.hpp b/hotspot/src/share/vm/runtime/sharedRuntime.hpp new file mode 100644 index 00000000000..b91837f36e4 --- /dev/null +++ b/hotspot/src/share/vm/runtime/sharedRuntime.hpp @@ -0,0 +1,536 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class AdapterHandlerEntry; +class vframeStream; + +// Runtime is the base class for various runtime interfaces +// (InterpreterRuntime, CompilerRuntime, etc.). It provides +// shared functionality such as exception forwarding (C++ to +// Java exceptions), locking/unlocking mechanisms, statistical +// information, etc. + +class SharedRuntime: AllStatic { + private: + static methodHandle resolve_sub_helper(JavaThread *thread, + bool is_virtual, + bool is_optimized, TRAPS); + + // Shared stub locations + + static RuntimeStub* _wrong_method_blob; + static RuntimeStub* _ic_miss_blob; + static RuntimeStub* _resolve_opt_virtual_call_blob; + static RuntimeStub* _resolve_virtual_call_blob; + static RuntimeStub* _resolve_static_call_blob; + + static SafepointBlob* _polling_page_safepoint_handler_blob; + static SafepointBlob* _polling_page_return_handler_blob; +#ifdef COMPILER2 + static ExceptionBlob* _exception_blob; + static UncommonTrapBlob* _uncommon_trap_blob; +#endif // COMPILER2 + +#ifndef PRODUCT + + // Counters + static int _nof_megamorphic_calls; // total # of megamorphic calls (through vtable) + +#endif // !PRODUCT + public: + // The following arithmetic routines are used on platforms that do + // not have machine instructions to implement their functionality. + // Do not remove these. + + // long arithmetics + static jlong lmul(jlong y, jlong x); + static jlong ldiv(jlong y, jlong x); + static jlong lrem(jlong y, jlong x); + + // float and double remainder + static jfloat frem(jfloat x, jfloat y); + static jdouble drem(jdouble x, jdouble y); + + // float conversion (needs to set appropriate rounding mode) + static jint f2i (jfloat x); + static jlong f2l (jfloat x); + static jint d2i (jdouble x); + static jlong d2l (jdouble x); + static jfloat d2f (jdouble x); + static jfloat l2f (jlong x); + static jdouble l2d (jlong x); + + // double trigonometrics and transcendentals + static jdouble dsin(jdouble x); + static jdouble dcos(jdouble x); + static jdouble dtan(jdouble x); + static jdouble dlog(jdouble x); + static jdouble dlog10(jdouble x); + static jdouble dexp(jdouble x); + static jdouble dpow(jdouble x, jdouble y); + + + // exception handling across interpreter/compiler boundaries + static address raw_exception_handler_for_return_address(address return_address); + static address exception_handler_for_return_address(address return_address); + + // exception handling and implicit exceptions + static address compute_compiled_exc_handler(nmethod* nm, address ret_pc, Handle& exception, + bool force_unwind, bool top_frame_only); + enum ImplicitExceptionKind { + IMPLICIT_NULL, + IMPLICIT_DIVIDE_BY_ZERO, + STACK_OVERFLOW + }; + static void throw_AbstractMethodError(JavaThread* thread); + static void throw_ArithmeticException(JavaThread* thread); + static void throw_NullPointerException(JavaThread* thread); + static void throw_NullPointerException_at_call(JavaThread* thread); + static void throw_StackOverflowError(JavaThread* thread); + static address continuation_for_implicit_exception(JavaThread* thread, + address faulting_pc, + ImplicitExceptionKind exception_kind); + + // Shared stub locations + static address get_poll_stub(address pc); + + static address get_ic_miss_stub() { + assert(_ic_miss_blob!= NULL, "oops"); + return _ic_miss_blob->instructions_begin(); + } + + static address get_handle_wrong_method_stub() { + assert(_wrong_method_blob!= NULL, "oops"); + return _wrong_method_blob->instructions_begin(); + } + +#ifdef COMPILER2 + static void generate_uncommon_trap_blob(void); + static UncommonTrapBlob* uncommon_trap_blob() { return _uncommon_trap_blob; } +#endif // COMPILER2 + + static address get_resolve_opt_virtual_call_stub(){ + assert(_resolve_opt_virtual_call_blob != NULL, "oops"); + return _resolve_opt_virtual_call_blob->instructions_begin(); + } + static address get_resolve_virtual_call_stub() { + assert(_resolve_virtual_call_blob != NULL, "oops"); + return _resolve_virtual_call_blob->instructions_begin(); + } + static address get_resolve_static_call_stub() { + assert(_resolve_static_call_blob != NULL, "oops"); + return _resolve_static_call_blob->instructions_begin(); + } + + static SafepointBlob* polling_page_return_handler_blob() { return _polling_page_return_handler_blob; } + static SafepointBlob* polling_page_safepoint_handler_blob() { return _polling_page_safepoint_handler_blob; } + + // Counters +#ifndef PRODUCT + static address nof_megamorphic_calls_addr() { return (address)&_nof_megamorphic_calls; } +#endif // PRODUCT + + // Helper routine for full-speed JVMTI exception throwing support + static void throw_and_post_jvmti_exception(JavaThread *thread, Handle h_exception); + static void throw_and_post_jvmti_exception(JavaThread *thread, symbolOop name, const char *message = NULL); + + // To be used as the entry point for unresolved native methods. + static address native_method_throw_unsatisfied_link_error_entry(); + + // bytecode tracing is only used by the TraceBytecodes + static intptr_t trace_bytecode(JavaThread* thread, intptr_t preserve_this_value, intptr_t tos, intptr_t tos2) PRODUCT_RETURN0; + + // Used to back off a spin lock that is under heavy contention + static void yield_all(JavaThread* thread, int attempts = 0); + + static oop retrieve_receiver( symbolHandle sig, frame caller ); + + static void verify_caller_frame(frame caller_frame, methodHandle callee_method) PRODUCT_RETURN; + static methodHandle find_callee_method_inside_interpreter(frame caller_frame, methodHandle caller_method, int bci) PRODUCT_RETURN_(return methodHandle();); + + static void register_finalizer(JavaThread* thread, oopDesc* obj); + + // dtrace notifications + static int dtrace_object_alloc(oopDesc* o); + static int dtrace_object_alloc_base(Thread* thread, oopDesc* o); + static int dtrace_method_entry(JavaThread* thread, methodOopDesc* m); + static int dtrace_method_exit(JavaThread* thread, methodOopDesc* m); + + // Utility method for retrieving the Java thread id, returns 0 if the + // thread is not a well formed Java thread. + static jlong get_java_tid(Thread* thread); + + + // used by native wrappers to reenable yellow if overflow happened in native code + static void reguard_yellow_pages(); + + /** + * Fill in the "X cannot be cast to a Y" message for ClassCastException + * + * @param thr the current thread + * @param name the name of the class of the object attempted to be cast + * @return the dynamically allocated exception message (must be freed + * by the caller using a resource mark) + * + * BCP must refer to the current 'checkcast' opcode for the frame + * on top of the stack. + * The caller (or one of it's callers) must use a ResourceMark + * in order to correctly free the result. + */ + static char* generate_class_cast_message(JavaThread* thr, const char* name); + + /** + * Fill in the "X cannot be cast to a Y" message for ClassCastException + * + * @param name the name of the class of the object attempted to be cast + * @param klass the name of the target klass attempt + * @return the dynamically allocated exception message (must be freed + * by the caller using a resource mark) + * + * This version does not require access the frame, so it can be called + * from interpreted code + * The caller (or one of it's callers) must use a ResourceMark + * in order to correctly free the result. + */ + static char* generate_class_cast_message(const char* name, const char* klass); + + // Resolves a call site- may patch in the destination of the call into the + // compiled code. + static methodHandle resolve_helper(JavaThread *thread, + bool is_virtual, + bool is_optimized, TRAPS); + + static void generate_stubs(void); + + private: + // deopt blob + static void generate_deopt_blob(void); + static DeoptimizationBlob* _deopt_blob; + + public: + static DeoptimizationBlob* deopt_blob(void) { return _deopt_blob; } + + // Resets a call-site in compiled code so it will get resolved again. + static methodHandle reresolve_call_site(JavaThread *thread, TRAPS); + + // In the code prolog, if the klass comparison fails, the inline cache + // misses and the call site is patched to megamorphic + static methodHandle handle_ic_miss_helper(JavaThread* thread, TRAPS); + + // Find the method that called us. + static methodHandle find_callee_method(JavaThread* thread, TRAPS); + + + private: + static Handle find_callee_info(JavaThread* thread, + Bytecodes::Code& bc, + CallInfo& callinfo, TRAPS); + static Handle find_callee_info_helper(JavaThread* thread, + vframeStream& vfst, + Bytecodes::Code& bc, + CallInfo& callinfo, TRAPS); + + static address clean_virtual_call_entry(); + static address clean_opt_virtual_call_entry(); + static address clean_static_call_entry(); + + public: + + + static void create_native_wrapper (JavaThread* thread, methodOop method); + + // Read the array of BasicTypes from a Java signature, and compute where + // compiled Java code would like to put the results. Values in reg_lo and + // reg_hi refer to 4-byte quantities. Values less than SharedInfo::stack0 are + // registers, those above refer to 4-byte stack slots. All stack slots are + // based off of the window top. SharedInfo::stack0 refers to the first usable + // slot in the bottom of the frame. SharedInfo::stack0+1 refers to the memory word + // 4-bytes higher. So for sparc because the register window save area is at + // the bottom of the frame the first 16 words will be skipped and SharedInfo::stack0 + // will be just above it. ( + // return value is the maximum number of VMReg stack slots the convention will use. + static int java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, int total_args_passed, int is_outgoing); + + // Ditto except for calling C + static int c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, int total_args_passed); + + // Generate I2C and C2I adapters. These adapters are simple argument marshalling + // blobs. Unlike adapters in the tiger and earlier releases the code in these + // blobs does not create a new frame and are therefore virtually invisible + // to the stack walking code. In general these blobs extend the callers stack + // as needed for the conversion of argument locations. + + // When calling a c2i blob the code will always call the interpreter even if + // by the time we reach the blob there is compiled code available. This allows + // the blob to pass the incoming stack pointer (the sender sp) in a known + // location for the interpreter to record. This is used by the frame code + // to correct the sender code to match up with the stack pointer when the + // thread left the compiled code. In addition it allows the interpreter + // to remove the space the c2i adapter allocated to do it argument conversion. + + // Although a c2i blob will always run interpreted even if compiled code is + // present if we see that compiled code is present the compiled call site + // will be patched/re-resolved so that later calls will run compiled. + + // Aditionally a c2i blob need to have a unverified entry because it can be reached + // in situations where the call site is an inlined cache site and may go megamorphic. + + // A i2c adapter is simpler than the c2i adapter. This is because it is assumed + // that the interpreter before it does any call dispatch will record the current + // stack pointer in the interpreter frame. On return it will restore the stack + // pointer as needed. This means the i2c adapter code doesn't need any special + // handshaking path with compiled code to keep the stack walking correct. + + static AdapterHandlerEntry* generate_i2c2i_adapters(MacroAssembler *_masm, + int total_args_passed, + int max_arg, + const BasicType *sig_bt, + const VMRegPair *regs); + + // OSR support + + // OSR_migration_begin will extract the jvm state from an interpreter + // frame (locals, monitors) and store the data in a piece of C heap + // storage. This then allows the interpreter frame to be removed from the + // stack and the OSR nmethod to be called. That method is called with a + // pointer to the C heap storage. This pointer is the return value from + // OSR_migration_begin. + + static intptr_t* OSR_migration_begin( JavaThread *thread); + + // OSR_migration_end is a trivial routine. It is called after the compiled + // method has extracted the jvm state from the C heap that OSR_migration_begin + // created. It's entire job is to simply free this storage. + static void OSR_migration_end ( intptr_t* buf); + + // Convert a sig into a calling convention register layout + // and find interesting things about it. + static VMRegPair* find_callee_arguments(symbolOop sig, bool is_static, int *arg_size); + static VMReg name_for_receiver(); + + // "Top of Stack" slots that may be unused by the calling convention but must + // otherwise be preserved. + // On Intel these are not necessary and the value can be zero. + // On Sparc this describes the words reserved for storing a register window + // when an interrupt occurs. + static uint out_preserve_stack_slots(); + + // Save and restore a native result + static void save_native_result(MacroAssembler *_masm, BasicType ret_type, int frame_slots ); + static void restore_native_result(MacroAssembler *_masm, BasicType ret_type, int frame_slots ); + + // Generate a native wrapper for a given method. The method takes arguments + // in the Java compiled code convention, marshals them to the native + // convention (handlizes oops, etc), transitions to native, makes the call, + // returns to java state (possibly blocking), unhandlizes any result and + // returns. + static nmethod *generate_native_wrapper(MacroAssembler* masm, + methodHandle method, + int total_args_passed, + int max_arg, + BasicType *sig_bt, + VMRegPair *regs, + BasicType ret_type ); + + // A compiled caller has just called the interpreter, but compiled code + // exists. Patch the caller so he no longer calls into the interpreter. + static void fixup_callers_callsite(methodOopDesc* moop, address ret_pc); + + // Slow-path Locking and Unlocking + static void complete_monitor_locking_C(oopDesc* obj, BasicLock* lock, JavaThread* thread); + static void complete_monitor_unlocking_C(oopDesc* obj, BasicLock* lock); + + // Resolving of calls + static address resolve_static_call_C (JavaThread *thread); + static address resolve_virtual_call_C (JavaThread *thread); + static address resolve_opt_virtual_call_C(JavaThread *thread); + + // arraycopy, the non-leaf version. (See StubRoutines for all the leaf calls.) + static void slow_arraycopy_C(oopDesc* src, jint src_pos, + oopDesc* dest, jint dest_pos, + jint length, JavaThread* thread); + + // handle ic miss with caller being compiled code + // wrong method handling (inline cache misses, zombie methods) + static address handle_wrong_method(JavaThread* thread); + static address handle_wrong_method_ic_miss(JavaThread* thread); + +#ifndef PRODUCT + + // Collect and print inline cache miss statistics + private: + enum { maxICmiss_count = 100 }; + static int _ICmiss_index; // length of IC miss histogram + static int _ICmiss_count[maxICmiss_count]; // miss counts + static address _ICmiss_at[maxICmiss_count]; // miss addresses + static void trace_ic_miss(address at); + + public: + static int _monitor_enter_ctr; // monitor enter slow + static int _monitor_exit_ctr; // monitor exit slow + static int _throw_null_ctr; // throwing a null-pointer exception + static int _ic_miss_ctr; // total # of IC misses + static int _wrong_method_ctr; + static int _resolve_static_ctr; + static int _resolve_virtual_ctr; + static int _resolve_opt_virtual_ctr; + static int _implicit_null_throws; + static int _implicit_div0_throws; + + static int _jbyte_array_copy_ctr; // Slow-path byte array copy + static int _jshort_array_copy_ctr; // Slow-path short array copy + static int _jint_array_copy_ctr; // Slow-path int array copy + static int _jlong_array_copy_ctr; // Slow-path long array copy + static int _oop_array_copy_ctr; // Slow-path oop array copy + static int _checkcast_array_copy_ctr; // Slow-path oop array copy, with cast + static int _unsafe_array_copy_ctr; // Slow-path includes alignment checks + static int _generic_array_copy_ctr; // Slow-path includes type decoding + static int _slow_array_copy_ctr; // Slow-path failed out to a method call + + static int _new_instance_ctr; // 'new' object requires GC + static int _new_array_ctr; // 'new' array requires GC + static int _multi1_ctr, _multi2_ctr, _multi3_ctr, _multi4_ctr, _multi5_ctr; + static int _find_handler_ctr; // find exception handler + static int _rethrow_ctr; // rethrow exception + static int _mon_enter_stub_ctr; // monitor enter stub + static int _mon_exit_stub_ctr; // monitor exit stub + static int _mon_enter_ctr; // monitor enter slow + static int _mon_exit_ctr; // monitor exit slow + static int _partial_subtype_ctr; // SubRoutines::partial_subtype_check + + // Statistics code + // stats for "normal" compiled calls (non-interface) + static int _nof_normal_calls; // total # of calls + static int _nof_optimized_calls; // total # of statically-bound calls + static int _nof_inlined_calls; // total # of inlined normal calls + static int _nof_static_calls; // total # of calls to static methods or super methods (invokespecial) + static int _nof_inlined_static_calls; // total # of inlined static calls + // stats for compiled interface calls + static int _nof_interface_calls; // total # of compiled calls + static int _nof_optimized_interface_calls; // total # of statically-bound interface calls + static int _nof_inlined_interface_calls; // total # of inlined interface calls + static int _nof_megamorphic_interface_calls;// total # of megamorphic interface calls + // stats for runtime exceptions + static int _nof_removable_exceptions; // total # of exceptions that could be replaced by branches due to inlining + + public: // for compiler + static address nof_normal_calls_addr() { return (address)&_nof_normal_calls; } + static address nof_optimized_calls_addr() { return (address)&_nof_optimized_calls; } + static address nof_inlined_calls_addr() { return (address)&_nof_inlined_calls; } + static address nof_static_calls_addr() { return (address)&_nof_static_calls; } + static address nof_inlined_static_calls_addr() { return (address)&_nof_inlined_static_calls; } + static address nof_interface_calls_addr() { return (address)&_nof_interface_calls; } + static address nof_optimized_interface_calls_addr() { return (address)&_nof_optimized_interface_calls; } + static address nof_inlined_interface_calls_addr() { return (address)&_nof_inlined_interface_calls; } + static address nof_megamorphic_interface_calls_addr() { return (address)&_nof_megamorphic_interface_calls; } + static void print_call_statistics(int comp_total); + static void print_statistics(); + static void print_ic_miss_histogram(); + +#endif // PRODUCT +}; + + +// --------------------------------------------------------------------------- +// Implementation of AdapterHandlerLibrary +// +// This library manages argument marshaling adapters and native wrappers. +// There are 2 flavors of adapters: I2C and C2I. +// +// The I2C flavor takes a stock interpreted call setup, marshals the arguments +// for a Java-compiled call, and jumps to Rmethod-> code()-> +// instructions_begin(). It is broken to call it without an nmethod assigned. +// The usual behavior is to lift any register arguments up out of the stack +// and possibly re-pack the extra arguments to be contigious. I2C adapters +// will save what the interpreter's stack pointer will be after arguments are +// popped, then adjust the interpreter's frame size to force alignment and +// possibly to repack the arguments. After re-packing, it jumps to the +// compiled code start. There are no safepoints in this adapter code and a GC +// cannot happen while marshaling is in progress. +// +// The C2I flavor takes a stock compiled call setup plus the target method in +// Rmethod, marshals the arguments for an interpreted call and jumps to +// Rmethod->_i2i_entry. On entry, the interpreted frame has not yet been +// setup. Compiled frames are fixed-size and the args are likely not in the +// right place. Hence all the args will likely be copied into the +// interpreter's frame, forcing that frame to grow. The compiled frame's +// outgoing stack args will be dead after the copy. +// +// Native wrappers, like adapters, marshal arguments. Unlike adapters they +// also perform an offical frame push & pop. They have a call to the native +// routine in their middles and end in a return (instead of ending in a jump). +// The native wrappers are stored in real nmethods instead of the BufferBlobs +// used by the adapters. The code generation happens here because it's very +// similar to what the adapters have to do. + +class AdapterHandlerEntry : public CHeapObj { + private: + address _i2c_entry; + address _c2i_entry; + address _c2i_unverified_entry; + + public: + AdapterHandlerEntry(address i2c_entry, address c2i_entry, address c2i_unverified_entry): + _i2c_entry(i2c_entry), + _c2i_entry(c2i_entry), + _c2i_unverified_entry(c2i_unverified_entry) { + } + // The name we give all buffer blobs + static const char* name; + + address get_i2c_entry() { return _i2c_entry; } + address get_c2i_entry() { return _c2i_entry; } + address get_c2i_unverified_entry() { return _c2i_unverified_entry; } + void relocate(address new_base); +#ifndef PRODUCT + void print(); +#endif /* PRODUCT */ +}; + + +class AdapterHandlerLibrary: public AllStatic { + private: + enum { + AbstractMethodHandler = 1 // special handler for abstract methods + }; + static GrowableArray* _fingerprints; // the fingerprint collection + static GrowableArray * _handlers; // the corresponding handlers + static u_char _buffer[]; // the temporary code buffer + static void initialize(); + static AdapterHandlerEntry* get_entry( int index ) { return _handlers->at(index); } + static int get_create_adapter_index(methodHandle method); + static address get_i2c_entry( int index ) { return get_entry(index)->get_i2c_entry(); } + static address get_c2i_entry( int index ) { return get_entry(index)->get_c2i_entry(); } + static address get_c2i_unverified_entry( int index ) { return get_entry(index)->get_c2i_unverified_entry(); } + + public: + static nmethod* create_native_wrapper(methodHandle method); + static AdapterHandlerEntry* get_adapter(methodHandle method) { return get_entry(get_create_adapter_index(method)); } + +#ifndef PRODUCT + static void print_handler(CodeBlob* b); + static bool contains(CodeBlob* b); +#endif /* PRODUCT */ + +}; diff --git a/hotspot/src/share/vm/runtime/sharedRuntimeTrans.cpp b/hotspot/src/share/vm/runtime/sharedRuntimeTrans.cpp new file mode 100644 index 00000000000..96db2c203f8 --- /dev/null +++ b/hotspot/src/share/vm/runtime/sharedRuntimeTrans.cpp @@ -0,0 +1,719 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_sharedRuntimeTrans.cpp.incl" + +// This file contains copies of the fdlibm routines used by +// StrictMath. It turns out that it is almost always required to use +// these runtime routines; the Intel CPU doesn't meet the Java +// specification for sin/cos outside a certain limited argument range, +// and the SPARC CPU doesn't appear to have sin/cos instructions. It +// also turns out that avoiding the indirect call through function +// pointer out to libjava.so in SharedRuntime speeds these routines up +// by roughly 15% on both Win32/x86 and Solaris/SPARC. + +// Enabling optimizations in this file causes incorrect code to be +// generated; can not figure out how to turn down optimization for one +// file in the IDE on Windows +#ifdef WIN32 +# pragma optimize ( "", off ) +#endif + +#include + +// VM_LITTLE_ENDIAN is #defined appropriately in the Makefiles +// [jk] this is not 100% correct because the float word order may different +// from the byte order (e.g. on ARM) +#ifdef VM_LITTLE_ENDIAN +# define __HI(x) *(1+(int*)&x) +# define __LO(x) *(int*)&x +#else +# define __HI(x) *(int*)&x +# define __LO(x) *(1+(int*)&x) +#endif + +double copysign(double x, double y) { + __HI(x) = (__HI(x)&0x7fffffff)|(__HI(y)&0x80000000); + return x; +} + +/* + * ==================================================== + * Copyright (C) 1998 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * scalbn (double x, int n) + * scalbn(x,n) returns x* 2**n computed by exponent + * manipulation rather than by actually performing an + * exponentiation or a multiplication. + */ + +static const double +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ + twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */ + hugeX = 1.0e+300, + tiny = 1.0e-300; + +double scalbn (double x, int n) { + int k,hx,lx; + hx = __HI(x); + lx = __LO(x); + k = (hx&0x7ff00000)>>20; /* extract exponent */ + if (k==0) { /* 0 or subnormal x */ + if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */ + x *= two54; + hx = __HI(x); + k = ((hx&0x7ff00000)>>20) - 54; + if (n< -50000) return tiny*x; /*underflow*/ + } + if (k==0x7ff) return x+x; /* NaN or Inf */ + k = k+n; + if (k > 0x7fe) return hugeX*copysign(hugeX,x); /* overflow */ + if (k > 0) /* normal result */ + {__HI(x) = (hx&0x800fffff)|(k<<20); return x;} + if (k <= -54) { + if (n > 50000) /* in case integer overflow in n+k */ + return hugeX*copysign(hugeX,x); /*overflow*/ + else return tiny*copysign(tiny,x); /*underflow*/ + } + k += 54; /* subnormal result */ + __HI(x) = (hx&0x800fffff)|(k<<20); + return x*twom54; +} + +/* __ieee754_log(x) + * Return the logrithm of x + * + * Method : + * 1. Argument Reduction: find k and f such that + * x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * 2. Approximation of log(1+f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Reme algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s + * (the values of Lg1 to Lg7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lg1*s +...+Lg7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log(1+f) = f - s*(f - R) (if f is not too large) + * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) + * + * 3. Finally, log(x) = k*ln2 + log(1+f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log(x) is NaN with signal if x < 0 (including -INF) ; + * log(+INF) is +INF; log(0) is -INF with signal; + * log(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +static const double +ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */ + ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */ + Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ + Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ + Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ + Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ + Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ + Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ + Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + +static double zero = 0.0; + +static double __ieee754_log(double x) { + double hfsq,f,s,z,R,w,t1,t2,dk; + int k,hx,i,j; + unsigned lx; + + hx = __HI(x); /* high word of x */ + lx = __LO(x); /* low word of x */ + + k=0; + if (hx < 0x00100000) { /* x < 2**-1022 */ + if (((hx&0x7fffffff)|lx)==0) + return -two54/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 54; x *= two54; /* subnormal number, scale up x */ + hx = __HI(x); /* high word of x */ + } + if (hx >= 0x7ff00000) return x+x; + k += (hx>>20)-1023; + hx &= 0x000fffff; + i = (hx+0x95f64)&0x100000; + __HI(x) = hx|(i^0x3ff00000); /* normalize x or x/2 */ + k += (i>>20); + f = x-1.0; + if((0x000fffff&(2+hx))<3) { /* |f| < 2**-20 */ + if(f==zero) { + if (k==0) return zero; + else {dk=(double)k; return dk*ln2_hi+dk*ln2_lo;} + } + R = f*f*(0.5-0.33333333333333333*f); + if(k==0) return f-R; else {dk=(double)k; + return dk*ln2_hi-((R-dk*ln2_lo)-f);} + } + s = f/(2.0+f); + dk = (double)k; + z = s*s; + i = hx-0x6147a; + w = z*z; + j = 0x6b851-hx; + t1= w*(Lg2+w*(Lg4+w*Lg6)); + t2= z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7))); + i |= j; + R = t2+t1; + if(i>0) { + hfsq=0.5*f*f; + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return dk*ln2_hi-((hfsq-(s*(hfsq+R)+dk*ln2_lo))-f); + } else { + if(k==0) return f-s*(f-R); else + return dk*ln2_hi-((s*(f-R)-dk*ln2_lo)-f); + } +} + +JRT_LEAF(jdouble, SharedRuntime::dlog(jdouble x)) + return __ieee754_log(x); +JRT_END + +/* __ieee754_log10(x) + * Return the base 10 logarithm of x + * + * Method : + * Let log10_2hi = leading 40 bits of log10(2) and + * log10_2lo = log10(2) - log10_2hi, + * ivln10 = 1/log(10) rounded. + * Then + * n = ilogb(x), + * if(n<0) n = n+1; + * x = scalbn(x,-n); + * log10(x) := n*log10_2hi + (n*log10_2lo + ivln10*log(x)) + * + * Note 1: + * To guarantee log10(10**n)=n, where 10**n is normal, the rounding + * mode must set to Round-to-Nearest. + * Note 2: + * [1/log(10)] rounded to 53 bits has error .198 ulps; + * log10 is monotonic at all binary break points. + * + * Special cases: + * log10(x) is NaN with signal if x < 0; + * log10(+INF) is +INF with no signal; log10(0) is -INF with signal; + * log10(NaN) is that NaN with no signal; + * log10(10**N) = N for N=0,1,...,22. + * + * Constants: + * The hexadecimal values are the intended ones for the following constants. + * The decimal values may be used, provided that the compiler will convert + * from decimal to binary accurately enough to produce the hexadecimal values + * shown. + */ + +static const double +ivln10 = 4.34294481903251816668e-01, /* 0x3FDBCB7B, 0x1526E50E */ + log10_2hi = 3.01029995663611771306e-01, /* 0x3FD34413, 0x509F6000 */ + log10_2lo = 3.69423907715893078616e-13; /* 0x3D59FEF3, 0x11F12B36 */ + +static double __ieee754_log10(double x) { + double y,z; + int i,k,hx; + unsigned lx; + + hx = __HI(x); /* high word of x */ + lx = __LO(x); /* low word of x */ + + k=0; + if (hx < 0x00100000) { /* x < 2**-1022 */ + if (((hx&0x7fffffff)|lx)==0) + return -two54/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 54; x *= two54; /* subnormal number, scale up x */ + hx = __HI(x); /* high word of x */ + } + if (hx >= 0x7ff00000) return x+x; + k += (hx>>20)-1023; + i = ((unsigned)k&0x80000000)>>31; + hx = (hx&0x000fffff)|((0x3ff-i)<<20); + y = (double)(k+i); + __HI(x) = hx; + z = y*log10_2lo + ivln10*__ieee754_log(x); + return z+y*log10_2hi; +} + +JRT_LEAF(jdouble, SharedRuntime::dlog10(jdouble x)) + return __ieee754_log10(x); +JRT_END + + +/* __ieee754_exp(x) + * Returns the exponential of x. + * + * Method + * 1. Argument reduction: + * Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658. + * Given x, find r and integer k such that + * + * x = k*ln2 + r, |r| <= 0.5*ln2. + * + * Here r will be represented as r = hi-lo for better + * accuracy. + * + * 2. Approximation of exp(r) by a special rational function on + * the interval [0,0.34658]: + * Write + * R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ... + * We use a special Reme algorithm on [0,0.34658] to generate + * a polynomial of degree 5 to approximate R. The maximum error + * of this polynomial approximation is bounded by 2**-59. In + * other words, + * R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5 + * (where z=r*r, and the values of P1 to P5 are listed below) + * and + * | 5 | -59 + * | 2.0+P1*z+...+P5*z - R(z) | <= 2 + * | | + * The computation of exp(r) thus becomes + * 2*r + * exp(r) = 1 + ------- + * R - r + * r*R1(r) + * = 1 + r + ----------- (for better accuracy) + * 2 - R1(r) + * where + * 2 4 10 + * R1(r) = r - (P1*r + P2*r + ... + P5*r ). + * + * 3. Scale back to obtain exp(x): + * From step 1, we have + * exp(x) = 2^k * exp(r) + * + * Special cases: + * exp(INF) is INF, exp(NaN) is NaN; + * exp(-INF) is 0, and + * for finite argument, only exp(0)=1 is exact. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Misc. info. + * For IEEE double + * if x > 7.09782712893383973096e+02 then exp(x) overflow + * if x < -7.45133219101941108420e+02 then exp(x) underflow + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +static const double +one = 1.0, + halF[2] = {0.5,-0.5,}, + twom1000= 9.33263618503218878990e-302, /* 2**-1000=0x01700000,0*/ + o_threshold= 7.09782712893383973096e+02, /* 0x40862E42, 0xFEFA39EF */ + u_threshold= -7.45133219101941108420e+02, /* 0xc0874910, 0xD52D3051 */ + ln2HI[2] ={ 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */ + -6.93147180369123816490e-01,},/* 0xbfe62e42, 0xfee00000 */ + ln2LO[2] ={ 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */ + -1.90821492927058770002e-10,},/* 0xbdea39ef, 0x35793c76 */ + invln2 = 1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */ + P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ + P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ + P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ + P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ + P5 = 4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */ + +static double __ieee754_exp(double x) { + double y,hi=0,lo=0,c,t; + int k=0,xsb; + unsigned hx; + + hx = __HI(x); /* high word of x */ + xsb = (hx>>31)&1; /* sign bit of x */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out non-finite argument */ + if(hx >= 0x40862E42) { /* if |x|>=709.78... */ + if(hx>=0x7ff00000) { + if(((hx&0xfffff)|__LO(x))!=0) + return x+x; /* NaN */ + else return (xsb==0)? x:0.0; /* exp(+-inf)={inf,0} */ + } + if(x > o_threshold) return hugeX*hugeX; /* overflow */ + if(x < u_threshold) return twom1000*twom1000; /* underflow */ + } + + /* argument reduction */ + if(hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ + hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb; + } else { + k = (int)(invln2*x+halF[xsb]); + t = k; + hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */ + lo = t*ln2LO[0]; + } + x = hi - lo; + } + else if(hx < 0x3e300000) { /* when |x|<2**-28 */ + if(hugeX+x>one) return one+x;/* trigger inexact */ + } + else k = 0; + + /* x is now in primary range */ + t = x*x; + c = x - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + if(k==0) return one-((x*c)/(c-2.0)-x); + else y = one-((lo-(x*c)/(2.0-c))-hi); + if(k >= -1021) { + __HI(y) += (k<<20); /* add k to y's exponent */ + return y; + } else { + __HI(y) += ((k+1000)<<20);/* add k to y's exponent */ + return y*twom1000; + } +} + +JRT_LEAF(jdouble, SharedRuntime::dexp(jdouble x)) + return __ieee754_exp(x); +JRT_END + +/* __ieee754_pow(x,y) return x**y + * + * n + * Method: Let x = 2 * (1+f) + * 1. Compute and return log2(x) in two pieces: + * log2(x) = w1 + w2, + * where w1 has 53-24 = 29 bit trailing zeros. + * 2. Perform y*log2(x) = n+y' by simulating muti-precision + * arithmetic, where |y'|<=0.5. + * 3. Return x**y = 2**n*exp(y'*log2) + * + * Special cases: + * 1. (anything) ** 0 is 1 + * 2. (anything) ** 1 is itself + * 3. (anything) ** NAN is NAN + * 4. NAN ** (anything except 0) is NAN + * 5. +-(|x| > 1) ** +INF is +INF + * 6. +-(|x| > 1) ** -INF is +0 + * 7. +-(|x| < 1) ** +INF is +0 + * 8. +-(|x| < 1) ** -INF is +INF + * 9. +-1 ** +-INF is NAN + * 10. +0 ** (+anything except 0, NAN) is +0 + * 11. -0 ** (+anything except 0, NAN, odd integer) is +0 + * 12. +0 ** (-anything except 0, NAN) is +INF + * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF + * 14. -0 ** (odd integer) = -( +0 ** (odd integer) ) + * 15. +INF ** (+anything except 0,NAN) is +INF + * 16. +INF ** (-anything except 0,NAN) is +0 + * 17. -INF ** (anything) = -0 ** (-anything) + * 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer) + * 19. (-anything except 0 and inf) ** (non-integer) is NAN + * + * Accuracy: + * pow(x,y) returns x**y nearly rounded. In particular + * pow(integer,integer) + * always returns the correct integer provided it is + * representable. + * + * Constants : + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +static const double +bp[] = {1.0, 1.5,}, + dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */ + dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */ + zeroX = 0.0, + two = 2.0, + two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */ + /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ + L1X = 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */ + L2X = 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */ + L3X = 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */ + L4X = 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */ + L5X = 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */ + L6X = 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */ + lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ + lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */ + lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */ + ovt = 8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */ + cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */ + cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */ + cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/ + ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */ + ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/ + ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/ + +double __ieee754_pow(double x, double y) { + double z,ax,z_h,z_l,p_h,p_l; + double y1,t1,t2,r,s,t,u,v,w; + int i0,i1,i,j,k,yisint,n; + int hx,hy,ix,iy; + unsigned lx,ly; + + i0 = ((*(int*)&one)>>29)^1; i1=1-i0; + hx = __HI(x); lx = __LO(x); + hy = __HI(y); ly = __LO(y); + ix = hx&0x7fffffff; iy = hy&0x7fffffff; + + /* y==zero: x**0 = 1 */ + if((iy|ly)==0) return one; + + /* +-NaN return x+y */ + if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) || + iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0))) + return x+y; + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if(hx<0) { + if(iy>=0x43400000) yisint = 2; /* even integer y */ + else if(iy>=0x3ff00000) { + k = (iy>>20)-0x3ff; /* exponent */ + if(k>20) { + j = ly>>(52-k); + if((unsigned)(j<<(52-k))==ly) yisint = 2-(j&1); + } else if(ly==0) { + j = iy>>(20-k); + if((j<<(20-k))==iy) yisint = 2-(j&1); + } + } + } + + /* special value of y */ + if(ly==0) { + if (iy==0x7ff00000) { /* y is +-inf */ + if(((ix-0x3ff00000)|lx)==0) + return y - y; /* inf**+-1 is NaN */ + else if (ix >= 0x3ff00000)/* (|x|>1)**+-inf = inf,0 */ + return (hy>=0)? y: zeroX; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy<0)?-y: zeroX; + } + if(iy==0x3ff00000) { /* y is +-1 */ + if(hy<0) return one/x; else return x; + } + if(hy==0x40000000) return x*x; /* y is 2 */ + if(hy==0x3fe00000) { /* y is 0.5 */ + if(hx>=0) /* x >= +0 */ + return sqrt(x); + } + } + + ax = fabsd(x); + /* special value of x */ + if(lx==0) { + if(ix==0x7ff00000||ix==0||ix==0x3ff00000){ + z = ax; /*x is +-0,+-inf,+-1*/ + if(hy<0) z = one/z; /* z = (1/|x|) */ + if(hx<0) { + if(((ix-0x3ff00000)|yisint)==0) { + z = (z-z)/(z-z); /* (-1)**non-int is NaN */ + } else if(yisint==1) + z = -1.0*z; /* (x<0)**odd = -(|x|**odd) */ + } + return z; + } + } + + n = (hx>>31)+1; + + /* (x<0)**(non-int) is NaN */ + if((n|yisint)==0) return (x-x)/(x-x); + + s = one; /* s (sign of result -ve**odd) = -1 else = 1 */ + if((n|(yisint-1))==0) s = -one;/* (-ve)**(odd int) */ + + /* |y| is huge */ + if(iy>0x41e00000) { /* if |y| > 2**31 */ + if(iy>0x43f00000){ /* if |y| > 2**64, must o/uflow */ + if(ix<=0x3fefffff) return (hy<0)? hugeX*hugeX:tiny*tiny; + if(ix>=0x3ff00000) return (hy>0)? hugeX*hugeX:tiny*tiny; + } + /* over/underflow if x is not close to one */ + if(ix<0x3fefffff) return (hy<0)? s*hugeX*hugeX:s*tiny*tiny; + if(ix>0x3ff00000) return (hy>0)? s*hugeX*hugeX:s*tiny*tiny; + /* now |1-x| is tiny <= 2**-20, suffice to compute + log(x) by x-x^2/2+x^3/3-x^4/4 */ + t = ax-one; /* t has 20 trailing zeros */ + w = (t*t)*(0.5-t*(0.3333333333333333333333-t*0.25)); + u = ivln2_h*t; /* ivln2_h has 21 sig. bits */ + v = t*ivln2_l-w*ivln2; + t1 = u+v; + __LO(t1) = 0; + t2 = v-(t1-u); + } else { + double ss,s2,s_h,s_l,t_h,t_l; + n = 0; + /* take care subnormal number */ + if(ix<0x00100000) + {ax *= two53; n -= 53; ix = __HI(ax); } + n += ((ix)>>20)-0x3ff; + j = ix&0x000fffff; + /* determine interval */ + ix = j|0x3ff00000; /* normalize ix */ + if(j<=0x3988E) k=0; /* |x|>1)|0x20000000)+0x00080000+(k<<18); + t_l = ax - (t_h-bp[k]); + s_l = v*((u-s_h*t_h)-s_h*t_l); + /* compute log(ax) */ + s2 = ss*ss; + r = s2*s2*(L1X+s2*(L2X+s2*(L3X+s2*(L4X+s2*(L5X+s2*L6X))))); + r += s_l*(s_h+ss); + s2 = s_h*s_h; + t_h = 3.0+s2+r; + __LO(t_h) = 0; + t_l = r-((t_h-3.0)-s2); + /* u+v = ss*(1+...) */ + u = s_h*t_h; + v = s_l*t_h+t_l*ss; + /* 2/(3log2)*(ss+...) */ + p_h = u+v; + __LO(p_h) = 0; + p_l = v-(p_h-u); + z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ + z_l = cp_l*p_h+p_l*cp+dp_l[k]; + /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */ + t = (double)n; + t1 = (((z_h+z_l)+dp_h[k])+t); + __LO(t1) = 0; + t2 = z_l-(((t1-t)-dp_h[k])-z_h); + } + + /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ + y1 = y; + __LO(y1) = 0; + p_l = (y-y1)*t1+y*t2; + p_h = y1*t1; + z = p_l+p_h; + j = __HI(z); + i = __LO(z); + if (j>=0x40900000) { /* z >= 1024 */ + if(((j-0x40900000)|i)!=0) /* if z > 1024 */ + return s*hugeX*hugeX; /* overflow */ + else { + if(p_l+ovt>z-p_h) return s*hugeX*hugeX; /* overflow */ + } + } else if((j&0x7fffffff)>=0x4090cc00 ) { /* z <= -1075 */ + if(((j-0xc090cc00)|i)!=0) /* z < -1075 */ + return s*tiny*tiny; /* underflow */ + else { + if(p_l<=z-p_h) return s*tiny*tiny; /* underflow */ + } + } + /* + * compute 2**(p_h+p_l) + */ + i = j&0x7fffffff; + k = (i>>20)-0x3ff; + n = 0; + if(i>0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */ + n = j+(0x00100000>>(k+1)); + k = ((n&0x7fffffff)>>20)-0x3ff; /* new k for n */ + t = zeroX; + __HI(t) = (n&~(0x000fffff>>k)); + n = ((n&0x000fffff)|0x00100000)>>(20-k); + if(j<0) n = -n; + p_h -= t; + } + t = p_l+p_h; + __LO(t) = 0; + u = t*lg2_h; + v = (p_l-(t-p_h))*lg2+t*lg2_l; + z = u+v; + w = v-(z-u); + t = z*z; + t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + r = (z*t1)/(t1-two)-(w+z*w); + z = one-(r-z); + j = __HI(z); + j += (n<<20); + if((j>>20)<=0) z = scalbn(z,n); /* subnormal output */ + else __HI(z) += (n<<20); + return s*z; +} + + +JRT_LEAF(jdouble, SharedRuntime::dpow(jdouble x, jdouble y)) + return __ieee754_pow(x, y); +JRT_END + +#ifdef WIN32 +# pragma optimize ( "", on ) +#endif diff --git a/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp b/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp new file mode 100644 index 00000000000..b6fe6613dfb --- /dev/null +++ b/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp @@ -0,0 +1,957 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_sharedRuntimeTrig.cpp.incl" + +// This file contains copies of the fdlibm routines used by +// StrictMath. It turns out that it is almost always required to use +// these runtime routines; the Intel CPU doesn't meet the Java +// specification for sin/cos outside a certain limited argument range, +// and the SPARC CPU doesn't appear to have sin/cos instructions. It +// also turns out that avoiding the indirect call through function +// pointer out to libjava.so in SharedRuntime speeds these routines up +// by roughly 15% on both Win32/x86 and Solaris/SPARC. + +// Enabling optimizations in this file causes incorrect code to be +// generated; can not figure out how to turn down optimization for one +// file in the IDE on Windows +#ifdef WIN32 +# pragma optimize ( "", off ) +#endif + +#include + +// VM_LITTLE_ENDIAN is #defined appropriately in the Makefiles +// [jk] this is not 100% correct because the float word order may different +// from the byte order (e.g. on ARM) +#ifdef VM_LITTLE_ENDIAN +# define __HI(x) *(1+(int*)&x) +# define __LO(x) *(int*)&x +#else +# define __HI(x) *(int*)&x +# define __LO(x) *(1+(int*)&x) +#endif + +static double copysignA(double x, double y) { + __HI(x) = (__HI(x)&0x7fffffff)|(__HI(y)&0x80000000); + return x; +} + +/* + * scalbn (double x, int n) + * scalbn(x,n) returns x* 2**n computed by exponent + * manipulation rather than by actually performing an + * exponentiation or a multiplication. + */ + +static const double +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ +twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */ +hugeX = 1.0e+300, +tiny = 1.0e-300; + +static double scalbnA (double x, int n) { + int k,hx,lx; + hx = __HI(x); + lx = __LO(x); + k = (hx&0x7ff00000)>>20; /* extract exponent */ + if (k==0) { /* 0 or subnormal x */ + if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */ + x *= two54; + hx = __HI(x); + k = ((hx&0x7ff00000)>>20) - 54; + if (n< -50000) return tiny*x; /*underflow*/ + } + if (k==0x7ff) return x+x; /* NaN or Inf */ + k = k+n; + if (k > 0x7fe) return hugeX*copysignA(hugeX,x); /* overflow */ + if (k > 0) /* normal result */ + {__HI(x) = (hx&0x800fffff)|(k<<20); return x;} + if (k <= -54) { + if (n > 50000) /* in case integer overflow in n+k */ + return hugeX*copysignA(hugeX,x); /*overflow*/ + else return tiny*copysignA(tiny,x); /*underflow*/ + } + k += 54; /* subnormal result */ + __HI(x) = (hx&0x800fffff)|(k<<20); + return x*twom54; +} + +/* + * __kernel_rem_pio2(x,y,e0,nx,prec,ipio2) + * double x[],y[]; int e0,nx,prec; int ipio2[]; + * + * __kernel_rem_pio2 return the last three digits of N with + * y = x - N*pi/2 + * so that |y| < pi/2. + * + * The method is to compute the integer (mod 8) and fraction parts of + * (2/pi)*x without doing the full multiplication. In general we + * skip the part of the product that are known to be a huge integer ( + * more accurately, = 0 mod 8 ). Thus the number of operations are + * independent of the exponent of the input. + * + * (2/pi) is represented by an array of 24-bit integers in ipio2[]. + * + * Input parameters: + * x[] The input value (must be positive) is broken into nx + * pieces of 24-bit integers in double precision format. + * x[i] will be the i-th 24 bit of x. The scaled exponent + * of x[0] is given in input parameter e0 (i.e., x[0]*2^e0 + * match x's up to 24 bits. + * + * Example of breaking a double positive z into x[0]+x[1]+x[2]: + * e0 = ilogb(z)-23 + * z = scalbn(z,-e0) + * for i = 0,1,2 + * x[i] = floor(z) + * z = (z-x[i])*2**24 + * + * + * y[] ouput result in an array of double precision numbers. + * The dimension of y[] is: + * 24-bit precision 1 + * 53-bit precision 2 + * 64-bit precision 2 + * 113-bit precision 3 + * The actual value is the sum of them. Thus for 113-bit + * precsion, one may have to do something like: + * + * long double t,w,r_head, r_tail; + * t = (long double)y[2] + (long double)y[1]; + * w = (long double)y[0]; + * r_head = t+w; + * r_tail = w - (r_head - t); + * + * e0 The exponent of x[0] + * + * nx dimension of x[] + * + * prec an interger indicating the precision: + * 0 24 bits (single) + * 1 53 bits (double) + * 2 64 bits (extended) + * 3 113 bits (quad) + * + * ipio2[] + * integer array, contains the (24*i)-th to (24*i+23)-th + * bit of 2/pi after binary point. The corresponding + * floating value is + * + * ipio2[i] * 2^(-24(i+1)). + * + * External function: + * double scalbn(), floor(); + * + * + * Here is the description of some local variables: + * + * jk jk+1 is the initial number of terms of ipio2[] needed + * in the computation. The recommended value is 2,3,4, + * 6 for single, double, extended,and quad. + * + * jz local integer variable indicating the number of + * terms of ipio2[] used. + * + * jx nx - 1 + * + * jv index for pointing to the suitable ipio2[] for the + * computation. In general, we want + * ( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8 + * is an integer. Thus + * e0-3-24*jv >= 0 or (e0-3)/24 >= jv + * Hence jv = max(0,(e0-3)/24). + * + * jp jp+1 is the number of terms in PIo2[] needed, jp = jk. + * + * q[] double array with integral value, representing the + * 24-bits chunk of the product of x and 2/pi. + * + * q0 the corresponding exponent of q[0]. Note that the + * exponent for q[i] would be q0-24*i. + * + * PIo2[] double precision array, obtained by cutting pi/2 + * into 24 bits chunks. + * + * f[] ipio2[] in floating point + * + * iq[] integer array by breaking up q[] in 24-bits chunk. + * + * fq[] final product of x*(2/pi) in fq[0],..,fq[jk] + * + * ih integer. If >0 it indicats q[] is >= 0.5, hence + * it also indicates the *sign* of the result. + * + */ + + +/* + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + + +static const int init_jk[] = {2,3,4,6}; /* initial value for jk */ + +static const double PIo2[] = { + 1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */ + 7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */ + 5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */ + 3.28200341580791294123e-22, /* 0x3B78CC51, 0x60000000 */ + 1.27065575308067607349e-29, /* 0x39F01B83, 0x80000000 */ + 1.22933308981111328932e-36, /* 0x387A2520, 0x40000000 */ + 2.73370053816464559624e-44, /* 0x36E38222, 0x80000000 */ + 2.16741683877804819444e-51, /* 0x3569F31D, 0x00000000 */ +}; + +static const double +zeroB = 0.0, +one = 1.0, +two24B = 1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */ +twon24 = 5.96046447753906250000e-08; /* 0x3E700000, 0x00000000 */ + +static int __kernel_rem_pio2(double *x, double *y, int e0, int nx, int prec, const int *ipio2) { + int jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih; + double z,fw,f[20],fq[20],q[20]; + + /* initialize jk*/ + jk = init_jk[prec]; + jp = jk; + + /* determine jx,jv,q0, note that 3>q0 */ + jx = nx-1; + jv = (e0-3)/24; if(jv<0) jv=0; + q0 = e0-24*(jv+1); + + /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */ + j = jv-jx; m = jx+jk; + for(i=0;i<=m;i++,j++) f[i] = (j<0)? zeroB : (double) ipio2[j]; + + /* compute q[0],q[1],...q[jk] */ + for (i=0;i<=jk;i++) { + for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; q[i] = fw; + } + + jz = jk; +recompute: + /* distill q[] into iq[] reversingly */ + for(i=0,j=jz,z=q[jz];j>0;i++,j--) { + fw = (double)((int)(twon24* z)); + iq[i] = (int)(z-two24B*fw); + z = q[j-1]+fw; + } + + /* compute n */ + z = scalbnA(z,q0); /* actual value of z */ + z -= 8.0*floor(z*0.125); /* trim off integer >= 8 */ + n = (int) z; + z -= (double)n; + ih = 0; + if(q0>0) { /* need iq[jz-1] to determine n */ + i = (iq[jz-1]>>(24-q0)); n += i; + iq[jz-1] -= i<<(24-q0); + ih = iq[jz-1]>>(23-q0); + } + else if(q0==0) ih = iq[jz-1]>>23; + else if(z>=0.5) ih=2; + + if(ih>0) { /* q > 0.5 */ + n += 1; carry = 0; + for(i=0;i0) { /* rare case: chance is 1 in 12 */ + switch(q0) { + case 1: + iq[jz-1] &= 0x7fffff; break; + case 2: + iq[jz-1] &= 0x3fffff; break; + } + } + if(ih==2) { + z = one - z; + if(carry!=0) z -= scalbnA(one,q0); + } + } + + /* check if recomputation is needed */ + if(z==zeroB) { + j = 0; + for (i=jz-1;i>=jk;i--) j |= iq[i]; + if(j==0) { /* need recomputation */ + for(k=1;iq[jk-k]==0;k++); /* k = no. of terms needed */ + + for(i=jz+1;i<=jz+k;i++) { /* add q[jz+1] to q[jz+k] */ + f[jx+i] = (double) ipio2[jv+i]; + for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; + q[i] = fw; + } + jz += k; + goto recompute; + } + } + + /* chop off zero terms */ + if(z==0.0) { + jz -= 1; q0 -= 24; + while(iq[jz]==0) { jz--; q0-=24;} + } else { /* break z into 24-bit if neccessary */ + z = scalbnA(z,-q0); + if(z>=two24B) { + fw = (double)((int)(twon24*z)); + iq[jz] = (int)(z-two24B*fw); + jz += 1; q0 += 24; + iq[jz] = (int) fw; + } else iq[jz] = (int) z ; + } + + /* convert integer "bit" chunk to floating-point value */ + fw = scalbnA(one,q0); + for(i=jz;i>=0;i--) { + q[i] = fw*(double)iq[i]; fw*=twon24; + } + + /* compute PIo2[0,...,jp]*q[jz,...,0] */ + for(i=jz;i>=0;i--) { + for(fw=0.0,k=0;k<=jp&&k<=jz-i;k++) fw += PIo2[k]*q[i+k]; + fq[jz-i] = fw; + } + + /* compress fq[] into y[] */ + switch(prec) { + case 0: + fw = 0.0; + for (i=jz;i>=0;i--) fw += fq[i]; + y[0] = (ih==0)? fw: -fw; + break; + case 1: + case 2: + fw = 0.0; + for (i=jz;i>=0;i--) fw += fq[i]; + y[0] = (ih==0)? fw: -fw; + fw = fq[0]-fw; + for (i=1;i<=jz;i++) fw += fq[i]; + y[1] = (ih==0)? fw: -fw; + break; + case 3: /* painful */ + for (i=jz;i>0;i--) { + fw = fq[i-1]+fq[i]; + fq[i] += fq[i-1]-fw; + fq[i-1] = fw; + } + for (i=jz;i>1;i--) { + fw = fq[i-1]+fq[i]; + fq[i] += fq[i-1]-fw; + fq[i-1] = fw; + } + for (fw=0.0,i=jz;i>=2;i--) fw += fq[i]; + if(ih==0) { + y[0] = fq[0]; y[1] = fq[1]; y[2] = fw; + } else { + y[0] = -fq[0]; y[1] = -fq[1]; y[2] = -fw; + } + } + return n&7; +} + + +/* + * ==================================================== + * Copyright 13 Dec 1993 Sun Microsystems, Inc. All Rights Reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_rem_pio2(x,y) + * + * return the remainder of x rem pi/2 in y[0]+y[1] + * use __kernel_rem_pio2() + */ + +/* + * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi + */ +static const int two_over_pi[] = { + 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, + 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, + 0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, + 0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, + 0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, + 0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF, + 0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, + 0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, + 0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, + 0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, + 0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, +}; + +static const int npio2_hw[] = { + 0x3FF921FB, 0x400921FB, 0x4012D97C, 0x401921FB, 0x401F6A7A, 0x4022D97C, + 0x4025FDBB, 0x402921FB, 0x402C463A, 0x402F6A7A, 0x4031475C, 0x4032D97C, + 0x40346B9C, 0x4035FDBB, 0x40378FDB, 0x403921FB, 0x403AB41B, 0x403C463A, + 0x403DD85A, 0x403F6A7A, 0x40407E4C, 0x4041475C, 0x4042106C, 0x4042D97C, + 0x4043A28C, 0x40446B9C, 0x404534AC, 0x4045FDBB, 0x4046C6CB, 0x40478FDB, + 0x404858EB, 0x404921FB, +}; + +/* + * invpio2: 53 bits of 2/pi + * pio2_1: first 33 bit of pi/2 + * pio2_1t: pi/2 - pio2_1 + * pio2_2: second 33 bit of pi/2 + * pio2_2t: pi/2 - (pio2_1+pio2_2) + * pio2_3: third 33 bit of pi/2 + * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) + */ + +static const double +zeroA = 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ +half = 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +two24A = 1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */ +invpio2 = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ +pio2_1 = 1.57079632673412561417e+00, /* 0x3FF921FB, 0x54400000 */ +pio2_1t = 6.07710050650619224932e-11, /* 0x3DD0B461, 0x1A626331 */ +pio2_2 = 6.07710050630396597660e-11, /* 0x3DD0B461, 0x1A600000 */ +pio2_2t = 2.02226624879595063154e-21, /* 0x3BA3198A, 0x2E037073 */ +pio2_3 = 2.02226624871116645580e-21, /* 0x3BA3198A, 0x2E000000 */ +pio2_3t = 8.47842766036889956997e-32; /* 0x397B839A, 0x252049C1 */ + +static int __ieee754_rem_pio2(double x, double *y) { + double z,w,t,r,fn; + double tx[3]; + int e0,i,j,nx,n,ix,hx,i0; + + i0 = ((*(int*)&two24A)>>30)^1; /* high word index */ + hx = *(i0+(int*)&x); /* high word of x */ + ix = hx&0x7fffffff; + if(ix<=0x3fe921fb) /* |x| ~<= pi/4 , no need for reduction */ + {y[0] = x; y[1] = 0; return 0;} + if(ix<0x4002d97c) { /* |x| < 3pi/4, special case with n=+-1 */ + if(hx>0) { + z = x - pio2_1; + if(ix!=0x3ff921fb) { /* 33+53 bit pi is good enough */ + y[0] = z - pio2_1t; + y[1] = (z-y[0])-pio2_1t; + } else { /* near pi/2, use 33+33+53 bit pi */ + z -= pio2_2; + y[0] = z - pio2_2t; + y[1] = (z-y[0])-pio2_2t; + } + return 1; + } else { /* negative x */ + z = x + pio2_1; + if(ix!=0x3ff921fb) { /* 33+53 bit pi is good enough */ + y[0] = z + pio2_1t; + y[1] = (z-y[0])+pio2_1t; + } else { /* near pi/2, use 33+33+53 bit pi */ + z += pio2_2; + y[0] = z + pio2_2t; + y[1] = (z-y[0])+pio2_2t; + } + return -1; + } + } + if(ix<=0x413921fb) { /* |x| ~<= 2^19*(pi/2), medium size */ + t = fabsd(x); + n = (int) (t*invpio2+half); + fn = (double)n; + r = t-fn*pio2_1; + w = fn*pio2_1t; /* 1st round good to 85 bit */ + if(n<32&&ix!=npio2_hw[n-1]) { + y[0] = r-w; /* quick check no cancellation */ + } else { + j = ix>>20; + y[0] = r-w; + i = j-(((*(i0+(int*)&y[0]))>>20)&0x7ff); + if(i>16) { /* 2nd iteration needed, good to 118 */ + t = r; + w = fn*pio2_2; + r = t-w; + w = fn*pio2_2t-((t-r)-w); + y[0] = r-w; + i = j-(((*(i0+(int*)&y[0]))>>20)&0x7ff); + if(i>49) { /* 3rd iteration need, 151 bits acc */ + t = r; /* will cover all possible cases */ + w = fn*pio2_3; + r = t-w; + w = fn*pio2_3t-((t-r)-w); + y[0] = r-w; + } + } + } + y[1] = (r-y[0])-w; + if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} + else return n; + } + /* + * all other (large) arguments + */ + if(ix>=0x7ff00000) { /* x is inf or NaN */ + y[0]=y[1]=x-x; return 0; + } + /* set z = scalbn(|x|,ilogb(x)-23) */ + *(1-i0+(int*)&z) = *(1-i0+(int*)&x); + e0 = (ix>>20)-1046; /* e0 = ilogb(z)-23; */ + *(i0+(int*)&z) = ix - (e0<<20); + for(i=0;i<2;i++) { + tx[i] = (double)((int)(z)); + z = (z-tx[i])*two24A; + } + tx[2] = z; + nx = 3; + while(tx[nx-1]==zeroA) nx--; /* skip zero term */ + n = __kernel_rem_pio2(tx,y,e0,nx,2,two_over_pi); + if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} + return n; +} + + +/* __kernel_sin( x, y, iy) + * kernel sin function on [-pi/4, pi/4], pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input iy indicates whether y is 0. (if iy=0, y assume to be 0). + * + * Algorithm + * 1. Since sin(-x) = -sin(x), we need only to consider positive x. + * 2. if x < 2^-27 (hx<0x3e400000 0), return x with inexact if x!=0. + * 3. sin(x) is approximated by a polynomial of degree 13 on + * [0,pi/4] + * 3 13 + * sin(x) ~ x + S1*x + ... + S6*x + * where + * + * |sin(x) 2 4 6 8 10 12 | -58 + * |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 + * | x | + * + * 4. sin(x+y) = sin(x) + sin'(x')*y + * ~ sin(x) + (1-x*x/2)*y + * For better accuracy, let + * 3 2 2 2 2 + * r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6)))) + * then 3 2 + * sin(x) = x + (S1*x + (x *(r-y/2)+y)) + */ + +static const double +S1 = -1.66666666666666324348e-01, /* 0xBFC55555, 0x55555549 */ +S2 = 8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */ +S3 = -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */ +S4 = 2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */ +S5 = -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */ +S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */ + +static double __kernel_sin(double x, double y, int iy) +{ + double z,r,v; + int ix; + ix = __HI(x)&0x7fffffff; /* high word of x */ + if(ix<0x3e400000) /* |x| < 2**-27 */ + {if((int)x==0) return x;} /* generate inexact */ + z = x*x; + v = z*x; + r = S2+z*(S3+z*(S4+z*(S5+z*S6))); + if(iy==0) return x+v*(S1+z*r); + else return x-((z*(half*y-v*r)-y)-v*S1); +} + +/* + * __kernel_cos( x, y ) + * kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * + * Algorithm + * 1. Since cos(-x) = cos(x), we need only to consider positive x. + * 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0. + * 3. cos(x) is approximated by a polynomial of degree 14 on + * [0,pi/4] + * 4 14 + * cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x + * where the remez error is + * + * | 2 4 6 8 10 12 14 | -58 + * |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 + * | | + * + * 4 6 8 10 12 14 + * 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then + * cos(x) = 1 - x*x/2 + r + * since cos(x+y) ~ cos(x) - sin(x)*y + * ~ cos(x) - x*y, + * a correction term is necessary in cos(x) and hence + * cos(x+y) = 1 - (x*x/2 - (r - x*y)) + * For better accuracy when x > 0.3, let qx = |x|/4 with + * the last 32 bits mask off, and if x > 0.78125, let qx = 0.28125. + * Then + * cos(x+y) = (1-qx) - ((x*x/2-qx) - (r-x*y)). + * Note that 1-qx and (x*x/2-qx) is EXACT here, and the + * magnitude of the latter is at least a quarter of x*x/2, + * thus, reducing the rounding error in the subtraction. + */ + +static const double +C1 = 4.16666666666666019037e-02, /* 0x3FA55555, 0x5555554C */ +C2 = -1.38888888888741095749e-03, /* 0xBF56C16C, 0x16C15177 */ +C3 = 2.48015872894767294178e-05, /* 0x3EFA01A0, 0x19CB1590 */ +C4 = -2.75573143513906633035e-07, /* 0xBE927E4F, 0x809C52AD */ +C5 = 2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */ +C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ + +static double __kernel_cos(double x, double y) +{ + double a,hz,z,r,qx; + int ix; + ix = __HI(x)&0x7fffffff; /* ix = |x|'s high word*/ + if(ix<0x3e400000) { /* if x < 2**27 */ + if(((int)x)==0) return one; /* generate inexact */ + } + z = x*x; + r = z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*C6))))); + if(ix < 0x3FD33333) /* if |x| < 0.3 */ + return one - (0.5*z - (z*r - x*y)); + else { + if(ix > 0x3fe90000) { /* x > 0.78125 */ + qx = 0.28125; + } else { + __HI(qx) = ix-0x00200000; /* x/4 */ + __LO(qx) = 0; + } + hz = 0.5*z-qx; + a = one-qx; + return a - (hz - (z*r-x*y)); + } +} + +/* __kernel_tan( x, y, k ) + * kernel tan function on [-pi/4, pi/4], pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input k indicates whether tan (if k=1) or + * -1/tan (if k= -1) is returned. + * + * Algorithm + * 1. Since tan(-x) = -tan(x), we need only to consider positive x. + * 2. if x < 2^-28 (hx<0x3e300000 0), return x with inexact if x!=0. + * 3. tan(x) is approximated by a odd polynomial of degree 27 on + * [0,0.67434] + * 3 27 + * tan(x) ~ x + T1*x + ... + T13*x + * where + * + * |tan(x) 2 4 26 | -59.2 + * |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2 + * | x | + * + * Note: tan(x+y) = tan(x) + tan'(x)*y + * ~ tan(x) + (1+x*x)*y + * Therefore, for better accuracy in computing tan(x+y), let + * 3 2 2 2 2 + * r = x *(T2+x *(T3+x *(...+x *(T12+x *T13)))) + * then + * 3 2 + * tan(x+y) = x + (T1*x + (x *(r+y)+y)) + * + * 4. For x in [0.67434,pi/4], let y = pi/4 - x, then + * tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y)) + * = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) + */ + +static const double +pio4 = 7.85398163397448278999e-01, /* 0x3FE921FB, 0x54442D18 */ +pio4lo= 3.06161699786838301793e-17, /* 0x3C81A626, 0x33145C07 */ +T[] = { + 3.33333333333334091986e-01, /* 0x3FD55555, 0x55555563 */ + 1.33333333333201242699e-01, /* 0x3FC11111, 0x1110FE7A */ + 5.39682539762260521377e-02, /* 0x3FABA1BA, 0x1BB341FE */ + 2.18694882948595424599e-02, /* 0x3F9664F4, 0x8406D637 */ + 8.86323982359930005737e-03, /* 0x3F8226E3, 0xE96E8493 */ + 3.59207910759131235356e-03, /* 0x3F6D6D22, 0xC9560328 */ + 1.45620945432529025516e-03, /* 0x3F57DBC8, 0xFEE08315 */ + 5.88041240820264096874e-04, /* 0x3F4344D8, 0xF2F26501 */ + 2.46463134818469906812e-04, /* 0x3F3026F7, 0x1A8D1068 */ + 7.81794442939557092300e-05, /* 0x3F147E88, 0xA03792A6 */ + 7.14072491382608190305e-05, /* 0x3F12B80F, 0x32F0A7E9 */ + -1.85586374855275456654e-05, /* 0xBEF375CB, 0xDB605373 */ + 2.59073051863633712884e-05, /* 0x3EFB2A70, 0x74BF7AD4 */ +}; + +static double __kernel_tan(double x, double y, int iy) +{ + double z,r,v,w,s; + int ix,hx; + hx = __HI(x); /* high word of x */ + ix = hx&0x7fffffff; /* high word of |x| */ + if(ix<0x3e300000) { /* x < 2**-28 */ + if((int)x==0) { /* generate inexact */ + if (((ix | __LO(x)) | (iy + 1)) == 0) + return one / fabsd(x); + else { + if (iy == 1) + return x; + else { /* compute -1 / (x+y) carefully */ + double a, t; + + z = w = x + y; + __LO(z) = 0; + v = y - (z - x); + t = a = -one / w; + __LO(t) = 0; + s = one + t * z; + return t + a * (s + t * v); + } + } + } + } + if(ix>=0x3FE59428) { /* |x|>=0.6744 */ + if(hx<0) {x = -x; y = -y;} + z = pio4-x; + w = pio4lo-y; + x = z+w; y = 0.0; + } + z = x*x; + w = z*z; + /* Break x^5*(T[1]+x^2*T[2]+...) into + * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + + * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) + */ + r = T[1]+w*(T[3]+w*(T[5]+w*(T[7]+w*(T[9]+w*T[11])))); + v = z*(T[2]+w*(T[4]+w*(T[6]+w*(T[8]+w*(T[10]+w*T[12]))))); + s = z*x; + r = y + z*(s*(r+v)+y); + r += T[0]*s; + w = x+r; + if(ix>=0x3FE59428) { + v = (double)iy; + return (double)(1-((hx>>30)&2))*(v-2.0*(x-(w*w/(w+v)-r))); + } + if(iy==1) return w; + else { /* if allow error up to 2 ulp, + simply return -1.0/(x+r) here */ + /* compute -1.0/(x+r) accurately */ + double a,t; + z = w; + __LO(z) = 0; + v = r-(z - x); /* z+v = r+x */ + t = a = -1.0/w; /* a = -1.0/w */ + __LO(t) = 0; + s = 1.0+t*z; + return t+a*(s+t*v); + } +} + + +//---------------------------------------------------------------------- +// +// Routines for new sin/cos implementation +// +//---------------------------------------------------------------------- + +/* sin(x) + * Return sine function of x. + * + * kernel function: + * __kernel_sin ... sine function on [-pi/4,pi/4] + * __kernel_cos ... cose function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +JRT_LEAF(jdouble, SharedRuntime::dsin(jdouble x)) + double y[2],z=0.0; + int n, ix; + + /* High word of x. */ + ix = __HI(x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) return __kernel_sin(x,z,0); + + /* sin(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + switch(n&3) { + case 0: return __kernel_sin(y[0],y[1],1); + case 1: return __kernel_cos(y[0],y[1]); + case 2: return -__kernel_sin(y[0],y[1],1); + default: + return -__kernel_cos(y[0],y[1]); + } + } +JRT_END + +/* cos(x) + * Return cosine function of x. + * + * kernel function: + * __kernel_sin ... sine function on [-pi/4,pi/4] + * __kernel_cos ... cosine function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +JRT_LEAF(jdouble, SharedRuntime::dcos(jdouble x)) + double y[2],z=0.0; + int n, ix; + + /* High word of x. */ + ix = __HI(x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) return __kernel_cos(x,z); + + /* cos(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + switch(n&3) { + case 0: return __kernel_cos(y[0],y[1]); + case 1: return -__kernel_sin(y[0],y[1],1); + case 2: return -__kernel_cos(y[0],y[1]); + default: + return __kernel_sin(y[0],y[1],1); + } + } +JRT_END + +/* tan(x) + * Return tangent function of x. + * + * kernel function: + * __kernel_tan ... tangent function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +JRT_LEAF(jdouble, SharedRuntime::dtan(jdouble x)) + double y[2],z=0.0; + int n, ix; + + /* High word of x. */ + ix = __HI(x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) return __kernel_tan(x,z,1); + + /* tan(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; /* NaN */ + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + return __kernel_tan(y[0],y[1],1-((n&1)<<1)); /* 1 -- n even + -1 -- n odd */ + } +JRT_END + + +#ifdef WIN32 +# pragma optimize ( "", on ) +#endif diff --git a/hotspot/src/share/vm/runtime/signature.cpp b/hotspot/src/share/vm/runtime/signature.cpp new file mode 100644 index 00000000000..c9c3859a612 --- /dev/null +++ b/hotspot/src/share/vm/runtime/signature.cpp @@ -0,0 +1,432 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_signature.cpp.incl" + + +// Implementation of SignatureIterator + +// Signature syntax: +// +// Signature = "(" {Parameter} ")" ReturnType. +// Parameter = FieldType. +// ReturnType = FieldType | "V". +// FieldType = "B" | "C" | "D" | "F" | "I" | "J" | "S" | "Z" | "L" ClassName ";" | "[" FieldType. +// ClassName = string. + + +SignatureIterator::SignatureIterator(symbolHandle signature) { + assert(signature->is_symbol(), "not a symbol"); + _signature = signature; + _parameter_index = 0; +} + +// Overloaded version called without handle +SignatureIterator::SignatureIterator(symbolOop signature) { + symbolHandle sh(Thread::current(), signature); + _signature = sh; + _parameter_index = 0; +} + +SignatureIterator::SignatureIterator(Thread *thread, symbolOop signature) { + symbolHandle sh(thread, signature); + _signature = sh; + _parameter_index = 0; +} + +void SignatureIterator::expect(char c) { + if (_signature->byte_at(_index) != c) fatal1("expecting %c", c); + _index++; +} + + +void SignatureIterator::skip_optional_size() { + symbolOop sig = _signature(); + char c = sig->byte_at(_index); + while ('0' <= c && c <= '9') c = sig->byte_at(++_index); +} + + +int SignatureIterator::parse_type() { + // Note: This function could be simplified by using "return T_XXX_size;" + // instead of the assignment and the break statements. However, it + // seems that the product build for win32_i486 with MS VC++ 6.0 doesn't + // work (stack underflow for some tests) - this seems to be a VC++ 6.0 + // compiler bug (was problem - gri 4/27/2000). + int size = -1; + switch(_signature->byte_at(_index)) { + case 'B': do_byte (); if (_parameter_index < 0 ) _return_type = T_BYTE; + _index++; size = T_BYTE_size ; break; + case 'C': do_char (); if (_parameter_index < 0 ) _return_type = T_CHAR; + _index++; size = T_CHAR_size ; break; + case 'D': do_double(); if (_parameter_index < 0 ) _return_type = T_DOUBLE; + _index++; size = T_DOUBLE_size ; break; + case 'F': do_float (); if (_parameter_index < 0 ) _return_type = T_FLOAT; + _index++; size = T_FLOAT_size ; break; + case 'I': do_int (); if (_parameter_index < 0 ) _return_type = T_INT; + _index++; size = T_INT_size ; break; + case 'J': do_long (); if (_parameter_index < 0 ) _return_type = T_LONG; + _index++; size = T_LONG_size ; break; + case 'S': do_short (); if (_parameter_index < 0 ) _return_type = T_SHORT; + _index++; size = T_SHORT_size ; break; + case 'Z': do_bool (); if (_parameter_index < 0 ) _return_type = T_BOOLEAN; + _index++; size = T_BOOLEAN_size; break; + case 'V': do_void (); if (_parameter_index < 0 ) _return_type = T_VOID; + _index++; size = T_VOID_size; ; break; + case 'L': + { int begin = ++_index; + symbolOop sig = _signature(); + while (sig->byte_at(_index++) != ';') ; + do_object(begin, _index); + } + if (_parameter_index < 0 ) _return_type = T_OBJECT; + size = T_OBJECT_size; + break; + case '[': + { int begin = ++_index; + skip_optional_size(); + symbolOop sig = _signature(); + while (sig->byte_at(_index) == '[') { + _index++; + skip_optional_size(); + } + if (sig->byte_at(_index) == 'L') { + while (sig->byte_at(_index++) != ';') ; + } else { + _index++; + } + do_array(begin, _index); + if (_parameter_index < 0 ) _return_type = T_ARRAY; + } + size = T_ARRAY_size; + break; + default: + ShouldNotReachHere(); + break; + } + assert(size >= 0, "size must be set"); + return size; +} + + +void SignatureIterator::check_signature_end() { + if (_index < _signature->utf8_length()) { + tty->print_cr("too many chars in signature"); + _signature->print_value_on(tty); + tty->print_cr(" @ %d", _index); + } +} + + +void SignatureIterator::dispatch_field() { + // no '(', just one (field) type + _index = 0; + _parameter_index = 0; + parse_type(); + check_signature_end(); +} + + +void SignatureIterator::iterate_parameters() { + // Parse parameters + _index = 0; + _parameter_index = 0; + expect('('); + while (_signature->byte_at(_index) != ')') _parameter_index += parse_type(); + expect(')'); + _parameter_index = 0; +} + +// Optimized version of iterat_parameters when fingerprint is known +void SignatureIterator::iterate_parameters( uint64_t fingerprint ) { + uint64_t saved_fingerprint = fingerprint; + + // Check for too many arguments + if ( fingerprint == UCONST64(-1) ) { + SignatureIterator::iterate_parameters(); + return; + } + + assert(fingerprint, "Fingerprint should not be 0"); + + _parameter_index = 0; + fingerprint = fingerprint >> (static_feature_size + result_feature_size); + while ( 1 ) { + switch ( fingerprint & parameter_feature_mask ) { + case bool_parm: + do_bool(); + _parameter_index += T_BOOLEAN_size; + break; + case byte_parm: + do_byte(); + _parameter_index += T_BYTE_size; + break; + case char_parm: + do_char(); + _parameter_index += T_CHAR_size; + break; + case short_parm: + do_short(); + _parameter_index += T_SHORT_size; + break; + case int_parm: + do_int(); + _parameter_index += T_INT_size; + break; + case obj_parm: + do_object(0, 0); + _parameter_index += T_OBJECT_size; + break; + case long_parm: + do_long(); + _parameter_index += T_LONG_size; + break; + case float_parm: + do_float(); + _parameter_index += T_FLOAT_size; + break; + case double_parm: + do_double(); + _parameter_index += T_DOUBLE_size; + break; + case done_parm: + return; + break; + default: + tty->print_cr("*** parameter is %d", fingerprint & parameter_feature_mask); + tty->print_cr("*** fingerprint is " PTR64_FORMAT, saved_fingerprint); + ShouldNotReachHere(); + break; + } + fingerprint >>= parameter_feature_size; + } + _parameter_index = 0; +} + + +void SignatureIterator::iterate_returntype() { + // Ignore parameters + _index = 0; + expect('('); + symbolOop sig = _signature(); + while (sig->byte_at(_index) != ')') _index++; + expect(')'); + // Parse return type + _parameter_index = -1; + parse_type(); + check_signature_end(); + _parameter_index = 0; +} + + +void SignatureIterator::iterate() { + // Parse parameters + _parameter_index = 0; + _index = 0; + expect('('); + while (_signature->byte_at(_index) != ')') _parameter_index += parse_type(); + expect(')'); + // Parse return type + _parameter_index = -1; + parse_type(); + check_signature_end(); + _parameter_index = 0; +} + + +// Implementation of SignatureStream + +bool SignatureStream::is_done() const { + return _end > _signature()->utf8_length(); +} + + +void SignatureStream::next_non_primitive(int t) { + switch (t) { + case 'L': { + _type = T_OBJECT; + symbolOop sig = _signature(); + while (sig->byte_at(_end++) != ';'); + break; + } + case '[': { + _type = T_ARRAY; + symbolOop sig = _signature(); + char c = sig->byte_at(_end); + while ('0' <= c && c <= '9') c = sig->byte_at(_end++); + while (sig->byte_at(_end) == '[') { + _end++; + c = sig->byte_at(_end); + while ('0' <= c && c <= '9') c = sig->byte_at(_end++); + } + switch(sig->byte_at(_end)) { + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z':_end++; break; + default: { + while (sig->byte_at(_end++) != ';'); + break; + } + } + break; + } + case ')': _end++; next(); _at_return_type = true; break; + default : ShouldNotReachHere(); + } +} + + +bool SignatureStream::is_object() const { + return _type == T_OBJECT + || _type == T_ARRAY; +} + +bool SignatureStream::is_array() const { + return _type == T_ARRAY; +} + +symbolOop SignatureStream::as_symbol(TRAPS) { + // Create a symbol from for string _begin _end + int begin = _begin; + int end = _end; + + if ( _signature()->byte_at(_begin) == 'L' + && _signature()->byte_at(_end-1) == ';') { + begin++; + end--; + } + + symbolOop result = oopFactory::new_symbol(_signature, begin, end, CHECK_NULL); + return result; +} + + +symbolOop SignatureStream::as_symbol_or_null() { + // Create a symbol from for string _begin _end + ResourceMark rm; + + int begin = _begin; + int end = _end; + + if ( _signature()->byte_at(_begin) == 'L' + && _signature()->byte_at(_end-1) == ';') { + begin++; + end--; + } + + char* buffer = NEW_RESOURCE_ARRAY(char, end - begin); + for (int index = begin; index < end; index++) { + buffer[index - begin] = _signature()->byte_at(index); + } + symbolOop result = SymbolTable::probe(buffer, end - begin); + return result; +} + +bool SignatureVerifier::is_valid_signature(symbolHandle sig) { + const char* signature = (const char*)sig->bytes(); + ssize_t len = sig->utf8_length(); + if (signature == NULL || signature[0] == '\0' || len < 1) { + return false; + } else if (signature[0] == '(') { + return is_valid_method_signature(sig); + } else { + return is_valid_type_signature(sig); + } +} + +bool SignatureVerifier::is_valid_method_signature(symbolHandle sig) { + const char* method_sig = (const char*)sig->bytes(); + ssize_t len = sig->utf8_length(); + ssize_t index = 0; + if (method_sig != NULL && len > 1 && method_sig[index] == '(') { + ++index; + while (index < len && method_sig[index] != ')') { + ssize_t res = is_valid_type(&method_sig[index], len - index); + if (res == -1) { + return false; + } else { + index += res; + } + } + if (index < len && method_sig[index] == ')') { + // check the return type + ++index; + return (is_valid_type(&method_sig[index], len - index) == (len - index)); + } + } + return false; +} + +bool SignatureVerifier::is_valid_type_signature(symbolHandle sig) { + const char* type_sig = (const char*)sig->bytes(); + ssize_t len = sig->utf8_length(); + return (type_sig != NULL && len >= 1 && + (is_valid_type(type_sig, len) == len)); +} + +// Checks to see if the type (not to go beyond 'limit') refers to a valid type. +// Returns -1 if it is not, or the index of the next character that is not part +// of the type. The type encoding may end before 'limit' and that's ok. +ssize_t SignatureVerifier::is_valid_type(const char* type, ssize_t limit) { + ssize_t index = 0; + + // Iterate over any number of array dimensions + while (index < limit && type[index] == '[') ++index; + if (index >= limit) { + return -1; + } + switch (type[index]) { + case 'B': case 'C': case 'D': case 'F': case 'I': + case 'J': case 'S': case 'Z': case 'V': + return index + 1; + case 'L': + for (index = index + 1; index < limit; ++index) { + char c = type[index]; + if (c == ';') { + return index + 1; + } + if (invalid_name_char(c)) { + return -1; + } + } + // fall through + default: ; // fall through + } + return -1; +} + +bool SignatureVerifier::invalid_name_char(char c) { + switch (c) { + case '\0': case '.': case ';': case '[': + return true; + default: + return false; + } +} diff --git a/hotspot/src/share/vm/runtime/signature.hpp b/hotspot/src/share/vm/runtime/signature.hpp new file mode 100644 index 00000000000..51b45a0a063 --- /dev/null +++ b/hotspot/src/share/vm/runtime/signature.hpp @@ -0,0 +1,416 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// SignatureIterators iterate over a Java signature (or parts of it). +// (Syntax according to: "The Java Virtual Machine Specification" by +// Tim Lindholm & Frank Yellin; section 4.3 Descriptors; p. 89ff.) +// +// Example: Iterating over ([Lfoo;D)I using +// 0123456789 +// +// iterate_parameters() calls: do_array(2, 7); do_double(); +// iterate_returntype() calls: do_int(); +// iterate() calls: do_array(2, 7); do_double(); do_int(); +// +// is_return_type() is: false ; false ; true +// +// NOTE: The new optimizer has an alternate, for-loop based signature +// iterator implemented in opto/type.cpp, TypeTuple::make(). + +class SignatureIterator: public ResourceObj { + protected: + symbolHandle _signature; // the signature to iterate over + int _index; // the current character index (only valid during iteration) + int _parameter_index; // the current parameter index (0 outside iteration phase) + BasicType _return_type; + + void expect(char c); + void skip_optional_size(); + int parse_type(); // returns the parameter size in words (0 for void) + void check_signature_end(); + + public: + // Definitions used in generating and iterating the + // bit field form of the signature generated by the + // Fingerprinter. + enum { + static_feature_size = 1, + result_feature_size = 4, + result_feature_mask = 0xF, + parameter_feature_size = 4, + parameter_feature_mask = 0xF, + + bool_parm = 1, + byte_parm = 2, + char_parm = 3, + short_parm = 4, + int_parm = 5, + long_parm = 6, + float_parm = 7, + double_parm = 8, + obj_parm = 9, + done_parm = 10, // marker for end of parameters + + // max parameters is wordsize minus + // The sign bit, termination field, the result and static bit fields + max_size_of_parameters = (BitsPerLong-1 - + result_feature_size - parameter_feature_size - + static_feature_size) / parameter_feature_size + }; + + // Constructors + SignatureIterator(symbolOop signature); + SignatureIterator(Thread *thread, symbolOop signature); + SignatureIterator(symbolHandle signature); + + // Iteration + void dispatch_field(); // dispatches once for field signatures + void iterate_parameters(); // iterates over parameters only + void iterate_parameters( uint64_t fingerprint ); + void iterate_returntype(); // iterates over returntype only + void iterate(); // iterates over whole signature + // Returns the word index of the current parameter; + int parameter_index() const { return _parameter_index; } + bool is_return_type() const { return parameter_index() < 0; } + BasicType get_ret_type() const { return _return_type; } + + // Basic types + virtual void do_bool () = 0; + virtual void do_char () = 0; + virtual void do_float () = 0; + virtual void do_double() = 0; + virtual void do_byte () = 0; + virtual void do_short () = 0; + virtual void do_int () = 0; + virtual void do_long () = 0; + virtual void do_void () = 0; + + // Object types (begin indexes the first character of the entry, end indexes the first character after the entry) + virtual void do_object(int begin, int end) = 0; + virtual void do_array (int begin, int end) = 0; +}; + + +// Specialized SignatureIterators: Used to compute signature specific values. + +class SignatureTypeNames : public SignatureIterator { + protected: + virtual void type_name(const char* name) = 0; + + void do_bool() { type_name("jboolean"); } + void do_char() { type_name("jchar" ); } + void do_float() { type_name("jfloat" ); } + void do_double() { type_name("jdouble" ); } + void do_byte() { type_name("jbyte" ); } + void do_short() { type_name("jshort" ); } + void do_int() { type_name("jint" ); } + void do_long() { type_name("jlong" ); } + void do_void() { type_name("void" ); } + void do_object(int begin, int end) { type_name("jobject" ); } + void do_array (int begin, int end) { type_name("jobject" ); } + + public: + SignatureTypeNames(symbolHandle signature) : SignatureIterator(signature) {} +}; + + +class SignatureInfo: public SignatureIterator { + protected: + bool _has_iterated; // need this because iterate cannot be called in constructor (set is virtual!) + bool _has_iterated_return; + int _size; + + void lazy_iterate_parameters() { if (!_has_iterated) { iterate_parameters(); _has_iterated = true; } } + void lazy_iterate_return() { if (!_has_iterated_return) { iterate_returntype(); _has_iterated_return = true; } } + + virtual void set(int size, BasicType type) = 0; + + void do_bool () { set(T_BOOLEAN_size, T_BOOLEAN); } + void do_char () { set(T_CHAR_size , T_CHAR ); } + void do_float () { set(T_FLOAT_size , T_FLOAT ); } + void do_double() { set(T_DOUBLE_size , T_DOUBLE ); } + void do_byte () { set(T_BYTE_size , T_BYTE ); } + void do_short () { set(T_SHORT_size , T_SHORT ); } + void do_int () { set(T_INT_size , T_INT ); } + void do_long () { set(T_LONG_size , T_LONG ); } + void do_void () { set(T_VOID_size , T_VOID ); } + void do_object(int begin, int end) { set(T_OBJECT_size , T_OBJECT ); } + void do_array (int begin, int end) { set(T_ARRAY_size , T_ARRAY ); } + + public: + SignatureInfo(symbolHandle signature) : SignatureIterator(signature) { + _has_iterated = _has_iterated_return = false; + _size = 0; + _return_type = T_ILLEGAL; + } + +}; + + +// Specialized SignatureIterator: Used to compute the argument size. + +class ArgumentSizeComputer: public SignatureInfo { + private: + void set(int size, BasicType type) { _size += size; } + public: + ArgumentSizeComputer(symbolHandle signature) : SignatureInfo(signature) {} + + int size() { lazy_iterate_parameters(); return _size; } +}; + + +class ArgumentCount: public SignatureInfo { + private: + void set(int size, BasicType type) { _size ++; } + public: + ArgumentCount(symbolHandle signature) : SignatureInfo(signature) {} + + int size() { lazy_iterate_parameters(); return _size; } +}; + + +// Specialized SignatureIterator: Used to compute the result type. + +class ResultTypeFinder: public SignatureInfo { + private: + void set(int size, BasicType type) { _return_type = type; } + public: + BasicType type() { lazy_iterate_return(); return _return_type; } + + ResultTypeFinder(symbolHandle signature) : SignatureInfo(signature) {} +}; + + +// Fingerprinter computes a unique ID for a given method. The ID +// is a bitvector characterizing the methods signature (incl. the receiver). +class Fingerprinter: public SignatureIterator { + private: + uint64_t _fingerprint; + int _shift_count; + methodHandle mh; + + public: + + void do_bool() { _fingerprint |= (((uint64_t)bool_parm) << _shift_count); _shift_count += parameter_feature_size; } + void do_char() { _fingerprint |= (((uint64_t)char_parm) << _shift_count); _shift_count += parameter_feature_size; } + void do_byte() { _fingerprint |= (((uint64_t)byte_parm) << _shift_count); _shift_count += parameter_feature_size; } + void do_short() { _fingerprint |= (((uint64_t)short_parm) << _shift_count); _shift_count += parameter_feature_size; } + void do_int() { _fingerprint |= (((uint64_t)int_parm) << _shift_count); _shift_count += parameter_feature_size; } + void do_long() { _fingerprint |= (((uint64_t)long_parm) << _shift_count); _shift_count += parameter_feature_size; } + void do_float() { _fingerprint |= (((uint64_t)float_parm) << _shift_count); _shift_count += parameter_feature_size; } + void do_double() { _fingerprint |= (((uint64_t)double_parm) << _shift_count); _shift_count += parameter_feature_size; } + + void do_object(int begin, int end) { _fingerprint |= (((uint64_t)obj_parm) << _shift_count); _shift_count += parameter_feature_size; } + void do_array (int begin, int end) { _fingerprint |= (((uint64_t)obj_parm) << _shift_count); _shift_count += parameter_feature_size; } + + void do_void() { ShouldNotReachHere(); } + + Fingerprinter(methodHandle method) : SignatureIterator(method->signature()) { + mh = method; + _fingerprint = 0; + } + + Fingerprinter(Thread *thread, methodHandle method) : SignatureIterator(thread, method->signature()) { + mh = method; + _fingerprint = 0; + } + + uint64_t fingerprint() { + // See if we fingerprinted this method already + if (mh->constMethod()->fingerprint() != CONST64(0)) { + return mh->constMethod()->fingerprint(); + } + + if (mh->size_of_parameters() > max_size_of_parameters ) { + _fingerprint = UCONST64(-1); + mh->constMethod()->set_fingerprint(_fingerprint); + return _fingerprint; + } + + assert( (int)mh->result_type() <= (int)result_feature_mask, "bad result type"); + _fingerprint = mh->result_type(); + _fingerprint <<= static_feature_size; + if (mh->is_static()) _fingerprint |= 1; + _shift_count = result_feature_size + static_feature_size; + iterate_parameters(); + _fingerprint |= ((uint64_t)done_parm) << _shift_count;// mark end of sig + mh->constMethod()->set_fingerprint(_fingerprint); + return _fingerprint; + } +}; + + +// Specialized SignatureIterator: Used for native call purposes + +class NativeSignatureIterator: public SignatureIterator { + private: + methodHandle _method; +// We need seperate JNI and Java offset values because in 64 bit mode, +// the argument offsets are not in sync with the Java stack. +// For example a long takes up 1 "C" stack entry but 2 Java stack entries. + int _offset; // The java stack offset + int _prepended; // number of prepended JNI parameters (1 JNIEnv, plus 1 mirror if static) + int _jni_offset; // the current parameter offset, starting with 0 + + void do_bool () { pass_int(); _jni_offset++; _offset++; } + void do_char () { pass_int(); _jni_offset++; _offset++; } +#ifdef _LP64 + void do_float () { pass_float(); _jni_offset++; _offset++; } + void do_double() { pass_double(); _jni_offset++; _offset += 2; } +#else + void do_float () { pass_int(); _jni_offset++; _offset++; } + void do_double() { pass_double(); _jni_offset += 2; _offset += 2; } +#endif + void do_byte () { pass_int(); _jni_offset++; _offset++; } + void do_short () { pass_int(); _jni_offset++; _offset++; } + void do_int () { pass_int(); _jni_offset++; _offset++; } +#ifdef _LP64 + void do_long () { pass_long(); _jni_offset++; _offset += 2; } +#else + void do_long () { pass_long(); _jni_offset += 2; _offset += 2; } +#endif + void do_void () { ShouldNotReachHere(); } + void do_object(int begin, int end) { pass_object(); _jni_offset++; _offset++; } + void do_array (int begin, int end) { pass_object(); _jni_offset++; _offset++; } + + public: + methodHandle method() const { return _method; } + int offset() const { return _offset; } + int jni_offset() const { return _jni_offset + _prepended; } +// int java_offset() const { return method()->size_of_parameters() - _offset - 1; } + bool is_static() const { return method()->is_static(); } + virtual void pass_int() = 0; + virtual void pass_long() = 0; + virtual void pass_object() = 0; +#ifdef _LP64 + virtual void pass_float() = 0; + virtual void pass_double() = 0; +#else + virtual void pass_double() { pass_long(); } // may be same as long +#endif + + NativeSignatureIterator(methodHandle method) : SignatureIterator(method->signature()) { + _method = method; + _offset = 0; + _jni_offset = 0; + + const int JNIEnv_words = 1; + const int mirror_words = 1; + _prepended = !is_static() ? JNIEnv_words : JNIEnv_words + mirror_words; + } + + // iterate() calles the 2 virtual methods according to the following invocation syntax: + // + // {pass_int | pass_long | pass_object} + // + // Arguments are handled from left to right (receiver first, if any). + // The offset() values refer to the Java stack offsets but are 0 based and increasing. + // The java_offset() values count down to 0, and refer to the Java TOS. + // The jni_offset() values increase from 1 or 2, and refer to C arguments. + + void iterate() { iterate(Fingerprinter(method()).fingerprint()); + } + + + // Optimized path if we have the bitvector form of signature + void iterate( uint64_t fingerprint ) { + + if (!is_static()) { + // handle receiver (not handled by iterate because not in signature) + pass_object(); _jni_offset++; _offset++; + } + + SignatureIterator::iterate_parameters( fingerprint ); + } +}; + + +// Handy stream for iterating over signature + +class SignatureStream : public StackObj { + private: + symbolHandle _signature; + int _begin; + int _end; + BasicType _type; + bool _at_return_type; + + public: + bool at_return_type() const { return _at_return_type; } + bool is_done() const; + void next_non_primitive(int t); + void next() { + symbolOop sig = _signature(); + int len = sig->utf8_length(); + if (_end >= len) { + _end = len + 1; + return; + } + + _begin = _end; + int t = sig->byte_at(_begin); + switch (t) { + case 'B': _type = T_BYTE; break; + case 'C': _type = T_CHAR; break; + case 'D': _type = T_DOUBLE; break; + case 'F': _type = T_FLOAT; break; + case 'I': _type = T_INT; break; + case 'J': _type = T_LONG; break; + case 'S': _type = T_SHORT; break; + case 'Z': _type = T_BOOLEAN; break; + case 'V': _type = T_VOID; break; + default : next_non_primitive(t); + return; + } + _end++; + } + + SignatureStream(symbolHandle signature, + bool is_method = true) : + _signature(signature), _at_return_type(false) { + _begin = _end = (is_method ? 1 : 0); // skip first '(' in method signatures + next(); + } + + bool is_object() const; // True if this argument is an object + bool is_array() const; // True if this argument is an array + BasicType type() const { return _type; } + symbolOop as_symbol(TRAPS); + + // return same as_symbol except allocation of new symbols is avoided. + symbolOop as_symbol_or_null(); +}; + +class SignatureVerifier : public StackObj { + public: + // Returns true if the symbol is valid method or type signature + static bool is_valid_signature(symbolHandle sig); + + static bool is_valid_method_signature(symbolHandle sig); + static bool is_valid_type_signature(symbolHandle sig); + private: + + static ssize_t is_valid_type(const char*, ssize_t); + static bool invalid_name_char(char); +}; diff --git a/hotspot/src/share/vm/runtime/stackValue.cpp b/hotspot/src/share/vm/runtime/stackValue.cpp new file mode 100644 index 00000000000..65387af23a9 --- /dev/null +++ b/hotspot/src/share/vm/runtime/stackValue.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_stackValue.cpp.incl" + +StackValue* StackValue::create_stack_value(const frame* fr, const RegisterMap* reg_map, ScopeValue* sv) { + if (sv->is_location()) { + // Stack or register value + Location loc = ((LocationValue *)sv)->location(); + +#ifdef SPARC + // %%%%% Callee-save floats will NOT be working on a Sparc until we + // handle the case of a 2 floats in a single double register. + assert( !(loc.is_register() && loc.type() == Location::float_in_dbl), "Sparc does not handle callee-save floats yet" ); +#endif // SPARC + + // First find address of value + + address value_addr = loc.is_register() + // Value was in a callee-save register + ? reg_map->location(VMRegImpl::as_VMReg(loc.register_number())) + // Else value was directly saved on the stack. The frame's original stack pointer, + // before any extension by its callee (due to Compiler1 linkage on SPARC), must be used. + : ((address)fr->unextended_sp()) + loc.stack_offset(); + + // Then package it right depending on type + // Note: the transfer of the data is thru a union that contains + // an intptr_t. This is because an interpreter stack slot is + // really an intptr_t. The use of a union containing an intptr_t + // ensures that on a 64 bit platform we have proper alignment + // and that we store the value where the interpreter will expect + // to find it (i.e. proper endian). Similarly on a 32bit platform + // using the intptr_t ensures that when a value is larger than + // a stack slot (jlong/jdouble) that we capture the proper part + // of the value for the stack slot in question. + // + switch( loc.type() ) { + case Location::float_in_dbl: { // Holds a float in a double register? + // The callee has no clue whether the register holds a float, + // double or is unused. He always saves a double. Here we know + // a double was saved, but we only want a float back. Narrow the + // saved double to the float that the JVM wants. + assert( loc.is_register(), "floats always saved to stack in 1 word" ); + union { intptr_t p; jfloat jf; } value; + value.p = (intptr_t) CONST64(0xDEADDEAFDEADDEAF); + value.jf = (jfloat) *(jdouble*) value_addr; + return new StackValue(value.p); // 64-bit high half is stack junk + } + case Location::int_in_long: { // Holds an int in a long register? + // The callee has no clue whether the register holds an int, + // long or is unused. He always saves a long. Here we know + // a long was saved, but we only want an int back. Narrow the + // saved long to the int that the JVM wants. + assert( loc.is_register(), "ints always saved to stack in 1 word" ); + union { intptr_t p; jint ji;} value; + value.p = (intptr_t) CONST64(0xDEADDEAFDEADDEAF); + value.ji = (jint) *(jlong*) value_addr; + return new StackValue(value.p); // 64-bit high half is stack junk + } +#ifdef _LP64 + case Location::dbl: + // Double value in an aligned adjacent pair + return new StackValue(*(intptr_t*)value_addr); + case Location::lng: + // Long value in an aligned adjacent pair + return new StackValue(*(intptr_t*)value_addr); +#endif + case Location::oop: { + Handle h(*(oop *)value_addr); // Wrap a handle around the oop + return new StackValue(h); + } + case Location::addr: { + ShouldNotReachHere(); // both C1 and C2 now inline jsrs + } + case Location::normal: { + // Just copy all other bits straight through + union { intptr_t p; jint ji;} value; + value.p = (intptr_t) CONST64(0xDEADDEAFDEADDEAF); + value.ji = *(jint*)value_addr; + return new StackValue(value.p); + } + case Location::invalid: + return new StackValue(); + default: + ShouldNotReachHere(); + } + + } else if (sv->is_constant_int()) { + // Constant int: treat same as register int. + union { intptr_t p; jint ji;} value; + value.p = (intptr_t) CONST64(0xDEADDEAFDEADDEAF); + value.ji = (jint)((ConstantIntValue*)sv)->value(); + return new StackValue(value.p); + } else if (sv->is_constant_oop()) { + // constant oop + return new StackValue(((ConstantOopReadValue *)sv)->value()); +#ifdef _LP64 + } else if (sv->is_constant_double()) { + // Constant double in a single stack slot + union { intptr_t p; double d; } value; + value.p = (intptr_t) CONST64(0xDEADDEAFDEADDEAF); + value.d = ((ConstantDoubleValue *)sv)->value(); + return new StackValue(value.p); + } else if (sv->is_constant_long()) { + // Constant long in a single stack slot + union { intptr_t p; jlong jl; } value; + value.p = (intptr_t) CONST64(0xDEADDEAFDEADDEAF); + value.jl = ((ConstantLongValue *)sv)->value(); + return new StackValue(value.p); +#endif + } else if (sv->is_object()) { + return new StackValue(((ObjectValue *)sv)->value()); + } + + // Unknown ScopeValue type + ShouldNotReachHere(); + return new StackValue((intptr_t) 0); // dummy +} + + +BasicLock* StackValue::resolve_monitor_lock(const frame* fr, Location location) { + assert(location.is_stack(), "for now we only look at the stack"); + int word_offset = location.stack_offset() / wordSize; + // (stack picture) + // high: [ ] word_offset + 1 + // low [ ] word_offset + // + // sp-> [ ] 0 + // the word_offset is the distance from the stack pointer to the lowest address + // The frame's original stack pointer, before any extension by its callee + // (due to Compiler1 linkage on SPARC), must be used. + return (BasicLock*) (fr->unextended_sp() + word_offset); +} + + +#ifndef PRODUCT + +void StackValue::print_on(outputStream* st) const { + switch(_type) { + case T_INT: + st->print("%d (int) %f (float) %x (hex)", *(int *)&_i, *(float *)&_i, *(int *)&_i); + break; + + case T_OBJECT: + _o()->print_value_on(st); + st->print(" <" INTPTR_FORMAT ">", (address)_o()); + break; + + case T_CONFLICT: + st->print("conflict"); + break; + + default: + ShouldNotReachHere(); + } +} + +#endif diff --git a/hotspot/src/share/vm/runtime/stackValue.hpp b/hotspot/src/share/vm/runtime/stackValue.hpp new file mode 100644 index 00000000000..6296b8073a4 --- /dev/null +++ b/hotspot/src/share/vm/runtime/stackValue.hpp @@ -0,0 +1,101 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class StackValue : public ResourceObj { + private: + BasicType _type; + intptr_t _i; // Blank java stack slot value + Handle _o; // Java stack slot value interpreted as a Handle + public: + + StackValue(intptr_t value) { + _type = T_INT; + _i = value; + } + + StackValue(Handle value) { + _type = T_OBJECT; + _o = value; + } + + StackValue() { + _type = T_CONFLICT; + _i = 0; + } + + // Only used during deopt- preserve object type. + StackValue(intptr_t o, BasicType t) { + assert(t == T_OBJECT, "should not be used"); + _type = t; + _i = o; + } + + Handle get_obj() const { + assert(type() == T_OBJECT, "type check"); + return _o; + } + + void set_obj(Handle value) { + assert(type() == T_OBJECT, "type check"); + _o = value; + } + + intptr_t get_int() const { + assert(type() == T_INT, "type check"); + return _i; + } + + // For special case in deopt. + intptr_t get_int(BasicType t) const { + assert(t == T_OBJECT && type() == T_OBJECT, "type check"); + return _i; + } + + void set_int(intptr_t value) { + assert(type() == T_INT, "type check"); + _i = value; + } + + BasicType type() const { return _type; } + + bool equal(StackValue *value) { + if (_type != value->_type) return false; + if (_type == T_OBJECT) + return (_o == value->_o); + else { + assert(_type == T_INT, "sanity check"); + // [phh] compare only low addressed portions of intptr_t slots + return (*(int *)&_i == *(int *)&value->_i); + } + } + + static StackValue* create_stack_value(const frame* fr, const RegisterMap* reg_map, ScopeValue* sv); + static BasicLock* resolve_monitor_lock(const frame* fr, Location location); + +#ifndef PRODUCT + public: + // Printing + void print_on(outputStream* st) const; +#endif +}; diff --git a/hotspot/src/share/vm/runtime/stackValueCollection.cpp b/hotspot/src/share/vm/runtime/stackValueCollection.cpp new file mode 100644 index 00000000000..09f27732dab --- /dev/null +++ b/hotspot/src/share/vm/runtime/stackValueCollection.cpp @@ -0,0 +1,149 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_stackValueCollection.cpp.incl" + +jint StackValueCollection::int_at(int slot) const { + intptr_t val = at(slot)->get_int(); + jint ival = *((jint*) (&val)); + return ival; +} + +jlong StackValueCollection::long_at(int slot) const { +#ifdef _LP64 + return at(slot+1)->get_int(); +#else + union { + jlong jl; + jint array[2]; + } value; + // Interpreter stack is reversed in memory: + // low memory location is in higher java local slot. + value.array[0] = at(slot+1)->get_int(); + value.array[1] = at(slot )->get_int(); + return value.jl; +#endif +} + +Handle StackValueCollection::obj_at(int slot) const { + return at(slot)->get_obj(); +} + +jfloat StackValueCollection::float_at(int slot) const { + intptr_t res = at(slot)->get_int(); + return *((jfloat*) (&res)); +} + +jdouble StackValueCollection::double_at(int slot) const { +#ifdef _LP64 + intptr_t res = at(slot+1)->get_int(); + return *((jdouble*) (&res)); +#else + union { + jdouble jd; + jint array[2]; + } value; + // Interpreter stack is reversed in memory: + // low memory location is in higher java local slot. + value.array[0] = at(slot+1)->get_int(); + value.array[1] = at(slot )->get_int(); + return value.jd; +#endif +} + +void StackValueCollection::set_int_at(int slot, jint value) { + intptr_t val; + *((jint*) (&val)) = value; + at(slot)->set_int(val); +} + +void StackValueCollection::set_long_at(int slot, jlong value) { +#ifdef _LP64 + at(slot+1)->set_int(value); +#else + union { + jlong jl; + jint array[2]; + } x; + // Interpreter stack is reversed in memory: + // low memory location is in higher java local slot. + x.jl = value; + at(slot+1)->set_int(x.array[0]); + at(slot+0)->set_int(x.array[1]); +#endif +} + +void StackValueCollection::set_obj_at(int slot, Handle value) { + at(slot)->set_obj(value); +} + +void StackValueCollection::set_float_at(int slot, jfloat value) { +#ifdef _LP64 + union { + intptr_t jd; + jint array[2]; + } val; + // Interpreter stores 32 bit floats in first half of 64 bit word. + val.array[0] = *(jint*)(&value); + val.array[1] = 0; + at(slot)->set_int(val.jd); +#else + at(slot)->set_int(*(jint*)(&value)); +#endif +} + +void StackValueCollection::set_double_at(int slot, jdouble value) { +#ifdef _LP64 + at(slot+1)->set_int(*(intptr_t*)(&value)); +#else + union { + jdouble jd; + jint array[2]; + } x; + // Interpreter stack is reversed in memory: + // low memory location is in higher java local slot. + x.jd = value; + at(slot+1)->set_int(x.array[0]); + at(slot+0)->set_int(x.array[1]); +#endif +} + +#ifndef PRODUCT +void StackValueCollection::print() { + for(int index = 0; index < size(); index++) { + tty->print("\t %2d ", index); + at(index)->print_on(tty); + if( at(index )->type() == T_INT && + index+1 < size() && + at(index+1)->type() == T_INT ) { + tty->print(" " INT64_FORMAT " (long)", long_at(index)); + tty->cr(); + tty->print("\t %.15e (double)", double_at(index)); + tty->print(" " PTR64_FORMAT " (longhex)", long_at(index)); + } + tty->cr(); + } +} +#endif diff --git a/hotspot/src/share/vm/runtime/stackValueCollection.hpp b/hotspot/src/share/vm/runtime/stackValueCollection.hpp new file mode 100644 index 00000000000..14de4051780 --- /dev/null +++ b/hotspot/src/share/vm/runtime/stackValueCollection.hpp @@ -0,0 +1,53 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class StackValueCollection : public ResourceObj { + private: + GrowableArray* _values; + + public: + StackValueCollection() { _values = new GrowableArray(); } + StackValueCollection(int length) { _values = new GrowableArray(length); } + + void add(StackValue *val) const { _values->push(val); } + int size() const { return _values->length(); } + bool is_empty() const { return (size() == 0); } + StackValue* at(int i) const { return _values->at(i); } + + // Get typed locals/expressions + jint int_at(int slot) const; + jlong long_at(int slot) const; + Handle obj_at(int slot) const; + jfloat float_at(int slot) const; + jdouble double_at(int slot) const; + + // Set typed locals/expressions + void set_int_at(int slot, jint value); + void set_long_at(int slot, jlong value); + void set_obj_at(int slot, Handle value); + void set_float_at(int slot, jfloat value); + void set_double_at(int slot, jdouble value); + + void print(); +}; diff --git a/hotspot/src/share/vm/runtime/statSampler.cpp b/hotspot/src/share/vm/runtime/statSampler.cpp new file mode 100644 index 00000000000..f7205b9e189 --- /dev/null +++ b/hotspot/src/share/vm/runtime/statSampler.cpp @@ -0,0 +1,359 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_statSampler.cpp.incl" + +// -------------------------------------------------------- +// StatSamplerTask + +class StatSamplerTask : public PeriodicTask { + public: + StatSamplerTask(int interval_time) : PeriodicTask(interval_time) {} + void task() { StatSampler::collect_sample(); } +}; + + +//---------------------------------------------------------- +// Implementation of StatSampler + +StatSamplerTask* StatSampler::_task = NULL; +PerfDataList* StatSampler::_sampled = NULL; + +/* + * the initialize method is called from the engage() method + * and is responsible for initializing various global variables. + */ +void StatSampler::initialize() { + + if (!UsePerfData) return; + + // create performance data that could not be created prior + // to vm_init_globals() or otherwise have no logical home. + + create_misc_perfdata(); + + // get copy of the sampled list + _sampled = PerfDataManager::sampled(); + +} + +/* + * The engage() method is called at initialization time via + * Thread::create_vm() to initialize the StatSampler and + * register it with the WatcherThread as a periodic task. + */ +void StatSampler::engage() { + + if (!UsePerfData) return; + + if (!is_active()) { + + initialize(); + + // start up the periodic task + _task = new StatSamplerTask(PerfDataSamplingInterval); + _task->enroll(); + } +} + + +/* + * the disengage() method is responsible for deactivating the periodic + * task and, if logging was enabled, for logging the final sample. This + * method is called from before_exit() in java.cpp and is only called + * after the WatcherThread has been stopped. + */ +void StatSampler::disengage() { + + if (!UsePerfData) return; + + if (!is_active()) + return; + + // remove StatSamplerTask + _task->disenroll(); + delete _task; + _task = NULL; + + // force a final sample + sample_data(_sampled); +} + +/* + * the destroy method is responsible for releasing any resources used by + * the StatSampler prior to shutdown of the VM. this method is called from + * before_exit() in java.cpp and is only called after the WatcherThread + * has stopped. + */ +void StatSampler::destroy() { + + if (!UsePerfData) return; + + if (_sampled != NULL) { + delete(_sampled); + _sampled = NULL; + } +} + +/* + * The sample_data() method is responsible for sampling the + * the data value for each PerfData instance in the given list. + */ +void StatSampler::sample_data(PerfDataList* list) { + + assert(list != NULL, "null list unexpected"); + + for (int index = 0; index < list->length(); index++) { + PerfData* item = list->at(index); + item->sample(); + } +} + +/* + * the collect_sample() method is the method invoked by the + * WatcherThread via the PeriodicTask::task() method. This method + * is responsible for collecting data samples from sampled + * PerfData instances every PerfDataSamplingInterval milliseconds. + * It is also responsible for logging the requested set of + * PerfData instances every _sample_count milliseconds. While + * logging data, it will output a column header after every _print_header + * rows of data have been logged. + */ +void StatSampler::collect_sample() { + + // future - check for new PerfData objects. PerfData objects might + // get added to the PerfDataManager lists after we have already + // built our local copies. + // + // if (PerfDataManager::count() > previous) { + // // get a new copy of the sampled list + // if (_sampled != NULL) { + // delete(_sampled); + // _sampled = NULL; + // } + // _sampled = PerfDataManager::sampled(); + // } + + assert(_sampled != NULL, "list not initialized"); + + sample_data(_sampled); +} + +/* + * method to upcall into Java to return the value of the specified + * property as a utf8 string, or NULL if does not exist. The caller + * is responsible for setting a ResourceMark for proper cleanup of + * the utf8 strings. + */ +const char* StatSampler::get_system_property(const char* name, TRAPS) { + + // setup the arguments to getProperty + Handle key_str = java_lang_String::create_from_str(name, CHECK_NULL); + + // return value + JavaValue result(T_OBJECT); + + // public static String getProperty(String key, String def); + JavaCalls::call_static(&result, + KlassHandle(THREAD, SystemDictionary::system_klass()), + vmSymbolHandles::getProperty_name(), + vmSymbolHandles::string_string_signature(), + key_str, + CHECK_NULL); + + oop value_oop = (oop)result.get_jobject(); + if (value_oop == NULL) { + return NULL; + } + + // convert Java String to utf8 string + char* value = java_lang_String::as_utf8_string(value_oop); + + return value; +} + +/* + * The list of System Properties that have corresponding PerfData + * string instrumentation created by retrieving the named property's + * value from System.getProperty() and unconditionally creating a + * PerfStringConstant object initialized to the retreived value. This + * is not an exhustive list of Java properties with corresponding string + * instrumentation as the create_system_property_instrumentation() method + * creates other property based instrumentation conditionally. + */ + +// stable interface, supported counters +static const char* property_counters_ss[] = { + "java.vm.specification.version", + "java.vm.specification.name", + "java.vm.specification.vendor", + "java.vm.version", + "java.vm.name", + "java.vm.vendor", + "java.vm.info", + "java.library.path", + "java.class.path", + "java.endorsed.dirs", + "java.ext.dirs", + "java.home", + NULL +}; + +// unstable interface, supported counters +static const char* property_counters_us[] = { + NULL +}; + +// unstable interface, unsupported counters +static const char* property_counters_uu[] = { + "sun.boot.class.path", + "sun.boot.library.path", + NULL +}; + +typedef struct { + const char** property_list; + CounterNS name_space; +} PropertyCounters; + +static PropertyCounters property_counters[] = { + { property_counters_ss, JAVA_PROPERTY }, + { property_counters_us, COM_PROPERTY }, + { property_counters_uu, SUN_PROPERTY }, + { NULL, SUN_PROPERTY } +}; + + +/* + * Method to create PerfData string instruments that contain the values + * of various system properties. String instruments are created for each + * property specified in the property lists provided in property_counters[]. + * Property counters have a counter name space prefix prepended to the + * property name as indicated in property_counters[]. + */ +void StatSampler::create_system_property_instrumentation(TRAPS) { + + ResourceMark rm; + + for (int i = 0; property_counters[i].property_list != NULL; i++) { + + for (int j = 0; property_counters[i].property_list[j] != NULL; j++) { + + const char* property_name = property_counters[i].property_list[j]; + assert(property_name != NULL, "property name should not be NULL"); + + const char* value = get_system_property(property_name, CHECK); + + // the property must exist + assert(value != NULL, "property name should be valid"); + + if (value != NULL) { + // create the property counter + PerfDataManager::create_string_constant(property_counters[i].name_space, + property_name, value, CHECK); + } + } + } +} + +/* + * The create_misc_perfdata() method provides a place to create + * PerfData instances that would otherwise have no better place + * to exist. + */ +void StatSampler::create_misc_perfdata() { + + ResourceMark rm; + EXCEPTION_MARK; + + // numeric constants + + // frequency of the native high resolution timer + PerfDataManager::create_constant(SUN_OS, "hrt.frequency", + PerfData::U_Hertz, os::elapsed_frequency(), + CHECK); + + // string constants + + // create string instrumentation for various Java properties. + create_system_property_instrumentation(CHECK); + + // hotspot flags (from .hotspotrc) and args (from command line) + // + PerfDataManager::create_string_constant(JAVA_RT, "vmFlags", + Arguments::jvm_flags(), CHECK); + PerfDataManager::create_string_constant(JAVA_RT, "vmArgs", + Arguments::jvm_args(), CHECK); + + // java class name/jar file and arguments to main class + // note: name is cooridnated with launcher and Arguments.cpp + PerfDataManager::create_string_constant(SUN_RT, "javaCommand", + Arguments::java_command(), CHECK); + + // the Java VM Internal version string + PerfDataManager::create_string_constant(SUN_RT, "internalVersion", + VM_Version::internal_vm_info_string(), + CHECK); + + // create sampled instrumentation objects + create_sampled_perfdata(); +} + +/* + * helper class to provide for sampling of the elapsed_counter value + * maintained in the OS class. + */ +class HighResTimeSampler : public PerfSampleHelper { + public: + jlong take_sample() { return os::elapsed_counter(); } +}; + +/* + * the create_sampled_perdata() method provides a place to instantiate + * sampled PerfData instances that would otherwise have no better place + * to exist. + */ +void StatSampler::create_sampled_perfdata() { + + EXCEPTION_MARK; + + // setup sampling of the elapsed time counter maintained in the + // the os class. This counter can be used as either a time stamp + // for each logged entry or as a liveness indicator for the VM. + PerfSampleHelper* psh = new HighResTimeSampler(); + PerfDataManager::create_counter(SUN_OS, "hrt.ticks", + PerfData::U_Ticks, psh, CHECK); +} + +/* + * the statSampler_exit() function is called from os_init.cpp on + * exit of the vm. + */ +void statSampler_exit() { + + if (!UsePerfData) return; + + StatSampler::destroy(); +} diff --git a/hotspot/src/share/vm/runtime/statSampler.hpp b/hotspot/src/share/vm/runtime/statSampler.hpp new file mode 100644 index 00000000000..173825cdea6 --- /dev/null +++ b/hotspot/src/share/vm/runtime/statSampler.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class StatSamplerTask; + +/* + * The StatSampler class is responsible for periodically updating + * sampled PerfData instances and writing the sampled values to the + * PerfData memory region. + * + * In addition it is also responsible for providing a home for + * PerfData instances that otherwise have no better home. + */ +class StatSampler : AllStatic { + + friend class StatSamplerTask; + + private: + + static StatSamplerTask* _task; + static PerfDataList* _sampled; + + static void collect_sample(); + static void create_misc_perfdata(); + static void create_sampled_perfdata(); + static void sample_data(PerfDataList* list); + static const char* get_system_property(const char* name, TRAPS); + static void create_system_property_instrumentation(TRAPS); + + public: + // Start/stop the sampler + static void engage(); + static void disengage(); + + static bool is_active() { return _task != NULL; } + + static void initialize(); + static void destroy(); +}; + +void statSampler_exit(); diff --git a/hotspot/src/share/vm/runtime/stubCodeGenerator.cpp b/hotspot/src/share/vm/runtime/stubCodeGenerator.cpp new file mode 100644 index 00000000000..bbdd6898b75 --- /dev/null +++ b/hotspot/src/share/vm/runtime/stubCodeGenerator.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubCodeGenerator.cpp.incl" + + +// Implementation of StubCodeDesc + +StubCodeDesc* StubCodeDesc::_list = NULL; +int StubCodeDesc::_count = 0; + + +StubCodeDesc* StubCodeDesc::desc_for(address pc) { + StubCodeDesc* p = _list; + while (p != NULL && !p->contains(pc)) p = p->_next; + // p == NULL || p->contains(pc) + return p; +} + + +StubCodeDesc* StubCodeDesc::desc_for_index(int index) { + StubCodeDesc* p = _list; + while (p != NULL && p->index() != index) p = p->_next; + return p; +} + + +const char* StubCodeDesc::name_for(address pc) { + StubCodeDesc* p = desc_for(pc); + return p == NULL ? NULL : p->name(); +} + + +void StubCodeDesc::print() { + tty->print(group()); + tty->print("::"); + tty->print(name()); + tty->print(" [" INTPTR_FORMAT ", " INTPTR_FORMAT "[ (%d bytes)", begin(), end(), size_in_bytes()); +} + + + +// Implementation of StubCodeGenerator + +StubCodeGenerator::StubCodeGenerator(CodeBuffer* code) { + _masm = new MacroAssembler(code); + _first_stub = _last_stub = NULL; +} + +#ifndef PRODUCT +extern "C" { + static int compare_cdesc(const void* void_a, const void* void_b) { + int ai = (*((StubCodeDesc**) void_a))->index(); + int bi = (*((StubCodeDesc**) void_b))->index(); + return ai - bi; + } +} +#endif + +StubCodeGenerator::~StubCodeGenerator() { +#ifndef PRODUCT + if (PrintStubCode) { + CodeBuffer* cbuf = _masm->code(); + CodeBlob* blob = CodeCache::find_blob_unsafe(cbuf->insts()->start()); + if (blob != NULL) { + blob->set_comments(cbuf->comments()); + } + bool saw_first = false; + StubCodeDesc* toprint[1000]; + int toprint_len = 0; + for (StubCodeDesc* cdesc = _last_stub; cdesc != NULL; cdesc = cdesc->_next) { + toprint[toprint_len++] = cdesc; + if (cdesc == _first_stub) { saw_first = true; break; } + } + assert(saw_first, "must get both first & last"); + // Print in reverse order: + qsort(toprint, toprint_len, sizeof(toprint[0]), compare_cdesc); + for (int i = 0; i < toprint_len; i++) { + StubCodeDesc* cdesc = toprint[i]; + cdesc->print(); + tty->cr(); + Disassembler::decode(cdesc->begin(), cdesc->end()); + tty->cr(); + } + } +#endif //PRODUCT +} + + +void StubCodeGenerator::stub_prolog(StubCodeDesc* cdesc) { + // default implementation - do nothing +} + + +void StubCodeGenerator::stub_epilog(StubCodeDesc* cdesc) { + // default implementation - record the cdesc + if (_first_stub == NULL) _first_stub = cdesc; + _last_stub = cdesc; +} + + +// Implementation of CodeMark + +StubCodeMark::StubCodeMark(StubCodeGenerator* cgen, const char* group, const char* name) { + _cgen = cgen; + _cdesc = new StubCodeDesc(group, name, _cgen->assembler()->pc()); + _cgen->stub_prolog(_cdesc); + // define the stub's beginning (= entry point) to be after the prolog: + _cdesc->set_begin(_cgen->assembler()->pc()); +} + +StubCodeMark::~StubCodeMark() { + _cgen->assembler()->flush(); + _cdesc->set_end(_cgen->assembler()->pc()); + assert(StubCodeDesc::_list == _cdesc, "expected order on list"); + _cgen->stub_epilog(_cdesc); + VTune::register_stub(_cdesc->name(), _cdesc->begin(), _cdesc->end()); + Forte::register_stub(_cdesc->name(), _cdesc->begin(), _cdesc->end()); + + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated(_cdesc->name(), _cdesc->begin(), _cdesc->end()); + } +} diff --git a/hotspot/src/share/vm/runtime/stubCodeGenerator.hpp b/hotspot/src/share/vm/runtime/stubCodeGenerator.hpp new file mode 100644 index 00000000000..530d954cd20 --- /dev/null +++ b/hotspot/src/share/vm/runtime/stubCodeGenerator.hpp @@ -0,0 +1,120 @@ +/* + * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// All the basic framework for stubcode generation/debugging/printing. + + +// A StubCodeDesc describes a piece of generated code (usually stubs). +// This information is mainly useful for debugging and printing. +// Currently, code descriptors are simply chained in a linked list, +// this may have to change if searching becomes too slow. + +class StubCodeDesc: public CHeapObj { + protected: + static StubCodeDesc* _list; // the list of all descriptors + static int _count; // length of list + + StubCodeDesc* _next; // the next element in the linked list + const char* _group; // the group to which the stub code belongs + const char* _name; // the name assigned to the stub code + int _index; // serial number assigned to the stub + address _begin; // points to the first byte of the stub code (included) + address _end; // points to the first byte after the stub code (excluded) + + void set_end(address end) { + assert(_begin <= end, "begin & end not properly ordered"); + _end = end; + } + + void set_begin(address begin) { + assert(begin >= _begin, "begin may not decrease"); + assert(_end == NULL || begin <= _end, "begin & end not properly ordered"); + _begin = begin; + } + + friend class StubCodeMark; + friend class StubCodeGenerator; + + public: + static StubCodeDesc* desc_for(address pc); // returns the code descriptor for the code containing pc or NULL + static StubCodeDesc* desc_for_index(int); // returns the code descriptor for the index or NULL + static const char* name_for(address pc); // returns the name of the code containing pc or NULL + + StubCodeDesc(const char* group, const char* name, address begin) { + assert(name != NULL, "no name specified"); + _next = _list; + _group = group; + _name = name; + _index = ++_count; // (never zero) + _begin = begin; + _end = NULL; + _list = this; + }; + + const char* group() const { return _group; } + const char* name() const { return _name; } + int index() const { return _index; } + address begin() const { return _begin; } + address end() const { return _end; } + int size_in_bytes() const { return _end - _begin; } + bool contains(address pc) const { return _begin <= pc && pc < _end; } + void print(); +}; + +// The base class for all stub-generating code generators. +// Provides utility functions. + +class StubCodeGenerator: public StackObj { + protected: + MacroAssembler* _masm; + + StubCodeDesc* _first_stub; + StubCodeDesc* _last_stub; + + public: + StubCodeGenerator(CodeBuffer* code); + ~StubCodeGenerator(); + + MacroAssembler* assembler() const { return _masm; } + + virtual void stub_prolog(StubCodeDesc* cdesc); // called by StubCodeMark constructor + virtual void stub_epilog(StubCodeDesc* cdesc); // called by StubCodeMark destructor +}; + + +// Stack-allocated helper class used to assciate a stub code with a name. +// All stub code generating functions that use a StubCodeMark will be registered +// in the global StubCodeDesc list and the generated stub code can be identified +// later via an address pointing into it. + +class StubCodeMark: public StackObj { + protected: + StubCodeGenerator* _cgen; + StubCodeDesc* _cdesc; + + public: + StubCodeMark(StubCodeGenerator* cgen, const char* group, const char* name); + ~StubCodeMark(); + +}; diff --git a/hotspot/src/share/vm/runtime/stubRoutines.cpp b/hotspot/src/share/vm/runtime/stubRoutines.cpp new file mode 100644 index 00000000000..63b30d86d7d --- /dev/null +++ b/hotspot/src/share/vm/runtime/stubRoutines.cpp @@ -0,0 +1,277 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubRoutines.cpp.incl" + + +// Implementation of StubRoutines - for a description +// of how to extend it, see the header file. + +// Class Variables + +BufferBlob* StubRoutines::_code1 = NULL; +BufferBlob* StubRoutines::_code2 = NULL; + +address StubRoutines::_call_stub_return_address = NULL; +address StubRoutines::_call_stub_entry = NULL; + +address StubRoutines::_catch_exception_entry = NULL; +address StubRoutines::_forward_exception_entry = NULL; +address StubRoutines::_throw_AbstractMethodError_entry = NULL; +address StubRoutines::_throw_ArithmeticException_entry = NULL; +address StubRoutines::_throw_NullPointerException_entry = NULL; +address StubRoutines::_throw_NullPointerException_at_call_entry = NULL; +address StubRoutines::_throw_StackOverflowError_entry = NULL; +address StubRoutines::_handler_for_unsafe_access_entry = NULL; +jint StubRoutines::_verify_oop_count = 0; +address StubRoutines::_verify_oop_subroutine_entry = NULL; +address StubRoutines::_atomic_xchg_entry = NULL; +address StubRoutines::_atomic_xchg_ptr_entry = NULL; +address StubRoutines::_atomic_store_entry = NULL; +address StubRoutines::_atomic_store_ptr_entry = NULL; +address StubRoutines::_atomic_cmpxchg_entry = NULL; +address StubRoutines::_atomic_cmpxchg_ptr_entry = NULL; +address StubRoutines::_atomic_cmpxchg_long_entry = NULL; +address StubRoutines::_atomic_add_entry = NULL; +address StubRoutines::_atomic_add_ptr_entry = NULL; +address StubRoutines::_fence_entry = NULL; +address StubRoutines::_d2i_wrapper = NULL; +address StubRoutines::_d2l_wrapper = NULL; + +jint StubRoutines::_fpu_cntrl_wrd_std = 0; +jint StubRoutines::_fpu_cntrl_wrd_24 = 0; +jint StubRoutines::_fpu_cntrl_wrd_64 = 0; +jint StubRoutines::_fpu_cntrl_wrd_trunc = 0; +jint StubRoutines::_mxcsr_std = 0; +jint StubRoutines::_fpu_subnormal_bias1[3] = { 0, 0, 0 }; +jint StubRoutines::_fpu_subnormal_bias2[3] = { 0, 0, 0 }; + +// Compiled code entry points default values +// The dafault functions don't have separate disjoint versions. +address StubRoutines::_jbyte_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::jbyte_copy); +address StubRoutines::_jshort_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::jshort_copy); +address StubRoutines::_jint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::jint_copy); +address StubRoutines::_jlong_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::jlong_copy); +address StubRoutines::_oop_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::oop_copy); +address StubRoutines::_jbyte_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::jbyte_copy); +address StubRoutines::_jshort_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::jshort_copy); +address StubRoutines::_jint_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::jint_copy); +address StubRoutines::_jlong_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::jlong_copy); +address StubRoutines::_oop_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::oop_copy); + +address StubRoutines::_arrayof_jbyte_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_jbyte_copy); +address StubRoutines::_arrayof_jshort_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_jshort_copy); +address StubRoutines::_arrayof_jint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_jint_copy); +address StubRoutines::_arrayof_jlong_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_jlong_copy); +address StubRoutines::_arrayof_oop_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_oop_copy); +address StubRoutines::_arrayof_jbyte_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_jbyte_copy); +address StubRoutines::_arrayof_jshort_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_jshort_copy); +address StubRoutines::_arrayof_jint_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_jint_copy); +address StubRoutines::_arrayof_jlong_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_jlong_copy); +address StubRoutines::_arrayof_oop_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_oop_copy); + +address StubRoutines::_checkcast_arraycopy = NULL; +address StubRoutines::_unsafe_arraycopy = NULL; +address StubRoutines::_generic_arraycopy = NULL; + +// Initialization +// +// Note: to break cycle with universe initialization, stubs are generated in two phases. +// The first one generates stubs needed during universe init (e.g., _handle_must_compile_first_entry). +// The second phase includes all other stubs (which may depend on universe being initialized.) + +extern void StubGenerator_generate(CodeBuffer* code, bool all); // only interface to generators + +void StubRoutines::initialize1() { + if (_code1 == NULL) { + ResourceMark rm; + TraceTime timer("StubRoutines generation 1", TraceStartupTime); + _code1 = BufferBlob::create("StubRoutines (1)", code_size1); + if( _code1 == NULL) vm_exit_out_of_memory1(code_size1, "CodeCache: no room for %s", "StubRoutines (1)"); + CodeBuffer buffer(_code1->instructions_begin(), _code1->instructions_size()); + StubGenerator_generate(&buffer, false); + } +} + + +#ifdef ASSERT +typedef void (*arraycopy_fn)(address src, address dst, int count); + +// simple tests of generated arraycopy functions +static void test_arraycopy_func(address func, int alignment) { + int v = 0xcc; + int v2 = 0x11; + jlong lbuffer[2]; + jlong lbuffer2[2]; + address buffer = (address) lbuffer; + address buffer2 = (address) lbuffer2; + unsigned int i; + for (i = 0; i < sizeof(lbuffer); i++) { + buffer[i] = v; buffer2[i] = v2; + } + // do an aligned copy + ((arraycopy_fn)func)(buffer, buffer2, 0); + for (i = 0; i < sizeof(lbuffer); i++) { + assert(buffer[i] == v && buffer2[i] == v2, "shouldn't have copied anything"); + } + // adjust destination alignment + ((arraycopy_fn)func)(buffer, buffer2 + alignment, 0); + for (i = 0; i < sizeof(lbuffer); i++) { + assert(buffer[i] == v && buffer2[i] == v2, "shouldn't have copied anything"); + } + // adjust source alignment + ((arraycopy_fn)func)(buffer + alignment, buffer2, 0); + for (i = 0; i < sizeof(lbuffer); i++) { + assert(buffer[i] == v && buffer2[i] == v2, "shouldn't have copied anything"); + } +} +#endif + + +void StubRoutines::initialize2() { + if (_code2 == NULL) { + ResourceMark rm; + TraceTime timer("StubRoutines generation 2", TraceStartupTime); + _code2 = BufferBlob::create("StubRoutines (2)", code_size2); + if( _code2 == NULL) vm_exit_out_of_memory1(code_size2, "CodeCache: no room for %s", "StubRoutines (2)"); + CodeBuffer buffer(_code2->instructions_begin(), _code2->instructions_size()); + StubGenerator_generate(&buffer, true); + } + +#ifdef ASSERT + +#define TEST_ARRAYCOPY(type) \ + test_arraycopy_func( type##_arraycopy(), sizeof(type)); \ + test_arraycopy_func( type##_disjoint_arraycopy(), sizeof(type)); \ + test_arraycopy_func(arrayof_##type##_arraycopy(), sizeof(HeapWord)); \ + test_arraycopy_func(arrayof_##type##_disjoint_arraycopy(), sizeof(HeapWord)) + + // Make sure all the arraycopy stubs properly handle zeros + TEST_ARRAYCOPY(jbyte); + TEST_ARRAYCOPY(jshort); + TEST_ARRAYCOPY(jint); + TEST_ARRAYCOPY(jlong); + +#undef TEST_ARRAYCOPY + +#endif +} + + +void stubRoutines_init1() { StubRoutines::initialize1(); } +void stubRoutines_init2() { StubRoutines::initialize2(); } + +// +// Default versions of arraycopy functions +// + +static void gen_arraycopy_barrier(oop* dest, size_t count) { + assert(count != 0, "count should be non-zero"); + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt"); + bs->write_ref_array(MemRegion((HeapWord*)dest, (HeapWord*)(dest + count))); +} + +JRT_LEAF(void, StubRoutines::jbyte_copy(jbyte* src, jbyte* dest, size_t count)) +#ifndef PRODUCT + SharedRuntime::_jbyte_array_copy_ctr++; // Slow-path byte array copy +#endif // !PRODUCT + assert(count != 0, "count should be non-zero"); + Copy::conjoint_bytes_atomic(src, dest, count); +JRT_END + +JRT_LEAF(void, StubRoutines::jshort_copy(jshort* src, jshort* dest, size_t count)) +#ifndef PRODUCT + SharedRuntime::_jshort_array_copy_ctr++; // Slow-path short/char array copy +#endif // !PRODUCT + assert(count != 0, "count should be non-zero"); + Copy::conjoint_jshorts_atomic(src, dest, count); +JRT_END + +JRT_LEAF(void, StubRoutines::jint_copy(jint* src, jint* dest, size_t count)) +#ifndef PRODUCT + SharedRuntime::_jint_array_copy_ctr++; // Slow-path int/float array copy +#endif // !PRODUCT + assert(count != 0, "count should be non-zero"); + Copy::conjoint_jints_atomic(src, dest, count); +JRT_END + +JRT_LEAF(void, StubRoutines::jlong_copy(jlong* src, jlong* dest, size_t count)) +#ifndef PRODUCT + SharedRuntime::_jlong_array_copy_ctr++; // Slow-path long/double array copy +#endif // !PRODUCT + assert(count != 0, "count should be non-zero"); + Copy::conjoint_jlongs_atomic(src, dest, count); +JRT_END + +JRT_LEAF(void, StubRoutines::oop_copy(oop* src, oop* dest, size_t count)) +#ifndef PRODUCT + SharedRuntime::_oop_array_copy_ctr++; // Slow-path oop array copy +#endif // !PRODUCT + assert(count != 0, "count should be non-zero"); + Copy::conjoint_oops_atomic(src, dest, count); + gen_arraycopy_barrier(dest, count); +JRT_END + +JRT_LEAF(void, StubRoutines::arrayof_jbyte_copy(HeapWord* src, HeapWord* dest, size_t count)) +#ifndef PRODUCT + SharedRuntime::_jbyte_array_copy_ctr++; // Slow-path byte array copy +#endif // !PRODUCT + assert(count != 0, "count should be non-zero"); + Copy::arrayof_conjoint_bytes(src, dest, count); +JRT_END + +JRT_LEAF(void, StubRoutines::arrayof_jshort_copy(HeapWord* src, HeapWord* dest, size_t count)) +#ifndef PRODUCT + SharedRuntime::_jshort_array_copy_ctr++; // Slow-path short/char array copy +#endif // !PRODUCT + assert(count != 0, "count should be non-zero"); + Copy::arrayof_conjoint_jshorts(src, dest, count); +JRT_END + +JRT_LEAF(void, StubRoutines::arrayof_jint_copy(HeapWord* src, HeapWord* dest, size_t count)) +#ifndef PRODUCT + SharedRuntime::_jint_array_copy_ctr++; // Slow-path int/float array copy +#endif // !PRODUCT + assert(count != 0, "count should be non-zero"); + Copy::arrayof_conjoint_jints(src, dest, count); +JRT_END + +JRT_LEAF(void, StubRoutines::arrayof_jlong_copy(HeapWord* src, HeapWord* dest, size_t count)) +#ifndef PRODUCT + SharedRuntime::_jlong_array_copy_ctr++; // Slow-path int/float array copy +#endif // !PRODUCT + assert(count != 0, "count should be non-zero"); + Copy::arrayof_conjoint_jlongs(src, dest, count); +JRT_END + +JRT_LEAF(void, StubRoutines::arrayof_oop_copy(HeapWord* src, HeapWord* dest, size_t count)) +#ifndef PRODUCT + SharedRuntime::_oop_array_copy_ctr++; // Slow-path oop array copy +#endif // !PRODUCT + assert(count != 0, "count should be non-zero"); + Copy::arrayof_conjoint_oops(src, dest, count); + gen_arraycopy_barrier((oop *) dest, count); +JRT_END diff --git a/hotspot/src/share/vm/runtime/stubRoutines.hpp b/hotspot/src/share/vm/runtime/stubRoutines.hpp new file mode 100644 index 00000000000..17246a8bcaa --- /dev/null +++ b/hotspot/src/share/vm/runtime/stubRoutines.hpp @@ -0,0 +1,261 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// StubRoutines provides entry points to assembly routines used by +// compiled code and the run-time system. Platform-specific entry +// points are defined in the platform-specific inner class. +// +// Class scheme: +// +// platform-independent platform-dependent +// +// stubRoutines.hpp <-- included -- stubRoutines_.hpp +// ^ ^ +// | | +// implements implements +// | | +// | | +// stubRoutines.cpp stubRoutines_.cpp +// stubRoutines_.cpp stubGenerator_.cpp +// stubRoutines_.cpp +// +// Note 1: The important thing is a clean decoupling between stub +// entry points (interfacing to the whole vm; i.e., 1-to-n +// relationship) and stub generators (interfacing only to +// the entry points implementation; i.e., 1-to-1 relationship). +// This significantly simplifies changes in the generator +// structure since the rest of the vm is not affected. +// +// Note 2: stubGenerator_.cpp contains a minimal portion of +// machine-independent code; namely the generator calls of +// the generator functions that are used platform-independently. +// However, it comes with the advantage of having a 1-file +// implementation of the generator. It should be fairly easy +// to change, should it become a problem later. +// +// Scheme for adding a new entry point: +// +// 1. determine if it's a platform-dependent or independent entry point +// a) if platform independent: make subsequent changes in the independent files +// b) if platform dependent: make subsequent changes in the dependent files +// 2. add a private instance variable holding the entry point address +// 3. add a public accessor function to the instance variable +// 4. implement the corresponding generator function in the platform-dependent +// stubGenerator_.cpp file and call the function in generate_all() of that file + + +class StubRoutines: AllStatic { + + public: + enum platform_independent_constants { + max_size_of_parameters = 256 // max. parameter size supported by megamorphic lookups + }; + + // Dependencies + friend class StubGenerator; + #include "incls/_stubRoutines_pd.hpp.incl" // machine-specific parts + + static jint _verify_oop_count; + static address _verify_oop_subroutine_entry; + + static address _call_stub_return_address; // the return PC, when returning to a call stub + static address _call_stub_entry; + static address _forward_exception_entry; + static address _catch_exception_entry; + static address _throw_AbstractMethodError_entry; + static address _throw_ArithmeticException_entry; + static address _throw_NullPointerException_entry; + static address _throw_NullPointerException_at_call_entry; + static address _throw_StackOverflowError_entry; + static address _handler_for_unsafe_access_entry; + + static address _atomic_xchg_entry; + static address _atomic_xchg_ptr_entry; + static address _atomic_store_entry; + static address _atomic_store_ptr_entry; + static address _atomic_cmpxchg_entry; + static address _atomic_cmpxchg_ptr_entry; + static address _atomic_cmpxchg_long_entry; + static address _atomic_add_entry; + static address _atomic_add_ptr_entry; + static address _fence_entry; + static address _d2i_wrapper; + static address _d2l_wrapper; + + static jint _fpu_cntrl_wrd_std; + static jint _fpu_cntrl_wrd_24; + static jint _fpu_cntrl_wrd_64; + static jint _fpu_cntrl_wrd_trunc; + static jint _mxcsr_std; + static jint _fpu_subnormal_bias1[3]; + static jint _fpu_subnormal_bias2[3]; + + static BufferBlob* _code1; // code buffer for initial routines + static BufferBlob* _code2; // code buffer for all other routines + + // Leaf routines which implement arraycopy and their addresses + // arraycopy operands aligned on element type boundary + static address _jbyte_arraycopy; + static address _jshort_arraycopy; + static address _jint_arraycopy; + static address _jlong_arraycopy; + static address _oop_arraycopy; + static address _jbyte_disjoint_arraycopy; + static address _jshort_disjoint_arraycopy; + static address _jint_disjoint_arraycopy; + static address _jlong_disjoint_arraycopy; + static address _oop_disjoint_arraycopy; + + // arraycopy operands aligned on zero'th element boundary + // These are identical to the ones aligned aligned on an + // element type boundary, except that they assume that both + // source and destination are HeapWord aligned. + static address _arrayof_jbyte_arraycopy; + static address _arrayof_jshort_arraycopy; + static address _arrayof_jint_arraycopy; + static address _arrayof_jlong_arraycopy; + static address _arrayof_oop_arraycopy; + static address _arrayof_jbyte_disjoint_arraycopy; + static address _arrayof_jshort_disjoint_arraycopy; + static address _arrayof_jint_disjoint_arraycopy; + static address _arrayof_jlong_disjoint_arraycopy; + static address _arrayof_oop_disjoint_arraycopy; + + // these are recommended but optional: + static address _checkcast_arraycopy; + static address _unsafe_arraycopy; + static address _generic_arraycopy; + + public: + // Initialization/Testing + static void initialize1(); // must happen before universe::genesis + static void initialize2(); // must happen after universe::genesis + + static bool contains(address addr) { + return + (_code1 != NULL && _code1->blob_contains(addr)) || + (_code2 != NULL && _code2->blob_contains(addr)) ; + } + + // Debugging + static jint verify_oop_count() { return _verify_oop_count; } + static jint* verify_oop_count_addr() { return &_verify_oop_count; } + // a subroutine for debugging the GC + static address verify_oop_subroutine_entry_address() { return (address)&_verify_oop_subroutine_entry; } + + static address catch_exception_entry() { return _catch_exception_entry; } + + // Calls to Java + typedef void (*CallStub)( + address link, + intptr_t* result, + BasicType result_type, + methodOopDesc* method, + address entry_point, + intptr_t* parameters, + int size_of_parameters, + TRAPS + ); + + static CallStub call_stub() { return CAST_TO_FN_PTR(CallStub, _call_stub_entry); } + + // Exceptions + static address forward_exception_entry() { return _forward_exception_entry; } + // Implicit exceptions + static address throw_AbstractMethodError_entry() { return _throw_AbstractMethodError_entry; } + static address throw_ArithmeticException_entry() { return _throw_ArithmeticException_entry; } + static address throw_NullPointerException_entry() { return _throw_NullPointerException_entry; } + static address throw_NullPointerException_at_call_entry(){ return _throw_NullPointerException_at_call_entry; } + static address throw_StackOverflowError_entry() { return _throw_StackOverflowError_entry; } + + // Exceptions during unsafe access - should throw Java exception rather + // than crash. + static address handler_for_unsafe_access() { return _handler_for_unsafe_access_entry; } + + static address atomic_xchg_entry() { return _atomic_xchg_entry; } + static address atomic_xchg_ptr_entry() { return _atomic_xchg_ptr_entry; } + static address atomic_store_entry() { return _atomic_store_entry; } + static address atomic_store_ptr_entry() { return _atomic_store_ptr_entry; } + static address atomic_cmpxchg_entry() { return _atomic_cmpxchg_entry; } + static address atomic_cmpxchg_ptr_entry() { return _atomic_cmpxchg_ptr_entry; } + static address atomic_cmpxchg_long_entry() { return _atomic_cmpxchg_long_entry; } + static address atomic_add_entry() { return _atomic_add_entry; } + static address atomic_add_ptr_entry() { return _atomic_add_ptr_entry; } + static address fence_entry() { return _fence_entry; } + + static address d2i_wrapper() { return _d2i_wrapper; } + static address d2l_wrapper() { return _d2l_wrapper; } + static jint fpu_cntrl_wrd_std() { return _fpu_cntrl_wrd_std; } + static address addr_fpu_cntrl_wrd_std() { return (address)&_fpu_cntrl_wrd_std; } + static address addr_fpu_cntrl_wrd_24() { return (address)&_fpu_cntrl_wrd_24; } + static address addr_fpu_cntrl_wrd_64() { return (address)&_fpu_cntrl_wrd_64; } + static address addr_fpu_cntrl_wrd_trunc() { return (address)&_fpu_cntrl_wrd_trunc; } + static address addr_mxcsr_std() { return (address)&_mxcsr_std; } + static address addr_fpu_subnormal_bias1() { return (address)&_fpu_subnormal_bias1; } + static address addr_fpu_subnormal_bias2() { return (address)&_fpu_subnormal_bias2; } + + + static address jbyte_arraycopy() { return _jbyte_arraycopy; } + static address jshort_arraycopy() { return _jshort_arraycopy; } + static address jint_arraycopy() { return _jint_arraycopy; } + static address jlong_arraycopy() { return _jlong_arraycopy; } + static address oop_arraycopy() { return _oop_arraycopy; } + static address jbyte_disjoint_arraycopy() { return _jbyte_disjoint_arraycopy; } + static address jshort_disjoint_arraycopy() { return _jshort_disjoint_arraycopy; } + static address jint_disjoint_arraycopy() { return _jint_disjoint_arraycopy; } + static address jlong_disjoint_arraycopy() { return _jlong_disjoint_arraycopy; } + static address oop_disjoint_arraycopy() { return _oop_disjoint_arraycopy; } + + static address arrayof_jbyte_arraycopy() { return _arrayof_jbyte_arraycopy; } + static address arrayof_jshort_arraycopy() { return _arrayof_jshort_arraycopy; } + static address arrayof_jint_arraycopy() { return _arrayof_jint_arraycopy; } + static address arrayof_jlong_arraycopy() { return _arrayof_jlong_arraycopy; } + static address arrayof_oop_arraycopy() { return _arrayof_oop_arraycopy; } + + static address arrayof_jbyte_disjoint_arraycopy() { return _arrayof_jbyte_disjoint_arraycopy; } + static address arrayof_jshort_disjoint_arraycopy() { return _arrayof_jshort_disjoint_arraycopy; } + static address arrayof_jint_disjoint_arraycopy() { return _arrayof_jint_disjoint_arraycopy; } + static address arrayof_jlong_disjoint_arraycopy() { return _arrayof_jlong_disjoint_arraycopy; } + static address arrayof_oop_disjoint_arraycopy() { return _arrayof_oop_disjoint_arraycopy; } + + static address checkcast_arraycopy() { return _checkcast_arraycopy; } + static address unsafe_arraycopy() { return _unsafe_arraycopy; } + static address generic_arraycopy() { return _generic_arraycopy; } + + // + // Default versions of the above arraycopy functions for platforms which do + // not have specialized versions + // + static void jbyte_copy (jbyte* src, jbyte* dest, size_t count); + static void jshort_copy(jshort* src, jshort* dest, size_t count); + static void jint_copy (jint* src, jint* dest, size_t count); + static void jlong_copy (jlong* src, jlong* dest, size_t count); + static void oop_copy (oop* src, oop* dest, size_t count); + + static void arrayof_jbyte_copy (HeapWord* src, HeapWord* dest, size_t count); + static void arrayof_jshort_copy(HeapWord* src, HeapWord* dest, size_t count); + static void arrayof_jint_copy (HeapWord* src, HeapWord* dest, size_t count); + static void arrayof_jlong_copy (HeapWord* src, HeapWord* dest, size_t count); + static void arrayof_oop_copy (HeapWord* src, HeapWord* dest, size_t count); +}; diff --git a/hotspot/src/share/vm/runtime/sweeper.cpp b/hotspot/src/share/vm/runtime/sweeper.cpp new file mode 100644 index 00000000000..bfa4761d4c2 --- /dev/null +++ b/hotspot/src/share/vm/runtime/sweeper.cpp @@ -0,0 +1,161 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_sweeper.cpp.incl" + +long NMethodSweeper::_traversals = 0; // No. of stack traversals performed +CodeBlob* NMethodSweeper::_current = NULL; // Current nmethod +int NMethodSweeper::_seen = 0 ; // No. of blobs we have currently processed in current pass of CodeCache +int NMethodSweeper::_invocations = 0; // No. of invocations left until we are completed with this pass + +jint NMethodSweeper::_locked_seen = 0; +jint NMethodSweeper::_not_entrant_seen_on_stack = 0; +bool NMethodSweeper::_rescan = false; + +void NMethodSweeper::sweep() { + assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint"); + if (!MethodFlushing) return; + + // No need to synchronize access, since this is always executed at a + // safepoint. If we aren't in the middle of scan and a rescan + // hasn't been requested then just return. + if (_current == NULL && !_rescan) return; + + // Make sure CompiledIC_lock in unlocked, since we might update some + // inline caches. If it is, we just bail-out and try later. + if (CompiledIC_lock->is_locked() || Patching_lock->is_locked()) return; + + // Check for restart + assert(CodeCache::find_blob_unsafe(_current) == _current, "Sweeper nmethod cached state invalid"); + if (_current == NULL) { + _seen = 0; + _invocations = NmethodSweepFraction; + _current = CodeCache::first(); + _traversals += 1; + if (PrintMethodFlushing) { + tty->print_cr("### Sweep: stack traversal %d", _traversals); + } + Threads::nmethods_do(); + + // reset the flags since we started a scan from the beginning. + _rescan = false; + _locked_seen = 0; + _not_entrant_seen_on_stack = 0; + } + + if (PrintMethodFlushing && Verbose) { + tty->print_cr("### Sweep at %d out of %d. Invocations left: %d", _seen, CodeCache::nof_blobs(), _invocations); + } + + // We want to visit all nmethods after NmethodSweepFraction invocations. + // If invocation is 1 we do the rest + int todo = CodeCache::nof_blobs(); + if (_invocations != 1) { + todo = (CodeCache::nof_blobs() - _seen) / _invocations; + _invocations--; + } + + for(int i = 0; i < todo && _current != NULL; i++) { + CodeBlob* next = CodeCache::next(_current); // Read next before we potentially delete current + if (_current->is_nmethod()) { + process_nmethod((nmethod *)_current); + } + _seen++; + _current = next; + } + // Because we could stop on a codeBlob other than an nmethod we skip forward + // to the next nmethod (if any). codeBlobs other than nmethods can be freed + // async to us and make _current invalid while we sleep. + while (_current != NULL && !_current->is_nmethod()) { + _current = CodeCache::next(_current); + } + + if (_current == NULL && !_rescan && (_locked_seen || _not_entrant_seen_on_stack)) { + // we've completed a scan without making progress but there were + // nmethods we were unable to process either because they were + // locked or were still on stack. We don't have to aggresively + // clean them up so just stop scanning. We could scan once more + // but that complicates the control logic and it's unlikely to + // matter much. + if (PrintMethodFlushing) { + tty->print_cr("### Couldn't make progress on some nmethods so stopping sweep"); + } + } +} + + +void NMethodSweeper::process_nmethod(nmethod *nm) { + // Skip methods that are currently referenced by the VM + if (nm->is_locked_by_vm()) { + // But still remember to clean-up inline caches for alive nmethods + if (nm->is_alive()) { + // Clean-up all inline caches that points to zombie/non-reentrant methods + nm->cleanup_inline_caches(); + } else { + _locked_seen++; + } + return; + } + + if (nm->is_zombie()) { + // If it is first time, we see nmethod then we mark it. Otherwise, + // we reclame it. When we have seen a zombie method twice, we know that + // there are no inline caches that referes to it. + if (nm->is_marked_for_reclamation()) { + assert(!nm->is_locked_by_vm(), "must not flush locked nmethods"); + nm->flush(); + } else { + nm->mark_for_reclamation(); + _rescan = true; + } + } else if (nm->is_not_entrant()) { + // If there is no current activations of this method on the + // stack we can safely convert it to a zombie method + if (nm->can_not_entrant_be_converted()) { + nm->make_zombie(); + _rescan = true; + } else { + // Still alive, clean up its inline caches + nm->cleanup_inline_caches(); + // we coudn't transition this nmethod so don't immediately + // request a rescan. If this method stays on the stack for a + // long time we don't want to keep rescanning at every safepoint. + _not_entrant_seen_on_stack++; + } + } else if (nm->is_unloaded()) { + // Unloaded code, just make it a zombie + if (nm->is_osr_only_method()) { + // No inline caches will ever point to osr methods, so we can just remove it + nm->flush(); + } else { + nm->make_zombie(); + _rescan = true; + } + } else { + assert(nm->is_alive(), "should be alive"); + // Clean-up all inline caches that points to zombie/non-reentrant methods + nm->cleanup_inline_caches(); + } +} diff --git a/hotspot/src/share/vm/runtime/sweeper.hpp b/hotspot/src/share/vm/runtime/sweeper.hpp new file mode 100644 index 00000000000..1f7260cac00 --- /dev/null +++ b/hotspot/src/share/vm/runtime/sweeper.hpp @@ -0,0 +1,54 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// An NmethodSweeper is an incremental cleaner for: +// - cleanup inline caches +// - reclamation of unreferences zombie nmethods +// + +class NMethodSweeper : public AllStatic { + static long _traversals; // Stack traversal count + static CodeBlob* _current; // Current nmethod + static int _seen; // Nof. nmethod we have currently processed in current pass of CodeCache + static int _invocations; // No. of invocations left until we are completed with this pass + + static bool _rescan; // Indicates that we should do a full rescan of the + // of the code cache looking for work to do. + static int _locked_seen; // Number of locked nmethods encountered during the scan + static int _not_entrant_seen_on_stack; // Number of not entrant nmethod were are still on stack + + + static void process_nmethod(nmethod *nm); + public: + static long traversal_count() { return _traversals; } + + static void sweep(); // Invoked at the end of each safepoint + + static void notify(nmethod* nm) { + // Perform a full scan of the code cache from the beginning. No + // need to synchronize the setting of this flag since it only + // changes to false at safepoint so we can never overwrite it with false. + _rescan = true; + } +}; diff --git a/hotspot/src/share/vm/runtime/synchronizer.cpp b/hotspot/src/share/vm/runtime/synchronizer.cpp new file mode 100644 index 00000000000..eb4e2cac0c4 --- /dev/null +++ b/hotspot/src/share/vm/runtime/synchronizer.cpp @@ -0,0 +1,4716 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_synchronizer.cpp.incl" + +#if defined(__GNUC__) && !defined(IA64) + // Need to inhibit inlining for older versions of GCC to avoid build-time failures + #define ATTR __attribute__((noinline)) +#else + #define ATTR +#endif + +// Native markword accessors for synchronization and hashCode(). +// +// The "core" versions of monitor enter and exit reside in this file. +// The interpreter and compilers contain specialized transliterated +// variants of the enter-exit fast-path operations. See i486.ad fast_lock(), +// for instance. If you make changes here, make sure to modify the +// interpreter, and both C1 and C2 fast-path inline locking code emission. +// +// TODO: merge the objectMonitor and synchronizer classes. +// +// ----------------------------------------------------------------------------- + +#ifdef DTRACE_ENABLED + +// Only bother with this argument setup if dtrace is available +// TODO-FIXME: probes should not fire when caller is _blocked. assert() accordingly. + +HS_DTRACE_PROBE_DECL5(hotspot, monitor__wait, + jlong, uintptr_t, char*, int, long); +HS_DTRACE_PROBE_DECL4(hotspot, monitor__waited, + jlong, uintptr_t, char*, int); +HS_DTRACE_PROBE_DECL4(hotspot, monitor__notify, + jlong, uintptr_t, char*, int); +HS_DTRACE_PROBE_DECL4(hotspot, monitor__notifyAll, + jlong, uintptr_t, char*, int); +HS_DTRACE_PROBE_DECL4(hotspot, monitor__contended__enter, + jlong, uintptr_t, char*, int); +HS_DTRACE_PROBE_DECL4(hotspot, monitor__contended__entered, + jlong, uintptr_t, char*, int); +HS_DTRACE_PROBE_DECL4(hotspot, monitor__contended__exit, + jlong, uintptr_t, char*, int); + +#define DTRACE_MONITOR_PROBE_COMMON(klassOop, thread) \ + char* bytes = NULL; \ + int len = 0; \ + jlong jtid = SharedRuntime::get_java_tid(thread); \ + symbolOop klassname = ((oop)(klassOop))->klass()->klass_part()->name(); \ + if (klassname != NULL) { \ + bytes = (char*)klassname->bytes(); \ + len = klassname->utf8_length(); \ + } + +#define DTRACE_MONITOR_WAIT_PROBE(monitor, klassOop, thread, millis) \ + { \ + if (DTraceMonitorProbes) { \ + DTRACE_MONITOR_PROBE_COMMON(klassOop, thread); \ + HS_DTRACE_PROBE5(hotspot, monitor__wait, jtid, \ + (monitor), bytes, len, (millis)); \ + } \ + } + +#define DTRACE_MONITOR_PROBE(probe, monitor, klassOop, thread) \ + { \ + if (DTraceMonitorProbes) { \ + DTRACE_MONITOR_PROBE_COMMON(klassOop, thread); \ + HS_DTRACE_PROBE4(hotspot, monitor__##probe, jtid, \ + (uintptr_t)(monitor), bytes, len); \ + } \ + } + +#else // ndef DTRACE_ENABLED + +#define DTRACE_MONITOR_WAIT_PROBE(klassOop, thread, millis, mon) {;} +#define DTRACE_MONITOR_PROBE(probe, klassOop, thread, mon) {;} + +#endif // ndef DTRACE_ENABLED + +// ObjectWaiter serves as a "proxy" or surrogate thread. +// TODO-FIXME: Eliminate ObjectWaiter and use the thread-specific +// ParkEvent instead. Beware, however, that the JVMTI code +// knows about ObjectWaiters, so we'll have to reconcile that code. +// See next_waiter(), first_waiter(), etc. + +class ObjectWaiter : public StackObj { + public: + enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ } ; + enum Sorted { PREPEND, APPEND, SORTED } ; + ObjectWaiter * volatile _next; + ObjectWaiter * volatile _prev; + Thread* _thread; + ParkEvent * _event; + volatile int _notified ; + volatile TStates TState ; + Sorted _Sorted ; // List placement disposition + bool _active ; // Contention monitoring is enabled + public: + ObjectWaiter(Thread* thread) { + _next = NULL; + _prev = NULL; + _notified = 0; + TState = TS_RUN ; + _thread = thread; + _event = thread->_ParkEvent ; + _active = false; + assert (_event != NULL, "invariant") ; + } + + void wait_reenter_begin(ObjectMonitor *mon) { + JavaThread *jt = (JavaThread *)this->_thread; + _active = JavaThreadBlockedOnMonitorEnterState::wait_reenter_begin(jt, mon); + } + + void wait_reenter_end(ObjectMonitor *mon) { + JavaThread *jt = (JavaThread *)this->_thread; + JavaThreadBlockedOnMonitorEnterState::wait_reenter_end(jt, _active); + } +}; + +enum ManifestConstants { + ClearResponsibleAtSTW = 0, + MaximumRecheckInterval = 1000 +} ; + + +#undef TEVENT +#define TEVENT(nom) {if (SyncVerbose) FEVENT(nom); } + +#define FEVENT(nom) { static volatile int ctr = 0 ; int v = ++ctr ; if ((v & (v-1)) == 0) { ::printf (#nom " : %d \n", v); ::fflush(stdout); }} + +#undef TEVENT +#define TEVENT(nom) {;} + +// Performance concern: +// OrderAccess::storestore() calls release() which STs 0 into the global volatile +// OrderAccess::Dummy variable. This store is unnecessary for correctness. +// Many threads STing into a common location causes considerable cache migration +// or "sloshing" on large SMP system. As such, I avoid using OrderAccess::storestore() +// until it's repaired. In some cases OrderAccess::fence() -- which incurs local +// latency on the executing processor -- is a better choice as it scales on SMP +// systems. See http://blogs.sun.com/dave/entry/biased_locking_in_hotspot for a +// discussion of coherency costs. Note that all our current reference platforms +// provide strong ST-ST order, so the issue is moot on IA32, x64, and SPARC. +// +// As a general policy we use "volatile" to control compiler-based reordering +// and explicit fences (barriers) to control for architectural reordering performed +// by the CPU(s) or platform. + +static int MBFence (int x) { OrderAccess::fence(); return x; } + +struct SharedGlobals { + // These are highly shared mostly-read variables. + // To avoid false-sharing they need to be the sole occupants of a $ line. + double padPrefix [8]; + volatile int stwRandom ; + volatile int stwCycle ; + + // Hot RW variables -- Sequester to avoid false-sharing + double padSuffix [16]; + volatile int hcSequence ; + double padFinal [8] ; +} ; + +static SharedGlobals GVars ; + + +// Tunables ... +// The knob* variables are effectively final. Once set they should +// never be modified hence. Consider using __read_mostly with GCC. + +static int Knob_LogSpins = 0 ; // enable jvmstat tally for spins +static int Knob_HandOff = 0 ; +static int Knob_Verbose = 0 ; +static int Knob_ReportSettings = 0 ; + +static int Knob_SpinLimit = 5000 ; // derived by an external tool - +static int Knob_SpinBase = 0 ; // Floor AKA SpinMin +static int Knob_SpinBackOff = 0 ; // spin-loop backoff +static int Knob_CASPenalty = -1 ; // Penalty for failed CAS +static int Knob_OXPenalty = -1 ; // Penalty for observed _owner change +static int Knob_SpinSetSucc = 1 ; // spinners set the _succ field +static int Knob_SpinEarly = 1 ; +static int Knob_SuccEnabled = 1 ; // futile wake throttling +static int Knob_SuccRestrict = 0 ; // Limit successors + spinners to at-most-one +static int Knob_MaxSpinners = -1 ; // Should be a function of # CPUs +static int Knob_Bonus = 100 ; // spin success bonus +static int Knob_BonusB = 100 ; // spin success bonus +static int Knob_Penalty = 200 ; // spin failure penalty +static int Knob_Poverty = 1000 ; +static int Knob_SpinAfterFutile = 1 ; // Spin after returning from park() +static int Knob_FixedSpin = 0 ; +static int Knob_OState = 3 ; // Spinner checks thread state of _owner +static int Knob_UsePause = 1 ; +static int Knob_ExitPolicy = 0 ; +static int Knob_PreSpin = 10 ; // 20-100 likely better +static int Knob_ResetEvent = 0 ; +static int BackOffMask = 0 ; + +static int Knob_FastHSSEC = 0 ; +static int Knob_MoveNotifyee = 2 ; // notify() - disposition of notifyee +static int Knob_QMode = 0 ; // EntryList-cxq policy - queue discipline +static volatile int InitDone = 0 ; + + +// hashCode() generation : +// +// Possibilities: +// * MD5Digest of {obj,stwRandom} +// * CRC32 of {obj,stwRandom} or any linear-feedback shift register function. +// * A DES- or AES-style SBox[] mechanism +// * One of the Phi-based schemes, such as: +// 2654435761 = 2^32 * Phi (golden ratio) +// HashCodeValue = ((uintptr_t(obj) >> 3) * 2654435761) ^ GVars.stwRandom ; +// * A variation of Marsaglia's shift-xor RNG scheme. +// * (obj ^ stwRandom) is appealing, but can result +// in undesirable regularity in the hashCode values of adjacent objects +// (objects allocated back-to-back, in particular). This could potentially +// result in hashtable collisions and reduced hashtable efficiency. +// There are simple ways to "diffuse" the middle address bits over the +// generated hashCode values: +// + +static inline intptr_t get_next_hash(Thread * Self, oop obj) { + intptr_t value = 0 ; + if (hashCode == 0) { + // This form uses an unguarded global Park-Miller RNG, + // so it's possible for two threads to race and generate the same RNG. + // On MP system we'll have lots of RW access to a global, so the + // mechanism induces lots of coherency traffic. + value = os::random() ; + } else + if (hashCode == 1) { + // This variation has the property of being stable (idempotent) + // between STW operations. This can be useful in some of the 1-0 + // synchronization schemes. + intptr_t addrBits = intptr_t(obj) >> 3 ; + value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ; + } else + if (hashCode == 2) { + value = 1 ; // for sensitivity testing + } else + if (hashCode == 3) { + value = ++GVars.hcSequence ; + } else + if (hashCode == 4) { + value = intptr_t(obj) ; + } else { + // Marsaglia's xor-shift scheme with thread-specific state + // This is probably the best overall implementation -- we'll + // likely make this the default in future releases. + unsigned t = Self->_hashStateX ; + t ^= (t << 11) ; + Self->_hashStateX = Self->_hashStateY ; + Self->_hashStateY = Self->_hashStateZ ; + Self->_hashStateZ = Self->_hashStateW ; + unsigned v = Self->_hashStateW ; + v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ; + Self->_hashStateW = v ; + value = v ; + } + + value &= markOopDesc::hash_mask; + if (value == 0) value = 0xBAD ; + assert (value != markOopDesc::no_hash, "invariant") ; + TEVENT (hashCode: GENERATE) ; + return value; +} + +void BasicLock::print_on(outputStream* st) const { + st->print("monitor"); +} + +void BasicLock::move_to(oop obj, BasicLock* dest) { + // Check to see if we need to inflate the lock. This is only needed + // if an object is locked using "this" lightweight monitor. In that + // case, the displaced_header() is unlocked, because the + // displaced_header() contains the header for the originally unlocked + // object. However the object could have already been inflated. But it + // does not matter, the inflation will just a no-op. For other cases, + // the displaced header will be either 0x0 or 0x3, which are location + // independent, therefore the BasicLock is free to move. + // + // During OSR we may need to relocate a BasicLock (which contains a + // displaced word) from a location in an interpreter frame to a + // new location in a compiled frame. "this" refers to the source + // basiclock in the interpreter frame. "dest" refers to the destination + // basiclock in the new compiled frame. We *always* inflate in move_to(). + // The always-Inflate policy works properly, but in 1.5.0 it can sometimes + // cause performance problems in code that makes heavy use of a small # of + // uncontended locks. (We'd inflate during OSR, and then sync performance + // would subsequently plummet because the thread would be forced thru the slow-path). + // This problem has been made largely moot on IA32 by inlining the inflated fast-path + // operations in Fast_Lock and Fast_Unlock in i486.ad. + // + // Note that there is a way to safely swing the object's markword from + // one stack location to another. This avoids inflation. Obviously, + // we need to ensure that both locations refer to the current thread's stack. + // There are some subtle concurrency issues, however, and since the benefit is + // is small (given the support for inflated fast-path locking in the fast_lock, etc) + // we'll leave that optimization for another time. + + if (displaced_header()->is_neutral()) { + ObjectSynchronizer::inflate_helper(obj); + // WARNING: We can not put check here, because the inflation + // will not update the displaced header. Once BasicLock is inflated, + // no one should ever look at its content. + } else { + // Typically the displaced header will be 0 (recursive stack lock) or + // unused_mark. Naively we'd like to assert that the displaced mark + // value is either 0, neutral, or 3. But with the advent of the + // store-before-CAS avoidance in fast_lock/compiler_lock_object + // we can find any flavor mark in the displaced mark. + } +// [RGV] The next line appears to do nothing! + intptr_t dh = (intptr_t) displaced_header(); + dest->set_displaced_header(displaced_header()); +} + +// ----------------------------------------------------------------------------- + +// standard constructor, allows locking failures +ObjectLocker::ObjectLocker(Handle obj, Thread* thread, bool doLock) { + _dolock = doLock; + _thread = thread; + debug_only(if (StrictSafepointChecks) _thread->check_for_valid_safepoint_state(false);) + _obj = obj; + + if (_dolock) { + TEVENT (ObjectLocker) ; + + ObjectSynchronizer::fast_enter(_obj, &_lock, false, _thread); + } +} + +ObjectLocker::~ObjectLocker() { + if (_dolock) { + ObjectSynchronizer::fast_exit(_obj(), &_lock, _thread); + } +} + +// ----------------------------------------------------------------------------- + + +PerfCounter * ObjectSynchronizer::_sync_Inflations = NULL ; +PerfCounter * ObjectSynchronizer::_sync_Deflations = NULL ; +PerfCounter * ObjectSynchronizer::_sync_ContendedLockAttempts = NULL ; +PerfCounter * ObjectSynchronizer::_sync_FutileWakeups = NULL ; +PerfCounter * ObjectSynchronizer::_sync_Parks = NULL ; +PerfCounter * ObjectSynchronizer::_sync_EmptyNotifications = NULL ; +PerfCounter * ObjectSynchronizer::_sync_Notifications = NULL ; +PerfCounter * ObjectSynchronizer::_sync_PrivateA = NULL ; +PerfCounter * ObjectSynchronizer::_sync_PrivateB = NULL ; +PerfCounter * ObjectSynchronizer::_sync_SlowExit = NULL ; +PerfCounter * ObjectSynchronizer::_sync_SlowEnter = NULL ; +PerfCounter * ObjectSynchronizer::_sync_SlowNotify = NULL ; +PerfCounter * ObjectSynchronizer::_sync_SlowNotifyAll = NULL ; +PerfCounter * ObjectSynchronizer::_sync_FailedSpins = NULL ; +PerfCounter * ObjectSynchronizer::_sync_SuccessfulSpins = NULL ; +PerfCounter * ObjectSynchronizer::_sync_MonInCirculation = NULL ; +PerfCounter * ObjectSynchronizer::_sync_MonScavenged = NULL ; +PerfLongVariable * ObjectSynchronizer::_sync_MonExtant = NULL ; + +// One-shot global initialization for the sync subsystem. +// We could also defer initialization and initialize on-demand +// the first time we call inflate(). Initialization would +// be protected - like so many things - by the MonitorCache_lock. + +void ObjectSynchronizer::Initialize () { + static int InitializationCompleted = 0 ; + assert (InitializationCompleted == 0, "invariant") ; + InitializationCompleted = 1 ; + if (UsePerfData) { + EXCEPTION_MARK ; + #define NEWPERFCOUNTER(n) {n = PerfDataManager::create_counter(SUN_RT, #n, PerfData::U_Events,CHECK); } + #define NEWPERFVARIABLE(n) {n = PerfDataManager::create_variable(SUN_RT, #n, PerfData::U_Events,CHECK); } + NEWPERFCOUNTER(_sync_Inflations) ; + NEWPERFCOUNTER(_sync_Deflations) ; + NEWPERFCOUNTER(_sync_ContendedLockAttempts) ; + NEWPERFCOUNTER(_sync_FutileWakeups) ; + NEWPERFCOUNTER(_sync_Parks) ; + NEWPERFCOUNTER(_sync_EmptyNotifications) ; + NEWPERFCOUNTER(_sync_Notifications) ; + NEWPERFCOUNTER(_sync_SlowEnter) ; + NEWPERFCOUNTER(_sync_SlowExit) ; + NEWPERFCOUNTER(_sync_SlowNotify) ; + NEWPERFCOUNTER(_sync_SlowNotifyAll) ; + NEWPERFCOUNTER(_sync_FailedSpins) ; + NEWPERFCOUNTER(_sync_SuccessfulSpins) ; + NEWPERFCOUNTER(_sync_PrivateA) ; + NEWPERFCOUNTER(_sync_PrivateB) ; + NEWPERFCOUNTER(_sync_MonInCirculation) ; + NEWPERFCOUNTER(_sync_MonScavenged) ; + NEWPERFVARIABLE(_sync_MonExtant) ; + #undef NEWPERFCOUNTER + } +} + +// Compile-time asserts +// When possible, it's better to catch errors deterministically at +// compile-time than at runtime. The down-side to using compile-time +// asserts is that error message -- often something about negative array +// indices -- is opaque. + +#define CTASSERT(x) { int tag[1-(2*!(x))]; printf ("Tag @%X\n", tag); } + +void ObjectMonitor::ctAsserts() { + CTASSERT(offset_of (ObjectMonitor, _header) == 0); +} + +static int Adjust (volatile int * adr, int dx) { + int v ; + for (v = *adr ; Atomic::cmpxchg (v + dx, adr, v) != v; v = *adr) ; + return v ; +} + +// Ad-hoc mutual exclusion primitives: SpinLock and Mux +// +// We employ SpinLocks _only for low-contention, fixed-length +// short-duration critical sections where we're concerned +// about native mutex_t or HotSpot Mutex:: latency. +// The mux construct provides a spin-then-block mutual exclusion +// mechanism. +// +// Testing has shown that contention on the ListLock guarding gFreeList +// is common. If we implement ListLock as a simple SpinLock it's common +// for the JVM to devolve to yielding with little progress. This is true +// despite the fact that the critical sections protected by ListLock are +// extremely short. +// +// TODO-FIXME: ListLock should be of type SpinLock. +// We should make this a 1st-class type, integrated into the lock +// hierarchy as leaf-locks. Critically, the SpinLock structure +// should have sufficient padding to avoid false-sharing and excessive +// cache-coherency traffic. + + +typedef volatile int SpinLockT ; + +void Thread::SpinAcquire (volatile int * adr, const char * LockName) { + if (Atomic::cmpxchg (1, adr, 0) == 0) { + return ; // normal fast-path return + } + + // Slow-path : We've encountered contention -- Spin/Yield/Block strategy. + TEVENT (SpinAcquire - ctx) ; + int ctr = 0 ; + int Yields = 0 ; + for (;;) { + while (*adr != 0) { + ++ctr ; + if ((ctr & 0xFFF) == 0 || !os::is_MP()) { + if (Yields > 5) { + // Consider using a simple NakedSleep() instead. + // Then SpinAcquire could be called by non-JVM threads + Thread::current()->_ParkEvent->park(1) ; + } else { + os::NakedYield() ; + ++Yields ; + } + } else { + SpinPause() ; + } + } + if (Atomic::cmpxchg (1, adr, 0) == 0) return ; + } +} + +void Thread::SpinRelease (volatile int * adr) { + assert (*adr != 0, "invariant") ; + OrderAccess::fence() ; // guarantee at least release consistency. + // Roach-motel semantics. + // It's safe if subsequent LDs and STs float "up" into the critical section, + // but prior LDs and STs within the critical section can't be allowed + // to reorder or float past the ST that releases the lock. + *adr = 0 ; +} + +// muxAcquire and muxRelease: +// +// * muxAcquire and muxRelease support a single-word lock-word construct. +// The LSB of the word is set IFF the lock is held. +// The remainder of the word points to the head of a singly-linked list +// of threads blocked on the lock. +// +// * The current implementation of muxAcquire-muxRelease uses its own +// dedicated Thread._MuxEvent instance. If we're interested in +// minimizing the peak number of extant ParkEvent instances then +// we could eliminate _MuxEvent and "borrow" _ParkEvent as long +// as certain invariants were satisfied. Specifically, care would need +// to be taken with regards to consuming unpark() "permits". +// A safe rule of thumb is that a thread would never call muxAcquire() +// if it's enqueued (cxq, EntryList, WaitList, etc) and will subsequently +// park(). Otherwise the _ParkEvent park() operation in muxAcquire() could +// consume an unpark() permit intended for monitorenter, for instance. +// One way around this would be to widen the restricted-range semaphore +// implemented in park(). Another alternative would be to provide +// multiple instances of the PlatformEvent() for each thread. One +// instance would be dedicated to muxAcquire-muxRelease, for instance. +// +// * Usage: +// -- Only as leaf locks +// -- for short-term locking only as muxAcquire does not perform +// thread state transitions. +// +// Alternatives: +// * We could implement muxAcquire and muxRelease with MCS or CLH locks +// but with parking or spin-then-park instead of pure spinning. +// * Use Taura-Oyama-Yonenzawa locks. +// * It's possible to construct a 1-0 lock if we encode the lockword as +// (List,LockByte). Acquire will CAS the full lockword while Release +// will STB 0 into the LockByte. The 1-0 scheme admits stranding, so +// acquiring threads use timers (ParkTimed) to detect and recover from +// the stranding window. Thread/Node structures must be aligned on 256-byte +// boundaries by using placement-new. +// * Augment MCS with advisory back-link fields maintained with CAS(). +// Pictorially: LockWord -> T1 <-> T2 <-> T3 <-> ... <-> Tn <-> Owner. +// The validity of the backlinks must be ratified before we trust the value. +// If the backlinks are invalid the exiting thread must back-track through the +// the forward links, which are always trustworthy. +// * Add a successor indication. The LockWord is currently encoded as +// (List, LOCKBIT:1). We could also add a SUCCBIT or an explicit _succ variable +// to provide the usual futile-wakeup optimization. +// See RTStt for details. +// * Consider schedctl.sc_nopreempt to cover the critical section. +// + + +typedef volatile intptr_t MutexT ; // Mux Lock-word +enum MuxBits { LOCKBIT = 1 } ; + +void Thread::muxAcquire (volatile intptr_t * Lock, const char * LockName) { + intptr_t w = Atomic::cmpxchg_ptr (LOCKBIT, Lock, 0) ; + if (w == 0) return ; + if ((w & LOCKBIT) == 0 && Atomic::cmpxchg_ptr (w|LOCKBIT, Lock, w) == w) { + return ; + } + + TEVENT (muxAcquire - Contention) ; + ParkEvent * const Self = Thread::current()->_MuxEvent ; + assert ((intptr_t(Self) & LOCKBIT) == 0, "invariant") ; + for (;;) { + int its = (os::is_MP() ? 100 : 0) + 1 ; + + // Optional spin phase: spin-then-park strategy + while (--its >= 0) { + w = *Lock ; + if ((w & LOCKBIT) == 0 && Atomic::cmpxchg_ptr (w|LOCKBIT, Lock, w) == w) { + return ; + } + } + + Self->reset() ; + Self->OnList = intptr_t(Lock) ; + // The following fence() isn't _strictly necessary as the subsequent + // CAS() both serializes execution and ratifies the fetched *Lock value. + OrderAccess::fence(); + for (;;) { + w = *Lock ; + if ((w & LOCKBIT) == 0) { + if (Atomic::cmpxchg_ptr (w|LOCKBIT, Lock, w) == w) { + Self->OnList = 0 ; // hygiene - allows stronger asserts + return ; + } + continue ; // Interference -- *Lock changed -- Just retry + } + assert (w & LOCKBIT, "invariant") ; + Self->ListNext = (ParkEvent *) (w & ~LOCKBIT ); + if (Atomic::cmpxchg_ptr (intptr_t(Self)|LOCKBIT, Lock, w) == w) break ; + } + + while (Self->OnList != 0) { + Self->park() ; + } + } +} + +void Thread::muxAcquireW (volatile intptr_t * Lock, ParkEvent * ev) { + intptr_t w = Atomic::cmpxchg_ptr (LOCKBIT, Lock, 0) ; + if (w == 0) return ; + if ((w & LOCKBIT) == 0 && Atomic::cmpxchg_ptr (w|LOCKBIT, Lock, w) == w) { + return ; + } + + TEVENT (muxAcquire - Contention) ; + ParkEvent * ReleaseAfter = NULL ; + if (ev == NULL) { + ev = ReleaseAfter = ParkEvent::Allocate (NULL) ; + } + assert ((intptr_t(ev) & LOCKBIT) == 0, "invariant") ; + for (;;) { + guarantee (ev->OnList == 0, "invariant") ; + int its = (os::is_MP() ? 100 : 0) + 1 ; + + // Optional spin phase: spin-then-park strategy + while (--its >= 0) { + w = *Lock ; + if ((w & LOCKBIT) == 0 && Atomic::cmpxchg_ptr (w|LOCKBIT, Lock, w) == w) { + if (ReleaseAfter != NULL) { + ParkEvent::Release (ReleaseAfter) ; + } + return ; + } + } + + ev->reset() ; + ev->OnList = intptr_t(Lock) ; + // The following fence() isn't _strictly necessary as the subsequent + // CAS() both serializes execution and ratifies the fetched *Lock value. + OrderAccess::fence(); + for (;;) { + w = *Lock ; + if ((w & LOCKBIT) == 0) { + if (Atomic::cmpxchg_ptr (w|LOCKBIT, Lock, w) == w) { + ev->OnList = 0 ; + // We call ::Release while holding the outer lock, thus + // artificially lengthening the critical section. + // Consider deferring the ::Release() until the subsequent unlock(), + // after we've dropped the outer lock. + if (ReleaseAfter != NULL) { + ParkEvent::Release (ReleaseAfter) ; + } + return ; + } + continue ; // Interference -- *Lock changed -- Just retry + } + assert (w & LOCKBIT, "invariant") ; + ev->ListNext = (ParkEvent *) (w & ~LOCKBIT ); + if (Atomic::cmpxchg_ptr (intptr_t(ev)|LOCKBIT, Lock, w) == w) break ; + } + + while (ev->OnList != 0) { + ev->park() ; + } + } +} + +// Release() must extract a successor from the list and then wake that thread. +// It can "pop" the front of the list or use a detach-modify-reattach (DMR) scheme +// similar to that used by ParkEvent::Allocate() and ::Release(). DMR-based +// Release() would : +// (A) CAS() or swap() null to *Lock, releasing the lock and detaching the list. +// (B) Extract a successor from the private list "in-hand" +// (C) attempt to CAS() the residual back into *Lock over null. +// If there were any newly arrived threads and the CAS() would fail. +// In that case Release() would detach the RATs, re-merge the list in-hand +// with the RATs and repeat as needed. Alternately, Release() might +// detach and extract a successor, but then pass the residual list to the wakee. +// The wakee would be responsible for reattaching and remerging before it +// competed for the lock. +// +// Both "pop" and DMR are immune from ABA corruption -- there can be +// multiple concurrent pushers, but only one popper or detacher. +// This implementation pops from the head of the list. This is unfair, +// but tends to provide excellent throughput as hot threads remain hot. +// (We wake recently run threads first). + +void Thread::muxRelease (volatile intptr_t * Lock) { + for (;;) { + const intptr_t w = Atomic::cmpxchg_ptr (0, Lock, LOCKBIT) ; + assert (w & LOCKBIT, "invariant") ; + if (w == LOCKBIT) return ; + ParkEvent * List = (ParkEvent *) (w & ~LOCKBIT) ; + assert (List != NULL, "invariant") ; + assert (List->OnList == intptr_t(Lock), "invariant") ; + ParkEvent * nxt = List->ListNext ; + + // The following CAS() releases the lock and pops the head element. + if (Atomic::cmpxchg_ptr (intptr_t(nxt), Lock, w) != w) { + continue ; + } + List->OnList = 0 ; + OrderAccess::fence() ; + List->unpark () ; + return ; + } +} + +// ObjectMonitor Lifecycle +// ----------------------- +// Inflation unlinks monitors from the global gFreeList and +// associates them with objects. Deflation -- which occurs at +// STW-time -- disassociates idle monitors from objects. Such +// scavenged monitors are returned to the gFreeList. +// +// The global list is protected by ListLock. All the critical sections +// are short and operate in constant-time. +// +// ObjectMonitors reside in type-stable memory (TSM) and are immortal. +// +// Lifecycle: +// -- unassigned and on the global free list +// -- unassigned and on a thread's private omFreeList +// -- assigned to an object. The object is inflated and the mark refers +// to the objectmonitor. +// +// TODO-FIXME: +// +// * We currently protect the gFreeList with a simple lock. +// An alternate lock-free scheme would be to pop elements from the gFreeList +// with CAS. This would be safe from ABA corruption as long we only +// recycled previously appearing elements onto the list in deflate_idle_monitors() +// at STW-time. Completely new elements could always be pushed onto the gFreeList +// with CAS. Elements that appeared previously on the list could only +// be installed at STW-time. +// +// * For efficiency and to help reduce the store-before-CAS penalty +// the objectmonitors on gFreeList or local free lists should be ready to install +// with the exception of _header and _object. _object can be set after inflation. +// In particular, keep all objectMonitors on a thread's private list in ready-to-install +// state with m.Owner set properly. +// +// * We could all diffuse contention by using multiple global (FreeList, Lock) +// pairs -- threads could use trylock() and a cyclic-scan strategy to search for +// an unlocked free list. +// +// * Add lifecycle tags and assert()s. +// +// * Be more consistent about when we clear an objectmonitor's fields: +// A. After extracting the objectmonitor from a free list. +// B. After adding an objectmonitor to a free list. +// + +ObjectMonitor * ObjectSynchronizer::gBlockList = NULL ; +ObjectMonitor * volatile ObjectSynchronizer::gFreeList = NULL ; +static volatile intptr_t ListLock = 0 ; // protects global monitor free-list cache +#define CHAINMARKER ((oop)-1) + +ObjectMonitor * ATTR ObjectSynchronizer::omAlloc (Thread * Self) { + // A large MAXPRIVATE value reduces both list lock contention + // and list coherency traffic, but also tends to increase the + // number of objectMonitors in circulation as well as the STW + // scavenge costs. As usual, we lean toward time in space-time + // tradeoffs. + const int MAXPRIVATE = 1024 ; + for (;;) { + ObjectMonitor * m ; + + // 1: try to allocate from the thread's local omFreeList. + // Threads will attempt to allocate first from their local list, then + // from the global list, and only after those attempts fail will the thread + // attempt to instantiate new monitors. Thread-local free lists take + // heat off the ListLock and improve allocation latency, as well as reducing + // coherency traffic on the shared global list. + m = Self->omFreeList ; + if (m != NULL) { + Self->omFreeList = m->FreeNext ; + Self->omFreeCount -- ; + // CONSIDER: set m->FreeNext = BAD -- diagnostic hygiene + guarantee (m->object() == NULL, "invariant") ; + return m ; + } + + // 2: try to allocate from the global gFreeList + // CONSIDER: use muxTry() instead of muxAcquire(). + // If the muxTry() fails then drop immediately into case 3. + // If we're using thread-local free lists then try + // to reprovision the caller's free list. + if (gFreeList != NULL) { + // Reprovision the thread's omFreeList. + // Use bulk transfers to reduce the allocation rate and heat + // on various locks. + Thread::muxAcquire (&ListLock, "omAlloc") ; + for (int i = Self->omFreeProvision; --i >= 0 && gFreeList != NULL; ) { + ObjectMonitor * take = gFreeList ; + gFreeList = take->FreeNext ; + guarantee (take->object() == NULL, "invariant") ; + guarantee (!take->is_busy(), "invariant") ; + take->Recycle() ; + omRelease (Self, take) ; + } + Thread::muxRelease (&ListLock) ; + Self->omFreeProvision += 1 + (Self->omFreeProvision/2) ; + if (Self->omFreeProvision > MAXPRIVATE ) Self->omFreeProvision = MAXPRIVATE ; + TEVENT (omFirst - reprovision) ; + continue ; + } + + // 3: allocate a block of new ObjectMonitors + // Both the local and global free lists are empty -- resort to malloc(). + // In the current implementation objectMonitors are TSM - immortal. + assert (_BLOCKSIZE > 1, "invariant") ; + ObjectMonitor * temp = new ObjectMonitor[_BLOCKSIZE]; + + // NOTE: (almost) no way to recover if allocation failed. + // We might be able to induce a STW safepoint and scavenge enough + // objectMonitors to permit progress. + if (temp == NULL) { + vm_exit_out_of_memory (sizeof (ObjectMonitor[_BLOCKSIZE]), "Allocate ObjectMonitors") ; + } + + // Format the block. + // initialize the linked list, each monitor points to its next + // forming the single linked free list, the very first monitor + // will points to next block, which forms the block list. + // The trick of using the 1st element in the block as gBlockList + // linkage should be reconsidered. A better implementation would + // look like: class Block { Block * next; int N; ObjectMonitor Body [N] ; } + + for (int i = 1; i < _BLOCKSIZE ; i++) { + temp[i].FreeNext = &temp[i+1]; + } + + // terminate the last monitor as the end of list + temp[_BLOCKSIZE - 1].FreeNext = NULL ; + + // Element [0] is reserved for global list linkage + temp[0].set_object(CHAINMARKER); + + // Consider carving out this thread's current request from the + // block in hand. This avoids some lock traffic and redundant + // list activity. + + // Acquire the ListLock to manipulate BlockList and FreeList. + // An Oyama-Taura-Yonezawa scheme might be more efficient. + Thread::muxAcquire (&ListLock, "omAlloc [2]") ; + + // Add the new block to the list of extant blocks (gBlockList). + // The very first objectMonitor in a block is reserved and dedicated. + // It serves as blocklist "next" linkage. + temp[0].FreeNext = gBlockList; + gBlockList = temp; + + // Add the new string of objectMonitors to the global free list + temp[_BLOCKSIZE - 1].FreeNext = gFreeList ; + gFreeList = temp + 1; + Thread::muxRelease (&ListLock) ; + TEVENT (Allocate block of monitors) ; + } +} + +// Place "m" on the caller's private per-thread omFreeList. +// In practice there's no need to clamp or limit the number of +// monitors on a thread's omFreeList as the only time we'll call +// omRelease is to return a monitor to the free list after a CAS +// attempt failed. This doesn't allow unbounded #s of monitors to +// accumulate on a thread's free list. +// +// In the future the usage of omRelease() might change and monitors +// could migrate between free lists. In that case to avoid excessive +// accumulation we could limit omCount to (omProvision*2), otherwise return +// the objectMonitor to the global list. We should drain (return) in reasonable chunks. +// That is, *not* one-at-a-time. + + +void ObjectSynchronizer::omRelease (Thread * Self, ObjectMonitor * m) { + guarantee (m->object() == NULL, "invariant") ; + m->FreeNext = Self->omFreeList ; + Self->omFreeList = m ; + Self->omFreeCount ++ ; +} + +// Return the monitors of a moribund thread's local free list to +// the global free list. Typically a thread calls omFlush() when +// it's dying. We could also consider having the VM thread steal +// monitors from threads that have not run java code over a few +// consecutive STW safepoints. Relatedly, we might decay +// omFreeProvision at STW safepoints. +// +// We currently call omFlush() from the Thread:: dtor _after the thread +// has been excised from the thread list and is no longer a mutator. +// That means that omFlush() can run concurrently with a safepoint and +// the scavenge operator. Calling omFlush() from JavaThread::exit() might +// be a better choice as we could safely reason that that the JVM is +// not at a safepoint at the time of the call, and thus there could +// be not inopportune interleavings between omFlush() and the scavenge +// operator. + +void ObjectSynchronizer::omFlush (Thread * Self) { + ObjectMonitor * List = Self->omFreeList ; // Null-terminated SLL + Self->omFreeList = NULL ; + if (List == NULL) return ; + ObjectMonitor * Tail = NULL ; + ObjectMonitor * s ; + for (s = List ; s != NULL ; s = s->FreeNext) { + Tail = s ; + guarantee (s->object() == NULL, "invariant") ; + guarantee (!s->is_busy(), "invariant") ; + s->set_owner (NULL) ; // redundant but good hygiene + TEVENT (omFlush - Move one) ; + } + + guarantee (Tail != NULL && List != NULL, "invariant") ; + Thread::muxAcquire (&ListLock, "omFlush") ; + Tail->FreeNext = gFreeList ; + gFreeList = List ; + Thread::muxRelease (&ListLock) ; + TEVENT (omFlush) ; +} + + +// Get the next block in the block list. +static inline ObjectMonitor* next(ObjectMonitor* block) { + assert(block->object() == CHAINMARKER, "must be a block header"); + block = block->FreeNext ; + assert(block == NULL || block->object() == CHAINMARKER, "must be a block header"); + return block; +} + +// Fast path code shared by multiple functions +ObjectMonitor* ObjectSynchronizer::inflate_helper(oop obj) { + markOop mark = obj->mark(); + if (mark->has_monitor()) { + assert(ObjectSynchronizer::verify_objmon_isinpool(mark->monitor()), "monitor is invalid"); + assert(mark->monitor()->header()->is_neutral(), "monitor must record a good object header"); + return mark->monitor(); + } + return ObjectSynchronizer::inflate(Thread::current(), obj); +} + +// Note that we could encounter some performance loss through false-sharing as +// multiple locks occupy the same $ line. Padding might be appropriate. + +#define NINFLATIONLOCKS 256 +static volatile intptr_t InflationLocks [NINFLATIONLOCKS] ; + +static markOop ReadStableMark (oop obj) { + markOop mark = obj->mark() ; + if (!mark->is_being_inflated()) { + return mark ; // normal fast-path return + } + + int its = 0 ; + for (;;) { + markOop mark = obj->mark() ; + if (!mark->is_being_inflated()) { + return mark ; // normal fast-path return + } + + // The object is being inflated by some other thread. + // The caller of ReadStableMark() must wait for inflation to complete. + // Avoid live-lock + // TODO: consider calling SafepointSynchronize::do_call_back() while + // spinning to see if there's a safepoint pending. If so, immediately + // yielding or blocking would be appropriate. Avoid spinning while + // there is a safepoint pending. + // TODO: add inflation contention performance counters. + // TODO: restrict the aggregate number of spinners. + + ++its ; + if (its > 10000 || !os::is_MP()) { + if (its & 1) { + os::NakedYield() ; + TEVENT (Inflate: INFLATING - yield) ; + } else { + // Note that the following code attenuates the livelock problem but is not + // a complete remedy. A more complete solution would require that the inflating + // thread hold the associated inflation lock. The following code simply restricts + // the number of spinners to at most one. We'll have N-2 threads blocked + // on the inflationlock, 1 thread holding the inflation lock and using + // a yield/park strategy, and 1 thread in the midst of inflation. + // A more refined approach would be to change the encoding of INFLATING + // to allow encapsulation of a native thread pointer. Threads waiting for + // inflation to complete would use CAS to push themselves onto a singly linked + // list rooted at the markword. Once enqueued, they'd loop, checking a per-thread flag + // and calling park(). When inflation was complete the thread that accomplished inflation + // would detach the list and set the markword to inflated with a single CAS and + // then for each thread on the list, set the flag and unpark() the thread. + // This is conceptually similar to muxAcquire-muxRelease, except that muxRelease + // wakes at most one thread whereas we need to wake the entire list. + int ix = (intptr_t(obj) >> 5) & (NINFLATIONLOCKS-1) ; + int YieldThenBlock = 0 ; + assert (ix >= 0 && ix < NINFLATIONLOCKS, "invariant") ; + assert ((NINFLATIONLOCKS & (NINFLATIONLOCKS-1)) == 0, "invariant") ; + Thread::muxAcquire (InflationLocks + ix, "InflationLock") ; + while (obj->mark() == markOopDesc::INFLATING()) { + // Beware: NakedYield() is advisory and has almost no effect on some platforms + // so we periodically call Self->_ParkEvent->park(1). + // We use a mixed spin/yield/block mechanism. + if ((YieldThenBlock++) >= 16) { + Thread::current()->_ParkEvent->park(1) ; + } else { + os::NakedYield() ; + } + } + Thread::muxRelease (InflationLocks + ix ) ; + TEVENT (Inflate: INFLATING - yield/park) ; + } + } else { + SpinPause() ; // SMP-polite spinning + } + } +} + +ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) { + // Inflate mutates the heap ... + // Relaxing assertion for bug 6320749. + assert (Universe::verify_in_progress() || + !SafepointSynchronize::is_at_safepoint(), "invariant") ; + + for (;;) { + const markOop mark = object->mark() ; + assert (!mark->has_bias_pattern(), "invariant") ; + + // The mark can be in one of the following states: + // * Inflated - just return + // * Stack-locked - coerce it to inflated + // * INFLATING - busy wait for conversion to complete + // * Neutral - aggressively inflate the object. + // * BIASED - Illegal. We should never see this + + // CASE: inflated + if (mark->has_monitor()) { + ObjectMonitor * inf = mark->monitor() ; + assert (inf->header()->is_neutral(), "invariant"); + assert (inf->object() == object, "invariant") ; + assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid"); + return inf ; + } + + // CASE: inflation in progress - inflating over a stack-lock. + // Some other thread is converting from stack-locked to inflated. + // Only that thread can complete inflation -- other threads must wait. + // The INFLATING value is transient. + // Currently, we spin/yield/park and poll the markword, waiting for inflation to finish. + // We could always eliminate polling by parking the thread on some auxiliary list. + if (mark == markOopDesc::INFLATING()) { + TEVENT (Inflate: spin while INFLATING) ; + ReadStableMark(object) ; + continue ; + } + + // CASE: stack-locked + // Could be stack-locked either by this thread or by some other thread. + // + // Note that we allocate the objectmonitor speculatively, _before_ attempting + // to install INFLATING into the mark word. We originally installed INFLATING, + // allocated the objectmonitor, and then finally STed the address of the + // objectmonitor into the mark. This was correct, but artificially lengthened + // the interval in which INFLATED appeared in the mark, thus increasing + // the odds of inflation contention. + // + // We now use per-thread private objectmonitor free lists. + // These list are reprovisioned from the global free list outside the + // critical INFLATING...ST interval. A thread can transfer + // multiple objectmonitors en-mass from the global free list to its local free list. + // This reduces coherency traffic and lock contention on the global free list. + // Using such local free lists, it doesn't matter if the omAlloc() call appears + // before or after the CAS(INFLATING) operation. + // See the comments in omAlloc(). + + if (mark->has_locker()) { + ObjectMonitor * m = omAlloc (Self) ; + // Optimistically prepare the objectmonitor - anticipate successful CAS + // We do this before the CAS in order to minimize the length of time + // in which INFLATING appears in the mark. + m->Recycle(); + m->FreeNext = NULL ; + m->_Responsible = NULL ; + m->OwnerIsThread = 0 ; + m->_recursions = 0 ; + m->_SpinDuration = Knob_SpinLimit ; // Consider: maintain by type/class + + markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ; + if (cmp != mark) { + omRelease (Self, m) ; + continue ; // Interference -- just retry + } + + // We've successfully installed INFLATING (0) into the mark-word. + // This is the only case where 0 will appear in a mark-work. + // Only the singular thread that successfully swings the mark-word + // to 0 can perform (or more precisely, complete) inflation. + // + // Why do we CAS a 0 into the mark-word instead of just CASing the + // mark-word from the stack-locked value directly to the new inflated state? + // Consider what happens when a thread unlocks a stack-locked object. + // It attempts to use CAS to swing the displaced header value from the + // on-stack basiclock back into the object header. Recall also that the + // header value (hashcode, etc) can reside in (a) the object header, or + // (b) a displaced header associated with the stack-lock, or (c) a displaced + // header in an objectMonitor. The inflate() routine must copy the header + // value from the basiclock on the owner's stack to the objectMonitor, all + // the while preserving the hashCode stability invariants. If the owner + // decides to release the lock while the value is 0, the unlock will fail + // and control will eventually pass from slow_exit() to inflate. The owner + // will then spin, waiting for the 0 value to disappear. Put another way, + // the 0 causes the owner to stall if the owner happens to try to + // drop the lock (restoring the header from the basiclock to the object) + // while inflation is in-progress. This protocol avoids races that might + // would otherwise permit hashCode values to change or "flicker" for an object. + // Critically, while object->mark is 0 mark->displaced_mark_helper() is stable. + // 0 serves as a "BUSY" inflate-in-progress indicator. + + + // fetch the displaced mark from the owner's stack. + // The owner can't die or unwind past the lock while our INFLATING + // object is in the mark. Furthermore the owner can't complete + // an unlock on the object, either. + markOop dmw = mark->displaced_mark_helper() ; + assert (dmw->is_neutral(), "invariant") ; + + // Setup monitor fields to proper values -- prepare the monitor + m->set_header(dmw) ; + + // Optimization: if the mark->locker stack address is associated + // with this thread we could simply set m->_owner = Self and + // m->OwnerIsThread = 1. Note that a thread can inflate an object + // that it has stack-locked -- as might happen in wait() -- directly + // with CAS. That is, we can avoid the xchg-NULL .... ST idiom. + m->set_owner (mark->locker()); + m->set_object(object); + // TODO-FIXME: assert BasicLock->dhw != 0. + + // Must preserve store ordering. The monitor state must + // be stable at the time of publishing the monitor address. + guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ; + object->release_set_mark(markOopDesc::encode(m)); + + // Hopefully the performance counters are allocated on distinct cache lines + // to avoid false sharing on MP systems ... + if (_sync_Inflations != NULL) _sync_Inflations->inc() ; + TEVENT(Inflate: overwrite stacklock) ; + if (TraceMonitorInflation) { + if (object->is_instance()) { + ResourceMark rm; + tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s", + (intptr_t) object, (intptr_t) object->mark(), + Klass::cast(object->klass())->external_name()); + } + } + return m ; + } + + // CASE: neutral + // TODO-FIXME: for entry we currently inflate and then try to CAS _owner. + // If we know we're inflating for entry it's better to inflate by swinging a + // pre-locked objectMonitor pointer into the object header. A successful + // CAS inflates the object *and* confers ownership to the inflating thread. + // In the current implementation we use a 2-step mechanism where we CAS() + // to inflate and then CAS() again to try to swing _owner from NULL to Self. + // An inflateTry() method that we could call from fast_enter() and slow_enter() + // would be useful. + + assert (mark->is_neutral(), "invariant"); + ObjectMonitor * m = omAlloc (Self) ; + // prepare m for installation - set monitor to initial state + m->Recycle(); + m->set_header(mark); + m->set_owner(NULL); + m->set_object(object); + m->OwnerIsThread = 1 ; + m->_recursions = 0 ; + m->FreeNext = NULL ; + m->_Responsible = NULL ; + m->_SpinDuration = Knob_SpinLimit ; // consider: keep metastats by type/class + + if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) { + m->set_object (NULL) ; + m->set_owner (NULL) ; + m->OwnerIsThread = 0 ; + m->Recycle() ; + omRelease (Self, m) ; + m = NULL ; + continue ; + // interference - the markword changed - just retry. + // The state-transitions are one-way, so there's no chance of + // live-lock -- "Inflated" is an absorbing state. + } + + // Hopefully the performance counters are allocated on distinct + // cache lines to avoid false sharing on MP systems ... + if (_sync_Inflations != NULL) _sync_Inflations->inc() ; + TEVENT(Inflate: overwrite neutral) ; + if (TraceMonitorInflation) { + if (object->is_instance()) { + ResourceMark rm; + tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s", + (intptr_t) object, (intptr_t) object->mark(), + Klass::cast(object->klass())->external_name()); + } + } + return m ; + } +} + + +// This the fast monitor enter. The interpreter and compiler use +// some assembly copies of this code. Make sure update those code +// if the following function is changed. The implementation is +// extremely sensitive to race condition. Be careful. + +void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) { + if (UseBiasedLocking) { + if (!SafepointSynchronize::is_at_safepoint()) { + BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD); + if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) { + return; + } + } else { + assert(!attempt_rebias, "can not rebias toward VM thread"); + BiasedLocking::revoke_at_safepoint(obj); + } + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + + THREAD->update_highest_lock((address)lock); + slow_enter (obj, lock, THREAD) ; +} + +void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) { + assert(!object->mark()->has_bias_pattern(), "should not see bias pattern here"); + // if displaced header is null, the previous enter is recursive enter, no-op + markOop dhw = lock->displaced_header(); + markOop mark ; + if (dhw == NULL) { + // Recursive stack-lock. + // Diagnostics -- Could be: stack-locked, inflating, inflated. + mark = object->mark() ; + assert (!mark->is_neutral(), "invariant") ; + if (mark->has_locker() && mark != markOopDesc::INFLATING()) { + assert(THREAD->is_lock_owned((address)mark->locker()), "invariant") ; + } + if (mark->has_monitor()) { + ObjectMonitor * m = mark->monitor() ; + assert(((oop)(m->object()))->mark() == mark, "invariant") ; + assert(m->is_entered(THREAD), "invariant") ; + } + return ; + } + + mark = object->mark() ; + + // If the object is stack-locked by the current thread, try to + // swing the displaced header from the box back to the mark. + if (mark == (markOop) lock) { + assert (dhw->is_neutral(), "invariant") ; + if ((markOop) Atomic::cmpxchg_ptr (dhw, object->mark_addr(), mark) == mark) { + TEVENT (fast_exit: release stacklock) ; + return; + } + } + + ObjectSynchronizer::inflate(THREAD, object)->exit (THREAD) ; +} + +// This routine is used to handle interpreter/compiler slow case +// We don't need to use fast path here, because it must have been +// failed in the interpreter/compiler code. +void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) { + markOop mark = obj->mark(); + assert(!mark->has_bias_pattern(), "should not see bias pattern here"); + + if (mark->is_neutral()) { + // Anticipate successful CAS -- the ST of the displaced mark must + // be visible <= the ST performed by the CAS. + lock->set_displaced_header(mark); + if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) { + TEVENT (slow_enter: release stacklock) ; + return ; + } + // Fall through to inflate() ... + } else + if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { + assert(lock != mark->locker(), "must not re-lock the same lock"); + assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock"); + lock->set_displaced_header(NULL); + return; + } + +#if 0 + // The following optimization isn't particularly useful. + if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) { + lock->set_displaced_header (NULL) ; + return ; + } +#endif + + // The object header will never be displaced to this lock, + // so it does not matter what the value is, except that it + // must be non-zero to avoid looking like a re-entrant lock, + // and must not look locked either. + lock->set_displaced_header(markOopDesc::unused_mark()); + ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD); +} + +// This routine is used to handle interpreter/compiler slow case +// We don't need to use fast path here, because it must have +// failed in the interpreter/compiler code. Simply use the heavy +// weight monitor should be ok, unless someone find otherwise. +void ObjectSynchronizer::slow_exit(oop object, BasicLock* lock, TRAPS) { + fast_exit (object, lock, THREAD) ; +} + +// NOTE: must use heavy weight monitor to handle jni monitor enter +void ObjectSynchronizer::jni_enter(Handle obj, TRAPS) { // possible entry from jni enter + // the current locking is from JNI instead of Java code + TEVENT (jni_enter) ; + if (UseBiasedLocking) { + BiasedLocking::revoke_and_rebias(obj, false, THREAD); + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + THREAD->set_current_pending_monitor_is_from_java(false); + ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD); + THREAD->set_current_pending_monitor_is_from_java(true); +} + +// NOTE: must use heavy weight monitor to handle jni monitor enter +bool ObjectSynchronizer::jni_try_enter(Handle obj, Thread* THREAD) { + if (UseBiasedLocking) { + BiasedLocking::revoke_and_rebias(obj, false, THREAD); + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + + ObjectMonitor* monitor = ObjectSynchronizer::inflate_helper(obj()); + return monitor->try_enter(THREAD); +} + + +// NOTE: must use heavy weight monitor to handle jni monitor exit +void ObjectSynchronizer::jni_exit(oop obj, Thread* THREAD) { + TEVENT (jni_exit) ; + if (UseBiasedLocking) { + BiasedLocking::revoke_and_rebias(obj, false, THREAD); + } + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + + ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj); + // If this thread has locked the object, exit the monitor. Note: can't use + // monitor->check(CHECK); must exit even if an exception is pending. + if (monitor->check(THREAD)) { + monitor->exit(THREAD); + } +} + +// complete_exit()/reenter() are used to wait on a nested lock +// i.e. to give up an outer lock completely and then re-enter +// Used when holding nested locks - lock acquisition order: lock1 then lock2 +// 1) complete_exit lock1 - saving recursion count +// 2) wait on lock2 +// 3) when notified on lock2, unlock lock2 +// 4) reenter lock1 with original recursion count +// 5) lock lock2 +// NOTE: must use heavy weight monitor to handle complete_exit/reenter() +intptr_t ObjectSynchronizer::complete_exit(Handle obj, TRAPS) { + TEVENT (complete_exit) ; + if (UseBiasedLocking) { + BiasedLocking::revoke_and_rebias(obj, false, THREAD); + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + + ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj()); + + return monitor->complete_exit(THREAD); +} + +// NOTE: must use heavy weight monitor to handle complete_exit/reenter() +void ObjectSynchronizer::reenter(Handle obj, intptr_t recursion, TRAPS) { + TEVENT (reenter) ; + if (UseBiasedLocking) { + BiasedLocking::revoke_and_rebias(obj, false, THREAD); + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + + ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj()); + + monitor->reenter(recursion, THREAD); +} + +// This exists only as a workaround of dtrace bug 6254741 +int dtrace_waited_probe(ObjectMonitor* monitor, Handle obj, Thread* thr) { + DTRACE_MONITOR_PROBE(waited, monitor, obj(), thr); + return 0; +} + +// NOTE: must use heavy weight monitor to handle wait() +void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) { + if (UseBiasedLocking) { + BiasedLocking::revoke_and_rebias(obj, false, THREAD); + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + if (millis < 0) { + TEVENT (wait - throw IAX) ; + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative"); + } + ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj()); + DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis); + monitor->wait(millis, true, THREAD); + + /* This dummy call is in place to get around dtrace bug 6254741. Once + that's fixed we can uncomment the following line and remove the call */ + // DTRACE_MONITOR_PROBE(waited, monitor, obj(), THREAD); + dtrace_waited_probe(monitor, obj, THREAD); +} + +void ObjectSynchronizer::waitUninterruptibly (Handle obj, jlong millis, TRAPS) { + if (UseBiasedLocking) { + BiasedLocking::revoke_and_rebias(obj, false, THREAD); + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + if (millis < 0) { + TEVENT (wait - throw IAX) ; + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative"); + } + ObjectSynchronizer::inflate(THREAD, obj()) -> wait(millis, false, THREAD) ; +} + +void ObjectSynchronizer::notify(Handle obj, TRAPS) { + if (UseBiasedLocking) { + BiasedLocking::revoke_and_rebias(obj, false, THREAD); + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + + markOop mark = obj->mark(); + if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { + return; + } + ObjectSynchronizer::inflate(THREAD, obj())->notify(THREAD); +} + +// NOTE: see comment of notify() +void ObjectSynchronizer::notifyall(Handle obj, TRAPS) { + if (UseBiasedLocking) { + BiasedLocking::revoke_and_rebias(obj, false, THREAD); + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + + markOop mark = obj->mark(); + if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { + return; + } + ObjectSynchronizer::inflate(THREAD, obj())->notifyAll(THREAD); +} + +intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) { + if (UseBiasedLocking) { + // NOTE: many places throughout the JVM do not expect a safepoint + // to be taken here, in particular most operations on perm gen + // objects. However, we only ever bias Java instances and all of + // the call sites of identity_hash that might revoke biases have + // been checked to make sure they can handle a safepoint. The + // added check of the bias pattern is to avoid useless calls to + // thread-local storage. + if (obj->mark()->has_bias_pattern()) { + // Box and unbox the raw reference just in case we cause a STW safepoint. + Handle hobj (Self, obj) ; + // Relaxing assertion for bug 6320749. + assert (Universe::verify_in_progress() || + !SafepointSynchronize::is_at_safepoint(), + "biases should not be seen by VM thread here"); + BiasedLocking::revoke_and_rebias(hobj, false, JavaThread::current()); + obj = hobj() ; + assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + } + + // hashCode() is a heap mutator ... + // Relaxing assertion for bug 6320749. + assert (Universe::verify_in_progress() || + !SafepointSynchronize::is_at_safepoint(), "invariant") ; + assert (Universe::verify_in_progress() || + Self->is_Java_thread() , "invariant") ; + assert (Universe::verify_in_progress() || + ((JavaThread *)Self)->thread_state() != _thread_blocked, "invariant") ; + + ObjectMonitor* monitor = NULL; + markOop temp, test; + intptr_t hash; + markOop mark = ReadStableMark (obj); + + // object should remain ineligible for biased locking + assert (!mark->has_bias_pattern(), "invariant") ; + + if (mark->is_neutral()) { + hash = mark->hash(); // this is a normal header + if (hash) { // if it has hash, just return it + return hash; + } + hash = get_next_hash(Self, obj); // allocate a new hash code + temp = mark->copy_set_hash(hash); // merge the hash code into header + // use (machine word version) atomic operation to install the hash + test = (markOop) Atomic::cmpxchg_ptr(temp, obj->mark_addr(), mark); + if (test == mark) { + return hash; + } + // If atomic operation failed, we must inflate the header + // into heavy weight monitor. We could add more code here + // for fast path, but it does not worth the complexity. + } else if (mark->has_monitor()) { + monitor = mark->monitor(); + temp = monitor->header(); + assert (temp->is_neutral(), "invariant") ; + hash = temp->hash(); + if (hash) { + return hash; + } + // Skip to the following code to reduce code size + } else if (Self->is_lock_owned((address)mark->locker())) { + temp = mark->displaced_mark_helper(); // this is a lightweight monitor owned + assert (temp->is_neutral(), "invariant") ; + hash = temp->hash(); // by current thread, check if the displaced + if (hash) { // header contains hash code + return hash; + } + // WARNING: + // The displaced header is strictly immutable. + // It can NOT be changed in ANY cases. So we have + // to inflate the header into heavyweight monitor + // even the current thread owns the lock. The reason + // is the BasicLock (stack slot) will be asynchronously + // read by other threads during the inflate() function. + // Any change to stack may not propagate to other threads + // correctly. + } + + // Inflate the monitor to set hash code + monitor = ObjectSynchronizer::inflate(Self, obj); + // Load displaced header and check it has hash code + mark = monitor->header(); + assert (mark->is_neutral(), "invariant") ; + hash = mark->hash(); + if (hash == 0) { + hash = get_next_hash(Self, obj); + temp = mark->copy_set_hash(hash); // merge hash code into header + assert (temp->is_neutral(), "invariant") ; + test = (markOop) Atomic::cmpxchg_ptr(temp, monitor, mark); + if (test != mark) { + // The only update to the header in the monitor (outside GC) + // is install the hash code. If someone add new usage of + // displaced header, please update this code + hash = test->hash(); + assert (test->is_neutral(), "invariant") ; + assert (hash != 0, "Trivial unexpected object/monitor header usage."); + } + } + // We finally get the hash + return hash; +} + +// Deprecated -- use FastHashCode() instead. + +intptr_t ObjectSynchronizer::identity_hash_value_for(Handle obj) { + return FastHashCode (Thread::current(), obj()) ; +} + +bool ObjectSynchronizer::current_thread_holds_lock(JavaThread* thread, + Handle h_obj) { + if (UseBiasedLocking) { + BiasedLocking::revoke_and_rebias(h_obj, false, thread); + assert(!h_obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + + assert(thread == JavaThread::current(), "Can only be called on current thread"); + oop obj = h_obj(); + + markOop mark = ReadStableMark (obj) ; + + // Uncontended case, header points to stack + if (mark->has_locker()) { + return thread->is_lock_owned((address)mark->locker()); + } + // Contended case, header points to ObjectMonitor (tagged pointer) + if (mark->has_monitor()) { + ObjectMonitor* monitor = mark->monitor(); + return monitor->is_entered(thread) != 0 ; + } + // Unlocked case, header in place + assert(mark->is_neutral(), "sanity check"); + return false; +} + +// Be aware of this method could revoke bias of the lock object. +// This method querys the ownership of the lock handle specified by 'h_obj'. +// If the current thread owns the lock, it returns owner_self. If no +// thread owns the lock, it returns owner_none. Otherwise, it will return +// ower_other. +ObjectSynchronizer::LockOwnership ObjectSynchronizer::query_lock_ownership +(JavaThread *self, Handle h_obj) { + // The caller must beware this method can revoke bias, and + // revocation can result in a safepoint. + assert (!SafepointSynchronize::is_at_safepoint(), "invariant") ; + assert (self->thread_state() != _thread_blocked , "invariant") ; + + // Possible mark states: neutral, biased, stack-locked, inflated + + if (UseBiasedLocking && h_obj()->mark()->has_bias_pattern()) { + // CASE: biased + BiasedLocking::revoke_and_rebias(h_obj, false, self); + assert(!h_obj->mark()->has_bias_pattern(), + "biases should be revoked by now"); + } + + assert(self == JavaThread::current(), "Can only be called on current thread"); + oop obj = h_obj(); + markOop mark = ReadStableMark (obj) ; + + // CASE: stack-locked. Mark points to a BasicLock on the owner's stack. + if (mark->has_locker()) { + return self->is_lock_owned((address)mark->locker()) ? + owner_self : owner_other; + } + + // CASE: inflated. Mark (tagged pointer) points to an objectMonitor. + // The Object:ObjectMonitor relationship is stable as long as we're + // not at a safepoint. + if (mark->has_monitor()) { + void * owner = mark->monitor()->_owner ; + if (owner == NULL) return owner_none ; + return (owner == self || + self->is_lock_owned((address)owner)) ? owner_self : owner_other; + } + + // CASE: neutral + assert(mark->is_neutral(), "sanity check"); + return owner_none ; // it's unlocked +} + +// FIXME: jvmti should call this +JavaThread* ObjectSynchronizer::get_lock_owner(Handle h_obj, bool doLock) { + if (UseBiasedLocking) { + if (SafepointSynchronize::is_at_safepoint()) { + BiasedLocking::revoke_at_safepoint(h_obj); + } else { + BiasedLocking::revoke_and_rebias(h_obj, false, JavaThread::current()); + } + assert(!h_obj->mark()->has_bias_pattern(), "biases should be revoked by now"); + } + + oop obj = h_obj(); + address owner = NULL; + + markOop mark = ReadStableMark (obj) ; + + // Uncontended case, header points to stack + if (mark->has_locker()) { + owner = (address) mark->locker(); + } + + // Contended case, header points to ObjectMonitor (tagged pointer) + if (mark->has_monitor()) { + ObjectMonitor* monitor = mark->monitor(); + assert(monitor != NULL, "monitor should be non-null"); + owner = (address) monitor->owner(); + } + + if (owner != NULL) { + return Threads::owning_thread_from_monitor_owner(owner, doLock); + } + + // Unlocked case, header in place + // Cannot have assertion since this object may have been + // locked by another thread when reaching here. + // assert(mark->is_neutral(), "sanity check"); + + return NULL; +} + +// Iterate through monitor cache and attempt to release thread's monitors +// Gives up on a particular monitor if an exception occurs, but continues +// the overall iteration, swallowing the exception. +class ReleaseJavaMonitorsClosure: public MonitorClosure { +private: + TRAPS; + +public: + ReleaseJavaMonitorsClosure(Thread* thread) : THREAD(thread) {} + void do_monitor(ObjectMonitor* mid) { + if (mid->owner() == THREAD) { + (void)mid->complete_exit(CHECK); + } + } +}; + +// Release all inflated monitors owned by THREAD. Lightweight monitors are +// ignored. This is meant to be called during JNI thread detach which assumes +// all remaining monitors are heavyweight. All exceptions are swallowed. +// Scanning the extant monitor list can be time consuming. +// A simple optimization is to add a per-thread flag that indicates a thread +// called jni_monitorenter() during its lifetime. +// +// Instead of No_Savepoint_Verifier it might be cheaper to +// use an idiom of the form: +// auto int tmp = SafepointSynchronize::_safepoint_counter ; +// +// guarantee (((tmp ^ _safepoint_counter) | (tmp & 1)) == 0) ; +// Since the tests are extremely cheap we could leave them enabled +// for normal product builds. + +void ObjectSynchronizer::release_monitors_owned_by_thread(TRAPS) { + assert(THREAD == JavaThread::current(), "must be current Java thread"); + No_Safepoint_Verifier nsv ; + ReleaseJavaMonitorsClosure rjmc(THREAD); + Thread::muxAcquire(&ListLock, "release_monitors_owned_by_thread"); + ObjectSynchronizer::monitors_iterate(&rjmc); + Thread::muxRelease(&ListLock); + THREAD->clear_pending_exception(); +} + +// Visitors ... + +void ObjectSynchronizer::monitors_iterate(MonitorClosure* closure) { + ObjectMonitor* block = gBlockList; + ObjectMonitor* mid; + while (block) { + assert(block->object() == CHAINMARKER, "must be a block header"); + for (int i = _BLOCKSIZE - 1; i > 0; i--) { + mid = block + i; + oop object = (oop) mid->object(); + if (object != NULL) { + closure->do_monitor(mid); + } + } + block = (ObjectMonitor*) block->FreeNext; + } +} + +void ObjectSynchronizer::oops_do(OopClosure* f) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + for (ObjectMonitor* block = gBlockList; block != NULL; block = next(block)) { + assert(block->object() == CHAINMARKER, "must be a block header"); + for (int i = 1; i < _BLOCKSIZE; i++) { + ObjectMonitor* mid = &block[i]; + if (mid->object() != NULL) { + f->do_oop((oop*)mid->object_addr()); + } + } + } +} + +// Deflate_idle_monitors() is called at all safepoints, immediately +// after all mutators are stopped, but before any objects have moved. +// It traverses the list of known monitors, deflating where possible. +// The scavenged monitor are returned to the monitor free list. +// +// Beware that we scavenge at *every* stop-the-world point. +// Having a large number of monitors in-circulation negatively +// impacts the performance of some applications (e.g., PointBase). +// Broadly, we want to minimize the # of monitors in circulation. +// Alternately, we could partition the active monitors into sub-lists +// of those that need scanning and those that do not. +// Specifically, we would add a new sub-list of objectmonitors +// that are in-circulation and potentially active. deflate_idle_monitors() +// would scan only that list. Other monitors could reside on a quiescent +// list. Such sequestered monitors wouldn't need to be scanned by +// deflate_idle_monitors(). omAlloc() would first check the global free list, +// then the quiescent list, and, failing those, would allocate a new block. +// Deflate_idle_monitors() would scavenge and move monitors to the +// quiescent list. +// +// Perversely, the heap size -- and thus the STW safepoint rate -- +// typically drives the scavenge rate. Large heaps can mean infrequent GC, +// which in turn can mean large(r) numbers of objectmonitors in circulation. +// This is an unfortunate aspect of this design. +// +// Another refinement would be to refrain from calling deflate_idle_monitors() +// except at stop-the-world points associated with garbage collections. +// +// An even better solution would be to deflate on-the-fly, aggressively, +// at monitorexit-time as is done in EVM's metalock or Relaxed Locks. + +void ObjectSynchronizer::deflate_idle_monitors() { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + int nInuse = 0 ; // currently associated with objects + int nInCirculation = 0 ; // extant + int nScavenged = 0 ; // reclaimed + + ObjectMonitor * FreeHead = NULL ; // Local SLL of scavenged monitors + ObjectMonitor * FreeTail = NULL ; + + // Iterate over all extant monitors - Scavenge all idle monitors. + TEVENT (deflate_idle_monitors) ; + for (ObjectMonitor* block = gBlockList; block != NULL; block = next(block)) { + assert(block->object() == CHAINMARKER, "must be a block header"); + nInCirculation += _BLOCKSIZE ; + for (int i = 1 ; i < _BLOCKSIZE; i++) { + ObjectMonitor* mid = &block[i]; + oop obj = (oop) mid->object(); + + if (obj == NULL) { + // The monitor is not associated with an object. + // The monitor should either be a thread-specific private + // free list or the global free list. + // obj == NULL IMPLIES mid->is_busy() == 0 + guarantee (!mid->is_busy(), "invariant") ; + continue ; + } + + // Normal case ... The monitor is associated with obj. + guarantee (obj->mark() == markOopDesc::encode(mid), "invariant") ; + guarantee (mid == obj->mark()->monitor(), "invariant"); + guarantee (mid->header()->is_neutral(), "invariant"); + + if (mid->is_busy()) { + if (ClearResponsibleAtSTW) mid->_Responsible = NULL ; + nInuse ++ ; + } else { + // Deflate the monitor if it is no longer being used + // It's idle - scavenge and return to the global free list + // plain old deflation ... + TEVENT (deflate_idle_monitors - scavenge1) ; + if (TraceMonitorInflation) { + if (obj->is_instance()) { + ResourceMark rm; + tty->print_cr("Deflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s", + (intptr_t) obj, (intptr_t) obj->mark(), Klass::cast(obj->klass())->external_name()); + } + } + + // Restore the header back to obj + obj->release_set_mark(mid->header()); + mid->clear(); + + assert (mid->object() == NULL, "invariant") ; + + // Move the object to the working free list defined by FreeHead,FreeTail. + mid->FreeNext = NULL ; + if (FreeHead == NULL) FreeHead = mid ; + if (FreeTail != NULL) FreeTail->FreeNext = mid ; + FreeTail = mid ; + nScavenged ++ ; + } + } + } + + // Move the scavenged monitors back to the global free list. + // In theory we don't need the freelist lock as we're at a STW safepoint. + // omAlloc() and omFree() can only be called while a thread is _not in safepoint state. + // But it's remotely possible that omFlush() or release_monitors_owned_by_thread() + // might be called while not at a global STW safepoint. In the interest of + // safety we protect the following access with ListLock. + // An even more conservative and prudent approach would be to guard + // the main loop in scavenge_idle_monitors() with ListLock. + if (FreeHead != NULL) { + guarantee (FreeTail != NULL && nScavenged > 0, "invariant") ; + assert (FreeTail->FreeNext == NULL, "invariant") ; + // constant-time list splice - prepend scavenged segment to gFreeList + Thread::muxAcquire (&ListLock, "scavenge - return") ; + FreeTail->FreeNext = gFreeList ; + gFreeList = FreeHead ; + Thread::muxRelease (&ListLock) ; + } + + if (_sync_Deflations != NULL) _sync_Deflations->inc(nScavenged) ; + if (_sync_MonExtant != NULL) _sync_MonExtant ->set_value(nInCirculation); + + // TODO: Add objectMonitor leak detection. + // Audit/inventory the objectMonitors -- make sure they're all accounted for. + GVars.stwRandom = os::random() ; + GVars.stwCycle ++ ; +} + +// A macro is used below because there may already be a pending +// exception which should not abort the execution of the routines +// which use this (which is why we don't put this into check_slow and +// call it with a CHECK argument). + +#define CHECK_OWNER() \ + do { \ + if (THREAD != _owner) { \ + if (THREAD->is_lock_owned((address) _owner)) { \ + _owner = THREAD ; /* Convert from basiclock addr to Thread addr */ \ + _recursions = 0; \ + OwnerIsThread = 1 ; \ + } else { \ + TEVENT (Throw IMSX) ; \ + THROW(vmSymbols::java_lang_IllegalMonitorStateException()); \ + } \ + } \ + } while (false) + +// TODO-FIXME: eliminate ObjectWaiters. Replace this visitor/enumerator +// interface with a simple FirstWaitingThread(), NextWaitingThread() interface. + +ObjectWaiter* ObjectMonitor::first_waiter() { + return _WaitSet; +} + +ObjectWaiter* ObjectMonitor::next_waiter(ObjectWaiter* o) { + return o->_next; +} + +Thread* ObjectMonitor::thread_of_waiter(ObjectWaiter* o) { + return o->_thread; +} + +// initialize the monitor, exception the semaphore, all other fields +// are simple integers or pointers +ObjectMonitor::ObjectMonitor() { + _header = NULL; + _count = 0; + _waiters = 0, + _recursions = 0; + _object = NULL; + _owner = NULL; + _WaitSet = NULL; + _WaitSetLock = 0 ; + _Responsible = NULL ; + _succ = NULL ; + _cxq = NULL ; + FreeNext = NULL ; + _EntryList = NULL ; + _SpinFreq = 0 ; + _SpinClock = 0 ; + OwnerIsThread = 0 ; +} + +ObjectMonitor::~ObjectMonitor() { + // TODO: Add asserts ... + // _cxq == 0 _succ == NULL _owner == NULL _waiters == 0 + // _count == 0 _EntryList == NULL etc +} + +intptr_t ObjectMonitor::is_busy() const { + // TODO-FIXME: merge _count and _waiters. + // TODO-FIXME: assert _owner == null implies _recursions = 0 + // TODO-FIXME: assert _WaitSet != null implies _count > 0 + return _count|_waiters|intptr_t(_owner)|intptr_t(_cxq)|intptr_t(_EntryList ) ; +} + +void ObjectMonitor::Recycle () { + // TODO: add stronger asserts ... + // _cxq == 0 _succ == NULL _owner == NULL _waiters == 0 + // _count == 0 EntryList == NULL + // _recursions == 0 _WaitSet == NULL + // TODO: assert (is_busy()|_recursions) == 0 + _succ = NULL ; + _EntryList = NULL ; + _cxq = NULL ; + _WaitSet = NULL ; + _recursions = 0 ; + _SpinFreq = 0 ; + _SpinClock = 0 ; + OwnerIsThread = 0 ; +} + +// WaitSet management ... + +inline void ObjectMonitor::AddWaiter(ObjectWaiter* node) { + assert(node != NULL, "should not dequeue NULL node"); + assert(node->_prev == NULL, "node already in list"); + assert(node->_next == NULL, "node already in list"); + // put node at end of queue (circular doubly linked list) + if (_WaitSet == NULL) { + _WaitSet = node; + node->_prev = node; + node->_next = node; + } else { + ObjectWaiter* head = _WaitSet ; + ObjectWaiter* tail = head->_prev; + assert(tail->_next == head, "invariant check"); + tail->_next = node; + head->_prev = node; + node->_next = head; + node->_prev = tail; + } +} + +inline ObjectWaiter* ObjectMonitor::DequeueWaiter() { + // dequeue the very first waiter + ObjectWaiter* waiter = _WaitSet; + if (waiter) { + DequeueSpecificWaiter(waiter); + } + return waiter; +} + +inline void ObjectMonitor::DequeueSpecificWaiter(ObjectWaiter* node) { + assert(node != NULL, "should not dequeue NULL node"); + assert(node->_prev != NULL, "node already removed from list"); + assert(node->_next != NULL, "node already removed from list"); + // when the waiter has woken up because of interrupt, + // timeout or other spurious wake-up, dequeue the + // waiter from waiting list + ObjectWaiter* next = node->_next; + if (next == node) { + assert(node->_prev == node, "invariant check"); + _WaitSet = NULL; + } else { + ObjectWaiter* prev = node->_prev; + assert(prev->_next == node, "invariant check"); + assert(next->_prev == node, "invariant check"); + next->_prev = prev; + prev->_next = next; + if (_WaitSet == node) { + _WaitSet = next; + } + } + node->_next = NULL; + node->_prev = NULL; +} + +static char * kvGet (char * kvList, const char * Key) { + if (kvList == NULL) return NULL ; + size_t n = strlen (Key) ; + char * Search ; + for (Search = kvList ; *Search ; Search += strlen(Search) + 1) { + if (strncmp (Search, Key, n) == 0) { + if (Search[n] == '=') return Search + n + 1 ; + if (Search[n] == 0) return (char *) "1" ; + } + } + return NULL ; +} + +static int kvGetInt (char * kvList, const char * Key, int Default) { + char * v = kvGet (kvList, Key) ; + int rslt = v ? ::strtol (v, NULL, 0) : Default ; + if (Knob_ReportSettings && v != NULL) { + ::printf (" SyncKnob: %s %d(%d)\n", Key, rslt, Default) ; + ::fflush (stdout) ; + } + return rslt ; +} + +// By convention we unlink a contending thread from EntryList|cxq immediately +// after the thread acquires the lock in ::enter(). Equally, we could defer +// unlinking the thread until ::exit()-time. + +void ObjectMonitor::UnlinkAfterAcquire (Thread * Self, ObjectWaiter * SelfNode) +{ + assert (_owner == Self, "invariant") ; + assert (SelfNode->_thread == Self, "invariant") ; + + if (SelfNode->TState == ObjectWaiter::TS_ENTER) { + // Normal case: remove Self from the DLL EntryList . + // This is a constant-time operation. + ObjectWaiter * nxt = SelfNode->_next ; + ObjectWaiter * prv = SelfNode->_prev ; + if (nxt != NULL) nxt->_prev = prv ; + if (prv != NULL) prv->_next = nxt ; + if (SelfNode == _EntryList ) _EntryList = nxt ; + assert (nxt == NULL || nxt->TState == ObjectWaiter::TS_ENTER, "invariant") ; + assert (prv == NULL || prv->TState == ObjectWaiter::TS_ENTER, "invariant") ; + TEVENT (Unlink from EntryList) ; + } else { + guarantee (SelfNode->TState == ObjectWaiter::TS_CXQ, "invariant") ; + // Inopportune interleaving -- Self is still on the cxq. + // This usually means the enqueue of self raced an exiting thread. + // Normally we'll find Self near the front of the cxq, so + // dequeueing is typically fast. If needbe we can accelerate + // this with some MCS/CHL-like bidirectional list hints and advisory + // back-links so dequeueing from the interior will normally operate + // in constant-time. + // Dequeue Self from either the head (with CAS) or from the interior + // with a linear-time scan and normal non-atomic memory operations. + // CONSIDER: if Self is on the cxq then simply drain cxq into EntryList + // and then unlink Self from EntryList. We have to drain eventually, + // so it might as well be now. + + ObjectWaiter * v = _cxq ; + assert (v != NULL, "invariant") ; + if (v != SelfNode || Atomic::cmpxchg_ptr (SelfNode->_next, &_cxq, v) != v) { + // The CAS above can fail from interference IFF a "RAT" arrived. + // In that case Self must be in the interior and can no longer be + // at the head of cxq. + if (v == SelfNode) { + assert (_cxq != v, "invariant") ; + v = _cxq ; // CAS above failed - start scan at head of list + } + ObjectWaiter * p ; + ObjectWaiter * q = NULL ; + for (p = v ; p != NULL && p != SelfNode; p = p->_next) { + q = p ; + assert (p->TState == ObjectWaiter::TS_CXQ, "invariant") ; + } + assert (v != SelfNode, "invariant") ; + assert (p == SelfNode, "Node not found on cxq") ; + assert (p != _cxq, "invariant") ; + assert (q != NULL, "invariant") ; + assert (q->_next == p, "invariant") ; + q->_next = p->_next ; + } + TEVENT (Unlink from cxq) ; + } + + // Diagnostic hygiene ... + SelfNode->_prev = (ObjectWaiter *) 0xBAD ; + SelfNode->_next = (ObjectWaiter *) 0xBAD ; + SelfNode->TState = ObjectWaiter::TS_RUN ; +} + +// Caveat: TryLock() is not necessarily serializing if it returns failure. +// Callers must compensate as needed. + +int ObjectMonitor::TryLock (Thread * Self) { + for (;;) { + void * own = _owner ; + if (own != NULL) return 0 ; + if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) { + // Either guarantee _recursions == 0 or set _recursions = 0. + assert (_recursions == 0, "invariant") ; + assert (_owner == Self, "invariant") ; + // CONSIDER: set or assert that OwnerIsThread == 1 + return 1 ; + } + // The lock had been free momentarily, but we lost the race to the lock. + // Interference -- the CAS failed. + // We can either return -1 or retry. + // Retry doesn't make as much sense because the lock was just acquired. + if (true) return -1 ; + } +} + +// NotRunnable() -- informed spinning +// +// Don't bother spinning if the owner is not eligible to drop the lock. +// Peek at the owner's schedctl.sc_state and Thread._thread_values and +// spin only if the owner thread is _thread_in_Java or _thread_in_vm. +// The thread must be runnable in order to drop the lock in timely fashion. +// If the _owner is not runnable then spinning will not likely be +// successful (profitable). +// +// Beware -- the thread referenced by _owner could have died +// so a simply fetch from _owner->_thread_state might trap. +// Instead, we use SafeFetchXX() to safely LD _owner->_thread_state. +// Because of the lifecycle issues the schedctl and _thread_state values +// observed by NotRunnable() might be garbage. NotRunnable must +// tolerate this and consider the observed _thread_state value +// as advisory. +// +// Beware too, that _owner is sometimes a BasicLock address and sometimes +// a thread pointer. We differentiate the two cases with OwnerIsThread. +// Alternately, we might tag the type (thread pointer vs basiclock pointer) +// with the LSB of _owner. Another option would be to probablistically probe +// the putative _owner->TypeTag value. +// +// Checking _thread_state isn't perfect. Even if the thread is +// in_java it might be blocked on a page-fault or have been preempted +// and sitting on a ready/dispatch queue. _thread state in conjunction +// with schedctl.sc_state gives us a good picture of what the +// thread is doing, however. +// +// TODO: check schedctl.sc_state. +// We'll need to use SafeFetch32() to read from the schedctl block. +// See RFE #5004247 and http://sac.sfbay.sun.com/Archives/CaseLog/arc/PSARC/2005/351/ +// +// The return value from NotRunnable() is *advisory* -- the +// result is based on sampling and is not necessarily coherent. +// The caller must tolerate false-negative and false-positive errors. +// Spinning, in general, is probabilistic anyway. + + +int ObjectMonitor::NotRunnable (Thread * Self, Thread * ox) { + // Check either OwnerIsThread or ox->TypeTag == 2BAD. + if (!OwnerIsThread) return 0 ; + + if (ox == NULL) return 0 ; + + // Avoid transitive spinning ... + // Say T1 spins or blocks trying to acquire L. T1._Stalled is set to L. + // Immediately after T1 acquires L it's possible that T2, also + // spinning on L, will see L.Owner=T1 and T1._Stalled=L. + // This occurs transiently after T1 acquired L but before + // T1 managed to clear T1.Stalled. T2 does not need to abort + // its spin in this circumstance. + intptr_t BlockedOn = SafeFetchN ((intptr_t *) &ox->_Stalled, intptr_t(1)) ; + + if (BlockedOn == 1) return 1 ; + if (BlockedOn != 0) { + return BlockedOn != intptr_t(this) && _owner == ox ; + } + + assert (sizeof(((JavaThread *)ox)->_thread_state == sizeof(int)), "invariant") ; + int jst = SafeFetch32 ((int *) &((JavaThread *) ox)->_thread_state, -1) ; ; + // consider also: jst != _thread_in_Java -- but that's overspecific. + return jst == _thread_blocked || jst == _thread_in_native ; +} + + +// Adaptive spin-then-block - rational spinning +// +// Note that we spin "globally" on _owner with a classic SMP-polite TATAS +// algorithm. On high order SMP systems it would be better to start with +// a brief global spin and then revert to spinning locally. In the spirit of MCS/CLH, +// a contending thread could enqueue itself on the cxq and then spin locally +// on a thread-specific variable such as its ParkEvent._Event flag. +// That's left as an exercise for the reader. Note that global spinning is +// not problematic on Niagara, as the L2$ serves the interconnect and has both +// low latency and massive bandwidth. +// +// Broadly, we can fix the spin frequency -- that is, the % of contended lock +// acquisition attempts where we opt to spin -- at 100% and vary the spin count +// (duration) or we can fix the count at approximately the duration of +// a context switch and vary the frequency. Of course we could also +// vary both satisfying K == Frequency * Duration, where K is adaptive by monitor. +// See http://j2se.east/~dice/PERSIST/040824-AdaptiveSpinning.html. +// +// This implementation varies the duration "D", where D varies with +// the success rate of recent spin attempts. (D is capped at approximately +// length of a round-trip context switch). The success rate for recent +// spin attempts is a good predictor of the success rate of future spin +// attempts. The mechanism adapts automatically to varying critical +// section length (lock modality), system load and degree of parallelism. +// D is maintained per-monitor in _SpinDuration and is initialized +// optimistically. Spin frequency is fixed at 100%. +// +// Note that _SpinDuration is volatile, but we update it without locks +// or atomics. The code is designed so that _SpinDuration stays within +// a reasonable range even in the presence of races. The arithmetic +// operations on _SpinDuration are closed over the domain of legal values, +// so at worst a race will install and older but still legal value. +// At the very worst this introduces some apparent non-determinism. +// We might spin when we shouldn't or vice-versa, but since the spin +// count are relatively short, even in the worst case, the effect is harmless. +// +// Care must be taken that a low "D" value does not become an +// an absorbing state. Transient spinning failures -- when spinning +// is overall profitable -- should not cause the system to converge +// on low "D" values. We want spinning to be stable and predictable +// and fairly responsive to change and at the same time we don't want +// it to oscillate, become metastable, be "too" non-deterministic, +// or converge on or enter undesirable stable absorbing states. +// +// We implement a feedback-based control system -- using past behavior +// to predict future behavior. We face two issues: (a) if the +// input signal is random then the spin predictor won't provide optimal +// results, and (b) if the signal frequency is too high then the control +// system, which has some natural response lag, will "chase" the signal. +// (b) can arise from multimodal lock hold times. Transient preemption +// can also result in apparent bimodal lock hold times. +// Although sub-optimal, neither condition is particularly harmful, as +// in the worst-case we'll spin when we shouldn't or vice-versa. +// The maximum spin duration is rather short so the failure modes aren't bad. +// To be conservative, I've tuned the gain in system to bias toward +// _not spinning. Relatedly, the system can sometimes enter a mode where it +// "rings" or oscillates between spinning and not spinning. This happens +// when spinning is just on the cusp of profitability, however, so the +// situation is not dire. The state is benign -- there's no need to add +// hysteresis control to damp the transition rate between spinning and +// not spinning. +// +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// Spin-then-block strategies ... +// +// Thoughts on ways to improve spinning : +// +// * Periodically call {psr_}getloadavg() while spinning, and +// permit unbounded spinning if the load average is < +// the number of processors. Beware, however, that getloadavg() +// is exceptionally fast on solaris (about 1/10 the cost of a full +// spin cycle, but quite expensive on linux. Beware also, that +// multiple JVMs could "ring" or oscillate in a feedback loop. +// Sufficient damping would solve that problem. +// +// * We currently use spin loops with iteration counters to approximate +// spinning for some interval. Given the availability of high-precision +// time sources such as gethrtime(), %TICK, %STICK, RDTSC, etc., we should +// someday reimplement the spin loops to duration-based instead of iteration-based. +// +// * Don't spin if there are more than N = (CPUs/2) threads +// currently spinning on the monitor (or globally). +// That is, limit the number of concurrent spinners. +// We might also limit the # of spinners in the JVM, globally. +// +// * If a spinning thread observes _owner change hands it should +// abort the spin (and park immediately) or at least debit +// the spin counter by a large "penalty". +// +// * Classically, the spin count is either K*(CPUs-1) or is a +// simple constant that approximates the length of a context switch. +// We currently use a value -- computed by a special utility -- that +// approximates round-trip context switch times. +// +// * Normally schedctl_start()/_stop() is used to advise the kernel +// to avoid preempting threads that are running in short, bounded +// critical sections. We could use the schedctl hooks in an inverted +// sense -- spinners would set the nopreempt flag, but poll the preempt +// pending flag. If a spinner observed a pending preemption it'd immediately +// abort the spin and park. As such, the schedctl service acts as +// a preemption warning mechanism. +// +// * In lieu of spinning, if the system is running below saturation +// (that is, loadavg() << #cpus), we can instead suppress futile +// wakeup throttling, or even wake more than one successor at exit-time. +// The net effect is largely equivalent to spinning. In both cases, +// contending threads go ONPROC and opportunistically attempt to acquire +// the lock, decreasing lock handover latency at the expense of wasted +// cycles and context switching. +// +// * We might to spin less after we've parked as the thread will +// have less $ and TLB affinity with the processor. +// Likewise, we might spin less if we come ONPROC on a different +// processor or after a long period (>> rechose_interval). +// +// * A table-driven state machine similar to Solaris' dispadmin scheduling +// tables might be a better design. Instead of encoding information in +// _SpinDuration, _SpinFreq and _SpinClock we'd just use explicit, +// discrete states. Success or failure during a spin would drive +// state transitions, and each state node would contain a spin count. +// +// * If the processor is operating in a mode intended to conserve power +// (such as Intel's SpeedStep) or to reduce thermal output (thermal +// step-down mode) then the Java synchronization subsystem should +// forgo spinning. +// +// * The minimum spin duration should be approximately the worst-case +// store propagation latency on the platform. That is, the time +// it takes a store on CPU A to become visible on CPU B, where A and +// B are "distant". +// +// * We might want to factor a thread's priority in the spin policy. +// Threads with a higher priority might spin for slightly longer. +// Similarly, if we use back-off in the TATAS loop, lower priority +// threads might back-off longer. We don't currently use a +// thread's priority when placing it on the entry queue. We may +// want to consider doing so in future releases. +// +// * We might transiently drop a thread's scheduling priority while it spins. +// SCHED_BATCH on linux and FX scheduling class at priority=0 on Solaris +// would suffice. We could even consider letting the thread spin indefinitely at +// a depressed or "idle" priority. This brings up fairness issues, however -- +// in a saturated system a thread would with a reduced priority could languish +// for extended periods on the ready queue. +// +// * While spinning try to use the otherwise wasted time to help the VM make +// progress: +// +// -- YieldTo() the owner, if the owner is OFFPROC but ready +// Done our remaining quantum directly to the ready thread. +// This helps "push" the lock owner through the critical section. +// It also tends to improve affinity/locality as the lock +// "migrates" less frequently between CPUs. +// -- Walk our own stack in anticipation of blocking. Memoize the roots. +// -- Perform strand checking for other thread. Unpark potential strandees. +// -- Help GC: trace or mark -- this would need to be a bounded unit of work. +// Unfortunately this will pollute our $ and TLBs. Recall that we +// spin to avoid context switching -- context switching has an +// immediate cost in latency, a disruptive cost to other strands on a CMT +// processor, and an amortized cost because of the D$ and TLB cache +// reload transient when the thread comes back ONPROC and repopulates +// $s and TLBs. +// -- call getloadavg() to see if the system is saturated. It'd probably +// make sense to call getloadavg() half way through the spin. +// If the system isn't at full capacity the we'd simply reset +// the spin counter to and extend the spin attempt. +// -- Doug points out that we should use the same "helping" policy +// in thread.yield(). +// +// * Try MONITOR-MWAIT on systems that support those instructions. +// +// * The spin statistics that drive spin decisions & frequency are +// maintained in the objectmonitor structure so if we deflate and reinflate +// we lose spin state. In practice this is not usually a concern +// as the default spin state after inflation is aggressive (optimistic) +// and tends toward spinning. So in the worst case for a lock where +// spinning is not profitable we may spin unnecessarily for a brief +// period. But then again, if a lock is contended it'll tend not to deflate +// in the first place. + + +intptr_t ObjectMonitor::SpinCallbackArgument = 0 ; +int (*ObjectMonitor::SpinCallbackFunction)(intptr_t, int) = NULL ; + +// Spinning: Fixed frequency (100%), vary duration + +int ObjectMonitor::TrySpin_VaryDuration (Thread * Self) { + + // Dumb, brutal spin. Good for comparative measurements against adaptive spinning. + int ctr = Knob_FixedSpin ; + if (ctr != 0) { + while (--ctr >= 0) { + if (TryLock (Self) > 0) return 1 ; + SpinPause () ; + } + return 0 ; + } + + for (ctr = Knob_PreSpin + 1; --ctr >= 0 ; ) { + if (TryLock(Self) > 0) { + // Increase _SpinDuration ... + // Note that we don't clamp SpinDuration precisely at SpinLimit. + // Raising _SpurDuration to the poverty line is key. + int x = _SpinDuration ; + if (x < Knob_SpinLimit) { + if (x < Knob_Poverty) x = Knob_Poverty ; + _SpinDuration = x + Knob_BonusB ; + } + return 1 ; + } + SpinPause () ; + } + + // Admission control - verify preconditions for spinning + // + // We always spin a little bit, just to prevent _SpinDuration == 0 from + // becoming an absorbing state. Put another way, we spin briefly to + // sample, just in case the system load, parallelism, contention, or lock + // modality changed. + // + // Consider the following alternative: + // Periodically set _SpinDuration = _SpinLimit and try a long/full + // spin attempt. "Periodically" might mean after a tally of + // the # of failed spin attempts (or iterations) reaches some threshold. + // This takes us into the realm of 1-out-of-N spinning, where we + // hold the duration constant but vary the frequency. + + ctr = _SpinDuration ; + if (ctr < Knob_SpinBase) ctr = Knob_SpinBase ; + if (ctr <= 0) return 0 ; + + if (Knob_SuccRestrict && _succ != NULL) return 0 ; + if (Knob_OState && NotRunnable (Self, (Thread *) _owner)) { + TEVENT (Spin abort - notrunnable [TOP]); + return 0 ; + } + + int MaxSpin = Knob_MaxSpinners ; + if (MaxSpin >= 0) { + if (_Spinner > MaxSpin) { + TEVENT (Spin abort -- too many spinners) ; + return 0 ; + } + // Slighty racy, but benign ... + Adjust (&_Spinner, 1) ; + } + + // We're good to spin ... spin ingress. + // CONSIDER: use Prefetch::write() to avoid RTS->RTO upgrades + // when preparing to LD...CAS _owner, etc and the CAS is likely + // to succeed. + int hits = 0 ; + int msk = 0 ; + int caspty = Knob_CASPenalty ; + int oxpty = Knob_OXPenalty ; + int sss = Knob_SpinSetSucc ; + if (sss && _succ == NULL ) _succ = Self ; + Thread * prv = NULL ; + + // There are three ways to exit the following loop: + // 1. A successful spin where this thread has acquired the lock. + // 2. Spin failure with prejudice + // 3. Spin failure without prejudice + + while (--ctr >= 0) { + + // Periodic polling -- Check for pending GC + // Threads may spin while they're unsafe. + // We don't want spinning threads to delay the JVM from reaching + // a stop-the-world safepoint or to steal cycles from GC. + // If we detect a pending safepoint we abort in order that + // (a) this thread, if unsafe, doesn't delay the safepoint, and (b) + // this thread, if safe, doesn't steal cycles from GC. + // This is in keeping with the "no loitering in runtime" rule. + // We periodically check to see if there's a safepoint pending. + if ((ctr & 0xFF) == 0) { + if (SafepointSynchronize::do_call_back()) { + TEVENT (Spin: safepoint) ; + goto Abort ; // abrupt spin egress + } + if (Knob_UsePause & 1) SpinPause () ; + + int (*scb)(intptr_t,int) = SpinCallbackFunction ; + if (hits > 50 && scb != NULL) { + int abend = (*scb)(SpinCallbackArgument, 0) ; + } + } + + if (Knob_UsePause & 2) SpinPause() ; + + // Exponential back-off ... Stay off the bus to reduce coherency traffic. + // This is useful on classic SMP systems, but is of less utility on + // N1-style CMT platforms. + // + // Trade-off: lock acquisition latency vs coherency bandwidth. + // Lock hold times are typically short. A histogram + // of successful spin attempts shows that we usually acquire + // the lock early in the spin. That suggests we want to + // sample _owner frequently in the early phase of the spin, + // but then back-off and sample less frequently as the spin + // progresses. The back-off makes a good citizen on SMP big + // SMP systems. Oversampling _owner can consume excessive + // coherency bandwidth. Relatedly, if we _oversample _owner we + // can inadvertently interfere with the the ST m->owner=null. + // executed by the lock owner. + if (ctr & msk) continue ; + ++hits ; + if ((hits & 0xF) == 0) { + // The 0xF, above, corresponds to the exponent. + // Consider: (msk+1)|msk + msk = ((msk << 2)|3) & BackOffMask ; + } + + // Probe _owner with TATAS + // If this thread observes the monitor transition or flicker + // from locked to unlocked to locked, then the odds that this + // thread will acquire the lock in this spin attempt go down + // considerably. The same argument applies if the CAS fails + // or if we observe _owner change from one non-null value to + // another non-null value. In such cases we might abort + // the spin without prejudice or apply a "penalty" to the + // spin count-down variable "ctr", reducing it by 100, say. + + Thread * ox = (Thread *) _owner ; + if (ox == NULL) { + ox = (Thread *) Atomic::cmpxchg_ptr (Self, &_owner, NULL) ; + if (ox == NULL) { + // The CAS succeeded -- this thread acquired ownership + // Take care of some bookkeeping to exit spin state. + if (sss && _succ == Self) { + _succ = NULL ; + } + if (MaxSpin > 0) Adjust (&_Spinner, -1) ; + + // Increase _SpinDuration : + // The spin was successful (profitable) so we tend toward + // longer spin attempts in the future. + // CONSIDER: factor "ctr" into the _SpinDuration adjustment. + // If we acquired the lock early in the spin cycle it + // makes sense to increase _SpinDuration proportionally. + // Note that we don't clamp SpinDuration precisely at SpinLimit. + int x = _SpinDuration ; + if (x < Knob_SpinLimit) { + if (x < Knob_Poverty) x = Knob_Poverty ; + _SpinDuration = x + Knob_Bonus ; + } + return 1 ; + } + + // The CAS failed ... we can take any of the following actions: + // * penalize: ctr -= Knob_CASPenalty + // * exit spin with prejudice -- goto Abort; + // * exit spin without prejudice. + // * Since CAS is high-latency, retry again immediately. + prv = ox ; + TEVENT (Spin: cas failed) ; + if (caspty == -2) break ; + if (caspty == -1) goto Abort ; + ctr -= caspty ; + continue ; + } + + // Did lock ownership change hands ? + if (ox != prv && prv != NULL ) { + TEVENT (spin: Owner changed) + if (oxpty == -2) break ; + if (oxpty == -1) goto Abort ; + ctr -= oxpty ; + } + prv = ox ; + + // Abort the spin if the owner is not executing. + // The owner must be executing in order to drop the lock. + // Spinning while the owner is OFFPROC is idiocy. + // Consider: ctr -= RunnablePenalty ; + if (Knob_OState && NotRunnable (Self, ox)) { + TEVENT (Spin abort - notrunnable); + goto Abort ; + } + if (sss && _succ == NULL ) _succ = Self ; + } + + // Spin failed with prejudice -- reduce _SpinDuration. + // TODO: Use an AIMD-like policy to adjust _SpinDuration. + // AIMD is globally stable. + TEVENT (Spin failure) ; + { + int x = _SpinDuration ; + if (x > 0) { + // Consider an AIMD scheme like: x -= (x >> 3) + 100 + // This is globally sample and tends to damp the response. + x -= Knob_Penalty ; + if (x < 0) x = 0 ; + _SpinDuration = x ; + } + } + + Abort: + if (MaxSpin >= 0) Adjust (&_Spinner, -1) ; + if (sss && _succ == Self) { + _succ = NULL ; + // Invariant: after setting succ=null a contending thread + // must recheck-retry _owner before parking. This usually happens + // in the normal usage of TrySpin(), but it's safest + // to make TrySpin() as foolproof as possible. + OrderAccess::fence() ; + if (TryLock(Self) > 0) return 1 ; + } + return 0 ; +} + +#define TrySpin TrySpin_VaryDuration + +static void DeferredInitialize () { + if (InitDone > 0) return ; + if (Atomic::cmpxchg (-1, &InitDone, 0) != 0) { + while (InitDone != 1) ; + return ; + } + + // One-shot global initialization ... + // The initialization is idempotent, so we don't need locks. + // In the future consider doing this via os::init_2(). + // SyncKnobs consist of = pairs in the style + // of environment variables. Start by converting ':' to NUL. + + if (SyncKnobs == NULL) SyncKnobs = "" ; + + size_t sz = strlen (SyncKnobs) ; + char * knobs = (char *) malloc (sz + 2) ; + if (knobs == NULL) { + vm_exit_out_of_memory (sz + 2, "Parse SyncKnobs") ; + guarantee (0, "invariant") ; + } + strcpy (knobs, SyncKnobs) ; + knobs[sz+1] = 0 ; + for (char * p = knobs ; *p ; p++) { + if (*p == ':') *p = 0 ; + } + + #define SETKNOB(x) { Knob_##x = kvGetInt (knobs, #x, Knob_##x); } + SETKNOB(ReportSettings) ; + SETKNOB(Verbose) ; + SETKNOB(FixedSpin) ; + SETKNOB(SpinLimit) ; + SETKNOB(SpinBase) ; + SETKNOB(SpinBackOff); + SETKNOB(CASPenalty) ; + SETKNOB(OXPenalty) ; + SETKNOB(LogSpins) ; + SETKNOB(SpinSetSucc) ; + SETKNOB(SuccEnabled) ; + SETKNOB(SuccRestrict) ; + SETKNOB(Penalty) ; + SETKNOB(Bonus) ; + SETKNOB(BonusB) ; + SETKNOB(Poverty) ; + SETKNOB(SpinAfterFutile) ; + SETKNOB(UsePause) ; + SETKNOB(SpinEarly) ; + SETKNOB(OState) ; + SETKNOB(MaxSpinners) ; + SETKNOB(PreSpin) ; + SETKNOB(ExitPolicy) ; + SETKNOB(QMode); + SETKNOB(ResetEvent) ; + SETKNOB(MoveNotifyee) ; + SETKNOB(FastHSSEC) ; + #undef SETKNOB + + if (os::is_MP()) { + BackOffMask = (1 << Knob_SpinBackOff) - 1 ; + if (Knob_ReportSettings) ::printf ("BackOffMask=%X\n", BackOffMask) ; + // CONSIDER: BackOffMask = ROUNDUP_NEXT_POWER2 (ncpus-1) + } else { + Knob_SpinLimit = 0 ; + Knob_SpinBase = 0 ; + Knob_PreSpin = 0 ; + Knob_FixedSpin = -1 ; + } + + if (Knob_LogSpins == 0) { + ObjectSynchronizer::_sync_FailedSpins = NULL ; + } + + free (knobs) ; + OrderAccess::fence() ; + InitDone = 1 ; +} + +// Theory of operations -- Monitors lists, thread residency, etc: +// +// * A thread acquires ownership of a monitor by successfully +// CAS()ing the _owner field from null to non-null. +// +// * Invariant: A thread appears on at most one monitor list -- +// cxq, EntryList or WaitSet -- at any one time. +// +// * Contending threads "push" themselves onto the cxq with CAS +// and then spin/park. +// +// * After a contending thread eventually acquires the lock it must +// dequeue itself from either the EntryList or the cxq. +// +// * The exiting thread identifies and unparks an "heir presumptive" +// tentative successor thread on the EntryList. Critically, the +// exiting thread doesn't unlink the successor thread from the EntryList. +// After having been unparked, the wakee will recontend for ownership of +// the monitor. The successor (wakee) will either acquire the lock or +// re-park itself. +// +// Succession is provided for by a policy of competitive handoff. +// The exiting thread does _not_ grant or pass ownership to the +// successor thread. (This is also referred to as "handoff" succession"). +// Instead the exiting thread releases ownership and possibly wakes +// a successor, so the successor can (re)compete for ownership of the lock. +// If the EntryList is empty but the cxq is populated the exiting +// thread will drain the cxq into the EntryList. It does so by +// by detaching the cxq (installing null with CAS) and folding +// the threads from the cxq into the EntryList. The EntryList is +// doubly linked, while the cxq is singly linked because of the +// CAS-based "push" used to enqueue recently arrived threads (RATs). +// +// * Concurrency invariants: +// +// -- only the monitor owner may access or mutate the EntryList. +// The mutex property of the monitor itself protects the EntryList +// from concurrent interference. +// -- Only the monitor owner may detach the cxq. +// +// * The monitor entry list operations avoid locks, but strictly speaking +// they're not lock-free. Enter is lock-free, exit is not. +// See http://j2se.east/~dice/PERSIST/040825-LockFreeQueues.html +// +// * The cxq can have multiple concurrent "pushers" but only one concurrent +// detaching thread. This mechanism is immune from the ABA corruption. +// More precisely, the CAS-based "push" onto cxq is ABA-oblivious. +// +// * Taken together, the cxq and the EntryList constitute or form a +// single logical queue of threads stalled trying to acquire the lock. +// We use two distinct lists to improve the odds of a constant-time +// dequeue operation after acquisition (in the ::enter() epilog) and +// to reduce heat on the list ends. (c.f. Michael Scott's "2Q" algorithm). +// A key desideratum is to minimize queue & monitor metadata manipulation +// that occurs while holding the monitor lock -- that is, we want to +// minimize monitor lock holds times. Note that even a small amount of +// fixed spinning will greatly reduce the # of enqueue-dequeue operations +// on EntryList|cxq. That is, spinning relieves contention on the "inner" +// locks and monitor metadata. +// +// Cxq points to the the set of Recently Arrived Threads attempting entry. +// Because we push threads onto _cxq with CAS, the RATs must take the form of +// a singly-linked LIFO. We drain _cxq into EntryList at unlock-time when +// the unlocking thread notices that EntryList is null but _cxq is != null. +// +// The EntryList is ordered by the prevailing queue discipline and +// can be organized in any convenient fashion, such as a doubly-linked list or +// a circular doubly-linked list. Critically, we want insert and delete operations +// to operate in constant-time. If we need a priority queue then something akin +// to Solaris' sleepq would work nicely. Viz., +// http://agg.eng/ws/on10_nightly/source/usr/src/uts/common/os/sleepq.c. +// Queue discipline is enforced at ::exit() time, when the unlocking thread +// drains the cxq into the EntryList, and orders or reorders the threads on the +// EntryList accordingly. +// +// Barring "lock barging", this mechanism provides fair cyclic ordering, +// somewhat similar to an elevator-scan. +// +// * The monitor synchronization subsystem avoids the use of native +// synchronization primitives except for the narrow platform-specific +// park-unpark abstraction. See the comments in os_solaris.cpp regarding +// the semantics of park-unpark. Put another way, this monitor implementation +// depends only on atomic operations and park-unpark. The monitor subsystem +// manages all RUNNING->BLOCKED and BLOCKED->READY transitions while the +// underlying OS manages the READY<->RUN transitions. +// +// * Waiting threads reside on the WaitSet list -- wait() puts +// the caller onto the WaitSet. +// +// * notify() or notifyAll() simply transfers threads from the WaitSet to +// either the EntryList or cxq. Subsequent exit() operations will +// unpark the notifyee. Unparking a notifee in notify() is inefficient - +// it's likely the notifyee would simply impale itself on the lock held +// by the notifier. +// +// * An interesting alternative is to encode cxq as (List,LockByte) where +// the LockByte is 0 iff the monitor is owned. _owner is simply an auxiliary +// variable, like _recursions, in the scheme. The threads or Events that form +// the list would have to be aligned in 256-byte addresses. A thread would +// try to acquire the lock or enqueue itself with CAS, but exiting threads +// could use a 1-0 protocol and simply STB to set the LockByte to 0. +// Note that is is *not* word-tearing, but it does presume that full-word +// CAS operations are coherent with intermix with STB operations. That's true +// on most common processors. +// +// * See also http://blogs.sun.com/dave + + +void ATTR ObjectMonitor::EnterI (TRAPS) { + Thread * Self = THREAD ; + assert (Self->is_Java_thread(), "invariant") ; + assert (((JavaThread *) Self)->thread_state() == _thread_blocked , "invariant") ; + + // Try the lock - TATAS + if (TryLock (Self) > 0) { + assert (_succ != Self , "invariant") ; + assert (_owner == Self , "invariant") ; + assert (_Responsible != Self , "invariant") ; + return ; + } + + DeferredInitialize () ; + + // We try one round of spinning *before* enqueueing Self. + // + // If the _owner is ready but OFFPROC we could use a YieldTo() + // operation to donate the remainder of this thread's quantum + // to the owner. This has subtle but beneficial affinity + // effects. + + if (TrySpin (Self) > 0) { + assert (_owner == Self , "invariant") ; + assert (_succ != Self , "invariant") ; + assert (_Responsible != Self , "invariant") ; + return ; + } + + // The Spin failed -- Enqueue and park the thread ... + assert (_succ != Self , "invariant") ; + assert (_owner != Self , "invariant") ; + assert (_Responsible != Self , "invariant") ; + + // Enqueue "Self" on ObjectMonitor's _cxq. + // + // Node acts as a proxy for Self. + // As an aside, if were to ever rewrite the synchronization code mostly + // in Java, WaitNodes, ObjectMonitors, and Events would become 1st-class + // Java objects. This would avoid awkward lifecycle and liveness issues, + // as well as eliminate a subset of ABA issues. + // TODO: eliminate ObjectWaiter and enqueue either Threads or Events. + // + + ObjectWaiter node(Self) ; + Self->_ParkEvent->reset() ; + node._prev = (ObjectWaiter *) 0xBAD ; + node.TState = ObjectWaiter::TS_CXQ ; + + // Push "Self" onto the front of the _cxq. + // Once on cxq/EntryList, Self stays on-queue until it acquires the lock. + // Note that spinning tends to reduce the rate at which threads + // enqueue and dequeue on EntryList|cxq. + ObjectWaiter * nxt ; + for (;;) { + node._next = nxt = _cxq ; + if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ; + + // Interference - the CAS failed because _cxq changed. Just retry. + // As an optional optimization we retry the lock. + if (TryLock (Self) > 0) { + assert (_succ != Self , "invariant") ; + assert (_owner == Self , "invariant") ; + assert (_Responsible != Self , "invariant") ; + return ; + } + } + + // Check for cxq|EntryList edge transition to non-null. This indicates + // the onset of contention. While contention persists exiting threads + // will use a ST:MEMBAR:LD 1-1 exit protocol. When contention abates exit + // operations revert to the faster 1-0 mode. This enter operation may interleave + // (race) a concurrent 1-0 exit operation, resulting in stranding, so we + // arrange for one of the contending thread to use a timed park() operations + // to detect and recover from the race. (Stranding is form of progress failure + // where the monitor is unlocked but all the contending threads remain parked). + // That is, at least one of the contended threads will periodically poll _owner. + // One of the contending threads will become the designated "Responsible" thread. + // The Responsible thread uses a timed park instead of a normal indefinite park + // operation -- it periodically wakes and checks for and recovers from potential + // strandings admitted by 1-0 exit operations. We need at most one Responsible + // thread per-monitor at any given moment. Only threads on cxq|EntryList may + // be responsible for a monitor. + // + // Currently, one of the contended threads takes on the added role of "Responsible". + // A viable alternative would be to use a dedicated "stranding checker" thread + // that periodically iterated over all the threads (or active monitors) and unparked + // successors where there was risk of stranding. This would help eliminate the + // timer scalability issues we see on some platforms as we'd only have one thread + // -- the checker -- parked on a timer. + + if ((SyncFlags & 16) == 0 && nxt == NULL && _EntryList == NULL) { + // Try to assume the role of responsible thread for the monitor. + // CONSIDER: ST vs CAS vs { if (Responsible==null) Responsible=Self } + Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ; + } + + // The lock have been released while this thread was occupied queueing + // itself onto _cxq. To close the race and avoid "stranding" and + // progress-liveness failure we must resample-retry _owner before parking. + // Note the Dekker/Lamport duality: ST cxq; MEMBAR; LD Owner. + // In this case the ST-MEMBAR is accomplished with CAS(). + // + // TODO: Defer all thread state transitions until park-time. + // Since state transitions are heavy and inefficient we'd like + // to defer the state transitions until absolutely necessary, + // and in doing so avoid some transitions ... + + TEVENT (Inflated enter - Contention) ; + int nWakeups = 0 ; + int RecheckInterval = 1 ; + + for (;;) { + + if (TryLock (Self) > 0) break ; + assert (_owner != Self, "invariant") ; + + if ((SyncFlags & 2) && _Responsible == NULL) { + Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ; + } + + // park self + if (_Responsible == Self || (SyncFlags & 1)) { + TEVENT (Inflated enter - park TIMED) ; + Self->_ParkEvent->park ((jlong) RecheckInterval) ; + // Increase the RecheckInterval, but clamp the value. + RecheckInterval *= 8 ; + if (RecheckInterval > 1000) RecheckInterval = 1000 ; + } else { + TEVENT (Inflated enter - park UNTIMED) ; + Self->_ParkEvent->park() ; + } + + if (TryLock(Self) > 0) break ; + + // The lock is still contested. + // Keep a tally of the # of futile wakeups. + // Note that the counter is not protected by a lock or updated by atomics. + // That is by design - we trade "lossy" counters which are exposed to + // races during updates for a lower probe effect. + TEVENT (Inflated enter - Futile wakeup) ; + if (ObjectSynchronizer::_sync_FutileWakeups != NULL) { + ObjectSynchronizer::_sync_FutileWakeups->inc() ; + } + ++ nWakeups ; + + // Assuming this is not a spurious wakeup we'll normally find _succ == Self. + // We can defer clearing _succ until after the spin completes + // TrySpin() must tolerate being called with _succ == Self. + // Try yet another round of adaptive spinning. + if ((Knob_SpinAfterFutile & 1) && TrySpin (Self) > 0) break ; + + // We can find that we were unpark()ed and redesignated _succ while + // we were spinning. That's harmless. If we iterate and call park(), + // park() will consume the event and return immediately and we'll + // just spin again. This pattern can repeat, leaving _succ to simply + // spin on a CPU. Enable Knob_ResetEvent to clear pending unparks(). + // Alternately, we can sample fired() here, and if set, forgo spinning + // in the next iteration. + + if ((Knob_ResetEvent & 1) && Self->_ParkEvent->fired()) { + Self->_ParkEvent->reset() ; + OrderAccess::fence() ; + } + if (_succ == Self) _succ = NULL ; + + // Invariant: after clearing _succ a thread *must* retry _owner before parking. + OrderAccess::fence() ; + } + + // Egress : + // Self has acquired the lock -- Unlink Self from the cxq or EntryList. + // Normally we'll find Self on the EntryList . + // From the perspective of the lock owner (this thread), the + // EntryList is stable and cxq is prepend-only. + // The head of cxq is volatile but the interior is stable. + // In addition, Self.TState is stable. + + assert (_owner == Self , "invariant") ; + assert (object() != NULL , "invariant") ; + // I'd like to write: + // guarantee (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ; + // but as we're at a safepoint that's not safe. + + UnlinkAfterAcquire (Self, &node) ; + if (_succ == Self) _succ = NULL ; + + assert (_succ != Self, "invariant") ; + if (_Responsible == Self) { + _Responsible = NULL ; + // Dekker pivot-point. + // Consider OrderAccess::storeload() here + + // We may leave threads on cxq|EntryList without a designated + // "Responsible" thread. This is benign. When this thread subsequently + // exits the monitor it can "see" such preexisting "old" threads -- + // threads that arrived on the cxq|EntryList before the fence, above -- + // by LDing cxq|EntryList. Newly arrived threads -- that is, threads + // that arrive on cxq after the ST:MEMBAR, above -- will set Responsible + // non-null and elect a new "Responsible" timer thread. + // + // This thread executes: + // ST Responsible=null; MEMBAR (in enter epilog - here) + // LD cxq|EntryList (in subsequent exit) + // + // Entering threads in the slow/contended path execute: + // ST cxq=nonnull; MEMBAR; LD Responsible (in enter prolog) + // The (ST cxq; MEMBAR) is accomplished with CAS(). + // + // The MEMBAR, above, prevents the LD of cxq|EntryList in the subsequent + // exit operation from floating above the ST Responsible=null. + // + // In *practice* however, EnterI() is always followed by some atomic + // operation such as the decrement of _count in ::enter(). Those atomics + // obviate the need for the explicit MEMBAR, above. + } + + // We've acquired ownership with CAS(). + // CAS is serializing -- it has MEMBAR/FENCE-equivalent semantics. + // But since the CAS() this thread may have also stored into _succ, + // EntryList, cxq or Responsible. These meta-data updates must be + // visible __before this thread subsequently drops the lock. + // Consider what could occur if we didn't enforce this constraint -- + // STs to monitor meta-data and user-data could reorder with (become + // visible after) the ST in exit that drops ownership of the lock. + // Some other thread could then acquire the lock, but observe inconsistent + // or old monitor meta-data and heap data. That violates the JMM. + // To that end, the 1-0 exit() operation must have at least STST|LDST + // "release" barrier semantics. Specifically, there must be at least a + // STST|LDST barrier in exit() before the ST of null into _owner that drops + // the lock. The barrier ensures that changes to monitor meta-data and data + // protected by the lock will be visible before we release the lock, and + // therefore before some other thread (CPU) has a chance to acquire the lock. + // See also: http://gee.cs.oswego.edu/dl/jmm/cookbook.html. + // + // Critically, any prior STs to _succ or EntryList must be visible before + // the ST of null into _owner in the *subsequent* (following) corresponding + // monitorexit. Recall too, that in 1-0 mode monitorexit does not necessarily + // execute a serializing instruction. + + if (SyncFlags & 8) { + OrderAccess::fence() ; + } + return ; +} + +// ExitSuspendEquivalent: +// A faster alternate to handle_special_suspend_equivalent_condition() +// +// handle_special_suspend_equivalent_condition() unconditionally +// acquires the SR_lock. On some platforms uncontended MutexLocker() +// operations have high latency. Note that in ::enter() we call HSSEC +// while holding the monitor, so we effectively lengthen the critical sections. +// +// There are a number of possible solutions: +// +// A. To ameliorate the problem we might also defer state transitions +// to as late as possible -- just prior to parking. +// Given that, we'd call HSSEC after having returned from park(), +// but before attempting to acquire the monitor. This is only a +// partial solution. It avoids calling HSSEC while holding the +// monitor (good), but it still increases successor reacquisition latency -- +// the interval between unparking a successor and the time the successor +// resumes and retries the lock. See ReenterI(), which defers state transitions. +// If we use this technique we can also avoid EnterI()-exit() loop +// in ::enter() where we iteratively drop the lock and then attempt +// to reacquire it after suspending. +// +// B. In the future we might fold all the suspend bits into a +// composite per-thread suspend flag and then update it with CAS(). +// Alternately, a Dekker-like mechanism with multiple variables +// would suffice: +// ST Self->_suspend_equivalent = false +// MEMBAR +// LD Self_>_suspend_flags +// + + +bool ObjectMonitor::ExitSuspendEquivalent (JavaThread * jSelf) { + int Mode = Knob_FastHSSEC ; + if (Mode && !jSelf->is_external_suspend()) { + assert (jSelf->is_suspend_equivalent(), "invariant") ; + jSelf->clear_suspend_equivalent() ; + if (2 == Mode) OrderAccess::storeload() ; + if (!jSelf->is_external_suspend()) return false ; + // We raced a suspension -- fall thru into the slow path + TEVENT (ExitSuspendEquivalent - raced) ; + jSelf->set_suspend_equivalent() ; + } + return jSelf->handle_special_suspend_equivalent_condition() ; +} + + +// ReenterI() is a specialized inline form of the latter half of the +// contended slow-path from EnterI(). We use ReenterI() only for +// monitor reentry in wait(). +// +// In the future we should reconcile EnterI() and ReenterI(), adding +// Knob_Reset and Knob_SpinAfterFutile support and restructuring the +// loop accordingly. + +void ATTR ObjectMonitor::ReenterI (Thread * Self, ObjectWaiter * SelfNode) { + assert (Self != NULL , "invariant") ; + assert (SelfNode != NULL , "invariant") ; + assert (SelfNode->_thread == Self , "invariant") ; + assert (_waiters > 0 , "invariant") ; + assert (((oop)(object()))->mark() == markOopDesc::encode(this) , "invariant") ; + assert (((JavaThread *)Self)->thread_state() != _thread_blocked, "invariant") ; + JavaThread * jt = (JavaThread *) Self ; + + int nWakeups = 0 ; + for (;;) { + ObjectWaiter::TStates v = SelfNode->TState ; + guarantee (v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant") ; + assert (_owner != Self, "invariant") ; + + if (TryLock (Self) > 0) break ; + if (TrySpin (Self) > 0) break ; + + TEVENT (Wait Reentry - parking) ; + + // State transition wrappers around park() ... + // ReenterI() wisely defers state transitions until + // it's clear we must park the thread. + { + OSThreadContendState osts(Self->osthread()); + ThreadBlockInVM tbivm(jt); + + // cleared by handle_special_suspend_equivalent_condition() + // or java_suspend_self() + jt->set_suspend_equivalent(); + if (SyncFlags & 1) { + Self->_ParkEvent->park ((jlong)1000) ; + } else { + Self->_ParkEvent->park () ; + } + + // were we externally suspended while we were waiting? + for (;;) { + if (!ExitSuspendEquivalent (jt)) break ; + if (_succ == Self) { _succ = NULL; OrderAccess::fence(); } + jt->java_suspend_self(); + jt->set_suspend_equivalent(); + } + } + + // Try again, but just so we distinguish between futile wakeups and + // successful wakeups. The following test isn't algorithmically + // necessary, but it helps us maintain sensible statistics. + if (TryLock(Self) > 0) break ; + + // The lock is still contested. + // Keep a tally of the # of futile wakeups. + // Note that the counter is not protected by a lock or updated by atomics. + // That is by design - we trade "lossy" counters which are exposed to + // races during updates for a lower probe effect. + TEVENT (Wait Reentry - futile wakeup) ; + ++ nWakeups ; + + // Assuming this is not a spurious wakeup we'll normally + // find that _succ == Self. + if (_succ == Self) _succ = NULL ; + + // Invariant: after clearing _succ a contending thread + // *must* retry _owner before parking. + OrderAccess::fence() ; + + if (ObjectSynchronizer::_sync_FutileWakeups != NULL) { + ObjectSynchronizer::_sync_FutileWakeups->inc() ; + } + } + + // Self has acquired the lock -- Unlink Self from the cxq or EntryList . + // Normally we'll find Self on the EntryList. + // Unlinking from the EntryList is constant-time and atomic-free. + // From the perspective of the lock owner (this thread), the + // EntryList is stable and cxq is prepend-only. + // The head of cxq is volatile but the interior is stable. + // In addition, Self.TState is stable. + + assert (_owner == Self, "invariant") ; + assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ; + UnlinkAfterAcquire (Self, SelfNode) ; + if (_succ == Self) _succ = NULL ; + assert (_succ != Self, "invariant") ; + SelfNode->TState = ObjectWaiter::TS_RUN ; + OrderAccess::fence() ; // see comments at the end of EnterI() +} + +bool ObjectMonitor::try_enter(Thread* THREAD) { + if (THREAD != _owner) { + if (THREAD->is_lock_owned ((address)_owner)) { + assert(_recursions == 0, "internal state error"); + _owner = THREAD ; + _recursions = 1 ; + OwnerIsThread = 1 ; + return true; + } + if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) { + return false; + } + return true; + } else { + _recursions++; + return true; + } +} + +void ATTR ObjectMonitor::enter(TRAPS) { + // The following code is ordered to check the most common cases first + // and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors. + Thread * const Self = THREAD ; + void * cur ; + + cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ; + if (cur == NULL) { + // Either ASSERT _recursions == 0 or explicitly set _recursions = 0. + assert (_recursions == 0 , "invariant") ; + assert (_owner == Self, "invariant") ; + // CONSIDER: set or assert OwnerIsThread == 1 + return ; + } + + if (cur == Self) { + // TODO-FIXME: check for integer overflow! BUGID 6557169. + _recursions ++ ; + return ; + } + + if (Self->is_lock_owned ((address)cur)) { + assert (_recursions == 0, "internal state error"); + _recursions = 1 ; + // Commute owner from a thread-specific on-stack BasicLockObject address to + // a full-fledged "Thread *". + _owner = Self ; + OwnerIsThread = 1 ; + return ; + } + + // We've encountered genuine contention. + assert (Self->_Stalled == 0, "invariant") ; + Self->_Stalled = intptr_t(this) ; + + // Try one round of spinning *before* enqueueing Self + // and before going through the awkward and expensive state + // transitions. The following spin is strictly optional ... + // Note that if we acquire the monitor from an initial spin + // we forgo posting JVMTI events and firing DTRACE probes. + if (Knob_SpinEarly && TrySpin (Self) > 0) { + assert (_owner == Self , "invariant") ; + assert (_recursions == 0 , "invariant") ; + assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ; + Self->_Stalled = 0 ; + return ; + } + + assert (_owner != Self , "invariant") ; + assert (_succ != Self , "invariant") ; + assert (Self->is_Java_thread() , "invariant") ; + JavaThread * jt = (JavaThread *) Self ; + assert (!SafepointSynchronize::is_at_safepoint(), "invariant") ; + assert (jt->thread_state() != _thread_blocked , "invariant") ; + assert (this->object() != NULL , "invariant") ; + assert (_count >= 0, "invariant") ; + + // Prevent deflation at STW-time. See deflate_idle_monitors() and is_busy(). + // Ensure the object-monitor relationship remains stable while there's contention. + Atomic::inc_ptr(&_count); + + { // Change java thread status to indicate blocked on monitor enter. + JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this); + + DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt); + if (JvmtiExport::should_post_monitor_contended_enter()) { + JvmtiExport::post_monitor_contended_enter(jt, this); + } + + OSThreadContendState osts(Self->osthread()); + ThreadBlockInVM tbivm(jt); + + Self->set_current_pending_monitor(this); + + // TODO-FIXME: change the following for(;;) loop to straight-line code. + for (;;) { + jt->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() + // or java_suspend_self() + + EnterI (THREAD) ; + + if (!ExitSuspendEquivalent(jt)) break ; + + // + // We have acquired the contended monitor, but while we were + // waiting another thread suspended us. We don't want to enter + // the monitor while suspended because that would surprise the + // thread that suspended us. + // + _recursions = 0 ; + _succ = NULL ; + exit (Self) ; + + jt->java_suspend_self(); + } + Self->set_current_pending_monitor(NULL); + } + + Atomic::dec_ptr(&_count); + assert (_count >= 0, "invariant") ; + Self->_Stalled = 0 ; + + // Must either set _recursions = 0 or ASSERT _recursions == 0. + assert (_recursions == 0 , "invariant") ; + assert (_owner == Self , "invariant") ; + assert (_succ != Self , "invariant") ; + assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ; + + // The thread -- now the owner -- is back in vm mode. + // Report the glorious news via TI,DTrace and jvmstat. + // The probe effect is non-trivial. All the reportage occurs + // while we hold the monitor, increasing the length of the critical + // section. Amdahl's parallel speedup law comes vividly into play. + // + // Another option might be to aggregate the events (thread local or + // per-monitor aggregation) and defer reporting until a more opportune + // time -- such as next time some thread encounters contention but has + // yet to acquire the lock. While spinning that thread could + // spinning we could increment JVMStat counters, etc. + + DTRACE_MONITOR_PROBE(contended__entered, this, object(), jt); + if (JvmtiExport::should_post_monitor_contended_entered()) { + JvmtiExport::post_monitor_contended_entered(jt, this); + } + if (ObjectSynchronizer::_sync_ContendedLockAttempts != NULL) { + ObjectSynchronizer::_sync_ContendedLockAttempts->inc() ; + } +} + +void ObjectMonitor::ExitEpilog (Thread * Self, ObjectWaiter * Wakee) { + assert (_owner == Self, "invariant") ; + + // Exit protocol: + // 1. ST _succ = wakee + // 2. membar #loadstore|#storestore; + // 2. ST _owner = NULL + // 3. unpark(wakee) + + _succ = Knob_SuccEnabled ? Wakee->_thread : NULL ; + ParkEvent * Trigger = Wakee->_event ; + + // Hygiene -- once we've set _owner = NULL we can't safely dereference Wakee again. + // The thread associated with Wakee may have grabbed the lock and "Wakee" may be + // out-of-scope (non-extant). + Wakee = NULL ; + + // Drop the lock + OrderAccess::release_store_ptr (&_owner, NULL) ; + OrderAccess::fence() ; // ST _owner vs LD in unpark() + + // TODO-FIXME: + // If there's a safepoint pending the best policy would be to + // get _this thread to a safepoint and only wake the successor + // after the safepoint completed. monitorexit uses a "leaf" + // state transition, however, so this thread can't become + // safe at this point in time. (Its stack isn't walkable). + // The next best thing is to defer waking the successor by + // adding to a list of thread to be unparked after at the + // end of the forthcoming STW). + if (SafepointSynchronize::do_call_back()) { + TEVENT (unpark before SAFEPOINT) ; + } + + // Possible optimizations ... + // + // * Consider: set Wakee->UnparkTime = timeNow() + // When the thread wakes up it'll compute (timeNow() - Self->UnparkTime()). + // By measuring recent ONPROC latency we can approximate the + // system load. In turn, we can feed that information back + // into the spinning & succession policies. + // (ONPROC latency correlates strongly with load). + // + // * Pull affinity: + // If the wakee is cold then transiently setting it's affinity + // to the current CPU is a good idea. + // See http://j2se.east/~dice/PERSIST/050624-PullAffinity.txt + Trigger->unpark() ; + + // Maintain stats and report events to JVMTI + if (ObjectSynchronizer::_sync_Parks != NULL) { + ObjectSynchronizer::_sync_Parks->inc() ; + } + DTRACE_MONITOR_PROBE(contended__exit, this, object(), Self); +} + + +// exit() +// ~~~~~~ +// Note that the collector can't reclaim the objectMonitor or deflate +// the object out from underneath the thread calling ::exit() as the +// thread calling ::exit() never transitions to a stable state. +// This inhibits GC, which in turn inhibits asynchronous (and +// inopportune) reclamation of "this". +// +// We'd like to assert that: (THREAD->thread_state() != _thread_blocked) ; +// There's one exception to the claim above, however. EnterI() can call +// exit() to drop a lock if the acquirer has been externally suspended. +// In that case exit() is called with _thread_state as _thread_blocked, +// but the monitor's _count field is > 0, which inhibits reclamation. +// +// 1-0 exit +// ~~~~~~~~ +// ::exit() uses a canonical 1-1 idiom with a MEMBAR although some of +// the fast-path operators have been optimized so the common ::exit() +// operation is 1-0. See i486.ad fast_unlock(), for instance. +// The code emitted by fast_unlock() elides the usual MEMBAR. This +// greatly improves latency -- MEMBAR and CAS having considerable local +// latency on modern processors -- but at the cost of "stranding". Absent the +// MEMBAR, a thread in fast_unlock() can race a thread in the slow +// ::enter() path, resulting in the entering thread being stranding +// and a progress-liveness failure. Stranding is extremely rare. +// We use timers (timed park operations) & periodic polling to detect +// and recover from stranding. Potentially stranded threads periodically +// wake up and poll the lock. See the usage of the _Responsible variable. +// +// The CAS() in enter provides for safety and exclusion, while the CAS or +// MEMBAR in exit provides for progress and avoids stranding. 1-0 locking +// eliminates the CAS/MEMBAR from the exist path, but it admits stranding. +// We detect and recover from stranding with timers. +// +// If a thread transiently strands it'll park until (a) another +// thread acquires the lock and then drops the lock, at which time the +// exiting thread will notice and unpark the stranded thread, or, (b) +// the timer expires. If the lock is high traffic then the stranding latency +// will be low due to (a). If the lock is low traffic then the odds of +// stranding are lower, although the worst-case stranding latency +// is longer. Critically, we don't want to put excessive load in the +// platform's timer subsystem. We want to minimize both the timer injection +// rate (timers created/sec) as well as the number of timers active at +// any one time. (more precisely, we want to minimize timer-seconds, which is +// the integral of the # of active timers at any instant over time). +// Both impinge on OS scalability. Given that, at most one thread parked on +// a monitor will use a timer. + +void ATTR ObjectMonitor::exit(TRAPS) { + Thread * Self = THREAD ; + if (THREAD != _owner) { + if (THREAD->is_lock_owned((address) _owner)) { + // Transmute _owner from a BasicLock pointer to a Thread address. + // We don't need to hold _mutex for this transition. + // Non-null to Non-null is safe as long as all readers can + // tolerate either flavor. + assert (_recursions == 0, "invariant") ; + _owner = THREAD ; + _recursions = 0 ; + OwnerIsThread = 1 ; + } else { + // NOTE: we need to handle unbalanced monitor enter/exit + // in native code by throwing an exception. + // TODO: Throw an IllegalMonitorStateException ? + TEVENT (Exit - Throw IMSX) ; + assert(false, "Non-balanced monitor enter/exit!"); + if (false) { + THROW(vmSymbols::java_lang_IllegalMonitorStateException()); + } + return; + } + } + + if (_recursions != 0) { + _recursions--; // this is simple recursive enter + TEVENT (Inflated exit - recursive) ; + return ; + } + + // Invariant: after setting Responsible=null an thread must execute + // a MEMBAR or other serializing instruction before fetching EntryList|cxq. + if ((SyncFlags & 4) == 0) { + _Responsible = NULL ; + } + + for (;;) { + assert (THREAD == _owner, "invariant") ; + + // Fast-path monitor exit: + // + // Observe the Dekker/Lamport duality: + // A thread in ::exit() executes: + // ST Owner=null; MEMBAR; LD EntryList|cxq. + // A thread in the contended ::enter() path executes the complementary: + // ST EntryList|cxq = nonnull; MEMBAR; LD Owner. + // + // Note that there's a benign race in the exit path. We can drop the + // lock, another thread can reacquire the lock immediately, and we can + // then wake a thread unnecessarily (yet another flavor of futile wakeup). + // This is benign, and we've structured the code so the windows are short + // and the frequency of such futile wakeups is low. + // + // We could eliminate the race by encoding both the "LOCKED" state and + // the queue head in a single word. Exit would then use either CAS to + // clear the LOCKED bit/byte. This precludes the desirable 1-0 optimization, + // however. + // + // Possible fast-path ::exit() optimization: + // The current fast-path exit implementation fetches both cxq and EntryList. + // See also i486.ad fast_unlock(). Testing has shown that two LDs + // isn't measurably slower than a single LD on any platforms. + // Still, we could reduce the 2 LDs to one or zero by one of the following: + // + // - Use _count instead of cxq|EntryList + // We intend to eliminate _count, however, when we switch + // to on-the-fly deflation in ::exit() as is used in + // Metalocks and RelaxedLocks. + // + // - Establish the invariant that cxq == null implies EntryList == null. + // set cxq == EMPTY (1) to encode the state where cxq is empty + // by EntryList != null. EMPTY is a distinguished value. + // The fast-path exit() would fetch cxq but not EntryList. + // + // - Encode succ as follows: + // succ = t : Thread t is the successor -- t is ready or is spinning. + // Exiting thread does not need to wake a successor. + // succ = 0 : No successor required -> (EntryList|cxq) == null + // Exiting thread does not need to wake a successor + // succ = 1 : Successor required -> (EntryList|cxq) != null and + // logically succ == null. + // Exiting thread must wake a successor. + // + // The 1-1 fast-exit path would appear as : + // _owner = null ; membar ; + // if (_succ == 1 && CAS (&_owner, null, Self) == null) goto SlowPath + // goto FastPathDone ; + // + // and the 1-0 fast-exit path would appear as: + // if (_succ == 1) goto SlowPath + // Owner = null ; + // goto FastPathDone + // + // - Encode the LSB of _owner as 1 to indicate that exit() + // must use the slow-path and make a successor ready. + // (_owner & 1) == 0 IFF succ != null || (EntryList|cxq) == null + // (_owner & 1) == 0 IFF succ == null && (EntryList|cxq) != null (obviously) + // The 1-0 fast exit path would read: + // if (_owner != Self) goto SlowPath + // _owner = null + // goto FastPathDone + + if (Knob_ExitPolicy == 0) { + // release semantics: prior loads and stores from within the critical section + // must not float (reorder) past the following store that drops the lock. + // On SPARC that requires MEMBAR #loadstore|#storestore. + // But of course in TSO #loadstore|#storestore is not required. + // I'd like to write one of the following: + // A. OrderAccess::release() ; _owner = NULL + // B. OrderAccess::loadstore(); OrderAccess::storestore(); _owner = NULL; + // Unfortunately OrderAccess::release() and OrderAccess::loadstore() both + // store into a _dummy variable. That store is not needed, but can result + // in massive wasteful coherency traffic on classic SMP systems. + // Instead, I use release_store(), which is implemented as just a simple + // ST on x64, x86 and SPARC. + OrderAccess::release_store_ptr (&_owner, NULL) ; // drop the lock + OrderAccess::storeload() ; // See if we need to wake a successor + if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) { + TEVENT (Inflated exit - simple egress) ; + return ; + } + TEVENT (Inflated exit - complex egress) ; + + // Normally the exiting thread is responsible for ensuring succession, + // but if other successors are ready or other entering threads are spinning + // then this thread can simply store NULL into _owner and exit without + // waking a successor. The existence of spinners or ready successors + // guarantees proper succession (liveness). Responsibility passes to the + // ready or running successors. The exiting thread delegates the duty. + // More precisely, if a successor already exists this thread is absolved + // of the responsibility of waking (unparking) one. + // + // The _succ variable is critical to reducing futile wakeup frequency. + // _succ identifies the "heir presumptive" thread that has been made + // ready (unparked) but that has not yet run. We need only one such + // successor thread to guarantee progress. + // See http://www.usenix.org/events/jvm01/full_papers/dice/dice.pdf + // section 3.3 "Futile Wakeup Throttling" for details. + // + // Note that spinners in Enter() also set _succ non-null. + // In the current implementation spinners opportunistically set + // _succ so that exiting threads might avoid waking a successor. + // Another less appealing alternative would be for the exiting thread + // to drop the lock and then spin briefly to see if a spinner managed + // to acquire the lock. If so, the exiting thread could exit + // immediately without waking a successor, otherwise the exiting + // thread would need to dequeue and wake a successor. + // (Note that we'd need to make the post-drop spin short, but no + // shorter than the worst-case round-trip cache-line migration time. + // The dropped lock needs to become visible to the spinner, and then + // the acquisition of the lock by the spinner must become visible to + // the exiting thread). + // + + // It appears that an heir-presumptive (successor) must be made ready. + // Only the current lock owner can manipulate the EntryList or + // drain _cxq, so we need to reacquire the lock. If we fail + // to reacquire the lock the responsibility for ensuring succession + // falls to the new owner. + // + if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) { + return ; + } + TEVENT (Exit - Reacquired) ; + } else { + if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) { + OrderAccess::release_store_ptr (&_owner, NULL) ; // drop the lock + OrderAccess::storeload() ; + // Ratify the previously observed values. + if (_cxq == NULL || _succ != NULL) { + TEVENT (Inflated exit - simple egress) ; + return ; + } + + // inopportune interleaving -- the exiting thread (this thread) + // in the fast-exit path raced an entering thread in the slow-enter + // path. + // We have two choices: + // A. Try to reacquire the lock. + // If the CAS() fails return immediately, otherwise + // we either restart/rerun the exit operation, or simply + // fall-through into the code below which wakes a successor. + // B. If the elements forming the EntryList|cxq are TSM + // we could simply unpark() the lead thread and return + // without having set _succ. + if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) { + TEVENT (Inflated exit - reacquired succeeded) ; + return ; + } + TEVENT (Inflated exit - reacquired failed) ; + } else { + TEVENT (Inflated exit - complex egress) ; + } + } + + guarantee (_owner == THREAD, "invariant") ; + + // Select an appropriate successor ("heir presumptive") from the EntryList + // and make it ready. Generally we just wake the head of EntryList . + // There's no algorithmic constraint that we use the head - it's just + // a policy decision. Note that the thread at head of the EntryList + // remains at the head until it acquires the lock. This means we'll + // repeatedly wake the same thread until it manages to grab the lock. + // This is generally a good policy - if we're seeing lots of futile wakeups + // at least we're waking/rewaking a thread that's like to be hot or warm + // (have residual D$ and TLB affinity). + // + // "Wakeup locality" optimization: + // http://j2se.east/~dice/PERSIST/040825-WakeLocality.txt + // In the future we'll try to bias the selection mechanism + // to preferentially pick a thread that recently ran on + // a processor element that shares cache with the CPU on which + // the exiting thread is running. We need access to Solaris' + // schedctl.sc_cpu to make that work. + // + ObjectWaiter * w = NULL ; + int QMode = Knob_QMode ; + + if (QMode == 2 && _cxq != NULL) { + // QMode == 2 : cxq has precedence over EntryList. + // Try to directly wake a successor from the cxq. + // If successful, the successor will need to unlink itself from cxq. + w = _cxq ; + assert (w != NULL, "invariant") ; + assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ; + ExitEpilog (Self, w) ; + return ; + } + + if (QMode == 3 && _cxq != NULL) { + // Aggressively drain cxq into EntryList at the first opportunity. + // This policy ensure that recently-run threads live at the head of EntryList. + // Drain _cxq into EntryList - bulk transfer. + // First, detach _cxq. + // The following loop is tantamount to: w = swap (&cxq, NULL) + w = _cxq ; + for (;;) { + assert (w != NULL, "Invariant") ; + ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ; + if (u == w) break ; + w = u ; + } + assert (w != NULL , "invariant") ; + + ObjectWaiter * q = NULL ; + ObjectWaiter * p ; + for (p = w ; p != NULL ; p = p->_next) { + guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ; + p->TState = ObjectWaiter::TS_ENTER ; + p->_prev = q ; + q = p ; + } + + // Append the RATs to the EntryList + // TODO: organize EntryList as a CDLL so we can locate the tail in constant-time. + ObjectWaiter * Tail ; + for (Tail = _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail = Tail->_next) ; + if (Tail == NULL) { + _EntryList = w ; + } else { + Tail->_next = w ; + w->_prev = Tail ; + } + + // Fall thru into code that tries to wake a successor from EntryList + } + + if (QMode == 4 && _cxq != NULL) { + // Aggressively drain cxq into EntryList at the first opportunity. + // This policy ensure that recently-run threads live at the head of EntryList. + + // Drain _cxq into EntryList - bulk transfer. + // First, detach _cxq. + // The following loop is tantamount to: w = swap (&cxq, NULL) + w = _cxq ; + for (;;) { + assert (w != NULL, "Invariant") ; + ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ; + if (u == w) break ; + w = u ; + } + assert (w != NULL , "invariant") ; + + ObjectWaiter * q = NULL ; + ObjectWaiter * p ; + for (p = w ; p != NULL ; p = p->_next) { + guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ; + p->TState = ObjectWaiter::TS_ENTER ; + p->_prev = q ; + q = p ; + } + + // Prepend the RATs to the EntryList + if (_EntryList != NULL) { + q->_next = _EntryList ; + _EntryList->_prev = q ; + } + _EntryList = w ; + + // Fall thru into code that tries to wake a successor from EntryList + } + + w = _EntryList ; + if (w != NULL) { + // I'd like to write: guarantee (w->_thread != Self). + // But in practice an exiting thread may find itself on the EntryList. + // Lets say thread T1 calls O.wait(). Wait() enqueues T1 on O's waitset and + // then calls exit(). Exit release the lock by setting O._owner to NULL. + // Lets say T1 then stalls. T2 acquires O and calls O.notify(). The + // notify() operation moves T1 from O's waitset to O's EntryList. T2 then + // release the lock "O". T2 resumes immediately after the ST of null into + // _owner, above. T2 notices that the EntryList is populated, so it + // reacquires the lock and then finds itself on the EntryList. + // Given all that, we have to tolerate the circumstance where "w" is + // associated with Self. + assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ; + ExitEpilog (Self, w) ; + return ; + } + + // If we find that both _cxq and EntryList are null then just + // re-run the exit protocol from the top. + w = _cxq ; + if (w == NULL) continue ; + + // Drain _cxq into EntryList - bulk transfer. + // First, detach _cxq. + // The following loop is tantamount to: w = swap (&cxq, NULL) + for (;;) { + assert (w != NULL, "Invariant") ; + ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ; + if (u == w) break ; + w = u ; + } + TEVENT (Inflated exit - drain cxq into EntryList) ; + + assert (w != NULL , "invariant") ; + assert (_EntryList == NULL , "invariant") ; + + // Convert the LIFO SLL anchored by _cxq into a DLL. + // The list reorganization step operates in O(LENGTH(w)) time. + // It's critical that this step operate quickly as + // "Self" still holds the outer-lock, restricting parallelism + // and effectively lengthening the critical section. + // Invariant: s chases t chases u. + // TODO-FIXME: consider changing EntryList from a DLL to a CDLL so + // we have faster access to the tail. + + if (QMode == 1) { + // QMode == 1 : drain cxq to EntryList, reversing order + // We also reverse the order of the list. + ObjectWaiter * s = NULL ; + ObjectWaiter * t = w ; + ObjectWaiter * u = NULL ; + while (t != NULL) { + guarantee (t->TState == ObjectWaiter::TS_CXQ, "invariant") ; + t->TState = ObjectWaiter::TS_ENTER ; + u = t->_next ; + t->_prev = u ; + t->_next = s ; + s = t; + t = u ; + } + _EntryList = s ; + assert (s != NULL, "invariant") ; + } else { + // QMode == 0 or QMode == 2 + _EntryList = w ; + ObjectWaiter * q = NULL ; + ObjectWaiter * p ; + for (p = w ; p != NULL ; p = p->_next) { + guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ; + p->TState = ObjectWaiter::TS_ENTER ; + p->_prev = q ; + q = p ; + } + } + + // In 1-0 mode we need: ST EntryList; MEMBAR #storestore; ST _owner = NULL + // The MEMBAR is satisfied by the release_store() operation in ExitEpilog(). + + // See if we can abdicate to a spinner instead of waking a thread. + // A primary goal of the implementation is to reduce the + // context-switch rate. + if (_succ != NULL) continue; + + w = _EntryList ; + if (w != NULL) { + guarantee (w->TState == ObjectWaiter::TS_ENTER, "invariant") ; + ExitEpilog (Self, w) ; + return ; + } + } +} +// complete_exit exits a lock returning recursion count +// complete_exit/reenter operate as a wait without waiting +// complete_exit requires an inflated monitor +// The _owner field is not always the Thread addr even with an +// inflated monitor, e.g. the monitor can be inflated by a non-owning +// thread due to contention. +intptr_t ObjectMonitor::complete_exit(TRAPS) { + Thread * const Self = THREAD; + assert(Self->is_Java_thread(), "Must be Java thread!"); + JavaThread *jt = (JavaThread *)THREAD; + + DeferredInitialize(); + + if (THREAD != _owner) { + if (THREAD->is_lock_owned ((address)_owner)) { + assert(_recursions == 0, "internal state error"); + _owner = THREAD ; /* Convert from basiclock addr to Thread addr */ + _recursions = 0 ; + OwnerIsThread = 1 ; + } + } + + guarantee(Self == _owner, "complete_exit not owner"); + intptr_t save = _recursions; // record the old recursion count + _recursions = 0; // set the recursion level to be 0 + exit (Self) ; // exit the monitor + guarantee (_owner != Self, "invariant"); + return save; +} + +// reenter() enters a lock and sets recursion count +// complete_exit/reenter operate as a wait without waiting +void ObjectMonitor::reenter(intptr_t recursions, TRAPS) { + Thread * const Self = THREAD; + assert(Self->is_Java_thread(), "Must be Java thread!"); + JavaThread *jt = (JavaThread *)THREAD; + + guarantee(_owner != Self, "reenter already owner"); + enter (THREAD); // enter the monitor + guarantee (_recursions == 0, "reenter recursion"); + _recursions = recursions; + return; +} + +// Note: a subset of changes to ObjectMonitor::wait() +// will need to be replicated in complete_exit above +void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { + Thread * const Self = THREAD ; + assert(Self->is_Java_thread(), "Must be Java thread!"); + JavaThread *jt = (JavaThread *)THREAD; + + DeferredInitialize () ; + + // Throw IMSX or IEX. + CHECK_OWNER(); + + // check for a pending interrupt + if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) { + // post monitor waited event. Note that this is past-tense, we are done waiting. + if (JvmtiExport::should_post_monitor_waited()) { + // Note: 'false' parameter is passed here because the + // wait was not timed out due to thread interrupt. + JvmtiExport::post_monitor_waited(jt, this, false); + } + TEVENT (Wait - Throw IEX) ; + THROW(vmSymbols::java_lang_InterruptedException()); + return ; + } + TEVENT (Wait) ; + + assert (Self->_Stalled == 0, "invariant") ; + Self->_Stalled = intptr_t(this) ; + jt->set_current_waiting_monitor(this); + + // create a node to be put into the queue + // Critically, after we reset() the event but prior to park(), we must check + // for a pending interrupt. + ObjectWaiter node(Self); + node.TState = ObjectWaiter::TS_WAIT ; + Self->_ParkEvent->reset() ; + OrderAccess::fence(); // ST into Event; membar ; LD interrupted-flag + + // Enter the waiting queue, which is a circular doubly linked list in this case + // but it could be a priority queue or any data structure. + // _WaitSetLock protects the wait queue. Normally the wait queue is accessed only + // by the the owner of the monitor *except* in the case where park() + // returns because of a timeout of interrupt. Contention is exceptionally rare + // so we use a simple spin-lock instead of a heavier-weight blocking lock. + + Thread::SpinAcquire (&_WaitSetLock, "WaitSet - add") ; + AddWaiter (&node) ; + Thread::SpinRelease (&_WaitSetLock) ; + + if ((SyncFlags & 4) == 0) { + _Responsible = NULL ; + } + intptr_t save = _recursions; // record the old recursion count + _waiters++; // increment the number of waiters + _recursions = 0; // set the recursion level to be 1 + exit (Self) ; // exit the monitor + guarantee (_owner != Self, "invariant") ; + + // As soon as the ObjectMonitor's ownership is dropped in the exit() + // call above, another thread can enter() the ObjectMonitor, do the + // notify(), and exit() the ObjectMonitor. If the other thread's + // exit() call chooses this thread as the successor and the unpark() + // call happens to occur while this thread is posting a + // MONITOR_CONTENDED_EXIT event, then we run the risk of the event + // handler using RawMonitors and consuming the unpark(). + // + // To avoid the problem, we re-post the event. This does no harm + // even if the original unpark() was not consumed because we are the + // chosen successor for this monitor. + if (node._notified != 0 && _succ == Self) { + node._event->unpark(); + } + + // The thread is on the WaitSet list - now park() it. + // On MP systems it's conceivable that a brief spin before we park + // could be profitable. + // + // TODO-FIXME: change the following logic to a loop of the form + // while (!timeout && !interrupted && _notified == 0) park() + + int ret = OS_OK ; + int WasNotified = 0 ; + { // State transition wrappers + OSThread* osthread = Self->osthread(); + OSThreadWaitState osts(osthread, true); + { + ThreadBlockInVM tbivm(jt); + // Thread is in thread_blocked state and oop access is unsafe. + jt->set_suspend_equivalent(); + + if (interruptible && (Thread::is_interrupted(THREAD, false) || HAS_PENDING_EXCEPTION)) { + // Intentionally empty + } else + if (node._notified == 0) { + if (millis <= 0) { + Self->_ParkEvent->park () ; + } else { + ret = Self->_ParkEvent->park (millis) ; + } + } + + // were we externally suspended while we were waiting? + if (ExitSuspendEquivalent (jt)) { + // TODO-FIXME: add -- if succ == Self then succ = null. + jt->java_suspend_self(); + } + + } // Exit thread safepoint: transition _thread_blocked -> _thread_in_vm + + + // Node may be on the WaitSet, the EntryList (or cxq), or in transition + // from the WaitSet to the EntryList. + // See if we need to remove Node from the WaitSet. + // We use double-checked locking to avoid grabbing _WaitSetLock + // if the thread is not on the wait queue. + // + // Note that we don't need a fence before the fetch of TState. + // In the worst case we'll fetch a old-stale value of TS_WAIT previously + // written by the is thread. (perhaps the fetch might even be satisfied + // by a look-aside into the processor's own store buffer, although given + // the length of the code path between the prior ST and this load that's + // highly unlikely). If the following LD fetches a stale TS_WAIT value + // then we'll acquire the lock and then re-fetch a fresh TState value. + // That is, we fail toward safety. + + if (node.TState == ObjectWaiter::TS_WAIT) { + Thread::SpinAcquire (&_WaitSetLock, "WaitSet - unlink") ; + if (node.TState == ObjectWaiter::TS_WAIT) { + DequeueSpecificWaiter (&node) ; // unlink from WaitSet + assert(node._notified == 0, "invariant"); + node.TState = ObjectWaiter::TS_RUN ; + } + Thread::SpinRelease (&_WaitSetLock) ; + } + + // The thread is now either on off-list (TS_RUN), + // on the EntryList (TS_ENTER), or on the cxq (TS_CXQ). + // The Node's TState variable is stable from the perspective of this thread. + // No other threads will asynchronously modify TState. + guarantee (node.TState != ObjectWaiter::TS_WAIT, "invariant") ; + OrderAccess::loadload() ; + if (_succ == Self) _succ = NULL ; + WasNotified = node._notified ; + + // Reentry phase -- reacquire the monitor. + // re-enter contended monitor after object.wait(). + // retain OBJECT_WAIT state until re-enter successfully completes + // Thread state is thread_in_vm and oop access is again safe, + // although the raw address of the object may have changed. + // (Don't cache naked oops over safepoints, of course). + + // post monitor waited event. Note that this is past-tense, we are done waiting. + if (JvmtiExport::should_post_monitor_waited()) { + JvmtiExport::post_monitor_waited(jt, this, ret == OS_TIMEOUT); + } + OrderAccess::fence() ; + + assert (Self->_Stalled != 0, "invariant") ; + Self->_Stalled = 0 ; + + assert (_owner != Self, "invariant") ; + ObjectWaiter::TStates v = node.TState ; + if (v == ObjectWaiter::TS_RUN) { + enter (Self) ; + } else { + guarantee (v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant") ; + ReenterI (Self, &node) ; + node.wait_reenter_end(this); + } + + // Self has reacquired the lock. + // Lifecycle - the node representing Self must not appear on any queues. + // Node is about to go out-of-scope, but even if it were immortal we wouldn't + // want residual elements associated with this thread left on any lists. + guarantee (node.TState == ObjectWaiter::TS_RUN, "invariant") ; + assert (_owner == Self, "invariant") ; + assert (_succ != Self , "invariant") ; + } // OSThreadWaitState() + + jt->set_current_waiting_monitor(NULL); + + guarantee (_recursions == 0, "invariant") ; + _recursions = save; // restore the old recursion count + _waiters--; // decrement the number of waiters + + // Verify a few postconditions + assert (_owner == Self , "invariant") ; + assert (_succ != Self , "invariant") ; + assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ; + + if (SyncFlags & 32) { + OrderAccess::fence() ; + } + + // check if the notification happened + if (!WasNotified) { + // no, it could be timeout or Thread.interrupt() or both + // check for interrupt event, otherwise it is timeout + if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) { + TEVENT (Wait - throw IEX from epilog) ; + THROW(vmSymbols::java_lang_InterruptedException()); + } + } + + // NOTE: Spurious wake up will be consider as timeout. + // Monitor notify has precedence over thread interrupt. +} + + +// Consider: +// If the lock is cool (cxq == null && succ == null) and we're on an MP system +// then instead of transferring a thread from the WaitSet to the EntryList +// we might just dequeue a thread from the WaitSet and directly unpark() it. + +void ObjectMonitor::notify(TRAPS) { + CHECK_OWNER(); + if (_WaitSet == NULL) { + TEVENT (Empty-Notify) ; + return ; + } + DTRACE_MONITOR_PROBE(notify, this, object(), THREAD); + + int Policy = Knob_MoveNotifyee ; + + Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ; + ObjectWaiter * iterator = DequeueWaiter() ; + if (iterator != NULL) { + TEVENT (Notify1 - Transfer) ; + guarantee (iterator->TState == ObjectWaiter::TS_WAIT, "invariant") ; + guarantee (iterator->_notified == 0, "invariant") ; + // Disposition - what might we do with iterator ? + // a. add it directly to the EntryList - either tail or head. + // b. push it onto the front of the _cxq. + // For now we use (a). + if (Policy != 4) { + iterator->TState = ObjectWaiter::TS_ENTER ; + } + iterator->_notified = 1 ; + + ObjectWaiter * List = _EntryList ; + if (List != NULL) { + assert (List->_prev == NULL, "invariant") ; + assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ; + assert (List != iterator, "invariant") ; + } + + if (Policy == 0) { // prepend to EntryList + if (List == NULL) { + iterator->_next = iterator->_prev = NULL ; + _EntryList = iterator ; + } else { + List->_prev = iterator ; + iterator->_next = List ; + iterator->_prev = NULL ; + _EntryList = iterator ; + } + } else + if (Policy == 1) { // append to EntryList + if (List == NULL) { + iterator->_next = iterator->_prev = NULL ; + _EntryList = iterator ; + } else { + // CONSIDER: finding the tail currently requires a linear-time walk of + // the EntryList. We can make tail access constant-time by converting to + // a CDLL instead of using our current DLL. + ObjectWaiter * Tail ; + for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ; + assert (Tail != NULL && Tail->_next == NULL, "invariant") ; + Tail->_next = iterator ; + iterator->_prev = Tail ; + iterator->_next = NULL ; + } + } else + if (Policy == 2) { // prepend to cxq + // prepend to cxq + if (List == NULL) { + iterator->_next = iterator->_prev = NULL ; + _EntryList = iterator ; + } else { + iterator->TState = ObjectWaiter::TS_CXQ ; + for (;;) { + ObjectWaiter * Front = _cxq ; + iterator->_next = Front ; + if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) { + break ; + } + } + } + } else + if (Policy == 3) { // append to cxq + iterator->TState = ObjectWaiter::TS_CXQ ; + for (;;) { + ObjectWaiter * Tail ; + Tail = _cxq ; + if (Tail == NULL) { + iterator->_next = NULL ; + if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) { + break ; + } + } else { + while (Tail->_next != NULL) Tail = Tail->_next ; + Tail->_next = iterator ; + iterator->_prev = Tail ; + iterator->_next = NULL ; + break ; + } + } + } else { + ParkEvent * ev = iterator->_event ; + iterator->TState = ObjectWaiter::TS_RUN ; + OrderAccess::fence() ; + ev->unpark() ; + } + + if (Policy < 4) { + iterator->wait_reenter_begin(this); + } + + // _WaitSetLock protects the wait queue, not the EntryList. We could + // move the add-to-EntryList operation, above, outside the critical section + // protected by _WaitSetLock. In practice that's not useful. With the + // exception of wait() timeouts and interrupts the monitor owner + // is the only thread that grabs _WaitSetLock. There's almost no contention + // on _WaitSetLock so it's not profitable to reduce the length of the + // critical section. + } + + Thread::SpinRelease (&_WaitSetLock) ; + + if (iterator != NULL && ObjectSynchronizer::_sync_Notifications != NULL) { + ObjectSynchronizer::_sync_Notifications->inc() ; + } +} + + +void ObjectMonitor::notifyAll(TRAPS) { + CHECK_OWNER(); + ObjectWaiter* iterator; + if (_WaitSet == NULL) { + TEVENT (Empty-NotifyAll) ; + return ; + } + DTRACE_MONITOR_PROBE(notifyAll, this, object(), THREAD); + + int Policy = Knob_MoveNotifyee ; + int Tally = 0 ; + Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notifyall") ; + + for (;;) { + iterator = DequeueWaiter () ; + if (iterator == NULL) break ; + TEVENT (NotifyAll - Transfer1) ; + ++Tally ; + + // Disposition - what might we do with iterator ? + // a. add it directly to the EntryList - either tail or head. + // b. push it onto the front of the _cxq. + // For now we use (a). + // + // TODO-FIXME: currently notifyAll() transfers the waiters one-at-a-time from the waitset + // to the EntryList. This could be done more efficiently with a single bulk transfer, + // but in practice it's not time-critical. Beware too, that in prepend-mode we invert the + // order of the waiters. Lets say that the waitset is "ABCD" and the EntryList is "XYZ". + // After a notifyAll() in prepend mode the waitset will be empty and the EntryList will + // be "DCBAXYZ". + + guarantee (iterator->TState == ObjectWaiter::TS_WAIT, "invariant") ; + guarantee (iterator->_notified == 0, "invariant") ; + iterator->_notified = 1 ; + if (Policy != 4) { + iterator->TState = ObjectWaiter::TS_ENTER ; + } + + ObjectWaiter * List = _EntryList ; + if (List != NULL) { + assert (List->_prev == NULL, "invariant") ; + assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ; + assert (List != iterator, "invariant") ; + } + + if (Policy == 0) { // prepend to EntryList + if (List == NULL) { + iterator->_next = iterator->_prev = NULL ; + _EntryList = iterator ; + } else { + List->_prev = iterator ; + iterator->_next = List ; + iterator->_prev = NULL ; + _EntryList = iterator ; + } + } else + if (Policy == 1) { // append to EntryList + if (List == NULL) { + iterator->_next = iterator->_prev = NULL ; + _EntryList = iterator ; + } else { + // CONSIDER: finding the tail currently requires a linear-time walk of + // the EntryList. We can make tail access constant-time by converting to + // a CDLL instead of using our current DLL. + ObjectWaiter * Tail ; + for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ; + assert (Tail != NULL && Tail->_next == NULL, "invariant") ; + Tail->_next = iterator ; + iterator->_prev = Tail ; + iterator->_next = NULL ; + } + } else + if (Policy == 2) { // prepend to cxq + // prepend to cxq + iterator->TState = ObjectWaiter::TS_CXQ ; + for (;;) { + ObjectWaiter * Front = _cxq ; + iterator->_next = Front ; + if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) { + break ; + } + } + } else + if (Policy == 3) { // append to cxq + iterator->TState = ObjectWaiter::TS_CXQ ; + for (;;) { + ObjectWaiter * Tail ; + Tail = _cxq ; + if (Tail == NULL) { + iterator->_next = NULL ; + if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) { + break ; + } + } else { + while (Tail->_next != NULL) Tail = Tail->_next ; + Tail->_next = iterator ; + iterator->_prev = Tail ; + iterator->_next = NULL ; + break ; + } + } + } else { + ParkEvent * ev = iterator->_event ; + iterator->TState = ObjectWaiter::TS_RUN ; + OrderAccess::fence() ; + ev->unpark() ; + } + + if (Policy < 4) { + iterator->wait_reenter_begin(this); + } + + // _WaitSetLock protects the wait queue, not the EntryList. We could + // move the add-to-EntryList operation, above, outside the critical section + // protected by _WaitSetLock. In practice that's not useful. With the + // exception of wait() timeouts and interrupts the monitor owner + // is the only thread that grabs _WaitSetLock. There's almost no contention + // on _WaitSetLock so it's not profitable to reduce the length of the + // critical section. + } + + Thread::SpinRelease (&_WaitSetLock) ; + + if (Tally != 0 && ObjectSynchronizer::_sync_Notifications != NULL) { + ObjectSynchronizer::_sync_Notifications->inc(Tally) ; + } +} + +// check_slow() is a misnomer. It's called to simply to throw an IMSX exception. +// TODO-FIXME: remove check_slow() -- it's likely dead. + +void ObjectMonitor::check_slow(TRAPS) { + TEVENT (check_slow - throw IMSX) ; + assert(THREAD != _owner && !THREAD->is_lock_owned((address) _owner), "must not be owner"); + THROW_MSG(vmSymbols::java_lang_IllegalMonitorStateException(), "current thread not owner"); +} + + +// ------------------------------------------------------------------------- +// The raw monitor subsystem is entirely distinct from normal +// java-synchronization or jni-synchronization. raw monitors are not +// associated with objects. They can be implemented in any manner +// that makes sense. The original implementors decided to piggy-back +// the raw-monitor implementation on the existing Java objectMonitor mechanism. +// This flaw needs to fixed. We should reimplement raw monitors as sui-generis. +// Specifically, we should not implement raw monitors via java monitors. +// Time permitting, we should disentangle and deconvolve the two implementations +// and move the resulting raw monitor implementation over to the JVMTI directories. +// Ideally, the raw monitor implementation would be built on top of +// park-unpark and nothing else. +// +// raw monitors are used mainly by JVMTI +// The raw monitor implementation borrows the ObjectMonitor structure, +// but the operators are degenerate and extremely simple. +// +// Mixed use of a single objectMonitor instance -- as both a raw monitor +// and a normal java monitor -- is not permissible. +// +// Note that we use the single RawMonitor_lock to protect queue operations for +// _all_ raw monitors. This is a scalability impediment, but since raw monitor usage +// is deprecated and rare, this is not of concern. The RawMonitor_lock can not +// be held indefinitely. The critical sections must be short and bounded. +// +// ------------------------------------------------------------------------- + +int ObjectMonitor::SimpleEnter (Thread * Self) { + for (;;) { + if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) { + return OS_OK ; + } + + ObjectWaiter Node (Self) ; + Self->_ParkEvent->reset() ; // strictly optional + Node.TState = ObjectWaiter::TS_ENTER ; + + RawMonitor_lock->lock_without_safepoint_check() ; + Node._next = _EntryList ; + _EntryList = &Node ; + OrderAccess::fence() ; + if (_owner == NULL && Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) { + _EntryList = Node._next ; + RawMonitor_lock->unlock() ; + return OS_OK ; + } + RawMonitor_lock->unlock() ; + while (Node.TState == ObjectWaiter::TS_ENTER) { + Self->_ParkEvent->park() ; + } + } +} + +int ObjectMonitor::SimpleExit (Thread * Self) { + guarantee (_owner == Self, "invariant") ; + OrderAccess::release_store_ptr (&_owner, NULL) ; + OrderAccess::fence() ; + if (_EntryList == NULL) return OS_OK ; + ObjectWaiter * w ; + + RawMonitor_lock->lock_without_safepoint_check() ; + w = _EntryList ; + if (w != NULL) { + _EntryList = w->_next ; + } + RawMonitor_lock->unlock() ; + if (w != NULL) { + guarantee (w ->TState == ObjectWaiter::TS_ENTER, "invariant") ; + ParkEvent * ev = w->_event ; + w->TState = ObjectWaiter::TS_RUN ; + OrderAccess::fence() ; + ev->unpark() ; + } + return OS_OK ; +} + +int ObjectMonitor::SimpleWait (Thread * Self, jlong millis) { + guarantee (_owner == Self , "invariant") ; + guarantee (_recursions == 0, "invariant") ; + + ObjectWaiter Node (Self) ; + Node._notified = 0 ; + Node.TState = ObjectWaiter::TS_WAIT ; + + RawMonitor_lock->lock_without_safepoint_check() ; + Node._next = _WaitSet ; + _WaitSet = &Node ; + RawMonitor_lock->unlock() ; + + SimpleExit (Self) ; + guarantee (_owner != Self, "invariant") ; + + int ret = OS_OK ; + if (millis <= 0) { + Self->_ParkEvent->park(); + } else { + ret = Self->_ParkEvent->park(millis); + } + + // If thread still resides on the waitset then unlink it. + // Double-checked locking -- the usage is safe in this context + // as we TState is volatile and the lock-unlock operators are + // serializing (barrier-equivalent). + + if (Node.TState == ObjectWaiter::TS_WAIT) { + RawMonitor_lock->lock_without_safepoint_check() ; + if (Node.TState == ObjectWaiter::TS_WAIT) { + // Simple O(n) unlink, but performance isn't critical here. + ObjectWaiter * p ; + ObjectWaiter * q = NULL ; + for (p = _WaitSet ; p != &Node; p = p->_next) { + q = p ; + } + guarantee (p == &Node, "invariant") ; + if (q == NULL) { + guarantee (p == _WaitSet, "invariant") ; + _WaitSet = p->_next ; + } else { + guarantee (p == q->_next, "invariant") ; + q->_next = p->_next ; + } + Node.TState = ObjectWaiter::TS_RUN ; + } + RawMonitor_lock->unlock() ; + } + + guarantee (Node.TState == ObjectWaiter::TS_RUN, "invariant") ; + SimpleEnter (Self) ; + + guarantee (_owner == Self, "invariant") ; + guarantee (_recursions == 0, "invariant") ; + return ret ; +} + +int ObjectMonitor::SimpleNotify (Thread * Self, bool All) { + guarantee (_owner == Self, "invariant") ; + if (_WaitSet == NULL) return OS_OK ; + + // We have two options: + // A. Transfer the threads from the WaitSet to the EntryList + // B. Remove the thread from the WaitSet and unpark() it. + // + // We use (B), which is crude and results in lots of futile + // context switching. In particular (B) induces lots of contention. + + ParkEvent * ev = NULL ; // consider using a small auto array ... + RawMonitor_lock->lock_without_safepoint_check() ; + for (;;) { + ObjectWaiter * w = _WaitSet ; + if (w == NULL) break ; + _WaitSet = w->_next ; + if (ev != NULL) { ev->unpark(); ev = NULL; } + ev = w->_event ; + OrderAccess::loadstore() ; + w->TState = ObjectWaiter::TS_RUN ; + OrderAccess::storeload(); + if (!All) break ; + } + RawMonitor_lock->unlock() ; + if (ev != NULL) ev->unpark(); + return OS_OK ; +} + +// Any JavaThread will enter here with state _thread_blocked +int ObjectMonitor::raw_enter(TRAPS) { + TEVENT (raw_enter) ; + void * Contended ; + + // don't enter raw monitor if thread is being externally suspended, it will + // surprise the suspender if a "suspended" thread can still enter monitor + JavaThread * jt = (JavaThread *)THREAD; + if (THREAD->is_Java_thread()) { + jt->SR_lock()->lock_without_safepoint_check(); + while (jt->is_external_suspend()) { + jt->SR_lock()->unlock(); + jt->java_suspend_self(); + jt->SR_lock()->lock_without_safepoint_check(); + } + // guarded by SR_lock to avoid racing with new external suspend requests. + Contended = Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) ; + jt->SR_lock()->unlock(); + } else { + Contended = Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) ; + } + + if (Contended == THREAD) { + _recursions ++ ; + return OM_OK ; + } + + if (Contended == NULL) { + guarantee (_owner == THREAD, "invariant") ; + guarantee (_recursions == 0, "invariant") ; + return OM_OK ; + } + + THREAD->set_current_pending_monitor(this); + + if (!THREAD->is_Java_thread()) { + // No other non-Java threads besides VM thread would acquire + // a raw monitor. + assert(THREAD->is_VM_thread(), "must be VM thread"); + SimpleEnter (THREAD) ; + } else { + guarantee (jt->thread_state() == _thread_blocked, "invariant") ; + for (;;) { + jt->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() + SimpleEnter (THREAD) ; + + // were we externally suspended while we were waiting? + if (!jt->handle_special_suspend_equivalent_condition()) break ; + + // This thread was externally suspended + // + // This logic isn't needed for JVMTI raw monitors, + // but doesn't hurt just in case the suspend rules change. This + // logic is needed for the ObjectMonitor.wait() reentry phase. + // We have reentered the contended monitor, but while we were + // waiting another thread suspended us. We don't want to reenter + // the monitor while suspended because that would surprise the + // thread that suspended us. + // + // Drop the lock - + SimpleExit (THREAD) ; + + jt->java_suspend_self(); + } + + assert(_owner == THREAD, "Fatal error with monitor owner!"); + assert(_recursions == 0, "Fatal error with monitor recursions!"); + } + + THREAD->set_current_pending_monitor(NULL); + guarantee (_recursions == 0, "invariant") ; + return OM_OK; +} + +// Used mainly for JVMTI raw monitor implementation +// Also used for ObjectMonitor::wait(). +int ObjectMonitor::raw_exit(TRAPS) { + TEVENT (raw_exit) ; + if (THREAD != _owner) { + return OM_ILLEGAL_MONITOR_STATE; + } + if (_recursions > 0) { + --_recursions ; + return OM_OK ; + } + + void * List = _EntryList ; + SimpleExit (THREAD) ; + + return OM_OK; +} + +// Used for JVMTI raw monitor implementation. +// All JavaThreads will enter here with state _thread_blocked + +int ObjectMonitor::raw_wait(jlong millis, bool interruptible, TRAPS) { + TEVENT (raw_wait) ; + if (THREAD != _owner) { + return OM_ILLEGAL_MONITOR_STATE; + } + + // To avoid spurious wakeups we reset the parkevent -- This is strictly optional. + // The caller must be able to tolerate spurious returns from raw_wait(). + THREAD->_ParkEvent->reset() ; + OrderAccess::fence() ; + + // check interrupt event + if (interruptible && Thread::is_interrupted(THREAD, true)) { + return OM_INTERRUPTED; + } + + intptr_t save = _recursions ; + _recursions = 0 ; + _waiters ++ ; + if (THREAD->is_Java_thread()) { + guarantee (((JavaThread *) THREAD)->thread_state() == _thread_blocked, "invariant") ; + ((JavaThread *)THREAD)->set_suspend_equivalent(); + } + int rv = SimpleWait (THREAD, millis) ; + _recursions = save ; + _waiters -- ; + + guarantee (THREAD == _owner, "invariant") ; + if (THREAD->is_Java_thread()) { + JavaThread * jSelf = (JavaThread *) THREAD ; + for (;;) { + if (!jSelf->handle_special_suspend_equivalent_condition()) break ; + SimpleExit (THREAD) ; + jSelf->java_suspend_self(); + SimpleEnter (THREAD) ; + jSelf->set_suspend_equivalent() ; + } + } + guarantee (THREAD == _owner, "invariant") ; + + if (interruptible && Thread::is_interrupted(THREAD, true)) { + return OM_INTERRUPTED; + } + return OM_OK ; +} + +int ObjectMonitor::raw_notify(TRAPS) { + TEVENT (raw_notify) ; + if (THREAD != _owner) { + return OM_ILLEGAL_MONITOR_STATE; + } + SimpleNotify (THREAD, false) ; + return OM_OK; +} + +int ObjectMonitor::raw_notifyAll(TRAPS) { + TEVENT (raw_notifyAll) ; + if (THREAD != _owner) { + return OM_ILLEGAL_MONITOR_STATE; + } + SimpleNotify (THREAD, true) ; + return OM_OK; +} + +#ifndef PRODUCT +void ObjectMonitor::verify() { +} + +void ObjectMonitor::print() { +} +#endif + +//------------------------------------------------------------------------------ +// Non-product code + +#ifndef PRODUCT + +void ObjectSynchronizer::trace_locking(Handle locking_obj, bool is_compiled, + bool is_method, bool is_locking) { + // Don't know what to do here +} + +// Verify all monitors in the monitor cache, the verification is weak. +void ObjectSynchronizer::verify() { + ObjectMonitor* block = gBlockList; + ObjectMonitor* mid; + while (block) { + assert(block->object() == CHAINMARKER, "must be a block header"); + for (int i = 1; i < _BLOCKSIZE; i++) { + mid = block + i; + oop object = (oop) mid->object(); + if (object != NULL) { + mid->verify(); + } + } + block = (ObjectMonitor*) block->FreeNext; + } +} + +// Check if monitor belongs to the monitor cache +// The list is grow-only so it's *relatively* safe to traverse +// the list of extant blocks without taking a lock. + +int ObjectSynchronizer::verify_objmon_isinpool(ObjectMonitor *monitor) { + ObjectMonitor* block = gBlockList; + + while (block) { + assert(block->object() == CHAINMARKER, "must be a block header"); + if (monitor > &block[0] && monitor < &block[_BLOCKSIZE]) { + address mon = (address) monitor; + address blk = (address) block; + size_t diff = mon - blk; + assert((diff % sizeof(ObjectMonitor)) == 0, "check"); + return 1; + } + block = (ObjectMonitor*) block->FreeNext; + } + return 0; +} + +#endif diff --git a/hotspot/src/share/vm/runtime/synchronizer.hpp b/hotspot/src/share/vm/runtime/synchronizer.hpp new file mode 100644 index 00000000000..7fcd29cdd2c --- /dev/null +++ b/hotspot/src/share/vm/runtime/synchronizer.hpp @@ -0,0 +1,216 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class BasicLock VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + volatile markOop _displaced_header; + public: + markOop displaced_header() const { return _displaced_header; } + void set_displaced_header(markOop header) { _displaced_header = header; } + + void print_on(outputStream* st) const; + + // move a basic lock (used during deoptimization + void move_to(oop obj, BasicLock* dest); + + static int displaced_header_offset_in_bytes() { return offset_of(BasicLock, _displaced_header); } +}; + +// A BasicObjectLock associates a specific Java object with a BasicLock. +// It is currently embedded in an interpreter frame. + +// Because some machines have alignment restrictions on the control stack, +// the actual space allocated by the interpreter may include padding words +// after the end of the BasicObjectLock. Also, in order to guarantee +// alignment of the embedded BasicLock objects on such machines, we +// put the embedded BasicLock at the beginning of the struct. + +class BasicObjectLock VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + BasicLock _lock; // the lock, must be double word aligned + oop _obj; // object holds the lock; + + public: + // Manipulation + oop obj() const { return _obj; } + void set_obj(oop obj) { _obj = obj; } + BasicLock* lock() { return &_lock; } + + // Note: Use frame::interpreter_frame_monitor_size() for the size of BasicObjectLocks + // in interpreter activation frames since it includes machine-specific padding. + static int size() { return sizeof(BasicObjectLock)/wordSize; } + + // GC support + void oops_do(OopClosure* f) { f->do_oop(&_obj); } + + static int obj_offset_in_bytes() { return offset_of(BasicObjectLock, _obj); } + static int lock_offset_in_bytes() { return offset_of(BasicObjectLock, _lock); } +}; + +class ObjectMonitor; + +class ObjectSynchronizer : AllStatic { + friend class VMStructs; + public: + typedef enum { + owner_self, + owner_none, + owner_other + } LockOwnership; + // exit must be implemented non-blocking, since the compiler cannot easily handle + // deoptimization at monitor exit. Hence, it does not take a Handle argument. + + // This is full version of monitor enter and exit. I choose not + // to use enter() and exit() in order to make sure user be ware + // of the performance and semantics difference. They are normally + // used by ObjectLocker etc. The interpreter and compiler use + // assembly copies of these routines. Please keep them synchornized. + // + // attempt_rebias flag is used by UseBiasedLocking implementation + static void fast_enter (Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS); + static void fast_exit (oop obj, BasicLock* lock, Thread* THREAD); + + // WARNING: They are ONLY used to handle the slow cases. They should + // only be used when the fast cases failed. Use of these functions + // without previous fast case check may cause fatal error. + static void slow_enter (Handle obj, BasicLock* lock, TRAPS); + static void slow_exit (oop obj, BasicLock* lock, Thread* THREAD); + + // Used only to handle jni locks or other unmatched monitor enter/exit + // Internally they will use heavy weight monitor. + static void jni_enter (Handle obj, TRAPS); + static bool jni_try_enter(Handle obj, Thread* THREAD); // Implements Unsafe.tryMonitorEnter + static void jni_exit (oop obj, Thread* THREAD); + + // Handle all interpreter, compiler and jni cases + static void wait (Handle obj, jlong millis, TRAPS); + static void notify (Handle obj, TRAPS); + static void notifyall (Handle obj, TRAPS); + + // Special internal-use-only method for use by JVM infrastructure + // that needs to wait() on a java-level object but that can't risk + // throwing unexpected InterruptedExecutionExceptions. + static void waitUninterruptibly (Handle obj, jlong Millis, Thread * THREAD) ; + + // used by classloading to free classloader object lock, + // wait on an internal lock, and reclaim original lock + // with original recursion count + static intptr_t complete_exit (Handle obj, TRAPS); + static void reenter (Handle obj, intptr_t recursion, TRAPS); + + // thread-specific and global objectMonitor free list accessors + static ObjectMonitor * omAlloc (Thread * Self) ; + static void omRelease (Thread * Self, ObjectMonitor * m) ; + static void omFlush (Thread * Self) ; + + // Inflate light weight monitor to heavy weight monitor + static ObjectMonitor* inflate(Thread * Self, oop obj); + // This version is only for internal use + static ObjectMonitor* inflate_helper(oop obj); + + // Returns the identity hash value for an oop + // NOTE: It may cause monitor inflation + static intptr_t identity_hash_value_for(Handle obj); + static intptr_t FastHashCode (Thread * Self, oop obj) ; + + // java.lang.Thread support + static bool current_thread_holds_lock(JavaThread* thread, Handle h_obj); + static LockOwnership query_lock_ownership(JavaThread * self, Handle h_obj); + + static JavaThread* get_lock_owner(Handle h_obj, bool doLock); + + // JNI detach support + static void release_monitors_owned_by_thread(TRAPS); + static void monitors_iterate(MonitorClosure* m); + + // GC: we current use aggressive monitor deflation policy + // Basically we deflate all monitors that are not busy. + // An adaptive profile-based deflation policy could be used if needed + static void deflate_idle_monitors(); + static void oops_do(OopClosure* f); + + // debugging + static void trace_locking(Handle obj, bool is_compiled, bool is_method, bool is_locking) PRODUCT_RETURN; + static void verify() PRODUCT_RETURN; + static int verify_objmon_isinpool(ObjectMonitor *addr) PRODUCT_RETURN0; + + private: + enum { _BLOCKSIZE = 128 }; + static ObjectMonitor* gBlockList; + static ObjectMonitor * volatile gFreeList; + + public: + static void Initialize () ; + static PerfCounter * _sync_ContendedLockAttempts ; + static PerfCounter * _sync_FutileWakeups ; + static PerfCounter * _sync_Parks ; + static PerfCounter * _sync_EmptyNotifications ; + static PerfCounter * _sync_Notifications ; + static PerfCounter * _sync_SlowEnter ; + static PerfCounter * _sync_SlowExit ; + static PerfCounter * _sync_SlowNotify ; + static PerfCounter * _sync_SlowNotifyAll ; + static PerfCounter * _sync_FailedSpins ; + static PerfCounter * _sync_SuccessfulSpins ; + static PerfCounter * _sync_PrivateA ; + static PerfCounter * _sync_PrivateB ; + static PerfCounter * _sync_MonInCirculation ; + static PerfCounter * _sync_MonScavenged ; + static PerfCounter * _sync_Inflations ; + static PerfCounter * _sync_Deflations ; + static PerfLongVariable * _sync_MonExtant ; + + public: + static void RegisterSpinCallback (int (*)(intptr_t, int), intptr_t) ; + +}; + +// ObjectLocker enforced balanced locking and can never thrown an +// IllegalMonitorStateException. However, a pending exception may +// have to pass through, and we must also be able to deal with +// asynchronous exceptions. The caller is responsible for checking +// the threads pending exception if needed. +// doLock was added to support classloading with UnsyncloadClass which +// requires flag based choice of locking the classloader lock. +class ObjectLocker : public StackObj { + private: + Thread* _thread; + Handle _obj; + BasicLock _lock; + bool _dolock; // default true + public: + ObjectLocker(Handle obj, Thread* thread, bool doLock = true); + ~ObjectLocker(); + + // Monitor behavior + void wait (TRAPS) { ObjectSynchronizer::wait (_obj, 0, CHECK); } // wait forever + void notify_all(TRAPS) { ObjectSynchronizer::notifyall(_obj, CHECK); } + void waitUninterruptibly (TRAPS) { ObjectSynchronizer::waitUninterruptibly (_obj, 0, CHECK);} + // complete_exit gives up lock completely, returning recursion count + // reenter reclaims lock with original recursion count + intptr_t complete_exit(TRAPS) { return ObjectSynchronizer::complete_exit(_obj, CHECK_0); } + void reenter(intptr_t recursion, TRAPS) { ObjectSynchronizer::reenter(_obj, recursion, CHECK); } +}; diff --git a/hotspot/src/share/vm/runtime/task.cpp b/hotspot/src/share/vm/runtime/task.cpp new file mode 100644 index 00000000000..0fd1bed8aee --- /dev/null +++ b/hotspot/src/share/vm/runtime/task.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_task.cpp.incl" + +int PeriodicTask::_num_tasks = 0; +PeriodicTask* PeriodicTask::_tasks[PeriodicTask::max_tasks]; +#ifndef PRODUCT +elapsedTimer PeriodicTask::_timer; +int PeriodicTask::_intervalHistogram[PeriodicTask::max_interval]; +int PeriodicTask::_ticks; + +void PeriodicTask::print_intervals() { + if (ProfilerCheckIntervals) { + for (int i = 0; i < PeriodicTask::max_interval; i++) { + int n = _intervalHistogram[i]; + if (n > 0) tty->print_cr("%3d: %5d (%4.1f%%)", i, n, 100.0 * n / _ticks); + } + } +} +#endif + +void PeriodicTask::real_time_tick(size_t delay_time) { +#ifndef PRODUCT + if (ProfilerCheckIntervals) { + _ticks++; + _timer.stop(); + int ms = (int)(_timer.seconds() * 1000.0); + _timer.reset(); + _timer.start(); + if (ms >= PeriodicTask::max_interval) ms = PeriodicTask::max_interval - 1; + _intervalHistogram[ms]++; + } +#endif + int orig_num_tasks = _num_tasks; + for(int index = 0; index < _num_tasks; index++) { + _tasks[index]->execute_if_pending(delay_time); + if (_num_tasks < orig_num_tasks) { // task dis-enrolled itself + index--; // re-do current slot as it has changed + orig_num_tasks = _num_tasks; + } + } +} + + +PeriodicTask::PeriodicTask(size_t interval_time) : + _counter(0), _interval(interval_time) { + assert(is_init_completed(), "Periodic tasks should not start during VM initialization"); + // Sanity check the interval time + assert(_interval >= PeriodicTask::min_interval && + _interval <= PeriodicTask::max_interval && + _interval % PeriodicTask::interval_gran == 0, + "improper PeriodicTask interval time"); +} + +PeriodicTask::~PeriodicTask() { + if (is_enrolled()) + disenroll(); +} + +bool PeriodicTask::is_enrolled() const { + for(int index = 0; index < _num_tasks; index++) + if (_tasks[index] == this) return true; + return false; +} + +void PeriodicTask::enroll() { + assert(WatcherThread::watcher_thread() == NULL, "dynamic enrollment of tasks not yet supported"); + + if (_num_tasks == PeriodicTask::max_tasks) + fatal("Overflow in PeriodicTask table"); + _tasks[_num_tasks++] = this; +} + +void PeriodicTask::disenroll() { + assert(WatcherThread::watcher_thread() == NULL || + Thread::current() == WatcherThread::watcher_thread(), + "dynamic disenrollment currently only handled from WatcherThread from within task() method"); + + int index; + for(index = 0; index < _num_tasks && _tasks[index] != this; index++); + if (index == _num_tasks) return; + _num_tasks--; + for (; index < _num_tasks; index++) { + _tasks[index] = _tasks[index+1]; + } +} + +TimeMillisUpdateTask* TimeMillisUpdateTask::_task = NULL; + +void TimeMillisUpdateTask::task() { + os::update_global_time(); +} + +void TimeMillisUpdateTask::engage() { + assert(_task == NULL, "init twice?"); + os::update_global_time(); // initial update + os::enable_global_time(); + _task = new TimeMillisUpdateTask(CacheTimeMillisGranularity); + _task->enroll(); +} + +void TimeMillisUpdateTask::disengage() { + assert(_task != NULL, "uninit twice?"); + os::disable_global_time(); + _task->disenroll(); + delete _task; + _task = NULL; +} diff --git a/hotspot/src/share/vm/runtime/task.hpp b/hotspot/src/share/vm/runtime/task.hpp new file mode 100644 index 00000000000..924562cf099 --- /dev/null +++ b/hotspot/src/share/vm/runtime/task.hpp @@ -0,0 +1,125 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A PeriodicTask has the sole purpose of executing its task +// function with regular intervals. +// Usage: +// PeriodicTask pf(10); +// pf.enroll(); +// ... +// pf.disenroll(); + +class PeriodicTask: public CHeapObj { + public: + // Useful constants. + // The interval constants are used to ensure the declared interval + // is appropriate; it must be between min_interval and max_interval, + // and have a granularity of interval_gran (all in millis). + enum { max_tasks = 10, // Max number of periodic tasks in system + interval_gran = 10, + min_interval = 10, + max_interval = 10000 }; + + static int num_tasks() { return _num_tasks; } + + private: + size_t _counter; + const size_t _interval; + + static int _num_tasks; + static PeriodicTask* _tasks[PeriodicTask::max_tasks]; + static void real_time_tick(size_t delay_time); + +#ifndef PRODUCT + static elapsedTimer _timer; // measures time between ticks + static int _ticks; // total number of ticks + static int _intervalHistogram[max_interval]; // to check spacing of timer interrupts + public: + static void print_intervals(); +#endif + // Only the WatcherThread can cause us to execute PeriodicTasks + friend class WatcherThread; + public: + PeriodicTask(size_t interval_time); // interval is in milliseconds of elapsed time + ~PeriodicTask(); + + // Tells whether is enrolled + bool is_enrolled() const; + + // Make the task active + // NOTE: this may only be called before the WatcherThread has been started + void enroll(); + + // Make the task deactive + // NOTE: this may only be called either while the WatcherThread is + // inactive or by a task from within its task() method. One-shot or + // several-shot tasks may be implemented this way. + void disenroll(); + + void execute_if_pending(size_t delay_time) { + _counter += delay_time; + if (_counter >= _interval) { + _counter = 0; + task(); + } + } + + // Returns how long (time in milliseconds) before the next time we should + // execute this task. + size_t time_to_next_interval() const { + assert(_interval > _counter, "task counter greater than interval?"); + return _interval - _counter; + } + + // Calculate when the next periodic task will fire. + // Called by the WatcherThread's run method. + // This assumes that periodic tasks aren't entering the system + // dynamically, except for during startup. + static size_t time_to_wait() { + if (_num_tasks == 0) { + // Don't wait any more; shut down the thread since we don't + // currently support dynamic enrollment. + return 0; + } + + size_t delay = _tasks[0]->time_to_next_interval(); + for (int index = 1; index < _num_tasks; index++) { + delay = MIN2(delay, _tasks[index]->time_to_next_interval()); + } + return delay; + } + + // The task to perform at each period + virtual void task() = 0; +}; + +class TimeMillisUpdateTask : public PeriodicTask { + private: + static TimeMillisUpdateTask* _task; + public: + TimeMillisUpdateTask(int interval) : PeriodicTask(interval) {} + void task(); + static void engage(); + static void disengage(); +}; diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp new file mode 100644 index 00000000000..607772a0242 --- /dev/null +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -0,0 +1,3972 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_thread.cpp.incl" + +#ifdef DTRACE_ENABLED + +// Only bother with this argument setup if dtrace is available + +HS_DTRACE_PROBE_DECL(hotspot, vm__init__begin); +HS_DTRACE_PROBE_DECL(hotspot, vm__init__end); +HS_DTRACE_PROBE_DECL5(hotspot, thread__start, char*, intptr_t, + intptr_t, intptr_t, bool); +HS_DTRACE_PROBE_DECL5(hotspot, thread__stop, char*, intptr_t, + intptr_t, intptr_t, bool); + +#define DTRACE_THREAD_PROBE(probe, javathread) \ + { \ + ResourceMark rm(this); \ + int len = 0; \ + const char* name = (javathread)->get_thread_name(); \ + len = strlen(name); \ + HS_DTRACE_PROBE5(hotspot, thread__##probe, \ + name, len, \ + java_lang_Thread::thread_id((javathread)->threadObj()), \ + (javathread)->osthread()->thread_id(), \ + java_lang_Thread::is_daemon((javathread)->threadObj())); \ + } + +#else // ndef DTRACE_ENABLED + +#define DTRACE_THREAD_PROBE(probe, javathread) + +#endif // ndef DTRACE_ENABLED + +// Class hierarchy +// - Thread +// - VMThread +// - WatcherThread +// - ConcurrentMarkSweepThread +// - JavaThread +// - CompilerThread + +// ======= Thread ======== + +// Support for forcing alignment of thread objects for biased locking +void* Thread::operator new(size_t size) { + if (UseBiasedLocking) { + const int alignment = markOopDesc::biased_lock_alignment; + size_t aligned_size = size + (alignment - sizeof(intptr_t)); + void* real_malloc_addr = CHeapObj::operator new(aligned_size); + void* aligned_addr = (void*) align_size_up((intptr_t) real_malloc_addr, alignment); + assert(((uintptr_t) aligned_addr + (uintptr_t) size) <= + ((uintptr_t) real_malloc_addr + (uintptr_t) aligned_size), + "JavaThread alignment code overflowed allocated storage"); + if (TraceBiasedLocking) { + if (aligned_addr != real_malloc_addr) + tty->print_cr("Aligned thread " INTPTR_FORMAT " to " INTPTR_FORMAT, + real_malloc_addr, aligned_addr); + } + ((Thread*) aligned_addr)->_real_malloc_address = real_malloc_addr; + return aligned_addr; + } else { + return CHeapObj::operator new(size); + } +} + +void Thread::operator delete(void* p) { + if (UseBiasedLocking) { + void* real_malloc_addr = ((Thread*) p)->_real_malloc_address; + CHeapObj::operator delete(real_malloc_addr); + } else { + CHeapObj::operator delete(p); + } +} + + +// Base class for all threads: VMThread, WatcherThread, ConcurrentMarkSweepThread, +// JavaThread + + +Thread::Thread() { + // stack + _stack_base = NULL; + _stack_size = 0; + _self_raw_id = 0; + _lgrp_id = -1; + _osthread = NULL; + + // allocated data structures + set_resource_area(new ResourceArea()); + set_handle_area(new HandleArea(NULL)); + set_active_handles(NULL); + set_free_handle_block(NULL); + set_last_handle_mark(NULL); + set_osthread(NULL); + + // This initial value ==> never claimed. + _oops_do_parity = 0; + + // the handle mark links itself to last_handle_mark + new HandleMark(this); + + // plain initialization + debug_only(_owned_locks = NULL;) + debug_only(_allow_allocation_count = 0;) + NOT_PRODUCT(_allow_safepoint_count = 0;) + CHECK_UNHANDLED_OOPS_ONLY(_gc_locked_out_count = 0;) + _highest_lock = NULL; + _jvmti_env_iteration_count = 0; + _vm_operation_started_count = 0; + _vm_operation_completed_count = 0; + _current_pending_monitor = NULL; + _current_pending_monitor_is_from_java = true; + _current_waiting_monitor = NULL; + _num_nested_signal = 0; + omFreeList = NULL ; + omFreeCount = 0 ; + omFreeProvision = 32 ; + + _SR_lock = new Monitor(Mutex::suspend_resume, "SR_lock", true); + _suspend_flags = 0; + + // thread-specific hashCode stream generator state - Marsaglia shift-xor form + _hashStateX = os::random() ; + _hashStateY = 842502087 ; + _hashStateZ = 0x8767 ; // (int)(3579807591LL & 0xffff) ; + _hashStateW = 273326509 ; + + _OnTrap = 0 ; + _schedctl = NULL ; + _Stalled = 0 ; + _TypeTag = 0x2BAD ; + + // Many of the following fields are effectively final - immutable + // Note that nascent threads can't use the Native Monitor-Mutex + // construct until the _MutexEvent is initialized ... + // CONSIDER: instead of using a fixed set of purpose-dedicated ParkEvents + // we might instead use a stack of ParkEvents that we could provision on-demand. + // The stack would act as a cache to avoid calls to ParkEvent::Allocate() + // and ::Release() + _ParkEvent = ParkEvent::Allocate (this) ; + _SleepEvent = ParkEvent::Allocate (this) ; + _MutexEvent = ParkEvent::Allocate (this) ; + _MuxEvent = ParkEvent::Allocate (this) ; + +#ifdef CHECK_UNHANDLED_OOPS + if (CheckUnhandledOops) { + _unhandled_oops = new UnhandledOops(this); + } +#endif // CHECK_UNHANDLED_OOPS +#ifdef ASSERT + if (UseBiasedLocking) { + assert((((uintptr_t) this) & (markOopDesc::biased_lock_alignment - 1)) == 0, "forced alignment of thread object failed"); + assert(this == _real_malloc_address || + this == (void*) align_size_up((intptr_t) _real_malloc_address, markOopDesc::biased_lock_alignment), + "bug in forced alignment of thread objects"); + } +#endif /* ASSERT */ +} + +void Thread::initialize_thread_local_storage() { + // Note: Make sure this method only calls + // non-blocking operations. Otherwise, it might not work + // with the thread-startup/safepoint interaction. + + // During Java thread startup, safepoint code should allow this + // method to complete because it may need to allocate memory to + // store information for the new thread. + + // initialize structure dependent on thread local storage + ThreadLocalStorage::set_thread(this); + + // set up any platform-specific state. + os::initialize_thread(); + +} + +void Thread::record_stack_base_and_size() { + set_stack_base(os::current_stack_base()); + set_stack_size(os::current_stack_size()); +} + + +Thread::~Thread() { + // Reclaim the objectmonitors from the omFreeList of the moribund thread. + ObjectSynchronizer::omFlush (this) ; + + // deallocate data structures + delete resource_area(); + // since the handle marks are using the handle area, we have to deallocated the root + // handle mark before deallocating the thread's handle area, + assert(last_handle_mark() != NULL, "check we have an element"); + delete last_handle_mark(); + assert(last_handle_mark() == NULL, "check we have reached the end"); + + // It's possible we can encounter a null _ParkEvent, etc., in stillborn threads. + // We NULL out the fields for good hygiene. + ParkEvent::Release (_ParkEvent) ; _ParkEvent = NULL ; + ParkEvent::Release (_SleepEvent) ; _SleepEvent = NULL ; + ParkEvent::Release (_MutexEvent) ; _MutexEvent = NULL ; + ParkEvent::Release (_MuxEvent) ; _MuxEvent = NULL ; + + delete handle_area(); + + // osthread() can be NULL, if creation of thread failed. + if (osthread() != NULL) os::free_thread(osthread()); + + delete _SR_lock; + + // clear thread local storage if the Thread is deleting itself + if (this == Thread::current()) { + ThreadLocalStorage::set_thread(NULL); + } else { + // In the case where we're not the current thread, invalidate all the + // caches in case some code tries to get the current thread or the + // thread that was destroyed, and gets stale information. + ThreadLocalStorage::invalidate_all(); + } + CHECK_UNHANDLED_OOPS_ONLY(if (CheckUnhandledOops) delete unhandled_oops();) +} + +// NOTE: dummy function for assertion purpose. +void Thread::run() { + ShouldNotReachHere(); +} + +#ifdef ASSERT +// Private method to check for dangling thread pointer +void check_for_dangling_thread_pointer(Thread *thread) { + assert(!thread->is_Java_thread() || Thread::current() == thread || Threads_lock->owned_by_self(), + "possibility of dangling Thread pointer"); +} +#endif + + +#ifndef PRODUCT +// Tracing method for basic thread operations +void Thread::trace(const char* msg, const Thread* const thread) { + if (!TraceThreadEvents) return; + ResourceMark rm; + ThreadCritical tc; + const char *name = "non-Java thread"; + int prio = -1; + if (thread->is_Java_thread() + && !thread->is_Compiler_thread()) { + // The Threads_lock must be held to get information about + // this thread but may not be in some situations when + // tracing thread events. + bool release_Threads_lock = false; + if (!Threads_lock->owned_by_self()) { + Threads_lock->lock(); + release_Threads_lock = true; + } + JavaThread* jt = (JavaThread *)thread; + name = (char *)jt->get_thread_name(); + oop thread_oop = jt->threadObj(); + if (thread_oop != NULL) { + prio = java_lang_Thread::priority(thread_oop); + } + if (release_Threads_lock) { + Threads_lock->unlock(); + } + } + tty->print_cr("Thread::%s " INTPTR_FORMAT " [%lx] %s (prio: %d)", msg, thread, thread->osthread()->thread_id(), name, prio); +} +#endif + + +ThreadPriority Thread::get_priority(const Thread* const thread) { + trace("get priority", thread); + ThreadPriority priority; + // Can return an error! + (void)os::get_priority(thread, priority); + assert(MinPriority <= priority && priority <= MaxPriority, "non-Java priority found"); + return priority; +} + +void Thread::set_priority(Thread* thread, ThreadPriority priority) { + trace("set priority", thread); + debug_only(check_for_dangling_thread_pointer(thread);) + // Can return an error! + (void)os::set_priority(thread, priority); +} + + +void Thread::start(Thread* thread) { + trace("start", thread); + // Start is different from resume in that its safety is guaranteed by context or + // being called from a Java method synchronized on the Thread object. + if (!DisableStartThread) { + if (thread->is_Java_thread()) { + // Initialize the thread state to RUNNABLE before starting this thread. + // Can not set it after the thread started because we do not know the + // exact thread state at that time. It could be in MONITOR_WAIT or + // in SLEEPING or some other state. + java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(), + java_lang_Thread::RUNNABLE); + } + os::start_thread(thread); + } +} + +// Enqueue a VM_Operation to do the job for us - sometime later +void Thread::send_async_exception(oop java_thread, oop java_throwable) { + VM_ThreadStop* vm_stop = new VM_ThreadStop(java_thread, java_throwable); + VMThread::execute(vm_stop); +} + + +// +// Check if an external suspend request has completed (or has been +// cancelled). Returns true if the thread is externally suspended and +// false otherwise. +// +// The bits parameter returns information about the code path through +// the routine. Useful for debugging: +// +// set in is_ext_suspend_completed(): +// 0x00000001 - routine was entered +// 0x00000010 - routine return false at end +// 0x00000100 - thread exited (return false) +// 0x00000200 - suspend request cancelled (return false) +// 0x00000400 - thread suspended (return true) +// 0x00001000 - thread is in a suspend equivalent state (return true) +// 0x00002000 - thread is native and walkable (return true) +// 0x00004000 - thread is native_trans and walkable (needed retry) +// +// set in wait_for_ext_suspend_completion(): +// 0x00010000 - routine was entered +// 0x00020000 - suspend request cancelled before loop (return false) +// 0x00040000 - thread suspended before loop (return true) +// 0x00080000 - suspend request cancelled in loop (return false) +// 0x00100000 - thread suspended in loop (return true) +// 0x00200000 - suspend not completed during retry loop (return false) +// + +// Helper class for tracing suspend wait debug bits. +// +// 0x00000100 indicates that the target thread exited before it could +// self-suspend which is not a wait failure. 0x00000200, 0x00020000 and +// 0x00080000 each indicate a cancelled suspend request so they don't +// count as wait failures either. +#define DEBUG_FALSE_BITS (0x00000010 | 0x00200000) + +class TraceSuspendDebugBits : public StackObj { + private: + JavaThread * jt; + bool is_wait; + bool called_by_wait; // meaningful when !is_wait + uint32_t * bits; + + public: + TraceSuspendDebugBits(JavaThread *_jt, bool _is_wait, bool _called_by_wait, + uint32_t *_bits) { + jt = _jt; + is_wait = _is_wait; + called_by_wait = _called_by_wait; + bits = _bits; + } + + ~TraceSuspendDebugBits() { + if (!is_wait) { +#if 1 + // By default, don't trace bits for is_ext_suspend_completed() calls. + // That trace is very chatty. + return; +#else + if (!called_by_wait) { + // If tracing for is_ext_suspend_completed() is enabled, then only + // trace calls to it from wait_for_ext_suspend_completion() + return; + } +#endif + } + + if (AssertOnSuspendWaitFailure || TraceSuspendWaitFailures) { + if (bits != NULL && (*bits & DEBUG_FALSE_BITS) != 0) { + MutexLocker ml(Threads_lock); // needed for get_thread_name() + ResourceMark rm; + + tty->print_cr( + "Failed wait_for_ext_suspend_completion(thread=%s, debug_bits=%x)", + jt->get_thread_name(), *bits); + + guarantee(!AssertOnSuspendWaitFailure, "external suspend wait failed"); + } + } + } +}; +#undef DEBUG_FALSE_BITS + + +bool JavaThread::is_ext_suspend_completed(bool called_by_wait, int delay, uint32_t *bits) { + TraceSuspendDebugBits tsdb(this, false /* !is_wait */, called_by_wait, bits); + + bool did_trans_retry = false; // only do thread_in_native_trans retry once + bool do_trans_retry; // flag to force the retry + + *bits |= 0x00000001; + + do { + do_trans_retry = false; + + if (is_exiting()) { + // Thread is in the process of exiting. This is always checked + // first to reduce the risk of dereferencing a freed JavaThread. + *bits |= 0x00000100; + return false; + } + + if (!is_external_suspend()) { + // Suspend request is cancelled. This is always checked before + // is_ext_suspended() to reduce the risk of a rogue resume + // confusing the thread that made the suspend request. + *bits |= 0x00000200; + return false; + } + + if (is_ext_suspended()) { + // thread is suspended + *bits |= 0x00000400; + return true; + } + + // Now that we no longer do hard suspends of threads running + // native code, the target thread can be changing thread state + // while we are in this routine: + // + // _thread_in_native -> _thread_in_native_trans -> _thread_blocked + // + // We save a copy of the thread state as observed at this moment + // and make our decision about suspend completeness based on the + // copy. This closes the race where the thread state is seen as + // _thread_in_native_trans in the if-thread_blocked check, but is + // seen as _thread_blocked in if-thread_in_native_trans check. + JavaThreadState save_state = thread_state(); + + if (save_state == _thread_blocked && is_suspend_equivalent()) { + // If the thread's state is _thread_blocked and this blocking + // condition is known to be equivalent to a suspend, then we can + // consider the thread to be externally suspended. This means that + // the code that sets _thread_blocked has been modified to do + // self-suspension if the blocking condition releases. We also + // used to check for CONDVAR_WAIT here, but that is now covered by + // the _thread_blocked with self-suspension check. + // + // Return true since we wouldn't be here unless there was still an + // external suspend request. + *bits |= 0x00001000; + return true; + } else if (save_state == _thread_in_native && frame_anchor()->walkable()) { + // Threads running native code will self-suspend on native==>VM/Java + // transitions. If its stack is walkable (should always be the case + // unless this function is called before the actual java_suspend() + // call), then the wait is done. + *bits |= 0x00002000; + return true; + } else if (!called_by_wait && !did_trans_retry && + save_state == _thread_in_native_trans && + frame_anchor()->walkable()) { + // The thread is transitioning from thread_in_native to another + // thread state. check_safepoint_and_suspend_for_native_trans() + // will force the thread to self-suspend. If it hasn't gotten + // there yet we may have caught the thread in-between the native + // code check above and the self-suspend. Lucky us. If we were + // called by wait_for_ext_suspend_completion(), then it + // will be doing the retries so we don't have to. + // + // Since we use the saved thread state in the if-statement above, + // there is a chance that the thread has already transitioned to + // _thread_blocked by the time we get here. In that case, we will + // make a single unnecessary pass through the logic below. This + // doesn't hurt anything since we still do the trans retry. + + *bits |= 0x00004000; + + // Once the thread leaves thread_in_native_trans for another + // thread state, we break out of this retry loop. We shouldn't + // need this flag to prevent us from getting back here, but + // sometimes paranoia is good. + did_trans_retry = true; + + // We wait for the thread to transition to a more usable state. + for (int i = 1; i <= SuspendRetryCount; i++) { + // We used to do an "os::yield_all(i)" call here with the intention + // that yielding would increase on each retry. However, the parameter + // is ignored on Linux which means the yield didn't scale up. Waiting + // on the SR_lock below provides a much more predictable scale up for + // the delay. It also provides a simple/direct point to check for any + // safepoint requests from the VMThread + + // temporarily drops SR_lock while doing wait with safepoint check + // (if we're a JavaThread - the WatcherThread can also call this) + // and increase delay with each retry + SR_lock()->wait(!Thread::current()->is_Java_thread(), i * delay); + + // check the actual thread state instead of what we saved above + if (thread_state() != _thread_in_native_trans) { + // the thread has transitioned to another thread state so + // try all the checks (except this one) one more time. + do_trans_retry = true; + break; + } + } // end retry loop + + + } + } while (do_trans_retry); + + *bits |= 0x00000010; + return false; +} + +// +// Wait for an external suspend request to complete (or be cancelled). +// Returns true if the thread is externally suspended and false otherwise. +// +bool JavaThread::wait_for_ext_suspend_completion(int retries, int delay, + uint32_t *bits) { + TraceSuspendDebugBits tsdb(this, true /* is_wait */, + false /* !called_by_wait */, bits); + + // local flag copies to minimize SR_lock hold time + bool is_suspended; + bool pending; + uint32_t reset_bits; + + // set a marker so is_ext_suspend_completed() knows we are the caller + *bits |= 0x00010000; + + // We use reset_bits to reinitialize the bits value at the top of + // each retry loop. This allows the caller to make use of any + // unused bits for their own marking purposes. + reset_bits = *bits; + + { + MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + is_suspended = is_ext_suspend_completed(true /* called_by_wait */, + delay, bits); + pending = is_external_suspend(); + } + // must release SR_lock to allow suspension to complete + + if (!pending) { + // A cancelled suspend request is the only false return from + // is_ext_suspend_completed() that keeps us from entering the + // retry loop. + *bits |= 0x00020000; + return false; + } + + if (is_suspended) { + *bits |= 0x00040000; + return true; + } + + for (int i = 1; i <= retries; i++) { + *bits = reset_bits; // reinit to only track last retry + + // We used to do an "os::yield_all(i)" call here with the intention + // that yielding would increase on each retry. However, the parameter + // is ignored on Linux which means the yield didn't scale up. Waiting + // on the SR_lock below provides a much more predictable scale up for + // the delay. It also provides a simple/direct point to check for any + // safepoint requests from the VMThread + + { + MutexLocker ml(SR_lock()); + // wait with safepoint check (if we're a JavaThread - the WatcherThread + // can also call this) and increase delay with each retry + SR_lock()->wait(!Thread::current()->is_Java_thread(), i * delay); + + is_suspended = is_ext_suspend_completed(true /* called_by_wait */, + delay, bits); + + // It is possible for the external suspend request to be cancelled + // (by a resume) before the actual suspend operation is completed. + // Refresh our local copy to see if we still need to wait. + pending = is_external_suspend(); + } + + if (!pending) { + // A cancelled suspend request is the only false return from + // is_ext_suspend_completed() that keeps us from staying in the + // retry loop. + *bits |= 0x00080000; + return false; + } + + if (is_suspended) { + *bits |= 0x00100000; + return true; + } + } // end retry loop + + // thread did not suspend after all our retries + *bits |= 0x00200000; + return false; +} + +#ifndef PRODUCT +void JavaThread::record_jump(address target, address instr, const char* file, int line) { + + // This should not need to be atomic as the only way for simultaneous + // updates is via interrupts. Even then this should be rare or non-existant + // and we don't care that much anyway. + + int index = _jmp_ring_index; + _jmp_ring_index = (index + 1 ) & (jump_ring_buffer_size - 1); + _jmp_ring[index]._target = (intptr_t) target; + _jmp_ring[index]._instruction = (intptr_t) instr; + _jmp_ring[index]._file = file; + _jmp_ring[index]._line = line; +} +#endif /* PRODUCT */ + +// Called by flat profiler +// Callers have already called wait_for_ext_suspend_completion +// The assertion for that is currently too complex to put here: +bool JavaThread::profile_last_Java_frame(frame* _fr) { + bool gotframe = false; + // self suspension saves needed state. + if (has_last_Java_frame() && _anchor.walkable()) { + *_fr = pd_last_frame(); + gotframe = true; + } + return gotframe; +} + +void Thread::interrupt(Thread* thread) { + trace("interrupt", thread); + debug_only(check_for_dangling_thread_pointer(thread);) + os::interrupt(thread); +} + +bool Thread::is_interrupted(Thread* thread, bool clear_interrupted) { + trace("is_interrupted", thread); + debug_only(check_for_dangling_thread_pointer(thread);) + // Note: If clear_interrupted==false, this simply fetches and + // returns the value of the field osthread()->interrupted(). + return os::is_interrupted(thread, clear_interrupted); +} + + +// GC Support +bool Thread::claim_oops_do_par_case(int strong_roots_parity) { + jint thread_parity = _oops_do_parity; + if (thread_parity != strong_roots_parity) { + jint res = Atomic::cmpxchg(strong_roots_parity, &_oops_do_parity, thread_parity); + if (res == thread_parity) return true; + else { + guarantee(res == strong_roots_parity, "Or else what?"); + assert(SharedHeap::heap()->n_par_threads() > 0, + "Should only fail when parallel."); + return false; + } + } + assert(SharedHeap::heap()->n_par_threads() > 0, + "Should only fail when parallel."); + return false; +} + +void Thread::oops_do(OopClosure* f) { + active_handles()->oops_do(f); + // Do oop for ThreadShadow + f->do_oop((oop*)&_pending_exception); + handle_area()->oops_do(f); +} + +void Thread::nmethods_do() { +} + +void Thread::print_on(outputStream* st) const { + // get_priority assumes osthread initialized + if (osthread() != NULL) { + st->print("prio=%d tid=" INTPTR_FORMAT " ", get_priority(this), this); + osthread()->print_on(st); + } + debug_only(if (WizardMode) print_owned_locks_on(st);) +} + +// Thread::print_on_error() is called by fatal error handler. Don't use +// any lock or allocate memory. +void Thread::print_on_error(outputStream* st, char* buf, int buflen) const { + if (is_VM_thread()) st->print("VMThread"); + else if (is_Compiler_thread()) st->print("CompilerThread"); + else if (is_Java_thread()) st->print("JavaThread"); + else if (is_GC_task_thread()) st->print("GCTaskThread"); + else if (is_Watcher_thread()) st->print("WatcherThread"); + else if (is_ConcurrentGC_thread()) st->print("ConcurrentGCThread"); + else st->print("Thread"); + + st->print(" [stack: " PTR_FORMAT "," PTR_FORMAT "]", + _stack_base - _stack_size, _stack_base); + + if (osthread()) { + st->print(" [id=%d]", osthread()->thread_id()); + } +} + +#ifdef ASSERT +void Thread::print_owned_locks_on(outputStream* st) const { + Monitor *cur = _owned_locks; + if (cur == NULL) { + st->print(" (no locks) "); + } else { + st->print_cr(" Locks owned:"); + while(cur) { + cur->print_on(st); + cur = cur->next(); + } + } +} + +static int ref_use_count = 0; + +bool Thread::owns_locks_but_compiled_lock() const { + for(Monitor *cur = _owned_locks; cur; cur = cur->next()) { + if (cur != Compile_lock) return true; + } + return false; +} + + +#endif + +#ifndef PRODUCT + +// The flag: potential_vm_operation notifies if this particular safepoint state could potential +// invoke the vm-thread (i.e., and oop allocation). In that case, we also have to make sure that +// no threads which allow_vm_block's are held +void Thread::check_for_valid_safepoint_state(bool potential_vm_operation) { + // Check if current thread is allowed to block at a safepoint + if (!(_allow_safepoint_count == 0)) + fatal("Possible safepoint reached by thread that does not allow it"); + if (is_Java_thread() && ((JavaThread*)this)->thread_state() != _thread_in_vm) { + fatal("LEAF method calling lock?"); + } + +#ifdef ASSERT + if (potential_vm_operation && is_Java_thread() + && !Universe::is_bootstrapping()) { + // Make sure we do not hold any locks that the VM thread also uses. + // This could potentially lead to deadlocks + for(Monitor *cur = _owned_locks; cur; cur = cur->next()) { + // Threads_lock is special, since the safepoint synchronization will not start before this is + // acquired. Hence, a JavaThread cannot be holding it at a safepoint. So is VMOperationRequest_lock, + // since it is used to transfer control between JavaThreads and the VMThread + // Do not *exclude* any locks unless you are absolutly sure it is correct. Ask someone else first! + if ( (cur->allow_vm_block() && + cur != Threads_lock && + cur != Compile_lock && // Temporary: should not be necessary when we get spearate compilation + cur != VMOperationRequest_lock && + cur != VMOperationQueue_lock) || + cur->rank() == Mutex::special) { + warning("Thread holding lock at safepoint that vm can block on: %s", cur->name()); + } + } + } + + if (GCALotAtAllSafepoints) { + // We could enter a safepoint here and thus have a gc + InterfaceSupport::check_gc_alot(); + } + +#endif +} +#endif + +bool Thread::lock_is_in_stack(address adr) const { + assert(Thread::current() == this, "lock_is_in_stack can only be called from current thread"); + // High limit: highest_lock is set during thread execution + // Low limit: address of the local variable dummy, rounded to 4K boundary. + // (The rounding helps finding threads in unsafe mode, even if the particular stack + // frame has been popped already. Correct as long as stacks are at least 4K long and aligned.) + address end = os::current_stack_pointer(); + if (_highest_lock >= adr && adr >= end) return true; + + return false; +} + + +bool Thread::is_in_stack(address adr) const { + assert(Thread::current() == this, "is_in_stack can only be called from current thread"); + address end = os::current_stack_pointer(); + if (stack_base() >= adr && adr >= end) return true; + + return false; +} + + +// We had to move these methods here, because vm threads get into ObjectSynchronizer::enter +// However, there is a note in JavaThread::is_lock_owned() about the VM threads not being +// used for compilation in the future. If that change is made, the need for these methods +// should be revisited, and they should be removed if possible. + +bool Thread::is_lock_owned(address adr) const { + if (lock_is_in_stack(adr) ) return true; + return false; +} + +bool Thread::set_as_starting_thread() { + // NOTE: this must be called inside the main thread. + return os::create_main_thread((JavaThread*)this); +} + +static void initialize_class(symbolHandle class_name, TRAPS) { + klassOop klass = SystemDictionary::resolve_or_fail(class_name, true, CHECK); + instanceKlass::cast(klass)->initialize(CHECK); +} + + +// Creates the initial ThreadGroup +static Handle create_initial_thread_group(TRAPS) { + klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_ThreadGroup(), true, CHECK_NH); + instanceKlassHandle klass (THREAD, k); + + Handle system_instance = klass->allocate_instance_handle(CHECK_NH); + { + JavaValue result(T_VOID); + JavaCalls::call_special(&result, + system_instance, + klass, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::void_method_signature(), + CHECK_NH); + } + Universe::set_system_thread_group(system_instance()); + + Handle main_instance = klass->allocate_instance_handle(CHECK_NH); + { + JavaValue result(T_VOID); + Handle string = java_lang_String::create_from_str("main", CHECK_NH); + JavaCalls::call_special(&result, + main_instance, + klass, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::threadgroup_string_void_signature(), + system_instance, + string, + CHECK_NH); + } + return main_instance; +} + +// Creates the initial Thread +static oop create_initial_thread(Handle thread_group, JavaThread* thread, TRAPS) { + klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_Thread(), true, CHECK_NULL); + instanceKlassHandle klass (THREAD, k); + instanceHandle thread_oop = klass->allocate_instance_handle(CHECK_NULL); + + java_lang_Thread::set_thread(thread_oop(), thread); + java_lang_Thread::set_priority(thread_oop(), NormPriority); + thread->set_threadObj(thread_oop()); + + Handle string = java_lang_String::create_from_str("main", CHECK_NULL); + + JavaValue result(T_VOID); + JavaCalls::call_special(&result, thread_oop, + klass, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::threadgroup_string_void_signature(), + thread_group, + string, + CHECK_NULL); + return thread_oop(); +} + +static void call_initializeSystemClass(TRAPS) { + klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_System(), true, CHECK); + instanceKlassHandle klass (THREAD, k); + + JavaValue result(T_VOID); + JavaCalls::call_static(&result, klass, vmSymbolHandles::initializeSystemClass_name(), + vmSymbolHandles::void_method_signature(), CHECK); +} + +static void reset_vm_info_property(TRAPS) { + // the vm info string + ResourceMark rm(THREAD); + const char *vm_info = VM_Version::vm_info_string(); + + // java.lang.System class + klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_System(), true, CHECK); + instanceKlassHandle klass (THREAD, k); + + // setProperty arguments + Handle key_str = java_lang_String::create_from_str("java.vm.info", CHECK); + Handle value_str = java_lang_String::create_from_str(vm_info, CHECK); + + // return value + JavaValue r(T_OBJECT); + + // public static String setProperty(String key, String value); + JavaCalls::call_static(&r, + klass, + vmSymbolHandles::setProperty_name(), + vmSymbolHandles::string_string_string_signature(), + key_str, + value_str, + CHECK); +} + + +void JavaThread::allocate_threadObj(Handle thread_group, char* thread_name, bool daemon, TRAPS) { + assert(thread_group.not_null(), "thread group should be specified"); + assert(threadObj() == NULL, "should only create Java thread object once"); + + klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_Thread(), true, CHECK); + instanceKlassHandle klass (THREAD, k); + instanceHandle thread_oop = klass->allocate_instance_handle(CHECK); + + java_lang_Thread::set_thread(thread_oop(), this); + java_lang_Thread::set_priority(thread_oop(), NormPriority); + set_threadObj(thread_oop()); + + JavaValue result(T_VOID); + if (thread_name != NULL) { + Handle name = java_lang_String::create_from_str(thread_name, CHECK); + // Thread gets assigned specified name and null target + JavaCalls::call_special(&result, + thread_oop, + klass, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::threadgroup_string_void_signature(), + thread_group, // Argument 1 + name, // Argument 2 + THREAD); + } else { + // Thread gets assigned name "Thread-nnn" and null target + // (java.lang.Thread doesn't have a constructor taking only a ThreadGroup argument) + JavaCalls::call_special(&result, + thread_oop, + klass, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::threadgroup_runnable_void_signature(), + thread_group, // Argument 1 + Handle(), // Argument 2 + THREAD); + } + + + if (daemon) { + java_lang_Thread::set_daemon(thread_oop()); + } + + if (HAS_PENDING_EXCEPTION) { + return; + } + + KlassHandle group(this, SystemDictionary::threadGroup_klass()); + Handle threadObj(this, this->threadObj()); + + JavaCalls::call_special(&result, + thread_group, + group, + vmSymbolHandles::add_method_name(), + vmSymbolHandles::thread_void_signature(), + threadObj, // Arg 1 + THREAD); + + +} + +// NamedThread -- non-JavaThread subclasses with multiple +// uniquely named instances should derive from this. +NamedThread::NamedThread() : Thread() { + _name = NULL; +} + +NamedThread::~NamedThread() { + if (_name != NULL) { + FREE_C_HEAP_ARRAY(char, _name); + _name = NULL; + } +} + +void NamedThread::set_name(const char* format, ...) { + guarantee(_name == NULL, "Only get to set name once."); + _name = NEW_C_HEAP_ARRAY(char, max_name_len); + guarantee(_name != NULL, "alloc failure"); + va_list ap; + va_start(ap, format); + jio_vsnprintf(_name, max_name_len, format, ap); + va_end(ap); +} + +// ======= WatcherThread ======== + +// The watcher thread exists to simulate timer interrupts. It should +// be replaced by an abstraction over whatever native support for +// timer interrupts exists on the platform. + +WatcherThread* WatcherThread::_watcher_thread = NULL; +bool WatcherThread::_should_terminate = false; + +WatcherThread::WatcherThread() : Thread() { + assert(watcher_thread() == NULL, "we can only allocate one WatcherThread"); + if (os::create_thread(this, os::watcher_thread)) { + _watcher_thread = this; + + // Set the watcher thread to the highest OS priority which should not be + // used, unless a Java thread with priority java.lang.Thread.MAX_PRIORITY + // is created. The only normal thread using this priority is the reference + // handler thread, which runs for very short intervals only. + // If the VMThread's priority is not lower than the WatcherThread profiling + // will be inaccurate. + os::set_priority(this, MaxPriority); + if (!DisableStartThread) { + os::start_thread(this); + } + } +} + +void WatcherThread::run() { + assert(this == watcher_thread(), "just checking"); + + this->record_stack_base_and_size(); + this->initialize_thread_local_storage(); + this->set_active_handles(JNIHandleBlock::allocate_block()); + while(!_should_terminate) { + assert(watcher_thread() == Thread::current(), "thread consistency check"); + assert(watcher_thread() == this, "thread consistency check"); + + // Calculate how long it'll be until the next PeriodicTask work + // should be done, and sleep that amount of time. + const size_t time_to_wait = PeriodicTask::time_to_wait(); + os::sleep(this, time_to_wait, false); + + if (is_error_reported()) { + // A fatal error has happened, the error handler(VMError::report_and_die) + // should abort JVM after creating an error log file. However in some + // rare cases, the error handler itself might deadlock. Here we try to + // kill JVM if the fatal error handler fails to abort in 2 minutes. + // + // This code is in WatcherThread because WatcherThread wakes up + // periodically so the fatal error handler doesn't need to do anything; + // also because the WatcherThread is less likely to crash than other + // threads. + + for (;;) { + if (!ShowMessageBoxOnError + && (OnError == NULL || OnError[0] == '\0') + && Arguments::abort_hook() == NULL) { + os::sleep(this, 2 * 60 * 1000, false); + fdStream err(defaultStream::output_fd()); + err.print_raw_cr("# [ timer expired, abort... ]"); + // skip atexit/vm_exit/vm_abort hooks + os::die(); + } + + // Wake up 5 seconds later, the fatal handler may reset OnError or + // ShowMessageBoxOnError when it is ready to abort. + os::sleep(this, 5 * 1000, false); + } + } + + PeriodicTask::real_time_tick(time_to_wait); + + // If we have no more tasks left due to dynamic disenrollment, + // shut down the thread since we don't currently support dynamic enrollment + if (PeriodicTask::num_tasks() == 0) { + _should_terminate = true; + } + } + + // Signal that it is terminated + { + MutexLockerEx mu(Terminator_lock, Mutex::_no_safepoint_check_flag); + _watcher_thread = NULL; + Terminator_lock->notify(); + } + + // Thread destructor usually does this.. + ThreadLocalStorage::set_thread(NULL); +} + +void WatcherThread::start() { + if (watcher_thread() == NULL) { + _should_terminate = false; + // Create the single instance of WatcherThread + new WatcherThread(); + } +} + +void WatcherThread::stop() { + // it is ok to take late safepoints here, if needed + MutexLocker mu(Terminator_lock); + _should_terminate = true; + while(watcher_thread() != NULL) { + // This wait should make safepoint checks, wait without a timeout, + // and wait as a suspend-equivalent condition. + // + // Note: If the FlatProfiler is running, then this thread is waiting + // for the WatcherThread to terminate and the WatcherThread, via the + // FlatProfiler task, is waiting for the external suspend request on + // this thread to complete. wait_for_ext_suspend_completion() will + // eventually timeout, but that takes time. Making this wait a + // suspend-equivalent condition solves that timeout problem. + // + Terminator_lock->wait(!Mutex::_no_safepoint_check_flag, 0, + Mutex::_as_suspend_equivalent_flag); + } +} + +void WatcherThread::print_on(outputStream* st) const { + st->print("\"%s\" ", name()); + Thread::print_on(st); + st->cr(); +} + +// ======= JavaThread ======== + +// A JavaThread is a normal Java thread + +void JavaThread::initialize() { + // Initialize fields + set_saved_exception_pc(NULL); + set_threadObj(NULL); + _anchor.clear(); + set_entry_point(NULL); + set_jni_functions(jni_functions()); + set_callee_target(NULL); + set_vm_result(NULL); + set_vm_result_2(NULL); + set_vframe_array_head(NULL); + set_vframe_array_last(NULL); + set_deferred_locals(NULL); + set_deopt_mark(NULL); + clear_must_deopt_id(); + set_monitor_chunks(NULL); + set_next(NULL); + set_thread_state(_thread_new); + _terminated = _not_terminated; + _privileged_stack_top = NULL; + _array_for_gc = NULL; + _suspend_equivalent = false; + _in_deopt_handler = 0; + _doing_unsafe_access = false; + _stack_guard_state = stack_guard_unused; + _exception_oop = NULL; + _exception_pc = 0; + _exception_handler_pc = 0; + _exception_stack_size = 0; + _jvmti_thread_state= NULL; + _jvmti_get_loaded_classes_closure = NULL; + _interp_only_mode = 0; + _special_runtime_exit_condition = _no_async_condition; + _pending_async_exception = NULL; + _is_compiling = false; + _thread_stat = NULL; + _thread_stat = new ThreadStatistics(); + _blocked_on_compilation = false; + _jni_active_critical = 0; + _do_not_unlock_if_synchronized = false; + _cached_monitor_info = NULL; + _parker = Parker::Allocate(this) ; + +#ifndef PRODUCT + _jmp_ring_index = 0; + for (int ji = 0 ; ji < jump_ring_buffer_size ; ji++ ) { + record_jump(NULL, NULL, NULL, 0); + } +#endif /* PRODUCT */ + + set_thread_profiler(NULL); + if (FlatProfiler::is_active()) { + // This is where we would decide to either give each thread it's own profiler + // or use one global one from FlatProfiler, + // or up to some count of the number of profiled threads, etc. + ThreadProfiler* pp = new ThreadProfiler(); + pp->engage(); + set_thread_profiler(pp); + } + + // Setup safepoint state info for this thread + ThreadSafepointState::create(this); + + debug_only(_java_call_counter = 0); + + // JVMTI PopFrame support + _popframe_condition = popframe_inactive; + _popframe_preserved_args = NULL; + _popframe_preserved_args_size = 0; + + pd_initialize(); +} + +JavaThread::JavaThread(bool is_attaching) : Thread() { + initialize(); + _is_attaching = is_attaching; +} + +bool JavaThread::reguard_stack(address cur_sp) { + if (_stack_guard_state != stack_guard_yellow_disabled) { + return true; // Stack already guarded or guard pages not needed. + } + + if (register_stack_overflow()) { + // For those architectures which have separate register and + // memory stacks, we must check the register stack to see if + // it has overflowed. + return false; + } + + // Java code never executes within the yellow zone: the latter is only + // there to provoke an exception during stack banging. If java code + // is executing there, either StackShadowPages should be larger, or + // some exception code in c1, c2 or the interpreter isn't unwinding + // when it should. + guarantee(cur_sp > stack_yellow_zone_base(), "not enough space to reguard - increase StackShadowPages"); + + enable_stack_yellow_zone(); + return true; +} + +bool JavaThread::reguard_stack(void) { + return reguard_stack(os::current_stack_pointer()); +} + + +void JavaThread::block_if_vm_exited() { + if (_terminated == _vm_exited) { + // _vm_exited is set at safepoint, and Threads_lock is never released + // we will block here forever + Threads_lock->lock_without_safepoint_check(); + ShouldNotReachHere(); + } +} + + +// Remove this ifdef when C1 is ported to the compiler interface. +static void compiler_thread_entry(JavaThread* thread, TRAPS); + +JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : Thread() { + if (TraceThreadEvents) { + tty->print_cr("creating thread %p", this); + } + initialize(); + _is_attaching = false; + set_entry_point(entry_point); + // Create the native thread itself. + // %note runtime_23 + os::ThreadType thr_type = os::java_thread; + thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread : + os::java_thread; + os::create_thread(this, thr_type, stack_sz); + + // The _osthread may be NULL here because we ran out of memory (too many threads active). + // We need to throw and OutOfMemoryError - however we cannot do this here because the caller + // may hold a lock and all locks must be unlocked before throwing the exception (throwing + // the exception consists of creating the exception object & initializing it, initialization + // will leave the VM via a JavaCall and then all locks must be unlocked). + // + // The thread is still suspended when we reach here. Thread must be explicit started + // by creator! Furthermore, the thread must also explicitly be added to the Threads list + // by calling Threads:add. The reason why this is not done here, is because the thread + // object must be fully initialized (take a look at JVM_Start) +} + +JavaThread::~JavaThread() { + if (TraceThreadEvents) { + tty->print_cr("terminate thread %p", this); + } + + // JSR166 -- return the parker to the free list + Parker::Release(_parker); + _parker = NULL ; + + // Free any remaining previous UnrollBlock + vframeArray* old_array = vframe_array_last(); + + if (old_array != NULL) { + Deoptimization::UnrollBlock* old_info = old_array->unroll_block(); + old_array->set_unroll_block(NULL); + delete old_info; + delete old_array; + } + + GrowableArray* deferred = deferred_locals(); + if (deferred != NULL) { + // This can only happen if thread is destroyed before deoptimization occurs. + assert(deferred->length() != 0, "empty array!"); + do { + jvmtiDeferredLocalVariableSet* dlv = deferred->at(0); + deferred->remove_at(0); + // individual jvmtiDeferredLocalVariableSet are CHeapObj's + delete dlv; + } while (deferred->length() != 0); + delete deferred; + } + + // All Java related clean up happens in exit + ThreadSafepointState::destroy(this); + if (_thread_profiler != NULL) delete _thread_profiler; + if (_thread_stat != NULL) delete _thread_stat; + + if (jvmti_thread_state() != NULL) { + JvmtiExport::cleanup_thread(this); + } +} + + +// The first routine called by a new Java thread +void JavaThread::run() { + // initialize thread-local alloc buffer related fields + this->initialize_tlab(); + + // used to test validitity of stack trace backs + this->record_base_of_stack_pointer(); + + // Record real stack base and size. + this->record_stack_base_and_size(); + + // Initialize thread local storage; set before calling MutexLocker + this->initialize_thread_local_storage(); + + this->create_stack_guard_pages(); + + // Thread is now sufficient initialized to be handled by the safepoint code as being + // in the VM. Change thread state from _thread_new to _thread_in_vm + ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm); + + assert(JavaThread::current() == this, "sanity check"); + assert(!Thread::current()->owns_locks(), "sanity check"); + + DTRACE_THREAD_PROBE(start, this); + + // This operation might block. We call that after all safepoint checks for a new thread has + // been completed. + this->set_active_handles(JNIHandleBlock::allocate_block()); + + if (JvmtiExport::should_post_thread_life()) { + JvmtiExport::post_thread_start(this); + } + + // We call another function to do the rest so we are sure that the stack addresses used + // from there will be lower than the stack base just computed + thread_main_inner(); + + // Note, thread is no longer valid at this point! +} + + +void JavaThread::thread_main_inner() { + assert(JavaThread::current() == this, "sanity check"); + assert(this->threadObj() != NULL, "just checking"); + + // Execute thread entry point. If this thread is being asked to restart, + // or has been stopped before starting, do not reexecute entry point. + // Note: Due to JVM_StopThread we can have pending exceptions already! + if (!this->has_pending_exception() && !java_lang_Thread::is_stillborn(this->threadObj())) { + // enter the thread's entry point only if we have no pending exceptions + HandleMark hm(this); + this->entry_point()(this, this); + } + + DTRACE_THREAD_PROBE(stop, this); + + this->exit(false); + delete this; +} + + +static void ensure_join(JavaThread* thread) { + // We do not need to grap the Threads_lock, since we are operating on ourself. + Handle threadObj(thread, thread->threadObj()); + assert(threadObj.not_null(), "java thread object must exist"); + ObjectLocker lock(threadObj, thread); + // Ignore pending exception (ThreadDeath), since we are exiting anyway + thread->clear_pending_exception(); + // It is of profound importance that we set the stillborn bit and reset the thread object, + // before we do the notify. Since, changing these two variable will make JVM_IsAlive return + // false. So in case another thread is doing a join on this thread , it will detect that the thread + // is dead when it gets notified. + java_lang_Thread::set_stillborn(threadObj()); + // Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED. + java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); + java_lang_Thread::set_thread(threadObj(), NULL); + lock.notify_all(thread); + // Ignore pending exception (ThreadDeath), since we are exiting anyway + thread->clear_pending_exception(); +} + +// For any new cleanup additions, please check to see if they need to be applied to +// cleanup_failed_attach_current_thread as well. +void JavaThread::exit(bool destroy_vm, ExitType exit_type) { + assert(this == JavaThread::current(), "thread consistency check"); + if (!InitializeJavaLangSystem) return; + + HandleMark hm(this); + Handle uncaught_exception(this, this->pending_exception()); + this->clear_pending_exception(); + Handle threadObj(this, this->threadObj()); + assert(threadObj.not_null(), "Java thread object should be created"); + + if (get_thread_profiler() != NULL) { + get_thread_profiler()->disengage(); + ResourceMark rm; + get_thread_profiler()->print(get_thread_name()); + } + + + // FIXIT: This code should be moved into else part, when reliable 1.2/1.3 check is in place + { + EXCEPTION_MARK; + + CLEAR_PENDING_EXCEPTION; + } + // FIXIT: The is_null check is only so it works better on JDK1.2 VM's. This + // has to be fixed by a runtime query method + if (!destroy_vm || JDK_Version::is_jdk12x_version()) { + // JSR-166: change call from from ThreadGroup.uncaughtException to + // java.lang.Thread.dispatchUncaughtException + if (uncaught_exception.not_null()) { + Handle group(this, java_lang_Thread::threadGroup(threadObj())); + Events::log("uncaught exception INTPTR_FORMAT " " INTPTR_FORMAT " " INTPTR_FORMAT", + (address)uncaught_exception(), (address)threadObj(), (address)group()); + { + EXCEPTION_MARK; + // Check if the method Thread.dispatchUncaughtException() exists. If so + // call it. Otherwise we have an older library without the JSR-166 changes, + // so call ThreadGroup.uncaughtException() + KlassHandle recvrKlass(THREAD, threadObj->klass()); + CallInfo callinfo; + KlassHandle thread_klass(THREAD, SystemDictionary::thread_klass()); + LinkResolver::resolve_virtual_call(callinfo, threadObj, recvrKlass, thread_klass, + vmSymbolHandles::dispatchUncaughtException_name(), + vmSymbolHandles::throwable_void_signature(), + KlassHandle(), false, false, THREAD); + CLEAR_PENDING_EXCEPTION; + methodHandle method = callinfo.selected_method(); + if (method.not_null()) { + JavaValue result(T_VOID); + JavaCalls::call_virtual(&result, + threadObj, thread_klass, + vmSymbolHandles::dispatchUncaughtException_name(), + vmSymbolHandles::throwable_void_signature(), + uncaught_exception, + THREAD); + } else { + KlassHandle thread_group(THREAD, SystemDictionary::threadGroup_klass()); + JavaValue result(T_VOID); + JavaCalls::call_virtual(&result, + group, thread_group, + vmSymbolHandles::uncaughtException_name(), + vmSymbolHandles::thread_throwable_void_signature(), + threadObj, // Arg 1 + uncaught_exception, // Arg 2 + THREAD); + } + CLEAR_PENDING_EXCEPTION; + } + } + + // Call Thread.exit(). We try 3 times in case we got another Thread.stop during + // the execution of the method. If that is not enough, then we don't really care. Thread.stop + // is deprecated anyhow. + { int count = 3; + while (java_lang_Thread::threadGroup(threadObj()) != NULL && (count-- > 0)) { + EXCEPTION_MARK; + JavaValue result(T_VOID); + KlassHandle thread_klass(THREAD, SystemDictionary::thread_klass()); + JavaCalls::call_virtual(&result, + threadObj, thread_klass, + vmSymbolHandles::exit_method_name(), + vmSymbolHandles::void_method_signature(), + THREAD); + CLEAR_PENDING_EXCEPTION; + } + } + + // notify JVMTI + if (JvmtiExport::should_post_thread_life()) { + JvmtiExport::post_thread_end(this); + } + + // We have notified the agents that we are exiting, before we go on, + // we must check for a pending external suspend request and honor it + // in order to not surprise the thread that made the suspend request. + while (true) { + { + MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + if (!is_external_suspend()) { + set_terminated(_thread_exiting); + ThreadService::current_thread_exiting(this); + break; + } + // Implied else: + // Things get a little tricky here. We have a pending external + // suspend request, but we are holding the SR_lock so we + // can't just self-suspend. So we temporarily drop the lock + // and then self-suspend. + } + + ThreadBlockInVM tbivm(this); + java_suspend_self(); + + // We're done with this suspend request, but we have to loop around + // and check again. Eventually we will get SR_lock without a pending + // external suspend request and will be able to mark ourselves as + // exiting. + } + // no more external suspends are allowed at this point + } else { + // before_exit() has already posted JVMTI THREAD_END events + } + + // Notify waiters on thread object. This has to be done after exit() is called + // on the thread (if the thread is the last thread in a daemon ThreadGroup the + // group should have the destroyed bit set before waiters are notified). + ensure_join(this); + assert(!this->has_pending_exception(), "ensure_join should have cleared"); + + // 6282335 JNI DetachCurrentThread spec states that all Java monitors + // held by this thread must be released. A detach operation must only + // get here if there are no Java frames on the stack. Therefore, any + // owned monitors at this point MUST be JNI-acquired monitors which are + // pre-inflated and in the monitor cache. + // + // ensure_join() ignores IllegalThreadStateExceptions, and so does this. + if (exit_type == jni_detach && JNIDetachReleasesMonitors) { + assert(!this->has_last_Java_frame(), "detaching with Java frames?"); + ObjectSynchronizer::release_monitors_owned_by_thread(this); + assert(!this->has_pending_exception(), "release_monitors should have cleared"); + } + + // These things needs to be done while we are still a Java Thread. Make sure that thread + // is in a consistent state, in case GC happens + assert(_privileged_stack_top == NULL, "must be NULL when we get here"); + + if (active_handles() != NULL) { + JNIHandleBlock* block = active_handles(); + set_active_handles(NULL); + JNIHandleBlock::release_block(block); + } + + if (free_handle_block() != NULL) { + JNIHandleBlock* block = free_handle_block(); + set_free_handle_block(NULL); + JNIHandleBlock::release_block(block); + } + + // These have to be removed while this is still a valid thread. + remove_stack_guard_pages(); + + if (UseTLAB) { + tlab().make_parsable(true); // retire TLAB + } + + // Remove from list of active threads list, and notify VM thread if we are the last non-daemon thread + Threads::remove(this); +} + +void JavaThread::cleanup_failed_attach_current_thread() { + + if (get_thread_profiler() != NULL) { + get_thread_profiler()->disengage(); + ResourceMark rm; + get_thread_profiler()->print(get_thread_name()); + } + + if (active_handles() != NULL) { + JNIHandleBlock* block = active_handles(); + set_active_handles(NULL); + JNIHandleBlock::release_block(block); + } + + if (free_handle_block() != NULL) { + JNIHandleBlock* block = free_handle_block(); + set_free_handle_block(NULL); + JNIHandleBlock::release_block(block); + } + + if (UseTLAB) { + tlab().make_parsable(true); // retire TLAB, if any + } + + Threads::remove(this); + delete this; +} + + +JavaThread* JavaThread::active() { + Thread* thread = ThreadLocalStorage::thread(); + assert(thread != NULL, "just checking"); + if (thread->is_Java_thread()) { + return (JavaThread*) thread; + } else { + assert(thread->is_VM_thread(), "this must be a vm thread"); + VM_Operation* op = ((VMThread*) thread)->vm_operation(); + JavaThread *ret=op == NULL ? NULL : (JavaThread *)op->calling_thread(); + assert(ret->is_Java_thread(), "must be a Java thread"); + return ret; + } +} + +bool JavaThread::is_lock_owned(address adr) const { + if (lock_is_in_stack(adr)) return true; + + for (MonitorChunk* chunk = monitor_chunks(); chunk != NULL; chunk = chunk->next()) { + if (chunk->contains(adr)) return true; + } + + return false; +} + + +void JavaThread::add_monitor_chunk(MonitorChunk* chunk) { + chunk->set_next(monitor_chunks()); + set_monitor_chunks(chunk); +} + +void JavaThread::remove_monitor_chunk(MonitorChunk* chunk) { + guarantee(monitor_chunks() != NULL, "must be non empty"); + if (monitor_chunks() == chunk) { + set_monitor_chunks(chunk->next()); + } else { + MonitorChunk* prev = monitor_chunks(); + while (prev->next() != chunk) prev = prev->next(); + prev->set_next(chunk->next()); + } +} + +// JVM support. + +// Note: this function shouldn't block if it's called in +// _thread_in_native_trans state (such as from +// check_special_condition_for_native_trans()). +void JavaThread::check_and_handle_async_exceptions(bool check_unsafe_error) { + + if (has_last_Java_frame() && has_async_condition()) { + // If we are at a polling page safepoint (not a poll return) + // then we must defer async exception because live registers + // will be clobbered by the exception path. Poll return is + // ok because the call we a returning from already collides + // with exception handling registers and so there is no issue. + // (The exception handling path kills call result registers but + // this is ok since the exception kills the result anyway). + + if (is_at_poll_safepoint()) { + // if the code we are returning to has deoptimized we must defer + // the exception otherwise live registers get clobbered on the + // exception path before deoptimization is able to retrieve them. + // + RegisterMap map(this, false); + frame caller_fr = last_frame().sender(&map); + assert(caller_fr.is_compiled_frame(), "what?"); + if (caller_fr.is_deoptimized_frame()) { + if (TraceExceptions) { + ResourceMark rm; + tty->print_cr("deferred async exception at compiled safepoint"); + } + return; + } + } + } + + JavaThread::AsyncRequests condition = clear_special_runtime_exit_condition(); + if (condition == _no_async_condition) { + // Conditions have changed since has_special_runtime_exit_condition() + // was called: + // - if we were here only because of an external suspend request, + // then that was taken care of above (or cancelled) so we are done + // - if we were here because of another async request, then it has + // been cleared between the has_special_runtime_exit_condition() + // and now so again we are done + return; + } + + // Check for pending async. exception + if (_pending_async_exception != NULL) { + // Only overwrite an already pending exception, if it is not a threadDeath. + if (!has_pending_exception() || !pending_exception()->is_a(SystemDictionary::threaddeath_klass())) { + + // We cannot call Exceptions::_throw(...) here because we cannot block + set_pending_exception(_pending_async_exception, __FILE__, __LINE__); + + if (TraceExceptions) { + ResourceMark rm; + tty->print("Async. exception installed at runtime exit (" INTPTR_FORMAT ")", this); + if (has_last_Java_frame() ) { + frame f = last_frame(); + tty->print(" (pc: " INTPTR_FORMAT " sp: " INTPTR_FORMAT " )", f.pc(), f.sp()); + } + tty->print_cr(" of type: %s", instanceKlass::cast(_pending_async_exception->klass())->external_name()); + } + _pending_async_exception = NULL; + clear_has_async_exception(); + } + } + + if (check_unsafe_error && + condition == _async_unsafe_access_error && !has_pending_exception()) { + condition = _no_async_condition; // done + switch (thread_state()) { + case _thread_in_vm: + { + JavaThread* THREAD = this; + THROW_MSG(vmSymbols::java_lang_InternalError(), "a fault occurred in an unsafe memory access operation"); + } + case _thread_in_native: + { + ThreadInVMfromNative tiv(this); + JavaThread* THREAD = this; + THROW_MSG(vmSymbols::java_lang_InternalError(), "a fault occurred in an unsafe memory access operation"); + } + case _thread_in_Java: + { + ThreadInVMfromJava tiv(this); + JavaThread* THREAD = this; + THROW_MSG(vmSymbols::java_lang_InternalError(), "a fault occurred in a recent unsafe memory access operation in compiled Java code"); + } + default: + ShouldNotReachHere(); + } + } + + assert(condition == _no_async_condition || has_pending_exception() || + (!check_unsafe_error && condition == _async_unsafe_access_error), + "must have handled the async condition, if no exception"); +} + +void JavaThread::handle_special_runtime_exit_condition(bool check_asyncs) { + // + // Check for pending external suspend. Internal suspend requests do + // not use handle_special_runtime_exit_condition(). + // If JNIEnv proxies are allowed, don't self-suspend if the target + // thread is not the current thread. In older versions of jdbx, jdbx + // threads could call into the VM with another thread's JNIEnv so we + // can be here operating on behalf of a suspended thread (4432884). + bool do_self_suspend = is_external_suspend_with_lock(); + if (do_self_suspend && (!AllowJNIEnvProxy || this == JavaThread::current())) { + // + // Because thread is external suspended the safepoint code will count + // thread as at a safepoint. This can be odd because we can be here + // as _thread_in_Java which would normally transition to _thread_blocked + // at a safepoint. We would like to mark the thread as _thread_blocked + // before calling java_suspend_self like all other callers of it but + // we must then observe proper safepoint protocol. (We can't leave + // _thread_blocked with a safepoint in progress). However we can be + // here as _thread_in_native_trans so we can't use a normal transition + // constructor/destructor pair because they assert on that type of + // transition. We could do something like: + // + // JavaThreadState state = thread_state(); + // set_thread_state(_thread_in_vm); + // { + // ThreadBlockInVM tbivm(this); + // java_suspend_self() + // } + // set_thread_state(_thread_in_vm_trans); + // if (safepoint) block; + // set_thread_state(state); + // + // but that is pretty messy. Instead we just go with the way the + // code has worked before and note that this is the only path to + // java_suspend_self that doesn't put the thread in _thread_blocked + // mode. + + frame_anchor()->make_walkable(this); + java_suspend_self(); + + // We might be here for reasons in addition to the self-suspend request + // so check for other async requests. + } + + if (check_asyncs) { + check_and_handle_async_exceptions(); + } +} + +void JavaThread::send_thread_stop(oop java_throwable) { + assert(Thread::current()->is_VM_thread(), "should be in the vm thread"); + assert(Threads_lock->is_locked(), "Threads_lock should be locked by safepoint code"); + assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped"); + + // Do not throw asynchronous exceptions against the compiler thread + // (the compiler thread should not be a Java thread -- fix in 1.4.2) + if (is_Compiler_thread()) return; + + // This is a change from JDK 1.1, but JDK 1.2 will also do it: + if (java_throwable->is_a(SystemDictionary::threaddeath_klass())) { + java_lang_Thread::set_stillborn(threadObj()); + } + + { + // Actually throw the Throwable against the target Thread - however + // only if there is no thread death exception installed already. + if (_pending_async_exception == NULL || !_pending_async_exception->is_a(SystemDictionary::threaddeath_klass())) { + // If the topmost frame is a runtime stub, then we are calling into + // OptoRuntime from compiled code. Some runtime stubs (new, monitor_exit..) + // must deoptimize the caller before continuing, as the compiled exception handler table + // may not be valid + if (has_last_Java_frame()) { + frame f = last_frame(); + if (f.is_runtime_frame() || f.is_safepoint_blob_frame()) { + // BiasedLocking needs an updated RegisterMap for the revoke monitors pass + RegisterMap reg_map(this, UseBiasedLocking); + frame compiled_frame = f.sender(®_map); + if (compiled_frame.can_be_deoptimized()) { + Deoptimization::deoptimize(this, compiled_frame, ®_map); + } + } + } + + // Set async. pending exception in thread. + set_pending_async_exception(java_throwable); + + if (TraceExceptions) { + ResourceMark rm; + tty->print_cr("Pending Async. exception installed of type: %s", instanceKlass::cast(_pending_async_exception->klass())->external_name()); + } + // for AbortVMOnException flag + NOT_PRODUCT(Exceptions::debug_check_abort(instanceKlass::cast(_pending_async_exception->klass())->external_name())); + } + } + + + // Interrupt thread so it will wake up from a potential wait() + Thread::interrupt(this); +} + +// External suspension mechanism. +// +// Tell the VM to suspend a thread when ever it knows that it does not hold on +// to any VM_locks and it is at a transition +// Self-suspension will happen on the transition out of the vm. +// Catch "this" coming in from JNIEnv pointers when the thread has been freed +// +// Guarantees on return: +// + Target thread will not execute any new bytecode (that's why we need to +// force a safepoint) +// + Target thread will not enter any new monitors +// +void JavaThread::java_suspend() { + { MutexLocker mu(Threads_lock); + if (!Threads::includes(this) || is_exiting() || this->threadObj() == NULL) { + return; + } + } + + { MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + if (!is_external_suspend()) { + // a racing resume has cancelled us; bail out now + return; + } + + // suspend is done + uint32_t debug_bits = 0; + // Warning: is_ext_suspend_completed() may temporarily drop the + // SR_lock to allow the thread to reach a stable thread state if + // it is currently in a transient thread state. + if (is_ext_suspend_completed(false /* !called_by_wait */, + SuspendRetryDelay, &debug_bits) ) { + return; + } + } + + VM_ForceSafepoint vm_suspend; + VMThread::execute(&vm_suspend); +} + +// Part II of external suspension. +// A JavaThread self suspends when it detects a pending external suspend +// request. This is usually on transitions. It is also done in places +// where continuing to the next transition would surprise the caller, +// e.g., monitor entry. +// +// Returns the number of times that the thread self-suspended. +// +// Note: DO NOT call java_suspend_self() when you just want to block current +// thread. java_suspend_self() is the second stage of cooperative +// suspension for external suspend requests and should only be used +// to complete an external suspend request. +// +int JavaThread::java_suspend_self() { + int ret = 0; + + // we are in the process of exiting so don't suspend + if (is_exiting()) { + clear_external_suspend(); + return ret; + } + + assert(_anchor.walkable() || + (is_Java_thread() && !((JavaThread*)this)->has_last_Java_frame()), + "must have walkable stack"); + + MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + + assert(!this->is_any_suspended(), + "a thread trying to self-suspend should not already be suspended"); + + if (this->is_suspend_equivalent()) { + // If we are self-suspending as a result of the lifting of a + // suspend equivalent condition, then the suspend_equivalent + // flag is not cleared until we set the ext_suspended flag so + // that wait_for_ext_suspend_completion() returns consistent + // results. + this->clear_suspend_equivalent(); + } + + // A racing resume may have cancelled us before we grabbed SR_lock + // above. Or another external suspend request could be waiting for us + // by the time we return from SR_lock()->wait(). The thread + // that requested the suspension may already be trying to walk our + // stack and if we return now, we can change the stack out from under + // it. This would be a "bad thing (TM)" and cause the stack walker + // to crash. We stay self-suspended until there are no more pending + // external suspend requests. + while (is_external_suspend()) { + ret++; + this->set_ext_suspended(); + + // _ext_suspended flag is cleared by java_resume() + while (is_ext_suspended()) { + this->SR_lock()->wait(Mutex::_no_safepoint_check_flag); + } + } + + return ret; +} + +#ifdef ASSERT +// verify the JavaThread has not yet been published in the Threads::list, and +// hence doesn't need protection from concurrent access at this stage +void JavaThread::verify_not_published() { + if (!Threads_lock->owned_by_self()) { + MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); + assert( !Threads::includes(this), + "java thread shouldn't have been published yet!"); + } + else { + assert( !Threads::includes(this), + "java thread shouldn't have been published yet!"); + } +} +#endif + +// Slow path when the native==>VM/Java barriers detect a safepoint is in +// progress or when _suspend_flags is non-zero. +// Current thread needs to self-suspend if there is a suspend request and/or +// block if a safepoint is in progress. +// Async exception ISN'T checked. +// Note only the ThreadInVMfromNative transition can call this function +// directly and when thread state is _thread_in_native_trans +void JavaThread::check_safepoint_and_suspend_for_native_trans(JavaThread *thread) { + assert(thread->thread_state() == _thread_in_native_trans, "wrong state"); + + JavaThread *curJT = JavaThread::current(); + bool do_self_suspend = thread->is_external_suspend(); + + assert(!curJT->has_last_Java_frame() || curJT->frame_anchor()->walkable(), "Unwalkable stack in native->vm transition"); + + // If JNIEnv proxies are allowed, don't self-suspend if the target + // thread is not the current thread. In older versions of jdbx, jdbx + // threads could call into the VM with another thread's JNIEnv so we + // can be here operating on behalf of a suspended thread (4432884). + if (do_self_suspend && (!AllowJNIEnvProxy || curJT == thread)) { + JavaThreadState state = thread->thread_state(); + + // We mark this thread_blocked state as a suspend-equivalent so + // that a caller to is_ext_suspend_completed() won't be confused. + // The suspend-equivalent state is cleared by java_suspend_self(). + thread->set_suspend_equivalent(); + + // If the safepoint code sees the _thread_in_native_trans state, it will + // wait until the thread changes to other thread state. There is no + // guarantee on how soon we can obtain the SR_lock and complete the + // self-suspend request. It would be a bad idea to let safepoint wait for + // too long. Temporarily change the state to _thread_blocked to + // let the VM thread know that this thread is ready for GC. The problem + // of changing thread state is that safepoint could happen just after + // java_suspend_self() returns after being resumed, and VM thread will + // see the _thread_blocked state. We must check for safepoint + // after restoring the state and make sure we won't leave while a safepoint + // is in progress. + thread->set_thread_state(_thread_blocked); + thread->java_suspend_self(); + thread->set_thread_state(state); + // Make sure new state is seen by VM thread + if (os::is_MP()) { + if (UseMembar) { + // Force a fence between the write above and read below + OrderAccess::fence(); + } else { + // Must use this rather than serialization page in particular on Windows + InterfaceSupport::serialize_memory(thread); + } + } + } + + if (SafepointSynchronize::do_call_back()) { + // If we are safepointing, then block the caller which may not be + // the same as the target thread (see above). + SafepointSynchronize::block(curJT); + } + + if (thread->is_deopt_suspend()) { + thread->clear_deopt_suspend(); + RegisterMap map(thread, false); + frame f = thread->last_frame(); + while ( f.id() != thread->must_deopt_id() && ! f.is_first_frame()) { + f = f.sender(&map); + } + if (f.id() == thread->must_deopt_id()) { + thread->clear_must_deopt_id(); + // Since we know we're safe to deopt the current state is a safe state + f.deoptimize(thread, true); + } else { + fatal("missed deoptimization!"); + } + } +} + +// Slow path when the native==>VM/Java barriers detect a safepoint is in +// progress or when _suspend_flags is non-zero. +// Current thread needs to self-suspend if there is a suspend request and/or +// block if a safepoint is in progress. +// Also check for pending async exception (not including unsafe access error). +// Note only the native==>VM/Java barriers can call this function and when +// thread state is _thread_in_native_trans. +void JavaThread::check_special_condition_for_native_trans(JavaThread *thread) { + check_safepoint_and_suspend_for_native_trans(thread); + + if (thread->has_async_exception()) { + // We are in _thread_in_native_trans state, don't handle unsafe + // access error since that may block. + thread->check_and_handle_async_exceptions(false); + } +} + +// We need to guarantee the Threads_lock here, since resumes are not +// allowed during safepoint synchronization +// Can only resume from an external suspension +void JavaThread::java_resume() { + assert_locked_or_safepoint(Threads_lock); + + // Sanity check: thread is gone, has started exiting or the thread + // was not externally suspended. + if (!Threads::includes(this) || is_exiting() || !is_external_suspend()) { + return; + } + + MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + + clear_external_suspend(); + + if (is_ext_suspended()) { + clear_ext_suspended(); + SR_lock()->notify_all(); + } +} + +void JavaThread::create_stack_guard_pages() { + if (! os::uses_stack_guard_pages() || _stack_guard_state != stack_guard_unused) return; + address low_addr = stack_base() - stack_size(); + size_t len = (StackYellowPages + StackRedPages) * os::vm_page_size(); + + int allocate = os::allocate_stack_guard_pages(); + // warning("Guarding at " PTR_FORMAT " for len " SIZE_FORMAT "\n", low_addr, len); + + if (allocate && !os::commit_memory((char *) low_addr, len)) { + warning("Attempt to allocate stack guard pages failed."); + return; + } + + if (os::guard_memory((char *) low_addr, len)) { + _stack_guard_state = stack_guard_enabled; + } else { + warning("Attempt to protect stack guard pages failed."); + if (os::uncommit_memory((char *) low_addr, len)) { + warning("Attempt to deallocate stack guard pages failed."); + } + } +} + +void JavaThread::remove_stack_guard_pages() { + if (_stack_guard_state == stack_guard_unused) return; + address low_addr = stack_base() - stack_size(); + size_t len = (StackYellowPages + StackRedPages) * os::vm_page_size(); + + if (os::allocate_stack_guard_pages()) { + if (os::uncommit_memory((char *) low_addr, len)) { + _stack_guard_state = stack_guard_unused; + } else { + warning("Attempt to deallocate stack guard pages failed."); + } + } else { + if (_stack_guard_state == stack_guard_unused) return; + if (os::unguard_memory((char *) low_addr, len)) { + _stack_guard_state = stack_guard_unused; + } else { + warning("Attempt to unprotect stack guard pages failed."); + } + } +} + +void JavaThread::enable_stack_yellow_zone() { + assert(_stack_guard_state != stack_guard_unused, "must be using guard pages."); + assert(_stack_guard_state != stack_guard_enabled, "already enabled"); + + // The base notation is from the stacks point of view, growing downward. + // We need to adjust it to work correctly with guard_memory() + address base = stack_yellow_zone_base() - stack_yellow_zone_size(); + + guarantee(base < stack_base(),"Error calculating stack yellow zone"); + guarantee(base < os::current_stack_pointer(),"Error calculating stack yellow zone"); + + if (os::guard_memory((char *) base, stack_yellow_zone_size())) { + _stack_guard_state = stack_guard_enabled; + } else { + warning("Attempt to guard stack yellow zone failed."); + } + enable_register_stack_guard(); +} + +void JavaThread::disable_stack_yellow_zone() { + assert(_stack_guard_state != stack_guard_unused, "must be using guard pages."); + assert(_stack_guard_state != stack_guard_yellow_disabled, "already disabled"); + + // Simply return if called for a thread that does not use guard pages. + if (_stack_guard_state == stack_guard_unused) return; + + // The base notation is from the stacks point of view, growing downward. + // We need to adjust it to work correctly with guard_memory() + address base = stack_yellow_zone_base() - stack_yellow_zone_size(); + + if (os::unguard_memory((char *)base, stack_yellow_zone_size())) { + _stack_guard_state = stack_guard_yellow_disabled; + } else { + warning("Attempt to unguard stack yellow zone failed."); + } + disable_register_stack_guard(); +} + +void JavaThread::enable_stack_red_zone() { + // The base notation is from the stacks point of view, growing downward. + // We need to adjust it to work correctly with guard_memory() + assert(_stack_guard_state != stack_guard_unused, "must be using guard pages."); + address base = stack_red_zone_base() - stack_red_zone_size(); + + guarantee(base < stack_base(),"Error calculating stack red zone"); + guarantee(base < os::current_stack_pointer(),"Error calculating stack red zone"); + + if(!os::guard_memory((char *) base, stack_red_zone_size())) { + warning("Attempt to guard stack red zone failed."); + } +} + +void JavaThread::disable_stack_red_zone() { + // The base notation is from the stacks point of view, growing downward. + // We need to adjust it to work correctly with guard_memory() + assert(_stack_guard_state != stack_guard_unused, "must be using guard pages."); + address base = stack_red_zone_base() - stack_red_zone_size(); + if (!os::unguard_memory((char *)base, stack_red_zone_size())) { + warning("Attempt to unguard stack red zone failed."); + } +} + +void JavaThread::frames_do(void f(frame*, const RegisterMap* map)) { + // ignore is there is no stack + if (!has_last_Java_frame()) return; + // traverse the stack frames. Starts from top frame. + for(StackFrameStream fst(this); !fst.is_done(); fst.next()) { + frame* fr = fst.current(); + f(fr, fst.register_map()); + } +} + + +#ifndef PRODUCT +// Deoptimization +// Function for testing deoptimization +void JavaThread::deoptimize() { + // BiasedLocking needs an updated RegisterMap for the revoke monitors pass + StackFrameStream fst(this, UseBiasedLocking); + bool deopt = false; // Dump stack only if a deopt actually happens. + bool only_at = strlen(DeoptimizeOnlyAt) > 0; + // Iterate over all frames in the thread and deoptimize + for(; !fst.is_done(); fst.next()) { + if(fst.current()->can_be_deoptimized()) { + + if (only_at) { + // Deoptimize only at particular bcis. DeoptimizeOnlyAt + // consists of comma or carriage return separated numbers so + // search for the current bci in that string. + address pc = fst.current()->pc(); + nmethod* nm = (nmethod*) fst.current()->cb(); + ScopeDesc* sd = nm->scope_desc_at( pc); + char buffer[8]; + jio_snprintf(buffer, sizeof(buffer), "%d", sd->bci()); + size_t len = strlen(buffer); + const char * found = strstr(DeoptimizeOnlyAt, buffer); + while (found != NULL) { + if ((found[len] == ',' || found[len] == '\n' || found[len] == '\0') && + (found == DeoptimizeOnlyAt || found[-1] == ',' || found[-1] == '\n')) { + // Check that the bci found is bracketed by terminators. + break; + } + found = strstr(found + 1, buffer); + } + if (!found) { + continue; + } + } + + if (DebugDeoptimization && !deopt) { + deopt = true; // One-time only print before deopt + tty->print_cr("[BEFORE Deoptimization]"); + trace_frames(); + trace_stack(); + } + Deoptimization::deoptimize(this, *fst.current(), fst.register_map()); + } + } + + if (DebugDeoptimization && deopt) { + tty->print_cr("[AFTER Deoptimization]"); + trace_frames(); + } +} + + +// Make zombies +void JavaThread::make_zombies() { + for(StackFrameStream fst(this); !fst.is_done(); fst.next()) { + if (fst.current()->can_be_deoptimized()) { + // it is a Java nmethod + nmethod* nm = CodeCache::find_nmethod(fst.current()->pc()); + nm->make_not_entrant(); + } + } +} +#endif // PRODUCT + + +void JavaThread::deoptimized_wrt_marked_nmethods() { + if (!has_last_Java_frame()) return; + // BiasedLocking needs an updated RegisterMap for the revoke monitors pass + StackFrameStream fst(this, UseBiasedLocking); + for(; !fst.is_done(); fst.next()) { + if (fst.current()->should_be_deoptimized()) { + Deoptimization::deoptimize(this, *fst.current(), fst.register_map()); + } + } +} + + +// GC support +static void frame_gc_epilogue(frame* f, const RegisterMap* map) { f->gc_epilogue(); } + +void JavaThread::gc_epilogue() { + frames_do(frame_gc_epilogue); +} + + +static void frame_gc_prologue(frame* f, const RegisterMap* map) { f->gc_prologue(); } + +void JavaThread::gc_prologue() { + frames_do(frame_gc_prologue); +} + + +void JavaThread::oops_do(OopClosure* f) { + // The ThreadProfiler oops_do is done from FlatProfiler::oops_do + // since there may be more than one thread using each ThreadProfiler. + + // Traverse the GCHandles + Thread::oops_do(f); + + assert( (!has_last_Java_frame() && java_call_counter() == 0) || + (has_last_Java_frame() && java_call_counter() > 0), "wrong java_sp info!"); + + if (has_last_Java_frame()) { + + // Traverse the privileged stack + if (_privileged_stack_top != NULL) { + _privileged_stack_top->oops_do(f); + } + + // traverse the registered growable array + if (_array_for_gc != NULL) { + for (int index = 0; index < _array_for_gc->length(); index++) { + f->do_oop(_array_for_gc->adr_at(index)); + } + } + + // Traverse the monitor chunks + for (MonitorChunk* chunk = monitor_chunks(); chunk != NULL; chunk = chunk->next()) { + chunk->oops_do(f); + } + + // Traverse the execution stack + for(StackFrameStream fst(this); !fst.is_done(); fst.next()) { + fst.current()->oops_do(f, fst.register_map()); + } + } + + // callee_target is never live across a gc point so NULL it here should + // it still contain a methdOop. + + set_callee_target(NULL); + + assert(vframe_array_head() == NULL, "deopt in progress at a safepoint!"); + // If we have deferred set_locals there might be oops waiting to be + // written + GrowableArray* list = deferred_locals(); + if (list != NULL) { + for (int i = 0; i < list->length(); i++) { + list->at(i)->oops_do(f); + } + } + + // Traverse instance variables at the end since the GC may be moving things + // around using this function + f->do_oop((oop*) &_threadObj); + f->do_oop((oop*) &_vm_result); + f->do_oop((oop*) &_vm_result_2); + f->do_oop((oop*) &_exception_oop); + f->do_oop((oop*) &_pending_async_exception); + + if (jvmti_thread_state() != NULL) { + jvmti_thread_state()->oops_do(f); + } +} + +void JavaThread::nmethods_do() { + // Traverse the GCHandles + Thread::nmethods_do(); + + assert( (!has_last_Java_frame() && java_call_counter() == 0) || + (has_last_Java_frame() && java_call_counter() > 0), "wrong java_sp info!"); + + if (has_last_Java_frame()) { + // Traverse the execution stack + for(StackFrameStream fst(this); !fst.is_done(); fst.next()) { + fst.current()->nmethods_do(); + } + } +} + +// Printing +const char* _get_thread_state_name(JavaThreadState _thread_state) { + switch (_thread_state) { + case _thread_uninitialized: return "_thread_uninitialized"; + case _thread_new: return "_thread_new"; + case _thread_new_trans: return "_thread_new_trans"; + case _thread_in_native: return "_thread_in_native"; + case _thread_in_native_trans: return "_thread_in_native_trans"; + case _thread_in_vm: return "_thread_in_vm"; + case _thread_in_vm_trans: return "_thread_in_vm_trans"; + case _thread_in_Java: return "_thread_in_Java"; + case _thread_in_Java_trans: return "_thread_in_Java_trans"; + case _thread_blocked: return "_thread_blocked"; + case _thread_blocked_trans: return "_thread_blocked_trans"; + default: return "unknown thread state"; + } +} + +#ifndef PRODUCT +void JavaThread::print_thread_state_on(outputStream *st) const { + st->print_cr(" JavaThread state: %s", _get_thread_state_name(_thread_state)); +}; +void JavaThread::print_thread_state() const { + print_thread_state_on(tty); +}; +#endif // PRODUCT + +// Called by Threads::print() for VM_PrintThreads operation +void JavaThread::print_on(outputStream *st) const { + st->print("\"%s\" ", get_thread_name()); + oop thread_oop = threadObj(); + if (thread_oop != NULL && java_lang_Thread::is_daemon(thread_oop)) st->print("daemon "); + Thread::print_on(st); + // print guess for valid stack memory region (assume 4K pages); helps lock debugging + st->print_cr("[" INTPTR_FORMAT ".." INTPTR_FORMAT "]", (intptr_t)last_Java_sp() & ~right_n_bits(12), highest_lock()); + if (thread_oop != NULL && JDK_Version::is_gte_jdk15x_version()) { + st->print_cr(" java.lang.Thread.State: %s", java_lang_Thread::thread_status_name(thread_oop)); + } +#ifndef PRODUCT + print_thread_state_on(st); + _safepoint_state->print_on(st); +#endif // PRODUCT +} + +// Called by fatal error handler. The difference between this and +// JavaThread::print() is that we can't grab lock or allocate memory. +void JavaThread::print_on_error(outputStream* st, char *buf, int buflen) const { + st->print("JavaThread \"%s\"", get_thread_name_string(buf, buflen)); + oop thread_obj = threadObj(); + if (thread_obj != NULL) { + if (java_lang_Thread::is_daemon(thread_obj)) st->print(" daemon"); + } + st->print(" ["); + st->print("%s", _get_thread_state_name(_thread_state)); + if (osthread()) { + st->print(", id=%d", osthread()->thread_id()); + } + st->print(", stack(" PTR_FORMAT "," PTR_FORMAT ")", + _stack_base - _stack_size, _stack_base); + st->print("]"); + return; +} + +// Verification + +static void frame_verify(frame* f, const RegisterMap *map) { f->verify(map); } + +void JavaThread::verify() { + // Verify oops in the thread. + oops_do(&VerifyOopClosure::verify_oop); + + // Verify the stack frames. + frames_do(frame_verify); +} + +// CR 6300358 (sub-CR 2137150) +// Most callers of this method assume that it can't return NULL but a +// thread may not have a name whilst it is in the process of attaching to +// the VM - see CR 6412693, and there are places where a JavaThread can be +// seen prior to having it's threadObj set (eg JNI attaching threads and +// if vm exit occurs during initialization). These cases can all be accounted +// for such that this method never returns NULL. +const char* JavaThread::get_thread_name() const { +#ifdef ASSERT + // early safepoints can hit while current thread does not yet have TLS + if (!SafepointSynchronize::is_at_safepoint()) { + Thread *cur = Thread::current(); + if (!(cur->is_Java_thread() && cur == this)) { + // Current JavaThreads are allowed to get their own name without + // the Threads_lock. + assert_locked_or_safepoint(Threads_lock); + } + } +#endif // ASSERT + return get_thread_name_string(); +} + +// Returns a non-NULL representation of this thread's name, or a suitable +// descriptive string if there is no set name +const char* JavaThread::get_thread_name_string(char* buf, int buflen) const { + const char* name_str; + oop thread_obj = threadObj(); + if (thread_obj != NULL) { + typeArrayOop name = java_lang_Thread::name(thread_obj); + if (name != NULL) { + if (buf == NULL) { + name_str = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); + } + else { + name_str = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length(), buf, buflen); + } + } + else if (is_attaching()) { // workaround for 6412693 - see 6404306 + name_str = ""; + } + else { + name_str = Thread::name(); + } + } + else { + name_str = Thread::name(); + } + assert(name_str != NULL, "unexpected NULL thread name"); + return name_str; +} + + +const char* JavaThread::get_threadgroup_name() const { + debug_only(if (JavaThread::current() != this) assert_locked_or_safepoint(Threads_lock);) + oop thread_obj = threadObj(); + if (thread_obj != NULL) { + oop thread_group = java_lang_Thread::threadGroup(thread_obj); + if (thread_group != NULL) { + typeArrayOop name = java_lang_ThreadGroup::name(thread_group); + // ThreadGroup.name can be null + if (name != NULL) { + const char* str = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); + return str; + } + } + } + return NULL; +} + +const char* JavaThread::get_parent_name() const { + debug_only(if (JavaThread::current() != this) assert_locked_or_safepoint(Threads_lock);) + oop thread_obj = threadObj(); + if (thread_obj != NULL) { + oop thread_group = java_lang_Thread::threadGroup(thread_obj); + if (thread_group != NULL) { + oop parent = java_lang_ThreadGroup::parent(thread_group); + if (parent != NULL) { + typeArrayOop name = java_lang_ThreadGroup::name(parent); + // ThreadGroup.name can be null + if (name != NULL) { + const char* str = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); + return str; + } + } + } + } + return NULL; +} + +ThreadPriority JavaThread::java_priority() const { + oop thr_oop = threadObj(); + if (thr_oop == NULL) return NormPriority; // Bootstrapping + ThreadPriority priority = java_lang_Thread::priority(thr_oop); + assert(MinPriority <= priority && priority <= MaxPriority, "sanity check"); + return priority; +} + +void JavaThread::prepare(jobject jni_thread, ThreadPriority prio) { + + assert(Threads_lock->owner() == Thread::current(), "must have threads lock"); + // Link Java Thread object <-> C++ Thread + + // Get the C++ thread object (an oop) from the JNI handle (a jthread) + // and put it into a new Handle. The Handle "thread_oop" can then + // be used to pass the C++ thread object to other methods. + + // Set the Java level thread object (jthread) field of the + // new thread (a JavaThread *) to C++ thread object using the + // "thread_oop" handle. + + // Set the thread field (a JavaThread *) of the + // oop representing the java_lang_Thread to the new thread (a JavaThread *). + + Handle thread_oop(Thread::current(), + JNIHandles::resolve_non_null(jni_thread)); + assert(instanceKlass::cast(thread_oop->klass())->is_linked(), + "must be initialized"); + set_threadObj(thread_oop()); + java_lang_Thread::set_thread(thread_oop(), this); + + if (prio == NoPriority) { + prio = java_lang_Thread::priority(thread_oop()); + assert(prio != NoPriority, "A valid priority should be present"); + } + + // Push the Java priority down to the native thread; needs Threads_lock + Thread::set_priority(this, prio); + + // Add the new thread to the Threads list and set it in motion. + // We must have threads lock in order to call Threads::add. + // It is crucial that we do not block before the thread is + // added to the Threads list for if a GC happens, then the java_thread oop + // will not be visited by GC. + Threads::add(this); +} + +oop JavaThread::current_park_blocker() { + // Support for JSR-166 locks + oop thread_oop = threadObj(); + if (thread_oop != NULL && JDK_Version::supports_thread_park_blocker()) { + return java_lang_Thread::park_blocker(thread_oop); + } + return NULL; +} + + +void JavaThread::print_stack_on(outputStream* st) { + if (!has_last_Java_frame()) return; + ResourceMark rm; + HandleMark hm; + + RegisterMap reg_map(this); + vframe* start_vf = last_java_vframe(®_map); + int count = 0; + for (vframe* f = start_vf; f; f = f->sender() ) { + if (f->is_java_frame()) { + javaVFrame* jvf = javaVFrame::cast(f); + java_lang_Throwable::print_stack_element(st, jvf->method(), jvf->bci()); + + // Print out lock information + if (JavaMonitorsInStackTrace) { + jvf->print_lock_info_on(st, count); + } + } else { + // Ignore non-Java frames + } + + // Bail-out case for too deep stacks + count++; + if (MaxJavaStackTraceDepth == count) return; + } +} + + +// JVMTI PopFrame support +void JavaThread::popframe_preserve_args(ByteSize size_in_bytes, void* start) { + assert(_popframe_preserved_args == NULL, "should not wipe out old PopFrame preserved arguments"); + if (in_bytes(size_in_bytes) != 0) { + _popframe_preserved_args = NEW_C_HEAP_ARRAY(char, in_bytes(size_in_bytes)); + _popframe_preserved_args_size = in_bytes(size_in_bytes); + Copy::conjoint_bytes(start, _popframe_preserved_args, _popframe_preserved_args_size); + } +} + +void* JavaThread::popframe_preserved_args() { + return _popframe_preserved_args; +} + +ByteSize JavaThread::popframe_preserved_args_size() { + return in_ByteSize(_popframe_preserved_args_size); +} + +WordSize JavaThread::popframe_preserved_args_size_in_words() { + int sz = in_bytes(popframe_preserved_args_size()); + assert(sz % wordSize == 0, "argument size must be multiple of wordSize"); + return in_WordSize(sz / wordSize); +} + +void JavaThread::popframe_free_preserved_args() { + assert(_popframe_preserved_args != NULL, "should not free PopFrame preserved arguments twice"); + FREE_C_HEAP_ARRAY(char, (char*) _popframe_preserved_args); + _popframe_preserved_args = NULL; + _popframe_preserved_args_size = 0; +} + +#ifndef PRODUCT + +void JavaThread::trace_frames() { + tty->print_cr("[Describe stack]"); + int frame_no = 1; + for(StackFrameStream fst(this); !fst.is_done(); fst.next()) { + tty->print(" %d. ", frame_no++); + fst.current()->print_value_on(tty,this); + tty->cr(); + } +} + + +void JavaThread::trace_stack_from(vframe* start_vf) { + ResourceMark rm; + int vframe_no = 1; + for (vframe* f = start_vf; f; f = f->sender() ) { + if (f->is_java_frame()) { + javaVFrame::cast(f)->print_activation(vframe_no++); + } else { + f->print(); + } + if (vframe_no > StackPrintLimit) { + tty->print_cr("......"); + return; + } + } +} + + +void JavaThread::trace_stack() { + if (!has_last_Java_frame()) return; + ResourceMark rm; + HandleMark hm; + RegisterMap reg_map(this); + trace_stack_from(last_java_vframe(®_map)); +} + + +#endif // PRODUCT + + +javaVFrame* JavaThread::last_java_vframe(RegisterMap *reg_map) { + assert(reg_map != NULL, "a map must be given"); + frame f = last_frame(); + for (vframe* vf = vframe::new_vframe(&f, reg_map, this); vf; vf = vf->sender() ) { + if (vf->is_java_frame()) return javaVFrame::cast(vf); + } + return NULL; +} + + +klassOop JavaThread::security_get_caller_class(int depth) { + vframeStream vfst(this); + vfst.security_get_caller_frame(depth); + if (!vfst.at_end()) { + return vfst.method()->method_holder(); + } + return NULL; +} + +static void compiler_thread_entry(JavaThread* thread, TRAPS) { + assert(thread->is_Compiler_thread(), "must be compiler thread"); + CompileBroker::compiler_thread_loop(); +} + +// Create a CompilerThread +CompilerThread::CompilerThread(CompileQueue* queue, CompilerCounters* counters) +: JavaThread(&compiler_thread_entry) { + _env = NULL; + _log = NULL; + _task = NULL; + _queue = queue; + _counters = counters; + +#ifndef PRODUCT + _ideal_graph_printer = NULL; +#endif +} + + +// ======= Threads ======== + +// The Threads class links together all active threads, and provides +// operations over all threads. It is protected by its own Mutex +// lock, which is also used in other contexts to protect thread +// operations from having the thread being operated on from exiting +// and going away unexpectedly (e.g., safepoint synchronization) + +JavaThread* Threads::_thread_list = NULL; +int Threads::_number_of_threads = 0; +int Threads::_number_of_non_daemon_threads = 0; +int Threads::_return_code = 0; +size_t JavaThread::_stack_size_at_create = 0; + +// All JavaThreads +#define ALL_JAVA_THREADS(X) for (JavaThread* X = _thread_list; X; X = X->next()) + +void os_stream(); + +// All JavaThreads + all non-JavaThreads (i.e., every thread in the system) +void Threads::threads_do(ThreadClosure* tc) { + assert_locked_or_safepoint(Threads_lock); + // ALL_JAVA_THREADS iterates through all JavaThreads + ALL_JAVA_THREADS(p) { + tc->do_thread(p); + } + // Someday we could have a table or list of all non-JavaThreads. + // For now, just manually iterate through them. + tc->do_thread(VMThread::vm_thread()); + Universe::heap()->gc_threads_do(tc); + tc->do_thread(WatcherThread::watcher_thread()); + // If CompilerThreads ever become non-JavaThreads, add them here +} + +jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { + + // Check version + if (!is_supported_jni_version(args->version)) return JNI_EVERSION; + + // Initialize the output stream module + ostream_init(); + + // Process java launcher properties. + Arguments::process_sun_java_launcher_properties(args); + + // Initialize the os module before using TLS + os::init(); + + // Initialize system properties. + Arguments::init_system_properties(); + + // Parse arguments + jint parse_result = Arguments::parse(args); + if (parse_result != JNI_OK) return parse_result; + + if (PauseAtStartup) { + os::pause(); + } + + HS_DTRACE_PROBE(hotspot, vm__init__begin); + + // Record VM creation timing statistics + TraceVmCreationTime create_vm_timer; + create_vm_timer.start(); + + // Timing (must come after argument parsing) + TraceTime timer("Create VM", TraceStartupTime); + + // Initialize the os module after parsing the args + jint os_init_2_result = os::init_2(); + if (os_init_2_result != JNI_OK) return os_init_2_result; + + // Initialize output stream logging + ostream_init_log(); + + // Convert -Xrun to -agentlib: if there is no JVM_OnLoad + // Must be before create_vm_init_agents() + if (Arguments::init_libraries_at_startup()) { + convert_vm_init_libraries_to_agents(); + } + + // Launch -agentlib/-agentpath and converted -Xrun agents + if (Arguments::init_agents_at_startup()) { + create_vm_init_agents(); + } + + // Initialize Threads state + _thread_list = NULL; + _number_of_threads = 0; + _number_of_non_daemon_threads = 0; + + // Initialize TLS + ThreadLocalStorage::init(); + + // Initialize global data structures and create system classes in heap + vm_init_globals(); + + // Attach the main thread to this os thread + JavaThread* main_thread = new JavaThread(); + main_thread->set_thread_state(_thread_in_vm); + // must do this before set_active_handles and initialize_thread_local_storage + // Note: on solaris initialize_thread_local_storage() will (indirectly) + // change the stack size recorded here to one based on the java thread + // stacksize. This adjusted size is what is used to figure the placement + // of the guard pages. + main_thread->record_stack_base_and_size(); + main_thread->initialize_thread_local_storage(); + + main_thread->set_active_handles(JNIHandleBlock::allocate_block()); + + if (!main_thread->set_as_starting_thread()) { + vm_shutdown_during_initialization( + "Failed necessary internal allocation. Out of swap space"); + delete main_thread; + *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again + return JNI_ENOMEM; + } + + // Enable guard page *after* os::create_main_thread(), otherwise it would + // crash Linux VM, see notes in os_linux.cpp. + main_thread->create_stack_guard_pages(); + + // Initialize Java-Leve synchronization subsystem + ObjectSynchronizer::Initialize() ; + + // Initialize global modules + jint status = init_globals(); + if (status != JNI_OK) { + delete main_thread; + *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again + return status; + } + + HandleMark hm; + + { MutexLocker mu(Threads_lock); + Threads::add(main_thread); + } + + // Any JVMTI raw monitors entered in onload will transition into + // real raw monitor. VM is setup enough here for raw monitor enter. + JvmtiExport::transition_pending_onload_raw_monitors(); + + if (VerifyBeforeGC && + Universe::heap()->total_collections() >= VerifyGCStartAt) { + Universe::heap()->prepare_for_verify(); + Universe::verify(); // make sure we're starting with a clean slate + } + + // Create the VMThread + { TraceTime timer("Start VMThread", TraceStartupTime); + VMThread::create(); + Thread* vmthread = VMThread::vm_thread(); + + if (!os::create_thread(vmthread, os::vm_thread)) + vm_exit_during_initialization("Cannot create VM thread. Out of system resources."); + + // Wait for the VM thread to become ready, and VMThread::run to initialize + // Monitors can have spurious returns, must always check another state flag + { + MutexLocker ml(Notify_lock); + os::start_thread(vmthread); + while (vmthread->active_handles() == NULL) { + Notify_lock->wait(); + } + } + } + + assert (Universe::is_fully_initialized(), "not initialized"); + EXCEPTION_MARK; + + // At this point, the Universe is initialized, but we have not executed + // any byte code. Now is a good time (the only time) to dump out the + // internal state of the JVM for sharing. + + if (DumpSharedSpaces) { + Universe::heap()->preload_and_dump(CHECK_0); + ShouldNotReachHere(); + } + + // Always call even when there are not JVMTI environments yet, since environments + // may be attached late and JVMTI must track phases of VM execution + JvmtiExport::enter_start_phase(); + + // Notify JVMTI agents that VM has started (JNI is up) - nop if no agents. + JvmtiExport::post_vm_start(); + + { + TraceTime timer("Initialize java.lang classes", TraceStartupTime); + + if (EagerXrunInit && Arguments::init_libraries_at_startup()) { + create_vm_init_libraries(); + } + + if (InitializeJavaLangString) { + initialize_class(vmSymbolHandles::java_lang_String(), CHECK_0); + } else { + warning("java.lang.String not initialized"); + } + + // Initialize java_lang.System (needed before creating the thread) + if (InitializeJavaLangSystem) { + initialize_class(vmSymbolHandles::java_lang_System(), CHECK_0); + initialize_class(vmSymbolHandles::java_lang_ThreadGroup(), CHECK_0); + Handle thread_group = create_initial_thread_group(CHECK_0); + Universe::set_main_thread_group(thread_group()); + initialize_class(vmSymbolHandles::java_lang_Thread(), CHECK_0); + oop thread_object = create_initial_thread(thread_group, main_thread, CHECK_0); + main_thread->set_threadObj(thread_object); + // Set thread status to running since main thread has + // been started and running. + java_lang_Thread::set_thread_status(thread_object, + java_lang_Thread::RUNNABLE); + + // The VM preresolve methods to these classes. Make sure that get initialized + initialize_class(vmSymbolHandles::java_lang_reflect_Method(), CHECK_0); + initialize_class(vmSymbolHandles::java_lang_ref_Finalizer(), CHECK_0); + // The VM creates & returns objects of this class. Make sure it's initialized. + initialize_class(vmSymbolHandles::java_lang_Class(), CHECK_0); + call_initializeSystemClass(CHECK_0); + } else { + warning("java.lang.System not initialized"); + } + + // an instance of OutOfMemory exception has been allocated earlier + if (InitializeJavaLangExceptionsErrors) { + initialize_class(vmSymbolHandles::java_lang_OutOfMemoryError(), CHECK_0); + initialize_class(vmSymbolHandles::java_lang_NullPointerException(), CHECK_0); + initialize_class(vmSymbolHandles::java_lang_ClassCastException(), CHECK_0); + initialize_class(vmSymbolHandles::java_lang_ArrayStoreException(), CHECK_0); + initialize_class(vmSymbolHandles::java_lang_ArithmeticException(), CHECK_0); + initialize_class(vmSymbolHandles::java_lang_StackOverflowError(), CHECK_0); + initialize_class(vmSymbolHandles::java_lang_IllegalMonitorStateException(), CHECK_0); + } else { + warning("java.lang.OutOfMemoryError has not been initialized"); + warning("java.lang.NullPointerException has not been initialized"); + warning("java.lang.ClassCastException has not been initialized"); + warning("java.lang.ArrayStoreException has not been initialized"); + warning("java.lang.ArithmeticException has not been initialized"); + warning("java.lang.StackOverflowError has not been initialized"); + } + } + + // See : bugid 4211085. + // Background : the static initializer of java.lang.Compiler tries to read + // property"java.compiler" and read & write property "java.vm.info". + // When a security manager is installed through the command line + // option "-Djava.security.manager", the above properties are not + // readable and the static initializer for java.lang.Compiler fails + // resulting in a NoClassDefFoundError. This can happen in any + // user code which calls methods in java.lang.Compiler. + // Hack : the hack is to pre-load and initialize this class, so that only + // system domains are on the stack when the properties are read. + // Currently even the AWT code has calls to methods in java.lang.Compiler. + // On the classic VM, java.lang.Compiler is loaded very early to load the JIT. + // Future Fix : the best fix is to grant everyone permissions to read "java.compiler" and + // read and write"java.vm.info" in the default policy file. See bugid 4211383 + // Once that is done, we should remove this hack. + initialize_class(vmSymbolHandles::java_lang_Compiler(), CHECK_0); + + // More hackery - the static initializer of java.lang.Compiler adds the string "nojit" to + // the java.vm.info property if no jit gets loaded through java.lang.Compiler (the hotspot + // compiler does not get loaded through java.lang.Compiler). "java -version" with the + // hotspot vm says "nojit" all the time which is confusing. So, we reset it here. + // This should also be taken out as soon as 4211383 gets fixed. + reset_vm_info_property(CHECK_0); + + quicken_jni_functions(); + + // Set flag that basic initialization has completed. Used by exceptions and various + // debug stuff, that does not work until all basic classes have been initialized. + set_init_completed(); + + HS_DTRACE_PROBE(hotspot, vm__init__end); + + // record VM initialization completion time + Management::record_vm_init_completed(); + + // Compute system loader. Note that this has to occur after set_init_completed, since + // valid exceptions may be thrown in the process. + // Note that we do not use CHECK_0 here since we are inside an EXCEPTION_MARK and + // set_init_completed has just been called, causing exceptions not to be shortcut + // anymore. We call vm_exit_during_initialization directly instead. + SystemDictionary::compute_java_system_loader(THREAD); + if (HAS_PENDING_EXCEPTION) { + vm_exit_during_initialization(Handle(THREAD, PENDING_EXCEPTION)); + } + +#ifndef SERIALGC + // Support for ConcurrentMarkSweep. This should be cleaned up + // and better encapsulated. XXX YSR + if (UseConcMarkSweepGC) { + ConcurrentMarkSweepThread::makeSurrogateLockerThread(THREAD); + if (HAS_PENDING_EXCEPTION) { + vm_exit_during_initialization(Handle(THREAD, PENDING_EXCEPTION)); + } + } +#endif // SERIALGC + + // Always call even when there are not JVMTI environments yet, since environments + // may be attached late and JVMTI must track phases of VM execution + JvmtiExport::enter_live_phase(); + + // Signal Dispatcher needs to be started before VMInit event is posted + os::signal_init(); + + // Start Attach Listener if +StartAttachListener or it can't be started lazily + if (!DisableAttachMechanism) { + if (StartAttachListener || AttachListener::init_at_startup()) { + AttachListener::init(); + } + } + + // Launch -Xrun agents + // Must be done in the JVMTI live phase so that for backward compatibility the JDWP + // back-end can launch with -Xdebug -Xrunjdwp. + if (!EagerXrunInit && Arguments::init_libraries_at_startup()) { + create_vm_init_libraries(); + } + + // Notify JVMTI agents that VM initialization is complete - nop if no agents. + JvmtiExport::post_vm_initialized(); + + Chunk::start_chunk_pool_cleaner_task(); + + // initialize compiler(s) + CompileBroker::compilation_init(); + + Management::initialize(THREAD); + if (HAS_PENDING_EXCEPTION) { + // management agent fails to start possibly due to + // configuration problem and is responsible for printing + // stack trace if appropriate. Simply exit VM. + vm_exit(1); + } + + if (Arguments::has_profile()) FlatProfiler::engage(main_thread, true); + if (Arguments::has_alloc_profile()) AllocationProfiler::engage(); + if (MemProfiling) MemProfiler::engage(); + StatSampler::engage(); + if (CheckJNICalls) JniPeriodicChecker::engage(); + if (CacheTimeMillis) TimeMillisUpdateTask::engage(); + + BiasedLocking::init(); + + + // Start up the WatcherThread if there are any periodic tasks + // NOTE: All PeriodicTasks should be registered by now. If they + // aren't, late joiners might appear to start slowly (we might + // take a while to process their first tick). + if (PeriodicTask::num_tasks() > 0) { + WatcherThread::start(); + } + + create_vm_timer.end(); + return JNI_OK; +} + +// type for the Agent_OnLoad and JVM_OnLoad entry points +extern "C" { + typedef jint (JNICALL *OnLoadEntry_t)(JavaVM *, char *, void *); +} +// Find a command line agent library and return its entry point for +// -agentlib: -agentpath: -Xrun +// num_symbol_entries must be passed-in since only the caller knows the number of symbols in the array. +static OnLoadEntry_t lookup_on_load(AgentLibrary* agent, const char *on_load_symbols[], size_t num_symbol_entries) { + OnLoadEntry_t on_load_entry = NULL; + void *library = agent->os_lib(); // check if we have looked it up before + + if (library == NULL) { + char buffer[JVM_MAXPATHLEN]; + char ebuf[1024]; + const char *name = agent->name(); + + if (agent->is_absolute_path()) { + library = hpi::dll_load(name, ebuf, sizeof ebuf); + if (library == NULL) { + // If we can't find the agent, exit. + vm_exit_during_initialization("Could not find agent library in absolute path", name); + } + } else { + // Try to load the agent from the standard dll directory + hpi::dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(), name); + library = hpi::dll_load(buffer, ebuf, sizeof ebuf); +#ifdef KERNEL + // Download instrument dll + if (library == NULL && strcmp(name, "instrument") == 0) { + char *props = Arguments::get_kernel_properties(); + char *home = Arguments::get_java_home(); + const char *fmt = "%s/bin/java %s -Dkernel.background.download=false" + " sun.jkernel.DownloadManager -download client_jvm"; + int length = strlen(props) + strlen(home) + strlen(fmt) + 1; + char *cmd = AllocateHeap(length); + jio_snprintf(cmd, length, fmt, home, props); + int status = os::fork_and_exec(cmd); + FreeHeap(props); + FreeHeap(cmd); + if (status == -1) { + warning(cmd); + vm_exit_during_initialization("fork_and_exec failed: %s", + strerror(errno)); + } + // when this comes back the instrument.dll should be where it belongs. + library = hpi::dll_load(buffer, ebuf, sizeof ebuf); + } +#endif // KERNEL + if (library == NULL) { // Try the local directory + char ns[1] = {0}; + hpi::dll_build_name(buffer, sizeof(buffer), ns, name); + library = hpi::dll_load(buffer, ebuf, sizeof ebuf); + if (library == NULL) { + // If we can't find the agent, exit. + vm_exit_during_initialization("Could not find agent library on the library path or in the local directory", name); + } + } + } + agent->set_os_lib(library); + } + + // Find the OnLoad function. + for (size_t symbol_index = 0; symbol_index < num_symbol_entries; symbol_index++) { + on_load_entry = CAST_TO_FN_PTR(OnLoadEntry_t, hpi::dll_lookup(library, on_load_symbols[symbol_index])); + if (on_load_entry != NULL) break; + } + return on_load_entry; +} + +// Find the JVM_OnLoad entry point +static OnLoadEntry_t lookup_jvm_on_load(AgentLibrary* agent) { + const char *on_load_symbols[] = JVM_ONLOAD_SYMBOLS; + return lookup_on_load(agent, on_load_symbols, sizeof(on_load_symbols) / sizeof(char*)); +} + +// Find the Agent_OnLoad entry point +static OnLoadEntry_t lookup_agent_on_load(AgentLibrary* agent) { + const char *on_load_symbols[] = AGENT_ONLOAD_SYMBOLS; + return lookup_on_load(agent, on_load_symbols, sizeof(on_load_symbols) / sizeof(char*)); +} + +// For backwards compatibility with -Xrun +// Convert libraries with no JVM_OnLoad, but which have Agent_OnLoad to be +// treated like -agentpath: +// Must be called before agent libraries are created +void Threads::convert_vm_init_libraries_to_agents() { + AgentLibrary* agent; + AgentLibrary* next; + + for (agent = Arguments::libraries(); agent != NULL; agent = next) { + next = agent->next(); // cache the next agent now as this agent may get moved off this list + OnLoadEntry_t on_load_entry = lookup_jvm_on_load(agent); + + // If there is an JVM_OnLoad function it will get called later, + // otherwise see if there is an Agent_OnLoad + if (on_load_entry == NULL) { + on_load_entry = lookup_agent_on_load(agent); + if (on_load_entry != NULL) { + // switch it to the agent list -- so that Agent_OnLoad will be called, + // JVM_OnLoad won't be attempted and Agent_OnUnload will + Arguments::convert_library_to_agent(agent); + } else { + vm_exit_during_initialization("Could not find JVM_OnLoad or Agent_OnLoad function in the library", agent->name()); + } + } + } +} + +// Create agents for -agentlib: -agentpath: and converted -Xrun +// Invokes Agent_OnLoad +// Called very early -- before JavaThreads exist +void Threads::create_vm_init_agents() { + extern struct JavaVM_ main_vm; + AgentLibrary* agent; + + JvmtiExport::enter_onload_phase(); + for (agent = Arguments::agents(); agent != NULL; agent = agent->next()) { + OnLoadEntry_t on_load_entry = lookup_agent_on_load(agent); + + if (on_load_entry != NULL) { + // Invoke the Agent_OnLoad function + jint err = (*on_load_entry)(&main_vm, agent->options(), NULL); + if (err != JNI_OK) { + vm_exit_during_initialization("agent library failed to init", agent->name()); + } + } else { + vm_exit_during_initialization("Could not find Agent_OnLoad function in the agent library", agent->name()); + } + } + JvmtiExport::enter_primordial_phase(); +} + +extern "C" { + typedef void (JNICALL *Agent_OnUnload_t)(JavaVM *); +} + +void Threads::shutdown_vm_agents() { + // Send any Agent_OnUnload notifications + const char *on_unload_symbols[] = AGENT_ONUNLOAD_SYMBOLS; + extern struct JavaVM_ main_vm; + for (AgentLibrary* agent = Arguments::agents(); agent != NULL; agent = agent->next()) { + + // Find the Agent_OnUnload function. + for (uint symbol_index = 0; symbol_index < ARRAY_SIZE(on_unload_symbols); symbol_index++) { + Agent_OnUnload_t unload_entry = CAST_TO_FN_PTR(Agent_OnUnload_t, + hpi::dll_lookup(agent->os_lib(), on_unload_symbols[symbol_index])); + + // Invoke the Agent_OnUnload function + if (unload_entry != NULL) { + JavaThread* thread = JavaThread::current(); + ThreadToNativeFromVM ttn(thread); + HandleMark hm(thread); + (*unload_entry)(&main_vm); + break; + } + } + } +} + +// Called for after the VM is initialized for -Xrun libraries which have not been converted to agent libraries +// Invokes JVM_OnLoad +void Threads::create_vm_init_libraries() { + extern struct JavaVM_ main_vm; + AgentLibrary* agent; + + for (agent = Arguments::libraries(); agent != NULL; agent = agent->next()) { + OnLoadEntry_t on_load_entry = lookup_jvm_on_load(agent); + + if (on_load_entry != NULL) { + // Invoke the JVM_OnLoad function + JavaThread* thread = JavaThread::current(); + ThreadToNativeFromVM ttn(thread); + HandleMark hm(thread); + jint err = (*on_load_entry)(&main_vm, agent->options(), NULL); + if (err != JNI_OK) { + vm_exit_during_initialization("-Xrun library failed to init", agent->name()); + } + } else { + vm_exit_during_initialization("Could not find JVM_OnLoad function in -Xrun library", agent->name()); + } + } +} + +// Last thread running calls java.lang.Shutdown.shutdown() +void JavaThread::invoke_shutdown_hooks() { + HandleMark hm(this); + + // We could get here with a pending exception, if so clear it now. + if (this->has_pending_exception()) { + this->clear_pending_exception(); + } + + EXCEPTION_MARK; + klassOop k = + SystemDictionary::resolve_or_null(vmSymbolHandles::java_lang_Shutdown(), + THREAD); + if (k != NULL) { + // SystemDictionary::resolve_or_null will return null if there was + // an exception. If we cannot load the Shutdown class, just don't + // call Shutdown.shutdown() at all. This will mean the shutdown hooks + // and finalizers (if runFinalizersOnExit is set) won't be run. + // Note that if a shutdown hook was registered or runFinalizersOnExit + // was called, the Shutdown class would have already been loaded + // (Runtime.addShutdownHook and runFinalizersOnExit will load it). + instanceKlassHandle shutdown_klass (THREAD, k); + JavaValue result(T_VOID); + JavaCalls::call_static(&result, + shutdown_klass, + vmSymbolHandles::shutdown_method_name(), + vmSymbolHandles::void_method_signature(), + THREAD); + } + CLEAR_PENDING_EXCEPTION; +} + +// Threads::destroy_vm() is normally called from jni_DestroyJavaVM() when +// the program falls off the end of main(). Another VM exit path is through +// vm_exit() when the program calls System.exit() to return a value or when +// there is a serious error in VM. The two shutdown paths are not exactly +// the same, but they share Shutdown.shutdown() at Java level and before_exit() +// and VM_Exit op at VM level. +// +// Shutdown sequence: +// + Wait until we are the last non-daemon thread to execute +// <-- every thing is still working at this moment --> +// + Call java.lang.Shutdown.shutdown(), which will invoke Java level +// shutdown hooks, run finalizers if finalization-on-exit +// + Call before_exit(), prepare for VM exit +// > run VM level shutdown hooks (they are registered through JVM_OnExit(), +// currently the only user of this mechanism is File.deleteOnExit()) +// > stop flat profiler, StatSampler, watcher thread, CMS threads, +// post thread end and vm death events to JVMTI, +// stop signal thread +// + Call JavaThread::exit(), it will: +// > release JNI handle blocks, remove stack guard pages +// > remove this thread from Threads list +// <-- no more Java code from this thread after this point --> +// + Stop VM thread, it will bring the remaining VM to a safepoint and stop +// the compiler threads at safepoint +// <-- do not use anything that could get blocked by Safepoint --> +// + Disable tracing at JNI/JVM barriers +// + Set _vm_exited flag for threads that are still running native code +// + Delete this thread +// + Call exit_globals() +// > deletes tty +// > deletes PerfMemory resources +// + Return to caller + +bool Threads::destroy_vm() { + JavaThread* thread = JavaThread::current(); + + // Wait until we are the last non-daemon thread to execute + { MutexLocker nu(Threads_lock); + while (Threads::number_of_non_daemon_threads() > 1 ) + // This wait should make safepoint checks, wait without a timeout, + // and wait as a suspend-equivalent condition. + // + // Note: If the FlatProfiler is running and this thread is waiting + // for another non-daemon thread to finish, then the FlatProfiler + // is waiting for the external suspend request on this thread to + // complete. wait_for_ext_suspend_completion() will eventually + // timeout, but that takes time. Making this wait a suspend- + // equivalent condition solves that timeout problem. + // + Threads_lock->wait(!Mutex::_no_safepoint_check_flag, 0, + Mutex::_as_suspend_equivalent_flag); + } + + // Hang forever on exit if we are reporting an error. + if (ShowMessageBoxOnError && is_error_reported()) { + os::infinite_sleep(); + } + + if (JDK_Version::is_jdk12x_version()) { + // We are the last thread running, so check if finalizers should be run. + // For 1.3 or later this is done in thread->invoke_shutdown_hooks() + HandleMark rm(thread); + Universe::run_finalizers_on_exit(); + } else { + // run Java level shutdown hooks + thread->invoke_shutdown_hooks(); + } + + before_exit(thread); + + thread->exit(true); + + // Stop VM thread. + { + // 4945125 The vm thread comes to a safepoint during exit. + // GC vm_operations can get caught at the safepoint, and the + // heap is unparseable if they are caught. Grab the Heap_lock + // to prevent this. The GC vm_operations will not be able to + // queue until after the vm thread is dead. + MutexLocker ml(Heap_lock); + + VMThread::wait_for_vm_thread_exit(); + assert(SafepointSynchronize::is_at_safepoint(), "VM thread should exit at Safepoint"); + VMThread::destroy(); + } + + // clean up ideal graph printers +#if defined(COMPILER2) && !defined(PRODUCT) + IdealGraphPrinter::clean_up(); +#endif + + // Now, all Java threads are gone except daemon threads. Daemon threads + // running Java code or in VM are stopped by the Safepoint. However, + // daemon threads executing native code are still running. But they + // will be stopped at native=>Java/VM barriers. Note that we can't + // simply kill or suspend them, as it is inherently deadlock-prone. + +#ifndef PRODUCT + // disable function tracing at JNI/JVM barriers + TraceHPI = false; + TraceJNICalls = false; + TraceJVMCalls = false; + TraceRuntimeCalls = false; +#endif + + VM_Exit::set_vm_exited(); + + notify_vm_shutdown(); + + delete thread; + + // exit_globals() will delete tty + exit_globals(); + + return true; +} + + +jboolean Threads::is_supported_jni_version_including_1_1(jint version) { + if (version == JNI_VERSION_1_1) return JNI_TRUE; + return is_supported_jni_version(version); +} + + +jboolean Threads::is_supported_jni_version(jint version) { + if (version == JNI_VERSION_1_2) return JNI_TRUE; + if (version == JNI_VERSION_1_4) return JNI_TRUE; + if (version == JNI_VERSION_1_6) return JNI_TRUE; + return JNI_FALSE; +} + + +void Threads::add(JavaThread* p, bool force_daemon) { + // The threads lock must be owned at this point + assert_locked_or_safepoint(Threads_lock); + p->set_next(_thread_list); + _thread_list = p; + _number_of_threads++; + oop threadObj = p->threadObj(); + bool daemon = true; + // Bootstrapping problem: threadObj can be null for initial + // JavaThread (or for threads attached via JNI) + if ((!force_daemon) && (threadObj == NULL || !java_lang_Thread::is_daemon(threadObj))) { + _number_of_non_daemon_threads++; + daemon = false; + } + + ThreadService::add_thread(p, daemon); + + // Possible GC point. + Events::log("Thread added: " INTPTR_FORMAT, p); +} + +void Threads::remove(JavaThread* p) { + // Extra scope needed for Thread_lock, so we can check + // that we do not remove thread without safepoint code notice + { MutexLocker ml(Threads_lock); + + assert(includes(p), "p must be present"); + + JavaThread* current = _thread_list; + JavaThread* prev = NULL; + + while (current != p) { + prev = current; + current = current->next(); + } + + if (prev) { + prev->set_next(current->next()); + } else { + _thread_list = p->next(); + } + _number_of_threads--; + oop threadObj = p->threadObj(); + bool daemon = true; + if (threadObj == NULL || !java_lang_Thread::is_daemon(threadObj)) { + _number_of_non_daemon_threads--; + daemon = false; + + // Only one thread left, do a notify on the Threads_lock so a thread waiting + // on destroy_vm will wake up. + if (number_of_non_daemon_threads() == 1) + Threads_lock->notify_all(); + } + ThreadService::remove_thread(p, daemon); + + // Make sure that safepoint code disregard this thread. This is needed since + // the thread might mess around with locks after this point. This can cause it + // to do callbacks into the safepoint code. However, the safepoint code is not aware + // of this thread since it is removed from the queue. + p->set_terminated_value(); + } // unlock Threads_lock + + // Since Events::log uses a lock, we grab it outside the Threads_lock + Events::log("Thread exited: " INTPTR_FORMAT, p); +} + +// Threads_lock must be held when this is called (or must be called during a safepoint) +bool Threads::includes(JavaThread* p) { + assert(Threads_lock->is_locked(), "sanity check"); + ALL_JAVA_THREADS(q) { + if (q == p ) { + return true; + } + } + return false; +} + +// Operations on the Threads list for GC. These are not explicitly locked, +// but the garbage collector must provide a safe context for them to run. +// In particular, these things should never be called when the Threads_lock +// is held by some other thread. (Note: the Safepoint abstraction also +// uses the Threads_lock to gurantee this property. It also makes sure that +// all threads gets blocked when exiting or starting). + +void Threads::oops_do(OopClosure* f) { + ALL_JAVA_THREADS(p) { + p->oops_do(f); + } + VMThread::vm_thread()->oops_do(f); +} + +void Threads::possibly_parallel_oops_do(OopClosure* f) { + // Introduce a mechanism allowing parallel threads to claim threads as + // root groups. Overhead should be small enough to use all the time, + // even in sequential code. + SharedHeap* sh = SharedHeap::heap(); + bool is_par = (sh->n_par_threads() > 0); + int cp = SharedHeap::heap()->strong_roots_parity(); + ALL_JAVA_THREADS(p) { + if (p->claim_oops_do(is_par, cp)) { + p->oops_do(f); + } + } + VMThread* vmt = VMThread::vm_thread(); + if (vmt->claim_oops_do(is_par, cp)) + vmt->oops_do(f); +} + +#ifndef SERIALGC +// Used by ParallelScavenge +void Threads::create_thread_roots_tasks(GCTaskQueue* q) { + ALL_JAVA_THREADS(p) { + q->enqueue(new ThreadRootsTask(p)); + } + q->enqueue(new ThreadRootsTask(VMThread::vm_thread())); +} + +// Used by Parallel Old +void Threads::create_thread_roots_marking_tasks(GCTaskQueue* q) { + ALL_JAVA_THREADS(p) { + q->enqueue(new ThreadRootsMarkingTask(p)); + } + q->enqueue(new ThreadRootsMarkingTask(VMThread::vm_thread())); +} +#endif // SERIALGC + +void Threads::nmethods_do() { + ALL_JAVA_THREADS(p) { + p->nmethods_do(); + } + VMThread::vm_thread()->nmethods_do(); +} + +void Threads::gc_epilogue() { + ALL_JAVA_THREADS(p) { + p->gc_epilogue(); + } +} + +void Threads::gc_prologue() { + ALL_JAVA_THREADS(p) { + p->gc_prologue(); + } +} + +void Threads::deoptimized_wrt_marked_nmethods() { + ALL_JAVA_THREADS(p) { + p->deoptimized_wrt_marked_nmethods(); + } +} + + +// Get count Java threads that are waiting to enter the specified monitor. +GrowableArray* Threads::get_pending_threads(int count, + address monitor, bool doLock) { + assert(doLock || SafepointSynchronize::is_at_safepoint(), + "must grab Threads_lock or be at safepoint"); + GrowableArray* result = new GrowableArray(count); + + int i = 0; + { + MutexLockerEx ml(doLock ? Threads_lock : NULL); + ALL_JAVA_THREADS(p) { + if (p->is_Compiler_thread()) continue; + + address pending = (address)p->current_pending_monitor(); + if (pending == monitor) { // found a match + if (i < count) result->append(p); // save the first count matches + i++; + } + } + } + return result; +} + + +JavaThread *Threads::owning_thread_from_monitor_owner(address owner, bool doLock) { + assert(doLock || + Threads_lock->owned_by_self() || + SafepointSynchronize::is_at_safepoint(), + "must grab Threads_lock or be at safepoint"); + + // NULL owner means not locked so we can skip the search + if (owner == NULL) return NULL; + + { + MutexLockerEx ml(doLock ? Threads_lock : NULL); + ALL_JAVA_THREADS(p) { + // first, see if owner is the address of a Java thread + if (owner == (address)p) return p; + } + } + assert(UseHeavyMonitors == false, "Did not find owning Java thread with UseHeavyMonitors enabled"); + if (UseHeavyMonitors) return NULL; + + // + // If we didn't find a matching Java thread and we didn't force use of + // heavyweight monitors, then the owner is the stack address of the + // Lock Word in the owning Java thread's stack. + // + // We can't use Thread::is_lock_owned() or Thread::lock_is_in_stack() because + // those routines rely on the "current" stack pointer. That would be our + // stack pointer which is not relevant to the question. Instead we use the + // highest lock ever entered by the thread and find the thread that is + // higher than and closest to our target stack address. + // + address least_diff = 0; + bool least_diff_initialized = false; + JavaThread* the_owner = NULL; + { + MutexLockerEx ml(doLock ? Threads_lock : NULL); + ALL_JAVA_THREADS(q) { + address addr = q->highest_lock(); + if (addr == NULL || addr < owner) continue; // thread has entered no monitors or is too low + address diff = (address)(addr - owner); + if (!least_diff_initialized || diff < least_diff) { + least_diff_initialized = true; + least_diff = diff; + the_owner = q; + } + } + } + assert(the_owner != NULL, "Did not find owning Java thread for lock word address"); + return the_owner; +} + +// Threads::print_on() is called at safepoint by VM_PrintThreads operation. +void Threads::print_on(outputStream* st, bool print_stacks, bool internal_format, bool print_concurrent_locks) { + char buf[32]; + st->print_cr(os::local_time_string(buf, sizeof(buf))); + + st->print_cr("Full thread dump %s (%s %s):", + Abstract_VM_Version::vm_name(), + Abstract_VM_Version::vm_release(), + Abstract_VM_Version::vm_info_string() + ); + st->cr(); + +#ifndef SERIALGC + // Dump concurrent locks + ConcurrentLocksDump concurrent_locks; + if (print_concurrent_locks) { + concurrent_locks.dump_at_safepoint(); + } +#endif // SERIALGC + + ALL_JAVA_THREADS(p) { + ResourceMark rm; + p->print_on(st); + if (print_stacks) { + if (internal_format) { + p->trace_stack(); + } else { + p->print_stack_on(st); + } + } + st->cr(); +#ifndef SERIALGC + if (print_concurrent_locks) { + concurrent_locks.print_locks_on(p, st); + } +#endif // SERIALGC + } + + VMThread::vm_thread()->print_on(st); + st->cr(); + Universe::heap()->print_gc_threads_on(st); + WatcherThread* wt = WatcherThread::watcher_thread(); + if (wt != NULL) wt->print_on(st); + st->cr(); + CompileBroker::print_compiler_threads_on(st); + st->flush(); +} + +// Threads::print_on_error() is called by fatal error handler. It's possible +// that VM is not at safepoint and/or current thread is inside signal handler. +// Don't print stack trace, as the stack may not be walkable. Don't allocate +// memory (even in resource area), it might deadlock the error handler. +void Threads::print_on_error(outputStream* st, Thread* current, char* buf, int buflen) { + bool found_current = false; + st->print_cr("Java Threads: ( => current thread )"); + ALL_JAVA_THREADS(thread) { + bool is_current = (current == thread); + found_current = found_current || is_current; + + st->print("%s", is_current ? "=>" : " "); + + st->print(PTR_FORMAT, thread); + st->print(" "); + thread->print_on_error(st, buf, buflen); + st->cr(); + } + st->cr(); + + st->print_cr("Other Threads:"); + if (VMThread::vm_thread()) { + bool is_current = (current == VMThread::vm_thread()); + found_current = found_current || is_current; + st->print("%s", current == VMThread::vm_thread() ? "=>" : " "); + + st->print(PTR_FORMAT, VMThread::vm_thread()); + st->print(" "); + VMThread::vm_thread()->print_on_error(st, buf, buflen); + st->cr(); + } + WatcherThread* wt = WatcherThread::watcher_thread(); + if (wt != NULL) { + bool is_current = (current == wt); + found_current = found_current || is_current; + st->print("%s", is_current ? "=>" : " "); + + st->print(PTR_FORMAT, wt); + st->print(" "); + wt->print_on_error(st, buf, buflen); + st->cr(); + } + if (!found_current) { + st->cr(); + st->print("=>" PTR_FORMAT " (exited) ", current); + current->print_on_error(st, buf, buflen); + st->cr(); + } +} + + +// Lifecycle management for TSM ParkEvents. +// ParkEvents are type-stable (TSM). +// In our particular implementation they happen to be immortal. +// +// We manage concurrency on the FreeList with a CAS-based +// detach-modify-reattach idiom that avoids the ABA problems +// that would otherwise be present in a simple CAS-based +// push-pop implementation. (push-one and pop-all) +// +// Caveat: Allocate() and Release() may be called from threads +// other than the thread associated with the Event! +// If we need to call Allocate() when running as the thread in +// question then look for the PD calls to initialize native TLS. +// Native TLS (Win32/Linux/Solaris) can only be initialized or +// accessed by the associated thread. +// See also pd_initialize(). +// +// Note that we could defer associating a ParkEvent with a thread +// until the 1st time the thread calls park(). unpark() calls to +// an unprovisioned thread would be ignored. The first park() call +// for a thread would allocate and associate a ParkEvent and return +// immediately. + +volatile int ParkEvent::ListLock = 0 ; +ParkEvent * volatile ParkEvent::FreeList = NULL ; + +ParkEvent * ParkEvent::Allocate (Thread * t) { + // In rare cases -- JVM_RawMonitor* operations -- we can find t == null. + ParkEvent * ev ; + + // Start by trying to recycle an existing but unassociated + // ParkEvent from the global free list. + for (;;) { + ev = FreeList ; + if (ev == NULL) break ; + // 1: Detach - sequester or privatize the list + // Tantamount to ev = Swap (&FreeList, NULL) + if (Atomic::cmpxchg_ptr (NULL, &FreeList, ev) != ev) { + continue ; + } + + // We've detached the list. The list in-hand is now + // local to this thread. This thread can operate on the + // list without risk of interference from other threads. + // 2: Extract -- pop the 1st element from the list. + ParkEvent * List = ev->FreeNext ; + if (List == NULL) break ; + for (;;) { + // 3: Try to reattach the residual list + guarantee (List != NULL, "invariant") ; + ParkEvent * Arv = (ParkEvent *) Atomic::cmpxchg_ptr (List, &FreeList, NULL) ; + if (Arv == NULL) break ; + + // New nodes arrived. Try to detach the recent arrivals. + if (Atomic::cmpxchg_ptr (NULL, &FreeList, Arv) != Arv) { + continue ; + } + guarantee (Arv != NULL, "invariant") ; + // 4: Merge Arv into List + ParkEvent * Tail = List ; + while (Tail->FreeNext != NULL) Tail = Tail->FreeNext ; + Tail->FreeNext = Arv ; + } + break ; + } + + if (ev != NULL) { + guarantee (ev->AssociatedWith == NULL, "invariant") ; + } else { + // Do this the hard way -- materialize a new ParkEvent. + // In rare cases an allocating thread might detach a long list -- + // installing null into FreeList -- and then stall or be obstructed. + // A 2nd thread calling Allocate() would see FreeList == null. + // The list held privately by the 1st thread is unavailable to the 2nd thread. + // In that case the 2nd thread would have to materialize a new ParkEvent, + // even though free ParkEvents existed in the system. In this case we end up + // with more ParkEvents in circulation than we need, but the race is + // rare and the outcome is benign. Ideally, the # of extant ParkEvents + // is equal to the maximum # of threads that existed at any one time. + // Because of the race mentioned above, segments of the freelist + // can be transiently inaccessible. At worst we may end up with the + // # of ParkEvents in circulation slightly above the ideal. + // Note that if we didn't have the TSM/immortal constraint, then + // when reattaching, above, we could trim the list. + ev = new ParkEvent () ; + guarantee ((intptr_t(ev) & 0xFF) == 0, "invariant") ; + } + ev->reset() ; // courtesy to caller + ev->AssociatedWith = t ; // Associate ev with t + ev->FreeNext = NULL ; + return ev ; +} + +void ParkEvent::Release (ParkEvent * ev) { + if (ev == NULL) return ; + guarantee (ev->FreeNext == NULL , "invariant") ; + ev->AssociatedWith = NULL ; + for (;;) { + // Push ev onto FreeList + // The mechanism is "half" lock-free. + ParkEvent * List = FreeList ; + ev->FreeNext = List ; + if (Atomic::cmpxchg_ptr (ev, &FreeList, List) == List) break ; + } +} + +// Override operator new and delete so we can ensure that the +// least significant byte of ParkEvent addresses is 0. +// Beware that excessive address alignment is undesirable +// as it can result in D$ index usage imbalance as +// well as bank access imbalance on Niagara-like platforms, +// although Niagara's hash function should help. + +void * ParkEvent::operator new (size_t sz) { + return (void *) ((intptr_t (CHeapObj::operator new (sz + 256)) + 256) & -256) ; +} + +void ParkEvent::operator delete (void * a) { + // ParkEvents are type-stable and immortal ... + ShouldNotReachHere(); +} + + +// 6399321 As a temporary measure we copied & modified the ParkEvent:: +// allocate() and release() code for use by Parkers. The Parker:: forms +// will eventually be removed as we consolide and shift over to ParkEvents +// for both builtin synchronization and JSR166 operations. + +volatile int Parker::ListLock = 0 ; +Parker * volatile Parker::FreeList = NULL ; + +Parker * Parker::Allocate (JavaThread * t) { + guarantee (t != NULL, "invariant") ; + Parker * p ; + + // Start by trying to recycle an existing but unassociated + // Parker from the global free list. + for (;;) { + p = FreeList ; + if (p == NULL) break ; + // 1: Detach + // Tantamount to p = Swap (&FreeList, NULL) + if (Atomic::cmpxchg_ptr (NULL, &FreeList, p) != p) { + continue ; + } + + // We've detached the list. The list in-hand is now + // local to this thread. This thread can operate on the + // list without risk of interference from other threads. + // 2: Extract -- pop the 1st element from the list. + Parker * List = p->FreeNext ; + if (List == NULL) break ; + for (;;) { + // 3: Try to reattach the residual list + guarantee (List != NULL, "invariant") ; + Parker * Arv = (Parker *) Atomic::cmpxchg_ptr (List, &FreeList, NULL) ; + if (Arv == NULL) break ; + + // New nodes arrived. Try to detach the recent arrivals. + if (Atomic::cmpxchg_ptr (NULL, &FreeList, Arv) != Arv) { + continue ; + } + guarantee (Arv != NULL, "invariant") ; + // 4: Merge Arv into List + Parker * Tail = List ; + while (Tail->FreeNext != NULL) Tail = Tail->FreeNext ; + Tail->FreeNext = Arv ; + } + break ; + } + + if (p != NULL) { + guarantee (p->AssociatedWith == NULL, "invariant") ; + } else { + // Do this the hard way -- materialize a new Parker.. + // In rare cases an allocating thread might detach + // a long list -- installing null into FreeList --and + // then stall. Another thread calling Allocate() would see + // FreeList == null and then invoke the ctor. In this case we + // end up with more Parkers in circulation than we need, but + // the race is rare and the outcome is benign. + // Ideally, the # of extant Parkers is equal to the + // maximum # of threads that existed at any one time. + // Because of the race mentioned above, segments of the + // freelist can be transiently inaccessible. At worst + // we may end up with the # of Parkers in circulation + // slightly above the ideal. + p = new Parker() ; + } + p->AssociatedWith = t ; // Associate p with t + p->FreeNext = NULL ; + return p ; +} + + +void Parker::Release (Parker * p) { + if (p == NULL) return ; + guarantee (p->AssociatedWith != NULL, "invariant") ; + guarantee (p->FreeNext == NULL , "invariant") ; + p->AssociatedWith = NULL ; + for (;;) { + // Push p onto FreeList + Parker * List = FreeList ; + p->FreeNext = List ; + if (Atomic::cmpxchg_ptr (p, &FreeList, List) == List) break ; + } +} + +void Threads::verify() { + ALL_JAVA_THREADS(p) { + p->verify(); + } + VMThread* thread = VMThread::vm_thread(); + if (thread != NULL) thread->verify(); +} diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp new file mode 100644 index 00000000000..72277e039b3 --- /dev/null +++ b/hotspot/src/share/vm/runtime/thread.hpp @@ -0,0 +1,1757 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ThreadSafepointState; +class ThreadProfiler; + +class JvmtiThreadState; +class JvmtiGetLoadedClassesClosure; +class ThreadStatistics; +class ConcurrentLocksDump; +class ParkEvent ; + +class ciEnv; +class CompileThread; +class CompileLog; +class CompileTask; +class CompileQueue; +class CompilerCounters; +class vframeArray; + +class DeoptResourceMark; +class jvmtiDeferredLocalVariableSet; + +class GCTaskQueue; +class ThreadClosure; +class IdealGraphPrinter; + +// Class hierarchy +// - Thread +// - VMThread +// - JavaThread +// - WatcherThread + +class Thread: public ThreadShadow { + friend class VMStructs; + private: + // Exception handling + // (Note: _pending_exception and friends are in ThreadShadow) + //oop _pending_exception; // pending exception for current thread + // const char* _exception_file; // file information for exception (debugging only) + // int _exception_line; // line information for exception (debugging only) + + // Support for forcing alignment of thread objects for biased locking + void* _real_malloc_address; + public: + void* operator new(size_t size); + void operator delete(void* p); + private: + + // *************************************************************** + // Suspend and resume support + // *************************************************************** + // + // VM suspend/resume no longer exists - it was once used for various + // things including safepoints but was deprecated and finally removed + // in Java 7. Because VM suspension was considered "internal" Java-level + // suspension was considered "external", and this legacy naming scheme + // remains. + // + // External suspend/resume requests come from JVM_SuspendThread, + // JVM_ResumeThread, JVMTI SuspendThread, and finally JVMTI + // ResumeThread. External + // suspend requests cause _external_suspend to be set and external + // resume requests cause _external_suspend to be cleared. + // External suspend requests do not nest on top of other external + // suspend requests. The higher level APIs reject suspend requests + // for already suspended threads. + // + // The external_suspend + // flag is checked by has_special_runtime_exit_condition() and java thread + // will self-suspend when handle_special_runtime_exit_condition() is + // called. Most uses of the _thread_blocked state in JavaThreads are + // considered the same as being externally suspended; if the blocking + // condition lifts, the JavaThread will self-suspend. Other places + // where VM checks for external_suspend include: + // + mutex granting (do not enter monitors when thread is suspended) + // + state transitions from _thread_in_native + // + // In general, java_suspend() does not wait for an external suspend + // request to complete. When it returns, the only guarantee is that + // the _external_suspend field is true. + // + // wait_for_ext_suspend_completion() is used to wait for an external + // suspend request to complete. External suspend requests are usually + // followed by some other interface call that requires the thread to + // be quiescent, e.g., GetCallTrace(). By moving the "wait time" into + // the interface that requires quiescence, we give the JavaThread a + // chance to self-suspend before we need it to be quiescent. This + // improves overall suspend/query performance. + // + // _suspend_flags controls the behavior of java_ suspend/resume. + // It must be set under the protection of SR_lock. Read from the flag is + // OK without SR_lock as long as the value is only used as a hint. + // (e.g., check _external_suspend first without lock and then recheck + // inside SR_lock and finish the suspension) + // + // _suspend_flags is also overloaded for other "special conditions" so + // that a single check indicates whether any special action is needed + // eg. for async exceptions. + // ------------------------------------------------------------------- + // Notes: + // 1. The suspend/resume logic no longer uses ThreadState in OSThread + // but we still update its value to keep other part of the system (mainly + // JVMTI) happy. ThreadState is legacy code (see notes in + // osThread.hpp). + // + // 2. It would be more natural if set_external_suspend() is private and + // part of java_suspend(), but that probably would affect the suspend/query + // performance. Need more investigation on this. + // + + // suspend/resume lock: used for self-suspend + Monitor* _SR_lock; + + protected: + enum SuspendFlags { + // NOTE: avoid using the sign-bit as cc generates different test code + // when the sign-bit is used, and sometimes incorrectly - see CR 6398077 + + _external_suspend = 0x20000000U, // thread is asked to self suspend + _ext_suspended = 0x40000000U, // thread has self-suspended + _deopt_suspend = 0x10000000U, // thread needs to self suspend for deopt + + _has_async_exception = 0x00000001U // there is a pending async exception + }; + + // various suspension related flags - atomically updated + // overloaded for async exception checking in check_special_condition_for_native_trans. + volatile uint32_t _suspend_flags; + + private: + int _num_nested_signal; + + public: + void enter_signal_handler() { _num_nested_signal++; } + void leave_signal_handler() { _num_nested_signal--; } + bool is_inside_signal_handler() const { return _num_nested_signal > 0; } + + private: + // Debug tracing + static void trace(const char* msg, const Thread* const thread) PRODUCT_RETURN; + + // Active_handles points to a block of handles + JNIHandleBlock* _active_handles; + + // One-element thread local free list + JNIHandleBlock* _free_handle_block; + + // Point to the last handle mark + HandleMark* _last_handle_mark; + + // The parity of the last strong_roots iteration in which this thread was + // claimed as a task. + jint _oops_do_parity; + + public: + void set_last_handle_mark(HandleMark* mark) { _last_handle_mark = mark; } + HandleMark* last_handle_mark() const { return _last_handle_mark; } + private: + + // debug support for checking if code does allow safepoints or not + // GC points in the VM can happen because of allocation, invoking a VM operation, or blocking on + // mutex, or blocking on an object synchronizer (Java locking). + // If !allow_safepoint(), then an assertion failure will happen in any of the above cases + // If !allow_allocation(), then an assertion failure will happen during allocation + // (Hence, !allow_safepoint() => !allow_allocation()). + // + // The two classes No_Safepoint_Verifier and No_Allocation_Verifier are used to set these counters. + // + NOT_PRODUCT(int _allow_safepoint_count;) // If 0, thread allow a safepoint to happen + debug_only (int _allow_allocation_count;) // If 0, the thread is allowed to allocate oops. + + // Record when GC is locked out via the GC_locker mechanism + CHECK_UNHANDLED_OOPS_ONLY(int _gc_locked_out_count;) + + friend class No_Alloc_Verifier; + friend class No_Safepoint_Verifier; + friend class Pause_No_Safepoint_Verifier; + friend class ThreadLocalStorage; + friend class GC_locker; + + // In order for all threads to be able to use fast locking, we need to know the highest stack + // address of where a lock is on the stack (stacks normally grow towards lower addresses). This + // variable is initially set to NULL, indicating no locks are used by the thread. During the thread's + // execution, it will be set whenever locking can happen, i.e., when we call out to Java code or use + // an ObjectLocker. The value is never decreased, hence, it will over the lifetime of a thread + // approximate the real stackbase. + address _highest_lock; // Highest stack address where a JavaLock exist + + ThreadLocalAllocBuffer _tlab; // Thread-local eden + + int _vm_operation_started_count; // VM_Operation support + int _vm_operation_completed_count; // VM_Operation support + + ObjectMonitor* _current_pending_monitor; // ObjectMonitor this thread + // is waiting to lock + bool _current_pending_monitor_is_from_java; // locking is from Java code + + // ObjectMonitor on which this thread called Object.wait() + ObjectMonitor* _current_waiting_monitor; + + // Private thread-local objectmonitor list - a simple cache organized as a SLL. + public: + ObjectMonitor * omFreeList ; + int omFreeCount ; // length of omFreeList + int omFreeProvision ; // reload chunk size + + public: + enum { + is_definitely_current_thread = true + }; + + // Constructor + Thread(); + virtual ~Thread(); + + // initializtion + void initialize_thread_local_storage(); + + // thread entry point + virtual void run(); + + // Testers + virtual bool is_VM_thread() const { return false; } + virtual bool is_Java_thread() const { return false; } + // Remove this ifdef when C1 is ported to the compiler interface. + virtual bool is_Compiler_thread() const { return false; } + virtual bool is_hidden_from_external_view() const { return false; } + virtual bool is_jvmti_agent_thread() const { return false; } + // True iff the thread can perform GC operations at a safepoint. + // Generally will be true only of VM thread and parallel GC WorkGang + // threads. + virtual bool is_GC_task_thread() const { return false; } + virtual bool is_Watcher_thread() const { return false; } + virtual bool is_ConcurrentGC_thread() const { return false; } + + virtual char* name() const { return (char*)"Unknown thread"; } + + // Returns the current thread + static inline Thread* current(); + + // Common thread operations + static void set_priority(Thread* thread, ThreadPriority priority); + static ThreadPriority get_priority(const Thread* const thread); + static void start(Thread* thread); + static void interrupt(Thread* thr); + static bool is_interrupted(Thread* thr, bool clear_interrupted); + + Monitor* SR_lock() const { return _SR_lock; } + + bool has_async_exception() const { return (_suspend_flags & _has_async_exception) != 0; } + + void set_suspend_flag(SuspendFlags f) { + assert(sizeof(jint) == sizeof(_suspend_flags), "size mismatch"); + uint32_t flags; + do { + flags = _suspend_flags; + } + while (Atomic::cmpxchg((jint)(flags | f), + (volatile jint*)&_suspend_flags, + (jint)flags) != (jint)flags); + } + void clear_suspend_flag(SuspendFlags f) { + assert(sizeof(jint) == sizeof(_suspend_flags), "size mismatch"); + uint32_t flags; + do { + flags = _suspend_flags; + } + while (Atomic::cmpxchg((jint)(flags & ~f), + (volatile jint*)&_suspend_flags, + (jint)flags) != (jint)flags); + } + + void set_has_async_exception() { + set_suspend_flag(_has_async_exception); + } + void clear_has_async_exception() { + clear_suspend_flag(_has_async_exception); + } + + // Support for Unhandled Oop detection +#ifdef CHECK_UNHANDLED_OOPS + private: + UnhandledOops *_unhandled_oops; + public: + UnhandledOops* unhandled_oops() { return _unhandled_oops; } + // Mark oop safe for gc. It may be stack allocated but won't move. + void allow_unhandled_oop(oop *op) { + if (CheckUnhandledOops) unhandled_oops()->allow_unhandled_oop(op); + } + // Clear oops at safepoint so crashes point to unhandled oop violator + void clear_unhandled_oops() { + if (CheckUnhandledOops) unhandled_oops()->clear_unhandled_oops(); + } + bool is_gc_locked_out() { return _gc_locked_out_count > 0; } +#endif // CHECK_UNHANDLED_OOPS + + public: + // Installs a pending exception to be inserted later + static void send_async_exception(oop thread_oop, oop java_throwable); + + // Resource area + ResourceArea* resource_area() const { return _resource_area; } + void set_resource_area(ResourceArea* area) { _resource_area = area; } + + OSThread* osthread() const { return _osthread; } + void set_osthread(OSThread* thread) { _osthread = thread; } + + // JNI handle support + JNIHandleBlock* active_handles() const { return _active_handles; } + void set_active_handles(JNIHandleBlock* block) { _active_handles = block; } + JNIHandleBlock* free_handle_block() const { return _free_handle_block; } + void set_free_handle_block(JNIHandleBlock* block) { _free_handle_block = block; } + + // Internal handle support + HandleArea* handle_area() const { return _handle_area; } + void set_handle_area(HandleArea* area) { _handle_area = area; } + + // Thread-Local Allocation Buffer (TLAB) support + ThreadLocalAllocBuffer& tlab() { return _tlab; } + void initialize_tlab() { + if (UseTLAB) { + tlab().initialize(); + } + } + + // VM operation support + int vm_operation_ticket() { return ++_vm_operation_started_count; } + int vm_operation_completed_count() { return _vm_operation_completed_count; } + void increment_vm_operation_completed_count() { _vm_operation_completed_count++; } + + // For tracking the heavyweight monitor the thread is pending on. + ObjectMonitor* current_pending_monitor() { + return _current_pending_monitor; + } + void set_current_pending_monitor(ObjectMonitor* monitor) { + _current_pending_monitor = monitor; + } + void set_current_pending_monitor_is_from_java(bool from_java) { + _current_pending_monitor_is_from_java = from_java; + } + bool current_pending_monitor_is_from_java() { + return _current_pending_monitor_is_from_java; + } + + // For tracking the ObjectMonitor on which this thread called Object.wait() + ObjectMonitor* current_waiting_monitor() { + return _current_waiting_monitor; + } + void set_current_waiting_monitor(ObjectMonitor* monitor) { + _current_waiting_monitor = monitor; + } + + // GC support + // Apply "f->do_oop" to all root oops in "this". + void oops_do(OopClosure* f); + + // Handles the parallel case for the method below. +private: + bool claim_oops_do_par_case(int collection_parity); +public: + // Requires that "collection_parity" is that of the current strong roots + // iteration. If "is_par" is false, sets the parity of "this" to + // "collection_parity", and returns "true". If "is_par" is true, + // uses an atomic instruction to set the current threads parity to + // "collection_parity", if it is not already. Returns "true" iff the + // calling thread does the update, this indicates that the calling thread + // has claimed the thread's stack as a root groop in the current + // collection. + bool claim_oops_do(bool is_par, int collection_parity) { + if (!is_par) { + _oops_do_parity = collection_parity; + return true; + } else { + return claim_oops_do_par_case(collection_parity); + } + } + + // Sweeper support + void nmethods_do(); + + // Fast-locking support + address highest_lock() const { return _highest_lock; } + void update_highest_lock(address base) { if (base > _highest_lock) _highest_lock = base; } + + // Tells if adr belong to this thread. This is used + // for checking if a lock is owned by the running thread. + // Warning: the method can only be used on the running thread + // Fast lock support uses these methods + virtual bool lock_is_in_stack(address adr) const; + virtual bool is_lock_owned(address adr) const; + + // Check if address is in the stack of the thread (not just for locks). + bool is_in_stack(address adr) const; + + // Sets this thread as starting thread. Returns failure if thread + // creation fails due to lack of memory, too many threads etc. + bool set_as_starting_thread(); + + protected: + // OS data associated with the thread + OSThread* _osthread; // Platform-specific thread information + + // Thread local resource area for temporary allocation within the VM + ResourceArea* _resource_area; + + // Thread local handle area for allocation of handles within the VM + HandleArea* _handle_area; + + // Support for stack overflow handling, get_thread, etc. + address _stack_base; + size_t _stack_size; + uintptr_t _self_raw_id; // used by get_thread (mutable) + int _lgrp_id; + + public: + // Stack overflow support + address stack_base() const { assert(_stack_base != NULL,"Sanity check"); return _stack_base; } + + void set_stack_base(address base) { _stack_base = base; } + size_t stack_size() const { return _stack_size; } + void set_stack_size(size_t size) { _stack_size = size; } + void record_stack_base_and_size(); + + int lgrp_id() const { return _lgrp_id; } + void set_lgrp_id(int value) { _lgrp_id = value; } + + // Printing + void print_on(outputStream* st) const; + void print() const { print_on(tty); } + virtual void print_on_error(outputStream* st, char* buf, int buflen) const; + + // Debug-only code + +#ifdef ASSERT + private: + // Deadlock detection support for Mutex locks. List of locks own by thread. + Monitor *_owned_locks; + // Mutex::set_owner_implementation is the only place where _owned_locks is modified, + // thus the friendship + friend class Mutex; + friend class Monitor; + + public: + void print_owned_locks_on(outputStream* st) const; + void print_owned_locks() const { print_owned_locks_on(tty); } + Monitor * owned_locks() const { return _owned_locks; } + bool owns_locks() const { return owned_locks() != NULL; } + bool owns_locks_but_compiled_lock() const; + + // Deadlock detection + bool allow_allocation() { return _allow_allocation_count == 0; } +#endif + + void check_for_valid_safepoint_state(bool potential_vm_operation) PRODUCT_RETURN; + + private: + volatile int _jvmti_env_iteration_count; + + public: + void entering_jvmti_env_iteration() { ++_jvmti_env_iteration_count; } + void leaving_jvmti_env_iteration() { --_jvmti_env_iteration_count; } + bool is_inside_jvmti_env_iteration() { return _jvmti_env_iteration_count > 0; } + + // Code generation + static ByteSize exception_file_offset() { return byte_offset_of(Thread, _exception_file ); } + static ByteSize exception_line_offset() { return byte_offset_of(Thread, _exception_line ); } + static ByteSize active_handles_offset() { return byte_offset_of(Thread, _active_handles ); } + + static ByteSize stack_base_offset() { return byte_offset_of(Thread, _stack_base ); } + static ByteSize stack_size_offset() { return byte_offset_of(Thread, _stack_size ); } + static ByteSize omFreeList_offset() { return byte_offset_of(Thread, omFreeList); } + +#define TLAB_FIELD_OFFSET(name) \ + static ByteSize tlab_##name##_offset() { return byte_offset_of(Thread, _tlab) + ThreadLocalAllocBuffer::name##_offset(); } + + TLAB_FIELD_OFFSET(start) + TLAB_FIELD_OFFSET(end) + TLAB_FIELD_OFFSET(top) + TLAB_FIELD_OFFSET(pf_top) + TLAB_FIELD_OFFSET(size) // desired_size + TLAB_FIELD_OFFSET(refill_waste_limit) + TLAB_FIELD_OFFSET(number_of_refills) + TLAB_FIELD_OFFSET(fast_refill_waste) + TLAB_FIELD_OFFSET(slow_allocations) + +#undef TLAB_FIELD_OFFSET + + public: + volatile intptr_t _Stalled ; + volatile int _TypeTag ; + ParkEvent * _ParkEvent ; // for synchronized() + ParkEvent * _SleepEvent ; // for Thread.sleep + ParkEvent * _MutexEvent ; // for native internal Mutex/Monitor + ParkEvent * _MuxEvent ; // for low-level muxAcquire-muxRelease + int NativeSyncRecursion ; // diagnostic + + volatile int _OnTrap ; // Resume-at IP delta + jint _hashStateW ; // Marsaglia Shift-XOR thread-local RNG + jint _hashStateX ; // thread-specific hashCode generator state + jint _hashStateY ; + jint _hashStateZ ; + void * _schedctl ; + + intptr_t _ScratchA, _ScratchB ; // Scratch locations for fast-path sync code + static ByteSize ScratchA_offset() { return byte_offset_of(Thread, _ScratchA ); } + static ByteSize ScratchB_offset() { return byte_offset_of(Thread, _ScratchB ); } + + volatile jint rng [4] ; // RNG for spin loop + + // Low-level leaf-lock primitives used to implement synchronization + // and native monitor-mutex infrastructure. + // Not for general synchronization use. + static void SpinAcquire (volatile int * Lock, const char * Name) ; + static void SpinRelease (volatile int * Lock) ; + static void muxAcquire (volatile intptr_t * Lock, const char * Name) ; + static void muxAcquireW (volatile intptr_t * Lock, ParkEvent * ev) ; + static void muxRelease (volatile intptr_t * Lock) ; + +}; + +// Inline implementation of Thread::current() +// Thread::current is "hot" it's called > 128K times in the 1st 500 msecs of +// startup. +// ThreadLocalStorage::thread is warm -- it's called > 16K times in the same +// period. This is inlined in thread_.inline.hpp. + +inline Thread* Thread::current() { +#ifdef ASSERT +// This function is very high traffic. Define PARANOID to enable expensive +// asserts. +#ifdef PARANOID + // Signal handler should call ThreadLocalStorage::get_thread_slow() + Thread* t = ThreadLocalStorage::get_thread_slow(); + assert(t != NULL && !t->is_inside_signal_handler(), + "Don't use Thread::current() inside signal handler"); +#endif +#endif + Thread* thread = ThreadLocalStorage::thread(); + assert(thread != NULL, "just checking"); + return thread; +} + +// Name support for threads. non-JavaThread subclasses with multiple +// uniquely named instances should derive from this. +class NamedThread: public Thread { + friend class VMStructs; + enum { + max_name_len = 64 + }; + private: + char* _name; + public: + NamedThread(); + ~NamedThread(); + // May only be called once per thread. + void set_name(const char* format, ...); + virtual char* name() const { return _name == NULL ? (char*)"Unknown Thread" : _name; } +}; + +// Worker threads are named and have an id of an assigned work. +class WorkerThread: public NamedThread { +private: + uint _id; +public: + WorkerThread() : _id(0) { } + void set_id(uint work_id) { _id = work_id; } + uint id() const { return _id; } +}; + +// A single WatcherThread is used for simulating timer interrupts. +class WatcherThread: public Thread { + friend class VMStructs; + public: + virtual void run(); + + private: + static WatcherThread* _watcher_thread; + + static bool _should_terminate; + public: + enum SomeConstants { + delay_interval = 10 // interrupt delay in milliseconds + }; + + // Constructor + WatcherThread(); + + // Tester + bool is_Watcher_thread() const { return true; } + + // Printing + char* name() const { return (char*)"VM Periodic Task Thread"; } + void print_on(outputStream* st) const; + void print() const { print_on(tty); } + + // Returns the single instance of WatcherThread + static WatcherThread* watcher_thread() { return _watcher_thread; } + + // Create and start the single instance of WatcherThread, or stop it on shutdown + static void start(); + static void stop(); +}; + + +class CompilerThread; + +typedef void (*ThreadFunction)(JavaThread*, TRAPS); + +class JavaThread: public Thread { + friend class VMStructs; + private: + JavaThread* _next; // The next thread in the Threads list + oop _threadObj; // The Java level thread object + +#ifdef ASSERT + private: + int _java_call_counter; + + public: + int java_call_counter() { return _java_call_counter; } + void inc_java_call_counter() { _java_call_counter++; } + void dec_java_call_counter() { + assert(_java_call_counter > 0, "Invalid nesting of JavaCallWrapper"); + _java_call_counter--; + } + private: // restore original namespace restriction +#endif // ifdef ASSERT + +#ifndef PRODUCT + public: + enum { + jump_ring_buffer_size = 16 + }; + private: // restore original namespace restriction +#endif + + JavaFrameAnchor _anchor; // Encapsulation of current java frame and it state + + ThreadFunction _entry_point; + + JNIEnv _jni_environment; + + // Deopt support + DeoptResourceMark* _deopt_mark; // Holds special ResourceMark for deoptimization + + intptr_t* _must_deopt_id; // id of frame that needs to be deopted once we + // transition out of native + + vframeArray* _vframe_array_head; // Holds the heap of the active vframeArrays + vframeArray* _vframe_array_last; // Holds last vFrameArray we popped + // Because deoptimization is lazy we must save jvmti requests to set locals + // in compiled frames until we deoptimize and we have an interpreter frame. + // This holds the pointer to array (yeah like there might be more than one) of + // description of compiled vframes that have locals that need to be updated. + GrowableArray* _deferred_locals_updates; + + // Handshake value for fixing 6243940. We need a place for the i2c + // adapter to store the callee methodOop. This value is NEVER live + // across a gc point so it does NOT have to be gc'd + // The handshake is open ended since we can't be certain that it will + // be NULLed. This is because we rarely ever see the race and end up + // in handle_wrong_method which is the backend of the handshake. See + // code in i2c adapters and handle_wrong_method. + + methodOop _callee_target; + + // Oop results of VM runtime calls + oop _vm_result; // Used to pass back an oop result into Java code, GC-preserved + oop _vm_result_2; // Used to pass back an oop result into Java code, GC-preserved + + MonitorChunk* _monitor_chunks; // Contains the off stack monitors + // allocated during deoptimization + // and by JNI_MonitorEnter/Exit + + // Async. requests support + enum AsyncRequests { + _no_async_condition = 0, + _async_exception, + _async_unsafe_access_error + }; + AsyncRequests _special_runtime_exit_condition; // Enum indicating pending async. request + oop _pending_async_exception; + + // Safepoint support + public: // Expose _thread_state for SafeFetchInt() + volatile JavaThreadState _thread_state; + private: + ThreadSafepointState *_safepoint_state; // Holds information about a thread during a safepoint + address _saved_exception_pc; // Saved pc of instruction where last implicit exception happened + + // JavaThread termination support + enum TerminatedTypes { + _not_terminated = 0xDEAD - 2, + _thread_exiting, // JavaThread::exit() has been called for this thread + _thread_terminated, // JavaThread is removed from thread list + _vm_exited // JavaThread is still executing native code, but VM is terminated + // only VM_Exit can set _vm_exited + }; + + // In general a JavaThread's _terminated field transitions as follows: + // + // _not_terminated => _thread_exiting => _thread_terminated + // + // _vm_exited is a special value to cover the case of a JavaThread + // executing native code after the VM itself is terminated. + TerminatedTypes _terminated; + // suspend/resume support + volatile bool _suspend_equivalent; // Suspend equivalent condition + jint _in_deopt_handler; // count of deoptimization + // handlers thread is in + volatile bool _doing_unsafe_access; // Thread may fault due to unsafe access + bool _do_not_unlock_if_synchronized; // Do not unlock the receiver of a synchronized method (since it was + // never locked) when throwing an exception. Used by interpreter only. + + // Flag to mark a JNI thread in the process of attaching - See CR 6404306 + // This flag is never set true other than at construction, and in that case + // is shortly thereafter set false + volatile bool _is_attaching; + + public: + // State of the stack guard pages for this thread. + enum StackGuardState { + stack_guard_unused, // not needed + stack_guard_yellow_disabled,// disabled (temporarily) after stack overflow + stack_guard_enabled // enabled + }; + + private: + + StackGuardState _stack_guard_state; + + // Compiler exception handling (NOTE: The _exception_oop is *NOT* the same as _pending_exception. It is + // used to temp. parsing values into and out of the runtime system during exception handling for compiled + // code) + volatile oop _exception_oop; // Exception thrown in compiled code + volatile address _exception_pc; // PC where exception happened + volatile address _exception_handler_pc; // PC for handler of exception + volatile int _exception_stack_size; // Size of frame where exception happened + + // support for compilation + bool _is_compiling; // is true if a compilation is active inthis thread (one compilation per thread possible) + + // support for JNI critical regions + jint _jni_active_critical; // count of entries into JNI critical region + + // For deadlock detection. + int _depth_first_number; + + // JVMTI PopFrame support + // This is set to popframe_pending to signal that top Java frame should be popped immediately + int _popframe_condition; + +#ifndef PRODUCT + int _jmp_ring_index; + struct { + // We use intptr_t instead of address so debugger doesn't try and display strings + intptr_t _target; + intptr_t _instruction; + const char* _file; + int _line; + } _jmp_ring[ jump_ring_buffer_size ]; +#endif /* PRODUCT */ + + friend class VMThread; + friend class ThreadWaitTransition; + friend class VM_Exit; + + void initialize(); // Initialized the instance variables + + public: + // Constructor + JavaThread(bool is_attaching = false); // for main thread and JNI attached threads + JavaThread(ThreadFunction entry_point, size_t stack_size = 0); + ~JavaThread(); + +#ifdef ASSERT + // verify this JavaThread hasn't be published in the Threads::list yet + void verify_not_published(); +#endif + + //JNI functiontable getter/setter for JVMTI jni function table interception API. + void set_jni_functions(struct JNINativeInterface_* functionTable) { + _jni_environment.functions = functionTable; + } + struct JNINativeInterface_* get_jni_functions() { + return (struct JNINativeInterface_ *)_jni_environment.functions; + } + + // Executes Shutdown.shutdown() + void invoke_shutdown_hooks(); + + // Cleanup on thread exit + enum ExitType { + normal_exit, + jni_detach + }; + void exit(bool destroy_vm, ExitType exit_type = normal_exit); + + void cleanup_failed_attach_current_thread(); + + // Testers + virtual bool is_Java_thread() const { return true; } + + // compilation + void set_is_compiling(bool f) { _is_compiling = f; } + bool is_compiling() const { return _is_compiling; } + + // Thread chain operations + JavaThread* next() const { return _next; } + void set_next(JavaThread* p) { _next = p; } + + // Thread oop. threadObj() can be NULL for initial JavaThread + // (or for threads attached via JNI) + oop threadObj() const { return _threadObj; } + void set_threadObj(oop p) { _threadObj = p; } + + ThreadPriority java_priority() const; // Read from threadObj() + + // Prepare thread and add to priority queue. If a priority is + // not specified, use the priority of the thread object. Threads_lock + // must be held while this function is called. + void prepare(jobject jni_thread, ThreadPriority prio=NoPriority); + + void set_saved_exception_pc(address pc) { _saved_exception_pc = pc; } + address saved_exception_pc() { return _saved_exception_pc; } + + + ThreadFunction entry_point() const { return _entry_point; } + + // Allocates a new Java level thread object for this thread. thread_name may be NULL. + void allocate_threadObj(Handle thread_group, char* thread_name, bool daemon, TRAPS); + + // Last frame anchor routines + + JavaFrameAnchor* frame_anchor(void) { return &_anchor; } + + // last_Java_sp + bool has_last_Java_frame() const { return _anchor.has_last_Java_frame(); } + intptr_t* last_Java_sp() const { return _anchor.last_Java_sp(); } + + // last_Java_pc + + address last_Java_pc(void) { return _anchor.last_Java_pc(); } + + // Safepoint support + JavaThreadState thread_state() const { return _thread_state; } + void set_thread_state(JavaThreadState s) { _thread_state=s; } + ThreadSafepointState *safepoint_state() const { return _safepoint_state; } + void set_safepoint_state(ThreadSafepointState *state) { _safepoint_state = state; } + bool is_at_poll_safepoint() { return _safepoint_state->is_at_poll_safepoint(); } + + // thread has called JavaThread::exit() or is terminated + bool is_exiting() { return _terminated == _thread_exiting || is_terminated(); } + // thread is terminated (no longer on the threads list); we compare + // against the two non-terminated values so that a freed JavaThread + // will also be considered terminated. + bool is_terminated() { return _terminated != _not_terminated && _terminated != _thread_exiting; } + void set_terminated(TerminatedTypes t) { _terminated = t; } + // special for Threads::remove() which is static: + void set_terminated_value() { _terminated = _thread_terminated; } + void block_if_vm_exited(); + + bool doing_unsafe_access() { return _doing_unsafe_access; } + void set_doing_unsafe_access(bool val) { _doing_unsafe_access = val; } + + bool do_not_unlock_if_synchronized() { return _do_not_unlock_if_synchronized; } + void set_do_not_unlock_if_synchronized(bool val) { _do_not_unlock_if_synchronized = val; } + + + // Suspend/resume support for JavaThread + + private: + void set_ext_suspended() { set_suspend_flag (_ext_suspended); } + void clear_ext_suspended() { clear_suspend_flag(_ext_suspended); } + + public: + void java_suspend(); + void java_resume(); + int java_suspend_self(); + + void check_and_wait_while_suspended() { + assert(JavaThread::current() == this, "sanity check"); + + bool do_self_suspend; + do { + // were we externally suspended while we were waiting? + do_self_suspend = handle_special_suspend_equivalent_condition(); + if (do_self_suspend) { + // don't surprise the thread that suspended us by returning + java_suspend_self(); + set_suspend_equivalent(); + } + } while (do_self_suspend); + } + static void check_safepoint_and_suspend_for_native_trans(JavaThread *thread); + // Check for async exception in addition to safepoint and suspend request. + static void check_special_condition_for_native_trans(JavaThread *thread); + + bool is_ext_suspend_completed(bool called_by_wait, int delay, uint32_t *bits); + bool is_ext_suspend_completed_with_lock(uint32_t *bits) { + MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + // Warning: is_ext_suspend_completed() may temporarily drop the + // SR_lock to allow the thread to reach a stable thread state if + // it is currently in a transient thread state. + return is_ext_suspend_completed(false /*!called_by_wait */, + SuspendRetryDelay, bits); + } + + // We cannot allow wait_for_ext_suspend_completion() to run forever or + // we could hang. SuspendRetryCount and SuspendRetryDelay are normally + // passed as the count and delay parameters. Experiments with specific + // calls to wait_for_ext_suspend_completion() can be done by passing + // other values in the code. Experiments with all calls can be done + // via the appropriate -XX options. + bool wait_for_ext_suspend_completion(int count, int delay, uint32_t *bits); + + void set_external_suspend() { set_suspend_flag (_external_suspend); } + void clear_external_suspend() { clear_suspend_flag(_external_suspend); } + + void set_deopt_suspend() { set_suspend_flag (_deopt_suspend); } + void clear_deopt_suspend() { clear_suspend_flag(_deopt_suspend); } + bool is_deopt_suspend() { return (_suspend_flags & _deopt_suspend) != 0; } + + bool is_external_suspend() const { + return (_suspend_flags & _external_suspend) != 0; + } + // Whenever a thread transitions from native to vm/java it must suspend + // if external|deopt suspend is present. + bool is_suspend_after_native() const { + return (_suspend_flags & (_external_suspend | _deopt_suspend) ) != 0; + } + + // external suspend request is completed + bool is_ext_suspended() const { + return (_suspend_flags & _ext_suspended) != 0; + } + + // legacy method that checked for either external suspension or vm suspension + bool is_any_suspended() const { + return is_ext_suspended(); + } + + bool is_external_suspend_with_lock() const { + MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + return is_external_suspend(); + } + + // Special method to handle a pending external suspend request + // when a suspend equivalent condition lifts. + bool handle_special_suspend_equivalent_condition() { + assert(is_suspend_equivalent(), + "should only be called in a suspend equivalence condition"); + MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + bool ret = is_external_suspend(); + if (!ret) { + // not about to self-suspend so clear suspend equivalence + clear_suspend_equivalent(); + } + // implied else: + // We have a pending external suspend request so we leave the + // suspend_equivalent flag set until java_suspend_self() sets + // the ext_suspended flag and clears the suspend_equivalent + // flag. This insures that wait_for_ext_suspend_completion() + // will return consistent values. + return ret; + } + + bool is_any_suspended_with_lock() const { + MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + return is_any_suspended(); + } + // utility methods to see if we are doing some kind of suspension + bool is_being_ext_suspended() const { + MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); + return is_ext_suspended() || is_external_suspend(); + } + + bool is_suspend_equivalent() const { return _suspend_equivalent; } + + void set_suspend_equivalent() { _suspend_equivalent = true; }; + void clear_suspend_equivalent() { _suspend_equivalent = false; }; + + // Thread.stop support + void send_thread_stop(oop throwable); + AsyncRequests clear_special_runtime_exit_condition() { + AsyncRequests x = _special_runtime_exit_condition; + _special_runtime_exit_condition = _no_async_condition; + return x; + } + + // Are any async conditions present? + bool has_async_condition() { return (_special_runtime_exit_condition != _no_async_condition); } + + void check_and_handle_async_exceptions(bool check_unsafe_error = true); + + // these next two are also used for self-suspension and async exception support + void handle_special_runtime_exit_condition(bool check_asyncs = true); + + // Return true if JavaThread has an asynchronous condition or + // if external suspension is requested. + bool has_special_runtime_exit_condition() { + // We call is_external_suspend() last since external suspend should + // be less common. Because we don't use is_external_suspend_with_lock + // it is possible that we won't see an asynchronous external suspend + // request that has just gotten started, i.e., SR_lock grabbed but + // _external_suspend field change either not made yet or not visible + // yet. However, this is okay because the request is asynchronous and + // we will see the new flag value the next time through. It's also + // possible that the external suspend request is dropped after + // we have checked is_external_suspend(), we will recheck its value + // under SR_lock in java_suspend_self(). + return (_special_runtime_exit_condition != _no_async_condition) || + is_external_suspend() || is_deopt_suspend(); + } + + void set_pending_unsafe_access_error() { _special_runtime_exit_condition = _async_unsafe_access_error; } + + void set_pending_async_exception(oop e) { + _pending_async_exception = e; + _special_runtime_exit_condition = _async_exception; + set_has_async_exception(); + } + + // Fast-locking support + bool is_lock_owned(address adr) const; + + // Accessors for vframe array top + // The linked list of vframe arrays are sorted on sp. This means when we + // unpack the head must contain the vframe array to unpack. + void set_vframe_array_head(vframeArray* value) { _vframe_array_head = value; } + vframeArray* vframe_array_head() const { return _vframe_array_head; } + + // Side structure for defering update of java frame locals until deopt occurs + GrowableArray* deferred_locals() const { return _deferred_locals_updates; } + void set_deferred_locals(GrowableArray* vf) { _deferred_locals_updates = vf; } + + // These only really exist to make debugging deopt problems simpler + + void set_vframe_array_last(vframeArray* value) { _vframe_array_last = value; } + vframeArray* vframe_array_last() const { return _vframe_array_last; } + + // The special resourceMark used during deoptimization + + void set_deopt_mark(DeoptResourceMark* value) { _deopt_mark = value; } + DeoptResourceMark* deopt_mark(void) { return _deopt_mark; } + + intptr_t* must_deopt_id() { return _must_deopt_id; } + void set_must_deopt_id(intptr_t* id) { _must_deopt_id = id; } + void clear_must_deopt_id() { _must_deopt_id = NULL; } + + methodOop callee_target() const { return _callee_target; } + void set_callee_target (methodOop x) { _callee_target = x; } + + // Oop results of vm runtime calls + oop vm_result() const { return _vm_result; } + void set_vm_result (oop x) { _vm_result = x; } + + oop vm_result_2() const { return _vm_result_2; } + void set_vm_result_2 (oop x) { _vm_result_2 = x; } + + // Exception handling for compiled methods + oop exception_oop() const { return _exception_oop; } + int exception_stack_size() const { return _exception_stack_size; } + address exception_pc() const { return _exception_pc; } + address exception_handler_pc() const { return _exception_handler_pc; } + + void set_exception_oop(oop o) { _exception_oop = o; } + void set_exception_pc(address a) { _exception_pc = a; } + void set_exception_handler_pc(address a) { _exception_handler_pc = a; } + void set_exception_stack_size(int size) { _exception_stack_size = size; } + + // Stack overflow support + inline size_t stack_available(address cur_sp); + address stack_yellow_zone_base() + { return (address)(stack_base() - (stack_size() - (stack_red_zone_size() + stack_yellow_zone_size()))); } + size_t stack_yellow_zone_size() + { return StackYellowPages * os::vm_page_size(); } + address stack_red_zone_base() + { return (address)(stack_base() - (stack_size() - stack_red_zone_size())); } + size_t stack_red_zone_size() + { return StackRedPages * os::vm_page_size(); } + bool in_stack_yellow_zone(address a) + { return (a <= stack_yellow_zone_base()) && (a >= stack_red_zone_base()); } + bool in_stack_red_zone(address a) + { return (a <= stack_red_zone_base()) && (a >= (address)((intptr_t)stack_base() - stack_size())); } + + void create_stack_guard_pages(); + void remove_stack_guard_pages(); + + void enable_stack_yellow_zone(); + void disable_stack_yellow_zone(); + void enable_stack_red_zone(); + void disable_stack_red_zone(); + + inline bool stack_yellow_zone_disabled(); + inline bool stack_yellow_zone_enabled(); + + // Attempt to reguard the stack after a stack overflow may have occurred. + // Returns true if (a) guard pages are not needed on this thread, (b) the + // pages are already guarded, or (c) the pages were successfully reguarded. + // Returns false if there is not enough stack space to reguard the pages, in + // which case the caller should unwind a frame and try again. The argument + // should be the caller's (approximate) sp. + bool reguard_stack(address cur_sp); + // Similar to above but see if current stackpoint is out of the guard area + // and reguard if possible. + bool reguard_stack(void); + + // Misc. accessors/mutators + void set_do_not_unlock(void) { _do_not_unlock_if_synchronized = true; } + void clr_do_not_unlock(void) { _do_not_unlock_if_synchronized = false; } + bool do_not_unlock(void) { return _do_not_unlock_if_synchronized; } + +#ifndef PRODUCT + void record_jump(address target, address instr, const char* file, int line); +#endif /* PRODUCT */ + + // For assembly stub generation + static ByteSize threadObj_offset() { return byte_offset_of(JavaThread, _threadObj ); } +#ifndef PRODUCT + static ByteSize jmp_ring_index_offset() { return byte_offset_of(JavaThread, _jmp_ring_index ); } + static ByteSize jmp_ring_offset() { return byte_offset_of(JavaThread, _jmp_ring ); } +#endif /* PRODUCT */ + static ByteSize jni_environment_offset() { return byte_offset_of(JavaThread, _jni_environment ); } + static ByteSize last_Java_sp_offset() { + return byte_offset_of(JavaThread, _anchor) + JavaFrameAnchor::last_Java_sp_offset(); + } + static ByteSize last_Java_pc_offset() { + return byte_offset_of(JavaThread, _anchor) + JavaFrameAnchor::last_Java_pc_offset(); + } + static ByteSize frame_anchor_offset() { + return byte_offset_of(JavaThread, _anchor); + } + static ByteSize callee_target_offset() { return byte_offset_of(JavaThread, _callee_target ); } + static ByteSize vm_result_offset() { return byte_offset_of(JavaThread, _vm_result ); } + static ByteSize vm_result_2_offset() { return byte_offset_of(JavaThread, _vm_result_2 ); } + static ByteSize thread_state_offset() { return byte_offset_of(JavaThread, _thread_state ); } + static ByteSize saved_exception_pc_offset() { return byte_offset_of(JavaThread, _saved_exception_pc ); } + static ByteSize osthread_offset() { return byte_offset_of(JavaThread, _osthread ); } + static ByteSize exception_oop_offset() { return byte_offset_of(JavaThread, _exception_oop ); } + static ByteSize exception_pc_offset() { return byte_offset_of(JavaThread, _exception_pc ); } + static ByteSize exception_handler_pc_offset() { return byte_offset_of(JavaThread, _exception_handler_pc); } + static ByteSize exception_stack_size_offset() { return byte_offset_of(JavaThread, _exception_stack_size); } + static ByteSize stack_guard_state_offset() { return byte_offset_of(JavaThread, _stack_guard_state ); } + static ByteSize suspend_flags_offset() { return byte_offset_of(JavaThread, _suspend_flags ); } + + static ByteSize do_not_unlock_if_synchronized_offset() { return byte_offset_of(JavaThread, _do_not_unlock_if_synchronized); } + + // Returns the jni environment for this thread + JNIEnv* jni_environment() { return &_jni_environment; } + + static JavaThread* thread_from_jni_environment(JNIEnv* env) { + JavaThread *thread_from_jni_env = (JavaThread*)((intptr_t)env - in_bytes(jni_environment_offset())); + // Only return NULL if thread is off the thread list; starting to + // exit should not return NULL. + if (thread_from_jni_env->is_terminated()) { + thread_from_jni_env->block_if_vm_exited(); + return NULL; + } else { + return thread_from_jni_env; + } + } + + // JNI critical regions. These can nest. + bool in_critical() { return _jni_active_critical > 0; } + void enter_critical() { assert(Thread::current() == this, + "this must be current thread"); + _jni_active_critical++; } + void exit_critical() { assert(Thread::current() == this, + "this must be current thread"); + _jni_active_critical--; + assert(_jni_active_critical >= 0, + "JNI critical nesting problem?"); } + + // For deadlock detection + int depth_first_number() { return _depth_first_number; } + void set_depth_first_number(int dfn) { _depth_first_number = dfn; } + + private: + void set_monitor_chunks(MonitorChunk* monitor_chunks) { _monitor_chunks = monitor_chunks; } + + public: + MonitorChunk* monitor_chunks() const { return _monitor_chunks; } + void add_monitor_chunk(MonitorChunk* chunk); + void remove_monitor_chunk(MonitorChunk* chunk); + bool in_deopt_handler() const { return _in_deopt_handler > 0; } + void inc_in_deopt_handler() { _in_deopt_handler++; } + void dec_in_deopt_handler() { + assert(_in_deopt_handler > 0, "mismatched deopt nesting"); + if (_in_deopt_handler > 0) { // robustness + _in_deopt_handler--; + } + } + + private: + void set_entry_point(ThreadFunction entry_point) { _entry_point = entry_point; } + + public: + + // Frame iteration; calls the function f for all frames on the stack + void frames_do(void f(frame*, const RegisterMap*)); + + // Memory operations + void oops_do(OopClosure* f); + + // Sweeper operations + void nmethods_do(); + + // Memory management operations + void gc_epilogue(); + void gc_prologue(); + + // Misc. operations + char* name() const { return (char*)get_thread_name(); } + void print_on(outputStream* st) const; + void print() const { print_on(tty); } + void print_value(); + void print_thread_state_on(outputStream* ) const PRODUCT_RETURN; + void print_thread_state() const PRODUCT_RETURN; + void print_on_error(outputStream* st, char* buf, int buflen) const; + void verify(); + const char* get_thread_name() const; +private: + // factor out low-level mechanics for use in both normal and error cases + const char* get_thread_name_string(char* buf = NULL, int buflen = 0) const; +public: + const char* get_threadgroup_name() const; + const char* get_parent_name() const; + + // Accessing frames + frame last_frame() { + _anchor.make_walkable(this); + return pd_last_frame(); + } + javaVFrame* last_java_vframe(RegisterMap* reg_map); + + // Returns method at 'depth' java or native frames down the stack + // Used for security checks + klassOop security_get_caller_class(int depth); + + // Print stack trace in external format + void print_stack_on(outputStream* st); + void print_stack() { print_stack_on(tty); } + + // Print stack traces in various internal formats + void trace_stack() PRODUCT_RETURN; + void trace_stack_from(vframe* start_vf) PRODUCT_RETURN; + void trace_frames() PRODUCT_RETURN; + + // Returns the number of stack frames on the stack + int depth() const; + + // Function for testing deoptimization + void deoptimize(); + void make_zombies(); + + void deoptimized_wrt_marked_nmethods(); + + // Profiling operation (see fprofile.cpp) + public: + bool profile_last_Java_frame(frame* fr); + + private: + ThreadProfiler* _thread_profiler; + private: + friend class FlatProfiler; // uses both [gs]et_thread_profiler. + friend class FlatProfilerTask; // uses get_thread_profiler. + friend class ThreadProfilerMark; // uses get_thread_profiler. + ThreadProfiler* get_thread_profiler() { return _thread_profiler; } + ThreadProfiler* set_thread_profiler(ThreadProfiler* tp) { + ThreadProfiler* result = _thread_profiler; + _thread_profiler = tp; + return result; + } + + // Static operations + public: + // Returns the running thread as a JavaThread + static inline JavaThread* current(); + + // Returns the active Java thread. Do not use this if you know you are calling + // from a JavaThread, as it's slower than JavaThread::current. If called from + // the VMThread, it also returns the JavaThread that instigated the VMThread's + // operation. You may not want that either. + static JavaThread* active(); + + inline CompilerThread* as_CompilerThread(); + + public: + virtual void run(); + void thread_main_inner(); + + private: + // PRIVILEGED STACK + PrivilegedElement* _privileged_stack_top; + GrowableArray* _array_for_gc; + public: + + // Returns the privileged_stack information. + PrivilegedElement* privileged_stack_top() const { return _privileged_stack_top; } + void set_privileged_stack_top(PrivilegedElement *e) { _privileged_stack_top = e; } + void register_array_for_gc(GrowableArray* array) { _array_for_gc = array; } + + public: + // Thread local information maintained by JVMTI. + void set_jvmti_thread_state(JvmtiThreadState *value) { _jvmti_thread_state = value; } + JvmtiThreadState *jvmti_thread_state() const { return _jvmti_thread_state; } + static ByteSize jvmti_thread_state_offset() { return byte_offset_of(JavaThread, _jvmti_thread_state); } + void set_jvmti_get_loaded_classes_closure(JvmtiGetLoadedClassesClosure* value) { _jvmti_get_loaded_classes_closure = value; } + JvmtiGetLoadedClassesClosure* get_jvmti_get_loaded_classes_closure() const { return _jvmti_get_loaded_classes_closure; } + + // JVMTI PopFrame support + // Setting and clearing popframe_condition + // All of these enumerated values are bits. popframe_pending + // indicates that a PopFrame() has been requested and not yet been + // completed. popframe_processing indicates that that PopFrame() is in + // the process of being completed. popframe_force_deopt_reexecution_bit + // indicates that special handling is required when returning to a + // deoptimized caller. + enum PopCondition { + popframe_inactive = 0x00, + popframe_pending_bit = 0x01, + popframe_processing_bit = 0x02, + popframe_force_deopt_reexecution_bit = 0x04 + }; + PopCondition popframe_condition() { return (PopCondition) _popframe_condition; } + void set_popframe_condition(PopCondition c) { _popframe_condition = c; } + void set_popframe_condition_bit(PopCondition c) { _popframe_condition |= c; } + void clear_popframe_condition() { _popframe_condition = popframe_inactive; } + static ByteSize popframe_condition_offset() { return byte_offset_of(JavaThread, _popframe_condition); } + bool has_pending_popframe() { return (popframe_condition() & popframe_pending_bit) != 0; } + bool popframe_forcing_deopt_reexecution() { return (popframe_condition() & popframe_force_deopt_reexecution_bit) != 0; } + void clear_popframe_forcing_deopt_reexecution() { _popframe_condition &= ~popframe_force_deopt_reexecution_bit; } +#ifdef CC_INTERP + bool pop_frame_pending(void) { return ((_popframe_condition & popframe_pending_bit) != 0); } + void clr_pop_frame_pending(void) { _popframe_condition = popframe_inactive; } + bool pop_frame_in_process(void) { return ((_popframe_condition & popframe_processing_bit) != 0); } + void set_pop_frame_in_process(void) { _popframe_condition |= popframe_processing_bit; } + void clr_pop_frame_in_process(void) { _popframe_condition &= ~popframe_processing_bit; } +#endif + + private: + // Saved incoming arguments to popped frame. + // Used only when popped interpreted frame returns to deoptimized frame. + void* _popframe_preserved_args; + int _popframe_preserved_args_size; + + public: + void popframe_preserve_args(ByteSize size_in_bytes, void* start); + void* popframe_preserved_args(); + ByteSize popframe_preserved_args_size(); + WordSize popframe_preserved_args_size_in_words(); + void popframe_free_preserved_args(); + + + private: + JvmtiThreadState *_jvmti_thread_state; + JvmtiGetLoadedClassesClosure* _jvmti_get_loaded_classes_closure; + + // Used by the interpreter in fullspeed mode for frame pop, method + // entry, method exit and single stepping support. This field is + // only set to non-zero by the VM_EnterInterpOnlyMode VM operation. + // It can be set to zero asynchronously (i.e., without a VM operation + // or a lock) so we have to be very careful. + int _interp_only_mode; + + public: + // used by the interpreter for fullspeed debugging support (see above) + static ByteSize interp_only_mode_offset() { return byte_offset_of(JavaThread, _interp_only_mode); } + bool is_interp_only_mode() { return (_interp_only_mode != 0); } + int get_interp_only_mode() { return _interp_only_mode; } + void increment_interp_only_mode() { ++_interp_only_mode; } + void decrement_interp_only_mode() { --_interp_only_mode; } + + private: + ThreadStatistics *_thread_stat; + + public: + ThreadStatistics* get_thread_stat() const { return _thread_stat; } + + // Return a blocker object for which this thread is blocked parking. + oop current_park_blocker(); + + private: + static size_t _stack_size_at_create; + + public: + static inline size_t stack_size_at_create(void) { + return _stack_size_at_create; + } + static inline void set_stack_size_at_create(size_t value) { + _stack_size_at_create = value; + } + + // Machine dependent stuff + #include "incls/_thread_pd.hpp.incl" + + public: + void set_blocked_on_compilation(bool value) { + _blocked_on_compilation = value; + } + + bool blocked_on_compilation() { + return _blocked_on_compilation; + } + protected: + bool _blocked_on_compilation; + + + // JSR166 per-thread parker +private: + Parker* _parker; +public: + Parker* parker() { return _parker; } + + // Biased locking support +private: + GrowableArray* _cached_monitor_info; +public: + GrowableArray* cached_monitor_info() { return _cached_monitor_info; } + void set_cached_monitor_info(GrowableArray* info) { _cached_monitor_info = info; } + + // clearing/querying jni attach status + bool is_attaching() const { return _is_attaching; } + void set_attached() { _is_attaching = false; OrderAccess::fence(); } +}; + +// Inline implementation of JavaThread::current +inline JavaThread* JavaThread::current() { + Thread* thread = ThreadLocalStorage::thread(); + assert(thread != NULL && thread->is_Java_thread(), "just checking"); + return (JavaThread*)thread; +} + +inline CompilerThread* JavaThread::as_CompilerThread() { + assert(is_Compiler_thread(), "just checking"); + return (CompilerThread*)this; +} + +inline bool JavaThread::stack_yellow_zone_disabled() { + return _stack_guard_state == stack_guard_yellow_disabled; +} + +inline bool JavaThread::stack_yellow_zone_enabled() { +#ifdef ASSERT + if (os::uses_stack_guard_pages()) { + assert(_stack_guard_state != stack_guard_unused, "guard pages must be in use"); + } +#endif + return _stack_guard_state == stack_guard_enabled; +} + +inline size_t JavaThread::stack_available(address cur_sp) { + // This code assumes java stacks grow down + address low_addr; // Limit on the address for deepest stack depth + if ( _stack_guard_state == stack_guard_unused) { + low_addr = stack_base() - stack_size(); + } else { + low_addr = stack_yellow_zone_base(); + } + return cur_sp > low_addr ? cur_sp - low_addr : 0; +} + +// A JavaThread for low memory detection support +class LowMemoryDetectorThread : public JavaThread { + friend class VMStructs; +public: + LowMemoryDetectorThread(ThreadFunction entry_point) : JavaThread(entry_point) {}; + + // Hide this thread from external view. + bool is_hidden_from_external_view() const { return true; } +}; + +// A thread used for Compilation. +class CompilerThread : public JavaThread { + friend class VMStructs; + private: + CompilerCounters* _counters; + + ciEnv* _env; + CompileLog* _log; + CompileTask* _task; + CompileQueue* _queue; + + public: + + static CompilerThread* current(); + + CompilerThread(CompileQueue* queue, CompilerCounters* counters); + + bool is_Compiler_thread() const { return true; } + // Hide this compiler thread from external view. + bool is_hidden_from_external_view() const { return true; } + + CompileQueue* queue() { return _queue; } + CompilerCounters* counters() { return _counters; } + + // Get/set the thread's compilation environment. + ciEnv* env() { return _env; } + void set_env(ciEnv* env) { _env = env; } + + // Get/set the thread's logging information + CompileLog* log() { return _log; } + void init_log(CompileLog* log) { + // Set once, for good. + assert(_log == NULL, "set only once"); + _log = log; + } + +#ifndef PRODUCT +private: + IdealGraphPrinter *_ideal_graph_printer; +public: + IdealGraphPrinter *ideal_graph_printer() { return _ideal_graph_printer; } + void set_ideal_graph_printer(IdealGraphPrinter *n) { _ideal_graph_printer = n; } +#endif + + // Get/set the thread's current task + CompileTask* task() { return _task; } + void set_task(CompileTask* task) { _task = task; } +}; + +inline CompilerThread* CompilerThread::current() { + return JavaThread::current()->as_CompilerThread(); +} + + +// The active thread queue. It also keeps track of the current used +// thread priorities. +class Threads: AllStatic { + friend class VMStructs; + private: + static JavaThread* _thread_list; + static int _number_of_threads; + static int _number_of_non_daemon_threads; + static int _return_code; + + public: + // Thread management + // force_daemon is a concession to JNI, where we may need to add a + // thread to the thread list before allocating its thread object + static void add(JavaThread* p, bool force_daemon = false); + static void remove(JavaThread* p); + static bool includes(JavaThread* p); + static JavaThread* first() { return _thread_list; } + static void threads_do(ThreadClosure* tc); + + // Initializes the vm and creates the vm thread + static jint create_vm(JavaVMInitArgs* args, bool* canTryAgain); + static void convert_vm_init_libraries_to_agents(); + static void create_vm_init_libraries(); + static void create_vm_init_agents(); + static void shutdown_vm_agents(); + static bool destroy_vm(); + // Supported VM versions via JNI + // Includes JNI_VERSION_1_1 + static jboolean is_supported_jni_version_including_1_1(jint version); + // Does not include JNI_VERSION_1_1 + static jboolean is_supported_jni_version(jint version); + + // Garbage collection + static void follow_other_roots(void f(oop*)); + + // Apply "f->do_oop" to all root oops in all threads. + // This version may only be called by sequential code. + static void oops_do(OopClosure* f); + // This version may be called by sequential or parallel code. + static void possibly_parallel_oops_do(OopClosure* f); + // This creates a list of GCTasks, one per thread. + static void create_thread_roots_tasks(GCTaskQueue* q); + // This creates a list of GCTasks, one per thread, for marking objects. + static void create_thread_roots_marking_tasks(GCTaskQueue* q); + + // Apply "f->do_oop" to roots in all threads that + // are part of compiled frames + static void compiled_frame_oops_do(OopClosure* f); + + static void convert_hcode_pointers(); + static void restore_hcode_pointers(); + + // Sweeper + static void nmethods_do(); + + static void gc_epilogue(); + static void gc_prologue(); + + // Verification + static void verify(); + static void print_on(outputStream* st, bool print_stacks, bool internal_format, bool print_concurrent_locks); + static void print(bool print_stacks, bool internal_format) { + // this function is only used by debug.cpp + print_on(tty, print_stacks, internal_format, false /* no concurrent lock printed */); + } + static void print_on_error(outputStream* st, Thread* current, char* buf, int buflen); + + // Get Java threads that are waiting to enter a monitor. If doLock + // is true, then Threads_lock is grabbed as needed. Otherwise, the + // VM needs to be at a safepoint. + static GrowableArray* get_pending_threads(int count, + address monitor, bool doLock); + + // Get owning Java thread from the monitor's owner field. If doLock + // is true, then Threads_lock is grabbed as needed. Otherwise, the + // VM needs to be at a safepoint. + static JavaThread *owning_thread_from_monitor_owner(address owner, + bool doLock); + + // Number of threads on the active threads list + static int number_of_threads() { return _number_of_threads; } + // Number of non-daemon threads on the active threads list + static int number_of_non_daemon_threads() { return _number_of_non_daemon_threads; } + + // Deoptimizes all frames tied to marked nmethods + static void deoptimized_wrt_marked_nmethods(); + +}; + + +// Thread iterator +class ThreadClosure: public StackObj { + public: + virtual void do_thread(Thread* thread) = 0; +}; + +class SignalHandlerMark: public StackObj { +private: + Thread* _thread; +public: + SignalHandlerMark(Thread* t) { + _thread = t; + if (_thread) _thread->enter_signal_handler(); + } + ~SignalHandlerMark() { + if (_thread) _thread->leave_signal_handler(); + _thread = NULL; + } +}; + +// ParkEvents are type-stable and immortal. +// +// Lifecycle: Once a ParkEvent is associated with a thread that ParkEvent remains +// associated with the thread for the thread's entire lifetime - the relationship is +// stable. A thread will be associated at most one ParkEvent. When the thread +// expires, the ParkEvent moves to the EventFreeList. New threads attempt to allocate from +// the EventFreeList before creating a new Event. Type-stability frees us from +// worrying about stale Event or Thread references in the objectMonitor subsystem. +// (A reference to ParkEvent is always valid, even though the event may no longer be associated +// with the desired or expected thread. A key aspect of this design is that the callers of +// park, unpark, etc must tolerate stale references and spurious wakeups). +// +// Only the "associated" thread can block (park) on the ParkEvent, although +// any other thread can unpark a reachable parkevent. Park() is allowed to +// return spuriously. In fact park-unpark a really just an optimization to +// avoid unbounded spinning and surrender the CPU to be a polite system citizen. +// A degenerate albeit "impolite" park-unpark implementation could simply return. +// See http://blogs.sun.com/dave for more details. +// +// Eventually I'd like to eliminate Events and ObjectWaiters, both of which serve as +// thread proxies, and simply make the THREAD structure type-stable and persistent. +// Currently, we unpark events associated with threads, but ideally we'd just +// unpark threads. +// +// The base-class, PlatformEvent, is platform-specific while the ParkEvent is +// platform-independent. PlatformEvent provides park(), unpark(), etc., and +// is abstract -- that is, a PlatformEvent should never be instantiated except +// as part of a ParkEvent. +// Equivalently we could have defined a platform-independent base-class that +// exported Allocate(), Release(), etc. The platform-specific class would extend +// that base-class, adding park(), unpark(), etc. +// +// A word of caution: The JVM uses 2 very similar constructs: +// 1. ParkEvent are used for Java-level "monitor" synchronization. +// 2. Parkers are used by JSR166-JUC park-unpark. +// +// We'll want to eventually merge these redundant facilities and use ParkEvent. + + +class ParkEvent : public os::PlatformEvent { + private: + ParkEvent * FreeNext ; + + // Current association + Thread * AssociatedWith ; + intptr_t RawThreadIdentity ; // LWPID etc + volatile int Incarnation ; + + // diagnostic : keep track of last thread to wake this thread. + // this is useful for construction of dependency graphs. + void * LastWaker ; + + public: + // MCS-CLH list linkage and Native Mutex/Monitor + ParkEvent * volatile ListNext ; + ParkEvent * volatile ListPrev ; + volatile intptr_t OnList ; + volatile int TState ; + volatile int Notified ; // for native monitor construct + volatile int IsWaiting ; // Enqueued on WaitSet + + + private: + static ParkEvent * volatile FreeList ; + static volatile int ListLock ; + + // It's prudent to mark the dtor as "private" + // ensuring that it's not visible outside the package. + // Unfortunately gcc warns about such usage, so + // we revert to the less desirable "protected" visibility. + // The other compilers accept private dtors. + + protected: // Ensure dtor is never invoked + ~ParkEvent() { guarantee (0, "invariant") ; } + + ParkEvent() : PlatformEvent() { + AssociatedWith = NULL ; + FreeNext = NULL ; + ListNext = NULL ; + ListPrev = NULL ; + OnList = 0 ; + TState = 0 ; + Notified = 0 ; + IsWaiting = 0 ; + } + + // We use placement-new to force ParkEvent instances to be + // aligned on 256-byte address boundaries. This ensures that the least + // significant byte of a ParkEvent address is always 0. + + void * operator new (size_t sz) ; + void operator delete (void * a) ; + + public: + static ParkEvent * Allocate (Thread * t) ; + static void Release (ParkEvent * e) ; +} ; diff --git a/hotspot/src/share/vm/runtime/threadCritical.hpp b/hotspot/src/share/vm/runtime/threadCritical.hpp new file mode 100644 index 00000000000..6f8529512e2 --- /dev/null +++ b/hotspot/src/share/vm/runtime/threadCritical.hpp @@ -0,0 +1,53 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ThreadCritical is used to protect short non-blocking critical sections. +// This class must use no vm facilities that require initialization. +// It is used very early in the vm's initialization, in allocation +// code and other areas. ThreadCritical regions are reentrant. +// +// Due to race conditions during vm exit, some of the os level +// synchronization primitives may not be deallocated at exit. It +// is a good plan to implement the platform dependant sections of +// code with resources that are recoverable during process +// cleanup by the os. Calling the initialize method before use +// is also problematic, it is best to use preinitialized primitives +// if possible. As an example: +// +// mutex_t mp = DEFAULTMUTEX; +// +// Also note that this class is declared as a StackObj to enforce +// block structured short locks. It cannot be declared a ResourceObj +// or CHeapObj, due to initialization issues. + +class ThreadCritical : public StackObj { + friend class os; + private: + static void initialize(); + static void release(); + + public: + ThreadCritical(); + ~ThreadCritical(); +}; diff --git a/hotspot/src/share/vm/runtime/threadLocalStorage.cpp b/hotspot/src/share/vm/runtime/threadLocalStorage.cpp new file mode 100644 index 00000000000..e00c7fe257b --- /dev/null +++ b/hotspot/src/share/vm/runtime/threadLocalStorage.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_threadLocalStorage.cpp.incl" + +// static member initialization +int ThreadLocalStorage::_thread_index = -1; + +Thread* ThreadLocalStorage::get_thread_slow() { + return (Thread*) os::thread_local_storage_at(ThreadLocalStorage::thread_index()); +} + +void ThreadLocalStorage::set_thread(Thread* thread) { + pd_set_thread(thread); + + // The following ensure that any optimization tricks we have tried + // did not backfire on us: + guarantee(get_thread() == thread, "must be the same thread, quickly"); + guarantee(get_thread_slow() == thread, "must be the same thread, slowly"); +} + +void ThreadLocalStorage::init() { + assert(ThreadLocalStorage::thread_index() == -1, "More than one attempt to initialize threadLocalStorage"); + pd_init(); + set_thread_index(os::allocate_thread_local_storage()); + generate_code_for_get_thread(); +} diff --git a/hotspot/src/share/vm/runtime/threadLocalStorage.hpp b/hotspot/src/share/vm/runtime/threadLocalStorage.hpp new file mode 100644 index 00000000000..e522d2791ab --- /dev/null +++ b/hotspot/src/share/vm/runtime/threadLocalStorage.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Interface for thread local storage + +// Fast variant of ThreadLocalStorage::get_thread_slow +extern "C" Thread* get_thread(); + +// Get raw thread id: e.g., %g7 on sparc, fs or gs on x86 +extern "C" uintptr_t _raw_thread_id(); + +class ThreadLocalStorage : AllStatic { + public: + static void set_thread(Thread* thread); + static Thread* get_thread_slow(); + static void invalidate_all() { pd_invalidate_all(); } + + // Machine dependent stuff + #include "incls/_threadLS_pd.hpp.incl" + + public: + // Accessor + static inline int thread_index() { return _thread_index; } + static inline void set_thread_index(int index) { _thread_index = index; } + + // Initialization + // Called explicitly from VMThread::activate_system instead of init_globals. + static void init(); + + private: + static int _thread_index; + + static void generate_code_for_get_thread(); + + // Processor dependent parts of set_thread and initialization + static void pd_set_thread(Thread* thread); + static void pd_init(); + // Invalidate any thread cacheing or optimization schemes. + static void pd_invalidate_all(); + +}; diff --git a/hotspot/src/share/vm/runtime/timer.cpp b/hotspot/src/share/vm/runtime/timer.cpp new file mode 100644 index 00000000000..42f598dd957 --- /dev/null +++ b/hotspot/src/share/vm/runtime/timer.cpp @@ -0,0 +1,209 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_timer.cpp.incl" + + +void elapsedTimer::add(elapsedTimer t) { + _counter += t._counter; +} + +void elapsedTimer::start() { + if (!_active) { + _active = true; + _start_counter = os::elapsed_counter(); + } +} + +void elapsedTimer::stop() { + if (_active) { + _counter += os::elapsed_counter() - _start_counter; + _active = false; + } +} + +double elapsedTimer::seconds() const { + double count = (double) _counter; + double freq = (double) os::elapsed_frequency(); + return count/freq; +} + +jlong elapsedTimer::milliseconds() const { + jlong ticks_per_ms = os::elapsed_frequency() / 1000; + return _counter / ticks_per_ms; +} + +jlong elapsedTimer::active_ticks() const { + if (!_active) { + return ticks(); + } + jlong counter = _counter + os::elapsed_counter() - _start_counter; + return counter; +} + +void TimeStamp::update_to(jlong ticks) { + _counter = ticks; + if (_counter == 0) _counter = 1; + assert(is_updated(), "must not look clear"); +} + +void TimeStamp::update() { + update_to(os::elapsed_counter()); +} + +double TimeStamp::seconds() const { + assert(is_updated(), "must not be clear"); + jlong new_count = os::elapsed_counter(); + double count = (double) new_count - _counter; + double freq = (double) os::elapsed_frequency(); + return count/freq; +} + +jlong TimeStamp::milliseconds() const { + assert(is_updated(), "must not be clear"); + + jlong new_count = os::elapsed_counter(); + jlong count = new_count - _counter; + jlong ticks_per_ms = os::elapsed_frequency() / 1000; + return count / ticks_per_ms; +} + +jlong TimeStamp::ticks_since_update() const { + assert(is_updated(), "must not be clear"); + return os::elapsed_counter() - _counter; +} + +TraceTime::TraceTime(const char* title, + bool doit, + bool print_cr, + outputStream* logfile) { + _active = doit; + _verbose = true; + _print_cr = print_cr; + _logfile = (logfile != NULL) ? logfile : tty; + + if (_active) { + _accum = NULL; + if (PrintGCTimeStamps) { + _logfile->stamp(); + _logfile->print(": "); + } + _logfile->print("[%s", title); + _logfile->flush(); + _t.start(); + } +} + +TraceTime::TraceTime(const char* title, + elapsedTimer* accumulator, + bool doit, + bool verbose, + outputStream* logfile) { + _active = doit; + _verbose = verbose; + _print_cr = true; + _logfile = (logfile != NULL) ? logfile : tty; + if (_active) { + if (_verbose) { + if (PrintGCTimeStamps) { + _logfile->stamp(); + _logfile->print(": "); + } + _logfile->print("[%s", title); + _logfile->flush(); + } + _accum = accumulator; + _t.start(); + } +} + +TraceTime::~TraceTime() { + if (_active) { + _t.stop(); + if (_accum!=NULL) _accum->add(_t); + if (_verbose) { + if (_print_cr) { + _logfile->print_cr(", %3.7f secs]", _t.seconds()); + } else { + _logfile->print(", %3.7f secs]", _t.seconds()); + } + _logfile->flush(); + } + } +} + +TraceCPUTime::TraceCPUTime(bool doit, + bool print_cr, + outputStream *logfile) : + _active(doit), + _print_cr(print_cr), + _starting_user_time(0.0), + _starting_system_time(0.0), + _starting_real_time(0.0), + _logfile(logfile), + _error(false) { + if (_active) { + if (logfile != NULL) { + _logfile = logfile; + } else { + _logfile = tty; + } + + _error = !os::getTimesSecs(&_starting_real_time, + &_starting_user_time, + &_starting_system_time); + } +} + +TraceCPUTime::~TraceCPUTime() { + if (_active) { + bool valid = false; + if (!_error) { + double real_secs; // walk clock time + double system_secs; // system time + double user_secs; // user time for all threads + + double real_time, user_time, system_time; + valid = os::getTimesSecs(&real_time, &user_time, &system_time); + if (valid) { + + user_secs = user_time - _starting_user_time; + system_secs = system_time - _starting_system_time; + real_secs = real_time - _starting_real_time; + + _logfile->print(" [Times: user=%3.2f sys=%3.2f, real=%3.2f secs] ", + user_secs, system_secs, real_secs); + + } else { + _logfile->print("[Invalid result in TraceCPUTime]"); + } + } else { + _logfile->print("[Error in TraceCPUTime]"); + } + if (_print_cr) { + _logfile->print_cr(""); + } + } +} diff --git a/hotspot/src/share/vm/runtime/timer.hpp b/hotspot/src/share/vm/runtime/timer.hpp new file mode 100644 index 00000000000..bddbd04f138 --- /dev/null +++ b/hotspot/src/share/vm/runtime/timer.hpp @@ -0,0 +1,121 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Timers for simple measurement. + +class elapsedTimer VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + jlong _counter; + jlong _start_counter; + bool _active; + public: + elapsedTimer() { _active = false; reset(); } + void add(elapsedTimer t); + void start(); + void stop(); + void reset() { _counter = 0; } + double seconds() const; + jlong milliseconds() const; + jlong ticks() const { return _counter; } + jlong active_ticks() const; + bool is_active() const { return _active; } +}; + +// TimeStamp is used for recording when an event took place. +class TimeStamp VALUE_OBJ_CLASS_SPEC { + private: + jlong _counter; + public: + TimeStamp() { _counter = 0; } + void clear() { _counter = 0; } + // has the timestamp been updated since being created or cleared? + bool is_updated() const { return _counter != 0; } + // update to current elapsed time + void update(); + // update to given elapsed time + void update_to(jlong ticks); + // returns seconds since updated + // (must not be in a cleared state: must have been previously updated) + double seconds() const; + jlong milliseconds() const; + // ticks elapsed between VM start and last update + jlong ticks() const { return _counter; } + // ticks elapsed since last update + jlong ticks_since_update() const; +}; + +// TraceTime is used for tracing the execution time of a block +// Usage: +// { TraceTime t("block time") +// some_code(); +// } +// + +class TraceTime: public StackObj { + private: + bool _active; // do timing + bool _verbose; // report every timing + bool _print_cr; // add a CR to the end of the timer report + elapsedTimer _t; // timer + elapsedTimer* _accum; // accumulator + outputStream* _logfile; // output log file + public: + // Constuctors + TraceTime(const char* title, + bool doit = true, + bool print_cr = true, + outputStream *logfile = NULL); + TraceTime(const char* title, + elapsedTimer* accumulator, + bool doit = true, + bool verbose = false, + outputStream *logfile = NULL ); + ~TraceTime(); + + // Accessors + void set_verbose(bool verbose) { _verbose = verbose; } + bool verbose() const { return _verbose; } + + // Activation + void suspend() { if (_active) _t.stop(); } + void resume() { if (_active) _t.start(); } +}; + +class TraceCPUTime: public StackObj { + private: + bool _active; // true if times will be measured and printed + bool _print_cr; // if true print carriage return at end + double _starting_user_time; // user time at start of measurement + double _starting_system_time; // system time at start of measurement + double _starting_real_time; // real time at start of measurement + outputStream* _logfile; // output is printed to this stream + bool _error; // true if an error occurred, turns off output + + public: + TraceCPUTime(bool doit = true, + bool print_cr = true, + outputStream *logfile = NULL); + ~TraceCPUTime(); +}; diff --git a/hotspot/src/share/vm/runtime/unhandledOops.cpp b/hotspot/src/share/vm/runtime/unhandledOops.cpp new file mode 100644 index 00000000000..fdaf43061dc --- /dev/null +++ b/hotspot/src/share/vm/runtime/unhandledOops.cpp @@ -0,0 +1,128 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_unhandledOops.cpp.incl" + +#ifdef CHECK_UNHANDLED_OOPS +const int free_list_size = 256; + + +UnhandledOops::UnhandledOops(Thread* thread) { + _thread = thread; + _oop_list = new (ResourceObj::C_HEAP) + GrowableArray(free_list_size, true); + _level = 0; +} + +UnhandledOops::~UnhandledOops() { + delete _oop_list; +} + + +void UnhandledOops::dump_oops(UnhandledOops *list) { + for (int k = 0; k < list->_oop_list->length(); k++) { + UnhandledOopEntry entry = list->_oop_list->at(k); + tty->print(" " INTPTR_FORMAT, entry._oop_ptr); + } + tty->cr(); +} + +// For debugging unhandled oop detector _in the debugger_ +// You don't want to turn it on in compiled code here. +static bool unhandled_oop_print=0; + +void UnhandledOops::register_unhandled_oop(oop* op, address pc) { + if (!_thread->is_in_stack((address)op)) + return; + + _level ++; + if (unhandled_oop_print) { + for (int i=0; i<_level; i++) tty->print(" "); + tty->print_cr("r " INTPTR_FORMAT, op); + } + UnhandledOopEntry entry(op, pc); + _oop_list->push(entry); +} + + +bool match_oop_entry(void *op, UnhandledOopEntry e) { + return (e.oop_ptr() == op); +} + +// Mark unhandled oop as okay for GC - the containing struct has an oops_do and +// for some reason the oop has to be on the stack. +// May not be called for the current thread, as in the case of +// VM_GetOrSetLocal in jvmti. +void UnhandledOops::allow_unhandled_oop(oop* op) { + assert (CheckUnhandledOops, "should only be called with checking option"); + + int i = _oop_list->find_at_end(op, match_oop_entry); + assert(i!=-1, "safe for gc oop not in unhandled_oop_list"); + + UnhandledOopEntry entry = _oop_list->at(i); + assert(!entry._ok_for_gc, "duplicate entry"); + entry._ok_for_gc = true; + _oop_list->at_put(i, entry); +} + + +// Called by the oop destructor to remove unhandled oop from the thread's +// oop list. All oops given are assumed to be on the list. If not, +// there's a bug in the unhandled oop detector. +void UnhandledOops::unregister_unhandled_oop(oop* op) { + if (!_thread->is_in_stack((address)op)) return; + + _level --; + if (unhandled_oop_print) { + for (int i=0; i<_level; i++) tty->print(" "); + tty->print_cr("u "INTPTR_FORMAT, op); + } + + int i = _oop_list->find_at_end(op, match_oop_entry); + assert(i!=-1, "oop not in unhandled_oop_list"); + _oop_list->remove_at(i); +} + +void UnhandledOops::clear_unhandled_oops() { + assert (CheckUnhandledOops, "should only be called with checking option"); + if (_thread->is_gc_locked_out()) { + return; + } + for (int k = 0; k < _oop_list->length(); k++) { + UnhandledOopEntry entry = _oop_list->at(k); + // If an entry is on the unhandled oop list but isn't on the stack + // anymore, it must not have gotten unregistered properly and it's a bug + // in the unhandled oop generator. + if(!_thread->is_in_stack((address)entry._oop_ptr)) { + tty->print_cr("oop_ptr is " INTPTR_FORMAT, (address)entry._oop_ptr); + tty->print_cr("thread is " INTPTR_FORMAT " from pc " INTPTR_FORMAT, + (address)_thread, (address)entry._pc); + assert(false, "heap is corrupted by the unhandled oop detector"); + } + // Set unhandled oops to a pattern that will crash distinctively + if (!entry._ok_for_gc) *(intptr_t*)(entry._oop_ptr) = BAD_OOP_ADDR; + } +} +#endif // CHECK_UNHANDLED_OOPS diff --git a/hotspot/src/share/vm/runtime/unhandledOops.hpp b/hotspot/src/share/vm/runtime/unhandledOops.hpp new file mode 100644 index 00000000000..c2cd95ee45c --- /dev/null +++ b/hotspot/src/share/vm/runtime/unhandledOops.hpp @@ -0,0 +1,83 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +#ifdef CHECK_UNHANDLED_OOPS + +// Detect unhanded oops in VM code + +// The design is that when an oop is declared on the stack as a local +// variable, the oop is actually a C++ struct with constructor and +// destructor. The constructor adds the oop address on a list +// off each thread and the destructor removes the oop. At a potential +// safepoint, the stack addresses of the local variable oops are trashed +// with a recognizeable value. If the local variable is used again, it +// will segfault, indicating an unsafe use of that oop. +// eg: +// oop o; //register &o on list +// funct(); // if potential safepoint - causes clear_naked_oops() +// // which trashes o above. +// o->do_something(); // Crashes because o is unsafe. +// +// This code implements the details of the unhandled oop list on the thread. +// + +class oop; +class Thread; + +class UnhandledOopEntry { + friend class UnhandledOops; + private: + oop* _oop_ptr; + bool _ok_for_gc; + address _pc; + public: + oop* oop_ptr() { return _oop_ptr; } + UnhandledOopEntry() : _oop_ptr(NULL), _ok_for_gc(false), _pc(NULL) {} + UnhandledOopEntry(oop* op, address pc) : + _oop_ptr(op), _ok_for_gc(false), _pc(pc) {} +}; + + +class UnhandledOops { + friend class Thread; + private: + Thread* _thread; + int _level; + GrowableArray *_oop_list; + void allow_unhandled_oop(oop* op); + void clear_unhandled_oops(); + UnhandledOops(Thread* thread); + ~UnhandledOops(); + + public: + static void dump_oops(UnhandledOops* list); + void register_unhandled_oop(oop* op, address pc); + void unregister_unhandled_oop(oop* op); +}; + +#ifdef _LP64 +const intptr_t BAD_OOP_ADDR = 0xfffffffffffffff1; +#else +const intptr_t BAD_OOP_ADDR = 0xfffffff1; +#endif // _LP64 +#endif // CHECK_UNHANDLED_OOPS diff --git a/hotspot/src/share/vm/runtime/vframe.cpp b/hotspot/src/share/vm/runtime/vframe.cpp new file mode 100644 index 00000000000..fdb2864df39 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vframe.cpp @@ -0,0 +1,636 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vframe.cpp.incl" + +vframe::vframe(const frame* fr, const RegisterMap* reg_map, JavaThread* thread) +: _reg_map(reg_map), _thread(thread) { + assert(fr != NULL, "must have frame"); + _fr = *fr; +} + +vframe::vframe(const frame* fr, JavaThread* thread) +: _reg_map(thread), _thread(thread) { + assert(fr != NULL, "must have frame"); + _fr = *fr; +} + +vframe* vframe::new_vframe(const frame* f, const RegisterMap* reg_map, JavaThread* thread) { + // Interpreter frame + if (f->is_interpreted_frame()) { + return new interpretedVFrame(f, reg_map, thread); + } + + // Compiled frame + CodeBlob* cb = f->cb(); + if (cb != NULL) { + if (cb->is_nmethod()) { + nmethod* nm = (nmethod*)cb; + return new compiledVFrame(f, reg_map, thread, nm); + } + + if (f->is_runtime_frame()) { + // Skip this frame and try again. + RegisterMap temp_map = *reg_map; + frame s = f->sender(&temp_map); + return new_vframe(&s, &temp_map, thread); + } + } + + // External frame + return new externalVFrame(f, reg_map, thread); +} + +vframe* vframe::sender() const { + RegisterMap temp_map = *register_map(); + assert(is_top(), "just checking"); + if (_fr.is_entry_frame() && _fr.is_first_frame()) return NULL; + frame s = _fr.real_sender(&temp_map); + if (s.is_first_frame()) return NULL; + return vframe::new_vframe(&s, &temp_map, thread()); +} + +vframe* vframe::top() const { + vframe* vf = (vframe*) this; + while (!vf->is_top()) vf = vf->sender(); + return vf; +} + + +javaVFrame* vframe::java_sender() const { + vframe* f = sender(); + while (f != NULL) { + if (f->is_java_frame()) return javaVFrame::cast(f); + f = f->sender(); + } + return NULL; +} + +// ------------- javaVFrame -------------- + +GrowableArray* javaVFrame::locked_monitors() { + assert(SafepointSynchronize::is_at_safepoint() || JavaThread::current() == thread(), + "must be at safepoint or it's a java frame of the current thread"); + + GrowableArray* mons = monitors(); + GrowableArray* result = new GrowableArray(mons->length()); + if (mons->is_empty()) return result; + + bool found_first_monitor = false; + ObjectMonitor *pending_monitor = thread()->current_pending_monitor(); + ObjectMonitor *waiting_monitor = thread()->current_waiting_monitor(); + oop pending_obj = (pending_monitor != NULL ? (oop) pending_monitor->object() : NULL); + oop waiting_obj = (waiting_monitor != NULL ? (oop) waiting_monitor->object() : NULL); + + for (int index = (mons->length()-1); index >= 0; index--) { + MonitorInfo* monitor = mons->at(index); + oop obj = monitor->owner(); + if (obj == NULL) continue; // skip unowned monitor + // + // Skip the monitor that the thread is blocked to enter or waiting on + // + if (!found_first_monitor && (obj == pending_obj || obj == waiting_obj)) { + continue; + } + found_first_monitor = true; + result->append(monitor); + } + return result; +} + +static void print_locked_object_class_name(outputStream* st, Handle obj, const char* lock_state) { + if (obj.not_null()) { + st->print("\t- %s <" INTPTR_FORMAT "> ", lock_state, (address)obj()); + if (obj->klass() == SystemDictionary::class_klass()) { + klassOop target_klass = java_lang_Class::as_klassOop(obj()); + st->print_cr("(a java.lang.Class for %s)", instanceKlass::cast(target_klass)->external_name()); + } else { + Klass* k = Klass::cast(obj->klass()); + st->print_cr("(a %s)", k->external_name()); + } + } +} + +void javaVFrame::print_lock_info_on(outputStream* st, int frame_count) { + ResourceMark rm; + + // If this is the first frame, and java.lang.Object.wait(...) then print out the receiver. + if (frame_count == 0) { + if (method()->name() == vmSymbols::wait_name() && + instanceKlass::cast(method()->method_holder())->name() == vmSymbols::java_lang_Object()) { + StackValueCollection* locs = locals(); + if (!locs->is_empty()) { + StackValue* sv = locs->at(0); + if (sv->type() == T_OBJECT) { + Handle o = locs->at(0)->get_obj(); + print_locked_object_class_name(st, o, "waiting on"); + } + } + } else if (thread()->current_park_blocker() != NULL) { + oop obj = thread()->current_park_blocker(); + Klass* k = Klass::cast(obj->klass()); + st->print_cr("\t- %s <" INTPTR_FORMAT "> (a %s)", "parking to wait for ", (address)obj, k->external_name()); + } + } + + + // Print out all monitors that we have locked or are trying to lock + GrowableArray* mons = monitors(); + if (!mons->is_empty()) { + bool found_first_monitor = false; + for (int index = (mons->length()-1); index >= 0; index--) { + MonitorInfo* monitor = mons->at(index); + if (monitor->owner() != NULL) { + + // First, assume we have the monitor locked. If we haven't found an + // owned monitor before and this is the first frame, then we need to + // see if we have completed the lock or we are blocked trying to + // acquire it - we can only be blocked if the monitor is inflated + + const char *lock_state = "locked"; // assume we have the monitor locked + if (!found_first_monitor && frame_count == 0) { + markOop mark = monitor->owner()->mark(); + if (mark->has_monitor() && + mark->monitor() == thread()->current_pending_monitor()) { + lock_state = "waiting to lock"; + } + } + + found_first_monitor = true; + print_locked_object_class_name(st, monitor->owner(), lock_state); + } + } + } +} + +// ------------- interpretedVFrame -------------- + +u_char* interpretedVFrame::bcp() const { + return fr().interpreter_frame_bcp(); +} + +void interpretedVFrame::set_bcp(u_char* bcp) { + fr().interpreter_frame_set_bcp(bcp); +} + +intptr_t* interpretedVFrame::locals_addr_at(int offset) const { + assert(fr().is_interpreted_frame(), "frame should be an interpreted frame"); + return fr().interpreter_frame_local_at(offset); +} + + +GrowableArray* interpretedVFrame::monitors() const { + GrowableArray* result = new GrowableArray(5); + for (BasicObjectLock* current = (fr().previous_monitor_in_interpreter_frame(fr().interpreter_frame_monitor_begin())); + current >= fr().interpreter_frame_monitor_end(); + current = fr().previous_monitor_in_interpreter_frame(current)) { + result->push(new MonitorInfo(current->obj(), current->lock())); + } + return result; +} + +int interpretedVFrame::bci() const { + return method()->bci_from(bcp()); +} + +methodOop interpretedVFrame::method() const { + return fr().interpreter_frame_method(); +} + +StackValueCollection* interpretedVFrame::locals() const { + int length = method()->max_locals(); + + if (method()->is_native()) { + // If the method is native, max_locals is not telling the truth. + // maxlocals then equals the size of parameters + length = method()->size_of_parameters(); + } + + StackValueCollection* result = new StackValueCollection(length); + + // Get oopmap describing oops and int for current bci + if (TaggedStackInterpreter) { + for(int i=0; i < length; i++) { + // Find stack location + intptr_t *addr = locals_addr_at(i); + + // Depending on oop/int put it in the right package + StackValue *sv; + frame::Tag tag = fr().interpreter_frame_local_tag(i); + if (tag == frame::TagReference) { + // oop value + Handle h(*(oop *)addr); + sv = new StackValue(h); + } else { + // integer + sv = new StackValue(*addr); + } + assert(sv != NULL, "sanity check"); + result->add(sv); + } + } else { + InterpreterOopMap oop_mask; + if (TraceDeoptimization && Verbose) { + methodHandle m_h(thread(), method()); + OopMapCache::compute_one_oop_map(m_h, bci(), &oop_mask); + } else { + method()->mask_for(bci(), &oop_mask); + } + // handle locals + for(int i=0; i < length; i++) { + // Find stack location + intptr_t *addr = locals_addr_at(i); + + // Depending on oop/int put it in the right package + StackValue *sv; + if (oop_mask.is_oop(i)) { + // oop value + Handle h(*(oop *)addr); + sv = new StackValue(h); + } else { + // integer + sv = new StackValue(*addr); + } + assert(sv != NULL, "sanity check"); + result->add(sv); + } + } + return result; +} + +void interpretedVFrame::set_locals(StackValueCollection* values) const { + if (values == NULL || values->size() == 0) return; + + int length = method()->max_locals(); + if (method()->is_native()) { + // If the method is native, max_locals is not telling the truth. + // maxlocals then equals the size of parameters + length = method()->size_of_parameters(); + } + + assert(length == values->size(), "Mismatch between actual stack format and supplied data"); + + // handle locals + for (int i = 0; i < length; i++) { + // Find stack location + intptr_t *addr = locals_addr_at(i); + + // Depending on oop/int put it in the right package + StackValue *sv = values->at(i); + assert(sv != NULL, "sanity check"); + if (sv->type() == T_OBJECT) { + *(oop *) addr = (sv->get_obj())(); + } else { // integer + *addr = sv->get_int(); + } + } +} + +StackValueCollection* interpretedVFrame::expressions() const { + int length = fr().interpreter_frame_expression_stack_size(); + if (method()->is_native()) { + // If the method is native, there is no expression stack + length = 0; + } + + int nof_locals = method()->max_locals(); + StackValueCollection* result = new StackValueCollection(length); + + if (TaggedStackInterpreter) { + // handle expressions + for(int i=0; i < length; i++) { + // Find stack location + intptr_t *addr = fr().interpreter_frame_expression_stack_at(i); + frame::Tag tag = fr().interpreter_frame_expression_stack_tag(i); + + // Depending on oop/int put it in the right package + StackValue *sv; + if (tag == frame::TagReference) { + // oop value + Handle h(*(oop *)addr); + sv = new StackValue(h); + } else { + // otherwise + sv = new StackValue(*addr); + } + assert(sv != NULL, "sanity check"); + result->add(sv); + } + } else { + InterpreterOopMap oop_mask; + // Get oopmap describing oops and int for current bci + if (TraceDeoptimization && Verbose) { + methodHandle m_h(method()); + OopMapCache::compute_one_oop_map(m_h, bci(), &oop_mask); + } else { + method()->mask_for(bci(), &oop_mask); + } + // handle expressions + for(int i=0; i < length; i++) { + // Find stack location + intptr_t *addr = fr().interpreter_frame_expression_stack_at(i); + + // Depending on oop/int put it in the right package + StackValue *sv; + if (oop_mask.is_oop(i + nof_locals)) { + // oop value + Handle h(*(oop *)addr); + sv = new StackValue(h); + } else { + // integer + sv = new StackValue(*addr); + } + assert(sv != NULL, "sanity check"); + result->add(sv); + } + } + return result; +} + + +// ------------- cChunk -------------- + +entryVFrame::entryVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread) +: externalVFrame(fr, reg_map, thread) {} + + +void vframeStreamCommon::found_bad_method_frame() { + // 6379830 Cut point for an assertion that occasionally fires when + // we are using the performance analyzer. + // Disable this assert when testing the analyzer with fastdebug. + // -XX:SuppressErrorAt=vframe.cpp:XXX (XXX=following line number) + assert(false, "invalid bci or invalid scope desc"); +} + +// top-frame will be skipped +vframeStream::vframeStream(JavaThread* thread, frame top_frame, + bool stop_at_java_call_stub) : vframeStreamCommon(thread) { + _stop_at_java_call_stub = stop_at_java_call_stub; + + // skip top frame, as it may not be at safepoint + _frame = top_frame.sender(&_reg_map); + while (!fill_from_frame()) { + _frame = _frame.sender(&_reg_map); + } +} + + +// Step back n frames, skip any pseudo frames in between. +// This function is used in Class.forName, Class.newInstance, Method.Invoke, +// AccessController.doPrivileged. +// +// NOTE that in JDK 1.4 this has been exposed to Java as +// sun.reflect.Reflection.getCallerClass(), which can be inlined. +// Inlined versions must match this routine's logic. +// Native method prefixing logic does not need to match since +// the method names don't match and inlining will not occur. +// See, for example, +// Parse::inline_native_Reflection_getCallerClass in +// opto/library_call.cpp. +void vframeStreamCommon::security_get_caller_frame(int depth) { + bool use_new_reflection = JDK_Version::is_gte_jdk14x_version() && UseNewReflection; + + while (!at_end()) { + if (Universe::reflect_invoke_cache()->is_same_method(method())) { + // This is Method.invoke() -- skip it + } else if (use_new_reflection && + Klass::cast(method()->method_holder()) + ->is_subclass_of(SystemDictionary::reflect_method_accessor_klass())) { + // This is an auxilary frame -- skip it + } else { + // This is non-excluded frame, we need to count it against the depth + if (depth-- <= 0) { + // we have reached the desired depth, we are done + break; + } + } + if (method()->is_prefixed_native()) { + skip_prefixed_method_and_wrappers(); + } else { + next(); + } + } +} + + +void vframeStreamCommon::skip_prefixed_method_and_wrappers() { + ResourceMark rm; + HandleMark hm; + + int method_prefix_count = 0; + char** method_prefixes = JvmtiExport::get_all_native_method_prefixes(&method_prefix_count); + KlassHandle prefixed_klass(method()->method_holder()); + const char* prefixed_name = method()->name()->as_C_string(); + size_t prefixed_name_len = strlen(prefixed_name); + int prefix_index = method_prefix_count-1; + + while (!at_end()) { + next(); + if (method()->method_holder() != prefixed_klass()) { + break; // classes don't match, can't be a wrapper + } + const char* name = method()->name()->as_C_string(); + size_t name_len = strlen(name); + size_t prefix_len = prefixed_name_len - name_len; + if (prefix_len <= 0 || strcmp(name, prefixed_name + prefix_len) != 0) { + break; // prefixed name isn't prefixed version of method name, can't be a wrapper + } + for (; prefix_index >= 0; --prefix_index) { + const char* possible_prefix = method_prefixes[prefix_index]; + size_t possible_prefix_len = strlen(possible_prefix); + if (possible_prefix_len == prefix_len && + strncmp(possible_prefix, prefixed_name, prefix_len) == 0) { + break; // matching prefix found + } + } + if (prefix_index < 0) { + break; // didn't find the prefix, can't be a wrapper + } + prefixed_name = name; + prefixed_name_len = name_len; + } +} + + +void vframeStreamCommon::skip_reflection_related_frames() { + while (!at_end() && + (JDK_Version::is_gte_jdk14x_version() && UseNewReflection && + (Klass::cast(method()->method_holder())->is_subclass_of(SystemDictionary::reflect_method_accessor_klass()) || + Klass::cast(method()->method_holder())->is_subclass_of(SystemDictionary::reflect_constructor_accessor_klass())))) { + next(); + } +} + + +#ifndef PRODUCT +void vframe::print() { + if (WizardMode) _fr.print_value_on(tty,NULL); +} + + +void vframe::print_value() const { + ((vframe*)this)->print(); +} + + +void entryVFrame::print_value() const { + ((entryVFrame*)this)->print(); +} + +void entryVFrame::print() { + vframe::print(); + tty->print_cr("C Chunk inbetween Java"); + tty->print_cr("C link " INTPTR_FORMAT, _fr.link()); +} + + +// ------------- javaVFrame -------------- + +static void print_stack_values(const char* title, StackValueCollection* values) { + if (values->is_empty()) return; + tty->print_cr("\t%s:", title); + values->print(); +} + + +void javaVFrame::print() { + ResourceMark rm; + vframe::print(); + tty->print("\t"); + method()->print_value(); + tty->cr(); + tty->print_cr("\tbci: %d", bci()); + + print_stack_values("locals", locals()); + print_stack_values("expressions", expressions()); + + GrowableArray* list = monitors(); + if (list->is_empty()) return; + tty->print_cr("\tmonitor list:"); + for (int index = (list->length()-1); index >= 0; index--) { + MonitorInfo* monitor = list->at(index); + tty->print("\t obj\t"); monitor->owner()->print_value(); + tty->print("(" INTPTR_FORMAT ")", (address)monitor->owner()); + tty->cr(); + tty->print("\t "); + monitor->lock()->print_on(tty); + tty->cr(); + } +} + + +void javaVFrame::print_value() const { + methodOop m = method(); + klassOop k = m->method_holder(); + tty->print_cr("frame( sp=" INTPTR_FORMAT ", unextended_sp=" INTPTR_FORMAT ", fp=" INTPTR_FORMAT ", pc=" INTPTR_FORMAT ")", + _fr.sp(), _fr.unextended_sp(), _fr.fp(), _fr.pc()); + tty->print("%s.%s", Klass::cast(k)->internal_name(), m->name()->as_C_string()); + + if (!m->is_native()) { + symbolOop source_name = instanceKlass::cast(k)->source_file_name(); + int line_number = m->line_number_from_bci(bci()); + if (source_name != NULL && (line_number != -1)) { + tty->print("(%s:%d)", source_name->as_C_string(), line_number); + } + } else { + tty->print("(Native Method)"); + } + // Check frame size and print warning if it looks suspiciously large + if (fr().sp() != NULL) { + uint size = fr().frame_size(); +#ifdef _LP64 + if (size > 8*K) warning("SUSPICIOUSLY LARGE FRAME (%d)", size); +#else + if (size > 4*K) warning("SUSPICIOUSLY LARGE FRAME (%d)", size); +#endif + } +} + + +bool javaVFrame::structural_compare(javaVFrame* other) { + // Check static part + if (method() != other->method()) return false; + if (bci() != other->bci()) return false; + + // Check locals + StackValueCollection *locs = locals(); + StackValueCollection *other_locs = other->locals(); + assert(locs->size() == other_locs->size(), "sanity check"); + int i; + for(i = 0; i < locs->size(); i++) { + // it might happen the compiler reports a conflict and + // the interpreter reports a bogus int. + if ( is_compiled_frame() && locs->at(i)->type() == T_CONFLICT) continue; + if (other->is_compiled_frame() && other_locs->at(i)->type() == T_CONFLICT) continue; + + if (!locs->at(i)->equal(other_locs->at(i))) + return false; + } + + // Check expressions + StackValueCollection* exprs = expressions(); + StackValueCollection* other_exprs = other->expressions(); + assert(exprs->size() == other_exprs->size(), "sanity check"); + for(i = 0; i < exprs->size(); i++) { + if (!exprs->at(i)->equal(other_exprs->at(i))) + return false; + } + + return true; +} + + +void javaVFrame::print_activation(int index) const { + // frame number and method + tty->print("%2d - ", index); + ((vframe*)this)->print_value(); + tty->cr(); + + if (WizardMode) { + ((vframe*)this)->print(); + tty->cr(); + } +} + + +void javaVFrame::verify() const { +} + + +void interpretedVFrame::verify() const { +} + + +// ------------- externalVFrame -------------- + +void externalVFrame::print() { + _fr.print_value_on(tty,NULL); +} + + +void externalVFrame::print_value() const { + ((vframe*)this)->print(); +} +#endif // PRODUCT diff --git a/hotspot/src/share/vm/runtime/vframe.hpp b/hotspot/src/share/vm/runtime/vframe.hpp new file mode 100644 index 00000000000..b62a6f76ffb --- /dev/null +++ b/hotspot/src/share/vm/runtime/vframe.hpp @@ -0,0 +1,447 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// vframes are virtual stack frames representing source level activations. +// A single frame may hold several source level activations in the case of +// optimized code. The debugging stored with the optimized code enables +// us to unfold a frame as a stack of vframes. +// A cVFrame represents an activation of a non-java method. + +// The vframe inheritance hierarchy: +// - vframe +// - javaVFrame +// - interpretedVFrame +// - compiledVFrame ; (used for both compiled Java methods and native stubs) +// - externalVFrame +// - entryVFrame ; special frame created when calling Java from C + +// - BasicLock + +class vframe: public ResourceObj { + protected: + frame _fr; // Raw frame behind the virtual frame. + RegisterMap _reg_map; // Register map for the raw frame (used to handle callee-saved registers). + JavaThread* _thread; // The thread owning the raw frame. + + vframe(const frame* fr, const RegisterMap* reg_map, JavaThread* thread); + vframe(const frame* fr, JavaThread* thread); + public: + // Factory method for creating vframes + static vframe* new_vframe(const frame* f, const RegisterMap *reg_map, JavaThread* thread); + + // Accessors + frame fr() const { return _fr; } + CodeBlob* cb() const { return _fr.cb(); } + nmethod* nm() const { + assert( cb() != NULL && cb()->is_nmethod(), "usage"); + return (nmethod*) cb(); + } + +// ???? Does this need to be a copy? + frame* frame_pointer() { return &_fr; } + const RegisterMap* register_map() const { return &_reg_map; } + JavaThread* thread() const { return _thread; } + + // Returns the sender vframe + virtual vframe* sender() const; + + // Returns the next javaVFrame on the stack (skipping all other kinds of frame) + javaVFrame *java_sender() const; + + // Answers if the this is the top vframe in the frame, i.e., if the sender vframe + // is in the caller frame + virtual bool is_top() const { return true; } + + // Returns top vframe within same frame (see is_top()) + virtual vframe* top() const; + + // Type testing operations + virtual bool is_entry_frame() const { return false; } + virtual bool is_java_frame() const { return false; } + virtual bool is_interpreted_frame() const { return false; } + virtual bool is_compiled_frame() const { return false; } + +#ifndef PRODUCT + // printing operations + virtual void print_value() const; + virtual void print(); +#endif +}; + + +class javaVFrame: public vframe { + public: + // JVM state + virtual methodOop method() const = 0; + virtual int bci() const = 0; + virtual StackValueCollection* locals() const = 0; + virtual StackValueCollection* expressions() const = 0; + // the order returned by monitors() is from oldest -> youngest#4418568 + virtual GrowableArray* monitors() const = 0; + + // Debugging support via JVMTI. + // NOTE that this is not guaranteed to give correct results for compiled vframes. + // Deoptimize first if necessary. + virtual void set_locals(StackValueCollection* values) const = 0; + + // Test operation + bool is_java_frame() const { return true; } + + protected: + javaVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread) : vframe(fr, reg_map, thread) {} + javaVFrame(const frame* fr, JavaThread* thread) : vframe(fr, thread) {} + + public: + // casting + static javaVFrame* cast(vframe* vf) { + assert(vf == NULL || vf->is_java_frame(), "must be java frame"); + return (javaVFrame*) vf; + } + + // Return an array of monitors locked by this frame in the youngest to oldest order + GrowableArray* locked_monitors(); + + // printing used during stack dumps + void print_lock_info_on(outputStream* st, int frame_count); + void print_lock_info(int frame_count) { print_lock_info_on(tty, frame_count); } + +#ifndef PRODUCT + public: + // printing operations + void print(); + void print_value() const; + void print_activation(int index) const; + + // verify operations + virtual void verify() const; + + // Structural compare + bool structural_compare(javaVFrame* other); +#endif + friend class vframe; +}; + +class interpretedVFrame: public javaVFrame { + public: + // JVM state + methodOop method() const; + int bci() const; + StackValueCollection* locals() const; + StackValueCollection* expressions() const; + GrowableArray* monitors() const; + + void set_locals(StackValueCollection* values) const; + + // Test operation + bool is_interpreted_frame() const { return true; } + + protected: + interpretedVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread) : javaVFrame(fr, reg_map, thread) {}; + + public: + // Accessors for Byte Code Pointer + u_char* bcp() const; + void set_bcp(u_char* bcp); + + // casting + static interpretedVFrame* cast(vframe* vf) { + assert(vf == NULL || vf->is_interpreted_frame(), "must be interpreted frame"); + return (interpretedVFrame*) vf; + } + + private: + static const int bcp_offset; + intptr_t* locals_addr_at(int offset) const; + + // returns where the parameters starts relative to the frame pointer + int start_of_parameters() const; + +#ifndef PRODUCT + public: + // verify operations + void verify() const; +#endif + friend class vframe; +}; + + +class externalVFrame: public vframe { + protected: + externalVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread) : vframe(fr, reg_map, thread) {} + +#ifndef PRODUCT + public: + // printing operations + void print_value() const; + void print(); +#endif + friend class vframe; +}; + +class entryVFrame: public externalVFrame { + public: + bool is_entry_frame() const { return true; } + + protected: + entryVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread); + + public: + // casting + static entryVFrame* cast(vframe* vf) { + assert(vf == NULL || vf->is_entry_frame(), "must be entry frame"); + return (entryVFrame*) vf; + } + +#ifndef PRODUCT + public: + // printing + void print_value() const; + void print(); +#endif + friend class vframe; +}; + + +// A MonitorInfo is a ResourceObject that describes a the pair: +// 1) the owner of the monitor +// 2) the monitor lock +class MonitorInfo : public ResourceObj { + private: + oop _owner; // the object owning the monitor + BasicLock* _lock; + public: + // Constructor + MonitorInfo(oop owner, BasicLock* lock) { + _owner = owner; + _lock = lock; + } + // Accessors + oop owner() const { return _owner; } + BasicLock* lock() const { return _lock; } +}; + +class vframeStreamCommon : StackObj { + protected: + // common + frame _frame; + JavaThread* _thread; + RegisterMap _reg_map; + enum { interpreted_mode, compiled_mode, at_end_mode } _mode; + + int _sender_decode_offset; + + // Cached information + methodOop _method; + int _bci; + + // Should VM activations be ignored or not + bool _stop_at_java_call_stub; + + bool fill_in_compiled_inlined_sender(); + void fill_from_compiled_frame(int decode_offset); + void fill_from_compiled_native_frame(); + + void found_bad_method_frame(); + + void fill_from_interpreter_frame(); + bool fill_from_frame(); + + // Helper routine for security_get_caller_frame + void skip_prefixed_method_and_wrappers(); + + public: + // Constructor + vframeStreamCommon(JavaThread* thread) : _reg_map(thread, false) { + _thread = thread; + } + + // Accessors + methodOop method() const { return _method; } + int bci() const { return _bci; } + intptr_t* frame_id() const { return _frame.id(); } + address frame_pc() const { return _frame.pc(); } + + CodeBlob* cb() const { return _frame.cb(); } + nmethod* nm() const { + assert( cb() != NULL && cb()->is_nmethod(), "usage"); + return (nmethod*) cb(); + } + + // Frame type + bool is_interpreted_frame() const { return _frame.is_interpreted_frame(); } + bool is_entry_frame() const { return _frame.is_entry_frame(); } + + // Iteration + void next() { + // handle frames with inlining + if (_mode == compiled_mode && fill_in_compiled_inlined_sender()) return; + + // handle general case + do { + _frame = _frame.sender(&_reg_map); + } while (!fill_from_frame()); + } + + bool at_end() const { return _mode == at_end_mode; } + + // Implements security traversal. Skips depth no. of frame including + // special security frames and prefixed native methods + void security_get_caller_frame(int depth); + + // Helper routine for JVM_LatestUserDefinedLoader -- needed for 1.4 + // reflection implementation + void skip_reflection_related_frames(); +}; + +class vframeStream : public vframeStreamCommon { + public: + // Constructors + vframeStream(JavaThread* thread, bool stop_at_java_call_stub = false) + : vframeStreamCommon(thread) { + _stop_at_java_call_stub = stop_at_java_call_stub; + + if (!thread->has_last_Java_frame()) { + _mode = at_end_mode; + return; + } + + _frame = _thread->last_frame(); + while (!fill_from_frame()) { + _frame = _frame.sender(&_reg_map); + } + } + + // top_frame may not be at safepoint, start with sender + vframeStream(JavaThread* thread, frame top_frame, bool stop_at_java_call_stub = false); +}; + + +inline bool vframeStreamCommon::fill_in_compiled_inlined_sender() { + if (_sender_decode_offset == DebugInformationRecorder::serialized_null) { + return false; + } + fill_from_compiled_frame(_sender_decode_offset); + return true; +} + + +inline void vframeStreamCommon::fill_from_compiled_frame(int decode_offset) { + _mode = compiled_mode; + + // Range check to detect ridiculous offsets. + if (decode_offset == DebugInformationRecorder::serialized_null || + decode_offset < 0 || + decode_offset >= nm()->scopes_data_size()) { + // 6379830 AsyncGetCallTrace sometimes feeds us wild frames. + // If we attempt to read nmethod::scopes_data at serialized_null (== 0), + // or if we read some at other crazy offset, + // we will decode garbage and make wild references into the heap, + // leading to crashes in product mode. + // (This isn't airtight, of course, since there are internal + // offsets which are also crazy.) +#ifdef ASSERT + if (WizardMode) { + tty->print_cr("Error in fill_from_frame: pc_desc for " + INTPTR_FORMAT " not found or invalid at %d", + _frame.pc(), decode_offset); + nm()->print(); + nm()->method()->print_codes(); + nm()->print_code(); + nm()->print_pcs(); + } +#endif + // Provide a cheap fallback in product mode. (See comment above.) + found_bad_method_frame(); + fill_from_compiled_native_frame(); + return; + } + + // Decode first part of scopeDesc + DebugInfoReadStream buffer(nm(), decode_offset); + _sender_decode_offset = buffer.read_int(); + _method = methodOop(buffer.read_oop()); + _bci = buffer.read_bci(); + + assert(_method->is_method(), "checking type of decoded method"); +} + +// The native frames are handled specially. We do not rely on ScopeDesc info +// since the pc might not be exact due to the _last_native_pc trick. +inline void vframeStreamCommon::fill_from_compiled_native_frame() { + _mode = compiled_mode; + _sender_decode_offset = DebugInformationRecorder::serialized_null; + _method = nm()->method(); + _bci = 0; +} + +inline bool vframeStreamCommon::fill_from_frame() { + // Interpreted frame + if (_frame.is_interpreted_frame()) { + fill_from_interpreter_frame(); + return true; + } + + // Compiled frame + + if (cb() != NULL && cb()->is_nmethod()) { + if (nm()->is_native_method()) { + // Do not rely on scopeDesc since the pc might be unprecise due to the _last_native_pc trick. + fill_from_compiled_native_frame(); + } else { + PcDesc* pc_desc = nm()->pc_desc_at(_frame.pc()); + int decode_offset; + if (pc_desc == NULL) { + // Should not happen, but let fill_from_compiled_frame handle it. + decode_offset = DebugInformationRecorder::serialized_null; + } else { + decode_offset = pc_desc->scope_decode_offset(); + } + fill_from_compiled_frame(decode_offset); + } + return true; + } + + // End of stack? + if (_frame.is_first_frame() || (_stop_at_java_call_stub && _frame.is_entry_frame())) { + _mode = at_end_mode; + return true; + } + + return false; +} + + +inline void vframeStreamCommon::fill_from_interpreter_frame() { + methodOop method = _frame.interpreter_frame_method(); + intptr_t bcx = _frame.interpreter_frame_bcx(); + int bci = method->validate_bci_from_bcx(bcx); + // 6379830 AsyncGetCallTrace sometimes feeds us wild frames. + if (bci < 0) { + found_bad_method_frame(); + bci = 0; // pretend it's on the point of entering + } + _mode = interpreted_mode; + _method = method; + _bci = bci; +} diff --git a/hotspot/src/share/vm/runtime/vframeArray.cpp b/hotspot/src/share/vm/runtime/vframeArray.cpp new file mode 100644 index 00000000000..84130636685 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vframeArray.cpp @@ -0,0 +1,585 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vframeArray.cpp.incl" + + +int vframeArrayElement:: bci(void) const { return (_bci == SynchronizationEntryBCI ? 0 : _bci); } + +void vframeArrayElement::free_monitors(JavaThread* jt) { + if (_monitors != NULL) { + MonitorChunk* chunk = _monitors; + _monitors = NULL; + jt->remove_monitor_chunk(chunk); + delete chunk; + } +} + +void vframeArrayElement::fill_in(compiledVFrame* vf) { + +// Copy the information from the compiled vframe to the +// interpreter frame we will be creating to replace vf + + _method = vf->method(); + _bci = vf->raw_bci(); + + int index; + + // Get the monitors off-stack + + GrowableArray* list = vf->monitors(); + if (list->is_empty()) { + _monitors = NULL; + } else { + + // Allocate monitor chunk + _monitors = new MonitorChunk(list->length()); + vf->thread()->add_monitor_chunk(_monitors); + + // Migrate the BasicLocks from the stack to the monitor chunk + for (index = 0; index < list->length(); index++) { + MonitorInfo* monitor = list->at(index); + assert(monitor->owner() == NULL || (!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern()), "object must be null or locked, and unbiased"); + BasicObjectLock* dest = _monitors->at(index); + dest->set_obj(monitor->owner()); + monitor->lock()->move_to(monitor->owner(), dest->lock()); + } + } + + // Convert the vframe locals and expressions to off stack + // values. Because we will not gc all oops can be converted to + // intptr_t (i.e. a stack slot) and we are fine. This is + // good since we are inside a HandleMark and the oops in our + // collection would go away between packing them here and + // unpacking them in unpack_on_stack. + + // First the locals go off-stack + + // FIXME this seems silly it creates a StackValueCollection + // in order to get the size to then copy them and + // convert the types to intptr_t size slots. Seems like it + // could do it in place... Still uses less memory than the + // old way though + + StackValueCollection *locs = vf->locals(); + _locals = new StackValueCollection(locs->size()); + for(index = 0; index < locs->size(); index++) { + StackValue* value = locs->at(index); + switch(value->type()) { + case T_OBJECT: + // preserve object type + _locals->add( new StackValue((intptr_t) (value->get_obj()()), T_OBJECT )); + break; + case T_CONFLICT: + // A dead local. Will be initialized to null/zero. + _locals->add( new StackValue()); + break; + case T_INT: + _locals->add( new StackValue(value->get_int())); + break; + default: + ShouldNotReachHere(); + } + } + + // Now the expressions off-stack + // Same silliness as above + + StackValueCollection *exprs = vf->expressions(); + _expressions = new StackValueCollection(exprs->size()); + for(index = 0; index < exprs->size(); index++) { + StackValue* value = exprs->at(index); + switch(value->type()) { + case T_OBJECT: + // preserve object type + _expressions->add( new StackValue((intptr_t) (value->get_obj()()), T_OBJECT )); + break; + case T_CONFLICT: + // A dead stack element. Will be initialized to null/zero. + // This can occur when the compiler emits a state in which stack + // elements are known to be dead (because of an imminent exception). + _expressions->add( new StackValue()); + break; + case T_INT: + _expressions->add( new StackValue(value->get_int())); + break; + default: + ShouldNotReachHere(); + } + } +} + +int unpack_counter = 0; + +void vframeArrayElement::unpack_on_stack(int callee_parameters, + int callee_locals, + frame* caller, + bool is_top_frame, + int exec_mode) { + JavaThread* thread = (JavaThread*) Thread::current(); + + // Look at bci and decide on bcp and continuation pc + address bcp; + // C++ interpreter doesn't need a pc since it will figure out what to do when it + // begins execution + address pc; + bool use_next_mdp; // true if we should use the mdp associated with the next bci + // rather than the one associated with bcp + if (raw_bci() == SynchronizationEntryBCI) { + // We are deoptimizing while hanging in prologue code for synchronized method + bcp = method()->bcp_from(0); // first byte code + pc = Interpreter::deopt_entry(vtos, 0); // step = 0 since we don't skip current bytecode + use_next_mdp = false; + } else { + bcp = method()->bcp_from(bci()); + pc = Interpreter::continuation_for(method(), bcp, callee_parameters, is_top_frame, use_next_mdp); + } + assert(Bytecodes::is_defined(*bcp), "must be a valid bytecode"); + + // Monitorenter and pending exceptions: + // + // For Compiler2, there should be no pending exception when deoptimizing at monitorenter + // because there is no safepoint at the null pointer check (it is either handled explicitly + // or prior to the monitorenter) and asynchronous exceptions are not made "pending" by the + // runtime interface for the slow case (see JRT_ENTRY_FOR_MONITORENTER). If an asynchronous + // exception was processed, the bytecode pointer would have to be extended one bytecode beyond + // the monitorenter to place it in the proper exception range. + // + // For Compiler1, deoptimization can occur while throwing a NullPointerException at monitorenter, + // in which case bcp should point to the monitorenter since it is within the exception's range. + + assert(*bcp != Bytecodes::_monitorenter || is_top_frame, "a _monitorenter must be a top frame"); + // TIERED Must know the compiler of the deoptee QQQ + COMPILER2_PRESENT(guarantee(*bcp != Bytecodes::_monitorenter || exec_mode != Deoptimization::Unpack_exception, + "shouldn't get exception during monitorenter");) + + int popframe_preserved_args_size_in_bytes = 0; + int popframe_preserved_args_size_in_words = 0; + if (is_top_frame) { + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (JvmtiExport::can_pop_frame() && + (thread->has_pending_popframe() || thread->popframe_forcing_deopt_reexecution())) { + if (thread->has_pending_popframe()) { + // Pop top frame after deoptimization +#ifndef CC_INTERP + pc = Interpreter::remove_activation_preserving_args_entry(); +#else + // Do an uncommon trap type entry. c++ interpreter will know + // to pop frame and preserve the args + pc = Interpreter::deopt_entry(vtos, 0); + use_next_mdp = false; +#endif + } else { + // Reexecute invoke in top frame + pc = Interpreter::deopt_entry(vtos, 0); + use_next_mdp = false; + popframe_preserved_args_size_in_bytes = in_bytes(thread->popframe_preserved_args_size()); + // Note: the PopFrame-related extension of the expression stack size is done in + // Deoptimization::fetch_unroll_info_helper + popframe_preserved_args_size_in_words = in_words(thread->popframe_preserved_args_size_in_words()); + } + } else if (JvmtiExport::can_force_early_return() && state != NULL && state->is_earlyret_pending()) { + // Force early return from top frame after deoptimization +#ifndef CC_INTERP + pc = Interpreter::remove_activation_early_entry(state->earlyret_tos()); +#else + // TBD: Need to implement ForceEarlyReturn for CC_INTERP (ia64) +#endif + } else { + // Possibly override the previous pc computation of the top (youngest) frame + switch (exec_mode) { + case Deoptimization::Unpack_deopt: + // use what we've got + break; + case Deoptimization::Unpack_exception: + // exception is pending + pc = SharedRuntime::raw_exception_handler_for_return_address(pc); + // [phh] We're going to end up in some handler or other, so it doesn't + // matter what mdp we point to. See exception_handler_for_exception() + // in interpreterRuntime.cpp. + break; + case Deoptimization::Unpack_uncommon_trap: + case Deoptimization::Unpack_reexecute: + // redo last byte code + pc = Interpreter::deopt_entry(vtos, 0); + use_next_mdp = false; + break; + default: + ShouldNotReachHere(); + } + } + } + + // Setup the interpreter frame + + assert(method() != NULL, "method must exist"); + int temps = expressions()->size(); + + int locks = monitors() == NULL ? 0 : monitors()->number_of_monitors(); + + Interpreter::layout_activation(method(), + temps + callee_parameters, + popframe_preserved_args_size_in_words, + locks, + callee_parameters, + callee_locals, + caller, + iframe(), + is_top_frame); + + // Update the pc in the frame object and overwrite the temporary pc + // we placed in the skeletal frame now that we finally know the + // exact interpreter address we should use. + + _frame.patch_pc(thread, pc); + + assert (!method()->is_synchronized() || locks > 0, "synchronized methods must have monitors"); + + BasicObjectLock* top = iframe()->interpreter_frame_monitor_begin(); + for (int index = 0; index < locks; index++) { + top = iframe()->previous_monitor_in_interpreter_frame(top); + BasicObjectLock* src = _monitors->at(index); + top->set_obj(src->obj()); + src->lock()->move_to(src->obj(), top->lock()); + } + if (ProfileInterpreter) { + iframe()->interpreter_frame_set_mdx(0); // clear out the mdp. + } + iframe()->interpreter_frame_set_bcx((intptr_t)bcp); // cannot use bcp because frame is not initialized yet + if (ProfileInterpreter) { + methodDataOop mdo = method()->method_data(); + if (mdo != NULL) { + int bci = iframe()->interpreter_frame_bci(); + if (use_next_mdp) ++bci; + address mdp = mdo->bci_to_dp(bci); + iframe()->interpreter_frame_set_mdp(mdp); + } + } + + // Unpack expression stack + // If this is an intermediate frame (i.e. not top frame) then this + // only unpacks the part of the expression stack not used by callee + // as parameters. The callee parameters are unpacked as part of the + // callee locals. + int i; + for(i = 0; i < expressions()->size(); i++) { + StackValue *value = expressions()->at(i); + intptr_t* addr = iframe()->interpreter_frame_expression_stack_at(i); + switch(value->type()) { + case T_INT: + *addr = value->get_int(); + break; + case T_OBJECT: + *addr = value->get_int(T_OBJECT); + break; + case T_CONFLICT: + // A dead stack slot. Initialize to null in case it is an oop. + *addr = NULL_WORD; + break; + default: + ShouldNotReachHere(); + } + if (TaggedStackInterpreter) { + // Write tag to the stack + iframe()->interpreter_frame_set_expression_stack_tag(i, + frame::tag_for_basic_type(value->type())); + } + } + + + // Unpack the locals + for(i = 0; i < locals()->size(); i++) { + StackValue *value = locals()->at(i); + intptr_t* addr = iframe()->interpreter_frame_local_at(i); + switch(value->type()) { + case T_INT: + *addr = value->get_int(); + break; + case T_OBJECT: + *addr = value->get_int(T_OBJECT); + break; + case T_CONFLICT: + // A dead location. If it is an oop then we need a NULL to prevent GC from following it + *addr = NULL_WORD; + break; + default: + ShouldNotReachHere(); + } + if (TaggedStackInterpreter) { + // Write tag to stack + iframe()->interpreter_frame_set_local_tag(i, + frame::tag_for_basic_type(value->type())); + } + } + + if (is_top_frame && JvmtiExport::can_pop_frame() && thread->popframe_forcing_deopt_reexecution()) { + // An interpreted frame was popped but it returns to a deoptimized + // frame. The incoming arguments to the interpreted activation + // were preserved in thread-local storage by the + // remove_activation_preserving_args_entry in the interpreter; now + // we put them back into the just-unpacked interpreter frame. + // Note that this assumes that the locals arena grows toward lower + // addresses. + if (popframe_preserved_args_size_in_words != 0) { + void* saved_args = thread->popframe_preserved_args(); + assert(saved_args != NULL, "must have been saved by interpreter"); +#ifdef ASSERT + int stack_words = Interpreter::stackElementWords(); + assert(popframe_preserved_args_size_in_words <= + iframe()->interpreter_frame_expression_stack_size()*stack_words, + "expression stack size should have been extended"); +#endif // ASSERT + int top_element = iframe()->interpreter_frame_expression_stack_size()-1; + intptr_t* base; + if (frame::interpreter_frame_expression_stack_direction() < 0) { + base = iframe()->interpreter_frame_expression_stack_at(top_element); + } else { + base = iframe()->interpreter_frame_expression_stack(); + } + Copy::conjoint_bytes(saved_args, + base, + popframe_preserved_args_size_in_bytes); + thread->popframe_free_preserved_args(); + } + } + +#ifndef PRODUCT + if (TraceDeoptimization && Verbose) { + ttyLocker ttyl; + tty->print_cr("[%d Interpreted Frame]", ++unpack_counter); + iframe()->print_on(tty); + RegisterMap map(thread); + vframe* f = vframe::new_vframe(iframe(), &map, thread); + f->print(); + iframe()->interpreter_frame_print_on(tty); + + tty->print_cr("locals size %d", locals()->size()); + tty->print_cr("expression size %d", expressions()->size()); + + method()->print_value(); + tty->cr(); + // method()->print_codes(); + } else if (TraceDeoptimization) { + tty->print(" "); + method()->print_value(); + Bytecodes::Code code = Bytecodes::java_code_at(bcp); + int bci = method()->bci_from(bcp); + tty->print(" - %s", Bytecodes::name(code)); + tty->print(" @ bci %d ", bci); + tty->print_cr("sp = " PTR_FORMAT, iframe()->sp()); + } +#endif // PRODUCT + + // The expression stack and locals are in the resource area don't leave + // a dangling pointer in the vframeArray we leave around for debug + // purposes + + _locals = _expressions = NULL; + +} + +int vframeArrayElement::on_stack_size(int callee_parameters, + int callee_locals, + bool is_top_frame, + int popframe_extra_stack_expression_els) const { + assert(method()->max_locals() == locals()->size(), "just checking"); + int locks = monitors() == NULL ? 0 : monitors()->number_of_monitors(); + int temps = expressions()->size(); + return Interpreter::size_activation(method(), + temps + callee_parameters, + popframe_extra_stack_expression_els, + locks, + callee_parameters, + callee_locals, + is_top_frame); +} + + + +vframeArray* vframeArray::allocate(JavaThread* thread, int frame_size, GrowableArray* chunk, + RegisterMap *reg_map, frame sender, frame caller, frame self) { + + // Allocate the vframeArray + vframeArray * result = (vframeArray*) AllocateHeap(sizeof(vframeArray) + // fixed part + sizeof(vframeArrayElement) * (chunk->length() - 1), // variable part + "vframeArray::allocate"); + result->_frames = chunk->length(); + result->_owner_thread = thread; + result->_sender = sender; + result->_caller = caller; + result->_original = self; + result->set_unroll_block(NULL); // initialize it + result->fill_in(thread, frame_size, chunk, reg_map); + return result; +} + +void vframeArray::fill_in(JavaThread* thread, + int frame_size, + GrowableArray* chunk, + const RegisterMap *reg_map) { + // Set owner first, it is used when adding monitor chunks + + _frame_size = frame_size; + for(int i = 0; i < chunk->length(); i++) { + element(i)->fill_in(chunk->at(i)); + } + + // Copy registers for callee-saved registers + if (reg_map != NULL) { + for(int i = 0; i < RegisterMap::reg_count; i++) { +#ifdef AMD64 + // The register map has one entry for every int (32-bit value), so + // 64-bit physical registers have two entries in the map, one for + // each half. Ignore the high halves of 64-bit registers, just like + // frame::oopmapreg_to_location does. + // + // [phh] FIXME: this is a temporary hack! This code *should* work + // correctly w/o this hack, possibly by changing RegisterMap::pd_location + // in frame_amd64.cpp and the values of the phantom high half registers + // in amd64.ad. + // if (VMReg::Name(i) < SharedInfo::stack0 && is_even(i)) { + intptr_t* src = (intptr_t*) reg_map->location(VMRegImpl::as_VMReg(i)); + _callee_registers[i] = src != NULL ? *src : NULL_WORD; + // } else { + // jint* src = (jint*) reg_map->location(VMReg::Name(i)); + // _callee_registers[i] = src != NULL ? *src : NULL_WORD; + // } +#else + jint* src = (jint*) reg_map->location(VMRegImpl::as_VMReg(i)); + _callee_registers[i] = src != NULL ? *src : NULL_WORD; +#endif + if (src == NULL) { + set_location_valid(i, false); + } else { + set_location_valid(i, true); + jint* dst = (jint*) register_location(i); + *dst = *src; + } + } + } +} + +void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode) { + // stack picture + // unpack_frame + // [new interpreter frames ] (frames are skeletal but walkable) + // caller_frame + // + // This routine fills in the missing data for the skeletal interpreter frames + // in the above picture. + + // Find the skeletal interpreter frames to unpack into + RegisterMap map(JavaThread::current(), false); + // Get the youngest frame we will unpack (last to be unpacked) + frame me = unpack_frame.sender(&map); + int index; + for (index = 0; index < frames(); index++ ) { + *element(index)->iframe() = me; + // Get the caller frame (possibly skeletal) + me = me.sender(&map); + } + + frame caller_frame = me; + + // Do the unpacking of interpreter frames; the frame at index 0 represents the top activation, so it has no callee + + // Unpack the frames from the oldest (frames() -1) to the youngest (0) + + for (index = frames() - 1; index >= 0 ; index--) { + int callee_parameters = index == 0 ? 0 : element(index-1)->method()->size_of_parameters(); + int callee_locals = index == 0 ? 0 : element(index-1)->method()->max_locals(); + element(index)->unpack_on_stack(callee_parameters, + callee_locals, + &caller_frame, + index == 0, + exec_mode); + if (index == frames() - 1) { + Deoptimization::unwind_callee_save_values(element(index)->iframe(), this); + } + caller_frame = *element(index)->iframe(); + } + + + deallocate_monitor_chunks(); +} + +void vframeArray::deallocate_monitor_chunks() { + JavaThread* jt = JavaThread::current(); + for (int index = 0; index < frames(); index++ ) { + element(index)->free_monitors(jt); + } +} + +#ifndef PRODUCT + +bool vframeArray::structural_compare(JavaThread* thread, GrowableArray* chunk) { + if (owner_thread() != thread) return false; + int index = 0; +#if 0 // FIXME can't do this comparison + + // Compare only within vframe array. + for (deoptimizedVFrame* vf = deoptimizedVFrame::cast(vframe_at(first_index())); vf; vf = vf->deoptimized_sender_or_null()) { + if (index >= chunk->length() || !vf->structural_compare(chunk->at(index))) return false; + index++; + } + if (index != chunk->length()) return false; +#endif + + return true; +} + +#endif + +address vframeArray::register_location(int i) const { + assert(0 <= i && i < RegisterMap::reg_count, "index out of bounds"); + return (address) & _callee_registers[i]; +} + + +#ifndef PRODUCT + +// Printing + +// Note: we cannot have print_on as const, as we allocate inside the method +void vframeArray::print_on_2(outputStream* st) { + st->print_cr(" - sp: " INTPTR_FORMAT, sp()); + st->print(" - thread: "); + Thread::current()->print(); + st->print_cr(" - frame size: %d", frame_size()); + for (int index = 0; index < frames() ; index++ ) { + element(index)->print(st); + } +} + +void vframeArrayElement::print(outputStream* st) { + st->print_cr(" - interpreter_frame -> sp: ", INTPTR_FORMAT, iframe()->sp()); +} + +void vframeArray::print_value_on(outputStream* st) const { + st->print_cr("vframeArray [%d] ", frames()); +} + + +#endif diff --git a/hotspot/src/share/vm/runtime/vframeArray.hpp b/hotspot/src/share/vm/runtime/vframeArray.hpp new file mode 100644 index 00000000000..767b98907b9 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vframeArray.hpp @@ -0,0 +1,201 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A vframeArray is an array used for momentarily storing off stack Java method activations +// during deoptimization. Essentially it is an array of vframes where each vframe +// data is stored off stack. This structure will never exist across a safepoint so +// there is no need to gc any oops that are stored in the structure. + + +class LocalsClosure; +class ExpressionStackClosure; +class MonitorStackClosure; +class MonitorArrayElement; +class StackValueCollection; + +// A vframeArrayElement is an element of a vframeArray. Each element +// represent an interpreter frame which will eventually be created. + +class vframeArrayElement : public _ValueObj { + private: + + frame _frame; // the interpreter frame we will unpack into + int _bci; // raw bci for this vframe + methodOop _method; // the method for this vframe + MonitorChunk* _monitors; // active monitors for this vframe + StackValueCollection* _locals; + StackValueCollection* _expressions; + + public: + + frame* iframe(void) { return &_frame; } + + int bci(void) const; + + int raw_bci(void) const { return _bci; } + + methodOop method(void) const { return _method; } + + MonitorChunk* monitors(void) const { return _monitors; } + + void free_monitors(JavaThread* jt); + + StackValueCollection* locals(void) const { return _locals; } + + StackValueCollection* expressions(void) const { return _expressions; } + + void fill_in(compiledVFrame* vf); + + // Formerly part of deoptimizedVFrame + + + // Returns the on stack word size for this frame + // callee_parameters is the number of callee locals residing inside this frame + int on_stack_size(int callee_parameters, + int callee_locals, + bool is_top_frame, + int popframe_extra_stack_expression_els) const; + + // Unpacks the element to skeletal interpreter frame + void unpack_on_stack(int callee_parameters, + int callee_locals, + frame* caller, + bool is_top_frame, + int exec_mode); + +#ifndef PRODUCT + void print(outputStream* st); +#endif /* PRODUCT */ +}; + +// this can be a ResourceObj if we don't save the last one... +// but it does make debugging easier even if we can't look +// at the data in each vframeElement + +class vframeArray: public CHeapObj { + private: + + + // Here is what a vframeArray looks like in memory + + /* + fixed part + description of the original frame + _frames - number of vframes in this array + adapter info + callee register save area + variable part + vframeArrayElement [ 0 ] + ... + vframeArrayElement [_frames - 1] + + */ + + JavaThread* _owner_thread; + vframeArray* _next; + frame _original; // the original frame of the deoptee + frame _caller; // caller of root frame in vframeArray + frame _sender; + + Deoptimization::UnrollBlock* _unroll_block; + int _frame_size; + + int _frames; // number of javavframes in the array (does not count any adapter) + + intptr_t _callee_registers[RegisterMap::reg_count]; + unsigned char _valid[RegisterMap::reg_count]; + + vframeArrayElement _elements[1]; // First variable section. + + void fill_in_element(int index, compiledVFrame* vf); + + bool is_location_valid(int i) const { return _valid[i] != 0; } + void set_location_valid(int i, bool valid) { _valid[i] = valid; } + + public: + + + // Tells whether index is within bounds. + bool is_within_bounds(int index) const { return 0 <= index && index < frames(); } + + // Accessores for instance variable + int frames() const { return _frames; } + + static vframeArray* allocate(JavaThread* thread, int frame_size, GrowableArray* chunk, + RegisterMap* reg_map, frame sender, frame caller, frame self); + + + vframeArrayElement* element(int index) { assert(is_within_bounds(index), "Bad index"); return &_elements[index]; } + + // Allocates a new vframe in the array and fills the array with vframe information in chunk + void fill_in(JavaThread* thread, int frame_size, GrowableArray* chunk, const RegisterMap *reg_map); + + // Returns the owner of this vframeArray + JavaThread* owner_thread() const { return _owner_thread; } + + // Accessors for next + vframeArray* next() const { return _next; } + void set_next(vframeArray* value) { _next = value; } + + // Accessors for sp + intptr_t* sp() const { return _original.sp(); } + + intptr_t* unextended_sp() const { return _original.unextended_sp(); } + + address original_pc() const { return _original.pc(); } + + frame original() const { return _original; } + + frame caller() const { return _caller; } + + frame sender() const { return _sender; } + + // Accessors for unroll block + Deoptimization::UnrollBlock* unroll_block() const { return _unroll_block; } + void set_unroll_block(Deoptimization::UnrollBlock* block) { _unroll_block = block; } + + // Returns the size of the frame that got deoptimized + int frame_size() const { return _frame_size; } + + // Unpack the array on the stack passed in stack interval + void unpack_to_stack(frame &unpack_frame, int exec_mode); + + // Deallocates monitor chunks allocated during deoptimization. + // This should be called when the array is not used anymore. + void deallocate_monitor_chunks(); + + + + // Accessor for register map + address register_location(int i) const; + + void print_on_2(outputStream* st) PRODUCT_RETURN; + void print_value_on(outputStream* st) const PRODUCT_RETURN; + +#ifndef PRODUCT + // Comparing + bool structural_compare(JavaThread* thread, GrowableArray* chunk); +#endif + +}; diff --git a/hotspot/src/share/vm/runtime/vframe_hp.cpp b/hotspot/src/share/vm/runtime/vframe_hp.cpp new file mode 100644 index 00000000000..3399e23a5d8 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vframe_hp.cpp @@ -0,0 +1,337 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vframe_hp.cpp.incl" + + +// ------------- compiledVFrame -------------- + +StackValueCollection* compiledVFrame::locals() const { + // Natives has no scope + if (scope() == NULL) return new StackValueCollection(0); + GrowableArray* scv_list = scope()->locals(); + if (scv_list == NULL) return new StackValueCollection(0); + + // scv_list is the list of ScopeValues describing the JVM stack state. + // There is one scv_list entry for every JVM stack state in use. + int length = scv_list->length(); + StackValueCollection* result = new StackValueCollection(length); + // In rare instances set_locals may have occurred in which case + // there are local values that are not described by the ScopeValue anymore + GrowableArray* deferred = NULL; + GrowableArray* list = thread()->deferred_locals(); + if (list != NULL ) { + // In real life this never happens or is typically a single element search + for (int i = 0; i < list->length(); i++) { + if (list->at(i)->matches((vframe*)this)) { + deferred = list->at(i)->locals(); + break; + } + } + } + + for( int i = 0; i < length; i++ ) { + result->add( create_stack_value(scv_list->at(i)) ); + } + + // Replace specified locals with any deferred writes that are present + if (deferred != NULL) { + for ( int l = 0; l < deferred->length() ; l ++) { + jvmtiDeferredLocalVariable* val = deferred->at(l); + switch (val->type()) { + case T_BOOLEAN: + result->set_int_at(val->index(), val->value().z); + break; + case T_CHAR: + result->set_int_at(val->index(), val->value().c); + break; + case T_FLOAT: + result->set_float_at(val->index(), val->value().f); + break; + case T_DOUBLE: + result->set_double_at(val->index(), val->value().d); + break; + case T_BYTE: + result->set_int_at(val->index(), val->value().b); + break; + case T_SHORT: + result->set_int_at(val->index(), val->value().s); + break; + case T_INT: + result->set_int_at(val->index(), val->value().i); + break; + case T_LONG: + result->set_long_at(val->index(), val->value().j); + break; + case T_OBJECT: + { + Handle obj((oop)val->value().l); + result->set_obj_at(val->index(), obj); + } + break; + default: + ShouldNotReachHere(); + } + } + } + + return result; +} + + +void compiledVFrame::set_locals(StackValueCollection* values) const { + + fatal("Should use update_local for each local update"); +} + +void compiledVFrame::update_local(BasicType type, int index, jvalue value) { + +#ifdef ASSERT + + assert(fr().is_deoptimized_frame(), "frame must be scheduled for deoptimization"); +#endif /* ASSERT */ + GrowableArray* deferred = thread()->deferred_locals(); + if (deferred != NULL ) { + // See if this vframe has already had locals with deferred writes + int f; + for ( f = 0 ; f < deferred->length() ; f++ ) { + if (deferred->at(f)->matches(this)) { + // Matching, vframe now see if the local already had deferred write + GrowableArray* locals = deferred->at(f)->locals(); + int l; + for (l = 0 ; l < locals->length() ; l++ ) { + if (locals->at(l)->index() == index) { + locals->at(l)->set_value(value); + return; + } + } + // No matching local already present. Push a new value onto the deferred collection + locals->push(new jvmtiDeferredLocalVariable(index, type, value)); + return; + } + } + // No matching vframe must push a new vframe + } else { + // No deferred updates pending for this thread. + // allocate in C heap + deferred = new(ResourceObj::C_HEAP) GrowableArray (1, true); + thread()->set_deferred_locals(deferred); + } + deferred->push(new jvmtiDeferredLocalVariableSet(method(), bci(), fr().id())); + assert(deferred->top()->id() == fr().id(), "Huh? Must match"); + deferred->top()->set_local_at(index, type, value); +} + +StackValueCollection* compiledVFrame::expressions() const { + // Natives has no scope + if (scope() == NULL) return new StackValueCollection(0); + GrowableArray* scv_list = scope()->expressions(); + if (scv_list == NULL) return new StackValueCollection(0); + + // scv_list is the list of ScopeValues describing the JVM stack state. + // There is one scv_list entry for every JVM stack state in use. + int length = scv_list->length(); + StackValueCollection* result = new StackValueCollection(length); + for( int i = 0; i < length; i++ ) + result->add( create_stack_value(scv_list->at(i)) ); + + return result; +} + + +// The implementation of the following two methods was factorized into the +// class StackValue because it is also used from within deoptimization.cpp for +// rematerialization and relocking of non-escaping objects. + +StackValue *compiledVFrame::create_stack_value(ScopeValue *sv) const { + return StackValue::create_stack_value(&_fr, register_map(), sv); +} + +BasicLock* compiledVFrame::resolve_monitor_lock(Location location) const { + return StackValue::resolve_monitor_lock(&_fr, location); +} + + +GrowableArray* compiledVFrame::monitors() const { + // Natives has no scope + if (scope() == NULL) { + nmethod* nm = code(); + methodOop method = nm->method(); + assert(method->is_native(), ""); + if (!method->is_synchronized()) { + return new GrowableArray(0); + } + // This monitor is really only needed for UseBiasedLocking, but + // return it in all cases for now as it might be useful for stack + // traces and tools as well + GrowableArray *monitors = new GrowableArray(1); + // Casting away const + frame& fr = (frame&) _fr; + MonitorInfo* info = new MonitorInfo(fr.compiled_synchronized_native_monitor_owner(nm), + fr.compiled_synchronized_native_monitor(nm)); + monitors->push(info); + return monitors; + } + GrowableArray* monitors = scope()->monitors(); + if (monitors == NULL) { + return new GrowableArray(0); + } + GrowableArray* result = new GrowableArray(monitors->length()); + for (int index = 0; index < monitors->length(); index++) { + MonitorValue* mv = monitors->at(index); + StackValue *owner_sv = create_stack_value(mv->owner()); // it is an oop + result->push(new MonitorInfo(owner_sv->get_obj()(), resolve_monitor_lock(mv->basic_lock()))); + } + return result; +} + + +compiledVFrame::compiledVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread, nmethod* nm) +: javaVFrame(fr, reg_map, thread) { + _scope = NULL; + // Compiled method (native stub or Java code) + // native wrappers have no scope data, it is implied + if (!nm->is_native_method()) { + _scope = nm->scope_desc_at(_fr.pc()); + } +} + +compiledVFrame::compiledVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread, ScopeDesc* scope) +: javaVFrame(fr, reg_map, thread) { + _scope = scope; + guarantee(_scope != NULL, "scope must be present"); +} + + +bool compiledVFrame::is_top() const { + // FIX IT: Remove this when new native stubs are in place + if (scope() == NULL) return true; + return scope()->is_top(); +} + + +nmethod* compiledVFrame::code() const { + return CodeCache::find_nmethod(_fr.pc()); +} + + +methodOop compiledVFrame::method() const { + if (scope() == NULL) { + // native nmethods have no scope the method is implied + nmethod* nm = code(); + assert(nm->is_native_method(), "must be native"); + return nm->method(); + } + return scope()->method()(); +} + + +int compiledVFrame::bci() const { + int raw = raw_bci(); + return raw == SynchronizationEntryBCI ? 0 : raw; +} + + +int compiledVFrame::raw_bci() const { + if (scope() == NULL) { + // native nmethods have no scope the method/bci is implied + nmethod* nm = code(); + assert(nm->is_native_method(), "must be native"); + return 0; + } + return scope()->bci(); +} + + +vframe* compiledVFrame::sender() const { + const frame f = fr(); + if (scope() == NULL) { + // native nmethods have no scope the method/bci is implied + nmethod* nm = code(); + assert(nm->is_native_method(), "must be native"); + return vframe::sender(); + } else { + return scope()->is_top() + ? vframe::sender() + : new compiledVFrame(&f, register_map(), thread(), scope()->sender()); + } +} + +jvmtiDeferredLocalVariableSet::jvmtiDeferredLocalVariableSet(methodOop method, int bci, intptr_t* id) { + _method = method; + _bci = bci; + _id = id; + // Alway will need at least one, must be on C heap + _locals = new(ResourceObj::C_HEAP) GrowableArray (1, true); +} + +jvmtiDeferredLocalVariableSet::~jvmtiDeferredLocalVariableSet() { + for (int i = 0; i < _locals->length() ; i++ ) { + delete _locals->at(i); + } + // Free growableArray and c heap for elements + delete _locals; +} + +bool jvmtiDeferredLocalVariableSet::matches(vframe* vf) { + if (!vf->is_compiled_frame()) return false; + compiledVFrame* cvf = (compiledVFrame*)vf; + return cvf->fr().id() == id() && cvf->method() == method() && cvf->bci() == bci(); +} + +void jvmtiDeferredLocalVariableSet::set_local_at(int idx, BasicType type, jvalue val) { + int i; + for ( i = 0 ; i < locals()->length() ; i++ ) { + if ( locals()->at(i)->index() == idx) { + assert(locals()->at(i)->type() == type, "Wrong type"); + locals()->at(i)->set_value(val); + return; + } + } + locals()->push(new jvmtiDeferredLocalVariable(idx, type, val)); +} + +void jvmtiDeferredLocalVariableSet::oops_do(OopClosure* f) { + + f->do_oop((oop*) &_method); + for ( int i = 0; i < locals()->length(); i++ ) { + if ( locals()->at(i)->type() == T_OBJECT) { + f->do_oop(locals()->at(i)->oop_addr()); + } + } +} + +jvmtiDeferredLocalVariable::jvmtiDeferredLocalVariable(int index, BasicType type, jvalue value) { + _index = index; + _type = type; + _value = value; +} + + +#ifndef PRODUCT +void compiledVFrame::verify() const { + Unimplemented(); +} +#endif // PRODUCT diff --git a/hotspot/src/share/vm/runtime/vframe_hp.hpp b/hotspot/src/share/vm/runtime/vframe_hp.hpp new file mode 100644 index 00000000000..a0dd0be2fdc --- /dev/null +++ b/hotspot/src/share/vm/runtime/vframe_hp.hpp @@ -0,0 +1,135 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class compiledVFrame: public javaVFrame { + public: + // JVM state + methodOop method() const; + int bci() const; + StackValueCollection* locals() const; + StackValueCollection* expressions() const; + GrowableArray* monitors() const; + + void set_locals(StackValueCollection* values) const; + + // Virtuals defined in vframe + bool is_compiled_frame() const { return true; } + vframe* sender() const; + bool is_top() const; + + // Casting + static compiledVFrame* cast(vframe* vf) { + assert(vf == NULL || vf->is_compiled_frame(), "must be compiled frame"); + return (compiledVFrame*) vf; + } + + public: + // Constructors + compiledVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread, nmethod* nm); + + // Update a local in a compiled frame. Update happens when deopt occurs + void update_local(BasicType type, int index, jvalue value); + + // Returns the active nmethod + nmethod* code() const; + + // Returns the scopeDesc + ScopeDesc* scope() const { return _scope; } + + // Returns SynchronizationEntryBCI or bci() (used for synchronization) + int raw_bci() const; + + protected: + ScopeDesc* _scope; + + + //StackValue resolve(ScopeValue* sv) const; + BasicLock* resolve_monitor_lock(Location location) const; + StackValue *create_stack_value(ScopeValue *sv) const; + + private: + compiledVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread, ScopeDesc* scope); + +#ifndef PRODUCT + public: + void verify() const; +#endif +}; + +// In order to implement set_locals for compiled vframes we must +// store updated locals in a data structure that contains enough +// information to recognize equality with a vframe and to store +// any updated locals. + +class jvmtiDeferredLocalVariable; +class jvmtiDeferredLocalVariableSet : public CHeapObj { +private: + + methodOop _method; // must be GC'd + int _bci; + intptr_t* _id; + GrowableArray* _locals; + + public: + // JVM state + methodOop method() const { return _method; } + int bci() const { return _bci; } + intptr_t* id() const { return _id; } + GrowableArray* locals() const { return _locals; } + void set_local_at(int idx, BasicType typ, jvalue val); + + // Does the vframe match this jvmtiDeferredLocalVariableSet + bool matches(vframe* vf); + // GC + void oops_do(OopClosure* f); + + // constructor + jvmtiDeferredLocalVariableSet(methodOop method, int bci, intptr_t* id); + + // destructor + ~jvmtiDeferredLocalVariableSet(); + + +}; + +class jvmtiDeferredLocalVariable : public CHeapObj { + public: + + jvmtiDeferredLocalVariable(int index, BasicType type, jvalue value); + + BasicType type(void) { return _type; } + int index(void) { return _index; } + jvalue value(void) { return _value; } + // Only mutator is for value as only it can change + void set_value(jvalue value) { _value = value; } + // For gc + oop* oop_addr(void) { return (oop*) &_value.l; } + + private: + + BasicType _type; + jvalue _value; + int _index; + +}; diff --git a/hotspot/src/share/vm/runtime/virtualspace.cpp b/hotspot/src/share/vm/runtime/virtualspace.cpp new file mode 100644 index 00000000000..23b75dc9dd4 --- /dev/null +++ b/hotspot/src/share/vm/runtime/virtualspace.cpp @@ -0,0 +1,704 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_virtualspace.cpp.incl" + + +// ReservedSpace +ReservedSpace::ReservedSpace(size_t size) { + initialize(size, 0, false, NULL); +} + +ReservedSpace::ReservedSpace(size_t size, size_t alignment, + bool large, char* requested_address) { + initialize(size, alignment, large, requested_address); +} + +char * +ReservedSpace::align_reserved_region(char* addr, const size_t len, + const size_t prefix_size, + const size_t prefix_align, + const size_t suffix_size, + const size_t suffix_align) +{ + assert(addr != NULL, "sanity"); + const size_t required_size = prefix_size + suffix_size; + assert(len >= required_size, "len too small"); + + const size_t s = size_t(addr); + const size_t beg_ofs = s + prefix_size & suffix_align - 1; + const size_t beg_delta = beg_ofs == 0 ? 0 : suffix_align - beg_ofs; + + if (len < beg_delta + required_size) { + return NULL; // Cannot do proper alignment. + } + const size_t end_delta = len - (beg_delta + required_size); + + if (beg_delta != 0) { + os::release_memory(addr, beg_delta); + } + + if (end_delta != 0) { + char* release_addr = (char*) (s + beg_delta + required_size); + os::release_memory(release_addr, end_delta); + } + + return (char*) (s + beg_delta); +} + +char* ReservedSpace::reserve_and_align(const size_t reserve_size, + const size_t prefix_size, + const size_t prefix_align, + const size_t suffix_size, + const size_t suffix_align) +{ + assert(reserve_size > prefix_size + suffix_size, "should not be here"); + + char* raw_addr = os::reserve_memory(reserve_size, NULL, prefix_align); + if (raw_addr == NULL) return NULL; + + char* result = align_reserved_region(raw_addr, reserve_size, prefix_size, + prefix_align, suffix_size, + suffix_align); + if (result == NULL && !os::release_memory(raw_addr, reserve_size)) { + fatal("os::release_memory failed"); + } + +#ifdef ASSERT + if (result != NULL) { + const size_t raw = size_t(raw_addr); + const size_t res = size_t(result); + assert(res >= raw, "alignment decreased start addr"); + assert(res + prefix_size + suffix_size <= raw + reserve_size, + "alignment increased end addr"); + assert((res & prefix_align - 1) == 0, "bad alignment of prefix"); + assert((res + prefix_size & suffix_align - 1) == 0, + "bad alignment of suffix"); + } +#endif + + return result; +} + +ReservedSpace::ReservedSpace(const size_t prefix_size, + const size_t prefix_align, + const size_t suffix_size, + const size_t suffix_align) +{ + assert(prefix_size != 0, "sanity"); + assert(prefix_align != 0, "sanity"); + assert(suffix_size != 0, "sanity"); + assert(suffix_align != 0, "sanity"); + assert((prefix_size & prefix_align - 1) == 0, + "prefix_size not divisible by prefix_align"); + assert((suffix_size & suffix_align - 1) == 0, + "suffix_size not divisible by suffix_align"); + assert((suffix_align & prefix_align - 1) == 0, + "suffix_align not divisible by prefix_align"); + + // On systems where the entire region has to be reserved and committed up + // front, the compound alignment normally done by this method is unnecessary. + const bool try_reserve_special = UseLargePages && + prefix_align == os::large_page_size(); + if (!os::can_commit_large_page_memory() && try_reserve_special) { + initialize(prefix_size + suffix_size, prefix_align, true); + return; + } + + _base = NULL; + _size = 0; + _alignment = 0; + _special = false; + + // Optimistically try to reserve the exact size needed. + const size_t size = prefix_size + suffix_size; + char* addr = os::reserve_memory(size, NULL, prefix_align); + if (addr == NULL) return; + + // Check whether the result has the needed alignment (unlikely unless + // prefix_align == suffix_align). + const size_t ofs = size_t(addr) + prefix_size & suffix_align - 1; + if (ofs != 0) { + // Wrong alignment. Release, allocate more space and do manual alignment. + // + // On most operating systems, another allocation with a somewhat larger size + // will return an address "close to" that of the previous allocation. The + // result is often the same address (if the kernel hands out virtual + // addresses from low to high), or an address that is offset by the increase + // in size. Exploit that to minimize the amount of extra space requested. + if (!os::release_memory(addr, size)) { + fatal("os::release_memory failed"); + } + + const size_t extra = MAX2(ofs, suffix_align - ofs); + addr = reserve_and_align(size + extra, prefix_size, prefix_align, + suffix_size, suffix_align); + if (addr == NULL) { + // Try an even larger region. If this fails, address space is exhausted. + addr = reserve_and_align(size + suffix_align, prefix_size, + prefix_align, suffix_size, suffix_align); + } + } + + _base = addr; + _size = size; + _alignment = prefix_align; +} + +void ReservedSpace::initialize(size_t size, size_t alignment, bool large, + char* requested_address) { + const size_t granularity = os::vm_allocation_granularity(); + assert((size & granularity - 1) == 0, + "size not aligned to os::vm_allocation_granularity()"); + assert((alignment & granularity - 1) == 0, + "alignment not aligned to os::vm_allocation_granularity()"); + assert(alignment == 0 || is_power_of_2((intptr_t)alignment), + "not a power of 2"); + + _base = NULL; + _size = 0; + _special = false; + _alignment = 0; + if (size == 0) { + return; + } + + // If OS doesn't support demand paging for large page memory, we need + // to use reserve_memory_special() to reserve and pin the entire region. + bool special = large && !os::can_commit_large_page_memory(); + char* base = NULL; + + if (special) { + // It's not hard to implement reserve_memory_special() such that it can + // allocate at fixed address, but there seems no use of this feature + // for now, so it's not implemented. + assert(requested_address == NULL, "not implemented"); + + base = os::reserve_memory_special(size); + + if (base != NULL) { + // Check alignment constraints + if (alignment > 0) { + assert((uintptr_t) base % alignment == 0, + "Large pages returned a non-aligned address"); + } + _special = true; + } else { + // failed; try to reserve regular memory below + } + } + + if (base == NULL) { + // Optimistically assume that the OSes returns an aligned base pointer. + // When reserving a large address range, most OSes seem to align to at + // least 64K. + + // If the memory was requested at a particular address, use + // os::attempt_reserve_memory_at() to avoid over mapping something + // important. If available space is not detected, return NULL. + + if (requested_address != 0) { + base = os::attempt_reserve_memory_at(size, requested_address); + } else { + base = os::reserve_memory(size, NULL, alignment); + } + + if (base == NULL) return; + + // Check alignment constraints + if (alignment > 0 && ((size_t)base & alignment - 1) != 0) { + // Base not aligned, retry + if (!os::release_memory(base, size)) fatal("os::release_memory failed"); + // Reserve size large enough to do manual alignment and + // increase size to a multiple of the desired alignment + size = align_size_up(size, alignment); + size_t extra_size = size + alignment; + char* extra_base = os::reserve_memory(extra_size, NULL, alignment); + if (extra_base == NULL) return; + // Do manual alignement + base = (char*) align_size_up((uintptr_t) extra_base, alignment); + assert(base >= extra_base, "just checking"); + // Release unused areas + size_t unused_bottom_size = base - extra_base; + size_t unused_top_size = extra_size - size - unused_bottom_size; + assert(unused_bottom_size % os::vm_allocation_granularity() == 0, + "size not allocation aligned"); + assert(unused_top_size % os::vm_allocation_granularity() == 0, + "size not allocation aligned"); + if (unused_bottom_size > 0) { + os::release_memory(extra_base, unused_bottom_size); + } + if (unused_top_size > 0) { + os::release_memory(base + size, unused_top_size); + } + } + } + // Done + _base = base; + _size = size; + _alignment = MAX2(alignment, (size_t) os::vm_page_size()); + + assert(markOopDesc::encode_pointer_as_mark(_base)->decode_pointer() == _base, + "area must be distinguisable from marks for mark-sweep"); + assert(markOopDesc::encode_pointer_as_mark(&_base[size])->decode_pointer() == &_base[size], + "area must be distinguisable from marks for mark-sweep"); +} + + +ReservedSpace::ReservedSpace(char* base, size_t size, size_t alignment, + bool special) { + assert((size % os::vm_allocation_granularity()) == 0, + "size not allocation aligned"); + _base = base; + _size = size; + _alignment = alignment; + _special = special; +} + + +ReservedSpace ReservedSpace::first_part(size_t partition_size, size_t alignment, + bool split, bool realloc) { + assert(partition_size <= size(), "partition failed"); + if (split) { + os::split_reserved_memory(_base, _size, partition_size, realloc); + } + ReservedSpace result(base(), partition_size, alignment, special()); + return result; +} + + +ReservedSpace +ReservedSpace::last_part(size_t partition_size, size_t alignment) { + assert(partition_size <= size(), "partition failed"); + ReservedSpace result(base() + partition_size, size() - partition_size, + alignment, special()); + return result; +} + + +size_t ReservedSpace::page_align_size_up(size_t size) { + return align_size_up(size, os::vm_page_size()); +} + + +size_t ReservedSpace::page_align_size_down(size_t size) { + return align_size_down(size, os::vm_page_size()); +} + + +size_t ReservedSpace::allocation_align_size_up(size_t size) { + return align_size_up(size, os::vm_allocation_granularity()); +} + + +size_t ReservedSpace::allocation_align_size_down(size_t size) { + return align_size_down(size, os::vm_allocation_granularity()); +} + + +void ReservedSpace::release() { + if (is_reserved()) { + if (special()) { + os::release_memory_special(_base, _size); + } else{ + os::release_memory(_base, _size); + } + _base = NULL; + _size = 0; + _special = false; + } +} + + +// VirtualSpace + +VirtualSpace::VirtualSpace() { + _low_boundary = NULL; + _high_boundary = NULL; + _low = NULL; + _high = NULL; + _lower_high = NULL; + _middle_high = NULL; + _upper_high = NULL; + _lower_high_boundary = NULL; + _middle_high_boundary = NULL; + _upper_high_boundary = NULL; + _lower_alignment = 0; + _middle_alignment = 0; + _upper_alignment = 0; +} + + +bool VirtualSpace::initialize(ReservedSpace rs, size_t committed_size) { + if(!rs.is_reserved()) return false; // allocation failed. + assert(_low_boundary == NULL, "VirtualSpace already initialized"); + _low_boundary = rs.base(); + _high_boundary = low_boundary() + rs.size(); + + _low = low_boundary(); + _high = low(); + + _special = rs.special(); + + // When a VirtualSpace begins life at a large size, make all future expansion + // and shrinking occur aligned to a granularity of large pages. This avoids + // fragmentation of physical addresses that inhibits the use of large pages + // by the OS virtual memory system. Empirically, we see that with a 4MB + // page size, the only spaces that get handled this way are codecache and + // the heap itself, both of which provide a substantial performance + // boost in many benchmarks when covered by large pages. + // + // No attempt is made to force large page alignment at the very top and + // bottom of the space if they are not aligned so already. + _lower_alignment = os::vm_page_size(); + _middle_alignment = os::page_size_for_region(rs.size(), rs.size(), 1); + _upper_alignment = os::vm_page_size(); + + // End of each region + _lower_high_boundary = (char*) round_to((intptr_t) low_boundary(), middle_alignment()); + _middle_high_boundary = (char*) round_down((intptr_t) high_boundary(), middle_alignment()); + _upper_high_boundary = high_boundary(); + + // High address of each region + _lower_high = low_boundary(); + _middle_high = lower_high_boundary(); + _upper_high = middle_high_boundary(); + + // commit to initial size + if (committed_size > 0) { + if (!expand_by(committed_size)) { + return false; + } + } + return true; +} + + +VirtualSpace::~VirtualSpace() { + release(); +} + + +void VirtualSpace::release() { + (void)os::release_memory(low_boundary(), reserved_size()); + _low_boundary = NULL; + _high_boundary = NULL; + _low = NULL; + _high = NULL; + _lower_high = NULL; + _middle_high = NULL; + _upper_high = NULL; + _lower_high_boundary = NULL; + _middle_high_boundary = NULL; + _upper_high_boundary = NULL; + _lower_alignment = 0; + _middle_alignment = 0; + _upper_alignment = 0; + _special = false; +} + + +size_t VirtualSpace::committed_size() const { + return pointer_delta(high(), low(), sizeof(char)); +} + + +size_t VirtualSpace::reserved_size() const { + return pointer_delta(high_boundary(), low_boundary(), sizeof(char)); +} + + +size_t VirtualSpace::uncommitted_size() const { + return reserved_size() - committed_size(); +} + + +bool VirtualSpace::contains(const void* p) const { + return low() <= (const char*) p && (const char*) p < high(); +} + +/* + First we need to determine if a particular virtual space is using large + pages. This is done at the initialize function and only virtual spaces + that are larger than LargePageSizeInBytes use large pages. Once we + have determined this, all expand_by and shrink_by calls must grow and + shrink by large page size chunks. If a particular request + is within the current large page, the call to commit and uncommit memory + can be ignored. In the case that the low and high boundaries of this + space is not large page aligned, the pages leading to the first large + page address and the pages after the last large page address must be + allocated with default pages. +*/ +bool VirtualSpace::expand_by(size_t bytes, bool pre_touch) { + if (uncommitted_size() < bytes) return false; + + if (special()) { + // don't commit memory if the entire space is pinned in memory + _high += bytes; + return true; + } + + char* previous_high = high(); + char* unaligned_new_high = high() + bytes; + assert(unaligned_new_high <= high_boundary(), + "cannot expand by more than upper boundary"); + + // Calculate where the new high for each of the regions should be. If + // the low_boundary() and high_boundary() are LargePageSizeInBytes aligned + // then the unaligned lower and upper new highs would be the + // lower_high() and upper_high() respectively. + char* unaligned_lower_new_high = + MIN2(unaligned_new_high, lower_high_boundary()); + char* unaligned_middle_new_high = + MIN2(unaligned_new_high, middle_high_boundary()); + char* unaligned_upper_new_high = + MIN2(unaligned_new_high, upper_high_boundary()); + + // Align the new highs based on the regions alignment. lower and upper + // alignment will always be default page size. middle alignment will be + // LargePageSizeInBytes if the actual size of the virtual space is in + // fact larger than LargePageSizeInBytes. + char* aligned_lower_new_high = + (char*) round_to((intptr_t) unaligned_lower_new_high, lower_alignment()); + char* aligned_middle_new_high = + (char*) round_to((intptr_t) unaligned_middle_new_high, middle_alignment()); + char* aligned_upper_new_high = + (char*) round_to((intptr_t) unaligned_upper_new_high, upper_alignment()); + + // Determine which regions need to grow in this expand_by call. + // If you are growing in the lower region, high() must be in that + // region so calcuate the size based on high(). For the middle and + // upper regions, determine the starting point of growth based on the + // location of high(). By getting the MAX of the region's low address + // (or the prevoius region's high address) and high(), we can tell if it + // is an intra or inter region growth. + size_t lower_needs = 0; + if (aligned_lower_new_high > lower_high()) { + lower_needs = + pointer_delta(aligned_lower_new_high, lower_high(), sizeof(char)); + } + size_t middle_needs = 0; + if (aligned_middle_new_high > middle_high()) { + middle_needs = + pointer_delta(aligned_middle_new_high, middle_high(), sizeof(char)); + } + size_t upper_needs = 0; + if (aligned_upper_new_high > upper_high()) { + upper_needs = + pointer_delta(aligned_upper_new_high, upper_high(), sizeof(char)); + } + + // Check contiguity. + assert(low_boundary() <= lower_high() && + lower_high() <= lower_high_boundary(), + "high address must be contained within the region"); + assert(lower_high_boundary() <= middle_high() && + middle_high() <= middle_high_boundary(), + "high address must be contained within the region"); + assert(middle_high_boundary() <= upper_high() && + upper_high() <= upper_high_boundary(), + "high address must be contained within the region"); + + // Commit regions + if (lower_needs > 0) { + assert(low_boundary() <= lower_high() && + lower_high() + lower_needs <= lower_high_boundary(), + "must not expand beyond region"); + if (!os::commit_memory(lower_high(), lower_needs)) { + debug_only(warning("os::commit_memory failed")); + return false; + } else { + _lower_high += lower_needs; + } + } + if (middle_needs > 0) { + assert(lower_high_boundary() <= middle_high() && + middle_high() + middle_needs <= middle_high_boundary(), + "must not expand beyond region"); + if (!os::commit_memory(middle_high(), middle_needs, middle_alignment())) { + debug_only(warning("os::commit_memory failed")); + return false; + } + _middle_high += middle_needs; + } + if (upper_needs > 0) { + assert(middle_high_boundary() <= upper_high() && + upper_high() + upper_needs <= upper_high_boundary(), + "must not expand beyond region"); + if (!os::commit_memory(upper_high(), upper_needs)) { + debug_only(warning("os::commit_memory failed")); + return false; + } else { + _upper_high += upper_needs; + } + } + + if (pre_touch || AlwaysPreTouch) { + int vm_ps = os::vm_page_size(); + for (char* curr = previous_high; + curr < unaligned_new_high; + curr += vm_ps) { + // Note the use of a write here; originally we tried just a read, but + // since the value read was unused, the optimizer removed the read. + // If we ever have a concurrent touchahead thread, we'll want to use + // a read, to avoid the potential of overwriting data (if a mutator + // thread beats the touchahead thread to a page). There are various + // ways of making sure this read is not optimized away: for example, + // generating the code for a read procedure at runtime. + *curr = 0; + } + } + + _high += bytes; + return true; +} + +// A page is uncommitted if the contents of the entire page is deemed unusable. +// Continue to decrement the high() pointer until it reaches a page boundary +// in which case that particular page can now be uncommitted. +void VirtualSpace::shrink_by(size_t size) { + if (committed_size() < size) + fatal("Cannot shrink virtual space to negative size"); + + if (special()) { + // don't uncommit if the entire space is pinned in memory + _high -= size; + return; + } + + char* unaligned_new_high = high() - size; + assert(unaligned_new_high >= low_boundary(), "cannot shrink past lower boundary"); + + // Calculate new unaligned address + char* unaligned_upper_new_high = + MAX2(unaligned_new_high, middle_high_boundary()); + char* unaligned_middle_new_high = + MAX2(unaligned_new_high, lower_high_boundary()); + char* unaligned_lower_new_high = + MAX2(unaligned_new_high, low_boundary()); + + // Align address to region's alignment + char* aligned_upper_new_high = + (char*) round_to((intptr_t) unaligned_upper_new_high, upper_alignment()); + char* aligned_middle_new_high = + (char*) round_to((intptr_t) unaligned_middle_new_high, middle_alignment()); + char* aligned_lower_new_high = + (char*) round_to((intptr_t) unaligned_lower_new_high, lower_alignment()); + + // Determine which regions need to shrink + size_t upper_needs = 0; + if (aligned_upper_new_high < upper_high()) { + upper_needs = + pointer_delta(upper_high(), aligned_upper_new_high, sizeof(char)); + } + size_t middle_needs = 0; + if (aligned_middle_new_high < middle_high()) { + middle_needs = + pointer_delta(middle_high(), aligned_middle_new_high, sizeof(char)); + } + size_t lower_needs = 0; + if (aligned_lower_new_high < lower_high()) { + lower_needs = + pointer_delta(lower_high(), aligned_lower_new_high, sizeof(char)); + } + + // Check contiguity. + assert(middle_high_boundary() <= upper_high() && + upper_high() <= upper_high_boundary(), + "high address must be contained within the region"); + assert(lower_high_boundary() <= middle_high() && + middle_high() <= middle_high_boundary(), + "high address must be contained within the region"); + assert(low_boundary() <= lower_high() && + lower_high() <= lower_high_boundary(), + "high address must be contained within the region"); + + // Uncommit + if (upper_needs > 0) { + assert(middle_high_boundary() <= aligned_upper_new_high && + aligned_upper_new_high + upper_needs <= upper_high_boundary(), + "must not shrink beyond region"); + if (!os::uncommit_memory(aligned_upper_new_high, upper_needs)) { + debug_only(warning("os::uncommit_memory failed")); + return; + } else { + _upper_high -= upper_needs; + } + } + if (middle_needs > 0) { + assert(lower_high_boundary() <= aligned_middle_new_high && + aligned_middle_new_high + middle_needs <= middle_high_boundary(), + "must not shrink beyond region"); + if (!os::uncommit_memory(aligned_middle_new_high, middle_needs)) { + debug_only(warning("os::uncommit_memory failed")); + return; + } else { + _middle_high -= middle_needs; + } + } + if (lower_needs > 0) { + assert(low_boundary() <= aligned_lower_new_high && + aligned_lower_new_high + lower_needs <= lower_high_boundary(), + "must not shrink beyond region"); + if (!os::uncommit_memory(aligned_lower_new_high, lower_needs)) { + debug_only(warning("os::uncommit_memory failed")); + return; + } else { + _lower_high -= lower_needs; + } + } + + _high -= size; +} + +#ifndef PRODUCT +void VirtualSpace::check_for_contiguity() { + // Check contiguity. + assert(low_boundary() <= lower_high() && + lower_high() <= lower_high_boundary(), + "high address must be contained within the region"); + assert(lower_high_boundary() <= middle_high() && + middle_high() <= middle_high_boundary(), + "high address must be contained within the region"); + assert(middle_high_boundary() <= upper_high() && + upper_high() <= upper_high_boundary(), + "high address must be contained within the region"); + assert(low() >= low_boundary(), "low"); + assert(low_boundary() <= lower_high_boundary(), "lower high boundary"); + assert(upper_high_boundary() <= high_boundary(), "upper high boundary"); + assert(high() <= upper_high(), "upper high"); +} + +void VirtualSpace::print() { + tty->print ("Virtual space:"); + if (special()) tty->print(" (pinned in memory)"); + tty->cr(); + tty->print_cr(" - committed: %ld", committed_size()); + tty->print_cr(" - reserved: %ld", reserved_size()); + tty->print_cr(" - [low, high]: [" INTPTR_FORMAT ", " INTPTR_FORMAT "]", low(), high()); + tty->print_cr(" - [low_b, high_b]: [" INTPTR_FORMAT ", " INTPTR_FORMAT "]", low_boundary(), high_boundary()); +} + +#endif diff --git a/hotspot/src/share/vm/runtime/virtualspace.hpp b/hotspot/src/share/vm/runtime/virtualspace.hpp new file mode 100644 index 00000000000..ad952e49a3d --- /dev/null +++ b/hotspot/src/share/vm/runtime/virtualspace.hpp @@ -0,0 +1,190 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ReservedSpace is a data structure for reserving a contiguous address range. + +class ReservedSpace VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + char* _base; + size_t _size; + size_t _alignment; + bool _special; + + // ReservedSpace + ReservedSpace(char* base, size_t size, size_t alignment, bool special); + void initialize(size_t size, size_t alignment, bool large, + char* requested_address = NULL); + + // Release parts of an already-reserved memory region [addr, addr + len) to + // get a new region that has "compound alignment." Return the start of the + // resulting region, or NULL on failure. + // + // The region is logically divided into a prefix and a suffix. The prefix + // starts at the result address, which is aligned to prefix_align. The suffix + // starts at result address + prefix_size, which is aligned to suffix_align. + // The total size of the result region is size prefix_size + suffix_size. + char* align_reserved_region(char* addr, const size_t len, + const size_t prefix_size, + const size_t prefix_align, + const size_t suffix_size, + const size_t suffix_align); + + // Reserve memory, call align_reserved_region() to alignment it and return the + // result. + char* reserve_and_align(const size_t reserve_size, + const size_t prefix_size, + const size_t prefix_align, + const size_t suffix_size, + const size_t suffix_align); + + public: + // Constructor + ReservedSpace(size_t size); + ReservedSpace(size_t size, size_t alignment, bool large, + char* requested_address = NULL); + ReservedSpace(const size_t prefix_size, const size_t prefix_align, + const size_t suffix_size, const size_t suffix_align); + + // Accessors + char* base() const { return _base; } + size_t size() const { return _size; } + size_t alignment() const { return _alignment; } + bool special() const { return _special; } + + bool is_reserved() const { return _base != NULL; } + void release(); + + // Splitting + ReservedSpace first_part(size_t partition_size, size_t alignment, + bool split = false, bool realloc = true); + ReservedSpace last_part (size_t partition_size, size_t alignment); + + // These simply call the above using the default alignment. + inline ReservedSpace first_part(size_t partition_size, + bool split = false, bool realloc = true); + inline ReservedSpace last_part (size_t partition_size); + + // Alignment + static size_t page_align_size_up(size_t size); + static size_t page_align_size_down(size_t size); + static size_t allocation_align_size_up(size_t size); + static size_t allocation_align_size_down(size_t size); +}; + +ReservedSpace +ReservedSpace::first_part(size_t partition_size, bool split, bool realloc) +{ + return first_part(partition_size, alignment(), split, realloc); +} + +ReservedSpace ReservedSpace::last_part(size_t partition_size) +{ + return last_part(partition_size, alignment()); +} + +// VirtualSpace is data structure for committing a previously reserved address range in smaller chunks. + +class VirtualSpace VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + // Reserved area + char* _low_boundary; + char* _high_boundary; + + // Committed area + char* _low; + char* _high; + + // The entire space has been committed and pinned in memory, no + // os::commit_memory() or os::uncommit_memory(). + bool _special; + + // MPSS Support + // Each virtualspace region has a lower, middle, and upper region. + // Each region has an end boundary and a high pointer which is the + // high water mark for the last allocated byte. + // The lower and upper unaligned to LargePageSizeInBytes uses default page. + // size. The middle region uses large page size. + char* _lower_high; + char* _middle_high; + char* _upper_high; + + char* _lower_high_boundary; + char* _middle_high_boundary; + char* _upper_high_boundary; + + size_t _lower_alignment; + size_t _middle_alignment; + size_t _upper_alignment; + + // MPSS Accessors + char* lower_high() const { return _lower_high; } + char* middle_high() const { return _middle_high; } + char* upper_high() const { return _upper_high; } + + char* lower_high_boundary() const { return _lower_high_boundary; } + char* middle_high_boundary() const { return _middle_high_boundary; } + char* upper_high_boundary() const { return _upper_high_boundary; } + + size_t lower_alignment() const { return _lower_alignment; } + size_t middle_alignment() const { return _middle_alignment; } + size_t upper_alignment() const { return _upper_alignment; } + + public: + // Committed area + char* low() const { return _low; } + char* high() const { return _high; } + + // Reserved area + char* low_boundary() const { return _low_boundary; } + char* high_boundary() const { return _high_boundary; } + + bool special() const { return _special; } + + public: + // Initialization + VirtualSpace(); + bool initialize(ReservedSpace rs, size_t committed_byte_size); + + // Destruction + ~VirtualSpace(); + + // Testers (all sizes are byte sizes) + size_t committed_size() const; + size_t reserved_size() const; + size_t uncommitted_size() const; + bool contains(const void* p) const; + + // Operations + // returns true on success, false otherwise + bool expand_by(size_t bytes, bool pre_touch = false); + void shrink_by(size_t bytes); + void release(); + + void check_for_contiguity() PRODUCT_RETURN; + + // Debugging + void print() PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp new file mode 100644 index 00000000000..fbc42430386 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -0,0 +1,2277 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vmStructs.cpp.incl" + +// Note: the cross-product of (c1, c2, product, nonproduct, ...), +// (nonstatic, static), and (unchecked, checked) has not been taken. +// Only the macros currently needed have been defined. + +// A field whose type is not checked is given a null string as the +// type name, indicating an "opaque" type to the serviceability agent. + +// NOTE: there is an interdependency between this file and +// HotSpotTypeDataBase.java, which parses the type strings. + +#ifndef REG_COUNT + #define REG_COUNT 0 +#endif + +// whole purpose of this function is to work around bug c++/27724 in gcc 4.1.1 +// with optimization turned on it doesn't affect produced code +static inline uint64_t cast_uint64_t(size_t x) +{ + return x; +} + + +//-------------------------------------------------------------------------------- +// VM_STRUCTS +// +// This list enumerates all of the fields the serviceability agent +// needs to know about. Be sure to see also the type table below this one. +// NOTE that there are platform-specific additions to this table in +// vmStructs__.hpp. + +#define VM_STRUCTS(nonstatic_field, \ + static_field, \ + unchecked_nonstatic_field, \ + volatile_nonstatic_field, \ + nonproduct_nonstatic_field, \ + c1_nonstatic_field, \ + c2_nonstatic_field, \ + unchecked_c1_static_field, \ + unchecked_c2_static_field, \ + last_entry) \ + \ + /******************************************************************/ \ + /* OopDesc and Klass hierarchies (NOTE: methodDataOop incomplete) */ \ + /******************************************************************/ \ + \ + volatile_nonstatic_field(oopDesc, _mark, markOop) \ + nonstatic_field(oopDesc, _klass, klassOop) \ + static_field(oopDesc, _bs, BarrierSet*) \ + nonstatic_field(arrayKlass, _dimension, int) \ + nonstatic_field(arrayKlass, _higher_dimension, klassOop) \ + nonstatic_field(arrayKlass, _lower_dimension, klassOop) \ + nonstatic_field(arrayKlass, _vtable_len, int) \ + nonstatic_field(arrayKlass, _alloc_size, juint) \ + nonstatic_field(arrayKlass, _component_mirror, oop) \ + nonstatic_field(arrayOopDesc, _length, int) \ + nonstatic_field(compiledICHolderKlass, _alloc_size, juint) \ + nonstatic_field(compiledICHolderOopDesc, _holder_method, methodOop) \ + nonstatic_field(compiledICHolderOopDesc, _holder_klass, klassOop) \ + nonstatic_field(constantPoolOopDesc, _tags, typeArrayOop) \ + nonstatic_field(constantPoolOopDesc, _cache, constantPoolCacheOop) \ + nonstatic_field(constantPoolOopDesc, _pool_holder, klassOop) \ + nonstatic_field(constantPoolCacheOopDesc, _constant_pool, constantPoolOop) \ + nonstatic_field(instanceKlass, _array_klasses, klassOop) \ + nonstatic_field(instanceKlass, _methods, objArrayOop) \ + nonstatic_field(instanceKlass, _method_ordering, typeArrayOop) \ + nonstatic_field(instanceKlass, _local_interfaces, objArrayOop) \ + nonstatic_field(instanceKlass, _transitive_interfaces, objArrayOop) \ + nonstatic_field(instanceKlass, _nof_implementors, int) \ + nonstatic_field(instanceKlass, _implementors[0], klassOop) \ + nonstatic_field(instanceKlass, _fields, typeArrayOop) \ + nonstatic_field(instanceKlass, _constants, constantPoolOop) \ + nonstatic_field(instanceKlass, _class_loader, oop) \ + nonstatic_field(instanceKlass, _protection_domain, oop) \ + nonstatic_field(instanceKlass, _signers, objArrayOop) \ + nonstatic_field(instanceKlass, _source_file_name, symbolOop) \ + nonstatic_field(instanceKlass, _source_debug_extension, symbolOop) \ + nonstatic_field(instanceKlass, _inner_classes, typeArrayOop) \ + nonstatic_field(instanceKlass, _nonstatic_field_size, int) \ + nonstatic_field(instanceKlass, _static_field_size, int) \ + nonstatic_field(instanceKlass, _static_oop_field_size, int) \ + nonstatic_field(instanceKlass, _nonstatic_oop_map_size, int) \ + nonstatic_field(instanceKlass, _is_marked_dependent, bool) \ + nonstatic_field(instanceKlass, _minor_version, u2) \ + nonstatic_field(instanceKlass, _major_version, u2) \ + nonstatic_field(instanceKlass, _init_state, instanceKlass::ClassState) \ + nonstatic_field(instanceKlass, _init_thread, Thread*) \ + nonstatic_field(instanceKlass, _vtable_len, int) \ + nonstatic_field(instanceKlass, _itable_len, int) \ + nonstatic_field(instanceKlass, _reference_type, ReferenceType) \ + volatile_nonstatic_field(instanceKlass, _oop_map_cache, OopMapCache*) \ + nonstatic_field(instanceKlass, _jni_ids, JNIid*) \ + nonstatic_field(instanceKlass, _osr_nmethods_head, nmethod*) \ + nonstatic_field(instanceKlass, _breakpoints, BreakpointInfo*) \ + nonstatic_field(instanceKlass, _generic_signature, symbolOop) \ + nonstatic_field(instanceKlass, _methods_jmethod_ids, jmethodID*) \ + nonstatic_field(instanceKlass, _methods_cached_itable_indices, int*) \ + volatile_nonstatic_field(instanceKlass, _idnum_allocated_count, u2) \ + nonstatic_field(instanceKlass, _class_annotations, typeArrayOop) \ + nonstatic_field(instanceKlass, _fields_annotations, objArrayOop) \ + nonstatic_field(instanceKlass, _methods_annotations, objArrayOop) \ + nonstatic_field(instanceKlass, _methods_parameter_annotations, objArrayOop) \ + nonstatic_field(instanceKlass, _methods_default_annotations, objArrayOop) \ + nonstatic_field(Klass, _super_check_offset, juint) \ + nonstatic_field(Klass, _secondary_super_cache, klassOop) \ + nonstatic_field(Klass, _secondary_supers, objArrayOop) \ + nonstatic_field(Klass, _primary_supers[0], klassOop) \ + nonstatic_field(Klass, _java_mirror, oop) \ + nonstatic_field(Klass, _modifier_flags, jint) \ + nonstatic_field(Klass, _super, klassOop) \ + nonstatic_field(Klass, _layout_helper, jint) \ + nonstatic_field(Klass, _name, symbolOop) \ + nonstatic_field(Klass, _access_flags, AccessFlags) \ + nonstatic_field(Klass, _subklass, klassOop) \ + nonstatic_field(Klass, _next_sibling, klassOop) \ + nonproduct_nonstatic_field(Klass, _verify_count, int) \ + nonstatic_field(Klass, _alloc_count, juint) \ + nonstatic_field(klassKlass, _alloc_size, juint) \ + nonstatic_field(methodKlass, _alloc_size, juint) \ + nonstatic_field(methodDataOopDesc, _size, int) \ + nonstatic_field(methodDataOopDesc, _method, methodOop) \ + nonstatic_field(methodOopDesc, _constMethod, constMethodOop) \ + nonstatic_field(methodOopDesc, _constants, constantPoolOop) \ + c2_nonstatic_field(methodOopDesc, _method_data, methodDataOop) \ + c2_nonstatic_field(methodOopDesc, _interpreter_invocation_count, int) \ + nonstatic_field(methodOopDesc, _access_flags, AccessFlags) \ + nonstatic_field(methodOopDesc, _vtable_index, int) \ + nonstatic_field(methodOopDesc, _method_size, u2) \ + nonstatic_field(methodOopDesc, _max_stack, u2) \ + nonstatic_field(methodOopDesc, _max_locals, u2) \ + nonstatic_field(methodOopDesc, _size_of_parameters, u2) \ + c2_nonstatic_field(methodOopDesc, _interpreter_throwout_count, u2) \ + nonstatic_field(methodOopDesc, _number_of_breakpoints, u2) \ + nonstatic_field(methodOopDesc, _invocation_counter, InvocationCounter) \ + nonstatic_field(methodOopDesc, _backedge_counter, InvocationCounter) \ + nonproduct_nonstatic_field(methodOopDesc, _compiled_invocation_count, int) \ + volatile_nonstatic_field(methodOopDesc, _code, nmethod*) \ + nonstatic_field(methodOopDesc, _i2i_entry, address) \ + nonstatic_field(methodOopDesc, _adapter, AdapterHandlerEntry*) \ + volatile_nonstatic_field(methodOopDesc, _from_compiled_entry, address) \ + volatile_nonstatic_field(methodOopDesc, _from_interpreted_entry, address) \ + volatile_nonstatic_field(constMethodOopDesc, _fingerprint, uint64_t) \ + nonstatic_field(constMethodOopDesc, _method, methodOop) \ + nonstatic_field(constMethodOopDesc, _stackmap_data, typeArrayOop) \ + nonstatic_field(constMethodOopDesc, _exception_table, typeArrayOop) \ + nonstatic_field(constMethodOopDesc, _constMethod_size, int) \ + nonstatic_field(constMethodOopDesc, _interpreter_kind, jbyte) \ + nonstatic_field(constMethodOopDesc, _flags, jbyte) \ + nonstatic_field(constMethodOopDesc, _code_size, u2) \ + nonstatic_field(constMethodOopDesc, _name_index, u2) \ + nonstatic_field(constMethodOopDesc, _signature_index, u2) \ + nonstatic_field(constMethodOopDesc, _method_idnum, u2) \ + nonstatic_field(constMethodOopDesc, _generic_signature_index, u2) \ + nonstatic_field(objArrayKlass, _element_klass, klassOop) \ + nonstatic_field(objArrayKlass, _bottom_klass, klassOop) \ + nonstatic_field(symbolKlass, _alloc_size, juint) \ + nonstatic_field(symbolOopDesc, _length, unsigned short) \ + unchecked_nonstatic_field(symbolOopDesc, _body, sizeof(jbyte)) /* NOTE: no type */ \ + nonstatic_field(typeArrayKlass, _max_length, int) \ + \ + /***********************/ \ + /* Constant Pool Cache */ \ + /***********************/ \ + \ + volatile_nonstatic_field(ConstantPoolCacheEntry, _indices, intx) \ + volatile_nonstatic_field(ConstantPoolCacheEntry, _f1, oop) \ + volatile_nonstatic_field(ConstantPoolCacheEntry, _f2, intx) \ + volatile_nonstatic_field(ConstantPoolCacheEntry, _flags, intx) \ + \ + /********************************/ \ + /* MethodOop-related structures */ \ + /********************************/ \ + \ + nonstatic_field(CheckedExceptionElement, class_cp_index, u2) \ + nonstatic_field(LocalVariableTableElement, start_bci, u2) \ + nonstatic_field(LocalVariableTableElement, length, u2) \ + nonstatic_field(LocalVariableTableElement, name_cp_index, u2) \ + nonstatic_field(LocalVariableTableElement, descriptor_cp_index, u2) \ + nonstatic_field(LocalVariableTableElement, signature_cp_index, u2) \ + nonstatic_field(LocalVariableTableElement, slot, u2) \ + nonstatic_field(BreakpointInfo, _orig_bytecode, Bytecodes::Code) \ + nonstatic_field(BreakpointInfo, _bci, int) \ + nonstatic_field(BreakpointInfo, _name_index, u2) \ + nonstatic_field(BreakpointInfo, _signature_index, u2) \ + nonstatic_field(BreakpointInfo, _next, BreakpointInfo*) \ + /***********/ \ + /* JNI IDs */ \ + /***********/ \ + \ + nonstatic_field(JNIid, _holder, klassOop) \ + nonstatic_field(JNIid, _next, JNIid*) \ + nonstatic_field(JNIid, _offset, int) \ + /************/ \ + /* Universe */ \ + /************/ \ + \ + static_field(Universe, _boolArrayKlassObj, klassOop) \ + static_field(Universe, _byteArrayKlassObj, klassOop) \ + static_field(Universe, _charArrayKlassObj, klassOop) \ + static_field(Universe, _intArrayKlassObj, klassOop) \ + static_field(Universe, _shortArrayKlassObj, klassOop) \ + static_field(Universe, _longArrayKlassObj, klassOop) \ + static_field(Universe, _singleArrayKlassObj, klassOop) \ + static_field(Universe, _doubleArrayKlassObj, klassOop) \ + static_field(Universe, _symbolKlassObj, klassOop) \ + static_field(Universe, _methodKlassObj, klassOop) \ + static_field(Universe, _constMethodKlassObj, klassOop) \ + static_field(Universe, _methodDataKlassObj, klassOop) \ + static_field(Universe, _klassKlassObj, klassOop) \ + static_field(Universe, _arrayKlassKlassObj, klassOop) \ + static_field(Universe, _objArrayKlassKlassObj, klassOop) \ + static_field(Universe, _typeArrayKlassKlassObj, klassOop) \ + static_field(Universe, _instanceKlassKlassObj, klassOop) \ + static_field(Universe, _constantPoolKlassObj, klassOop) \ + static_field(Universe, _constantPoolCacheKlassObj, klassOop) \ + static_field(Universe, _compiledICHolderKlassObj, klassOop) \ + static_field(Universe, _systemObjArrayKlassObj, klassOop) \ + static_field(Universe, _mirrors[0], oop) \ + static_field(Universe, _main_thread_group, oop) \ + static_field(Universe, _system_thread_group, oop) \ + static_field(Universe, _the_empty_byte_array, typeArrayOop) \ + static_field(Universe, _the_empty_short_array, typeArrayOop) \ + static_field(Universe, _the_empty_int_array, typeArrayOop) \ + static_field(Universe, _the_empty_system_obj_array, objArrayOop) \ + static_field(Universe, _the_empty_class_klass_array, objArrayOop) \ + static_field(Universe, _out_of_memory_error_java_heap, oop) \ + static_field(Universe, _out_of_memory_error_perm_gen, oop) \ + static_field(Universe, _out_of_memory_error_array_size, oop) \ + static_field(Universe, _out_of_memory_error_gc_overhead_limit, oop) \ + static_field(Universe, _null_ptr_exception_instance, oop) \ + static_field(Universe, _arithmetic_exception_instance, oop) \ + static_field(Universe, _vm_exception, oop) \ + static_field(Universe, _collectedHeap, CollectedHeap*) \ + static_field(Universe, _base_vtable_size, int) \ + static_field(Universe, _bootstrapping, bool) \ + static_field(Universe, _fully_initialized, bool) \ + static_field(Universe, _verify_count, int) \ + \ + /**********************************************************************************/ \ + /* Generation and Space hierarchies */ \ + /**********************************************************************************/ \ + \ + unchecked_nonstatic_field(ageTable, sizes, sizeof(ageTable::sizes)) \ + \ + nonstatic_field(BarrierSet, _max_covered_regions, int) \ + nonstatic_field(BlockOffsetTable, _bottom, HeapWord*) \ + nonstatic_field(BlockOffsetTable, _end, HeapWord*) \ + \ + nonstatic_field(BlockOffsetSharedArray, _reserved, MemRegion) \ + nonstatic_field(BlockOffsetSharedArray, _end, HeapWord*) \ + nonstatic_field(BlockOffsetSharedArray, _vs, VirtualSpace) \ + nonstatic_field(BlockOffsetSharedArray, _offset_array, u_char*) \ + \ + nonstatic_field(BlockOffsetArray, _array, BlockOffsetSharedArray*) \ + nonstatic_field(BlockOffsetArray, _sp, Space*) \ + nonstatic_field(BlockOffsetArrayContigSpace, _next_offset_threshold, HeapWord*) \ + nonstatic_field(BlockOffsetArrayContigSpace, _next_offset_index, size_t) \ + \ + nonstatic_field(BlockOffsetArrayNonContigSpace, _unallocated_block, HeapWord*) \ + \ + nonstatic_field(CardGeneration, _rs, GenRemSet*) \ + nonstatic_field(CardGeneration, _bts, BlockOffsetSharedArray*) \ + \ + nonstatic_field(CardTableModRefBS, _whole_heap, const MemRegion) \ + nonstatic_field(CardTableModRefBS, _guard_index, const size_t) \ + nonstatic_field(CardTableModRefBS, _last_valid_index, const size_t) \ + nonstatic_field(CardTableModRefBS, _page_size, const size_t) \ + nonstatic_field(CardTableModRefBS, _byte_map_size, const size_t) \ + nonstatic_field(CardTableModRefBS, _byte_map, jbyte*) \ + nonstatic_field(CardTableModRefBS, _cur_covered_regions, int) \ + nonstatic_field(CardTableModRefBS, _covered, MemRegion*) \ + nonstatic_field(CardTableModRefBS, _committed, MemRegion*) \ + nonstatic_field(CardTableModRefBS, _guard_region, MemRegion) \ + nonstatic_field(CardTableModRefBS, byte_map_base, jbyte*) \ + \ + nonstatic_field(CardTableRS, _ct_bs, CardTableModRefBS) \ + \ + nonstatic_field(CollectedHeap, _reserved, MemRegion) \ + nonstatic_field(SharedHeap, _perm_gen, PermGen*) \ + nonstatic_field(CollectedHeap, _barrier_set, BarrierSet*) \ + nonstatic_field(CollectedHeap, _is_gc_active, bool) \ + nonstatic_field(CollectedHeap, _max_heap_capacity, size_t) \ + \ + nonstatic_field(CompactibleSpace, _compaction_top, HeapWord*) \ + nonstatic_field(CompactibleSpace, _first_dead, HeapWord*) \ + nonstatic_field(CompactibleSpace, _end_of_live, HeapWord*) \ + \ + nonstatic_field(CompactingPermGen, _gen, OneContigSpaceCardGeneration*) \ + \ + nonstatic_field(ContiguousSpace, _top, HeapWord*) \ + nonstatic_field(ContiguousSpace, _concurrent_iteration_safe_limit, HeapWord*) \ + nonstatic_field(ContiguousSpace, _saved_mark_word, HeapWord*) \ + \ + nonstatic_field(DefNewGeneration, _next_gen, Generation*) \ + nonstatic_field(DefNewGeneration, _tenuring_threshold, int) \ + nonstatic_field(DefNewGeneration, _age_table, ageTable) \ + nonstatic_field(DefNewGeneration, _eden_space, EdenSpace*) \ + nonstatic_field(DefNewGeneration, _from_space, ContiguousSpace*) \ + nonstatic_field(DefNewGeneration, _to_space, ContiguousSpace*) \ + \ + nonstatic_field(EdenSpace, _gen, DefNewGeneration*) \ + \ + nonstatic_field(Generation, _reserved, MemRegion) \ + nonstatic_field(Generation, _virtual_space, VirtualSpace) \ + nonstatic_field(Generation, _level, int) \ + nonstatic_field(Generation, _stat_record, Generation::StatRecord) \ + \ + nonstatic_field(Generation::StatRecord, invocations, int) \ + nonstatic_field(Generation::StatRecord, accumulated_time, elapsedTimer) \ + \ + nonstatic_field(GenerationSpec, _name, Generation::Name) \ + nonstatic_field(GenerationSpec, _init_size, size_t) \ + nonstatic_field(GenerationSpec, _max_size, size_t) \ + \ + static_field(GenCollectedHeap, _gch, GenCollectedHeap*) \ + nonstatic_field(GenCollectedHeap, _n_gens, int) \ + unchecked_nonstatic_field(GenCollectedHeap, _gens, sizeof(GenCollectedHeap::_gens)) /* NOTE: no type */ \ + nonstatic_field(GenCollectedHeap, _gen_specs, GenerationSpec**) \ + \ + nonstatic_field(HeapWord, i, char*) \ + \ + nonstatic_field(MemRegion, _start, HeapWord*) \ + nonstatic_field(MemRegion, _word_size, size_t) \ + \ + nonstatic_field(OffsetTableContigSpace, _offsets, BlockOffsetArray) \ + \ + nonstatic_field(OneContigSpaceCardGeneration, _min_heap_delta_bytes, size_t) \ + nonstatic_field(OneContigSpaceCardGeneration, _the_space, ContiguousSpace*) \ + nonstatic_field(OneContigSpaceCardGeneration, _last_gc, WaterMark) \ + \ + nonstatic_field(CompactingPermGenGen, _ro_vs, VirtualSpace) \ + nonstatic_field(CompactingPermGenGen, _rw_vs, VirtualSpace) \ + nonstatic_field(CompactingPermGenGen, _md_vs, VirtualSpace) \ + nonstatic_field(CompactingPermGenGen, _mc_vs, VirtualSpace) \ + nonstatic_field(CompactingPermGenGen, _ro_space, OffsetTableContigSpace*) \ + nonstatic_field(CompactingPermGenGen, _rw_space, OffsetTableContigSpace*) \ + static_field(CompactingPermGenGen, unshared_bottom, HeapWord*) \ + static_field(CompactingPermGenGen, unshared_end, HeapWord*) \ + static_field(CompactingPermGenGen, shared_bottom, HeapWord*) \ + static_field(CompactingPermGenGen, readonly_bottom, HeapWord*) \ + static_field(CompactingPermGenGen, readonly_end, HeapWord*) \ + static_field(CompactingPermGenGen, readwrite_bottom, HeapWord*) \ + static_field(CompactingPermGenGen, readwrite_end, HeapWord*) \ + static_field(CompactingPermGenGen, miscdata_bottom, HeapWord*) \ + static_field(CompactingPermGenGen, miscdata_end, HeapWord*) \ + static_field(CompactingPermGenGen, misccode_bottom, HeapWord*) \ + static_field(CompactingPermGenGen, misccode_end, HeapWord*) \ + static_field(CompactingPermGenGen, shared_end, HeapWord*) \ + \ + nonstatic_field(PermGen, _capacity_expansion_limit, size_t) \ + \ + nonstatic_field(PermanentGenerationSpec, _name, PermGen::Name) \ + nonstatic_field(PermanentGenerationSpec, _init_size, size_t) \ + nonstatic_field(PermanentGenerationSpec, _max_size, size_t) \ + \ + nonstatic_field(Space, _bottom, HeapWord*) \ + nonstatic_field(Space, _end, HeapWord*) \ + \ + nonstatic_field(TenuredGeneration, _shrink_factor, size_t) \ + nonstatic_field(TenuredGeneration, _capacity_at_prologue, size_t) \ + nonstatic_field(ThreadLocalAllocBuffer, _start, HeapWord*) \ + nonstatic_field(ThreadLocalAllocBuffer, _top, HeapWord*) \ + nonstatic_field(ThreadLocalAllocBuffer, _end, HeapWord*) \ + nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ + nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ + static_field(ThreadLocalAllocBuffer, _target_refills, unsigned) \ + nonstatic_field(VirtualSpace, _low_boundary, char*) \ + nonstatic_field(VirtualSpace, _high_boundary, char*) \ + nonstatic_field(VirtualSpace, _low, char*) \ + nonstatic_field(VirtualSpace, _high, char*) \ + nonstatic_field(VirtualSpace, _lower_high, char*) \ + nonstatic_field(VirtualSpace, _middle_high, char*) \ + nonstatic_field(VirtualSpace, _upper_high, char*) \ + nonstatic_field(WaterMark, _point, HeapWord*) \ + nonstatic_field(WaterMark, _space, Space*) \ + \ + /************************/ \ + /* PerfMemory - jvmstat */ \ + /************************/ \ + \ + nonstatic_field(PerfDataPrologue, magic, jint) \ + nonstatic_field(PerfDataPrologue, byte_order, jbyte) \ + nonstatic_field(PerfDataPrologue, major_version, jbyte) \ + nonstatic_field(PerfDataPrologue, minor_version, jbyte) \ + nonstatic_field(PerfDataPrologue, accessible, jbyte) \ + nonstatic_field(PerfDataPrologue, used, jint) \ + nonstatic_field(PerfDataPrologue, overflow, jint) \ + nonstatic_field(PerfDataPrologue, mod_time_stamp, jlong) \ + nonstatic_field(PerfDataPrologue, entry_offset, jint) \ + nonstatic_field(PerfDataPrologue, num_entries, jint) \ + \ + nonstatic_field(PerfDataEntry, entry_length, jint) \ + nonstatic_field(PerfDataEntry, name_offset, jint) \ + nonstatic_field(PerfDataEntry, vector_length, jint) \ + nonstatic_field(PerfDataEntry, data_type, jbyte) \ + nonstatic_field(PerfDataEntry, flags, jbyte) \ + nonstatic_field(PerfDataEntry, data_units, jbyte) \ + nonstatic_field(PerfDataEntry, data_variability, jbyte) \ + nonstatic_field(PerfDataEntry, data_offset, jint) \ + \ + static_field(PerfMemory, _start, char*) \ + static_field(PerfMemory, _end, char*) \ + static_field(PerfMemory, _top, char*) \ + static_field(PerfMemory, _capacity, size_t) \ + static_field(PerfMemory, _prologue, PerfDataPrologue*) \ + static_field(PerfMemory, _initialized, jint) \ + \ + /***************/ \ + /* SymbolTable */ \ + /***************/ \ + \ + static_field(SymbolTable, _the_table, SymbolTable*) \ + \ + /***************/ \ + /* StringTable */ \ + /***************/ \ + \ + static_field(StringTable, _the_table, StringTable*) \ + \ + /********************/ \ + /* SystemDictionary */ \ + /********************/ \ + \ + static_field(SystemDictionary, _dictionary, Dictionary*) \ + static_field(SystemDictionary, _placeholders, PlaceholderTable*) \ + static_field(SystemDictionary, _shared_dictionary, Dictionary*) \ + static_field(SystemDictionary, _system_loader_lock_obj, oop) \ + static_field(SystemDictionary, _loader_constraints, LoaderConstraintTable*) \ + static_field(SystemDictionary, _object_klass, klassOop) \ + static_field(SystemDictionary, _string_klass, klassOop) \ + static_field(SystemDictionary, _class_klass, klassOop) \ + static_field(SystemDictionary, _cloneable_klass, klassOop) \ + static_field(SystemDictionary, _classloader_klass, klassOop) \ + static_field(SystemDictionary, _serializable_klass, klassOop) \ + static_field(SystemDictionary, _system_klass, klassOop) \ + static_field(SystemDictionary, _throwable_klass, klassOop) \ + static_field(SystemDictionary, _threaddeath_klass, klassOop) \ + static_field(SystemDictionary, _error_klass, klassOop) \ + static_field(SystemDictionary, _exception_klass, klassOop) \ + static_field(SystemDictionary, _runtime_exception_klass, klassOop) \ + static_field(SystemDictionary, _classNotFoundException_klass, klassOop) \ + static_field(SystemDictionary, _noClassDefFoundError_klass, klassOop) \ + static_field(SystemDictionary, _linkageError_klass, klassOop) \ + static_field(SystemDictionary, _classCastException_klass, klassOop) \ + static_field(SystemDictionary, _arrayStoreException_klass, klassOop) \ + static_field(SystemDictionary, _virtualMachineError_klass, klassOop) \ + static_field(SystemDictionary, _outOfMemoryError_klass, klassOop) \ + static_field(SystemDictionary, _StackOverflowError_klass, klassOop) \ + static_field(SystemDictionary, _protectionDomain_klass, klassOop) \ + static_field(SystemDictionary, _AccessControlContext_klass, klassOop) \ + static_field(SystemDictionary, _reference_klass, klassOop) \ + static_field(SystemDictionary, _soft_reference_klass, klassOop) \ + static_field(SystemDictionary, _weak_reference_klass, klassOop) \ + static_field(SystemDictionary, _final_reference_klass, klassOop) \ + static_field(SystemDictionary, _phantom_reference_klass, klassOop) \ + static_field(SystemDictionary, _finalizer_klass, klassOop) \ + static_field(SystemDictionary, _thread_klass, klassOop) \ + static_field(SystemDictionary, _threadGroup_klass, klassOop) \ + static_field(SystemDictionary, _properties_klass, klassOop) \ + static_field(SystemDictionary, _stringBuffer_klass, klassOop) \ + static_field(SystemDictionary, _vector_klass, klassOop) \ + static_field(SystemDictionary, _hashtable_klass, klassOop) \ + static_field(SystemDictionary, _box_klasses[0], klassOop) \ + static_field(SystemDictionary, _java_system_loader, oop) \ + \ + /*******************/ \ + /* HashtableBucket */ \ + /*******************/ \ + \ + nonstatic_field(HashtableBucket, _entry, BasicHashtableEntry*) \ + \ + /******************/ \ + /* HashtableEntry */ \ + /******************/ \ + \ + nonstatic_field(BasicHashtableEntry, _next, BasicHashtableEntry*) \ + nonstatic_field(BasicHashtableEntry, _hash, unsigned int) \ + nonstatic_field(HashtableEntry, _literal, oop) \ + \ + /*************/ \ + /* Hashtable */ \ + /*************/ \ + \ + nonstatic_field(BasicHashtable, _table_size, int) \ + nonstatic_field(BasicHashtable, _buckets, HashtableBucket*) \ + nonstatic_field(BasicHashtable, _free_list, BasicHashtableEntry*) \ + nonstatic_field(BasicHashtable, _first_free_entry, char*) \ + nonstatic_field(BasicHashtable, _end_block, char*) \ + nonstatic_field(BasicHashtable, _entry_size, int) \ + \ + /*******************/ \ + /* DictionaryEntry */ \ + /*******************/ \ + \ + nonstatic_field(DictionaryEntry, _loader, oop) \ + nonstatic_field(DictionaryEntry, _pd_set, ProtectionDomainEntry*) \ + \ + /********************/ \ + \ + nonstatic_field(PlaceholderEntry, _loader, oop) \ + \ + /**************************/ \ + /* ProctectionDomainEntry */ \ + /**************************/ \ + \ + nonstatic_field(ProtectionDomainEntry, _next, ProtectionDomainEntry*) \ + nonstatic_field(ProtectionDomainEntry, _protection_domain, oop) \ + \ + /*************************/ \ + /* LoaderConstraintEntry */ \ + /*************************/ \ + \ + nonstatic_field(LoaderConstraintEntry, _name, symbolOop) \ + nonstatic_field(LoaderConstraintEntry, _num_loaders, int) \ + nonstatic_field(LoaderConstraintEntry, _max_loaders, int) \ + nonstatic_field(LoaderConstraintEntry, _loaders, oop*) \ + \ + /********************************/ \ + /* CodeCache (NOTE: incomplete) */ \ + /********************************/ \ + \ + static_field(CodeCache, _heap, CodeHeap*) \ + \ + /*******************************/ \ + /* CodeHeap (NOTE: incomplete) */ \ + /*******************************/ \ + \ + nonstatic_field(CodeHeap, _memory, VirtualSpace) \ + nonstatic_field(CodeHeap, _segmap, VirtualSpace) \ + nonstatic_field(CodeHeap, _log2_segment_size, int) \ + nonstatic_field(HeapBlock, _header, HeapBlock::Header) \ + nonstatic_field(HeapBlock::Header, _length, size_t) \ + nonstatic_field(HeapBlock::Header, _used, bool) \ + \ + /**********************************/ \ + /* Interpreter (NOTE: incomplete) */ \ + /**********************************/ \ + \ + static_field(AbstractInterpreter, _code, StubQueue*) \ + \ + /****************************/ \ + /* Stubs (NOTE: incomplete) */ \ + /****************************/ \ + \ + nonstatic_field(StubQueue, _stub_buffer, address) \ + nonstatic_field(StubQueue, _buffer_limit, int) \ + nonstatic_field(StubQueue, _queue_begin, int) \ + nonstatic_field(StubQueue, _queue_end, int) \ + nonstatic_field(StubQueue, _number_of_stubs, int) \ + nonstatic_field(InterpreterCodelet, _size, int) \ + nonstatic_field(InterpreterCodelet, _description, const char*) \ + nonstatic_field(InterpreterCodelet, _bytecode, Bytecodes::Code) \ + \ + /***********************************/ \ + /* StubRoutines (NOTE: incomplete) */ \ + /***********************************/ \ + \ + static_field(StubRoutines, _call_stub_return_address, address) \ + IA32_ONLY(static_field(StubRoutines::i486,_call_stub_compiled_return, address)) \ + \ + /***************************************/ \ + /* PcDesc and other compiled code info */ \ + /***************************************/ \ + \ + nonstatic_field(PcDesc, _pc_offset, int) \ + nonstatic_field(PcDesc, _scope_decode_offset, int) \ + \ + /***************************************************/ \ + /* CodeBlobs (NOTE: incomplete, but only a little) */ \ + /***************************************************/ \ + \ + nonstatic_field(CodeBlob, _name, const char*) \ + nonstatic_field(CodeBlob, _size, int) \ + nonstatic_field(CodeBlob, _header_size, int) \ + nonstatic_field(CodeBlob, _relocation_size, int) \ + nonstatic_field(CodeBlob, _instructions_offset, int) \ + nonstatic_field(CodeBlob, _frame_complete_offset, int) \ + nonstatic_field(CodeBlob, _data_offset, int) \ + nonstatic_field(CodeBlob, _oops_offset, int) \ + nonstatic_field(CodeBlob, _oops_length, int) \ + nonstatic_field(CodeBlob, _frame_size, int) \ + nonstatic_field(CodeBlob, _oop_maps, OopMapSet*) \ + \ + /**************************************************/ \ + /* NMethods (NOTE: incomplete, but only a little) */ \ + /**************************************************/ \ + \ + static_field(nmethod, _zombie_instruction_size, int) \ + nonstatic_field(nmethod, _method, methodOop) \ + nonstatic_field(nmethod, _entry_bci, int) \ + nonstatic_field(nmethod, _link, nmethod*) \ + nonstatic_field(nmethod, _exception_offset, int) \ + nonstatic_field(nmethod, _deoptimize_offset, int) \ + nonstatic_field(nmethod, _orig_pc_offset, int) \ + nonstatic_field(nmethod, _stub_offset, int) \ + nonstatic_field(nmethod, _scopes_data_offset, int) \ + nonstatic_field(nmethod, _scopes_pcs_offset, int) \ + nonstatic_field(nmethod, _dependencies_offset, int) \ + nonstatic_field(nmethod, _handler_table_offset, int) \ + nonstatic_field(nmethod, _nul_chk_table_offset, int) \ + nonstatic_field(nmethod, _nmethod_end_offset, int) \ + nonstatic_field(nmethod, _entry_point, address) \ + nonstatic_field(nmethod, _verified_entry_point, address) \ + nonstatic_field(nmethod, _osr_entry_point, address) \ + nonstatic_field(nmethod, _lock_count, jint) \ + nonstatic_field(nmethod, _stack_traversal_mark, long) \ + \ + /********************************/ \ + /* JavaCalls (NOTE: incomplete) */ \ + /********************************/ \ + \ + nonstatic_field(JavaCallWrapper, _anchor, JavaFrameAnchor) \ + /********************************/ \ + /* JavaFrameAnchor (NOTE: incomplete) */ \ + /********************************/ \ + volatile_nonstatic_field(JavaFrameAnchor, _last_Java_sp, intptr_t*) \ + volatile_nonstatic_field(JavaFrameAnchor, _last_Java_pc, address) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + \ + static_field(Threads, _thread_list, JavaThread*) \ + static_field(Threads, _number_of_threads, int) \ + static_field(Threads, _number_of_non_daemon_threads, int) \ + static_field(Threads, _return_code, int) \ + \ + volatile_nonstatic_field(Thread, _suspend_flags, uint32_t) \ + nonstatic_field(Thread, _active_handles, JNIHandleBlock*) \ + nonstatic_field(Thread, _highest_lock, address) \ + nonstatic_field(Thread, _tlab, ThreadLocalAllocBuffer) \ + nonstatic_field(Thread, _current_pending_monitor, ObjectMonitor*) \ + nonstatic_field(Thread, _current_pending_monitor_is_from_java, bool) \ + nonstatic_field(Thread, _current_waiting_monitor, ObjectMonitor*) \ + nonstatic_field(NamedThread, _name, char*) \ + nonstatic_field(JavaThread, _next, JavaThread*) \ + nonstatic_field(JavaThread, _threadObj, oop) \ + nonstatic_field(JavaThread, _anchor, JavaFrameAnchor) \ + volatile_nonstatic_field(JavaThread, _thread_state, JavaThreadState) \ + nonstatic_field(JavaThread, _osthread, OSThread*) \ + nonstatic_field(JavaThread, _stack_base, address) \ + nonstatic_field(JavaThread, _stack_size, size_t) \ + \ + /************/ \ + /* OSThread */ \ + /************/ \ + \ + nonstatic_field(OSThread, _interrupted, jint) \ + \ + /************************/ \ + /* OopMap and OopMapSet */ \ + /************************/ \ + \ + nonstatic_field(OopMap, _pc_offset, int) \ + nonstatic_field(OopMap, _omv_count, int) \ + nonstatic_field(OopMap, _omv_data_size, int) \ + nonstatic_field(OopMap, _omv_data, unsigned char*) \ + nonstatic_field(OopMap, _write_stream, CompressedWriteStream*) \ + nonstatic_field(OopMapSet, _om_count, int) \ + nonstatic_field(OopMapSet, _om_size, int) \ + nonstatic_field(OopMapSet, _om_data, OopMap**) \ + \ + /*********************************/ \ + /* JNIHandles and JNIHandleBlock */ \ + /*********************************/ \ + static_field(JNIHandles, _global_handles, JNIHandleBlock*) \ + static_field(JNIHandles, _weak_global_handles, JNIHandleBlock*) \ + static_field(JNIHandles, _deleted_handle, oop) \ + \ + unchecked_nonstatic_field(JNIHandleBlock, _handles, JNIHandleBlock::block_size_in_oops * sizeof(Oop)) /* Note: no type */ \ + nonstatic_field(JNIHandleBlock, _top, int) \ + nonstatic_field(JNIHandleBlock, _next, JNIHandleBlock*) \ + \ + /********************/ \ + /* CompressedStream */ \ + /********************/ \ + \ + nonstatic_field(CompressedStream, _buffer, u_char*) \ + nonstatic_field(CompressedStream, _position, int) \ + \ + /*********************************/ \ + /* VMRegImpl (NOTE: incomplete) */ \ + /*********************************/ \ + \ + static_field(VMRegImpl, regName[0], const char*) \ + static_field(VMRegImpl, stack0, VMReg) \ + \ + /*******************************/ \ + /* Runtime1 (NOTE: incomplete) */ \ + /*******************************/ \ + \ + unchecked_c1_static_field(Runtime1, _blobs, sizeof(Runtime1::_blobs)) /* NOTE: no type */ \ + \ + /************/ \ + /* Monitors */ \ + /************/ \ + \ + volatile_nonstatic_field(ObjectMonitor, _header, markOop) \ + unchecked_nonstatic_field(ObjectMonitor, _object, sizeof(void *)) /* NOTE: no type */ \ + unchecked_nonstatic_field(ObjectMonitor, _owner, sizeof(void *)) /* NOTE: no type */ \ + volatile_nonstatic_field(ObjectMonitor, _count, intptr_t) \ + volatile_nonstatic_field(ObjectMonitor, _waiters, intptr_t) \ + volatile_nonstatic_field(ObjectMonitor, _recursions, intptr_t) \ + nonstatic_field(ObjectMonitor, FreeNext, ObjectMonitor*) \ + volatile_nonstatic_field(BasicLock, _displaced_header, markOop) \ + nonstatic_field(BasicObjectLock, _lock, BasicLock) \ + nonstatic_field(BasicObjectLock, _obj, oop) \ + static_field(ObjectSynchronizer, gBlockList, ObjectMonitor*) \ + \ + /*********************/ \ + /* Matcher (C2 only) */ \ + /*********************/ \ + \ + unchecked_c2_static_field(Matcher, _regEncode, sizeof(Matcher::_regEncode)) /* NOTE: no type */ \ + \ + /*********************/ \ + /* -XX flags */ \ + /*********************/ \ + \ + nonstatic_field(Flag, type, const char*) \ + nonstatic_field(Flag, name, const char*) \ + unchecked_nonstatic_field(Flag, addr, sizeof(void*)) /* NOTE: no type */ \ + nonstatic_field(Flag, kind, const char*) \ + static_field(Flag, flags, Flag*) \ + static_field(Flag, numFlags, size_t) \ + \ + /*************************/ \ + /* JDK / VM version info */ \ + /*************************/ \ + \ + static_field(Abstract_VM_Version, _s_vm_release, const char*) \ + static_field(Abstract_VM_Version, _s_internal_vm_info_string, const char*) \ + static_field(Abstract_VM_Version, _vm_major_version, int) \ + static_field(Abstract_VM_Version, _vm_minor_version, int) \ + static_field(Abstract_VM_Version, _vm_build_number, int) \ + \ + static_field(JDK_Version, _pre_jdk16_version, bool) \ + static_field(JDK_Version, _jdk_version, int) \ + \ + \ + \ + /*************/ \ + /* Arguments */ \ + /*************/ \ + \ + static_field(Arguments, _jvm_flags_array, char**) \ + static_field(Arguments, _num_jvm_flags, int) \ + static_field(Arguments, _jvm_args_array, char**) \ + static_field(Arguments, _num_jvm_args, int) \ + static_field(Arguments, _java_command, char*) \ + \ + \ + /************************/ \ + /* Miscellaneous fields */ \ + /************************/ \ + \ + nonstatic_field(AccessFlags, _flags, jint) \ + nonstatic_field(elapsedTimer, _counter, jlong) \ + nonstatic_field(elapsedTimer, _active, bool) \ + nonstatic_field(InvocationCounter, _counter, unsigned int) + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_STRUCTS_OS_CPU macro (and must */ + /* be present there) */ + +//-------------------------------------------------------------------------------- +// VM_TYPES +// +// This list must enumerate at least all of the types in the above +// list. For the types in the above list, the entry below must have +// exactly the same spacing since string comparisons are done in the +// code which verifies the consistency of these tables (in the debug +// build). +// +// In addition to the above types, this list is required to enumerate +// the JNI's java types, which are used to indicate the size of Java +// fields in this VM to the SA. Further, oop types are currently +// distinguished by name (i.e., ends with "oop") over in the SA. +// +// The declare_toplevel_type macro should be used to declare types +// which do not have a superclass. +// +// The declare_integer_type and declare_unsigned_integer_type macros +// are required in order to properly identify C integer types over in +// the SA. They should be used for any type which is otherwise opaque +// and which it is necessary to coerce into an integer value. This +// includes, for example, the type uintptr_t. Note that while they +// will properly identify the type's size regardless of the platform, +// since it is does not seem possible to deduce or check signedness at +// compile time using the pointer comparison tricks, it is currently +// required that the given types have the same signedness across all +// platforms. +// +// NOTE that there are platform-specific additions to this table in +// vmStructs__.hpp. + +#define VM_TYPES(declare_type, \ + declare_toplevel_type, \ + declare_oop_type, \ + declare_integer_type, \ + declare_unsigned_integer_type, \ + declare_c1_toplevel_type, \ + declare_c2_type, \ + declare_c2_toplevel_type, \ + last_entry) \ + \ + /*************************************************************/ \ + /* Java primitive types -- required by the SA implementation */ \ + /* in order to determine the size of Java fields in this VM */ \ + /* (the implementation looks up these names specifically) */ \ + /* NOTE: since we fetch these sizes from the remote VM, we */ \ + /* have a bootstrapping sequence during which it is not */ \ + /* valid to fetch Java values from the remote process, only */ \ + /* C integer values (of known size). NOTE also that we do */ \ + /* NOT include "Java unsigned" types like juint here; since */ \ + /* Java does not have unsigned primitive types, those can */ \ + /* not be mapped directly and are considered to be C integer */ \ + /* types in this system (see the "other types" section, */ \ + /* below.) */ \ + /*************************************************************/ \ + \ + declare_toplevel_type(jboolean) \ + declare_toplevel_type(jbyte) \ + declare_toplevel_type(jchar) \ + declare_toplevel_type(jdouble) \ + declare_toplevel_type(jfloat) \ + declare_toplevel_type(jint) \ + declare_toplevel_type(jlong) \ + declare_toplevel_type(jshort) \ + \ + /*********************************************************************/ \ + /* C integer types. User-defined typedefs (like "size_t" or */ \ + /* "intptr_t") are guaranteed to be present with the same names over */ \ + /* in the SA's type database. Names like "unsigned short" are not */ \ + /* guaranteed to be visible through the SA's type database lookup */ \ + /* mechanism, though they will have a Type object created for them */ \ + /* and are valid types for Fields. */ \ + /*********************************************************************/ \ + declare_integer_type(bool) \ + declare_integer_type(int) \ + declare_integer_type(long) \ + declare_integer_type(char) \ + declare_unsigned_integer_type(unsigned char) \ + declare_unsigned_integer_type(unsigned int) \ + declare_unsigned_integer_type(unsigned short) \ + declare_unsigned_integer_type(unsigned long) \ + /* The compiler thinks this is a different type than */ \ + /* unsigned short on Win32 */ \ + declare_unsigned_integer_type(u2) \ + declare_unsigned_integer_type(unsigned) \ + \ + /*****************************/ \ + /* C primitive pointer types */ \ + /*****************************/ \ + \ + declare_toplevel_type(int*) \ + declare_toplevel_type(char*) \ + declare_toplevel_type(char**) \ + declare_toplevel_type(const char*) \ + declare_toplevel_type(u_char*) \ + declare_toplevel_type(unsigned char*) \ + \ + /*******************************************************************/ \ + /* Types which it will be handy to have available over in the SA */ \ + /* in order to do platform-independent address -> integer coercion */ \ + /* (note: these will be looked up by name) */ \ + /*******************************************************************/ \ + \ + declare_unsigned_integer_type(size_t) \ + declare_unsigned_integer_type(const size_t) \ + declare_integer_type(intx) \ + declare_integer_type(intptr_t) \ + declare_unsigned_integer_type(uintx) \ + declare_unsigned_integer_type(uintptr_t) \ + declare_unsigned_integer_type(uint32_t) \ + declare_unsigned_integer_type(uint64_t) \ + declare_integer_type(const int) \ + \ + /*******************************************************************************/ \ + /* OopDesc and Klass hierarchies (NOTE: missing methodDataOop-related classes) */ \ + /*******************************************************************************/ \ + \ + declare_toplevel_type(oopDesc) \ + declare_toplevel_type(Klass_vtbl) \ + declare_type(Klass, Klass_vtbl) \ + declare_type(arrayKlass, Klass) \ + declare_type(arrayKlassKlass, klassKlass) \ + declare_type(arrayOopDesc, oopDesc) \ + declare_type(compiledICHolderKlass, Klass) \ + declare_type(compiledICHolderOopDesc, oopDesc) \ + declare_type(constantPoolKlass, arrayKlass) \ + declare_type(constantPoolOopDesc, arrayOopDesc) \ + declare_type(constantPoolCacheKlass, arrayKlass) \ + declare_type(constantPoolCacheOopDesc, arrayOopDesc) \ + declare_type(instanceKlass, Klass) \ + declare_type(instanceKlassKlass, klassKlass) \ + declare_type(instanceOopDesc, oopDesc) \ + declare_type(instanceRefKlass, instanceKlass) \ + declare_type(klassKlass, Klass) \ + declare_type(klassOopDesc, oopDesc) \ + declare_type(markOopDesc, oopDesc) \ + declare_type(methodDataKlass, Klass) \ + declare_type(methodDataOopDesc, oopDesc) \ + declare_type(methodKlass, Klass) \ + declare_type(constMethodKlass, Klass) \ + declare_type(methodOopDesc, oopDesc) \ + declare_type(objArrayKlass, arrayKlass) \ + declare_type(objArrayKlassKlass, arrayKlassKlass) \ + declare_type(objArrayOopDesc, arrayOopDesc) \ + declare_type(constMethodOopDesc, oopDesc) \ + declare_type(symbolKlass, Klass) \ + declare_type(symbolOopDesc, oopDesc) \ + declare_type(typeArrayKlass, arrayKlass) \ + declare_type(typeArrayKlassKlass, arrayKlassKlass) \ + declare_type(typeArrayOopDesc, arrayOopDesc) \ + \ + /********/ \ + /* Oops */ \ + /********/ \ + \ + declare_oop_type(constantPoolOop) \ + declare_oop_type(constantPoolCacheOop) \ + declare_oop_type(klassOop) \ + declare_oop_type(markOop) \ + declare_oop_type(methodOop) \ + declare_oop_type(methodDataOop) \ + declare_oop_type(objArrayOop) \ + declare_oop_type(oop) \ + declare_oop_type(constMethodOop) \ + declare_oop_type(symbolOop) \ + declare_oop_type(typeArrayOop) \ + \ + /*************************************/ \ + /* MethodOop-related data structures */ \ + /*************************************/ \ + \ + declare_toplevel_type(CheckedExceptionElement) \ + declare_toplevel_type(LocalVariableTableElement) \ + \ + /******************************************/ \ + /* Generation and space hierarchies */ \ + /* (needed for run-time type information) */ \ + /******************************************/ \ + \ + declare_toplevel_type(CollectedHeap) \ + declare_type(SharedHeap, CollectedHeap) \ + declare_type(GenCollectedHeap, SharedHeap) \ + declare_toplevel_type(Generation) \ + declare_type(DefNewGeneration, Generation) \ + declare_type(CardGeneration, Generation) \ + declare_type(OneContigSpaceCardGeneration, CardGeneration) \ + declare_type(TenuredGeneration, OneContigSpaceCardGeneration) \ + declare_type(CompactingPermGenGen, OneContigSpaceCardGeneration) \ + declare_toplevel_type(Space) \ + declare_toplevel_type(BitMap) \ + declare_type(CompactibleSpace, Space) \ + declare_type(ContiguousSpace, CompactibleSpace) \ + declare_type(EdenSpace, ContiguousSpace) \ + declare_type(OffsetTableContigSpace, ContiguousSpace) \ + declare_type(TenuredSpace, OffsetTableContigSpace) \ + declare_type(ContigPermSpace, OffsetTableContigSpace) \ + declare_toplevel_type(PermGen) \ + declare_type(CompactingPermGen, PermGen) \ + declare_toplevel_type(BarrierSet) \ + declare_type(ModRefBarrierSet, BarrierSet) \ + declare_type(CardTableModRefBS, ModRefBarrierSet) \ + declare_toplevel_type(GenRemSet) \ + declare_type(CardTableRS, GenRemSet) \ + declare_toplevel_type(BlockOffsetSharedArray) \ + declare_toplevel_type(BlockOffsetTable) \ + declare_type(BlockOffsetArray, BlockOffsetTable) \ + declare_type(BlockOffsetArrayContigSpace, BlockOffsetArray) \ + declare_type(BlockOffsetArrayNonContigSpace, BlockOffsetArray) \ + \ + /* Miscellaneous other GC types */ \ + \ + declare_toplevel_type(ageTable) \ + declare_toplevel_type(Generation::StatRecord) \ + declare_toplevel_type(GenerationSpec) \ + declare_toplevel_type(HeapWord) \ + declare_toplevel_type(MemRegion) \ + declare_toplevel_type(const MemRegion) \ + declare_toplevel_type(PermanentGenerationSpec) \ + declare_toplevel_type(ThreadLocalAllocBuffer) \ + declare_toplevel_type(VirtualSpace) \ + declare_toplevel_type(WaterMark) \ + \ + /* Pointers to Garbage Collection types */ \ + \ + declare_toplevel_type(BarrierSet*) \ + declare_toplevel_type(BlockOffsetSharedArray*) \ + declare_toplevel_type(GenRemSet*) \ + declare_toplevel_type(CardTableRS*) \ + declare_toplevel_type(CollectedHeap*) \ + declare_toplevel_type(ContiguousSpace*) \ + declare_toplevel_type(DefNewGeneration*) \ + declare_toplevel_type(EdenSpace*) \ + declare_toplevel_type(GenCollectedHeap*) \ + declare_toplevel_type(Generation*) \ + declare_toplevel_type(GenerationSpec**) \ + declare_toplevel_type(HeapWord*) \ + declare_toplevel_type(MemRegion*) \ + declare_toplevel_type(OffsetTableContigSpace*) \ + declare_toplevel_type(OneContigSpaceCardGeneration*) \ + declare_toplevel_type(PermGen*) \ + declare_toplevel_type(Space*) \ + declare_toplevel_type(ThreadLocalAllocBuffer*) \ + \ + /************************/ \ + /* PerfMemory - jvmstat */ \ + /************************/ \ + \ + declare_toplevel_type(PerfDataPrologue) \ + declare_toplevel_type(PerfDataPrologue*) \ + declare_toplevel_type(PerfDataEntry) \ + declare_toplevel_type(PerfMemory) \ + \ + /*********************************/ \ + /* SymbolTable, SystemDictionary */ \ + /*********************************/ \ + \ + declare_toplevel_type(BasicHashtable) \ + declare_type(Hashtable, BasicHashtable) \ + declare_type(SymbolTable, Hashtable) \ + declare_type(StringTable, Hashtable) \ + declare_type(LoaderConstraintTable, Hashtable) \ + declare_type(TwoOopHashtable, Hashtable) \ + declare_type(Dictionary, TwoOopHashtable) \ + declare_type(PlaceholderTable, TwoOopHashtable) \ + declare_toplevel_type(Hashtable*) \ + declare_toplevel_type(SymbolTable*) \ + declare_toplevel_type(StringTable*) \ + declare_toplevel_type(LoaderConstraintTable*) \ + declare_toplevel_type(TwoOopHashtable*) \ + declare_toplevel_type(Dictionary*) \ + declare_toplevel_type(PlaceholderTable*) \ + declare_toplevel_type(BasicHashtableEntry) \ + declare_toplevel_type(BasicHashtableEntry*) \ + declare_type(HashtableEntry, BasicHashtableEntry) \ + declare_type(DictionaryEntry, HashtableEntry) \ + declare_type(PlaceholderEntry, HashtableEntry) \ + declare_type(LoaderConstraintEntry, HashtableEntry) \ + declare_toplevel_type(HashtableEntry*) \ + declare_toplevel_type(DictionaryEntry*) \ + declare_toplevel_type(HashtableBucket) \ + declare_toplevel_type(HashtableBucket*) \ + declare_toplevel_type(SystemDictionary) \ + declare_toplevel_type(ProtectionDomainEntry) \ + declare_toplevel_type(ProtectionDomainEntry*) \ + \ + /***********************************************************/ \ + /* Thread hierarchy (needed for run-time type information) */ \ + /***********************************************************/ \ + \ + declare_toplevel_type(Threads) \ + declare_toplevel_type(ThreadShadow) \ + declare_type(Thread, ThreadShadow) \ + declare_type(NamedThread, Thread) \ + declare_type(WatcherThread, Thread) \ + declare_type(JavaThread, Thread) \ + declare_type(JvmtiAgentThread, JavaThread) \ + declare_type(LowMemoryDetectorThread, JavaThread) \ + declare_type(CompilerThread, JavaThread) \ + declare_toplevel_type(OSThread) \ + declare_toplevel_type(JavaFrameAnchor) \ + \ + /***************/ \ + /* Interpreter */ \ + /***************/ \ + \ + declare_toplevel_type(AbstractInterpreter) \ + \ + /*********/ \ + /* Stubs */ \ + /*********/ \ + \ + declare_toplevel_type(StubQueue) \ + declare_toplevel_type(StubRoutines) \ + IA32_ONLY(declare_toplevel_type(StubRoutines::i486)) \ + declare_toplevel_type(Stub) \ + declare_type(InterpreterCodelet, Stub) \ + \ + /*************/ \ + /* JavaCalls */ \ + /*************/ \ + \ + declare_toplevel_type(JavaCallWrapper) \ + \ + /*************/ \ + /* CodeCache */ \ + /*************/ \ + \ + declare_toplevel_type(CodeCache) \ + \ + /************/ \ + /* CodeHeap */ \ + /************/ \ + \ + declare_toplevel_type(CodeHeap) \ + declare_toplevel_type(CodeHeap*) \ + declare_toplevel_type(HeapBlock) \ + declare_toplevel_type(HeapBlock::Header) \ + declare_type(FreeBlock, HeapBlock) \ + \ + /*************************************************************/ \ + /* CodeBlob hierarchy (needed for run-time type information) */ \ + /*************************************************************/ \ + \ + declare_toplevel_type(CodeBlob) \ + declare_type(BufferBlob, CodeBlob) \ + declare_type(nmethod, CodeBlob) \ + declare_type(RuntimeStub, CodeBlob) \ + declare_type(SingletonBlob, CodeBlob) \ + declare_type(SafepointBlob, SingletonBlob) \ + declare_type(DeoptimizationBlob, SingletonBlob) \ + declare_c2_type(ExceptionBlob, SingletonBlob) \ + declare_c2_type(UncommonTrapBlob, CodeBlob) \ + \ + /***************************************/ \ + /* PcDesc and other compiled code info */ \ + /***************************************/ \ + \ + declare_toplevel_type(PcDesc) \ + \ + /************************/ \ + /* OopMap and OopMapSet */ \ + /************************/ \ + \ + declare_toplevel_type(OopMap) \ + declare_toplevel_type(OopMapSet) \ + \ + /********************/ \ + /* CompressedStream */ \ + /********************/ \ + \ + declare_toplevel_type(CompressedStream) \ + \ + /**************/ \ + /* VMRegImpl */ \ + /**************/ \ + \ + declare_toplevel_type(VMRegImpl) \ + \ + /*********************************/ \ + /* JNIHandles and JNIHandleBlock */ \ + /*********************************/ \ + \ + declare_toplevel_type(JNIHandles) \ + declare_toplevel_type(JNIHandleBlock) \ + \ + /**********************/ \ + /* Runtime1 (C1 only) */ \ + /**********************/ \ + \ + declare_c1_toplevel_type(Runtime1) \ + \ + /************/ \ + /* Monitors */ \ + /************/ \ + \ + declare_toplevel_type(ObjectMonitor) \ + declare_toplevel_type(ObjectSynchronizer) \ + declare_toplevel_type(BasicLock) \ + declare_toplevel_type(BasicObjectLock) \ + \ + /*********************/ \ + /* Matcher (C2 only) */ \ + /*********************/ \ + \ + /* NOTE: this is not really a toplevel type, but we only need */ \ + /* this one -- FIXME later if necessary */ \ + declare_c2_toplevel_type(Matcher) \ + \ + /*********************/ \ + /* Adapter Blob Entries */ \ + /*********************/ \ + declare_toplevel_type(AdapterHandlerEntry) \ + declare_toplevel_type(AdapterHandlerEntry*) \ + \ + /********************/ \ + /* -XX flags */ \ + /********************/ \ + \ + declare_toplevel_type(Flag) \ + declare_toplevel_type(Flag*) \ + \ + /********************/ \ + /* JDK/VM version */ \ + /********************/ \ + \ + declare_toplevel_type(Abstract_VM_Version) \ + declare_toplevel_type(JDK_Version) \ + \ + /*************/ \ + /* Arguments */ \ + /*************/ \ + \ + declare_toplevel_type(Arguments) \ + \ + /***************/ \ + /* Other types */ \ + /***************/ \ + \ + /* all enum types */ \ + \ + declare_integer_type(Bytecodes::Code) \ + declare_integer_type(Generation::Name) \ + declare_integer_type(instanceKlass::ClassState) \ + declare_integer_type(JavaThreadState) \ + declare_integer_type(Location::Type) \ + declare_integer_type(Location::Where) \ + declare_integer_type(PermGen::Name) \ + \ + declare_integer_type(AccessFlags) /* FIXME: wrong type (not integer) */\ + declare_toplevel_type(address) /* FIXME: should this be an integer type? */\ + declare_toplevel_type(BreakpointInfo) \ + declare_toplevel_type(BreakpointInfo*) \ + declare_toplevel_type(CodeBlob*) \ + declare_toplevel_type(CompressedWriteStream*) \ + declare_toplevel_type(ConstantPoolCacheEntry) \ + declare_toplevel_type(elapsedTimer) \ + declare_toplevel_type(intptr_t*) \ + declare_unsigned_integer_type(InvocationCounter) /* FIXME: wrong type (not integer) */ \ + declare_toplevel_type(JavaThread*) \ + declare_toplevel_type(jbyte*) \ + declare_toplevel_type(jbyte**) \ + declare_toplevel_type(jint*) \ + declare_toplevel_type(jniIdMapBase*) \ + declare_unsigned_integer_type(juint) \ + declare_unsigned_integer_type(julong) \ + declare_toplevel_type(JNIHandleBlock*) \ + declare_toplevel_type(JNIid) \ + declare_toplevel_type(JNIid*) \ + declare_toplevel_type(jmethodID*) \ + declare_toplevel_type(Mutex*) \ + declare_toplevel_type(nmethod*) \ + declare_toplevel_type(ObjectMonitor*) \ + declare_toplevel_type(oop*) \ + declare_toplevel_type(OopMap**) \ + declare_toplevel_type(OopMapCache*) \ + declare_toplevel_type(OopMapSet*) \ + declare_toplevel_type(VMReg) \ + declare_toplevel_type(OSThread*) \ + declare_integer_type(ReferenceType) \ + declare_toplevel_type(StubQueue*) \ + declare_toplevel_type(Thread*) \ + declare_toplevel_type(Universe) + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_TYPES_OS_CPU macro (and must be */ + /* present there) */ + +//-------------------------------------------------------------------------------- +// VM_INT_CONSTANTS +// +// This table contains integer constants required over in the +// serviceability agent. The "declare_constant" macro is used for all +// enums, etc., while "declare_preprocessor_constant" must be used for +// all #defined constants. + +#define VM_INT_CONSTANTS(declare_constant, \ + declare_preprocessor_constant, \ + declare_c1_constant, \ + declare_c2_constant, \ + declare_c2_preprocessor_constant, \ + last_entry) \ + \ + /******************/ \ + /* Useful globals */ \ + /******************/ \ + \ + declare_constant(UseTLAB) \ + \ + /**************/ \ + /* Stack bias */ \ + /**************/ \ + \ + declare_preprocessor_constant("STACK_BIAS", STACK_BIAS) \ + \ + /****************/ \ + /* Object sizes */ \ + /****************/ \ + \ + declare_constant(LogBytesPerWord) \ + declare_constant(BytesPerLong) \ + \ + /********************/ \ + /* Object alignment */ \ + /********************/ \ + \ + declare_constant(MinObjAlignmentInBytes) \ + \ + /********************************************/ \ + /* Generation and Space Hierarchy Constants */ \ + /********************************************/ \ + \ + declare_constant(ageTable::table_size) \ + \ + declare_constant(BarrierSet::ModRef) \ + declare_constant(BarrierSet::CardTableModRef) \ + declare_constant(BarrierSet::Other) \ + \ + declare_constant(BlockOffsetSharedArray::LogN) \ + declare_constant(BlockOffsetSharedArray::LogN_words) \ + declare_constant(BlockOffsetSharedArray::N_bytes) \ + declare_constant(BlockOffsetSharedArray::N_words) \ + \ + declare_constant(BlockOffsetArray::N_words) \ + \ + declare_constant(CardTableModRefBS::clean_card) \ + declare_constant(CardTableModRefBS::last_card) \ + declare_constant(CardTableModRefBS::dirty_card) \ + declare_constant(CardTableModRefBS::Precise) \ + declare_constant(CardTableModRefBS::ObjHeadPreciseArray) \ + declare_constant(CardTableModRefBS::card_shift) \ + declare_constant(CardTableModRefBS::card_size) \ + declare_constant(CardTableModRefBS::card_size_in_words) \ + \ + declare_constant(CardTableRS::youngergen_card) \ + \ + declare_constant(CollectedHeap::Abstract) \ + declare_constant(CollectedHeap::SharedHeap) \ + declare_constant(CollectedHeap::GenCollectedHeap) \ + \ + declare_constant(GenCollectedHeap::max_gens) \ + \ + /* constants from Generation::Name enum */ \ + \ + declare_constant(Generation::DefNew) \ + declare_constant(Generation::MarkSweepCompact) \ + declare_constant(Generation::Other) \ + \ + declare_constant(Generation::LogOfGenGrain) \ + declare_constant(Generation::GenGrain) \ + \ + declare_constant(HeapWordSize) \ + declare_constant(LogHeapWordSize) \ + declare_constant(HeapWordsPerOop) \ + \ + /* constants from PermGen::Name enum */ \ + \ + declare_constant(PermGen::MarkSweepCompact) \ + declare_constant(PermGen::MarkSweep) \ + \ + /************************/ \ + /* PerfMemory - jvmstat */ \ + /************************/ \ + \ + declare_preprocessor_constant("PERFDATA_MAJOR_VERSION", PERFDATA_MAJOR_VERSION) \ + declare_preprocessor_constant("PERFDATA_MINOR_VERSION", PERFDATA_MINOR_VERSION) \ + declare_preprocessor_constant("PERFDATA_BIG_ENDIAN", PERFDATA_BIG_ENDIAN) \ + declare_preprocessor_constant("PERFDATA_LITTLE_ENDIAN", PERFDATA_LITTLE_ENDIAN) \ + \ + /***************/ \ + /* SymbolTable */ \ + /***************/ \ + \ + declare_constant(SymbolTable::symbol_table_size) \ + \ + /***************/ \ + /* StringTable */ \ + /***************/ \ + \ + declare_constant(StringTable::string_table_size) \ + \ + /********************/ \ + /* SystemDictionary */ \ + /********************/ \ + \ + declare_constant(SystemDictionary::_loader_constraint_size) \ + declare_constant(SystemDictionary::_nof_buckets) \ + \ + /***********************************/ \ + /* LoaderConstraintTable constants */ \ + /***********************************/ \ + \ + declare_constant(LoaderConstraintTable::_loader_constraint_size) \ + declare_constant(LoaderConstraintTable::_nof_buckets) \ + \ + /************************************************************/ \ + /* HotSpot specific JVM_ACC constants from global anon enum */ \ + /************************************************************/ \ + \ + declare_constant(JVM_ACC_WRITTEN_FLAGS) \ + declare_constant(JVM_ACC_MONITOR_MATCH) \ + declare_constant(JVM_ACC_HAS_MONITOR_BYTECODES) \ + declare_constant(JVM_ACC_HAS_LOOPS) \ + declare_constant(JVM_ACC_LOOPS_FLAG_INIT) \ + declare_constant(JVM_ACC_QUEUED) \ + declare_constant(JVM_ACC_NOT_OSR_COMPILABLE) \ + declare_constant(JVM_ACC_HAS_LINE_NUMBER_TABLE) \ + declare_constant(JVM_ACC_HAS_CHECKED_EXCEPTIONS) \ + declare_constant(JVM_ACC_HAS_JSRS) \ + declare_constant(JVM_ACC_IS_OLD) \ + declare_constant(JVM_ACC_IS_OBSOLETE) \ + declare_constant(JVM_ACC_IS_PREFIXED_NATIVE) \ + declare_constant(JVM_ACC_HAS_MIRANDA_METHODS) \ + declare_constant(JVM_ACC_HAS_VANILLA_CONSTRUCTOR) \ + declare_constant(JVM_ACC_HAS_FINALIZER) \ + declare_constant(JVM_ACC_IS_CLONEABLE) \ + declare_constant(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE) \ + declare_constant(JVM_ACC_PROMOTED_FLAGS) \ + declare_constant(JVM_ACC_FIELD_ACCESS_WATCHED) \ + declare_constant(JVM_ACC_FIELD_MODIFICATION_WATCHED) \ + \ + /*****************************/ \ + /* Thread::SuspendFlags enum */ \ + /*****************************/ \ + \ + declare_constant(Thread::_external_suspend) \ + declare_constant(Thread::_ext_suspended) \ + declare_constant(Thread::_has_async_exception) \ + \ + /*******************/ \ + /* JavaThreadState */ \ + /*******************/ \ + \ + declare_constant(_thread_uninitialized) \ + declare_constant(_thread_new) \ + declare_constant(_thread_new_trans) \ + declare_constant(_thread_in_native) \ + declare_constant(_thread_in_native_trans) \ + declare_constant(_thread_in_vm) \ + declare_constant(_thread_in_vm_trans) \ + declare_constant(_thread_in_Java) \ + declare_constant(_thread_in_Java_trans) \ + declare_constant(_thread_blocked) \ + declare_constant(_thread_blocked_trans) \ + \ + /******************************/ \ + /* Klass misc. enum constants */ \ + /******************************/ \ + \ + declare_constant(Klass::_primary_super_limit) \ + declare_constant(Klass::_lh_instance_slow_path_bit) \ + declare_constant(Klass::_lh_log2_element_size_shift) \ + declare_constant(Klass::_lh_element_type_shift) \ + declare_constant(Klass::_lh_header_size_shift) \ + declare_constant(Klass::_lh_array_tag_shift) \ + declare_constant(Klass::_lh_array_tag_type_value) \ + declare_constant(Klass::_lh_array_tag_obj_value) \ + \ + /********************************/ \ + /* constMethodOopDesc anon-enum */ \ + /********************************/ \ + \ + declare_constant(constMethodOopDesc::_has_linenumber_table) \ + declare_constant(constMethodOopDesc::_has_checked_exceptions) \ + declare_constant(constMethodOopDesc::_has_localvariable_table) \ + \ + /*************************************/ \ + /* instanceKlass FieldOffset enum */ \ + /*************************************/ \ + \ + declare_constant(instanceKlass::access_flags_offset) \ + declare_constant(instanceKlass::name_index_offset) \ + declare_constant(instanceKlass::signature_index_offset) \ + declare_constant(instanceKlass::initval_index_offset) \ + declare_constant(instanceKlass::low_offset) \ + declare_constant(instanceKlass::high_offset) \ + declare_constant(instanceKlass::generic_signature_offset) \ + declare_constant(instanceKlass::next_offset) \ + declare_constant(instanceKlass::implementors_limit) \ + \ + /************************************************/ \ + /* instanceKlass InnerClassAttributeOffset enum */ \ + /************************************************/ \ + \ + declare_constant(instanceKlass::inner_class_inner_class_info_offset) \ + declare_constant(instanceKlass::inner_class_outer_class_info_offset) \ + declare_constant(instanceKlass::inner_class_inner_name_offset) \ + declare_constant(instanceKlass::inner_class_access_flags_offset) \ + declare_constant(instanceKlass::inner_class_next_offset) \ + \ + /*********************************/ \ + /* instanceKlass ClassState enum */ \ + /*********************************/ \ + \ + declare_constant(instanceKlass::unparsable_by_gc) \ + declare_constant(instanceKlass::allocated) \ + declare_constant(instanceKlass::loaded) \ + declare_constant(instanceKlass::linked) \ + declare_constant(instanceKlass::being_initialized) \ + declare_constant(instanceKlass::fully_initialized) \ + declare_constant(instanceKlass::initialization_error) \ + \ + /*********************************/ \ + /* symbolOop - symbol max length */ \ + /*********************************/ \ + \ + declare_constant(symbolOopDesc::max_symbol_length) \ + \ + /*********************************************/ \ + /* ConstantPoolCacheEntry FlagBitValues enum */ \ + /*********************************************/ \ + \ + declare_constant(ConstantPoolCacheEntry::hotSwapBit) \ + declare_constant(ConstantPoolCacheEntry::methodInterface) \ + declare_constant(ConstantPoolCacheEntry::volatileField) \ + declare_constant(ConstantPoolCacheEntry::vfinalMethod) \ + declare_constant(ConstantPoolCacheEntry::finalField) \ + \ + /******************************************/ \ + /* ConstantPoolCacheEntry FlagValues enum */ \ + /******************************************/ \ + \ + declare_constant(ConstantPoolCacheEntry::tosBits) \ + \ + /*********************************/ \ + /* java_lang_Class field offsets */ \ + /*********************************/ \ + \ + declare_constant(java_lang_Class::hc_klass_offset) \ + declare_constant(java_lang_Class::hc_array_klass_offset) \ + declare_constant(java_lang_Class::hc_resolved_constructor_offset) \ + declare_constant(java_lang_Class::hc_number_of_fake_oop_fields) \ + \ + /***************************************/ \ + /* java_lang_Thread::ThreadStatus enum */ \ + /***************************************/ \ + \ + declare_constant(java_lang_Thread::NEW) \ + declare_constant(java_lang_Thread::RUNNABLE) \ + declare_constant(java_lang_Thread::SLEEPING) \ + declare_constant(java_lang_Thread::IN_OBJECT_WAIT) \ + declare_constant(java_lang_Thread::IN_OBJECT_WAIT_TIMED) \ + declare_constant(java_lang_Thread::PARKED) \ + declare_constant(java_lang_Thread::PARKED_TIMED) \ + declare_constant(java_lang_Thread::BLOCKED_ON_MONITOR_ENTER) \ + declare_constant(java_lang_Thread::TERMINATED) \ + \ + /******************************/ \ + /* Debug info */ \ + /******************************/ \ + \ + declare_constant(Location::OFFSET_MASK) \ + declare_constant(Location::OFFSET_SHIFT) \ + declare_constant(Location::TYPE_MASK) \ + declare_constant(Location::TYPE_SHIFT) \ + declare_constant(Location::WHERE_MASK) \ + declare_constant(Location::WHERE_SHIFT) \ + \ + /* constants from Location::Type enum */ \ + \ + declare_constant(Location::normal) \ + declare_constant(Location::oop) \ + declare_constant(Location::int_in_long) \ + declare_constant(Location::lng) \ + declare_constant(Location::float_in_dbl) \ + declare_constant(Location::dbl) \ + declare_constant(Location::addr) \ + declare_constant(Location::invalid) \ + \ + /* constants from Location::Where enum */ \ + \ + declare_constant(Location::on_stack) \ + declare_constant(Location::in_register) \ + \ + /*********************/ \ + /* Matcher (C2 only) */ \ + /*********************/ \ + \ + declare_c2_preprocessor_constant("Matcher::interpreter_frame_pointer_reg", Matcher::interpreter_frame_pointer_reg()) \ + \ + /*********************************************/ \ + /* MethodCompilation (globalDefinitions.hpp) */ \ + /*********************************************/ \ + \ + declare_constant(InvocationEntryBci) \ + declare_constant(InvalidOSREntryBci) \ + \ + /***************/ \ + /* OopMapValue */ \ + /***************/ \ + \ + declare_constant(OopMapValue::type_bits) \ + declare_constant(OopMapValue::register_bits) \ + declare_constant(OopMapValue::type_shift) \ + declare_constant(OopMapValue::register_shift) \ + declare_constant(OopMapValue::type_mask) \ + declare_constant(OopMapValue::type_mask_in_place) \ + declare_constant(OopMapValue::register_mask) \ + declare_constant(OopMapValue::register_mask_in_place) \ + declare_constant(OopMapValue::unused_value) \ + declare_constant(OopMapValue::oop_value) \ + declare_constant(OopMapValue::value_value) \ + declare_constant(OopMapValue::dead_value) \ + declare_constant(OopMapValue::callee_saved_value) \ + declare_constant(OopMapValue::derived_oop_value) \ + \ + /******************/ \ + /* JNIHandleBlock */ \ + /******************/ \ + \ + declare_constant(JNIHandleBlock::block_size_in_oops) \ + \ + /**********************/ \ + /* ObjectSynchronizer */ \ + /**********************/ \ + \ + declare_constant(ObjectSynchronizer::_BLOCKSIZE) \ + \ + /********************************/ \ + /* Calling convention constants */ \ + /********************************/ \ + \ + declare_constant(RegisterImpl::number_of_registers) \ + declare_constant(ConcreteRegisterImpl::number_of_registers) \ + declare_preprocessor_constant("REG_COUNT", REG_COUNT) \ + declare_c2_preprocessor_constant("SAVED_ON_ENTRY_REG_COUNT", SAVED_ON_ENTRY_REG_COUNT) \ + declare_c2_preprocessor_constant("C_SAVED_ON_ENTRY_REG_COUNT", C_SAVED_ON_ENTRY_REG_COUNT) + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_INT_CONSTANTS_OS_CPU macro (and */ + /* must be present there) */ + +//-------------------------------------------------------------------------------- +// VM_LONG_CONSTANTS +// +// This table contains long constants required over in the +// serviceability agent. The "declare_constant" macro is used for all +// enums, etc., while "declare_preprocessor_constant" must be used for +// all #defined constants. + +#define VM_LONG_CONSTANTS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /*********************/ \ + /* MarkOop constants */ \ + /*********************/ \ + \ + /* Note: some of these are declared as long constants just for */ \ + /* consistency. The mask constants are the only ones requiring */ \ + /* 64 bits (on 64-bit platforms). */ \ + \ + declare_constant(markOopDesc::age_bits) \ + declare_constant(markOopDesc::lock_bits) \ + declare_constant(markOopDesc::biased_lock_bits) \ + declare_constant(markOopDesc::max_hash_bits) \ + declare_constant(markOopDesc::hash_bits) \ + \ + declare_constant(markOopDesc::lock_shift) \ + declare_constant(markOopDesc::biased_lock_shift) \ + declare_constant(markOopDesc::age_shift) \ + declare_constant(markOopDesc::hash_shift) \ + \ + declare_constant(markOopDesc::lock_mask) \ + declare_constant(markOopDesc::lock_mask_in_place) \ + declare_constant(markOopDesc::biased_lock_mask) \ + declare_constant(markOopDesc::biased_lock_mask_in_place) \ + declare_constant(markOopDesc::biased_lock_bit_in_place) \ + declare_constant(markOopDesc::age_mask) \ + declare_constant(markOopDesc::age_mask_in_place) \ + declare_constant(markOopDesc::hash_mask) \ + declare_constant(markOopDesc::hash_mask_in_place) \ + declare_constant(markOopDesc::biased_lock_alignment) \ + \ + declare_constant(markOopDesc::locked_value) \ + declare_constant(markOopDesc::unlocked_value) \ + declare_constant(markOopDesc::monitor_value) \ + declare_constant(markOopDesc::marked_value) \ + declare_constant(markOopDesc::biased_lock_pattern) \ + \ + declare_constant(markOopDesc::no_hash) \ + declare_constant(markOopDesc::no_hash_in_place) \ + declare_constant(markOopDesc::no_lock_in_place) \ + declare_constant(markOopDesc::max_age) + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_LONG_CONSTANTS_OS_CPU macro (and */ + /* must be present there) */ + + +//-------------------------------------------------------------------------------- +// Macros operating on the above lists +//-------------------------------------------------------------------------------- + +// This utility macro quotes the passed string +#define QUOTE(x) #x + +//-------------------------------------------------------------------------------- +// VMStructEntry macros +// + +// This macro generates a VMStructEntry line for a nonstatic field +#define GENERATE_NONSTATIC_VM_STRUCT_ENTRY(typeName, fieldName, type) \ + { QUOTE(typeName), QUOTE(fieldName), QUOTE(type), 0, cast_uint64_t(offset_of(typeName, fieldName)), NULL }, + +// This macro generates a VMStructEntry line for a static field +#define GENERATE_STATIC_VM_STRUCT_ENTRY(typeName, fieldName, type) \ + { QUOTE(typeName), QUOTE(fieldName), QUOTE(type), 1, 0, &typeName::fieldName }, + +// This macro generates a VMStructEntry line for an unchecked +// nonstatic field, in which the size of the type is also specified. +// The type string is given as NULL, indicating an "opaque" type. +#define GENERATE_UNCHECKED_NONSTATIC_VM_STRUCT_ENTRY(typeName, fieldName, size) \ + { QUOTE(typeName), QUOTE(fieldName), NULL, 0, cast_uint64_t(offset_of(typeName, fieldName)), NULL }, + +// This macro generates a VMStructEntry line for an unchecked +// static field, in which the size of the type is also specified. +// The type string is given as NULL, indicating an "opaque" type. +#define GENERATE_UNCHECKED_STATIC_VM_STRUCT_ENTRY(typeName, fieldName, size) \ + { QUOTE(typeName), QUOTE(fieldName), NULL, 1, 0, (void*) &typeName::fieldName }, + +// This macro generates the sentinel value indicating the end of the list +#define GENERATE_VM_STRUCT_LAST_ENTRY() \ + { NULL, NULL, NULL, 0, 0, NULL } + +// This macro checks the type of a VMStructEntry by comparing pointer types +#define CHECK_NONSTATIC_VM_STRUCT_ENTRY(typeName, fieldName, type) \ + {typeName *dummyObj = NULL; type* dummy = &dummyObj->fieldName; } + +// This macro checks the type of a volatile VMStructEntry by comparing pointer types +#define CHECK_VOLATILE_NONSTATIC_VM_STRUCT_ENTRY(typeName, fieldName, type) \ + {typedef type dummyvtype; typeName *dummyObj = NULL; volatile dummyvtype* dummy = &dummyObj->fieldName; } + +// This macro checks the type of a VMStructEntry by comparing pointer types +#define CHECK_STATIC_VM_STRUCT_ENTRY(typeName, fieldName, type) \ + {type* dummy = &typeName::fieldName; } + +// This macro ensures the type of a field and its containing type are +// present in the type table. The assertion string is shorter than +// preferable because (incredibly) of a bug in Solstice NFS client +// which seems to prevent very long lines from compiling. This assertion +// means that an entry in VMStructs::localHotSpotVMStructs[] was not +// found in VMStructs::localHotSpotVMTypes[]. +#define ENSURE_FIELD_TYPE_PRESENT(typeName, fieldName, type) \ + { assert(findType(QUOTE(typeName)) != 0, "type \"" QUOTE(typeName) "\" not found in type table"); \ + assert(findType(QUOTE(type)) != 0, "type \"" QUOTE(type) "\" not found in type table"); } + +// This is a no-op macro for unchecked fields +#define CHECK_NO_OP(a, b, c) + +// This is a no-op macro for the sentinel value +#define CHECK_SENTINEL() + +// +// Build-specific macros: +// + +// Generate and check a nonstatic field in non-product builds +#ifndef PRODUCT +# define GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) GENERATE_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define CHECK_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) CHECK_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT(a, b, c) ENSURE_FIELD_TYPE_PRESENT(a, b, c) +# define GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) GENERATE_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define CHECK_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) CHECK_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT(a, b, c) ENSURE_FIELD_TYPE_PRESENT(a, b, c) +#else +# define GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define CHECK_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT(a, b, c) +# define GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define CHECK_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT(a, b, c) +#endif /* PRODUCT */ + +// Generate and check a nonstatic field in C1 builds +#ifdef COMPILER1 +# define GENERATE_C1_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) GENERATE_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define CHECK_C1_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) CHECK_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define ENSURE_C1_FIELD_TYPE_PRESENT(a, b, c) ENSURE_FIELD_TYPE_PRESENT(a, b, c) +#else +# define GENERATE_C1_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define CHECK_C1_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define ENSURE_C1_FIELD_TYPE_PRESENT(a, b, c) +#endif /* COMPILER1 */ +// Generate and check a nonstatic field in C2 builds +#ifdef COMPILER2 +# define GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) GENERATE_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define CHECK_C2_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) CHECK_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define ENSURE_C2_FIELD_TYPE_PRESENT(a, b, c) ENSURE_FIELD_TYPE_PRESENT(a, b, c) +#else +# define GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define CHECK_C2_NONSTATIC_VM_STRUCT_ENTRY(a, b, c) +# define ENSURE_C2_FIELD_TYPE_PRESENT(a, b, c) +#endif /* COMPILER2 */ + +// Generate but do not check a static field in C1 builds +#ifdef COMPILER1 +# define GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY(a, b, c) GENERATE_UNCHECKED_STATIC_VM_STRUCT_ENTRY(a, b, c) +#else +# define GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY(a, b, c) +#endif /* COMPILER1 */ + +// Generate but do not check a static field in C2 builds +#ifdef COMPILER2 +# define GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY(a, b, c) GENERATE_UNCHECKED_STATIC_VM_STRUCT_ENTRY(a, b, c) +#else +# define GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY(a, b, c) +#endif /* COMPILER2 */ + +//-------------------------------------------------------------------------------- +// VMTypeEntry macros +// + +#define GENERATE_VM_TYPE_ENTRY(type, superclass) \ + { QUOTE(type), QUOTE(superclass), 0, 0, 0, sizeof(type) }, + +#define GENERATE_TOPLEVEL_VM_TYPE_ENTRY(type) \ + { QUOTE(type), NULL, 0, 0, 0, sizeof(type) }, + +#define GENERATE_OOP_VM_TYPE_ENTRY(type) \ + { QUOTE(type), NULL, 1, 0, 0, sizeof(type) }, + +#define GENERATE_INTEGER_VM_TYPE_ENTRY(type) \ + { QUOTE(type), NULL, 0, 1, 0, sizeof(type) }, + +#define GENERATE_UNSIGNED_INTEGER_VM_TYPE_ENTRY(type) \ + { QUOTE(type), NULL, 0, 1, 1, sizeof(type) }, + +#define GENERATE_VM_TYPE_LAST_ENTRY() \ + { NULL, NULL, 0, 0, 0, 0 } + +#define CHECK_VM_TYPE_ENTRY(type, superclass) \ + { type* dummyObj = NULL; superclass* dummySuperObj = dummyObj; } + +#define CHECK_VM_TYPE_NO_OP(a) +#define CHECK_SINGLE_ARG_VM_TYPE_NO_OP(a) + +// +// Build-specific macros: +// + +#ifdef COMPILER1 +# define GENERATE_C1_TOPLEVEL_VM_TYPE_ENTRY(a) GENERATE_TOPLEVEL_VM_TYPE_ENTRY(a) +# define CHECK_C1_TOPLEVEL_VM_TYPE_ENTRY(a) +#else +# define GENERATE_C1_TOPLEVEL_VM_TYPE_ENTRY(a) +# define CHECK_C1_TOPLEVEL_VM_TYPE_ENTRY(a) +#endif /* COMPILER1 */ + +#ifdef COMPILER2 +# define GENERATE_C2_VM_TYPE_ENTRY(a, b) GENERATE_VM_TYPE_ENTRY(a, b) +# define CHECK_C2_VM_TYPE_ENTRY(a, b) CHECK_VM_TYPE_ENTRY(a, b) +# define GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY(a) GENERATE_TOPLEVEL_VM_TYPE_ENTRY(a) +# define CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY(a) +#else +# define GENERATE_C2_VM_TYPE_ENTRY(a, b) +# define CHECK_C2_VM_TYPE_ENTRY(a, b) +# define GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY(a) +# define CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY(a) +#endif /* COMPILER2 */ + + +//-------------------------------------------------------------------------------- +// VMIntConstantEntry macros +// + +#define GENERATE_VM_INT_CONSTANT_ENTRY(name) \ + { QUOTE(name), (int32_t) name }, + +#define GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY(name, value) \ + { name, (int32_t) value }, + +// This macro generates the sentinel value indicating the end of the list +#define GENERATE_VM_INT_CONSTANT_LAST_ENTRY() \ + { NULL, 0 } + + +// Generate an int constant for a C1 build +#ifdef COMPILER1 +# define GENERATE_C1_VM_INT_CONSTANT_ENTRY(name) GENERATE_VM_INT_CONSTANT_ENTRY(name) +#else +# define GENERATE_C1_VM_INT_CONSTANT_ENTRY(name) +#endif /* COMPILER1 */ + +// Generate an int constant for a C2 build +#ifdef COMPILER2 +# define GENERATE_C2_VM_INT_CONSTANT_ENTRY(name) GENERATE_VM_INT_CONSTANT_ENTRY(name) +# define GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY(name, value) GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY(name, value) +#else +# define GENERATE_C2_VM_INT_CONSTANT_ENTRY(name) +# define GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY(name, value) +#endif /* COMPILER1 */ + +//-------------------------------------------------------------------------------- +// VMLongConstantEntry macros +// + +#define GENERATE_VM_LONG_CONSTANT_ENTRY(name) \ + { QUOTE(name), cast_uint64_t(name) }, + +#define GENERATE_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY(name, value) \ + { name, cast_uint64_t(value) }, + +// This macro generates the sentinel value indicating the end of the list +#define GENERATE_VM_LONG_CONSTANT_LAST_ENTRY() \ + { NULL, 0 } + +// Generate a long constant for a C1 build +#ifdef COMPILER1 +# define GENERATE_C1_VM_LONG_CONSTANT_ENTRY(name) GENERATE_VM_LONG_CONSTANT_ENTRY(name) +#else +# define GENERATE_C1_VM_LONG_CONSTANT_ENTRY(name) +#endif /* COMPILER1 */ + +// Generate a long constant for a C2 build +#ifdef COMPILER2 +# define GENERATE_C2_VM_LONG_CONSTANT_ENTRY(name) GENERATE_VM_LONG_CONSTANT_ENTRY(name) +# define GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY(name, value) GENERATE_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY(name, value) +#else +# define GENERATE_C2_VM_LONG_CONSTANT_ENTRY(name) +# define GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY(name, value) +#endif /* COMPILER1 */ + +// +// Instantiation of VMStructEntries, VMTypeEntries and VMIntConstantEntries +// + +// These initializers are allowed to access private fields in classes +// as long as class VMStructs is a friend +VMStructEntry VMStructs::localHotSpotVMStructs[] = { + + VM_STRUCTS(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_STATIC_VM_STRUCT_ENTRY, \ + GENERATE_UNCHECKED_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_C1_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ + GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ + GENERATE_VM_STRUCT_LAST_ENTRY) + +#ifndef SERIALGC + VM_STRUCTS_PARALLELGC(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_STATIC_VM_STRUCT_ENTRY) + + VM_STRUCTS_CMS(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_STATIC_VM_STRUCT_ENTRY) +#endif // SERIALGC + + VM_STRUCTS_CPU(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_STATIC_VM_STRUCT_ENTRY, \ + GENERATE_UNCHECKED_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ + GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ + GENERATE_VM_STRUCT_LAST_ENTRY) + + VM_STRUCTS_OS_CPU(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_STATIC_VM_STRUCT_ENTRY, \ + GENERATE_UNCHECKED_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY, \ + GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ + GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ + GENERATE_VM_STRUCT_LAST_ENTRY) +}; + +VMTypeEntry VMStructs::localHotSpotVMTypes[] = { + + VM_TYPES(GENERATE_VM_TYPE_ENTRY, + GENERATE_TOPLEVEL_VM_TYPE_ENTRY, + GENERATE_OOP_VM_TYPE_ENTRY, + GENERATE_INTEGER_VM_TYPE_ENTRY, + GENERATE_UNSIGNED_INTEGER_VM_TYPE_ENTRY, + GENERATE_C1_TOPLEVEL_VM_TYPE_ENTRY, + GENERATE_C2_VM_TYPE_ENTRY, + GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY, + GENERATE_VM_TYPE_LAST_ENTRY) + +#ifndef SERIALGC + VM_TYPES_PARALLELGC(GENERATE_VM_TYPE_ENTRY, + GENERATE_TOPLEVEL_VM_TYPE_ENTRY) + + VM_TYPES_CMS(GENERATE_VM_TYPE_ENTRY, + GENERATE_TOPLEVEL_VM_TYPE_ENTRY) + + VM_TYPES_PARNEW(GENERATE_VM_TYPE_ENTRY) +#endif // SERIALGC + + VM_TYPES_CPU(GENERATE_VM_TYPE_ENTRY, + GENERATE_TOPLEVEL_VM_TYPE_ENTRY, + GENERATE_OOP_VM_TYPE_ENTRY, + GENERATE_INTEGER_VM_TYPE_ENTRY, + GENERATE_UNSIGNED_INTEGER_VM_TYPE_ENTRY, + GENERATE_C1_TOPLEVEL_VM_TYPE_ENTRY, + GENERATE_C2_VM_TYPE_ENTRY, + GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY, + GENERATE_VM_TYPE_LAST_ENTRY) + + VM_TYPES_OS_CPU(GENERATE_VM_TYPE_ENTRY, + GENERATE_TOPLEVEL_VM_TYPE_ENTRY, + GENERATE_OOP_VM_TYPE_ENTRY, + GENERATE_INTEGER_VM_TYPE_ENTRY, + GENERATE_UNSIGNED_INTEGER_VM_TYPE_ENTRY, + GENERATE_C1_TOPLEVEL_VM_TYPE_ENTRY, + GENERATE_C2_VM_TYPE_ENTRY, + GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY, + GENERATE_VM_TYPE_LAST_ENTRY) +}; + +VMIntConstantEntry VMStructs::localHotSpotVMIntConstants[] = { + + VM_INT_CONSTANTS(GENERATE_VM_INT_CONSTANT_ENTRY, + GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, + GENERATE_C1_VM_INT_CONSTANT_ENTRY, + GENERATE_C2_VM_INT_CONSTANT_ENTRY, + GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, + GENERATE_VM_INT_CONSTANT_LAST_ENTRY) + +#ifndef SERIALGC + VM_INT_CONSTANTS_CMS(GENERATE_VM_INT_CONSTANT_ENTRY) + + VM_INT_CONSTANTS_PARNEW(GENERATE_VM_INT_CONSTANT_ENTRY) +#endif // SERIALGC + + VM_INT_CONSTANTS_CPU(GENERATE_VM_INT_CONSTANT_ENTRY, + GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, + GENERATE_C1_VM_INT_CONSTANT_ENTRY, + GENERATE_C2_VM_INT_CONSTANT_ENTRY, + GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, + GENERATE_VM_INT_CONSTANT_LAST_ENTRY) + + VM_INT_CONSTANTS_OS_CPU(GENERATE_VM_INT_CONSTANT_ENTRY, + GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, + GENERATE_C1_VM_INT_CONSTANT_ENTRY, + GENERATE_C2_VM_INT_CONSTANT_ENTRY, + GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, + GENERATE_VM_INT_CONSTANT_LAST_ENTRY) +}; + +VMLongConstantEntry VMStructs::localHotSpotVMLongConstants[] = { + + VM_LONG_CONSTANTS(GENERATE_VM_LONG_CONSTANT_ENTRY, + GENERATE_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, + GENERATE_C1_VM_LONG_CONSTANT_ENTRY, + GENERATE_C2_VM_LONG_CONSTANT_ENTRY, + GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, + GENERATE_VM_LONG_CONSTANT_LAST_ENTRY) + + VM_LONG_CONSTANTS_CPU(GENERATE_VM_LONG_CONSTANT_ENTRY, + GENERATE_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, + GENERATE_C1_VM_LONG_CONSTANT_ENTRY, + GENERATE_C2_VM_LONG_CONSTANT_ENTRY, + GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, + GENERATE_VM_LONG_CONSTANT_LAST_ENTRY) + + VM_LONG_CONSTANTS_OS_CPU(GENERATE_VM_LONG_CONSTANT_ENTRY, + GENERATE_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, + GENERATE_C1_VM_LONG_CONSTANT_ENTRY, + GENERATE_C2_VM_LONG_CONSTANT_ENTRY, + GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, + GENERATE_VM_LONG_CONSTANT_LAST_ENTRY) +}; + +// This is used both to check the types of referenced fields and, in +// debug builds, to ensure that all of the field types are present. +void +VMStructs::init() { + VM_STRUCTS(CHECK_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_STATIC_VM_STRUCT_ENTRY, + CHECK_NO_OP, + CHECK_VOLATILE_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_C1_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_C2_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_NO_OP, + CHECK_NO_OP, + CHECK_SENTINEL); + +#ifndef SERIALGC + VM_STRUCTS_PARALLELGC(CHECK_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_STATIC_VM_STRUCT_ENTRY); + + VM_STRUCTS_CMS(CHECK_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_STATIC_VM_STRUCT_ENTRY); +#endif // SERIALGC + + VM_STRUCTS_CPU(CHECK_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_STATIC_VM_STRUCT_ENTRY, + CHECK_NO_OP, + CHECK_VOLATILE_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_C2_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_NO_OP, + CHECK_NO_OP, + CHECK_SENTINEL); + + VM_STRUCTS_OS_CPU(CHECK_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_STATIC_VM_STRUCT_ENTRY, + CHECK_NO_OP, + CHECK_VOLATILE_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_C2_NONSTATIC_VM_STRUCT_ENTRY, + CHECK_NO_OP, + CHECK_NO_OP, + CHECK_SENTINEL); + + VM_TYPES(CHECK_VM_TYPE_ENTRY, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_C1_TOPLEVEL_VM_TYPE_ENTRY, + CHECK_C2_VM_TYPE_ENTRY, + CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY, + CHECK_SENTINEL); + +#ifndef SERIALGC + VM_TYPES_PARALLELGC(CHECK_VM_TYPE_ENTRY, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP); + + VM_TYPES_CMS(CHECK_VM_TYPE_ENTRY, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP); + + VM_TYPES_PARNEW(CHECK_VM_TYPE_ENTRY) +#endif // SERIALGC + + VM_TYPES_CPU(CHECK_VM_TYPE_ENTRY, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_C1_TOPLEVEL_VM_TYPE_ENTRY, + CHECK_C2_VM_TYPE_ENTRY, + CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY, + CHECK_SENTINEL); + + VM_TYPES_OS_CPU(CHECK_VM_TYPE_ENTRY, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_SINGLE_ARG_VM_TYPE_NO_OP, + CHECK_C1_TOPLEVEL_VM_TYPE_ENTRY, + CHECK_C2_VM_TYPE_ENTRY, + CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY, + CHECK_SENTINEL); + + // + // Split VM_STRUCTS() invocation into two parts to allow MS VC++ 6.0 + // to build with the source mounted over SNC3.2. Symptom was that + // debug build failed with an internal compiler error. Has been seen + // mounting sources from Solaris 2.6 and 2.7 hosts, but so far not + // 2.8 hosts. Appears to occur because line is too long. + // + // If an assertion failure is triggered here it means that an entry + // in VMStructs::localHotSpotVMStructs[] was not found in + // VMStructs::localHotSpotVMTypes[]. (The assertion itself had to be + // made less descriptive because of this above bug -- see the + // definition of ENSURE_FIELD_TYPE_PRESENT.) + // + // NOTE: taken out because this was just not working on everyone's + // Solstice NFS setup. If everyone switches to local workspaces on + // Win32, we can put this back in. +#ifndef _WINDOWS + debug_only(VM_STRUCTS(ENSURE_FIELD_TYPE_PRESENT, \ + CHECK_NO_OP, \ + CHECK_NO_OP, \ + CHECK_NO_OP, \ + CHECK_NO_OP, \ + CHECK_NO_OP, \ + CHECK_NO_OP, \ + CHECK_NO_OP, \ + CHECK_NO_OP, \ + CHECK_SENTINEL)); + debug_only(VM_STRUCTS(CHECK_NO_OP, \ + ENSURE_FIELD_TYPE_PRESENT, \ + CHECK_NO_OP, \ + ENSURE_FIELD_TYPE_PRESENT, \ + ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT, \ + ENSURE_C1_FIELD_TYPE_PRESENT, \ + ENSURE_C2_FIELD_TYPE_PRESENT, \ + CHECK_NO_OP, \ + CHECK_NO_OP, \ + CHECK_SENTINEL)); +#ifndef SERIALGC + debug_only(VM_STRUCTS_PARALLELGC(ENSURE_FIELD_TYPE_PRESENT, \ + ENSURE_FIELD_TYPE_PRESENT)); + debug_only(VM_STRUCTS_CMS(ENSURE_FIELD_TYPE_PRESENT, \ + ENSURE_FIELD_TYPE_PRESENT)); +#endif // SERIALGC + debug_only(VM_STRUCTS_CPU(ENSURE_FIELD_TYPE_PRESENT, \ + ENSURE_FIELD_TYPE_PRESENT, \ + CHECK_NO_OP, \ + ENSURE_FIELD_TYPE_PRESENT, \ + ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT, \ + ENSURE_C2_FIELD_TYPE_PRESENT, \ + CHECK_NO_OP, \ + CHECK_NO_OP, \ + CHECK_SENTINEL)); + debug_only(VM_STRUCTS_OS_CPU(ENSURE_FIELD_TYPE_PRESENT, \ + ENSURE_FIELD_TYPE_PRESENT, \ + CHECK_NO_OP, \ + ENSURE_FIELD_TYPE_PRESENT, \ + ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT, \ + ENSURE_C2_FIELD_TYPE_PRESENT, \ + CHECK_NO_OP, \ + CHECK_NO_OP, \ + CHECK_SENTINEL)); +#endif +} + +extern "C" { + +// see comments on cast_uint64_t at the top of this file +#define ASSIGN_CONST_TO_64BIT_VAR(var, expr) \ + JNIEXPORT uint64_t var = cast_uint64_t(expr); +#define ASSIGN_OFFSET_TO_64BIT_VAR(var, type, field) \ + ASSIGN_CONST_TO_64BIT_VAR(var, offset_of(type, field)) +#define ASSIGN_STRIDE_TO_64BIT_VAR(var, array) \ + ASSIGN_CONST_TO_64BIT_VAR(var, (char*)&array[1] - (char*)&array[0]) + +JNIEXPORT VMStructEntry* gHotSpotVMStructs = VMStructs::localHotSpotVMStructs; +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMStructEntryTypeNameOffset, VMStructEntry, typeName); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMStructEntryFieldNameOffset, VMStructEntry, fieldName); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMStructEntryTypeStringOffset, VMStructEntry, typeString); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMStructEntryIsStaticOffset, VMStructEntry, isStatic); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMStructEntryOffsetOffset, VMStructEntry, offset); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMStructEntryAddressOffset, VMStructEntry, address); +ASSIGN_STRIDE_TO_64BIT_VAR(gHotSpotVMStructEntryArrayStride, gHotSpotVMStructs); +JNIEXPORT VMTypeEntry* gHotSpotVMTypes = VMStructs::localHotSpotVMTypes; +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMTypeEntryTypeNameOffset, VMTypeEntry, typeName); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMTypeEntrySuperclassNameOffset, VMTypeEntry, superclassName); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMTypeEntryIsOopTypeOffset, VMTypeEntry, isOopType); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMTypeEntryIsIntegerTypeOffset, VMTypeEntry, isIntegerType); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMTypeEntryIsUnsignedOffset, VMTypeEntry, isUnsigned); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMTypeEntrySizeOffset, VMTypeEntry, size); +ASSIGN_STRIDE_TO_64BIT_VAR(gHotSpotVMTypeEntryArrayStride,gHotSpotVMTypes); +JNIEXPORT VMIntConstantEntry* gHotSpotVMIntConstants = VMStructs::localHotSpotVMIntConstants; +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMIntConstantEntryNameOffset, VMIntConstantEntry, name); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMIntConstantEntryValueOffset, VMIntConstantEntry, value); +ASSIGN_STRIDE_TO_64BIT_VAR(gHotSpotVMIntConstantEntryArrayStride, gHotSpotVMIntConstants); +JNIEXPORT VMLongConstantEntry* gHotSpotVMLongConstants = VMStructs::localHotSpotVMLongConstants; +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMLongConstantEntryNameOffset, VMLongConstantEntry, name); +ASSIGN_OFFSET_TO_64BIT_VAR(gHotSpotVMLongConstantEntryValueOffset, VMLongConstantEntry, value); +ASSIGN_STRIDE_TO_64BIT_VAR(gHotSpotVMLongConstantEntryArrayStride, gHotSpotVMLongConstants); +} + +#ifdef ASSERT +int +VMStructs::findType(const char* typeName) { + VMTypeEntry* types = gHotSpotVMTypes; + + while (types->typeName != NULL) { + if (!strcmp(typeName, types->typeName)) { + return 1; + } + ++types; + } + return 0; +} +#endif + +void vmStructs_init() { + debug_only(VMStructs::init()); +} diff --git a/hotspot/src/share/vm/runtime/vmStructs.hpp b/hotspot/src/share/vm/runtime/vmStructs.hpp new file mode 100644 index 00000000000..4e2670da678 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vmStructs.hpp @@ -0,0 +1,121 @@ +/* + * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This table encapsulates the debugging information required by the +// serviceability agent in order to run. Specifically, we need to +// understand the layout of certain C data structures (offsets, in +// bytes, of their fields.) +// +// There are alternatives for the design of this mechanism, including +// parsing platform-specific debugging symbols from a debug build into +// a program database. While this current mechanism can be considered +// to be a workaround for the inability to debug arbitrary C and C++ +// programs at the present time, it does have certain advantages. +// First, it is platform-independent, which will vastly simplify the +// initial bringup of the system both now and on future platforms. +// Second, it is embedded within the VM, as opposed to being in a +// separate program database; experience has shown that whenever +// portions of a system are decoupled, version skew is problematic. +// Third, generating a program database, for example for a product +// build, would probably require two builds to be done: the desired +// product build as well as an intermediary build with the PRODUCT +// flag turned on but also compiled with -g, leading to a doubling of +// the time required to get a serviceability agent-debuggable product +// build. Fourth, and very significantly, this table probably +// preserves more information about field types than stabs do; for +// example, it preserves the fact that a field is a "jlong" rather +// than transforming the type according to the typedef in jni_md.h, +// which allows the Java-side code to identify "Java-sized" fields in +// C++ data structures. If the symbol parsing mechanism was redone +// using stabs, it might still be necessary to have a table somewhere +// containing this information. +// +// Do not change the sizes or signedness of the integer values in +// these data structures; they are fixed over in the serviceability +// agent's Java code (for bootstrapping). + +typedef struct { + const char* typeName; // The type name containing the given field (example: "Klass") + const char* fieldName; // The field name within the type (example: "_name") + const char* typeString; // Quoted name of the type of this field (example: "symbolOopDesc*"; + // parsed in Java to ensure type correctness + int32_t isStatic; // Indicates whether following field is an offset or an address + uint64_t offset; // Offset of field within structure; only used for nonstatic fields + void* address; // Address of field; only used for static fields + // ("offset" can not be reused because of apparent SparcWorks compiler bug + // in generation of initializer data) +} VMStructEntry; + +typedef struct { + const char* typeName; // Type name (example: "methodOopDesc") + const char* superclassName; // Superclass name, or null if none (example: "oopDesc") + int32_t isOopType; // Does this type represent an oop typedef? (i.e., "methodOop" or + // "klassOop", but NOT "methodOopDesc") + int32_t isIntegerType; // Does this type represent an integer type (of arbitrary size)? + int32_t isUnsigned; // If so, is it unsigned? + uint64_t size; // Size, in bytes, of the type +} VMTypeEntry; + +typedef struct { + const char* name; // Name of constant (example: "_thread_in_native") + int32_t value; // Value of constant +} VMIntConstantEntry; + +typedef struct { + const char* name; // Name of constant (example: "_thread_in_native") + uint64_t value; // Value of constant +} VMLongConstantEntry; + +// This class is a friend of most classes, to be able to access +// private fields +class VMStructs { +public: + // The last entry is identified over in the serviceability agent by + // the fact that it has a NULL fieldName + static VMStructEntry localHotSpotVMStructs[]; + + // The last entry is identified over in the serviceability agent by + // the fact that it has a NULL typeName + static VMTypeEntry localHotSpotVMTypes[]; + + // Table of integer constants required by the serviceability agent. + // The last entry is identified over in the serviceability agent by + // the fact that it has a NULL typeName + static VMIntConstantEntry localHotSpotVMIntConstants[]; + + // Table of long constants required by the serviceability agent. + // The last entry is identified over in the serviceability agent by + // the fact that it has a NULL typeName + static VMLongConstantEntry localHotSpotVMLongConstants[]; + + // This is used to run any checking code necessary for validation of + // the data structure (debug build only) + static void init(); + +private: + // Look up a type in localHotSpotVMTypes using strcmp() (debug build only). + // Returns 1 if found, 0 if not. + // debug_only(static int findType(const char* typeName);) + static int findType(const char* typeName); +}; diff --git a/hotspot/src/share/vm/runtime/vmThread.cpp b/hotspot/src/share/vm/runtime/vmThread.cpp new file mode 100644 index 00000000000..56b54c381bd --- /dev/null +++ b/hotspot/src/share/vm/runtime/vmThread.cpp @@ -0,0 +1,655 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vmThread.cpp.incl" + +// Dummy VM operation to act as first element in our circular double-linked list +class VM_Dummy: public VM_Operation { + VMOp_Type type() const { return VMOp_Dummy; } + void doit() {}; +}; + +VMOperationQueue::VMOperationQueue() { + // The queue is a circular doubled-linked list, which always contains + // one element (i.e., one element means empty). + for(int i = 0; i < nof_priorities; i++) { + _queue_length[i] = 0; + _queue_counter = 0; + _queue[i] = new VM_Dummy(); + _queue[i]->set_next(_queue[i]); + _queue[i]->set_prev(_queue[i]); + } + _drain_list = NULL; +} + + +bool VMOperationQueue::queue_empty(int prio) { + // It is empty if there is exactly one element + bool empty = (_queue[prio] == _queue[prio]->next()); + assert( (_queue_length[prio] == 0 && empty) || + (_queue_length[prio] > 0 && !empty), "sanity check"); + return _queue_length[prio] == 0; +} + +// Inserts an element to the right of the q element +void VMOperationQueue::insert(VM_Operation* q, VM_Operation* n) { + assert(q->next()->prev() == q && q->prev()->next() == q, "sanity check"); + n->set_prev(q); + n->set_next(q->next()); + q->next()->set_prev(n); + q->set_next(n); +} + +void VMOperationQueue::queue_add_front(int prio, VM_Operation *op) { + _queue_length[prio]++; + insert(_queue[prio]->next(), op); +} + +void VMOperationQueue::queue_add_back(int prio, VM_Operation *op) { + _queue_length[prio]++; + insert(_queue[prio]->prev(), op); +} + + +void VMOperationQueue::unlink(VM_Operation* q) { + assert(q->next()->prev() == q && q->prev()->next() == q, "sanity check"); + q->prev()->set_next(q->next()); + q->next()->set_prev(q->prev()); +} + +VM_Operation* VMOperationQueue::queue_remove_front(int prio) { + if (queue_empty(prio)) return NULL; + assert(_queue_length[prio] >= 0, "sanity check"); + _queue_length[prio]--; + VM_Operation* r = _queue[prio]->next(); + assert(r != _queue[prio], "cannot remove base element"); + unlink(r); + return r; +} + +VM_Operation* VMOperationQueue::queue_drain(int prio) { + if (queue_empty(prio)) return NULL; + DEBUG_ONLY(int length = _queue_length[prio];); + assert(length >= 0, "sanity check"); + _queue_length[prio] = 0; + VM_Operation* r = _queue[prio]->next(); + assert(r != _queue[prio], "cannot remove base element"); + // remove links to base element from head and tail + r->set_prev(NULL); + _queue[prio]->prev()->set_next(NULL); + // restore queue to empty state + _queue[prio]->set_next(_queue[prio]); + _queue[prio]->set_prev(_queue[prio]); + assert(queue_empty(prio), "drain corrupted queue") +#ifdef DEBUG + int len = 0; + VM_Operation* cur; + for(cur = r; cur != NULL; cur=cur->next()) len++; + assert(len == length, "drain lost some ops"); +#endif + return r; +} + +void VMOperationQueue::queue_oops_do(int queue, OopClosure* f) { + VM_Operation* cur = _queue[queue]; + cur = cur->next(); + while (cur != _queue[queue]) { + cur->oops_do(f); + cur = cur->next(); + } +} + +void VMOperationQueue::drain_list_oops_do(OopClosure* f) { + VM_Operation* cur = _drain_list; + while (cur != NULL) { + cur->oops_do(f); + cur = cur->next(); + } +} + +//----------------------------------------------------------------- +// High-level interface +bool VMOperationQueue::add(VM_Operation *op) { + // Encapsulates VM queue policy. Currently, that + // only involves putting them on the right list + if (op->evaluate_at_safepoint()) { + queue_add_back(SafepointPriority, op); + return true; + } + + queue_add_back(MediumPriority, op); + return true; +} + +VM_Operation* VMOperationQueue::remove_next() { + // Assuming VMOperation queue is two-level priority queue. If there are + // more than two priorities, we need a different scheduling algorithm. + assert(SafepointPriority == 0 && MediumPriority == 1 && nof_priorities == 2, + "current algorithm does not work"); + + // simple counter based scheduling to prevent starvation of lower priority + // queue. -- see 4390175 + int high_prio, low_prio; + if (_queue_counter++ < 10) { + high_prio = SafepointPriority; + low_prio = MediumPriority; + } else { + _queue_counter = 0; + high_prio = MediumPriority; + low_prio = SafepointPriority; + } + + return queue_remove_front(queue_empty(high_prio) ? low_prio : high_prio); +} + +void VMOperationQueue::oops_do(OopClosure* f) { + for(int i = 0; i < nof_priorities; i++) { + queue_oops_do(i, f); + } + drain_list_oops_do(f); +} + + +//------------------------------------------------------------------------------------------------------------------ +// Implementation of VMThread stuff + +bool VMThread::_should_terminate = false; +bool VMThread::_terminated = false; +Monitor* VMThread::_terminate_lock = NULL; +VMThread* VMThread::_vm_thread = NULL; +VM_Operation* VMThread::_cur_vm_operation = NULL; +VMOperationQueue* VMThread::_vm_queue = NULL; +PerfCounter* VMThread::_perf_accumulated_vm_operation_time = NULL; + + +void VMThread::create() { + assert(vm_thread() == NULL, "we can only allocate one VMThread"); + _vm_thread = new VMThread(); + + // Create VM operation queue + _vm_queue = new VMOperationQueue(); + guarantee(_vm_queue != NULL, "just checking"); + + _terminate_lock = new Monitor(Mutex::safepoint, "VMThread::_terminate_lock", true); + + if (UsePerfData) { + // jvmstat performance counters + Thread* THREAD = Thread::current(); + _perf_accumulated_vm_operation_time = + PerfDataManager::create_counter(SUN_THREADS, "vmOperationTime", + PerfData::U_Ticks, CHECK); + } +} + + +VMThread::VMThread() : Thread() { + // nothing to do +} + +void VMThread::destroy() { + if (_vm_thread != NULL) { + delete _vm_thread; + _vm_thread = NULL; // VM thread is gone + } +} + +void VMThread::run() { + assert(this == vm_thread(), "check"); + + this->initialize_thread_local_storage(); + this->record_stack_base_and_size(); + // Notify_lock wait checks on active_handles() to rewait in + // case of spurious wakeup, it should wait on the last + // value set prior to the notify + this->set_active_handles(JNIHandleBlock::allocate_block()); + + { + MutexLocker ml(Notify_lock); + Notify_lock->notify(); + } + // Notify_lock is destroyed by Threads::create_vm() + + int prio = (VMThreadPriority == -1) + ? os::java_to_os_priority[NearMaxPriority] + : VMThreadPriority; + // Note that I cannot call os::set_priority because it expects Java + // priorities and I am *explicitly* using OS priorities so that it's + // possible to set the VM thread priority higher than any Java thread. + os::set_native_priority( this, prio ); + + // Wait for VM_Operations until termination + this->loop(); + + // Note the intention to exit before safepointing. + // 6295565 This has the effect of waiting for any large tty + // outputs to finish. + if (xtty != NULL) { + ttyLocker ttyl; + xtty->begin_elem("destroy_vm"); + xtty->stamp(); + xtty->end_elem(); + assert(should_terminate(), "termination flag must be set"); + } + + // 4526887 let VM thread exit at Safepoint + SafepointSynchronize::begin(); + + if (VerifyBeforeExit) { + HandleMark hm(VMThread::vm_thread()); + // Among other things, this ensures that Eden top is correct. + Universe::heap()->prepare_for_verify(); + os::check_heap(); + Universe::verify(true, true); // Silent verification to not polute normal output + } + + CompileBroker::set_should_block(); + + // wait for threads (compiler threads or daemon threads) in the + // _thread_in_native state to block. + VM_Exit::wait_for_threads_in_native_to_block(); + + // signal other threads that VM process is gone + { + // Note: we must have the _no_safepoint_check_flag. Mutex::lock() allows + // VM thread to enter any lock at Safepoint as long as its _owner is NULL. + // If that happens after _terminate_lock->wait() has unset _owner + // but before it actually drops the lock and waits, the notification below + // may get lost and we will have a hang. To avoid this, we need to use + // Mutex::lock_without_safepoint_check(). + MutexLockerEx ml(_terminate_lock, Mutex::_no_safepoint_check_flag); + _terminated = true; + _terminate_lock->notify(); + } + + // Deletion must be done synchronously by the JNI DestroyJavaVM thread + // so that the VMThread deletion completes before the main thread frees + // up the CodeHeap. + +} + + +// Notify the VMThread that the last non-daemon JavaThread has terminated, +// and wait until operation is performed. +void VMThread::wait_for_vm_thread_exit() { + { MutexLocker mu(VMOperationQueue_lock); + _should_terminate = true; + VMOperationQueue_lock->notify(); + } + + // Note: VM thread leaves at Safepoint. We are not stopped by Safepoint + // because this thread has been removed from the threads list. But anything + // that could get blocked by Safepoint should not be used after this point, + // otherwise we will hang, since there is no one can end the safepoint. + + // Wait until VM thread is terminated + // Note: it should be OK to use Terminator_lock here. But this is called + // at a very delicate time (VM shutdown) and we are operating in non- VM + // thread at Safepoint. It's safer to not share lock with other threads. + { MutexLockerEx ml(_terminate_lock, Mutex::_no_safepoint_check_flag); + while(!VMThread::is_terminated()) { + _terminate_lock->wait(Mutex::_no_safepoint_check_flag); + } + } +} + +void VMThread::print_on(outputStream* st) const { + st->print("\"%s\" ", name()); + Thread::print_on(st); + st->cr(); +} + +void VMThread::evaluate_operation(VM_Operation* op) { + ResourceMark rm; + + { + PerfTraceTime vm_op_timer(perf_accumulated_vm_operation_time()); + op->evaluate(); + } + + // Last access of info in _cur_vm_operation! + bool c_heap_allocated = op->is_cheap_allocated(); + + // Mark as completed + if (!op->evaluate_concurrently()) { + op->calling_thread()->increment_vm_operation_completed_count(); + } + // It is unsafe to access the _cur_vm_operation after the 'increment_vm_operation_completed_count' call, + // since if it is stack allocated the calling thread might have deallocated + if (c_heap_allocated) { + delete _cur_vm_operation; + } +} + + +void VMThread::loop() { + assert(_cur_vm_operation == NULL, "no current one should be executing"); + + while(true) { + VM_Operation* safepoint_ops = NULL; + // + // Wait for VM operation + // + // use no_safepoint_check to get lock without attempting to "sneak" + { MutexLockerEx mu_queue(VMOperationQueue_lock, + Mutex::_no_safepoint_check_flag); + + // Look for new operation + assert(_cur_vm_operation == NULL, "no current one should be executing"); + _cur_vm_operation = _vm_queue->remove_next(); + + // Stall time tracking code + if (PrintVMQWaitTime && _cur_vm_operation != NULL && + !_cur_vm_operation->evaluate_concurrently()) { + long stall = os::javaTimeMillis() - _cur_vm_operation->timestamp(); + if (stall > 0) + tty->print_cr("%s stall: %Ld", _cur_vm_operation->name(), stall); + } + + while (!should_terminate() && _cur_vm_operation == NULL) { + // wait with a timeout to guarantee safepoints at regular intervals + bool timedout = + VMOperationQueue_lock->wait(Mutex::_no_safepoint_check_flag, + GuaranteedSafepointInterval); + + // Support for self destruction + if ((SelfDestructTimer != 0) && !is_error_reported() && + (os::elapsedTime() > SelfDestructTimer * 60)) { + tty->print_cr("VM self-destructed"); + exit(-1); + } + + if (timedout && (SafepointALot || + SafepointSynchronize::is_cleanup_needed())) { + MutexUnlockerEx mul(VMOperationQueue_lock, + Mutex::_no_safepoint_check_flag); + // Force a safepoint since we have not had one for at least + // 'GuaranteedSafepointInterval' milliseconds. This will run all + // the clean-up processing that needs to be done regularly at a + // safepoint + SafepointSynchronize::begin(); + #ifdef ASSERT + if (GCALotAtAllSafepoints) InterfaceSupport::check_gc_alot(); + #endif + SafepointSynchronize::end(); + } + _cur_vm_operation = _vm_queue->remove_next(); + + // If we are at a safepoint we will evaluate all the operations that + // follow that also require a safepoint + if (_cur_vm_operation != NULL && + _cur_vm_operation->evaluate_at_safepoint()) { + safepoint_ops = _vm_queue->drain_at_safepoint_priority(); + } + } + + if (should_terminate()) break; + } // Release mu_queue_lock + + // + // Execute VM operation + // + { HandleMark hm(VMThread::vm_thread()); + + EventMark em("Executing VM operation: %s", vm_operation()->name()); + assert(_cur_vm_operation != NULL, "we should have found an operation to execute"); + + // Give the VM thread an extra quantum. Jobs tend to be bursty and this + // helps the VM thread to finish up the job. + // FIXME: When this is enabled and there are many threads, this can degrade + // performance significantly. + if( VMThreadHintNoPreempt ) + os::hint_no_preempt(); + + // If we are at a safepoint we will evaluate all the operations that + // follow that also require a safepoint + if (_cur_vm_operation->evaluate_at_safepoint()) { + + if (PrintGCApplicationConcurrentTime) { + gclog_or_tty->print_cr("Application time: %3.7f seconds", + RuntimeService::last_application_time_sec()); + } + + _vm_queue->set_drain_list(safepoint_ops); // ensure ops can be scanned + + SafepointSynchronize::begin(); + evaluate_operation(_cur_vm_operation); + // now process all queued safepoint ops, iteratively draining + // the queue until there are none left + do { + _cur_vm_operation = safepoint_ops; + if (_cur_vm_operation != NULL) { + do { + // evaluate_operation deletes the op object so we have + // to grab the next op now + VM_Operation* next = _cur_vm_operation->next(); + _vm_queue->set_drain_list(next); + evaluate_operation(_cur_vm_operation); + _cur_vm_operation = next; + if (PrintSafepointStatistics) { + SafepointSynchronize::inc_vmop_coalesced_count(); + } + } while (_cur_vm_operation != NULL); + } + // There is a chance that a thread enqueued a safepoint op + // since we released the op-queue lock and initiated the safepoint. + // So we drain the queue again if there is anything there, as an + // optimization to try and reduce the number of safepoints. + // As the safepoint synchronizes us with JavaThreads we will see + // any enqueue made by a JavaThread, but the peek will not + // necessarily detect a concurrent enqueue by a GC thread, but + // that simply means the op will wait for the next major cycle of the + // VMThread - just as it would if the GC thread lost the race for + // the lock. + if (_vm_queue->peek_at_safepoint_priority()) { + // must hold lock while draining queue + MutexLockerEx mu_queue(VMOperationQueue_lock, + Mutex::_no_safepoint_check_flag); + safepoint_ops = _vm_queue->drain_at_safepoint_priority(); + } else { + safepoint_ops = NULL; + } + } while(safepoint_ops != NULL); + + _vm_queue->set_drain_list(NULL); + + // Complete safepoint synchronization + SafepointSynchronize::end(); + + if (PrintGCApplicationStoppedTime) { + gclog_or_tty->print_cr("Total time for which application threads " + "were stopped: %3.7f seconds", + RuntimeService::last_safepoint_time_sec()); + } + + } else { // not a safepoint operation + if (TraceLongCompiles) { + elapsedTimer t; + t.start(); + evaluate_operation(_cur_vm_operation); + t.stop(); + double secs = t.seconds(); + if (secs * 1e3 > LongCompileThreshold) { + // XXX - _cur_vm_operation should not be accessed after + // the completed count has been incremented; the waiting + // thread may have already freed this memory. + tty->print_cr("vm %s: %3.7f secs]", _cur_vm_operation->name(), secs); + } + } else { + evaluate_operation(_cur_vm_operation); + } + + _cur_vm_operation = NULL; + } + } + + // + // Notify (potential) waiting Java thread(s) - lock without safepoint + // check so that sneaking is not possible + { MutexLockerEx mu(VMOperationRequest_lock, + Mutex::_no_safepoint_check_flag); + VMOperationRequest_lock->notify_all(); + } + + // + // We want to make sure that we get to a safepoint regularly. + // + if (SafepointALot || SafepointSynchronize::is_cleanup_needed()) { + long interval = SafepointSynchronize::last_non_safepoint_interval(); + bool max_time_exceeded = GuaranteedSafepointInterval != 0 && (interval > GuaranteedSafepointInterval); + if (SafepointALot || max_time_exceeded) { + HandleMark hm(VMThread::vm_thread()); + SafepointSynchronize::begin(); + SafepointSynchronize::end(); + } + } + } +} + +void VMThread::execute(VM_Operation* op) { + Thread* t = Thread::current(); + + if (!t->is_VM_thread()) { + // JavaThread or WatcherThread + t->check_for_valid_safepoint_state(true); + + // New request from Java thread, evaluate prologue + if (!op->doit_prologue()) { + return; // op was cancelled + } + + // Setup VM_operations for execution + op->set_calling_thread(t, Thread::get_priority(t)); + + // It does not make sense to execute the epilogue, if the VM operation object is getting + // deallocated by the VM thread. + bool concurrent = op->evaluate_concurrently(); + bool execute_epilog = !op->is_cheap_allocated(); + assert(!concurrent || op->is_cheap_allocated(), "concurrent => cheap_allocated"); + + // Get ticket number for non-concurrent VM operations + int ticket = 0; + if (!concurrent) { + ticket = t->vm_operation_ticket(); + } + + // Add VM operation to list of waiting threads. We are guaranteed not to block while holding the + // VMOperationQueue_lock, so we can block without a safepoint check. This allows vm operation requests + // to be queued up during a safepoint synchronization. + { + VMOperationQueue_lock->lock_without_safepoint_check(); + bool ok = _vm_queue->add(op); + op->set_timestamp(os::javaTimeMillis()); + VMOperationQueue_lock->notify(); + VMOperationQueue_lock->unlock(); + // VM_Operation got skipped + if (!ok) { + assert(concurrent, "can only skip concurrent tasks"); + if (op->is_cheap_allocated()) delete op; + return; + } + } + + if (!concurrent) { + // Wait for completion of request (non-concurrent) + // Note: only a JavaThread triggers the safepoint check when locking + MutexLocker mu(VMOperationRequest_lock); + while(t->vm_operation_completed_count() < ticket) { + VMOperationRequest_lock->wait(!t->is_Java_thread()); + } + } + + if (execute_epilog) { + op->doit_epilogue(); + } + } else { + // invoked by VM thread; usually nested VM operation + assert(t->is_VM_thread(), "must be a VM thread"); + VM_Operation* prev_vm_operation = vm_operation(); + if (prev_vm_operation != NULL) { + // Check the VM operation allows nested VM operation. This normally not the case, e.g., the compiler + // does not allow nested scavenges or compiles. + if (!prev_vm_operation->allow_nested_vm_operations()) { + fatal2("Nested VM operation %s requested by operation %s", op->name(), vm_operation()->name()); + } + op->set_calling_thread(prev_vm_operation->calling_thread(), prev_vm_operation->priority()); + } + + EventMark em("Executing %s VM operation: %s", prev_vm_operation ? "nested" : "", op->name()); + + // Release all internal handles after operation is evaluated + HandleMark hm(t); + _cur_vm_operation = op; + + if (op->evaluate_at_safepoint() && !SafepointSynchronize::is_at_safepoint()) { + SafepointSynchronize::begin(); + op->evaluate(); + SafepointSynchronize::end(); + } else { + op->evaluate(); + } + + // Free memory if needed + if (op->is_cheap_allocated()) delete op; + + _cur_vm_operation = prev_vm_operation; + } +} + + +void VMThread::oops_do(OopClosure* f) { + Thread::oops_do(f); + _vm_queue->oops_do(f); +} + +//------------------------------------------------------------------------------------------------------------------ +#ifndef PRODUCT + +void VMOperationQueue::verify_queue(int prio) { + // Check that list is correctly linked + int length = _queue_length[prio]; + VM_Operation *cur = _queue[prio]; + int i; + + // Check forward links + for(i = 0; i < length; i++) { + cur = cur->next(); + assert(cur != _queue[prio], "list to short (forward)"); + } + assert(cur->next() == _queue[prio], "list to long (forward)"); + + // Check backwards links + cur = _queue[prio]; + for(i = 0; i < length; i++) { + cur = cur->prev(); + assert(cur != _queue[prio], "list to short (backwards)"); + } + assert(cur->prev() == _queue[prio], "list to long (backwards)"); +} + +#endif + +void VMThread::verify() { + oops_do(&VerifyOopClosure::verify_oop); +} diff --git a/hotspot/src/share/vm/runtime/vmThread.hpp b/hotspot/src/share/vm/runtime/vmThread.hpp new file mode 100644 index 00000000000..b196d0fc581 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vmThread.hpp @@ -0,0 +1,148 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Prioritized queue of VM operations. +// +// Encapsulates both queue management and +// and priority policy +// +class VMOperationQueue : public CHeapObj { + private: + enum Priorities { + SafepointPriority, // Highest priority (operation executed at a safepoint) + MediumPriority, // Medium priority + nof_priorities + }; + + // We maintain a doubled linked list, with explicit count. + int _queue_length[nof_priorities]; + int _queue_counter; + VM_Operation* _queue [nof_priorities]; + // we also allow the vmThread to register the ops it has drained so we + // can scan them from oops_do + VM_Operation* _drain_list; + + // Double-linked non-empty list insert. + void insert(VM_Operation* q,VM_Operation* n); + void unlink(VM_Operation* q); + + // Basic queue manipulation + bool queue_empty (int prio); + void queue_add_front (int prio, VM_Operation *op); + void queue_add_back (int prio, VM_Operation *op); + VM_Operation* queue_remove_front(int prio); + void queue_oops_do(int queue, OopClosure* f); + void drain_list_oops_do(OopClosure* f); + VM_Operation* queue_drain(int prio); + // lock-free query: may return the wrong answer but must not break + bool queue_peek(int prio) { return _queue_length[prio] > 0; } + + public: + VMOperationQueue(); + + // Highlevel operations. Encapsulates policy + bool add(VM_Operation *op); + VM_Operation* remove_next(); // Returns next or null + VM_Operation* remove_next_at_safepoint_priority() { return queue_remove_front(SafepointPriority); } + VM_Operation* drain_at_safepoint_priority() { return queue_drain(SafepointPriority); } + void set_drain_list(VM_Operation* list) { _drain_list = list; } + bool peek_at_safepoint_priority() { return queue_peek(SafepointPriority); } + + // GC support + void oops_do(OopClosure* f); + + void verify_queue(int prio) PRODUCT_RETURN; +}; + + +// +// A single VMThread (the primordial thread) spawns all other threads +// and is itself used by other threads to offload heavy vm operations +// like scavenge, garbage_collect etc. +// + +class VMThread: public Thread { + private: + static ThreadPriority _current_priority; + + static bool _should_terminate; + static bool _terminated; + static Monitor * _terminate_lock; + static PerfCounter* _perf_accumulated_vm_operation_time; + + void evaluate_operation(VM_Operation* op); + public: + // Constructor + VMThread(); + + // Tester + bool is_VM_thread() const { return true; } + bool is_GC_thread() const { return true; } + + char* name() const { return (char*)"VM Thread"; } + + // The ever running loop for the VMThread + void loop(); + + // Called to stop the VM thread + static void wait_for_vm_thread_exit(); + static bool should_terminate() { return _should_terminate; } + static bool is_terminated() { return _terminated == true; } + + // Execution of vm operation + static void execute(VM_Operation* op); + + // Returns the current vm operation if any. + static VM_Operation* vm_operation() { return _cur_vm_operation; } + + // Returns the single instance of VMThread. + static VMThread* vm_thread() { return _vm_thread; } + + // GC support + void oops_do(OopClosure* f); + + // Debugging + void print_on(outputStream* st) const; + void print() const { print_on(tty); } + void verify(); + + // Performance measurement + static PerfCounter* perf_accumulated_vm_operation_time() { return _perf_accumulated_vm_operation_time; } + + // Entry for starting vm thread + virtual void run(); + + // Creations/Destructions + static void create(); + static void destroy(); + + private: + // VM_Operation support + static VM_Operation* _cur_vm_operation; // Current VM operation + static VMOperationQueue* _vm_queue; // Queue (w/ policy) of VM operations + + // Pointer to single-instance of VM thread + static VMThread* _vm_thread; +}; diff --git a/hotspot/src/share/vm/runtime/vm_operations.cpp b/hotspot/src/share/vm/runtime/vm_operations.cpp new file mode 100644 index 00000000000..9733aed03cc --- /dev/null +++ b/hotspot/src/share/vm/runtime/vm_operations.cpp @@ -0,0 +1,450 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vm_operations.cpp.incl" + +#define VM_OP_NAME_INITIALIZE(name) #name, + +const char* VM_Operation::_names[VM_Operation::VMOp_Terminating] = \ + { VM_OPS_DO(VM_OP_NAME_INITIALIZE) }; + +void VM_Operation::set_calling_thread(Thread* thread, ThreadPriority priority) { + _calling_thread = thread; + assert(MinPriority <= priority && priority <= MaxPriority, "sanity check"); + _priority = priority; +} + + +void VM_Operation::evaluate() { + ResourceMark rm; + if (TraceVMOperation) { + tty->print("["); + NOT_PRODUCT(print();) + } + doit(); + if (TraceVMOperation) { + tty->print_cr("]"); + } +} + +// Called by fatal error handler. +void VM_Operation::print_on_error(outputStream* st) const { + st->print("VM_Operation (" PTR_FORMAT "): ", this); + st->print("%s", name()); + + const char* mode; + switch(evaluation_mode()) { + case _safepoint : mode = "safepoint"; break; + case _no_safepoint : mode = "no safepoint"; break; + case _concurrent : mode = "concurrent"; break; + case _async_safepoint: mode = "async safepoint"; break; + default : mode = "unknown"; break; + } + st->print(", mode: %s", mode); + + if (calling_thread()) { + st->print(", requested by thread " PTR_FORMAT, calling_thread()); + } +} + +void VM_ThreadStop::doit() { + assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint"); + JavaThread* target = java_lang_Thread::thread(target_thread()); + // Note that this now allows multiple ThreadDeath exceptions to be + // thrown at a thread. + if (target != NULL) { + // the thread has run and is not already in the process of exiting + target->send_thread_stop(throwable()); + } +} + +void VM_Deoptimize::doit() { + // We do not want any GCs to happen while we are in the middle of this VM operation + ResourceMark rm; + DeoptimizationMarker dm; + + // Deoptimize all activations depending on marked nmethods + Deoptimization::deoptimize_dependents(); + + // Make the dependent methods zombies + CodeCache::make_marked_nmethods_zombies(); +} + + +VM_DeoptimizeFrame::VM_DeoptimizeFrame(JavaThread* thread, intptr_t* id) { + _thread = thread; + _id = id; +} + + +void VM_DeoptimizeFrame::doit() { + Deoptimization::deoptimize_frame(_thread, _id); +} + + +#ifndef PRODUCT + +void VM_DeoptimizeAll::doit() { + DeoptimizationMarker dm; + // deoptimize all java threads in the system + if (DeoptimizeALot) { + for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) { + if (thread->has_last_Java_frame()) { + thread->deoptimize(); + } + } + } else if (DeoptimizeRandom) { + + // Deoptimize some selected threads and frames + int tnum = os::random() & 0x3; + int fnum = os::random() & 0x3; + int tcount = 0; + for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) { + if (thread->has_last_Java_frame()) { + if (tcount++ == tnum) { + tcount = 0; + int fcount = 0; + // Deoptimize some selected frames. + // Biased llocking wants a updated register map + for(StackFrameStream fst(thread, UseBiasedLocking); !fst.is_done(); fst.next()) { + if (fst.current()->can_be_deoptimized()) { + if (fcount++ == fnum) { + fcount = 0; + Deoptimization::deoptimize(thread, *fst.current(), fst.register_map()); + } + } + } + } + } + } + } +} + + +void VM_ZombieAll::doit() { + JavaThread *thread = (JavaThread *)calling_thread(); + assert(thread->is_Java_thread(), "must be a Java thread"); + thread->make_zombies(); +} + +#endif // !PRODUCT + +void VM_Verify::doit() { + Universe::verify(); +} + +bool VM_PrintThreads::doit_prologue() { + assert(Thread::current()->is_Java_thread(), "just checking"); + + // Make sure AbstractOwnableSynchronizer is loaded + if (JDK_Version::is_gte_jdk16x_version()) { + java_util_concurrent_locks_AbstractOwnableSynchronizer::initialize(JavaThread::current()); + } + + // Get Heap_lock if concurrent locks will be dumped + if (_print_concurrent_locks) { + Heap_lock->lock(); + } + return true; +} + +void VM_PrintThreads::doit() { + Threads::print_on(_out, true, false, _print_concurrent_locks); +} + +void VM_PrintThreads::doit_epilogue() { + if (_print_concurrent_locks) { + // Release Heap_lock + Heap_lock->unlock(); + } +} + +void VM_PrintJNI::doit() { + JNIHandles::print_on(_out); +} + +VM_FindDeadlocks::~VM_FindDeadlocks() { + if (_deadlocks != NULL) { + DeadlockCycle* cycle = _deadlocks; + while (cycle != NULL) { + DeadlockCycle* d = cycle; + cycle = cycle->next(); + delete d; + } + } +} + +bool VM_FindDeadlocks::doit_prologue() { + assert(Thread::current()->is_Java_thread(), "just checking"); + + // Load AbstractOwnableSynchronizer class + if (_concurrent_locks && JDK_Version::is_gte_jdk16x_version()) { + java_util_concurrent_locks_AbstractOwnableSynchronizer::initialize(JavaThread::current()); + } + + return true; +} + +void VM_FindDeadlocks::doit() { + _deadlocks = ThreadService::find_deadlocks_at_safepoint(_concurrent_locks); + if (_out != NULL) { + int num_deadlocks = 0; + for (DeadlockCycle* cycle = _deadlocks; cycle != NULL; cycle = cycle->next()) { + num_deadlocks++; + cycle->print_on(_out); + } + + if (num_deadlocks == 1) { + _out->print_cr("\nFound 1 deadlock.\n"); + _out->flush(); + } else if (num_deadlocks > 1) { + _out->print_cr("\nFound %d deadlocks.\n", num_deadlocks); + _out->flush(); + } + } +} + +VM_ThreadDump::VM_ThreadDump(ThreadDumpResult* result, + int max_depth, + bool with_locked_monitors, + bool with_locked_synchronizers) { + _result = result; + _num_threads = 0; // 0 indicates all threads + _threads = NULL; + _result = result; + _max_depth = max_depth; + _with_locked_monitors = with_locked_monitors; + _with_locked_synchronizers = with_locked_synchronizers; +} + +VM_ThreadDump::VM_ThreadDump(ThreadDumpResult* result, + GrowableArray* threads, + int num_threads, + int max_depth, + bool with_locked_monitors, + bool with_locked_synchronizers) { + _result = result; + _num_threads = num_threads; + _threads = threads; + _result = result; + _max_depth = max_depth; + _with_locked_monitors = with_locked_monitors; + _with_locked_synchronizers = with_locked_synchronizers; +} + +bool VM_ThreadDump::doit_prologue() { + assert(Thread::current()->is_Java_thread(), "just checking"); + + // Load AbstractOwnableSynchronizer class before taking thread snapshots + if (JDK_Version::is_gte_jdk16x_version()) { + java_util_concurrent_locks_AbstractOwnableSynchronizer::initialize(JavaThread::current()); + } + + if (_with_locked_synchronizers) { + // Acquire Heap_lock to dump concurrent locks + Heap_lock->lock(); + } + + return true; +} + +void VM_ThreadDump::doit_epilogue() { + if (_with_locked_synchronizers) { + // Release Heap_lock + Heap_lock->unlock(); + } +} + +void VM_ThreadDump::doit() { + ResourceMark rm; + + ConcurrentLocksDump concurrent_locks(true); + if (_with_locked_synchronizers) { + concurrent_locks.dump_at_safepoint(); + } + + if (_num_threads == 0) { + // Snapshot all live threads + for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) { + if (jt->is_exiting() || + jt->is_hidden_from_external_view()) { + // skip terminating threads and hidden threads + continue; + } + ThreadConcurrentLocks* tcl = NULL; + if (_with_locked_synchronizers) { + tcl = concurrent_locks.thread_concurrent_locks(jt); + } + ThreadSnapshot* ts = snapshot_thread(jt, tcl); + _result->add_thread_snapshot(ts); + } + } else { + // Snapshot threads in the given _threads array + // A dummy snapshot is created if a thread doesn't exist + for (int i = 0; i < _num_threads; i++) { + instanceHandle th = _threads->at(i); + if (th() == NULL) { + // skip if the thread doesn't exist + // Add a dummy snapshot + _result->add_thread_snapshot(new ThreadSnapshot()); + continue; + } + + // Dump thread stack only if the thread is alive and not exiting + // and not VM internal thread. + JavaThread* jt = java_lang_Thread::thread(th()); + if (jt == NULL || /* thread not alive */ + jt->is_exiting() || + jt->is_hidden_from_external_view()) { + // add a NULL snapshot if skipped + _result->add_thread_snapshot(new ThreadSnapshot()); + continue; + } + ThreadConcurrentLocks* tcl = NULL; + if (_with_locked_synchronizers) { + tcl = concurrent_locks.thread_concurrent_locks(jt); + } + ThreadSnapshot* ts = snapshot_thread(jt, tcl); + _result->add_thread_snapshot(ts); + } + } +} + +ThreadSnapshot* VM_ThreadDump::snapshot_thread(JavaThread* java_thread, ThreadConcurrentLocks* tcl) { + ThreadSnapshot* snapshot = new ThreadSnapshot(java_thread); + snapshot->dump_stack_at_safepoint(_max_depth, _with_locked_monitors); + snapshot->set_concurrent_locks(tcl); + return snapshot; +} + +volatile bool VM_Exit::_vm_exited = false; +Thread * VM_Exit::_shutdown_thread = NULL; + +int VM_Exit::set_vm_exited() { + Thread * thr_cur = ThreadLocalStorage::get_thread_slow(); + + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint already"); + + int num_active = 0; + + _shutdown_thread = thr_cur; + _vm_exited = true; // global flag + for(JavaThread *thr = Threads::first(); thr != NULL; thr = thr->next()) + if (thr!=thr_cur && thr->thread_state() == _thread_in_native) { + ++num_active; + thr->set_terminated(JavaThread::_vm_exited); // per-thread flag + } + + return num_active; +} + +int VM_Exit::wait_for_threads_in_native_to_block() { + // VM exits at safepoint. This function must be called at the final safepoint + // to wait for threads in _thread_in_native state to be quiescent. + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint already"); + + Thread * thr_cur = ThreadLocalStorage::get_thread_slow(); + Monitor timer(Mutex::leaf, "VM_Exit timer", true); + + // Compiler threads need longer wait because they can access VM data directly + // while in native. If they are active and some structures being used are + // deleted by the shutdown sequence, they will crash. On the other hand, user + // threads must go through native=>Java/VM transitions first to access VM + // data, and they will be stopped during state transition. In theory, we + // don't have to wait for user threads to be quiescent, but it's always + // better to terminate VM when current thread is the only active thread, so + // wait for user threads too. Numbers are in 10 milliseconds. + int max_wait_user_thread = 30; // at least 300 milliseconds + int max_wait_compiler_thread = 1000; // at least 10 seconds + + int max_wait = max_wait_compiler_thread; + + int attempts = 0; + while (true) { + int num_active = 0; + int num_active_compiler_thread = 0; + + for(JavaThread *thr = Threads::first(); thr != NULL; thr = thr->next()) { + if (thr!=thr_cur && thr->thread_state() == _thread_in_native) { + num_active++; + if (thr->is_Compiler_thread()) { + num_active_compiler_thread++; + } + } + } + + if (num_active == 0) { + return 0; + } else if (attempts > max_wait) { + return num_active; + } else if (num_active_compiler_thread == 0 && attempts > max_wait_user_thread) { + return num_active; + } + + attempts++; + + MutexLockerEx ml(&timer, Mutex::_no_safepoint_check_flag); + timer.wait(Mutex::_no_safepoint_check_flag, 10); + } +} + +void VM_Exit::doit() { + CompileBroker::set_should_block(); + + // Wait for a short period for threads in native to block. Any thread + // still executing native code after the wait will be stopped at + // native==>Java/VM barriers. + // Among 16276 JCK tests, 94% of them come here without any threads still + // running in native; the other 6% are quiescent within 250ms (Ultra 80). + wait_for_threads_in_native_to_block(); + + set_vm_exited(); + + // cleanup globals resources before exiting. exit_globals() currently + // cleans up outputStream resources and PerfMemory resources. + exit_globals(); + + // Check for exit hook + exit_hook_t exit_hook = Arguments::exit_hook(); + if (exit_hook != NULL) { + // exit hook should exit. + exit_hook(_exit_code); + // ... but if it didn't, we must do it here + vm_direct_exit(_exit_code); + } else { + vm_direct_exit(_exit_code); + } +} + + +void VM_Exit::wait_if_vm_exited() { + if (_vm_exited && + ThreadLocalStorage::get_thread_slow() != _shutdown_thread) { + // _vm_exited is set at safepoint, and the Threads_lock is never released + // we will block here until the process dies + Threads_lock->lock_without_safepoint_check(); + ShouldNotReachHere(); + } +} diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp new file mode 100644 index 00000000000..778a46dd718 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vm_operations.hpp @@ -0,0 +1,366 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The following classes are used for operations +// initiated by a Java thread but that must +// take place in the VMThread. + +#define VM_OP_ENUM(type) VMOp_##type, + +// Note: When new VM_XXX comes up, add 'XXX' to the template table. +#define VM_OPS_DO(template) \ + template(Dummy) \ + template(ThreadStop) \ + template(ThreadDump) \ + template(PrintThreads) \ + template(FindDeadlocks) \ + template(ForceSafepoint) \ + template(ForceAsyncSafepoint) \ + template(Deoptimize) \ + template(DeoptimizeFrame) \ + template(DeoptimizeAll) \ + template(ZombieAll) \ + template(Verify) \ + template(PrintJNI) \ + template(HeapDumper) \ + template(DeoptimizeTheWorld) \ + template(GC_HeapInspection) \ + template(GenCollectFull) \ + template(GenCollectFullConcurrent) \ + template(GenCollectForAllocation) \ + template(ParallelGCFailedAllocation) \ + template(ParallelGCFailedPermanentAllocation) \ + template(ParallelGCSystemGC) \ + template(CMS_Initial_Mark) \ + template(CMS_Final_Remark) \ + template(EnableBiasedLocking) \ + template(RevokeBias) \ + template(BulkRevokeBias) \ + template(PopulateDumpSharedSpace) \ + template(JNIFunctionTableCopier) \ + template(RedefineClasses) \ + template(GetOwnedMonitorInfo) \ + template(GetObjectMonitorUsage) \ + template(GetCurrentContendedMonitor) \ + template(GetStackTrace) \ + template(GetMultipleStackTraces) \ + template(GetAllStackTraces) \ + template(GetThreadListStackTraces) \ + template(GetFrameCount) \ + template(GetFrameLocation) \ + template(ChangeBreakpoints) \ + template(GetOrSetLocal) \ + template(GetCurrentLocation) \ + template(EnterInterpOnlyMode) \ + template(ChangeSingleStep) \ + template(HeapWalkOperation) \ + template(HeapIterateOperation) \ + template(ReportJavaOutOfMemory) \ + template(Exit) \ + +class VM_Operation: public CHeapObj { + public: + enum Mode { + _safepoint, // blocking, safepoint, vm_op C-heap allocated + _no_safepoint, // blocking, no safepoint, vm_op C-Heap allocated + _concurrent, // non-blocking, no safepoint, vm_op C-Heap allocated + _async_safepoint // non-blocking, safepoint, vm_op C-Heap allocated + }; + + enum VMOp_Type { + VM_OPS_DO(VM_OP_ENUM) + VMOp_Terminating + }; + + private: + Thread* _calling_thread; + ThreadPriority _priority; + long _timestamp; + VM_Operation* _next; + VM_Operation* _prev; + + // The VM operation name array + static const char* _names[]; + + public: + VM_Operation() { _calling_thread = NULL; _next = NULL; _prev = NULL; } + virtual ~VM_Operation() {} + + // VM operation support (used by VM thread) + Thread* calling_thread() const { return _calling_thread; } + ThreadPriority priority() { return _priority; } + void set_calling_thread(Thread* thread, ThreadPriority priority); + + long timestamp() const { return _timestamp; } + void set_timestamp(long timestamp) { _timestamp = timestamp; } + + // Called by VM thread - does in turn invoke doit(). Do not override this + void evaluate(); + + // evaluate() is called by the VMThread and in turn calls doit(). + // If the thread invoking VMThread::execute((VM_Operation*) is a JavaThread, + // doit_prologue() is called in that thread before transferring control to + // the VMThread. + // If doit_prologue() returns true the VM operation will proceed, and + // doit_epilogue() will be called by the JavaThread once the VM operation + // completes. If doit_prologue() returns false the VM operation is cancelled. + virtual void doit() = 0; + virtual bool doit_prologue() { return true; }; + virtual void doit_epilogue() {}; // Note: Not called if mode is: _concurrent + + // Type test + virtual bool is_methodCompiler() const { return false; } + + // Linking + VM_Operation *next() const { return _next; } + VM_Operation *prev() const { return _prev; } + void set_next(VM_Operation *next) { _next = next; } + void set_prev(VM_Operation *prev) { _prev = prev; } + + // Configuration. Override these appropriatly in subclasses. + virtual VMOp_Type type() const = 0; + virtual Mode evaluation_mode() const { return _safepoint; } + virtual bool allow_nested_vm_operations() const { return false; } + virtual bool is_cheap_allocated() const { return false; } + virtual void oops_do(OopClosure* f) { /* do nothing */ }; + + // CAUTION: + // If you override these methods, make sure that the evaluation + // of these methods is race-free and non-blocking, since these + // methods may be evaluated either by the mutators or by the + // vm thread, either concurrently with mutators or with the mutators + // stopped. In other words, taking locks is verboten, and if there + // are any races in evaluating the conditions, they'd better be benign. + virtual bool evaluate_at_safepoint() const { + return evaluation_mode() == _safepoint || + evaluation_mode() == _async_safepoint; + } + virtual bool evaluate_concurrently() const { + return evaluation_mode() == _concurrent || + evaluation_mode() == _async_safepoint; + } + + // Debugging + void print_on_error(outputStream* st) const; + const char* name() const { return _names[type()]; } + static const char* name(int type) { + assert(type >= 0 && type < VMOp_Terminating, "invalid VM operation type"); + return _names[type]; + } +#ifndef PRODUCT + void print_on(outputStream* st) const { print_on_error(st); } +#endif +}; + +class VM_ThreadStop: public VM_Operation { + private: + oop _thread; // The Thread that the Throwable is thrown against + oop _throwable; // The Throwable thrown at the target Thread + public: + // All oops are passed as JNI handles, since there is no guarantee that a GC might happen before the + // VM operation is executed. + VM_ThreadStop(oop thread, oop throwable) { + _thread = thread; + _throwable = throwable; + } + VMOp_Type type() const { return VMOp_ThreadStop; } + oop target_thread() const { return _thread; } + oop throwable() const { return _throwable;} + void doit(); + // We deoptimize if top-most frame is compiled - this might require a C2I adapter to be generated + bool allow_nested_vm_operations() const { return true; } + Mode evaluation_mode() const { return _async_safepoint; } + bool is_cheap_allocated() const { return true; } + + // GC support + void oops_do(OopClosure* f) { + f->do_oop(&_thread); f->do_oop(&_throwable); + } +}; + +// dummy vm op, evaluated just to force a safepoint +class VM_ForceSafepoint: public VM_Operation { + public: + VM_ForceSafepoint() {} + void doit() {} + VMOp_Type type() const { return VMOp_ForceSafepoint; } +}; + +// dummy vm op, evaluated just to force a safepoint +class VM_ForceAsyncSafepoint: public VM_Operation { + public: + VM_ForceAsyncSafepoint() {} + void doit() {} + VMOp_Type type() const { return VMOp_ForceAsyncSafepoint; } + Mode evaluation_mode() const { return _async_safepoint; } + bool is_cheap_allocated() const { return true; } +}; + +class VM_Deoptimize: public VM_Operation { + public: + VM_Deoptimize() {} + VMOp_Type type() const { return VMOp_Deoptimize; } + void doit(); + bool allow_nested_vm_operations() const { return true; } +}; + +class VM_DeoptimizeFrame: public VM_Operation { + private: + JavaThread* _thread; + intptr_t* _id; + public: + VM_DeoptimizeFrame(JavaThread* thread, intptr_t* id); + VMOp_Type type() const { return VMOp_DeoptimizeFrame; } + void doit(); + bool allow_nested_vm_operations() const { return true; } +}; + +#ifndef PRODUCT +class VM_DeoptimizeAll: public VM_Operation { + private: + KlassHandle _dependee; + public: + VM_DeoptimizeAll() {} + VMOp_Type type() const { return VMOp_DeoptimizeAll; } + void doit(); + bool allow_nested_vm_operations() const { return true; } +}; + + +class VM_ZombieAll: public VM_Operation { + public: + VM_ZombieAll() {} + VMOp_Type type() const { return VMOp_ZombieAll; } + void doit(); + bool allow_nested_vm_operations() const { return true; } +}; +#endif // PRODUCT + +class VM_Verify: public VM_Operation { + private: + KlassHandle _dependee; + public: + VM_Verify() {} + VMOp_Type type() const { return VMOp_Verify; } + void doit(); +}; + + +class VM_PrintThreads: public VM_Operation { + private: + outputStream* _out; + bool _print_concurrent_locks; + public: + VM_PrintThreads() { _out = tty; _print_concurrent_locks = PrintConcurrentLocks; } + VM_PrintThreads(outputStream* out, bool print_concurrent_locks) { _out = out; _print_concurrent_locks = print_concurrent_locks; } + VMOp_Type type() const { return VMOp_PrintThreads; } + void doit(); + bool doit_prologue(); + void doit_epilogue(); +}; + +class VM_PrintJNI: public VM_Operation { + private: + outputStream* _out; + public: + VM_PrintJNI() { _out = tty; } + VM_PrintJNI(outputStream* out) { _out = out; } + VMOp_Type type() const { return VMOp_PrintJNI; } + void doit(); +}; + +class DeadlockCycle; +class VM_FindDeadlocks: public VM_Operation { + private: + bool _concurrent_locks; + DeadlockCycle* _deadlocks; + outputStream* _out; + + public: + VM_FindDeadlocks(bool concurrent_locks) : _concurrent_locks(concurrent_locks), _out(NULL), _deadlocks(NULL) {}; + VM_FindDeadlocks(outputStream* st) : _concurrent_locks(true), _out(st), _deadlocks(NULL) {}; + ~VM_FindDeadlocks(); + + DeadlockCycle* result() { return _deadlocks; }; + VMOp_Type type() const { return VMOp_FindDeadlocks; } + void doit(); + bool doit_prologue(); +}; + +class ThreadDumpResult; +class ThreadSnapshot; +class ThreadConcurrentLocks; + +class VM_ThreadDump : public VM_Operation { + private: + ThreadDumpResult* _result; + int _num_threads; + GrowableArray* _threads; + int _max_depth; + bool _with_locked_monitors; + bool _with_locked_synchronizers; + + ThreadSnapshot* snapshot_thread(JavaThread* java_thread, ThreadConcurrentLocks* tcl); + + public: + VM_ThreadDump(ThreadDumpResult* result, + int max_depth, // -1 indicates entire stack + bool with_locked_monitors, + bool with_locked_synchronizers); + + VM_ThreadDump(ThreadDumpResult* result, + GrowableArray* threads, + int num_threads, // -1 indicates entire stack + int max_depth, + bool with_locked_monitors, + bool with_locked_synchronizers); + + VMOp_Type type() const { return VMOp_ThreadDump; } + void doit(); + bool doit_prologue(); + void doit_epilogue(); +}; + + +class VM_Exit: public VM_Operation { + private: + int _exit_code; + static volatile bool _vm_exited; + static Thread * _shutdown_thread; + static void wait_if_vm_exited(); + public: + VM_Exit(int exit_code) { + _exit_code = exit_code; + } + static int wait_for_threads_in_native_to_block(); + static int set_vm_exited(); + static bool vm_exited() { return _vm_exited; } + static void block_if_vm_exited() { + if (_vm_exited) { + wait_if_vm_exited(); + } + } + VMOp_Type type() const { return VMOp_Exit; } + void doit(); +}; diff --git a/hotspot/src/share/vm/runtime/vm_version.cpp b/hotspot/src/share/vm/runtime/vm_version.cpp new file mode 100644 index 00000000000..f5ee15ade86 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vm_version.cpp @@ -0,0 +1,212 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vm_version.cpp.incl" + +const char* Abstract_VM_Version::_s_vm_release = Abstract_VM_Version::vm_release(); +const char* Abstract_VM_Version::_s_internal_vm_info_string = Abstract_VM_Version::internal_vm_info_string(); +bool Abstract_VM_Version::_supports_cx8 = false; +unsigned int Abstract_VM_Version::_logical_processors_per_package = 1U; + +#ifndef HOTSPOT_RELEASE_VERSION + #error HOTSPOT_RELEASE_VERSION must be defined +#endif +#ifndef JRE_RELEASE_VERSION + #error JRE_RELEASE_VERSION must be defined +#endif +#ifndef HOTSPOT_BUILD_TARGET + #error HOTSPOT_BUILD_TARGET must be defined +#endif + +#ifdef PRODUCT + #define VM_RELEASE HOTSPOT_RELEASE_VERSION +#else + #define VM_RELEASE HOTSPOT_RELEASE_VERSION "-" HOTSPOT_BUILD_TARGET +#endif + +// HOTSPOT_RELEASE_VERSION must follow the release version naming convention +// .-b[-][-] +int Abstract_VM_Version::_vm_major_version = 0; +int Abstract_VM_Version::_vm_minor_version = 0; +int Abstract_VM_Version::_vm_build_number = 0; +bool Abstract_VM_Version::_initialized = false; + +void Abstract_VM_Version::initialize() { + if (_initialized) { + return; + } + char* vm_version = os::strdup(HOTSPOT_RELEASE_VERSION); + + // Expecting the next vm_version format: + // .-b[-] + char* vm_major_ver = vm_version; + assert(isdigit(vm_major_ver[0]),"wrong vm major version number"); + char* vm_minor_ver = strchr(vm_major_ver, '.'); + assert(vm_minor_ver != NULL && isdigit(vm_minor_ver[1]),"wrong vm minor version number"); + vm_minor_ver[0] = '\0'; // terminate vm_major_ver + vm_minor_ver += 1; + char* vm_build_num = strchr(vm_minor_ver, '-'); + assert(vm_build_num != NULL && vm_build_num[1] == 'b' && isdigit(vm_build_num[2]),"wrong vm build number"); + vm_build_num[0] = '\0'; // terminate vm_minor_ver + vm_build_num += 2; + + _vm_major_version = atoi(vm_major_ver); + _vm_minor_version = atoi(vm_minor_ver); + _vm_build_number = atoi(vm_build_num); + + os::free(vm_version); + _initialized = true; +} + +#if defined(_LP64) + #define VMLP "64-Bit " +#else + #define VMLP "" +#endif + +#ifdef KERNEL + #define VMTYPE "Kernel" +#else // KERNEL +#ifdef TIERED + #define VMTYPE "Server" +#else + #define VMTYPE COMPILER1_PRESENT("Client") \ + COMPILER2_PRESENT("Server") +#endif // TIERED +#endif // KERNEL + +#ifndef HOTSPOT_VM_DISTRO + #error HOTSPOT_VM_DISTRO must be defined +#endif +#define VMNAME HOTSPOT_VM_DISTRO " " VMLP VMTYPE " VM" + +const char* Abstract_VM_Version::vm_name() { + return VMNAME; +} + + +const char* Abstract_VM_Version::vm_vendor() { +#ifdef VENDOR + return XSTR(VENDOR); +#else + return "Sun Microsystems Inc."; +#endif +} + + +const char* Abstract_VM_Version::vm_info_string() { + switch (Arguments::mode()) { + case Arguments::_int: + return UseSharedSpaces ? "interpreted mode, sharing" : "interpreted mode"; + case Arguments::_mixed: + return UseSharedSpaces ? "mixed mode, sharing" : "mixed mode"; + case Arguments::_comp: + return UseSharedSpaces ? "compiled mode, sharing" : "compiled mode"; + }; + ShouldNotReachHere(); + return ""; +} + +// NOTE: do *not* use stringStream. this function is called by +// fatal error handler. if the crash is in native thread, +// stringStream cannot get resource allocated and will SEGV. +const char* Abstract_VM_Version::vm_release() { + return VM_RELEASE; +} + +#define OS LINUX_ONLY("linux") \ + WINDOWS_ONLY("windows") \ + SOLARIS_ONLY("solaris") + +#define CPU IA32_ONLY("x86") \ + IA64_ONLY("ia64") \ + AMD64_ONLY("amd64") \ + SPARC_ONLY("sparc") + +const char *Abstract_VM_Version::vm_platform_string() { + return OS "-" CPU; +} + +const char* Abstract_VM_Version::internal_vm_info_string() { + #ifndef HOTSPOT_BUILD_USER + #define HOTSPOT_BUILD_USER unknown + #endif + + #ifndef HOTSPOT_BUILD_COMPILER + #ifdef _MSC_VER + #if _MSC_VER == 1100 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 5.0" + #elif _MSC_VER == 1200 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 6.0" + #elif _MSC_VER == 1310 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 7.1" + #elif _MSC_VER == 1400 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 8.0" + #else + #define HOTSPOT_BUILD_COMPILER "unknown MS VC++:" XSTR(_MSC_VER) + #endif + #elif defined(__SUNPRO_CC) + #if __SUNPRO_CC == 0x420 + #define HOTSPOT_BUILD_COMPILER "Workshop 4.2" + #elif __SUNPRO_CC == 0x500 + #define HOTSPOT_BUILD_COMPILER "Workshop 5.0 compat=" XSTR(__SUNPRO_CC_COMPAT) + #elif __SUNPRO_CC == 0x520 + #define HOTSPOT_BUILD_COMPILER "Workshop 5.2 compat=" XSTR(__SUNPRO_CC_COMPAT) + #elif __SUNPRO_CC == 0x580 + #define HOTSPOT_BUILD_COMPILER "Workshop 5.8" + #elif __SUNPRO_CC == 0x590 + #define HOTSPOT_BUILD_COMPILER "Workshop 5.9" + #else + #define HOTSPOT_BUILD_COMPILER "unknown Workshop:" XSTR(__SUNPRO_CC) + #endif + #elif defined(__GNUC__) + #define HOTSPOT_BUILD_COMPILER "gcc " __VERSION__ + #else + #define HOTSPOT_BUILD_COMPILER "unknown compiler" + #endif + #endif + + + return VMNAME " (" VM_RELEASE ") for " OS "-" CPU + " JRE (" JRE_RELEASE_VERSION "), built on " __DATE__ " " __TIME__ + " by " XSTR(HOTSPOT_BUILD_USER) " with " HOTSPOT_BUILD_COMPILER; +} + +unsigned int Abstract_VM_Version::jvm_version() { + return ((Abstract_VM_Version::vm_major_version() & 0xFF) << 24) | + ((Abstract_VM_Version::vm_minor_version() & 0xFF) << 16) | + (Abstract_VM_Version::vm_build_number() & 0xFF); +} + + +void VM_Version_init() { + VM_Version::initialize(); + +#ifndef PRODUCT + if (PrintMiscellaneous && Verbose) { + os::print_cpu_info(tty); + } +#endif +} diff --git a/hotspot/src/share/vm/runtime/vm_version.hpp b/hotspot/src/share/vm/runtime/vm_version.hpp new file mode 100644 index 00000000000..2f708e39db8 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vm_version.hpp @@ -0,0 +1,72 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// VM_Version provides information about the VM. + +class Abstract_VM_Version: AllStatic { + protected: + friend class VMStructs; + static const char* _s_vm_release; + static const char* _s_internal_vm_info_string; + // These are set by machine-dependent initializations + static bool _supports_cx8; + static unsigned int _logical_processors_per_package; + static int _vm_major_version; + static int _vm_minor_version; + static int _vm_build_number; + static bool _initialized; + public: + static void initialize(); + + // Name + static const char* vm_name(); + // Vendor + static const char* vm_vendor(); + // VM version information string printed by launcher (java -version) + static const char* vm_info_string(); + static const char* vm_release(); + static const char* vm_platform_string(); + + static int vm_major_version() { assert(_initialized, "not initialized"); return _vm_major_version; } + static int vm_minor_version() { assert(_initialized, "not initialized"); return _vm_minor_version; } + static int vm_build_number() { assert(_initialized, "not initialized"); return _vm_build_number; } + + // Gets the jvm_version_info.jvm_version defined in jvm.h + static unsigned int jvm_version(); + + // Internal version providing additional build information + static const char* internal_vm_info_string(); + + // does HW support an 8-byte compare-exchange operation? + static bool supports_cx8() {return _supports_cx8;} + static unsigned int logical_processors_per_package() { + return _logical_processors_per_package; + } + + // Number of page sizes efficiently supported by the hardware. Most chips now + // support two sizes, thus this default implementation. Processor-specific + // subclasses should define new versions to hide this one as needed. Note + // that the O/S may support more sizes, but at most this many are used. + static uint page_size_count() { return 2; } +}; diff --git a/hotspot/src/share/vm/runtime/vtune.hpp b/hotspot/src/share/vm/runtime/vtune.hpp new file mode 100644 index 00000000000..3d6187baa00 --- /dev/null +++ b/hotspot/src/share/vm/runtime/vtune.hpp @@ -0,0 +1,55 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Interface to Intel's VTune profiler. + +class VTune : AllStatic { + public: + static void create_nmethod(nmethod* nm); // register newly created nmethod + static void delete_nmethod(nmethod* nm); // unregister nmethod before discarding it + + static void register_stub(const char* name, address start, address end); + // register internal VM stub + static void start_GC(); // start/end of GC or scavenge + static void end_GC(); + + static void start_class_load(); // start/end of class loading + static void end_class_load(); + + static void exit(); // VM exit +}; + + +// helper objects +class VTuneGCMarker : StackObj { + public: + VTuneGCMarker() { VTune::start_GC(); } + ~VTuneGCMarker() { VTune::end_GC(); } +}; + +class VTuneClassLoadMarker : StackObj { + public: + VTuneClassLoadMarker() { VTune::start_class_load(); } + ~VTuneClassLoadMarker() { VTune::end_class_load(); } +}; diff --git a/hotspot/src/share/vm/services/attachListener.cpp b/hotspot/src/share/vm/services/attachListener.cpp new file mode 100644 index 00000000000..2361f200a4e --- /dev/null +++ b/hotspot/src/share/vm/services/attachListener.cpp @@ -0,0 +1,451 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_attachListener.cpp.incl" + +volatile bool AttachListener::_initialized; + +// Implementation of "properties" command. +// +// Invokes sun.misc.VMSupport.serializePropertiesToByteArray to serialize +// the system properties into a byte array. + +static klassOop load_and_initialize_klass(symbolHandle sh, TRAPS) { + klassOop k = SystemDictionary::resolve_or_fail(sh, true, CHECK_NULL); + instanceKlassHandle ik (THREAD, k); + if (ik->should_be_initialized()) { + ik->initialize(CHECK_NULL); + } + return ik(); +} + +static jint get_properties(AttachOperation* op, outputStream* out, symbolHandle serializePropertiesMethod) { + Thread* THREAD = Thread::current(); + HandleMark hm; + + // load sun.misc.VMSupport + symbolHandle klass = vmSymbolHandles::sun_misc_VMSupport(); + klassOop k = load_and_initialize_klass(klass, THREAD); + if (HAS_PENDING_EXCEPTION) { + java_lang_Throwable::print(PENDING_EXCEPTION, out); + CLEAR_PENDING_EXCEPTION; + return JNI_ERR; + } + instanceKlassHandle ik(THREAD, k); + + // invoke the serializePropertiesToByteArray method + JavaValue result(T_OBJECT); + JavaCallArguments args; + + + symbolHandle signature = vmSymbolHandles::serializePropertiesToByteArray_signature(); + JavaCalls::call_static(&result, + ik, + serializePropertiesMethod, + signature, + &args, + THREAD); + if (HAS_PENDING_EXCEPTION) { + java_lang_Throwable::print(PENDING_EXCEPTION, out); + CLEAR_PENDING_EXCEPTION; + return JNI_ERR; + } + + // The result should be a [B + oop res = (oop)result.get_jobject(); + assert(res->is_typeArray(), "just checking"); + assert(typeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "just checking"); + + // copy the bytes to the output stream + typeArrayOop ba = typeArrayOop(res); + jbyte* addr = typeArrayOop(res)->byte_at_addr(0); + out->print_raw((const char*)addr, ba->length()); + + return JNI_OK; +} + +// Implementation of "properties" command. +static jint get_system_properties(AttachOperation* op, outputStream* out) { + return get_properties(op, out, vmSymbolHandles::serializePropertiesToByteArray_name()); +} + +// Implementation of "agent_properties" command. +static jint get_agent_properties(AttachOperation* op, outputStream* out) { + return get_properties(op, out, vmSymbolHandles::serializeAgentPropertiesToByteArray_name()); +} + +// Implementation of "datadump" command. +// +// Raises a SIGBREAK signal so that VM dump threads, does deadlock detection, +// etc. In theory this command should only post a DataDumpRequest to any +// JVMTI environment that has enabled this event. However it's useful to +// trigger the SIGBREAK handler. + +static jint data_dump(AttachOperation* op, outputStream* out) { + if (!ReduceSignalUsage) { + AttachListener::pd_data_dump(); + } else { + if (JvmtiExport::should_post_data_dump()) { + JvmtiExport::post_data_dump(); + } + } + return JNI_OK; +} + +// Implementation of "threaddump" command - essentially a remote ctrl-break +// +static jint thread_dump(AttachOperation* op, outputStream* out) { + bool print_concurrent_locks = false; + if (op->arg(0) != NULL && strcmp(op->arg(0), "-l") == 0) { + print_concurrent_locks = true; + } + + // thread stacks + VM_PrintThreads op1(out, print_concurrent_locks); + VMThread::execute(&op1); + + // JNI global handles + VM_PrintJNI op2(out); + VMThread::execute(&op2); + + // Deadlock detection + VM_FindDeadlocks op3(out); + VMThread::execute(&op3); + + return JNI_OK; +} + +#ifndef SERVICES_KERNEL // Heap dumping not supported +// Implementation of "dumpheap" command. +// +// Input arguments :- +// arg0: Name of the dump file +// arg1: "-live" or "-all" +jint dump_heap(AttachOperation* op, outputStream* out) { + const char* path = op->arg(0); + if (path == NULL || path[0] == '\0') { + out->print_cr("No dump file specified"); + } else { + bool live_objects_only = true; // default is true to retain the behavior before this change is made + const char* arg1 = op->arg(1); + if (arg1 != NULL && (strlen(arg1) > 0)) { + if (strcmp(arg1, "-all") != 0 && strcmp(arg1, "-live") != 0) { + out->print_cr("Invalid argument to dumpheap operation: %s", arg1); + return JNI_ERR; + } + live_objects_only = strcmp(arg1, "-live") == 0; + } + + // Request a full GC before heap dump if live_objects_only = true + // This helps reduces the amount of unreachable objects in the dump + // and makes it easier to browse. + HeapDumper dumper(live_objects_only /* request GC */); + int res = dumper.dump(op->arg(0)); + if (res == 0) { + out->print_cr("Heap dump file created"); + } else { + // heap dump failed + ResourceMark rm; + char* error = dumper.error_as_C_string(); + if (error == NULL) { + out->print_cr("Dump failed - reason unknown"); + } else { + out->print_cr("%s", error); + } + } + } + return JNI_OK; +} +#endif // SERVICES_KERNEL + +// Implementation of "inspectheap" command +// +// Input arguments :- +// arg0: "-live" or "-all" +static jint heap_inspection(AttachOperation* op, outputStream* out) { + bool live_objects_only = true; // default is true to retain the behavior before this change is made + const char* arg0 = op->arg(0); + if (arg0 != NULL && (strlen(arg0) > 0)) { + if (strcmp(arg0, "-all") != 0 && strcmp(arg0, "-live") != 0) { + out->print_cr("Invalid argument to inspectheap operation: %s", arg0); + return JNI_ERR; + } + live_objects_only = strcmp(arg0, "-live") == 0; + } + VM_GC_HeapInspection heapop(out, live_objects_only /* request gc */); + VMThread::execute(&heapop); + return JNI_OK; +} + +// set a boolean global flag using value from AttachOperation +static jint set_bool_flag(const char* name, AttachOperation* op, outputStream* out) { + bool value = true; + const char* arg1; + if ((arg1 = op->arg(1)) != NULL) { + int tmp; + int n = sscanf(arg1, "%d", &tmp); + if (n != 1) { + out->print_cr("flag value has to be boolean (1 or 0)"); + return JNI_ERR; + } + value = (tmp != 0); + } + bool res = CommandLineFlags::boolAtPut((char*)name, &value, ATTACH_ON_DEMAND); + if (! res) { + out->print_cr("setting flag %s failed", name); + } + return res? JNI_OK : JNI_ERR; +} + +// set a intx global flag using value from AttachOperation +static jint set_intx_flag(const char* name, AttachOperation* op, outputStream* out) { + intx value; + const char* arg1; + if ((arg1 = op->arg(1)) != NULL) { + int n = sscanf(arg1, INTX_FORMAT, &value); + if (n != 1) { + out->print_cr("flag value has to be integer"); + return JNI_ERR; + } + } + bool res = CommandLineFlags::intxAtPut((char*)name, &value, ATTACH_ON_DEMAND); + if (! res) { + out->print_cr("setting flag %s failed", name); + } + + return res? JNI_OK : JNI_ERR; +} + +// set a uintx global flag using value from AttachOperation +static jint set_uintx_flag(const char* name, AttachOperation* op, outputStream* out) { + uintx value; + const char* arg1; + if ((arg1 = op->arg(1)) != NULL) { + int n = sscanf(arg1, UINTX_FORMAT, &value); + if (n != 1) { + out->print_cr("flag value has to be integer"); + return JNI_ERR; + } + } + bool res = CommandLineFlags::uintxAtPut((char*)name, &value, ATTACH_ON_DEMAND); + if (! res) { + out->print_cr("setting flag %s failed", name); + } + + return res? JNI_OK : JNI_ERR; +} + +// set a string global flag using value from AttachOperation +static jint set_ccstr_flag(const char* name, AttachOperation* op, outputStream* out) { + const char* value; + if ((value = op->arg(1)) == NULL) { + out->print_cr("flag value has to be a string"); + return JNI_ERR; + } + bool res = CommandLineFlags::ccstrAtPut((char*)name, &value, ATTACH_ON_DEMAND); + if (res) { + FREE_C_HEAP_ARRAY(char, value); + } else { + out->print_cr("setting flag %s failed", name); + } + + return res? JNI_OK : JNI_ERR; +} + +// Implementation of "setflag" command +static jint set_flag(AttachOperation* op, outputStream* out) { + + const char* name = NULL; + if ((name = op->arg(0)) == NULL) { + out->print_cr("flag name is missing"); + return JNI_ERR; + } + + Flag* f = Flag::find_flag((char*)name, strlen(name)); + if (f && f->is_external() && f->is_writeable()) { + if (f->is_bool()) { + return set_bool_flag(name, op, out); + } else if (f->is_intx()) { + return set_intx_flag(name, op, out); + } else if (f->is_uintx()) { + return set_uintx_flag(name, op, out); + } else if (f->is_ccstr()) { + return set_ccstr_flag(name, op, out); + } else { + ShouldNotReachHere(); + return JNI_ERR; + } + } else { + return AttachListener::pd_set_flag(op, out); + } +} + +// Implementation of "printflag" command +static jint print_flag(AttachOperation* op, outputStream* out) { + const char* name = NULL; + if ((name = op->arg(0)) == NULL) { + out->print_cr("flag name is missing"); + return JNI_ERR; + } + Flag* f = Flag::find_flag((char*)name, strlen(name)); + if (f) { + f->print_as_flag(out); + out->print_cr(""); + } else { + out->print_cr("no such flag '%s'", name); + } + return JNI_OK; +} + +// Table to map operation names to functions. + +// names must be of length <= AttachOperation::name_length_max +static AttachOperationFunctionInfo funcs[] = { + { "agentProperties", get_agent_properties }, + { "datadump", data_dump }, +#ifndef SERVICES_KERNEL + { "dumpheap", dump_heap }, +#endif // SERVICES_KERNEL + { "load", JvmtiExport::load_agent_library }, + { "properties", get_system_properties }, + { "threaddump", thread_dump }, + { "inspectheap", heap_inspection }, + { "setflag", set_flag }, + { "printflag", print_flag }, + { NULL, NULL } +}; + + + +// The Attach Listener threads services a queue. It dequeues an operation +// from the queue, examines the operation name (command), and dispatches +// to the corresponding function to perform the operation. + +static void attach_listener_thread_entry(JavaThread* thread, TRAPS) { + os::set_priority(thread, NearMaxPriority); + + if (AttachListener::pd_init() != 0) { + return; + } + AttachListener::set_initialized(); + + for (;;) { + AttachOperation* op = AttachListener::dequeue(); + if (op == NULL) { + return; // dequeue failed or shutdown + } + + ResourceMark rm; + bufferedStream st; + jint res = JNI_OK; + + // handle special detachall operation + if (strcmp(op->name(), AttachOperation::detachall_operation_name()) == 0) { + AttachListener::detachall(); + } else { + // find the function to dispatch too + AttachOperationFunctionInfo* info = NULL; + for (int i=0; funcs[i].name != NULL; i++) { + const char* name = funcs[i].name; + assert(strlen(name) <= AttachOperation::name_length_max, "operation <= name_length_max"); + if (strcmp(op->name(), name) == 0) { + info = &(funcs[i]); + break; + } + } + + // check for platform dependent attach operation + if (info == NULL) { + info = AttachListener::pd_find_operation(op->name()); + } + + if (info != NULL) { + // dispatch to the function that implements this operation + res = (info->func)(op, &st); + } else { + st.print("Operation %s not recognized!", op->name()); + res = JNI_ERR; + } + } + + // operation complete - send result and output to client + op->complete(res, &st); + } +} + +// Starts the Attach Listener thread +void AttachListener::init() { + EXCEPTION_MARK; + klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_Thread(), true, CHECK); + instanceKlassHandle klass (THREAD, k); + instanceHandle thread_oop = klass->allocate_instance_handle(CHECK); + + const char thread_name[] = "Attach Listener"; + Handle string = java_lang_String::create_from_str(thread_name, CHECK); + + // Initialize thread_oop to put it into the system threadGroup + Handle thread_group (THREAD, Universe::system_thread_group()); + JavaValue result(T_VOID); + JavaCalls::call_special(&result, thread_oop, + klass, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::threadgroup_string_void_signature(), + thread_group, + string, + CHECK); + + KlassHandle group(THREAD, SystemDictionary::threadGroup_klass()); + JavaCalls::call_special(&result, + thread_group, + group, + vmSymbolHandles::add_method_name(), + vmSymbolHandles::thread_void_signature(), + thread_oop, // ARG 1 + CHECK); + + { MutexLocker mu(Threads_lock); + JavaThread* listener_thread = new JavaThread(&attach_listener_thread_entry); + + // Check that thread and osthread were created + if (listener_thread == NULL || listener_thread->osthread() == NULL) { + vm_exit_during_initialization("java.lang.OutOfMemoryError", + "unable to create new native thread"); + } + + java_lang_Thread::set_thread(thread_oop(), listener_thread); + java_lang_Thread::set_daemon(thread_oop()); + + listener_thread->set_threadObj(thread_oop()); + Threads::add(listener_thread); + Thread::start(listener_thread); + } +} + +// Performs clean-up tasks on platforms where we can detect that the last +// client has detached +void AttachListener::detachall() { + // call the platform dependent clean-up + pd_detachall(); +} diff --git a/hotspot/src/share/vm/services/attachListener.hpp b/hotspot/src/share/vm/services/attachListener.hpp new file mode 100644 index 00000000000..7ae0c233d20 --- /dev/null +++ b/hotspot/src/share/vm/services/attachListener.hpp @@ -0,0 +1,147 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The AttachListener thread services a queue of operations that are enqueued +// by client tools. Each operation is identified by a name and has up to 3 +// arguments. The operation name is mapped to a function which performs the +// operation. The function is called with an outputStream which is can use to +// write any result data (for examples the properties command serializes +// properties names and values to the output stream). When the function +// complets the result value and any result data is returned to the client +// tool. + +#ifndef SERVICES_KERNEL + +class AttachOperation; + +typedef jint (*AttachOperationFunction)(AttachOperation* op, outputStream* out); + +struct AttachOperationFunctionInfo { + const char* name; + AttachOperationFunction func; +}; +#endif // SERVICES_KERNEL + +class AttachListener: AllStatic { + public: + static void init() KERNEL_RETURN; + static void abort() KERNEL_RETURN; + + // invoke to perform clean-up tasks when all clients detach + static void detachall() KERNEL_RETURN; + + // indicates if the Attach Listener needs to be created at startup + static bool init_at_startup() KERNEL_RETURN_(return false;); + + // indicates if we have a trigger to start the Attach Listener + static bool is_init_trigger() KERNEL_RETURN_(return false;); + +#ifdef SERVICES_KERNEL + static bool is_attach_supported() { return false; } +#else // SERVICES_KERNEL + private: + static volatile bool _initialized; + + public: + static bool is_initialized() { return _initialized; } + static void set_initialized() { _initialized = true; } + + // indicates if this VM supports attach-on-demand + static bool is_attach_supported() { return !DisableAttachMechanism; } + + // platform specific initialization + static int pd_init(); + + // platform specific operation + static AttachOperationFunctionInfo* pd_find_operation(const char* name); + + // platform specific flag change + static jint pd_set_flag(AttachOperation* op, outputStream* out); + + // platform specific detachall + static void pd_detachall(); + + // platform specific data dump + static void pd_data_dump(); + + // dequeue the next operation + static AttachOperation* dequeue(); +#endif // SERVICES_KERNEL +}; + +#ifndef SERVICES_KERNEL +class AttachOperation: public CHeapObj { + public: + enum { + name_length_max = 16, // maximum length of name + arg_length_max = 1024, // maximum length of argument + arg_count_max = 3 // maximum number of arguments + }; + + // name of special operation that can be enqueued when all + // clients detach + static char* detachall_operation_name() { return (char*)"detachall"; } + + private: + char _name[name_length_max+1]; + char _arg[arg_count_max][arg_length_max+1]; + + public: + const char* name() const { return _name; } + + // set the operation name + void set_name(char* name) { + assert(strlen(name) <= name_length_max, "exceeds maximum name length"); + strcpy(_name, name); + } + + // get an argument value + const char* arg(int i) const { + assert(i>=0 && i=0 && iname(); \ + if (name != NULL) { \ + data = (char*)name->bytes(); \ + len = name->utf8_length(); \ + } \ + HS_DTRACE_PROBE4(hotspot, class__##type, \ + data, len, (clss)->class_loader(), (shared)); \ + } + +#else // ndef DTRACE_ENABLED + +#define DTRACE_CLASSLOAD_PROBE(type, clss, shared) + +#endif + +// counters for classes loaded from class files +PerfCounter* ClassLoadingService::_classes_loaded_count = NULL; +PerfCounter* ClassLoadingService::_classes_unloaded_count = NULL; +PerfCounter* ClassLoadingService::_classbytes_loaded = NULL; +PerfCounter* ClassLoadingService::_classbytes_unloaded = NULL; + +// counters for classes loaded from shared archive +PerfCounter* ClassLoadingService::_shared_classes_loaded_count = NULL; +PerfCounter* ClassLoadingService::_shared_classes_unloaded_count = NULL; +PerfCounter* ClassLoadingService::_shared_classbytes_loaded = NULL; +PerfCounter* ClassLoadingService::_shared_classbytes_unloaded = NULL; +PerfVariable* ClassLoadingService::_class_methods_size = NULL; + +void ClassLoadingService::init() { + EXCEPTION_MARK; + + // These counters are for java.lang.management API support. + // They are created even if -XX:-UsePerfData is set and in + // that case, they will be allocated on C heap. + _classes_loaded_count = + PerfDataManager::create_counter(JAVA_CLS, "loadedClasses", + PerfData::U_Events, CHECK); + + _classes_unloaded_count = + PerfDataManager::create_counter(JAVA_CLS, "unloadedClasses", + PerfData::U_Events, CHECK); + + _shared_classes_loaded_count = + PerfDataManager::create_counter(JAVA_CLS, "sharedLoadedClasses", + PerfData::U_Events, CHECK); + + _shared_classes_unloaded_count = + PerfDataManager::create_counter(JAVA_CLS, "sharedUnloadedClasses", + PerfData::U_Events, CHECK); + + if (UsePerfData) { + _classbytes_loaded = + PerfDataManager::create_counter(SUN_CLS, "loadedBytes", + PerfData::U_Bytes, CHECK); + + _classbytes_unloaded = + PerfDataManager::create_counter(SUN_CLS, "unloadedBytes", + PerfData::U_Bytes, CHECK); + _shared_classbytes_loaded = + PerfDataManager::create_counter(SUN_CLS, "sharedLoadedBytes", + PerfData::U_Bytes, CHECK); + + _shared_classbytes_unloaded = + PerfDataManager::create_counter(SUN_CLS, "sharedUnloadedBytes", + PerfData::U_Bytes, CHECK); + _class_methods_size = + PerfDataManager::create_variable(SUN_CLS, "methodBytes", + PerfData::U_Bytes, CHECK); + } +} + +void ClassLoadingService::notify_class_unloaded(instanceKlass* k) { + DTRACE_CLASSLOAD_PROBE(unloaded, k, false); + // Classes that can be unloaded must be non-shared + _classes_unloaded_count->inc(); + + if (UsePerfData) { + // add the class size + size_t size = compute_class_size(k); + _classbytes_unloaded->inc(size); + + // Compute method size & subtract from running total. + // We are called during phase 1 of mark sweep, so it's + // still ok to iterate through methodOops here. + objArrayOop methods = k->methods(); + for (int i = 0; i < methods->length(); i++) { + _class_methods_size->inc(-methods->obj_at(i)->size()); + } + } + + if (TraceClassUnloading) { + ResourceMark rm; + tty->print_cr("[Unloading class %s]", k->external_name()); + } +} + +void ClassLoadingService::notify_class_loaded(instanceKlass* k, bool shared_class) { + DTRACE_CLASSLOAD_PROBE(loaded, k, shared_class); + PerfCounter* classes_counter = (shared_class ? _shared_classes_loaded_count + : _classes_loaded_count); + // increment the count + classes_counter->inc(); + + if (UsePerfData) { + PerfCounter* classbytes_counter = (shared_class ? _shared_classbytes_loaded + : _classbytes_loaded); + // add the class size + size_t size = compute_class_size(k); + classbytes_counter->inc(size); + } +} + +size_t ClassLoadingService::compute_class_size(instanceKlass* k) { + // lifted from ClassStatistics.do_class(klassOop k) + + size_t class_size = 0; + + class_size += k->as_klassOop()->size(); + + if (k->oop_is_instance()) { + class_size += k->methods()->size(); + class_size += k->constants()->size(); + class_size += k->local_interfaces()->size(); + class_size += k->transitive_interfaces()->size(); + // We do not have to count implementors, since we only store one! + class_size += k->fields()->size(); + } + return class_size * oopSize; +} + + +bool ClassLoadingService::set_verbose(bool verbose) { + MutexLocker m(Management_lock); + + // verbose will be set to the previous value + bool succeed = CommandLineFlags::boolAtPut((char*)"TraceClassLoading", &verbose, MANAGEMENT); + assert(succeed, "Setting TraceClassLoading flag fails"); + reset_trace_class_unloading(); + + return verbose; +} + +// Caller to this function must own Management_lock +void ClassLoadingService::reset_trace_class_unloading() { + assert(Management_lock->owned_by_self(), "Must own the Management_lock"); + bool value = MemoryService::get_verbose() || ClassLoadingService::get_verbose(); + bool succeed = CommandLineFlags::boolAtPut((char*)"TraceClassUnloading", &value, MANAGEMENT); + assert(succeed, "Setting TraceClassUnLoading flag fails"); +} + +GrowableArray* LoadedClassesEnumerator::_loaded_classes = NULL; +Thread* LoadedClassesEnumerator::_current_thread = NULL; + +LoadedClassesEnumerator::LoadedClassesEnumerator(Thread* cur_thread) { + assert(cur_thread == Thread::current(), "Check current thread"); + + int init_size = ClassLoadingService::loaded_class_count(); + _klass_handle_array = new GrowableArray(init_size); + + // For consistency of the loaded classes, grab the SystemDictionary lock + MutexLocker sd_mutex(SystemDictionary_lock); + + // Set _loaded_classes and _current_thread and begin enumerating all classes. + // Only one thread will do the enumeration at a time. + // These static variables are needed and they are used by the static method + // add_loaded_class called from classes_do(). + _loaded_classes = _klass_handle_array; + _current_thread = cur_thread; + + SystemDictionary::classes_do(&add_loaded_class); + + // FIXME: Exclude array klasses for now + // Universe::basic_type_classes_do(&add_loaded_class); +} diff --git a/hotspot/src/share/vm/services/classLoadingService.hpp b/hotspot/src/share/vm/services/classLoadingService.hpp new file mode 100644 index 00000000000..c20824e29ef --- /dev/null +++ b/hotspot/src/share/vm/services/classLoadingService.hpp @@ -0,0 +1,135 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class instanceKlass; + +// VM monitoring and management support for the Class Loading subsystem +class ClassLoadingService : public AllStatic { +private: + // Counters for classes loaded from class files + static PerfCounter* _classes_loaded_count; + static PerfCounter* _classes_unloaded_count; + static PerfCounter* _classbytes_loaded; + static PerfCounter* _classbytes_unloaded; + + // Counters for classes loaded from shared archive + static PerfCounter* _shared_classes_loaded_count; + static PerfCounter* _shared_classes_unloaded_count; + static PerfCounter* _shared_classbytes_loaded; + static PerfCounter* _shared_classbytes_unloaded; + + static PerfVariable* _class_methods_size; + + static size_t compute_class_size(instanceKlass* k); + +public: + static void init(); + + static bool get_verbose() { return TraceClassLoading; } + static bool set_verbose(bool verbose); + static void reset_trace_class_unloading(); + + static jlong loaded_class_count() { + return _classes_loaded_count->get_value() + _shared_classes_loaded_count->get_value(); + } + static jlong unloaded_class_count() { + return _classes_unloaded_count->get_value() + _shared_classes_unloaded_count->get_value(); + } + static jlong loaded_class_bytes() { + if (UsePerfData) { + return _classbytes_loaded->get_value() + _shared_classbytes_loaded->get_value(); + } else { + return -1; + } + } + static jlong unloaded_class_bytes() { + if (UsePerfData) { + return _classbytes_unloaded->get_value() + _shared_classbytes_unloaded->get_value(); + } else { + return -1; + } + } + + static jlong loaded_shared_class_count() { + return _shared_classes_loaded_count->get_value(); + } + static jlong unloaded_shared_class_count() { + return _shared_classes_unloaded_count->get_value(); + } + static jlong loaded_shared_class_bytes() { + if (UsePerfData) { + return _shared_classbytes_loaded->get_value(); + } else { + return -1; + } + } + static jlong unloaded_shared_class_bytes() { + if (UsePerfData) { + return _shared_classbytes_unloaded->get_value(); + } else { + return -1; + } + } + static jlong class_method_data_size() { + return (UsePerfData ? _class_methods_size->get_value() : -1); + } + + static void notify_class_loaded(instanceKlass* k, bool shared_class); + // All unloaded classes are non-shared + static void notify_class_unloaded(instanceKlass* k); + static void add_class_method_size(int size) { + if (UsePerfData) { + _class_methods_size->inc(size); + } + } +}; + +// FIXME: make this piece of code to be shared by M&M and JVMTI +class LoadedClassesEnumerator : public StackObj { +private: + static GrowableArray* _loaded_classes; + // _current_thread is for creating a KlassHandle with a faster version constructor + static Thread* _current_thread; + + GrowableArray* _klass_handle_array; + +public: + LoadedClassesEnumerator(Thread* cur_thread); + + int num_loaded_classes() { return _klass_handle_array->length(); } + KlassHandle get_klass(int index) { return _klass_handle_array->at(index); } + + static void add_loaded_class(klassOop k) { + // FIXME: For now - don't include array klasses + // The spec is unclear at this point to count array klasses or not + // and also indirect creation of array of super class and secondaries + // + // for (klassOop l = k; l != NULL; l = Klass::cast(l)->array_klass_or_null()) { + // KlassHandle h(_current_thread, l); + // _loaded_classes->append(h); + // } + KlassHandle h(_current_thread, k); + _loaded_classes->append(h); + } +}; diff --git a/hotspot/src/share/vm/services/dtraceAttacher.cpp b/hotspot/src/share/vm/services/dtraceAttacher.cpp new file mode 100644 index 00000000000..ec2c8c82b9c --- /dev/null +++ b/hotspot/src/share/vm/services/dtraceAttacher.cpp @@ -0,0 +1,138 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_dtraceAttacher.cpp.incl" + +#ifdef SOLARIS + +class VM_DeoptimizeTheWorld : public VM_Operation { + public: + VMOp_Type type() const { + return VMOp_DeoptimizeTheWorld; + } + void doit() { + CodeCache::mark_all_nmethods_for_deoptimization(); + ResourceMark rm; + DeoptimizationMarker dm; + // Deoptimize all activations depending on marked methods + Deoptimization::deoptimize_dependents(); + + // Mark the dependent methods non entrant + CodeCache::make_marked_nmethods_not_entrant(); + } +}; + +static void set_bool_flag(const char* flag, bool value) { + CommandLineFlags::boolAtPut((char*)flag, strlen(flag), &value, + ATTACH_ON_DEMAND); +} + +// Enable only the "fine grained" flags. Do *not* touch +// the overall "ExtendedDTraceProbes" flag. +void DTrace::enable_dprobes(int probes) { + bool changed = false; + if (!DTraceAllocProbes && (probes & DTRACE_ALLOC_PROBES)) { + set_bool_flag("DTraceAllocProbes", true); + changed = true; + } + if (!DTraceMethodProbes && (probes & DTRACE_METHOD_PROBES)) { + set_bool_flag("DTraceMethodProbes", true); + changed = true; + } + if (!DTraceMonitorProbes && (probes & DTRACE_MONITOR_PROBES)) { + set_bool_flag("DTraceMonitorProbes", true); + changed = true; + } + + if (changed) { + // one or more flags changed, need to deoptimize + VM_DeoptimizeTheWorld op; + VMThread::execute(&op); + } +} + +// Disable only the "fine grained" flags. Do *not* touch +// the overall "ExtendedDTraceProbes" flag. +void DTrace::disable_dprobes(int probes) { + bool changed = false; + if (DTraceAllocProbes && (probes & DTRACE_ALLOC_PROBES)) { + set_bool_flag("DTraceAllocProbes", false); + changed = true; + } + if (DTraceMethodProbes && (probes & DTRACE_METHOD_PROBES)) { + set_bool_flag("DTraceMethodProbes", false); + changed = true; + } + if (DTraceMonitorProbes && (probes & DTRACE_MONITOR_PROBES)) { + set_bool_flag("DTraceMonitorProbes", false); + changed = true; + } + if (changed) { + // one or more flags changed, need to deoptimize + VM_DeoptimizeTheWorld op; + VMThread::execute(&op); + } +} + +// Do clean-up on "all door clients detached" event. +void DTrace::detach_all_clients() { + /* + * We restore the state of the fine grained flags + * to be consistent with overall ExtendedDTraceProbes. + * This way, we will honour command line setting or the + * last explicit modification of ExtendedDTraceProbes by + * a call to set_extended_dprobes. + */ + if (ExtendedDTraceProbes) { + enable_dprobes(DTRACE_ALL_PROBES); + } else { + disable_dprobes(DTRACE_ALL_PROBES); + } +} + +void DTrace::set_extended_dprobes(bool flag) { + // explicit setting of ExtendedDTraceProbes flag + set_bool_flag("ExtendedDTraceProbes", flag); + + // make sure that the fine grained flags reflect the change. + if (flag) { + enable_dprobes(DTRACE_ALL_PROBES); + } else { + /* + * FIXME: Revisit this: currently all-client-detach detection + * does not work and hence disabled. The following scheme does + * not work. So, we have to disable fine-grained flags here. + * + * disable_dprobes call has to be delayed till next "detach all "event. + * This is to be done so that concurrent DTrace clients that may + * have enabled one or more fine grained dprobes and may be running + * still. On "detach all" clients event, we would sync ExtendedDTraceProbes + * with fine grained flags which would take care of disabling fine grained flags. + */ + disable_dprobes(DTRACE_ALL_PROBES); + } +} + +#endif /* SOLARIS */ diff --git a/hotspot/src/share/vm/services/dtraceAttacher.hpp b/hotspot/src/share/vm/services/dtraceAttacher.hpp new file mode 100644 index 00000000000..089decd9d34 --- /dev/null +++ b/hotspot/src/share/vm/services/dtraceAttacher.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#define DTRACE_ALLOC_PROBES 0x1 +#define DTRACE_METHOD_PROBES 0x2 +#define DTRACE_MONITOR_PROBES 0x4 +#define DTRACE_ALL_PROBES (DTRACE_ALLOC_PROBES | \ + DTRACE_METHOD_PROBES | \ + DTRACE_MONITOR_PROBES) + +class DTrace : public AllStatic { + private: + // disable one or more probes - OR above constants + static void disable_dprobes(int probe_types); + + public: + // enable one or more probes - OR above constants + static void enable_dprobes(int probe_types); + // all clients detached, do any clean-up + static void detach_all_clients(); + // set ExtendedDTraceProbes flag + static void set_extended_dprobes(bool value); +}; diff --git a/hotspot/src/share/vm/services/heapDumper.cpp b/hotspot/src/share/vm/services/heapDumper.cpp new file mode 100644 index 00000000000..cdd79a61817 --- /dev/null +++ b/hotspot/src/share/vm/services/heapDumper.cpp @@ -0,0 +1,1773 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_heapDumper.cpp.incl" + +/* + * HPROF binary format - description copied from: + * src/share/demo/jvmti/hprof/hprof_io.c + * + * + * header "JAVA PROFILE 1.0.1" or "JAVA PROFILE 1.0.2" + * (0-terminated) + * + * u4 size of identifiers. Identifiers are used to represent + * UTF8 strings, objects, stack traces, etc. They usually + * have the same size as host pointers. For example, on + * Solaris and Win32, the size is 4. + * u4 high word + * u4 low word number of milliseconds since 0:00 GMT, 1/1/70 + * [record]* a sequence of records. + * + * + * Record format: + * + * u1 a TAG denoting the type of the record + * u4 number of *microseconds* since the time stamp in the + * header. (wraps around in a little more than an hour) + * u4 number of bytes *remaining* in the record. Note that + * this number excludes the tag and the length field itself. + * [u1]* BODY of the record (a sequence of bytes) + * + * + * The following TAGs are supported: + * + * TAG BODY notes + *---------------------------------------------------------- + * HPROF_UTF8 a UTF8-encoded name + * + * id name ID + * [u1]* UTF8 characters (no trailing zero) + * + * HPROF_LOAD_CLASS a newly loaded class + * + * u4 class serial number (> 0) + * id class object ID + * u4 stack trace serial number + * id class name ID + * + * HPROF_UNLOAD_CLASS an unloading class + * + * u4 class serial_number + * + * HPROF_FRAME a Java stack frame + * + * id stack frame ID + * id method name ID + * id method signature ID + * id source file name ID + * u4 class serial number + * i4 line number. >0: normal + * -1: unknown + * -2: compiled method + * -3: native method + * + * HPROF_TRACE a Java stack trace + * + * u4 stack trace serial number + * u4 thread serial number + * u4 number of frames + * [id]* stack frame IDs + * + * + * HPROF_ALLOC_SITES a set of heap allocation sites, obtained after GC + * + * u2 flags 0x0001: incremental vs. complete + * 0x0002: sorted by allocation vs. live + * 0x0004: whether to force a GC + * u4 cutoff ratio + * u4 total live bytes + * u4 total live instances + * u8 total bytes allocated + * u8 total instances allocated + * u4 number of sites that follow + * [u1 is_array: 0: normal object + * 2: object array + * 4: boolean array + * 5: char array + * 6: float array + * 7: double array + * 8: byte array + * 9: short array + * 10: int array + * 11: long array + * u4 class serial number (may be zero during startup) + * u4 stack trace serial number + * u4 number of bytes alive + * u4 number of instances alive + * u4 number of bytes allocated + * u4]* number of instance allocated + * + * HPROF_START_THREAD a newly started thread. + * + * u4 thread serial number (> 0) + * id thread object ID + * u4 stack trace serial number + * id thread name ID + * id thread group name ID + * id thread group parent name ID + * + * HPROF_END_THREAD a terminating thread. + * + * u4 thread serial number + * + * HPROF_HEAP_SUMMARY heap summary + * + * u4 total live bytes + * u4 total live instances + * u8 total bytes allocated + * u8 total instances allocated + * + * HPROF_HEAP_DUMP denote a heap dump + * + * [heap dump sub-records]* + * + * There are four kinds of heap dump sub-records: + * + * u1 sub-record type + * + * HPROF_GC_ROOT_UNKNOWN unknown root + * + * id object ID + * + * HPROF_GC_ROOT_THREAD_OBJ thread object + * + * id thread object ID (may be 0 for a + * thread newly attached through JNI) + * u4 thread sequence number + * u4 stack trace sequence number + * + * HPROF_GC_ROOT_JNI_GLOBAL JNI global ref root + * + * id object ID + * id JNI global ref ID + * + * HPROF_GC_ROOT_JNI_LOCAL JNI local ref + * + * id object ID + * u4 thread serial number + * u4 frame # in stack trace (-1 for empty) + * + * HPROF_GC_ROOT_JAVA_FRAME Java stack frame + * + * id object ID + * u4 thread serial number + * u4 frame # in stack trace (-1 for empty) + * + * HPROF_GC_ROOT_NATIVE_STACK Native stack + * + * id object ID + * u4 thread serial number + * + * HPROF_GC_ROOT_STICKY_CLASS System class + * + * id object ID + * + * HPROF_GC_ROOT_THREAD_BLOCK Reference from thread block + * + * id object ID + * u4 thread serial number + * + * HPROF_GC_ROOT_MONITOR_USED Busy monitor + * + * id object ID + * + * HPROF_GC_CLASS_DUMP dump of a class object + * + * id class object ID + * u4 stack trace serial number + * id super class object ID + * id class loader object ID + * id signers object ID + * id protection domain object ID + * id reserved + * id reserved + * + * u4 instance size (in bytes) + * + * u2 size of constant pool + * [u2, constant pool index, + * ty, type + * 2: object + * 4: boolean + * 5: char + * 6: float + * 7: double + * 8: byte + * 9: short + * 10: int + * 11: long + * vl]* and value + * + * u2 number of static fields + * [id, static field name, + * ty, type, + * vl]* and value + * + * u2 number of inst. fields (not inc. super) + * [id, instance field name, + * ty]* type + * + * HPROF_GC_INSTANCE_DUMP dump of a normal object + * + * id object ID + * u4 stack trace serial number + * id class object ID + * u4 number of bytes that follow + * [vl]* instance field values (class, followed + * by super, super's super ...) + * + * HPROF_GC_OBJ_ARRAY_DUMP dump of an object array + * + * id array object ID + * u4 stack trace serial number + * u4 number of elements + * id array class ID + * [id]* elements + * + * HPROF_GC_PRIM_ARRAY_DUMP dump of a primitive array + * + * id array object ID + * u4 stack trace serial number + * u4 number of elements + * u1 element type + * 4: boolean array + * 5: char array + * 6: float array + * 7: double array + * 8: byte array + * 9: short array + * 10: int array + * 11: long array + * [u1]* elements + * + * HPROF_CPU_SAMPLES a set of sample traces of running threads + * + * u4 total number of samples + * u4 # of traces + * [u4 # of samples + * u4]* stack trace serial number + * + * HPROF_CONTROL_SETTINGS the settings of on/off switches + * + * u4 0x00000001: alloc traces on/off + * 0x00000002: cpu sampling on/off + * u2 stack trace depth + * + * + * When the header is "JAVA PROFILE 1.0.2" a heap dump can optionally + * be generated as a sequence of heap dump segments. This sequence is + * terminated by an end record. The additional tags allowed by format + * "JAVA PROFILE 1.0.2" are: + * + * HPROF_HEAP_DUMP_SEGMENT denote a heap dump segment + * + * [heap dump sub-records]* + * The same sub-record types allowed by HPROF_HEAP_DUMP + * + * HPROF_HEAP_DUMP_END denotes the end of a heap dump + * + */ + + +// HPROF tags + +typedef enum { + // top-level records + HPROF_UTF8 = 0x01, + HPROF_LOAD_CLASS = 0x02, + HPROF_UNLOAD_CLASS = 0x03, + HPROF_FRAME = 0x04, + HPROF_TRACE = 0x05, + HPROF_ALLOC_SITES = 0x06, + HPROF_HEAP_SUMMARY = 0x07, + HPROF_START_THREAD = 0x0A, + HPROF_END_THREAD = 0x0B, + HPROF_HEAP_DUMP = 0x0C, + HPROF_CPU_SAMPLES = 0x0D, + HPROF_CONTROL_SETTINGS = 0x0E, + + // 1.0.2 record types + HPROF_HEAP_DUMP_SEGMENT = 0x1C, + HPROF_HEAP_DUMP_END = 0x2C, + + // field types + HPROF_ARRAY_OBJECT = 0x01, + HPROF_NORMAL_OBJECT = 0x02, + HPROF_BOOLEAN = 0x04, + HPROF_CHAR = 0x05, + HPROF_FLOAT = 0x06, + HPROF_DOUBLE = 0x07, + HPROF_BYTE = 0x08, + HPROF_SHORT = 0x09, + HPROF_INT = 0x0A, + HPROF_LONG = 0x0B, + + // data-dump sub-records + HPROF_GC_ROOT_UNKNOWN = 0xFF, + HPROF_GC_ROOT_JNI_GLOBAL = 0x01, + HPROF_GC_ROOT_JNI_LOCAL = 0x02, + HPROF_GC_ROOT_JAVA_FRAME = 0x03, + HPROF_GC_ROOT_NATIVE_STACK = 0x04, + HPROF_GC_ROOT_STICKY_CLASS = 0x05, + HPROF_GC_ROOT_THREAD_BLOCK = 0x06, + HPROF_GC_ROOT_MONITOR_USED = 0x07, + HPROF_GC_ROOT_THREAD_OBJ = 0x08, + HPROF_GC_CLASS_DUMP = 0x20, + HPROF_GC_INSTANCE_DUMP = 0x21, + HPROF_GC_OBJ_ARRAY_DUMP = 0x22, + HPROF_GC_PRIM_ARRAY_DUMP = 0x23 +} hprofTag; + +// Default stack trace ID (used for dummy HPROF_TRACE record) +enum { + STACK_TRACE_ID = 1 +}; + + +// Supports I/O operations on a dump file + +class DumpWriter : public StackObj { + private: + enum { + io_buffer_size = 8*M + }; + + int _fd; // file descriptor (-1 if dump file not open) + jlong _bytes_written; // number of byte written to dump file + + char* _buffer; // internal buffer + int _size; + int _pos; + + char* _error; // error message when I/O fails + + void set_file_descriptor(int fd) { _fd = fd; } + int file_descriptor() const { return _fd; } + + char* buffer() const { return _buffer; } + int buffer_size() const { return _size; } + int position() const { return _pos; } + void set_position(int pos) { _pos = pos; } + + void set_error(const char* error) { _error = (char*)os::strdup(error); } + + // all I/O go through this function + void write_internal(void* s, int len); + + public: + DumpWriter(const char* path); + ~DumpWriter(); + + void close(); + bool is_open() const { return file_descriptor() >= 0; } + void flush(); + + // total number of bytes written to the disk + jlong bytes_written() const { return _bytes_written; } + + // adjust the number of bytes written to disk (used to keep the count + // of the number of bytes written in case of rewrites) + void adjust_bytes_written(jlong n) { _bytes_written += n; } + + // number of (buffered) bytes as yet unwritten to the dump file + jlong bytes_unwritten() const { return (jlong)position(); } + + char* error() const { return _error; } + + jlong current_offset(); + void seek_to_offset(jlong pos); + + // writer functions + void write_raw(void* s, int len); + void write_u1(u1 x) { write_raw((void*)&x, 1); } + void write_u2(u2 x); + void write_u4(u4 x); + void write_u8(u8 x); + void write_objectID(oop o); + void write_classID(Klass* k); +}; + +DumpWriter::DumpWriter(const char* path) { + // try to allocate an I/O buffer of io_buffer_size. If there isn't + // sufficient memory then reduce size until we can allocate something. + _size = io_buffer_size; + do { + _buffer = (char*)os::malloc(_size); + if (_buffer == NULL) { + _size = _size >> 1; + } + } while (_buffer == NULL && _size > 0); + assert((_size > 0 && _buffer != NULL) || (_size == 0 && _buffer == NULL), "sanity check"); + _pos = 0; + _error = NULL; + _bytes_written = 0L; + _fd = os::create_binary_file(path, false); // don't replace existing file + + // if the open failed we record the error + if (_fd < 0) { + _error = (char*)os::strdup(strerror(errno)); + } +} + +DumpWriter::~DumpWriter() { + // flush and close dump file + if (file_descriptor() >= 0) { + close(); + } + if (_buffer != NULL) os::free(_buffer); + if (_error != NULL) os::free(_error); +} + +// closes dump file (if open) +void DumpWriter::close() { + // flush and close dump file + if (file_descriptor() >= 0) { + flush(); + ::close(file_descriptor()); + } +} + +// write directly to the file +void DumpWriter::write_internal(void* s, int len) { + if (is_open()) { + int n = ::write(file_descriptor(), s, len); + if (n > 0) { + _bytes_written += n; + } + if (n != len) { + if (n < 0) { + set_error(strerror(errno)); + } else { + set_error("file size limit"); + } + ::close(file_descriptor()); + set_file_descriptor(-1); + } + } +} + +// write raw bytes +void DumpWriter::write_raw(void* s, int len) { + if (is_open()) { + // flush buffer to make toom + if ((position()+ len) >= buffer_size()) { + flush(); + } + + // buffer not available or too big to buffer it + if ((buffer() == NULL) || (len >= buffer_size())) { + write_internal(s, len); + } else { + // Should optimize this for u1/u2/u4/u8 sizes. + memcpy(buffer() + position(), s, len); + set_position(position() + len); + } + } +} + +// flush any buffered bytes to the file +void DumpWriter::flush() { + if (is_open() && position() > 0) { + write_internal(buffer(), position()); + set_position(0); + } +} + + +jlong DumpWriter::current_offset() { + if (is_open()) { + // the offset is the file offset plus whatever we have buffered + jlong offset = os::current_file_offset(file_descriptor()); + assert(offset >= 0, "lseek failed"); + return offset + (jlong)position(); + } else { + return (jlong)-1; + } +} + +void DumpWriter::seek_to_offset(jlong off) { + assert(off >= 0, "bad offset"); + + // need to flush before seeking + flush(); + + // may be closed due to I/O error + if (is_open()) { + jlong n = os::seek_to_file_offset(file_descriptor(), off); + assert(n >= 0, "lseek failed"); + } +} + +void DumpWriter::write_u2(u2 x) { + u2 v; + Bytes::put_Java_u2((address)&v, x); + write_raw((void*)&v, 2); +} + +void DumpWriter::write_u4(u4 x) { + u4 v; + Bytes::put_Java_u4((address)&v, x); + write_raw((void*)&v, 4); +} + +void DumpWriter::write_u8(u8 x) { + u8 v; + Bytes::put_Java_u8((address)&v, x); + write_raw((void*)&v, 8); +} + +void DumpWriter::write_objectID(oop o) { + address a = (address)((uintptr_t)o); +#ifdef _LP64 + write_u8((u8)a); +#else + write_u4((u4)a); +#endif +} + +// We use java mirror as the class ID +void DumpWriter::write_classID(Klass* k) { + write_objectID(k->java_mirror()); +} + + + +// Support class with a collection of functions used when dumping the heap + +class DumperSupport : AllStatic { + public: + + // write a header of the given type + static void write_header(DumpWriter* writer, hprofTag tag, u4 len); + + // returns hprof tag for the given type signature + static hprofTag sig2tag(symbolOop sig); + // returns hprof tag for the given basic type + static hprofTag type2tag(BasicType type); + + // returns the size of the instance of the given class + static u4 instance_size(klassOop k); + + // dump a jfloat + static void dump_float(DumpWriter* writer, jfloat f); + // dump a jdouble + static void dump_double(DumpWriter* writer, jdouble d); + // dumps the raw value of the given field + static void dump_field_value(DumpWriter* writer, char type, address addr); + // dumps static fields of the given class + static void dump_static_fields(DumpWriter* writer, klassOop k); + // dump the raw values of the instance fields of the given object + static void dump_instance_fields(DumpWriter* writer, oop o); + // dumps the definition of the instance fields for a given class + static void dump_instance_field_descriptors(DumpWriter* writer, klassOop k); + // creates HPROF_GC_INSTANCE_DUMP record for the given object + static void dump_instance(DumpWriter* writer, oop o); + // creates HPROF_GC_CLASS_DUMP record for the given class and each of its + // array classes + static void dump_class_and_array_classes(DumpWriter* writer, klassOop k); + // creates HPROF_GC_CLASS_DUMP record for a given primitive array + // class (and each multi-dimensional array class too) + static void dump_basic_type_array_class(DumpWriter* writer, klassOop k); + + // creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array + static void dump_object_array(DumpWriter* writer, objArrayOop array); + // creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array + static void dump_prim_array(DumpWriter* writer, typeArrayOop array); +}; + +// write a header of the given type +void DumperSupport:: write_header(DumpWriter* writer, hprofTag tag, u4 len) { + writer->write_u1((u1)tag); + writer->write_u4(0); // current ticks + writer->write_u4(len); +} + +// returns hprof tag for the given type signature +hprofTag DumperSupport::sig2tag(symbolOop sig) { + switch (sig->byte_at(0)) { + case JVM_SIGNATURE_CLASS : return HPROF_NORMAL_OBJECT; + case JVM_SIGNATURE_ARRAY : return HPROF_NORMAL_OBJECT; + case JVM_SIGNATURE_BYTE : return HPROF_BYTE; + case JVM_SIGNATURE_CHAR : return HPROF_CHAR; + case JVM_SIGNATURE_FLOAT : return HPROF_FLOAT; + case JVM_SIGNATURE_DOUBLE : return HPROF_DOUBLE; + case JVM_SIGNATURE_INT : return HPROF_INT; + case JVM_SIGNATURE_LONG : return HPROF_LONG; + case JVM_SIGNATURE_SHORT : return HPROF_SHORT; + case JVM_SIGNATURE_BOOLEAN : return HPROF_BOOLEAN; + default : ShouldNotReachHere(); /* to shut up compiler */ return HPROF_BYTE; + } +} + +hprofTag DumperSupport::type2tag(BasicType type) { + switch (type) { + case T_BYTE : return HPROF_BYTE; + case T_CHAR : return HPROF_CHAR; + case T_FLOAT : return HPROF_FLOAT; + case T_DOUBLE : return HPROF_DOUBLE; + case T_INT : return HPROF_INT; + case T_LONG : return HPROF_LONG; + case T_SHORT : return HPROF_SHORT; + case T_BOOLEAN : return HPROF_BOOLEAN; + default : ShouldNotReachHere(); /* to shut up compiler */ return HPROF_BYTE; + } +} + +// dump a jfloat +void DumperSupport::dump_float(DumpWriter* writer, jfloat f) { + if (g_isnan(f)) { + writer->write_u4(0x7fc00000); // collapsing NaNs + } else { + union { + int i; + float f; + } u; + u.f = (float)f; + writer->write_u4((u4)u.i); + } +} + +// dump a jdouble +void DumperSupport::dump_double(DumpWriter* writer, jdouble d) { + union { + jlong l; + double d; + } u; + if (g_isnan(d)) { // collapsing NaNs + u.l = (jlong)(0x7ff80000); + u.l = (u.l << 32); + } else { + u.d = (double)d; + } + writer->write_u8((u8)u.l); +} + +// dumps the raw value of the given field +void DumperSupport::dump_field_value(DumpWriter* writer, char type, address addr) { + switch (type) { + case JVM_SIGNATURE_CLASS : + case JVM_SIGNATURE_ARRAY : { + oop* f = (oop*)addr; + oop o = *f; + + // reflection and sun.misc.Unsafe classes may have a reference to a + // klassOop so filter it out. + if (o != NULL && o->is_klass()) { + o = NULL; + } + + // FIXME: When sharing is enabled we don't emit field references to objects + // in shared spaces. We can remove this once we write records for the classes + // and strings that are shared. + if (o != NULL && o->is_shared()) { + o = NULL; + } + writer->write_objectID(o); + break; + } + case JVM_SIGNATURE_BYTE : { + jbyte* b = (jbyte*)addr; + writer->write_u1((u1)*b); + break; + } + case JVM_SIGNATURE_CHAR : { + jchar* c = (jchar*)addr; + writer->write_u2((u2)*c); + break; + } + case JVM_SIGNATURE_SHORT : { + jshort* s = (jshort*)addr; + writer->write_u2((u2)*s); + break; + } + case JVM_SIGNATURE_FLOAT : { + jfloat* f = (jfloat*)addr; + dump_float(writer, *f); + break; + } + case JVM_SIGNATURE_DOUBLE : { + jdouble* f = (jdouble*)addr; + dump_double(writer, *f); + break; + } + case JVM_SIGNATURE_INT : { + jint* i = (jint*)addr; + writer->write_u4((u4)*i); + break; + } + case JVM_SIGNATURE_LONG : { + jlong* l = (jlong*)addr; + writer->write_u8((u8)*l); + break; + } + case JVM_SIGNATURE_BOOLEAN : { + jboolean* b = (jboolean*)addr; + writer->write_u1((u1)*b); + break; + } + default : ShouldNotReachHere(); + } +} + +// returns the size of the instance of the given class +u4 DumperSupport::instance_size(klassOop k) { + HandleMark hm; + instanceKlassHandle ikh = instanceKlassHandle(Thread::current(), k); + + int size = 0; + + for (FieldStream fld(ikh, false, false); !fld.eos(); fld.next()) { + if (!fld.access_flags().is_static()) { + symbolOop sig = fld.signature(); + switch (sig->byte_at(0)) { + case JVM_SIGNATURE_CLASS : + case JVM_SIGNATURE_ARRAY : size += oopSize; break; + + case JVM_SIGNATURE_BYTE : + case JVM_SIGNATURE_BOOLEAN : size += 1; break; + + case JVM_SIGNATURE_CHAR : + case JVM_SIGNATURE_SHORT : size += 2; break; + + case JVM_SIGNATURE_INT : + case JVM_SIGNATURE_FLOAT : size += 4; break; + + case JVM_SIGNATURE_LONG : + case JVM_SIGNATURE_DOUBLE : size += 8; break; + + default : ShouldNotReachHere(); + } + } + } + return (u4)size; +} + +// dumps static fields of the given class +void DumperSupport::dump_static_fields(DumpWriter* writer, klassOop k) { + HandleMark hm; + instanceKlassHandle ikh = instanceKlassHandle(Thread::current(), k); + + // pass 1 - count the static fields + u2 field_count = 0; + for (FieldStream fldc(ikh, true, true); !fldc.eos(); fldc.next()) { + if (fldc.access_flags().is_static()) field_count++; + } + + writer->write_u2(field_count); + + // pass 2 - dump the field descriptors and raw values + for (FieldStream fld(ikh, true, true); !fld.eos(); fld.next()) { + if (fld.access_flags().is_static()) { + symbolOop sig = fld.signature(); + + writer->write_objectID(fld.name()); // name + writer->write_u1(sig2tag(sig)); // type + + // value + int offset = fld.offset(); + address addr = (address)k + offset; + + dump_field_value(writer, sig->byte_at(0), addr); + } + } +} + +// dump the raw values of the instance fields of the given object +void DumperSupport::dump_instance_fields(DumpWriter* writer, oop o) { + HandleMark hm; + instanceKlassHandle ikh = instanceKlassHandle(Thread::current(), o->klass()); + + for (FieldStream fld(ikh, false, false); !fld.eos(); fld.next()) { + if (!fld.access_flags().is_static()) { + symbolOop sig = fld.signature(); + address addr = (address)o + fld.offset(); + + dump_field_value(writer, sig->byte_at(0), addr); + } + } +} + +// dumps the definition of the instance fields for a given class +void DumperSupport::dump_instance_field_descriptors(DumpWriter* writer, klassOop k) { + HandleMark hm; + instanceKlassHandle ikh = instanceKlassHandle(Thread::current(), k); + + // pass 1 - count the instance fields + u2 field_count = 0; + for (FieldStream fldc(ikh, true, true); !fldc.eos(); fldc.next()) { + if (!fldc.access_flags().is_static()) field_count++; + } + + writer->write_u2(field_count); + + // pass 2 - dump the field descriptors + for (FieldStream fld(ikh, true, true); !fld.eos(); fld.next()) { + if (!fld.access_flags().is_static()) { + symbolOop sig = fld.signature(); + + writer->write_objectID(fld.name()); // name + writer->write_u1(sig2tag(sig)); // type + } + } +} + +// creates HPROF_GC_INSTANCE_DUMP record for the given object +void DumperSupport::dump_instance(DumpWriter* writer, oop o) { + klassOop k = o->klass(); + + writer->write_u1(HPROF_GC_INSTANCE_DUMP); + writer->write_objectID(o); + writer->write_u4(STACK_TRACE_ID); + + // class ID + writer->write_classID(Klass::cast(k)); + + // number of bytes that follow + writer->write_u4(instance_size(k) ); + + // field values + dump_instance_fields(writer, o); +} + +// creates HPROF_GC_CLASS_DUMP record for the given class and each of +// its array classes +void DumperSupport::dump_class_and_array_classes(DumpWriter* writer, klassOop k) { + Klass* klass = Klass::cast(k); + assert(klass->oop_is_instance(), "not an instanceKlass"); + instanceKlass* ik = (instanceKlass*)klass; + + writer->write_u1(HPROF_GC_CLASS_DUMP); + + // class ID + writer->write_classID(ik); + writer->write_u4(STACK_TRACE_ID); + + // super class ID + klassOop java_super = ik->java_super(); + if (java_super == NULL) { + writer->write_objectID(NULL); + } else { + writer->write_classID(Klass::cast(java_super)); + } + + writer->write_objectID(ik->class_loader()); + writer->write_objectID(ik->signers()); + writer->write_objectID(ik->protection_domain()); + + // reserved + writer->write_objectID(NULL); + writer->write_objectID(NULL); + + // instance size + writer->write_u4(DumperSupport::instance_size(k)); + + // size of constant pool - ignored by HAT 1.1 + writer->write_u2(0); + + // number of static fields + dump_static_fields(writer, k); + + // description of instance fields + dump_instance_field_descriptors(writer, k); + + // array classes + k = klass->array_klass_or_null(); + while (k != NULL) { + Klass* klass = Klass::cast(k); + assert(klass->oop_is_objArray(), "not an objArrayKlass"); + + writer->write_u1(HPROF_GC_CLASS_DUMP); + writer->write_classID(klass); + writer->write_u4(STACK_TRACE_ID); + + // super class of array classes is java.lang.Object + java_super = klass->java_super(); + assert(java_super != NULL, "checking"); + writer->write_classID(Klass::cast(java_super)); + + writer->write_objectID(ik->class_loader()); + writer->write_objectID(ik->signers()); + writer->write_objectID(ik->protection_domain()); + + writer->write_objectID(NULL); // reserved + writer->write_objectID(NULL); + writer->write_u4(0); // instance size + writer->write_u2(0); // constant pool + writer->write_u2(0); // static fields + writer->write_u2(0); // instance fields + + // get the array class for the next rank + k = klass->array_klass_or_null(); + } +} + +// creates HPROF_GC_CLASS_DUMP record for a given primitive array +// class (and each multi-dimensional array class too) +void DumperSupport::dump_basic_type_array_class(DumpWriter* writer, klassOop k) { + // array classes + while (k != NULL) { + Klass* klass = Klass::cast(k); + + writer->write_u1(HPROF_GC_CLASS_DUMP); + writer->write_classID(klass); + writer->write_u4(STACK_TRACE_ID); + + // super class of array classes is java.lang.Object + klassOop java_super = klass->java_super(); + assert(java_super != NULL, "checking"); + writer->write_classID(Klass::cast(java_super)); + + writer->write_objectID(NULL); // loader + writer->write_objectID(NULL); // signers + writer->write_objectID(NULL); // protection domain + + writer->write_objectID(NULL); // reserved + writer->write_objectID(NULL); + writer->write_u4(0); // instance size + writer->write_u2(0); // constant pool + writer->write_u2(0); // static fields + writer->write_u2(0); // instance fields + + // get the array class for the next rank + k = klass->array_klass_or_null(); + } +} + +// creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array +void DumperSupport::dump_object_array(DumpWriter* writer, objArrayOop array) { + + // filter this + if (array->klass() == Universe::systemObjArrayKlassObj()) return; + + writer->write_u1(HPROF_GC_OBJ_ARRAY_DUMP); + writer->write_objectID(array); + writer->write_u4(STACK_TRACE_ID); + writer->write_u4((u4)array->length()); + + // array class ID + writer->write_classID(Klass::cast(array->klass())); + + // [id]* elements + for (int index=0; indexlength(); index++) { + oop o = array->obj_at(index); + writer->write_objectID(o); + } +} + +#define WRITE_ARRAY(Array, Type, Size) \ + for (int i=0; ilength(); i++) { writer->write_##Size((Size)array->Type##_at(i)); } + + +// creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array +void DumperSupport::dump_prim_array(DumpWriter* writer, typeArrayOop array) { + BasicType type = typeArrayKlass::cast(array->klass())->element_type(); + + writer->write_u1(HPROF_GC_PRIM_ARRAY_DUMP); + writer->write_objectID(array); + writer->write_u4(STACK_TRACE_ID); + writer->write_u4((u4)array->length()); + writer->write_u1(type2tag(type)); + + // nothing to copy + if (array->length() == 0) { + return; + } + + // If the byte ordering is big endian then we can copy most types directly + int length_in_bytes = array->length() * type2aelembytes[type]; + assert(length_in_bytes > 0, "nothing to copy"); + + switch (type) { + case T_INT : { + if (Bytes::is_Java_byte_ordering_different()) { + WRITE_ARRAY(array, int, u4); + } else { + writer->write_raw((void*)(array->int_at_addr(0)), length_in_bytes); + } + break; + } + case T_BYTE : { + writer->write_raw((void*)(array->byte_at_addr(0)), length_in_bytes); + break; + } + case T_CHAR : { + if (Bytes::is_Java_byte_ordering_different()) { + WRITE_ARRAY(array, char, u2); + } else { + writer->write_raw((void*)(array->char_at_addr(0)), length_in_bytes); + } + break; + } + case T_SHORT : { + if (Bytes::is_Java_byte_ordering_different()) { + WRITE_ARRAY(array, short, u2); + } else { + writer->write_raw((void*)(array->short_at_addr(0)), length_in_bytes); + } + break; + } + case T_BOOLEAN : { + if (Bytes::is_Java_byte_ordering_different()) { + WRITE_ARRAY(array, bool, u1); + } else { + writer->write_raw((void*)(array->bool_at_addr(0)), length_in_bytes); + } + break; + } + case T_LONG : { + if (Bytes::is_Java_byte_ordering_different()) { + WRITE_ARRAY(array, long, u8); + } else { + writer->write_raw((void*)(array->long_at_addr(0)), length_in_bytes); + } + break; + } + + // handle float/doubles in a special value to ensure than NaNs are + // written correctly. TO DO: Check if we can avoid this on processors that + // use IEEE 754. + + case T_FLOAT : { + for (int i=0; ilength(); i++) { + dump_float( writer, array->float_at(i) ); + } + break; + } + case T_DOUBLE : { + for (int i=0; ilength(); i++) { + dump_double( writer, array->double_at(i) ); + } + break; + } + default : ShouldNotReachHere(); + } +} + + +// Support class used to generate HPROF_UTF8 records from the entries in the +// SymbolTable. + +class SymbolTableDumper : public OopClosure { + private: + DumpWriter* _writer; + DumpWriter* writer() const { return _writer; } + public: + SymbolTableDumper(DumpWriter* writer) { _writer = writer; } + void do_oop(oop* obj_p); +}; + +void SymbolTableDumper::do_oop(oop* obj_p) { + ResourceMark rm; + symbolOop sym = (symbolOop)*obj_p; + + int len = sym->utf8_length(); + if (len > 0) { + char* s = sym->as_utf8(); + DumperSupport::write_header(writer(), HPROF_UTF8, oopSize + len); + writer()->write_objectID(sym); + writer()->write_raw(s, len); + } +} + + +// Support class used to generate HPROF_GC_ROOT_JNI_LOCAL records + +class JNILocalsDumper : public OopClosure { + private: + DumpWriter* _writer; + u4 _thread_serial_num; + DumpWriter* writer() const { return _writer; } + public: + JNILocalsDumper(DumpWriter* writer, u4 thread_serial_num) { + _writer = writer; + _thread_serial_num = thread_serial_num; + } + void do_oop(oop* obj_p); +}; + + +void JNILocalsDumper::do_oop(oop* obj_p) { + // ignore null or deleted handles + oop o = *obj_p; + if (o != NULL && o != JNIHandles::deleted_handle()) { + writer()->write_u1(HPROF_GC_ROOT_JNI_LOCAL); + writer()->write_objectID(o); + writer()->write_u4(_thread_serial_num); + writer()->write_u4((u4)-1); // empty + } +} + + +// Support class used to generate HPROF_GC_ROOT_JNI_GLOBAL records + +class JNIGlobalsDumper : public OopClosure { + private: + DumpWriter* _writer; + DumpWriter* writer() const { return _writer; } + + public: + JNIGlobalsDumper(DumpWriter* writer) { + _writer = writer; + } + void do_oop(oop* obj_p); +}; + +void JNIGlobalsDumper::do_oop(oop* obj_p) { + oop o = *obj_p; + + // ignore these + if (o == NULL || o == JNIHandles::deleted_handle()) return; + + // we ignore global ref to symbols and other internal objects + if (o->is_instance() || o->is_objArray() || o->is_typeArray()) { + writer()->write_u1(HPROF_GC_ROOT_JNI_GLOBAL); + writer()->write_objectID(o); + writer()->write_objectID((oopDesc*)obj_p); // global ref ID + } +}; + + +// Support class used to generate HPROF_GC_ROOT_MONITOR_USED records + +class MonitorUsedDumper : public OopClosure { + private: + DumpWriter* _writer; + DumpWriter* writer() const { return _writer; } + public: + MonitorUsedDumper(DumpWriter* writer) { + _writer = writer; + } + void do_oop(oop* obj_p) { + writer()->write_u1(HPROF_GC_ROOT_MONITOR_USED); + writer()->write_objectID(*obj_p); + } +}; + + +// Support class used to generate HPROF_GC_ROOT_STICKY_CLASS records + +class StickyClassDumper : public OopClosure { + private: + DumpWriter* _writer; + DumpWriter* writer() const { return _writer; } + public: + StickyClassDumper(DumpWriter* writer) { + _writer = writer; + } + void do_oop(oop* obj_p); +}; + +void StickyClassDumper::do_oop(oop* obj_p) { + if (*obj_p != NULL) { + oop o = *obj_p; + if (o->is_klass()) { + klassOop k = klassOop(o); + if (Klass::cast(k)->oop_is_instance()) { + instanceKlass* ik = instanceKlass::cast(k); + writer()->write_u1(HPROF_GC_ROOT_STICKY_CLASS); + writer()->write_classID(ik); + } + } + } +} + + +class VM_HeapDumper; + +// Support class using when iterating over the heap. + +class HeapObjectDumper : public ObjectClosure { + private: + VM_HeapDumper* _dumper; + DumpWriter* _writer; + + VM_HeapDumper* dumper() { return _dumper; } + DumpWriter* writer() { return _writer; } + + // used to indicate that a record has been writen + void mark_end_of_record(); + + public: + HeapObjectDumper(VM_HeapDumper* dumper, DumpWriter* writer) { + _dumper = dumper; + _writer = writer; + } + + // called for each object in the heap + void do_object(oop o); +}; + +void HeapObjectDumper::do_object(oop o) { + // hide the sentinel for deleted handles + if (o == JNIHandles::deleted_handle()) return; + + // ignore KlassKlass objects + if (o->is_klass()) return; + + // skip classes as these emitted as HPROF_GC_CLASS_DUMP records + if (o->klass() == SystemDictionary::class_klass()) { + if (!java_lang_Class::is_primitive(o)) { + return; + } + } + + // create a HPROF_GC_INSTANCE record for each object + if (o->is_instance()) { + DumperSupport::dump_instance(writer(), o); + mark_end_of_record(); + } else { + // create a HPROF_GC_OBJ_ARRAY_DUMP record for each object array + if (o->is_objArray()) { + DumperSupport::dump_object_array(writer(), objArrayOop(o)); + mark_end_of_record(); + } else { + // create a HPROF_GC_PRIM_ARRAY_DUMP record for each type array + if (o->is_typeArray()) { + DumperSupport::dump_prim_array(writer(), typeArrayOop(o)); + mark_end_of_record(); + } + } + } +} + +// The VM operation that performs the heap dump +class VM_HeapDumper : public VM_GC_Operation { + private: + DumpWriter* _writer; + bool _gc_before_heap_dump; + bool _is_segmented_dump; + jlong _dump_start; + + // accessors + DumpWriter* writer() const { return _writer; } + bool is_segmented_dump() const { return _is_segmented_dump; } + void set_segmented_dump() { _is_segmented_dump = true; } + jlong dump_start() const { return _dump_start; } + void set_dump_start(jlong pos); + + bool skip_operation() const; + + // writes a HPROF_LOAD_CLASS record + static void do_load_class(klassOop k); + + // writes a HPROF_GC_CLASS_DUMP record for the given class + // (and each array class too) + static void do_class_dump(klassOop k); + + // writes a HPROF_GC_CLASS_DUMP records for a given basic type + // array (and each multi-dimensional array too) + static void do_basic_type_array_class_dump(klassOop k); + + // HPROF_GC_ROOT_THREAD_OBJ records + void do_thread(JavaThread* thread, u4 thread_serial_num); + void do_threads(); + + // writes a HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT record + void write_dump_header(); + + // fixes up the length of the current dump record + void write_current_dump_record_length(); + + // fixes up the current dump record )and writes HPROF_HEAP_DUMP_END + // record in the case of a segmented heap dump) + void end_of_dump(); + + public: + VM_HeapDumper(DumpWriter* writer, bool gc_before_heap_dump) : + VM_GC_Operation(0 /* total collections, dummy, ignored */, + 0 /* total full collections, dummy, ignored */, + gc_before_heap_dump) { + _writer = writer; + _gc_before_heap_dump = gc_before_heap_dump; + _is_segmented_dump = false; + _dump_start = (jlong)-1; + } + + VMOp_Type type() const { return VMOp_HeapDumper; } + // used to mark sub-record boundary + void check_segment_length(); + void doit(); +}; + +bool VM_HeapDumper::skip_operation() const { + return false; +} + +// sets the dump starting position +void VM_HeapDumper::set_dump_start(jlong pos) { + _dump_start = pos; +} + + // writes a HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT record +void VM_HeapDumper::write_dump_header() { + if (writer()->is_open()) { + if (is_segmented_dump()) { + writer()->write_u1(HPROF_HEAP_DUMP_SEGMENT); + } else { + writer()->write_u1(HPROF_HEAP_DUMP); + } + writer()->write_u4(0); // current ticks + + // record the starting position for the dump (its length will be fixed up later) + set_dump_start(writer()->current_offset()); + writer()->write_u4(0); + } +} + +// fixes up the length of the current dump record +void VM_HeapDumper::write_current_dump_record_length() { + if (writer()->is_open()) { + assert(dump_start() >= 0, "no dump start recorded"); + + // calculate the size of the dump record + jlong dump_end = writer()->current_offset(); + jlong dump_len = (dump_end - dump_start() - 4); + + // record length must fit in a u4 + if (dump_len > (jlong)(4L*(jlong)G)) { + warning("record is too large"); + } + + // seek to the dump start and fix-up the length + writer()->seek_to_offset(dump_start()); + writer()->write_u4((u4)dump_len); + + // adjust the total size written to keep the bytes written correct. + writer()->adjust_bytes_written(-((long) sizeof(u4))); + + // seek to dump end so we can continue + writer()->seek_to_offset(dump_end); + + // no current dump record + set_dump_start((jlong)-1); + } +} + +// used on a sub-record boundary to check if we need to start a +// new segment. +void VM_HeapDumper::check_segment_length() { + if (writer()->is_open()) { + if (is_segmented_dump()) { + // don't use current_offset that would be too expensive on a per record basis + jlong dump_end = writer()->bytes_written() + writer()->bytes_unwritten(); + assert(dump_end == writer()->current_offset(), "checking"); + jlong dump_len = (dump_end - dump_start() - 4); + assert(dump_len >= 0 && dump_len <= max_juint, "bad dump length"); + + if (dump_len > (jlong)HeapDumpSegmentSize) { + write_current_dump_record_length(); + write_dump_header(); + } + } + } +} + +// fixes up the current dump record )and writes HPROF_HEAP_DUMP_END +// record in the case of a segmented heap dump) +void VM_HeapDumper::end_of_dump() { + if (writer()->is_open()) { + write_current_dump_record_length(); + + // for segmented dump we write the end record + if (is_segmented_dump()) { + writer()->write_u1(HPROF_HEAP_DUMP_END); + writer()->write_u4(0); + writer()->write_u4(0); + } + } +} + +// marks sub-record boundary +void HeapObjectDumper::mark_end_of_record() { + dumper()->check_segment_length(); +} + +// writes a HPROF_LOAD_CLASS record for the class (and each of its +// array classes) +void VM_HeapDumper::do_load_class(klassOop k) { + static u4 class_serial_num = 0; + + VM_HeapDumper* dumper = ((VM_HeapDumper*)VMThread::vm_operation()); + DumpWriter* writer = dumper->writer(); + + // len of HPROF_LOAD_CLASS record + u4 remaining = 2*oopSize + 2*sizeof(u4); + + // write a HPROF_LOAD_CLASS for the class and each array class + do { + DumperSupport::write_header(writer, HPROF_LOAD_CLASS, remaining); + + // class serial number is just a number + writer->write_u4(++class_serial_num); + + // class ID + Klass* klass = Klass::cast(k); + writer->write_classID(klass); + + writer->write_u4(STACK_TRACE_ID); + + // class name ID + symbolOop name = klass->name(); + writer->write_objectID(name); + + // write a LOAD_CLASS record for the array type (if it exists) + k = klass->array_klass_or_null(); + } while (k != NULL); +} + +// writes a HPROF_GC_CLASS_DUMP record for the given class +void VM_HeapDumper::do_class_dump(klassOop k) { + VM_HeapDumper* dumper = ((VM_HeapDumper*)VMThread::vm_operation()); + DumpWriter* writer = dumper->writer(); + DumperSupport::dump_class_and_array_classes(writer, k); +} + +// writes a HPROF_GC_CLASS_DUMP records for a given basic type +// array (and each multi-dimensional array too) +void VM_HeapDumper::do_basic_type_array_class_dump(klassOop k) { + VM_HeapDumper* dumper = ((VM_HeapDumper*)VMThread::vm_operation()); + DumpWriter* writer = dumper->writer(); + DumperSupport::dump_basic_type_array_class(writer, k); +} + +// Walk the stack of the given thread. +// Dumps a HPROF_GC_ROOT_JAVA_FRAME record for each local +// Dumps a HPROF_GC_ROOT_JNI_LOCAL record for each JNI local +void VM_HeapDumper::do_thread(JavaThread* java_thread, u4 thread_serial_num) { + JNILocalsDumper blk(writer(), thread_serial_num); + + oop threadObj = java_thread->threadObj(); + assert(threadObj != NULL, "sanity check"); + + // JNI locals for the top frame + java_thread->active_handles()->oops_do(&blk); + + if (java_thread->has_last_Java_frame()) { + + // vframes are resource allocated + Thread* current_thread = Thread::current(); + ResourceMark rm(current_thread); + HandleMark hm(current_thread); + + RegisterMap reg_map(java_thread); + frame f = java_thread->last_frame(); + vframe* vf = vframe::new_vframe(&f, ®_map, java_thread); + + while (vf != NULL) { + if (vf->is_java_frame()) { + + // java frame (interpreted, compiled, ...) + javaVFrame *jvf = javaVFrame::cast(vf); + + if (!(jvf->method()->is_native())) { + StackValueCollection* locals = jvf->locals(); + for (int slot=0; slotsize(); slot++) { + if (locals->at(slot)->type() == T_OBJECT) { + oop o = locals->obj_at(slot)(); + + if (o != NULL) { + writer()->write_u1(HPROF_GC_ROOT_JAVA_FRAME); + writer()->write_objectID(o); + writer()->write_u4(thread_serial_num); + writer()->write_u4((u4)-1); // empty + } + } + } + } + } else { + + // externalVFrame - if it's an entry frame then report any JNI locals + // as roots + frame* fr = vf->frame_pointer(); + assert(fr != NULL, "sanity check"); + if (fr->is_entry_frame()) { + fr->entry_frame_call_wrapper()->handles()->oops_do(&blk); + } + } + + vf = vf->sender(); + } + } +} + + +// write a HPROF_GC_ROOT_THREAD_OBJ record for each java thread. Then walk +// the stack so that locals and JNI locals are dumped. +void VM_HeapDumper::do_threads() { + u4 thread_serial_num = 0; + for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) { + oop threadObj = thread->threadObj(); + if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) { + ++thread_serial_num; + + writer()->write_u1(HPROF_GC_ROOT_THREAD_OBJ); + writer()->write_objectID(threadObj); + writer()->write_u4(thread_serial_num); + writer()->write_u4(STACK_TRACE_ID); + + do_thread(thread, thread_serial_num); + } + } +} + + +// The VM operation that dumps the heap. The dump consists of the following +// records: +// +// HPROF_HEADER +// HPROF_TRACE +// [HPROF_UTF8]* +// [HPROF_LOAD_CLASS]* +// [HPROF_GC_CLASS_DUMP]* +// HPROF_HEAP_DUMP +// +// The HPROF_TRACE record after the header is "dummy trace" record which does +// not include any frames. Other records which require a stack trace ID will +// specify the trace ID of this record (1). It also means we can run HAT without +// needing the -stack false option. +// +// The HPROF_HEAP_DUMP record has a length following by sub-records. To allow +// the heap dump be generated in a single pass we remember the position of +// the dump length and fix it up after all sub-records have been written. +// To generate the sub-records we iterate over the heap, writing +// HPROF_GC_INSTANCE_DUMP, HPROF_GC_OBJ_ARRAY_DUMP, and HPROF_GC_PRIM_ARRAY_DUMP +// records as we go. Once that is done we write records for some of the GC +// roots. + +void VM_HeapDumper::doit() { + + HandleMark hm; + CollectedHeap* ch = Universe::heap(); + if (_gc_before_heap_dump) { + ch->collect_as_vm_thread(GCCause::_heap_dump); + } else { + // make the heap parsable (no need to retire TLABs) + ch->ensure_parsability(false); + } + + // Write the file header - use 1.0.2 for large heaps, otherwise 1.0.1 + size_t used; + const char* header; +#ifndef SERIALGC + if (Universe::heap()->kind() == CollectedHeap::GenCollectedHeap) { + used = GenCollectedHeap::heap()->used(); + } else { + used = ParallelScavengeHeap::heap()->used(); + } +#else // SERIALGC + used = GenCollectedHeap::heap()->used(); +#endif // SERIALGC + if (used > (size_t)SegmentedHeapDumpThreshold) { + set_segmented_dump(); + header = "JAVA PROFILE 1.0.2"; + } else { + header = "JAVA PROFILE 1.0.1"; + } + // header is few bytes long - no chance to overflow int + writer()->write_raw((void*)header, (int)strlen(header)); + writer()->write_u1(0); // terminator + writer()->write_u4(oopSize); + writer()->write_u8(os::javaTimeMillis()); + + // HPROF_TRACE record without any frames + DumperSupport::write_header(writer(), HPROF_TRACE, 3*sizeof(u4)); + writer()->write_u4(STACK_TRACE_ID); + writer()->write_u4(0); // thread number + writer()->write_u4(0); // frame count + + // HPROF_UTF8 records + SymbolTableDumper sym_dumper(writer()); + SymbolTable::oops_do(&sym_dumper); + + // write HPROF_LOAD_CLASS records + SystemDictionary::classes_do(&do_load_class); + Universe::basic_type_classes_do(&do_load_class); + + // write HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT + write_dump_header(); + + // Writes HPROF_GC_CLASS_DUMP records + SystemDictionary::classes_do(&do_class_dump); + Universe::basic_type_classes_do(&do_basic_type_array_class_dump); + check_segment_length(); + + // writes HPROF_GC_INSTANCE_DUMP records. + // After each sub-record is written check_segment_length will be invoked. When + // generated a segmented heap dump this allows us to check if the current + // segment exceeds a threshold and if so, then a new segment is started. + // The HPROF_GC_CLASS_DUMP and HPROF_GC_INSTANCE_DUMP are the vast bulk + // of the heap dump. + HeapObjectDumper obj_dumper(this, writer()); + Universe::heap()->object_iterate(&obj_dumper); + + // HPROF_GC_ROOT_THREAD_OBJ + frames + jni locals + do_threads(); + check_segment_length(); + + // HPROF_GC_ROOT_MONITOR_USED + MonitorUsedDumper mon_dumper(writer()); + ObjectSynchronizer::oops_do(&mon_dumper); + check_segment_length(); + + // HPROF_GC_ROOT_JNI_GLOBAL + JNIGlobalsDumper jni_dumper(writer()); + JNIHandles::oops_do(&jni_dumper); + check_segment_length(); + + // HPROF_GC_ROOT_STICKY_CLASS + StickyClassDumper class_dumper(writer()); + SystemDictionary::always_strong_oops_do(&class_dumper); + + // fixes up the length of the dump record. In the case of a segmented + // heap then the HPROF_HEAP_DUMP_END record is also written. + end_of_dump(); +} + + +// dump the heap to given path. +int HeapDumper::dump(const char* path) { + assert(path != NULL && strlen(path) > 0, "path missing"); + + // print message in interactive case + if (print_to_tty()) { + tty->print_cr("Dumping heap to %s ...", path); + timer()->start(); + } + + // create the dump writer. If the file can be opened then bail + DumpWriter writer(path); + if (!writer.is_open()) { + set_error(writer.error()); + if (print_to_tty()) { + tty->print_cr("Unable to create %s: %s", path, + (error() != NULL) ? error() : "reason unknown"); + } + return -1; + } + + // generate the dump + VM_HeapDumper dumper(&writer, _gc_before_heap_dump); + VMThread::execute(&dumper); + + // close dump file and record any error that the writer may have encountered + writer.close(); + set_error(writer.error()); + + // print message in interactive case + if (print_to_tty()) { + timer()->stop(); + if (error() == NULL) { + char msg[256]; + sprintf(msg, "Heap dump file created [%s bytes in %3.3f secs]", + os::jlong_format_specifier(), timer()->seconds()); + tty->print_cr(msg, writer.bytes_written()); + } else { + tty->print_cr("Dump file is incomplete: %s", writer.error()); + } + } + + return (writer.error() == NULL) ? 0 : -1; +} + +// stop timer (if still active), and free any error string we might be holding +HeapDumper::~HeapDumper() { + if (timer()->is_active()) { + timer()->stop(); + } + set_error(NULL); +} + + +// returns the error string (resource allocated), or NULL +char* HeapDumper::error_as_C_string() const { + if (error() != NULL) { + char* str = NEW_RESOURCE_ARRAY(char, strlen(error())+1); + strcpy(str, error()); + return str; + } else { + return NULL; + } +} + +// set the error string +void HeapDumper::set_error(char* error) { + if (_error != NULL) { + os::free(_error); + } + if (error == NULL) { + _error = NULL; + } else { + _error = os::strdup(error); + assert(_error != NULL, "allocation failure"); + } +} + + +// Called by error reporting +void HeapDumper::dump_heap() { + static char path[JVM_MAXPATHLEN]; + + // The dump file defaults to java_pid.hprof in the current working + // directory. HeapDumpPath= can be used to specify an alternative + // dump file name or a directory where dump file is created. + bool use_default_filename = true; + if (HeapDumpPath == NULL || HeapDumpPath[0] == '\0') { + path[0] = '\0'; // HeapDumpPath= not specified + } else { + assert(strlen(HeapDumpPath) < sizeof(path), "HeapDumpPath too long"); + strcpy(path, HeapDumpPath); + // check if the path is a directory (must exist) + DIR* dir = os::opendir(path); + if (dir == NULL) { + use_default_filename = false; + } else { + // HeapDumpPath specified a directory. We append a file separator + // (if needed). + os::closedir(dir); + size_t fs_len = strlen(os::file_separator()); + if (strlen(path) >= fs_len) { + char* end = path; + end += (strlen(path) - fs_len); + if (strcmp(end, os::file_separator()) != 0) { + assert(strlen(path) + strlen(os::file_separator()) < sizeof(path), + "HeapDumpPath too long"); + strcat(path, os::file_separator()); + } + } + } + } + // If HeapDumpPath wasn't a file name then we append the default name + if (use_default_filename) { + char fn[32]; + sprintf(fn, "java_pid%d.hprof", os::current_process_id()); + assert(strlen(path) + strlen(fn) < sizeof(path), "HeapDumpPath too long"); + strcat(path, fn); + } + + HeapDumper dumper(false /* no GC before heap dump */, + true /* send to tty */); + dumper.dump(path); +} diff --git a/hotspot/src/share/vm/services/heapDumper.hpp b/hotspot/src/share/vm/services/heapDumper.hpp new file mode 100644 index 00000000000..247512e0182 --- /dev/null +++ b/hotspot/src/share/vm/services/heapDumper.hpp @@ -0,0 +1,69 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// HeapDumper is used to dump the java heap to file in HPROF binary format: +// +// { HeapDumper dumper(true /* full GC before heap dump */); +// if (dumper.dump("/export/java.hprof")) { +// ResourceMark rm; +// tty->print_cr("Dump failed: %s", dumper.error_as_C_string()); +// } else { +// // dump succeeded +// } +// } +// + +class HeapDumper : public StackObj { + private: + char* _error; + bool _print_to_tty; + bool _gc_before_heap_dump; + elapsedTimer _t; + + // string representation of error + char* error() const { return _error; } + void set_error(char* error); + + // indicates if progress messages can be sent to tty + bool print_to_tty() const { return _print_to_tty; } + + // internal timer. + elapsedTimer* timer() { return &_t; } + + public: + HeapDumper(bool gc_before_heap_dump) : + _gc_before_heap_dump(gc_before_heap_dump), _error(NULL), _print_to_tty(false) { } + HeapDumper(bool gc_before_heap_dump, bool print_to_tty) : + _gc_before_heap_dump(gc_before_heap_dump), _error(NULL), _print_to_tty(print_to_tty) { } + + ~HeapDumper(); + + // dumps the heap to the specified file, returns 0 if success. + int dump(const char* path); + + // returns error message (resource allocated), or NULL if no error + char* error_as_C_string() const; + + static void dump_heap() KERNEL_RETURN; +}; diff --git a/hotspot/src/share/vm/services/jmm.h b/hotspot/src/share/vm/services/jmm.h new file mode 100644 index 00000000000..b4777f9fe52 --- /dev/null +++ b/hotspot/src/share/vm/services/jmm.h @@ -0,0 +1,287 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#ifndef _JAVA_JMM_H_ +#define _JAVA_JMM_H_ + +/* + * This is a private interface used by JDK for JVM monitoring + * and management. + * + * Bump the version number when either of the following happens: + * + * 1. There is a change in functions in JmmInterface. + * + * 2. There is a change in the contract between VM and Java classes. + */ + +#include "jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + JMM_VERSION_1 = 0x20010000, + JMM_VERSION_1_0 = 0x20010000, + JMM_VERSION_1_1 = 0x20010100, // JDK 6 + JMM_VERSION_1_2 = 0x20010200, // JDK 7 + JMM_VERSION = 0x20010200 +}; + +typedef struct { + unsigned int isLowMemoryDetectionSupported : 1; + unsigned int isCompilationTimeMonitoringSupported : 1; + unsigned int isThreadContentionMonitoringSupported : 1; + unsigned int isCurrentThreadCpuTimeSupported : 1; + unsigned int isOtherThreadCpuTimeSupported : 1; + unsigned int isBootClassPathSupported : 1; + unsigned int isObjectMonitorUsageSupported : 1; + unsigned int isSynchronizerUsageSupported : 1; + unsigned int : 24; +} jmmOptionalSupport; + +typedef enum { + JMM_CLASS_LOADED_COUNT = 1, /* Total number of loaded classes */ + JMM_CLASS_UNLOADED_COUNT = 2, /* Total number of unloaded classes */ + JMM_THREAD_TOTAL_COUNT = 3, /* Total number of threads that have been started */ + JMM_THREAD_LIVE_COUNT = 4, /* Current number of live threads */ + JMM_THREAD_PEAK_COUNT = 5, /* Peak number of live threads */ + JMM_THREAD_DAEMON_COUNT = 6, /* Current number of daemon threads */ + JMM_JVM_INIT_DONE_TIME_MS = 7, /* Time when the JVM finished initialization */ + JMM_COMPILE_TOTAL_TIME_MS = 8, /* Total accumulated time spent in compilation */ + JMM_GC_TIME_MS = 9, /* Total accumulated time spent in collection */ + JMM_GC_COUNT = 10, /* Total number of collections */ + + JMM_INTERNAL_ATTRIBUTE_INDEX = 100, + JMM_CLASS_LOADED_BYTES = 101, /* Number of bytes loaded instance classes */ + JMM_CLASS_UNLOADED_BYTES = 102, /* Number of bytes unloaded instance classes */ + JMM_TOTAL_CLASSLOAD_TIME_MS = 103, /* Accumulated VM class loader time (TraceClassLoadingTime) */ + JMM_VM_GLOBAL_COUNT = 104, /* Number of VM internal flags */ + JMM_SAFEPOINT_COUNT = 105, /* Total number of safepoints */ + JMM_TOTAL_SAFEPOINTSYNC_TIME_MS = 106, /* Accumulated time spent getting to safepoints */ + JMM_TOTAL_STOPPED_TIME_MS = 107, /* Accumulated time spent at safepoints */ + JMM_TOTAL_APP_TIME_MS = 108, /* Accumulated time spent in Java application */ + JMM_VM_THREAD_COUNT = 109, /* Current number of VM internal threads */ + JMM_CLASS_INIT_TOTAL_COUNT = 110, /* Number of classes for which initializers were run */ + JMM_CLASS_INIT_TOTAL_TIME_MS = 111, /* Accumulated time spent in class initializers */ + JMM_METHOD_DATA_SIZE_BYTES = 112, /* Size of method data in memory */ + JMM_CLASS_VERIFY_TOTAL_TIME_MS = 113, /* Accumulated time spent in class verifier */ + JMM_SHARED_CLASS_LOADED_COUNT = 114, /* Number of shared classes loaded */ + JMM_SHARED_CLASS_UNLOADED_COUNT = 115, /* Number of shared classes unloaded */ + JMM_SHARED_CLASS_LOADED_BYTES = 116, /* Number of bytes loaded shared classes */ + JMM_SHARED_CLASS_UNLOADED_BYTES = 117, /* Number of bytes unloaded shared classes */ + + JMM_OS_ATTRIBUTE_INDEX = 200, + JMM_OS_PROCESS_ID = 201, /* Process id of the JVM */ + JMM_OS_MEM_TOTAL_PHYSICAL_BYTES = 202, /* Physical memory size */ + + JMM_GC_EXT_ATTRIBUTE_INFO_SIZE = 401 /* the size of the GC specific attributes for a given GC memory manager */ +} jmmLongAttribute; + +typedef enum { + JMM_VERBOSE_GC = 21, + JMM_VERBOSE_CLASS = 22, + JMM_THREAD_CONTENTION_MONITORING = 23, + JMM_THREAD_CPU_TIME = 24 +} jmmBoolAttribute; + + +enum { + JMM_THREAD_STATE_FLAG_SUSPENDED = 0x00100000, + JMM_THREAD_STATE_FLAG_NATIVE = 0x00400000 +}; + +#define JMM_THREAD_STATE_FLAG_MASK 0xFFF00000 + +typedef enum { + JMM_STAT_PEAK_THREAD_COUNT = 801, + JMM_STAT_THREAD_CONTENTION_COUNT = 802, + JMM_STAT_THREAD_CONTENTION_TIME = 803, + JMM_STAT_THREAD_CONTENTION_STAT = 804, + JMM_STAT_PEAK_POOL_USAGE = 805, + JMM_STAT_GC_STAT = 806 +} jmmStatisticType; + +typedef enum { + JMM_USAGE_THRESHOLD_HIGH = 901, + JMM_USAGE_THRESHOLD_LOW = 902, + JMM_COLLECTION_USAGE_THRESHOLD_HIGH = 903, + JMM_COLLECTION_USAGE_THRESHOLD_LOW = 904 +} jmmThresholdType; + +/* Should match what is allowed in globals.hpp */ +typedef enum { + JMM_VMGLOBAL_TYPE_UNKNOWN = 0, + JMM_VMGLOBAL_TYPE_JBOOLEAN = 1, + JMM_VMGLOBAL_TYPE_JSTRING = 2, + JMM_VMGLOBAL_TYPE_JLONG = 3 +} jmmVMGlobalType; + +typedef enum { + JMM_VMGLOBAL_ORIGIN_DEFAULT = 1, /* Default value */ + JMM_VMGLOBAL_ORIGIN_COMMAND_LINE = 2, /* Set at command line (or JNI invocation) */ + JMM_VMGLOBAL_ORIGIN_MANAGEMENT = 3, /* Set via management interface */ + JMM_VMGLOBAL_ORIGIN_ENVIRON_VAR = 4, /* Set via environment variables */ + JMM_VMGLOBAL_ORIGIN_CONFIG_FILE = 5, /* Set via config file (such as .hotspotrc) */ + JMM_VMGLOBAL_ORIGIN_ERGONOMIC = 6, /* Set via ergonomic */ + JMM_VMGLOBAL_ORIGIN_OTHER = 99 /* Set via some other mechanism */ +} jmmVMGlobalOrigin; + +typedef struct { + jstring name; + jvalue value; + jmmVMGlobalType type; /* Data type */ + jmmVMGlobalOrigin origin; /* Default or non-default value */ + unsigned int writeable : 1; /* dynamically writeable */ + unsigned int external : 1; /* external supported interface */ + unsigned int reserved : 30; + void *reserved1; + void *reserved2; +} jmmVMGlobal; + +typedef struct { + const char* name; + char type; + const char* description; +} jmmExtAttributeInfo; + +/* Caller has to set the following fields before calling GetLastGCStat + * o usage_before_gc - array of MemoryUsage objects + * o usage_after_gc - array of MemoryUsage objects + * o gc_ext_attribute_values_size - size of gc_ext_atttribute_values array + * o gc_ext_attribtue_values - array of jvalues + */ +typedef struct { + jlong gc_index; /* Index of the collections */ + jlong start_time; /* Start time of the GC */ + jlong end_time; /* End time of the GC */ + jobjectArray usage_before_gc; /* Memory usage array before GC */ + jobjectArray usage_after_gc; /* Memory usage array after GC */ + jint gc_ext_attribute_values_size; /* set by the caller of GetGCStat */ + jvalue* gc_ext_attribute_values; /* Array of jvalue for GC extension attributes */ + jint num_gc_ext_attributes; /* number of GC extension attribute values s are filled */ + /* -1 indicates gc_ext_attribute_values is not big enough */ +} jmmGCStat; + +typedef struct jmmInterface_1_ { + void* reserved1; + void* reserved2; + + jint (JNICALL *GetVersion) (JNIEnv *env); + + jint (JNICALL *GetOptionalSupport) (JNIEnv *env, + jmmOptionalSupport* support_ptr); + + /* This is used by JDK 6 and earlier. + * For JDK 7 and after, use GetInputArgumentArray. + */ + jobject (JNICALL *GetInputArguments) (JNIEnv *env); + + jint (JNICALL *GetThreadInfo) (JNIEnv *env, + jlongArray ids, + jint maxDepth, + jobjectArray infoArray); + jobjectArray (JNICALL *GetInputArgumentArray) (JNIEnv *env); + + jobjectArray (JNICALL *GetMemoryPools) (JNIEnv* env, jobject mgr); + + jobjectArray (JNICALL *GetMemoryManagers) (JNIEnv* env, jobject pool); + + jobject (JNICALL *GetMemoryPoolUsage) (JNIEnv* env, jobject pool); + jobject (JNICALL *GetPeakMemoryPoolUsage) (JNIEnv* env, jobject pool); + + void* reserved4; + + jobject (JNICALL *GetMemoryUsage) (JNIEnv* env, jboolean heap); + + jlong (JNICALL *GetLongAttribute) (JNIEnv *env, jobject obj, jmmLongAttribute att); + jboolean (JNICALL *GetBoolAttribute) (JNIEnv *env, jmmBoolAttribute att); + jboolean (JNICALL *SetBoolAttribute) (JNIEnv *env, jmmBoolAttribute att, jboolean flag); + + jint (JNICALL *GetLongAttributes) (JNIEnv *env, + jobject obj, + jmmLongAttribute* atts, + jint count, + jlong* result); + + jobjectArray (JNICALL *FindCircularBlockedThreads) (JNIEnv *env); + jlong (JNICALL *GetThreadCpuTime) (JNIEnv *env, jlong thread_id); + + jobjectArray (JNICALL *GetVMGlobalNames) (JNIEnv *env); + jint (JNICALL *GetVMGlobals) (JNIEnv *env, + jobjectArray names, + jmmVMGlobal *globals, + jint count); + + jint (JNICALL *GetInternalThreadTimes) (JNIEnv *env, + jobjectArray names, + jlongArray times); + + jboolean (JNICALL *ResetStatistic) (JNIEnv *env, + jvalue obj, + jmmStatisticType type); + + void (JNICALL *SetPoolSensor) (JNIEnv *env, + jobject pool, + jmmThresholdType type, + jobject sensor); + + jlong (JNICALL *SetPoolThreshold) (JNIEnv *env, + jobject pool, + jmmThresholdType type, + jlong threshold); + jobject (JNICALL *GetPoolCollectionUsage) (JNIEnv* env, jobject pool); + + jint (JNICALL *GetGCExtAttributeInfo) (JNIEnv *env, + jobject mgr, + jmmExtAttributeInfo *ext_info, + jint count); + void (JNICALL *GetLastGCStat) (JNIEnv *env, + jobject mgr, + jmmGCStat *gc_stat); + jlong (JNICALL *GetThreadCpuTimeWithKind) (JNIEnv *env, + jlong thread_id, + jboolean user_sys_cpu_time); + void* reserved5; + jint (JNICALL *DumpHeap0) (JNIEnv *env, + jstring outputfile, + jboolean live); + jobjectArray (JNICALL *FindDeadlocks) (JNIEnv *env, jboolean object_monitors_only); + void (JNICALL *SetVMGlobal) (JNIEnv *env, + jstring flag_name, + jvalue new_value); + void* reserved6; + jobjectArray (JNICALL *DumpThreads) (JNIEnv *env, + jlongArray ids, + jboolean lockedMonitors, + jboolean lockedSynchronizers); +} JmmInterface; + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVA_JMM_H_ */ diff --git a/hotspot/src/share/vm/services/lowMemoryDetector.cpp b/hotspot/src/share/vm/services/lowMemoryDetector.cpp new file mode 100644 index 00000000000..14ca5243c5a --- /dev/null +++ b/hotspot/src/share/vm/services/lowMemoryDetector.cpp @@ -0,0 +1,422 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_lowMemoryDetector.cpp.incl" + +LowMemoryDetectorThread* LowMemoryDetector::_detector_thread = NULL; +volatile bool LowMemoryDetector::_enabled_for_collected_pools = false; +volatile jint LowMemoryDetector::_disabled_count = 0; + +void LowMemoryDetector::initialize() { + EXCEPTION_MARK; + + instanceKlassHandle klass (THREAD, SystemDictionary::thread_klass()); + instanceHandle thread_oop = klass->allocate_instance_handle(CHECK); + + const char thread_name[] = "Low Memory Detector"; + Handle string = java_lang_String::create_from_str(thread_name, CHECK); + + // Initialize thread_oop to put it into the system threadGroup + Handle thread_group (THREAD, Universe::system_thread_group()); + JavaValue result(T_VOID); + JavaCalls::call_special(&result, thread_oop, + klass, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::threadgroup_string_void_signature(), + thread_group, + string, + CHECK); + + { + MutexLocker mu(Threads_lock); + _detector_thread = new LowMemoryDetectorThread(&low_memory_detector_thread_entry); + + // At this point it may be possible that no osthread was created for the + // JavaThread due to lack of memory. We would have to throw an exception + // in that case. However, since this must work and we do not allow + // exceptions anyway, check and abort if this fails. + if (_detector_thread == NULL || _detector_thread->osthread() == NULL) { + vm_exit_during_initialization("java.lang.OutOfMemoryError", + "unable to create new native thread"); + } + + java_lang_Thread::set_thread(thread_oop(), _detector_thread); + java_lang_Thread::set_priority(thread_oop(), NearMaxPriority); + java_lang_Thread::set_daemon(thread_oop()); + _detector_thread->set_threadObj(thread_oop()); + + Threads::add(_detector_thread); + Thread::start(_detector_thread); + } +} + +bool LowMemoryDetector::has_pending_requests() { + assert(LowMemory_lock->owned_by_self(), "Must own LowMemory_lock"); + bool has_requests = false; + int num_memory_pools = MemoryService::num_memory_pools(); + for (int i = 0; i < num_memory_pools; i++) { + MemoryPool* pool = MemoryService::get_memory_pool(i); + SensorInfo* sensor = pool->usage_sensor(); + if (sensor != NULL) { + has_requests = has_requests || sensor->has_pending_requests(); + } + + SensorInfo* gc_sensor = pool->gc_usage_sensor(); + if (gc_sensor != NULL) { + has_requests = has_requests || gc_sensor->has_pending_requests(); + } + } + return has_requests; +} + +void LowMemoryDetector::low_memory_detector_thread_entry(JavaThread* jt, TRAPS) { + while (true) { + bool sensors_changed = false; + + { + // _no_safepoint_check_flag is used here as LowMemory_lock is a + // special lock and the VMThread may acquire this lock at safepoint. + // Need state transition ThreadBlockInVM so that this thread + // will be handled by safepoint correctly when this thread is + // notified at a safepoint. + + // This ThreadBlockInVM object is not also considered to be + // suspend-equivalent because LowMemoryDetector threads are + // not visible to external suspension. + + ThreadBlockInVM tbivm(jt); + + MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag); + while (!(sensors_changed = has_pending_requests())) { + // wait until one of the sensors has pending requests + LowMemory_lock->wait(Mutex::_no_safepoint_check_flag); + } + } + + { + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + + // No need to hold LowMemory_lock to call out to Java + int num_memory_pools = MemoryService::num_memory_pools(); + for (int i = 0; i < num_memory_pools; i++) { + MemoryPool* pool = MemoryService::get_memory_pool(i); + SensorInfo* sensor = pool->usage_sensor(); + SensorInfo* gc_sensor = pool->gc_usage_sensor(); + if (sensor != NULL && sensor->has_pending_requests()) { + sensor->process_pending_requests(CHECK); + } + if (gc_sensor != NULL && gc_sensor->has_pending_requests()) { + gc_sensor->process_pending_requests(CHECK); + } + } + } + } +} + +// This method could be called from any Java threads +// and also VMThread. +void LowMemoryDetector::detect_low_memory() { + MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag); + + bool has_pending_requests = false; + int num_memory_pools = MemoryService::num_memory_pools(); + for (int i = 0; i < num_memory_pools; i++) { + MemoryPool* pool = MemoryService::get_memory_pool(i); + SensorInfo* sensor = pool->usage_sensor(); + if (sensor != NULL && + pool->usage_threshold()->is_high_threshold_supported() && + pool->usage_threshold()->high_threshold() != 0) { + MemoryUsage usage = pool->get_memory_usage(); + sensor->set_gauge_sensor_level(usage, + pool->usage_threshold()); + has_pending_requests = has_pending_requests || sensor->has_pending_requests(); + } + } + + if (has_pending_requests) { + LowMemory_lock->notify_all(); + } +} + +// This method could be called from any Java threads +// and also VMThread. +void LowMemoryDetector::detect_low_memory(MemoryPool* pool) { + SensorInfo* sensor = pool->usage_sensor(); + if (sensor == NULL || + !pool->usage_threshold()->is_high_threshold_supported() || + pool->usage_threshold()->high_threshold() == 0) { + return; + } + + { + MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag); + + MemoryUsage usage = pool->get_memory_usage(); + sensor->set_gauge_sensor_level(usage, + pool->usage_threshold()); + if (sensor->has_pending_requests()) { + // notify sensor state update + LowMemory_lock->notify_all(); + } + } +} + +// Only called by VMThread at GC time +void LowMemoryDetector::detect_after_gc_memory(MemoryPool* pool) { + SensorInfo* sensor = pool->gc_usage_sensor(); + if (sensor == NULL || + !pool->gc_usage_threshold()->is_high_threshold_supported() || + pool->gc_usage_threshold()->high_threshold() == 0) { + return; + } + + { + MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag); + + MemoryUsage usage = pool->get_last_collection_usage(); + sensor->set_counter_sensor_level(usage, pool->gc_usage_threshold()); + + if (sensor->has_pending_requests()) { + // notify sensor state update + LowMemory_lock->notify_all(); + } + } +} + +// recompute enabled flag +void LowMemoryDetector::recompute_enabled_for_collected_pools() { + bool enabled = false; + int num_memory_pools = MemoryService::num_memory_pools(); + for (int i=0; iis_collected_pool() && is_enabled(pool)) { + enabled = true; + break; + } + } + _enabled_for_collected_pools = enabled; +} + +SensorInfo::SensorInfo() { + _sensor_obj = NULL; + _sensor_on = false; + _sensor_count = 0; + _pending_trigger_count = 0; + _pending_clear_count = 0; +} + +// When this method is used, the memory usage is monitored +// as a gauge attribute. Sensor notifications (trigger or +// clear) is only emitted at the first time it crosses +// a threshold. +// +// High and low thresholds are designed to provide a +// hysteresis mechanism to avoid repeated triggering +// of notifications when the attribute value makes small oscillations +// around the high or low threshold value. +// +// The sensor will be triggered if: +// (1) the usage is crossing above the high threshold and +// the sensor is currently off and no pending +// trigger requests; or +// (2) the usage is crossing above the high threshold and +// the sensor will be off (i.e. sensor is currently on +// and has pending clear requests). +// +// Subsequent crossings of the high threshold value do not cause +// any triggers unless the usage becomes less than the low threshold. +// +// The sensor will be cleared if: +// (1) the usage is crossing below the low threshold and +// the sensor is currently on and no pending +// clear requests; or +// (2) the usage is crossing below the low threshold and +// the sensor will be on (i.e. sensor is currently off +// and has pending trigger requests). +// +// Subsequent crossings of the low threshold value do not cause +// any clears unless the usage becomes greater than or equal +// to the high threshold. +// +// If the current level is between high and low threhsold, no change. +// +void SensorInfo::set_gauge_sensor_level(MemoryUsage usage, ThresholdSupport* high_low_threshold) { + assert(high_low_threshold->is_high_threshold_supported(), "just checking"); + + bool is_over_high = high_low_threshold->is_high_threshold_crossed(usage); + bool is_below_low = high_low_threshold->is_low_threshold_crossed(usage); + + assert(!(is_over_high && is_below_low), "Can't be both true"); + + if (is_over_high && + ((!_sensor_on && _pending_trigger_count == 0) || + _pending_clear_count > 0)) { + // low memory detected and need to increment the trigger pending count + // if the sensor is off or will be off due to _pending_clear_ > 0 + // Request to trigger the sensor + _pending_trigger_count++; + _usage = usage; + + if (_pending_clear_count > 0) { + // non-zero pending clear requests indicates that there are + // pending requests to clear this sensor. + // This trigger request needs to clear this clear count + // since the resulting sensor flag should be on. + _pending_clear_count = 0; + } + } else if (is_below_low && + ((_sensor_on && _pending_clear_count == 0) || + (_pending_trigger_count > 0 && _pending_clear_count == 0))) { + // memory usage returns below the threshold + // Request to clear the sensor if the sensor is on or will be on due to + // _pending_trigger_count > 0 and also no clear request + _pending_clear_count++; + } +} + +// When this method is used, the memory usage is monitored as a +// simple counter attribute. The sensor will be triggered +// whenever the usage is crossing the threshold to keep track +// of the number of times the VM detects such a condition occurs. +// +// High and low thresholds are designed to provide a +// hysteresis mechanism to avoid repeated triggering +// of notifications when the attribute value makes small oscillations +// around the high or low threshold value. +// +// The sensor will be triggered if: +// - the usage is crossing above the high threshold regardless +// of the current sensor state. +// +// The sensor will be cleared if: +// (1) the usage is crossing below the low threshold and +// the sensor is currently on; or +// (2) the usage is crossing below the low threshold and +// the sensor will be on (i.e. sensor is currently off +// and has pending trigger requests). +void SensorInfo::set_counter_sensor_level(MemoryUsage usage, ThresholdSupport* counter_threshold) { + assert(counter_threshold->is_high_threshold_supported(), "just checking"); + + bool is_over_high = counter_threshold->is_high_threshold_crossed(usage); + bool is_below_low = counter_threshold->is_low_threshold_crossed(usage); + + assert(!(is_over_high && is_below_low), "Can't be both true"); + + if (is_over_high) { + _pending_trigger_count++; + _usage = usage; + _pending_clear_count = 0; + } else if (is_below_low && (_sensor_on || _pending_trigger_count > 0)) { + _pending_clear_count++; + } +} + +void SensorInfo::oops_do(OopClosure* f) { + f->do_oop((oop*) &_sensor_obj); +} + +void SensorInfo::process_pending_requests(TRAPS) { + if (!has_pending_requests()) { + return; + } + + int pending_count = pending_trigger_count(); + if (pending_clear_count() > 0) { + clear(pending_count, CHECK); + } else { + trigger(pending_count, CHECK); + } + +} + +void SensorInfo::trigger(int count, TRAPS) { + assert(count <= _pending_trigger_count, "just checking"); + + if (_sensor_obj != NULL) { + klassOop k = Management::sun_management_Sensor_klass(CHECK); + instanceKlassHandle sensorKlass (THREAD, k); + Handle sensor_h(THREAD, _sensor_obj); + Handle usage_h = MemoryService::create_MemoryUsage_obj(_usage, CHECK); + + JavaValue result(T_VOID); + JavaCallArguments args(sensor_h); + args.push_int((int) count); + args.push_oop(usage_h); + + JavaCalls::call_virtual(&result, + sensorKlass, + vmSymbolHandles::trigger_name(), + vmSymbolHandles::trigger_method_signature(), + &args, + CHECK); + } + + { + // Holds LowMemory_lock and update the sensor state + MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag); + _sensor_on = true; + _sensor_count += count; + _pending_trigger_count = _pending_trigger_count - count; + } +} + +void SensorInfo::clear(int count, TRAPS) { + if (_sensor_obj != NULL) { + klassOop k = Management::sun_management_Sensor_klass(CHECK); + instanceKlassHandle sensorKlass (THREAD, k); + Handle sensor(THREAD, _sensor_obj); + + JavaValue result(T_VOID); + JavaCallArguments args(sensor); + args.push_int((int) count); + JavaCalls::call_virtual(&result, + sensorKlass, + vmSymbolHandles::clear_name(), + vmSymbolHandles::int_void_signature(), + &args, + CHECK); + } + + { + // Holds LowMemory_lock and update the sensor state + MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag); + _sensor_on = false; + _pending_clear_count = 0; + _pending_trigger_count = _pending_trigger_count - count; + } +} + +//-------------------------------------------------------------- +// Non-product code + +#ifndef PRODUCT +void SensorInfo::print() { + tty->print_cr("%s count = %ld pending_triggers = %ld pending_clears = %ld", + (_sensor_on ? "on" : "off"), + _sensor_count, _pending_trigger_count, _pending_clear_count); +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/services/lowMemoryDetector.hpp b/hotspot/src/share/vm/services/lowMemoryDetector.hpp new file mode 100644 index 00000000000..684a66a239a --- /dev/null +++ b/hotspot/src/share/vm/services/lowMemoryDetector.hpp @@ -0,0 +1,285 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Low Memory Detection Support +// Two memory alarms in the JDK (we called them sensors). +// - Heap memory sensor +// - Non-heap memory sensor +// When the VM detects if the memory usage of a memory pool has reached +// or exceeded its threshold, it will trigger the sensor for the type +// of the memory pool (heap or nonheap or both). +// +// If threshold == -1, no low memory detection is supported and +// the threshold value is not allowed to be changed. +// If threshold == 0, no low memory detection is performed for +// that memory pool. The threshold can be set to any non-negative +// value. +// +// The default threshold of the Hotspot memory pools are: +// Eden space -1 +// Survivor space 1 -1 +// Survivor space 2 -1 +// Old generation 0 +// Perm generation 0 +// CodeCache 0 +// +// For heap memory, detection will be performed when GC finishes +// and also in the slow path allocation. +// For Code cache, detection will be performed in the allocation +// and deallocation. +// +// May need to deal with hysteresis effect. +// + +class LowMemoryDetectorThread; +class OopClosure; +class MemoryPool; + +class ThresholdSupport : public CHeapObj { + private: + bool _support_high_threshold; + bool _support_low_threshold; + size_t _high_threshold; + size_t _low_threshold; + public: + ThresholdSupport(bool support_high, bool support_low) { + _support_high_threshold = support_high; + _support_low_threshold = support_low; + _high_threshold = 0; + _low_threshold= 0; + } + + size_t high_threshold() const { return _high_threshold; } + size_t low_threshold() const { return _low_threshold; } + bool is_high_threshold_supported() { return _support_high_threshold; } + bool is_low_threshold_supported() { return _support_low_threshold; } + + bool is_high_threshold_crossed(MemoryUsage usage) { + if (_support_high_threshold && _high_threshold > 0) { + return (usage.used() >= _high_threshold); + } + return false; + } + bool is_low_threshold_crossed(MemoryUsage usage) { + if (_support_low_threshold && _low_threshold > 0) { + return (usage.used() < _low_threshold); + } + return false; + } + + size_t set_high_threshold(size_t new_threshold) { + assert(_support_high_threshold, "can only be set if supported"); + assert(new_threshold >= _low_threshold, "new_threshold must be >= _low_threshold"); + size_t prev = _high_threshold; + _high_threshold = new_threshold; + return prev; + } + + size_t set_low_threshold(size_t new_threshold) { + assert(_support_low_threshold, "can only be set if supported"); + assert(new_threshold <= _high_threshold, "new_threshold must be <= _high_threshold"); + size_t prev = _low_threshold; + _low_threshold = new_threshold; + return prev; + } +}; + +class SensorInfo : public CHeapObj { +private: + instanceOop _sensor_obj; + bool _sensor_on; + size_t _sensor_count; + + // before the actual sensor on flag and sensor count are set + // we maintain the number of pending triggers and clears. + // _pending_trigger_count means the number of pending triggers + // and the sensor count should be incremented by the same number. + + int _pending_trigger_count; + + // _pending_clear_count takes precedence if it's > 0 which + // indicates the resulting sensor will be off + // Sensor trigger requests will reset this clear count to + // indicate the resulting flag should be on. + + int _pending_clear_count; + + MemoryUsage _usage; + + void clear(int count, TRAPS); + void trigger(int count, TRAPS); +public: + SensorInfo(); + void set_sensor(instanceOop sensor) { + assert(_sensor_obj == NULL, "Should be set only once"); + _sensor_obj = sensor; + } + + bool has_pending_requests() { + return (_pending_trigger_count > 0 || _pending_clear_count > 0); + } + + int pending_trigger_count() { return _pending_trigger_count; } + int pending_clear_count() { return _pending_clear_count; } + + // When this method is used, the memory usage is monitored + // as a gauge attribute. High and low thresholds are designed + // to provide a hysteresis mechanism to avoid repeated triggering + // of notifications when the attribute value makes small oscillations + // around the high or low threshold value. + // + // The sensor will be triggered if: + // (1) the usage is crossing above the high threshold and + // the sensor is currently off and no pending + // trigger requests; or + // (2) the usage is crossing above the high threshold and + // the sensor will be off (i.e. sensor is currently on + // and has pending clear requests). + // + // Subsequent crossings of the high threshold value do not cause + // any triggers unless the usage becomes less than the low threshold. + // + // The sensor will be cleared if: + // (1) the usage is crossing below the low threshold and + // the sensor is currently on and no pending + // clear requests; or + // (2) the usage is crossing below the low threshold and + // the sensor will be on (i.e. sensor is currently off + // and has pending trigger requests). + // + // Subsequent crossings of the low threshold value do not cause + // any clears unless the usage becomes greater than or equal + // to the high threshold. + // + // If the current level is between high and low threhsold, no change. + // + void set_gauge_sensor_level(MemoryUsage usage, ThresholdSupport* high_low_threshold); + + // When this method is used, the memory usage is monitored as a + // simple counter attribute. The sensor will be triggered + // whenever the usage is crossing the threshold to keep track + // of the number of times the VM detects such a condition occurs. + // + // The sensor will be triggered if: + // - the usage is crossing above the high threshold regardless + // of the current sensor state. + // + // The sensor will be cleared if: + // (1) the usage is crossing below the low threshold and + // the sensor is currently on; or + // (2) the usage is crossing below the low threshold and + // the sensor will be on (i.e. sensor is currently off + // and has pending trigger requests). + // + void set_counter_sensor_level(MemoryUsage usage, ThresholdSupport* counter_threshold); + + void process_pending_requests(TRAPS); + void oops_do(OopClosure* f); + +#ifndef PRODUCT + // printing on default output stream; + void print(); +#endif // PRODUCT +}; + +class LowMemoryDetector : public AllStatic { +friend class LowMemoryDetectorDisabler; +private: + // true if any collected heap has low memory detection enabled + static volatile bool _enabled_for_collected_pools; + // > 0 if temporary disabed + static volatile jint _disabled_count; + + static LowMemoryDetectorThread* _detector_thread; + static void low_memory_detector_thread_entry(JavaThread* thread, TRAPS); + static void check_memory_usage(); + static bool has_pending_requests(); + static bool temporary_disabled() { return _disabled_count > 0; } + static void disable() { Atomic::inc(&_disabled_count); } + static void enable() { Atomic::dec(&_disabled_count); } + +public: + static void initialize(); + static void detect_low_memory(); + static void detect_low_memory(MemoryPool* pool); + static void detect_after_gc_memory(MemoryPool* pool); + + static bool is_enabled(MemoryPool* pool) { + // low memory detection is enabled for collected memory pools + // iff one of the collected memory pool has a sensor and the + // threshold set non-zero + if (pool->usage_sensor() == NULL) { + return false; + } else { + ThresholdSupport* threshold_support = pool->usage_threshold(); + return (threshold_support->is_high_threshold_supported() ? + (threshold_support->high_threshold() > 0) : false); + } + } + + // indicates if low memory detection is enabled for any collected + // memory pools + static inline bool is_enabled_for_collected_pools() { + return !temporary_disabled() && _enabled_for_collected_pools; + } + + // recompute enabled flag + static void recompute_enabled_for_collected_pools(); + + // low memory detection for collected memory pools. + static inline void detect_low_memory_for_collected_pools() { + // no-op if low memory detection not enabled + if (!is_enabled_for_collected_pools()) { + return; + } + int num_memory_pools = MemoryService::num_memory_pools(); + for (int i=0; iis_collected_pool() && is_enabled(pool)) { + size_t used = pool->used_in_bytes(); + size_t high = pool->usage_threshold()->high_threshold(); + if (used > high) { + detect_low_memory(pool); + } + } + } + } + +}; + +class LowMemoryDetectorDisabler: public StackObj { +public: + LowMemoryDetectorDisabler() + { + LowMemoryDetector::disable(); + } + ~LowMemoryDetectorDisabler() + { + assert(LowMemoryDetector::temporary_disabled(), "should be disabled!"); + LowMemoryDetector::enable(); + } +}; diff --git a/hotspot/src/share/vm/services/management.cpp b/hotspot/src/share/vm/services/management.cpp new file mode 100644 index 00000000000..88e5f1e5b88 --- /dev/null +++ b/hotspot/src/share/vm/services/management.cpp @@ -0,0 +1,2019 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_management.cpp.incl" + +PerfVariable* Management::_begin_vm_creation_time = NULL; +PerfVariable* Management::_end_vm_creation_time = NULL; +PerfVariable* Management::_vm_init_done_time = NULL; + +klassOop Management::_sensor_klass = NULL; +klassOop Management::_threadInfo_klass = NULL; +klassOop Management::_memoryUsage_klass = NULL; +klassOop Management::_memoryPoolMXBean_klass = NULL; +klassOop Management::_memoryManagerMXBean_klass = NULL; +klassOop Management::_garbageCollectorMXBean_klass = NULL; +klassOop Management::_managementFactory_klass = NULL; + +jmmOptionalSupport Management::_optional_support = {0}; +TimeStamp Management::_stamp; + +void management_init() { + Management::init(); + ThreadService::init(); + RuntimeService::init(); + ClassLoadingService::init(); +} + +void Management::init() { + EXCEPTION_MARK; + + // These counters are for java.lang.management API support. + // They are created even if -XX:-UsePerfData is set and in + // that case, they will be allocated on C heap. + + _begin_vm_creation_time = + PerfDataManager::create_variable(SUN_RT, "createVmBeginTime", + PerfData::U_None, CHECK); + + _end_vm_creation_time = + PerfDataManager::create_variable(SUN_RT, "createVmEndTime", + PerfData::U_None, CHECK); + + _vm_init_done_time = + PerfDataManager::create_variable(SUN_RT, "vmInitDoneTime", + PerfData::U_None, CHECK); + + // Initialize optional support + _optional_support.isLowMemoryDetectionSupported = 1; + _optional_support.isCompilationTimeMonitoringSupported = 1; + _optional_support.isThreadContentionMonitoringSupported = 1; + + if (os::is_thread_cpu_time_supported()) { + _optional_support.isCurrentThreadCpuTimeSupported = 1; + _optional_support.isOtherThreadCpuTimeSupported = 1; + } else { + _optional_support.isCurrentThreadCpuTimeSupported = 0; + _optional_support.isOtherThreadCpuTimeSupported = 0; + } + _optional_support.isBootClassPathSupported = 1; + _optional_support.isObjectMonitorUsageSupported = 1; +#ifndef SERVICES_KERNEL + // This depends on the heap inspector + _optional_support.isSynchronizerUsageSupported = 1; +#endif // SERVICES_KERNEL +} + +void Management::initialize(TRAPS) { + // Start the low memory detector thread + LowMemoryDetector::initialize(); + + if (ManagementServer) { + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + + // Load and initialize the sun.management.Agent class + // invoke startAgent method to start the management server + Handle loader = Handle(THREAD, SystemDictionary::java_system_loader()); + klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::sun_management_Agent(), + loader, + Handle(), + true, + CHECK); + instanceKlassHandle ik (THREAD, k); + + JavaValue result(T_VOID); + JavaCalls::call_static(&result, + ik, + vmSymbolHandles::startAgent_name(), + vmSymbolHandles::void_method_signature(), + CHECK); + } +} + +void Management::get_optional_support(jmmOptionalSupport* support) { + memcpy(support, &_optional_support, sizeof(jmmOptionalSupport)); +} + +klassOop Management::load_and_initialize_klass(symbolHandle sh, TRAPS) { + klassOop k = SystemDictionary::resolve_or_fail(sh, true, CHECK_NULL); + instanceKlassHandle ik (THREAD, k); + if (ik->should_be_initialized()) { + ik->initialize(CHECK_NULL); + } + return ik(); +} + +void Management::record_vm_startup_time(jlong begin, jlong duration) { + // if the performance counter is not initialized, + // then vm initialization failed; simply return. + if (_begin_vm_creation_time == NULL) return; + + _begin_vm_creation_time->set_value(begin); + _end_vm_creation_time->set_value(begin + duration); + PerfMemory::set_accessible(true); +} + +jlong Management::timestamp() { + TimeStamp t; + t.update(); + return t.ticks() - _stamp.ticks(); +} + +void Management::oops_do(OopClosure* f) { + MemoryService::oops_do(f); + ThreadService::oops_do(f); + + f->do_oop((oop*) &_sensor_klass); + f->do_oop((oop*) &_threadInfo_klass); + f->do_oop((oop*) &_memoryUsage_klass); + f->do_oop((oop*) &_memoryPoolMXBean_klass); + f->do_oop((oop*) &_memoryManagerMXBean_klass); + f->do_oop((oop*) &_garbageCollectorMXBean_klass); + f->do_oop((oop*) &_managementFactory_klass); +} + +klassOop Management::java_lang_management_ThreadInfo_klass(TRAPS) { + if (_threadInfo_klass == NULL) { + _threadInfo_klass = load_and_initialize_klass(vmSymbolHandles::java_lang_management_ThreadInfo(), CHECK_NULL); + } + return _threadInfo_klass; +} + +klassOop Management::java_lang_management_MemoryUsage_klass(TRAPS) { + if (_memoryUsage_klass == NULL) { + _memoryUsage_klass = load_and_initialize_klass(vmSymbolHandles::java_lang_management_MemoryUsage(), CHECK_NULL); + } + return _memoryUsage_klass; +} + +klassOop Management::java_lang_management_MemoryPoolMXBean_klass(TRAPS) { + if (_memoryPoolMXBean_klass == NULL) { + _memoryPoolMXBean_klass = load_and_initialize_klass(vmSymbolHandles::java_lang_management_MemoryPoolMXBean(), CHECK_NULL); + } + return _memoryPoolMXBean_klass; +} + +klassOop Management::java_lang_management_MemoryManagerMXBean_klass(TRAPS) { + if (_memoryManagerMXBean_klass == NULL) { + _memoryManagerMXBean_klass = load_and_initialize_klass(vmSymbolHandles::java_lang_management_MemoryManagerMXBean(), CHECK_NULL); + } + return _memoryManagerMXBean_klass; +} + +klassOop Management::java_lang_management_GarbageCollectorMXBean_klass(TRAPS) { + if (_garbageCollectorMXBean_klass == NULL) { + _garbageCollectorMXBean_klass = load_and_initialize_klass(vmSymbolHandles::java_lang_management_GarbageCollectorMXBean(), CHECK_NULL); + } + return _garbageCollectorMXBean_klass; +} + +klassOop Management::sun_management_Sensor_klass(TRAPS) { + if (_sensor_klass == NULL) { + _sensor_klass = load_and_initialize_klass(vmSymbolHandles::sun_management_Sensor(), CHECK_NULL); + } + return _sensor_klass; +} + +klassOop Management::sun_management_ManagementFactory_klass(TRAPS) { + if (_managementFactory_klass == NULL) { + _managementFactory_klass = load_and_initialize_klass(vmSymbolHandles::sun_management_ManagementFactory(), CHECK_NULL); + } + return _managementFactory_klass; +} + +static void initialize_ThreadInfo_constructor_arguments(JavaCallArguments* args, ThreadSnapshot* snapshot, TRAPS) { + Handle snapshot_thread(THREAD, snapshot->threadObj()); + + jlong contended_time; + jlong waited_time; + if (ThreadService::is_thread_monitoring_contention()) { + contended_time = Management::ticks_to_ms(snapshot->contended_enter_ticks()); + waited_time = Management::ticks_to_ms(snapshot->monitor_wait_ticks() + snapshot->sleep_ticks()); + } else { + // set them to -1 if thread contention monitoring is disabled. + contended_time = max_julong; + waited_time = max_julong; + } + + int thread_status = snapshot->thread_status(); + assert((thread_status & JMM_THREAD_STATE_FLAG_MASK) == 0, "Flags already set in thread_status in Thread object"); + if (snapshot->is_ext_suspended()) { + thread_status |= JMM_THREAD_STATE_FLAG_SUSPENDED; + } + if (snapshot->is_in_native()) { + thread_status |= JMM_THREAD_STATE_FLAG_NATIVE; + } + + ThreadStackTrace* st = snapshot->get_stack_trace(); + Handle stacktrace_h; + if (st != NULL) { + stacktrace_h = st->allocate_fill_stack_trace_element_array(CHECK); + } else { + stacktrace_h = Handle(); + } + + args->push_oop(snapshot_thread); + args->push_int(thread_status); + args->push_oop(Handle(THREAD, snapshot->blocker_object())); + args->push_oop(Handle(THREAD, snapshot->blocker_object_owner())); + args->push_long(snapshot->contended_enter_count()); + args->push_long(contended_time); + args->push_long(snapshot->monitor_wait_count() + snapshot->sleep_count()); + args->push_long(waited_time); + args->push_oop(stacktrace_h); +} + +// Helper function to construct a ThreadInfo object +instanceOop Management::create_thread_info_instance(ThreadSnapshot* snapshot, TRAPS) { + klassOop k = Management::java_lang_management_ThreadInfo_klass(CHECK_NULL); + instanceKlassHandle ik (THREAD, k); + + JavaValue result(T_VOID); + JavaCallArguments args(14); + + // First allocate a ThreadObj object and + // push the receiver as the first argument + Handle element = ik->allocate_instance_handle(CHECK_NULL); + args.push_oop(element); + + // initialize the arguments for the ThreadInfo constructor + initialize_ThreadInfo_constructor_arguments(&args, snapshot, CHECK_NULL); + + // Call ThreadInfo constructor with no locked monitors and synchronizers + JavaCalls::call_special(&result, + ik, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::java_lang_management_ThreadInfo_constructor_signature(), + &args, + CHECK_NULL); + + return (instanceOop) element(); +} + +instanceOop Management::create_thread_info_instance(ThreadSnapshot* snapshot, + objArrayHandle monitors_array, + typeArrayHandle depths_array, + objArrayHandle synchronizers_array, + TRAPS) { + klassOop k = Management::java_lang_management_ThreadInfo_klass(CHECK_NULL); + instanceKlassHandle ik (THREAD, k); + + JavaValue result(T_VOID); + JavaCallArguments args(17); + + // First allocate a ThreadObj object and + // push the receiver as the first argument + Handle element = ik->allocate_instance_handle(CHECK_NULL); + args.push_oop(element); + + // initialize the arguments for the ThreadInfo constructor + initialize_ThreadInfo_constructor_arguments(&args, snapshot, CHECK_NULL); + + // push the locked monitors and synchronizers in the arguments + args.push_oop(monitors_array); + args.push_oop(depths_array); + args.push_oop(synchronizers_array); + + // Call ThreadInfo constructor with locked monitors and synchronizers + JavaCalls::call_special(&result, + ik, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::java_lang_management_ThreadInfo_with_locks_constructor_signature(), + &args, + CHECK_NULL); + + return (instanceOop) element(); +} + +// Helper functions +static JavaThread* find_java_thread_from_id(jlong thread_id) { + assert(Threads_lock->owned_by_self(), "Must hold Threads_lock"); + + JavaThread* java_thread = NULL; + // Sequential search for now. Need to do better optimization later. + for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) { + oop tobj = thread->threadObj(); + if (!thread->is_exiting() && + tobj != NULL && + thread_id == java_lang_Thread::thread_id(tobj)) { + java_thread = thread; + break; + } + } + return java_thread; +} + +static GCMemoryManager* get_gc_memory_manager_from_jobject(jobject mgr, TRAPS) { + if (mgr == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), NULL); + } + oop mgr_obj = JNIHandles::resolve(mgr); + instanceHandle h(THREAD, (instanceOop) mgr_obj); + + klassOop k = Management::java_lang_management_GarbageCollectorMXBean_klass(CHECK_NULL); + if (!h->is_a(k)) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "the object is not an instance of java.lang.management.GarbageCollectorMXBean class", + NULL); + } + + MemoryManager* gc = MemoryService::get_memory_manager(h); + if (gc == NULL || !gc->is_gc_memory_manager()) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "Invalid GC memory manager", + NULL); + } + return (GCMemoryManager*) gc; +} + +static MemoryPool* get_memory_pool_from_jobject(jobject obj, TRAPS) { + if (obj == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), NULL); + } + + oop pool_obj = JNIHandles::resolve(obj); + assert(pool_obj->is_instance(), "Should be an instanceOop"); + instanceHandle ph(THREAD, (instanceOop) pool_obj); + + return MemoryService::get_memory_pool(ph); +} + +static void validate_thread_id_array(typeArrayHandle ids_ah, TRAPS) { + int num_threads = ids_ah->length(); + // should be non-empty array + if (num_threads == 0) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Empty array of thread IDs"); + } + + // Validate input thread IDs + int i = 0; + for (i = 0; i < num_threads; i++) { + jlong tid = ids_ah->long_at(i); + if (tid <= 0) { + // throw exception if invalid thread id. + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Invalid thread ID entry"); + } + } + +} + +static void validate_thread_info_array(objArrayHandle infoArray_h, TRAPS) { + + // check if the element of infoArray is of type ThreadInfo class + klassOop threadinfo_klass = Management::java_lang_management_ThreadInfo_klass(CHECK); + klassOop element_klass = objArrayKlass::cast(infoArray_h->klass())->element_klass(); + if (element_klass != threadinfo_klass) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "infoArray element type is not ThreadInfo class"); + } + +} + + +static MemoryManager* get_memory_manager_from_jobject(jobject obj, TRAPS) { + if (obj == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), NULL); + } + + oop mgr_obj = JNIHandles::resolve(obj); + assert(mgr_obj->is_instance(), "Should be an instanceOop"); + instanceHandle mh(THREAD, (instanceOop) mgr_obj); + + return MemoryService::get_memory_manager(mh); +} + +// Returns a version string and sets major and minor version if +// the input parameters are non-null. +JVM_LEAF(jint, jmm_GetVersion(JNIEnv *env)) + return JMM_VERSION; +JVM_END + +// Gets the list of VM monitoring and management optional supports +// Returns 0 if succeeded; otherwise returns non-zero. +JVM_LEAF(jint, jmm_GetOptionalSupport(JNIEnv *env, jmmOptionalSupport* support)) + if (support == NULL) { + return -1; + } + Management::get_optional_support(support); + return 0; +JVM_END + +// Returns a java.lang.String object containing the input arguments to the VM. +JVM_ENTRY(jobject, jmm_GetInputArguments(JNIEnv *env)) + ResourceMark rm(THREAD); + + if (Arguments::num_jvm_args() == 0 && Arguments::num_jvm_flags() == 0) { + return NULL; + } + + char** vm_flags = Arguments::jvm_flags_array(); + char** vm_args = Arguments::jvm_args_array(); + int num_flags = Arguments::num_jvm_flags(); + int num_args = Arguments::num_jvm_args(); + + size_t length = 1; // null terminator + int i; + for (i = 0; i < num_flags; i++) { + length += strlen(vm_flags[i]); + } + for (i = 0; i < num_args; i++) { + length += strlen(vm_args[i]); + } + // add a space between each argument + length += num_flags + num_args - 1; + + // Return the list of input arguments passed to the VM + // and preserve the order that the VM processes. + char* args = NEW_RESOURCE_ARRAY(char, length); + args[0] = '\0'; + // concatenate all jvm_flags + if (num_flags > 0) { + strcat(args, vm_flags[0]); + for (i = 1; i < num_flags; i++) { + strcat(args, " "); + strcat(args, vm_flags[i]); + } + } + + if (num_args > 0 && num_flags > 0) { + // append a space if args already contains one or more jvm_flags + strcat(args, " "); + } + + // concatenate all jvm_args + if (num_args > 0) { + strcat(args, vm_args[0]); + for (i = 1; i < num_args; i++) { + strcat(args, " "); + strcat(args, vm_args[i]); + } + } + + Handle hargs = java_lang_String::create_from_platform_dependent_str(args, CHECK_NULL); + return JNIHandles::make_local(env, hargs()); +JVM_END + +// Returns an array of java.lang.String object containing the input arguments to the VM. +JVM_ENTRY(jobjectArray, jmm_GetInputArgumentArray(JNIEnv *env)) + ResourceMark rm(THREAD); + + if (Arguments::num_jvm_args() == 0 && Arguments::num_jvm_flags() == 0) { + return NULL; + } + + char** vm_flags = Arguments::jvm_flags_array(); + char** vm_args = Arguments::jvm_args_array(); + int num_flags = Arguments::num_jvm_flags(); + int num_args = Arguments::num_jvm_args(); + + instanceKlassHandle ik (THREAD, SystemDictionary::string_klass()); + objArrayOop r = oopFactory::new_objArray(ik(), num_args + num_flags, CHECK_NULL); + objArrayHandle result_h(THREAD, r); + + int index = 0; + for (int j = 0; j < num_flags; j++, index++) { + Handle h = java_lang_String::create_from_platform_dependent_str(vm_flags[j], CHECK_NULL); + result_h->obj_at_put(index, h()); + } + for (int i = 0; i < num_args; i++, index++) { + Handle h = java_lang_String::create_from_platform_dependent_str(vm_args[i], CHECK_NULL); + result_h->obj_at_put(index, h()); + } + return (jobjectArray) JNIHandles::make_local(env, result_h()); +JVM_END + +// Returns an array of java/lang/management/MemoryPoolMXBean object +// one for each memory pool if obj == null; otherwise returns +// an array of memory pools for a given memory manager if +// it is a valid memory manager. +JVM_ENTRY(jobjectArray, jmm_GetMemoryPools(JNIEnv* env, jobject obj)) + ResourceMark rm(THREAD); + + int num_memory_pools; + MemoryManager* mgr = NULL; + if (obj == NULL) { + num_memory_pools = MemoryService::num_memory_pools(); + } else { + mgr = get_memory_manager_from_jobject(obj, CHECK_NULL); + if (mgr == NULL) { + return NULL; + } + num_memory_pools = mgr->num_memory_pools(); + } + + // Allocate the resulting MemoryPoolMXBean[] object + klassOop k = Management::java_lang_management_MemoryPoolMXBean_klass(CHECK_NULL); + instanceKlassHandle ik (THREAD, k); + objArrayOop r = oopFactory::new_objArray(ik(), num_memory_pools, CHECK_NULL); + objArrayHandle poolArray(THREAD, r); + + if (mgr == NULL) { + // Get all memory pools + for (int i = 0; i < num_memory_pools; i++) { + MemoryPool* pool = MemoryService::get_memory_pool(i); + instanceOop p = pool->get_memory_pool_instance(CHECK_NULL); + instanceHandle ph(THREAD, p); + poolArray->obj_at_put(i, ph()); + } + } else { + // Get memory pools managed by a given memory manager + for (int i = 0; i < num_memory_pools; i++) { + MemoryPool* pool = mgr->get_memory_pool(i); + instanceOop p = pool->get_memory_pool_instance(CHECK_NULL); + instanceHandle ph(THREAD, p); + poolArray->obj_at_put(i, ph()); + } + } + return (jobjectArray) JNIHandles::make_local(env, poolArray()); +JVM_END + +// Returns an array of java/lang/management/MemoryManagerMXBean object +// one for each memory manager if obj == null; otherwise returns +// an array of memory managers for a given memory pool if +// it is a valid memory pool. +JVM_ENTRY(jobjectArray, jmm_GetMemoryManagers(JNIEnv* env, jobject obj)) + ResourceMark rm(THREAD); + + int num_mgrs; + MemoryPool* pool = NULL; + if (obj == NULL) { + num_mgrs = MemoryService::num_memory_managers(); + } else { + pool = get_memory_pool_from_jobject(obj, CHECK_NULL); + if (pool == NULL) { + return NULL; + } + num_mgrs = pool->num_memory_managers(); + } + + // Allocate the resulting MemoryManagerMXBean[] object + klassOop k = Management::java_lang_management_MemoryManagerMXBean_klass(CHECK_NULL); + instanceKlassHandle ik (THREAD, k); + objArrayOop r = oopFactory::new_objArray(ik(), num_mgrs, CHECK_NULL); + objArrayHandle mgrArray(THREAD, r); + + if (pool == NULL) { + // Get all memory managers + for (int i = 0; i < num_mgrs; i++) { + MemoryManager* mgr = MemoryService::get_memory_manager(i); + instanceOop p = mgr->get_memory_manager_instance(CHECK_NULL); + instanceHandle ph(THREAD, p); + mgrArray->obj_at_put(i, ph()); + } + } else { + // Get memory managers for a given memory pool + for (int i = 0; i < num_mgrs; i++) { + MemoryManager* mgr = pool->get_memory_manager(i); + instanceOop p = mgr->get_memory_manager_instance(CHECK_NULL); + instanceHandle ph(THREAD, p); + mgrArray->obj_at_put(i, ph()); + } + } + return (jobjectArray) JNIHandles::make_local(env, mgrArray()); +JVM_END + + +// Returns a java/lang/management/MemoryUsage object containing the memory usage +// of a given memory pool. +JVM_ENTRY(jobject, jmm_GetMemoryPoolUsage(JNIEnv* env, jobject obj)) + ResourceMark rm(THREAD); + + MemoryPool* pool = get_memory_pool_from_jobject(obj, CHECK_NULL); + if (pool != NULL) { + MemoryUsage usage = pool->get_memory_usage(); + Handle h = MemoryService::create_MemoryUsage_obj(usage, CHECK_NULL); + return JNIHandles::make_local(env, h()); + } else { + return NULL; + } +JVM_END + +// Returns a java/lang/management/MemoryUsage object containing the memory usage +// of a given memory pool. +JVM_ENTRY(jobject, jmm_GetPeakMemoryPoolUsage(JNIEnv* env, jobject obj)) + ResourceMark rm(THREAD); + + MemoryPool* pool = get_memory_pool_from_jobject(obj, CHECK_NULL); + if (pool != NULL) { + MemoryUsage usage = pool->get_peak_memory_usage(); + Handle h = MemoryService::create_MemoryUsage_obj(usage, CHECK_NULL); + return JNIHandles::make_local(env, h()); + } else { + return NULL; + } +JVM_END + +// Returns a java/lang/management/MemoryUsage object containing the memory usage +// of a given memory pool after most recent GC. +JVM_ENTRY(jobject, jmm_GetPoolCollectionUsage(JNIEnv* env, jobject obj)) + ResourceMark rm(THREAD); + + MemoryPool* pool = get_memory_pool_from_jobject(obj, CHECK_NULL); + if (pool != NULL && pool->is_collected_pool()) { + MemoryUsage usage = pool->get_last_collection_usage(); + Handle h = MemoryService::create_MemoryUsage_obj(usage, CHECK_NULL); + return JNIHandles::make_local(env, h()); + } else { + return NULL; + } +JVM_END + +// Sets the memory pool sensor for a threshold type +JVM_ENTRY(void, jmm_SetPoolSensor(JNIEnv* env, jobject obj, jmmThresholdType type, jobject sensorObj)) + if (obj == NULL || sensorObj == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + + klassOop sensor_klass = Management::sun_management_Sensor_klass(CHECK); + oop s = JNIHandles::resolve(sensorObj); + assert(s->is_instance(), "Sensor should be an instanceOop"); + instanceHandle sensor_h(THREAD, (instanceOop) s); + if (!sensor_h->is_a(sensor_klass)) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Sensor is not an instance of sun.management.Sensor class"); + } + + MemoryPool* mpool = get_memory_pool_from_jobject(obj, CHECK); + assert(mpool != NULL, "MemoryPool should exist"); + + switch (type) { + case JMM_USAGE_THRESHOLD_HIGH: + case JMM_USAGE_THRESHOLD_LOW: + // have only one sensor for threshold high and low + mpool->set_usage_sensor_obj(sensor_h); + break; + case JMM_COLLECTION_USAGE_THRESHOLD_HIGH: + case JMM_COLLECTION_USAGE_THRESHOLD_LOW: + // have only one sensor for threshold high and low + mpool->set_gc_usage_sensor_obj(sensor_h); + break; + default: + assert(false, "Unrecognized type"); + } + +JVM_END + + +// Sets the threshold of a given memory pool. +// Returns the previous threshold. +// +// Input parameters: +// pool - the MemoryPoolMXBean object +// type - threshold type +// threshold - the new threshold (must not be negative) +// +JVM_ENTRY(jlong, jmm_SetPoolThreshold(JNIEnv* env, jobject obj, jmmThresholdType type, jlong threshold)) + if (threshold < 0) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "Invalid threshold value", + -1); + } + + if (threshold > max_intx) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "Invalid threshold value > max value of size_t", + -1); + } + + MemoryPool* pool = get_memory_pool_from_jobject(obj, CHECK_(0L)); + assert(pool != NULL, "MemoryPool should exist"); + + jlong prev = 0; + switch (type) { + case JMM_USAGE_THRESHOLD_HIGH: + if (!pool->usage_threshold()->is_high_threshold_supported()) { + return -1; + } + prev = pool->usage_threshold()->set_high_threshold((size_t) threshold); + break; + + case JMM_USAGE_THRESHOLD_LOW: + if (!pool->usage_threshold()->is_low_threshold_supported()) { + return -1; + } + prev = pool->usage_threshold()->set_low_threshold((size_t) threshold); + break; + + case JMM_COLLECTION_USAGE_THRESHOLD_HIGH: + if (!pool->gc_usage_threshold()->is_high_threshold_supported()) { + return -1; + } + // return and the new threshold is effective for the next GC + return pool->gc_usage_threshold()->set_high_threshold((size_t) threshold); + + case JMM_COLLECTION_USAGE_THRESHOLD_LOW: + if (!pool->gc_usage_threshold()->is_low_threshold_supported()) { + return -1; + } + // return and the new threshold is effective for the next GC + return pool->gc_usage_threshold()->set_low_threshold((size_t) threshold); + + default: + assert(false, "Unrecognized type"); + return -1; + } + + // When the threshold is changed, reevaluate if the low memory + // detection is enabled. + if (prev != threshold) { + LowMemoryDetector::recompute_enabled_for_collected_pools(); + LowMemoryDetector::detect_low_memory(pool); + } + return prev; +JVM_END + +// Returns a java/lang/management/MemoryUsage object representing +// the memory usage for the heap or non-heap memory. +JVM_ENTRY(jobject, jmm_GetMemoryUsage(JNIEnv* env, jboolean heap)) + ResourceMark rm(THREAD); + + // Calculate the memory usage + size_t total_init = 0; + size_t total_used = 0; + size_t total_committed = 0; + size_t total_max = 0; + bool has_undefined_init_size = false; + bool has_undefined_max_size = false; + + for (int i = 0; i < MemoryService::num_memory_pools(); i++) { + MemoryPool* pool = MemoryService::get_memory_pool(i); + if ((heap && pool->is_heap()) || (!heap && pool->is_non_heap())) { + MemoryUsage u = pool->get_memory_usage(); + total_used += u.used(); + total_committed += u.committed(); + + // if any one of the memory pool has undefined init_size or max_size, + // set it to -1 + if (u.init_size() == (size_t)-1) { + has_undefined_init_size = true; + } + if (!has_undefined_init_size) { + total_init += u.init_size(); + } + + if (u.max_size() == (size_t)-1) { + has_undefined_max_size = true; + } + if (!has_undefined_max_size) { + total_max += u.max_size(); + } + } + } + + // In our current implementation, all pools should have + // defined init and max size + assert(!has_undefined_init_size, "Undefined init size"); + assert(!has_undefined_max_size, "Undefined max size"); + + MemoryUsage usage((heap ? Arguments::initial_heap_size() : total_init), + total_used, + total_committed, + (heap ? Universe::heap()->max_capacity() : total_max)); + + Handle obj = MemoryService::create_MemoryUsage_obj(usage, CHECK_NULL); + return JNIHandles::make_local(env, obj()); +JVM_END + +// Returns the boolean value of a given attribute. +JVM_LEAF(jboolean, jmm_GetBoolAttribute(JNIEnv *env, jmmBoolAttribute att)) + switch (att) { + case JMM_VERBOSE_GC: + return MemoryService::get_verbose(); + case JMM_VERBOSE_CLASS: + return ClassLoadingService::get_verbose(); + case JMM_THREAD_CONTENTION_MONITORING: + return ThreadService::is_thread_monitoring_contention(); + case JMM_THREAD_CPU_TIME: + return ThreadService::is_thread_cpu_time_enabled(); + default: + assert(0, "Unrecognized attribute"); + return false; + } +JVM_END + +// Sets the given boolean attribute and returns the previous value. +JVM_ENTRY(jboolean, jmm_SetBoolAttribute(JNIEnv *env, jmmBoolAttribute att, jboolean flag)) + switch (att) { + case JMM_VERBOSE_GC: + return MemoryService::set_verbose(flag != 0); + case JMM_VERBOSE_CLASS: + return ClassLoadingService::set_verbose(flag != 0); + case JMM_THREAD_CONTENTION_MONITORING: + return ThreadService::set_thread_monitoring_contention(flag != 0); + case JMM_THREAD_CPU_TIME: + return ThreadService::set_thread_cpu_time_enabled(flag != 0); + default: + assert(0, "Unrecognized attribute"); + return false; + } +JVM_END + + +static jlong get_gc_attribute(GCMemoryManager* mgr, jmmLongAttribute att) { + switch (att) { + case JMM_GC_TIME_MS: + return mgr->gc_time_ms(); + + case JMM_GC_COUNT: + return mgr->gc_count(); + + case JMM_GC_EXT_ATTRIBUTE_INFO_SIZE: + // current implementation only has 1 ext attribute + return 1; + + default: + assert(0, "Unrecognized GC attribute"); + return -1; + } +} + +class VmThreadCountClosure: public ThreadClosure { + private: + int _count; + public: + VmThreadCountClosure() : _count(0) {}; + void do_thread(Thread* thread); + int count() { return _count; } +}; + +void VmThreadCountClosure::do_thread(Thread* thread) { + // exclude externally visible JavaThreads + if (thread->is_Java_thread() && !thread->is_hidden_from_external_view()) { + return; + } + + _count++; +} + +static jint get_vm_thread_count() { + VmThreadCountClosure vmtcc; + { + MutexLockerEx ml(Threads_lock); + Threads::threads_do(&vmtcc); + } + + return vmtcc.count(); +} + +static jint get_num_flags() { + // last flag entry is always NULL, so subtract 1 + int nFlags = (int) Flag::numFlags - 1; + int count = 0; + for (int i = 0; i < nFlags; i++) { + Flag* flag = &Flag::flags[i]; + // Exclude the diagnostic flags + if (flag->is_unlocked() || flag->is_unlocker()) { + count++; + } + } + return count; +} + +static jlong get_long_attribute(jmmLongAttribute att) { + switch (att) { + case JMM_CLASS_LOADED_COUNT: + return ClassLoadingService::loaded_class_count(); + + case JMM_CLASS_UNLOADED_COUNT: + return ClassLoadingService::unloaded_class_count(); + + case JMM_THREAD_TOTAL_COUNT: + return ThreadService::get_total_thread_count(); + + case JMM_THREAD_LIVE_COUNT: + return ThreadService::get_live_thread_count(); + + case JMM_THREAD_PEAK_COUNT: + return ThreadService::get_peak_thread_count(); + + case JMM_THREAD_DAEMON_COUNT: + return ThreadService::get_daemon_thread_count(); + + case JMM_JVM_INIT_DONE_TIME_MS: + return Management::vm_init_done_time(); + + case JMM_COMPILE_TOTAL_TIME_MS: + return Management::ticks_to_ms(CompileBroker::total_compilation_ticks()); + + case JMM_OS_PROCESS_ID: + return os::current_process_id(); + + // Hotspot-specific counters + case JMM_CLASS_LOADED_BYTES: + return ClassLoadingService::loaded_class_bytes(); + + case JMM_CLASS_UNLOADED_BYTES: + return ClassLoadingService::unloaded_class_bytes(); + + case JMM_SHARED_CLASS_LOADED_COUNT: + return ClassLoadingService::loaded_shared_class_count(); + + case JMM_SHARED_CLASS_UNLOADED_COUNT: + return ClassLoadingService::unloaded_shared_class_count(); + + + case JMM_SHARED_CLASS_LOADED_BYTES: + return ClassLoadingService::loaded_shared_class_bytes(); + + case JMM_SHARED_CLASS_UNLOADED_BYTES: + return ClassLoadingService::unloaded_shared_class_bytes(); + + case JMM_TOTAL_CLASSLOAD_TIME_MS: + return ClassLoader::classloader_time_ms(); + + case JMM_VM_GLOBAL_COUNT: + return get_num_flags(); + + case JMM_SAFEPOINT_COUNT: + return RuntimeService::safepoint_count(); + + case JMM_TOTAL_SAFEPOINTSYNC_TIME_MS: + return RuntimeService::safepoint_sync_time_ms(); + + case JMM_TOTAL_STOPPED_TIME_MS: + return RuntimeService::safepoint_time_ms(); + + case JMM_TOTAL_APP_TIME_MS: + return RuntimeService::application_time_ms(); + + case JMM_VM_THREAD_COUNT: + return get_vm_thread_count(); + + case JMM_CLASS_INIT_TOTAL_COUNT: + return ClassLoader::class_init_count(); + + case JMM_CLASS_INIT_TOTAL_TIME_MS: + return ClassLoader::class_init_time_ms(); + + case JMM_CLASS_VERIFY_TOTAL_TIME_MS: + return ClassLoader::class_verify_time_ms(); + + case JMM_METHOD_DATA_SIZE_BYTES: + return ClassLoadingService::class_method_data_size(); + + case JMM_OS_MEM_TOTAL_PHYSICAL_BYTES: + return os::physical_memory(); + + default: + return -1; + } +} + + +// Returns the long value of a given attribute. +JVM_ENTRY(jlong, jmm_GetLongAttribute(JNIEnv *env, jobject obj, jmmLongAttribute att)) + if (obj == NULL) { + return get_long_attribute(att); + } else { + GCMemoryManager* mgr = get_gc_memory_manager_from_jobject(obj, CHECK_(0L)); + if (mgr != NULL) { + return get_gc_attribute(mgr, att); + } + } + return -1; +JVM_END + +// Gets the value of all attributes specified in the given array +// and sets the value in the result array. +// Returns the number of attributes found. +JVM_ENTRY(jint, jmm_GetLongAttributes(JNIEnv *env, + jobject obj, + jmmLongAttribute* atts, + jint count, + jlong* result)) + + int num_atts = 0; + if (obj == NULL) { + for (int i = 0; i < count; i++) { + result[i] = get_long_attribute(atts[i]); + if (result[i] != -1) { + num_atts++; + } + } + } else { + GCMemoryManager* mgr = get_gc_memory_manager_from_jobject(obj, CHECK_0); + for (int i = 0; i < count; i++) { + result[i] = get_gc_attribute(mgr, atts[i]); + if (result[i] != -1) { + num_atts++; + } + } + } + return num_atts; +JVM_END + +// Helper function to do thread dump for a specific list of threads +static void do_thread_dump(ThreadDumpResult* dump_result, + typeArrayHandle ids_ah, // array of thread ID (long[]) + int num_threads, + int max_depth, + bool with_locked_monitors, + bool with_locked_synchronizers, + TRAPS) { + + // First get an array of threadObj handles. + // A JavaThread may terminate before we get the stack trace. + GrowableArray* thread_handle_array = new GrowableArray(num_threads); + { + MutexLockerEx ml(Threads_lock); + for (int i = 0; i < num_threads; i++) { + jlong tid = ids_ah->long_at(i); + JavaThread* jt = find_java_thread_from_id(tid); + oop thread_obj = (jt != NULL ? jt->threadObj() : (oop)NULL); + instanceHandle threadObj_h(THREAD, (instanceOop) thread_obj); + thread_handle_array->append(threadObj_h); + } + } + + // Obtain thread dumps and thread snapshot information + VM_ThreadDump op(dump_result, + thread_handle_array, + num_threads, + max_depth, /* stack depth */ + with_locked_monitors, + with_locked_synchronizers); + VMThread::execute(&op); +} + +// Gets an array of ThreadInfo objects. Each element is the ThreadInfo +// for the thread ID specified in the corresponding entry in +// the given array of thread IDs; or NULL if the thread does not exist +// or has terminated. +// +// Input parameters: +// ids - array of thread IDs +// maxDepth - the maximum depth of stack traces to be dumped: +// maxDepth == -1 requests to dump entire stack trace. +// maxDepth == 0 requests no stack trace. +// infoArray - array of ThreadInfo objects +// +JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jobjectArray infoArray)) + // Check if threads is null + if (ids == NULL || infoArray == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), -1); + } + + if (maxDepth < -1) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "Invalid maxDepth", -1); + } + + ResourceMark rm(THREAD); + typeArrayOop ta = typeArrayOop(JNIHandles::resolve_non_null(ids)); + typeArrayHandle ids_ah(THREAD, ta); + + oop infoArray_obj = JNIHandles::resolve_non_null(infoArray); + objArrayOop oa = objArrayOop(infoArray_obj); + objArrayHandle infoArray_h(THREAD, oa); + + // validate the thread id array + validate_thread_id_array(ids_ah, CHECK_0); + + // validate the ThreadInfo[] parameters + validate_thread_info_array(infoArray_h, CHECK_0); + + // infoArray must be of the same length as the given array of thread IDs + int num_threads = ids_ah->length(); + if (num_threads != infoArray_h->length()) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "The length of the given ThreadInfo array does not match the length of the given array of thread IDs", -1); + } + + if (JDK_Version::is_gte_jdk16x_version()) { + // make sure the AbstractOwnableSynchronizer klass is loaded before taking thread snapshots + java_util_concurrent_locks_AbstractOwnableSynchronizer::initialize(CHECK_0); + } + + // Must use ThreadDumpResult to store the ThreadSnapshot. + // GC may occur after the thread snapshots are taken but before + // this function returns. The threadObj and other oops kept + // in the ThreadSnapshot are marked and adjusted during GC. + ThreadDumpResult dump_result(num_threads); + + if (maxDepth == 0) { + // no stack trace dumped - do not need to stop the world + { + MutexLockerEx ml(Threads_lock); + for (int i = 0; i < num_threads; i++) { + jlong tid = ids_ah->long_at(i); + JavaThread* jt = find_java_thread_from_id(tid); + ThreadSnapshot* ts; + if (jt == NULL) { + // if the thread does not exist or now it is terminated, + // create dummy snapshot + ts = new ThreadSnapshot(); + } else { + ts = new ThreadSnapshot(jt); + } + dump_result.add_thread_snapshot(ts); + } + } + } else { + // obtain thread dump with the specific list of threads with stack trace + + do_thread_dump(&dump_result, + ids_ah, + num_threads, + maxDepth, + false, /* no locked monitor */ + false, /* no locked synchronizers */ + CHECK_0); + } + + int num_snapshots = dump_result.num_snapshots(); + assert(num_snapshots == num_threads, "Must match the number of thread snapshots"); + int index = 0; + for (ThreadSnapshot* ts = dump_result.snapshots(); ts != NULL; index++, ts = ts->next()) { + // For each thread, create an java/lang/management/ThreadInfo object + // and fill with the thread information + + if (ts->threadObj() == NULL) { + // if the thread does not exist or now it is terminated, set threadinfo to NULL + infoArray_h->obj_at_put(index, NULL); + continue; + } + + // Create java.lang.management.ThreadInfo object + instanceOop info_obj = Management::create_thread_info_instance(ts, CHECK_0); + infoArray_h->obj_at_put(index, info_obj); + } + return 0; +JVM_END + +// Dump thread info for the specified threads. +// It returns an array of ThreadInfo objects. Each element is the ThreadInfo +// for the thread ID specified in the corresponding entry in +// the given array of thread IDs; or NULL if the thread does not exist +// or has terminated. +// +// Input parameter: +// ids - array of thread IDs; NULL indicates all live threads +// locked_monitors - if true, dump locked object monitors +// locked_synchronizers - if true, dump locked JSR-166 synchronizers +// +JVM_ENTRY(jobjectArray, jmm_DumpThreads(JNIEnv *env, jlongArray thread_ids, jboolean locked_monitors, jboolean locked_synchronizers)) + ResourceMark rm(THREAD); + + if (JDK_Version::is_gte_jdk16x_version()) { + // make sure the AbstractOwnableSynchronizer klass is loaded before taking thread snapshots + java_util_concurrent_locks_AbstractOwnableSynchronizer::initialize(CHECK_NULL); + } + + typeArrayOop ta = typeArrayOop(JNIHandles::resolve(thread_ids)); + int num_threads = (ta != NULL ? ta->length() : 0); + typeArrayHandle ids_ah(THREAD, ta); + + ThreadDumpResult dump_result(num_threads); // can safepoint + + if (ids_ah() != NULL) { + + // validate the thread id array + validate_thread_id_array(ids_ah, CHECK_NULL); + + // obtain thread dump of a specific list of threads + do_thread_dump(&dump_result, + ids_ah, + num_threads, + -1, /* entire stack */ + (locked_monitors ? true : false), /* with locked monitors */ + (locked_synchronizers ? true : false), /* with locked synchronizers */ + CHECK_NULL); + } else { + // obtain thread dump of all threads + VM_ThreadDump op(&dump_result, + -1, /* entire stack */ + (locked_monitors ? true : false), /* with locked monitors */ + (locked_synchronizers ? true : false) /* with locked synchronizers */); + VMThread::execute(&op); + } + + int num_snapshots = dump_result.num_snapshots(); + + // create the result ThreadInfo[] object + klassOop k = Management::java_lang_management_ThreadInfo_klass(CHECK_NULL); + instanceKlassHandle ik (THREAD, k); + objArrayOop r = oopFactory::new_objArray(ik(), num_snapshots, CHECK_NULL); + objArrayHandle result_h(THREAD, r); + + int index = 0; + for (ThreadSnapshot* ts = dump_result.snapshots(); ts != NULL; ts = ts->next(), index++) { + if (ts->threadObj() == NULL) { + // if the thread does not exist or now it is terminated, set threadinfo to NULL + result_h->obj_at_put(index, NULL); + continue; + } + + + + ThreadStackTrace* stacktrace = ts->get_stack_trace(); + assert(stacktrace != NULL, "Must have a stack trace dumped"); + + // Create Object[] filled with locked monitors + // Create int[] filled with the stack depth where a monitor was locked + int num_frames = stacktrace->get_stack_depth(); + int num_locked_monitors = stacktrace->num_jni_locked_monitors(); + + // Count the total number of locked monitors + for (int i = 0; i < num_frames; i++) { + StackFrameInfo* frame = stacktrace->stack_frame_at(i); + num_locked_monitors += frame->num_locked_monitors(); + } + + objArrayHandle monitors_array; + typeArrayHandle depths_array; + objArrayHandle synchronizers_array; + + if (locked_monitors) { + // Constructs Object[] and int[] to contain the object monitor and the stack depth + // where the thread locked it + objArrayOop array = oopFactory::new_system_objArray(num_locked_monitors, CHECK_NULL); + objArrayHandle mh(THREAD, array); + monitors_array = mh; + + typeArrayOop tarray = oopFactory::new_typeArray(T_INT, num_locked_monitors, CHECK_NULL); + typeArrayHandle dh(THREAD, tarray); + depths_array = dh; + + int count = 0; + int j = 0; + for (int depth = 0; depth < num_frames; depth++) { + StackFrameInfo* frame = stacktrace->stack_frame_at(depth); + int len = frame->num_locked_monitors(); + GrowableArray* locked_monitors = frame->locked_monitors(); + for (j = 0; j < len; j++) { + oop monitor = locked_monitors->at(j); + assert(monitor != NULL && monitor->is_instance(), "must be a Java object"); + monitors_array->obj_at_put(count, monitor); + depths_array->int_at_put(count, depth); + count++; + } + } + + GrowableArray* jni_locked_monitors = stacktrace->jni_locked_monitors(); + for (j = 0; j < jni_locked_monitors->length(); j++) { + oop object = jni_locked_monitors->at(j); + assert(object != NULL && object->is_instance(), "must be a Java object"); + monitors_array->obj_at_put(count, object); + // Monitor locked via JNI MonitorEnter call doesn't have stack depth info + depths_array->int_at_put(count, -1); + count++; + } + assert(count == num_locked_monitors, "number of locked monitors doesn't match"); + } + + if (locked_synchronizers) { + // Create Object[] filled with locked JSR-166 synchronizers + assert(ts->threadObj() != NULL, "Must be a valid JavaThread"); + ThreadConcurrentLocks* tcl = ts->get_concurrent_locks(); + GrowableArray* locks = (tcl != NULL ? tcl->owned_locks() : NULL); + int num_locked_synchronizers = (locks != NULL ? locks->length() : 0); + + objArrayOop array = oopFactory::new_system_objArray(num_locked_synchronizers, CHECK_NULL); + objArrayHandle sh(THREAD, array); + synchronizers_array = sh; + + for (int k = 0; k < num_locked_synchronizers; k++) { + synchronizers_array->obj_at_put(k, locks->at(k)); + } + } + + // Create java.lang.management.ThreadInfo object + instanceOop info_obj = Management::create_thread_info_instance(ts, + monitors_array, + depths_array, + synchronizers_array, + CHECK_NULL); + result_h->obj_at_put(index, info_obj); + } + + return (jobjectArray) JNIHandles::make_local(env, result_h()); +JVM_END + +// Returns an array of Class objects. +JVM_ENTRY(jobjectArray, jmm_GetLoadedClasses(JNIEnv *env)) + ResourceMark rm(THREAD); + + LoadedClassesEnumerator lce(THREAD); // Pass current Thread as parameter + + int num_classes = lce.num_loaded_classes(); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::class_klass(), num_classes, CHECK_0); + objArrayHandle classes_ah(THREAD, r); + + for (int i = 0; i < num_classes; i++) { + KlassHandle kh = lce.get_klass(i); + oop mirror = Klass::cast(kh())->java_mirror(); + classes_ah->obj_at_put(i, mirror); + } + + return (jobjectArray) JNIHandles::make_local(env, classes_ah()); +JVM_END + +// Reset statistic. Return true if the requested statistic is reset. +// Otherwise, return false. +// +// Input parameters: +// obj - specify which instance the statistic associated with to be reset +// For PEAK_POOL_USAGE stat, obj is required to be a memory pool object. +// For THREAD_CONTENTION_COUNT and TIME stat, obj is required to be a thread ID. +// type - the type of statistic to be reset +// +JVM_ENTRY(jboolean, jmm_ResetStatistic(JNIEnv *env, jvalue obj, jmmStatisticType type)) + ResourceMark rm(THREAD); + + switch (type) { + case JMM_STAT_PEAK_THREAD_COUNT: + ThreadService::reset_peak_thread_count(); + return true; + + case JMM_STAT_THREAD_CONTENTION_COUNT: + case JMM_STAT_THREAD_CONTENTION_TIME: { + jlong tid = obj.j; + if (tid < 0) { + THROW_(vmSymbols::java_lang_IllegalArgumentException(), JNI_FALSE); + } + + // Look for the JavaThread of this given tid + MutexLockerEx ml(Threads_lock); + if (tid == 0) { + // reset contention statistics for all threads if tid == 0 + for (JavaThread* java_thread = Threads::first(); java_thread != NULL; java_thread = java_thread->next()) { + if (type == JMM_STAT_THREAD_CONTENTION_COUNT) { + ThreadService::reset_contention_count_stat(java_thread); + } else { + ThreadService::reset_contention_time_stat(java_thread); + } + } + } else { + // reset contention statistics for a given thread + JavaThread* java_thread = find_java_thread_from_id(tid); + if (java_thread == NULL) { + return false; + } + + if (type == JMM_STAT_THREAD_CONTENTION_COUNT) { + ThreadService::reset_contention_count_stat(java_thread); + } else { + ThreadService::reset_contention_time_stat(java_thread); + } + } + return true; + break; + } + case JMM_STAT_PEAK_POOL_USAGE: { + jobject o = obj.l; + if (o == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), JNI_FALSE); + } + + oop pool_obj = JNIHandles::resolve(o); + assert(pool_obj->is_instance(), "Should be an instanceOop"); + instanceHandle ph(THREAD, (instanceOop) pool_obj); + + MemoryPool* pool = MemoryService::get_memory_pool(ph); + if (pool != NULL) { + pool->reset_peak_memory_usage(); + return true; + } + break; + } + case JMM_STAT_GC_STAT: { + jobject o = obj.l; + if (o == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), JNI_FALSE); + } + + GCMemoryManager* mgr = get_gc_memory_manager_from_jobject(o, CHECK_0); + if (mgr != NULL) { + mgr->reset_gc_stat(); + return true; + } + break; + } + default: + assert(0, "Unknown Statistic Type"); + } + return false; +JVM_END + +// Returns the fast estimate of CPU time consumed by +// a given thread (in nanoseconds). +// If thread_id == 0, return CPU time for the current thread. +JVM_ENTRY(jlong, jmm_GetThreadCpuTime(JNIEnv *env, jlong thread_id)) + if (!os::is_thread_cpu_time_supported()) { + return -1; + } + + if (thread_id < 0) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "Invalid thread ID", -1); + } + + JavaThread* java_thread = NULL; + if (thread_id == 0) { + // current thread + return os::current_thread_cpu_time(); + } else { + MutexLockerEx ml(Threads_lock); + java_thread = find_java_thread_from_id(thread_id); + if (java_thread != NULL) { + return os::thread_cpu_time((Thread*) java_thread); + } + } + return -1; +JVM_END + +// Returns the CPU time consumed by a given thread (in nanoseconds). +// If thread_id == 0, CPU time for the current thread is returned. +// If user_sys_cpu_time = true, user level and system CPU time of +// a given thread is returned; otherwise, only user level CPU time +// is returned. +JVM_ENTRY(jlong, jmm_GetThreadCpuTimeWithKind(JNIEnv *env, jlong thread_id, jboolean user_sys_cpu_time)) + if (!os::is_thread_cpu_time_supported()) { + return -1; + } + + if (thread_id < 0) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "Invalid thread ID", -1); + } + + JavaThread* java_thread = NULL; + if (thread_id == 0) { + // current thread + return os::current_thread_cpu_time(user_sys_cpu_time != 0); + } else { + MutexLockerEx ml(Threads_lock); + java_thread = find_java_thread_from_id(thread_id); + if (java_thread != NULL) { + return os::thread_cpu_time((Thread*) java_thread, user_sys_cpu_time != 0); + } + } + return -1; +JVM_END + +// Returns a String array of all VM global flag names +JVM_ENTRY(jobjectArray, jmm_GetVMGlobalNames(JNIEnv *env)) + // last flag entry is always NULL, so subtract 1 + int nFlags = (int) Flag::numFlags - 1; + // allocate a temp array + objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + nFlags, CHECK_0); + objArrayHandle flags_ah(THREAD, r); + int num_entries = 0; + for (int i = 0; i < nFlags; i++) { + Flag* flag = &Flag::flags[i]; + // Exclude the diagnostic flags + if (flag->is_unlocked() || flag->is_unlocker()) { + Handle s = java_lang_String::create_from_str(flag->name, CHECK_0); + flags_ah->obj_at_put(num_entries, s()); + num_entries++; + } + } + + if (num_entries < nFlags) { + // Return array of right length + objArrayOop res = oopFactory::new_objArray(SystemDictionary::string_klass(), num_entries, CHECK_0); + for(int i = 0; i < num_entries; i++) { + res->obj_at_put(i, flags_ah->obj_at(i)); + } + return (jobjectArray)JNIHandles::make_local(env, res); + } + + return (jobjectArray)JNIHandles::make_local(env, flags_ah()); +JVM_END + +// utility function used by jmm_GetVMGlobals +void add_global_entry(JNIEnv* env, Handle name, jmmVMGlobal *global, Flag *flag, TRAPS) { + Handle flag_name; + if (name() == NULL) { + flag_name = java_lang_String::create_from_str(flag->name, CHECK); + } else { + flag_name = name; + } + global->name = (jstring)JNIHandles::make_local(env, flag_name()); + global->type = JMM_VMGLOBAL_TYPE_UNKNOWN; + + if (flag->is_bool()) { + global->value.z = flag->get_bool() ? JNI_TRUE : JNI_FALSE; + global->type = JMM_VMGLOBAL_TYPE_JBOOLEAN; + } else if (flag->is_intx()) { + global->value.j = (jlong)flag->get_intx(); + global->type = JMM_VMGLOBAL_TYPE_JLONG; + } else if (flag->is_uintx()) { + global->value.j = (jlong)flag->get_uintx(); + global->type = JMM_VMGLOBAL_TYPE_JLONG; + } else if (flag->is_ccstr()) { + Handle str = java_lang_String::create_from_str(flag->get_ccstr(), CHECK); + global->value.l = (jobject)JNIHandles::make_local(env, str()); + global->type = JMM_VMGLOBAL_TYPE_JSTRING; + } + + global->writeable = flag->is_writeable(); + global->external = flag->is_external(); + switch (flag->origin) { + case DEFAULT: + global->origin = JMM_VMGLOBAL_ORIGIN_DEFAULT; + break; + case COMMAND_LINE: + global->origin = JMM_VMGLOBAL_ORIGIN_COMMAND_LINE; + break; + case ENVIRON_VAR: + global->origin = JMM_VMGLOBAL_ORIGIN_ENVIRON_VAR; + break; + case CONFIG_FILE: + global->origin = JMM_VMGLOBAL_ORIGIN_CONFIG_FILE; + break; + case MANAGEMENT: + global->origin = JMM_VMGLOBAL_ORIGIN_MANAGEMENT; + break; + case ERGONOMIC: + global->origin = JMM_VMGLOBAL_ORIGIN_ERGONOMIC; + break; + default: + global->origin = JMM_VMGLOBAL_ORIGIN_OTHER; + } +} + +// Fill globals array of count length with jmmVMGlobal entries +// specified by names. If names == NULL, fill globals array +// with all Flags. Return value is number of entries +// created in globals. +// If a Flag with a given name in an array element does not +// exist, globals[i].name will be set to NULL. +JVM_ENTRY(jint, jmm_GetVMGlobals(JNIEnv *env, + jobjectArray names, + jmmVMGlobal *globals, + jint count)) + + + if (globals == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), 0); + } + + ResourceMark rm(THREAD); + + if (names != NULL) { + // return the requested globals + objArrayOop ta = objArrayOop(JNIHandles::resolve_non_null(names)); + objArrayHandle names_ah(THREAD, ta); + // Make sure we have a String array + klassOop element_klass = objArrayKlass::cast(names_ah->klass())->element_klass(); + if (element_klass != SystemDictionary::string_klass()) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "Array element type is not String class", 0); + } + + int names_length = names_ah->length(); + int num_entries = 0; + for (int i = 0; i < names_length && i < count; i++) { + oop s = names_ah->obj_at(i); + if (s == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), 0); + } + + Handle sh(THREAD, s); + char* str = java_lang_String::as_utf8_string(s); + Flag* flag = Flag::find_flag(str, strlen(str)); + if (flag != NULL) { + add_global_entry(env, sh, &globals[i], flag, THREAD); + num_entries++; + } else { + globals[i].name = NULL; + } + } + return num_entries; + } else { + // return all globals if names == NULL + + // last flag entry is always NULL, so subtract 1 + int nFlags = (int) Flag::numFlags - 1; + Handle null_h; + int num_entries = 0; + for (int i = 0; i < nFlags && num_entries < count; i++) { + Flag* flag = &Flag::flags[i]; + // Exclude the diagnostic flags + if (flag->is_unlocked() || flag->is_unlocker()) { + add_global_entry(env, null_h, &globals[num_entries], flag, THREAD); + num_entries++; + } + } + return num_entries; + } +JVM_END + +JVM_ENTRY(void, jmm_SetVMGlobal(JNIEnv *env, jstring flag_name, jvalue new_value)) + ResourceMark rm(THREAD); + + oop fn = JNIHandles::resolve_external_guard(flag_name); + if (fn == NULL) { + THROW_MSG(vmSymbols::java_lang_NullPointerException(), + "The flag name cannot be null."); + } + char* name = java_lang_String::as_utf8_string(fn); + Flag* flag = Flag::find_flag(name, strlen(name)); + if (flag == NULL) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Flag does not exist."); + } + if (!flag->is_writeable()) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "This flag is not writeable."); + } + + bool succeed; + if (flag->is_bool()) { + bool bvalue = (new_value.z == JNI_TRUE ? true : false); + succeed = CommandLineFlags::boolAtPut(name, &bvalue, MANAGEMENT); + } else if (flag->is_intx()) { + intx ivalue = new_value.j; + succeed = CommandLineFlags::intxAtPut(name, &ivalue, MANAGEMENT); + } else if (flag->is_uintx()) { + uintx uvalue = new_value.j; + succeed = CommandLineFlags::uintxAtPut(name, &uvalue, MANAGEMENT); + } else if (flag->is_ccstr()) { + oop str = JNIHandles::resolve_external_guard(new_value.l); + if (str == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + ccstr svalue = java_lang_String::as_utf8_string(str); + succeed = CommandLineFlags::ccstrAtPut(name, &svalue, MANAGEMENT); + } + assert(succeed, "Setting flag should succeed"); +JVM_END + +class ThreadTimesClosure: public ThreadClosure { + private: + objArrayOop _names; + typeArrayOop _times; + int _names_len; + int _times_len; + int _count; + + public: + ThreadTimesClosure(objArrayOop names, typeArrayOop times); + virtual void do_thread(Thread* thread); + int count() { return _count; } +}; + +ThreadTimesClosure::ThreadTimesClosure(objArrayOop names, + typeArrayOop times) { + assert(names != NULL, "names was NULL"); + assert(times != NULL, "times was NULL"); + _names = names; + _names_len = names->length(); + _times = times; + _times_len = times->length(); + _count = 0; +} + +void ThreadTimesClosure::do_thread(Thread* thread) { + Handle s; + assert(thread != NULL, "thread was NULL"); + + // exclude externally visible JavaThreads + if (thread->is_Java_thread() && !thread->is_hidden_from_external_view()) { + return; + } + + if (_count >= _names_len || _count >= _times_len) { + // skip if the result array is not big enough + return; + } + + EXCEPTION_MARK; + + assert(thread->name() != NULL, "All threads should have a name"); + s = java_lang_String::create_from_str(thread->name(), CHECK); + _names->obj_at_put(_count, s()); + + _times->long_at_put(_count, os::is_thread_cpu_time_supported() ? + os::thread_cpu_time(thread) : -1); + _count++; +} + +// Fills names with VM internal thread names and times with the corresponding +// CPU times. If names or times is NULL, a NullPointerException is thrown. +// If the element type of names is not String, an IllegalArgumentException is +// thrown. +// If an array is not large enough to hold all the entries, only the entries +// that fit will be returned. Return value is the number of VM internal +// threads entries. +JVM_ENTRY(jint, jmm_GetInternalThreadTimes(JNIEnv *env, + jobjectArray names, + jlongArray times)) + if (names == NULL || times == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), 0); + } + objArrayOop na = objArrayOop(JNIHandles::resolve_non_null(names)); + objArrayHandle names_ah(THREAD, na); + + // Make sure we have a String array + klassOop element_klass = objArrayKlass::cast(names_ah->klass())->element_klass(); + if (element_klass != SystemDictionary::string_klass()) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "Array element type is not String class", 0); + } + + typeArrayOop ta = typeArrayOop(JNIHandles::resolve_non_null(times)); + typeArrayHandle times_ah(THREAD, ta); + + ThreadTimesClosure ttc(names_ah(), times_ah()); + { + MutexLockerEx ml(Threads_lock); + Threads::threads_do(&ttc); + } + + return ttc.count(); +JVM_END + +static Handle find_deadlocks(bool object_monitors_only, TRAPS) { + ResourceMark rm(THREAD); + + VM_FindDeadlocks op(!object_monitors_only /* also check concurrent locks? */); + VMThread::execute(&op); + + DeadlockCycle* deadlocks = op.result(); + if (deadlocks == NULL) { + // no deadlock found and return + return Handle(); + } + + int num_threads = 0; + DeadlockCycle* cycle; + for (cycle = deadlocks; cycle != NULL; cycle = cycle->next()) { + num_threads += cycle->num_threads(); + } + + objArrayOop r = oopFactory::new_objArray(SystemDictionary::thread_klass(), num_threads, CHECK_NH); + objArrayHandle threads_ah(THREAD, r); + + int index = 0; + for (cycle = deadlocks; cycle != NULL; cycle = cycle->next()) { + GrowableArray* deadlock_threads = cycle->threads(); + int len = deadlock_threads->length(); + for (int i = 0; i < len; i++) { + threads_ah->obj_at_put(index, deadlock_threads->at(i)->threadObj()); + index++; + } + } + return threads_ah; +} + +// Finds cycles of threads that are deadlocked involved in object monitors +// and JSR-166 synchronizers. +// Returns an array of Thread objects which are in deadlock, if any. +// Otherwise, returns NULL. +// +// Input parameter: +// object_monitors_only - if true, only check object monitors +// +JVM_ENTRY(jobjectArray, jmm_FindDeadlockedThreads(JNIEnv *env, jboolean object_monitors_only)) + Handle result = find_deadlocks(object_monitors_only != 0, CHECK_0); + return (jobjectArray) JNIHandles::make_local(env, result()); +JVM_END + +// Finds cycles of threads that are deadlocked on monitor locks +// Returns an array of Thread objects which are in deadlock, if any. +// Otherwise, returns NULL. +JVM_ENTRY(jobjectArray, jmm_FindMonitorDeadlockedThreads(JNIEnv *env)) + Handle result = find_deadlocks(true, CHECK_0); + return (jobjectArray) JNIHandles::make_local(env, result()); +JVM_END + +// Gets the information about GC extension attributes including +// the name of the attribute, its type, and a short description. +// +// Input parameters: +// mgr - GC memory manager +// info - caller allocated array of jmmExtAttributeInfo +// count - number of elements of the info array +// +// Returns the number of GC extension attributes filled in the info array; or +// -1 if info is not big enough +// +JVM_ENTRY(jint, jmm_GetGCExtAttributeInfo(JNIEnv *env, jobject mgr, jmmExtAttributeInfo* info, jint count)) + // All GC memory managers have 1 attribute (number of GC threads) + if (count == 0) { + return 0; + } + + if (info == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), 0); + } + + info[0].name = "GcThreadCount"; + info[0].type = 'I'; + info[0].description = "Number of GC threads"; + return 1; +JVM_END + +// verify the given array is an array of java/lang/management/MemoryUsage objects +// of a given length and return the objArrayOop +static objArrayOop get_memory_usage_objArray(jobjectArray array, int length, TRAPS) { + if (array == NULL) { + THROW_(vmSymbols::java_lang_NullPointerException(), 0); + } + + objArrayOop oa = objArrayOop(JNIHandles::resolve_non_null(array)); + objArrayHandle array_h(THREAD, oa); + + // array must be of the given length + if (length != array_h->length()) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "The length of the given MemoryUsage array does not match the number of memory pools.", 0); + } + + // check if the element of array is of type MemoryUsage class + klassOop usage_klass = Management::java_lang_management_MemoryUsage_klass(CHECK_0); + klassOop element_klass = objArrayKlass::cast(array_h->klass())->element_klass(); + if (element_klass != usage_klass) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "The element type is not MemoryUsage class", 0); + } + + return array_h(); +} + +// Gets the statistics of the last GC of a given GC memory manager. +// Input parameters: +// obj - GarbageCollectorMXBean object +// gc_stat - caller allocated jmmGCStat where: +// a. before_gc_usage - array of MemoryUsage objects +// b. after_gc_usage - array of MemoryUsage objects +// c. gc_ext_attributes_values_size is set to the +// gc_ext_attribute_values array allocated +// d. gc_ext_attribute_values is a caller allocated array of jvalue. +// +// On return, +// gc_index == 0 indicates no GC statistics available +// +// before_gc_usage and after_gc_usage - filled with per memory pool +// before and after GC usage in the same order as the memory pools +// returned by GetMemoryPools for a given GC memory manager. +// num_gc_ext_attributes indicates the number of elements in +// the gc_ext_attribute_values array is filled; or +// -1 if the gc_ext_attributes_values array is not big enough +// +JVM_ENTRY(void, jmm_GetLastGCStat(JNIEnv *env, jobject obj, jmmGCStat *gc_stat)) + ResourceMark rm(THREAD); + + if (gc_stat->gc_ext_attribute_values_size > 0 && gc_stat->gc_ext_attribute_values == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + + // Get the GCMemoryManager + GCMemoryManager* mgr = get_gc_memory_manager_from_jobject(obj, CHECK); + if (mgr->last_gc_stat() == NULL) { + gc_stat->gc_index = 0; + return; + } + + // Make a copy of the last GC statistics + // GC may occur while constructing the last GC information + int num_pools = MemoryService::num_memory_pools(); + GCStatInfo* stat = new GCStatInfo(num_pools); + stat->copy_stat(mgr->last_gc_stat()); + + gc_stat->gc_index = stat->gc_index(); + gc_stat->start_time = Management::ticks_to_ms(stat->start_time()); + gc_stat->end_time = Management::ticks_to_ms(stat->end_time()); + + // Current implementation does not have GC extension attributes + gc_stat->num_gc_ext_attributes = 0; + + // Fill the arrays of MemoryUsage objects with before and after GC + // per pool memory usage + objArrayOop bu = get_memory_usage_objArray(gc_stat->usage_before_gc, + num_pools, + CHECK); + objArrayHandle usage_before_gc_ah(THREAD, bu); + + objArrayOop au = get_memory_usage_objArray(gc_stat->usage_after_gc, + num_pools, + CHECK); + objArrayHandle usage_after_gc_ah(THREAD, au); + + for (int i = 0; i < num_pools; i++) { + Handle before_usage = MemoryService::create_MemoryUsage_obj(stat->before_gc_usage_for_pool(i), CHECK); + Handle after_usage; + + MemoryUsage u = stat->after_gc_usage_for_pool(i); + if (u.max_size() == 0 && u.used() > 0) { + // If max size == 0, this pool is a survivor space. + // Set max size = -1 since the pools will be swapped after GC. + MemoryUsage usage(u.init_size(), u.used(), u.committed(), (size_t)-1); + after_usage = MemoryService::create_MemoryUsage_obj(usage, CHECK); + } else { + after_usage = MemoryService::create_MemoryUsage_obj(stat->after_gc_usage_for_pool(i), CHECK); + } + usage_before_gc_ah->obj_at_put(i, before_usage()); + usage_after_gc_ah->obj_at_put(i, after_usage()); + } + + if (gc_stat->gc_ext_attribute_values_size > 0) { + // Current implementation only has 1 attribute (number of GC threads) + // The type is 'I' + gc_stat->gc_ext_attribute_values[0].i = mgr->num_gc_threads(); + } +JVM_END + +// Dump heap - Returns 0 if succeeds. +JVM_ENTRY(jint, jmm_DumpHeap0(JNIEnv *env, jstring outputfile, jboolean live)) +#ifndef SERVICES_KERNEL + ResourceMark rm(THREAD); + oop on = JNIHandles::resolve_external_guard(outputfile); + if (on == NULL) { + THROW_MSG_(vmSymbols::java_lang_NullPointerException(), + "Output file name cannot be null.", -1); + } + char* name = java_lang_String::as_utf8_string(on); + if (name == NULL) { + THROW_MSG_(vmSymbols::java_lang_NullPointerException(), + "Output file name cannot be null.", -1); + } + HeapDumper dumper(live ? true : false); + if (dumper.dump(name) != 0) { + const char* errmsg = dumper.error_as_C_string(); + THROW_MSG_(vmSymbols::java_io_IOException(), errmsg, -1); + } + return 0; +#else // SERVICES_KERNEL + return -1; +#endif // SERVICES_KERNEL +JVM_END + +jlong Management::ticks_to_ms(jlong ticks) { + assert(os::elapsed_frequency() > 0, "Must be non-zero"); + return (jlong)(((double)ticks / (double)os::elapsed_frequency()) + * (double)1000.0); +} + +const struct jmmInterface_1_ jmm_interface = { + NULL, + NULL, + jmm_GetVersion, + jmm_GetOptionalSupport, + jmm_GetInputArguments, + jmm_GetThreadInfo, + jmm_GetInputArgumentArray, + jmm_GetMemoryPools, + jmm_GetMemoryManagers, + jmm_GetMemoryPoolUsage, + jmm_GetPeakMemoryPoolUsage, + NULL, + jmm_GetMemoryUsage, + jmm_GetLongAttribute, + jmm_GetBoolAttribute, + jmm_SetBoolAttribute, + jmm_GetLongAttributes, + jmm_FindMonitorDeadlockedThreads, + jmm_GetThreadCpuTime, + jmm_GetVMGlobalNames, + jmm_GetVMGlobals, + jmm_GetInternalThreadTimes, + jmm_ResetStatistic, + jmm_SetPoolSensor, + jmm_SetPoolThreshold, + jmm_GetPoolCollectionUsage, + jmm_GetGCExtAttributeInfo, + jmm_GetLastGCStat, + jmm_GetThreadCpuTimeWithKind, + NULL, + jmm_DumpHeap0, + jmm_FindDeadlockedThreads, + jmm_SetVMGlobal, + NULL, + jmm_DumpThreads +}; + +void* Management::get_jmm_interface(int version) { + if (version == JMM_VERSION_1_0) { + return (void*) &jmm_interface; + } + return NULL; +} diff --git a/hotspot/src/share/vm/services/management.hpp b/hotspot/src/share/vm/services/management.hpp new file mode 100644 index 00000000000..c37a25fab81 --- /dev/null +++ b/hotspot/src/share/vm/services/management.hpp @@ -0,0 +1,106 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class OopClosure; +class ThreadSnapshot; + +class Management : public AllStatic { +private: + static PerfVariable* _begin_vm_creation_time; + static PerfVariable* _end_vm_creation_time; + static PerfVariable* _vm_init_done_time; + static jmmOptionalSupport _optional_support; + static TimeStamp _stamp; // Timestamp since vm init done time + + // Management klasses + static klassOop _sensor_klass; + static klassOop _threadInfo_klass; + static klassOop _memoryUsage_klass; + static klassOop _memoryPoolMXBean_klass; + static klassOop _memoryManagerMXBean_klass; + static klassOop _garbageCollectorMXBean_klass; + static klassOop _managementFactory_klass; + + static klassOop load_and_initialize_klass(symbolHandle sh, TRAPS); + +public: + static void init(); + static void initialize(TRAPS); + + static jlong ticks_to_ms(jlong ticks); + static jlong timestamp(); + + static void oops_do(OopClosure* f); + static void* get_jmm_interface(int version); + static void get_optional_support(jmmOptionalSupport* support); + + static void get_loaded_classes(JavaThread* cur_thread, GrowableArray* klass_handle_array); + + static void record_vm_startup_time(jlong begin, jlong duration); + static void record_vm_init_completed() { + // Initialize the timestamp to get the current time + _vm_init_done_time->set_value(os::javaTimeMillis()); + + // Update the timestamp to the vm init done time + _stamp.update(); + } + + static jlong vm_init_done_time() { + return _vm_init_done_time->get_value(); + } + + // methods to return a klassOop. + static klassOop java_lang_management_ThreadInfo_klass(TRAPS); + static klassOop java_lang_management_MemoryUsage_klass(TRAPS); + static klassOop java_lang_management_MemoryPoolMXBean_klass(TRAPS); + static klassOop java_lang_management_MemoryManagerMXBean_klass(TRAPS); + static klassOop java_lang_management_GarbageCollectorMXBean_klass(TRAPS); + static klassOop sun_management_Sensor_klass(TRAPS); + static klassOop sun_management_ManagementFactory_klass(TRAPS); + + static instanceOop create_thread_info_instance(ThreadSnapshot* snapshot, TRAPS); + static instanceOop create_thread_info_instance(ThreadSnapshot* snapshot, objArrayHandle monitors_array, typeArrayHandle depths_array, objArrayHandle synchronizers_array, TRAPS); +}; + +class TraceVmCreationTime : public StackObj { +private: + TimeStamp _timer; + jlong _begin_time; + +public: + TraceVmCreationTime() {} + ~TraceVmCreationTime() {} + + void start() + { _timer.update_to(0); _begin_time = os::javaTimeMillis(); } + + /** + * Only call this if initialization completes successfully; it will + * crash if PerfMemory_exit() has already been called (usually by + * os::shutdown() when there was an initialization failure). + */ + void end() + { Management::record_vm_startup_time(_begin_time, _timer.milliseconds()); } + +}; diff --git a/hotspot/src/share/vm/services/memoryManager.cpp b/hotspot/src/share/vm/services/memoryManager.cpp new file mode 100644 index 00000000000..7b7905c5ab7 --- /dev/null +++ b/hotspot/src/share/vm/services/memoryManager.cpp @@ -0,0 +1,245 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_memoryManager.cpp.incl" + +HS_DTRACE_PROBE_DECL8(hotspot, mem__pool__gc__begin, char*, int, char*, int, + size_t, size_t, size_t, size_t); +HS_DTRACE_PROBE_DECL8(hotspot, mem__pool__gc__end, char*, int, char*, int, + size_t, size_t, size_t, size_t); + +MemoryManager::MemoryManager() { + _num_pools = 0; + _memory_mgr_obj = NULL; +} + +void MemoryManager::add_pool(MemoryPool* pool) { + assert(_num_pools < MemoryManager::max_num_pools, "_num_pools exceeds the max"); + if (_num_pools < MemoryManager::max_num_pools) { + _pools[_num_pools] = pool; + _num_pools++; + } + pool->add_manager(this); +} + +MemoryManager* MemoryManager::get_code_cache_memory_manager() { + return (MemoryManager*) new CodeCacheMemoryManager(); +} + +GCMemoryManager* MemoryManager::get_copy_memory_manager() { + return (GCMemoryManager*) new CopyMemoryManager(); +} + +GCMemoryManager* MemoryManager::get_msc_memory_manager() { + return (GCMemoryManager*) new MSCMemoryManager(); +} + +GCMemoryManager* MemoryManager::get_parnew_memory_manager() { + return (GCMemoryManager*) new ParNewMemoryManager(); +} + +GCMemoryManager* MemoryManager::get_cms_memory_manager() { + return (GCMemoryManager*) new CMSMemoryManager(); +} + +GCMemoryManager* MemoryManager::get_psScavenge_memory_manager() { + return (GCMemoryManager*) new PSScavengeMemoryManager(); +} + +GCMemoryManager* MemoryManager::get_psMarkSweep_memory_manager() { + return (GCMemoryManager*) new PSMarkSweepMemoryManager(); +} + +instanceOop MemoryManager::get_memory_manager_instance(TRAPS) { + // Must do an acquire so as to force ordering of subsequent + // loads from anything _memory_mgr_obj points to or implies. + instanceOop mgr_obj = (instanceOop)OrderAccess::load_ptr_acquire(&_memory_mgr_obj); + if (mgr_obj == NULL) { + // It's ok for more than one thread to execute the code up to the locked region. + // Extra manager instances will just be gc'ed. + klassOop k = Management::sun_management_ManagementFactory_klass(CHECK_0); + instanceKlassHandle ik(THREAD, k); + + Handle mgr_name = java_lang_String::create_from_str(name(), CHECK_0); + + JavaValue result(T_OBJECT); + JavaCallArguments args; + args.push_oop(mgr_name); // Argument 1 + + symbolHandle method_name; + symbolHandle signature; + if (is_gc_memory_manager()) { + method_name = vmSymbolHandles::createGarbageCollector_name(); + signature = vmSymbolHandles::createGarbageCollector_signature(); + args.push_oop(Handle()); // Argument 2 (for future extension) + } else { + method_name = vmSymbolHandles::createMemoryManager_name(); + signature = vmSymbolHandles::createMemoryManager_signature(); + } + + JavaCalls::call_static(&result, + ik, + method_name, + signature, + &args, + CHECK_0); + + instanceOop m = (instanceOop) result.get_jobject(); + instanceHandle mgr(THREAD, m); + + { + // Get lock before setting _memory_mgr_obj + // since another thread may have created the instance + MutexLocker ml(Management_lock); + + // Check if another thread has created the management object. We reload + // _memory_mgr_obj here because some other thread may have initialized + // it while we were executing the code before the lock. + // + // The lock has done an acquire, so the load can't float above it, but + // we need to do a load_acquire as above. + mgr_obj = (instanceOop)OrderAccess::load_ptr_acquire(&_memory_mgr_obj); + if (mgr_obj != NULL) { + return mgr_obj; + } + + // Get the address of the object we created via call_special. + mgr_obj = mgr(); + + // Use store barrier to make sure the memory accesses associated + // with creating the management object are visible before publishing + // its address. The unlock will publish the store to _memory_mgr_obj + // because it does a release first. + OrderAccess::release_store_ptr(&_memory_mgr_obj, mgr_obj); + } + } + + return mgr_obj; +} + +void MemoryManager::oops_do(OopClosure* f) { + f->do_oop((oop*) &_memory_mgr_obj); +} + +GCStatInfo::GCStatInfo(int num_pools) { + // initialize the arrays for memory usage + _before_gc_usage_array = (MemoryUsage*) NEW_C_HEAP_ARRAY(MemoryUsage, num_pools); + _after_gc_usage_array = (MemoryUsage*) NEW_C_HEAP_ARRAY(MemoryUsage, num_pools); + size_t len = num_pools * sizeof(MemoryUsage); + memset(_before_gc_usage_array, 0, len); + memset(_after_gc_usage_array, 0, len); + _usage_array_size = num_pools; +} + +GCStatInfo::~GCStatInfo() { + FREE_C_HEAP_ARRAY(MemoryUsage*, _before_gc_usage_array); + FREE_C_HEAP_ARRAY(MemoryUsage*, _after_gc_usage_array); +} + +void GCStatInfo::copy_stat(GCStatInfo* stat) { + set_index(stat->gc_index()); + set_start_time(stat->start_time()); + set_end_time(stat->end_time()); + assert(_usage_array_size == stat->usage_array_size(), "Must have same array size"); + for (int i = 0; i < _usage_array_size; i++) { + set_before_gc_usage(i, stat->before_gc_usage_for_pool(i)); + set_after_gc_usage(i, stat->after_gc_usage_for_pool(i)); + } +} + +void GCStatInfo::set_gc_usage(int pool_index, MemoryUsage usage, bool before_gc) { + MemoryUsage* gc_usage_array; + if (before_gc) { + gc_usage_array = _before_gc_usage_array; + } else { + gc_usage_array = _after_gc_usage_array; + } + gc_usage_array[pool_index] = usage; +} + +GCMemoryManager::GCMemoryManager() : MemoryManager() { + _num_collections = 0; + _last_gc_stat = NULL; + _num_gc_threads = 1; +} + +GCMemoryManager::~GCMemoryManager() { + delete _last_gc_stat; +} + +void GCMemoryManager::initialize_gc_stat_info() { + assert(MemoryService::num_memory_pools() > 0, "should have one or more memory pools"); + _last_gc_stat = new GCStatInfo(MemoryService::num_memory_pools()); +} + +void GCMemoryManager::gc_begin() { + assert(_last_gc_stat != NULL, "Just checking"); + _accumulated_timer.start(); + _num_collections++; + _last_gc_stat->set_index(_num_collections); + _last_gc_stat->set_start_time(Management::timestamp()); + + // Keep memory usage of all memory pools + for (int i = 0; i < MemoryService::num_memory_pools(); i++) { + MemoryPool* pool = MemoryService::get_memory_pool(i); + MemoryUsage usage = pool->get_memory_usage(); + _last_gc_stat->set_before_gc_usage(i, usage); + HS_DTRACE_PROBE8(hotspot, mem__pool__gc__begin, + name(), strlen(name()), + pool->name(), strlen(pool->name()), + usage.init_size(), usage.used(), + usage.committed(), usage.max_size()); + } +} + +void GCMemoryManager::gc_end() { + _accumulated_timer.stop(); + _last_gc_stat->set_end_time(Management::timestamp()); + + int i; + // keep the last gc statistics for all memory pools + for (i = 0; i < MemoryService::num_memory_pools(); i++) { + MemoryPool* pool = MemoryService::get_memory_pool(i); + MemoryUsage usage = pool->get_memory_usage(); + + HS_DTRACE_PROBE8(hotspot, mem__pool__gc__end, + name(), strlen(name()), + pool->name(), strlen(pool->name()), + usage.init_size(), usage.used(), + usage.committed(), usage.max_size()); + + _last_gc_stat->set_after_gc_usage(i, usage); + } + + // Set last collection usage of the memory pools managed by this collector + for (i = 0; i < num_memory_pools(); i++) { + MemoryPool* pool = get_memory_pool(i); + MemoryUsage usage = pool->get_memory_usage(); + + // Compare with GC usage threshold + pool->set_last_collection_usage(usage); + LowMemoryDetector::detect_after_gc_memory(pool); + } +} diff --git a/hotspot/src/share/vm/services/memoryManager.hpp b/hotspot/src/share/vm/services/memoryManager.hpp new file mode 100644 index 00000000000..4efc955eb82 --- /dev/null +++ b/hotspot/src/share/vm/services/memoryManager.hpp @@ -0,0 +1,233 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A memory manager is responsible for managing one or more memory pools. +// The garbage collector is one type of memory managers responsible +// for reclaiming memory occupied by unreachable objects. A Java virtual +// machine may have one or more memory managers. It may +// add or remove memory managers during execution. +// A memory pool can be managed by more than one memory managers. + +class MemoryPool; +class GCMemoryManager; +class OopClosure; + +class MemoryManager : public CHeapObj { +private: + enum { + max_num_pools = 10 + }; + + MemoryPool* _pools[max_num_pools]; + int _num_pools; + +protected: + volatile instanceOop _memory_mgr_obj; + +public: + enum Name { + Abstract, + CodeCache, + Copy, + MarkSweepCompact, + ParNew, + ConcurrentMarkSweep, + PSScavenge, + PSMarkSweep + }; + + MemoryManager(); + + int num_memory_pools() const { return _num_pools; } + MemoryPool* get_memory_pool(int index) { + assert(index >= 0 && index < _num_pools, "Invalid index"); + return _pools[index]; + } + + void add_pool(MemoryPool* pool); + + bool is_manager(instanceHandle mh) { return mh() == _memory_mgr_obj; } + + virtual instanceOop get_memory_manager_instance(TRAPS); + virtual MemoryManager::Name kind() { return MemoryManager::Abstract; } + virtual bool is_gc_memory_manager() { return false; } + virtual const char* name() = 0; + + // GC support + void oops_do(OopClosure* f); + + // Static factory methods to get a memory manager of a specific type + static MemoryManager* get_code_cache_memory_manager(); + static GCMemoryManager* get_copy_memory_manager(); + static GCMemoryManager* get_msc_memory_manager(); + static GCMemoryManager* get_parnew_memory_manager(); + static GCMemoryManager* get_cms_memory_manager(); + static GCMemoryManager* get_psScavenge_memory_manager(); + static GCMemoryManager* get_psMarkSweep_memory_manager(); + +}; + +class CodeCacheMemoryManager : public MemoryManager { +private: +public: + CodeCacheMemoryManager() : MemoryManager() {} + + MemoryManager::Name kind() { return MemoryManager::CodeCache; } + const char* name() { return "CodeCacheManager"; } +}; + +class GCStatInfo : public CHeapObj { +private: + size_t _index; + jlong _start_time; + jlong _end_time; + + // We keep memory usage of all memory pools + MemoryUsage* _before_gc_usage_array; + MemoryUsage* _after_gc_usage_array; + int _usage_array_size; + + void set_gc_usage(int pool_index, MemoryUsage, bool before_gc); + +public: + GCStatInfo(int num_pools); + ~GCStatInfo(); + + size_t gc_index() { return _index; } + jlong start_time() { return _start_time; } + jlong end_time() { return _end_time; } + int usage_array_size() { return _usage_array_size; } + MemoryUsage before_gc_usage_for_pool(int pool_index) { + assert(pool_index >= 0 && pool_index < _usage_array_size, "Range checking"); + return _before_gc_usage_array[pool_index]; + } + MemoryUsage after_gc_usage_for_pool(int pool_index) { + assert(pool_index >= 0 && pool_index < _usage_array_size, "Range checking"); + return _after_gc_usage_array[pool_index]; + } + + void set_index(size_t index) { _index = index; } + void set_start_time(jlong time) { _start_time = time; } + void set_end_time(jlong time) { _end_time = time; } + void set_before_gc_usage(int pool_index, MemoryUsage usage) { + assert(pool_index >= 0 && pool_index < _usage_array_size, "Range checking"); + set_gc_usage(pool_index, usage, true /* before gc */); + } + void set_after_gc_usage(int pool_index, MemoryUsage usage) { + assert(pool_index >= 0 && pool_index < _usage_array_size, "Range checking"); + set_gc_usage(pool_index, usage, false /* after gc */); + } + + void copy_stat(GCStatInfo* stat); +}; + +class GCMemoryManager : public MemoryManager { +private: + // TODO: We should unify the GCCounter and GCMemoryManager statistic + size_t _num_collections; + elapsedTimer _accumulated_timer; + elapsedTimer _gc_timer; // for measuring every GC duration + GCStatInfo* _last_gc_stat; + int _num_gc_threads; +public: + GCMemoryManager(); + ~GCMemoryManager(); + + void initialize_gc_stat_info(); + + bool is_gc_memory_manager() { return true; } + jlong gc_time_ms() { return _accumulated_timer.milliseconds(); } + size_t gc_count() { return _num_collections; } + int num_gc_threads() { return _num_gc_threads; } + void set_num_gc_threads(int count) { _num_gc_threads = count; } + + void gc_begin(); + void gc_end(); + + void reset_gc_stat() { _num_collections = 0; _accumulated_timer.reset(); } + GCStatInfo* last_gc_stat() { return _last_gc_stat; } + + virtual MemoryManager::Name kind() = 0; +}; + +// These subclasses of GCMemoryManager are defined to include +// GC-specific information. +// TODO: Add GC-specific information +class CopyMemoryManager : public GCMemoryManager { +private: +public: + CopyMemoryManager() : GCMemoryManager() {} + + MemoryManager::Name kind() { return MemoryManager::Copy; } + const char* name() { return "Copy"; } +}; + +class MSCMemoryManager : public GCMemoryManager { +private: +public: + MSCMemoryManager() : GCMemoryManager() {} + + MemoryManager::Name kind() { return MemoryManager::MarkSweepCompact; } + const char* name() { return "MarkSweepCompact"; } + +}; + +class ParNewMemoryManager : public GCMemoryManager { +private: +public: + ParNewMemoryManager() : GCMemoryManager() {} + + MemoryManager::Name kind() { return MemoryManager::ParNew; } + const char* name() { return "ParNew"; } + +}; + +class CMSMemoryManager : public GCMemoryManager { +private: +public: + CMSMemoryManager() : GCMemoryManager() {} + + MemoryManager::Name kind() { return MemoryManager::ConcurrentMarkSweep; } + const char* name() { return "ConcurrentMarkSweep";} + +}; + +class PSScavengeMemoryManager : public GCMemoryManager { +private: +public: + PSScavengeMemoryManager() : GCMemoryManager() {} + + MemoryManager::Name kind() { return MemoryManager::PSScavenge; } + const char* name() { return "PS Scavenge"; } + +}; + +class PSMarkSweepMemoryManager : public GCMemoryManager { +private: +public: + PSMarkSweepMemoryManager() : GCMemoryManager() {} + + MemoryManager::Name kind() { return MemoryManager::PSMarkSweep; } + const char* name() { return "PS MarkSweep"; } +}; diff --git a/hotspot/src/share/vm/services/memoryPool.cpp b/hotspot/src/share/vm/services/memoryPool.cpp new file mode 100644 index 00000000000..74557747639 --- /dev/null +++ b/hotspot/src/share/vm/services/memoryPool.cpp @@ -0,0 +1,249 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_memoryPool.cpp.incl" + +MemoryPool::MemoryPool(const char* name, + PoolType type, + size_t init_size, + size_t max_size, + bool support_usage_threshold, + bool support_gc_threshold) { + _name = name; + _initial_size = init_size; + _max_size = max_size; + _memory_pool_obj = NULL; + _available_for_allocation = true; + _num_managers = 0; + _type = type; + + // initialize the max and init size of collection usage + _after_gc_usage = MemoryUsage(_initial_size, 0, 0, _max_size); + + _usage_sensor = NULL; + _gc_usage_sensor = NULL; + // usage threshold supports both high and low threshold + _usage_threshold = new ThresholdSupport(support_usage_threshold, support_usage_threshold); + // gc usage threshold supports only high threshold + _gc_usage_threshold = new ThresholdSupport(support_gc_threshold, support_gc_threshold); +} + +void MemoryPool::add_manager(MemoryManager* mgr) { + assert(_num_managers < MemoryPool::max_num_managers, "_num_managers exceeds the max"); + if (_num_managers < MemoryPool::max_num_managers) { + _managers[_num_managers] = mgr; + _num_managers++; + } +} + + +// Returns an instanceHandle of a MemoryPool object. +// It creates a MemoryPool instance when the first time +// this function is called. +instanceOop MemoryPool::get_memory_pool_instance(TRAPS) { + // Must do an acquire so as to force ordering of subsequent + // loads from anything _memory_pool_obj points to or implies. + instanceOop pool_obj = (instanceOop)OrderAccess::load_ptr_acquire(&_memory_pool_obj); + if (pool_obj == NULL) { + // It's ok for more than one thread to execute the code up to the locked region. + // Extra pool instances will just be gc'ed. + klassOop k = Management::sun_management_ManagementFactory_klass(CHECK_NULL); + instanceKlassHandle ik(THREAD, k); + + Handle pool_name = java_lang_String::create_from_str(_name, CHECK_NULL); + jlong usage_threshold_value = (_usage_threshold->is_high_threshold_supported() ? 0 : -1L); + jlong gc_usage_threshold_value = (_gc_usage_threshold->is_high_threshold_supported() ? 0 : -1L); + + JavaValue result(T_OBJECT); + JavaCallArguments args; + args.push_oop(pool_name); // Argument 1 + args.push_int((int) is_heap()); // Argument 2 + + symbolHandle method_name = vmSymbolHandles::createMemoryPool_name(); + symbolHandle signature = vmSymbolHandles::createMemoryPool_signature(); + + args.push_long(usage_threshold_value); // Argument 3 + args.push_long(gc_usage_threshold_value); // Argument 4 + + JavaCalls::call_static(&result, + ik, + method_name, + signature, + &args, + CHECK_NULL); + + instanceOop p = (instanceOop) result.get_jobject(); + instanceHandle pool(THREAD, p); + + { + // Get lock since another thread may have create the instance + MutexLocker ml(Management_lock); + + // Check if another thread has created the pool. We reload + // _memory_pool_obj here because some other thread may have + // initialized it while we were executing the code before the lock. + // + // The lock has done an acquire, so the load can't float above it, + // but we need to do a load_acquire as above. + pool_obj = (instanceOop)OrderAccess::load_ptr_acquire(&_memory_pool_obj); + if (pool_obj != NULL) { + return pool_obj; + } + + // Get the address of the object we created via call_special. + pool_obj = pool(); + + // Use store barrier to make sure the memory accesses associated + // with creating the pool are visible before publishing its address. + // The unlock will publish the store to _memory_pool_obj because + // it does a release first. + OrderAccess::release_store_ptr(&_memory_pool_obj, pool_obj); + } + } + + return pool_obj; +} + +inline static size_t get_max_value(size_t val1, size_t val2) { + return (val1 > val2 ? val1 : val2); +} + +void MemoryPool::record_peak_memory_usage() { + // Caller in JDK is responsible for synchronization - + // acquire the lock for this memory pool before calling VM + MemoryUsage usage = get_memory_usage(); + size_t peak_used = get_max_value(usage.used(), _peak_usage.used()); + size_t peak_committed = get_max_value(usage.committed(), _peak_usage.committed()); + size_t peak_max_size = get_max_value(usage.max_size(), _peak_usage.max_size()); + + _peak_usage = MemoryUsage(initial_size(), peak_used, peak_committed, peak_max_size); +} + +static void set_sensor_obj_at(SensorInfo** sensor_ptr, instanceHandle sh) { + assert(*sensor_ptr == NULL, "Should be called only once"); + SensorInfo* sensor = new SensorInfo(); + sensor->set_sensor(sh()); + *sensor_ptr = sensor; +} + +void MemoryPool::set_usage_sensor_obj(instanceHandle sh) { + set_sensor_obj_at(&_usage_sensor, sh); +} + +void MemoryPool::set_gc_usage_sensor_obj(instanceHandle sh) { + set_sensor_obj_at(&_gc_usage_sensor, sh); +} + +void MemoryPool::oops_do(OopClosure* f) { + f->do_oop((oop*) &_memory_pool_obj); + if (_usage_sensor != NULL) { + _usage_sensor->oops_do(f); + } + if (_gc_usage_sensor != NULL) { + _gc_usage_sensor->oops_do(f); + } +} + +ContiguousSpacePool::ContiguousSpacePool(ContiguousSpace* space, + const char* name, + PoolType type, + size_t max_size, + bool support_usage_threshold) : + CollectedMemoryPool(name, type, space->capacity(), max_size, + support_usage_threshold), _space(space) { +} + +MemoryUsage ContiguousSpacePool::get_memory_usage() { + size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t used = used_in_bytes(); + size_t committed = _space->capacity(); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} + +SurvivorContiguousSpacePool::SurvivorContiguousSpacePool(DefNewGeneration* gen, + const char* name, + PoolType type, + size_t max_size, + bool support_usage_threshold) : + CollectedMemoryPool(name, type, gen->from()->capacity(), max_size, + support_usage_threshold), _gen(gen) { +} + +MemoryUsage SurvivorContiguousSpacePool::get_memory_usage() { + size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t used = used_in_bytes(); + size_t committed = committed_in_bytes(); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} + +#ifndef SERIALGC +CompactibleFreeListSpacePool::CompactibleFreeListSpacePool(CompactibleFreeListSpace* space, + const char* name, + PoolType type, + size_t max_size, + bool support_usage_threshold) : + CollectedMemoryPool(name, type, space->capacity(), max_size, + support_usage_threshold), _space(space) { +} + +MemoryUsage CompactibleFreeListSpacePool::get_memory_usage() { + size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t used = used_in_bytes(); + size_t committed = _space->capacity(); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} +#endif // SERIALGC + +GenerationPool::GenerationPool(Generation* gen, + const char* name, + PoolType type, + bool support_usage_threshold) : + CollectedMemoryPool(name, type, gen->capacity(), gen->max_capacity(), + support_usage_threshold), _gen(gen) { +} + +MemoryUsage GenerationPool::get_memory_usage() { + size_t used = used_in_bytes(); + size_t committed = _gen->capacity(); + size_t maxSize = (available_for_allocation() ? max_size() : 0); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} + +CodeHeapPool::CodeHeapPool(CodeHeap* codeHeap, const char* name, bool support_usage_threshold) : + MemoryPool(name, NonHeap, codeHeap->capacity(), codeHeap->max_capacity(), + support_usage_threshold, false), _codeHeap(codeHeap) { +} + +MemoryUsage CodeHeapPool::get_memory_usage() { + size_t used = used_in_bytes(); + size_t committed = _codeHeap->capacity(); + size_t maxSize = (available_for_allocation() ? max_size() : 0); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} diff --git a/hotspot/src/share/vm/services/memoryPool.hpp b/hotspot/src/share/vm/services/memoryPool.hpp new file mode 100644 index 00000000000..953890c057c --- /dev/null +++ b/hotspot/src/share/vm/services/memoryPool.hpp @@ -0,0 +1,212 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A memory pool represents the memory area that the VM manages. +// The Java virtual machine has at least one memory pool +// and it may create or remove memory pools during execution. +// A memory pool can belong to the heap or the non-heap memory. +// A Java virtual machine may also have memory pools belonging to +// both heap and non-heap memory. + +// Forward declaration +class MemoryManager; +class SensorInfo; +class Generation; +class DefNewGeneration; +class PSPermGen; +class PermGen; +class ThresholdSupport; + +class MemoryPool : public CHeapObj { + friend class MemoryManager; + public: + enum PoolType { + Heap = 1, + NonHeap = 2 + }; + + private: + enum { + max_num_managers = 5 + }; + + // We could make some of the following as performance counters + // for external monitoring. + const char* _name; + PoolType _type; + size_t _initial_size; + size_t _max_size; + bool _available_for_allocation; // Default is true + MemoryManager* _managers[max_num_managers]; + int _num_managers; + MemoryUsage _peak_usage; // Peak memory usage + MemoryUsage _after_gc_usage; // After GC memory usage + + ThresholdSupport* _usage_threshold; + ThresholdSupport* _gc_usage_threshold; + + SensorInfo* _usage_sensor; + SensorInfo* _gc_usage_sensor; + + volatile instanceOop _memory_pool_obj; + + void add_manager(MemoryManager* mgr); + + public: + MemoryPool(const char* name, + PoolType type, + size_t init_size, + size_t max_size, + bool support_usage_threshold, + bool support_gc_threshold); + + const char* name() { return _name; } + bool is_heap() { return _type == Heap; } + bool is_non_heap() { return _type == NonHeap; } + size_t initial_size() const { return _initial_size; } + int num_memory_managers() const { return _num_managers; } + // max size could be changed + virtual size_t max_size() const { return _max_size; } + + bool is_pool(instanceHandle pool) { return (pool() == _memory_pool_obj); } + + bool available_for_allocation() { return _available_for_allocation; } + bool set_available_for_allocation(bool value) { + bool prev = _available_for_allocation; + _available_for_allocation = value; + return prev; + } + + MemoryManager* get_memory_manager(int index) { + assert(index >= 0 && index < _num_managers, "Invalid index"); + return _managers[index]; + } + + // Records current memory usage if it's a peak usage + void record_peak_memory_usage(); + + MemoryUsage get_peak_memory_usage() { + // check current memory usage first and then return peak usage + record_peak_memory_usage(); + return _peak_usage; + } + void reset_peak_memory_usage() { + _peak_usage = get_memory_usage(); + } + + ThresholdSupport* usage_threshold() { return _usage_threshold; } + ThresholdSupport* gc_usage_threshold() { return _gc_usage_threshold; } + + SensorInfo* usage_sensor() { return _usage_sensor; } + SensorInfo* gc_usage_sensor() { return _gc_usage_sensor; } + + void set_usage_sensor_obj(instanceHandle s); + void set_gc_usage_sensor_obj(instanceHandle s); + void set_last_collection_usage(MemoryUsage u) { _after_gc_usage = u; } + + virtual instanceOop get_memory_pool_instance(TRAPS); + virtual MemoryUsage get_memory_usage() = 0; + virtual size_t used_in_bytes() = 0; + virtual bool is_collected_pool() { return false; } + virtual MemoryUsage get_last_collection_usage() { return _after_gc_usage; } + + // GC support + void oops_do(OopClosure* f); +}; + +class CollectedMemoryPool : public MemoryPool { +public: + CollectedMemoryPool(const char* name, PoolType type, size_t init_size, size_t max_size, bool support_usage_threshold) : + MemoryPool(name, type, init_size, max_size, support_usage_threshold, true) {}; + bool is_collected_pool() { return true; } +}; + +class ContiguousSpacePool : public CollectedMemoryPool { +private: + ContiguousSpace* _space; + +public: + ContiguousSpacePool(ContiguousSpace* space, const char* name, PoolType type, size_t max_size, bool support_usage_threshold); + + ContiguousSpace* space() { return _space; } + MemoryUsage get_memory_usage(); + size_t used_in_bytes() { return space()->used(); } +}; + +class SurvivorContiguousSpacePool : public CollectedMemoryPool { +private: + DefNewGeneration* _gen; + +public: + SurvivorContiguousSpacePool(DefNewGeneration* gen, + const char* name, + PoolType type, + size_t max_size, + bool support_usage_threshold); + + MemoryUsage get_memory_usage(); + + size_t used_in_bytes() { + return _gen->from()->used(); + } + size_t committed_in_bytes() { + return _gen->from()->capacity(); + } +}; + +#ifndef SERIALGC +class CompactibleFreeListSpacePool : public CollectedMemoryPool { +private: + CompactibleFreeListSpace* _space; +public: + CompactibleFreeListSpacePool(CompactibleFreeListSpace* space, + const char* name, + PoolType type, + size_t max_size, + bool support_usage_threshold); + + MemoryUsage get_memory_usage(); + size_t used_in_bytes() { return _space->used(); } +}; +#endif // SERIALGC + + +class GenerationPool : public CollectedMemoryPool { +private: + Generation* _gen; +public: + GenerationPool(Generation* gen, const char* name, PoolType type, bool support_usage_threshold); + + MemoryUsage get_memory_usage(); + size_t used_in_bytes() { return _gen->used(); } +}; + +class CodeHeapPool: public MemoryPool { +private: + CodeHeap* _codeHeap; +public: + CodeHeapPool(CodeHeap* codeHeap, const char* name, bool support_usage_threshold); + MemoryUsage get_memory_usage(); + size_t used_in_bytes() { return _codeHeap->allocated_capacity(); } +}; diff --git a/hotspot/src/share/vm/services/memoryService.cpp b/hotspot/src/share/vm/services/memoryService.cpp new file mode 100644 index 00000000000..176f11e89bb --- /dev/null +++ b/hotspot/src/share/vm/services/memoryService.cpp @@ -0,0 +1,548 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_memoryService.cpp.incl" + +GrowableArray* MemoryService::_pools_list = + new (ResourceObj::C_HEAP) GrowableArray(init_pools_list_size, true); +GrowableArray* MemoryService::_managers_list = + new (ResourceObj::C_HEAP) GrowableArray(init_managers_list_size, true); + +GCMemoryManager* MemoryService::_minor_gc_manager = NULL; +GCMemoryManager* MemoryService::_major_gc_manager = NULL; +MemoryPool* MemoryService::_code_heap_pool = NULL; + +class GcThreadCountClosure: public ThreadClosure { + private: + int _count; + public: + GcThreadCountClosure() : _count(0) {}; + void do_thread(Thread* thread); + int count() { return _count; } +}; + +void GcThreadCountClosure::do_thread(Thread* thread) { + _count++; +} + +void MemoryService::set_universe_heap(CollectedHeap* heap) { + CollectedHeap::Name kind = heap->kind(); + switch (kind) { + case CollectedHeap::GenCollectedHeap : { + add_gen_collected_heap_info(GenCollectedHeap::heap()); + break; + } +#ifndef SERIALGC + case CollectedHeap::ParallelScavengeHeap : { + add_parallel_scavenge_heap_info(ParallelScavengeHeap::heap()); + break; + } +#endif // SERIALGC + default: { + guarantee(false, "Not recognized kind of heap"); + } + } + + // set the GC thread count + GcThreadCountClosure gctcc; + heap->gc_threads_do(&gctcc); + int count = gctcc.count(); + if (count > 0) { + _minor_gc_manager->set_num_gc_threads(count); + _major_gc_manager->set_num_gc_threads(count); + } + + // All memory pools and memory managers are initialized. + // + _minor_gc_manager->initialize_gc_stat_info(); + _major_gc_manager->initialize_gc_stat_info(); +} + +// Add memory pools for GenCollectedHeap +// This function currently only supports two generations collected heap. +// The collector for GenCollectedHeap will have two memory managers. +void MemoryService::add_gen_collected_heap_info(GenCollectedHeap* heap) { + CollectorPolicy* policy = heap->collector_policy(); + + assert(policy->is_two_generation_policy(), "Only support two generations"); + guarantee(heap->n_gens() == 2, "Only support two-generation heap"); + + TwoGenerationCollectorPolicy* two_gen_policy = policy->as_two_generation_policy(); + if (two_gen_policy != NULL) { + GenerationSpec** specs = two_gen_policy->generations(); + Generation::Name kind = specs[0]->name(); + switch (kind) { + case Generation::DefNew: + _minor_gc_manager = MemoryManager::get_copy_memory_manager(); + break; +#ifndef SERIALGC + case Generation::ParNew: + case Generation::ASParNew: + _minor_gc_manager = MemoryManager::get_parnew_memory_manager(); + break; +#endif // SERIALGC + default: + guarantee(false, "Unrecognized generation spec"); + break; + } + if (policy->is_mark_sweep_policy()) { + _major_gc_manager = MemoryManager::get_msc_memory_manager(); +#ifndef SERIALGC + } else if (policy->is_concurrent_mark_sweep_policy()) { + _major_gc_manager = MemoryManager::get_cms_memory_manager(); +#endif // SERIALGC + } else { + guarantee(false, "Unknown two-gen policy"); + } + } else { + guarantee(false, "Non two-gen policy"); + } + _managers_list->append(_minor_gc_manager); + _managers_list->append(_major_gc_manager); + + add_generation_memory_pool(heap->get_gen(minor), _major_gc_manager, _minor_gc_manager); + add_generation_memory_pool(heap->get_gen(major), _major_gc_manager); + + PermGen::Name name = policy->permanent_generation()->name(); + switch (name) { + case PermGen::MarkSweepCompact: { + CompactingPermGenGen* perm_gen = (CompactingPermGenGen*) heap->perm_gen(); + add_compact_perm_gen_memory_pool(perm_gen, _major_gc_manager); + break; + } +#ifndef SERIALGC + case PermGen::ConcurrentMarkSweep: { + CMSPermGenGen* cms_gen = (CMSPermGenGen*) heap->perm_gen(); + add_cms_perm_gen_memory_pool(cms_gen, _major_gc_manager); + break; + } +#endif // SERIALGC + default: + guarantee(false, "Unrecognized perm generation"); + break; + } +} + +#ifndef SERIALGC +// Add memory pools for ParallelScavengeHeap +// This function currently only supports two generations collected heap. +// The collector for ParallelScavengeHeap will have two memory managers. +void MemoryService::add_parallel_scavenge_heap_info(ParallelScavengeHeap* heap) { + // Two managers to keep statistics about _minor_gc_manager and _major_gc_manager GC. + _minor_gc_manager = MemoryManager::get_psScavenge_memory_manager(); + _major_gc_manager = MemoryManager::get_psMarkSweep_memory_manager(); + _managers_list->append(_minor_gc_manager); + _managers_list->append(_major_gc_manager); + + add_psYoung_memory_pool(heap->young_gen(), _major_gc_manager, _minor_gc_manager); + add_psOld_memory_pool(heap->old_gen(), _major_gc_manager); + add_psPerm_memory_pool(heap->perm_gen(), _major_gc_manager); +} +#endif // SERIALGC + +MemoryPool* MemoryService::add_gen(Generation* gen, + const char* name, + bool is_heap, + bool support_usage_threshold) { + + MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap); + GenerationPool* pool = new GenerationPool(gen, name, type, support_usage_threshold); + _pools_list->append(pool); + return (MemoryPool*) pool; +} + +MemoryPool* MemoryService::add_space(ContiguousSpace* space, + const char* name, + bool is_heap, + size_t max_size, + bool support_usage_threshold) { + MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap); + ContiguousSpacePool* pool = new ContiguousSpacePool(space, name, type, max_size, support_usage_threshold); + + _pools_list->append(pool); + return (MemoryPool*) pool; +} + +MemoryPool* MemoryService::add_survivor_spaces(DefNewGeneration* gen, + const char* name, + bool is_heap, + size_t max_size, + bool support_usage_threshold) { + MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap); + SurvivorContiguousSpacePool* pool = new SurvivorContiguousSpacePool(gen, name, type, max_size, support_usage_threshold); + + _pools_list->append(pool); + return (MemoryPool*) pool; +} + +#ifndef SERIALGC +MemoryPool* MemoryService::add_cms_space(CompactibleFreeListSpace* space, + const char* name, + bool is_heap, + size_t max_size, + bool support_usage_threshold) { + MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap); + CompactibleFreeListSpacePool* pool = new CompactibleFreeListSpacePool(space, name, type, max_size, support_usage_threshold); + _pools_list->append(pool); + return (MemoryPool*) pool; +} +#endif // SERIALGC + +// Add memory pool(s) for one generation +void MemoryService::add_generation_memory_pool(Generation* gen, + MemoryManager* major_mgr, + MemoryManager* minor_mgr) { + Generation::Name kind = gen->kind(); + int index = _pools_list->length(); + + switch (kind) { + case Generation::DefNew: { + assert(major_mgr != NULL && minor_mgr != NULL, "Should have two managers"); + DefNewGeneration* young_gen = (DefNewGeneration*) gen; + // Add a memory pool for each space and young gen doesn't + // support low memory detection as it is expected to get filled up. + MemoryPool* eden = add_space(young_gen->eden(), + "Eden Space", + true, /* is_heap */ + young_gen->max_eden_size(), + false /* support_usage_threshold */); + MemoryPool* survivor = add_survivor_spaces(young_gen, + "Survivor Space", + true, /* is_heap */ + young_gen->max_survivor_size(), + false /* support_usage_threshold */); + break; + } + +#ifndef SERIALGC + case Generation::ParNew: + case Generation::ASParNew: + { + assert(major_mgr != NULL && minor_mgr != NULL, "Should have two managers"); + // Add a memory pool for each space and young gen doesn't + // support low memory detection as it is expected to get filled up. + ParNewGeneration* parnew_gen = (ParNewGeneration*) gen; + MemoryPool* eden = add_space(parnew_gen->eden(), + "Par Eden Space", + true /* is_heap */, + parnew_gen->max_eden_size(), + false /* support_usage_threshold */); + MemoryPool* survivor = add_survivor_spaces(parnew_gen, + "Par Survivor Space", + true, /* is_heap */ + parnew_gen->max_survivor_size(), + false /* support_usage_threshold */); + + break; + } +#endif // SERIALGC + + case Generation::MarkSweepCompact: { + assert(major_mgr != NULL && minor_mgr == NULL, "Should have only one manager"); + add_gen(gen, + "Tenured Gen", + true, /* is_heap */ + true /* support_usage_threshold */); + break; + } + +#ifndef SERIALGC + case Generation::ConcurrentMarkSweep: + case Generation::ASConcurrentMarkSweep: + { + assert(major_mgr != NULL && minor_mgr == NULL, "Should have only one manager"); + ConcurrentMarkSweepGeneration* cms = (ConcurrentMarkSweepGeneration*) gen; + MemoryPool* pool = add_cms_space(cms->cmsSpace(), + "CMS Old Gen", + true, /* is_heap */ + cms->reserved().byte_size(), + true /* support_usage_threshold */); + break; + } +#endif // SERIALGC + + default: + assert(false, "should not reach here"); + // no memory pool added for others + break; + } + + assert(major_mgr != NULL, "Should have at least one manager"); + // Link managers and the memory pools together + for (int i = index; i < _pools_list->length(); i++) { + MemoryPool* pool = _pools_list->at(i); + major_mgr->add_pool(pool); + if (minor_mgr != NULL) { + minor_mgr->add_pool(pool); + } + } +} + +void MemoryService::add_compact_perm_gen_memory_pool(CompactingPermGenGen* perm_gen, + MemoryManager* mgr) { + PermanentGenerationSpec* spec = perm_gen->spec(); + size_t max_size = spec->max_size() - spec->read_only_size() - spec->read_write_size(); + MemoryPool* pool = add_space(perm_gen->unshared_space(), + "Perm Gen", + false, /* is_heap */ + max_size, + true /* support_usage_threshold */); + mgr->add_pool(pool); + if (UseSharedSpaces) { + pool = add_space(perm_gen->ro_space(), + "Perm Gen [shared-ro]", + false, /* is_heap */ + spec->read_only_size(), + true /* support_usage_threshold */); + mgr->add_pool(pool); + + pool = add_space(perm_gen->rw_space(), + "Perm Gen [shared-rw]", + false, /* is_heap */ + spec->read_write_size(), + true /* support_usage_threshold */); + mgr->add_pool(pool); + } +} + +#ifndef SERIALGC +void MemoryService::add_cms_perm_gen_memory_pool(CMSPermGenGen* cms_gen, + MemoryManager* mgr) { + + MemoryPool* pool = add_cms_space(cms_gen->cmsSpace(), + "CMS Perm Gen", + false, /* is_heap */ + cms_gen->reserved().byte_size(), + true /* support_usage_threshold */); + mgr->add_pool(pool); +} + +void MemoryService::add_psYoung_memory_pool(PSYoungGen* gen, MemoryManager* major_mgr, MemoryManager* minor_mgr) { + assert(major_mgr != NULL && minor_mgr != NULL, "Should have two managers"); + + // Add a memory pool for each space and young gen doesn't + // support low memory detection as it is expected to get filled up. + EdenMutableSpacePool* eden = new EdenMutableSpacePool(gen, + gen->eden_space(), + "PS Eden Space", + MemoryPool::Heap, + false /* support_usage_threshold */); + + SurvivorMutableSpacePool* survivor = new SurvivorMutableSpacePool(gen, + "PS Survivor Space", + MemoryPool::Heap, + false /* support_usage_threshold */); + + major_mgr->add_pool(eden); + major_mgr->add_pool(survivor); + minor_mgr->add_pool(eden); + minor_mgr->add_pool(survivor); + _pools_list->append(eden); + _pools_list->append(survivor); +} + +void MemoryService::add_psOld_memory_pool(PSOldGen* gen, MemoryManager* mgr) { + PSGenerationPool* old_gen = new PSGenerationPool(gen, + "PS Old Gen", + MemoryPool::Heap, + true /* support_usage_threshold */); + mgr->add_pool(old_gen); + _pools_list->append(old_gen); +} + +void MemoryService::add_psPerm_memory_pool(PSPermGen* gen, MemoryManager* mgr) { + PSGenerationPool* perm_gen = new PSGenerationPool(gen, + "PS Perm Gen", + MemoryPool::NonHeap, + true /* support_usage_threshold */); + mgr->add_pool(perm_gen); + _pools_list->append(perm_gen); +} +#endif // SERIALGC + +void MemoryService::add_code_heap_memory_pool(CodeHeap* heap) { + _code_heap_pool = new CodeHeapPool(heap, + "Code Cache", + true /* support_usage_threshold */); + MemoryManager* mgr = MemoryManager::get_code_cache_memory_manager(); + mgr->add_pool(_code_heap_pool); + + _pools_list->append(_code_heap_pool); + _managers_list->append(mgr); +} + +MemoryManager* MemoryService::get_memory_manager(instanceHandle mh) { + for (int i = 0; i < _managers_list->length(); i++) { + MemoryManager* mgr = _managers_list->at(i); + if (mgr->is_manager(mh)) { + return mgr; + } + } + return NULL; +} + +MemoryPool* MemoryService::get_memory_pool(instanceHandle ph) { + for (int i = 0; i < _pools_list->length(); i++) { + MemoryPool* pool = _pools_list->at(i); + if (pool->is_pool(ph)) { + return pool; + } + } + return NULL; +} + +void MemoryService::track_memory_usage() { + // Track the peak memory usage + for (int i = 0; i < _pools_list->length(); i++) { + MemoryPool* pool = _pools_list->at(i); + pool->record_peak_memory_usage(); + } + + // Detect low memory + LowMemoryDetector::detect_low_memory(); +} + +void MemoryService::track_memory_pool_usage(MemoryPool* pool) { + // Track the peak memory usage + pool->record_peak_memory_usage(); + + // Detect low memory + if (LowMemoryDetector::is_enabled(pool)) { + LowMemoryDetector::detect_low_memory(pool); + } +} + +void MemoryService::gc_begin(bool fullGC) { + GCMemoryManager* mgr; + if (fullGC) { + mgr = _major_gc_manager; + } else { + mgr = _minor_gc_manager; + } + assert(mgr->is_gc_memory_manager(), "Sanity check"); + mgr->gc_begin(); + + // Track the peak memory usage when GC begins + for (int i = 0; i < _pools_list->length(); i++) { + MemoryPool* pool = _pools_list->at(i); + pool->record_peak_memory_usage(); + } +} + +void MemoryService::gc_end(bool fullGC) { + GCMemoryManager* mgr; + if (fullGC) { + mgr = (GCMemoryManager*) _major_gc_manager; + } else { + mgr = (GCMemoryManager*) _minor_gc_manager; + } + assert(mgr->is_gc_memory_manager(), "Sanity check"); + + // register the GC end statistics and memory usage + mgr->gc_end(); +} + +void MemoryService::oops_do(OopClosure* f) { + int i; + + for (i = 0; i < _pools_list->length(); i++) { + MemoryPool* pool = _pools_list->at(i); + pool->oops_do(f); + } + for (i = 0; i < _managers_list->length(); i++) { + MemoryManager* mgr = _managers_list->at(i); + mgr->oops_do(f); + } +} + +bool MemoryService::set_verbose(bool verbose) { + MutexLocker m(Management_lock); + // verbose will be set to the previous value + bool succeed = CommandLineFlags::boolAtPut((char*)"PrintGC", &verbose, MANAGEMENT); + assert(succeed, "Setting PrintGC flag fails"); + ClassLoadingService::reset_trace_class_unloading(); + + return verbose; +} + +Handle MemoryService::create_MemoryUsage_obj(MemoryUsage usage, TRAPS) { + klassOop k = Management::java_lang_management_MemoryUsage_klass(CHECK_NH); + instanceKlassHandle ik(THREAD, k); + + instanceHandle obj = ik->allocate_instance_handle(CHECK_NH); + + JavaValue result(T_VOID); + JavaCallArguments args(10); + args.push_oop(obj); // receiver + args.push_long(usage.init_size_as_jlong()); // Argument 1 + args.push_long(usage.used_as_jlong()); // Argument 2 + args.push_long(usage.committed_as_jlong()); // Argument 3 + args.push_long(usage.max_size_as_jlong()); // Argument 4 + + JavaCalls::call_special(&result, + ik, + vmSymbolHandles::object_initializer_name(), + vmSymbolHandles::long_long_long_long_void_signature(), + &args, + CHECK_NH); + return obj; +} +// +// GC manager type depends on the type of Generation. Depending the space +// availablity and vm option the gc uses major gc manager or minor gc +// manager or both. The type of gc manager depends on the generation kind. +// For DefNew, ParNew and ASParNew generation doing scavange gc uses minor +// gc manager (so _fullGC is set to false ) and for other generation kind +// DOing mark-sweep-compact uses major gc manager (so _fullGC is set +// to true). +TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind) { + switch (kind) { + case Generation::DefNew: +#ifndef SERIALGC + case Generation::ParNew: + case Generation::ASParNew: +#endif // SERIALGC + _fullGC=false; + break; + case Generation::MarkSweepCompact: +#ifndef SERIALGC + case Generation::ConcurrentMarkSweep: + case Generation::ASConcurrentMarkSweep: +#endif // SERIALGC + _fullGC=true; + break; + default: + assert(false, "Unrecognized gc generation kind."); + } + MemoryService::gc_begin(_fullGC); +} +TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC) { + _fullGC = fullGC; + MemoryService::gc_begin(_fullGC); +} + +TraceMemoryManagerStats::~TraceMemoryManagerStats() { + MemoryService::gc_end(_fullGC); +} diff --git a/hotspot/src/share/vm/services/memoryService.hpp b/hotspot/src/share/vm/services/memoryService.hpp new file mode 100644 index 00000000000..52b76a72937 --- /dev/null +++ b/hotspot/src/share/vm/services/memoryService.hpp @@ -0,0 +1,162 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Forward declaration +class MemoryPool; +class MemoryManager; +class GCMemoryManager; +class CollectedHeap; +class Generation; +class DefNewGeneration; +class PSYoungGen; +class PSOldGen; +class PSPermGen; +class CodeHeap; +class ContiguousSpace; +class CompactibleFreeListSpace; +class PermanentGenerationSpec; +class GenCollectedHeap; +class ParallelScavengeHeap; +class CompactingPermGenGen; +class CMSPermGenGen; + +// VM Monitoring and Management Support + +class MemoryService : public AllStatic { +private: + enum { + init_pools_list_size = 10, + init_managers_list_size = 5 + }; + + // index for minor and major generations + enum { + minor = 0, + major = 1, + n_gens = 2 + }; + + static GrowableArray* _pools_list; + static GrowableArray* _managers_list; + + // memory managers for minor and major GC statistics + static GCMemoryManager* _major_gc_manager; + static GCMemoryManager* _minor_gc_manager; + + // Code heap memory pool + static MemoryPool* _code_heap_pool; + + static void add_generation_memory_pool(Generation* gen, + MemoryManager* major_mgr, + MemoryManager* minor_mgr); + static void add_generation_memory_pool(Generation* gen, + MemoryManager* major_mgr) { + add_generation_memory_pool(gen, major_mgr, NULL); + } + + static void add_compact_perm_gen_memory_pool(CompactingPermGenGen* perm_gen, + MemoryManager* mgr); + static void add_cms_perm_gen_memory_pool(CMSPermGenGen* perm_gen, + MemoryManager* mgr); + + static void add_psYoung_memory_pool(PSYoungGen* gen, + MemoryManager* major_mgr, + MemoryManager* minor_mgr); + static void add_psOld_memory_pool(PSOldGen* gen, + MemoryManager* mgr); + static void add_psPerm_memory_pool(PSPermGen* perm, + MemoryManager* mgr); + + + static MemoryPool* add_space(ContiguousSpace* space, + const char* name, + bool is_heap, + size_t max_size, + bool support_usage_threshold); + static MemoryPool* add_survivor_spaces(DefNewGeneration* gen, + const char* name, + bool is_heap, + size_t max_size, + bool support_usage_threshold); + static MemoryPool* add_gen(Generation* gen, + const char* name, + bool is_heap, + bool support_usage_threshold); + static MemoryPool* add_cms_space(CompactibleFreeListSpace* space, + const char* name, + bool is_heap, + size_t max_size, + bool support_usage_threshold); + + static void add_gen_collected_heap_info(GenCollectedHeap* heap); + static void add_parallel_scavenge_heap_info(ParallelScavengeHeap* heap); + +public: + static void set_universe_heap(CollectedHeap* heap); + static void add_code_heap_memory_pool(CodeHeap* heap); + + static MemoryPool* get_memory_pool(instanceHandle pool); + static MemoryManager* get_memory_manager(instanceHandle mgr); + + static const int num_memory_pools() { + return _pools_list->length(); + } + static const int num_memory_managers() { + return _managers_list->length(); + } + + static MemoryPool* get_memory_pool(int index) { + return _pools_list->at(index); + } + + static MemoryManager* get_memory_manager(int index) { + return _managers_list->at(index); + } + + static void track_memory_usage(); + static void track_code_cache_memory_usage() { + track_memory_pool_usage(_code_heap_pool); + } + static void track_memory_pool_usage(MemoryPool* pool); + + static void gc_begin(bool fullGC); + static void gc_end(bool fullGC); + + static void oops_do(OopClosure* f); + + static bool get_verbose() { return PrintGC; } + static bool set_verbose(bool verbose); + + // Create an instance of java/lang/management/MemoryUsage + static Handle create_MemoryUsage_obj(MemoryUsage usage, TRAPS); +}; + +class TraceMemoryManagerStats : public StackObj { +private: + bool _fullGC; +public: + TraceMemoryManagerStats(bool fullGC); + TraceMemoryManagerStats(Generation::Name kind); + ~TraceMemoryManagerStats(); +}; diff --git a/hotspot/src/share/vm/services/memoryUsage.hpp b/hotspot/src/share/vm/services/memoryUsage.hpp new file mode 100644 index 00000000000..7276d9ca58f --- /dev/null +++ b/hotspot/src/share/vm/services/memoryUsage.hpp @@ -0,0 +1,77 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A memory usage contains the following attributes about memory usage: +// initSize - represents the initial amount of memory (in bytes) that +// the Java virtual machine requests from the operating system +// for memory management. The Java virtual machine may request +// additional memory from the operating system later when appropriate. +// Its value may be undefined. +// used - represents the amount of memory currently used (in bytes). +// committed - represents the amount of memory (in bytes) that is +// guaranteed to be available for use by the Java virtual machine. +// The amount of committed memory may change over time (increase +// or decrease). It is guaranteed to be greater than or equal +// to initSize. +// maxSize - represents the maximum amount of memory (in bytes) +// that can be used for memory management. The maximum amount of +// memory for memory management could be less than the amount of +// committed memory. Its value may be undefined. + +class MemoryUsage VALUE_OBJ_CLASS_SPEC { +private: + size_t _initSize; + size_t _used; + size_t _committed; + size_t _maxSize; + +public: + // Constructors + MemoryUsage(size_t i, size_t u, size_t c, size_t m) : + _initSize(i), _used(u), _committed(c), _maxSize(m) {}; + MemoryUsage() : + _initSize(0), _used(0), _committed(0), _maxSize(0) {}; + + size_t init_size() const { return _initSize; } + size_t used() const { return _used; } + size_t committed() const { return _committed; } + size_t max_size() const { return _maxSize; } + + inline static jlong convert_to_jlong(size_t val) { + // In the 64-bit vm, a size_t can overflow a jlong (which is signed). + jlong ret; + if (val == (size_t)-1) { + ret = -1L; + } else { + NOT_LP64(ret = val;) + LP64_ONLY(ret = MIN2(val, (size_t)max_jlong);) + } + return ret; + } + + jlong init_size_as_jlong() const { return convert_to_jlong(_initSize); } + jlong used_as_jlong() const { return convert_to_jlong(_used); } + jlong committed_as_jlong() const { return convert_to_jlong(_committed); } + jlong max_size_as_jlong() const { return convert_to_jlong(_maxSize); } +}; diff --git a/hotspot/src/share/vm/services/psMemoryPool.cpp b/hotspot/src/share/vm/services/psMemoryPool.cpp new file mode 100644 index 00000000000..3541d00a1f4 --- /dev/null +++ b/hotspot/src/share/vm/services/psMemoryPool.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_psMemoryPool.cpp.incl" + +PSGenerationPool::PSGenerationPool(PSOldGen* gen, + const char* name, + PoolType type, + bool support_usage_threshold) : + CollectedMemoryPool(name, type, gen->capacity_in_bytes(), + gen->reserved().byte_size(), support_usage_threshold), _gen(gen) { +} + +PSGenerationPool::PSGenerationPool(PSPermGen* gen, + const char* name, + PoolType type, + bool support_usage_threshold) : + CollectedMemoryPool(name, type, gen->capacity_in_bytes(), + gen->reserved().byte_size(), support_usage_threshold), _gen(gen) { +} + +MemoryUsage PSGenerationPool::get_memory_usage() { + size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t used = used_in_bytes(); + size_t committed = _gen->capacity_in_bytes(); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} + +// The max size of EdenMutableSpacePool = +// max size of the PSYoungGen - capacity of two survivor spaces +// +// Max size of PS eden space is changing due to ergonomic. +// PSYoungGen, PSOldGen, Eden, Survivor spaces are all resizable. +// +EdenMutableSpacePool::EdenMutableSpacePool(PSYoungGen* gen, + MutableSpace* space, + const char* name, + PoolType type, + bool support_usage_threshold) : + CollectedMemoryPool(name, type, space->capacity_in_bytes(), + (gen->max_size() - gen->from_space()->capacity_in_bytes() - gen->to_space()->capacity_in_bytes()), + support_usage_threshold), + _gen(gen), _space(space) { +} + +MemoryUsage EdenMutableSpacePool::get_memory_usage() { + size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t used = used_in_bytes(); + size_t committed = _space->capacity_in_bytes(); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} + +// The max size of SurvivorMutableSpacePool = +// current capacity of the from-space +// +// PS from and to survivor spaces could have different sizes. +// +SurvivorMutableSpacePool::SurvivorMutableSpacePool(PSYoungGen* gen, + const char* name, + PoolType type, + bool support_usage_threshold) : + CollectedMemoryPool(name, type, gen->from_space()->capacity_in_bytes(), + gen->from_space()->capacity_in_bytes(), + support_usage_threshold), _gen(gen) { +} + +MemoryUsage SurvivorMutableSpacePool::get_memory_usage() { + size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t used = used_in_bytes(); + size_t committed = committed_in_bytes(); + return MemoryUsage(initial_size(), used, committed, maxSize); +} diff --git a/hotspot/src/share/vm/services/psMemoryPool.hpp b/hotspot/src/share/vm/services/psMemoryPool.hpp new file mode 100644 index 00000000000..5188a8ff540 --- /dev/null +++ b/hotspot/src/share/vm/services/psMemoryPool.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class PSGenerationPool : public CollectedMemoryPool { +private: + PSOldGen* _gen; + +public: + PSGenerationPool(PSOldGen* pool, const char* name, PoolType type, bool support_usage_threshold); + PSGenerationPool(PSPermGen* pool, const char* name, PoolType type, bool support_usage_threshold); + + MemoryUsage get_memory_usage(); + size_t used_in_bytes() { return _gen->used_in_bytes(); } + size_t max_size() const { return _gen->reserved().byte_size(); } +}; + +class EdenMutableSpacePool : public CollectedMemoryPool { +private: + PSYoungGen* _gen; + MutableSpace* _space; + +public: + EdenMutableSpacePool(PSYoungGen* gen, + MutableSpace* space, + const char* name, + PoolType type, + bool support_usage_threshold); + + MutableSpace* space() { return _space; } + MemoryUsage get_memory_usage(); + size_t used_in_bytes() { return space()->used_in_bytes(); } + size_t max_size() const { + // Eden's max_size = max_size of Young Gen - the current committed size of survivor spaces + return _gen->max_size() - _gen->from_space()->capacity_in_bytes() - _gen->to_space()->capacity_in_bytes(); + } +}; + +class SurvivorMutableSpacePool : public CollectedMemoryPool { +private: + PSYoungGen* _gen; + +public: + SurvivorMutableSpacePool(PSYoungGen* gen, + const char* name, + PoolType type, + bool support_usage_threshold); + + MemoryUsage get_memory_usage(); + + size_t used_in_bytes() { + return _gen->from_space()->used_in_bytes(); + } + size_t committed_in_bytes() { + return _gen->from_space()->capacity_in_bytes(); + } + size_t max_size() const { + // Return current committed size of the from-space + return _gen->from_space()->capacity_in_bytes(); + } +}; diff --git a/hotspot/src/share/vm/services/runtimeService.cpp b/hotspot/src/share/vm/services/runtimeService.cpp new file mode 100644 index 00000000000..16811a2aa5b --- /dev/null +++ b/hotspot/src/share/vm/services/runtimeService.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_runtimeService.cpp.incl" + +HS_DTRACE_PROBE_DECL(hs_private, safepoint__begin); +HS_DTRACE_PROBE_DECL(hs_private, safepoint__end); + +TimeStamp RuntimeService::_app_timer; +TimeStamp RuntimeService::_safepoint_timer; +PerfCounter* RuntimeService::_sync_time_ticks = NULL; +PerfCounter* RuntimeService::_total_safepoints = NULL; +PerfCounter* RuntimeService::_safepoint_time_ticks = NULL; +PerfCounter* RuntimeService::_application_time_ticks = NULL; +PerfCounter* RuntimeService::_thread_interrupt_signaled_count = NULL; +PerfCounter* RuntimeService::_interrupted_before_count = NULL; +PerfCounter* RuntimeService::_interrupted_during_count = NULL; + +void RuntimeService::init() { + // Make sure the VM version is initialized + Abstract_VM_Version::initialize(); + + if (UsePerfData) { + EXCEPTION_MARK; + + _sync_time_ticks = + PerfDataManager::create_counter(SUN_RT, "safepointSyncTime", + PerfData::U_Ticks, CHECK); + + _total_safepoints = + PerfDataManager::create_counter(SUN_RT, "safepoints", + PerfData::U_Events, CHECK); + + _safepoint_time_ticks = + PerfDataManager::create_counter(SUN_RT, "safepointTime", + PerfData::U_Ticks, CHECK); + + _application_time_ticks = + PerfDataManager::create_counter(SUN_RT, "applicationTime", + PerfData::U_Ticks, CHECK); + + + // create performance counters for jvm_version and its capabilities + PerfDataManager::create_constant(SUN_RT, "jvmVersion", PerfData::U_None, + (jlong) Abstract_VM_Version::jvm_version(), CHECK); + + // I/O interruption related counters + + // thread signaling via os::interrupt() + + _thread_interrupt_signaled_count = + PerfDataManager::create_counter(SUN_RT, + "threadInterruptSignaled", PerfData::U_Events, CHECK); + + // OS_INTRPT via "check before" in _INTERRUPTIBLE + + _interrupted_before_count = + PerfDataManager::create_counter(SUN_RT, "interruptedBeforeIO", + PerfData::U_Events, CHECK); + + // OS_INTRPT via "check during" in _INTERRUPTIBLE + + _interrupted_during_count = + PerfDataManager::create_counter(SUN_RT, "interruptedDuringIO", + PerfData::U_Events, CHECK); + + // The capabilities counter is a binary representation of the VM capabilities in string. + // This string respresentation simplifies the implementation of the client side + // to parse the value. + char capabilities[65]; + size_t len = sizeof(capabilities); + memset((void*) capabilities, '0', len); + capabilities[len-1] = '\0'; + capabilities[0] = AttachListener::is_attach_supported() ? '1' : '0'; +#ifdef KERNEL + capabilities[1] = '1'; +#endif // KERNEL + PerfDataManager::create_string_constant(SUN_RT, "jvmCapabilities", + capabilities, CHECK); + } +} + +void RuntimeService::record_safepoint_begin() { + HS_DTRACE_PROBE(hs_private, safepoint__begin); + // update the time stamp to begin recording safepoint time + _safepoint_timer.update(); + if (UsePerfData) { + _total_safepoints->inc(); + if (_app_timer.is_updated()) { + _application_time_ticks->inc(_app_timer.ticks_since_update()); + } + } +} + +void RuntimeService::record_safepoint_synchronized() { + if (UsePerfData) { + _sync_time_ticks->inc(_safepoint_timer.ticks_since_update()); + } +} + +void RuntimeService::record_safepoint_end() { + HS_DTRACE_PROBE(hs_private, safepoint__end); + // update the time stamp to begin recording app time + _app_timer.update(); + if (UsePerfData) { + _safepoint_time_ticks->inc(_safepoint_timer.ticks_since_update()); + } +} + +void RuntimeService::record_application_start() { + // update the time stamp to begin recording app time + _app_timer.update(); +} + +// Don't need to record application end because we currently +// exit at a safepoint and record_safepoint_begin() handles updating +// the application time counter at VM exit. + +jlong RuntimeService::safepoint_sync_time_ms() { + return UsePerfData ? + Management::ticks_to_ms(_sync_time_ticks->get_value()) : -1; +} + +jlong RuntimeService::safepoint_count() { + return UsePerfData ? + _total_safepoints->get_value() : -1; +} +jlong RuntimeService::safepoint_time_ms() { + return UsePerfData ? + Management::ticks_to_ms(_safepoint_time_ticks->get_value()) : -1; +} + +jlong RuntimeService::application_time_ms() { + return UsePerfData ? + Management::ticks_to_ms(_application_time_ticks->get_value()) : -1; +} + +void RuntimeService::record_interrupted_before_count() { + if (UsePerfData) { + _interrupted_before_count->inc(); + } +} + +void RuntimeService::record_interrupted_during_count() { + if (UsePerfData) { + _interrupted_during_count->inc(); + } +} + +void RuntimeService::record_thread_interrupt_signaled_count() { + if (UsePerfData) { + _thread_interrupt_signaled_count->inc(); + } +} diff --git a/hotspot/src/share/vm/services/runtimeService.hpp b/hotspot/src/share/vm/services/runtimeService.hpp new file mode 100644 index 00000000000..47de1c05667 --- /dev/null +++ b/hotspot/src/share/vm/services/runtimeService.hpp @@ -0,0 +1,59 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class RuntimeService : public AllStatic { +private: + static PerfCounter* _sync_time_ticks; // Accumulated time spent getting to safepoints + static PerfCounter* _total_safepoints; + static PerfCounter* _safepoint_time_ticks; // Accumulated time at safepoints + static PerfCounter* _application_time_ticks; // Accumulated time not at safepoints + static PerfCounter* _thread_interrupt_signaled_count;// os:interrupt thr_kill + static PerfCounter* _interrupted_before_count; // _INTERRUPTIBLE OS_INTRPT + static PerfCounter* _interrupted_during_count; // _INTERRUPTIBLE OS_INTRPT + + static TimeStamp _safepoint_timer; + static TimeStamp _app_timer; + +public: + static void init(); + + static jlong safepoint_sync_time_ms(); + static jlong safepoint_count(); + static jlong safepoint_time_ms(); + static jlong application_time_ms(); + + static double last_safepoint_time_sec() { return _safepoint_timer.seconds(); } + static double last_application_time_sec() { return _app_timer.seconds(); } + + // callbacks + static void record_safepoint_begin(); + static void record_safepoint_synchronized(); + static void record_safepoint_end(); + static void record_application_start(); + + // interruption events + static void record_interrupted_before_count(); + static void record_interrupted_during_count(); + static void record_thread_interrupt_signaled_count(); +}; diff --git a/hotspot/src/share/vm/services/serviceUtil.hpp b/hotspot/src/share/vm/services/serviceUtil.hpp new file mode 100644 index 00000000000..bf907fe00e0 --- /dev/null +++ b/hotspot/src/share/vm/services/serviceUtil.hpp @@ -0,0 +1,89 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Serviceability utility functions. +// (Shared by MM and JVMTI). +// +class ServiceUtil : public AllStatic { + public: + + // Return true if oop represents an object that is "visible" + // to the java world. + static inline bool visible_oop(oop o) { + // the sentinel for deleted handles isn't visible + if (o == JNIHandles::deleted_handle()) { + return false; + } + + // ignore KlassKlass + if (o->is_klass()) { + return false; + } + + // instance + if (o->is_instance()) { + // instance objects are visible + if (o->klass() != SystemDictionary::class_klass()) { + return true; + } + if (java_lang_Class::is_primitive(o)) { + return true; + } + // java.lang.Classes are visible + o = java_lang_Class::as_klassOop(o); + if (o->is_klass()) { + // if it's a class for an object, an object array, or + // primitive (type) array then it's visible. + klassOop klass = (klassOop)o; + if (Klass::cast(klass)->oop_is_instance()) { + return true; + } + if (Klass::cast(klass)->oop_is_objArray()) { + return true; + } + if (Klass::cast(klass)->oop_is_typeArray()) { + return true; + } + } + return false; + } + // object arrays are visible if they aren't system object arrays + if (o->is_objArray()) { + objArrayOop array = (objArrayOop)o; + if (array->klass() != Universe::systemObjArrayKlassObj()) { + return true; + } else { + return false; + } + } + // type arrays are visible + if (o->is_typeArray()) { + return true; + } + // everything else (methodOops, ...) aren't visible + return false; + }; // end of visible_oop() + +}; diff --git a/hotspot/src/share/vm/services/threadService.cpp b/hotspot/src/share/vm/services/threadService.cpp new file mode 100644 index 00000000000..80dcb486c33 --- /dev/null +++ b/hotspot/src/share/vm/services/threadService.cpp @@ -0,0 +1,885 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_threadService.cpp.incl" + +// TODO: we need to define a naming convention for perf counters +// to distinguish counters for: +// - standard JSR174 use +// - Hotspot extension (public and committed) +// - Hotspot extension (private/internal and uncommitted) + +// Default is disabled. +bool ThreadService::_thread_monitoring_contention_enabled = false; +bool ThreadService::_thread_cpu_time_enabled = false; + +PerfCounter* ThreadService::_total_threads_count = NULL; +PerfVariable* ThreadService::_live_threads_count = NULL; +PerfVariable* ThreadService::_peak_threads_count = NULL; +PerfVariable* ThreadService::_daemon_threads_count = NULL; +volatile int ThreadService::_exiting_threads_count = 0; +volatile int ThreadService::_exiting_daemon_threads_count = 0; + +ThreadDumpResult* ThreadService::_threaddump_list = NULL; + +static const int INITIAL_ARRAY_SIZE = 10; + +void ThreadService::init() { + EXCEPTION_MARK; + + // These counters are for java.lang.management API support. + // They are created even if -XX:-UsePerfData is set and in + // that case, they will be allocated on C heap. + + _total_threads_count = + PerfDataManager::create_counter(JAVA_THREADS, "started", + PerfData::U_Events, CHECK); + + _live_threads_count = + PerfDataManager::create_variable(JAVA_THREADS, "live", + PerfData::U_None, CHECK); + + _peak_threads_count = + PerfDataManager::create_variable(JAVA_THREADS, "livePeak", + PerfData::U_None, CHECK); + + _daemon_threads_count = + PerfDataManager::create_variable(JAVA_THREADS, "daemon", + PerfData::U_None, CHECK); + + if (os::is_thread_cpu_time_supported()) { + _thread_cpu_time_enabled = true; + } +} + +void ThreadService::reset_peak_thread_count() { + // Acquire the lock to update the peak thread count + // to synchronize with thread addition and removal. + MutexLockerEx mu(Threads_lock); + _peak_threads_count->set_value(get_live_thread_count()); +} + +void ThreadService::add_thread(JavaThread* thread, bool daemon) { + // Do not count VM internal or JVMTI agent threads + if (thread->is_hidden_from_external_view() || + thread->is_jvmti_agent_thread()) { + return; + } + + _total_threads_count->inc(); + _live_threads_count->inc(); + + if (_live_threads_count->get_value() > _peak_threads_count->get_value()) { + _peak_threads_count->set_value(_live_threads_count->get_value()); + } + + if (daemon) { + _daemon_threads_count->inc(); + } +} + +void ThreadService::remove_thread(JavaThread* thread, bool daemon) { + Atomic::dec((jint*) &_exiting_threads_count); + + if (thread->is_hidden_from_external_view() || + thread->is_jvmti_agent_thread()) { + return; + } + + _live_threads_count->set_value(_live_threads_count->get_value() - 1); + + if (daemon) { + _daemon_threads_count->set_value(_daemon_threads_count->get_value() - 1); + Atomic::dec((jint*) &_exiting_daemon_threads_count); + } +} + +void ThreadService::current_thread_exiting(JavaThread* jt) { + assert(jt == JavaThread::current(), "Called by current thread"); + Atomic::inc((jint*) &_exiting_threads_count); + + oop threadObj = jt->threadObj(); + if (threadObj != NULL && java_lang_Thread::is_daemon(threadObj)) { + Atomic::inc((jint*) &_exiting_daemon_threads_count); + } +} + +// FIXME: JVMTI should call this function +Handle ThreadService::get_current_contended_monitor(JavaThread* thread) { + assert(thread != NULL, "should be non-NULL"); + assert(Threads_lock->owned_by_self(), "must grab Threads_lock or be at safepoint"); + + ObjectMonitor *wait_obj = thread->current_waiting_monitor(); + + oop obj = NULL; + if (wait_obj != NULL) { + // thread is doing an Object.wait() call + obj = (oop) wait_obj->object(); + assert(obj != NULL, "Object.wait() should have an object"); + } else { + ObjectMonitor *enter_obj = thread->current_pending_monitor(); + if (enter_obj != NULL) { + // thread is trying to enter() or raw_enter() an ObjectMonitor. + obj = (oop) enter_obj->object(); + } + // If obj == NULL, then ObjectMonitor is raw which doesn't count. + } + + Handle h(obj); + return h; +} + +bool ThreadService::set_thread_monitoring_contention(bool flag) { + MutexLocker m(Management_lock); + + bool prev = _thread_monitoring_contention_enabled; + _thread_monitoring_contention_enabled = flag; + + return prev; +} + +bool ThreadService::set_thread_cpu_time_enabled(bool flag) { + MutexLocker m(Management_lock); + + bool prev = _thread_cpu_time_enabled; + _thread_cpu_time_enabled = flag; + + return prev; +} + +// GC support +void ThreadService::oops_do(OopClosure* f) { + for (ThreadDumpResult* dump = _threaddump_list; dump != NULL; dump = dump->next()) { + dump->oops_do(f); + } +} + +void ThreadService::add_thread_dump(ThreadDumpResult* dump) { + MutexLocker ml(Management_lock); + if (_threaddump_list == NULL) { + _threaddump_list = dump; + } else { + dump->set_next(_threaddump_list); + _threaddump_list = dump; + } +} + +void ThreadService::remove_thread_dump(ThreadDumpResult* dump) { + MutexLocker ml(Management_lock); + + ThreadDumpResult* prev = NULL; + bool found = false; + for (ThreadDumpResult* d = _threaddump_list; d != NULL; prev = d, d = d->next()) { + if (d == dump) { + if (prev == NULL) { + _threaddump_list = dump->next(); + } else { + prev->set_next(dump->next()); + } + found = true; + break; + } + } + assert(found, "The threaddump result to be removed must exist."); +} + +// Dump stack trace of threads specified in the given threads array. +// Returns StackTraceElement[][] each element is the stack trace of a thread in +// the corresponding entry in the given threads array +Handle ThreadService::dump_stack_traces(GrowableArray* threads, + int num_threads, + TRAPS) { + assert(num_threads > 0, "just checking"); + + ThreadDumpResult dump_result; + VM_ThreadDump op(&dump_result, + threads, + num_threads, + -1, /* entire stack */ + false, /* with locked monitors */ + false /* with locked synchronizers */); + VMThread::execute(&op); + + // Allocate the resulting StackTraceElement[][] object + + ResourceMark rm(THREAD); + klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_StackTraceElement_array(), true, CHECK_NH); + objArrayKlassHandle ik (THREAD, k); + objArrayOop r = oopFactory::new_objArray(ik(), num_threads, CHECK_NH); + objArrayHandle result_obj(THREAD, r); + + int num_snapshots = dump_result.num_snapshots(); + assert(num_snapshots == num_threads, "Must have num_threads thread snapshots"); + int i = 0; + for (ThreadSnapshot* ts = dump_result.snapshots(); ts != NULL; i++, ts = ts->next()) { + ThreadStackTrace* stacktrace = ts->get_stack_trace(); + if (stacktrace == NULL) { + // No stack trace + result_obj->obj_at_put(i, NULL); + } else { + // Construct an array of java/lang/StackTraceElement object + Handle backtrace_h = stacktrace->allocate_fill_stack_trace_element_array(CHECK_NH); + result_obj->obj_at_put(i, backtrace_h()); + } + } + + return result_obj; +} + +void ThreadService::reset_contention_count_stat(JavaThread* thread) { + ThreadStatistics* stat = thread->get_thread_stat(); + if (stat != NULL) { + stat->reset_count_stat(); + } +} + +void ThreadService::reset_contention_time_stat(JavaThread* thread) { + ThreadStatistics* stat = thread->get_thread_stat(); + if (stat != NULL) { + stat->reset_time_stat(); + } +} + +// Find deadlocks involving object monitors and concurrent locks if concurrent_locks is true +DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks) { + // This code was modified from the original Threads::find_deadlocks code. + int globalDfn = 0, thisDfn; + ObjectMonitor* waitingToLockMonitor = NULL; + oop waitingToLockBlocker = NULL; + bool blocked_on_monitor = false; + JavaThread *currentThread, *previousThread; + int num_deadlocks = 0; + + for (JavaThread* p = Threads::first(); p != NULL; p = p->next()) { + // Initialize the depth-first-number + p->set_depth_first_number(-1); + } + + DeadlockCycle* deadlocks = NULL; + DeadlockCycle* last = NULL; + DeadlockCycle* cycle = new DeadlockCycle(); + for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) { + if (jt->depth_first_number() >= 0) { + // this thread was already visited + continue; + } + + thisDfn = globalDfn; + jt->set_depth_first_number(globalDfn++); + previousThread = jt; + currentThread = jt; + + cycle->reset(); + + // When there is a deadlock, all the monitors involved in the dependency + // cycle must be contended and heavyweight. So we only care about the + // heavyweight monitor a thread is waiting to lock. + waitingToLockMonitor = (ObjectMonitor*)jt->current_pending_monitor(); + if (concurrent_locks) { + waitingToLockBlocker = jt->current_park_blocker(); + } + while (waitingToLockMonitor != NULL || waitingToLockBlocker != NULL) { + cycle->add_thread(currentThread); + if (waitingToLockMonitor != NULL) { + currentThread = Threads::owning_thread_from_monitor_owner((address)waitingToLockMonitor->owner(), + false /* no locking needed */); + } else { + if (concurrent_locks) { + if (waitingToLockBlocker->is_a(SystemDictionary::abstract_ownable_synchronizer_klass())) { + oop threadObj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(waitingToLockBlocker); + currentThread = threadObj != NULL ? java_lang_Thread::thread(threadObj) : NULL; + } else { + currentThread = NULL; + } + } + } + + if (currentThread == NULL) { + // No dependency on another thread + break; + } + if (currentThread->depth_first_number() < 0) { + // First visit to this thread + currentThread->set_depth_first_number(globalDfn++); + } else if (currentThread->depth_first_number() < thisDfn) { + // Thread already visited, and not on a (new) cycle + break; + } else if (currentThread == previousThread) { + // Self-loop, ignore + break; + } else { + // We have a (new) cycle + num_deadlocks++; + + cycle->set_deadlock(true); + + // add this cycle to the deadlocks list + if (deadlocks == NULL) { + deadlocks = cycle; + } else { + last->set_next(cycle); + } + last = cycle; + cycle = new DeadlockCycle(); + break; + } + previousThread = currentThread; + waitingToLockMonitor = (ObjectMonitor*)currentThread->current_pending_monitor(); + if (concurrent_locks) { + waitingToLockBlocker = currentThread->current_park_blocker(); + } + } + + } + + return deadlocks; +} + +ThreadDumpResult::ThreadDumpResult() : _num_threads(0), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL) { + + // Create a new ThreadDumpResult object and append to the list. + // If GC happens before this function returns, methodOop + // in the stack trace will be visited. + ThreadService::add_thread_dump(this); +} + +ThreadDumpResult::ThreadDumpResult(int num_threads) : _num_threads(num_threads), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL) { + // Create a new ThreadDumpResult object and append to the list. + // If GC happens before this function returns, oops + // will be visited. + ThreadService::add_thread_dump(this); +} + +ThreadDumpResult::~ThreadDumpResult() { + ThreadService::remove_thread_dump(this); + + // free all the ThreadSnapshot objects created during + // the VM_ThreadDump operation + ThreadSnapshot* ts = _snapshots; + while (ts != NULL) { + ThreadSnapshot* p = ts; + ts = ts->next(); + delete p; + } +} + + +void ThreadDumpResult::add_thread_snapshot(ThreadSnapshot* ts) { + assert(_num_threads == 0 || _num_snapshots < _num_threads, + "_num_snapshots must be less than _num_threads"); + _num_snapshots++; + if (_snapshots == NULL) { + _snapshots = ts; + } else { + _last->set_next(ts); + } + _last = ts; +} + +void ThreadDumpResult::oops_do(OopClosure* f) { + for (ThreadSnapshot* ts = _snapshots; ts != NULL; ts = ts->next()) { + ts->oops_do(f); + } +} + +StackFrameInfo::StackFrameInfo(javaVFrame* jvf, bool with_lock_info) { + _method = jvf->method(); + _bci = jvf->bci(); + _locked_monitors = NULL; + if (with_lock_info) { + ResourceMark rm; + GrowableArray* list = jvf->locked_monitors(); + int length = list->length(); + if (length > 0) { + _locked_monitors = new (ResourceObj::C_HEAP) GrowableArray(length, true); + for (int i = 0; i < length; i++) { + MonitorInfo* monitor = list->at(i); + assert(monitor->owner(), "This monitor must have an owning object"); + _locked_monitors->append(monitor->owner()); + } + } + } +} + +void StackFrameInfo::oops_do(OopClosure* f) { + f->do_oop((oop*) &_method); + if (_locked_monitors != NULL) { + int length = _locked_monitors->length(); + for (int i = 0; i < length; i++) { + f->do_oop((oop*) _locked_monitors->adr_at(i)); + } + } +} + +void StackFrameInfo::print_on(outputStream* st) const { + ResourceMark rm; + java_lang_Throwable::print_stack_element(st, method(), bci()); + int len = (_locked_monitors != NULL ? _locked_monitors->length() : 0); + for (int i = 0; i < len; i++) { + oop o = _locked_monitors->at(i); + instanceKlass* ik = instanceKlass::cast(o->klass()); + st->print_cr("\t- locked <" INTPTR_FORMAT "> (a %s)", (address)o, ik->external_name()); + } + +} + +// Iterate through monitor cache to find JNI locked monitors +class InflatedMonitorsClosure: public MonitorClosure { +private: + ThreadStackTrace* _stack_trace; + Thread* _thread; +public: + InflatedMonitorsClosure(Thread* t, ThreadStackTrace* st) { + _thread = t; + _stack_trace = st; + } + void do_monitor(ObjectMonitor* mid) { + if (mid->owner() == _thread) { + oop object = (oop) mid->object(); + if (!_stack_trace->is_owned_monitor_on_stack(object)) { + _stack_trace->add_jni_locked_monitor(object); + } + } + } +}; + +ThreadStackTrace::ThreadStackTrace(JavaThread* t, bool with_locked_monitors) { + _thread = t; + _frames = new (ResourceObj::C_HEAP) GrowableArray(INITIAL_ARRAY_SIZE, true); + _depth = 0; + _with_locked_monitors = with_locked_monitors; + if (_with_locked_monitors) { + _jni_locked_monitors = new (ResourceObj::C_HEAP) GrowableArray(INITIAL_ARRAY_SIZE, true); + } else { + _jni_locked_monitors = NULL; + } +} + +ThreadStackTrace::~ThreadStackTrace() { + for (int i = 0; i < _frames->length(); i++) { + delete _frames->at(i); + } + delete _frames; + if (_jni_locked_monitors != NULL) { + delete _jni_locked_monitors; + } +} + +void ThreadStackTrace::dump_stack_at_safepoint(int maxDepth) { + assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped"); + + if (_thread->has_last_Java_frame()) { + RegisterMap reg_map(_thread); + vframe* start_vf = _thread->last_java_vframe(®_map); + int count = 0; + for (vframe* f = start_vf; f; f = f->sender() ) { + if (f->is_java_frame()) { + javaVFrame* jvf = javaVFrame::cast(f); + add_stack_frame(jvf); + count++; + } else { + // Ignore non-Java frames + } + if (maxDepth > 0 && count == maxDepth) { + // Skip frames if more than maxDepth + break; + } + } + } + + if (_with_locked_monitors) { + // Iterate inflated monitors and find monitors locked by this thread + // not found in the stack + InflatedMonitorsClosure imc(_thread, this); + ObjectSynchronizer::monitors_iterate(&imc); + } +} + + +bool ThreadStackTrace::is_owned_monitor_on_stack(oop object) { + assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped"); + + bool found = false; + int num_frames = get_stack_depth(); + for (int depth = 0; depth < num_frames; depth++) { + StackFrameInfo* frame = stack_frame_at(depth); + int len = frame->num_locked_monitors(); + GrowableArray* locked_monitors = frame->locked_monitors(); + for (int j = 0; j < len; j++) { + oop monitor = locked_monitors->at(j); + assert(monitor != NULL && monitor->is_instance(), "must be a Java object"); + if (monitor == object) { + found = true; + break; + } + } + } + return found; +} + +Handle ThreadStackTrace::allocate_fill_stack_trace_element_array(TRAPS) { + klassOop k = SystemDictionary::stackTraceElement_klass(); + instanceKlassHandle ik(THREAD, k); + + // Allocate an array of java/lang/StackTraceElement object + objArrayOop ste = oopFactory::new_objArray(ik(), _depth, CHECK_NH); + objArrayHandle backtrace(THREAD, ste); + for (int j = 0; j < _depth; j++) { + StackFrameInfo* frame = _frames->at(j); + methodHandle mh(THREAD, frame->method()); + oop element = java_lang_StackTraceElement::create(mh, frame->bci(), CHECK_NH); + backtrace->obj_at_put(j, element); + } + return backtrace; +} + +void ThreadStackTrace::add_stack_frame(javaVFrame* jvf) { + StackFrameInfo* frame = new StackFrameInfo(jvf, _with_locked_monitors); + _frames->append(frame); + _depth++; +} + +void ThreadStackTrace::oops_do(OopClosure* f) { + int length = _frames->length(); + for (int i = 0; i < length; i++) { + _frames->at(i)->oops_do(f); + } + + length = (_jni_locked_monitors != NULL ? _jni_locked_monitors->length() : 0); + for (int j = 0; j < length; j++) { + f->do_oop((oop*) _jni_locked_monitors->adr_at(j)); + } +} + +ConcurrentLocksDump::~ConcurrentLocksDump() { + if (_retain_map_on_free) { + return; + } + + for (ThreadConcurrentLocks* t = _map; t != NULL;) { + ThreadConcurrentLocks* tcl = t; + t = t->next(); + delete tcl; + } +} + +void ConcurrentLocksDump::dump_at_safepoint() { + // dump all locked concurrent locks + assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped"); + + if (JDK_Version::is_gte_jdk16x_version()) { + ResourceMark rm; + + GrowableArray* aos_objects = new GrowableArray(INITIAL_ARRAY_SIZE); + + // Find all instances of AbstractOwnableSynchronizer + HeapInspection::find_instances_at_safepoint(SystemDictionary::abstract_ownable_synchronizer_klass(), + aos_objects); + // Build a map of thread to its owned AQS locks + build_map(aos_objects); + } +} + + +// build a map of JavaThread to all its owned AbstractOwnableSynchronizer +void ConcurrentLocksDump::build_map(GrowableArray* aos_objects) { + int length = aos_objects->length(); + for (int i = 0; i < length; i++) { + oop o = aos_objects->at(i); + oop owner_thread_obj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(o); + if (owner_thread_obj != NULL) { + JavaThread* thread = java_lang_Thread::thread(owner_thread_obj); + assert(o->is_instance(), "Must be an instanceOop"); + add_lock(thread, (instanceOop) o); + } + } +} + +void ConcurrentLocksDump::add_lock(JavaThread* thread, instanceOop o) { + ThreadConcurrentLocks* tcl = thread_concurrent_locks(thread); + if (tcl != NULL) { + tcl->add_lock(o); + return; + } + + // First owned lock found for this thread + tcl = new ThreadConcurrentLocks(thread); + tcl->add_lock(o); + if (_map == NULL) { + _map = tcl; + } else { + _last->set_next(tcl); + } + _last = tcl; +} + +ThreadConcurrentLocks* ConcurrentLocksDump::thread_concurrent_locks(JavaThread* thread) { + for (ThreadConcurrentLocks* tcl = _map; tcl != NULL; tcl = tcl->next()) { + if (tcl->java_thread() == thread) { + return tcl; + } + } + return NULL; +} + +void ConcurrentLocksDump::print_locks_on(JavaThread* t, outputStream* st) { + st->print_cr(" Locked ownable synchronizers:"); + ThreadConcurrentLocks* tcl = thread_concurrent_locks(t); + GrowableArray* locks = (tcl != NULL ? tcl->owned_locks() : NULL); + if (locks == NULL || locks->is_empty()) { + st->print_cr("\t- None"); + st->cr(); + return; + } + + for (int i = 0; i < locks->length(); i++) { + instanceOop obj = locks->at(i); + instanceKlass* ik = instanceKlass::cast(obj->klass()); + st->print_cr("\t- <" INTPTR_FORMAT "> (a %s)", (address)obj, ik->external_name()); + } + st->cr(); +} + +ThreadConcurrentLocks::ThreadConcurrentLocks(JavaThread* thread) { + _thread = thread; + _owned_locks = new (ResourceObj::C_HEAP) GrowableArray(INITIAL_ARRAY_SIZE, true); + _next = NULL; +} + +ThreadConcurrentLocks::~ThreadConcurrentLocks() { + delete _owned_locks; +} + +void ThreadConcurrentLocks::add_lock(instanceOop o) { + _owned_locks->append(o); +} + +void ThreadConcurrentLocks::oops_do(OopClosure* f) { + int length = _owned_locks->length(); + for (int i = 0; i < length; i++) { + f->do_oop((oop*) _owned_locks->adr_at(i)); + } +} + +ThreadStatistics::ThreadStatistics() { + _contended_enter_count = 0; + _monitor_wait_count = 0; + _sleep_count = 0; + _class_init_recursion_count = 0; + _class_verify_recursion_count = 0; + _count_pending_reset = false; + _timer_pending_reset = false; +} + +ThreadSnapshot::ThreadSnapshot(JavaThread* thread) { + _thread = thread; + _threadObj = thread->threadObj(); + _stack_trace = NULL; + _concurrent_locks = NULL; + _next = NULL; + + ThreadStatistics* stat = thread->get_thread_stat(); + _contended_enter_ticks = stat->contended_enter_ticks(); + _contended_enter_count = stat->contended_enter_count(); + _monitor_wait_ticks = stat->monitor_wait_ticks(); + _monitor_wait_count = stat->monitor_wait_count(); + _sleep_ticks = stat->sleep_ticks(); + _sleep_count = stat->sleep_count(); + + _blocker_object = NULL; + _blocker_object_owner = NULL; + + _thread_status = java_lang_Thread::get_thread_status(_threadObj); + _is_ext_suspended = thread->is_being_ext_suspended(); + _is_in_native = (thread->thread_state() == _thread_in_native); + + if (_thread_status == java_lang_Thread::BLOCKED_ON_MONITOR_ENTER || + _thread_status == java_lang_Thread::IN_OBJECT_WAIT || + _thread_status == java_lang_Thread::IN_OBJECT_WAIT_TIMED) { + + Handle obj = ThreadService::get_current_contended_monitor(thread); + if (obj() == NULL) { + // monitor no longer exists; thread is not blocked + _thread_status = java_lang_Thread::RUNNABLE; + } else { + _blocker_object = obj(); + JavaThread* owner = ObjectSynchronizer::get_lock_owner(obj, false); + if ((owner == NULL && _thread_status == java_lang_Thread::BLOCKED_ON_MONITOR_ENTER) + || (owner != NULL && owner->is_attaching())) { + // ownership information of the monitor is not available + // (may no longer be owned or releasing to some other thread) + // make this thread in RUNNABLE state. + // And when the owner thread is in attaching state, the java thread + // is not completely initialized. For example thread name and id + // and may not be set, so hide the attaching thread. + _thread_status = java_lang_Thread::RUNNABLE; + _blocker_object = NULL; + } else if (owner != NULL) { + _blocker_object_owner = owner->threadObj(); + } + } + } + + // Support for JSR-166 locks + if (JDK_Version::supports_thread_park_blocker() && + (_thread_status == java_lang_Thread::PARKED || + _thread_status == java_lang_Thread::PARKED_TIMED)) { + + _blocker_object = thread->current_park_blocker(); + if (_blocker_object != NULL && _blocker_object->is_a(SystemDictionary::abstract_ownable_synchronizer_klass())) { + _blocker_object_owner = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(_blocker_object); + } + } +} + +ThreadSnapshot::~ThreadSnapshot() { + delete _stack_trace; + delete _concurrent_locks; +} + +void ThreadSnapshot::dump_stack_at_safepoint(int max_depth, bool with_locked_monitors) { + _stack_trace = new ThreadStackTrace(_thread, with_locked_monitors); + _stack_trace->dump_stack_at_safepoint(max_depth); +} + + +void ThreadSnapshot::oops_do(OopClosure* f) { + f->do_oop(&_threadObj); + f->do_oop(&_blocker_object); + f->do_oop(&_blocker_object_owner); + if (_stack_trace != NULL) { + _stack_trace->oops_do(f); + } + if (_concurrent_locks != NULL) { + _concurrent_locks->oops_do(f); + } +} + +DeadlockCycle::DeadlockCycle() { + _is_deadlock = false; + _threads = new (ResourceObj::C_HEAP) GrowableArray(INITIAL_ARRAY_SIZE, true); + _next = NULL; +} + +DeadlockCycle::~DeadlockCycle() { + delete _threads; +} + +void DeadlockCycle::print_on(outputStream* st) const { + st->cr(); + st->print_cr("Found one Java-level deadlock:"); + st->print("============================="); + + JavaThread* currentThread; + ObjectMonitor* waitingToLockMonitor; + oop waitingToLockBlocker; + int len = _threads->length(); + for (int i = 0; i < len; i++) { + currentThread = _threads->at(i); + waitingToLockMonitor = (ObjectMonitor*)currentThread->current_pending_monitor(); + waitingToLockBlocker = currentThread->current_park_blocker(); + st->cr(); + st->print_cr("\"%s\":", currentThread->get_thread_name()); + const char* owner_desc = ",\n which is held by"; + if (waitingToLockMonitor != NULL) { + st->print(" waiting to lock monitor " INTPTR_FORMAT, waitingToLockMonitor); + oop obj = (oop)waitingToLockMonitor->object(); + if (obj != NULL) { + st->print(" (object "INTPTR_FORMAT ", a %s)", (address)obj, + (instanceKlass::cast(obj->klass()))->external_name()); + + if (!currentThread->current_pending_monitor_is_from_java()) { + owner_desc = "\n in JNI, which is held by"; + } + } else { + // No Java object associated - a JVMTI raw monitor + owner_desc = " (JVMTI raw monitor),\n which is held by"; + } + currentThread = Threads::owning_thread_from_monitor_owner( + (address)waitingToLockMonitor->owner(), false /* no locking needed */); + } else { + st->print(" waiting for ownable synchronizer " INTPTR_FORMAT ", (a %s)", + (address)waitingToLockBlocker, + (instanceKlass::cast(waitingToLockBlocker->klass()))->external_name()); + assert(waitingToLockBlocker->is_a(SystemDictionary::abstract_ownable_synchronizer_klass()), + "Must be an AbstractOwnableSynchronizer"); + oop ownerObj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(waitingToLockBlocker); + currentThread = java_lang_Thread::thread(ownerObj); + } + st->print("%s \"%s\"", owner_desc, currentThread->get_thread_name()); + } + + st->cr(); + st->cr(); + + // Print stack traces + bool oldJavaMonitorsInStackTrace = JavaMonitorsInStackTrace; + JavaMonitorsInStackTrace = true; + st->print_cr("Java stack information for the threads listed above:"); + st->print_cr("==================================================="); + for (int j = 0; j < len; j++) { + currentThread = _threads->at(j); + st->print_cr("\"%s\":", currentThread->get_thread_name()); + currentThread->print_stack_on(st); + } + JavaMonitorsInStackTrace = oldJavaMonitorsInStackTrace; +} + +ThreadsListEnumerator::ThreadsListEnumerator(Thread* cur_thread, + bool include_jvmti_agent_threads, + bool include_jni_attaching_threads) { + assert(cur_thread == Thread::current(), "Check current thread"); + + int init_size = ThreadService::get_live_thread_count(); + _threads_array = new GrowableArray(init_size); + + MutexLockerEx ml(Threads_lock); + + for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) { + // skips JavaThreads in the process of exiting + // and also skips VM internal JavaThreads + // Threads in _thread_new or _thread_new_trans state are included. + // i.e. threads have been started but not yet running. + if (jt->threadObj() == NULL || + jt->is_exiting() || + !java_lang_Thread::is_alive(jt->threadObj()) || + jt->is_hidden_from_external_view()) { + continue; + } + + // skip agent threads + if (!include_jvmti_agent_threads && jt->is_jvmti_agent_thread()) { + continue; + } + + // skip jni threads in the process of attaching + if (!include_jni_attaching_threads && jt->is_attaching()) { + continue; + } + + instanceHandle h(cur_thread, (instanceOop) jt->threadObj()); + _threads_array->append(h); + } +} diff --git a/hotspot/src/share/vm/services/threadService.hpp b/hotspot/src/share/vm/services/threadService.hpp new file mode 100644 index 00000000000..291a8eebb0e --- /dev/null +++ b/hotspot/src/share/vm/services/threadService.hpp @@ -0,0 +1,566 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class OopClosure; +class ThreadDumpResult; +class ThreadStackTrace; +class ThreadSnapshot; +class StackFrameInfo; +class ThreadConcurrentLocks; +class DeadlockCycle; + +// VM monitoring and management support for the thread and +// synchronization subsystem +// +// Thread contention monitoring is disabled by default. +// When enabled, the VM will begin measuring the accumulated +// elapsed time a thread blocked on synchronization. +// +class ThreadService : public AllStatic { +private: + // These counters could be moved to Threads class + static PerfCounter* _total_threads_count; + static PerfVariable* _live_threads_count; + static PerfVariable* _peak_threads_count; + static PerfVariable* _daemon_threads_count; + + // These 2 counters are atomically incremented once the thread is exiting. + // They will be atomically decremented when ThreadService::remove_thread is called. + static volatile int _exiting_threads_count; + static volatile int _exiting_daemon_threads_count; + + static bool _thread_monitoring_contention_enabled; + static bool _thread_cpu_time_enabled; + + // Need to keep the list of thread dump result that + // keep references to methodOop since thread dump can be + // requested by multiple threads concurrently. + static ThreadDumpResult* _threaddump_list; + +public: + static void init(); + static void add_thread(JavaThread* thread, bool daemon); + static void remove_thread(JavaThread* thread, bool daemon); + static void current_thread_exiting(JavaThread* jt); + + static bool set_thread_monitoring_contention(bool flag); + static bool is_thread_monitoring_contention() { return _thread_monitoring_contention_enabled; } + + static bool set_thread_cpu_time_enabled(bool flag); + static bool is_thread_cpu_time_enabled() { return _thread_cpu_time_enabled; } + + static jlong get_total_thread_count() { return _total_threads_count->get_value(); } + static jlong get_peak_thread_count() { return _peak_threads_count->get_value(); } + static jlong get_live_thread_count() { return _live_threads_count->get_value() - _exiting_threads_count; } + static jlong get_daemon_thread_count() { return _daemon_threads_count->get_value() - _exiting_daemon_threads_count; } + + static int exiting_threads_count() { return _exiting_threads_count; } + static int exiting_daemon_threads_count() { return _exiting_daemon_threads_count; } + + // Support for thread dump + static void add_thread_dump(ThreadDumpResult* dump); + static void remove_thread_dump(ThreadDumpResult* dump); + + static Handle get_current_contended_monitor(JavaThread* thread); + + // This function is called by JVM_DumpThreads. + static Handle dump_stack_traces(GrowableArray* threads, + int num_threads, TRAPS); + + static void reset_peak_thread_count(); + static void reset_contention_count_stat(JavaThread* thread); + static void reset_contention_time_stat(JavaThread* thread); + + static DeadlockCycle* find_deadlocks_at_safepoint(bool object_monitors_only); + + // GC support + static void oops_do(OopClosure* f); +}; + +// Per-thread Statistics for synchronization +class ThreadStatistics : public CHeapObj { +private: + // The following contention statistics are only updated by + // the thread owning these statistics when contention occurs. + + jlong _contended_enter_count; + elapsedTimer _contended_enter_timer; + jlong _monitor_wait_count; + elapsedTimer _monitor_wait_timer; + jlong _sleep_count; + elapsedTimer _sleep_timer; + + + // These two reset flags are set to true when another thread + // requests to reset the statistics. The actual statistics + // are reset when the thread contention occurs and attempts + // to update the statistics. + bool _count_pending_reset; + bool _timer_pending_reset; + + // Keep accurate times for potentially recursive class operations + int _class_init_recursion_count; + int _class_verify_recursion_count; + int _class_link_recursion_count; + + // utility functions + void check_and_reset_count() { + if (!_count_pending_reset) return; + _contended_enter_count = 0; + _monitor_wait_count = 0; + _sleep_count = 0; + _count_pending_reset = 0; + } + void check_and_reset_timer() { + if (!_timer_pending_reset) return; + _contended_enter_timer.reset(); + _monitor_wait_timer.reset(); + _sleep_timer.reset(); + _timer_pending_reset = 0; + } + +public: + ThreadStatistics(); + + jlong contended_enter_count() { return (_count_pending_reset ? 0 : _contended_enter_count); } + jlong contended_enter_ticks() { return (_timer_pending_reset ? 0 : _contended_enter_timer.active_ticks()); } + jlong monitor_wait_count() { return (_count_pending_reset ? 0 : _monitor_wait_count); } + jlong monitor_wait_ticks() { return (_timer_pending_reset ? 0 : _monitor_wait_timer.active_ticks()); } + jlong sleep_count() { return (_count_pending_reset ? 0 : _sleep_count); } + jlong sleep_ticks() { return (_timer_pending_reset ? 0 : _sleep_timer.active_ticks()); } + + void monitor_wait() { check_and_reset_count(); _monitor_wait_count++; } + void monitor_wait_begin() { check_and_reset_timer(); _monitor_wait_timer.start(); } + void monitor_wait_end() { _monitor_wait_timer.stop(); check_and_reset_timer(); } + + void thread_sleep() { check_and_reset_count(); _sleep_count++; } + void thread_sleep_begin() { check_and_reset_timer(); _sleep_timer.start(); } + void thread_sleep_end() { _sleep_timer.stop(); check_and_reset_timer(); } + + void contended_enter() { check_and_reset_count(); _contended_enter_count++; } + void contended_enter_begin() { check_and_reset_timer(); _contended_enter_timer.start(); } + void contended_enter_end() { _contended_enter_timer.stop(); check_and_reset_timer(); } + + void reset_count_stat() { _count_pending_reset = true; } + void reset_time_stat() { _timer_pending_reset = true; } + + int* class_init_recursion_count_addr() { return &_class_init_recursion_count; } + int* class_verify_recursion_count_addr() { return &_class_verify_recursion_count; } + int* class_link_recursion_count_addr() { return &_class_link_recursion_count; } +}; + +// Thread snapshot to represent the thread state and statistics +class ThreadSnapshot : public CHeapObj { +private: + JavaThread* _thread; + oop _threadObj; + java_lang_Thread::ThreadStatus _thread_status; + + bool _is_ext_suspended; + bool _is_in_native; + + jlong _contended_enter_ticks; + jlong _contended_enter_count; + jlong _monitor_wait_ticks; + jlong _monitor_wait_count; + jlong _sleep_ticks; + jlong _sleep_count; + oop _blocker_object; + oop _blocker_object_owner; + + ThreadStackTrace* _stack_trace; + ThreadConcurrentLocks* _concurrent_locks; + ThreadSnapshot* _next; + +public: + // Dummy snapshot + ThreadSnapshot() : _thread(NULL), _threadObj(NULL), _stack_trace(NULL), _concurrent_locks(NULL), _next(NULL), + _blocker_object(NULL), _blocker_object_owner(NULL) {}; + ThreadSnapshot(JavaThread* thread); + ~ThreadSnapshot(); + + java_lang_Thread::ThreadStatus thread_status() { return _thread_status; } + + oop threadObj() const { return _threadObj; } + + void set_next(ThreadSnapshot* n) { _next = n; } + + bool is_ext_suspended() { return _is_ext_suspended; } + bool is_in_native() { return _is_in_native; } + + jlong contended_enter_count() { return _contended_enter_count; } + jlong contended_enter_ticks() { return _contended_enter_ticks; } + jlong monitor_wait_count() { return _monitor_wait_count; } + jlong monitor_wait_ticks() { return _monitor_wait_ticks; } + jlong sleep_count() { return _sleep_count; } + jlong sleep_ticks() { return _sleep_ticks; } + + + oop blocker_object() { return _blocker_object; } + oop blocker_object_owner() { return _blocker_object_owner; } + + ThreadSnapshot* next() const { return _next; } + ThreadStackTrace* get_stack_trace() { return _stack_trace; } + ThreadConcurrentLocks* get_concurrent_locks() { return _concurrent_locks; } + + void dump_stack_at_safepoint(int max_depth, bool with_locked_monitors); + void set_concurrent_locks(ThreadConcurrentLocks* l) { _concurrent_locks = l; } + void oops_do(OopClosure* f); +}; + +class ThreadStackTrace : public CHeapObj { + private: + JavaThread* _thread; + int _depth; // number of stack frames added + bool _with_locked_monitors; + GrowableArray* _frames; + GrowableArray* _jni_locked_monitors; + + public: + + ThreadStackTrace(JavaThread* thread, bool with_locked_monitors); + ~ThreadStackTrace(); + + StackFrameInfo* stack_frame_at(int i) { return _frames->at(i); } + int get_stack_depth() { return _depth; } + + void add_stack_frame(javaVFrame* jvf); + void dump_stack_at_safepoint(int max_depth); + Handle allocate_fill_stack_trace_element_array(TRAPS); + void oops_do(OopClosure* f); + GrowableArray* jni_locked_monitors() { return _jni_locked_monitors; } + int num_jni_locked_monitors() { return (_jni_locked_monitors != NULL ? _jni_locked_monitors->length() : 0); } + + bool is_owned_monitor_on_stack(oop object); + void add_jni_locked_monitor(oop object) { _jni_locked_monitors->append(object); } +}; + +// StackFrameInfo for keeping methodOop and bci during +// stack walking for later construction of StackTraceElement[] +// Java instances +class StackFrameInfo : public CHeapObj { + private: + methodOop _method; + int _bci; + GrowableArray* _locked_monitors; // list of object monitors locked by this frame + + public: + + StackFrameInfo(javaVFrame* jvf, bool with_locked_monitors); + ~StackFrameInfo() { + if (_locked_monitors != NULL) { + delete _locked_monitors; + } + }; + methodOop method() const { return _method; } + int bci() const { return _bci; } + void oops_do(OopClosure* f); + + int num_locked_monitors() { return (_locked_monitors != NULL ? _locked_monitors->length() : 0); } + GrowableArray* locked_monitors() { return _locked_monitors; } + + void print_on(outputStream* st) const; +}; + +class ThreadConcurrentLocks : public CHeapObj { +private: + GrowableArray* _owned_locks; + ThreadConcurrentLocks* _next; + JavaThread* _thread; + public: + ThreadConcurrentLocks(JavaThread* thread); + ~ThreadConcurrentLocks(); + + void add_lock(instanceOop o); + void set_next(ThreadConcurrentLocks* n) { _next = n; } + ThreadConcurrentLocks* next() { return _next; } + JavaThread* java_thread() { return _thread; } + GrowableArray* owned_locks() { return _owned_locks; } + void oops_do(OopClosure* f); +}; + +class ConcurrentLocksDump : public StackObj { + private: + ThreadConcurrentLocks* _map; + ThreadConcurrentLocks* _last; // Last ThreadConcurrentLocks in the map + bool _retain_map_on_free; + + void build_map(GrowableArray* aos_objects); + void add_lock(JavaThread* thread, instanceOop o); + + public: + ConcurrentLocksDump(bool retain_map_on_free) : _map(NULL), _last(NULL), _retain_map_on_free(retain_map_on_free) {}; + ConcurrentLocksDump() : _map(NULL), _last(NULL), _retain_map_on_free(false) {}; + ~ConcurrentLocksDump(); + + void dump_at_safepoint(); + ThreadConcurrentLocks* thread_concurrent_locks(JavaThread* thread); + void print_locks_on(JavaThread* t, outputStream* st); +}; + +class ThreadDumpResult : public StackObj { + private: + int _num_threads; + int _num_snapshots; + ThreadSnapshot* _snapshots; + ThreadSnapshot* _last; + ThreadDumpResult* _next; + public: + ThreadDumpResult(); + ThreadDumpResult(int num_threads); + ~ThreadDumpResult(); + + void add_thread_snapshot(ThreadSnapshot* ts); + void set_next(ThreadDumpResult* next) { _next = next; } + ThreadDumpResult* next() { return _next; } + int num_threads() { return _num_threads; } + int num_snapshots() { return _num_snapshots; } + ThreadSnapshot* snapshots() { return _snapshots; } + void oops_do(OopClosure* f); +}; + +class DeadlockCycle : public CHeapObj { + private: + bool _is_deadlock; + GrowableArray* _threads; + DeadlockCycle* _next; + public: + DeadlockCycle(); + ~DeadlockCycle(); + + DeadlockCycle* next() { return _next; } + void set_next(DeadlockCycle* d) { _next = d; } + void add_thread(JavaThread* t) { _threads->append(t); } + void reset() { _is_deadlock = false; _threads->clear(); } + void set_deadlock(bool value) { _is_deadlock = value; } + bool is_deadlock() { return _is_deadlock; } + int num_threads() { return _threads->length(); } + GrowableArray* threads() { return _threads; } + void print_on(outputStream* st) const; +}; + +// Utility class to get list of java threads. +class ThreadsListEnumerator : public StackObj { +private: + GrowableArray* _threads_array; +public: + ThreadsListEnumerator(Thread* cur_thread, + bool include_jvmti_agent_threads = false, + bool include_jni_attaching_threads = true); + int num_threads() { return _threads_array->length(); } + instanceHandle get_threadObj(int index) { return _threads_array->at(index); } +}; + + +// abstract utility class to set new thread states, and restore previous after the block exits +class JavaThreadStatusChanger : public StackObj { + private: + java_lang_Thread::ThreadStatus _old_state; + JavaThread* _java_thread; + bool _is_alive; + + void save_old_state(JavaThread* java_thread) { + _java_thread = java_thread; + _is_alive = is_alive(java_thread); + if (is_alive()) { + _old_state = java_lang_Thread::get_thread_status(_java_thread->threadObj()); + } + } + + public: + static void set_thread_status(JavaThread* java_thread, + java_lang_Thread::ThreadStatus state) { + java_lang_Thread::set_thread_status(java_thread->threadObj(), state); + } + + void set_thread_status(java_lang_Thread::ThreadStatus state) { + if (is_alive()) { + set_thread_status(_java_thread, state); + } + } + + JavaThreadStatusChanger(JavaThread* java_thread, + java_lang_Thread::ThreadStatus state) { + save_old_state(java_thread); + set_thread_status(state); + } + + JavaThreadStatusChanger(JavaThread* java_thread) { + save_old_state(java_thread); + } + + ~JavaThreadStatusChanger() { + set_thread_status(_old_state); + } + + static bool is_alive(JavaThread* java_thread) { + return java_thread != NULL && java_thread->threadObj() != NULL; + } + + bool is_alive() { + return _is_alive; + } +}; + +// Change status to waiting on an object (timed or indefinite) +class JavaThreadInObjectWaitState : public JavaThreadStatusChanger { + private: + ThreadStatistics* _stat; + bool _active; + + public: + JavaThreadInObjectWaitState(JavaThread *java_thread, bool timed) : + JavaThreadStatusChanger(java_thread, + timed ? java_lang_Thread::IN_OBJECT_WAIT_TIMED : java_lang_Thread::IN_OBJECT_WAIT) { + if (is_alive()) { + _stat = java_thread->get_thread_stat(); + _active = ThreadService::is_thread_monitoring_contention(); + _stat->monitor_wait(); + if (_active) { + _stat->monitor_wait_begin(); + } + } else { + _active = false; + } + } + + ~JavaThreadInObjectWaitState() { + if (_active) { + _stat->monitor_wait_end(); + } + } +}; + +// Change status to parked (timed or indefinite) +class JavaThreadParkedState : public JavaThreadStatusChanger { + private: + ThreadStatistics* _stat; + bool _active; + + public: + JavaThreadParkedState(JavaThread *java_thread, bool timed) : + JavaThreadStatusChanger(java_thread, + timed ? java_lang_Thread::PARKED_TIMED : java_lang_Thread::PARKED) { + if (is_alive()) { + _stat = java_thread->get_thread_stat(); + _active = ThreadService::is_thread_monitoring_contention(); + _stat->monitor_wait(); + if (_active) { + _stat->monitor_wait_begin(); + } + } else { + _active = false; + } + } + + ~JavaThreadParkedState() { + if (_active) { + _stat->monitor_wait_end(); + } + } +}; + +// Change status to blocked on (re-)entering a synchronization block +class JavaThreadBlockedOnMonitorEnterState : public JavaThreadStatusChanger { + private: + ThreadStatistics* _stat; + bool _active; + + static bool contended_enter_begin(JavaThread *java_thread) { + set_thread_status(java_thread, java_lang_Thread::BLOCKED_ON_MONITOR_ENTER); + ThreadStatistics* stat = java_thread->get_thread_stat(); + stat->contended_enter(); + bool active = ThreadService::is_thread_monitoring_contention(); + if (active) { + stat->contended_enter_begin(); + } + return active; + } + + public: + // java_thread is waiting thread being blocked on monitor reenter. + // Current thread is the notifying thread which holds the monitor. + static bool wait_reenter_begin(JavaThread *java_thread, ObjectMonitor *obj_m) { + assert((java_thread != NULL), "Java thread should not be null here"); + bool active = false; + if (is_alive(java_thread) && ServiceUtil::visible_oop((oop)obj_m->object())) { + active = contended_enter_begin(java_thread); + } + return active; + } + + static void wait_reenter_end(JavaThread *java_thread, bool active) { + if (active) { + java_thread->get_thread_stat()->contended_enter_end(); + } + set_thread_status(java_thread, java_lang_Thread::RUNNABLE); + } + + JavaThreadBlockedOnMonitorEnterState(JavaThread *java_thread, ObjectMonitor *obj_m) : + JavaThreadStatusChanger(java_thread) { + assert((java_thread != NULL), "Java thread should not be null here"); + // Change thread status and collect contended enter stats for monitor contended + // enter done for external java world objects and it is contended. All other cases + // like for vm internal objects and for external objects which are not contended + // thread status is not changed and contended enter stat is not collected. + _active = false; + if (is_alive() && ServiceUtil::visible_oop((oop)obj_m->object()) && obj_m->contentions() > 0) { + _stat = java_thread->get_thread_stat(); + _active = contended_enter_begin(java_thread); + } + } + + ~JavaThreadBlockedOnMonitorEnterState() { + if (_active) { + _stat->contended_enter_end(); + } + } +}; + +// Change status to sleeping +class JavaThreadSleepState : public JavaThreadStatusChanger { + private: + ThreadStatistics* _stat; + bool _active; + public: + JavaThreadSleepState(JavaThread *java_thread) : + JavaThreadStatusChanger(java_thread, java_lang_Thread::SLEEPING) { + if (is_alive()) { + _stat = java_thread->get_thread_stat(); + _active = ThreadService::is_thread_monitoring_contention(); + _stat->thread_sleep(); + if (_active) { + _stat->thread_sleep_begin(); + } + } else { + _active = false; + } + } + + ~JavaThreadSleepState() { + if (_active) { + _stat->thread_sleep_end(); + } + } +}; diff --git a/hotspot/src/share/vm/utilities/accessFlags.cpp b/hotspot/src/share/vm/utilities/accessFlags.cpp new file mode 100644 index 00000000000..3ad8f1f0062 --- /dev/null +++ b/hotspot/src/share/vm/utilities/accessFlags.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_accessFlags.cpp.incl" + + +void AccessFlags::atomic_set_bits(jint bits) { + // Atomically update the flags with the bits given + jint old_flags, new_flags, f; + do { + old_flags = _flags; + new_flags = old_flags | bits; + f = Atomic::cmpxchg(new_flags, &_flags, old_flags); + } while(f != old_flags); +} + +void AccessFlags::atomic_clear_bits(jint bits) { + // Atomically update the flags with the bits given + jint old_flags, new_flags, f; + do { + old_flags = _flags; + new_flags = old_flags & ~bits; + f = Atomic::cmpxchg(new_flags, &_flags, old_flags); + } while(f != old_flags); +} + +#ifndef PRODUCT + +void AccessFlags::print_on(outputStream* st) const { + if (is_public ()) st->print("public " ); + if (is_private ()) st->print("private " ); + if (is_protected ()) st->print("protected " ); + if (is_static ()) st->print("static " ); + if (is_final ()) st->print("final " ); + if (is_synchronized()) st->print("synchronized "); + if (is_volatile ()) st->print("volatile " ); + if (is_transient ()) st->print("transient " ); + if (is_native ()) st->print("native " ); + if (is_interface ()) st->print("interface " ); + if (is_abstract ()) st->print("abstract " ); + if (is_strict ()) st->print("strict " ); + if (is_synthetic ()) st->print("synthetic " ); + if (is_old ()) st->print("{old} " ); + if (is_obsolete ()) st->print("{obsolete} " ); +} + +#endif + +void accessFlags_init() { + assert(sizeof(AccessFlags) == sizeof(jint), "just checking size of flags"); +} diff --git a/hotspot/src/share/vm/utilities/accessFlags.hpp b/hotspot/src/share/vm/utilities/accessFlags.hpp new file mode 100644 index 00000000000..4562be6208f --- /dev/null +++ b/hotspot/src/share/vm/utilities/accessFlags.hpp @@ -0,0 +1,204 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// AccessFlags is an abstraction over Java access flags. + + +enum { + // See jvm.h for shared JVM_ACC_XXX access flags + + // HotSpot-specific access flags + + // flags actually put in .class file + JVM_ACC_WRITTEN_FLAGS = 0x00007FFF, + + // methodOop flags + JVM_ACC_MONITOR_MATCH = 0x10000000, // True if we know that monitorenter/monitorexit bytecodes match + JVM_ACC_HAS_MONITOR_BYTECODES = 0x20000000, // Method contains monitorenter/monitorexit bytecodes + JVM_ACC_HAS_LOOPS = 0x40000000, // Method has loops + JVM_ACC_LOOPS_FLAG_INIT = (int)0x80000000,// The loop flag has been initialized + JVM_ACC_QUEUED = 0x01000000, // Queued for compilation + JVM_ACC_NOT_TIER1_COMPILABLE = 0x04000000, + JVM_ACC_NOT_OSR_COMPILABLE = 0x08000000, + JVM_ACC_HAS_LINE_NUMBER_TABLE = 0x00100000, + JVM_ACC_HAS_CHECKED_EXCEPTIONS = 0x00400000, + JVM_ACC_HAS_JSRS = 0x00800000, + JVM_ACC_IS_OLD = 0x00010000, // RedefineClasses() has replaced this method + JVM_ACC_IS_OBSOLETE = 0x00020000, // RedefineClasses() has made method obsolete + JVM_ACC_IS_PREFIXED_NATIVE = 0x00040000, // JVMTI has prefixed this native method + + // klassOop flags + JVM_ACC_HAS_MIRANDA_METHODS = 0x10000000, // True if this class has miranda methods in it's vtable + JVM_ACC_HAS_VANILLA_CONSTRUCTOR = 0x20000000, // True if klass has a vanilla default constructor + JVM_ACC_HAS_FINALIZER = 0x40000000, // True if klass has a non-empty finalize() method + JVM_ACC_IS_CLONEABLE = (int)0x80000000,// True if klass supports the Clonable interface + JVM_ACC_HAS_FINAL_METHOD = 0x01000000, // True if klass has final method + + // klassOop and methodOop flags + JVM_ACC_HAS_LOCAL_VARIABLE_TABLE= 0x00200000, + + JVM_ACC_PROMOTED_FLAGS = 0x00200000, // flags promoted from methods to the holding klass + + // field flags + // Note: these flags must be defined in the low order 16 bits because + // instanceKlass only stores a ushort worth of information from the + // AccessFlags value. + // These bits must not conflict with any other field-related access flags + // (e.g., ACC_ENUM). + // Note that the class-related ACC_ANNOTATION bit conflicts with these flags. + JVM_ACC_FIELD_ACCESS_WATCHED = 0x00002000, // field access is watched by JVMTI + JVM_ACC_FIELD_MODIFICATION_WATCHED = 0x00008000, // field modification is watched by JVMTI + + // flags accepted by set_field_flags() + JVM_ACC_FIELD_FLAGS = 0x00008000 | JVM_ACC_WRITTEN_FLAGS +}; + + +class AccessFlags VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + jint _flags; + + public: + // Java access flags + bool is_public () const { return (_flags & JVM_ACC_PUBLIC ) != 0; } + bool is_private () const { return (_flags & JVM_ACC_PRIVATE ) != 0; } + bool is_protected () const { return (_flags & JVM_ACC_PROTECTED ) != 0; } + bool is_static () const { return (_flags & JVM_ACC_STATIC ) != 0; } + bool is_final () const { return (_flags & JVM_ACC_FINAL ) != 0; } + bool is_synchronized() const { return (_flags & JVM_ACC_SYNCHRONIZED) != 0; } + bool is_super () const { return (_flags & JVM_ACC_SUPER ) != 0; } + bool is_volatile () const { return (_flags & JVM_ACC_VOLATILE ) != 0; } + bool is_transient () const { return (_flags & JVM_ACC_TRANSIENT ) != 0; } + bool is_native () const { return (_flags & JVM_ACC_NATIVE ) != 0; } + bool is_interface () const { return (_flags & JVM_ACC_INTERFACE ) != 0; } + bool is_abstract () const { return (_flags & JVM_ACC_ABSTRACT ) != 0; } + bool is_strict () const { return (_flags & JVM_ACC_STRICT ) != 0; } + + // Attribute flags + bool is_synthetic () const { return (_flags & JVM_ACC_SYNTHETIC ) != 0; } + + // methodOop flags + bool is_monitor_matching () const { return (_flags & JVM_ACC_MONITOR_MATCH ) != 0; } + bool has_monitor_bytecodes () const { return (_flags & JVM_ACC_HAS_MONITOR_BYTECODES ) != 0; } + bool has_loops () const { return (_flags & JVM_ACC_HAS_LOOPS ) != 0; } + bool loops_flag_init () const { return (_flags & JVM_ACC_LOOPS_FLAG_INIT ) != 0; } + bool queued_for_compilation () const { return (_flags & JVM_ACC_QUEUED ) != 0; } + bool is_not_tier1_compilable () const { return (_flags & JVM_ACC_NOT_TIER1_COMPILABLE ) != 0; } + bool is_not_osr_compilable () const { return (_flags & JVM_ACC_NOT_OSR_COMPILABLE ) != 0; } + bool has_linenumber_table () const { return (_flags & JVM_ACC_HAS_LINE_NUMBER_TABLE ) != 0; } + bool has_checked_exceptions () const { return (_flags & JVM_ACC_HAS_CHECKED_EXCEPTIONS ) != 0; } + bool has_jsrs () const { return (_flags & JVM_ACC_HAS_JSRS ) != 0; } + bool is_old () const { return (_flags & JVM_ACC_IS_OLD ) != 0; } + bool is_obsolete () const { return (_flags & JVM_ACC_IS_OBSOLETE ) != 0; } + bool is_prefixed_native () const { return (_flags & JVM_ACC_IS_PREFIXED_NATIVE ) != 0; } + + // klassOop flags + bool has_miranda_methods () const { return (_flags & JVM_ACC_HAS_MIRANDA_METHODS ) != 0; } + bool has_vanilla_constructor () const { return (_flags & JVM_ACC_HAS_VANILLA_CONSTRUCTOR) != 0; } + bool has_finalizer () const { return (_flags & JVM_ACC_HAS_FINALIZER ) != 0; } + bool has_final_method () const { return (_flags & JVM_ACC_HAS_FINAL_METHOD ) != 0; } + bool is_cloneable () const { return (_flags & JVM_ACC_IS_CLONEABLE ) != 0; } + // klassOop and methodOop flags + bool has_localvariable_table () const { return (_flags & JVM_ACC_HAS_LOCAL_VARIABLE_TABLE) != 0; } + void set_has_localvariable_table() { atomic_set_bits(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE); } + void clear_has_localvariable_table() { atomic_clear_bits(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE); } + + // field flags + bool is_field_access_watched() const { return (_flags & JVM_ACC_FIELD_ACCESS_WATCHED) != 0; } + bool is_field_modification_watched() const + { return (_flags & JVM_ACC_FIELD_MODIFICATION_WATCHED) != 0; } + + // get .class file flags + jint get_flags () const { return (_flags & JVM_ACC_WRITTEN_FLAGS); } + + // Initialization + void add_promoted_flags(jint flags) { _flags |= (flags & JVM_ACC_PROMOTED_FLAGS); } + void set_field_flags(jint flags) { _flags = (flags & JVM_ACC_FIELD_FLAGS); } + void set_flags(jint flags) { _flags = (flags & JVM_ACC_WRITTEN_FLAGS); } + + void set_queued_for_compilation() { atomic_set_bits(JVM_ACC_QUEUED); } + void clear_queued_for_compilation() { atomic_clear_bits(JVM_ACC_QUEUED); } + + // Atomic update of flags + void atomic_set_bits(jint bits); + void atomic_clear_bits(jint bits); + + private: + friend class methodOopDesc; + friend class Klass; + friend class ClassFileParser; + // the functions below should only be called on the _access_flags inst var directly, + // otherwise they are just changing a copy of the flags + + // attribute flags + void set_is_synthetic() { atomic_set_bits(JVM_ACC_SYNTHETIC); } + + // methodOop flags + void set_monitor_matching() { atomic_set_bits(JVM_ACC_MONITOR_MATCH); } + void set_has_monitor_bytecodes() { atomic_set_bits(JVM_ACC_HAS_MONITOR_BYTECODES); } + void set_has_loops() { atomic_set_bits(JVM_ACC_HAS_LOOPS); } + void set_loops_flag_init() { atomic_set_bits(JVM_ACC_LOOPS_FLAG_INIT); } + void set_not_tier1_compilable() { atomic_set_bits(JVM_ACC_NOT_TIER1_COMPILABLE); } + void set_not_osr_compilable() { atomic_set_bits(JVM_ACC_NOT_OSR_COMPILABLE); } + void set_has_linenumber_table() { atomic_set_bits(JVM_ACC_HAS_LINE_NUMBER_TABLE); } + void set_has_checked_exceptions() { atomic_set_bits(JVM_ACC_HAS_CHECKED_EXCEPTIONS); } + void set_has_jsrs() { atomic_set_bits(JVM_ACC_HAS_JSRS); } + void set_is_old() { atomic_set_bits(JVM_ACC_IS_OLD); } + void set_is_obsolete() { atomic_set_bits(JVM_ACC_IS_OBSOLETE); } + void set_is_prefixed_native() { atomic_set_bits(JVM_ACC_IS_PREFIXED_NATIVE); } + + // klassOop flags + void set_has_vanilla_constructor() { atomic_set_bits(JVM_ACC_HAS_VANILLA_CONSTRUCTOR); } + void set_has_finalizer() { atomic_set_bits(JVM_ACC_HAS_FINALIZER); } + void set_has_final_method() { atomic_set_bits(JVM_ACC_HAS_FINAL_METHOD); } + void set_is_cloneable() { atomic_set_bits(JVM_ACC_IS_CLONEABLE); } + void set_has_miranda_methods() { atomic_set_bits(JVM_ACC_HAS_MIRANDA_METHODS); } + + public: + // field flags + void set_is_field_access_watched(const bool value) + { + if (value) { + atomic_set_bits(JVM_ACC_FIELD_ACCESS_WATCHED); + } else { + atomic_clear_bits(JVM_ACC_FIELD_ACCESS_WATCHED); + } + } + void set_is_field_modification_watched(const bool value) + { + if (value) { + atomic_set_bits(JVM_ACC_FIELD_MODIFICATION_WATCHED); + } else { + atomic_clear_bits(JVM_ACC_FIELD_MODIFICATION_WATCHED); + } + } + + // Conversion + jshort as_short() { return (jshort)_flags; } + jint as_int() { return _flags; } + + // Printing/debugging + void print_on(outputStream* st) const PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/utilities/array.cpp b/hotspot/src/share/vm/utilities/array.cpp new file mode 100644 index 00000000000..22b452330f7 --- /dev/null +++ b/hotspot/src/share/vm/utilities/array.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_array.cpp.incl" + + +#ifdef ASSERT +void ResourceArray::init_nesting() { + _nesting = Thread::current()->resource_area()->nesting(); +} +#endif + + +void ResourceArray::sort(size_t esize, ftype f) { + if (!is_empty()) qsort(_data, length(), esize, f); +} +void CHeapArray::sort(size_t esize, ftype f) { + if (!is_empty()) qsort(_data, length(), esize, f); +} + + +void ResourceArray::expand(size_t esize, int i, int& size) { + // make sure we are expanding within the original resource mark + assert( + _nesting == Thread::current()->resource_area()->nesting(), + "allocating outside original resource mark" + ); + // determine new size + if (size == 0) size = 4; // prevent endless loop + while (i >= size) size *= 2; + // allocate and initialize new data section + void* data = resource_allocate_bytes(esize * size); + memcpy(data, _data, esize * length()); + _data = data; +} + + +void CHeapArray::expand(size_t esize, int i, int& size) { + // determine new size + if (size == 0) size = 4; // prevent endless loop + while (i >= size) size *= 2; + // allocate and initialize new data section + void* data = NEW_C_HEAP_ARRAY(char*, esize * size); + memcpy(data, _data, esize * length()); + FREE_C_HEAP_ARRAY(char*, _data); + _data = data; +} + + +void ResourceArray::remove_at(size_t esize, int i) { + assert(0 <= i && i < length(), "index out of bounds"); + _length--; + void* dst = (char*)_data + i*esize; + void* src = (char*)dst + esize; + size_t cnt = (length() - i)*esize; + memmove(dst, src, cnt); +} + +void CHeapArray::remove_at(size_t esize, int i) { + assert(0 <= i && i < length(), "index out of bounds"); + _length--; + void* dst = (char*)_data + i*esize; + void* src = (char*)dst + esize; + size_t cnt = (length() - i)*esize; + memmove(dst, src, cnt); +} diff --git a/hotspot/src/share/vm/utilities/array.hpp b/hotspot/src/share/vm/utilities/array.hpp new file mode 100644 index 00000000000..1c229505fe1 --- /dev/null +++ b/hotspot/src/share/vm/utilities/array.hpp @@ -0,0 +1,264 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// correct linkage required to compile w/o warnings +// (must be on file level - cannot be local) +extern "C" { typedef int (*ftype)(const void*, const void*); } + + +class ResourceArray: public ResourceObj { + protected: + int _length; // the number of array elements + void* _data; // the array memory +#ifdef ASSERT + int _nesting; // the resource area nesting level +#endif + + // creation + ResourceArray() { + _length = 0; + _data = NULL; + DEBUG_ONLY(init_nesting();) + } + + + ResourceArray(size_t esize, int length) { + assert(length >= 0, "illegal length"); + _length = length; + _data = resource_allocate_bytes(esize * length); + DEBUG_ONLY(init_nesting();) + } + +#ifdef ASSERT + void init_nesting(); +#endif + + // helper functions + void sort (size_t esize, ftype f); // sort the array + void expand (size_t esize, int i, int& size);// expand the array to include slot i + void remove_at(size_t esize, int i); // remove the element in slot i + + public: + // standard operations + int length() const { return _length; } + bool is_empty() const { return length() == 0; } +}; + + +class CHeapArray: public CHeapObj { + protected: + int _length; // the number of array elements + void* _data; // the array memory + + // creation + CHeapArray() { + _length = 0; + _data = NULL; + } + + + CHeapArray(size_t esize, int length) { + assert(length >= 0, "illegal length"); + _length = length; + _data = (void*) NEW_C_HEAP_ARRAY(char *, esize * length); + } + +#ifdef ASSERT + void init_nesting(); +#endif + + // helper functions + void sort (size_t esize, ftype f); // sort the array + void expand (size_t esize, int i, int& size);// expand the array to include slot i + void remove_at(size_t esize, int i); // remove the element in slot i + + public: + // standard operations + int length() const { return _length; } + bool is_empty() const { return length() == 0; } +}; + +#define define_generic_array(array_name,element_type, base_class) \ + class array_name: public base_class { \ + protected: \ + typedef element_type etype; \ + enum { esize = sizeof(etype) }; \ + \ + void base_remove_at(size_t size, int i) { base_class::remove_at(size, i); } \ + \ + public: \ + /* creation */ \ + array_name() : base_class() {} \ + array_name(const int length) : base_class(esize, length) {} \ + array_name(const int length, const etype fx) : base_class(esize, length) { \ + for (int i = 0; i < length; i++) ((etype*)_data)[i] = fx; \ + } \ + \ + /* standard operations */ \ + etype& operator [] (const int i) const { \ + assert(0 <= i && i < length(), "index out of bounds"); \ + return ((etype*)_data)[i]; \ + } \ + \ + int index_of(const etype x) const { \ + int i = length(); \ + while (i-- > 0 && ((etype*)_data)[i] != x) ; \ + /* i < 0 || ((etype*)_data)_data[i] == x */ \ + return i; \ + } \ + \ + void sort(int f(etype*, etype*)) { base_class::sort(esize, (ftype)f); } \ + bool contains(const etype x) const { return index_of(x) >= 0; } \ + \ + /* deprecated operations - for compatibility with GrowableArray only */ \ + etype at(const int i) const { return (*this)[i]; } \ + void at_put(const int i, const etype x) { (*this)[i] = x; } \ + etype* adr_at(const int i) { return &(*this)[i]; } \ + int find(const etype x) { return index_of(x); } \ + }; \ + + +#define define_array(array_name,element_type) \ + define_generic_array(array_name, element_type, ResourceArray) + + +#define define_stack(stack_name,array_name) \ + class stack_name: public array_name { \ + protected: \ + int _size; \ + \ + void grow(const int i, const etype fx) { \ + assert(i >= length(), "index too small"); \ + if (i >= size()) expand(esize, i, _size); \ + for (int j = length(); j <= i; j++) ((etype*)_data)[j] = fx; \ + _length = i+1; \ + } \ + \ + public: \ + /* creation */ \ + stack_name() : array_name() { _size = 0; } \ + stack_name(const int size) : array_name(size){ _length = 0; _size = size; } \ + stack_name(const int size, const etype fx) : array_name(size, fx) { _size = size; } \ + \ + /* standard operations */ \ + int size() const { return _size; } \ + \ + void push(const etype x) { \ + if (length() >= size()) expand(esize, length(), _size); \ + ((etype*)_data)[_length++] = x; \ + } \ + \ + etype pop() { \ + assert(!is_empty(), "stack is empty"); \ + return ((etype*)_data)[--_length]; \ + } \ + \ + etype top() const { \ + assert(!is_empty(), "stack is empty"); \ + return ((etype*)_data)[length() - 1]; \ + } \ + \ + void push_all(const stack_name* stack) { \ + const int l = stack->length(); \ + for (int i = 0; i < l; i++) push(((etype*)(stack->_data))[i]); \ + } \ + \ + etype at_grow(const int i, const etype fx) { \ + if (i >= length()) grow(i, fx); \ + return ((etype*)_data)[i]; \ + } \ + \ + void at_put_grow(const int i, const etype x, const etype fx) { \ + if (i >= length()) grow(i, fx); \ + ((etype*)_data)[i] = x; \ + } \ + \ + void truncate(const int length) { \ + assert(0 <= length && length <= this->length(), "illegal length"); \ + _length = length; \ + } \ + \ + void remove_at(int i) { base_remove_at(esize, i); } \ + void remove(etype x) { remove_at(index_of(x)); } \ + \ + /* inserts the given element before the element at index i */ \ + void insert_before(const int i, const etype el) { \ + int len = length(); \ + int new_length = len + 1; \ + if (new_length >= size()) expand(esize, new_length, _size); \ + for (int j = len - 1; j >= i; j--) { \ + ((etype*)_data)[j + 1] = ((etype*)_data)[j]; \ + } \ + _length = new_length; \ + at_put(i, el); \ + } \ + \ + /* inserts contents of the given stack before the element at index i */ \ + void insert_before(const int i, const stack_name *st) { \ + if (st->length() == 0) return; \ + int len = length(); \ + int st_len = st->length(); \ + int new_length = len + st_len; \ + if (new_length >= size()) expand(esize, new_length, _size); \ + int j; \ + for (j = len - 1; j >= i; j--) { \ + ((etype*)_data)[j + st_len] = ((etype*)_data)[j]; \ + } \ + for (j = 0; j < st_len; j++) { \ + ((etype*)_data)[i + j] = ((etype*)st->_data)[j]; \ + } \ + _length = new_length; \ + } \ + \ + /* deprecated operations - for compatibility with GrowableArray only */ \ + int capacity() const { return size(); } \ + void clear() { truncate(0); } \ + void trunc_to(const int length) { truncate(length); } \ + void append(const etype x) { push(x); } \ + void appendAll(const stack_name* stack) { push_all(stack); } \ + etype last() const { return top(); } \ + }; \ + + +#define define_resource_list(element_type) \ + define_generic_array(element_type##Array, element_type, ResourceArray) \ + define_stack(element_type##List, element_type##Array) + +#define define_resource_pointer_list(element_type) \ + define_generic_array(element_type##Array, element_type *, ResourceArray) \ + define_stack(element_type##List, element_type##Array) + +#define define_c_heap_list(element_type) \ + define_generic_array(element_type##Array, element_type, CHeapArray) \ + define_stack(element_type##List, element_type##Array) + +#define define_c_heap_pointer_list(element_type) \ + define_generic_array(element_type##Array, element_type *, CHeapArray) \ + define_stack(element_type##List, element_type##Array) + + +// Arrays for basic types + +define_array(boolArray, bool) define_stack(boolStack, boolArray) +define_array(intArray , int ) define_stack(intStack , intArray ) diff --git a/hotspot/src/share/vm/utilities/bitMap.cpp b/hotspot/src/share/vm/utilities/bitMap.cpp new file mode 100644 index 00000000000..b1d466b3e29 --- /dev/null +++ b/hotspot/src/share/vm/utilities/bitMap.cpp @@ -0,0 +1,572 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_bitMap.cpp.incl" + + +BitMap::BitMap(idx_t* map, idx_t size_in_bits) { + assert(size_in_bits >= 0, "just checking"); + _map = map; + _size = size_in_bits; +} + + +BitMap::BitMap(idx_t size_in_bits) { + assert(size_in_bits >= 0, "just checking"); + _size = size_in_bits; + _map = NEW_RESOURCE_ARRAY(idx_t, size_in_words()); +} + + +void BitMap::resize(idx_t size_in_bits) { + assert(size_in_bits >= 0, "just checking"); + size_t old_size_in_words = size_in_words(); + uintptr_t* old_map = map(); + _size = size_in_bits; + size_t new_size_in_words = size_in_words(); + _map = NEW_RESOURCE_ARRAY(idx_t, new_size_in_words); + Copy::disjoint_words((HeapWord*) old_map, (HeapWord*) _map, MIN2(old_size_in_words, new_size_in_words)); + if (new_size_in_words > old_size_in_words) { + clear_range_of_words(old_size_in_words, size_in_words()); + } +} + +// Returns a bit mask for a range of bits [beg, end) within a single word. Each +// bit in the mask is 0 if the bit is in the range, 1 if not in the range. The +// returned mask can be used directly to clear the range, or inverted to set the +// range. Note: end must not be 0. +inline BitMap::idx_t +BitMap::inverted_bit_mask_for_range(idx_t beg, idx_t end) const { + assert(end != 0, "does not work when end == 0"); + assert(beg == end || word_index(beg) == word_index(end - 1), + "must be a single-word range"); + idx_t mask = bit_mask(beg) - 1; // low (right) bits + if (bit_in_word(end) != 0) { + mask |= ~(bit_mask(end) - 1); // high (left) bits + } + return mask; +} + +void BitMap::set_range_within_word(idx_t beg, idx_t end) { + // With a valid range (beg <= end), this test ensures that end != 0, as + // required by inverted_bit_mask_for_range. Also avoids an unnecessary write. + if (beg != end) { + idx_t mask = inverted_bit_mask_for_range(beg, end); + *word_addr(beg) |= ~mask; + } +} + +void BitMap::clear_range_within_word(idx_t beg, idx_t end) { + // With a valid range (beg <= end), this test ensures that end != 0, as + // required by inverted_bit_mask_for_range. Also avoids an unnecessary write. + if (beg != end) { + idx_t mask = inverted_bit_mask_for_range(beg, end); + *word_addr(beg) &= mask; + } +} + +void BitMap::par_put_range_within_word(idx_t beg, idx_t end, bool value) { + assert(value == 0 || value == 1, "0 for clear, 1 for set"); + // With a valid range (beg <= end), this test ensures that end != 0, as + // required by inverted_bit_mask_for_range. Also avoids an unnecessary write. + if (beg != end) { + intptr_t* pw = (intptr_t*)word_addr(beg); + intptr_t w = *pw; + intptr_t mr = (intptr_t)inverted_bit_mask_for_range(beg, end); + intptr_t nw = value ? (w | ~mr) : (w & mr); + while (true) { + intptr_t res = Atomic::cmpxchg_ptr(nw, pw, w); + if (res == w) break; + w = *pw; + nw = value ? (w | ~mr) : (w & mr); + } + } +} + +inline void BitMap::set_large_range_of_words(idx_t beg, idx_t end) { + memset(_map + beg, ~(unsigned char)0, (end - beg) * sizeof(uintptr_t)); +} + +inline void BitMap::clear_large_range_of_words(idx_t beg, idx_t end) { + memset(_map + beg, 0, (end - beg) * sizeof(uintptr_t)); +} + +inline BitMap::idx_t BitMap::word_index_round_up(idx_t bit) const { + idx_t bit_rounded_up = bit + (BitsPerWord - 1); + // Check for integer arithmetic overflow. + return bit_rounded_up > bit ? word_index(bit_rounded_up) : size_in_words(); +} + +void BitMap::set_range(idx_t beg, idx_t end) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + if (beg_full_word < end_full_word) { + // The range includes at least one full word. + set_range_within_word(beg, bit_index(beg_full_word)); + set_range_of_words(beg_full_word, end_full_word); + set_range_within_word(bit_index(end_full_word), end); + } else { + // The range spans at most 2 partial words. + idx_t boundary = MIN2(bit_index(beg_full_word), end); + set_range_within_word(beg, boundary); + set_range_within_word(boundary, end); + } +} + +void BitMap::clear_range(idx_t beg, idx_t end) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + if (beg_full_word < end_full_word) { + // The range includes at least one full word. + clear_range_within_word(beg, bit_index(beg_full_word)); + clear_range_of_words(beg_full_word, end_full_word); + clear_range_within_word(bit_index(end_full_word), end); + } else { + // The range spans at most 2 partial words. + idx_t boundary = MIN2(bit_index(beg_full_word), end); + clear_range_within_word(beg, boundary); + clear_range_within_word(boundary, end); + } +} + +void BitMap::set_large_range(idx_t beg, idx_t end) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + assert(end_full_word - beg_full_word >= 32, + "the range must include at least 32 bytes"); + + // The range includes at least one full word. + set_range_within_word(beg, bit_index(beg_full_word)); + set_large_range_of_words(beg_full_word, end_full_word); + set_range_within_word(bit_index(end_full_word), end); +} + +void BitMap::clear_large_range(idx_t beg, idx_t end) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + assert(end_full_word - beg_full_word >= 32, + "the range must include at least 32 bytes"); + + // The range includes at least one full word. + clear_range_within_word(beg, bit_index(beg_full_word)); + clear_large_range_of_words(beg_full_word, end_full_word); + clear_range_within_word(bit_index(end_full_word), end); +} + +void BitMap::at_put(idx_t offset, bool value) { + if (value) { + set_bit(offset); + } else { + clear_bit(offset); + } +} + +// Return true to indicate that this thread changed +// the bit, false to indicate that someone else did. +// In either case, the requested bit is in the +// requested state some time during the period that +// this thread is executing this call. More importantly, +// if no other thread is executing an action to +// change the requested bit to a state other than +// the one that this thread is trying to set it to, +// then the the bit is in the expected state +// at exit from this method. However, rather than +// make such a strong assertion here, based on +// assuming such constrained use (which though true +// today, could change in the future to service some +// funky parallel algorithm), we encourage callers +// to do such verification, as and when appropriate. +bool BitMap::par_at_put(idx_t bit, bool value) { + return value ? par_set_bit(bit) : par_clear_bit(bit); +} + +void BitMap::at_put_grow(idx_t offset, bool value) { + if (offset >= size()) { + resize(2 * MAX2(size(), offset)); + } + at_put(offset, value); +} + +void BitMap::at_put_range(idx_t start_offset, idx_t end_offset, bool value) { + if (value) { + set_range(start_offset, end_offset); + } else { + clear_range(start_offset, end_offset); + } +} + +void BitMap::par_at_put_range(idx_t beg, idx_t end, bool value) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + if (beg_full_word < end_full_word) { + // The range includes at least one full word. + par_put_range_within_word(beg, bit_index(beg_full_word), value); + if (value) { + set_range_of_words(beg_full_word, end_full_word); + } else { + clear_range_of_words(beg_full_word, end_full_word); + } + par_put_range_within_word(bit_index(end_full_word), end, value); + } else { + // The range spans at most 2 partial words. + idx_t boundary = MIN2(bit_index(beg_full_word), end); + par_put_range_within_word(beg, boundary, value); + par_put_range_within_word(boundary, end, value); + } + +} + +void BitMap::at_put_large_range(idx_t beg, idx_t end, bool value) { + if (value) { + set_large_range(beg, end); + } else { + clear_large_range(beg, end); + } +} + +void BitMap::par_at_put_large_range(idx_t beg, idx_t end, bool value) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + assert(end_full_word - beg_full_word >= 32, + "the range must include at least 32 bytes"); + + // The range includes at least one full word. + par_put_range_within_word(beg, bit_index(beg_full_word), value); + if (value) { + set_large_range_of_words(beg_full_word, end_full_word); + } else { + clear_large_range_of_words(beg_full_word, end_full_word); + } + par_put_range_within_word(bit_index(end_full_word), end, value); +} + +bool BitMap::contains(const BitMap other) const { + assert(size() == other.size(), "must have same size"); + uintptr_t* dest_map = map(); + uintptr_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size_in_words(); index++) { + uintptr_t word_union = dest_map[index] | other_map[index]; + // If this has more bits set than dest_map[index], then other is not a + // subset. + if (word_union != dest_map[index]) return false; + } + return true; +} + +bool BitMap::intersects(const BitMap other) const { + assert(size() == other.size(), "must have same size"); + uintptr_t* dest_map = map(); + uintptr_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size_in_words(); index++) { + if ((dest_map[index] & other_map[index]) != 0) return true; + } + // Otherwise, no intersection. + return false; +} + +void BitMap::set_union(BitMap other) { + assert(size() == other.size(), "must have same size"); + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size_in_words(); index++) { + dest_map[index] = dest_map[index] | other_map[index]; + } +} + + +void BitMap::set_difference(BitMap other) { + assert(size() == other.size(), "must have same size"); + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size_in_words(); index++) { + dest_map[index] = dest_map[index] & ~(other_map[index]); + } +} + + +void BitMap::set_intersection(BitMap other) { + assert(size() == other.size(), "must have same size"); + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + dest_map[index] = dest_map[index] & other_map[index]; + } +} + + +bool BitMap::set_union_with_result(BitMap other) { + assert(size() == other.size(), "must have same size"); + bool changed = false; + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + idx_t temp = map(index) | other_map[index]; + changed = changed || (temp != map(index)); + map()[index] = temp; + } + return changed; +} + + +bool BitMap::set_difference_with_result(BitMap other) { + assert(size() == other.size(), "must have same size"); + bool changed = false; + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + idx_t temp = dest_map[index] & ~(other_map[index]); + changed = changed || (temp != dest_map[index]); + dest_map[index] = temp; + } + return changed; +} + + +bool BitMap::set_intersection_with_result(BitMap other) { + assert(size() == other.size(), "must have same size"); + bool changed = false; + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + idx_t orig = dest_map[index]; + idx_t temp = orig & other_map[index]; + changed = changed || (temp != orig); + dest_map[index] = temp; + } + return changed; +} + + +void BitMap::set_from(BitMap other) { + assert(size() == other.size(), "must have same size"); + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + dest_map[index] = other_map[index]; + } +} + + +bool BitMap::is_same(BitMap other) { + assert(size() == other.size(), "must have same size"); + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + if (dest_map[index] != other_map[index]) return false; + } + return true; +} + +bool BitMap::is_full() const { + uintptr_t* word = map(); + idx_t rest = size(); + for (; rest >= (idx_t) BitsPerWord; rest -= BitsPerWord) { + if (*word != (uintptr_t) AllBits) return false; + word++; + } + return rest == 0 || (*word | ~right_n_bits((int)rest)) == (uintptr_t) AllBits; +} + + +bool BitMap::is_empty() const { + uintptr_t* word = map(); + idx_t rest = size(); + for (; rest >= (idx_t) BitsPerWord; rest -= BitsPerWord) { + if (*word != (uintptr_t) NoBits) return false; + word++; + } + return rest == 0 || (*word & right_n_bits((int)rest)) == (uintptr_t) NoBits; +} + +void BitMap::clear_large() { + clear_large_range_of_words(0, size_in_words()); +} + +// Note that if the closure itself modifies the bitmap +// then modifications in and to the left of the _bit_ being +// currently sampled will not be seen. Note also that the +// interval [leftOffset, rightOffset) is right open. +void BitMap::iterate(BitMapClosure* blk, idx_t leftOffset, idx_t rightOffset) { + verify_range(leftOffset, rightOffset); + + idx_t startIndex = word_index(leftOffset); + idx_t endIndex = MIN2(word_index(rightOffset) + 1, size_in_words()); + for (idx_t index = startIndex, offset = leftOffset; + offset < rightOffset && index < endIndex; + offset = (++index) << LogBitsPerWord) { + idx_t rest = map(index) >> (offset & (BitsPerWord - 1)); + for (; offset < rightOffset && rest != (uintptr_t)NoBits; offset++) { + if (rest & 1) { + blk->do_bit(offset); + // resample at each closure application + // (see, for instance, CMS bug 4525989) + rest = map(index) >> (offset & (BitsPerWord -1)); + // XXX debugging: remove + // The following assertion assumes that closure application + // doesn't clear bits (may not be true in general, e.g. G1). + assert(rest & 1, + "incorrect shift or closure application can clear bits?"); + } + rest = rest >> 1; + } + } +} + +BitMap::idx_t BitMap::get_next_one_offset(idx_t l_offset, + idx_t r_offset) const { + assert(l_offset <= size(), "BitMap index out of bounds"); + assert(r_offset <= size(), "BitMap index out of bounds"); + assert(l_offset <= r_offset, "l_offset > r_offset ?"); + + if (l_offset == r_offset) { + return l_offset; + } + idx_t index = word_index(l_offset); + idx_t r_index = word_index(r_offset-1) + 1; + idx_t res_offset = l_offset; + + // check bits including and to the _left_ of offset's position + idx_t pos = bit_in_word(res_offset); + idx_t res = map(index) >> pos; + if (res != (uintptr_t)NoBits) { + // find the position of the 1-bit + for (; !(res & 1); res_offset++) { + res = res >> 1; + } + assert(res_offset >= l_offset, "just checking"); + return MIN2(res_offset, r_offset); + } + // skip over all word length 0-bit runs + for (index++; index < r_index; index++) { + res = map(index); + if (res != (uintptr_t)NoBits) { + // found a 1, return the offset + for (res_offset = index << LogBitsPerWord; !(res & 1); + res_offset++) { + res = res >> 1; + } + assert(res & 1, "tautology; see loop condition"); + assert(res_offset >= l_offset, "just checking"); + return MIN2(res_offset, r_offset); + } + } + return r_offset; +} + +BitMap::idx_t BitMap::get_next_zero_offset(idx_t l_offset, + idx_t r_offset) const { + assert(l_offset <= size(), "BitMap index out of bounds"); + assert(r_offset <= size(), "BitMap index out of bounds"); + assert(l_offset <= r_offset, "l_offset > r_offset ?"); + + if (l_offset == r_offset) { + return l_offset; + } + idx_t index = word_index(l_offset); + idx_t r_index = word_index(r_offset-1) + 1; + idx_t res_offset = l_offset; + + // check bits including and to the _left_ of offset's position + idx_t pos = res_offset & (BitsPerWord - 1); + idx_t res = (map(index) >> pos) | left_n_bits((int)pos); + + if (res != (uintptr_t)AllBits) { + // find the position of the 0-bit + for (; res & 1; res_offset++) { + res = res >> 1; + } + assert(res_offset >= l_offset, "just checking"); + return MIN2(res_offset, r_offset); + } + // skip over all word length 1-bit runs + for (index++; index < r_index; index++) { + res = map(index); + if (res != (uintptr_t)AllBits) { + // found a 0, return the offset + for (res_offset = index << LogBitsPerWord; res & 1; + res_offset++) { + res = res >> 1; + } + assert(!(res & 1), "tautology; see loop condition"); + assert(res_offset >= l_offset, "just checking"); + return MIN2(res_offset, r_offset); + } + } + return r_offset; +} + +#ifndef PRODUCT + +void BitMap::print_on(outputStream* st) const { + tty->print("Bitmap(%d):", size()); + for (idx_t index = 0; index < size(); index++) { + tty->print("%c", at(index) ? '1' : '0'); + } + tty->cr(); +} + +#endif + + +BitMap2D::BitMap2D(uintptr_t* map, idx_t size_in_slots, idx_t bits_per_slot) + : _bits_per_slot(bits_per_slot) + , _map(map, size_in_slots * bits_per_slot) +{ +} + + +BitMap2D::BitMap2D(idx_t size_in_slots, idx_t bits_per_slot) + : _bits_per_slot(bits_per_slot) + , _map(size_in_slots * bits_per_slot) +{ +} diff --git a/hotspot/src/share/vm/utilities/bitMap.hpp b/hotspot/src/share/vm/utilities/bitMap.hpp new file mode 100644 index 00000000000..961a2f1b34e --- /dev/null +++ b/hotspot/src/share/vm/utilities/bitMap.hpp @@ -0,0 +1,396 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Closure for iterating over BitMaps + +class BitMapClosure VALUE_OBJ_CLASS_SPEC { + public: + // Callback when bit in map is set + virtual void do_bit(size_t offset) = 0; +}; + + +// Operations for bitmaps represented as arrays of unsigned 32- or 64-bit +// integers (uintptr_t). +// +// Bit offsets are numbered from 0 to size-1 + +class BitMap VALUE_OBJ_CLASS_SPEC { + friend class BitMap2D; + + public: + typedef size_t idx_t; // Type used for bit and word indices. + + // Hints for range sizes. + typedef enum { + unknown_range, small_range, large_range + } RangeSizeHint; + + private: + idx_t* _map; // First word in bitmap + idx_t _size; // Size of bitmap (in bits) + + // Puts the given value at the given offset, using resize() to size + // the bitmap appropriately if needed using factor-of-two expansion. + void at_put_grow(idx_t index, bool value); + + protected: + // Return the position of bit within the word that contains it (e.g., if + // bitmap words are 32 bits, return a number 0 <= n <= 31). + static idx_t bit_in_word(idx_t bit) { return bit & (BitsPerWord - 1); } + + // Return a mask that will select the specified bit, when applied to the word + // containing the bit. + static idx_t bit_mask(idx_t bit) { return (idx_t)1 << bit_in_word(bit); } + + // Return the index of the word containing the specified bit. + static idx_t word_index(idx_t bit) { return bit >> LogBitsPerWord; } + + // Return the bit number of the first bit in the specified word. + static idx_t bit_index(idx_t word) { return word << LogBitsPerWord; } + + // Return the array of bitmap words, or a specific word from it. + idx_t* map() const { return _map; } + idx_t map(idx_t word) const { return _map[word]; } + + // Return a pointer to the word containing the specified bit. + idx_t* word_addr(idx_t bit) const { return map() + word_index(bit); } + + // Set a word to a specified value or to all ones; clear a word. + void set_word (idx_t word, idx_t val) { _map[word] = val; } + void set_word (idx_t word) { set_word(word, ~(uintptr_t)0); } + void clear_word(idx_t word) { _map[word] = 0; } + + // Utilities for ranges of bits. Ranges are half-open [beg, end). + + // Ranges within a single word. + inline idx_t inverted_bit_mask_for_range(idx_t beg, idx_t end) const; + inline void set_range_within_word (idx_t beg, idx_t end); + inline void clear_range_within_word (idx_t beg, idx_t end); + inline void par_put_range_within_word (idx_t beg, idx_t end, bool value); + + // Ranges spanning entire words. + inline void set_range_of_words (idx_t beg, idx_t end); + inline void clear_range_of_words (idx_t beg, idx_t end); + inline void set_large_range_of_words (idx_t beg, idx_t end); + inline void clear_large_range_of_words (idx_t beg, idx_t end); + + // The index of the first full word in a range. + inline idx_t word_index_round_up(idx_t bit) const; + + // Verification, statistics. + void verify_index(idx_t index) const { + assert(index < _size, "BitMap index out of bounds"); + } + + void verify_range(idx_t beg_index, idx_t end_index) const { +#ifdef ASSERT + assert(beg_index <= end_index, "BitMap range error"); + // Note that [0,0) and [size,size) are both valid ranges. + if (end_index != _size) verify_index(end_index); +#endif + } + + public: + + // Constructs a bitmap with no map, and size 0. + BitMap() : _map(NULL), _size(0) {} + + // Construction + BitMap(idx_t* map, idx_t size_in_bits); + + // Allocates necessary data structure in resource area + BitMap(idx_t size_in_bits); + + void set_map(idx_t* map) { _map = map; } + void set_size(idx_t size_in_bits) { _size = size_in_bits; } + + // Allocates necessary data structure in resource area. + // Preserves state currently in bit map by copying data. + // Zeros any newly-addressable bits. + // Does not perform any frees (i.e., of current _map). + void resize(idx_t size_in_bits); + + // Accessing + idx_t size() const { return _size; } + idx_t size_in_words() const { + return word_index(size() + BitsPerWord - 1); + } + + bool at(idx_t index) const { + verify_index(index); + return (*word_addr(index) & bit_mask(index)) != 0; + } + + // Align bit index up or down to the next bitmap word boundary, or check + // alignment. + static idx_t word_align_up(idx_t bit) { + return align_size_up(bit, BitsPerWord); + } + static idx_t word_align_down(idx_t bit) { + return align_size_down(bit, BitsPerWord); + } + static bool is_word_aligned(idx_t bit) { + return word_align_up(bit) == bit; + } + + // Set or clear the specified bit. + inline void set_bit(idx_t bit); + inline void clear_bit(idx_t bit); + + // Atomically set or clear the specified bit. + inline bool par_set_bit(idx_t bit); + inline bool par_clear_bit(idx_t bit); + + // Put the given value at the given offset. The parallel version + // will CAS the value into the bitmap and is quite a bit slower. + // The parallel version also returns a value indicating if the + // calling thread was the one that changed the value of the bit. + void at_put(idx_t index, bool value); + bool par_at_put(idx_t index, bool value); + + // Update a range of bits. Ranges are half-open [beg, end). + void set_range (idx_t beg, idx_t end); + void clear_range (idx_t beg, idx_t end); + void set_large_range (idx_t beg, idx_t end); + void clear_large_range (idx_t beg, idx_t end); + void at_put_range(idx_t beg, idx_t end, bool value); + void par_at_put_range(idx_t beg, idx_t end, bool value); + void at_put_large_range(idx_t beg, idx_t end, bool value); + void par_at_put_large_range(idx_t beg, idx_t end, bool value); + + // Update a range of bits, using a hint about the size. Currently only + // inlines the predominant case of a 1-bit range. Works best when hint is a + // compile-time constant. + inline void set_range(idx_t beg, idx_t end, RangeSizeHint hint); + inline void clear_range(idx_t beg, idx_t end, RangeSizeHint hint); + inline void par_set_range(idx_t beg, idx_t end, RangeSizeHint hint); + inline void par_clear_range (idx_t beg, idx_t end, RangeSizeHint hint); + + // Clearing + void clear(); + void clear_large(); + + // Iteration support + void iterate(BitMapClosure* blk, idx_t leftIndex, idx_t rightIndex); + inline void iterate(BitMapClosure* blk) { + // call the version that takes an interval + iterate(blk, 0, size()); + } + + // Looking for 1's and 0's to the "right" + idx_t get_next_one_offset (idx_t l_index, idx_t r_index) const; + idx_t get_next_zero_offset(idx_t l_index, idx_t r_index) const; + + idx_t get_next_one_offset(idx_t offset) const { + return get_next_one_offset(offset, size()); + } + idx_t get_next_zero_offset(idx_t offset) const { + return get_next_zero_offset(offset, size()); + } + + + + // Find the next one bit in the range [beg_bit, end_bit), or return end_bit if + // no one bit is found. Equivalent to get_next_one_offset(), but inline for + // use in performance-critical code. + inline idx_t find_next_one_bit(idx_t beg_bit, idx_t end_bit) const; + + // Set operations. + void set_union(BitMap bits); + void set_difference(BitMap bits); + void set_intersection(BitMap bits); + // Returns true iff "this" is a superset of "bits". + bool contains(const BitMap bits) const; + // Returns true iff "this and "bits" have a non-empty intersection. + bool intersects(const BitMap bits) const; + + // Returns result of whether this map changed + // during the operation + bool set_union_with_result(BitMap bits); + bool set_difference_with_result(BitMap bits); + bool set_intersection_with_result(BitMap bits); + + void set_from(BitMap bits); + + bool is_same(BitMap bits); + + // Test if all bits are set or cleared + bool is_full() const; + bool is_empty() const; + + +#ifndef PRODUCT + public: + // Printing + void print_on(outputStream* st) const; +#endif +}; + +inline void BitMap::set_bit(idx_t bit) { + verify_index(bit); + *word_addr(bit) |= bit_mask(bit); +} + +inline void BitMap::clear_bit(idx_t bit) { + verify_index(bit); + *word_addr(bit) &= ~bit_mask(bit); +} + +inline void BitMap::set_range(idx_t beg, idx_t end, RangeSizeHint hint) { + if (hint == small_range && end - beg == 1) { + set_bit(beg); + } else { + if (hint == large_range) { + set_large_range(beg, end); + } else { + set_range(beg, end); + } + } +} + +inline void BitMap::clear_range(idx_t beg, idx_t end, RangeSizeHint hint) { + if (hint == small_range && end - beg == 1) { + clear_bit(beg); + } else { + if (hint == large_range) { + clear_large_range(beg, end); + } else { + clear_range(beg, end); + } + } +} + +inline void BitMap::par_set_range(idx_t beg, idx_t end, RangeSizeHint hint) { + if (hint == small_range && end - beg == 1) { + par_at_put(beg, true); + } else { + if (hint == large_range) { + par_at_put_large_range(beg, end, true); + } else { + par_at_put_range(beg, end, true); + } + } +} + + +// Convenience class wrapping BitMap which provides multiple bits per slot. +class BitMap2D VALUE_OBJ_CLASS_SPEC { + public: + typedef size_t idx_t; // Type used for bit and word indices. + + private: + BitMap _map; + idx_t _bits_per_slot; + + idx_t bit_index(idx_t slot_index, idx_t bit_within_slot_index) const { + return slot_index * _bits_per_slot + bit_within_slot_index; + } + + void verify_bit_within_slot_index(idx_t index) const { + assert(index < _bits_per_slot, "bit_within_slot index out of bounds"); + } + + public: + // Construction. bits_per_slot must be greater than 0. + BitMap2D(uintptr_t* map, idx_t size_in_slots, idx_t bits_per_slot); + + // Allocates necessary data structure in resource area. bits_per_slot must be greater than 0. + BitMap2D(idx_t size_in_slots, idx_t bits_per_slot); + + idx_t size_in_bits() { + return _map.size(); + } + + // Returns number of full slots that have been allocated + idx_t size_in_slots() { + // Round down + return _map.size() / _bits_per_slot; + } + + bool is_valid_index(idx_t slot_index, idx_t bit_within_slot_index) { + verify_bit_within_slot_index(bit_within_slot_index); + return (bit_index(slot_index, bit_within_slot_index) < size_in_bits()); + } + + bool at(idx_t slot_index, idx_t bit_within_slot_index) const { + verify_bit_within_slot_index(bit_within_slot_index); + return _map.at(bit_index(slot_index, bit_within_slot_index)); + } + + void set_bit(idx_t slot_index, idx_t bit_within_slot_index) { + verify_bit_within_slot_index(bit_within_slot_index); + _map.set_bit(bit_index(slot_index, bit_within_slot_index)); + } + + void clear_bit(idx_t slot_index, idx_t bit_within_slot_index) { + verify_bit_within_slot_index(bit_within_slot_index); + _map.clear_bit(bit_index(slot_index, bit_within_slot_index)); + } + + void at_put(idx_t slot_index, idx_t bit_within_slot_index, bool value) { + verify_bit_within_slot_index(bit_within_slot_index); + _map.at_put(bit_index(slot_index, bit_within_slot_index), value); + } + + void at_put_grow(idx_t slot_index, idx_t bit_within_slot_index, bool value) { + verify_bit_within_slot_index(bit_within_slot_index); + _map.at_put_grow(bit_index(slot_index, bit_within_slot_index), value); + } + + void clear() { + _map.clear(); + } +}; + + + +inline void BitMap::set_range_of_words(idx_t beg, idx_t end) { + uintptr_t* map = _map; + for (idx_t i = beg; i < end; ++i) map[i] = ~(uintptr_t)0; +} + + +inline void BitMap::clear_range_of_words(idx_t beg, idx_t end) { + uintptr_t* map = _map; + for (idx_t i = beg; i < end; ++i) map[i] = 0; +} + + +inline void BitMap::clear() { + clear_range_of_words(0, size_in_words()); +} + + +inline void BitMap::par_clear_range(idx_t beg, idx_t end, RangeSizeHint hint) { + if (hint == small_range && end - beg == 1) { + par_at_put(beg, false); + } else { + if (hint == large_range) { + par_at_put_large_range(beg, end, false); + } else { + par_at_put_range(beg, end, false); + } + } +} diff --git a/hotspot/src/share/vm/utilities/bitMap.inline.hpp b/hotspot/src/share/vm/utilities/bitMap.inline.hpp new file mode 100644 index 00000000000..5e656d99e63 --- /dev/null +++ b/hotspot/src/share/vm/utilities/bitMap.inline.hpp @@ -0,0 +1,105 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline bool BitMap::par_set_bit(idx_t bit) { + verify_index(bit); + volatile idx_t* const addr = word_addr(bit); + const idx_t mask = bit_mask(bit); + idx_t old_val = *addr; + + do { + const idx_t new_val = old_val | mask; + if (new_val == old_val) { + return false; // Someone else beat us to it. + } + const idx_t cur_val = (idx_t) Atomic::cmpxchg_ptr((void*) new_val, + (volatile void*) addr, + (void*) old_val); + if (cur_val == old_val) { + return true; // Success. + } + old_val = cur_val; // The value changed, try again. + } while (true); +} + +inline bool BitMap::par_clear_bit(idx_t bit) { + verify_index(bit); + volatile idx_t* const addr = word_addr(bit); + const idx_t mask = ~bit_mask(bit); + idx_t old_val = *addr; + + do { + const idx_t new_val = old_val & mask; + if (new_val == old_val) { + return false; // Someone else beat us to it. + } + const idx_t cur_val = (idx_t) Atomic::cmpxchg_ptr((void*) new_val, + (volatile void*) addr, + (void*) old_val); + if (cur_val == old_val) { + return true; // Success. + } + old_val = cur_val; // The value changed, try again. + } while (true); +} + +inline BitMap::idx_t +BitMap::find_next_one_bit(idx_t beg_bit, idx_t end_bit) const +{ + verify_range(beg_bit, end_bit); + assert(bit_in_word(end_bit) == 0, "end_bit not word-aligned"); + + if (beg_bit == end_bit) { + return beg_bit; + } + + idx_t index = word_index(beg_bit); + idx_t r_index = word_index(end_bit); + idx_t res_bit = beg_bit; + + // check bits including and to the _left_ of offset's position + idx_t res = map(index) >> bit_in_word(res_bit); + if (res != (uintptr_t) NoBits) { + // find the position of the 1-bit + for (; !(res & 1); res_bit++) { + res = res >> 1; + } + assert(res_bit >= beg_bit && res_bit < end_bit, "just checking"); + return res_bit; + } + // skip over all word length 0-bit runs + for (index++; index < r_index; index++) { + res = map(index); + if (res != (uintptr_t) NoBits) { + // found a 1, return the offset + for (res_bit = bit_index(index); !(res & 1); res_bit++) { + res = res >> 1; + } + assert(res & 1, "tautology; see loop condition"); + assert(res_bit >= beg_bit && res_bit < end_bit, "just checking"); + return res_bit; + } + } + return end_bit; +} diff --git a/hotspot/src/share/vm/utilities/constantTag.cpp b/hotspot/src/share/vm/utilities/constantTag.cpp new file mode 100644 index 00000000000..e9cbace4f54 --- /dev/null +++ b/hotspot/src/share/vm/utilities/constantTag.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_constantTag.cpp.incl" + +#ifndef PRODUCT + +void constantTag::print_on(outputStream* st) const { + switch (_tag) { + case JVM_CONSTANT_Class : + st->print("Class"); + break; + case JVM_CONSTANT_Fieldref : + st->print("Field"); + break; + case JVM_CONSTANT_Methodref : + st->print("Method"); + break; + case JVM_CONSTANT_InterfaceMethodref : + st->print("InterfaceMethod"); + break; + case JVM_CONSTANT_String : + st->print("String"); + break; + case JVM_CONSTANT_Integer : + st->print("Integer"); + break; + case JVM_CONSTANT_Float : + st->print("Float"); + break; + case JVM_CONSTANT_Long : + st->print("Long"); + break; + case JVM_CONSTANT_Double : + st->print("Double"); + break; + case JVM_CONSTANT_NameAndType : + st->print("NameAndType"); + break; + case JVM_CONSTANT_Utf8 : + st->print("Utf8"); + break; + case JVM_CONSTANT_UnresolvedClass : + st->print("Unresolved class"); + break; + case JVM_CONSTANT_ClassIndex : + st->print("Unresolved class index"); + break; + case JVM_CONSTANT_UnresolvedString : + st->print("Unresolved string"); + break; + case JVM_CONSTANT_StringIndex : + st->print("Unresolved string index"); + break; + default: + ShouldNotReachHere(); + break; + } +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/utilities/constantTag.hpp b/hotspot/src/share/vm/utilities/constantTag.hpp new file mode 100644 index 00000000000..b8a213f8b7b --- /dev/null +++ b/hotspot/src/share/vm/utilities/constantTag.hpp @@ -0,0 +1,86 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// constant tags in Java .class files + + +enum { + // See jvm.h for shared JVM_CONSTANT_XXX tags + // NOTE: replicated in SA in vm/agent/sun/jvm/hotspot/utilities/ConstantTag.java + // Hotspot specific tags + JVM_CONSTANT_Invalid = 0, // For bad value initialization + JVM_CONSTANT_InternalMin = 100, // First implementation tag (aside from bad value of course) + JVM_CONSTANT_UnresolvedClass = 100, // Temporary tag until actual use + JVM_CONSTANT_ClassIndex = 101, // Temporary tag while constructing constant pool + JVM_CONSTANT_UnresolvedString = 102, // Temporary tag until actual use + JVM_CONSTANT_StringIndex = 103, // Temporary tag while constructing constant pool + JVM_CONSTANT_UnresolvedClassInError = 104, // Error tag due to resolution error + JVM_CONSTANT_InternalMax = 104 // Last implementation tag +}; + + +class constantTag VALUE_OBJ_CLASS_SPEC { + private: + jbyte _tag; + public: + bool is_klass() const { return _tag == JVM_CONSTANT_Class; } + bool is_field () const { return _tag == JVM_CONSTANT_Fieldref; } + bool is_method() const { return _tag == JVM_CONSTANT_Methodref; } + bool is_interface_method() const { return _tag == JVM_CONSTANT_InterfaceMethodref; } + bool is_string() const { return _tag == JVM_CONSTANT_String; } + bool is_int() const { return _tag == JVM_CONSTANT_Integer; } + bool is_float() const { return _tag == JVM_CONSTANT_Float; } + bool is_long() const { return _tag == JVM_CONSTANT_Long; } + bool is_double() const { return _tag == JVM_CONSTANT_Double; } + bool is_name_and_type() const { return _tag == JVM_CONSTANT_NameAndType; } + bool is_utf8() const { return _tag == JVM_CONSTANT_Utf8; } + + bool is_invalid() const { return _tag == JVM_CONSTANT_Invalid; } + + bool is_unresolved_klass() const { + return _tag == JVM_CONSTANT_UnresolvedClass || _tag == JVM_CONSTANT_UnresolvedClassInError; + } + + bool is_unresolved_klass_in_error() const { + return _tag == JVM_CONSTANT_UnresolvedClassInError; + } + + bool is_klass_index() const { return _tag == JVM_CONSTANT_ClassIndex; } + bool is_unresolved_string() const { return _tag == JVM_CONSTANT_UnresolvedString; } + bool is_string_index() const { return _tag == JVM_CONSTANT_StringIndex; } + + bool is_klass_reference() const { return is_klass_index() || is_unresolved_klass(); } + bool is_field_or_method() const { return is_field() || is_method() || is_interface_method(); } + bool is_symbol() const { return is_utf8(); } + + constantTag(jbyte tag) { + assert((tag >= 0 && tag <= JVM_CONSTANT_NameAndType) || + (tag >= JVM_CONSTANT_InternalMin && tag <= JVM_CONSTANT_InternalMax), "Invalid constant tag"); + _tag = tag; + } + + jbyte value() { return _tag; } + + void print_on(outputStream* st) const PRODUCT_RETURN; +}; diff --git a/hotspot/src/share/vm/utilities/copy.cpp b/hotspot/src/share/vm/utilities/copy.cpp new file mode 100644 index 00000000000..1bfef0afea7 --- /dev/null +++ b/hotspot/src/share/vm/utilities/copy.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_copy.cpp.incl" + + +// Copy bytes; larger units are filled atomically if everything is aligned. +void Copy::conjoint_memory_atomic(void* from, void* to, size_t size) { + address src = (address) from; + address dst = (address) to; + uintptr_t bits = (uintptr_t) src | (uintptr_t) dst | (uintptr_t) size; + + // (Note: We could improve performance by ignoring the low bits of size, + // and putting a short cleanup loop after each bulk copy loop. + // There are plenty of other ways to make this faster also, + // and it's a slippery slope. For now, let's keep this code simple + // since the simplicity helps clarify the atomicity semantics of + // this operation. There are also CPU-specific assembly versions + // which may or may not want to include such optimizations.) + + if (bits % sizeof(jlong) == 0) { + Copy::conjoint_jlongs_atomic((jlong*) src, (jlong*) dst, size / sizeof(jlong)); + } else if (bits % sizeof(jint) == 0) { + Copy::conjoint_jints_atomic((jint*) src, (jint*) dst, size / sizeof(jint)); + } else if (bits % sizeof(jshort) == 0) { + Copy::conjoint_jshorts_atomic((jshort*) src, (jshort*) dst, size / sizeof(jshort)); + } else { + // Not aligned, so no need to be atomic. + Copy::conjoint_bytes((void*) src, (void*) dst, size); + } +} + + +// Fill bytes; larger units are filled atomically if everything is aligned. +void Copy::fill_to_memory_atomic(void* to, size_t size, jubyte value) { + address dst = (address) to; + uintptr_t bits = (uintptr_t) to | (uintptr_t) size; + if (bits % sizeof(jlong) == 0) { + jlong fill = (julong)( (jubyte)value ); // zero-extend + if (fill != 0) { + fill += fill << 8; + fill += fill << 16; + fill += fill << 32; + } + //Copy::fill_to_jlongs_atomic((jlong*) dst, size / sizeof(jlong)); + for (uintptr_t off = 0; off < size; off += sizeof(jlong)) { + *(jlong*)(dst + off) = fill; + } + } else if (bits % sizeof(jint) == 0) { + jint fill = (juint)( (jubyte)value ); // zero-extend + if (fill != 0) { + fill += fill << 8; + fill += fill << 16; + } + //Copy::fill_to_jints_atomic((jint*) dst, size / sizeof(jint)); + for (uintptr_t off = 0; off < size; off += sizeof(jint)) { + *(jint*)(dst + off) = fill; + } + } else if (bits % sizeof(jshort) == 0) { + jshort fill = (jushort)( (jubyte)value ); // zero-extend + fill += fill << 8; + //Copy::fill_to_jshorts_atomic((jshort*) dst, size / sizeof(jshort)); + for (uintptr_t off = 0; off < size; off += sizeof(jshort)) { + *(jshort*)(dst + off) = fill; + } + } else { + // Not aligned, so no need to be atomic. + Copy::fill_to_bytes(dst, size, value); + } +} diff --git a/hotspot/src/share/vm/utilities/copy.hpp b/hotspot/src/share/vm/utilities/copy.hpp new file mode 100644 index 00000000000..1bbea38ad66 --- /dev/null +++ b/hotspot/src/share/vm/utilities/copy.hpp @@ -0,0 +1,332 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Assembly code for platforms that need it. +extern "C" { + void _Copy_conjoint_words(HeapWord* from, HeapWord* to, size_t count); + void _Copy_disjoint_words(HeapWord* from, HeapWord* to, size_t count); + + void _Copy_conjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count); + void _Copy_disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count); + + void _Copy_aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count); + void _Copy_aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count); + + void _Copy_conjoint_bytes(void* from, void* to, size_t count); + + void _Copy_conjoint_bytes_atomic (void* from, void* to, size_t count); + void _Copy_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count); + void _Copy_conjoint_jints_atomic (jint* from, jint* to, size_t count); + void _Copy_conjoint_jlongs_atomic (jlong* from, jlong* to, size_t count); + void _Copy_conjoint_oops_atomic (oop* from, oop* to, size_t count); + + void _Copy_arrayof_conjoint_bytes (HeapWord* from, HeapWord* to, size_t count); + void _Copy_arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count); + void _Copy_arrayof_conjoint_jints (HeapWord* from, HeapWord* to, size_t count); + void _Copy_arrayof_conjoint_jlongs (HeapWord* from, HeapWord* to, size_t count); + void _Copy_arrayof_conjoint_oops (HeapWord* from, HeapWord* to, size_t count); +} + +class Copy : AllStatic { + public: + // Block copy methods have four attributes. We don't define all possibilities. + // alignment: aligned according to minimum Java object alignment (MinObjAlignment) + // arrayof: arraycopy operation with both operands aligned on the same + // boundary as the first element of an array of the copy unit. + // This is currently a HeapWord boundary on all platforms, except + // for long and double arrays, which are aligned on an 8-byte + // boundary on all platforms. + // arraycopy operations are implicitly atomic on each array element. + // overlap: disjoint or conjoint. + // copy unit: bytes or words (i.e., HeapWords) or oops (i.e., pointers). + // atomicity: atomic or non-atomic on the copy unit. + // + // Names are constructed thusly: + // + // [ 'aligned_' | 'arrayof_' ] + // ('conjoint_' | 'disjoint_') + // ('words' | 'bytes' | 'jshorts' | 'jints' | 'jlongs' | 'oops') + // [ '_atomic' ] + // + // Except in the arrayof case, whatever the alignment is, we assume we can copy + // whole alignment units. E.g., if MinObjAlignment is 2x word alignment, an odd + // count may copy an extra word. In the arrayof case, we are allowed to copy + // only the number of copy units specified. + + // HeapWords + + // Word-aligned words, conjoint, not atomic on each word + static void conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogHeapWordSize); + pd_conjoint_words(from, to, count); + } + + // Word-aligned words, disjoint, not atomic on each word + static void disjoint_words(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogHeapWordSize); + assert_disjoint(from, to, count); + pd_disjoint_words(from, to, count); + } + + // Word-aligned words, disjoint, atomic on each word + static void disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogHeapWordSize); + assert_disjoint(from, to, count); + pd_disjoint_words_atomic(from, to, count); + } + + // Object-aligned words, conjoint, not atomic on each word + static void aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + assert_params_aligned(from, to); + assert_non_zero(count); + pd_aligned_conjoint_words(from, to, count); + } + + // Object-aligned words, disjoint, not atomic on each word + static void aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { + assert_params_aligned(from, to); + assert_disjoint(from, to, count); + assert_non_zero(count); + pd_aligned_disjoint_words(from, to, count); + } + + // bytes, jshorts, jints, jlongs, oops + + // bytes, conjoint, not atomic on each byte (not that it matters) + static void conjoint_bytes(void* from, void* to, size_t count) { + assert_non_zero(count); + pd_conjoint_bytes(from, to, count); + } + + // bytes, conjoint, atomic on each byte (not that it matters) + static void conjoint_bytes_atomic(void* from, void* to, size_t count) { + assert_non_zero(count); + pd_conjoint_bytes(from, to, count); + } + + // jshorts, conjoint, atomic on each jshort + static void conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { + assert_params_ok(from, to, LogBytesPerShort); + assert_non_zero(count); + pd_conjoint_jshorts_atomic(from, to, count); + } + + // jints, conjoint, atomic on each jint + static void conjoint_jints_atomic(jint* from, jint* to, size_t count) { + assert_params_ok(from, to, LogBytesPerInt); + assert_non_zero(count); + pd_conjoint_jints_atomic(from, to, count); + } + + // jlongs, conjoint, atomic on each jlong + static void conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { + assert_params_ok(from, to, LogBytesPerLong); + assert_non_zero(count); + pd_conjoint_jlongs_atomic(from, to, count); + } + + // oops, conjoint, atomic on each oop + static void conjoint_oops_atomic(oop* from, oop* to, size_t count) { + assert_params_ok(from, to, LogBytesPerOop); + assert_non_zero(count); + pd_conjoint_oops_atomic(from, to, count); + } + + // Copy a span of memory. If the span is an integral number of aligned + // longs, words, or ints, copy those units atomically. + // The largest atomic transfer unit is 8 bytes, or the largest power + // of two which divides all of from, to, and size, whichever is smaller. + static void conjoint_memory_atomic(void* from, void* to, size_t size); + + // bytes, conjoint array, atomic on each byte (not that it matters) + static void arrayof_conjoint_bytes(HeapWord* from, HeapWord* to, size_t count) { + assert_non_zero(count); + pd_arrayof_conjoint_bytes(from, to, count); + } + + // jshorts, conjoint array, atomic on each jshort + static void arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogBytesPerShort); + assert_non_zero(count); + pd_arrayof_conjoint_jshorts(from, to, count); + } + + // jints, conjoint array, atomic on each jint + static void arrayof_conjoint_jints(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogBytesPerInt); + assert_non_zero(count); + pd_arrayof_conjoint_jints(from, to, count); + } + + // jlongs, conjoint array, atomic on each jlong + static void arrayof_conjoint_jlongs(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogBytesPerLong); + assert_non_zero(count); + pd_arrayof_conjoint_jlongs(from, to, count); + } + + // oops, conjoint array, atomic on each oop + static void arrayof_conjoint_oops(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogBytesPerOop); + assert_non_zero(count); + pd_arrayof_conjoint_oops(from, to, count); + } + + // Known overlap methods + + // Copy word-aligned words from higher to lower addresses, not atomic on each word + inline static void conjoint_words_to_lower(HeapWord* from, HeapWord* to, size_t byte_count) { + // byte_count is in bytes to check its alignment + assert_params_ok(from, to, LogHeapWordSize); + assert_byte_count_ok(byte_count, HeapWordSize); + + size_t count = (size_t)round_to(byte_count, HeapWordSize) >> LogHeapWordSize; + assert(to <= from || from + count <= to, "do not overwrite source data"); + + while (count-- > 0) { + *to++ = *from++; + } + } + + // Copy word-aligned words from lower to higher addresses, not atomic on each word + inline static void conjoint_words_to_higher(HeapWord* from, HeapWord* to, size_t byte_count) { + // byte_count is in bytes to check its alignment + assert_params_ok(from, to, LogHeapWordSize); + assert_byte_count_ok(byte_count, HeapWordSize); + + size_t count = (size_t)round_to(byte_count, HeapWordSize) >> LogHeapWordSize; + assert(from <= to || to + count <= from, "do not overwrite source data"); + + from += count - 1; + to += count - 1; + while (count-- > 0) { + *to-- = *from--; + } + } + + // Fill methods + + // Fill word-aligned words, not atomic on each word + // set_words + static void fill_to_words(HeapWord* to, size_t count, juint value = 0) { + assert_params_ok(to, LogHeapWordSize); + pd_fill_to_words(to, count, value); + } + + static void fill_to_aligned_words(HeapWord* to, size_t count, juint value = 0) { + assert_params_aligned(to); + pd_fill_to_aligned_words(to, count, value); + } + + // Fill bytes + static void fill_to_bytes(void* to, size_t count, jubyte value = 0) { + pd_fill_to_bytes(to, count, value); + } + + // Fill a span of memory. If the span is an integral number of aligned + // longs, words, or ints, store to those units atomically. + // The largest atomic transfer unit is 8 bytes, or the largest power + // of two which divides both to and size, whichever is smaller. + static void fill_to_memory_atomic(void* to, size_t size, jubyte value = 0); + + // Zero-fill methods + + // Zero word-aligned words, not atomic on each word + static void zero_to_words(HeapWord* to, size_t count) { + assert_params_ok(to, LogHeapWordSize); + pd_zero_to_words(to, count); + } + + // Zero bytes + static void zero_to_bytes(void* to, size_t count) { + pd_zero_to_bytes(to, count); + } + + private: + static bool params_disjoint(HeapWord* from, HeapWord* to, size_t count) { + if (from < to) { + return pointer_delta(to, from) >= count; + } + return pointer_delta(from, to) >= count; + } + + // These methods raise a fatal if they detect a problem. + + static void assert_disjoint(HeapWord* from, HeapWord* to, size_t count) { +#ifdef ASSERT + if (!params_disjoint(from, to, count)) + basic_fatal("source and dest overlap"); +#endif + } + + static void assert_params_ok(void* from, void* to, intptr_t log_align) { +#ifdef ASSERT + if (mask_bits((uintptr_t)from, right_n_bits(log_align)) != 0) + basic_fatal("not aligned"); + if (mask_bits((uintptr_t)to, right_n_bits(log_align)) != 0) + basic_fatal("not aligned"); +#endif + } + + static void assert_params_ok(HeapWord* to, intptr_t log_align) { +#ifdef ASSERT + if (mask_bits((uintptr_t)to, right_n_bits(log_align)) != 0) + basic_fatal("not word aligned"); +#endif + } + static void assert_params_aligned(HeapWord* from, HeapWord* to) { +#ifdef ASSERT + if (mask_bits((uintptr_t)from, MinObjAlignmentInBytes-1) != 0) + basic_fatal("not object aligned"); + if (mask_bits((uintptr_t)to, MinObjAlignmentInBytes-1) != 0) + basic_fatal("not object aligned"); +#endif + } + + static void assert_params_aligned(HeapWord* to) { +#ifdef ASSERT + if (mask_bits((uintptr_t)to, MinObjAlignmentInBytes-1) != 0) + basic_fatal("not object aligned"); +#endif + } + + static void assert_non_zero(size_t count) { +#ifdef ASSERT + if (count == 0) { + basic_fatal("count must be non-zero"); + } +#endif + } + + static void assert_byte_count_ok(size_t byte_count, size_t unit_size) { +#ifdef ASSERT + if ((size_t)round_to(byte_count, unit_size) != byte_count) { + basic_fatal("byte count must be aligned"); + } +#endif + } + + // Platform dependent implementations of the above methods. + #include "incls/_copy_pd.hpp.incl" +}; diff --git a/hotspot/src/share/vm/utilities/debug.cpp b/hotspot/src/share/vm/utilities/debug.cpp new file mode 100644 index 00000000000..8eb84cddcf5 --- /dev/null +++ b/hotspot/src/share/vm/utilities/debug.cpp @@ -0,0 +1,936 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_debug.cpp.incl" + +#ifndef ASSERT +# ifdef _DEBUG + // NOTE: don't turn the lines below into a comment -- if you're getting + // a compile error here, change the settings to define ASSERT + ASSERT should be defined when _DEBUG is defined. It is not intended to be used for debugging + functions that do not slow down the system too much and thus can be left in optimized code. + On the other hand, the code should not be included in a production version. +# endif // _DEBUG +#endif // ASSERT + + +#ifdef _DEBUG +# ifndef ASSERT + configuration error: ASSERT must be defined in debug version +# endif // ASSERT +#endif // _DEBUG + + +#ifdef PRODUCT +# if -defined _DEBUG || -defined ASSERT + configuration error: ASSERT et al. must not be defined in PRODUCT version +# endif +#endif // PRODUCT + + +void warning(const char* format, ...) { + // In case error happens before init or during shutdown + if (tty == NULL) ostream_init(); + + tty->print("%s warning: ", VM_Version::vm_name()); + va_list ap; + va_start(ap, format); + tty->vprint_cr(format, ap); + va_end(ap); + if (BreakAtWarning) BREAKPOINT; +} + +#ifndef PRODUCT + +#define is_token_break(ch) (isspace(ch) || (ch) == ',') + +static const char* last_file_name = NULL; +static int last_line_no = -1; + +// assert/guarantee/... may happen very early during VM initialization. +// Don't rely on anything that is initialized by Threads::create_vm(). For +// example, don't use tty. +bool assert_is_suppressed(const char* file_name, int line_no) { + // The following 1-element cache requires that passed-in + // file names are always only constant literals. + if (file_name == last_file_name && line_no == last_line_no) return true; + + int file_name_len = (int)strlen(file_name); + char separator = os::file_separator()[0]; + const char* base_name = strrchr(file_name, separator); + if (base_name == NULL) + base_name = file_name; + + // scan the SuppressErrorAt option + const char* cp = SuppressErrorAt; + for (;;) { + const char* sfile; + int sfile_len; + int sline; + bool noisy; + while ((*cp) != '\0' && is_token_break(*cp)) cp++; + if ((*cp) == '\0') break; + sfile = cp; + while ((*cp) != '\0' && !is_token_break(*cp) && (*cp) != ':') cp++; + sfile_len = cp - sfile; + if ((*cp) == ':') cp++; + sline = 0; + while ((*cp) != '\0' && isdigit(*cp)) { + sline *= 10; + sline += (*cp) - '0'; + cp++; + } + // "file:line!" means the assert suppression is not silent + noisy = ((*cp) == '!'); + while ((*cp) != '\0' && !is_token_break(*cp)) cp++; + // match the line + if (sline != 0) { + if (sline != line_no) continue; + } + // match the file + if (sfile_len > 0) { + const char* look = file_name; + const char* look_max = file_name + file_name_len - sfile_len; + const char* foundp; + bool match = false; + while (!match + && (foundp = strchr(look, sfile[0])) != NULL + && foundp <= look_max) { + match = true; + for (int i = 1; i < sfile_len; i++) { + if (sfile[i] != foundp[i]) { + match = false; + break; + } + } + look = foundp + 1; + } + if (!match) continue; + } + // got a match! + if (noisy) { + fdStream out(defaultStream::output_fd()); + out.print_raw("[error suppressed at "); + out.print_raw(base_name); + char buf[16]; + jio_snprintf(buf, sizeof(buf), ":%d]", line_no); + out.print_raw_cr(buf); + } else { + // update 1-element cache for fast silent matches + last_file_name = file_name; + last_line_no = line_no; + } + return true; + } + + if (!is_error_reported()) { + // print a friendly hint: + fdStream out(defaultStream::output_fd()); + out.print_raw_cr("# To suppress the following error report, specify this argument"); + out.print_raw ("# after -XX: or in .hotspotrc: SuppressErrorAt="); + out.print_raw (base_name); + char buf[16]; + jio_snprintf(buf, sizeof(buf), ":%d", line_no); + out.print_raw_cr(buf); + } + return false; +} + +#undef is_token_break + +#else + +// Place-holder for non-existent suppression check: +#define assert_is_suppressed(file_name, line_no) (false) + +#endif //PRODUCT + +void report_assertion_failure(const char* file_name, int line_no, const char* message) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + VMError err(ThreadLocalStorage::get_thread_slow(), message, file_name, line_no); + err.report_and_die(); +} + +void report_fatal(const char* file_name, int line_no, const char* message) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + VMError err(ThreadLocalStorage::get_thread_slow(), message, file_name, line_no); + err.report_and_die(); +} + +void report_fatal_vararg(const char* file_name, int line_no, const char* format, ...) { + char buffer[256]; + va_list ap; + va_start(ap, format); + jio_vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + report_fatal(file_name, line_no, buffer); +} + + +// Used by report_vm_out_of_memory to detect recursion. +static jint _exiting_out_of_mem = 0; + +// Just passing the flow to VMError to handle error +void report_vm_out_of_memory(const char* file_name, int line_no, size_t size, const char* message) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + + // We try to gather additional information for the first out of memory + // error only; gathering additional data might cause an allocation and a + // recursive out_of_memory condition. + + const jint exiting = 1; + // If we succeed in changing the value, we're the first one in. + bool first_time_here = Atomic::xchg(exiting, &_exiting_out_of_mem) != exiting; + + if (first_time_here) { + Thread* thread = ThreadLocalStorage::get_thread_slow(); + VMError(thread, size, message, file_name, line_no).report_and_die(); + } + vm_abort(); +} + +void report_vm_out_of_memory_vararg(const char* file_name, int line_no, size_t size, const char* format, ...) { + char buffer[256]; + va_list ap; + va_start(ap, format); + jio_vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + report_vm_out_of_memory(file_name, line_no, size, buffer); +} + +void report_should_not_call(const char* file_name, int line_no) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + VMError err(ThreadLocalStorage::get_thread_slow(), "ShouldNotCall()", file_name, line_no); + err.report_and_die(); +} + + +void report_should_not_reach_here(const char* file_name, int line_no) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + VMError err(ThreadLocalStorage::get_thread_slow(), "ShouldNotReachHere()", file_name, line_no); + err.report_and_die(); +} + + +void report_unimplemented(const char* file_name, int line_no) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + VMError err(ThreadLocalStorage::get_thread_slow(), "Unimplemented()", file_name, line_no); + err.report_and_die(); +} + + +void report_untested(const char* file_name, int line_no, const char* msg) { +#ifndef PRODUCT + warning("Untested: %s in %s: %d\n", msg, file_name, line_no); +#endif // PRODUCT +} + +void report_java_out_of_memory(const char* message) { + static jint out_of_memory_reported = 0; + + // A number of threads may attempt to report OutOfMemoryError at around the + // same time. To avoid dumping the heap or executing the data collection + // commands multiple times we just do it once when the first threads reports + // the error. + if (Atomic::cmpxchg(1, &out_of_memory_reported, 0) == 0) { + // create heap dump before OnOutOfMemoryError commands are executed + if (HeapDumpOnOutOfMemoryError) { + tty->print_cr("java.lang.OutOfMemoryError: %s", message); + HeapDumper::dump_heap(); + } + + if (OnOutOfMemoryError && OnOutOfMemoryError[0]) { + VMError err(message); + err.report_java_out_of_memory(); + } + } +} + + +extern "C" void ps(); + +static bool error_reported = false; + +// call this when the VM is dying--it might loosen some asserts +void set_error_reported() { + error_reported = true; +} + +bool is_error_reported() { + return error_reported; +} + +// ------ helper functions for debugging go here ------------ + +#ifndef PRODUCT +// All debug entries should be wrapped with a stack allocated +// Command object. It makes sure a resource mark is set and +// flushes the logfile to prevent file sharing problems. + +class Command : public StackObj { + private: + ResourceMark rm; + ResetNoHandleMark rnhm; + HandleMark hm; + bool debug_save; + public: + static int level; + Command(const char* str) { + debug_save = Debugging; + Debugging = true; + if (level++ > 0) return; + tty->cr(); + tty->print_cr("\"Executing %s\"", str); + } + + ~Command() { tty->flush(); Debugging = debug_save; level--; } +}; + +int Command::level = 0; + +extern "C" void blob(CodeBlob* cb) { + Command c("blob"); + cb->print(); +} + + +extern "C" void dump_vtable(address p) { + Command c("dump_vtable"); + klassOop k = (klassOop)p; + instanceKlass::cast(k)->vtable()->print(); +} + + +extern "C" void nm(intptr_t p) { + // Actually we look through all CodeBlobs (the nm name has been kept for backwards compatability) + Command c("nm"); + CodeBlob* cb = CodeCache::find_blob((address)p); + if (cb == NULL) { + tty->print_cr("NULL"); + } else { + cb->print(); + } +} + + +extern "C" void disnm(intptr_t p) { + Command c("disnm"); + CodeBlob* cb = CodeCache::find_blob((address) p); + cb->print(); + Disassembler::decode(cb); +} + + +extern "C" void printnm(intptr_t p) { + char buffer[256]; + sprintf(buffer, "printnm: " INTPTR_FORMAT, p); + Command c(buffer); + CodeBlob* cb = CodeCache::find_blob((address) p); + if (cb->is_nmethod()) { + nmethod* nm = (nmethod*)cb; + nm->print_nmethod(true); + } +} + + +extern "C" void universe() { + Command c("universe"); + Universe::print(); +} + + +extern "C" void verify() { + // try to run a verify on the entire system + // note: this may not be safe if we're not at a safepoint; for debugging, + // this manipulates the safepoint settings to avoid assertion failures + Command c("universe verify"); + bool safe = SafepointSynchronize::is_at_safepoint(); + if (!safe) { + tty->print_cr("warning: not at safepoint -- verify may fail"); + SafepointSynchronize::set_is_at_safepoint(); + } + // Ensure Eden top is correct before verification + Universe::heap()->prepare_for_verify(); + Universe::verify(true); + if (!safe) SafepointSynchronize::set_is_not_at_safepoint(); +} + + +extern "C" void pp(void* p) { + Command c("pp"); + FlagSetting fl(PrintVMMessages, true); + if (Universe::heap()->is_in(p)) { + oop obj = oop(p); + obj->print(); + } else { + tty->print("%#p", p); + } +} + + +// pv: print vm-printable object +extern "C" void pa(intptr_t p) { ((AllocatedObj*) p)->print(); } +extern "C" void findpc(intptr_t x); + +extern "C" void ps() { // print stack + Command c("ps"); + + + // Prints the stack of the current Java thread + JavaThread* p = JavaThread::active(); + tty->print(" for thread: "); + p->print(); + tty->cr(); + + if (p->has_last_Java_frame()) { + // If the last_Java_fp is set we are in C land and + // can call the standard stack_trace function. + p->trace_stack(); + } else { + frame f = os::current_frame(); + RegisterMap reg_map(p); + f = f.sender(®_map); + tty->print("(guessing starting frame id=%#p based on current fp)\n", f.id()); + p->trace_stack_from(vframe::new_vframe(&f, ®_map, p)); + pd_ps(f); + } + +} + + +extern "C" void psf() { // print stack frames + { + Command c("psf"); + JavaThread* p = JavaThread::active(); + tty->print(" for thread: "); + p->print(); + tty->cr(); + if (p->has_last_Java_frame()) { + p->trace_frames(); + } + } +} + + +extern "C" void threads() { + Command c("threads"); + Threads::print(false, true); +} + + +extern "C" void psd() { + Command c("psd"); + SystemDictionary::print(); +} + + +extern "C" void safepoints() { + Command c("safepoints"); + SafepointSynchronize::print_state(); +} + + +extern "C" void pss() { // print all stacks + Command c("pss"); + Threads::print(true, true); +} + + +extern "C" void debug() { // to set things up for compiler debugging + Command c("debug"); + WizardMode = true; + PrintVMMessages = PrintCompilation = true; + PrintInlining = PrintAssembly = true; + tty->flush(); +} + + +extern "C" void ndebug() { // undo debug() + Command c("ndebug"); + PrintCompilation = false; + PrintInlining = PrintAssembly = false; + tty->flush(); +} + + +extern "C" void flush() { + Command c("flush"); + tty->flush(); +} + + +extern "C" void events() { + Command c("events"); + Events::print_last(tty, 50); +} + + +extern "C" void nevents(int n) { + Command c("events"); + Events::print_last(tty, n); +} + + +// Given a heap address that was valid before the most recent GC, if +// the oop that used to contain it is still live, prints the new +// location of the oop and the address. Useful for tracking down +// certain kinds of naked oop and oop map bugs. +extern "C" void pnl(intptr_t old_heap_addr) { + // Print New Location of old heap address + Command c("pnl"); +#ifndef VALIDATE_MARK_SWEEP + tty->print_cr("Requires build with VALIDATE_MARK_SWEEP defined (debug build) and RecordMarkSweepCompaction enabled"); +#else + MarkSweep::print_new_location_of_heap_address((HeapWord*) old_heap_addr); +#endif +} + + +extern "C" methodOop findm(intptr_t pc) { + Command c("findm"); + nmethod* nm = CodeCache::find_nmethod((address)pc); + return (nm == NULL) ? (methodOop)NULL : nm->method(); +} + + +extern "C" nmethod* findnm(intptr_t addr) { + Command c("findnm"); + return CodeCache::find_nmethod((address)addr); +} + +static address same_page(address x, address y) { + intptr_t page_bits = -os::vm_page_size(); + if ((intptr_t(x) & page_bits) == (intptr_t(y) & page_bits)) { + return x; + } else if (x > y) { + return (address)(intptr_t(y) | ~page_bits) + 1; + } else { + return (address)(intptr_t(y) & page_bits); + } +} + + +static void find(intptr_t x, bool print_pc) { + address addr = (address)x; + + CodeBlob* b = CodeCache::find_blob_unsafe(addr); + if (b != NULL) { + if (b->is_buffer_blob()) { + // the interpreter is generated into a buffer blob + InterpreterCodelet* i = Interpreter::codelet_containing(addr); + if (i != NULL) { + i->print(); + return; + } + if (Interpreter::contains(addr)) { + tty->print_cr(INTPTR_FORMAT " is pointing into interpreter code (not bytecode specific)", addr); + return; + } + // + if (AdapterHandlerLibrary::contains(b)) { + AdapterHandlerLibrary::print_handler(b); + } + // the stubroutines are generated into a buffer blob + StubCodeDesc* d = StubCodeDesc::desc_for(addr); + if (d != NULL) { + d->print(); + if (print_pc) tty->cr(); + return; + } + if (StubRoutines::contains(addr)) { + tty->print_cr(INTPTR_FORMAT " is pointing to an (unnamed) stub routine", addr); + return; + } + // the InlineCacheBuffer is using stubs generated into a buffer blob + if (InlineCacheBuffer::contains(addr)) { + tty->print_cr(INTPTR_FORMAT "is pointing into InlineCacheBuffer", addr); + return; + } + VtableStub* v = VtableStubs::stub_containing(addr); + if (v != NULL) { + v->print(); + return; + } + } + if (print_pc && b->is_nmethod()) { + ResourceMark rm; + tty->print("%#p: Compiled ", addr); + ((nmethod*)b)->method()->print_value_on(tty); + tty->print(" = (CodeBlob*)" INTPTR_FORMAT, b); + tty->cr(); + return; + } + if ( b->is_nmethod()) { + if (b->is_zombie()) { + tty->print_cr(INTPTR_FORMAT " is zombie nmethod", b); + } else if (b->is_not_entrant()) { + tty->print_cr(INTPTR_FORMAT " is non-entrant nmethod", b); + } + } + b->print(); + return; + } + + if (Universe::heap()->is_in_reserved(addr)) { + HeapWord* p = Universe::heap()->block_start(addr); + bool print = false; + // If we couldn't find it it just may mean that heap wasn't parseable + // See if we were just given an oop directly + if (p != NULL && Universe::heap()->block_is_obj(p)) { + print = true; + } else if (p == NULL && ((oopDesc*)addr)->is_oop()) { + p = (HeapWord*) addr; + print = true; + } + if (print) { + oop(p)->print(); + if (p != (HeapWord*)x && oop(p)->is_constMethod() && + constMethodOop(p)->contains(addr)) { + Thread *thread = Thread::current(); + HandleMark hm(thread); + methodHandle mh (thread, constMethodOop(p)->method()); + if (!mh->is_native()) { + tty->print_cr("bci_from(%p) = %d; print_codes():", + addr, mh->bci_from(address(x))); + mh->print_codes(); + } + } + return; + } + } + if (JNIHandles::is_global_handle((jobject) addr)) { + tty->print_cr(INTPTR_FORMAT "is a global jni handle", addr); + return; + } + if (JNIHandles::is_weak_global_handle((jobject) addr)) { + tty->print_cr(INTPTR_FORMAT "is a weak global jni handle", addr); + return; + } + if (JNIHandleBlock::any_contains((jobject) addr)) { + tty->print_cr(INTPTR_FORMAT "is a local jni handle", addr); + return; + } + + for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + // Check for priviledge stack + if (thread->privileged_stack_top() != NULL && thread->privileged_stack_top()->contains(addr)) { + tty->print_cr(INTPTR_FORMAT "is pointing into the priviledge stack for thread: " INTPTR_FORMAT, addr, thread); + return; + } + // If the addr is a java thread print information about that. + if (addr == (address)thread) { + thread->print(); + return; + } + } + + // Try an OS specific find + if (os::find(addr)) { + return; + } + + if (print_pc) { + tty->print_cr(INTPTR_FORMAT ": probably in C++ code; check debugger", addr); + Disassembler::decode(same_page(addr-40,addr),same_page(addr+40,addr)); + return; + } + + tty->print_cr(INTPTR_FORMAT "is pointing to unknown location", addr); +} + + +class LookForRefInGenClosure : public OopsInGenClosure { +public: + oop target; + void do_oop(oop* o) { + if (o != NULL && *o == target) { + tty->print_cr("0x%08x", o); + } + } +}; + + +class LookForRefInObjectClosure : public ObjectClosure { +private: + LookForRefInGenClosure look_in_object; +public: + LookForRefInObjectClosure(oop target) { look_in_object.target = target; } + void do_object(oop obj) { + obj->oop_iterate(&look_in_object); + } +}; + + +static void findref(intptr_t x) { + GenCollectedHeap *gch = GenCollectedHeap::heap(); + LookForRefInGenClosure lookFor; + lookFor.target = (oop) x; + LookForRefInObjectClosure look_in_object((oop) x); + + tty->print_cr("Searching heap:"); + gch->object_iterate(&look_in_object); + + tty->print_cr("Searching strong roots:"); + Universe::oops_do(&lookFor, false); + JNIHandles::oops_do(&lookFor); // Global (strong) JNI handles + Threads::oops_do(&lookFor); + ObjectSynchronizer::oops_do(&lookFor); + //FlatProfiler::oops_do(&lookFor); + SystemDictionary::oops_do(&lookFor); + + tty->print_cr("Done."); +} + +class FindClassObjectClosure: public ObjectClosure { + private: + const char* _target; + public: + FindClassObjectClosure(const char name[]) { _target = name; } + + virtual void do_object(oop obj) { + if (obj->is_klass()) { + Klass* k = klassOop(obj)->klass_part(); + if (k->name() != NULL) { + ResourceMark rm; + const char* ext = k->external_name(); + if ( strcmp(_target, ext) == 0 ) { + tty->print_cr("Found " INTPTR_FORMAT, obj); + obj->print(); + } + } + } + } +}; + +// +extern "C" void findclass(const char name[]) { + Command c("findclass"); + if (name != NULL) { + tty->print_cr("Finding class %s -> ", name); + FindClassObjectClosure srch(name); + Universe::heap()->permanent_object_iterate(&srch); + } +} + +// Another interface that isn't ambiguous in dbx. +// Can we someday rename the other find to hsfind? +extern "C" void hsfind(intptr_t x) { + Command c("hsfind"); + find(x, false); +} + + +extern "C" void hsfindref(intptr_t x) { + Command c("hsfindref"); + findref(x); +} + +extern "C" void find(intptr_t x) { + Command c("find"); + find(x, false); +} + + +extern "C" void findpc(intptr_t x) { + Command c("findpc"); + find(x, true); +} + + +// int versions of all methods to avoid having to type type casts in the debugger + +void pp(intptr_t p) { pp((void*)p); } +void pp(oop p) { pp((void*)p); } + +void help() { + Command c("help"); + tty->print_cr("basic"); + tty->print_cr(" pp(void* p) - try to make sense of p"); + tty->print_cr(" pv(intptr_t p)- ((PrintableResourceObj*) p)->print()"); + tty->print_cr(" ps() - print current thread stack"); + tty->print_cr(" pss() - print all thread stacks"); + tty->print_cr(" pm(int pc) - print methodOop given compiled PC"); + tty->print_cr(" findm(intptr_t pc) - finds methodOop"); + tty->print_cr(" find(intptr_t x) - finds & prints nmethod/stub/bytecode/oop based on pointer into it"); + + tty->print_cr("misc."); + tty->print_cr(" flush() - flushes the log file"); + tty->print_cr(" events() - dump last 50 events"); + + + tty->print_cr("compiler debugging"); + tty->print_cr(" debug() - to set things up for compiler debugging"); + tty->print_cr(" ndebug() - undo debug"); +} + +#if 0 + +// BobV's command parser for debugging on windows when nothing else works. + +enum CommandID { + CMDID_HELP, + CMDID_QUIT, + CMDID_HSFIND, + CMDID_PSS, + CMDID_PS, + CMDID_PSF, + CMDID_FINDM, + CMDID_FINDNM, + CMDID_PP, + CMDID_BPT, + CMDID_EXIT, + CMDID_VERIFY, + CMDID_THREADS, + CMDID_ILLEGAL = 99 +}; + +struct CommandParser { + char *name; + CommandID code; + char *description; +}; + +struct CommandParser CommandList[] = { + (char *)"help", CMDID_HELP, " Dump this list", + (char *)"quit", CMDID_QUIT, " Return from this routine", + (char *)"hsfind", CMDID_HSFIND, "Perform an hsfind on an address", + (char *)"ps", CMDID_PS, " Print Current Thread Stack Trace", + (char *)"pss", CMDID_PSS, " Print All Thread Stack Trace", + (char *)"psf", CMDID_PSF, " Print All Stack Frames", + (char *)"findm", CMDID_FINDM, " Find a methodOop from a PC", + (char *)"findnm", CMDID_FINDNM, "Find an nmethod from a PC", + (char *)"pp", CMDID_PP, " Find out something about a pointer", + (char *)"break", CMDID_BPT, " Execute a breakpoint", + (char *)"exitvm", CMDID_EXIT, "Exit the VM", + (char *)"verify", CMDID_VERIFY, "Perform a Heap Verify", + (char *)"thread", CMDID_THREADS, "Dump Info on all Threads", + (char *)0, CMDID_ILLEGAL +}; + + +// get_debug_command() +// +// Read a command from standard input. +// This is useful when you have a debugger +// which doesn't support calling into functions. +// +void get_debug_command() +{ + ssize_t count; + int i,j; + bool gotcommand; + intptr_t addr; + char buffer[256]; + nmethod *nm; + methodOop m; + + tty->print_cr("You have entered the diagnostic command interpreter"); + tty->print("The supported commands are:\n"); + for ( i=0; ; i++ ) { + if ( CommandList[i].code == CMDID_ILLEGAL ) + break; + tty->print_cr(" %s \n", CommandList[i].name ); + } + + while ( 1 ) { + gotcommand = false; + tty->print("Please enter a command: "); + count = scanf("%s", buffer) ; + if ( count >=0 ) { + for ( i=0; ; i++ ) { + if ( CommandList[i].code == CMDID_ILLEGAL ) { + if (!gotcommand) tty->print("Invalid command, please try again\n"); + break; + } + if ( strcmp(buffer, CommandList[i].name) == 0 ) { + gotcommand = true; + switch ( CommandList[i].code ) { + case CMDID_PS: + ps(); + break; + case CMDID_PSS: + pss(); + break; + case CMDID_PSF: + psf(); + break; + case CMDID_FINDM: + tty->print("Please enter the hex addr to pass to findm: "); + scanf("%I64X", &addr); + m = (methodOop)findm(addr); + tty->print("findm(0x%I64X) returned 0x%I64X\n", addr, m); + break; + case CMDID_FINDNM: + tty->print("Please enter the hex addr to pass to findnm: "); + scanf("%I64X", &addr); + nm = (nmethod*)findnm(addr); + tty->print("findnm(0x%I64X) returned 0x%I64X\n", addr, nm); + break; + case CMDID_PP: + tty->print("Please enter the hex addr to pass to pp: "); + scanf("%I64X", &addr); + pp((void*)addr); + break; + case CMDID_EXIT: + exit(0); + case CMDID_HELP: + tty->print("Here are the supported commands: "); + for ( j=0; ; j++ ) { + if ( CommandList[j].code == CMDID_ILLEGAL ) + break; + tty->print_cr(" %s -- %s\n", CommandList[j].name, + CommandList[j].description ); + } + break; + case CMDID_QUIT: + return; + break; + case CMDID_BPT: + BREAKPOINT; + break; + case CMDID_VERIFY: + verify();; + break; + case CMDID_THREADS: + threads();; + break; + case CMDID_HSFIND: + tty->print("Please enter the hex addr to pass to hsfind: "); + scanf("%I64X", &addr); + tty->print("Calling hsfind(0x%I64X)\n", addr); + hsfind(addr); + break; + default: + case CMDID_ILLEGAL: + break; + } + } + } + } + } +} +#endif + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/utilities/debug.hpp b/hotspot/src/share/vm/utilities/debug.hpp new file mode 100644 index 00000000000..98d3e622697 --- /dev/null +++ b/hotspot/src/share/vm/utilities/debug.hpp @@ -0,0 +1,129 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// assertions +#ifdef ASSERT +// Turn this off by default: +//#define USE_REPEATED_ASSERTS +#ifdef USE_REPEATED_ASSERTS + #define assert(p,msg) \ + { for (int __i = 0; __i < AssertRepeat; __i++) { \ + if (!(p)) { \ + report_assertion_failure(__FILE__, __LINE__, \ + "assert(" XSTR(p) ",\"" msg "\")");\ + BREAKPOINT; \ + } \ + } \ + } +#else + #define assert(p,msg) \ + if (!(p)) { \ + report_assertion_failure(__FILE__, __LINE__, \ + "assert(" XSTR(p) ",\"" msg "\")");\ + BREAKPOINT; \ + } +#endif + +// This version of assert is for use with checking return status from +// library calls that return actual error values eg. EINVAL, +// ENOMEM etc, rather than returning -1 and setting errno. +// When the status is not what is expected it is very useful to know +// what status was actually returned, so we pass the status variable as +// an extra arg and use strerror to convert it to a meaningful string +// like "Invalid argument", "out of memory" etc +#define assert_status(p, status, msg) \ + do { \ + if (!(p)) { \ + char buf[128]; \ + snprintf(buf, 127, \ + "assert_status(" XSTR(p) ", error: %s(%d), \"" msg "\")" , \ + strerror((status)), (status)); \ + report_assertion_failure(__FILE__, __LINE__, buf); \ + BREAKPOINT; \ + } \ + } while (0) + +// Another version of assert where the message is not a string literal +// The boolean condition is not printed out because cpp doesn't like it. +#define assert_msg(p, msg) \ + if (!(p)) { \ + report_assertion_failure(__FILE__, __LINE__, msg); \ + BREAKPOINT; \ + } + +// Do not assert this condition if there's already another error reported. +#define assert_if_no_error(cond,msg) assert((cond) || is_error_reported(), msg) +#else + #define assert(p,msg) + #define assert_status(p,status,msg) + #define assert_if_no_error(cond,msg) + #define assert_msg(cond,msg) +#endif + + +// fatals +#define fatal(m) { report_fatal(__FILE__, __LINE__, m ); BREAKPOINT; } +#define fatal1(m,x1) { report_fatal_vararg(__FILE__, __LINE__, m, x1 ); BREAKPOINT; } +#define fatal2(m,x1,x2) { report_fatal_vararg(__FILE__, __LINE__, m, x1, x2 ); BREAKPOINT; } +#define fatal3(m,x1,x2,x3) { report_fatal_vararg(__FILE__, __LINE__, m, x1, x2, x3 ); BREAKPOINT; } +#define fatal4(m,x1,x2,x3,x4) { report_fatal_vararg(__FILE__, __LINE__, m, x1, x2, x3, x4 ); BREAKPOINT; } + +// out of memory +#define vm_exit_out_of_memory(s,m) { report_vm_out_of_memory(__FILE__, __LINE__, s, m ); BREAKPOINT; } +#define vm_exit_out_of_memory1(s,m,x1) { report_vm_out_of_memory_vararg(__FILE__, __LINE__, s, m, x1 ); BREAKPOINT; } +#define vm_exit_out_of_memory2(s,m,x1,x2) { report_vm_out_of_memory_vararg(__FILE__, __LINE__, s, m, x1, x2 ); BREAKPOINT; } +#define vm_exit_out_of_memory3(s,m,x1,x2,x3) { report_vm_out_of_memory_vararg(__FILE__, __LINE__, s, m, x1, x2, x3 ); BREAKPOINT; } +#define vm_exit_out_of_memory4(s,m,x1,x2,x3,x4) { report_vm_out_of_memory_vararg(__FILE__, __LINE__, s, m, x1, x2, x3, x4); BREAKPOINT; } + +// guarantee is like assert except it's always executed -- use it for +// cheap tests that catch errors that would otherwise be hard to find +// guarantee is also used for Verify options. +#define guarantee(b,msg) { if (!(b)) fatal("guarantee(" XSTR(b) ",\"" msg "\")"); } + +#define ShouldNotCallThis() { report_should_not_call (__FILE__, __LINE__); BREAKPOINT; } +#define ShouldNotReachHere() { report_should_not_reach_here (__FILE__, __LINE__); BREAKPOINT; } +#define Unimplemented() { report_unimplemented (__FILE__, __LINE__); BREAKPOINT; } +#define Untested(msg) { report_untested (__FILE__, __LINE__, msg); BREAKPOINT; } + +// error reporting helper functions +void report_assertion_failure(const char* file_name, int line_no, const char* message); +void report_fatal_vararg(const char* file_name, int line_no, const char* format, ...); +void report_fatal(const char* file_name, int line_no, const char* message); +void report_vm_out_of_memory_vararg(const char* file_name, int line_no, size_t size, const char* format, ...); +void report_vm_out_of_memory(const char* file_name, int line_no, size_t size, const char* message); +void report_should_not_call(const char* file_name, int line_no); +void report_should_not_reach_here(const char* file_name, int line_no); +void report_unimplemented(const char* file_name, int line_no); +void report_untested(const char* file_name, int line_no, const char* msg); +void warning(const char* format, ...); + +// out of memory reporting +void report_java_out_of_memory(const char* message); + +// Support for self-destruct +bool is_error_reported(); +void set_error_reported(); + +void pd_ps(frame f); +void pd_obfuscate_location(char *buf, size_t buflen); diff --git a/hotspot/src/share/vm/utilities/defaultStream.hpp b/hotspot/src/share/vm/utilities/defaultStream.hpp new file mode 100644 index 00000000000..979f5cd7439 --- /dev/null +++ b/hotspot/src/share/vm/utilities/defaultStream.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class defaultStream : public xmlTextStream { + friend void ostream_abort(); + public: + enum { NO_WRITER = -1 }; + private: + bool _inited; + fileStream* _log_file; // XML-formatted file shared by all threads + static int _output_fd; + static int _error_fd; + static FILE* _output_stream; + static FILE* _error_stream; + + void init(); + void init_log(); + void finish_log(); + void finish_log_on_error(char *buf, int buflen); + public: + // must defer time stamp due to the fact that os::init() hasn't + // yet been called and os::elapsed_counter() may not be valid + defaultStream() { + _log_file = NULL; + _inited = false; + _writer = -1; + _last_writer = -1; + } + + ~defaultStream() { + if (has_log_file()) finish_log(); + } + + static inline FILE* output_stream() { + return DisplayVMOutputToStderr ? _error_stream : _output_stream; + } + static inline FILE* error_stream() { + return DisplayVMOutputToStdout ? _output_stream : _error_stream; + } + static inline int output_fd() { + return DisplayVMOutputToStderr ? _error_fd : _output_fd; + } + static inline int error_fd() { + return DisplayVMOutputToStdout ? _output_fd : _error_fd; + } + + virtual void write(const char* s, size_t len); + + void flush() { + // once we can determine whether we are in a signal handler, we + // should add the following assert here: + // assert(xxxxxx, "can not flush buffer inside signal handler"); + xmlTextStream::flush(); + fflush(output_stream()); + if (has_log_file()) _log_file->flush(); + } + + // advisory lock/unlock of _writer field: + private: + intx _writer; // thread_id with current rights to output + intx _last_writer; + public: + intx hold(intx writer_id); + void release(intx holder); + intx writer() { return _writer; } + bool has_log_file(); + + static defaultStream* instance; // sole instance +}; diff --git a/hotspot/src/share/vm/utilities/dtrace.hpp b/hotspot/src/share/vm/utilities/dtrace.hpp new file mode 100644 index 00000000000..e4e9f03a492 --- /dev/null +++ b/hotspot/src/share/vm/utilities/dtrace.hpp @@ -0,0 +1,125 @@ +/* + * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#if defined(SOLARIS) && defined(DTRACE_ENABLED) + +#include + +#define DTRACE_ONLY(x) x +#define NOT_DTRACE(x) + +#else // ndef SOLARIS || ndef DTRACE_ENABLED + +#define DTRACE_ONLY(x) +#define NOT_DTRACE(x) x + +#define DTRACE_PROBE(a,b) {;} +#define DTRACE_PROBE1(a,b,c) {;} +#define DTRACE_PROBE2(a,b,c,d) {;} +#define DTRACE_PROBE3(a,b,c,d,e) {;} +#define DTRACE_PROBE4(a,b,c,d,e,f) {;} +#define DTRACE_PROBE5(a,b,c,d,e,f,g) {;} + +#endif + +#define HS_DTRACE_PROBE_FN(provider,name)\ + __dtrace_##provider##___##name + +#define HS_DTRACE_PROBE_DECL_N(provider,name,args) \ + DTRACE_ONLY(extern "C" void HS_DTRACE_PROBE_FN(provider,name) args) +#define HS_DTRACE_PROBE_CDECL_N(provider,name,args) \ + DTRACE_ONLY(extern void HS_DTRACE_PROBE_FN(provider,name) args) + +/* Dtrace probe declarations */ +#define HS_DTRACE_PROBE_DECL(provider,name) \ + HS_DTRACE_PROBE_DECL0(provider,name) +#define HS_DTRACE_PROBE_DECL0(provider,name)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(void)) +#define HS_DTRACE_PROBE_DECL1(provider,name,t0)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(uintptr_t)) +#define HS_DTRACE_PROBE_DECL2(provider,name,t0,t1)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL3(provider,name,t0,t1,t2)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(uintptr_t,uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL4(provider,name,t0,t1,t2,t3)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(uintptr_t,uintptr_t,uintptr_t,\ + uintptr_t)) +#define HS_DTRACE_PROBE_DECL5(provider,name,t0,t1,t2,t3,t4)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL6(provider,name,t0,t1,t2,t3,t4,t5)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL7(provider,name,t0,t1,t2,t3,t4,t5,t6)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL8(provider,name,t0,t1,t2,t3,t4,t5,t6,t7)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,\ + uintptr_t)) +#define HS_DTRACE_PROBE_DECL9(provider,name,t0,t1,t2,t3,t4,t5,t6,t7,t8)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,\ + uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL10(provider,name,t0,t1,t2,t3,t4,t5,t6,t7,t8,t9)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,\ + uintptr_t,uintptr_t,uintptr_t)) + +/* Dtrace probe definitions */ +#define HS_DTRACE_PROBE_N(provider,name, args) \ + DTRACE_ONLY(HS_DTRACE_PROBE_FN(provider,name) args) + +#define HS_DTRACE_PROBE(provider,name) HS_DTRACE_PROBE0(provider,name) +#define HS_DTRACE_PROBE0(provider,name)\ + HS_DTRACE_PROBE_N(provider,name,()) +#define HS_DTRACE_PROBE1(provider,name,a0)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0)) +#define HS_DTRACE_PROBE2(provider,name,a0,a1)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1)) +#define HS_DTRACE_PROBE3(provider,name,a0,a1,a2)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2)) +#define HS_DTRACE_PROBE4(provider,name,a0,a1,a2,a3)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3)) +#define HS_DTRACE_PROBE5(provider,name,a0,a1,a2,a3,a4)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4)) +#define HS_DTRACE_PROBE6(provider,name,a0,a1,a2,a3,a4,a5)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4,(uintptr_t)a5)) +#define HS_DTRACE_PROBE7(provider,name,a0,a1,a2,a3,a4,a5,a6)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4,(uintptr_t)a5,(uintptr_t)a6)) +#define HS_DTRACE_PROBE8(provider,name,a0,a1,a2,a3,a4,a5,a6,a7)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4,(uintptr_t)a5,(uintptr_t)a6,(uintptr_t)a7)) +#define HS_DTRACE_PROBE9(provider,name,a0,a1,a2,a3,a4,a5,a6,a7,a8)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4,(uintptr_t)a5,(uintptr_t)a6,(uintptr_t)a7,\ + (uintptr_t)a8)) +#define HS_DTRACE_PROBE10(provider,name,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4,(uintptr_t)a5,(uintptr_t)a6,(uintptr_t)a7,\ + (uintptr_t)a8,(uintptr_t)a9)) diff --git a/hotspot/src/share/vm/utilities/events.cpp b/hotspot/src/share/vm/utilities/events.cpp new file mode 100644 index 00000000000..b24452e83ce --- /dev/null +++ b/hotspot/src/share/vm/utilities/events.cpp @@ -0,0 +1,249 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_events.cpp.incl" + + +#ifndef PRODUCT + +//////////////////////////////////////////////////////////////////////////// +// Event + +typedef u4 EventID; + +class Event VALUE_OBJ_CLASS_SPEC { + private: + jlong _time_tick; + intx _thread_id; + const char* _format; + int _indent; + intptr_t _arg_1; + intptr_t _arg_2; + intptr_t _arg_3; + + // only EventBuffer::add_event() can assign event id + friend class EventBuffer; + EventID _id; + + public: + + void clear() { _format = NULL; } + + EventID id() const { return _id; } + + void fill(int indent, const char* format, intptr_t arg_1, intptr_t arg_2, intptr_t arg_3) { + _format = format; + _arg_1 = arg_1; + _arg_2 = arg_2; + _arg_3 = arg_3; + + _indent = indent; + + _thread_id = os::current_thread_id(); + _time_tick = os::elapsed_counter(); + } + + void print_on(outputStream *st) { + if (_format == NULL) return; + st->print(" %d", _thread_id); + st->print(" %3.2g ", (double)_time_tick / os::elapsed_frequency()); + st->fill_to(20); + for (int index = 0; index < _indent; index++) { + st->print("| "); + } + st->print_cr(_format, _arg_1, _arg_2, _arg_3); + } +}; + +//////////////////////////////////////////////////////////////////////////// +// EventBuffer +// +// Simple lock-free event queue. Every event has a unique 32-bit id. +// It's fine if two threads add events at the same time, because they +// will get different event id, and then write to different buffer location. +// However, it is assumed that add_event() is quick enough (or buffer size +// is big enough), so when one thread is adding event, there can't be more +// than "size" events created by other threads; otherwise we'll end up having +// two threads writing to the same location. + +class EventBuffer : AllStatic { + private: + static Event* buffer; + static int size; + static jint indent; + static volatile EventID _current_event_id; + + static EventID get_next_event_id() { + return (EventID)Atomic::add(1, (jint*)&_current_event_id); + } + + public: + static void inc_indent() { Atomic::inc(&indent); } + static void dec_indent() { Atomic::dec(&indent); } + + static bool get_event(EventID id, Event* event) { + int index = (int)(id % size); + if (buffer[index].id() == id) { + memcpy(event, &buffer[index], sizeof(Event)); + // check id again; if buffer[index] is being updated by another thread, + // event->id() will contain different value. + return (event->id() == id); + } else { + // id does not match - id is invalid, or event is overwritten + return false; + } + } + + // add a new event to the queue; if EventBuffer is full, this call will + // overwrite the oldest event in the queue + static EventID add_event(const char* format, + intptr_t arg_1, intptr_t arg_2, intptr_t arg_3) { + // assign a unique id + EventID id = get_next_event_id(); + + // event will be copied to buffer[index] + int index = (int)(id % size); + + // first, invalidate id, buffer[index] can't have event with id = index + 2 + buffer[index]._id = index + 2; + + // make sure everyone has seen that buffer[index] is invalid + OrderAccess::fence(); + + // ... before updating its value + buffer[index].fill(indent, format, arg_1, arg_2, arg_3); + + // finally, set up real event id, now buffer[index] contains valid event + OrderAccess::release_store(&(buffer[index]._id), id); + + return id; + } + + static void print_last(outputStream *st, int number) { + st->print_cr("[Last %d events in the event buffer]", number); + st->print_cr("------------------------"); + + int count = 0; + EventID id = _current_event_id; + while (count < number) { + Event event; + if (get_event(id, &event)) { + event.print_on(st); + } + id--; + count++; + } + } + + static void print_all(outputStream* st) { + print_last(st, size); + } + + static void init() { + // Allocate the event buffer + size = EventLogLength; + buffer = NEW_C_HEAP_ARRAY(Event, size); + + _current_event_id = 0; + + // Clear the event buffer + for (int index = 0; index < size; index++) { + buffer[index]._id = index + 1; // index + 1 is invalid id + buffer[index].clear(); + } + } +}; + +Event* EventBuffer::buffer; +int EventBuffer::size; +volatile EventID EventBuffer::_current_event_id; +int EventBuffer::indent; + +//////////////////////////////////////////////////////////////////////////// +// Events + +// Events::log() is safe for signal handlers +void Events::log(const char* format, ...) { + if (LogEvents) { + va_list ap; + va_start(ap, format); + intptr_t arg_1 = va_arg(ap, intptr_t); + intptr_t arg_2 = va_arg(ap, intptr_t); + intptr_t arg_3 = va_arg(ap, intptr_t); + va_end(ap); + + EventBuffer::add_event(format, arg_1, arg_2, arg_3); + } +} + +void Events::print_all(outputStream *st) { + EventBuffer::print_all(st); +} + +void Events::print_last(outputStream *st, int number) { + EventBuffer::print_last(st, number); +} + +/////////////////////////////////////////////////////////////////////////// +// EventMark + +EventMark::EventMark(const char* format, ...) { + if (LogEvents) { + va_list ap; + va_start(ap, format); + intptr_t arg_1 = va_arg(ap, intptr_t); + intptr_t arg_2 = va_arg(ap, intptr_t); + intptr_t arg_3 = va_arg(ap, intptr_t); + va_end(ap); + + EventBuffer::add_event(format, arg_1, arg_2, arg_3); + EventBuffer::inc_indent(); + } +} + +EventMark::~EventMark() { + if (LogEvents) { + EventBuffer::dec_indent(); + EventBuffer::add_event("done", 0, 0, 0); + } +} + +/////////////////////////////////////////////////////////////////////////// + +void eventlog_init() { + EventBuffer::init(); +} + +int print_all_events(outputStream *st) { + EventBuffer::print_all(st); + return 1; +} + +#else + +void eventlog_init() {} +int print_all_events(outputStream *st) { return 0; } + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/utilities/events.hpp b/hotspot/src/share/vm/utilities/events.hpp new file mode 100644 index 00000000000..fa76807aa84 --- /dev/null +++ b/hotspot/src/share/vm/utilities/events.hpp @@ -0,0 +1,64 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Events and EventMark provide interfaces to log events taking place in the vm. +// This facility is extremly useful for post-mortem debugging. The eventlog +// often provides crucial information about events leading up to the crash. +// +// All arguments past the format string must be passed as an intptr_t. +// +// To log a single event use: +// Events::log("New nmethod has been created " INTPTR_FORMAT, nm); +// +// To log a block of events use: +// EventMark m("GarbageCollecting %d", (intptr_t)gc_number); +// +// The constructor to eventlog indents the eventlog until the +// destructor has been executed. +// +// IMPLEMENTATION RESTRICTION: +// Max 3 arguments are saved for each logged event. +// + +class Events : AllStatic { + public: + // Logs an event, format as printf + static void log(const char* format, ...) PRODUCT_RETURN; + + // Prints all events in the buffer + static void print_all(outputStream* st) PRODUCT_RETURN; + + // Prints last number events from the event buffer + static void print_last(outputStream *st, int number) PRODUCT_RETURN; +}; + +class EventMark : public StackObj { + public: + // log a begin event, format as printf + EventMark(const char* format, ...) PRODUCT_RETURN; + // log an end event + ~EventMark() PRODUCT_RETURN; +}; + +int print_all_events(outputStream *st); diff --git a/hotspot/src/share/vm/utilities/exceptions.cpp b/hotspot/src/share/vm/utilities/exceptions.cpp new file mode 100644 index 00000000000..91c3cd733d3 --- /dev/null +++ b/hotspot/src/share/vm/utilities/exceptions.cpp @@ -0,0 +1,388 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_exceptions.cpp.incl" + + +// Implementation of ThreadShadow +void check_ThreadShadow() { + const ByteSize offset1 = byte_offset_of(ThreadShadow, _pending_exception); + const ByteSize offset2 = Thread::pending_exception_offset(); + if (offset1 != offset2) fatal("ThreadShadow::_pending_exception is not positioned correctly"); +} + + +void ThreadShadow::set_pending_exception(oop exception, const char* file, int line) { + assert(exception != NULL && exception->is_oop(), "invalid exception oop"); + _pending_exception = exception; + _exception_file = file; + _exception_line = line; +} + +void ThreadShadow::clear_pending_exception() { + if (TraceClearedExceptions) { + if (_pending_exception != NULL) { + tty->print_cr("Thread::clear_pending_exception: cleared exception:"); + _pending_exception->print(); + } + } + _pending_exception = NULL; + _exception_file = NULL; + _exception_line = 0; +} +// Implementation of Exceptions + +bool Exceptions::special_exception(Thread* thread, const char* file, int line, Handle h_exception) { + // bootstrapping check + if (!Universe::is_fully_initialized()) { + vm_exit_during_initialization(h_exception); + ShouldNotReachHere(); + } + + if (thread->is_VM_thread() + || thread->is_Compiler_thread() ) { + // We do not care what kind of exception we get for the vm-thread or a thread which + // is compiling. We just install a dummy exception object + thread->set_pending_exception(Universe::vm_exception(), file, line); + return true; + } + + return false; +} + +bool Exceptions::special_exception(Thread* thread, const char* file, int line, symbolHandle h_name, const char* message) { + // bootstrapping check + if (!Universe::is_fully_initialized()) { + if (h_name.is_null()) { + // atleast an informative message. + vm_exit_during_initialization("Exception", message); + } else { + vm_exit_during_initialization(h_name, message); + } + ShouldNotReachHere(); + } + + if (thread->is_VM_thread() + || thread->is_Compiler_thread() ) { + // We do not care what kind of exception we get for the vm-thread or a thread which + // is compiling. We just install a dummy exception object + thread->set_pending_exception(Universe::vm_exception(), file, line); + return true; + } + + return false; +} + +// This method should only be called from generated code, +// therefore the exception oop should be in the oopmap. +void Exceptions::_throw_oop(Thread* thread, const char* file, int line, oop exception) { + assert(exception != NULL, "exception should not be NULL"); + Handle h_exception = Handle(thread, exception); + _throw(thread, file, line, h_exception); +} + +void Exceptions::_throw(Thread* thread, const char* file, int line, Handle h_exception) { + assert(h_exception() != NULL, "exception should not be NULL"); + + // tracing (do this up front - so it works during boot strapping) + if (TraceExceptions) { + ttyLocker ttyl; + ResourceMark rm; + tty->print_cr("Exception <%s> (" INTPTR_FORMAT " ) \nthrown [%s, line %d]\nfor thread " INTPTR_FORMAT, + h_exception->print_value_string(), (address)h_exception(), file, line, thread); + } + // for AbortVMOnException flag + NOT_PRODUCT(Exceptions::debug_check_abort(h_exception)); + + // Check for special boot-strapping/vm-thread handling + if (special_exception(thread, file, line, h_exception)) return; + + assert(h_exception->is_a(SystemDictionary::throwable_klass()), "exception is not a subclass of java/lang/Throwable"); + + // set the pending exception + thread->set_pending_exception(h_exception(), file, line); + + // vm log + Events::log("throw_exception " INTPTR_FORMAT, (address)h_exception()); +} + + +void Exceptions::_throw_msg(Thread* thread, const char* file, int line, symbolHandle h_name, const char* message, Handle h_loader, Handle h_protection_domain) { + // Check for special boot-strapping/vm-thread handling + if (special_exception(thread, file, line, h_name, message)) return; + // Create and throw exception + Handle h_cause(thread, NULL); + Handle h_exception = new_exception(thread, h_name, message, h_cause, h_loader, h_protection_domain); + _throw(thread, file, line, h_exception); +} + +// Throw an exception with a message and a cause +void Exceptions::_throw_msg_cause(Thread* thread, const char* file, int line, symbolHandle h_name, const char* message, Handle h_cause, Handle h_loader, Handle h_protection_domain) { + // Check for special boot-strapping/vm-thread handling + if (special_exception(thread, file, line, h_name, message)) return; + // Create and throw exception and init cause + Handle h_exception = new_exception(thread, h_name, message, h_cause, h_loader, h_protection_domain); + _throw(thread, file, line, h_exception); +} + +// This version creates handles and calls the other version +void Exceptions::_throw_msg(Thread* thread, const char* file, int line, + symbolOop name, const char* message) { + symbolHandle h_name(thread, name); + Handle h_loader(thread, NULL); + Handle h_protection_domain(thread, NULL); + Exceptions::_throw_msg(thread, file, line, h_name, message, h_loader, h_protection_domain); +} + +// This version already has a handle for name +void Exceptions::_throw_msg(Thread* thread, const char* file, int line, + symbolHandle name, const char* message) { + Handle h_loader(thread, NULL); + Handle h_protection_domain(thread, NULL); + Exceptions::_throw_msg(thread, file, line, name, message, h_loader, h_protection_domain); +} + +// This version already has a handle for name +void Exceptions::_throw_msg_cause(Thread* thread, const char* file, int line, + symbolHandle name, const char* message, Handle cause) { + Handle h_loader(thread, NULL); + Handle h_protection_domain(thread, NULL); + Exceptions::_throw_msg_cause(thread, file, line, name, message, cause, h_loader, h_protection_domain); +} + +void Exceptions::_throw_args(Thread* thread, const char* file, int line, symbolHandle h_name, symbolHandle h_signature, JavaCallArguments *args) { + // Check for special boot-strapping/vm-thread handling + if (special_exception(thread, file, line, h_name, NULL)) return; + // Create and throw exception + Handle h_loader(thread, NULL); + Handle h_prot(thread, NULL); + Handle h_cause(thread, NULL); + Handle exception = new_exception(thread, h_name, h_signature, args, h_cause, h_loader, h_prot); + _throw(thread, file, line, exception); +} + + +void Exceptions::throw_stack_overflow_exception(Thread* THREAD, const char* file, int line) { + Handle exception; + if (!THREAD->has_pending_exception()) { + klassOop k = SystemDictionary::StackOverflowError_klass(); + oop e = instanceKlass::cast(k)->allocate_instance(CHECK); + exception = Handle(THREAD, e); // fill_in_stack trace does gc + if (StackTraceInThrowable) { + java_lang_Throwable::fill_in_stack_trace(exception); + } + } else { + // if prior exception, throw that one instead + exception = Handle(THREAD, THREAD->pending_exception()); + } + _throw_oop(THREAD, file, line, exception()); +} + +void Exceptions::fthrow(Thread* thread, const char* file, int line, symbolHandle h_name, const char* format, ...) { + const int max_msg_size = 1024; + va_list ap; + va_start(ap, format); + char msg[max_msg_size]; + vsnprintf(msg, max_msg_size, format, ap); + msg[max_msg_size-1] = '\0'; + va_end(ap); + _throw_msg(thread, file, line, h_name, msg); +} + +// Creates an exception oop, calls the method with the given signature. +// and returns a Handle +// Initializes the cause if cause non-null +Handle Exceptions::new_exception(Thread *thread, symbolHandle h_name, + symbolHandle signature, + JavaCallArguments *args, + Handle h_cause, Handle h_loader, + Handle h_protection_domain) { + assert(Universe::is_fully_initialized(), + "cannot be called during initialization"); + assert(thread->is_Java_thread(), "can only be called by a Java thread"); + assert(!thread->has_pending_exception(), "already has exception"); + + Handle h_exception; + + // Resolve exception klass + klassOop ik = SystemDictionary::resolve_or_fail(h_name, h_loader, h_protection_domain, true, thread); + instanceKlassHandle klass (thread, ik); + + if (!thread->has_pending_exception()) { + assert(klass.not_null(), "klass must exist"); + // We are about to create an instance - so make sure that klass is initialized + klass->initialize(thread); + if (!thread->has_pending_exception()) { + // Allocate new exception + h_exception = klass->allocate_instance_handle(thread); + if (!thread->has_pending_exception()) { + JavaValue result(T_VOID); + args->set_receiver(h_exception); + // Call constructor + JavaCalls::call_special(&result, klass, + vmSymbolHandles::object_initializer_name(), + signature, + args, + thread); + + } + } + + // Future: object initializer should take a cause argument + if (h_cause() != NULL) { + assert(h_cause->is_a(SystemDictionary::throwable_klass()), + "exception cause is not a subclass of java/lang/Throwable"); + JavaValue result1(T_OBJECT); + JavaCallArguments args1; + args1.set_receiver(h_exception); + args1.push_oop(h_cause); + JavaCalls::call_virtual(&result1, klass, + vmSymbolHandles::initCause_name(), + vmSymbolHandles::throwable_throwable_signature(), + &args1, + thread); + } + } + + // Check if another exception was thrown in the process, if so rethrow that one + if (thread->has_pending_exception()) { + h_exception = Handle(thread, thread->pending_exception()); + thread->clear_pending_exception(); + } + return h_exception; +} + +// Convenience method. Calls either the () or (String) method when +// creating a new exception +Handle Exceptions::new_exception(Thread* thread, symbolHandle h_name, + const char* message, Handle h_cause, + Handle h_loader, + Handle h_protection_domain, + ExceptionMsgToUtf8Mode to_utf8_safe) { + JavaCallArguments args; + symbolHandle signature; + if (message == NULL) { + signature = vmSymbolHandles::void_method_signature(); + } else { + // We want to allocate storage, but we can't do that if there's + // a pending exception, so we preserve any pending exception + // around the allocation. + // If we get an exception from the allocation, prefer that to + // the exception we are trying to build, or the pending exception. + // This is sort of like what PRESERVE_EXCEPTION_MARK does, except + // for the preferencing and the early returns. + Handle incoming_exception (thread, NULL); + if (thread->has_pending_exception()) { + incoming_exception = Handle(thread, thread->pending_exception()); + thread->clear_pending_exception(); + } + Handle msg; + if (to_utf8_safe == safe_to_utf8) { + // Make a java UTF8 string. + msg = java_lang_String::create_from_str(message, thread); + } else { + // Make a java string keeping the encoding scheme of the original string. + msg = java_lang_String::create_from_platform_dependent_str(message, thread); + } + if (thread->has_pending_exception()) { + Handle exception(thread, thread->pending_exception()); + thread->clear_pending_exception(); + return exception; + } + if (incoming_exception.not_null()) { + return incoming_exception; + } + args.push_oop(msg); + signature = vmSymbolHandles::string_void_signature(); + } + return new_exception(thread, h_name, signature, &args, h_cause, h_loader, h_protection_domain); +} + +// Another convenience method that creates handles for null class loaders and +// protection domains and null causes. +// If the last parameter 'to_utf8_mode' is safe_to_utf8, +// it means we can safely ignore the encoding scheme of the message string and +// convert it directly to a java UTF8 string. Otherwise, we need to take the +// encoding scheme of the string into account. One thing we should do at some +// point is to push this flag down to class java_lang_String since other +// classes may need similar functionalities. +Handle Exceptions::new_exception(Thread* thread, + symbolOop name, + const char* message, + ExceptionMsgToUtf8Mode to_utf8_safe) { + + symbolHandle h_name(thread, name); + Handle h_loader(thread, NULL); + Handle h_prot(thread, NULL); + Handle h_cause(thread, NULL); + return Exceptions::new_exception(thread, h_name, message, h_cause, h_loader, + h_prot, to_utf8_safe); +} + +// Implementation of ExceptionMark + +ExceptionMark::ExceptionMark(Thread*& thread) { + thread = Thread::current(); + _thread = thread; + if (_thread->has_pending_exception()) { + oop exception = _thread->pending_exception(); + _thread->clear_pending_exception(); // Needed to avoid infinite recursion + exception->print(); + fatal("ExceptionMark constructor expects no pending exceptions"); + } +} + + +ExceptionMark::~ExceptionMark() { + if (_thread->has_pending_exception()) { + Handle exception(_thread, _thread->pending_exception()); + _thread->clear_pending_exception(); // Needed to avoid infinite recursion + if (is_init_completed()) { + exception->print(); + fatal("ExceptionMark destructor expects no pending exceptions"); + } else { + vm_exit_during_initialization(exception); + } + } +} + +// ---------------------------------------------------------------------------------------- + +#ifndef PRODUCT +// caller frees value_string if necessary +void Exceptions::debug_check_abort(const char *value_string) { + if (AbortVMOnException != NULL && value_string != NULL && + strstr(value_string, AbortVMOnException)) { + fatal1("Saw %s, aborting", value_string); + } +} + +void Exceptions::debug_check_abort(Handle exception) { + if (AbortVMOnException != NULL) { + ResourceMark rm; + debug_check_abort(instanceKlass::cast(exception()->klass())->external_name()); + } +} +#endif diff --git a/hotspot/src/share/vm/utilities/exceptions.hpp b/hotspot/src/share/vm/utilities/exceptions.hpp new file mode 100644 index 00000000000..e8388ad4abb --- /dev/null +++ b/hotspot/src/share/vm/utilities/exceptions.hpp @@ -0,0 +1,275 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file provides the basic support for exception handling in the VM. +// Note: We do not use C++ exceptions to avoid compiler dependencies and +// unpredictable performance. +// +// Scheme: Exceptions are stored with the thread. There is never more +// than one pending exception per thread. All functions that can throw +// an exception carry a THREAD argument (usually the last argument and +// declared with the TRAPS macro). Throwing an exception means setting +// a pending exception in the thread. Upon return from a function that +// can throw an exception, we must check if an exception is pending. +// The CHECK macros do this in a convenient way. Carrying around the +// thread provides also convenient access to it (e.g. for Handle +// creation, w/o the need for recomputation). + + + +// Forward declarations to be independent of the include structure. +// This allows us to have exceptions.hpp included in top.hpp. + +class Thread; +class Handle; +class symbolHandle; +class symbolOopDesc; +class JavaCallArguments; + +// The ThreadShadow class is a helper class to access the _pending_exception +// field of the Thread class w/o having access to the Thread's interface (for +// include hierachy reasons). + +class ThreadShadow: public CHeapObj { + protected: + oop _pending_exception; // Thread has gc actions. + const char* _exception_file; // file information for exception (debugging only) + int _exception_line; // line information for exception (debugging only) + friend void check_ThreadShadow(); // checks _pending_exception offset + + // The following virtual exists only to force creation of a vtable. + // We need ThreadShadow to have a vtable, even in product builds, + // so that its layout will start at an offset of zero relative to Thread. + // Some C++ compilers are so "clever" that they put the ThreadShadow + // base class at offset 4 in Thread (after Thread's vtable), if they + // notice that Thread has a vtable but ThreadShadow does not. + virtual void unused_initial_virtual() { } + + public: + oop pending_exception() const { return _pending_exception; } + bool has_pending_exception() const { return _pending_exception != NULL; } + const char* exception_file() const { return _exception_file; } + int exception_line() const { return _exception_line; } + + // Code generation support + static ByteSize pending_exception_offset() { return byte_offset_of(ThreadShadow, _pending_exception); } + + // use THROW whenever possible! + void set_pending_exception(oop exception, const char* file, int line); + + // use CLEAR_PENDING_EXCEPTION whenever possible! + void clear_pending_exception(); + + ThreadShadow() : _pending_exception(NULL), + _exception_file(NULL), _exception_line(0) {} +}; + + +// Exceptions is a helper class that encapsulates all operations +// that require access to the thread interface and which are +// relatively rare. The Exceptions operations should only be +// used directly if the macros below are insufficient. + +class Exceptions { + static bool special_exception(Thread *thread, const char* file, int line, Handle exception); + static bool special_exception(Thread* thread, const char* file, int line, symbolHandle name, const char* message); + public: + // this enum is defined to indicate whether it is safe to + // ignore the encoding scheme of the original message string. + typedef enum { + safe_to_utf8 = 0, + unsafe_to_utf8 = 1 + } ExceptionMsgToUtf8Mode; + // Throw exceptions: w/o message, w/ message & with formatted message. + static void _throw_oop(Thread* thread, const char* file, int line, oop exception); + static void _throw(Thread* thread, const char* file, int line, Handle exception); + static void _throw_msg(Thread* thread, const char* file, int line, + symbolHandle name, const char* message, Handle loader, + Handle protection_domain); + static void _throw_msg(Thread* thread, const char* file, int line, + symbolOop name, const char* message); + static void _throw_msg(Thread* thread, const char* file, int line, + symbolHandle name, const char* message); + static void _throw_args(Thread* thread, const char* file, int line, + symbolHandle name, symbolHandle signature, + JavaCallArguments* args); + static void _throw_msg_cause(Thread* thread, const char* file, + int line, symbolHandle h_name, const char* message, + Handle h_cause, Handle h_loader, Handle h_protection_domain); + static void _throw_msg_cause(Thread* thread, const char* file, int line, + symbolHandle name, const char* message, Handle cause); + + // There is no THROW... macro for this method. Caller should remember + // to do a return after calling it. + static void fthrow(Thread* thread, const char* file, int line, symbolHandle name, + const char* format, ...); + + // Create and initialize a new exception + static Handle new_exception(Thread* thread, symbolHandle name, + symbolHandle signature, JavaCallArguments* args, + Handle cause, Handle loader, + Handle protection_domain); + + static Handle new_exception(Thread* thread, symbolHandle name, + const char* message, Handle cause, Handle loader, + Handle protection_domain, + ExceptionMsgToUtf8Mode to_utf8_safe = safe_to_utf8); + + static Handle new_exception(Thread* thread, symbolOop name, + const char* message, + ExceptionMsgToUtf8Mode to_utf8_safe = safe_to_utf8); + + static void throw_stack_overflow_exception(Thread* thread, const char* file, int line); + + // for AbortVMOnException flag + NOT_PRODUCT(static void debug_check_abort(Handle exception);) + NOT_PRODUCT(static void debug_check_abort(const char *value_string);) +}; + + +// The THREAD & TRAPS macros facilitate the declaration of functions that throw exceptions. +// Convention: Use the TRAPS macro as the last argument of such a function; e.g.: +// +// int this_function_may_trap(int x, float y, TRAPS) + +#define THREAD __the_thread__ +#define TRAPS Thread* THREAD + + +// The CHECK... macros should be used to pass along a THREAD reference and to check for pending +// exceptions. In special situations it is necessary to handle pending exceptions explicitly, +// in these cases the PENDING_EXCEPTION helper macros should be used. +// +// Macro naming conventions: Macros that end with _ require a result value to be returned. They +// are for functions with non-void result type. The result value is usually ignored because of +// the exception and is only needed for syntactic correctness. The _0 ending is a shortcut for +// _(0) since this is a frequent case. Example: +// +// int result = this_function_may_trap(x_arg, y_arg, CHECK_0); +// +// CAUTION: make sure that the function call using a CHECK macro is not the only statement of a +// conditional branch w/o enclosing {} braces, since the CHECK macros expand into several state- +// ments! + +#define PENDING_EXCEPTION (((ThreadShadow*)THREAD)->pending_exception()) +#define HAS_PENDING_EXCEPTION (((ThreadShadow*)THREAD)->has_pending_exception()) +#define CLEAR_PENDING_EXCEPTION (((ThreadShadow*)THREAD)->clear_pending_exception()) + +#define CHECK THREAD); if (HAS_PENDING_EXCEPTION) return ; (0 +#define CHECK_(result) THREAD); if (HAS_PENDING_EXCEPTION) return result; (0 +#define CHECK_0 CHECK_(0) +#define CHECK_NH CHECK_(Handle()) +#define CHECK_NULL CHECK_(NULL) +#define CHECK_false CHECK_(false) + +// The THROW... macros should be used to throw an exception. They require a THREAD variable to be +// visible within the scope containing the THROW. Usually this is achieved by declaring the function +// with a TRAPS argument. + +#define THREAD_AND_LOCATION THREAD, __FILE__, __LINE__ + +#define THROW_OOP(e) \ + { Exceptions::_throw_oop(THREAD_AND_LOCATION, e); return; } + +#define THROW_HANDLE(e) \ + { Exceptions::_throw(THREAD_AND_LOCATION, e); return; } + +#define THROW(name) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, NULL); return; } + +#define THROW_MSG(name, message) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, message); return; } + +#define THROW_MSG_LOADER(name, message, loader, protection_domain) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, message, loader, protection_domain); return; } + +#define THROW_ARG(name, signature, args) \ + { Exceptions::_throw_args(THREAD_AND_LOCATION, name, signature, args); return; } + +#define THROW_OOP_(e, result) \ + { Exceptions::_throw_oop(THREAD_AND_LOCATION, e); return result; } + +#define THROW_HANDLE_(e, result) \ + { Exceptions::_throw(THREAD_AND_LOCATION, e); return result; } + +#define THROW_(name, result) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, NULL); return result; } + +#define THROW_MSG_(name, message, result) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, message); return result; } + +#define THROW_MSG_LOADER_(name, message, loader, protection_domain, result) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, message, loader, protection_domain); return result; } + +#define THROW_ARG_(name, signature, args, result) \ + { Exceptions::_throw_args(THREAD_AND_LOCATION, name, signature, args); return result; } + +#define THROW_MSG_CAUSE_(name, message, cause, result) \ + { Exceptions::_throw_msg_cause(THREAD_AND_LOCATION, name, message, cause); return result; } + + +#define THROW_OOP_0(e) THROW_OOP_(e, 0) +#define THROW_HANDLE_0(e) THROW_HANDLE_(e, 0) +#define THROW_0(name) THROW_(name, 0) +#define THROW_MSG_0(name, message) THROW_MSG_(name, message, 0) +#define THROW_WRAPPED_0(name, oop_to_wrap) THROW_WRAPPED_(name, oop_to_wrap, 0) +#define THROW_ARG_0(name, signature, arg) THROW_ARG_(name, signature, arg, 0) +#define THROW_MSG_CAUSE_0(name, message, cause) THROW_MSG_CAUSE_(name, message, cause, 0) + +// The CATCH macro checks that no exception has been thrown by a function; it is used at +// call sites about which is statically known that the callee cannot throw an exception +// even though it is declared with TRAPS. + +#define CATCH \ + THREAD); if (HAS_PENDING_EXCEPTION) { \ + oop ex = PENDING_EXCEPTION; \ + CLEAR_PENDING_EXCEPTION; \ + ex->print(); \ + ShouldNotReachHere(); \ + } (0 + + +// ExceptionMark is a stack-allocated helper class for local exception handling. +// It is used with the EXCEPTION_MARK macro. + +class ExceptionMark { + private: + Thread* _thread; + + public: + ExceptionMark(Thread*& thread); + ~ExceptionMark(); +}; + + + +// Use an EXCEPTION_MARK for 'local' exceptions. EXCEPTION_MARK makes sure that no +// pending exception exists upon entering its scope and tests that no pending exception +// exists when leaving the scope. + +// See also preserveException.hpp for PRESERVE_EXCEPTION_MARK macro, +// which preserves pre-existing exceptions and does not allow new +// exceptions. + +#define EXCEPTION_MARK Thread* THREAD; ExceptionMark __em(THREAD); diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.cpp b/hotspot/src/share/vm/utilities/globalDefinitions.cpp new file mode 100644 index 00000000000..12edbeff792 --- /dev/null +++ b/hotspot/src/share/vm/utilities/globalDefinitions.cpp @@ -0,0 +1,297 @@ +/* + * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_globalDefinitions.cpp.incl" + + +// Basic error support + +void basic_fatal(const char* msg) { + fatal(msg); +} + + +// Something to help porters sleep at night + +void check_basic_types() { +#ifdef ASSERT +#ifdef _LP64 + assert(min_intx == (intx)CONST64(0x8000000000000000), "correct constant"); + assert(max_intx == CONST64(0x7FFFFFFFFFFFFFFF), "correct constant"); + assert(max_uintx == CONST64(0xFFFFFFFFFFFFFFFF), "correct constant"); + assert( 8 == sizeof( intx), "wrong size for basic type"); + assert( 8 == sizeof( jobject), "wrong size for basic type"); +#else + assert(min_intx == (intx)0x80000000, "correct constant"); + assert(max_intx == 0x7FFFFFFF, "correct constant"); + assert(max_uintx == 0xFFFFFFFF, "correct constant"); + assert( 4 == sizeof( intx), "wrong size for basic type"); + assert( 4 == sizeof( jobject), "wrong size for basic type"); +#endif + assert( (~max_juint) == 0, "max_juint has all its bits"); + assert( (~max_uintx) == 0, "max_uintx has all its bits"); + assert( (~max_julong) == 0, "max_julong has all its bits"); + assert( 1 == sizeof( jbyte), "wrong size for basic type"); + assert( 2 == sizeof( jchar), "wrong size for basic type"); + assert( 2 == sizeof( jshort), "wrong size for basic type"); + assert( 4 == sizeof( juint), "wrong size for basic type"); + assert( 4 == sizeof( jint), "wrong size for basic type"); + assert( 1 == sizeof( jboolean), "wrong size for basic type"); + assert( 8 == sizeof( jlong), "wrong size for basic type"); + assert( 4 == sizeof( jfloat), "wrong size for basic type"); + assert( 8 == sizeof( jdouble), "wrong size for basic type"); + assert( 1 == sizeof( u1), "wrong size for basic type"); + assert( 2 == sizeof( u2), "wrong size for basic type"); + assert( 4 == sizeof( u4), "wrong size for basic type"); + + int num_type_chars = 0; + for (int i = 0; i < 99; i++) { + if (type2char((BasicType)i) != 0) { + assert(char2type(type2char((BasicType)i)) == i, "proper inverses"); + num_type_chars++; + } + } + assert(num_type_chars == 11, "must have tested the right number of mappings"); + assert(char2type(0) == T_ILLEGAL, "correct illegality"); + + { + for (int i = T_BOOLEAN; i <= T_CONFLICT; i++) { + BasicType vt = (BasicType)i; + BasicType ft = type2field[vt]; + switch (vt) { + // the following types might plausibly show up in memory layouts: + case T_BOOLEAN: + case T_BYTE: + case T_CHAR: + case T_SHORT: + case T_INT: + case T_FLOAT: + case T_DOUBLE: + case T_LONG: + case T_OBJECT: + case T_ADDRESS: // random raw pointer + case T_CONFLICT: // might as well support a bottom type + case T_VOID: // padding or other unaddressed word + // layout type must map to itself + assert(vt == ft, ""); + break; + default: + // non-layout type must map to a (different) layout type + assert(vt != ft, ""); + assert(ft == type2field[ft], ""); + } + // every type must map to same-sized layout type: + assert(type2size[vt] == type2size[ft], ""); + } + } + // These are assumed, e.g., when filling HeapWords with juints. + assert(is_power_of_2(sizeof(juint)), "juint must be power of 2"); + assert(is_power_of_2(HeapWordSize), "HeapWordSize must be power of 2"); + assert((size_t)HeapWordSize >= sizeof(juint), + "HeapWord should be at least as large as juint"); + assert(sizeof(NULL) == sizeof(char*), "NULL must be same size as pointer"); +#endif + + if( JavaPriority1_To_OSPriority != -1 ) + os::java_to_os_priority[1] = JavaPriority1_To_OSPriority; + if( JavaPriority2_To_OSPriority != -1 ) + os::java_to_os_priority[2] = JavaPriority2_To_OSPriority; + if( JavaPriority3_To_OSPriority != -1 ) + os::java_to_os_priority[3] = JavaPriority3_To_OSPriority; + if( JavaPriority4_To_OSPriority != -1 ) + os::java_to_os_priority[4] = JavaPriority4_To_OSPriority; + if( JavaPriority5_To_OSPriority != -1 ) + os::java_to_os_priority[5] = JavaPriority5_To_OSPriority; + if( JavaPriority6_To_OSPriority != -1 ) + os::java_to_os_priority[6] = JavaPriority6_To_OSPriority; + if( JavaPriority7_To_OSPriority != -1 ) + os::java_to_os_priority[7] = JavaPriority7_To_OSPriority; + if( JavaPriority8_To_OSPriority != -1 ) + os::java_to_os_priority[8] = JavaPriority8_To_OSPriority; + if( JavaPriority9_To_OSPriority != -1 ) + os::java_to_os_priority[9] = JavaPriority9_To_OSPriority; + if(JavaPriority10_To_OSPriority != -1 ) + os::java_to_os_priority[10] = JavaPriority10_To_OSPriority; +} + + +// Map BasicType to signature character +char type2char_tab[T_CONFLICT+1]={ 0, 0, 0, 0, 'Z', 'C', 'F', 'D', 'B', 'S', 'I', 'J', 'L', '[', 'V', 0, 0}; + +// Map BasicType to Java type name +const char* type2name_tab[T_CONFLICT+1] = { + NULL, NULL, NULL, NULL, + "boolean", + "char", + "float", + "double", + "byte", + "short", + "int", + "long", + "object", + "array", + "void", + "*address*", + "*conflict*" +}; + + +BasicType name2type(const char* name) { + for (int i = T_BOOLEAN; i <= T_VOID; i++) { + BasicType t = (BasicType)i; + if (type2name_tab[t] != NULL && 0 == strcmp(type2name_tab[t], name)) + return t; + } + return T_ILLEGAL; +} + + +// Map BasicType to size in words +int type2size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, -1}; + +BasicType type2field[T_CONFLICT+1] = { + (BasicType)0, // 0, + (BasicType)0, // 1, + (BasicType)0, // 2, + (BasicType)0, // 3, + T_BOOLEAN, // T_BOOLEAN = 4, + T_CHAR, // T_CHAR = 5, + T_FLOAT, // T_FLOAT = 6, + T_DOUBLE, // T_DOUBLE = 7, + T_BYTE, // T_BYTE = 8, + T_SHORT, // T_SHORT = 9, + T_INT, // T_INT = 10, + T_LONG, // T_LONG = 11, + T_OBJECT, // T_OBJECT = 12, + T_OBJECT, // T_ARRAY = 13, + T_VOID, // T_VOID = 14, + T_ADDRESS, // T_ADDRESS = 15, + T_CONFLICT // T_CONFLICT = 16, +}; + + +BasicType type2wfield[T_CONFLICT+1] = { + (BasicType)0, // 0, + (BasicType)0, // 1, + (BasicType)0, // 2, + (BasicType)0, // 3, + T_INT, // T_BOOLEAN = 4, + T_INT, // T_CHAR = 5, + T_FLOAT, // T_FLOAT = 6, + T_DOUBLE, // T_DOUBLE = 7, + T_INT, // T_BYTE = 8, + T_INT, // T_SHORT = 9, + T_INT, // T_INT = 10, + T_LONG, // T_LONG = 11, + T_OBJECT, // T_OBJECT = 12, + T_OBJECT, // T_ARRAY = 13, + T_VOID, // T_VOID = 14, + T_ADDRESS, // T_ADDRESS = 15, + T_CONFLICT // T_CONFLICT = 16, +}; + + +int type2aelembytes[T_CONFLICT+1] = { + 0, // 0 + 0, // 1 + 0, // 2 + 0, // 3 + T_BOOLEAN_aelem_bytes, // T_BOOLEAN = 4, + T_CHAR_aelem_bytes, // T_CHAR = 5, + T_FLOAT_aelem_bytes, // T_FLOAT = 6, + T_DOUBLE_aelem_bytes, // T_DOUBLE = 7, + T_BYTE_aelem_bytes, // T_BYTE = 8, + T_SHORT_aelem_bytes, // T_SHORT = 9, + T_INT_aelem_bytes, // T_INT = 10, + T_LONG_aelem_bytes, // T_LONG = 11, + T_OBJECT_aelem_bytes, // T_OBJECT = 12, + T_ARRAY_aelem_bytes, // T_ARRAY = 13, + 0, // T_VOID = 14, + T_INT_aelem_bytes, // T_ADDRESS = 15, + 0 // T_CONFLICT = 16, +}; + + +// Support for 64-bit integer arithmetic + +// The following code is mostly taken from JVM typedefs_md.h and system_md.c + +static const jlong high_bit = (jlong)1 << (jlong)63; +static const jlong other_bits = ~high_bit; + +jlong float2long(jfloat f) { + jlong tmp = (jlong) f; + if (tmp != high_bit) { + return tmp; + } else { + if (g_isnan((jdouble)f)) { + return 0; + } + if (f < 0) { + return high_bit; + } else { + return other_bits; + } + } +} + + +jlong double2long(jdouble f) { + jlong tmp = (jlong) f; + if (tmp != high_bit) { + return tmp; + } else { + if (g_isnan(f)) { + return 0; + } + if (f < 0) { + return high_bit; + } else { + return other_bits; + } + } +} + +// least common multiple +size_t lcm(size_t a, size_t b) { + size_t cur, div, next; + + cur = MAX2(a, b); + div = MIN2(a, b); + + assert(div != 0, "lcm requires positive arguments"); + + + while ((next = cur % div) != 0) { + cur = div; div = next; + } + + + julong result = julong(a) * b / div; + assert(result <= (size_t)max_uintx, "Integer overflow in lcm"); + + return size_t(result); +} diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.hpp b/hotspot/src/share/vm/utilities/globalDefinitions.hpp new file mode 100644 index 00000000000..460cd1f345e --- /dev/null +++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp @@ -0,0 +1,1101 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds all globally used constants & types, class (forward) +// declarations and a few frequently used utility functions. + +//---------------------------------------------------------------------------------------------------- +// Constants + +const int LogBytesPerShort = 1; +const int LogBytesPerInt = 2; +#ifdef _LP64 +const int LogBytesPerWord = 3; +#else +const int LogBytesPerWord = 2; +#endif +const int LogBytesPerLong = 3; + +const int BytesPerShort = 1 << LogBytesPerShort; +const int BytesPerInt = 1 << LogBytesPerInt; +const int BytesPerWord = 1 << LogBytesPerWord; +const int BytesPerLong = 1 << LogBytesPerLong; + +const int LogBitsPerByte = 3; +const int LogBitsPerShort = LogBitsPerByte + LogBytesPerShort; +const int LogBitsPerInt = LogBitsPerByte + LogBytesPerInt; +const int LogBitsPerWord = LogBitsPerByte + LogBytesPerWord; +const int LogBitsPerLong = LogBitsPerByte + LogBytesPerLong; + +const int BitsPerByte = 1 << LogBitsPerByte; +const int BitsPerShort = 1 << LogBitsPerShort; +const int BitsPerInt = 1 << LogBitsPerInt; +const int BitsPerWord = 1 << LogBitsPerWord; +const int BitsPerLong = 1 << LogBitsPerLong; + +const int WordAlignmentMask = (1 << LogBytesPerWord) - 1; +const int LongAlignmentMask = (1 << LogBytesPerLong) - 1; + +const int WordsPerLong = 2; // Number of stack entries for longs + +const int oopSize = sizeof(char*); +const int wordSize = sizeof(char*); +const int longSize = sizeof(jlong); +const int jintSize = sizeof(jint); +const int size_tSize = sizeof(size_t); + +// Size of a char[] needed to represent a jint as a string in decimal. +const int jintAsStringSize = 12; + +const int LogBytesPerOop = LogBytesPerWord; +const int LogBitsPerOop = LogBitsPerWord; +const int BytesPerOop = 1 << LogBytesPerOop; +const int BitsPerOop = 1 << LogBitsPerOop; + +const int BitsPerJavaInteger = 32; +const int BitsPerSize_t = size_tSize * BitsPerByte; + +// In fact this should be +// log2_intptr(sizeof(class JavaThread)) - log2_intptr(64); +// see os::set_memory_serialize_page() +#ifdef _LP64 +const int SerializePageShiftCount = 4; +#else +const int SerializePageShiftCount = 3; +#endif + +// An opaque struct of heap-word width, so that HeapWord* can be a generic +// pointer into the heap. We require that object sizes be measured in +// units of heap words, so that that +// HeapWord* hw; +// hw += oop(hw)->foo(); +// works, where foo is a method (like size or scavenge) that returns the +// object size. +class HeapWord { + friend class VMStructs; +private: + char* i; +}; + +// HeapWordSize must be 2^LogHeapWordSize. +const int HeapWordSize = sizeof(HeapWord); +#ifdef _LP64 +const int LogHeapWordSize = 3; +#else +const int LogHeapWordSize = 2; +#endif +const int HeapWordsPerOop = oopSize / HeapWordSize; +const int HeapWordsPerLong = BytesPerLong / HeapWordSize; + +// The larger HeapWordSize for 64bit requires larger heaps +// for the same application running in 64bit. See bug 4967770. +// The minimum alignment to a heap word size is done. Other +// parts of the memory system may required additional alignment +// and are responsible for those alignments. +#ifdef _LP64 +#define ScaleForWordSize(x) align_size_down_((x) * 13 / 10, HeapWordSize) +#else +#define ScaleForWordSize(x) (x) +#endif + +// The minimum number of native machine words necessary to contain "byte_size" +// bytes. +inline size_t heap_word_size(size_t byte_size) { + return (byte_size + (HeapWordSize-1)) >> LogHeapWordSize; +} + + +const size_t K = 1024; +const size_t M = K*K; +const size_t G = M*K; +const size_t HWperKB = K / sizeof(HeapWord); + +const jint min_jint = (jint)1 << (sizeof(jint)*BitsPerByte-1); // 0x80000000 == smallest jint +const jint max_jint = (juint)min_jint - 1; // 0x7FFFFFFF == largest jint + +// Constants for converting from a base unit to milli-base units. For +// example from seconds to milliseconds and microseconds + +const int MILLIUNITS = 1000; // milli units per base unit +const int MICROUNITS = 1000000; // micro units per base unit +const int NANOUNITS = 1000000000; // nano units per base unit + +inline const char* proper_unit_for_byte_size(size_t s) { + if (s >= 10*M) { + return "M"; + } else if (s >= 10*K) { + return "K"; + } else { + return "B"; + } +} + +inline size_t byte_size_in_proper_unit(size_t s) { + if (s >= 10*M) { + return s/M; + } else if (s >= 10*K) { + return s/K; + } else { + return s; + } +} + + +//---------------------------------------------------------------------------------------------------- +// VM type definitions + +// intx and uintx are the 'extended' int and 'extended' unsigned int types; +// they are 32bit wide on a 32-bit platform, and 64bit wide on a 64bit platform. + +typedef intptr_t intx; +typedef uintptr_t uintx; + +const intx min_intx = (intx)1 << (sizeof(intx)*BitsPerByte-1); +const intx max_intx = (uintx)min_intx - 1; +const uintx max_uintx = (uintx)-1; + +// Table of values: +// sizeof intx 4 8 +// min_intx 0x80000000 0x8000000000000000 +// max_intx 0x7FFFFFFF 0x7FFFFFFFFFFFFFFF +// max_uintx 0xFFFFFFFF 0xFFFFFFFFFFFFFFFF + +typedef unsigned int uint; NEEDS_CLEANUP + + +//---------------------------------------------------------------------------------------------------- +// Java type definitions + +// All kinds of 'plain' byte addresses +typedef signed char s_char; +typedef unsigned char u_char; +typedef u_char* address; +typedef uintptr_t address_word; // unsigned integer which will hold a pointer + // except for some implementations of a C++ + // linkage pointer to function. Should never + // need one of those to be placed in this + // type anyway. + +// Utility functions to "portably" (?) bit twiddle pointers +// Where portable means keep ANSI C++ compilers quiet + +inline address set_address_bits(address x, int m) { return address(intptr_t(x) | m); } +inline address clear_address_bits(address x, int m) { return address(intptr_t(x) & ~m); } + +// Utility functions to "portably" make cast to/from function pointers. + +inline address_word mask_address_bits(address x, int m) { return address_word(x) & m; } +inline address_word castable_address(address x) { return address_word(x) ; } +inline address_word castable_address(void* x) { return address_word(x) ; } + +// Pointer subtraction. +// The idea here is to avoid ptrdiff_t, which is signed and so doesn't have +// the range we might need to find differences from one end of the heap +// to the other. +// A typical use might be: +// if (pointer_delta(end(), top()) >= size) { +// // enough room for an object of size +// ... +// and then additions like +// ... top() + size ... +// are safe because we know that top() is at least size below end(). +inline size_t pointer_delta(const void* left, + const void* right, + size_t element_size) { + return (((uintptr_t) left) - ((uintptr_t) right)) / element_size; +} +// A version specialized for HeapWord*'s. +inline size_t pointer_delta(const HeapWord* left, const HeapWord* right) { + return pointer_delta(left, right, sizeof(HeapWord)); +} + +// +// ANSI C++ does not allow casting from one pointer type to a function pointer +// directly without at best a warning. This macro accomplishes it silently +// In every case that is present at this point the value be cast is a pointer +// to a C linkage function. In somecase the type used for the cast reflects +// that linkage and a picky compiler would not complain. In other cases because +// there is no convenient place to place a typedef with extern C linkage (i.e +// a platform dependent header file) it doesn't. At this point no compiler seems +// picky enough to catch these instances (which are few). It is possible that +// using templates could fix these for all cases. This use of templates is likely +// so far from the middle of the road that it is likely to be problematic in +// many C++ compilers. +// +#define CAST_TO_FN_PTR(func_type, value) ((func_type)(castable_address(value))) +#define CAST_FROM_FN_PTR(new_type, func_ptr) ((new_type)((address_word)(func_ptr))) + +// Unsigned byte types for os and stream.hpp + +// Unsigned one, two, four and eigth byte quantities used for describing +// the .class file format. See JVM book chapter 4. + +typedef jubyte u1; +typedef jushort u2; +typedef juint u4; +typedef julong u8; + +const jubyte max_jubyte = (jubyte)-1; // 0xFF largest jubyte +const jushort max_jushort = (jushort)-1; // 0xFFFF largest jushort +const juint max_juint = (juint)-1; // 0xFFFFFFFF largest juint +const julong max_julong = (julong)-1; // 0xFF....FF largest julong + +//---------------------------------------------------------------------------------------------------- +// JVM spec restrictions + +const int max_method_code_size = 64*K - 1; // JVM spec, 2nd ed. section 4.8.1 (p.134) + + +//---------------------------------------------------------------------------------------------------- +// HotSwap - for JVMTI aka Class File Replacement and PopFrame +// +// Determines whether on-the-fly class replacement and frame popping are enabled. + +#define HOTSWAP + +//---------------------------------------------------------------------------------------------------- +// Object alignment, in units of HeapWords. +// +// Minimum is max(BytesPerLong, BytesPerDouble, BytesPerOop) / HeapWordSize, so jlong, jdouble and +// reference fields can be naturally aligned. + +const int MinObjAlignment = HeapWordsPerLong; +const int MinObjAlignmentInBytes = MinObjAlignment * HeapWordSize; +const int MinObjAlignmentInBytesMask = MinObjAlignmentInBytes - 1; + +// Machine dependent stuff + +#include "incls/_globalDefinitions_pd.hpp.incl" + +// The byte alignment to be used by Arena::Amalloc. See bugid 4169348. +// Note: this value must be a power of 2 + +#define ARENA_AMALLOC_ALIGNMENT (2*BytesPerWord) + +// Signed variants of alignment helpers. There are two versions of each, a macro +// for use in places like enum definitions that require compile-time constant +// expressions and a function for all other places so as to get type checking. + +#define align_size_up_(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1)) + +inline intptr_t align_size_up(intptr_t size, intptr_t alignment) { + return align_size_up_(size, alignment); +} + +#define align_size_down_(size, alignment) ((size) & ~((alignment) - 1)) + +inline intptr_t align_size_down(intptr_t size, intptr_t alignment) { + return align_size_down_(size, alignment); +} + +// Align objects by rounding up their size, in HeapWord units. + +#define align_object_size_(size) align_size_up_(size, MinObjAlignment) + +inline intptr_t align_object_size(intptr_t size) { + return align_size_up(size, MinObjAlignment); +} + +// Pad out certain offsets to jlong alignment, in HeapWord units. + +#define align_object_offset_(offset) align_size_up_(offset, HeapWordsPerLong) + +inline intptr_t align_object_offset(intptr_t offset) { + return align_size_up(offset, HeapWordsPerLong); +} + +inline bool is_object_aligned(intptr_t offset) { + return offset == align_object_offset(offset); +} + + +//---------------------------------------------------------------------------------------------------- +// Utility macros for compilers +// used to silence compiler warnings + +#define Unused_Variable(var) var + + +//---------------------------------------------------------------------------------------------------- +// Miscellaneous + +// 6302670 Eliminate Hotspot __fabsf dependency +// All fabs() callers should call this function instead, which will implicitly +// convert the operand to double, avoiding a dependency on __fabsf which +// doesn't exist in early versions of Solaris 8. +inline double fabsd(double value) { + return fabs(value); +} + +inline jint low (jlong value) { return jint(value); } +inline jint high(jlong value) { return jint(value >> 32); } + +// the fancy casts are a hopefully portable way +// to do unsigned 32 to 64 bit type conversion +inline void set_low (jlong* value, jint low ) { *value &= (jlong)0xffffffff << 32; + *value |= (jlong)(julong)(juint)low; } + +inline void set_high(jlong* value, jint high) { *value &= (jlong)(julong)(juint)0xffffffff; + *value |= (jlong)high << 32; } + +inline jlong jlong_from(jint h, jint l) { + jlong result = 0; // initialization to avoid warning + set_high(&result, h); + set_low(&result, l); + return result; +} + +union jlong_accessor { + jint words[2]; + jlong long_value; +}; + +void check_basic_types(); // cannot define here; uses assert + + +// NOTE: replicated in SA in vm/agent/sun/jvm/hotspot/runtime/BasicType.java +enum BasicType { + T_BOOLEAN = 4, + T_CHAR = 5, + T_FLOAT = 6, + T_DOUBLE = 7, + T_BYTE = 8, + T_SHORT = 9, + T_INT = 10, + T_LONG = 11, + T_OBJECT = 12, + T_ARRAY = 13, + T_VOID = 14, + T_ADDRESS = 15, + T_CONFLICT = 16, // for stack value type with conflicting contents + T_ILLEGAL = 99 +}; + +// Convert a char from a classfile signature to a BasicType +inline BasicType char2type(char c) { + switch( c ) { + case 'B': return T_BYTE; + case 'C': return T_CHAR; + case 'D': return T_DOUBLE; + case 'F': return T_FLOAT; + case 'I': return T_INT; + case 'J': return T_LONG; + case 'S': return T_SHORT; + case 'Z': return T_BOOLEAN; + case 'V': return T_VOID; + case 'L': return T_OBJECT; + case '[': return T_ARRAY; + } + return T_ILLEGAL; +} + +extern char type2char_tab[T_CONFLICT+1]; // Map a BasicType to a jchar +inline char type2char(BasicType t) { return (uint)t < T_CONFLICT+1 ? type2char_tab[t] : 0; } +extern int type2size[T_CONFLICT+1]; // Map BasicType to result stack elements +extern const char* type2name_tab[T_CONFLICT+1]; // Map a BasicType to a jchar +inline const char* type2name(BasicType t) { return (uint)t < T_CONFLICT+1 ? type2name_tab[t] : NULL; } +extern BasicType name2type(const char* name); + +// Auxilary math routines +// least common multiple +extern size_t lcm(size_t a, size_t b); + + +// NOTE: replicated in SA in vm/agent/sun/jvm/hotspot/runtime/BasicType.java +enum BasicTypeSize { + T_BOOLEAN_size = 1, + T_CHAR_size = 1, + T_FLOAT_size = 1, + T_DOUBLE_size = 2, + T_BYTE_size = 1, + T_SHORT_size = 1, + T_INT_size = 1, + T_LONG_size = 2, + T_OBJECT_size = 1, + T_ARRAY_size = 1, + T_VOID_size = 0 +}; + + +// maps a BasicType to its instance field storage type: +// all sub-word integral types are widened to T_INT +extern BasicType type2field[T_CONFLICT+1]; +extern BasicType type2wfield[T_CONFLICT+1]; + + +// size in bytes +enum ArrayElementSize { + T_BOOLEAN_aelem_bytes = 1, + T_CHAR_aelem_bytes = 2, + T_FLOAT_aelem_bytes = 4, + T_DOUBLE_aelem_bytes = 8, + T_BYTE_aelem_bytes = 1, + T_SHORT_aelem_bytes = 2, + T_INT_aelem_bytes = 4, + T_LONG_aelem_bytes = 8, +#ifdef _LP64 + T_OBJECT_aelem_bytes = 8, + T_ARRAY_aelem_bytes = 8, +#else + T_OBJECT_aelem_bytes = 4, + T_ARRAY_aelem_bytes = 4, +#endif + T_VOID_aelem_bytes = 0 +}; + +extern int type2aelembytes[T_CONFLICT+1]; // maps a BasicType to nof bytes used by its array element + + +// JavaValue serves as a container for arbitrary Java values. + +class JavaValue { + + public: + typedef union JavaCallValue { + jfloat f; + jdouble d; + jint i; + jlong l; + jobject h; + } JavaCallValue; + + private: + BasicType _type; + JavaCallValue _value; + + public: + JavaValue(BasicType t = T_ILLEGAL) { _type = t; } + + JavaValue(jfloat value) { + _type = T_FLOAT; + _value.f = value; + } + + JavaValue(jdouble value) { + _type = T_DOUBLE; + _value.d = value; + } + + jfloat get_jfloat() const { return _value.f; } + jdouble get_jdouble() const { return _value.d; } + jint get_jint() const { return _value.i; } + jlong get_jlong() const { return _value.l; } + jobject get_jobject() const { return _value.h; } + JavaCallValue* get_value_addr() { return &_value; } + BasicType get_type() const { return _type; } + + void set_jfloat(jfloat f) { _value.f = f;} + void set_jdouble(jdouble d) { _value.d = d;} + void set_jint(jint i) { _value.i = i;} + void set_jlong(jlong l) { _value.l = l;} + void set_jobject(jobject h) { _value.h = h;} + void set_type(BasicType t) { _type = t; } + + jboolean get_jboolean() const { return (jboolean) (_value.i);} + jbyte get_jbyte() const { return (jbyte) (_value.i);} + jchar get_jchar() const { return (jchar) (_value.i);} + jshort get_jshort() const { return (jshort) (_value.i);} + +}; + + +#define STACK_BIAS 0 +// V9 Sparc CPU's running in 64 Bit mode use a stack bias of 7ff +// in order to extend the reach of the stack pointer. +#if defined(SPARC) && defined(_LP64) +#undef STACK_BIAS +#define STACK_BIAS 0x7ff +#endif + + +// TosState describes the top-of-stack state before and after the execution of +// a bytecode or method. The top-of-stack value may be cached in one or more CPU +// registers. The TosState corresponds to the 'machine represention' of this cached +// value. There's 4 states corresponding to the JAVA types int, long, float & double +// as well as a 5th state in case the top-of-stack value is actually on the top +// of stack (in memory) and thus not cached. The atos state corresponds to the itos +// state when it comes to machine representation but is used separately for (oop) +// type specific operations (e.g. verification code). + +enum TosState { // describes the tos cache contents + btos = 0, // byte, bool tos cached + ctos = 1, // short, char tos cached + stos = 2, // short, char tos cached + itos = 3, // int tos cached + ltos = 4, // long tos cached + ftos = 5, // float tos cached + dtos = 6, // double tos cached + atos = 7, // object cached + vtos = 8, // tos not cached + number_of_states, + ilgl // illegal state: should not occur +}; + + +inline TosState as_TosState(BasicType type) { + switch (type) { + case T_BYTE : return btos; + case T_BOOLEAN: return btos; + case T_CHAR : return ctos; + case T_SHORT : return stos; + case T_INT : return itos; + case T_LONG : return ltos; + case T_FLOAT : return ftos; + case T_DOUBLE : return dtos; + case T_VOID : return vtos; + case T_ARRAY : // fall through + case T_OBJECT : return atos; + } + return ilgl; +} + + +// Helper function to convert BasicType info into TosState +// Note: Cannot define here as it uses global constant at the time being. +TosState as_TosState(BasicType type); + + +// ReferenceType is used to distinguish between java/lang/ref/Reference subclasses + +enum ReferenceType { + REF_NONE, // Regular class + REF_OTHER, // Subclass of java/lang/ref/Reference, but not subclass of one of the classes below + REF_SOFT, // Subclass of java/lang/ref/SoftReference + REF_WEAK, // Subclass of java/lang/ref/WeakReference + REF_FINAL, // Subclass of java/lang/ref/FinalReference + REF_PHANTOM // Subclass of java/lang/ref/PhantomReference +}; + + +// JavaThreadState keeps track of which part of the code a thread is executing in. This +// information is needed by the safepoint code. +// +// There are 4 essential states: +// +// _thread_new : Just started, but not executed init. code yet (most likely still in OS init code) +// _thread_in_native : In native code. This is a safepoint region, since all oops will be in jobject handles +// _thread_in_vm : Executing in the vm +// _thread_in_Java : Executing either interpreted or compiled Java code (or could be in a stub) +// +// Each state has an associated xxxx_trans state, which is an intermediate state used when a thread is in +// a transition from one state to another. These extra states makes it possible for the safepoint code to +// handle certain thread_states without having to suspend the thread - making the safepoint code faster. +// +// Given a state, the xxx_trans state can always be found by adding 1. +// +enum JavaThreadState { + _thread_uninitialized = 0, // should never happen (missing initialization) + _thread_new = 2, // just starting up, i.e., in process of being initialized + _thread_new_trans = 3, // corresponding transition state (not used, included for completness) + _thread_in_native = 4, // running in native code + _thread_in_native_trans = 5, // corresponding transition state + _thread_in_vm = 6, // running in VM + _thread_in_vm_trans = 7, // corresponding transition state + _thread_in_Java = 8, // running in Java or in stub code + _thread_in_Java_trans = 9, // corresponding transition state (not used, included for completness) + _thread_blocked = 10, // blocked in vm + _thread_blocked_trans = 11, // corresponding transition state + _thread_max_state = 12 // maximum thread state+1 - used for statistics allocation +}; + + +// Handy constants for deciding which compiler mode to use. +enum MethodCompilation { + InvocationEntryBci = -1, // i.e., not a on-stack replacement compilation + InvalidOSREntryBci = -2 +}; + +// Enumeration to distinguish tiers of compilation +enum CompLevel { + CompLevel_none = 0, + CompLevel_fast_compile = 1, + CompLevel_full_optimization = 2, + + CompLevel_highest_tier = CompLevel_full_optimization, +#ifdef TIERED + CompLevel_initial_compile = CompLevel_fast_compile +#else + CompLevel_initial_compile = CompLevel_full_optimization +#endif // TIERED +}; + +inline bool is_tier1_compile(int comp_level) { + return comp_level == CompLevel_fast_compile; +} +inline bool is_tier2_compile(int comp_level) { + return comp_level == CompLevel_full_optimization; +} +inline bool is_highest_tier_compile(int comp_level) { + return comp_level == CompLevel_highest_tier; +} + +//---------------------------------------------------------------------------------------------------- +// 'Forward' declarations of frequently used classes +// (in order to reduce interface dependencies & reduce +// number of unnecessary compilations after changes) + +class symbolTable; +class ClassFileStream; + +class Event; + +class Thread; +class VMThread; +class JavaThread; +class Threads; + +class VM_Operation; +class VMOperationQueue; + +class CodeBlob; +class nmethod; +class OSRAdapter; +class I2CAdapter; +class C2IAdapter; +class CompiledIC; +class relocInfo; +class ScopeDesc; +class PcDesc; + +class Recompiler; +class Recompilee; +class RecompilationPolicy; +class RFrame; +class CompiledRFrame; +class InterpretedRFrame; + +class frame; + +class vframe; +class javaVFrame; +class interpretedVFrame; +class compiledVFrame; +class deoptimizedVFrame; +class externalVFrame; +class entryVFrame; + +class RegisterMap; + +class Mutex; +class Monitor; +class BasicLock; +class BasicObjectLock; + +class PeriodicTask; + +class JavaCallWrapper; + +class oopDesc; + +class NativeCall; + +class zone; + +class StubQueue; + +class outputStream; + +class ResourceArea; + +class DebugInformationRecorder; +class ScopeValue; +class CompressedStream; +class DebugInfoReadStream; +class DebugInfoWriteStream; +class LocationValue; +class ConstantValue; +class IllegalValue; + +class PrivilegedElement; +class MonitorArray; + +class MonitorInfo; + +class OffsetClosure; +class OopMapCache; +class InterpreterOopMap; +class OopMapCacheEntry; +class OSThread; + +typedef int (*OSThreadStartFunc)(void*); + +class Space; + +class JavaValue; +class methodHandle; +class JavaCallArguments; + +// Basic support for errors (general debug facilities not defined at this point fo the include phase) + +extern void basic_fatal(const char* msg); + + +//---------------------------------------------------------------------------------------------------- +// Special constants for debugging + +const jint badInt = -3; // generic "bad int" value +const long badAddressVal = -2; // generic "bad address" value +const long badOopVal = -1; // generic "bad oop" value +const intptr_t badHeapOopVal = (intptr_t) CONST64(0x2BAD4B0BBAADBABE); // value used to zap heap after GC +const int badHandleValue = 0xBC; // value used to zap vm handle area +const int badResourceValue = 0xAB; // value used to zap resource area +const int freeBlockPad = 0xBA; // value used to pad freed blocks. +const int uninitBlockPad = 0xF1; // value used to zap newly malloc'd blocks. +const intptr_t badJNIHandleVal = (intptr_t) CONST64(0xFEFEFEFEFEFEFEFE); // value used to zap jni handle area +const juint badHeapWordVal = 0xBAADBABE; // value used to zap heap after GC +const int badCodeHeapNewVal= 0xCC; // value used to zap Code heap at allocation +const int badCodeHeapFreeVal = 0xDD; // value used to zap Code heap at deallocation + + +// (These must be implemented as #defines because C++ compilers are +// not obligated to inline non-integral constants!) +#define badAddress ((address)::badAddressVal) +#define badOop ((oop)::badOopVal) +#define badHeapWord (::badHeapWordVal) +#define badJNIHandle ((oop)::badJNIHandleVal) + + +//---------------------------------------------------------------------------------------------------- +// Utility functions for bitfield manipulations + +const intptr_t AllBits = ~0; // all bits set in a word +const intptr_t NoBits = 0; // no bits set in a word +const jlong NoLongBits = 0; // no bits set in a long +const intptr_t OneBit = 1; // only right_most bit set in a word + +// get a word with the n.th or the right-most or left-most n bits set +// (note: #define used only so that they can be used in enum constant definitions) +#define nth_bit(n) (n >= BitsPerWord ? 0 : OneBit << (n)) +#define right_n_bits(n) (nth_bit(n) - 1) +#define left_n_bits(n) (right_n_bits(n) << (n >= BitsPerWord ? 0 : (BitsPerWord - n))) + +// bit-operations using a mask m +inline void set_bits (intptr_t& x, intptr_t m) { x |= m; } +inline void clear_bits (intptr_t& x, intptr_t m) { x &= ~m; } +inline intptr_t mask_bits (intptr_t x, intptr_t m) { return x & m; } +inline jlong mask_long_bits (jlong x, jlong m) { return x & m; } +inline bool mask_bits_are_true (intptr_t flags, intptr_t mask) { return (flags & mask) == mask; } + +// bit-operations using the n.th bit +inline void set_nth_bit(intptr_t& x, int n) { set_bits (x, nth_bit(n)); } +inline void clear_nth_bit(intptr_t& x, int n) { clear_bits(x, nth_bit(n)); } +inline bool is_set_nth_bit(intptr_t x, int n) { return mask_bits (x, nth_bit(n)) != NoBits; } + +// returns the bitfield of x starting at start_bit_no with length field_length (no sign-extension!) +inline intptr_t bitfield(intptr_t x, int start_bit_no, int field_length) { + return mask_bits(x >> start_bit_no, right_n_bits(field_length)); +} + + +//---------------------------------------------------------------------------------------------------- +// Utility functions for integers + +// Avoid use of global min/max macros which may cause unwanted double +// evaluation of arguments. +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#define max(a,b) Do_not_use_max_use_MAX2_instead +#define min(a,b) Do_not_use_min_use_MIN2_instead + +// It is necessary to use templates here. Having normal overloaded +// functions does not work because it is necessary to provide both 32- +// and 64-bit overloaded functions, which does not work, and having +// explicitly-typed versions of these routines (i.e., MAX2I, MAX2L) +// will be even more error-prone than macros. +template inline T MAX2(T a, T b) { return (a > b) ? a : b; } +template inline T MIN2(T a, T b) { return (a < b) ? a : b; } +template inline T MAX3(T a, T b, T c) { return MAX2(MAX2(a, b), c); } +template inline T MIN3(T a, T b, T c) { return MIN2(MIN2(a, b), c); } +template inline T MAX4(T a, T b, T c, T d) { return MAX2(MAX3(a, b, c), d); } +template inline T MIN4(T a, T b, T c, T d) { return MIN2(MIN3(a, b, c), d); } + +template inline T ABS(T x) { return (x > 0) ? x : -x; } + +// true if x is a power of 2, false otherwise +inline bool is_power_of_2(intptr_t x) { + return ((x != NoBits) && (mask_bits(x, x - 1) == NoBits)); +} + +// long version of is_power_of_2 +inline bool is_power_of_2_long(jlong x) { + return ((x != NoLongBits) && (mask_long_bits(x, x - 1) == NoLongBits)); +} + +//* largest i such that 2^i <= x +// A negative value of 'x' will return '31' +inline int log2_intptr(intptr_t x) { + int i = -1; + uintptr_t p = 1; + while (p != 0 && p <= (uintptr_t)x) { + // p = 2^(i+1) && p <= x (i.e., 2^(i+1) <= x) + i++; p *= 2; + } + // p = 2^(i+1) && x < p (i.e., 2^i <= x < 2^(i+1)) + // (if p = 0 then overflow occured and i = 31) + return i; +} + +//* largest i such that 2^i <= x +// A negative value of 'x' will return '63' +inline int log2_long(jlong x) { + int i = -1; + julong p = 1; + while (p != 0 && p <= (julong)x) { + // p = 2^(i+1) && p <= x (i.e., 2^(i+1) <= x) + i++; p *= 2; + } + // p = 2^(i+1) && x < p (i.e., 2^i <= x < 2^(i+1)) + // (if p = 0 then overflow occured and i = 31) + return i; +} + +//* the argument must be exactly a power of 2 +inline int exact_log2(intptr_t x) { + #ifdef ASSERT + if (!is_power_of_2(x)) basic_fatal("x must be a power of 2"); + #endif + return log2_intptr(x); +} + + +// returns integer round-up to the nearest multiple of s (s must be a power of two) +inline intptr_t round_to(intptr_t x, uintx s) { + #ifdef ASSERT + if (!is_power_of_2(s)) basic_fatal("s must be a power of 2"); + #endif + const uintx m = s - 1; + return mask_bits(x + m, ~m); +} + +// returns integer round-down to the nearest multiple of s (s must be a power of two) +inline intptr_t round_down(intptr_t x, uintx s) { + #ifdef ASSERT + if (!is_power_of_2(s)) basic_fatal("s must be a power of 2"); + #endif + const uintx m = s - 1; + return mask_bits(x, ~m); +} + + +inline bool is_odd (intx x) { return x & 1; } +inline bool is_even(intx x) { return !is_odd(x); } + +// "to" should be greater than "from." +inline intx byte_size(void* from, void* to) { + return (address)to - (address)from; +} + +//---------------------------------------------------------------------------------------------------- +// Avoid non-portable casts with these routines (DEPRECATED) + +// NOTE: USE Bytes class INSTEAD WHERE POSSIBLE +// Bytes is optimized machine-specifically and may be much faster then the portable routines below. + +// Given sequence of four bytes, build into a 32-bit word +// following the conventions used in class files. +// On the 386, this could be realized with a simple address cast. +// + +// This routine takes eight bytes: +inline u8 build_u8_from( u1 c1, u1 c2, u1 c3, u1 c4, u1 c5, u1 c6, u1 c7, u1 c8 ) { + return ( u8(c1) << 56 ) & ( u8(0xff) << 56 ) + | ( u8(c2) << 48 ) & ( u8(0xff) << 48 ) + | ( u8(c3) << 40 ) & ( u8(0xff) << 40 ) + | ( u8(c4) << 32 ) & ( u8(0xff) << 32 ) + | ( u8(c5) << 24 ) & ( u8(0xff) << 24 ) + | ( u8(c6) << 16 ) & ( u8(0xff) << 16 ) + | ( u8(c7) << 8 ) & ( u8(0xff) << 8 ) + | ( u8(c8) << 0 ) & ( u8(0xff) << 0 ); +} + +// This routine takes four bytes: +inline u4 build_u4_from( u1 c1, u1 c2, u1 c3, u1 c4 ) { + return ( u4(c1) << 24 ) & 0xff000000 + | ( u4(c2) << 16 ) & 0x00ff0000 + | ( u4(c3) << 8 ) & 0x0000ff00 + | ( u4(c4) << 0 ) & 0x000000ff; +} + +// And this one works if the four bytes are contiguous in memory: +inline u4 build_u4_from( u1* p ) { + return build_u4_from( p[0], p[1], p[2], p[3] ); +} + +// Ditto for two-byte ints: +inline u2 build_u2_from( u1 c1, u1 c2 ) { + return u2(( u2(c1) << 8 ) & 0xff00 + | ( u2(c2) << 0 ) & 0x00ff); +} + +// And this one works if the two bytes are contiguous in memory: +inline u2 build_u2_from( u1* p ) { + return build_u2_from( p[0], p[1] ); +} + +// Ditto for floats: +inline jfloat build_float_from( u1 c1, u1 c2, u1 c3, u1 c4 ) { + u4 u = build_u4_from( c1, c2, c3, c4 ); + return *(jfloat*)&u; +} + +inline jfloat build_float_from( u1* p ) { + u4 u = build_u4_from( p ); + return *(jfloat*)&u; +} + + +// now (64-bit) longs + +inline jlong build_long_from( u1 c1, u1 c2, u1 c3, u1 c4, u1 c5, u1 c6, u1 c7, u1 c8 ) { + return ( jlong(c1) << 56 ) & ( jlong(0xff) << 56 ) + | ( jlong(c2) << 48 ) & ( jlong(0xff) << 48 ) + | ( jlong(c3) << 40 ) & ( jlong(0xff) << 40 ) + | ( jlong(c4) << 32 ) & ( jlong(0xff) << 32 ) + | ( jlong(c5) << 24 ) & ( jlong(0xff) << 24 ) + | ( jlong(c6) << 16 ) & ( jlong(0xff) << 16 ) + | ( jlong(c7) << 8 ) & ( jlong(0xff) << 8 ) + | ( jlong(c8) << 0 ) & ( jlong(0xff) << 0 ); +} + +inline jlong build_long_from( u1* p ) { + return build_long_from( p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7] ); +} + + +// Doubles, too! +inline jdouble build_double_from( u1 c1, u1 c2, u1 c3, u1 c4, u1 c5, u1 c6, u1 c7, u1 c8 ) { + jlong u = build_long_from( c1, c2, c3, c4, c5, c6, c7, c8 ); + return *(jdouble*)&u; +} + +inline jdouble build_double_from( u1* p ) { + jlong u = build_long_from( p ); + return *(jdouble*)&u; +} + + +// Portable routines to go the other way: + +inline void explode_short_to( u2 x, u1& c1, u1& c2 ) { + c1 = u1(x >> 8); + c2 = u1(x); +} + +inline void explode_short_to( u2 x, u1* p ) { + explode_short_to( x, p[0], p[1]); +} + +inline void explode_int_to( u4 x, u1& c1, u1& c2, u1& c3, u1& c4 ) { + c1 = u1(x >> 24); + c2 = u1(x >> 16); + c3 = u1(x >> 8); + c4 = u1(x); +} + +inline void explode_int_to( u4 x, u1* p ) { + explode_int_to( x, p[0], p[1], p[2], p[3]); +} + + +// Pack and extract shorts to/from ints: + +inline int extract_low_short_from_int(jint x) { + return x & 0xffff; +} + +inline int extract_high_short_from_int(jint x) { + return (x >> 16) & 0xffff; +} + +inline int build_int_from_shorts( jushort low, jushort high ) { + return ((int)((unsigned int)high << 16) | (unsigned int)low); +} + +// Printf-style formatters for fixed- and variable-width types as pointers and +// integers. +// +// Each compiler-specific definitions file (e.g., globalDefinitions_gcc.hpp) +// must define the macro FORMAT64_MODIFIER, which is the modifier for '%x' or +// '%d' formats to indicate a 64-bit quantity; commonly "l" (in LP64) or "ll" +// (in ILP32). + +// Format 32-bit quantities. +#define INT32_FORMAT "%d" +#define UINT32_FORMAT "%u" +#define INT32_FORMAT_W(width) "%" #width "d" +#define UINT32_FORMAT_W(width) "%" #width "u" + +#define PTR32_FORMAT "0x%08x" + +// Format 64-bit quantities. +#define INT64_FORMAT "%" FORMAT64_MODIFIER "d" +#define UINT64_FORMAT "%" FORMAT64_MODIFIER "u" +#define PTR64_FORMAT "0x%016" FORMAT64_MODIFIER "x" + +#define INT64_FORMAT_W(width) "%" #width FORMAT64_MODIFIER "d" +#define UINT64_FORMAT_W(width) "%" #width FORMAT64_MODIFIER "u" + +// Format macros that allow the field width to be specified. The width must be +// a string literal (e.g., "8") or a macro that evaluates to one. +#ifdef _LP64 +#define SSIZE_FORMAT_W(width) INT64_FORMAT_W(width) +#define SIZE_FORMAT_W(width) UINT64_FORMAT_W(width) +#else +#define SSIZE_FORMAT_W(width) INT32_FORMAT_W(width) +#define SIZE_FORMAT_W(width) UINT32_FORMAT_W(width) +#endif // _LP64 + +// Format pointers and size_t (or size_t-like integer types) which change size +// between 32- and 64-bit. +#ifdef _LP64 +#define PTR_FORMAT PTR64_FORMAT +#define UINTX_FORMAT UINT64_FORMAT +#define INTX_FORMAT INT64_FORMAT +#define SIZE_FORMAT UINT64_FORMAT +#define SSIZE_FORMAT INT64_FORMAT +#else // !_LP64 +#define PTR_FORMAT PTR32_FORMAT +#define UINTX_FORMAT UINT32_FORMAT +#define INTX_FORMAT INT32_FORMAT +#define SIZE_FORMAT UINT32_FORMAT +#define SSIZE_FORMAT INT32_FORMAT +#endif // _LP64 + +#define INTPTR_FORMAT PTR_FORMAT + +// Enable zap-a-lot if in debug version. + +# ifdef ASSERT +# ifdef COMPILER2 +# define ENABLE_ZAP_DEAD_LOCALS +#endif /* COMPILER2 */ +# endif /* ASSERT */ + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof((array)[0])) diff --git a/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp b/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp new file mode 100644 index 00000000000..417978f0891 --- /dev/null +++ b/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp @@ -0,0 +1,275 @@ +/* + * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds compiler-dependent includes, +// globally used constants & types, class (forward) +// declarations and a few frequently used utility functions. + +#include +#include +#include +#include +#include +#include +#include + +#ifdef SOLARIS +#include +#endif // SOLARIS + +#include +#ifndef FP_PZERO +// Linux doesn't have positive/negative zero +#define FP_PZERO FP_ZERO +#endif +#if (!defined fpclass) && ((!defined SPARC) || (!defined SOLARIS)) +#define fpclass fpclassify +#endif + +#include +#include +#include +#include + +#ifdef SOLARIS +#include +#endif // SOLARIS + +#include +#include + +#ifdef SOLARIS +#include +#include +#include +#include +#include +#endif // SOLARIS + +# ifdef SOLARIS_MUTATOR_LIBTHREAD +# include +# endif + +#ifdef LINUX +#include +#include +#include +#include +#endif // LINUX + +// 4810578: varargs unsafe on 32-bit integer/64-bit pointer architectures +// When __cplusplus is defined, NULL is defined as 0 (32-bit constant) in +// system header files. On 32-bit architectures, there is no problem. +// On 64-bit architectures, defining NULL as a 32-bit constant can cause +// problems with varargs functions: C++ integral promotion rules say for +// varargs, we pass the argument 0 as an int. So, if NULL was passed to a +// varargs function it will remain 32-bits. Depending on the calling +// convention of the machine, if the argument is passed on the stack then +// only 32-bits of the "NULL" pointer may be initialized to zero. The +// other 32-bits will be garbage. If the varargs function is expecting a +// pointer when it extracts the argument, then we have a problem. +// +// Solution: For 64-bit architectures, redefine NULL as 64-bit constant 0. +// +// Note: this fix doesn't work well on Linux because NULL will be overwritten +// whenever a system header file is included. Linux handles NULL correctly +// through a special type '__null'. +#ifdef SOLARIS + #ifdef _LP64 + #undef NULL + #define NULL 0L + #else + #ifndef NULL + #define NULL 0 + #endif + #endif +#endif + +// NULL vs NULL_WORD: +// On Linux NULL is defined as a special type '__null'. Assigning __null to +// integer variable will cause gcc warning. Use NULL_WORD in places where a +// pointer is stored as integer value. On some platforms, sizeof(intptr_t) > +// sizeof(void*), so here we want something which is integer type, but has the +// same size as a pointer. +#ifdef LINUX + #ifdef _LP64 + #define NULL_WORD 0L + #else + #define NULL_WORD 0 + #endif +#else + #define NULL_WORD NULL +#endif + +#ifndef LINUX +// Compiler-specific primitive types +typedef unsigned short uint16_t; +#ifndef _UINT32_T +#define _UINT32_T +typedef unsigned int uint32_t; +#endif // _UINT32_T + +#if !defined(_SYS_INT_TYPES_H) +#ifndef _UINT64_T +#define _UINT64_T +typedef unsigned long long uint64_t; +#endif // _UINT64_T +// %%%% how to access definition of intptr_t portably in 5.5 onward? +typedef int intptr_t; +typedef unsigned int uintptr_t; +// If this gets an error, figure out a symbol XXX that implies the +// prior definition of intptr_t, and add "&& !defined(XXX)" above. +#endif // _SYS_INT_TYPES_H + +#endif // !LINUX + +// Additional Java basic types + +typedef uint8_t jubyte; +typedef uint16_t jushort; +typedef uint32_t juint; +typedef uint64_t julong; + +//---------------------------------------------------------------------------------------------------- +// Special (possibly not-portable) casts +// Cast floats into same-size integers and vice-versa w/o changing bit-pattern +// %%%%%% These seem like standard C++ to me--how about factoring them out? - Ungar + +inline jint jint_cast (jfloat x) { return *(jint* )&x; } +inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } + +inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } +inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } + +//---------------------------------------------------------------------------------------------------- +// Constant for jlong (specifying an long long canstant is C++ compiler specific) + +// Build a 64bit integer constant +#define CONST64(x) (x ## LL) +#define UCONST64(x) (x ## ULL) + +const jlong min_jlong = CONST64(0x8000000000000000); +const jlong max_jlong = CONST64(0x7fffffffffffffff); + + +#ifdef SOLARIS +//---------------------------------------------------------------------------------------------------- +// ANSI C++ fixes +// NOTE:In the ANSI committee's continuing attempt to make each version +// of C++ incompatible with the previous version, you can no longer cast +// pointers to functions without specifying linkage unless you want to get +// warnings. +// +// This also means that pointers to functions can no longer be "hidden" +// in opaque types like void * because at the invokation point warnings +// will be generated. While this makes perfect sense from a type safety +// point of view it causes a lot of warnings on old code using C header +// files. Here are some typedefs to make the job of silencing warnings +// a bit easier. +// +// The final kick in the teeth is that you can only have extern "C" linkage +// specified at file scope. So these typedefs are here rather than in the +// .hpp for the class (os:Solaris usually) that needs them. + +extern "C" { + typedef int (*int_fnP_thread_t_iP_uP_stack_tP_gregset_t)(thread_t, int*, unsigned *, stack_t*, gregset_t); + typedef int (*int_fnP_thread_t_i_gregset_t)(thread_t, int, gregset_t); + typedef int (*int_fnP_thread_t_i)(thread_t, int); + typedef int (*int_fnP_thread_t)(thread_t); + + typedef int (*int_fnP_cond_tP_mutex_tP_timestruc_tP)(cond_t *cv, mutex_t *mx, timestruc_t *abst); + typedef int (*int_fnP_cond_tP_mutex_tP)(cond_t *cv, mutex_t *mx); + + // typedef for missing API in libc + typedef int (*int_fnP_mutex_tP_i_vP)(mutex_t *, int, void *); + typedef int (*int_fnP_mutex_tP)(mutex_t *); + typedef int (*int_fnP_cond_tP_i_vP)(cond_t *cv, int scope, void *arg); + typedef int (*int_fnP_cond_tP)(cond_t *cv); +}; +#endif // SOLARIS + +//---------------------------------------------------------------------------------------------------- +// Debugging + +#define DEBUG_EXCEPTION ::abort(); + +extern "C" void breakpoint(); +#define BREAKPOINT ::breakpoint() + +// checking for nanness +#ifdef SOLARIS +#ifdef SPARC +inline int g_isnan(float f) { return isnanf(f); } +#else +// isnanf() broken on Intel Solaris use isnand() +inline int g_isnan(float f) { return isnand(f); } +#endif +inline int g_isnan(double f) { return isnand(f); } +#elif LINUX +inline int g_isnan(float f) { return isnanf(f); } +inline int g_isnan(double f) { return isnan(f); } +#else +#error "missing platform-specific definition here" +#endif + +// Checking for finiteness + +inline int g_isfinite(jfloat f) { return finite(f); } +inline int g_isfinite(jdouble f) { return finite(f); } + + +// Wide characters + +inline int wcslen(const jchar* x) { return wcslen((const wchar_t*)x); } + + +// Portability macros +#define PRAGMA_INTERFACE #pragma interface +#define PRAGMA_IMPLEMENTATION #pragma implementation +#define VALUE_OBJ_CLASS_SPEC + +#if (__GNUC__ == 2) && (__GNUC_MINOR__ < 95) +#define TEMPLATE_TABLE_BUG +#endif +#if (__GNUC__ == 2) && (__GNUC_MINOR__ >= 96) +#define CONST_SDM_BUG +#endif + +// Formatting. +#ifdef _LP64 +#define FORMAT64_MODIFIER "l" +#else // !_LP64 +#define FORMAT64_MODIFIER "ll" +#endif // _LP64 + +// HACK: gcc warns about applying offsetof() to non-POD object or calculating +// offset directly when base address is NULL. Use 16 to get around the +// warning. gcc-3.4 has an option -Wno-invalid-offsetof to suppress +// this warning. +#define offset_of(klass,field) (size_t)((intx)&(((klass*)16)->field) - 16) + +#ifdef offsetof +# undef offsetof +#endif +#define offsetof(klass,field) offset_of(klass,field) diff --git a/hotspot/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp b/hotspot/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp new file mode 100644 index 00000000000..16ae1ce9b12 --- /dev/null +++ b/hotspot/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp @@ -0,0 +1,215 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds compiler-dependent includes, +// globally used constants & types, class (forward) +// declarations and a few frequently used utility functions. + + +# include +# include +# include +# include // for bsd'isms +# include +# include // for offsetof +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# ifdef SOLARIS_MUTATOR_LIBTHREAD +# include +# endif + +// 4810578: varargs unsafe on 32-bit integer/64-bit pointer architectures +// When __cplusplus is defined, NULL is defined as 0 (32-bit constant) in +// system header files. On 32-bit architectures, there is no problem. +// On 64-bit architectures, defining NULL as a 32-bit constant can cause +// problems with varargs functions: C++ integral promotion rules say for +// varargs, we pass the argument 0 as an int. So, if NULL was passed to a +// varargs function it will remain 32-bits. Depending on the calling +// convention of the machine, if the argument is passed on the stack then +// only 32-bits of the "NULL" pointer may be initialized to zero. The +// other 32-bits will be garbage. If the varargs function is expecting a +// pointer when it extracts the argument, then we have a problem. +// +// Solution: For 64-bit architectures, redefine NULL as 64-bit constant 0. +#ifdef _LP64 +#undef NULL +#define NULL 0L +#else +#ifndef NULL +#define NULL 0 +#endif +#endif + +// NULL vs NULL_WORD: +// On Linux NULL is defined as a special type '__null'. Assigning __null to +// integer variable will cause gcc warning. Use NULL_WORD in places where a +// pointer is stored as integer value. +#define NULL_WORD NULL + +// Compiler-specific primitive types +typedef unsigned short uint16_t; +#ifndef _UINT32_T +#define _UINT32_T +typedef unsigned int uint32_t; +#endif +#if !defined(_SYS_INT_TYPES_H) +#ifndef _UINT64_T +#define _UINT64_T +typedef unsigned long long uint64_t; +#endif +// %%%% how to access definition of intptr_t portably in 5.5 onward? +typedef int intptr_t; +typedef unsigned int uintptr_t; +// If this gets an error, figure out a symbol XXX that implies the +// prior definition of intptr_t, and add "&& !defined(XXX)" above. +#endif + +// Additional Java basic types + +typedef unsigned char jubyte; +typedef unsigned short jushort; +typedef unsigned int juint; +typedef unsigned long long julong; + +//---------------------------------------------------------------------------------------------------- +// Special (possibly not-portable) casts +// Cast floats into same-size integers and vice-versa w/o changing bit-pattern + +inline jint jint_cast (jfloat x) { return *(jint* )&x; } +inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } + +inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } +inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } + +//---------------------------------------------------------------------------------------------------- +// Constant for jlong (specifying an long long constant is C++ compiler specific) + +// Build a 64bit integer constant +#define CONST64(x) (x ## LL) +#define UCONST64(x) (x ## ULL) + +const jlong min_jlong = CONST64(0x8000000000000000); +const jlong max_jlong = CONST64(0x7fffffffffffffff); + + +//---------------------------------------------------------------------------------------------------- +// ANSI C++ fixes +// NOTE:In the ANSI committee's continuing attempt to make each version +// of C++ incompatible with the previous version, you can no longer cast +// pointers to functions without specifying linkage unless you want to get +// warnings. +// +// This also means that pointers to functions can no longer be "hidden" +// in opaque types like void * because at the invokation point warnings +// will be generated. While this makes perfect sense from a type safety +// point of view it causes a lot of warnings on old code using C header +// files. Here are some typedefs to make the job of silencing warnings +// a bit easier. +// +// The final kick in the teeth is that you can only have extern "C" linkage +// specified at file scope. So these typedefs are here rather than in the +// .hpp for the class (os:Solaris usually) that needs them. + +extern "C" { + typedef int (*int_fnP_thread_t_iP_uP_stack_tP_gregset_t)(thread_t, int*, unsigned *, stack_t*, gregset_t); + typedef int (*int_fnP_thread_t_i_gregset_t)(thread_t, int, gregset_t); + typedef int (*int_fnP_thread_t_i)(thread_t, int); + typedef int (*int_fnP_thread_t)(thread_t); + + typedef int (*int_fnP_cond_tP_mutex_tP_timestruc_tP)(cond_t *cv, mutex_t *mx, timestruc_t *abst); + typedef int (*int_fnP_cond_tP_mutex_tP)(cond_t *cv, mutex_t *mx); + + // typedef for missing API in libc + typedef int (*int_fnP_mutex_tP_i_vP)(mutex_t *, int, void *); + typedef int (*int_fnP_mutex_tP)(mutex_t *); + typedef int (*int_fnP_cond_tP_i_vP)(cond_t *cv, int scope, void *arg); + typedef int (*int_fnP_cond_tP)(cond_t *cv); +}; + + +//---------------------------------------------------------------------------------------------------- +// Debugging + +#define DEBUG_EXCEPTION ::abort(); + +extern "C" void breakpoint(); +#define BREAKPOINT ::breakpoint() + +// checking for nanness + +#ifdef SPARC +inline int g_isnan(float f) { return isnanf(f); } +#else +// isnanf() broken on Intel Solaris use isnand() +inline int g_isnan(float f) { return isnand(f); } +#endif + +inline int g_isnan(double f) { return isnand(f); } + +// Checking for finiteness + +inline int g_isfinite(jfloat f) { return finite(f); } +inline int g_isfinite(jdouble f) { return finite(f); } + + +// Wide characters + +inline int wcslen(const jchar* x) { return wcslen((const wchar_t*)x); } + + +// Misc +int local_vsnprintf(char* buf, size_t count, const char* fmt, va_list argptr); +#define vsnprintf local_vsnprintf + + +// Portability macros +#define PRAGMA_INTERFACE +#define PRAGMA_IMPLEMENTATION +#define PRAGMA_IMPLEMENTATION_(arg) +#define VALUE_OBJ_CLASS_SPEC : public _ValueObj + +// Formatting. +#ifdef _LP64 +#define FORMAT64_MODIFIER "l" +#else // !_LP64 +#define FORMAT64_MODIFIER "ll" +#endif // _LP64 + +#define offset_of(klass,field) offsetof(klass,field) diff --git a/hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp b/hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp new file mode 100644 index 00000000000..6b4804ec565 --- /dev/null +++ b/hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp @@ -0,0 +1,193 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file holds compiler-dependent includes, +// globally used constants & types, class (forward) +// declarations and a few frequently used utility functions. + +# include +# include +# include +# include +# include // for offsetof +# include // for stream.cpp +# include // for _isnan +# include // for va_list +# include +# include +// Need this on windows to get the math constants (e.g., M_PI). +#define _USE_MATH_DEFINES +# include + +// 4810578: varargs unsafe on 32-bit integer/64-bit pointer architectures +// When __cplusplus is defined, NULL is defined as 0 (32-bit constant) in +// system header files. On 32-bit architectures, there is no problem. +// On 64-bit architectures, defining NULL as a 32-bit constant can cause +// problems with varargs functions: C++ integral promotion rules say for +// varargs, we pass the argument 0 as an int. So, if NULL was passed to a +// varargs function it will remain 32-bits. Depending on the calling +// convention of the machine, if the argument is passed on the stack then +// only 32-bits of the "NULL" pointer may be initialized to zero. The +// other 32-bits will be garbage. If the varargs function is expecting a +// pointer when it extracts the argument, then we may have a problem. +// +// Solution: For 64-bit architectures, redefine NULL as 64-bit constant 0. +#ifdef _LP64 +#undef NULL +// 64-bit Windows uses a P64 data model (not LP64, although we define _LP64) +// Since longs are 32-bit we cannot use 0L here. Use the Visual C++ specific +// 64-bit integer-suffix (i64) instead. +#define NULL 0i64 +#else +#ifndef NULL +#define NULL 0 +#endif +#endif + +// NULL vs NULL_WORD: +// On Linux NULL is defined as a special type '__null'. Assigning __null to +// integer variable will cause gcc warning. Use NULL_WORD in places where a +// pointer is stored as integer value. +#define NULL_WORD NULL + +// Compiler-specific primitive types +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#ifdef _WIN64 +typedef unsigned __int64 uintptr_t; +#else +typedef unsigned int uintptr_t; +#endif +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef signed __int64 int64_t; +#ifdef _WIN64 +typedef signed __int64 intptr_t; +typedef signed __int64 ssize_t; +#else +typedef signed int intptr_t; +typedef signed int ssize_t; +#endif + +//---------------------------------------------------------------------------------------------------- +// Additional Java basic types + +typedef unsigned char jubyte; +typedef unsigned short jushort; +typedef unsigned int juint; +typedef unsigned __int64 julong; + +//---------------------------------------------------------------------------------------------------- +// Special (possibly not-portable) casts +// Cast floats into same-size integers and vice-versa w/o changing bit-pattern + +inline jint jint_cast (jfloat x) { return *(jint* )&x; } +inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } + +inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } +inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } + + +//---------------------------------------------------------------------------------------------------- +// Non-standard stdlib-like stuff: +inline int strcasecmp(const char *s1, const char *s2) { return _stricmp(s1,s2); } + + +//---------------------------------------------------------------------------------------------------- +// Debugging + +#if _WIN64 +extern "C" void breakpoint(); +#define BREAKPOINT ::breakpoint() +#else +#define BREAKPOINT __asm { int 3 } +#endif + +//---------------------------------------------------------------------------------------------------- +// Checking for nanness + +inline int g_isnan(jfloat f) { return _isnan(f); } +inline int g_isnan(jdouble f) { return _isnan(f); } + +//---------------------------------------------------------------------------------------------------- +// Checking for finiteness + +inline int g_isfinite(jfloat f) { return _finite(f); } +inline int g_isfinite(jdouble f) { return _finite(f); } + +//---------------------------------------------------------------------------------------------------- +// Constant for jlong (specifying an long long constant is C++ compiler specific) + +// Build a 64bit integer constant on with Visual C++ +#define CONST64(x) (x ## i64) +#define UCONST64(x) ((uint64_t)CONST64(x)) + +const jlong min_jlong = CONST64(0x8000000000000000); +const jlong max_jlong = CONST64(0x7fffffffffffffff); + +//---------------------------------------------------------------------------------------------------- +// Miscellaneous + +inline int vsnprintf(char* buf, size_t count, const char* fmt, va_list argptr) { + // If number of characters written == count, Windows doesn't write a + // terminating NULL, so we do it ourselves. + int ret = _vsnprintf(buf, count, fmt, argptr); + if (count > 0) buf[count-1] = '\0'; + return ret; +} + +// Visual Studio 2005 deprecates POSIX names - use ISO C++ names instead +#if _MSC_VER >= 1400 && !defined(_WIN64) +#define open _open +#define close _close +#define read _read +#define write _write +#define lseek _lseek +#define unlink _unlink +#define strdup _strdup +#endif + +#pragma warning( disable : 4100 ) // unreferenced formal parameter +#pragma warning( disable : 4127 ) // conditional expression is constant +#pragma warning( disable : 4514 ) // unreferenced inline function has been removed +#pragma warning( disable : 4244 ) // possible loss of data +#pragma warning( disable : 4512 ) // assignment operator could not be generated +#pragma warning( disable : 4201 ) // nonstandard extension used : nameless struct/union (needed in windows.h) +#pragma warning( disable : 4511 ) // copy constructor could not be generated +#pragma warning( disable : 4291 ) // no matching operator delete found; memory will not be freed if initialization thows an exception + +// Portability macros +#define PRAGMA_INTERFACE +#define PRAGMA_IMPLEMENTATION +#define PRAGMA_IMPLEMENTATION_(arg) +#define VALUE_OBJ_CLASS_SPEC : public _ValueObj + +// Formatting. +#define FORMAT64_MODIFIER "I64" + +#define offset_of(klass,field) offsetof(klass,field) diff --git a/hotspot/src/share/vm/utilities/growableArray.cpp b/hotspot/src/share/vm/utilities/growableArray.cpp new file mode 100644 index 00000000000..eeb259c5317 --- /dev/null +++ b/hotspot/src/share/vm/utilities/growableArray.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +# include "incls/_precompiled.incl" +# include "incls/_growableArray.cpp.incl" + +#ifdef ASSERT +void GenericGrowableArray::set_nesting() { + if (on_stack()) { + _nesting = Thread::current()->resource_area()->nesting(); + } +} + +void GenericGrowableArray::check_nesting() { + // Check for insidious allocation bug: if a GrowableArray overflows, the + // grown array must be allocated under the same ResourceMark as the original. + // Otherwise, the _data array will be deallocated too early. + if (on_stack() && + _nesting != Thread::current()->resource_area()->nesting()) { + fatal("allocation bug: GrowableArray could grow within nested ResourceMark"); + } +} +#endif + +void* GenericGrowableArray::raw_allocate(int elementSize) { + if (on_stack()) { + return (void*)resource_allocate_bytes(elementSize * _max); + } else if (on_C_heap()) { + return (void*)AllocateHeap(elementSize * _max, "GrET in " __FILE__); + } else { + return _arena->Amalloc(elementSize * _max); + } +} diff --git a/hotspot/src/share/vm/utilities/growableArray.hpp b/hotspot/src/share/vm/utilities/growableArray.hpp new file mode 100644 index 00000000000..208b145185f --- /dev/null +++ b/hotspot/src/share/vm/utilities/growableArray.hpp @@ -0,0 +1,331 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A growable array. + +/*************************************************************************/ +/* */ +/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING */ +/* */ +/* Should you use GrowableArrays to contain handles you must be certain */ +/* the the GrowableArray does not outlive the HandleMark that contains */ +/* the handles. Since GrowableArrays are typically resource allocated */ +/* the following is an example of INCORRECT CODE, */ +/* */ +/* ResourceMark rm; */ +/* GrowableArray* arr = new GrowableArray(size); */ +/* if (blah) { */ +/* while (...) { */ +/* HandleMark hm; */ +/* ... */ +/* Handle h(THREAD, some_oop); */ +/* arr->append(h); */ +/* } */ +/* } */ +/* if (arr->length() != 0 ) { */ +/* oop bad_oop = arr->at(0)(); // Handle is BAD HERE. */ +/* ... */ +/* } */ +/* */ +/* If the GrowableArrays you are creating is C_Heap allocated then it */ +/* hould not old handles since the handles could trivially try and */ +/* outlive their HandleMark. In some situations you might need to do */ +/* this and it would be legal but be very careful and see if you can do */ +/* the code in some other manner. */ +/* */ +/*************************************************************************/ + +// To call default constructor the placement operator new() is used. +// It should be empty (it only returns the passed void* pointer). +// The definition of placement operator new(size_t, void*) in the . + +#include + +// Need the correct linkage to call qsort without warnings +extern "C" { + typedef int (*_sort_Fn)(const void *, const void *); +} + +class GenericGrowableArray : public ResourceObj { + protected: + int _len; // current length + int _max; // maximum length + Arena* _arena; // Indicates where allocation occurs: + // 0 means default ResourceArea + // 1 means on C heap + // otherwise, allocate in _arena +#ifdef ASSERT + int _nesting; // resource area nesting at creation + void set_nesting(); + void check_nesting(); +#else +#define set_nesting(); +#define check_nesting(); +#endif + + // Where are we going to allocate memory? + bool on_C_heap() { return _arena == (Arena*)1; } + bool on_stack () { return _arena == NULL; } + bool on_arena () { return _arena > (Arena*)1; } + + // This GA will use the resource stack for storage if c_heap==false, + // Else it will use the C heap. Use clear_and_deallocate to avoid leaks. + GenericGrowableArray(int initial_size, int initial_len, bool c_heap) { + _len = initial_len; + _max = initial_size; + assert(_len >= 0 && _len <= _max, "initial_len too big"); + _arena = (c_heap ? (Arena*)1 : NULL); + set_nesting(); + assert(!c_heap || allocated_on_C_heap(), "growable array must be on C heap if elements are"); + } + + // This GA will use the given arena for storage. + // Consider using new(arena) GrowableArray to allocate the header. + GenericGrowableArray(Arena* arena, int initial_size, int initial_len) { + _len = initial_len; + _max = initial_size; + assert(_len >= 0 && _len <= _max, "initial_len too big"); + _arena = arena; + assert(on_arena(), "arena has taken on reserved value 0 or 1"); + } + + void* raw_allocate(int elementSize); +}; + +template class GrowableArray : public GenericGrowableArray { + private: + E* _data; // data array + + void grow(int j); + void raw_at_put_grow(int i, const E& p, const E& fill); + void clear_and_deallocate(); + public: + GrowableArray(int initial_size, bool C_heap = false) : GenericGrowableArray(initial_size, 0, C_heap) { + _data = (E*)raw_allocate(sizeof(E)); + for (int i = 0; i < _max; i++) ::new ((void*)&_data[i]) E(); + } + + GrowableArray(int initial_size, int initial_len, const E& filler, bool C_heap = false) : GenericGrowableArray(initial_size, initial_len, C_heap) { + _data = (E*)raw_allocate(sizeof(E)); + int i = 0; + for (; i < _len; i++) ::new ((void*)&_data[i]) E(filler); + for (; i < _max; i++) ::new ((void*)&_data[i]) E(); + } + + GrowableArray(Arena* arena, int initial_size, int initial_len, const E& filler) : GenericGrowableArray(arena, initial_size, initial_len) { + _data = (E*)raw_allocate(sizeof(E)); + int i = 0; + for (; i < _len; i++) ::new ((void*)&_data[i]) E(filler); + for (; i < _max; i++) ::new ((void*)&_data[i]) E(); + } + + GrowableArray() : GenericGrowableArray(2, 0, false) { + _data = (E*)raw_allocate(sizeof(E)); + ::new ((void*)&_data[0]) E(); + ::new ((void*)&_data[1]) E(); + } + + // Does nothing for resource and arena objects + ~GrowableArray() { if (on_C_heap()) clear_and_deallocate(); } + + void clear() { _len = 0; } + int length() const { return _len; } + void trunc_to(int l) { assert(l <= _len,"cannot increase length"); _len = l; } + bool is_empty() const { return _len == 0; } + bool is_nonempty() const { return _len != 0; } + bool is_full() const { return _len == _max; } + DEBUG_ONLY(E* data_addr() const { return _data; }) + + void print(); + + void append(const E& elem) { + check_nesting(); + if (_len == _max) grow(_len); + _data[_len++] = elem; + } + + void append_if_missing(const E& elem) { + if (!contains(elem)) append(elem); + } + + E at(int i) const { + assert(0 <= i && i < _len, "illegal index"); + return _data[i]; + } + + E* adr_at(int i) const { + assert(0 <= i && i < _len, "illegal index"); + return &_data[i]; + } + + E first() const { + assert(_len > 0, "empty list"); + return _data[0]; + } + + E top() const { + assert(_len > 0, "empty list"); + return _data[_len-1]; + } + + void push(const E& elem) { append(elem); } + + E pop() { + assert(_len > 0, "empty list"); + return _data[--_len]; + } + + void at_put(int i, const E& elem) { + assert(0 <= i && i < _len, "illegal index"); + _data[i] = elem; + } + + E at_grow(int i, const E& fill = E()) { + assert(0 <= i, "negative index"); + check_nesting(); + if (i >= _len) { + if (i >= _max) grow(i); + for (int j = _len; j <= i; j++) + _data[j] = fill; + _len = i+1; + } + return _data[i]; + } + + void at_put_grow(int i, const E& elem, const E& fill = E()) { + assert(0 <= i, "negative index"); + check_nesting(); + raw_at_put_grow(i, elem, fill); + } + + bool contains(const E& elem) const { + for (int i = 0; i < _len; i++) { + if (_data[i] == elem) return true; + } + return false; + } + + int find(const E& elem) const { + for (int i = 0; i < _len; i++) { + if (_data[i] == elem) return i; + } + return -1; + } + + int find(void* token, bool f(void*, E)) const { + for (int i = 0; i < _len; i++) { + if (f(token, _data[i])) return i; + } + return -1; + } + + int find_at_end(void* token, bool f(void*, E)) const { + // start at the end of the array + for (int i = _len-1; i >= 0; i--) { + if (f(token, _data[i])) return i; + } + return -1; + } + + void remove(const E& elem) { + for (int i = 0; i < _len; i++) { + if (_data[i] == elem) { + for (int j = i + 1; j < _len; j++) _data[j-1] = _data[j]; + _len--; + return; + } + } + ShouldNotReachHere(); + } + + void remove_at(int index) { + assert(0 <= index && index < _len, "illegal index"); + for (int j = index + 1; j < _len; j++) _data[j-1] = _data[j]; + _len--; + } + + void appendAll(const GrowableArray* l) { + for (int i = 0; i < l->_len; i++) { + raw_at_put_grow(_len, l->_data[i], 0); + } + } + + void sort(int f(E*,E*)) { + qsort(_data, length(), sizeof(E), (_sort_Fn)f); + } + // sort by fixed-stride sub arrays: + void sort(int f(E*,E*), int stride) { + qsort(_data, length() / stride, sizeof(E) * stride, (_sort_Fn)f); + } +}; + +// Global GrowableArray methods (one instance in the library per each 'E' type). + +template void GrowableArray::grow(int j) { + // grow the array by doubling its size (amortized growth) + int old_max = _max; + if (_max == 0) _max = 1; // prevent endless loop + while (j >= _max) _max = _max*2; + // j < _max + E* newData = (E*)raw_allocate(sizeof(E)); + int i = 0; + for ( ; i < _len; i++) ::new ((void*)&newData[i]) E(_data[i]); + for ( ; i < _max; i++) ::new ((void*)&newData[i]) E(); + for (i = 0; i < old_max; i++) _data[i].~E(); + if (on_C_heap() && _data != NULL) { + FreeHeap(_data); + } + _data = newData; +} + +template void GrowableArray::raw_at_put_grow(int i, const E& p, const E& fill) { + if (i >= _len) { + if (i >= _max) grow(i); + for (int j = _len; j < i; j++) + _data[j] = fill; + _len = i+1; + } + _data[i] = p; +} + +// This function clears and deallocate the data in the growable array that +// has been allocated on the C heap. It's not public - called by the +// destructor. +template void GrowableArray::clear_and_deallocate() { + assert(on_C_heap(), + "clear_and_deallocate should only be called when on C heap"); + clear(); + if (_data != NULL) { + for (int i = 0; i < _max; i++) _data[i].~E(); + FreeHeap(_data); + _data = NULL; + } +} + +template void GrowableArray::print() { + tty->print("Growable Array " INTPTR_FORMAT, this); + tty->print(": length %ld (_max %ld) { ", _len, _max); + for (int i = 0; i < _len; i++) tty->print(INTPTR_FORMAT " ", *(intptr_t*)&(_data[i])); + tty->print("}\n"); +} diff --git a/hotspot/src/share/vm/utilities/hashtable.cpp b/hotspot/src/share/vm/utilities/hashtable.cpp new file mode 100644 index 00000000000..58d675d001a --- /dev/null +++ b/hotspot/src/share/vm/utilities/hashtable.cpp @@ -0,0 +1,270 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_hashtable.cpp.incl" + +HS_DTRACE_PROBE_DECL4(hs_private, hashtable__new_entry, + void*, unsigned int, oop, void*); + +// This is a generic hashtable, designed to be used for the symbol +// and string tables. +// +// It is implemented as an open hash table with a fixed number of buckets. +// +// %note: +// - HashtableEntrys are allocated in blocks to reduce the space overhead. + +BasicHashtableEntry* BasicHashtable::new_entry(unsigned int hashValue) { + BasicHashtableEntry* entry; + + if (_free_list) { + entry = _free_list; + _free_list = _free_list->next(); + } else { + const int block_size = 500; + if (_first_free_entry == _end_block) { + int len = _entry_size * block_size; + _first_free_entry = NEW_C_HEAP_ARRAY(char, len); + _end_block = _first_free_entry + len; + } + entry = (BasicHashtableEntry*)_first_free_entry; + _first_free_entry += _entry_size; + } + + entry->set_hash(hashValue); + return entry; +} + + +HashtableEntry* Hashtable::new_entry(unsigned int hashValue, oop obj) { + HashtableEntry* entry; + + entry = (HashtableEntry*)BasicHashtable::new_entry(hashValue); + entry->set_literal(obj); // clears literal string field + HS_DTRACE_PROBE4(hs_private, hashtable__new_entry, + this, hashValue, obj, entry); + return entry; +} + + +// GC support + +void Hashtable::unlink(BoolObjectClosure* is_alive) { + // Readers of the table are unlocked, so we should only be removing + // entries at a safepoint. + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + for (int i = 0; i < table_size(); ++i) { + for (HashtableEntry** p = bucket_addr(i); *p != NULL; ) { + HashtableEntry* entry = *p; + if (entry->is_shared()) { + break; + } + assert(entry->literal() != NULL, "just checking"); + if (is_alive->do_object_b(entry->literal())) { + p = entry->next_addr(); + } else { + *p = entry->next(); + free_entry(entry); + } + } + } +} + + +void Hashtable::oops_do(OopClosure* f) { + for (int i = 0; i < table_size(); ++i) { + HashtableEntry** p = bucket_addr(i); + HashtableEntry* entry = bucket(i); + while (entry != NULL) { + f->do_oop(entry->literal_addr()); + + // Did the closure remove the literal from the table? + if (entry->literal() == NULL) { + assert(!entry->is_shared(), "immutable hashtable entry?"); + *p = entry->next(); + free_entry(entry); + } else { + p = entry->next_addr(); + } + entry = (HashtableEntry*)HashtableEntry::make_ptr(*p); + } + } +} + + +// Reverse the order of elements in the hash buckets. + +void BasicHashtable::reverse() { + + for (int i = 0; i < _table_size; ++i) { + BasicHashtableEntry* new_list = NULL; + BasicHashtableEntry* p = bucket(i); + while (p != NULL) { + BasicHashtableEntry* next = p->next(); + p->set_next(new_list); + new_list = p; + p = next; + } + *bucket_addr(i) = new_list; + } +} + + +// Copy the table to the shared space. + +void BasicHashtable::copy_table(char** top, char* end) { + + // Dump the hash table entries. + + intptr_t *plen = (intptr_t*)(*top); + *top += sizeof(*plen); + + int i; + for (i = 0; i < _table_size; ++i) { + for (BasicHashtableEntry** p = _buckets[i].entry_addr(); + *p != NULL; + p = (*p)->next_addr()) { + if (*top + entry_size() > end) { + warning("\nThe shared miscellaneous data space is not large " + "enough to \npreload requested classes. Use " + "-XX:SharedMiscDataSize= to increase \nthe initial " + "size of the miscellaneous data space.\n"); + exit(2); + } + *p = (BasicHashtableEntry*)memcpy(*top, *p, entry_size()); + *top += entry_size(); + } + } + *plen = (char*)(*top) - (char*)plen - sizeof(*plen); + + // Set the shared bit. + + for (i = 0; i < _table_size; ++i) { + for (BasicHashtableEntry* p = bucket(i); p != NULL; p = p->next()) { + p->set_shared(); + } + } +} + + + +// Reverse the order of elements in the hash buckets. + +void Hashtable::reverse(void* boundary) { + + for (int i = 0; i < table_size(); ++i) { + HashtableEntry* high_list = NULL; + HashtableEntry* low_list = NULL; + HashtableEntry* last_low_entry = NULL; + HashtableEntry* p = bucket(i); + while (p != NULL) { + HashtableEntry* next = p->next(); + if ((void*)p->literal() >= boundary) { + p->set_next(high_list); + high_list = p; + } else { + p->set_next(low_list); + low_list = p; + if (last_low_entry == NULL) { + last_low_entry = p; + } + } + p = next; + } + if (low_list != NULL) { + *bucket_addr(i) = low_list; + last_low_entry->set_next(high_list); + } else { + *bucket_addr(i) = high_list; + } + } +} + + +// Dump the hash table buckets. + +void BasicHashtable::copy_buckets(char** top, char* end) { + intptr_t len = _table_size * sizeof(HashtableBucket); + *(intptr_t*)(*top) = len; + *top += sizeof(intptr_t); + + *(intptr_t*)(*top) = _number_of_entries; + *top += sizeof(intptr_t); + + if (*top + len > end) { + warning("\nThe shared miscellaneous data space is not large " + "enough to \npreload requested classes. Use " + "-XX:SharedMiscDataSize= to increase \nthe initial " + "size of the miscellaneous data space.\n"); + exit(2); + } + _buckets = (HashtableBucket*)memcpy(*top, _buckets, len); + *top += len; +} + + +#ifndef PRODUCT + +void Hashtable::print() { + ResourceMark rm; + + for (int i = 0; i < table_size(); i++) { + HashtableEntry* entry = bucket(i); + while(entry != NULL) { + tty->print("%d : ", i); + entry->literal()->print(); + tty->cr(); + entry = entry->next(); + } + } +} + + +void BasicHashtable::verify() { + int count = 0; + for (int i = 0; i < table_size(); i++) { + for (BasicHashtableEntry* p = bucket(i); p != NULL; p = p->next()) { + ++count; + } + } + assert(count == number_of_entries(), "number of hashtable entries incorrect"); +} + + +#endif // PRODUCT + + +#ifdef ASSERT + +void BasicHashtable::verify_lookup_length(double load) { + if ((double)_lookup_length / (double)_lookup_count > load * 2.0) { + warning("Performance bug: SystemDictionary lookup_count=%d " + "lookup_length=%d average=%lf load=%f", + _lookup_count, _lookup_length, + (double) _lookup_length / _lookup_count, load); + } +} + +#endif diff --git a/hotspot/src/share/vm/utilities/hashtable.hpp b/hotspot/src/share/vm/utilities/hashtable.hpp new file mode 100644 index 00000000000..dd30d250dc7 --- /dev/null +++ b/hotspot/src/share/vm/utilities/hashtable.hpp @@ -0,0 +1,280 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This is a generic hashtable, designed to be used for the symbol +// and string tables. +// +// It is implemented as an open hash table with a fixed number of buckets. +// +// %note: +// - TableEntrys are allocated in blocks to reduce the space overhead. + + + +class BasicHashtableEntry : public CHeapObj { + friend class VMStructs; +private: + unsigned int _hash; // 32-bit hash for item + + // Link to next element in the linked list for this bucket. EXCEPT + // bit 0 set indicates that this entry is shared and must not be + // unlinked from the table. Bit 0 is set during the dumping of the + // archive. Since shared entries are immutable, _next fields in the + // shared entries will not change. New entries will always be + // unshared and since pointers are align, bit 0 will always remain 0 + // with no extra effort. + BasicHashtableEntry* _next; + + // Windows IA64 compiler requires subclasses to be able to access these +protected: + // Entry objects should not be created, they should be taken from the + // free list with BasicHashtable.new_entry(). + BasicHashtableEntry() { ShouldNotReachHere(); } + // Entry objects should not be destroyed. They should be placed on + // the free list instead with BasicHashtable.free_entry(). + ~BasicHashtableEntry() { ShouldNotReachHere(); } + +public: + + unsigned int hash() const { return _hash; } + void set_hash(unsigned int hash) { _hash = hash; } + unsigned int* hash_addr() { return &_hash; } + + static BasicHashtableEntry* make_ptr(BasicHashtableEntry* p) { + return (BasicHashtableEntry*)((intptr_t)p & -2); + } + + BasicHashtableEntry* next() const { + return make_ptr(_next); + } + + void set_next(BasicHashtableEntry* next) { + _next = next; + } + + BasicHashtableEntry** next_addr() { + return &_next; + } + + bool is_shared() const { + return ((intptr_t)_next & 1) != 0; + } + + void set_shared() { + _next = (BasicHashtableEntry*)((intptr_t)_next | 1); + } +}; + + + +class HashtableEntry : public BasicHashtableEntry { + friend class VMStructs; +private: + oop _literal; // ref to item in table. + +public: + // Literal + oop literal() const { return _literal; } + oop* literal_addr() { return &_literal; } + void set_literal(oop s) { _literal = s; } + + HashtableEntry* next() const { + return (HashtableEntry*)BasicHashtableEntry::next(); + } + HashtableEntry** next_addr() { + return (HashtableEntry**)BasicHashtableEntry::next_addr(); + } +}; + + + +class HashtableBucket : public CHeapObj { + friend class VMStructs; +private: + // Instance variable + BasicHashtableEntry* _entry; + +public: + // Accessing + void clear() { _entry = NULL; } + + // The following methods use order access methods to avoid race + // conditions in multiprocessor systems. + BasicHashtableEntry* get_entry() const; + void set_entry(BasicHashtableEntry* l); + + // The following method is not MT-safe and must be done under lock. + BasicHashtableEntry** entry_addr() { return &_entry; } +}; + + +class BasicHashtable : public CHeapObj { + friend class VMStructs; + +public: + BasicHashtable(int table_size, int entry_size); + BasicHashtable(int table_size, int entry_size, + HashtableBucket* buckets, int number_of_entries); + + // Sharing support. + void copy_buckets(char** top, char* end); + void copy_table(char** top, char* end); + + // Bucket handling + int hash_to_index(unsigned int full_hash) { + int h = full_hash % _table_size; + assert(h >= 0 && h < _table_size, "Illegal hash value"); + return h; + } + + // Reverse the order of elements in each of the buckets. + void reverse(); + +private: + // Instance variables + int _table_size; + HashtableBucket* _buckets; + BasicHashtableEntry* _free_list; + char* _first_free_entry; + char* _end_block; + int _entry_size; + int _number_of_entries; + +protected: + +#ifdef ASSERT + int _lookup_count; + int _lookup_length; + void verify_lookup_length(double load); +#endif + + void initialize(int table_size, int entry_size, int number_of_entries); + + // Accessor + int entry_size() const { return _entry_size; } + int table_size() { return _table_size; } + + // The following method is MT-safe and may be used with caution. + BasicHashtableEntry* bucket(int i); + + // The following method is not MT-safe and must be done under lock. + BasicHashtableEntry** bucket_addr(int i) { return _buckets[i].entry_addr(); } + + // Table entry management + BasicHashtableEntry* new_entry(unsigned int hashValue); + +public: + void set_entry(int index, BasicHashtableEntry* entry); + + void add_entry(int index, BasicHashtableEntry* entry); + + void free_entry(BasicHashtableEntry* entry); + + int number_of_entries() { return _number_of_entries; } + + void verify() PRODUCT_RETURN; +}; + + +class Hashtable : public BasicHashtable { + friend class VMStructs; + +public: + Hashtable(int table_size, int entry_size) + : BasicHashtable(table_size, entry_size) { } + + Hashtable(int table_size, int entry_size, + HashtableBucket* buckets, int number_of_entries) + : BasicHashtable(table_size, entry_size, buckets, number_of_entries) { } + + // Invoke "f->do_oop" on the locations of all oops in the table. + void oops_do(OopClosure* f); + + // Debugging + void print() PRODUCT_RETURN; + + // GC support + // Delete pointers to otherwise-unreachable objects. + void unlink(BoolObjectClosure* cl); + + // Reverse the order of elements in each of the buckets. Hashtable + // entries which refer to objects at a lower address than 'boundary' + // are separated from those which refer to objects at higher + // addresses, and appear first in the list. + void reverse(void* boundary = NULL); + +protected: + + static unsigned int hash_symbol(const char* s, int len); + + unsigned int compute_hash(symbolHandle name) { + return (unsigned int) name->identity_hash(); + } + + int index_for(symbolHandle name) { + return hash_to_index(compute_hash(name)); + } + + // Table entry management + HashtableEntry* new_entry(unsigned int hashValue, oop obj); + + // The following method is MT-safe and may be used with caution. + HashtableEntry* bucket(int i) { + return (HashtableEntry*)BasicHashtable::bucket(i); + } + + // The following method is not MT-safe and must be done under lock. + HashtableEntry** bucket_addr(int i) { + return (HashtableEntry**)BasicHashtable::bucket_addr(i); + } +}; + + +// Verions of hashtable where two handles are used to compute the index. + +class TwoOopHashtable : public Hashtable { + friend class VMStructs; +protected: + TwoOopHashtable(int table_size, int entry_size) + : Hashtable(table_size, entry_size) {} + + TwoOopHashtable(int table_size, int entry_size, HashtableBucket* t, + int number_of_entries) + : Hashtable(table_size, entry_size, t, number_of_entries) {} + +public: + unsigned int compute_hash(symbolHandle name, Handle loader) { + // Be careful with identity_hash(), it can safepoint and if this + // were one expression, the compiler could choose to unhandle each + // oop before calling identity_hash() for either of them. If the first + // causes a GC, the next would fail. + unsigned int name_hash = name->identity_hash(); + unsigned int loader_hash = loader.is_null() ? 0 : loader->identity_hash(); + return name_hash ^ loader_hash; + } + + int index_for(symbolHandle name, Handle loader) { + return hash_to_index(compute_hash(name, loader)); + } +}; diff --git a/hotspot/src/share/vm/utilities/hashtable.inline.hpp b/hotspot/src/share/vm/utilities/hashtable.inline.hpp new file mode 100644 index 00000000000..9d898063236 --- /dev/null +++ b/hotspot/src/share/vm/utilities/hashtable.inline.hpp @@ -0,0 +1,126 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline function definitions for hashtable.hpp. + + +// -------------------------------------------------------------------------- +// Hash function + +// We originally used hashpjw, but hash P(31) gives just as good results +// and is slighly faster. We would like a hash function that looks at every +// character, since package names have large common prefixes, and also because +// hash_or_fail does error checking while iterating. + +// hash P(31) from Kernighan & Ritchie + +inline unsigned int Hashtable::hash_symbol(const char* s, int len) { + unsigned int h = 0; + while (len-- > 0) { + h = 31*h + (unsigned) *s; + s++; + } + return h; +} + + +// -------------------------------------------------------------------------- + +// Initialize a table. + +inline BasicHashtable::BasicHashtable(int table_size, int entry_size) { + // Called on startup, no locking needed + initialize(table_size, entry_size, 0); + _buckets = NEW_C_HEAP_ARRAY(HashtableBucket, table_size); + for (int index = 0; index < _table_size; index++) { + _buckets[index].clear(); + } +} + + +inline BasicHashtable::BasicHashtable(int table_size, int entry_size, + HashtableBucket* buckets, + int number_of_entries) { + // Called on startup, no locking needed + initialize(table_size, entry_size, number_of_entries); + _buckets = buckets; +} + + +inline void BasicHashtable::initialize(int table_size, int entry_size, + int number_of_entries) { + // Called on startup, no locking needed + _table_size = table_size; + _entry_size = entry_size; + _free_list = NULL; + _first_free_entry = NULL; + _end_block = NULL; + _number_of_entries = number_of_entries; +#ifdef ASSERT + _lookup_count = 0; + _lookup_length = 0; +#endif +} + + +// The following method is MT-safe and may be used with caution. +inline BasicHashtableEntry* BasicHashtable::bucket(int i) { + return _buckets[i].get_entry(); +} + + +inline void HashtableBucket::set_entry(BasicHashtableEntry* l) { + // Warning: Preserve store ordering. The SystemDictionary is read + // without locks. The new SystemDictionaryEntry must be + // complete before other threads can be allowed to see it + // via a store to _buckets[index]. + OrderAccess::release_store_ptr(&_entry, l); +} + + +inline BasicHashtableEntry* HashtableBucket::get_entry() const { + // Warning: Preserve load ordering. The SystemDictionary is read + // without locks. The new SystemDictionaryEntry must be + // complete before other threads can be allowed to see it + // via a store to _buckets[index]. + return (BasicHashtableEntry*) OrderAccess::load_ptr_acquire(&_entry); +} + + +inline void BasicHashtable::set_entry(int index, BasicHashtableEntry* entry) { + _buckets[index].set_entry(entry); +} + + +inline void BasicHashtable::add_entry(int index, BasicHashtableEntry* entry) { + entry->set_next(bucket(index)); + _buckets[index].set_entry(entry); + ++_number_of_entries; +} + +inline void BasicHashtable::free_entry(BasicHashtableEntry* entry) { + entry->set_next(_free_list); + _free_list = entry; + --_number_of_entries; +} diff --git a/hotspot/src/share/vm/utilities/histogram.cpp b/hotspot/src/share/vm/utilities/histogram.cpp new file mode 100644 index 00000000000..f60269f1c58 --- /dev/null +++ b/hotspot/src/share/vm/utilities/histogram.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 1998-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_histogram.cpp.incl" + +#ifdef ASSERT + +////////////////// HistogramElement //////////////////////// + +HistogramElement::HistogramElement() { + _count = 0; +} + +int HistogramElement::count() { + return _count; +} + +const char* HistogramElement::name() { + return _name; +} + +void HistogramElement::increment_count() { + // We can't use the accessor :-(. + Atomic::inc(&_count); +} + +int HistogramElement::compare(HistogramElement* e1,HistogramElement* e2) { + if(e1->count() > e2->count()) { + return -1; + } else if(e1->count() < e2->count()) { + return 1; + } + return 0; +} + +void HistogramElement::print_on(outputStream* st) const { + st->print("%10d ",((HistogramElement*)this)->count()); + st->print_cr("%s",((HistogramElement*)this)->name()); +} + +////////////////// Histogram //////////////////////// + +int Histogram::sort_helper(HistogramElement** e1, HistogramElement** e2) { + return (*e1)->compare(*e1,*e2); +} + +Histogram::Histogram(const char* title,int estimatedCount) { + _title = title; + _elements = new (ResourceObj::C_HEAP) GrowableArray(estimatedCount,true); +} + +void Histogram::add_element(HistogramElement* element) { + // Note, we need to add locking ! + elements()->append(element); +} + +void Histogram::print_header(outputStream* st) { + st->print_cr("%s",title()); + st->print_cr("--------------------------------------------------"); +} + +void Histogram::print_elements(outputStream* st) { + elements()->sort(Histogram::sort_helper); + jint total = 0; + for(int i=0; i < elements()->length(); i++) { + elements()->at(i)->print(); + total += elements()->at(i)->count(); + } + st->print("%10d ", total); + st->print_cr("Total"); +} + +void Histogram::print_on(outputStream* st) const { + ((Histogram*)this)->print_header(st); + ((Histogram*)this)->print_elements(st); +} + +#endif diff --git a/hotspot/src/share/vm/utilities/histogram.hpp b/hotspot/src/share/vm/utilities/histogram.hpp new file mode 100644 index 00000000000..0e15d82071f --- /dev/null +++ b/hotspot/src/share/vm/utilities/histogram.hpp @@ -0,0 +1,91 @@ +/* + * Copyright 1998-2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This class provides a framework for collecting various statistics. +// The current implementation is oriented towards counting invocations +// of various types, but that can be easily changed. +// +// To use it, you need to declare a Histogram*, and a subtype of +// HistogramElement: +// +// HistogramElement* MyHistogram; +// +// class MyHistogramElement : public HistogramElement { +// public: +// MyHistogramElement(char* name); +// }; +// +// MyHistogramElement::MyHistogramElement(char* elementName) { +// _name = elementName; +// +// if(MyHistogram == NULL) +// MyHistogram = new Histogram("My Call Counts",100); +// +// MyHistogram->add_element(this); +// } +// +// #define MyCountWrapper(arg) static MyHistogramElement* e = new MyHistogramElement(arg); e->increment_count() +// +// This gives you a simple way to count invocations of specfic functions: +// +// void a_function_that_is_being_counted() { +// MyCountWrapper("FunctionName"); +// ... +// } +// +// To print the results, invoke print() on your Histogram*. + +#ifdef ASSERT + +class HistogramElement : public CHeapObj { + protected: + jint _count; + const char* _name; + + public: + HistogramElement(); + virtual int count(); + virtual const char* name(); + virtual void increment_count(); + void print_on(outputStream* st) const; + virtual int compare(HistogramElement* e1,HistogramElement* e2); +}; + +class Histogram : public CHeapObj { + protected: + GrowableArray* _elements; + GrowableArray* elements() { return _elements; } + const char* _title; + const char* title() { return _title; } + static int sort_helper(HistogramElement** e1,HistogramElement** e2); + virtual void print_header(outputStream* st); + virtual void print_elements(outputStream* st); + + public: + Histogram(const char* title,int estimatedSize); + virtual void add_element(HistogramElement* element); + void print_on(outputStream* st) const; +}; + +#endif diff --git a/hotspot/src/share/vm/utilities/macros.hpp b/hotspot/src/share/vm/utilities/macros.hpp new file mode 100644 index 00000000000..2f495efac67 --- /dev/null +++ b/hotspot/src/share/vm/utilities/macros.hpp @@ -0,0 +1,181 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Use this to mark code that needs to be cleaned up (for development only) +#define NEEDS_CLEANUP + +// Makes a string of the argument (which is not macro-expanded) +#define STR(a) #a + +// Makes a string of the macro expansion of a +#define XSTR(a) STR(a) + +// KERNEL variant +#ifdef KERNEL +#define COMPILER1 +#define SERIALGC + +#define JVMTI_KERNEL +#define FPROF_KERNEL +#define VM_STRUCTS_KERNEL +#define JNICHECK_KERNEL +#define SERVICES_KERNEL + +#define KERNEL_RETURN {} +#define KERNEL_RETURN_(code) { code } + +#else // KERNEL + +#define KERNEL_RETURN /* next token must be ; */ +#define KERNEL_RETURN_(code) /* next token must be ; */ + +#endif // KERNEL + +// COMPILER1 variant +#ifdef COMPILER1 +#ifdef COMPILER2 + #define TIERED +#endif +#define COMPILER1_PRESENT(code) code +#else // COMPILER1 +#define COMPILER1_PRESENT(code) +#endif // COMPILER1 + +// COMPILER2 variant +#ifdef COMPILER2 +#define COMPILER2_PRESENT(code) code +#else // COMPILER2 +#define COMPILER2_PRESENT(code) +#endif // COMPILER2 + + +// PRODUCT variant +#ifdef PRODUCT +#define PRODUCT_ONLY(code) code +#define NOT_PRODUCT(code) +#define PRODUCT_RETURN {} +#define PRODUCT_RETURN0 { return 0; } +#define PRODUCT_RETURN_(code) { code } +#else // PRODUCT +#define PRODUCT_ONLY(code) +#define NOT_PRODUCT(code) code +#define PRODUCT_RETURN /*next token must be ;*/ +#define PRODUCT_RETURN0 /*next token must be ;*/ +#define PRODUCT_RETURN_(code) /*next token must be ;*/ +#endif // PRODUCT + +#ifdef CHECK_UNHANDLED_OOPS +#define CHECK_UNHANDLED_OOPS_ONLY(code) code +#define NOT_CHECK_UNHANDLED_OOPS(code) +#else +#define CHECK_UNHANDLED_OOPS_ONLY(code) +#define NOT_CHECK_UNHANDLED_OOPS(code) code +#endif // CHECK_UNHANDLED_OOPS + +#ifdef CC_INTERP +#define CC_INTERP_ONLY(code) code +#define NOT_CC_INTERP(code) +#else +#define CC_INTERP_ONLY(code) +#define NOT_CC_INTERP(code) code +#endif // CC_INTERP + +#ifdef ASSERT +#define DEBUG_ONLY(code) code +#define NOT_DEBUG(code) +// Historical. +#define debug_only(code) code +#else // ASSERT +#define DEBUG_ONLY(code) +#define NOT_DEBUG(code) code +#define debug_only(code) +#endif // ASSERT + +#ifdef _LP64 +#define LP64_ONLY(code) code +#define NOT_LP64(code) +#else // !_LP64 +#define LP64_ONLY(code) +#define NOT_LP64(code) code +#endif // _LP64 + +#ifdef LINUX +#define LINUX_ONLY(code) code +#define NOT_LINUX(code) +#else +#define LINUX_ONLY(code) +#define NOT_LINUX(code) code +#endif + +#ifdef SOLARIS +#define SOLARIS_ONLY(code) code +#define NOT_SOLARIS(code) +#else +#define SOLARIS_ONLY(code) +#define NOT_SOLARIS(code) code +#endif + +#ifdef _WINDOWS +#define WINDOWS_ONLY(code) code +#define NOT_WINDOWS(code) +#else +#define WINDOWS_ONLY(code) +#define NOT_WINDOWS(code) code +#endif + +#ifdef IA32 +#define IA32_ONLY(code) code +#define NOT_IA32(code) +#else +#define IA32_ONLY(code) +#define NOT_IA32(code) code +#endif + +#ifdef IA64 +#define IA64_ONLY(code) code +#define NOT_IA64(code) +#else +#define IA64_ONLY(code) +#define NOT_IA64(code) code +#endif + +#ifdef AMD64 +#define AMD64_ONLY(code) code +#define NOT_AMD64(code) +#else +#define AMD64_ONLY(code) +#define NOT_AMD64(code) code +#endif + +#ifdef SPARC +#define SPARC_ONLY(code) code +#define NOT_SPARC(code) +#else +#define SPARC_ONLY(code) +#define NOT_SPARC(code) code +#endif + +#define FIX_THIS(code) report_assertion_failure("FIX_THIS",__FILE__, __LINE__, "") + +#define define_pd_global(type, name, value) const type pd_##name = value; diff --git a/hotspot/src/share/vm/utilities/ostream.cpp b/hotspot/src/share/vm/utilities/ostream.cpp new file mode 100644 index 00000000000..d5ad211fc01 --- /dev/null +++ b/hotspot/src/share/vm/utilities/ostream.cpp @@ -0,0 +1,850 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_ostream.cpp.incl" + +extern "C" void jio_print(const char* s); // Declarationtion of jvm method + +outputStream::outputStream(int width) { + _width = width; + _position = 0; + _newlines = 0; + _precount = 0; + _indentation = 0; +} + +outputStream::outputStream(int width, bool has_time_stamps) { + _width = width; + _position = 0; + _newlines = 0; + _precount = 0; + _indentation = 0; + if (has_time_stamps) _stamp.update(); +} + +void outputStream::update_position(const char* s, size_t len) { + for (size_t i = 0; i < len; i++) { + char ch = s[i]; + if (ch == '\n') { + _newlines += 1; + _precount += _position + 1; + _position = 0; + } else if (ch == '\t') { + _position += 8; + _precount -= 7; // invariant: _precount + _position == total count + } else { + _position += 1; + } + } +} + +// Execute a vsprintf, using the given buffer if necessary. +// Return a pointer to the formatted string. +const char* outputStream::do_vsnprintf(char* buffer, size_t buflen, + const char* format, va_list ap, + bool add_cr, + size_t& result_len) { + const char* result; + if (add_cr) buflen--; + if (!strchr(format, '%')) { + // constant format string + result = format; + result_len = strlen(result); + if (add_cr && result_len >= buflen) result_len = buflen-1; // truncate + } else if (format[0] == '%' && format[1] == 's' && format[2] == '\0') { + // trivial copy-through format string + result = va_arg(ap, const char*); + result_len = strlen(result); + if (add_cr && result_len >= buflen) result_len = buflen-1; // truncate + } else if (vsnprintf(buffer, buflen, format, ap) >= 0) { + result = buffer; + result_len = strlen(result); + } else { + DEBUG_ONLY(warning("increase O_BUFLEN in ostream.hpp -- output truncated");) + result = buffer; + result_len = buflen - 1; + buffer[result_len] = 0; + } + if (add_cr) { + if (result != buffer) { + strncpy(buffer, result, buflen); + result = buffer; + } + buffer[result_len++] = '\n'; + buffer[result_len] = 0; + } + return result; +} + +void outputStream::print(const char* format, ...) { + char buffer[O_BUFLEN]; + va_list ap; + va_start(ap, format); + size_t len; + const char* str = do_vsnprintf(buffer, O_BUFLEN, format, ap, false, len); + write(str, len); + va_end(ap); +} + +void outputStream::print_cr(const char* format, ...) { + char buffer[O_BUFLEN]; + va_list ap; + va_start(ap, format); + size_t len; + const char* str = do_vsnprintf(buffer, O_BUFLEN, format, ap, true, len); + write(str, len); + va_end(ap); +} + +void outputStream::vprint(const char *format, va_list argptr) { + char buffer[O_BUFLEN]; + size_t len; + const char* str = do_vsnprintf(buffer, O_BUFLEN, format, argptr, false, len); + write(str, len); +} + +void outputStream::vprint_cr(const char* format, va_list argptr) { + char buffer[O_BUFLEN]; + size_t len; + const char* str = do_vsnprintf(buffer, O_BUFLEN, format, argptr, true, len); + write(str, len); +} + +void outputStream::fill_to(int col) { + while (position() < col) sp(); +} + +void outputStream::put(char ch) { + assert(ch != 0, "please fix call site"); + char buf[] = { ch, '\0' }; + write(buf, 1); +} + +void outputStream::sp() { + this->write(" ", 1); +} + +void outputStream::cr() { + this->write("\n", 1); +} + +void outputStream::stamp() { + if (! _stamp.is_updated()) { + _stamp.update(); // start at 0 on first call to stamp() + } + + // outputStream::stamp() may get called by ostream_abort(), use snprintf + // to avoid allocating large stack buffer in print(). + char buf[40]; + jio_snprintf(buf, sizeof(buf), "%.3f", _stamp.seconds()); + print_raw(buf); +} + +void outputStream::date_stamp(bool guard, + const char* prefix, + const char* suffix) { + if (!guard) { + return; + } + print_raw(prefix); + static const char error_time[] = "yyyy-mm-ddThh:mm:ss.mmm+zzzz"; + static const int buffer_length = 32; + char buffer[buffer_length]; + const char* iso8601_result = os::iso8601_time(buffer, buffer_length); + if (iso8601_result != NULL) { + print_raw(buffer); + } else { + print_raw(error_time); + } + print_raw(suffix); + return; +} + +void outputStream::indent() { + while (_position < _indentation) sp(); +} + +void outputStream::print_jlong(jlong value) { + // N.B. Same as INT64_FORMAT + print(os::jlong_format_specifier(), value); +} + +void outputStream::print_julong(julong value) { + // N.B. Same as UINT64_FORMAT + print(os::julong_format_specifier(), value); +} + +stringStream::stringStream(size_t initial_size) : outputStream() { + buffer_length = initial_size; + buffer = NEW_RESOURCE_ARRAY(char, buffer_length); + buffer_pos = 0; + buffer_fixed = false; +} + +// useful for output to fixed chunks of memory, such as performance counters +stringStream::stringStream(char* fixed_buffer, size_t fixed_buffer_size) : outputStream() { + buffer_length = fixed_buffer_size; + buffer = fixed_buffer; + buffer_pos = 0; + buffer_fixed = true; +} + +void stringStream::write(const char* s, size_t len) { + size_t write_len = len; // number of non-null bytes to write + size_t end = buffer_pos + len + 1; // position after write and final '\0' + if (end > buffer_length) { + if (buffer_fixed) { + // if buffer cannot resize, silently truncate + end = buffer_length; + write_len = end - buffer_pos - 1; // leave room for the final '\0' + } else { + // For small overruns, double the buffer. For larger ones, + // increase to the requested size. + if (end < buffer_length * 2) { + end = buffer_length * 2; + } + char* oldbuf = buffer; + buffer = NEW_RESOURCE_ARRAY(char, end); + strncpy(buffer, oldbuf, buffer_pos); + buffer_length = end; + } + } + // invariant: buffer is always null-terminated + guarantee(buffer_pos + write_len + 1 <= buffer_length, "stringStream oob"); + buffer[buffer_pos + write_len] = 0; + strncpy(buffer + buffer_pos, s, write_len); + buffer_pos += write_len; + + // Note that the following does not depend on write_len. + // This means that position and count get updated + // even when overflow occurs. + update_position(s, len); +} + +char* stringStream::as_string() { + char* copy = NEW_RESOURCE_ARRAY(char, buffer_pos+1); + strncpy(copy, buffer, buffer_pos); + copy[buffer_pos] = 0; // terminating null + return copy; +} + +stringStream::~stringStream() {} + +xmlStream* xtty; +outputStream* tty; +outputStream* gclog_or_tty; +extern Mutex* tty_lock; + +fileStream::fileStream(const char* file_name) { + _file = fopen(file_name, "w"); + _need_close = true; +} + +void fileStream::write(const char* s, size_t len) { + if (_file != NULL) fwrite(s, 1, len, _file); + update_position(s, len); +} + +fileStream::~fileStream() { + if (_file != NULL) { + if (_need_close) fclose(_file); + _file = NULL; + } +} + +void fileStream::flush() { + fflush(_file); +} + +fdStream::fdStream(const char* file_name) { + _fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666); + _need_close = true; +} + +fdStream::~fdStream() { + if (_fd != -1) { + if (_need_close) close(_fd); + _fd = -1; + } +} + +void fdStream::write(const char* s, size_t len) { + if (_fd != -1) ::write(_fd, s, (int)len); + update_position(s, len); +} + +defaultStream* defaultStream::instance = NULL; +int defaultStream::_output_fd = 1; +int defaultStream::_error_fd = 2; +FILE* defaultStream::_output_stream = stdout; +FILE* defaultStream::_error_stream = stderr; + +#define LOG_MAJOR_VERSION 160 +#define LOG_MINOR_VERSION 1 + +void defaultStream::init() { + _inited = true; + if (LogVMOutput || LogCompilation) { + init_log(); + } +} + +bool defaultStream::has_log_file() { + // lazily create log file (at startup, LogVMOutput is false even + // if +LogVMOutput is used, because the flags haven't been parsed yet) + // For safer printing during fatal error handling, do not init logfile + // if a VM error has been reported. + if (!_inited && !is_error_reported()) init(); + return _log_file != NULL; +} + +static const char* make_log_name(const char* log_name, const char* force_directory, char* buf) { + const char* basename = log_name; + char file_sep = os::file_separator()[0]; + const char* cp; + for (cp = log_name; *cp != '\0'; cp++) { + if (*cp == '/' || *cp == file_sep) { + basename = cp+1; + } + } + const char* nametail = log_name; + + strcpy(buf, ""); + if (force_directory != NULL) { + strcat(buf, force_directory); + strcat(buf, os::file_separator()); + nametail = basename; // completely skip directory prefix + } + + const char* star = strchr(basename, '*'); + int star_pos = (star == NULL) ? -1 : (star - nametail); + + if (star_pos >= 0) { + // convert foo*bar.log to foo123bar.log + int buf_pos = (int) strlen(buf); + strncpy(&buf[buf_pos], nametail, star_pos); + sprintf(&buf[buf_pos + star_pos], "%u", os::current_process_id()); + nametail += star_pos + 1; // skip prefix and star + } + + strcat(buf, nametail); // append rest of name, or all of name + return buf; +} + +void defaultStream::init_log() { + // %%% Need a MutexLocker? + const char* log_name = LogFile != NULL ? LogFile : "hotspot.log"; + char buf[O_BUFLEN*2]; + const char* try_name = make_log_name(log_name, NULL, buf); + fileStream* file = new(ResourceObj::C_HEAP) fileStream(try_name); + if (!file->is_open()) { + // Try again to open the file. + char warnbuf[O_BUFLEN*2]; + sprintf(warnbuf, "Warning: Cannot open log file: %s\n", try_name); + // Note: This feature is for maintainer use only. No need for L10N. + jio_print(warnbuf); + try_name = make_log_name("hs_pid*.log", os::get_temp_directory(), buf); + sprintf(warnbuf, "Warning: Forcing option -XX:LogFile=%s\n", try_name); + jio_print(warnbuf); + delete file; + file = new(ResourceObj::C_HEAP) fileStream(try_name); + } + if (file->is_open()) { + _log_file = file; + xmlStream* xs = new(ResourceObj::C_HEAP) xmlStream(file); + _outer_xmlStream = xs; + if (this == tty) xtty = xs; + // Write XML header. + xs->print_cr(""); + // (For now, don't bother to issue a DTD for this private format.) + jlong time_ms = os::javaTimeMillis() - tty->time_stamp().milliseconds(); + // %%% Should be: jlong time_ms = os::start_time_milliseconds(), if + // we ever get round to introduce that method on the os class + xs->head("hotspot_log version='%d %d'" + " process='%d' time_ms='"INT64_FORMAT"'", + LOG_MAJOR_VERSION, LOG_MINOR_VERSION, + os::current_process_id(), time_ms); + // Write VM version header immediately. + xs->head("vm_version"); + xs->head("name"); xs->text("%s", VM_Version::vm_name()); xs->cr(); + xs->tail("name"); + xs->head("release"); xs->text("%s", VM_Version::vm_release()); xs->cr(); + xs->tail("release"); + xs->head("info"); xs->text("%s", VM_Version::internal_vm_info_string()); xs->cr(); + xs->tail("info"); + xs->tail("vm_version"); + // Record information about the command-line invocation. + xs->head("vm_arguments"); // Cf. Arguments::print_on() + if (Arguments::num_jvm_flags() > 0) { + xs->head("flags"); + Arguments::print_jvm_flags_on(xs->text()); + xs->tail("flags"); + } + if (Arguments::num_jvm_args() > 0) { + xs->head("args"); + Arguments::print_jvm_args_on(xs->text()); + xs->tail("args"); + } + if (Arguments::java_command() != NULL) { + xs->head("command"); xs->text()->print_cr("%s", Arguments::java_command()); + xs->tail("command"); + } + if (Arguments::sun_java_launcher() != NULL) { + xs->head("launcher"); xs->text()->print_cr("%s", Arguments::sun_java_launcher()); + xs->tail("launcher"); + } + if (Arguments::system_properties() != NULL) { + xs->head("properties"); + // Print it as a java-style property list. + // System properties don't generally contain newlines, so don't bother with unparsing. + for (SystemProperty* p = Arguments::system_properties(); p != NULL; p = p->next()) { + xs->text()->print_cr("%s=%s", p->key(), p->value()); + } + xs->tail("properties"); + } + xs->tail("vm_arguments"); + // tty output per se is grouped under the ... element. + xs->head("tty"); + // All further non-markup text gets copied to the tty: + xs->_text = this; // requires friend declaration! + } else { + delete(file); + // and leave xtty as NULL + LogVMOutput = false; + DisplayVMOutput = true; + LogCompilation = false; + } +} + +// finish_log() is called during normal VM shutdown. finish_log_on_error() is +// called by ostream_abort() after a fatal error. +// +void defaultStream::finish_log() { + xmlStream* xs = _outer_xmlStream; + xs->done("tty"); + + // Other log forks are appended here, at the End of Time: + CompileLog::finish_log(xs->out()); // write compile logging, if any, now + + xs->done("hotspot_log"); + xs->flush(); + + fileStream* file = _log_file; + _log_file = NULL; + + delete _outer_xmlStream; + _outer_xmlStream = NULL; + + file->flush(); + delete file; +} + +void defaultStream::finish_log_on_error(char *buf, int buflen) { + xmlStream* xs = _outer_xmlStream; + + if (xs && xs->out()) { + + xs->done_raw("tty"); + + // Other log forks are appended here, at the End of Time: + CompileLog::finish_log_on_error(xs->out(), buf, buflen); // write compile logging, if any, now + + xs->done_raw("hotspot_log"); + xs->flush(); + + fileStream* file = _log_file; + _log_file = NULL; + _outer_xmlStream = NULL; + + if (file) { + file->flush(); + + // Can't delete or close the file because delete and fclose aren't + // async-safe. We are about to die, so leave it to the kernel. + // delete file; + } + } +} + +intx defaultStream::hold(intx writer_id) { + bool has_log = has_log_file(); // check before locking + if (// impossible, but who knows? + writer_id == NO_WRITER || + + // bootstrap problem + tty_lock == NULL || + + // can't grab a lock or call Thread::current() if TLS isn't initialized + ThreadLocalStorage::thread() == NULL || + + // developer hook + !SerializeVMOutput || + + // VM already unhealthy + is_error_reported() || + + // safepoint == global lock (for VM only) + (SafepointSynchronize::is_synchronizing() && + Thread::current()->is_VM_thread()) + ) { + // do not attempt to lock unless we know the thread and the VM is healthy + return NO_WRITER; + } + if (_writer == writer_id) { + // already held, no need to re-grab the lock + return NO_WRITER; + } + tty_lock->lock_without_safepoint_check(); + // got the lock + if (writer_id != _last_writer) { + if (has_log) { + _log_file->bol(); + // output a hint where this output is coming from: + _log_file->print_cr("", writer_id); + } + _last_writer = writer_id; + } + _writer = writer_id; + return writer_id; +} + +void defaultStream::release(intx holder) { + if (holder == NO_WRITER) { + // nothing to release: either a recursive lock, or we scribbled (too bad) + return; + } + if (_writer != holder) { + return; // already unlocked, perhaps via break_tty_lock_for_safepoint + } + _writer = NO_WRITER; + tty_lock->unlock(); +} + + +// Yuck: jio_print does not accept char*/len. +static void call_jio_print(const char* s, size_t len) { + char buffer[O_BUFLEN+100]; + if (len > sizeof(buffer)-1) { + warning("increase O_BUFLEN in ostream.cpp -- output truncated"); + len = sizeof(buffer)-1; + } + strncpy(buffer, s, len); + buffer[len] = '\0'; + jio_print(buffer); +} + + +void defaultStream::write(const char* s, size_t len) { + intx thread_id = os::current_thread_id(); + intx holder = hold(thread_id); + + if (DisplayVMOutput && + (_outer_xmlStream == NULL || !_outer_xmlStream->inside_attrs())) { + // print to output stream. It can be redirected by a vfprintf hook + if (s[len] == '\0') { + jio_print(s); + } else { + call_jio_print(s, len); + } + } + + // print to log file + if (has_log_file()) { + int nl0 = _newlines; + xmlTextStream::write(s, len); + // flush the log file too, if there were any newlines + if (nl0 != _newlines){ + flush(); + } + } else { + update_position(s, len); + } + + release(holder); +} + +intx ttyLocker::hold_tty() { + if (defaultStream::instance == NULL) return defaultStream::NO_WRITER; + intx thread_id = os::current_thread_id(); + return defaultStream::instance->hold(thread_id); +} + +void ttyLocker::release_tty(intx holder) { + if (holder == defaultStream::NO_WRITER) return; + defaultStream::instance->release(holder); +} + +void ttyLocker::break_tty_lock_for_safepoint(intx holder) { + if (defaultStream::instance != NULL && + defaultStream::instance->writer() == holder) { + if (xtty != NULL) { + xtty->print_cr(""); + } + defaultStream::instance->release(holder); + } + // (else there was no lock to break) +} + +void ostream_init() { + if (defaultStream::instance == NULL) { + defaultStream::instance = new(ResourceObj::C_HEAP) defaultStream(); + tty = defaultStream::instance; + + // We want to ensure that time stamps in GC logs consider time 0 + // the time when the JVM is initialized, not the first time we ask + // for a time stamp. So, here, we explicitly update the time stamp + // of tty. + tty->time_stamp().update_to(1); + } +} + +void ostream_init_log() { + // For -Xloggc: option - called in runtime/thread.cpp + // Note : this must be called AFTER ostream_init() + + gclog_or_tty = tty; // default to tty + if (Arguments::gc_log_filename() != NULL) { + fileStream * gclog = new(ResourceObj::C_HEAP) + fileStream(Arguments::gc_log_filename()); + if (gclog->is_open()) { + // now we update the time stamp of the GC log to be synced up + // with tty. + gclog->time_stamp().update_to(tty->time_stamp().ticks()); + gclog_or_tty = gclog; + } + } + + // If we haven't lazily initialized the logfile yet, do it now, + // to avoid the possibility of lazy initialization during a VM + // crash, which can affect the stability of the fatal error handler. + defaultStream::instance->has_log_file(); +} + +// ostream_exit() is called during normal VM exit to finish log files, flush +// output and free resource. +void ostream_exit() { + static bool ostream_exit_called = false; + if (ostream_exit_called) return; + ostream_exit_called = true; + if (gclog_or_tty != tty) { + delete gclog_or_tty; + } + { + // we temporaly disable PrintMallocFree here + // as otherwise it'll lead to using of almost deleted + // tty or defaultStream::instance in logging facility + // of HeapFree(), see 6391258 + DEBUG_ONLY(FlagSetting fs(PrintMallocFree, false);) + if (tty != defaultStream::instance) { + delete tty; + } + if (defaultStream::instance != NULL) { + delete defaultStream::instance; + } + } + tty = NULL; + xtty = NULL; + gclog_or_tty = NULL; + defaultStream::instance = NULL; +} + +// ostream_abort() is called by os::abort() when VM is about to die. +void ostream_abort() { + // Here we can't delete gclog_or_tty and tty, just flush their output + if (gclog_or_tty) gclog_or_tty->flush(); + if (tty) tty->flush(); + + if (defaultStream::instance != NULL) { + static char buf[4096]; + defaultStream::instance->finish_log_on_error(buf, sizeof(buf)); + } +} + +staticBufferStream::staticBufferStream(char* buffer, size_t buflen, + outputStream *outer_stream) { + _buffer = buffer; + _buflen = buflen; + _outer_stream = outer_stream; +} + +void staticBufferStream::write(const char* c, size_t len) { + _outer_stream->print_raw(c, (int)len); +} + +void staticBufferStream::flush() { + _outer_stream->flush(); +} + +void staticBufferStream::print(const char* format, ...) { + va_list ap; + va_start(ap, format); + size_t len; + const char* str = do_vsnprintf(_buffer, _buflen, format, ap, false, len); + write(str, len); + va_end(ap); +} + +void staticBufferStream::print_cr(const char* format, ...) { + va_list ap; + va_start(ap, format); + size_t len; + const char* str = do_vsnprintf(_buffer, _buflen, format, ap, true, len); + write(str, len); + va_end(ap); +} + +void staticBufferStream::vprint(const char *format, va_list argptr) { + size_t len; + const char* str = do_vsnprintf(_buffer, _buflen, format, argptr, false, len); + write(str, len); +} + +void staticBufferStream::vprint_cr(const char* format, va_list argptr) { + size_t len; + const char* str = do_vsnprintf(_buffer, _buflen, format, argptr, true, len); + write(str, len); +} + +bufferedStream::bufferedStream(size_t initial_size) : outputStream() { + buffer_length = initial_size; + buffer = NEW_C_HEAP_ARRAY(char, buffer_length); + buffer_pos = 0; + buffer_fixed = false; +} + +bufferedStream::bufferedStream(char* fixed_buffer, size_t fixed_buffer_size) : outputStream() { + buffer_length = fixed_buffer_size; + buffer = fixed_buffer; + buffer_pos = 0; + buffer_fixed = true; +} + +void bufferedStream::write(const char* s, size_t len) { + size_t end = buffer_pos + len; + if (end >= buffer_length) { + if (buffer_fixed) { + // if buffer cannot resize, silently truncate + len = buffer_length - buffer_pos - 1; + } else { + // For small overruns, double the buffer. For larger ones, + // increase to the requested size. + if (end < buffer_length * 2) { + end = buffer_length * 2; + } + buffer = REALLOC_C_HEAP_ARRAY(char, buffer, end); + buffer_length = end; + } + } + memcpy(buffer + buffer_pos, s, len); + buffer_pos += len; + update_position(s, len); +} + +char* bufferedStream::as_string() { + char* copy = NEW_RESOURCE_ARRAY(char, buffer_pos+1); + strncpy(copy, buffer, buffer_pos); + copy[buffer_pos] = 0; // terminating null + return copy; +} + +bufferedStream::~bufferedStream() { + if (!buffer_fixed) { + FREE_C_HEAP_ARRAY(char, buffer); + } +} + +#ifndef PRODUCT + +#if defined(SOLARIS) || defined(LINUX) +#include +#include +#include +#include +#endif + +// Network access +networkStream::networkStream() { + + _socket = -1; + + hpi::initialize_socket_library(); + + int result = hpi::socket(AF_INET, SOCK_STREAM, 0); + if (result <= 0) { + assert(false, "Socket could not be created!"); + } else { + _socket = result; + } +} + +int networkStream::read(char *buf, size_t len) { + return hpi::recv(_socket, buf, (int)len, 0); +} + +void networkStream::flush() { + if (size() != 0) { + hpi::send(_socket, (char *)base(), (int)size(), 0); + } + reset(); +} + +networkStream::~networkStream() { + close(); +} + +void networkStream::close() { + if (_socket != -1) { + flush(); + hpi::socket_close(_socket); + _socket = -1; + } +} + +bool networkStream::connect(const char *ip, short port) { + + struct sockaddr_in server; + server.sin_family = AF_INET; + server.sin_port = htons(port); + + server.sin_addr.s_addr = inet_addr(ip); + if (server.sin_addr.s_addr == (unsigned long)-1) { +#ifdef _WINDOWS + struct hostent* host = hpi::get_host_by_name((char*)ip); +#else + struct hostent* host = gethostbyname(ip); +#endif + if (host != NULL) { + memcpy(&server.sin_addr, host->h_addr_list[0], host->h_length); + } else { + return false; + } + } + + + int result = hpi::connect(_socket, (struct sockaddr*)&server, sizeof(struct sockaddr_in)); + return (result >= 0); +} + +#endif diff --git a/hotspot/src/share/vm/utilities/ostream.hpp b/hotspot/src/share/vm/utilities/ostream.hpp new file mode 100644 index 00000000000..3d50c69888e --- /dev/null +++ b/hotspot/src/share/vm/utilities/ostream.hpp @@ -0,0 +1,241 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Output streams for printing +// +// Printing guidelines: +// Where possible, please use tty->print() and tty->print_cr(). +// For product mode VM warnings use warning() which internally uses tty. +// In places where tty is not initialized yet or too much overhead, +// we may use jio_printf: +// jio_fprintf(defaultStream::output_stream(), "Message"); +// This allows for redirection via -XX:+DisplayVMOutputToStdout and +// -XX:+DisplayVMOutputToStderr +class outputStream : public ResourceObj { + protected: + int _indentation; // current indentation + int _width; // width of the page + int _position; // position on the current line + int _newlines; // number of '\n' output so far + julong _precount; // number of chars output, less _position + TimeStamp _stamp; // for time stamps + + void update_position(const char* s, size_t len); + static const char* do_vsnprintf(char* buffer, size_t buflen, + const char* format, va_list ap, + bool add_cr, + size_t& result_len); + + public: + // creation + outputStream(int width = 80); + outputStream(int width, bool has_time_stamps); + + // indentation + void indent(); + void inc() { _indentation++; }; + void dec() { _indentation--; }; + int indentation() const { return _indentation; } + void set_indentation(int i) { _indentation = i; } + void fill_to(int col); + + // sizing + int width() const { return _width; } + int position() const { return _position; } + int newlines() const { return _newlines; } + julong count() const { return _precount + _position; } + void set_count(julong count) { _precount = count - _position; } + void set_position(int pos) { _position = pos; } + + // printing + void print(const char* format, ...); + void print_cr(const char* format, ...); + void vprint(const char *format, va_list argptr); + void vprint_cr(const char* format, va_list argptr); + void print_raw(const char* str) { write(str, strlen(str)); } + void print_raw(const char* str, int len) { write(str, len); } + void print_raw_cr(const char* str) { write(str, strlen(str)); cr(); } + void print_raw_cr(const char* str, int len){ write(str, len); cr(); } + void put(char ch); + void sp(); + void cr(); + void bol() { if (_position > 0) cr(); } + + // Time stamp + TimeStamp& time_stamp() { return _stamp; } + void stamp(); + // Date stamp + void date_stamp(bool guard, const char* prefix, const char* suffix); + // A simplified call that includes a suffix of ": " + void date_stamp(bool guard) { + date_stamp(guard, "", ": "); + } + + // portable printing of 64 bit integers + void print_jlong(jlong value); + void print_julong(julong value); + + // flushing + virtual void flush() {} + virtual void write(const char* str, size_t len) = 0; + virtual ~outputStream() {} // close properly on deletion + + void dec_cr() { dec(); cr(); } + void inc_cr() { inc(); cr(); } +}; + +// standard output + // ANSI C++ name collision +extern outputStream* tty; // tty output +extern outputStream* gclog_or_tty; // stream for gc log if -Xloggc:, or tty + +// advisory locking for the shared tty stream: +class ttyLocker: StackObj { + private: + intx _holder; + + public: + static intx hold_tty(); // returns a "holder" token + static void release_tty(intx holder); // must witness same token + static void break_tty_lock_for_safepoint(intx holder); + + ttyLocker() { _holder = hold_tty(); } + ~ttyLocker() { release_tty(_holder); } +}; + +// for writing to strings; buffer will expand automatically +class stringStream : public outputStream { + protected: + char* buffer; + size_t buffer_pos; + size_t buffer_length; + bool buffer_fixed; + public: + stringStream(size_t initial_bufsize = 256); + stringStream(char* fixed_buffer, size_t fixed_buffer_size); + ~stringStream(); + virtual void write(const char* c, size_t len); + size_t size() { return buffer_pos; } + const char* base() { return buffer; } + void reset() { buffer_pos = 0; _precount = 0; _position = 0; } + char* as_string(); +}; + +class fileStream : public outputStream { + protected: + FILE* _file; + bool _need_close; + public: + fileStream(const char* file_name); + fileStream(FILE* file) { _file = file; _need_close = false; } + ~fileStream(); + bool is_open() const { return _file != NULL; } + virtual void write(const char* c, size_t len); + void flush(); +}; + +// unlike fileStream, fdStream does unbuffered I/O by calling +// open() and write() directly. It is async-safe, but output +// from multiple thread may be mixed together. Used by fatal +// error handler. +class fdStream : public outputStream { + protected: + int _fd; + bool _need_close; + public: + fdStream(const char* file_name); + fdStream(int fd = -1) { _fd = fd; _need_close = false; } + ~fdStream(); + bool is_open() const { return _fd != -1; } + void set_fd(int fd) { _fd = fd; _need_close = false; } + int fd() const { return _fd; } + virtual void write(const char* c, size_t len); + void flush() {}; +}; + +void ostream_init(); +void ostream_init_log(); +void ostream_exit(); +void ostream_abort(); + +// staticBufferStream uses a user-supplied buffer for all formatting. +// Used for safe formatting during fatal error handling. Not MT safe. +// Do not share the stream between multiple threads. +class staticBufferStream : public outputStream { + private: + char* _buffer; + size_t _buflen; + outputStream* _outer_stream; + public: + staticBufferStream(char* buffer, size_t buflen, + outputStream *outer_stream); + ~staticBufferStream() {}; + virtual void write(const char* c, size_t len); + void flush(); + void print(const char* format, ...); + void print_cr(const char* format, ...); + void vprint(const char *format, va_list argptr); + void vprint_cr(const char* format, va_list argptr); +}; + +// In the non-fixed buffer case an underlying buffer will be created and +// managed in C heap. Not MT-safe. +class bufferedStream : public outputStream { + protected: + char* buffer; + size_t buffer_pos; + size_t buffer_length; + bool buffer_fixed; + public: + bufferedStream(size_t initial_bufsize = 256); + bufferedStream(char* fixed_buffer, size_t fixed_buffer_size); + ~bufferedStream(); + virtual void write(const char* c, size_t len); + size_t size() { return buffer_pos; } + const char* base() { return buffer; } + void reset() { buffer_pos = 0; _precount = 0; _position = 0; } + char* as_string(); +}; + +#define O_BUFLEN 2000 // max size of output of individual print() methods + +#ifndef PRODUCT + +class networkStream : public bufferedStream { + + private: + int _socket; + + public: + networkStream(); + ~networkStream(); + + bool connect(const char *host, short port); + bool is_open() const { return _socket != -1; } + int read(char *buf, size_t len); + void close(); + virtual void flush(); +}; + +#endif diff --git a/hotspot/src/share/vm/utilities/preserveException.cpp b/hotspot/src/share/vm/utilities/preserveException.cpp new file mode 100644 index 00000000000..46d8dc96aa6 --- /dev/null +++ b/hotspot/src/share/vm/utilities/preserveException.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_preserveException.cpp.incl" + +// TODO: These three classes should be refactored + +PreserveExceptionMark::PreserveExceptionMark(Thread*& thread) { + thread = Thread::current(); + _thread = thread; + _preserved_exception_oop = Handle(thread, _thread->pending_exception()); + _thread->clear_pending_exception(); // Needed to avoid infinite recursion + _preserved_exception_line = _thread->exception_line(); + _preserved_exception_file = _thread->exception_file(); +} + + +PreserveExceptionMark::~PreserveExceptionMark() { + if (_thread->has_pending_exception()) { + oop exception = _thread->pending_exception(); + _thread->clear_pending_exception(); // Needed to avoid infinite recursion + exception->print(); + fatal("PreserveExceptionMark destructor expects no pending exceptions"); + } + if (_preserved_exception_oop() != NULL) { + _thread->set_pending_exception(_preserved_exception_oop(), _preserved_exception_file, _preserved_exception_line); + } +} + + +// This code is cloned from PreserveExceptionMark, except that: +// returned pending exceptions do not cause a crash. +// thread is passed in, not set (not a reference parameter) +// and bug 6431341 has been addressed. + +CautiouslyPreserveExceptionMark::CautiouslyPreserveExceptionMark(Thread* thread) { + _thread = thread; + _preserved_exception_oop = Handle(thread, _thread->pending_exception()); + _preserved_exception_line = _thread->exception_line(); + _preserved_exception_file = _thread->exception_file(); + _thread->clear_pending_exception(); // Pending exceptions are checked in the destructor +} + + +CautiouslyPreserveExceptionMark::~CautiouslyPreserveExceptionMark() { + assert(!_thread->has_pending_exception(), "unexpected exception generated"); + if (_thread->has_pending_exception()) { + _thread->clear_pending_exception(); + } + if (_preserved_exception_oop() != NULL) { + _thread->set_pending_exception(_preserved_exception_oop(), _preserved_exception_file, _preserved_exception_line); + } +} + + +void WeakPreserveExceptionMark::preserve() { + _preserved_exception_oop = Handle(_thread, _thread->pending_exception()); + _preserved_exception_line = _thread->exception_line(); + _preserved_exception_file = _thread->exception_file(); + _thread->clear_pending_exception(); +} + +void WeakPreserveExceptionMark::restore() { + if (!_thread->has_pending_exception()) { + _thread->set_pending_exception(_preserved_exception_oop(), _preserved_exception_file, _preserved_exception_line); + } +} diff --git a/hotspot/src/share/vm/utilities/preserveException.hpp b/hotspot/src/share/vm/utilities/preserveException.hpp new file mode 100644 index 00000000000..9ed3797d35f --- /dev/null +++ b/hotspot/src/share/vm/utilities/preserveException.hpp @@ -0,0 +1,85 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file provides more support for exception handling; see also exceptions.hpp +class PreserveExceptionMark { + private: + Thread* _thread; + Handle _preserved_exception_oop; + int _preserved_exception_line; + const char* _preserved_exception_file; + + public: + PreserveExceptionMark(Thread*& thread); + ~PreserveExceptionMark(); +}; + + +// This is a clone of PreserveExceptionMark which asserts instead +// of failing when what it wraps generates a pending exception. +// It also addresses bug 6431341. +class CautiouslyPreserveExceptionMark { + private: + Thread* _thread; + Handle _preserved_exception_oop; + int _preserved_exception_line; + const char* _preserved_exception_file; + + public: + CautiouslyPreserveExceptionMark(Thread* thread); + ~CautiouslyPreserveExceptionMark(); +}; + + +// Like PreserveExceptionMark but allows new exceptions to be generated in +// the body of the mark. If a new exception is generated then the original one +// is discarded. +class WeakPreserveExceptionMark { +private: + Thread* _thread; + Handle _preserved_exception_oop; + int _preserved_exception_line; + const char* _preserved_exception_file; + + void preserve(); + void restore(); + + public: + WeakPreserveExceptionMark(Thread* pThread) : _thread(pThread), _preserved_exception_oop() { + if (pThread->has_pending_exception()) { + preserve(); + } + } + ~WeakPreserveExceptionMark() { + if (_preserved_exception_oop.not_null()) { + restore(); + } + } +}; + + + +// use global exception mark when allowing pending exception to be set and +// saving and restoring them +#define PRESERVE_EXCEPTION_MARK Thread* THREAD; PreserveExceptionMark __em(THREAD); diff --git a/hotspot/src/share/vm/utilities/sizes.cpp b/hotspot/src/share/vm/utilities/sizes.cpp new file mode 100644 index 00000000000..62d02473a02 --- /dev/null +++ b/hotspot/src/share/vm/utilities/sizes.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_sizes.cpp.incl" diff --git a/hotspot/src/share/vm/utilities/sizes.hpp b/hotspot/src/share/vm/utilities/sizes.hpp new file mode 100644 index 00000000000..7b805f18af3 --- /dev/null +++ b/hotspot/src/share/vm/utilities/sizes.hpp @@ -0,0 +1,144 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The following two classes are used to represent 'sizes' and 'offsets' in the VM; +// they serve as 'unit' types. ByteSize is used for sizes measured in bytes, while +// WordSize is used for sizes measured in machine words (i.e., 32bit or 64bit words +// depending on platform). +// +// The classes are defined with friend functions operating on them instead of member +// functions so that they (the classes) can be re-#define'd to int types in optimized +// mode. This allows full type checking and maximum safety in debug mode, and full +// optimizations (constant folding) and zero overhead (time and space wise) in the +// optimized build (some compilers do not optimize one-element value classes but +// instead create an object in memory - thus the overhead may be significant). +// +// Note: 1) DO NOT add new overloaded friend functions that do not have a unique function +// function name but require signature types for resolution. This will not work +// in optimized mode as both, ByteSize and WordSize are mapped to the same type +// and thus the distinction would not be possible anymore (=> compiler errors). +// +// 2) DO NOT add non-static member functions as they cannot be mapped so something +// compilable in the optimized build. Static member functions could be added +// but require a corresponding class definition in the optimized build. +// +// These classes should help doing a transition from (currently) word-size based offsets +// to byte-size based offsets in the VM (this will be important if we desire to pack +// objects more densely in the VM for 64bit machines). Such a transition should proceed +// in two steps to minimize the risk of introducing hard-to-find bugs: +// +// a) first transition the whole VM into a form where all sizes are strongly typed +// b) change all WordSize's to ByteSize's where desired and fix the compilation errors + + +#ifdef ASSERT + +class ByteSize VALUE_OBJ_CLASS_SPEC { + private: + int _size; + + // Note: This constructor must be private to avoid implicit conversions! + ByteSize(int size) { _size = size; } + + public: + // constructors + inline friend ByteSize in_ByteSize(int size); + + // accessors + inline friend int in_bytes(ByteSize x); + + // operators + friend ByteSize operator + (ByteSize x, ByteSize y) { return ByteSize(in_bytes(x) + in_bytes(y)); } + friend ByteSize operator - (ByteSize x, ByteSize y) { return ByteSize(in_bytes(x) - in_bytes(y)); } + friend ByteSize operator * (ByteSize x, int y) { return ByteSize(in_bytes(x) * y ); } + + // comparison + friend bool operator == (ByteSize x, ByteSize y) { return in_bytes(x) == in_bytes(y); } + friend bool operator != (ByteSize x, ByteSize y) { return in_bytes(x) != in_bytes(y); } + friend bool operator < (ByteSize x, ByteSize y) { return in_bytes(x) < in_bytes(y); } + friend bool operator <= (ByteSize x, ByteSize y) { return in_bytes(x) <= in_bytes(y); } + friend bool operator > (ByteSize x, ByteSize y) { return in_bytes(x) > in_bytes(y); } + friend bool operator >= (ByteSize x, ByteSize y) { return in_bytes(x) >= in_bytes(y); } +}; + +inline ByteSize in_ByteSize(int size) { return ByteSize(size); } +inline int in_bytes(ByteSize x) { return x._size; } + + +class WordSize VALUE_OBJ_CLASS_SPEC { + private: + int _size; + + // Note: This constructor must be private to avoid implicit conversions! + WordSize(int size) { _size = size; } + + public: + // constructors + inline friend WordSize in_WordSize(int size); + + // accessors + inline friend int in_words(WordSize x); + + // operators + friend WordSize operator + (WordSize x, WordSize y) { return WordSize(in_words(x) + in_words(y)); } + friend WordSize operator - (WordSize x, WordSize y) { return WordSize(in_words(x) - in_words(y)); } + friend WordSize operator * (WordSize x, int y) { return WordSize(in_words(x) * y ); } + + // comparison + friend bool operator == (WordSize x, WordSize y) { return in_words(x) == in_words(y); } + friend bool operator != (WordSize x, WordSize y) { return in_words(x) != in_words(y); } + friend bool operator < (WordSize x, WordSize y) { return in_words(x) < in_words(y); } + friend bool operator <= (WordSize x, WordSize y) { return in_words(x) <= in_words(y); } + friend bool operator > (WordSize x, WordSize y) { return in_words(x) > in_words(y); } + friend bool operator >= (WordSize x, WordSize y) { return in_words(x) >= in_words(y); } +}; + +inline WordSize in_WordSize(int size) { return WordSize(size); } +inline int in_words(WordSize x) { return x._size; } + + +#else // ASSERT + +// The following definitions must match the corresponding friend declarations +// in the Byte/WordSize classes if they are typedef'ed to be int. This will +// be the case in optimized mode to ensure zero overhead for these types. +// +// Note: If a compiler does not inline these function calls away, one may +// want to use #define's to make sure full optimization (constant +// folding in particular) is possible. + +typedef int ByteSize; +inline ByteSize in_ByteSize(int size) { return size; } +inline int in_bytes (ByteSize x) { return x; } + +typedef int WordSize; +inline WordSize in_WordSize(int size) { return size; } +inline int in_words (WordSize x) { return x; } + +#endif // ASSERT + + +// Use the following #define to get C++ field member offsets + +#define byte_offset_of(klass,field) in_ByteSize((int)offset_of(klass, field)) diff --git a/hotspot/src/share/vm/utilities/taskqueue.cpp b/hotspot/src/share/vm/utilities/taskqueue.cpp new file mode 100644 index 00000000000..691a85031df --- /dev/null +++ b/hotspot/src/share/vm/utilities/taskqueue.cpp @@ -0,0 +1,178 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_taskqueue.cpp.incl" + +bool TaskQueueSuper::peek() { + return _bottom != _age.top(); +} + +int TaskQueueSetSuper::randomParkAndMiller(int *seed0) { + const int a = 16807; + const int m = 2147483647; + const int q = 127773; /* m div a */ + const int r = 2836; /* m mod a */ + assert(sizeof(int) == 4, "I think this relies on that"); + int seed = *seed0; + int hi = seed / q; + int lo = seed % q; + int test = a * lo - r * hi; + if (test > 0) + seed = test; + else + seed = test + m; + *seed0 = seed; + return seed; +} + +ParallelTaskTerminator:: +ParallelTaskTerminator(int n_threads, TaskQueueSetSuper* queue_set) : + _n_threads(n_threads), + _queue_set(queue_set), + _offered_termination(0) {} + +bool ParallelTaskTerminator::peek_in_queue_set() { + return _queue_set->peek(); +} + +void ParallelTaskTerminator::yield() { + os::yield(); +} + +void ParallelTaskTerminator::sleep(uint millis) { + os::sleep(Thread::current(), millis, false); +} + +bool ParallelTaskTerminator::offer_termination() { + Atomic::inc(&_offered_termination); + + juint yield_count = 0; + while (true) { + if (_offered_termination == _n_threads) { + //inner_termination_loop(); + return true; + } else { + if (yield_count <= WorkStealingYieldsBeforeSleep) { + yield_count++; + yield(); + } else { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("ParallelTaskTerminator::offer_termination() " + "thread %d sleeps after %d yields", + Thread::current(), yield_count); + } + yield_count = 0; + // A sleep will cause this processor to seek work on another processor's + // runqueue, if it has nothing else to run (as opposed to the yield + // which may only move the thread to the end of the this processor's + // runqueue). + sleep(WorkStealingSleepMillis); + } + + if (peek_in_queue_set()) { + Atomic::dec(&_offered_termination); + return false; + } + } + } +} + +void ParallelTaskTerminator::reset_for_reuse() { + if (_offered_termination != 0) { + assert(_offered_termination == _n_threads, + "Terminator may still be in use"); + _offered_termination = 0; + } +} + +bool ChunkTaskQueueWithOverflow::is_empty() { + return (_chunk_queue.size() == 0) && + (_overflow_stack->length() == 0); +} + +bool ChunkTaskQueueWithOverflow::stealable_is_empty() { + return _chunk_queue.size() == 0; +} + +bool ChunkTaskQueueWithOverflow::overflow_is_empty() { + return _overflow_stack->length() == 0; +} + +void ChunkTaskQueueWithOverflow::initialize() { + _chunk_queue.initialize(); + assert(_overflow_stack == 0, "Creating memory leak"); + _overflow_stack = + new (ResourceObj::C_HEAP) GrowableArray(10, true); +} + +void ChunkTaskQueueWithOverflow::save(ChunkTask t) { + if (TraceChunkTasksQueuing && Verbose) { + gclog_or_tty->print_cr("CTQ: save " PTR_FORMAT, t); + } + if(!_chunk_queue.push(t)) { + _overflow_stack->push(t); + } +} + +// Note that using this method will retrieve all chunks +// that have been saved but that it will always check +// the overflow stack. It may be more efficient to +// check the stealable queue and the overflow stack +// separately. +bool ChunkTaskQueueWithOverflow::retrieve(ChunkTask& chunk_task) { + bool result = retrieve_from_overflow(chunk_task); + if (!result) { + result = retrieve_from_stealable_queue(chunk_task); + } + if (TraceChunkTasksQueuing && Verbose && result) { + gclog_or_tty->print_cr(" CTQ: retrieve " PTR_FORMAT, result); + } + return result; +} + +bool ChunkTaskQueueWithOverflow::retrieve_from_stealable_queue( + ChunkTask& chunk_task) { + bool result = _chunk_queue.pop_local(chunk_task); + if (TraceChunkTasksQueuing && Verbose) { + gclog_or_tty->print_cr("CTQ: retrieve_stealable " PTR_FORMAT, chunk_task); + } + return result; +} + +bool ChunkTaskQueueWithOverflow::retrieve_from_overflow( + ChunkTask& chunk_task) { + bool result; + if (!_overflow_stack->is_empty()) { + chunk_task = _overflow_stack->pop(); + result = true; + } else { + chunk_task = (ChunkTask) NULL; + result = false; + } + if (TraceChunkTasksQueuing && Verbose) { + gclog_or_tty->print_cr("CTQ: retrieve_stealable " PTR_FORMAT, chunk_task); + } + return result; +} diff --git a/hotspot/src/share/vm/utilities/taskqueue.hpp b/hotspot/src/share/vm/utilities/taskqueue.hpp new file mode 100644 index 00000000000..7fa983f8237 --- /dev/null +++ b/hotspot/src/share/vm/utilities/taskqueue.hpp @@ -0,0 +1,525 @@ +/* + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class TaskQueueSuper: public CHeapObj { +protected: + // The first free element after the last one pushed (mod _n). + // (For now we'll assume only 32-bit CAS). + volatile juint _bottom; + + // log2 of the size of the queue. + enum SomeProtectedConstants { + Log_n = 14 + }; + + // Size of the queue. + juint n() { return (1 << Log_n); } + // For computing "x mod n" efficiently. + juint n_mod_mask() { return n() - 1; } + + struct Age { + jushort _top; + jushort _tag; + + jushort tag() const { return _tag; } + jushort top() const { return _top; } + + Age() { _tag = 0; _top = 0; } + + friend bool operator ==(const Age& a1, const Age& a2) { + return a1.tag() == a2.tag() && a1.top() == a2.top(); + } + + }; + Age _age; + // These make sure we do single atomic reads and writes. + Age get_age() { + jint res = *(volatile jint*)(&_age); + return *(Age*)(&res); + } + void set_age(Age a) { + *(volatile jint*)(&_age) = *(int*)(&a); + } + + jushort get_top() { + return get_age().top(); + } + + // These both operate mod _n. + juint increment_index(juint ind) { + return (ind + 1) & n_mod_mask(); + } + juint decrement_index(juint ind) { + return (ind - 1) & n_mod_mask(); + } + + // Returns a number in the range [0.._n). If the result is "n-1", it + // should be interpreted as 0. + juint dirty_size(juint bot, juint top) { + return ((jint)bot - (jint)top) & n_mod_mask(); + } + + // Returns the size corresponding to the given "bot" and "top". + juint size(juint bot, juint top) { + juint sz = dirty_size(bot, top); + // Has the queue "wrapped", so that bottom is less than top? + // There's a complicated special case here. A pair of threads could + // perform pop_local and pop_global operations concurrently, starting + // from a state in which _bottom == _top+1. The pop_local could + // succeed in decrementing _bottom, and the pop_global in incrementing + // _top (in which case the pop_global will be awarded the contested + // queue element.) The resulting state must be interpreted as an empty + // queue. (We only need to worry about one such event: only the queue + // owner performs pop_local's, and several concurrent threads + // attempting to perform the pop_global will all perform the same CAS, + // and only one can succeed. Any stealing thread that reads after + // either the increment or decrement will seen an empty queue, and will + // not join the competitors. The "sz == -1 || sz == _n-1" state will + // not be modified by concurrent queues, so the owner thread can reset + // the state to _bottom == top so subsequent pushes will be performed + // normally. + if (sz == (n()-1)) return 0; + else return sz; + } + +public: + TaskQueueSuper() : _bottom(0), _age() {} + + // Return "true" if the TaskQueue contains any tasks. + bool peek(); + + // Return an estimate of the number of elements in the queue. + // The "careful" version admits the possibility of pop_local/pop_global + // races. + juint size() { + return size(_bottom, get_top()); + } + + juint dirty_size() { + return dirty_size(_bottom, get_top()); + } + + // Maximum number of elements allowed in the queue. This is two less + // than the actual queue size, for somewhat complicated reasons. + juint max_elems() { return n() - 2; } + +}; + +template class GenericTaskQueue: public TaskQueueSuper { +private: + // Slow paths for push, pop_local. (pop_global has no fast path.) + bool push_slow(E t, juint dirty_n_elems); + bool pop_local_slow(juint localBot, Age oldAge); + +public: + // Initializes the queue to empty. + GenericTaskQueue(); + + void initialize(); + + // Push the task "t" on the queue. Returns "false" iff the queue is + // full. + inline bool push(E t); + + // If succeeds in claiming a task (from the 'local' end, that is, the + // most recently pushed task), returns "true" and sets "t" to that task. + // Otherwise, the queue is empty and returns false. + inline bool pop_local(E& t); + + // If succeeds in claiming a task (from the 'global' end, that is, the + // least recently pushed task), returns "true" and sets "t" to that task. + // Otherwise, the queue is empty and returns false. + bool pop_global(E& t); + + // Delete any resource associated with the queue. + ~GenericTaskQueue(); + +private: + // Element array. + volatile E* _elems; +}; + +template +GenericTaskQueue::GenericTaskQueue():TaskQueueSuper() { + assert(sizeof(Age) == sizeof(jint), "Depends on this."); +} + +template +void GenericTaskQueue::initialize() { + _elems = NEW_C_HEAP_ARRAY(E, n()); + guarantee(_elems != NULL, "Allocation failed."); +} + +template +bool GenericTaskQueue::push_slow(E t, juint dirty_n_elems) { + if (dirty_n_elems == n() - 1) { + // Actually means 0, so do the push. + juint localBot = _bottom; + _elems[localBot] = t; + _bottom = increment_index(localBot); + return true; + } else + return false; +} + +template +bool GenericTaskQueue:: +pop_local_slow(juint localBot, Age oldAge) { + // This queue was observed to contain exactly one element; either this + // thread will claim it, or a competing "pop_global". In either case, + // the queue will be logically empty afterwards. Create a new Age value + // that represents the empty queue for the given value of "_bottom". (We + // must also increment "tag" because of the case where "bottom == 1", + // "top == 0". A pop_global could read the queue element in that case, + // then have the owner thread do a pop followed by another push. Without + // the incrementing of "tag", the pop_global's CAS could succeed, + // allowing it to believe it has claimed the stale element.) + Age newAge; + newAge._top = localBot; + newAge._tag = oldAge.tag() + 1; + // Perhaps a competing pop_global has already incremented "top", in which + // case it wins the element. + if (localBot == oldAge.top()) { + Age tempAge; + // No competing pop_global has yet incremented "top"; we'll try to + // install new_age, thus claiming the element. + assert(sizeof(Age) == sizeof(jint) && sizeof(jint) == sizeof(juint), + "Assumption about CAS unit."); + *(jint*)&tempAge = Atomic::cmpxchg(*(jint*)&newAge, (volatile jint*)&_age, *(jint*)&oldAge); + if (tempAge == oldAge) { + // We win. + assert(dirty_size(localBot, get_top()) != n() - 1, + "Shouldn't be possible..."); + return true; + } + } + // We fail; a completing pop_global gets the element. But the queue is + // empty (and top is greater than bottom.) Fix this representation of + // the empty queue to become the canonical one. + set_age(newAge); + assert(dirty_size(localBot, get_top()) != n() - 1, + "Shouldn't be possible..."); + return false; +} + +template +bool GenericTaskQueue::pop_global(E& t) { + Age newAge; + Age oldAge = get_age(); + juint localBot = _bottom; + juint n_elems = size(localBot, oldAge.top()); + if (n_elems == 0) { + return false; + } + t = _elems[oldAge.top()]; + newAge = oldAge; + newAge._top = increment_index(newAge.top()); + if ( newAge._top == 0 ) newAge._tag++; + Age resAge; + *(jint*)&resAge = Atomic::cmpxchg(*(jint*)&newAge, (volatile jint*)&_age, *(jint*)&oldAge); + // Note that using "_bottom" here might fail, since a pop_local might + // have decremented it. + assert(dirty_size(localBot, newAge._top) != n() - 1, + "Shouldn't be possible..."); + return (resAge == oldAge); +} + +template +GenericTaskQueue::~GenericTaskQueue() { + FREE_C_HEAP_ARRAY(E, _elems); +} + +// Inherits the typedef of "Task" from above. +class TaskQueueSetSuper: public CHeapObj { +protected: + static int randomParkAndMiller(int* seed0); +public: + // Returns "true" if some TaskQueue in the set contains a task. + virtual bool peek() = 0; +}; + +template class GenericTaskQueueSet: public TaskQueueSetSuper { +private: + int _n; + GenericTaskQueue** _queues; + +public: + GenericTaskQueueSet(int n) : _n(n) { + typedef GenericTaskQueue* GenericTaskQueuePtr; + _queues = NEW_C_HEAP_ARRAY(GenericTaskQueuePtr, n); + guarantee(_queues != NULL, "Allocation failure."); + for (int i = 0; i < n; i++) { + _queues[i] = NULL; + } + } + + bool steal_1_random(int queue_num, int* seed, E& t); + bool steal_best_of_2(int queue_num, int* seed, E& t); + bool steal_best_of_all(int queue_num, int* seed, E& t); + + void register_queue(int i, GenericTaskQueue* q); + + GenericTaskQueue* queue(int n); + + // The thread with queue number "queue_num" (and whose random number seed + // is at "seed") is trying to steal a task from some other queue. (It + // may try several queues, according to some configuration parameter.) + // If some steal succeeds, returns "true" and sets "t" the stolen task, + // otherwise returns false. + bool steal(int queue_num, int* seed, E& t); + + bool peek(); +}; + +template +void GenericTaskQueueSet::register_queue(int i, GenericTaskQueue* q) { + assert(0 <= i && i < _n, "index out of range."); + _queues[i] = q; +} + +template +GenericTaskQueue* GenericTaskQueueSet::queue(int i) { + return _queues[i]; +} + +template +bool GenericTaskQueueSet::steal(int queue_num, int* seed, E& t) { + for (int i = 0; i < 2 * _n; i++) + if (steal_best_of_2(queue_num, seed, t)) + return true; + return false; +} + +template +bool GenericTaskQueueSet::steal_best_of_all(int queue_num, int* seed, E& t) { + if (_n > 2) { + int best_k; + jint best_sz = 0; + for (int k = 0; k < _n; k++) { + if (k == queue_num) continue; + jint sz = _queues[k]->size(); + if (sz > best_sz) { + best_sz = sz; + best_k = k; + } + } + return best_sz > 0 && _queues[best_k]->pop_global(t); + } else if (_n == 2) { + // Just try the other one. + int k = (queue_num + 1) % 2; + return _queues[k]->pop_global(t); + } else { + assert(_n == 1, "can't be zero."); + return false; + } +} + +template +bool GenericTaskQueueSet::steal_1_random(int queue_num, int* seed, E& t) { + if (_n > 2) { + int k = queue_num; + while (k == queue_num) k = randomParkAndMiller(seed) % _n; + return _queues[2]->pop_global(t); + } else if (_n == 2) { + // Just try the other one. + int k = (queue_num + 1) % 2; + return _queues[k]->pop_global(t); + } else { + assert(_n == 1, "can't be zero."); + return false; + } +} + +template +bool GenericTaskQueueSet::steal_best_of_2(int queue_num, int* seed, E& t) { + if (_n > 2) { + int k1 = queue_num; + while (k1 == queue_num) k1 = randomParkAndMiller(seed) % _n; + int k2 = queue_num; + while (k2 == queue_num || k2 == k1) k2 = randomParkAndMiller(seed) % _n; + // Sample both and try the larger. + juint sz1 = _queues[k1]->size(); + juint sz2 = _queues[k2]->size(); + if (sz2 > sz1) return _queues[k2]->pop_global(t); + else return _queues[k1]->pop_global(t); + } else if (_n == 2) { + // Just try the other one. + int k = (queue_num + 1) % 2; + return _queues[k]->pop_global(t); + } else { + assert(_n == 1, "can't be zero."); + return false; + } +} + +template +bool GenericTaskQueueSet::peek() { + // Try all the queues. + for (int j = 0; j < _n; j++) { + if (_queues[j]->peek()) + return true; + } + return false; +} + +// A class to aid in the termination of a set of parallel tasks using +// TaskQueueSet's for work stealing. + +class ParallelTaskTerminator: public StackObj { +private: + int _n_threads; + TaskQueueSetSuper* _queue_set; + jint _offered_termination; + + bool peek_in_queue_set(); +protected: + virtual void yield(); + void sleep(uint millis); + +public: + + // "n_threads" is the number of threads to be terminated. "queue_set" is a + // queue sets of work queues of other threads. + ParallelTaskTerminator(int n_threads, TaskQueueSetSuper* queue_set); + + // The current thread has no work, and is ready to terminate if everyone + // else is. If returns "true", all threads are terminated. If returns + // "false", available work has been observed in one of the task queues, + // so the global task is not complete. + bool offer_termination(); + + // Reset the terminator, so that it may be reused again. + // The caller is responsible for ensuring that this is done + // in an MT-safe manner, once the previous round of use of + // the terminator is finished. + void reset_for_reuse(); + +}; + +#define SIMPLE_STACK 0 + +template inline bool GenericTaskQueue::push(E t) { +#if SIMPLE_STACK + juint localBot = _bottom; + if (_bottom < max_elems()) { + _elems[localBot] = t; + _bottom = localBot + 1; + return true; + } else { + return false; + } +#else + juint localBot = _bottom; + assert((localBot >= 0) && (localBot < n()), "_bottom out of range."); + jushort top = get_top(); + juint dirty_n_elems = dirty_size(localBot, top); + assert((dirty_n_elems >= 0) && (dirty_n_elems < n()), + "n_elems out of range."); + if (dirty_n_elems < max_elems()) { + _elems[localBot] = t; + _bottom = increment_index(localBot); + return true; + } else { + return push_slow(t, dirty_n_elems); + } +#endif +} + +template inline bool GenericTaskQueue::pop_local(E& t) { +#if SIMPLE_STACK + juint localBot = _bottom; + assert(localBot > 0, "precondition."); + localBot--; + t = _elems[localBot]; + _bottom = localBot; + return true; +#else + juint localBot = _bottom; + // This value cannot be n-1. That can only occur as a result of + // the assignment to bottom in this method. If it does, this method + // resets the size( to 0 before the next call (which is sequential, + // since this is pop_local.) + juint dirty_n_elems = dirty_size(localBot, get_top()); + assert(dirty_n_elems != n() - 1, "Shouldn't be possible..."); + if (dirty_n_elems == 0) return false; + localBot = decrement_index(localBot); + _bottom = localBot; + // This is necessary to prevent any read below from being reordered + // before the store just above. + OrderAccess::fence(); + t = _elems[localBot]; + // This is a second read of "age"; the "size()" above is the first. + // If there's still at least one element in the queue, based on the + // "_bottom" and "age" we've read, then there can be no interference with + // a "pop_global" operation, and we're done. + juint tp = get_top(); + if (size(localBot, tp) > 0) { + assert(dirty_size(localBot, tp) != n() - 1, + "Shouldn't be possible..."); + return true; + } else { + // Otherwise, the queue contained exactly one element; we take the slow + // path. + return pop_local_slow(localBot, get_age()); + } +#endif +} + +typedef oop Task; +typedef GenericTaskQueue OopTaskQueue; +typedef GenericTaskQueueSet OopTaskQueueSet; + +typedef oop* StarTask; +typedef GenericTaskQueue OopStarTaskQueue; +typedef GenericTaskQueueSet OopStarTaskQueueSet; + +typedef size_t ChunkTask; // index for chunk +typedef GenericTaskQueue ChunkTaskQueue; +typedef GenericTaskQueueSet ChunkTaskQueueSet; + +class ChunkTaskQueueWithOverflow: public CHeapObj { + protected: + ChunkTaskQueue _chunk_queue; + GrowableArray* _overflow_stack; + + public: + ChunkTaskQueueWithOverflow() : _overflow_stack(NULL) {} + // Initialize both stealable queue and overflow + void initialize(); + // Save first to stealable queue and then to overflow + void save(ChunkTask t); + // Retrieve first from overflow and then from stealable queue + bool retrieve(ChunkTask& chunk_index); + // Retrieve from stealable queue + bool retrieve_from_stealable_queue(ChunkTask& chunk_index); + // Retrieve from overflow + bool retrieve_from_overflow(ChunkTask& chunk_index); + bool is_empty(); + bool stealable_is_empty(); + bool overflow_is_empty(); + juint stealable_size() { return _chunk_queue.size(); } + ChunkTaskQueue* task_queue() { return &_chunk_queue; } +}; + +#define USE_ChunkTaskQueueWithOverflow diff --git a/hotspot/src/share/vm/utilities/top.hpp b/hotspot/src/share/vm/utilities/top.hpp new file mode 100644 index 00000000000..2b709bb33ff --- /dev/null +++ b/hotspot/src/share/vm/utilities/top.hpp @@ -0,0 +1,26 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// THIS FILE IS INTESIONALLY LEFT EMPTY +// IT IS USED TO MINIMIZE THE NUMBER OF DEPENDENCIES IN includeDB diff --git a/hotspot/src/share/vm/utilities/utf8.cpp b/hotspot/src/share/vm/utilities/utf8.cpp new file mode 100644 index 00000000000..120b55277ed --- /dev/null +++ b/hotspot/src/share/vm/utilities/utf8.cpp @@ -0,0 +1,244 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_utf8.cpp.incl" + +// Assume the utf8 string is in legal form and has been +// checked in the class file parser/format checker. +char* UTF8::next(const char* str, jchar* value) { + unsigned const char *ptr = (const unsigned char *)str; + unsigned char ch, ch2, ch3; + int length = -1; /* bad length */ + jchar result; + switch ((ch = ptr[0]) >> 4) { + default: + result = ch; + length = 1; + break; + + case 0x8: case 0x9: case 0xA: case 0xB: case 0xF: + /* Shouldn't happen. */ + break; + + case 0xC: case 0xD: + /* 110xxxxx 10xxxxxx */ + if (((ch2 = ptr[1]) & 0xC0) == 0x80) { + unsigned char high_five = ch & 0x1F; + unsigned char low_six = ch2 & 0x3F; + result = (high_five << 6) + low_six; + length = 2; + break; + } + break; + + case 0xE: + /* 1110xxxx 10xxxxxx 10xxxxxx */ + if (((ch2 = ptr[1]) & 0xC0) == 0x80) { + if (((ch3 = ptr[2]) & 0xC0) == 0x80) { + unsigned char high_four = ch & 0x0f; + unsigned char mid_six = ch2 & 0x3f; + unsigned char low_six = ch3 & 0x3f; + result = (((high_four << 6) + mid_six) << 6) + low_six; + length = 3; + } + } + break; + } /* end of switch */ + + if (length <= 0) { + *value = ptr[0]; /* default bad result; */ + return (char*)(ptr + 1); // make progress somehow + } + + *value = result; + + // The assert is correct but the .class file is wrong + // assert(UNICODE::utf8_size(result) == length, "checking reverse computation"); + return (char *)(ptr + length); +} + +char* UTF8::next_character(const char* str, jint* value) { + unsigned const char *ptr = (const unsigned char *)str; + /* See if it's legal supplementary character: + 11101101 1010xxxx 10xxxxxx 11101101 1011xxxx 10xxxxxx */ + if (is_supplementary_character(ptr)) { + *value = get_supplementary_character(ptr); + return (char *)(ptr + 6); + } + jchar result; + char* next_ch = next(str, &result); + *value = result; + return next_ch; +} + +// Count bytes of the form 10xxxxxx and deduct this count +// from the total byte count. The utf8 string must be in +// legal form which has been verified in the format checker. +int UTF8::unicode_length(const char* str, int len) { + int num_chars = len; + for (int i = 0; i < len; i++) { + if ((str[i] & 0xC0) == 0x80) { + --num_chars; + } + } + return num_chars; +} + +// Count bytes of the utf8 string except those in form +// 10xxxxxx which only appear in multibyte characters. +// The utf8 string must be in legal form and has been +// verified in the format checker. +int UTF8::unicode_length(const char* str) { + int num_chars = 0; + for (const char* p = str; *p; p++) { + if (((*p) & 0xC0) != 0x80) { + num_chars++; + } + } + return num_chars; +} + +// Writes a jchar a utf8 and returns the end +static u_char* utf8_write(u_char* base, jchar ch) { + if ((ch != 0) && (ch <=0x7f)) { + base[0] = (u_char) ch; + return base + 1; + } + + if (ch <= 0x7FF) { + /* 11 bits or less. */ + unsigned char high_five = ch >> 6; + unsigned char low_six = ch & 0x3F; + base[0] = high_five | 0xC0; /* 110xxxxx */ + base[1] = low_six | 0x80; /* 10xxxxxx */ + return base + 2; + } + /* possibly full 16 bits. */ + char high_four = ch >> 12; + char mid_six = (ch >> 6) & 0x3F; + char low_six = ch & 0x3f; + base[0] = high_four | 0xE0; /* 1110xxxx */ + base[1] = mid_six | 0x80; /* 10xxxxxx */ + base[2] = low_six | 0x80; /* 10xxxxxx */ + return base + 3; +} + +void UTF8::convert_to_unicode(const char* utf8_str, jchar* unicode_str, int unicode_length) { + unsigned char ch; + const char *ptr = (const char *)utf8_str; + int index = 0; + + /* ASCII case loop optimization */ + for (; index < unicode_length; index++) { + if((ch = ptr[0]) > 0x7F) { break; } + unicode_str[index] = ch; + ptr = (const char *)(ptr + 1); + } + + for (; index < unicode_length; index++) { + ptr = UTF8::next(ptr, &unicode_str[index]); + } +} + +// Returns NULL if 'c' it not found. This only works as long +// as 'c' is an ASCII character +jbyte* UTF8::strrchr(jbyte* base, int length, jbyte c) { + assert(length >= 0, "sanity check"); + assert(c >= 0, "does not work for non-ASCII characters"); + // Skip backwards in string until 'c' is found or end is reached + while(--length >= 0 && base[length] != c); + return (length < 0) ? NULL : &base[length]; +} + +bool UTF8::equal(jbyte* base1, int length1, jbyte* base2, int length2) { + // Length must be the same + if (length1 != length2) return false; + for (int i = 0; i < length1; i++) { + if (base1[i] != base2[i]) return false; + } + return true; +} + +bool UTF8::is_supplementary_character(const unsigned char* str) { + return ((str[0] & 0xFF) == 0xED) && ((str[1] & 0xF0) == 0xA0) && ((str[2] & 0xC0) == 0x80) + && ((str[3] & 0xFF) == 0xED) && ((str[4] & 0xF0) == 0xB0) && ((str[5] & 0xC0) == 0x80); +} + +jint UTF8::get_supplementary_character(const unsigned char* str) { + return 0x10000 + ((str[1] & 0x0f) << 16) + ((str[2] & 0x3f) << 10) + + ((str[4] & 0x0f) << 6) + (str[5] & 0x3f); +} + + +//------------------------------------------------------------------------------------- + + +int UNICODE::utf8_size(jchar c) { + if ((0x0001 <= c) && (c <= 0x007F)) return 1; + if (c <= 0x07FF) return 2; + return 3; +} + +int UNICODE::utf8_length(jchar* base, int length) { + int result = 0; + for (int index = 0; index < length; index++) { + jchar c = base[index]; + if ((0x0001 <= c) && (c <= 0x007F)) result += 1; + else if (c <= 0x07FF) result += 2; + else result += 3; + } + return result; +} + +char* UNICODE::as_utf8(jchar* base, int length) { + int utf8_len = utf8_length(base, length); + u_char* result = NEW_RESOURCE_ARRAY(u_char, utf8_len + 1); + u_char* p = result; + for (int index = 0; index < length; index++) { + p = utf8_write(p, base[index]); + } + *p = '\0'; + assert(p == &result[utf8_len], "length prediction must be correct"); + return (char*) result; +} + +char* UNICODE::as_utf8(jchar* base, int length, char* buf, int buflen) { + u_char* p = (u_char*)buf; + u_char* end = (u_char*)buf + buflen; + for (int index = 0; index < length; index++) { + jchar c = base[index]; + if (p + utf8_size(c) >= end) break; // string is truncated + p = utf8_write(p, base[index]); + } + *p = '\0'; + return buf; +} + +void UNICODE::convert_to_utf8(const jchar* base, int length, char* utf8_buffer) { + for(int index = 0; index < length; index++) { + utf8_buffer = (char*)utf8_write((u_char*)utf8_buffer, base[index]); + } + *utf8_buffer = '\0'; +} diff --git a/hotspot/src/share/vm/utilities/utf8.hpp b/hotspot/src/share/vm/utilities/utf8.hpp new file mode 100644 index 00000000000..baa8e7795ae --- /dev/null +++ b/hotspot/src/share/vm/utilities/utf8.hpp @@ -0,0 +1,76 @@ +/* + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Low-level interface for UTF8 strings + +class UTF8 : AllStatic { + public: + // returns the unicode length of a 0-terminated uft8 string + static int unicode_length(const char* uft8_str); + + // returns the unicode length of a non-0-terminated uft8 string + static int unicode_length(const char* uft8_str, int len); + + // converts a uft8 string to a unicode string + static void convert_to_unicode(const char* utf8_str, jchar* unicode_buffer, int unicode_length); + + // decodes the current utf8 character, stores the result in value, + // and returns the end of the current uft8 chararacter. + static char* next(const char* str, jchar* value); + + // decodes the current utf8 character, gets the supplementary character instead of + // the surrogate pair when seeing a supplementary character in string, + // stores the result in value, and returns the end of the current uft8 chararacter. + static char* next_character(const char* str, jint* value); + + // Utility methods + static jbyte* strrchr(jbyte* base, int length, jbyte c); + static bool equal(jbyte* base1, int length1, jbyte* base2, int length2); + static bool is_supplementary_character(const unsigned char* str); + static jint get_supplementary_character(const unsigned char* str); +}; + + +// Low-level interface for UNICODE strings + +// A unicode string represents a string in the UTF-16 format in which supplementary +// characters are represented by surrogate pairs. Index values refer to char code +// units, so a supplementary character uses two positions in a unicode string. + +class UNICODE : AllStatic { + public: + // returns the utf8 size of a unicode character + static int utf8_size(jchar c); + + // returns the utf8 length of a unicode string + static int utf8_length(jchar* base, int length); + + // converts a unicode string to utf8 string + static void convert_to_utf8(const jchar* base, int length, char* utf8_buffer); + + // converts a unicode string to a utf8 string; result is allocated + // in resource area unless a buffer is provided. + static char* as_utf8(jchar* base, int length); + static char* as_utf8(jchar* base, int length, char* buf, int buflen); +}; diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp new file mode 100644 index 00000000000..17dfb6b0c65 --- /dev/null +++ b/hotspot/src/share/vm/utilities/vmError.cpp @@ -0,0 +1,875 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_vmError.cpp.incl" + +// List of environment variables that should be reported in error log file. +const char *env_list[] = { + // All platforms + "JAVA_HOME", "JRE_HOME", "JAVA_TOOL_OPTIONS", "_JAVA_OPTIONS", "CLASSPATH", + "JAVA_COMPILER", "PATH", "USERNAME", + + // Env variables that are defined on Solaris/Linux + "LD_LIBRARY_PATH", "LD_PRELOAD", "SHELL", "DISPLAY", + "HOSTTYPE", "OSTYPE", "ARCH", "MACHTYPE", + + // defined on Linux + "LD_ASSUME_KERNEL", "_JAVA_SR_SIGNUM", + + // defined on Windows + "OS", "PROCESSOR_IDENTIFIER", "_ALT_JAVA_HOME_DIR", + + (const char *)0 +}; + +// Fatal error handler for internal errors and crashes. +// +// The default behavior of fatal error handler is to print a brief message +// to standard out (defaultStream::output_fd()), then save detailed information +// into an error report file (hs_err_pid.log) and abort VM. If multiple +// threads are having troubles at the same time, only one error is reported. +// The thread that is reporting error will abort VM when it is done, all other +// threads are blocked forever inside report_and_die(). + +// Constructor for crashes +VMError::VMError(Thread* thread, int sig, address pc, void* siginfo, void* context) { + _thread = thread; + _id = sig; + _pc = pc; + _siginfo = siginfo; + _context = context; + + _verbose = false; + _current_step = 0; + _current_step_info = NULL; + + _message = ""; + _filename = NULL; + _lineno = 0; + + _size = 0; +} + +// Constructor for internal errors +VMError::VMError(Thread* thread, const char* message, const char* filename, int lineno) { + _thread = thread; + _id = internal_error; // set it to a value that's not an OS exception/signal + _filename = filename; + _lineno = lineno; + _message = message; + + _verbose = false; + _current_step = 0; + _current_step_info = NULL; + + _pc = NULL; + _siginfo = NULL; + _context = NULL; + + _size = 0; +} + +// Constructor for OOM errors +VMError::VMError(Thread* thread, size_t size, const char* message, const char* filename, int lineno) { + _thread = thread; + _id = oom_error; // set it to a value that's not an OS exception/signal + _filename = filename; + _lineno = lineno; + _message = message; + + _verbose = false; + _current_step = 0; + _current_step_info = NULL; + + _pc = NULL; + _siginfo = NULL; + _context = NULL; + + _size = size; +} + + +// Constructor for non-fatal errors +VMError::VMError(const char* message) { + _thread = NULL; + _id = internal_error; // set it to a value that's not an OS exception/signal + _filename = NULL; + _lineno = 0; + _message = message; + + _verbose = false; + _current_step = 0; + _current_step_info = NULL; + + _pc = NULL; + _siginfo = NULL; + _context = NULL; + + _size = 0; +} + +// -XX:OnError=, where can be a list of commands, separated +// by ';'. "%p" is replaced by current process id (pid); "%%" is replaced by +// a single "%". Some examples: +// +// -XX:OnError="pmap %p" // show memory map +// -XX:OnError="gcore %p; dbx - %p" // dump core and launch debugger +// -XX:OnError="cat hs_err_pid%p.log | mail my_email@sun.com" +// -XX:OnError="kill -9 %p" // ?#!@# + +// A simple parser for -XX:OnError, usage: +// ptr = OnError; +// while ((cmd = next_OnError_command(buffer, sizeof(buffer), &ptr) != NULL) +// ... ... +static char* next_OnError_command(char* buf, int buflen, const char** ptr) { + if (ptr == NULL || *ptr == NULL) return NULL; + + const char* cmd = *ptr; + + // skip leading blanks or ';' + while (*cmd == ' ' || *cmd == ';') cmd++; + + if (*cmd == '\0') return NULL; + + const char * cmdend = cmd; + while (*cmdend != '\0' && *cmdend != ';') cmdend++; + + Arguments::copy_expand_pid(cmd, cmdend - cmd, buf, buflen); + + *ptr = (*cmdend == '\0' ? cmdend : cmdend + 1); + return buf; +} + + +static void print_bug_submit_message(outputStream *out, Thread *thread) { + if (out == NULL) return; + out->print_raw_cr("# If you would like to submit a bug report, please visit:"); + out->print_raw ("# "); + out->print_raw_cr(Arguments::java_vendor_url_bug()); + // If the crash is in native code, encourage user to submit a bug to the + // provider of that code. + if (thread && thread->is_Java_thread()) { + JavaThread* jt = (JavaThread*)thread; + if (jt->thread_state() == _thread_in_native) { + out->print_cr("# The crash happened outside the Java Virtual Machine in native code.\n# See problematic frame for where to report the bug."); + } + } + out->print_raw_cr("#"); +} + + +// Return a string to describe the error +char* VMError::error_string(char* buf, int buflen) { + char signame_buf[64]; + const char *signame = os::exception_name(_id, signame_buf, sizeof(signame_buf)); + + if (signame) { + jio_snprintf(buf, buflen, + "%s (0x%x) at pc=" PTR_FORMAT ", pid=%d, tid=" UINTX_FORMAT, + signame, _id, _pc, + os::current_process_id(), os::current_thread_id()); + } else { + if (_filename != NULL && _lineno > 0) { + // skip directory names + char separator = os::file_separator()[0]; + const char *p = strrchr(_filename, separator); + + jio_snprintf(buf, buflen, + "Internal Error at %s:%d, pid=%d, tid=" UINTX_FORMAT " \nError: %s", + p ? p + 1 : _filename, _lineno, + os::current_process_id(), os::current_thread_id(), + _message ? _message : ""); + } else { + jio_snprintf(buf, buflen, + "Internal Error (0x%x), pid=%d, tid=" UINTX_FORMAT, + _id, os::current_process_id(), os::current_thread_id()); + } + } + + return buf; +} + + +// This is the main function to report a fatal error. Only one thread can +// call this function, so we don't need to worry about MT-safety. But it's +// possible that the error handler itself may crash or die on an internal +// error, for example, when the stack/heap is badly damaged. We must be +// able to handle recursive errors that happen inside error handler. +// +// Error reporting is done in several steps. If a crash or internal error +// occurred when reporting an error, the nested signal/exception handler +// can skip steps that are already (or partially) done. Error reporting will +// continue from the next step. This allows us to retrieve and print +// information that may be unsafe to get after a fatal error. If it happens, +// you may find nested report_and_die() frames when you look at the stack +// in a debugger. +// +// In general, a hang in error handler is much worse than a crash or internal +// error, as it's harder to recover from a hang. Deadlock can happen if we +// try to grab a lock that is already owned by current thread, or if the +// owner is blocked forever (e.g. in os::infinite_sleep()). If possible, the +// error handler and all the functions it called should avoid grabbing any +// lock. An important thing to notice is that memory allocation needs a lock. +// +// We should avoid using large stack allocated buffers. Many errors happen +// when stack space is already low. Making things even worse is that there +// could be nested report_and_die() calls on stack (see above). Only one +// thread can report error, so large buffers are statically allocated in data +// segment. + +void VMError::report(outputStream* st) { +# define BEGIN if (_current_step == 0) { _current_step = 1; +# define STEP(n, s) } if (_current_step < n) { _current_step = n; _current_step_info = s; +# define END } + + // don't allocate large buffer on stack + static char buf[O_BUFLEN]; + + BEGIN + + STEP(10, "(printing unexpected error message)") + + st->print_cr("#"); + st->print_cr("# An unexpected error has been detected by Java Runtime Environment:"); + + STEP(15, "(printing type of error)") + + switch(_id) { + case oom_error: + st->print_cr("#"); + st->print("# java.lang.OutOfMemoryError: "); + if (_size) { + st->print("requested "); + sprintf(buf,"%d",_size); + st->print(buf); + st->print(" bytes"); + if (_message != NULL) { + st->print(" for "); + st->print(_message); + } + st->print_cr(". Out of swap space?"); + } else { + if (_message != NULL) + st->print_cr(_message); + } + break; + case internal_error: + default: + break; + } + + STEP(20, "(printing exception/signal name)") + + st->print_cr("#"); + st->print("# "); + // Is it an OS exception/signal? + if (os::exception_name(_id, buf, sizeof(buf))) { + st->print("%s", buf); + st->print(" (0x%x)", _id); // signal number + st->print(" at pc=" PTR_FORMAT, _pc); + } else { + st->print("Internal Error"); + if (_filename != NULL && _lineno > 0) { +#ifdef PRODUCT + // In product mode chop off pathname? + char separator = os::file_separator()[0]; + const char *p = strrchr(_filename, separator); + const char *file = p ? p+1 : _filename; +#else + const char *file = _filename; +#endif + size_t len = strlen(file); + size_t buflen = sizeof(buf); + + strncpy(buf, file, buflen); + if (len + 10 < buflen) { + sprintf(buf + len, ":" SIZE_FORMAT, _lineno); + } + st->print(" (%s)", buf); + } else { + st->print(" (0x%x)", _id); + } + } + + STEP(30, "(printing current thread and pid)") + + // process id, thread id + st->print(", pid=%d", os::current_process_id()); + st->print(", tid=" UINTX_FORMAT, os::current_thread_id()); + st->cr(); + + STEP(40, "(printing error message)") + + // error message + if (_message && _message[0] != '\0') { + st->print_cr("# Error: %s", _message); + } + + STEP(50, "(printing Java version string)") + + // VM version + st->print_cr("#"); + st->print_cr("# Java VM: %s (%s %s %s)", + Abstract_VM_Version::vm_name(), + Abstract_VM_Version::vm_release(), + Abstract_VM_Version::vm_info_string(), + Abstract_VM_Version::vm_platform_string() + ); + + STEP(60, "(printing problematic frame)") + + // Print current frame if we have a context (i.e. it's a crash) + if (_context) { + st->print_cr("# Problematic frame:"); + st->print("# "); + frame fr = os::fetch_frame_from_context(_context); + fr.print_on_error(st, buf, sizeof(buf)); + st->cr(); + st->print_cr("#"); + } + + STEP(65, "(printing bug submit message)") + + if (_verbose) print_bug_submit_message(st, _thread); + + STEP(70, "(printing thread)" ) + + if (_verbose) { + st->cr(); + st->print_cr("--------------- T H R E A D ---------------"); + st->cr(); + } + + STEP(80, "(printing current thread)" ) + + // current thread + if (_verbose) { + if (_thread) { + st->print("Current thread (" PTR_FORMAT "): ", _thread); + _thread->print_on_error(st, buf, sizeof(buf)); + st->cr(); + } else { + st->print_cr("Current thread is native thread"); + } + st->cr(); + } + + STEP(90, "(printing siginfo)" ) + + // signal no, signal code, address that caused the fault + if (_verbose && _siginfo) { + os::print_siginfo(st, _siginfo); + st->cr(); + } + + STEP(100, "(printing registers, top of stack, instructions near pc)") + + // registers, top of stack, instructions near pc + if (_verbose && _context) { + os::print_context(st, _context); + st->cr(); + } + + STEP(110, "(printing stack bounds)" ) + + if (_verbose) { + st->print("Stack: "); + + address stack_top; + size_t stack_size; + + if (_thread) { + stack_top = _thread->stack_base(); + stack_size = _thread->stack_size(); + } else { + stack_top = os::current_stack_base(); + stack_size = os::current_stack_size(); + } + + address stack_bottom = stack_top - stack_size; + st->print("[" PTR_FORMAT "," PTR_FORMAT "]", stack_bottom, stack_top); + + frame fr = _context ? os::fetch_frame_from_context(_context) + : os::current_frame(); + + if (fr.sp()) { + st->print(", sp=" PTR_FORMAT, fr.sp()); + st->print(", free space=%dk", + ((intptr_t)fr.sp() - (intptr_t)stack_bottom) >> 10); + } + + st->cr(); + } + + STEP(120, "(printing native stack)" ) + + if (_verbose) { + frame fr = _context ? os::fetch_frame_from_context(_context) + : os::current_frame(); + + // see if it's a valid frame + if (fr.pc()) { + st->print_cr("Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)"); + + int count = 0; + + while (count++ < StackPrintLimit) { + fr.print_on_error(st, buf, sizeof(buf)); + st->cr(); + if (os::is_first_C_frame(&fr)) break; + fr = os::get_sender_for_C_frame(&fr); + } + + if (count > StackPrintLimit) { + st->print_cr("......"); + } + + st->cr(); + } + } + + STEP(130, "(printing Java stack)" ) + + if (_verbose && _thread && _thread->is_Java_thread()) { + JavaThread* jt = (JavaThread*)_thread; + if (jt->has_last_Java_frame()) { + st->print_cr("Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)"); + for(StackFrameStream sfs(jt); !sfs.is_done(); sfs.next()) { + sfs.current()->print_on_error(st, buf, sizeof(buf)); + st->cr(); + } + } + } + + STEP(140, "(printing VM operation)" ) + + if (_verbose && _thread && _thread->is_VM_thread()) { + VMThread* t = (VMThread*)_thread; + VM_Operation* op = t->vm_operation(); + if (op) { + op->print_on_error(st); + st->cr(); + st->cr(); + } + } + + STEP(150, "(printing current compile task)" ) + + if (_verbose && _thread && _thread->is_Compiler_thread()) { + CompilerThread* t = (CompilerThread*)_thread; + if (t->task()) { + st->cr(); + st->print_cr("Current CompileTask:"); + t->task()->print_line_on_error(st, buf, sizeof(buf)); + st->cr(); + } + } + + STEP(160, "(printing process)" ) + + if (_verbose) { + st->cr(); + st->print_cr("--------------- P R O C E S S ---------------"); + st->cr(); + } + + STEP(170, "(printing all threads)" ) + + // all threads + if (_verbose && _thread) { + Threads::print_on_error(st, _thread, buf, sizeof(buf)); + st->cr(); + } + + STEP(175, "(printing VM state)" ) + + if (_verbose) { + // Safepoint state + st->print("VM state:"); + + if (SafepointSynchronize::is_synchronizing()) st->print("synchronizing"); + else if (SafepointSynchronize::is_at_safepoint()) st->print("at safepoint"); + else st->print("not at safepoint"); + + // Also see if error occurred during initialization or shutdown + if (!Universe::is_fully_initialized()) { + st->print(" (not fully initialized)"); + } else if (VM_Exit::vm_exited()) { + st->print(" (shutting down)"); + } else { + st->print(" (normal execution)"); + } + st->cr(); + st->cr(); + } + + STEP(180, "(printing owned locks on error)" ) + + // mutexes/monitors that currently have an owner + if (_verbose) { + print_owned_locks_on_error(st); + st->cr(); + } + + STEP(190, "(printing heap information)" ) + + if (_verbose && Universe::is_fully_initialized()) { + // print heap information before vm abort + Universe::print_on(st); + st->cr(); + } + + STEP(200, "(printing dynamic libraries)" ) + + if (_verbose) { + // dynamic libraries, or memory map + os::print_dll_info(st); + st->cr(); + } + + STEP(210, "(printing VM options)" ) + + if (_verbose) { + // VM options + Arguments::print_on(st); + st->cr(); + } + + STEP(220, "(printing environment variables)" ) + + if (_verbose) { + os::print_environment_variables(st, env_list, buf, sizeof(buf)); + st->cr(); + } + + STEP(225, "(printing signal handlers)" ) + + if (_verbose) { + os::print_signal_handlers(st, buf, sizeof(buf)); + st->cr(); + } + + STEP(230, "" ) + + if (_verbose) { + st->cr(); + st->print_cr("--------------- S Y S T E M ---------------"); + st->cr(); + } + + STEP(240, "(printing OS information)" ) + + if (_verbose) { + os::print_os_info(st); + st->cr(); + } + + STEP(250, "(printing CPU info)" ) + if (_verbose) { + os::print_cpu_info(st); + st->cr(); + } + + STEP(260, "(printing memory info)" ) + + if (_verbose) { + os::print_memory_info(st); + st->cr(); + } + + STEP(270, "(printing internal vm info)" ) + + if (_verbose) { + st->print_cr("vm_info: %s", Abstract_VM_Version::internal_vm_info_string()); + st->cr(); + } + + STEP(280, "(printing date and time)" ) + + if (_verbose) { + os::print_date_and_time(st); + st->cr(); + } + + END + +# undef BEGIN +# undef STEP +# undef END +} + + +void VMError::report_and_die() { + // Don't allocate large buffer on stack + static char buffer[O_BUFLEN]; + + // First error, and its thread id. We must be able to handle native thread, + // so use thread id instead of Thread* to identify thread. + static VMError* first_error; + static jlong first_error_tid; + + // An error could happen before tty is initialized or after it has been + // destroyed. Here we use a very simple unbuffered fdStream for printing. + // Only out.print_raw() and out.print_raw_cr() should be used, as other + // printing methods need to allocate large buffer on stack. To format a + // string, use jio_snprintf() with a static buffer or use staticBufferStream. + static fdStream out(defaultStream::output_fd()); + + // How many errors occurred in error handler when reporting first_error. + static int recursive_error_count; + + // We will first print a brief message to standard out (verbose = false), + // then save detailed information in log file (verbose = true). + static bool out_done = false; // done printing to standard out + static bool log_done = false; // done saving error log + static fdStream log; // error log + + if (SuppressFatalErrorMessage) { + os::abort(); + } + jlong mytid = os::current_thread_id(); + if (first_error == NULL && + Atomic::cmpxchg_ptr(this, &first_error, NULL) == NULL) { + + // first time + first_error_tid = mytid; + set_error_reported(); + + if (ShowMessageBoxOnError) { + show_message_box(buffer, sizeof(buffer)); + + // User has asked JVM to abort. Reset ShowMessageBoxOnError so the + // WatcherThread can kill JVM if the error handler hangs. + ShowMessageBoxOnError = false; + } + + // reset signal handlers or exception filter; make sure recursive crashes + // are handled properly. + reset_signal_handlers(); + + } else { + // This is not the first error, see if it happened in a different thread + // or in the same thread during error reporting. + if (first_error_tid != mytid) { + jio_snprintf(buffer, sizeof(buffer), + "[thread " INT64_FORMAT " also had an error]", + mytid); + out.print_raw_cr(buffer); + + // error reporting is not MT-safe, block current thread + os::infinite_sleep(); + + } else { + if (recursive_error_count++ > 30) { + out.print_raw_cr("[Too many errors, abort]"); + os::die(); + } + + jio_snprintf(buffer, sizeof(buffer), + "[error occurred during error reporting %s, id 0x%x]", + first_error ? first_error->_current_step_info : "", + _id); + if (log.is_open()) { + log.cr(); + log.print_raw_cr(buffer); + log.cr(); + } else { + out.cr(); + out.print_raw_cr(buffer); + out.cr(); + } + } + } + + // print to screen + if (!out_done) { + first_error->_verbose = false; + + staticBufferStream sbs(buffer, sizeof(buffer), &out); + first_error->report(&sbs); + + out_done = true; + + first_error->_current_step = 0; // reset current_step + first_error->_current_step_info = ""; // reset current_step string + } + + // print to error log file + if (!log_done) { + first_error->_verbose = true; + + // see if log file is already open + if (!log.is_open()) { + // open log file + int fd = -1; + + if (ErrorFile != NULL) { + bool copy_ok = + Arguments::copy_expand_pid(ErrorFile, strlen(ErrorFile), buffer, sizeof(buffer)); + if (copy_ok) { + fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, 0666); + } + } + + if (fd == -1) { + const char *cwd = os::get_current_directory(buffer, sizeof(buffer)); + size_t len = strlen(cwd); + // either user didn't specify, or the user's location failed, + // so use the default name in the current directory + jio_snprintf(&buffer[len], sizeof(buffer)-len, "%shs_err_pid%u.log", + os::file_separator(), os::current_process_id()); + fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, 0666); + } + + if (fd == -1) { + // try temp directory + const char * tmpdir = os::get_temp_directory(); + jio_snprintf(buffer, sizeof(buffer), "%shs_err_pid%u.log", + (tmpdir ? tmpdir : ""), os::current_process_id()); + fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, 0666); + } + + if (fd != -1) { + out.print_raw("# An error report file with more information is saved as:\n# "); + out.print_raw_cr(buffer); + os::set_error_file(buffer); + + log.set_fd(fd); + } else { + out.print_raw_cr("# Can not save log file, dump to screen.."); + log.set_fd(defaultStream::output_fd()); + } + } + + staticBufferStream sbs(buffer, O_BUFLEN, &log); + first_error->report(&sbs); + first_error->_current_step = 0; // reset current_step + first_error->_current_step_info = ""; // reset current_step string + + if (log.fd() != defaultStream::output_fd()) { + close(log.fd()); + } + + log.set_fd(-1); + log_done = true; + } + + + static bool skip_OnError = false; + if (!skip_OnError && OnError && OnError[0]) { + skip_OnError = true; + + out.print_raw_cr("#"); + out.print_raw ("# -XX:OnError=\""); + out.print_raw (OnError); + out.print_raw_cr("\""); + + char* cmd; + const char* ptr = OnError; + while ((cmd = next_OnError_command(buffer, sizeof(buffer), &ptr)) != NULL){ + out.print_raw ("# Executing "); +#if defined(LINUX) + out.print_raw ("/bin/sh -c "); +#elif defined(SOLARIS) + out.print_raw ("/usr/bin/sh -c "); +#endif + out.print_raw ("\""); + out.print_raw (cmd); + out.print_raw_cr("\" ..."); + + os::fork_and_exec(cmd); + } + + // done with OnError + OnError = NULL; + } + + static bool skip_bug_url = false; + if (!skip_bug_url) { + skip_bug_url = true; + + out.print_raw_cr("#"); + print_bug_submit_message(&out, _thread); + } + + if (!UseOSErrorReporting) { + // os::abort() will call abort hooks, try it first. + static bool skip_os_abort = false; + if (!skip_os_abort) { + skip_os_abort = true; + os::abort(); + } + + // if os::abort() doesn't abort, try os::die(); + os::die(); + } +} + +/* + * OnOutOfMemoryError scripts/commands executed while VM is a safepoint - this + * ensures utilities such as jmap can observe the process is a consistent state. + */ +class VM_ReportJavaOutOfMemory : public VM_Operation { + private: + VMError *_err; + public: + VM_ReportJavaOutOfMemory(VMError *err) { _err = err; } + VMOp_Type type() const { return VMOp_ReportJavaOutOfMemory; } + void doit(); +}; + +void VM_ReportJavaOutOfMemory::doit() { + // Don't allocate large buffer on stack + static char buffer[O_BUFLEN]; + + tty->print_cr("#"); + tty->print_cr("# java.lang.OutOfMemoryError: %s", _err->message()); + tty->print_cr("# -XX:OnOutOfMemoryError=\"%s\"", OnOutOfMemoryError); + + // make heap parsability + Universe::heap()->ensure_parsability(false); // no need to retire TLABs + + char* cmd; + const char* ptr = OnOutOfMemoryError; + while ((cmd = next_OnError_command(buffer, sizeof(buffer), &ptr)) != NULL){ + tty->print("# Executing "); +#if defined(LINUX) + tty->print ("/bin/sh -c "); +#elif defined(SOLARIS) + tty->print ("/usr/bin/sh -c "); +#endif + tty->print_cr("\"%s\"...", cmd); + + os::fork_and_exec(cmd); + } +} + +void VMError::report_java_out_of_memory() { + if (OnOutOfMemoryError && OnOutOfMemoryError[0]) { + MutexLocker ml(Heap_lock); + VM_ReportJavaOutOfMemory op(this); + VMThread::execute(&op); + } +} diff --git a/hotspot/src/share/vm/utilities/vmError.hpp b/hotspot/src/share/vm/utilities/vmError.hpp new file mode 100644 index 00000000000..414bc7f21eb --- /dev/null +++ b/hotspot/src/share/vm/utilities/vmError.hpp @@ -0,0 +1,103 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +class VM_ReportJavaOutOfMemory; + +class VMError : public StackObj { + friend class VM_ReportJavaOutOfMemory; + + enum ErrorType { + internal_error = 0xe0000000, + oom_error = 0xe0000001 + }; + int _id; // Solaris/Linux signals: 0 - SIGRTMAX + // Windows exceptions: 0xCxxxxxxx system errors + // 0x8xxxxxxx system warnings + + const char * _message; + + Thread * _thread; // NULL if it's native thread + + + // additional info for crashes + address _pc; // faulting PC + void * _siginfo; // ExceptionRecord on Windows, + // siginfo_t on Solaris/Linux + void * _context; // ContextRecord on Windows, + // ucontext_t on Solaris/Linux + + // additional info for VM internal errors + const char * _filename; + int _lineno; + + // used by fatal error handler + int _current_step; + const char * _current_step_info; + int _verbose; + + // used by reporting about OOM + size_t _size; + + // set signal handlers on Solaris/Linux or the default exception filter + // on Windows, to handle recursive crashes. + void reset_signal_handlers(); + + // handle -XX:+ShowMessageBoxOnError. buf is used to format the message string + void show_message_box(char* buf, int buflen); + + // generate an error report + void report(outputStream* st); + + // accessor + const char* message() { return _message; } + +public: + // Constructor for crashes + VMError(Thread* thread, int sig, address pc, void* siginfo, void* context); + // Constructor for VM internal errors + VMError(Thread* thread, const char* message, const char* filename, int lineno); + + // Constructors for VM OOM errors + VMError(Thread* thread, size_t size, const char* message, const char* filename, int lineno); + // Constructor for non-fatal errors + VMError(const char* message); + + // return a string to describe the error + char *error_string(char* buf, int buflen); + + // main error reporting function + void report_and_die(); + + // reporting OutOfMemoryError + void report_java_out_of_memory(); + + // returns original flags for signal, if it was resetted, or -1 if + // signal was not changed by error reporter + static int get_resetted_sigflags(int sig); + + // returns original handler for signal, if it was resetted, or NULL if + // signal was not changed by error reporter + static address get_resetted_sighandler(int sig); +}; diff --git a/hotspot/src/share/vm/utilities/workgroup.cpp b/hotspot/src/share/vm/utilities/workgroup.cpp new file mode 100644 index 00000000000..bdf650bbca7 --- /dev/null +++ b/hotspot/src/share/vm/utilities/workgroup.cpp @@ -0,0 +1,444 @@ +/* + * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_workgroup.cpp.incl" + +// Definitions of WorkGang methods. + +AbstractWorkGang::AbstractWorkGang(const char* name, + bool are_GC_threads) : + _name(name), + _are_GC_threads(are_GC_threads) { + // Other initialization. + _monitor = new Monitor(/* priority */ Mutex::leaf, + /* name */ "WorkGroup monitor", + /* allow_vm_block */ are_GC_threads); + assert(monitor() != NULL, "Failed to allocate monitor"); + _terminate = false; + _task = NULL; + _sequence_number = 0; + _started_workers = 0; + _finished_workers = 0; +} + +WorkGang::WorkGang(const char* name, + int workers, + bool are_GC_threads) : + AbstractWorkGang(name, are_GC_threads) { + // Save arguments. + _total_workers = workers; + if (TraceWorkGang) { + tty->print_cr("Constructing work gang %s with %d threads", name, workers); + } + _gang_workers = NEW_C_HEAP_ARRAY(GangWorker*, workers); + assert(gang_workers() != NULL, "Failed to allocate gang workers"); + for (int worker = 0; worker < total_workers(); worker += 1) { + GangWorker* new_worker = new GangWorker(this, worker); + assert(new_worker != NULL, "Failed to allocate GangWorker"); + _gang_workers[worker] = new_worker; + if (new_worker == NULL || !os::create_thread(new_worker, os::pgc_thread)) + vm_exit_out_of_memory(0, "Cannot create worker GC thread. Out of system resources."); + if (!DisableStartThread) { + os::start_thread(new_worker); + } + } +} + +AbstractWorkGang::~AbstractWorkGang() { + if (TraceWorkGang) { + tty->print_cr("Destructing work gang %s", name()); + } + stop(); // stop all the workers + for (int worker = 0; worker < total_workers(); worker += 1) { + delete gang_worker(worker); + } + delete gang_workers(); + delete monitor(); +} + +GangWorker* AbstractWorkGang::gang_worker(int i) const { + // Array index bounds checking. + GangWorker* result = NULL; + assert(gang_workers() != NULL, "No workers for indexing"); + assert(((i >= 0) && (i < total_workers())), "Worker index out of bounds"); + result = _gang_workers[i]; + assert(result != NULL, "Indexing to null worker"); + return result; +} + +void WorkGang::run_task(AbstractGangTask* task) { + // This thread is executed by the VM thread which does not block + // on ordinary MutexLocker's. + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + if (TraceWorkGang) { + tty->print_cr("Running work gang %s task %s", name(), task->name()); + } + // Tell all the workers to run a task. + assert(task != NULL, "Running a null task"); + // Initialize. + _task = task; + _sequence_number += 1; + _started_workers = 0; + _finished_workers = 0; + // Tell the workers to get to work. + monitor()->notify_all(); + // Wait for them to be finished + while (finished_workers() < total_workers()) { + if (TraceWorkGang) { + tty->print_cr("Waiting in work gang %s: %d/%d finished sequence %d", + name(), finished_workers(), total_workers(), + _sequence_number); + } + monitor()->wait(/* no_safepoint_check */ true); + } + _task = NULL; + if (TraceWorkGang) { + tty->print_cr("/nFinished work gang %s: %d/%d sequence %d", + name(), finished_workers(), total_workers(), + _sequence_number); + } +} + +void AbstractWorkGang::stop() { + // Tell all workers to terminate, then wait for them to become inactive. + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + if (TraceWorkGang) { + tty->print_cr("Stopping work gang %s task %s", name(), task()->name()); + } + _task = NULL; + _terminate = true; + monitor()->notify_all(); + while (finished_workers() < total_workers()) { + if (TraceWorkGang) { + tty->print_cr("Waiting in work gang %s: %d/%d finished", + name(), finished_workers(), total_workers()); + } + monitor()->wait(/* no_safepoint_check */ true); + } +} + +void AbstractWorkGang::internal_worker_poll(WorkData* data) const { + assert(monitor()->owned_by_self(), "worker_poll is an internal method"); + assert(data != NULL, "worker data is null"); + data->set_terminate(terminate()); + data->set_task(task()); + data->set_sequence_number(sequence_number()); +} + +void AbstractWorkGang::internal_note_start() { + assert(monitor()->owned_by_self(), "note_finish is an internal method"); + _started_workers += 1; +} + +void AbstractWorkGang::internal_note_finish() { + assert(monitor()->owned_by_self(), "note_finish is an internal method"); + _finished_workers += 1; +} + +void AbstractWorkGang::print_worker_threads_on(outputStream* st) const { + uint num_thr = total_workers(); + for (uint i = 0; i < num_thr; i++) { + gang_worker(i)->print_on(st); + st->cr(); + } +} + +void AbstractWorkGang::threads_do(ThreadClosure* tc) const { + assert(tc != NULL, "Null ThreadClosure"); + uint num_thr = total_workers(); + for (uint i = 0; i < num_thr; i++) { + tc->do_thread(gang_worker(i)); + } +} + +// GangWorker methods. + +GangWorker::GangWorker(AbstractWorkGang* gang, uint id) { + _gang = gang; + set_id(id); + set_name("Gang worker#%d (%s)", id, gang->name()); +} + +void GangWorker::run() { + initialize(); + loop(); +} + +void GangWorker::initialize() { + this->initialize_thread_local_storage(); + assert(_gang != NULL, "No gang to run in"); + os::set_priority(this, NearMaxPriority); + if (TraceWorkGang) { + tty->print_cr("Running gang worker for gang %s id %d", + gang()->name(), id()); + } + // The VM thread should not execute here because MutexLocker's are used + // as (opposed to MutexLockerEx's). + assert(!Thread::current()->is_VM_thread(), "VM thread should not be part" + " of a work gang"); +} + +void GangWorker::loop() { + int previous_sequence_number = 0; + Monitor* gang_monitor = gang()->monitor(); + for ( ; /* !terminate() */; ) { + WorkData data; + int part; // Initialized below. + { + // Grab the gang mutex. + MutexLocker ml(gang_monitor); + // Wait for something to do. + // Polling outside the while { wait } avoids missed notifies + // in the outer loop. + gang()->internal_worker_poll(&data); + if (TraceWorkGang) { + tty->print("Polled outside for work in gang %s worker %d", + gang()->name(), id()); + tty->print(" terminate: %s", + data.terminate() ? "true" : "false"); + tty->print(" sequence: %d (prev: %d)", + data.sequence_number(), previous_sequence_number); + if (data.task() != NULL) { + tty->print(" task: %s", data.task()->name()); + } else { + tty->print(" task: NULL"); + } + tty->cr(); + } + for ( ; /* break or return */; ) { + // Terminate if requested. + if (data.terminate()) { + gang()->internal_note_finish(); + gang_monitor->notify_all(); + return; + } + // Check for new work. + if ((data.task() != NULL) && + (data.sequence_number() != previous_sequence_number)) { + gang()->internal_note_start(); + gang_monitor->notify_all(); + part = gang()->started_workers() - 1; + break; + } + // Nothing to do. + gang_monitor->wait(/* no_safepoint_check */ true); + gang()->internal_worker_poll(&data); + if (TraceWorkGang) { + tty->print("Polled inside for work in gang %s worker %d", + gang()->name(), id()); + tty->print(" terminate: %s", + data.terminate() ? "true" : "false"); + tty->print(" sequence: %d (prev: %d)", + data.sequence_number(), previous_sequence_number); + if (data.task() != NULL) { + tty->print(" task: %s", data.task()->name()); + } else { + tty->print(" task: NULL"); + } + tty->cr(); + } + } + // Drop gang mutex. + } + if (TraceWorkGang) { + tty->print("Work for work gang %s id %d task %s part %d", + gang()->name(), id(), data.task()->name(), part); + } + assert(data.task() != NULL, "Got null task"); + data.task()->work(part); + { + if (TraceWorkGang) { + tty->print("Finish for work gang %s id %d task %s part %d", + gang()->name(), id(), data.task()->name(), part); + } + // Grab the gang mutex. + MutexLocker ml(gang_monitor); + gang()->internal_note_finish(); + // Tell the gang you are done. + gang_monitor->notify_all(); + // Drop the gang mutex. + } + previous_sequence_number = data.sequence_number(); + } +} + +bool GangWorker::is_GC_task_thread() const { + return gang()->are_GC_threads(); +} + +void GangWorker::print_on(outputStream* st) const { + st->print("\"%s\" ", name()); + Thread::print_on(st); + st->cr(); +} + +// Printing methods + +const char* AbstractWorkGang::name() const { + return _name; +} + +#ifndef PRODUCT + +const char* AbstractGangTask::name() const { + return _name; +} + +#endif /* PRODUCT */ + +// *** WorkGangBarrierSync + +WorkGangBarrierSync::WorkGangBarrierSync() + : _monitor(Mutex::safepoint, "work gang barrier sync", true), + _n_workers(0), _n_completed(0) { +} + +WorkGangBarrierSync::WorkGangBarrierSync(int n_workers, const char* name) + : _monitor(Mutex::safepoint, name, true), + _n_workers(n_workers), _n_completed(0) { +} + +void WorkGangBarrierSync::set_n_workers(int n_workers) { + _n_workers = n_workers; + _n_completed = 0; +} + +void WorkGangBarrierSync::enter() { + MutexLockerEx x(monitor(), Mutex::_no_safepoint_check_flag); + inc_completed(); + if (n_completed() == n_workers()) { + monitor()->notify_all(); + } + else { + while (n_completed() != n_workers()) { + monitor()->wait(/* no_safepoint_check */ true); + } + } +} + +// SubTasksDone functions. + +SubTasksDone::SubTasksDone(int n) : + _n_tasks(n), _n_threads(1), _tasks(NULL) { + _tasks = NEW_C_HEAP_ARRAY(jint, n); + guarantee(_tasks != NULL, "alloc failure"); + clear(); +} + +bool SubTasksDone::valid() { + return _tasks != NULL; +} + +void SubTasksDone::set_par_threads(int t) { +#ifdef ASSERT + assert(_claimed == 0 || _threads_completed == _n_threads, + "should not be called while tasks are being processed!"); +#endif + _n_threads = (t == 0 ? 1 : t); +} + +void SubTasksDone::clear() { + for (int i = 0; i < _n_tasks; i++) { + _tasks[i] = 0; + } + _threads_completed = 0; +#ifdef ASSERT + _claimed = 0; +#endif +} + +bool SubTasksDone::is_task_claimed(int t) { + assert(0 <= t && t < _n_tasks, "bad task id."); + jint old = _tasks[t]; + if (old == 0) { + old = Atomic::cmpxchg(1, &_tasks[t], 0); + } + assert(_tasks[t] == 1, "What else?"); + bool res = old != 0; +#ifdef ASSERT + if (!res) { + assert(_claimed < _n_tasks, "Too many tasks claimed; missing clear?"); + Atomic::inc(&_claimed); + } +#endif + return res; +} + +void SubTasksDone::all_tasks_completed() { + jint observed = _threads_completed; + jint old; + do { + old = observed; + observed = Atomic::cmpxchg(old+1, &_threads_completed, old); + } while (observed != old); + // If this was the last thread checking in, clear the tasks. + if (observed+1 == _n_threads) clear(); +} + + +SubTasksDone::~SubTasksDone() { + if (_tasks != NULL) FREE_C_HEAP_ARRAY(jint, _tasks); +} + +// *** SequentialSubTasksDone + +void SequentialSubTasksDone::clear() { + _n_tasks = _n_claimed = 0; + _n_threads = _n_completed = 0; +} + +bool SequentialSubTasksDone::valid() { + return _n_threads > 0; +} + +bool SequentialSubTasksDone::is_task_claimed(int& t) { + jint* n_claimed_ptr = &_n_claimed; + t = *n_claimed_ptr; + while (t < _n_tasks) { + jint res = Atomic::cmpxchg(t+1, n_claimed_ptr, t); + if (res == t) { + return false; + } + t = *n_claimed_ptr; + } + return true; +} + +bool SequentialSubTasksDone::all_tasks_completed() { + jint* n_completed_ptr = &_n_completed; + jint complete = *n_completed_ptr; + while (true) { + jint res = Atomic::cmpxchg(complete+1, n_completed_ptr, complete); + if (res == complete) { + break; + } + complete = res; + } + if (complete+1 == _n_threads) { + clear(); + return true; + } + return false; +} diff --git a/hotspot/src/share/vm/utilities/workgroup.hpp b/hotspot/src/share/vm/utilities/workgroup.hpp new file mode 100644 index 00000000000..3797a3f76c2 --- /dev/null +++ b/hotspot/src/share/vm/utilities/workgroup.hpp @@ -0,0 +1,345 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Forward declarations of classes defined here + +class WorkGang; +class GangWorker; +class YieldingFlexibleGangWorker; +class YieldingFlexibleGangTask; +class WorkData; + +// An abstract task to be worked on by a gang. +// You subclass this to supply your own work() method +class AbstractGangTask: public CHeapObj { +public: + // The abstract work method. + // The argument tells you which member of the gang you are. + virtual void work(int i) = 0; + + // Debugging accessor for the name. + const char* name() const PRODUCT_RETURN_(return NULL;); + int counter() { return _counter; } + void set_counter(int value) { _counter = value; } + int *address_of_counter() { return &_counter; } + + // RTTI + NOT_PRODUCT(virtual bool is_YieldingFlexibleGang_task() const { + return false; + }) + +private: + NOT_PRODUCT(const char* _name;) + // ??? Should a task have a priority associated with it? + // ??? Or can the run method adjust priority as needed? + int _counter; + +protected: + // Constructor and desctructor: only construct subclasses. + AbstractGangTask(const char* name) { + NOT_PRODUCT(_name = name); + _counter = 0; + } + virtual ~AbstractGangTask() { } +}; + + +// Class AbstractWorkGang: +// An abstract class representing a gang of workers. +// You subclass this to supply an implementation of run_task(). +class AbstractWorkGang: public CHeapObj { + // Here's the public interface to this class. +public: + // Constructor and destructor. + AbstractWorkGang(const char* name, bool are_GC_threads); + ~AbstractWorkGang(); + // Run a task, returns when the task is done (or terminated). + virtual void run_task(AbstractGangTask* task) = 0; + // Stop and terminate all workers. + virtual void stop(); +public: + // Debugging. + const char* name() const; +protected: + // Initialize only instance data. + const bool _are_GC_threads; + // Printing support. + const char* _name; + // The monitor which protects these data, + // and notifies of changes in it. + Monitor* _monitor; + // The count of the number of workers in the gang. + int _total_workers; + // Whether the workers should terminate. + bool _terminate; + // The array of worker threads for this gang. + // This is only needed for cleaning up. + GangWorker** _gang_workers; + // The task for this gang. + AbstractGangTask* _task; + // A sequence number for the current task. + int _sequence_number; + // The number of started workers. + int _started_workers; + // The number of finished workers. + int _finished_workers; +public: + // Accessors for fields + Monitor* monitor() const { + return _monitor; + } + int total_workers() const { + return _total_workers; + } + bool terminate() const { + return _terminate; + } + GangWorker** gang_workers() const { + return _gang_workers; + } + AbstractGangTask* task() const { + return _task; + } + int sequence_number() const { + return _sequence_number; + } + int started_workers() const { + return _started_workers; + } + int finished_workers() const { + return _finished_workers; + } + bool are_GC_threads() const { + return _are_GC_threads; + } + // Predicates. + bool is_idle() const { + return (task() == NULL); + } + // Return the Ith gang worker. + GangWorker* gang_worker(int i) const; + + void threads_do(ThreadClosure* tc) const; + + // Printing + void print_worker_threads_on(outputStream *st) const; + void print_worker_threads() const { + print_worker_threads_on(tty); + } + +protected: + friend class GangWorker; + friend class YieldingFlexibleGangWorker; + // Note activation and deactivation of workers. + // These methods should only be called with the mutex held. + void internal_worker_poll(WorkData* data) const; + void internal_note_start(); + void internal_note_finish(); +}; + +class WorkData: public StackObj { + // This would be a struct, but I want accessor methods. +private: + bool _terminate; + AbstractGangTask* _task; + int _sequence_number; +public: + // Constructor and destructor + WorkData() { + _terminate = false; + _task = NULL; + _sequence_number = 0; + } + ~WorkData() { + } + // Accessors and modifiers + bool terminate() const { return _terminate; } + void set_terminate(bool value) { _terminate = value; } + AbstractGangTask* task() const { return _task; } + void set_task(AbstractGangTask* value) { _task = value; } + int sequence_number() const { return _sequence_number; } + void set_sequence_number(int value) { _sequence_number = value; } + + YieldingFlexibleGangTask* yf_task() const { + return (YieldingFlexibleGangTask*)_task; + } +}; + +// Class WorkGang: +class WorkGang: public AbstractWorkGang { +public: + // Constructor + WorkGang(const char* name, int workers, bool are_GC_threads); + // Run a task, returns when the task is done (or terminated). + virtual void run_task(AbstractGangTask* task); +}; + +// Class GangWorker: +// Several instances of this class run in parallel as workers for a gang. +class GangWorker: public WorkerThread { +public: + // Constructors and destructor. + GangWorker(AbstractWorkGang* gang, uint id); + + // The only real method: run a task for the gang. + virtual void run(); + // Predicate for Thread + virtual bool is_GC_task_thread() const; + // Printing + void print_on(outputStream* st) const; + virtual void print() const { print_on(tty); } +protected: + AbstractWorkGang* _gang; + + virtual void initialize(); + virtual void loop(); + +public: + AbstractWorkGang* gang() const { return _gang; } +}; + +// A class that acts as a synchronisation barrier. Workers enter +// the barrier and must wait until all other workers have entered +// before any of them may leave. + +class WorkGangBarrierSync : public StackObj { +protected: + Monitor _monitor; + int _n_workers; + int _n_completed; + + Monitor* monitor() { return &_monitor; } + int n_workers() { return _n_workers; } + int n_completed() { return _n_completed; } + + void inc_completed() { _n_completed++; } + +public: + WorkGangBarrierSync(); + WorkGangBarrierSync(int n_workers, const char* name); + + // Set the number of workers that will use the barrier. + // Must be called before any of the workers start running. + void set_n_workers(int n_workers); + + // Enter the barrier. A worker that enters the barrier will + // not be allowed to leave until all other threads have + // also entered the barrier. + void enter(); +}; + +// A class to manage claiming of subtasks within a group of tasks. The +// subtasks will be identified by integer indices, usually elements of an +// enumeration type. + +class SubTasksDone: public CHeapObj { + jint* _tasks; + int _n_tasks; + int _n_threads; + jint _threads_completed; +#ifdef ASSERT + jint _claimed; +#endif + + // Set all tasks to unclaimed. + void clear(); + +public: + // Initializes "this" to a state in which there are "n" tasks to be + // processed, none of the which are originally claimed. The number of + // threads doing the tasks is initialized 1. + SubTasksDone(int n); + + // True iff the object is in a valid state. + bool valid(); + + // Set the number of parallel threads doing the tasks to "t". Can only + // be called before tasks start or after they are complete. + void set_par_threads(int t); + + // Returns "false" if the task "t" is unclaimed, and ensures that task is + // claimed. The task "t" is required to be within the range of "this". + bool is_task_claimed(int t); + + // The calling thread asserts that it has attempted to claim all the + // tasks that it will try to claim. Every thread in the parallel task + // must execute this. (When the last thread does so, the task array is + // cleared.) + void all_tasks_completed(); + + // Destructor. + ~SubTasksDone(); +}; + +// As above, but for sequential tasks, i.e. instead of claiming +// sub-tasks from a set (possibly an enumeration), claim sub-tasks +// in sequential order. This is ideal for claiming dynamically +// partitioned tasks (like striding in the parallel remembered +// set scanning). Note that unlike the above class this is +// a stack object - is there any reason for it not to be? + +class SequentialSubTasksDone : public StackObj { +protected: + jint _n_tasks; // Total number of tasks available. + jint _n_claimed; // Number of tasks claimed. + jint _n_threads; // Total number of parallel threads. + jint _n_completed; // Number of completed threads. + + void clear(); + +public: + SequentialSubTasksDone() { clear(); } + ~SequentialSubTasksDone() {} + + // True iff the object is in a valid state. + bool valid(); + + // number of tasks + jint n_tasks() const { return _n_tasks; } + + // Set the number of parallel threads doing the tasks to t. + // Should be called before the task starts but it is safe + // to call this once a task is running provided that all + // threads agree on the number of threads. + void set_par_threads(int t) { _n_threads = t; } + + // Set the number of tasks to be claimed to t. As above, + // should be called before the tasks start but it is safe + // to call this once a task is running provided all threads + // agree on the number of tasks. + void set_n_tasks(int t) { _n_tasks = t; } + + // Returns false if the next task in the sequence is unclaimed, + // and ensures that it is claimed. Will set t to be the index + // of the claimed task in the sequence. Will return true if + // the task cannot be claimed and there are none left to claim. + bool is_task_claimed(int& t); + + // The calling thread asserts that it has attempted to claim + // all the tasks it possibly can in the sequence. Every thread + // claiming tasks must promise call this. Returns true if this + // is the last thread to complete so that the thread can perform + // cleanup if necessary. + bool all_tasks_completed(); +}; diff --git a/hotspot/src/share/vm/utilities/xmlstream.cpp b/hotspot/src/share/vm/utilities/xmlstream.cpp new file mode 100644 index 00000000000..b7098c857aa --- /dev/null +++ b/hotspot/src/share/vm/utilities/xmlstream.cpp @@ -0,0 +1,470 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_xmlstream.cpp.incl" + +void xmlStream::initialize(outputStream* out) { + _out = out; + _last_flush = 0; + _markup_state = BODY; + _text_init._outer_xmlStream = this; + _text = &_text_init; + +#ifdef ASSERT + _element_depth = 0; + int init_len = 100; + char* init_buf = NEW_C_HEAP_ARRAY(char, init_len); + _element_close_stack_low = init_buf; + _element_close_stack_high = init_buf + init_len; + _element_close_stack_ptr = init_buf + init_len - 1; + _element_close_stack_ptr[0] = '\0'; +#endif + + // Make sure each log uses the same base for time stamps. + if (is_open()) { + _out->time_stamp().update_to(1); + } +} + +#ifdef ASSERT +xmlStream::~xmlStream() { + FREE_C_HEAP_ARRAY(char, _element_close_stack_low); +} +#endif + +// Pass the given chars directly to _out. +void xmlStream::write(const char* s, size_t len) { + if (!is_open()) return; + + out()->write(s, len); +} + + +// Pass the given chars directly to _out, except that +// we watch for special "<&>" chars. +// This is suitable for either attribute text or for body text. +// We don't fool with "" chars + for (size_t i = 0; i < len; i++) { + char ch = s[i]; + // Escape special chars. + const char* esc = NULL; + switch (ch) { + // These are important only in attrs, but we do them always: + case '\'': esc = "'"; break; + case '"': esc = """; break; + case '<': esc = "<"; break; + case '&': esc = "&"; break; + // This is a freebie. + case '>': esc = ">"; break; + } + if (esc != NULL) { + if (written < i) { + out()->write(&s[written], i - written); + written = i; + } + out()->print_raw(esc); + written++; + } + } + + // Print the clean remainder. Usually, it is all of s. + if (written < len) { + out()->write(&s[written], len - written); + } +} + +// ------------------------------------------------------------------ +// Outputs XML text, with special characters quoted. +void xmlStream::text(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_text(format, ap); + va_end(ap); +} + +#define BUFLEN 2*K /* max size of output of individual print methods */ + +// ------------------------------------------------------------------ +void xmlStream::va_tag(bool push, const char* format, va_list ap) { + assert_if_no_error(!inside_attrs(), "cannot print tag inside attrs"); + char buffer[BUFLEN]; + size_t len; + const char* kind = do_vsnprintf(buffer, BUFLEN, format, ap, false, len); + see_tag(kind, push); + print_raw("<"); + write(kind, len); + _markup_state = (push ? HEAD : ELEM); +} + +#ifdef ASSERT +/// Debugging goo to make sure element tags nest properly. + +// ------------------------------------------------------------------ +void xmlStream::see_tag(const char* tag, bool push) { + assert_if_no_error(!inside_attrs(), "cannot start new element inside attrs"); + if (!push) return; + + // tag goes up until either null or space: + const char* tag_end = strchr(tag, ' '); + size_t tag_len = (tag_end == NULL) ? strlen(tag) : tag_end - tag; + assert(tag_len > 0, "tag must not be empty"); + // push the tag onto the stack, pulling down the pointer + char* old_ptr = _element_close_stack_ptr; + char* old_low = _element_close_stack_low; + char* push_ptr = old_ptr - (tag_len+1); + if (push_ptr < old_low) { + int old_len = _element_close_stack_high - old_ptr; + int new_len = old_len * 2; + if (new_len < 100) new_len = 100; + char* new_low = NEW_C_HEAP_ARRAY(char, new_len); + char* new_high = new_low + new_len; + char* new_ptr = new_high - old_len; + memcpy(new_ptr, old_ptr, old_len); + _element_close_stack_high = new_high; + _element_close_stack_low = new_low; + _element_close_stack_ptr = new_ptr; + FREE_C_HEAP_ARRAY(char, old_low); + push_ptr = new_ptr - (tag_len+1); + } + assert(push_ptr >= _element_close_stack_low, "in range"); + memcpy(push_ptr, tag, tag_len); + push_ptr[tag_len] = 0; + _element_close_stack_ptr = push_ptr; + _element_depth += 1; +} + +// ------------------------------------------------------------------ +void xmlStream::pop_tag(const char* tag) { + assert_if_no_error(!inside_attrs(), "cannot close element inside attrs"); + assert(_element_depth > 0, "must be in an element to close"); + assert(*tag != 0, "tag must not be empty"); + char* cur_tag = _element_close_stack_ptr; + bool bad_tag = false; + while (*cur_tag != 0 && strcmp(cur_tag, tag) != 0) { + this->print_cr(" ", cur_tag); + _element_close_stack_ptr = (cur_tag += strlen(cur_tag) + 1); + _element_depth -= 1; + bad_tag = true; + } + if (*cur_tag == 0) { + bad_tag = true; + } else { + // Pop the stack, by skipping over the tag and its null. + _element_close_stack_ptr = cur_tag + strlen(cur_tag) + 1; + _element_depth -= 1; + } + if (bad_tag && !VMThread::should_terminate() && !is_error_reported()) + assert(false, "bad tag in log"); +} +#endif + + +// ------------------------------------------------------------------ +// First word in formatted string is element kind, and any subsequent +// words must be XML attributes. Outputs "". +void xmlStream::elem(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_elem(format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +void xmlStream::va_elem(const char* format, va_list ap) { + va_begin_elem(format, ap); + end_elem(); +} + + +// ------------------------------------------------------------------ +// First word in formatted string is element kind, and any subsequent +// words must be XML attributes. Outputs "". +void xmlStream::begin_elem(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_tag(false, format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +void xmlStream::va_begin_elem(const char* format, va_list ap) { + va_tag(false, format, ap); +} + +// ------------------------------------------------------------------ +// Outputs "/>". +void xmlStream::end_elem() { + assert(_markup_state == ELEM, "misplaced end_elem"); + print_raw("/>\n"); + _markup_state = BODY; +} + +// ------------------------------------------------------------------ +// Outputs formatted text, followed by "/>". +void xmlStream::end_elem(const char* format, ...) { + va_list ap; + va_start(ap, format); + out()->vprint(format, ap); + va_end(ap); + end_elem(); +} + + +// ------------------------------------------------------------------ +// First word in formatted string is element kind, and any subsequent +// words must be XML attributes. Outputs "". +void xmlStream::head(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_head(format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +void xmlStream::va_head(const char* format, va_list ap) { + va_begin_head(format, ap); + end_head(); +} + +// ------------------------------------------------------------------ +// First word in formatted string is element kind, and any subsequent +// words must be XML attributes. Outputs "". +void xmlStream::begin_head(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_tag(true, format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +void xmlStream::va_begin_head(const char* format, va_list ap) { + va_tag(true, format, ap); +} + +// ------------------------------------------------------------------ +// Outputs ">". +void xmlStream::end_head() { + assert(_markup_state == HEAD, "misplaced end_head"); + print_raw(">\n"); + _markup_state = BODY; +} + + +// ------------------------------------------------------------------ +// Outputs formatted text, followed by ">". +void xmlStream::end_head(const char* format, ...) { + va_list ap; + va_start(ap, format); + out()->vprint(format, ap); + va_end(ap); + end_head(); +} + + +// ------------------------------------------------------------------ +// Outputs "". +void xmlStream::tail(const char* kind) { + pop_tag(kind); + print_raw("\n"); +} + +// ------------------------------------------------------------------ +// Outputs " ". +void xmlStream::done(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_done(format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +// Outputs " ". +// Because done_raw() doesn't need to format strings, it's simpler than +// done(), and can be called safely by fatal error handler. +void xmlStream::done_raw(const char* kind) { + print_raw("<"); + print_raw(kind); + print_raw("_done stamp='"); + out()->stamp(); + print_raw_cr("'/>"); + print_raw(""); +} + +// ------------------------------------------------------------------ +void xmlStream::va_done(const char* format, va_list ap) { + char buffer[200]; + guarantee(strlen(format) + 10 < sizeof(buffer), "bigger format buffer") + const char* kind = format; + const char* kind_end = strchr(kind, ' '); + size_t kind_len = (kind_end != NULL) ? (kind_end - kind) : strlen(kind); + strncpy(buffer, kind, kind_len); + strcpy(buffer + kind_len, "_done"); + strcat(buffer, format + kind_len); + // Output the trailing event with the timestamp. + va_begin_elem(buffer, ap); + stamp(); + end_elem(); + // Output the tail-tag of the enclosing element. + buffer[kind_len] = 0; + tail(buffer); +} + +// Output a timestamp attribute. +void xmlStream::stamp() { + assert_if_no_error(inside_attrs(), "stamp must be an attribute"); + print_raw(" stamp='"); + out()->stamp(); + print_raw("'"); +} + + +// ------------------------------------------------------------------ +// Output a method attribute, in the form " method='pkg/cls name sig'". +// This is used only when there is no ciMethod available. +void xmlStream::method(methodHandle method) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (method.is_null()) return; + print_raw(" method='"); + method_text(method); + print("' bytes='%d'", method->code_size()); + print(" count='%d'", method->invocation_count()); + int bec = method->backedge_count(); + if (bec != 0) print(" backedge_count='%d'", bec); + print(" iicount='%d'", method->interpreter_invocation_count()); + int throwouts = method->interpreter_throwout_count(); + if (throwouts != 0) print(" throwouts='%d'", throwouts); + methodDataOop mdo = method->method_data(); + if (mdo != NULL) { + uint cnt; + cnt = mdo->decompile_count(); + if (cnt != 0) print(" decompiles='%d'", cnt); + for (uint reason = 0; reason < mdo->trap_reason_limit(); reason++) { + cnt = mdo->trap_count(reason); + if (cnt != 0) print(" %s_traps='%d'", Deoptimization::trap_reason_name(reason), cnt); + } + cnt = mdo->overflow_trap_count(); + if (cnt != 0) print(" overflow_traps='%d'", cnt); + cnt = mdo->overflow_recompile_count(); + if (cnt != 0) print(" overflow_recompiles='%d'", cnt); + } +} + +void xmlStream::method_text(methodHandle method) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (method.is_null()) return; + //method->print_short_name(text()); + method->method_holder()->klass_part()->name()->print_symbol_on(text()); + print_raw(" "); // " " is easier for tools to parse than "::" + method->name()->print_symbol_on(text()); + print_raw(" "); // separator + method->signature()->print_symbol_on(text()); +} + + +// ------------------------------------------------------------------ +// Output a klass attribute, in the form " klass='pkg/cls'". +// This is used only when there is no ciKlass available. +void xmlStream::klass(KlassHandle klass) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (klass.is_null()) return; + print_raw(" klass='"); + klass_text(klass); + print_raw("'"); +} + +void xmlStream::klass_text(KlassHandle klass) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (klass.is_null()) return; + //klass->print_short_name(log->out()); + klass->name()->print_symbol_on(out()); +} + +void xmlStream::name(symbolHandle name) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (name.is_null()) return; + print_raw(" name='"); + name_text(name); + print_raw("'"); +} + +void xmlStream::name_text(symbolHandle name) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (name.is_null()) return; + //name->print_short_name(text()); + name->print_symbol_on(text()); +} + +void xmlStream::object(const char* attr, Handle x) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (x.is_null()) return; + print_raw(" "); + print_raw(attr); + print_raw("='"); + object_text(x); + print_raw("'"); +} + +void xmlStream::object_text(Handle x) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (x.is_null()) return; + //x->print_value_on(text()); + if (x->is_method()) + method_text(methodOop(x())); + else if (x->is_klass()) + klass_text(klassOop(x())); + else if (x->is_symbol()) + name_text(symbolOop(x())); + else + x->print_value_on(text()); +} + + +void xmlStream::flush() { + out()->flush(); + _last_flush = count(); +} + +void xmlTextStream::flush() { + if (_outer_xmlStream == NULL) return; + _outer_xmlStream->flush(); +} + +void xmlTextStream::write(const char* str, size_t len) { + if (_outer_xmlStream == NULL) return; + _outer_xmlStream->write_text(str, len); + update_position(str, len); +} diff --git a/hotspot/src/share/vm/utilities/xmlstream.hpp b/hotspot/src/share/vm/utilities/xmlstream.hpp new file mode 100644 index 00000000000..27839700fcd --- /dev/null +++ b/hotspot/src/share/vm/utilities/xmlstream.hpp @@ -0,0 +1,177 @@ +/* + * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class xmlStream; +class defaultStream; + +// Sub-stream for writing quoted text, as opposed to markup. +// Characters written to this stream are subject to quoting, +// as '<' => "<", etc. +class xmlTextStream : public outputStream { + friend class xmlStream; + friend class defaultStream; // tty + private: + + xmlStream* _outer_xmlStream; + + xmlTextStream() { _outer_xmlStream = NULL; } + + public: + virtual void flush(); // _outer.flush(); + virtual void write(const char* str, size_t len); // _outer->write_text() +}; + + +// Output stream for writing XML-structured logs. +// To write markup, use special calls elem, head/tail, etc. +// Use the xmlStream::text() stream to write unmarked text. +// Text written that way will be quoted as necessary using '<', etc. +// Characters written directly to an xmlStream via print_cr, etc., +// are directly written to the encapsulated stream, xmlStream::out(). +// This can be used to produce markup directly, character by character. +// (Such writes are not checked for markup syntax errors.) + +class xmlStream : public outputStream { + friend class defaultStream; // tty + public: + enum MarkupState { BODY, // after end_head() call, in text + HEAD, // after begin_head() call, in attrs + ELEM }; // after begin_elem() call, in attrs + + protected: + outputStream* _out; // file stream by which it goes + julong _last_flush; // last position of flush + MarkupState _markup_state; // where in the elem/head/tail dance + outputStream* _text; // text stream + xmlTextStream _text_init; + + // for subclasses + xmlStream() {} + void initialize(outputStream* out); + + // protect this from public use: + outputStream* out() { return _out; } + + // helpers for writing XML elements + void va_tag(bool push, const char* format, va_list ap); + virtual void see_tag(const char* tag, bool push) NOT_DEBUG({}); + virtual void pop_tag(const char* tag) NOT_DEBUG({}); + +#ifdef ASSERT + // in debug mode, we verify matching of opening and closing tags + int _element_depth; // number of unfinished elements + char* _element_close_stack_high; // upper limit of down-growing stack + char* _element_close_stack_low; // upper limit of down-growing stack + char* _element_close_stack_ptr; // pointer of down-growing stack +#endif + + public: + // creation + xmlStream(outputStream* out) { initialize(out); } + DEBUG_ONLY(virtual ~xmlStream();) + + bool is_open() { return _out != NULL; } + + // text output + bool inside_attrs() { return _markup_state != BODY; } + + // flushing + virtual void flush(); // flushes out, sets _last_flush = count() + virtual void write(const char* s, size_t len); + void write_text(const char* s, size_t len); // used by xmlTextStream + int unflushed_count() { return (int)(out()->count() - _last_flush); } + + // writing complete XML elements + void elem(const char* format, ...); + void begin_elem(const char* format, ...); + void end_elem(const char* format, ...); + void end_elem(); + void head(const char* format, ...); + void begin_head(const char* format, ...); + void end_head(const char* format, ...); + void end_head(); + void done(const char* format, ...); // xxx_done event, plus tail + void done_raw(const char * kind); + void tail(const char* kind); + + // va_list versions + void va_elem(const char* format, va_list ap); + void va_begin_elem(const char* format, va_list ap); + void va_head(const char* format, va_list ap); + void va_begin_head(const char* format, va_list ap); + void va_done(const char* format, va_list ap); + + // write text (with quoting of special XML characters <>&'" etc.) + outputStream* text() { return _text; } + void text(const char* format, ...); + void va_text(const char* format, va_list ap) { + text()->vprint(format, ap); + } + + // commonly used XML attributes + void stamp(); // stamp='1.234' + void method(methodHandle m); // method='k n s' ... + void klass(KlassHandle k); // klass='name' + void name(symbolHandle s); // name='name' + void object(const char* attr, Handle val); + + // print the text alone (sans ''): + void method_text(methodHandle m); + void klass_text(KlassHandle k); // klass='name' + void name_text(symbolHandle s); // name='name' + void object_text(Handle x); + + /* Example uses: + + // Empty element, simple case. + elem("X Y='Z'"); \n + + // Empty element, general case. + begin_elem("X Y='Z'"); + + // Compound element, simple case. + head("X Y='Z'"); \n + ...body... ...body... + tail("X"); \n + + // Compound element, general case. + begin_head("X Y='Z'"); \n + ...body... ...body... + tail("X"); \n + + // Printf-style formatting: + elem("X Y='%s'", "Z"); \n + + */ + +}; + +// Standard log file, null if no logging is happening. +extern xmlStream* xtty; + +// Note: If ::xtty != NULL, ::tty == ::xtty->text(). diff --git a/hotspot/src/share/vm/utilities/yieldingWorkgroup.cpp b/hotspot/src/share/vm/utilities/yieldingWorkgroup.cpp new file mode 100644 index 00000000000..d4c0ea92d11 --- /dev/null +++ b/hotspot/src/share/vm/utilities/yieldingWorkgroup.cpp @@ -0,0 +1,396 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_yieldingWorkgroup.cpp.incl" + +// Forward declaration of classes declared here. + +class GangWorker; +class WorkData; + +YieldingFlexibleWorkGang::YieldingFlexibleWorkGang( + const char* name, int workers, bool are_GC_threads) : + AbstractWorkGang(name, are_GC_threads) { + // Save arguments. + _total_workers = workers; + assert(_total_workers > 0, "Must have more than 1 worker"); + + _yielded_workers = 0; + + if (TraceWorkGang) { + tty->print_cr("Constructing work gang %s with %d threads", name, workers); + } + _gang_workers = NEW_C_HEAP_ARRAY(GangWorker*, workers); + assert(gang_workers() != NULL, "Failed to allocate gang workers"); + for (int worker = 0; worker < total_workers(); worker += 1) { + YieldingFlexibleGangWorker* new_worker = + new YieldingFlexibleGangWorker(this, worker); + assert(new_worker != NULL, "Failed to allocate YieldingFlexibleGangWorker"); + _gang_workers[worker] = new_worker; + if (new_worker == NULL || !os::create_thread(new_worker, os::pgc_thread)) + vm_exit_out_of_memory(0, "Cannot create worker GC thread. Out of system resources."); + if (!DisableStartThread) { + os::start_thread(new_worker); + } + } +} + +// Run a task; returns when the task is done, or the workers yield, +// or the task is aborted, or the work gang is terminated via stop(). +// A task that has been yielded can be continued via this interface +// by using the same task repeatedly as the argument to the call. +// It is expected that the YieldingFlexibleGangTask carries the appropriate +// continuation information used by workers to continue the task +// from its last yield point. Thus, a completed task will return +// immediately with no actual work having been done by the workers. +///////////////////// +// Implementatiuon notes: remove before checking XXX +/* +Each gang is working on a task at a certain time. +Some subset of workers may have yielded and some may +have finished their quota of work. Until this task has +been completed, the workers are bound to that task. +Once the task has been completed, the gang unbounds +itself from the task. + +The yielding work gang thus exports two invokation +interfaces: run_task() and continue_task(). The +first is used to initiate a new task and bind it +to the workers; the second is used to continue an +already bound task that has yielded. Upon completion +the binding is released and a new binding may be +created. + +The shape of a yielding work gang is as follows: + +Overseer invokes run_task(*task). + Lock gang monitor + Check that there is no existing binding for the gang + If so, abort with an error + Else, create a new binding of this gang to the given task + Set number of active workers (as asked) + Notify workers that work is ready to be done + [the requisite # workers would then start up + and do the task] + Wait on the monitor until either + all work is completed or the task has yielded + -- this is normally done through + yielded + completed == active + [completed workers are rest to idle state by overseer?] + return appropriate status to caller + +Overseer invokes continue_task(*task), + Lock gang monitor + Check that task is the same as current binding + If not, abort with an error + Else, set the number of active workers as requested? + Notify workers that they can continue from yield points + New workers can also start up as required + while satisfying the constraint that + active + yielded does not exceed required number + Wait (as above). + +NOTE: In the above, for simplicity in a first iteration + our gangs will be of fixed population and will not + therefore be flexible work gangs, just yielding work + gangs. Once this works well, we will in a second + iteration.refinement introduce flexibility into + the work gang. + +NOTE: we can always create a new gang per each iteration + in order to get the flexibility, but we will for now + desist that simplified route. + + */ +///////////////////// +void YieldingFlexibleWorkGang::start_task(YieldingFlexibleGangTask* new_task) { + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + assert(task() == NULL, "Gang currently tied to a task"); + assert(new_task != NULL, "Null task"); + // Bind task to gang + _task = new_task; + new_task->set_gang(this); // Establish 2-way binding to support yielding + _sequence_number++; + + int requested_size = new_task->requested_size(); + assert(requested_size >= 0, "Should be non-negative"); + if (requested_size != 0) { + _active_workers = MIN2(requested_size, total_workers()); + } else { + _active_workers = total_workers(); + } + new_task->set_actual_size(_active_workers); + + assert(_started_workers == 0, "Tabula rasa non"); + assert(_finished_workers == 0, "Tabula rasa non"); + assert(_yielded_workers == 0, "Tabula rasa non"); + yielding_task()->set_status(ACTIVE); + + // Wake up all the workers, the first few will get to work, + // and the rest will go back to sleep + monitor()->notify_all(); + wait_for_gang(); +} + +void YieldingFlexibleWorkGang::wait_for_gang() { + + assert(monitor()->owned_by_self(), "Data race"); + // Wait for task to complete or yield + for (Status status = yielding_task()->status(); + status != COMPLETED && status != YIELDED && status != ABORTED; + status = yielding_task()->status()) { + assert(started_workers() <= active_workers(), "invariant"); + assert(finished_workers() <= active_workers(), "invariant"); + assert(yielded_workers() <= active_workers(), "invariant"); + monitor()->wait(Mutex::_no_safepoint_check_flag); + } + switch (yielding_task()->status()) { + case COMPLETED: + case ABORTED: { + assert(finished_workers() == active_workers(), "Inconsistent status"); + assert(yielded_workers() == 0, "Invariant"); + reset(); // for next task; gang<->task binding released + break; + } + case YIELDED: { + assert(yielded_workers() > 0, "Invariant"); + assert(yielded_workers() + finished_workers() == active_workers(), + "Inconsistent counts"); + break; + } + case ACTIVE: + case INACTIVE: + case COMPLETING: + case YIELDING: + case ABORTING: + default: + ShouldNotReachHere(); + } +} + +void YieldingFlexibleWorkGang::continue_task( + YieldingFlexibleGangTask* gang_task) { + + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + assert(task() != NULL && task() == gang_task, "Incorrect usage"); + // assert(_active_workers == total_workers(), "For now"); + assert(_started_workers == _active_workers, "Precondition"); + assert(_yielded_workers > 0 && yielding_task()->status() == YIELDED, + "Else why are we calling continue_task()"); + // Restart the yielded gang workers + yielding_task()->set_status(ACTIVE); + monitor()->notify_all(); + wait_for_gang(); +} + +void YieldingFlexibleWorkGang::reset() { + _started_workers = 0; + _finished_workers = 0; + _active_workers = 0; + yielding_task()->set_gang(NULL); + _task = NULL; // unbind gang from task +} + +void YieldingFlexibleWorkGang::yield() { + assert(task() != NULL, "Inconsistency; should have task binding"); + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + assert(yielded_workers() < active_workers(), "Consistency check"); + if (yielding_task()->status() == ABORTING) { + // Do not yield; we need to abort as soon as possible + // XXX NOTE: This can cause a performance pathology in the + // current implementation in Mustang, as of today, and + // pre-Mustang in that as soon as an overflow occurs, + // yields will not be honoured. The right way to proceed + // of course is to fix bug # TBF, so that abort's cause + // us to return at each potential yield point. + return; + } + if (++_yielded_workers + finished_workers() == active_workers()) { + yielding_task()->set_status(YIELDED); + monitor()->notify_all(); + } else { + yielding_task()->set_status(YIELDING); + } + + while (true) { + switch (yielding_task()->status()) { + case YIELDING: + case YIELDED: { + monitor()->wait(Mutex::_no_safepoint_check_flag); + break; // from switch + } + case ACTIVE: + case ABORTING: + case COMPLETING: { + assert(_yielded_workers > 0, "Else why am i here?"); + _yielded_workers--; + return; + } + case INACTIVE: + case ABORTED: + case COMPLETED: + default: { + ShouldNotReachHere(); + } + } + } + // Only return is from inside switch statement above + ShouldNotReachHere(); +} + +void YieldingFlexibleWorkGang::abort() { + assert(task() != NULL, "Inconsistency; should have task binding"); + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + assert(yielded_workers() < active_workers(), "Consistency check"); + #ifndef PRODUCT + switch (yielding_task()->status()) { + // allowed states + case ACTIVE: + case ABORTING: + case COMPLETING: + case YIELDING: + break; + // not allowed states + case INACTIVE: + case ABORTED: + case COMPLETED: + case YIELDED: + default: + ShouldNotReachHere(); + } + #endif // !PRODUCT + Status prev_status = yielding_task()->status(); + yielding_task()->set_status(ABORTING); + if (prev_status == YIELDING) { + assert(yielded_workers() > 0, "Inconsistency"); + // At least one thread has yielded, wake it up + // so it can go back to waiting stations ASAP. + monitor()->notify_all(); + } +} + +/////////////////////////////// +// YieldingFlexibleGangTask +/////////////////////////////// +void YieldingFlexibleGangTask::yield() { + assert(gang() != NULL, "No gang to signal"); + gang()->yield(); +} + +void YieldingFlexibleGangTask::abort() { + assert(gang() != NULL, "No gang to signal"); + gang()->abort(); +} + +/////////////////////////////// +// YieldingFlexibleGangWorker +/////////////////////////////// +void YieldingFlexibleGangWorker::loop() { + int previous_sequence_number = 0; + Monitor* gang_monitor = gang()->monitor(); + MutexLockerEx ml(gang_monitor, Mutex::_no_safepoint_check_flag); + WorkData data; + int id; + while (true) { + // Check if there is work to do or if we have been asked + // to terminate + gang()->internal_worker_poll(&data); + if (data.terminate()) { + // We have been asked to terminate. + assert(gang()->task() == NULL, "No task binding"); + // set_status(TERMINATED); + return; + } else if (data.task() != NULL && + data.sequence_number() != previous_sequence_number) { + // There is work to be done. + // First check if we need to become active or if there + // are already the requisite number of workers + if (gang()->started_workers() == yf_gang()->active_workers()) { + // There are already enough workers, we do not need to + // to run; fall through and wait on monitor. + } else { + // We need to pitch in and do the work. + assert(gang()->started_workers() < yf_gang()->active_workers(), + "Unexpected state"); + id = gang()->started_workers(); + gang()->internal_note_start(); + // Now, release the gang mutex and do the work. + { + MutexUnlockerEx mul(gang_monitor, Mutex::_no_safepoint_check_flag); + data.task()->work(id); // This might include yielding + } + // Reacquire monitor and note completion of this worker + gang()->internal_note_finish(); + // Update status of task based on whether all workers have + // finished or some have yielded + assert(data.task() == gang()->task(), "Confused task binding"); + if (gang()->finished_workers() == yf_gang()->active_workers()) { + switch (data.yf_task()->status()) { + case ABORTING: { + data.yf_task()->set_status(ABORTED); + break; + } + case ACTIVE: + case COMPLETING: { + data.yf_task()->set_status(COMPLETED); + break; + } + default: + ShouldNotReachHere(); + } + gang_monitor->notify_all(); // Notify overseer + } else { // at least one worker is still working or yielded + assert(gang()->finished_workers() < yf_gang()->active_workers(), + "Counts inconsistent"); + switch (data.yf_task()->status()) { + case ACTIVE: { + // first, but not only thread to complete + data.yf_task()->set_status(COMPLETING); + break; + } + case YIELDING: { + if (gang()->finished_workers() + yf_gang()->yielded_workers() + == yf_gang()->active_workers()) { + data.yf_task()->set_status(YIELDED); + gang_monitor->notify_all(); // notify overseer + } + break; + } + case ABORTING: + case COMPLETING: { + break; // nothing to do + } + default: // everything else: INACTIVE, YIELDED, ABORTED, COMPLETED + ShouldNotReachHere(); + } + } + } + } + // Remember the sequence number + previous_sequence_number = data.sequence_number(); + // Wait for more work + gang_monitor->wait(Mutex::_no_safepoint_check_flag); + } +} diff --git a/hotspot/src/share/vm/utilities/yieldingWorkgroup.hpp b/hotspot/src/share/vm/utilities/yieldingWorkgroup.hpp new file mode 100644 index 00000000000..d7890f17d70 --- /dev/null +++ b/hotspot/src/share/vm/utilities/yieldingWorkgroup.hpp @@ -0,0 +1,203 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + +// Forward declarations +class YieldingFlexibleWorkGang; + +// Status of tasks +enum Status { + INACTIVE, + ACTIVE, + YIELDING, + YIELDED, + ABORTING, + ABORTED, + COMPLETING, + COMPLETED +}; + +// Class YieldingFlexibleGangWorker: +// Several instances of this class run in parallel as workers for a gang. +class YieldingFlexibleGangWorker: public GangWorker { +public: + // Ctor + YieldingFlexibleGangWorker(AbstractWorkGang* gang, int id) : + GangWorker(gang, id) { } + +public: + YieldingFlexibleWorkGang* yf_gang() const + { return (YieldingFlexibleWorkGang*)gang(); } + +protected: // Override from parent class + virtual void loop(); +}; + +// An abstract task to be worked on by a flexible work gang, +// and where the workers will periodically yield, usually +// in response to some condition that is signalled by means +// that are specific to the task at hand. +// You subclass this to supply your own work() method. +// A second feature of this kind of work gang is that +// it allows for the signalling of certain exceptional +// conditions that may be encountered during the performance +// of the task and that may require the task at hand to be +// `aborted' forthwith. Finally, these gangs are `flexible' +// in that they can operate at partial capacity with some +// gang workers waiting on the bench; in other words, the +// size of the active worker pool can flex (up to an apriori +// maximum) in response to task requests at certain points. +// The last part (the flexible part) has not yet been fully +// fleshed out and is a work in progress. +class YieldingFlexibleGangTask: public AbstractGangTask { + Status _status; + YieldingFlexibleWorkGang* _gang; + int _actual_size; // size of gang obtained + +protected: + int _requested_size; // size of gang requested + + // Constructor and desctructor: only construct subclasses. + YieldingFlexibleGangTask(const char* name): AbstractGangTask(name), + _status(INACTIVE), + _gang(NULL), + _requested_size(0) { } + + virtual ~YieldingFlexibleGangTask() { } + + friend class YieldingFlexibleWorkGang; + friend class YieldingFlexibleGangWorker; + NOT_PRODUCT(virtual bool is_YieldingFlexibleGang_task() const { + return true; + }) + + void set_status(Status s) { + _status = s; + } + YieldingFlexibleWorkGang* gang() { + return _gang; + } + void set_gang(YieldingFlexibleWorkGang* gang) { + assert(_gang == NULL || gang == NULL, "Clobber without intermediate reset?"); + _gang = gang; + } + +public: + // The abstract work method. + // The argument tells you which member of the gang you are. + virtual void work(int i) = 0; + + // Subclasses should call the parent's yield() method + // after having done any work specific to the subclass. + virtual void yield(); + + // An abstract method supplied by + // a concrete sub-class which is used by the coordinator + // to do any "central yielding" work. + virtual void coordinator_yield() = 0; + + // Subclasses should call the parent's abort() method + // after having done any work specific to the sunbclass. + virtual void abort(); + + Status status() const { return _status; } + bool yielded() const { return _status == YIELDED; } + bool completed() const { return _status == COMPLETED; } + bool aborted() const { return _status == ABORTED; } + bool active() const { return _status == ACTIVE; } + + int requested_size() const { return _requested_size; } + int actual_size() const { return _actual_size; } + + void set_requested_size(int sz) { _requested_size = sz; } + void set_actual_size(int sz) { _actual_size = sz; } +}; + +// Class YieldingWorkGang: A subclass of WorkGang. +// In particular, a YieldingWorkGang is made up of +// YieldingGangWorkers, and provides infrastructure +// supporting yielding to the "GangOverseer", +// being the thread that orchestrates the WorkGang via run_task(). +class YieldingFlexibleWorkGang: public AbstractWorkGang { + // Here's the public interface to this class. +public: + // Constructor and destructor. + YieldingFlexibleWorkGang(const char* name, int workers, bool are_GC_threads); + + YieldingFlexibleGangTask* yielding_task() const { + assert(task() == NULL || task()->is_YieldingFlexibleGang_task(), + "Incorrect cast"); + return (YieldingFlexibleGangTask*)task(); + } + // Run a task; returns when the task is done, or the workers yield, + // or the task is aborted, or the work gang is terminated via stop(). + // A task that has been yielded can be continued via this same interface + // by using the same task repeatedly as the argument to the call. + // It is expected that the YieldingFlexibleGangTask carries the appropriate + // continuation information used by workers to continue the task + // from its last yield point. Thus, a completed task will return + // immediately with no actual work having been done by the workers. + void run_task(AbstractGangTask* task) { + guarantee(false, "Use start_task instead"); + } + void start_task(YieldingFlexibleGangTask* new_task); + void continue_task(YieldingFlexibleGangTask* gang_task); + + // Abort a currently running task, if any; returns when all the workers + // have stopped working on the current task and have returned to their + // waiting stations. + void abort_task(); + + // Yield: workers wait at their current working stations + // until signalled to proceed by the overseer. + void yield(); + + // Abort: workers are expected to return to their waiting + // stations, whence they are ready for the next task dispatched + // by the overseer. + void abort(); + +private: + // The currently active workers in this gang. + // This is a number that is dynamically adjusted by + // the run_task() method at each subsequent invocation, + // using data in the YieldingFlexibleGangTask. + int _active_workers; + int _yielded_workers; + void wait_for_gang(); + +public: + // Accessors for fields + int active_workers() const { + return _active_workers; + } + + int yielded_workers() const { + return _yielded_workers; + } + +private: + friend class YieldingFlexibleGangWorker; + void reset(); // NYI +}; diff --git a/hotspot/test/Makefile b/hotspot/test/Makefile new file mode 100644 index 00000000000..26fdb59a0fa --- /dev/null +++ b/hotspot/test/Makefile @@ -0,0 +1,239 @@ +# +# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# +# Makefile to run jtreg +# + +OSNAME = $(shell uname -s) +ifeq ($(OSNAME), SunOS) + PLATFORM = solaris + JCT_PLATFORM = solaris + ARCH = $(shell uname -p) + ifeq ($(ARCH), i386) + ARCH=i586 + endif +endif +ifeq ($(OSNAME), Linux) + PLATFORM = linux + JCT_PLATFORM = linux + ARCH = $(shell uname -m) + ifeq ($(ARCH), i386) + ARCH=i586 + endif +endif +ifeq ($(OSNAME), Windows_NT) + PLATFORM = windows + JCT_PLATFORM = win32 + ifeq ($(word 1, $(PROCESSOR_IDENTIFIER)),ia64) + ARCH=ia64 + else + ifeq ($(word 1, $(PROCESSOR_IDENTIFIER)),AMD64) + ARCH=x64 + else + ifeq ($(word 1, $(PROCESSOR_IDENTIFIER)),EM64T) + ARCH=x64 + else + ARCH=i586 + endif + endif + endif +endif + +# Default bundle of all test results (passed or not) +JPRT_ARCHIVE_BUNDLE=$(TEST_ROOT)/JPRT_ARCHIVE_BUNDLE.zip + +# Default home for JTREG +ifeq ($(PLATFORM), windows) + JT_HOME = J:/svc/jct-tools3.2.2_01 +else + JT_HOME = /java/svc/jct-tools3.2.2_01 +endif + +# Default JTREG to run +JTREG = $(JT_HOME)/$(JCT_PLATFORM)/bin/jtreg + +# Root of this test area +TEST_ROOT := $(shell pwd) + +# Default JDK to test +JAVA_HOME = $(TEST_ROOT)/../build/$(PLATFORM)-$(ARCH) + +# The test directories to run +DEFAULT_TESTDIRS = serviceability +TESTDIRS = $(DEFAULT_TESTDIRS) + +# Files that hold total passed and failed counts (passed==0 is bad) +JTREG_TOTALS_DIR = $(TEST_ROOT)/JTREG_TOTALS_$(PLATFORM)_$(ARCH) +JTREG_FAILED = $(JTREG_TOTALS_DIR)/failed_count +JTREG_PASSED = $(JTREG_TOTALS_DIR)/passed_count + +# Root of all test results +JTREG_ALL_OUTPUT_DIRNAME = JTREG_OUTPUT_$(PLATFORM)_$(ARCH) +JTREG_ALL_OUTPUT_DIR = $(TEST_ROOT)/$(JTREG_ALL_OUTPUT_DIRNAME) + +# Test results for one test directory +JTREG_TEST_OUTPUT_DIR = $(JTREG_ALL_OUTPUT_DIR)/$@ +JTREG_TEST_REPORT_DIR = $(JTREG_TEST_OUTPUT_DIR)/JTreport +JTREG_TEST_WORK_DIR = $(JTREG_TEST_OUTPUT_DIR)/JTwork +JTREG_TEST_SUMMARY = $(JTREG_TEST_REPORT_DIR)/summary.txt + +# Temp files used by this Makefile +JTREG_TEST_TEMP_DIR = $(JTREG_ALL_OUTPUT_DIR)/$@/temp +JTREG_TEMP_PASSED = $(JTREG_TEST_TEMP_DIR)/passed +JTREG_TEMP_FAILED = $(JTREG_TEST_TEMP_DIR)/failed +JTREG_TEMP_OUTPUT = $(JTREG_TEST_TEMP_DIR)/output +JTREG_TEMP_RESULTS = $(JTREG_TEST_TEMP_DIR)/results + +# JTREG options (different for 2.1.6 and 3.2.2_01) +JTREG_COMMON_OPTIONS = -r:$(JTREG_TEST_REPORT_DIR) \ + -w:$(JTREG_TEST_WORK_DIR) \ + -testjdk:$(JAVA_HOME) \ + -automatic \ + -verbose:all +JTREG_216_OPTIONS = $(JTREG_COMMON_OPTIONS) $@ $(JAVA_ARGS) +JTREG_322_OPTIONS = $(JTREG_COMMON_OPTIONS) $(JAVA_ARGS:%=-vmoption:%) $@ + +# Default make rule +all: clean check tests + +# Chaeck to make sure these directories exist +check: $(JT_HOME) $(JAVA_HOME) $(JTREG) + +# Prime the test run +primecounts: FRC + @rm -f -r $(JTREG_TOTALS_DIR) + @mkdir -p $(JTREG_TOTALS_DIR) + @echo "0" > $(JTREG_FAILED) + @echo "0" > $(JTREG_PASSED) + +# Run the tests and determine the 'make' command exit status +# Ultimately we determine the make exit code based on the passed/failed count +tests: primecounts $(TESTDIRS) + @echo "JTREG TOTAL: passed=`cat $(JTREG_PASSED)` failed=`cat $(JTREG_FAILED)`" + zip -q -r $(JPRT_ARCHIVE_BUNDLE) $(JTREG_ALL_OUTPUT_DIRNAME) + @if [ `cat $(JTREG_FAILED)` -ne 0 -o \ + `cat $(JTREG_PASSED)` -le 0 ] ; then \ + echo "JTREG FAILED"; \ + exit 1; \ + else \ + echo "JTREG PASSED"; \ + exit 0; \ + fi + +# Just make sure these directires exist +$(JT_HOME) $(JAVA_HOME): FRC + @if [ ! -d $@ ] ; then \ + echo "ERROR: Directory $@ does not exist"; \ + exit 1; \ + fi + +# Make sure this file exists +$(JTREG): FRC + @if [ ! -f $@ ] ; then \ + echo "ERROR: File $@ does not exist"; \ + exit 1; \ + fi + +# Process each test directory one by one, this rule always completes. +# Note that the use of 'tee' tosses the jtreg process exit status, this +# is as expected because even if jtreg fails, we need to save the +# output. So we update the JTREG_PASSED and JTREG_FAILED count files. +# Note that missing the 'results:' line in the last few lines of output +# will indicate a failure (or a bump by one of the JTREG_FAILED file. +# Note that passed: 0 or no passed: indication means a failure. +# Note that any indication of the word 'failed' indicates failure. +# Ultimately if the contents of JTREG_FAILED is not 0, we have failed +# tests, and if the contents of JTREG_PASSED is 0, we consider that a +# failure. +$(TESTDIRS): FRC + @if [ ! -d $@ ] ; then \ + echo "ERROR: Directory $@ does not exist"; \ + exit 1; \ + fi + @echo "---------------------------------------------------" + @rm -f -r $(JTREG_TEST_OUTPUT_DIR) + @mkdir -p $(JTREG_TEST_OUTPUT_DIR) + @mkdir -p $(JTREG_TEST_WORK_DIR) + @mkdir -p $(JTREG_TEST_WORK_DIR)/scratch + @mkdir -p $(JTREG_TEST_REPORT_DIR) + @mkdir -p $(JTREG_TEST_TEMP_DIR) + @echo "Testing $@" + @echo "Using JAVA_HOME=$(JAVA_HOME)" + @echo "Using JAVA_ARGS=$(JAVA_ARGS)" + @if [ "`$(JTREG) -help 2>&1 | fgrep -- -vmoption`" != "" ] ; then \ + echo "Assume we are using jtreg 3.2.2_01 or newer"; \ + echo "$(JTREG) $(JTREG_322_OPTIONS)"; \ + $(JTREG) $(JTREG_322_OPTIONS) 2>&1 | tee $(JTREG_TEMP_OUTPUT) ; \ + else \ + echo "Assume we are using jtreg 2.1.6"; \ + echo "$(JTREG) $(JTREG_216_OPTIONS)"; \ + $(JTREG) $(JTREG_216_OPTIONS) 2>&1 | tee $(JTREG_TEMP_OUTPUT) ; \ + fi + @echo "---------------------------------------------------" + @echo "Extracting passed and failed counts from jtreg output" + @tail -10 $(JTREG_TEMP_OUTPUT) | fgrep -i 'results:' | \ + tail -1 | tee $(JTREG_TEMP_RESULTS) + @sed -e 's@.*\ passed:\ \([1-9][0-9]*\).*@\1@' $(JTREG_TEMP_RESULTS) \ + > $(JTREG_TEMP_PASSED) + @if [ "`cat $(JTREG_TEMP_PASSED)`" = "" ] ; then \ + echo "ERROR: No passed indication in results"; \ + expr `cat $(JTREG_FAILED)` '+' 1 > $(JTREG_FAILED); \ + elif [ `cat $(JTREG_TEMP_PASSED)` -le 0 ] ; then \ + echo "ERROR: Passed count appears to be 0"; \ + expr `cat $(JTREG_FAILED)` '+' 1 > $(JTREG_FAILED); \ + elif [ "`fgrep -i failed $(JTREG_TEMP_RESULTS)`" = "" ] ; then \ + echo "No indication anything failed"; \ + expr `cat $(JTREG_PASSED)` '+' `cat $(JTREG_TEMP_PASSED)` \ + > $(JTREG_PASSED); \ + else \ + sed -e 's@.*\ failed:\ \([1-9][0-9]*\).*@\1@' $(JTREG_TEMP_FAILED) \ + > $(JTREG_TEMP_FAILED); \ + if [ "`cat $(JTREG_TEMP_FAILED)`" = "" ] ; then \ + echo "ERROR: Failed pattern but no failed count in results"; \ + expr `cat $(JTREG_FAILED)` '+' 1 > $(JTREG_FAILED); \ + elif [ `cat $(JTREG_TEMP_FAILED)` -le 0 ] ; then \ + echo "ERROR: Failed count is 0, did something failed or not?"; \ + expr `cat $(JTREG_FAILED)` '+' 1 > $(JTREG_FAILED); \ + else \ + expr `cat $(JTREG_FAILED)` '+' `cat $(JTREG_TEMP_FAILED)` \ + > $(JTREG_FAILED); \ + fi; \ + fi + @echo "---------------------------------------------------" + @echo "Summary: " + @if [ -f $(JTREG_TEST_SUMMARY) ] ; then \ + cat $(JTREG_TEST_SUMMARY) ; \ + else \ + echo "ERROR: Missing $(JTREG_TEST_SUMMARY)"; \ + fi + @echo "---------------------------------------------------" + +# Cleanup +clean: + rm -f -r $(JTREG_ALL_OUTPUT_DIR) + rm -f $(JPRT_ARCHIVE_BUNDLE) + +FRC: + diff --git a/hotspot/test/TEST.ROOT b/hotspot/test/TEST.ROOT new file mode 100644 index 00000000000..fdb0b5a0713 --- /dev/null +++ b/hotspot/test/TEST.ROOT @@ -0,0 +1,31 @@ +# +# Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# + +# This file identifies the root of the test-suite hierarchy. +# It also contains test-suite configuration information. +# DO NOT EDIT without first contacting hotspot-regtest@sun.com + +# The list of keywords supported in this test suite +keys=cte_test diff --git a/hotspot/test/jprt.config b/hotspot/test/jprt.config new file mode 100644 index 00000000000..0fe153736c8 --- /dev/null +++ b/hotspot/test/jprt.config @@ -0,0 +1,163 @@ +#!echo "This is not a shell script" +############################################################################# +# +# Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +############################################################################# + +############################################################################# +# +# JPRT shell configuration for testing. +# +# Input environment variables: +# Windows Only: +# PATH +# ROOTDIR +# +# Output variable settings: +# make Full path to GNU make +# +# Output environment variables: +# PATH +# +############################################################################# + +############################################################################# +# Error +error() # message +{ + echo "ERROR: $1" + exit 6 +} +# Directory must exist +dirMustExist() # dir name +{ + if [ ! -d "$1" ] ; then + error "Directory for $2 does not exist: $1" + fi +} +# File must exist +fileMustExist() # dir name +{ + if [ ! -f "$1" ] ; then + error "File for $2 does not exist: $1" + fi +} +############################################################################# + +# Should be set by JPRT as the 3 basic inputs +slashjava="${ALT_SLASH_JAVA}" +if [ "${slashjava}" = "" ] ; then + slashjava=/java +fi + +# Check input +dirMustExist "${slashjava}" ALT_SLASH_JAVA + +# Uses 'uname -s', but only expect SunOS or Linux, assume Windows otherwise. +osname=`uname -s` +if [ "${osname}" = SunOS ] ; then + + # SOLARIS: Sparc or X86 + osarch=`uname -p` + if [ "${osarch}" = sparc ] ; then + solaris_arch=sparc + else + solaris_arch=i386 + fi + + # Add basic solaris system paths + path4sdk=/usr/ccs/bin:/usr/ccs/lib:/usr/bin:/bin:/usr/sfw/bin + + # Find GNU make + make=/usr/sfw/bin/gmake + if [ ! -f ${make} ] ; then + make=/opt/sfw/bin/gmake + if [ ! -f ${make} ] ; then + make=${slashjava}/devtools/${solaris_arch}/bin/gnumake + fi + fi + fileMustExist "${make}" make + + # File creation mask + umask 002 + +elif [ "${osname}" = Linux ] ; then + + # Add basic paths + path4sdk=/usr/bin:/bin:/usr/sbin:/sbin + + # Find GNU make + make=/usr/bin/make + fileMustExist "${make}" make + + umask 002 + +else + + # Windows: Differs on CYGWIN vs. MKS. + + # We need to determine if we are running a CYGWIN shell or an MKS shell + # (if uname isn't available, then it will be unix_toolset=unknown) + unix_toolset=unknown + if [ "`uname -a | fgrep Cygwin`" = "" -a -d "${ROOTDIR}" ] ; then + # We kind of assume ROOTDIR is where MKS is and it's ok + unix_toolset=MKS + mkshome=`dosname -s "${ROOTDIR}"` + # Most unix utilities are in the mksnt directory of ROOTDIR + unixcommand_path="${mkshome}/mksnt" + path4sdk="${unixcommand_path}" + devtools_path="${slashjava}/devtools/win32/bin" + path4sdk="${devtools_path};${path4sdk}" + # Find GNU make + make="${devtools_path}/gnumake.exe" + fileMustExist "${make}" make + elif [ "`uname -a | fgrep Cygwin`" != "" -a -f /bin/cygpath ] ; then + # For CYGWIN, uname will have "Cygwin" in it, and /bin/cygpath should exist + unix_toolset=CYGWIN + # Most unix utilities are in the /usr/bin + unixcommand_path="/usr/bin" + path4sdk="${unixcommand_path}" + # Find GNU make + make="${unixcommand_path}/make.exe" + fileMustExist "${make}" make + else + echo "WARNING: Cannot figure out if this is MKS or CYGWIN" + fi + + + # For windows, it's hard to know where the system is, so we just add this + # to PATH. + slash_path="`echo ${path4sdk} | sed -e 's@\\\\@/@g' -e 's@//@/@g' -e 's@/$@@' -e 's@/;@;@g'`" + path4sdk="${slash_path};${PATH}" + + # Convert path4sdk to cygwin style + if [ "${unix_toolset}" = CYGWIN ] ; then + path4sdk="`/usr/bin/cygpath -p ${path4sdk}`" + fi + +fi + +# Export PATH setting +PATH="${path4sdk}" +export PATH +